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>
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 /* 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
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
= ntlmResponse(pwdHash
, sessionHash
, ntlmResp
);
546 dprintf("***Error on ntlmResponse (3)\n");
549 ntlmGen
->mNegotiatedVersion
= NW_NTLM2
;
551 else if(ntlmGen
->mWhich
& NW_NTLM1
) {
553 * LM response - the old style 2-DES "password hash" applied
554 * the the server's challenge
556 // ortn = lmPasswordHash(password, pwdHash);
558 // dprintf("***Error on lmPasswordHash\n");
561 memmove(pwdHash
, CFDataGetBytePtr(lmHash
), sizeof(pwdHash
));
563 ortn
= ntlmResponse(pwdHash
, serverChallenge
, lmResp
);
565 dprintf("***Error on ntlmResponse (1)\n");
570 * NTLM response: md4 password hash, DES with three different keys
572 // ntlmPasswordHash(password, pwdHash);
573 memmove(pwdHash
, CFDataGetBytePtr(ntlmHash
), sizeof(pwdHash
));
575 ortn
= ntlmResponse(pwdHash
, serverChallenge
, ntlmResp
);
577 dprintf("***Error on ntlmResponse (2)\n");
580 ntlmGen
->mNegotiatedVersion
= NW_NTLM1
;
583 dprintf("***NTLM protocol mismatch\n");
584 ortn
= NTLM_ERR_PROTOCOL_MISMATCH
;
590 * Write security buffers.
592 * byte 12: LM response
593 * byte 20: NTLM response
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
;
602 * convert domain and user as appropriate
603 * byte 28: domain (server) name
606 ortn
= ntlmStringFlatten(domain
, unicode
, &domainNameFlat
, &domainNameFlatLen
);
608 dprintf("createClientResponse: error converting domain name\n");
609 ortn
= NTLM_ERR_PARSE_ERR
;
613 appendSecBuf(clientBuf
, domainNameFlatLen
, &domainNameOffset
);
615 /* byte 36: user name */
616 ortn
= ntlmStringFlatten(userName
, unicode
, &userNameFlat
, &userNameFlatLen
);
618 dprintf("createClientResponse: error converting user name\n");
619 ortn
= NTLM_ERR_PARSE_ERR
;
622 appendSecBuf(clientBuf
, userNameFlatLen
, &userNameOffset
);
624 /* byte 44: hostname */
625 ortn
= ntlmHostName(unicode
, &workstationName
, &workstationNameLen
);
627 dprintf("createClientResponse: error getting host name\n");
630 appendSecBuf(clientBuf
, workstationNameLen
, &workstationNameOffset
);
632 /* byte 52: session key (whatever that is): optional, empty here */
633 appendSecBuf(clientBuf
, 0, &nullDex
);
635 /* byte 60: negotiated flags */
636 appendUint32(clientBuf
, ntlmGen
->mSentFlags
& serverFlags
);
638 /* finally, the data associated with the security buffers */
639 secBufOffset(clientBuf
, lmRespOffset
);
640 CFDataAppendBytes(clientBuf
, lmResp
, NTLM_LM_RESPONSE_LEN
);
642 secBufOffset(clientBuf
, ntlmRespOffset
);
643 CFDataAppendBytes(clientBuf
, ntlmResponsePtr
, ntlmResponseLen
);
646 secBufOffset(clientBuf
, domainNameOffset
);
647 CFDataAppendBytes(clientBuf
, domainNameFlat
, domainNameFlatLen
);
650 secBufOffset(clientBuf
, userNameOffset
);
651 CFDataAppendBytes(clientBuf
, userNameFlat
, userNameFlatLen
);
653 secBufOffset(clientBuf
, workstationNameOffset
);
654 CFDataAppendBytes(clientBuf
, workstationName
, workstationNameLen
);
659 CFREE(domainNameFlat
);
661 CFREE(workstationName
);
662 if (domain
) CFRelease(domain
);
663 if(ntlmResponsePtr
!= ntlmResp
) {
664 /* i.e., it was mallocd by ntlmGenerateNtlmV2Response */
665 CFREE(ntlmResponsePtr
);
667 if(ortn
== errSecSuccess
) {
668 *clientResponse
= clientBuf
;
672 CFRelease(clientBuf
);
677 /* replacement for NtlmNegotiatedNtlm2: returns NW_NTLM1Only, NW_NTLM2Only,
678 * or NW_NTLMv2Only */
679 NLTM_Which
NtlmGetNegotiatedVersion(
680 NtlmGeneratorRef ntlmGen
)
682 return ntlmGen
->mNegotiatedVersion
;
685 OSStatus
_NtlmGeneratePasswordHashes(
686 CFAllocatorRef alloc
,
687 NtlmGeneratorRef ntlm
,
688 CFStringRef password
,
692 OSStatus result
= errSecSuccess
;
693 unsigned char hash
[NTLM_DIGEST_LENGTH
];
695 ntlmPasswordHash(password
, hash
);
697 *ntlmHash
= CFDataCreate(alloc
, hash
, sizeof(hash
));
699 result
= lmPasswordHash(password
, hash
);
701 if (result
== errSecSuccess
)
702 *lmHash
= CFDataCreate(alloc
, hash
, sizeof(hash
));
707 OSStatus
NtlmGeneratePasswordHashes(
708 CFAllocatorRef alloc
,
709 CFStringRef password
,
713 NtlmGeneratorRef ntlm
= NULL
;
715 OSStatus result
= NtlmGeneratorCreate(NW_Any
, &ntlm
);
717 if (result
== errSecSuccess
) {
718 result
= _NtlmGeneratePasswordHashes(alloc
, ntlm
, password
, ntlmHash
, lmHash
);
722 NtlmGeneratorRelease(ntlm
);