#include "../curl_setup.h"
#if defined(USE_NTLM) && !defined(USE_WINDOWS_SSPI)
#define DEBUG_ME 0
#include "../urldata.h"
#include "../sendf.h"
#include "../curl_ntlm_core.h"
#include "../curl_gethostname.h"
#include "../curlx/multibyte.h"
#include "../curlx/warnless.h"
#include "../rand.h"
#include "../vtls/vtls.h"
#include "../strdup.h"
#include "vauth.h"
#include "../curl_endian.h"
#include "../curl_memory.h"
#include "../memdebug.h"
#define NTLM_BUFSIZE 1024
#define NTLMFLAG_NEGOTIATE_UNICODE (1<<0)
#define NTLMFLAG_NEGOTIATE_OEM (1<<1)
#define NTLMFLAG_REQUEST_TARGET (1<<2)
#define NTLMFLAG_NEGOTIATE_SIGN (1<<4)
#define NTLMFLAG_NEGOTIATE_SEAL (1<<5)
#define NTLMFLAG_NEGOTIATE_DATAGRAM_STYLE (1<<6)
#define NTLMFLAG_NEGOTIATE_LM_KEY (1<<7)
#define NTLMFLAG_NEGOTIATE_NTLM_KEY (1<<9)
#define NTLMFLAG_NEGOTIATE_ANONYMOUS (1<<11)
#define NTLMFLAG_NEGOTIATE_DOMAIN_SUPPLIED (1<<12)
#define NTLMFLAG_NEGOTIATE_WORKSTATION_SUPPLIED (1<<13)
#define NTLMFLAG_NEGOTIATE_LOCAL_CALL (1<<14)
#define NTLMFLAG_NEGOTIATE_ALWAYS_SIGN (1<<15)
#define NTLMFLAG_TARGET_TYPE_DOMAIN (1<<16)
#define NTLMFLAG_TARGET_TYPE_SERVER (1<<17)
#define NTLMFLAG_TARGET_TYPE_SHARE (1<<18)
#define NTLMFLAG_NEGOTIATE_NTLM2_KEY (1<<19)
#define NTLMFLAG_REQUEST_INIT_RESPONSE (1<<20)
#define NTLMFLAG_REQUEST_ACCEPT_RESPONSE (1<<21)
#define NTLMFLAG_REQUEST_NONNT_SESSION_KEY (1<<22)
#define NTLMFLAG_NEGOTIATE_TARGET_INFO (1<<23)
#define NTLMFLAG_NEGOTIATE_128 (1<<29)
#define NTLMFLAG_NEGOTIATE_KEY_EXCHANGE (1<<30)
#define NTLMFLAG_NEGOTIATE_56 (1<<31)
#define NTLMSSP_SIGNATURE "\x4e\x54\x4c\x4d\x53\x53\x50"
#if DEBUG_ME
# define DEBUG_OUT(x) x
static void ntlm_print_flags(FILE *handle, unsigned long flags)
{
if(flags & NTLMFLAG_NEGOTIATE_UNICODE)
curl_mfprintf(handle, "NTLMFLAG_NEGOTIATE_UNICODE ");
if(flags & NTLMFLAG_NEGOTIATE_OEM)
curl_mfprintf(handle, "NTLMFLAG_NEGOTIATE_OEM ");
if(flags & NTLMFLAG_REQUEST_TARGET)
curl_mfprintf(handle, "NTLMFLAG_REQUEST_TARGET ");
if(flags & (1 << 3))
curl_mfprintf(handle, "NTLMFLAG_UNKNOWN_3 ");
if(flags & NTLMFLAG_NEGOTIATE_SIGN)
curl_mfprintf(handle, "NTLMFLAG_NEGOTIATE_SIGN ");
if(flags & NTLMFLAG_NEGOTIATE_SEAL)
curl_mfprintf(handle, "NTLMFLAG_NEGOTIATE_SEAL ");
if(flags & NTLMFLAG_NEGOTIATE_DATAGRAM_STYLE)
curl_mfprintf(handle, "NTLMFLAG_NEGOTIATE_DATAGRAM_STYLE ");
if(flags & NTLMFLAG_NEGOTIATE_LM_KEY)
curl_mfprintf(handle, "NTLMFLAG_NEGOTIATE_LM_KEY ");
if(flags & NTLMFLAG_NEGOTIATE_NTLM_KEY)
curl_mfprintf(handle, "NTLMFLAG_NEGOTIATE_NTLM_KEY ");
if(flags & (1 << 10))
curl_mfprintf(handle, "NTLMFLAG_UNKNOWN_10 ");
if(flags & NTLMFLAG_NEGOTIATE_ANONYMOUS)
curl_mfprintf(handle, "NTLMFLAG_NEGOTIATE_ANONYMOUS ");
if(flags & NTLMFLAG_NEGOTIATE_DOMAIN_SUPPLIED)
curl_mfprintf(handle, "NTLMFLAG_NEGOTIATE_DOMAIN_SUPPLIED ");
if(flags & NTLMFLAG_NEGOTIATE_WORKSTATION_SUPPLIED)
curl_mfprintf(handle, "NTLMFLAG_NEGOTIATE_WORKSTATION_SUPPLIED ");
if(flags & NTLMFLAG_NEGOTIATE_LOCAL_CALL)
curl_mfprintf(handle, "NTLMFLAG_NEGOTIATE_LOCAL_CALL ");
if(flags & NTLMFLAG_NEGOTIATE_ALWAYS_SIGN)
curl_mfprintf(handle, "NTLMFLAG_NEGOTIATE_ALWAYS_SIGN ");
if(flags & NTLMFLAG_TARGET_TYPE_DOMAIN)
curl_mfprintf(handle, "NTLMFLAG_TARGET_TYPE_DOMAIN ");
if(flags & NTLMFLAG_TARGET_TYPE_SERVER)
curl_mfprintf(handle, "NTLMFLAG_TARGET_TYPE_SERVER ");
if(flags & NTLMFLAG_TARGET_TYPE_SHARE)
curl_mfprintf(handle, "NTLMFLAG_TARGET_TYPE_SHARE ");
if(flags & NTLMFLAG_NEGOTIATE_NTLM2_KEY)
curl_mfprintf(handle, "NTLMFLAG_NEGOTIATE_NTLM2_KEY ");
if(flags & NTLMFLAG_REQUEST_INIT_RESPONSE)
curl_mfprintf(handle, "NTLMFLAG_REQUEST_INIT_RESPONSE ");
if(flags & NTLMFLAG_REQUEST_ACCEPT_RESPONSE)
curl_mfprintf(handle, "NTLMFLAG_REQUEST_ACCEPT_RESPONSE ");
if(flags & NTLMFLAG_REQUEST_NONNT_SESSION_KEY)
curl_mfprintf(handle, "NTLMFLAG_REQUEST_NONNT_SESSION_KEY ");
if(flags & NTLMFLAG_NEGOTIATE_TARGET_INFO)
curl_mfprintf(handle, "NTLMFLAG_NEGOTIATE_TARGET_INFO ");
if(flags & (1 << 24))
curl_mfprintf(handle, "NTLMFLAG_UNKNOWN_24 ");
if(flags & (1 << 25))
curl_mfprintf(handle, "NTLMFLAG_UNKNOWN_25 ");
if(flags & (1 << 26))
curl_mfprintf(handle, "NTLMFLAG_UNKNOWN_26 ");
if(flags & (1 << 27))
curl_mfprintf(handle, "NTLMFLAG_UNKNOWN_27 ");
if(flags & (1 << 28))
curl_mfprintf(handle, "NTLMFLAG_UNKNOWN_28 ");
if(flags & NTLMFLAG_NEGOTIATE_128)
curl_mfprintf(handle, "NTLMFLAG_NEGOTIATE_128 ");
if(flags & NTLMFLAG_NEGOTIATE_KEY_EXCHANGE)
curl_mfprintf(handle, "NTLMFLAG_NEGOTIATE_KEY_EXCHANGE ");
if(flags & NTLMFLAG_NEGOTIATE_56)
curl_mfprintf(handle, "NTLMFLAG_NEGOTIATE_56 ");
}
static void ntlm_print_hex(FILE *handle, const char *buf, size_t len)
{
const char *p = buf;
(void)handle;
curl_mfprintf(stderr, "0x");
while(len-- > 0)
curl_mfprintf(stderr, "%02.2x", (unsigned int)*p++);
}
#else
# define DEBUG_OUT(x) Curl_nop_stmt
#endif
static CURLcode ntlm_decode_type2_target(struct Curl_easy *data,
const struct bufref *type2ref,
struct ntlmdata *ntlm)
{
unsigned short target_info_len = 0;
unsigned int target_info_offset = 0;
const unsigned char *type2 = Curl_bufref_ptr(type2ref);
size_t type2len = Curl_bufref_len(type2ref);
#ifdef CURL_DISABLE_VERBOSE_STRINGS
(void)data;
#endif
if(type2len >= 48) {
target_info_len = Curl_read16_le(&type2[40]);
target_info_offset = Curl_read32_le(&type2[44]);
if(target_info_len > 0) {
if((target_info_offset > type2len) ||
(target_info_offset + target_info_len) > type2len ||
target_info_offset < 48) {
infof(data, "NTLM handshake failure (bad type-2 message). "
"Target Info Offset Len is set incorrect by the peer");
return CURLE_BAD_CONTENT_ENCODING;
}
free(ntlm->target_info);
ntlm->target_info = Curl_memdup(&type2[target_info_offset],
target_info_len);
if(!ntlm->target_info)
return CURLE_OUT_OF_MEMORY;
}
}
ntlm->target_info_len = target_info_len;
return CURLE_OK;
}
bool Curl_auth_is_ntlm_supported(void)
{
return TRUE;
}
CURLcode Curl_auth_decode_ntlm_type2_message(struct Curl_easy *data,
const struct bufref *type2ref,
struct ntlmdata *ntlm)
{
static const char type2_marker[] = { 0x02, 0x00, 0x00, 0x00 };
CURLcode result = CURLE_OK;
const unsigned char *type2 = Curl_bufref_ptr(type2ref);
size_t type2len = Curl_bufref_len(type2ref);
#ifdef CURL_DISABLE_VERBOSE_STRINGS
(void)data;
#endif
ntlm->flags = 0;
if((type2len < 32) ||
(memcmp(type2, NTLMSSP_SIGNATURE, 8) != 0) ||
(memcmp(type2 + 8, type2_marker, sizeof(type2_marker)) != 0)) {
infof(data, "NTLM handshake failure (bad type-2 message)");
return CURLE_BAD_CONTENT_ENCODING;
}
ntlm->flags = Curl_read32_le(&type2[20]);
memcpy(ntlm->nonce, &type2[24], 8);
if(ntlm->flags & NTLMFLAG_NEGOTIATE_TARGET_INFO) {
result = ntlm_decode_type2_target(data, type2ref, ntlm);
if(result) {
infof(data, "NTLM handshake failure (bad type-2 message)");
return result;
}
}
DEBUG_OUT({
curl_mfprintf(stderr, "**** TYPE2 header flags=0x%08.8lx ", ntlm->flags);
ntlm_print_flags(stderr, ntlm->flags);
curl_mfprintf(stderr, "\n nonce=");
ntlm_print_hex(stderr, (char *)ntlm->nonce, 8);
curl_mfprintf(stderr, "\n****\n");
curl_mfprintf(stderr, "**** Header %s\n ", header);
});
return result;
}
static void unicodecpy(unsigned char *dest, const char *src, size_t length)
{
size_t i;
for(i = 0; i < length; i++) {
dest[2 * i] = (unsigned char)src[i];
dest[2 * i + 1] = '\0';
}
}
CURLcode Curl_auth_create_ntlm_type1_message(struct Curl_easy *data,
const char *userp,
const char *passwdp,
const char *service,
const char *hostname,
struct ntlmdata *ntlm,
struct bufref *out)
{
size_t size;
char *ntlmbuf;
const char *host = "";
const char *domain = "";
size_t hostlen = 0;
size_t domlen = 0;
size_t hostoff = 0;
size_t domoff = hostoff + hostlen;
(void)data;
(void)userp;
(void)passwdp;
(void)service;
(void)hostname;
Curl_auth_cleanup_ntlm(ntlm);
ntlmbuf = curl_maprintf(NTLMSSP_SIGNATURE "%c"
"\x01%c%c%c"
"%c%c%c%c"
"%c%c"
"%c%c"
"%c%c"
"%c%c"
"%c%c"
"%c%c"
"%c%c"
"%c%c"
"%s"
"%s",
0,
0, 0, 0,
LONGQUARTET(NTLMFLAG_NEGOTIATE_OEM |
NTLMFLAG_REQUEST_TARGET |
NTLMFLAG_NEGOTIATE_NTLM_KEY |
NTLMFLAG_NEGOTIATE_NTLM2_KEY |
NTLMFLAG_NEGOTIATE_ALWAYS_SIGN),
SHORTPAIR(domlen),
SHORTPAIR(domlen),
SHORTPAIR(domoff),
0, 0,
SHORTPAIR(hostlen),
SHORTPAIR(hostlen),
SHORTPAIR(hostoff),
0, 0,
host,
domain );
if(!ntlmbuf)
return CURLE_OUT_OF_MEMORY;
size = 32 + hostlen + domlen;
DEBUG_OUT({
curl_mfprintf(stderr, "* TYPE1 header flags=0x%02.2x%02.2x%02.2x%02.2x "
"0x%08.8x ",
LONGQUARTET(NTLMFLAG_NEGOTIATE_OEM |
NTLMFLAG_REQUEST_TARGET |
NTLMFLAG_NEGOTIATE_NTLM_KEY |
NTLMFLAG_NEGOTIATE_NTLM2_KEY |
NTLMFLAG_NEGOTIATE_ALWAYS_SIGN),
NTLMFLAG_NEGOTIATE_OEM |
NTLMFLAG_REQUEST_TARGET |
NTLMFLAG_NEGOTIATE_NTLM_KEY |
NTLMFLAG_NEGOTIATE_NTLM2_KEY |
NTLMFLAG_NEGOTIATE_ALWAYS_SIGN);
ntlm_print_flags(stderr,
NTLMFLAG_NEGOTIATE_OEM |
NTLMFLAG_REQUEST_TARGET |
NTLMFLAG_NEGOTIATE_NTLM_KEY |
NTLMFLAG_NEGOTIATE_NTLM2_KEY |
NTLMFLAG_NEGOTIATE_ALWAYS_SIGN);
curl_mfprintf(stderr, "\n****\n");
});
Curl_bufref_set(out, ntlmbuf, size, curl_free);
return CURLE_OK;
}
CURLcode Curl_auth_create_ntlm_type3_message(struct Curl_easy *data,
const char *userp,
const char *passwdp,
struct ntlmdata *ntlm,
struct bufref *out)
{
CURLcode result = CURLE_OK;
size_t size;
unsigned char ntlmbuf[NTLM_BUFSIZE];
unsigned int lmrespoff;
unsigned char lmresp[24];
unsigned int ntrespoff;
unsigned int ntresplen = 24;
unsigned char ntresp[24];
unsigned char *ptr_ntresp = &ntresp[0];
unsigned char *ntlmv2resp = NULL;
bool unicode = (ntlm->flags & NTLMFLAG_NEGOTIATE_UNICODE);
static const char host[] = "WORKSTATION";
const char *user;
const char *domain = "";
size_t hostoff = 0;
size_t useroff = 0;
size_t domoff = 0;
size_t hostlen = 0;
size_t userlen = 0;
size_t domlen = 0;
memset(lmresp, 0, sizeof(lmresp));
memset(ntresp, 0, sizeof(ntresp));
user = strchr(userp, '\\');
if(!user)
user = strchr(userp, '/');
if(user) {
domain = userp;
domlen = (user - domain);
user++;
}
else
user = userp;
userlen = strlen(user);
hostlen = sizeof(host) - 1;
if(ntlm->flags & NTLMFLAG_NEGOTIATE_NTLM2_KEY) {
unsigned char ntbuffer[0x18];
unsigned char entropy[8];
unsigned char ntlmv2hash[0x18];
result = Curl_rand(data, entropy, 8);
if(result)
return result;
result = Curl_ntlm_core_mk_nt_hash(passwdp, ntbuffer);
if(result)
return result;
result = Curl_ntlm_core_mk_ntlmv2_hash(user, userlen, domain, domlen,
ntbuffer, ntlmv2hash);
if(result)
return result;
result = Curl_ntlm_core_mk_lmv2_resp(ntlmv2hash, entropy,
&ntlm->nonce[0], lmresp);
if(result)
return result;
result = Curl_ntlm_core_mk_ntlmv2_resp(ntlmv2hash, entropy,
ntlm, &ntlmv2resp, &ntresplen);
if(result)
return result;
ptr_ntresp = ntlmv2resp;
}
else {
unsigned char ntbuffer[0x18];
unsigned char lmbuffer[0x18];
result = Curl_ntlm_core_mk_nt_hash(passwdp, ntbuffer);
if(result)
return result;
Curl_ntlm_core_lm_resp(ntbuffer, &ntlm->nonce[0], ntresp);
result = Curl_ntlm_core_mk_lm_hash(passwdp, lmbuffer);
if(result)
return result;
Curl_ntlm_core_lm_resp(lmbuffer, &ntlm->nonce[0], lmresp);
ntlm->flags &= ~(unsigned int)NTLMFLAG_NEGOTIATE_NTLM2_KEY;
}
if(unicode) {
domlen = domlen * 2;
userlen = userlen * 2;
hostlen = hostlen * 2;
}
lmrespoff = 64;
ntrespoff = lmrespoff + 0x18;
domoff = ntrespoff + ntresplen;
useroff = domoff + domlen;
hostoff = useroff + userlen;
size = curl_msnprintf((char *)ntlmbuf, NTLM_BUFSIZE,
NTLMSSP_SIGNATURE "%c"
"\x03%c%c%c"
"%c%c"
"%c%c"
"%c%c"
"%c%c"
"%c%c"
"%c%c"
"%c%c"
"%c%c"
"%c%c"
"%c%c"
"%c%c"
"%c%c"
"%c%c"
"%c%c"
"%c%c"
"%c%c"
"%c%c"
"%c%c"
"%c%c"
"%c%c"
"%c%c"
"%c%c"
"%c%c"
"%c%c"
"%c%c%c%c",
0,
0, 0, 0,
SHORTPAIR(0x18),
SHORTPAIR(0x18),
SHORTPAIR(lmrespoff),
0x0, 0x0,
SHORTPAIR(ntresplen),
SHORTPAIR(ntresplen),
SHORTPAIR(ntrespoff),
0x0, 0x0,
SHORTPAIR(domlen),
SHORTPAIR(domlen),
SHORTPAIR(domoff),
0x0, 0x0,
SHORTPAIR(userlen),
SHORTPAIR(userlen),
SHORTPAIR(useroff),
0x0, 0x0,
SHORTPAIR(hostlen),
SHORTPAIR(hostlen),
SHORTPAIR(hostoff),
0x0, 0x0,
0x0, 0x0,
0x0, 0x0,
0x0, 0x0,
0x0, 0x0,
LONGQUARTET(ntlm->flags));
DEBUGASSERT(size == 64);
DEBUGASSERT(size == (size_t)lmrespoff);
if(size < (NTLM_BUFSIZE - 0x18)) {
memcpy(&ntlmbuf[size], lmresp, 0x18);
size += 0x18;
}
DEBUG_OUT({
curl_mfprintf(stderr, "**** TYPE3 header lmresp=");
ntlm_print_hex(stderr, (char *)&ntlmbuf[lmrespoff], 0x18);
});
if(ntresplen + size > sizeof(ntlmbuf)) {
failf(data, "incoming NTLM message too big");
result = CURLE_TOO_LARGE;
goto error;
}
DEBUGASSERT(size == (size_t)ntrespoff);
memcpy(&ntlmbuf[size], ptr_ntresp, ntresplen);
size += ntresplen;
DEBUG_OUT({
curl_mfprintf(stderr, "\n ntresp=");
ntlm_print_hex(stderr, (char *)&ntlmbuf[ntrespoff], ntresplen);
});
DEBUG_OUT({
curl_mfprintf(stderr, "\n flags=0x%02.2x%02.2x%02.2x%02.2x 0x%08.8x ",
LONGQUARTET(ntlm->flags), ntlm->flags);
ntlm_print_flags(stderr, ntlm->flags);
curl_mfprintf(stderr, "\n****\n");
});
if(size + userlen + domlen + hostlen >= NTLM_BUFSIZE) {
failf(data, "user + domain + hostname too big for NTLM");
result = CURLE_TOO_LARGE;
goto error;
}
DEBUGASSERT(size == domoff);
if(unicode)
unicodecpy(&ntlmbuf[size], domain, domlen / 2);
else
memcpy(&ntlmbuf[size], domain, domlen);
size += domlen;
DEBUGASSERT(size == useroff);
if(unicode)
unicodecpy(&ntlmbuf[size], user, userlen / 2);
else
memcpy(&ntlmbuf[size], user, userlen);
size += userlen;
DEBUGASSERT(size == hostoff);
if(unicode)
unicodecpy(&ntlmbuf[size], host, hostlen / 2);
else
memcpy(&ntlmbuf[size], host, hostlen);
size += hostlen;
result = Curl_bufref_memdup(out, ntlmbuf, size);
error:
free(ntlmv2resp);
Curl_auth_cleanup_ntlm(ntlm);
return result;
}
void Curl_auth_cleanup_ntlm(struct ntlmdata *ntlm)
{
Curl_safefree(ntlm->target_info);
ntlm->target_info_len = 0;
}
#endif