From 2a5156a66c27dc36b155d0157798a81fe120efc9 Mon Sep 17 00:00:00 2001 From: Jouni Malinen Date: Sat, 29 Nov 2014 20:33:09 +0200 Subject: [PATCH] ERP: Add optional EAP-Initiate/Re-auth-Start transmission hostapd can now be configured to transmit EAP-Initiate/Re-auth-Start before EAP-Request/Identity to try to initiate ERP. This is disabled by default and can be enabled with erp_send_reauth_start=1 and optional erp_reauth_start_domain=. Signed-off-by: Jouni Malinen --- hostapd/config_file.c | 5 ++ hostapd/hostapd.conf | 10 ++++ src/ap/ap_config.c | 1 + src/ap/ap_config.h | 2 + src/ap/ieee802_1x.c | 2 + src/eap_server/eap.h | 2 + src/eap_server/eap_i.h | 8 ++- src/eap_server/eap_server.c | 99 ++++++++++++++++++++++++++++++++-- src/eapol_auth/eapol_auth_sm.c | 59 ++++++++++++++------ src/eapol_auth/eapol_auth_sm.h | 2 + src/radius/radius_server.c | 2 + 11 files changed, 169 insertions(+), 23 deletions(-) diff --git a/hostapd/config_file.c b/hostapd/config_file.c index 79bbba4d2..76d89649f 100644 --- a/hostapd/config_file.c +++ b/hostapd/config_file.c @@ -2071,6 +2071,11 @@ static int hostapd_config_fill(struct hostapd_config *conf, (term - bss->eap_req_id_text) - 1); bss->eap_req_id_text_len--; } + } else if (os_strcmp(buf, "erp_send_reauth_start") == 0) { + bss->erp_send_reauth_start = atoi(pos); + } else if (os_strcmp(buf, "erp_domain") == 0) { + os_free(bss->erp_domain); + bss->erp_domain = os_strdup(pos); } else if (os_strcmp(buf, "wep_key_len_broadcast") == 0) { bss->default_wep_key_len = atoi(pos); if (bss->default_wep_key_len > 13) { diff --git a/hostapd/hostapd.conf b/hostapd/hostapd.conf index dc7e95b64..44dacb733 100644 --- a/hostapd/hostapd.conf +++ b/hostapd/hostapd.conf @@ -696,6 +696,16 @@ eapol_key_index_workaround=0 # is only used by one station. #use_pae_group_addr=1 +# EAP Re-authentication Protocol (ERP) authenticator (RFC 6696) +# +# Whether to initiate EAP authentication with EAP-Initiate/Re-auth-Start before +# EAP-Identity/Request +#erp_send_reauth_start=1 +# +# Domain name for EAP-Initiate/Re-auth-Start. Omitted from the message if not +# set (no local ER server). +#erp_domain=example.com + ##### Integrated EAP server ################################################### # Optionally, hostapd can be configured to use an integrated EAP server diff --git a/src/ap/ap_config.c b/src/ap/ap_config.c index e888b69a8..a69d8bc8e 100644 --- a/src/ap/ap_config.c +++ b/src/ap/ap_config.c @@ -425,6 +425,7 @@ void hostapd_config_free_bss(struct hostapd_bss_config *conf) os_free(conf->eap_user_sqlite); os_free(conf->eap_req_id_text); + os_free(conf->erp_domain); os_free(conf->accept_mac); os_free(conf->deny_mac); os_free(conf->nas_identifier); diff --git a/src/ap/ap_config.h b/src/ap/ap_config.h index 7abfb5f15..874ce6171 100644 --- a/src/ap/ap_config.h +++ b/src/ap/ap_config.h @@ -259,6 +259,8 @@ struct hostapd_bss_config { int wep_rekeying_period; int broadcast_key_idx_min, broadcast_key_idx_max; int eap_reauth_period; + int erp_send_reauth_start; + char *erp_domain; int ieee802_11f; /* use IEEE 802.11f (IAPP) */ char iapp_iface[IFNAMSIZ + 1]; /* interface used with IAPP broadcast diff --git a/src/ap/ieee802_1x.c b/src/ap/ieee802_1x.c index 02d8ae584..d29838564 100644 --- a/src/ap/ieee802_1x.c +++ b/src/ap/ieee802_1x.c @@ -2003,6 +2003,8 @@ int ieee802_1x_init(struct hostapd_data *hapd) conf.eap_sim_db_priv = hapd->eap_sim_db_priv; conf.eap_req_id_text = hapd->conf->eap_req_id_text; 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.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; diff --git a/src/eap_server/eap.h b/src/eap_server/eap.h index 3fdc0668e..395d8955f 100644 --- a/src/eap_server/eap.h +++ b/src/eap_server/eap.h @@ -85,6 +85,8 @@ struct eapol_callbacks { int phase2, struct eap_user *user); const char * (*get_eap_req_id_text)(void *ctx, size_t *len); 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_config { diff --git a/src/eap_server/eap_i.h b/src/eap_server/eap_i.h index 0ec71abd4..9c757d9c8 100644 --- a/src/eap_server/eap_i.h +++ b/src/eap_server/eap_i.h @@ -116,7 +116,8 @@ struct eap_sm { EAP_INITIALIZE_PASSTHROUGH, EAP_IDLE2, EAP_RETRANSMIT2, EAP_RECEIVED2, EAP_DISCARD2, EAP_SEND_REQUEST2, EAP_AAA_REQUEST, EAP_AAA_RESPONSE, EAP_AAA_IDLE, - EAP_TIMEOUT_FAILURE2, EAP_FAILURE2, EAP_SUCCESS2 + EAP_TIMEOUT_FAILURE2, EAP_FAILURE2, EAP_SUCCESS2, + EAP_INITIATE_REAUTH_START } EAP_state; /* Constants */ @@ -145,7 +146,7 @@ struct eap_sm { Boolean ignore; enum { DECISION_SUCCESS, DECISION_FAILURE, DECISION_CONTINUE, - DECISION_PASSTHROUGH + DECISION_PASSTHROUGH, DECISION_INITIATE_REAUTH_START } decision; /* Miscellaneous variables */ @@ -205,6 +206,9 @@ struct eap_sm { const u8 *server_id; size_t server_id_len; + Boolean initiate_reauth_start_sent; + Boolean try_initiate_reauth; + #ifdef CONFIG_TESTING_OPTIONS u32 tls_test_flags; #endif /* CONFIG_TESTING_OPTIONS */ diff --git a/src/eap_server/eap_server.c b/src/eap_server/eap_server.c index ff98acce8..b0ec75700 100644 --- a/src/eap_server/eap_server.c +++ b/src/eap_server/eap_server.c @@ -1,6 +1,6 @@ /* * hostapd / EAP Full Authenticator state machine (RFC 4137) - * Copyright (c) 2004-2007, Jouni Malinen + * Copyright (c) 2004-2014, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -44,6 +44,52 @@ static int eap_sm_Policy_getDecision(struct eap_sm *sm); static Boolean eap_sm_Policy_doPickUp(struct eap_sm *sm, EapType method); +static int eap_get_erp_send_reauth_start(struct eap_sm *sm) +{ + if (sm->eapol_cb->get_erp_send_reauth_start) + return sm->eapol_cb->get_erp_send_reauth_start(sm->eapol_ctx); + return 0; +} + + +static const char * eap_get_erp_domain(struct eap_sm *sm) +{ + if (sm->eapol_cb->get_erp_domain) + return sm->eapol_cb->get_erp_domain(sm->eapol_ctx); + return NULL; +} + + +static struct wpabuf * eap_sm_buildInitiateReauthStart(struct eap_sm *sm, + u8 id) +{ + const char *domain; + size_t plen = 1; + struct wpabuf *msg; + size_t domain_len = 0; + + domain = eap_get_erp_domain(sm); + if (domain) { + domain_len = os_strlen(domain); + plen += 2 + domain_len;; + } + + msg = eap_msg_alloc(EAP_VENDOR_IETF, EAP_ERP_TYPE_REAUTH_START, plen, + EAP_CODE_INITIATE, id); + if (msg == NULL) + return NULL; + wpabuf_put_u8(msg, 0); /* Reserved */ + if (domain) { + /* Domain name TLV */ + wpabuf_put_u8(msg, EAP_ERP_TLV_DOMAIN_NAME); + wpabuf_put_u8(msg, domain_len); + wpabuf_put_data(msg, domain, domain_len); + } + + return msg; +} + + static int eap_copy_buf(struct wpabuf **dst, const struct wpabuf *src) { if (src == NULL) @@ -164,6 +210,8 @@ 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; sm->eap_if.eapFail = FALSE; @@ -382,6 +430,7 @@ SM_STATE(EAP, PROPOSE_METHOD) SM_ENTRY(EAP, PROPOSE_METHOD); + sm->try_initiate_reauth = FALSE; try_another_method: type = eap_sm_Policy_getNextMethod(sm, &vendor); if (vendor == EAP_VENDOR_IETF) @@ -505,6 +554,25 @@ SM_STATE(EAP, SUCCESS) } +SM_STATE(EAP, INITIATE_REAUTH_START) +{ + SM_ENTRY(EAP, INITIATE_REAUTH_START); + + sm->initiate_reauth_start_sent = TRUE; + sm->try_initiate_reauth = TRUE; + sm->currentId = eap_sm_nextId(sm, sm->currentId); + wpa_printf(MSG_DEBUG, + "EAP: building EAP-Initiate-Re-auth-Start: Identifier %d", + sm->currentId); + sm->lastId = sm->currentId; + wpabuf_free(sm->eap_if.eapReqData); + sm->eap_if.eapReqData = eap_sm_buildInitiateReauthStart(sm, + sm->currentId); + wpabuf_free(sm->lastReqData); + sm->lastReqData = NULL; +} + + SM_STATE(EAP, INITIALIZE_PASSTHROUGH) { SM_ENTRY(EAP, INITIALIZE_PASSTHROUGH); @@ -704,9 +772,14 @@ SM_STEP(EAP) SM_ENTER(EAP, INITIALIZE); break; case EAP_IDLE: - if (sm->eap_if.retransWhile == 0) - SM_ENTER(EAP, RETRANSMIT); - else if (sm->eap_if.eapResp) + if (sm->eap_if.retransWhile == 0) { + if (sm->try_initiate_reauth) { + sm->try_initiate_reauth = FALSE; + SM_ENTER(EAP, SELECT_ACTION); + } else { + SM_ENTER(EAP, RETRANSMIT); + } + } else if (sm->eap_if.eapResp) SM_ENTER(EAP, RECEIVED); break; case EAP_RETRANSMIT: @@ -817,9 +890,14 @@ SM_STEP(EAP) SM_ENTER(EAP, SUCCESS); else if (sm->decision == DECISION_PASSTHROUGH) SM_ENTER(EAP, INITIALIZE_PASSTHROUGH); + else if (sm->decision == DECISION_INITIATE_REAUTH_START) + SM_ENTER(EAP, INITIATE_REAUTH_START); else SM_ENTER(EAP, PROPOSE_METHOD); break; + case EAP_INITIATE_REAUTH_START: + SM_ENTER(EAP, SEND_REQUEST); + break; case EAP_TIMEOUT_FAILURE: break; case EAP_FAILURE: @@ -889,6 +967,12 @@ static int eap_sm_calculateTimeout(struct eap_sm *sm, int retransCount, { int rto, i; + if (sm->try_initiate_reauth) { + wpa_printf(MSG_DEBUG, + "EAP: retransmit timeout 1 second for EAP-Initiate-Re-auth-Start"); + return 1; + } + if (methodTimeout) { /* * EAP method (either internal or through AAA server, provided @@ -1229,6 +1313,13 @@ static int eap_sm_Policy_getDecision(struct eap_sm *sm) return DECISION_CONTINUE; } + if (!sm->identity && eap_get_erp_send_reauth_start(sm) && + !sm->initiate_reauth_start_sent) { + wpa_printf(MSG_DEBUG, + "EAP: getDecision: send EAP-Initiate/Re-auth-Start"); + return DECISION_INITIATE_REAUTH_START; + } + if (sm->identity == NULL || sm->currentId == -1) { wpa_printf(MSG_DEBUG, "EAP: getDecision: no identity known " "yet -> CONTINUE"); diff --git a/src/eapol_auth/eapol_auth_sm.c b/src/eapol_auth/eapol_auth_sm.c index 8584cf0b4..088e9d3e2 100644 --- a/src/eapol_auth/eapol_auth_sm.c +++ b/src/eapol_auth/eapol_auth_sm.c @@ -1,6 +1,6 @@ /* * IEEE 802.1X-2004 Authenticator - EAPOL state machine - * Copyright (c) 2002-2009, Jouni Malinen + * Copyright (c) 2002-2014, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -43,6 +43,7 @@ sm->eapol->cb.set_port_authorized(sm->eapol->conf.ctx, sm->sta, 0) static void eapol_sm_step_run(struct eapol_state_machine *sm); static void eapol_sm_step_cb(void *eloop_ctx, void *timeout_ctx); static void eapol_auth_initialize(struct eapol_state_machine *sm); +static void eapol_auth_conf_free(struct eapol_auth_config *conf); static void eapol_auth_logger(struct eapol_authenticator *eapol, @@ -1025,11 +1026,27 @@ static const char * eapol_sm_get_eap_req_id_text(void *ctx, size_t *len) } +static int eapol_sm_get_erp_send_reauth_start(void *ctx) +{ + struct eapol_state_machine *sm = ctx; + return sm->eapol->conf.erp_send_reauth_start; +} + + +static const char * eapol_sm_get_erp_domain(void *ctx) +{ + struct eapol_state_machine *sm = ctx; + return sm->eapol->conf.erp_domain; +} + + static struct eapol_callbacks eapol_cb = { eapol_sm_get_eap_user, eapol_sm_get_eap_req_id_text, - NULL + NULL, + eapol_sm_get_erp_send_reauth_start, + eapol_sm_get_erp_domain, }; @@ -1074,21 +1091,16 @@ static int eapol_auth_conf_clone(struct eapol_auth_config *dst, } if (src->pac_opaque_encr_key) { dst->pac_opaque_encr_key = os_malloc(16); - if (dst->pac_opaque_encr_key == NULL) { - os_free(dst->eap_req_id_text); - return -1; - } + if (dst->pac_opaque_encr_key == NULL) + goto fail; os_memcpy(dst->pac_opaque_encr_key, src->pac_opaque_encr_key, 16); } else dst->pac_opaque_encr_key = NULL; if (src->eap_fast_a_id) { dst->eap_fast_a_id = os_malloc(src->eap_fast_a_id_len); - if (dst->eap_fast_a_id == NULL) { - os_free(dst->eap_req_id_text); - os_free(dst->pac_opaque_encr_key); - return -1; - } + if (dst->eap_fast_a_id == NULL) + goto fail; os_memcpy(dst->eap_fast_a_id, src->eap_fast_a_id, src->eap_fast_a_id_len); dst->eap_fast_a_id_len = src->eap_fast_a_id_len; @@ -1096,12 +1108,8 @@ static int eapol_auth_conf_clone(struct eapol_auth_config *dst, dst->eap_fast_a_id = NULL; if (src->eap_fast_a_id_info) { dst->eap_fast_a_id_info = os_strdup(src->eap_fast_a_id_info); - if (dst->eap_fast_a_id_info == NULL) { - os_free(dst->eap_req_id_text); - os_free(dst->pac_opaque_encr_key); - os_free(dst->eap_fast_a_id); - return -1; - } + if (dst->eap_fast_a_id_info == NULL) + goto fail; } else dst->eap_fast_a_id_info = NULL; dst->eap_fast_prov = src->eap_fast_prov; @@ -1111,7 +1119,22 @@ static int eapol_auth_conf_clone(struct eapol_auth_config *dst, dst->tnc = src->tnc; dst->wps = src->wps; dst->fragment_size = src->fragment_size; + + os_free(dst->erp_domain); + if (src->erp_domain) { + dst->erp_domain = os_strdup(src->erp_domain); + if (dst->erp_domain == NULL) + goto fail; + } else { + dst->erp_domain = NULL; + } + dst->erp_send_reauth_start = src->erp_send_reauth_start; + return 0; + +fail: + eapol_auth_conf_free(dst); + return -1; } @@ -1125,6 +1148,8 @@ static void eapol_auth_conf_free(struct eapol_auth_config *conf) conf->eap_fast_a_id = NULL; os_free(conf->eap_fast_a_id_info); conf->eap_fast_a_id_info = NULL; + os_free(conf->erp_domain); + conf->erp_domain = NULL; } diff --git a/src/eapol_auth/eapol_auth_sm.h b/src/eapol_auth/eapol_auth_sm.h index 320a0adbf..90194d1f6 100644 --- a/src/eapol_auth/eapol_auth_sm.h +++ b/src/eapol_auth/eapol_auth_sm.h @@ -24,6 +24,8 @@ struct eapol_auth_config { void *eap_sim_db_priv; char *eap_req_id_text; /* a copy of this will be allocated */ size_t eap_req_id_text_len; + int erp_send_reauth_start; + char *erp_domain; /* a copy of this will be allocated */ u8 *pac_opaque_encr_key; u8 *eap_fast_a_id; size_t eap_fast_a_id_len; diff --git a/src/radius/radius_server.c b/src/radius/radius_server.c index 00394b49c..54b258993 100644 --- a/src/radius/radius_server.c +++ b/src/radius/radius_server.c @@ -2022,6 +2022,8 @@ 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, };