ERP: Add support for ERP on EAP server and authenticator

Derive rRK and rIK on EAP server if ERP is enabled and use these keys to
allow EAP re-authentication to be used and to derive rMSK.

The new hostapd configuration parameter eap_server_erp=1 can now be used
to configure the integrated EAP server to derive EMSK, rRK, and rIK at
the successful completion of an EAP authentication method. This
functionality is not included in the default build and can be enabled
with CONFIG_ERP=y.

Signed-off-by: Jouni Malinen <j@w1.fi>
This commit is contained in:
Jouni Malinen 2014-11-29 21:28:24 +02:00
parent e2ee327b19
commit d3bddd8b84
15 changed files with 676 additions and 13 deletions

View file

@ -258,6 +258,12 @@ OBJS += ../src/l2_packet/l2_packet_none.o
endif
ifdef CONFIG_ERP
CFLAGS += -DCONFIG_ERP
NEED_SHA256=y
NEED_HMAC_SHA256_KDF=y
endif
ifdef CONFIG_EAP_MD5
CFLAGS += -DEAP_SERVER_MD5
OBJS += ../src/eap_server/eap_server_md5.o
@ -767,6 +773,9 @@ endif
ifdef NEED_TLS_PRF_SHA256
OBJS += ../src/crypto/sha256-tlsprf.o
endif
ifdef NEED_HMAC_SHA256_KDF
OBJS += ../src/crypto/sha256-kdf.o
endif
endif
ifdef NEED_DH_GROUPS

View file

