OLD | NEW |
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 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_controller.h" | 5 #include "net/http/http_auth_controller.h" |
6 | 6 |
7 #include "base/bind.h" | 7 #include "base/bind.h" |
8 #include "base/bind_helpers.h" | 8 #include "base/bind_helpers.h" |
| 9 #include "base/callback_helpers.h" |
9 #include "base/metrics/histogram_macros.h" | 10 #include "base/metrics/histogram_macros.h" |
10 #include "base/strings/string_util.h" | 11 #include "base/strings/string_util.h" |
11 #include "base/strings/utf_string_conversions.h" | 12 #include "base/strings/utf_string_conversions.h" |
12 #include "base/threading/platform_thread.h" | 13 #include "base/threading/platform_thread.h" |
| 14 #include "base/values.h" |
13 #include "net/base/auth.h" | 15 #include "net/base/auth.h" |
14 #include "net/base/net_util.h" | 16 #include "net/base/net_util.h" |
15 #include "net/dns/host_resolver.h" | 17 #include "net/dns/host_resolver.h" |
16 #include "net/http/http_auth_challenge_tokenizer.h" | 18 #include "net/http/http_auth_challenge_tokenizer.h" |
| 19 #include "net/http/http_auth_challenge_tokenizer.h" |
17 #include "net/http/http_auth_handler.h" | 20 #include "net/http/http_auth_handler.h" |
18 #include "net/http/http_auth_handler_factory.h" | 21 #include "net/http/http_auth_handler_factory.h" |
19 #include "net/http/http_network_session.h" | 22 #include "net/http/http_network_session.h" |
20 #include "net/http/http_request_headers.h" | 23 #include "net/http/http_request_headers.h" |
21 #include "net/http/http_request_info.h" | 24 #include "net/http/http_request_info.h" |
22 #include "net/http/http_response_headers.h" | 25 #include "net/http/http_response_headers.h" |
23 | 26 |
24 namespace net { | 27 namespace net { |
25 | 28 |
26 namespace { | 29 namespace { |
27 | 30 |
28 // Returns a log message for all the response headers related to the auth | |
29 // challenge. | |
30 std::string AuthChallengeLogMessage(HttpResponseHeaders* headers) { | |
31 std::string msg; | |
32 std::string header_val; | |
33 void* iter = NULL; | |
34 while (headers->EnumerateHeader(&iter, "proxy-authenticate", &header_val)) { | |
35 msg.append("\n Has header Proxy-Authenticate: "); | |
36 msg.append(header_val); | |
37 } | |
38 | |
39 iter = NULL; | |
40 while (headers->EnumerateHeader(&iter, "www-authenticate", &header_val)) { | |
41 msg.append("\n Has header WWW-Authenticate: "); | |
42 msg.append(header_val); | |
43 } | |
44 | |
45 // RFC 4559 requires that a proxy indicate its support of NTLM/Negotiate | |
46 // authentication with a "Proxy-Support: Session-Based-Authentication" | |
47 // response header. | |
48 iter = NULL; | |
49 while (headers->EnumerateHeader(&iter, "proxy-support", &header_val)) { | |
50 msg.append("\n Has header Proxy-Support: "); | |
51 msg.append(header_val); | |
52 } | |
53 | |
54 return msg; | |
55 } | |
56 | |
57 enum AuthEvent { | 31 enum AuthEvent { |
58 AUTH_EVENT_START = 0, | 32 AUTH_EVENT_START = 0, |
59 AUTH_EVENT_REJECT, | 33 AUTH_EVENT_REJECT, |
60 AUTH_EVENT_MAX, | 34 AUTH_EVENT_MAX, |
61 }; | 35 }; |
62 | 36 |
63 enum AuthTarget { | 37 enum AuthTarget { |
64 AUTH_TARGET_PROXY = 0, | 38 AUTH_TARGET_PROXY = 0, |
65 AUTH_TARGET_SECURE_PROXY, | 39 AUTH_TARGET_SECURE_PROXY, |
66 AUTH_TARGET_SERVER, | 40 AUTH_TARGET_SERVER, |
(...skipping 111 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
178 if (auth_event != AUTH_EVENT_START) | 152 if (auth_event != AUTH_EVENT_START) |
179 return; | 153 return; |
180 static const int kTargetBucketsEnd = AUTH_SCHEME_MAX * AUTH_TARGET_MAX; | 154 static const int kTargetBucketsEnd = AUTH_SCHEME_MAX * AUTH_TARGET_MAX; |
181 AuthTarget auth_target = DetermineAuthTarget(handler); | 155 AuthTarget auth_target = DetermineAuthTarget(handler); |
182 int target_bucket = auth_scheme * AUTH_TARGET_MAX + auth_target; | 156 int target_bucket = auth_scheme * AUTH_TARGET_MAX + auth_target; |
183 DCHECK(target_bucket >= 0 && target_bucket < kTargetBucketsEnd); | 157 DCHECK(target_bucket >= 0 && target_bucket < kTargetBucketsEnd); |
184 UMA_HISTOGRAM_ENUMERATION("Net.HttpAuthTarget", target_bucket, | 158 UMA_HISTOGRAM_ENUMERATION("Net.HttpAuthTarget", target_bucket, |
185 kTargetBucketsEnd); | 159 kTargetBucketsEnd); |
186 } | 160 } |
187 | 161 |
| 162 // Hardcoded map of HTTP authentication scheme priorities. Higher priorities are |
| 163 // preferred over lower. |
| 164 struct SchemePriority { |
| 165 const char* scheme; |
| 166 int priority; |
| 167 } kSchemeScores[] = { |
| 168 {"basic", 1}, |
| 169 {"digest", 2}, |
| 170 {"ntlm", 3}, |
| 171 {"negotiate", 4}, |
| 172 }; |
| 173 |
| 174 // Priority assigned to unknown authentication schemes. They are currently |
| 175 // ranked lower than Basic, which might be a bit too conservative. |
| 176 const int kSchemePriorityDefault = 0; |
| 177 |
| 178 // Higher priority schemes are preferred over lower priority schemes. |
| 179 int GetSchemePriority(const std::string& scheme) { |
| 180 DCHECK(HttpAuth::IsValidNormalizedScheme(scheme)); |
| 181 for (const auto& iter : kSchemeScores) { |
| 182 if (scheme == iter.scheme) |
| 183 return iter.priority; |
| 184 } |
| 185 return kSchemePriorityDefault; |
| 186 } |
| 187 |
188 } // namespace | 188 } // namespace |
189 | 189 |
190 HttpAuthController::HttpAuthController( | 190 HttpAuthController::HttpAuthController( |
191 HttpAuth::Target target, | 191 HttpAuth::Target target, |
192 const GURL& auth_url, | 192 const GURL& auth_url, |
193 HttpAuthCache* http_auth_cache, | 193 HttpAuthCache* http_auth_cache, |
194 HttpAuthHandlerFactory* http_auth_handler_factory) | 194 HttpAuthHandlerFactory* http_auth_handler_factory) |
195 : target_(target), | 195 : target_(target), |
196 auth_url_(auth_url), | 196 auth_url_(auth_url), |
197 auth_origin_(auth_url.GetOrigin()), | 197 auth_origin_(auth_url.GetOrigin()), |
198 auth_path_(HttpAuth::AUTH_PROXY ? std::string() : auth_url.path()), | 198 auth_path_(HttpAuth::AUTH_PROXY ? std::string() : auth_url.path()), |
199 embedded_identity_used_(false), | 199 embedded_identity_used_(false), |
200 default_credentials_used_(false), | 200 default_credentials_used_(false), |
201 http_auth_cache_(http_auth_cache), | 201 http_auth_cache_(http_auth_cache), |
202 http_auth_handler_factory_(http_auth_handler_factory) { | 202 http_auth_handler_factory_(http_auth_handler_factory), |
| 203 weak_ptr_factory_(this) { |
| 204 io_callback_ = base::Bind(&HttpAuthController::OnIOComplete, |
| 205 weak_ptr_factory_.GetWeakPtr()); |
203 } | 206 } |
204 | 207 |
205 HttpAuthController::~HttpAuthController() { | 208 HttpAuthController::~HttpAuthController() { |
206 DCHECK(CalledOnValidThread()); | 209 DCHECK(CalledOnValidThread()); |
207 } | 210 } |
208 | 211 |
209 int HttpAuthController::MaybeGenerateAuthToken( | 212 int HttpAuthController::MaybeGenerateAuthToken( |
210 const HttpRequestInfo* request, const CompletionCallback& callback, | 213 const HttpRequestInfo* request, const CompletionCallback& callback, |
211 const BoundNetLog& net_log) { | 214 const BoundNetLog& net_log) { |
212 DCHECK(CalledOnValidThread()); | 215 DCHECK(CalledOnValidThread()); |
213 DCHECK(request); | 216 DCHECK(callback_.is_null()); |
| 217 DCHECK(!callback.is_null()); |
| 218 |
214 bool needs_auth = HaveAuth() || SelectPreemptiveAuth(net_log); | 219 bool needs_auth = HaveAuth() || SelectPreemptiveAuth(net_log); |
215 if (!needs_auth) | 220 if (!needs_auth) |
216 return OK; | 221 return OK; |
217 const AuthCredentials* credentials = NULL; | 222 request_info_ = request; |
218 if (identity_.source != HttpAuth::IDENT_SRC_DEFAULT_CREDENTIALS) | 223 next_state_ = STATE_GENERATE_TOKEN; |
219 credentials = &identity_.credentials; | 224 net_log_ = net_log; |
220 DCHECK(auth_token_.empty()); | 225 int rv = DoLoop(OK); |
221 DCHECK(callback_.is_null()); | |
222 int rv = handler_->GenerateAuthToken( | |
223 credentials, *request, | |
224 base::Bind(&HttpAuthController::OnIOComplete, base::Unretained(this)), | |
225 &auth_token_); | |
226 if (DisableOnAuthHandlerResult(rv)) | |
227 rv = OK; | |
228 if (rv == ERR_IO_PENDING) | 226 if (rv == ERR_IO_PENDING) |
229 callback_ = callback; | 227 callback_ = callback; |
230 else | |
231 OnIOComplete(rv); | |
232 return rv; | 228 return rv; |
233 } | 229 } |
234 | 230 |
235 bool HttpAuthController::SelectPreemptiveAuth(const BoundNetLog& net_log) { | 231 bool HttpAuthController::SelectPreemptiveAuth(const BoundNetLog& net_log) { |
236 DCHECK(CalledOnValidThread()); | 232 DCHECK(CalledOnValidThread()); |
237 DCHECK(!HaveAuth()); | 233 DCHECK(!HaveAuth()); |
238 DCHECK(identity_.invalid); | 234 DCHECK(identity_.invalid); |
239 | 235 |
240 // Don't do preemptive authorization if the URL contains a username:password, | 236 // Don't do preemptive authorization if the URL contains a username:password, |
241 // since we must first be challenged in order to use the URL's identity. | 237 // since we must first be challenged in order to use the URL's identity. |
242 if (auth_url_.has_username()) | 238 if (auth_url_.has_username()) |
243 return false; | 239 return false; |
244 | 240 |
245 // SelectPreemptiveAuth() is on the critical path for each request, so it | 241 // SelectPreemptiveAuth() is on the critical path for each request, so it |
246 // is expected to be fast. LookupByPath() is fast in the common case, since | 242 // is expected to be fast. LookupByPath() is fast in the common case, since |
247 // the number of http auth cache entries is expected to be very small. | 243 // the number of http auth cache entries is expected to be very small. |
248 // (For most users in fact, it will be 0.) | 244 // (For most users in fact, it will be 0.) |
249 HttpAuthCache::Entry* entry = http_auth_cache_->LookupByPath( | 245 HttpAuthCache::Entry* entry = http_auth_cache_->LookupByPath( |
250 auth_origin_, auth_path_); | 246 auth_origin_, auth_path_); |
251 if (!entry) | 247 if (!entry) |
252 return false; | 248 return false; |
253 | 249 |
254 // Try to create a handler using the previous auth challenge. | 250 // Try to create a handler using the previous auth challenge. |
255 std::string challenge = entry->auth_challenge(); | |
256 HttpAuthChallengeTokenizer tokenizer(challenge.begin(), challenge.end()); | |
257 scoped_ptr<HttpAuthHandler> handler_preemptive = | 251 scoped_ptr<HttpAuthHandler> handler_preemptive = |
258 http_auth_handler_factory_->CreateAndInitPreemptiveAuthHandler( | 252 http_auth_handler_factory_->CreateAndInitPreemptiveAuthHandler( |
259 entry, tokenizer, target_, net_log); | 253 entry, target_, net_log); |
260 if (!handler_preemptive) | 254 if (!handler_preemptive) |
261 return false; | 255 return false; |
262 | 256 |
263 // Set the state | 257 // Set the state |
264 identity_.source = HttpAuth::IDENT_SRC_PATH_LOOKUP; | 258 identity_.source = HttpAuth::IDENT_SRC_PATH_LOOKUP; |
265 identity_.invalid = false; | 259 identity_.invalid = false; |
266 identity_.credentials = entry->credentials(); | 260 identity_.credentials = entry->credentials(); |
267 handler_.swap(handler_preemptive); | 261 handler_.swap(handler_preemptive); |
268 return true; | 262 return true; |
269 } | 263 } |
270 | 264 |
271 void HttpAuthController::AddAuthorizationHeader( | 265 void HttpAuthController::AddAuthorizationHeader( |
272 HttpRequestHeaders* authorization_headers) { | 266 HttpRequestHeaders* authorization_headers) { |
273 DCHECK(CalledOnValidThread()); | 267 DCHECK(CalledOnValidThread()); |
274 DCHECK(HaveAuth()); | 268 DCHECK(HaveAuth()); |
275 // auth_token_ can be empty if we encountered a permanent error with | 269 // auth_token_ can be empty if we encountered a permanent error with |
276 // the auth scheme and want to retry. | 270 // the auth scheme and want to retry. |
277 if (!auth_token_.empty()) { | 271 if (!auth_token_.empty()) { |
278 authorization_headers->SetHeader( | 272 authorization_headers->SetHeader( |
279 HttpAuth::GetAuthorizationHeaderName(target_), auth_token_); | 273 HttpAuth::GetAuthorizationHeaderName(target_), auth_token_); |
280 auth_token_.clear(); | 274 auth_token_.clear(); |
281 } | 275 } |
282 } | 276 } |
283 | 277 |
284 int HttpAuthController::HandleAuthChallenge( | 278 int HttpAuthController::HandleAuthChallenge( |
285 scoped_refptr<HttpResponseHeaders> headers, | 279 const HttpResponseInfo& response_info, |
286 bool do_not_send_server_auth, | 280 const CompletionCallback& callback, |
287 bool establishing_tunnel, | |
288 const BoundNetLog& net_log) { | 281 const BoundNetLog& net_log) { |
289 DCHECK(CalledOnValidThread()); | 282 DCHECK(CalledOnValidThread()); |
290 DCHECK(headers.get()); | 283 DCHECK(response_info.headers.get()); |
291 DCHECK(auth_origin_.is_valid()); | 284 DCHECK(auth_origin_.is_valid()); |
292 VLOG(1) << "The " << HttpAuth::GetAuthTargetString(target_) << " " | 285 DCHECK(callback_.is_null()); |
293 << auth_origin_ << " requested auth " | 286 DCHECK(!callback.is_null()); |
294 << AuthChallengeLogMessage(headers.get()); | |
295 | 287 |
296 // Give the existing auth handler first try at the authentication headers. | 288 next_state_ = HaveAuthHandler() ? STATE_HANDLE_ANOTHER_CHALLENGE |
297 // This will also evict the entry in the HttpAuthCache if the previous | 289 : STATE_CHOOSE_CHALLENGE; |
298 // challenge appeared to be rejected, or is using a stale nonce in the Digest | 290 response_info_ = &response_info; |
299 // case. | 291 net_log_ = net_log; |
300 if (HaveAuth()) { | 292 int result = DoLoop(OK); |
301 std::string challenge_used; | |
302 HttpAuth::AuthorizationResult result = | |
303 HttpAuth::HandleChallengeResponse(handler_.get(), | |
304 headers.get(), | |
305 target_, | |
306 disabled_schemes_, | |
307 &challenge_used); | |
308 switch (result) { | |
309 case HttpAuth::AUTHORIZATION_RESULT_ACCEPT: | |
310 break; | |
311 case HttpAuth::AUTHORIZATION_RESULT_INVALID: | |
312 InvalidateCurrentHandler(INVALIDATE_HANDLER_AND_CACHED_CREDENTIALS); | |
313 break; | |
314 case HttpAuth::AUTHORIZATION_RESULT_REJECT: | |
315 HistogramAuthEvent(handler_.get(), AUTH_EVENT_REJECT); | |
316 InvalidateCurrentHandler(INVALIDATE_HANDLER_AND_CACHED_CREDENTIALS); | |
317 break; | |
318 case HttpAuth::AUTHORIZATION_RESULT_STALE: | |
319 if (http_auth_cache_->UpdateStaleChallenge(auth_origin_, | |
320 handler_->realm(), | |
321 handler_->auth_scheme(), | |
322 challenge_used)) { | |
323 InvalidateCurrentHandler(INVALIDATE_HANDLER); | |
324 } else { | |
325 // It's possible that a server could incorrectly issue a stale | |
326 // response when the entry is not in the cache. Just evict the | |
327 // current value from the cache. | |
328 InvalidateCurrentHandler(INVALIDATE_HANDLER_AND_CACHED_CREDENTIALS); | |
329 } | |
330 break; | |
331 case HttpAuth::AUTHORIZATION_RESULT_DIFFERENT_REALM: | |
332 // If the server changes the authentication realm in a | |
333 // subsequent challenge, invalidate cached credentials for the | |
334 // previous realm. If the server rejects a preemptive | |
335 // authorization and requests credentials for a different | |
336 // realm, we keep the cached credentials. | |
337 InvalidateCurrentHandler( | |
338 (identity_.source == HttpAuth::IDENT_SRC_PATH_LOOKUP) ? | |
339 INVALIDATE_HANDLER : | |
340 INVALIDATE_HANDLER_AND_CACHED_CREDENTIALS); | |
341 break; | |
342 default: | |
343 NOTREACHED(); | |
344 break; | |
345 } | |
346 } | |
347 | 293 |
348 identity_.invalid = true; | 294 if (result == ERR_IO_PENDING) |
349 | 295 callback_ = callback; |
350 bool can_send_auth = (target_ != HttpAuth::AUTH_SERVER || | 296 return result; |
351 !do_not_send_server_auth); | |
352 | |
353 do { | |
354 if (!handler_.get() && can_send_auth) { | |
355 // Find the best authentication challenge that we support. | |
356 HttpAuth::ChooseBestChallenge(http_auth_handler_factory_, | |
357 headers.get(), | |
358 target_, | |
359 auth_origin_, | |
360 disabled_schemes_, | |
361 net_log, | |
362 &handler_); | |
363 if (handler_.get()) | |
364 HistogramAuthEvent(handler_.get(), AUTH_EVENT_START); | |
365 } | |
366 | |
367 if (!handler_.get()) { | |
368 if (establishing_tunnel) { | |
369 LOG(ERROR) << "Can't perform auth to the " | |
370 << HttpAuth::GetAuthTargetString(target_) << " " | |
371 << auth_origin_ << " when establishing a tunnel" | |
372 << AuthChallengeLogMessage(headers.get()); | |
373 | |
374 // We are establishing a tunnel, we can't show the error page because an | |
375 // active network attacker could control its contents. Instead, we just | |
376 // fail to establish the tunnel. | |
377 DCHECK(target_ == HttpAuth::AUTH_PROXY); | |
378 return ERR_PROXY_AUTH_UNSUPPORTED; | |
379 } | |
380 // We found no supported challenge -- let the transaction continue so we | |
381 // end up displaying the error page. | |
382 return OK; | |
383 } | |
384 | |
385 if (handler_->NeedsIdentity()) { | |
386 // Pick a new auth identity to try, by looking to the URL and auth cache. | |
387 // If an identity to try is found, it is saved to identity_. | |
388 SelectNextAuthIdentityToTry(); | |
389 } else { | |
390 // Proceed with the existing identity or a null identity. | |
391 identity_.invalid = false; | |
392 } | |
393 | |
394 // From this point on, we are restartable. | |
395 | |
396 if (identity_.invalid) { | |
397 // We have exhausted all identity possibilities. | |
398 if (!handler_->AllowsExplicitCredentials()) { | |
399 // If the handler doesn't accept explicit credentials, then we need to | |
400 // choose a different auth scheme. | |
401 HistogramAuthEvent(handler_.get(), AUTH_EVENT_REJECT); | |
402 InvalidateCurrentHandler(INVALIDATE_HANDLER_AND_DISABLE_SCHEME); | |
403 } else { | |
404 // Pass the challenge information back to the client. | |
405 PopulateAuthChallenge(); | |
406 } | |
407 } else { | |
408 auth_info_ = NULL; | |
409 } | |
410 | |
411 // If we get here and we don't have a handler_, that's because we | |
412 // invalidated it due to not having any viable identities to use with it. Go | |
413 // back and try again. | |
414 // TODO(asanka): Instead we should create a priority list of | |
415 // <handler,identity> and iterate through that. | |
416 } while(!handler_.get()); | |
417 return OK; | |
418 } | 297 } |
419 | 298 |
420 void HttpAuthController::ResetAuth(const AuthCredentials& credentials) { | 299 void HttpAuthController::ResetAuth(const AuthCredentials& credentials) { |
421 DCHECK(CalledOnValidThread()); | 300 DCHECK(CalledOnValidThread()); |
422 DCHECK(identity_.invalid || credentials.Empty()); | 301 DCHECK(identity_.invalid || credentials.Empty()); |
423 | 302 |
424 if (identity_.invalid) { | 303 if (identity_.invalid) { |
425 // Update the credentials. | 304 // Update the credentials. |
426 identity_.source = HttpAuth::IDENT_SRC_EXTERNAL; | 305 identity_.source = HttpAuth::IDENT_SRC_EXTERNAL; |
427 identity_.invalid = false; | 306 identity_.invalid = false; |
(...skipping 104 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
532 identity_.invalid = false; | 411 identity_.invalid = false; |
533 default_credentials_used_ = true; | 412 default_credentials_used_ = true; |
534 return true; | 413 return true; |
535 } | 414 } |
536 | 415 |
537 return false; | 416 return false; |
538 } | 417 } |
539 | 418 |
540 void HttpAuthController::PopulateAuthChallenge() { | 419 void HttpAuthController::PopulateAuthChallenge() { |
541 DCHECK(CalledOnValidThread()); | 420 DCHECK(CalledOnValidThread()); |
| 421 DCHECK(!auth_info_); |
542 | 422 |
543 // Populates response_.auth_challenge with the authentication challenge info. | 423 // Populates response_.auth_challenge with the authentication challenge info. |
544 // This info is consumed by URLRequestHttpJob::GetAuthChallengeInfo(). | 424 // This info is consumed by URLRequestHttpJob::GetAuthChallengeInfo(). |
545 | 425 |
546 auth_info_ = new AuthChallengeInfo; | 426 auth_info_ = new AuthChallengeInfo; |
547 auth_info_->is_proxy = (target_ == HttpAuth::AUTH_PROXY); | 427 auth_info_->is_proxy = (target_ == HttpAuth::AUTH_PROXY); |
548 auth_info_->challenger = HostPortPair::FromURL(auth_origin_); | 428 auth_info_->challenger = HostPortPair::FromURL(auth_origin_); |
549 auth_info_->scheme = handler_->auth_scheme(); | 429 auth_info_->scheme = handler_->auth_scheme(); |
550 auth_info_->realm = handler_->realm(); | 430 auth_info_->realm = handler_->realm(); |
551 } | 431 } |
(...skipping 23 matching lines...) Expand all Loading... |
575 auth_token_.clear(); | 455 auth_token_.clear(); |
576 return true; | 456 return true; |
577 | 457 |
578 default: | 458 default: |
579 return false; | 459 return false; |
580 } | 460 } |
581 } | 461 } |
582 | 462 |
583 void HttpAuthController::OnIOComplete(int result) { | 463 void HttpAuthController::OnIOComplete(int result) { |
584 DCHECK(CalledOnValidThread()); | 464 DCHECK(CalledOnValidThread()); |
585 if (DisableOnAuthHandlerResult(result)) | 465 result = DoLoop(result); |
586 result = OK; | 466 |
587 if (!callback_.is_null()) { | 467 if (result != ERR_IO_PENDING && !callback_.is_null()) { |
588 CompletionCallback c = callback_; | 468 base::ResetAndReturn(&callback_).Run(result); |
589 callback_.Reset(); | |
590 c.Run(result); | |
591 } | 469 } |
592 } | 470 } |
593 | 471 |
594 scoped_refptr<AuthChallengeInfo> HttpAuthController::auth_info() { | 472 scoped_refptr<AuthChallengeInfo> HttpAuthController::auth_info() { |
595 DCHECK(CalledOnValidThread()); | 473 DCHECK(CalledOnValidThread()); |
596 return auth_info_; | 474 return auth_info_; |
597 } | 475 } |
598 | 476 |
599 bool HttpAuthController::IsAuthSchemeDisabled(const std::string& scheme) const { | 477 bool HttpAuthController::IsAuthSchemeDisabled(const std::string& scheme) const { |
600 DCHECK(CalledOnValidThread()); | 478 DCHECK(CalledOnValidThread()); |
601 return disabled_schemes_.Contains(scheme); | 479 return disabled_schemes_.Contains(scheme); |
602 } | 480 } |
603 | 481 |
604 void HttpAuthController::DisableAuthScheme(const std::string& scheme) { | 482 void HttpAuthController::DisableAuthScheme(const std::string& scheme) { |
605 DCHECK(CalledOnValidThread()); | 483 DCHECK(CalledOnValidThread()); |
606 disabled_schemes_.Add(scheme); | 484 disabled_schemes_.Add(scheme); |
607 } | 485 } |
608 | 486 |
609 void HttpAuthController::DisableEmbeddedIdentity() { | 487 void HttpAuthController::DisableEmbeddedIdentity() { |
610 DCHECK(CalledOnValidThread()); | 488 DCHECK(CalledOnValidThread()); |
611 embedded_identity_used_ = true; | 489 embedded_identity_used_ = true; |
612 } | 490 } |
613 | 491 |
| 492 int HttpAuthController::DoLoop(int result) { |
| 493 DCHECK(CalledOnValidThread()); |
| 494 |
| 495 do { |
| 496 State state = next_state_; |
| 497 next_state_ = STATE_NONE; |
| 498 |
| 499 switch (state) { |
| 500 case STATE_CHOOSE_CHALLENGE: |
| 501 DCHECK_EQ(OK, result); |
| 502 result = DoChooseChallenge(); |
| 503 break; |
| 504 |
| 505 case STATE_TRY_NEXT_CHALLENGE: |
| 506 DCHECK_EQ(OK, result); |
| 507 result = DoTryNextChallenge(); |
| 508 break; |
| 509 |
| 510 case STATE_TRY_NEXT_CHALLENGE_COMPLETE: |
| 511 result = DoTryNextChallengeComplete(result); |
| 512 break; |
| 513 |
| 514 case STATE_CHOOSE_IDENTITY: |
| 515 DCHECK_EQ(OK, result); |
| 516 result = DoChooseIdentity(); |
| 517 break; |
| 518 |
| 519 case STATE_HANDLE_ANOTHER_CHALLENGE: |
| 520 DCHECK_EQ(OK, result); |
| 521 result = DoHandleAnotherChallenge(); |
| 522 break; |
| 523 |
| 524 case STATE_HANDLE_ANOTHER_CHALLENGE_COMPLETE: |
| 525 result = DoHandleAnotherChallengeComplete(result); |
| 526 break; |
| 527 |
| 528 case STATE_GENERATE_TOKEN: |
| 529 DCHECK_EQ(OK, result); |
| 530 result = DoGenerateToken(); |
| 531 break; |
| 532 |
| 533 case STATE_GENERATE_TOKEN_COMPLETE: |
| 534 result = DoGenerateTokenComplete(result); |
| 535 break; |
| 536 |
| 537 case STATE_NONE: |
| 538 NOTREACHED() << "next_state_: " << next_state_; |
| 539 } |
| 540 } while (next_state_ != STATE_NONE && result != ERR_IO_PENDING); |
| 541 |
| 542 if (result != ERR_IO_PENDING) { |
| 543 // Exiting the state machine. |
| 544 request_info_ = nullptr; |
| 545 response_info_ = nullptr; |
| 546 net_log_ = BoundNetLog(); |
| 547 } |
| 548 |
| 549 return result; |
| 550 } |
| 551 |
| 552 int HttpAuthController::DoChooseChallenge() { |
| 553 DCHECK(response_info_); |
| 554 DCHECK(response_info_->headers.get()); |
| 555 DCHECK(!handler_); |
| 556 DCHECK(auth_token_.empty()); |
| 557 |
| 558 auth_info_ = nullptr; |
| 559 |
| 560 std::priority_queue<CandidateChallenge> old_challenges; |
| 561 candidate_challenges_.swap(old_challenges); |
| 562 |
| 563 void* iter = nullptr; |
| 564 const std::string header_name = HttpAuth::GetChallengeHeaderName(target_); |
| 565 std::string current_challenge; |
| 566 while (response_info_->headers->EnumerateHeader(&iter, header_name, |
| 567 ¤t_challenge)) { |
| 568 HttpAuthChallengeTokenizer tokenizer(current_challenge.begin(), |
| 569 current_challenge.end()); |
| 570 std::string current_scheme = tokenizer.NormalizedScheme(); |
| 571 if (current_scheme.empty()) |
| 572 continue; |
| 573 if (IsAuthSchemeDisabled(current_scheme)) |
| 574 continue; |
| 575 |
| 576 CandidateChallenge candidate_challenge; |
| 577 candidate_challenge.scheme = current_scheme; |
| 578 candidate_challenge.priority = GetSchemePriority(current_scheme); |
| 579 candidate_challenge.challenge = current_challenge; |
| 580 candidate_challenges_.push(candidate_challenge); |
| 581 } |
| 582 next_state_ = STATE_TRY_NEXT_CHALLENGE; |
| 583 return OK; |
| 584 } |
| 585 |
| 586 int HttpAuthController::DoTryNextChallenge() { |
| 587 DCHECK(!handler_); |
| 588 DCHECK(auth_token_.empty()); |
| 589 DCHECK(response_info_); |
| 590 |
| 591 for (; !handler_ && !candidate_challenges_.empty(); |
| 592 candidate_challenges_.pop()) { |
| 593 const CandidateChallenge& candidate = candidate_challenges_.top(); |
| 594 selected_auth_challenge_ = candidate.challenge; |
| 595 if (IsAuthSchemeDisabled(candidate.scheme)) |
| 596 continue; |
| 597 handler_ = http_auth_handler_factory_->CreateAuthHandlerForScheme( |
| 598 candidate.scheme); |
| 599 } |
| 600 |
| 601 if (!handler_) |
| 602 return OK; |
| 603 |
| 604 HttpAuthChallengeTokenizer tokenizer(selected_auth_challenge_.begin(), |
| 605 selected_auth_challenge_.end()); |
| 606 next_state_ = STATE_TRY_NEXT_CHALLENGE_COMPLETE; |
| 607 return handler_->HandleInitialChallenge(tokenizer, *response_info_, target_, |
| 608 auth_origin_, net_log_, io_callback_); |
| 609 } |
| 610 |
| 611 int HttpAuthController::DoTryNextChallengeComplete(int result) { |
| 612 DCHECK(handler_); |
| 613 |
| 614 if (result != OK) { |
| 615 InvalidateCurrentHandler(INVALIDATE_HANDLER); |
| 616 next_state_ = STATE_TRY_NEXT_CHALLENGE; |
| 617 return OK; |
| 618 } |
| 619 |
| 620 HistogramAuthEvent(handler_.get(), AUTH_EVENT_START); |
| 621 next_state_ = STATE_CHOOSE_IDENTITY; |
| 622 return OK; |
| 623 } |
| 624 |
| 625 int HttpAuthController::DoChooseIdentity() { |
| 626 DCHECK(handler_.get()); |
| 627 DCHECK(identity_.invalid); |
| 628 DCHECK(!auth_info_); |
| 629 |
| 630 if (handler_->NeedsIdentity()) { |
| 631 SelectNextAuthIdentityToTry(); |
| 632 } else { |
| 633 // TODO(asanka): This is a false flag and shouldn't be necessary. Instead |
| 634 // rely on NeedsIdentity() to determine if the handler needs an identity. |
| 635 identity_.invalid = false; |
| 636 } |
| 637 |
| 638 if (!identity_.invalid) { |
| 639 return OK; |
| 640 } |
| 641 |
| 642 if (!handler_->AllowsExplicitCredentials()) { |
| 643 HistogramAuthEvent(handler_.get(), AUTH_EVENT_REJECT); |
| 644 InvalidateCurrentHandler(INVALIDATE_HANDLER_AND_DISABLE_SCHEME); |
| 645 next_state_ = STATE_TRY_NEXT_CHALLENGE; |
| 646 return OK; |
| 647 } |
| 648 |
| 649 PopulateAuthChallenge(); |
| 650 return OK; |
| 651 } |
| 652 |
| 653 int HttpAuthController::DoHandleAnotherChallenge() { |
| 654 DCHECK(HaveAuth()); |
| 655 DCHECK(response_info_); |
| 656 DCHECK(response_info_->headers.get()); |
| 657 DCHECK(auth_token_.empty()); |
| 658 |
| 659 next_state_ = STATE_HANDLE_ANOTHER_CHALLENGE_COMPLETE; |
| 660 auth_info_ = nullptr; |
| 661 |
| 662 selected_auth_challenge_.clear(); |
| 663 if (disabled_schemes_.Contains(handler_->auth_scheme())) |
| 664 return HttpAuth::AUTHORIZATION_RESULT_REJECT; |
| 665 |
| 666 std::string current_scheme_name = handler_->auth_scheme(); |
| 667 std::string challenge_header_name = HttpAuth::GetChallengeHeaderName(target_); |
| 668 |
| 669 std::string challenge; |
| 670 void* iter = nullptr; |
| 671 HttpAuth::AuthorizationResult authorization_result = |
| 672 HttpAuth::AUTHORIZATION_RESULT_INVALID; |
| 673 while (response_info_->headers->EnumerateHeader(&iter, challenge_header_name, |
| 674 &challenge)) { |
| 675 HttpAuthChallengeTokenizer tokenizer(challenge.begin(), challenge.end()); |
| 676 if (!tokenizer.SchemeIs(current_scheme_name)) |
| 677 continue; |
| 678 |
| 679 authorization_result = handler_->HandleAnotherChallenge(tokenizer); |
| 680 if (authorization_result != HttpAuth::AUTHORIZATION_RESULT_INVALID) { |
| 681 selected_auth_challenge_ = challenge; |
| 682 return authorization_result; |
| 683 } |
| 684 } |
| 685 |
| 686 return HttpAuth::AUTHORIZATION_RESULT_REJECT; |
| 687 } |
| 688 |
| 689 int HttpAuthController::DoHandleAnotherChallengeComplete(int result) { |
| 690 if (result < 0) |
| 691 return result; |
| 692 |
| 693 HttpAuth::AuthorizationResult auth_result = |
| 694 static_cast<HttpAuth::AuthorizationResult>(result); |
| 695 switch (auth_result) { |
| 696 case HttpAuth::AUTHORIZATION_RESULT_ACCEPT: |
| 697 return OK; |
| 698 |
| 699 case HttpAuth::AUTHORIZATION_RESULT_INVALID: |
| 700 InvalidateCurrentHandler(INVALIDATE_HANDLER_AND_CACHED_CREDENTIALS); |
| 701 break; |
| 702 |
| 703 case HttpAuth::AUTHORIZATION_RESULT_REJECT: |
| 704 HistogramAuthEvent(handler_.get(), AUTH_EVENT_REJECT); |
| 705 InvalidateCurrentHandler(INVALIDATE_HANDLER_AND_CACHED_CREDENTIALS); |
| 706 break; |
| 707 |
| 708 case HttpAuth::AUTHORIZATION_RESULT_STALE: |
| 709 if (http_auth_cache_->UpdateStaleChallenge( |
| 710 auth_origin_, handler_->realm(), handler_->auth_scheme(), |
| 711 selected_auth_challenge_)) { |
| 712 InvalidateCurrentHandler(INVALIDATE_HANDLER); |
| 713 } else { |
| 714 InvalidateCurrentHandler(INVALIDATE_HANDLER_AND_CACHED_CREDENTIALS); |
| 715 } |
| 716 break; |
| 717 |
| 718 case HttpAuth::AUTHORIZATION_RESULT_DIFFERENT_REALM: |
| 719 InvalidateCurrentHandler(identity_.source == |
| 720 HttpAuth::IDENT_SRC_PATH_LOOKUP |
| 721 ? INVALIDATE_HANDLER |
| 722 : INVALIDATE_HANDLER_AND_CACHED_CREDENTIALS); |
| 723 break; |
| 724 } |
| 725 |
| 726 DCHECK(!handler_.get()); |
| 727 next_state_ = STATE_CHOOSE_CHALLENGE; |
| 728 return OK; |
| 729 } |
| 730 |
| 731 int HttpAuthController::DoGenerateToken() { |
| 732 DCHECK(handler_.get()); |
| 733 DCHECK(!identity_.invalid || !handler_->NeedsIdentity()); |
| 734 DCHECK(request_info_); |
| 735 DCHECK(HaveAuth()); |
| 736 DCHECK(auth_token_.empty()); |
| 737 |
| 738 next_state_ = STATE_GENERATE_TOKEN_COMPLETE; |
| 739 const AuthCredentials* credentials = |
| 740 identity_.source == HttpAuth::IDENT_SRC_DEFAULT_CREDENTIALS |
| 741 ? nullptr |
| 742 : &identity_.credentials; |
| 743 return handler_->GenerateAuthToken(credentials, *request_info_, io_callback_, |
| 744 &auth_token_); |
| 745 } |
| 746 |
| 747 int HttpAuthController::DoGenerateTokenComplete(int result) { |
| 748 if (DisableOnAuthHandlerResult(result)) |
| 749 result = OK; |
| 750 return result; |
| 751 } |
| 752 |
614 } // namespace net | 753 } // namespace net |
OLD | NEW |