LCOV - app.info - netwerk/protocol/http/nsHttpChannelAuthProvider.cpp

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-07-07 | 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            3513 : nsHttpChannelAuthProvider::nsHttpChannelAuthProvider()
          61                 :     : mAuthChannel(nsnull)
          62                 :     , mProxyAuthContinuationState(nsnull)
          63                 :     , mAuthContinuationState(nsnull)
          64                 :     , mProxyAuth(false)
          65                 :     , mTriedProxyAuth(false)
          66                 :     , mTriedHostAuth(false)
          67            3513 :     , mSuppressDefensiveAuth(false)
          68                 : {
          69                 :     // grab a reference to the handler to ensure that it doesn't go away.
          70            3513 :     nsHttpHandler *handler = gHttpHandler;
          71            3513 :     NS_ADDREF(handler);
          72            3513 : }
          73                 :
          74            9717 : nsHttpChannelAuthProvider::~nsHttpChannelAuthProvider()
          75                 : {
          76            3239 :     NS_ASSERTION(!mAuthChannel, "Disconnect wasn't called");
          77                 :
          78                 :     // release our reference to the handler
          79            3239 :     nsHttpHandler *handler = gHttpHandler;
          80            3239 :     NS_RELEASE(handler);
          81           12956 : }
          82                 :
          83                 : NS_IMETHODIMP
          84            3513 : nsHttpChannelAuthProvider::Init(nsIHttpAuthenticableChannel *channel)
          85                 : {
          86            3513 :     NS_ASSERTION(channel, "channel expected!");
          87                 :
          88            3513 :     mAuthChannel = channel;
          89                 :
          90            3513 :     nsresult rv = mAuthChannel->GetURI(getter_AddRefs(mURI));
          91            3513 :     if (NS_FAILED(rv)) return rv;
          92                 :
          93            3513 :     mAuthChannel->GetIsSSL(&mUsingSSL);
          94            3513 :     if (NS_FAILED(rv)) return rv;
          95                 :
          96            3513 :     rv = mURI->GetAsciiHost(mHost);
          97            3513 :     if (NS_FAILED(rv)) return rv;
          98                 :
          99                 :     // reject the URL if it doesn't specify a host
         100            3513 :     if (mHost.IsEmpty())
         101               0 :         return NS_ERROR_MALFORMED_URI;
         102                 :
         103            3513 :     rv = mURI->GetPort(&mPort);
         104            3513 :     if (NS_FAILED(rv)) return rv;
         105                 :
         106            3513 :     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            3482 : nsHttpChannelAuthProvider::AddAuthorizationHeaders()
         187                 : {
         188            3482 :     LOG(("nsHttpChannelAuthProvider::AddAuthorizationHeaders? "
         189                 :          "[this=%p channel=%p]\n", this, mAuthChannel));
         190                 :
         191            3482 :     NS_ASSERTION(mAuthChannel, "Channel not initialized");
         192                 :
         193            6964 :     nsCOMPtr<nsIProxyInfo> proxyInfo;
         194            3482 :     nsresult rv = mAuthChannel->GetProxyInfo(getter_AddRefs(proxyInfo));
         195            3482 :     if (NS_FAILED(rv)) return rv;
         196            3482 :     if (proxyInfo) {
         197              22 :         mProxyInfo = do_QueryInterface(proxyInfo);
         198              22 :         if (!mProxyInfo) return NS_ERROR_NO_INTERFACE;
         199                 :     }
         200                 :
         201                 :     PRUint32 loadFlags;
         202            3482 :     rv = mAuthChannel->GetLoadFlags(&loadFlags);
         203            3482 :     if (NS_FAILED(rv)) return rv;
         204                 :
         205                 :     // this getter never fails
         206            3482 :     nsHttpAuthCache *authCache = gHttpHandler->AuthCache();
         207                 :
         208                 :     // check if proxy credentials should be sent
         209            3482 :     const char *proxyHost = ProxyHost();
         210            3482 :     if (proxyHost && UsingHttpProxy())
         211                 :         SetAuthorizationHeader(authCache, nsHttp::Proxy_Authorization,
         212                 :                                "http", proxyHost, ProxyPort(),
         213                 :                                nsnull, // proxy has no path
         214              12 :                                mProxyIdent);
         215                 :
         216            3482 :     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            6962 :     nsCAutoString path, scheme;
         223            6962 :     if (NS_SUCCEEDED(GetCurrentPath(path)) &&
         224            3481 :         NS_SUCCEEDED(mURI->GetScheme(scheme))) {
         225                 :         SetAuthorizationHeader(authCache, nsHttp::Authorization,
         226                 :                                scheme.get(),
         227                 :                                Host(),
         228                 :                                Port(),
         229                 :                                path.get(),
         230            3481 :                                mIdent);
         231                 :     }
         232                 :
         233            3481 :     return NS_OK;
         234                 : }
         235                 :
         236                 : NS_IMETHODIMP
         237            2792 : nsHttpChannelAuthProvider::CheckForSuperfluousAuth()
         238                 : {
         239            2792 :     LOG(("nsHttpChannelAuthProvider::CheckForSuperfluousAuth? "
         240                 :          "[this=%p channel=%p]\n", this, mAuthChannel));
         241                 :
         242            2792 :     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            2792 :     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            2792 :     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            3239 : nsHttpChannelAuthProvider::Disconnect(nsresult status)
         272                 : {
         273            3239 :     mAuthChannel = nsnull;
         274                 :
         275            3239 :     if (mAsyncPromptAuthCancelable) {
         276               0 :         mAsyncPromptAuthCancelable->Cancel(status);
         277               0 :         mAsyncPromptAuthCancelable = nsnull;
         278                 :     }
         279                 :
         280            3239 :     NS_IF_RELEASE(mProxyAuthContinuationState);
         281            3239 :     NS_IF_RELEASE(mAuthContinuationState);
         282                 :
         283            3239 :     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            2793 : 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            2793 :     nsresult rv = mAuthChannel->GetLoadFlags(&loadFlags);
        1125            2793 :     if (NS_FAILED(rv))
        1126               0 :         return true;
        1127                 :
        1128            5565 :     if (mSuppressDefensiveAuth ||
        1129            2772 :         !(loadFlags & nsIChannel::LOAD_INITIAL_DOCUMENT_URI))
        1130            2793 :         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            3493 : 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            3493 :     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            3493 :     if (header == nsHttp::Proxy_Authorization) {
        1232              12 :         continuationState = &mProxyAuthContinuationState;
        1233                 :     } else {
        1234            3481 :         continuationState = &mAuthContinuationState;
        1235                 :     }
        1236                 :
        1237            3493 :     rv = authCache->GetAuthEntryForPath(scheme, host, port, path, &entry);
        1238            3493 :     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            3493 : }
        1303                 :
        1304                 : nsresult
        1305            3518 : nsHttpChannelAuthProvider::GetCurrentPath(nsACString &path)
        1306                 : {
        1307                 :     nsresult rv;
        1308            7036 :     nsCOMPtr<nsIURL> url = do_QueryInterface(mURI);
        1309            3518 :     if (url)
        1310            3518 :         rv = url->GetDirectory(path);
        1311                 :     else
        1312               0 :         rv = mURI->GetPath(path);
        1313            3518 :     return rv;
        1314                 : }
        1315                 :
        1316           28174 : NS_IMPL_ISUPPORTS3(nsHttpChannelAuthProvider, nsICancelable,
        1317                 :                    nsIHttpChannelAuthProvider, nsIAuthPromptCallback)

Generated by: LCOV version 1.7