Line data Source code
1 : /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 : /* vim:set expandtab ts=4 sw=4 sts=4 cin: */
3 : /* This Source Code Form is subject to the terms of the Mozilla Public
4 : * License, v. 2.0. If a copy of the MPL was not distributed with this
5 : * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 :
7 : // HttpLog.h should generally be included first
8 : #include "HttpLog.h"
9 :
10 : #include "mozilla/Preferences.h"
11 : #include "nsHttpChannelAuthProvider.h"
12 : #include "nsNetUtil.h"
13 : #include "nsHttpHandler.h"
14 : #include "nsIHttpAuthenticator.h"
15 : #include "nsIHttpChannelInternal.h"
16 : #include "nsIAuthPrompt2.h"
17 : #include "nsIAuthPromptProvider.h"
18 : #include "nsIInterfaceRequestor.h"
19 : #include "nsIInterfaceRequestorUtils.h"
20 : #include "nsEscape.h"
21 : #include "nsAuthInformationHolder.h"
22 : #include "nsIStringBundle.h"
23 : #include "nsIPrompt.h"
24 : #include "netCore.h"
25 : #include "nsIHttpAuthenticableChannel.h"
26 : #include "nsIURI.h"
27 : #include "nsContentUtils.h"
28 : #include "nsServiceManagerUtils.h"
29 : #include "nsILoadContext.h"
30 : #include "nsIURL.h"
31 : #include "mozilla/Telemetry.h"
32 : #include "nsIProxiedChannel.h"
33 : #include "nsIProxyInfo.h"
34 :
35 : namespace mozilla {
36 : namespace net {
37 :
38 : #define SUBRESOURCE_AUTH_DIALOG_DISALLOW_ALL 0
39 : #define SUBRESOURCE_AUTH_DIALOG_DISALLOW_CROSS_ORIGIN 1
40 : #define SUBRESOURCE_AUTH_DIALOG_ALLOW_ALL 2
41 :
42 : #define HTTP_AUTH_DIALOG_TOP_LEVEL_DOC 29
43 : #define HTTP_AUTH_DIALOG_SAME_ORIGIN_SUBRESOURCE 30
44 : #define HTTP_AUTH_DIALOG_SAME_ORIGIN_XHR 31
45 :
46 : #define HTTP_AUTH_BASIC_INSECURE 0
47 : #define HTTP_AUTH_BASIC_SECURE 1
48 : #define HTTP_AUTH_DIGEST_INSECURE 2
49 : #define HTTP_AUTH_DIGEST_SECURE 3
50 : #define HTTP_AUTH_NTLM_INSECURE 4
51 : #define HTTP_AUTH_NTLM_SECURE 5
52 : #define HTTP_AUTH_NEGOTIATE_INSECURE 6
53 : #define HTTP_AUTH_NEGOTIATE_SECURE 7
54 :
55 : #define MAX_DISPLAYED_USER_LENGTH 64
56 : #define MAX_DISPLAYED_HOST_LENGTH 64
57 :
58 : static void
59 6 : GetOriginAttributesSuffix(nsIChannel* aChan, nsACString &aSuffix)
60 : {
61 12 : OriginAttributes oa;
62 :
63 : // Deliberately ignoring the result and going with defaults
64 6 : if (aChan) {
65 6 : NS_GetOriginAttributes(aChan, oa);
66 : }
67 :
68 6 : oa.CreateSuffix(aSuffix);
69 6 : }
70 :
71 6 : nsHttpChannelAuthProvider::nsHttpChannelAuthProvider()
72 : : mAuthChannel(nullptr)
73 : , mPort(-1)
74 : , mUsingSSL(false)
75 : , mProxyUsingSSL(false)
76 : , mIsPrivate(false)
77 : , mProxyAuthContinuationState(nullptr)
78 : , mAuthContinuationState(nullptr)
79 : , mProxyAuth(false)
80 : , mTriedProxyAuth(false)
81 : , mTriedHostAuth(false)
82 : , mSuppressDefensiveAuth(false)
83 : , mCrossOrigin(false)
84 : , mConnectionBased(false)
85 6 : , mHttpHandler(gHttpHandler)
86 : {
87 6 : }
88 :
89 18 : nsHttpChannelAuthProvider::~nsHttpChannelAuthProvider()
90 : {
91 6 : MOZ_ASSERT(!mAuthChannel, "Disconnect wasn't called");
92 18 : }
93 :
94 : uint32_t nsHttpChannelAuthProvider::sAuthAllowPref =
95 : SUBRESOURCE_AUTH_DIALOG_ALLOW_ALL;
96 :
97 : bool nsHttpChannelAuthProvider::sImgCrossOriginAuthAllowPref = true;
98 :
99 : void
100 2 : nsHttpChannelAuthProvider::InitializePrefs()
101 : {
102 2 : MOZ_ASSERT(NS_IsMainThread());
103 : mozilla::Preferences::AddUintVarCache(&sAuthAllowPref,
104 : "network.auth.subresource-http-auth-allow",
105 2 : SUBRESOURCE_AUTH_DIALOG_ALLOW_ALL);
106 : mozilla::Preferences::AddBoolVarCache(&sImgCrossOriginAuthAllowPref,
107 : "network.auth.subresource-img-cross-origin-http-auth-allow",
108 2 : true);
109 2 : }
110 :
111 : NS_IMETHODIMP
112 6 : nsHttpChannelAuthProvider::Init(nsIHttpAuthenticableChannel *channel)
113 : {
114 6 : MOZ_ASSERT(channel, "channel expected!");
115 :
116 6 : mAuthChannel = channel;
117 :
118 6 : nsresult rv = mAuthChannel->GetURI(getter_AddRefs(mURI));
119 6 : if (NS_FAILED(rv)) return rv;
120 :
121 6 : rv = mAuthChannel->GetIsSSL(&mUsingSSL);
122 6 : if (NS_FAILED(rv)) return rv;
123 :
124 12 : nsCOMPtr<nsIProxiedChannel> proxied(do_QueryInterface(channel));
125 6 : if (proxied) {
126 12 : nsCOMPtr<nsIProxyInfo> pi;
127 6 : rv = proxied->GetProxyInfo(getter_AddRefs(pi));
128 6 : if (NS_FAILED(rv)) return rv;
129 :
130 6 : if (pi) {
131 0 : nsAutoCString proxyType;
132 0 : rv = pi->GetType(proxyType);
133 0 : if (NS_FAILED(rv)) return rv;
134 :
135 0 : mProxyUsingSSL = proxyType.EqualsLiteral("https");
136 : }
137 : }
138 :
139 6 : rv = mURI->GetAsciiHost(mHost);
140 6 : if (NS_FAILED(rv)) return rv;
141 :
142 : // reject the URL if it doesn't specify a host
143 6 : if (mHost.IsEmpty())
144 0 : return NS_ERROR_MALFORMED_URI;
145 :
146 6 : rv = mURI->GetPort(&mPort);
147 6 : if (NS_FAILED(rv)) return rv;
148 :
149 12 : nsCOMPtr<nsIChannel> bareChannel = do_QueryInterface(channel);
150 6 : mIsPrivate = NS_UsePrivateBrowsing(bareChannel);
151 :
152 6 : return NS_OK;
153 : }
154 :
155 : NS_IMETHODIMP
156 0 : nsHttpChannelAuthProvider::ProcessAuthentication(uint32_t httpStatus,
157 : bool SSLConnectFailed)
158 : {
159 0 : LOG(("nsHttpChannelAuthProvider::ProcessAuthentication "
160 : "[this=%p channel=%p code=%u SSLConnectFailed=%d]\n",
161 : this, mAuthChannel, httpStatus, SSLConnectFailed));
162 :
163 0 : MOZ_ASSERT(mAuthChannel, "Channel not initialized");
164 :
165 0 : nsCOMPtr<nsIProxyInfo> proxyInfo;
166 0 : nsresult rv = mAuthChannel->GetProxyInfo(getter_AddRefs(proxyInfo));
167 0 : if (NS_FAILED(rv)) return rv;
168 0 : if (proxyInfo) {
169 0 : mProxyInfo = do_QueryInterface(proxyInfo);
170 0 : if (!mProxyInfo) return NS_ERROR_NO_INTERFACE;
171 : }
172 :
173 0 : nsAutoCString challenges;
174 0 : mProxyAuth = (httpStatus == 407);
175 :
176 0 : rv = PrepareForAuthentication(mProxyAuth);
177 0 : if (NS_FAILED(rv))
178 0 : return rv;
179 :
180 0 : if (mProxyAuth) {
181 : // only allow a proxy challenge if we have a proxy server configured.
182 : // otherwise, we could inadvertently expose the user's proxy
183 : // credentials to an origin server. We could attempt to proceed as
184 : // if we had received a 401 from the server, but why risk flirting
185 : // with trouble? IE similarly rejects 407s when a proxy server is
186 : // not configured, so there's no reason not to do the same.
187 0 : if (!UsingHttpProxy()) {
188 0 : LOG(("rejecting 407 when proxy server not configured!\n"));
189 0 : return NS_ERROR_UNEXPECTED;
190 : }
191 0 : if (UsingSSL() && !SSLConnectFailed) {
192 : // we need to verify that this challenge came from the proxy
193 : // server itself, and not some server on the other side of the
194 : // SSL tunnel.
195 0 : LOG(("rejecting 407 from origin server!\n"));
196 0 : return NS_ERROR_UNEXPECTED;
197 : }
198 0 : rv = mAuthChannel->GetProxyChallenges(challenges);
199 : }
200 : else
201 0 : rv = mAuthChannel->GetWWWChallenges(challenges);
202 0 : if (NS_FAILED(rv)) return rv;
203 :
204 0 : nsAutoCString creds;
205 0 : rv = GetCredentials(challenges.get(), mProxyAuth, creds);
206 0 : if (rv == NS_ERROR_IN_PROGRESS)
207 0 : return rv;
208 0 : if (NS_FAILED(rv))
209 0 : LOG(("unable to authenticate\n"));
210 : else {
211 : // set the authentication credentials
212 0 : if (mProxyAuth)
213 0 : rv = mAuthChannel->SetProxyCredentials(creds);
214 : else
215 0 : rv = mAuthChannel->SetWWWCredentials(creds);
216 : }
217 0 : return rv;
218 : }
219 :
220 : NS_IMETHODIMP
221 6 : nsHttpChannelAuthProvider::AddAuthorizationHeaders(bool aDontUseCachedWWWCreds)
222 : {
223 6 : LOG(("nsHttpChannelAuthProvider::AddAuthorizationHeaders? "
224 : "[this=%p channel=%p]\n", this, mAuthChannel));
225 :
226 6 : MOZ_ASSERT(mAuthChannel, "Channel not initialized");
227 :
228 12 : nsCOMPtr<nsIProxyInfo> proxyInfo;
229 6 : nsresult rv = mAuthChannel->GetProxyInfo(getter_AddRefs(proxyInfo));
230 6 : if (NS_FAILED(rv)) return rv;
231 6 : if (proxyInfo) {
232 0 : mProxyInfo = do_QueryInterface(proxyInfo);
233 0 : if (!mProxyInfo) return NS_ERROR_NO_INTERFACE;
234 : }
235 :
236 : uint32_t loadFlags;
237 6 : rv = mAuthChannel->GetLoadFlags(&loadFlags);
238 6 : if (NS_FAILED(rv)) return rv;
239 :
240 : // this getter never fails
241 6 : nsHttpAuthCache *authCache = gHttpHandler->AuthCache(mIsPrivate);
242 :
243 : // check if proxy credentials should be sent
244 6 : const char *proxyHost = ProxyHost();
245 6 : if (proxyHost && UsingHttpProxy()) {
246 0 : SetAuthorizationHeader(authCache, nsHttp::Proxy_Authorization,
247 : "http", proxyHost, ProxyPort(),
248 : nullptr, // proxy has no path
249 0 : mProxyIdent);
250 : }
251 :
252 6 : if (loadFlags & nsIRequest::LOAD_ANONYMOUS) {
253 0 : LOG(("Skipping Authorization header for anonymous load\n"));
254 0 : return NS_OK;
255 : }
256 :
257 6 : if (aDontUseCachedWWWCreds) {
258 0 : LOG(("Authorization header already present:"
259 : " skipping adding auth header from cache\n"));
260 0 : return NS_OK;
261 : }
262 :
263 : // check if server credentials should be sent
264 12 : nsAutoCString path, scheme;
265 12 : if (NS_SUCCEEDED(GetCurrentPath(path)) &&
266 6 : NS_SUCCEEDED(mURI->GetScheme(scheme))) {
267 6 : SetAuthorizationHeader(authCache, nsHttp::Authorization,
268 : scheme.get(),
269 : Host(),
270 : Port(),
271 : path.get(),
272 6 : mIdent);
273 : }
274 :
275 6 : return NS_OK;
276 : }
277 :
278 : NS_IMETHODIMP
279 3 : nsHttpChannelAuthProvider::CheckForSuperfluousAuth()
280 : {
281 3 : LOG(("nsHttpChannelAuthProvider::CheckForSuperfluousAuth? "
282 : "[this=%p channel=%p]\n", this, mAuthChannel));
283 :
284 3 : MOZ_ASSERT(mAuthChannel, "Channel not initialized");
285 :
286 : // we've been called because it has been determined that this channel is
287 : // getting loaded without taking the userpass from the URL. if the URL
288 : // contained a userpass, then (provided some other conditions are true),
289 : // we'll give the user an opportunity to abort the channel as this might be
290 : // an attempt to spoof a different site (see bug 232567).
291 3 : if (!ConfirmAuth(NS_LITERAL_STRING("SuperfluousAuth"), true)) {
292 : // calling cancel here sets our mStatus and aborts the HTTP
293 : // transaction, which prevents OnDataAvailable events.
294 0 : Unused << mAuthChannel->Cancel(NS_ERROR_ABORT);
295 0 : return NS_ERROR_ABORT;
296 : }
297 3 : return NS_OK;
298 : }
299 :
300 : NS_IMETHODIMP
301 1 : nsHttpChannelAuthProvider::Cancel(nsresult status)
302 : {
303 1 : MOZ_ASSERT(mAuthChannel, "Channel not initialized");
304 :
305 1 : if (mAsyncPromptAuthCancelable) {
306 0 : mAsyncPromptAuthCancelable->Cancel(status);
307 0 : mAsyncPromptAuthCancelable = nullptr;
308 : }
309 :
310 1 : if (mGenerateCredentialsCancelable) {
311 0 : mGenerateCredentialsCancelable->Cancel(status);
312 0 : mGenerateCredentialsCancelable = nullptr;
313 : }
314 1 : return NS_OK;
315 : }
316 :
317 : NS_IMETHODIMP
318 6 : nsHttpChannelAuthProvider::Disconnect(nsresult status)
319 : {
320 6 : mAuthChannel = nullptr;
321 :
322 6 : if (mAsyncPromptAuthCancelable) {
323 0 : mAsyncPromptAuthCancelable->Cancel(status);
324 0 : mAsyncPromptAuthCancelable = nullptr;
325 : }
326 :
327 6 : if (mGenerateCredentialsCancelable) {
328 0 : mGenerateCredentialsCancelable->Cancel(status);
329 0 : mGenerateCredentialsCancelable = nullptr;
330 : }
331 :
332 6 : NS_IF_RELEASE(mProxyAuthContinuationState);
333 6 : NS_IF_RELEASE(mAuthContinuationState);
334 :
335 6 : return NS_OK;
336 : }
337 :
338 : // buf contains "domain\user"
339 : static void
340 0 : ParseUserDomain(char16_t *buf,
341 : const char16_t **user,
342 : const char16_t **domain)
343 : {
344 0 : char16_t *p = buf;
345 0 : while (*p && *p != '\\') ++p;
346 0 : if (!*p)
347 0 : return;
348 0 : *p = '\0';
349 0 : *domain = buf;
350 0 : *user = p + 1;
351 : }
352 :
353 : // helper function for setting identity from raw user:pass
354 : static void
355 0 : SetIdent(nsHttpAuthIdentity &ident,
356 : uint32_t authFlags,
357 : char16_t *userBuf,
358 : char16_t *passBuf)
359 : {
360 0 : const char16_t *user = userBuf;
361 0 : const char16_t *domain = nullptr;
362 :
363 0 : if (authFlags & nsIHttpAuthenticator::IDENTITY_INCLUDES_DOMAIN)
364 0 : ParseUserDomain(userBuf, &user, &domain);
365 :
366 0 : DebugOnly<nsresult> rv = ident.Set(domain, user, passBuf);
367 0 : MOZ_ASSERT(NS_SUCCEEDED(rv));
368 0 : }
369 :
370 : // helper function for getting an auth prompt from an interface requestor
371 : static void
372 0 : GetAuthPrompt(nsIInterfaceRequestor *ifreq, bool proxyAuth,
373 : nsIAuthPrompt2 **result)
374 : {
375 0 : if (!ifreq)
376 0 : return;
377 :
378 : uint32_t promptReason;
379 0 : if (proxyAuth)
380 0 : promptReason = nsIAuthPromptProvider::PROMPT_PROXY;
381 : else
382 0 : promptReason = nsIAuthPromptProvider::PROMPT_NORMAL;
383 :
384 0 : nsCOMPtr<nsIAuthPromptProvider> promptProvider = do_GetInterface(ifreq);
385 0 : if (promptProvider)
386 0 : promptProvider->GetAuthPrompt(promptReason,
387 : NS_GET_IID(nsIAuthPrompt2),
388 0 : reinterpret_cast<void**>(result));
389 : else
390 0 : NS_QueryAuthPrompt2(ifreq, result);
391 : }
392 :
393 : // generate credentials for the given challenge, and update the auth cache.
394 : nsresult
395 0 : nsHttpChannelAuthProvider::GenCredsAndSetEntry(nsIHttpAuthenticator *auth,
396 : bool proxyAuth,
397 : const char *scheme,
398 : const char *host,
399 : int32_t port,
400 : const char *directory,
401 : const char *realm,
402 : const char *challenge,
403 : const nsHttpAuthIdentity &ident,
404 : nsCOMPtr<nsISupports> &sessionState,
405 : char **result)
406 : {
407 : nsresult rv;
408 0 : nsISupports *ss = sessionState;
409 :
410 : // set informations that depend on whether
411 : // we're authenticating against a proxy
412 : // or a webserver
413 : nsISupports **continuationState;
414 :
415 0 : if (proxyAuth) {
416 0 : continuationState = &mProxyAuthContinuationState;
417 : } else {
418 0 : continuationState = &mAuthContinuationState;
419 : }
420 :
421 0 : rv = auth->GenerateCredentialsAsync(mAuthChannel,
422 : this,
423 : challenge,
424 : proxyAuth,
425 : ident.Domain(),
426 : ident.User(),
427 : ident.Password(),
428 : ss,
429 : *continuationState,
430 0 : getter_AddRefs(mGenerateCredentialsCancelable));
431 0 : if (NS_SUCCEEDED(rv)) {
432 : // Calling generate credentials async, results will be dispatched to the
433 : // main thread by calling OnCredsGenerated method
434 0 : return NS_ERROR_IN_PROGRESS;
435 : }
436 :
437 : uint32_t generateFlags;
438 0 : rv = auth->GenerateCredentials(mAuthChannel,
439 : challenge,
440 : proxyAuth,
441 : ident.Domain(),
442 : ident.User(),
443 : ident.Password(),
444 : &ss,
445 : &*continuationState,
446 : &generateFlags,
447 0 : result);
448 :
449 0 : sessionState.swap(ss);
450 0 : if (NS_FAILED(rv)) return rv;
451 :
452 : // don't log this in release build since it could contain sensitive info.
453 : #ifdef DEBUG
454 0 : LOG(("generated creds: %s\n", *result));
455 : #endif
456 :
457 0 : return UpdateCache(auth, scheme, host, port, directory, realm,
458 0 : challenge, ident, *result, generateFlags, sessionState);
459 : }
460 :
461 : nsresult
462 0 : nsHttpChannelAuthProvider::UpdateCache(nsIHttpAuthenticator *auth,
463 : const char *scheme,
464 : const char *host,
465 : int32_t port,
466 : const char *directory,
467 : const char *realm,
468 : const char *challenge,
469 : const nsHttpAuthIdentity &ident,
470 : const char *creds,
471 : uint32_t generateFlags,
472 : nsISupports *sessionState)
473 : {
474 : nsresult rv;
475 :
476 : uint32_t authFlags;
477 0 : rv = auth->GetAuthFlags(&authFlags);
478 0 : if (NS_FAILED(rv)) return rv;
479 :
480 : // find out if this authenticator allows reuse of credentials and/or
481 : // challenge.
482 : bool saveCreds =
483 0 : 0 != (authFlags & nsIHttpAuthenticator::REUSABLE_CREDENTIALS);
484 : bool saveChallenge =
485 0 : 0 != (authFlags & nsIHttpAuthenticator::REUSABLE_CHALLENGE);
486 :
487 : bool saveIdentity =
488 0 : 0 == (generateFlags & nsIHttpAuthenticator::USING_INTERNAL_IDENTITY);
489 :
490 : // this getter never fails
491 0 : nsHttpAuthCache *authCache = gHttpHandler->AuthCache(mIsPrivate);
492 :
493 0 : nsCOMPtr<nsIChannel> chan = do_QueryInterface(mAuthChannel);
494 0 : nsAutoCString suffix;
495 0 : GetOriginAttributesSuffix(chan, suffix);
496 :
497 :
498 : // create a cache entry. we do this even though we don't yet know that
499 : // these credentials are valid b/c we need to avoid prompting the user
500 : // more than once in case the credentials are valid.
501 : //
502 : // if the credentials are not reusable, then we don't bother sticking
503 : // them in the auth cache.
504 0 : rv = authCache->SetAuthEntry(scheme, host, port, directory, realm,
505 : saveCreds ? creds : nullptr,
506 : saveChallenge ? challenge : nullptr,
507 : suffix,
508 : saveIdentity ? &ident : nullptr,
509 0 : sessionState);
510 0 : return rv;
511 :
512 : }
513 :
514 : nsresult
515 0 : nsHttpChannelAuthProvider::PrepareForAuthentication(bool proxyAuth)
516 : {
517 0 : LOG(("nsHttpChannelAuthProvider::PrepareForAuthentication "
518 : "[this=%p channel=%p]\n", this, mAuthChannel));
519 :
520 0 : if (!proxyAuth) {
521 : // reset the current proxy continuation state because our last
522 : // authentication attempt was completed successfully.
523 0 : NS_IF_RELEASE(mProxyAuthContinuationState);
524 0 : LOG((" proxy continuation state has been reset"));
525 : }
526 :
527 0 : if (!UsingHttpProxy() || mProxyAuthType.IsEmpty())
528 0 : return NS_OK;
529 :
530 : // We need to remove any Proxy_Authorization header left over from a
531 : // non-request based authentication handshake (e.g., for NTLM auth).
532 :
533 0 : nsAutoCString contractId;
534 0 : contractId.Assign(NS_HTTP_AUTHENTICATOR_CONTRACTID_PREFIX);
535 0 : contractId.Append(mProxyAuthType);
536 :
537 : nsresult rv;
538 : nsCOMPtr<nsIHttpAuthenticator> precedingAuth =
539 0 : do_GetService(contractId.get(), &rv);
540 0 : if (NS_FAILED(rv))
541 0 : return rv;
542 :
543 : uint32_t precedingAuthFlags;
544 0 : rv = precedingAuth->GetAuthFlags(&precedingAuthFlags);
545 0 : if (NS_FAILED(rv))
546 0 : return rv;
547 :
548 0 : if (!(precedingAuthFlags & nsIHttpAuthenticator::REQUEST_BASED)) {
549 0 : nsAutoCString challenges;
550 0 : rv = mAuthChannel->GetProxyChallenges(challenges);
551 0 : if (NS_FAILED(rv)) {
552 : // delete the proxy authorization header because we weren't
553 : // asked to authenticate
554 0 : rv = mAuthChannel->SetProxyCredentials(EmptyCString());
555 0 : if (NS_FAILED(rv)) return rv;
556 0 : LOG((" cleared proxy authorization header"));
557 : }
558 : }
559 :
560 0 : return NS_OK;
561 : }
562 :
563 : nsresult
564 0 : nsHttpChannelAuthProvider::GetCredentials(const char *challenges,
565 : bool proxyAuth,
566 : nsCString& creds)
567 : {
568 0 : nsCOMPtr<nsIHttpAuthenticator> auth;
569 0 : nsAutoCString challenge;
570 :
571 0 : nsCString authType; // force heap allocation to enable string sharing since
572 : // we'll be assigning this value into mAuthType.
573 :
574 : // set informations that depend on whether we're authenticating against a
575 : // proxy or a webserver
576 : nsISupports **currentContinuationState;
577 : nsCString *currentAuthType;
578 :
579 0 : if (proxyAuth) {
580 0 : currentContinuationState = &mProxyAuthContinuationState;
581 0 : currentAuthType = &mProxyAuthType;
582 : } else {
583 0 : currentContinuationState = &mAuthContinuationState;
584 0 : currentAuthType = &mAuthType;
585 : }
586 :
587 0 : nsresult rv = NS_ERROR_NOT_AVAILABLE;
588 0 : bool gotCreds = false;
589 :
590 : // figure out which challenge we can handle and which authenticator to use.
591 0 : for (const char *eol = challenges - 1; eol; ) {
592 0 : const char *p = eol + 1;
593 :
594 : // get the challenge string (LF separated -- see nsHttpHeaderArray)
595 0 : if ((eol = strchr(p, '\n')) != nullptr)
596 0 : challenge.Assign(p, eol - p);
597 : else
598 0 : challenge.Assign(p);
599 :
600 0 : rv = GetAuthenticator(challenge.get(), authType, getter_AddRefs(auth));
601 0 : if (NS_SUCCEEDED(rv)) {
602 : //
603 : // if we've already selected an auth type from a previous challenge
604 : // received while processing this channel, then skip others until
605 : // we find a challenge corresponding to the previously tried auth
606 : // type.
607 : //
608 0 : if (!currentAuthType->IsEmpty() && authType != *currentAuthType)
609 0 : continue;
610 :
611 : //
612 : // we allow the routines to run all the way through before we
613 : // decide if they are valid.
614 : //
615 : // we don't worry about the auth cache being altered because that
616 : // would have been the last step, and if the error is from updating
617 : // the authcache it wasn't really altered anyway. -CTN
618 : //
619 : // at this point the code is really only useful for client side
620 : // errors (it will not automatically fail over to do a different
621 : // auth type if the server keeps rejecting what is being sent, even
622 : // if a particular auth method only knows 1 thing, like a
623 : // non-identity based authentication method)
624 : //
625 0 : rv = GetCredentialsForChallenge(challenge.get(), authType.get(),
626 0 : proxyAuth, auth, creds);
627 0 : if (NS_SUCCEEDED(rv)) {
628 0 : gotCreds = true;
629 0 : *currentAuthType = authType;
630 :
631 0 : break;
632 : }
633 0 : if (rv == NS_ERROR_IN_PROGRESS) {
634 : // authentication prompt has been invoked and result is
635 : // expected asynchronously, save current challenge being
636 : // processed and all remaining challenges to use later in
637 : // OnAuthAvailable and now immediately return
638 0 : mCurrentChallenge = challenge;
639 0 : mRemainingChallenges = eol ? eol+1 : nullptr;
640 0 : return rv;
641 : }
642 :
643 : // reset the auth type and continuation state
644 0 : NS_IF_RELEASE(*currentContinuationState);
645 0 : currentAuthType->Truncate();
646 : }
647 : }
648 :
649 0 : if (!gotCreds && !currentAuthType->IsEmpty()) {
650 : // looks like we never found the auth type we were looking for.
651 : // reset the auth type and continuation state, and try again.
652 0 : currentAuthType->Truncate();
653 0 : NS_IF_RELEASE(*currentContinuationState);
654 :
655 0 : rv = GetCredentials(challenges, proxyAuth, creds);
656 : }
657 :
658 0 : return rv;
659 : }
660 :
661 : nsresult
662 0 : nsHttpChannelAuthProvider::GetAuthorizationMembers(bool proxyAuth,
663 : nsACString& scheme,
664 : const char*& host,
665 : int32_t& port,
666 : nsACString& path,
667 : nsHttpAuthIdentity*& ident,
668 : nsISupports**& continuationState)
669 : {
670 0 : if (proxyAuth) {
671 0 : MOZ_ASSERT (UsingHttpProxy(),
672 : "proxyAuth is true, but no HTTP proxy is configured!");
673 :
674 0 : host = ProxyHost();
675 0 : port = ProxyPort();
676 0 : ident = &mProxyIdent;
677 0 : scheme.AssignLiteral("http");
678 :
679 0 : continuationState = &mProxyAuthContinuationState;
680 : }
681 : else {
682 0 : host = Host();
683 0 : port = Port();
684 0 : ident = &mIdent;
685 :
686 : nsresult rv;
687 0 : rv = GetCurrentPath(path);
688 0 : if (NS_FAILED(rv)) return rv;
689 :
690 0 : rv = mURI->GetScheme(scheme);
691 0 : if (NS_FAILED(rv)) return rv;
692 :
693 0 : continuationState = &mAuthContinuationState;
694 : }
695 :
696 0 : return NS_OK;
697 : }
698 :
699 : nsresult
700 0 : nsHttpChannelAuthProvider::GetCredentialsForChallenge(const char *challenge,
701 : const char *authType,
702 : bool proxyAuth,
703 : nsIHttpAuthenticator *auth,
704 : nsCString& creds)
705 : {
706 0 : LOG(("nsHttpChannelAuthProvider::GetCredentialsForChallenge "
707 : "[this=%p channel=%p proxyAuth=%d challenges=%s]\n",
708 : this, mAuthChannel, proxyAuth, challenge));
709 :
710 : // this getter never fails
711 0 : nsHttpAuthCache *authCache = gHttpHandler->AuthCache(mIsPrivate);
712 :
713 : uint32_t authFlags;
714 0 : nsresult rv = auth->GetAuthFlags(&authFlags);
715 0 : if (NS_FAILED(rv)) return rv;
716 :
717 0 : nsAutoCString realm;
718 0 : ParseRealm(challenge, realm);
719 :
720 : // if no realm, then use the auth type as the realm. ToUpperCase so the
721 : // ficticious realm stands out a bit more.
722 : // XXX this will cause some single signon misses!
723 : // XXX this was meant to be used with NTLM, which supplies no realm.
724 : /*
725 : if (realm.IsEmpty()) {
726 : realm = authType;
727 : ToUpperCase(realm);
728 : }
729 : */
730 :
731 : // set informations that depend on whether
732 : // we're authenticating against a proxy
733 : // or a webserver
734 : const char *host;
735 : int32_t port;
736 : nsHttpAuthIdentity *ident;
737 0 : nsAutoCString path, scheme;
738 0 : bool identFromURI = false;
739 : nsISupports **continuationState;
740 :
741 0 : rv = GetAuthorizationMembers(proxyAuth, scheme, host, port,
742 0 : path, ident, continuationState);
743 0 : if (NS_FAILED(rv)) return rv;
744 :
745 : uint32_t loadFlags;
746 0 : rv = mAuthChannel->GetLoadFlags(&loadFlags);
747 0 : if (NS_FAILED(rv)) return rv;
748 :
749 0 : if (!proxyAuth) {
750 : // if this is the first challenge, then try using the identity
751 : // specified in the URL.
752 0 : if (mIdent.IsEmpty()) {
753 0 : GetIdentityFromURI(authFlags, mIdent);
754 0 : identFromURI = !mIdent.IsEmpty();
755 : }
756 :
757 0 : if ((loadFlags & nsIRequest::LOAD_ANONYMOUS) && !identFromURI) {
758 0 : LOG(("Skipping authentication for anonymous non-proxy request\n"));
759 0 : return NS_ERROR_NOT_AVAILABLE;
760 : }
761 :
762 : // Let explicit URL credentials pass
763 : // regardless of the LOAD_ANONYMOUS flag
764 : }
765 0 : else if ((loadFlags & nsIRequest::LOAD_ANONYMOUS) && !UsingHttpProxy()) {
766 0 : LOG(("Skipping authentication for anonymous non-proxy request\n"));
767 0 : return NS_ERROR_NOT_AVAILABLE;
768 : }
769 :
770 0 : nsCOMPtr<nsIChannel> chan = do_QueryInterface(mAuthChannel);
771 0 : nsAutoCString suffix;
772 0 : GetOriginAttributesSuffix(chan, suffix);
773 :
774 : //
775 : // if we already tried some credentials for this transaction, then
776 : // we need to possibly clear them from the cache, unless the credentials
777 : // in the cache have changed, in which case we'd want to give them a
778 : // try instead.
779 : //
780 0 : nsHttpAuthEntry *entry = nullptr;
781 0 : Unused << authCache->GetAuthEntryForDomain(scheme.get(), host, port,
782 : realm.get(), suffix, &entry);
783 :
784 : // hold reference to the auth session state (in case we clear our
785 : // reference to the entry).
786 0 : nsCOMPtr<nsISupports> sessionStateGrip;
787 0 : if (entry)
788 0 : sessionStateGrip = entry->mMetaData;
789 :
790 : // remember if we already had the continuation state. it means we are in
791 : // the middle of the authentication exchange and the connection must be
792 : // kept sticky then (and only then).
793 0 : bool authAtProgress = !!*continuationState;
794 :
795 : // for digest auth, maybe our cached nonce value simply timed out...
796 : bool identityInvalid;
797 0 : nsISupports *sessionState = sessionStateGrip;
798 0 : rv = auth->ChallengeReceived(mAuthChannel,
799 : challenge,
800 : proxyAuth,
801 : &sessionState,
802 : &*continuationState,
803 0 : &identityInvalid);
804 0 : sessionStateGrip.swap(sessionState);
805 0 : if (NS_FAILED(rv)) return rv;
806 :
807 0 : LOG((" identity invalid = %d\n", identityInvalid));
808 :
809 0 : if (mConnectionBased && identityInvalid) {
810 : // If the flag is set and identity is invalid, it means we received the first
811 : // challange for a new negotiation round after negotiating a connection based
812 : // auth failed (invalid password).
813 : // The mConnectionBased flag is set later for the newly received challenge,
814 : // so here it reflects the previous 401/7 response schema.
815 0 : rv = mAuthChannel->CloseStickyConnection();
816 0 : MOZ_ASSERT(NS_SUCCEEDED(rv));
817 0 : if (!proxyAuth) {
818 : // We must clear proxy ident in the following scenario + explanation:
819 : // - we are authenticating to an NTLM proxy and an NTLM server
820 : // - we successfully authenticated to the proxy, mProxyIdent keeps
821 : // the user name/domain and password, the identity has also been cached
822 : // - we just threw away the connection because we are now asking for
823 : // creds for the server (WWW auth)
824 : // - hence, we will have to auth to the proxy again as well
825 : // - if we didn't clear the proxy identity, it would be considered
826 : // as non-valid and we would ask the user again ; clearing it forces
827 : // use of the cached identity and not asking the user again
828 0 : mProxyIdent.Clear();
829 : }
830 : }
831 :
832 0 : mConnectionBased = !!(authFlags & nsIHttpAuthenticator::CONNECTION_BASED);
833 :
834 : // It's legal if the peer closes the connection after the first 401/7.
835 : // Making the connection sticky will prevent its restart giving the user
836 : // a 'network reset' error every time. Hence, we mark the connection
837 : // as restartable.
838 0 : mAuthChannel->ConnectionRestartable(mConnectionBased && !authAtProgress);
839 :
840 0 : if (identityInvalid) {
841 0 : if (entry) {
842 0 : if (ident->Equals(entry->Identity())) {
843 0 : if (!identFromURI) {
844 0 : LOG((" clearing bad auth cache entry\n"));
845 : // ok, we've already tried this user identity, so clear the
846 : // corresponding entry from the auth cache.
847 0 : authCache->ClearAuthEntry(scheme.get(), host,
848 : port, realm.get(),
849 0 : suffix);
850 0 : entry = nullptr;
851 0 : ident->Clear();
852 : }
853 : }
854 0 : else if (!identFromURI ||
855 0 : (nsCRT::strcmp(ident->User(),
856 0 : entry->Identity().User()) == 0 &&
857 0 : !(loadFlags &
858 : (nsIChannel::LOAD_ANONYMOUS |
859 : nsIChannel::LOAD_EXPLICIT_CREDENTIALS)))) {
860 0 : LOG((" taking identity from auth cache\n"));
861 : // the password from the auth cache is more likely to be
862 : // correct than the one in the URL. at least, we know that it
863 : // works with the given username. it is possible for a server
864 : // to distinguish logons based on the supplied password alone,
865 : // but that would be quite unusual... and i don't think we need
866 : // to worry about such unorthodox cases.
867 0 : rv = ident->Set(entry->Identity());
868 0 : MOZ_ASSERT(NS_SUCCEEDED(rv));
869 0 : identFromURI = false;
870 0 : if (entry->Creds()[0] != '\0') {
871 0 : LOG((" using cached credentials!\n"));
872 0 : creds.Assign(entry->Creds());
873 0 : return entry->AddPath(path.get());
874 : }
875 : }
876 : }
877 0 : else if (!identFromURI) {
878 : // hmm... identity invalid, but no auth entry! the realm probably
879 : // changed (see bug 201986).
880 0 : ident->Clear();
881 : }
882 :
883 0 : if (!entry && ident->IsEmpty()) {
884 0 : uint32_t level = nsIAuthPrompt2::LEVEL_NONE;
885 0 : if ((!proxyAuth && mUsingSSL) || (proxyAuth && mProxyUsingSSL))
886 0 : level = nsIAuthPrompt2::LEVEL_SECURE;
887 0 : else if (authFlags & nsIHttpAuthenticator::IDENTITY_ENCRYPTED)
888 0 : level = nsIAuthPrompt2::LEVEL_PW_ENCRYPTED;
889 :
890 : // Collect statistics on how frequently the various types of HTTP
891 : // authentication are used over SSL and non-SSL connections.
892 0 : if (gHttpHandler->IsTelemetryEnabled()) {
893 0 : if (NS_LITERAL_CSTRING("basic").LowerCaseEqualsASCII(authType)) {
894 0 : Telemetry::Accumulate(Telemetry::HTTP_AUTH_TYPE_STATS,
895 0 : UsingSSL() ? HTTP_AUTH_BASIC_SECURE : HTTP_AUTH_BASIC_INSECURE);
896 0 : } else if (NS_LITERAL_CSTRING("digest").LowerCaseEqualsASCII(authType)) {
897 0 : Telemetry::Accumulate(Telemetry::HTTP_AUTH_TYPE_STATS,
898 0 : UsingSSL() ? HTTP_AUTH_DIGEST_SECURE : HTTP_AUTH_DIGEST_INSECURE);
899 0 : } else if (NS_LITERAL_CSTRING("ntlm").LowerCaseEqualsASCII(authType)) {
900 0 : Telemetry::Accumulate(Telemetry::HTTP_AUTH_TYPE_STATS,
901 0 : UsingSSL() ? HTTP_AUTH_NTLM_SECURE : HTTP_AUTH_NTLM_INSECURE);
902 0 : } else if (NS_LITERAL_CSTRING("negotiate").LowerCaseEqualsASCII(authType)) {
903 0 : Telemetry::Accumulate(Telemetry::HTTP_AUTH_TYPE_STATS,
904 0 : UsingSSL() ? HTTP_AUTH_NEGOTIATE_SECURE : HTTP_AUTH_NEGOTIATE_INSECURE);
905 : }
906 : }
907 :
908 : // Depending on the pref setting, the authentication dialog may be
909 : // blocked for all sub-resources, blocked for cross-origin
910 : // sub-resources, or always allowed for sub-resources.
911 : // For more details look at the bug 647010.
912 : // BlockPrompt will set mCrossOrigin parameter as well.
913 0 : if (BlockPrompt()) {
914 0 : LOG(("nsHttpChannelAuthProvider::GetCredentialsForChallenge: "
915 : "Prompt is blocked [this=%p pref=%d img-pref=%d]\n",
916 : this, sAuthAllowPref, sImgCrossOriginAuthAllowPref));
917 0 : return NS_ERROR_ABORT;
918 : }
919 :
920 : // at this point we are forced to interact with the user to get
921 : // their username and password for this domain.
922 0 : rv = PromptForIdentity(level, proxyAuth, realm.get(),
923 0 : authType, authFlags, *ident);
924 0 : if (NS_FAILED(rv)) return rv;
925 0 : identFromURI = false;
926 : }
927 : }
928 :
929 0 : if (identFromURI) {
930 : // Warn the user before automatically using the identity from the URL
931 : // to automatically log them into a site (see bug 232567).
932 0 : if (!ConfirmAuth(NS_LITERAL_STRING("AutomaticAuth"), false)) {
933 : // calling cancel here sets our mStatus and aborts the HTTP
934 : // transaction, which prevents OnDataAvailable events.
935 0 : rv = mAuthChannel->Cancel(NS_ERROR_ABORT);
936 0 : MOZ_ASSERT(NS_SUCCEEDED(rv));
937 : // this return code alone is not equivalent to Cancel, since
938 : // it only instructs our caller that authentication failed.
939 : // without an explicit call to Cancel, our caller would just
940 : // load the page that accompanies the HTTP auth challenge.
941 0 : return NS_ERROR_ABORT;
942 : }
943 : }
944 :
945 : //
946 : // get credentials for the given user:pass
947 : //
948 : // always store the credentials we're trying now so that they will be used
949 : // on subsequent links. This will potentially remove good credentials from
950 : // the cache. This is ok as we don't want to use cached credentials if the
951 : // user specified something on the URI or in another manner. This is so
952 : // that we don't transparently authenticate as someone they're not
953 : // expecting to authenticate as.
954 : //
955 0 : nsXPIDLCString result;
956 0 : rv = GenCredsAndSetEntry(auth, proxyAuth, scheme.get(), host, port,
957 : path.get(), realm.get(), challenge, *ident,
958 0 : sessionStateGrip, getter_Copies(result));
959 0 : if (NS_SUCCEEDED(rv))
960 0 : creds = result;
961 0 : return rv;
962 : }
963 :
964 : bool
965 0 : nsHttpChannelAuthProvider::BlockPrompt()
966 : {
967 : // Verify that it's ok to prompt for credentials here, per spec
968 : // http://xhr.spec.whatwg.org/#the-send%28%29-method
969 :
970 0 : nsCOMPtr<nsIHttpChannelInternal> chanInternal = do_QueryInterface(mAuthChannel);
971 0 : MOZ_ASSERT(chanInternal);
972 :
973 0 : if (chanInternal->GetBlockAuthPrompt()) {
974 0 : LOG(("nsHttpChannelAuthProvider::BlockPrompt: Prompt is blocked "
975 : "[this=%p channel=%p]\n", this, mAuthChannel));
976 0 : return true;
977 : }
978 :
979 0 : nsCOMPtr<nsIChannel> chan = do_QueryInterface(mAuthChannel);
980 0 : nsCOMPtr<nsILoadInfo> loadInfo;
981 0 : chan->GetLoadInfo(getter_AddRefs(loadInfo));
982 :
983 : // We will treat loads w/o loadInfo as a top level document.
984 0 : bool topDoc = true;
985 0 : bool xhr = false;
986 :
987 0 : if (loadInfo) {
988 0 : if (loadInfo->GetExternalContentPolicyType() !=
989 : nsIContentPolicy::TYPE_DOCUMENT) {
990 0 : topDoc = false;
991 : }
992 0 : if (loadInfo->GetExternalContentPolicyType() ==
993 : nsIContentPolicy::TYPE_XMLHTTPREQUEST) {
994 0 : xhr = true;
995 : }
996 :
997 0 : if (!topDoc && !xhr) {
998 0 : nsCOMPtr<nsIURI> topURI;
999 0 : Unused << chanInternal->GetTopWindowURI(getter_AddRefs(topURI));
1000 :
1001 0 : if (!topURI) {
1002 : // If we do not have topURI try the loadingPrincipal.
1003 0 : nsCOMPtr<nsIPrincipal> loadingPrinc = loadInfo->LoadingPrincipal();
1004 0 : if (loadingPrinc) {
1005 0 : loadingPrinc->GetURI(getter_AddRefs(topURI));
1006 : }
1007 : }
1008 :
1009 0 : if (!NS_SecurityCompareURIs(topURI, mURI, true)) {
1010 0 : mCrossOrigin = true;
1011 : }
1012 : }
1013 : }
1014 :
1015 0 : if (gHttpHandler->IsTelemetryEnabled()) {
1016 0 : if (topDoc) {
1017 : Telemetry::Accumulate(Telemetry::HTTP_AUTH_DIALOG_STATS_2,
1018 0 : HTTP_AUTH_DIALOG_TOP_LEVEL_DOC);
1019 0 : } else if (!mCrossOrigin) {
1020 0 : if (xhr) {
1021 : Telemetry::Accumulate(Telemetry::HTTP_AUTH_DIALOG_STATS_2,
1022 0 : HTTP_AUTH_DIALOG_SAME_ORIGIN_XHR);
1023 : } else {
1024 : Telemetry::Accumulate(Telemetry::HTTP_AUTH_DIALOG_STATS_2,
1025 0 : HTTP_AUTH_DIALOG_SAME_ORIGIN_SUBRESOURCE);
1026 : }
1027 : } else {
1028 0 : Telemetry::Accumulate(Telemetry::HTTP_AUTH_DIALOG_STATS_2,
1029 0 : loadInfo->GetExternalContentPolicyType());
1030 : }
1031 : }
1032 :
1033 0 : switch (sAuthAllowPref) {
1034 : case SUBRESOURCE_AUTH_DIALOG_DISALLOW_ALL:
1035 : // Do not open the http-authentication credentials dialog for
1036 : // the sub-resources.
1037 0 : return !topDoc && !xhr;
1038 : case SUBRESOURCE_AUTH_DIALOG_DISALLOW_CROSS_ORIGIN:
1039 : // Open the http-authentication credentials dialog for
1040 : // the sub-resources only if they are not cross-origin.
1041 0 : return !topDoc && !xhr && mCrossOrigin;
1042 : case SUBRESOURCE_AUTH_DIALOG_ALLOW_ALL:
1043 : // Allow the http-authentication dialog for subresources.
1044 : // If pref network.auth.subresource-img-cross-origin-http-auth-allow
1045 : // is set, http-authentication dialog for image subresources is
1046 : // blocked.
1047 0 : if (!sImgCrossOriginAuthAllowPref &&
1048 0 : loadInfo &&
1049 0 : ((loadInfo->GetExternalContentPolicyType() == nsIContentPolicy::TYPE_IMAGE) ||
1050 0 : (loadInfo->GetExternalContentPolicyType() == nsIContentPolicy::TYPE_IMAGESET))) {
1051 0 : return true;
1052 : }
1053 0 : return false;
1054 : default:
1055 : // This is an invalid value.
1056 0 : MOZ_ASSERT(false, "A non valid value!");
1057 : }
1058 : return false;
1059 : }
1060 :
1061 : inline void
1062 0 : GetAuthType(const char *challenge, nsCString &authType)
1063 : {
1064 : const char *p;
1065 :
1066 : // get the challenge type
1067 0 : if ((p = strchr(challenge, ' ')) != nullptr)
1068 0 : authType.Assign(challenge, p - challenge);
1069 : else
1070 0 : authType.Assign(challenge);
1071 0 : }
1072 :
1073 : nsresult
1074 0 : nsHttpChannelAuthProvider::GetAuthenticator(const char *challenge,
1075 : nsCString &authType,
1076 : nsIHttpAuthenticator **auth)
1077 : {
1078 0 : LOG(("nsHttpChannelAuthProvider::GetAuthenticator [this=%p channel=%p]\n",
1079 : this, mAuthChannel));
1080 :
1081 0 : GetAuthType(challenge, authType);
1082 :
1083 : // normalize to lowercase
1084 0 : ToLowerCase(authType);
1085 :
1086 0 : nsAutoCString contractid;
1087 0 : contractid.Assign(NS_HTTP_AUTHENTICATOR_CONTRACTID_PREFIX);
1088 0 : contractid.Append(authType);
1089 :
1090 0 : return CallGetService(contractid.get(), auth);
1091 : }
1092 :
1093 : void
1094 0 : nsHttpChannelAuthProvider::GetIdentityFromURI(uint32_t authFlags,
1095 : nsHttpAuthIdentity &ident)
1096 : {
1097 0 : LOG(("nsHttpChannelAuthProvider::GetIdentityFromURI [this=%p channel=%p]\n",
1098 : this, mAuthChannel));
1099 :
1100 0 : nsAutoString userBuf;
1101 0 : nsAutoString passBuf;
1102 :
1103 : // XXX i18n
1104 0 : nsAutoCString buf;
1105 0 : mURI->GetUsername(buf);
1106 0 : if (!buf.IsEmpty()) {
1107 0 : NS_UnescapeURL(buf);
1108 0 : CopyASCIItoUTF16(buf, userBuf);
1109 0 : mURI->GetPassword(buf);
1110 0 : if (!buf.IsEmpty()) {
1111 0 : NS_UnescapeURL(buf);
1112 0 : CopyASCIItoUTF16(buf, passBuf);
1113 : }
1114 : }
1115 :
1116 0 : if (!userBuf.IsEmpty()) {
1117 0 : SetIdent(ident, authFlags, (char16_t *) userBuf.get(),
1118 0 : (char16_t *) passBuf.get());
1119 : }
1120 0 : }
1121 :
1122 : void
1123 0 : nsHttpChannelAuthProvider::ParseRealm(const char *challenge,
1124 : nsACString &realm)
1125 : {
1126 : //
1127 : // From RFC2617 section 1.2, the realm value is defined as such:
1128 : //
1129 : // realm = "realm" "=" realm-value
1130 : // realm-value = quoted-string
1131 : //
1132 : // but, we'll accept anything after the the "=" up to the first space, or
1133 : // end-of-line, if the string is not quoted.
1134 : //
1135 :
1136 0 : const char *p = PL_strcasestr(challenge, "realm=");
1137 0 : if (p) {
1138 0 : bool has_quote = false;
1139 0 : p += 6;
1140 0 : if (*p == '"') {
1141 0 : has_quote = true;
1142 0 : p++;
1143 : }
1144 :
1145 : const char *end;
1146 0 : if (has_quote) {
1147 0 : end = p;
1148 0 : while (*end) {
1149 0 : if (*end == '\\') {
1150 : // escaped character, store that one instead if not zero
1151 0 : if (!*++end)
1152 0 : break;
1153 : }
1154 0 : else if (*end == '\"')
1155 : // end of string
1156 0 : break;
1157 :
1158 0 : realm.Append(*end);
1159 0 : ++end;
1160 : }
1161 : }
1162 : else {
1163 : // realm given without quotes
1164 0 : end = strchr(p, ' ');
1165 0 : if (end)
1166 0 : realm.Assign(p, end - p);
1167 : else
1168 0 : realm.Assign(p);
1169 : }
1170 : }
1171 0 : }
1172 :
1173 :
1174 0 : class nsHTTPAuthInformation : public nsAuthInformationHolder {
1175 : public:
1176 0 : nsHTTPAuthInformation(uint32_t aFlags, const nsString& aRealm,
1177 : const nsCString& aAuthType)
1178 0 : : nsAuthInformationHolder(aFlags, aRealm, aAuthType) {}
1179 :
1180 : void SetToHttpAuthIdentity(uint32_t authFlags,
1181 : nsHttpAuthIdentity& identity);
1182 : };
1183 :
1184 : void
1185 0 : nsHTTPAuthInformation::SetToHttpAuthIdentity(uint32_t authFlags,
1186 : nsHttpAuthIdentity& identity)
1187 : {
1188 0 : DebugOnly<nsresult> rv = identity.Set(Domain().get(), User().get(), Password().get());
1189 0 : MOZ_ASSERT(NS_SUCCEEDED(rv));
1190 0 : }
1191 :
1192 : nsresult
1193 0 : nsHttpChannelAuthProvider::PromptForIdentity(uint32_t level,
1194 : bool proxyAuth,
1195 : const char *realm,
1196 : const char *authType,
1197 : uint32_t authFlags,
1198 : nsHttpAuthIdentity &ident)
1199 : {
1200 0 : LOG(("nsHttpChannelAuthProvider::PromptForIdentity [this=%p channel=%p]\n",
1201 : this, mAuthChannel));
1202 :
1203 : nsresult rv;
1204 :
1205 0 : nsCOMPtr<nsIInterfaceRequestor> callbacks;
1206 0 : rv = mAuthChannel->GetNotificationCallbacks(getter_AddRefs(callbacks));
1207 0 : if (NS_FAILED(rv)) return rv;
1208 :
1209 0 : nsCOMPtr<nsILoadGroup> loadGroup;
1210 0 : rv = mAuthChannel->GetLoadGroup(getter_AddRefs(loadGroup));
1211 0 : if (NS_FAILED(rv)) return rv;
1212 :
1213 0 : nsCOMPtr<nsIAuthPrompt2> authPrompt;
1214 0 : GetAuthPrompt(callbacks, proxyAuth, getter_AddRefs(authPrompt));
1215 0 : if (!authPrompt && loadGroup) {
1216 0 : nsCOMPtr<nsIInterfaceRequestor> cbs;
1217 0 : loadGroup->GetNotificationCallbacks(getter_AddRefs(cbs));
1218 0 : GetAuthPrompt(cbs, proxyAuth, getter_AddRefs(authPrompt));
1219 : }
1220 0 : if (!authPrompt)
1221 0 : return NS_ERROR_NO_INTERFACE;
1222 :
1223 : // XXX i18n: need to support non-ASCII realm strings (see bug 41489)
1224 0 : NS_ConvertASCIItoUTF16 realmU(realm);
1225 :
1226 : // prompt the user...
1227 0 : uint32_t promptFlags = 0;
1228 0 : if (proxyAuth)
1229 : {
1230 0 : promptFlags |= nsIAuthInformation::AUTH_PROXY;
1231 0 : if (mTriedProxyAuth)
1232 0 : promptFlags |= nsIAuthInformation::PREVIOUS_FAILED;
1233 0 : mTriedProxyAuth = true;
1234 : }
1235 : else {
1236 0 : promptFlags |= nsIAuthInformation::AUTH_HOST;
1237 0 : if (mTriedHostAuth)
1238 0 : promptFlags |= nsIAuthInformation::PREVIOUS_FAILED;
1239 0 : mTriedHostAuth = true;
1240 : }
1241 :
1242 0 : if (authFlags & nsIHttpAuthenticator::IDENTITY_INCLUDES_DOMAIN)
1243 0 : promptFlags |= nsIAuthInformation::NEED_DOMAIN;
1244 :
1245 0 : if (mCrossOrigin) {
1246 0 : promptFlags |= nsIAuthInformation::CROSS_ORIGIN_SUB_RESOURCE;
1247 : }
1248 :
1249 : RefPtr<nsHTTPAuthInformation> holder =
1250 : new nsHTTPAuthInformation(promptFlags, realmU,
1251 0 : nsDependentCString(authType));
1252 0 : if (!holder)
1253 0 : return NS_ERROR_OUT_OF_MEMORY;
1254 :
1255 0 : nsCOMPtr<nsIChannel> channel(do_QueryInterface(mAuthChannel, &rv));
1256 0 : if (NS_FAILED(rv)) return rv;
1257 :
1258 0 : rv =
1259 0 : authPrompt->AsyncPromptAuth(channel, this, nullptr, level, holder,
1260 0 : getter_AddRefs(mAsyncPromptAuthCancelable));
1261 :
1262 0 : if (NS_SUCCEEDED(rv)) {
1263 : // indicate using this error code that authentication prompt
1264 : // result is expected asynchronously
1265 0 : rv = NS_ERROR_IN_PROGRESS;
1266 : }
1267 : else {
1268 : // Fall back to synchronous prompt
1269 0 : bool retval = false;
1270 0 : rv = authPrompt->PromptAuth(channel, level, holder, &retval);
1271 0 : if (NS_FAILED(rv))
1272 0 : return rv;
1273 :
1274 0 : if (!retval)
1275 0 : rv = NS_ERROR_ABORT;
1276 : else
1277 0 : holder->SetToHttpAuthIdentity(authFlags, ident);
1278 : }
1279 :
1280 : // remember that we successfully showed the user an auth dialog
1281 0 : if (!proxyAuth)
1282 0 : mSuppressDefensiveAuth = true;
1283 :
1284 0 : if (mConnectionBased) {
1285 : // Connection can be reset by the server in the meantime user is entering
1286 : // the credentials. Result would be just a "Connection was reset" error.
1287 : // Hence, we drop the current regardless if the user would make it on time
1288 : // to provide credentials.
1289 : // It's OK to send the NTLM type 1 message (response to the plain "NTLM"
1290 : // challenge) on a new connection.
1291 : {
1292 0 : DebugOnly<nsresult> rv = mAuthChannel->CloseStickyConnection();
1293 0 : MOZ_ASSERT(NS_SUCCEEDED(rv));
1294 : }
1295 : }
1296 :
1297 0 : return rv;
1298 : }
1299 :
1300 0 : NS_IMETHODIMP nsHttpChannelAuthProvider::OnAuthAvailable(nsISupports *aContext,
1301 : nsIAuthInformation *aAuthInfo)
1302 : {
1303 0 : LOG(("nsHttpChannelAuthProvider::OnAuthAvailable [this=%p channel=%p]",
1304 : this, mAuthChannel));
1305 :
1306 0 : mAsyncPromptAuthCancelable = nullptr;
1307 0 : if (!mAuthChannel)
1308 0 : return NS_OK;
1309 :
1310 : nsresult rv;
1311 :
1312 : const char *host;
1313 : int32_t port;
1314 : nsHttpAuthIdentity *ident;
1315 0 : nsAutoCString path, scheme;
1316 : nsISupports **continuationState;
1317 0 : rv = GetAuthorizationMembers(mProxyAuth, scheme, host, port,
1318 0 : path, ident, continuationState);
1319 0 : if (NS_FAILED(rv))
1320 0 : OnAuthCancelled(aContext, false);
1321 :
1322 0 : nsAutoCString realm;
1323 0 : ParseRealm(mCurrentChallenge.get(), realm);
1324 :
1325 0 : nsCOMPtr<nsIChannel> chan = do_QueryInterface(mAuthChannel);
1326 0 : nsAutoCString suffix;
1327 0 : GetOriginAttributesSuffix(chan, suffix);
1328 :
1329 0 : nsHttpAuthCache *authCache = gHttpHandler->AuthCache(mIsPrivate);
1330 0 : nsHttpAuthEntry *entry = nullptr;
1331 0 : Unused << authCache->GetAuthEntryForDomain(scheme.get(), host, port,
1332 : realm.get(), suffix,
1333 : &entry);
1334 :
1335 0 : nsCOMPtr<nsISupports> sessionStateGrip;
1336 0 : if (entry)
1337 0 : sessionStateGrip = entry->mMetaData;
1338 :
1339 : nsAuthInformationHolder* holder =
1340 0 : static_cast<nsAuthInformationHolder*>(aAuthInfo);
1341 0 : rv = ident->Set(holder->Domain().get(),
1342 0 : holder->User().get(),
1343 0 : holder->Password().get());
1344 0 : MOZ_ASSERT(NS_SUCCEEDED(rv));
1345 :
1346 0 : nsAutoCString unused;
1347 0 : nsCOMPtr<nsIHttpAuthenticator> auth;
1348 0 : rv = GetAuthenticator(mCurrentChallenge.get(), unused,
1349 0 : getter_AddRefs(auth));
1350 0 : if (NS_FAILED(rv)) {
1351 0 : MOZ_ASSERT(false, "GetAuthenticator failed");
1352 : OnAuthCancelled(aContext, true);
1353 : return NS_OK;
1354 : }
1355 :
1356 0 : nsXPIDLCString creds;
1357 0 : rv = GenCredsAndSetEntry(auth, mProxyAuth,
1358 : scheme.get(), host, port, path.get(),
1359 : realm.get(), mCurrentChallenge.get(), *ident,
1360 0 : sessionStateGrip, getter_Copies(creds));
1361 :
1362 0 : mCurrentChallenge.Truncate();
1363 0 : if (NS_FAILED(rv)) {
1364 0 : OnAuthCancelled(aContext, true);
1365 0 : return NS_OK;
1366 : }
1367 :
1368 0 : return ContinueOnAuthAvailable(creds);
1369 : }
1370 :
1371 0 : NS_IMETHODIMP nsHttpChannelAuthProvider::OnAuthCancelled(nsISupports *aContext,
1372 : bool userCancel)
1373 : {
1374 0 : LOG(("nsHttpChannelAuthProvider::OnAuthCancelled [this=%p channel=%p]",
1375 : this, mAuthChannel));
1376 :
1377 0 : mAsyncPromptAuthCancelable = nullptr;
1378 0 : if (!mAuthChannel)
1379 0 : return NS_OK;
1380 :
1381 : // When user cancels or auth fails we want to close the connection for
1382 : // connection based schemes like NTLM. Some servers don't like re-negotiation
1383 : // on the same connection.
1384 : nsresult rv;
1385 0 : if (mConnectionBased) {
1386 0 : rv = mAuthChannel->CloseStickyConnection();
1387 0 : MOZ_ASSERT(NS_SUCCEEDED(rv));
1388 0 : mConnectionBased = false;
1389 : }
1390 :
1391 0 : if (userCancel) {
1392 0 : if (!mRemainingChallenges.IsEmpty()) {
1393 : // there are still some challenges to process, do so
1394 :
1395 : // Get rid of current continuationState to avoid reusing it in
1396 : // next challenges since it is no longer relevant.
1397 0 : if (mProxyAuth) {
1398 0 : NS_IF_RELEASE(mProxyAuthContinuationState);
1399 : } else {
1400 0 : NS_IF_RELEASE(mAuthContinuationState);
1401 : }
1402 0 : nsAutoCString creds;
1403 0 : rv = GetCredentials(mRemainingChallenges.get(), mProxyAuth, creds);
1404 0 : if (NS_SUCCEEDED(rv)) {
1405 : // GetCredentials loaded the credentials from the cache or
1406 : // some other way in a synchronous manner, process those
1407 : // credentials now
1408 0 : mRemainingChallenges.Truncate();
1409 0 : return ContinueOnAuthAvailable(creds);
1410 : }
1411 0 : if (rv == NS_ERROR_IN_PROGRESS) {
1412 : // GetCredentials successfully queued another authprompt for
1413 : // a challenge from the list, we are now waiting for the user
1414 : // to provide the credentials
1415 0 : return NS_OK;
1416 : }
1417 :
1418 : // otherwise, we failed...
1419 : }
1420 :
1421 0 : mRemainingChallenges.Truncate();
1422 : }
1423 :
1424 0 : rv = mAuthChannel->OnAuthCancelled(userCancel);
1425 0 : MOZ_ASSERT(NS_SUCCEEDED(rv));
1426 :
1427 0 : return NS_OK;
1428 : }
1429 :
1430 0 : NS_IMETHODIMP nsHttpChannelAuthProvider::OnCredsGenerated(const char *aGeneratedCreds,
1431 : uint32_t aFlags,
1432 : nsresult aResult,
1433 : nsISupports* aSessionState,
1434 : nsISupports* aContinuationState)
1435 : {
1436 : nsresult rv;
1437 :
1438 0 : MOZ_ASSERT(NS_IsMainThread());
1439 :
1440 : // When channel is closed, do not proceed
1441 0 : if (!mAuthChannel) {
1442 0 : return NS_OK;
1443 : }
1444 :
1445 0 : mGenerateCredentialsCancelable = nullptr;
1446 :
1447 0 : if (NS_FAILED(aResult)) {
1448 0 : return OnAuthCancelled(nullptr, true);
1449 : }
1450 :
1451 : // We want to update m(Proxy)AuthContinuationState in case it was changed by
1452 : // nsHttpNegotiateAuth::GenerateCredentials
1453 0 : nsCOMPtr<nsISupports> contState(aContinuationState);
1454 0 : if (mProxyAuth) {
1455 0 : contState.swap(mProxyAuthContinuationState);
1456 : } else {
1457 0 : contState.swap(mAuthContinuationState);
1458 : }
1459 :
1460 0 : nsCOMPtr<nsIHttpAuthenticator> auth;
1461 0 : nsAutoCString unused;
1462 0 : rv = GetAuthenticator(mCurrentChallenge.get(), unused, getter_AddRefs(auth));
1463 0 : NS_ENSURE_SUCCESS(rv, rv);
1464 :
1465 : const char *host;
1466 : int32_t port;
1467 : nsHttpAuthIdentity *ident;
1468 0 : nsAutoCString directory, scheme;
1469 : nsISupports **unusedContinuationState;
1470 :
1471 : // Get realm from challenge
1472 0 : nsAutoCString realm;
1473 0 : ParseRealm(mCurrentChallenge.get(), realm);
1474 :
1475 0 : rv = GetAuthorizationMembers(mProxyAuth, scheme, host, port,
1476 0 : directory, ident, unusedContinuationState);
1477 0 : if (NS_FAILED(rv)) return rv;
1478 :
1479 0 : rv = UpdateCache(auth, scheme.get(), host, port, directory.get(),
1480 : realm.get(), mCurrentChallenge.get(), *ident,
1481 0 : aGeneratedCreds, aFlags, aSessionState);
1482 0 : MOZ_ASSERT(NS_SUCCEEDED(rv));
1483 0 : mCurrentChallenge.Truncate();
1484 :
1485 0 : rv = ContinueOnAuthAvailable(nsDependentCString(aGeneratedCreds));
1486 0 : MOZ_ASSERT(NS_SUCCEEDED(rv));
1487 0 : return NS_OK;
1488 : }
1489 :
1490 : nsresult
1491 0 : nsHttpChannelAuthProvider::ContinueOnAuthAvailable(const nsACString& creds)
1492 : {
1493 : nsresult rv;
1494 0 : if (mProxyAuth)
1495 0 : rv = mAuthChannel->SetProxyCredentials(creds);
1496 : else
1497 0 : rv = mAuthChannel->SetWWWCredentials(creds);
1498 0 : if (NS_FAILED(rv)) return rv;
1499 :
1500 : // drop our remaining list of challenges. We don't need them, because we
1501 : // have now authenticated against a challenge and will be sending that
1502 : // information to the server (or proxy). If it doesn't accept our
1503 : // authentication it'll respond with failure and resend the challenge list
1504 0 : mRemainingChallenges.Truncate();
1505 :
1506 0 : Unused << mAuthChannel->OnAuthAvailable();
1507 :
1508 0 : return NS_OK;
1509 : }
1510 :
1511 : bool
1512 3 : nsHttpChannelAuthProvider::ConfirmAuth(const nsString &bundleKey,
1513 : bool doYesNoPrompt)
1514 : {
1515 : // skip prompting the user if
1516 : // 1) we've already prompted the user
1517 : // 2) we're not a toplevel channel
1518 : // 3) the userpass length is less than the "phishy" threshold
1519 :
1520 : uint32_t loadFlags;
1521 3 : nsresult rv = mAuthChannel->GetLoadFlags(&loadFlags);
1522 3 : if (NS_FAILED(rv))
1523 0 : return true;
1524 :
1525 6 : if (mSuppressDefensiveAuth ||
1526 6 : !(loadFlags & nsIChannel::LOAD_INITIAL_DOCUMENT_URI))
1527 2 : return true;
1528 :
1529 2 : nsAutoCString userPass;
1530 1 : rv = mURI->GetUserPass(userPass);
1531 2 : if (NS_FAILED(rv) ||
1532 1 : (userPass.Length() < gHttpHandler->PhishyUserPassLength()))
1533 1 : return true;
1534 :
1535 : // we try to confirm by prompting the user. if we cannot do so, then
1536 : // assume the user said ok. this is done to keep things working in
1537 : // embedded builds, where the string bundle might not be present, etc.
1538 :
1539 : nsCOMPtr<nsIStringBundleService> bundleService =
1540 0 : do_GetService(NS_STRINGBUNDLE_CONTRACTID);
1541 0 : if (!bundleService)
1542 0 : return true;
1543 :
1544 0 : nsCOMPtr<nsIStringBundle> bundle;
1545 0 : bundleService->CreateBundle(NECKO_MSGS_URL, getter_AddRefs(bundle));
1546 0 : if (!bundle)
1547 0 : return true;
1548 :
1549 0 : nsAutoCString host;
1550 0 : rv = mURI->GetHost(host);
1551 0 : if (NS_FAILED(rv))
1552 0 : return true;
1553 :
1554 0 : nsAutoCString user;
1555 0 : rv = mURI->GetUsername(user);
1556 0 : if (NS_FAILED(rv))
1557 0 : return true;
1558 :
1559 0 : NS_ConvertUTF8toUTF16 ucsHost(host), ucsUser(user);
1560 :
1561 0 : size_t userLength = ucsUser.Length();
1562 0 : if (userLength > MAX_DISPLAYED_USER_LENGTH) {
1563 0 : size_t desiredLength = MAX_DISPLAYED_USER_LENGTH;
1564 : // Don't cut off right before a low surrogate. Just include it.
1565 0 : if (NS_IS_LOW_SURROGATE(ucsUser[desiredLength])) {
1566 0 : desiredLength++;
1567 : }
1568 0 : ucsUser.Replace(desiredLength, userLength - desiredLength,
1569 0 : nsContentUtils::GetLocalizedEllipsis());
1570 : }
1571 :
1572 0 : size_t hostLen = ucsHost.Length();
1573 0 : if (hostLen > MAX_DISPLAYED_HOST_LENGTH) {
1574 0 : size_t cutPoint = hostLen - MAX_DISPLAYED_HOST_LENGTH;
1575 : // Likewise, don't cut off right before a low surrogate here.
1576 : // Keep the low surrogate
1577 0 : if (NS_IS_LOW_SURROGATE(ucsHost[cutPoint])) {
1578 0 : cutPoint--;
1579 : }
1580 : // It's possible cutPoint was 1 and is now 0. Only insert the ellipsis
1581 : // if we're actually removing anything.
1582 0 : if (cutPoint > 0) {
1583 0 : ucsHost.Replace(0, cutPoint, nsContentUtils::GetLocalizedEllipsis());
1584 : }
1585 : }
1586 :
1587 0 : const char16_t *strs[2] = { ucsHost.get(), ucsUser.get() };
1588 :
1589 0 : nsXPIDLString msg;
1590 0 : bundle->FormatStringFromName(bundleKey.get(), strs, 2, getter_Copies(msg));
1591 0 : if (!msg)
1592 0 : return true;
1593 :
1594 0 : nsCOMPtr<nsIInterfaceRequestor> callbacks;
1595 0 : rv = mAuthChannel->GetNotificationCallbacks(getter_AddRefs(callbacks));
1596 0 : if (NS_FAILED(rv))
1597 0 : return true;
1598 :
1599 0 : nsCOMPtr<nsILoadGroup> loadGroup;
1600 0 : rv = mAuthChannel->GetLoadGroup(getter_AddRefs(loadGroup));
1601 0 : if (NS_FAILED(rv))
1602 0 : return true;
1603 :
1604 0 : nsCOMPtr<nsIPrompt> prompt;
1605 0 : NS_QueryNotificationCallbacks(callbacks, loadGroup, NS_GET_IID(nsIPrompt),
1606 0 : getter_AddRefs(prompt));
1607 0 : if (!prompt)
1608 0 : return true;
1609 :
1610 : // do not prompt again
1611 0 : mSuppressDefensiveAuth = true;
1612 :
1613 : bool confirmed;
1614 0 : if (doYesNoPrompt) {
1615 : int32_t choice;
1616 0 : bool checkState = false;
1617 0 : rv = prompt->ConfirmEx(nullptr, msg,
1618 : nsIPrompt::BUTTON_POS_1_DEFAULT +
1619 : nsIPrompt::STD_YES_NO_BUTTONS,
1620 : nullptr, nullptr, nullptr, nullptr,
1621 0 : &checkState, &choice);
1622 0 : if (NS_FAILED(rv))
1623 0 : return true;
1624 :
1625 0 : confirmed = choice == 0;
1626 : }
1627 : else {
1628 0 : rv = prompt->Confirm(nullptr, msg, &confirmed);
1629 0 : if (NS_FAILED(rv))
1630 0 : return true;
1631 : }
1632 :
1633 0 : return confirmed;
1634 : }
1635 :
1636 : void
1637 6 : nsHttpChannelAuthProvider::SetAuthorizationHeader(nsHttpAuthCache *authCache,
1638 : nsHttpAtom header,
1639 : const char *scheme,
1640 : const char *host,
1641 : int32_t port,
1642 : const char *path,
1643 : nsHttpAuthIdentity &ident)
1644 : {
1645 6 : nsHttpAuthEntry *entry = nullptr;
1646 : nsresult rv;
1647 :
1648 : // set informations that depend on whether
1649 : // we're authenticating against a proxy
1650 : // or a webserver
1651 : nsISupports **continuationState;
1652 :
1653 6 : if (header == nsHttp::Proxy_Authorization) {
1654 0 : continuationState = &mProxyAuthContinuationState;
1655 : } else {
1656 6 : continuationState = &mAuthContinuationState;
1657 : }
1658 :
1659 12 : nsCOMPtr<nsIChannel> chan = do_QueryInterface(mAuthChannel);
1660 12 : nsAutoCString suffix;
1661 6 : GetOriginAttributesSuffix(chan, suffix);
1662 :
1663 : rv = authCache->GetAuthEntryForPath(scheme, host, port, path,
1664 6 : suffix, &entry);
1665 6 : if (NS_SUCCEEDED(rv)) {
1666 : // if we are trying to add a header for origin server auth and if the
1667 : // URL contains an explicit username, then try the given username first.
1668 : // we only want to do this, however, if we know the URL requires auth
1669 : // based on the presence of an auth cache entry for this URL (which is
1670 : // true since we are here). but, if the username from the URL matches
1671 : // the username from the cache, then we should prefer the password
1672 : // stored in the cache since that is most likely to be valid.
1673 0 : if (header == nsHttp::Authorization && entry->Domain()[0] == '\0') {
1674 0 : GetIdentityFromURI(0, ident);
1675 : // if the usernames match, then clear the ident so we will pick
1676 : // up the one from the auth cache instead.
1677 : // when this is undesired, specify LOAD_EXPLICIT_CREDENTIALS load
1678 : // flag.
1679 0 : if (nsCRT::strcmp(ident.User(), entry->User()) == 0) {
1680 : uint32_t loadFlags;
1681 0 : if (NS_SUCCEEDED(mAuthChannel->GetLoadFlags(&loadFlags)) &&
1682 0 : !(loadFlags & nsIChannel::LOAD_EXPLICIT_CREDENTIALS)) {
1683 0 : ident.Clear();
1684 : }
1685 : }
1686 : }
1687 : bool identFromURI;
1688 0 : if (ident.IsEmpty()) {
1689 0 : rv = ident.Set(entry->Identity());
1690 0 : MOZ_ASSERT(NS_SUCCEEDED(rv));
1691 0 : identFromURI = false;
1692 : }
1693 : else
1694 0 : identFromURI = true;
1695 :
1696 0 : nsXPIDLCString temp;
1697 0 : const char *creds = entry->Creds();
1698 0 : const char *challenge = entry->Challenge();
1699 : // we can only send a preemptive Authorization header if we have either
1700 : // stored credentials or a stored challenge from which to derive
1701 : // credentials. if the identity is from the URI, then we cannot use
1702 : // the stored credentials.
1703 0 : if ((!creds[0] || identFromURI) && challenge[0]) {
1704 0 : nsCOMPtr<nsIHttpAuthenticator> auth;
1705 0 : nsAutoCString unused;
1706 0 : rv = GetAuthenticator(challenge, unused, getter_AddRefs(auth));
1707 0 : if (NS_SUCCEEDED(rv)) {
1708 0 : bool proxyAuth = (header == nsHttp::Proxy_Authorization);
1709 0 : rv = GenCredsAndSetEntry(auth, proxyAuth, scheme, host, port,
1710 : path, entry->Realm(), challenge, ident,
1711 0 : entry->mMetaData, getter_Copies(temp));
1712 0 : if (NS_SUCCEEDED(rv))
1713 0 : creds = temp.get();
1714 :
1715 : // make sure the continuation state is null since we do not
1716 : // support mixing preemptive and 'multirequest' authentication.
1717 0 : NS_IF_RELEASE(*continuationState);
1718 : }
1719 : }
1720 0 : if (creds[0]) {
1721 0 : LOG((" adding \"%s\" request header\n", header.get()));
1722 0 : if (header == nsHttp::Proxy_Authorization) {
1723 0 : rv = mAuthChannel->SetProxyCredentials(nsDependentCString(creds));
1724 0 : MOZ_ASSERT(NS_SUCCEEDED(rv));
1725 : }
1726 : else {
1727 0 : rv = mAuthChannel->SetWWWCredentials(nsDependentCString(creds));
1728 0 : MOZ_ASSERT(NS_SUCCEEDED(rv));
1729 : }
1730 :
1731 : // suppress defensive auth prompting for this channel since we know
1732 : // that we already prompted at least once this session. we only do
1733 : // this for non-proxy auth since the URL's userpass is not used for
1734 : // proxy auth.
1735 0 : if (header == nsHttp::Authorization)
1736 0 : mSuppressDefensiveAuth = true;
1737 : }
1738 : else
1739 0 : ident.Clear(); // don't remember the identity
1740 : }
1741 6 : }
1742 :
1743 : nsresult
1744 6 : nsHttpChannelAuthProvider::GetCurrentPath(nsACString &path)
1745 : {
1746 : nsresult rv;
1747 12 : nsCOMPtr<nsIURL> url = do_QueryInterface(mURI);
1748 6 : if (url)
1749 6 : rv = url->GetDirectory(path);
1750 : else
1751 0 : rv = mURI->GetPath(path);
1752 12 : return rv;
1753 : }
1754 :
1755 48 : NS_IMPL_ISUPPORTS(nsHttpChannelAuthProvider, nsICancelable,
1756 : nsIHttpChannelAuthProvider, nsIAuthPromptCallback, nsIHttpAuthenticatorCallback)
1757 :
1758 : } // namespace net
1759 : } // namespace mozilla
|