mirror of
https://git.kernel.org/pub/scm/network/wireless/iwd.git
synced 2024-11-22 14:49:24 +01:00
eap-ttls: Add tunneled MSCHAPv2 support
This commit is contained in:
parent
fd2c34cebc
commit
e57f46df30
238
src/eap-ttls.c
238
src/eap-ttls.c
@ -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 },
|
||||
{ }
|
||||
};
|
||||
|
Loading…
Reference in New Issue
Block a user