1 /* vim:set ts=2 sw=2 et cindent: */
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 #include "nsNTLMAuthModule.h"
7
8 #include <time.h>
9
10 #include "ScopedNSSTypes.h"
11 #include "md4.h"
12 #include "mozilla/Base64.h"
13 #include "mozilla/Casting.h"
14 #include "mozilla/CheckedInt.h"
15 #include "mozilla/EndianUtils.h"
16 #include "mozilla/Likely.h"
17 #include "mozilla/Logging.h"
18 #include "mozilla/Preferences.h"
19 #include "mozilla/Sprintf.h"
20 #include "mozilla/Telemetry.h"
21 #include "nsCOMPtr.h"
22 #include "nsComponentManagerUtils.h"
23 #include "nsICryptoHMAC.h"
24 #include "nsICryptoHash.h"
25 #include "nsIKeyModule.h"
26 #include "nsKeyModule.h"
27 #include "nsNSSShutDown.h"
28 #include "nsNativeCharsetUtils.h"
29 #include "nsNetCID.h"
30 #include "nsUnicharUtils.h"
31 #include "pk11pub.h"
32 #include "prsystem.h"
33
34 static bool sNTLMv1Forced = false;
35 static mozilla::LazyLogModule sNTLMLog("NTLM");
36
37 #define LOG(x) MOZ_LOG(sNTLMLog, mozilla::LogLevel::Debug, x)
38 #define LOG_ENABLED() MOZ_LOG_TEST(sNTLMLog, mozilla::LogLevel::Debug)
39
40 static void des_makekey(const uint8_t *raw, uint8_t *key);
41 static void des_encrypt(const uint8_t *key, const uint8_t *src, uint8_t *hash);
42
43 //-----------------------------------------------------------------------------
44 // this file contains a cross-platform NTLM authentication implementation. it
45 // is based on documentation from: http://davenport.sourceforge.net/ntlm.html
46 //-----------------------------------------------------------------------------
47
48 #define NTLM_NegotiateUnicode 0x00000001
49 #define NTLM_NegotiateOEM 0x00000002
50 #define NTLM_RequestTarget 0x00000004
51 #define NTLM_Unknown1 0x00000008
52 #define NTLM_NegotiateSign 0x00000010
53 #define NTLM_NegotiateSeal 0x00000020
54 #define NTLM_NegotiateDatagramStyle 0x00000040
55 #define NTLM_NegotiateLanManagerKey 0x00000080
56 #define NTLM_NegotiateNetware 0x00000100
57 #define NTLM_NegotiateNTLMKey 0x00000200
58 #define NTLM_Unknown2 0x00000400
59 #define NTLM_Unknown3 0x00000800
60 #define NTLM_NegotiateDomainSupplied 0x00001000
61 #define NTLM_NegotiateWorkstationSupplied 0x00002000
62 #define NTLM_NegotiateLocalCall 0x00004000
63 #define NTLM_NegotiateAlwaysSign 0x00008000
64 #define NTLM_TargetTypeDomain 0x00010000
65 #define NTLM_TargetTypeServer 0x00020000
66 #define NTLM_TargetTypeShare 0x00040000
67 #define NTLM_NegotiateNTLM2Key 0x00080000
68 #define NTLM_RequestInitResponse 0x00100000
69 #define NTLM_RequestAcceptResponse 0x00200000
70 #define NTLM_RequestNonNTSessionKey 0x00400000
71 #define NTLM_NegotiateTargetInfo 0x00800000
72 #define NTLM_Unknown4 0x01000000
73 #define NTLM_Unknown5 0x02000000
74 #define NTLM_Unknown6 0x04000000
75 #define NTLM_Unknown7 0x08000000
76 #define NTLM_Unknown8 0x10000000
77 #define NTLM_Negotiate128 0x20000000
78 #define NTLM_NegotiateKeyExchange 0x40000000
79 #define NTLM_Negotiate56 0x80000000
80
81 // we send these flags with our type 1 message
82 #define NTLM_TYPE1_FLAGS \
83 (NTLM_NegotiateUnicode | \
84 NTLM_NegotiateOEM | \
85 NTLM_RequestTarget | \
86 NTLM_NegotiateNTLMKey | \
87 NTLM_NegotiateAlwaysSign | \
88 NTLM_NegotiateNTLM2Key)
89
90 static const char NTLM_SIGNATURE[] = "NTLMSSP";
91 static const char NTLM_TYPE1_MARKER[] = { 0x01, 0x00, 0x00, 0x00 };
92 static const char NTLM_TYPE2_MARKER[] = { 0x02, 0x00, 0x00, 0x00 };
93 static const char NTLM_TYPE3_MARKER[] = { 0x03, 0x00, 0x00, 0x00 };
94
95 #define NTLM_TYPE1_HEADER_LEN 32
96 #define NTLM_TYPE2_HEADER_LEN 48
97 #define NTLM_TYPE3_HEADER_LEN 64
98
99 /**
100 * We don't actually send a LM response, but we still have to send something in this spot
101 */
102 #define LM_RESP_LEN 24
103
104 #define NTLM_CHAL_LEN 8
105
106 #define NTLM_HASH_LEN 16
107 #define NTLMv2_HASH_LEN 16
108 #define NTLM_RESP_LEN 24
109 #define NTLMv2_RESP_LEN 16
110 #define NTLMv2_BLOB1_LEN 28
111
112 //-----------------------------------------------------------------------------
113
114 /**
115 * Prints a description of flags to the NSPR Log, if enabled.
116 */
LogFlags(uint32_t flags)117 static void LogFlags(uint32_t flags)
118 {
119 if (!LOG_ENABLED())
120 return;
121 #define TEST(_flag) \
122 if (flags & NTLM_ ## _flag) \
123 PR_LogPrint(" 0x%08x (" # _flag ")\n", NTLM_ ## _flag)
124
125 TEST(NegotiateUnicode);
126 TEST(NegotiateOEM);
127 TEST(RequestTarget);
128 TEST(Unknown1);
129 TEST(NegotiateSign);
130 TEST(NegotiateSeal);
131 TEST(NegotiateDatagramStyle);
132 TEST(NegotiateLanManagerKey);
133 TEST(NegotiateNetware);
134 TEST(NegotiateNTLMKey);
135 TEST(Unknown2);
136 TEST(Unknown3);
137 TEST(NegotiateDomainSupplied);
138 TEST(NegotiateWorkstationSupplied);
139 TEST(NegotiateLocalCall);
140 TEST(NegotiateAlwaysSign);
141 TEST(TargetTypeDomain);
142 TEST(TargetTypeServer);
143 TEST(TargetTypeShare);
144 TEST(NegotiateNTLM2Key);
145 TEST(RequestInitResponse);
146 TEST(RequestAcceptResponse);
147 TEST(RequestNonNTSessionKey);
148 TEST(NegotiateTargetInfo);
149 TEST(Unknown4);
150 TEST(Unknown5);
151 TEST(Unknown6);
152 TEST(Unknown7);
153 TEST(Unknown8);
154 TEST(Negotiate128);
155 TEST(NegotiateKeyExchange);
156 TEST(Negotiate56);
157
158 #undef TEST
159 }
160
161 /**
162 * Prints a hexdump of buf to the NSPR Log, if enabled.
163 * @param tag Description of the data, will be printed in front of the data
164 * @param buf the data to print
165 * @param bufLen length of the data
166 */
167 static void
LogBuf(const char * tag,const uint8_t * buf,uint32_t bufLen)168 LogBuf(const char *tag, const uint8_t *buf, uint32_t bufLen)
169 {
170 int i;
171
172 if (!LOG_ENABLED())
173 return;
174
175 PR_LogPrint("%s =\n", tag);
176 char line[80];
177 while (bufLen > 0)
178 {
179 int count = bufLen;
180 if (count > 8)
181 count = 8;
182
183 strcpy(line, " ");
184 for (i=0; i<count; ++i)
185 {
186 int len = strlen(line);
187 snprintf(line + len, sizeof(line) - len, "0x%02x ", int(buf[i]));
188 }
189 for (; i<8; ++i)
190 {
191 int len = strlen(line);
192 snprintf(line + len, sizeof(line) - len, " ");
193 }
194
195 int len = strlen(line);
196 snprintf(line + len, sizeof(line) - len, " ");
197 for (i=0; i<count; ++i)
198 {
199 len = strlen(line);
200 if (isprint(buf[i]))
201 snprintf(line + len, sizeof(line) - len, "%c", buf[i]);
202 else
203 snprintf(line + len, sizeof(line) - len, ".");
204 }
205 PR_LogPrint("%s\n", line);
206
207 bufLen -= count;
208 buf += count;
209 }
210 }
211
212 /**
213 * Print base64-encoded token to the NSPR Log.
214 * @param name Description of the token, will be printed in front
215 * @param token The token to print
216 * @param tokenLen length of the data in token
217 */
218 static void
LogToken(const char * name,const void * token,uint32_t tokenLen)219 LogToken(const char* name, const void* token, uint32_t tokenLen)
220 {
221 if (!LOG_ENABLED()) {
222 return;
223 }
224
225 nsDependentCSubstring tokenString(static_cast<const char*>(token), tokenLen);
226 nsAutoCString base64Token;
227 nsresult rv = mozilla::Base64Encode(tokenString, base64Token);
228 if (NS_FAILED(rv)) {
229 return;
230 }
231
232 PR_LogPrint("%s: %s\n", name, base64Token.get());
233 }
234
235 //-----------------------------------------------------------------------------
236
237 // byte order swapping
238 #define SWAP16(x) ((((x) & 0xff) << 8) | (((x) >> 8) & 0xff))
239 #define SWAP32(x) ((SWAP16((x) & 0xffff) << 16) | (SWAP16((x) >> 16)))
240
241 static void *
WriteBytes(void * buf,const void * data,uint32_t dataLen)242 WriteBytes(void *buf, const void *data, uint32_t dataLen)
243 {
244 memcpy(buf, data, dataLen);
245 return (uint8_t *) buf + dataLen;
246 }
247
248 static void *
WriteDWORD(void * buf,uint32_t dword)249 WriteDWORD(void *buf, uint32_t dword)
250 {
251 #ifdef IS_BIG_ENDIAN
252 // NTLM uses little endian on the wire
253 dword = SWAP32(dword);
254 #endif
255 return WriteBytes(buf, &dword, sizeof(dword));
256 }
257
258 static void *
WriteSecBuf(void * buf,uint16_t length,uint32_t offset)259 WriteSecBuf(void *buf, uint16_t length, uint32_t offset)
260 {
261 #ifdef IS_BIG_ENDIAN
262 length = SWAP16(length);
263 offset = SWAP32(offset);
264 #endif
265 buf = WriteBytes(buf, &length, sizeof(length));
266 buf = WriteBytes(buf, &length, sizeof(length));
267 buf = WriteBytes(buf, &offset, sizeof(offset));
268 return buf;
269 }
270
271 #ifdef IS_BIG_ENDIAN
272 /**
273 * WriteUnicodeLE copies a unicode string from one buffer to another. The
274 * resulting unicode string is in little-endian format. The input string is
275 * assumed to be in the native endianness of the local machine. It is safe
276 * to pass the same buffer as both input and output, which is a handy way to
277 * convert the unicode buffer to little-endian on big-endian platforms.
278 */
279 static void *
WriteUnicodeLE(void * buf,const char16_t * str,uint32_t strLen)280 WriteUnicodeLE(void *buf, const char16_t *str, uint32_t strLen)
281 {
282 // convert input string from BE to LE
283 uint8_t *cursor = (uint8_t *) buf,
284 *input = (uint8_t *) str;
285 for (uint32_t i=0; i<strLen; ++i, input+=2, cursor+=2)
286 {
287 // allow for the case where |buf == str|
288 uint8_t temp = input[0];
289 cursor[0] = input[1];
290 cursor[1] = temp;
291 }
292 return buf;
293 }
294 #endif
295
296 static uint16_t
ReadUint16(const uint8_t * & buf)297 ReadUint16(const uint8_t *&buf)
298 {
299 uint16_t x = ((uint16_t) buf[0]) | ((uint16_t) buf[1] << 8);
300 buf += sizeof(x);
301 return x;
302 }
303
304 static uint32_t
ReadUint32(const uint8_t * & buf)305 ReadUint32(const uint8_t *&buf)
306 {
307 uint32_t x = ( (uint32_t) buf[0]) |
308 (((uint32_t) buf[1]) << 8) |
309 (((uint32_t) buf[2]) << 16) |
310 (((uint32_t) buf[3]) << 24);
311 buf += sizeof(x);
312 return x;
313 }
314
315 //-----------------------------------------------------------------------------
316
317 static void
ZapBuf(void * buf,size_t bufLen)318 ZapBuf(void *buf, size_t bufLen)
319 {
320 memset(buf, 0, bufLen);
321 }
322
323 static void
ZapString(nsString & s)324 ZapString(nsString &s)
325 {
326 ZapBuf(s.BeginWriting(), s.Length() * 2);
327 }
328
329 /**
330 * NTLM_Hash computes the NTLM hash of the given password.
331 *
332 * @param password
333 * null-terminated unicode password.
334 * @param hash
335 * 16-byte result buffer
336 */
337 static void
NTLM_Hash(const nsString & password,unsigned char * hash)338 NTLM_Hash(const nsString &password, unsigned char *hash)
339 {
340 uint32_t len = password.Length();
341 uint8_t *passbuf;
342
343 #ifdef IS_BIG_ENDIAN
344 passbuf = (uint8_t *) malloc(len * 2);
345 WriteUnicodeLE(passbuf, password.get(), len);
346 #else
347 passbuf = (uint8_t *) password.get();
348 #endif
349
350 md4sum(passbuf, len * 2, hash);
351
352 #ifdef IS_BIG_ENDIAN
353 ZapBuf(passbuf, len * 2);
354 free(passbuf);
355 #endif
356 }
357
358 //-----------------------------------------------------------------------------
359
360 /**
361 * LM_Response generates the LM response given a 16-byte password hash and the
362 * challenge from the Type-2 message.
363 *
364 * @param hash
365 * 16-byte password hash
366 * @param challenge
367 * 8-byte challenge from Type-2 message
368 * @param response
369 * 24-byte buffer to contain the LM response upon return
370 */
371 static void
LM_Response(const uint8_t * hash,const uint8_t * challenge,uint8_t * response)372 LM_Response(const uint8_t *hash, const uint8_t *challenge, uint8_t *response)
373 {
374 uint8_t keybytes[21], k1[8], k2[8], k3[8];
375
376 memcpy(keybytes, hash, 16);
377 ZapBuf(keybytes + 16, 5);
378
379 des_makekey(keybytes , k1);
380 des_makekey(keybytes + 7, k2);
381 des_makekey(keybytes + 14, k3);
382
383 des_encrypt(k1, challenge, response);
384 des_encrypt(k2, challenge, response + 8);
385 des_encrypt(k3, challenge, response + 16);
386 }
387
388 //-----------------------------------------------------------------------------
389
390 static nsresult
GenerateType1Msg(void ** outBuf,uint32_t * outLen)391 GenerateType1Msg(void **outBuf, uint32_t *outLen)
392 {
393 //
394 // verify that bufLen is sufficient
395 //
396 *outLen = NTLM_TYPE1_HEADER_LEN;
397 *outBuf = moz_xmalloc(*outLen);
398 if (!*outBuf)
399 return NS_ERROR_OUT_OF_MEMORY;
400
401 //
402 // write out type 1 msg
403 //
404 void *cursor = *outBuf;
405
406 // 0 : signature
407 cursor = WriteBytes(cursor, NTLM_SIGNATURE, sizeof(NTLM_SIGNATURE));
408
409 // 8 : marker
410 cursor = WriteBytes(cursor, NTLM_TYPE1_MARKER, sizeof(NTLM_TYPE1_MARKER));
411
412 // 12 : flags
413 cursor = WriteDWORD(cursor, NTLM_TYPE1_FLAGS);
414
415 //
416 // NOTE: it is common for the domain and workstation fields to be empty.
417 // this is true of Win2k clients, and my guess is that there is
418 // little utility to sending these strings before the charset has
419 // been negotiated. we follow suite -- anyways, it doesn't hurt
420 // to save some bytes on the wire ;-)
421 //
422
423 // 16 : supplied domain security buffer (empty)
424 cursor = WriteSecBuf(cursor, 0, 0);
425
426 // 24 : supplied workstation security buffer (empty)
427 cursor = WriteSecBuf(cursor, 0, 0);
428
429 return NS_OK;
430 }
431
432 struct Type2Msg
433 {
434 uint32_t flags; // NTLM_Xxx bitwise combination
435 uint8_t challenge[NTLM_CHAL_LEN]; // 8 byte challenge
436 const uint8_t *target; // target string (type depends on flags)
437 uint32_t targetLen; // target length in bytes
438 const uint8_t *targetInfo; // target Attribute-Value pairs (DNS domain, et al)
439 uint32_t targetInfoLen; // target AV pairs length in bytes
440 };
441
442 static nsresult
ParseType2Msg(const void * inBuf,uint32_t inLen,Type2Msg * msg)443 ParseType2Msg(const void *inBuf, uint32_t inLen, Type2Msg *msg)
444 {
445 // make sure inBuf is long enough to contain a meaningful type2 msg.
446 //
447 // 0 NTLMSSP Signature
448 // 8 NTLM Message Type
449 // 12 Target Name
450 // 20 Flags
451 // 24 Challenge
452 // 32 targetInfo
453 // 48 start of optional data blocks
454 //
455 if (inLen < NTLM_TYPE2_HEADER_LEN)
456 return NS_ERROR_UNEXPECTED;
457
458 auto cursor = static_cast<const uint8_t*>(inBuf);
459
460 // verify NTLMSSP signature
461 if (memcmp(cursor, NTLM_SIGNATURE, sizeof(NTLM_SIGNATURE)) != 0)
462 return NS_ERROR_UNEXPECTED;
463
464 cursor += sizeof(NTLM_SIGNATURE);
465
466 // verify Type-2 marker
467 if (memcmp(cursor, NTLM_TYPE2_MARKER, sizeof(NTLM_TYPE2_MARKER)) != 0)
468 return NS_ERROR_UNEXPECTED;
469
470 cursor += sizeof(NTLM_TYPE2_MARKER);
471
472 // Read target name security buffer: ...
473 // ... read target length.
474 uint32_t targetLen = ReadUint16(cursor);
475 // ... skip next 16-bit "allocated space" value.
476 ReadUint16(cursor);
477 // ... read offset from inBuf.
478 uint32_t offset = ReadUint32(cursor);
479 mozilla::CheckedInt<uint32_t> targetEnd = offset;
480 targetEnd += targetLen;
481 // Check the offset / length combo is in range of the input buffer, including
482 // integer overflow checking.
483 if (MOZ_LIKELY(targetEnd.isValid() && targetEnd.value() <= inLen)) {
484 msg->targetLen = targetLen;
485 msg->target = static_cast<const uint8_t*>(inBuf) + offset;
486 } else {
487 // Do not error out, for (conservative) backward compatibility.
488 msg->targetLen = 0;
489 msg->target = nullptr;
490 }
491
492 // read flags
493 msg->flags = ReadUint32(cursor);
494
495 // read challenge
496 memcpy(msg->challenge, cursor, sizeof(msg->challenge));
497 cursor += sizeof(msg->challenge);
498
499 LOG(("NTLM type 2 message:\n"));
500 LogBuf("target", msg->target, msg->targetLen);
501 LogBuf("flags",
502 mozilla::BitwiseCast<const uint8_t*, const uint32_t*>(&msg->flags), 4);
503 LogFlags(msg->flags);
504 LogBuf("challenge", msg->challenge, sizeof(msg->challenge));
505
506 // Read (and skip) the reserved field
507 ReadUint32(cursor);
508 ReadUint32(cursor);
509 // Read target name security buffer: ...
510 // ... read target length.
511 uint32_t targetInfoLen = ReadUint16(cursor);
512 // ... skip next 16-bit "allocated space" value.
513 ReadUint16(cursor);
514 // ... read offset from inBuf.
515 offset = ReadUint32(cursor);
516 mozilla::CheckedInt<uint32_t> targetInfoEnd = offset;
517 targetInfoEnd += targetInfoLen;
518 // Check the offset / length combo is in range of the input buffer, including
519 // integer overflow checking.
520 if (MOZ_LIKELY(targetInfoEnd.isValid() && targetInfoEnd.value() <= inLen)) {
521 msg->targetInfoLen = targetInfoLen;
522 msg->targetInfo = static_cast<const uint8_t*>(inBuf) + offset;
523 } else {
524 NS_ERROR("failed to get NTLMv2 target info");
525 return NS_ERROR_UNEXPECTED;
526 }
527
528 return NS_OK;
529 }
530
531 static nsresult
GenerateType3Msg(const nsString & domain,const nsString & username,const nsString & password,const void * inBuf,uint32_t inLen,void ** outBuf,uint32_t * outLen)532 GenerateType3Msg(const nsString &domain,
533 const nsString &username,
534 const nsString &password,
535 const void *inBuf,
536 uint32_t inLen,
537 void **outBuf,
538 uint32_t *outLen)
539 {
540 // inBuf contains Type-2 msg (the challenge) from server
541 MOZ_ASSERT(NS_IsMainThread());
542 nsresult rv;
543 Type2Msg msg;
544
545 rv = ParseType2Msg(inBuf, inLen, &msg);
546 if (NS_FAILED(rv))
547 return rv;
548
549 bool unicode = (msg.flags & NTLM_NegotiateUnicode);
550
551 // There is no negotiation for NTLMv2, so we just do it unless we are forced
552 // by explict user configuration to use the older DES-based cryptography.
553 bool ntlmv2 = (sNTLMv1Forced == false);
554
555 // temporary buffers for unicode strings
556 #ifdef IS_BIG_ENDIAN
557 nsAutoString ucsDomainBuf, ucsUserBuf;
558 #endif
559 nsAutoCString hostBuf;
560 nsAutoString ucsHostBuf;
561 // temporary buffers for oem strings
562 nsAutoCString oemDomainBuf, oemUserBuf, oemHostBuf;
563 // pointers and lengths for the string buffers; encoding is unicode if
564 // the "negotiate unicode" flag was set in the Type-2 message.
565 const void *domainPtr, *userPtr, *hostPtr;
566 uint32_t domainLen, userLen, hostLen;
567
568 // This is for NTLM, for NTLMv2 we set the new full length once we know it
569 mozilla::CheckedInt<uint16_t> ntlmRespLen = NTLM_RESP_LEN;
570
571 //
572 // get domain name
573 //
574 if (unicode)
575 {
576 #ifdef IS_BIG_ENDIAN
577 ucsDomainBuf = domain;
578 domainPtr = ucsDomainBuf.get();
579 domainLen = ucsDomainBuf.Length() * 2;
580 WriteUnicodeLE(const_cast<void*>(domainPtr),
581 static_cast<const char16_t*>(domainPtr),
582 ucsDomainBuf.Length());
583 #else
584 domainPtr = domain.get();
585 domainLen = domain.Length() * 2;
586 #endif
587 }
588 else
589 {
590 NS_CopyUnicodeToNative(domain, oemDomainBuf);
591 domainPtr = oemDomainBuf.get();
592 domainLen = oemDomainBuf.Length();
593 }
594
595 //
596 // get user name
597 //
598 if (unicode)
599 {
600 #ifdef IS_BIG_ENDIAN
601 ucsUserBuf = username;
602 userPtr = ucsUserBuf.get();
603 userLen = ucsUserBuf.Length() * 2;
604 WriteUnicodeLE(const_cast<void*>(userPtr),
605 static_cast<const char16_t*>(userPtr),
606 ucsUserBuf.Length());
607 #else
608 userPtr = username.get();
609 userLen = username.Length() * 2;
610 #endif
611 }
612 else
613 {
614 NS_CopyUnicodeToNative(username, oemUserBuf);
615 userPtr = oemUserBuf.get();
616 userLen = oemUserBuf.Length();
617 }
618
619 //
620 // get workstation name
621 // (do not use local machine's hostname after bug 1046421)
622 //
623 rv = mozilla::Preferences::GetCString("network.generic-ntlm-auth.workstation",
624 &hostBuf);
625 if (NS_FAILED(rv)) {
626 return rv;
627 }
628
629 if (unicode)
630 {
631 ucsHostBuf = NS_ConvertUTF8toUTF16(hostBuf);
632 hostPtr = ucsHostBuf.get();
633 hostLen = ucsHostBuf.Length() * 2;
634 #ifdef IS_BIG_ENDIAN
635 WriteUnicodeLE(const_cast<void*>(hostPtr),
636 static_cast<const char16_t*>(hostPtr),
637 ucsHostBuf.Length());
638 #endif
639 }
640 else
641 {
642 hostPtr = hostBuf.get();
643 hostLen = hostBuf.Length();
644 }
645
646 //
647 // now that we have generated all of the strings, we can allocate outBuf.
648 //
649 //
650 // next, we compute the NTLM or NTLM2 responses.
651 //
652 uint8_t lmResp[LM_RESP_LEN];
653 uint8_t ntlmResp[NTLM_RESP_LEN];
654 uint8_t ntlmv2Resp[NTLMv2_RESP_LEN];
655 uint8_t ntlmHash[NTLM_HASH_LEN];
656 uint8_t ntlmv2_blob1[NTLMv2_BLOB1_LEN];
657 if (ntlmv2) {
658 // NTLMv2 mode, the default
659 nsString userUpper, domainUpper;
660 nsAutoCString ntlmHashStr;
661 nsAutoCString ntlmv2HashStr;
662 nsAutoCString lmv2ResponseStr;
663 nsAutoCString ntlmv2ResponseStr;
664
665 // temporary buffers for unicode strings
666 nsAutoString ucsDomainUpperBuf;
667 nsAutoString ucsUserUpperBuf;
668 const void *domainUpperPtr;
669 const void *userUpperPtr;
670 uint32_t domainUpperLen;
671 uint32_t userUpperLen;
672
673 if (msg.targetInfoLen == 0) {
674 NS_ERROR("failed to get NTLMv2 target info, can not do NTLMv2");
675 return NS_ERROR_UNEXPECTED;
676 }
677
678 ToUpperCase(username, ucsUserUpperBuf);
679 userUpperPtr = ucsUserUpperBuf.get();
680 userUpperLen = ucsUserUpperBuf.Length() * 2;
681 #ifdef IS_BIG_ENDIAN
682 WriteUnicodeLE(const_cast<void*>(userUpperPtr),
683 static_cast<const char16_t*>(userUpperPtr),
684 ucsUserUpperBuf.Length());
685 #endif
686 ToUpperCase(domain, ucsDomainUpperBuf);
687 domainUpperPtr = ucsDomainUpperBuf.get();
688 domainUpperLen = ucsDomainUpperBuf.Length() * 2;
689 #ifdef IS_BIG_ENDIAN
690 WriteUnicodeLE(const_cast<void*>(domainUpperPtr),
691 static_cast<const char16_t*>(domainUpperPtr),
692 ucsDomainUpperBuf.Length());
693 #endif
694
695 NTLM_Hash(password, ntlmHash);
696 ntlmHashStr = nsAutoCString(
697 mozilla::BitwiseCast<const char*, const uint8_t*>(ntlmHash), NTLM_HASH_LEN);
698
699 nsCOMPtr<nsIKeyObjectFactory> keyFactory =
700 do_CreateInstance(NS_KEYMODULEOBJECTFACTORY_CONTRACTID, &rv);
701
702 if (NS_FAILED(rv)) {
703 return rv;
704 }
705
706 nsCOMPtr<nsIKeyObject> ntlmKey =
707 do_CreateInstance(NS_KEYMODULEOBJECT_CONTRACTID, &rv);
708 if (NS_FAILED(rv)) {
709 return rv;
710 }
711
712 rv = keyFactory->KeyFromString(nsIKeyObject::HMAC, ntlmHashStr, getter_AddRefs(ntlmKey));
713 if (NS_FAILED(rv)) {
714 return rv;
715 }
716
717 nsCOMPtr<nsICryptoHMAC> hasher =
718 do_CreateInstance(NS_CRYPTO_HMAC_CONTRACTID, &rv);
719 if (NS_FAILED(rv)) {
720 return rv;
721 }
722 rv = hasher->Init(nsICryptoHMAC::MD5, ntlmKey);
723 if (NS_FAILED(rv)) {
724 return rv;
725 }
726 rv = hasher->Update(static_cast<const uint8_t*>(userUpperPtr), userUpperLen);
727 if (NS_FAILED(rv)) {
728 return rv;
729 }
730 rv = hasher->Update(static_cast<const uint8_t*>(domainUpperPtr),
731 domainUpperLen);
732 if (NS_FAILED(rv)) {
733 return rv;
734 }
735 rv = hasher->Finish(false, ntlmv2HashStr);
736 if (NS_FAILED(rv)) {
737 return rv;
738 }
739
740 uint8_t client_random[NTLM_CHAL_LEN];
741 PK11_GenerateRandom(client_random, NTLM_CHAL_LEN);
742
743 nsCOMPtr<nsIKeyObject> ntlmv2Key =
744 do_CreateInstance(NS_KEYMODULEOBJECT_CONTRACTID, &rv);
745 if (NS_FAILED(rv)) {
746 return rv;
747 }
748
749 // Prepare the LMv2 response
750 rv = keyFactory->KeyFromString(nsIKeyObject::HMAC, ntlmv2HashStr, getter_AddRefs(ntlmv2Key));
751 if (NS_FAILED(rv)) {
752 return rv;
753 }
754
755 rv = hasher->Init(nsICryptoHMAC::MD5, ntlmv2Key);
756 if (NS_FAILED(rv)) {
757 return rv;
758 }
759 rv = hasher->Update(msg.challenge, NTLM_CHAL_LEN);
760 if (NS_FAILED(rv)) {
761 return rv;
762 }
763 rv = hasher->Update(client_random, NTLM_CHAL_LEN);
764 if (NS_FAILED(rv)) {
765 return rv;
766 }
767 rv = hasher->Finish(false, lmv2ResponseStr);
768 if (NS_FAILED(rv)) {
769 return rv;
770 }
771
772 if (lmv2ResponseStr.Length() != NTLMv2_HASH_LEN) {
773 return NS_ERROR_UNEXPECTED;
774 }
775
776 memcpy(lmResp, lmv2ResponseStr.get(), NTLMv2_HASH_LEN);
777 memcpy(lmResp + NTLMv2_HASH_LEN, client_random, NTLM_CHAL_LEN);
778
779 memset(ntlmv2_blob1, 0, NTLMv2_BLOB1_LEN);
780
781 time_t unix_time;
782 uint64_t nt_time = time(&unix_time);
783 nt_time += 11644473600LL; // Number of seconds betwen 1601 and 1970
784 nt_time *= 1000 * 1000 * 10; // Convert seconds to 100 ns units
785
786 ntlmv2_blob1[0] = 1;
787 ntlmv2_blob1[1] = 1;
788 mozilla::LittleEndian::writeUint64(&ntlmv2_blob1[8], nt_time);
789 PK11_GenerateRandom(&ntlmv2_blob1[16], NTLM_CHAL_LEN);
790
791 rv = hasher->Init(nsICryptoHMAC::MD5, ntlmv2Key);
792 if (NS_FAILED(rv)) {
793 return rv;
794 }
795 rv = hasher->Update(msg.challenge, NTLM_CHAL_LEN);
796 if (NS_FAILED(rv)) {
797 return rv;
798 }
799 rv = hasher->Update(ntlmv2_blob1, NTLMv2_BLOB1_LEN);
800 if (NS_FAILED(rv)) {
801 return rv;
802 }
803 rv = hasher->Update(msg.targetInfo, msg.targetInfoLen);
804 if (NS_FAILED(rv)) {
805 return rv;
806 }
807 rv = hasher->Finish(false, ntlmv2ResponseStr);
808 if (NS_FAILED(rv)) {
809 return rv;
810 }
811
812 if (ntlmv2ResponseStr.Length() != NTLMv2_RESP_LEN) {
813 return NS_ERROR_UNEXPECTED;
814 }
815
816 memcpy(ntlmv2Resp, ntlmv2ResponseStr.get(), NTLMv2_RESP_LEN);
817 ntlmRespLen = NTLMv2_RESP_LEN + NTLMv2_BLOB1_LEN;
818 ntlmRespLen += msg.targetInfoLen;
819 if (!ntlmRespLen.isValid()) {
820 NS_ERROR("failed to do NTLMv2: integer overflow?!?");
821 return NS_ERROR_UNEXPECTED;
822 }
823 } else if (msg.flags & NTLM_NegotiateNTLM2Key) {
824 // compute NTLM2 session response
825 nsCString sessionHashString;
826
827 PK11_GenerateRandom(lmResp, NTLM_CHAL_LEN);
828 memset(lmResp + NTLM_CHAL_LEN, 0, LM_RESP_LEN - NTLM_CHAL_LEN);
829
830 nsCOMPtr<nsICryptoHash> hasher =
831 do_CreateInstance(NS_CRYPTO_HASH_CONTRACTID, &rv);
832 if (NS_FAILED(rv)) {
833 return rv;
834 }
835 rv = hasher->Init(nsICryptoHash::MD5);
836 if (NS_FAILED(rv)) {
837 return rv;
838 }
839 rv = hasher->Update(msg.challenge, NTLM_CHAL_LEN);
840 if (NS_FAILED(rv)) {
841 return rv;
842 }
843 rv = hasher->Update(lmResp, NTLM_CHAL_LEN);
844 if (NS_FAILED(rv)) {
845 return rv;
846 }
847 rv = hasher->Finish(false, sessionHashString);
848 if (NS_FAILED(rv)) {
849 return rv;
850 }
851
852 auto sessionHash = mozilla::BitwiseCast<const uint8_t*, const char*>(
853 sessionHashString.get());
854
855 LogBuf("NTLM2 effective key: ", sessionHash, 8);
856
857 NTLM_Hash(password, ntlmHash);
858 LM_Response(ntlmHash, sessionHash, ntlmResp);
859 } else {
860 NTLM_Hash(password, ntlmHash);
861 LM_Response(ntlmHash, msg.challenge, ntlmResp);
862
863 // According to http://davenport.sourceforge.net/ntlm.html#ntlmVersion2,
864 // the correct way to not send the LM hash is to send the NTLM hash twice
865 // in both the LM and NTLM response fields.
866 LM_Response(ntlmHash, msg.challenge, lmResp);
867 }
868
869 mozilla::CheckedInt<uint32_t> totalLen = NTLM_TYPE3_HEADER_LEN + LM_RESP_LEN;
870 totalLen += hostLen;
871 totalLen += domainLen;
872 totalLen += userLen;
873 totalLen += ntlmRespLen.value();
874
875 if (!totalLen.isValid()) {
876 NS_ERROR("failed preparing to allocate NTLM response: integer overflow?!?");
877 return NS_ERROR_FAILURE;
878 }
879 *outBuf = moz_xmalloc(totalLen.value());
880 *outLen = totalLen.value();
881 if (!*outBuf) {
882 return NS_ERROR_OUT_OF_MEMORY;
883 }
884
885 //
886 // finally, we assemble the Type-3 msg :-)
887 //
888 void *cursor = *outBuf;
889 mozilla::CheckedInt<uint32_t> offset;
890
891 // 0 : signature
892 cursor = WriteBytes(cursor, NTLM_SIGNATURE, sizeof(NTLM_SIGNATURE));
893
894 // 8 : marker
895 cursor = WriteBytes(cursor, NTLM_TYPE3_MARKER, sizeof(NTLM_TYPE3_MARKER));
896
897 // 12 : LM response sec buf
898 offset = NTLM_TYPE3_HEADER_LEN;
899 offset += domainLen;
900 offset += userLen;
901 offset += hostLen;
902 if (!offset.isValid()) {
903 NS_ERROR("failed preparing to write NTLM response: integer overflow?!?");
904 return NS_ERROR_UNEXPECTED;
905 }
906 cursor = WriteSecBuf(cursor, LM_RESP_LEN, offset.value());
907 memcpy(static_cast<uint8_t*>(*outBuf) + offset.value(), lmResp, LM_RESP_LEN);
908
909 // 20 : NTLM or NTLMv2 response sec buf
910 offset += LM_RESP_LEN;
911 if (!offset.isValid()) {
912 NS_ERROR("failed preparing to write NTLM response: integer overflow?!?");
913 return NS_ERROR_UNEXPECTED;
914 }
915 cursor = WriteSecBuf(cursor, ntlmRespLen.value(), offset.value());
916 if (ntlmv2) {
917 memcpy(static_cast<uint8_t*>(*outBuf) + offset.value(), ntlmv2Resp,
918 NTLMv2_RESP_LEN);
919 offset += NTLMv2_RESP_LEN;
920 if (!offset.isValid()) {
921 NS_ERROR("failed preparing to write NTLM response: integer overflow?!?");
922 return NS_ERROR_UNEXPECTED;
923 }
924 memcpy(static_cast<uint8_t*>(*outBuf) + offset.value(), ntlmv2_blob1,
925 NTLMv2_BLOB1_LEN);
926 offset += NTLMv2_BLOB1_LEN;
927 if (!offset.isValid()) {
928 NS_ERROR("failed preparing to write NTLM response: integer overflow?!?");
929 return NS_ERROR_UNEXPECTED;
930 }
931 memcpy(static_cast<uint8_t*>(*outBuf) + offset.value(), msg.targetInfo,
932 msg.targetInfoLen);
933 } else {
934 memcpy(static_cast<uint8_t*>(*outBuf) + offset.value(), ntlmResp,
935 NTLM_RESP_LEN);
936 }
937 // 28 : domain name sec buf
938 offset = NTLM_TYPE3_HEADER_LEN;
939 cursor = WriteSecBuf(cursor, domainLen, offset.value());
940 memcpy(static_cast<uint8_t*>(*outBuf) + offset.value(), domainPtr, domainLen);
941
942 // 36 : user name sec buf
943 offset += domainLen;
944 if (!offset.isValid()) {
945 NS_ERROR("failed preparing to write NTLM response: integer overflow?!?");
946 return NS_ERROR_UNEXPECTED;
947 }
948 cursor = WriteSecBuf(cursor, userLen, offset.value());
949 memcpy(static_cast<uint8_t*>(*outBuf) + offset.value(), userPtr, userLen);
950
951 // 44 : workstation (host) name sec buf
952 offset += userLen;
953 if (!offset.isValid()) {
954 NS_ERROR("failed preparing to write NTLM response: integer overflow?!?");
955 return NS_ERROR_UNEXPECTED;
956 }
957 cursor = WriteSecBuf(cursor, hostLen, offset.value());
958 memcpy(static_cast<uint8_t*>(*outBuf) + offset.value(), hostPtr, hostLen);
959
960 // 52 : session key sec buf (not used)
961 cursor = WriteSecBuf(cursor, 0, 0);
962
963 // 60 : negotiated flags
964 cursor = WriteDWORD(cursor, msg.flags & NTLM_TYPE1_FLAGS);
965
966 return NS_OK;
967 }
968
969 //-----------------------------------------------------------------------------
970
NS_IMPL_ISUPPORTS(nsNTLMAuthModule,nsIAuthModule)971 NS_IMPL_ISUPPORTS(nsNTLMAuthModule, nsIAuthModule)
972
973 nsNTLMAuthModule::~nsNTLMAuthModule()
974 {
975 ZapString(mPassword);
976 }
977
978 nsresult
InitTest()979 nsNTLMAuthModule::InitTest()
980 {
981 static bool prefObserved = false;
982 if (!prefObserved) {
983 mozilla::Preferences::AddBoolVarCache(
984 &sNTLMv1Forced, "network.auth.force-generic-ntlm-v1", sNTLMv1Forced);
985 prefObserved = true;
986 }
987
988 nsNSSShutDownPreventionLock locker;
989 //
990 // disable NTLM authentication when FIPS mode is enabled.
991 //
992 return PK11_IsFIPS() ? NS_ERROR_NOT_AVAILABLE : NS_OK;
993 }
994
995 NS_IMETHODIMP
Init(const char * serviceName,uint32_t serviceFlags,const char16_t * domain,const char16_t * username,const char16_t * password)996 nsNTLMAuthModule::Init(const char *serviceName,
997 uint32_t serviceFlags,
998 const char16_t *domain,
999 const char16_t *username,
1000 const char16_t *password)
1001 {
1002 NS_ASSERTION((serviceFlags & ~nsIAuthModule::REQ_PROXY_AUTH) == nsIAuthModule::REQ_DEFAULT,
1003 "unexpected service flags");
1004
1005 mDomain = domain;
1006 mUsername = username;
1007 mPassword = password;
1008 mNTLMNegotiateSent = false;
1009
1010 static bool sTelemetrySent = false;
1011 if (!sTelemetrySent) {
1012 mozilla::Telemetry::Accumulate(
1013 mozilla::Telemetry::NTLM_MODULE_USED_2,
1014 serviceFlags & nsIAuthModule::REQ_PROXY_AUTH
1015 ? NTLM_MODULE_GENERIC_PROXY
1016 : NTLM_MODULE_GENERIC_DIRECT);
1017 sTelemetrySent = true;
1018 }
1019
1020 return NS_OK;
1021 }
1022
1023 NS_IMETHODIMP
GetNextToken(const void * inToken,uint32_t inTokenLen,void ** outToken,uint32_t * outTokenLen)1024 nsNTLMAuthModule::GetNextToken(const void *inToken,
1025 uint32_t inTokenLen,
1026 void **outToken,
1027 uint32_t *outTokenLen)
1028 {
1029 nsresult rv;
1030 nsNSSShutDownPreventionLock locker;
1031 //
1032 // disable NTLM authentication when FIPS mode is enabled.
1033 //
1034 if (PK11_IsFIPS())
1035 return NS_ERROR_NOT_AVAILABLE;
1036
1037 if (mNTLMNegotiateSent) {
1038 // if inToken is non-null, and we have sent the NTLMSSP_NEGOTIATE (type 1),
1039 // then the NTLMSSP_CHALLENGE (type 2) is expected
1040 if (inToken) {
1041 LogToken("in-token", inToken, inTokenLen);
1042 // Now generate the NTLMSSP_AUTH (type 3)
1043 rv = GenerateType3Msg(mDomain, mUsername, mPassword, inToken,
1044 inTokenLen, outToken, outTokenLen);
1045 } else {
1046 LOG(("NTLMSSP_NEGOTIATE already sent and presumably "
1047 "rejected by the server, refusing to send another"));
1048 rv = NS_ERROR_UNEXPECTED;
1049 }
1050 } else {
1051 if (inToken) {
1052 LOG(("NTLMSSP_NEGOTIATE not sent but NTLM reply already received?!?"));
1053 rv = NS_ERROR_UNEXPECTED;
1054 } else {
1055 rv = GenerateType1Msg(outToken, outTokenLen);
1056 if (NS_SUCCEEDED(rv)) {
1057 mNTLMNegotiateSent = true;
1058 }
1059 }
1060 }
1061
1062 if (NS_SUCCEEDED(rv))
1063 LogToken("out-token", *outToken, *outTokenLen);
1064
1065 return rv;
1066 }
1067
1068 NS_IMETHODIMP
Unwrap(const void * inToken,uint32_t inTokenLen,void ** outToken,uint32_t * outTokenLen)1069 nsNTLMAuthModule::Unwrap(const void *inToken,
1070 uint32_t inTokenLen,
1071 void **outToken,
1072 uint32_t *outTokenLen)
1073 {
1074 return NS_ERROR_NOT_IMPLEMENTED;
1075 }
1076
1077 NS_IMETHODIMP
Wrap(const void * inToken,uint32_t inTokenLen,bool confidential,void ** outToken,uint32_t * outTokenLen)1078 nsNTLMAuthModule::Wrap(const void *inToken,
1079 uint32_t inTokenLen,
1080 bool confidential,
1081 void **outToken,
1082 uint32_t *outTokenLen)
1083 {
1084 return NS_ERROR_NOT_IMPLEMENTED;
1085 }
1086
1087 //-----------------------------------------------------------------------------
1088 // DES support code
1089
1090 // set odd parity bit (in least significant bit position)
1091 static uint8_t
des_setkeyparity(uint8_t x)1092 des_setkeyparity(uint8_t x)
1093 {
1094 if ((((x >> 7) ^ (x >> 6) ^ (x >> 5) ^
1095 (x >> 4) ^ (x >> 3) ^ (x >> 2) ^
1096 (x >> 1)) & 0x01) == 0)
1097 x |= 0x01;
1098 else
1099 x &= 0xfe;
1100 return x;
1101 }
1102
1103 // build 64-bit des key from 56-bit raw key
1104 static void
des_makekey(const uint8_t * raw,uint8_t * key)1105 des_makekey(const uint8_t *raw, uint8_t *key)
1106 {
1107 key[0] = des_setkeyparity(raw[0]);
1108 key[1] = des_setkeyparity((raw[0] << 7) | (raw[1] >> 1));
1109 key[2] = des_setkeyparity((raw[1] << 6) | (raw[2] >> 2));
1110 key[3] = des_setkeyparity((raw[2] << 5) | (raw[3] >> 3));
1111 key[4] = des_setkeyparity((raw[3] << 4) | (raw[4] >> 4));
1112 key[5] = des_setkeyparity((raw[4] << 3) | (raw[5] >> 5));
1113 key[6] = des_setkeyparity((raw[5] << 2) | (raw[6] >> 6));
1114 key[7] = des_setkeyparity((raw[6] << 1));
1115 }
1116
1117 // run des encryption algorithm (using NSS)
1118 static void
des_encrypt(const uint8_t * key,const uint8_t * src,uint8_t * hash)1119 des_encrypt(const uint8_t *key, const uint8_t *src, uint8_t *hash)
1120 {
1121 CK_MECHANISM_TYPE cipherMech = CKM_DES_ECB;
1122 PK11SymKey *symkey = nullptr;
1123 PK11Context *ctxt = nullptr;
1124 SECItem keyItem;
1125 mozilla::UniqueSECItem param;
1126 SECStatus rv;
1127 unsigned int n;
1128
1129 mozilla::UniquePK11SlotInfo slot(PK11_GetBestSlot(cipherMech, nullptr));
1130 if (!slot)
1131 {
1132 NS_ERROR("no slot");
1133 goto done;
1134 }
1135
1136 keyItem.data = const_cast<uint8_t*>(key);
1137 keyItem.len = 8;
1138 symkey = PK11_ImportSymKey(slot.get(), cipherMech,
1139 PK11_OriginUnwrap, CKA_ENCRYPT,
1140 &keyItem, nullptr);
1141 if (!symkey)
1142 {
1143 NS_ERROR("no symkey");
1144 goto done;
1145 }
1146
1147 // no initialization vector required
1148 param = mozilla::UniqueSECItem(PK11_ParamFromIV(cipherMech, nullptr));
1149 if (!param)
1150 {
1151 NS_ERROR("no param");
1152 goto done;
1153 }
1154
1155 ctxt = PK11_CreateContextBySymKey(cipherMech, CKA_ENCRYPT,
1156 symkey, param.get());
1157 if (!ctxt) {
1158 NS_ERROR("no context");
1159 goto done;
1160 }
1161
1162 rv = PK11_CipherOp(ctxt, hash, (int *) &n, 8, (uint8_t *) src, 8);
1163 if (rv != SECSuccess) {
1164 NS_ERROR("des failure");
1165 goto done;
1166 }
1167
1168 rv = PK11_DigestFinal(ctxt, hash+8, &n, 0);
1169 if (rv != SECSuccess) {
1170 NS_ERROR("des failure");
1171 goto done;
1172 }
1173
1174 done:
1175 if (ctxt)
1176 PK11_DestroyContext(ctxt, true);
1177 if (symkey)
1178 PK11_FreeSymKey(symkey);
1179 }
1180