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