security/manager/ssl/src/nsNTLMAuthModule.cpp

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/security/manager/ssl/src/nsNTLMAuthModule.cpp	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,964 @@
     1.4 +/* vim:set ts=2 sw=2 et cindent: */
     1.5 +/* This Source Code Form is subject to the terms of the Mozilla Public
     1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this
     1.7 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     1.8 +
     1.9 +#include "prlog.h"
    1.10 +
    1.11 +#include "nsNTLMAuthModule.h"
    1.12 +#include "nsNSSShutDown.h"
    1.13 +#include "nsNativeCharsetUtils.h"
    1.14 +#include "prsystem.h"
    1.15 +#include "pk11pub.h"
    1.16 +#include "md4.h"
    1.17 +#include "mozilla/Likely.h"
    1.18 +#include "mozilla/Telemetry.h"
    1.19 +#include "mozilla/Preferences.h"
    1.20 +
    1.21 +#ifdef PR_LOGGING
    1.22 +static PRLogModuleInfo *
    1.23 +GetNTLMLog()
    1.24 +{
    1.25 +  static PRLogModuleInfo *sNTLMLog;
    1.26 +  if (!sNTLMLog)
    1.27 +    sNTLMLog = PR_NewLogModule("NTLM");
    1.28 +  return sNTLMLog;
    1.29 +}
    1.30 +
    1.31 +#define LOG(x) PR_LOG(GetNTLMLog(), PR_LOG_DEBUG, x)
    1.32 +#define LOG_ENABLED() PR_LOG_TEST(GetNTLMLog(), PR_LOG_DEBUG)
    1.33 +#else
    1.34 +#define LOG(x)
    1.35 +#endif
    1.36 +
    1.37 +static void des_makekey(const uint8_t *raw, uint8_t *key);
    1.38 +static void des_encrypt(const uint8_t *key, const uint8_t *src, uint8_t *hash);
    1.39 +static void md5sum(const uint8_t *input, uint32_t inputLen, uint8_t *result);
    1.40 +
    1.41 +//-----------------------------------------------------------------------------
    1.42 +// this file contains a cross-platform NTLM authentication implementation. it
    1.43 +// is based on documentation from: http://davenport.sourceforge.net/ntlm.html
    1.44 +//-----------------------------------------------------------------------------
    1.45 +
    1.46 +#define NTLM_NegotiateUnicode               0x00000001
    1.47 +#define NTLM_NegotiateOEM                   0x00000002
    1.48 +#define NTLM_RequestTarget                  0x00000004
    1.49 +#define NTLM_Unknown1                       0x00000008
    1.50 +#define NTLM_NegotiateSign                  0x00000010
    1.51 +#define NTLM_NegotiateSeal                  0x00000020
    1.52 +#define NTLM_NegotiateDatagramStyle         0x00000040
    1.53 +#define NTLM_NegotiateLanManagerKey         0x00000080
    1.54 +#define NTLM_NegotiateNetware               0x00000100
    1.55 +#define NTLM_NegotiateNTLMKey               0x00000200
    1.56 +#define NTLM_Unknown2                       0x00000400
    1.57 +#define NTLM_Unknown3                       0x00000800
    1.58 +#define NTLM_NegotiateDomainSupplied        0x00001000
    1.59 +#define NTLM_NegotiateWorkstationSupplied   0x00002000
    1.60 +#define NTLM_NegotiateLocalCall             0x00004000
    1.61 +#define NTLM_NegotiateAlwaysSign            0x00008000
    1.62 +#define NTLM_TargetTypeDomain               0x00010000
    1.63 +#define NTLM_TargetTypeServer               0x00020000
    1.64 +#define NTLM_TargetTypeShare                0x00040000
    1.65 +#define NTLM_NegotiateNTLM2Key              0x00080000
    1.66 +#define NTLM_RequestInitResponse            0x00100000
    1.67 +#define NTLM_RequestAcceptResponse          0x00200000
    1.68 +#define NTLM_RequestNonNTSessionKey         0x00400000
    1.69 +#define NTLM_NegotiateTargetInfo            0x00800000
    1.70 +#define NTLM_Unknown4                       0x01000000
    1.71 +#define NTLM_Unknown5                       0x02000000
    1.72 +#define NTLM_Unknown6                       0x04000000
    1.73 +#define NTLM_Unknown7                       0x08000000
    1.74 +#define NTLM_Unknown8                       0x10000000
    1.75 +#define NTLM_Negotiate128                   0x20000000
    1.76 +#define NTLM_NegotiateKeyExchange           0x40000000
    1.77 +#define NTLM_Negotiate56                    0x80000000
    1.78 +
    1.79 +// we send these flags with our type 1 message
    1.80 +#define NTLM_TYPE1_FLAGS      \
    1.81 +  (NTLM_NegotiateUnicode |    \
    1.82 +   NTLM_NegotiateOEM |        \
    1.83 +   NTLM_RequestTarget |       \
    1.84 +   NTLM_NegotiateNTLMKey |    \
    1.85 +   NTLM_NegotiateAlwaysSign | \
    1.86 +   NTLM_NegotiateNTLM2Key)
    1.87 +
    1.88 +static const char NTLM_SIGNATURE[] = "NTLMSSP";
    1.89 +static const char NTLM_TYPE1_MARKER[] = { 0x01, 0x00, 0x00, 0x00 };
    1.90 +static const char NTLM_TYPE2_MARKER[] = { 0x02, 0x00, 0x00, 0x00 };
    1.91 +static const char NTLM_TYPE3_MARKER[] = { 0x03, 0x00, 0x00, 0x00 };
    1.92 +
    1.93 +#define NTLM_TYPE1_HEADER_LEN 32
    1.94 +#define NTLM_TYPE2_HEADER_LEN 32
    1.95 +#define NTLM_TYPE3_HEADER_LEN 64
    1.96 +
    1.97 +#define LM_HASH_LEN 16
    1.98 +#define LM_RESP_LEN 24
    1.99 +
   1.100 +#define NTLM_HASH_LEN 16
   1.101 +#define NTLM_RESP_LEN 24
   1.102 +
   1.103 +//-----------------------------------------------------------------------------
   1.104 +
   1.105 +static bool sendLM = false;
   1.106 +
   1.107 +/*static*/ void
   1.108 +nsNTLMAuthModule::SetSendLM(bool newSendLM)
   1.109 +{
   1.110 +  sendLM = newSendLM;
   1.111 +}
   1.112 +
   1.113 +//-----------------------------------------------------------------------------
   1.114 +
   1.115 +#ifdef PR_LOGGING
   1.116 +
   1.117 +/**
   1.118 + * Prints a description of flags to the NSPR Log, if enabled.
   1.119 + */
   1.120 +static void LogFlags(uint32_t flags)
   1.121 +{
   1.122 +  if (!LOG_ENABLED())
   1.123 +    return;
   1.124 +#define TEST(_flag) \
   1.125 +  if (flags & NTLM_ ## _flag) \
   1.126 +    PR_LogPrint("    0x%08x (" # _flag ")\n", NTLM_ ## _flag)
   1.127 +
   1.128 +  TEST(NegotiateUnicode);
   1.129 +  TEST(NegotiateOEM);
   1.130 +  TEST(RequestTarget);
   1.131 +  TEST(Unknown1);
   1.132 +  TEST(NegotiateSign);
   1.133 +  TEST(NegotiateSeal);
   1.134 +  TEST(NegotiateDatagramStyle);
   1.135 +  TEST(NegotiateLanManagerKey);
   1.136 +  TEST(NegotiateNetware);
   1.137 +  TEST(NegotiateNTLMKey);
   1.138 +  TEST(Unknown2);
   1.139 +  TEST(Unknown3);
   1.140 +  TEST(NegotiateDomainSupplied);
   1.141 +  TEST(NegotiateWorkstationSupplied);
   1.142 +  TEST(NegotiateLocalCall);
   1.143 +  TEST(NegotiateAlwaysSign);
   1.144 +  TEST(TargetTypeDomain);
   1.145 +  TEST(TargetTypeServer);
   1.146 +  TEST(TargetTypeShare);
   1.147 +  TEST(NegotiateNTLM2Key);
   1.148 +  TEST(RequestInitResponse);
   1.149 +  TEST(RequestAcceptResponse);
   1.150 +  TEST(RequestNonNTSessionKey);
   1.151 +  TEST(NegotiateTargetInfo);
   1.152 +  TEST(Unknown4);
   1.153 +  TEST(Unknown5);
   1.154 +  TEST(Unknown6);
   1.155 +  TEST(Unknown7);
   1.156 +  TEST(Unknown8);
   1.157 +  TEST(Negotiate128);
   1.158 +  TEST(NegotiateKeyExchange);
   1.159 +  TEST(Negotiate56);
   1.160 +
   1.161 +#undef TEST
   1.162 +}
   1.163 +
   1.164 +/**
   1.165 + * Prints a hexdump of buf to the NSPR Log, if enabled.
   1.166 + * @param tag Description of the data, will be printed in front of the data
   1.167 + * @param buf the data to print
   1.168 + * @param bufLen length of the data
   1.169 + */
   1.170 +static void
   1.171 +LogBuf(const char *tag, const uint8_t *buf, uint32_t bufLen)
   1.172 +{
   1.173 +  int i;
   1.174 +
   1.175 +  if (!LOG_ENABLED())
   1.176 +    return;
   1.177 +
   1.178 +  PR_LogPrint("%s =\n", tag);
   1.179 +  char line[80];
   1.180 +  while (bufLen > 0)
   1.181 +  {
   1.182 +    int count = bufLen;
   1.183 +    if (count > 8)
   1.184 +      count = 8;
   1.185 +
   1.186 +    strcpy(line, "    ");
   1.187 +    for (i=0; i<count; ++i)
   1.188 +    {
   1.189 +      int len = strlen(line);
   1.190 +      PR_snprintf(line + len, sizeof(line) - len, "0x%02x ", int(buf[i]));
   1.191 +    }
   1.192 +    for (; i<8; ++i)
   1.193 +    {
   1.194 +      int len = strlen(line);
   1.195 +      PR_snprintf(line + len, sizeof(line) - len, "     ");
   1.196 +    }
   1.197 +
   1.198 +    int len = strlen(line);
   1.199 +    PR_snprintf(line + len, sizeof(line) - len, "   ");
   1.200 +    for (i=0; i<count; ++i)
   1.201 +    {
   1.202 +      len = strlen(line);
   1.203 +      if (isprint(buf[i]))
   1.204 +        PR_snprintf(line + len, sizeof(line) - len, "%c", buf[i]);
   1.205 +      else
   1.206 +        PR_snprintf(line + len, sizeof(line) - len, ".");
   1.207 +    }
   1.208 +    PR_LogPrint("%s\n", line);
   1.209 +
   1.210 +    bufLen -= count;
   1.211 +    buf += count;
   1.212 +  }
   1.213 +}
   1.214 +
   1.215 +#include "plbase64.h"
   1.216 +#include "prmem.h"
   1.217 +/**
   1.218 + * Print base64-encoded token to the NSPR Log.
   1.219 + * @param name Description of the token, will be printed in front
   1.220 + * @param token The token to print
   1.221 + * @param tokenLen length of the data in token
   1.222 + */
   1.223 +static void LogToken(const char *name, const void *token, uint32_t tokenLen)
   1.224 +{
   1.225 +  if (!LOG_ENABLED())
   1.226 +    return;
   1.227 +
   1.228 +  char *b64data = PL_Base64Encode((const char *) token, tokenLen, nullptr);
   1.229 +  if (b64data)
   1.230 +  {
   1.231 +    PR_LogPrint("%s: %s\n", name, b64data);
   1.232 +    PR_Free(b64data);
   1.233 +  }
   1.234 +}
   1.235 +
   1.236 +#else
   1.237 +#define LogFlags(x)
   1.238 +#define LogBuf(a,b,c)
   1.239 +#define LogToken(a,b,c)
   1.240 +
   1.241 +#endif // PR_LOGGING
   1.242 +
   1.243 +//-----------------------------------------------------------------------------
   1.244 +
   1.245 +// byte order swapping
   1.246 +#define SWAP16(x) ((((x) & 0xff) << 8) | (((x) >> 8) & 0xff))
   1.247 +#define SWAP32(x) ((SWAP16((x) & 0xffff) << 16) | (SWAP16((x) >> 16)))
   1.248 +
   1.249 +static void *
   1.250 +WriteBytes(void *buf, const void *data, uint32_t dataLen)
   1.251 +{
   1.252 +  memcpy(buf, data, dataLen);
   1.253 +  return (uint8_t *) buf + dataLen;
   1.254 +}
   1.255 +
   1.256 +static void *
   1.257 +WriteDWORD(void *buf, uint32_t dword)
   1.258 +{
   1.259 +#ifdef IS_BIG_ENDIAN 
   1.260 +  // NTLM uses little endian on the wire
   1.261 +  dword = SWAP32(dword);
   1.262 +#endif
   1.263 +  return WriteBytes(buf, &dword, sizeof(dword));
   1.264 +}
   1.265 +
   1.266 +static void *
   1.267 +WriteSecBuf(void *buf, uint16_t length, uint32_t offset)
   1.268 +{
   1.269 +#ifdef IS_BIG_ENDIAN
   1.270 +  length = SWAP16(length);
   1.271 +  offset = SWAP32(offset);
   1.272 +#endif
   1.273 +  buf = WriteBytes(buf, &length, sizeof(length));
   1.274 +  buf = WriteBytes(buf, &length, sizeof(length));
   1.275 +  buf = WriteBytes(buf, &offset, sizeof(offset));
   1.276 +  return buf;
   1.277 +}
   1.278 +
   1.279 +#ifdef IS_BIG_ENDIAN
   1.280 +/**
   1.281 + * WriteUnicodeLE copies a unicode string from one buffer to another.  The
   1.282 + * resulting unicode string is in little-endian format.  The input string is
   1.283 + * assumed to be in the native endianness of the local machine.  It is safe
   1.284 + * to pass the same buffer as both input and output, which is a handy way to
   1.285 + * convert the unicode buffer to little-endian on big-endian platforms.
   1.286 + */
   1.287 +static void *
   1.288 +WriteUnicodeLE(void *buf, const char16_t *str, uint32_t strLen)
   1.289 +{
   1.290 +  // convert input string from BE to LE
   1.291 +  uint8_t *cursor = (uint8_t *) buf,
   1.292 +          *input  = (uint8_t *) str;
   1.293 +  for (uint32_t i=0; i<strLen; ++i, input+=2, cursor+=2)
   1.294 +  {
   1.295 +    // allow for the case where |buf == str|
   1.296 +    uint8_t temp = input[0];
   1.297 +    cursor[0] = input[1];
   1.298 +    cursor[1] = temp;
   1.299 +  }
   1.300 +  return buf;
   1.301 +}
   1.302 +#endif
   1.303 +
   1.304 +static uint16_t
   1.305 +ReadUint16(const uint8_t *&buf)
   1.306 +{
   1.307 +  uint16_t x = ((uint16_t) buf[0]) | ((uint16_t) buf[1] << 8);
   1.308 +  buf += sizeof(x);
   1.309 +  return x;
   1.310 +}
   1.311 +
   1.312 +static uint32_t
   1.313 +ReadUint32(const uint8_t *&buf)
   1.314 +{
   1.315 +  uint32_t x = ( (uint32_t) buf[0])        |
   1.316 +               (((uint32_t) buf[1]) << 8)  |
   1.317 +               (((uint32_t) buf[2]) << 16) |
   1.318 +               (((uint32_t) buf[3]) << 24);
   1.319 +  buf += sizeof(x);
   1.320 +  return x;
   1.321 +}
   1.322 +
   1.323 +//-----------------------------------------------------------------------------
   1.324 +
   1.325 +static void
   1.326 +ZapBuf(void *buf, size_t bufLen)
   1.327 +{
   1.328 +  memset(buf, 0, bufLen);
   1.329 +}
   1.330 +
   1.331 +static void
   1.332 +ZapString(nsCString &s)
   1.333 +{
   1.334 +  ZapBuf(s.BeginWriting(), s.Length());
   1.335 +}
   1.336 +
   1.337 +static void
   1.338 +ZapString(nsString &s)
   1.339 +{
   1.340 +  ZapBuf(s.BeginWriting(), s.Length() * 2);
   1.341 +}
   1.342 +
   1.343 +static const unsigned char LM_MAGIC[] = "KGS!@#$%";
   1.344 +
   1.345 +/**
   1.346 + * LM_Hash computes the LM hash of the given password.
   1.347 + *
   1.348 + * @param password
   1.349 + *        null-terminated unicode password.
   1.350 + * @param hash
   1.351 + *        16-byte result buffer
   1.352 + */
   1.353 +static void
   1.354 +LM_Hash(const nsString &password, unsigned char *hash)
   1.355 +{
   1.356 +  // convert password to OEM character set.  we'll just use the native
   1.357 +  // filesystem charset.
   1.358 +  nsAutoCString passbuf;
   1.359 +  NS_CopyUnicodeToNative(password, passbuf);
   1.360 +  ToUpperCase(passbuf);
   1.361 +  uint32_t n = passbuf.Length();
   1.362 +  passbuf.SetLength(14);
   1.363 +  for (uint32_t i=n; i<14; ++i)
   1.364 +    passbuf.SetCharAt('\0', i);
   1.365 +
   1.366 +  unsigned char k1[8], k2[8];
   1.367 +  des_makekey((const unsigned char *) passbuf.get()    , k1);
   1.368 +  des_makekey((const unsigned char *) passbuf.get() + 7, k2);
   1.369 +  ZapString(passbuf);
   1.370 +
   1.371 +  // use password keys to hash LM magic string twice.
   1.372 +  des_encrypt(k1, LM_MAGIC, hash);
   1.373 +  des_encrypt(k2, LM_MAGIC, hash + 8);
   1.374 +}
   1.375 +
   1.376 +/**
   1.377 + * NTLM_Hash computes the NTLM hash of the given password.
   1.378 + *
   1.379 + * @param password
   1.380 + *        null-terminated unicode password.
   1.381 + * @param hash
   1.382 + *        16-byte result buffer
   1.383 + */
   1.384 +static void
   1.385 +NTLM_Hash(const nsString &password, unsigned char *hash)
   1.386 +{
   1.387 +  uint32_t len = password.Length();
   1.388 +  uint8_t *passbuf;
   1.389 +  
   1.390 +#ifdef IS_BIG_ENDIAN
   1.391 +  passbuf = (uint8_t *) malloc(len * 2);
   1.392 +  WriteUnicodeLE(passbuf, password.get(), len);
   1.393 +#else
   1.394 +  passbuf = (uint8_t *) password.get();
   1.395 +#endif
   1.396 +
   1.397 +  md4sum(passbuf, len * 2, hash);
   1.398 +
   1.399 +#ifdef IS_BIG_ENDIAN
   1.400 +  ZapBuf(passbuf, len * 2);
   1.401 +  free(passbuf);
   1.402 +#endif
   1.403 +}
   1.404 +
   1.405 +//-----------------------------------------------------------------------------
   1.406 +
   1.407 +/** 
   1.408 + * LM_Response generates the LM response given a 16-byte password hash and the
   1.409 + * challenge from the Type-2 message.
   1.410 + *
   1.411 + * @param hash
   1.412 + *        16-byte password hash
   1.413 + * @param challenge
   1.414 + *        8-byte challenge from Type-2 message
   1.415 + * @param response
   1.416 + *        24-byte buffer to contain the LM response upon return
   1.417 + */
   1.418 +static void
   1.419 +LM_Response(const uint8_t *hash, const uint8_t *challenge, uint8_t *response)
   1.420 +{
   1.421 +  uint8_t keybytes[21], k1[8], k2[8], k3[8];
   1.422 +
   1.423 +  memcpy(keybytes, hash, 16);
   1.424 +  ZapBuf(keybytes + 16, 5);
   1.425 +
   1.426 +  des_makekey(keybytes     , k1);
   1.427 +  des_makekey(keybytes +  7, k2);
   1.428 +  des_makekey(keybytes + 14, k3);
   1.429 +
   1.430 +  des_encrypt(k1, challenge, response);
   1.431 +  des_encrypt(k2, challenge, response + 8);
   1.432 +  des_encrypt(k3, challenge, response + 16);
   1.433 +}
   1.434 +
   1.435 +//-----------------------------------------------------------------------------
   1.436 +
   1.437 +static nsresult
   1.438 +GenerateType1Msg(void **outBuf, uint32_t *outLen)
   1.439 +{
   1.440 +  //
   1.441 +  // verify that bufLen is sufficient
   1.442 +  //
   1.443 +  *outLen = NTLM_TYPE1_HEADER_LEN;
   1.444 +  *outBuf = nsMemory::Alloc(*outLen);
   1.445 +  if (!*outBuf)
   1.446 +    return NS_ERROR_OUT_OF_MEMORY;
   1.447 +
   1.448 +  //
   1.449 +  // write out type 1 msg
   1.450 +  //
   1.451 +  void *cursor = *outBuf;
   1.452 +
   1.453 +  // 0 : signature
   1.454 +  cursor = WriteBytes(cursor, NTLM_SIGNATURE, sizeof(NTLM_SIGNATURE));
   1.455 +
   1.456 +  // 8 : marker
   1.457 +  cursor = WriteBytes(cursor, NTLM_TYPE1_MARKER, sizeof(NTLM_TYPE1_MARKER));
   1.458 +
   1.459 +  // 12 : flags
   1.460 +  cursor = WriteDWORD(cursor, NTLM_TYPE1_FLAGS);
   1.461 +
   1.462 +  //
   1.463 +  // NOTE: it is common for the domain and workstation fields to be empty.
   1.464 +  //       this is true of Win2k clients, and my guess is that there is
   1.465 +  //       little utility to sending these strings before the charset has
   1.466 +  //       been negotiated.  we follow suite -- anyways, it doesn't hurt
   1.467 +  //       to save some bytes on the wire ;-)
   1.468 +  //
   1.469 +
   1.470 +  // 16 : supplied domain security buffer (empty)
   1.471 +  cursor = WriteSecBuf(cursor, 0, 0);
   1.472 +
   1.473 +  // 24 : supplied workstation security buffer (empty)
   1.474 +  cursor = WriteSecBuf(cursor, 0, 0);
   1.475 +
   1.476 +  return NS_OK;
   1.477 +}
   1.478 +
   1.479 +struct Type2Msg
   1.480 +{
   1.481 +  uint32_t    flags;         // NTLM_Xxx bitwise combination
   1.482 +  uint8_t     challenge[8];  // 8 byte challenge
   1.483 +  const void *target;        // target string (type depends on flags)
   1.484 +  uint32_t    targetLen;     // target length in bytes
   1.485 +};
   1.486 +
   1.487 +static nsresult
   1.488 +ParseType2Msg(const void *inBuf, uint32_t inLen, Type2Msg *msg)
   1.489 +{
   1.490 +  // make sure inBuf is long enough to contain a meaningful type2 msg.
   1.491 +  //
   1.492 +  // 0  NTLMSSP Signature
   1.493 +  // 8  NTLM Message Type
   1.494 +  // 12 Target Name
   1.495 +  // 20 Flags
   1.496 +  // 24 Challenge
   1.497 +  // 32 end of header, start of optional data blocks
   1.498 +  //
   1.499 +  if (inLen < NTLM_TYPE2_HEADER_LEN)
   1.500 +    return NS_ERROR_UNEXPECTED;
   1.501 +
   1.502 +  const uint8_t *cursor = (const uint8_t *) inBuf;
   1.503 +
   1.504 +  // verify NTLMSSP signature
   1.505 +  if (memcmp(cursor, NTLM_SIGNATURE, sizeof(NTLM_SIGNATURE)) != 0)
   1.506 +    return NS_ERROR_UNEXPECTED;
   1.507 +
   1.508 +  cursor += sizeof(NTLM_SIGNATURE);
   1.509 +
   1.510 +  // verify Type-2 marker
   1.511 +  if (memcmp(cursor, NTLM_TYPE2_MARKER, sizeof(NTLM_TYPE2_MARKER)) != 0)
   1.512 +    return NS_ERROR_UNEXPECTED;
   1.513 +
   1.514 +  cursor += sizeof(NTLM_TYPE2_MARKER);
   1.515 +
   1.516 +  // Read target name security buffer: ...
   1.517 +  // ... read target length.
   1.518 +  uint32_t targetLen = ReadUint16(cursor);
   1.519 +  // ... skip next 16-bit "allocated space" value.
   1.520 +  ReadUint16(cursor);
   1.521 +  // ... read offset from inBuf.
   1.522 +  uint32_t offset = ReadUint32(cursor);
   1.523 +  // Check the offset / length combo is in range of the input buffer, including
   1.524 +  // integer overflow checking.
   1.525 +  if (MOZ_LIKELY(offset < offset + targetLen && offset + targetLen <= inLen)) {
   1.526 +    msg->targetLen = targetLen;
   1.527 +    msg->target = ((const uint8_t *) inBuf) + offset;
   1.528 +  }
   1.529 +  else
   1.530 +  {
   1.531 +    // Do not error out, for (conservative) backward compatibility.
   1.532 +    msg->targetLen = 0;
   1.533 +    msg->target = nullptr;
   1.534 +  }
   1.535 +
   1.536 +  // read flags
   1.537 +  msg->flags = ReadUint32(cursor);
   1.538 +
   1.539 +  // read challenge
   1.540 +  memcpy(msg->challenge, cursor, sizeof(msg->challenge));
   1.541 +  cursor += sizeof(msg->challenge);
   1.542 +
   1.543 +
   1.544 +  LOG(("NTLM type 2 message:\n"));
   1.545 +  LogBuf("target", (const uint8_t *) msg->target, msg->targetLen);
   1.546 +  LogBuf("flags", (const uint8_t *) &msg->flags, 4);
   1.547 +  LogFlags(msg->flags);
   1.548 +  LogBuf("challenge", msg->challenge, sizeof(msg->challenge));
   1.549 +
   1.550 +  // we currently do not implement LMv2/NTLMv2 or NTLM2 responses,
   1.551 +  // so we can ignore target information.  we may want to enable
   1.552 +  // support for these alternate mechanisms in the future.
   1.553 +  return NS_OK;
   1.554 +}
   1.555 +
   1.556 +static nsresult
   1.557 +GenerateType3Msg(const nsString &domain,
   1.558 +                 const nsString &username,
   1.559 +                 const nsString &password,
   1.560 +                 const void     *inBuf,
   1.561 +                 uint32_t        inLen,
   1.562 +                 void          **outBuf,
   1.563 +                 uint32_t       *outLen)
   1.564 +{
   1.565 +  // inBuf contains Type-2 msg (the challenge) from server
   1.566 +
   1.567 +  nsresult rv;
   1.568 +  Type2Msg msg;
   1.569 +
   1.570 +  rv = ParseType2Msg(inBuf, inLen, &msg);
   1.571 +  if (NS_FAILED(rv))
   1.572 +    return rv;
   1.573 +
   1.574 +  bool unicode = (msg.flags & NTLM_NegotiateUnicode);
   1.575 +
   1.576 +  // temporary buffers for unicode strings
   1.577 +#ifdef IS_BIG_ENDIAN
   1.578 +  nsAutoString ucsDomainBuf, ucsUserBuf;
   1.579 +#endif
   1.580 +  nsAutoString ucsHostBuf; 
   1.581 +  // temporary buffers for oem strings
   1.582 +  nsAutoCString oemDomainBuf, oemUserBuf, oemHostBuf;
   1.583 +  // pointers and lengths for the string buffers; encoding is unicode if
   1.584 +  // the "negotiate unicode" flag was set in the Type-2 message.
   1.585 +  const void *domainPtr, *userPtr, *hostPtr;
   1.586 +  uint32_t domainLen, userLen, hostLen;
   1.587 +
   1.588 +  //
   1.589 +  // get domain name
   1.590 +  //
   1.591 +  if (unicode)
   1.592 +  {
   1.593 +#ifdef IS_BIG_ENDIAN
   1.594 +    ucsDomainBuf = domain;
   1.595 +    domainPtr = ucsDomainBuf.get();
   1.596 +    domainLen = ucsDomainBuf.Length() * 2;
   1.597 +    WriteUnicodeLE((void *) domainPtr, (const char16_t *) domainPtr,
   1.598 +                   ucsDomainBuf.Length());
   1.599 +#else
   1.600 +    domainPtr = domain.get();
   1.601 +    domainLen = domain.Length() * 2;
   1.602 +#endif
   1.603 +  }
   1.604 +  else
   1.605 +  {
   1.606 +    NS_CopyUnicodeToNative(domain, oemDomainBuf);
   1.607 +    domainPtr = oemDomainBuf.get();
   1.608 +    domainLen = oemDomainBuf.Length();
   1.609 +  }
   1.610 +
   1.611 +  //
   1.612 +  // get user name
   1.613 +  //
   1.614 +  if (unicode)
   1.615 +  {
   1.616 +#ifdef IS_BIG_ENDIAN
   1.617 +    ucsUserBuf = username;
   1.618 +    userPtr = ucsUserBuf.get();
   1.619 +    userLen = ucsUserBuf.Length() * 2;
   1.620 +    WriteUnicodeLE((void *) userPtr, (const char16_t *) userPtr,
   1.621 +                   ucsUserBuf.Length());
   1.622 +#else
   1.623 +    userPtr = username.get();
   1.624 +    userLen = username.Length() * 2;
   1.625 +#endif
   1.626 +  }
   1.627 +  else
   1.628 +  {
   1.629 +    NS_CopyUnicodeToNative(username, oemUserBuf);
   1.630 +    userPtr = oemUserBuf.get();
   1.631 +    userLen = oemUserBuf.Length();
   1.632 +  }
   1.633 +
   1.634 +  //
   1.635 +  // get workstation name (use local machine's hostname)
   1.636 +  //
   1.637 +  char hostBuf[SYS_INFO_BUFFER_LENGTH];
   1.638 +  if (PR_GetSystemInfo(PR_SI_HOSTNAME, hostBuf, sizeof(hostBuf)) == PR_FAILURE)
   1.639 +    return NS_ERROR_UNEXPECTED;
   1.640 +  hostLen = strlen(hostBuf);
   1.641 +  if (unicode)
   1.642 +  {
   1.643 +    // hostname is ASCII, so we can do a simple zero-pad expansion:
   1.644 +    CopyASCIItoUTF16(nsDependentCString(hostBuf, hostLen), ucsHostBuf);
   1.645 +    hostPtr = ucsHostBuf.get();
   1.646 +    hostLen = ucsHostBuf.Length() * 2;
   1.647 +#ifdef IS_BIG_ENDIAN
   1.648 +    WriteUnicodeLE((void *) hostPtr, (const char16_t *) hostPtr,
   1.649 +                   ucsHostBuf.Length());
   1.650 +#endif
   1.651 +  }
   1.652 +  else
   1.653 +    hostPtr = hostBuf;
   1.654 +
   1.655 +  //
   1.656 +  // now that we have generated all of the strings, we can allocate outBuf.
   1.657 +  //
   1.658 +  *outLen = NTLM_TYPE3_HEADER_LEN + hostLen + domainLen + userLen +
   1.659 +            LM_RESP_LEN + NTLM_RESP_LEN;
   1.660 +  *outBuf = nsMemory::Alloc(*outLen);
   1.661 +  if (!*outBuf)
   1.662 +    return NS_ERROR_OUT_OF_MEMORY;
   1.663 +
   1.664 +  //
   1.665 +  // next, we compute the LM and NTLM responses.
   1.666 +  //
   1.667 +  uint8_t lmResp[LM_RESP_LEN], ntlmResp[NTLM_RESP_LEN], ntlmHash[NTLM_HASH_LEN];
   1.668 +  if (msg.flags & NTLM_NegotiateNTLM2Key)
   1.669 +  {
   1.670 +    // compute NTLM2 session response
   1.671 +    uint8_t sessionHash[16], temp[16];
   1.672 +
   1.673 +    PK11_GenerateRandom(lmResp, 8);
   1.674 +    memset(lmResp + 8, 0, LM_RESP_LEN - 8);
   1.675 +
   1.676 +    memcpy(temp, msg.challenge, 8);
   1.677 +    memcpy(temp + 8, lmResp, 8);
   1.678 +    md5sum(temp, 16, sessionHash);
   1.679 +
   1.680 +    NTLM_Hash(password, ntlmHash);
   1.681 +    LM_Response(ntlmHash, sessionHash, ntlmResp);
   1.682 +  }
   1.683 +  else
   1.684 +  {
   1.685 +    NTLM_Hash(password, ntlmHash);
   1.686 +    LM_Response(ntlmHash, msg.challenge, ntlmResp);
   1.687 +
   1.688 +    if (sendLM)
   1.689 +    {
   1.690 +      uint8_t lmHash[LM_HASH_LEN];
   1.691 +      LM_Hash(password, lmHash);
   1.692 +      LM_Response(lmHash, msg.challenge, lmResp);
   1.693 +    }
   1.694 +    else
   1.695 +    {
   1.696 +      // According to http://davenport.sourceforge.net/ntlm.html#ntlmVersion2,
   1.697 +      // the correct way to not send the LM hash is to send the NTLM hash twice
   1.698 +      // in both the LM and NTLM response fields.
   1.699 +      LM_Response(ntlmHash, msg.challenge, lmResp);
   1.700 +    }
   1.701 +  }
   1.702 +
   1.703 +  //
   1.704 +  // finally, we assemble the Type-3 msg :-)
   1.705 +  //
   1.706 +  void *cursor = *outBuf;
   1.707 +  uint32_t offset;
   1.708 +
   1.709 +  // 0 : signature
   1.710 +  cursor = WriteBytes(cursor, NTLM_SIGNATURE, sizeof(NTLM_SIGNATURE));
   1.711 +
   1.712 +  // 8 : marker
   1.713 +  cursor = WriteBytes(cursor, NTLM_TYPE3_MARKER, sizeof(NTLM_TYPE3_MARKER));
   1.714 +
   1.715 +  // 12 : LM response sec buf
   1.716 +  offset = NTLM_TYPE3_HEADER_LEN + domainLen + userLen + hostLen;
   1.717 +  cursor = WriteSecBuf(cursor, LM_RESP_LEN, offset);
   1.718 +  memcpy((uint8_t *) *outBuf + offset, lmResp, LM_RESP_LEN);
   1.719 +
   1.720 +  // 20 : NTLM response sec buf
   1.721 +  offset += LM_RESP_LEN;
   1.722 +  cursor = WriteSecBuf(cursor, NTLM_RESP_LEN, offset);
   1.723 +  memcpy((uint8_t *) *outBuf + offset, ntlmResp, NTLM_RESP_LEN);
   1.724 +
   1.725 +  // 28 : domain name sec buf
   1.726 +  offset = NTLM_TYPE3_HEADER_LEN;
   1.727 +  cursor = WriteSecBuf(cursor, domainLen, offset);
   1.728 +  memcpy((uint8_t *) *outBuf + offset, domainPtr, domainLen);
   1.729 +
   1.730 +  // 36 : user name sec buf
   1.731 +  offset += domainLen;
   1.732 +  cursor = WriteSecBuf(cursor, userLen, offset);
   1.733 +  memcpy((uint8_t *) *outBuf + offset, userPtr, userLen);
   1.734 +
   1.735 +  // 44 : workstation (host) name sec buf
   1.736 +  offset += userLen;
   1.737 +  cursor = WriteSecBuf(cursor, hostLen, offset);
   1.738 +  memcpy((uint8_t *) *outBuf + offset, hostPtr, hostLen);
   1.739 +
   1.740 +  // 52 : session key sec buf (not used)
   1.741 +  cursor = WriteSecBuf(cursor, 0, 0);
   1.742 +
   1.743 +  // 60 : negotiated flags
   1.744 +  cursor = WriteDWORD(cursor, msg.flags & NTLM_TYPE1_FLAGS);
   1.745 +
   1.746 +  return NS_OK;
   1.747 +}
   1.748 +
   1.749 +//-----------------------------------------------------------------------------
   1.750 +
   1.751 +NS_IMPL_ISUPPORTS(nsNTLMAuthModule, nsIAuthModule)
   1.752 +
   1.753 +nsNTLMAuthModule::~nsNTLMAuthModule()
   1.754 +{
   1.755 +  ZapString(mPassword);
   1.756 +}
   1.757 +
   1.758 +nsresult
   1.759 +nsNTLMAuthModule::InitTest()
   1.760 +{
   1.761 +  nsNSSShutDownPreventionLock locker;
   1.762 +  //
   1.763 +  // disable NTLM authentication when FIPS mode is enabled.
   1.764 +  //
   1.765 +  return PK11_IsFIPS() ? NS_ERROR_NOT_AVAILABLE : NS_OK;
   1.766 +}
   1.767 +
   1.768 +NS_IMETHODIMP
   1.769 +nsNTLMAuthModule::Init(const char      *serviceName,
   1.770 +                       uint32_t         serviceFlags,
   1.771 +                       const char16_t *domain,
   1.772 +                       const char16_t *username,
   1.773 +                       const char16_t *password)
   1.774 +{
   1.775 +  NS_ASSERTION((serviceFlags & ~nsIAuthModule::REQ_PROXY_AUTH) == nsIAuthModule::REQ_DEFAULT,
   1.776 +      "unexpected service flags");
   1.777 +
   1.778 +  mDomain = domain;
   1.779 +  mUsername = username;
   1.780 +  mPassword = password;
   1.781 +
   1.782 +  static bool sTelemetrySent = false;
   1.783 +  if (!sTelemetrySent) {
   1.784 +      mozilla::Telemetry::Accumulate(
   1.785 +          mozilla::Telemetry::NTLM_MODULE_USED_2,
   1.786 +          serviceFlags & nsIAuthModule::REQ_PROXY_AUTH
   1.787 +              ? NTLM_MODULE_GENERIC_PROXY
   1.788 +              : NTLM_MODULE_GENERIC_DIRECT);
   1.789 +      sTelemetrySent = true;
   1.790 +  }
   1.791 +
   1.792 +  return NS_OK;
   1.793 +}
   1.794 +
   1.795 +NS_IMETHODIMP
   1.796 +nsNTLMAuthModule::GetNextToken(const void *inToken,
   1.797 +                               uint32_t    inTokenLen,
   1.798 +                               void      **outToken,
   1.799 +                               uint32_t   *outTokenLen)
   1.800 +{
   1.801 +  nsresult rv;
   1.802 +  nsNSSShutDownPreventionLock locker;
   1.803 +  //
   1.804 +  // disable NTLM authentication when FIPS mode is enabled.
   1.805 +  //
   1.806 +  if (PK11_IsFIPS())
   1.807 +    return NS_ERROR_NOT_AVAILABLE;
   1.808 +
   1.809 +  // if inToken is non-null, then assume it contains a type 2 message...
   1.810 +  if (inToken)
   1.811 +  {
   1.812 +    LogToken("in-token", inToken, inTokenLen);
   1.813 +    rv = GenerateType3Msg(mDomain, mUsername, mPassword, inToken,
   1.814 +                          inTokenLen, outToken, outTokenLen);
   1.815 +  }
   1.816 +  else
   1.817 +  {
   1.818 +    rv = GenerateType1Msg(outToken, outTokenLen);
   1.819 +  }
   1.820 +
   1.821 +#ifdef PR_LOGGING
   1.822 +  if (NS_SUCCEEDED(rv))
   1.823 +    LogToken("out-token", *outToken, *outTokenLen);
   1.824 +#endif
   1.825 +
   1.826 +  return rv;
   1.827 +}
   1.828 +
   1.829 +NS_IMETHODIMP
   1.830 +nsNTLMAuthModule::Unwrap(const void *inToken,
   1.831 +                        uint32_t    inTokenLen,
   1.832 +                        void      **outToken,
   1.833 +                        uint32_t   *outTokenLen)
   1.834 +{
   1.835 +  return NS_ERROR_NOT_IMPLEMENTED;
   1.836 +}
   1.837 +
   1.838 +NS_IMETHODIMP
   1.839 +nsNTLMAuthModule::Wrap(const void *inToken,
   1.840 +                       uint32_t    inTokenLen,
   1.841 +                       bool        confidential,
   1.842 +                       void      **outToken,
   1.843 +                       uint32_t   *outTokenLen)
   1.844 +{
   1.845 +  return NS_ERROR_NOT_IMPLEMENTED;
   1.846 +}
   1.847 +
   1.848 +//-----------------------------------------------------------------------------
   1.849 +// DES support code
   1.850 +
   1.851 +// set odd parity bit (in least significant bit position)
   1.852 +static uint8_t
   1.853 +des_setkeyparity(uint8_t x)
   1.854 +{
   1.855 +  if ((((x >> 7) ^ (x >> 6) ^ (x >> 5) ^
   1.856 +        (x >> 4) ^ (x >> 3) ^ (x >> 2) ^
   1.857 +        (x >> 1)) & 0x01) == 0)
   1.858 +    x |= 0x01;
   1.859 +  else
   1.860 +    x &= 0xfe;
   1.861 +  return x;
   1.862 +}
   1.863 +
   1.864 +// build 64-bit des key from 56-bit raw key
   1.865 +static void
   1.866 +des_makekey(const uint8_t *raw, uint8_t *key)
   1.867 +{
   1.868 +  key[0] = des_setkeyparity(raw[0]);
   1.869 +  key[1] = des_setkeyparity((raw[0] << 7) | (raw[1] >> 1));
   1.870 +  key[2] = des_setkeyparity((raw[1] << 6) | (raw[2] >> 2));
   1.871 +  key[3] = des_setkeyparity((raw[2] << 5) | (raw[3] >> 3));
   1.872 +  key[4] = des_setkeyparity((raw[3] << 4) | (raw[4] >> 4));
   1.873 +  key[5] = des_setkeyparity((raw[4] << 3) | (raw[5] >> 5));
   1.874 +  key[6] = des_setkeyparity((raw[5] << 2) | (raw[6] >> 6));
   1.875 +  key[7] = des_setkeyparity((raw[6] << 1));
   1.876 +}
   1.877 +
   1.878 +// run des encryption algorithm (using NSS)
   1.879 +static void
   1.880 +des_encrypt(const uint8_t *key, const uint8_t *src, uint8_t *hash)
   1.881 +{
   1.882 +  CK_MECHANISM_TYPE cipherMech = CKM_DES_ECB;
   1.883 +  PK11SlotInfo *slot = nullptr;
   1.884 +  PK11SymKey *symkey = nullptr;
   1.885 +  PK11Context *ctxt = nullptr;
   1.886 +  SECItem keyItem, *param = nullptr;
   1.887 +  SECStatus rv;
   1.888 +  unsigned int n;
   1.889 +  
   1.890 +  slot = PK11_GetBestSlot(cipherMech, nullptr);
   1.891 +  if (!slot)
   1.892 +  {
   1.893 +    NS_ERROR("no slot");
   1.894 +    goto done;
   1.895 +  }
   1.896 +
   1.897 +  keyItem.data = (uint8_t *) key;
   1.898 +  keyItem.len = 8;
   1.899 +  symkey = PK11_ImportSymKey(slot, cipherMech,
   1.900 +                             PK11_OriginUnwrap, CKA_ENCRYPT,
   1.901 +                             &keyItem, nullptr);
   1.902 +  if (!symkey)
   1.903 +  {
   1.904 +    NS_ERROR("no symkey");
   1.905 +    goto done;
   1.906 +  }
   1.907 +
   1.908 +  // no initialization vector required
   1.909 +  param = PK11_ParamFromIV(cipherMech, nullptr);
   1.910 +  if (!param)
   1.911 +  {
   1.912 +    NS_ERROR("no param");
   1.913 +    goto done;
   1.914 +  }
   1.915 +
   1.916 +  ctxt = PK11_CreateContextBySymKey(cipherMech, CKA_ENCRYPT,
   1.917 +                                    symkey, param);
   1.918 +  if (!ctxt)
   1.919 +  {
   1.920 +    NS_ERROR("no context");
   1.921 +    goto done;
   1.922 +  }
   1.923 +
   1.924 +  rv = PK11_CipherOp(ctxt, hash, (int *) &n, 8, (uint8_t *) src, 8);
   1.925 +  if (rv != SECSuccess)
   1.926 +  {
   1.927 +    NS_ERROR("des failure");
   1.928 +    goto done;
   1.929 +  }
   1.930 +
   1.931 +  rv = PK11_DigestFinal(ctxt, hash+8, &n, 0);
   1.932 +  if (rv != SECSuccess)
   1.933 +  {
   1.934 +    NS_ERROR("des failure");
   1.935 +    goto done;
   1.936 +  }
   1.937 +
   1.938 +done:
   1.939 +  if (ctxt)
   1.940 +    PK11_DestroyContext(ctxt, true);
   1.941 +  if (symkey)
   1.942 +    PK11_FreeSymKey(symkey);
   1.943 +  if (param)
   1.944 +    SECITEM_FreeItem(param, true);
   1.945 +  if (slot)
   1.946 +    PK11_FreeSlot(slot);
   1.947 +}
   1.948 +
   1.949 +//-----------------------------------------------------------------------------
   1.950 +// MD5 support code
   1.951 +
   1.952 +static void md5sum(const uint8_t *input, uint32_t inputLen, uint8_t *result)
   1.953 +{
   1.954 +  PK11Context *ctxt = PK11_CreateDigestContext(SEC_OID_MD5);
   1.955 +  if (ctxt)
   1.956 +  {
   1.957 +    if (PK11_DigestBegin(ctxt) == SECSuccess)
   1.958 +    {
   1.959 +      if (PK11_DigestOp(ctxt, input, inputLen) == SECSuccess)
   1.960 +      {
   1.961 +        uint32_t resultLen = 16;
   1.962 +        PK11_DigestFinal(ctxt, result, &resultLen, resultLen);
   1.963 +      }
   1.964 +    }
   1.965 +    PK11_DestroyContext(ctxt, true);
   1.966 +  }
   1.967 +}

mercurial