From 881cb4198b552562bc732077170219bb295a64e8 Mon Sep 17 00:00:00 2001 From: Jouni Malinen Date: Thu, 21 Sep 2023 10:46:38 +0300 Subject: [PATCH] EAP-SIM/AKA peer: Simplify identity selection for MK derivation Redesign the identity selection for MK derivation to be done explicitly based on the last indicated identity (whether it is from EAP-Response/Identity or method specific AT_IDENTITY) during the current exchange. This makes the implementation cleaner and avoids cases were more or less duplicated selection steps ended up being slightly different. This is not as clean as it could otherwise be due to the exception needed for the IMSI privacy case where the identity used in MK derivation is actually not the one exchanged in the EAP messages. Furthermore, this moves the somewhat confusing EAP method specific tracking of the lasgt EAP-Response/Identity value from EAP-SIM/AKA into the main EAP peer implementation. Signed-off-by: Jouni Malinen --- src/eap_peer/eap.c | 5 ++ src/eap_peer/eap_aka.c | 104 ++++++++++++++++++++++------------------- src/eap_peer/eap_i.h | 4 ++ src/eap_peer/eap_sim.c | 104 ++++++++++++++++++++++------------------- 4 files changed, 123 insertions(+), 94 deletions(-) diff --git a/src/eap_peer/eap.c b/src/eap_peer/eap.c index d07060213..c8e514ab8 100644 --- a/src/eap_peer/eap.c +++ b/src/eap_peer/eap.c @@ -1759,6 +1759,10 @@ struct wpabuf * eap_sm_buildIdentity(struct eap_sm *sm, int id, int encrypted) wpabuf_put_data(resp, identity, identity_len); wpabuf_free(privacy_identity); + os_free(sm->identity); + sm->identity = os_memdup(identity, identity_len); + sm->identity_len = identity_len; + return resp; } @@ -2262,6 +2266,7 @@ void eap_peer_sm_deinit(struct eap_sm *sm) tls_deinit(sm->ssl_ctx2); tls_deinit(sm->ssl_ctx); eap_peer_erp_free_keys(sm); + os_free(sm->identity); os_free(sm); } diff --git a/src/eap_peer/eap_aka.c b/src/eap_peer/eap_aka.c index 6fe939b5c..72d4ee505 100644 --- a/src/eap_peer/eap_aka.c +++ b/src/eap_peer/eap_aka.c @@ -41,8 +41,8 @@ struct eap_aka_data { size_t reauth_id_len; int reauth; unsigned int counter, counter_too_small; - u8 *last_eap_identity; - size_t last_eap_identity_len; + u8 *mk_identity; + size_t mk_identity_len; enum { CONTINUE, RESULT_SUCCESS, SUCCESS, FAILURE } state; @@ -140,6 +140,13 @@ static void * eap_aka_init(struct eap_sm *sm) } } + if (sm->identity) { + /* Use the EAP-Response/Identity in MK derivation if AT_IDENTITY + * is not used. */ + data->mk_identity = os_memdup(sm->identity, sm->identity_len); + data->mk_identity_len = sm->identity_len; + } + return data; } @@ -177,7 +184,7 @@ static void eap_aka_deinit(struct eap_sm *sm, void *priv) if (data) { os_free(data->pseudonym); os_free(data->reauth_id); - os_free(data->last_eap_identity); + os_free(data->mk_identity); wpabuf_free(data->id_msgs); os_free(data->network_name); eap_aka_clear_keys(data, 0); @@ -373,7 +380,6 @@ static int eap_aka_umts_auth(struct eap_sm *sm, struct eap_aka_data *data) #define CLEAR_PSEUDONYM 0x01 #define CLEAR_REAUTH_ID 0x02 -#define CLEAR_EAP_ID 0x04 static void eap_aka_clear_identities(struct eap_sm *sm, struct eap_aka_data *data, int id) @@ -392,12 +398,6 @@ static void eap_aka_clear_identities(struct eap_sm *sm, data->reauth_id = NULL; data->reauth_id_len = 0; } - if ((id & CLEAR_EAP_ID) && data->last_eap_identity) { - wpa_printf(MSG_DEBUG, "EAP-AKA: forgetting old eap_id"); - os_free(data->last_eap_identity); - data->last_eap_identity = NULL; - data->last_eap_identity_len = 0; - } } @@ -699,6 +699,8 @@ static struct wpabuf * eap_aka_response_identity(struct eap_sm *sm, size_t identity_len = 0; struct eap_sim_msg *msg; struct wpabuf *enc_identity = NULL; + struct eap_peer_config *config = NULL; + bool use_imsi_identity = false; data->reauth = 0; if (id_req == ANY_ID && data->reauth_id) { @@ -722,10 +724,13 @@ static struct wpabuf * eap_aka_response_identity(struct eap_sm *sm, data->pseudonym_len)) ids &= ~CLEAR_PSEUDONYM; eap_aka_clear_identities(sm, data, ids); + + config = eap_get_config(sm); + if (config && config->imsi_identity) + use_imsi_identity = true; } #ifdef CRYPTO_RSA_OAEP_SHA256 if (identity && data->imsi_privacy_key) { - struct eap_peer_config *config; const char *attr = NULL; config = eap_get_config(sm); @@ -741,13 +746,16 @@ static struct wpabuf * eap_aka_response_identity(struct eap_sm *sm, data, id, EAP_AKA_UNABLE_TO_PROCESS_PACKET); } + /* Use the real identity, not the encrypted one, in MK + * derivation. */ + os_free(data->mk_identity); + data->mk_identity = os_memdup(identity, identity_len); + data->mk_identity_len = identity_len; identity = wpabuf_head(enc_identity); identity_len = wpabuf_len(enc_identity); } #endif /* CRYPTO_RSA_OAEP_SHA256 */ } - if (id_req != NO_ID_REQ) - 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, @@ -758,6 +766,22 @@ static struct wpabuf * eap_aka_response_identity(struct eap_sm *sm, identity, identity_len); eap_sim_msg_add(msg, EAP_SIM_AT_IDENTITY, identity_len, identity, identity_len); + if (use_imsi_identity && config && config->imsi_identity) { + /* Use the IMSI identity override, i.e., the not + * encrypted one, in MK derivation, when using + * externally encrypted identity in configuration. */ + os_free(data->mk_identity); + data->mk_identity = os_memdup( + config->imsi_identity, + config->imsi_identity_len); + data->mk_identity_len = config->imsi_identity_len; + } else if (!enc_identity) { + /* Use the last AT_IDENTITY value as the identity in + * MK derivation. */ + os_free(data->mk_identity); + data->mk_identity = os_memdup(identity, identity_len); + data->mk_identity_len = identity_len; + } } wpabuf_free(enc_identity); @@ -1147,28 +1171,9 @@ static struct wpabuf * eap_aka_process_challenge(struct eap_sm *sm, data->network_name_len); } #endif /* EAP_AKA_PRIME */ - if (data->last_eap_identity) { - identity = data->last_eap_identity; - identity_len = data->last_eap_identity_len; - } else if (data->reauth_id) { - identity = data->reauth_id; - identity_len = data->reauth_id_len; - } else if (data->pseudonym && - !eap_sim_anonymous_username(data->pseudonym, - data->pseudonym_len)) { - identity = data->pseudonym; - identity_len = data->pseudonym_len; - } else { - struct eap_peer_config *config; - config = eap_get_config(sm); - if (config && config->imsi_identity) { - identity = config->imsi_identity; - identity_len = config->imsi_identity_len; - } else { - identity = eap_get_config_identity(sm, &identity_len); - } - } + identity = data->mk_identity; + identity_len = data->mk_identity_len; wpa_hexdump_ascii(MSG_DEBUG, "EAP-AKA: Selected identity for MK " "derivation", identity, identity_len); if (data->eap_method == EAP_TYPE_AKA_PRIME) { @@ -1197,7 +1202,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(sm, data, CLEAR_REAUTH_ID | CLEAR_EAP_ID); + eap_aka_clear_identities(sm, data, CLEAR_REAUTH_ID); if (attr->encr_data) { u8 *decrypted; @@ -1407,14 +1412,8 @@ static struct wpabuf * eap_aka_process_reauthentication( /* Reply using Re-auth w/ AT_COUNTER_TOO_SMALL. The current * reauth_id must not be used to start a new reauthentication. - * However, since it was used in the last EAP-Response-Identity - * packet, it has to saved for the following fullauth to be - * used in MK derivation. */ - os_free(data->last_eap_identity); - data->last_eap_identity = data->reauth_id; - data->last_eap_identity_len = data->reauth_id_len; - data->reauth_id = NULL; - data->reauth_id_len = 0; + */ + eap_aka_clear_identities(sm, data, CLEAR_REAUTH_ID); res = eap_aka_response_reauth(data, id, 1, eattr.nonce_s); os_free(decrypted); @@ -1439,7 +1438,7 @@ static struct wpabuf * eap_aka_process_reauthentication( data->nonce_s, data->mk, data->msk, data->emsk); } - eap_aka_clear_identities(sm, data, CLEAR_REAUTH_ID | CLEAR_EAP_ID); + eap_aka_clear_identities(sm, data, CLEAR_REAUTH_ID); eap_aka_learn_ids(sm, data, &eattr); if (data->result_ind && attr->result_ind) @@ -1455,8 +1454,7 @@ 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(sm, data, - CLEAR_REAUTH_ID | CLEAR_EAP_ID); + eap_aka_clear_identities(sm, data, CLEAR_REAUTH_ID); } os_free(decrypted); return eap_aka_response_reauth(data, id, 0, data->nonce_s); @@ -1572,7 +1570,10 @@ static bool 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(sm, data, CLEAR_EAP_ID); + + os_free(data->mk_identity); + data->mk_identity = NULL; + data->mk_identity_len = 0; data->prev_id = -1; wpabuf_free(data->id_msgs); data->id_msgs = NULL; @@ -1585,6 +1586,15 @@ static void eap_aka_deinit_for_reauth(struct eap_sm *sm, void *priv) static void * eap_aka_init_for_reauth(struct eap_sm *sm, void *priv) { struct eap_aka_data *data = priv; + + if (sm->identity) { + /* Use the EAP-Response/Identity in MK derivation if AT_IDENTITY + * is not used. */ + os_free(data->mk_identity); + data->mk_identity = os_memdup(sm->identity, sm->identity_len); + data->mk_identity_len = sm->identity_len; + } + data->num_id_req = 0; data->num_notification = 0; eap_aka_state(data, CONTINUE); diff --git a/src/eap_peer/eap_i.h b/src/eap_peer/eap_i.h index 08dd569d6..21681658d 100644 --- a/src/eap_peer/eap_i.h +++ b/src/eap_peer/eap_i.h @@ -390,6 +390,10 @@ struct eap_sm { unsigned int use_machine_cred:1; struct dl_list erp_keys; /* struct eap_erp_key */ + + /* Identity used in EAP-Response/Identity */ + u8 *identity; + size_t identity_len; }; const u8 * eap_get_config_identity(struct eap_sm *sm, size_t *len); diff --git a/src/eap_peer/eap_sim.c b/src/eap_peer/eap_sim.c index 71da424d2..b5e324023 100644 --- a/src/eap_peer/eap_sim.c +++ b/src/eap_peer/eap_sim.c @@ -43,8 +43,8 @@ struct eap_sim_data { size_t reauth_id_len; int reauth; unsigned int counter, counter_too_small; - u8 *last_eap_identity; - size_t last_eap_identity_len; + u8 *mk_identity; + size_t mk_identity_len; enum { CONTINUE, START_DONE, RESULT_SUCCESS, SUCCESS, FAILURE } state; @@ -158,6 +158,13 @@ static void * eap_sim_init(struct eap_sm *sm) } } + if (sm->identity) { + /* Use the EAP-Response/Identity in MK derivation if AT_IDENTITY + * is not used. */ + data->mk_identity = os_memdup(sm->identity, sm->identity_len); + data->mk_identity_len = sm->identity_len; + } + eap_sim_state(data, CONTINUE); return data; @@ -185,7 +192,7 @@ static void eap_sim_deinit(struct eap_sm *sm, void *priv) os_free(data->ver_list); os_free(data->pseudonym); os_free(data->reauth_id); - os_free(data->last_eap_identity); + os_free(data->mk_identity); eap_sim_clear_keys(data, 0); #ifdef CRYPTO_RSA_OAEP_SHA256 crypto_rsa_key_free(data->imsi_privacy_key); @@ -399,7 +406,6 @@ static int eap_sim_supported_ver(int version) #define CLEAR_PSEUDONYM 0x01 #define CLEAR_REAUTH_ID 0x02 -#define CLEAR_EAP_ID 0x04 static void eap_sim_clear_identities(struct eap_sm *sm, struct eap_sim_data *data, int id) @@ -418,12 +424,6 @@ static void eap_sim_clear_identities(struct eap_sm *sm, data->reauth_id = NULL; data->reauth_id_len = 0; } - if ((id & CLEAR_EAP_ID) && data->last_eap_identity) { - wpa_printf(MSG_DEBUG, "EAP-SIM: forgetting old eap_id"); - os_free(data->last_eap_identity); - data->last_eap_identity = NULL; - data->last_eap_identity_len = 0; - } } @@ -567,6 +567,8 @@ static struct wpabuf * eap_sim_response_start(struct eap_sm *sm, struct eap_sim_msg *msg; struct wpabuf *resp; struct wpabuf *enc_identity = NULL; + struct eap_peer_config *config = NULL; + bool use_imsi_identity = false; data->reauth = 0; if (id_req == ANY_ID && data->reauth_id) { @@ -590,10 +592,13 @@ static struct wpabuf * eap_sim_response_start(struct eap_sm *sm, data->pseudonym_len)) ids &= ~CLEAR_PSEUDONYM; eap_sim_clear_identities(sm, data, ids); + + config = eap_get_config(sm); + if (config && config->imsi_identity) + use_imsi_identity = true; } #ifdef CRYPTO_RSA_OAEP_SHA256 if (identity && data->imsi_privacy_key) { - struct eap_peer_config *config; const char *attr = NULL; config = eap_get_config(sm); @@ -609,13 +614,16 @@ static struct wpabuf * eap_sim_response_start(struct eap_sm *sm, data, id, EAP_SIM_UNABLE_TO_PROCESS_PACKET); } + /* Use the real identity, not the encrypted one, in MK + * derivation. */ + os_free(data->mk_identity); + data->mk_identity = os_memdup(identity, identity_len); + data->mk_identity_len = identity_len; identity = wpabuf_head(enc_identity); identity_len = wpabuf_len(enc_identity); } #endif /* CRYPTO_RSA_OAEP_SHA256 */ } - if (id_req != NO_ID_REQ) - 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, @@ -625,6 +633,22 @@ static struct wpabuf * eap_sim_response_start(struct eap_sm *sm, identity, identity_len); eap_sim_msg_add(msg, EAP_SIM_AT_IDENTITY, identity_len, identity, identity_len); + if (use_imsi_identity && config && config->imsi_identity) { + /* Use the IMSI identity override, i.e., the not + * encrypted one, in MK derivation, when using + * externally encrypted identity in configuration. */ + os_free(data->mk_identity); + data->mk_identity = os_memdup( + config->imsi_identity, + config->imsi_identity_len); + data->mk_identity_len = config->imsi_identity_len; + } else if (!enc_identity) { + /* Use the last AT_IDENTITY value as the identity in + * MK derivation. */ + os_free(data->mk_identity); + data->mk_identity = os_memdup(identity, identity_len); + data->mk_identity_len = identity_len; + } } wpabuf_free(enc_identity); if (!data->reauth) { @@ -886,28 +910,9 @@ static struct wpabuf * eap_sim_process_challenge(struct eap_sm *sm, return eap_sim_client_error(data, id, EAP_SIM_UNABLE_TO_PROCESS_PACKET); } - if (data->last_eap_identity) { - identity = data->last_eap_identity; - identity_len = data->last_eap_identity_len; - } else if (data->reauth_id) { - identity = data->reauth_id; - identity_len = data->reauth_id_len; - } else if (data->pseudonym && - !eap_sim_anonymous_username(data->pseudonym, - data->pseudonym_len)) { - identity = data->pseudonym; - identity_len = data->pseudonym_len; - } else { - struct eap_peer_config *config; - config = eap_get_config(sm); - if (config && config->imsi_identity) { - identity = config->imsi_identity; - identity_len = config->imsi_identity_len; - } else { - identity = eap_get_config_identity(sm, &identity_len); - } - } + identity = data->mk_identity; + identity_len = data->mk_identity_len; wpa_hexdump_ascii(MSG_DEBUG, "EAP-SIM: Selected identity for MK " "derivation", identity, identity_len); eap_sim_derive_mk(identity, identity_len, data->nonce_mt, @@ -933,7 +938,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(sm, data, CLEAR_REAUTH_ID | CLEAR_EAP_ID); + eap_sim_clear_identities(sm, data, CLEAR_REAUTH_ID); if (attr->encr_data) { u8 *decrypted; @@ -1143,14 +1148,8 @@ static struct wpabuf * eap_sim_process_reauthentication( /* Reply using Re-auth w/ AT_COUNTER_TOO_SMALL. The current * reauth_id must not be used to start a new reauthentication. - * However, since it was used in the last EAP-Response-Identity - * packet, it has to saved for the following fullauth to be - * used in MK derivation. */ - os_free(data->last_eap_identity); - data->last_eap_identity = data->reauth_id; - data->last_eap_identity_len = data->reauth_id_len; - data->reauth_id = NULL; - data->reauth_id_len = 0; + */ + eap_sim_clear_identities(sm, data, CLEAR_REAUTH_ID); res = eap_sim_response_reauth(data, id, 1, eattr.nonce_s); os_free(decrypted); @@ -1167,7 +1166,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(sm, data, CLEAR_REAUTH_ID | CLEAR_EAP_ID); + eap_sim_clear_identities(sm, data, CLEAR_REAUTH_ID); eap_sim_learn_ids(sm, data, &eattr); if (data->result_ind && attr->result_ind) @@ -1183,8 +1182,7 @@ 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(sm, data, - CLEAR_REAUTH_ID | CLEAR_EAP_ID); + eap_sim_clear_identities(sm, data, CLEAR_REAUTH_ID); } os_free(decrypted); return eap_sim_response_reauth(data, id, 0, data->nonce_s); @@ -1293,7 +1291,10 @@ static bool 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(sm, data, CLEAR_EAP_ID); + + os_free(data->mk_identity); + data->mk_identity = NULL; + data->mk_identity_len = 0; data->use_result_ind = 0; eap_sim_clear_keys(data, 1); } @@ -1308,6 +1309,15 @@ static void * eap_sim_init_for_reauth(struct eap_sm *sm, void *priv) eap_sim_deinit(sm, data); return NULL; } + + if (sm->identity) { + /* Use the EAP-Response/Identity in MK derivation if AT_IDENTITY + * is not used. */ + os_free(data->mk_identity); + data->mk_identity = os_memdup(sm->identity, sm->identity_len); + data->mk_identity_len = sm->identity_len; + } + data->num_id_req = 0; data->num_notification = 0; eap_sim_state(data, CONTINUE);