Thu, 15 Jan 2015 21:03:48 +0100
Integrate friendly tips from Tor colleagues to make (or not) 4.5 alpha 3;
This includes removal of overloaded (but unused) methods, and addition of
a overlooked call to DataStruct::SetData(nsISupports, uint32_t, bool.)
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /* vim:set expandtab ts=4 sw=4 sts=4 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 "nsHttpChannelAuthProvider.h"
11 #include "nsNetUtil.h"
12 #include "nsHttpHandler.h"
13 #include "nsIHttpAuthenticator.h"
14 #include "nsIAuthPrompt2.h"
15 #include "nsIAuthPromptProvider.h"
16 #include "nsIInterfaceRequestor.h"
17 #include "nsIInterfaceRequestorUtils.h"
18 #include "nsEscape.h"
19 #include "nsAuthInformationHolder.h"
20 #include "nsIStringBundle.h"
21 #include "nsIPrompt.h"
22 #include "netCore.h"
23 #include "nsIHttpAuthenticableChannel.h"
24 #include "nsIURI.h"
26 namespace mozilla {
27 namespace net {
29 static void
30 GetAppIdAndBrowserStatus(nsIChannel* aChan, uint32_t* aAppId, bool* aInBrowserElem)
31 {
32 nsCOMPtr<nsILoadContext> loadContext;
33 if (aChan) {
34 NS_QueryNotificationCallbacks(aChan, loadContext);
35 }
36 if (!loadContext) {
37 *aAppId = NECKO_NO_APP_ID;
38 *aInBrowserElem = false;
39 } else {
40 loadContext->GetAppId(aAppId);
41 loadContext->GetIsInBrowserElement(aInBrowserElem);
42 }
43 }
45 nsHttpChannelAuthProvider::nsHttpChannelAuthProvider()
46 : mAuthChannel(nullptr)
47 , mIsPrivate(false)
48 , mProxyAuthContinuationState(nullptr)
49 , mAuthContinuationState(nullptr)
50 , mProxyAuth(false)
51 , mTriedProxyAuth(false)
52 , mTriedHostAuth(false)
53 , mSuppressDefensiveAuth(false)
54 , mHttpHandler(gHttpHandler)
55 {
56 }
58 nsHttpChannelAuthProvider::~nsHttpChannelAuthProvider()
59 {
60 MOZ_ASSERT(!mAuthChannel, "Disconnect wasn't called");
61 }
63 NS_IMETHODIMP
64 nsHttpChannelAuthProvider::Init(nsIHttpAuthenticableChannel *channel)
65 {
66 MOZ_ASSERT(channel, "channel expected!");
68 mAuthChannel = channel;
70 nsresult rv = mAuthChannel->GetURI(getter_AddRefs(mURI));
71 if (NS_FAILED(rv)) return rv;
73 mAuthChannel->GetIsSSL(&mUsingSSL);
74 if (NS_FAILED(rv)) return rv;
76 rv = mURI->GetAsciiHost(mHost);
77 if (NS_FAILED(rv)) return rv;
79 // reject the URL if it doesn't specify a host
80 if (mHost.IsEmpty())
81 return NS_ERROR_MALFORMED_URI;
83 rv = mURI->GetPort(&mPort);
84 if (NS_FAILED(rv)) return rv;
86 nsCOMPtr<nsIChannel> bareChannel = do_QueryInterface(channel);
87 mIsPrivate = NS_UsePrivateBrowsing(bareChannel);
89 return NS_OK;
90 }
92 NS_IMETHODIMP
93 nsHttpChannelAuthProvider::ProcessAuthentication(uint32_t httpStatus,
94 bool SSLConnectFailed)
95 {
96 LOG(("nsHttpChannelAuthProvider::ProcessAuthentication "
97 "[this=%p channel=%p code=%u SSLConnectFailed=%d]\n",
98 this, mAuthChannel, httpStatus, SSLConnectFailed));
100 MOZ_ASSERT(mAuthChannel, "Channel not initialized");
102 nsCOMPtr<nsIProxyInfo> proxyInfo;
103 nsresult rv = mAuthChannel->GetProxyInfo(getter_AddRefs(proxyInfo));
104 if (NS_FAILED(rv)) return rv;
105 if (proxyInfo) {
106 mProxyInfo = do_QueryInterface(proxyInfo);
107 if (!mProxyInfo) return NS_ERROR_NO_INTERFACE;
108 }
110 nsAutoCString challenges;
111 mProxyAuth = (httpStatus == 407);
113 rv = PrepareForAuthentication(mProxyAuth);
114 if (NS_FAILED(rv))
115 return rv;
117 if (mProxyAuth) {
118 // only allow a proxy challenge if we have a proxy server configured.
119 // otherwise, we could inadvertently expose the user's proxy
120 // credentials to an origin server. We could attempt to proceed as
121 // if we had received a 401 from the server, but why risk flirting
122 // with trouble? IE similarly rejects 407s when a proxy server is
123 // not configured, so there's no reason not to do the same.
124 if (!UsingHttpProxy()) {
125 LOG(("rejecting 407 when proxy server not configured!\n"));
126 return NS_ERROR_UNEXPECTED;
127 }
128 if (UsingSSL() && !SSLConnectFailed) {
129 // we need to verify that this challenge came from the proxy
130 // server itself, and not some server on the other side of the
131 // SSL tunnel.
132 LOG(("rejecting 407 from origin server!\n"));
133 return NS_ERROR_UNEXPECTED;
134 }
135 rv = mAuthChannel->GetProxyChallenges(challenges);
136 }
137 else
138 rv = mAuthChannel->GetWWWChallenges(challenges);
139 if (NS_FAILED(rv)) return rv;
141 nsAutoCString creds;
142 rv = GetCredentials(challenges.get(), mProxyAuth, creds);
143 if (rv == NS_ERROR_IN_PROGRESS)
144 return rv;
145 if (NS_FAILED(rv))
146 LOG(("unable to authenticate\n"));
147 else {
148 // set the authentication credentials
149 if (mProxyAuth)
150 rv = mAuthChannel->SetProxyCredentials(creds);
151 else
152 rv = mAuthChannel->SetWWWCredentials(creds);
153 }
154 return rv;
155 }
157 NS_IMETHODIMP
158 nsHttpChannelAuthProvider::AddAuthorizationHeaders()
159 {
160 LOG(("nsHttpChannelAuthProvider::AddAuthorizationHeaders? "
161 "[this=%p channel=%p]\n", this, mAuthChannel));
163 MOZ_ASSERT(mAuthChannel, "Channel not initialized");
165 nsCOMPtr<nsIProxyInfo> proxyInfo;
166 nsresult rv = mAuthChannel->GetProxyInfo(getter_AddRefs(proxyInfo));
167 if (NS_FAILED(rv)) return rv;
168 if (proxyInfo) {
169 mProxyInfo = do_QueryInterface(proxyInfo);
170 if (!mProxyInfo) return NS_ERROR_NO_INTERFACE;
171 }
173 uint32_t loadFlags;
174 rv = mAuthChannel->GetLoadFlags(&loadFlags);
175 if (NS_FAILED(rv)) return rv;
177 // this getter never fails
178 nsHttpAuthCache *authCache = gHttpHandler->AuthCache(mIsPrivate);
180 // check if proxy credentials should be sent
181 const char *proxyHost = ProxyHost();
182 if (proxyHost && UsingHttpProxy())
183 SetAuthorizationHeader(authCache, nsHttp::Proxy_Authorization,
184 "http", proxyHost, ProxyPort(),
185 nullptr, // proxy has no path
186 mProxyIdent);
188 if (loadFlags & nsIRequest::LOAD_ANONYMOUS) {
189 LOG(("Skipping Authorization header for anonymous load\n"));
190 return NS_OK;
191 }
193 // check if server credentials should be sent
194 nsAutoCString path, scheme;
195 if (NS_SUCCEEDED(GetCurrentPath(path)) &&
196 NS_SUCCEEDED(mURI->GetScheme(scheme))) {
197 SetAuthorizationHeader(authCache, nsHttp::Authorization,
198 scheme.get(),
199 Host(),
200 Port(),
201 path.get(),
202 mIdent);
203 }
205 return NS_OK;
206 }
208 NS_IMETHODIMP
209 nsHttpChannelAuthProvider::CheckForSuperfluousAuth()
210 {
211 LOG(("nsHttpChannelAuthProvider::CheckForSuperfluousAuth? "
212 "[this=%p channel=%p]\n", this, mAuthChannel));
214 MOZ_ASSERT(mAuthChannel, "Channel not initialized");
216 // we've been called because it has been determined that this channel is
217 // getting loaded without taking the userpass from the URL. if the URL
218 // contained a userpass, then (provided some other conditions are true),
219 // we'll give the user an opportunity to abort the channel as this might be
220 // an attempt to spoof a different site (see bug 232567).
221 if (!ConfirmAuth(NS_LITERAL_STRING("SuperfluousAuth"), true)) {
222 // calling cancel here sets our mStatus and aborts the HTTP
223 // transaction, which prevents OnDataAvailable events.
224 mAuthChannel->Cancel(NS_ERROR_ABORT);
225 return NS_ERROR_ABORT;
226 }
227 return NS_OK;
228 }
230 NS_IMETHODIMP
231 nsHttpChannelAuthProvider::Cancel(nsresult status)
232 {
233 MOZ_ASSERT(mAuthChannel, "Channel not initialized");
235 if (mAsyncPromptAuthCancelable) {
236 mAsyncPromptAuthCancelable->Cancel(status);
237 mAsyncPromptAuthCancelable = nullptr;
238 }
239 return NS_OK;
240 }
242 NS_IMETHODIMP
243 nsHttpChannelAuthProvider::Disconnect(nsresult status)
244 {
245 mAuthChannel = nullptr;
247 if (mAsyncPromptAuthCancelable) {
248 mAsyncPromptAuthCancelable->Cancel(status);
249 mAsyncPromptAuthCancelable = nullptr;
250 }
252 NS_IF_RELEASE(mProxyAuthContinuationState);
253 NS_IF_RELEASE(mAuthContinuationState);
255 return NS_OK;
256 }
258 // buf contains "domain\user"
259 static void
260 ParseUserDomain(char16_t *buf,
261 const char16_t **user,
262 const char16_t **domain)
263 {
264 char16_t *p = buf;
265 while (*p && *p != '\\') ++p;
266 if (!*p)
267 return;
268 *p = '\0';
269 *domain = buf;
270 *user = p + 1;
271 }
273 // helper function for setting identity from raw user:pass
274 static void
275 SetIdent(nsHttpAuthIdentity &ident,
276 uint32_t authFlags,
277 char16_t *userBuf,
278 char16_t *passBuf)
279 {
280 const char16_t *user = userBuf;
281 const char16_t *domain = nullptr;
283 if (authFlags & nsIHttpAuthenticator::IDENTITY_INCLUDES_DOMAIN)
284 ParseUserDomain(userBuf, &user, &domain);
286 ident.Set(domain, user, passBuf);
287 }
289 // helper function for getting an auth prompt from an interface requestor
290 static void
291 GetAuthPrompt(nsIInterfaceRequestor *ifreq, bool proxyAuth,
292 nsIAuthPrompt2 **result)
293 {
294 if (!ifreq)
295 return;
297 uint32_t promptReason;
298 if (proxyAuth)
299 promptReason = nsIAuthPromptProvider::PROMPT_PROXY;
300 else
301 promptReason = nsIAuthPromptProvider::PROMPT_NORMAL;
303 nsCOMPtr<nsIAuthPromptProvider> promptProvider = do_GetInterface(ifreq);
304 if (promptProvider)
305 promptProvider->GetAuthPrompt(promptReason,
306 NS_GET_IID(nsIAuthPrompt2),
307 reinterpret_cast<void**>(result));
308 else
309 NS_QueryAuthPrompt2(ifreq, result);
310 }
312 // generate credentials for the given challenge, and update the auth cache.
313 nsresult
314 nsHttpChannelAuthProvider::GenCredsAndSetEntry(nsIHttpAuthenticator *auth,
315 bool proxyAuth,
316 const char *scheme,
317 const char *host,
318 int32_t port,
319 const char *directory,
320 const char *realm,
321 const char *challenge,
322 const nsHttpAuthIdentity &ident,
323 nsCOMPtr<nsISupports> &sessionState,
324 char **result)
325 {
326 nsresult rv;
327 uint32_t authFlags;
329 rv = auth->GetAuthFlags(&authFlags);
330 if (NS_FAILED(rv)) return rv;
332 nsISupports *ss = sessionState;
334 // set informations that depend on whether
335 // we're authenticating against a proxy
336 // or a webserver
337 nsISupports **continuationState;
339 if (proxyAuth) {
340 continuationState = &mProxyAuthContinuationState;
341 } else {
342 continuationState = &mAuthContinuationState;
343 }
345 uint32_t generateFlags;
346 rv = auth->GenerateCredentials(mAuthChannel,
347 challenge,
348 proxyAuth,
349 ident.Domain(),
350 ident.User(),
351 ident.Password(),
352 &ss,
353 &*continuationState,
354 &generateFlags,
355 result);
357 sessionState.swap(ss);
358 if (NS_FAILED(rv)) return rv;
360 // don't log this in release build since it could contain sensitive info.
361 #ifdef DEBUG
362 LOG(("generated creds: %s\n", *result));
363 #endif
365 // find out if this authenticator allows reuse of credentials and/or
366 // challenge.
367 bool saveCreds =
368 0 != (authFlags & nsIHttpAuthenticator::REUSABLE_CREDENTIALS);
369 bool saveChallenge =
370 0 != (authFlags & nsIHttpAuthenticator::REUSABLE_CHALLENGE);
372 bool saveIdentity =
373 0 == (generateFlags & nsIHttpAuthenticator::USING_INTERNAL_IDENTITY);
375 // this getter never fails
376 nsHttpAuthCache *authCache = gHttpHandler->AuthCache(mIsPrivate);
378 nsCOMPtr<nsIChannel> chan = do_QueryInterface(mAuthChannel);
379 uint32_t appId;
380 bool isInBrowserElement;
381 GetAppIdAndBrowserStatus(chan, &appId, &isInBrowserElement);
383 // create a cache entry. we do this even though we don't yet know that
384 // these credentials are valid b/c we need to avoid prompting the user
385 // more than once in case the credentials are valid.
386 //
387 // if the credentials are not reusable, then we don't bother sticking
388 // them in the auth cache.
389 rv = authCache->SetAuthEntry(scheme, host, port, directory, realm,
390 saveCreds ? *result : nullptr,
391 saveChallenge ? challenge : nullptr,
392 appId, isInBrowserElement,
393 saveIdentity ? &ident : nullptr,
394 sessionState);
395 return rv;
396 }
398 nsresult
399 nsHttpChannelAuthProvider::PrepareForAuthentication(bool proxyAuth)
400 {
401 LOG(("nsHttpChannelAuthProvider::PrepareForAuthentication "
402 "[this=%p channel=%p]\n", this, mAuthChannel));
404 if (!proxyAuth) {
405 // reset the current proxy continuation state because our last
406 // authentication attempt was completed successfully.
407 NS_IF_RELEASE(mProxyAuthContinuationState);
408 LOG((" proxy continuation state has been reset"));
409 }
411 if (!UsingHttpProxy() || mProxyAuthType.IsEmpty())
412 return NS_OK;
414 // We need to remove any Proxy_Authorization header left over from a
415 // non-request based authentication handshake (e.g., for NTLM auth).
417 nsAutoCString contractId;
418 contractId.Assign(NS_HTTP_AUTHENTICATOR_CONTRACTID_PREFIX);
419 contractId.Append(mProxyAuthType);
421 nsresult rv;
422 nsCOMPtr<nsIHttpAuthenticator> precedingAuth =
423 do_GetService(contractId.get(), &rv);
424 if (NS_FAILED(rv))
425 return rv;
427 uint32_t precedingAuthFlags;
428 rv = precedingAuth->GetAuthFlags(&precedingAuthFlags);
429 if (NS_FAILED(rv))
430 return rv;
432 if (!(precedingAuthFlags & nsIHttpAuthenticator::REQUEST_BASED)) {
433 nsAutoCString challenges;
434 rv = mAuthChannel->GetProxyChallenges(challenges);
435 if (NS_FAILED(rv)) {
436 // delete the proxy authorization header because we weren't
437 // asked to authenticate
438 rv = mAuthChannel->SetProxyCredentials(EmptyCString());
439 if (NS_FAILED(rv)) return rv;
440 LOG((" cleared proxy authorization header"));
441 }
442 }
444 return NS_OK;
445 }
447 nsresult
448 nsHttpChannelAuthProvider::GetCredentials(const char *challenges,
449 bool proxyAuth,
450 nsAFlatCString &creds)
451 {
452 nsCOMPtr<nsIHttpAuthenticator> auth;
453 nsAutoCString challenge;
455 nsCString authType; // force heap allocation to enable string sharing since
456 // we'll be assigning this value into mAuthType.
458 // set informations that depend on whether we're authenticating against a
459 // proxy or a webserver
460 nsISupports **currentContinuationState;
461 nsCString *currentAuthType;
463 if (proxyAuth) {
464 currentContinuationState = &mProxyAuthContinuationState;
465 currentAuthType = &mProxyAuthType;
466 } else {
467 currentContinuationState = &mAuthContinuationState;
468 currentAuthType = &mAuthType;
469 }
471 nsresult rv = NS_ERROR_NOT_AVAILABLE;
472 bool gotCreds = false;
474 // figure out which challenge we can handle and which authenticator to use.
475 for (const char *eol = challenges - 1; eol; ) {
476 const char *p = eol + 1;
478 // get the challenge string (LF separated -- see nsHttpHeaderArray)
479 if ((eol = strchr(p, '\n')) != nullptr)
480 challenge.Assign(p, eol - p);
481 else
482 challenge.Assign(p);
484 rv = GetAuthenticator(challenge.get(), authType, getter_AddRefs(auth));
485 if (NS_SUCCEEDED(rv)) {
486 //
487 // if we've already selected an auth type from a previous challenge
488 // received while processing this channel, then skip others until
489 // we find a challenge corresponding to the previously tried auth
490 // type.
491 //
492 if (!currentAuthType->IsEmpty() && authType != *currentAuthType)
493 continue;
495 //
496 // we allow the routines to run all the way through before we
497 // decide if they are valid.
498 //
499 // we don't worry about the auth cache being altered because that
500 // would have been the last step, and if the error is from updating
501 // the authcache it wasn't really altered anyway. -CTN
502 //
503 // at this point the code is really only useful for client side
504 // errors (it will not automatically fail over to do a different
505 // auth type if the server keeps rejecting what is being sent, even
506 // if a particular auth method only knows 1 thing, like a
507 // non-identity based authentication method)
508 //
509 rv = GetCredentialsForChallenge(challenge.get(), authType.get(),
510 proxyAuth, auth, creds);
511 if (NS_SUCCEEDED(rv)) {
512 gotCreds = true;
513 *currentAuthType = authType;
515 break;
516 }
517 else if (rv == NS_ERROR_IN_PROGRESS) {
518 // authentication prompt has been invoked and result is
519 // expected asynchronously, save current challenge being
520 // processed and all remaining challenges to use later in
521 // OnAuthAvailable and now immediately return
522 mCurrentChallenge = challenge;
523 mRemainingChallenges = eol ? eol+1 : nullptr;
524 return rv;
525 }
527 // reset the auth type and continuation state
528 NS_IF_RELEASE(*currentContinuationState);
529 currentAuthType->Truncate();
530 }
531 }
533 if (!gotCreds && !currentAuthType->IsEmpty()) {
534 // looks like we never found the auth type we were looking for.
535 // reset the auth type and continuation state, and try again.
536 currentAuthType->Truncate();
537 NS_IF_RELEASE(*currentContinuationState);
539 rv = GetCredentials(challenges, proxyAuth, creds);
540 }
542 return rv;
543 }
545 nsresult
546 nsHttpChannelAuthProvider::GetAuthorizationMembers(bool proxyAuth,
547 nsCSubstring& scheme,
548 const char*& host,
549 int32_t& port,
550 nsCSubstring& path,
551 nsHttpAuthIdentity*& ident,
552 nsISupports**& continuationState)
553 {
554 if (proxyAuth) {
555 MOZ_ASSERT (UsingHttpProxy(),
556 "proxyAuth is true, but no HTTP proxy is configured!");
558 host = ProxyHost();
559 port = ProxyPort();
560 ident = &mProxyIdent;
561 scheme.AssignLiteral("http");
563 continuationState = &mProxyAuthContinuationState;
564 }
565 else {
566 host = Host();
567 port = Port();
568 ident = &mIdent;
570 nsresult rv;
571 rv = GetCurrentPath(path);
572 if (NS_FAILED(rv)) return rv;
574 rv = mURI->GetScheme(scheme);
575 if (NS_FAILED(rv)) return rv;
577 continuationState = &mAuthContinuationState;
578 }
580 return NS_OK;
581 }
583 nsresult
584 nsHttpChannelAuthProvider::GetCredentialsForChallenge(const char *challenge,
585 const char *authType,
586 bool proxyAuth,
587 nsIHttpAuthenticator *auth,
588 nsAFlatCString &creds)
589 {
590 LOG(("nsHttpChannelAuthProvider::GetCredentialsForChallenge "
591 "[this=%p channel=%p proxyAuth=%d challenges=%s]\n",
592 this, mAuthChannel, proxyAuth, challenge));
594 // this getter never fails
595 nsHttpAuthCache *authCache = gHttpHandler->AuthCache(mIsPrivate);
597 uint32_t authFlags;
598 nsresult rv = auth->GetAuthFlags(&authFlags);
599 if (NS_FAILED(rv)) return rv;
601 nsAutoCString realm;
602 ParseRealm(challenge, realm);
604 // if no realm, then use the auth type as the realm. ToUpperCase so the
605 // ficticious realm stands out a bit more.
606 // XXX this will cause some single signon misses!
607 // XXX this was meant to be used with NTLM, which supplies no realm.
608 /*
609 if (realm.IsEmpty()) {
610 realm = authType;
611 ToUpperCase(realm);
612 }
613 */
615 // set informations that depend on whether
616 // we're authenticating against a proxy
617 // or a webserver
618 const char *host;
619 int32_t port;
620 nsHttpAuthIdentity *ident;
621 nsAutoCString path, scheme;
622 bool identFromURI = false;
623 nsISupports **continuationState;
625 rv = GetAuthorizationMembers(proxyAuth, scheme, host, port,
626 path, ident, continuationState);
627 if (NS_FAILED(rv)) return rv;
629 uint32_t loadFlags;
630 rv = mAuthChannel->GetLoadFlags(&loadFlags);
631 if (NS_FAILED(rv)) return rv;
633 if (!proxyAuth) {
634 // if this is the first challenge, then try using the identity
635 // specified in the URL.
636 if (mIdent.IsEmpty()) {
637 GetIdentityFromURI(authFlags, mIdent);
638 identFromURI = !mIdent.IsEmpty();
639 }
641 if ((loadFlags & nsIRequest::LOAD_ANONYMOUS) && !identFromURI) {
642 LOG(("Skipping authentication for anonymous non-proxy request\n"));
643 return NS_ERROR_NOT_AVAILABLE;
644 }
646 // Let explicit URL credentials pass
647 // regardless of the LOAD_ANONYMOUS flag
648 }
649 else if ((loadFlags & nsIRequest::LOAD_ANONYMOUS) && !UsingHttpProxy()) {
650 LOG(("Skipping authentication for anonymous non-proxy request\n"));
651 return NS_ERROR_NOT_AVAILABLE;
652 }
654 nsCOMPtr<nsIChannel> chan = do_QueryInterface(mAuthChannel);
655 uint32_t appId;
656 bool isInBrowserElement;
657 GetAppIdAndBrowserStatus(chan, &appId, &isInBrowserElement);
659 //
660 // if we already tried some credentials for this transaction, then
661 // we need to possibly clear them from the cache, unless the credentials
662 // in the cache have changed, in which case we'd want to give them a
663 // try instead.
664 //
665 nsHttpAuthEntry *entry = nullptr;
666 authCache->GetAuthEntryForDomain(scheme.get(), host, port,
667 realm.get(), appId,
668 isInBrowserElement, &entry);
670 // hold reference to the auth session state (in case we clear our
671 // reference to the entry).
672 nsCOMPtr<nsISupports> sessionStateGrip;
673 if (entry)
674 sessionStateGrip = entry->mMetaData;
676 // for digest auth, maybe our cached nonce value simply timed out...
677 bool identityInvalid;
678 nsISupports *sessionState = sessionStateGrip;
679 rv = auth->ChallengeReceived(mAuthChannel,
680 challenge,
681 proxyAuth,
682 &sessionState,
683 &*continuationState,
684 &identityInvalid);
685 sessionStateGrip.swap(sessionState);
686 if (NS_FAILED(rv)) return rv;
688 LOG((" identity invalid = %d\n", identityInvalid));
690 if (identityInvalid) {
691 if (entry) {
692 if (ident->Equals(entry->Identity())) {
693 if (!identFromURI) {
694 LOG((" clearing bad auth cache entry\n"));
695 // ok, we've already tried this user identity, so clear the
696 // corresponding entry from the auth cache.
697 authCache->ClearAuthEntry(scheme.get(), host,
698 port, realm.get(),
699 appId, isInBrowserElement);
700 entry = nullptr;
701 ident->Clear();
702 }
703 }
704 else if (!identFromURI ||
705 (nsCRT::strcmp(ident->User(),
706 entry->Identity().User()) == 0 &&
707 !(loadFlags &
708 (nsIChannel::LOAD_ANONYMOUS |
709 nsIChannel::LOAD_EXPLICIT_CREDENTIALS)))) {
710 LOG((" taking identity from auth cache\n"));
711 // the password from the auth cache is more likely to be
712 // correct than the one in the URL. at least, we know that it
713 // works with the given username. it is possible for a server
714 // to distinguish logons based on the supplied password alone,
715 // but that would be quite unusual... and i don't think we need
716 // to worry about such unorthodox cases.
717 ident->Set(entry->Identity());
718 identFromURI = false;
719 if (entry->Creds()[0] != '\0') {
720 LOG((" using cached credentials!\n"));
721 creds.Assign(entry->Creds());
722 return entry->AddPath(path.get());
723 }
724 }
725 }
726 else if (!identFromURI) {
727 // hmm... identity invalid, but no auth entry! the realm probably
728 // changed (see bug 201986).
729 ident->Clear();
730 }
732 if (!entry && ident->IsEmpty()) {
733 uint32_t level = nsIAuthPrompt2::LEVEL_NONE;
734 if (mUsingSSL)
735 level = nsIAuthPrompt2::LEVEL_SECURE;
736 else if (authFlags & nsIHttpAuthenticator::IDENTITY_ENCRYPTED)
737 level = nsIAuthPrompt2::LEVEL_PW_ENCRYPTED;
739 // at this point we are forced to interact with the user to get
740 // their username and password for this domain.
741 rv = PromptForIdentity(level, proxyAuth, realm.get(),
742 authType, authFlags, *ident);
743 if (NS_FAILED(rv)) return rv;
744 identFromURI = false;
745 }
746 }
748 if (identFromURI) {
749 // Warn the user before automatically using the identity from the URL
750 // to automatically log them into a site (see bug 232567).
751 if (!ConfirmAuth(NS_LITERAL_STRING("AutomaticAuth"), false)) {
752 // calling cancel here sets our mStatus and aborts the HTTP
753 // transaction, which prevents OnDataAvailable events.
754 mAuthChannel->Cancel(NS_ERROR_ABORT);
755 // this return code alone is not equivalent to Cancel, since
756 // it only instructs our caller that authentication failed.
757 // without an explicit call to Cancel, our caller would just
758 // load the page that accompanies the HTTP auth challenge.
759 return NS_ERROR_ABORT;
760 }
761 }
763 //
764 // get credentials for the given user:pass
765 //
766 // always store the credentials we're trying now so that they will be used
767 // on subsequent links. This will potentially remove good credentials from
768 // the cache. This is ok as we don't want to use cached credentials if the
769 // user specified something on the URI or in another manner. This is so
770 // that we don't transparently authenticate as someone they're not
771 // expecting to authenticate as.
772 //
773 nsXPIDLCString result;
774 rv = GenCredsAndSetEntry(auth, proxyAuth, scheme.get(), host, port,
775 path.get(), realm.get(), challenge, *ident,
776 sessionStateGrip, getter_Copies(result));
777 if (NS_SUCCEEDED(rv))
778 creds = result;
779 return rv;
780 }
782 inline void
783 GetAuthType(const char *challenge, nsCString &authType)
784 {
785 const char *p;
787 // get the challenge type
788 if ((p = strchr(challenge, ' ')) != nullptr)
789 authType.Assign(challenge, p - challenge);
790 else
791 authType.Assign(challenge);
792 }
794 nsresult
795 nsHttpChannelAuthProvider::GetAuthenticator(const char *challenge,
796 nsCString &authType,
797 nsIHttpAuthenticator **auth)
798 {
799 LOG(("nsHttpChannelAuthProvider::GetAuthenticator [this=%p channel=%p]\n",
800 this, mAuthChannel));
802 GetAuthType(challenge, authType);
804 // normalize to lowercase
805 ToLowerCase(authType);
807 nsAutoCString contractid;
808 contractid.Assign(NS_HTTP_AUTHENTICATOR_CONTRACTID_PREFIX);
809 contractid.Append(authType);
811 return CallGetService(contractid.get(), auth);
812 }
814 void
815 nsHttpChannelAuthProvider::GetIdentityFromURI(uint32_t authFlags,
816 nsHttpAuthIdentity &ident)
817 {
818 LOG(("nsHttpChannelAuthProvider::GetIdentityFromURI [this=%p channel=%p]\n",
819 this, mAuthChannel));
821 nsAutoString userBuf;
822 nsAutoString passBuf;
824 // XXX i18n
825 nsAutoCString buf;
826 mURI->GetUsername(buf);
827 if (!buf.IsEmpty()) {
828 NS_UnescapeURL(buf);
829 CopyASCIItoUTF16(buf, userBuf);
830 mURI->GetPassword(buf);
831 if (!buf.IsEmpty()) {
832 NS_UnescapeURL(buf);
833 CopyASCIItoUTF16(buf, passBuf);
834 }
835 }
837 if (!userBuf.IsEmpty()) {
838 SetIdent(ident, authFlags, (char16_t *) userBuf.get(),
839 (char16_t *) passBuf.get());
840 }
841 }
843 void
844 nsHttpChannelAuthProvider::ParseRealm(const char *challenge,
845 nsACString &realm)
846 {
847 //
848 // From RFC2617 section 1.2, the realm value is defined as such:
849 //
850 // realm = "realm" "=" realm-value
851 // realm-value = quoted-string
852 //
853 // but, we'll accept anything after the the "=" up to the first space, or
854 // end-of-line, if the string is not quoted.
855 //
857 const char *p = PL_strcasestr(challenge, "realm=");
858 if (p) {
859 bool has_quote = false;
860 p += 6;
861 if (*p == '"') {
862 has_quote = true;
863 p++;
864 }
866 const char *end;
867 if (has_quote) {
868 end = p;
869 while (*end) {
870 if (*end == '\\') {
871 // escaped character, store that one instead if not zero
872 if (!*++end)
873 break;
874 }
875 else if (*end == '\"')
876 // end of string
877 break;
879 realm.Append(*end);
880 ++end;
881 }
882 }
883 else {
884 // realm given without quotes
885 end = strchr(p, ' ');
886 if (end)
887 realm.Assign(p, end - p);
888 else
889 realm.Assign(p);
890 }
891 }
892 }
895 class nsHTTPAuthInformation : public nsAuthInformationHolder {
896 public:
897 nsHTTPAuthInformation(uint32_t aFlags, const nsString& aRealm,
898 const nsCString& aAuthType)
899 : nsAuthInformationHolder(aFlags, aRealm, aAuthType) {}
901 void SetToHttpAuthIdentity(uint32_t authFlags,
902 nsHttpAuthIdentity& identity);
903 };
905 void
906 nsHTTPAuthInformation::SetToHttpAuthIdentity(uint32_t authFlags,
907 nsHttpAuthIdentity& identity)
908 {
909 identity.Set(Domain().get(), User().get(), Password().get());
910 }
912 nsresult
913 nsHttpChannelAuthProvider::PromptForIdentity(uint32_t level,
914 bool proxyAuth,
915 const char *realm,
916 const char *authType,
917 uint32_t authFlags,
918 nsHttpAuthIdentity &ident)
919 {
920 LOG(("nsHttpChannelAuthProvider::PromptForIdentity [this=%p channel=%p]\n",
921 this, mAuthChannel));
923 nsresult rv;
925 nsCOMPtr<nsIInterfaceRequestor> callbacks;
926 rv = mAuthChannel->GetNotificationCallbacks(getter_AddRefs(callbacks));
927 if (NS_FAILED(rv)) return rv;
929 nsCOMPtr<nsILoadGroup> loadGroup;
930 rv = mAuthChannel->GetLoadGroup(getter_AddRefs(loadGroup));
931 if (NS_FAILED(rv)) return rv;
933 nsCOMPtr<nsIAuthPrompt2> authPrompt;
934 GetAuthPrompt(callbacks, proxyAuth, getter_AddRefs(authPrompt));
935 if (!authPrompt && loadGroup) {
936 nsCOMPtr<nsIInterfaceRequestor> cbs;
937 loadGroup->GetNotificationCallbacks(getter_AddRefs(cbs));
938 GetAuthPrompt(cbs, proxyAuth, getter_AddRefs(authPrompt));
939 }
940 if (!authPrompt)
941 return NS_ERROR_NO_INTERFACE;
943 // XXX i18n: need to support non-ASCII realm strings (see bug 41489)
944 NS_ConvertASCIItoUTF16 realmU(realm);
946 // prompt the user...
947 uint32_t promptFlags = 0;
948 if (proxyAuth)
949 {
950 promptFlags |= nsIAuthInformation::AUTH_PROXY;
951 if (mTriedProxyAuth)
952 promptFlags |= nsIAuthInformation::PREVIOUS_FAILED;
953 mTriedProxyAuth = true;
954 }
955 else {
956 promptFlags |= nsIAuthInformation::AUTH_HOST;
957 if (mTriedHostAuth)
958 promptFlags |= nsIAuthInformation::PREVIOUS_FAILED;
959 mTriedHostAuth = true;
960 }
962 if (authFlags & nsIHttpAuthenticator::IDENTITY_INCLUDES_DOMAIN)
963 promptFlags |= nsIAuthInformation::NEED_DOMAIN;
965 nsRefPtr<nsHTTPAuthInformation> holder =
966 new nsHTTPAuthInformation(promptFlags, realmU,
967 nsDependentCString(authType));
968 if (!holder)
969 return NS_ERROR_OUT_OF_MEMORY;
971 nsCOMPtr<nsIChannel> channel(do_QueryInterface(mAuthChannel, &rv));
972 if (NS_FAILED(rv)) return rv;
974 rv =
975 authPrompt->AsyncPromptAuth(channel, this, nullptr, level, holder,
976 getter_AddRefs(mAsyncPromptAuthCancelable));
978 if (NS_SUCCEEDED(rv)) {
979 // indicate using this error code that authentication prompt
980 // result is expected asynchronously
981 rv = NS_ERROR_IN_PROGRESS;
982 }
983 else {
984 // Fall back to synchronous prompt
985 bool retval = false;
986 rv = authPrompt->PromptAuth(channel, level, holder, &retval);
987 if (NS_FAILED(rv))
988 return rv;
990 if (!retval)
991 rv = NS_ERROR_ABORT;
992 else
993 holder->SetToHttpAuthIdentity(authFlags, ident);
994 }
996 // remember that we successfully showed the user an auth dialog
997 if (!proxyAuth)
998 mSuppressDefensiveAuth = true;
1000 return rv;
1001 }
1003 NS_IMETHODIMP nsHttpChannelAuthProvider::OnAuthAvailable(nsISupports *aContext,
1004 nsIAuthInformation *aAuthInfo)
1005 {
1006 LOG(("nsHttpChannelAuthProvider::OnAuthAvailable [this=%p channel=%p]",
1007 this, mAuthChannel));
1009 mAsyncPromptAuthCancelable = nullptr;
1010 if (!mAuthChannel)
1011 return NS_OK;
1013 nsresult rv;
1015 const char *host;
1016 int32_t port;
1017 nsHttpAuthIdentity *ident;
1018 nsAutoCString path, scheme;
1019 nsISupports **continuationState;
1020 rv = GetAuthorizationMembers(mProxyAuth, scheme, host, port,
1021 path, ident, continuationState);
1022 if (NS_FAILED(rv))
1023 OnAuthCancelled(aContext, false);
1025 nsAutoCString realm;
1026 ParseRealm(mCurrentChallenge.get(), realm);
1028 nsCOMPtr<nsIChannel> chan = do_QueryInterface(mAuthChannel);
1029 uint32_t appId;
1030 bool isInBrowserElement;
1031 GetAppIdAndBrowserStatus(chan, &appId, &isInBrowserElement);
1033 nsHttpAuthCache *authCache = gHttpHandler->AuthCache(mIsPrivate);
1034 nsHttpAuthEntry *entry = nullptr;
1035 authCache->GetAuthEntryForDomain(scheme.get(), host, port,
1036 realm.get(), appId,
1037 isInBrowserElement,
1038 &entry);
1040 nsCOMPtr<nsISupports> sessionStateGrip;
1041 if (entry)
1042 sessionStateGrip = entry->mMetaData;
1044 nsAuthInformationHolder* holder =
1045 static_cast<nsAuthInformationHolder*>(aAuthInfo);
1046 ident->Set(holder->Domain().get(),
1047 holder->User().get(),
1048 holder->Password().get());
1050 nsAutoCString unused;
1051 nsCOMPtr<nsIHttpAuthenticator> auth;
1052 rv = GetAuthenticator(mCurrentChallenge.get(), unused,
1053 getter_AddRefs(auth));
1054 if (NS_FAILED(rv)) {
1055 MOZ_ASSERT(false, "GetAuthenticator failed");
1056 OnAuthCancelled(aContext, true);
1057 return NS_OK;
1058 }
1060 nsXPIDLCString creds;
1061 rv = GenCredsAndSetEntry(auth, mProxyAuth,
1062 scheme.get(), host, port, path.get(),
1063 realm.get(), mCurrentChallenge.get(), *ident,
1064 sessionStateGrip, getter_Copies(creds));
1066 mCurrentChallenge.Truncate();
1067 if (NS_FAILED(rv)) {
1068 OnAuthCancelled(aContext, true);
1069 return NS_OK;
1070 }
1072 return ContinueOnAuthAvailable(creds);
1073 }
1075 NS_IMETHODIMP nsHttpChannelAuthProvider::OnAuthCancelled(nsISupports *aContext,
1076 bool userCancel)
1077 {
1078 LOG(("nsHttpChannelAuthProvider::OnAuthCancelled [this=%p channel=%p]",
1079 this, mAuthChannel));
1081 mAsyncPromptAuthCancelable = nullptr;
1082 if (!mAuthChannel)
1083 return NS_OK;
1085 if (userCancel) {
1086 if (!mRemainingChallenges.IsEmpty()) {
1087 // there are still some challenges to process, do so
1088 nsresult rv;
1090 nsAutoCString creds;
1091 rv = GetCredentials(mRemainingChallenges.get(), mProxyAuth, creds);
1092 if (NS_SUCCEEDED(rv)) {
1093 // GetCredentials loaded the credentials from the cache or
1094 // some other way in a synchronous manner, process those
1095 // credentials now
1096 mRemainingChallenges.Truncate();
1097 return ContinueOnAuthAvailable(creds);
1098 }
1099 else if (rv == NS_ERROR_IN_PROGRESS) {
1100 // GetCredentials successfully queued another authprompt for
1101 // a challenge from the list, we are now waiting for the user
1102 // to provide the credentials
1103 return NS_OK;
1104 }
1106 // otherwise, we failed...
1107 }
1109 mRemainingChallenges.Truncate();
1110 }
1112 mAuthChannel->OnAuthCancelled(userCancel);
1114 return NS_OK;
1115 }
1117 nsresult
1118 nsHttpChannelAuthProvider::ContinueOnAuthAvailable(const nsCSubstring& creds)
1119 {
1120 nsresult rv;
1121 if (mProxyAuth)
1122 rv = mAuthChannel->SetProxyCredentials(creds);
1123 else
1124 rv = mAuthChannel->SetWWWCredentials(creds);
1125 if (NS_FAILED(rv)) return rv;
1127 // drop our remaining list of challenges. We don't need them, because we
1128 // have now authenticated against a challenge and will be sending that
1129 // information to the server (or proxy). If it doesn't accept our
1130 // authentication it'll respond with failure and resend the challenge list
1131 mRemainingChallenges.Truncate();
1133 mAuthChannel->OnAuthAvailable();
1135 return NS_OK;
1136 }
1138 bool
1139 nsHttpChannelAuthProvider::ConfirmAuth(const nsString &bundleKey,
1140 bool doYesNoPrompt)
1141 {
1142 // skip prompting the user if
1143 // 1) we've already prompted the user
1144 // 2) we're not a toplevel channel
1145 // 3) the userpass length is less than the "phishy" threshold
1147 uint32_t loadFlags;
1148 nsresult rv = mAuthChannel->GetLoadFlags(&loadFlags);
1149 if (NS_FAILED(rv))
1150 return true;
1152 if (mSuppressDefensiveAuth ||
1153 !(loadFlags & nsIChannel::LOAD_INITIAL_DOCUMENT_URI))
1154 return true;
1156 nsAutoCString userPass;
1157 rv = mURI->GetUserPass(userPass);
1158 if (NS_FAILED(rv) ||
1159 (userPass.Length() < gHttpHandler->PhishyUserPassLength()))
1160 return true;
1162 // we try to confirm by prompting the user. if we cannot do so, then
1163 // assume the user said ok. this is done to keep things working in
1164 // embedded builds, where the string bundle might not be present, etc.
1166 nsCOMPtr<nsIStringBundleService> bundleService =
1167 do_GetService(NS_STRINGBUNDLE_CONTRACTID);
1168 if (!bundleService)
1169 return true;
1171 nsCOMPtr<nsIStringBundle> bundle;
1172 bundleService->CreateBundle(NECKO_MSGS_URL, getter_AddRefs(bundle));
1173 if (!bundle)
1174 return true;
1176 nsAutoCString host;
1177 rv = mURI->GetHost(host);
1178 if (NS_FAILED(rv))
1179 return true;
1181 nsAutoCString user;
1182 rv = mURI->GetUsername(user);
1183 if (NS_FAILED(rv))
1184 return true;
1186 NS_ConvertUTF8toUTF16 ucsHost(host), ucsUser(user);
1187 const char16_t *strs[2] = { ucsHost.get(), ucsUser.get() };
1189 nsXPIDLString msg;
1190 bundle->FormatStringFromName(bundleKey.get(), strs, 2, getter_Copies(msg));
1191 if (!msg)
1192 return true;
1194 nsCOMPtr<nsIInterfaceRequestor> callbacks;
1195 rv = mAuthChannel->GetNotificationCallbacks(getter_AddRefs(callbacks));
1196 if (NS_FAILED(rv))
1197 return true;
1199 nsCOMPtr<nsILoadGroup> loadGroup;
1200 rv = mAuthChannel->GetLoadGroup(getter_AddRefs(loadGroup));
1201 if (NS_FAILED(rv))
1202 return true;
1204 nsCOMPtr<nsIPrompt> prompt;
1205 NS_QueryNotificationCallbacks(callbacks, loadGroup, NS_GET_IID(nsIPrompt),
1206 getter_AddRefs(prompt));
1207 if (!prompt)
1208 return true;
1210 // do not prompt again
1211 mSuppressDefensiveAuth = true;
1213 bool confirmed;
1214 if (doYesNoPrompt) {
1215 int32_t choice;
1216 bool checkState = false;
1217 rv = prompt->ConfirmEx(nullptr, msg,
1218 nsIPrompt::BUTTON_POS_1_DEFAULT +
1219 nsIPrompt::STD_YES_NO_BUTTONS,
1220 nullptr, nullptr, nullptr, nullptr,
1221 &checkState, &choice);
1222 if (NS_FAILED(rv))
1223 return true;
1225 confirmed = choice == 0;
1226 }
1227 else {
1228 rv = prompt->Confirm(nullptr, msg, &confirmed);
1229 if (NS_FAILED(rv))
1230 return true;
1231 }
1233 return confirmed;
1234 }
1236 void
1237 nsHttpChannelAuthProvider::SetAuthorizationHeader(nsHttpAuthCache *authCache,
1238 nsHttpAtom header,
1239 const char *scheme,
1240 const char *host,
1241 int32_t port,
1242 const char *path,
1243 nsHttpAuthIdentity &ident)
1244 {
1245 nsHttpAuthEntry *entry = nullptr;
1246 nsresult rv;
1248 // set informations that depend on whether
1249 // we're authenticating against a proxy
1250 // or a webserver
1251 nsISupports **continuationState;
1253 if (header == nsHttp::Proxy_Authorization) {
1254 continuationState = &mProxyAuthContinuationState;
1255 } else {
1256 continuationState = &mAuthContinuationState;
1257 }
1259 nsCOMPtr<nsIChannel> chan = do_QueryInterface(mAuthChannel);
1260 uint32_t appId;
1261 bool isInBrowserElement;
1262 GetAppIdAndBrowserStatus(chan, &appId, &isInBrowserElement);
1264 rv = authCache->GetAuthEntryForPath(scheme, host, port, path,
1265 appId, isInBrowserElement, &entry);
1266 if (NS_SUCCEEDED(rv)) {
1267 // if we are trying to add a header for origin server auth and if the
1268 // URL contains an explicit username, then try the given username first.
1269 // we only want to do this, however, if we know the URL requires auth
1270 // based on the presence of an auth cache entry for this URL (which is
1271 // true since we are here). but, if the username from the URL matches
1272 // the username from the cache, then we should prefer the password
1273 // stored in the cache since that is most likely to be valid.
1274 if (header == nsHttp::Authorization && entry->Domain()[0] == '\0') {
1275 GetIdentityFromURI(0, ident);
1276 // if the usernames match, then clear the ident so we will pick
1277 // up the one from the auth cache instead.
1278 // when this is undesired, specify LOAD_EXPLICIT_CREDENTIALS load
1279 // flag.
1280 if (nsCRT::strcmp(ident.User(), entry->User()) == 0) {
1281 uint32_t loadFlags;
1282 if (NS_SUCCEEDED(mAuthChannel->GetLoadFlags(&loadFlags)) &&
1283 !(loadFlags & nsIChannel::LOAD_EXPLICIT_CREDENTIALS)) {
1284 ident.Clear();
1285 }
1286 }
1287 }
1288 bool identFromURI;
1289 if (ident.IsEmpty()) {
1290 ident.Set(entry->Identity());
1291 identFromURI = false;
1292 }
1293 else
1294 identFromURI = true;
1296 nsXPIDLCString temp;
1297 const char *creds = entry->Creds();
1298 const char *challenge = entry->Challenge();
1299 // we can only send a preemptive Authorization header if we have either
1300 // stored credentials or a stored challenge from which to derive
1301 // credentials. if the identity is from the URI, then we cannot use
1302 // the stored credentials.
1303 if ((!creds[0] || identFromURI) && challenge[0]) {
1304 nsCOMPtr<nsIHttpAuthenticator> auth;
1305 nsAutoCString unused;
1306 rv = GetAuthenticator(challenge, unused, getter_AddRefs(auth));
1307 if (NS_SUCCEEDED(rv)) {
1308 bool proxyAuth = (header == nsHttp::Proxy_Authorization);
1309 rv = GenCredsAndSetEntry(auth, proxyAuth, scheme, host, port,
1310 path, entry->Realm(), challenge, ident,
1311 entry->mMetaData, getter_Copies(temp));
1312 if (NS_SUCCEEDED(rv))
1313 creds = temp.get();
1315 // make sure the continuation state is null since we do not
1316 // support mixing preemptive and 'multirequest' authentication.
1317 NS_IF_RELEASE(*continuationState);
1318 }
1319 }
1320 if (creds[0]) {
1321 LOG((" adding \"%s\" request header\n", header.get()));
1322 if (header == nsHttp::Proxy_Authorization)
1323 mAuthChannel->SetProxyCredentials(nsDependentCString(creds));
1324 else
1325 mAuthChannel->SetWWWCredentials(nsDependentCString(creds));
1327 // suppress defensive auth prompting for this channel since we know
1328 // that we already prompted at least once this session. we only do
1329 // this for non-proxy auth since the URL's userpass is not used for
1330 // proxy auth.
1331 if (header == nsHttp::Authorization)
1332 mSuppressDefensiveAuth = true;
1333 }
1334 else
1335 ident.Clear(); // don't remember the identity
1336 }
1337 }
1339 nsresult
1340 nsHttpChannelAuthProvider::GetCurrentPath(nsACString &path)
1341 {
1342 nsresult rv;
1343 nsCOMPtr<nsIURL> url = do_QueryInterface(mURI);
1344 if (url)
1345 rv = url->GetDirectory(path);
1346 else
1347 rv = mURI->GetPath(path);
1348 return rv;
1349 }
1351 NS_IMPL_ISUPPORTS(nsHttpChannelAuthProvider, nsICancelable,
1352 nsIHttpChannelAuthProvider, nsIAuthPromptCallback)
1354 } // namespace mozilla::net
1355 } // namespace mozilla