diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/crypto.c | 68 | ||||
-rw-r--r-- | src/crypto.h | 28 | ||||
-rw-r--r-- | src/gss_creds.c | 2 | ||||
-rw-r--r-- | src/ntlm.h | 78 | ||||
-rw-r--r-- | src/ntlm_crypto.c | 152 |
5 files changed, 324 insertions, 4 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 <errno.h> +#include <string.h> +#include <openssl/des.h> #include <openssl/evp.h> #include <openssl/hmac.h> #include <openssl/rand.h> @@ -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; } @@ -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) { |