@ -2052,6 +2052,8 @@ static int hostapd_config_fill(struct hostapd_config *conf,
} else if (os_strcmp(buf, "pwd_group") == 0) {
bss->pwd_group = atoi(pos);
#endif /* EAP_SERVER_PWD */
} else if (os_strcmp(buf, "eap_server_erp") == 0) {
bss->eap_server_erp = atoi(pos);
#endif /* EAP_SERVER */
} else if (os_strcmp(buf, "eap_message") == 0) {
char *term;

View file

@ -56,6 +56,9 @@ CONFIG_IEEE80211W=y
# Integrated EAP server
CONFIG_EAP=y
# EAP Re-authentication Protocol (ERP) in integrated EAP server
CONFIG_ERP=y
# EAP-MD5 for the integrated EAP server
CONFIG_EAP_MD5=y

View file

@ -703,7 +703,8 @@ eapol_key_index_workaround=0
#erp_send_reauth_start=1
#
# Domain name for EAP-Initiate/Re-auth-Start. Omitted from the message if not
# set (no local ER server).
# set (no local ER server). This is also used by the integrated EAP server if
# ERP is enabled (eap_server_erp=1).
#erp_domain=example.com
##### Integrated EAP server ###################################################
@ -851,6 +852,10 @@ eap_server=0
# EAP method is enabled, the peer will be allowed to connect without TNC.
#tnc=1
# EAP Re-authentication Protocol (ERP) - RFC 6696
#
# Whether to enable ERP on the EAP server.
#eap_server_erp=1
##### IEEE 802.11f - Inter-Access Point Protocol (IAPP) #######################

View file

@ -233,6 +233,7 @@ struct hostapd_bss_config {
struct hostapd_eap_user *eap_user;
char *eap_user_sqlite;
char *eap_sim_db;
int eap_server_erp; /* Whether ERP is enabled on internal EAP server */
struct hostapd_ip_addr own_ip_addr;
char *nas_identifier;
struct hostapd_radius_servers *radius;

View file

@ -124,6 +124,8 @@ static int hostapd_setup_radius_srv(struct hostapd_data *hapd)
srv.subscr_remediation_url = conf->subscr_remediation_url;
srv.subscr_remediation_method = conf->subscr_remediation_method;
#endif /* CONFIG_HS20 */
srv.erp = conf->eap_server_erp;
srv.erp_domain = conf->erp_domain;
hapd->radius_srv = radius_server_init(&srv);
if (hapd->radius_srv == NULL) {

View file

@ -1,6 +1,6 @@
/*
* hostapd / Initialization and configuration
* Copyright (c) 2002-2013, Jouni Malinen <j@w1.fi>
* Copyright (c) 2002-2014, Jouni Malinen <j@w1.fi>
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
@ -10,6 +10,7 @@
#define HOSTAPD_H
#include "common/defs.h"
#include "utils/list.h"
#include "ap_config.h"
#include "drivers/driver.h"
@ -153,6 +154,7 @@ struct hostapd_data {
void *ssl_ctx;
void *eap_sim_db_priv;
struct radius_server_data *radius_srv;
struct dl_list erp_keys; /* struct eap_server_erp_key */
int parameter_set_count;

View file

@ -296,9 +296,15 @@ static void ieee802_1x_learn_identity(struct hostapd_data *hapd,
{
const u8 *identity;
size_t identity_len;
const struct eap_hdr *hdr = (const struct eap_hdr *) eap;
if (len <= sizeof(struct eap_hdr) ||
eap[sizeof(struct eap_hdr)] != EAP_TYPE_IDENTITY)
(hdr->code == EAP_CODE_RESPONSE &&
eap[sizeof(struct eap_hdr)] != EAP_TYPE_IDENTITY) ||
(hdr->code == EAP_CODE_INITIATE &&
eap[sizeof(struct eap_hdr)] != EAP_ERP_TYPE_REAUTH) ||
(hdr->code != EAP_CODE_RESPONSE &&
hdr->code != EAP_CODE_INITIATE))
return;
identity = eap_get_identity(sm->eap, &identity_len);
@ -711,6 +717,39 @@ static void handle_eap_response(struct hostapd_data *hapd,
}
static void handle_eap_initiate(struct hostapd_data *hapd,
struct sta_info *sta, struct eap_hdr *eap,
size_t len)
{
#ifdef CONFIG_ERP
u8 type, *data;
struct eapol_state_machine *sm = sta->eapol_sm;
if (sm == NULL)
return;
if (len < sizeof(*eap) + 1) {
wpa_printf(MSG_INFO,
"handle_eap_initiate: too short response data");
return;
}
data = (u8 *) (eap + 1);
type = data[0];
hostapd_logger(hapd, sm->addr, HOSTAPD_MODULE_IEEE8021X,
HOSTAPD_LEVEL_DEBUG, "received EAP packet (code=%d "
"id=%d len=%d) from STA: EAP Initiate type %u",
eap->code, eap->identifier, be_to_host16(eap->length),
type);
wpabuf_free(sm->eap_if->eapRespData);
sm->eap_if->eapRespData = wpabuf_alloc_copy(eap, len);
sm->eapolEap = TRUE;
#endif /* CONFIG_ERP */
}
/* Process incoming EAP packet from Supplicant */
static void handle_eap(struct hostapd_data *hapd, struct sta_info *sta,
u8 *buf, size_t len)
@ -754,6 +793,13 @@ static void handle_eap(struct hostapd_data *hapd, struct sta_info *sta,
case EAP_CODE_FAILURE:
wpa_printf(MSG_DEBUG, " (failure)");
return;
case EAP_CODE_INITIATE:
wpa_printf(MSG_DEBUG, " (initiate)");
handle_eap_initiate(hapd, sta, eap, eap_len);
break;
case EAP_CODE_FINISH:
wpa_printf(MSG_DEBUG, " (finish)");
break;
default:
wpa_printf(MSG_DEBUG, " (unknown code)");
return;
@ -1986,12 +2032,43 @@ static void ieee802_1x_eapol_event(void *ctx, void *sta_ctx,
}
#ifdef CONFIG_ERP
static struct eap_server_erp_key *
ieee802_1x_erp_get_key(void *ctx, const char *keyname)
{
struct hostapd_data *hapd = ctx;
struct eap_server_erp_key *erp;
dl_list_for_each(erp, &hapd->erp_keys, struct eap_server_erp_key,
list) {
if (os_strcmp(erp->keyname_nai, keyname) == 0)
return erp;
}
return NULL;
}
static int ieee802_1x_erp_add_key(void *ctx, struct eap_server_erp_key *erp)
{
struct hostapd_data *hapd = ctx;
dl_list_add(&hapd->erp_keys, &erp->list);
return 0;
}
#endif /* CONFIG_ERP */
int ieee802_1x_init(struct hostapd_data *hapd)
{
int i;
struct eapol_auth_config conf;
struct eapol_auth_cb cb;
dl_list_init(&hapd->erp_keys);
os_memset(&conf, 0, sizeof(conf));
conf.ctx = hapd;
conf.eap_reauth_period = hapd->conf->eap_reauth_period;
@ -2005,6 +2082,7 @@ int ieee802_1x_init(struct hostapd_data *hapd)
conf.eap_req_id_text_len = hapd->conf->eap_req_id_text_len;
conf.erp_send_reauth_start = hapd->conf->erp_send_reauth_start;
conf.erp_domain = hapd->conf->erp_domain;
conf.erp = hapd->conf->eap_server_erp;
conf.pac_opaque_encr_key = hapd->conf->pac_opaque_encr_key;
conf.eap_fast_a_id = hapd->conf->eap_fast_a_id;
conf.eap_fast_a_id_len = hapd->conf->eap_fast_a_id_len;
@ -2037,6 +2115,10 @@ int ieee802_1x_init(struct hostapd_data *hapd)
cb.abort_auth = _ieee802_1x_abort_auth;
cb.tx_key = _ieee802_1x_tx_key;
cb.eapol_event = ieee802_1x_eapol_event;
#ifdef CONFIG_ERP
cb.erp_get_key = ieee802_1x_erp_get_key;
cb.erp_add_key = ieee802_1x_erp_add_key;
#endif /* CONFIG_ERP */
hapd->eapol_auth = eapol_auth_init(&conf, &cb);
if (hapd->eapol_auth == NULL)
@ -2070,6 +2152,8 @@ int ieee802_1x_init(struct hostapd_data *hapd)
void ieee802_1x_deinit(struct hostapd_data *hapd)
{
struct eap_server_erp_key *erp;
eloop_cancel_timeout(ieee802_1x_rekey, hapd, NULL);
if (hapd->driver != NULL &&
@ -2078,6 +2162,12 @@ void ieee802_1x_deinit(struct hostapd_data *hapd)
eapol_auth_deinit(hapd->eapol_auth);
hapd->eapol_auth = NULL;
while ((erp = dl_list_first(&hapd->erp_keys, struct eap_server_erp_key,
list)) != NULL) {
dl_list_del(&erp->list);
bin_clear_free(erp, sizeof(*erp));
}
}

View file

@ -1,6 +1,6 @@
/*
* hostapd / EAP Full Authenticator state machine (RFC 4137)
* Copyright (c) 2004-2007, Jouni Malinen <j@w1.fi>
* Copyright (c) 2004-2014, Jouni Malinen <j@w1.fi>
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
@ -10,6 +10,7 @@
#define EAP_H
#include "common/defs.h"
#include "utils/list.h"
#include "eap_common/eap_defs.h"
#include "eap_server/eap_methods.h"
#include "wpabuf.h"
@ -80,6 +81,17 @@ struct eap_eapol_interface {
Boolean aaaTimeout;
};
struct eap_server_erp_key {
struct dl_list list;
size_t rRK_len;
size_t rIK_len;
u8 rRK[ERP_MAX_KEY_LEN];
u8 rIK[ERP_MAX_KEY_LEN];
u32 recv_seq;
u8 cryptosuite;
char keyname_nai[];
};
struct eapol_callbacks {
int (*get_eap_user)(void *ctx, const u8 *identity, size_t identity_len,
int phase2, struct eap_user *user);
@ -87,6 +99,9 @@ struct eapol_callbacks {
void (*log_msg)(void *ctx, const char *msg);
int (*get_erp_send_reauth_start)(void *ctx);
const char * (*get_erp_domain)(void *ctx);
struct eap_server_erp_key * (*erp_get_key)(void *ctx,
const char *keyname);
int (*erp_add_key)(void *ctx, struct eap_server_erp_key *erp);
};
struct eap_config {
@ -115,6 +130,7 @@ struct eap_config {
const u8 *server_id;
size_t server_id_len;
int erp;
#ifdef CONFIG_TESTING_OPTIONS
u32 tls_test_flags;

View file

@ -117,7 +117,7 @@ struct eap_sm {
EAP_RECEIVED2, EAP_DISCARD2, EAP_SEND_REQUEST2,
EAP_AAA_REQUEST, EAP_AAA_RESPONSE, EAP_AAA_IDLE,
EAP_TIMEOUT_FAILURE2, EAP_FAILURE2, EAP_SUCCESS2,
EAP_INITIATE_REAUTH_START
EAP_INITIATE_REAUTH_START, EAP_INITIATE_RECEIVED
} EAP_state;
/* Constants */
@ -139,6 +139,7 @@ struct eap_sm {
/* Short-term (not maintained between packets) */
Boolean rxResp;
Boolean rxInitiate;
int respId;
EapType respMethod;
int respVendor;
@ -208,6 +209,7 @@ struct eap_sm {
Boolean initiate_reauth_start_sent;
Boolean try_initiate_reauth;
int erp;
#ifdef CONFIG_TESTING_OPTIONS
u32 tls_test_flags;

View file

@ -15,6 +15,7 @@
#include "includes.h"
#include "common.h"
#include "crypto/sha256.h"
#include "eap_i.h"
#include "state_machine.h"
#include "common/wpa_ctrl.h"
@ -60,6 +61,27 @@ static const char * eap_get_erp_domain(struct eap_sm *sm)
}
#ifdef CONFIG_ERP
static struct eap_server_erp_key * eap_erp_get_key(struct eap_sm *sm,
const char *keyname)
{
if (sm->eapol_cb->erp_get_key)
return sm->eapol_cb->erp_get_key(sm->eapol_ctx, keyname);
return NULL;
}
static int eap_erp_add_key(struct eap_sm *sm, struct eap_server_erp_key *erp)
{
if (sm->eapol_cb->erp_add_key)
return sm->eapol_cb->erp_add_key(sm->eapol_ctx, erp);
return -1;
}
#endif /* CONFIG_ERP */
static struct wpabuf * eap_sm_buildInitiateReauthStart(struct eap_sm *sm,
u8 id)
{
@ -71,7 +93,7 @@ static struct wpabuf * eap_sm_buildInitiateReauthStart(struct eap_sm *sm,
domain = eap_get_erp_domain(sm);
if (domain) {
domain_len = os_strlen(domain);
plen += 2 + domain_len;;
plen += 2 + domain_len;
}
msg = eap_msg_alloc(EAP_VENDOR_IETF, EAP_ERP_TYPE_REAUTH_START, plen,
@ -210,7 +232,6 @@ SM_STATE(EAP, INITIALIZE)
eap_server_clear_identity(sm);
}
sm->initiate_reauth_start_sent = FALSE;
sm->try_initiate_reauth = FALSE;
sm->currentId = -1;
sm->eap_if.eapSuccess = FALSE;
@ -387,6 +408,95 @@ SM_STATE(EAP, METHOD_REQUEST)
}
static void eap_server_erp_init(struct eap_sm *sm)
{
#ifdef CONFIG_ERP
u8 *emsk = NULL;
size_t emsk_len;
u8 EMSKname[EAP_EMSK_NAME_LEN];
u8 len[2];
const char *domain;
size_t domain_len, nai_buf_len;
struct eap_server_erp_key *erp = NULL;
int pos;
domain = eap_get_erp_domain(sm);
if (!domain)
return;
domain_len = os_strlen(domain);
nai_buf_len = 2 * EAP_EMSK_NAME_LEN + 1 + domain_len;
if (nai_buf_len > 253) {
/*
* keyName-NAI has a maximum length of 253 octet to fit in
* RADIUS attributes.
*/
wpa_printf(MSG_DEBUG,
"EAP: Too long realm for ERP keyName-NAI maximum length");
return;
}
nai_buf_len++; /* null termination */
erp = os_zalloc(sizeof(*erp) + nai_buf_len);
if (erp == NULL)
goto fail;
erp->recv_seq = (u32) -1;
emsk = sm->m->get_emsk(sm, sm->eap_method_priv, &emsk_len);
if (!emsk || emsk_len == 0 || emsk_len > ERP_MAX_KEY_LEN) {
wpa_printf(MSG_DEBUG,
"EAP: No suitable EMSK available for ERP");
goto fail;
}
wpa_hexdump_key(MSG_DEBUG, "EAP: EMSK", emsk, emsk_len);
WPA_PUT_BE16(len, 8);
if (hmac_sha256_kdf(sm->eap_if.eapSessionId, sm->eap_if.eapSessionIdLen,
"EMSK", len, sizeof(len),
EMSKname, EAP_EMSK_NAME_LEN) < 0) {
wpa_printf(MSG_DEBUG, "EAP: Could not derive EMSKname");
goto fail;
}
wpa_hexdump(MSG_DEBUG, "EAP: EMSKname", EMSKname, EAP_EMSK_NAME_LEN);
pos = wpa_snprintf_hex(erp->keyname_nai, nai_buf_len,
EMSKname, EAP_EMSK_NAME_LEN);
erp->keyname_nai[pos] = '@';
os_memcpy(&erp->keyname_nai[pos + 1], domain, domain_len);
WPA_PUT_BE16(len, emsk_len);
if (hmac_sha256_kdf(emsk, emsk_len,
"EAP Re-authentication Root Key@ietf.org",
len, sizeof(len), erp->rRK, emsk_len) < 0) {
wpa_printf(MSG_DEBUG, "EAP: Could not derive rRK for ERP");
goto fail;
}
erp->rRK_len = emsk_len;
wpa_hexdump_key(MSG_DEBUG, "EAP: ERP rRK", erp->rRK, erp->rRK_len);
if (hmac_sha256_kdf(erp->rRK, erp->rRK_len,
"EAP Re-authentication Integrity Key@ietf.org",
len, sizeof(len), erp->rIK, erp->rRK_len) < 0) {
wpa_printf(MSG_DEBUG, "EAP: Could not derive rIK for ERP");
goto fail;
}
erp->rIK_len = erp->rRK_len;
wpa_hexdump_key(MSG_DEBUG, "EAP: ERP rIK", erp->rIK, erp->rIK_len);
if (eap_erp_add_key(sm, erp) == 0) {
wpa_printf(MSG_DEBUG, "EAP: Stored ERP keys %s",
erp->keyname_nai);
erp = NULL;
}
fail:
bin_clear_free(emsk, emsk_len);
bin_clear_free(erp, sizeof(*erp));
#endif /* CONFIG_ERP */
}
SM_STATE(EAP, METHOD_RESPONSE)
{
SM_ENTRY(EAP, METHOD_RESPONSE);
@ -416,6 +526,8 @@ SM_STATE(EAP, METHOD_RESPONSE)
sm->eap_if.eapSessionId,
sm->eap_if.eapSessionIdLen);
}
if (sm->erp && sm->m->get_emsk && sm->eap_if.eapSessionId)
eap_server_erp_init(sm);
sm->methodState = METHOD_END;
} else {
sm->methodState = METHOD_CONTINUE;
@ -573,12 +685,307 @@ SM_STATE(EAP, INITIATE_REAUTH_START)
}
#ifdef CONFIG_ERP
static void erp_send_finish_reauth(struct eap_sm *sm,
struct eap_server_erp_key *erp, u8 id,
u8 flags, u16 seq, const char *nai)
{
size_t plen;
struct wpabuf *msg;
u8 hash[SHA256_MAC_LEN];
size_t hash_len;
u8 seed[4];
if (erp) {
switch (erp->cryptosuite) {
case EAP_ERP_CS_HMAC_SHA256_256:
hash_len = 32;
break;
case EAP_ERP_CS_HMAC_SHA256_128:
hash_len = 16;
break;
default:
return;
}
} else
hash_len = 0;
plen = 1 + 2 + 2 + os_strlen(nai);
if (hash_len)
plen += 1 + hash_len;
msg = eap_msg_alloc(EAP_VENDOR_IETF, EAP_ERP_TYPE_REAUTH, plen,
EAP_CODE_FINISH, id);
if (msg == NULL)
return;
wpabuf_put_u8(msg, flags);
wpabuf_put_be16(msg, seq);
wpabuf_put_u8(msg, EAP_ERP_TLV_KEYNAME_NAI);
wpabuf_put_u8(msg, os_strlen(nai));
wpabuf_put_str(msg, nai);
if (erp) {
wpabuf_put_u8(msg, erp->cryptosuite);
if (hmac_sha256(erp->rIK, erp->rIK_len,
wpabuf_head(msg), wpabuf_len(msg), hash) < 0) {
wpabuf_free(msg);
return;
}
wpabuf_put_data(msg, hash, hash_len);
}
wpa_printf(MSG_DEBUG, "EAP: Send EAP-Finish/Re-auth (%s)",
flags & 0x80 ? "failure" : "success");
sm->lastId = sm->currentId;
sm->currentId = id;
wpabuf_free(sm->eap_if.eapReqData);
sm->eap_if.eapReqData = msg;
wpabuf_free(sm->lastReqData);
sm->lastReqData = NULL;
if (flags & 0x80) {
sm->eap_if.eapFail = TRUE;
wpa_msg(sm->msg_ctx, MSG_INFO, WPA_EVENT_EAP_FAILURE
MACSTR, MAC2STR(sm->peer_addr));
return;
}
bin_clear_free(sm->eap_if.eapKeyData, sm->eap_if.eapKeyDataLen);
sm->eap_if.eapKeyDataLen = 0;
sm->eap_if.eapKeyData = os_malloc(erp->rRK_len);
if (!sm->eap_if.eapKeyData)
return;
WPA_PUT_BE16(seed, seq);
WPA_PUT_BE16(&seed[2], erp->rRK_len);
if (hmac_sha256_kdf(erp->rRK, erp->rRK_len,
"Re-authentication Master Session Key@ietf.org",
seed, sizeof(seed),
sm->eap_if.eapKeyData, erp->rRK_len) < 0) {
wpa_printf(MSG_DEBUG, "EAP: Could not derive rMSK for ERP");
bin_clear_free(sm->eap_if.eapKeyData, erp->rRK_len);
sm->eap_if.eapKeyData = NULL;
return;
}
sm->eap_if.eapKeyDataLen = erp->rRK_len;
sm->eap_if.eapKeyAvailable = TRUE;
wpa_hexdump_key(MSG_DEBUG, "EAP: ERP rMSK",
sm->eap_if.eapKeyData, sm->eap_if.eapKeyDataLen);
sm->eap_if.eapSuccess = TRUE;
wpa_msg(sm->msg_ctx, MSG_INFO, WPA_EVENT_EAP_SUCCESS
MACSTR, MAC2STR(sm->peer_addr));
}
SM_STATE(EAP, INITIATE_RECEIVED)
{
const u8 *pos, *end, *start, *tlvs, *hdr;
const struct eap_hdr *ehdr;
size_t len;
u8 flags;
u16 seq;
char nai[254];
struct eap_server_erp_key *erp;
int max_len;
u8 hash[SHA256_MAC_LEN];
size_t hash_len;
struct erp_tlvs parse;
u8 resp_flags = 0x80; /* default to failure; cleared on success */
SM_ENTRY(EAP, INITIATE_RECEIVED);
sm->rxInitiate = FALSE;
pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_ERP_TYPE_REAUTH,
sm->eap_if.eapRespData, &len);
if (pos == NULL) {
wpa_printf(MSG_INFO, "EAP-Initiate: Invalid frame");
goto fail;
}
hdr = wpabuf_head(sm->eap_if.eapRespData);
ehdr = wpabuf_head(sm->eap_if.eapRespData);
wpa_hexdump(MSG_DEBUG, "EAP: EAP-Initiate/Re-Auth", pos, len);
if (len < 4) {
wpa_printf(MSG_INFO, "EAP: Too short EAP-Initiate/Re-auth");
goto fail;
}
end = pos + len;
flags = *pos++;
seq = WPA_GET_BE16(pos);
pos += 2;
wpa_printf(MSG_DEBUG, "EAP: Flags=0x%x SEQ=%u", flags, seq);
tlvs = pos;
/*
* Parse TVs/TLVs. Since we do not yet know the length of the
* Authentication Tag, stop parsing if an unknown TV/TLV is seen and
* just try to find the keyName-NAI first so that we can check the
* Authentication Tag.
*/
if (erp_parse_tlvs(tlvs, end, &parse, 1) < 0)
goto fail;
if (!parse.keyname) {
wpa_printf(MSG_DEBUG,
"EAP: No keyName-NAI in EAP-Initiate/Re-auth Packet");
goto fail;
}
wpa_hexdump_ascii(MSG_DEBUG, "EAP: EAP-Initiate/Re-auth - keyName-NAI",
parse.keyname, parse.keyname_len);
if (parse.keyname_len > 253) {
wpa_printf(MSG_DEBUG,
"EAP: Too long keyName-NAI in EAP-Initiate/Re-auth");
goto fail;
}
os_memcpy(nai, parse.keyname, parse.keyname_len);
nai[parse.keyname_len] = '\0';
if (!sm->eap_server) {
/*
* In passthrough case, EAP-Initiate/Re-auth replaces
* EAP Identity exchange. Use keyName-NAI as the user identity
* and forward EAP-Initiate/Re-auth to the backend
* authentication server.
*/
wpa_printf(MSG_DEBUG,
"EAP: Use keyName-NAI as user identity for backend authentication");
eap_server_clear_identity(sm);
sm->identity = (u8 *) dup_binstr(parse.keyname,
parse.keyname_len);
if (!sm->identity)
goto fail;
sm->identity_len = parse.keyname_len;
return;
}
erp = eap_erp_get_key(sm, nai);
if (!erp) {
wpa_printf(MSG_DEBUG, "EAP: No matching ERP key found for %s",
nai);
goto report_error;
}
if (erp->recv_seq != (u32) -1 && erp->recv_seq >= seq) {
wpa_printf(MSG_DEBUG,
"EAP: SEQ=%u replayed (already received SEQ=%u)",
seq, erp->recv_seq);
goto fail;
}
/* Is there enough room for Cryptosuite and Authentication Tag? */
start = parse.keyname + parse.keyname_len;
max_len = end - start;
if (max_len <
1 + (erp->cryptosuite == EAP_ERP_CS_HMAC_SHA256_256 ? 32 : 16)) {
wpa_printf(MSG_DEBUG,
"EAP: Not enough room for Authentication Tag");
goto fail;
}
switch (erp->cryptosuite) {
case EAP_ERP_CS_HMAC_SHA256_256:
if (end[-33] != erp->cryptosuite) {
wpa_printf(MSG_DEBUG,
"EAP: Different Cryptosuite used");
goto fail;
}
hash_len = 32;
break;
case EAP_ERP_CS_HMAC_SHA256_128:
if (end[-17] != erp->cryptosuite) {
wpa_printf(MSG_DEBUG,
"EAP: Different Cryptosuite used");
goto fail;
}
hash_len = 16;
break;
default:
hash_len = 0;
break;
}
if (hash_len) {
if (hmac_sha256(erp->rIK, erp->rIK_len, hdr,
end - hdr - hash_len, hash) < 0)
goto fail;
if (os_memcmp(end - hash_len, hash, hash_len) != 0) {
wpa_printf(MSG_DEBUG,
"EAP: Authentication Tag mismatch");
goto fail;
}
}
/* Check if any supported CS results in matching tag */
if (!hash_len && max_len >= 1 + 32 &&
end[-33] == EAP_ERP_CS_HMAC_SHA256_256) {
if (hmac_sha256(erp->rIK, erp->rIK_len, hdr,
end - hdr - 32, hash) < 0)
goto fail;
if (os_memcmp(end - 32, hash, 32) == 0) {
wpa_printf(MSG_DEBUG,
"EAP: Authentication Tag match using HMAC-SHA256-256");
hash_len = 32;
erp->cryptosuite = EAP_ERP_CS_HMAC_SHA256_256;
}
}
if (!hash_len && end[-17] == EAP_ERP_CS_HMAC_SHA256_128) {
if (hmac_sha256(erp->rIK, erp->rIK_len, hdr,
end - hdr - 16, hash) < 0)
goto fail;
if (os_memcmp(end - 16, hash, 16) == 0) {
wpa_printf(MSG_DEBUG,
"EAP: Authentication Tag match using HMAC-SHA256-128");
hash_len = 16;
erp->cryptosuite = EAP_ERP_CS_HMAC_SHA256_128;
}
}
if (!hash_len) {
wpa_printf(MSG_DEBUG,
"EAP: No supported cryptosuite matched Authentication Tag");
goto fail;
}
end -= 1 + hash_len;
/*
* Parse TVs/TLVs again now that we know the exact part of the buffer
* that contains them.
*/
wpa_hexdump(MSG_DEBUG, "EAP: EAP-Initiate/Re-Auth TVs/TLVs",
tlvs, end - tlvs);
if (erp_parse_tlvs(tlvs, end, &parse, 0) < 0)
goto fail;
wpa_printf(MSG_DEBUG, "EAP: ERP key %s SEQ updated to %u",
erp->keyname_nai, seq);
erp->recv_seq = seq;
resp_flags &= ~0x80; /* R=0 - success */
report_error:
erp_send_finish_reauth(sm, erp, ehdr->identifier, resp_flags, seq, nai);
return;
fail:
sm->ignore = TRUE;
}
#endif /* CONFIG_ERP */
SM_STATE(EAP, INITIALIZE_PASSTHROUGH)
{
SM_ENTRY(EAP, INITIALIZE_PASSTHROUGH);
wpabuf_free(sm->eap_if.aaaEapRespData);
sm->eap_if.aaaEapRespData = NULL;
sm->try_initiate_reauth = FALSE;
}
@ -802,6 +1209,10 @@ SM_STEP(EAP)
sm->respVendor == EAP_VENDOR_IETF &&
sm->respVendorMethod == sm->currentMethod)))
SM_ENTER(EAP, INTEGRITY_CHECK);
#ifdef CONFIG_ERP
else if (sm->rxInitiate)
SM_ENTER(EAP, INITIATE_RECEIVED);
#endif /* CONFIG_ERP */
else {
wpa_printf(MSG_DEBUG, "EAP: RECEIVED->DISCARD: "
"rxResp=%d respId=%d currentId=%d "
@ -892,12 +1303,20 @@ SM_STEP(EAP)
SM_ENTER(EAP, INITIALIZE_PASSTHROUGH);
else if (sm->decision == DECISION_INITIATE_REAUTH_START)
SM_ENTER(EAP, INITIATE_REAUTH_START);
#ifdef CONFIG_ERP
else if (sm->eap_server && sm->erp && sm->rxInitiate)
SM_ENTER(EAP, INITIATE_RECEIVED);
#endif /* CONFIG_ERP */
else
SM_ENTER(EAP, PROPOSE_METHOD);
break;
case EAP_INITIATE_REAUTH_START:
SM_ENTER(EAP, SEND_REQUEST);
break;
case EAP_INITIATE_RECEIVED:
if (!sm->eap_server)
SM_ENTER(EAP, SELECT_ACTION);
break;
case EAP_TIMEOUT_FAILURE:
break;
case EAP_FAILURE:
@ -1026,6 +1445,7 @@ static void eap_sm_parseEapResp(struct eap_sm *sm, const struct wpabuf *resp)
/* parse rxResp, respId, respMethod */
sm->rxResp = FALSE;
sm->rxInitiate = FALSE;
sm->respId = -1;
sm->respMethod = EAP_TYPE_NONE;
sm->respVendor = EAP_VENDOR_IETF;
@ -1052,6 +1472,8 @@ static void eap_sm_parseEapResp(struct eap_sm *sm, const struct wpabuf *resp)
if (hdr->code == EAP_CODE_RESPONSE)
sm->rxResp = TRUE;
else if (hdr->code == EAP_CODE_INITIATE)
sm->rxInitiate = TRUE;
if (plen > sizeof(*hdr)) {
u8 *pos = (u8 *) (hdr + 1);
@ -1069,10 +1491,10 @@ static void eap_sm_parseEapResp(struct eap_sm *sm, const struct wpabuf *resp)
}
}
wpa_printf(MSG_DEBUG, "EAP: parseEapResp: rxResp=%d respId=%d "
"respMethod=%u respVendor=%u respVendorMethod=%u",
sm->rxResp, sm->respId, sm->respMethod, sm->respVendor,
sm->respVendorMethod);
wpa_printf(MSG_DEBUG,
"EAP: parseEapResp: rxResp=%d rxInitiate=%d respId=%d respMethod=%u respVendor=%u respVendorMethod=%u",
sm->rxResp, sm->rxInitiate, sm->respId, sm->respMethod,
sm->respVendor, sm->respVendorMethod);
}
@ -1430,6 +1852,7 @@ struct eap_sm * eap_server_sm_init(void *eapol_ctx,
sm->pbc_in_m1 = conf->pbc_in_m1;
sm->server_id = conf->server_id;
sm->server_id_len = conf->server_id_len;
sm->erp = conf->erp;
#ifdef CONFIG_TESTING_OPTIONS
sm->tls_test_flags = conf->tls_test_flags;

View file

@ -834,6 +834,7 @@ eapol_auth_alloc(struct eapol_authenticator *eapol, const u8 *addr,
eap_conf.pbc_in_m1 = eapol->conf.pbc_in_m1;
eap_conf.server_id = eapol->conf.server_id;
eap_conf.server_id_len = eapol->conf.server_id_len;
eap_conf.erp = eapol->conf.erp;
sm->eap = eap_server_sm_init(sm, &eapol_cb, &eap_conf);
if (sm->eap == NULL) {
eapol_auth_free(sm);
@ -1040,6 +1041,21 @@ static const char * eapol_sm_get_erp_domain(void *ctx)
}
static struct eap_server_erp_key * eapol_sm_erp_get_key(void *ctx,
const char *keyname)
{
struct eapol_state_machine *sm = ctx;
return sm->eapol->cb.erp_get_key(sm->eapol->conf.ctx, keyname);
}
static int eapol_sm_erp_add_key(void *ctx, struct eap_server_erp_key *erp)
{
struct eapol_state_machine *sm = ctx;
return sm->eapol->cb.erp_add_key(sm->eapol->conf.ctx, erp);
}
static struct eapol_callbacks eapol_cb =
{
eapol_sm_get_eap_user,
@ -1047,6 +1063,8 @@ static struct eapol_callbacks eapol_cb =
NULL,
eapol_sm_get_erp_send_reauth_start,
eapol_sm_get_erp_domain,
eapol_sm_erp_get_key,
eapol_sm_erp_add_key,
};
@ -1129,6 +1147,7 @@ static int eapol_auth_conf_clone(struct eapol_auth_config *dst,
dst->erp_domain = NULL;
}
dst->erp_send_reauth_start = src->erp_send_reauth_start;
dst->erp = src->erp;
return 0;
@ -1183,6 +1202,8 @@ struct eapol_authenticator * eapol_auth_init(struct eapol_auth_config *conf,
eapol->cb.abort_auth = cb->abort_auth;
eapol->cb.tx_key = cb->tx_key;
eapol->cb.eapol_event = cb->eapol_event;
eapol->cb.erp_get_key = cb->erp_get_key;
eapol->cb.erp_add_key = cb->erp_add_key;
/* Acct-Multi-Session-Id should be unique over reboots. If reliable
* clock is not available, this could be replaced with reboot counter,

View file

@ -26,6 +26,7 @@ struct eapol_auth_config {
size_t eap_req_id_text_len;
int erp_send_reauth_start;
char *erp_domain; /* a copy of this will be allocated */
int erp; /* Whether ERP is enabled on authentication server */
u8 *pac_opaque_encr_key;
u8 *eap_fast_a_id;
size_t eap_fast_a_id_len;
@ -47,6 +48,7 @@ struct eapol_auth_config {
};
struct eap_user;
struct eap_server_erp_key;
typedef enum {
EAPOL_LOGGER_DEBUG, EAPOL_LOGGER_INFO, EAPOL_LOGGER_WARNING
@ -73,6 +75,9 @@ struct eapol_auth_cb {
void (*abort_auth)(void *ctx, void *sta_ctx);
void (*tx_key)(void *ctx, void *sta_ctx);
void (*eapol_event)(void *ctx, void *sta_ctx, enum eapol_event type);
struct eap_server_erp_key * (*erp_get_key)(void *ctx,
const char *keyname);
int (*erp_add_key)(void *ctx, struct eap_server_erp_key *erp);
};

View file

@ -251,6 +251,20 @@ struct radius_server_data {
*/
const char *server_id;
/**
* erp - Whether EAP Re-authentication Protocol (ERP) is enabled
*
* This controls whether the authentication server derives ERP key
* hierarchy (rRK and rIK) from full EAP authentication and allows
* these keys to be used to perform ERP to derive rMSK instead of full
* EAP authentication to derive MSK.
*/
int erp;
const char *erp_domain;
struct dl_list erp_keys; /* struct eap_server_erp_key */
/**
* wps - Wi-Fi Protected Setup context
*
@ -673,6 +687,7 @@ radius_server_get_new_session(struct radius_server_data *data,
eap_conf.pwd_group = data->pwd_group;
eap_conf.server_id = (const u8 *) data->server_id;
eap_conf.server_id_len = os_strlen(data->server_id);
eap_conf.erp = data->erp;
radius_server_testing_options(sess, &eap_conf);
sess->eap = eap_server_sm_init(sess, &radius_server_eapol_cb,
&eap_conf);
@ -1687,6 +1702,7 @@ radius_server_init(struct radius_server_conf *conf)
if (data == NULL)
return NULL;
dl_list_init(&data->erp_keys);
os_get_reltime(&data->start_time);
data->conf_ctx = conf->conf_ctx;
data->eap_sim_db_priv = conf->eap_sim_db_priv;
@ -1725,6 +1741,8 @@ radius_server_init(struct radius_server_conf *conf)
data->eap_req_id_text_len = conf->eap_req_id_text_len;
}
}
data->erp = conf->erp;
data->erp_domain = conf->erp_domain;
if (conf->subscr_remediation_url) {
data->subscr_remediation_url =
@ -1807,6 +1825,8 @@ radius_server_init(struct radius_server_conf *conf)
*/
void radius_server_deinit(struct radius_server_data *data)
{
struct eap_server_erp_key *erp;
if (data == NULL)
return;
@ -1836,6 +1856,12 @@ void radius_server_deinit(struct radius_server_data *data)
sqlite3_close(data->db);
#endif /* CONFIG_SQLITE */
while ((erp = dl_list_first(&data->erp_keys, struct eap_server_erp_key,
list)) != NULL) {
dl_list_del(&erp->list);
bin_clear_free(erp, sizeof(*erp));
}
os_free(data);
}
@ -2017,13 +2043,57 @@ static void radius_server_log_msg(void *ctx, const char *msg)
}
#ifdef CONFIG_ERP
static const char * radius_server_get_erp_domain(void *ctx)
{
struct radius_session *sess = ctx;
struct radius_server_data *data = sess->server;
return data->erp_domain;
}
static struct eap_server_erp_key *
radius_server_erp_get_key(void *ctx, const char *keyname)
{
struct radius_session *sess = ctx;
struct radius_server_data *data = sess->server;
struct eap_server_erp_key *erp;
dl_list_for_each(erp, &data->erp_keys, struct eap_server_erp_key,
list) {
if (os_strcmp(erp->keyname_nai, keyname) == 0)
return erp;
}
return NULL;
}
static int radius_server_erp_add_key(void *ctx, struct eap_server_erp_key *erp)
{
struct radius_session *sess = ctx;
struct radius_server_data *data = sess->server;
dl_list_add(&data->erp_keys, &erp->list);
return 0;
}
#endif /* CONFIG_ERP */
static struct eapol_callbacks radius_server_eapol_cb =
{
.get_eap_user = radius_server_get_eap_user,
.get_eap_req_id_text = radius_server_get_eap_req_id_text,
.log_msg = radius_server_log_msg,
NULL,
NULL,
#ifdef CONFIG_ERP
.get_erp_send_reauth_start = NULL,
.get_erp_domain = radius_server_get_erp_domain,
.erp_get_key = radius_server_erp_get_key,
.erp_add_key = radius_server_erp_add_key,
#endif /* CONFIG_ERP */
};

View file

@ -158,6 +158,18 @@ struct radius_server_conf {
*/
const char *server_id;
/**
* erp - Whether EAP Re-authentication Protocol (ERP) is enabled
*
* This controls whether the authentication server derives ERP key
* hierarchy (rRK and rIK) from full EAP authentication and allows
* these keys to be used to perform ERP to derive rMSK instead of full
* EAP authentication to derive MSK.
*/
int erp;
const char *erp_domain;
/**
* wps - Wi-Fi Protected Setup context
*