security/manager/ssl/src/nsNTLMAuthModule.cpp

Thu, 22 Jan 2015 13:21:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 22 Jan 2015 13:21:57 +0100
branch
TOR_BUG_9701
changeset 15
b8a032363ba2
permissions
-rw-r--r--

Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6

     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/. */
     6 #include "prlog.h"
     8 #include "nsNTLMAuthModule.h"
     9 #include "nsNSSShutDown.h"
    10 #include "nsNativeCharsetUtils.h"
    11 #include "prsystem.h"
    12 #include "pk11pub.h"
    13 #include "md4.h"
    14 #include "mozilla/Likely.h"
    15 #include "mozilla/Telemetry.h"
    16 #include "mozilla/Preferences.h"
    18 #ifdef PR_LOGGING
    19 static PRLogModuleInfo *
    20 GetNTLMLog()
    21 {
    22   static PRLogModuleInfo *sNTLMLog;
    23   if (!sNTLMLog)
    24     sNTLMLog = PR_NewLogModule("NTLM");
    25   return sNTLMLog;
    26 }
    28 #define LOG(x) PR_LOG(GetNTLMLog(), PR_LOG_DEBUG, x)
    29 #define LOG_ENABLED() PR_LOG_TEST(GetNTLMLog(), PR_LOG_DEBUG)
    30 #else
    31 #define LOG(x)
    32 #endif
    34 static void des_makekey(const uint8_t *raw, uint8_t *key);
    35 static void des_encrypt(const uint8_t *key, const uint8_t *src, uint8_t *hash);
    36 static void md5sum(const uint8_t *input, uint32_t inputLen, uint8_t *result);
    38 //-----------------------------------------------------------------------------
    39 // this file contains a cross-platform NTLM authentication implementation. it
    40 // is based on documentation from: http://davenport.sourceforge.net/ntlm.html
    41 //-----------------------------------------------------------------------------
    43 #define NTLM_NegotiateUnicode               0x00000001
    44 #define NTLM_NegotiateOEM                   0x00000002
    45 #define NTLM_RequestTarget                  0x00000004
    46 #define NTLM_Unknown1                       0x00000008
    47 #define NTLM_NegotiateSign                  0x00000010
    48 #define NTLM_NegotiateSeal                  0x00000020
    49 #define NTLM_NegotiateDatagramStyle         0x00000040
    50 #define NTLM_NegotiateLanManagerKey         0x00000080
    51 #define NTLM_NegotiateNetware               0x00000100
    52 #define NTLM_NegotiateNTLMKey               0x00000200
    53 #define NTLM_Unknown2                       0x00000400
    54 #define NTLM_Unknown3                       0x00000800
    55 #define NTLM_NegotiateDomainSupplied        0x00001000
    56 #define NTLM_NegotiateWorkstationSupplied   0x00002000
    57 #define NTLM_NegotiateLocalCall             0x00004000
    58 #define NTLM_NegotiateAlwaysSign            0x00008000
    59 #define NTLM_TargetTypeDomain               0x00010000
    60 #define NTLM_TargetTypeServer               0x00020000
    61 #define NTLM_TargetTypeShare                0x00040000
    62 #define NTLM_NegotiateNTLM2Key              0x00080000
    63 #define NTLM_RequestInitResponse            0x00100000
    64 #define NTLM_RequestAcceptResponse          0x00200000
    65 #define NTLM_RequestNonNTSessionKey         0x00400000
    66 #define NTLM_NegotiateTargetInfo            0x00800000
    67 #define NTLM_Unknown4                       0x01000000
    68 #define NTLM_Unknown5                       0x02000000
    69 #define NTLM_Unknown6                       0x04000000
    70 #define NTLM_Unknown7                       0x08000000
    71 #define NTLM_Unknown8                       0x10000000
    72 #define NTLM_Negotiate128                   0x20000000
    73 #define NTLM_NegotiateKeyExchange           0x40000000
    74 #define NTLM_Negotiate56                    0x80000000
    76 // we send these flags with our type 1 message
    77 #define NTLM_TYPE1_FLAGS      \
    78   (NTLM_NegotiateUnicode |    \
    79    NTLM_NegotiateOEM |        \
    80    NTLM_RequestTarget |       \
    81    NTLM_NegotiateNTLMKey |    \
    82    NTLM_NegotiateAlwaysSign | \
    83    NTLM_NegotiateNTLM2Key)
    85 static const char NTLM_SIGNATURE[] = "NTLMSSP";
    86 static const char NTLM_TYPE1_MARKER[] = { 0x01, 0x00, 0x00, 0x00 };
    87 static const char NTLM_TYPE2_MARKER[] = { 0x02, 0x00, 0x00, 0x00 };
    88 static const char NTLM_TYPE3_MARKER[] = { 0x03, 0x00, 0x00, 0x00 };
    90 #define NTLM_TYPE1_HEADER_LEN 32
    91 #define NTLM_TYPE2_HEADER_LEN 32
    92 #define NTLM_TYPE3_HEADER_LEN 64
    94 #define LM_HASH_LEN 16
    95 #define LM_RESP_LEN 24
    97 #define NTLM_HASH_LEN 16
    98 #define NTLM_RESP_LEN 24
   100 //-----------------------------------------------------------------------------
   102 static bool sendLM = false;
   104 /*static*/ void
   105 nsNTLMAuthModule::SetSendLM(bool newSendLM)
   106 {
   107   sendLM = newSendLM;
   108 }
   110 //-----------------------------------------------------------------------------
   112 #ifdef PR_LOGGING
   114 /**
   115  * Prints a description of flags to the NSPR Log, if enabled.
   116  */
   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)
   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);
   158 #undef TEST
   159 }
   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
   168 LogBuf(const char *tag, const uint8_t *buf, uint32_t bufLen)
   169 {
   170   int i;
   172   if (!LOG_ENABLED())
   173     return;
   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;
   183     strcpy(line, "    ");
   184     for (i=0; i<count; ++i)
   185     {
   186       int len = strlen(line);
   187       PR_snprintf(line + len, sizeof(line) - len, "0x%02x ", int(buf[i]));
   188     }
   189     for (; i<8; ++i)
   190     {
   191       int len = strlen(line);
   192       PR_snprintf(line + len, sizeof(line) - len, "     ");
   193     }
   195     int len = strlen(line);
   196     PR_snprintf(line + len, sizeof(line) - len, "   ");
   197     for (i=0; i<count; ++i)
   198     {
   199       len = strlen(line);
   200       if (isprint(buf[i]))
   201         PR_snprintf(line + len, sizeof(line) - len, "%c", buf[i]);
   202       else
   203         PR_snprintf(line + len, sizeof(line) - len, ".");
   204     }
   205     PR_LogPrint("%s\n", line);
   207     bufLen -= count;
   208     buf += count;
   209   }
   210 }
   212 #include "plbase64.h"
   213 #include "prmem.h"
   214 /**
   215  * Print base64-encoded token to the NSPR Log.
   216  * @param name Description of the token, will be printed in front
   217  * @param token The token to print
   218  * @param tokenLen length of the data in token
   219  */
   220 static void LogToken(const char *name, const void *token, uint32_t tokenLen)
   221 {
   222   if (!LOG_ENABLED())
   223     return;
   225   char *b64data = PL_Base64Encode((const char *) token, tokenLen, nullptr);
   226   if (b64data)
   227   {
   228     PR_LogPrint("%s: %s\n", name, b64data);
   229     PR_Free(b64data);
   230   }
   231 }
   233 #else
   234 #define LogFlags(x)
   235 #define LogBuf(a,b,c)
   236 #define LogToken(a,b,c)
   238 #endif // PR_LOGGING
   240 //-----------------------------------------------------------------------------
   242 // byte order swapping
   243 #define SWAP16(x) ((((x) & 0xff) << 8) | (((x) >> 8) & 0xff))
   244 #define SWAP32(x) ((SWAP16((x) & 0xffff) << 16) | (SWAP16((x) >> 16)))
   246 static void *
   247 WriteBytes(void *buf, const void *data, uint32_t dataLen)
   248 {
   249   memcpy(buf, data, dataLen);
   250   return (uint8_t *) buf + dataLen;
   251 }
   253 static void *
   254 WriteDWORD(void *buf, uint32_t dword)
   255 {
   256 #ifdef IS_BIG_ENDIAN 
   257   // NTLM uses little endian on the wire
   258   dword = SWAP32(dword);
   259 #endif
   260   return WriteBytes(buf, &dword, sizeof(dword));
   261 }
   263 static void *
   264 WriteSecBuf(void *buf, uint16_t length, uint32_t offset)
   265 {
   266 #ifdef IS_BIG_ENDIAN
   267   length = SWAP16(length);
   268   offset = SWAP32(offset);
   269 #endif
   270   buf = WriteBytes(buf, &length, sizeof(length));
   271   buf = WriteBytes(buf, &length, sizeof(length));
   272   buf = WriteBytes(buf, &offset, sizeof(offset));
   273   return buf;
   274 }
   276 #ifdef IS_BIG_ENDIAN
   277 /**
   278  * WriteUnicodeLE copies a unicode string from one buffer to another.  The
   279  * resulting unicode string is in little-endian format.  The input string is
   280  * assumed to be in the native endianness of the local machine.  It is safe
   281  * to pass the same buffer as both input and output, which is a handy way to
   282  * convert the unicode buffer to little-endian on big-endian platforms.
   283  */
   284 static void *
   285 WriteUnicodeLE(void *buf, const char16_t *str, uint32_t strLen)
   286 {
   287   // convert input string from BE to LE
   288   uint8_t *cursor = (uint8_t *) buf,
   289           *input  = (uint8_t *) str;
   290   for (uint32_t i=0; i<strLen; ++i, input+=2, cursor+=2)
   291   {
   292     // allow for the case where |buf == str|
   293     uint8_t temp = input[0];
   294     cursor[0] = input[1];
   295     cursor[1] = temp;
   296   }
   297   return buf;
   298 }
   299 #endif
   301 static uint16_t
   302 ReadUint16(const uint8_t *&buf)
   303 {
   304   uint16_t x = ((uint16_t) buf[0]) | ((uint16_t) buf[1] << 8);
   305   buf += sizeof(x);
   306   return x;
   307 }
   309 static uint32_t
   310 ReadUint32(const uint8_t *&buf)
   311 {
   312   uint32_t x = ( (uint32_t) buf[0])        |
   313                (((uint32_t) buf[1]) << 8)  |
   314                (((uint32_t) buf[2]) << 16) |
   315                (((uint32_t) buf[3]) << 24);
   316   buf += sizeof(x);
   317   return x;
   318 }
   320 //-----------------------------------------------------------------------------
   322 static void
   323 ZapBuf(void *buf, size_t bufLen)
   324 {
   325   memset(buf, 0, bufLen);
   326 }
   328 static void
   329 ZapString(nsCString &s)
   330 {
   331   ZapBuf(s.BeginWriting(), s.Length());
   332 }
   334 static void
   335 ZapString(nsString &s)
   336 {
   337   ZapBuf(s.BeginWriting(), s.Length() * 2);
   338 }
   340 static const unsigned char LM_MAGIC[] = "KGS!@#$%";
   342 /**
   343  * LM_Hash computes the LM hash of the given password.
   344  *
   345  * @param password
   346  *        null-terminated unicode password.
   347  * @param hash
   348  *        16-byte result buffer
   349  */
   350 static void
   351 LM_Hash(const nsString &password, unsigned char *hash)
   352 {
   353   // convert password to OEM character set.  we'll just use the native
   354   // filesystem charset.
   355   nsAutoCString passbuf;
   356   NS_CopyUnicodeToNative(password, passbuf);
   357   ToUpperCase(passbuf);
   358   uint32_t n = passbuf.Length();
   359   passbuf.SetLength(14);
   360   for (uint32_t i=n; i<14; ++i)
   361     passbuf.SetCharAt('\0', i);
   363   unsigned char k1[8], k2[8];
   364   des_makekey((const unsigned char *) passbuf.get()    , k1);
   365   des_makekey((const unsigned char *) passbuf.get() + 7, k2);
   366   ZapString(passbuf);
   368   // use password keys to hash LM magic string twice.
   369   des_encrypt(k1, LM_MAGIC, hash);
   370   des_encrypt(k2, LM_MAGIC, hash + 8);
   371 }
   373 /**
   374  * NTLM_Hash computes the NTLM hash of the given password.
   375  *
   376  * @param password
   377  *        null-terminated unicode password.
   378  * @param hash
   379  *        16-byte result buffer
   380  */
   381 static void
   382 NTLM_Hash(const nsString &password, unsigned char *hash)
   383 {
   384   uint32_t len = password.Length();
   385   uint8_t *passbuf;
   387 #ifdef IS_BIG_ENDIAN
   388   passbuf = (uint8_t *) malloc(len * 2);
   389   WriteUnicodeLE(passbuf, password.get(), len);
   390 #else
   391   passbuf = (uint8_t *) password.get();
   392 #endif
   394   md4sum(passbuf, len * 2, hash);
   396 #ifdef IS_BIG_ENDIAN
   397   ZapBuf(passbuf, len * 2);
   398   free(passbuf);
   399 #endif
   400 }
   402 //-----------------------------------------------------------------------------
   404 /** 
   405  * LM_Response generates the LM response given a 16-byte password hash and the
   406  * challenge from the Type-2 message.
   407  *
   408  * @param hash
   409  *        16-byte password hash
   410  * @param challenge
   411  *        8-byte challenge from Type-2 message
   412  * @param response
   413  *        24-byte buffer to contain the LM response upon return
   414  */
   415 static void
   416 LM_Response(const uint8_t *hash, const uint8_t *challenge, uint8_t *response)
   417 {
   418   uint8_t keybytes[21], k1[8], k2[8], k3[8];
   420   memcpy(keybytes, hash, 16);
   421   ZapBuf(keybytes + 16, 5);
   423   des_makekey(keybytes     , k1);
   424   des_makekey(keybytes +  7, k2);
   425   des_makekey(keybytes + 14, k3);
   427   des_encrypt(k1, challenge, response);
   428   des_encrypt(k2, challenge, response + 8);
   429   des_encrypt(k3, challenge, response + 16);
   430 }
   432 //-----------------------------------------------------------------------------
   434 static nsresult
   435 GenerateType1Msg(void **outBuf, uint32_t *outLen)
   436 {
   437   //
   438   // verify that bufLen is sufficient
   439   //
   440   *outLen = NTLM_TYPE1_HEADER_LEN;
   441   *outBuf = nsMemory::Alloc(*outLen);
   442   if (!*outBuf)
   443     return NS_ERROR_OUT_OF_MEMORY;
   445   //
   446   // write out type 1 msg
   447   //
   448   void *cursor = *outBuf;
   450   // 0 : signature
   451   cursor = WriteBytes(cursor, NTLM_SIGNATURE, sizeof(NTLM_SIGNATURE));
   453   // 8 : marker
   454   cursor = WriteBytes(cursor, NTLM_TYPE1_MARKER, sizeof(NTLM_TYPE1_MARKER));
   456   // 12 : flags
   457   cursor = WriteDWORD(cursor, NTLM_TYPE1_FLAGS);
   459   //
   460   // NOTE: it is common for the domain and workstation fields to be empty.
   461   //       this is true of Win2k clients, and my guess is that there is
   462   //       little utility to sending these strings before the charset has
   463   //       been negotiated.  we follow suite -- anyways, it doesn't hurt
   464   //       to save some bytes on the wire ;-)
   465   //
   467   // 16 : supplied domain security buffer (empty)
   468   cursor = WriteSecBuf(cursor, 0, 0);
   470   // 24 : supplied workstation security buffer (empty)
   471   cursor = WriteSecBuf(cursor, 0, 0);
   473   return NS_OK;
   474 }
   476 struct Type2Msg
   477 {
   478   uint32_t    flags;         // NTLM_Xxx bitwise combination
   479   uint8_t     challenge[8];  // 8 byte challenge
   480   const void *target;        // target string (type depends on flags)
   481   uint32_t    targetLen;     // target length in bytes
   482 };
   484 static nsresult
   485 ParseType2Msg(const void *inBuf, uint32_t inLen, Type2Msg *msg)
   486 {
   487   // make sure inBuf is long enough to contain a meaningful type2 msg.
   488   //
   489   // 0  NTLMSSP Signature
   490   // 8  NTLM Message Type
   491   // 12 Target Name
   492   // 20 Flags
   493   // 24 Challenge
   494   // 32 end of header, start of optional data blocks
   495   //
   496   if (inLen < NTLM_TYPE2_HEADER_LEN)
   497     return NS_ERROR_UNEXPECTED;
   499   const uint8_t *cursor = (const uint8_t *) inBuf;
   501   // verify NTLMSSP signature
   502   if (memcmp(cursor, NTLM_SIGNATURE, sizeof(NTLM_SIGNATURE)) != 0)
   503     return NS_ERROR_UNEXPECTED;
   505   cursor += sizeof(NTLM_SIGNATURE);
   507   // verify Type-2 marker
   508   if (memcmp(cursor, NTLM_TYPE2_MARKER, sizeof(NTLM_TYPE2_MARKER)) != 0)
   509     return NS_ERROR_UNEXPECTED;
   511   cursor += sizeof(NTLM_TYPE2_MARKER);
   513   // Read target name security buffer: ...
   514   // ... read target length.
   515   uint32_t targetLen = ReadUint16(cursor);
   516   // ... skip next 16-bit "allocated space" value.
   517   ReadUint16(cursor);
   518   // ... read offset from inBuf.
   519   uint32_t offset = ReadUint32(cursor);
   520   // Check the offset / length combo is in range of the input buffer, including
   521   // integer overflow checking.
   522   if (MOZ_LIKELY(offset < offset + targetLen && offset + targetLen <= inLen)) {
   523     msg->targetLen = targetLen;
   524     msg->target = ((const uint8_t *) inBuf) + offset;
   525   }
   526   else
   527   {
   528     // Do not error out, for (conservative) backward compatibility.
   529     msg->targetLen = 0;
   530     msg->target = nullptr;
   531   }
   533   // read flags
   534   msg->flags = ReadUint32(cursor);
   536   // read challenge
   537   memcpy(msg->challenge, cursor, sizeof(msg->challenge));
   538   cursor += sizeof(msg->challenge);
   541   LOG(("NTLM type 2 message:\n"));
   542   LogBuf("target", (const uint8_t *) msg->target, msg->targetLen);
   543   LogBuf("flags", (const uint8_t *) &msg->flags, 4);
   544   LogFlags(msg->flags);
   545   LogBuf("challenge", msg->challenge, sizeof(msg->challenge));
   547   // we currently do not implement LMv2/NTLMv2 or NTLM2 responses,
   548   // so we can ignore target information.  we may want to enable
   549   // support for these alternate mechanisms in the future.
   550   return NS_OK;
   551 }
   553 static nsresult
   554 GenerateType3Msg(const nsString &domain,
   555                  const nsString &username,
   556                  const nsString &password,
   557                  const void     *inBuf,
   558                  uint32_t        inLen,
   559                  void          **outBuf,
   560                  uint32_t       *outLen)
   561 {
   562   // inBuf contains Type-2 msg (the challenge) from server
   564   nsresult rv;
   565   Type2Msg msg;
   567   rv = ParseType2Msg(inBuf, inLen, &msg);
   568   if (NS_FAILED(rv))
   569     return rv;
   571   bool unicode = (msg.flags & NTLM_NegotiateUnicode);
   573   // temporary buffers for unicode strings
   574 #ifdef IS_BIG_ENDIAN
   575   nsAutoString ucsDomainBuf, ucsUserBuf;
   576 #endif
   577   nsAutoString ucsHostBuf; 
   578   // temporary buffers for oem strings
   579   nsAutoCString oemDomainBuf, oemUserBuf, oemHostBuf;
   580   // pointers and lengths for the string buffers; encoding is unicode if
   581   // the "negotiate unicode" flag was set in the Type-2 message.
   582   const void *domainPtr, *userPtr, *hostPtr;
   583   uint32_t domainLen, userLen, hostLen;
   585   //
   586   // get domain name
   587   //
   588   if (unicode)
   589   {
   590 #ifdef IS_BIG_ENDIAN
   591     ucsDomainBuf = domain;
   592     domainPtr = ucsDomainBuf.get();
   593     domainLen = ucsDomainBuf.Length() * 2;
   594     WriteUnicodeLE((void *) domainPtr, (const char16_t *) domainPtr,
   595                    ucsDomainBuf.Length());
   596 #else
   597     domainPtr = domain.get();
   598     domainLen = domain.Length() * 2;
   599 #endif
   600   }
   601   else
   602   {
   603     NS_CopyUnicodeToNative(domain, oemDomainBuf);
   604     domainPtr = oemDomainBuf.get();
   605     domainLen = oemDomainBuf.Length();
   606   }
   608   //
   609   // get user name
   610   //
   611   if (unicode)
   612   {
   613 #ifdef IS_BIG_ENDIAN
   614     ucsUserBuf = username;
   615     userPtr = ucsUserBuf.get();
   616     userLen = ucsUserBuf.Length() * 2;
   617     WriteUnicodeLE((void *) userPtr, (const char16_t *) userPtr,
   618                    ucsUserBuf.Length());
   619 #else
   620     userPtr = username.get();
   621     userLen = username.Length() * 2;
   622 #endif
   623   }
   624   else
   625   {
   626     NS_CopyUnicodeToNative(username, oemUserBuf);
   627     userPtr = oemUserBuf.get();
   628     userLen = oemUserBuf.Length();
   629   }
   631   //
   632   // get workstation name (use local machine's hostname)
   633   //
   634   char hostBuf[SYS_INFO_BUFFER_LENGTH];
   635   if (PR_GetSystemInfo(PR_SI_HOSTNAME, hostBuf, sizeof(hostBuf)) == PR_FAILURE)
   636     return NS_ERROR_UNEXPECTED;
   637   hostLen = strlen(hostBuf);
   638   if (unicode)
   639   {
   640     // hostname is ASCII, so we can do a simple zero-pad expansion:
   641     CopyASCIItoUTF16(nsDependentCString(hostBuf, hostLen), ucsHostBuf);
   642     hostPtr = ucsHostBuf.get();
   643     hostLen = ucsHostBuf.Length() * 2;
   644 #ifdef IS_BIG_ENDIAN
   645     WriteUnicodeLE((void *) hostPtr, (const char16_t *) hostPtr,
   646                    ucsHostBuf.Length());
   647 #endif
   648   }
   649   else
   650     hostPtr = hostBuf;
   652   //
   653   // now that we have generated all of the strings, we can allocate outBuf.
   654   //
   655   *outLen = NTLM_TYPE3_HEADER_LEN + hostLen + domainLen + userLen +
   656             LM_RESP_LEN + NTLM_RESP_LEN;
   657   *outBuf = nsMemory::Alloc(*outLen);
   658   if (!*outBuf)
   659     return NS_ERROR_OUT_OF_MEMORY;
   661   //
   662   // next, we compute the LM and NTLM responses.
   663   //
   664   uint8_t lmResp[LM_RESP_LEN], ntlmResp[NTLM_RESP_LEN], ntlmHash[NTLM_HASH_LEN];
   665   if (msg.flags & NTLM_NegotiateNTLM2Key)
   666   {
   667     // compute NTLM2 session response
   668     uint8_t sessionHash[16], temp[16];
   670     PK11_GenerateRandom(lmResp, 8);
   671     memset(lmResp + 8, 0, LM_RESP_LEN - 8);
   673     memcpy(temp, msg.challenge, 8);
   674     memcpy(temp + 8, lmResp, 8);
   675     md5sum(temp, 16, sessionHash);
   677     NTLM_Hash(password, ntlmHash);
   678     LM_Response(ntlmHash, sessionHash, ntlmResp);
   679   }
   680   else
   681   {
   682     NTLM_Hash(password, ntlmHash);
   683     LM_Response(ntlmHash, msg.challenge, ntlmResp);
   685     if (sendLM)
   686     {
   687       uint8_t lmHash[LM_HASH_LEN];
   688       LM_Hash(password, lmHash);
   689       LM_Response(lmHash, msg.challenge, lmResp);
   690     }
   691     else
   692     {
   693       // According to http://davenport.sourceforge.net/ntlm.html#ntlmVersion2,
   694       // the correct way to not send the LM hash is to send the NTLM hash twice
   695       // in both the LM and NTLM response fields.
   696       LM_Response(ntlmHash, msg.challenge, lmResp);
   697     }
   698   }
   700   //
   701   // finally, we assemble the Type-3 msg :-)
   702   //
   703   void *cursor = *outBuf;
   704   uint32_t offset;
   706   // 0 : signature
   707   cursor = WriteBytes(cursor, NTLM_SIGNATURE, sizeof(NTLM_SIGNATURE));
   709   // 8 : marker
   710   cursor = WriteBytes(cursor, NTLM_TYPE3_MARKER, sizeof(NTLM_TYPE3_MARKER));
   712   // 12 : LM response sec buf
   713   offset = NTLM_TYPE3_HEADER_LEN + domainLen + userLen + hostLen;
   714   cursor = WriteSecBuf(cursor, LM_RESP_LEN, offset);
   715   memcpy((uint8_t *) *outBuf + offset, lmResp, LM_RESP_LEN);
   717   // 20 : NTLM response sec buf
   718   offset += LM_RESP_LEN;
   719   cursor = WriteSecBuf(cursor, NTLM_RESP_LEN, offset);
   720   memcpy((uint8_t *) *outBuf + offset, ntlmResp, NTLM_RESP_LEN);
   722   // 28 : domain name sec buf
   723   offset = NTLM_TYPE3_HEADER_LEN;
   724   cursor = WriteSecBuf(cursor, domainLen, offset);
   725   memcpy((uint8_t *) *outBuf + offset, domainPtr, domainLen);
   727   // 36 : user name sec buf
   728   offset += domainLen;
   729   cursor = WriteSecBuf(cursor, userLen, offset);
   730   memcpy((uint8_t *) *outBuf + offset, userPtr, userLen);
   732   // 44 : workstation (host) name sec buf
   733   offset += userLen;
   734   cursor = WriteSecBuf(cursor, hostLen, offset);
   735   memcpy((uint8_t *) *outBuf + offset, hostPtr, hostLen);
   737   // 52 : session key sec buf (not used)
   738   cursor = WriteSecBuf(cursor, 0, 0);
   740   // 60 : negotiated flags
   741   cursor = WriteDWORD(cursor, msg.flags & NTLM_TYPE1_FLAGS);
   743   return NS_OK;
   744 }
   746 //-----------------------------------------------------------------------------
   748 NS_IMPL_ISUPPORTS(nsNTLMAuthModule, nsIAuthModule)
   750 nsNTLMAuthModule::~nsNTLMAuthModule()
   751 {
   752   ZapString(mPassword);
   753 }
   755 nsresult
   756 nsNTLMAuthModule::InitTest()
   757 {
   758   nsNSSShutDownPreventionLock locker;
   759   //
   760   // disable NTLM authentication when FIPS mode is enabled.
   761   //
   762   return PK11_IsFIPS() ? NS_ERROR_NOT_AVAILABLE : NS_OK;
   763 }
   765 NS_IMETHODIMP
   766 nsNTLMAuthModule::Init(const char      *serviceName,
   767                        uint32_t         serviceFlags,
   768                        const char16_t *domain,
   769                        const char16_t *username,
   770                        const char16_t *password)
   771 {
   772   NS_ASSERTION((serviceFlags & ~nsIAuthModule::REQ_PROXY_AUTH) == nsIAuthModule::REQ_DEFAULT,
   773       "unexpected service flags");
   775   mDomain = domain;
   776   mUsername = username;
   777   mPassword = password;
   779   static bool sTelemetrySent = false;
   780   if (!sTelemetrySent) {
   781       mozilla::Telemetry::Accumulate(
   782           mozilla::Telemetry::NTLM_MODULE_USED_2,
   783           serviceFlags & nsIAuthModule::REQ_PROXY_AUTH
   784               ? NTLM_MODULE_GENERIC_PROXY
   785               : NTLM_MODULE_GENERIC_DIRECT);
   786       sTelemetrySent = true;
   787   }
   789   return NS_OK;
   790 }
   792 NS_IMETHODIMP
   793 nsNTLMAuthModule::GetNextToken(const void *inToken,
   794                                uint32_t    inTokenLen,
   795                                void      **outToken,
   796                                uint32_t   *outTokenLen)
   797 {
   798   nsresult rv;
   799   nsNSSShutDownPreventionLock locker;
   800   //
   801   // disable NTLM authentication when FIPS mode is enabled.
   802   //
   803   if (PK11_IsFIPS())
   804     return NS_ERROR_NOT_AVAILABLE;
   806   // if inToken is non-null, then assume it contains a type 2 message...
   807   if (inToken)
   808   {
   809     LogToken("in-token", inToken, inTokenLen);
   810     rv = GenerateType3Msg(mDomain, mUsername, mPassword, inToken,
   811                           inTokenLen, outToken, outTokenLen);
   812   }
   813   else
   814   {
   815     rv = GenerateType1Msg(outToken, outTokenLen);
   816   }
   818 #ifdef PR_LOGGING
   819   if (NS_SUCCEEDED(rv))
   820     LogToken("out-token", *outToken, *outTokenLen);
   821 #endif
   823   return rv;
   824 }
   826 NS_IMETHODIMP
   827 nsNTLMAuthModule::Unwrap(const void *inToken,
   828                         uint32_t    inTokenLen,
   829                         void      **outToken,
   830                         uint32_t   *outTokenLen)
   831 {
   832   return NS_ERROR_NOT_IMPLEMENTED;
   833 }
   835 NS_IMETHODIMP
   836 nsNTLMAuthModule::Wrap(const void *inToken,
   837                        uint32_t    inTokenLen,
   838                        bool        confidential,
   839                        void      **outToken,
   840                        uint32_t   *outTokenLen)
   841 {
   842   return NS_ERROR_NOT_IMPLEMENTED;
   843 }
   845 //-----------------------------------------------------------------------------
   846 // DES support code
   848 // set odd parity bit (in least significant bit position)
   849 static uint8_t
   850 des_setkeyparity(uint8_t x)
   851 {
   852   if ((((x >> 7) ^ (x >> 6) ^ (x >> 5) ^
   853         (x >> 4) ^ (x >> 3) ^ (x >> 2) ^
   854         (x >> 1)) & 0x01) == 0)
   855     x |= 0x01;
   856   else
   857     x &= 0xfe;
   858   return x;
   859 }
   861 // build 64-bit des key from 56-bit raw key
   862 static void
   863 des_makekey(const uint8_t *raw, uint8_t *key)
   864 {
   865   key[0] = des_setkeyparity(raw[0]);
   866   key[1] = des_setkeyparity((raw[0] << 7) | (raw[1] >> 1));
   867   key[2] = des_setkeyparity((raw[1] << 6) | (raw[2] >> 2));
   868   key[3] = des_setkeyparity((raw[2] << 5) | (raw[3] >> 3));
   869   key[4] = des_setkeyparity((raw[3] << 4) | (raw[4] >> 4));
   870   key[5] = des_setkeyparity((raw[4] << 3) | (raw[5] >> 5));
   871   key[6] = des_setkeyparity((raw[5] << 2) | (raw[6] >> 6));
   872   key[7] = des_setkeyparity((raw[6] << 1));
   873 }
   875 // run des encryption algorithm (using NSS)
   876 static void
   877 des_encrypt(const uint8_t *key, const uint8_t *src, uint8_t *hash)
   878 {
   879   CK_MECHANISM_TYPE cipherMech = CKM_DES_ECB;
   880   PK11SlotInfo *slot = nullptr;
   881   PK11SymKey *symkey = nullptr;
   882   PK11Context *ctxt = nullptr;
   883   SECItem keyItem, *param = nullptr;
   884   SECStatus rv;
   885   unsigned int n;
   887   slot = PK11_GetBestSlot(cipherMech, nullptr);
   888   if (!slot)
   889   {
   890     NS_ERROR("no slot");
   891     goto done;
   892   }
   894   keyItem.data = (uint8_t *) key;
   895   keyItem.len = 8;
   896   symkey = PK11_ImportSymKey(slot, cipherMech,
   897                              PK11_OriginUnwrap, CKA_ENCRYPT,
   898                              &keyItem, nullptr);
   899   if (!symkey)
   900   {
   901     NS_ERROR("no symkey");
   902     goto done;
   903   }
   905   // no initialization vector required
   906   param = PK11_ParamFromIV(cipherMech, nullptr);
   907   if (!param)
   908   {
   909     NS_ERROR("no param");
   910     goto done;
   911   }
   913   ctxt = PK11_CreateContextBySymKey(cipherMech, CKA_ENCRYPT,
   914                                     symkey, param);
   915   if (!ctxt)
   916   {
   917     NS_ERROR("no context");
   918     goto done;
   919   }
   921   rv = PK11_CipherOp(ctxt, hash, (int *) &n, 8, (uint8_t *) src, 8);
   922   if (rv != SECSuccess)
   923   {
   924     NS_ERROR("des failure");
   925     goto done;
   926   }
   928   rv = PK11_DigestFinal(ctxt, hash+8, &n, 0);
   929   if (rv != SECSuccess)
   930   {
   931     NS_ERROR("des failure");
   932     goto done;
   933   }
   935 done:
   936   if (ctxt)
   937     PK11_DestroyContext(ctxt, true);
   938   if (symkey)
   939     PK11_FreeSymKey(symkey);
   940   if (param)
   941     SECITEM_FreeItem(param, true);
   942   if (slot)
   943     PK11_FreeSlot(slot);
   944 }
   946 //-----------------------------------------------------------------------------
   947 // MD5 support code
   949 static void md5sum(const uint8_t *input, uint32_t inputLen, uint8_t *result)
   950 {
   951   PK11Context *ctxt = PK11_CreateDigestContext(SEC_OID_MD5);
   952   if (ctxt)
   953   {
   954     if (PK11_DigestBegin(ctxt) == SECSuccess)
   955     {
   956       if (PK11_DigestOp(ctxt, input, inputLen) == SECSuccess)
   957       {
   958         uint32_t resultLen = 16;
   959         PK11_DigestFinal(ctxt, result, &resultLen, resultLen);
   960       }
   961     }
   962     PK11_DestroyContext(ctxt, true);
   963   }
   964 }

mercurial