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