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