Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/netwerk/protocol/http/nsHttpNTLMAuth.cpp
Line
Count
Source (jump to first uncovered line)
1
/* vim:set ts=4 sw=4 sts=4 et ci: */
2
/* This Source Code Form is subject to the terms of the Mozilla Public
3
 * License, v. 2.0. If a copy of the MPL was not distributed with this
4
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5
6
// HttpLog.h should generally be included first
7
#include "HttpLog.h"
8
9
#include "nsHttpNTLMAuth.h"
10
#include "nsIAuthModule.h"
11
#include "nsCOMPtr.h"
12
#include "nsServiceManagerUtils.h"
13
#include "plbase64.h"
14
#include "plstr.h"
15
#include "prnetdb.h"
16
17
//-----------------------------------------------------------------------------
18
19
#include "nsIPrefBranch.h"
20
#include "nsIPrefService.h"
21
#include "nsIHttpAuthenticableChannel.h"
22
#include "nsIURI.h"
23
#ifdef XP_WIN
24
#include "nsIChannel.h"
25
#include "nsIX509Cert.h"
26
#include "nsITransportSecurityInfo.h"
27
#endif
28
#include "mozilla/Attributes.h"
29
#include "mozilla/Base64.h"
30
#include "mozilla/CheckedInt.h"
31
#include "mozilla/Tokenizer.h"
32
#include "mozilla/UniquePtr.h"
33
#include "mozilla/Unused.h"
34
#include "nsNetUtil.h"
35
#include "nsIChannel.h"
36
#include "nsUnicharUtils.h"
37
#include "mozilla/net/HttpAuthUtils.h"
38
39
namespace mozilla {
40
namespace net {
41
42
static const char kAllowProxies[] = "network.automatic-ntlm-auth.allow-proxies";
43
static const char kAllowNonFqdn[] = "network.automatic-ntlm-auth.allow-non-fqdn";
44
static const char kTrustedURIs[]  = "network.automatic-ntlm-auth.trusted-uris";
45
static const char kForceGeneric[] = "network.auth.force-generic-ntlm";
46
static const char kSSOinPBmode[] = "network.auth.private-browsing-sso";
47
48
static bool
49
IsNonFqdn(nsIURI *uri)
50
0
{
51
0
    nsAutoCString host;
52
0
    PRNetAddr addr;
53
0
54
0
    if (NS_FAILED(uri->GetAsciiHost(host)))
55
0
        return false;
56
0
57
0
    // return true if host does not contain a dot and is not an ip address
58
0
    return !host.IsEmpty() && !host.Contains('.') &&
59
0
           PR_StringToNetAddr(host.BeginReading(), &addr) != PR_SUCCESS;
60
0
}
61
62
// Check to see if we should use our generic (internal) NTLM auth module.
63
static bool
64
ForceGenericNTLM()
65
0
{
66
0
    nsCOMPtr<nsIPrefBranch> prefs = do_GetService(NS_PREFSERVICE_CONTRACTID);
67
0
    if (!prefs)
68
0
        return false;
69
0
    bool flag = false;
70
0
71
0
    if (NS_FAILED(prefs->GetBoolPref(kForceGeneric, &flag)))
72
0
        flag = false;
73
0
74
0
    LOG(("Force use of generic ntlm auth module: %d\n", flag));
75
0
    return flag;
76
0
}
77
78
// Check to see if we should use default credentials for this host or proxy.
79
static bool
80
CanUseDefaultCredentials(nsIHttpAuthenticableChannel *channel,
81
                         bool isProxyAuth)
82
0
{
83
0
    nsCOMPtr<nsIPrefBranch> prefs = do_GetService(NS_PREFSERVICE_CONTRACTID);
84
0
    if (!prefs) {
85
0
        return false;
86
0
    }
87
0
88
0
    // Proxy should go all the time, it's not considered a privacy leak
89
0
    // to send default credentials to a proxy.
90
0
    if (isProxyAuth) {
91
0
        bool val;
92
0
        if (NS_FAILED(prefs->GetBoolPref(kAllowProxies, &val)))
93
0
            val = false;
94
0
        LOG(("Default credentials allowed for proxy: %d\n", val));
95
0
        return val;
96
0
    }
97
0
98
0
    // Prevent using default credentials for authentication when we are in the
99
0
    // private browsing mode (but not in "never remember history" mode) and when
100
0
    // not explicitely allowed.  Otherwise, it would cause a privacy data leak.
101
0
    nsCOMPtr<nsIChannel> bareChannel = do_QueryInterface(channel);
102
0
    MOZ_ASSERT(bareChannel);
103
0
104
0
    if (NS_UsePrivateBrowsing(bareChannel)) {
105
0
        bool ssoInPb;
106
0
        if (NS_SUCCEEDED(prefs->GetBoolPref(kSSOinPBmode, &ssoInPb)) &&
107
0
            ssoInPb) {
108
0
            return true;
109
0
        }
110
0
111
0
        bool dontRememberHistory;
112
0
        if (NS_SUCCEEDED(prefs->GetBoolPref("browser.privatebrowsing.autostart",
113
0
                                            &dontRememberHistory)) &&
114
0
            !dontRememberHistory) {
115
0
            return false;
116
0
        }
117
0
    }
118
0
119
0
    nsCOMPtr<nsIURI> uri;
120
0
    Unused << channel->GetURI(getter_AddRefs(uri));
121
0
122
0
    bool allowNonFqdn;
123
0
    if (NS_FAILED(prefs->GetBoolPref(kAllowNonFqdn, &allowNonFqdn)))
124
0
        allowNonFqdn = false;
125
0
    if (allowNonFqdn && uri && IsNonFqdn(uri)) {
126
0
        LOG(("Host is non-fqdn, default credentials are allowed\n"));
127
0
        return true;
128
0
    }
129
0
130
0
    bool isTrustedHost = (uri && auth::URIMatchesPrefPattern(uri, kTrustedURIs));
131
0
    LOG(("Default credentials allowed for host: %d\n", isTrustedHost));
132
0
    return isTrustedHost;
133
0
}
134
135
// Dummy class for session state object.  This class doesn't hold any data.
136
// Instead we use its existence as a flag.  See ChallengeReceived.
137
class nsNTLMSessionState final : public nsISupports
138
{
139
    ~nsNTLMSessionState() = default;
140
public:
141
    NS_DECL_ISUPPORTS
142
};
143
NS_IMPL_ISUPPORTS0(nsNTLMSessionState)
144
145
//-----------------------------------------------------------------------------
146
147
NS_IMPL_ISUPPORTS(nsHttpNTLMAuth, nsIHttpAuthenticator)
148
149
NS_IMETHODIMP
150
nsHttpNTLMAuth::ChallengeReceived(nsIHttpAuthenticableChannel *channel,
151
                                  const char     *challenge,
152
                                  bool            isProxyAuth,
153
                                  nsISupports   **sessionState,
154
                                  nsISupports   **continuationState,
155
                                  bool           *identityInvalid)
156
0
{
157
0
    LOG(("nsHttpNTLMAuth::ChallengeReceived [ss=%p cs=%p]\n",
158
0
         *sessionState, *continuationState));
159
0
160
0
    // Use the native NTLM if available
161
0
    mUseNative = true;
162
0
163
0
    // NOTE: we don't define any session state, but we do use the pointer.
164
0
165
0
    *identityInvalid = false;
166
0
167
0
    // Start a new auth sequence if the challenge is exactly "NTLM".
168
0
    // If native NTLM auth apis are available and enabled through prefs,
169
0
    // try to use them.
170
0
    if (PL_strcasecmp(challenge, "NTLM") == 0) {
171
0
        nsCOMPtr<nsISupports> module;
172
0
173
0
        // Check to see if we should default to our generic NTLM auth module
174
0
        // through UseGenericNTLM. (We use native auth by default if the
175
0
        // system provides it.) If *sessionState is non-null, we failed to
176
0
        // instantiate a native NTLM module the last time, so skip trying again.
177
0
        bool forceGeneric = ForceGenericNTLM();
178
0
        if (!forceGeneric && !*sessionState) {
179
0
            // Check for approved default credentials hosts and proxies. If
180
0
            // *continuationState is non-null, the last authentication attempt
181
0
            // failed so skip default credential use.
182
0
            if (!*continuationState && CanUseDefaultCredentials(channel, isProxyAuth)) {
183
0
                // Try logging in with the user's default credentials. If
184
0
                // successful, |identityInvalid| is false, which will trigger
185
0
                // a default credentials attempt once we return.
186
0
                module = do_CreateInstance(NS_AUTH_MODULE_CONTRACTID_PREFIX "sys-ntlm");
187
0
            }
188
#ifdef XP_WIN
189
            else {
190
                // Try to use native NTLM and prompt the user for their domain,
191
                // username, and password. (only supported by windows nsAuthSSPI module.)
192
                // Note, for servers that use LMv1 a weak hash of the user's password
193
                // will be sent. We rely on windows internal apis to decide whether
194
                // we should support this older, less secure version of the protocol.
195
                module = do_CreateInstance(NS_AUTH_MODULE_CONTRACTID_PREFIX "sys-ntlm");
196
                *identityInvalid = true;
197
            }
198
#endif // XP_WIN
199
0
            if (!module)
200
0
                LOG(("Native sys-ntlm auth module not found.\n"));
201
0
        }
202
0
203
#ifdef XP_WIN
204
        // On windows, never fall back unless the user has specifically requested so.
205
        if (!forceGeneric && !module)
206
            return NS_ERROR_UNEXPECTED;
207
#endif
208
209
0
        // If no native support was available. Fall back on our internal NTLM implementation.
210
0
        if (!module) {
211
0
            if (!*sessionState) {
212
0
                // Remember the fact that we cannot use the "sys-ntlm" module,
213
0
                // so we don't ever bother trying again for this auth domain.
214
0
                *sessionState = new nsNTLMSessionState();
215
0
                if (!*sessionState)
216
0
                    return NS_ERROR_OUT_OF_MEMORY;
217
0
                NS_ADDREF(*sessionState);
218
0
            }
219
0
220
0
            // Use our internal NTLM implementation. Note, this is less secure,
221
0
            // see bug 520607 for details.
222
0
            LOG(("Trying to fall back on internal ntlm auth.\n"));
223
0
            module = do_CreateInstance(NS_AUTH_MODULE_CONTRACTID_PREFIX "ntlm");
224
0
225
0
            mUseNative = false;
226
0
227
0
            // Prompt user for domain, username, and password.
228
0
            *identityInvalid = true;
229
0
        }
230
0
231
0
        // If this fails, then it means that we cannot do NTLM auth.
232
0
        if (!module) {
233
0
            LOG(("No ntlm auth modules available.\n"));
234
0
            return NS_ERROR_UNEXPECTED;
235
0
        }
236
0
237
0
        // A non-null continuation state implies that we failed to authenticate.
238
0
        // Blow away the old authentication state, and use the new one.
239
0
        module.swap(*continuationState);
240
0
    }
241
0
    return NS_OK;
242
0
}
243
244
NS_IMETHODIMP
245
nsHttpNTLMAuth::GenerateCredentialsAsync(nsIHttpAuthenticableChannel *authChannel,
246
                                         nsIHttpAuthenticatorCallback* aCallback,
247
                                         const char *challenge,
248
                                         bool isProxyAuth,
249
                                         const char16_t *domain,
250
                                         const char16_t *username,
251
                                         const char16_t *password,
252
                                         nsISupports *sessionState,
253
                                         nsISupports *continuationState,
254
                                         nsICancelable **aCancellable)
255
0
{
256
0
  return NS_ERROR_NOT_IMPLEMENTED;
257
0
}
258
259
NS_IMETHODIMP
260
nsHttpNTLMAuth::GenerateCredentials(nsIHttpAuthenticableChannel *authChannel,
261
                                    const char      *challenge,
262
                                    bool             isProxyAuth,
263
                                    const char16_t *domain,
264
                                    const char16_t *user,
265
                                    const char16_t *pass,
266
                                    nsISupports    **sessionState,
267
                                    nsISupports    **continuationState,
268
                                    uint32_t       *aFlags,
269
                                    char           **creds)
270
271
0
{
272
0
    LOG(("nsHttpNTLMAuth::GenerateCredentials\n"));
273
0
274
0
    *creds = nullptr;
275
0
    *aFlags = 0;
276
0
277
0
    // if user or password is empty, ChallengeReceived returned
278
0
    // identityInvalid = false, that means we are using default user
279
0
    // credentials; see  nsAuthSSPI::Init method for explanation of this
280
0
    // condition
281
0
    if (!user || !pass)
282
0
        *aFlags = USING_INTERNAL_IDENTITY;
283
0
284
0
    nsresult rv;
285
0
    nsCOMPtr<nsIAuthModule> module = do_QueryInterface(*continuationState, &rv);
286
0
    NS_ENSURE_SUCCESS(rv, rv);
287
0
288
0
    void *inBuf, *outBuf;
289
0
    uint32_t inBufLen, outBufLen;
290
0
291
0
    // initial challenge
292
0
    if (PL_strcasecmp(challenge, "NTLM") == 0) {
293
0
        // NTLM service name format is 'HTTP@host' for both http and https
294
0
        nsCOMPtr<nsIURI> uri;
295
0
        rv = authChannel->GetURI(getter_AddRefs(uri));
296
0
        if (NS_FAILED(rv))
297
0
            return rv;
298
0
        nsAutoCString serviceName, host;
299
0
        rv = uri->GetAsciiHost(host);
300
0
        if (NS_FAILED(rv))
301
0
            return rv;
302
0
        serviceName.AppendLiteral("HTTP@");
303
0
        serviceName.Append(host);
304
0
        // initialize auth module
305
0
        uint32_t reqFlags = nsIAuthModule::REQ_DEFAULT;
306
0
        if (isProxyAuth)
307
0
            reqFlags |= nsIAuthModule::REQ_PROXY_AUTH;
308
0
309
0
        rv = module->Init(serviceName.get(), reqFlags, domain, user, pass);
310
0
        if (NS_FAILED(rv))
311
0
            return rv;
312
0
313
0
// This update enables updated Windows machines (Win7 or patched previous
314
0
// versions) and Linux machines running Samba (updated for Channel
315
0
// Binding), to perform Channel Binding when authenticating using NTLMv2
316
0
// and an outer secure channel.
317
0
//
318
0
// Currently only implemented for Windows, linux support will be landing in
319
0
// a separate patch, update this #ifdef accordingly then.
320
#if defined (XP_WIN) /* || defined (LINUX) */
321
        // We should retrieve the server certificate and compute the CBT,
