summaryrefslogtreecommitdiffstats
path: root/net/http
diff options
context:
space:
mode:
authorahendrickson@google.com <ahendrickson@google.com@0039d316-1c4b-4281-b951-d872f2087c98>2010-06-28 18:03:04 +0000
committerahendrickson@google.com <ahendrickson@google.com@0039d316-1c4b-4281-b951-d872f2087c98>2010-06-28 18:03:04 +0000
commitaef042746af7a3a9a9967195c24cb7a9c755a0e8 (patch)
tree2c0aa7088f240c63f1f12b2bd19651314cdfc928 /net/http
parentf9f41ec4f27ba5fd19ca82d4c04b13bed6627d23 (diff)
downloadchromium_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.cc473
-rw-r--r--net/http/http_auth_gssapi_posix.h40
-rw-r--r--net/http/http_auth_handler_factory_unittest.cc14
-rw-r--r--net/http/http_auth_handler_negotiate.h33
-rw-r--r--net/http/http_auth_handler_negotiate_posix.cc197
-rw-r--r--net/http/http_auth_unittest.cc75
-rw-r--r--net/http/http_network_transaction.cc53
-rw-r--r--net/http/http_network_transaction.h3
-rw-r--r--net/http/http_network_transaction_unittest.cc18
-rw-r--r--net/http/http_response_headers.cc24
-rw-r--r--net/http/http_response_headers.h3
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.