2 * Copyright (c) 2000-2004,2006-2007,2011,2013 Apple Inc. All Rights Reserved.
4 * @APPLE_LICENSE_HEADER_START@
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
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.
21 * @APPLE_LICENSE_HEADER_END@
25 * NtlmGenerator.c - NTLM client-side authentication engine.
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
31 * http://davenport.sourceforge.net/ntlm.html#localAuthentication
34 #include "NtlmGenerator.h"
35 #include "ntlmBlobPriv.h"
36 #include <Security/SecBase.h>
41 #include <security_utilities/simulatecrash_assert.h>
45 * For debugging using fixed server challenge and client nonce.
47 #if DEBUG_FIXED_CHALLENGE
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 };
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,
73 /* app's NtlmGeneratorRef is a pointer to one of these */
74 struct NtlmGenerator
{
76 NLTM_Which mNegotiatedVersion
;
77 uint32_t mSentFlags
; /* the flags we sent in first mst */
80 static OSStatus
_NtlmGeneratePasswordHashes(
82 NtlmGeneratorRef ntlm
,
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.
92 static OSStatus
ntlmParseServerChallenge(
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 */
108 if(serverBlob
== NULL
) {
109 return NTLM_ERR_PARSE_ERR
;
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 */
117 unsigned bufLen
= (unsigned)CFDataGetLength(serverBlob
);
118 if(bufLen
< minLength
) {
119 dprintf("ntlmParseServerChallenge: bad length\n");
120 return NTLM_ERR_PARSE_ERR
;
123 /* do not even think of touching serverBlob after this */
124 const unsigned char *cp
= CFDataGetBytePtr(serverBlob
);
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
;
132 const unsigned char *currCp
= cp
+ NTLM_SIGNATURE_LEN
;
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
;
140 currCp
+= sizeof(uint32_t);
142 /* byte 12: target name, security buffer */
143 const unsigned char *sbData
;
145 OSStatus ortn
= ntlmParseSecBuffer(currCp
, cp
, bufLen
, &sbData
, &sbLen
);
149 *targetName
= (unsigned char *)malloc(sbLen
);
150 *targetNameLen
= sbLen
;
151 memmove(*targetName
, sbData
, sbLen
);
152 currCp
+= NTLM_SIZEOF_SEC_BUF
;
155 *serverFlags
= OSReadLittleInt32(currCp
, 0);
156 currCp
+= sizeof(uint32_t);
158 /* byte 24: challenge */
159 #if DEBUG_FIXED_CHALLENGE
160 memmove(challenge
, fixServerChallenge
, NTLM_CHALLENGE_LEN
);
162 memmove(challenge
, currCp
, NTLM_CHALLENGE_LEN
);
164 currCp
+= NTLM_CHALLENGE_LEN
;
166 /* remaining fields optional */
167 const unsigned char *endOfBuf
= cp
+ bufLen
;
168 assert(endOfBuf
>= currCp
);
169 if(endOfBuf
== currCp
) {
170 return errSecSuccess
;
173 if(endOfBuf
< (currCp
+ NTLM_SIZEOF_SEC_BUF
)) {
174 /* not enough left for even one security buf; ignore */
175 return errSecSuccess
;
178 /* byte 32: context: skip */
179 currCp
+= NTLM_SIZEOF_SEC_BUF
;
181 if(endOfBuf
< (currCp
+ NTLM_SIZEOF_SEC_BUF
)) {
182 /* not enough left for target info security buf; ignore */
183 return errSecSuccess
;
186 /* byte 40: target info */
187 ortn
= ntlmParseSecBuffer(currCp
, cp
, bufLen
, &sbData
, &sbLen
);
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
;
204 * Create NTLMv2 responses (both NTLM and LM).
206 static OSStatus
ntlmGenerateNtlmV2Response(
209 CFStringRef userName
,
213 const unsigned char *serverChallenge
,
214 const unsigned char *targetInfo
,
215 unsigned targetInfoLen
,
218 unsigned char *lmV2Response
, // caller supplied, NTLM_LM_RESPONSE_LEN bytes
219 unsigned char **ntlmv2Response
, // mallocd and RETURNED
220 unsigned *ntlmV2ResponseLen
) // RETURNED
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
);
227 ntlmRand(NTLM_CLIENT_NONCE_LEN
, challenge
);
230 /* NTLM password hash */
231 unsigned char ntlmPwdHash
[NTLM_DIGEST_LENGTH
];
232 // ntlmPasswordHash(password, ntlmPwdHash);
233 memmove(ntlmPwdHash
, CFDataGetBytePtr(ntlmHash
), sizeof(ntlmPwdHash
));
235 /* uppercase(userName | domain) */
236 CFMutableStringRef userDomain
= CFStringCreateMutableCopy(NULL
, 0, userName
);
238 CFStringAppend(userDomain
, domain
);
240 CFStringUppercase(userDomain
, NULL
);
242 /* declare some locals prior to any gotos */
243 unsigned char *ucode
= NULL
;
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};
255 /* HMAC(passwordHash, uppercase(userName | domain)) */
256 ntlmStringToLE(userDomain
, &ucode
, &ucodeLen
);
257 OSStatus ortn
= ntlmHmacMD5(ntlmPwdHash
, NTLM_DIGEST_LENGTH
,
258 ucode
, ucodeLen
, ntlmV2Hash
);
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
);
272 /* LMv2 response := challengeMac | clientChallenge */
273 memmove(lmV2Response
, challengeMac
, NTLM_DIGEST_LENGTH
);
274 memmove(lmV2Response
+ NTLM_DIGEST_LENGTH
, challenge
, NTLM_CLIENT_NONCE_LEN
);
276 /* Prepare the NTLMv2 'blob' */
277 ntlmV2Blob
= CFDataCreateMutable(NULL
, 0);
280 CFDataAppendBytes(ntlmV2Blob
, blobSig
, 4);
281 /* 4: reserved, zeroes */
282 appendUint32(ntlmV2Blob
, 0);
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);
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
297 ntlmV2BlobLen
= (unsigned)CFDataGetLength(ntlmV2Blob
);
298 catBlob
= CFDataCreateMutable(NULL
, 0);
299 CFDataAppendBytes(catBlob
, serverChallenge
, NTLM_CHALLENGE_LEN
);
300 CFDataAppendBytes(catBlob
, CFDataGetBytePtr(ntlmV2Blob
), ntlmV2BlobLen
);
302 /* HMAC(ntlmV2Hash, serverChallenge | blob) */
303 ortn
= ntlmHmacMD5(ntlmV2Hash
, NTLM_DIGEST_LENGTH
,
304 CFDataGetBytePtr(catBlob
), (unsigned)CFDataGetLength(catBlob
),
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
;
319 CFRelease(userDomain
);
322 CFRelease(ntlmV2Blob
);
332 * Create/release NtlmGenerator objects.
334 OSStatus
NtlmGeneratorCreate(
336 NtlmGeneratorRef
*ntlmGen
) /* RETURNED */
338 struct NtlmGenerator
*gen
=
339 (struct NtlmGenerator
*)malloc(sizeof(struct NtlmGenerator
));
341 return errSecAllocate
;
344 gen
->mNegotiatedVersion
= 0; /* i.e., unknown */
347 return errSecSuccess
;
350 void NtlmGeneratorRelease(
351 NtlmGeneratorRef ntlmGen
)
353 if(ntlmGen
== NULL
) {
359 OSStatus
NtlmCreateClientRequest(
360 NtlmGeneratorRef ntlmGen
,
361 CFDataRef
*clientRequest
) /* RETURNED */
363 CFMutableDataRef req
= CFDataCreateMutable(NULL
, 0);
365 return errSecAllocate
;
367 /* byte 0: signature, NULL terminated */
368 CFDataAppendBytes(req
, (UInt8
*)NTLM_SIGNATURE
, NTLM_SIGNATURE_LEN
);
370 /* byte 8: message type */
371 appendUint32(req
, NTLM_MSG_MARKER_TYPE1
);
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
|
380 if(ntlmGen
->mWhich
& NW_NTLM2
) {
381 ntlmGen
->mSentFlags
|= NTLM_NegotiateNTLM2Key
;
383 appendUint32(req
, ntlmGen
->mSentFlags
);
385 /* byte 16: optional supplied domain: not needed */
387 appendSecBuf(req
, 0, &dex
);
389 /* byte 24: optional supplied workstation: not needed */
390 appendSecBuf(req
, 0, &dex
);
392 *clientRequest
= req
;
393 return errSecSuccess
;
397 * The meat & potatoes: given a server type 2 message, cook up a type 3 response.
399 OSStatus
NtlmCreateClientResponse(
400 NtlmGeneratorRef ntlmGen
,
401 CFDataRef serverBlob
,
402 CFStringRef domain
, /* optional */
403 CFStringRef userName
,
404 CFStringRef password
,
405 CFDataRef
*clientResponse
) /* RETURNED */
407 CFDataRef ntlmHash
= NULL
;
408 CFDataRef lmHash
= NULL
;
409 OSStatus result
= _NtlmGeneratePasswordHashes(kCFAllocatorDefault
, ntlmGen
, password
, &ntlmHash
, &lmHash
);
411 if (result
== errSecSuccess
) {
413 result
= _NtlmCreateClientResponse(ntlmGen
, serverBlob
, domain
, userName
, ntlmHash
, lmHash
, clientResponse
);
425 OSStatus
_NtlmCreateClientResponse(
426 NtlmGeneratorRef ntlmGen
,
427 CFDataRef serverBlob
,
428 CFStringRef domain
, /* optional */
429 CFStringRef userName
,
432 CFDataRef
*clientResponse
) /* RETURNED */
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
;
457 unsigned char pwdHash
[NTLM_DIGEST_LENGTH
];
459 ortn
= ntlmParseServerChallenge(serverBlob
, &serverFlags
, serverChallenge
,
460 &targetName
, &targetNameLen
,
461 &targetInfo
, &targetInfoLen
);
465 /* subsequent errors to errOut: */
467 /* gather negotiated parameters */
468 bool lm2Key
= (serverFlags
& NTLM_NegotiateNTLM2Key
) ? true : false;
469 bool unicode
= (serverFlags
& NTLM_NegotiateUnicode
) ? true : false;
472 CFMutableDataRef clientBuf
= CFDataCreateMutable(NULL
, 0);
473 if(clientBuf
== NULL
) {
474 ortn
= errSecAllocate
;
479 domain
= CFStringCreateMutableCopy(NULL
, 0, domain
);
481 CFStringUppercase((CFMutableStringRef
)domain
, NULL
);
483 ortn
= errSecAllocate
;
488 /* byte 0: signature, NULL terminated */
489 CFDataAppendBytes(clientBuf
, (UInt8
*)NTLM_SIGNATURE
, NTLM_SIGNATURE_LEN
);
491 /* byte 8: message type */
492 appendUint32(clientBuf
, NTLM_MSG_MARKER_TYPE3
);
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
502 ortn
= ntlmGenerateNtlmV2Response(domain
, userName
, ntlmHash
,
503 serverChallenge
, targetInfo
, targetInfoLen
,
504 lmResp
, &ntlmResponsePtr
, &ntlmResponseLen
);
510 * Write security buffers.
512 * byte 12: LM response
513 * byte 20: NTLM response
515 appendSecBuf(clientBuf
, NTLM_LM_RESPONSE_LEN
, &lmRespOffset
);
516 appendSecBuf(clientBuf
, ntlmResponseLen
, &ntlmRespOffset
);
517 ntlmGen
->mNegotiatedVersion
= NW_NTLMv2
;
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
);
525 ntlmRand(NTLM_CLIENT_NONCE_LEN
, lmResp
);
527 memset(lmResp
+ NTLM_CLIENT_NONCE_LEN
, 0,
528 NTLM_LM_RESPONSE_LEN
- NTLM_CLIENT_NONCE_LEN
);
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
);
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
);
539 /* standard password hash */
540 // ntlmPasswordHash(password, pwdHash);
541 memmove(pwdHash
, CFDataGetBytePtr(ntlmHash
), sizeof(pwdHash
));
543 /* NTLM response: DES with three different keys */
544 ortn
= lmv2Response(pwdHash
, sessionHash
, ntlmResp
);
546 dprintf("***Error on ntlmResponse (3)\n");
549 ntlmGen
->mNegotiatedVersion
= NW_NTLM2
;
552 dprintf("***NTLM protocol mismatch\n");
553 ortn
= NTLM_ERR_PROTOCOL_MISMATCH
;
559 * Write security buffers.
561 * byte 12: LM response
562 * byte 20: NTLM response
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
;
571 * convert domain and user as appropriate
572 * byte 28: domain (server) name
575 ortn
= ntlmStringFlatten(domain
, unicode
, &domainNameFlat
, &domainNameFlatLen
);
577 dprintf("createClientResponse: error converting domain name\n");
578 ortn
= NTLM_ERR_PARSE_ERR
;
582 appendSecBuf(clientBuf
, domainNameFlatLen
, &domainNameOffset
);
584 /* byte 36: user name */
585 ortn
= ntlmStringFlatten(userName
, unicode
, &userNameFlat
, &userNameFlatLen
);
587 dprintf("createClientResponse: error converting user name\n");
588 ortn
= NTLM_ERR_PARSE_ERR
;
591 appendSecBuf(clientBuf
, userNameFlatLen
, &userNameOffset
);
593 /* byte 44: hostname */
594 ortn
= ntlmHostName(unicode
, &workstationName
, &workstationNameLen
);
596 dprintf("createClientResponse: error getting host name\n");
599 appendSecBuf(clientBuf
, workstationNameLen
, &workstationNameOffset
);
601 /* byte 52: session key (whatever that is): optional, empty here */
602 appendSecBuf(clientBuf
, 0, &nullDex
);
604 /* byte 60: negotiated flags */
605 appendUint32(clientBuf
, ntlmGen
->mSentFlags
& serverFlags
);
607 /* finally, the data associated with the security buffers */
608 secBufOffset(clientBuf
, lmRespOffset
);
609 CFDataAppendBytes(clientBuf
, lmResp
, NTLM_LM_RESPONSE_LEN
);
611 secBufOffset(clientBuf
, ntlmRespOffset
);
612 CFDataAppendBytes(clientBuf
, ntlmResponsePtr
, ntlmResponseLen
);
615 secBufOffset(clientBuf
, domainNameOffset
);
616 CFDataAppendBytes(clientBuf
, domainNameFlat
, domainNameFlatLen
);
619 secBufOffset(clientBuf
, userNameOffset
);
620 CFDataAppendBytes(clientBuf
, userNameFlat
, userNameFlatLen
);
622 secBufOffset(clientBuf
, workstationNameOffset
);
623 CFDataAppendBytes(clientBuf
, workstationName
, workstationNameLen
);
628 CFREE(domainNameFlat
);
630 CFREE(workstationName
);
631 if (domain
) CFRelease(domain
);
632 if(ntlmResponsePtr
!= ntlmResp
) {
633 /* i.e., it was mallocd by ntlmGenerateNtlmV2Response */
634 CFREE(ntlmResponsePtr
);
636 if(ortn
== errSecSuccess
) {
637 *clientResponse
= clientBuf
;
641 CFRelease(clientBuf
);
646 /* replacement for NtlmNegotiatedNtlm2: returns NW_NTLM2Only,
647 * or NW_NTLMv2Only */
648 NLTM_Which
NtlmGetNegotiatedVersion(
649 NtlmGeneratorRef ntlmGen
)
651 return ntlmGen
->mNegotiatedVersion
;
654 OSStatus
_NtlmGeneratePasswordHashes(
655 CFAllocatorRef alloc
,
656 NtlmGeneratorRef ntlm
,
657 CFStringRef password
,
661 OSStatus result
= errSecSuccess
;
662 unsigned char hash
[NTLM_DIGEST_LENGTH
];
664 result
= ntlmPasswordHash(password
, hash
);
669 *ntlmHash
= CFDataCreate(alloc
, hash
, sizeof(hash
));
670 memset(hash
, 0, sizeof(hash
));
671 if (*ntlmHash
== NULL
) {
672 result
= errSecAllocate
;
675 static const UInt8 zero
[NTLM_DIGEST_LENGTH
] = { 0 };
676 *lmHash
= CFDataCreate(NULL
, zero
, sizeof(zero
));
682 OSStatus
NtlmGeneratePasswordHashes(
683 CFAllocatorRef alloc
,
684 CFStringRef password
,
688 NtlmGeneratorRef ntlm
= NULL
;
690 OSStatus result
= NtlmGeneratorCreate(NW_Any
, &ntlm
);
692 if (result
== errSecSuccess
) {
693 result
= _NtlmGeneratePasswordHashes(alloc
, ntlm
, password
, ntlmHash
, lmHash
);
697 NtlmGeneratorRelease(ntlm
);