f534ee0804
Reduce the amount of time keying material (MSK, EMSK, temporary private data) remains in memory in EAP methods. This provides additional protection should there be any issues that could expose process memory to external observers. Signed-off-by: Jouni Malinen <j@w1.fi>
765 lines
20 KiB
C
765 lines
20 KiB
C
/*
|
|
* EAP peer method: EAP-EKE (RFC 6124)
|
|
* Copyright (c) 2013, Jouni Malinen <j@w1.fi>
|
|
*
|
|
* This software may be distributed under the terms of the BSD license.
|
|
* See README for more details.
|
|
*/
|
|
|
|
#include "includes.h"
|
|
|
|
#include "common.h"
|
|
#include "crypto/random.h"
|
|
#include "eap_peer/eap_i.h"
|
|
#include "eap_common/eap_eke_common.h"
|
|
|
|
struct eap_eke_data {
|
|
enum {
|
|
IDENTITY, COMMIT, CONFIRM, SUCCESS, FAILURE
|
|
} state;
|
|
u8 msk[EAP_MSK_LEN];
|
|
u8 emsk[EAP_EMSK_LEN];
|
|
u8 *peerid;
|
|
size_t peerid_len;
|
|
u8 *serverid;
|
|
size_t serverid_len;
|
|
u8 dh_priv[EAP_EKE_MAX_DH_LEN];
|
|
struct eap_eke_session sess;
|
|
u8 nonce_p[EAP_EKE_MAX_NONCE_LEN];
|
|
u8 nonce_s[EAP_EKE_MAX_NONCE_LEN];
|
|
struct wpabuf *msgs;
|
|
u8 dhgroup; /* forced DH group or 0 to allow all supported */
|
|
u8 encr; /* forced encryption algorithm or 0 to allow all supported */
|
|
u8 prf; /* forced PRF or 0 to allow all supported */
|
|
u8 mac; /* forced MAC or 0 to allow all supported */
|
|
};
|
|
|
|
|
|
static const char * eap_eke_state_txt(int state)
|
|
{
|
|
switch (state) {
|
|
case IDENTITY:
|
|
return "IDENTITY";
|
|
case COMMIT:
|
|
return "COMMIT";
|
|
case CONFIRM:
|
|
return "CONFIRM";
|
|
case SUCCESS:
|
|
return "SUCCESS";
|
|
case FAILURE:
|
|
return "FAILURE";
|
|
default:
|
|
return "?";
|
|
}
|
|
}
|
|
|
|
|
|
static void eap_eke_state(struct eap_eke_data *data, int state)
|
|
{
|
|
wpa_printf(MSG_DEBUG, "EAP-EKE: %s -> %s",
|
|
eap_eke_state_txt(data->state), eap_eke_state_txt(state));
|
|
data->state = state;
|
|
}
|
|
|
|
|
|
static void eap_eke_deinit(struct eap_sm *sm, void *priv);
|
|
|
|
|
|
static void * eap_eke_init(struct eap_sm *sm)
|
|
{
|
|
struct eap_eke_data *data;
|
|
const u8 *identity, *password;
|
|
size_t identity_len, password_len;
|
|
const char *phase1;
|
|
|
|
password = eap_get_config_password(sm, &password_len);
|
|
if (!password) {
|
|
wpa_printf(MSG_INFO, "EAP-EKE: No password configured");
|
|
return NULL;
|
|
}
|
|
|
|
data = os_zalloc(sizeof(*data));
|
|
if (data == NULL)
|
|
return NULL;
|
|
eap_eke_state(data, IDENTITY);
|
|
|
|
identity = eap_get_config_identity(sm, &identity_len);
|
|
if (identity) {
|
|
data->peerid = os_malloc(identity_len);
|
|
if (data->peerid == NULL) {
|
|
eap_eke_deinit(sm, data);
|
|
return NULL;
|
|
}
|
|
os_memcpy(data->peerid, identity, identity_len);
|
|
data->peerid_len = identity_len;
|
|
}
|
|
|
|
phase1 = eap_get_config_phase1(sm);
|
|
if (phase1) {
|
|
const char *pos;
|
|
|
|
pos = os_strstr(phase1, "dhgroup=");
|
|
if (pos) {
|
|
data->dhgroup = atoi(pos + 8);
|
|
wpa_printf(MSG_DEBUG, "EAP-EKE: Forced dhgroup %u",
|
|
data->dhgroup);
|
|
}
|
|
|
|
pos = os_strstr(phase1, "encr=");
|
|
if (pos) {
|
|
data->encr = atoi(pos + 5);
|
|
wpa_printf(MSG_DEBUG, "EAP-EKE: Forced encr %u",
|
|
data->encr);
|
|
}
|
|
|
|
pos = os_strstr(phase1, "prf=");
|
|
if (pos) {
|
|
data->prf = atoi(pos + 4);
|
|
wpa_printf(MSG_DEBUG, "EAP-EKE: Forced prf %u",
|
|
data->prf);
|
|
}
|
|
|
|
pos = os_strstr(phase1, "mac=");
|
|
if (pos) {
|
|
data->mac = atoi(pos + 4);
|
|
wpa_printf(MSG_DEBUG, "EAP-EKE: Forced mac %u",
|
|
data->mac);
|
|
}
|
|
}
|
|
|
|
return data;
|
|
}
|
|
|
|
|
|
static void eap_eke_deinit(struct eap_sm *sm, void *priv)
|
|
{
|
|
struct eap_eke_data *data = priv;
|
|
eap_eke_session_clean(&data->sess);
|
|
os_free(data->serverid);
|
|
os_free(data->peerid);
|
|
wpabuf_free(data->msgs);
|
|
bin_clear_free(data, sizeof(*data));
|
|
}
|
|
|
|
|
|
static struct wpabuf * eap_eke_build_msg(struct eap_eke_data *data, int id,
|
|
size_t length, u8 eke_exch)
|
|
{
|
|
struct wpabuf *msg;
|
|
size_t plen;
|
|
|
|
plen = 1 + length;
|
|
|
|
msg = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_EKE, plen,
|
|
EAP_CODE_RESPONSE, id);
|
|
if (msg == NULL) {
|
|
wpa_printf(MSG_ERROR, "EAP-EKE: Failed to allocate memory");
|
|
return NULL;
|
|
}
|
|
|
|
wpabuf_put_u8(msg, eke_exch);
|
|
|
|
return msg;
|
|
}
|
|
|
|
|
|
static int eap_eke_supp_dhgroup(u8 dhgroup)
|
|
{
|
|
return dhgroup == EAP_EKE_DHGROUP_EKE_2 ||
|
|
dhgroup == EAP_EKE_DHGROUP_EKE_5 ||
|
|
dhgroup == EAP_EKE_DHGROUP_EKE_14 ||
|
|
dhgroup == EAP_EKE_DHGROUP_EKE_15 ||
|
|
dhgroup == EAP_EKE_DHGROUP_EKE_16;
|
|
}
|
|
|
|
|
|
static int eap_eke_supp_encr(u8 encr)
|
|
{
|
|
return encr == EAP_EKE_ENCR_AES128_CBC;
|
|
}
|
|
|
|
|
|
static int eap_eke_supp_prf(u8 prf)
|
|
{
|
|
return prf == EAP_EKE_PRF_HMAC_SHA1 ||
|
|
prf == EAP_EKE_PRF_HMAC_SHA2_256;
|
|
}
|
|
|
|
|
|
static int eap_eke_supp_mac(u8 mac)
|
|
{
|
|
return mac == EAP_EKE_MAC_HMAC_SHA1 ||
|
|
mac == EAP_EKE_MAC_HMAC_SHA2_256;
|
|
}
|
|
|
|
|
|
static struct wpabuf * eap_eke_build_fail(struct eap_eke_data *data,
|
|
struct eap_method_ret *ret,
|
|
const struct wpabuf *reqData,
|
|
u32 failure_code)
|
|
{
|
|
struct wpabuf *resp;
|
|
|
|
wpa_printf(MSG_DEBUG, "EAP-EKE: Sending EAP-EKE-Failure/Response - code=0x%x",
|
|
failure_code);
|
|
|
|
resp = eap_eke_build_msg(data, eap_get_id(reqData), 4, EAP_EKE_FAILURE);
|
|
if (resp)
|
|
wpabuf_put_be32(resp, failure_code);
|
|
|
|
os_memset(data->dh_priv, 0, sizeof(data->dh_priv));
|
|
eap_eke_session_clean(&data->sess);
|
|
|
|
eap_eke_state(data, FAILURE);
|
|
ret->methodState = METHOD_DONE;
|
|
ret->decision = DECISION_FAIL;
|
|
ret->allowNotifications = FALSE;
|
|
|
|
return resp;
|
|
}
|
|
|
|
|
|
static struct wpabuf * eap_eke_process_id(struct eap_eke_data *data,
|
|
struct eap_method_ret *ret,
|
|
const struct wpabuf *reqData,
|
|
const u8 *payload,
|
|
size_t payload_len)
|
|
{
|
|
struct wpabuf *resp;
|
|
unsigned num_prop, i;
|
|
const u8 *pos, *end;
|
|
const u8 *prop = NULL;
|
|
u8 idtype;
|
|
|
|
if (data->state != IDENTITY) {
|
|
return eap_eke_build_fail(data, ret, reqData,
|
|
EAP_EKE_FAIL_PROTO_ERROR);
|
|
}
|
|
|
|
wpa_printf(MSG_DEBUG, "EAP-EKE: Received EAP-EKE-ID/Request");
|
|
|
|
if (payload_len < 2 + 4) {
|
|
wpa_printf(MSG_DEBUG, "EAP-EKE: Too short ID/Request Data");
|
|
return eap_eke_build_fail(data, ret, reqData,
|
|
EAP_EKE_FAIL_PROTO_ERROR);
|
|
}
|
|
|
|
pos = payload;
|
|
end = payload + payload_len;
|
|
|
|
num_prop = *pos++;
|
|
pos++; /* Ignore Reserved field */
|
|
|
|
if (pos + num_prop * 4 > end) {
|
|
wpa_printf(MSG_DEBUG, "EAP-EKE: Too short ID/Request Data (num_prop=%u)",
|
|
num_prop);
|
|
return eap_eke_build_fail(data, ret, reqData,
|
|
EAP_EKE_FAIL_PROTO_ERROR);
|
|
}
|
|
|
|
for (i = 0; i < num_prop; i++) {
|
|
const u8 *tmp = pos;
|
|
|
|
wpa_printf(MSG_DEBUG, "EAP-EKE: Proposal #%u: dh=%u encr=%u prf=%u mac=%u",
|
|
i, pos[0], pos[1], pos[2], pos[3]);
|
|
pos += 4;
|
|
|
|
if ((data->dhgroup && data->dhgroup != *tmp) ||
|
|
!eap_eke_supp_dhgroup(*tmp))
|
|
continue;
|
|
tmp++;
|
|
if ((data->encr && data->encr != *tmp) ||
|
|
!eap_eke_supp_encr(*tmp))
|
|
continue;
|
|
tmp++;
|
|
if ((data->prf && data->prf != *tmp) ||
|
|
!eap_eke_supp_prf(*tmp))
|
|
continue;
|
|
tmp++;
|
|
if ((data->mac && data->mac != *tmp) ||
|
|
!eap_eke_supp_mac(*tmp))
|
|
continue;
|
|
|
|
prop = tmp - 3;
|
|
if (eap_eke_session_init(&data->sess, prop[0], prop[1], prop[2],
|
|
prop[3]) < 0) {
|
|
prop = NULL;
|
|
continue;
|
|
}
|
|
|
|
wpa_printf(MSG_DEBUG, "EAP-EKE: Selected proposal");
|
|
break;
|
|
}
|
|
|
|
if (prop == NULL) {
|
|
wpa_printf(MSG_DEBUG, "EAP-EKE: No acceptable proposal found");
|
|
return eap_eke_build_fail(data, ret, reqData,
|
|
EAP_EKE_FAIL_NO_PROPOSAL_CHOSEN);
|
|
}
|
|
|
|
pos += (num_prop - i - 1) * 4;
|
|
|
|
if (pos == end) {
|
|
wpa_printf(MSG_DEBUG, "EAP-EKE: Too short ID/Request Data to include IDType/Identity");
|
|
return eap_eke_build_fail(data, ret, reqData,
|
|
EAP_EKE_FAIL_PROTO_ERROR);
|
|
}
|
|
|
|
idtype = *pos++;
|
|
wpa_printf(MSG_DEBUG, "EAP-EKE: Server IDType %u", idtype);
|
|
wpa_hexdump_ascii(MSG_DEBUG, "EAP-EKE: Server Identity",
|
|
pos, end - pos);
|
|
os_free(data->serverid);
|
|
data->serverid = os_malloc(end - pos);
|
|
if (data->serverid == NULL) {
|
|
return eap_eke_build_fail(data, ret, reqData,
|
|
EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR);
|
|
}
|
|
os_memcpy(data->serverid, pos, end - pos);
|
|
data->serverid_len = end - pos;
|
|
|
|
wpa_printf(MSG_DEBUG, "EAP-EKE: Sending EAP-EKE-ID/Response");
|
|
|
|
resp = eap_eke_build_msg(data, eap_get_id(reqData),
|
|
2 + 4 + 1 + data->peerid_len,
|
|
EAP_EKE_ID);
|
|
if (resp == NULL) {
|
|
return eap_eke_build_fail(data, ret, reqData,
|
|
EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR);
|
|
}
|
|
|
|
wpabuf_put_u8(resp, 1); /* NumProposals */
|
|
wpabuf_put_u8(resp, 0); /* Reserved */
|
|
wpabuf_put_data(resp, prop, 4); /* Selected Proposal */
|
|
wpabuf_put_u8(resp, EAP_EKE_ID_NAI);
|
|
if (data->peerid)
|
|
wpabuf_put_data(resp, data->peerid, data->peerid_len);
|
|
|
|
wpabuf_free(data->msgs);
|
|
data->msgs = wpabuf_alloc(wpabuf_len(reqData) + wpabuf_len(resp));
|
|
if (data->msgs == NULL) {
|
|
wpabuf_free(resp);
|
|
return eap_eke_build_fail(data, ret, reqData,
|
|
EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR);
|
|
}
|
|
wpabuf_put_buf(data->msgs, reqData);
|
|
wpabuf_put_buf(data->msgs, resp);
|
|
|
|
eap_eke_state(data, COMMIT);
|
|
|
|
return resp;
|
|
}
|
|
|
|
|
|
static struct wpabuf * eap_eke_process_commit(struct eap_sm *sm,
|
|
struct eap_eke_data *data,
|
|
struct eap_method_ret *ret,
|
|
const struct wpabuf *reqData,
|
|
const u8 *payload,
|
|
size_t payload_len)
|
|
{
|
|
struct wpabuf *resp;
|
|
const u8 *pos, *end, *dhcomp;
|
|
size_t prot_len;
|
|
u8 *rpos;
|
|
u8 key[EAP_EKE_MAX_KEY_LEN];
|
|
u8 pub[EAP_EKE_MAX_DH_LEN];
|
|
const u8 *password;
|
|
size_t password_len;
|
|
|
|
if (data->state != COMMIT) {
|
|
wpa_printf(MSG_DEBUG, "EAP-EKE: EAP-EKE-Commit/Request received in unexpected state (%d)", data->state);
|
|
return eap_eke_build_fail(data, ret, reqData,
|
|
EAP_EKE_FAIL_PROTO_ERROR);
|
|
}
|
|
|
|
wpa_printf(MSG_DEBUG, "EAP-EKE: Received EAP-EKE-Commit/Request");
|
|
|
|
password = eap_get_config_password(sm, &password_len);
|
|
if (password == NULL) {
|
|
wpa_printf(MSG_INFO, "EAP-EKE: No password configured!");
|
|
return eap_eke_build_fail(data, ret, reqData,
|
|
EAP_EKE_FAIL_PASSWD_NOT_FOUND);
|
|
}
|
|
|
|
pos = payload;
|
|
end = payload + payload_len;
|
|
|
|
if (pos + data->sess.dhcomp_len > end) {
|
|
wpa_printf(MSG_DEBUG, "EAP-EKE: Too short EAP-EKE-Commit");
|
|
return eap_eke_build_fail(data, ret, reqData,
|
|
EAP_EKE_FAIL_PROTO_ERROR);
|
|
}
|
|
|
|
wpa_hexdump(MSG_DEBUG, "EAP-EKE: DHComponent_S",
|
|
pos, data->sess.dhcomp_len);
|
|
dhcomp = pos;
|
|
pos += data->sess.dhcomp_len;
|
|
wpa_hexdump(MSG_DEBUG, "EAP-EKE: CBValue", pos, end - pos);
|
|
|
|
/*
|
|
* temp = prf(0+, password)
|
|
* key = prf+(temp, ID_S | ID_P)
|
|
*/
|
|
if (eap_eke_derive_key(&data->sess, password, password_len,
|
|
data->serverid, data->serverid_len,
|
|
data->peerid, data->peerid_len, key) < 0) {
|
|
wpa_printf(MSG_INFO, "EAP-EKE: Failed to derive key");
|
|
return eap_eke_build_fail(data, ret, reqData,
|
|
EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR);
|
|
}
|
|
|
|
/*
|
|
* y_p = g ^ x_p (mod p)
|
|
* x_p = random number 2 .. p-1
|
|
*/
|
|
if (eap_eke_dh_init(data->sess.dhgroup, data->dh_priv, pub) < 0) {
|
|
wpa_printf(MSG_INFO, "EAP-EKE: Failed to initialize DH");
|
|
os_memset(key, 0, sizeof(key));
|
|
return eap_eke_build_fail(data, ret, reqData,
|
|
EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR);
|
|
}
|
|
|
|
if (eap_eke_shared_secret(&data->sess, key, data->dh_priv, dhcomp) < 0)
|
|
{
|
|
wpa_printf(MSG_INFO, "EAP-EKE: Failed to derive shared secret");
|
|
os_memset(key, 0, sizeof(key));
|
|
return eap_eke_build_fail(data, ret, reqData,
|
|
EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR);
|
|
}
|
|
|
|
if (eap_eke_derive_ke_ki(&data->sess,
|
|
data->serverid, data->serverid_len,
|
|
data->peerid, data->peerid_len) < 0) {
|
|
wpa_printf(MSG_INFO, "EAP-EKE: Failed to derive Ke/Ki");
|
|
os_memset(key, 0, sizeof(key));
|
|
return eap_eke_build_fail(data, ret, reqData,
|
|
EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR);
|
|
}
|
|
|
|
wpa_printf(MSG_DEBUG, "EAP-EKE: Sending EAP-EKE-Commit/Response");
|
|
|
|
resp = eap_eke_build_msg(data, eap_get_id(reqData),
|
|
data->sess.dhcomp_len + data->sess.pnonce_len,
|
|
EAP_EKE_COMMIT);
|
|
if (resp == NULL) {
|
|
os_memset(key, 0, sizeof(key));
|
|
return eap_eke_build_fail(data, ret, reqData,
|
|
EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR);
|
|
}
|
|
|
|
/* DHComponent_P = Encr(key, y_p) */
|
|
rpos = wpabuf_put(resp, data->sess.dhcomp_len);
|
|
if (eap_eke_dhcomp(&data->sess, key, pub, rpos) < 0) {
|
|
wpa_printf(MSG_INFO, "EAP-EKE: Failed to build DHComponent_P");
|
|
os_memset(key, 0, sizeof(key));
|
|
return eap_eke_build_fail(data, ret, reqData,
|
|
EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR);
|
|
}
|
|
os_memset(key, 0, sizeof(key));
|
|
|
|
wpa_hexdump(MSG_DEBUG, "EAP-EKE: DHComponent_P",
|
|
rpos, data->sess.dhcomp_len);
|
|
|
|
if (random_get_bytes(data->nonce_p, data->sess.nonce_len)) {
|
|
wpabuf_free(resp);
|
|
return eap_eke_build_fail(data, ret, reqData,
|
|
EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR);
|
|
}
|
|
wpa_hexdump_key(MSG_DEBUG, "EAP-EKE: Nonce_P",
|
|
data->nonce_p, data->sess.nonce_len);
|
|
prot_len = wpabuf_tailroom(resp);
|
|
if (eap_eke_prot(&data->sess, data->nonce_p, data->sess.nonce_len,
|
|
wpabuf_put(resp, 0), &prot_len) < 0) {
|
|
wpabuf_free(resp);
|
|
return eap_eke_build_fail(data, ret, reqData,
|
|
EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR);
|
|
}
|
|
wpa_hexdump(MSG_DEBUG, "EAP-EKE: PNonce_P",
|
|
wpabuf_put(resp, 0), prot_len);
|
|
wpabuf_put(resp, prot_len);
|
|
|
|
/* TODO: CBValue */
|
|
|
|
if (wpabuf_resize(&data->msgs, wpabuf_len(reqData) + wpabuf_len(resp))
|
|
< 0) {
|
|
wpabuf_free(resp);
|
|
return eap_eke_build_fail(data, ret, reqData,
|
|
EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR);
|
|
}
|
|
wpabuf_put_buf(data->msgs, reqData);
|
|
wpabuf_put_buf(data->msgs, resp);
|
|
|
|
eap_eke_state(data, CONFIRM);
|
|
|
|
return resp;
|
|
}
|
|
|
|
|
|
static struct wpabuf * eap_eke_process_confirm(struct eap_eke_data *data,
|
|
struct eap_method_ret *ret,
|
|
const struct wpabuf *reqData,
|
|
const u8 *payload,
|
|
size_t payload_len)
|
|
{
|
|
struct wpabuf *resp;
|
|
const u8 *pos, *end;
|
|
size_t prot_len;
|
|
u8 nonces[2 * EAP_EKE_MAX_NONCE_LEN];
|
|
u8 auth_s[EAP_EKE_MAX_HASH_LEN];
|
|
size_t decrypt_len;
|
|
u8 *auth;
|
|
|
|
if (data->state != CONFIRM) {
|
|
wpa_printf(MSG_DEBUG, "EAP-EKE: EAP-EKE-Confirm/Request received in unexpected state (%d)",
|
|
data->state);
|
|
return eap_eke_build_fail(data, ret, reqData,
|
|
EAP_EKE_FAIL_PROTO_ERROR);
|
|
}
|
|
|
|
wpa_printf(MSG_DEBUG, "EAP-EKE: Received EAP-EKE-Confirm/Request");
|
|
|
|
pos = payload;
|
|
end = payload + payload_len;
|
|
|
|
if (pos + data->sess.pnonce_ps_len + data->sess.prf_len > end) {
|
|
wpa_printf(MSG_DEBUG, "EAP-EKE: Too short EAP-EKE-Confirm");
|
|
return eap_eke_build_fail(data, ret, reqData,
|
|
EAP_EKE_FAIL_PROTO_ERROR);
|
|
}
|
|
|
|
decrypt_len = sizeof(nonces);
|
|
if (eap_eke_decrypt_prot(&data->sess, pos, data->sess.pnonce_ps_len,
|
|
nonces, &decrypt_len) < 0) {
|
|
wpa_printf(MSG_INFO, "EAP-EKE: Failed to decrypt PNonce_PS");
|
|
return eap_eke_build_fail(data, ret, reqData,
|
|
EAP_EKE_FAIL_AUTHENTICATION_FAIL);
|
|
}
|
|
if (decrypt_len != (size_t) 2 * data->sess.nonce_len) {
|
|
wpa_printf(MSG_INFO, "EAP-EKE: PNonce_PS protected data length does not match length of Nonce_P and Nonce_S");
|
|
return eap_eke_build_fail(data, ret, reqData,
|
|
EAP_EKE_FAIL_AUTHENTICATION_FAIL);
|
|
}
|
|
wpa_hexdump_key(MSG_DEBUG, "EAP-EKE: Received Nonce_P | Nonce_S",
|
|
nonces, 2 * data->sess.nonce_len);
|
|
if (os_memcmp(data->nonce_p, nonces, data->sess.nonce_len) != 0) {
|
|
wpa_printf(MSG_INFO, "EAP-EKE: Received Nonce_P does not match transmitted Nonce_P");
|
|
return eap_eke_build_fail(data, ret, reqData,
|
|
EAP_EKE_FAIL_AUTHENTICATION_FAIL);
|
|
}
|
|
|
|
os_memcpy(data->nonce_s, nonces + data->sess.nonce_len,
|
|
data->sess.nonce_len);
|
|
wpa_hexdump_key(MSG_DEBUG, "EAP-EKE: Nonce_S",
|
|
data->nonce_s, data->sess.nonce_len);
|
|
|
|
if (eap_eke_derive_ka(&data->sess, data->serverid, data->serverid_len,
|
|
data->peerid, data->peerid_len,
|
|
data->nonce_p, data->nonce_s) < 0) {
|
|
return eap_eke_build_fail(data, ret, reqData,
|
|
EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR);
|
|
}
|
|
|
|
if (eap_eke_auth(&data->sess, "EAP-EKE server", data->msgs, auth_s) < 0)
|
|
{
|
|
return eap_eke_build_fail(data, ret, reqData,
|
|
EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR);
|
|
}
|
|
wpa_hexdump(MSG_DEBUG, "EAP-EKE: Auth_S", auth_s, data->sess.prf_len);
|
|
if (os_memcmp_const(auth_s, pos + data->sess.pnonce_ps_len,
|
|
data->sess.prf_len) != 0) {
|
|
wpa_printf(MSG_INFO, "EAP-EKE: Auth_S does not match");
|
|
return eap_eke_build_fail(data, ret, reqData,
|
|
EAP_EKE_FAIL_AUTHENTICATION_FAIL);
|
|
}
|
|
|
|
wpa_printf(MSG_DEBUG, "EAP-EKE: Sending EAP-EKE-Confirm/Response");
|
|
|
|
resp = eap_eke_build_msg(data, eap_get_id(reqData),
|
|
data->sess.pnonce_len + data->sess.prf_len,
|
|
EAP_EKE_CONFIRM);
|
|
if (resp == NULL) {
|
|
return eap_eke_build_fail(data, ret, reqData,
|
|
EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR);
|
|
}
|
|
|
|
prot_len = wpabuf_tailroom(resp);
|
|
if (eap_eke_prot(&data->sess, data->nonce_s, data->sess.nonce_len,
|
|
wpabuf_put(resp, 0), &prot_len) < 0) {
|
|
wpabuf_free(resp);
|
|
return eap_eke_build_fail(data, ret, reqData,
|
|
EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR);
|
|
}
|
|
wpabuf_put(resp, prot_len);
|
|
|
|
auth = wpabuf_put(resp, data->sess.prf_len);
|
|
if (eap_eke_auth(&data->sess, "EAP-EKE peer", data->msgs, auth) < 0) {
|
|
wpabuf_free(resp);
|
|
return eap_eke_build_fail(data, ret, reqData,
|
|
EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR);
|
|
}
|
|
wpa_hexdump(MSG_DEBUG, "EAP-EKE: Auth_P", auth, data->sess.prf_len);
|
|
|
|
if (eap_eke_derive_msk(&data->sess, data->serverid, data->serverid_len,
|
|
data->peerid, data->peerid_len,
|
|
data->nonce_s, data->nonce_p,
|
|
data->msk, data->emsk) < 0) {
|
|
wpa_printf(MSG_INFO, "EAP-EKE: Failed to derive MSK/EMSK");
|
|
wpabuf_free(resp);
|
|
return eap_eke_build_fail(data, ret, reqData,
|
|
EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR);
|
|
}
|
|
|
|
os_memset(data->dh_priv, 0, sizeof(data->dh_priv));
|
|
eap_eke_session_clean(&data->sess);
|
|
|
|
eap_eke_state(data, SUCCESS);
|
|
ret->methodState = METHOD_MAY_CONT;
|
|
ret->decision = DECISION_COND_SUCC;
|
|
ret->allowNotifications = FALSE;
|
|
|
|
return resp;
|
|
}
|
|
|
|
|
|
static struct wpabuf * eap_eke_process_failure(struct eap_eke_data *data,
|
|
struct eap_method_ret *ret,
|
|
const struct wpabuf *reqData,
|
|
const u8 *payload,
|
|
size_t payload_len)
|
|
{
|
|
wpa_printf(MSG_DEBUG, "EAP-EKE: Received EAP-EKE-Failure/Request");
|
|
|
|
if (payload_len < 4) {
|
|
wpa_printf(MSG_DEBUG, "EAP-EKE: Too short EAP-EKE-Failure");
|
|
} else {
|
|
u32 code;
|
|
code = WPA_GET_BE32(payload);
|
|
wpa_printf(MSG_INFO, "EAP-EKE: Failure-Code 0x%x", code);
|
|
}
|
|
|
|
return eap_eke_build_fail(data, ret, reqData, EAP_EKE_FAIL_NO_ERROR);
|
|
}
|
|
|
|
|
|
static struct wpabuf * eap_eke_process(struct eap_sm *sm, void *priv,
|
|
struct eap_method_ret *ret,
|
|
const struct wpabuf *reqData)
|
|
{
|
|
struct eap_eke_data *data = priv;
|
|
struct wpabuf *resp;
|
|
const u8 *pos, *end;
|
|
size_t len;
|
|
u8 eke_exch;
|
|
|
|
pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_EKE, reqData, &len);
|
|
if (pos == NULL || len < 1) {
|
|
ret->ignore = TRUE;
|
|
return NULL;
|
|
}
|
|
|
|
end = pos + len;
|
|
eke_exch = *pos++;
|
|
|
|
wpa_printf(MSG_DEBUG, "EAP-EKE: Received frame: exch %d", eke_exch);
|
|
wpa_hexdump(MSG_DEBUG, "EAP-EKE: Received Data", pos, end - pos);
|
|
|
|
ret->ignore = FALSE;
|
|
ret->methodState = METHOD_MAY_CONT;
|
|
ret->decision = DECISION_FAIL;
|
|
ret->allowNotifications = TRUE;
|
|
|
|
switch (eke_exch) {
|
|
case EAP_EKE_ID:
|
|
resp = eap_eke_process_id(data, ret, reqData, pos, end - pos);
|
|
break;
|
|
case EAP_EKE_COMMIT:
|
|
resp = eap_eke_process_commit(sm, data, ret, reqData,
|
|
pos, end - pos);
|
|
break;
|
|
case EAP_EKE_CONFIRM:
|
|
resp = eap_eke_process_confirm(data, ret, reqData,
|
|
pos, end - pos);
|
|
break;
|
|
case EAP_EKE_FAILURE:
|
|
resp = eap_eke_process_failure(data, ret, reqData,
|
|
pos, end - pos);
|
|
break;
|
|
default:
|
|
wpa_printf(MSG_DEBUG, "EAP-EKE: Ignoring message with unknown EKE-Exch %d", eke_exch);
|
|
ret->ignore = TRUE;
|
|
return NULL;
|
|
}
|
|
|
|
if (ret->methodState == METHOD_DONE)
|
|
ret->allowNotifications = FALSE;
|
|
|
|
return resp;
|
|
}
|
|
|
|
|
|
static Boolean eap_eke_isKeyAvailable(struct eap_sm *sm, void *priv)
|
|
{
|
|
struct eap_eke_data *data = priv;
|
|
return data->state == SUCCESS;
|
|
}
|
|
|
|
|
|
static u8 * eap_eke_getKey(struct eap_sm *sm, void *priv, size_t *len)
|
|
{
|
|
struct eap_eke_data *data = priv;
|
|
u8 *key;
|
|
|
|
if (data->state != SUCCESS)
|
|
return NULL;
|
|
|
|
key = os_malloc(EAP_MSK_LEN);
|
|
if (key == NULL)
|
|
return NULL;
|
|
os_memcpy(key, data->msk, EAP_MSK_LEN);
|
|
*len = EAP_MSK_LEN;
|
|
|
|
return key;
|
|
}
|
|
|
|
|
|
static u8 * eap_eke_get_emsk(struct eap_sm *sm, void *priv, size_t *len)
|
|
{
|
|
struct eap_eke_data *data = priv;
|
|
u8 *key;
|
|
|
|
if (data->state != SUCCESS)
|
|
return NULL;
|
|
|
|
key = os_malloc(EAP_EMSK_LEN);
|
|
if (key == NULL)
|
|
return NULL;
|
|
os_memcpy(key, data->emsk, EAP_EMSK_LEN);
|
|
*len = EAP_EMSK_LEN;
|
|
|
|
return key;
|
|
}
|
|
|
|
|
|
int eap_peer_eke_register(void)
|
|
{
|
|
struct eap_method *eap;
|
|
int ret;
|
|
|
|
eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION,
|
|
EAP_VENDOR_IETF, EAP_TYPE_EKE, "EKE");
|
|
if (eap == NULL)
|
|
return -1;
|
|
|
|
eap->init = eap_eke_init;
|
|
eap->deinit = eap_eke_deinit;
|
|
eap->process = eap_eke_process;
|
|
eap->isKeyAvailable = eap_eke_isKeyAvailable;
|
|
eap->getKey = eap_eke_getKey;
|
|
eap->get_emsk = eap_eke_get_emsk;
|
|
|
|
ret = eap_peer_method_register(eap);
|
|
if (ret)
|
|
eap_peer_method_free(eap);
|
|
return ret;
|
|
}
|