/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 |