diff --git a/hostapd/config_file.c b/hostapd/config_file.c index 97eea8eda..8a86ce08b 100644 --- a/hostapd/config_file.c +++ b/hostapd/config_file.c @@ -2589,6 +2589,9 @@ static int hostapd_config_fill(struct hostapd_config *conf, bss->eap_sim_aka_result_ind = atoi(pos); } else if (os_strcmp(buf, "eap_sim_id") == 0) { bss->eap_sim_id = atoi(pos); + } else if (os_strcmp(buf, "imsi_privacy_key") == 0) { + os_free(bss->imsi_privacy_key); + bss->imsi_privacy_key = os_strdup(pos); #endif /* EAP_SERVER_SIM */ #ifdef EAP_SERVER_TNC } else if (os_strcmp(buf, "tnc") == 0) { diff --git a/hostapd/hostapd.conf b/hostapd/hostapd.conf index 6ccb61115..f37d5634b 100644 --- a/hostapd/hostapd.conf +++ b/hostapd/hostapd.conf @@ -1423,6 +1423,10 @@ eap_server=0 # 3 = use pseudonyms and use fast reauthentication (default) #eap_sim_id=3 +# IMSI privacy key (PEM encoded RSA 2048-bit private key) for decrypting +# permanent identity when using EAP-SIM/AKA/AKA'. +#imsi_privacy_key=imsi-privacy-key.pem + # Trusted Network Connect (TNC) # If enabled, TNC validation will be required before the peer is allowed to # connect. Note: This is only used with EAP-TTLS and EAP-FAST. If any other diff --git a/src/ap/ap_config.c b/src/ap/ap_config.c index ac0617e3f..e208788d1 100644 --- a/src/ap/ap_config.c +++ b/src/ap/ap_config.c @@ -809,6 +809,7 @@ void hostapd_config_free_bss(struct hostapd_bss_config *conf) os_free(conf->eap_fast_a_id); os_free(conf->eap_fast_a_id_info); os_free(conf->eap_sim_db); + os_free(conf->imsi_privacy_key); os_free(conf->radius_server_clients); os_free(conf->radius); os_free(conf->radius_das_shared_secret); diff --git a/src/ap/ap_config.h b/src/ap/ap_config.h index 5b7e574a5..274b6f309 100644 --- a/src/ap/ap_config.h +++ b/src/ap/ap_config.h @@ -443,6 +443,7 @@ struct hostapd_bss_config { int eap_teap_id; int eap_sim_aka_result_ind; int eap_sim_id; + char *imsi_privacy_key; int tnc; int fragment_size; u16 pwd_group; diff --git a/src/ap/authsrv.c b/src/ap/authsrv.c index 35df59803..516c1da74 100644 --- a/src/ap/authsrv.c +++ b/src/ap/authsrv.c @@ -9,6 +9,7 @@ #include "utils/includes.h" #include "utils/common.h" +#include "crypto/crypto.h" #include "crypto/tls.h" #include "eap_server/eap.h" #include "eap_server/eap_sim_db.h" @@ -209,6 +210,7 @@ static struct eap_config * authsrv_eap_config(struct hostapd_data *hapd) cfg->eap_teap_id = hapd->conf->eap_teap_id; cfg->eap_sim_aka_result_ind = hapd->conf->eap_sim_aka_result_ind; cfg->eap_sim_id = hapd->conf->eap_sim_id; + cfg->imsi_privacy_key = hapd->imsi_privacy_key; cfg->tnc = hapd->conf->tnc; cfg->wps = hapd->wps; cfg->fragment_size = hapd->conf->fragment_size; @@ -295,6 +297,22 @@ int authsrv_init(struct hostapd_data *hapd) } #endif /* EAP_TLS_FUNCS */ +#ifdef CRYPTO_RSA_OAEP_SHA256 + crypto_rsa_key_free(hapd->imsi_privacy_key); + hapd->imsi_privacy_key = NULL; + if (hapd->conf->imsi_privacy_key) { + hapd->imsi_privacy_key = crypto_rsa_key_read( + hapd->conf->imsi_privacy_key, true); + if (!hapd->imsi_privacy_key) { + wpa_printf(MSG_ERROR, + "Failed to read/parse IMSI privacy key %s", + hapd->conf->imsi_privacy_key); + authsrv_deinit(hapd); + return -1; + } + } +#endif /* CRYPTO_RSA_OAEP_SHA256 */ + #ifdef EAP_SIM_DB if (hapd->conf->eap_sim_db) { hapd->eap_sim_db_priv = @@ -335,6 +353,11 @@ void authsrv_deinit(struct hostapd_data *hapd) hapd->radius_srv = NULL; #endif /* RADIUS_SERVER */ +#ifdef CRYPTO_RSA_OAEP_SHA256 + crypto_rsa_key_free(hapd->imsi_privacy_key); + hapd->imsi_privacy_key = NULL; +#endif /* CRYPTO_RSA_OAEP_SHA256 */ + #ifdef EAP_TLS_FUNCS if (hapd->ssl_ctx) { tls_deinit(hapd->ssl_ctx); diff --git a/src/ap/hostapd.h b/src/ap/hostapd.h index a1ac45f89..297faaf14 100644 --- a/src/ap/hostapd.h +++ b/src/ap/hostapd.h @@ -208,6 +208,7 @@ struct hostapd_data { void *ssl_ctx; void *eap_sim_db_priv; + struct crypto_rsa_key *imsi_privacy_key; struct radius_server_data *radius_srv; struct dl_list erp_keys; /* struct eap_server_erp_key */ diff --git a/src/eap_server/eap.h b/src/eap_server/eap.h index f1d3a9c99..2894cfbfe 100644 --- a/src/eap_server/eap.h +++ b/src/eap_server/eap.h @@ -124,6 +124,9 @@ struct eap_config { * callback context. */ void *eap_sim_db_priv; + + struct crypto_rsa_key *imsi_privacy_key; + bool backend_auth; int eap_server; diff --git a/src/eap_server/eap_server_aka.c b/src/eap_server/eap_server_aka.c index e9bf0300c..5fb19e976 100644 --- a/src/eap_server/eap_server_aka.c +++ b/src/eap_server/eap_server_aka.c @@ -9,6 +9,7 @@ #include "includes.h" #include "common.h" +#include "utils/base64.h" #include "crypto/sha256.h" #include "crypto/crypto.h" #include "crypto/random.h" @@ -737,11 +738,8 @@ static void eap_aka_determine_identity(struct eap_sm *sm, sm->identity, sm->identity_len); username = sim_get_username(sm->identity, sm->identity_len); - if (username == NULL) { - data->notification = EAP_SIM_GENERAL_FAILURE_BEFORE_AUTH; - eap_aka_state(data, NOTIFICATION); - return; - } + if (!username) + goto fail; if (eap_aka_check_identity_reauth(sm, data, username) > 0) { os_free(username); @@ -785,16 +783,87 @@ static void eap_aka_determine_identity(struct eap_sm *sm, username); os_strlcpy(data->permanent, username, sizeof(data->permanent)); os_free(username); +#ifdef CRYPTO_RSA_OAEP_SHA256 + } else if (sm->identity_len > 1 && sm->identity[0] == '\0') { + char *enc_id, *pos, *end; + size_t enc_id_len; + u8 *decoded_id; + size_t decoded_id_len; + struct wpabuf *enc, *dec; + u8 *new_id; + + os_free(username); + if (!sm->cfg->imsi_privacy_key) { + wpa_printf(MSG_DEBUG, + "EAP-AKA: Received encrypted identity, but no IMSI privacy key configured to decrypt it"); + goto fail; + } + + enc_id = (char *) &sm->identity[1]; + end = (char *) &sm->identity[sm->identity_len]; + for (pos = enc_id; pos < end; pos++) { + if (*pos == ',') + break; + } + enc_id_len = pos - enc_id; + + wpa_hexdump_ascii(MSG_DEBUG, + "EAP-AKA: Encrypted permanent identity", + enc_id, enc_id_len); + decoded_id = base64_decode(enc_id, enc_id_len, &decoded_id_len); + if (!decoded_id) { + wpa_printf(MSG_DEBUG, + "EAP-AKA: Could not base64 decode encrypted identity"); + goto fail; + } + wpa_hexdump(MSG_DEBUG, + "EAP-AKA: Decoded encrypted permanent identity", + decoded_id, decoded_id_len); + enc = wpabuf_alloc_copy(decoded_id, decoded_id_len); + os_free(decoded_id); + if (!enc) + goto fail; + dec = crypto_rsa_oaep_sha256_decrypt(sm->cfg->imsi_privacy_key, + enc); + wpabuf_free(enc); + if (!dec) { + wpa_printf(MSG_DEBUG, + "EAP-AKA: Failed to decrypt encrypted identity"); + goto fail; + } + wpa_hexdump_ascii(MSG_DEBUG, "EAP-AKA: Decrypted permanent identity", + wpabuf_head(dec), wpabuf_len(dec)); + username = sim_get_username(wpabuf_head(dec), wpabuf_len(dec)); + if (!username) { + wpabuf_free(dec); + goto fail; + } + new_id = os_memdup(wpabuf_head(dec), wpabuf_len(dec)); + if (!new_id) { + wpabuf_free(dec); + goto fail; + } + os_free(sm->identity); + sm->identity = new_id; + sm->identity_len = wpabuf_len(dec); + wpabuf_free(dec); + os_strlcpy(data->permanent, username, sizeof(data->permanent)); + os_free(username); +#endif /* CRYPTO_RSA_OAEP_SHA256 */ } else { wpa_printf(MSG_DEBUG, "EAP-AKA: Unrecognized username '%s'", username); os_free(username); - data->notification = EAP_SIM_GENERAL_FAILURE_BEFORE_AUTH; - eap_aka_state(data, NOTIFICATION); + goto fail; return; } eap_aka_fullauth(sm, data); + return; + +fail: + data->notification = EAP_SIM_GENERAL_FAILURE_BEFORE_AUTH; + eap_aka_state(data, NOTIFICATION); } diff --git a/src/eap_server/eap_server_sim.c b/src/eap_server/eap_server_sim.c index 8a6828962..1bcf26c46 100644 --- a/src/eap_server/eap_server_sim.c +++ b/src/eap_server/eap_server_sim.c @@ -9,6 +9,8 @@ #include "includes.h" #include "common.h" +#include "utils/base64.h" +#include "crypto/crypto.h" #include "crypto/random.h" #include "eap_server/eap_i.h" #include "eap_common/eap_sim_common.h" @@ -512,6 +514,73 @@ static void eap_sim_process_start(struct eap_sm *sm, username); os_strlcpy(data->permanent, username, sizeof(data->permanent)); os_free(username); +#ifdef CRYPTO_RSA_OAEP_SHA256 + } else if (sm->identity_len > 1 && sm->identity[0] == '\0') { + char *enc_id, *pos, *end; + size_t enc_id_len; + u8 *decoded_id; + size_t decoded_id_len; + struct wpabuf *enc, *dec; + u8 *new_id; + + os_free(username); + if (!sm->cfg->imsi_privacy_key) { + wpa_printf(MSG_DEBUG, + "EAP-SIM: Received encrypted identity, but no IMSI privacy key configured to decrypt it"); + goto failed; + } + + enc_id = (char *) &sm->identity[1]; + end = (char *) &sm->identity[sm->identity_len]; + for (pos = enc_id; pos < end; pos++) { + if (*pos == ',') + break; + } + enc_id_len = pos - enc_id; + + wpa_hexdump_ascii(MSG_DEBUG, + "EAP-SIM: Encrypted permanent identity", + enc_id, enc_id_len); + decoded_id = base64_decode(enc_id, enc_id_len, &decoded_id_len); + if (!decoded_id) { + wpa_printf(MSG_DEBUG, + "EAP-SIM: Could not base64 decode encrypted identity"); + goto failed; + } + wpa_hexdump(MSG_DEBUG, + "EAP-SIM: Decoded encrypted permanent identity", + decoded_id, decoded_id_len); + enc = wpabuf_alloc_copy(decoded_id, decoded_id_len); + os_free(decoded_id); + if (!enc) + goto failed; + dec = crypto_rsa_oaep_sha256_decrypt(sm->cfg->imsi_privacy_key, + enc); + wpabuf_free(enc); + if (!dec) { + wpa_printf(MSG_DEBUG, + "EAP-SIM: Failed to decrypt encrypted identity"); + goto failed; + } + wpa_hexdump_ascii(MSG_DEBUG, "EAP-SIM: Decrypted permanent identity", + wpabuf_head(dec), wpabuf_len(dec)); + username = sim_get_username(wpabuf_head(dec), wpabuf_len(dec)); + if (!username) { + wpabuf_free(dec); + goto failed; + } + new_id = os_memdup(wpabuf_head(dec), wpabuf_len(dec)); + if (!new_id) { + wpabuf_free(dec); + goto failed; + } + os_free(sm->identity); + sm->identity = new_id; + sm->identity_len = wpabuf_len(dec); + wpabuf_free(dec); + os_strlcpy(data->permanent, username, sizeof(data->permanent)); + os_free(username); +#endif /* CRYPTO_RSA_OAEP_SHA256 */ } else { wpa_printf(MSG_DEBUG, "EAP-SIM: Unrecognized username '%s'", username);