#include "ntlmBlobPriv.h"
#include <Security/SecBase.h>
#include <sys/types.h>
#include <sys/uio.h>
#include <unistd.h>
#include <sys/param.h>
#include <stdlib.h>
#include <stdint.h>
#include <assert.h>
#include <fcntl.h>
#include <ctype.h>
#include <strings.h>
#include <CommonCrypto/CommonDigest.h>
#include <CommonCrypto/CommonCryptor.h>
#include <CommonCrypto/CommonHMAC.h>
#include <CoreFoundation/CFDate.h>
#include <Security/SecFramework.h>
#include <Security/SecRandom.h>
#include <utilities/SecCFWrappers.h>
#if DEBUG_FIXED_CHALLENGE
static unsigned char dbgStamp[] =
{
0x00, 0x90, 0xd3, 0x36, 0xb7, 0x34, 0xc3, 0x01
};
#endif
void appendUint64(
CFMutableDataRef buf,
uint64_t word)
{
#if 1
unsigned char cb[8];
OSWriteLittleInt64(cb, 0, word);
CFDataAppendBytes(buf, cb, 8);
#else
CFIndex offset = CFDataGetLength(buf);
UInt8 *bytes = CFDataGetMutableBytePtr(buf);
CFDataIncreaseLength(buf, 8);
OSWriteLittleInt64(bytes, offset, word);
#endif
}
void appendUint32(
CFMutableDataRef buf,
uint32_t word)
{
#if 1
unsigned char cb[4];
OSWriteLittleInt32(cb, 0, word);
CFDataAppendBytes(buf, cb, 4);
#else
CFIndex offset = CFDataGetLength(buf);
UInt8 *bytes = CFDataGetMutableBytePtr(buf);
CFDataIncreaseLength(buf, 4);
OSWriteLittleInt32(bytes, offset, word);
#endif
}
void appendUint16(
CFMutableDataRef buf,
uint16_t word)
{
unsigned char cb[2];
OSWriteLittleInt16(cb, 0, word);
CFDataAppendBytes(buf, cb, 2);
}
void appendSecBuf(
CFMutableDataRef buf,
uint16_t len,
CFIndex *offsetIndex)
{
#if 1
unsigned char cb[8];
OSWriteLittleInt16(cb, 0, len);
OSWriteLittleInt16(cb, 2, len);
OSWriteLittleInt32(cb, 4, 0);
CFDataAppendBytes(buf, cb, 8);
*offsetIndex = CFDataGetLength(buf) - 4;
#else
appendUint16(buf, len);
appendUint16(buf, len);
*offsetIndex = CFDataGetLength(buf);
appendUint32(buf, 0);
#endif
}
void secBufOffset(
CFMutableDataRef buf,
CFIndex offsetIndex)
{
CFIndex currPos = CFDataGetLength(buf);
unsigned char cb[4];
OSWriteLittleInt32(cb, 0, (uint32_t)currPos);
CFRange range = {offsetIndex, 4};
CFDataReplaceBytes(buf, range, cb, 4);
}
OSStatus ntlmParseSecBuffer(
const unsigned char *cp,
const unsigned char *bufStart,
unsigned bufLen,
const unsigned char **data,
uint16_t *dataLen)
{
assert(cp >= bufStart);
uint16_t secBufLen = OSReadLittleInt16(cp, 0);
cp += 4;
uint32_t offset = OSReadLittleInt32(cp, 0);
if((offset + secBufLen) > bufLen) {
dprintf("ntlmParseSecBuffer: buf overflow\n");
return NTLM_ERR_PARSE_ERR;
}
*data = bufStart + offset;
*dataLen = secBufLen;
return errSecSuccess;
}
OSStatus ntlmStringToLE(
CFStringRef pwd,
unsigned char **ucode, unsigned *ucodeLen) {
CFIndex len = CFStringGetLength(pwd);
if (len > NTLM_MAX_STRING_LEN)
return errSecAllocate;
unsigned char *data = (unsigned char *)malloc(len * 2);
if (data == NULL)
return errSecAllocate;
unsigned char *cp = data;
CFIndex dex;
for(dex=0; dex<len; dex++) {
UniChar uc = CFStringGetCharacterAtIndex(pwd, dex);
*cp++ = uc & 0xff;
*cp++ = uc >> 8;
}
*ucode = data;
*ucodeLen = (unsigned)(len * 2);
return errSecSuccess;
}
OSStatus ntlmStringFlatten(
CFStringRef str,
bool unicode,
unsigned char **flat, unsigned *flatLen) {
if(unicode) {
return ntlmStringToLE(str, flat, flatLen);
}
else {
CFIndex strLen = CFStringGetLength(str);
if (strLen > NTLM_MAX_STRING_LEN)
return errSecAllocate;
char *cStr = (char *)malloc(strLen + 1);
if(cStr == NULL) {
return errSecAllocate;
}
if(CFStringGetCString(str, cStr, strLen + 1, kCFStringEncodingASCII)) {
*flat = (unsigned char *)cStr;
*flatLen = (unsigned)strLen;
return errSecSuccess;
}
dprintf("ntlmStringFlatten: ASCII password conversion failed; trying UTF8\n");
free(cStr);
CFDataRef dataFromString = CFStringCreateExternalRepresentation(NULL, str, kCFStringEncodingUTF8, 0);
if(dataFromString) {
*flatLen = (unsigned)CFDataGetLength(dataFromString);
*flat = malloc(*flatLen);
if (*flat == NULL) {
CFRelease(dataFromString);
return errSecAllocate;
}
memcpy(*flat, CFDataGetBytePtr(dataFromString), *flatLen);
CFReleaseNull(dataFromString);
return errSecSuccess;
}
dprintf("lmPasswordHash: UTF8 password conversion failed\n");
CFReleaseNull(dataFromString);
return NTLM_ERR_PARSE_ERR;
}
}
void ntlmRand(
unsigned len,
void *buf)
{
int status;
status=SecRandomCopyBytes(kSecRandomDefault, len, buf);
(void)status; }
OSStatus ntlmHostName(
bool unicode,
unsigned char **flat, unsigned *flatLen) {
char hostname[] = "WORKSTATION";
size_t len = strlen(hostname);
if(unicode) {
*flat = (unsigned char *)malloc(len * 2);
unsigned char *cp = *flat;
size_t dex;
for(dex=0; dex<len; dex++) {
*cp++ = hostname[dex];
*cp++ = 0;
}
*flatLen = (unsigned)len * 2;
return errSecSuccess;
}
else {
*flat = (unsigned char *)malloc(len+1);
*flatLen = (unsigned)len;
memmove(*flat, hostname, len);
flat[len] = NULL; return errSecSuccess;
}
}
CFGiblisGetSingleton(CFAbsoluteTime, ntlmGetBasis, ntlmBasisAbsoluteTime, ^{
*ntlmBasisAbsoluteTime = CFAbsoluteTimeForGregorianZuluDay(1601, 1, 1);
});
void ntlmAppendTimestamp(
CFMutableDataRef ntlmV2Blob)
{
#if DEBUG_FIXED_CHALLENGE
CFDataAppendBytes(ntlmV2Blob, dbgStamp, 8);
#else
CFAbsoluteTime nowTime = CFAbsoluteTimeGetCurrent();
CFTimeInterval elapsed = nowTime - ntlmGetBasis();
elapsed *= 10000000.0;
appendUint64(ntlmV2Blob, (uint64_t)elapsed);
#endif
}
#define NTLM_DIGEST_LENGTH 16
void md4Hash(
const unsigned char *data,
unsigned dataLen,
unsigned char *digest) {
CC_MD4_CTX ctx;
CC_MD4_Init(&ctx);
CC_MD4_Update(&ctx, data, dataLen);
CC_MD4_Final(digest, &ctx);
}
void md5Hash(
const unsigned char *data,
unsigned dataLen,
unsigned char *digest) {
CC_MD5_CTX ctx;
CC_MD5_Init(&ctx);
CC_MD5_Update(&ctx, data, dataLen);
CC_MD5_Final(digest, &ctx);
}
void ntlmMakeDesKey(
const unsigned char *inKey, unsigned char *outKey) {
outKey[0] = inKey[0] & 0xfe;
outKey[1] = ((inKey[0] << 7) | (inKey[1] >> 1)) & 0xfe;
outKey[2] = ((inKey[1] << 6) | (inKey[2] >> 2)) & 0xfe;
outKey[3] = ((inKey[2] << 5) | (inKey[3] >> 3)) & 0xfe;
outKey[4] = ((inKey[3] << 4) | (inKey[4] >> 4)) & 0xfe;
outKey[5] = ((inKey[4] << 3) | (inKey[5] >> 5)) & 0xfe;
outKey[6] = ((inKey[5] << 2) | (inKey[6] >> 6)) & 0xfe;
outKey[7] = (inKey[6] << 1) & 0xfe;
}
OSStatus ntlmDesCrypt(
const unsigned char *key, const unsigned char *inData, unsigned char *outData) {
size_t data_moved;
return CCCrypt(kCCEncrypt, kCCAlgorithmDES, 0, key, kCCKeySizeDES,
NULL , inData, 1 * kCCBlockSizeDES, outData,
1 * kCCBlockSizeDES, &data_moved);
}
OSStatus ntlmHmacMD5(
const unsigned char *key,
unsigned keyLen,
const unsigned char *inData,
unsigned inDataLen,
unsigned char *mac) {
CCHmacContext hmac_md5_context;
CCHmacInit(&hmac_md5_context, kCCHmacAlgMD5, key, keyLen);
CCHmacUpdate(&hmac_md5_context, inData, inDataLen);
CCHmacFinal(&hmac_md5_context, mac);
return errSecSuccess;
}
OSStatus ntlmPasswordHash(
CFStringRef pwd,
unsigned char *digest) {
OSStatus res;
unsigned char *data;
unsigned len;
res = ntlmStringToLE(pwd, &data, &len);
if (res)
return res;
md4Hash(data, len, digest);
free(data);
return 0;
}
#define ALL_KEYS_LENGTH (3 * DES_RAW_KEY_SIZE)
OSStatus lmv2Response(
const unsigned char *digest, const unsigned char *ptext, unsigned char *ntlmResp) {
unsigned char allKeys[ALL_KEYS_LENGTH];
unsigned char key1[DES_KEY_SIZE], key2[DES_KEY_SIZE], key3[DES_KEY_SIZE];
OSStatus ortn;
memmove(allKeys, digest, NTLM_DIGEST_LENGTH);
memset(allKeys + NTLM_DIGEST_LENGTH, 0, ALL_KEYS_LENGTH - NTLM_DIGEST_LENGTH);
ntlmMakeDesKey(allKeys, key1);
ntlmMakeDesKey(allKeys + DES_RAW_KEY_SIZE, key2);
ntlmMakeDesKey(allKeys + (2 * DES_RAW_KEY_SIZE), key3);
ortn = ntlmDesCrypt(key1, ptext, ntlmResp);
if(ortn == errSecSuccess) {
ortn = ntlmDesCrypt(key2, ptext, ntlmResp + DES_BLOCK_SIZE);
}
if(ortn == errSecSuccess) {
ortn = ntlmDesCrypt(key3, ptext, ntlmResp + (2 * DES_BLOCK_SIZE));
}
return ortn;
}