eap-ttls: Add tunneled MSCHAPv2 support

This commit is contained in:
Tim Kourt 2019-01-10 14:34:28 -08:00 committed by Denis Kenzior
parent fd2c34cebc
commit e57f46df30
1 changed files with 238 additions and 0 deletions

View File

@ -47,8 +47,11 @@ enum ttls_avp_flag {
enum radius_attr {
RADIUS_ATTR_USER_NAME = 1,
RADIUS_ATTR_USER_PASSWORD = 2,
RADIUS_ATTR_MSCHAPV2_ERROR = 2,
RADIUS_ATTR_CHAP_PASSWORD = 3,
RADIUS_ATTR_MS_CHAP_CHALLENGE = 11,
RADIUS_ATTR_MSCHAPV2_RESPONSE = 25,
RADIUS_ATTR_MSCHAPV2_SUCCESS = 26,
RADIUS_ATTR_CHAP_CHALLENGE = 60,
RADIUS_ATTR_EAP_MESSAGE = 79,
};
@ -276,6 +279,58 @@ static void build_avp_ms_chap_response(struct avp_builder *builder,
avp_builder_finalize_avp(builder);
}
#define MSCHAPV2_RESERVED_LEN 8
#define MSCHAPV2_RESPONSE_LEN 24
#define MSCHAPV2_CHALLENGE_LEN 16
#define MSCHAPV2_SERVER_RESPONSE_LEN 42
static void build_avp_mschapv2_challenge(struct avp_builder *builder,
const uint8_t *challenge)
{
avp_builder_start_avp(builder, RADIUS_ATTR_MS_CHAP_CHALLENGE, true,
RADIUS_VENDOR_ID_MICROSOFT);
memcpy(avp_builder_reserve(builder, 1, MSCHAPV2_CHALLENGE_LEN),
challenge, MSCHAPV2_CHALLENGE_LEN);
avp_builder_finalize_avp(builder);
}
static void build_avp_mschapv2_response(struct avp_builder *builder,
const uint8_t *ident,
const uint8_t *peer_challenge,
const uint8_t *response)
{
uint8_t *flags;
avp_builder_start_avp(builder, RADIUS_ATTR_MSCHAPV2_RESPONSE, true,
RADIUS_VENDOR_ID_MICROSOFT);
memcpy(avp_builder_reserve(builder, 1, CHAP_IDENT_LEN),
ident, CHAP_IDENT_LEN);
/*
* RFC 2548: Section 2.3.2.
*
* The Flags field is one octet in length. It is reserved for future
* use and MUST be zero.
*/
flags = avp_builder_reserve(builder, 1, 1);
*flags = 0;
memcpy(avp_builder_reserve(builder, 1, MSCHAPV2_CHALLENGE_LEN),
peer_challenge,
MSCHAPV2_CHALLENGE_LEN);
/*
* Reserved - This field is 8 octets long and MUST be zero.
*/
avp_builder_reserve(builder, 1, MSCHAPV2_RESERVED_LEN);
memcpy(avp_builder_reserve(builder, 1, MSCHAPV2_RESPONSE_LEN),
response, MSCHAPV2_RESPONSE_LEN);
avp_builder_finalize_avp(builder);
}
struct avp_iter {
enum radius_attr type;
uint8_t flags;
@ -514,6 +569,188 @@ static const struct phase2_method_ops phase2_mschap_ops = {
.init = eap_ttls_phase2_ms_chap_init,
};
struct mschapv2_state {
uint8_t server_challenge[MSCHAPV2_CHALLENGE_LEN + CHAP_IDENT_LEN];
uint8_t peer_challenge[MSCHAPV2_CHALLENGE_LEN];
uint8_t password_hash[16];
};
static void mschapv2_state_destroy(struct phase2_method *phase2)
{
struct mschapv2_state *state = phase2->state;
if (!state)
return;
memset(state->server_challenge, 0, MSCHAPV2_CHALLENGE_LEN +
CHAP_IDENT_LEN);
memset(state->peer_challenge, 0, MSCHAPV2_CHALLENGE_LEN);
memset(state->password_hash, 0, 16);
l_free(state);
phase2->state = NULL;
}
static bool eap_ttls_phase2_mschapv2_init(struct eap_state *eap)
{
struct phase2_method *phase2 = eap_tls_common_get_variant_data(eap);
struct phase2_credentials *credentials = &phase2->credentials;
struct mschapv2_state *mschapv2_state;
struct avp_builder *builder;
uint8_t response[MSCHAPV2_RESPONSE_LEN];
uint8_t ident;
uint8_t *data;
size_t data_len;
phase2->state = mschapv2_state = l_new(struct mschapv2_state, 1);
if (!l_getrandom(mschapv2_state->peer_challenge,
MSCHAPV2_CHALLENGE_LEN)) {
l_error("TTLS Tunneled-MSCHAPv2: Failed to generate random for "
"peer challenge.");
return false;
}
if (!eap_ttls_phase2_chap_generate_challenge(eap,
mschapv2_state->server_challenge,
MSCHAPV2_CHALLENGE_LEN +
CHAP_IDENT_LEN)) {
l_error("TTLS Tunneled-MSCHAPv2: Failed to generate CHAP "
"challenge.");
return false;
}
if (!mschap_nt_password_hash(credentials->password,
mschapv2_state->password_hash)) {
l_error("TTLS Tunneled-MSCHAPv2: Failed to generate password "
"hash.");
return false;
}
if (!mschapv2_generate_nt_response(mschapv2_state->password_hash,
mschapv2_state->peer_challenge,
mschapv2_state->server_challenge,
credentials->username, response)) {
l_error("TTLS Tunneled-MSCHAPv2: Failed to generate "
"NT response.");
return false;
}
ident = mschapv2_state->server_challenge[MSCHAPV2_CHALLENGE_LEN];
builder = avp_builder_new(512);
build_avp_user_name(builder, credentials->username);
build_avp_mschapv2_challenge(builder, mschapv2_state->server_challenge);
build_avp_mschapv2_response(builder, &ident,
mschapv2_state->peer_challenge,
response);
data = avp_builder_free(builder, false, &data_len);
eap_tls_common_tunnel_send(eap, data, data_len);
l_free(data);
return true;
}
static bool eap_ttls_phase2_mschapv2_handle_success(struct eap_state *eap,
const uint8_t *data,
size_t len)
{
struct phase2_method *phase2 = eap_tls_common_get_variant_data(eap);
struct phase2_credentials *credentials = &phase2->credentials;
struct mschapv2_state *mschapv2_state = phase2->state;
uint8_t nt_response[MSCHAPV2_RESPONSE_LEN];
char nt_server_response[MSCHAPV2_SERVER_RESPONSE_LEN];
uint8_t password_hash_hash[16];
if (len != CHAP_IDENT_LEN + MSCHAPV2_SERVER_RESPONSE_LEN) {
l_error("TTLS Tunneled MSCHAPv2: Server response has invalid "
"length.");
goto error;
}
if (!mschapv2_generate_nt_response(mschapv2_state->password_hash,
mschapv2_state->peer_challenge,
mschapv2_state->server_challenge,
credentials->username,
nt_response)) {
l_error("TTLS Tunneled-MSCHAPv2: Failed to generate "
"NT response.");
goto error;
}
if (!mschapv2_hash_nt_password_hash(mschapv2_state->password_hash,
password_hash_hash)) {
l_error("TTLS Tunneled-MSCHAPv2: Failed to generate "
"hash of the password hash.");
goto error;
}
if (!mschapv2_generate_authenticator_response(
password_hash_hash, nt_response,
mschapv2_state->peer_challenge,
mschapv2_state->server_challenge,
credentials->username,
nt_server_response)) {
l_error("TTLS Tunneled-MSCHAPv2: Failed to generate server "
"response.");
goto error;
}
if (memcmp(nt_server_response, data + CHAP_IDENT_LEN,
MSCHAPV2_SERVER_RESPONSE_LEN)) {
l_error("TTLS Tunneled-MSCHAPv2: Invalid server response.");
goto error;
}
eap_tls_common_send_empty_response(eap);
return true;
error:
eap_tls_common_set_phase2_failed(eap);
return false;
}
static bool eap_ttls_phase2_mschapv2_handle_error(struct eap_state *eap,
const uint8_t *data,
size_t len)
{
l_error("TTLS Tunneled-MSCHAPv2: Authentication failed.");
eap_tls_common_set_phase2_failed(eap);
return false;
}
static bool eap_ttls_phase2_mschapv2_handle_avp(struct eap_state *eap,
enum radius_attr type,
uint32_t vendor_id,
const uint8_t *data,
size_t len)
{
if (vendor_id != RADIUS_VENDOR_ID_MICROSOFT)
return false;
if (type == RADIUS_ATTR_MSCHAPV2_SUCCESS)
return eap_ttls_phase2_mschapv2_handle_success(eap, data, len);
else if (type == RADIUS_ATTR_MSCHAPV2_ERROR)
return eap_ttls_phase2_mschapv2_handle_error(eap, data, len);
return false;
}
static const struct phase2_method_ops phase2_mschapv2_ops = {
.init = eap_ttls_phase2_mschapv2_init,
.handle_avp = eap_ttls_phase2_mschapv2_handle_avp,
.reset = mschapv2_state_destroy,
.destroy = mschapv2_state_destroy,
};
static bool eap_ttls_phase2_pap_init(struct eap_state *eap)
{
struct phase2_method *phase2 = eap_tls_common_get_variant_data(eap);
@ -731,6 +968,7 @@ static const struct {
} tunneled_non_eap_method_ops[] = {
{ "Tunneled-CHAP", &phase2_chap_ops },
{ "Tunneled-MSCHAP", &phase2_mschap_ops },
{ "Tunneled-MSCHAPv2", &phase2_mschapv2_ops },
{ "Tunneled-PAP", &phase2_pap_ops },
{ }
};