1/* 2 * Copyright (c) 2000-2004,2006-2007,2011,2013 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 * NtlmGenerator.c - NTLM client-side authentication engine. 26 * 27 * In the usual absence of documentation from Microsoft, the "inventors" of this 28 * protocol, this module was written using the superb revers engineering documented 29 * at 30 * 31 * http://davenport.sourceforge.net/ntlm.html#localAuthentication 32 */ 33 34#include "NtlmGenerator.h" 35#include "ntlmBlobPriv.h" 36#include <Security/SecBase.h> 37 38#include <stdint.h> 39#include <stdio.h> 40#include <stdlib.h> 41#include <assert.h> 42#include <strings.h> 43 44/* 45 * For debugging using fixed server challenge and client nonce. 46 */ 47#if DEBUG_FIXED_CHALLENGE 48 49/* these are "test vectors", effectively, from sourceforge */ 50/* use pwd SecREt01, host/domain DOMAIN */ 51static const unsigned char fixServerChallenge[8] = 52 { 0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef }; 53static const unsigned char fixClientNonce[8] = 54 { 0xff, 0xff, 0xff, 0x00, 0x11, 0x22, 0x33, 0x44 }; 55 56static const unsigned char fixTargetInfo[] = { 57 0x02, 0x00, 0x0c, 0x00, 0x44, 0x00, 0x4f, 0x00, 58 0x4d, 0x00, 0x41, 0x00, 0x49, 0x00, 0x4e, 0x00, 59 0x01, 0x00, 0x0c, 0x00, 0x53, 0x00, 0x45, 0x00, 60 0x52, 0x00, 0x56, 0x00, 0x45, 0x00, 0x52, 0x00, 61 0x04, 0x00, 0x14, 0x00, 0x64, 0x00, 0x6f, 0x00, 62 0x6d, 0x00, 0x61, 0x00, 0x69, 0x00, 0x6e, 0x00, 63 0x2e, 0x00, 0x63, 0x00, 0x6f, 0x00, 0x6d, 0x00, 64 0x03, 0x00, 0x22, 0x00, 0x73, 0x00, 0x65, 0x00, 65 0x72, 0x00, 0x76, 0x00, 0x65, 0x00, 0x72, 0x00, 66 0x2e, 0x00, 0x64, 0x00, 0x6f, 0x00, 0x6d, 0x00, 67 0x61, 0x00, 0x69, 0x00, 0x6e, 0x00, 0x2e, 0x00, 68 0x63, 0x00, 0x6f, 0x00, 0x6d, 0x00, 0x00, 0x00, 69 0x00, 0x00 70}; 71#endif 72 73/* app's NtlmGeneratorRef is a pointer to one of these */ 74struct NtlmGenerator { 75 NLTM_Which mWhich; 76 NLTM_Which mNegotiatedVersion; 77 uint32_t mSentFlags; /* the flags we sent in first mst */ 78}; 79 80static OSStatus _NtlmGeneratePasswordHashes( 81 CFAllocatorRef alloc, 82 NtlmGeneratorRef ntlm, 83 CFStringRef password, 84 CFDataRef* ntlmHash, 85 CFDataRef* lmHash); 86 87/* 88 * Validate type 2 message sent by the server; return interesting fields. 89 * NOTE we do not deal with the Context field here, which is only used 90 * for local authetication. 91 */ 92static OSStatus ntlmParseServerChallenge( 93 CFDataRef serverBlob, 94 uint32_t *serverFlags, /* RETURNED */ 95 unsigned char *challenge, /* 8 bytes, mallocd by caller, RETURNED */ 96 unsigned char **targetName, /* mallocd and RETURNED */ 97 unsigned *targetNameLen, /* RETURNED */ 98 unsigned char **targetInfo, /* optionally mallocd and RETURNED */ 99 unsigned *targetInfoLen) /* optionally RETURNED */ 100{ 101 unsigned minLength; 102 103 *targetName = NULL; 104 *targetNameLen = 0; 105 *targetInfo = NULL; 106 *targetInfoLen = 0; 107 108 if(serverBlob == NULL) { 109 return NTLM_ERR_PARSE_ERR; 110 } 111 112 minLength = NTLM_SIGNATURE_LEN + 113 (unsigned)sizeof(uint32_t) + /* msg type */ 114 NTLM_SIZEOF_SEC_BUF + /* target name */ 115 (unsigned)sizeof(uint32_t) + /* flags */ 116 NTLM_CHALLENGE_LEN; 117 unsigned bufLen = (unsigned)CFDataGetLength(serverBlob); 118 if(bufLen < minLength) { 119 dprintf("ntlmParseServerChallenge: bad length\n"); 120 return NTLM_ERR_PARSE_ERR; 121 } 122 123 /* do not even think of touching serverBlob after this */ 124 const unsigned char *cp = CFDataGetBytePtr(serverBlob); 125 126 /* byte 0: signature */ 127 if(memcmp(cp, NTLM_SIGNATURE, NTLM_SIGNATURE_LEN)) { 128 dprintf("ntlmParseServerChallenge: signature mismatch\n"); 129 return NTLM_ERR_PARSE_ERR; 130 } 131 132 const unsigned char *currCp = cp + NTLM_SIGNATURE_LEN; 133 134 /* byte 8: message type */ 135 uint32_t msgType = OSReadLittleInt32(currCp, 0); 136 if(msgType != NTLM_MSG_MARKER_TYPE2) { 137 dprintf("ntlmParseServerChallenge: bad msg type\n"); 138 return NTLM_ERR_PARSE_ERR; 139 } 140 currCp += sizeof(uint32_t); 141 142 /* byte 12: target name, security buffer */ 143 const unsigned char *sbData; 144 uint16_t sbLen; 145 OSStatus ortn = ntlmParseSecBuffer(currCp, cp, bufLen, &sbData, &sbLen); 146 if(ortn) { 147 return ortn; 148 } 149 *targetName = (unsigned char *)malloc(sbLen); 150 *targetNameLen = sbLen; 151 memmove(*targetName, sbData, sbLen); 152 currCp += NTLM_SIZEOF_SEC_BUF; 153 154 /* byte 20: flags */ 155 *serverFlags = OSReadLittleInt32(currCp, 0); 156 currCp += sizeof(uint32_t); 157 158 /* byte 24: challenge */ 159 #if DEBUG_FIXED_CHALLENGE 160 memmove(challenge, fixServerChallenge, NTLM_CHALLENGE_LEN); 161 #else 162 memmove(challenge, currCp, NTLM_CHALLENGE_LEN); 163 #endif 164 currCp += NTLM_CHALLENGE_LEN; 165 166 /* remaining fields optional */ 167 const unsigned char *endOfBuf = cp + bufLen; 168 assert(endOfBuf >= currCp); 169 if(endOfBuf == currCp) { 170 return errSecSuccess; 171 } 172 173 if(endOfBuf < (currCp + NTLM_SIZEOF_SEC_BUF)) { 174 /* not enough left for even one security buf; ignore */ 175 return errSecSuccess; 176 } 177 178 /* byte 32: context: skip */ 179 currCp += NTLM_SIZEOF_SEC_BUF; 180 181 if(endOfBuf < (currCp + NTLM_SIZEOF_SEC_BUF)) { 182 /* not enough left for target info security buf; ignore */ 183 return errSecSuccess; 184 } 185 186 /* byte 40: target info */ 187 ortn = ntlmParseSecBuffer(currCp, cp, bufLen, &sbData, &sbLen); 188 if(ortn) { 189 free(*targetName); 190 *targetName = NULL; 191 return ortn; 192 } 193 #if DEBUG_FIXED_CHALLENGE 194 sbData = fixTargetInfo; 195 sbLen = sizeof(fixTargetInfo); 196 #endif /* DEBUG_FIXED_CHALLENGE */ 197 *targetInfo = (unsigned char *)malloc(sbLen); 198 *targetInfoLen = sbLen; 199 memmove(*targetInfo, sbData, sbLen); 200 return errSecSuccess; 201} 202 203/* 204 * Create NTLMv2 responses (both NTLM and LM). 205 */ 206static OSStatus ntlmGenerateNtlmV2Response( 207 /* from app */ 208 CFStringRef domain, 209 CFStringRef userName, 210 CFDataRef ntlmHash, 211 212 /* from server */ 213 const unsigned char *serverChallenge, 214 const unsigned char *targetInfo, 215 unsigned targetInfoLen, 216 217 /* returned */ 218 unsigned char *lmV2Response, // caller supplied, NTLM_LM_RESPONSE_LEN bytes 219 unsigned char **ntlmv2Response, // mallocd and RETURNED 220 unsigned *ntlmV2ResponseLen) // RETURNED 221{ 222 /* Random challenge used in both responses */ 223 unsigned char challenge[NTLM_CLIENT_NONCE_LEN]; 224 #if DEBUG_FIXED_CHALLENGE 225 memmove(challenge, fixClientNonce, NTLM_CLIENT_NONCE_LEN); 226 #else 227 ntlmRand(NTLM_CLIENT_NONCE_LEN, challenge); 228 #endif 229 230 /* NTLM password hash */ 231 unsigned char ntlmPwdHash[NTLM_DIGEST_LENGTH]; 232// ntlmPasswordHash(password, ntlmPwdHash); 233 memmove(ntlmPwdHash, CFDataGetBytePtr(ntlmHash), sizeof(ntlmPwdHash)); 234 235 /* uppercase(userName | domain) */ 236 CFMutableStringRef userDomain = CFStringCreateMutableCopy(NULL, 0, userName); 237 if(domain != NULL) { 238 CFStringAppend(userDomain, domain); 239 } 240 CFStringUppercase(userDomain, NULL); 241 242 /* declare some locals prior to any gotos */ 243 unsigned char *ucode = NULL; 244 unsigned ucodeLen; 245 unsigned char ntlmV2Hash[NTLM_DIGEST_LENGTH]; 246 unsigned char macText2[NTLM_CHALLENGE_LEN + NTLM_CLIENT_NONCE_LEN]; 247 unsigned char challengeMac[NTLM_DIGEST_LENGTH]; 248 unsigned char blobMac[NTLM_DIGEST_LENGTH]; 249 unsigned char *ntlmv2Resp = NULL; 250 CFMutableDataRef ntlmV2Blob = NULL; 251 CFMutableDataRef catBlob = NULL; 252 unsigned ntlmV2BlobLen; 253 unsigned char blobSig[4] = {0x01, 0x01, 0x00, 0x00}; 254 255 /* HMAC(passwordHash, uppercase(userName | domain)) */ 256 ntlmStringToLE(userDomain, &ucode, &ucodeLen); 257 OSStatus ortn = ntlmHmacMD5(ntlmPwdHash, NTLM_DIGEST_LENGTH, 258 ucode, ucodeLen, ntlmV2Hash); 259 if(ortn) { 260 goto errOut; 261 } 262 263 /* HMAC(ntlmV2Hash, serverChallenge | clientChallenge) */ 264 memmove(macText2, serverChallenge, NTLM_CHALLENGE_LEN); 265 memmove(macText2 + NTLM_CHALLENGE_LEN, challenge, NTLM_CLIENT_NONCE_LEN); 266 ortn = ntlmHmacMD5(ntlmV2Hash, NTLM_DIGEST_LENGTH, 267 macText2, NTLM_CHALLENGE_LEN + NTLM_CLIENT_NONCE_LEN, challengeMac); 268 if(ortn) { 269 goto errOut; 270 } 271 272 /* LMv2 response := challengeMac | clientChallenge */ 273 memmove(lmV2Response, challengeMac, NTLM_DIGEST_LENGTH); 274 memmove(lmV2Response + NTLM_DIGEST_LENGTH, challenge, NTLM_CLIENT_NONCE_LEN); 275 276 /* Prepare the NTLMv2 'blob' */ 277 ntlmV2Blob = CFDataCreateMutable(NULL, 0); 278 279 /* 0: 0x01010000 */ 280 CFDataAppendBytes(ntlmV2Blob, blobSig, 4); 281 /* 4: reserved, zeroes */ 282 appendUint32(ntlmV2Blob, 0); 283 /* 8: Timestamp */ 284 ntlmAppendTimestamp(ntlmV2Blob); 285 /* 16: client challenge */ 286 CFDataAppendBytes(ntlmV2Blob, challenge, NTLM_CLIENT_NONCE_LEN); 287 /* 24: unknown, zeroes */ 288 appendUint32(ntlmV2Blob, 0); 289 /* 28: target info from server */ 290 CFDataAppendBytes(ntlmV2Blob, targetInfo, targetInfoLen); 291 /* *: unknown, zeroes */ 292 appendUint32(ntlmV2Blob, 0); 293 294 /* keep that blob; it'll go directly into the response. Now cook up 295 * another one, the concatentation of the server challenge with the 296 * ntlmV2Blob */ 297 ntlmV2BlobLen = (unsigned)CFDataGetLength(ntlmV2Blob); 298 catBlob = CFDataCreateMutable(NULL, 0); 299 CFDataAppendBytes(catBlob, serverChallenge, NTLM_CHALLENGE_LEN); 300 CFDataAppendBytes(catBlob, CFDataGetBytePtr(ntlmV2Blob), ntlmV2BlobLen); 301 302 /* HMAC(ntlmV2Hash, serverChallenge | blob) */ 303 ortn = ntlmHmacMD5(ntlmV2Hash, NTLM_DIGEST_LENGTH, 304 CFDataGetBytePtr(catBlob), (unsigned)CFDataGetLength(catBlob), 305 blobMac); 306 if(ortn) { 307 goto errOut; 308 } 309 310 /* Finally, NTLMv2 response := (blobMac | ntlmV2Blob) */ 311 ntlmv2Resp = (unsigned char *)malloc(NTLM_DIGEST_LENGTH + ntlmV2BlobLen); 312 memmove(ntlmv2Resp, blobMac, NTLM_DIGEST_LENGTH); 313 memmove(ntlmv2Resp + NTLM_DIGEST_LENGTH, CFDataGetBytePtr(ntlmV2Blob), ntlmV2BlobLen); 314 *ntlmv2Response = ntlmv2Resp; 315 *ntlmV2ResponseLen = NTLM_DIGEST_LENGTH + ntlmV2BlobLen; 316 ortn = errSecSuccess; 317errOut: 318 if(userDomain) { 319 CFRelease(userDomain); 320 } 321 if(ntlmV2Blob) { 322 CFRelease(ntlmV2Blob); 323 } 324 if(catBlob) { 325 CFRelease(catBlob); 326 } 327 CFREE(ucode); 328 return ortn; 329} 330 331/* 332 * Create/release NtlmGenerator objects. 333 */ 334OSStatus NtlmGeneratorCreate( 335 NLTM_Which which, 336 NtlmGeneratorRef *ntlmGen) /* RETURNED */ 337{ 338 struct NtlmGenerator *gen = 339 (struct NtlmGenerator *)malloc(sizeof(struct NtlmGenerator)); 340 if(gen == NULL) { 341 return errSecAllocate; 342 } 343 gen->mWhich = which; 344 gen->mNegotiatedVersion = 0; /* i.e., unknown */ 345 gen->mSentFlags = 0; 346 *ntlmGen = gen; 347 return errSecSuccess; 348} 349 350void NtlmGeneratorRelease( 351 NtlmGeneratorRef ntlmGen) 352{ 353 if(ntlmGen == NULL) { 354 return; 355 } 356 free(ntlmGen); 357} 358 359OSStatus NtlmCreateClientRequest( 360 NtlmGeneratorRef ntlmGen, 361 CFDataRef *clientRequest) /* RETURNED */ 362{ 363 CFMutableDataRef req = CFDataCreateMutable(NULL, 0); 364 if(req == NULL) { 365 return errSecAllocate; 366 } 367 /* byte 0: signature, NULL terminated */ 368 CFDataAppendBytes(req, (UInt8 *)NTLM_SIGNATURE, NTLM_SIGNATURE_LEN); 369 370 /* byte 8: message type */ 371 appendUint32(req, NTLM_MSG_MARKER_TYPE1); 372 373 /* byte 12: the standard flags we send - we're wide open to all types */ 374 /* FIXME isn't there a way to tell the server we support NTLMv2? */ 375 ntlmGen->mSentFlags = NTLM_NegotiateUnicode | 376 NTLM_NegotiateOEM | 377 NTLM_RequestTarget | 378 NTLM_NegotiateNTLM | 379 NTLM_AlwaysSign; 380 if(ntlmGen->mWhich & NW_NTLM2) { 381 ntlmGen->mSentFlags |= NTLM_NegotiateNTLM2Key; 382 } 383 appendUint32(req, ntlmGen->mSentFlags); 384 385 /* byte 16: optional supplied domain: not needed */ 386 CFIndex dex; 387 appendSecBuf(req, 0, &dex); 388 389 /* byte 24: optional supplied workstation: not needed */ 390 appendSecBuf(req, 0, &dex); 391 392 *clientRequest = req; 393 return errSecSuccess; 394} 395 396/* 397 * The meat & potatoes: given a server type 2 message, cook up a type 3 response. 398 */ 399OSStatus NtlmCreateClientResponse( 400 NtlmGeneratorRef ntlmGen, 401 CFDataRef serverBlob, 402 CFStringRef domain, /* optional */ 403 CFStringRef userName, 404 CFStringRef password, 405 CFDataRef *clientResponse) /* RETURNED */ 406{ 407 CFDataRef ntlmHash = NULL; 408 CFDataRef lmHash = NULL; 409 OSStatus result = _NtlmGeneratePasswordHashes(kCFAllocatorDefault, ntlmGen, password, &ntlmHash, &lmHash); 410 411 if (result == errSecSuccess) { 412 413 result = _NtlmCreateClientResponse(ntlmGen, serverBlob, domain, userName, ntlmHash, lmHash, clientResponse); 414 } 415 416 if (ntlmHash) 417 CFRelease(ntlmHash); 418 419 if (lmHash) 420 CFRelease(lmHash); 421 422 return result; 423} 424 425OSStatus _NtlmCreateClientResponse( 426 NtlmGeneratorRef ntlmGen, 427 CFDataRef serverBlob, 428 CFStringRef domain, /* optional */ 429 CFStringRef userName, 430 CFDataRef ntlmHash, 431 CFDataRef lmHash, 432 CFDataRef *clientResponse) /* RETURNED */ 433{ 434 OSStatus ortn; 435 uint32_t serverFlags; 436 unsigned char serverChallenge[NTLM_CHALLENGE_LEN]; 437 unsigned char *targetName = NULL; 438 unsigned targetNameLen = 0; 439 unsigned char *targetInfo = NULL; 440 unsigned targetInfoLen = 0; 441 CFIndex lmRespOffset; 442 unsigned char lmResp[NTLM_LM_RESPONSE_LEN]; 443 CFIndex ntlmRespOffset; 444 unsigned char ntlmResp[NTLM_LM_RESPONSE_LEN]; 445 unsigned char *ntlmResponsePtr = NULL; 446 unsigned ntlmResponseLen = 0; 447 unsigned char *domainNameFlat = NULL; 448 unsigned domainNameFlatLen = 0; 449 CFIndex domainNameOffset; 450 unsigned char *userNameFlat = NULL; 451 unsigned userNameFlatLen = 0; 452 CFIndex userNameOffset; 453 unsigned char *workstationName = NULL; 454 unsigned workstationNameLen = 0; 455 CFIndex workstationNameOffset; 456 CFIndex nullDex; 457 unsigned char pwdHash[NTLM_DIGEST_LENGTH]; 458 459 ortn = ntlmParseServerChallenge(serverBlob, &serverFlags, serverChallenge, 460 &targetName, &targetNameLen, 461 &targetInfo, &targetInfoLen); 462 if(ortn) { 463 return ortn; 464 } 465 /* subsequent errors to errOut: */ 466 467 /* gather negotiated parameters */ 468 bool lm2Key = (serverFlags & NTLM_NegotiateNTLM2Key) ? true : false; 469 bool unicode = (serverFlags & NTLM_NegotiateUnicode) ? true : false; 470 /* any others? */ 471 472 CFMutableDataRef clientBuf = CFDataCreateMutable(NULL, 0); 473 if(clientBuf == NULL) { 474 ortn = errSecAllocate; 475 goto errOut; 476 } 477 478 if (domain) { 479 domain = CFStringCreateMutableCopy(NULL, 0, domain); 480 if (domain) 481 CFStringUppercase((CFMutableStringRef)domain, NULL); 482 else { 483 ortn = errSecAllocate; 484 goto errOut; 485 } 486 } 487 488 /* byte 0: signature, NULL terminated */ 489 CFDataAppendBytes(clientBuf, (UInt8 *)NTLM_SIGNATURE, NTLM_SIGNATURE_LEN); 490 491 /* byte 8: message type */ 492 appendUint32(clientBuf, NTLM_MSG_MARKER_TYPE3); 493 494 /* LM and NTLM responses */ 495 if( (targetInfo != NULL) && // server is NTLMv2 capable 496 (targetInfoLen != 0) && // ditto 497 (serverFlags & NTLM_NegotiateTargetInfo) && // ditto 498 (ntlmGen->mWhich & NW_NTLMv2) ) { // ...and we are 499 /* 500 * NTLMv2 501 */ 502 ortn = ntlmGenerateNtlmV2Response(domain, userName, ntlmHash, 503 serverChallenge, targetInfo, targetInfoLen, 504 lmResp, &ntlmResponsePtr, &ntlmResponseLen); 505 if(ortn) { 506 goto errOut; 507 } 508 509 /* 510 * Write security buffers. 511 * 512 * byte 12: LM response 513 * byte 20: NTLM response 514 */ 515 appendSecBuf(clientBuf, NTLM_LM_RESPONSE_LEN, &lmRespOffset); 516 appendSecBuf(clientBuf, ntlmResponseLen, &ntlmRespOffset); 517 ntlmGen->mNegotiatedVersion = NW_NTLMv2; 518 } 519 else { 520 if(lm2Key && (ntlmGen->mWhich & NW_NTLM2)) { 521 /* LM response: 8 random bytes, rest zeroes */ 522 #if DEBUG_FIXED_CHALLENGE 523 memmove(lmResp, fixClientNonce, NTLM_CLIENT_NONCE_LEN); 524 #else 525 ntlmRand(NTLM_CLIENT_NONCE_LEN, lmResp); 526 #endif 527 memset(lmResp + NTLM_CLIENT_NONCE_LEN, 0, 528 NTLM_LM_RESPONSE_LEN - NTLM_CLIENT_NONCE_LEN); 529 530 /* session nonce: server challenge | client nonce */ 531 unsigned char sessionNonce[NTLM_CHALLENGE_LEN + NTLM_CLIENT_NONCE_LEN]; 532 memmove(sessionNonce, serverChallenge, NTLM_CHALLENGE_LEN); 533 memmove(sessionNonce + NTLM_CHALLENGE_LEN, lmResp, NTLM_CLIENT_NONCE_LEN); 534 535 /* NTLM2 session hash: the first 8 bytes of MD5(sessionNonce) */ 536 unsigned char sessionHash[NTLM_DIGEST_LENGTH]; 537 md5Hash(sessionNonce, NTLM_CHALLENGE_LEN + NTLM_CLIENT_NONCE_LEN, sessionHash); 538 539 /* standard password hash */ 540// ntlmPasswordHash(password, pwdHash); 541 memmove(pwdHash, CFDataGetBytePtr(ntlmHash), sizeof(pwdHash)); 542 543 /* NTLM response: DES with three different keys */ 544 ortn = ntlmResponse(pwdHash, sessionHash, ntlmResp); 545 if(ortn) { 546 dprintf("***Error on ntlmResponse (3)\n"); 547 goto errOut; 548 } 549 ntlmGen->mNegotiatedVersion = NW_NTLM2; 550 } 551 else if(ntlmGen->mWhich & NW_NTLM1) { 552 /* 553 * LM response - the old style 2-DES "password hash" applied 554 * the the server's challenge 555 */ 556// ortn = lmPasswordHash(password, pwdHash); 557// if(ortn) { 558// dprintf("***Error on lmPasswordHash\n"); 559// goto errOut; 560// } 561 memmove(pwdHash, CFDataGetBytePtr(lmHash), sizeof(pwdHash)); 562 563 ortn = ntlmResponse(pwdHash, serverChallenge, lmResp); 564 if(ortn) { 565 dprintf("***Error on ntlmResponse (1)\n"); 566 goto errOut; 567 } 568 569 /* 570 * NTLM response: md4 password hash, DES with three different keys 571 */ 572// ntlmPasswordHash(password, pwdHash); 573 memmove(pwdHash, CFDataGetBytePtr(ntlmHash), sizeof(pwdHash)); 574 575 ortn = ntlmResponse(pwdHash, serverChallenge, ntlmResp); 576 if(ortn) { 577 dprintf("***Error on ntlmResponse (2)\n"); 578 goto errOut; 579 } 580 ntlmGen->mNegotiatedVersion = NW_NTLM1; 581 } 582 else { 583 dprintf("***NTLM protocol mismatch\n"); 584 ortn = NTLM_ERR_PROTOCOL_MISMATCH; 585 goto errOut; 586 587 } 588 589 /* 590 * Write security buffers. 591 * 592 * byte 12: LM response 593 * byte 20: NTLM response 594 */ 595 appendSecBuf(clientBuf, NTLM_LM_RESPONSE_LEN, &lmRespOffset); 596 appendSecBuf(clientBuf, NTLM_LM_RESPONSE_LEN, &ntlmRespOffset); 597 ntlmResponsePtr = ntlmResp; 598 ntlmResponseLen = NTLM_LM_RESPONSE_LEN; 599 } /* not NTLMv2 */ 600 601 /* 602 * convert domain and user as appropriate 603 * byte 28: domain (server) name 604 */ 605 if(domain != NULL) { 606 ortn = ntlmStringFlatten(domain, unicode, &domainNameFlat, &domainNameFlatLen); 607 if(ortn) { 608 dprintf("createClientResponse: error converting domain name\n"); 609 ortn = NTLM_ERR_PARSE_ERR; 610 goto errOut; 611 } 612 } 613 appendSecBuf(clientBuf, domainNameFlatLen, &domainNameOffset); 614 615 /* byte 36: user name */ 616 ortn = ntlmStringFlatten(userName, unicode, &userNameFlat, &userNameFlatLen); 617 if(ortn) { 618 dprintf("createClientResponse: error converting user name\n"); 619 ortn = NTLM_ERR_PARSE_ERR; 620 goto errOut; 621 } 622 appendSecBuf(clientBuf, userNameFlatLen, &userNameOffset); 623 624 /* byte 44: hostname */ 625 ortn = ntlmHostName(unicode, &workstationName, &workstationNameLen); 626 if(ortn) { 627 dprintf("createClientResponse: error getting host name\n"); 628 goto errOut; 629 } 630 appendSecBuf(clientBuf, workstationNameLen, &workstationNameOffset); 631 632 /* byte 52: session key (whatever that is): optional, empty here */ 633 appendSecBuf(clientBuf, 0, &nullDex); 634 635 /* byte 60: negotiated flags */ 636 appendUint32(clientBuf, ntlmGen->mSentFlags & serverFlags); 637 638 /* finally, the data associated with the security buffers */ 639 secBufOffset(clientBuf, lmRespOffset); 640 CFDataAppendBytes(clientBuf, lmResp, NTLM_LM_RESPONSE_LEN); 641 642 secBufOffset(clientBuf, ntlmRespOffset); 643 CFDataAppendBytes(clientBuf, ntlmResponsePtr, ntlmResponseLen); 644 645 if(domain != NULL) { 646 secBufOffset(clientBuf, domainNameOffset); 647 CFDataAppendBytes(clientBuf, domainNameFlat, domainNameFlatLen); 648 } 649 650 secBufOffset(clientBuf, userNameOffset); 651 CFDataAppendBytes(clientBuf, userNameFlat, userNameFlatLen); 652 653 secBufOffset(clientBuf, workstationNameOffset); 654 CFDataAppendBytes(clientBuf, workstationName, workstationNameLen); 655 656errOut: 657 CFREE(targetName); 658 CFREE(targetInfo); 659 CFREE(domainNameFlat); 660 CFREE(userNameFlat); 661 CFREE(workstationName); 662 if (domain) CFRelease(domain); 663 if(ntlmResponsePtr != ntlmResp) { 664 /* i.e., it was mallocd by ntlmGenerateNtlmV2Response */ 665 CFREE(ntlmResponsePtr); 666 } 667 if(ortn == errSecSuccess) { 668 *clientResponse = clientBuf; 669 } 670 else { 671 if (clientBuf) 672 CFRelease(clientBuf); 673 } 674 return ortn; 675} 676 677/* replacement for NtlmNegotiatedNtlm2: returns NW_NTLM1Only, NW_NTLM2Only, 678 * or NW_NTLMv2Only */ 679NLTM_Which NtlmGetNegotiatedVersion( 680 NtlmGeneratorRef ntlmGen) 681{ 682 return ntlmGen->mNegotiatedVersion; 683} 684 685OSStatus _NtlmGeneratePasswordHashes( 686 CFAllocatorRef alloc, 687 NtlmGeneratorRef ntlm, 688 CFStringRef password, 689 CFDataRef* ntlmHash, 690 CFDataRef* lmHash) 691{ 692 OSStatus result = errSecSuccess; 693 unsigned char hash[NTLM_DIGEST_LENGTH]; 694 695 ntlmPasswordHash(password, hash); 696 697 *ntlmHash = CFDataCreate(alloc, hash, sizeof(hash)); 698 699 result = lmPasswordHash(password, hash); 700 701 if (result == errSecSuccess) 702 *lmHash = CFDataCreate(alloc, hash, sizeof(hash)); 703 704 return result; 705} 706 707OSStatus NtlmGeneratePasswordHashes( 708 CFAllocatorRef alloc, 709 CFStringRef password, 710 CFDataRef* ntlmHash, 711 CFDataRef* lmHash) 712{ 713 NtlmGeneratorRef ntlm = NULL; 714 715 OSStatus result = NtlmGeneratorCreate(NW_Any, &ntlm); 716 717 if (result == errSecSuccess) { 718 result = _NtlmGeneratePasswordHashes(alloc, ntlm, password, ntlmHash, lmHash); 719 } 720 721 if (ntlm) 722 NtlmGeneratorRelease(ntlm); 723 724 return result; 725} 726 727