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" | 8 #include "base/string_util.h" |
9 #include "base/stringprintf.h" | 9 #include "base/stringprintf.h" |
10 #include "base/utf_string_conversions.h" | 10 #include "base/utf_string_conversions.h" |
11 #include "net/base/address_family.h" | 11 #include "net/base/address_family.h" |
12 #include "net/base/host_resolver.h" | 12 #include "net/base/host_resolver.h" |
13 #include "net/base/net_errors.h" | 13 #include "net/base/net_errors.h" |
14 #include "net/http/http_auth_filter.h" | 14 #include "net/http/http_auth_filter.h" |
15 #include "net/http/url_security_manager.h" | 15 #include "net/http/url_security_manager.h" |
16 | 16 |
17 namespace net { | 17 namespace net { |
18 | 18 |
| 19 HttpAuthHandlerNegotiate::Factory::Factory() |
| 20 : disable_cname_lookup_(false), |
| 21 use_port_(false), |
| 22 #if defined(OS_WIN) |
| 23 max_token_length_(0), |
| 24 first_creation_(true), |
| 25 is_unsupported_(false), |
| 26 #endif |
| 27 auth_library_(NULL) { |
| 28 } |
| 29 |
| 30 HttpAuthHandlerNegotiate::Factory::~Factory() { |
| 31 } |
| 32 |
| 33 void HttpAuthHandlerNegotiate::Factory::set_host_resolver( |
| 34 HostResolver* resolver) { |
| 35 resolver_ = resolver; |
| 36 } |
| 37 |
| 38 int HttpAuthHandlerNegotiate::Factory::CreateAuthHandler( |
| 39 HttpAuth::ChallengeTokenizer* challenge, |
| 40 HttpAuth::Target target, |
| 41 const GURL& origin, |
| 42 CreateReason reason, |
| 43 int digest_nonce_count, |
| 44 const BoundNetLog& net_log, |
| 45 scoped_ptr<HttpAuthHandler>* handler) { |
| 46 #if defined(OS_WIN) |
| 47 if (is_unsupported_ || reason == CREATE_PREEMPTIVE) |
| 48 return ERR_UNSUPPORTED_AUTH_SCHEME; |
| 49 if (max_token_length_ == 0) { |
| 50 int rv = DetermineMaxTokenLength(auth_library_.get(), NEGOSSP_NAME, |
| 51 &max_token_length_); |
| 52 if (rv == ERR_UNSUPPORTED_AUTH_SCHEME) |
| 53 is_unsupported_ = true; |
| 54 if (rv != OK) |
| 55 return rv; |
| 56 } |
| 57 // TODO(cbentzel): Move towards model of parsing in the factory |
| 58 // method and only constructing when valid. |
| 59 scoped_ptr<HttpAuthHandler> tmp_handler( |
| 60 new HttpAuthHandlerNegotiate(auth_library_.get(), max_token_length_, |
| 61 url_security_manager(), resolver_, |
| 62 disable_cname_lookup_, use_port_)); |
| 63 if (!tmp_handler->InitFromChallenge(challenge, target, origin, net_log)) |
| 64 return ERR_INVALID_RESPONSE; |
| 65 handler->swap(tmp_handler); |
| 66 return OK; |
| 67 #elif defined(OS_POSIX) |
| 68 // TODO(ahendrickson): Move towards model of parsing in the factory |
| 69 // method and only constructing when valid. |
| 70 scoped_ptr<HttpAuthHandler> tmp_handler( |
| 71 new HttpAuthHandlerNegotiate(auth_library_.get(), url_security_manager(), |
| 72 resolver_, disable_cname_lookup_, |
| 73 use_port_)); |
| 74 if (!tmp_handler->InitFromChallenge(challenge, target, origin, net_log)) |
| 75 return ERR_INVALID_RESPONSE; |
| 76 handler->swap(tmp_handler); |
| 77 return OK; |
| 78 #endif |
| 79 } |
| 80 |
19 HttpAuthHandlerNegotiate::HttpAuthHandlerNegotiate( | 81 HttpAuthHandlerNegotiate::HttpAuthHandlerNegotiate( |
20 AuthLibrary* auth_library, | 82 AuthLibrary* auth_library, |
21 #if defined(OS_WIN) | 83 #if defined(OS_WIN) |
22 ULONG max_token_length, | 84 ULONG max_token_length, |
23 #endif | 85 #endif |
24 URLSecurityManager* url_security_manager, | 86 URLSecurityManager* url_security_manager, |
25 HostResolver* resolver, | 87 HostResolver* resolver, |
26 bool disable_cname_lookup, | 88 bool disable_cname_lookup, |
27 bool use_port) | 89 bool use_port) |
28 #if defined(OS_WIN) | 90 #if defined(OS_WIN) |
(...skipping 10 matching lines...) Expand all Loading... |
39 has_username_and_password_(false), | 101 has_username_and_password_(false), |
40 user_callback_(NULL), | 102 user_callback_(NULL), |
41 auth_token_(NULL), | 103 auth_token_(NULL), |
42 next_state_(STATE_NONE), | 104 next_state_(STATE_NONE), |
43 url_security_manager_(url_security_manager) { | 105 url_security_manager_(url_security_manager) { |
44 } | 106 } |
45 | 107 |
46 HttpAuthHandlerNegotiate::~HttpAuthHandlerNegotiate() { | 108 HttpAuthHandlerNegotiate::~HttpAuthHandlerNegotiate() { |
47 } | 109 } |
48 | 110 |
49 int HttpAuthHandlerNegotiate::GenerateAuthTokenImpl( | |
50 const string16* username, | |
51 const string16* password, | |
52 const HttpRequestInfo* request, | |
53 CompletionCallback* callback, | |
54 std::string* auth_token) { | |
55 DCHECK(user_callback_ == NULL); | |
56 DCHECK((username == NULL) == (password == NULL)); | |
57 DCHECK(auth_token_ == NULL); | |
58 auth_token_ = auth_token; | |
59 if (already_called_) { | |
60 DCHECK((!has_username_and_password_ && username == NULL) || | |
61 (has_username_and_password_ && *username == username_ && | |
62 *password == password_)); | |
63 next_state_ = STATE_GENERATE_AUTH_TOKEN; | |
64 } else { | |
65 already_called_ = true; | |
66 if (username) { | |
67 has_username_and_password_ = true; | |
68 username_ = *username; | |
69 password_ = *password; | |
70 } | |
71 next_state_ = STATE_RESOLVE_CANONICAL_NAME; | |
72 } | |
73 int rv = DoLoop(OK); | |
74 if (rv == ERR_IO_PENDING) | |
75 user_callback_ = callback; | |
76 return rv; | |
77 } | |
78 | |
79 // The Negotiate challenge header looks like: | |
80 // WWW-Authenticate: NEGOTIATE auth-data | |
81 bool HttpAuthHandlerNegotiate::Init(HttpAuth::ChallengeTokenizer* challenge) { | |
82 #if defined(OS_POSIX) | |
83 if (!auth_system_.Init()) { | |
84 VLOG(1) << "can't initialize GSSAPI library"; | |
85 return false; | |
86 } | |
87 // GSSAPI does not provide a way to enter username/password to | |
88 // obtain a TGT. If the default credentials are not allowed for | |
89 // a particular site (based on whitelist), fall back to a | |
90 // different scheme. | |
91 if (!AllowsDefaultCredentials()) | |
92 return false; | |
93 #endif | |
94 if (CanDelegate()) | |
95 auth_system_.Delegate(); | |
96 auth_scheme_ = HttpAuth::AUTH_SCHEME_NEGOTIATE; | |
97 score_ = 4; | |
98 properties_ = ENCRYPTS_IDENTITY | IS_CONNECTION_BASED; | |
99 HttpAuth::AuthorizationResult auth_result = | |
100 auth_system_.ParseChallenge(challenge); | |
101 return (auth_result == HttpAuth::AUTHORIZATION_RESULT_ACCEPT); | |
102 } | |
103 | |
104 HttpAuth::AuthorizationResult HttpAuthHandlerNegotiate::HandleAnotherChallenge( | |
105 HttpAuth::ChallengeTokenizer* challenge) { | |
106 return auth_system_.ParseChallenge(challenge); | |
107 } | |
108 | |
109 // Require identity on first pass instead of second. | |
110 bool HttpAuthHandlerNegotiate::NeedsIdentity() { | |
111 return auth_system_.NeedsIdentity(); | |
112 } | |
113 | |
114 bool HttpAuthHandlerNegotiate::AllowsDefaultCredentials() { | |
115 if (target_ == HttpAuth::AUTH_PROXY) | |
116 return true; | |
117 if (!url_security_manager_) | |
118 return false; | |
119 return url_security_manager_->CanUseDefaultCredentials(origin_); | |
120 } | |
121 | |
122 bool HttpAuthHandlerNegotiate::CanDelegate() const { | |
123 // TODO(cbentzel): Should delegation be allowed on proxies? | |
124 if (target_ == HttpAuth::AUTH_PROXY) | |
125 return false; | |
126 if (!url_security_manager_) | |
127 return false; | |
128 return url_security_manager_->CanDelegate(origin_); | |
129 } | |
130 | |
131 std::wstring HttpAuthHandlerNegotiate::CreateSPN( | 111 std::wstring HttpAuthHandlerNegotiate::CreateSPN( |
132 const AddressList& address_list, const GURL& origin) { | 112 const AddressList& address_list, const GURL& origin) { |
133 // Kerberos Web Server SPNs are in the form HTTP/<host>:<port> through SSPI, | 113 // Kerberos Web Server SPNs are in the form HTTP/<host>:<port> through SSPI, |
134 // and in the form HTTP@<host>:<port> through GSSAPI | 114 // and in the form HTTP@<host>:<port> through GSSAPI |
135 // http://msdn.microsoft.com/en-us/library/ms677601%28VS.85%29.aspx | 115 // http://msdn.microsoft.com/en-us/library/ms677601%28VS.85%29.aspx |
136 // | 116 // |
137 // However, reality differs from the specification. A good description of | 117 // However, reality differs from the specification. A good description of |
138 // the problems can be found here: | 118 // the problems can be found here: |
139 // http://blog.michelbarneveld.nl/michel/archive/2009/11/14/the-reason-why-k
b911149-and-kb908209-are-not-the-soluton.aspx | 119 // http://blog.michelbarneveld.nl/michel/archive/2009/11/14/the-reason-why-k
b911149-and-kb908209-are-not-the-soluton.aspx |
140 // | 120 // |
(...skipping 29 matching lines...) Expand all Loading... |
170 #endif | 150 #endif |
171 if (port != 80 && port != 443 && use_port_) { | 151 if (port != 80 && port != 443 && use_port_) { |
172 return ASCIIToWide(base::StringPrintf("HTTP%c%s:%d", kSpnSeparator, | 152 return ASCIIToWide(base::StringPrintf("HTTP%c%s:%d", kSpnSeparator, |
173 server.c_str(), port)); | 153 server.c_str(), port)); |
174 } else { | 154 } else { |
175 return ASCIIToWide(base::StringPrintf("HTTP%c%s", kSpnSeparator, | 155 return ASCIIToWide(base::StringPrintf("HTTP%c%s", kSpnSeparator, |
176 server.c_str())); | 156 server.c_str())); |
177 } | 157 } |
178 } | 158 } |
179 | 159 |
| 160 HttpAuth::AuthorizationResult HttpAuthHandlerNegotiate::HandleAnotherChallenge( |
| 161 HttpAuth::ChallengeTokenizer* challenge) { |
| 162 return auth_system_.ParseChallenge(challenge); |
| 163 } |
| 164 |
| 165 // Require identity on first pass instead of second. |
| 166 bool HttpAuthHandlerNegotiate::NeedsIdentity() { |
| 167 return auth_system_.NeedsIdentity(); |
| 168 } |
| 169 |
| 170 bool HttpAuthHandlerNegotiate::AllowsDefaultCredentials() { |
| 171 if (target_ == HttpAuth::AUTH_PROXY) |
| 172 return true; |
| 173 if (!url_security_manager_) |
| 174 return false; |
| 175 return url_security_manager_->CanUseDefaultCredentials(origin_); |
| 176 } |
| 177 |
| 178 // The Negotiate challenge header looks like: |
| 179 // WWW-Authenticate: NEGOTIATE auth-data |
| 180 bool HttpAuthHandlerNegotiate::Init(HttpAuth::ChallengeTokenizer* challenge) { |
| 181 #if defined(OS_POSIX) |
| 182 if (!auth_system_.Init()) { |
| 183 VLOG(1) << "can't initialize GSSAPI library"; |
| 184 return false; |
| 185 } |
| 186 // GSSAPI does not provide a way to enter username/password to |
| 187 // obtain a TGT. If the default credentials are not allowed for |
| 188 // a particular site (based on whitelist), fall back to a |
| 189 // different scheme. |
| 190 if (!AllowsDefaultCredentials()) |
| 191 return false; |
| 192 #endif |
| 193 if (CanDelegate()) |
| 194 auth_system_.Delegate(); |
| 195 auth_scheme_ = HttpAuth::AUTH_SCHEME_NEGOTIATE; |
| 196 score_ = 4; |
| 197 properties_ = ENCRYPTS_IDENTITY | IS_CONNECTION_BASED; |
| 198 HttpAuth::AuthorizationResult auth_result = |
| 199 auth_system_.ParseChallenge(challenge); |
| 200 return (auth_result == HttpAuth::AUTHORIZATION_RESULT_ACCEPT); |
| 201 } |
| 202 |
| 203 int HttpAuthHandlerNegotiate::GenerateAuthTokenImpl( |
| 204 const string16* username, |
| 205 const string16* password, |
| 206 const HttpRequestInfo* request, |
| 207 CompletionCallback* callback, |
| 208 std::string* auth_token) { |
| 209 DCHECK(user_callback_ == NULL); |
| 210 DCHECK((username == NULL) == (password == NULL)); |
| 211 DCHECK(auth_token_ == NULL); |
| 212 auth_token_ = auth_token; |
| 213 if (already_called_) { |
| 214 DCHECK((!has_username_and_password_ && username == NULL) || |
| 215 (has_username_and_password_ && *username == username_ && |
| 216 *password == password_)); |
| 217 next_state_ = STATE_GENERATE_AUTH_TOKEN; |
| 218 } else { |
| 219 already_called_ = true; |
| 220 if (username) { |
| 221 has_username_and_password_ = true; |
| 222 username_ = *username; |
| 223 password_ = *password; |
| 224 } |
| 225 next_state_ = STATE_RESOLVE_CANONICAL_NAME; |
| 226 } |
| 227 int rv = DoLoop(OK); |
| 228 if (rv == ERR_IO_PENDING) |
| 229 user_callback_ = callback; |
| 230 return rv; |
| 231 } |
| 232 |
| 233 void HttpAuthHandlerNegotiate::OnIOComplete(int result) { |
| 234 int rv = DoLoop(result); |
| 235 if (rv != ERR_IO_PENDING) |
| 236 DoCallback(rv); |
| 237 } |
| 238 |
| 239 void HttpAuthHandlerNegotiate::DoCallback(int rv) { |
| 240 DCHECK(rv != ERR_IO_PENDING); |
| 241 DCHECK(user_callback_); |
| 242 CompletionCallback* callback = user_callback_; |
| 243 user_callback_ = NULL; |
| 244 callback->Run(rv); |
| 245 } |
| 246 |
180 int HttpAuthHandlerNegotiate::DoLoop(int result) { | 247 int HttpAuthHandlerNegotiate::DoLoop(int result) { |
181 DCHECK(next_state_ != STATE_NONE); | 248 DCHECK(next_state_ != STATE_NONE); |
182 | 249 |
183 int rv = result; | 250 int rv = result; |
184 do { | 251 do { |
185 State state = next_state_; | 252 State state = next_state_; |
186 next_state_ = STATE_NONE; | 253 next_state_ = STATE_NONE; |
187 switch (state) { | 254 switch (state) { |
188 case STATE_RESOLVE_CANONICAL_NAME: | 255 case STATE_RESOLVE_CANONICAL_NAME: |
189 DCHECK_EQ(OK, rv); | 256 DCHECK_EQ(OK, rv); |
(...skipping 56 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
246 // TODO(cbentzel): This should possibly be done async. | 313 // TODO(cbentzel): This should possibly be done async. |
247 return auth_system_.GenerateAuthToken(username, password, spn_, auth_token_); | 314 return auth_system_.GenerateAuthToken(username, password, spn_, auth_token_); |
248 } | 315 } |
249 | 316 |
250 int HttpAuthHandlerNegotiate::DoGenerateAuthTokenComplete(int rv) { | 317 int HttpAuthHandlerNegotiate::DoGenerateAuthTokenComplete(int rv) { |
251 DCHECK_NE(ERR_IO_PENDING, rv); | 318 DCHECK_NE(ERR_IO_PENDING, rv); |
252 auth_token_ = NULL; | 319 auth_token_ = NULL; |
253 return rv; | 320 return rv; |
254 } | 321 } |
255 | 322 |
256 void HttpAuthHandlerNegotiate::OnIOComplete(int result) { | 323 bool HttpAuthHandlerNegotiate::CanDelegate() const { |
257 int rv = DoLoop(result); | 324 // TODO(cbentzel): Should delegation be allowed on proxies? |
258 if (rv != ERR_IO_PENDING) | 325 if (target_ == HttpAuth::AUTH_PROXY) |
259 DoCallback(rv); | 326 return false; |
260 } | 327 if (!url_security_manager_) |
261 | 328 return false; |
262 void HttpAuthHandlerNegotiate::DoCallback(int rv) { | 329 return url_security_manager_->CanDelegate(origin_); |
263 DCHECK(rv != ERR_IO_PENDING); | |
264 DCHECK(user_callback_); | |
265 CompletionCallback* callback = user_callback_; | |
266 user_callback_ = NULL; | |
267 callback->Run(rv); | |
268 } | |
269 | |
270 HttpAuthHandlerNegotiate::Factory::Factory() | |
271 : disable_cname_lookup_(false), | |
272 use_port_(false), | |
273 #if defined(OS_WIN) | |
274 max_token_length_(0), | |
275 first_creation_(true), | |
276 is_unsupported_(false), | |
277 #endif | |
278 auth_library_(NULL) { | |
279 } | |
280 | |
281 HttpAuthHandlerNegotiate::Factory::~Factory() { | |
282 } | |
283 | |
284 void HttpAuthHandlerNegotiate::Factory::set_host_resolver( | |
285 HostResolver* resolver) { | |
286 resolver_ = resolver; | |
287 } | |
288 | |
289 int HttpAuthHandlerNegotiate::Factory::CreateAuthHandler( | |
290 HttpAuth::ChallengeTokenizer* challenge, | |
291 HttpAuth::Target target, | |
292 const GURL& origin, | |
293 CreateReason reason, | |
294 int digest_nonce_count, | |
295 const BoundNetLog& net_log, | |
296 scoped_ptr<HttpAuthHandler>* handler) { | |
297 #if defined(OS_WIN) | |
298 if (is_unsupported_ || reason == CREATE_PREEMPTIVE) | |
299 return ERR_UNSUPPORTED_AUTH_SCHEME; | |
300 if (max_token_length_ == 0) { | |
301 int rv = DetermineMaxTokenLength(auth_library_.get(), NEGOSSP_NAME, | |
302 &max_token_length_); | |
303 if (rv == ERR_UNSUPPORTED_AUTH_SCHEME) | |
304 is_unsupported_ = true; | |
305 if (rv != OK) | |
306 return rv; | |
307 } | |
308 // TODO(cbentzel): Move towards model of parsing in the factory | |
309 // method and only constructing when valid. | |
310 scoped_ptr<HttpAuthHandler> tmp_handler( | |
311 new HttpAuthHandlerNegotiate(auth_library_.get(), max_token_length_, | |
312 url_security_manager(), resolver_, | |
313 disable_cname_lookup_, use_port_)); | |
314 if (!tmp_handler->InitFromChallenge(challenge, target, origin, net_log)) | |
315 return ERR_INVALID_RESPONSE; | |
316 handler->swap(tmp_handler); | |
317 return OK; | |
318 #elif defined(OS_POSIX) | |
319 // TODO(ahendrickson): Move towards model of parsing in the factory | |
320 // method and only constructing when valid. | |
321 scoped_ptr<HttpAuthHandler> tmp_handler( | |
322 new HttpAuthHandlerNegotiate(auth_library_.get(), url_security_manager(), | |
323 resolver_, disable_cname_lookup_, | |
324 use_port_)); | |
325 if (!tmp_handler->InitFromChallenge(challenge, target, origin, net_log)) | |
326 return ERR_INVALID_RESPONSE; | |
327 handler->swap(tmp_handler); | |
328 return OK; | |
329 #endif | |
330 } | 330 } |
331 | 331 |
332 } // namespace net | 332 } // namespace net |
OLD | NEW |