tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

nsNTLMAuthModule.cpp (32169B)


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