Hi Daniel/Steve/Colin/All, Thanks for the valuable comments!
We have incorporated the comments provided by you. Could you please help us in writing the helper for Big Endian for the below line: FileName: curl_ntlm_core.c *memcpy(ptr+24, &tw, 8); /*Re-Write this line for Big Endian*/* Please find attached the new GIT diff. Thanks again! Regards Prashant On Tue, Jan 21, 2014 at 4:28 PM, Prash Dush <pradush...@gmail.com> wrote: > Hi Steve/All, > > > We have written NTLMv2 authentication algorithm in Curl code using the > following link. > http://davenport.sourceforge.net/ntlm.html > > Also we are done with some basic testing of the code and it seems working > fine over NTLMv2 authentication. > > > We need to check-in these changes curl GIT, we followed the process > explained in following URL: > > http://curl.haxx.se/dev/contribute.html > > And collected diff of our code changes, PFA diff patch. > > > Kindly review the code changes and provide your valuable feedback. > > > Looking forward to the support in check-in for NTLMv2 Code in curl GIT. > > > > Thanks, > > Prashant > > >
diff --git a/lib/curl_ntlm.c b/lib/curl_ntlm.c index f0dbb16..0995eab 100644 --- a/lib/curl_ntlm.c +++ b/lib/curl_ntlm.c @@ -235,6 +235,12 @@ void Curl_http_ntlm_cleanup(struct connectdata *conn) #else (void)conn; #endif + +#ifndef USE_WINDOWS_SSPI + Curl_safefree(conn->ntlm.target_info); + conn->ntlm.target_info_len = 0; +#endif + } #endif /* USE_NTLM */ diff --git a/lib/curl_ntlm_core.c b/lib/curl_ntlm_core.c index 79aeb08..6ae2fe4 100644 --- a/lib/curl_ntlm_core.c +++ b/lib/curl_ntlm_core.c @@ -96,12 +96,16 @@ #include "rawstr.h" #include "curl_memory.h" #include "curl_ntlm_core.h" +#include "warnless.h" #define _MPRINTF_REPLACE /* use our functions only */ #include <curl/mprintf.h> /* The last #include file should be: */ #include "memdebug.h" +#include "curl_md5.h" +#include "curl_hmac.h" + #ifdef USE_SSLEAY /* @@ -377,6 +381,16 @@ static void ascii_to_unicode_le(unsigned char *dest, const char *src, } } +static void ascii_uppercase_to_unicode_le(unsigned char *dest, const char *src, + size_t srclen) +{ + size_t i; + for(i = 0; i < srclen; i++) { + dest[2 * i] = (unsigned char)(toupper(src[i])); + dest[2 * i + 1] = '\0'; + } +} + /* * Set up nt hashed passwords */ @@ -431,6 +445,180 @@ CURLcode Curl_ntlm_core_mk_nt_hash(struct SessionHandle *data, return CURLE_OK; } + + +/* This returns the HMAC MD5 digest */ +CURLcode Curl_hmac_md5(const unsigned char *key, unsigned int keylen, + const unsigned char *data, unsigned int datalen, + unsigned char *output) +{ + HMAC_context *ctxt = Curl_HMAC_init(Curl_HMAC_MD5, key, keylen); + + if(!ctxt) + return CURLE_OUT_OF_MEMORY; + + /* Update the digest with the given challenge */ + Curl_HMAC_update(ctxt, data, datalen); + + /* Finalise the digest */ + Curl_HMAC_final(ctxt, output); + + return CURLE_OK; +} + + +/* This creates the NTLMv2 hash by using NTLM hash as the key and Unicode + * (uppercase UserName + Domain) as the data + */ +CURLcode Curl_ntlm_core_mk_ntlmv2_hash(const char *user, + size_t userlen, + const char *domain, + size_t domlen, + unsigned char *ntlmhash, + unsigned char *ntlmv2hash) +{ + /* Unicode representation */ + size_t identity_len = (userlen + domlen) * 2; + unsigned char *identity = malloc(identity_len); + CURLcode res = CURLE_OK; + + if(!identity) + return CURLE_OUT_OF_MEMORY; + + ascii_uppercase_to_unicode_le(identity, user, userlen); + ascii_to_unicode_le(identity+(userlen<<1), domain, domlen); + + res = Curl_hmac_md5(ntlmhash, 16, identity, curlx_uztoui(identity_len), + ntlmv2hash); + + Curl_safefree(identity); + return res; +} + + +/* + * Curl_ntlm_core_mk_ntlmv2_resp() + * + * This creates the NTLMv2 response as per + * http://davenport.sourceforge.net/ntlm.html + * This response will be set in the Type 3 response + * + * Parameters: + * + * ntlmv2hash [in] - The ntlmv2 hash (16 bytes) + * challenge_client [in] - The client nonce (8 bytes) + * ntlm [in] - The ntlm data struct being used to read TargetInfo + and Server challenge received in the Type2 message + * ntresp [out] - The address where a pointer to newly allocated + * memory holding the NTLMv2 response. + * ntresp_len [out] - The length of the output message. + * + * Returns CURLE_OK on success. + */ +CURLcode Curl_ntlm_core_mk_ntlmv2_resp(unsigned char *ntlmv2hash, + unsigned char *challenge_client, + struct ntlmdata *ntlm, + unsigned char **ntresp, + unsigned int *ntresp_len) +{ +/* NTLMv2 response structure : +------------------------------------------------------------------------------ +0 HMAC MD5 16 bytes +------BLOB-------------------------------------------------------------------- +16 Signature 0x01010000 +20 Reserved long (0x00000000) +24 Timestamp LE, 64-bit signed value representing the number of + tenths of a microsecond since January 1, 1601. +32 Client Nonce 8 bytes +40 Unknown 4 bytes +44 Target Info N bytes (from the Type 2 message). +44+N Unknown 4 bytes +------------------------------------------------------------------------------ +*/ + +#define NTLM_HMAC_MD5_LEN (16) +#define NTLMv2_BLOB_SIGNATURE "\x01\x01\x00\x00" +#define BLOB_LEN (44 -16 + ntlm->target_info_len + 4) + + unsigned char *ptr = NULL; + unsigned char hmac_output[NTLM_HMAC_MD5_LEN]; + long long tw; + CURLcode res = CURLE_OK; + tw = ((size_t)time(NULL) + 11644473600LLU) * 10000000LLU; + + *ntresp_len = NTLM_HMAC_MD5_LEN + BLOB_LEN; + *ntresp = malloc(*ntresp_len); + if(!*ntresp) + return CURLE_OUT_OF_MEMORY; + + ptr = *ntresp; + memset(ptr, 0, *ntresp_len); + + /* Create the BLOB structure */ + snprintf((char *)ptr + NTLM_HMAC_MD5_LEN, BLOB_LEN, + NTLMv2_BLOB_SIGNATURE + "%c%c%c%c", /* Reserved = 0 */ + 0, 0, 0, 0); + + memcpy(ptr+24, &tw, 8); /*Re-Write this line for Big Endian*/ + memcpy(ptr+32, challenge_client, 8); + memcpy(ptr+44, ntlm->target_info, ntlm->target_info_len); + + /* Concatenate the Type 2 challenge with the BLOB and do HMAC MD5 */ + memcpy(ptr+8, &ntlm->nonce[0], 8); + res = Curl_hmac_md5(ntlmv2hash, NTLM_HMAC_MD5_LEN, ptr+8, BLOB_LEN + 8, + hmac_output); + if(res) { + Curl_safefree(*ntresp); + *ntresp_len = 0; + return res; + } + + /* Concatenate the HMAC MD5 output with the BLOB*/ + memcpy(ptr, hmac_output, NTLM_HMAC_MD5_LEN); + return CURLE_OK; +} + + +/* + * Curl_ntlm_core_mk_lmv2_resp() + * + * This creates the LMv2 response as per + * http://davenport.sourceforge.net/ntlm.html + * This response will be set in the Type 3 message alongwith NTLMv2 response + * + * Parameters: + * + * ntlmv2hash [in] - The ntlmv2 hash (16 bytes) + * challenge_client [in] - The client nonce (8 bytes) + * challenge_client [in] - The server challenge (8 bytes) + * lmresp [out] - The LMv2 response (24 bytes) + * + * Returns CURLE_OK on success. + */ +CURLcode Curl_ntlm_core_mk_lmv2_resp(unsigned char *ntlmv2hash, + unsigned char *challenge_client, + unsigned char *challenge_server, + unsigned char *lmresp) +{ + unsigned char data[16]; + unsigned char hmac_output[16]; + CURLcode res = CURLE_OK; + + memcpy(&data[0], challenge_server, 8); + memcpy(&data[8], challenge_client, 8); + + res = Curl_hmac_md5(ntlmv2hash, 16, &data[0], 16, hmac_output); + if(res) + return res; + + /* Concatenate the HMAC MD5 output with the client nonce */ + memcpy(lmresp, hmac_output, 16); + memcpy(lmresp+16, challenge_client, 8); + + return CURLE_OK; +} + #endif /* USE_NTRESPONSES */ #endif /* USE_NTLM && !USE_WINDOWS_SSPI */ diff --git a/lib/curl_ntlm_core.h b/lib/curl_ntlm_core.h index 9aa126b..80685b8 100644 --- a/lib/curl_ntlm_core.h +++ b/lib/curl_ntlm_core.h @@ -58,9 +58,34 @@ void Curl_ntlm_core_mk_lm_hash(struct SessionHandle *data, unsigned char *lmbuffer /* 21 bytes */); #if USE_NTRESPONSES +CURLcode Curl_hmac_md5(const unsigned char *key, + unsigned int keylen, + const unsigned char *data, + unsigned int datalen, + unsigned char *output); + CURLcode Curl_ntlm_core_mk_nt_hash(struct SessionHandle *data, const char *password, unsigned char *ntbuffer /* 21 bytes */); + +CURLcode Curl_ntlm_core_mk_ntlmv2_hash(const char *user, + size_t userlen, + const char *domain, + size_t domlen, + unsigned char *ntlmhash, + unsigned char *ntlmv2hash); + +CURLcode Curl_ntlm_core_mk_ntlmv2_resp(unsigned char *ntlmv2hash, + unsigned char *challenge_client, + struct ntlmdata *ntlm, + unsigned char **ntresp, + unsigned int *ntresp_len); + +CURLcode Curl_ntlm_core_mk_lmv2_resp(unsigned char *ntlmv2hash, + unsigned char *challenge_client, + unsigned char *challenge_server, + unsigned char *lmresp); + #endif #endif /* USE_NTLM && !USE_WINDOWS_SSPI */ diff --git a/lib/curl_ntlm_msgs.c b/lib/curl_ntlm_msgs.c index 1fda1d7..00a7ea3 100644 --- a/lib/curl_ntlm_msgs.c +++ b/lib/curl_ntlm_msgs.c @@ -158,6 +158,68 @@ static unsigned int readint_le(unsigned char *buf) return ((unsigned int)buf[0]) | ((unsigned int)buf[1] << 8) | ((unsigned int)buf[2] << 16) | ((unsigned int)buf[3] << 24); } + +/* + * This function converts from the little endian format used in the + * incoming package to whatever endian format we're using natively. + * Argument is a pointer to a 2 byte buffer. + */ +static unsigned int readshort_le(unsigned char *buf) +{ + return ((unsigned int)buf[0]) | ((unsigned int)buf[1] << 8); +} + +/* + * Curl_ntlm_decode_type2_target() + * + * This is used to decode the "target info" in + * the ntlm type-2 message received. + * + * Parameters: + * + * data [in] - pointer to the session handle + * buffer [in] - the decoded base64 ntlm header of Type 2 + * size [in] - the input buffer size, atleast 32 bytes + * ntlm [in] - Pointer to ntlm data struct being used and modified. + * + * Returns CURLE_OK on success. + */ +CURLcode Curl_ntlm_decode_type2_target(struct SessionHandle *data, + unsigned char* buffer, + size_t size, + struct ntlmdata* ntlm) +{ + unsigned int target_info_len = 0; + unsigned int target_info_offset = 0; + + Curl_safefree(ntlm->target_info); + ntlm->target_info_len = 0; + + if(size >= 48) { + target_info_len = readshort_le(&buffer[40]); + target_info_offset = readint_le(&buffer[44]); + if(target_info_len > 0) { + if(((target_info_offset + target_info_len) > size) || + (target_info_offset < 48)) { + infof(data, "NTLM handshake failure (bad type-2 message). " + "Target Info Offset Len is set incorrect by the peer\n"); + return CURLE_REMOTE_ACCESS_DENIED; + } + + ntlm->target_info = malloc(target_info_len); + if(ntlm->target_info == NULL) + return CURLE_OUT_OF_MEMORY; + + memcpy(ntlm->target_info, &buffer[target_info_offset], target_info_len); + ntlm->target_info_len = target_info_len; + + } + + } + + return CURLE_OK; +} + #endif /* @@ -257,6 +319,15 @@ CURLcode Curl_ntlm_decode_type2_message(struct SessionHandle *data, ntlm->flags = readint_le(&buffer[20]); memcpy(ntlm->nonce, &buffer[24], 8); + if(ntlm->flags & NTLMFLAG_NEGOTIATE_TARGET_INFO) { + error = Curl_ntlm_decode_type2_target(data, buffer, size, ntlm); + if(error) { + free(buffer); + infof(data, "NTLM handshake failure (bad type-2 message)\n"); + return error; + } + } + DEBUG_OUT({ fprintf(stderr, "**** TYPE2 header flags=0x%08.8lx ", ntlm->flags); ntlm_print_flags(stderr, ntlm->flags); @@ -645,7 +716,10 @@ CURLcode Curl_ntlm_create_type3_message(struct SessionHandle *data, unsigned char lmresp[24]; /* fixed-size */ #if USE_NTRESPONSES int ntrespoff; + unsigned int ntresplen = 24; unsigned char ntresp[24]; /* fixed-size */ + unsigned char *ptr_ntresp = &ntresp[0]; + unsigned char *ntlmv2resp = NULL; #endif bool unicode = (ntlm->flags & NTLMFLAG_NEGOTIATE_UNICODE) ? TRUE : FALSE; char host[HOSTNAME_MAX + 1] = ""; @@ -657,7 +731,7 @@ CURLcode Curl_ntlm_create_type3_message(struct SessionHandle *data, size_t hostlen = 0; size_t userlen = 0; size_t domlen = 0; - CURLcode res; + CURLcode res = CURLE_OK; user = strchr(userp, '\\'); if(!user) @@ -684,11 +758,40 @@ CURLcode Curl_ntlm_create_type3_message(struct SessionHandle *data, hostlen = strlen(host); } - if(unicode) { - domlen = domlen * 2; - userlen = userlen * 2; - hostlen = hostlen * 2; +#if USE_NTRESPONSES + if(ntlm->target_info_len) { + unsigned char ntbuffer[0x18]; + unsigned char entropy[8]; + unsigned char ntlmv2hash[0x18]; + + /* Need to create 8 bytes random client nonce */ + Curl_ssl_random(data, entropy, sizeof(entropy)); + + res = Curl_ntlm_core_mk_nt_hash(data, passwdp, ntbuffer); + if(res) + return res; + + res = Curl_ntlm_core_mk_ntlmv2_hash(user, userlen, domain, domlen, + ntbuffer, ntlmv2hash); + if(res) + return res; + + /* LMv2 response */ + res = Curl_ntlm_core_mk_lmv2_resp(ntlmv2hash, entropy, &ntlm->nonce[0], + lmresp); + if(res) + return res; + + /* NTLMv2 response */ + res = Curl_ntlm_core_mk_ntlmv2_resp(ntlmv2hash, entropy, ntlm, &ntlmv2resp, + &ntresplen); + if(res) + return res; + + ptr_ntresp = ntlmv2resp; } + else +#endif #if USE_NTLM2SESSION /* We don't support NTLM2 if we don't have USE_NTRESPONSES */ @@ -718,9 +821,11 @@ CURLcode Curl_ntlm_create_type3_message(struct SessionHandle *data, if(CURLE_OUT_OF_MEMORY == Curl_ntlm_core_mk_nt_hash(data, passwdp, ntbuffer)) return CURLE_OUT_OF_MEMORY; + Curl_ntlm_core_lm_resp(ntbuffer, md5sum, ntresp); /* End of NTLM2 Session code */ + } else #endif @@ -745,10 +850,16 @@ CURLcode Curl_ntlm_create_type3_message(struct SessionHandle *data, * See http://davenport.sourceforge.net/ntlm.html#ntlmVersion2 */ } + if(unicode) { + domlen = domlen * 2; + userlen = userlen * 2; + hostlen = hostlen * 2; + } + lmrespoff = 64; /* size of the message header */ #if USE_NTRESPONSES ntrespoff = lmrespoff + 0x18; - domoff = ntrespoff + 0x18; + domoff = ntrespoff + ntresplen; #else domoff = lmrespoff + 0x18; #endif @@ -807,8 +918,8 @@ CURLcode Curl_ntlm_create_type3_message(struct SessionHandle *data, 0x0, 0x0, #if USE_NTRESPONSES - SHORTPAIR(0x18), /* NT-response length, twice */ - SHORTPAIR(0x18), + SHORTPAIR(ntresplen), /* NT-response length, twice */ + SHORTPAIR(ntresplen), SHORTPAIR(ntrespoff), 0x0, 0x0, #else @@ -854,17 +965,19 @@ CURLcode Curl_ntlm_create_type3_message(struct SessionHandle *data, }); #if USE_NTRESPONSES - if(size < (NTLM_BUFSIZE - 0x18)) { + if(size < (NTLM_BUFSIZE - ntresplen)) { DEBUGASSERT(size == (size_t)ntrespoff); - memcpy(&ntlmbuf[size], ntresp, 0x18); - size += 0x18; + memcpy(&ntlmbuf[size], ptr_ntresp, ntresplen); + size += ntresplen; } DEBUG_OUT({ fprintf(stderr, "\n ntresp="); - ntlm_print_hex(stderr, (char *)&ntlmbuf[ntrespoff], 0x18); + ntlm_print_hex(stderr, (char *)&ntlmbuf[ntrespoff], ntresplen); }); + Curl_safefree(ntlmv2resp);/* Free the dynamic buffer allocated for NTLMv2 */ + #endif DEBUG_OUT({ diff --git a/lib/curl_ntlm_msgs.h b/lib/curl_ntlm_msgs.h index e7d185d..fae3b99 100644 --- a/lib/curl_ntlm_msgs.h +++ b/lib/curl_ntlm_msgs.h @@ -46,6 +46,13 @@ CURLcode Curl_ntlm_decode_type2_message(struct SessionHandle *data, const char* header, struct ntlmdata* ntlm); +/* This is to decode target info received in NTLM type-2 message */ +CURLcode Curl_ntlm_decode_type2_target(struct SessionHandle *data, + unsigned char* buffer, + size_t size, + struct ntlmdata* ntlm); + + /* This is to clean up the ntlm data structure */ #ifdef USE_WINDOWS_SSPI void Curl_ntlm_sspi_cleanup(struct ntlmdata *ntlm); diff --git a/lib/urldata.h b/lib/urldata.h index a00894e..e14cd4a 100644 --- a/lib/urldata.h +++ b/lib/urldata.h @@ -435,6 +435,8 @@ struct ntlmdata { #else unsigned int flags; unsigned char nonce[8]; + void* target_info; /* TargetInfo received in the Type 2 message */ + unsigned int target_info_len; #endif };
------------------------------------------------------------------- List admin: http://cool.haxx.se/list/listinfo/curl-library Etiquette: http://curl.haxx.se/mail/etiquette.html