| Index: net/http/http_auth_gssapi_posix.cc
|
| diff --git a/net/http/http_auth_gssapi_posix.cc b/net/http/http_auth_gssapi_posix.cc
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..7c428e6456cea71826c2bd51afcfa7424104efe3
|
| --- /dev/null
|
| +++ b/net/http/http_auth_gssapi_posix.cc
|
| @@ -0,0 +1,499 @@
|
| +// Copyright (c) 2010 The Chromium Authors. All rights reserved.
|
| +// Use of this source code is governed by a BSD-style license that can be
|
| +// found in the LICENSE file.
|
| +
|
| +#include "net/http/http_auth_gssapi_posix.h"
|
| +
|
| +#include <string>
|
| +
|
| +#include "base/base64.h"
|
| +#include "base/file_path.h"
|
| +#include "base/logging.h"
|
| +#include "base/native_library.h"
|
| +#include "base/singleton.h"
|
| +#include "base/string_util.h"
|
| +#include "net/base/net_errors.h"
|
| +#include "net/base/net_util.h"
|
| +#include "net/http/http_auth.h"
|
| +
|
| +namespace net {
|
| +
|
| +gssapi::gss_OID_desc GSS_C_NT_USER_NAME = {
|
| + 10,
|
| + (void *) "\x2a\x86\x48\x86\xf7\x12\x01\x02\x01\x01"
|
| +};
|
| +gssapi::gss_OID_desc GSS_C_NT_MACHINE_UID_NAME = {
|
| + 10,
|
| + (void *) "\x2a\x86\x48\x86\xf7\x12\x01\x02\x01\x02"
|
| +};
|
| +gssapi::gss_OID_desc GSS_C_NT_STRING_UID_NAME = {
|
| + 10,
|
| + (void *) "\x2a\x86\x48\x86\xf7\x12\x01\x02\x01\x03"
|
| +};
|
| +gssapi::gss_OID_desc GSS_C_NT_HOSTBASED_SERVICE_X = {
|
| + 6,
|
| + (void *) "\x2b\x06\x01\x05\x06\x02"
|
| +};
|
| +gssapi::gss_OID_desc GSS_C_NT_HOSTBASED_SERVICE = {
|
| + 10,
|
| + (void *) "\x2a\x86\x48\x86\xf7\x12\x01\x02\x01\x04"
|
| +};
|
| +gssapi::gss_OID_desc GSS_C_NT_ANONYMOUS = {
|
| + 6,
|
| + (void *) "\x2b\x06\01\x05\x06\x03"
|
| +};
|
| +gssapi::gss_OID_desc GSS_C_NT_EXPORT_NAME = {
|
| + 6,
|
| + (void *) "\x2b\x06\x01\x05\x06\x04"
|
| +};
|
| +
|
| +GSSAPISharedLibrary::GSSAPISharedLibrary()
|
| + : initialized_(false),
|
| + cached_initialize_value_(false),
|
| + gssapi_library_(NULL),
|
| + import_name_(NULL),
|
| + release_name_(NULL),
|
| + release_buffer_(NULL),
|
| + display_status_(NULL),
|
| + init_sec_context_(NULL),
|
| + wrap_size_limit_(NULL) {
|
| +}
|
| +
|
| +GSSAPISharedLibrary::~GSSAPISharedLibrary() {
|
| + if (gssapi_library_) {
|
| + base::UnloadNativeLibrary(gssapi_library_);
|
| + gssapi_library_ = NULL;
|
| + }
|
| +}
|
| +
|
| +bool GSSAPISharedLibrary::Init() {
|
| + if (!initialized_) {
|
| + cached_initialize_value_ = InitImpl();
|
| + initialized_ = true;
|
| + }
|
| + return cached_initialize_value_;
|
| +}
|
| +
|
| +bool GSSAPISharedLibrary::InitImpl() {
|
| + DCHECK(!initialized_);
|
| + gssapi_library_ = LoadSharedObject();
|
| + if (gssapi_library_ == NULL)
|
| + return false;
|
| + if (!BindMethods())
|
| + return false;
|
| + return true;
|
| +}
|
| +
|
| +const char* GSSAPISharedLibrary::kLibraryNames[] = {
|
| + "libgssapi_krb5.so.2",
|
| + "libgssapi.so.4",
|
| + "libgssapi.so.1",
|
| +};
|
| +
|
| +base::NativeLibrary GSSAPISharedLibrary::LoadSharedObject() {
|
| + for (unsigned int i = 0; i < arraysize(kLibraryNames); ++i) {
|
| + FilePath file_path(kLibraryNames[i]);
|
| + base::NativeLibrary lib = base::LoadNativeLibrary(file_path);
|
| + if (lib)
|
| + return lib;
|
| + }
|
| + return NULL;
|
| +}
|
| +
|
| +template <typename T>
|
| +bool FindAndBind(base::NativeLibrary library, const char* name, T* t) {
|
| + void* func = base::GetFunctionPointerFromNativeLibrary(library, name);
|
| + if (func == NULL) {
|
| + string16 error_message = base::GetLibraryErrorMessage();
|
| + LOG(WARNING) << "dlsym failed when finding function pointer for " << name
|
| + << ": " << error_message;
|
| + return false;
|
| + }
|
| + *t = reinterpret_cast<T>(func);
|
| + return true;
|
| +}
|
| +
|
| +bool GSSAPISharedLibrary::BindMethods() {
|
| + DCHECK(gssapi_library_ != NULL);
|
| + if (!FindAndBind(gssapi_library_, "gss_import_name", &import_name_))
|
| + return false;
|
| + if (!FindAndBind(gssapi_library_, "gss_release_name", &release_name_))
|
| + return false;
|
| + if (!FindAndBind(gssapi_library_, "gss_release_buffer", &release_buffer_))
|
| + return false;
|
| + if (!FindAndBind(gssapi_library_, "gss_display_status", &display_status_))
|
| + return false;
|
| + if (!FindAndBind(gssapi_library_, "gss_init_sec_context", &init_sec_context_))
|
| + return false;
|
| + if (!FindAndBind(gssapi_library_, "gss_wrap_size_limit", &wrap_size_limit_))
|
| + return false;
|
| + return true;
|
| +}
|
| +
|
| +gssapi::OM_uint32 GSSAPISharedLibrary::import_name(
|
| + gssapi::OM_uint32* minor_status,
|
| + const gssapi::gss_buffer_t input_name_buffer,
|
| + const gssapi::gss_OID input_name_type,
|
| + gssapi::gss_name_t* output_name) {
|
| + DCHECK(cached_initialize_value_);
|
| + if (!import_name_)
|
| + return gssapi::OM_uint32(-1);
|
| + return import_name_(minor_status, input_name_buffer, input_name_type,
|
| + output_name);
|
| +}
|
| +
|
| +gssapi::OM_uint32 GSSAPISharedLibrary::release_name(
|
| + gssapi::OM_uint32* minor_status,
|
| + gssapi::gss_name_t* input_name) {
|
| + DCHECK(cached_initialize_value_);
|
| + if (!release_name_)
|
| + return gssapi::OM_uint32(-1);
|
| + return release_name_(minor_status, input_name);
|
| +}
|
| +
|
| +gssapi::OM_uint32 GSSAPISharedLibrary::release_buffer(
|
| + gssapi::OM_uint32* minor_status,
|
| + gssapi::gss_buffer_t buffer) {
|
| + DCHECK(cached_initialize_value_);
|
| + if (!release_buffer_)
|
| + return gssapi::OM_uint32(-1);
|
| + return release_buffer_(minor_status, buffer);
|
| +}
|
| +
|
| +gssapi::OM_uint32 GSSAPISharedLibrary::display_status(
|
| + gssapi::OM_uint32* minor_status,
|
| + gssapi::OM_uint32 status_value,
|
| + int status_type,
|
| + const gssapi::gss_OID mech_type,
|
| + gssapi::OM_uint32* message_context,
|
| + gssapi::gss_buffer_t status_string) {
|
| + DCHECK(cached_initialize_value_);
|
| + if (!display_status_)
|
| + return gssapi::OM_uint32(-1);
|
| + status_string->length = 0;
|
| + status_string->value = NULL;
|
| + return display_status_(minor_status, status_value, status_type, mech_type,
|
| + message_context, status_string);
|
| +}
|
| +
|
| +gssapi::OM_uint32 GSSAPISharedLibrary::init_sec_context(
|
| + gssapi::OM_uint32* minor_status,
|
| + const gssapi::gss_cred_id_t initiator_cred_handle,
|
| + gssapi::gss_ctx_id_t* context_handle,
|
| + const gssapi::gss_name_t target_name,
|
| + const gssapi::gss_OID mech_type,
|
| + gssapi::OM_uint32 req_flags,
|
| + gssapi::OM_uint32 time_req,
|
| + const gssapi::gss_channel_bindings_t input_chan_bindings,
|
| + const gssapi::gss_buffer_t input_token,
|
| + gssapi::gss_OID* actual_mech_type,
|
| + gssapi::gss_buffer_t* output_token,
|
| + gssapi::OM_uint32* ret_flags,
|
| + gssapi::OM_uint32* time_rec) {
|
| + DCHECK(cached_initialize_value_);
|
| + if (!init_sec_context_)
|
| + return gssapi::OM_uint32(-1);
|
| + return init_sec_context_(minor_status,
|
| + initiator_cred_handle,
|
| + context_handle,
|
| + target_name,
|
| + mech_type,
|
| + req_flags,
|
| + time_req,
|
| + input_chan_bindings,
|
| + input_token,
|
| + actual_mech_type,
|
| + reinterpret_cast<gssapi::gss_buffer_t>(output_token),
|
| + ret_flags,
|
| + time_rec);
|
| +}
|
| +
|
| +gssapi::OM_uint32 GSSAPISharedLibrary::wrap_size_limit(
|
| + gssapi::OM_uint32* minor_status,
|
| + const gssapi::gss_ctx_id_t context_handle,
|
| + int conf_req_flag,
|
| + gssapi::gss_qop_t qop_req,
|
| + gssapi::OM_uint32 req_output_size,
|
| + gssapi::OM_uint32* max_input_size) {
|
| + DCHECK(cached_initialize_value_);
|
| + if (!wrap_size_limit_)
|
| + return gssapi::OM_uint32(-1);
|
| + return wrap_size_limit_(minor_status,
|
| + context_handle,
|
| + conf_req_flag,
|
| + qop_req,
|
| + req_output_size,
|
| + max_input_size);
|
| +}
|
| +
|
| +GSSAPILibrary* GSSAPILibrary::GetDefault() {
|
| + return Singleton<GSSAPISharedLibrary>::get();
|
| +}
|
| +
|
| +namespace {
|
| +std::string DisplayStatus(gssapi::OM_uint32 major_status, gssapi::OM_uint32 minor_status) {
|
| + if (major_status == GSS_S_COMPLETE)
|
| + return "OK";
|
| + return StringPrintf("%x %x", major_status, minor_status);
|
| +}
|
| +
|
| +static const int kMaxDisplayIterations = 8;
|
| +
|
| +std::string DisplayCode(GSSAPILibrary* gssapi_lib,
|
| + gssapi::OM_uint32 status,
|
| + gssapi::OM_uint32 status_code_type) {
|
| + // msg_ctx needs to be outside the loop because it is invoked multiple times.
|
| + gssapi::OM_uint32 msg_ctx = 0;
|
| + std::string rv = StringPrintf("(0x%08X)", status);
|
| +
|
| + // This loop should continue iterating until msg_ctx is 0 after the first
|
| + // iteration. To be cautious and prevent an infinite loop, it stops after
|
| + // a finite number of iterations as well.
|
| + for (int i = 0; i < kMaxDisplayIterations; ++i) {
|
| + gssapi::OM_uint32 min_stat;
|
| + gssapi::gss_buffer_desc_struct msg = GSS_C_EMPTY_BUFFER;
|
| + gssapi_lib->display_status(&min_stat, status, status_code_type,
|
| + GSS_C_NULL_OID,
|
| + &msg_ctx, &msg);
|
| + rv += StringPrintf(" %s", static_cast<char *>(msg.value));
|
| + gssapi_lib->release_buffer(&min_stat, &msg);
|
| + if (!msg_ctx)
|
| + break;
|
| + }
|
| + return rv;
|
| +}
|
| +
|
| +std::string DisplayExtendedStatus(GSSAPILibrary* gssapi_lib,
|
| + gssapi::OM_uint32 major_status,
|
| + gssapi::OM_uint32 minor_status) {
|
| + if (major_status == GSS_S_COMPLETE)
|
| + return "OK";
|
| + std::string major = DisplayCode(gssapi_lib, major_status, GSS_C_GSS_CODE);
|
| + std::string minor = DisplayCode(gssapi_lib, minor_status, GSS_C_MECH_CODE);
|
| + return StringPrintf("Major: %s | Minor: %s", major.c_str(), minor.c_str());
|
| +}
|
| +
|
| +
|
| +// ScopedName releases a gssapi::gss_name_t when it goes out of scope.
|
| +class ScopedName {
|
| + public:
|
| + ScopedName(gssapi::gss_name_t name,
|
| + GSSAPILibrary* gssapi_lib)
|
| + : name_(name),
|
| + gssapi_lib_(gssapi_lib) {
|
| + DCHECK(gssapi_lib_);
|
| + }
|
| +
|
| + ~ScopedName() {
|
| + if (name_ != GSS_C_NO_NAME) {
|
| + gssapi::OM_uint32 minor_status = 0;
|
| + gssapi::OM_uint32 major_status =
|
| + gssapi_lib_->release_name(&minor_status, &name_);
|
| + if (major_status != GSS_S_COMPLETE) {
|
| + LOG(WARNING) << "Problem releasing name. "
|
| + << DisplayStatus(major_status, minor_status);
|
| + }
|
| + name_ = GSS_C_NO_NAME;
|
| + }
|
| + }
|
| +
|
| + private:
|
| + gssapi::gss_name_t name_;
|
| + GSSAPILibrary* gssapi_lib_;
|
| +
|
| + DISALLOW_COPY_AND_ASSIGN(ScopedName);
|
| +};
|
| +
|
| +// ScopedBuffer releases a gssapi::gss_buffer_t when it goes out of scope.
|
| +class ScopedBuffer {
|
| + public:
|
| + ScopedBuffer(gssapi::gss_buffer_t buffer,
|
| + GSSAPILibrary* gssapi_lib)
|
| + : buffer_(buffer),
|
| + gssapi_lib_(gssapi_lib) {
|
| + DCHECK(gssapi_lib_);
|
| + }
|
| +
|
| + ~ScopedBuffer() {
|
| + if (buffer_ != GSS_C_NO_BUFFER) {
|
| + gssapi::OM_uint32 minor_status = 0;
|
| + gssapi::OM_uint32 major_status =
|
| + gssapi_lib_->release_buffer(&minor_status, buffer_);
|
| + if (major_status != GSS_S_COMPLETE) {
|
| + LOG(WARNING) << "Problem releasing buffer. "
|
| + << DisplayStatus(major_status, minor_status);
|
| + }
|
| + buffer_ = GSS_C_NO_BUFFER;
|
| + }
|
| + }
|
| +
|
| + private:
|
| + gssapi::gss_buffer_t buffer_;
|
| + GSSAPILibrary* gssapi_lib_;
|
| +
|
| + DISALLOW_COPY_AND_ASSIGN(ScopedBuffer);
|
| +};
|
| +
|
| +} // namespace
|
| +
|
| +HttpAuthGSSAPI::HttpAuthGSSAPI(const std::string& scheme,
|
| + gssapi::gss_OID gss_oid,
|
| + GSSAPILibrary* library)
|
| + : scheme_(scheme),
|
| + gss_oid_(gss_oid),
|
| + library_(library),
|
| + sec_context_(NULL) {
|
| +}
|
| +
|
| +HttpAuthGSSAPI::~HttpAuthGSSAPI() {
|
| +}
|
| +
|
| +bool HttpAuthGSSAPI::NeedsIdentity() const {
|
| + return decoded_server_auth_token_.empty();
|
| +}
|
| +
|
| +bool HttpAuthGSSAPI::IsFinalRound() const {
|
| + return !NeedsIdentity();
|
| +}
|
| +
|
| +bool HttpAuthGSSAPI::ParseChallenge(HttpAuth::ChallengeTokenizer* tok) {
|
| + // Verify the challenge's auth-scheme.
|
| + if (!tok->valid() ||
|
| + !LowerCaseEqualsASCII(tok->scheme(), StringToLowerASCII(scheme_).c_str()))
|
| + return false;
|
| +
|
| + tok->set_expect_base64_token(true);
|
| + if (!tok->GetNext()) {
|
| + decoded_server_auth_token_.clear();
|
| + return true;
|
| + }
|
| +
|
| + std::string encoded_auth_token = tok->value();
|
| + std::string decoded_auth_token;
|
| + bool base64_rv = base::Base64Decode(encoded_auth_token, &decoded_auth_token);
|
| + if (!base64_rv) {
|
| + LOG(ERROR) << "Base64 decoding of auth token failed.";
|
| + return false;
|
| + }
|
| + decoded_server_auth_token_ = decoded_auth_token;
|
| + return true;
|
| +}
|
| +
|
| +
|
| +static uint8 kHostbasedServiceOidElements[] = {
|
| + 0x2a, 0x86, 0x48, 0x86, 0xF7, 0x12, 0x01, 0x02, 0x01, 0x04};
|
| +static gssapi::gss_OID_desc kHostbasedServiceOidDesc = {
|
| + arraysize(kHostbasedServiceOidElements),
|
| + static_cast<void *>(&kHostbasedServiceOidElements)};
|
| +
|
| +int HttpAuthGSSAPI::GenerateAuthToken(const std::wstring* username,
|
| + const std::wstring* password,
|
| + const std::wstring& origin,
|
| + const HttpRequestInfo* request,
|
| + const ProxyInfo* proxy,
|
| + std::string* out_credentials) {
|
| + DCHECK(library_);
|
| + DCHECK((username == NULL) == (password == NULL));
|
| +
|
| + library_->Init();
|
| +
|
| + if (!IsFinalRound()) {
|
| + int rv = OnFirstRound(username, password);
|
| + if (rv != OK)
|
| + return rv;
|
| + }
|
| +
|
| + gssapi::gss_buffer_desc input_token = GSS_C_EMPTY_BUFFER;
|
| + input_token.length = decoded_server_auth_token_.length();
|
| + input_token.value = static_cast<void *>(const_cast<char *>(
|
| + decoded_server_auth_token_.c_str()));
|
| + gssapi::gss_buffer_desc output_token1 = GSS_C_EMPTY_BUFFER;
|
| + gssapi::gss_buffer_t output_token = &output_token1;
|
| + int rv = GetNextSecurityToken(origin, &input_token, &output_token);
|
| + if (rv != OK)
|
| + return rv;
|
| + ScopedBuffer sb_out_token(output_token, library_);
|
| +
|
| + // Base64 encode data in output buffer and prepend the scheme.
|
| + std::string encode_input(static_cast<char*>(output_token->value),
|
| + output_token->length);
|
| + std::string encode_output;
|
| + bool ok = base::Base64Encode(encode_input, &encode_output);
|
| + if (!ok)
|
| + return rv;
|
| + *out_credentials = scheme_ + " " + encode_output;
|
| + return OK;
|
| +}
|
| +
|
| +int HttpAuthGSSAPI::OnFirstRound(const std::wstring* username,
|
| + const std::wstring* password) {
|
| + // TODO(cbentzel): Acquire credentials?
|
| + DCHECK((username == NULL) == (password == NULL));
|
| + username_ = L"";
|
| + password_ = L"";
|
| + if (username) {
|
| + username_ = *username;
|
| + password_ = *password;
|
| + }
|
| + return OK;
|
| +}
|
| +
|
| +int HttpAuthGSSAPI::GetNextSecurityToken(const std::wstring& origin,
|
| + gssapi::gss_buffer_t in_token,
|
| + gssapi::gss_buffer_t* out_token) {
|
| + // Create a name for the principal
|
| + // TODO(cbentzel): Should this be username@origin? What about domain?
|
| + // TODO(cbentzel): Just do this on the first pass?
|
| + const GURL orig(WideToASCII(origin));
|
| + std::string origin_principal = GetHostAndPort(orig);
|
| + gssapi::gss_buffer_desc origin_buffer = GSS_C_EMPTY_BUFFER;
|
| + origin_buffer.value = static_cast<void *>(const_cast<char *>(
|
| + origin_principal.c_str()));
|
| + origin_buffer.length = origin_principal.size() + 1;
|
| + gssapi::OM_uint32 minor_status_import_name = 0;
|
| + gssapi::gss_name_t principal_name;
|
| + gssapi::OM_uint32 major_status_import_name = library_->import_name(
|
| + &minor_status_import_name,
|
| + &origin_buffer,
|
| + &kHostbasedServiceOidDesc,
|
| + &principal_name);
|
| + if (major_status_import_name != GSS_S_COMPLETE) {
|
| + LOG(WARNING) << "Problem importing name. "
|
| + << DisplayExtendedStatus(library_,
|
| + major_status_import_name,
|
| + minor_status_import_name);
|
| + return ERR_UNEXPECTED;
|
| + }
|
| + ScopedName scoped_name(principal_name, library_);
|
| +
|
| + // Create a security context.
|
| + gssapi::OM_uint32 minor_status_sec_context;
|
| + gssapi::OM_uint32 req_flags = 0;
|
| + gssapi::OM_uint32 major_status_sec_context = library_->init_sec_context(
|
| + &minor_status_sec_context,
|
| + GSS_C_NO_CREDENTIAL,
|
| + &sec_context_,
|
| + principal_name,
|
| + gss_oid_,
|
| + req_flags,
|
| + GSS_C_INDEFINITE,
|
| + GSS_C_NO_CHANNEL_BINDINGS,
|
| + in_token,
|
| + NULL, // actual_mech_type
|
| + out_token,
|
| + NULL, // ret flags
|
| + NULL);
|
| + if (major_status_sec_context != GSS_S_COMPLETE &&
|
| + major_status_sec_context != GSS_S_CONTINUE_NEEDED) {
|
| + LOG(WARNING) << "Problem initializing context. "
|
| + << DisplayExtendedStatus(library_,
|
| + major_status_sec_context,
|
| + minor_status_sec_context);
|
| + return ERR_UNEXPECTED;
|
| + }
|
| +
|
| + return (major_status_sec_context != GSS_S_COMPLETE) ? ERR_IO_PENDING : OK;
|
| +}
|
| +
|
| +} // namespace net
|
| +
|
|
|