| 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 |