From e026159a8e8e38feb0ec455bf7ae09146033016f Mon Sep 17 00:00:00 2001 From: Jouni Malinen Date: Sun, 2 Sep 2012 13:04:18 +0300 Subject: [PATCH] EAP-SIM/AKA: Store pseudonym identity in configuration Use the anonymous_identity field to store EAP-SIM/AKA pseudonym identity so that this can be maintained between EAP sessions (e.g., after wpa_supplicant restart) even if fast re-authentication data was cleared. Signed-hostap: Jouni Malinen --- src/eap_peer/eap.c | 13 ++++++++++ src/eap_peer/eap.h | 11 +++++++- src/eap_peer/eap_aka.c | 30 ++++++++++++++++------ src/eap_peer/eap_config.h | 3 +++ src/eap_peer/eap_sim.c | 31 +++++++++++++++------- src/eapol_supp/eapol_supp_sm.c | 12 ++++++++- src/eapol_supp/eapol_supp_sm.h | 8 ++++++ wpa_supplicant/eapol_test.c | 32 +++++++++++++++++++++++ wpa_supplicant/wpa_supplicant.conf | 3 ++- wpa_supplicant/wpas_glue.c | 41 +++++++++++++++++++++++++++++- 10 files changed, 163 insertions(+), 21 deletions(-) diff --git a/src/eap_peer/eap.c b/src/eap_peer/eap.c index ba973a52a..2ed74b8f0 100644 --- a/src/eap_peer/eap.c +++ b/src/eap_peer/eap.c @@ -2321,3 +2321,16 @@ void eap_sm_set_ext_pw_ctx(struct eap_sm *sm, struct ext_password_data *ext) sm->ext_pw_buf = NULL; sm->ext_pw = ext; } + + +/** + * eap_set_anon_id - Set or add anonymous identity + * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() + * @id: Anonymous identity (e.g., EAP-SIM pseudonym) or %NULL to clear + * @len: Length of anonymous identity in octets + */ +void eap_set_anon_id(struct eap_sm *sm, const u8 *id, size_t len) +{ + if (sm->eapol_cb->set_anon_id) + sm->eapol_cb->set_anon_id(sm->eapol_ctx, id, len); +} diff --git a/src/eap_peer/eap.h b/src/eap_peer/eap.h index cf58608c7..8bccef1b1 100644 --- a/src/eap_peer/eap.h +++ b/src/eap_peer/eap.h @@ -1,6 +1,6 @@ /* * EAP peer state machine functions (RFC 4137) - * Copyright (c) 2004-2007, Jouni Malinen + * Copyright (c) 2004-2012, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -235,6 +235,14 @@ struct eapol_callbacks { */ void (*notify_status)(void *ctx, const char *status, const char *parameter); + + /** + * set_anon_id - Set or add anonymous identity + * @ctx: eapol_ctx from eap_peer_sm_init() call + * @id: Anonymous identity (e.g., EAP-SIM pseudonym) or %NULL to clear + * @len: Length of anonymous identity in octets + */ + void (*set_anon_id)(void *ctx, const u8 *id, size_t len); }; /** @@ -308,6 +316,7 @@ int eap_is_wps_pin_enrollee(struct eap_peer_config *conf); struct ext_password_data; void eap_sm_set_ext_pw_ctx(struct eap_sm *sm, struct ext_password_data *ext); +void eap_set_anon_id(struct eap_sm *sm, const u8 *id, size_t len); #endif /* IEEE8021X_EAPOL */ diff --git a/src/eap_peer/eap_aka.c b/src/eap_peer/eap_aka.c index 108027cf7..59861cba1 100644 --- a/src/eap_peer/eap_aka.c +++ b/src/eap_peer/eap_aka.c @@ -90,6 +90,7 @@ static void * eap_aka_init(struct eap_sm *sm) { struct eap_aka_data *data; const char *phase1 = eap_get_config_phase1(sm); + struct eap_peer_config *config = eap_get_config(sm); data = os_zalloc(sizeof(*data)); if (data == NULL) @@ -102,6 +103,15 @@ static void * eap_aka_init(struct eap_sm *sm) data->result_ind = phase1 && os_strstr(phase1, "result_ind=1") != NULL; + if (config && config->anonymous_identity) { + data->pseudonym = os_malloc(config->anonymous_identity_len); + if (data->pseudonym) { + os_memcpy(data->pseudonym, config->anonymous_identity, + config->anonymous_identity_len); + data->pseudonym_len = config->anonymous_identity_len; + } + } + return data; } @@ -227,13 +237,15 @@ static int eap_aka_umts_auth(struct eap_sm *sm, struct eap_aka_data *data) #define CLEAR_REAUTH_ID 0x02 #define CLEAR_EAP_ID 0x04 -static void eap_aka_clear_identities(struct eap_aka_data *data, int id) +static void eap_aka_clear_identities(struct eap_sm *sm, + struct eap_aka_data *data, int id) { if ((id & CLEAR_PSEUDONYM) && data->pseudonym) { wpa_printf(MSG_DEBUG, "EAP-AKA: forgetting old pseudonym"); os_free(data->pseudonym); data->pseudonym = NULL; data->pseudonym_len = 0; + eap_set_anon_id(sm, NULL, 0); } if ((id & CLEAR_REAUTH_ID) && data->reauth_id) { wpa_printf(MSG_DEBUG, "EAP-AKA: forgetting old reauth_id"); @@ -288,6 +300,7 @@ static int eap_aka_learn_ids(struct eap_sm *sm, struct eap_aka_data *data, realm, realm_len); } data->pseudonym_len = attr->next_pseudonym_len + realm_len; + eap_set_anon_id(sm, data->pseudonym, data->pseudonym_len); } if (attr->next_reauth_id) { @@ -488,16 +501,16 @@ static struct wpabuf * eap_aka_response_identity(struct eap_sm *sm, data->pseudonym) { identity = data->pseudonym; identity_len = data->pseudonym_len; - eap_aka_clear_identities(data, CLEAR_REAUTH_ID); + eap_aka_clear_identities(sm, data, CLEAR_REAUTH_ID); } else if (id_req != NO_ID_REQ) { identity = eap_get_config_identity(sm, &identity_len); if (identity) { - eap_aka_clear_identities(data, CLEAR_PSEUDONYM | + eap_aka_clear_identities(sm, data, CLEAR_PSEUDONYM | CLEAR_REAUTH_ID); } } if (id_req != NO_ID_REQ) - eap_aka_clear_identities(data, CLEAR_EAP_ID); + eap_aka_clear_identities(sm, data, CLEAR_EAP_ID); wpa_printf(MSG_DEBUG, "Generating EAP-AKA Identity (id=%d)", id); msg = eap_sim_msg_init(EAP_CODE_RESPONSE, id, data->eap_method, @@ -900,7 +913,7 @@ static struct wpabuf * eap_aka_process_challenge(struct eap_sm *sm, * other words, if no new identities are received, full * authentication will be used on next reauthentication (using * pseudonym identity or permanent identity). */ - eap_aka_clear_identities(data, CLEAR_REAUTH_ID | CLEAR_EAP_ID); + eap_aka_clear_identities(sm, data, CLEAR_REAUTH_ID | CLEAR_EAP_ID); if (attr->encr_data) { u8 *decrypted; @@ -1128,7 +1141,7 @@ static struct wpabuf * eap_aka_process_reauthentication( data->nonce_s, data->mk, data->msk, data->emsk); } - eap_aka_clear_identities(data, CLEAR_REAUTH_ID | CLEAR_EAP_ID); + eap_aka_clear_identities(sm, data, CLEAR_REAUTH_ID | CLEAR_EAP_ID); eap_aka_learn_ids(sm, data, &eattr); if (data->result_ind && attr->result_ind) @@ -1144,7 +1157,8 @@ static struct wpabuf * eap_aka_process_reauthentication( if (data->counter > EAP_AKA_MAX_FAST_REAUTHS) { wpa_printf(MSG_DEBUG, "EAP-AKA: Maximum number of " "fast reauths performed - force fullauth"); - eap_aka_clear_identities(data, CLEAR_REAUTH_ID | CLEAR_EAP_ID); + eap_aka_clear_identities(sm, data, + CLEAR_REAUTH_ID | CLEAR_EAP_ID); } os_free(decrypted); return eap_aka_response_reauth(data, id, 0, data->nonce_s); @@ -1262,7 +1276,7 @@ static Boolean eap_aka_has_reauth_data(struct eap_sm *sm, void *priv) static void eap_aka_deinit_for_reauth(struct eap_sm *sm, void *priv) { struct eap_aka_data *data = priv; - eap_aka_clear_identities(data, CLEAR_EAP_ID); + eap_aka_clear_identities(sm, data, CLEAR_EAP_ID); data->prev_id = -1; wpabuf_free(data->id_msgs); data->id_msgs = NULL; diff --git a/src/eap_peer/eap_config.h b/src/eap_peer/eap_config.h index a08543e11..ed909198b 100644 --- a/src/eap_peer/eap_config.h +++ b/src/eap_peer/eap_config.h @@ -35,6 +35,9 @@ struct eap_peer_config { * * If not set, the identity field will be used for both unencrypted and * protected fields. + * + * This field can also be used with EAP-SIM/AKA/AKA' to store the + * pseudonym identity. */ u8 *anonymous_identity; diff --git a/src/eap_peer/eap_sim.c b/src/eap_peer/eap_sim.c index 0159e1747..c936a4475 100644 --- a/src/eap_peer/eap_sim.c +++ b/src/eap_peer/eap_sim.c @@ -1,6 +1,6 @@ /* * EAP peer method: EAP-SIM (RFC 4186) - * Copyright (c) 2004-2008, Jouni Malinen + * Copyright (c) 2004-2012, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -117,6 +117,15 @@ static void * eap_sim_init(struct eap_sm *sm) NULL; } + if (config && config->anonymous_identity) { + data->pseudonym = os_malloc(config->anonymous_identity_len); + if (data->pseudonym) { + os_memcpy(data->pseudonym, config->anonymous_identity, + config->anonymous_identity_len); + data->pseudonym_len = config->anonymous_identity_len; + } + } + eap_sim_state(data, CONTINUE); return data; @@ -258,13 +267,15 @@ static int eap_sim_supported_ver(int version) #define CLEAR_REAUTH_ID 0x02 #define CLEAR_EAP_ID 0x04 -static void eap_sim_clear_identities(struct eap_sim_data *data, int id) +static void eap_sim_clear_identities(struct eap_sm *sm, + struct eap_sim_data *data, int id) { if ((id & CLEAR_PSEUDONYM) && data->pseudonym) { wpa_printf(MSG_DEBUG, "EAP-SIM: forgetting old pseudonym"); os_free(data->pseudonym); data->pseudonym = NULL; data->pseudonym_len = 0; + eap_set_anon_id(sm, NULL, 0); } if ((id & CLEAR_REAUTH_ID) && data->reauth_id) { wpa_printf(MSG_DEBUG, "EAP-SIM: forgetting old reauth_id"); @@ -319,6 +330,7 @@ static int eap_sim_learn_ids(struct eap_sm *sm, struct eap_sim_data *data, realm, realm_len); } data->pseudonym_len = attr->next_pseudonym_len + realm_len; + eap_set_anon_id(sm, data->pseudonym, data->pseudonym_len); } if (attr->next_reauth_id) { @@ -378,16 +390,16 @@ static struct wpabuf * eap_sim_response_start(struct eap_sm *sm, data->pseudonym) { identity = data->pseudonym; identity_len = data->pseudonym_len; - eap_sim_clear_identities(data, CLEAR_REAUTH_ID); + eap_sim_clear_identities(sm, data, CLEAR_REAUTH_ID); } else if (id_req != NO_ID_REQ) { identity = eap_get_config_identity(sm, &identity_len); if (identity) { - eap_sim_clear_identities(data, CLEAR_PSEUDONYM | + eap_sim_clear_identities(sm, data, CLEAR_PSEUDONYM | CLEAR_REAUTH_ID); } } if (id_req != NO_ID_REQ) - eap_sim_clear_identities(data, CLEAR_EAP_ID); + eap_sim_clear_identities(sm, data, CLEAR_EAP_ID); wpa_printf(MSG_DEBUG, "Generating EAP-SIM Start (id=%d)", id); msg = eap_sim_msg_init(EAP_CODE_RESPONSE, id, @@ -670,7 +682,7 @@ static struct wpabuf * eap_sim_process_challenge(struct eap_sm *sm, * other words, if no new reauth identity is received, full * authentication will be used on next reauthentication (using * pseudonym identity or permanent identity). */ - eap_sim_clear_identities(data, CLEAR_REAUTH_ID | CLEAR_EAP_ID); + eap_sim_clear_identities(sm, data, CLEAR_REAUTH_ID | CLEAR_EAP_ID); if (attr->encr_data) { u8 *decrypted; @@ -878,7 +890,7 @@ static struct wpabuf * eap_sim_process_reauthentication( data->reauth_id, data->reauth_id_len, data->nonce_s, data->mk, data->msk, data->emsk); - eap_sim_clear_identities(data, CLEAR_REAUTH_ID | CLEAR_EAP_ID); + eap_sim_clear_identities(sm, data, CLEAR_REAUTH_ID | CLEAR_EAP_ID); eap_sim_learn_ids(sm, data, &eattr); if (data->result_ind && attr->result_ind) @@ -894,7 +906,8 @@ static struct wpabuf * eap_sim_process_reauthentication( if (data->counter > EAP_SIM_MAX_FAST_REAUTHS) { wpa_printf(MSG_DEBUG, "EAP-SIM: Maximum number of " "fast reauths performed - force fullauth"); - eap_sim_clear_identities(data, CLEAR_REAUTH_ID | CLEAR_EAP_ID); + eap_sim_clear_identities(sm, data, + CLEAR_REAUTH_ID | CLEAR_EAP_ID); } os_free(decrypted); return eap_sim_response_reauth(data, id, 0, data->nonce_s); @@ -1005,7 +1018,7 @@ static Boolean eap_sim_has_reauth_data(struct eap_sm *sm, void *priv) static void eap_sim_deinit_for_reauth(struct eap_sm *sm, void *priv) { struct eap_sim_data *data = priv; - eap_sim_clear_identities(data, CLEAR_EAP_ID); + eap_sim_clear_identities(sm, data, CLEAR_EAP_ID); data->use_result_ind = 0; } diff --git a/src/eapol_supp/eapol_supp_sm.c b/src/eapol_supp/eapol_supp_sm.c index e3bfa38a6..851cf49bf 100644 --- a/src/eapol_supp/eapol_supp_sm.c +++ b/src/eapol_supp/eapol_supp_sm.c @@ -1825,6 +1825,15 @@ static void eapol_sm_notify_status(void *ctx, const char *status, } +static void eapol_sm_set_anon_id(void *ctx, const u8 *id, size_t len) +{ + struct eapol_sm *sm = ctx; + + if (sm->ctx->set_anon_id) + sm->ctx->set_anon_id(sm->ctx->ctx, id, len); +} + + static struct eapol_callbacks eapol_cb = { eapol_sm_get_config, @@ -1838,7 +1847,8 @@ static struct eapol_callbacks eapol_cb = eapol_sm_notify_pending, eapol_sm_eap_param_needed, eapol_sm_notify_cert, - eapol_sm_notify_status + eapol_sm_notify_status, + eapol_sm_set_anon_id }; diff --git a/src/eapol_supp/eapol_supp_sm.h b/src/eapol_supp/eapol_supp_sm.h index d2a4b94a9..c4b87da1f 100644 --- a/src/eapol_supp/eapol_supp_sm.h +++ b/src/eapol_supp/eapol_supp_sm.h @@ -239,6 +239,14 @@ struct eapol_ctx { */ void (*status_cb)(void *ctx, const char *status, const char *parameter); + + /** + * set_anon_id - Set or add anonymous identity + * @ctx: eapol_ctx from eap_peer_sm_init() call + * @id: Anonymous identity (e.g., EAP-SIM pseudonym) + * @len: Length of anonymous identity in octets + */ + void (*set_anon_id)(void *ctx, const u8 *id, size_t len); }; diff --git a/wpa_supplicant/eapol_test.c b/wpa_supplicant/eapol_test.c index 7d63c1b42..03b8c7e4a 100644 --- a/wpa_supplicant/eapol_test.c +++ b/wpa_supplicant/eapol_test.c @@ -429,6 +429,37 @@ static void eapol_test_cert_cb(void *ctx, int depth, const char *subject, } +static void eapol_test_set_anon_id(void *ctx, const u8 *id, size_t len) +{ + struct eapol_test_data *e = ctx; + struct wpa_supplicant *wpa_s = e->wpa_s; + char *str; + int res; + + wpa_hexdump_ascii(MSG_DEBUG, "EAP method updated anonymous_identity", + id, len); + + if (wpa_s->current_ssid == NULL) + return; + + if (id == NULL) { + if (wpa_config_set(wpa_s->current_ssid, "anonymous_identity", + "NULL", 0) < 0) + return; + } else { + str = os_malloc(len * 2 + 1); + if (str == NULL) + return; + wpa_snprintf_hex(str, len * 2 + 1, id, len); + res = wpa_config_set(wpa_s->current_ssid, "anonymous_identity", + str, 0); + os_free(str); + if (res < 0) + return; + } +} + + static int test_eapol(struct eapol_test_data *e, struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid) { @@ -456,6 +487,7 @@ static int test_eapol(struct eapol_test_data *e, struct wpa_supplicant *wpa_s, ctx->pkcs11_module_path = wpa_s->conf->pkcs11_module_path; ctx->cert_cb = eapol_test_cert_cb; ctx->cert_in_cb = 1; + ctx->set_anon_id = eapol_test_set_anon_id; wpa_s->eapol = eapol_sm_init(ctx); if (wpa_s->eapol == NULL) { diff --git a/wpa_supplicant/wpa_supplicant.conf b/wpa_supplicant/wpa_supplicant.conf index 2c07fd0a8..0b0ea88d7 100644 --- a/wpa_supplicant/wpa_supplicant.conf +++ b/wpa_supplicant/wpa_supplicant.conf @@ -586,7 +586,8 @@ fast_reauth=1 # EAP-PSK/PAX/SAKE/GPSK. # anonymous_identity: Anonymous identity string for EAP (to be used as the # unencrypted identity with EAP types that support different tunnelled -# identity, e.g., EAP-TTLS) +# identity, e.g., EAP-TTLS). This field can also be used with +# EAP-SIM/AKA/AKA' to store the pseudonym identity. # password: Password string for EAP. This field can include either the # plaintext password (using ASCII or hex string) or a NtPasswordHash # (16-byte MD4 hash of password) in hash:<32 hex digits> format. diff --git a/wpa_supplicant/wpas_glue.c b/wpa_supplicant/wpas_glue.c index fb4fa2244..6aa52053e 100644 --- a/wpa_supplicant/wpas_glue.c +++ b/wpa_supplicant/wpas_glue.c @@ -1,6 +1,6 @@ /* * WPA Supplicant - Glue code to setup EAPOL and RSN modules - * Copyright (c) 2003-2008, Jouni Malinen + * Copyright (c) 2003-2012, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -729,6 +729,44 @@ static void wpa_supplicant_status_cb(void *ctx, const char *status, wpas_notify_eap_status(wpa_s, status, parameter); } + + +static void wpa_supplicant_set_anon_id(void *ctx, const u8 *id, size_t len) +{ + struct wpa_supplicant *wpa_s = ctx; + char *str; + int res; + + wpa_hexdump_ascii(MSG_DEBUG, "EAP method updated anonymous_identity", + id, len); + + if (wpa_s->current_ssid == NULL) + return; + + if (id == NULL) { + if (wpa_config_set(wpa_s->current_ssid, "anonymous_identity", + "NULL", 0) < 0) + return; + } else { + str = os_malloc(len * 2 + 1); + if (str == NULL) + return; + wpa_snprintf_hex(str, len * 2 + 1, id, len); + res = wpa_config_set(wpa_s->current_ssid, "anonymous_identity", + str, 0); + os_free(str); + if (res < 0) + return; + } + + if (wpa_s->conf->update_config) { + res = wpa_config_write(wpa_s->confname, wpa_s->conf); + if (res) { + wpa_printf(MSG_DEBUG, "Failed to update config after " + "anonymous_id update"); + } + } +} #endif /* IEEE8021X_EAPOL */ @@ -761,6 +799,7 @@ int wpa_supplicant_init_eapol(struct wpa_supplicant *wpa_s) ctx->cb = wpa_supplicant_eapol_cb; ctx->cert_cb = wpa_supplicant_cert_cb; ctx->status_cb = wpa_supplicant_status_cb; + ctx->set_anon_id = wpa_supplicant_set_anon_id; ctx->cb_ctx = wpa_s; wpa_s->eapol = eapol_sm_init(ctx); if (wpa_s->eapol == NULL) {