]> git.saurik.com Git - apple/security.git/blobdiff - ntlm/NtlmGenerator.c
Security-57031.1.35.tar.gz
[apple/security.git] / ntlm / NtlmGenerator.c
diff --git a/ntlm/NtlmGenerator.c b/ntlm/NtlmGenerator.c
new file mode 100644 (file)
index 0000000..72db6c4
--- /dev/null
@@ -0,0 +1,726 @@
+/*
+ * Copyright (c) 2000-2004,2006-2007,2011,2013 Apple Inc. All Rights Reserved.
+ * 
+ * @APPLE_LICENSE_HEADER_START@
+ * 
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this
+ * file.
+ * 
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ * 
+ * @APPLE_LICENSE_HEADER_END@
+ */
+
+/*
+ * NtlmGenerator.c - NTLM client-side authentication engine. 
+ *
+ * In the usual absence of documentation from Microsoft, the "inventors" of this
+ * protocol, this module was written using the superb revers engineering documented
+ * at 
+ *
+ *     http://davenport.sourceforge.net/ntlm.html#localAuthentication
+ */
+
+#include "NtlmGenerator.h"
+#include "ntlmBlobPriv.h"
+#include <Security/SecBase.h>
+
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <assert.h>
+#include <strings.h>
+
+/* 
+ * For debugging using fixed server challenge and client nonce. 
+ */
+#if            DEBUG_FIXED_CHALLENGE
+
+/* these are "test vectors", effectively, from sourceforge */
+/* use pwd SecREt01, host/domain DOMAIN */
+static const unsigned char fixServerChallenge[8] = 
+       { 0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef };
+static const unsigned char fixClientNonce[8] = 
+       { 0xff, 0xff, 0xff, 0x00, 0x11, 0x22, 0x33, 0x44 };
+
+static const unsigned char fixTargetInfo[] = {
+       0x02, 0x00, 0x0c, 0x00, 0x44, 0x00, 0x4f, 0x00,
+       0x4d, 0x00, 0x41, 0x00, 0x49, 0x00, 0x4e, 0x00,
+       0x01, 0x00, 0x0c, 0x00, 0x53, 0x00, 0x45, 0x00,
+       0x52, 0x00, 0x56, 0x00, 0x45, 0x00, 0x52, 0x00,
+       0x04, 0x00, 0x14, 0x00, 0x64, 0x00, 0x6f, 0x00,
+       0x6d, 0x00, 0x61, 0x00, 0x69, 0x00, 0x6e, 0x00,
+       0x2e, 0x00, 0x63, 0x00, 0x6f, 0x00, 0x6d, 0x00,
+       0x03, 0x00, 0x22, 0x00, 0x73, 0x00, 0x65, 0x00, 
+       0x72, 0x00, 0x76, 0x00, 0x65, 0x00, 0x72, 0x00,
+       0x2e, 0x00, 0x64, 0x00, 0x6f, 0x00, 0x6d, 0x00,
+       0x61, 0x00, 0x69, 0x00, 0x6e, 0x00, 0x2e, 0x00,
+       0x63, 0x00, 0x6f, 0x00, 0x6d, 0x00, 0x00, 0x00,
+       0x00, 0x00
+};
+#endif
+
+/* app's NtlmGeneratorRef is a pointer to one of these */
+struct NtlmGenerator {
+       NLTM_Which                      mWhich;
+       NLTM_Which                      mNegotiatedVersion;
+       uint32_t                        mSentFlags;                     /* the flags we sent in first mst */
+};     
+
+static OSStatus _NtlmGeneratePasswordHashes(
+       CFAllocatorRef alloc,
+       NtlmGeneratorRef ntlm,
+       CFStringRef password,
+       CFDataRef* ntlmHash,
+       CFDataRef* lmHash);
+                                                                                 
+/*
+ * Validate type 2 message sent by the server; return interesting fields. 
+ * NOTE we do not deal with the Context field here, which is only used
+ * for local authetication.
+ */
+static OSStatus ntlmParseServerChallenge(
+       CFDataRef               serverBlob,
+       uint32_t                *serverFlags,           /* RETURNED */
+       unsigned char   *challenge,                     /* 8 bytes, mallocd by caller, RETURNED */
+       unsigned char   **targetName,           /* mallocd and RETURNED */
+       unsigned                *targetNameLen,         /* RETURNED */
+       unsigned char   **targetInfo,           /* optionally mallocd and RETURNED */
+       unsigned                *targetInfoLen)         /* optionally RETURNED */
+{
+       unsigned minLength;
+       
+       *targetName = NULL;
+       *targetNameLen = 0;
+       *targetInfo = NULL;
+       *targetInfoLen = 0;
+       
+       if(serverBlob == NULL) {
+               return NTLM_ERR_PARSE_ERR;
+       }
+       
+       minLength = NTLM_SIGNATURE_LEN +
+               (unsigned)sizeof(uint32_t) +                    /* msg type */
+               NTLM_SIZEOF_SEC_BUF +           /* target name */
+               (unsigned)sizeof(uint32_t) +                    /* flags */
+               NTLM_CHALLENGE_LEN;             
+       unsigned bufLen = (unsigned)CFDataGetLength(serverBlob);
+       if(bufLen < minLength) {
+               dprintf("ntlmParseServerChallenge: bad length\n");
+               return NTLM_ERR_PARSE_ERR;
+       }
+       
+       /* do not even think of touching serverBlob after this */
+       const unsigned char *cp = CFDataGetBytePtr(serverBlob);
+       
+       /* byte 0: signature */
+       if(memcmp(cp, NTLM_SIGNATURE, NTLM_SIGNATURE_LEN)) {
+               dprintf("ntlmParseServerChallenge: signature mismatch\n");
+               return NTLM_ERR_PARSE_ERR;
+       }
+       
+       const unsigned char *currCp = cp + NTLM_SIGNATURE_LEN;
+       
+       /* byte 8: message type */
+       uint32_t msgType = OSReadLittleInt32(currCp, 0);
+       if(msgType != NTLM_MSG_MARKER_TYPE2) {
+               dprintf("ntlmParseServerChallenge: bad msg type\n");
+               return NTLM_ERR_PARSE_ERR;
+       }
+       currCp += sizeof(uint32_t);
+       
+       /* byte 12: target name, security buffer */
+       const unsigned char *sbData;
+       uint16_t sbLen;
+       OSStatus ortn = ntlmParseSecBuffer(currCp, cp, bufLen, &sbData, &sbLen);
+       if(ortn) {
+               return ortn;
+       }
+       *targetName = (unsigned char *)malloc(sbLen);
+       *targetNameLen = sbLen;
+       memmove(*targetName, sbData, sbLen);
+       currCp += NTLM_SIZEOF_SEC_BUF;
+       
+       /* byte 20: flags */
+       *serverFlags = OSReadLittleInt32(currCp, 0);
+       currCp += sizeof(uint32_t);
+       
+       /* byte 24: challenge */
+       #if             DEBUG_FIXED_CHALLENGE
+       memmove(challenge, fixServerChallenge, NTLM_CHALLENGE_LEN);
+       #else
+       memmove(challenge, currCp, NTLM_CHALLENGE_LEN);
+       #endif
+       currCp += NTLM_CHALLENGE_LEN;
+       
+       /* remaining fields optional */
+       const unsigned char *endOfBuf = cp + bufLen;
+       assert(endOfBuf >= currCp);
+       if(endOfBuf == currCp) {
+               return errSecSuccess;
+       }
+       
+       if(endOfBuf < (currCp + NTLM_SIZEOF_SEC_BUF)) {
+               /* not enough left for even one security buf; ignore */
+               return errSecSuccess;
+       }
+       
+       /* byte 32: context: skip */
+       currCp += NTLM_SIZEOF_SEC_BUF;
+       
+       if(endOfBuf < (currCp + NTLM_SIZEOF_SEC_BUF)) {
+               /* not enough left for target info security buf; ignore */
+               return errSecSuccess;
+       }
+       
+       /* byte 40: target info */
+       ortn = ntlmParseSecBuffer(currCp, cp, bufLen, &sbData, &sbLen);
+       if(ortn) {
+               free(*targetName);
+               *targetName = NULL;
+               return ortn;
+       }
+       #if             DEBUG_FIXED_CHALLENGE
+       sbData = fixTargetInfo;
+       sbLen = sizeof(fixTargetInfo);
+       #endif  /* DEBUG_FIXED_CHALLENGE */
+       *targetInfo = (unsigned char *)malloc(sbLen);
+       *targetInfoLen = sbLen;
+       memmove(*targetInfo, sbData, sbLen);
+       return errSecSuccess;
+}
+
+/* 
+ * Create NTLMv2 responses (both NTLM and LM).
+ */
+static OSStatus ntlmGenerateNtlmV2Response(
+       /* from app */
+       CFStringRef             domain,         
+       CFStringRef             userName,
+       CFDataRef               ntlmHash,
+       
+       /* from server */
+       const unsigned char *serverChallenge,
+       const unsigned char *targetInfo,
+       unsigned                        targetInfoLen,
+       
+       /* returned */
+       unsigned char   *lmV2Response,          // caller supplied, NTLM_LM_RESPONSE_LEN bytes
+       unsigned char   **ntlmv2Response,   // mallocd and RETURNED
+       unsigned                *ntlmV2ResponseLen) // RETURNED
+{
+       /* Random challenge used in both responses */
+       unsigned char challenge[NTLM_CLIENT_NONCE_LEN];
+       #if DEBUG_FIXED_CHALLENGE
+       memmove(challenge, fixClientNonce, NTLM_CLIENT_NONCE_LEN);
+       #else
+       ntlmRand(NTLM_CLIENT_NONCE_LEN, challenge);
+       #endif
+       
+       /* NTLM password hash */
+       unsigned char ntlmPwdHash[NTLM_DIGEST_LENGTH];
+//     ntlmPasswordHash(password, ntlmPwdHash);
+       memmove(ntlmPwdHash, CFDataGetBytePtr(ntlmHash), sizeof(ntlmPwdHash));
+       
+       /* uppercase(userName | domain) */
+       CFMutableStringRef userDomain = CFStringCreateMutableCopy(NULL, 0, userName);
+       if(domain != NULL) {
+               CFStringAppend(userDomain, domain);
+       }
+       CFStringUppercase(userDomain, NULL);
+       
+       /* declare some locals prior to any gotos */
+       unsigned char *ucode = NULL;
+       unsigned ucodeLen;
+       unsigned char ntlmV2Hash[NTLM_DIGEST_LENGTH];
+       unsigned char macText2[NTLM_CHALLENGE_LEN + NTLM_CLIENT_NONCE_LEN];
+       unsigned char challengeMac[NTLM_DIGEST_LENGTH];
+       unsigned char blobMac[NTLM_DIGEST_LENGTH];
+       unsigned char *ntlmv2Resp = NULL;
+       CFMutableDataRef ntlmV2Blob = NULL;
+       CFMutableDataRef catBlob = NULL;
+       unsigned ntlmV2BlobLen;
+       unsigned char blobSig[4] = {0x01, 0x01, 0x00, 0x00};
+       
+       /* HMAC(passwordHash, uppercase(userName | domain)) */
+       ntlmStringToLE(userDomain, &ucode, &ucodeLen);
+       OSStatus ortn = ntlmHmacMD5(ntlmPwdHash, NTLM_DIGEST_LENGTH,
+               ucode, ucodeLen, ntlmV2Hash);
+       if(ortn) {
+               goto errOut;
+       }
+       
+       /* HMAC(ntlmV2Hash, serverChallenge | clientChallenge) */
+       memmove(macText2, serverChallenge, NTLM_CHALLENGE_LEN);
+       memmove(macText2 + NTLM_CHALLENGE_LEN, challenge, NTLM_CLIENT_NONCE_LEN);
+       ortn = ntlmHmacMD5(ntlmV2Hash, NTLM_DIGEST_LENGTH,
+               macText2, NTLM_CHALLENGE_LEN + NTLM_CLIENT_NONCE_LEN, challengeMac);
+       if(ortn) {
+               goto errOut;
+       }
+
+       /* LMv2 response := challengeMac | clientChallenge */
+       memmove(lmV2Response, challengeMac, NTLM_DIGEST_LENGTH);
+       memmove(lmV2Response + NTLM_DIGEST_LENGTH, challenge, NTLM_CLIENT_NONCE_LEN);
+       
+       /* Prepare the NTLMv2 'blob' */
+       ntlmV2Blob = CFDataCreateMutable(NULL, 0);
+       
+       /* 0: 0x01010000 */
+       CFDataAppendBytes(ntlmV2Blob, blobSig, 4);
+       /* 4: reserved, zeroes */
+       appendUint32(ntlmV2Blob, 0);
+       /* 8: Timestamp */
+       ntlmAppendTimestamp(ntlmV2Blob);
+       /* 16: client challenge */
+       CFDataAppendBytes(ntlmV2Blob, challenge, NTLM_CLIENT_NONCE_LEN);
+       /* 24: unknown, zeroes */
+       appendUint32(ntlmV2Blob, 0);
+       /* 28: target info from server */
+       CFDataAppendBytes(ntlmV2Blob, targetInfo, targetInfoLen);
+       /* *: unknown, zeroes */
+       appendUint32(ntlmV2Blob, 0);
+       
+       /* keep that blob; it'll go directly into the response. Now cook up 
+        * another one, the concatentation of the server challenge with the
+        * ntlmV2Blob */
+       ntlmV2BlobLen = (unsigned)CFDataGetLength(ntlmV2Blob);
+       catBlob = CFDataCreateMutable(NULL, 0);
+       CFDataAppendBytes(catBlob, serverChallenge, NTLM_CHALLENGE_LEN);
+       CFDataAppendBytes(catBlob, CFDataGetBytePtr(ntlmV2Blob), ntlmV2BlobLen);
+
+       /* HMAC(ntlmV2Hash, serverChallenge | blob) */
+       ortn = ntlmHmacMD5(ntlmV2Hash, NTLM_DIGEST_LENGTH,
+               CFDataGetBytePtr(catBlob), (unsigned)CFDataGetLength(catBlob),
+               blobMac);
+       if(ortn) {
+               goto errOut;
+       }
+       
+       /* Finally, NTLMv2 response := (blobMac | ntlmV2Blob) */
+       ntlmv2Resp = (unsigned char *)malloc(NTLM_DIGEST_LENGTH + ntlmV2BlobLen);
+       memmove(ntlmv2Resp, blobMac, NTLM_DIGEST_LENGTH);
+       memmove(ntlmv2Resp + NTLM_DIGEST_LENGTH, CFDataGetBytePtr(ntlmV2Blob), ntlmV2BlobLen);
+       *ntlmv2Response = ntlmv2Resp;
+       *ntlmV2ResponseLen = NTLM_DIGEST_LENGTH + ntlmV2BlobLen;
+       ortn = errSecSuccess;
+errOut:
+       if(userDomain) {
+               CFRelease(userDomain);
+       }
+       if(ntlmV2Blob) {
+               CFRelease(ntlmV2Blob);
+       }
+       if(catBlob) {
+               CFRelease(catBlob);
+       }
+       CFREE(ucode);
+       return ortn;
+}
+
+/*
+ * Create/release NtlmGenerator objects.
+ */
+OSStatus NtlmGeneratorCreate(
+       NLTM_Which                      which,
+       NtlmGeneratorRef        *ntlmGen)                       /* RETURNED */
+{
+       struct NtlmGenerator *gen = 
+               (struct NtlmGenerator *)malloc(sizeof(struct NtlmGenerator));
+       if(gen == NULL) {
+               return errSecAllocate;
+       }
+       gen->mWhich = which;
+       gen->mNegotiatedVersion = 0;                    /* i.e., unknown */
+       gen->mSentFlags = 0;
+       *ntlmGen = gen;
+       return errSecSuccess;
+}
+       
+void NtlmGeneratorRelease(
+       NtlmGeneratorRef        ntlmGen)
+{
+       if(ntlmGen == NULL) {
+               return;
+       }
+       free(ntlmGen);
+}
+
+OSStatus NtlmCreateClientRequest(
+       NtlmGeneratorRef        ntlmGen,
+       CFDataRef                       *clientRequest)         /* RETURNED */
+{
+       CFMutableDataRef req = CFDataCreateMutable(NULL, 0);
+       if(req == NULL) {
+               return errSecAllocate;
+       }
+       /* byte 0: signature, NULL terminated */
+       CFDataAppendBytes(req, (UInt8 *)NTLM_SIGNATURE, NTLM_SIGNATURE_LEN);
+                
+       /* byte 8: message type */
+       appendUint32(req, NTLM_MSG_MARKER_TYPE1);
+       
+       /* byte 12: the standard flags we send - we're wide open to all types */
+       /* FIXME isn't there a way to tell the server we support NTLMv2? */
+       ntlmGen->mSentFlags = NTLM_NegotiateUnicode |
+               NTLM_NegotiateOEM |
+               NTLM_RequestTarget |
+               NTLM_NegotiateNTLM |
+               NTLM_AlwaysSign;
+       if(ntlmGen->mWhich & NW_NTLM2) {
+               ntlmGen->mSentFlags |= NTLM_NegotiateNTLM2Key;
+       }
+       appendUint32(req, ntlmGen->mSentFlags);
+       
+       /* byte 16: optional supplied domain: not needed */
+       CFIndex dex;
+       appendSecBuf(req, 0, &dex);
+               
+       /* byte 24: optional supplied workstation: not needed */
+       appendSecBuf(req, 0, &dex);
+
+       *clientRequest = req;
+       return errSecSuccess;
+}
+       
+/* 
+ * The meat & potatoes: given a server type 2 message, cook up a type 3 response. 
+ */
+OSStatus NtlmCreateClientResponse(
+       NtlmGeneratorRef        ntlmGen,
+       CFDataRef                       serverBlob,
+       CFStringRef                     domain,                         /* optional */
+       CFStringRef                     userName,
+       CFStringRef                     password,
+       CFDataRef                       *clientResponse)        /* RETURNED */
+{
+       CFDataRef ntlmHash = NULL;
+       CFDataRef lmHash = NULL;
+       OSStatus result = _NtlmGeneratePasswordHashes(kCFAllocatorDefault, ntlmGen, password, &ntlmHash, &lmHash);
+       
+       if (result == errSecSuccess) {
+               
+               result = _NtlmCreateClientResponse(ntlmGen, serverBlob, domain, userName, ntlmHash, lmHash, clientResponse);
+       }
+       
+       if (ntlmHash)
+               CFRelease(ntlmHash);
+       
+       if (lmHash)
+               CFRelease(lmHash);
+       
+       return result;
+}
+
+OSStatus _NtlmCreateClientResponse(
+       NtlmGeneratorRef        ntlmGen,
+       CFDataRef                       serverBlob,
+       CFStringRef                     domain,                         /* optional */
+       CFStringRef                     userName,
+       CFDataRef                       ntlmHash,
+       CFDataRef                       lmHash,
+       CFDataRef                       *clientResponse)        /* RETURNED */
+{
+       OSStatus                ortn;
+       uint32_t                serverFlags;
+       unsigned char   serverChallenge[NTLM_CHALLENGE_LEN];  
+       unsigned char   *targetName = NULL;
+       unsigned                targetNameLen = 0;
+       unsigned char   *targetInfo = NULL;
+       unsigned                targetInfoLen = 0;
+       CFIndex                 lmRespOffset;
+       unsigned char   lmResp[NTLM_LM_RESPONSE_LEN];
+       CFIndex                 ntlmRespOffset;
+       unsigned char   ntlmResp[NTLM_LM_RESPONSE_LEN];
+       unsigned char   *ntlmResponsePtr = NULL;
+       unsigned                ntlmResponseLen = 0;
+       unsigned char   *domainNameFlat = NULL;
+       unsigned                domainNameFlatLen = 0;
+       CFIndex                 domainNameOffset;
+       unsigned char   *userNameFlat = NULL;
+       unsigned                userNameFlatLen = 0;
+       CFIndex                 userNameOffset;
+       unsigned char   *workstationName = NULL;
+       unsigned                workstationNameLen = 0;
+       CFIndex                 workstationNameOffset;
+       CFIndex                 nullDex;
+       unsigned char   pwdHash[NTLM_DIGEST_LENGTH];
+       
+       ortn = ntlmParseServerChallenge(serverBlob, &serverFlags, serverChallenge,
+               &targetName, &targetNameLen,
+               &targetInfo, &targetInfoLen);
+       if(ortn) {
+               return ortn;
+       }
+       /* subsequent errors to errOut: */
+
+       /* gather negotiated parameters */
+       bool lm2Key  = (serverFlags & NTLM_NegotiateNTLM2Key) ? true : false;
+       bool unicode = (serverFlags & NTLM_NegotiateUnicode) ? true : false;
+       /* any others? */
+       
+       CFMutableDataRef clientBuf = CFDataCreateMutable(NULL, 0);
+       if(clientBuf == NULL) {
+               ortn = errSecAllocate;
+               goto errOut;
+       }
+       
+       if (domain) {
+               domain = CFStringCreateMutableCopy(NULL, 0, domain);
+               if (domain)
+                       CFStringUppercase((CFMutableStringRef)domain, NULL);
+               else {
+                       ortn = errSecAllocate;
+                       goto errOut;
+               }
+       }
+       
+       /* byte 0: signature, NULL terminated */
+       CFDataAppendBytes(clientBuf, (UInt8 *)NTLM_SIGNATURE, NTLM_SIGNATURE_LEN);
+                
+       /* byte 8: message type */
+       appendUint32(clientBuf, NTLM_MSG_MARKER_TYPE3);
+       
+       /* LM and NTLM responses */
+       if( (targetInfo != NULL) &&                                                     // server is NTLMv2 capable
+           (targetInfoLen != 0) &&                                                     // ditto
+               (serverFlags & NTLM_NegotiateTargetInfo) &&             // ditto
+               (ntlmGen->mWhich & NW_NTLMv2) ) {                               // ...and we are
+               /*
+                * NTLMv2
+                */
+               ortn = ntlmGenerateNtlmV2Response(domain, userName, ntlmHash,
+                       serverChallenge, targetInfo, targetInfoLen,
+                       lmResp, &ntlmResponsePtr, &ntlmResponseLen);
+               if(ortn) {
+                       goto errOut;
+               }
+               
+               /* 
+                * Write security buffers.
+                * 
+                * byte 12: LM response
+                * byte 20: NTLM response
+                */
+               appendSecBuf(clientBuf, NTLM_LM_RESPONSE_LEN, &lmRespOffset);
+               appendSecBuf(clientBuf, ntlmResponseLen, &ntlmRespOffset);
+               ntlmGen->mNegotiatedVersion = NW_NTLMv2;
+       }
+       else {
+               if(lm2Key && (ntlmGen->mWhich & NW_NTLM2)) {
+                       /* LM response: 8 random bytes, rest zeroes */
+                       #if DEBUG_FIXED_CHALLENGE
+                       memmove(lmResp, fixClientNonce, NTLM_CLIENT_NONCE_LEN);
+                       #else
+                       ntlmRand(NTLM_CLIENT_NONCE_LEN, lmResp);
+                       #endif
+                       memset(lmResp + NTLM_CLIENT_NONCE_LEN, 0, 
+                               NTLM_LM_RESPONSE_LEN - NTLM_CLIENT_NONCE_LEN);
+                       
+                       /* session nonce: server challenge | client nonce */
+                       unsigned char sessionNonce[NTLM_CHALLENGE_LEN + NTLM_CLIENT_NONCE_LEN];
+                       memmove(sessionNonce, serverChallenge, NTLM_CHALLENGE_LEN);
+                       memmove(sessionNonce + NTLM_CHALLENGE_LEN, lmResp, NTLM_CLIENT_NONCE_LEN);
+                       
+                       /* NTLM2 session hash: the first 8 bytes of MD5(sessionNonce) */
+                       unsigned char sessionHash[NTLM_DIGEST_LENGTH];
+                       md5Hash(sessionNonce, NTLM_CHALLENGE_LEN + NTLM_CLIENT_NONCE_LEN, sessionHash);
+                       
+                       /* standard password hash */
+//                     ntlmPasswordHash(password, pwdHash);
+                       memmove(pwdHash, CFDataGetBytePtr(ntlmHash), sizeof(pwdHash));
+                       
+                       /* NTLM response: DES with three different keys */
+                       ortn = ntlmResponse(pwdHash, sessionHash, ntlmResp);
+                       if(ortn) {
+                               dprintf("***Error on ntlmResponse (3)\n");
+                               goto errOut;
+                       }
+                       ntlmGen->mNegotiatedVersion = NW_NTLM2;
+               }
+               else if(ntlmGen->mWhich & NW_NTLM1) {
+                       /* 
+                        * LM response - the old style 2-DES "password hash" applied
+                        * the the server's challenge 
+                        */
+//                     ortn = lmPasswordHash(password, pwdHash);
+//                     if(ortn) {
+//                             dprintf("***Error on lmPasswordHash\n");
+//                             goto errOut;
+//                     }
+                       memmove(pwdHash, CFDataGetBytePtr(lmHash), sizeof(pwdHash));
+                       
+                       ortn = ntlmResponse(pwdHash, serverChallenge, lmResp);
+                       if(ortn) {
+                               dprintf("***Error on ntlmResponse (1)\n");
+                               goto errOut;
+                       }
+                       
+                       /*
+                        * NTLM response: md4 password hash, DES with three different keys 
+                        */
+//                     ntlmPasswordHash(password, pwdHash);
+                       memmove(pwdHash, CFDataGetBytePtr(ntlmHash), sizeof(pwdHash));
+
+                       ortn = ntlmResponse(pwdHash, serverChallenge, ntlmResp);
+                       if(ortn) {
+                               dprintf("***Error on ntlmResponse (2)\n");
+                               goto errOut;
+                       }
+                       ntlmGen->mNegotiatedVersion = NW_NTLM1;
+               }
+               else {
+                       dprintf("***NTLM protocol mismatch\n");
+                       ortn = NTLM_ERR_PROTOCOL_MISMATCH;
+                       goto errOut;
+               
+               }
+               
+               /* 
+                * Write security buffers.
+                * 
+                * byte 12: LM response
+                * byte 20: NTLM response
+                */
+               appendSecBuf(clientBuf, NTLM_LM_RESPONSE_LEN, &lmRespOffset);
+               appendSecBuf(clientBuf, NTLM_LM_RESPONSE_LEN, &ntlmRespOffset);
+               ntlmResponsePtr = ntlmResp;
+               ntlmResponseLen = NTLM_LM_RESPONSE_LEN;
+       }   /* not NTLMv2 */
+       
+       /* 
+        * convert domain and user as appropriate
+        * byte 28: domain (server) name
+        */
+       if(domain != NULL) {
+               ortn = ntlmStringFlatten(domain, unicode, &domainNameFlat, &domainNameFlatLen);
+               if(ortn) {
+                       dprintf("createClientResponse: error converting domain name\n");
+                       ortn = NTLM_ERR_PARSE_ERR;
+                       goto errOut;
+               }
+       }
+       appendSecBuf(clientBuf, domainNameFlatLen, &domainNameOffset);
+
+       /* byte 36: user name */
+       ortn = ntlmStringFlatten(userName, unicode, &userNameFlat, &userNameFlatLen);
+       if(ortn) {
+               dprintf("createClientResponse: error converting user name\n");
+               ortn = NTLM_ERR_PARSE_ERR;
+               goto errOut;
+       }
+       appendSecBuf(clientBuf, userNameFlatLen, &userNameOffset);
+       
+       /* byte 44: hostname */
+       ortn = ntlmHostName(unicode, &workstationName, &workstationNameLen);
+       if(ortn) {
+               dprintf("createClientResponse: error getting host name\n");
+               goto errOut;
+       }
+       appendSecBuf(clientBuf, workstationNameLen, &workstationNameOffset);
+       
+       /* byte 52: session key (whatever that is): optional, empty here */
+       appendSecBuf(clientBuf, 0, &nullDex);
+       
+       /* byte 60: negotiated flags */
+       appendUint32(clientBuf, ntlmGen->mSentFlags & serverFlags);
+
+       /* finally, the data associated with the security buffers */
+       secBufOffset(clientBuf, lmRespOffset);
+       CFDataAppendBytes(clientBuf, lmResp, NTLM_LM_RESPONSE_LEN);
+
+       secBufOffset(clientBuf, ntlmRespOffset);
+       CFDataAppendBytes(clientBuf, ntlmResponsePtr, ntlmResponseLen);
+
+       if(domain != NULL) {
+               secBufOffset(clientBuf, domainNameOffset);
+               CFDataAppendBytes(clientBuf, domainNameFlat, domainNameFlatLen);
+       }
+       
+       secBufOffset(clientBuf, userNameOffset);
+       CFDataAppendBytes(clientBuf, userNameFlat, userNameFlatLen);
+       
+       secBufOffset(clientBuf, workstationNameOffset);
+       CFDataAppendBytes(clientBuf, workstationName, workstationNameLen);
+
+errOut:
+       CFREE(targetName);
+       CFREE(targetInfo);
+       CFREE(domainNameFlat);
+       CFREE(userNameFlat);
+       CFREE(workstationName);
+       if (domain) CFRelease(domain);
+       if(ntlmResponsePtr != ntlmResp) {
+               /* i.e., it was mallocd by ntlmGenerateNtlmV2Response */
+               CFREE(ntlmResponsePtr);
+       }
+       if(ortn == errSecSuccess) {
+               *clientResponse = clientBuf;
+       }
+       else {
+               if (clientBuf)
+            CFRelease(clientBuf);
+       }
+       return ortn;
+}
+       
+/* replacement for NtlmNegotiatedNtlm2: returns NW_NTLM1Only, NW_NTLM2Only,
+ * or NW_NTLMv2Only */
+NLTM_Which NtlmGetNegotiatedVersion(
+       NtlmGeneratorRef        ntlmGen)
+{
+       return ntlmGen->mNegotiatedVersion;
+}
+
+OSStatus _NtlmGeneratePasswordHashes(
+       CFAllocatorRef alloc,
+       NtlmGeneratorRef ntlm,
+       CFStringRef password,
+       CFDataRef* ntlmHash,
+       CFDataRef* lmHash)
+{
+       OSStatus result = errSecSuccess;
+       unsigned char hash[NTLM_DIGEST_LENGTH];
+       
+       ntlmPasswordHash(password, hash);
+       
+       *ntlmHash = CFDataCreate(alloc, hash, sizeof(hash));
+       
+       result = lmPasswordHash(password, hash);
+       
+       if (result == errSecSuccess)
+               *lmHash = CFDataCreate(alloc, hash, sizeof(hash));
+       
+       return result;
+}
+
+OSStatus NtlmGeneratePasswordHashes(
+       CFAllocatorRef alloc,
+       CFStringRef password,
+       CFDataRef* ntlmHash,
+       CFDataRef* lmHash)
+{
+       NtlmGeneratorRef ntlm = NULL;
+       
+       OSStatus result = NtlmGeneratorCreate(NW_Any, &ntlm);
+       
+       if (result == errSecSuccess) {
+               result = _NtlmGeneratePasswordHashes(alloc, ntlm, password, ntlmHash, lmHash);
+       }
+       
+       if (ntlm)
+               NtlmGeneratorRelease(ntlm);
+       
+       return result;
+}
+