]> git.saurik.com Git - apple/security.git/blob - ntlm/NtlmGenerator.c
Security-59754.80.3.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 <security_utilities/simulatecrash_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 /* LMv2 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 = lmv2Response(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 {
552 dprintf("***NTLM protocol mismatch\n");
553 ortn = NTLM_ERR_PROTOCOL_MISMATCH;
554 goto errOut;
555
556 }
557
558 /*
559 * Write security buffers.
560 *
561 * byte 12: LM response
562 * byte 20: NTLM response
563 */
564 appendSecBuf(clientBuf, NTLM_LM_RESPONSE_LEN, &lmRespOffset);
565 appendSecBuf(clientBuf, NTLM_LM_RESPONSE_LEN, &ntlmRespOffset);
566 ntlmResponsePtr = ntlmResp;
567 ntlmResponseLen = NTLM_LM_RESPONSE_LEN;
568 } /* not NTLMv2 */
569
570 /*
571 * convert domain and user as appropriate
572 * byte 28: domain (server) name
573 */
574 if(domain != NULL) {
575 ortn = ntlmStringFlatten(domain, unicode, &domainNameFlat, &domainNameFlatLen);
576 if(ortn) {
577 dprintf("createClientResponse: error converting domain name\n");
578 ortn = NTLM_ERR_PARSE_ERR;
579 goto errOut;
580 }
581 }
582 appendSecBuf(clientBuf, domainNameFlatLen, &domainNameOffset);
583
584 /* byte 36: user name */
585 ortn = ntlmStringFlatten(userName, unicode, &userNameFlat, &userNameFlatLen);
586 if(ortn) {
587 dprintf("createClientResponse: error converting user name\n");
588 ortn = NTLM_ERR_PARSE_ERR;
589 goto errOut;
590 }
591 appendSecBuf(clientBuf, userNameFlatLen, &userNameOffset);
592
593 /* byte 44: hostname */
594 ortn = ntlmHostName(unicode, &workstationName, &workstationNameLen);
595 if(ortn) {
596 dprintf("createClientResponse: error getting host name\n");
597 goto errOut;
598 }
599 appendSecBuf(clientBuf, workstationNameLen, &workstationNameOffset);
600
601 /* byte 52: session key (whatever that is): optional, empty here */
602 appendSecBuf(clientBuf, 0, &nullDex);
603
604 /* byte 60: negotiated flags */
605 appendUint32(clientBuf, ntlmGen->mSentFlags & serverFlags);
606
607 /* finally, the data associated with the security buffers */
608 secBufOffset(clientBuf, lmRespOffset);
609 CFDataAppendBytes(clientBuf, lmResp, NTLM_LM_RESPONSE_LEN);
610
611 secBufOffset(clientBuf, ntlmRespOffset);
612 CFDataAppendBytes(clientBuf, ntlmResponsePtr, ntlmResponseLen);
613
614 if(domain != NULL) {
615 secBufOffset(clientBuf, domainNameOffset);
616 CFDataAppendBytes(clientBuf, domainNameFlat, domainNameFlatLen);
617 }
618
619 secBufOffset(clientBuf, userNameOffset);
620 CFDataAppendBytes(clientBuf, userNameFlat, userNameFlatLen);
621
622 secBufOffset(clientBuf, workstationNameOffset);
623 CFDataAppendBytes(clientBuf, workstationName, workstationNameLen);
624
625 errOut:
626 CFREE(targetName);
627 CFREE(targetInfo);
628 CFREE(domainNameFlat);
629 CFREE(userNameFlat);
630 CFREE(workstationName);
631 if (domain) CFRelease(domain);
632 if(ntlmResponsePtr != ntlmResp) {
633 /* i.e., it was mallocd by ntlmGenerateNtlmV2Response */
634 CFREE(ntlmResponsePtr);
635 }
636 if(ortn == errSecSuccess) {
637 *clientResponse = clientBuf;
638 }
639 else {
640 if (clientBuf)
641 CFRelease(clientBuf);
642 }
643 return ortn;
644 }
645
646 /* replacement for NtlmNegotiatedNtlm2: returns NW_NTLM2Only,
647 * or NW_NTLMv2Only */
648 NLTM_Which NtlmGetNegotiatedVersion(
649 NtlmGeneratorRef ntlmGen)
650 {
651 return ntlmGen->mNegotiatedVersion;
652 }
653
654 OSStatus _NtlmGeneratePasswordHashes(
655 CFAllocatorRef alloc,
656 NtlmGeneratorRef ntlm,
657 CFStringRef password,
658 CFDataRef* ntlmHash,
659 CFDataRef* lmHash)
660 {
661 OSStatus result = errSecSuccess;
662 unsigned char hash[NTLM_DIGEST_LENGTH];
663
664 result = ntlmPasswordHash(password, hash);
665 if (result) {
666 return result;
667 }
668
669 *ntlmHash = CFDataCreate(alloc, hash, sizeof(hash));
670 memset(hash, 0, sizeof(hash));
671 if (*ntlmHash == NULL) {
672 result = errSecAllocate;
673 }
674
675 static const UInt8 zero[NTLM_DIGEST_LENGTH] = { 0 };
676 *lmHash = CFDataCreate(NULL, zero, sizeof(zero));
677
678
679 return result;
680 }
681
682 OSStatus NtlmGeneratePasswordHashes(
683 CFAllocatorRef alloc,
684 CFStringRef password,
685 CFDataRef* ntlmHash,
686 CFDataRef* lmHash)
687 {
688 NtlmGeneratorRef ntlm = NULL;
689
690 OSStatus result = NtlmGeneratorCreate(NW_Any, &ntlm);
691
692 if (result == errSecSuccess) {
693 result = _NtlmGeneratePasswordHashes(alloc, ntlm, password, ntlmHash, lmHash);
694 }
695
696 if (ntlm)
697 NtlmGeneratorRelease(ntlm);
698
699 return result;
700 }
701