2 * Copyright (c) 2000-2004,2006-2008,2010-2014 Apple Inc. All Rights Reserved.
4 * @APPLE_LICENSE_HEADER_START@
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
21 * @APPLE_LICENSE_HEADER_END@
25 * ntlmBlobPriv.c - Private routines used by NtlmGenerator module.
28 #include "ntlmBlobPriv.h"
29 #include <Security/SecBase.h>
31 #include <sys/types.h>
34 #include <sys/param.h>
41 #include <CommonCrypto/CommonDigest.h>
42 #include <CommonCrypto/CommonCryptor.h>
43 #include <CommonCrypto/CommonHMAC.h>
44 #include <CoreFoundation/CFDate.h>
45 #include <Security/SecFramework.h>
46 #include <Security/SecRandom.h>
47 #include <utilities/SecCFWrappers.h>
49 #if DEBUG_FIXED_CHALLENGE
50 /* Fixed 64-bit timestamp for sourceforge test vectors */
51 static unsigned char dbgStamp
[] =
53 0x00, 0x90, 0xd3, 0x36, 0xb7, 0x34, 0xc3, 0x01
55 #endif /* DEBUG_FIXED_CHALLENGE */
58 // MARK: Encode/Decode Routines
60 /* write a 64-bit word, little endian */
67 OSWriteLittleInt64(cb
, 0, word
);
68 CFDataAppendBytes(buf
, cb
, 8);
70 /* This is an alternate implementation which may or may not be faster than
72 CFIndex offset
= CFDataGetLength(buf
);
73 UInt8
*bytes
= CFDataGetMutableBytePtr(buf
);
74 CFDataIncreaseLength(buf
, 8);
75 OSWriteLittleInt64(bytes
, offset
, word
);
79 /* write a 32-bit word, little endian */
86 OSWriteLittleInt32(cb
, 0, word
);
87 CFDataAppendBytes(buf
, cb
, 4);
89 /* This is an alternate implementation which may or may not be faster than
91 CFIndex offset
= CFDataGetLength(buf
);
92 UInt8
*bytes
= CFDataGetMutableBytePtr(buf
);
93 CFDataIncreaseLength(buf
, 4);
94 OSWriteLittleInt32(bytes
, offset
, word
);
98 /* write a 16-bit word, little endian */
100 CFMutableDataRef buf
,
104 OSWriteLittleInt16(cb
, 0, word
);
105 CFDataAppendBytes(buf
, cb
, 2);
109 * Write a security buffer, providing the index into the CFData at which
110 * this security buffer's offset is located. Just before the actual data is written,
111 * go back and update the offset with the start of that data using secBufOffset().
114 CFMutableDataRef buf
,
116 CFIndex
*offsetIndex
)
120 OSWriteLittleInt16(cb
, 0, len
); /* buffer length */
121 OSWriteLittleInt16(cb
, 2, len
); /* buffer allocated size */
122 OSWriteLittleInt32(cb
, 4, 0); /* offset is empty for now */
123 CFDataAppendBytes(buf
, cb
, 8);
124 *offsetIndex
= CFDataGetLength(buf
) - 4; /* offset will go here */
126 appendUint16(buf
, len
); /* buffer length */
127 appendUint16(buf
, len
); /* buffer allocated size */
128 *offsetIndex
= CFDataGetLength(buf
); /* offset will go here */
129 appendUint32(buf
, 0); /* but it's empty for now */
134 * Update a security buffer's offset to be the current end of data in a CFData.
137 CFMutableDataRef buf
,
138 CFIndex offsetIndex
) /* obtained from appendSecBuf() */
140 CFIndex currPos
= CFDataGetLength(buf
);
142 OSWriteLittleInt32(cb
, 0, (uint32_t)currPos
);
143 CFRange range
= {offsetIndex
, 4};
144 CFDataReplaceBytes(buf
, range
, cb
, 4);
148 * Parse/validate a security buffer. Verifies that supplied offset/length don't go
149 * past end of avaialble data. Returns ptr to actual data and its length. Returns
150 * NTLM_ERR_PARSE_ERR on bogus values.
152 OSStatus
ntlmParseSecBuffer(
153 const unsigned char *cp
, /* start of security buffer */
154 const unsigned char *bufStart
, /* start of whole msg buffer */
155 unsigned bufLen
, /* # of valid bytes starting at bufStart */
156 const unsigned char **data
, /* RETURNED, start of actual data */
157 uint16_t *dataLen
) /* RETURNED, length of actual data */
159 assert(cp
>= bufStart
);
161 uint16_t secBufLen
= OSReadLittleInt16(cp
, 0);
162 /* skip length we just parsed plus alloc size, which we don't use */
164 uint32_t offset
= OSReadLittleInt32(cp
, 0);
165 if((offset
+ secBufLen
) > bufLen
) {
166 dprintf("ntlmParseSecBuffer: buf overflow\n");
167 return NTLM_ERR_PARSE_ERR
;
169 *data
= bufStart
+ offset
;
170 *dataLen
= secBufLen
;
171 return errSecSuccess
;
175 // MARK: CFString Converters
178 * Convert CFString to little-endian unicode.
182 unsigned char **ucode
, // mallocd and RETURNED
183 unsigned *ucodeLen
) // RETURNED
185 CFIndex len
= CFStringGetLength(pwd
);
186 unsigned char *data
= (unsigned char *)malloc(len
* 2);
187 unsigned char *cp
= data
;
190 for(dex
=0; dex
<len
; dex
++) {
191 UniChar uc
= CFStringGetCharacterAtIndex(pwd
, dex
);
196 *ucodeLen
= (unsigned)(len
* 2);
200 * Convert a CFStringRef into a mallocd array of chars suitable for the specified
201 * encoding. This might return an error if the string can't be converted
204 OSStatus
ntlmStringFlatten(
207 unsigned char **flat
, // mallocd and RETURNED
208 unsigned *flatLen
) // RETURNED
211 /* convert to little-endian unicode */
212 ntlmStringToLE(str
, flat
, flatLen
);
213 return errSecSuccess
;
216 /* convert to ASCII C string */
217 CFIndex strLen
= CFStringGetLength(str
);
218 char *cStr
= (char *)malloc(strLen
+ 1);
220 return errSecAllocate
;
222 if(CFStringGetCString(str
, cStr
, strLen
+ 1, kCFStringEncodingASCII
)) {
223 *flat
= (unsigned char *)cStr
;
224 *flatLen
= (unsigned)strLen
;
225 return errSecSuccess
;
229 * Well that didn't work. Try UTF8 - I don't know how a MS would behave if
230 * this portion of auth (only used for the LM response) didn't work.
232 dprintf("lmPasswordHash: ASCII password conversion failed; trying UTF8\n");
234 cStr
= (char *)malloc(strLen
* 4);
236 return errSecAllocate
;
238 CFDataRef dataFromCString
= CFStringCreateExternalRepresentation(NULL
, str
, kCFStringEncodingUTF8
, 0);
239 if(dataFromCString
) {
240 *flat
= (unsigned char *)cStr
;
241 *flatLen
= (unsigned)strLen
;
242 CFReleaseNull(dataFromCString
);
243 return errSecSuccess
;
245 dprintf("lmPasswordHash: UTF8 password conversion failed\n");
247 CFReleaseNull(dataFromCString
);
248 return NTLM_ERR_PARSE_ERR
;
253 // MARK: Machine Dependent Cruft
255 /* random number generator */
258 void *buf
) /* allocated by caller, random data RETURNED */
260 SecRandomCopyBytes(kSecRandomDefault
, len
, buf
);
263 /* Obtain host name in appropriate encoding */
264 OSStatus
ntlmHostName(
266 unsigned char **flat
, // mallocd and RETURNED
267 unsigned *flatLen
) // RETURNED
269 char hostname
[MAXHOSTNAMELEN
];
270 if(gethostname(hostname
, MAXHOSTNAMELEN
)) {
272 perror("gethostname");
274 return errSecInternalComponent
;
276 size_t len
= strlen(hostname
);
278 /* quickie "little endian unicode" conversion */
279 *flat
= (unsigned char *)malloc(len
* 2);
280 unsigned char *cp
= *flat
;
282 for(dex
=0; dex
<len
; dex
++) {
283 *cp
++ = hostname
[dex
];
286 *flatLen
= (unsigned)len
* 2;
287 return errSecSuccess
;
290 *flat
= (unsigned char *)malloc(len
);
291 *flatLen
= (unsigned)len
;
292 memmove(*flat
, hostname
, len
);
293 return errSecSuccess
;
298 * Append 64-bit little-endiam timestamp to a CFData. Time is relative to
299 * January 1 1601, in tenths of a microsecond.
302 CFGiblisGetSingleton(CFAbsoluteTime
, ntlmGetBasis
, ntlmBasisAbsoluteTime
, ^{
303 *ntlmBasisAbsoluteTime
= CFAbsoluteTimeForGregorianZuluDay(1601, 1, 1);
306 void ntlmAppendTimestamp(
307 CFMutableDataRef ntlmV2Blob
)
309 #if DEBUG_FIXED_CHALLENGE
310 /* Fixed 64-bit timestamp for sourceforge test vectors */
311 CFDataAppendBytes(ntlmV2Blob
, dbgStamp
, 8);
314 CFAbsoluteTime nowTime
= CFAbsoluteTimeGetCurrent();
316 /* elapsed := time in seconds since basis */
317 CFTimeInterval elapsed
= nowTime
- ntlmGetBasis();
318 /* now in tenths of microseconds */
319 elapsed
*= 10000000.0;
321 appendUint64(ntlmV2Blob
, (uint64_t)elapsed
);
328 /* MD4 and MD5 hash */
329 #define NTLM_DIGEST_LENGTH 16
331 const unsigned char *data
,
333 unsigned char *digest
) // caller-supplied, NTLM_DIGEST_LENGTH */
337 CC_MD4_Update(&ctx
, data
, dataLen
);
338 CC_MD4_Final(digest
, &ctx
);
342 const unsigned char *data
,
344 unsigned char *digest
) // caller-supplied, NTLM_DIGEST_LENGTH */
348 CC_MD5_Update(&ctx
, data
, dataLen
);
349 CC_MD5_Final(digest
, &ctx
);
353 * Given 7 bytes, create 8-byte DES key. Our implementation ignores the
354 * parity bit (lsb), which simplifies this somewhat.
357 const unsigned char *inKey
, // 7 bytes
358 unsigned char *outKey
) // 8 bytes
360 outKey
[0] = inKey
[0] & 0xfe;
361 outKey
[1] = ((inKey
[0] << 7) | (inKey
[1] >> 1)) & 0xfe;
362 outKey
[2] = ((inKey
[1] << 6) | (inKey
[2] >> 2)) & 0xfe;
363 outKey
[3] = ((inKey
[2] << 5) | (inKey
[3] >> 3)) & 0xfe;
364 outKey
[4] = ((inKey
[3] << 4) | (inKey
[4] >> 4)) & 0xfe;
365 outKey
[5] = ((inKey
[4] << 3) | (inKey
[5] >> 5)) & 0xfe;
366 outKey
[6] = ((inKey
[5] << 2) | (inKey
[6] >> 6)) & 0xfe;
367 outKey
[7] = (inKey
[6] << 1) & 0xfe;
371 * single block DES encrypt.
372 * This would really benefit from a DES implementation in CommonCrypto.
374 OSStatus
ntlmDesCrypt(
375 const unsigned char *key
, // 8 bytes
376 const unsigned char *inData
, // 8 bytes
377 unsigned char *outData
) // 8 bytes
380 return CCCrypt(kCCEncrypt
, kCCAlgorithmDES
, 0, key
, kCCKeySizeDES
,
381 NULL
/*no iv, 1 block*/, inData
, 1 * kCCBlockSizeDES
, outData
,
382 1 * kCCBlockSizeDES
, &data_moved
);
388 OSStatus
ntlmHmacMD5(
389 const unsigned char *key
,
391 const unsigned char *inData
,
393 unsigned char *mac
) // caller provided, NTLM_DIGEST_LENGTH
395 CCHmacContext hmac_md5_context
;
397 CCHmacInit(&hmac_md5_context
, kCCHmacAlgMD5
, key
, keyLen
);
398 CCHmacUpdate(&hmac_md5_context
, inData
, inDataLen
);
399 CCHmacFinal(&hmac_md5_context
, mac
);
405 // MARK: LM and NTLM password and digest munging
408 * Calculate LM-style password hash. This really only works if the password
409 * is convertible to ASCII (that is, it will indeed return an error if that
412 * This is the most gawdawful constant I've ever seen in security-related code.
414 static const unsigned char lmHashPlaintext
[] = {'K', 'G', 'S', '!', '@', '#', '$', '%'};
416 OSStatus
lmPasswordHash(
418 unsigned char *digest
) // caller-supplied, NTLM_DIGEST_LENGTH
420 /* convert to ASCII */
424 ortn
= ntlmStringFlatten(pwd
, false, &cStr
, &strLen
);
426 dprintf("lmPasswordHash: ASCII password conversion failed\n");
430 /* truncate/pad to 14 bytes and convert to upper case */
431 unsigned char pwdFix
[NTLM_LM_PASSWORD_LEN
];
432 unsigned toMove
= NTLM_LM_PASSWORD_LEN
;
433 if(strLen
< NTLM_LM_PASSWORD_LEN
) {
436 memmove(pwdFix
, cStr
, toMove
);
439 for(dex
=0; dex
<NTLM_LM_PASSWORD_LEN
; dex
++) {
440 pwdFix
[dex
] = toupper(pwdFix
[dex
]);
443 /* two DES keys - raw material 7 bytes, munge to 8 bytes */
444 unsigned char desKey1
[DES_KEY_SIZE
], desKey2
[DES_KEY_SIZE
];
445 ntlmMakeDesKey(pwdFix
, desKey1
);
446 ntlmMakeDesKey(pwdFix
+ DES_RAW_KEY_SIZE
, desKey2
);
448 /* use each of those keys to encrypt the magic string */
449 ortn
= ntlmDesCrypt(desKey1
, lmHashPlaintext
, digest
);
450 if(ortn
== errSecSuccess
) {
451 ortn
= ntlmDesCrypt(desKey2
, lmHashPlaintext
, digest
+ DES_BLOCK_SIZE
);
457 * Calculate NTLM password hash (MD4 on a unicode password).
459 void ntlmPasswordHash(
461 unsigned char *digest
) // caller-supplied, NTLM_DIGEST_LENGTH
466 /* convert to little-endian unicode */
467 ntlmStringToLE(pwd
, &data
, &len
);
468 /* md4 hash of that */
469 md4Hash(data
, len
, digest
);
474 * NTLM response: DES encrypt the challenge (or session hash) with three
475 * different keys derived from the password hash. Result is concatenation
476 * of three DES encrypts.
478 #define ALL_KEYS_LENGTH (3 * DES_RAW_KEY_SIZE)
479 OSStatus
ntlmResponse(
480 const unsigned char *digest
, // NTLM_DIGEST_LENGTH bytes
481 const unsigned char *ptext
, // challenge or session hash
482 unsigned char *ntlmResp
) // caller-supplied NTLM_LM_RESPONSE_LEN
484 unsigned char allKeys
[ALL_KEYS_LENGTH
];
485 unsigned char key1
[DES_KEY_SIZE
], key2
[DES_KEY_SIZE
], key3
[DES_KEY_SIZE
];
488 memmove(allKeys
, digest
, NTLM_DIGEST_LENGTH
);
489 memset(allKeys
+ NTLM_DIGEST_LENGTH
, 0, ALL_KEYS_LENGTH
- NTLM_DIGEST_LENGTH
);
490 ntlmMakeDesKey(allKeys
, key1
);
491 ntlmMakeDesKey(allKeys
+ DES_RAW_KEY_SIZE
, key2
);
492 ntlmMakeDesKey(allKeys
+ (2 * DES_RAW_KEY_SIZE
), key3
);
493 ortn
= ntlmDesCrypt(key1
, ptext
, ntlmResp
);
494 if(ortn
== errSecSuccess
) {
495 ortn
= ntlmDesCrypt(key2
, ptext
, ntlmResp
+ DES_BLOCK_SIZE
);
497 if(ortn
== errSecSuccess
) {
498 ortn
= ntlmDesCrypt(key3
, ptext
, ntlmResp
+ (2 * DES_BLOCK_SIZE
));