| /* |
| * EAP peer method: EAP-SIM (RFC 4186) |
| * Copyright (c) 2004-2012, Jouni Malinen <j@w1.fi> |
| * |
| * This software may be distributed under the terms of the BSD license. |
| * See README for more details. |
| */ |
| |
| #include "includes.h" |
| |
| #include "common.h" |
| #include "utils/base64.h" |
| #include "pcsc_funcs.h" |
| #include "crypto/crypto.h" |
| #include "crypto/milenage.h" |
| #include "crypto/random.h" |
| #include "eap_peer/eap_i.h" |
| #include "eap_config.h" |
| #include "eap_common/eap_sim_common.h" |
| |
| |
| struct eap_sim_data { |
| u8 *ver_list; |
| size_t ver_list_len; |
| int selected_version; |
| size_t min_num_chal, num_chal; |
| |
| u8 kc[3][EAP_SIM_KC_LEN]; |
| u8 sres[3][EAP_SIM_SRES_LEN]; |
| u8 nonce_mt[EAP_SIM_NONCE_MT_LEN], nonce_s[EAP_SIM_NONCE_S_LEN]; |
| u8 mk[EAP_SIM_MK_LEN]; |
| u8 k_aut[EAP_SIM_K_AUT_LEN]; |
| u8 k_encr[EAP_SIM_K_ENCR_LEN]; |
| u8 msk[EAP_SIM_KEYING_DATA_LEN]; |
| u8 emsk[EAP_EMSK_LEN]; |
| u8 rand[3][GSM_RAND_LEN]; |
| u8 reauth_mac[EAP_SIM_MAC_LEN]; |
| |
| int num_id_req, num_notification; |
| u8 *pseudonym; |
| size_t pseudonym_len; |
| u8 *reauth_id; |
| size_t reauth_id_len; |
| int reauth; |
| unsigned int counter, counter_too_small; |
| u8 *mk_identity; |
| size_t mk_identity_len; |
| enum { |
| CONTINUE, START_DONE, RESULT_SUCCESS, SUCCESS, FAILURE |
| } state; |
| int result_ind, use_result_ind; |
| int use_pseudonym; |
| int error_code; |
| struct crypto_rsa_key *imsi_privacy_key; |
| }; |
| |
| |
| #ifndef CONFIG_NO_STDOUT_DEBUG |
| static const char * eap_sim_state_txt(int state) |
| { |
| switch (state) { |
| case CONTINUE: |
| return "CONTINUE"; |
| case START_DONE: |
| return "START_DONE"; |
| case RESULT_SUCCESS: |
| return "RESULT_SUCCESS"; |
| case SUCCESS: |
| return "SUCCESS"; |
| case FAILURE: |
| return "FAILURE"; |
| default: |
| return "?"; |
| } |
| } |
| #endif /* CONFIG_NO_STDOUT_DEBUG */ |
| |
| |
| static void eap_sim_state(struct eap_sim_data *data, int state) |
| { |
| wpa_printf(MSG_DEBUG, "EAP-SIM: %s -> %s", |
| eap_sim_state_txt(data->state), |
| eap_sim_state_txt(state)); |
| data->state = state; |
| } |
| |
| |
| static void * eap_sim_init(struct eap_sm *sm) |
| { |
| struct eap_sim_data *data; |
| struct eap_peer_config *config = eap_get_config(sm); |
| |
| data = os_zalloc(sizeof(*data)); |
| if (data == NULL) |
| return NULL; |
| |
| if (random_get_bytes(data->nonce_mt, EAP_SIM_NONCE_MT_LEN)) { |
| wpa_printf(MSG_WARNING, "EAP-SIM: Failed to get random data " |
| "for NONCE_MT"); |
| os_free(data); |
| return NULL; |
| } |
| |
| if (config && config->imsi_privacy_cert) { |
| #ifdef CRYPTO_RSA_OAEP_SHA256 |
| data->imsi_privacy_key = crypto_rsa_key_read( |
| config->imsi_privacy_cert, false); |
| if (!data->imsi_privacy_key) { |
| wpa_printf(MSG_ERROR, |
| "EAP-SIM: Failed to read/parse IMSI privacy certificate %s", |
| config->imsi_privacy_cert); |
| os_free(data); |
| return NULL; |
| } |
| #else /* CRYPTO_RSA_OAEP_SHA256 */ |
| wpa_printf(MSG_ERROR, |
| "EAP-SIM: No support for imsi_privacy_cert in the build"); |
| os_free(data); |
| return NULL; |
| #endif /* CRYPTO_RSA_OAEP_SHA256 */ |
| } |
| |
| /* Zero is a valid error code, so we need to initialize */ |
| data->error_code = NO_EAP_METHOD_ERROR; |
| |
| data->min_num_chal = 2; |
| if (config && config->phase1) { |
| char *pos = os_strstr(config->phase1, "sim_min_num_chal="); |
| if (pos) { |
| data->min_num_chal = atoi(pos + 17); |
| if (data->min_num_chal < 2 || data->min_num_chal > 3) { |
| wpa_printf(MSG_WARNING, "EAP-SIM: Invalid " |
| "sim_min_num_chal configuration " |
| "(%lu, expected 2 or 3)", |
| (unsigned long) data->min_num_chal); |
| #ifdef CRYPTO_RSA_OAEP_SHA256 |
| crypto_rsa_key_free(data->imsi_privacy_key); |
| #endif /* CRYPTO_RSA_OAEP_SHA256 */ |
| os_free(data); |
| return NULL; |
| } |
| wpa_printf(MSG_DEBUG, "EAP-SIM: Set minimum number of " |
| "challenges to %lu", |
| (unsigned long) data->min_num_chal); |
| } |
| |
| data->result_ind = os_strstr(config->phase1, "result_ind=1") != |
| NULL; |
| } |
| |
| 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, |
| config->anonymous_identity_len); |
| data->pseudonym_len = config->anonymous_identity_len; |
| } |
| } |
| |
| 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; |
| } |
| |
| |
| static void eap_sim_clear_keys(struct eap_sim_data *data, int reauth) |
| { |
| if (!reauth) { |
| os_memset(data->mk, 0, EAP_SIM_MK_LEN); |
| os_memset(data->k_aut, 0, EAP_SIM_K_AUT_LEN); |
| os_memset(data->k_encr, 0, EAP_SIM_K_ENCR_LEN); |
| } |
| os_memset(data->kc, 0, 3 * EAP_SIM_KC_LEN); |
| os_memset(data->sres, 0, 3 * EAP_SIM_SRES_LEN); |
| os_memset(data->msk, 0, EAP_SIM_KEYING_DATA_LEN); |
| os_memset(data->emsk, 0, EAP_EMSK_LEN); |
| } |
| |
| |
| static void eap_sim_deinit(struct eap_sm *sm, void *priv) |
| { |
| struct eap_sim_data *data = priv; |
| if (data) { |
| os_free(data->ver_list); |
| os_free(data->pseudonym); |
| os_free(data->reauth_id); |
| os_free(data->mk_identity); |
| eap_sim_clear_keys(data, 0); |
| #ifdef CRYPTO_RSA_OAEP_SHA256 |
| crypto_rsa_key_free(data->imsi_privacy_key); |
| #endif /* CRYPTO_RSA_OAEP_SHA256 */ |
| os_free(data); |
| } |
| } |
| |
| |
| static int eap_sim_ext_sim_req(struct eap_sm *sm, struct eap_sim_data *data) |
| { |
| char req[200], *pos, *end; |
| size_t i; |
| |
| wpa_printf(MSG_DEBUG, "EAP-SIM: Use external SIM processing"); |
| pos = req; |
| end = pos + sizeof(req); |
| pos += os_snprintf(pos, end - pos, "GSM-AUTH"); |
| for (i = 0; i < data->num_chal; i++) { |
| pos += os_snprintf(pos, end - pos, ":"); |
| pos += wpa_snprintf_hex(pos, end - pos, data->rand[i], |
| GSM_RAND_LEN); |
| } |
| |
| eap_sm_request_sim(sm, req); |
| return 1; |
| } |
| |
| |
| static int eap_sim_ext_sim_result(struct eap_sm *sm, struct eap_sim_data *data, |
| struct eap_peer_config *conf) |
| { |
| char *resp, *pos; |
| size_t i; |
| |
| wpa_printf(MSG_DEBUG, |
| "EAP-SIM: Use result from external SIM processing"); |
| |
| resp = conf->external_sim_resp; |
| conf->external_sim_resp = NULL; |
| |
| if (os_strncmp(resp, "GSM-AUTH:", 9) != 0) { |
| wpa_printf(MSG_DEBUG, "EAP-SIM: Unrecognized external SIM processing response"); |
| os_free(resp); |
| return -1; |
| } |
| |
| pos = resp + 9; |
| for (i = 0; i < data->num_chal; i++) { |
| wpa_hexdump(MSG_DEBUG, "EAP-SIM: RAND", |
| data->rand[i], GSM_RAND_LEN); |
| |
| if (hexstr2bin(pos, data->kc[i], EAP_SIM_KC_LEN) < 0) |
| goto invalid; |
| wpa_hexdump_key(MSG_DEBUG, "EAP-SIM: Kc", |
| data->kc[i], EAP_SIM_KC_LEN); |
| pos += EAP_SIM_KC_LEN * 2; |
| if (*pos != ':') |
| goto invalid; |
| pos++; |
| |
| if (hexstr2bin(pos, data->sres[i], EAP_SIM_SRES_LEN) < 0) |
| goto invalid; |
| wpa_hexdump_key(MSG_DEBUG, "EAP-SIM: SRES", |
| data->sres[i], EAP_SIM_SRES_LEN); |
| pos += EAP_SIM_SRES_LEN * 2; |
| if (i + 1 < data->num_chal) { |
| if (*pos != ':') |
| goto invalid; |
| pos++; |
| } |
| } |
| |
| os_free(resp); |
| return 0; |
| |
| invalid: |
| wpa_printf(MSG_DEBUG, "EAP-SIM: Invalid external SIM processing GSM-AUTH response"); |
| os_free(resp); |
| return -1; |
| } |
| |
| |
| static int eap_sim_gsm_auth(struct eap_sm *sm, struct eap_sim_data *data) |
| { |
| struct eap_peer_config *conf; |
| |
| wpa_printf(MSG_DEBUG, "EAP-SIM: GSM authentication algorithm"); |
| |
| conf = eap_get_config(sm); |
| if (conf == NULL) |
| return -1; |
| |
| if (sm->external_sim) { |
| if (conf->external_sim_resp) |
| return eap_sim_ext_sim_result(sm, data, conf); |
| else |
| return eap_sim_ext_sim_req(sm, data); |
| } |
| |
| #ifdef PCSC_FUNCS |
| if (conf->pcsc) { |
| if (scard_gsm_auth(sm->scard_ctx, data->rand[0], |
| data->sres[0], data->kc[0]) || |
| scard_gsm_auth(sm->scard_ctx, data->rand[1], |
| data->sres[1], data->kc[1]) || |
| (data->num_chal > 2 && |
| scard_gsm_auth(sm->scard_ctx, data->rand[2], |
| data->sres[2], data->kc[2]))) { |
| wpa_printf(MSG_DEBUG, "EAP-SIM: GSM SIM " |
| "authentication could not be completed"); |
| return -1; |
| } |
| return 0; |
| } |
| #endif /* PCSC_FUNCS */ |
| |
| #ifdef CONFIG_SIM_SIMULATOR |
| if (conf->password) { |
| u8 opc[16], k[16]; |
| const char *pos; |
| size_t i; |
| wpa_printf(MSG_DEBUG, "EAP-SIM: Use internal GSM-Milenage " |
| "implementation for authentication"); |
| if (conf->password_len < 65) { |
| wpa_printf(MSG_DEBUG, "EAP-SIM: invalid GSM-Milenage " |
| "password"); |
| return -1; |
| } |
| pos = (const char *) conf->password; |
| if (hexstr2bin(pos, k, 16)) |
| return -1; |
| pos += 32; |
| if (*pos != ':') |
| return -1; |
| pos++; |
| |
| if (hexstr2bin(pos, opc, 16)) |
| return -1; |
| |
| for (i = 0; i < data->num_chal; i++) { |
| if (gsm_milenage(opc, k, data->rand[i], |
| data->sres[i], data->kc[i])) { |
| wpa_printf(MSG_DEBUG, "EAP-SIM: " |
| "GSM-Milenage authentication " |
| "could not be completed"); |
| return -1; |
| } |
| wpa_hexdump(MSG_DEBUG, "EAP-SIM: RAND", |
| data->rand[i], GSM_RAND_LEN); |
| wpa_hexdump_key(MSG_DEBUG, "EAP-SIM: SRES", |
| data->sres[i], EAP_SIM_SRES_LEN); |
| wpa_hexdump_key(MSG_DEBUG, "EAP-SIM: Kc", |
| data->kc[i], EAP_SIM_KC_LEN); |
| } |
| return 0; |
| } |
| #endif /* CONFIG_SIM_SIMULATOR */ |
| |
| #ifdef CONFIG_SIM_HARDCODED |
| /* These hardcoded Kc and SRES values are used for testing. RAND to |
| * KC/SREC mapping is very bogus as far as real authentication is |
| * concerned, but it is quite useful for cases where the AS is rotating |
| * the order of pre-configured values. */ |
| { |
| size_t i; |
| |
| wpa_printf(MSG_DEBUG, "EAP-SIM: Use hardcoded Kc and SRES " |
| "values for testing"); |
| |
| for (i = 0; i < data->num_chal; i++) { |
| if (data->rand[i][0] == 0xaa) { |
| os_memcpy(data->kc[i], |
| "\xa0\xa1\xa2\xa3\xa4\xa5\xa6\xa7", |
| EAP_SIM_KC_LEN); |
| os_memcpy(data->sres[i], "\xd1\xd2\xd3\xd4", |
| EAP_SIM_SRES_LEN); |
| } else if (data->rand[i][0] == 0xbb) { |
| os_memcpy(data->kc[i], |
| "\xb0\xb1\xb2\xb3\xb4\xb5\xb6\xb7", |
| EAP_SIM_KC_LEN); |
| os_memcpy(data->sres[i], "\xe1\xe2\xe3\xe4", |
| EAP_SIM_SRES_LEN); |
| } else { |
| os_memcpy(data->kc[i], |
| "\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7", |
| EAP_SIM_KC_LEN); |
| os_memcpy(data->sres[i], "\xf1\xf2\xf3\xf4", |
| EAP_SIM_SRES_LEN); |
| } |
| } |
| } |
| |
| return 0; |
| |
| #else /* CONFIG_SIM_HARDCODED */ |
| |
| wpa_printf(MSG_DEBUG, "EAP-SIM: No GSM authentication algorithm " |
| "enabled"); |
| return -1; |
| |
| #endif /* CONFIG_SIM_HARDCODED */ |
| } |
| |
| |
| static int eap_sim_supported_ver(int version) |
| { |
| return version == EAP_SIM_VERSION; |
| } |
| |
| |
| #define CLEAR_PSEUDONYM 0x01 |
| #define CLEAR_REAUTH_ID 0x02 |
| |
| 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; |
| if (data->use_pseudonym) |
| 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"); |
| os_free(data->reauth_id); |
| data->reauth_id = NULL; |
| data->reauth_id_len = 0; |
| } |
| } |
| |
| |
| static int eap_sim_learn_ids(struct eap_sm *sm, struct eap_sim_data *data, |
| struct eap_sim_attrs *attr) |
| { |
| if (attr->next_pseudonym) { |
| const u8 *identity = NULL; |
| size_t identity_len = 0; |
| const u8 *realm = NULL; |
| size_t realm_len = 0; |
| |
| wpa_hexdump_ascii(MSG_DEBUG, |
| "EAP-SIM: (encr) AT_NEXT_PSEUDONYM", |
| attr->next_pseudonym, |
| attr->next_pseudonym_len); |
| os_free(data->pseudonym); |
| |
| /* Get realm from identities to decorate pseudonym. */ |
| realm = eap_get_config_realm(sm, &realm_len); |
| |
| data->pseudonym = os_malloc(attr->next_pseudonym_len + |
| realm_len); |
| if (data->pseudonym == NULL) { |
| wpa_printf(MSG_INFO, "EAP-SIM: (encr) No memory for " |
| "next pseudonym"); |
| data->pseudonym_len = 0; |
| return -1; |
| } |
| os_memcpy(data->pseudonym, attr->next_pseudonym, |
| attr->next_pseudonym_len); |
| if (realm_len) { |
| os_memcpy(data->pseudonym + attr->next_pseudonym_len, |
| realm, realm_len); |
| } |
| data->pseudonym_len = attr->next_pseudonym_len + realm_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_memdup(attr->next_reauth_id, |
| attr->next_reauth_id_len); |
| if (data->reauth_id == NULL) { |
| wpa_printf(MSG_INFO, "EAP-SIM: (encr) No memory for " |
| "next reauth_id"); |
| data->reauth_id_len = 0; |
| return -1; |
| } |
| data->reauth_id_len = attr->next_reauth_id_len; |
| wpa_hexdump_ascii(MSG_DEBUG, |
| "EAP-SIM: (encr) AT_NEXT_REAUTH_ID", |
| data->reauth_id, |
| data->reauth_id_len); |
| } |
| |
| return 0; |
| } |
| |
| |
| static struct wpabuf * eap_sim_client_error(struct eap_sim_data *data, u8 id, |
| int err) |
| { |
| struct eap_sim_msg *msg; |
| |
| eap_sim_state(data, FAILURE); |
| data->num_id_req = 0; |
| data->num_notification = 0; |
| |
| wpa_printf(MSG_DEBUG, "EAP-SIM: Send Client-Error (error code %d)", |
| err); |
| msg = eap_sim_msg_init(EAP_CODE_RESPONSE, id, EAP_TYPE_SIM, |
| EAP_SIM_SUBTYPE_CLIENT_ERROR); |
| eap_sim_msg_add(msg, EAP_SIM_AT_CLIENT_ERROR_CODE, err, NULL, 0); |
| return eap_sim_msg_finish(msg, EAP_TYPE_SIM, NULL, NULL, 0); |
| } |
| |
| |
| #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 char *attr) |
| { |
| struct wpabuf *imsi_buf, *enc; |
| char *b64; |
| size_t b64_len, len; |
| |
| wpa_hexdump_ascii(MSG_DEBUG, "EAP-SIM: Encrypt permanent identity", |
| identity, identity_len); |
| |
| imsi_buf = wpabuf_alloc_copy(identity, identity_len); |
| if (!imsi_buf) |
| return NULL; |
| enc = crypto_rsa_oaep_sha256_encrypt(imsi_privacy_key, imsi_buf); |
| wpabuf_free(imsi_buf); |
| if (!enc) |
| return NULL; |
| |
| b64 = base64_encode_no_lf(wpabuf_head(enc), wpabuf_len(enc), &b64_len); |
| wpabuf_free(enc); |
| if (!b64) |
| return NULL; |
| |
| len = 1 + b64_len; |
| if (attr) |
| len += 1 + os_strlen(attr); |
| enc = wpabuf_alloc(len); |
| if (!enc) { |
| os_free(b64); |
| return NULL; |
| } |
| 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)); |
| |
| return enc; |
| } |
| #endif /* CRYPTO_RSA_OAEP_SHA256 */ |
| |
| |
| static struct wpabuf * eap_sim_response_start(struct eap_sm *sm, |
| struct eap_sim_data *data, u8 id, |
| enum eap_sim_id_req id_req) |
| { |
| const u8 *identity = NULL; |
| size_t identity_len = 0; |
| 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) { |
| identity = data->reauth_id; |
| identity_len = data->reauth_id_len; |
| data->reauth = 1; |
| } else if ((id_req == ANY_ID || id_req == FULLAUTH_ID) && |
| data->pseudonym && |
| !eap_sim_anonymous_username(data->pseudonym, |
| data->pseudonym_len)) { |
| identity = data->pseudonym; |
| identity_len = data->pseudonym_len; |
| eap_sim_clear_identities(sm, data, CLEAR_REAUTH_ID); |
| } else if (id_req != NO_ID_REQ) { |
| if (id_req == PERMANENT_ID && eap_get_config_strict_conservative_peer_mode(sm)) { |
| wpa_printf(MSG_INFO, "EAP-SIM: permanent_id_req is denied in " |
| "the strict conservative peer mode"); |
| eap_notify_permanent_id_req_denied(sm); |
| return eap_sim_client_error(data, id, EAP_SIM_UNABLE_TO_PROCESS_PACKET); |
| } |
| identity = eap_get_config_identity(sm, &identity_len); |
| if (identity) { |
| int ids = CLEAR_PSEUDONYM | CLEAR_REAUTH_ID; |
| |
| if (data->pseudonym && |
| eap_sim_anonymous_username(data->pseudonym, |
| 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) { |
| 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, attr); |
| if (!enc_identity) { |
| wpa_printf(MSG_INFO, |
| "EAP-SIM: Failed to encrypt permanent identity"); |
| return eap_sim_client_error( |
| 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 */ |
| } |
| |
| wpa_printf(MSG_DEBUG, "Generating EAP-SIM Start (id=%d)", id); |
| msg = eap_sim_msg_init(EAP_CODE_RESPONSE, id, |
| EAP_TYPE_SIM, EAP_SIM_SUBTYPE_START); |
| if (identity) { |
| wpa_hexdump_ascii(MSG_DEBUG, " AT_IDENTITY", |
| 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) { |
| wpa_hexdump(MSG_DEBUG, " AT_NONCE_MT", |
| data->nonce_mt, EAP_SIM_NONCE_MT_LEN); |
| eap_sim_msg_add(msg, EAP_SIM_AT_NONCE_MT, 0, |
| data->nonce_mt, EAP_SIM_NONCE_MT_LEN); |
| wpa_printf(MSG_DEBUG, " AT_SELECTED_VERSION %d", |
| data->selected_version); |
| eap_sim_msg_add(msg, EAP_SIM_AT_SELECTED_VERSION, |
| data->selected_version, NULL, 0); |
| } |
| |
| resp = eap_sim_msg_finish(msg, EAP_TYPE_SIM, NULL, NULL, 0); |
| if (resp) |
| eap_sim_state(data, START_DONE); |
| return resp; |
| } |
| |
| |
| static struct wpabuf * eap_sim_response_challenge(struct eap_sim_data *data, |
| u8 id) |
| { |
| struct eap_sim_msg *msg; |
| |
| wpa_printf(MSG_DEBUG, "Generating EAP-SIM Challenge (id=%d)", id); |
| msg = eap_sim_msg_init(EAP_CODE_RESPONSE, id, EAP_TYPE_SIM, |
| EAP_SIM_SUBTYPE_CHALLENGE); |
| if (data->use_result_ind) { |
| wpa_printf(MSG_DEBUG, " AT_RESULT_IND"); |
| eap_sim_msg_add(msg, EAP_SIM_AT_RESULT_IND, 0, NULL, 0); |
| } |
| wpa_printf(MSG_DEBUG, " AT_MAC"); |
| eap_sim_msg_add_mac(msg, EAP_SIM_AT_MAC); |
| return eap_sim_msg_finish(msg, EAP_TYPE_SIM, data->k_aut, |
| (u8 *) data->sres, |
| data->num_chal * EAP_SIM_SRES_LEN); |
| } |
| |
| |
| static struct wpabuf * eap_sim_response_reauth(struct eap_sim_data *data, |
| u8 id, int counter_too_small, |
| const u8 *nonce_s) |
| { |
| struct eap_sim_msg *msg; |
| unsigned int counter; |
| |
| wpa_printf(MSG_DEBUG, "Generating EAP-SIM Reauthentication (id=%d)", |
| id); |
| msg = eap_sim_msg_init(EAP_CODE_RESPONSE, id, EAP_TYPE_SIM, |
| EAP_SIM_SUBTYPE_REAUTHENTICATION); |
| wpa_printf(MSG_DEBUG, " AT_IV"); |
| wpa_printf(MSG_DEBUG, " AT_ENCR_DATA"); |
| eap_sim_msg_add_encr_start(msg, EAP_SIM_AT_IV, EAP_SIM_AT_ENCR_DATA); |
| |
| if (counter_too_small) { |
| wpa_printf(MSG_DEBUG, " *AT_COUNTER_TOO_SMALL"); |
| eap_sim_msg_add(msg, EAP_SIM_AT_COUNTER_TOO_SMALL, 0, NULL, 0); |
| counter = data->counter_too_small; |
| } else |
| counter = data->counter; |
| |
| wpa_printf(MSG_DEBUG, " *AT_COUNTER %d", counter); |
| eap_sim_msg_add(msg, EAP_SIM_AT_COUNTER, counter, NULL, 0); |
| |
| if (eap_sim_msg_add_encr_end(msg, data->k_encr, EAP_SIM_AT_PADDING)) { |
| wpa_printf(MSG_WARNING, "EAP-SIM: Failed to encrypt " |
| "AT_ENCR_DATA"); |
| eap_sim_msg_free(msg); |
| return NULL; |
| } |
| if (data->use_result_ind) { |
| wpa_printf(MSG_DEBUG, " AT_RESULT_IND"); |
| eap_sim_msg_add(msg, EAP_SIM_AT_RESULT_IND, 0, NULL, 0); |
| } |
| wpa_printf(MSG_DEBUG, " AT_MAC"); |
| eap_sim_msg_add_mac(msg, EAP_SIM_AT_MAC); |
| return eap_sim_msg_finish(msg, EAP_TYPE_SIM, data->k_aut, nonce_s, |
| EAP_SIM_NONCE_S_LEN); |
| } |
| |
| |
| static struct wpabuf * eap_sim_response_notification(struct eap_sim_data *data, |
| u8 id, u16 notification) |
| { |
| struct eap_sim_msg *msg; |
| u8 *k_aut = (notification & 0x4000) == 0 ? data->k_aut : NULL; |
| |
| wpa_printf(MSG_DEBUG, "Generating EAP-SIM Notification (id=%d)", id); |
| msg = eap_sim_msg_init(EAP_CODE_RESPONSE, id, |
| EAP_TYPE_SIM, EAP_SIM_SUBTYPE_NOTIFICATION); |
| if (k_aut && data->reauth) { |
| wpa_printf(MSG_DEBUG, " AT_IV"); |
| wpa_printf(MSG_DEBUG, " AT_ENCR_DATA"); |
| eap_sim_msg_add_encr_start(msg, EAP_SIM_AT_IV, |
| EAP_SIM_AT_ENCR_DATA); |
| wpa_printf(MSG_DEBUG, " *AT_COUNTER %d", data->counter); |
| eap_sim_msg_add(msg, EAP_SIM_AT_COUNTER, data->counter, |
| NULL, 0); |
| if (eap_sim_msg_add_encr_end(msg, data->k_encr, |
| EAP_SIM_AT_PADDING)) { |
| wpa_printf(MSG_WARNING, "EAP-SIM: Failed to encrypt " |
| "AT_ENCR_DATA"); |
| eap_sim_msg_free(msg); |
| return NULL; |
| } |
| } |
| if (k_aut) { |
| wpa_printf(MSG_DEBUG, " AT_MAC"); |
| eap_sim_msg_add_mac(msg, EAP_SIM_AT_MAC); |
| } |
| return eap_sim_msg_finish(msg, EAP_TYPE_SIM, k_aut, (u8 *) "", 0); |
| } |
| |
| |
| static struct wpabuf * eap_sim_process_start(struct eap_sm *sm, |
| struct eap_sim_data *data, u8 id, |
| struct eap_sim_attrs *attr) |
| { |
| int selected_version = -1, id_error; |
| size_t i; |
| u8 *pos; |
| |
| wpa_printf(MSG_DEBUG, "EAP-SIM: subtype Start"); |
| if (attr->version_list == NULL) { |
| wpa_printf(MSG_INFO, "EAP-SIM: No AT_VERSION_LIST in " |
| "SIM/Start"); |
| return eap_sim_client_error(data, id, |
| EAP_SIM_UNSUPPORTED_VERSION); |
| } |
| |
| os_free(data->ver_list); |
| data->ver_list = os_memdup(attr->version_list, attr->version_list_len); |
| if (data->ver_list == NULL) { |
| wpa_printf(MSG_DEBUG, "EAP-SIM: Failed to allocate " |
| "memory for version list"); |
| return eap_sim_client_error(data, id, |
| EAP_SIM_UNABLE_TO_PROCESS_PACKET); |
| } |
| data->ver_list_len = attr->version_list_len; |
| pos = data->ver_list; |
| for (i = 0; i < data->ver_list_len / 2; i++) { |
| int ver = pos[0] * 256 + pos[1]; |
| pos += 2; |
| if (eap_sim_supported_ver(ver)) { |
| selected_version = ver; |
| break; |
| } |
| } |
| if (selected_version < 0) { |
| wpa_printf(MSG_INFO, "EAP-SIM: Could not find a supported " |
| "version"); |
| return eap_sim_client_error(data, id, |
| EAP_SIM_UNSUPPORTED_VERSION); |
| } |
| wpa_printf(MSG_DEBUG, "EAP-SIM: Selected Version %d", |
| selected_version); |
| data->selected_version = selected_version; |
| |
| id_error = 0; |
| switch (attr->id_req) { |
| case NO_ID_REQ: |
| break; |
| case ANY_ID: |
| if (data->num_id_req > 0) |
| id_error++; |
| data->num_id_req++; |
| break; |
| case FULLAUTH_ID: |
| if (data->num_id_req > 1) |
| id_error++; |
| data->num_id_req++; |
| break; |
| case PERMANENT_ID: |
| if (data->num_id_req > 2) |
| id_error++; |
| data->num_id_req++; |
| break; |
| } |
| if (id_error) { |
| wpa_printf(MSG_INFO, "EAP-SIM: Too many ID requests " |
| "used within one authentication"); |
| return eap_sim_client_error(data, id, |
| EAP_SIM_UNABLE_TO_PROCESS_PACKET); |
| } |
| |
| return eap_sim_response_start(sm, data, id, attr->id_req); |
| } |
| |
| |
| static struct wpabuf * eap_sim_process_challenge(struct eap_sm *sm, |
| struct eap_sim_data *data, |
| u8 id, |
| const struct wpabuf *reqData, |
| struct eap_sim_attrs *attr) |
| { |
| const u8 *identity; |
| size_t identity_len; |
| struct eap_sim_attrs eattr; |
| int res; |
| |
| wpa_printf(MSG_DEBUG, "EAP-SIM: subtype Challenge"); |
| if (data->state != START_DONE) { |
| wpa_printf(MSG_DEBUG, |
| "EAP-SIM: Unexpected Challenge in state %s", |
| eap_sim_state_txt(data->state)); |
| return eap_sim_client_error(data, id, |
| EAP_SIM_UNABLE_TO_PROCESS_PACKET); |
| } |
| data->reauth = 0; |
| if (!attr->mac || !attr->rand) { |
| wpa_printf(MSG_WARNING, "EAP-SIM: Challenge message " |
| "did not include%s%s", |
| !attr->mac ? " AT_MAC" : "", |
| !attr->rand ? " AT_RAND" : ""); |
| return eap_sim_client_error(data, id, |
| EAP_SIM_UNABLE_TO_PROCESS_PACKET); |
| } |
| |
| wpa_printf(MSG_DEBUG, "EAP-SIM: %lu challenges", |
| (unsigned long) attr->num_chal); |
| if (attr->num_chal < data->min_num_chal) { |
| wpa_printf(MSG_INFO, "EAP-SIM: Insufficient number of " |
| "challenges (%lu)", (unsigned long) attr->num_chal); |
| return eap_sim_client_error(data, id, |
| EAP_SIM_INSUFFICIENT_NUM_OF_CHAL); |
| } |
| if (attr->num_chal > 3) { |
| wpa_printf(MSG_INFO, "EAP-SIM: Too many challenges " |
| "(%lu)", (unsigned long) attr->num_chal); |
| return eap_sim_client_error(data, id, |
| EAP_SIM_UNABLE_TO_PROCESS_PACKET); |
| } |
| |
| /* Verify that RANDs are different */ |
| if (os_memcmp(attr->rand, attr->rand + GSM_RAND_LEN, |
| GSM_RAND_LEN) == 0 || |
| (attr->num_chal > 2 && |
| (os_memcmp(attr->rand, attr->rand + 2 * GSM_RAND_LEN, |
| GSM_RAND_LEN) == 0 || |
| os_memcmp(attr->rand + GSM_RAND_LEN, |
| attr->rand + 2 * GSM_RAND_LEN, |
| GSM_RAND_LEN) == 0))) { |
| wpa_printf(MSG_INFO, "EAP-SIM: Same RAND used multiple times"); |
| return eap_sim_client_error(data, id, |
| EAP_SIM_RAND_NOT_FRESH); |
| } |
| |
| os_memcpy(data->rand, attr->rand, attr->num_chal * GSM_RAND_LEN); |
| data->num_chal = attr->num_chal; |
| |
| res = eap_sim_gsm_auth(sm, data); |
| if (res > 0) { |
| wpa_printf(MSG_DEBUG, "EAP-SIM: Wait for external SIM processing"); |
| return NULL; |
| } |
| if (res) { |
| wpa_printf(MSG_WARNING, "EAP-SIM: GSM authentication failed"); |
| return eap_sim_client_error(data, id, |
| EAP_SIM_UNABLE_TO_PROCESS_PACKET); |
| } |
| |
| 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, |
| data->selected_version, data->ver_list, |
| data->ver_list_len, data->num_chal, |
| (const u8 *) data->kc, data->mk); |
| eap_sim_derive_keys(data->mk, data->k_encr, data->k_aut, data->msk, |
| data->emsk); |
| if (eap_sim_verify_mac(data->k_aut, reqData, attr->mac, data->nonce_mt, |
| EAP_SIM_NONCE_MT_LEN)) { |
| wpa_printf(MSG_WARNING, "EAP-SIM: Challenge message " |
| "used invalid AT_MAC"); |
| #ifdef TEST_FUZZ |
| wpa_printf(MSG_INFO, |
| "TEST: Ignore AT_MAC mismatch for fuzz testing"); |
| #else /* TEST_FUZZ */ |
| return eap_sim_client_error(data, id, |
| EAP_SIM_UNABLE_TO_PROCESS_PACKET); |
| #endif /* TEST_FUZZ */ |
| } |
| |
| /* Old reauthentication identity must not be used anymore. In |
| * 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); |
| |
| if (attr->encr_data) { |
| u8 *decrypted; |
| decrypted = eap_sim_parse_encr(data->k_encr, attr->encr_data, |
| attr->encr_data_len, attr->iv, |
| &eattr, 0); |
| if (decrypted == NULL) { |
| return eap_sim_client_error( |
| data, id, EAP_SIM_UNABLE_TO_PROCESS_PACKET); |
| } |
| eap_sim_learn_ids(sm, data, &eattr); |
| os_free(decrypted); |
| } |
| |
| if (data->result_ind && attr->result_ind) |
| data->use_result_ind = 1; |
| |
| if (data->state != FAILURE) { |
| eap_sim_state(data, data->use_result_ind ? |
| RESULT_SUCCESS : SUCCESS); |
| } |
| |
| data->num_id_req = 0; |
| data->num_notification = 0; |
| /* RFC 4186 specifies that counter is initialized to one after |
| * fullauth, but initializing it to zero makes it easier to implement |
| * reauth verification. */ |
| data->counter = 0; |
| return eap_sim_response_challenge(data, id); |
| } |
| |
| |
| static int eap_sim_process_notification_reauth(struct eap_sim_data *data, |
| struct eap_sim_attrs *attr) |
| { |
| struct eap_sim_attrs eattr; |
| u8 *decrypted; |
| |
| if (attr->encr_data == NULL || attr->iv == NULL) { |
| wpa_printf(MSG_WARNING, "EAP-SIM: Notification message after " |
| "reauth did not include encrypted data"); |
| return -1; |
| } |
| |
| decrypted = eap_sim_parse_encr(data->k_encr, attr->encr_data, |
| attr->encr_data_len, attr->iv, &eattr, |
| 0); |
| if (decrypted == NULL) { |
| wpa_printf(MSG_WARNING, "EAP-SIM: Failed to parse encrypted " |
| "data from notification message"); |
| return -1; |
| } |
| |
| if (eattr.counter < 0 || (size_t) eattr.counter != data->counter) { |
| wpa_printf(MSG_WARNING, "EAP-SIM: Counter in notification " |
| "message does not match with counter in reauth " |
| "message"); |
| os_free(decrypted); |
| return -1; |
| } |
| |
| os_free(decrypted); |
| return 0; |
| } |
| |
| |
| static int eap_sim_process_notification_auth(struct eap_sim_data *data, |
| const struct wpabuf *reqData, |
| struct eap_sim_attrs *attr) |
| { |
| if (attr->mac == NULL) { |
| wpa_printf(MSG_INFO, "EAP-SIM: no AT_MAC in after_auth " |
| "Notification message"); |
| return -1; |
| } |
| |
| if (eap_sim_verify_mac(data->k_aut, reqData, attr->mac, (u8 *) "", 0)) |
| { |
| wpa_printf(MSG_WARNING, "EAP-SIM: Notification message " |
| "used invalid AT_MAC"); |
| return -1; |
| } |
| |
| if (data->reauth && |
| eap_sim_process_notification_reauth(data, attr)) { |
| wpa_printf(MSG_WARNING, "EAP-SIM: Invalid notification " |
| "message after reauth"); |
| return -1; |
| } |
| |
| return 0; |
| } |
| |
| |
| static struct wpabuf * eap_sim_process_notification( |
| struct eap_sm *sm, struct eap_sim_data *data, u8 id, |
| const struct wpabuf *reqData, struct eap_sim_attrs *attr) |
| { |
| wpa_printf(MSG_DEBUG, "EAP-SIM: subtype Notification"); |
| if (data->num_notification > 0) { |
| wpa_printf(MSG_INFO, "EAP-SIM: too many notification " |
| "rounds (only one allowed)"); |
| return eap_sim_client_error(data, id, |
| EAP_SIM_UNABLE_TO_PROCESS_PACKET); |
| } |
| data->num_notification++; |
| if (attr->notification == -1) { |
| wpa_printf(MSG_INFO, "EAP-SIM: no AT_NOTIFICATION in " |
| "Notification message"); |
| return eap_sim_client_error(data, id, |
| EAP_SIM_UNABLE_TO_PROCESS_PACKET); |
| } |
| |
| if ((attr->notification & 0x4000) == 0 && |
| eap_sim_process_notification_auth(data, reqData, attr)) { |
| return eap_sim_client_error(data, id, |
| EAP_SIM_UNABLE_TO_PROCESS_PACKET); |
| } |
| |
| eap_sim_report_notification(sm->msg_ctx, attr->notification, 0); |
| if (attr->notification >= 0 && attr->notification < 32768) { |
| data->error_code = attr->notification; |
| eap_sim_state(data, FAILURE); |
| } else if (attr->notification == EAP_SIM_SUCCESS && |
| data->state == RESULT_SUCCESS) |
| eap_sim_state(data, SUCCESS); |
| return eap_sim_response_notification(data, id, attr->notification); |
| } |
| |
| |
| static struct wpabuf * eap_sim_process_reauthentication( |
| struct eap_sm *sm, struct eap_sim_data *data, u8 id, |
| const struct wpabuf *reqData, struct eap_sim_attrs *attr) |
| { |
| struct eap_sim_attrs eattr; |
| u8 *decrypted; |
| |
| wpa_printf(MSG_DEBUG, "EAP-SIM: subtype Reauthentication"); |
| |
| if (data->reauth_id == NULL) { |
| wpa_printf(MSG_WARNING, "EAP-SIM: Server is trying " |
| "reauthentication, but no reauth_id available"); |
| return eap_sim_client_error(data, id, |
| EAP_SIM_UNABLE_TO_PROCESS_PACKET); |
| } |
| |
| data->reauth = 1; |
| if (eap_sim_verify_mac(data->k_aut, reqData, attr->mac, (u8 *) "", 0)) |
| { |
| wpa_printf(MSG_WARNING, "EAP-SIM: Reauthentication " |
| "did not have valid AT_MAC"); |
| #ifdef TEST_FUZZ |
| wpa_printf(MSG_INFO, |
| "TEST: Ignore AT_MAC mismatch for fuzz testing"); |
| #else /* TEST_FUZZ */ |
| return eap_sim_client_error(data, id, |
| EAP_SIM_UNABLE_TO_PROCESS_PACKET); |
| #endif /* TEST_FUZZ */ |
| } |
| |
| /* At this stage the received MAC has been verified. Use this MAC for |
| * reauth Session-Id calculation if all other checks pass. |
| * The peer does not use the local MAC but the received MAC in deriving |
| * Session-Id. */ |
| #ifdef TEST_FUZZ |
| if (attr->mac) |
| os_memcpy(data->reauth_mac, attr->mac, EAP_SIM_MAC_LEN); |
| else |
| os_memset(data->reauth_mac, 0x12, EAP_SIM_MAC_LEN); |
| #else /* TEST_FUZZ */ |
| os_memcpy(data->reauth_mac, attr->mac, EAP_SIM_MAC_LEN); |
| #endif /* TEST_FUZZ */ |
| wpa_hexdump(MSG_DEBUG, "EAP-SIM: Server MAC", |
| data->reauth_mac, EAP_SIM_MAC_LEN); |
| |
| if (attr->encr_data == NULL || attr->iv == NULL) { |
| wpa_printf(MSG_WARNING, "EAP-SIM: Reauthentication " |
| "message did not include encrypted data"); |
| return eap_sim_client_error(data, id, |
| EAP_SIM_UNABLE_TO_PROCESS_PACKET); |
| } |
| |
| decrypted = eap_sim_parse_encr(data->k_encr, attr->encr_data, |
| attr->encr_data_len, attr->iv, &eattr, |
| 0); |
| if (decrypted == NULL) { |
| wpa_printf(MSG_WARNING, "EAP-SIM: Failed to parse encrypted " |
| "data from reauthentication message"); |
| return eap_sim_client_error(data, id, |
| EAP_SIM_UNABLE_TO_PROCESS_PACKET); |
| } |
| |
| if (eattr.nonce_s == NULL || eattr.counter < 0) { |
| wpa_printf(MSG_INFO, "EAP-SIM: (encr) No%s%s in reauth packet", |
| !eattr.nonce_s ? " AT_NONCE_S" : "", |
| eattr.counter < 0 ? " AT_COUNTER" : ""); |
| os_free(decrypted); |
| return eap_sim_client_error(data, id, |
| EAP_SIM_UNABLE_TO_PROCESS_PACKET); |
| } |
| |
| if (eattr.counter < 0 || (size_t) eattr.counter <= data->counter) { |
| struct wpabuf *res; |
| wpa_printf(MSG_INFO, "EAP-SIM: (encr) Invalid counter " |
| "(%d <= %d)", eattr.counter, data->counter); |
| data->counter_too_small = eattr.counter; |
| |
| /* Reply using Re-auth w/ AT_COUNTER_TOO_SMALL. The current |
| * reauth_id must not be used to start a new reauthentication. |
| */ |
| eap_sim_clear_identities(sm, data, CLEAR_REAUTH_ID); |
| |
| res = eap_sim_response_reauth(data, id, 1, eattr.nonce_s); |
| os_free(decrypted); |
| |
| return res; |
| } |
| data->counter = eattr.counter; |
| |
| os_memcpy(data->nonce_s, eattr.nonce_s, EAP_SIM_NONCE_S_LEN); |
| wpa_hexdump(MSG_DEBUG, "EAP-SIM: (encr) AT_NONCE_S", |
| data->nonce_s, EAP_SIM_NONCE_S_LEN); |
| |
| eap_sim_derive_keys_reauth(data->counter, |
| 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); |
| eap_sim_learn_ids(sm, data, &eattr); |
| |
| if (data->result_ind && attr->result_ind) |
| data->use_result_ind = 1; |
| |
| if (data->state != FAILURE) { |
| eap_sim_state(data, data->use_result_ind ? |
| RESULT_SUCCESS : SUCCESS); |
| } |
| |
| data->num_id_req = 0; |
| data->num_notification = 0; |
| 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); |
| } |
| os_free(decrypted); |
| return eap_sim_response_reauth(data, id, 0, data->nonce_s); |
| } |
| |
| |
| static struct wpabuf * eap_sim_process(struct eap_sm *sm, void *priv, |
| struct eap_method_ret *ret, |
| const struct wpabuf *reqData) |
| { |
| struct eap_sim_data *data = priv; |
| const struct eap_hdr *req; |
| u8 subtype, id; |
| struct wpabuf *res; |
| const u8 *pos; |
| struct eap_sim_attrs attr; |
| size_t len; |
| |
| wpa_hexdump_buf(MSG_DEBUG, "EAP-SIM: EAP data", reqData); |
| if (eap_get_config_identity(sm, &len) == NULL) { |
| wpa_printf(MSG_INFO, "EAP-SIM: Identity not configured"); |
| eap_sm_request_identity(sm); |
| ret->ignore = true; |
| return NULL; |
| } |
| |
| pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_SIM, reqData, &len); |
| if (pos == NULL || len < 3) { |
| ret->ignore = true; |
| return NULL; |
| } |
| req = wpabuf_head(reqData); |
| id = req->identifier; |
| len = be_to_host16(req->length); |
| |
| ret->ignore = false; |
| ret->methodState = METHOD_MAY_CONT; |
| ret->decision = DECISION_FAIL; |
| ret->allowNotifications = true; |
| |
| subtype = *pos++; |
| wpa_printf(MSG_DEBUG, "EAP-SIM: Subtype=%d", subtype); |
| pos += 2; /* Reserved */ |
| |
| if (eap_sim_parse_attr(pos, wpabuf_head_u8(reqData) + len, &attr, 0, |
| 0)) { |
| res = eap_sim_client_error(data, id, |
| EAP_SIM_UNABLE_TO_PROCESS_PACKET); |
| goto done; |
| } |
| |
| switch (subtype) { |
| case EAP_SIM_SUBTYPE_START: |
| res = eap_sim_process_start(sm, data, id, &attr); |
| break; |
| case EAP_SIM_SUBTYPE_CHALLENGE: |
| res = eap_sim_process_challenge(sm, data, id, reqData, &attr); |
| break; |
| case EAP_SIM_SUBTYPE_NOTIFICATION: |
| res = eap_sim_process_notification(sm, data, id, reqData, |
| &attr); |
| break; |
| case EAP_SIM_SUBTYPE_REAUTHENTICATION: |
| res = eap_sim_process_reauthentication(sm, data, id, reqData, |
| &attr); |
| break; |
| case EAP_SIM_SUBTYPE_CLIENT_ERROR: |
| wpa_printf(MSG_DEBUG, "EAP-SIM: subtype Client-Error"); |
| res = eap_sim_client_error(data, id, |
| EAP_SIM_UNABLE_TO_PROCESS_PACKET); |
| break; |
| default: |
| wpa_printf(MSG_DEBUG, "EAP-SIM: Unknown subtype=%d", subtype); |
| res = eap_sim_client_error(data, id, |
| EAP_SIM_UNABLE_TO_PROCESS_PACKET); |
| break; |
| } |
| |
| done: |
| if (data->state == FAILURE) { |
| ret->decision = DECISION_FAIL; |
| ret->methodState = METHOD_DONE; |
| } else if (data->state == SUCCESS) { |
| ret->decision = data->use_result_ind ? |
| DECISION_UNCOND_SUCC : DECISION_COND_SUCC; |
| ret->methodState = data->use_result_ind ? |
| METHOD_DONE : METHOD_MAY_CONT; |
| } else if (data->state == RESULT_SUCCESS) |
| ret->methodState = METHOD_CONT; |
| |
| if (ret->methodState == METHOD_DONE) { |
| ret->allowNotifications = false; |
| } |
| |
| return res; |
| } |
| |
| |
| static bool eap_sim_has_reauth_data(struct eap_sm *sm, void *priv) |
| { |
| struct eap_sim_data *data = priv; |
| return data->pseudonym || data->reauth_id; |
| } |
| |
| |
| static void eap_sim_deinit_for_reauth(struct eap_sm *sm, void *priv) |
| { |
| struct eap_sim_data *data = priv; |
| |
| 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); |
| } |
| |
| |
| static void * eap_sim_init_for_reauth(struct eap_sm *sm, void *priv) |
| { |
| struct eap_sim_data *data = priv; |
| if (random_get_bytes(data->nonce_mt, EAP_SIM_NONCE_MT_LEN)) { |
| wpa_printf(MSG_WARNING, "EAP-SIM: Failed to get random data " |
| "for NONCE_MT"); |
| 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); |
| return priv; |
| } |
| |
| |
| static const u8 * eap_sim_get_identity(struct eap_sm *sm, void *priv, |
| size_t *len) |
| { |
| struct eap_sim_data *data = priv; |
| |
| if (data->reauth_id) { |
| *len = data->reauth_id_len; |
| return data->reauth_id; |
| } |
| |
| if (data->pseudonym) { |
| *len = data->pseudonym_len; |
| return data->pseudonym; |
| } |
| |
| return NULL; |
| } |
| |
| |
| static bool eap_sim_isKeyAvailable(struct eap_sm *sm, void *priv) |
| { |
| struct eap_sim_data *data = priv; |
| return data->state == SUCCESS; |
| } |
| |
| |
| static u8 * eap_sim_getKey(struct eap_sm *sm, void *priv, size_t *len) |
| { |
| struct eap_sim_data *data = priv; |
| u8 *key; |
| |
| if (data->state != SUCCESS) |
| return NULL; |
| |
| key = os_memdup(data->msk, EAP_SIM_KEYING_DATA_LEN); |
| if (key == NULL) |
| return NULL; |
| |
| *len = EAP_SIM_KEYING_DATA_LEN; |
| |
| return key; |
| } |
| |
| |
| static u8 * eap_sim_get_session_id(struct eap_sm *sm, void *priv, size_t *len) |
| { |
| struct eap_sim_data *data = priv; |
| u8 *id; |
| |
| if (data->state != SUCCESS) |
| return NULL; |
| |
| if (!data->reauth) |
| *len = 1 + data->num_chal * GSM_RAND_LEN + EAP_SIM_NONCE_MT_LEN; |
| else |
| *len = 1 + EAP_SIM_NONCE_S_LEN + EAP_SIM_MAC_LEN; |
| id = os_malloc(*len); |
| if (id == NULL) |
| return NULL; |
| |
| id[0] = EAP_TYPE_SIM; |
| if (!data->reauth) { |
| os_memcpy(id + 1, data->rand, data->num_chal * GSM_RAND_LEN); |
| os_memcpy(id + 1 + data->num_chal * GSM_RAND_LEN, |
| data->nonce_mt, EAP_SIM_NONCE_MT_LEN); |
| } else { |
| os_memcpy(id + 1, data->nonce_s, EAP_SIM_NONCE_S_LEN); |
| os_memcpy(id + 1 + EAP_SIM_NONCE_S_LEN, data->reauth_mac, |
| EAP_SIM_MAC_LEN); |
| } |
| wpa_hexdump(MSG_DEBUG, "EAP-SIM: Derived Session-Id", id, *len); |
| |
| return id; |
| } |
| |
| |
| static u8 * eap_sim_get_emsk(struct eap_sm *sm, void *priv, size_t *len) |
| { |
| struct eap_sim_data *data = priv; |
| u8 *key; |
| |
| if (data->state != SUCCESS) |
| return NULL; |
| |
| key = os_memdup(data->emsk, EAP_EMSK_LEN); |
| if (key == NULL) |
| return NULL; |
| |
| *len = EAP_EMSK_LEN; |
| |
| return key; |
| } |
| |
| |
| static int eap_sim_get_error_code(void *priv) |
| { |
| struct eap_sim_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_sim_register(void) |
| { |
| struct eap_method *eap; |
| |
| eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION, |
| EAP_VENDOR_IETF, EAP_TYPE_SIM, "SIM"); |
| if (eap == NULL) |
| return -1; |
| |
| eap->init = eap_sim_init; |
| eap->deinit = eap_sim_deinit; |
| eap->process = eap_sim_process; |
| eap->isKeyAvailable = eap_sim_isKeyAvailable; |
| eap->getKey = eap_sim_getKey; |
| eap->getSessionId = eap_sim_get_session_id; |
| eap->has_reauth_data = eap_sim_has_reauth_data; |
| eap->deinit_for_reauth = eap_sim_deinit_for_reauth; |
| eap->init_for_reauth = eap_sim_init_for_reauth; |
| eap->get_identity = eap_sim_get_identity; |
| eap->get_emsk = eap_sim_get_emsk; |
| eap->get_error_code = eap_sim_get_error_code; |
| |
| return eap_peer_method_register(eap); |
| } |