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 |
deleted file mode 100644 |
index d294a5ea72b21be10863fe91b5d7158e9db22ad4..0000000000000000000000000000000000000000 |
--- a/net/http/http_auth_gssapi_posix.cc |
+++ /dev/null |
@@ -1,891 +0,0 @@ |
-// Copyright (c) 2012 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 <limits> |
-#include <string> |
- |
-#include "base/base64.h" |
-#include "base/files/file_path.h" |
-#include "base/format_macros.h" |
-#include "base/logging.h" |
-#include "base/strings/string_util.h" |
-#include "base/strings/stringprintf.h" |
-#include "base/threading/thread_restrictions.h" |
-#include "net/base/net_errors.h" |
-#include "net/base/net_util.h" |
-#include "net/http/http_auth_challenge_tokenizer.h" |
- |
-// These are defined for the GSSAPI library: |
-// Paraphrasing the comments from gssapi.h: |
-// "The implementation must reserve static storage for a |
-// gss_OID_desc object for each constant. That constant |
-// should be initialized to point to that gss_OID_desc." |
-// These are encoded using ASN.1 BER encoding. |
-namespace { |
- |
-static gss_OID_desc GSS_C_NT_USER_NAME_VAL = { |
- 10, |
- const_cast<char*>("\x2a\x86\x48\x86\xf7\x12\x01\x02\x01\x01") |
-}; |
-static gss_OID_desc GSS_C_NT_MACHINE_UID_NAME_VAL = { |
- 10, |
- const_cast<char*>("\x2a\x86\x48\x86\xf7\x12\x01\x02\x01\x02") |
-}; |
-static gss_OID_desc GSS_C_NT_STRING_UID_NAME_VAL = { |
- 10, |
- const_cast<char*>("\x2a\x86\x48\x86\xf7\x12\x01\x02\x01\x03") |
-}; |
-static gss_OID_desc GSS_C_NT_HOSTBASED_SERVICE_X_VAL = { |
- 6, |
- const_cast<char*>("\x2b\x06\x01\x05\x06\x02") |
-}; |
-static gss_OID_desc GSS_C_NT_HOSTBASED_SERVICE_VAL = { |
- 10, |
- const_cast<char*>("\x2a\x86\x48\x86\xf7\x12\x01\x02\x01\x04") |
-}; |
-static gss_OID_desc GSS_C_NT_ANONYMOUS_VAL = { |
- 6, |
- const_cast<char*>("\x2b\x06\01\x05\x06\x03") |
-}; |
-static gss_OID_desc GSS_C_NT_EXPORT_NAME_VAL = { |
- 6, |
- const_cast<char*>("\x2b\x06\x01\x05\x06\x04") |
-}; |
- |
-} // namespace |
- |
-// Heimdal >= 1.4 will define the following as preprocessor macros. |
-// To avoid conflicting declarations, we have to undefine these. |
-#undef GSS_C_NT_USER_NAME |
-#undef GSS_C_NT_MACHINE_UID_NAME |
-#undef GSS_C_NT_STRING_UID_NAME |
-#undef GSS_C_NT_HOSTBASED_SERVICE_X |
-#undef GSS_C_NT_HOSTBASED_SERVICE |
-#undef GSS_C_NT_ANONYMOUS |
-#undef GSS_C_NT_EXPORT_NAME |
- |
-gss_OID GSS_C_NT_USER_NAME = &GSS_C_NT_USER_NAME_VAL; |
-gss_OID GSS_C_NT_MACHINE_UID_NAME = &GSS_C_NT_MACHINE_UID_NAME_VAL; |
-gss_OID GSS_C_NT_STRING_UID_NAME = &GSS_C_NT_STRING_UID_NAME_VAL; |
-gss_OID GSS_C_NT_HOSTBASED_SERVICE_X = &GSS_C_NT_HOSTBASED_SERVICE_X_VAL; |
-gss_OID GSS_C_NT_HOSTBASED_SERVICE = &GSS_C_NT_HOSTBASED_SERVICE_VAL; |
-gss_OID GSS_C_NT_ANONYMOUS = &GSS_C_NT_ANONYMOUS_VAL; |
-gss_OID GSS_C_NT_EXPORT_NAME = &GSS_C_NT_EXPORT_NAME_VAL; |
- |
-namespace net { |
- |
-// Exported mechanism for GSSAPI. We always use SPNEGO: |
- |
-// iso.org.dod.internet.security.mechanism.snego (1.3.6.1.5.5.2) |
-gss_OID_desc CHROME_GSS_SPNEGO_MECH_OID_DESC_VAL = { |
- 6, |
- const_cast<char*>("\x2b\x06\x01\x05\x05\x02") |
-}; |
- |
-gss_OID CHROME_GSS_SPNEGO_MECH_OID_DESC = |
- &CHROME_GSS_SPNEGO_MECH_OID_DESC_VAL; |
- |
-// Debugging helpers. |
-namespace { |
- |
-std::string DisplayStatus(OM_uint32 major_status, |
- OM_uint32 minor_status) { |
- if (major_status == GSS_S_COMPLETE) |
- return "OK"; |
- return base::StringPrintf("0x%08X 0x%08X", major_status, minor_status); |
-} |
- |
-std::string DisplayCode(GSSAPILibrary* gssapi_lib, |
- OM_uint32 status, |
- OM_uint32 status_code_type) { |
- const int kMaxDisplayIterations = 8; |
- const size_t kMaxMsgLength = 4096; |
- // msg_ctx needs to be outside the loop because it is invoked multiple times. |
- OM_uint32 msg_ctx = 0; |
- std::string rv = base::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. As an added sanity check, no |
- // individual message may exceed |kMaxMsgLength|, and the final result |
- // will not exceed |kMaxMsgLength|*2-1. |
- for (int i = 0; i < kMaxDisplayIterations && rv.size() < kMaxMsgLength; |
- ++i) { |
- OM_uint32 min_stat; |
- gss_buffer_desc_struct msg = GSS_C_EMPTY_BUFFER; |
- OM_uint32 maj_stat = |
- gssapi_lib->display_status(&min_stat, status, status_code_type, |
- GSS_C_NULL_OID, &msg_ctx, &msg); |
- if (maj_stat == GSS_S_COMPLETE) { |
- int msg_len = (msg.length > kMaxMsgLength) ? |
- static_cast<int>(kMaxMsgLength) : |
- static_cast<int>(msg.length); |
- if (msg_len > 0 && msg.value != NULL) { |
- rv += base::StringPrintf(" %.*s", msg_len, |
- static_cast<char*>(msg.value)); |
- } |
- } |
- gssapi_lib->release_buffer(&min_stat, &msg); |
- if (!msg_ctx) |
- break; |
- } |
- return rv; |
-} |
- |
-std::string DisplayExtendedStatus(GSSAPILibrary* gssapi_lib, |
- OM_uint32 major_status, |
- 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 base::StringPrintf("Major: %s | Minor: %s", major.c_str(), |
- minor.c_str()); |
-} |
- |
-// ScopedName releases a gss_name_t when it goes out of scope. |
-class ScopedName { |
- public: |
- ScopedName(gss_name_t name, |
- GSSAPILibrary* gssapi_lib) |
- : name_(name), |
- gssapi_lib_(gssapi_lib) { |
- DCHECK(gssapi_lib_); |
- } |
- |
- ~ScopedName() { |
- if (name_ != GSS_C_NO_NAME) { |
- OM_uint32 minor_status = 0; |
- 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: |
- gss_name_t name_; |
- GSSAPILibrary* gssapi_lib_; |
- |
- DISALLOW_COPY_AND_ASSIGN(ScopedName); |
-}; |
- |
-// ScopedBuffer releases a gss_buffer_t when it goes out of scope. |
-class ScopedBuffer { |
- public: |
- ScopedBuffer(gss_buffer_t buffer, |
- GSSAPILibrary* gssapi_lib) |
- : buffer_(buffer), |
- gssapi_lib_(gssapi_lib) { |
- DCHECK(gssapi_lib_); |
- } |
- |
- ~ScopedBuffer() { |
- if (buffer_ != GSS_C_NO_BUFFER) { |
- OM_uint32 minor_status = 0; |
- 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: |
- gss_buffer_t buffer_; |
- GSSAPILibrary* gssapi_lib_; |
- |
- DISALLOW_COPY_AND_ASSIGN(ScopedBuffer); |
-}; |
- |
-namespace { |
- |
-std::string AppendIfPredefinedValue(gss_OID oid, |
- gss_OID predefined_oid, |
- const char* predefined_oid_name) { |
- DCHECK(oid); |
- DCHECK(predefined_oid); |
- DCHECK(predefined_oid_name); |
- std::string output; |
- if (oid->length != predefined_oid->length) |
- return output; |
- if (0 != memcmp(oid->elements, |
- predefined_oid->elements, |
- predefined_oid->length)) |
- return output; |
- |
- output += " ("; |
- output += predefined_oid_name; |
- output += ")"; |
- return output; |
-} |
- |
-} // namespace |
- |
-std::string DescribeOid(GSSAPILibrary* gssapi_lib, const gss_OID oid) { |
- if (!oid) |
- return "<NULL>"; |
- std::string output; |
- const size_t kMaxCharsToPrint = 1024; |
- OM_uint32 byte_length = oid->length; |
- size_t char_length = byte_length / sizeof(char); |
- if (char_length > kMaxCharsToPrint) { |
- // This might be a plain ASCII string. |
- // Check if the first |kMaxCharsToPrint| characters |
- // contain only printable characters and are NULL terminated. |
- const char* str = reinterpret_cast<const char*>(oid); |
- size_t str_length = 0; |
- for ( ; str_length < kMaxCharsToPrint; ++str_length) { |
- if (!str[str_length] || !isprint(str[str_length])) |
- break; |
- } |
- if (!str[str_length]) { |
- output += base::StringPrintf("\"%s\"", str); |
- return output; |
- } |
- } |
- output = base::StringPrintf("(%u) \"", byte_length); |
- if (!oid->elements) { |
- output += "<NULL>"; |
- return output; |
- } |
- const unsigned char* elements = |
- reinterpret_cast<const unsigned char*>(oid->elements); |
- // Don't print more than |kMaxCharsToPrint| characters. |
- size_t i = 0; |
- for ( ; (i < byte_length) && (i < kMaxCharsToPrint); ++i) { |
- output += base::StringPrintf("\\x%02X", elements[i]); |
- } |
- if (i >= kMaxCharsToPrint) |
- output += "..."; |
- output += "\""; |
- |
- // Check if the OID is one of the predefined values. |
- output += AppendIfPredefinedValue(oid, |
- GSS_C_NT_USER_NAME, |
- "GSS_C_NT_USER_NAME"); |
- output += AppendIfPredefinedValue(oid, |
- GSS_C_NT_MACHINE_UID_NAME, |
- "GSS_C_NT_MACHINE_UID_NAME"); |
- output += AppendIfPredefinedValue(oid, |
- GSS_C_NT_STRING_UID_NAME, |
- "GSS_C_NT_STRING_UID_NAME"); |
- output += AppendIfPredefinedValue(oid, |
- GSS_C_NT_HOSTBASED_SERVICE_X, |
- "GSS_C_NT_HOSTBASED_SERVICE_X"); |
- output += AppendIfPredefinedValue(oid, |
- GSS_C_NT_HOSTBASED_SERVICE, |
- "GSS_C_NT_HOSTBASED_SERVICE"); |
- output += AppendIfPredefinedValue(oid, |
- GSS_C_NT_ANONYMOUS, |
- "GSS_C_NT_ANONYMOUS"); |
- output += AppendIfPredefinedValue(oid, |
- GSS_C_NT_EXPORT_NAME, |
- "GSS_C_NT_EXPORT_NAME"); |
- |
- return output; |
-} |
- |
-std::string DescribeName(GSSAPILibrary* gssapi_lib, const gss_name_t name) { |
- OM_uint32 major_status = 0; |
- OM_uint32 minor_status = 0; |
- gss_buffer_desc_struct output_name_buffer = GSS_C_EMPTY_BUFFER; |
- gss_OID_desc output_name_type_desc = GSS_C_EMPTY_BUFFER; |
- gss_OID output_name_type = &output_name_type_desc; |
- major_status = gssapi_lib->display_name(&minor_status, |
- name, |
- &output_name_buffer, |
- &output_name_type); |
- ScopedBuffer scoped_output_name(&output_name_buffer, gssapi_lib); |
- if (major_status != GSS_S_COMPLETE) { |
- std::string error = |
- base::StringPrintf("Unable to describe name 0x%p, %s", |
- name, |
- DisplayExtendedStatus(gssapi_lib, |
- major_status, |
- minor_status).c_str()); |
- return error; |
- } |
- int len = output_name_buffer.length; |
- std::string description = base::StringPrintf( |
- "%*s (Type %s)", |
- len, |
- reinterpret_cast<const char*>(output_name_buffer.value), |
- DescribeOid(gssapi_lib, output_name_type).c_str()); |
- return description; |
-} |
- |
-std::string DescribeContext(GSSAPILibrary* gssapi_lib, |
- const gss_ctx_id_t context_handle) { |
- OM_uint32 major_status = 0; |
- OM_uint32 minor_status = 0; |
- gss_name_t src_name = GSS_C_NO_NAME; |
- gss_name_t targ_name = GSS_C_NO_NAME; |
- OM_uint32 lifetime_rec = 0; |
- gss_OID mech_type = GSS_C_NO_OID; |
- OM_uint32 ctx_flags = 0; |
- int locally_initiated = 0; |
- int open = 0; |
- if (context_handle == GSS_C_NO_CONTEXT) |
- return std::string("Context: GSS_C_NO_CONTEXT"); |
- major_status = gssapi_lib->inquire_context(&minor_status, |
- context_handle, |
- &src_name, |
- &targ_name, |
- &lifetime_rec, |
- &mech_type, |
- &ctx_flags, |
- &locally_initiated, |
- &open); |
- ScopedName(src_name, gssapi_lib); |
- ScopedName(targ_name, gssapi_lib); |
- if (major_status != GSS_S_COMPLETE) { |
- std::string error = |
- base::StringPrintf("Unable to describe context 0x%p, %s", |
- context_handle, |
- DisplayExtendedStatus(gssapi_lib, |
- major_status, |
- minor_status).c_str()); |
- return error; |
- } |
- std::string source(DescribeName(gssapi_lib, src_name)); |
- std::string target(DescribeName(gssapi_lib, targ_name)); |
- std::string description = base::StringPrintf("Context 0x%p: " |
- "Source \"%s\", " |
- "Target \"%s\", " |
- "lifetime %d, " |
- "mechanism %s, " |
- "flags 0x%08X, " |
- "local %d, " |
- "open %d", |
- context_handle, |
- source.c_str(), |
- target.c_str(), |
- lifetime_rec, |
- DescribeOid(gssapi_lib, |
- mech_type).c_str(), |
- ctx_flags, |
- locally_initiated, |
- open); |
- return description; |
-} |
- |
-} // namespace |
- |
-GSSAPISharedLibrary::GSSAPISharedLibrary(const std::string& gssapi_library_name) |
- : initialized_(false), |
- gssapi_library_name_(gssapi_library_name), |
- gssapi_library_(NULL), |
- import_name_(NULL), |
- release_name_(NULL), |
- release_buffer_(NULL), |
- display_name_(NULL), |
- display_status_(NULL), |
- init_sec_context_(NULL), |
- wrap_size_limit_(NULL), |
- delete_sec_context_(NULL), |
- inquire_context_(NULL) { |
-} |
- |
-GSSAPISharedLibrary::~GSSAPISharedLibrary() { |
- if (gssapi_library_) { |
- base::UnloadNativeLibrary(gssapi_library_); |
- gssapi_library_ = NULL; |
- } |
-} |
- |
-bool GSSAPISharedLibrary::Init() { |
- if (!initialized_) |
- InitImpl(); |
- return initialized_; |
-} |
- |
-bool GSSAPISharedLibrary::InitImpl() { |
- DCHECK(!initialized_); |
-#if defined(DLOPEN_KERBEROS) |
- gssapi_library_ = LoadSharedLibrary(); |
- if (gssapi_library_ == NULL) |
- return false; |
-#endif // defined(DLOPEN_KERBEROS) |
- initialized_ = true; |
- return true; |
-} |
- |
-base::NativeLibrary GSSAPISharedLibrary::LoadSharedLibrary() { |
- const char* const* library_names; |
- size_t num_lib_names; |
- const char* user_specified_library[1]; |
- if (!gssapi_library_name_.empty()) { |
- user_specified_library[0] = gssapi_library_name_.c_str(); |
- library_names = user_specified_library; |
- num_lib_names = 1; |
- } else { |
- static const char* const kDefaultLibraryNames[] = { |
-#if defined(OS_MACOSX) |
- "libgssapi_krb5.dylib" // MIT Kerberos |
-#elif defined(OS_OPENBSD) |
- "libgssapi.so" // Heimdal - OpenBSD |
-#else |
- "libgssapi_krb5.so.2", // MIT Kerberos - FC, Suse10, Debian |
- "libgssapi.so.4", // Heimdal - Suse10, MDK |
- "libgssapi.so.2", // Heimdal - Gentoo |
- "libgssapi.so.1" // Heimdal - Suse9, CITI - FC, MDK, Suse10 |
-#endif |
- }; |
- library_names = kDefaultLibraryNames; |
- num_lib_names = arraysize(kDefaultLibraryNames); |
- } |
- |
- for (size_t i = 0; i < num_lib_names; ++i) { |
- const char* library_name = library_names[i]; |
- base::FilePath file_path(library_name); |
- |
- // TODO(asanka): Move library loading to a separate thread. |
- // http://crbug.com/66702 |
- base::ThreadRestrictions::ScopedAllowIO allow_io_temporarily; |
- base::NativeLibrary lib = base::LoadNativeLibrary(file_path, NULL); |
- if (lib) { |
- // Only return this library if we can bind the functions we need. |
- if (BindMethods(lib)) |
- return lib; |
- base::UnloadNativeLibrary(lib); |
- } |
- } |
- LOG(WARNING) << "Unable to find a compatible GSSAPI library"; |
- return NULL; |
-} |
- |
-#if defined(DLOPEN_KERBEROS) |
-#define BIND(lib, x) \ |
- DCHECK(lib); \ |
- gss_##x##_type x = reinterpret_cast<gss_##x##_type>( \ |
- base::GetFunctionPointerFromNativeLibrary(lib, "gss_" #x)); \ |
- if (x == NULL) { \ |
- LOG(WARNING) << "Unable to bind function \"" << "gss_" #x << "\""; \ |
- return false; \ |
- } |
-#else |
-#define BIND(lib, x) gss_##x##_type x = gss_##x |
-#endif |
- |
-bool GSSAPISharedLibrary::BindMethods(base::NativeLibrary lib) { |
- BIND(lib, import_name); |
- BIND(lib, release_name); |
- BIND(lib, release_buffer); |
- BIND(lib, display_name); |
- BIND(lib, display_status); |
- BIND(lib, init_sec_context); |
- BIND(lib, wrap_size_limit); |
- BIND(lib, delete_sec_context); |
- BIND(lib, inquire_context); |
- |
- import_name_ = import_name; |
- release_name_ = release_name; |
- release_buffer_ = release_buffer; |
- display_name_ = display_name; |
- display_status_ = display_status; |
- init_sec_context_ = init_sec_context; |
- wrap_size_limit_ = wrap_size_limit; |
- delete_sec_context_ = delete_sec_context; |
- inquire_context_ = inquire_context; |
- |
- return true; |
-} |
- |
-#undef BIND |
- |
-OM_uint32 GSSAPISharedLibrary::import_name( |
- OM_uint32* minor_status, |
- const gss_buffer_t input_name_buffer, |
- const gss_OID input_name_type, |
- gss_name_t* output_name) { |
- DCHECK(initialized_); |
- return import_name_(minor_status, input_name_buffer, input_name_type, |
- output_name); |
-} |
- |
-OM_uint32 GSSAPISharedLibrary::release_name( |
- OM_uint32* minor_status, |
- gss_name_t* input_name) { |
- DCHECK(initialized_); |
- return release_name_(minor_status, input_name); |
-} |
- |
-OM_uint32 GSSAPISharedLibrary::release_buffer( |
- OM_uint32* minor_status, |
- gss_buffer_t buffer) { |
- DCHECK(initialized_); |
- return release_buffer_(minor_status, buffer); |
-} |
- |
-OM_uint32 GSSAPISharedLibrary::display_name( |
- OM_uint32* minor_status, |
- const gss_name_t input_name, |
- gss_buffer_t output_name_buffer, |
- gss_OID* output_name_type) { |
- DCHECK(initialized_); |
- return display_name_(minor_status, |
- input_name, |
- output_name_buffer, |
- output_name_type); |
-} |
- |
-OM_uint32 GSSAPISharedLibrary::display_status( |
- OM_uint32* minor_status, |
- OM_uint32 status_value, |
- int status_type, |
- const gss_OID mech_type, |
- OM_uint32* message_context, |
- gss_buffer_t status_string) { |
- DCHECK(initialized_); |
- return display_status_(minor_status, status_value, status_type, mech_type, |
- message_context, status_string); |
-} |
- |
-OM_uint32 GSSAPISharedLibrary::init_sec_context( |
- OM_uint32* minor_status, |
- const gss_cred_id_t initiator_cred_handle, |
- gss_ctx_id_t* context_handle, |
- const gss_name_t target_name, |
- const gss_OID mech_type, |
- OM_uint32 req_flags, |
- OM_uint32 time_req, |
- const gss_channel_bindings_t input_chan_bindings, |
- const gss_buffer_t input_token, |
- gss_OID* actual_mech_type, |
- gss_buffer_t output_token, |
- OM_uint32* ret_flags, |
- OM_uint32* time_rec) { |
- DCHECK(initialized_); |
- 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, |
- output_token, |
- ret_flags, |
- time_rec); |
-} |
- |
-OM_uint32 GSSAPISharedLibrary::wrap_size_limit( |
- OM_uint32* minor_status, |
- const gss_ctx_id_t context_handle, |
- int conf_req_flag, |
- gss_qop_t qop_req, |
- OM_uint32 req_output_size, |
- OM_uint32* max_input_size) { |
- DCHECK(initialized_); |
- return wrap_size_limit_(minor_status, |
- context_handle, |
- conf_req_flag, |
- qop_req, |
- req_output_size, |
- max_input_size); |
-} |
- |
-OM_uint32 GSSAPISharedLibrary::delete_sec_context( |
- OM_uint32* minor_status, |
- gss_ctx_id_t* context_handle, |
- gss_buffer_t output_token) { |
- // This is called from the owner class' destructor, even if |
- // Init() is not called, so we can't assume |initialized_| |
- // is set. |
- if (!initialized_) |
- return 0; |
- return delete_sec_context_(minor_status, |
- context_handle, |
- output_token); |
-} |
- |
-OM_uint32 GSSAPISharedLibrary::inquire_context( |
- OM_uint32* minor_status, |
- const gss_ctx_id_t context_handle, |
- gss_name_t* src_name, |
- gss_name_t* targ_name, |
- OM_uint32* lifetime_rec, |
- gss_OID* mech_type, |
- OM_uint32* ctx_flags, |
- int* locally_initiated, |
- int* open) { |
- DCHECK(initialized_); |
- return inquire_context_(minor_status, |
- context_handle, |
- src_name, |
- targ_name, |
- lifetime_rec, |
- mech_type, |
- ctx_flags, |
- locally_initiated, |
- open); |
-} |
- |
-ScopedSecurityContext::ScopedSecurityContext(GSSAPILibrary* gssapi_lib) |
- : security_context_(GSS_C_NO_CONTEXT), |
- gssapi_lib_(gssapi_lib) { |
- DCHECK(gssapi_lib_); |
-} |
- |
-ScopedSecurityContext::~ScopedSecurityContext() { |
- if (security_context_ != GSS_C_NO_CONTEXT) { |
- gss_buffer_desc output_token = GSS_C_EMPTY_BUFFER; |
- OM_uint32 minor_status = 0; |
- OM_uint32 major_status = gssapi_lib_->delete_sec_context( |
- &minor_status, &security_context_, &output_token); |
- if (major_status != GSS_S_COMPLETE) { |
- LOG(WARNING) << "Problem releasing security_context. " |
- << DisplayStatus(major_status, minor_status); |
- } |
- security_context_ = GSS_C_NO_CONTEXT; |
- } |
-} |
- |
-HttpAuthGSSAPI::HttpAuthGSSAPI(GSSAPILibrary* library, |
- const std::string& scheme, |
- gss_OID gss_oid) |
- : scheme_(scheme), |
- gss_oid_(gss_oid), |
- library_(library), |
- scoped_sec_context_(library), |
- can_delegate_(false) { |
- DCHECK(library_); |
-} |
- |
-HttpAuthGSSAPI::~HttpAuthGSSAPI() { |
-} |
- |
-bool HttpAuthGSSAPI::Init() { |
- if (!library_) |
- return false; |
- return library_->Init(); |
-} |
- |
-bool HttpAuthGSSAPI::NeedsIdentity() const { |
- return decoded_server_auth_token_.empty(); |
-} |
- |
-bool HttpAuthGSSAPI::AllowsExplicitCredentials() const { |
- return false; |
-} |
- |
-void HttpAuthGSSAPI::Delegate() { |
- can_delegate_ = true; |
-} |
- |
-HttpAuth::AuthorizationResult HttpAuthGSSAPI::ParseChallenge( |
- HttpAuthChallengeTokenizer* tok) { |
- // Verify the challenge's auth-scheme. |
- if (!LowerCaseEqualsASCII(tok->scheme(), |
- base::StringToLowerASCII(scheme_).c_str())) |
- return HttpAuth::AUTHORIZATION_RESULT_INVALID; |
- |
- std::string encoded_auth_token = tok->base64_param(); |
- |
- if (encoded_auth_token.empty()) { |
- // If a context has already been established, an empty Negotiate challenge |
- // should be treated as a rejection of the current attempt. |
- if (scoped_sec_context_.get() != GSS_C_NO_CONTEXT) |
- return HttpAuth::AUTHORIZATION_RESULT_REJECT; |
- DCHECK(decoded_server_auth_token_.empty()); |
- return HttpAuth::AUTHORIZATION_RESULT_ACCEPT; |
- } else { |
- // If a context has not already been established, additional tokens should |
- // not be present in the auth challenge. |
- if (scoped_sec_context_.get() == GSS_C_NO_CONTEXT) |
- return HttpAuth::AUTHORIZATION_RESULT_INVALID; |
- } |
- |
- // Make sure the additional token is base64 encoded. |
- std::string decoded_auth_token; |
- bool base64_rv = base::Base64Decode(encoded_auth_token, &decoded_auth_token); |
- if (!base64_rv) |
- return HttpAuth::AUTHORIZATION_RESULT_INVALID; |
- decoded_server_auth_token_ = decoded_auth_token; |
- return HttpAuth::AUTHORIZATION_RESULT_ACCEPT; |
-} |
- |
-int HttpAuthGSSAPI::GenerateAuthToken(const AuthCredentials* credentials, |
- const std::string& spn, |
- std::string* auth_token) { |
- DCHECK(auth_token); |
- |
- gss_buffer_desc input_token = GSS_C_EMPTY_BUFFER; |
- input_token.length = decoded_server_auth_token_.length(); |
- input_token.value = (input_token.length > 0) ? |
- const_cast<char*>(decoded_server_auth_token_.data()) : |
- NULL; |
- gss_buffer_desc output_token = GSS_C_EMPTY_BUFFER; |
- ScopedBuffer scoped_output_token(&output_token, library_); |
- int rv = GetNextSecurityToken(spn, &input_token, &output_token); |
- if (rv != OK) |
- return rv; |
- |
- // 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; |
- base::Base64Encode(encode_input, &encode_output); |
- *auth_token = scheme_ + " " + encode_output; |
- return OK; |
-} |
- |
- |
-namespace { |
- |
-// GSSAPI status codes consist of a calling error (essentially, a programmer |
-// bug), a routine error (defined by the RFC), and supplementary information, |
-// all bitwise-or'ed together in different regions of the 32 bit return value. |
-// This means a simple switch on the return codes is not sufficient. |
- |
-int MapImportNameStatusToError(OM_uint32 major_status) { |
- VLOG(1) << "import_name returned 0x" << std::hex << major_status; |
- if (major_status == GSS_S_COMPLETE) |
- return OK; |
- if (GSS_CALLING_ERROR(major_status) != 0) |
- return ERR_UNEXPECTED; |
- OM_uint32 routine_error = GSS_ROUTINE_ERROR(major_status); |
- switch (routine_error) { |
- case GSS_S_FAILURE: |
- // Looking at the MIT Kerberos implementation, this typically is returned |
- // when memory allocation fails. However, the API does not guarantee |
- // that this is the case, so using ERR_UNEXPECTED rather than |
- // ERR_OUT_OF_MEMORY. |
- return ERR_UNEXPECTED_SECURITY_LIBRARY_STATUS; |
- case GSS_S_BAD_NAME: |
- case GSS_S_BAD_NAMETYPE: |
- return ERR_MALFORMED_IDENTITY; |
- case GSS_S_DEFECTIVE_TOKEN: |
- // Not mentioned in the API, but part of code. |
- return ERR_UNEXPECTED_SECURITY_LIBRARY_STATUS; |
- case GSS_S_BAD_MECH: |
- return ERR_UNSUPPORTED_AUTH_SCHEME; |
- default: |
- return ERR_UNDOCUMENTED_SECURITY_LIBRARY_STATUS; |
- } |
-} |
- |
-int MapInitSecContextStatusToError(OM_uint32 major_status) { |
- VLOG(1) << "init_sec_context returned 0x" << std::hex << major_status; |
- // Although GSS_S_CONTINUE_NEEDED is an additional bit, it seems like |
- // other code just checks if major_status is equivalent to it to indicate |
- // that there are no other errors included. |
- if (major_status == GSS_S_COMPLETE || major_status == GSS_S_CONTINUE_NEEDED) |
- return OK; |
- if (GSS_CALLING_ERROR(major_status) != 0) |
- return ERR_UNEXPECTED; |
- OM_uint32 routine_status = GSS_ROUTINE_ERROR(major_status); |
- switch (routine_status) { |
- case GSS_S_DEFECTIVE_TOKEN: |
- return ERR_INVALID_RESPONSE; |
- case GSS_S_DEFECTIVE_CREDENTIAL: |
- // Not expected since this implementation uses the default credential. |
- return ERR_UNEXPECTED_SECURITY_LIBRARY_STATUS; |
- case GSS_S_BAD_SIG: |
- // Probably won't happen, but it's a bad response. |
- return ERR_INVALID_RESPONSE; |
- case GSS_S_NO_CRED: |
- return ERR_INVALID_AUTH_CREDENTIALS; |
- case GSS_S_CREDENTIALS_EXPIRED: |
- return ERR_INVALID_AUTH_CREDENTIALS; |
- case GSS_S_BAD_BINDINGS: |
- // This only happens with mutual authentication. |
- return ERR_UNEXPECTED_SECURITY_LIBRARY_STATUS; |
- case GSS_S_NO_CONTEXT: |
- return ERR_UNEXPECTED_SECURITY_LIBRARY_STATUS; |
- case GSS_S_BAD_NAMETYPE: |
- return ERR_UNSUPPORTED_AUTH_SCHEME; |
- case GSS_S_BAD_NAME: |
- return ERR_UNSUPPORTED_AUTH_SCHEME; |
- case GSS_S_BAD_MECH: |
- return ERR_UNEXPECTED_SECURITY_LIBRARY_STATUS; |
- case GSS_S_FAILURE: |
- // This should be an "Unexpected Security Status" according to the |
- // GSSAPI documentation, but it's typically used to indicate that |
- // credentials are not correctly set up on a user machine, such |
- // as a missing credential cache or hitting this after calling |
- // kdestroy. |
- // TODO(cbentzel): Use minor code for even better mapping? |
- return ERR_MISSING_AUTH_CREDENTIALS; |
- default: |
- if (routine_status != 0) |
- return ERR_UNDOCUMENTED_SECURITY_LIBRARY_STATUS; |
- break; |
- } |
- OM_uint32 supplemental_status = GSS_SUPPLEMENTARY_INFO(major_status); |
- // Replays could indicate an attack. |
- if (supplemental_status & (GSS_S_DUPLICATE_TOKEN | GSS_S_OLD_TOKEN | |
- GSS_S_UNSEQ_TOKEN | GSS_S_GAP_TOKEN)) |
- return ERR_INVALID_RESPONSE; |
- |
- // At this point, every documented status has been checked. |
- return ERR_UNDOCUMENTED_SECURITY_LIBRARY_STATUS; |
-} |
- |
-} |
- |
-int HttpAuthGSSAPI::GetNextSecurityToken(const std::string& spn, |
- gss_buffer_t in_token, |
- gss_buffer_t out_token) { |
- // Create a name for the principal |
- // TODO(cbentzel): Just do this on the first pass? |
- std::string spn_principal = spn; |
- gss_buffer_desc spn_buffer = GSS_C_EMPTY_BUFFER; |
- spn_buffer.value = const_cast<char*>(spn_principal.c_str()); |
- spn_buffer.length = spn_principal.size() + 1; |
- OM_uint32 minor_status = 0; |
- gss_name_t principal_name = GSS_C_NO_NAME; |
- OM_uint32 major_status = library_->import_name( |
- &minor_status, |
- &spn_buffer, |
- GSS_C_NT_HOSTBASED_SERVICE, |
- &principal_name); |
- int rv = MapImportNameStatusToError(major_status); |
- if (rv != OK) { |
- LOG(ERROR) << "Problem importing name from " |
- << "spn \"" << spn_principal << "\"\n" |
- << DisplayExtendedStatus(library_, major_status, minor_status); |
- return rv; |
- } |
- ScopedName scoped_name(principal_name, library_); |
- |
- // Continue creating a security context. |
- OM_uint32 req_flags = 0; |
- if (can_delegate_) |
- req_flags |= GSS_C_DELEG_FLAG; |
- major_status = library_->init_sec_context( |
- &minor_status, |
- GSS_C_NO_CREDENTIAL, |
- scoped_sec_context_.receive(), |
- 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); |
- rv = MapInitSecContextStatusToError(major_status); |
- if (rv != OK) { |
- LOG(ERROR) << "Problem initializing context. \n" |
- << DisplayExtendedStatus(library_, major_status, minor_status) |
- << '\n' |
- << DescribeContext(library_, scoped_sec_context_.get()); |
- } |
- return rv; |
-} |
- |
-} // namespace net |