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 <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 */
51static 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 */
61void 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 */
80void 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 */
99void 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 */
113void 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 */
136void 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 */
152OSStatus 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 */
180void ntlmStringToLE(
181	CFStringRef		pwd,
182	unsigned char   **ucode,		// mallocd and RETURNED
183	unsigned		*ucodeLen)		// RETURNED
184{
185	CFIndex len = CFStringGetLength(pwd);
186	unsigned char *data = (unsigned char *)malloc(len * 2);
187	unsigned char *cp = data;
188
189	CFIndex dex;
190	for(dex=0; dex<len; dex++) {
191		UniChar uc = CFStringGetCharacterAtIndex(pwd, dex);
192		*cp++ = uc & 0xff;
193		*cp++ = uc >> 8;
194	}
195	*ucode = data;
196	*ucodeLen = (unsigned)(len * 2);
197}
198
199/*
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
202 * appropriately.
203 */
204OSStatus ntlmStringFlatten(
205	CFStringRef str,
206	bool unicode,
207	unsigned char **flat,			// mallocd and RETURNED
208	unsigned *flatLen)				// RETURNED
209{
210	if(unicode) {
211		/* convert to little-endian unicode */
212		ntlmStringToLE(str, flat, flatLen);
213		return errSecSuccess;
214	}
215	else {
216		/* convert to ASCII C string */
217		CFIndex strLen = CFStringGetLength(str);
218		char *cStr = (char *)malloc(strLen + 1);
219		if(cStr == NULL) {
220			return errSecAllocate;
221		}
222		if(CFStringGetCString(str, cStr, strLen + 1, kCFStringEncodingASCII)) {
223			*flat = (unsigned char *)cStr;
224			*flatLen = (unsigned)strLen;
225			return errSecSuccess;
226		}
227
228		/*
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.
231		 */
232		dprintf("lmPasswordHash: ASCII password conversion failed; trying UTF8\n");
233		free(cStr);
234		cStr = (char *)malloc(strLen * 4);
235		if(cStr == NULL) {
236			return errSecAllocate;
237		}
238		if(CFStringCreateExternalRepresentation(NULL, str, kCFStringEncodingUTF8, 0)) {
239			*flat = (unsigned char *)cStr;
240			*flatLen = (unsigned)strLen;
241			return errSecSuccess;
242		}
243		dprintf("lmPasswordHash: UTF8 password conversion failed\n");
244		free(cStr);
245		return NTLM_ERR_PARSE_ERR;
246	}
247}
248
249// MARK: -
250// MARK: Machine Dependent Cruft
251
252/* random number generator */
253void ntlmRand(
254	unsigned		len,
255	void			*buf)				/* allocated by caller, random data RETURNED */
256{
257	SecRandomCopyBytes(kSecRandomDefault, len, buf);
258}
259
260/* Obtain host name in appropriate encoding */
261OSStatus ntlmHostName(
262	bool unicode,
263	unsigned char **flat,			// mallocd and RETURNED
264	unsigned *flatLen)				// RETURNED
265{
266	char hostname[MAXHOSTNAMELEN];
267	if(gethostname(hostname, MAXHOSTNAMELEN)) {
268		#ifndef NDEBUG
269		perror("gethostname");
270		#endif
271		return errSecInternalComponent;
272	}
273	size_t len = strlen(hostname);
274	if(unicode) {
275		/* quickie "little endian unicode" conversion */
276		*flat = (unsigned char *)malloc(len * 2);
277		unsigned char *cp = *flat;
278		size_t dex;
279		for(dex=0; dex<len; dex++) {
280			*cp++ = hostname[dex];
281			*cp++ = 0;
282		}
283		*flatLen = (unsigned)len * 2;
284		return errSecSuccess;
285	}
286	else {
287		*flat = (unsigned char *)malloc(len);
288		*flatLen = (unsigned)len;
289		memmove(*flat, hostname, len);
290		return errSecSuccess;
291	}
292}
293
294/*
295 * Append 64-bit little-endiam timestamp to a CFData. Time is relative to
296 * January 1 1601, in tenths of a microsecond.
297 */
298
299CFGiblisGetSingleton(CFAbsoluteTime, ntlmGetBasis, ntlmBasisAbsoluteTime, ^{
300    *ntlmBasisAbsoluteTime = CFAbsoluteTimeForGregorianZuluDay(1601, 1, 1);
301});
302
303void ntlmAppendTimestamp(
304	CFMutableDataRef ntlmV2Blob)
305{
306	#if DEBUG_FIXED_CHALLENGE
307	/* Fixed 64-bit timestamp for sourceforge test vectors */
308	CFDataAppendBytes(ntlmV2Blob, dbgStamp, 8);
309	#else
310
311	CFAbsoluteTime nowTime   = CFAbsoluteTimeGetCurrent();
312
313	/* elapsed := time in seconds since basis */
314	CFTimeInterval elapsed = nowTime - ntlmGetBasis();
315	/* now in tenths of microseconds */
316	elapsed *= 10000000.0;
317
318	appendUint64(ntlmV2Blob, (uint64_t)elapsed);
319	#endif
320}
321
322// MARK: -
323// MARK: Crypto
324
325/* MD4 and MD5 hash */
326#define NTLM_DIGEST_LENGTH   16
327void md4Hash(
328	const unsigned char *data,
329	unsigned			dataLen,
330	unsigned char		*digest)		// caller-supplied, NTLM_DIGEST_LENGTH */
331{
332	CC_MD4_CTX ctx;
333	CC_MD4_Init(&ctx);
334	CC_MD4_Update(&ctx, data, dataLen);
335	CC_MD4_Final(digest, &ctx);
336}
337
338void md5Hash(
339	const unsigned char *data,
340	unsigned			dataLen,
341	unsigned char		*digest)		// caller-supplied, NTLM_DIGEST_LENGTH */
342{
343	CC_MD5_CTX ctx;
344	CC_MD5_Init(&ctx);
345	CC_MD5_Update(&ctx, data, dataLen);
346	CC_MD5_Final(digest, &ctx);
347}
348
349/*
350 * Given 7 bytes, create 8-byte DES key. Our implementation ignores the
351 * parity bit (lsb), which simplifies this somewhat.
352 */
353void ntlmMakeDesKey(
354	const unsigned char *inKey,			// 7 bytes
355	unsigned char *outKey)				// 8 bytes
356{
357	outKey[0] =   inKey[0] & 0xfe;
358	outKey[1] = ((inKey[0] << 7) | (inKey[1] >> 1)) & 0xfe;
359	outKey[2] = ((inKey[1] << 6) | (inKey[2] >> 2)) & 0xfe;
360	outKey[3] = ((inKey[2] << 5) | (inKey[3] >> 3)) & 0xfe;
361	outKey[4] = ((inKey[3] << 4) | (inKey[4] >> 4)) & 0xfe;
362	outKey[5] = ((inKey[4] << 3) | (inKey[5] >> 5)) & 0xfe;
363	outKey[6] = ((inKey[5] << 2) | (inKey[6] >> 6)) & 0xfe;
364	outKey[7] =  (inKey[6] << 1) & 0xfe;
365}
366
367/*
368 * single block DES encrypt.
369 * This would really benefit from a DES implementation in CommonCrypto.
370 */
371OSStatus ntlmDesCrypt(
372	const unsigned char *key,		// 8 bytes
373	const unsigned char *inData,	// 8 bytes
374	unsigned char *outData)   // 8 bytes
375{
376    size_t data_moved;
377    return CCCrypt(kCCEncrypt, kCCAlgorithmDES, 0, key, kCCKeySizeDES,
378        NULL /*no iv, 1 block*/, inData, 1 * kCCBlockSizeDES, outData,
379        1 * kCCBlockSizeDES, &data_moved);
380}
381
382/*
383 * HMAC/MD5.
384 */
385OSStatus ntlmHmacMD5(
386	const unsigned char *key,
387	unsigned			keyLen,
388	const unsigned char *inData,
389	unsigned			inDataLen,
390	unsigned char		*mac)		// caller provided, NTLM_DIGEST_LENGTH
391{
392    CCHmacContext hmac_md5_context;
393
394    CCHmacInit(&hmac_md5_context, kCCHmacAlgMD5, key, keyLen);
395    CCHmacUpdate(&hmac_md5_context, inData, inDataLen);
396    CCHmacFinal(&hmac_md5_context, mac);
397
398    return 0;
399}
400
401// MARK: -
402// MARK: LM and NTLM password and digest munging
403
404/*
405 * Calculate LM-style password hash. This really only works if the password
406 * is convertible to ASCII (that is, it will indeed return an error if that
407 * is not true).
408 *
409 * This is the most gawdawful constant I've ever seen in security-related code.
410 */
411static const unsigned char lmHashPlaintext[] = {'K', 'G', 'S', '!', '@', '#', '$', '%'};
412
413OSStatus lmPasswordHash(
414	CFStringRef		pwd,
415	unsigned char   *digest)		// caller-supplied, NTLM_DIGEST_LENGTH
416{
417	/* convert to ASCII */
418	unsigned strLen;
419	unsigned char *cStr;
420	OSStatus ortn;
421	ortn = ntlmStringFlatten(pwd, false, &cStr, &strLen);
422	if(ortn) {
423		dprintf("lmPasswordHash: ASCII password conversion failed\n");
424		return ortn;
425	}
426
427	/* truncate/pad to 14 bytes and convert to upper case */
428	unsigned char pwdFix[NTLM_LM_PASSWORD_LEN];
429	unsigned toMove = NTLM_LM_PASSWORD_LEN;
430	if(strLen < NTLM_LM_PASSWORD_LEN) {
431		toMove = strLen;
432	}
433	memmove(pwdFix, cStr, toMove);
434	free(cStr);
435	unsigned dex;
436	for(dex=0; dex<NTLM_LM_PASSWORD_LEN; dex++) {
437		pwdFix[dex] = toupper(pwdFix[dex]);
438	}
439
440	/* two DES keys - raw material 7 bytes, munge to 8 bytes */
441	unsigned char desKey1[DES_KEY_SIZE], desKey2[DES_KEY_SIZE];
442	ntlmMakeDesKey(pwdFix, desKey1);
443	ntlmMakeDesKey(pwdFix + DES_RAW_KEY_SIZE, desKey2);
444
445	/* use each of those keys to encrypt the magic string */
446	ortn = ntlmDesCrypt(desKey1, lmHashPlaintext, digest);
447	if(ortn == errSecSuccess) {
448		ortn = ntlmDesCrypt(desKey2, lmHashPlaintext, digest + DES_BLOCK_SIZE);
449	}
450	return ortn;
451}
452
453/*
454 * Calculate NTLM password hash (MD4 on a unicode password).
455 */
456void ntlmPasswordHash(
457	CFStringRef		pwd,
458	unsigned char   *digest)		// caller-supplied, NTLM_DIGEST_LENGTH
459{
460	unsigned char *data;
461	unsigned len;
462
463	/* convert to little-endian unicode */
464	ntlmStringToLE(pwd, &data, &len);
465	/* md4 hash of that */
466	md4Hash(data, len, digest);
467	free(data);
468}
469
470/*
471 * NTLM response: DES encrypt the challenge (or session hash) with three
472 * different keys derived from the password hash. Result is concatenation
473 * of three DES encrypts.
474 */
475#define ALL_KEYS_LENGTH (3 * DES_RAW_KEY_SIZE)
476OSStatus ntlmResponse(
477	const unsigned char *digest,		// NTLM_DIGEST_LENGTH bytes
478	const unsigned char *ptext,			// challenge or session hash
479	unsigned char		*ntlmResp)		// caller-supplied NTLM_LM_RESPONSE_LEN
480{
481	unsigned char allKeys[ALL_KEYS_LENGTH];
482	unsigned char key1[DES_KEY_SIZE], key2[DES_KEY_SIZE], key3[DES_KEY_SIZE];
483	OSStatus ortn;
484
485	memmove(allKeys, digest, NTLM_DIGEST_LENGTH);
486	memset(allKeys + NTLM_DIGEST_LENGTH, 0, ALL_KEYS_LENGTH - NTLM_DIGEST_LENGTH);
487	ntlmMakeDesKey(allKeys, key1);
488	ntlmMakeDesKey(allKeys + DES_RAW_KEY_SIZE, key2);
489	ntlmMakeDesKey(allKeys + (2 * DES_RAW_KEY_SIZE), key3);
490	ortn = ntlmDesCrypt(key1, ptext, ntlmResp);
491	if(ortn == errSecSuccess) {
492		ortn = ntlmDesCrypt(key2, ptext, ntlmResp + DES_BLOCK_SIZE);
493	}
494	if(ortn == errSecSuccess) {
495		ortn = ntlmDesCrypt(key3, ptext, ntlmResp + (2 * DES_BLOCK_SIZE));
496	}
497	return ortn;
498}
499
500