netwerk/protocol/http/nsHttpChannelAuthProvider.cpp

Thu, 15 Jan 2015 21:03:48 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 15 Jan 2015 21:03:48 +0100
branch
TOR_BUG_9701
changeset 11
deefc01c0e14
permissions
-rw-r--r--

Integrate friendly tips from Tor colleagues to make (or not) 4.5 alpha 3;
This includes removal of overloaded (but unused) methods, and addition of
a overlooked call to DataStruct::SetData(nsISupports, uint32_t, bool.)

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

mercurial