OLD | NEW |
1 // Copyright (c) 2010 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2010 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "net/http/http_auth_handler_negotiate.h" | 5 #include "net/http/http_auth_handler_negotiate.h" |
6 | 6 |
7 #include "base/logging.h" | 7 #include "base/logging.h" |
| 8 #include "base/string_util.h" |
| 9 #include "net/base/address_family.h" |
| 10 #include "net/base/host_resolver.h" |
8 #include "net/base/net_errors.h" | 11 #include "net/base/net_errors.h" |
| 12 #include "net/third_party/gssapi/gssapi.h" |
9 | 13 |
10 namespace net { | 14 namespace net { |
11 | 15 |
12 // TODO(ahendrickson): Implement via GSSAPI. | 16 // TODO(ahendrickson): Implement via GSSAPI. |
13 | 17 |
14 // TODO(cbentzel): Negotiate authentication protocol is not supported on Posix | 18 // TODO(cbentzel): Negotiate authentication protocol is not supported on Posix |
15 // systems currently. These stubs make the main HTTP Authentication code bypass | 19 // systems currently. These stubs make the main HTTP Authentication code bypass |
16 // Negotiate without requiring conditional compilation. | 20 // Negotiate without requiring conditional compilation. |
17 | 21 |
18 HttpAuthHandlerNegotiate::HttpAuthHandlerNegotiate( | 22 HttpAuthHandlerNegotiate::HttpAuthHandlerNegotiate( |
19 URLSecurityManager* url_security_manager) | 23 GSSAPILibrary* library, |
20 : url_security_manager_(url_security_manager) { | 24 URLSecurityManager* url_security_manager, |
| 25 bool disable_cname_lookup, |
| 26 bool use_port) |
| 27 : auth_gssapi_("Negotiate", GSS_C_NO_OID, library), |
| 28 current_token_(NULL), |
| 29 user_callback_(NULL), |
| 30 ALLOW_THIS_IN_INITIALIZER_LIST(resolve_cname_callback_( |
| 31 this, &HttpAuthHandlerNegotiate::OnResolveCanonicalName)), |
| 32 disable_cname_lookup_(disable_cname_lookup), |
| 33 use_port_(use_port), |
| 34 url_security_manager_(url_security_manager) { |
21 } | 35 } |
22 | 36 |
23 HttpAuthHandlerNegotiate::~HttpAuthHandlerNegotiate() { | 37 HttpAuthHandlerNegotiate::~HttpAuthHandlerNegotiate() { |
24 } | 38 } |
25 | 39 |
26 bool HttpAuthHandlerNegotiate::NeedsIdentity() { | 40 bool HttpAuthHandlerNegotiate::NeedsIdentity() { |
27 NOTREACHED(); | 41 return auth_gssapi_.NeedsIdentity(); |
28 LOG(ERROR) << ErrorToString(ERR_NOT_IMPLEMENTED); | |
29 return false; | |
30 } | 42 } |
31 | 43 |
32 bool HttpAuthHandlerNegotiate::IsFinalRound() { | 44 bool HttpAuthHandlerNegotiate::IsFinalRound() { |
33 NOTREACHED(); | 45 return auth_gssapi_.IsFinalRound(); |
34 LOG(ERROR) << ErrorToString(ERR_NOT_IMPLEMENTED); | |
35 return false; | |
36 } | 46 } |
37 | 47 |
38 bool HttpAuthHandlerNegotiate::AllowsDefaultCredentials() { | 48 bool HttpAuthHandlerNegotiate::AllowsDefaultCredentials() { |
39 NOTREACHED(); | 49 if (target_ == HttpAuth::AUTH_PROXY) |
40 LOG(ERROR) << ErrorToString(ERR_NOT_IMPLEMENTED); | 50 return true; |
41 return false; | 51 if (!url_security_manager_) |
| 52 return false; |
| 53 return url_security_manager_->CanUseDefaultCredentials(origin_); |
42 } | 54 } |
43 | 55 |
44 bool HttpAuthHandlerNegotiate::Init(HttpAuth::ChallengeTokenizer* tok) { | 56 bool HttpAuthHandlerNegotiate::Init(HttpAuth::ChallengeTokenizer* tok) { |
45 return false; | 57 scheme_ = "negotiate"; |
| 58 score_ = 4; |
| 59 properties_ = ENCRYPTS_IDENTITY | IS_CONNECTION_BASED; |
| 60 return auth_gssapi_.ParseChallenge(tok); |
46 } | 61 } |
47 | 62 |
48 int HttpAuthHandlerNegotiate::GenerateAuthToken( | 63 int HttpAuthHandlerNegotiate::GenerateAuthToken( |
49 const std::wstring& username, | 64 const std::wstring& username, |
50 const std::wstring& password, | 65 const std::wstring& password, |
51 const HttpRequestInfo* request, | 66 const HttpRequestInfo* request, |
52 const ProxyInfo* proxy, | 67 const ProxyInfo* proxy, |
53 std::string* auth_token) { | 68 std::string* auth_token) { |
54 NOTREACHED(); | 69 return auth_gssapi_.GenerateAuthToken(&username, |
55 LOG(ERROR) << ErrorToString(ERR_NOT_IMPLEMENTED); | 70 &password, |
56 return ERR_NOT_IMPLEMENTED; | 71 spn_, |
| 72 request, |
| 73 proxy, |
| 74 auth_token); |
57 } | 75 } |
58 | 76 |
59 int HttpAuthHandlerNegotiate::GenerateDefaultAuthToken( | 77 int HttpAuthHandlerNegotiate::GenerateDefaultAuthToken( |
60 const HttpRequestInfo* request, | 78 const HttpRequestInfo* request, |
61 const ProxyInfo* proxy, | 79 const ProxyInfo* proxy, |
62 std::string* auth_token) { | 80 std::string* auth_token) { |
63 NOTREACHED(); | 81 return auth_gssapi_.GenerateAuthToken(NULL, // username |
64 LOG(ERROR) << ErrorToString(ERR_NOT_IMPLEMENTED); | 82 NULL, // password |
65 return ERR_NOT_IMPLEMENTED; | 83 spn_, |
| 84 request, |
| 85 proxy, |
| 86 auth_token); |
66 } | 87 } |
67 | 88 |
68 bool HttpAuthHandlerNegotiate::NeedsCanonicalName() { | 89 bool HttpAuthHandlerNegotiate::NeedsCanonicalName() { |
69 return false; | 90 if (!spn_.empty()) |
| 91 return false; |
| 92 if (disable_cname_lookup_) { |
| 93 spn_ = CreateSPN(address_list_, origin_); |
| 94 address_list_.Reset(); |
| 95 return false; |
| 96 } |
| 97 return true; |
70 } | 98 } |
71 | 99 |
72 int HttpAuthHandlerNegotiate::ResolveCanonicalName(HostResolver* host_resolver, | 100 int HttpAuthHandlerNegotiate::ResolveCanonicalName(HostResolver* resolver, |
73 CompletionCallback* callback, | 101 CompletionCallback* callback, |
74 const BoundNetLog& net_log) { | 102 const BoundNetLog& net_log) { |
75 NOTREACHED(); | 103 // TODO(cbentzel): Add reverse DNS lookup for numeric addresses. |
76 LOG(ERROR) << ErrorToString(ERR_NOT_IMPLEMENTED); | 104 DCHECK(!single_resolve_.get()); |
77 return ERR_NOT_IMPLEMENTED; | 105 DCHECK(!disable_cname_lookup_); |
| 106 DCHECK(callback); |
| 107 |
| 108 HostResolver::RequestInfo info(origin_.host(), 0); |
| 109 info.set_host_resolver_flags(HOST_RESOLVER_CANONNAME); |
| 110 single_resolve_.reset(new SingleRequestHostResolver(resolver)); |
| 111 int rv = single_resolve_->Resolve(info, &address_list_, |
| 112 &resolve_cname_callback_, |
| 113 net_log); |
| 114 if (rv == ERR_IO_PENDING) { |
| 115 user_callback_ = callback; |
| 116 return rv; |
| 117 } |
| 118 OnResolveCanonicalName(rv); |
| 119 // Always return OK. OnResolveCanonicalName logs the error code if not |
| 120 // OK and attempts to use the original origin_ hostname rather than failing |
| 121 // the auth attempt completely. |
| 122 return OK; |
| 123 } |
| 124 |
| 125 void HttpAuthHandlerNegotiate::OnResolveCanonicalName(int result) { |
| 126 if (result != OK) { |
| 127 // Even in the error case, try to use origin_.host instead of |
| 128 // passing the failure on to the caller. |
| 129 LOG(INFO) << "Problem finding canonical name for SPN for host " |
| 130 << origin_.host() << ": " << ErrorToString(result); |
| 131 result = OK; |
| 132 } |
| 133 spn_ = CreateSPN(address_list_, origin_); |
| 134 address_list_.Reset(); |
| 135 if (user_callback_) { |
| 136 CompletionCallback* callback = user_callback_; |
| 137 user_callback_ = NULL; |
| 138 callback->Run(result); |
| 139 } |
| 140 } |
| 141 |
| 142 std::wstring HttpAuthHandlerNegotiate::CreateSPN( |
| 143 const AddressList& address_list, const GURL& origin) { |
| 144 // Kerberos SPNs are in the form HTTP/<host>:<port> |
| 145 // http://msdn.microsoft.com/en-us/library/ms677601%28VS.85%29.aspx |
| 146 // |
| 147 // However, reality differs from the specification. A good description of |
| 148 // the problems can be found here: |
| 149 // http://blog.michelbarneveld.nl/michel/archive/2009/11/14/the-reason-why-k
b911149-and-kb908209-are-not-the-soluton.aspx |
| 150 // |
| 151 // Typically the <host> portion should be the canonical FQDN for the service. |
| 152 // If this could not be resolved, the original hostname in the URL will be |
| 153 // attempted instead. However, some intranets register SPNs using aliases |
| 154 // for the same canonical DNS name to allow multiple web services to reside |
| 155 // on the same host machine without requiring different ports. IE6 and IE7 |
| 156 // have hotpatches that allow the default behavior to be overridden. |
| 157 // http://support.microsoft.com/kb/911149 |
| 158 // http://support.microsoft.com/kb/938305 |
| 159 // |
| 160 // According to the spec, the <port> option should be included if it is a |
| 161 // non-standard port (i.e. not 80 or 443 in the HTTP case). However, |
| 162 // historically browsers have not included the port, even on non-standard |
| 163 // ports. IE6 required a hotpatch and a registry setting to enable |
| 164 // including non-standard ports, and IE7 and IE8 also require the same |
| 165 // registry setting, but no hotpatch. Firefox does not appear to have an |
| 166 // option to include non-standard ports as of 3.6. |
| 167 // http://support.microsoft.com/kb/908209 |
| 168 // |
| 169 // Without any command-line flags, Chrome matches the behavior of Firefox |
| 170 // and IE. Users can override the behavior so aliases are allowed and |
| 171 // non-standard ports are included. |
| 172 int port = origin.EffectiveIntPort(); |
| 173 std::string server; |
| 174 if (!address_list.GetCanonicalName(&server)) |
| 175 server = origin.host(); |
| 176 if (port != 80 && port != 443 && use_port_) { |
| 177 return ASCIIToWide(StringPrintf("HTTP/%s:%d", server.c_str(), port)); |
| 178 } else { |
| 179 return ASCIIToWide(StringPrintf("HTTP/%s", server.c_str())); |
| 180 } |
78 } | 181 } |
79 | 182 |
80 HttpAuthHandlerNegotiate::Factory::Factory() | 183 HttpAuthHandlerNegotiate::Factory::Factory() |
81 : disable_cname_lookup_(false), use_port_(false) { | 184 : disable_cname_lookup_(false), |
| 185 use_port_(false), |
| 186 gssapi_library_(GSSAPILibrary::GetDefault()) { |
82 } | 187 } |
83 | 188 |
84 HttpAuthHandlerNegotiate::Factory::~Factory() { | 189 HttpAuthHandlerNegotiate::Factory::~Factory() { |
85 } | 190 } |
86 | 191 |
87 int HttpAuthHandlerNegotiate::Factory::CreateAuthHandler( | 192 int HttpAuthHandlerNegotiate::Factory::CreateAuthHandler( |
88 HttpAuth::ChallengeTokenizer* challenge, | 193 HttpAuth::ChallengeTokenizer* challenge, |
89 HttpAuth::Target target, | 194 HttpAuth::Target target, |
90 const GURL& origin, | 195 const GURL& origin, |
91 CreateReason reason, | 196 CreateReason reason, |
92 int digest_nonce_count, | 197 int digest_nonce_count, |
93 scoped_refptr<HttpAuthHandler>* handler) { | 198 scoped_refptr<HttpAuthHandler>* handler) { |
94 return ERR_UNSUPPORTED_AUTH_SCHEME; | 199 // TODO(cbentzel): Move towards model of parsing in the factory |
| 200 // method and only constructing when valid. |
| 201 scoped_refptr<HttpAuthHandler> tmp_handler( |
| 202 new HttpAuthHandlerNegotiate(gssapi_library_, |
| 203 url_security_manager(), |
| 204 disable_cname_lookup_, use_port_)); |
| 205 if (!tmp_handler->InitFromChallenge(challenge, target, origin)) |
| 206 return ERR_INVALID_RESPONSE; |
| 207 handler->swap(tmp_handler); |
| 208 return OK; |
95 } | 209 } |
96 | 210 |
97 } // namespace net | 211 } // namespace net |
OLD | NEW |