diff --git a/src/eap_peer/eap_aka.c b/src/eap_peer/eap_aka.c index 0c9b4b3a9..5c9a8c739 100644 --- a/src/eap_peer/eap_aka.c +++ b/src/eap_peer/eap_aka.c @@ -644,11 +644,12 @@ static struct wpabuf * eap_aka_synchronization_failure( #ifdef CRYPTO_RSA_OAEP_SHA256 static struct wpabuf * eap_aka_encrypt_identity(struct crypto_rsa_key *imsi_privacy_key, - const u8 *identity, size_t identity_len) + const u8 *identity, size_t identity_len, + const char *attr) { struct wpabuf *imsi_buf, *enc; char *b64; - size_t b64_len; + size_t b64_len, len; wpa_hexdump_ascii(MSG_DEBUG, "EAP-AKA: Encrypt permanent identity", identity, identity_len); @@ -666,7 +667,10 @@ eap_aka_encrypt_identity(struct crypto_rsa_key *imsi_privacy_key, if (!b64) return NULL; - enc = wpabuf_alloc(1 + b64_len); + len = 1 + b64_len; + if (attr) + len += 1 + os_strlen(attr); + enc = wpabuf_alloc(len); if (!enc) { os_free(b64); return NULL; @@ -674,6 +678,10 @@ eap_aka_encrypt_identity(struct crypto_rsa_key *imsi_privacy_key, wpabuf_put_u8(enc, '\0'); wpabuf_put_data(enc, b64, b64_len); os_free(b64); + if (attr) { + wpabuf_put_u8(enc, ','); + wpabuf_put_str(enc, attr); + } wpa_hexdump_ascii(MSG_DEBUG, "EAP-AKA: Encrypted permanent identity", wpabuf_head(enc), wpabuf_len(enc)); @@ -717,9 +725,15 @@ static struct wpabuf * eap_aka_response_identity(struct eap_sm *sm, } #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); + if (config) + attr = config->imsi_privacy_attr; enc_identity = eap_aka_encrypt_identity( data->imsi_privacy_key, - identity, identity_len); + identity, identity_len, attr); if (!enc_identity) { wpa_printf(MSG_INFO, "EAP-AKA: Failed to encrypt permanent identity"); diff --git a/src/eap_peer/eap_config.h b/src/eap_peer/eap_config.h index b52007263..26744ab68 100644 --- a/src/eap_peer/eap_config.h +++ b/src/eap_peer/eap_config.h @@ -327,6 +327,16 @@ struct eap_peer_config { */ char *imsi_privacy_cert; + /** + * imsi_privacy_attr - IMSI privacy attribute + * + * This field is used to help the EAP-SIM/AKA/AKA' server to identify + * the used certificate (and as such, the matching private key). This + * is set to an attribute in name=value format if the operator needs + * this information. + */ + char *imsi_privacy_attr; + /** * machine_identity - EAP Identity for machine credential * diff --git a/src/eap_peer/eap_sim.c b/src/eap_peer/eap_sim.c index b23222eec..c5a7d0174 100644 --- a/src/eap_peer/eap_sim.c +++ b/src/eap_peer/eap_sim.c @@ -512,11 +512,12 @@ static struct wpabuf * eap_sim_client_error(struct eap_sim_data *data, u8 id, #ifdef CRYPTO_RSA_OAEP_SHA256 static struct wpabuf * eap_sim_encrypt_identity(struct crypto_rsa_key *imsi_privacy_key, - const u8 *identity, size_t identity_len) + const u8 *identity, size_t identity_len, + const char *attr) { struct wpabuf *imsi_buf, *enc; char *b64; - size_t b64_len; + size_t b64_len, len; wpa_hexdump_ascii(MSG_DEBUG, "EAP-SIM: Encrypt permanent identity", identity, identity_len); @@ -534,7 +535,10 @@ eap_sim_encrypt_identity(struct crypto_rsa_key *imsi_privacy_key, if (!b64) return NULL; - enc = wpabuf_alloc(1 + b64_len); + len = 1 + b64_len; + if (attr) + len += 1 + os_strlen(attr); + enc = wpabuf_alloc(len); if (!enc) { os_free(b64); return NULL; @@ -542,6 +546,10 @@ eap_sim_encrypt_identity(struct crypto_rsa_key *imsi_privacy_key, wpabuf_put_u8(enc, '\0'); wpabuf_put_data(enc, b64, b64_len); os_free(b64); + if (attr) { + wpabuf_put_u8(enc, ','); + wpabuf_put_str(enc, attr); + } wpa_hexdump_ascii(MSG_DEBUG, "EAP-SIM: Encrypted permanent identity", wpabuf_head(enc), wpabuf_len(enc)); @@ -585,9 +593,15 @@ static struct wpabuf * eap_sim_response_start(struct eap_sm *sm, } #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); + if (config) + attr = config->imsi_privacy_attr; enc_identity = eap_sim_encrypt_identity( data->imsi_privacy_key, - identity, identity_len); + identity, identity_len, attr); if (!enc_identity) { wpa_printf(MSG_INFO, "EAP-SIM: Failed to encrypt permanent identity"); diff --git a/wpa_supplicant/README-HS20 b/wpa_supplicant/README-HS20 index a099a85b4..0cc5f3949 100644 --- a/wpa_supplicant/README-HS20 +++ b/wpa_supplicant/README-HS20 @@ -168,11 +168,16 @@ Credentials can be pre-configured for automatic network selection: # milenage: Milenage parameters for SIM/USIM simulator in :: # format # -# imsi_privacy_key: IMSI privacy key (PEM encoded X.509v3 certificate) +# imsi_privacy_cert: IMSI privacy certificate (PEM encoded X.509v3 certificate) # This field is used with EAP-SIM/AKA/AKA' to encrypt the permanent # identity (IMSI) to improve privacy. The X.509v3 certificate needs to # include a 2048-bit RSA public key and this is from the operator who # authenticates the SIM/USIM. +# imsi_privacy_attr: IMSI privacy attribute +# This field is used to help the EAP-SIM/AKA/AKA' server to identify +# the used certificate (and as such, the matching private key). This +# is set to an attribute in name=value format if the operator needs +# this information. # # domain_suffix_match: Constraint for server domain name # If set, this FQDN is used as a suffix match requirement for the AAA diff --git a/wpa_supplicant/config.c b/wpa_supplicant/config.c index 49d3244f6..bfbc46000 100644 --- a/wpa_supplicant/config.c +++ b/wpa_supplicant/config.c @@ -2504,6 +2504,7 @@ static const struct parse_data ssid_fields[] = { { INT(eapol_flags) }, { INTe(sim_num, sim_num) }, { STRe(imsi_privacy_cert, imsi_privacy_cert) }, + { STRe(imsi_privacy_attr, imsi_privacy_attr) }, { STRe(openssl_ciphers, openssl_ciphers) }, { INTe(erp, erp) }, #endif /* IEEE8021X_EAPOL */ @@ -2772,6 +2773,7 @@ static void eap_peer_config_free(struct eap_peer_config *eap) os_free(eap->anonymous_identity); os_free(eap->imsi_identity); os_free(eap->imsi_privacy_cert); + os_free(eap->imsi_privacy_attr); os_free(eap->machine_identity); bin_clear_free(eap->password, eap->password_len); bin_clear_free(eap->machine_password, eap->machine_password_len); @@ -2876,6 +2878,7 @@ void wpa_config_free_cred(struct wpa_cred *cred) os_free(cred->req_conn_capab_port); os_free(cred->req_conn_capab_proto); os_free(cred->imsi_privacy_cert); + os_free(cred->imsi_privacy_attr); os_free(cred); } @@ -3917,6 +3920,12 @@ int wpa_config_set_cred(struct wpa_cred *cred, const char *var, return 0; } + if (os_strcmp(var, "imsi_privacy_attr") == 0) { + os_free(cred->imsi_privacy_attr); + cred->imsi_privacy_attr = val; + return 0; + } + if (line) { wpa_printf(MSG_ERROR, "Line %d: unknown cred field '%s'.", line, var); @@ -4070,6 +4079,9 @@ char * wpa_config_get_cred_no_key(struct wpa_cred *cred, const char *var) if (os_strcmp(var, "imsi_privacy_cert") == 0) return alloc_strdup(cred->imsi_privacy_cert); + if (os_strcmp(var, "imsi_privacy_attr") == 0) + return alloc_strdup(cred->imsi_privacy_attr); + if (os_strcmp(var, "milenage") == 0) { if (!(cred->milenage)) return NULL; diff --git a/wpa_supplicant/config.h b/wpa_supplicant/config.h index 9e6ee87cf..ba9f2ed8b 100644 --- a/wpa_supplicant/config.h +++ b/wpa_supplicant/config.h @@ -190,6 +190,16 @@ struct wpa_cred { */ char *imsi_privacy_cert; + /** + * imsi_privacy_attr - IMSI privacy attribute + * + * This field is used to help the EAP-SIM/AKA/AKA' server to identify + * the used certificate (and as such, the matching private key). This + * is set to an attribute in name=value format if the operator needs + * this information. + */ + char *imsi_privacy_attr; + /** * engine - Use an engine for private key operations */ diff --git a/wpa_supplicant/config_file.c b/wpa_supplicant/config_file.c index 163b48073..c0763253f 100644 --- a/wpa_supplicant/config_file.c +++ b/wpa_supplicant/config_file.c @@ -1036,6 +1036,13 @@ static void wpa_config_write_cred(FILE *f, struct wpa_cred *cred) fprintf(f, "\tcert_id=\"%s\"\n", cred->cert_id); if (cred->ca_cert_id) fprintf(f, "\tca_cert_id=\"%s\"\n", cred->ca_cert_id); + + if (cred->imsi_privacy_cert) + fprintf(f, "\timsi_privacy_cert=\"%s\"\n", + cred->imsi_privacy_cert); + if (cred->imsi_privacy_attr) + fprintf(f, "\timsi_privacy_attr=\"%s\"\n", + cred->imsi_privacy_attr); } diff --git a/wpa_supplicant/interworking.c b/wpa_supplicant/interworking.c index 78e3087de..bc81728ad 100644 --- a/wpa_supplicant/interworking.c +++ b/wpa_supplicant/interworking.c @@ -1071,6 +1071,12 @@ static int interworking_connect_3gpp(struct wpa_supplicant *wpa_s, goto fail; } + if (cred->imsi_privacy_attr && cred->imsi_privacy_attr[0]) { + if (wpa_config_set_quoted(ssid, "imsi_privacy_attr", + cred->imsi_privacy_attr) < 0) + goto fail; + } + wpa_s->next_ssid = ssid; wpa_config_update_prio_list(wpa_s->conf); if (!only_add)