xref: /aosp_15_r20/external/cronet/net/http/http_auth_controller.cc (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
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