0a13e06bdb
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>
793 lines
21 KiB
C
793 lines
21 KiB
C
/*
|
|
* hostapd / EAP-EKE (RFC 6124) server
|
|
* 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_server/eap_i.h"
|
|
#include "eap_common/eap_eke_common.h"
|
|
|
|
|
|
struct eap_eke_data {
|
|
enum {
|
|
IDENTITY, COMMIT, CONFIRM, FAILURE_REPORT, SUCCESS, FAILURE
|
|
} state;
|
|
u8 msk[EAP_MSK_LEN];
|
|
u8 emsk[EAP_EMSK_LEN];
|
|
u8 *peerid;
|
|
size_t peerid_len;
|
|
u8 peerid_type;
|
|
u8 serverid_type;
|
|
u8 dh_priv[EAP_EKE_MAX_DH_LEN];
|
|
u8 key[EAP_EKE_MAX_KEY_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;
|
|
int phase2;
|
|
u32 failure_code;
|
|
};
|
|
|
|
|
|
static const char * eap_eke_state_txt(int state)
|
|
{
|
|
switch (state) {
|
|
case IDENTITY:
|
|
return "IDENTITY";
|
|
case COMMIT:
|
|
return "COMMIT";
|
|
case CONFIRM:
|
|
return "CONFIRM";
|
|
case FAILURE_REPORT:
|
|
return "FAILURE_REPORT";
|
|
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_fail(struct eap_eke_data *data, u32 code)
|
|
{
|
|
wpa_printf(MSG_DEBUG, "EAP-EKE: Failure - code 0x%x", code);
|
|
data->failure_code = code;
|
|
eap_eke_state(data, FAILURE_REPORT);
|
|
}
|
|
|
|
|
|
static void * eap_eke_init(struct eap_sm *sm)
|
|
{
|
|
struct eap_eke_data *data;
|
|
size_t i;
|
|
|
|
data = os_zalloc(sizeof(*data));
|
|
if (data == NULL)
|
|
return NULL;
|
|
eap_eke_state(data, IDENTITY);
|
|
|
|
data->serverid_type = EAP_EKE_ID_OPAQUE;
|
|
for (i = 0; i < sm->server_id_len; i++) {
|
|
if (sm->server_id[i] == '.' &&
|
|
data->serverid_type == EAP_EKE_ID_OPAQUE)
|
|
data->serverid_type = EAP_EKE_ID_FQDN;
|
|
if (sm->server_id[i] == '@')
|
|
data->serverid_type = EAP_EKE_ID_NAI;
|
|
}
|
|
|
|
data->phase2 = sm->init_phase2;
|
|
|
|
return data;
|
|
}
|
|
|
|
|
|
static void eap_eke_reset(struct eap_sm *sm, void *priv)
|
|
{
|
|
struct eap_eke_data *data = priv;
|
|
eap_eke_session_clean(&data->sess);
|
|
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,
|
|
u8 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_REQUEST, 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 supported_proposal(const u8 *pos)
|
|
{
|
|
if (pos[0] == EAP_EKE_DHGROUP_EKE_16 &&
|
|
pos[1] == EAP_EKE_ENCR_AES128_CBC &&
|
|
pos[2] == EAP_EKE_PRF_HMAC_SHA2_256 &&
|
|
pos[3] == EAP_EKE_MAC_HMAC_SHA2_256)
|
|
return 1;
|
|
|
|
if (pos[0] == EAP_EKE_DHGROUP_EKE_15 &&
|
|
pos[1] == EAP_EKE_ENCR_AES128_CBC &&
|
|
pos[2] == EAP_EKE_PRF_HMAC_SHA2_256 &&
|
|
pos[3] == EAP_EKE_MAC_HMAC_SHA2_256)
|
|
return 1;
|
|
|
|
if (pos[0] == EAP_EKE_DHGROUP_EKE_14 &&
|
|
pos[1] == EAP_EKE_ENCR_AES128_CBC &&
|
|
pos[2] == EAP_EKE_PRF_HMAC_SHA2_256 &&
|
|
pos[3] == EAP_EKE_MAC_HMAC_SHA2_256)
|
|
return 1;
|
|
|
|
if (pos[0] == EAP_EKE_DHGROUP_EKE_14 &&
|
|
pos[1] == EAP_EKE_ENCR_AES128_CBC &&
|
|
pos[2] == EAP_EKE_PRF_HMAC_SHA1 &&
|
|
pos[3] == EAP_EKE_MAC_HMAC_SHA1)
|
|
return 1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
static struct wpabuf * eap_eke_build_failure(struct eap_eke_data *data, u8 id)
|
|
{
|
|
struct wpabuf *msg;
|
|
|
|
wpa_printf(MSG_DEBUG, "EAP-EKE: Request/Failure: Failure-Code=0x%x",
|
|
data->failure_code);
|
|
|
|
msg = eap_eke_build_msg(data, id, 4, EAP_EKE_FAILURE);
|
|
if (msg == NULL) {
|
|
eap_eke_state(data, FAILURE);
|
|
return NULL;
|
|
}
|
|
wpabuf_put_be32(msg, data->failure_code);
|
|
|
|
return msg;
|
|
}
|
|
|
|
|
|
static struct wpabuf * eap_eke_build_identity(struct eap_sm *sm,
|
|
struct eap_eke_data *data,
|
|
u8 id)
|
|
{
|
|
struct wpabuf *msg;
|
|
size_t plen;
|
|
|
|
wpa_printf(MSG_DEBUG, "EAP-EKE: Request/Identity");
|
|
|
|
plen = 2 + 4 * 4 + 1 + sm->server_id_len;
|
|
msg = eap_eke_build_msg(data, id, plen, EAP_EKE_ID);
|
|
if (msg == NULL)
|
|
return NULL;
|
|
|
|
wpabuf_put_u8(msg, 4); /* NumProposals */
|
|
wpabuf_put_u8(msg, 0); /* Reserved */
|
|
|
|
/* Proposal - DH Group 16 with AES128-CBC and SHA256 */
|
|
wpabuf_put_u8(msg, EAP_EKE_DHGROUP_EKE_16); /* Group Description */
|
|
wpabuf_put_u8(msg, EAP_EKE_ENCR_AES128_CBC); /* Encryption */
|
|
wpabuf_put_u8(msg, EAP_EKE_PRF_HMAC_SHA2_256); /* PRF */
|
|
wpabuf_put_u8(msg, EAP_EKE_MAC_HMAC_SHA2_256); /* MAC */
|
|
|
|
/* Proposal - DH Group 15 with AES128-CBC and SHA256 */
|
|
wpabuf_put_u8(msg, EAP_EKE_DHGROUP_EKE_15); /* Group Description */
|
|
wpabuf_put_u8(msg, EAP_EKE_ENCR_AES128_CBC); /* Encryption */
|
|
wpabuf_put_u8(msg, EAP_EKE_PRF_HMAC_SHA2_256); /* PRF */
|
|
wpabuf_put_u8(msg, EAP_EKE_MAC_HMAC_SHA2_256); /* MAC */
|
|
|
|
/* Proposal - DH Group 14 with AES128-CBC and SHA256 */
|
|
wpabuf_put_u8(msg, EAP_EKE_DHGROUP_EKE_14); /* Group Description */
|
|
wpabuf_put_u8(msg, EAP_EKE_ENCR_AES128_CBC); /* Encryption */
|
|
wpabuf_put_u8(msg, EAP_EKE_PRF_HMAC_SHA2_256); /* PRF */
|
|
wpabuf_put_u8(msg, EAP_EKE_MAC_HMAC_SHA2_256); /* MAC */
|
|
|
|
/*
|
|
* Proposal - DH Group 14 with AES128-CBC and SHA1
|
|
* (mandatory to implement algorithms)
|
|
*/
|
|
wpabuf_put_u8(msg, EAP_EKE_DHGROUP_EKE_14); /* Group Description */
|
|
wpabuf_put_u8(msg, EAP_EKE_ENCR_AES128_CBC); /* Encryption */
|
|
wpabuf_put_u8(msg, EAP_EKE_PRF_HMAC_SHA1); /* PRF */
|
|
wpabuf_put_u8(msg, EAP_EKE_MAC_HMAC_SHA1); /* MAC */
|
|
|
|
/* Server IDType + Identity */
|
|
wpabuf_put_u8(msg, data->serverid_type);
|
|
wpabuf_put_data(msg, sm->server_id, sm->server_id_len);
|
|
|
|
wpabuf_free(data->msgs);
|
|
data->msgs = wpabuf_dup(msg);
|
|
if (data->msgs == NULL) {
|
|
wpabuf_free(msg);
|
|
return NULL;
|
|
}
|
|
|
|
return msg;
|
|
}
|
|
|
|
|
|
static struct wpabuf * eap_eke_build_commit(struct eap_sm *sm,
|
|
struct eap_eke_data *data, u8 id)
|
|
{
|
|
struct wpabuf *msg;
|
|
u8 pub[EAP_EKE_MAX_DH_LEN];
|
|
|
|
wpa_printf(MSG_DEBUG, "EAP-EKE: Request/Commit");
|
|
|
|
if (sm->user == NULL || sm->user->password == NULL) {
|
|
wpa_printf(MSG_INFO, "EAP-EKE: Password with not configured");
|
|
eap_eke_fail(data, EAP_EKE_FAIL_PASSWD_NOT_FOUND);
|
|
return eap_eke_build_failure(data, id);
|
|
}
|
|
|
|
if (eap_eke_derive_key(&data->sess, sm->user->password,
|
|
sm->user->password_len,
|
|
sm->server_id, sm->server_id_len,
|
|
data->peerid, data->peerid_len, data->key) < 0) {
|
|
wpa_printf(MSG_INFO, "EAP-EKE: Failed to derive key");
|
|
eap_eke_fail(data, EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR);
|
|
return eap_eke_build_failure(data, id);
|
|
}
|
|
|
|
msg = eap_eke_build_msg(data, id, data->sess.dhcomp_len,
|
|
EAP_EKE_COMMIT);
|
|
if (msg == NULL) {
|
|
eap_eke_fail(data, EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR);
|
|
return eap_eke_build_failure(data, id);
|
|
}
|
|
|
|
/*
|
|
* y_s = g ^ x_s (mod p)
|
|
* x_s = random number 2 .. p-1
|
|
* temp = prf(0+, password)
|
|
* key = prf+(temp, ID_S | ID_P)
|
|
* DHComponent_S = Encr(key, y_s)
|
|
*/
|
|
|
|
if (eap_eke_dh_init(data->sess.dhgroup, data->dh_priv, pub) < 0) {
|
|
wpa_printf(MSG_INFO, "EAP-EKE: Failed to initialize DH");
|
|
eap_eke_fail(data, EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR);
|
|
return eap_eke_build_failure(data, id);
|
|
}
|
|
|
|
if (eap_eke_dhcomp(&data->sess, data->key, pub,
|
|
wpabuf_put(msg, data->sess.dhcomp_len))
|
|
< 0) {
|
|
wpa_printf(MSG_INFO, "EAP-EKE: Failed to build DHComponent_S");
|
|
wpabuf_free(msg);
|
|
eap_eke_fail(data, EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR);
|
|
return eap_eke_build_failure(data, id);
|
|
}
|
|
|
|
if (wpabuf_resize(&data->msgs, wpabuf_len(msg)) < 0) {
|
|
wpabuf_free(msg);
|
|
eap_eke_fail(data, EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR);
|
|
return eap_eke_build_failure(data, id);
|
|
}
|
|
wpabuf_put_buf(data->msgs, msg);
|
|
|
|
return msg;
|
|
}
|
|
|
|
|
|
static struct wpabuf * eap_eke_build_confirm(struct eap_sm *sm,
|
|
struct eap_eke_data *data, u8 id)
|
|
{
|
|
struct wpabuf *msg;
|
|
size_t plen, prot_len;
|
|
u8 nonces[2 * EAP_EKE_MAX_NONCE_LEN];
|
|
u8 *auth;
|
|
|
|
wpa_printf(MSG_DEBUG, "EAP-EKE: Request/Confirm");
|
|
|
|
plen = data->sess.pnonce_ps_len + data->sess.prf_len;
|
|
msg = eap_eke_build_msg(data, id, plen, EAP_EKE_CONFIRM);
|
|
if (msg == NULL) {
|
|
eap_eke_fail(data, EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR);
|
|
return eap_eke_build_failure(data, id);
|
|
}
|
|
|
|
if (random_get_bytes(data->nonce_s, data->sess.nonce_len)) {
|
|
wpabuf_free(msg);
|
|
eap_eke_fail(data, EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR);
|
|
return eap_eke_build_failure(data, id);
|
|
}
|
|
wpa_hexdump_key(MSG_DEBUG, "EAP-EKE: Nonce_S",
|
|
data->nonce_s, data->sess.nonce_len);
|
|
|
|
os_memcpy(nonces, data->nonce_p, data->sess.nonce_len);
|
|
os_memcpy(nonces + data->sess.nonce_len, data->nonce_s,
|
|
data->sess.nonce_len);
|
|
prot_len = wpabuf_tailroom(msg);
|
|
if (eap_eke_prot(&data->sess, nonces, 2 * data->sess.nonce_len,
|
|
wpabuf_put(msg, 0), &prot_len) < 0) {
|
|
wpabuf_free(msg);
|
|
eap_eke_fail(data, EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR);
|
|
return eap_eke_build_failure(data, id);
|
|
}
|
|
wpabuf_put(msg, prot_len);
|
|
|
|
if (eap_eke_derive_ka(&data->sess,
|
|
sm->server_id, sm->server_id_len,
|
|
data->peerid, data->peerid_len,
|
|
data->nonce_p, data->nonce_s) < 0) {
|
|
wpabuf_free(msg);
|
|
eap_eke_fail(data, EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR);
|
|
return eap_eke_build_failure(data, id);
|
|
}
|
|
|
|
auth = wpabuf_put(msg, data->sess.prf_len);
|
|
if (eap_eke_auth(&data->sess, "EAP-EKE server", data->msgs, auth) < 0) {
|
|
wpabuf_free(msg);
|
|
eap_eke_fail(data, EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR);
|
|
return eap_eke_build_failure(data, id);
|
|
}
|
|
wpa_hexdump(MSG_DEBUG, "EAP-EKE: Auth_S", auth, data->sess.prf_len);
|
|
|
|
return msg;
|
|
}
|
|
|
|
|
|
static struct wpabuf * eap_eke_buildReq(struct eap_sm *sm, void *priv, u8 id)
|
|
{
|
|
struct eap_eke_data *data = priv;
|
|
|
|
switch (data->state) {
|
|
case IDENTITY:
|
|
return eap_eke_build_identity(sm, data, id);
|
|
case COMMIT:
|
|
return eap_eke_build_commit(sm, data, id);
|
|
case CONFIRM:
|
|
return eap_eke_build_confirm(sm, data, id);
|
|
case FAILURE_REPORT:
|
|
return eap_eke_build_failure(data, id);
|
|
default:
|
|
wpa_printf(MSG_DEBUG, "EAP-EKE: Unknown state %d in buildReq",
|
|
data->state);
|
|
break;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
|
|
static Boolean eap_eke_check(struct eap_sm *sm, void *priv,
|
|
struct wpabuf *respData)
|
|
{
|
|
struct eap_eke_data *data = priv;
|
|
size_t len;
|
|
const u8 *pos;
|
|
u8 eke_exch;
|
|
|
|
pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_EKE, respData, &len);
|
|
if (pos == NULL || len < 1) {
|
|
wpa_printf(MSG_INFO, "EAP-EKE: Invalid frame");
|
|
return TRUE;
|
|
}
|
|
|
|
eke_exch = *pos;
|
|
wpa_printf(MSG_DEBUG, "EAP-EKE: Received frame: EKE-Exch=%d", eke_exch);
|
|
|
|
if (data->state == IDENTITY && eke_exch == EAP_EKE_ID)
|
|
return FALSE;
|
|
|
|
if (data->state == COMMIT && eke_exch == EAP_EKE_COMMIT)
|
|
return FALSE;
|
|
|
|
if (data->state == CONFIRM && eke_exch == EAP_EKE_CONFIRM)
|
|
return FALSE;
|
|
|
|
if (eke_exch == EAP_EKE_FAILURE)
|
|
return FALSE;
|
|
|
|
wpa_printf(MSG_INFO, "EAP-EKE: Unexpected EKE-Exch=%d in state=%d",
|
|
eke_exch, data->state);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
static void eap_eke_process_identity(struct eap_sm *sm,
|
|
struct eap_eke_data *data,
|
|
const struct wpabuf *respData,
|
|
const u8 *payload, size_t payloadlen)
|
|
{
|
|
const u8 *pos, *end;
|
|
int i;
|
|
|
|
wpa_printf(MSG_DEBUG, "EAP-EKE: Received Response/Identity");
|
|
|
|
if (data->state != IDENTITY) {
|
|
eap_eke_fail(data, EAP_EKE_FAIL_PROTO_ERROR);
|
|
return;
|
|
}
|
|
|
|
pos = payload;
|
|
end = payload + payloadlen;
|
|
|
|
if (pos + 2 + 4 + 1 > end) {
|
|
wpa_printf(MSG_INFO, "EAP-EKE: Too short EAP-EKE-ID payload");
|
|
eap_eke_fail(data, EAP_EKE_FAIL_PROTO_ERROR);
|
|
return;
|
|
}
|
|
|
|
if (*pos != 1) {
|
|
wpa_printf(MSG_INFO, "EAP-EKE: Unexpected NumProposals %d (expected 1)",
|
|
*pos);
|
|
eap_eke_fail(data, EAP_EKE_FAIL_PROTO_ERROR);
|
|
return;
|
|
}
|
|
|
|
pos += 2;
|
|
|
|
if (!supported_proposal(pos)) {
|
|
wpa_printf(MSG_INFO, "EAP-EKE: Unexpected Proposal (%u:%u:%u:%u)",
|
|
pos[0], pos[1], pos[2], pos[3]);
|
|
eap_eke_fail(data, EAP_EKE_FAIL_PROTO_ERROR);
|
|
return;
|
|
}
|
|
|
|
wpa_printf(MSG_DEBUG, "EAP-EKE: Selected Proposal (%u:%u:%u:%u)",
|
|
pos[0], pos[1], pos[2], pos[3]);
|
|
if (eap_eke_session_init(&data->sess, pos[0], pos[1], pos[2], pos[3]) <
|
|
0) {
|
|
eap_eke_fail(data, EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR);
|
|
return;
|
|
}
|
|
pos += 4;
|
|
|
|
data->peerid_type = *pos++;
|
|
os_free(data->peerid);
|
|
data->peerid = os_malloc(end - pos);
|
|
if (data->peerid == NULL) {
|
|
wpa_printf(MSG_INFO, "EAP-EKE: Failed to allocate memory for peerid");
|
|
eap_eke_fail(data, EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR);
|
|
return;
|
|
}
|
|
os_memcpy(data->peerid, pos, end - pos);
|
|
data->peerid_len = end - pos;
|
|
wpa_printf(MSG_DEBUG, "EAP-EKE: Peer IDType %u", data->peerid_type);
|
|
wpa_hexdump_ascii(MSG_DEBUG, "EAP-EKE: Peer Identity",
|
|
data->peerid, data->peerid_len);
|
|
|
|
if (eap_user_get(sm, data->peerid, data->peerid_len, data->phase2)) {
|
|
wpa_printf(MSG_INFO, "EAP-EKE: Peer Identity not found from user database");
|
|
eap_eke_fail(data, EAP_EKE_FAIL_PASSWD_NOT_FOUND);
|
|
return;
|
|
}
|
|
|
|
for (i = 0; i < EAP_MAX_METHODS; i++) {
|
|
if (sm->user->methods[i].vendor == EAP_VENDOR_IETF &&
|
|
sm->user->methods[i].method == EAP_TYPE_EKE)
|
|
break;
|
|
}
|
|
if (i == EAP_MAX_METHODS) {
|
|
wpa_printf(MSG_INFO, "EAP-EKE: Matching user entry does not allow EAP-EKE");
|
|
eap_eke_fail(data, EAP_EKE_FAIL_PASSWD_NOT_FOUND);
|
|
return;
|
|
}
|
|
|
|
if (sm->user->password == NULL || sm->user->password_len == 0) {
|
|
wpa_printf(MSG_INFO, "EAP-EKE: No password configured for peer");
|
|
eap_eke_fail(data, EAP_EKE_FAIL_PASSWD_NOT_FOUND);
|
|
return;
|
|
}
|
|
|
|
if (wpabuf_resize(&data->msgs, wpabuf_len(respData)) < 0) {
|
|
eap_eke_fail(data, EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR);
|
|
return;
|
|
}
|
|
wpabuf_put_buf(data->msgs, respData);
|
|
|
|
eap_eke_state(data, COMMIT);
|
|
}
|
|
|
|
|
|
static void eap_eke_process_commit(struct eap_sm *sm,
|
|
struct eap_eke_data *data,
|
|
const struct wpabuf *respData,
|
|
const u8 *payload, size_t payloadlen)
|
|
{
|
|
const u8 *pos, *end, *dhcomp, *pnonce;
|
|
size_t decrypt_len;
|
|
|
|
wpa_printf(MSG_DEBUG, "EAP-EKE: Received Response/Commit");
|
|
|
|
if (data->state != COMMIT) {
|
|
eap_eke_fail(data, EAP_EKE_FAIL_PROTO_ERROR);
|
|
return;
|
|
}
|
|
|
|
pos = payload;
|
|
end = payload + payloadlen;
|
|
|
|
if (pos + data->sess.dhcomp_len + data->sess.pnonce_len > end) {
|
|
wpa_printf(MSG_DEBUG, "EAP-EKE: Too short EAP-EKE-Commit");
|
|
eap_eke_fail(data, EAP_EKE_FAIL_PROTO_ERROR);
|
|
return;
|
|
}
|
|
|
|
wpa_hexdump(MSG_DEBUG, "EAP-EKE: DHComponent_P",
|
|
pos, data->sess.dhcomp_len);
|
|
dhcomp = pos;
|
|
pos += data->sess.dhcomp_len;
|
|
wpa_hexdump(MSG_DEBUG, "EAP-EKE: PNonce_P", pos, data->sess.pnonce_len);
|
|
pnonce = pos;
|
|
pos += data->sess.pnonce_len;
|
|
wpa_hexdump(MSG_DEBUG, "EAP-EKE: CBValue", pos, end - pos);
|
|
|
|
if (eap_eke_shared_secret(&data->sess, data->key, data->dh_priv, dhcomp)
|
|
< 0) {
|
|
wpa_printf(MSG_INFO, "EAP-EKE: Failed to derive shared secret");
|
|
eap_eke_fail(data, EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR);
|
|
return;
|
|
}
|
|
|
|
if (eap_eke_derive_ke_ki(&data->sess,
|
|
sm->server_id, sm->server_id_len,
|
|
data->peerid, data->peerid_len) < 0) {
|
|
wpa_printf(MSG_INFO, "EAP-EKE: Failed to derive Ke/Ki");
|
|
eap_eke_fail(data, EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR);
|
|
return;
|
|
}
|
|
|
|
decrypt_len = sizeof(data->nonce_p);
|
|
if (eap_eke_decrypt_prot(&data->sess, pnonce, data->sess.pnonce_len,
|
|
data->nonce_p, &decrypt_len) < 0) {
|
|
wpa_printf(MSG_INFO, "EAP-EKE: Failed to decrypt PNonce_P");
|
|
eap_eke_fail(data, EAP_EKE_FAIL_AUTHENTICATION_FAIL);
|
|
return;
|
|
}
|
|
if (decrypt_len < (size_t) data->sess.nonce_len) {
|
|
wpa_printf(MSG_INFO, "EAP-EKE: PNonce_P protected data too short to include Nonce_P");
|
|
eap_eke_fail(data, EAP_EKE_FAIL_AUTHENTICATION_FAIL);
|
|
return;
|
|
}
|
|
wpa_hexdump_key(MSG_DEBUG, "EAP-EKE: Nonce_P",
|
|
data->nonce_p, data->sess.nonce_len);
|
|
|
|
if (wpabuf_resize(&data->msgs, wpabuf_len(respData)) < 0) {
|
|
eap_eke_fail(data, EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR);
|
|
return;
|
|
}
|
|
wpabuf_put_buf(data->msgs, respData);
|
|
|
|
eap_eke_state(data, CONFIRM);
|
|
}
|
|
|
|
|
|
static void eap_eke_process_confirm(struct eap_sm *sm,
|
|
struct eap_eke_data *data,
|
|
const struct wpabuf *respData,
|
|
const u8 *payload, size_t payloadlen)
|
|
{
|
|
size_t decrypt_len;
|
|
u8 nonce[EAP_EKE_MAX_NONCE_LEN];
|
|
u8 auth_p[EAP_EKE_MAX_HASH_LEN];
|
|
|
|
wpa_printf(MSG_DEBUG, "EAP-EKE: Received Response/Confirm");
|
|
|
|
if (data->state != CONFIRM) {
|
|
eap_eke_fail(data, EAP_EKE_FAIL_PROTO_ERROR);
|
|
return;
|
|
}
|
|
|
|
wpa_printf(MSG_DEBUG, "EAP-EKE: Received Response/Confirm");
|
|
|
|
if (payloadlen < (size_t) data->sess.pnonce_len + data->sess.prf_len) {
|
|
wpa_printf(MSG_DEBUG, "EAP-EKE: Too short EAP-EKE-Confirm");
|
|
eap_eke_fail(data, EAP_EKE_FAIL_PROTO_ERROR);
|
|
return;
|
|
}
|
|
|
|
decrypt_len = sizeof(nonce);
|
|
if (eap_eke_decrypt_prot(&data->sess, payload, data->sess.pnonce_len,
|
|
nonce, &decrypt_len) < 0) {
|
|
wpa_printf(MSG_INFO, "EAP-EKE: Failed to decrypt PNonce_S");
|
|
eap_eke_fail(data, EAP_EKE_FAIL_AUTHENTICATION_FAIL);
|
|
return;
|
|
}
|
|
if (decrypt_len < (size_t) data->sess.nonce_len) {
|
|
wpa_printf(MSG_INFO, "EAP-EKE: PNonce_S protected data too short to include Nonce_S");
|
|
eap_eke_fail(data, EAP_EKE_FAIL_AUTHENTICATION_FAIL);
|
|
return;
|
|
}
|
|
wpa_hexdump_key(MSG_DEBUG, "EAP-EKE: Received Nonce_S",
|
|
nonce, data->sess.nonce_len);
|
|
if (os_memcmp(nonce, data->nonce_s, data->sess.nonce_len) != 0) {
|
|
wpa_printf(MSG_INFO, "EAP-EKE: Received Nonce_S does not match previously sent Nonce_S");
|
|
eap_eke_fail(data, EAP_EKE_FAIL_AUTHENTICATION_FAIL);
|
|
return;
|
|
}
|
|
|
|
if (eap_eke_auth(&data->sess, "EAP-EKE peer", data->msgs, auth_p) < 0) {
|
|
wpa_printf(MSG_INFO, "EAP-EKE: Could not derive Auth_P");
|
|
eap_eke_fail(data, EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR);
|
|
return;
|
|
}
|
|
wpa_hexdump(MSG_DEBUG, "EAP-EKE: Auth_P", auth_p, data->sess.prf_len);
|
|
if (os_memcmp_const(auth_p, payload + data->sess.pnonce_len,
|
|
data->sess.prf_len) != 0) {
|
|
wpa_printf(MSG_INFO, "EAP-EKE: Auth_P does not match");
|
|
eap_eke_fail(data, EAP_EKE_FAIL_AUTHENTICATION_FAIL);
|
|
return;
|
|
}
|
|
|
|
if (eap_eke_derive_msk(&data->sess, sm->server_id, sm->server_id_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");
|
|
eap_eke_fail(data, EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR);
|
|
return;
|
|
}
|
|
|
|
os_memset(data->dh_priv, 0, sizeof(data->dh_priv));
|
|
os_memset(data->key, 0, sizeof(data->key));
|
|
eap_eke_session_clean(&data->sess);
|
|
|
|
eap_eke_state(data, SUCCESS);
|
|
}
|
|
|
|
|
|
static void eap_eke_process_failure(struct eap_sm *sm,
|
|
struct eap_eke_data *data,
|
|
const struct wpabuf *respData,
|
|
const u8 *payload, size_t payloadlen)
|
|
{
|
|
u32 code;
|
|
|
|
wpa_printf(MSG_DEBUG, "EAP-EKE: Received Response/Failure");
|
|
|
|
if (payloadlen < 4) {
|
|
wpa_printf(MSG_DEBUG, "EAP-EKE: Too short EAP-EKE-Failure");
|
|
eap_eke_state(data, FAILURE);
|
|
return;
|
|
}
|
|
|
|
code = WPA_GET_BE32(payload);
|
|
wpa_printf(MSG_DEBUG, "EAP-EKE: Peer reported failure code 0x%x", code);
|
|
|
|
eap_eke_state(data, FAILURE);
|
|
}
|
|
|
|
|
|
static void eap_eke_process(struct eap_sm *sm, void *priv,
|
|
struct wpabuf *respData)
|
|
{
|
|
struct eap_eke_data *data = priv;
|
|
u8 eke_exch;
|
|
size_t len;
|
|
const u8 *pos, *end;
|
|
|
|
pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_EKE, respData, &len);
|
|
if (pos == NULL || len < 1)
|
|
return;
|
|
|
|
eke_exch = *pos;
|
|
end = pos + len;
|
|
pos++;
|
|
|
|
wpa_hexdump(MSG_DEBUG, "EAP-EKE: Received payload", pos, end - pos);
|
|
|
|
switch (eke_exch) {
|
|
case EAP_EKE_ID:
|
|
eap_eke_process_identity(sm, data, respData, pos, end - pos);
|
|
break;
|
|
case EAP_EKE_COMMIT:
|
|
eap_eke_process_commit(sm, data, respData, pos, end - pos);
|
|
break;
|
|
case EAP_EKE_CONFIRM:
|
|
eap_eke_process_confirm(sm, data, respData, pos, end - pos);
|
|
break;
|
|
case EAP_EKE_FAILURE:
|
|
eap_eke_process_failure(sm, data, respData, pos, end - pos);
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
static Boolean eap_eke_isDone(struct eap_sm *sm, void *priv)
|
|
{
|
|
struct eap_eke_data *data = priv;
|
|
return data->state == SUCCESS || data->state == FAILURE;
|
|
}
|
|
|
|
|
|
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;
|
|
}
|
|
|
|
|
|
static Boolean eap_eke_isSuccess(struct eap_sm *sm, void *priv)
|
|
{
|
|
struct eap_eke_data *data = priv;
|
|
return data->state == SUCCESS;
|
|
}
|
|
|
|
|
|
int eap_server_eke_register(void)
|
|
{
|
|
struct eap_method *eap;
|
|
int ret;
|
|
|
|
eap = eap_server_method_alloc(EAP_SERVER_METHOD_INTERFACE_VERSION,
|
|
EAP_VENDOR_IETF, EAP_TYPE_EKE, "EKE");
|
|
if (eap == NULL)
|
|
return -1;
|
|
|
|
eap->init = eap_eke_init;
|
|
eap->reset = eap_eke_reset;
|
|
eap->buildReq = eap_eke_buildReq;
|
|
eap->check = eap_eke_check;
|
|
eap->process = eap_eke_process;
|
|
eap->isDone = eap_eke_isDone;
|
|
eap->getKey = eap_eke_getKey;
|
|
eap->isSuccess = eap_eke_isSuccess;
|
|
eap->get_emsk = eap_eke_get_emsk;
|
|
|
|
ret = eap_server_method_register(eap);
|
|
if (ret)
|
|
eap_server_method_free(eap);
|
|
return ret;
|
|
}
|