diff options
Diffstat (limited to 'src/eap_peer/eap_aka.c')
-rw-r--r-- | src/eap_peer/eap_aka.c | 123 |
1 files changed, 91 insertions, 32 deletions
diff --git a/src/eap_peer/eap_aka.c b/src/eap_peer/eap_aka.c index 0bac62dee523a..a4441413f0ddd 100644 --- a/src/eap_peer/eap_aka.c +++ b/src/eap_peer/eap_aka.c @@ -48,11 +48,15 @@ struct eap_aka_data { struct wpabuf *id_msgs; int prev_id; int result_ind, use_result_ind; + int use_pseudonym; u8 eap_method; u8 *network_name; size_t network_name_len; u16 kdf; int kdf_negotiation; + u16 last_kdf_attrs[EAP_AKA_PRIME_KDF_MAX]; + size_t last_kdf_count; + int error_code; }; @@ -96,12 +100,16 @@ static void * eap_aka_init(struct eap_sm *sm) data->eap_method = EAP_TYPE_AKA; + /* Zero is a valid error code, so we need to initialize */ + data->error_code = NO_EAP_METHOD_ERROR; + eap_aka_state(data, CONTINUE); data->prev_id = -1; data->result_ind = phase1 && os_strstr(phase1, "result_ind=1") != NULL; - if (config && config->anonymous_identity) { + data->use_pseudonym = !sm->init_phase2; + if (config && config->anonymous_identity && data->use_pseudonym) { data->pseudonym = os_malloc(config->anonymous_identity_len); if (data->pseudonym) { os_memcpy(data->pseudonym, config->anonymous_identity, @@ -350,7 +358,8 @@ static void eap_aka_clear_identities(struct eap_sm *sm, os_free(data->pseudonym); data->pseudonym = NULL; data->pseudonym_len = 0; - eap_set_anon_id(sm, NULL, 0); + if (data->use_pseudonym) + 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"); @@ -405,20 +414,21 @@ 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 (data->use_pseudonym) + eap_set_anon_id(sm, data->pseudonym, + data->pseudonym_len); } if (attr->next_reauth_id) { os_free(data->reauth_id); - data->reauth_id = os_malloc(attr->next_reauth_id_len); + data->reauth_id = os_memdup(attr->next_reauth_id, + attr->next_reauth_id_len); if (data->reauth_id == NULL) { wpa_printf(MSG_INFO, "EAP-AKA: (encr) No memory for " "next reauth_id"); data->reauth_id_len = 0; return -1; } - os_memcpy(data->reauth_id, attr->next_reauth_id, - attr->next_reauth_id_len); data->reauth_id_len = attr->next_reauth_id_len; wpa_hexdump_ascii(MSG_DEBUG, "EAP-AKA: (encr) AT_NEXT_REAUTH_ID", @@ -570,7 +580,7 @@ static struct wpabuf * eap_aka_authentication_reject(struct eap_aka_data *data, static struct wpabuf * eap_aka_synchronization_failure( - struct eap_aka_data *data, u8 id) + struct eap_aka_data *data, u8 id, struct eap_sim_attrs *attr) { struct eap_sim_msg *msg; @@ -584,6 +594,15 @@ static struct wpabuf * eap_aka_synchronization_failure( wpa_printf(MSG_DEBUG, " AT_AUTS"); eap_sim_msg_add_full(msg, EAP_SIM_AT_AUTS, data->auts, EAP_AKA_AUTS_LEN); + if (data->eap_method == EAP_TYPE_AKA_PRIME) { + size_t i; + + for (i = 0; i < attr->kdf_count; i++) { + wpa_printf(MSG_DEBUG, " AT_KDF"); + eap_sim_msg_add(msg, EAP_SIM_AT_KDF, attr->kdf[i], + NULL, 0); + } + } return eap_sim_msg_finish(msg, data->eap_method, NULL, NULL, 0); } @@ -817,9 +836,13 @@ static struct wpabuf * eap_aka_prime_kdf_neg(struct eap_aka_data *data, size_t i; for (i = 0; i < attr->kdf_count; i++) { - if (attr->kdf[i] == EAP_AKA_PRIME_KDF) + if (attr->kdf[i] == EAP_AKA_PRIME_KDF) { + os_memcpy(data->last_kdf_attrs, attr->kdf, + sizeof(u16) * attr->kdf_count); + data->last_kdf_count = attr->kdf_count; return eap_aka_prime_kdf_select(data, id, EAP_AKA_PRIME_KDF); + } } /* No matching KDF found - fail authentication as if AUTN had been @@ -840,26 +863,32 @@ static int eap_aka_prime_kdf_valid(struct eap_aka_data *data, * of the selected KDF into the beginning of the list. */ if (data->kdf_negotiation) { + /* When the peer receives the new EAP-Request/AKA'-Challenge + * message, must check only requested change occurred in the + * list of AT_KDF attributes. If there are any other changes, + * the peer must behave like the case that AT_MAC had been + * incorrect and authentication is failed. These are defined in + * EAP-AKA' specification RFC 5448, Section 3.2. */ if (attr->kdf[0] != data->kdf) { wpa_printf(MSG_WARNING, "EAP-AKA': The server did not " "accept the selected KDF"); - return 0; + return -1; } - for (i = 1; i < attr->kdf_count; i++) { - if (attr->kdf[i] == data->kdf) - break; - } - if (i == attr->kdf_count && - attr->kdf_count < EAP_AKA_PRIME_KDF_MAX) { - wpa_printf(MSG_WARNING, "EAP-AKA': The server did not " - "duplicate the selected KDF"); - return 0; + if (attr->kdf_count > EAP_AKA_PRIME_KDF_MAX || + attr->kdf_count != data->last_kdf_count + 1) { + wpa_printf(MSG_WARNING, + "EAP-AKA': The length of KDF attributes is wrong"); + return -1; } - /* TODO: should check that the list is identical to the one - * used in the previous Challenge message apart from the added - * entry in the beginning. */ + for (i = 1; i < attr->kdf_count; i++) { + if (attr->kdf[i] != data->last_kdf_attrs[i - 1]) { + wpa_printf(MSG_WARNING, + "EAP-AKA': The KDF attributes except selected KDF are not same as original one"); + return -1; + } + } } for (i = data->kdf ? 1 : 0; i < attr->kdf_count; i++) { @@ -908,22 +937,25 @@ static struct wpabuf * eap_aka_process_challenge(struct eap_sm *sm, return eap_aka_authentication_reject(data, id); } os_free(data->network_name); - data->network_name = os_malloc(attr->kdf_input_len); + data->network_name = os_memdup(attr->kdf_input, + attr->kdf_input_len); if (data->network_name == NULL) { wpa_printf(MSG_WARNING, "EAP-AKA': No memory for " "storing Network Name"); return eap_aka_authentication_reject(data, id); } - os_memcpy(data->network_name, attr->kdf_input, - attr->kdf_input_len); data->network_name_len = attr->kdf_input_len; wpa_hexdump_ascii(MSG_DEBUG, "EAP-AKA': Network Name " "(AT_KDF_INPUT)", data->network_name, data->network_name_len); /* TODO: check Network Name per 3GPP.33.402 */ - if (!eap_aka_prime_kdf_valid(data, attr)) + res = eap_aka_prime_kdf_valid(data, attr); + if (res == 0) return eap_aka_authentication_reject(data, id); + else if (res == -1) + return eap_aka_client_error( + data, id, EAP_AKA_UNABLE_TO_PROCESS_PACKET); if (attr->kdf[0] != EAP_AKA_PRIME_KDF) return eap_aka_prime_kdf_neg(data, id, attr); @@ -966,7 +998,7 @@ static struct wpabuf * eap_aka_process_challenge(struct eap_sm *sm, } else if (res == -2) { wpa_printf(MSG_WARNING, "EAP-AKA: UMTS authentication " "failed (AUTN seq# -> AUTS)"); - return eap_aka_synchronization_failure(data, id); + return eap_aka_synchronization_failure(data, id, attr); } else if (res > 0) { wpa_printf(MSG_DEBUG, "EAP-AKA: Wait for external USIM processing"); return NULL; @@ -997,8 +1029,17 @@ static struct wpabuf * eap_aka_process_challenge(struct eap_sm *sm, } else if (data->pseudonym) { identity = data->pseudonym; identity_len = data->pseudonym_len; - } else - identity = eap_get_config_identity(sm, &identity_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); + } + } wpa_hexdump_ascii(MSG_DEBUG, "EAP-AKA: Selected identity for MK " "derivation", identity, identity_len); if (data->eap_method == EAP_TYPE_AKA_PRIME) { @@ -1143,6 +1184,7 @@ static struct wpabuf * eap_aka_process_notification( eap_sim_report_notification(sm->msg_ctx, attr->notification, 1); if (attr->notification >= 0 && attr->notification < 32768) { + data->error_code = attr->notification; eap_aka_state(data, FAILURE); } else if (attr->notification == EAP_SIM_SUCCESS && data->state == RESULT_SUCCESS) @@ -1437,12 +1479,11 @@ static u8 * eap_aka_getKey(struct eap_sm *sm, void *priv, size_t *len) if (data->state != SUCCESS) return NULL; - key = os_malloc(EAP_SIM_KEYING_DATA_LEN); + key = os_memdup(data->msk, EAP_SIM_KEYING_DATA_LEN); if (key == NULL) return NULL; *len = EAP_SIM_KEYING_DATA_LEN; - os_memcpy(key, data->msk, EAP_SIM_KEYING_DATA_LEN); return key; } @@ -1478,17 +1519,33 @@ static u8 * eap_aka_get_emsk(struct eap_sm *sm, void *priv, size_t *len) if (data->state != SUCCESS) return NULL; - key = os_malloc(EAP_EMSK_LEN); + key = os_memdup(data->emsk, EAP_EMSK_LEN); if (key == NULL) return NULL; *len = EAP_EMSK_LEN; - os_memcpy(key, data->emsk, EAP_EMSK_LEN); return key; } +static int eap_aka_get_error_code(void *priv) +{ + struct eap_aka_data *data = priv; + int current_data_error; + + if (!data) + return NO_EAP_METHOD_ERROR; + + current_data_error = data->error_code; + + /* Now reset for next transaction */ + data->error_code = NO_EAP_METHOD_ERROR; + + return current_data_error; +} + + int eap_peer_aka_register(void) { struct eap_method *eap; @@ -1509,6 +1566,7 @@ int eap_peer_aka_register(void) eap->init_for_reauth = eap_aka_init_for_reauth; eap->get_identity = eap_aka_get_identity; eap->get_emsk = eap_aka_get_emsk; + eap->get_error_code = eap_aka_get_error_code; return eap_peer_method_register(eap); } @@ -1536,6 +1594,7 @@ int eap_peer_aka_prime_register(void) eap->init_for_reauth = eap_aka_init_for_reauth; eap->get_identity = eap_aka_get_identity; eap->get_emsk = eap_aka_get_emsk; + eap->get_error_code = eap_aka_get_error_code; return eap_peer_method_register(eap); } |