/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim:set expandtab ts=4 sw=2 sts=2 cin: */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
// HttpLog.h should generally be included first #include"HttpLog.h"
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;
MOZ_ASSERT(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(mIsPrivate);
// check if proxy credentials should be sent if (!ProxyHost().IsEmpty() && UsingHttpProxy()) {
SetAuthorizationHeader(authCache, nsHttp::Proxy_Authorization, "http"_ns,
ProxyHost(), ProxyPort(), ""_ns, // proxy has no path
mProxyIdent);
}
if (loadFlags & nsIRequest::LOAD_ANONYMOUS) {
LOG(("Skipping Authorization header for anonymous load\n")); return NS_OK;
}
if (aDontUseCachedWWWCreds) {
LOG(
("Authorization header already present:" " skipping adding auth header from cache\n")); return NS_OK;
}
// check if server credentials should be sent
nsAutoCString path, scheme; if (NS_SUCCEEDED(GetCurrentPath(path)) &&
NS_SUCCEEDED(mURI->GetScheme(scheme))) {
SetAuthorizationHeader(authCache, nsHttp::Authorization, scheme, Host(),
Port(), path, mIdent);
}
MOZ_ASSERT(mAuthChannel, "Channel not initialized");
// we've been called because it has been determined that this channel is // getting loaded without taking the userpass from the URL. if the URL // contained a userpass, then (provided some other conditions are true), // we'll give the user an opportunity to abort the channel as this might be // an attempt to spoof a different site (see bug 232567). if (!ConfirmAuth("SuperfluousAuth", true)) { // calling cancel here sets our mStatus and aborts the HTTP // transaction, which prevents OnDataAvailable events.
Unused << mAuthChannel->Cancel(NS_ERROR_SUPERFLUOS_AUTH); return NS_ERROR_SUPERFLUOS_AUTH;
} return NS_OK;
}
NS_IMETHODIMP
nsHttpChannelAuthProvider::Cancel(nsresult status) {
MOZ_ASSERT(mAuthChannel, "Channel not initialized");
if (mAsyncPromptAuthCancelable) {
mAsyncPromptAuthCancelable->Cancel(status);
mAsyncPromptAuthCancelable = nullptr;
}
// helper function for getting an auth prompt from an interface requestor staticvoid GetAuthPrompt(nsIInterfaceRequestor* ifreq, bool proxyAuth,
nsIAuthPrompt2** result) { if (!ifreq) return;
// this getter never fails
nsHttpAuthCache* authCache = gHttpHandler->AuthCache(mIsPrivate);
nsAutoCString suffix; if (!aProxyAuth) { // We don't isolate proxy credentials cache entries with the origin suffix // as it would only annoy users with authentication dialogs popping up.
nsCOMPtr<nsIChannel> chan = do_QueryInterface(mAuthChannel);
GetOriginAttributesSuffix(chan, suffix);
}
// create a cache entry. we do this even though we don't yet know that // these credentials are valid b/c we need to avoid prompting the user // more than once in case the credentials are valid. // // if the credentials are not reusable, then we don't bother sticking // them in the auth cache.
rv = authCache->SetAuthEntry(scheme, host, port, directory, realm,
saveCreds ? creds : ""_ns,
saveChallenge ? challenge : ""_ns, suffix,
saveIdentity ? &ident : nullptr, sessionState); return rv;
}
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).
while (Next(t)) { if (t.Type() == TOKEN_EOL) {
Claim(result, ClaimInclusion::EXCLUDE_LAST);
SkipWhites(WhiteSkipping::INCLUDE_NEW_LINE);
Record();
inQuote = false; if (!result.IsEmpty()) { return Some(result);
}
} elseif (t.Equals(Token::Char(',')) && !inQuote) { // Sometimes we get multiple challenges separated by a comma. // This is not great, as it's slightly ambiguous. We check if something // is a new challenge by matching agains <param_name> = // If the , isn't followed by a word and = then most likely // it is the name of an authType.
auto hasWordAndEqual = [&]() {
SkipWhites();
nsDependentCSubstring word; if (!ReadWord(word)) { returnfalse;
}
SkipWhites(); return Check(Token::Char('='));
}; if (!hasWordAndEqual()) { // This is not a parameter. It means the `,` character starts a // different challenge. // We'll revert the cursor and return the contents so far.
mCursor = prevCursorPos;
mRollback = prevRollbackPos;
Claim(result, ClaimInclusion::EXCLUDE_LAST);
SkipWhites();
Record(); if (!result.IsEmpty()) { return Some(result);
}
}
} elseif (t.Equals(Token::Char('"'))) {
inQuote = !inQuote;
}
}
ChallengeParser p(challenges); while (true) { auto next = p.GetNext(); if (next.isNothing()) { break;
}
AuthChallenge ac{next.ref(), 0};
nsAutoCString realm, domain, nonce, opaque; bool stale = false;
uint16_t qop = 0;
ac.rank = Rank(ac.challenge); if (StringBeginsWith(ac.challenge, "Digest"_ns,
nsCaseInsensitiveCStringComparator)) {
Unused << nsHttpDigestAuth::ParseChallenge(ac.challenge, realm, domain,
nonce, opaque, &stale,
&ac.algorithm, &qop);
}
cc.AppendElement(ac);
}
// Returns true if an authorization is in progress auto authInProgress = [&]() -> bool { return proxyAuth ? mProxyAuthContinuationState : mAuthContinuationState;
};
// We shouldn't sort if authorization is already in progress // otherwise we might end up picking the wrong one. See bug 1805666 if (!authInProgress() ||
StaticPrefs::network_auth_sort_challenge_in_progress()) {
cc.StableSort([](const AuthChallenge& lhs, const AuthChallenge& rhs) { // Different auth types if (lhs.rank != rhs.rank) { return lhs.rank < rhs.rank ? 1 : -1;
}
// If they're the same auth type, and not a Digest, then we treat them // as equal (don't reorder them). if (lhs.rank != ChallengeRank::Digest) { return 0;
}
// If a preference to enable basic HTTP Auth is unset and the scheme is HTTP, // check if Basic is the sole available authentication challenge. if (NS_SUCCEEDED(mURI->GetScheme(scheme)) && scheme == "http"_ns &&
!StaticPrefs::network_http_basic_http_auth_enabled() &&
cc[0].rank == ChallengeRank::Basic) { // HTTP Auth and "Basic" is the sole available authentication. return NS_ERROR_BASIC_HTTP_AUTH_DISABLED;
}
nsCOMPtr<nsIHttpAuthenticator> auth;
nsCString authType; // force heap allocation to enable string sharing since // we'll be assigning this value into mAuthType.
// set informations that depend on whether we're authenticating against a // proxy or a webserver
nsISupports** currentContinuationState;
nsCString* currentAuthType;
// figure out which challenge we can handle and which authenticator to use. for (size_t i = 0; i < cc.Length(); i++) {
rv = GetAuthenticator(cc[i].challenge, authType, getter_AddRefs(auth));
LOG(("trying auth for %s", authType.get())); if (NS_SUCCEEDED(rv)) { // // if we've already selected an auth type from a previous challenge // received while processing this channel, then skip others until // we find a challenge corresponding to the previously tried auth // type. // if (!currentAuthType->IsEmpty() && authType != *currentAuthType) continue;
// // we allow the routines to run all the way through before we // decide if they are valid. // // we don't worry about the auth cache being altered because that // would have been the last step, and if the error is from updating // the authcache it wasn't really altered anyway. -CTN // // at this point the code is really only useful for client side // errors (it will not automatically fail over to do a different // auth type if the server keeps rejecting what is being sent, even // if a particular auth method only knows 1 thing, like a // non-identity based authentication method) //
rv = GetCredentialsForChallenge(cc[i].challenge, authType, proxyAuth,
auth, creds); if (NS_SUCCEEDED(rv)) {
gotCreds = true;
*currentAuthType = authType;
break;
} if (rv == NS_ERROR_IN_PROGRESS) { // authentication prompt has been invoked and result is // expected asynchronously, save current challenge being // processed and all remaining challenges to use later in // OnAuthAvailable and now immediately return
mCurrentChallenge = cc[i].challenge; // imperfect; does not save server-side preference ordering. // instead, continues with remaining string as provided by client
mRemainingChallenges.Truncate(); while (i + 1 < cc.Length()) {
i++;
mRemainingChallenges.Append(cc[i].challenge);
mRemainingChallenges.Append("\n"_ns);
} return rv;
}
// reset the auth type and continuation state
NS_IF_RELEASE(*currentContinuationState);
currentAuthType->Truncate();
}
}
if (!gotCreds && !currentAuthType->IsEmpty()) { // looks like we never found the auth type we were looking for. // reset the auth type and continuation state, and try again.
currentAuthType->Truncate();
NS_IF_RELEASE(*currentContinuationState);
// 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
nsAutoCString host;
int32_t port;
nsHttpAuthIdentity* ident;
nsAutoCString path, scheme; bool identFromURI = false;
nsISupports** continuationState;
uint32_t loadFlags;
rv = mAuthChannel->GetLoadFlags(&loadFlags); if (NS_FAILED(rv)) return rv;
// Fill only for non-proxy auth, proxy credentials are not OA-isolated.
nsAutoCString suffix;
if (!proxyAuth) {
nsCOMPtr<nsIChannel> chan = do_QueryInterface(mAuthChannel);
GetOriginAttributesSuffix(chan, suffix);
// 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
} elseif ((loadFlags & nsIRequest::LOAD_ANONYMOUS) && !UsingHttpProxy()) {
LOG(("Skipping authentication for anonymous non-proxy request\n")); return NS_ERROR_NOT_AVAILABLE;
}
// // 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;
Unused << authCache->GetAuthEntryForDomain(scheme, host, port, realm, suffix,
&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;
// remember if we already had the continuation state. it means we are in // the middle of the authentication exchange and the connection must be // kept sticky then (and only then). bool authAtProgress = !!*continuationState;
if (mConnectionBased && identityInvalid) { // If the flag is set and identity is invalid, it means we received the // first challange for a new negotiation round after negotiating a // connection based auth failed (invalid password). The mConnectionBased // flag is set later for the newly received challenge, so here it reflects // the previous 401/7 response schema.
rv = mAuthChannel->CloseStickyConnection();
MOZ_ASSERT(NS_SUCCEEDED(rv)); if (!proxyAuth) { // We must clear proxy ident in the following scenario + explanation: // - we are authenticating to an NTLM proxy and an NTLM server // - we successfully authenticated to the proxy, mProxyIdent keeps // the user name/domain and password, the identity has also been cached // - we just threw away the connection because we are now asking for // creds for the server (WWW auth) // - hence, we will have to auth to the proxy again as well // - if we didn't clear the proxy identity, it would be considered // as non-valid and we would ask the user again ; clearing it forces // use of the cached identity and not asking the user again
ClearProxyIdent();
}
}
// It's legal if the peer closes the connection after the first 401/7. // Making the connection sticky will prevent its restart giving the user // a 'network reset' error every time. Hence, we mark the connection // as restartable.
mAuthChannel->ConnectionRestartable(!authAtProgress);
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, host, port, realm, suffix);
entry = nullptr;
ident->Clear();
}
} elseif (!identFromURI ||
(ident->User() == entry->Identity().User() &&
!(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 = entry->Identity();
identFromURI = false; if (entry->Creds()[0] != '\0') {
LOG((" using cached credentials!\n"));
creds.Assign(entry->Creds()); return entry->AddPath(path);
}
}
} elseif (!identFromURI) { // hmm... identity invalid, but no auth entry! the realm probably // changed (see bug 201986).
ident->Clear();
}
// Depending on the pref setting, the authentication dialog may be // blocked for all sub-resources, blocked for cross-origin // sub-resources, or always allowed for sub-resources. // For more details look at the bug 647010. // BlockPrompt will set mCrossOrigin parameter as well. if (BlockPrompt(proxyAuth)) {
LOG(( "nsHttpChannelAuthProvider::GetCredentialsForChallenge: " "Prompt is blocked [this=%p pref=%d img-pref=%d " "non-web-content-triggered-pref=%d]\n", this, StaticPrefs::network_auth_subresource_http_auth_allow(),
StaticPrefs::
network_auth_subresource_img_cross_origin_http_auth_allow(),
StaticPrefs::
network_auth_non_web_content_triggered_resources_http_auth_allow())); return NS_ERROR_ABORT;
}
// 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, aAuthType, 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("AutomaticAuth", false)) { // calling cancel here sets our mStatus and aborts the HTTP // transaction, which prevents OnDataAvailable events.
rv = mAuthChannel->Cancel(NS_ERROR_ABORT);
MOZ_ASSERT(NS_SUCCEEDED(rv)); // 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. //
nsCString result;
rv = GenCredsAndSetEntry(auth, proxyAuth, scheme, host, port, path, realm,
aChallenge, *ident, sessionStateGrip, creds); return rv;
}
if (!topDoc &&
!StaticPrefs::
network_auth_non_web_content_triggered_resources_http_auth_allow() &&
nonWebContent) { returntrue;
}
switch (StaticPrefs::network_auth_subresource_http_auth_allow()) { case SUBRESOURCE_AUTH_DIALOG_DISALLOW_ALL: // Do not open the http-authentication credentials dialog for // the sub-resources. return !topDoc && !xhr; case SUBRESOURCE_AUTH_DIALOG_DISALLOW_CROSS_ORIGIN: // Open the http-authentication credentials dialog for // the sub-resources only if they are not cross-origin. return !topDoc && !xhr && mCrossOrigin; case SUBRESOURCE_AUTH_DIALOG_ALLOW_ALL: // Allow the http-authentication dialog for subresources. // If pref network.auth.subresource-img-cross-origin-http-auth-allow // is set, http-authentication dialog for image subresources is // blocked. if (mCrossOrigin &&
!StaticPrefs::
network_auth_subresource_img_cross_origin_http_auth_allow() &&
loadInfo &&
((loadInfo->GetExternalContentPolicyType() ==
ExtContentPolicy::TYPE_IMAGE) ||
(loadInfo->GetExternalContentPolicyType() ==
ExtContentPolicy::TYPE_IMAGESET))) { returntrue;
} returnfalse; default: // This is an invalid value.
MOZ_ASSERT(false, "A non valid value!");
} returnfalse;
}
if (!authenticator) { // If called during shutdown it's possible that the singleton authenticator // was already cleared so we have a null one here. return NS_ERROR_NOT_AVAILABLE;
}
void nsHttpChannelAuthProvider::ParseRealm(const nsACString& aChallenge,
nsACString& realm) { // // From RFC2617 section 1.2, the realm value is defined as such: // // realm = "realm" "=" realm-value // realm-value = quoted-string // // but, we'll accept anything after the the "=" up to the first space, or // end-of-line, if the string is not quoted. //
Tokenizer t(aChallenge);
// The challenge begins with the authType. // If we can't find that something has probably gone wrong.
t.SkipWhites();
nsDependentCSubstring authType; if (!t.ReadWord(authType)) { return;
}
// Will return true if the tokenizer advanced the cursor - false otherwise. auto readParam = [&](nsDependentCSubstring& key, nsAutoCString& value) {
key.Rebind(EmptyCString(), 0);
value.Truncate();
t.SkipWhites(); if (!t.ReadWord(key)) { returnfalse;
}
t.SkipWhites(); if (!t.CheckChar('=')) { returntrue;
}
t.SkipWhites();
while (!t.CheckEOF()) {
nsDependentCSubstring key;
nsAutoCString value; // If we couldn't read anything, and the input isn't followed by a , // then we exit. if (!readParam(key, value) && !t.Check(Tokenizer::Token::Char(','))) { break;
} // When we find the first instance of realm we exit. // Theoretically there should be only one instance and we should fail // if there are more, but we're trying to preserve existing behaviour. if (key.Equals("realm"_ns, nsCaseInsensitiveCStringComparator)) {
realm = value; break;
}
}
}
class nsHTTPAuthInformation : public nsAuthInformationHolder { public:
nsHTTPAuthInformation(uint32_t aFlags, const nsString& aRealm, const nsACString& aAuthType)
: nsAuthInformationHolder(aFlags, aRealm, aAuthType) {}
if (NS_SUCCEEDED(rv)) { // indicate using this error code that authentication prompt // result is expected asynchronously
rv = NS_ERROR_IN_PROGRESS;
} else { // Fall back to synchronous prompt bool retval = false;
rv = authPrompt->PromptAuth(channel, level, holder, &retval); if (NS_FAILED(rv)) return rv;
// remember that we successfully showed the user an auth dialog if (!proxyAuth) mSuppressDefensiveAuth = true;
if (mConnectionBased) { // Connection can be reset by the server in the meantime user is entering // the credentials. Result would be just a "Connection was reset" error. // Hence, we drop the current regardless if the user would make it on time // to provide credentials. // It's OK to send the NTLM type 1 message (response to the plain "NTLM" // challenge) on a new connection.
{
DebugOnly<nsresult> rv = mAuthChannel->CloseStickyConnection();
MOZ_ASSERT(NS_SUCCEEDED(rv));
}
}
nsCOMPtr<nsIChannel> chan = do_QueryInterface(mAuthChannel);
nsAutoCString suffix; if (!mProxyAuth) { // Fill only for non-proxy auth, proxy credentials are not OA-isolated.
GetOriginAttributesSuffix(chan, suffix);
}
mAsyncPromptAuthCancelable = nullptr; if (!mAuthChannel) return NS_OK;
// When user cancels or auth fails we want to close the connection for // connection based schemes like NTLM. Some servers don't like re-negotiation // on the same connection.
nsresult rv; if (mConnectionBased) {
rv = mAuthChannel->CloseStickyConnection();
MOZ_ASSERT(NS_SUCCEEDED(rv));
mConnectionBased = false;
}
nsCOMPtr<nsIChannel> channel = do_QueryInterface(mAuthChannel); if (channel) {
nsresult status;
Unused << channel->GetStatus(&status); if (NS_FAILED(status)) { // If the channel is already cancelled, there is no need to deal with the // rest challenges.
LOG((" Clear mRemainingChallenges, since mAuthChannel is cancelled"));
mRemainingChallenges.Truncate();
}
}
if (userCancel) { if (!mRemainingChallenges.IsEmpty()) { // there are still some challenges to process, do so
// Get rid of current continuationState to avoid reusing it in // next challenges since it is no longer relevant. if (mProxyAuth) {
NS_IF_RELEASE(mProxyAuthContinuationState);
} else {
NS_IF_RELEASE(mAuthContinuationState);
}
nsAutoCString creds;
rv = GetCredentials(mRemainingChallenges, mProxyAuth, creds); if (NS_SUCCEEDED(rv)) { // GetCredentials loaded the credentials from the cache or // some other way in a synchronous manner, process those // credentials now
mRemainingChallenges.Truncate(); return ContinueOnAuthAvailable(creds);
} if (rv == NS_ERROR_IN_PROGRESS) { // GetCredentials successfully queued another authprompt for // a challenge from the list, we are now waiting for the user // to provide the credentials return NS_OK;
}
// When channel is closed, do not proceed if (!mAuthChannel) { return NS_OK;
}
mGenerateCredentialsCancelable = nullptr;
if (NS_FAILED(aResult)) { return OnAuthCancelled(nullptr, true);
}
// We want to update m(Proxy)AuthContinuationState in case it was changed by // nsHttpNegotiateAuth::GenerateCredentials
nsCOMPtr<nsISupports> contState(aContinuationState); if (mProxyAuth) {
contState.swap(mProxyAuthContinuationState);
} else {
contState.swap(mAuthContinuationState);
}
// drop our remaining list of challenges. We don't need them, because we // have now authenticated against a challenge and will be sending that // information to the server (or proxy). If it doesn't accept our // authentication it'll respond with failure and resend the challenge list
mRemainingChallenges.Truncate();
Unused << mAuthChannel->OnAuthAvailable();
return NS_OK;
}
bool nsHttpChannelAuthProvider::ConfirmAuth(constchar* bundleKey, bool doYesNoPrompt) { // skip prompting the user if // 1) prompts are disabled by pref // 2) we've already prompted the user // 3) we're not a toplevel channel // 4) the userpass length is less than the "phishy" threshold
if (!StaticPrefs::network_auth_confirmAuth_enabled()) { returntrue;
}
uint32_t loadFlags;
nsresult rv = mAuthChannel->GetLoadFlags(&loadFlags); if (NS_FAILED(rv)) returntrue;
if (mSuppressDefensiveAuth ||
!(loadFlags & nsIChannel::LOAD_INITIAL_DOCUMENT_URI)) { returntrue;
}
// we try to confirm by prompting the user. if we cannot do so, then // assume the user said ok. this is done to keep things working in // embedded builds, where the string bundle might not be present, etc.
nsCOMPtr<nsIStringBundleService> bundleService;
bundleService = mozilla::components::StringBundle::Service(); if (!bundleService) returntrue;
nsCOMPtr<nsIStringBundle> bundle;
bundleService->CreateBundle(NECKO_MSGS_URL, getter_AddRefs(bundle)); if (!bundle) returntrue;
nsAutoCString host;
rv = mURI->GetHost(host); if (NS_FAILED(rv)) returntrue;
nsAutoCString user;
rv = mURI->GetUsername(user); if (NS_FAILED(rv)) returntrue;
size_t userLength = ucsUser.Length(); if (userLength > MAX_DISPLAYED_USER_LENGTH) {
size_t desiredLength = MAX_DISPLAYED_USER_LENGTH; // Don't cut off right before a low surrogate. Just include it. if (NS_IS_LOW_SURROGATE(ucsUser[desiredLength])) {
desiredLength++;
}
ucsUser.Replace(desiredLength, userLength - desiredLength,
nsContentUtils::GetLocalizedEllipsis());
}
size_t hostLen = ucsHost.Length(); if (hostLen > MAX_DISPLAYED_HOST_LENGTH) {
size_t cutPoint = hostLen - MAX_DISPLAYED_HOST_LENGTH; // Likewise, don't cut off right before a low surrogate here. // Keep the low surrogate if (NS_IS_LOW_SURROGATE(ucsHost[cutPoint])) {
cutPoint--;
} // It's possible cutPoint was 1 and is now 0. Only insert the ellipsis // if we're actually removing anything. if (cutPoint > 0) {
ucsHost.Replace(0, cutPoint, nsContentUtils::GetLocalizedEllipsis());
}
}
// set informations that depend on whether // we're authenticating against a proxy // or a webserver
nsISupports** continuationState;
nsAutoCString suffix; if (header == nsHttp::Proxy_Authorization) {
continuationState = &mProxyAuthContinuationState;
if (mProxyInfo) {
nsAutoCString type;
mProxyInfo->GetType(type); if (type.EqualsLiteral("https")) { // Let this be overriden by anything from the cache. autoconst& pa = mProxyInfo->ProxyAuthorizationHeader(); if (!pa.IsEmpty()) {
rv = mAuthChannel->SetProxyCredentials(pa);
MOZ_ASSERT(NS_SUCCEEDED(rv));
}
}
}
} else {
continuationState = &mAuthContinuationState;
rv = authCache->GetAuthEntryForPath(scheme, host, port, path, suffix, &entry); if (NS_SUCCEEDED(rv)) { // if we are trying to add a header for origin server auth and if the // URL contains an explicit username, then try the given username first. // we only want to do this, however, if we know the URL requires auth // based on the presence of an auth cache entry for this URL (which is // true since we are here). but, if the username from the URL matches // the username from the cache, then we should prefer the password // stored in the cache since that is most likely to be valid. if (header == nsHttp::Authorization && entry->Domain()[0] == '\0') {
GetIdentityFromURI(0, ident); // if the usernames match, then clear the ident so we will pick // up the one from the auth cache instead. // when this is undesired, specify LOAD_EXPLICIT_CREDENTIALS load // flag. if (ident.User() == entry->User()) {
uint32_t loadFlags; if (NS_SUCCEEDED(mAuthChannel->GetLoadFlags(&loadFlags)) &&
!(loadFlags & nsIChannel::LOAD_EXPLICIT_CREDENTIALS)) {
ident.Clear();
}
}
} bool identFromURI; if (ident.IsEmpty()) {
ident = entry->Identity();
identFromURI = false;
} else {
identFromURI = true;
}
nsCString temp; // this must have the same lifetime as creds
nsAutoCString creds(entry->Creds()); // we can only send a preemptive Authorization header if we have either // stored credentials or a stored challenge from which to derive // credentials. if the identity is from the URI, then we cannot use // the stored credentials. if ((creds.IsEmpty() || identFromURI) && !entry->Challenge().IsEmpty()) {
nsCOMPtr<nsIHttpAuthenticator> auth;
nsAutoCString unused;
rv = GetAuthenticator(entry->Challenge(), unused, getter_AddRefs(auth)); if (NS_SUCCEEDED(rv)) { bool proxyAuth = (header == nsHttp::Proxy_Authorization);
rv = GenCredsAndSetEntry(auth, proxyAuth, scheme, host, port, path,
entry->Realm(), entry->Challenge(), ident,
entry->mMetaData, temp); if (NS_SUCCEEDED(rv)) creds = temp;
// make sure the continuation state is null since we do not // support mixing preemptive and 'multirequest' authentication.
NS_IF_RELEASE(*continuationState);
}
} if (!creds.IsEmpty()) {
LOG((" adding \"%s\" request header\n", header.get())); if (header == nsHttp::Proxy_Authorization) {
rv = mAuthChannel->SetProxyCredentials(creds);
MOZ_ASSERT(NS_SUCCEEDED(rv));
} else {
rv = mAuthChannel->SetWWWCredentials(creds);
MOZ_ASSERT(NS_SUCCEEDED(rv));
}
// suppress defensive auth prompting for this channel since we know // that we already prompted at least once this session. we only do // this for non-proxy auth since the URL's userpass is not used for // proxy auth. if (header == nsHttp::Authorization) mSuppressDefensiveAuth = true;
} else {
ident.Clear(); // don't remember the identity
}
}
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung ist noch experimentell.