security/manager/ssl/src/nsNTLMAuthModule.cpp

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

mercurial