From a121c1b405c43b6fcc48fb87ebbe78b9e9dbb74c Mon Sep 17 00:00:00 2001 From: Simo Sorce Date: Wed, 17 Jul 2013 03:20:16 -0400 Subject: Add NTLM Crypto support functions for NTLMv1 --- src/crypto.c | 68 +++++++++ src/crypto.h | 28 +++- src/gss_creds.c | 2 +- src/ntlm.h | 78 ++++++++++- src/ntlm_crypto.c | 152 +++++++++++++++++++- tests/ntlmssptest.c | 392 +++++++++++++++++++++++++++++++++++++++++++++++++++- 6 files changed, 713 insertions(+), 7 deletions(-) diff --git a/src/crypto.c b/src/crypto.c index 14e6c8d..ab1efbf 100644 --- a/src/crypto.c +++ b/src/crypto.c @@ -16,7 +16,9 @@ */ #include +#include +#include #include #include #include @@ -219,3 +221,69 @@ int RC4K(struct ntlm_buffer *key, return ret; } +int WEAK_DES(struct ntlm_buffer *key, + struct ntlm_buffer *payload, + struct ntlm_buffer *result) +{ + DES_key_schedule schedule; + DES_cblock key8; + + if ((key->length != 7) || + (payload->length != 8) || + (result->length != 8)) { + return EINVAL; + } + + /* Undocumented shuffle needed before calling DES_set_key_unchecked */ + key8[0] = key->data[0]; + key8[1] = (key->data[0] << 7) | (key->data[1] >> 1); + key8[2] = (key->data[1] << 6) | (key->data[2] >> 2); + key8[3] = (key->data[2] << 5) | (key->data[3] >> 3); + key8[4] = (key->data[3] << 4) | (key->data[4] >> 4); + key8[5] = (key->data[4] << 3) | (key->data[5] >> 5); + key8[6] = (key->data[5] << 2) | (key->data[6] >> 6); + key8[7] = (key->data[6] << 1); + + DES_set_key_unchecked(&key8, &schedule); + DES_ecb_encrypt((DES_cblock *)payload->data, + (DES_cblock *)result->data, &schedule, 1); + return 0; +} + +int DESL(struct ntlm_buffer *key, + struct ntlm_buffer *payload, + struct ntlm_buffer *result) +{ + uint8_t buf7[7]; + struct ntlm_buffer key7; + struct ntlm_buffer res8; + + if ((key->length != 16) || + (payload->length != 8) || + (result->length != 24)) { + return EINVAL; + } + + /* part 1 */ + key7.data = key->data; + key7.length = 7; + res8.data = result->data; + res8.length = 8; + WEAK_DES(&key7, payload, &res8); + /* part 2 */ + key7.data = &key->data[7]; + key7.length = 7; + res8.data = &result->data[8]; + res8.length = 8; + WEAK_DES(&key7, payload, &res8); + /* part 3 */ + memcpy(buf7, &key->data[14], 2); + memset(&buf7[2], 0, 5); + key7.data = buf7; + key7.length = 7; + res8.data = &result->data[16]; + res8.length = 8; + WEAK_DES(&key7, payload, &res8); + + return 0; +} diff --git a/src/crypto.h b/src/crypto.h index 3a2f6f9..efce1fc 100644 --- a/src/crypto.h +++ b/src/crypto.h @@ -112,6 +112,32 @@ void RC4_FREE(struct ntlm_rc4_handle **handle); int RC4K(struct ntlm_buffer *key, enum ntlm_cipher_mode mode, struct ntlm_buffer *payload, -struct ntlm_buffer *result); + struct ntlm_buffer *result); + +/** + * @brief Extreely weak DES encryption + * + * @param key The encryption/decryption key (must be 8 bytes) + * @param payload Input buffer (must be 8 bytes) + * @param result Output buffer (must be 8 bytes) + * + * @return 0 on success or EINVAL if any buffer is not 8 in length + */ +int WEAK_DES(struct ntlm_buffer *key, + struct ntlm_buffer *payload, + struct ntlm_buffer *result); + +/** + * @brief A sad weak encryption/expansion scheme needed by NTLMv1 + * + * @param key The encryption/decryption key (must be 16 bytes) + * @param payload Input buffer (must be 8 bytes) + * @param result Output buffer (must be 24 bytes) + * + * @return 0 on success or EINVAL if any buffer is not of proper length + */ +int DESL(struct ntlm_buffer *key, + struct ntlm_buffer *payload, + struct ntlm_buffer *result); #endif /* _SRC_CRYPTO_H_ */ diff --git a/src/gss_creds.c b/src/gss_creds.c index b16f9ca..2f062bd 100644 --- a/src/gss_creds.c +++ b/src/gss_creds.c @@ -98,7 +98,7 @@ static int get_initial_creds(struct gssntlm_name *name, goto done; } cred->cred.user.nt_hash.length = 16; - ret = ntlm_pwd_to_nt_hash(pwd, &cred->cred.user.nt_hash); + ret = NTOWFv1(pwd, &cred->cred.user.nt_hash); goto done; } diff --git a/src/ntlm.h b/src/ntlm.h index 0acdbfc..18cb79a 100644 --- a/src/ntlm.h +++ b/src/ntlm.h @@ -112,7 +112,83 @@ struct ntlm_key { * * @return 0 on success or an error; */ -int ntlm_pwd_to_nt_hash(const char *password, struct ntlm_key *result); +int NTOWFv1(const char *password, struct ntlm_key *result); + +/** + * @brief Turns a utf8 password into an LM Hash + * + * @param password The password + * @param result The returned hash + * + * @return 0 on success or an error; + */ +int LMOWFv1(const char *password, struct ntlm_key *result); + +/** + * @brief Generates a v1 NT Response + * + * @param nt_key The NTLMv1 key computed by NTOWFv1() + * @param ext_sec Whether Extended Security has been negotiated + * @param server_chal[8] The server challenge + * @param client_chal[8] The client challenge (only with Extended Security) + * @param nt_response The output buffer (must be 24 bytes preallocated) + * + * @return 0 on success or ERR_CRYPTO + */ +int ntlm_compute_nt_response(struct ntlm_key *nt_key, bool ext_sec, + uint8_t server_chal[8], uint8_t client_chal[8], + struct ntlm_buffer *nt_response); + +/** + * @brief Generates a v1 LM Response + * + * @param lm_key The LMv1 key computed by LMOWFv1() + * @param ext_sec Whether Extended Security has been negotiated + * @param server_chal[8] The server challenge + * @param client_chal[8] The client challenge (only with Extended Security) + * @param lm_response The output buffer (must be 24 bytes preallocated) + * + * @return 0 on success or ERR_CRYPTO + */ +int ntlm_compute_lm_response(struct ntlm_key *lm_key, bool ext_sec, + uint8_t server_chal[8], uint8_t client_chal[8], + struct ntlm_buffer *lm_response); + +/** + * @brief Returns the v1 session key + * + * @param nt_key The NTLMv1 key computed by NTOWFv1() + * @param session_base_key The output buffer (must be 16 bytes preallocated) + * + * @return 0 on success or ERR_CRYPTO + */ +int ntlm_session_base_key(struct ntlm_key *nt_key, + struct ntlm_key *session_base_key); + +/** + * @brief V1 Key Exchange Key calculation + * + * @param ctx An ntlm context + * @param ext_sec Whether Extended Security has been negotiated + * @param neg_lm_key Whether LM KEY has been negotiated + * @param non_nt_sess_key Whether non NT Session Key has been negotiated + * @param server_chal The server challenge (only with Extended Security) + * @param lm_key The LMv1 key computed by LMOWFv1() + * @param session_base_key The Session Base Key + * @param lm_response The LM v1 Response + * @param key_exchange_key The output buffer (must be 16 bytes preallocated) + * + * @return 0 on success or ERR_CRYPTO + */ +int KXKEY(struct ntlm_ctx *ctx, + bool ext_sec, + bool neg_lm_key, + bool non_nt_sess_key, + uint8_t server_chal[8], + struct ntlm_key *lm_key, + struct ntlm_key *session_base_key, + struct ntlm_buffer *lm_response, + struct ntlm_key *key_exchange_key); /** * @brief Generates a NTLMv2 Response Key diff --git a/src/ntlm_crypto.c b/src/ntlm_crypto.c index c94ea79..d999d0f 100644 --- a/src/ntlm_crypto.c +++ b/src/ntlm_crypto.c @@ -81,7 +81,7 @@ struct wire_ntlmv2_cli_chal { #define MAX_USER_DOM_LEN 512 -int ntlm_pwd_to_nt_hash(const char *password, struct ntlm_key *result) +int NTOWFv1(const char *password, struct ntlm_key *result) { struct ntlm_buffer payload; struct ntlm_buffer hash; @@ -106,6 +106,156 @@ int ntlm_pwd_to_nt_hash(const char *password, struct ntlm_key *result) return ret; } +#define DES_CONST "KGS!@#$%" +int LMOWFv1(const char *password, struct ntlm_key *result) +{ + struct ntlm_buffer key; + struct ntlm_buffer plain; + struct ntlm_buffer cipher; + char upcased[15]; + char *retstr; + size_t out; + size_t len; + int ret; + + if (result->length != 16) return EINVAL; + + len = strlen(password); + if (len > 14) return ERANGE; + + out = 15; + retstr = (char *)u8_toupper((const uint8_t *)password, len, + NULL, NULL, (uint8_t *)upcased, &out); + if (!retstr) return ERR_CRYPTO; + if (retstr != upcased) { + free(retstr); + ret = EINVAL; + } + memset(&upcased[len], 0, 15 - len); + + /* part1 */ + key.data = (uint8_t *)upcased; + key.length = 7; + plain.data = discard_const(DES_CONST); + plain.length = 8; + cipher.data = result->data; + cipher.length = 8; + ret = WEAK_DES(&key, &plain, &cipher); + if (ret) return ret; + + /* part2 */ + key.data = (uint8_t *)&upcased[7]; + key.length = 7; + plain.data = discard_const(DES_CONST); + plain.length = 8; + cipher.data = &result->data[8]; + cipher.length = 8; + return WEAK_DES(&key, &plain, &cipher); +} + +int ntlm_compute_nt_response(struct ntlm_key *nt_key, bool ext_sec, + uint8_t server_chal[8], uint8_t client_chal[8], + struct ntlm_buffer *nt_response) +{ + struct ntlm_buffer key = { nt_key->data, nt_key->length }; + struct ntlm_buffer payload; + struct ntlm_buffer result; + uint8_t buf1[16]; + uint8_t buf2[16]; + int ret; + + memcpy(buf1, server_chal, 8); + if (ext_sec) { + memcpy(&buf1[8], client_chal, 8); + payload.data = buf1; + payload.length = 16; + result.data = buf2; + result.length = 16; + ret = MD5_HASH(&payload, &result); + if (ret) return ret; + memcpy(buf1, result.data, 8); + } + payload.data = buf1; + payload.length = 8; + + return DESL(&key, &payload, nt_response); +} + +int ntlm_compute_lm_response(struct ntlm_key *lm_key, bool ext_sec, + uint8_t server_chal[8], uint8_t client_chal[8], + struct ntlm_buffer *lm_response) +{ + struct ntlm_buffer key = { lm_key->data, lm_key->length }; + struct ntlm_buffer payload = { server_chal, 8 }; + + if (ext_sec) { + memcpy(lm_response->data, client_chal, 8); + memset(&lm_response->data[8], 0, 16); + return 0; + } + return DESL(&key, &payload, lm_response); +} + +int ntlm_session_base_key(struct ntlm_key *nt_key, + struct ntlm_key *session_base_key) +{ + struct ntlm_buffer payload = { nt_key->data, nt_key->length }; + struct ntlm_buffer hash = { session_base_key->data, + session_base_key->length }; + + return MD4_HASH(&payload, &hash); +} + +int KXKEY(struct ntlm_ctx *ctx, + bool ext_sec, + bool neg_lm_key, + bool non_nt_sess_key, + uint8_t server_chal[8], + struct ntlm_key *lm_key, + struct ntlm_key *session_base_key, + struct ntlm_buffer *lm_response, + struct ntlm_key *key_exchange_key) +{ + struct ntlm_buffer payload; + struct ntlm_buffer result; + struct ntlm_buffer key; + uint8_t buf[16]; + int ret = 0; + + if (ext_sec) { + key.data = session_base_key->data; + key.length = session_base_key->length; + memcpy(buf, server_chal, 8); + memcpy(&buf[8], lm_response->data, 8); + payload.data = buf; + payload.length = 16; + result.data = key_exchange_key->data; + result.length = key_exchange_key->length; + ret = HMAC_MD5(&key, &payload, &result); + } else if (neg_lm_key) { + payload.data = lm_response->data; + payload.length = 8; + key.data = lm_key->data; + key.length = 7; + result.data = key_exchange_key->data; + result.length = 8; + ret = WEAK_DES(&key, &payload, &result); + if (ret) return ret; + buf[0] = lm_key->data[7]; + memset(&buf[1], 0xbd, 6); + key.data = buf; + result.data = &key_exchange_key->data[8]; + result.length = 8; + ret = WEAK_DES(&key, &payload, &result); + } else if (non_nt_sess_key) { + memcpy(key_exchange_key->data, lm_key, 8); + memset(&key_exchange_key->data[8], 0, 8); + } else { + memcpy(key_exchange_key->data, session_base_key->data, 16); + } + return ret; +} + int NTOWFv2(struct ntlm_ctx *ctx, struct ntlm_key *nt_hash, const char *user, const char *domain, struct ntlm_key *result) { diff --git a/tests/ntlmssptest.c b/tests/ntlmssptest.c index 4f653c4..176eee4 100644 --- a/tests/ntlmssptest.c +++ b/tests/ntlmssptest.c @@ -97,6 +97,138 @@ uint8_t T_ServerChallenge[] = { 0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef }; +/* NTLMv1 Auth Test Data */ +struct { + struct ntlm_key ResponseKeyLM; + struct ntlm_key ResponseKeyNT; + struct ntlm_key SessionBaseKey; + uint8_t LMv1Response[24]; + uint8_t NTLMv1Response[24]; + struct ntlm_key KeyExchangeKey; + struct ntlm_key EncryptedSessionKey1; + struct ntlm_key EncryptedSessionKey2; + struct ntlm_key EncryptedSessionKey3; + uint32_t ChallengeFlags; + uint8_t ChallengeMessage[0x44]; + /* Version field differs from the one MS DOCS generated */ + uint8_t EncChallengeMessage[0x44]; + uint8_t AuthenticateMessage[0xAC]; +} T_NTLMv1 = { + { + .data = { + 0xe5, 0x2c, 0xac, 0x67, 0x41, 0x9a, 0x9a, 0x22, + 0x4a, 0x3b, 0x10, 0x8f, 0x3f, 0xa6, 0xcb, 0x6d + }, + .length = 16 + }, + { + .data = { + 0xa4, 0xf4, 0x9c, 0x40, 0x65, 0x10, 0xbd, 0xca, + 0xb6, 0x82, 0x4e, 0xe7, 0xc3, 0x0f, 0xd8, 0x52 + }, + .length = 16 + }, + { + .data = { + 0xd8, 0x72, 0x62, 0xb0, 0xcd, 0xe4, 0xb1, 0xcb, + 0x74, 0x99, 0xbe, 0xcc, 0xcd, 0xf1, 0x07, 0x84 + }, + .length = 16 + }, + { + 0x98, 0xde, 0xf7, 0xb8, 0x7f, 0x88, 0xaa, 0x5d, + 0xaf, 0xe2, 0xdf, 0x77, 0x96, 0x88, 0xa1, 0x72, + 0xde, 0xf1, 0x1c, 0x7d, 0x5c, 0xcd, 0xef, 0x13 + }, + { + 0x67, 0xc4, 0x30, 0x11, 0xf3, 0x02, 0x98, 0xa2, + 0xad, 0x35, 0xec, 0xe6, 0x4f, 0x16, 0x33, 0x1c, + 0x44, 0xbd, 0xbe, 0xd9, 0x27, 0x84, 0x1f, 0x94 + }, + { + .data = { + 0xb0, 0x9e, 0x37, 0x9f, 0x7f, 0xbe, 0xcb, 0x1e, + 0xaf, 0x0a, 0xfd, 0xcb, 0x03, 0x83, 0xc8, 0xa0 + }, + .length = 16 + }, + { + .data = { + 0x51, 0x88, 0x22, 0xb1, 0xb3, 0xf3, 0x50, 0xc8, + 0x95, 0x86, 0x82, 0xec, 0xbb, 0x3e, 0x3c, 0xb7 + }, + .length = 16 + }, + { + .data = { + 0x74, 0x52, 0xca, 0x55, 0xc2, 0x25, 0xa1, 0xca, + 0x04, 0xb4, 0x8f, 0xae, 0x32, 0xcf, 0x56, 0xfc + }, + .length = 16 + }, + { + .data = { + 0x4c, 0xd7, 0xbb, 0x57, 0xd6, 0x97, 0xef, 0x9b, + 0x54, 0x9f, 0x02, 0xb8, 0xf9, 0xb3, 0x78, 0x64 + }, + .length = 16 + }, + ( + NTLMSSP_NEGOTIATE_56 | NTLMSSP_NEGOTIATE_KEY_EXCH | + NTLMSSP_NEGOTIATE_128 | NTLMSSP_NEGOTIATE_VERSION | + NTLMSSP_TARGET_TYPE_SERVER | + NTLMSSP_NEGOTIATE_ALWAYS_SIGN | NTLMSSP_NEGOTIATE_NTLM | + NTLMSSP_NEGOTIATE_SEAL | NTLMSSP_NEGOTIATE_SIGN | + NTLMSSP_NEGOTIATE_OEM | NTLMSSP_NEGOTIATE_UNICODE + ), + { + 0x4e, 0x54, 0x4c, 0x4d, 0x53, 0x53, 0x50, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x0c, 0x00, + 0x38, 0x00, 0x00, 0x00, 0x33, 0x82, 0x02, 0xe2, + 0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x06, 0x00, 0x70, 0x17, 0x00, 0x00, 0x00, 0x0f, + 0x53, 0x00, 0x65, 0x00, 0x72, 0x00, 0x76, 0x00, + 0x65, 0x00, 0x72, 0x00 + }, + { + 0x4e, 0x54, 0x4c, 0x4d, 0x53, 0x53, 0x50, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x0c, 0x00, + 0x38, 0x00, 0x00, 0x00, 0x33, 0x82, 0x02, 0xe2, + 0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x06, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, + 0x53, 0x00, 0x65, 0x00, 0x72, 0x00, 0x76, 0x00, + 0x65, 0x00, 0x72, 0x00 + }, + { + 0x4e, 0x54, 0x4c, 0x4d, 0x53, 0x53, 0x50, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x18, 0x00, 0x18, 0x00, + 0x6c, 0x00, 0x00, 0x00, 0x18, 0x00, 0x18, 0x00, + 0x84, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x0c, 0x00, + 0x48, 0x00, 0x00, 0x00, 0x08, 0x00, 0x08, 0x00, + 0x54, 0x00, 0x00, 0x00, 0x10, 0x00, 0x10, 0x00, + 0x5c, 0x00, 0x00, 0x00, 0x10, 0x00, 0x10, 0x00, + 0x9c, 0x00, 0x00, 0x00, 0x35, 0x82, 0x80, 0xe2, + 0x05, 0x01, 0x28, 0x0a, 0x00, 0x00, 0x00, 0x0f, + 0x44, 0x00, 0x6f, 0x00, 0x6d, 0x00, 0x61, 0x00, + 0x69, 0x00, 0x6e, 0x00, 0x55, 0x00, 0x73, 0x00, + 0x65, 0x00, 0x72, 0x00, 0x43, 0x00, 0x4f, 0x00, + 0x4d, 0x00, 0x50, 0x00, 0x55, 0x00, 0x54, 0x00, + 0x45, 0x00, 0x52, 0x00, 0x98, 0xde, 0xf7, 0xb8, + 0x7f, 0x88, 0xaa, 0x5d, 0xaf, 0xe2, 0xdf, 0x77, + 0x96, 0x88, 0xa1, 0x72, 0xde, 0xf1, 0x1c, 0x7d, + 0x5c, 0xcd, 0xef, 0x13, 0x67, 0xc4, 0x30, 0x11, + 0xf3, 0x02, 0x98, 0xa2, 0xad, 0x35, 0xec, 0xe6, + 0x4f, 0x16, 0x33, 0x1c, 0x44, 0xbd, 0xbe, 0xd9, + 0x27, 0x84, 0x1f, 0x94, 0x51, 0x88, 0x22, 0xb1, + 0xb3, 0xf3, 0x50, 0xc8, 0x95, 0x86, 0x82, 0xec, + 0xbb, 0x3e, 0x3c, 0xb7 + } +}; + /* NTLMv2 Auth Test Data */ struct { uint32_t ChallengeFlags; @@ -223,13 +355,119 @@ struct { } }; +int test_LMOWFv1(struct ntlm_ctx *ctx) +{ + struct ntlm_key result = { .length = 16 }; + int ret; + + ret = LMOWFv1(T_Passwd, &result); + if (ret) return ret; + + if (memcmp(result.data, T_NTLMv1.ResponseKeyLM.data, 16) != 0) { + fprintf(stderr, "results differ!\n"); + fprintf(stderr, "expected %s\n", + hex_to_str_16(T_NTLMv1.ResponseKeyLM.data)); + fprintf(stderr, "obtained %s\n", + hex_to_str_16(result.data)); + ret = EINVAL; + } + + return ret; +} + +int test_NTOWFv1(struct ntlm_ctx *ctx) +{ + struct ntlm_key result = { .length = 16 }; + int ret; + + ret = NTOWFv1(T_Passwd, &result); + if (ret) return ret; + + if (memcmp(result.data, T_NTLMv1.ResponseKeyNT.data, 16) != 0) { + fprintf(stderr, "results differ!\n"); + fprintf(stderr, "expected %s\n", + hex_to_str_16(T_NTLMv1.ResponseKeyNT.data)); + fprintf(stderr, "obtained %s\n", + hex_to_str_16(result.data)); + ret = EINVAL; + } + + return ret; +} + +int test_SessionBaseKeyV1(struct ntlm_ctx *ctx) +{ + struct ntlm_key session_base_key = { .length = 16 }; + int ret; + + ret = ntlm_session_base_key(&T_NTLMv1.ResponseKeyNT, &session_base_key); + if (ret) return ret; + + if (memcmp(session_base_key.data, T_NTLMv1.SessionBaseKey.data, 16) != 0) { + fprintf(stderr, "results differ!\n"); + fprintf(stderr, "expected %s\n", + hex_to_str_16(T_NTLMv1.SessionBaseKey.data)); + fprintf(stderr, "obtained %s\n", + hex_to_str_16(session_base_key.data)); + ret = EINVAL; + } + + return ret; +} + +int test_LMResponseV1(struct ntlm_ctx *ctx) +{ + uint8_t buf[24]; + struct ntlm_buffer result = { buf, 24 }; + int ret; + + ret = ntlm_compute_lm_response(&T_NTLMv1.ResponseKeyLM, false, + T_ServerChallenge, T_ClientChallenge, + &result); + if (ret) return ret; + + if (memcmp(result.data, T_NTLMv1.LMv1Response, 16) != 0) { + fprintf(stderr, "results differ!\n"); + fprintf(stderr, "expected %s\n", + hex_to_str_16(T_NTLMv1.LMv1Response)); + fprintf(stderr, "obtained %s\n", + hex_to_str_16(result.data)); + ret = EINVAL; + } + + return ret; +} + +int test_NTResponseV1(struct ntlm_ctx *ctx) +{ + uint8_t buf[24]; + struct ntlm_buffer result = { buf, 24 }; + int ret; + + ret = ntlm_compute_nt_response(&T_NTLMv1.ResponseKeyNT, false, + T_ServerChallenge, T_ClientChallenge, + &result); + if (ret) return ret; + + if (memcmp(result.data, T_NTLMv1.NTLMv1Response, 16) != 0) { + fprintf(stderr, "results differ!\n"); + fprintf(stderr, "expected %s\n", + hex_to_str_16(T_NTLMv1.NTLMv1Response)); + fprintf(stderr, "obtained %s\n", + hex_to_str_16(result.data)); + ret = EINVAL; + } + + return ret; +} + int test_NTOWFv2(struct ntlm_ctx *ctx) { struct ntlm_key nt_hash = { .length = 16 }; struct ntlm_key result = { .length = 16 }; int ret; - ret = ntlm_pwd_to_nt_hash(T_Passwd, &nt_hash); + ret = NTOWFv1(T_Passwd, &nt_hash); if (ret) return ret; ret = NTOWFv2(ctx, &nt_hash, T_User, T_UserDom, &result); @@ -344,6 +582,118 @@ int test_EncryptedSessionKey(struct ntlm_ctx *ctx, return ret; } +int test_EncryptedSessionKey1(struct ntlm_ctx *ctx) +{ + struct ntlm_buffer lm_response = { T_NTLMv1.LMv1Response, 24 }; + struct ntlm_key key_exchnage_key = { .length = 16 }; + int ret; + + ret = KXKEY(ctx, false, false, false, T_ServerChallenge, + &T_NTLMv1.ResponseKeyLM, &T_NTLMv1.SessionBaseKey, + &lm_response, &key_exchnage_key); + if (ret) return ret; + + return test_EncryptedSessionKey(ctx, &key_exchnage_key, + &T_NTLMv1.EncryptedSessionKey1); +} + +int test_EncryptedSessionKey2(struct ntlm_ctx *ctx) +{ + struct ntlm_buffer lm_response = { T_NTLMv1.LMv1Response, 24 }; + struct ntlm_key key_exchnage_key = { .length = 16 }; + int ret; + + ret = KXKEY(ctx, false, false, true, T_ServerChallenge, + &T_NTLMv1.ResponseKeyLM, &T_NTLMv1.SessionBaseKey, + &lm_response, &key_exchnage_key); + if (ret) return ret; + + return test_EncryptedSessionKey(ctx, &key_exchnage_key, + &T_NTLMv1.EncryptedSessionKey2); +} + +int test_EncryptedSessionKey3(struct ntlm_ctx *ctx) +{ + struct ntlm_buffer lm_response = { T_NTLMv1.LMv1Response, 24 }; + struct ntlm_key key_exchnage_key = { .length = 16 }; + int ret; + + ret = KXKEY(ctx, false, true, false, T_ServerChallenge, + &T_NTLMv1.ResponseKeyLM, &T_NTLMv1.SessionBaseKey, + &lm_response, &key_exchnage_key); + if (ret) return ret; + + return test_EncryptedSessionKey(ctx, &key_exchnage_key, + &T_NTLMv1.EncryptedSessionKey3); +} + +int test_DecodeChallengeMessageV1(struct ntlm_ctx *ctx) +{ + struct ntlm_buffer chal_msg = { T_NTLMv1.ChallengeMessage, 0x68 }; + uint32_t type; + uint32_t flags; + char *target_name = NULL; + uint8_t chal[8]; + struct ntlm_buffer challenge = { chal, 8 }; + int ret; + + ret = ntlm_decode_msg_type(ctx, &chal_msg, &type); + if (ret) return ret; + if (type != 2) return EINVAL; + + ret = ntlm_decode_chal_msg(ctx, &chal_msg, &flags, &target_name, + &challenge, NULL); + if (ret) return ret; + + if (flags != T_NTLMv1.ChallengeFlags) { + fprintf(stderr, "flags differ!\n"); + fprintf(stderr, "expected %d\n", T_NTLMv1.ChallengeFlags); + fprintf(stderr, "obtained %d\n", flags); + ret = EINVAL; + } + + if (strcmp(target_name, T_Server_Name) != 0) { + fprintf(stderr, "Target Names differ!\n"); + fprintf(stderr, "expected %s\n", T_Server_Name); + fprintf(stderr, "obtained %s\n", target_name); + ret = EINVAL; + } + + if (memcmp(chal, T_ServerChallenge, 8) != 0) { + fprintf(stderr, "Challenges differ!\n"); + fprintf(stderr, "expected %s\n", hex_to_str_8(T_ServerChallenge)); + fprintf(stderr, "obtained %s\n", hex_to_str_8(chal)); + ret = EINVAL; + } + + free(target_name); + return ret; +} + +int test_EncodeChallengeMessageV1(struct ntlm_ctx *ctx) +{ + struct ntlm_buffer challenge = { T_ServerChallenge, 8 }; + struct ntlm_buffer message = { 0 }; + int ret; + + ret = ntlm_encode_chal_msg(ctx, T_NTLMv1.ChallengeFlags, T_Server_Name, + &challenge, NULL, &message); + if (ret) return ret; + + if ((message.length != 0x44) || + (memcmp(message.data, T_NTLMv1.EncChallengeMessage, 0x44) != 0)) { + fprintf(stderr, "Challenge Messages differ!\n"); + fprintf(stderr, "expected:\n%s", + hex_to_dump(T_NTLMv1.EncChallengeMessage, 0x44)); + fprintf(stderr, "obtained:\n%s", + hex_to_dump(message.data, message.length)); + ret = EINVAL; + } + + free(message.data); + return ret; +} + int test_DecodeChallengeMessageV2(struct ntlm_ctx *ctx) { struct ntlm_buffer chal_msg = { T_NTLMv2.ChallengeMessage, 0x68 }; @@ -530,8 +880,44 @@ int main(int argc, const char *argv[]) ret = ntlm_init_ctx(&ctx); if (ret) goto done; - fprintf(stdout, "Test NTOWFv2\n"); - ret = test_NTOWFv2(ctx); + fprintf(stdout, "Test LMOWFv1\n"); + ret = test_LMOWFv1(ctx); + fprintf(stdout, "Test: %s\n", (ret ? "FAIL":"SUCCESS")); + + fprintf(stdout, "Test NTOWFv1\n"); + ret = test_NTOWFv1(ctx); + fprintf(stdout, "Test: %s\n", (ret ? "FAIL":"SUCCESS")); + + fprintf(stdout, "Test LMResponse v1\n"); + ret = test_LMResponseV1(ctx); + fprintf(stdout, "Test: %s\n", (ret ? "FAIL":"SUCCESS")); + + fprintf(stdout, "Test NTResponse v1\n"); + ret = test_NTResponseV1(ctx); + fprintf(stdout, "Test: %s\n", (ret ? "FAIL":"SUCCESS")); + + fprintf(stdout, "Test SessionBaseKey v1\n"); + ret = test_SessionBaseKeyV1(ctx); + fprintf(stdout, "Test: %s\n", (ret ? "FAIL":"SUCCESS")); + + fprintf(stdout, "Test EncryptedSessionKey v1 (1)\n"); + ret = test_EncryptedSessionKey1(ctx); + fprintf(stdout, "Test: %s\n", (ret ? "FAIL":"SUCCESS")); + + fprintf(stdout, "Test EncryptedSessionKey v1 (2)\n"); + ret = test_EncryptedSessionKey2(ctx); + fprintf(stdout, "Test: %s\n", (ret ? "FAIL":"SUCCESS")); + + fprintf(stdout, "Test EncryptedSessionKey v1 (3)\n"); + ret = test_EncryptedSessionKey3(ctx); + fprintf(stdout, "Test: %s\n", (ret ? "FAIL":"SUCCESS")); + + fprintf(stdout, "Test decoding ChallengeMessage v1\n"); + ret = test_DecodeChallengeMessageV1(ctx); + fprintf(stdout, "Test: %s\n", (ret ? "FAIL":"SUCCESS")); + + fprintf(stdout, "Test encoding ChallengeMessage v1\n"); + ret = test_EncodeChallengeMessageV1(ctx); fprintf(stdout, "Test: %s\n", (ret ? "FAIL":"SUCCESS")); fprintf(stdout, "Test LMResponse v2\n"); -- cgit