| OLD | NEW |
| (Empty) |
| 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 | |
| 3 // found in the LICENSE file. | |
| 4 | |
| 5 #include "net/http/http_auth_controller.h" | |
| 6 | |
| 7 #include "base/bind.h" | |
| 8 #include "base/bind_helpers.h" | |
| 9 #include "base/metrics/histogram.h" | |
| 10 #include "base/strings/string_util.h" | |
| 11 #include "base/strings/utf_string_conversions.h" | |
| 12 #include "base/threading/platform_thread.h" | |
| 13 #include "net/base/auth.h" | |
| 14 #include "net/base/net_util.h" | |
| 15 #include "net/dns/host_resolver.h" | |
| 16 #include "net/http/http_auth_handler.h" | |
| 17 #include "net/http/http_auth_handler_factory.h" | |
| 18 #include "net/http/http_network_session.h" | |
| 19 #include "net/http/http_request_headers.h" | |
| 20 #include "net/http/http_request_info.h" | |
| 21 #include "net/http/http_response_headers.h" | |
| 22 | |
| 23 namespace net { | |
| 24 | |
| 25 namespace { | |
| 26 | |
| 27 // Returns a log message for all the response headers related to the auth | |
| 28 // challenge. | |
| 29 std::string AuthChallengeLogMessage(HttpResponseHeaders* headers) { | |
| 30 std::string msg; | |
| 31 std::string header_val; | |
| 32 void* iter = NULL; | |
| 33 while (headers->EnumerateHeader(&iter, "proxy-authenticate", &header_val)) { | |
| 34 msg.append("\n Has header Proxy-Authenticate: "); | |
| 35 msg.append(header_val); | |
| 36 } | |
| 37 | |
| 38 iter = NULL; | |
| 39 while (headers->EnumerateHeader(&iter, "www-authenticate", &header_val)) { | |
| 40 msg.append("\n Has header WWW-Authenticate: "); | |
| 41 msg.append(header_val); | |
| 42 } | |
| 43 | |
| 44 // RFC 4559 requires that a proxy indicate its support of NTLM/Negotiate | |
| 45 // authentication with a "Proxy-Support: Session-Based-Authentication" | |
| 46 // response header. | |
| 47 iter = NULL; | |
| 48 while (headers->EnumerateHeader(&iter, "proxy-support", &header_val)) { | |
| 49 msg.append("\n Has header Proxy-Support: "); | |
| 50 msg.append(header_val); | |
| 51 } | |
| 52 | |
| 53 return msg; | |
| 54 } | |
| 55 | |
| 56 enum AuthEvent { | |
| 57 AUTH_EVENT_START = 0, | |
| 58 AUTH_EVENT_REJECT, | |
| 59 AUTH_EVENT_MAX, | |
| 60 }; | |
| 61 | |
| 62 enum AuthTarget { | |
| 63 AUTH_TARGET_PROXY = 0, | |
| 64 AUTH_TARGET_SECURE_PROXY, | |
| 65 AUTH_TARGET_SERVER, | |
| 66 AUTH_TARGET_SECURE_SERVER, | |
| 67 AUTH_TARGET_MAX, | |
| 68 }; | |
| 69 | |
| 70 AuthTarget DetermineAuthTarget(const HttpAuthHandler* handler) { | |
| 71 switch (handler->target()) { | |
| 72 case HttpAuth::AUTH_PROXY: | |
| 73 if (handler->origin().SchemeIsSecure()) | |
| 74 return AUTH_TARGET_SECURE_PROXY; | |
| 75 else | |
| 76 return AUTH_TARGET_PROXY; | |
| 77 case HttpAuth::AUTH_SERVER: | |
| 78 if (handler->origin().SchemeIsSecure()) | |
| 79 return AUTH_TARGET_SECURE_SERVER; | |
| 80 else | |
| 81 return AUTH_TARGET_SERVER; | |
| 82 default: | |
| 83 NOTREACHED(); | |
| 84 return AUTH_TARGET_MAX; | |
| 85 } | |
| 86 } | |
| 87 | |
| 88 // Records the number of authentication events per authentication scheme. | |
| 89 void HistogramAuthEvent(HttpAuthHandler* handler, AuthEvent auth_event) { | |
| 90 #if !defined(NDEBUG) | |
| 91 // Note: The on-same-thread check is intentionally not using a lock | |
| 92 // to protect access to first_thread. This method is meant to be only | |
| 93 // used on the same thread, in which case there are no race conditions. If | |
| 94 // there are race conditions (say, a read completes during a partial write), | |
| 95 // the DCHECK will correctly fail. | |
| 96 static base::PlatformThreadId first_thread = | |
| 97 base::PlatformThread::CurrentId(); | |
| 98 DCHECK_EQ(first_thread, base::PlatformThread::CurrentId()); | |
| 99 #endif | |
| 100 | |
| 101 HttpAuth::Scheme auth_scheme = handler->auth_scheme(); | |
| 102 DCHECK(auth_scheme >= 0 && auth_scheme < HttpAuth::AUTH_SCHEME_MAX); | |
| 103 | |
| 104 // Record start and rejection events for authentication. | |
| 105 // | |
| 106 // The results map to: | |
| 107 // Basic Start: 0 | |
| 108 // Basic Reject: 1 | |
| 109 // Digest Start: 2 | |
| 110 // Digest Reject: 3 | |
| 111 // NTLM Start: 4 | |
| 112 // NTLM Reject: 5 | |
| 113 // Negotiate Start: 6 | |
| 114 // Negotiate Reject: 7 | |
| 115 static const int kEventBucketsEnd = | |
| 116 HttpAuth::AUTH_SCHEME_MAX * AUTH_EVENT_MAX; | |
| 117 int event_bucket = auth_scheme * AUTH_EVENT_MAX + auth_event; | |
| 118 DCHECK(event_bucket >= 0 && event_bucket < kEventBucketsEnd); | |
| 119 UMA_HISTOGRAM_ENUMERATION("Net.HttpAuthCount", event_bucket, | |
| 120 kEventBucketsEnd); | |
| 121 | |
| 122 // Record the target of the authentication. | |
| 123 // | |
| 124 // The results map to: | |
| 125 // Basic Proxy: 0 | |
| 126 // Basic Secure Proxy: 1 | |
| 127 // Basic Server: 2 | |
| 128 // Basic Secure Server: 3 | |
| 129 // Digest Proxy: 4 | |
| 130 // Digest Secure Proxy: 5 | |
| 131 // Digest Server: 6 | |
| 132 // Digest Secure Server: 7 | |
| 133 // NTLM Proxy: 8 | |
| 134 // NTLM Secure Proxy: 9 | |
| 135 // NTLM Server: 10 | |
| 136 // NTLM Secure Server: 11 | |
| 137 // Negotiate Proxy: 12 | |
| 138 // Negotiate Secure Proxy: 13 | |
| 139 // Negotiate Server: 14 | |
| 140 // Negotiate Secure Server: 15 | |
| 141 if (auth_event != AUTH_EVENT_START) | |
| 142 return; | |
| 143 static const int kTargetBucketsEnd = | |
| 144 HttpAuth::AUTH_SCHEME_MAX * AUTH_TARGET_MAX; | |
| 145 AuthTarget auth_target = DetermineAuthTarget(handler); | |
| 146 int target_bucket = auth_scheme * AUTH_TARGET_MAX + auth_target; | |
| 147 DCHECK(target_bucket >= 0 && target_bucket < kTargetBucketsEnd); | |
| 148 UMA_HISTOGRAM_ENUMERATION("Net.HttpAuthTarget", target_bucket, | |
| 149 kTargetBucketsEnd); | |
| 150 } | |
| 151 | |
| 152 } // namespace | |
| 153 | |
| 154 HttpAuthController::HttpAuthController( | |
| 155 HttpAuth::Target target, | |
| 156 const GURL& auth_url, | |
| 157 HttpAuthCache* http_auth_cache, | |
| 158 HttpAuthHandlerFactory* http_auth_handler_factory) | |
| 159 : target_(target), | |
| 160 auth_url_(auth_url), | |
| 161 auth_origin_(auth_url.GetOrigin()), | |
| 162 auth_path_(HttpAuth::AUTH_PROXY ? std::string() : auth_url.path()), | |
| 163 embedded_identity_used_(false), | |
| 164 default_credentials_used_(false), | |
| 165 http_auth_cache_(http_auth_cache), | |
| 166 http_auth_handler_factory_(http_auth_handler_factory) { | |
| 167 } | |
| 168 | |
| 169 HttpAuthController::~HttpAuthController() { | |
| 170 DCHECK(CalledOnValidThread()); | |
| 171 } | |
| 172 | |
| 173 int HttpAuthController::MaybeGenerateAuthToken( | |
| 174 const HttpRequestInfo* request, const CompletionCallback& callback, | |
| 175 const BoundNetLog& net_log) { | |
| 176 DCHECK(CalledOnValidThread()); | |
| 177 bool needs_auth = HaveAuth() || SelectPreemptiveAuth(net_log); | |
| 178 if (!needs_auth) | |
| 179 return OK; | |
| 180 const AuthCredentials* credentials = NULL; | |
| 181 if (identity_.source != HttpAuth::IDENT_SRC_DEFAULT_CREDENTIALS) | |
| 182 credentials = &identity_.credentials; | |
| 183 DCHECK(auth_token_.empty()); | |
| 184 DCHECK(callback_.is_null()); | |
| 185 int rv = handler_->GenerateAuthToken( | |
| 186 credentials, request, | |
| 187 base::Bind(&HttpAuthController::OnIOComplete, base::Unretained(this)), | |
| 188 &auth_token_); | |
| 189 if (DisableOnAuthHandlerResult(rv)) | |
| 190 rv = OK; | |
| 191 if (rv == ERR_IO_PENDING) | |
| 192 callback_ = callback; | |
| 193 else | |
| 194 OnIOComplete(rv); | |
| 195 return rv; | |
| 196 } | |
| 197 | |
| 198 bool HttpAuthController::SelectPreemptiveAuth(const BoundNetLog& net_log) { | |
| 199 DCHECK(CalledOnValidThread()); | |
| 200 DCHECK(!HaveAuth()); | |
| 201 DCHECK(identity_.invalid); | |
| 202 | |
| 203 // Don't do preemptive authorization if the URL contains a username:password, | |
| 204 // since we must first be challenged in order to use the URL's identity. | |
| 205 if (auth_url_.has_username()) | |
| 206 return false; | |
| 207 | |
| 208 // SelectPreemptiveAuth() is on the critical path for each request, so it | |
| 209 // is expected to be fast. LookupByPath() is fast in the common case, since | |
| 210 // the number of http auth cache entries is expected to be very small. | |
| 211 // (For most users in fact, it will be 0.) | |
| 212 HttpAuthCache::Entry* entry = http_auth_cache_->LookupByPath( | |
| 213 auth_origin_, auth_path_); | |
| 214 if (!entry) | |
| 215 return false; | |
| 216 | |
| 217 // Try to create a handler using the previous auth challenge. | |
| 218 scoped_ptr<HttpAuthHandler> handler_preemptive; | |
| 219 int rv_create = http_auth_handler_factory_-> | |
| 220 CreatePreemptiveAuthHandlerFromString(entry->auth_challenge(), target_, | |
| 221 auth_origin_, | |
| 222 entry->IncrementNonceCount(), | |
| 223 net_log, &handler_preemptive); | |
| 224 if (rv_create != OK) | |
| 225 return false; | |
| 226 | |
| 227 // Set the state | |
| 228 identity_.source = HttpAuth::IDENT_SRC_PATH_LOOKUP; | |
| 229 identity_.invalid = false; | |
| 230 identity_.credentials = entry->credentials(); | |
| 231 handler_.swap(handler_preemptive); | |
| 232 return true; | |
| 233 } | |
| 234 | |
| 235 void HttpAuthController::AddAuthorizationHeader( | |
| 236 HttpRequestHeaders* authorization_headers) { | |
| 237 DCHECK(CalledOnValidThread()); | |
| 238 DCHECK(HaveAuth()); | |
| 239 // auth_token_ can be empty if we encountered a permanent error with | |
| 240 // the auth scheme and want to retry. | |
| 241 if (!auth_token_.empty()) { | |
| 242 authorization_headers->SetHeader( | |
| 243 HttpAuth::GetAuthorizationHeaderName(target_), auth_token_); | |
| 244 auth_token_.clear(); | |
| 245 } | |
| 246 } | |
| 247 | |
| 248 int HttpAuthController::HandleAuthChallenge( | |
| 249 scoped_refptr<HttpResponseHeaders> headers, | |
| 250 bool do_not_send_server_auth, | |
| 251 bool establishing_tunnel, | |
| 252 const BoundNetLog& net_log) { | |
| 253 DCHECK(CalledOnValidThread()); | |
| 254 DCHECK(headers.get()); | |
| 255 DCHECK(auth_origin_.is_valid()); | |
| 256 VLOG(1) << "The " << HttpAuth::GetAuthTargetString(target_) << " " | |
| 257 << auth_origin_ << " requested auth " | |
| 258 << AuthChallengeLogMessage(headers.get()); | |
| 259 | |
| 260 // Give the existing auth handler first try at the authentication headers. | |
| 261 // This will also evict the entry in the HttpAuthCache if the previous | |
| 262 // challenge appeared to be rejected, or is using a stale nonce in the Digest | |
| 263 // case. | |
| 264 if (HaveAuth()) { | |
| 265 std::string challenge_used; | |
| 266 HttpAuth::AuthorizationResult result = | |
| 267 HttpAuth::HandleChallengeResponse(handler_.get(), | |
| 268 headers.get(), | |
| 269 target_, | |
| 270 disabled_schemes_, | |
| 271 &challenge_used); | |
| 272 switch (result) { | |
| 273 case HttpAuth::AUTHORIZATION_RESULT_ACCEPT: | |
| 274 break; | |
| 275 case HttpAuth::AUTHORIZATION_RESULT_INVALID: | |
| 276 InvalidateCurrentHandler(INVALIDATE_HANDLER_AND_CACHED_CREDENTIALS); | |
| 277 break; | |
| 278 case HttpAuth::AUTHORIZATION_RESULT_REJECT: | |
| 279 HistogramAuthEvent(handler_.get(), AUTH_EVENT_REJECT); | |
| 280 InvalidateCurrentHandler(INVALIDATE_HANDLER_AND_CACHED_CREDENTIALS); | |
| 281 break; | |
| 282 case HttpAuth::AUTHORIZATION_RESULT_STALE: | |
| 283 if (http_auth_cache_->UpdateStaleChallenge(auth_origin_, | |
| 284 handler_->realm(), | |
| 285 handler_->auth_scheme(), | |
| 286 challenge_used)) { | |
| 287 InvalidateCurrentHandler(INVALIDATE_HANDLER); | |
| 288 } else { | |
| 289 // It's possible that a server could incorrectly issue a stale | |
| 290 // response when the entry is not in the cache. Just evict the | |
| 291 // current value from the cache. | |
| 292 InvalidateCurrentHandler(INVALIDATE_HANDLER_AND_CACHED_CREDENTIALS); | |
| 293 } | |
| 294 break; | |
| 295 case HttpAuth::AUTHORIZATION_RESULT_DIFFERENT_REALM: | |
| 296 // If the server changes the authentication realm in a | |
| 297 // subsequent challenge, invalidate cached credentials for the | |
| 298 // previous realm. If the server rejects a preemptive | |
| 299 // authorization and requests credentials for a different | |
| 300 // realm, we keep the cached credentials. | |
| 301 InvalidateCurrentHandler( | |
| 302 (identity_.source == HttpAuth::IDENT_SRC_PATH_LOOKUP) ? | |
| 303 INVALIDATE_HANDLER : | |
| 304 INVALIDATE_HANDLER_AND_CACHED_CREDENTIALS); | |
| 305 break; | |
| 306 default: | |
| 307 NOTREACHED(); | |
| 308 break; | |
| 309 } | |
| 310 } | |
| 311 | |
| 312 identity_.invalid = true; | |
| 313 | |
| 314 bool can_send_auth = (target_ != HttpAuth::AUTH_SERVER || | |
| 315 !do_not_send_server_auth); | |
| 316 | |
| 317 do { | |
| 318 if (!handler_.get() && can_send_auth) { | |
| 319 // Find the best authentication challenge that we support. | |
| 320 HttpAuth::ChooseBestChallenge(http_auth_handler_factory_, | |
| 321 headers.get(), | |
| 322 target_, | |
| 323 auth_origin_, | |
| 324 disabled_schemes_, | |
| 325 net_log, | |
| 326 &handler_); | |
| 327 if (handler_.get()) | |
| 328 HistogramAuthEvent(handler_.get(), AUTH_EVENT_START); | |
| 329 } | |
| 330 | |
| 331 if (!handler_.get()) { | |
| 332 if (establishing_tunnel) { | |
| 333 LOG(ERROR) << "Can't perform auth to the " | |
| 334 << HttpAuth::GetAuthTargetString(target_) << " " | |
| 335 << auth_origin_ << " when establishing a tunnel" | |
| 336 << AuthChallengeLogMessage(headers.get()); | |
| 337 | |
| 338 // We are establishing a tunnel, we can't show the error page because an | |
| 339 // active network attacker could control its contents. Instead, we just | |
| 340 // fail to establish the tunnel. | |
| 341 DCHECK(target_ == HttpAuth::AUTH_PROXY); | |
| 342 return ERR_PROXY_AUTH_UNSUPPORTED; | |
| 343 } | |
| 344 // We found no supported challenge -- let the transaction continue so we | |
| 345 // end up displaying the error page. | |
| 346 return OK; | |
| 347 } | |
| 348 | |
| 349 if (handler_->NeedsIdentity()) { | |
| 350 // Pick a new auth identity to try, by looking to the URL and auth cache. | |
| 351 // If an identity to try is found, it is saved to identity_. | |
| 352 SelectNextAuthIdentityToTry(); | |
| 353 } else { | |
| 354 // Proceed with the existing identity or a null identity. | |
| 355 identity_.invalid = false; | |
| 356 } | |
| 357 | |
| 358 // From this point on, we are restartable. | |
| 359 | |
| 360 if (identity_.invalid) { | |
| 361 // We have exhausted all identity possibilities. | |
| 362 if (!handler_->AllowsExplicitCredentials()) { | |
| 363 // If the handler doesn't accept explicit credentials, then we need to | |
| 364 // choose a different auth scheme. | |
| 365 HistogramAuthEvent(handler_.get(), AUTH_EVENT_REJECT); | |
| 366 InvalidateCurrentHandler(INVALIDATE_HANDLER_AND_DISABLE_SCHEME); | |
| 367 } else { | |
| 368 // Pass the challenge information back to the client. | |
| 369 PopulateAuthChallenge(); | |
| 370 } | |
| 371 } else { | |
| 372 auth_info_ = NULL; | |
| 373 } | |
| 374 | |
| 375 // If we get here and we don't have a handler_, that's because we | |
| 376 // invalidated it due to not having any viable identities to use with it. Go | |
| 377 // back and try again. | |
| 378 // TODO(asanka): Instead we should create a priority list of | |
| 379 // <handler,identity> and iterate through that. | |
| 380 } while(!handler_.get()); | |
| 381 return OK; | |
| 382 } | |
| 383 | |
| 384 void HttpAuthController::ResetAuth(const AuthCredentials& credentials) { | |
| 385 DCHECK(CalledOnValidThread()); | |
| 386 DCHECK(identity_.invalid || credentials.Empty()); | |
| 387 | |
| 388 if (identity_.invalid) { | |
| 389 // Update the credentials. | |
| 390 identity_.source = HttpAuth::IDENT_SRC_EXTERNAL; | |
| 391 identity_.invalid = false; | |
| 392 identity_.credentials = credentials; | |
| 393 } | |
| 394 | |
| 395 DCHECK(identity_.source != HttpAuth::IDENT_SRC_PATH_LOOKUP); | |
| 396 | |
| 397 // Add the auth entry to the cache before restarting. We don't know whether | |
| 398 // the identity is valid yet, but if it is valid we want other transactions | |
| 399 // to know about it. If an entry for (origin, handler->realm()) already | |
| 400 // exists, we update it. | |
| 401 // | |
| 402 // If identity_.source is HttpAuth::IDENT_SRC_NONE or | |
| 403 // HttpAuth::IDENT_SRC_DEFAULT_CREDENTIALS, identity_ contains no | |
| 404 // identity because identity is not required yet or we're using default | |
| 405 // credentials. | |
| 406 // | |
| 407 // TODO(wtc): For NTLM_SSPI, we add the same auth entry to the cache in | |
| 408 // round 1 and round 2, which is redundant but correct. It would be nice | |
| 409 // to add an auth entry to the cache only once, preferrably in round 1. | |
| 410 // See http://crbug.com/21015. | |
| 411 switch (identity_.source) { | |
| 412 case HttpAuth::IDENT_SRC_NONE: | |
| 413 case HttpAuth::IDENT_SRC_DEFAULT_CREDENTIALS: | |
| 414 break; | |
| 415 default: | |
| 416 http_auth_cache_->Add(auth_origin_, handler_->realm(), | |
| 417 handler_->auth_scheme(), handler_->challenge(), | |
| 418 identity_.credentials, auth_path_); | |
| 419 break; | |
| 420 } | |
| 421 } | |
| 422 | |
| 423 bool HttpAuthController::HaveAuthHandler() const { | |
| 424 return handler_.get() != NULL; | |
| 425 } | |
| 426 | |
| 427 bool HttpAuthController::HaveAuth() const { | |
| 428 return handler_.get() && !identity_.invalid; | |
| 429 } | |
| 430 | |
| 431 void HttpAuthController::InvalidateCurrentHandler( | |
| 432 InvalidateHandlerAction action) { | |
| 433 DCHECK(CalledOnValidThread()); | |
| 434 DCHECK(handler_.get()); | |
| 435 | |
| 436 if (action == INVALIDATE_HANDLER_AND_CACHED_CREDENTIALS) | |
| 437 InvalidateRejectedAuthFromCache(); | |
| 438 if (action == INVALIDATE_HANDLER_AND_DISABLE_SCHEME) | |
| 439 DisableAuthScheme(handler_->auth_scheme()); | |
| 440 handler_.reset(); | |
| 441 identity_ = HttpAuth::Identity(); | |
| 442 } | |
| 443 | |
| 444 void HttpAuthController::InvalidateRejectedAuthFromCache() { | |
| 445 DCHECK(CalledOnValidThread()); | |
| 446 DCHECK(HaveAuth()); | |
| 447 | |
| 448 // Clear the cache entry for the identity we just failed on. | |
| 449 // Note: we require the credentials to match before invalidating | |
| 450 // since the entry in the cache may be newer than what we used last time. | |
| 451 http_auth_cache_->Remove(auth_origin_, handler_->realm(), | |
| 452 handler_->auth_scheme(), identity_.credentials); | |
| 453 } | |
| 454 | |
| 455 bool HttpAuthController::SelectNextAuthIdentityToTry() { | |
| 456 DCHECK(CalledOnValidThread()); | |
| 457 DCHECK(handler_.get()); | |
| 458 DCHECK(identity_.invalid); | |
| 459 | |
| 460 // Try to use the username:password encoded into the URL first. | |
| 461 if (target_ == HttpAuth::AUTH_SERVER && auth_url_.has_username() && | |
| 462 !embedded_identity_used_) { | |
| 463 identity_.source = HttpAuth::IDENT_SRC_URL; | |
| 464 identity_.invalid = false; | |
| 465 // Extract the username:password from the URL. | |
| 466 base::string16 username; | |
| 467 base::string16 password; | |
| 468 GetIdentityFromURL(auth_url_, &username, &password); | |
| 469 identity_.credentials.Set(username, password); | |
| 470 embedded_identity_used_ = true; | |
| 471 // TODO(eroman): If the password is blank, should we also try combining | |
| 472 // with a password from the cache? | |
| 473 UMA_HISTOGRAM_BOOLEAN("net.HttpIdentSrcURL", true); | |
| 474 return true; | |
| 475 } | |
| 476 | |
| 477 // Check the auth cache for a realm entry. | |
| 478 HttpAuthCache::Entry* entry = | |
| 479 http_auth_cache_->Lookup(auth_origin_, handler_->realm(), | |
| 480 handler_->auth_scheme()); | |
| 481 | |
| 482 if (entry) { | |
| 483 identity_.source = HttpAuth::IDENT_SRC_REALM_LOOKUP; | |
| 484 identity_.invalid = false; | |
| 485 identity_.credentials = entry->credentials(); | |
| 486 return true; | |
| 487 } | |
| 488 | |
| 489 // Use default credentials (single sign on) if this is the first attempt | |
| 490 // at identity. Do not allow multiple times as it will infinite loop. | |
| 491 // We use default credentials after checking the auth cache so that if | |
| 492 // single sign-on doesn't work, we won't try default credentials for future | |
| 493 // transactions. | |
| 494 if (!default_credentials_used_ && handler_->AllowsDefaultCredentials()) { | |
| 495 identity_.source = HttpAuth::IDENT_SRC_DEFAULT_CREDENTIALS; | |
| 496 identity_.invalid = false; | |
| 497 default_credentials_used_ = true; | |
| 498 return true; | |
| 499 } | |
| 500 | |
| 501 return false; | |
| 502 } | |
| 503 | |
| 504 void HttpAuthController::PopulateAuthChallenge() { | |
| 505 DCHECK(CalledOnValidThread()); | |
| 506 | |
| 507 // Populates response_.auth_challenge with the authentication challenge info. | |
| 508 // This info is consumed by URLRequestHttpJob::GetAuthChallengeInfo(). | |
| 509 | |
| 510 auth_info_ = new AuthChallengeInfo; | |
| 511 auth_info_->is_proxy = (target_ == HttpAuth::AUTH_PROXY); | |
| 512 auth_info_->challenger = HostPortPair::FromURL(auth_origin_); | |
| 513 auth_info_->scheme = HttpAuth::SchemeToString(handler_->auth_scheme()); | |
| 514 auth_info_->realm = handler_->realm(); | |
| 515 } | |
| 516 | |
| 517 bool HttpAuthController::DisableOnAuthHandlerResult(int result) { | |
| 518 DCHECK(CalledOnValidThread()); | |
| 519 | |
| 520 switch (result) { | |
| 521 // Occurs with GSSAPI, if the user has not already logged in. | |
| 522 case ERR_MISSING_AUTH_CREDENTIALS: | |
| 523 | |
| 524 // Can occur with GSSAPI or SSPI if the underlying library reports | |
| 525 // a permanent error. | |
| 526 case ERR_UNSUPPORTED_AUTH_SCHEME: | |
| 527 | |
| 528 // These two error codes represent failures we aren't handling. | |
| 529 case ERR_UNEXPECTED_SECURITY_LIBRARY_STATUS: | |
| 530 case ERR_UNDOCUMENTED_SECURITY_LIBRARY_STATUS: | |
| 531 | |
| 532 // Can be returned by SSPI if the authenticating authority or | |
| 533 // target is not known. | |
| 534 case ERR_MISCONFIGURED_AUTH_ENVIRONMENT: | |
| 535 | |
| 536 // In these cases, disable the current scheme as it cannot | |
| 537 // succeed. | |
| 538 DisableAuthScheme(handler_->auth_scheme()); | |
| 539 auth_token_.clear(); | |
| 540 return true; | |
| 541 | |
| 542 default: | |
| 543 return false; | |
| 544 } | |
| 545 } | |
| 546 | |
| 547 void HttpAuthController::OnIOComplete(int result) { | |
| 548 DCHECK(CalledOnValidThread()); | |
| 549 if (DisableOnAuthHandlerResult(result)) | |
| 550 result = OK; | |
| 551 if (!callback_.is_null()) { | |
| 552 CompletionCallback c = callback_; | |
| 553 callback_.Reset(); | |
| 554 c.Run(result); | |
| 555 } | |
| 556 } | |
| 557 | |
| 558 scoped_refptr<AuthChallengeInfo> HttpAuthController::auth_info() { | |
| 559 DCHECK(CalledOnValidThread()); | |
| 560 return auth_info_; | |
| 561 } | |
| 562 | |
| 563 bool HttpAuthController::IsAuthSchemeDisabled(HttpAuth::Scheme scheme) const { | |
| 564 DCHECK(CalledOnValidThread()); | |
| 565 return disabled_schemes_.find(scheme) != disabled_schemes_.end(); | |
| 566 } | |
| 567 | |
| 568 void HttpAuthController::DisableAuthScheme(HttpAuth::Scheme scheme) { | |
| 569 DCHECK(CalledOnValidThread()); | |
| 570 disabled_schemes_.insert(scheme); | |
| 571 } | |
| 572 | |
| 573 void HttpAuthController::DisableEmbeddedIdentity() { | |
| 574 DCHECK(CalledOnValidThread()); | |
| 575 embedded_identity_used_ = true; | |
| 576 } | |
| 577 | |
| 578 } // namespace net | |
| OLD | NEW |