Index: net/http/http_auth_handler_negotiate_win.cc |
diff --git a/net/http/http_auth_handler_negotiate_win.cc b/net/http/http_auth_handler_negotiate_win.cc |
index fb849d4ee2ec2c1bea8f9a34ff3fe9c08c16efa8..6c6e76700571ff7698e721b61aabed164f30f036 100644 |
--- a/net/http/http_auth_handler_negotiate_win.cc |
+++ b/net/http/http_auth_handler_negotiate_win.cc |
@@ -5,14 +5,23 @@ |
#include "net/http/http_auth_handler_negotiate.h" |
#include "base/logging.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" |
namespace net { |
HttpAuthHandlerNegotiate::HttpAuthHandlerNegotiate(SSPILibrary* library, |
- ULONG max_token_length) |
- : auth_sspi_(library, "Negotiate", NEGOSSP_NAME, max_token_length) { |
+ ULONG max_token_length, |
+ bool disable_cname_lookup, |
+ bool use_port) |
+ : auth_sspi_(library, "Negotiate", NEGOSSP_NAME, max_token_length), |
+ user_callback_(NULL), |
+ ALLOW_THIS_IN_INITIALIZER_LIST(resolve_cname_callback_( |
+ this, &HttpAuthHandlerNegotiate::OnResolveCanonicalName)), |
+ disable_cname_lookup_(disable_cname_lookup), |
+ use_port_(use_port) { |
} |
HttpAuthHandlerNegotiate::~HttpAuthHandlerNegotiate() { |
@@ -27,7 +36,7 @@ int HttpAuthHandlerNegotiate::GenerateAuthToken( |
return auth_sspi_.GenerateAuthToken( |
&username, |
&password, |
- origin_, |
+ spn_, |
request, |
proxy, |
auth_token); |
@@ -55,6 +64,100 @@ bool HttpAuthHandlerNegotiate::SupportsDefaultCredentials() { |
return true; |
} |
+bool HttpAuthHandlerNegotiate::NeedsCanonicalName() { |
+ if (!spn_.empty()) |
+ return false; |
+ if (disable_cname_lookup_) { |
+ spn_ = CreateSPN(address_list_, origin_); |
+ address_list_.Reset(); |
+ return false; |
+ } |
+ return true; |
+} |
+ |
+int HttpAuthHandlerNegotiate::ResolveCanonicalName(HostResolver* resolver, |
+ CompletionCallback* callback, |
+ const BoundNetLog& net_log) { |
+ // TODO(cbentzel): 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; |
+ return rv; |
+ } |
+ 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. |
+ return OK; |
+} |
+ |
+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); |
+ } |
+} |
+ |
+std::wstring HttpAuthHandlerNegotiate::CreateSPN( |
+ const AddressList& address_list, const GURL& origin) { |
+ // Kerberos SPNs are in the form HTTP/<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(); |
+ if (port != 80 && port != 443 && use_port_) { |
+ return ASCIIToWide(StringPrintf("HTTP/%s:%d", server.c_str(), port)); |
+ } else { |
+ return ASCIIToWide(StringPrintf("HTTP/%s", server.c_str())); |
+ } |
+} |
+ |
int HttpAuthHandlerNegotiate::GenerateDefaultAuthToken( |
const HttpRequestInfo* request, |
const ProxyInfo* proxy, |
@@ -62,14 +165,16 @@ int HttpAuthHandlerNegotiate::GenerateDefaultAuthToken( |
return auth_sspi_.GenerateAuthToken( |
NULL, // username |
NULL, // password |
- origin_, |
+ spn_, |
request, |
proxy, |
auth_token); |
} |
HttpAuthHandlerNegotiate::Factory::Factory() |
- : max_token_length_(0), |
+ : disable_cname_lookup_(false), |
+ use_port_(false), |
+ max_token_length_(0), |
first_creation_(true), |
is_unsupported_(false), |
sspi_library_(SSPILibrary::GetDefault()) { |
@@ -96,7 +201,8 @@ int HttpAuthHandlerNegotiate::Factory::CreateAuthHandler( |
// TODO(cbentzel): Move towards model of parsing in the factory |
// method and only constructing when valid. |
scoped_refptr<HttpAuthHandler> tmp_handler( |
- new HttpAuthHandlerNegotiate(sspi_library_, max_token_length_)); |
+ new HttpAuthHandlerNegotiate(sspi_library_, max_token_length_, |
+ disable_cname_lookup_, use_port_)); |
if (!tmp_handler->InitFromChallenge(challenge, target, origin)) |
return ERR_INVALID_RESPONSE; |
handler->swap(tmp_handler); |