#include "ne_ntlm.h"
#ifdef HAVE_NTLM
#include "ne_string.h"
typedef enum {
NTLMSTATE_NONE,
NTLMSTATE_TYPE1,
NTLMSTATE_TYPE2,
NTLMSTATE_TYPE3,
NTLMSTATE_LAST
} NTLMState;
struct ne_ntlm_context_s {
NTLMState state;
unsigned char nonce[8];
char *user;
char *passwd;
char *requestToken;
};
typedef enum {
NTLM_NONE,
NTLM_BAD,
NTLM_FIRST,
NTLM_FINE,
NTLM_LAST
} ntlm;
#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_NETWARE (1<<8)
#define NTLMFLAG_NEGOTIATE_NTLM_KEY (1<<9)
#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)
#ifdef HAVE_OPENSSL
#include <stdio.h>
#include <string.h>
#include <stdarg.h>
#include <stdlib.h>
#include <ctype.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
#define USE_NTRESPONSES 1
static ntlm ne_input_ntlm(ne_ntlm_context *ctx,
const char *responseToken)
{
if(responseToken) {
unsigned char * buffer = NULL;
int size = ne_unbase64(responseToken, &buffer);
ctx->state = NTLMSTATE_TYPE2;
if(size >= 48)
memcpy(ctx->nonce, &buffer[24], 8);
if (buffer) ne_free(buffer);
}
else {
if(ctx->state >= NTLMSTATE_TYPE1)
return NTLM_BAD;
ctx->state = NTLMSTATE_TYPE1;
}
return NTLM_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
};
int i;
int len = strlen(password);
pw = ne_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, 5);
}
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, 8);
}
calc_resp(ntbuffer, nonce, ntresp);
#endif
ne_free(pw);
}
#define SHORTPAIR(x) ((x) & 0xff), ((x) >> 8)
#define LONGQUARTET(x) ((x) & 0xff), (((x) >> 8)&0xff), \
(((x) >>16)&0xff), ((x)>>24)
static int ne_output_ntlm(ne_ntlm_context *ctx)
{
const char *domain="";
const char *host="";
int domlen=strlen(domain);
int hostlen = strlen(host);
int hostoff;
int domoff;
int size;
unsigned char ntlmbuf[256];
if(!ctx->user || !ctx->passwd)
return 0;
switch(ctx->state) {
case NTLMSTATE_TYPE1:
default:
hostoff = 32;
domoff = hostoff + hostlen;
ne_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;
if (ctx->requestToken) ne_free(ctx->requestToken);
ctx->requestToken = ne_base64(ntlmbuf, size);
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(ctx->user, '\\');
if(!user)
user = strchr(ctx->user, '/');
if (user) {
domain = ctx->user;
domlen = user - domain;
user++;
}
else
user = ctx->user;
userlen = strlen(user);
mkhash(ctx->passwd, &ctx->nonce[0], lmresp
#ifdef USE_NTRESPONSES
, ntresp
#endif
);
domoff = 64;
useroff = domoff + domlen;
hostoff = useroff + userlen;
lmrespoff = hostoff + hostlen;
ntrespoff = lmrespoff + 0x18;
size = ne_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_t)size + userlen + domlen >= sizeof(ntlmbuf)) {
return -1;
}
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] = size & 0xff;
ntlmbuf[57] = size >> 8;
ctx->requestToken = ne_base64(ntlmbuf, size);
ctx->state = NTLMSTATE_TYPE3;
}
break;
case NTLMSTATE_TYPE3:
if (ctx->requestToken) ne_free(ctx->requestToken);
ctx->requestToken = NULL;
break;
}
return 0;
}
ne_ntlm_context *ne__ntlm_create_context(const char *userName, const char *password)
{
ne_ntlm_context *ctx = ne_calloc(sizeof(ne_ntlm_context));
ctx->state = NTLMSTATE_NONE;
ctx->user = ne_strdup(userName);
ctx->passwd = ne_strdup(password);
return ctx;
}
void ne__ntlm_destroy_context(ne_ntlm_context *context)
{
if (context->user)
ne_free(context->user);
if (context->passwd)
ne_free(context->passwd);
if (context->requestToken)
ne_free(context->requestToken);
ne_free(context);
}
int ne__ntlm_authenticate(ne_ntlm_context *context, const char *responseToken)
{
if (context == NULL) {
return -1;
} else {
if (context->state <= NTLMSTATE_TYPE3) {
ntlm ntlmstatus = ne_input_ntlm(context, responseToken);
if (ntlmstatus != NTLM_FINE) {
return -1;
}
}
}
return ne_output_ntlm(context);
}
char *ne__ntlm_getRequestToken(ne_ntlm_context *context)
{
char *ret;
if (context == NULL || !context->requestToken) {
return NULL;
}
ret = ne_strdup(context->requestToken);
ne_free(context->requestToken);
context->requestToken = NULL;
return ret;
}
#endif
#endif