]> git.ipfire.org Git - thirdparty/hostap.git/commitdiff
ERP: Add optional EAP-Initiate/Re-auth-Start transmission
authorJouni Malinen <j@w1.fi>
Sat, 29 Nov 2014 18:33:09 +0000 (20:33 +0200)
committerJouni Malinen <j@w1.fi>
Thu, 4 Dec 2014 10:08:56 +0000 (12:08 +0200)
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=<domain>.

Signed-off-by: Jouni Malinen <j@w1.fi>
hostapd/config_file.c
hostapd/hostapd.conf
src/ap/ap_config.c
src/ap/ap_config.h
src/ap/ieee802_1x.c
src/eap_server/eap.h
src/eap_server/eap_i.h
src/eap_server/eap_server.c
src/eapol_auth/eapol_auth_sm.c
src/eapol_auth/eapol_auth_sm.h
src/radius/radius_server.c

index 79bbba4d248099e0bc9396be08af3cbf4174634c..76d89649f832a14d20dea2deab2229562cae1c9c 100644 (file)
@@ -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) {
index dc7e95b64c63ee41f09c182f9fa297dd27f920d2..44dacb733b80e9d5e2ea847a4011b259a6e85017 100644 (file)
@@ -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
index e888b69a84cd4e0479e8414c81b89d78967f3a13..a69d8bc8ee67fa4b54462ceda7911721a7491179 100644 (file)
@@ -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);
index 7abfb5f15485ba2de2df6ae9a161bc1d1ff63f1f..874ce617112b1f5f17b1aedb68c93c3a6b043b5b 100644 (file)
@@ -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
index 02d8ae584dd17925475d8f8ed44acb2e70e7c3d1..d29838564f682465c27e7aa74522c42293bbf223 100644 (file)
@@ -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;
index 3fdc0668e66f1febe3c31dd25a02702c30a1e953..395d8955f0e1f9a7e8a2c1a3d537a2f11b55fe64 100644 (file)
@@ -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 {
index 0ec71abd43d87c03f54ab517b1f7812fa5d51768..9c757d9c8c6f0f95ab196d7fbc21930e0844d563 100644 (file)
@@ -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 */
index ff98acce817830b981b78ee235f481f7376d499c..b0ec75700f98c4e5c02bb46f3ea9086310ec7119 100644 (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.
@@ -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");
index 8584cf0b4d0e6027809367f73299e76b95e00bed..088e9d3e2aeccadd996005012e22dd8543963fcc 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * IEEE 802.1X-2004 Authenticator - EAPOL state machine
- * Copyright (c) 2002-2009, 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.
@@ -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;
 }
 
 
index 320a0adbf148c9a4eb42f5172fec337e245462e8..90194d1f6ce5d25e1dc9c0ed92629eda756f25e7 100644 (file)
@@ -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;
index 00394b49c22003178db19018f4c81d35708e48cf..54b25899353e5ec0d5b3b58f6126584fe796bc47 100644 (file)
@@ -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,
 };