]> git.saurik.com Git - apple/security.git/blob - ntlm/ntlmBlobPriv.c
Security-59754.80.3.tar.gz
[apple/security.git] / ntlm / ntlmBlobPriv.c
1 /*
2 * Copyright (c) 2000-2004,2006-2008,2010-2014 Apple Inc. All Rights Reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
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
11 * file.
12 *
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.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 */
23
24 /*
25 * ntlmBlobPriv.c - Private routines used by NtlmGenerator module.
26 */
27
28 #include "ntlmBlobPriv.h"
29 #include <Security/SecBase.h>
30
31 #include <sys/types.h>
32 #include <sys/uio.h>
33 #include <unistd.h>
34 #include <sys/param.h>
35 #include <stdlib.h>
36 #include <stdint.h>
37 #include <utilities/simulatecrash_assert.h>
38 #include <fcntl.h>
39 #include <ctype.h>
40 #include <strings.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>
48
49 #if DEBUG_FIXED_CHALLENGE
50 /* Fixed 64-bit timestamp for sourceforge test vectors */
51 static unsigned char dbgStamp[] =
52 {
53 0x00, 0x90, 0xd3, 0x36, 0xb7, 0x34, 0xc3, 0x01
54 };
55 #endif /* DEBUG_FIXED_CHALLENGE */
56
57 // MARK: -
58 // MARK: Encode/Decode Routines
59
60 /* write a 64-bit word, little endian */
61 void appendUint64(
62 CFMutableDataRef buf,
63 uint64_t word)
64 {
65 #if 1
66 unsigned char cb[8];
67 OSWriteLittleInt64(cb, 0, word);
68 CFDataAppendBytes(buf, cb, 8);
69 #else
70 /* This is an alternate implementation which may or may not be faster than
71 the above. */
72 CFIndex offset = CFDataGetLength(buf);
73 UInt8 *bytes = CFDataGetMutableBytePtr(buf);
74 CFDataIncreaseLength(buf, 8);
75 OSWriteLittleInt64(bytes, offset, word);
76 #endif
77 }
78
79 /* write a 32-bit word, little endian */
80 void appendUint32(
81 CFMutableDataRef buf,
82 uint32_t word)
83 {
84 #if 1
85 unsigned char cb[4];
86 OSWriteLittleInt32(cb, 0, word);
87 CFDataAppendBytes(buf, cb, 4);
88 #else
89 /* This is an alternate implementation which may or may not be faster than
90 the above. */
91 CFIndex offset = CFDataGetLength(buf);
92 UInt8 *bytes = CFDataGetMutableBytePtr(buf);
93 CFDataIncreaseLength(buf, 4);
94 OSWriteLittleInt32(bytes, offset, word);
95 #endif
96 }
97
98 /* write a 16-bit word, little endian */
99 void appendUint16(
100 CFMutableDataRef buf,
101 uint16_t word)
102 {
103 unsigned char cb[2];
104 OSWriteLittleInt16(cb, 0, word);
105 CFDataAppendBytes(buf, cb, 2);
106 }
107
108 /*
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().
112 */
113 void appendSecBuf(
114 CFMutableDataRef buf,
115 uint16_t len,
116 CFIndex *offsetIndex)
117 {
118 #if 1
119 unsigned char cb[8];
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 */
125 #else
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 */
130 #endif
131 }
132
133 /*
134 * Update a security buffer's offset to be the current end of data in a CFData.
135 */
136 void secBufOffset(
137 CFMutableDataRef buf,
138 CFIndex offsetIndex) /* obtained from appendSecBuf() */
139 {
140 CFIndex currPos = CFDataGetLength(buf);
141 unsigned char cb[4];
142 OSWriteLittleInt32(cb, 0, (uint32_t)currPos);
143 CFRange range = {offsetIndex, 4};
144 CFDataReplaceBytes(buf, range, cb, 4);
145 }
146
147 /*
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.
151 */
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 */
158 {
159 assert(cp >= bufStart);
160
161 uint16_t secBufLen = OSReadLittleInt16(cp, 0);
162 /* skip length we just parsed plus alloc size, which we don't use */
163 cp += 4;
164 uint32_t offset = OSReadLittleInt32(cp, 0);
165 if((offset + secBufLen) > bufLen) {
166 dprintf("ntlmParseSecBuffer: buf overflow\n");
167 return NTLM_ERR_PARSE_ERR;
168 }
169 *data = bufStart + offset;
170 *dataLen = secBufLen;
171 return errSecSuccess;
172 }
173
174 // MARK: -
175 // MARK: CFString Converters
176
177 /*
178 * Convert CFString to little-endian unicode.
179 */
180 OSStatus ntlmStringToLE(
181 CFStringRef pwd,
182 unsigned char **ucode, // mallocd and RETURNED
183 unsigned *ucodeLen) // RETURNED
184 {
185 CFIndex len = CFStringGetLength(pwd);
186 if (len > NTLM_MAX_STRING_LEN) {
187 return errSecAllocate;
188 }
189 unsigned char *data = (unsigned char *)malloc(len * 2);
190 if (data == NULL) {
191 return errSecAllocate;
192 }
193 unsigned char *cp = data;
194
195 CFIndex dex;
196 for(dex=0; dex<len; dex++) {
197 UniChar uc = CFStringGetCharacterAtIndex(pwd, dex);
198 *cp++ = uc & 0xff;
199 *cp++ = uc >> 8;
200 }
201 *ucode = data;
202 *ucodeLen = (unsigned)(len * 2);
203
204 return errSecSuccess;
205 }
206
207 /*
208 * Convert a CFStringRef into a mallocd array of chars suitable for the specified
209 * encoding. This might return an error if the string can't be converted
210 * appropriately.
211 */
212 OSStatus ntlmStringFlatten(
213 CFStringRef str,
214 bool unicode,
215 unsigned char **flat, // mallocd and RETURNED
216 unsigned *flatLen) // RETURNED
217 {
218 if(unicode) {
219 /* convert to little-endian unicode */
220 return ntlmStringToLE(str, flat, flatLen);
221 }
222 else {
223 /* convert to ASCII C string */
224 CFIndex strLen = CFStringGetLength(str);
225 if (strLen > NTLM_MAX_STRING_LEN)
226 return errSecAllocate;
227
228 char *cStr = (char *)malloc(strLen + 1);
229 if(cStr == NULL) {
230 return errSecAllocate;
231 }
232 if(CFStringGetCString(str, cStr, strLen + 1, kCFStringEncodingASCII)) {
233 *flat = (unsigned char *)cStr;
234 *flatLen = (unsigned)strLen;
235 return errSecSuccess;
236 }
237
238 /*
239 * Well that didn't work. Try UTF8 - I don't know how a MS would behave if
240 * this portion of auth (only used for the LM response) didn't work.
241 */
242 dprintf("ntlmStringFlatten: ASCII password conversion failed; trying UTF8\n");
243 free(cStr);
244
245 CFDataRef dataFromString = CFStringCreateExternalRepresentation(NULL, str, kCFStringEncodingUTF8, 0);
246 if(dataFromString) {
247 *flatLen = (unsigned)CFDataGetLength(dataFromString);
248 *flat = malloc(*flatLen);
249 if (*flat == NULL) {
250 CFRelease(dataFromString);
251 return errSecAllocate;
252 }
253 memcpy(*flat, CFDataGetBytePtr(dataFromString), *flatLen);
254 CFReleaseNull(dataFromString);
255 return errSecSuccess;
256 }
257 dprintf("lmPasswordHash: UTF8 password conversion failed\n");
258 CFReleaseNull(dataFromString);
259 return NTLM_ERR_PARSE_ERR;
260 }
261 }
262
263 // MARK: -
264 // MARK: Machine Dependent Cruft
265
266 /* random number generator */
267 void ntlmRand(
268 unsigned len,
269 void *buf) /* allocated by caller, random data RETURNED */
270 {
271 int status;
272 status=SecRandomCopyBytes(kSecRandomDefault, len, buf);
273 (void)status; // Prevent warning
274 }
275
276 /* Obtain host name in appropriate encoding */
277 OSStatus ntlmHostName(
278 bool unicode,
279 unsigned char **flat, // mallocd and RETURNED
280 unsigned *flatLen) // RETURNED
281 {
282 char hostname[] = "WORKSTATION";
283 size_t len = strlen(hostname);
284 if(unicode) {
285 /* quickie "little endian unicode" conversion */
286 *flat = (unsigned char *)malloc(len * 2);
287 unsigned char *cp = *flat;
288 size_t dex;
289 for(dex=0; dex<len; dex++) {
290 *cp++ = hostname[dex];
291 *cp++ = 0;
292 }
293 *flatLen = (unsigned)len * 2;
294 return errSecSuccess;
295 }
296 else {
297 *flat = (unsigned char *)malloc(len+1);
298 *flatLen = (unsigned)len;
299 memmove(*flat, hostname, len);
300 flat[len] = NULL; // ensure null terminator
301 return errSecSuccess;
302 }
303 }
304
305 /*
306 * Append 64-bit little-endiam timestamp to a CFData. Time is relative to
307 * January 1 1601, in tenths of a microsecond.
308 */
309
310 CFGiblisGetSingleton(CFAbsoluteTime, ntlmGetBasis, ntlmBasisAbsoluteTime, ^{
311 *ntlmBasisAbsoluteTime = CFAbsoluteTimeForGregorianZuluDay(1601, 1, 1);
312 });
313
314 void ntlmAppendTimestamp(
315 CFMutableDataRef ntlmV2Blob)
316 {
317 #if DEBUG_FIXED_CHALLENGE
318 /* Fixed 64-bit timestamp for sourceforge test vectors */
319 CFDataAppendBytes(ntlmV2Blob, dbgStamp, 8);
320 #else
321
322 CFAbsoluteTime nowTime = CFAbsoluteTimeGetCurrent();
323
324 /* elapsed := time in seconds since basis */
325 CFTimeInterval elapsed = nowTime - ntlmGetBasis();
326 /* now in tenths of microseconds */
327 elapsed *= 10000000.0;
328
329 appendUint64(ntlmV2Blob, (uint64_t)elapsed);
330 #endif
331 }
332
333 // MARK: -
334 // MARK: Crypto
335
336 /* MD4 and MD5 hash */
337 #define NTLM_DIGEST_LENGTH 16
338 void md4Hash(
339 const unsigned char *data,
340 unsigned dataLen,
341 unsigned char *digest) // caller-supplied, NTLM_DIGEST_LENGTH */
342 {
343 #pragma clang diagnostic push
344 #pragma clang diagnostic ignored "-Wdeprecated-declarations"
345 CC_MD4_CTX ctx;
346 CC_MD4_Init(&ctx);
347 CC_MD4_Update(&ctx, data, dataLen);
348 CC_MD4_Final(digest, &ctx);
349 #pragma clang diagnostic pop
350 }
351
352 void md5Hash(
353 const unsigned char *data,
354 unsigned dataLen,
355 unsigned char *digest) // caller-supplied, NTLM_DIGEST_LENGTH */
356 {
357 #pragma clang diagnostic push
358 #pragma clang diagnostic ignored "-Wdeprecated-declarations"
359 CC_MD5_CTX ctx;
360 CC_MD5_Init(&ctx);
361 CC_MD5_Update(&ctx, data, dataLen);
362 CC_MD5_Final(digest, &ctx);
363 #pragma clang diagnostic pop
364 }
365
366 /*
367 * Given 7 bytes, create 8-byte DES key. Our implementation ignores the
368 * parity bit (lsb), which simplifies this somewhat.
369 */
370 void ntlmMakeDesKey(
371 const unsigned char *inKey, // 7 bytes
372 unsigned char *outKey) // 8 bytes
373 {
374 outKey[0] = inKey[0] & 0xfe;
375 outKey[1] = ((inKey[0] << 7) | (inKey[1] >> 1)) & 0xfe;
376 outKey[2] = ((inKey[1] << 6) | (inKey[2] >> 2)) & 0xfe;
377 outKey[3] = ((inKey[2] << 5) | (inKey[3] >> 3)) & 0xfe;
378 outKey[4] = ((inKey[3] << 4) | (inKey[4] >> 4)) & 0xfe;
379 outKey[5] = ((inKey[4] << 3) | (inKey[5] >> 5)) & 0xfe;
380 outKey[6] = ((inKey[5] << 2) | (inKey[6] >> 6)) & 0xfe;
381 outKey[7] = (inKey[6] << 1) & 0xfe;
382 }
383
384 /*
385 * single block DES encrypt.
386 * This would really benefit from a DES implementation in CommonCrypto.
387 */
388 OSStatus ntlmDesCrypt(
389 const unsigned char *key, // 8 bytes
390 const unsigned char *inData, // 8 bytes
391 unsigned char *outData) // 8 bytes
392 {
393 size_t data_moved;
394 return CCCrypt(kCCEncrypt, kCCAlgorithmDES, 0, key, kCCKeySizeDES,
395 NULL /*no iv, 1 block*/, inData, 1 * kCCBlockSizeDES, outData,
396 1 * kCCBlockSizeDES, &data_moved);
397 }
398
399 /*
400 * HMAC/MD5.
401 */
402 OSStatus ntlmHmacMD5(
403 const unsigned char *key,
404 unsigned keyLen,
405 const unsigned char *inData,
406 unsigned inDataLen,
407 unsigned char *mac) // caller provided, NTLM_DIGEST_LENGTH
408 {
409 CCHmacContext hmac_md5_context;
410
411 CCHmacInit(&hmac_md5_context, kCCHmacAlgMD5, key, keyLen);
412 CCHmacUpdate(&hmac_md5_context, inData, inDataLen);
413 CCHmacFinal(&hmac_md5_context, mac);
414
415 return errSecSuccess;
416 }
417
418 // MARK: -
419 // MARK: NTLM password and digest munging
420
421
422 /*
423 * Calculate NTLM password hash (MD4 on a unicode password).
424 */
425 OSStatus ntlmPasswordHash(
426 CFStringRef pwd,
427 unsigned char *digest) // caller-supplied, NTLM_DIGEST_LENGTH
428 {
429 OSStatus res;
430 unsigned char *data;
431 unsigned len;
432
433 /* convert to little-endian unicode */
434 res = ntlmStringToLE(pwd, &data, &len);
435 if (res) {
436 return res;
437 }
438 /* md4 hash of that */
439 md4Hash(data, len, digest);
440 free(data);
441
442 return 0;
443 }
444
445 /*
446 * NTLM response: DES encrypt the challenge (or session hash) with three
447 * different keys derived from the password hash. Result is concatenation
448 * of three DES encrypts.
449 */
450 #define ALL_KEYS_LENGTH (3 * DES_RAW_KEY_SIZE)
451 OSStatus lmv2Response(
452 const unsigned char *digest, // NTLM_DIGEST_LENGTH bytes
453 const unsigned char *ptext, // challenge or session hash
454 unsigned char *ntlmResp) // caller-supplied NTLM_LM_RESPONSE_LEN
455 {
456 unsigned char allKeys[ALL_KEYS_LENGTH];
457 unsigned char key1[DES_KEY_SIZE], key2[DES_KEY_SIZE], key3[DES_KEY_SIZE];
458 OSStatus ortn;
459
460 memmove(allKeys, digest, NTLM_DIGEST_LENGTH);
461 memset(allKeys + NTLM_DIGEST_LENGTH, 0, ALL_KEYS_LENGTH - NTLM_DIGEST_LENGTH);
462 ntlmMakeDesKey(allKeys, key1);
463 ntlmMakeDesKey(allKeys + DES_RAW_KEY_SIZE, key2);
464 ntlmMakeDesKey(allKeys + (2 * DES_RAW_KEY_SIZE), key3);
465 ortn = ntlmDesCrypt(key1, ptext, ntlmResp);
466 if(ortn == errSecSuccess) {
467 ortn = ntlmDesCrypt(key2, ptext, ntlmResp + DES_BLOCK_SIZE);
468 }
469 if(ortn == errSecSuccess) {
470 ortn = ntlmDesCrypt(key3, ptext, ntlmResp + (2 * DES_BLOCK_SIZE));
471 }
472 return ortn;
473 }
474