322
        // but only when we are using the native NTLM implementation and
323
        // not the internal one.
324
        // It is a valid case not having the security info object.  This
325
        // occures when we connect an https site through an ntlm proxy.
326
        // After the ssl tunnel has been created, we get here the second
327
        // time and now generate the CBT from now valid security info.
328
        nsCOMPtr<nsIChannel> channel = do_QueryInterface(authChannel, &rv);
329
        if (NS_FAILED(rv))
330
            return rv;
331
332
        nsCOMPtr<nsISupports> security;
333
        rv = channel->GetSecurityInfo(getter_AddRefs(security));
334
        if (NS_FAILED(rv))
335
            return rv;
336
337
        nsCOMPtr<nsITransportSecurityInfo> secInfo =
338
            do_QueryInterface(security);
339
340
        if (mUseNative && secInfo) {
341
            nsCOMPtr<nsIX509Cert> cert;
342
            rv = secInfo->GetServerCert(getter_AddRefs(cert));
343
            if (NS_FAILED(rv))
344
                return rv;
345
346
            uint32_t length;
347
            uint8_t* certArray;
348
            rv = cert->GetRawDER(&length, &certArray);
349
            if (NS_FAILED(rv))
350
                return rv;
351
352
            // If there is a server certificate, we pass it along the
353
            // first time we call GetNextToken().
354
            inBufLen = length;
355
            inBuf = certArray;
356
        } else {
357
            // If there is no server certificate, we don't pass anything.
358
            inBufLen = 0;
359
            inBuf = nullptr;
360
        }
