diff options
author | ahendrickson@google.com <ahendrickson@google.com@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-06-28 18:03:04 +0000 |
---|---|---|
committer | ahendrickson@google.com <ahendrickson@google.com@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-06-28 18:03:04 +0000 |
commit | aef042746af7a3a9a9967195c24cb7a9c755a0e8 (patch) | |
tree | 2c0aa7088f240c63f1f12b2bd19651314cdfc928 /net/http | |
parent | f9f41ec4f27ba5fd19ca82d4c04b13bed6627d23 (diff) | |
download | chromium_src-aef042746af7a3a9a9967195c24cb7a9c755a0e8.zip chromium_src-aef042746af7a3a9a9967195c24cb7a9c755a0e8.tar.gz chromium_src-aef042746af7a3a9a9967195c24cb7a9c755a0e8.tar.bz2 |
Hooking GSSAPI code into HTTP authenticate Negotiate handler.
BUG=33033.
TEST=None.
Review URL: http://codereview.chromium.org/2684001
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@51001 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'net/http')
-rw-r--r-- | net/http/http_auth_gssapi_posix.cc | 473 | ||||
-rw-r--r-- | net/http/http_auth_gssapi_posix.h | 40 | ||||
-rw-r--r-- | net/http/http_auth_handler_factory_unittest.cc | 14 | ||||
-rw-r--r-- | net/http/http_auth_handler_negotiate.h | 33 | ||||
-rw-r--r-- | net/http/http_auth_handler_negotiate_posix.cc | 197 | ||||
-rw-r--r-- | net/http/http_auth_unittest.cc | 75 | ||||
-rw-r--r-- | net/http/http_network_transaction.cc | 53 | ||||
-rw-r--r-- | net/http/http_network_transaction.h | 3 | ||||
-rw-r--r-- | net/http/http_network_transaction_unittest.cc | 18 | ||||
-rw-r--r-- | net/http/http_response_headers.cc | 24 | ||||
-rw-r--r-- | net/http/http_response_headers.h | 3 |
11 files changed, 717 insertions, 216 deletions
diff --git a/net/http/http_auth_gssapi_posix.cc b/net/http/http_auth_gssapi_posix.cc index 6e41bdc..94e2b11 100644 --- a/net/http/http_auth_gssapi_posix.cc +++ b/net/http/http_auth_gssapi_posix.cc @@ -5,6 +5,7 @@ #include "net/http/http_auth_gssapi_posix.h" #include <limits> +#include <string> #include "base/base64.h" #include "base/file_path.h" @@ -14,12 +15,67 @@ #include "net/base/net_errors.h" #include "net/base/net_util.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." +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 + +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 { +// These are encoded using ASN.1 BER encoding. + +// This one is used by Firefox's nsAuthGSSAPI class. +gss_OID_desc CHROME_GSS_KRB5_MECH_OID_DESC_VAL = { + 9, + const_cast<char *>("\x2a\x86\x48\x86\xf7\x12\x01\x02\x02") +}; + gss_OID_desc CHROME_GSS_C_NT_HOSTBASED_SERVICE_X_VAL = { 6, const_cast<char *>("\x2b\x06\x01\x05\x06\x02") }; + gss_OID_desc CHROME_GSS_C_NT_HOSTBASED_SERVICE_VAL = { 10, const_cast<char *>("\x2a\x86\x48\x86\xf7\x12\x01\x02\x01\x04") @@ -29,6 +85,229 @@ gss_OID CHROME_GSS_C_NT_HOSTBASED_SERVICE_X = &CHROME_GSS_C_NT_HOSTBASED_SERVICE_X_VAL; gss_OID CHROME_GSS_C_NT_HOSTBASED_SERVICE = &CHROME_GSS_C_NT_HOSTBASED_SERVICE_VAL; +gss_OID CHROME_GSS_KRB5_MECH_OID_DESC = + &CHROME_GSS_KRB5_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 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 = 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 += 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 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); +}; + + +std::string DescribeOid(GSSAPILibrary* gssapi_lib, const gss_OID oid) { + if (!oid) + return "<NULL>"; + std::string output; + size_t i; + output = StringPrintf("(%d) \"", oid->length); + if (!oid->elements) { + output += "<NULL>"; + return output; + } + const unsigned char* elements = + reinterpret_cast<const unsigned char *>(oid->elements); + for (i = 0; i < oid->length; ++i) { + output += StringPrintf("\\x%02X", elements[i]); + } + output += "\""; + 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 = + 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 = + 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_name_t targ_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; + 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 = + 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 = 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() : initialized_(false), @@ -36,10 +315,12 @@ GSSAPISharedLibrary::GSSAPISharedLibrary() 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) { + delete_sec_context_(NULL), + inquire_context_(NULL) { } GSSAPISharedLibrary::~GSSAPISharedLibrary() { @@ -69,9 +350,9 @@ base::NativeLibrary GSSAPISharedLibrary::LoadSharedLibrary() { #if defined(OS_MACOSX) "libgssapi_krb5.dylib" // MIT Kerberos #else - "libgssapi_krb5.so.2", // MIT Kerberos - "libgssapi.so.4", // Heimdal - "libgssapi.so.1" // Heimdal + "libgssapi_krb5.so.2", // MIT Kerberos - FC, Suse10, Debian + "libgssapi.so.4", // Heimdal - Suse10, MDK + "libgssapi.so.1" // Heimdal - Suse9, CITI - FC, MDK, Suse10 #endif }; static size_t num_lib_names = arraysize(kLibraryNames); @@ -105,18 +386,22 @@ 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; } @@ -147,6 +432,18 @@ OM_uint32 GSSAPISharedLibrary::release_buffer( 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, @@ -219,128 +516,31 @@ OM_uint32 GSSAPISharedLibrary::delete_sec_context( 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); +} GSSAPILibrary* GSSAPILibrary::GetDefault() { return Singleton<GSSAPISharedLibrary>::get(); } -namespace { - -std::string DisplayStatus(OM_uint32 major_status, - OM_uint32 minor_status) { - if (major_status == GSS_S_COMPLETE) - return "OK"; - return 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 = 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 += 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 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 - ScopedSecurityContext::ScopedSecurityContext(GSSAPILibrary* gssapi_lib) : security_context_(GSS_C_NO_CONTEXT), gssapi_lib_(gssapi_lib) { @@ -374,6 +574,12 @@ HttpAuthGSSAPI::HttpAuthGSSAPI(GSSAPILibrary* library, HttpAuthGSSAPI::~HttpAuthGSSAPI() { } +bool HttpAuthGSSAPI::Init() { + if (!library_) + return false; + return library_->Init(); +} + bool HttpAuthGSSAPI::NeedsIdentity() const { return decoded_server_auth_token_.empty(); } @@ -409,16 +615,9 @@ int HttpAuthGSSAPI::GenerateAuthToken(const std::wstring* username, const std::wstring* password, const std::wstring& spn, const HttpRequestInfo* request, - const ProxyInfo* proxy, std::string* auth_token) { - DCHECK(library_); DCHECK((username == NULL) == (password == NULL)); - bool initialized = library_->Init(); - - if (!initialized) - return ERR_UNSUPPORTED_AUTH_SCHEME; - if (!IsFinalRound()) { int rv = OnFirstRound(username, password); if (rv != OK) @@ -427,7 +626,10 @@ int HttpAuthGSSAPI::GenerateAuthToken(const std::wstring* username, gss_buffer_desc input_token = GSS_C_EMPTY_BUFFER; input_token.length = decoded_server_auth_token_.length(); - input_token.value = const_cast<char *>(decoded_server_auth_token_.data()); + 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); @@ -462,15 +664,13 @@ int HttpAuthGSSAPI::GetNextSecurityToken(const std::wstring& spn, gss_buffer_t in_token, gss_buffer_t out_token) { // Create a name for the principal - // TODO(cbentzel): Should this be username@spn? What about domain? // TODO(cbentzel): Just do this on the first pass? - const GURL spn_url(WideToASCII(spn)); - std::string spn_principal = GetHostAndPort(spn_url); + std::string spn_principal = WideToASCII(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_name_t principal_name = GSS_C_NO_NAME; OM_uint32 major_status = library_->import_name( &minor_status, &spn_buffer, @@ -504,9 +704,12 @@ int HttpAuthGSSAPI::GetNextSecurityToken(const std::wstring& spn, if (major_status != GSS_S_COMPLETE && major_status != GSS_S_CONTINUE_NEEDED) { LOG(ERROR) << "Problem initializing context. " + << std::endl << DisplayExtendedStatus(library_, major_status, - minor_status); + minor_status) + << std::endl + << DescribeContext(library_, scoped_sec_context_.get()); return ERR_UNEXPECTED; } diff --git a/net/http/http_auth_gssapi_posix.h b/net/http/http_auth_gssapi_posix.h index 74089ba..d42a523 100644 --- a/net/http/http_auth_gssapi_posix.h +++ b/net/http/http_auth_gssapi_posix.h @@ -19,10 +19,10 @@ class GURL; namespace net { class HttpRequestInfo; -class ProxyInfo; extern gss_OID CHROME_GSS_C_NT_HOSTBASED_SERVICE_X; extern gss_OID CHROME_GSS_C_NT_HOSTBASED_SERVICE; +extern gss_OID CHROME_GSS_KRB5_MECH_OID_DESC; // GSSAPILibrary is introduced so unit tests can mock the calls to the GSSAPI // library. The default implementation attempts to load one of the standard @@ -49,6 +49,11 @@ class GSSAPILibrary { virtual OM_uint32 release_buffer( OM_uint32* minor_status, gss_buffer_t buffer) = 0; + virtual OM_uint32 display_name( + OM_uint32* minor_status, + const gss_name_t input_name, + gss_buffer_t output_name_buffer, + gss_OID* output_name_type) = 0; virtual OM_uint32 display_status( OM_uint32* minor_status, OM_uint32 status_value, @@ -81,6 +86,16 @@ class GSSAPILibrary { OM_uint32* minor_status, gss_ctx_id_t* context_handle, gss_buffer_t output_token) = 0; + virtual OM_uint32 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) = 0; // Get the default GSSPILibrary instance. The object returned is a singleton // instance, and the caller should not delete it. @@ -106,6 +121,11 @@ class GSSAPISharedLibrary : public GSSAPILibrary { virtual OM_uint32 release_buffer( OM_uint32* minor_status, gss_buffer_t buffer); + virtual OM_uint32 display_name( + OM_uint32* minor_status, + const gss_name_t input_name, + gss_buffer_t output_name_buffer, + gss_OID* output_name_type); virtual OM_uint32 display_status( OM_uint32* minor_status, OM_uint32 status_value, @@ -138,6 +158,16 @@ class GSSAPISharedLibrary : public GSSAPILibrary { OM_uint32* minor_status, gss_ctx_id_t* context_handle, gss_buffer_t output_token); + virtual OM_uint32 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); private: FRIEND_TEST_ALL_PREFIXES(HttpAuthGSSAPIPOSIXTest, GSSAPIStartup); @@ -158,10 +188,12 @@ class GSSAPISharedLibrary : public GSSAPILibrary { gss_import_name_type import_name_; gss_release_name_type release_name_; gss_release_buffer_type release_buffer_; + gss_display_name_type display_name_; gss_display_status_type display_status_; gss_init_sec_context_type init_sec_context_; gss_wrap_size_limit_type wrap_size_limit_; gss_delete_sec_context_type delete_sec_context_; + gss_inquire_context_type inquire_context_; }; // ScopedSecurityContext releases a gss_ctx_id_t when it goes out of @@ -171,6 +203,7 @@ class ScopedSecurityContext { ScopedSecurityContext(GSSAPILibrary* gssapi_lib); ~ScopedSecurityContext(); + const gss_ctx_id_t get() const { return security_context_; } gss_ctx_id_t* receive() { return &security_context_; } private: @@ -181,7 +214,7 @@ class ScopedSecurityContext { }; -// TODO(cbentzel): Share code with HttpAuthSSPI. +// TODO(ahendrickson): Share code with HttpAuthSSPI. class HttpAuthGSSAPI { public: HttpAuthGSSAPI(GSSAPILibrary* library, @@ -189,6 +222,8 @@ class HttpAuthGSSAPI { const gss_OID gss_oid); ~HttpAuthGSSAPI(); + bool Init(); + bool NeedsIdentity() const; bool IsFinalRound() const; @@ -206,7 +241,6 @@ class HttpAuthGSSAPI { const std::wstring* password, const std::wstring& spn, const HttpRequestInfo* request, - const ProxyInfo* proxy, std::string* auth_token); private: diff --git a/net/http/http_auth_handler_factory_unittest.cc b/net/http/http_auth_handler_factory_unittest.cc index bece442..526b62b 100644 --- a/net/http/http_auth_handler_factory_unittest.cc +++ b/net/http/http_auth_handler_factory_unittest.cc @@ -154,7 +154,6 @@ TEST(HttpAuthHandlerFactoryTest, DefaultFactory) { EXPECT_TRUE(handler->encrypts_identity()); EXPECT_TRUE(handler->is_connection_based()); } -#if defined(OS_WIN) { scoped_ptr<HttpAuthHandler> handler; int rv = http_auth_handler_factory->CreateAuthHandlerFromString( @@ -171,19 +170,6 @@ TEST(HttpAuthHandlerFactoryTest, DefaultFactory) { EXPECT_TRUE(handler->encrypts_identity()); EXPECT_TRUE(handler->is_connection_based()); } -#else // !defined(OS_WIN) - { - scoped_ptr<HttpAuthHandler> handler; - int rv = http_auth_handler_factory->CreateAuthHandlerFromString( - "Negotiate", - HttpAuth::AUTH_SERVER, - server_origin, - BoundNetLog(), - &handler); - EXPECT_EQ(ERR_UNSUPPORTED_AUTH_SCHEME, rv); - EXPECT_TRUE(handler.get() == NULL); - } -#endif // !defined(OS_WIN) } } // namespace net diff --git a/net/http/http_auth_handler_negotiate.h b/net/http/http_auth_handler_negotiate.h index c1414ec..687df8a 100644 --- a/net/http/http_auth_handler_negotiate.h +++ b/net/http/http_auth_handler_negotiate.h @@ -5,10 +5,10 @@ #ifndef NET_HTTP_HTTP_AUTH_HANDLER_NEGOTIATE_H_ #define NET_HTTP_HTTP_AUTH_HANDLER_NEGOTIATE_H_ -#include "build/build_config.h" - #include <string> +#include "build/build_config.h" + #include "net/base/address_list.h" #include "net/http/http_auth_handler.h" #include "net/http/http_auth_handler_factory.h" @@ -17,6 +17,10 @@ #include "net/http/http_auth_sspi_win.h" #endif +#if defined(OS_POSIX) +#include "net/http/http_auth_gssapi_posix.h" +#endif + namespace net { class SingleRequestHostResolver; @@ -78,14 +82,22 @@ class HttpAuthHandlerNegotiate : public HttpAuthHandler { bool is_unsupported_; SSPILibrary* sspi_library_; #endif // defined(OS_WIN) + +#if defined(OS_POSIX) + GSSAPILibrary* gssapi_library_; +#endif }; #if defined(OS_WIN) HttpAuthHandlerNegotiate(SSPILibrary* sspi_library, ULONG max_token_length, URLSecurityManager* url_security_manager, bool disable_cname_lookup, bool use_port); -#else - explicit HttpAuthHandlerNegotiate(URLSecurityManager* url_security_manager); +#endif + +#if defined(OS_POSIX) + HttpAuthHandlerNegotiate(GSSAPILibrary* gssapi_library, + URLSecurityManager* url_security_manager, + bool disable_cname_lookup, bool use_port); #endif virtual ~HttpAuthHandlerNegotiate(); @@ -101,11 +113,9 @@ class HttpAuthHandlerNegotiate : public HttpAuthHandler { virtual int ResolveCanonicalName(HostResolver* host_resolver, CompletionCallback* callback); -#if defined(OS_WIN) // These are public for unit tests std::wstring CreateSPN(const AddressList& address_list, const GURL& orign); const std::wstring& spn() const { return spn_; } -#endif // defined(OS_WIN) protected: virtual bool Init(HttpAuth::ChallengeTokenizer* challenge); @@ -117,9 +127,16 @@ class HttpAuthHandlerNegotiate : public HttpAuthHandler { std::string* auth_token); private: -#if defined(OS_WIN) void OnResolveCanonicalName(int result); + +#if defined(OS_WIN) HttpAuthSSPI auth_sspi_; +#endif + +#if defined(OS_POSIX) + HttpAuthGSSAPI auth_gssapi_; +#endif + AddressList address_list_; scoped_ptr<SingleRequestHostResolver> single_resolve_; CompletionCallback* user_callback_; @@ -127,8 +144,6 @@ class HttpAuthHandlerNegotiate : public HttpAuthHandler { bool disable_cname_lookup_; bool use_port_; std::wstring spn_; -#endif - URLSecurityManager* url_security_manager_; }; diff --git a/net/http/http_auth_handler_negotiate_posix.cc b/net/http/http_auth_handler_negotiate_posix.cc index 17ef690..a863fe4 100644 --- a/net/http/http_auth_handler_negotiate_posix.cc +++ b/net/http/http_auth_handler_negotiate_posix.cc @@ -5,70 +5,185 @@ #include "net/http/http_auth_handler_negotiate.h" #include "base/logging.h" +#include "base/string_util.h" +#include "net/base/address_family.h" +#include "net/base/host_resolver.h" #include "net/base/net_errors.h" +#include "net/http/http_auth_filter.h" +#include "net/http/http_auth_gssapi_posix.h" +#include "net/http/url_security_manager.h" namespace net { -// TODO(ahendrickson): Implement via GSSAPI. - -// TODO(cbentzel): Negotiate authentication protocol is not supported on Posix -// systems currently. These stubs make the main HTTP Authentication code bypass -// Negotiate without requiring conditional compilation. - HttpAuthHandlerNegotiate::HttpAuthHandlerNegotiate( - URLSecurityManager* url_security_manager) - : url_security_manager_(url_security_manager) { + GSSAPILibrary* gssapi_library, + URLSecurityManager* url_security_manager, + bool disable_cname_lookup, + bool use_port) + : auth_gssapi_(gssapi_library, + "Negotiate", + CHROME_GSS_KRB5_MECH_OID_DESC), + user_callback_(NULL), + ALLOW_THIS_IN_INITIALIZER_LIST(resolve_cname_callback_( + this, &HttpAuthHandlerNegotiate::OnResolveCanonicalName)), + disable_cname_lookup_(disable_cname_lookup), + use_port_(use_port), + url_security_manager_(url_security_manager) { } HttpAuthHandlerNegotiate::~HttpAuthHandlerNegotiate() { } +int HttpAuthHandlerNegotiate::GenerateAuthTokenImpl( + const std::wstring* username, + const std::wstring* password, + const HttpRequestInfo* request, + CompletionCallback* callback, + std::string* auth_token) { + int rv = auth_gssapi_.GenerateAuthToken( + username, + password, + spn_, + request, + auth_token); + return rv; +} + +bool HttpAuthHandlerNegotiate::Init(HttpAuth::ChallengeTokenizer* challenge) { + if (!auth_gssapi_.Init()) { + LOG(INFO) << "can't initialize GSSAPI library"; + return false; + } + scheme_ = "negotiate"; + score_ = 4; + properties_ = ENCRYPTS_IDENTITY | IS_CONNECTION_BASED; + bool value = auth_gssapi_.ParseChallenge(challenge); + return value; +} + bool HttpAuthHandlerNegotiate::NeedsIdentity() { - NOTREACHED(); - LOG(ERROR) << ErrorToString(ERR_NOT_IMPLEMENTED); - return false; + bool value = auth_gssapi_.NeedsIdentity(); + return value; } bool HttpAuthHandlerNegotiate::IsFinalRound() { - NOTREACHED(); - LOG(ERROR) << ErrorToString(ERR_NOT_IMPLEMENTED); - return false; + bool value = auth_gssapi_.IsFinalRound(); + return value; } +// TODO(ahendrickson) -- Most of this code can be shared between Windows and +// Posix now. bool HttpAuthHandlerNegotiate::AllowsDefaultCredentials() { - NOTREACHED(); - LOG(ERROR) << ErrorToString(ERR_NOT_IMPLEMENTED); - return false; + bool allowed = false; + if (target_ == HttpAuth::AUTH_PROXY) + allowed = true; + else if (!url_security_manager_) + allowed = false; + else + allowed = url_security_manager_->CanUseDefaultCredentials(origin_); + return allowed; } -bool HttpAuthHandlerNegotiate::Init(HttpAuth::ChallengeTokenizer* tok) { - return false; +bool HttpAuthHandlerNegotiate::NeedsCanonicalName() { + bool needs_name = true; + if (!spn_.empty()) + needs_name = false; + else if (disable_cname_lookup_) { + spn_ = CreateSPN(address_list_, origin_); + address_list_.Reset(); + needs_name = false; + } + return needs_name; } -int HttpAuthHandlerNegotiate::GenerateAuthTokenImpl( - const std::wstring* username, - const std::wstring* password, - const HttpRequestInfo* request, - CompletionCallback* callback, - std::string* auth_token) { - NOTREACHED(); - LOG(ERROR) << ErrorToString(ERR_NOT_IMPLEMENTED); - return ERR_NOT_IMPLEMENTED; +int HttpAuthHandlerNegotiate::ResolveCanonicalName( + HostResolver* resolver, CompletionCallback* callback) { + // TODO(ahendrickson): Add reverse DNS lookup for numeric addresses. + DCHECK(!single_resolve_.get()); + DCHECK(!disable_cname_lookup_); + DCHECK(callback); + + HostResolver::RequestInfo info(origin_.host(), 0); + info.set_host_resolver_flags(HOST_RESOLVER_CANONNAME); + single_resolve_.reset(new SingleRequestHostResolver(resolver)); + int rv = single_resolve_->Resolve(info, &address_list_, + &resolve_cname_callback_, + net_log_); + if (rv == ERR_IO_PENDING) { + user_callback_ = callback; + } else { + OnResolveCanonicalName(rv); + // Always return OK. OnResolveCanonicalName logs the error code if not + // OK and attempts to use the original origin_ hostname rather than failing + // the auth attempt completely. + rv = OK; + } + return rv; } -bool HttpAuthHandlerNegotiate::NeedsCanonicalName() { - return false; +void HttpAuthHandlerNegotiate::OnResolveCanonicalName(int result) { + if (result != OK) { + // Even in the error case, try to use origin_.host instead of + // passing the failure on to the caller. + LOG(INFO) << "Problem finding canonical name for SPN for host " + << origin_.host() << ": " << ErrorToString(result); + result = OK; + } + spn_ = CreateSPN(address_list_, origin_); + address_list_.Reset(); + if (user_callback_) { + CompletionCallback* callback = user_callback_; + user_callback_ = NULL; + callback->Run(result); + } } -int HttpAuthHandlerNegotiate::ResolveCanonicalName( - HostResolver* host_resolver, CompletionCallback* callback) { - NOTREACHED(); - LOG(ERROR) << ErrorToString(ERR_NOT_IMPLEMENTED); - return ERR_NOT_IMPLEMENTED; +std::wstring HttpAuthHandlerNegotiate::CreateSPN( + const AddressList& address_list, const GURL& origin) { + // Kerberos SPNs for GSSAPI are in the form host@<host>:<port> + // http://msdn.microsoft.com/en-us/library/ms677601%28VS.85%29.aspx + // + // However, reality differs from the specification. A good description of + // the problems can be found here: + // http://blog.michelbarneveld.nl/michel/archive/2009/11/14/the-reason-why-kb911149-and-kb908209-are-not-the-soluton.aspx + // + // Typically the <host> portion should be the canonical FQDN for the service. + // If this could not be resolved, the original hostname in the URL will be + // attempted instead. However, some intranets register SPNs using aliases + // for the same canonical DNS name to allow multiple web services to reside + // on the same host machine without requiring different ports. IE6 and IE7 + // have hotpatches that allow the default behavior to be overridden. + // http://support.microsoft.com/kb/911149 + // http://support.microsoft.com/kb/938305 + // + // According to the spec, the <port> option should be included if it is a + // non-standard port (i.e. not 80 or 443 in the HTTP case). However, + // historically browsers have not included the port, even on non-standard + // ports. IE6 required a hotpatch and a registry setting to enable + // including non-standard ports, and IE7 and IE8 also require the same + // registry setting, but no hotpatch. Firefox does not appear to have an + // option to include non-standard ports as of 3.6. + // http://support.microsoft.com/kb/908209 + // + // Without any command-line flags, Chrome matches the behavior of Firefox + // and IE. Users can override the behavior so aliases are allowed and + // non-standard ports are included. + int port = origin.EffectiveIntPort(); + std::string server; + if (!address_list.GetCanonicalName(&server)) + server = origin.host(); + std::string resulting_spn; + if (port != 80 && port != 443 && use_port_) { + resulting_spn = StringPrintf("host@%s:%d", server.c_str(), port); + } else { + resulting_spn = StringPrintf("host@%s", server.c_str()); + } + return ASCIIToWide(resulting_spn); } HttpAuthHandlerNegotiate::Factory::Factory() - : disable_cname_lookup_(false), use_port_(false) { + : disable_cname_lookup_(false), use_port_(false), + gssapi_library_(GSSAPILibrary::GetDefault()) { } HttpAuthHandlerNegotiate::Factory::~Factory() { @@ -82,7 +197,15 @@ int HttpAuthHandlerNegotiate::Factory::CreateAuthHandler( int digest_nonce_count, const BoundNetLog& net_log, scoped_ptr<HttpAuthHandler>* handler) { - return ERR_UNSUPPORTED_AUTH_SCHEME; + // TODO(ahendrickson): Move towards model of parsing in the factory + // method and only constructing when valid. + scoped_ptr<HttpAuthHandler> tmp_handler( + new HttpAuthHandlerNegotiate(gssapi_library_, url_security_manager(), + disable_cname_lookup_, use_port_)); + if (!tmp_handler->InitFromChallenge(challenge, target, origin, net_log)) + return ERR_INVALID_RESPONSE; + handler->swap(tmp_handler); + return OK; } } // namespace net diff --git a/net/http/http_auth_unittest.cc b/net/http/http_auth_unittest.cc index a87ac6e..d9c06fa 100644 --- a/net/http/http_auth_unittest.cc +++ b/net/http/http_auth_unittest.cc @@ -2,17 +2,19 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "testing/gtest/include/gtest/gtest.h" +#include <string> #include "base/ref_counted.h" #include "base/scoped_ptr.h" #include "base/string_util.h" +#include "net/base/net_errors.h" #include "net/http/http_auth.h" #include "net/http/http_auth_filter.h" #include "net/http/http_auth_handler.h" #include "net/http/http_auth_handler_factory.h" #include "net/http/http_response_headers.h" #include "net/http/http_util.h" +#include "testing/gtest/include/gtest/gtest.h" namespace net { @@ -60,15 +62,11 @@ TEST(HttpAuthTest, ChooseBestChallenge) { "WWW-Authenticate: Negotiate\n" "WWW-Authenticate: NTLM\n", - // Negotiate is not currently support on non-Windows platforms, so - // the choice varies depending on platform. -#if defined(OS_WIN) + // TODO(ahendrickson): This may be flaky on Linux and OSX as it + // relies on being able to load one of the known .so files + // for gssapi. "negotiate", "", -#else - "ntlm", - "", -#endif } }; GURL origin("http://www.example.com"); @@ -103,7 +101,7 @@ TEST(HttpAuthTest, ChooseBestChallenge) { } } -TEST(HttpAuthTest, ChooseBestChallengeConnectionBased) { +TEST(HttpAuthTest, ChooseBestChallengeConnectionBasedNTLM) { static const struct { const char* headers; const char* challenge_realm; @@ -163,6 +161,65 @@ TEST(HttpAuthTest, ChooseBestChallengeConnectionBased) { } } +TEST(HttpAuthTest, ChooseBestChallengeConnectionBasedNegotiate) { + static const struct { + const char* headers; + const char* challenge_realm; + } tests[] = { + { + "WWW-Authenticate: Negotiate\r\n", + + "", + }, + { + "WWW-Authenticate: Negotiate " + "TlRMTVNTUAACAAAADAAMADgAAAAFgokCTroKF1e/DRcAAAAAAAAAALo" + "AugBEAAAABQEoCgAAAA9HAE8ATwBHAEwARQACAAwARwBPAE8ARwBMAE" + "UAAQAaAEEASwBFAEUAUwBBAFIAQQAtAEMATwBSAFAABAAeAGMAbwByA" + "HAALgBnAG8AbwBnAGwAZQAuAGMAbwBtAAMAQABhAGsAZQBlAHMAYQBy" + "AGEALQBjAG8AcgBwAC4AYQBkAC4AYwBvAHIAcAAuAGcAbwBvAGcAbAB" + "lAC4AYwBvAG0ABQAeAGMAbwByAHAALgBnAG8AbwBnAGwAZQAuAGMAbw" + "BtAAAAAAA=\r\n", + + // Realm is empty. + "", + } + }; + GURL origin("http://www.example.com"); + scoped_ptr<HttpAuthHandlerFactory> http_auth_handler_factory( + HttpAuthHandlerFactory::CreateDefault()); + + scoped_ptr<HttpAuthHandler> handler; + for (size_t i = 0; i < ARRAYSIZE_UNSAFE(tests); ++i) { + // Make a HttpResponseHeaders object. + std::string headers_with_status_line("HTTP/1.1 401 Unauthorized\n"); + headers_with_status_line += tests[i].headers; + scoped_refptr<net::HttpResponseHeaders> headers( + new net::HttpResponseHeaders( + net::HttpUtil::AssembleRawHeaders( + headers_with_status_line.c_str(), + headers_with_status_line.length()))); + + HttpAuthHandler* old_handler = handler.get(); + HttpAuth::ChooseBestChallenge(http_auth_handler_factory.get(), + headers.get(), + HttpAuth::AUTH_SERVER, + origin, + BoundNetLog(), + &handler); + + EXPECT_TRUE(handler != NULL); + // Since Negotiate is connection-based, we should continue to use the + // existing handler rather than creating a new one. + if (i != 0) + EXPECT_EQ(old_handler, handler.get()); + + ASSERT_NE(reinterpret_cast<net::HttpAuthHandler *>(NULL), handler.get()); + + EXPECT_STREQ(tests[i].challenge_realm, handler->realm().c_str()); + } +} + TEST(HttpAuthTest, ChallengeTokenizer) { std::string challenge_str = "Basic realm=\"foobar\""; HttpAuth::ChallengeTokenizer challenge(challenge_str.begin(), diff --git a/net/http/http_network_transaction.cc b/net/http/http_network_transaction.cc index 9524704..c228dab 100644 --- a/net/http/http_network_transaction.cc +++ b/net/http/http_network_transaction.cc @@ -1,5 +1,5 @@ -// Copyright (c) 2010 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be +// 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_network_transaction.h" @@ -2049,4 +2049,53 @@ void HttpNetworkTransaction::MarkBrokenAlternateProtocolAndFallback() { next_state_ = STATE_INIT_CONNECTION; } +#define STATE_CASE(s) case s: \ + description = StringPrintf("%s (0x%08X)", #s, s); \ + break + +std::string HttpNetworkTransaction::DescribeState(State state) { + std::string description; + switch (state) { + STATE_CASE(STATE_RESOLVE_PROXY); + STATE_CASE(STATE_RESOLVE_PROXY_COMPLETE); + STATE_CASE(STATE_INIT_CONNECTION); + STATE_CASE(STATE_INIT_CONNECTION_COMPLETE); + STATE_CASE(STATE_TUNNEL_GENERATE_AUTH_TOKEN); + STATE_CASE(STATE_TUNNEL_GENERATE_AUTH_TOKEN_COMPLETE); + STATE_CASE(STATE_TUNNEL_SEND_REQUEST); + STATE_CASE(STATE_TUNNEL_SEND_REQUEST_COMPLETE); + STATE_CASE(STATE_TUNNEL_READ_HEADERS); + STATE_CASE(STATE_TUNNEL_READ_HEADERS_COMPLETE); + STATE_CASE(STATE_SSL_CONNECT); + STATE_CASE(STATE_SSL_CONNECT_COMPLETE); + STATE_CASE(STATE_GENERATE_PROXY_AUTH_TOKEN); + STATE_CASE(STATE_GENERATE_PROXY_AUTH_TOKEN_COMPLETE); + STATE_CASE(STATE_GENERATE_SERVER_AUTH_TOKEN); + STATE_CASE(STATE_GENERATE_SERVER_AUTH_TOKEN_COMPLETE); + STATE_CASE(STATE_SEND_REQUEST); + STATE_CASE(STATE_SEND_REQUEST_COMPLETE); + STATE_CASE(STATE_READ_HEADERS); + STATE_CASE(STATE_READ_HEADERS_COMPLETE); + STATE_CASE(STATE_RESOLVE_CANONICAL_NAME); + STATE_CASE(STATE_RESOLVE_CANONICAL_NAME_COMPLETE); + STATE_CASE(STATE_READ_BODY); + STATE_CASE(STATE_READ_BODY_COMPLETE); + STATE_CASE(STATE_DRAIN_BODY_FOR_AUTH_RESTART); + STATE_CASE(STATE_DRAIN_BODY_FOR_AUTH_RESTART_COMPLETE); + STATE_CASE(STATE_SPDY_SEND_REQUEST); + STATE_CASE(STATE_SPDY_SEND_REQUEST_COMPLETE); + STATE_CASE(STATE_SPDY_READ_HEADERS); + STATE_CASE(STATE_SPDY_READ_HEADERS_COMPLETE); + STATE_CASE(STATE_SPDY_READ_BODY); + STATE_CASE(STATE_SPDY_READ_BODY_COMPLETE); + STATE_CASE(STATE_NONE); + default: + description = StringPrintf("Unknown state 0x%08X (%u)", state, state); + break; + } + return description; +} + +#undef STATE_CASE + } // namespace net diff --git a/net/http/http_network_transaction.h b/net/http/http_network_transaction.h index bc1d06b..429c08a 100644 --- a/net/http/http_network_transaction.h +++ b/net/http/http_network_transaction.h @@ -256,6 +256,9 @@ class HttpNetworkTransaction : public HttpTransaction { void MarkBrokenAlternateProtocolAndFallback(); + // Debug helper. + static std::string DescribeState(State state); + static bool g_ignore_certificate_errors; scoped_ptr<HttpAuthController> auth_controllers_[HttpAuth::AUTH_NUM_TARGETS]; diff --git a/net/http/http_network_transaction_unittest.cc b/net/http/http_network_transaction_unittest.cc index 991e697..37d6f60 100644 --- a/net/http/http_network_transaction_unittest.cc +++ b/net/http/http_network_transaction_unittest.cc @@ -1864,8 +1864,9 @@ TEST_F(HttpNetworkTransactionTest, NTLMAuth1) { MockRead data_reads1[] = { MockRead("HTTP/1.1 401 Access Denied\r\n"), - // Negotiate and NTLM are often requested together. We only support NTLM. - MockRead("WWW-Authenticate: Negotiate\r\n"), + // Negotiate and NTLM are often requested together. However, we only want + // to test NTLM. Since Negotiate is preferred over NTLM, we have to skip + // the header that requests Negotiate for this test. MockRead("WWW-Authenticate: NTLM\r\n"), MockRead("Connection: close\r\n"), MockRead("Content-Length: 42\r\n"), @@ -1944,9 +1945,10 @@ TEST_F(HttpNetworkTransactionTest, NTLMAuth1) { EXPECT_FALSE(trans->IsReadyToRestartForAuth()); const HttpResponseInfo* response = trans->GetResponseInfo(); - EXPECT_FALSE(response == NULL); + ASSERT_FALSE(response == NULL); - // The password prompt info should have been set in response->auth_challenge. + // The password prompt info should have been set in + // response->auth_challenge. EXPECT_FALSE(response->auth_challenge.get() == NULL); EXPECT_EQ(L"172.22.68.17:80", response->auth_challenge->host_and_port); @@ -1962,6 +1964,8 @@ TEST_F(HttpNetworkTransactionTest, NTLMAuth1) { EXPECT_EQ(OK, rv); response = trans->GetResponseInfo(); + ASSERT_FALSE(response == NULL); + EXPECT_TRUE(response->auth_challenge.get() == NULL); EXPECT_EQ(13, response->headers->GetContentLength()); } @@ -1987,8 +1991,9 @@ TEST_F(HttpNetworkTransactionTest, NTLMAuth2) { MockRead data_reads1[] = { MockRead("HTTP/1.1 401 Access Denied\r\n"), - // Negotiate and NTLM are often requested together. We only support NTLM. - MockRead("WWW-Authenticate: Negotiate\r\n"), + // Negotiate and NTLM are often requested together. However, we only want + // to test NTLM. Since Negotiate is preferred over NTLM, we have to skip + // the header that requests Negotiate for this test. MockRead("WWW-Authenticate: NTLM\r\n"), MockRead("Connection: close\r\n"), MockRead("Content-Length: 42\r\n"), @@ -2037,7 +2042,6 @@ TEST_F(HttpNetworkTransactionTest, NTLMAuth2) { // Wrong password. MockRead("HTTP/1.1 401 Access Denied\r\n"), - MockRead("WWW-Authenticate: Negotiate\r\n"), MockRead("WWW-Authenticate: NTLM\r\n"), MockRead("Connection: close\r\n"), MockRead("Content-Length: 42\r\n"), diff --git a/net/http/http_response_headers.cc b/net/http/http_response_headers.cc index e6980ab..08f397d 100644 --- a/net/http/http_response_headers.cc +++ b/net/http/http_response_headers.cc @@ -358,6 +358,30 @@ void HttpResponseHeaders::GetNormalizedHeaders(std::string* output) const { output->push_back('\n'); } +void HttpResponseHeaders::GetRawHeaders(std::string* output) const { + if (!output) + return; + output->erase(); + const char* headers_string = raw_headers().c_str(); + size_t headers_length = raw_headers().length(); + if (!headers_string) + return; + // The headers_string is a NULL-terminated status line, followed by NULL- + // terminated headers. + std::string raw_string = headers_string; + size_t current_length = strlen(headers_string) + 1; + while (headers_length > current_length) { + // Move to the next header, and append it. + headers_string += current_length; + headers_length -= current_length; + raw_string += "\n"; + raw_string += headers_string; + // Get the next header location. + current_length = strlen(headers_string) + 1; + } + *output = raw_string; +} + bool HttpResponseHeaders::GetNormalizedHeader(const std::string& name, std::string* value) const { // If you hit this assertion, please use EnumerateHeader instead! diff --git a/net/http/http_response_headers.h b/net/http/http_response_headers.h index c66e701..30e43eaf 100644 --- a/net/http/http_response_headers.h +++ b/net/http/http_response_headers.h @@ -99,6 +99,9 @@ class HttpResponseHeaders // void GetNormalizedHeaders(std::string* output) const; + // Gets the raw stored headers, in human-readable form. + void GetRawHeaders(std::string* output) const; + // Fetch the "normalized" value of a single header, where all values for the // header name are separated by commas. See the GetNormalizedHeaders for // format details. Returns false if this header wasn't found. |