Coverage Report

Created: 2018-09-25 14:53

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