361
#else // Extended protection update is just for Linux and Windows machines.
362
0
        inBufLen = 0;
363
0
        inBuf = nullptr;
364
0
#endif
365
0
    }
366
0
    else {
367
0
        // decode challenge; skip past "NTLM " to the start of the base64
368
0
        // encoded data.
369
0
        int len = strlen(challenge);
370
0
        if (len < 6)
371
0
            return NS_ERROR_UNEXPECTED; // bogus challenge
372
0
        challenge += 5;
373
0
        len -= 5;
374
0
375
0
        // strip off any padding (see bug 230351)
376
0
        while (challenge[len - 1] == '=')
377
0
          len--;
378
0
379
0
        // decode into the input secbuffer
380
0
        rv = Base64Decode(challenge, len, (char**)&inBuf, &inBufLen);
381
0
        if (NS_FAILED(rv)) {
382
0
            return rv;
383
0
        }
384
0
    }
385
0
386
0
    rv = module->GetNextToken(inBuf, inBufLen, &outBuf, &outBufLen);
387
0
    if (NS_SUCCEEDED(rv)) {
388
0
        // base64 encode data in output buffer and prepend "NTLM "
389
0
        CheckedUint32 credsLen = ((CheckedUint32(outBufLen) + 2) / 3) * 4;
390
0
        credsLen += 5; // "NTLM "
391
0
        credsLen += 1; // null terminate
392
0
393
0
        if (!credsLen.isValid()) {
394
0
          rv = NS_ERROR_FAILURE;
395
0
        } else {
396
0
          *creds = (char *) moz_xmalloc(credsLen.value());
397
0
          memcpy(*creds, "NTLM ", 5);
398
0
          PL_Base64Encode((char *) outBuf, outBufLen, *creds + 5);
399
0
          (*creds)[credsLen.value() - 1] = '\0'; // null terminate
400
0
        }
401
0
402
0
        // OK, we are done with |outBuf|
403
0
        free(outBuf);
404
0
    }
405
0
406
0
    if (inBuf)
407
0
        free(inBuf);
408
0
409
0
    return rv;
410
0
}
411
412
NS_IMETHODIMP
413
nsHttpNTLMAuth::GetAuthFlags(uint32_t *flags)
414
0
{
415
0
    *flags = CONNECTION_BASED | IDENTITY_INCLUDES_DOMAIN | IDENTITY_ENCRYPTED;
416
0
    return NS_OK;
417
0
}
418
419
} // namespace net
420
} // namespace mozilla