Index: net/http/http_auth_handler_negotiate_posix.cc |
diff --git a/net/http/http_auth_handler_negotiate_posix.cc b/net/http/http_auth_handler_negotiate_posix.cc |
index 270ee5ba172dce09c5e9892e9c31d85fb756b807..6ce3cd734f23a791e268bd6bf4a4ac1153e916ce 100644 |
--- a/net/http/http_auth_handler_negotiate_posix.cc |
+++ b/net/http/http_auth_handler_negotiate_posix.cc |
@@ -5,7 +5,11 @@ |
#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/third_party/gssapi/gssapi.h" |
namespace net { |
@@ -16,33 +20,44 @@ namespace net { |
// Negotiate without requiring conditional compilation. |
HttpAuthHandlerNegotiate::HttpAuthHandlerNegotiate( |
- URLSecurityManager* url_security_manager) |
- : url_security_manager_(url_security_manager) { |
+ GSSAPILibrary* library, |
+ URLSecurityManager* url_security_manager, |
+ bool disable_cname_lookup, |
+ bool use_port) |
+ : auth_gssapi_("Negotiate", GSS_C_NO_OID, library), |
+ current_token_(NULL), |
+ 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() { |
} |
bool HttpAuthHandlerNegotiate::NeedsIdentity() { |
- NOTREACHED(); |
- LOG(ERROR) << ErrorToString(ERR_NOT_IMPLEMENTED); |
- return false; |
+ return auth_gssapi_.NeedsIdentity(); |
} |
bool HttpAuthHandlerNegotiate::IsFinalRound() { |
- NOTREACHED(); |
- LOG(ERROR) << ErrorToString(ERR_NOT_IMPLEMENTED); |
- return false; |
+ return auth_gssapi_.IsFinalRound(); |
} |
bool HttpAuthHandlerNegotiate::AllowsDefaultCredentials() { |
- NOTREACHED(); |
- LOG(ERROR) << ErrorToString(ERR_NOT_IMPLEMENTED); |
- return false; |
+ if (target_ == HttpAuth::AUTH_PROXY) |
+ return true; |
+ if (!url_security_manager_) |
+ return false; |
+ return url_security_manager_->CanUseDefaultCredentials(origin_); |
} |
bool HttpAuthHandlerNegotiate::Init(HttpAuth::ChallengeTokenizer* tok) { |
- return false; |
+ scheme_ = "negotiate"; |
+ score_ = 4; |
+ properties_ = ENCRYPTS_IDENTITY | IS_CONNECTION_BASED; |
+ return auth_gssapi_.ParseChallenge(tok); |
} |
int HttpAuthHandlerNegotiate::GenerateAuthToken( |
@@ -51,34 +66,124 @@ int HttpAuthHandlerNegotiate::GenerateAuthToken( |
const HttpRequestInfo* request, |
const ProxyInfo* proxy, |
std::string* auth_token) { |
- NOTREACHED(); |
- LOG(ERROR) << ErrorToString(ERR_NOT_IMPLEMENTED); |
- return ERR_NOT_IMPLEMENTED; |
+ return auth_gssapi_.GenerateAuthToken(&username, |
+ &password, |
+ spn_, |
+ request, |
+ proxy, |
+ auth_token); |
} |
int HttpAuthHandlerNegotiate::GenerateDefaultAuthToken( |
const HttpRequestInfo* request, |
const ProxyInfo* proxy, |
std::string* auth_token) { |
- NOTREACHED(); |
- LOG(ERROR) << ErrorToString(ERR_NOT_IMPLEMENTED); |
- return ERR_NOT_IMPLEMENTED; |
+ return auth_gssapi_.GenerateAuthToken(NULL, // username |
+ NULL, // password |
+ spn_, |
+ request, |
+ proxy, |
+ auth_token); |
} |
bool HttpAuthHandlerNegotiate::NeedsCanonicalName() { |
- return false; |
+ 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* host_resolver, |
+int HttpAuthHandlerNegotiate::ResolveCanonicalName(HostResolver* resolver, |
CompletionCallback* callback, |
const BoundNetLog& net_log) { |
- NOTREACHED(); |
- LOG(ERROR) << ErrorToString(ERR_NOT_IMPLEMENTED); |
- return ERR_NOT_IMPLEMENTED; |
+ // 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())); |
+ } |
} |
HttpAuthHandlerNegotiate::Factory::Factory() |
- : disable_cname_lookup_(false), use_port_(false) { |
+ : disable_cname_lookup_(false), |
+ use_port_(false), |
+ gssapi_library_(GSSAPILibrary::GetDefault()) { |
} |
HttpAuthHandlerNegotiate::Factory::~Factory() { |
@@ -91,7 +196,16 @@ int HttpAuthHandlerNegotiate::Factory::CreateAuthHandler( |
CreateReason reason, |
int digest_nonce_count, |
scoped_refptr<HttpAuthHandler>* handler) { |
- return ERR_UNSUPPORTED_AUTH_SCHEME; |
+ // TODO(cbentzel): Move towards model of parsing in the factory |
+ // method and only constructing when valid. |
+ scoped_refptr<HttpAuthHandler> tmp_handler( |
+ new HttpAuthHandlerNegotiate(gssapi_library_, |
+ url_security_manager(), |
+ disable_cname_lookup_, use_port_)); |
+ if (!tmp_handler->InitFromChallenge(challenge, target, origin)) |
+ return ERR_INVALID_RESPONSE; |
+ handler->swap(tmp_handler); |
+ return OK; |
} |
} // namespace net |