LCOV - code coverage report
Current view: directory - netwerk/protocol/http - nsHttpChannelAuthProvider.cpp (source / functions) Found Hit Coverage
Test: app.info Lines: 552 399 72.3 %
Date: 2012-06-02 Functions: 33 31 93.9 %

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

Generated by: LCOV version 1.7