• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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();
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     }
324   }
325 
326   identity_.invalid = true;
327   bool can_send_auth = (target_ != HttpAuth::AUTH_SERVER ||
328                         !do_not_send_server_auth);
329 
330   do {
331     if (!handler_.get() && can_send_auth) {
332       // Find the best authentication challenge that we support.
333       HttpAuth::ChooseBestChallenge(
334           http_auth_handler_factory_, *headers, ssl_info,
335           network_anonymization_key_, target_, auth_scheme_host_port_,
336           disabled_schemes_, net_log_, host_resolver_, &handler_);
337       if (handler_.get()) {
338         HistogramAuthEvent(handler_.get(), AUTH_EVENT_START);
339       }
340     }
341 
342     if (!handler_.get()) {
343       if (establishing_tunnel) {
344         // We are establishing a tunnel, we can't show the error page because an
345         // active network attacker could control its contents.  Instead, we just
346         // fail to establish the tunnel.
347         DCHECK_EQ(target_, HttpAuth::AUTH_PROXY);
348         net_log_.EndEventWithNetErrorCode(
349             NetLogEventType::AUTH_HANDLE_CHALLENGE, ERR_PROXY_AUTH_UNSUPPORTED);
350         return ERR_PROXY_AUTH_UNSUPPORTED;
351       }
352       // We found no supported challenge -- let the transaction continue so we
353       // end up displaying the error page.
354       net_log_.EndEvent(NetLogEventType::AUTH_HANDLE_CHALLENGE);
355       return OK;
356     }
357 
358     if (handler_->NeedsIdentity()) {
359       // Pick a new auth identity to try, by looking to the URL and auth cache.
360       // If an identity to try is found, it is saved to identity_.
361       SelectNextAuthIdentityToTry();
362     } else {
363       // Proceed with the existing identity or a null identity.
364       identity_.invalid = false;
365     }
366 
367     // From this point on, we are restartable.
368 
369     if (identity_.invalid) {
370       // We have exhausted all identity possibilities.
371       if (!handler_->AllowsExplicitCredentials()) {
372         // If the handler doesn't accept explicit credentials, then we need to
373         // choose a different auth scheme.
374         HistogramAuthEvent(handler_.get(), AUTH_EVENT_REJECT);
375         InvalidateCurrentHandler(INVALIDATE_HANDLER_AND_DISABLE_SCHEME);
376       } else {
377         // Pass the challenge information back to the client.
378         PopulateAuthChallenge();
379       }
380     }
381 
382     // If we get here and we don't have a handler_, that's because we
383     // invalidated it due to not having any viable identities to use with it. Go
384     // back and try again.
385     // TODO(asanka): Instead we should create a priority list of
386     //     <handler,identity> and iterate through that.
387   } while(!handler_.get());
388   net_log_.EndEvent(NetLogEventType::AUTH_HANDLE_CHALLENGE);
389   return OK;
390 }
391 
ResetAuth(const AuthCredentials & credentials)392 void HttpAuthController::ResetAuth(const AuthCredentials& credentials) {
393   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
394   DCHECK(identity_.invalid || credentials.Empty());
395 
396   if (identity_.invalid) {
397     // Update the credentials.
398     identity_.source = HttpAuth::IDENT_SRC_EXTERNAL;
399     identity_.invalid = false;
400     identity_.credentials = credentials;
401 
402     // auth_info_ is no longer necessary.
403     auth_info_ = std::nullopt;
404   }
405 
406   DCHECK(identity_.source != HttpAuth::IDENT_SRC_PATH_LOOKUP);
407 
408   // Add the auth entry to the cache before restarting. We don't know whether
409   // the identity is valid yet, but if it is valid we want other transactions
410   // to know about it. If an entry for (origin, handler->realm()) already
411   // exists, we update it.
412   //
413   // If identity_.source is HttpAuth::IDENT_SRC_NONE or
414   // HttpAuth::IDENT_SRC_DEFAULT_CREDENTIALS, identity_ contains no
415   // identity because identity is not required yet or we're using default
416   // credentials.
417   //
418   // TODO(wtc): For NTLM_SSPI, we add the same auth entry to the cache in
419   // round 1 and round 2, which is redundant but correct.  It would be nice
420   // to add an auth entry to the cache only once, preferrably in round 1.
421   // See http://crbug.com/21015.
422   switch (identity_.source) {
423     case HttpAuth::IDENT_SRC_NONE:
424     case HttpAuth::IDENT_SRC_DEFAULT_CREDENTIALS:
425       break;
426     default:
427       http_auth_cache_->Add(auth_scheme_host_port_, target_, handler_->realm(),
428                             handler_->auth_scheme(), network_anonymization_key_,
429                             handler_->challenge(), identity_.credentials,
430                             auth_path_);
431       break;
432   }
433 }
434 
HaveAuthHandler() const435 bool HttpAuthController::HaveAuthHandler() const {
436   return handler_.get() != nullptr;
437 }
438 
HaveAuth() const439 bool HttpAuthController::HaveAuth() const {
440   return handler_.get() && !identity_.invalid;
441 }
442 
NeedsHTTP11() const443 bool HttpAuthController::NeedsHTTP11() const {
444   return handler_ && handler_->is_connection_based();
445 }
446 
InvalidateCurrentHandler(InvalidateHandlerAction action)447 void HttpAuthController::InvalidateCurrentHandler(
448     InvalidateHandlerAction action) {
449   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
450   DCHECK(handler_.get());
451 
452   switch (action) {
453     case INVALIDATE_HANDLER_AND_CACHED_CREDENTIALS:
454       InvalidateRejectedAuthFromCache();
455       break;
456 
457     case INVALIDATE_HANDLER_AND_DISABLE_SCHEME:
458       DisableAuthScheme(handler_->auth_scheme());
459       break;
460 
461     case INVALIDATE_HANDLER:
462       PrepareIdentityForReuse();
463       break;
464   }
465 
466   handler_.reset();
467   identity_ = HttpAuth::Identity();
468 }
469 
InvalidateRejectedAuthFromCache()470 void HttpAuthController::InvalidateRejectedAuthFromCache() {
471   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
472   DCHECK(HaveAuth());
473 
474   // Clear the cache entry for the identity we just failed on.
475   // Note: we require the credentials to match before invalidating
476   // since the entry in the cache may be newer than what we used last time.
477   http_auth_cache_->Remove(auth_scheme_host_port_, target_, handler_->realm(),
478                            handler_->auth_scheme(), network_anonymization_key_,
479                            identity_.credentials);
480 }
481 
PrepareIdentityForReuse()482 void HttpAuthController::PrepareIdentityForReuse() {
483   if (identity_.invalid)
484     return;
485 
486   switch (identity_.source) {
487     case HttpAuth::IDENT_SRC_DEFAULT_CREDENTIALS:
488       DCHECK(default_credentials_used_);
489       default_credentials_used_ = false;
490       break;
491 
492     case HttpAuth::IDENT_SRC_URL:
493       DCHECK(embedded_identity_used_);
494       embedded_identity_used_ = false;
495       break;
496 
497     case HttpAuth::IDENT_SRC_NONE:
498     case HttpAuth::IDENT_SRC_PATH_LOOKUP:
499     case HttpAuth::IDENT_SRC_REALM_LOOKUP:
500     case HttpAuth::IDENT_SRC_EXTERNAL:
501       break;
502   }
503 }
504 
SelectNextAuthIdentityToTry()505 bool HttpAuthController::SelectNextAuthIdentityToTry() {
506   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
507   DCHECK(handler_.get());
508   DCHECK(identity_.invalid);
509 
510   // Try to use the username:password encoded into the URL first.
511   if (target_ == HttpAuth::AUTH_SERVER && auth_url_.has_username() &&
512       !embedded_identity_used_) {
513     identity_.source = HttpAuth::IDENT_SRC_URL;
514     identity_.invalid = false;
515     // Extract the username:password from the URL.
516     std::u16string username;
517     std::u16string password;
518     GetIdentityFromURL(auth_url_, &username, &password);
519     identity_.credentials.Set(username, password);
520     embedded_identity_used_ = true;
521     // TODO(eroman): If the password is blank, should we also try combining
522     // with a password from the cache?
523     return true;
524   }
525 
526   // Check the auth cache for a realm entry.
527   HttpAuthCache::Entry* entry = http_auth_cache_->Lookup(
528       auth_scheme_host_port_, target_, handler_->realm(),
529       handler_->auth_scheme(), network_anonymization_key_);
530 
531   if (entry) {
532     identity_.source = HttpAuth::IDENT_SRC_REALM_LOOKUP;
533     identity_.invalid = false;
534     identity_.credentials = entry->credentials();
535     return true;
536   }
537 
538   // Use default credentials (single sign-on) if they're allowed and this is the
539   // first attempt at using an identity. Do not allow multiple times as it will
540   // infinite loop. We use default credentials after checking the auth cache so
541   // that if single sign-on doesn't work, we won't try default credentials for
542   // future transactions.
543   if (!default_credentials_used_ && handler_->AllowsDefaultCredentials()) {
544     identity_.source = HttpAuth::IDENT_SRC_DEFAULT_CREDENTIALS;
545     identity_.invalid = false;
546     default_credentials_used_ = true;
547     return true;
548   }
549 
550   return false;
551 }
552 
PopulateAuthChallenge()553 void HttpAuthController::PopulateAuthChallenge() {
554   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
555 
556   // Populates response_.auth_challenge with the authentication challenge info.
557   // This info is consumed by URLRequestHttpJob::GetAuthChallengeInfo().
558 
559   auth_info_ = AuthChallengeInfo();
560   auth_info_->is_proxy = (target_ == HttpAuth::AUTH_PROXY);
561   auth_info_->challenger = auth_scheme_host_port_;
562   auth_info_->scheme = HttpAuth::SchemeToString(handler_->auth_scheme());
563   auth_info_->realm = handler_->realm();
564   auth_info_->path = auth_path_;
565   auth_info_->challenge = handler_->challenge();
566 }
567 
HandleGenerateTokenResult(int result)568 int HttpAuthController::HandleGenerateTokenResult(int result) {
569   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
570   net_log_.EndEventWithNetErrorCode(NetLogEventType::AUTH_GENERATE_TOKEN,
571                                     result);
572   switch (result) {
573     // Occurs if the credential handle is found to be invalid at the point it is
574     // exercised (i.e. GenerateAuthToken stage). We are going to consider this
575     // to be an error that invalidates the identity but not necessarily the
576     // scheme. Doing so allows a different identity to be used with the same
577     // scheme. See https://crbug.com/648366.
578     case ERR_INVALID_HANDLE:
579 
580     // If the GenerateAuthToken call fails with this error, this means that the
581     // handler can no longer be used. However, the authentication scheme is
582     // considered still usable. This allows a scheme that attempted and failed
583     // to use default credentials to recover and use explicit credentials.
584     //
585     // The current handler may be tied to external state that is no longer
586     // valid, hence should be discarded. Since the scheme is still valid, a new
587     // handler can be created for the current scheme.
588     case ERR_INVALID_AUTH_CREDENTIALS:
589       InvalidateCurrentHandler(INVALIDATE_HANDLER_AND_CACHED_CREDENTIALS);
590       auth_token_.clear();
591       return OK;
592 
593     // Occurs with GSSAPI, if the user has not already logged in.
594     case ERR_MISSING_AUTH_CREDENTIALS:
595       // Usually, GSSAPI doesn't allow explicit credentials and the scheme
596       // cannot succeed anymore hence it gets disabled. However, on ChromeOS
597       // it's not the case so we invalidate the current handler and can ask for
598       // explicit credentials later. (See b/260522530).
599       if (!handler_->AllowsExplicitCredentials()) {
600         InvalidateCurrentHandler(INVALIDATE_HANDLER_AND_DISABLE_SCHEME);
601       } else {
602         InvalidateCurrentHandler(INVALIDATE_HANDLER_AND_CACHED_CREDENTIALS);
603       }
604       auth_token_.clear();
605       return OK;
606 
607     // Can occur with GSSAPI or SSPI if the underlying library reports
608     // a permanent error.
609     case ERR_UNSUPPORTED_AUTH_SCHEME:
610 
611     // These two error codes represent failures we aren't handling.
612     case ERR_UNEXPECTED_SECURITY_LIBRARY_STATUS:
613     case ERR_UNDOCUMENTED_SECURITY_LIBRARY_STATUS:
614 
615     // Can be returned by SSPI if the authenticating authority or
616     // target is not known.
617     case ERR_MISCONFIGURED_AUTH_ENVIRONMENT:
618 
619       // In these cases, disable the current scheme as it cannot
620       // succeed.
621       InvalidateCurrentHandler(INVALIDATE_HANDLER_AND_DISABLE_SCHEME);
622       auth_token_.clear();
623       return OK;
624 
625     default:
626       return result;
627   }
628 }
629 
OnGenerateAuthTokenDone(int result)630 void HttpAuthController::OnGenerateAuthTokenDone(int result) {
631   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
632   result = HandleGenerateTokenResult(result);
633   if (!callback_.is_null()) {
634     std::move(callback_).Run(result);
635   }
636 }
637 
TakeAuthInfo(std::optional<AuthChallengeInfo> * other)638 void HttpAuthController::TakeAuthInfo(std::optional<AuthChallengeInfo>* other) {
639   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
640   auth_info_.swap(*other);
641 }
642 
IsAuthSchemeDisabled(HttpAuth::Scheme scheme) const643 bool HttpAuthController::IsAuthSchemeDisabled(HttpAuth::Scheme scheme) const {
644   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
645   return disabled_schemes_.find(scheme) != disabled_schemes_.end();
646 }
647 
DisableAuthScheme(HttpAuth::Scheme scheme)648 void HttpAuthController::DisableAuthScheme(HttpAuth::Scheme scheme) {
649   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
650   disabled_schemes_.insert(scheme);
651 }
652 
DisableEmbeddedIdentity()653 void HttpAuthController::DisableEmbeddedIdentity() {
654   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
655   embedded_identity_used_ = true;
656 }
657 
OnConnectionClosed()658 void HttpAuthController::OnConnectionClosed() {
659   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
660   InvalidateCurrentHandler(INVALIDATE_HANDLER);
661 }
662 
663 }  // namespace net
664