1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/netwerk/protocol/http/nsHttpChannelAuthProvider.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,1355 @@ 1.4 +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ 1.5 +/* vim:set expandtab ts=4 sw=4 sts=4 cin: */ 1.6 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.7 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.8 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.9 + 1.10 +// HttpLog.h should generally be included first 1.11 +#include "HttpLog.h" 1.12 + 1.13 +#include "nsHttpChannelAuthProvider.h" 1.14 +#include "nsNetUtil.h" 1.15 +#include "nsHttpHandler.h" 1.16 +#include "nsIHttpAuthenticator.h" 1.17 +#include "nsIAuthPrompt2.h" 1.18 +#include "nsIAuthPromptProvider.h" 1.19 +#include "nsIInterfaceRequestor.h" 1.20 +#include "nsIInterfaceRequestorUtils.h" 1.21 +#include "nsEscape.h" 1.22 +#include "nsAuthInformationHolder.h" 1.23 +#include "nsIStringBundle.h" 1.24 +#include "nsIPrompt.h" 1.25 +#include "netCore.h" 1.26 +#include "nsIHttpAuthenticableChannel.h" 1.27 +#include "nsIURI.h" 1.28 + 1.29 +namespace mozilla { 1.30 +namespace net { 1.31 + 1.32 +static void 1.33 +GetAppIdAndBrowserStatus(nsIChannel* aChan, uint32_t* aAppId, bool* aInBrowserElem) 1.34 +{ 1.35 + nsCOMPtr<nsILoadContext> loadContext; 1.36 + if (aChan) { 1.37 + NS_QueryNotificationCallbacks(aChan, loadContext); 1.38 + } 1.39 + if (!loadContext) { 1.40 + *aAppId = NECKO_NO_APP_ID; 1.41 + *aInBrowserElem = false; 1.42 + } else { 1.43 + loadContext->GetAppId(aAppId); 1.44 + loadContext->GetIsInBrowserElement(aInBrowserElem); 1.45 + } 1.46 +} 1.47 + 1.48 +nsHttpChannelAuthProvider::nsHttpChannelAuthProvider() 1.49 + : mAuthChannel(nullptr) 1.50 + , mIsPrivate(false) 1.51 + , mProxyAuthContinuationState(nullptr) 1.52 + , mAuthContinuationState(nullptr) 1.53 + , mProxyAuth(false) 1.54 + , mTriedProxyAuth(false) 1.55 + , mTriedHostAuth(false) 1.56 + , mSuppressDefensiveAuth(false) 1.57 + , mHttpHandler(gHttpHandler) 1.58 +{ 1.59 +} 1.60 + 1.61 +nsHttpChannelAuthProvider::~nsHttpChannelAuthProvider() 1.62 +{ 1.63 + MOZ_ASSERT(!mAuthChannel, "Disconnect wasn't called"); 1.64 +} 1.65 + 1.66 +NS_IMETHODIMP 1.67 +nsHttpChannelAuthProvider::Init(nsIHttpAuthenticableChannel *channel) 1.68 +{ 1.69 + MOZ_ASSERT(channel, "channel expected!"); 1.70 + 1.71 + mAuthChannel = channel; 1.72 + 1.73 + nsresult rv = mAuthChannel->GetURI(getter_AddRefs(mURI)); 1.74 + if (NS_FAILED(rv)) return rv; 1.75 + 1.76 + mAuthChannel->GetIsSSL(&mUsingSSL); 1.77 + if (NS_FAILED(rv)) return rv; 1.78 + 1.79 + rv = mURI->GetAsciiHost(mHost); 1.80 + if (NS_FAILED(rv)) return rv; 1.81 + 1.82 + // reject the URL if it doesn't specify a host 1.83 + if (mHost.IsEmpty()) 1.84 + return NS_ERROR_MALFORMED_URI; 1.85 + 1.86 + rv = mURI->GetPort(&mPort); 1.87 + if (NS_FAILED(rv)) return rv; 1.88 + 1.89 + nsCOMPtr<nsIChannel> bareChannel = do_QueryInterface(channel); 1.90 + mIsPrivate = NS_UsePrivateBrowsing(bareChannel); 1.91 + 1.92 + return NS_OK; 1.93 +} 1.94 + 1.95 +NS_IMETHODIMP 1.96 +nsHttpChannelAuthProvider::ProcessAuthentication(uint32_t httpStatus, 1.97 + bool SSLConnectFailed) 1.98 +{ 1.99 + LOG(("nsHttpChannelAuthProvider::ProcessAuthentication " 1.100 + "[this=%p channel=%p code=%u SSLConnectFailed=%d]\n", 1.101 + this, mAuthChannel, httpStatus, SSLConnectFailed)); 1.102 + 1.103 + MOZ_ASSERT(mAuthChannel, "Channel not initialized"); 1.104 + 1.105 + nsCOMPtr<nsIProxyInfo> proxyInfo; 1.106 + nsresult rv = mAuthChannel->GetProxyInfo(getter_AddRefs(proxyInfo)); 1.107 + if (NS_FAILED(rv)) return rv; 1.108 + if (proxyInfo) { 1.109 + mProxyInfo = do_QueryInterface(proxyInfo); 1.110 + if (!mProxyInfo) return NS_ERROR_NO_INTERFACE; 1.111 + } 1.112 + 1.113 + nsAutoCString challenges; 1.114 + mProxyAuth = (httpStatus == 407); 1.115 + 1.116 + rv = PrepareForAuthentication(mProxyAuth); 1.117 + if (NS_FAILED(rv)) 1.118 + return rv; 1.119 + 1.120 + if (mProxyAuth) { 1.121 + // only allow a proxy challenge if we have a proxy server configured. 1.122 + // otherwise, we could inadvertently expose the user's proxy 1.123 + // credentials to an origin server. We could attempt to proceed as 1.124 + // if we had received a 401 from the server, but why risk flirting 1.125 + // with trouble? IE similarly rejects 407s when a proxy server is 1.126 + // not configured, so there's no reason not to do the same. 1.127 + if (!UsingHttpProxy()) { 1.128 + LOG(("rejecting 407 when proxy server not configured!\n")); 1.129 + return NS_ERROR_UNEXPECTED; 1.130 + } 1.131 + if (UsingSSL() && !SSLConnectFailed) { 1.132 + // we need to verify that this challenge came from the proxy 1.133 + // server itself, and not some server on the other side of the 1.134 + // SSL tunnel. 1.135 + LOG(("rejecting 407 from origin server!\n")); 1.136 + return NS_ERROR_UNEXPECTED; 1.137 + } 1.138 + rv = mAuthChannel->GetProxyChallenges(challenges); 1.139 + } 1.140 + else 1.141 + rv = mAuthChannel->GetWWWChallenges(challenges); 1.142 + if (NS_FAILED(rv)) return rv; 1.143 + 1.144 + nsAutoCString creds; 1.145 + rv = GetCredentials(challenges.get(), mProxyAuth, creds); 1.146 + if (rv == NS_ERROR_IN_PROGRESS) 1.147 + return rv; 1.148 + if (NS_FAILED(rv)) 1.149 + LOG(("unable to authenticate\n")); 1.150 + else { 1.151 + // set the authentication credentials 1.152 + if (mProxyAuth) 1.153 + rv = mAuthChannel->SetProxyCredentials(creds); 1.154 + else 1.155 + rv = mAuthChannel->SetWWWCredentials(creds); 1.156 + } 1.157 + return rv; 1.158 +} 1.159 + 1.160 +NS_IMETHODIMP 1.161 +nsHttpChannelAuthProvider::AddAuthorizationHeaders() 1.162 +{ 1.163 + LOG(("nsHttpChannelAuthProvider::AddAuthorizationHeaders? " 1.164 + "[this=%p channel=%p]\n", this, mAuthChannel)); 1.165 + 1.166 + MOZ_ASSERT(mAuthChannel, "Channel not initialized"); 1.167 + 1.168 + nsCOMPtr<nsIProxyInfo> proxyInfo; 1.169 + nsresult rv = mAuthChannel->GetProxyInfo(getter_AddRefs(proxyInfo)); 1.170 + if (NS_FAILED(rv)) return rv; 1.171 + if (proxyInfo) { 1.172 + mProxyInfo = do_QueryInterface(proxyInfo); 1.173 + if (!mProxyInfo) return NS_ERROR_NO_INTERFACE; 1.174 + } 1.175 + 1.176 + uint32_t loadFlags; 1.177 + rv = mAuthChannel->GetLoadFlags(&loadFlags); 1.178 + if (NS_FAILED(rv)) return rv; 1.179 + 1.180 + // this getter never fails 1.181 + nsHttpAuthCache *authCache = gHttpHandler->AuthCache(mIsPrivate); 1.182 + 1.183 + // check if proxy credentials should be sent 1.184 + const char *proxyHost = ProxyHost(); 1.185 + if (proxyHost && UsingHttpProxy()) 1.186 + SetAuthorizationHeader(authCache, nsHttp::Proxy_Authorization, 1.187 + "http", proxyHost, ProxyPort(), 1.188 + nullptr, // proxy has no path 1.189 + mProxyIdent); 1.190 + 1.191 + if (loadFlags & nsIRequest::LOAD_ANONYMOUS) { 1.192 + LOG(("Skipping Authorization header for anonymous load\n")); 1.193 + return NS_OK; 1.194 + } 1.195 + 1.196 + // check if server credentials should be sent 1.197 + nsAutoCString path, scheme; 1.198 + if (NS_SUCCEEDED(GetCurrentPath(path)) && 1.199 + NS_SUCCEEDED(mURI->GetScheme(scheme))) { 1.200 + SetAuthorizationHeader(authCache, nsHttp::Authorization, 1.201 + scheme.get(), 1.202 + Host(), 1.203 + Port(), 1.204 + path.get(), 1.205 + mIdent); 1.206 + } 1.207 + 1.208 + return NS_OK; 1.209 +} 1.210 + 1.211 +NS_IMETHODIMP 1.212 +nsHttpChannelAuthProvider::CheckForSuperfluousAuth() 1.213 +{ 1.214 + LOG(("nsHttpChannelAuthProvider::CheckForSuperfluousAuth? " 1.215 + "[this=%p channel=%p]\n", this, mAuthChannel)); 1.216 + 1.217 + MOZ_ASSERT(mAuthChannel, "Channel not initialized"); 1.218 + 1.219 + // we've been called because it has been determined that this channel is 1.220 + // getting loaded without taking the userpass from the URL. if the URL 1.221 + // contained a userpass, then (provided some other conditions are true), 1.222 + // we'll give the user an opportunity to abort the channel as this might be 1.223 + // an attempt to spoof a different site (see bug 232567). 1.224 + if (!ConfirmAuth(NS_LITERAL_STRING("SuperfluousAuth"), true)) { 1.225 + // calling cancel here sets our mStatus and aborts the HTTP 1.226 + // transaction, which prevents OnDataAvailable events. 1.227 + mAuthChannel->Cancel(NS_ERROR_ABORT); 1.228 + return NS_ERROR_ABORT; 1.229 + } 1.230 + return NS_OK; 1.231 +} 1.232 + 1.233 +NS_IMETHODIMP 1.234 +nsHttpChannelAuthProvider::Cancel(nsresult status) 1.235 +{ 1.236 + MOZ_ASSERT(mAuthChannel, "Channel not initialized"); 1.237 + 1.238 + if (mAsyncPromptAuthCancelable) { 1.239 + mAsyncPromptAuthCancelable->Cancel(status); 1.240 + mAsyncPromptAuthCancelable = nullptr; 1.241 + } 1.242 + return NS_OK; 1.243 +} 1.244 + 1.245 +NS_IMETHODIMP 1.246 +nsHttpChannelAuthProvider::Disconnect(nsresult status) 1.247 +{ 1.248 + mAuthChannel = nullptr; 1.249 + 1.250 + if (mAsyncPromptAuthCancelable) { 1.251 + mAsyncPromptAuthCancelable->Cancel(status); 1.252 + mAsyncPromptAuthCancelable = nullptr; 1.253 + } 1.254 + 1.255 + NS_IF_RELEASE(mProxyAuthContinuationState); 1.256 + NS_IF_RELEASE(mAuthContinuationState); 1.257 + 1.258 + return NS_OK; 1.259 +} 1.260 + 1.261 +// buf contains "domain\user" 1.262 +static void 1.263 +ParseUserDomain(char16_t *buf, 1.264 + const char16_t **user, 1.265 + const char16_t **domain) 1.266 +{ 1.267 + char16_t *p = buf; 1.268 + while (*p && *p != '\\') ++p; 1.269 + if (!*p) 1.270 + return; 1.271 + *p = '\0'; 1.272 + *domain = buf; 1.273 + *user = p + 1; 1.274 +} 1.275 + 1.276 +// helper function for setting identity from raw user:pass 1.277 +static void 1.278 +SetIdent(nsHttpAuthIdentity &ident, 1.279 + uint32_t authFlags, 1.280 + char16_t *userBuf, 1.281 + char16_t *passBuf) 1.282 +{ 1.283 + const char16_t *user = userBuf; 1.284 + const char16_t *domain = nullptr; 1.285 + 1.286 + if (authFlags & nsIHttpAuthenticator::IDENTITY_INCLUDES_DOMAIN) 1.287 + ParseUserDomain(userBuf, &user, &domain); 1.288 + 1.289 + ident.Set(domain, user, passBuf); 1.290 +} 1.291 + 1.292 +// helper function for getting an auth prompt from an interface requestor 1.293 +static void 1.294 +GetAuthPrompt(nsIInterfaceRequestor *ifreq, bool proxyAuth, 1.295 + nsIAuthPrompt2 **result) 1.296 +{ 1.297 + if (!ifreq) 1.298 + return; 1.299 + 1.300 + uint32_t promptReason; 1.301 + if (proxyAuth) 1.302 + promptReason = nsIAuthPromptProvider::PROMPT_PROXY; 1.303 + else 1.304 + promptReason = nsIAuthPromptProvider::PROMPT_NORMAL; 1.305 + 1.306 + nsCOMPtr<nsIAuthPromptProvider> promptProvider = do_GetInterface(ifreq); 1.307 + if (promptProvider) 1.308 + promptProvider->GetAuthPrompt(promptReason, 1.309 + NS_GET_IID(nsIAuthPrompt2), 1.310 + reinterpret_cast<void**>(result)); 1.311 + else 1.312 + NS_QueryAuthPrompt2(ifreq, result); 1.313 +} 1.314 + 1.315 +// generate credentials for the given challenge, and update the auth cache. 1.316 +nsresult 1.317 +nsHttpChannelAuthProvider::GenCredsAndSetEntry(nsIHttpAuthenticator *auth, 1.318 + bool proxyAuth, 1.319 + const char *scheme, 1.320 + const char *host, 1.321 + int32_t port, 1.322 + const char *directory, 1.323 + const char *realm, 1.324 + const char *challenge, 1.325 + const nsHttpAuthIdentity &ident, 1.326 + nsCOMPtr<nsISupports> &sessionState, 1.327 + char **result) 1.328 +{ 1.329 + nsresult rv; 1.330 + uint32_t authFlags; 1.331 + 1.332 + rv = auth->GetAuthFlags(&authFlags); 1.333 + if (NS_FAILED(rv)) return rv; 1.334 + 1.335 + nsISupports *ss = sessionState; 1.336 + 1.337 + // set informations that depend on whether 1.338 + // we're authenticating against a proxy 1.339 + // or a webserver 1.340 + nsISupports **continuationState; 1.341 + 1.342 + if (proxyAuth) { 1.343 + continuationState = &mProxyAuthContinuationState; 1.344 + } else { 1.345 + continuationState = &mAuthContinuationState; 1.346 + } 1.347 + 1.348 + uint32_t generateFlags; 1.349 + rv = auth->GenerateCredentials(mAuthChannel, 1.350 + challenge, 1.351 + proxyAuth, 1.352 + ident.Domain(), 1.353 + ident.User(), 1.354 + ident.Password(), 1.355 + &ss, 1.356 + &*continuationState, 1.357 + &generateFlags, 1.358 + result); 1.359 + 1.360 + sessionState.swap(ss); 1.361 + if (NS_FAILED(rv)) return rv; 1.362 + 1.363 + // don't log this in release build since it could contain sensitive info. 1.364 +#ifdef DEBUG 1.365 + LOG(("generated creds: %s\n", *result)); 1.366 +#endif 1.367 + 1.368 + // find out if this authenticator allows reuse of credentials and/or 1.369 + // challenge. 1.370 + bool saveCreds = 1.371 + 0 != (authFlags & nsIHttpAuthenticator::REUSABLE_CREDENTIALS); 1.372 + bool saveChallenge = 1.373 + 0 != (authFlags & nsIHttpAuthenticator::REUSABLE_CHALLENGE); 1.374 + 1.375 + bool saveIdentity = 1.376 + 0 == (generateFlags & nsIHttpAuthenticator::USING_INTERNAL_IDENTITY); 1.377 + 1.378 + // this getter never fails 1.379 + nsHttpAuthCache *authCache = gHttpHandler->AuthCache(mIsPrivate); 1.380 + 1.381 + nsCOMPtr<nsIChannel> chan = do_QueryInterface(mAuthChannel); 1.382 + uint32_t appId; 1.383 + bool isInBrowserElement; 1.384 + GetAppIdAndBrowserStatus(chan, &appId, &isInBrowserElement); 1.385 + 1.386 + // create a cache entry. we do this even though we don't yet know that 1.387 + // these credentials are valid b/c we need to avoid prompting the user 1.388 + // more than once in case the credentials are valid. 1.389 + // 1.390 + // if the credentials are not reusable, then we don't bother sticking 1.391 + // them in the auth cache. 1.392 + rv = authCache->SetAuthEntry(scheme, host, port, directory, realm, 1.393 + saveCreds ? *result : nullptr, 1.394 + saveChallenge ? challenge : nullptr, 1.395 + appId, isInBrowserElement, 1.396 + saveIdentity ? &ident : nullptr, 1.397 + sessionState); 1.398 + return rv; 1.399 +} 1.400 + 1.401 +nsresult 1.402 +nsHttpChannelAuthProvider::PrepareForAuthentication(bool proxyAuth) 1.403 +{ 1.404 + LOG(("nsHttpChannelAuthProvider::PrepareForAuthentication " 1.405 + "[this=%p channel=%p]\n", this, mAuthChannel)); 1.406 + 1.407 + if (!proxyAuth) { 1.408 + // reset the current proxy continuation state because our last 1.409 + // authentication attempt was completed successfully. 1.410 + NS_IF_RELEASE(mProxyAuthContinuationState); 1.411 + LOG((" proxy continuation state has been reset")); 1.412 + } 1.413 + 1.414 + if (!UsingHttpProxy() || mProxyAuthType.IsEmpty()) 1.415 + return NS_OK; 1.416 + 1.417 + // We need to remove any Proxy_Authorization header left over from a 1.418 + // non-request based authentication handshake (e.g., for NTLM auth). 1.419 + 1.420 + nsAutoCString contractId; 1.421 + contractId.Assign(NS_HTTP_AUTHENTICATOR_CONTRACTID_PREFIX); 1.422 + contractId.Append(mProxyAuthType); 1.423 + 1.424 + nsresult rv; 1.425 + nsCOMPtr<nsIHttpAuthenticator> precedingAuth = 1.426 + do_GetService(contractId.get(), &rv); 1.427 + if (NS_FAILED(rv)) 1.428 + return rv; 1.429 + 1.430 + uint32_t precedingAuthFlags; 1.431 + rv = precedingAuth->GetAuthFlags(&precedingAuthFlags); 1.432 + if (NS_FAILED(rv)) 1.433 + return rv; 1.434 + 1.435 + if (!(precedingAuthFlags & nsIHttpAuthenticator::REQUEST_BASED)) { 1.436 + nsAutoCString challenges; 1.437 + rv = mAuthChannel->GetProxyChallenges(challenges); 1.438 + if (NS_FAILED(rv)) { 1.439 + // delete the proxy authorization header because we weren't 1.440 + // asked to authenticate 1.441 + rv = mAuthChannel->SetProxyCredentials(EmptyCString()); 1.442 + if (NS_FAILED(rv)) return rv; 1.443 + LOG((" cleared proxy authorization header")); 1.444 + } 1.445 + } 1.446 + 1.447 + return NS_OK; 1.448 +} 1.449 + 1.450 +nsresult 1.451 +nsHttpChannelAuthProvider::GetCredentials(const char *challenges, 1.452 + bool proxyAuth, 1.453 + nsAFlatCString &creds) 1.454 +{ 1.455 + nsCOMPtr<nsIHttpAuthenticator> auth; 1.456 + nsAutoCString challenge; 1.457 + 1.458 + nsCString authType; // force heap allocation to enable string sharing since 1.459 + // we'll be assigning this value into mAuthType. 1.460 + 1.461 + // set informations that depend on whether we're authenticating against a 1.462 + // proxy or a webserver 1.463 + nsISupports **currentContinuationState; 1.464 + nsCString *currentAuthType; 1.465 + 1.466 + if (proxyAuth) { 1.467 + currentContinuationState = &mProxyAuthContinuationState; 1.468 + currentAuthType = &mProxyAuthType; 1.469 + } else { 1.470 + currentContinuationState = &mAuthContinuationState; 1.471 + currentAuthType = &mAuthType; 1.472 + } 1.473 + 1.474 + nsresult rv = NS_ERROR_NOT_AVAILABLE; 1.475 + bool gotCreds = false; 1.476 + 1.477 + // figure out which challenge we can handle and which authenticator to use. 1.478 + for (const char *eol = challenges - 1; eol; ) { 1.479 + const char *p = eol + 1; 1.480 + 1.481 + // get the challenge string (LF separated -- see nsHttpHeaderArray) 1.482 + if ((eol = strchr(p, '\n')) != nullptr) 1.483 + challenge.Assign(p, eol - p); 1.484 + else 1.485 + challenge.Assign(p); 1.486 + 1.487 + rv = GetAuthenticator(challenge.get(), authType, getter_AddRefs(auth)); 1.488 + if (NS_SUCCEEDED(rv)) { 1.489 + // 1.490 + // if we've already selected an auth type from a previous challenge 1.491 + // received while processing this channel, then skip others until 1.492 + // we find a challenge corresponding to the previously tried auth 1.493 + // type. 1.494 + // 1.495 + if (!currentAuthType->IsEmpty() && authType != *currentAuthType) 1.496 + continue; 1.497 + 1.498 + // 1.499 + // we allow the routines to run all the way through before we 1.500 + // decide if they are valid. 1.501 + // 1.502 + // we don't worry about the auth cache being altered because that 1.503 + // would have been the last step, and if the error is from updating 1.504 + // the authcache it wasn't really altered anyway. -CTN 1.505 + // 1.506 + // at this point the code is really only useful for client side 1.507 + // errors (it will not automatically fail over to do a different 1.508 + // auth type if the server keeps rejecting what is being sent, even 1.509 + // if a particular auth method only knows 1 thing, like a 1.510 + // non-identity based authentication method) 1.511 + // 1.512 + rv = GetCredentialsForChallenge(challenge.get(), authType.get(), 1.513 + proxyAuth, auth, creds); 1.514 + if (NS_SUCCEEDED(rv)) { 1.515 + gotCreds = true; 1.516 + *currentAuthType = authType; 1.517 + 1.518 + break; 1.519 + } 1.520 + else if (rv == NS_ERROR_IN_PROGRESS) { 1.521 + // authentication prompt has been invoked and result is 1.522 + // expected asynchronously, save current challenge being 1.523 + // processed and all remaining challenges to use later in 1.524 + // OnAuthAvailable and now immediately return 1.525 + mCurrentChallenge = challenge; 1.526 + mRemainingChallenges = eol ? eol+1 : nullptr; 1.527 + return rv; 1.528 + } 1.529 + 1.530 + // reset the auth type and continuation state 1.531 + NS_IF_RELEASE(*currentContinuationState); 1.532 + currentAuthType->Truncate(); 1.533 + } 1.534 + } 1.535 + 1.536 + if (!gotCreds && !currentAuthType->IsEmpty()) { 1.537 + // looks like we never found the auth type we were looking for. 1.538 + // reset the auth type and continuation state, and try again. 1.539 + currentAuthType->Truncate(); 1.540 + NS_IF_RELEASE(*currentContinuationState); 1.541 + 1.542 + rv = GetCredentials(challenges, proxyAuth, creds); 1.543 + } 1.544 + 1.545 + return rv; 1.546 +} 1.547 + 1.548 +nsresult 1.549 +nsHttpChannelAuthProvider::GetAuthorizationMembers(bool proxyAuth, 1.550 + nsCSubstring& scheme, 1.551 + const char*& host, 1.552 + int32_t& port, 1.553 + nsCSubstring& path, 1.554 + nsHttpAuthIdentity*& ident, 1.555 + nsISupports**& continuationState) 1.556 +{ 1.557 + if (proxyAuth) { 1.558 + MOZ_ASSERT (UsingHttpProxy(), 1.559 + "proxyAuth is true, but no HTTP proxy is configured!"); 1.560 + 1.561 + host = ProxyHost(); 1.562 + port = ProxyPort(); 1.563 + ident = &mProxyIdent; 1.564 + scheme.AssignLiteral("http"); 1.565 + 1.566 + continuationState = &mProxyAuthContinuationState; 1.567 + } 1.568 + else { 1.569 + host = Host(); 1.570 + port = Port(); 1.571 + ident = &mIdent; 1.572 + 1.573 + nsresult rv; 1.574 + rv = GetCurrentPath(path); 1.575 + if (NS_FAILED(rv)) return rv; 1.576 + 1.577 + rv = mURI->GetScheme(scheme); 1.578 + if (NS_FAILED(rv)) return rv; 1.579 + 1.580 + continuationState = &mAuthContinuationState; 1.581 + } 1.582 + 1.583 + return NS_OK; 1.584 +} 1.585 + 1.586 +nsresult 1.587 +nsHttpChannelAuthProvider::GetCredentialsForChallenge(const char *challenge, 1.588 + const char *authType, 1.589 + bool proxyAuth, 1.590 + nsIHttpAuthenticator *auth, 1.591 + nsAFlatCString &creds) 1.592 +{ 1.593 + LOG(("nsHttpChannelAuthProvider::GetCredentialsForChallenge " 1.594 + "[this=%p channel=%p proxyAuth=%d challenges=%s]\n", 1.595 + this, mAuthChannel, proxyAuth, challenge)); 1.596 + 1.597 + // this getter never fails 1.598 + nsHttpAuthCache *authCache = gHttpHandler->AuthCache(mIsPrivate); 1.599 + 1.600 + uint32_t authFlags; 1.601 + nsresult rv = auth->GetAuthFlags(&authFlags); 1.602 + if (NS_FAILED(rv)) return rv; 1.603 + 1.604 + nsAutoCString realm; 1.605 + ParseRealm(challenge, realm); 1.606 + 1.607 + // if no realm, then use the auth type as the realm. ToUpperCase so the 1.608 + // ficticious realm stands out a bit more. 1.609 + // XXX this will cause some single signon misses! 1.610 + // XXX this was meant to be used with NTLM, which supplies no realm. 1.611 + /* 1.612 + if (realm.IsEmpty()) { 1.613 + realm = authType; 1.614 + ToUpperCase(realm); 1.615 + } 1.616 + */ 1.617 + 1.618 + // set informations that depend on whether 1.619 + // we're authenticating against a proxy 1.620 + // or a webserver 1.621 + const char *host; 1.622 + int32_t port; 1.623 + nsHttpAuthIdentity *ident; 1.624 + nsAutoCString path, scheme; 1.625 + bool identFromURI = false; 1.626 + nsISupports **continuationState; 1.627 + 1.628 + rv = GetAuthorizationMembers(proxyAuth, scheme, host, port, 1.629 + path, ident, continuationState); 1.630 + if (NS_FAILED(rv)) return rv; 1.631 + 1.632 + uint32_t loadFlags; 1.633 + rv = mAuthChannel->GetLoadFlags(&loadFlags); 1.634 + if (NS_FAILED(rv)) return rv; 1.635 + 1.636 + if (!proxyAuth) { 1.637 + // if this is the first challenge, then try using the identity 1.638 + // specified in the URL. 1.639 + if (mIdent.IsEmpty()) { 1.640 + GetIdentityFromURI(authFlags, mIdent); 1.641 + identFromURI = !mIdent.IsEmpty(); 1.642 + } 1.643 + 1.644 + if ((loadFlags & nsIRequest::LOAD_ANONYMOUS) && !identFromURI) { 1.645 + LOG(("Skipping authentication for anonymous non-proxy request\n")); 1.646 + return NS_ERROR_NOT_AVAILABLE; 1.647 + } 1.648 + 1.649 + // Let explicit URL credentials pass 1.650 + // regardless of the LOAD_ANONYMOUS flag 1.651 + } 1.652 + else if ((loadFlags & nsIRequest::LOAD_ANONYMOUS) && !UsingHttpProxy()) { 1.653 + LOG(("Skipping authentication for anonymous non-proxy request\n")); 1.654 + return NS_ERROR_NOT_AVAILABLE; 1.655 + } 1.656 + 1.657 + nsCOMPtr<nsIChannel> chan = do_QueryInterface(mAuthChannel); 1.658 + uint32_t appId; 1.659 + bool isInBrowserElement; 1.660 + GetAppIdAndBrowserStatus(chan, &appId, &isInBrowserElement); 1.661 + 1.662 + // 1.663 + // if we already tried some credentials for this transaction, then 1.664 + // we need to possibly clear them from the cache, unless the credentials 1.665 + // in the cache have changed, in which case we'd want to give them a 1.666 + // try instead. 1.667 + // 1.668 + nsHttpAuthEntry *entry = nullptr; 1.669 + authCache->GetAuthEntryForDomain(scheme.get(), host, port, 1.670 + realm.get(), appId, 1.671 + isInBrowserElement, &entry); 1.672 + 1.673 + // hold reference to the auth session state (in case we clear our 1.674 + // reference to the entry). 1.675 + nsCOMPtr<nsISupports> sessionStateGrip; 1.676 + if (entry) 1.677 + sessionStateGrip = entry->mMetaData; 1.678 + 1.679 + // for digest auth, maybe our cached nonce value simply timed out... 1.680 + bool identityInvalid; 1.681 + nsISupports *sessionState = sessionStateGrip; 1.682 + rv = auth->ChallengeReceived(mAuthChannel, 1.683 + challenge, 1.684 + proxyAuth, 1.685 + &sessionState, 1.686 + &*continuationState, 1.687 + &identityInvalid); 1.688 + sessionStateGrip.swap(sessionState); 1.689 + if (NS_FAILED(rv)) return rv; 1.690 + 1.691 + LOG((" identity invalid = %d\n", identityInvalid)); 1.692 + 1.693 + if (identityInvalid) { 1.694 + if (entry) { 1.695 + if (ident->Equals(entry->Identity())) { 1.696 + if (!identFromURI) { 1.697 + LOG((" clearing bad auth cache entry\n")); 1.698 + // ok, we've already tried this user identity, so clear the 1.699 + // corresponding entry from the auth cache. 1.700 + authCache->ClearAuthEntry(scheme.get(), host, 1.701 + port, realm.get(), 1.702 + appId, isInBrowserElement); 1.703 + entry = nullptr; 1.704 + ident->Clear(); 1.705 + } 1.706 + } 1.707 + else if (!identFromURI || 1.708 + (nsCRT::strcmp(ident->User(), 1.709 + entry->Identity().User()) == 0 && 1.710 + !(loadFlags & 1.711 + (nsIChannel::LOAD_ANONYMOUS | 1.712 + nsIChannel::LOAD_EXPLICIT_CREDENTIALS)))) { 1.713 + LOG((" taking identity from auth cache\n")); 1.714 + // the password from the auth cache is more likely to be 1.715 + // correct than the one in the URL. at least, we know that it 1.716 + // works with the given username. it is possible for a server 1.717 + // to distinguish logons based on the supplied password alone, 1.718 + // but that would be quite unusual... and i don't think we need 1.719 + // to worry about such unorthodox cases. 1.720 + ident->Set(entry->Identity()); 1.721 + identFromURI = false; 1.722 + if (entry->Creds()[0] != '\0') { 1.723 + LOG((" using cached credentials!\n")); 1.724 + creds.Assign(entry->Creds()); 1.725 + return entry->AddPath(path.get()); 1.726 + } 1.727 + } 1.728 + } 1.729 + else if (!identFromURI) { 1.730 + // hmm... identity invalid, but no auth entry! the realm probably 1.731 + // changed (see bug 201986). 1.732 + ident->Clear(); 1.733 + } 1.734 + 1.735 + if (!entry && ident->IsEmpty()) { 1.736 + uint32_t level = nsIAuthPrompt2::LEVEL_NONE; 1.737 + if (mUsingSSL) 1.738 + level = nsIAuthPrompt2::LEVEL_SECURE; 1.739 + else if (authFlags & nsIHttpAuthenticator::IDENTITY_ENCRYPTED) 1.740 + level = nsIAuthPrompt2::LEVEL_PW_ENCRYPTED; 1.741 + 1.742 + // at this point we are forced to interact with the user to get 1.743 + // their username and password for this domain. 1.744 + rv = PromptForIdentity(level, proxyAuth, realm.get(), 1.745 + authType, authFlags, *ident); 1.746 + if (NS_FAILED(rv)) return rv; 1.747 + identFromURI = false; 1.748 + } 1.749 + } 1.750 + 1.751 + if (identFromURI) { 1.752 + // Warn the user before automatically using the identity from the URL 1.753 + // to automatically log them into a site (see bug 232567). 1.754 + if (!ConfirmAuth(NS_LITERAL_STRING("AutomaticAuth"), false)) { 1.755 + // calling cancel here sets our mStatus and aborts the HTTP 1.756 + // transaction, which prevents OnDataAvailable events. 1.757 + mAuthChannel->Cancel(NS_ERROR_ABORT); 1.758 + // this return code alone is not equivalent to Cancel, since 1.759 + // it only instructs our caller that authentication failed. 1.760 + // without an explicit call to Cancel, our caller would just 1.761 + // load the page that accompanies the HTTP auth challenge. 1.762 + return NS_ERROR_ABORT; 1.763 + } 1.764 + } 1.765 + 1.766 + // 1.767 + // get credentials for the given user:pass 1.768 + // 1.769 + // always store the credentials we're trying now so that they will be used 1.770 + // on subsequent links. This will potentially remove good credentials from 1.771 + // the cache. This is ok as we don't want to use cached credentials if the 1.772 + // user specified something on the URI or in another manner. This is so 1.773 + // that we don't transparently authenticate as someone they're not 1.774 + // expecting to authenticate as. 1.775 + // 1.776 + nsXPIDLCString result; 1.777 + rv = GenCredsAndSetEntry(auth, proxyAuth, scheme.get(), host, port, 1.778 + path.get(), realm.get(), challenge, *ident, 1.779 + sessionStateGrip, getter_Copies(result)); 1.780 + if (NS_SUCCEEDED(rv)) 1.781 + creds = result; 1.782 + return rv; 1.783 +} 1.784 + 1.785 +inline void 1.786 +GetAuthType(const char *challenge, nsCString &authType) 1.787 +{ 1.788 + const char *p; 1.789 + 1.790 + // get the challenge type 1.791 + if ((p = strchr(challenge, ' ')) != nullptr) 1.792 + authType.Assign(challenge, p - challenge); 1.793 + else 1.794 + authType.Assign(challenge); 1.795 +} 1.796 + 1.797 +nsresult 1.798 +nsHttpChannelAuthProvider::GetAuthenticator(const char *challenge, 1.799 + nsCString &authType, 1.800 + nsIHttpAuthenticator **auth) 1.801 +{ 1.802 + LOG(("nsHttpChannelAuthProvider::GetAuthenticator [this=%p channel=%p]\n", 1.803 + this, mAuthChannel)); 1.804 + 1.805 + GetAuthType(challenge, authType); 1.806 + 1.807 + // normalize to lowercase 1.808 + ToLowerCase(authType); 1.809 + 1.810 + nsAutoCString contractid; 1.811 + contractid.Assign(NS_HTTP_AUTHENTICATOR_CONTRACTID_PREFIX); 1.812 + contractid.Append(authType); 1.813 + 1.814 + return CallGetService(contractid.get(), auth); 1.815 +} 1.816 + 1.817 +void 1.818 +nsHttpChannelAuthProvider::GetIdentityFromURI(uint32_t authFlags, 1.819 + nsHttpAuthIdentity &ident) 1.820 +{ 1.821 + LOG(("nsHttpChannelAuthProvider::GetIdentityFromURI [this=%p channel=%p]\n", 1.822 + this, mAuthChannel)); 1.823 + 1.824 + nsAutoString userBuf; 1.825 + nsAutoString passBuf; 1.826 + 1.827 + // XXX i18n 1.828 + nsAutoCString buf; 1.829 + mURI->GetUsername(buf); 1.830 + if (!buf.IsEmpty()) { 1.831 + NS_UnescapeURL(buf); 1.832 + CopyASCIItoUTF16(buf, userBuf); 1.833 + mURI->GetPassword(buf); 1.834 + if (!buf.IsEmpty()) { 1.835 + NS_UnescapeURL(buf); 1.836 + CopyASCIItoUTF16(buf, passBuf); 1.837 + } 1.838 + } 1.839 + 1.840 + if (!userBuf.IsEmpty()) { 1.841 + SetIdent(ident, authFlags, (char16_t *) userBuf.get(), 1.842 + (char16_t *) passBuf.get()); 1.843 + } 1.844 +} 1.845 + 1.846 +void 1.847 +nsHttpChannelAuthProvider::ParseRealm(const char *challenge, 1.848 + nsACString &realm) 1.849 +{ 1.850 + // 1.851 + // From RFC2617 section 1.2, the realm value is defined as such: 1.852 + // 1.853 + // realm = "realm" "=" realm-value 1.854 + // realm-value = quoted-string 1.855 + // 1.856 + // but, we'll accept anything after the the "=" up to the first space, or 1.857 + // end-of-line, if the string is not quoted. 1.858 + // 1.859 + 1.860 + const char *p = PL_strcasestr(challenge, "realm="); 1.861 + if (p) { 1.862 + bool has_quote = false; 1.863 + p += 6; 1.864 + if (*p == '"') { 1.865 + has_quote = true; 1.866 + p++; 1.867 + } 1.868 + 1.869 + const char *end; 1.870 + if (has_quote) { 1.871 + end = p; 1.872 + while (*end) { 1.873 + if (*end == '\\') { 1.874 + // escaped character, store that one instead if not zero 1.875 + if (!*++end) 1.876 + break; 1.877 + } 1.878 + else if (*end == '\"') 1.879 + // end of string 1.880 + break; 1.881 + 1.882 + realm.Append(*end); 1.883 + ++end; 1.884 + } 1.885 + } 1.886 + else { 1.887 + // realm given without quotes 1.888 + end = strchr(p, ' '); 1.889 + if (end) 1.890 + realm.Assign(p, end - p); 1.891 + else 1.892 + realm.Assign(p); 1.893 + } 1.894 + } 1.895 +} 1.896 + 1.897 + 1.898 +class nsHTTPAuthInformation : public nsAuthInformationHolder { 1.899 +public: 1.900 + nsHTTPAuthInformation(uint32_t aFlags, const nsString& aRealm, 1.901 + const nsCString& aAuthType) 1.902 + : nsAuthInformationHolder(aFlags, aRealm, aAuthType) {} 1.903 + 1.904 + void SetToHttpAuthIdentity(uint32_t authFlags, 1.905 + nsHttpAuthIdentity& identity); 1.906 +}; 1.907 + 1.908 +void 1.909 +nsHTTPAuthInformation::SetToHttpAuthIdentity(uint32_t authFlags, 1.910 + nsHttpAuthIdentity& identity) 1.911 +{ 1.912 + identity.Set(Domain().get(), User().get(), Password().get()); 1.913 +} 1.914 + 1.915 +nsresult 1.916 +nsHttpChannelAuthProvider::PromptForIdentity(uint32_t level, 1.917 + bool proxyAuth, 1.918 + const char *realm, 1.919 + const char *authType, 1.920 + uint32_t authFlags, 1.921 + nsHttpAuthIdentity &ident) 1.922 +{ 1.923 + LOG(("nsHttpChannelAuthProvider::PromptForIdentity [this=%p channel=%p]\n", 1.924 + this, mAuthChannel)); 1.925 + 1.926 + nsresult rv; 1.927 + 1.928 + nsCOMPtr<nsIInterfaceRequestor> callbacks; 1.929 + rv = mAuthChannel->GetNotificationCallbacks(getter_AddRefs(callbacks)); 1.930 + if (NS_FAILED(rv)) return rv; 1.931 + 1.932 + nsCOMPtr<nsILoadGroup> loadGroup; 1.933 + rv = mAuthChannel->GetLoadGroup(getter_AddRefs(loadGroup)); 1.934 + if (NS_FAILED(rv)) return rv; 1.935 + 1.936 + nsCOMPtr<nsIAuthPrompt2> authPrompt; 1.937 + GetAuthPrompt(callbacks, proxyAuth, getter_AddRefs(authPrompt)); 1.938 + if (!authPrompt && loadGroup) { 1.939 + nsCOMPtr<nsIInterfaceRequestor> cbs; 1.940 + loadGroup->GetNotificationCallbacks(getter_AddRefs(cbs)); 1.941 + GetAuthPrompt(cbs, proxyAuth, getter_AddRefs(authPrompt)); 1.942 + } 1.943 + if (!authPrompt) 1.944 + return NS_ERROR_NO_INTERFACE; 1.945 + 1.946 + // XXX i18n: need to support non-ASCII realm strings (see bug 41489) 1.947 + NS_ConvertASCIItoUTF16 realmU(realm); 1.948 + 1.949 + // prompt the user... 1.950 + uint32_t promptFlags = 0; 1.951 + if (proxyAuth) 1.952 + { 1.953 + promptFlags |= nsIAuthInformation::AUTH_PROXY; 1.954 + if (mTriedProxyAuth) 1.955 + promptFlags |= nsIAuthInformation::PREVIOUS_FAILED; 1.956 + mTriedProxyAuth = true; 1.957 + } 1.958 + else { 1.959 + promptFlags |= nsIAuthInformation::AUTH_HOST; 1.960 + if (mTriedHostAuth) 1.961 + promptFlags |= nsIAuthInformation::PREVIOUS_FAILED; 1.962 + mTriedHostAuth = true; 1.963 + } 1.964 + 1.965 + if (authFlags & nsIHttpAuthenticator::IDENTITY_INCLUDES_DOMAIN) 1.966 + promptFlags |= nsIAuthInformation::NEED_DOMAIN; 1.967 + 1.968 + nsRefPtr<nsHTTPAuthInformation> holder = 1.969 + new nsHTTPAuthInformation(promptFlags, realmU, 1.970 + nsDependentCString(authType)); 1.971 + if (!holder) 1.972 + return NS_ERROR_OUT_OF_MEMORY; 1.973 + 1.974 + nsCOMPtr<nsIChannel> channel(do_QueryInterface(mAuthChannel, &rv)); 1.975 + if (NS_FAILED(rv)) return rv; 1.976 + 1.977 + rv = 1.978 + authPrompt->AsyncPromptAuth(channel, this, nullptr, level, holder, 1.979 + getter_AddRefs(mAsyncPromptAuthCancelable)); 1.980 + 1.981 + if (NS_SUCCEEDED(rv)) { 1.982 + // indicate using this error code that authentication prompt 1.983 + // result is expected asynchronously 1.984 + rv = NS_ERROR_IN_PROGRESS; 1.985 + } 1.986 + else { 1.987 + // Fall back to synchronous prompt 1.988 + bool retval = false; 1.989 + rv = authPrompt->PromptAuth(channel, level, holder, &retval); 1.990 + if (NS_FAILED(rv)) 1.991 + return rv; 1.992 + 1.993 + if (!retval) 1.994 + rv = NS_ERROR_ABORT; 1.995 + else 1.996 + holder->SetToHttpAuthIdentity(authFlags, ident); 1.997 + } 1.998 + 1.999 + // remember that we successfully showed the user an auth dialog 1.1000 + if (!proxyAuth) 1.1001 + mSuppressDefensiveAuth = true; 1.1002 + 1.1003 + return rv; 1.1004 +} 1.1005 + 1.1006 +NS_IMETHODIMP nsHttpChannelAuthProvider::OnAuthAvailable(nsISupports *aContext, 1.1007 + nsIAuthInformation *aAuthInfo) 1.1008 +{ 1.1009 + LOG(("nsHttpChannelAuthProvider::OnAuthAvailable [this=%p channel=%p]", 1.1010 + this, mAuthChannel)); 1.1011 + 1.1012 + mAsyncPromptAuthCancelable = nullptr; 1.1013 + if (!mAuthChannel) 1.1014 + return NS_OK; 1.1015 + 1.1016 + nsresult rv; 1.1017 + 1.1018 + const char *host; 1.1019 + int32_t port; 1.1020 + nsHttpAuthIdentity *ident; 1.1021 + nsAutoCString path, scheme; 1.1022 + nsISupports **continuationState; 1.1023 + rv = GetAuthorizationMembers(mProxyAuth, scheme, host, port, 1.1024 + path, ident, continuationState); 1.1025 + if (NS_FAILED(rv)) 1.1026 + OnAuthCancelled(aContext, false); 1.1027 + 1.1028 + nsAutoCString realm; 1.1029 + ParseRealm(mCurrentChallenge.get(), realm); 1.1030 + 1.1031 + nsCOMPtr<nsIChannel> chan = do_QueryInterface(mAuthChannel); 1.1032 + uint32_t appId; 1.1033 + bool isInBrowserElement; 1.1034 + GetAppIdAndBrowserStatus(chan, &appId, &isInBrowserElement); 1.1035 + 1.1036 + nsHttpAuthCache *authCache = gHttpHandler->AuthCache(mIsPrivate); 1.1037 + nsHttpAuthEntry *entry = nullptr; 1.1038 + authCache->GetAuthEntryForDomain(scheme.get(), host, port, 1.1039 + realm.get(), appId, 1.1040 + isInBrowserElement, 1.1041 + &entry); 1.1042 + 1.1043 + nsCOMPtr<nsISupports> sessionStateGrip; 1.1044 + if (entry) 1.1045 + sessionStateGrip = entry->mMetaData; 1.1046 + 1.1047 + nsAuthInformationHolder* holder = 1.1048 + static_cast<nsAuthInformationHolder*>(aAuthInfo); 1.1049 + ident->Set(holder->Domain().get(), 1.1050 + holder->User().get(), 1.1051 + holder->Password().get()); 1.1052 + 1.1053 + nsAutoCString unused; 1.1054 + nsCOMPtr<nsIHttpAuthenticator> auth; 1.1055 + rv = GetAuthenticator(mCurrentChallenge.get(), unused, 1.1056 + getter_AddRefs(auth)); 1.1057 + if (NS_FAILED(rv)) { 1.1058 + MOZ_ASSERT(false, "GetAuthenticator failed"); 1.1059 + OnAuthCancelled(aContext, true); 1.1060 + return NS_OK; 1.1061 + } 1.1062 + 1.1063 + nsXPIDLCString creds; 1.1064 + rv = GenCredsAndSetEntry(auth, mProxyAuth, 1.1065 + scheme.get(), host, port, path.get(), 1.1066 + realm.get(), mCurrentChallenge.get(), *ident, 1.1067 + sessionStateGrip, getter_Copies(creds)); 1.1068 + 1.1069 + mCurrentChallenge.Truncate(); 1.1070 + if (NS_FAILED(rv)) { 1.1071 + OnAuthCancelled(aContext, true); 1.1072 + return NS_OK; 1.1073 + } 1.1074 + 1.1075 + return ContinueOnAuthAvailable(creds); 1.1076 +} 1.1077 + 1.1078 +NS_IMETHODIMP nsHttpChannelAuthProvider::OnAuthCancelled(nsISupports *aContext, 1.1079 + bool userCancel) 1.1080 +{ 1.1081 + LOG(("nsHttpChannelAuthProvider::OnAuthCancelled [this=%p channel=%p]", 1.1082 + this, mAuthChannel)); 1.1083 + 1.1084 + mAsyncPromptAuthCancelable = nullptr; 1.1085 + if (!mAuthChannel) 1.1086 + return NS_OK; 1.1087 + 1.1088 + if (userCancel) { 1.1089 + if (!mRemainingChallenges.IsEmpty()) { 1.1090 + // there are still some challenges to process, do so 1.1091 + nsresult rv; 1.1092 + 1.1093 + nsAutoCString creds; 1.1094 + rv = GetCredentials(mRemainingChallenges.get(), mProxyAuth, creds); 1.1095 + if (NS_SUCCEEDED(rv)) { 1.1096 + // GetCredentials loaded the credentials from the cache or 1.1097 + // some other way in a synchronous manner, process those 1.1098 + // credentials now 1.1099 + mRemainingChallenges.Truncate(); 1.1100 + return ContinueOnAuthAvailable(creds); 1.1101 + } 1.1102 + else if (rv == NS_ERROR_IN_PROGRESS) { 1.1103 + // GetCredentials successfully queued another authprompt for 1.1104 + // a challenge from the list, we are now waiting for the user 1.1105 + // to provide the credentials 1.1106 + return NS_OK; 1.1107 + } 1.1108 + 1.1109 + // otherwise, we failed... 1.1110 + } 1.1111 + 1.1112 + mRemainingChallenges.Truncate(); 1.1113 + } 1.1114 + 1.1115 + mAuthChannel->OnAuthCancelled(userCancel); 1.1116 + 1.1117 + return NS_OK; 1.1118 +} 1.1119 + 1.1120 +nsresult 1.1121 +nsHttpChannelAuthProvider::ContinueOnAuthAvailable(const nsCSubstring& creds) 1.1122 +{ 1.1123 + nsresult rv; 1.1124 + if (mProxyAuth) 1.1125 + rv = mAuthChannel->SetProxyCredentials(creds); 1.1126 + else 1.1127 + rv = mAuthChannel->SetWWWCredentials(creds); 1.1128 + if (NS_FAILED(rv)) return rv; 1.1129 + 1.1130 + // drop our remaining list of challenges. We don't need them, because we 1.1131 + // have now authenticated against a challenge and will be sending that 1.1132 + // information to the server (or proxy). If it doesn't accept our 1.1133 + // authentication it'll respond with failure and resend the challenge list 1.1134 + mRemainingChallenges.Truncate(); 1.1135 + 1.1136 + mAuthChannel->OnAuthAvailable(); 1.1137 + 1.1138 + return NS_OK; 1.1139 +} 1.1140 + 1.1141 +bool 1.1142 +nsHttpChannelAuthProvider::ConfirmAuth(const nsString &bundleKey, 1.1143 + bool doYesNoPrompt) 1.1144 +{ 1.1145 + // skip prompting the user if 1.1146 + // 1) we've already prompted the user 1.1147 + // 2) we're not a toplevel channel 1.1148 + // 3) the userpass length is less than the "phishy" threshold 1.1149 + 1.1150 + uint32_t loadFlags; 1.1151 + nsresult rv = mAuthChannel->GetLoadFlags(&loadFlags); 1.1152 + if (NS_FAILED(rv)) 1.1153 + return true; 1.1154 + 1.1155 + if (mSuppressDefensiveAuth || 1.1156 + !(loadFlags & nsIChannel::LOAD_INITIAL_DOCUMENT_URI)) 1.1157 + return true; 1.1158 + 1.1159 + nsAutoCString userPass; 1.1160 + rv = mURI->GetUserPass(userPass); 1.1161 + if (NS_FAILED(rv) || 1.1162 + (userPass.Length() < gHttpHandler->PhishyUserPassLength())) 1.1163 + return true; 1.1164 + 1.1165 + // we try to confirm by prompting the user. if we cannot do so, then 1.1166 + // assume the user said ok. this is done to keep things working in 1.1167 + // embedded builds, where the string bundle might not be present, etc. 1.1168 + 1.1169 + nsCOMPtr<nsIStringBundleService> bundleService = 1.1170 + do_GetService(NS_STRINGBUNDLE_CONTRACTID); 1.1171 + if (!bundleService) 1.1172 + return true; 1.1173 + 1.1174 + nsCOMPtr<nsIStringBundle> bundle; 1.1175 + bundleService->CreateBundle(NECKO_MSGS_URL, getter_AddRefs(bundle)); 1.1176 + if (!bundle) 1.1177 + return true; 1.1178 + 1.1179 + nsAutoCString host; 1.1180 + rv = mURI->GetHost(host); 1.1181 + if (NS_FAILED(rv)) 1.1182 + return true; 1.1183 + 1.1184 + nsAutoCString user; 1.1185 + rv = mURI->GetUsername(user); 1.1186 + if (NS_FAILED(rv)) 1.1187 + return true; 1.1188 + 1.1189 + NS_ConvertUTF8toUTF16 ucsHost(host), ucsUser(user); 1.1190 + const char16_t *strs[2] = { ucsHost.get(), ucsUser.get() }; 1.1191 + 1.1192 + nsXPIDLString msg; 1.1193 + bundle->FormatStringFromName(bundleKey.get(), strs, 2, getter_Copies(msg)); 1.1194 + if (!msg) 1.1195 + return true; 1.1196 + 1.1197 + nsCOMPtr<nsIInterfaceRequestor> callbacks; 1.1198 + rv = mAuthChannel->GetNotificationCallbacks(getter_AddRefs(callbacks)); 1.1199 + if (NS_FAILED(rv)) 1.1200 + return true; 1.1201 + 1.1202 + nsCOMPtr<nsILoadGroup> loadGroup; 1.1203 + rv = mAuthChannel->GetLoadGroup(getter_AddRefs(loadGroup)); 1.1204 + if (NS_FAILED(rv)) 1.1205 + return true; 1.1206 + 1.1207 + nsCOMPtr<nsIPrompt> prompt; 1.1208 + NS_QueryNotificationCallbacks(callbacks, loadGroup, NS_GET_IID(nsIPrompt), 1.1209 + getter_AddRefs(prompt)); 1.1210 + if (!prompt) 1.1211 + return true; 1.1212 + 1.1213 + // do not prompt again 1.1214 + mSuppressDefensiveAuth = true; 1.1215 + 1.1216 + bool confirmed; 1.1217 + if (doYesNoPrompt) { 1.1218 + int32_t choice; 1.1219 + bool checkState = false; 1.1220 + rv = prompt->ConfirmEx(nullptr, msg, 1.1221 + nsIPrompt::BUTTON_POS_1_DEFAULT + 1.1222 + nsIPrompt::STD_YES_NO_BUTTONS, 1.1223 + nullptr, nullptr, nullptr, nullptr, 1.1224 + &checkState, &choice); 1.1225 + if (NS_FAILED(rv)) 1.1226 + return true; 1.1227 + 1.1228 + confirmed = choice == 0; 1.1229 + } 1.1230 + else { 1.1231 + rv = prompt->Confirm(nullptr, msg, &confirmed); 1.1232 + if (NS_FAILED(rv)) 1.1233 + return true; 1.1234 + } 1.1235 + 1.1236 + return confirmed; 1.1237 +} 1.1238 + 1.1239 +void 1.1240 +nsHttpChannelAuthProvider::SetAuthorizationHeader(nsHttpAuthCache *authCache, 1.1241 + nsHttpAtom header, 1.1242 + const char *scheme, 1.1243 + const char *host, 1.1244 + int32_t port, 1.1245 + const char *path, 1.1246 + nsHttpAuthIdentity &ident) 1.1247 +{ 1.1248 + nsHttpAuthEntry *entry = nullptr; 1.1249 + nsresult rv; 1.1250 + 1.1251 + // set informations that depend on whether 1.1252 + // we're authenticating against a proxy 1.1253 + // or a webserver 1.1254 + nsISupports **continuationState; 1.1255 + 1.1256 + if (header == nsHttp::Proxy_Authorization) { 1.1257 + continuationState = &mProxyAuthContinuationState; 1.1258 + } else { 1.1259 + continuationState = &mAuthContinuationState; 1.1260 + } 1.1261 + 1.1262 + nsCOMPtr<nsIChannel> chan = do_QueryInterface(mAuthChannel); 1.1263 + uint32_t appId; 1.1264 + bool isInBrowserElement; 1.1265 + GetAppIdAndBrowserStatus(chan, &appId, &isInBrowserElement); 1.1266 + 1.1267 + rv = authCache->GetAuthEntryForPath(scheme, host, port, path, 1.1268 + appId, isInBrowserElement, &entry); 1.1269 + if (NS_SUCCEEDED(rv)) { 1.1270 + // if we are trying to add a header for origin server auth and if the 1.1271 + // URL contains an explicit username, then try the given username first. 1.1272 + // we only want to do this, however, if we know the URL requires auth 1.1273 + // based on the presence of an auth cache entry for this URL (which is 1.1274 + // true since we are here). but, if the username from the URL matches 1.1275 + // the username from the cache, then we should prefer the password 1.1276 + // stored in the cache since that is most likely to be valid. 1.1277 + if (header == nsHttp::Authorization && entry->Domain()[0] == '\0') { 1.1278 + GetIdentityFromURI(0, ident); 1.1279 + // if the usernames match, then clear the ident so we will pick 1.1280 + // up the one from the auth cache instead. 1.1281 + // when this is undesired, specify LOAD_EXPLICIT_CREDENTIALS load 1.1282 + // flag. 1.1283 + if (nsCRT::strcmp(ident.User(), entry->User()) == 0) { 1.1284 + uint32_t loadFlags; 1.1285 + if (NS_SUCCEEDED(mAuthChannel->GetLoadFlags(&loadFlags)) && 1.1286 + !(loadFlags & nsIChannel::LOAD_EXPLICIT_CREDENTIALS)) { 1.1287 + ident.Clear(); 1.1288 + } 1.1289 + } 1.1290 + } 1.1291 + bool identFromURI; 1.1292 + if (ident.IsEmpty()) { 1.1293 + ident.Set(entry->Identity()); 1.1294 + identFromURI = false; 1.1295 + } 1.1296 + else 1.1297 + identFromURI = true; 1.1298 + 1.1299 + nsXPIDLCString temp; 1.1300 + const char *creds = entry->Creds(); 1.1301 + const char *challenge = entry->Challenge(); 1.1302 + // we can only send a preemptive Authorization header if we have either 1.1303 + // stored credentials or a stored challenge from which to derive 1.1304 + // credentials. if the identity is from the URI, then we cannot use 1.1305 + // the stored credentials. 1.1306 + if ((!creds[0] || identFromURI) && challenge[0]) { 1.1307 + nsCOMPtr<nsIHttpAuthenticator> auth; 1.1308 + nsAutoCString unused; 1.1309 + rv = GetAuthenticator(challenge, unused, getter_AddRefs(auth)); 1.1310 + if (NS_SUCCEEDED(rv)) { 1.1311 + bool proxyAuth = (header == nsHttp::Proxy_Authorization); 1.1312 + rv = GenCredsAndSetEntry(auth, proxyAuth, scheme, host, port, 1.1313 + path, entry->Realm(), challenge, ident, 1.1314 + entry->mMetaData, getter_Copies(temp)); 1.1315 + if (NS_SUCCEEDED(rv)) 1.1316 + creds = temp.get(); 1.1317 + 1.1318 + // make sure the continuation state is null since we do not 1.1319 + // support mixing preemptive and 'multirequest' authentication. 1.1320 + NS_IF_RELEASE(*continuationState); 1.1321 + } 1.1322 + } 1.1323 + if (creds[0]) { 1.1324 + LOG((" adding \"%s\" request header\n", header.get())); 1.1325 + if (header == nsHttp::Proxy_Authorization) 1.1326 + mAuthChannel->SetProxyCredentials(nsDependentCString(creds)); 1.1327 + else 1.1328 + mAuthChannel->SetWWWCredentials(nsDependentCString(creds)); 1.1329 + 1.1330 + // suppress defensive auth prompting for this channel since we know 1.1331 + // that we already prompted at least once this session. we only do 1.1332 + // this for non-proxy auth since the URL's userpass is not used for 1.1333 + // proxy auth. 1.1334 + if (header == nsHttp::Authorization) 1.1335 + mSuppressDefensiveAuth = true; 1.1336 + } 1.1337 + else 1.1338 + ident.Clear(); // don't remember the identity 1.1339 + } 1.1340 +} 1.1341 + 1.1342 +nsresult 1.1343 +nsHttpChannelAuthProvider::GetCurrentPath(nsACString &path) 1.1344 +{ 1.1345 + nsresult rv; 1.1346 + nsCOMPtr<nsIURL> url = do_QueryInterface(mURI); 1.1347 + if (url) 1.1348 + rv = url->GetDirectory(path); 1.1349 + else 1.1350 + rv = mURI->GetPath(path); 1.1351 + return rv; 1.1352 +} 1.1353 + 1.1354 +NS_IMPL_ISUPPORTS(nsHttpChannelAuthProvider, nsICancelable, 1.1355 + nsIHttpChannelAuthProvider, nsIAuthPromptCallback) 1.1356 + 1.1357 +} // namespace mozilla::net 1.1358 +} // namespace mozilla