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