LCOV - code coverage report
Current view: top level - netwerk/protocol/http - nsHttpChannelAuthProvider.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 102 756 13.5 %
Date: 2017-07-14 16:53:18 Functions: 16 40 40.0 %
Legend: Lines: hit not hit

          Line data    Source code
       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/. */
       6             : 
       7             : // HttpLog.h should generally be included first
       8             : #include "HttpLog.h"
       9             : 
      10             : #include "mozilla/Preferences.h"
      11             : #include "nsHttpChannelAuthProvider.h"
      12             : #include "nsNetUtil.h"
      13             : #include "nsHttpHandler.h"
      14             : #include "nsIHttpAuthenticator.h"
      15             : #include "nsIHttpChannelInternal.h"
      16             : #include "nsIAuthPrompt2.h"
      17             : #include "nsIAuthPromptProvider.h"
      18             : #include "nsIInterfaceRequestor.h"
      19             : #include "nsIInterfaceRequestorUtils.h"
      20             : #include "nsEscape.h"
      21             : #include "nsAuthInformationHolder.h"
      22             : #include "nsIStringBundle.h"
      23             : #include "nsIPrompt.h"
      24             : #include "netCore.h"
      25             : #include "nsIHttpAuthenticableChannel.h"
      26             : #include "nsIURI.h"
      27             : #include "nsContentUtils.h"
      28             : #include "nsServiceManagerUtils.h"
      29             : #include "nsILoadContext.h"
      30             : #include "nsIURL.h"
      31             : #include "mozilla/Telemetry.h"
      32             : #include "nsIProxiedChannel.h"
      33             : #include "nsIProxyInfo.h"
      34             : 
      35             : namespace mozilla {
      36             : namespace net {
      37             : 
      38             : #define SUBRESOURCE_AUTH_DIALOG_DISALLOW_ALL 0
      39             : #define SUBRESOURCE_AUTH_DIALOG_DISALLOW_CROSS_ORIGIN 1
      40             : #define SUBRESOURCE_AUTH_DIALOG_ALLOW_ALL 2
      41             : 
      42             : #define HTTP_AUTH_DIALOG_TOP_LEVEL_DOC 29
      43             : #define HTTP_AUTH_DIALOG_SAME_ORIGIN_SUBRESOURCE 30
      44             : #define HTTP_AUTH_DIALOG_SAME_ORIGIN_XHR 31
      45             : 
      46             : #define HTTP_AUTH_BASIC_INSECURE 0
      47             : #define HTTP_AUTH_BASIC_SECURE 1
      48             : #define HTTP_AUTH_DIGEST_INSECURE 2
      49             : #define HTTP_AUTH_DIGEST_SECURE 3
      50             : #define HTTP_AUTH_NTLM_INSECURE 4
      51             : #define HTTP_AUTH_NTLM_SECURE 5
      52             : #define HTTP_AUTH_NEGOTIATE_INSECURE 6
      53             : #define HTTP_AUTH_NEGOTIATE_SECURE 7
      54             : 
      55             : #define MAX_DISPLAYED_USER_LENGTH 64
      56             : #define MAX_DISPLAYED_HOST_LENGTH 64
      57             : 
      58             : static void
      59           6 : GetOriginAttributesSuffix(nsIChannel* aChan, nsACString &aSuffix)
      60             : {
      61          12 :     OriginAttributes oa;
      62             : 
      63             :     // Deliberately ignoring the result and going with defaults
      64           6 :     if (aChan) {
      65           6 :         NS_GetOriginAttributes(aChan, oa);
      66             :     }
      67             : 
      68           6 :     oa.CreateSuffix(aSuffix);
      69           6 : }
      70             : 
      71           6 : nsHttpChannelAuthProvider::nsHttpChannelAuthProvider()
      72             :     : mAuthChannel(nullptr)
      73             :     , mPort(-1)
      74             :     , mUsingSSL(false)
      75             :     , mProxyUsingSSL(false)
      76             :     , mIsPrivate(false)
      77             :     , mProxyAuthContinuationState(nullptr)
      78             :     , mAuthContinuationState(nullptr)
      79             :     , mProxyAuth(false)
      80             :     , mTriedProxyAuth(false)
      81             :     , mTriedHostAuth(false)
      82             :     , mSuppressDefensiveAuth(false)
      83             :     , mCrossOrigin(false)
      84             :     , mConnectionBased(false)
      85           6 :     , mHttpHandler(gHttpHandler)
      86             : {
      87           6 : }
      88             : 
      89          18 : nsHttpChannelAuthProvider::~nsHttpChannelAuthProvider()
      90             : {
      91           6 :     MOZ_ASSERT(!mAuthChannel, "Disconnect wasn't called");
      92          18 : }
      93             : 
      94             : uint32_t nsHttpChannelAuthProvider::sAuthAllowPref =
      95             :     SUBRESOURCE_AUTH_DIALOG_ALLOW_ALL;
      96             : 
      97             : bool nsHttpChannelAuthProvider::sImgCrossOriginAuthAllowPref = true;
      98             : 
      99             : void
     100           2 : nsHttpChannelAuthProvider::InitializePrefs()
     101             : {
     102           2 :   MOZ_ASSERT(NS_IsMainThread());
     103             :   mozilla::Preferences::AddUintVarCache(&sAuthAllowPref,
     104             :                                         "network.auth.subresource-http-auth-allow",
     105           2 :                                         SUBRESOURCE_AUTH_DIALOG_ALLOW_ALL);
     106             :   mozilla::Preferences::AddBoolVarCache(&sImgCrossOriginAuthAllowPref,
     107             :                                         "network.auth.subresource-img-cross-origin-http-auth-allow",
     108           2 :                                         true);
     109           2 : }
     110             : 
     111             : NS_IMETHODIMP
     112           6 : nsHttpChannelAuthProvider::Init(nsIHttpAuthenticableChannel *channel)
     113             : {
     114           6 :     MOZ_ASSERT(channel, "channel expected!");
     115             : 
     116           6 :     mAuthChannel = channel;
     117             : 
     118           6 :     nsresult rv = mAuthChannel->GetURI(getter_AddRefs(mURI));
     119           6 :     if (NS_FAILED(rv)) return rv;
     120             : 
     121           6 :     rv = mAuthChannel->GetIsSSL(&mUsingSSL);
     122           6 :     if (NS_FAILED(rv)) return rv;
     123             : 
     124          12 :     nsCOMPtr<nsIProxiedChannel> proxied(do_QueryInterface(channel));
     125           6 :     if (proxied) {
     126          12 :         nsCOMPtr<nsIProxyInfo> pi;
     127           6 :         rv = proxied->GetProxyInfo(getter_AddRefs(pi));
     128           6 :         if (NS_FAILED(rv)) return rv;
     129             : 
     130           6 :         if (pi) {
     131           0 :             nsAutoCString proxyType;
     132           0 :             rv = pi->GetType(proxyType);
     133           0 :             if (NS_FAILED(rv)) return rv;
     134             : 
     135           0 :             mProxyUsingSSL = proxyType.EqualsLiteral("https");
     136             :         }
     137             :     }
     138             : 
     139           6 :     rv = mURI->GetAsciiHost(mHost);
     140           6 :     if (NS_FAILED(rv)) return rv;
     141             : 
     142             :     // reject the URL if it doesn't specify a host
     143           6 :     if (mHost.IsEmpty())
     144           0 :         return NS_ERROR_MALFORMED_URI;
     145             : 
     146           6 :     rv = mURI->GetPort(&mPort);
     147           6 :     if (NS_FAILED(rv)) return rv;
     148             : 
     149          12 :     nsCOMPtr<nsIChannel> bareChannel = do_QueryInterface(channel);
     150           6 :     mIsPrivate = NS_UsePrivateBrowsing(bareChannel);
     151             : 
     152           6 :     return NS_OK;
     153             : }
     154             : 
     155             : NS_IMETHODIMP
     156           0 : nsHttpChannelAuthProvider::ProcessAuthentication(uint32_t httpStatus,
     157             :                                                  bool     SSLConnectFailed)
     158             : {
     159           0 :     LOG(("nsHttpChannelAuthProvider::ProcessAuthentication "
     160             :          "[this=%p channel=%p code=%u SSLConnectFailed=%d]\n",
     161             :          this, mAuthChannel, httpStatus, SSLConnectFailed));
     162             : 
     163           0 :     MOZ_ASSERT(mAuthChannel, "Channel not initialized");
     164             : 
     165           0 :     nsCOMPtr<nsIProxyInfo> proxyInfo;
     166           0 :     nsresult rv = mAuthChannel->GetProxyInfo(getter_AddRefs(proxyInfo));
     167           0 :     if (NS_FAILED(rv)) return rv;
     168           0 :     if (proxyInfo) {
     169           0 :         mProxyInfo = do_QueryInterface(proxyInfo);
     170           0 :         if (!mProxyInfo) return NS_ERROR_NO_INTERFACE;
     171             :     }
     172             : 
     173           0 :     nsAutoCString challenges;
     174           0 :     mProxyAuth = (httpStatus == 407);
     175             : 
     176           0 :     rv = PrepareForAuthentication(mProxyAuth);
     177           0 :     if (NS_FAILED(rv))
     178           0 :         return rv;
     179             : 
     180           0 :     if (mProxyAuth) {
     181             :         // only allow a proxy challenge if we have a proxy server configured.
     182             :         // otherwise, we could inadvertently expose the user's proxy
     183             :         // credentials to an origin server.  We could attempt to proceed as
     184             :         // if we had received a 401 from the server, but why risk flirting
     185             :         // with trouble?  IE similarly rejects 407s when a proxy server is
     186             :         // not configured, so there's no reason not to do the same.
     187           0 :         if (!UsingHttpProxy()) {
     188           0 :             LOG(("rejecting 407 when proxy server not configured!\n"));
     189           0 :             return NS_ERROR_UNEXPECTED;
     190             :         }
     191           0 :         if (UsingSSL() && !SSLConnectFailed) {
     192             :             // we need to verify that this challenge came from the proxy
     193             :             // server itself, and not some server on the other side of the
     194             :             // SSL tunnel.
     195           0 :             LOG(("rejecting 407 from origin server!\n"));
     196           0 :             return NS_ERROR_UNEXPECTED;
     197             :         }
     198           0 :         rv = mAuthChannel->GetProxyChallenges(challenges);
     199             :     }
     200             :     else
     201           0 :         rv = mAuthChannel->GetWWWChallenges(challenges);
     202           0 :     if (NS_FAILED(rv)) return rv;
     203             : 
     204           0 :     nsAutoCString creds;
     205           0 :     rv = GetCredentials(challenges.get(), mProxyAuth, creds);
     206           0 :     if (rv == NS_ERROR_IN_PROGRESS)
     207           0 :         return rv;
     208           0 :     if (NS_FAILED(rv))
     209           0 :         LOG(("unable to authenticate\n"));
     210             :     else {
     211             :         // set the authentication credentials
     212           0 :         if (mProxyAuth)
     213           0 :             rv = mAuthChannel->SetProxyCredentials(creds);
     214             :         else
     215           0 :             rv = mAuthChannel->SetWWWCredentials(creds);
     216             :     }
     217           0 :     return rv;
     218             : }
     219             : 
     220             : NS_IMETHODIMP
     221           6 : nsHttpChannelAuthProvider::AddAuthorizationHeaders(bool aDontUseCachedWWWCreds)
     222             : {
     223           6 :     LOG(("nsHttpChannelAuthProvider::AddAuthorizationHeaders? "
     224             :          "[this=%p channel=%p]\n", this, mAuthChannel));
     225             : 
     226           6 :     MOZ_ASSERT(mAuthChannel, "Channel not initialized");
     227             : 
     228          12 :     nsCOMPtr<nsIProxyInfo> proxyInfo;
     229           6 :     nsresult rv = mAuthChannel->GetProxyInfo(getter_AddRefs(proxyInfo));
     230           6 :     if (NS_FAILED(rv)) return rv;
     231           6 :     if (proxyInfo) {
     232           0 :         mProxyInfo = do_QueryInterface(proxyInfo);
     233           0 :         if (!mProxyInfo) return NS_ERROR_NO_INTERFACE;
     234             :     }
     235             : 
     236             :     uint32_t loadFlags;
     237           6 :     rv = mAuthChannel->GetLoadFlags(&loadFlags);
     238           6 :     if (NS_FAILED(rv)) return rv;
     239             : 
     240             :     // this getter never fails
     241           6 :     nsHttpAuthCache *authCache = gHttpHandler->AuthCache(mIsPrivate);
     242             : 
     243             :     // check if proxy credentials should be sent
     244           6 :     const char *proxyHost = ProxyHost();
     245           6 :     if (proxyHost && UsingHttpProxy()) {
     246           0 :         SetAuthorizationHeader(authCache, nsHttp::Proxy_Authorization,
     247             :                                "http", proxyHost, ProxyPort(),
     248             :                                nullptr, // proxy has no path
     249           0 :                                mProxyIdent);
     250             :     }
     251             : 
     252           6 :     if (loadFlags & nsIRequest::LOAD_ANONYMOUS) {
     253           0 :         LOG(("Skipping Authorization header for anonymous load\n"));
     254           0 :         return NS_OK;
     255             :     }
     256             : 
     257           6 :     if (aDontUseCachedWWWCreds) {
     258           0 :         LOG(("Authorization header already present:"
     259             :              " skipping adding auth header from cache\n"));
     260           0 :         return NS_OK;
     261             :     }
     262             : 
     263             :     // check if server credentials should be sent
     264          12 :     nsAutoCString path, scheme;
     265          12 :     if (NS_SUCCEEDED(GetCurrentPath(path)) &&
     266           6 :         NS_SUCCEEDED(mURI->GetScheme(scheme))) {
     267           6 :         SetAuthorizationHeader(authCache, nsHttp::Authorization,
     268             :                                scheme.get(),
     269             :                                Host(),
     270             :                                Port(),
     271             :                                path.get(),
     272           6 :                                mIdent);
     273             :     }
     274             : 
     275           6 :     return NS_OK;
     276             : }
     277             : 
     278             : NS_IMETHODIMP
     279           3 : nsHttpChannelAuthProvider::CheckForSuperfluousAuth()
     280             : {
     281           3 :     LOG(("nsHttpChannelAuthProvider::CheckForSuperfluousAuth? "
     282             :          "[this=%p channel=%p]\n", this, mAuthChannel));
     283             : 
     284           3 :     MOZ_ASSERT(mAuthChannel, "Channel not initialized");
     285             : 
     286             :     // we've been called because it has been determined that this channel is
     287             :     // getting loaded without taking the userpass from the URL.  if the URL
     288             :     // contained a userpass, then (provided some other conditions are true),
     289             :     // we'll give the user an opportunity to abort the channel as this might be
     290             :     // an attempt to spoof a different site (see bug 232567).
     291           3 :     if (!ConfirmAuth(NS_LITERAL_STRING("SuperfluousAuth"), true)) {
     292             :         // calling cancel here sets our mStatus and aborts the HTTP
     293             :         // transaction, which prevents OnDataAvailable events.
     294           0 :         Unused << mAuthChannel->Cancel(NS_ERROR_ABORT);
     295           0 :         return NS_ERROR_ABORT;
     296             :     }
     297           3 :     return NS_OK;
     298             : }
     299             : 
     300             : NS_IMETHODIMP
     301           1 : nsHttpChannelAuthProvider::Cancel(nsresult status)
     302             : {
     303           1 :     MOZ_ASSERT(mAuthChannel, "Channel not initialized");
     304             : 
     305           1 :     if (mAsyncPromptAuthCancelable) {
     306           0 :         mAsyncPromptAuthCancelable->Cancel(status);
     307           0 :         mAsyncPromptAuthCancelable = nullptr;
     308             :     }
     309             : 
     310           1 :     if (mGenerateCredentialsCancelable) {
     311           0 :         mGenerateCredentialsCancelable->Cancel(status);
     312           0 :         mGenerateCredentialsCancelable = nullptr;
     313             :     }
     314           1 :     return NS_OK;
     315             : }
     316             : 
     317             : NS_IMETHODIMP
     318           6 : nsHttpChannelAuthProvider::Disconnect(nsresult status)
     319             : {
     320           6 :     mAuthChannel = nullptr;
     321             : 
     322           6 :     if (mAsyncPromptAuthCancelable) {
     323           0 :         mAsyncPromptAuthCancelable->Cancel(status);
     324           0 :         mAsyncPromptAuthCancelable = nullptr;
     325             :     }
     326             : 
     327           6 :     if (mGenerateCredentialsCancelable) {
     328           0 :         mGenerateCredentialsCancelable->Cancel(status);
     329           0 :         mGenerateCredentialsCancelable = nullptr;
     330             :     }
     331             : 
     332           6 :     NS_IF_RELEASE(mProxyAuthContinuationState);
     333           6 :     NS_IF_RELEASE(mAuthContinuationState);
     334             : 
     335           6 :     return NS_OK;
     336             : }
     337             : 
     338             : // buf contains "domain\user"
     339             : static void
     340           0 : ParseUserDomain(char16_t *buf,
     341             :                 const char16_t **user,
     342             :                 const char16_t **domain)
     343             : {
     344           0 :     char16_t *p = buf;
     345           0 :     while (*p && *p != '\\') ++p;
     346           0 :     if (!*p)
     347           0 :         return;
     348           0 :     *p = '\0';
     349           0 :     *domain = buf;
     350           0 :     *user = p + 1;
     351             : }
     352             : 
     353             : // helper function for setting identity from raw user:pass
     354             : static void
     355           0 : SetIdent(nsHttpAuthIdentity &ident,
     356             :          uint32_t authFlags,
     357             :          char16_t *userBuf,
     358             :          char16_t *passBuf)
     359             : {
     360           0 :     const char16_t *user = userBuf;
     361           0 :     const char16_t *domain = nullptr;
     362             : 
     363           0 :     if (authFlags & nsIHttpAuthenticator::IDENTITY_INCLUDES_DOMAIN)
     364           0 :         ParseUserDomain(userBuf, &user, &domain);
     365             : 
     366           0 :     DebugOnly<nsresult> rv = ident.Set(domain, user, passBuf);
     367           0 :     MOZ_ASSERT(NS_SUCCEEDED(rv));
     368           0 : }
     369             : 
     370             : // helper function for getting an auth prompt from an interface requestor
     371             : static void
     372           0 : GetAuthPrompt(nsIInterfaceRequestor *ifreq, bool proxyAuth,
     373             :               nsIAuthPrompt2 **result)
     374             : {
     375           0 :     if (!ifreq)
     376           0 :         return;
     377             : 
     378             :     uint32_t promptReason;
     379           0 :     if (proxyAuth)
     380           0 :         promptReason = nsIAuthPromptProvider::PROMPT_PROXY;
     381             :     else
     382           0 :         promptReason = nsIAuthPromptProvider::PROMPT_NORMAL;
     383             : 
     384           0 :     nsCOMPtr<nsIAuthPromptProvider> promptProvider = do_GetInterface(ifreq);
     385           0 :     if (promptProvider)
     386           0 :         promptProvider->GetAuthPrompt(promptReason,
     387             :                                       NS_GET_IID(nsIAuthPrompt2),
     388           0 :                                       reinterpret_cast<void**>(result));
     389             :     else
     390           0 :         NS_QueryAuthPrompt2(ifreq, result);
     391             : }
     392             : 
     393             : // generate credentials for the given challenge, and update the auth cache.
     394             : nsresult
     395           0 : nsHttpChannelAuthProvider::GenCredsAndSetEntry(nsIHttpAuthenticator *auth,
     396             :                                                bool                  proxyAuth,
     397             :                                                const char           *scheme,
     398             :                                                const char           *host,
     399             :                                                int32_t               port,
     400             :                                                const char           *directory,
     401             :                                                const char           *realm,
     402             :                                                const char           *challenge,
     403             :                                                const nsHttpAuthIdentity &ident,
     404             :                                                nsCOMPtr<nsISupports>    &sessionState,
     405             :                                                char                    **result)
     406             : {
     407             :     nsresult rv;
     408           0 :     nsISupports *ss = sessionState;
     409             : 
     410             :     // set informations that depend on whether
     411             :     // we're authenticating against a proxy
     412             :     // or a webserver
     413             :     nsISupports **continuationState;
     414             : 
     415           0 :     if (proxyAuth) {
     416           0 :         continuationState = &mProxyAuthContinuationState;
     417             :     } else {
     418           0 :         continuationState = &mAuthContinuationState;
     419             :     }
     420             : 
     421           0 :     rv = auth->GenerateCredentialsAsync(mAuthChannel,
     422             :                                        this,
     423             :                                        challenge,
     424             :                                        proxyAuth,
     425             :                                        ident.Domain(),
     426             :                                        ident.User(),
     427             :                                        ident.Password(),
     428             :                                        ss,
     429             :                                        *continuationState,
     430           0 :                                        getter_AddRefs(mGenerateCredentialsCancelable));
     431           0 :     if (NS_SUCCEEDED(rv)) {
     432             :         // Calling generate credentials async, results will be dispatched to the
     433             :         // main thread by calling OnCredsGenerated method
     434           0 :         return NS_ERROR_IN_PROGRESS;
     435             :     }
     436             : 
     437             :     uint32_t generateFlags;
     438           0 :     rv = auth->GenerateCredentials(mAuthChannel,
     439             :                                    challenge,
     440             :                                    proxyAuth,
     441             :                                    ident.Domain(),
     442             :                                    ident.User(),
     443             :                                    ident.Password(),
     444             :                                    &ss,
     445             :                                    &*continuationState,
     446             :                                    &generateFlags,
     447           0 :                                    result);
     448             : 
     449           0 :     sessionState.swap(ss);
     450           0 :     if (NS_FAILED(rv)) return rv;
     451             : 
     452             :     // don't log this in release build since it could contain sensitive info.
     453             : #ifdef DEBUG
     454           0 :     LOG(("generated creds: %s\n", *result));
     455             : #endif
     456             : 
     457           0 :     return UpdateCache(auth, scheme, host, port, directory, realm,
     458           0 :             challenge, ident, *result, generateFlags, sessionState);
     459             : }
     460             : 
     461             : nsresult
     462           0 : nsHttpChannelAuthProvider::UpdateCache(nsIHttpAuthenticator *auth,
     463             :                                        const char           *scheme,
     464             :                                        const char           *host,
     465             :                                        int32_t               port,
     466             :                                        const char           *directory,
     467             :                                        const char           *realm,
     468             :                                        const char           *challenge,
     469             :                                        const nsHttpAuthIdentity &ident,
     470             :                                        const char           *creds,
     471             :                                        uint32_t              generateFlags,
     472             :                                        nsISupports          *sessionState)
     473             : {
     474             :     nsresult rv;
     475             : 
     476             :     uint32_t authFlags;
     477           0 :     rv = auth->GetAuthFlags(&authFlags);
     478           0 :     if (NS_FAILED(rv)) return rv;
     479             : 
     480             :     // find out if this authenticator allows reuse of credentials and/or
     481             :     // challenge.
     482             :     bool saveCreds =
     483           0 :         0 != (authFlags & nsIHttpAuthenticator::REUSABLE_CREDENTIALS);
     484             :     bool saveChallenge =
     485           0 :         0 != (authFlags & nsIHttpAuthenticator::REUSABLE_CHALLENGE);
     486             : 
     487             :     bool saveIdentity =
     488           0 :         0 == (generateFlags & nsIHttpAuthenticator::USING_INTERNAL_IDENTITY);
     489             : 
     490             :     // this getter never fails
     491           0 :     nsHttpAuthCache *authCache = gHttpHandler->AuthCache(mIsPrivate);
     492             : 
     493           0 :     nsCOMPtr<nsIChannel> chan = do_QueryInterface(mAuthChannel);
     494           0 :     nsAutoCString suffix;
     495           0 :     GetOriginAttributesSuffix(chan, suffix);
     496             : 
     497             : 
     498             :     // create a cache entry.  we do this even though we don't yet know that
     499             :     // these credentials are valid b/c we need to avoid prompting the user
     500             :     // more than once in case the credentials are valid.
     501             :     //
     502             :     // if the credentials are not reusable, then we don't bother sticking
     503             :     // them in the auth cache.
     504           0 :     rv = authCache->SetAuthEntry(scheme, host, port, directory, realm,
     505             :                                  saveCreds ? creds : nullptr,
     506             :                                  saveChallenge ? challenge : nullptr,
     507             :                                  suffix,
     508             :                                  saveIdentity ? &ident : nullptr,
     509           0 :                                  sessionState);
     510           0 :     return rv;
     511             : 
     512             : }
     513             : 
     514             : nsresult
     515           0 : nsHttpChannelAuthProvider::PrepareForAuthentication(bool proxyAuth)
     516             : {
     517           0 :     LOG(("nsHttpChannelAuthProvider::PrepareForAuthentication "
     518             :          "[this=%p channel=%p]\n", this, mAuthChannel));
     519             : 
     520           0 :     if (!proxyAuth) {
     521             :         // reset the current proxy continuation state because our last
     522             :         // authentication attempt was completed successfully.
     523           0 :         NS_IF_RELEASE(mProxyAuthContinuationState);
     524           0 :         LOG(("  proxy continuation state has been reset"));
     525             :     }
     526             : 
     527           0 :     if (!UsingHttpProxy() || mProxyAuthType.IsEmpty())
     528           0 :         return NS_OK;
     529             : 
     530             :     // We need to remove any Proxy_Authorization header left over from a
     531             :     // non-request based authentication handshake (e.g., for NTLM auth).
     532             : 
     533           0 :     nsAutoCString contractId;
     534           0 :     contractId.Assign(NS_HTTP_AUTHENTICATOR_CONTRACTID_PREFIX);
     535           0 :     contractId.Append(mProxyAuthType);
     536             : 
     537             :     nsresult rv;
     538             :     nsCOMPtr<nsIHttpAuthenticator> precedingAuth =
     539           0 :         do_GetService(contractId.get(), &rv);
     540           0 :     if (NS_FAILED(rv))
     541           0 :         return rv;
     542             : 
     543             :     uint32_t precedingAuthFlags;
     544           0 :     rv = precedingAuth->GetAuthFlags(&precedingAuthFlags);
     545           0 :     if (NS_FAILED(rv))
     546           0 :         return rv;
     547             : 
     548           0 :     if (!(precedingAuthFlags & nsIHttpAuthenticator::REQUEST_BASED)) {
     549           0 :         nsAutoCString challenges;
     550           0 :         rv = mAuthChannel->GetProxyChallenges(challenges);
     551           0 :         if (NS_FAILED(rv)) {
     552             :             // delete the proxy authorization header because we weren't
     553             :             // asked to authenticate
     554           0 :             rv = mAuthChannel->SetProxyCredentials(EmptyCString());
     555           0 :             if (NS_FAILED(rv)) return rv;
     556           0 :             LOG(("  cleared proxy authorization header"));
     557             :         }
     558             :     }
     559             : 
     560           0 :     return NS_OK;
     561             : }
     562             : 
     563             : nsresult
     564           0 : nsHttpChannelAuthProvider::GetCredentials(const char     *challenges,
     565             :                                           bool            proxyAuth,
     566             :                                           nsCString& creds)
     567             : {
     568           0 :     nsCOMPtr<nsIHttpAuthenticator> auth;
     569           0 :     nsAutoCString challenge;
     570             : 
     571           0 :     nsCString authType; // force heap allocation to enable string sharing since
     572             :                         // we'll be assigning this value into mAuthType.
     573             : 
     574             :     // set informations that depend on whether we're authenticating against a
     575             :     // proxy or a webserver
     576             :     nsISupports **currentContinuationState;
     577             :     nsCString *currentAuthType;
     578             : 
     579           0 :     if (proxyAuth) {
     580           0 :         currentContinuationState = &mProxyAuthContinuationState;
     581           0 :         currentAuthType = &mProxyAuthType;
     582             :     } else {
     583           0 :         currentContinuationState = &mAuthContinuationState;
     584           0 :         currentAuthType = &mAuthType;
     585             :     }
     586             : 
     587           0 :     nsresult rv = NS_ERROR_NOT_AVAILABLE;
     588           0 :     bool gotCreds = false;
     589             : 
     590             :     // figure out which challenge we can handle and which authenticator to use.
     591           0 :     for (const char *eol = challenges - 1; eol; ) {
     592           0 :         const char *p = eol + 1;
     593             : 
     594             :         // get the challenge string (LF separated -- see nsHttpHeaderArray)
     595           0 :         if ((eol = strchr(p, '\n')) != nullptr)
     596           0 :             challenge.Assign(p, eol - p);
     597             :         else
     598           0 :             challenge.Assign(p);
     599             : 
     600           0 :         rv = GetAuthenticator(challenge.get(), authType, getter_AddRefs(auth));
     601           0 :         if (NS_SUCCEEDED(rv)) {
     602             :             //
     603             :             // if we've already selected an auth type from a previous challenge
     604             :             // received while processing this channel, then skip others until
     605             :             // we find a challenge corresponding to the previously tried auth
     606             :             // type.
     607             :             //
     608           0 :             if (!currentAuthType->IsEmpty() && authType != *currentAuthType)
     609           0 :                 continue;
     610             : 
     611             :             //
     612             :             // we allow the routines to run all the way through before we
     613             :             // decide if they are valid.
     614             :             //
     615             :             // we don't worry about the auth cache being altered because that
     616             :             // would have been the last step, and if the error is from updating
     617             :             // the authcache it wasn't really altered anyway. -CTN
     618             :             //
     619             :             // at this point the code is really only useful for client side
     620             :             // errors (it will not automatically fail over to do a different
     621             :             // auth type if the server keeps rejecting what is being sent, even
     622             :             // if a particular auth method only knows 1 thing, like a
     623             :             // non-identity based authentication method)
     624             :             //
     625           0 :             rv = GetCredentialsForChallenge(challenge.get(), authType.get(),
     626           0 :                                             proxyAuth, auth, creds);
     627           0 :             if (NS_SUCCEEDED(rv)) {
     628           0 :                 gotCreds = true;
     629           0 :                 *currentAuthType = authType;
     630             : 
     631           0 :                 break;
     632             :             }
     633           0 :             if (rv == NS_ERROR_IN_PROGRESS) {
     634             :                 // authentication prompt has been invoked and result is
     635             :                 // expected asynchronously, save current challenge being
     636             :                 // processed and all remaining challenges to use later in
     637             :                 // OnAuthAvailable and now immediately return
     638           0 :                 mCurrentChallenge = challenge;
     639           0 :                 mRemainingChallenges = eol ? eol+1 : nullptr;
     640           0 :                 return rv;
     641             :             }
     642             : 
     643             :             // reset the auth type and continuation state
     644           0 :             NS_IF_RELEASE(*currentContinuationState);
     645           0 :             currentAuthType->Truncate();
     646             :         }
     647             :     }
     648             : 
     649           0 :     if (!gotCreds && !currentAuthType->IsEmpty()) {
     650             :         // looks like we never found the auth type we were looking for.
     651             :         // reset the auth type and continuation state, and try again.
     652           0 :         currentAuthType->Truncate();
     653           0 :         NS_IF_RELEASE(*currentContinuationState);
     654             : 
     655           0 :         rv = GetCredentials(challenges, proxyAuth, creds);
     656             :     }
     657             : 
     658           0 :     return rv;
     659             : }
     660             : 
     661             : nsresult
     662           0 : nsHttpChannelAuthProvider::GetAuthorizationMembers(bool                 proxyAuth,
     663             :                                                    nsACString&        scheme,
     664             :                                                    const char*&         host,
     665             :                                                    int32_t&             port,
     666             :                                                    nsACString&        path,
     667             :                                                    nsHttpAuthIdentity*& ident,
     668             :                                                    nsISupports**&       continuationState)
     669             : {
     670           0 :     if (proxyAuth) {
     671           0 :         MOZ_ASSERT (UsingHttpProxy(),
     672             :                     "proxyAuth is true, but no HTTP proxy is configured!");
     673             : 
     674           0 :         host = ProxyHost();
     675           0 :         port = ProxyPort();
     676           0 :         ident = &mProxyIdent;
     677           0 :         scheme.AssignLiteral("http");
     678             : 
     679           0 :         continuationState = &mProxyAuthContinuationState;
     680             :     }
     681             :     else {
     682           0 :         host = Host();
     683           0 :         port = Port();
     684           0 :         ident = &mIdent;
     685             : 
     686             :         nsresult rv;
     687           0 :         rv = GetCurrentPath(path);
     688           0 :         if (NS_FAILED(rv)) return rv;
     689             : 
     690           0 :         rv = mURI->GetScheme(scheme);
     691           0 :         if (NS_FAILED(rv)) return rv;
     692             : 
     693           0 :         continuationState = &mAuthContinuationState;
     694             :     }
     695             : 
     696           0 :     return NS_OK;
     697             : }
     698             : 
     699             : nsresult
     700           0 : nsHttpChannelAuthProvider::GetCredentialsForChallenge(const char *challenge,
     701             :                                                       const char *authType,
     702             :                                                       bool        proxyAuth,
     703             :                                                       nsIHttpAuthenticator *auth,
     704             :                                                       nsCString& creds)
     705             : {
     706           0 :     LOG(("nsHttpChannelAuthProvider::GetCredentialsForChallenge "
     707             :          "[this=%p channel=%p proxyAuth=%d challenges=%s]\n",
     708             :         this, mAuthChannel, proxyAuth, challenge));
     709             : 
     710             :     // this getter never fails
     711           0 :     nsHttpAuthCache *authCache = gHttpHandler->AuthCache(mIsPrivate);
     712             : 
     713             :     uint32_t authFlags;
     714           0 :     nsresult rv = auth->GetAuthFlags(&authFlags);
     715           0 :     if (NS_FAILED(rv)) return rv;
     716             : 
     717           0 :     nsAutoCString realm;
     718           0 :     ParseRealm(challenge, realm);
     719             : 
     720             :     // if no realm, then use the auth type as the realm.  ToUpperCase so the
     721             :     // ficticious realm stands out a bit more.
     722             :     // XXX this will cause some single signon misses!
     723             :     // XXX this was meant to be used with NTLM, which supplies no realm.
     724             :     /*
     725             :     if (realm.IsEmpty()) {
     726             :         realm = authType;
     727             :         ToUpperCase(realm);
     728             :     }
     729             :     */
     730             : 
     731             :     // set informations that depend on whether
     732             :     // we're authenticating against a proxy
     733             :     // or a webserver
     734             :     const char *host;
     735             :     int32_t port;
     736             :     nsHttpAuthIdentity *ident;
     737           0 :     nsAutoCString path, scheme;
     738           0 :     bool identFromURI = false;
     739             :     nsISupports **continuationState;
     740             : 
     741           0 :     rv = GetAuthorizationMembers(proxyAuth, scheme, host, port,
     742           0 :                                  path, ident, continuationState);
     743           0 :     if (NS_FAILED(rv)) return rv;
     744             : 
     745             :     uint32_t loadFlags;
     746           0 :     rv = mAuthChannel->GetLoadFlags(&loadFlags);
     747           0 :     if (NS_FAILED(rv)) return rv;
     748             : 
     749           0 :     if (!proxyAuth) {
     750             :         // if this is the first challenge, then try using the identity
     751             :         // specified in the URL.
     752           0 :         if (mIdent.IsEmpty()) {
     753           0 :             GetIdentityFromURI(authFlags, mIdent);
     754           0 :             identFromURI = !mIdent.IsEmpty();
     755             :         }
     756             : 
     757           0 :         if ((loadFlags & nsIRequest::LOAD_ANONYMOUS) && !identFromURI) {
     758           0 :             LOG(("Skipping authentication for anonymous non-proxy request\n"));
     759           0 :             return NS_ERROR_NOT_AVAILABLE;
     760             :         }
     761             : 
     762             :         // Let explicit URL credentials pass
     763             :         // regardless of the LOAD_ANONYMOUS flag
     764             :     }
     765           0 :     else if ((loadFlags & nsIRequest::LOAD_ANONYMOUS) && !UsingHttpProxy()) {
     766           0 :         LOG(("Skipping authentication for anonymous non-proxy request\n"));
     767           0 :         return NS_ERROR_NOT_AVAILABLE;
     768             :     }
     769             : 
     770           0 :     nsCOMPtr<nsIChannel> chan = do_QueryInterface(mAuthChannel);
     771           0 :     nsAutoCString suffix;
     772           0 :     GetOriginAttributesSuffix(chan, suffix);
     773             : 
     774             :     //
     775             :     // if we already tried some credentials for this transaction, then
     776             :     // we need to possibly clear them from the cache, unless the credentials
     777             :     // in the cache have changed, in which case we'd want to give them a
     778             :     // try instead.
     779             :     //
     780           0 :     nsHttpAuthEntry *entry = nullptr;
     781           0 :     Unused << authCache->GetAuthEntryForDomain(scheme.get(), host, port,
     782             :                                                realm.get(), suffix, &entry);
     783             : 
     784             :     // hold reference to the auth session state (in case we clear our
     785             :     // reference to the entry).
     786           0 :     nsCOMPtr<nsISupports> sessionStateGrip;
     787           0 :     if (entry)
     788           0 :         sessionStateGrip = entry->mMetaData;
     789             : 
     790             :     // remember if we already had the continuation state.  it means we are in
     791             :     // the middle of the authentication exchange and the connection must be
     792             :     // kept sticky then (and only then).
     793           0 :     bool authAtProgress = !!*continuationState;
     794             : 
     795             :     // for digest auth, maybe our cached nonce value simply timed out...
     796             :     bool identityInvalid;
     797           0 :     nsISupports *sessionState = sessionStateGrip;
     798           0 :     rv = auth->ChallengeReceived(mAuthChannel,
     799             :                                  challenge,
     800             :                                  proxyAuth,
     801             :                                  &sessionState,
     802             :                                  &*continuationState,
     803           0 :                                  &identityInvalid);
     804           0 :     sessionStateGrip.swap(sessionState);
     805           0 :     if (NS_FAILED(rv)) return rv;
     806             : 
     807           0 :     LOG(("  identity invalid = %d\n", identityInvalid));
     808             : 
     809           0 :     if (mConnectionBased && identityInvalid) {
     810             :         // If the flag is set and identity is invalid, it means we received the first
     811             :         // challange for a new negotiation round after negotiating a connection based
     812             :         // auth failed (invalid password).
     813             :         // The mConnectionBased flag is set later for the newly received challenge,
     814             :         // so here it reflects the previous 401/7 response schema.
     815           0 :         rv = mAuthChannel->CloseStickyConnection();
     816           0 :         MOZ_ASSERT(NS_SUCCEEDED(rv));
     817           0 :         if (!proxyAuth) {
     818             :             // We must clear proxy ident in the following scenario + explanation:
     819             :             // - we are authenticating to an NTLM proxy and an NTLM server
     820             :             // - we successfully authenticated to the proxy, mProxyIdent keeps
     821             :             //   the user name/domain and password, the identity has also been cached
     822             :             // - we just threw away the connection because we are now asking for
     823             :             //   creds for the server (WWW auth)
     824             :             // - hence, we will have to auth to the proxy again as well
     825             :             // - if we didn't clear the proxy identity, it would be considered
     826             :             //   as non-valid and we would ask the user again ; clearing it forces
     827             :             //   use of the cached identity and not asking the user again
     828           0 :             mProxyIdent.Clear();
     829             :         }
     830             :     }
     831             : 
     832           0 :     mConnectionBased = !!(authFlags & nsIHttpAuthenticator::CONNECTION_BASED);
     833             : 
     834             :     // It's legal if the peer closes the connection after the first 401/7.
     835             :     // Making the connection sticky will prevent its restart giving the user
     836             :     // a 'network reset' error every time.  Hence, we mark the connection
     837             :     // as restartable.
     838           0 :     mAuthChannel->ConnectionRestartable(mConnectionBased && !authAtProgress);
     839             : 
     840           0 :     if (identityInvalid) {
     841           0 :         if (entry) {
     842           0 :             if (ident->Equals(entry->Identity())) {
     843           0 :                 if (!identFromURI) {
     844           0 :                     LOG(("  clearing bad auth cache entry\n"));
     845             :                     // ok, we've already tried this user identity, so clear the
     846             :                     // corresponding entry from the auth cache.
     847           0 :                     authCache->ClearAuthEntry(scheme.get(), host,
     848             :                                               port, realm.get(),
     849           0 :                                               suffix);
     850           0 :                     entry = nullptr;
     851           0 :                     ident->Clear();
     852             :                 }
     853             :             }
     854           0 :             else if (!identFromURI ||
     855           0 :                      (nsCRT::strcmp(ident->User(),
     856           0 :                                     entry->Identity().User()) == 0 &&
     857           0 :                      !(loadFlags &
     858             :                        (nsIChannel::LOAD_ANONYMOUS |
     859             :                         nsIChannel::LOAD_EXPLICIT_CREDENTIALS)))) {
     860           0 :                 LOG(("  taking identity from auth cache\n"));
     861             :                 // the password from the auth cache is more likely to be
     862             :                 // correct than the one in the URL.  at least, we know that it
     863             :                 // works with the given username.  it is possible for a server
     864             :                 // to distinguish logons based on the supplied password alone,
     865             :                 // but that would be quite unusual... and i don't think we need
     866             :                 // to worry about such unorthodox cases.
     867           0 :                 rv = ident->Set(entry->Identity());
     868           0 :                 MOZ_ASSERT(NS_SUCCEEDED(rv));
     869           0 :                 identFromURI = false;
     870           0 :                 if (entry->Creds()[0] != '\0') {
     871           0 :                     LOG(("    using cached credentials!\n"));
     872           0 :                     creds.Assign(entry->Creds());
     873           0 :                     return entry->AddPath(path.get());
     874             :                 }
     875             :             }
     876             :         }
     877           0 :         else if (!identFromURI) {
     878             :             // hmm... identity invalid, but no auth entry!  the realm probably
     879             :             // changed (see bug 201986).
     880           0 :             ident->Clear();
     881             :         }
     882             : 
     883           0 :         if (!entry && ident->IsEmpty()) {
     884           0 :             uint32_t level = nsIAuthPrompt2::LEVEL_NONE;
     885           0 :             if ((!proxyAuth && mUsingSSL) || (proxyAuth && mProxyUsingSSL))
     886           0 :                 level = nsIAuthPrompt2::LEVEL_SECURE;
     887           0 :             else if (authFlags & nsIHttpAuthenticator::IDENTITY_ENCRYPTED)
     888           0 :                 level = nsIAuthPrompt2::LEVEL_PW_ENCRYPTED;
     889             : 
     890             :             // Collect statistics on how frequently the various types of HTTP
     891             :             // authentication are used over SSL and non-SSL connections.
     892           0 :             if (gHttpHandler->IsTelemetryEnabled()) {
     893           0 :               if (NS_LITERAL_CSTRING("basic").LowerCaseEqualsASCII(authType)) {
     894           0 :                 Telemetry::Accumulate(Telemetry::HTTP_AUTH_TYPE_STATS,
     895           0 :                   UsingSSL() ? HTTP_AUTH_BASIC_SECURE : HTTP_AUTH_BASIC_INSECURE);
     896           0 :               } else if (NS_LITERAL_CSTRING("digest").LowerCaseEqualsASCII(authType)) {
     897           0 :                 Telemetry::Accumulate(Telemetry::HTTP_AUTH_TYPE_STATS,
     898           0 :                   UsingSSL() ? HTTP_AUTH_DIGEST_SECURE : HTTP_AUTH_DIGEST_INSECURE);
     899           0 :               } else if (NS_LITERAL_CSTRING("ntlm").LowerCaseEqualsASCII(authType)) {
     900           0 :                 Telemetry::Accumulate(Telemetry::HTTP_AUTH_TYPE_STATS,
     901           0 :                   UsingSSL() ? HTTP_AUTH_NTLM_SECURE : HTTP_AUTH_NTLM_INSECURE);
     902           0 :               } else if (NS_LITERAL_CSTRING("negotiate").LowerCaseEqualsASCII(authType)) {
     903           0 :                 Telemetry::Accumulate(Telemetry::HTTP_AUTH_TYPE_STATS,
     904           0 :                   UsingSSL() ? HTTP_AUTH_NEGOTIATE_SECURE : HTTP_AUTH_NEGOTIATE_INSECURE);
     905             :               }
     906             :             }
     907             : 
     908             :             // Depending on the pref setting, the authentication dialog may be
     909             :             // blocked for all sub-resources, blocked for cross-origin
     910             :             // sub-resources, or always allowed for sub-resources.
     911             :             // For more details look at the bug 647010.
     912             :             // BlockPrompt will set mCrossOrigin parameter as well.
     913           0 :             if (BlockPrompt()) {
     914           0 :                 LOG(("nsHttpChannelAuthProvider::GetCredentialsForChallenge: "
     915             :                      "Prompt is blocked [this=%p pref=%d img-pref=%d]\n",
     916             :                       this, sAuthAllowPref, sImgCrossOriginAuthAllowPref));
     917           0 :                 return NS_ERROR_ABORT;
     918             :             }
     919             : 
     920             :             // at this point we are forced to interact with the user to get
     921             :             // their username and password for this domain.
     922           0 :             rv = PromptForIdentity(level, proxyAuth, realm.get(),
     923           0 :                                    authType, authFlags, *ident);
     924           0 :             if (NS_FAILED(rv)) return rv;
     925           0 :             identFromURI = false;
     926             :         }
     927             :     }
     928             : 
     929           0 :     if (identFromURI) {
     930             :         // Warn the user before automatically using the identity from the URL
     931             :         // to automatically log them into a site (see bug 232567).
     932           0 :         if (!ConfirmAuth(NS_LITERAL_STRING("AutomaticAuth"), false)) {
     933             :             // calling cancel here sets our mStatus and aborts the HTTP
     934             :             // transaction, which prevents OnDataAvailable events.
     935           0 :             rv = mAuthChannel->Cancel(NS_ERROR_ABORT);
     936           0 :             MOZ_ASSERT(NS_SUCCEEDED(rv));
     937             :             // this return code alone is not equivalent to Cancel, since
     938             :             // it only instructs our caller that authentication failed.
     939             :             // without an explicit call to Cancel, our caller would just
     940             :             // load the page that accompanies the HTTP auth challenge.
     941           0 :             return NS_ERROR_ABORT;
     942             :         }
     943             :     }
     944             : 
     945             :     //
     946             :     // get credentials for the given user:pass
     947             :     //
     948             :     // always store the credentials we're trying now so that they will be used
     949             :     // on subsequent links.  This will potentially remove good credentials from
     950             :     // the cache.  This is ok as we don't want to use cached credentials if the
     951             :     // user specified something on the URI or in another manner.  This is so
     952             :     // that we don't transparently authenticate as someone they're not
     953             :     // expecting to authenticate as.
     954             :     //
     955           0 :     nsXPIDLCString result;
     956           0 :     rv = GenCredsAndSetEntry(auth, proxyAuth, scheme.get(), host, port,
     957             :                              path.get(), realm.get(), challenge, *ident,
     958           0 :                              sessionStateGrip, getter_Copies(result));
     959           0 :     if (NS_SUCCEEDED(rv))
     960           0 :         creds = result;
     961           0 :     return rv;
     962             : }
     963             : 
     964             : bool
     965           0 : nsHttpChannelAuthProvider::BlockPrompt()
     966             : {
     967             :     // Verify that it's ok to prompt for credentials here, per spec
     968             :     // http://xhr.spec.whatwg.org/#the-send%28%29-method
     969             : 
     970           0 :     nsCOMPtr<nsIHttpChannelInternal> chanInternal = do_QueryInterface(mAuthChannel);
     971           0 :     MOZ_ASSERT(chanInternal);
     972             : 
     973           0 :     if (chanInternal->GetBlockAuthPrompt()) {
     974           0 :         LOG(("nsHttpChannelAuthProvider::BlockPrompt: Prompt is blocked "
     975             :              "[this=%p channel=%p]\n", this, mAuthChannel));
     976           0 :         return true;
     977             :     }
     978             : 
     979           0 :     nsCOMPtr<nsIChannel> chan = do_QueryInterface(mAuthChannel);
     980           0 :     nsCOMPtr<nsILoadInfo> loadInfo;
     981           0 :     chan->GetLoadInfo(getter_AddRefs(loadInfo));
     982             : 
     983             :     // We will treat loads w/o loadInfo as a top level document.
     984           0 :     bool topDoc = true;
     985           0 :     bool xhr = false;
     986             : 
     987           0 :     if (loadInfo) {
     988           0 :         if (loadInfo->GetExternalContentPolicyType() !=
     989             :             nsIContentPolicy::TYPE_DOCUMENT) {
     990           0 :             topDoc = false;
     991             :         }
     992           0 :         if (loadInfo->GetExternalContentPolicyType() ==
     993             :             nsIContentPolicy::TYPE_XMLHTTPREQUEST) {
     994           0 :             xhr = true;
     995             :         }
     996             : 
     997           0 :         if (!topDoc && !xhr) {
     998           0 :             nsCOMPtr<nsIURI> topURI;
     999           0 :             Unused << chanInternal->GetTopWindowURI(getter_AddRefs(topURI));
    1000             : 
    1001           0 :             if (!topURI) {
    1002             :                 // If we do not have topURI try the loadingPrincipal.
    1003           0 :                 nsCOMPtr<nsIPrincipal> loadingPrinc = loadInfo->LoadingPrincipal();
    1004           0 :                 if (loadingPrinc) {
    1005           0 :                     loadingPrinc->GetURI(getter_AddRefs(topURI));
    1006             :                 }
    1007             :             }
    1008             : 
    1009           0 :             if (!NS_SecurityCompareURIs(topURI, mURI, true)) {
    1010           0 :                 mCrossOrigin = true;
    1011             :             }
    1012             :         }
    1013             :     }
    1014             : 
    1015           0 :     if (gHttpHandler->IsTelemetryEnabled()) {
    1016           0 :         if (topDoc) {
    1017             :             Telemetry::Accumulate(Telemetry::HTTP_AUTH_DIALOG_STATS_2,
    1018           0 :                                   HTTP_AUTH_DIALOG_TOP_LEVEL_DOC);
    1019           0 :         } else if (!mCrossOrigin) {
    1020           0 :             if (xhr) {
    1021             :                 Telemetry::Accumulate(Telemetry::HTTP_AUTH_DIALOG_STATS_2,
    1022           0 :                                       HTTP_AUTH_DIALOG_SAME_ORIGIN_XHR);
    1023             :             } else {
    1024             :                 Telemetry::Accumulate(Telemetry::HTTP_AUTH_DIALOG_STATS_2,
    1025           0 :                                       HTTP_AUTH_DIALOG_SAME_ORIGIN_SUBRESOURCE);
    1026             :             }
    1027             :         } else {
    1028           0 :             Telemetry::Accumulate(Telemetry::HTTP_AUTH_DIALOG_STATS_2,
    1029           0 :                                   loadInfo->GetExternalContentPolicyType());
    1030             :         }
    1031             :     }
    1032             : 
    1033           0 :     switch (sAuthAllowPref) {
    1034             :     case SUBRESOURCE_AUTH_DIALOG_DISALLOW_ALL:
    1035             :         // Do not open the http-authentication credentials dialog for
    1036             :         // the sub-resources.
    1037           0 :         return !topDoc && !xhr;
    1038             :     case SUBRESOURCE_AUTH_DIALOG_DISALLOW_CROSS_ORIGIN:
    1039             :         // Open the http-authentication credentials dialog for
    1040             :         // the sub-resources only if they are not cross-origin.
    1041           0 :         return !topDoc && !xhr && mCrossOrigin;
    1042             :     case SUBRESOURCE_AUTH_DIALOG_ALLOW_ALL:
    1043             :         // Allow the http-authentication dialog for subresources.
    1044             :         // If pref network.auth.subresource-img-cross-origin-http-auth-allow
    1045             :         // is set, http-authentication dialog for image subresources is
    1046             :         // blocked.
    1047           0 :         if (!sImgCrossOriginAuthAllowPref &&
    1048           0 :             loadInfo &&
    1049           0 :             ((loadInfo->GetExternalContentPolicyType() == nsIContentPolicy::TYPE_IMAGE) ||
    1050           0 :              (loadInfo->GetExternalContentPolicyType() == nsIContentPolicy::TYPE_IMAGESET))) {
    1051           0 :             return true;
    1052             :         }
    1053           0 :         return false;
    1054             :     default:
    1055             :         // This is an invalid value.
    1056           0 :         MOZ_ASSERT(false, "A non valid value!");
    1057             :     }
    1058             :     return false;
    1059             : }
    1060             : 
    1061             : inline void
    1062           0 : GetAuthType(const char *challenge, nsCString &authType)
    1063             : {
    1064             :     const char *p;
    1065             : 
    1066             :     // get the challenge type
    1067           0 :     if ((p = strchr(challenge, ' ')) != nullptr)
    1068           0 :         authType.Assign(challenge, p - challenge);
    1069             :     else
    1070           0 :         authType.Assign(challenge);
    1071           0 : }
    1072             : 
    1073             : nsresult
    1074           0 : nsHttpChannelAuthProvider::GetAuthenticator(const char            *challenge,
    1075             :                                             nsCString             &authType,
    1076             :                                             nsIHttpAuthenticator **auth)
    1077             : {
    1078           0 :     LOG(("nsHttpChannelAuthProvider::GetAuthenticator [this=%p channel=%p]\n",
    1079             :         this, mAuthChannel));
    1080             : 
    1081           0 :     GetAuthType(challenge, authType);
    1082             : 
    1083             :     // normalize to lowercase
    1084           0 :     ToLowerCase(authType);
    1085             : 
    1086           0 :     nsAutoCString contractid;
    1087           0 :     contractid.Assign(NS_HTTP_AUTHENTICATOR_CONTRACTID_PREFIX);
    1088           0 :     contractid.Append(authType);
    1089             : 
    1090           0 :     return CallGetService(contractid.get(), auth);
    1091             : }
    1092             : 
    1093             : void
    1094           0 : nsHttpChannelAuthProvider::GetIdentityFromURI(uint32_t            authFlags,
    1095             :                                               nsHttpAuthIdentity &ident)
    1096             : {
    1097           0 :     LOG(("nsHttpChannelAuthProvider::GetIdentityFromURI [this=%p channel=%p]\n",
    1098             :         this, mAuthChannel));
    1099             : 
    1100           0 :     nsAutoString userBuf;
    1101           0 :     nsAutoString passBuf;
    1102             : 
    1103             :     // XXX i18n
    1104           0 :     nsAutoCString buf;
    1105           0 :     mURI->GetUsername(buf);
    1106           0 :     if (!buf.IsEmpty()) {
    1107           0 :         NS_UnescapeURL(buf);
    1108           0 :         CopyASCIItoUTF16(buf, userBuf);
    1109           0 :         mURI->GetPassword(buf);
    1110           0 :         if (!buf.IsEmpty()) {
    1111           0 :             NS_UnescapeURL(buf);
    1112           0 :             CopyASCIItoUTF16(buf, passBuf);
    1113             :         }
    1114             :     }
    1115             : 
    1116           0 :     if (!userBuf.IsEmpty()) {
    1117           0 :         SetIdent(ident, authFlags, (char16_t *) userBuf.get(),
    1118           0 :                  (char16_t *) passBuf.get());
    1119             :     }
    1120           0 : }
    1121             : 
    1122             : void
    1123           0 : nsHttpChannelAuthProvider::ParseRealm(const char *challenge,
    1124             :                                       nsACString &realm)
    1125             : {
    1126             :     //
    1127             :     // From RFC2617 section 1.2, the realm value is defined as such:
    1128             :     //
    1129             :     //    realm       = "realm" "=" realm-value
    1130             :     //    realm-value = quoted-string
    1131             :     //
    1132             :     // but, we'll accept anything after the the "=" up to the first space, or
    1133             :     // end-of-line, if the string is not quoted.
    1134             :     //
    1135             : 
    1136           0 :     const char *p = PL_strcasestr(challenge, "realm=");
    1137           0 :     if (p) {
    1138           0 :         bool has_quote = false;
    1139           0 :         p += 6;
    1140           0 :         if (*p == '"') {
    1141           0 :             has_quote = true;
    1142           0 :             p++;
    1143             :         }
    1144             : 
    1145             :         const char *end;
    1146           0 :         if (has_quote) {
    1147           0 :             end = p;
    1148           0 :             while (*end) {
    1149           0 :                 if (*end == '\\') {
    1150             :                     // escaped character, store that one instead if not zero
    1151           0 :                     if (!*++end)
    1152           0 :                         break;
    1153             :                 }
    1154           0 :                 else if (*end == '\"')
    1155             :                     // end of string
    1156           0 :                     break;
    1157             : 
    1158           0 :                 realm.Append(*end);
    1159           0 :                 ++end;
    1160             :             }
    1161             :         }
    1162             :         else {
    1163             :             // realm given without quotes
    1164           0 :             end = strchr(p, ' ');
    1165           0 :             if (end)
    1166           0 :                 realm.Assign(p, end - p);
    1167             :             else
    1168           0 :                 realm.Assign(p);
    1169             :         }
    1170             :     }
    1171           0 : }
    1172             : 
    1173             : 
    1174           0 : class nsHTTPAuthInformation : public nsAuthInformationHolder {
    1175             : public:
    1176           0 :     nsHTTPAuthInformation(uint32_t aFlags, const nsString& aRealm,
    1177             :                           const nsCString& aAuthType)
    1178           0 :         : nsAuthInformationHolder(aFlags, aRealm, aAuthType) {}
    1179             : 
    1180             :     void SetToHttpAuthIdentity(uint32_t authFlags,
    1181             :                                nsHttpAuthIdentity& identity);
    1182             : };
    1183             : 
    1184             : void
    1185           0 : nsHTTPAuthInformation::SetToHttpAuthIdentity(uint32_t authFlags,
    1186             :                                              nsHttpAuthIdentity& identity)
    1187             : {
    1188           0 :     DebugOnly<nsresult> rv = identity.Set(Domain().get(), User().get(), Password().get());
    1189           0 :     MOZ_ASSERT(NS_SUCCEEDED(rv));
    1190           0 : }
    1191             : 
    1192             : nsresult
    1193           0 : nsHttpChannelAuthProvider::PromptForIdentity(uint32_t            level,
    1194             :                                              bool                proxyAuth,
    1195             :                                              const char         *realm,
    1196             :                                              const char         *authType,
    1197             :                                              uint32_t            authFlags,
    1198             :                                              nsHttpAuthIdentity &ident)
    1199             : {
    1200           0 :     LOG(("nsHttpChannelAuthProvider::PromptForIdentity [this=%p channel=%p]\n",
    1201             :         this, mAuthChannel));
    1202             : 
    1203             :     nsresult rv;
    1204             : 
    1205           0 :     nsCOMPtr<nsIInterfaceRequestor> callbacks;
    1206           0 :     rv = mAuthChannel->GetNotificationCallbacks(getter_AddRefs(callbacks));
    1207           0 :     if (NS_FAILED(rv)) return rv;
    1208             : 
    1209           0 :     nsCOMPtr<nsILoadGroup> loadGroup;
    1210           0 :     rv = mAuthChannel->GetLoadGroup(getter_AddRefs(loadGroup));
    1211           0 :     if (NS_FAILED(rv)) return rv;
    1212             : 
    1213           0 :     nsCOMPtr<nsIAuthPrompt2> authPrompt;
    1214           0 :     GetAuthPrompt(callbacks, proxyAuth, getter_AddRefs(authPrompt));
    1215           0 :     if (!authPrompt && loadGroup) {
    1216           0 :         nsCOMPtr<nsIInterfaceRequestor> cbs;
    1217           0 :         loadGroup->GetNotificationCallbacks(getter_AddRefs(cbs));
    1218           0 :         GetAuthPrompt(cbs, proxyAuth, getter_AddRefs(authPrompt));
    1219             :     }
    1220           0 :     if (!authPrompt)
    1221           0 :         return NS_ERROR_NO_INTERFACE;
    1222             : 
    1223             :     // XXX i18n: need to support non-ASCII realm strings (see bug 41489)
    1224           0 :     NS_ConvertASCIItoUTF16 realmU(realm);
    1225             : 
    1226             :     // prompt the user...
    1227           0 :     uint32_t promptFlags = 0;
    1228           0 :     if (proxyAuth)
    1229             :     {
    1230           0 :         promptFlags |= nsIAuthInformation::AUTH_PROXY;
    1231           0 :         if (mTriedProxyAuth)
    1232           0 :             promptFlags |= nsIAuthInformation::PREVIOUS_FAILED;
    1233           0 :         mTriedProxyAuth = true;
    1234             :     }
    1235             :     else {
    1236           0 :         promptFlags |= nsIAuthInformation::AUTH_HOST;
    1237           0 :         if (mTriedHostAuth)
    1238           0 :             promptFlags |= nsIAuthInformation::PREVIOUS_FAILED;
    1239           0 :         mTriedHostAuth = true;
    1240             :     }
    1241             : 
    1242           0 :     if (authFlags & nsIHttpAuthenticator::IDENTITY_INCLUDES_DOMAIN)
    1243           0 :         promptFlags |= nsIAuthInformation::NEED_DOMAIN;
    1244             : 
    1245           0 :     if (mCrossOrigin) {
    1246           0 :         promptFlags |= nsIAuthInformation::CROSS_ORIGIN_SUB_RESOURCE;
    1247             :     }
    1248             : 
    1249             :     RefPtr<nsHTTPAuthInformation> holder =
    1250             :         new nsHTTPAuthInformation(promptFlags, realmU,
    1251           0 :                                   nsDependentCString(authType));
    1252           0 :     if (!holder)
    1253           0 :         return NS_ERROR_OUT_OF_MEMORY;
    1254             : 
    1255           0 :     nsCOMPtr<nsIChannel> channel(do_QueryInterface(mAuthChannel, &rv));
    1256           0 :     if (NS_FAILED(rv)) return rv;
    1257             : 
    1258           0 :     rv =
    1259           0 :         authPrompt->AsyncPromptAuth(channel, this, nullptr, level, holder,
    1260           0 :                                     getter_AddRefs(mAsyncPromptAuthCancelable));
    1261             : 
    1262           0 :     if (NS_SUCCEEDED(rv)) {
    1263             :         // indicate using this error code that authentication prompt
    1264             :         // result is expected asynchronously
    1265           0 :         rv = NS_ERROR_IN_PROGRESS;
    1266             :     }
    1267             :     else {
    1268             :         // Fall back to synchronous prompt
    1269           0 :         bool retval = false;
    1270           0 :         rv = authPrompt->PromptAuth(channel, level, holder, &retval);
    1271           0 :         if (NS_FAILED(rv))
    1272           0 :             return rv;
    1273             : 
    1274           0 :         if (!retval)
    1275           0 :             rv = NS_ERROR_ABORT;
    1276             :         else
    1277           0 :             holder->SetToHttpAuthIdentity(authFlags, ident);
    1278             :     }
    1279             : 
    1280             :     // remember that we successfully showed the user an auth dialog
    1281           0 :     if (!proxyAuth)
    1282           0 :         mSuppressDefensiveAuth = true;
    1283             : 
    1284           0 :     if (mConnectionBased) {
    1285             :         // Connection can be reset by the server in the meantime user is entering
    1286             :         // the credentials.  Result would be just a "Connection was reset" error.
    1287             :         // Hence, we drop the current regardless if the user would make it on time
    1288             :         // to provide credentials.
    1289             :         // It's OK to send the NTLM type 1 message (response to the plain "NTLM"
    1290             :         // challenge) on a new connection.
    1291             :         {
    1292           0 :             DebugOnly<nsresult> rv = mAuthChannel->CloseStickyConnection();
    1293           0 :             MOZ_ASSERT(NS_SUCCEEDED(rv));
    1294             :         }
    1295             :     }
    1296             : 
    1297           0 :     return rv;
    1298             : }
    1299             : 
    1300           0 : NS_IMETHODIMP nsHttpChannelAuthProvider::OnAuthAvailable(nsISupports *aContext,
    1301             :                                                          nsIAuthInformation *aAuthInfo)
    1302             : {
    1303           0 :     LOG(("nsHttpChannelAuthProvider::OnAuthAvailable [this=%p channel=%p]",
    1304             :         this, mAuthChannel));
    1305             : 
    1306           0 :     mAsyncPromptAuthCancelable = nullptr;
    1307           0 :     if (!mAuthChannel)
    1308           0 :         return NS_OK;
    1309             : 
    1310             :     nsresult rv;
    1311             : 
    1312             :     const char *host;
    1313             :     int32_t port;
    1314             :     nsHttpAuthIdentity *ident;
    1315           0 :     nsAutoCString path, scheme;
    1316             :     nsISupports **continuationState;
    1317           0 :     rv = GetAuthorizationMembers(mProxyAuth, scheme, host, port,
    1318           0 :                                  path, ident, continuationState);
    1319           0 :     if (NS_FAILED(rv))
    1320           0 :         OnAuthCancelled(aContext, false);
    1321             : 
    1322           0 :     nsAutoCString realm;
    1323           0 :     ParseRealm(mCurrentChallenge.get(), realm);
    1324             : 
    1325           0 :     nsCOMPtr<nsIChannel> chan = do_QueryInterface(mAuthChannel);
    1326           0 :     nsAutoCString suffix;
    1327           0 :     GetOriginAttributesSuffix(chan, suffix);
    1328             : 
    1329           0 :     nsHttpAuthCache *authCache = gHttpHandler->AuthCache(mIsPrivate);
    1330           0 :     nsHttpAuthEntry *entry = nullptr;
    1331           0 :     Unused << authCache->GetAuthEntryForDomain(scheme.get(), host, port,
    1332             :                                                realm.get(), suffix,
    1333             :                                                &entry);
    1334             : 
    1335           0 :     nsCOMPtr<nsISupports> sessionStateGrip;
    1336           0 :     if (entry)
    1337           0 :         sessionStateGrip = entry->mMetaData;
    1338             : 
    1339             :     nsAuthInformationHolder* holder =
    1340           0 :             static_cast<nsAuthInformationHolder*>(aAuthInfo);
    1341           0 :     rv = ident->Set(holder->Domain().get(),
    1342           0 :                     holder->User().get(),
    1343           0 :                     holder->Password().get());
    1344           0 :     MOZ_ASSERT(NS_SUCCEEDED(rv));
    1345             : 
    1346           0 :     nsAutoCString unused;
    1347           0 :     nsCOMPtr<nsIHttpAuthenticator> auth;
    1348           0 :     rv = GetAuthenticator(mCurrentChallenge.get(), unused,
    1349           0 :                           getter_AddRefs(auth));
    1350           0 :     if (NS_FAILED(rv)) {
    1351           0 :         MOZ_ASSERT(false, "GetAuthenticator failed");
    1352             :         OnAuthCancelled(aContext, true);
    1353             :         return NS_OK;
    1354             :     }
    1355             : 
    1356           0 :     nsXPIDLCString creds;
    1357           0 :     rv = GenCredsAndSetEntry(auth, mProxyAuth,
    1358             :                              scheme.get(), host, port, path.get(),
    1359             :                              realm.get(), mCurrentChallenge.get(), *ident,
    1360           0 :                              sessionStateGrip, getter_Copies(creds));
    1361             : 
    1362           0 :     mCurrentChallenge.Truncate();
    1363           0 :     if (NS_FAILED(rv)) {
    1364           0 :         OnAuthCancelled(aContext, true);
    1365           0 :         return NS_OK;
    1366             :     }
    1367             : 
    1368           0 :     return ContinueOnAuthAvailable(creds);
    1369             : }
    1370             : 
    1371           0 : NS_IMETHODIMP nsHttpChannelAuthProvider::OnAuthCancelled(nsISupports *aContext,
    1372             :                                                          bool        userCancel)
    1373             : {
    1374           0 :     LOG(("nsHttpChannelAuthProvider::OnAuthCancelled [this=%p channel=%p]",
    1375             :         this, mAuthChannel));
    1376             : 
    1377           0 :     mAsyncPromptAuthCancelable = nullptr;
    1378           0 :     if (!mAuthChannel)
    1379           0 :         return NS_OK;
    1380             : 
    1381             :     // When user cancels or auth fails we want to close the connection for
    1382             :     // connection based schemes like NTLM.  Some servers don't like re-negotiation
    1383             :     // on the same connection.
    1384             :     nsresult rv;
    1385           0 :     if (mConnectionBased) {
    1386           0 :         rv = mAuthChannel->CloseStickyConnection();
    1387           0 :         MOZ_ASSERT(NS_SUCCEEDED(rv));
    1388           0 :         mConnectionBased = false;
    1389             :     }
    1390             : 
    1391           0 :     if (userCancel) {
    1392           0 :         if (!mRemainingChallenges.IsEmpty()) {
    1393             :             // there are still some challenges to process, do so
    1394             : 
    1395             :             // Get rid of current continuationState to avoid reusing it in
    1396             :             // next challenges since it is no longer relevant.
    1397           0 :             if (mProxyAuth) {
    1398           0 :                 NS_IF_RELEASE(mProxyAuthContinuationState);
    1399             :             } else {
    1400           0 :                 NS_IF_RELEASE(mAuthContinuationState);
    1401             :             }
    1402           0 :             nsAutoCString creds;
    1403           0 :             rv = GetCredentials(mRemainingChallenges.get(), mProxyAuth, creds);
    1404           0 :             if (NS_SUCCEEDED(rv)) {
    1405             :                 // GetCredentials loaded the credentials from the cache or
    1406             :                 // some other way in a synchronous manner, process those
    1407             :                 // credentials now
    1408           0 :                 mRemainingChallenges.Truncate();
    1409           0 :                 return ContinueOnAuthAvailable(creds);
    1410             :             }
    1411           0 :             if (rv == NS_ERROR_IN_PROGRESS) {
    1412             :                 // GetCredentials successfully queued another authprompt for
    1413             :                 // a challenge from the list, we are now waiting for the user
    1414             :                 // to provide the credentials
    1415           0 :                 return NS_OK;
    1416             :             }
    1417             : 
    1418             :             // otherwise, we failed...
    1419             :         }
    1420             : 
    1421           0 :         mRemainingChallenges.Truncate();
    1422             :     }
    1423             : 
    1424           0 :     rv = mAuthChannel->OnAuthCancelled(userCancel);
    1425           0 :     MOZ_ASSERT(NS_SUCCEEDED(rv));
    1426             : 
    1427           0 :     return NS_OK;
    1428             : }
    1429             : 
    1430           0 : NS_IMETHODIMP nsHttpChannelAuthProvider::OnCredsGenerated(const char *aGeneratedCreds,
    1431             :                                                           uint32_t aFlags,
    1432             :                                                           nsresult aResult,
    1433             :                                                           nsISupports* aSessionState,
    1434             :                                                           nsISupports* aContinuationState)
    1435             : {
    1436             :     nsresult rv;
    1437             : 
    1438           0 :     MOZ_ASSERT(NS_IsMainThread());
    1439             : 
    1440             :     // When channel is closed, do not proceed
    1441           0 :     if (!mAuthChannel) {
    1442           0 :         return NS_OK;
    1443             :     }
    1444             : 
    1445           0 :     mGenerateCredentialsCancelable = nullptr;
    1446             : 
    1447           0 :     if (NS_FAILED(aResult)) {
    1448           0 :         return OnAuthCancelled(nullptr, true);
    1449             :     }
    1450             : 
    1451             :     // We want to update m(Proxy)AuthContinuationState in case it was changed by
    1452             :     // nsHttpNegotiateAuth::GenerateCredentials
    1453           0 :     nsCOMPtr<nsISupports> contState(aContinuationState);
    1454           0 :     if (mProxyAuth) {
    1455           0 :         contState.swap(mProxyAuthContinuationState);
    1456             :     } else {
    1457           0 :         contState.swap(mAuthContinuationState);
    1458             :     }
    1459             : 
    1460           0 :     nsCOMPtr<nsIHttpAuthenticator> auth;
    1461           0 :     nsAutoCString unused;
    1462           0 :     rv = GetAuthenticator(mCurrentChallenge.get(), unused, getter_AddRefs(auth));
    1463           0 :     NS_ENSURE_SUCCESS(rv, rv);
    1464             : 
    1465             :     const char *host;
    1466             :     int32_t port;
    1467             :     nsHttpAuthIdentity *ident;
    1468           0 :     nsAutoCString directory, scheme;
    1469             :     nsISupports **unusedContinuationState;
    1470             : 
    1471             :     // Get realm from challenge
    1472           0 :     nsAutoCString realm;
    1473           0 :     ParseRealm(mCurrentChallenge.get(), realm);
    1474             : 
    1475           0 :     rv = GetAuthorizationMembers(mProxyAuth, scheme, host, port,
    1476           0 :                                  directory, ident, unusedContinuationState);
    1477           0 :     if (NS_FAILED(rv)) return rv;
    1478             : 
    1479           0 :     rv = UpdateCache(auth, scheme.get(), host, port, directory.get(),
    1480             :                      realm.get(), mCurrentChallenge.get(), *ident,
    1481           0 :                      aGeneratedCreds, aFlags, aSessionState);
    1482           0 :     MOZ_ASSERT(NS_SUCCEEDED(rv));
    1483           0 :     mCurrentChallenge.Truncate();
    1484             : 
    1485           0 :     rv = ContinueOnAuthAvailable(nsDependentCString(aGeneratedCreds));
    1486           0 :     MOZ_ASSERT(NS_SUCCEEDED(rv));
    1487           0 :     return NS_OK;
    1488             : }
    1489             : 
    1490             : nsresult
    1491           0 : nsHttpChannelAuthProvider::ContinueOnAuthAvailable(const nsACString& creds)
    1492             : {
    1493             :     nsresult rv;
    1494           0 :     if (mProxyAuth)
    1495           0 :         rv = mAuthChannel->SetProxyCredentials(creds);
    1496             :     else
    1497           0 :         rv = mAuthChannel->SetWWWCredentials(creds);
    1498           0 :     if (NS_FAILED(rv)) return rv;
    1499             : 
    1500             :     // drop our remaining list of challenges.  We don't need them, because we
    1501             :     // have now authenticated against a challenge and will be sending that
    1502             :     // information to the server (or proxy).  If it doesn't accept our
    1503             :     // authentication it'll respond with failure and resend the challenge list
    1504           0 :     mRemainingChallenges.Truncate();
    1505             : 
    1506           0 :     Unused << mAuthChannel->OnAuthAvailable();
    1507             : 
    1508           0 :     return NS_OK;
    1509             : }
    1510             : 
    1511             : bool
    1512           3 : nsHttpChannelAuthProvider::ConfirmAuth(const nsString &bundleKey,
    1513             :                                        bool            doYesNoPrompt)
    1514             : {
    1515             :     // skip prompting the user if
    1516             :     //   1) we've already prompted the user
    1517             :     //   2) we're not a toplevel channel
    1518             :     //   3) the userpass length is less than the "phishy" threshold
    1519             : 
    1520             :     uint32_t loadFlags;
    1521           3 :     nsresult rv = mAuthChannel->GetLoadFlags(&loadFlags);
    1522           3 :     if (NS_FAILED(rv))
    1523           0 :         return true;
    1524             : 
    1525           6 :     if (mSuppressDefensiveAuth ||
    1526           6 :         !(loadFlags & nsIChannel::LOAD_INITIAL_DOCUMENT_URI))
    1527           2 :         return true;
    1528             : 
    1529           2 :     nsAutoCString userPass;
    1530           1 :     rv = mURI->GetUserPass(userPass);
    1531           2 :     if (NS_FAILED(rv) ||
    1532           1 :         (userPass.Length() < gHttpHandler->PhishyUserPassLength()))
    1533           1 :         return true;
    1534             : 
    1535             :     // we try to confirm by prompting the user.  if we cannot do so, then
    1536             :     // assume the user said ok.  this is done to keep things working in
    1537             :     // embedded builds, where the string bundle might not be present, etc.
    1538             : 
    1539             :     nsCOMPtr<nsIStringBundleService> bundleService =
    1540           0 :             do_GetService(NS_STRINGBUNDLE_CONTRACTID);
    1541           0 :     if (!bundleService)
    1542           0 :         return true;
    1543             : 
    1544           0 :     nsCOMPtr<nsIStringBundle> bundle;
    1545           0 :     bundleService->CreateBundle(NECKO_MSGS_URL, getter_AddRefs(bundle));
    1546           0 :     if (!bundle)
    1547           0 :         return true;
    1548             : 
    1549           0 :     nsAutoCString host;
    1550           0 :     rv = mURI->GetHost(host);
    1551           0 :     if (NS_FAILED(rv))
    1552           0 :         return true;
    1553             : 
    1554           0 :     nsAutoCString user;
    1555           0 :     rv = mURI->GetUsername(user);
    1556           0 :     if (NS_FAILED(rv))
    1557           0 :         return true;
    1558             : 
    1559           0 :     NS_ConvertUTF8toUTF16 ucsHost(host), ucsUser(user);
    1560             : 
    1561           0 :     size_t userLength = ucsUser.Length();
    1562           0 :     if (userLength > MAX_DISPLAYED_USER_LENGTH) {
    1563           0 :       size_t desiredLength = MAX_DISPLAYED_USER_LENGTH;
    1564             :       // Don't cut off right before a low surrogate. Just include it.
    1565           0 :       if (NS_IS_LOW_SURROGATE(ucsUser[desiredLength])) {
    1566           0 :         desiredLength++;
    1567             :       }
    1568           0 :       ucsUser.Replace(desiredLength, userLength - desiredLength,
    1569           0 :                       nsContentUtils::GetLocalizedEllipsis());
    1570             :     }
    1571             : 
    1572           0 :     size_t hostLen = ucsHost.Length();
    1573           0 :     if (hostLen > MAX_DISPLAYED_HOST_LENGTH) {
    1574           0 :       size_t cutPoint = hostLen - MAX_DISPLAYED_HOST_LENGTH;
    1575             :       // Likewise, don't cut off right before a low surrogate here.
    1576             :       // Keep the low surrogate
    1577           0 :       if (NS_IS_LOW_SURROGATE(ucsHost[cutPoint])) {
    1578           0 :         cutPoint--;
    1579             :       }
    1580             :       // It's possible cutPoint was 1 and is now 0. Only insert the ellipsis
    1581             :       // if we're actually removing anything.
    1582           0 :       if (cutPoint > 0) {
    1583           0 :         ucsHost.Replace(0, cutPoint, nsContentUtils::GetLocalizedEllipsis());
    1584             :       }
    1585             :     }
    1586             : 
    1587           0 :     const char16_t *strs[2] = { ucsHost.get(), ucsUser.get() };
    1588             : 
    1589           0 :     nsXPIDLString msg;
    1590           0 :     bundle->FormatStringFromName(bundleKey.get(), strs, 2, getter_Copies(msg));
    1591           0 :     if (!msg)
    1592           0 :         return true;
    1593             : 
    1594           0 :     nsCOMPtr<nsIInterfaceRequestor> callbacks;
    1595           0 :     rv = mAuthChannel->GetNotificationCallbacks(getter_AddRefs(callbacks));
    1596           0 :     if (NS_FAILED(rv))
    1597           0 :         return true;
    1598             : 
    1599           0 :     nsCOMPtr<nsILoadGroup> loadGroup;
    1600           0 :     rv = mAuthChannel->GetLoadGroup(getter_AddRefs(loadGroup));
    1601           0 :     if (NS_FAILED(rv))
    1602           0 :         return true;
    1603             : 
    1604           0 :     nsCOMPtr<nsIPrompt> prompt;
    1605           0 :     NS_QueryNotificationCallbacks(callbacks, loadGroup, NS_GET_IID(nsIPrompt),
    1606           0 :                                   getter_AddRefs(prompt));
    1607           0 :     if (!prompt)
    1608           0 :         return true;
    1609             : 
    1610             :     // do not prompt again
    1611           0 :     mSuppressDefensiveAuth = true;
    1612             : 
    1613             :     bool confirmed;
    1614           0 :     if (doYesNoPrompt) {
    1615             :         int32_t choice;
    1616           0 :         bool checkState = false;
    1617           0 :         rv = prompt->ConfirmEx(nullptr, msg,
    1618             :                                nsIPrompt::BUTTON_POS_1_DEFAULT +
    1619             :                                nsIPrompt::STD_YES_NO_BUTTONS,
    1620             :                                nullptr, nullptr, nullptr, nullptr,
    1621           0 :                                &checkState, &choice);
    1622           0 :         if (NS_FAILED(rv))
    1623           0 :             return true;
    1624             : 
    1625           0 :         confirmed = choice == 0;
    1626             :     }
    1627             :     else {
    1628           0 :         rv = prompt->Confirm(nullptr, msg, &confirmed);
    1629           0 :         if (NS_FAILED(rv))
    1630           0 :             return true;
    1631             :     }
    1632             : 
    1633           0 :     return confirmed;
    1634             : }
    1635             : 
    1636             : void
    1637           6 : nsHttpChannelAuthProvider::SetAuthorizationHeader(nsHttpAuthCache    *authCache,
    1638             :                                                   nsHttpAtom          header,
    1639             :                                                   const char         *scheme,
    1640             :                                                   const char         *host,
    1641             :                                                   int32_t             port,
    1642             :                                                   const char         *path,
    1643             :                                                   nsHttpAuthIdentity &ident)
    1644             : {
    1645           6 :     nsHttpAuthEntry *entry = nullptr;
    1646             :     nsresult rv;
    1647             : 
    1648             :     // set informations that depend on whether
    1649             :     // we're authenticating against a proxy
    1650             :     // or a webserver
    1651             :     nsISupports **continuationState;
    1652             : 
    1653           6 :     if (header == nsHttp::Proxy_Authorization) {
    1654           0 :         continuationState = &mProxyAuthContinuationState;
    1655             :     } else {
    1656           6 :         continuationState = &mAuthContinuationState;
    1657             :     }
    1658             : 
    1659          12 :     nsCOMPtr<nsIChannel> chan = do_QueryInterface(mAuthChannel);
    1660          12 :     nsAutoCString suffix;
    1661           6 :     GetOriginAttributesSuffix(chan, suffix);
    1662             : 
    1663             :     rv = authCache->GetAuthEntryForPath(scheme, host, port, path,
    1664           6 :                                         suffix, &entry);
    1665           6 :     if (NS_SUCCEEDED(rv)) {
    1666             :         // if we are trying to add a header for origin server auth and if the
    1667             :         // URL contains an explicit username, then try the given username first.
    1668             :         // we only want to do this, however, if we know the URL requires auth
    1669             :         // based on the presence of an auth cache entry for this URL (which is
    1670             :         // true since we are here).  but, if the username from the URL matches
    1671             :         // the username from the cache, then we should prefer the password
    1672             :         // stored in the cache since that is most likely to be valid.
    1673           0 :         if (header == nsHttp::Authorization && entry->Domain()[0] == '\0') {
    1674           0 :             GetIdentityFromURI(0, ident);
    1675             :             // if the usernames match, then clear the ident so we will pick
    1676             :             // up the one from the auth cache instead.
    1677             :             // when this is undesired, specify LOAD_EXPLICIT_CREDENTIALS load
    1678             :             // flag.
    1679           0 :             if (nsCRT::strcmp(ident.User(), entry->User()) == 0) {
    1680             :                 uint32_t loadFlags;
    1681           0 :                 if (NS_SUCCEEDED(mAuthChannel->GetLoadFlags(&loadFlags)) &&
    1682           0 :                     !(loadFlags & nsIChannel::LOAD_EXPLICIT_CREDENTIALS)) {
    1683           0 :                     ident.Clear();
    1684             :                 }
    1685             :             }
    1686             :         }
    1687             :         bool identFromURI;
    1688           0 :         if (ident.IsEmpty()) {
    1689           0 :             rv = ident.Set(entry->Identity());
    1690           0 :             MOZ_ASSERT(NS_SUCCEEDED(rv));
    1691           0 :             identFromURI = false;
    1692             :         }
    1693             :         else
    1694           0 :             identFromURI = true;
    1695             : 
    1696           0 :         nsXPIDLCString temp;
    1697           0 :         const char *creds     = entry->Creds();
    1698           0 :         const char *challenge = entry->Challenge();
    1699             :         // we can only send a preemptive Authorization header if we have either
    1700             :         // stored credentials or a stored challenge from which to derive
    1701             :         // credentials.  if the identity is from the URI, then we cannot use
    1702             :         // the stored credentials.
    1703           0 :         if ((!creds[0] || identFromURI) && challenge[0]) {
    1704           0 :             nsCOMPtr<nsIHttpAuthenticator> auth;
    1705           0 :             nsAutoCString unused;
    1706           0 :             rv = GetAuthenticator(challenge, unused, getter_AddRefs(auth));
    1707           0 :             if (NS_SUCCEEDED(rv)) {
    1708           0 :                 bool proxyAuth = (header == nsHttp::Proxy_Authorization);
    1709           0 :                 rv = GenCredsAndSetEntry(auth, proxyAuth, scheme, host, port,
    1710             :                                          path, entry->Realm(), challenge, ident,
    1711           0 :                                          entry->mMetaData, getter_Copies(temp));
    1712           0 :                 if (NS_SUCCEEDED(rv))
    1713           0 :                     creds = temp.get();
    1714             : 
    1715             :                 // make sure the continuation state is null since we do not
    1716             :                 // support mixing preemptive and 'multirequest' authentication.
    1717           0 :                 NS_IF_RELEASE(*continuationState);
    1718             :             }
    1719             :         }
    1720           0 :         if (creds[0]) {
    1721           0 :             LOG(("   adding \"%s\" request header\n", header.get()));
    1722           0 :             if (header == nsHttp::Proxy_Authorization) {
    1723           0 :                 rv = mAuthChannel->SetProxyCredentials(nsDependentCString(creds));
    1724           0 :                 MOZ_ASSERT(NS_SUCCEEDED(rv));
    1725             :             }
    1726             :             else {
    1727           0 :                 rv = mAuthChannel->SetWWWCredentials(nsDependentCString(creds));
    1728           0 :                 MOZ_ASSERT(NS_SUCCEEDED(rv));
    1729             :             }
    1730             : 
    1731             :             // suppress defensive auth prompting for this channel since we know
    1732             :             // that we already prompted at least once this session.  we only do
    1733             :             // this for non-proxy auth since the URL's userpass is not used for
    1734             :             // proxy auth.
    1735           0 :             if (header == nsHttp::Authorization)
    1736           0 :                 mSuppressDefensiveAuth = true;
    1737             :         }
    1738             :         else
    1739           0 :             ident.Clear(); // don't remember the identity
    1740             :     }
    1741           6 : }
    1742             : 
    1743             : nsresult
    1744           6 : nsHttpChannelAuthProvider::GetCurrentPath(nsACString &path)
    1745             : {
    1746             :     nsresult rv;
    1747          12 :     nsCOMPtr<nsIURL> url = do_QueryInterface(mURI);
    1748           6 :     if (url)
    1749           6 :         rv = url->GetDirectory(path);
    1750             :     else
    1751           0 :         rv = mURI->GetPath(path);
    1752          12 :     return rv;
    1753             : }
    1754             : 
    1755          48 : NS_IMPL_ISUPPORTS(nsHttpChannelAuthProvider, nsICancelable,
    1756             :                   nsIHttpChannelAuthProvider, nsIAuthPromptCallback, nsIHttpAuthenticatorCallback)
    1757             : 
    1758             : } // namespace net
    1759             : } // namespace mozilla

Generated by: LCOV version 1.13