Bug 1814798 - pt 2. Add a PHCManager component to control PHC r=glandium,emilio
[gecko.git] / netwerk / protocol / http / nsHttpChannelAuthProvider.cpp
blobb9bc452cba5e1eb28ae281c8005d7ff85c82e633
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim:set expandtab ts=4 sw=2 sts=2 cin: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 // HttpLog.h should generally be included first
8 #include "HttpLog.h"
10 #include "mozilla/BasePrincipal.h"
11 #include "mozilla/StoragePrincipalHelper.h"
12 #include "mozilla/Tokenizer.h"
13 #include "MockHttpAuth.h"
14 #include "nsHttpChannelAuthProvider.h"
15 #include "nsCRT.h"
16 #include "nsNetUtil.h"
17 #include "nsHttpHandler.h"
18 #include "nsIHttpAuthenticator.h"
19 #include "nsIHttpChannelInternal.h"
20 #include "nsIAuthPrompt2.h"
21 #include "nsIAuthPromptProvider.h"
22 #include "nsIInterfaceRequestor.h"
23 #include "nsIInterfaceRequestorUtils.h"
24 #include "nsEscape.h"
25 #include "nsAuthInformationHolder.h"
26 #include "nsIStringBundle.h"
27 #include "nsIPromptService.h"
28 #include "netCore.h"
29 #include "nsIHttpAuthenticableChannel.h"
30 #include "nsIURI.h"
31 #include "nsContentUtils.h"
32 #include "nsHttp.h"
33 #include "nsHttpBasicAuth.h"
34 #include "nsHttpDigestAuth.h"
35 #ifdef MOZ_AUTH_EXTENSION
36 # include "nsHttpNegotiateAuth.h"
37 #endif
38 #include "nsHttpNTLMAuth.h"
39 #include "nsServiceManagerUtils.h"
40 #include "nsIURL.h"
41 #include "mozilla/StaticPrefs_network.h"
42 #include "mozilla/StaticPrefs_prompts.h"
43 #include "mozilla/Telemetry.h"
44 #include "nsIProxiedChannel.h"
45 #include "nsIProxyInfo.h"
47 namespace mozilla::net {
49 #define SUBRESOURCE_AUTH_DIALOG_DISALLOW_ALL 0
50 #define SUBRESOURCE_AUTH_DIALOG_DISALLOW_CROSS_ORIGIN 1
51 #define SUBRESOURCE_AUTH_DIALOG_ALLOW_ALL 2
53 #define HTTP_AUTH_DIALOG_TOP_LEVEL_DOC 29
54 #define HTTP_AUTH_DIALOG_SAME_ORIGIN_SUBRESOURCE 30
55 #define HTTP_AUTH_DIALOG_SAME_ORIGIN_XHR 31
56 #define HTTP_AUTH_DIALOG_NON_WEB_CONTENT 32
58 #define HTTP_AUTH_BASIC_INSECURE 0
59 #define HTTP_AUTH_BASIC_SECURE 1
60 #define HTTP_AUTH_DIGEST_INSECURE 2
61 #define HTTP_AUTH_DIGEST_SECURE 3
62 #define HTTP_AUTH_NTLM_INSECURE 4
63 #define HTTP_AUTH_NTLM_SECURE 5
64 #define HTTP_AUTH_NEGOTIATE_INSECURE 6
65 #define HTTP_AUTH_NEGOTIATE_SECURE 7
67 #define MAX_DISPLAYED_USER_LENGTH 64
68 #define MAX_DISPLAYED_HOST_LENGTH 64
70 static void GetOriginAttributesSuffix(nsIChannel* aChan, nsACString& aSuffix) {
71 OriginAttributes oa;
73 // Deliberately ignoring the result and going with defaults
74 if (aChan) {
75 StoragePrincipalHelper::GetOriginAttributesForNetworkState(aChan, oa);
78 oa.CreateSuffix(aSuffix);
81 nsHttpChannelAuthProvider::nsHttpChannelAuthProvider()
82 : mProxyAuth(false),
83 mTriedProxyAuth(false),
84 mTriedHostAuth(false),
85 mSuppressDefensiveAuth(false),
86 mCrossOrigin(false),
87 mConnectionBased(false),
88 mHttpHandler(gHttpHandler) {}
90 nsHttpChannelAuthProvider::~nsHttpChannelAuthProvider() {
91 MOZ_RELEASE_ASSERT(NS_IsMainThread());
92 MOZ_ASSERT(!mAuthChannel, "Disconnect wasn't called");
95 NS_IMETHODIMP
96 nsHttpChannelAuthProvider::Init(nsIHttpAuthenticableChannel* channel) {
97 MOZ_ASSERT(channel, "channel expected!");
99 mAuthChannel = channel;
101 nsresult rv = mAuthChannel->GetURI(getter_AddRefs(mURI));
102 if (NS_FAILED(rv)) return rv;
104 rv = mAuthChannel->GetIsSSL(&mUsingSSL);
105 if (NS_FAILED(rv)) return rv;
107 nsCOMPtr<nsIProxiedChannel> proxied(channel);
108 if (proxied) {
109 nsCOMPtr<nsIProxyInfo> pi;
110 rv = proxied->GetProxyInfo(getter_AddRefs(pi));
111 if (NS_FAILED(rv)) return rv;
113 if (pi) {
114 nsAutoCString proxyType;
115 rv = pi->GetType(proxyType);
116 if (NS_FAILED(rv)) return rv;
118 mProxyUsingSSL = proxyType.EqualsLiteral("https");
122 rv = mURI->GetAsciiHost(mHost);
123 if (NS_FAILED(rv)) return rv;
125 // reject the URL if it doesn't specify a host
126 if (mHost.IsEmpty()) return NS_ERROR_MALFORMED_URI;
128 rv = mURI->GetPort(&mPort);
129 if (NS_FAILED(rv)) return rv;
131 nsCOMPtr<nsIChannel> bareChannel = do_QueryInterface(channel);
132 mIsPrivate = NS_UsePrivateBrowsing(bareChannel);
134 return NS_OK;
137 NS_IMETHODIMP
138 nsHttpChannelAuthProvider::ProcessAuthentication(uint32_t httpStatus,
139 bool SSLConnectFailed) {
140 LOG(
141 ("nsHttpChannelAuthProvider::ProcessAuthentication "
142 "[this=%p channel=%p code=%u SSLConnectFailed=%d]\n",
143 this, mAuthChannel, httpStatus, SSLConnectFailed));
145 MOZ_ASSERT(mAuthChannel, "Channel not initialized");
147 nsCOMPtr<nsIProxyInfo> proxyInfo;
148 nsresult rv = mAuthChannel->GetProxyInfo(getter_AddRefs(proxyInfo));
149 if (NS_FAILED(rv)) return rv;
150 if (proxyInfo) {
151 mProxyInfo = do_QueryInterface(proxyInfo);
152 if (!mProxyInfo) return NS_ERROR_NO_INTERFACE;
155 nsAutoCString challenges;
156 mProxyAuth = (httpStatus == 407);
158 rv = PrepareForAuthentication(mProxyAuth);
159 if (NS_FAILED(rv)) return rv;
161 if (mProxyAuth) {
162 // only allow a proxy challenge if we have a proxy server configured.
163 // otherwise, we could inadvertently expose the user's proxy
164 // credentials to an origin server. We could attempt to proceed as
165 // if we had received a 401 from the server, but why risk flirting
166 // with trouble? IE similarly rejects 407s when a proxy server is
167 // not configured, so there's no reason not to do the same.
168 if (!UsingHttpProxy()) {
169 LOG(("rejecting 407 when proxy server not configured!\n"));
170 return NS_ERROR_UNEXPECTED;
172 if (UsingSSL() && !SSLConnectFailed) {
173 // we need to verify that this challenge came from the proxy
174 // server itself, and not some server on the other side of the
175 // SSL tunnel.
176 LOG(("rejecting 407 from origin server!\n"));
177 return NS_ERROR_UNEXPECTED;
179 rv = mAuthChannel->GetProxyChallenges(challenges);
180 } else {
181 rv = mAuthChannel->GetWWWChallenges(challenges);
183 if (NS_FAILED(rv)) return rv;
185 nsAutoCString creds;
186 rv = GetCredentials(challenges, mProxyAuth, creds);
187 if (rv == NS_ERROR_IN_PROGRESS) return rv;
188 if (NS_FAILED(rv)) {
189 LOG(("unable to authenticate\n"));
190 } else {
191 // set the authentication credentials
192 if (mProxyAuth) {
193 rv = mAuthChannel->SetProxyCredentials(creds);
194 } else {
195 rv = mAuthChannel->SetWWWCredentials(creds);
198 return rv;
201 NS_IMETHODIMP
202 nsHttpChannelAuthProvider::AddAuthorizationHeaders(
203 bool aDontUseCachedWWWCreds) {
204 LOG(
205 ("nsHttpChannelAuthProvider::AddAuthorizationHeaders? "
206 "[this=%p channel=%p]\n",
207 this, mAuthChannel));
209 MOZ_ASSERT(mAuthChannel, "Channel not initialized");
211 nsCOMPtr<nsIProxyInfo> proxyInfo;
212 nsresult rv = mAuthChannel->GetProxyInfo(getter_AddRefs(proxyInfo));
213 if (NS_FAILED(rv)) return rv;
214 if (proxyInfo) {
215 mProxyInfo = do_QueryInterface(proxyInfo);
216 if (!mProxyInfo) return NS_ERROR_NO_INTERFACE;
219 uint32_t loadFlags;
220 rv = mAuthChannel->GetLoadFlags(&loadFlags);
221 if (NS_FAILED(rv)) return rv;
223 // this getter never fails
224 nsHttpAuthCache* authCache = gHttpHandler->AuthCache(mIsPrivate);
226 // check if proxy credentials should be sent
227 if (!ProxyHost().IsEmpty() && UsingHttpProxy()) {
228 SetAuthorizationHeader(authCache, nsHttp::Proxy_Authorization, "http"_ns,
229 ProxyHost(), ProxyPort(),
230 ""_ns, // proxy has no path
231 mProxyIdent);
234 if (loadFlags & nsIRequest::LOAD_ANONYMOUS) {
235 LOG(("Skipping Authorization header for anonymous load\n"));
236 return NS_OK;
239 if (aDontUseCachedWWWCreds) {
240 LOG(
241 ("Authorization header already present:"
242 " skipping adding auth header from cache\n"));
243 return NS_OK;
246 // check if server credentials should be sent
247 nsAutoCString path, scheme;
248 if (NS_SUCCEEDED(GetCurrentPath(path)) &&
249 NS_SUCCEEDED(mURI->GetScheme(scheme))) {
250 SetAuthorizationHeader(authCache, nsHttp::Authorization, scheme, Host(),
251 Port(), path, mIdent);
254 return NS_OK;
257 NS_IMETHODIMP
258 nsHttpChannelAuthProvider::CheckForSuperfluousAuth() {
259 LOG(
260 ("nsHttpChannelAuthProvider::CheckForSuperfluousAuth? "
261 "[this=%p channel=%p]\n",
262 this, mAuthChannel));
264 MOZ_ASSERT(mAuthChannel, "Channel not initialized");
266 // we've been called because it has been determined that this channel is
267 // getting loaded without taking the userpass from the URL. if the URL
268 // contained a userpass, then (provided some other conditions are true),
269 // we'll give the user an opportunity to abort the channel as this might be
270 // an attempt to spoof a different site (see bug 232567).
271 if (!ConfirmAuth("SuperfluousAuth", true)) {
272 // calling cancel here sets our mStatus and aborts the HTTP
273 // transaction, which prevents OnDataAvailable events.
274 Unused << mAuthChannel->Cancel(NS_ERROR_ABORT);
275 return NS_ERROR_ABORT;
277 return NS_OK;
280 NS_IMETHODIMP
281 nsHttpChannelAuthProvider::Cancel(nsresult status) {
282 MOZ_ASSERT(mAuthChannel, "Channel not initialized");
284 if (mAsyncPromptAuthCancelable) {
285 mAsyncPromptAuthCancelable->Cancel(status);
286 mAsyncPromptAuthCancelable = nullptr;
289 if (mGenerateCredentialsCancelable) {
290 mGenerateCredentialsCancelable->Cancel(status);
291 mGenerateCredentialsCancelable = nullptr;
293 return NS_OK;
296 NS_IMETHODIMP
297 nsHttpChannelAuthProvider::Disconnect(nsresult status) {
298 mAuthChannel = nullptr;
300 if (mAsyncPromptAuthCancelable) {
301 mAsyncPromptAuthCancelable->Cancel(status);
302 mAsyncPromptAuthCancelable = nullptr;
305 if (mGenerateCredentialsCancelable) {
306 mGenerateCredentialsCancelable->Cancel(status);
307 mGenerateCredentialsCancelable = nullptr;
310 NS_IF_RELEASE(mProxyAuthContinuationState);
311 NS_IF_RELEASE(mAuthContinuationState);
313 return NS_OK;
316 // helper function for getting an auth prompt from an interface requestor
317 static void GetAuthPrompt(nsIInterfaceRequestor* ifreq, bool proxyAuth,
318 nsIAuthPrompt2** result) {
319 if (!ifreq) return;
321 uint32_t promptReason;
322 if (proxyAuth) {
323 promptReason = nsIAuthPromptProvider::PROMPT_PROXY;
324 } else {
325 promptReason = nsIAuthPromptProvider::PROMPT_NORMAL;
328 nsCOMPtr<nsIAuthPromptProvider> promptProvider = do_GetInterface(ifreq);
329 if (promptProvider) {
330 promptProvider->GetAuthPrompt(promptReason, NS_GET_IID(nsIAuthPrompt2),
331 reinterpret_cast<void**>(result));
332 } else {
333 NS_QueryAuthPrompt2(ifreq, result);
337 // generate credentials for the given challenge, and update the auth cache.
338 nsresult nsHttpChannelAuthProvider::GenCredsAndSetEntry(
339 nsIHttpAuthenticator* auth, bool proxyAuth, const nsACString& scheme,
340 const nsACString& host, int32_t port, const nsACString& directory,
341 const nsACString& realm, const nsACString& challenge,
342 const nsHttpAuthIdentity& ident, nsCOMPtr<nsISupports>& sessionState,
343 nsACString& result) {
344 nsresult rv;
345 nsISupports* ss = sessionState;
347 // set informations that depend on whether
348 // we're authenticating against a proxy
349 // or a webserver
350 nsISupports** continuationState;
352 if (proxyAuth) {
353 continuationState = &mProxyAuthContinuationState;
354 } else {
355 continuationState = &mAuthContinuationState;
358 rv = auth->GenerateCredentialsAsync(
359 mAuthChannel, this, challenge, proxyAuth, ident.Domain(), ident.User(),
360 ident.Password(), ss, *continuationState,
361 getter_AddRefs(mGenerateCredentialsCancelable));
362 if (NS_SUCCEEDED(rv)) {
363 // Calling generate credentials async, results will be dispatched to the
364 // main thread by calling OnCredsGenerated method
365 return NS_ERROR_IN_PROGRESS;
368 uint32_t generateFlags;
369 rv = auth->GenerateCredentials(
370 mAuthChannel, challenge, proxyAuth, ident.Domain(), ident.User(),
371 ident.Password(), &ss, &*continuationState, &generateFlags, result);
373 sessionState.swap(ss);
374 if (NS_FAILED(rv)) return rv;
376 // don't log this in release build since it could contain sensitive info.
377 #ifdef DEBUG
378 LOG(("generated creds: %s\n", result.BeginReading()));
379 #endif
381 return UpdateCache(auth, scheme, host, port, directory, realm, challenge,
382 ident, result, generateFlags, sessionState, proxyAuth);
385 nsresult nsHttpChannelAuthProvider::UpdateCache(
386 nsIHttpAuthenticator* auth, const nsACString& scheme,
387 const nsACString& host, int32_t port, const nsACString& directory,
388 const nsACString& realm, const nsACString& challenge,
389 const nsHttpAuthIdentity& ident, const nsACString& creds,
390 uint32_t generateFlags, nsISupports* sessionState, bool aProxyAuth) {
391 nsresult rv;
393 uint32_t authFlags;
394 rv = auth->GetAuthFlags(&authFlags);
395 if (NS_FAILED(rv)) return rv;
397 // find out if this authenticator allows reuse of credentials and/or
398 // challenge.
399 bool saveCreds =
400 0 != (authFlags & nsIHttpAuthenticator::REUSABLE_CREDENTIALS);
401 bool saveChallenge =
402 0 != (authFlags & nsIHttpAuthenticator::REUSABLE_CHALLENGE);
404 bool saveIdentity =
405 0 == (generateFlags & nsIHttpAuthenticator::USING_INTERNAL_IDENTITY);
407 // this getter never fails
408 nsHttpAuthCache* authCache = gHttpHandler->AuthCache(mIsPrivate);
410 nsAutoCString suffix;
411 if (!aProxyAuth) {
412 // We don't isolate proxy credentials cache entries with the origin suffix
413 // as it would only annoy users with authentication dialogs popping up.
414 nsCOMPtr<nsIChannel> chan = do_QueryInterface(mAuthChannel);
415 GetOriginAttributesSuffix(chan, suffix);
418 // create a cache entry. we do this even though we don't yet know that
419 // these credentials are valid b/c we need to avoid prompting the user
420 // more than once in case the credentials are valid.
422 // if the credentials are not reusable, then we don't bother sticking
423 // them in the auth cache.
424 rv = authCache->SetAuthEntry(scheme, host, port, directory, realm,
425 saveCreds ? creds : ""_ns,
426 saveChallenge ? challenge : ""_ns, suffix,
427 saveIdentity ? &ident : nullptr, sessionState);
428 return rv;
431 NS_IMETHODIMP nsHttpChannelAuthProvider::ClearProxyIdent() {
432 LOG(("nsHttpChannelAuthProvider::ClearProxyIdent [this=%p]\n", this));
434 mProxyIdent.Clear();
435 return NS_OK;
438 nsresult nsHttpChannelAuthProvider::PrepareForAuthentication(bool proxyAuth) {
439 LOG(
440 ("nsHttpChannelAuthProvider::PrepareForAuthentication "
441 "[this=%p channel=%p]\n",
442 this, mAuthChannel));
444 if (!proxyAuth) {
445 // reset the current proxy continuation state because our last
446 // authentication attempt was completed successfully.
447 NS_IF_RELEASE(mProxyAuthContinuationState);
448 LOG((" proxy continuation state has been reset"));
451 if (!UsingHttpProxy() || mProxyAuthType.IsEmpty()) return NS_OK;
453 // We need to remove any Proxy_Authorization header left over from a
454 // non-request based authentication handshake (e.g., for NTLM auth).
456 nsresult rv;
457 nsCOMPtr<nsIHttpAuthenticator> precedingAuth;
458 nsCString proxyAuthType;
459 rv = GetAuthenticator(mProxyAuthType, proxyAuthType,
460 getter_AddRefs(precedingAuth));
461 if (NS_FAILED(rv)) return rv;
463 uint32_t precedingAuthFlags;
464 rv = precedingAuth->GetAuthFlags(&precedingAuthFlags);
465 if (NS_FAILED(rv)) return rv;
467 if (!(precedingAuthFlags & nsIHttpAuthenticator::REQUEST_BASED)) {
468 nsAutoCString challenges;
469 rv = mAuthChannel->GetProxyChallenges(challenges);
470 if (NS_FAILED(rv)) {
471 // delete the proxy authorization header because we weren't
472 // asked to authenticate
473 rv = mAuthChannel->SetProxyCredentials(""_ns);
474 if (NS_FAILED(rv)) return rv;
475 LOG((" cleared proxy authorization header"));
479 return NS_OK;
482 class MOZ_STACK_CLASS ChallengeParser final : Tokenizer {
483 public:
484 explicit ChallengeParser(const nsACString& aChallenges)
485 : Tokenizer(aChallenges, nullptr, "") {
486 Record();
489 Maybe<nsDependentCSubstring> GetNext() {
490 Token t;
491 nsDependentCSubstring result;
493 bool inQuote = false;
495 while (Next(t)) {
496 if (t.Type() == TOKEN_EOL) {
497 Claim(result, ClaimInclusion::EXCLUDE_LAST);
498 SkipWhites(WhiteSkipping::INCLUDE_NEW_LINE);
499 Record();
500 inQuote = false;
501 if (!result.IsEmpty()) {
502 return Some(result);
504 } else if (t.Equals(Token::Char(',')) && !inQuote) {
505 // Sometimes we get multiple challenges separated by a comma.
506 // This is not great, as it's slightly ambiguous. We check if something
507 // is a new challenge by matching agains <param_name> =
508 // If the , isn't followed by a word and = then most likely
509 // it is the name of an authType.
511 const char* prevCursorPos = mCursor;
512 const char* prevRollbackPos = mRollback;
514 auto hasWordAndEqual = [&]() {
515 SkipWhites();
516 nsDependentCSubstring word;
517 if (!ReadWord(word)) {
518 return false;
520 SkipWhites();
521 return Check(Token::Char('='));
523 if (!hasWordAndEqual()) {
524 // This is not a parameter. It means the `,` character starts a
525 // different challenge.
526 // We'll revert the cursor and return the contents so far.
527 mCursor = prevCursorPos;
528 mRollback = prevRollbackPos;
529 Claim(result, ClaimInclusion::EXCLUDE_LAST);
530 SkipWhites();
531 Record();
532 if (!result.IsEmpty()) {
533 return Some(result);
536 } else if (t.Equals(Token::Char('"'))) {
537 inQuote = !inQuote;
541 Claim(result, Tokenizer::ClaimInclusion::INCLUDE_LAST);
542 SkipWhites();
543 Record();
544 if (!result.IsEmpty()) {
545 return Some(result);
547 return Nothing{};
551 enum ChallengeRank {
552 Unknown = 0,
553 Basic = 1,
554 Digest = 2,
555 NTLM = 3,
556 Negotiate = 4,
559 ChallengeRank Rank(const nsACString& aChallenge) {
560 if (StringBeginsWith(aChallenge, "Negotiate"_ns,
561 nsCaseInsensitiveCStringComparator)) {
562 return ChallengeRank::Negotiate;
565 if (StringBeginsWith(aChallenge, "NTLM"_ns,
566 nsCaseInsensitiveCStringComparator)) {
567 return ChallengeRank::NTLM;
570 if (StringBeginsWith(aChallenge, "Digest"_ns,
571 nsCaseInsensitiveCStringComparator)) {
572 return ChallengeRank::Digest;
575 if (StringBeginsWith(aChallenge, "Basic"_ns,
576 nsCaseInsensitiveCStringComparator)) {
577 return ChallengeRank::Basic;
580 return ChallengeRank::Unknown;
583 nsresult nsHttpChannelAuthProvider::GetCredentials(
584 const nsACString& aChallenges, bool proxyAuth, nsCString& creds) {
585 LOG(("nsHttpChannelAuthProvider::GetCredentials"));
586 nsAutoCString challenges(aChallenges);
588 using AuthChallenge = struct AuthChallenge {
589 nsDependentCSubstring challenge;
590 uint16_t algorithm = 0;
591 ChallengeRank rank = ChallengeRank::Unknown;
593 void operator=(const AuthChallenge& aOther) {
594 challenge.Rebind(aOther.challenge, 0);
595 algorithm = aOther.algorithm;
596 rank = aOther.rank;
600 nsTArray<AuthChallenge> cc;
602 ChallengeParser p(challenges);
603 while (true) {
604 auto next = p.GetNext();
605 if (next.isNothing()) {
606 break;
608 AuthChallenge ac{next.ref(), 0};
609 nsAutoCString realm, domain, nonce, opaque;
610 bool stale = false;
611 uint16_t qop = 0;
612 ac.rank = Rank(ac.challenge);
613 if (StringBeginsWith(ac.challenge, "Digest"_ns,
614 nsCaseInsensitiveCStringComparator)) {
615 Unused << nsHttpDigestAuth::ParseChallenge(ac.challenge, realm, domain,
616 nonce, opaque, &stale,
617 &ac.algorithm, &qop);
619 cc.AppendElement(ac);
622 cc.StableSort([](const AuthChallenge& lhs, const AuthChallenge& rhs) {
623 if (StaticPrefs::network_auth_choose_most_secure_challenge()) {
624 // Different auth types
625 if (lhs.rank != rhs.rank) {
626 return lhs.rank < rhs.rank ? 1 : -1;
629 // If they're the same auth type, and not a Digest, then we treat them
630 // as equal (don't reorder them).
631 if (lhs.rank != ChallengeRank::Digest) {
632 return 0;
634 } else {
635 // Non-digest challenges should not be reordered when the pref is off.
636 if (lhs.algorithm == 0 || rhs.algorithm == 0) {
637 return 0;
641 // Same algorithm.
642 if (lhs.algorithm == rhs.algorithm) {
643 return 0;
645 return lhs.algorithm < rhs.algorithm ? 1 : -1;
648 nsCOMPtr<nsIHttpAuthenticator> auth;
649 nsCString authType; // force heap allocation to enable string sharing since
650 // we'll be assigning this value into mAuthType.
652 // set informations that depend on whether we're authenticating against a
653 // proxy or a webserver
654 nsISupports** currentContinuationState;
655 nsCString* currentAuthType;
657 if (proxyAuth) {
658 currentContinuationState = &mProxyAuthContinuationState;
659 currentAuthType = &mProxyAuthType;
660 } else {
661 currentContinuationState = &mAuthContinuationState;
662 currentAuthType = &mAuthType;
665 nsresult rv = NS_ERROR_NOT_AVAILABLE;
666 bool gotCreds = false;
668 // figure out which challenge we can handle and which authenticator to use.
669 for (size_t i = 0; i < cc.Length(); i++) {
670 rv = GetAuthenticator(cc[i].challenge, authType, getter_AddRefs(auth));
671 LOG(("trying auth for %s", authType.get()));
672 if (NS_SUCCEEDED(rv)) {
674 // if we've already selected an auth type from a previous challenge
675 // received while processing this channel, then skip others until
676 // we find a challenge corresponding to the previously tried auth
677 // type.
679 if (!currentAuthType->IsEmpty() && authType != *currentAuthType) continue;
682 // we allow the routines to run all the way through before we
683 // decide if they are valid.
685 // we don't worry about the auth cache being altered because that
686 // would have been the last step, and if the error is from updating
687 // the authcache it wasn't really altered anyway. -CTN
689 // at this point the code is really only useful for client side
690 // errors (it will not automatically fail over to do a different
691 // auth type if the server keeps rejecting what is being sent, even
692 // if a particular auth method only knows 1 thing, like a
693 // non-identity based authentication method)
695 rv = GetCredentialsForChallenge(cc[i].challenge, authType, proxyAuth,
696 auth, creds);
697 if (NS_SUCCEEDED(rv)) {
698 gotCreds = true;
699 *currentAuthType = authType;
701 break;
703 if (rv == NS_ERROR_IN_PROGRESS) {
704 // authentication prompt has been invoked and result is
705 // expected asynchronously, save current challenge being
706 // processed and all remaining challenges to use later in
707 // OnAuthAvailable and now immediately return
708 mCurrentChallenge = cc[i].challenge;
709 // imperfect; does not save server-side preference ordering.
710 // instead, continues with remaining string as provided by client
711 mRemainingChallenges.Truncate();
712 while (i + 1 < cc.Length()) {
713 i++;
714 mRemainingChallenges.Append(cc[i].challenge);
715 mRemainingChallenges.Append("\n"_ns);
717 return rv;
720 // reset the auth type and continuation state
721 NS_IF_RELEASE(*currentContinuationState);
722 currentAuthType->Truncate();
726 if (!gotCreds && !currentAuthType->IsEmpty()) {
727 // looks like we never found the auth type we were looking for.
728 // reset the auth type and continuation state, and try again.
729 currentAuthType->Truncate();
730 NS_IF_RELEASE(*currentContinuationState);
732 rv = GetCredentials(challenges, proxyAuth, creds);
735 return rv;
738 nsresult nsHttpChannelAuthProvider::GetAuthorizationMembers(
739 bool proxyAuth, nsACString& scheme, nsCString& host, int32_t& port,
740 nsACString& path, nsHttpAuthIdentity*& ident,
741 nsISupports**& continuationState) {
742 if (proxyAuth) {
743 MOZ_ASSERT(UsingHttpProxy(),
744 "proxyAuth is true, but no HTTP proxy is configured!");
746 host = ProxyHost();
747 port = ProxyPort();
748 ident = &mProxyIdent;
749 scheme.AssignLiteral("http");
751 continuationState = &mProxyAuthContinuationState;
752 } else {
753 host = Host();
754 port = Port();
755 ident = &mIdent;
757 nsresult rv;
758 rv = GetCurrentPath(path);
759 if (NS_FAILED(rv)) return rv;
761 rv = mURI->GetScheme(scheme);
762 if (NS_FAILED(rv)) return rv;
764 continuationState = &mAuthContinuationState;
767 return NS_OK;
770 nsresult nsHttpChannelAuthProvider::GetCredentialsForChallenge(
771 const nsACString& aChallenge, const nsACString& aAuthType, bool proxyAuth,
772 nsIHttpAuthenticator* auth, nsCString& creds) {
773 LOG(
774 ("nsHttpChannelAuthProvider::GetCredentialsForChallenge "
775 "[this=%p channel=%p proxyAuth=%d challenges=%s]\n",
776 this, mAuthChannel, proxyAuth, nsCString(aChallenge).get()));
778 // this getter never fails
779 nsHttpAuthCache* authCache = gHttpHandler->AuthCache(mIsPrivate);
781 uint32_t authFlags;
782 nsresult rv = auth->GetAuthFlags(&authFlags);
783 if (NS_FAILED(rv)) return rv;
785 nsAutoCString realm;
786 ParseRealm(aChallenge, realm);
788 // if no realm, then use the auth type as the realm. ToUpperCase so the
789 // ficticious realm stands out a bit more.
790 // XXX this will cause some single signon misses!
791 // XXX this was meant to be used with NTLM, which supplies no realm.
793 if (realm.IsEmpty()) {
794 realm = authType;
795 ToUpperCase(realm);
799 // set informations that depend on whether
800 // we're authenticating against a proxy
801 // or a webserver
802 nsAutoCString host;
803 int32_t port;
804 nsHttpAuthIdentity* ident;
805 nsAutoCString path, scheme;
806 bool identFromURI = false;
807 nsISupports** continuationState;
809 rv = GetAuthorizationMembers(proxyAuth, scheme, host, port, path, ident,
810 continuationState);
811 if (NS_FAILED(rv)) return rv;
813 uint32_t loadFlags;
814 rv = mAuthChannel->GetLoadFlags(&loadFlags);
815 if (NS_FAILED(rv)) return rv;
817 // Fill only for non-proxy auth, proxy credentials are not OA-isolated.
818 nsAutoCString suffix;
820 if (!proxyAuth) {
821 nsCOMPtr<nsIChannel> chan = do_QueryInterface(mAuthChannel);
822 GetOriginAttributesSuffix(chan, suffix);
824 // if this is the first challenge, then try using the identity
825 // specified in the URL.
826 if (mIdent.IsEmpty()) {
827 GetIdentityFromURI(authFlags, mIdent);
828 identFromURI = !mIdent.IsEmpty();
831 if ((loadFlags & nsIRequest::LOAD_ANONYMOUS) && !identFromURI) {
832 LOG(("Skipping authentication for anonymous non-proxy request\n"));
833 return NS_ERROR_NOT_AVAILABLE;
836 // Let explicit URL credentials pass
837 // regardless of the LOAD_ANONYMOUS flag
838 } else if ((loadFlags & nsIRequest::LOAD_ANONYMOUS) && !UsingHttpProxy()) {
839 LOG(("Skipping authentication for anonymous non-proxy request\n"));
840 return NS_ERROR_NOT_AVAILABLE;
844 // if we already tried some credentials for this transaction, then
845 // we need to possibly clear them from the cache, unless the credentials
846 // in the cache have changed, in which case we'd want to give them a
847 // try instead.
849 nsHttpAuthEntry* entry = nullptr;
850 Unused << authCache->GetAuthEntryForDomain(scheme, host, port, realm, suffix,
851 &entry);
853 // hold reference to the auth session state (in case we clear our
854 // reference to the entry).
855 nsCOMPtr<nsISupports> sessionStateGrip;
856 if (entry) sessionStateGrip = entry->mMetaData;
858 // remember if we already had the continuation state. it means we are in
859 // the middle of the authentication exchange and the connection must be
860 // kept sticky then (and only then).
861 bool authAtProgress = !!*continuationState;
863 // for digest auth, maybe our cached nonce value simply timed out...
864 bool identityInvalid;
865 nsISupports* sessionState = sessionStateGrip;
866 rv = auth->ChallengeReceived(mAuthChannel, aChallenge, proxyAuth,
867 &sessionState, &*continuationState,
868 &identityInvalid);
869 sessionStateGrip.swap(sessionState);
870 if (NS_FAILED(rv)) return rv;
872 LOG((" identity invalid = %d\n", identityInvalid));
874 if (mConnectionBased && identityInvalid) {
875 // If the flag is set and identity is invalid, it means we received the
876 // first challange for a new negotiation round after negotiating a
877 // connection based auth failed (invalid password). The mConnectionBased
878 // flag is set later for the newly received challenge, so here it reflects
879 // the previous 401/7 response schema.
880 rv = mAuthChannel->CloseStickyConnection();
881 MOZ_ASSERT(NS_SUCCEEDED(rv));
882 if (!proxyAuth) {
883 // We must clear proxy ident in the following scenario + explanation:
884 // - we are authenticating to an NTLM proxy and an NTLM server
885 // - we successfully authenticated to the proxy, mProxyIdent keeps
886 // the user name/domain and password, the identity has also been cached
887 // - we just threw away the connection because we are now asking for
888 // creds for the server (WWW auth)
889 // - hence, we will have to auth to the proxy again as well
890 // - if we didn't clear the proxy identity, it would be considered
891 // as non-valid and we would ask the user again ; clearing it forces
892 // use of the cached identity and not asking the user again
893 ClearProxyIdent();
897 mConnectionBased = !!(authFlags & nsIHttpAuthenticator::CONNECTION_BASED);
899 // It's legal if the peer closes the connection after the first 401/7.
900 // Making the connection sticky will prevent its restart giving the user
901 // a 'network reset' error every time. Hence, we mark the connection
902 // as restartable.
903 mAuthChannel->ConnectionRestartable(!authAtProgress);
905 if (identityInvalid) {
906 if (entry) {
907 if (ident->Equals(entry->Identity())) {
908 if (!identFromURI) {
909 LOG((" clearing bad auth cache entry\n"));
910 // ok, we've already tried this user identity, so clear the
911 // corresponding entry from the auth cache.
912 authCache->ClearAuthEntry(scheme, host, port, realm, suffix);
913 entry = nullptr;
914 ident->Clear();
916 } else if (!identFromURI ||
917 (ident->User() == entry->Identity().User() &&
918 !(loadFlags & (nsIChannel::LOAD_ANONYMOUS |
919 nsIChannel::LOAD_EXPLICIT_CREDENTIALS)))) {
920 LOG((" taking identity from auth cache\n"));
921 // the password from the auth cache is more likely to be
922 // correct than the one in the URL. at least, we know that it
923 // works with the given username. it is possible for a server
924 // to distinguish logons based on the supplied password alone,
925 // but that would be quite unusual... and i don't think we need
926 // to worry about such unorthodox cases.
927 *ident = entry->Identity();
928 identFromURI = false;
929 if (entry->Creds()[0] != '\0') {
930 LOG((" using cached credentials!\n"));
931 creds.Assign(entry->Creds());
932 return entry->AddPath(path);
935 } else if (!identFromURI) {
936 // hmm... identity invalid, but no auth entry! the realm probably
937 // changed (see bug 201986).
938 ident->Clear();
941 if (!entry && ident->IsEmpty()) {
942 uint32_t level = nsIAuthPrompt2::LEVEL_NONE;
943 if ((!proxyAuth && mUsingSSL) || (proxyAuth && mProxyUsingSSL)) {
944 level = nsIAuthPrompt2::LEVEL_SECURE;
945 } else if (authFlags & nsIHttpAuthenticator::IDENTITY_ENCRYPTED) {
946 level = nsIAuthPrompt2::LEVEL_PW_ENCRYPTED;
949 // Collect statistics on how frequently the various types of HTTP
950 // authentication are used over SSL and non-SSL connections.
951 if (Telemetry::CanRecordPrereleaseData()) {
952 if ("basic"_ns.Equals(aAuthType, nsCaseInsensitiveCStringComparator)) {
953 Telemetry::Accumulate(
954 Telemetry::HTTP_AUTH_TYPE_STATS,
955 UsingSSL() ? HTTP_AUTH_BASIC_SECURE : HTTP_AUTH_BASIC_INSECURE);
956 } else if ("digest"_ns.Equals(aAuthType,
957 nsCaseInsensitiveCStringComparator)) {
958 Telemetry::Accumulate(
959 Telemetry::HTTP_AUTH_TYPE_STATS,
960 UsingSSL() ? HTTP_AUTH_DIGEST_SECURE : HTTP_AUTH_DIGEST_INSECURE);
961 } else if ("ntlm"_ns.Equals(aAuthType,
962 nsCaseInsensitiveCStringComparator)) {
963 Telemetry::Accumulate(
964 Telemetry::HTTP_AUTH_TYPE_STATS,
965 UsingSSL() ? HTTP_AUTH_NTLM_SECURE : HTTP_AUTH_NTLM_INSECURE);
966 } else if ("negotiate"_ns.Equals(aAuthType,
967 nsCaseInsensitiveCStringComparator)) {
968 Telemetry::Accumulate(Telemetry::HTTP_AUTH_TYPE_STATS,
969 UsingSSL() ? HTTP_AUTH_NEGOTIATE_SECURE
970 : HTTP_AUTH_NEGOTIATE_INSECURE);
974 // Depending on the pref setting, the authentication dialog may be
975 // blocked for all sub-resources, blocked for cross-origin
976 // sub-resources, or always allowed for sub-resources.
977 // For more details look at the bug 647010.
978 // BlockPrompt will set mCrossOrigin parameter as well.
979 if (BlockPrompt(proxyAuth)) {
980 LOG((
981 "nsHttpChannelAuthProvider::GetCredentialsForChallenge: "
982 "Prompt is blocked [this=%p pref=%d img-pref=%d "
983 "non-web-content-triggered-pref=%d]\n",
984 this, StaticPrefs::network_auth_subresource_http_auth_allow(),
985 StaticPrefs::
986 network_auth_subresource_img_cross_origin_http_auth_allow(),
987 StaticPrefs::
988 network_auth_non_web_content_triggered_resources_http_auth_allow()));
989 return NS_ERROR_ABORT;
992 // at this point we are forced to interact with the user to get
993 // their username and password for this domain.
994 rv = PromptForIdentity(level, proxyAuth, realm, aAuthType, authFlags,
995 *ident);
996 if (NS_FAILED(rv)) return rv;
997 identFromURI = false;
1001 if (identFromURI) {
1002 // Warn the user before automatically using the identity from the URL
1003 // to automatically log them into a site (see bug 232567).
1004 if (!ConfirmAuth("AutomaticAuth", false)) {
1005 // calling cancel here sets our mStatus and aborts the HTTP
1006 // transaction, which prevents OnDataAvailable events.
1007 rv = mAuthChannel->Cancel(NS_ERROR_ABORT);
1008 MOZ_ASSERT(NS_SUCCEEDED(rv));
1009 // this return code alone is not equivalent to Cancel, since
1010 // it only instructs our caller that authentication failed.
1011 // without an explicit call to Cancel, our caller would just
1012 // load the page that accompanies the HTTP auth challenge.
1013 return NS_ERROR_ABORT;
1018 // get credentials for the given user:pass
1020 // always store the credentials we're trying now so that they will be used
1021 // on subsequent links. This will potentially remove good credentials from
1022 // the cache. This is ok as we don't want to use cached credentials if the
1023 // user specified something on the URI or in another manner. This is so
1024 // that we don't transparently authenticate as someone they're not
1025 // expecting to authenticate as.
1027 nsCString result;
1028 rv = GenCredsAndSetEntry(auth, proxyAuth, scheme, host, port, path, realm,
1029 aChallenge, *ident, sessionStateGrip, creds);
1030 return rv;
1033 bool nsHttpChannelAuthProvider::BlockPrompt(bool proxyAuth) {
1034 // Verify that it's ok to prompt for credentials here, per spec
1035 // http://xhr.spec.whatwg.org/#the-send%28%29-method
1037 nsCOMPtr<nsIHttpChannelInternal> chanInternal =
1038 do_QueryInterface(mAuthChannel);
1039 MOZ_ASSERT(chanInternal);
1041 if (chanInternal->GetBlockAuthPrompt()) {
1042 LOG(
1043 ("nsHttpChannelAuthProvider::BlockPrompt: Prompt is blocked "
1044 "[this=%p channel=%p]\n",
1045 this, mAuthChannel));
1046 return true;
1049 if (proxyAuth) {
1050 // Do not block auth-dialog if this is a proxy authentication.
1051 return false;
1054 nsCOMPtr<nsIChannel> chan = do_QueryInterface(mAuthChannel);
1055 nsCOMPtr<nsILoadInfo> loadInfo = chan->LoadInfo();
1057 // We will treat loads w/o loadInfo as a top level document.
1058 bool topDoc = true;
1059 bool xhr = false;
1060 bool nonWebContent = false;
1062 if (loadInfo->GetExternalContentPolicyType() !=
1063 ExtContentPolicy::TYPE_DOCUMENT) {
1064 topDoc = false;
1067 if (!topDoc) {
1068 nsCOMPtr<nsIPrincipal> triggeringPrinc = loadInfo->TriggeringPrincipal();
1069 if (triggeringPrinc->IsSystemPrincipal()) {
1070 nonWebContent = true;
1074 if (loadInfo->GetExternalContentPolicyType() ==
1075 ExtContentPolicy::TYPE_XMLHTTPREQUEST) {
1076 xhr = true;
1079 if (!topDoc && !xhr) {
1080 nsCOMPtr<nsIURI> topURI;
1081 Unused << chanInternal->GetTopWindowURI(getter_AddRefs(topURI));
1082 if (topURI) {
1083 mCrossOrigin = !NS_SecurityCompareURIs(topURI, mURI, true);
1084 } else {
1085 nsIPrincipal* loadingPrinc = loadInfo->GetLoadingPrincipal();
1086 MOZ_ASSERT(loadingPrinc);
1087 mCrossOrigin = !loadingPrinc->IsSameOrigin(mURI);
1091 if (Telemetry::CanRecordPrereleaseData()) {
1092 if (topDoc) {
1093 Telemetry::Accumulate(Telemetry::HTTP_AUTH_DIALOG_STATS_3,
1094 HTTP_AUTH_DIALOG_TOP_LEVEL_DOC);
1095 } else if (nonWebContent) {
1096 Telemetry::Accumulate(Telemetry::HTTP_AUTH_DIALOG_STATS_3,
1097 HTTP_AUTH_DIALOG_NON_WEB_CONTENT);
1098 } else if (!mCrossOrigin) {
1099 if (xhr) {
1100 Telemetry::Accumulate(Telemetry::HTTP_AUTH_DIALOG_STATS_3,
1101 HTTP_AUTH_DIALOG_SAME_ORIGIN_XHR);
1102 } else {
1103 Telemetry::Accumulate(Telemetry::HTTP_AUTH_DIALOG_STATS_3,
1104 HTTP_AUTH_DIALOG_SAME_ORIGIN_SUBRESOURCE);
1106 } else {
1107 Telemetry::Accumulate(
1108 Telemetry::HTTP_AUTH_DIALOG_STATS_3,
1109 static_cast<uint32_t>(loadInfo->GetExternalContentPolicyType()));
1113 if (!topDoc &&
1114 !StaticPrefs::
1115 network_auth_non_web_content_triggered_resources_http_auth_allow() &&
1116 nonWebContent) {
1117 return true;
1120 switch (StaticPrefs::network_auth_subresource_http_auth_allow()) {
1121 case SUBRESOURCE_AUTH_DIALOG_DISALLOW_ALL:
1122 // Do not open the http-authentication credentials dialog for
1123 // the sub-resources.
1124 return !topDoc && !xhr;
1125 case SUBRESOURCE_AUTH_DIALOG_DISALLOW_CROSS_ORIGIN:
1126 // Open the http-authentication credentials dialog for
1127 // the sub-resources only if they are not cross-origin.
1128 return !topDoc && !xhr && mCrossOrigin;
1129 case SUBRESOURCE_AUTH_DIALOG_ALLOW_ALL:
1130 // Allow the http-authentication dialog for subresources.
1131 // If pref network.auth.subresource-img-cross-origin-http-auth-allow
1132 // is set, http-authentication dialog for image subresources is
1133 // blocked.
1134 if (mCrossOrigin &&
1135 !StaticPrefs::
1136 network_auth_subresource_img_cross_origin_http_auth_allow() &&
1137 loadInfo &&
1138 ((loadInfo->GetExternalContentPolicyType() ==
1139 ExtContentPolicy::TYPE_IMAGE) ||
1140 (loadInfo->GetExternalContentPolicyType() ==
1141 ExtContentPolicy::TYPE_IMAGESET))) {
1142 return true;
1144 return false;
1145 default:
1146 // This is an invalid value.
1147 MOZ_ASSERT(false, "A non valid value!");
1149 return false;
1152 inline void GetAuthType(const nsACString& aChallenge, nsCString& authType) {
1153 auto spaceIndex = aChallenge.FindChar(' ');
1154 authType = Substring(aChallenge, 0, spaceIndex);
1155 // normalize to lowercase
1156 ToLowerCase(authType);
1159 nsresult nsHttpChannelAuthProvider::GetAuthenticator(
1160 const nsACString& aChallenge, nsCString& authType,
1161 nsIHttpAuthenticator** auth) {
1162 LOG(("nsHttpChannelAuthProvider::GetAuthenticator [this=%p channel=%p]\n",
1163 this, mAuthChannel));
1165 GetAuthType(aChallenge, authType);
1167 nsCOMPtr<nsIHttpAuthenticator> authenticator;
1168 #ifdef MOZ_AUTH_EXTENSION
1169 if (authType.EqualsLiteral("negotiate")) {
1170 authenticator = nsHttpNegotiateAuth::GetOrCreate();
1171 } else
1172 #endif
1173 if (authType.EqualsLiteral("basic")) {
1174 authenticator = nsHttpBasicAuth::GetOrCreate();
1175 } else if (authType.EqualsLiteral("digest")) {
1176 authenticator = nsHttpDigestAuth::GetOrCreate();
1177 } else if (authType.EqualsLiteral("ntlm")) {
1178 authenticator = nsHttpNTLMAuth::GetOrCreate();
1179 } else if (authType.EqualsLiteral("mock_auth") &&
1180 PR_GetEnv("XPCSHELL_TEST_PROFILE_DIR")) {
1181 authenticator = MockHttpAuth::Create();
1182 } else {
1183 return NS_ERROR_FACTORY_NOT_REGISTERED;
1186 if (!authenticator) {
1187 // If called during shutdown it's possible that the singleton authenticator
1188 // was already cleared so we have a null one here.
1189 return NS_ERROR_NOT_AVAILABLE;
1192 MOZ_ASSERT(authenticator);
1193 authenticator.forget(auth);
1195 return NS_OK;
1198 // buf contains "domain\user"
1199 static void ParseUserDomain(const nsAString& buf, nsDependentSubstring& user,
1200 nsDependentSubstring& domain) {
1201 auto backslashPos = buf.FindChar(u'\\');
1202 if (backslashPos != kNotFound) {
1203 domain.Rebind(buf, 0, backslashPos);
1204 user.Rebind(buf, backslashPos + 1);
1208 void nsHttpChannelAuthProvider::GetIdentityFromURI(uint32_t authFlags,
1209 nsHttpAuthIdentity& ident) {
1210 LOG(("nsHttpChannelAuthProvider::GetIdentityFromURI [this=%p channel=%p]\n",
1211 this, mAuthChannel));
1213 nsAutoString userBuf;
1214 nsAutoString passBuf;
1216 // XXX i18n
1217 nsAutoCString buf;
1218 mURI->GetUsername(buf);
1219 if (!buf.IsEmpty()) {
1220 NS_UnescapeURL(buf);
1221 CopyUTF8toUTF16(buf, userBuf);
1222 mURI->GetPassword(buf);
1223 if (!buf.IsEmpty()) {
1224 NS_UnescapeURL(buf);
1225 CopyUTF8toUTF16(buf, passBuf);
1229 if (!userBuf.IsEmpty()) {
1230 nsDependentSubstring user(userBuf, 0);
1231 nsDependentSubstring domain(u""_ns, 0);
1233 if (authFlags & nsIHttpAuthenticator::IDENTITY_INCLUDES_DOMAIN) {
1234 ParseUserDomain(userBuf, user, domain);
1237 ident = nsHttpAuthIdentity(domain, user, passBuf);
1241 void nsHttpChannelAuthProvider::ParseRealm(const nsACString& aChallenge,
1242 nsACString& realm) {
1244 // From RFC2617 section 1.2, the realm value is defined as such:
1246 // realm = "realm" "=" realm-value
1247 // realm-value = quoted-string
1249 // but, we'll accept anything after the the "=" up to the first space, or
1250 // end-of-line, if the string is not quoted.
1253 Tokenizer t(aChallenge);
1255 // The challenge begins with the authType.
1256 // If we can't find that something has probably gone wrong.
1257 t.SkipWhites();
1258 nsDependentCSubstring authType;
1259 if (!t.ReadWord(authType)) {
1260 return;
1263 // Will return true if the tokenizer advanced the cursor - false otherwise.
1264 auto readParam = [&](nsDependentCSubstring& key, nsAutoCString& value) {
1265 key.Rebind(EmptyCString(), 0);
1266 value.Truncate();
1268 t.SkipWhites();
1269 if (!t.ReadWord(key)) {
1270 return false;
1272 t.SkipWhites();
1273 if (!t.CheckChar('=')) {
1274 return true;
1276 t.SkipWhites();
1278 Tokenizer::Token token1;
1280 t.Record();
1281 if (!t.Next(token1)) {
1282 return true;
1284 nsDependentCSubstring sub;
1285 bool hasQuote = false;
1286 if (token1.Equals(Tokenizer::Token::Char('"'))) {
1287 hasQuote = true;
1288 } else {
1289 t.Claim(sub, Tokenizer::ClaimInclusion::INCLUDE_LAST);
1290 value.Append(sub);
1292 t.Record();
1293 Tokenizer::Token token2;
1294 while (t.Next(token2)) {
1295 if (hasQuote && token2.Equals(Tokenizer::Token::Char('"')) &&
1296 !token1.Equals(Tokenizer::Token::Char('\\'))) {
1297 break;
1299 if (!hasQuote && (token2.Type() == Tokenizer::TokenType::TOKEN_WS ||
1300 token2.Type() == Tokenizer::TokenType::TOKEN_EOL)) {
1301 break;
1304 t.Claim(sub, Tokenizer::ClaimInclusion::INCLUDE_LAST);
1305 if (!sub.Equals(R"(\)")) {
1306 value.Append(sub);
1308 t.Record();
1309 token1 = token2;
1311 return true;
1314 while (!t.CheckEOF()) {
1315 nsDependentCSubstring key;
1316 nsAutoCString value;
1317 // If we couldn't read anything, and the input isn't followed by a ,
1318 // then we exit.
1319 if (!readParam(key, value) && !t.Check(Tokenizer::Token::Char(','))) {
1320 break;
1322 // When we find the first instance of realm we exit.
1323 // Theoretically there should be only one instance and we should fail
1324 // if there are more, but we're trying to preserve existing behaviour.
1325 if (key.Equals("realm"_ns, nsCaseInsensitiveCStringComparator)) {
1326 realm = value;
1327 break;
1332 class nsHTTPAuthInformation : public nsAuthInformationHolder {
1333 public:
1334 nsHTTPAuthInformation(uint32_t aFlags, const nsString& aRealm,
1335 const nsACString& aAuthType)
1336 : nsAuthInformationHolder(aFlags, aRealm, aAuthType) {}
1338 void SetToHttpAuthIdentity(uint32_t authFlags, nsHttpAuthIdentity& identity);
1341 void nsHTTPAuthInformation::SetToHttpAuthIdentity(
1342 uint32_t authFlags, nsHttpAuthIdentity& identity) {
1343 identity = nsHttpAuthIdentity(Domain(), User(), Password());
1346 nsresult nsHttpChannelAuthProvider::PromptForIdentity(
1347 uint32_t level, bool proxyAuth, const nsACString& realm,
1348 const nsACString& authType, uint32_t authFlags, nsHttpAuthIdentity& ident) {
1349 LOG(("nsHttpChannelAuthProvider::PromptForIdentity [this=%p channel=%p]\n",
1350 this, mAuthChannel));
1352 nsresult rv;
1354 nsCOMPtr<nsIInterfaceRequestor> callbacks;
1355 rv = mAuthChannel->GetNotificationCallbacks(getter_AddRefs(callbacks));
1356 if (NS_FAILED(rv)) return rv;
1358 nsCOMPtr<nsILoadGroup> loadGroup;
1359 rv = mAuthChannel->GetLoadGroup(getter_AddRefs(loadGroup));
1360 if (NS_FAILED(rv)) return rv;
1362 nsCOMPtr<nsIAuthPrompt2> authPrompt;
1363 GetAuthPrompt(callbacks, proxyAuth, getter_AddRefs(authPrompt));
1364 if (!authPrompt && loadGroup) {
1365 nsCOMPtr<nsIInterfaceRequestor> cbs;
1366 loadGroup->GetNotificationCallbacks(getter_AddRefs(cbs));
1367 GetAuthPrompt(cbs, proxyAuth, getter_AddRefs(authPrompt));
1369 if (!authPrompt) return NS_ERROR_NO_INTERFACE;
1371 // XXX i18n: need to support non-ASCII realm strings (see bug 41489)
1372 NS_ConvertASCIItoUTF16 realmU(realm);
1374 // prompt the user...
1375 uint32_t promptFlags = 0;
1376 if (proxyAuth) {
1377 promptFlags |= nsIAuthInformation::AUTH_PROXY;
1378 if (mTriedProxyAuth) promptFlags |= nsIAuthInformation::PREVIOUS_FAILED;
1379 mTriedProxyAuth = true;
1380 } else {
1381 promptFlags |= nsIAuthInformation::AUTH_HOST;
1382 if (mTriedHostAuth) promptFlags |= nsIAuthInformation::PREVIOUS_FAILED;
1383 mTriedHostAuth = true;
1386 if (authFlags & nsIHttpAuthenticator::IDENTITY_INCLUDES_DOMAIN) {
1387 promptFlags |= nsIAuthInformation::NEED_DOMAIN;
1390 if (mCrossOrigin) {
1391 promptFlags |= nsIAuthInformation::CROSS_ORIGIN_SUB_RESOURCE;
1394 RefPtr<nsHTTPAuthInformation> holder =
1395 new nsHTTPAuthInformation(promptFlags, realmU, authType);
1396 if (!holder) return NS_ERROR_OUT_OF_MEMORY;
1398 nsCOMPtr<nsIChannel> channel(do_QueryInterface(mAuthChannel, &rv));
1399 if (NS_FAILED(rv)) return rv;
1401 rv = authPrompt->AsyncPromptAuth(channel, this, nullptr, level, holder,
1402 getter_AddRefs(mAsyncPromptAuthCancelable));
1404 if (NS_SUCCEEDED(rv)) {
1405 // indicate using this error code that authentication prompt
1406 // result is expected asynchronously
1407 rv = NS_ERROR_IN_PROGRESS;
1408 } else {
1409 // Fall back to synchronous prompt
1410 bool retval = false;
1411 rv = authPrompt->PromptAuth(channel, level, holder, &retval);
1412 if (NS_FAILED(rv)) return rv;
1414 if (!retval) {
1415 rv = NS_ERROR_ABORT;
1416 } else {
1417 holder->SetToHttpAuthIdentity(authFlags, ident);
1421 // remember that we successfully showed the user an auth dialog
1422 if (!proxyAuth) mSuppressDefensiveAuth = true;
1424 if (mConnectionBased) {
1425 // Connection can be reset by the server in the meantime user is entering
1426 // the credentials. Result would be just a "Connection was reset" error.
1427 // Hence, we drop the current regardless if the user would make it on time
1428 // to provide credentials.
1429 // It's OK to send the NTLM type 1 message (response to the plain "NTLM"
1430 // challenge) on a new connection.
1432 DebugOnly<nsresult> rv = mAuthChannel->CloseStickyConnection();
1433 MOZ_ASSERT(NS_SUCCEEDED(rv));
1437 return rv;
1440 NS_IMETHODIMP nsHttpChannelAuthProvider::OnAuthAvailable(
1441 nsISupports* aContext, nsIAuthInformation* aAuthInfo) {
1442 LOG(("nsHttpChannelAuthProvider::OnAuthAvailable [this=%p channel=%p]", this,
1443 mAuthChannel));
1445 mAsyncPromptAuthCancelable = nullptr;
1446 if (!mAuthChannel) return NS_OK;
1448 nsresult rv;
1450 nsAutoCString host;
1451 int32_t port;
1452 nsHttpAuthIdentity* ident;
1453 nsAutoCString path, scheme;
1454 nsISupports** continuationState;
1455 rv = GetAuthorizationMembers(mProxyAuth, scheme, host, port, path, ident,
1456 continuationState);
1457 if (NS_FAILED(rv)) OnAuthCancelled(aContext, false);
1459 nsAutoCString realm;
1460 ParseRealm(mCurrentChallenge, realm);
1462 nsCOMPtr<nsIChannel> chan = do_QueryInterface(mAuthChannel);
1463 nsAutoCString suffix;
1464 if (!mProxyAuth) {
1465 // Fill only for non-proxy auth, proxy credentials are not OA-isolated.
1466 GetOriginAttributesSuffix(chan, suffix);
1469 nsHttpAuthCache* authCache = gHttpHandler->AuthCache(mIsPrivate);
1470 nsHttpAuthEntry* entry = nullptr;
1471 Unused << authCache->GetAuthEntryForDomain(scheme, host, port, realm, suffix,
1472 &entry);
1474 nsCOMPtr<nsISupports> sessionStateGrip;
1475 if (entry) sessionStateGrip = entry->mMetaData;
1477 nsAuthInformationHolder* holder =
1478 static_cast<nsAuthInformationHolder*>(aAuthInfo);
1479 *ident =
1480 nsHttpAuthIdentity(holder->Domain(), holder->User(), holder->Password());
1482 nsAutoCString unused;
1483 nsCOMPtr<nsIHttpAuthenticator> auth;
1484 rv = GetAuthenticator(mCurrentChallenge, unused, getter_AddRefs(auth));
1485 if (NS_FAILED(rv)) {
1486 MOZ_ASSERT(false, "GetAuthenticator failed");
1487 OnAuthCancelled(aContext, true);
1488 return NS_OK;
1491 nsCString creds;
1492 rv = GenCredsAndSetEntry(auth, mProxyAuth, scheme, host, port, path, realm,
1493 mCurrentChallenge, *ident, sessionStateGrip, creds);
1495 mCurrentChallenge.Truncate();
1496 if (NS_FAILED(rv)) {
1497 OnAuthCancelled(aContext, true);
1498 return NS_OK;
1501 return ContinueOnAuthAvailable(creds);
1504 NS_IMETHODIMP nsHttpChannelAuthProvider::OnAuthCancelled(nsISupports* aContext,
1505 bool userCancel) {
1506 LOG(("nsHttpChannelAuthProvider::OnAuthCancelled [this=%p channel=%p]", this,
1507 mAuthChannel));
1509 mAsyncPromptAuthCancelable = nullptr;
1510 if (!mAuthChannel) return NS_OK;
1512 // When user cancels or auth fails we want to close the connection for
1513 // connection based schemes like NTLM. Some servers don't like re-negotiation
1514 // on the same connection.
1515 nsresult rv;
1516 if (mConnectionBased) {
1517 rv = mAuthChannel->CloseStickyConnection();
1518 MOZ_ASSERT(NS_SUCCEEDED(rv));
1519 mConnectionBased = false;
1522 nsCOMPtr<nsIChannel> channel = do_QueryInterface(mAuthChannel);
1523 if (channel) {
1524 nsresult status;
1525 Unused << channel->GetStatus(&status);
1526 if (NS_FAILED(status)) {
1527 // If the channel is already cancelled, there is no need to deal with the
1528 // rest challenges.
1529 LOG((" Clear mRemainingChallenges, since mAuthChannel is cancelled"));
1530 mRemainingChallenges.Truncate();
1534 if (userCancel) {
1535 if (!mRemainingChallenges.IsEmpty()) {
1536 // there are still some challenges to process, do so
1538 // Get rid of current continuationState to avoid reusing it in
1539 // next challenges since it is no longer relevant.
1540 if (mProxyAuth) {
1541 NS_IF_RELEASE(mProxyAuthContinuationState);
1542 } else {
1543 NS_IF_RELEASE(mAuthContinuationState);
1545 nsAutoCString creds;
1546 rv = GetCredentials(mRemainingChallenges, mProxyAuth, creds);
1547 if (NS_SUCCEEDED(rv)) {
1548 // GetCredentials loaded the credentials from the cache or
1549 // some other way in a synchronous manner, process those
1550 // credentials now
1551 mRemainingChallenges.Truncate();
1552 return ContinueOnAuthAvailable(creds);
1554 if (rv == NS_ERROR_IN_PROGRESS) {
1555 // GetCredentials successfully queued another authprompt for
1556 // a challenge from the list, we are now waiting for the user
1557 // to provide the credentials
1558 return NS_OK;
1561 // otherwise, we failed...
1564 mRemainingChallenges.Truncate();
1567 rv = mAuthChannel->OnAuthCancelled(userCancel);
1568 MOZ_ASSERT(NS_SUCCEEDED(rv));
1570 return NS_OK;
1573 NS_IMETHODIMP nsHttpChannelAuthProvider::OnCredsGenerated(
1574 const nsACString& aGeneratedCreds, uint32_t aFlags, nsresult aResult,
1575 nsISupports* aSessionState, nsISupports* aContinuationState) {
1576 nsresult rv;
1578 MOZ_ASSERT(NS_IsMainThread());
1580 // When channel is closed, do not proceed
1581 if (!mAuthChannel) {
1582 return NS_OK;
1585 mGenerateCredentialsCancelable = nullptr;
1587 if (NS_FAILED(aResult)) {
1588 return OnAuthCancelled(nullptr, true);
1591 // We want to update m(Proxy)AuthContinuationState in case it was changed by
1592 // nsHttpNegotiateAuth::GenerateCredentials
1593 nsCOMPtr<nsISupports> contState(aContinuationState);
1594 if (mProxyAuth) {
1595 contState.swap(mProxyAuthContinuationState);
1596 } else {
1597 contState.swap(mAuthContinuationState);
1600 nsCOMPtr<nsIHttpAuthenticator> auth;
1601 nsAutoCString unused;
1602 rv = GetAuthenticator(mCurrentChallenge, unused, getter_AddRefs(auth));
1603 NS_ENSURE_SUCCESS(rv, rv);
1605 nsAutoCString host;
1606 int32_t port;
1607 nsHttpAuthIdentity* ident;
1608 nsAutoCString directory, scheme;
1609 nsISupports** unusedContinuationState;
1611 // Get realm from challenge
1612 nsAutoCString realm;
1613 ParseRealm(mCurrentChallenge, realm);
1615 rv = GetAuthorizationMembers(mProxyAuth, scheme, host, port, directory, ident,
1616 unusedContinuationState);
1617 if (NS_FAILED(rv)) return rv;
1619 rv =
1620 UpdateCache(auth, scheme, host, port, directory, realm, mCurrentChallenge,
1621 *ident, aGeneratedCreds, aFlags, aSessionState, mProxyAuth);
1622 MOZ_ASSERT(NS_SUCCEEDED(rv));
1623 mCurrentChallenge.Truncate();
1625 rv = ContinueOnAuthAvailable(aGeneratedCreds);
1626 MOZ_ASSERT(NS_SUCCEEDED(rv));
1627 return NS_OK;
1630 nsresult nsHttpChannelAuthProvider::ContinueOnAuthAvailable(
1631 const nsACString& creds) {
1632 nsresult rv;
1633 if (mProxyAuth) {
1634 rv = mAuthChannel->SetProxyCredentials(creds);
1635 } else {
1636 rv = mAuthChannel->SetWWWCredentials(creds);
1638 if (NS_FAILED(rv)) return rv;
1640 // drop our remaining list of challenges. We don't need them, because we
1641 // have now authenticated against a challenge and will be sending that
1642 // information to the server (or proxy). If it doesn't accept our
1643 // authentication it'll respond with failure and resend the challenge list
1644 mRemainingChallenges.Truncate();
1646 Unused << mAuthChannel->OnAuthAvailable();
1648 return NS_OK;
1651 bool nsHttpChannelAuthProvider::ConfirmAuth(const char* bundleKey,
1652 bool doYesNoPrompt) {
1653 // skip prompting the user if
1654 // 1) prompts are disabled by pref
1655 // 2) we've already prompted the user
1656 // 3) we're not a toplevel channel
1657 // 4) the userpass length is less than the "phishy" threshold
1659 if (!StaticPrefs::network_auth_confirmAuth_enabled()) {
1660 return true;
1663 uint32_t loadFlags;
1664 nsresult rv = mAuthChannel->GetLoadFlags(&loadFlags);
1665 if (NS_FAILED(rv)) return true;
1667 if (mSuppressDefensiveAuth ||
1668 !(loadFlags & nsIChannel::LOAD_INITIAL_DOCUMENT_URI)) {
1669 return true;
1672 nsAutoCString userPass;
1673 rv = mURI->GetUserPass(userPass);
1674 if (NS_FAILED(rv) ||
1675 (userPass.Length() < gHttpHandler->PhishyUserPassLength())) {
1676 return true;
1679 // we try to confirm by prompting the user. if we cannot do so, then
1680 // assume the user said ok. this is done to keep things working in
1681 // embedded builds, where the string bundle might not be present, etc.
1683 nsCOMPtr<nsIStringBundleService> bundleService =
1684 do_GetService(NS_STRINGBUNDLE_CONTRACTID);
1685 if (!bundleService) return true;
1687 nsCOMPtr<nsIStringBundle> bundle;
1688 bundleService->CreateBundle(NECKO_MSGS_URL, getter_AddRefs(bundle));
1689 if (!bundle) return true;
1691 nsAutoCString host;
1692 rv = mURI->GetHost(host);
1693 if (NS_FAILED(rv)) return true;
1695 nsAutoCString user;
1696 rv = mURI->GetUsername(user);
1697 if (NS_FAILED(rv)) return true;
1699 NS_ConvertUTF8toUTF16 ucsHost(host), ucsUser(user);
1701 size_t userLength = ucsUser.Length();
1702 if (userLength > MAX_DISPLAYED_USER_LENGTH) {
1703 size_t desiredLength = MAX_DISPLAYED_USER_LENGTH;
1704 // Don't cut off right before a low surrogate. Just include it.
1705 if (NS_IS_LOW_SURROGATE(ucsUser[desiredLength])) {
1706 desiredLength++;
1708 ucsUser.Replace(desiredLength, userLength - desiredLength,
1709 nsContentUtils::GetLocalizedEllipsis());
1712 size_t hostLen = ucsHost.Length();
1713 if (hostLen > MAX_DISPLAYED_HOST_LENGTH) {
1714 size_t cutPoint = hostLen - MAX_DISPLAYED_HOST_LENGTH;
1715 // Likewise, don't cut off right before a low surrogate here.
1716 // Keep the low surrogate
1717 if (NS_IS_LOW_SURROGATE(ucsHost[cutPoint])) {
1718 cutPoint--;
1720 // It's possible cutPoint was 1 and is now 0. Only insert the ellipsis
1721 // if we're actually removing anything.
1722 if (cutPoint > 0) {
1723 ucsHost.Replace(0, cutPoint, nsContentUtils::GetLocalizedEllipsis());
1727 AutoTArray<nsString, 2> strs = {ucsHost, ucsUser};
1729 nsAutoString msg;
1730 rv = bundle->FormatStringFromName(bundleKey, strs, msg);
1731 if (NS_FAILED(rv)) return true;
1733 nsCOMPtr<nsIInterfaceRequestor> callbacks;
1734 rv = mAuthChannel->GetNotificationCallbacks(getter_AddRefs(callbacks));
1735 if (NS_FAILED(rv)) return true;
1737 nsCOMPtr<nsILoadGroup> loadGroup;
1738 rv = mAuthChannel->GetLoadGroup(getter_AddRefs(loadGroup));
1739 if (NS_FAILED(rv)) return true;
1741 nsCOMPtr<nsIPromptService> promptSvc =
1742 do_GetService("@mozilla.org/prompter;1", &rv);
1743 if (NS_FAILED(rv) || !promptSvc) {
1744 return true;
1747 // do not prompt again
1748 mSuppressDefensiveAuth = true;
1750 // Get current browsing context to use as prompt parent
1751 nsCOMPtr<nsIChannel> chan = do_QueryInterface(mAuthChannel);
1752 if (!chan) {
1753 return true;
1756 nsCOMPtr<nsILoadInfo> loadInfo = chan->LoadInfo();
1757 RefPtr<mozilla::dom::BrowsingContext> browsingContext;
1758 loadInfo->GetBrowsingContext(getter_AddRefs(browsingContext));
1760 bool confirmed;
1761 if (doYesNoPrompt) {
1762 int32_t choice;
1763 bool checkState = false;
1764 rv = promptSvc->ConfirmExBC(
1765 browsingContext, StaticPrefs::prompts_modalType_confirmAuth(), nullptr,
1766 msg.get(),
1767 nsIPromptService::BUTTON_POS_1_DEFAULT +
1768 nsIPromptService::STD_YES_NO_BUTTONS,
1769 nullptr, nullptr, nullptr, nullptr, &checkState, &choice);
1770 if (NS_FAILED(rv)) return true;
1772 confirmed = choice == 0;
1773 } else {
1774 rv = promptSvc->ConfirmBC(browsingContext,
1775 StaticPrefs::prompts_modalType_confirmAuth(),
1776 nullptr, msg.get(), &confirmed);
1777 if (NS_FAILED(rv)) return true;
1780 return confirmed;
1783 void nsHttpChannelAuthProvider::SetAuthorizationHeader(
1784 nsHttpAuthCache* authCache, const nsHttpAtom& header,
1785 const nsACString& scheme, const nsACString& host, int32_t port,
1786 const nsACString& path, nsHttpAuthIdentity& ident) {
1787 nsHttpAuthEntry* entry = nullptr;
1788 nsresult rv;
1790 // set informations that depend on whether
1791 // we're authenticating against a proxy
1792 // or a webserver
1793 nsISupports** continuationState;
1795 nsAutoCString suffix;
1796 if (header == nsHttp::Proxy_Authorization) {
1797 continuationState = &mProxyAuthContinuationState;
1799 if (mProxyInfo) {
1800 nsAutoCString type;
1801 mProxyInfo->GetType(type);
1802 if (type.EqualsLiteral("https")) {
1803 // Let this be overriden by anything from the cache.
1804 auto const& pa = mProxyInfo->ProxyAuthorizationHeader();
1805 if (!pa.IsEmpty()) {
1806 rv = mAuthChannel->SetProxyCredentials(pa);
1807 MOZ_ASSERT(NS_SUCCEEDED(rv));
1811 } else {
1812 continuationState = &mAuthContinuationState;
1814 nsCOMPtr<nsIChannel> chan = do_QueryInterface(mAuthChannel);
1815 GetOriginAttributesSuffix(chan, suffix);
1818 rv = authCache->GetAuthEntryForPath(scheme, host, port, path, suffix, &entry);
1819 if (NS_SUCCEEDED(rv)) {
1820 // if we are trying to add a header for origin server auth and if the
1821 // URL contains an explicit username, then try the given username first.
1822 // we only want to do this, however, if we know the URL requires auth
1823 // based on the presence of an auth cache entry for this URL (which is
1824 // true since we are here). but, if the username from the URL matches
1825 // the username from the cache, then we should prefer the password
1826 // stored in the cache since that is most likely to be valid.
1827 if (header == nsHttp::Authorization && entry->Domain()[0] == '\0') {
1828 GetIdentityFromURI(0, ident);
1829 // if the usernames match, then clear the ident so we will pick
1830 // up the one from the auth cache instead.
1831 // when this is undesired, specify LOAD_EXPLICIT_CREDENTIALS load
1832 // flag.
1833 if (ident.User() == entry->User()) {
1834 uint32_t loadFlags;
1835 if (NS_SUCCEEDED(mAuthChannel->GetLoadFlags(&loadFlags)) &&
1836 !(loadFlags & nsIChannel::LOAD_EXPLICIT_CREDENTIALS)) {
1837 ident.Clear();
1841 bool identFromURI;
1842 if (ident.IsEmpty()) {
1843 ident = entry->Identity();
1844 identFromURI = false;
1845 } else {
1846 identFromURI = true;
1849 nsCString temp; // this must have the same lifetime as creds
1850 nsAutoCString creds(entry->Creds());
1851 // we can only send a preemptive Authorization header if we have either
1852 // stored credentials or a stored challenge from which to derive
1853 // credentials. if the identity is from the URI, then we cannot use
1854 // the stored credentials.
1855 if ((creds.IsEmpty() || identFromURI) && !entry->Challenge().IsEmpty()) {
1856 nsCOMPtr<nsIHttpAuthenticator> auth;
1857 nsAutoCString unused;
1858 rv = GetAuthenticator(entry->Challenge(), unused, getter_AddRefs(auth));
1859 if (NS_SUCCEEDED(rv)) {
1860 bool proxyAuth = (header == nsHttp::Proxy_Authorization);
1861 rv = GenCredsAndSetEntry(auth, proxyAuth, scheme, host, port, path,
1862 entry->Realm(), entry->Challenge(), ident,
1863 entry->mMetaData, temp);
1864 if (NS_SUCCEEDED(rv)) creds = temp;
1866 // make sure the continuation state is null since we do not
1867 // support mixing preemptive and 'multirequest' authentication.
1868 NS_IF_RELEASE(*continuationState);
1871 if (!creds.IsEmpty()) {
1872 LOG((" adding \"%s\" request header\n", header.get()));
1873 if (header == nsHttp::Proxy_Authorization) {
1874 rv = mAuthChannel->SetProxyCredentials(creds);
1875 MOZ_ASSERT(NS_SUCCEEDED(rv));
1876 } else {
1877 rv = mAuthChannel->SetWWWCredentials(creds);
1878 MOZ_ASSERT(NS_SUCCEEDED(rv));
1881 // suppress defensive auth prompting for this channel since we know
1882 // that we already prompted at least once this session. we only do
1883 // this for non-proxy auth since the URL's userpass is not used for
1884 // proxy auth.
1885 if (header == nsHttp::Authorization) mSuppressDefensiveAuth = true;
1886 } else {
1887 ident.Clear(); // don't remember the identity
1892 nsresult nsHttpChannelAuthProvider::GetCurrentPath(nsACString& path) {
1893 nsresult rv;
1894 nsCOMPtr<nsIURL> url = do_QueryInterface(mURI);
1895 if (url) {
1896 rv = url->GetDirectory(path);
1897 } else {
1898 rv = mURI->GetPathQueryRef(path);
1900 return rv;
1903 NS_IMPL_ISUPPORTS(nsHttpChannelAuthProvider, nsICancelable,
1904 nsIHttpChannelAuthProvider, nsIAuthPromptCallback,
1905 nsIHttpAuthenticatorCallback)
1907 } // namespace mozilla::net