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/metrics/histogram.h" | 9 #include "base/metrics/histogram.h" |
10 #include "base/strings/string_util.h" | 10 #include "base/strings/string_util.h" |
(...skipping 98 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
109 // Digest Start: 2 | 109 // Digest Start: 2 |
110 // Digest Reject: 3 | 110 // Digest Reject: 3 |
111 // NTLM Start: 4 | 111 // NTLM Start: 4 |
112 // NTLM Reject: 5 | 112 // NTLM Reject: 5 |
113 // Negotiate Start: 6 | 113 // Negotiate Start: 6 |
114 // Negotiate Reject: 7 | 114 // Negotiate Reject: 7 |
115 static const int kEventBucketsEnd = | 115 static const int kEventBucketsEnd = |
116 HttpAuth::AUTH_SCHEME_MAX * AUTH_EVENT_MAX; | 116 HttpAuth::AUTH_SCHEME_MAX * AUTH_EVENT_MAX; |
117 int event_bucket = auth_scheme * AUTH_EVENT_MAX + auth_event; | 117 int event_bucket = auth_scheme * AUTH_EVENT_MAX + auth_event; |
118 DCHECK(event_bucket >= 0 && event_bucket < kEventBucketsEnd); | 118 DCHECK(event_bucket >= 0 && event_bucket < kEventBucketsEnd); |
119 UMA_HISTOGRAM_ENUMERATION("Net.HttpAuthCount", event_bucket, | 119 UMA_HISTOGRAM_ENUMERATION( |
120 kEventBucketsEnd); | 120 "Net.HttpAuthCount", event_bucket, kEventBucketsEnd); |
121 | 121 |
122 // Record the target of the authentication. | 122 // Record the target of the authentication. |
123 // | 123 // |
124 // The results map to: | 124 // The results map to: |
125 // Basic Proxy: 0 | 125 // Basic Proxy: 0 |
126 // Basic Secure Proxy: 1 | 126 // Basic Secure Proxy: 1 |
127 // Basic Server: 2 | 127 // Basic Server: 2 |
128 // Basic Secure Server: 3 | 128 // Basic Secure Server: 3 |
129 // Digest Proxy: 4 | 129 // Digest Proxy: 4 |
130 // Digest Secure Proxy: 5 | 130 // Digest Secure Proxy: 5 |
131 // Digest Server: 6 | 131 // Digest Server: 6 |
132 // Digest Secure Server: 7 | 132 // Digest Secure Server: 7 |
133 // NTLM Proxy: 8 | 133 // NTLM Proxy: 8 |
134 // NTLM Secure Proxy: 9 | 134 // NTLM Secure Proxy: 9 |
135 // NTLM Server: 10 | 135 // NTLM Server: 10 |
136 // NTLM Secure Server: 11 | 136 // NTLM Secure Server: 11 |
137 // Negotiate Proxy: 12 | 137 // Negotiate Proxy: 12 |
138 // Negotiate Secure Proxy: 13 | 138 // Negotiate Secure Proxy: 13 |
139 // Negotiate Server: 14 | 139 // Negotiate Server: 14 |
140 // Negotiate Secure Server: 15 | 140 // Negotiate Secure Server: 15 |
141 if (auth_event != AUTH_EVENT_START) | 141 if (auth_event != AUTH_EVENT_START) |
142 return; | 142 return; |
143 static const int kTargetBucketsEnd = | 143 static const int kTargetBucketsEnd = |
144 HttpAuth::AUTH_SCHEME_MAX * AUTH_TARGET_MAX; | 144 HttpAuth::AUTH_SCHEME_MAX * AUTH_TARGET_MAX; |
145 AuthTarget auth_target = DetermineAuthTarget(handler); | 145 AuthTarget auth_target = DetermineAuthTarget(handler); |
146 int target_bucket = auth_scheme * AUTH_TARGET_MAX + auth_target; | 146 int target_bucket = auth_scheme * AUTH_TARGET_MAX + auth_target; |
147 DCHECK(target_bucket >= 0 && target_bucket < kTargetBucketsEnd); | 147 DCHECK(target_bucket >= 0 && target_bucket < kTargetBucketsEnd); |
148 UMA_HISTOGRAM_ENUMERATION("Net.HttpAuthTarget", target_bucket, | 148 UMA_HISTOGRAM_ENUMERATION( |
149 kTargetBucketsEnd); | 149 "Net.HttpAuthTarget", target_bucket, kTargetBucketsEnd); |
150 } | 150 } |
151 | 151 |
152 } // namespace | 152 } // namespace |
153 | 153 |
154 HttpAuthController::HttpAuthController( | 154 HttpAuthController::HttpAuthController( |
155 HttpAuth::Target target, | 155 HttpAuth::Target target, |
156 const GURL& auth_url, | 156 const GURL& auth_url, |
157 HttpAuthCache* http_auth_cache, | 157 HttpAuthCache* http_auth_cache, |
158 HttpAuthHandlerFactory* http_auth_handler_factory) | 158 HttpAuthHandlerFactory* http_auth_handler_factory) |
159 : target_(target), | 159 : target_(target), |
160 auth_url_(auth_url), | 160 auth_url_(auth_url), |
161 auth_origin_(auth_url.GetOrigin()), | 161 auth_origin_(auth_url.GetOrigin()), |
162 auth_path_(HttpAuth::AUTH_PROXY ? std::string() : auth_url.path()), | 162 auth_path_(HttpAuth::AUTH_PROXY ? std::string() : auth_url.path()), |
163 embedded_identity_used_(false), | 163 embedded_identity_used_(false), |
164 default_credentials_used_(false), | 164 default_credentials_used_(false), |
165 http_auth_cache_(http_auth_cache), | 165 http_auth_cache_(http_auth_cache), |
166 http_auth_handler_factory_(http_auth_handler_factory) { | 166 http_auth_handler_factory_(http_auth_handler_factory) { |
167 } | 167 } |
168 | 168 |
169 HttpAuthController::~HttpAuthController() { | 169 HttpAuthController::~HttpAuthController() { |
170 DCHECK(CalledOnValidThread()); | 170 DCHECK(CalledOnValidThread()); |
171 } | 171 } |
172 | 172 |
173 int HttpAuthController::MaybeGenerateAuthToken( | 173 int HttpAuthController::MaybeGenerateAuthToken( |
174 const HttpRequestInfo* request, const CompletionCallback& callback, | 174 const HttpRequestInfo* request, |
| 175 const CompletionCallback& callback, |
175 const BoundNetLog& net_log) { | 176 const BoundNetLog& net_log) { |
176 DCHECK(CalledOnValidThread()); | 177 DCHECK(CalledOnValidThread()); |
177 bool needs_auth = HaveAuth() || SelectPreemptiveAuth(net_log); | 178 bool needs_auth = HaveAuth() || SelectPreemptiveAuth(net_log); |
178 if (!needs_auth) | 179 if (!needs_auth) |
179 return OK; | 180 return OK; |
180 const AuthCredentials* credentials = NULL; | 181 const AuthCredentials* credentials = NULL; |
181 if (identity_.source != HttpAuth::IDENT_SRC_DEFAULT_CREDENTIALS) | 182 if (identity_.source != HttpAuth::IDENT_SRC_DEFAULT_CREDENTIALS) |
182 credentials = &identity_.credentials; | 183 credentials = &identity_.credentials; |
183 DCHECK(auth_token_.empty()); | 184 DCHECK(auth_token_.empty()); |
184 DCHECK(callback_.is_null()); | 185 DCHECK(callback_.is_null()); |
185 int rv = handler_->GenerateAuthToken( | 186 int rv = handler_->GenerateAuthToken( |
186 credentials, request, | 187 credentials, |
| 188 request, |
187 base::Bind(&HttpAuthController::OnIOComplete, base::Unretained(this)), | 189 base::Bind(&HttpAuthController::OnIOComplete, base::Unretained(this)), |
188 &auth_token_); | 190 &auth_token_); |
189 if (DisableOnAuthHandlerResult(rv)) | 191 if (DisableOnAuthHandlerResult(rv)) |
190 rv = OK; | 192 rv = OK; |
191 if (rv == ERR_IO_PENDING) | 193 if (rv == ERR_IO_PENDING) |
192 callback_ = callback; | 194 callback_ = callback; |
193 else | 195 else |
194 OnIOComplete(rv); | 196 OnIOComplete(rv); |
195 return rv; | 197 return rv; |
196 } | 198 } |
197 | 199 |
198 bool HttpAuthController::SelectPreemptiveAuth(const BoundNetLog& net_log) { | 200 bool HttpAuthController::SelectPreemptiveAuth(const BoundNetLog& net_log) { |
199 DCHECK(CalledOnValidThread()); | 201 DCHECK(CalledOnValidThread()); |
200 DCHECK(!HaveAuth()); | 202 DCHECK(!HaveAuth()); |
201 DCHECK(identity_.invalid); | 203 DCHECK(identity_.invalid); |
202 | 204 |
203 // Don't do preemptive authorization if the URL contains a username:password, | 205 // 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. | 206 // since we must first be challenged in order to use the URL's identity. |
205 if (auth_url_.has_username()) | 207 if (auth_url_.has_username()) |
206 return false; | 208 return false; |
207 | 209 |
208 // SelectPreemptiveAuth() is on the critical path for each request, so it | 210 // 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 | 211 // 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. | 212 // the number of http auth cache entries is expected to be very small. |
211 // (For most users in fact, it will be 0.) | 213 // (For most users in fact, it will be 0.) |
212 HttpAuthCache::Entry* entry = http_auth_cache_->LookupByPath( | 214 HttpAuthCache::Entry* entry = |
213 auth_origin_, auth_path_); | 215 http_auth_cache_->LookupByPath(auth_origin_, auth_path_); |
214 if (!entry) | 216 if (!entry) |
215 return false; | 217 return false; |
216 | 218 |
217 // Try to create a handler using the previous auth challenge. | 219 // Try to create a handler using the previous auth challenge. |
218 scoped_ptr<HttpAuthHandler> handler_preemptive; | 220 scoped_ptr<HttpAuthHandler> handler_preemptive; |
219 int rv_create = http_auth_handler_factory_-> | 221 int rv_create = |
220 CreatePreemptiveAuthHandlerFromString(entry->auth_challenge(), target_, | 222 http_auth_handler_factory_->CreatePreemptiveAuthHandlerFromString( |
221 auth_origin_, | 223 entry->auth_challenge(), |
222 entry->IncrementNonceCount(), | 224 target_, |
223 net_log, &handler_preemptive); | 225 auth_origin_, |
| 226 entry->IncrementNonceCount(), |
| 227 net_log, |
| 228 &handler_preemptive); |
224 if (rv_create != OK) | 229 if (rv_create != OK) |
225 return false; | 230 return false; |
226 | 231 |
227 // Set the state | 232 // Set the state |
228 identity_.source = HttpAuth::IDENT_SRC_PATH_LOOKUP; | 233 identity_.source = HttpAuth::IDENT_SRC_PATH_LOOKUP; |
229 identity_.invalid = false; | 234 identity_.invalid = false; |
230 identity_.credentials = entry->credentials(); | 235 identity_.credentials = entry->credentials(); |
231 handler_.swap(handler_preemptive); | 236 handler_.swap(handler_preemptive); |
232 return true; | 237 return true; |
233 } | 238 } |
(...skipping 58 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
292 InvalidateCurrentHandler(INVALIDATE_HANDLER_AND_CACHED_CREDENTIALS); | 297 InvalidateCurrentHandler(INVALIDATE_HANDLER_AND_CACHED_CREDENTIALS); |
293 } | 298 } |
294 break; | 299 break; |
295 case HttpAuth::AUTHORIZATION_RESULT_DIFFERENT_REALM: | 300 case HttpAuth::AUTHORIZATION_RESULT_DIFFERENT_REALM: |
296 // If the server changes the authentication realm in a | 301 // If the server changes the authentication realm in a |
297 // subsequent challenge, invalidate cached credentials for the | 302 // subsequent challenge, invalidate cached credentials for the |
298 // previous realm. If the server rejects a preemptive | 303 // previous realm. If the server rejects a preemptive |
299 // authorization and requests credentials for a different | 304 // authorization and requests credentials for a different |
300 // realm, we keep the cached credentials. | 305 // realm, we keep the cached credentials. |
301 InvalidateCurrentHandler( | 306 InvalidateCurrentHandler( |
302 (identity_.source == HttpAuth::IDENT_SRC_PATH_LOOKUP) ? | 307 (identity_.source == HttpAuth::IDENT_SRC_PATH_LOOKUP) |
303 INVALIDATE_HANDLER : | 308 ? INVALIDATE_HANDLER |
304 INVALIDATE_HANDLER_AND_CACHED_CREDENTIALS); | 309 : INVALIDATE_HANDLER_AND_CACHED_CREDENTIALS); |
305 break; | 310 break; |
306 default: | 311 default: |
307 NOTREACHED(); | 312 NOTREACHED(); |
308 break; | 313 break; |
309 } | 314 } |
310 } | 315 } |
311 | 316 |
312 identity_.invalid = true; | 317 identity_.invalid = true; |
313 | 318 |
314 bool can_send_auth = (target_ != HttpAuth::AUTH_SERVER || | 319 bool can_send_auth = |
315 !do_not_send_server_auth); | 320 (target_ != HttpAuth::AUTH_SERVER || !do_not_send_server_auth); |
316 | 321 |
317 do { | 322 do { |
318 if (!handler_.get() && can_send_auth) { | 323 if (!handler_.get() && can_send_auth) { |
319 // Find the best authentication challenge that we support. | 324 // Find the best authentication challenge that we support. |
320 HttpAuth::ChooseBestChallenge(http_auth_handler_factory_, | 325 HttpAuth::ChooseBestChallenge(http_auth_handler_factory_, |
321 headers.get(), | 326 headers.get(), |
322 target_, | 327 target_, |
323 auth_origin_, | 328 auth_origin_, |
324 disabled_schemes_, | 329 disabled_schemes_, |
325 net_log, | 330 net_log, |
(...skipping 44 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
370 } | 375 } |
371 } else { | 376 } else { |
372 auth_info_ = NULL; | 377 auth_info_ = NULL; |
373 } | 378 } |
374 | 379 |
375 // If we get here and we don't have a handler_, that's because we | 380 // 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 | 381 // invalidated it due to not having any viable identities to use with it. Go |
377 // back and try again. | 382 // back and try again. |
378 // TODO(asanka): Instead we should create a priority list of | 383 // TODO(asanka): Instead we should create a priority list of |
379 // <handler,identity> and iterate through that. | 384 // <handler,identity> and iterate through that. |
380 } while(!handler_.get()); | 385 } while (!handler_.get()); |
381 return OK; | 386 return OK; |
382 } | 387 } |
383 | 388 |
384 void HttpAuthController::ResetAuth(const AuthCredentials& credentials) { | 389 void HttpAuthController::ResetAuth(const AuthCredentials& credentials) { |
385 DCHECK(CalledOnValidThread()); | 390 DCHECK(CalledOnValidThread()); |
386 DCHECK(identity_.invalid || credentials.Empty()); | 391 DCHECK(identity_.invalid || credentials.Empty()); |
387 | 392 |
388 if (identity_.invalid) { | 393 if (identity_.invalid) { |
389 // Update the credentials. | 394 // Update the credentials. |
390 identity_.source = HttpAuth::IDENT_SRC_EXTERNAL; | 395 identity_.source = HttpAuth::IDENT_SRC_EXTERNAL; |
(...skipping 15 matching lines...) Expand all Loading... |
406 // | 411 // |
407 // TODO(wtc): For NTLM_SSPI, we add the same auth entry to the cache in | 412 // 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 | 413 // 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. | 414 // to add an auth entry to the cache only once, preferrably in round 1. |
410 // See http://crbug.com/21015. | 415 // See http://crbug.com/21015. |
411 switch (identity_.source) { | 416 switch (identity_.source) { |
412 case HttpAuth::IDENT_SRC_NONE: | 417 case HttpAuth::IDENT_SRC_NONE: |
413 case HttpAuth::IDENT_SRC_DEFAULT_CREDENTIALS: | 418 case HttpAuth::IDENT_SRC_DEFAULT_CREDENTIALS: |
414 break; | 419 break; |
415 default: | 420 default: |
416 http_auth_cache_->Add(auth_origin_, handler_->realm(), | 421 http_auth_cache_->Add(auth_origin_, |
417 handler_->auth_scheme(), handler_->challenge(), | 422 handler_->realm(), |
418 identity_.credentials, auth_path_); | 423 handler_->auth_scheme(), |
| 424 handler_->challenge(), |
| 425 identity_.credentials, |
| 426 auth_path_); |
419 break; | 427 break; |
420 } | 428 } |
421 } | 429 } |
422 | 430 |
423 bool HttpAuthController::HaveAuthHandler() const { | 431 bool HttpAuthController::HaveAuthHandler() const { |
424 return handler_.get() != NULL; | 432 return handler_.get() != NULL; |
425 } | 433 } |
426 | 434 |
427 bool HttpAuthController::HaveAuth() const { | 435 bool HttpAuthController::HaveAuth() const { |
428 return handler_.get() && !identity_.invalid; | 436 return handler_.get() && !identity_.invalid; |
(...skipping 12 matching lines...) Expand all Loading... |
441 identity_ = HttpAuth::Identity(); | 449 identity_ = HttpAuth::Identity(); |
442 } | 450 } |
443 | 451 |
444 void HttpAuthController::InvalidateRejectedAuthFromCache() { | 452 void HttpAuthController::InvalidateRejectedAuthFromCache() { |
445 DCHECK(CalledOnValidThread()); | 453 DCHECK(CalledOnValidThread()); |
446 DCHECK(HaveAuth()); | 454 DCHECK(HaveAuth()); |
447 | 455 |
448 // Clear the cache entry for the identity we just failed on. | 456 // Clear the cache entry for the identity we just failed on. |
449 // Note: we require the credentials to match before invalidating | 457 // 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. | 458 // since the entry in the cache may be newer than what we used last time. |
451 http_auth_cache_->Remove(auth_origin_, handler_->realm(), | 459 http_auth_cache_->Remove(auth_origin_, |
452 handler_->auth_scheme(), identity_.credentials); | 460 handler_->realm(), |
| 461 handler_->auth_scheme(), |
| 462 identity_.credentials); |
453 } | 463 } |
454 | 464 |
455 bool HttpAuthController::SelectNextAuthIdentityToTry() { | 465 bool HttpAuthController::SelectNextAuthIdentityToTry() { |
456 DCHECK(CalledOnValidThread()); | 466 DCHECK(CalledOnValidThread()); |
457 DCHECK(handler_.get()); | 467 DCHECK(handler_.get()); |
458 DCHECK(identity_.invalid); | 468 DCHECK(identity_.invalid); |
459 | 469 |
460 // Try to use the username:password encoded into the URL first. | 470 // Try to use the username:password encoded into the URL first. |
461 if (target_ == HttpAuth::AUTH_SERVER && auth_url_.has_username() && | 471 if (target_ == HttpAuth::AUTH_SERVER && auth_url_.has_username() && |
462 !embedded_identity_used_) { | 472 !embedded_identity_used_) { |
463 identity_.source = HttpAuth::IDENT_SRC_URL; | 473 identity_.source = HttpAuth::IDENT_SRC_URL; |
464 identity_.invalid = false; | 474 identity_.invalid = false; |
465 // Extract the username:password from the URL. | 475 // Extract the username:password from the URL. |
466 base::string16 username; | 476 base::string16 username; |
467 base::string16 password; | 477 base::string16 password; |
468 GetIdentityFromURL(auth_url_, &username, &password); | 478 GetIdentityFromURL(auth_url_, &username, &password); |
469 identity_.credentials.Set(username, password); | 479 identity_.credentials.Set(username, password); |
470 embedded_identity_used_ = true; | 480 embedded_identity_used_ = true; |
471 // TODO(eroman): If the password is blank, should we also try combining | 481 // TODO(eroman): If the password is blank, should we also try combining |
472 // with a password from the cache? | 482 // with a password from the cache? |
473 UMA_HISTOGRAM_BOOLEAN("net.HttpIdentSrcURL", true); | 483 UMA_HISTOGRAM_BOOLEAN("net.HttpIdentSrcURL", true); |
474 return true; | 484 return true; |
475 } | 485 } |
476 | 486 |
477 // Check the auth cache for a realm entry. | 487 // Check the auth cache for a realm entry. |
478 HttpAuthCache::Entry* entry = | 488 HttpAuthCache::Entry* entry = http_auth_cache_->Lookup( |
479 http_auth_cache_->Lookup(auth_origin_, handler_->realm(), | 489 auth_origin_, handler_->realm(), handler_->auth_scheme()); |
480 handler_->auth_scheme()); | |
481 | 490 |
482 if (entry) { | 491 if (entry) { |
483 identity_.source = HttpAuth::IDENT_SRC_REALM_LOOKUP; | 492 identity_.source = HttpAuth::IDENT_SRC_REALM_LOOKUP; |
484 identity_.invalid = false; | 493 identity_.invalid = false; |
485 identity_.credentials = entry->credentials(); | 494 identity_.credentials = entry->credentials(); |
486 return true; | 495 return true; |
487 } | 496 } |
488 | 497 |
489 // Use default credentials (single sign on) if this is the first attempt | 498 // 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. | 499 // at identity. Do not allow multiple times as it will infinite loop. |
(...skipping 78 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
569 DCHECK(CalledOnValidThread()); | 578 DCHECK(CalledOnValidThread()); |
570 disabled_schemes_.insert(scheme); | 579 disabled_schemes_.insert(scheme); |
571 } | 580 } |
572 | 581 |
573 void HttpAuthController::DisableEmbeddedIdentity() { | 582 void HttpAuthController::DisableEmbeddedIdentity() { |
574 DCHECK(CalledOnValidThread()); | 583 DCHECK(CalledOnValidThread()); |
575 embedded_identity_used_ = true; | 584 embedded_identity_used_ = true; |
576 } | 585 } |
577 | 586 |
578 } // namespace net | 587 } // namespace net |
OLD | NEW |