NS_IMETHODIMP nsHttpChannelAuthProvider::AddAuthorizationHeaders() { LOG(("nsHttpChannelAuthProvider::AddAuthorizationHeaders? " "[this=%p channel=%p]\n", this, mAuthChannel)); NS_ASSERTION(mAuthChannel, "Channel not initialized"); nsCOMPtr<nsIProxyInfo> proxyInfo; nsresult rv = mAuthChannel->GetProxyInfo(getter_AddRefs(proxyInfo)); if (NS_FAILED(rv)) return rv; if (proxyInfo) { mProxyInfo = do_QueryInterface(proxyInfo); if (!mProxyInfo) return NS_ERROR_NO_INTERFACE; } uint32_t loadFlags; rv = mAuthChannel->GetLoadFlags(&loadFlags); if (NS_FAILED(rv)) return rv; // this getter never fails nsHttpAuthCache *authCache = gHttpHandler->AuthCache(); // check if proxy credentials should be sent const char *proxyHost = ProxyHost(); if (proxyHost && UsingHttpProxy()) SetAuthorizationHeader(authCache, nsHttp::Proxy_Authorization, "http", proxyHost, ProxyPort(), nullptr, // proxy has no path mProxyIdent); if (loadFlags & nsIRequest::LOAD_ANONYMOUS) { LOG(("Skipping Authorization header for anonymous load\n")); return NS_OK; } // check if server credentials should be sent nsCAutoString path, scheme; if (NS_SUCCEEDED(GetCurrentPath(path)) && NS_SUCCEEDED(mURI->GetScheme(scheme))) { SetAuthorizationHeader(authCache, nsHttp::Authorization, scheme.get(), Host(), Port(), path.get(), mIdent); } return NS_OK; }
nsresult nsHttpChannelAuthProvider::PrepareForAuthentication(bool proxyAuth) { LOG(("nsHttpChannelAuthProvider::PrepareForAuthentication " "[this=%p channel=%p]\n", this, mAuthChannel)); if (!proxyAuth) { // reset the current proxy continuation state because our last // authentication attempt was completed successfully. NS_IF_RELEASE(mProxyAuthContinuationState); LOG((" proxy continuation state has been reset")); } if (!UsingHttpProxy() || mProxyAuthType.IsEmpty()) return NS_OK; // We need to remove any Proxy_Authorization header left over from a // non-request based authentication handshake (e.g., for NTLM auth). nsCAutoString contractId; contractId.Assign(NS_HTTP_AUTHENTICATOR_CONTRACTID_PREFIX); contractId.Append(mProxyAuthType); nsresult rv; nsCOMPtr<nsIHttpAuthenticator> precedingAuth = do_GetService(contractId.get(), &rv); if (NS_FAILED(rv)) return rv; uint32_t precedingAuthFlags; rv = precedingAuth->GetAuthFlags(&precedingAuthFlags); if (NS_FAILED(rv)) return rv; if (!(precedingAuthFlags & nsIHttpAuthenticator::REQUEST_BASED)) { nsCAutoString challenges; rv = mAuthChannel->GetProxyChallenges(challenges); if (NS_FAILED(rv)) { // delete the proxy authorization header because we weren't // asked to authenticate rv = mAuthChannel->SetProxyCredentials(EmptyCString()); if (NS_FAILED(rv)) return rv; LOG((" cleared proxy authorization header")); } } return NS_OK; }
nsresult nsHttpChannelAuthProvider::GetAuthorizationMembers(bool proxyAuth, nsCSubstring& scheme, const char*& host, int32_t& port, nsCSubstring& path, nsHttpAuthIdentity*& ident, nsISupports**& continuationState) { if (proxyAuth) { NS_ASSERTION (UsingHttpProxy(), "proxyAuth is true, but no HTTP proxy is configured!"); host = ProxyHost(); port = ProxyPort(); ident = &mProxyIdent; scheme.AssignLiteral("http"); continuationState = &mProxyAuthContinuationState; } else { host = Host(); port = Port(); ident = &mIdent; nsresult rv; rv = GetCurrentPath(path); if (NS_FAILED(rv)) return rv; rv = mURI->GetScheme(scheme); if (NS_FAILED(rv)) return rv; continuationState = &mAuthContinuationState; } return NS_OK; }
NS_IMETHODIMP nsHttpChannelAuthProvider::ProcessAuthentication(uint32_t httpStatus, bool SSLConnectFailed) { LOG(("nsHttpChannelAuthProvider::ProcessAuthentication " "[this=%p channel=%p code=%u SSLConnectFailed=%d]\n", this, mAuthChannel, httpStatus, SSLConnectFailed)); NS_ASSERTION(mAuthChannel, "Channel not initialized"); nsCOMPtr<nsIProxyInfo> proxyInfo; nsresult rv = mAuthChannel->GetProxyInfo(getter_AddRefs(proxyInfo)); if (NS_FAILED(rv)) return rv; if (proxyInfo) { mProxyInfo = do_QueryInterface(proxyInfo); if (!mProxyInfo) return NS_ERROR_NO_INTERFACE; } uint32_t loadFlags; rv = mAuthChannel->GetLoadFlags(&loadFlags); if (NS_FAILED(rv)) return rv; nsCAutoString challenges; mProxyAuth = (httpStatus == 407); // Do proxy auth even if we're LOAD_ANONYMOUS if ((loadFlags & nsIRequest::LOAD_ANONYMOUS) && (!mProxyAuth || !UsingHttpProxy())) { LOG(("Skipping authentication for anonymous non-proxy request\n")); return NS_ERROR_NOT_AVAILABLE; } rv = PrepareForAuthentication(mProxyAuth); if (NS_FAILED(rv)) return rv; if (mProxyAuth) { // only allow a proxy challenge if we have a proxy server configured. // otherwise, we could inadvertently expose the user's proxy // credentials to an origin server. We could attempt to proceed as // if we had received a 401 from the server, but why risk flirting // with trouble? IE similarly rejects 407s when a proxy server is // not configured, so there's no reason not to do the same. if (!UsingHttpProxy()) { LOG(("rejecting 407 when proxy server not configured!\n")); return NS_ERROR_UNEXPECTED; } if (UsingSSL() && !SSLConnectFailed) { // we need to verify that this challenge came from the proxy // server itself, and not some server on the other side of the // SSL tunnel. LOG(("rejecting 407 from origin server!\n")); return NS_ERROR_UNEXPECTED; } rv = mAuthChannel->GetProxyChallenges(challenges); } else rv = mAuthChannel->GetWWWChallenges(challenges); if (NS_FAILED(rv)) return rv; nsCAutoString creds; rv = GetCredentials(challenges.get(), mProxyAuth, creds); if (rv == NS_ERROR_IN_PROGRESS) return rv; if (NS_FAILED(rv)) LOG(("unable to authenticate\n")); else { // set the authentication credentials if (mProxyAuth) rv = mAuthChannel->SetProxyCredentials(creds); else rv = mAuthChannel->SetWWWCredentials(creds); } return rv; }
nsresult nsHttpChannelAuthProvider::GetCredentialsForChallenge(const char *challenge, const char *authType, bool proxyAuth, nsIHttpAuthenticator *auth, nsAFlatCString &creds) { LOG(("nsHttpChannelAuthProvider::GetCredentialsForChallenge " "[this=%p channel=%p proxyAuth=%d challenges=%s]\n", this, mAuthChannel, proxyAuth, challenge)); // this getter never fails nsHttpAuthCache *authCache = gHttpHandler->AuthCache(mIsPrivate); uint32_t authFlags; nsresult rv = auth->GetAuthFlags(&authFlags); if (NS_FAILED(rv)) return rv; nsAutoCString realm; ParseRealm(challenge, realm); // if no realm, then use the auth type as the realm. ToUpperCase so the // ficticious realm stands out a bit more. // XXX this will cause some single signon misses! // XXX this was meant to be used with NTLM, which supplies no realm. /* if (realm.IsEmpty()) { realm = authType; ToUpperCase(realm); } */ // set informations that depend on whether // we're authenticating against a proxy // or a webserver const char *host; int32_t port; nsHttpAuthIdentity *ident; nsAutoCString path, scheme; bool identFromURI = false; nsISupports **continuationState; rv = GetAuthorizationMembers(proxyAuth, scheme, host, port, path, ident, continuationState); if (NS_FAILED(rv)) return rv; uint32_t loadFlags; rv = mAuthChannel->GetLoadFlags(&loadFlags); if (NS_FAILED(rv)) return rv; if (!proxyAuth) { // if this is the first challenge, then try using the identity // specified in the URL. if (mIdent.IsEmpty()) { GetIdentityFromURI(authFlags, mIdent); identFromURI = !mIdent.IsEmpty(); } if ((loadFlags & nsIRequest::LOAD_ANONYMOUS) && !identFromURI) { LOG(("Skipping authentication for anonymous non-proxy request\n")); return NS_ERROR_NOT_AVAILABLE; } // Let explicit URL credentials pass // regardless of the LOAD_ANONYMOUS flag } else if ((loadFlags & nsIRequest::LOAD_ANONYMOUS) && !UsingHttpProxy()) { LOG(("Skipping authentication for anonymous non-proxy request\n")); return NS_ERROR_NOT_AVAILABLE; } nsCOMPtr<nsIChannel> chan = do_QueryInterface(mAuthChannel); uint32_t appId; bool isInBrowserElement; GetAppIdAndBrowserStatus(chan, &appId, &isInBrowserElement); // // if we already tried some credentials for this transaction, then // we need to possibly clear them from the cache, unless the credentials // in the cache have changed, in which case we'd want to give them a // try instead. // nsHttpAuthEntry *entry = nullptr; authCache->GetAuthEntryForDomain(scheme.get(), host, port, realm.get(), appId, isInBrowserElement, &entry); // hold reference to the auth session state (in case we clear our // reference to the entry). nsCOMPtr<nsISupports> sessionStateGrip; if (entry) sessionStateGrip = entry->mMetaData; // for digest auth, maybe our cached nonce value simply timed out... bool identityInvalid; nsISupports *sessionState = sessionStateGrip; rv = auth->ChallengeReceived(mAuthChannel, challenge, proxyAuth, &sessionState, &*continuationState, &identityInvalid); sessionStateGrip.swap(sessionState); if (NS_FAILED(rv)) return rv; LOG((" identity invalid = %d\n", identityInvalid)); if (identityInvalid) { if (entry) { if (ident->Equals(entry->Identity())) { if (!identFromURI) { LOG((" clearing bad auth cache entry\n")); // ok, we've already tried this user identity, so clear the // corresponding entry from the auth cache. authCache->ClearAuthEntry(scheme.get(), host, port, realm.get(), appId, isInBrowserElement); entry = nullptr; ident->Clear(); } } else if (!identFromURI || (nsCRT::strcmp(ident->User(), entry->Identity().User()) == 0 && !(loadFlags & (nsIChannel::LOAD_ANONYMOUS | nsIChannel::LOAD_EXPLICIT_CREDENTIALS)))) { LOG((" taking identity from auth cache\n")); // the password from the auth cache is more likely to be // correct than the one in the URL. at least, we know that it // works with the given username. it is possible for a server // to distinguish logons based on the supplied password alone, // but that would be quite unusual... and i don't think we need // to worry about such unorthodox cases. ident->Set(entry->Identity()); identFromURI = false; if (entry->Creds()[0] != '\0') { LOG((" using cached credentials!\n")); creds.Assign(entry->Creds()); return entry->AddPath(path.get()); } } } else if (!identFromURI) { // hmm... identity invalid, but no auth entry! the realm probably // changed (see bug 201986). ident->Clear(); } if (!entry && ident->IsEmpty()) { uint32_t level = nsIAuthPrompt2::LEVEL_NONE; if (mUsingSSL) level = nsIAuthPrompt2::LEVEL_SECURE; else if (authFlags & nsIHttpAuthenticator::IDENTITY_ENCRYPTED) level = nsIAuthPrompt2::LEVEL_PW_ENCRYPTED; // at this point we are forced to interact with the user to get // their username and password for this domain. rv = PromptForIdentity(level, proxyAuth, realm.get(), authType, authFlags, *ident); if (NS_FAILED(rv)) return rv; identFromURI = false; } } if (identFromURI) { // Warn the user before automatically using the identity from the URL // to automatically log them into a site (see bug 232567). if (!ConfirmAuth(NS_LITERAL_STRING("AutomaticAuth"), false)) { // calling cancel here sets our mStatus and aborts the HTTP // transaction, which prevents OnDataAvailable events. mAuthChannel->Cancel(NS_ERROR_ABORT); // this return code alone is not equivalent to Cancel, since // it only instructs our caller that authentication failed. // without an explicit call to Cancel, our caller would just // load the page that accompanies the HTTP auth challenge. return NS_ERROR_ABORT; } } // // get credentials for the given user:pass // // always store the credentials we're trying now so that they will be used // on subsequent links. This will potentially remove good credentials from // the cache. This is ok as we don't want to use cached credentials if the // user specified something on the URI or in another manner. This is so // that we don't transparently authenticate as someone they're not // expecting to authenticate as. // nsXPIDLCString result; rv = GenCredsAndSetEntry(auth, proxyAuth, scheme.get(), host, port, path.get(), realm.get(), challenge, *ident, sessionStateGrip, getter_Copies(result)); if (NS_SUCCEEDED(rv)) creds = result; return rv; }