#include "setup.h"
#ifndef CURL_DISABLE_HTTP
#ifdef USE_SSLEAY
#include <stdio.h>
#include <string.h>
#include <stdarg.h>
#include <stdlib.h>
#include <ctype.h>
#include "urldata.h"
#include "sendf.h"
#include "strequal.h"
#include "base64.h"
#include "http_ntlm.h"
#include "url.h"
#include "memory.h"
#define _MPRINTF_REPLACE
#include <curl/mprintf.h>
#include <openssl/des.h>
#include <openssl/md4.h>
#include <openssl/ssl.h>
#if OPENSSL_VERSION_NUMBER < 0x00907001L
#define DES_key_schedule des_key_schedule
#define DES_cblock des_cblock
#define DES_set_odd_parity des_set_odd_parity
#define DES_set_key des_set_key
#define DES_ecb_encrypt des_ecb_encrypt
#define DESKEY(x) x
#define DESKEYARG(x) x
#else
#define DESKEYARG(x) *x
#define DESKEY(x) &x
#endif
#include "memdebug.h"
#define USE_NTRESPONSES 1
CURLntlm Curl_input_ntlm(struct connectdata *conn,
bool proxy,
char *header)
{
struct ntlmdata *ntlm;
ntlm = proxy?&conn->proxyntlm:&conn->ntlm;
while(*header && isspace((int)*header))
header++;
if(checkprefix("NTLM", header)) {
header += strlen("NTLM");
while(*header && isspace((int)*header))
header++;
if(*header) {
size_t size;
unsigned char *buffer;
size = Curl_base64_decode(header, &buffer);
if(!buffer)
return CURLNTLM_BAD;
ntlm->state = NTLMSTATE_TYPE2;
if(size >= 48)
memcpy(ntlm->nonce, &buffer[24], 8);
free(buffer);
}
else {
if(ntlm->state >= NTLMSTATE_TYPE1)
return CURLNTLM_BAD;
ntlm->state = NTLMSTATE_TYPE1;
}
}
return CURLNTLM_FINE;
}
static void setup_des_key(unsigned char *key_56,
DES_key_schedule DESKEYARG(ks))
{
DES_cblock key;
key[0] = key_56[0];
key[1] = ((key_56[0] << 7) & 0xFF) | (key_56[1] >> 1);
key[2] = ((key_56[1] << 6) & 0xFF) | (key_56[2] >> 2);
key[3] = ((key_56[2] << 5) & 0xFF) | (key_56[3] >> 3);
key[4] = ((key_56[3] << 4) & 0xFF) | (key_56[4] >> 4);
key[5] = ((key_56[4] << 3) & 0xFF) | (key_56[5] >> 5);
key[6] = ((key_56[5] << 2) & 0xFF) | (key_56[6] >> 6);
key[7] = (key_56[6] << 1) & 0xFF;
DES_set_odd_parity(&key);
DES_set_key(&key, ks);
}
static void calc_resp(unsigned char *keys,
unsigned char *plaintext,
unsigned char *results)
{
DES_key_schedule ks;
setup_des_key(keys, DESKEY(ks));
DES_ecb_encrypt((DES_cblock*) plaintext, (DES_cblock*) results,
DESKEY(ks), DES_ENCRYPT);
setup_des_key(keys+7, DESKEY(ks));
DES_ecb_encrypt((DES_cblock*) plaintext, (DES_cblock*) (results+8),
DESKEY(ks), DES_ENCRYPT);
setup_des_key(keys+14, DESKEY(ks));
DES_ecb_encrypt((DES_cblock*) plaintext, (DES_cblock*) (results+16),
DESKEY(ks), DES_ENCRYPT);
}
static void mkhash(char *password,
unsigned char *nonce,
unsigned char *lmresp
#ifdef USE_NTRESPONSES
, unsigned char *ntresp
#endif
)
{
unsigned char lmbuffer[21];
#ifdef USE_NTRESPONSES
unsigned char ntbuffer[21];
#endif
unsigned char *pw;
static const unsigned char magic[] = {
0x4B, 0x47, 0x53, 0x21, 0x40, 0x23, 0x24, 0x25
};
unsigned int i;
size_t len = strlen(password);
pw = malloc(len<7?14:len*2);
if(!pw)
return;
if (len > 14)
len = 14;
for (i=0; i<len; i++)
pw[i] = toupper(password[i]);
for (; i<14; i++)
pw[i] = 0;
{
DES_key_schedule ks;
setup_des_key(pw, DESKEY(ks));
DES_ecb_encrypt((DES_cblock *)magic, (DES_cblock *)lmbuffer,
DESKEY(ks), DES_ENCRYPT);
setup_des_key(pw+7, DESKEY(ks));
DES_ecb_encrypt((DES_cblock *)magic, (DES_cblock *)(lmbuffer+8),
DESKEY(ks), DES_ENCRYPT);
memset(lmbuffer+16, 0, sizeof(lmbuffer)-16);
}
calc_resp(lmbuffer, nonce, lmresp);
#ifdef USE_NTRESPONSES
{
MD4_CTX MD4;
len = strlen(password);
for (i=0; i<len; i++) {
pw[2*i] = password[i];
pw[2*i+1] = 0;
}
MD4_Init(&MD4);
MD4_Update(&MD4, pw, 2*len);
MD4_Final(ntbuffer, &MD4);
memset(ntbuffer+16, 0, sizeof(ntbuffer)-16);
}
calc_resp(ntbuffer, nonce, ntresp);
#endif
free(pw);
}
#define SHORTPAIR(x) ((x) & 0xff), ((x) >> 8)
#define LONGQUARTET(x) ((x) & 0xff), (((x) >> 8)&0xff), \
(((x) >>16)&0xff), ((x)>>24)
CURLcode Curl_output_ntlm(struct connectdata *conn,
bool proxy)
{
const char *domain="";
const char *host="";
int domlen=(int)strlen(domain);
int hostlen = (int)strlen(host);
int hostoff;
int domoff;
size_t size;
char *base64=NULL;
unsigned char ntlmbuf[256];
char **allocuserpwd;
char *userp;
char *passwdp;
struct ntlmdata *ntlm;
struct auth *authp;
curlassert(conn);
curlassert(conn->data);
if(proxy) {
allocuserpwd = &conn->allocptr.proxyuserpwd;
userp = conn->proxyuser;
passwdp = conn->proxypasswd;
ntlm = &conn->proxyntlm;
authp = &conn->data->state.authproxy;
}
else {
allocuserpwd = &conn->allocptr.userpwd;
userp = conn->user;
passwdp = conn->passwd;
ntlm = &conn->ntlm;
authp = &conn->data->state.authhost;
}
authp->done = FALSE;
if(!userp)
userp=(char *)"";
if(!passwdp)
passwdp=(char *)"";
switch(ntlm->state) {
case NTLMSTATE_TYPE1:
default:
hostoff = 32;
domoff = hostoff + hostlen;
snprintf((char *)ntlmbuf, sizeof(ntlmbuf), "NTLMSSP%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_NEGOTIATE_NTLM_KEY
),
SHORTPAIR(domlen),
SHORTPAIR(domlen),
SHORTPAIR(domoff),
0,0,
SHORTPAIR(hostlen),
SHORTPAIR(hostlen),
SHORTPAIR(hostoff),
0,0,
host, domain);
size = 32 + hostlen + domlen;
size = Curl_base64_encode((char *)ntlmbuf, size, &base64);
if(size >0 ) {
Curl_safefree(*allocuserpwd);
*allocuserpwd = aprintf("%sAuthorization: NTLM %s\r\n",
proxy?"Proxy-":"",
base64);
free(base64);
}
else
return CURLE_OUT_OF_MEMORY;
break;
case NTLMSTATE_TYPE2:
{
int lmrespoff;
int ntrespoff;
int useroff;
unsigned char lmresp[0x18];
#ifdef USE_NTRESPONSES
unsigned char ntresp[0x18];
#endif
const char *user;
int userlen;
user = strchr(userp, '\\');
if(!user)
user = strchr(userp, '/');
if (user) {
domain = userp;
domlen = (int)(user - domain);
user++;
}
else
user = userp;
userlen = (int)strlen(user);
mkhash(passwdp, &ntlm->nonce[0], lmresp
#ifdef USE_NTRESPONSES
, ntresp
#endif
);
domoff = 64;
useroff = domoff + domlen;
hostoff = useroff + userlen;
lmrespoff = hostoff + hostlen;
ntrespoff = lmrespoff + 0x18;
size = snprintf((char *)ntlmbuf, sizeof(ntlmbuf),
"NTLMSSP%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"
"\xff\xff"
"%c%c"
"\x01\x82"
"%c%c"
,
0,
0,0,0,
SHORTPAIR(0x18),
SHORTPAIR(0x18),
SHORTPAIR(lmrespoff),
0x0, 0x0,
#ifdef USE_NTRESPONSES
SHORTPAIR(0x18),
SHORTPAIR(0x18),
#else
0x0, 0x0,
0x0, 0x0,
#endif
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);
size=64;
ntlmbuf[62]=ntlmbuf[63]=0;
if(size + userlen + domlen >= sizeof(ntlmbuf)) {
failf(conn->data, "user + domain name too big");
return CURLE_OUT_OF_MEMORY;
}
memcpy(&ntlmbuf[size], domain, domlen);
size += domlen;
memcpy(&ntlmbuf[size], user, userlen);
size += userlen;
if(size < ((int)sizeof(ntlmbuf) - 0x18)) {
memcpy(&ntlmbuf[size], lmresp, 0x18);
size += 0x18;
}
#ifdef USE_NTRESPONSES
if(size < ((int)sizeof(ntlmbuf) - 0x18)) {
memcpy(&ntlmbuf[size], ntresp, 0x18);
size += 0x18;
}
#endif
ntlmbuf[56] = (unsigned char)(size & 0xff);
ntlmbuf[57] = (unsigned char)(size >> 8);
size = Curl_base64_encode((char *)ntlmbuf, size, &base64);
if(size >0 ) {
Curl_safefree(*allocuserpwd);
*allocuserpwd = aprintf("%sAuthorization: NTLM %s\r\n",
proxy?"Proxy-":"",
base64);
free(base64);
}
else
return CURLE_OUT_OF_MEMORY;
ntlm->state = NTLMSTATE_TYPE3;
authp->done = TRUE;
}
break;
case NTLMSTATE_TYPE3:
if(*allocuserpwd) {
free(*allocuserpwd);
*allocuserpwd=NULL;
}
authp->done = TRUE;
break;
}
return CURLE_OK;
}
#endif
#endif