Add ntlmv2 answer calculating functions.
git-svn-id: svn://svn.h5l.se/heimdal/trunk/heimdal@19478 ec53bebd-3082-4978-b11e-865c3cabbd6b
This commit is contained in:
239
lib/ntlm/ntlm.c
239
lib/ntlm/ntlm.c
@@ -39,6 +39,7 @@ RCSID("$Id$");
|
|||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
#include <ctype.h>
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
|
|
||||||
#include <krb5.h>
|
#include <krb5.h>
|
||||||
@@ -65,6 +66,48 @@ static const unsigned char ntlmsigature[8] = "NTLMSSP\x00";
|
|||||||
#define CHECK(f, e) \
|
#define CHECK(f, e) \
|
||||||
do { ret = f ; if (ret != (e)) { ret = EINVAL; goto out; } } while(0)
|
do { ret = f ; if (ret != (e)) { ret = EINVAL; goto out; } } while(0)
|
||||||
|
|
||||||
|
static void
|
||||||
|
_ntlm_free_buf(struct ntlm_buf *p)
|
||||||
|
{
|
||||||
|
if (p->data)
|
||||||
|
free(p->data);
|
||||||
|
p->data = NULL;
|
||||||
|
p->length = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int
|
||||||
|
ascii2ucs2le(const char *string, int up, struct ntlm_buf *buf)
|
||||||
|
{
|
||||||
|
unsigned char *p;
|
||||||
|
size_t len, i;
|
||||||
|
|
||||||
|
len = strlen(string);
|
||||||
|
if (len / 2 > UINT_MAX)
|
||||||
|
return ERANGE;
|
||||||
|
|
||||||
|
buf->length = len * 2;
|
||||||
|
buf->data = malloc(buf->length);
|
||||||
|
if (buf->data == NULL && len != 0) {
|
||||||
|
_ntlm_free_buf(buf);
|
||||||
|
return ENOMEM;
|
||||||
|
}
|
||||||
|
|
||||||
|
p = buf->data;
|
||||||
|
for (i = 0; i < len; i++) {
|
||||||
|
unsigned char t = (unsigned char)string[i];
|
||||||
|
if (t & 0x80) {
|
||||||
|
_ntlm_free_buf(buf);
|
||||||
|
return EINVAL;
|
||||||
|
}
|
||||||
|
if (up)
|
||||||
|
t = toupper(t);
|
||||||
|
p[(i * 2) + 0] = t;
|
||||||
|
p[(i * 2) + 1] = 0;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
@@ -138,24 +181,20 @@ static krb5_error_code
|
|||||||
put_string(krb5_storage *sp, int ucs2, const char *s)
|
put_string(krb5_storage *sp, int ucs2, const char *s)
|
||||||
{
|
{
|
||||||
krb5_error_code ret;
|
krb5_error_code ret;
|
||||||
size_t len = strlen(s);
|
struct ntlm_buf buf;
|
||||||
char *p;
|
|
||||||
|
|
||||||
/* ascii -> ucs2-le */
|
|
||||||
if (ucs2) {
|
if (ucs2) {
|
||||||
size_t i;
|
ret = ascii2ucs2le(s, 0, &buf);
|
||||||
p = malloc(len * 2);
|
if (ret)
|
||||||
for (i = 0; i < len; i++) {
|
return ret;
|
||||||
p[(i * 2) + 0] = s[i];
|
} else {
|
||||||
p[(i * 2) + 1] = 0;
|
buf.data = rk_UNCONST(s);
|
||||||
|
buf.length = strlen(s);
|
||||||
}
|
}
|
||||||
len *= 2;
|
|
||||||
} else
|
|
||||||
p = rk_UNCONST(s);
|
|
||||||
|
|
||||||
CHECK(krb5_storage_write(sp, p, len), len);
|
CHECK(krb5_storage_write(sp, buf.data, buf.length), buf.length);
|
||||||
if (p != s)
|
if (ucs2)
|
||||||
free(p);
|
_ntlm_free_buf(&buf);
|
||||||
ret = 0;
|
ret = 0;
|
||||||
out:
|
out:
|
||||||
return ret;
|
return ret;
|
||||||
@@ -710,31 +749,24 @@ splitandenc(unsigned char *hash,
|
|||||||
int
|
int
|
||||||
heim_ntlm_nt_key(const char *password, struct ntlm_buf *key)
|
heim_ntlm_nt_key(const char *password, struct ntlm_buf *key)
|
||||||
{
|
{
|
||||||
size_t i, len = strlen(password);
|
struct ntlm_buf buf;
|
||||||
unsigned char *p;
|
|
||||||
MD4_CTX ctx;
|
MD4_CTX ctx;
|
||||||
|
int ret;
|
||||||
|
|
||||||
key->data = malloc(MD5_DIGEST_LENGTH);
|
key->data = malloc(MD5_DIGEST_LENGTH);
|
||||||
if (key->data == NULL)
|
if (key->data == NULL)
|
||||||
return ENOMEM;
|
return ENOMEM;
|
||||||
key->length = MD5_DIGEST_LENGTH;
|
key->length = MD5_DIGEST_LENGTH;
|
||||||
|
|
||||||
p = malloc(len * 2);
|
ret = ascii2ucs2le(password, 0, &buf);
|
||||||
if (p == NULL) {
|
if (ret) {
|
||||||
free(key->data);
|
_ntlm_free_buf(key);
|
||||||
key->data = NULL;
|
return ret;
|
||||||
return ENOMEM;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ascii -> ucs2-le */
|
|
||||||
for (i = 0; i < len; i++) {
|
|
||||||
p[(i * 2) + 0] = password[i];
|
|
||||||
p[(i * 2) + 1] = 0;
|
|
||||||
}
|
}
|
||||||
MD4_Init(&ctx);
|
MD4_Init(&ctx);
|
||||||
MD4_Update(&ctx, p, len * 2);
|
MD4_Update(&ctx, buf.data, buf.length);
|
||||||
free(p);
|
|
||||||
MD4_Final(key->data, &ctx);
|
MD4_Final(key->data, &ctx);
|
||||||
|
_ntlm_free_buf(&buf);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -770,19 +802,24 @@ heim_ntlm_build_ntlm1_master(void *key, size_t len,
|
|||||||
{
|
{
|
||||||
RC4_KEY rc4;
|
RC4_KEY rc4;
|
||||||
|
|
||||||
|
memset(master, 0, sizeof(*master));
|
||||||
|
memset(session, 0, sizeof(*session));
|
||||||
|
|
||||||
if (len != MD4_DIGEST_LENGTH)
|
if (len != MD4_DIGEST_LENGTH)
|
||||||
return EINVAL;
|
return EINVAL;
|
||||||
|
|
||||||
session->length = MD4_DIGEST_LENGTH;
|
session->length = MD4_DIGEST_LENGTH;
|
||||||
session->data = malloc(session->length);
|
session->data = malloc(session->length);
|
||||||
if (session->data == NULL)
|
if (session->data == NULL) {
|
||||||
goto out;
|
session->length = 0;
|
||||||
|
return EINVAL;
|
||||||
|
}
|
||||||
master->length = MD4_DIGEST_LENGTH;
|
master->length = MD4_DIGEST_LENGTH;
|
||||||
master->data = malloc(master->length);
|
master->data = malloc(master->length);
|
||||||
if (master->data == NULL) {
|
if (master->data == NULL) {
|
||||||
free(session->data);
|
_ntlm_free_buf(master);
|
||||||
goto out;
|
_ntlm_free_buf(session);
|
||||||
|
return EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
@@ -797,21 +834,137 @@ heim_ntlm_build_ntlm1_master(void *key, size_t len,
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (RAND_bytes(session->data, session->length) != 1) {
|
if (RAND_bytes(session->data, session->length) != 1) {
|
||||||
free(master->data);
|
_ntlm_free_buf(master);
|
||||||
free(session->data);
|
_ntlm_free_buf(session);
|
||||||
goto out;
|
return EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
RC4(&rc4, master->length, session->data, master->data);
|
RC4(&rc4, master->length, session->data, master->data);
|
||||||
memset(&rc4, 0, sizeof(rc4));
|
memset(&rc4, 0, sizeof(rc4));
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
out:
|
|
||||||
master->data = NULL;
|
|
||||||
master->length = 0;
|
|
||||||
session->data = NULL;
|
|
||||||
session->length = 0;
|
|
||||||
return EINVAL;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
void
|
||||||
|
heim_ntlm_ntlmv2_key(const void *key, size_t len,
|
||||||
|
const char *username,
|
||||||
|
const char *target,
|
||||||
|
unsigned char ntlmv2[16])
|
||||||
|
{
|
||||||
|
unsigned int hmaclen;
|
||||||
|
HMAC_CTX c;
|
||||||
|
|
||||||
|
HMAC_CTX_init(&c);
|
||||||
|
HMAC_Init_ex(&c, key, len, EVP_md5(), NULL);
|
||||||
|
{
|
||||||
|
struct ntlm_buf buf;
|
||||||
|
/* uppercase username and turn it inte ucs2-le */
|
||||||
|
ascii2ucs2le(username, 1, &buf);
|
||||||
|
HMAC_Update(&c, buf.data, buf.length);
|
||||||
|
free(buf.data);
|
||||||
|
/* turn target into ucs2-le */
|
||||||
|
ascii2ucs2le(target, 0, &buf);
|
||||||
|
HMAC_Update(&c, buf.data, buf.length);
|
||||||
|
free(buf.data);
|
||||||
|
}
|
||||||
|
HMAC_Final(&c, ntlmv2, &hmaclen);
|
||||||
|
HMAC_CTX_cleanup(&c);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#define NTTIME_EPOCH 0x019DB1DED53E8000LL
|
||||||
|
|
||||||
|
static uint64_t
|
||||||
|
unix2nttime(time_t unix_time)
|
||||||
|
{
|
||||||
|
long long wt;
|
||||||
|
wt = unix_time * (uint64_t)10000000 + (uint64_t)NTTIME_EPOCH;
|
||||||
|
return wt;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
heim_ntlm_calculate_ntlm2(const void *key, size_t len,
|
||||||
|
const char *username,
|
||||||
|
const char *target,
|
||||||
|
const unsigned char serverchallange[8],
|
||||||
|
const struct ntlm_buf *infotarget,
|
||||||
|
unsigned char ntlmv2[16],
|
||||||
|
struct ntlm_buf *answer)
|
||||||
|
{
|
||||||
|
krb5_error_code ret;
|
||||||
|
krb5_data data;
|
||||||
|
unsigned int hmaclen;
|
||||||
|
unsigned char ntlmv2answer[16];
|
||||||
|
krb5_storage *sp;
|
||||||
|
unsigned char clientchallange[8];
|
||||||
|
HMAC_CTX c;
|
||||||
|
uint64_t t;
|
||||||
|
|
||||||
|
t = unix2nttime(time(NULL));
|
||||||
|
|
||||||
|
if (RAND_bytes(clientchallange, sizeof(clientchallange)) != 1)
|
||||||
|
return EINVAL;
|
||||||
|
|
||||||
|
/* calculate ntlmv2 key */
|
||||||
|
|
||||||
|
heim_ntlm_ntlmv2_key(key, len, username, target, ntlmv2);
|
||||||
|
|
||||||
|
/* calculate and build ntlmv2 answer */
|
||||||
|
|
||||||
|
sp = krb5_storage_emem();
|
||||||
|
if (sp == NULL)
|
||||||
|
return ENOMEM;
|
||||||
|
krb5_storage_set_flags(sp, KRB5_STORAGE_BYTEORDER_LE);
|
||||||
|
|
||||||
|
CHECK(krb5_store_uint32(sp, 0x01010000), 0);
|
||||||
|
CHECK(krb5_store_uint32(sp, 0), 0);
|
||||||
|
/* timestamp le 64 bit ts */
|
||||||
|
CHECK(krb5_store_uint32(sp, t & 0xffffffff), 0);
|
||||||
|
CHECK(krb5_store_uint32(sp, t >> 32), 0);
|
||||||
|
CHECK(krb5_storage_write(sp, clientchallange, 8), 8);
|
||||||
|
CHECK(krb5_storage_write(sp, infotarget->data, infotarget->length), 0);
|
||||||
|
/* unknown */
|
||||||
|
CHECK(krb5_store_uint32(sp, 0), 0);
|
||||||
|
|
||||||
|
CHECK(krb5_storage_to_data(sp, &data), 0);
|
||||||
|
krb5_storage_free(sp);
|
||||||
|
sp = NULL;
|
||||||
|
|
||||||
|
HMAC_CTX_init(&c);
|
||||||
|
HMAC_Init_ex(&c, ntlmv2, sizeof(ntlmv2), EVP_md5(), NULL);
|
||||||
|
HMAC_Update(&c, data.data, data.length);
|
||||||
|
HMAC_Update(&c, serverchallange, 8);
|
||||||
|
HMAC_Final(&c, ntlmv2answer, &hmaclen);
|
||||||
|
HMAC_CTX_cleanup(&c);
|
||||||
|
|
||||||
|
sp = krb5_storage_emem();
|
||||||
|
if (sp == NULL) {
|
||||||
|
krb5_data_free(&data);
|
||||||
|
return ENOMEM;
|
||||||
|
}
|
||||||
|
|
||||||
|
CHECK(krb5_storage_write(sp, ntlmv2answer, 16), 16);
|
||||||
|
CHECK(krb5_storage_write(sp, data.data, data.length), data.length);
|
||||||
|
krb5_data_free(&data);
|
||||||
|
|
||||||
|
CHECK(krb5_storage_to_data(sp, &data), 0);
|
||||||
|
krb5_storage_free(sp);
|
||||||
|
sp = NULL;
|
||||||
|
|
||||||
|
answer->data = data.data;
|
||||||
|
answer->length = data.length;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
out:
|
||||||
|
if (sp)
|
||||||
|
krb5_storage_free(sp);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
Reference in New Issue
Block a user