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