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 +}