+/*
+ * 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;
+}
+