]> git.saurik.com Git - apple/security.git/blob - ntlm/NtlmGenerator.c
Security-57031.1.35.tar.gz
[apple/security.git] / ntlm / NtlmGenerator.c
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 */
51 static const unsigned char fixServerChallenge[8] =
52 { 0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef };
53 static const unsigned char fixClientNonce[8] =
54 { 0xff, 0xff, 0xff, 0x00, 0x11, 0x22, 0x33, 0x44 };
55
56 static 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 */
74 struct NtlmGenerator {
75 NLTM_Which mWhich;
76 NLTM_Which mNegotiatedVersion;
77 uint32_t mSentFlags; /* the flags we sent in first mst */
78 };
79
80 static 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 */
92 static 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 */
206 static 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;
317 errOut:
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 */
334 OSStatus 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
350 void NtlmGeneratorRelease(
351 NtlmGeneratorRef ntlmGen)
352 {
353 if(ntlmGen == NULL) {
354 return;
355 }
356 free(ntlmGen);
357 }
358
359 OSStatus 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 */
399 OSStatus 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
425 OSStatus _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
656 errOut:
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 */
679 NLTM_Which NtlmGetNegotiatedVersion(
680 NtlmGeneratorRef ntlmGen)
681 {
682 return ntlmGen->mNegotiatedVersion;
683 }
684
685 OSStatus _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
707 OSStatus 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