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