EAP-MSCHAPv2 can use stored NT hashes in addition to plaintext passwords
This commit is contained in:
@@ -1,5 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (C) 2009 Tobias Brunner
|
* Copyright (C) 2009 Tobias Brunner
|
||||||
|
* Copyright (C) 2010 Martin Willi
|
||||||
* Hochschule fuer Technik Rapperswil
|
* Hochschule fuer Technik Rapperswil
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or modify it
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
@@ -460,37 +461,31 @@ static status_t GenerateMSK(chunk_t password_hash_hash,
|
|||||||
|
|
||||||
static status_t GenerateStuff(private_eap_mschapv2_t *this,
|
static status_t GenerateStuff(private_eap_mschapv2_t *this,
|
||||||
chunk_t server_challenge, chunk_t peer_challenge,
|
chunk_t server_challenge, chunk_t peer_challenge,
|
||||||
chunk_t username, chunk_t password)
|
chunk_t username, chunk_t nt_hash)
|
||||||
{
|
{
|
||||||
status_t status = FAILED;
|
status_t status = FAILED;
|
||||||
chunk_t password_hash = chunk_empty, password_hash_hash = chunk_empty,
|
chunk_t nt_hash_hash = chunk_empty, challenge_hash = chunk_empty;
|
||||||
challenge_hash = chunk_empty;
|
|
||||||
|
|
||||||
if (NtPasswordHash(password, &password_hash) != SUCCESS)
|
if (NtPasswordHash(nt_hash, &nt_hash_hash) != SUCCESS)
|
||||||
{
|
|
||||||
goto error;
|
|
||||||
}
|
|
||||||
if (NtPasswordHash(password_hash, &password_hash_hash) != SUCCESS)
|
|
||||||
{
|
{
|
||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
if (ChallengeHash(peer_challenge, server_challenge, username,
|
if (ChallengeHash(peer_challenge, server_challenge, username,
|
||||||
&challenge_hash) != SUCCESS)
|
&challenge_hash) != SUCCESS)
|
||||||
{
|
{
|
||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
|
if (ChallengeResponse(challenge_hash, nt_hash,
|
||||||
if (ChallengeResponse(challenge_hash, password_hash,
|
&this->nt_response) != SUCCESS)
|
||||||
&this->nt_response) != SUCCESS)
|
|
||||||
{
|
{
|
||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
if (AuthenticatorResponse(password_hash_hash, challenge_hash,
|
if (AuthenticatorResponse(nt_hash_hash, challenge_hash,
|
||||||
this->nt_response, &this->auth_response) != SUCCESS)
|
this->nt_response, &this->auth_response) != SUCCESS)
|
||||||
{
|
{
|
||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
if (GenerateMSK(password_hash_hash, this->nt_response, &this->msk) != SUCCESS)
|
if (GenerateMSK(nt_hash_hash, this->nt_response, &this->msk) != SUCCESS)
|
||||||
{
|
{
|
||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
@@ -498,8 +493,7 @@ static status_t GenerateStuff(private_eap_mschapv2_t *this,
|
|||||||
status = SUCCESS;
|
status = SUCCESS;
|
||||||
|
|
||||||
error:
|
error:
|
||||||
chunk_free(&password_hash);
|
chunk_free(&nt_hash_hash);
|
||||||
chunk_free(&password_hash_hash);
|
|
||||||
chunk_free(&challenge_hash);
|
chunk_free(&challenge_hash);
|
||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
@@ -613,6 +607,39 @@ static status_t initiate_server(private_eap_mschapv2_t *this, eap_payload_t **ou
|
|||||||
return NEED_MORE;
|
return NEED_MORE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool get_nt_hash(private_eap_mschapv2_t *this, identification_t *me,
|
||||||
|
identification_t *other, chunk_t *nt_hash)
|
||||||
|
{
|
||||||
|
shared_key_t *shared;
|
||||||
|
chunk_t password;
|
||||||
|
|
||||||
|
/* try to find a stored NT_HASH first */
|
||||||
|
shared = charon->credentials->get_shared(charon->credentials,
|
||||||
|
SHARED_NT_HASH, me, other);
|
||||||
|
if (shared )
|
||||||
|
{
|
||||||
|
*nt_hash = chunk_clone(shared->get_key(shared));
|
||||||
|
shared->destroy(shared);
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* fallback to plaintext password */
|
||||||
|
shared = charon->credentials->get_shared(charon->credentials,
|
||||||
|
SHARED_EAP, me, other);
|
||||||
|
if (shared)
|
||||||
|
{
|
||||||
|
password = ascii_to_unicode(shared->get_key(shared));
|
||||||
|
shared->destroy(shared);
|
||||||
|
|
||||||
|
if (NtPasswordHash(password, nt_hash) == SUCCESS)
|
||||||
|
{
|
||||||
|
chunk_clear(&password);
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
chunk_clear(&password);
|
||||||
|
}
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Process MS-CHAPv2 Challenge Requests
|
* Process MS-CHAPv2 Challenge Requests
|
||||||
@@ -624,8 +651,7 @@ static status_t process_peer_challenge(private_eap_mschapv2_t *this,
|
|||||||
eap_mschapv2_header_t *eap;
|
eap_mschapv2_header_t *eap;
|
||||||
eap_mschapv2_challenge_t *cha;
|
eap_mschapv2_challenge_t *cha;
|
||||||
eap_mschapv2_response_t *res;
|
eap_mschapv2_response_t *res;
|
||||||
shared_key_t *shared;
|
chunk_t data, peer_challenge, username, nt_hash;
|
||||||
chunk_t data, peer_challenge, username, password;
|
|
||||||
u_int16_t len = RESPONSE_PAYLOAD_LEN;
|
u_int16_t len = RESPONSE_PAYLOAD_LEN;
|
||||||
|
|
||||||
data = in->get_data(in);
|
data = in->get_data(in);
|
||||||
@@ -660,28 +686,24 @@ static status_t process_peer_challenge(private_eap_mschapv2_t *this,
|
|||||||
rng->get_bytes(rng, CHALLENGE_LEN, peer_challenge.ptr);
|
rng->get_bytes(rng, CHALLENGE_LEN, peer_challenge.ptr);
|
||||||
rng->destroy(rng);
|
rng->destroy(rng);
|
||||||
|
|
||||||
shared = charon->credentials->get_shared(charon->credentials,
|
if (!get_nt_hash(this, this->peer, this->server, &nt_hash))
|
||||||
SHARED_EAP, this->peer, this->server);
|
|
||||||
if (shared == NULL)
|
|
||||||
{
|
{
|
||||||
DBG1(DBG_IKE, "no EAP key found for hosts '%Y' - '%Y'",
|
DBG1(DBG_IKE, "no EAP key found for hosts '%Y' - '%Y'",
|
||||||
this->server, this->peer);
|
this->server, this->peer);
|
||||||
return NOT_FOUND;
|
return NOT_FOUND;
|
||||||
}
|
}
|
||||||
|
|
||||||
password = ascii_to_unicode(shared->get_key(shared));
|
|
||||||
shared->destroy(shared);
|
|
||||||
|
|
||||||
username = extract_username(this->peer);
|
username = extract_username(this->peer);
|
||||||
len += username.len;
|
len += username.len;
|
||||||
|
|
||||||
if (GenerateStuff(this, this->challenge, peer_challenge, username, password) != SUCCESS)
|
if (GenerateStuff(this, this->challenge, peer_challenge,
|
||||||
|
username, nt_hash) != SUCCESS)
|
||||||
{
|
{
|
||||||
DBG1(DBG_IKE, "EAP-MS-CHAPv2 generating NT-Response failed");
|
DBG1(DBG_IKE, "EAP-MS-CHAPv2 generating NT-Response failed");
|
||||||
chunk_clear(&password);
|
chunk_clear(&nt_hash);
|
||||||
return FAILED;
|
return FAILED;
|
||||||
}
|
}
|
||||||
chunk_clear(&password);
|
chunk_clear(&nt_hash);
|
||||||
|
|
||||||
eap = alloca(len);
|
eap = alloca(len);
|
||||||
eap->code = EAP_RESPONSE;
|
eap->code = EAP_RESPONSE;
|
||||||
@@ -995,9 +1017,8 @@ static status_t process_server_response(private_eap_mschapv2_t *this,
|
|||||||
{
|
{
|
||||||
eap_mschapv2_header_t *eap;
|
eap_mschapv2_header_t *eap;
|
||||||
eap_mschapv2_response_t *res;
|
eap_mschapv2_response_t *res;
|
||||||
chunk_t data, peer_challenge, username, password;
|
chunk_t data, peer_challenge, username, nt_hash;
|
||||||
identification_t *userid;
|
identification_t *userid;
|
||||||
shared_key_t *shared;
|
|
||||||
int name_len;
|
int name_len;
|
||||||
char buf[256];
|
char buf[256];
|
||||||
|
|
||||||
@@ -1019,9 +1040,7 @@ static status_t process_server_response(private_eap_mschapv2_t *this,
|
|||||||
DBG2(DBG_IKE, "EAP-MS-CHAPv2 username: '%Y'", userid);
|
DBG2(DBG_IKE, "EAP-MS-CHAPv2 username: '%Y'", userid);
|
||||||
username = extract_username(userid);
|
username = extract_username(userid);
|
||||||
|
|
||||||
shared = charon->credentials->get_shared(charon->credentials,
|
if (!get_nt_hash(this, this->server, userid, &nt_hash))
|
||||||
SHARED_EAP, this->server, userid);
|
|
||||||
if (shared == NULL)
|
|
||||||
{
|
{
|
||||||
DBG1(DBG_IKE, "no EAP key found for hosts '%Y' - '%Y'",
|
DBG1(DBG_IKE, "no EAP key found for hosts '%Y' - '%Y'",
|
||||||
this->server, userid);
|
this->server, userid);
|
||||||
@@ -1035,21 +1054,19 @@ static status_t process_server_response(private_eap_mschapv2_t *this,
|
|||||||
return process_server_retry(this, out);
|
return process_server_retry(this, out);
|
||||||
}
|
}
|
||||||
|
|
||||||
password = ascii_to_unicode(shared->get_key(shared));
|
|
||||||
shared->destroy(shared);
|
|
||||||
|
|
||||||
if (GenerateStuff(this, this->challenge, peer_challenge,
|
if (GenerateStuff(this, this->challenge, peer_challenge,
|
||||||
username, password) != SUCCESS)
|
username, nt_hash) != SUCCESS)
|
||||||
{
|
{
|
||||||
DBG1(DBG_IKE, "EAP-MS-CHAPv2 verification failed");
|
DBG1(DBG_IKE, "EAP-MS-CHAPv2 verification failed");
|
||||||
userid->destroy(userid);
|
userid->destroy(userid);
|
||||||
chunk_clear(&password);
|
chunk_clear(&nt_hash);
|
||||||
return FAILED;
|
return FAILED;
|
||||||
}
|
}
|
||||||
userid->destroy(userid);
|
userid->destroy(userid);
|
||||||
chunk_clear(&password);
|
chunk_clear(&nt_hash);
|
||||||
|
|
||||||
if (memeq(res->response.nt_response, this->nt_response.ptr, this->nt_response.len))
|
if (memeq(res->response.nt_response, this->nt_response.ptr,
|
||||||
|
this->nt_response.len))
|
||||||
{
|
{
|
||||||
chunk_t hex;
|
chunk_t hex;
|
||||||
char msg[AUTH_RESPONSE_LEN + sizeof(SUCCESS_MESSAGE)];
|
char msg[AUTH_RESPONSE_LEN + sizeof(SUCCESS_MESSAGE)];
|
||||||
|
|||||||
@@ -41,6 +41,8 @@ enum shared_key_type_t {
|
|||||||
SHARED_PRIVATE_KEY_PASS,
|
SHARED_PRIVATE_KEY_PASS,
|
||||||
/** PIN to unlock a smartcard */
|
/** PIN to unlock a smartcard */
|
||||||
SHARED_PIN,
|
SHARED_PIN,
|
||||||
|
/** Calculated NT Hash = MD4(UTF-16LE(password)) */
|
||||||
|
SHARED_NT_HASH,
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
Reference in New Issue
Block a user