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