1e28a4053SRui Paulo /* 2e28a4053SRui Paulo * hostapd / EAP-SIM (RFC 4186) 3f05cddf9SRui Paulo * Copyright (c) 2005-2012, Jouni Malinen <j@w1.fi> 4e28a4053SRui Paulo * 5f05cddf9SRui Paulo * This software may be distributed under the terms of the BSD license. 6f05cddf9SRui Paulo * See README for more details. 7e28a4053SRui Paulo */ 8e28a4053SRui Paulo 9e28a4053SRui Paulo #include "includes.h" 10e28a4053SRui Paulo 11e28a4053SRui Paulo #include "common.h" 12*a90b9d01SCy Schubert #include "utils/base64.h" 13*a90b9d01SCy Schubert #include "crypto/crypto.h" 14f05cddf9SRui Paulo #include "crypto/random.h" 15e28a4053SRui Paulo #include "eap_server/eap_i.h" 16e28a4053SRui Paulo #include "eap_common/eap_sim_common.h" 17e28a4053SRui Paulo #include "eap_server/eap_sim_db.h" 18e28a4053SRui Paulo 19e28a4053SRui Paulo 20e28a4053SRui Paulo struct eap_sim_data { 21e28a4053SRui Paulo u8 mk[EAP_SIM_MK_LEN]; 22e28a4053SRui Paulo u8 nonce_mt[EAP_SIM_NONCE_MT_LEN]; 23e28a4053SRui Paulo u8 nonce_s[EAP_SIM_NONCE_S_LEN]; 24e28a4053SRui Paulo u8 k_aut[EAP_SIM_K_AUT_LEN]; 25e28a4053SRui Paulo u8 k_encr[EAP_SIM_K_ENCR_LEN]; 26e28a4053SRui Paulo u8 msk[EAP_SIM_KEYING_DATA_LEN]; 27e28a4053SRui Paulo u8 emsk[EAP_EMSK_LEN]; 28e28a4053SRui Paulo u8 kc[EAP_SIM_MAX_CHAL][EAP_SIM_KC_LEN]; 29e28a4053SRui Paulo u8 sres[EAP_SIM_MAX_CHAL][EAP_SIM_SRES_LEN]; 30e28a4053SRui Paulo u8 rand[EAP_SIM_MAX_CHAL][GSM_RAND_LEN]; 31206b73d0SCy Schubert u8 reauth_mac[EAP_SIM_MAC_LEN]; 32e28a4053SRui Paulo int num_chal; 33e28a4053SRui Paulo enum { 34e28a4053SRui Paulo START, CHALLENGE, REAUTH, NOTIFICATION, SUCCESS, FAILURE 35e28a4053SRui Paulo } state; 36e28a4053SRui Paulo char *next_pseudonym; 37e28a4053SRui Paulo char *next_reauth_id; 38e28a4053SRui Paulo u16 counter; 39e28a4053SRui Paulo struct eap_sim_reauth *reauth; 40e28a4053SRui Paulo u16 notification; 41e28a4053SRui Paulo int use_result_ind; 42f05cddf9SRui Paulo int start_round; 43f05cddf9SRui Paulo char permanent[20]; /* Permanent username */ 44e28a4053SRui Paulo }; 45e28a4053SRui Paulo 46e28a4053SRui Paulo 47e28a4053SRui Paulo static const char * eap_sim_state_txt(int state) 48e28a4053SRui Paulo { 49e28a4053SRui Paulo switch (state) { 50e28a4053SRui Paulo case START: 51e28a4053SRui Paulo return "START"; 52e28a4053SRui Paulo case CHALLENGE: 53e28a4053SRui Paulo return "CHALLENGE"; 54e28a4053SRui Paulo case REAUTH: 55e28a4053SRui Paulo return "REAUTH"; 56e28a4053SRui Paulo case SUCCESS: 57e28a4053SRui Paulo return "SUCCESS"; 58e28a4053SRui Paulo case FAILURE: 59e28a4053SRui Paulo return "FAILURE"; 60e28a4053SRui Paulo case NOTIFICATION: 61e28a4053SRui Paulo return "NOTIFICATION"; 62e28a4053SRui Paulo default: 63e28a4053SRui Paulo return "Unknown?!"; 64e28a4053SRui Paulo } 65e28a4053SRui Paulo } 66e28a4053SRui Paulo 67e28a4053SRui Paulo 68e28a4053SRui Paulo static void eap_sim_state(struct eap_sim_data *data, int state) 69e28a4053SRui Paulo { 70e28a4053SRui Paulo wpa_printf(MSG_DEBUG, "EAP-SIM: %s -> %s", 71e28a4053SRui Paulo eap_sim_state_txt(data->state), 72e28a4053SRui Paulo eap_sim_state_txt(state)); 73e28a4053SRui Paulo data->state = state; 74e28a4053SRui Paulo } 75e28a4053SRui Paulo 76e28a4053SRui Paulo 77e28a4053SRui Paulo static void * eap_sim_init(struct eap_sm *sm) 78e28a4053SRui Paulo { 79e28a4053SRui Paulo struct eap_sim_data *data; 80e28a4053SRui Paulo 81c1d255d3SCy Schubert if (!sm->cfg->eap_sim_db_priv) { 82e28a4053SRui Paulo wpa_printf(MSG_WARNING, "EAP-SIM: eap_sim_db not configured"); 83e28a4053SRui Paulo return NULL; 84e28a4053SRui Paulo } 85e28a4053SRui Paulo 86e28a4053SRui Paulo data = os_zalloc(sizeof(*data)); 87e28a4053SRui Paulo if (data == NULL) 88e28a4053SRui Paulo return NULL; 89e28a4053SRui Paulo data->state = START; 90e28a4053SRui Paulo 91e28a4053SRui Paulo return data; 92e28a4053SRui Paulo } 93e28a4053SRui Paulo 94e28a4053SRui Paulo 95e28a4053SRui Paulo static void eap_sim_reset(struct eap_sm *sm, void *priv) 96e28a4053SRui Paulo { 97e28a4053SRui Paulo struct eap_sim_data *data = priv; 98e28a4053SRui Paulo os_free(data->next_pseudonym); 99e28a4053SRui Paulo os_free(data->next_reauth_id); 1005b9c547cSRui Paulo bin_clear_free(data, sizeof(*data)); 101e28a4053SRui Paulo } 102e28a4053SRui Paulo 103e28a4053SRui Paulo 104e28a4053SRui Paulo static struct wpabuf * eap_sim_build_start(struct eap_sm *sm, 105e28a4053SRui Paulo struct eap_sim_data *data, u8 id) 106e28a4053SRui Paulo { 107e28a4053SRui Paulo struct eap_sim_msg *msg; 108e28a4053SRui Paulo u8 ver[2]; 109*a90b9d01SCy Schubert bool id_req = true; 110e28a4053SRui Paulo 111e28a4053SRui Paulo wpa_printf(MSG_DEBUG, "EAP-SIM: Generating Start"); 112e28a4053SRui Paulo msg = eap_sim_msg_init(EAP_CODE_REQUEST, id, EAP_TYPE_SIM, 113e28a4053SRui Paulo EAP_SIM_SUBTYPE_START); 114f05cddf9SRui Paulo data->start_round++; 115*a90b9d01SCy Schubert 116*a90b9d01SCy Schubert if (data->start_round == 1 && (sm->cfg->eap_sim_id & 0x04)) { 117*a90b9d01SCy Schubert char *username; 118*a90b9d01SCy Schubert 119*a90b9d01SCy Schubert username = sim_get_username(sm->identity, sm->identity_len); 120*a90b9d01SCy Schubert if (username && username[0] == EAP_SIM_REAUTH_ID_PREFIX && 121*a90b9d01SCy Schubert eap_sim_db_get_reauth_entry(sm->cfg->eap_sim_db_priv, 122*a90b9d01SCy Schubert username)) 123*a90b9d01SCy Schubert id_req = false; 124*a90b9d01SCy Schubert 125*a90b9d01SCy Schubert os_free(username); 126*a90b9d01SCy Schubert } 127*a90b9d01SCy Schubert 128*a90b9d01SCy Schubert if (!id_req) { 129*a90b9d01SCy Schubert wpa_printf(MSG_DEBUG, " No identity request"); 130*a90b9d01SCy Schubert } else if (data->start_round == 1) { 131e28a4053SRui Paulo /* 132e28a4053SRui Paulo * RFC 4186, Chap. 4.2.4 recommends that identity from EAP is 133e28a4053SRui Paulo * ignored and the SIM/Start is used to request the identity. 134e28a4053SRui Paulo */ 135e28a4053SRui Paulo wpa_printf(MSG_DEBUG, " AT_ANY_ID_REQ"); 136e28a4053SRui Paulo eap_sim_msg_add(msg, EAP_SIM_AT_ANY_ID_REQ, 0, NULL, 0); 137f05cddf9SRui Paulo } else if (data->start_round > 3) { 138f05cddf9SRui Paulo /* Cannot use more than three rounds of Start messages */ 139f05cddf9SRui Paulo eap_sim_msg_free(msg); 140f05cddf9SRui Paulo return NULL; 141f05cddf9SRui Paulo } else if (data->start_round == 0) { 142f05cddf9SRui Paulo /* 143f05cddf9SRui Paulo * This is a special case that is used to recover from 144f05cddf9SRui Paulo * AT_COUNTER_TOO_SMALL during re-authentication. Since we 145f05cddf9SRui Paulo * already know the identity of the peer, there is no need to 146f05cddf9SRui Paulo * request any identity in this case. 147f05cddf9SRui Paulo */ 148f05cddf9SRui Paulo } else if (sm->identity && sm->identity_len > 0 && 149f05cddf9SRui Paulo sm->identity[0] == EAP_SIM_REAUTH_ID_PREFIX) { 150f05cddf9SRui Paulo /* Reauth id may have expired - try fullauth */ 151f05cddf9SRui Paulo wpa_printf(MSG_DEBUG, " AT_FULLAUTH_ID_REQ"); 152f05cddf9SRui Paulo eap_sim_msg_add(msg, EAP_SIM_AT_FULLAUTH_ID_REQ, 0, NULL, 0); 153f05cddf9SRui Paulo } else { 154f05cddf9SRui Paulo wpa_printf(MSG_DEBUG, " AT_PERMANENT_ID_REQ"); 155f05cddf9SRui Paulo eap_sim_msg_add(msg, EAP_SIM_AT_PERMANENT_ID_REQ, 0, NULL, 0); 156e28a4053SRui Paulo } 157e28a4053SRui Paulo wpa_printf(MSG_DEBUG, " AT_VERSION_LIST"); 158e28a4053SRui Paulo ver[0] = 0; 159e28a4053SRui Paulo ver[1] = EAP_SIM_VERSION; 160e28a4053SRui Paulo eap_sim_msg_add(msg, EAP_SIM_AT_VERSION_LIST, sizeof(ver), 161e28a4053SRui Paulo ver, sizeof(ver)); 1625b9c547cSRui Paulo return eap_sim_msg_finish(msg, EAP_TYPE_SIM, NULL, NULL, 0); 163e28a4053SRui Paulo } 164e28a4053SRui Paulo 165e28a4053SRui Paulo 166e28a4053SRui Paulo static int eap_sim_build_encr(struct eap_sm *sm, struct eap_sim_data *data, 167e28a4053SRui Paulo struct eap_sim_msg *msg, u16 counter, 168e28a4053SRui Paulo const u8 *nonce_s) 169e28a4053SRui Paulo { 170e28a4053SRui Paulo os_free(data->next_pseudonym); 171c1d255d3SCy Schubert if (!(sm->cfg->eap_sim_id & 0x01)) { 172206b73d0SCy Schubert /* Use of pseudonyms disabled in configuration */ 173206b73d0SCy Schubert data->next_pseudonym = NULL; 174206b73d0SCy Schubert } else if (!nonce_s) { 175e28a4053SRui Paulo data->next_pseudonym = 176c1d255d3SCy Schubert eap_sim_db_get_next_pseudonym(sm->cfg->eap_sim_db_priv, 177f05cddf9SRui Paulo EAP_SIM_DB_SIM); 178f05cddf9SRui Paulo } else { 179f05cddf9SRui Paulo /* Do not update pseudonym during re-authentication */ 180f05cddf9SRui Paulo data->next_pseudonym = NULL; 181f05cddf9SRui Paulo } 182e28a4053SRui Paulo os_free(data->next_reauth_id); 183c1d255d3SCy Schubert if (!(sm->cfg->eap_sim_id & 0x02)) { 184206b73d0SCy Schubert /* Use of fast reauth disabled in configuration */ 185206b73d0SCy Schubert data->next_reauth_id = NULL; 186206b73d0SCy Schubert } else if (data->counter <= EAP_SIM_MAX_FAST_REAUTHS) { 187e28a4053SRui Paulo data->next_reauth_id = 188c1d255d3SCy Schubert eap_sim_db_get_next_reauth_id(sm->cfg->eap_sim_db_priv, 189f05cddf9SRui Paulo EAP_SIM_DB_SIM); 190e28a4053SRui Paulo } else { 191e28a4053SRui Paulo wpa_printf(MSG_DEBUG, "EAP-SIM: Max fast re-authentication " 192e28a4053SRui Paulo "count exceeded - force full authentication"); 193e28a4053SRui Paulo data->next_reauth_id = NULL; 194e28a4053SRui Paulo } 195e28a4053SRui Paulo 196e28a4053SRui Paulo if (data->next_pseudonym == NULL && data->next_reauth_id == NULL && 197e28a4053SRui Paulo counter == 0 && nonce_s == NULL) 198e28a4053SRui Paulo return 0; 199e28a4053SRui Paulo 200e28a4053SRui Paulo wpa_printf(MSG_DEBUG, " AT_IV"); 201e28a4053SRui Paulo wpa_printf(MSG_DEBUG, " AT_ENCR_DATA"); 202e28a4053SRui Paulo eap_sim_msg_add_encr_start(msg, EAP_SIM_AT_IV, EAP_SIM_AT_ENCR_DATA); 203e28a4053SRui Paulo 204e28a4053SRui Paulo if (counter > 0) { 205e28a4053SRui Paulo wpa_printf(MSG_DEBUG, " *AT_COUNTER (%u)", counter); 206e28a4053SRui Paulo eap_sim_msg_add(msg, EAP_SIM_AT_COUNTER, counter, NULL, 0); 207e28a4053SRui Paulo } 208e28a4053SRui Paulo 209e28a4053SRui Paulo if (nonce_s) { 210e28a4053SRui Paulo wpa_printf(MSG_DEBUG, " *AT_NONCE_S"); 211e28a4053SRui Paulo eap_sim_msg_add(msg, EAP_SIM_AT_NONCE_S, 0, nonce_s, 212e28a4053SRui Paulo EAP_SIM_NONCE_S_LEN); 213e28a4053SRui Paulo } 214e28a4053SRui Paulo 215e28a4053SRui Paulo if (data->next_pseudonym) { 216e28a4053SRui Paulo wpa_printf(MSG_DEBUG, " *AT_NEXT_PSEUDONYM (%s)", 217e28a4053SRui Paulo data->next_pseudonym); 218e28a4053SRui Paulo eap_sim_msg_add(msg, EAP_SIM_AT_NEXT_PSEUDONYM, 219e28a4053SRui Paulo os_strlen(data->next_pseudonym), 220e28a4053SRui Paulo (u8 *) data->next_pseudonym, 221e28a4053SRui Paulo os_strlen(data->next_pseudonym)); 222e28a4053SRui Paulo } 223e28a4053SRui Paulo 224e28a4053SRui Paulo if (data->next_reauth_id) { 225e28a4053SRui Paulo wpa_printf(MSG_DEBUG, " *AT_NEXT_REAUTH_ID (%s)", 226e28a4053SRui Paulo data->next_reauth_id); 227e28a4053SRui Paulo eap_sim_msg_add(msg, EAP_SIM_AT_NEXT_REAUTH_ID, 228e28a4053SRui Paulo os_strlen(data->next_reauth_id), 229e28a4053SRui Paulo (u8 *) data->next_reauth_id, 230e28a4053SRui Paulo os_strlen(data->next_reauth_id)); 231e28a4053SRui Paulo } 232e28a4053SRui Paulo 233e28a4053SRui Paulo if (eap_sim_msg_add_encr_end(msg, data->k_encr, EAP_SIM_AT_PADDING)) { 234e28a4053SRui Paulo wpa_printf(MSG_WARNING, "EAP-SIM: Failed to encrypt " 235e28a4053SRui Paulo "AT_ENCR_DATA"); 236e28a4053SRui Paulo return -1; 237e28a4053SRui Paulo } 238e28a4053SRui Paulo 239e28a4053SRui Paulo return 0; 240e28a4053SRui Paulo } 241e28a4053SRui Paulo 242e28a4053SRui Paulo 243e28a4053SRui Paulo static struct wpabuf * eap_sim_build_challenge(struct eap_sm *sm, 244e28a4053SRui Paulo struct eap_sim_data *data, 245e28a4053SRui Paulo u8 id) 246e28a4053SRui Paulo { 247e28a4053SRui Paulo struct eap_sim_msg *msg; 248e28a4053SRui Paulo 249e28a4053SRui Paulo wpa_printf(MSG_DEBUG, "EAP-SIM: Generating Challenge"); 250e28a4053SRui Paulo msg = eap_sim_msg_init(EAP_CODE_REQUEST, id, EAP_TYPE_SIM, 251e28a4053SRui Paulo EAP_SIM_SUBTYPE_CHALLENGE); 252e28a4053SRui Paulo wpa_printf(MSG_DEBUG, " AT_RAND"); 253e28a4053SRui Paulo eap_sim_msg_add(msg, EAP_SIM_AT_RAND, 0, (u8 *) data->rand, 254e28a4053SRui Paulo data->num_chal * GSM_RAND_LEN); 255e28a4053SRui Paulo 256e28a4053SRui Paulo if (eap_sim_build_encr(sm, data, msg, 0, NULL)) { 257e28a4053SRui Paulo eap_sim_msg_free(msg); 258e28a4053SRui Paulo return NULL; 259e28a4053SRui Paulo } 260e28a4053SRui Paulo 261c1d255d3SCy Schubert if (sm->cfg->eap_sim_aka_result_ind) { 262e28a4053SRui Paulo wpa_printf(MSG_DEBUG, " AT_RESULT_IND"); 263e28a4053SRui Paulo eap_sim_msg_add(msg, EAP_SIM_AT_RESULT_IND, 0, NULL, 0); 264e28a4053SRui Paulo } 265e28a4053SRui Paulo 266e28a4053SRui Paulo wpa_printf(MSG_DEBUG, " AT_MAC"); 267e28a4053SRui Paulo eap_sim_msg_add_mac(msg, EAP_SIM_AT_MAC); 2685b9c547cSRui Paulo return eap_sim_msg_finish(msg, EAP_TYPE_SIM, data->k_aut, 2695b9c547cSRui Paulo data->nonce_mt, EAP_SIM_NONCE_MT_LEN); 270e28a4053SRui Paulo } 271e28a4053SRui Paulo 272e28a4053SRui Paulo 273e28a4053SRui Paulo static struct wpabuf * eap_sim_build_reauth(struct eap_sm *sm, 274e28a4053SRui Paulo struct eap_sim_data *data, u8 id) 275e28a4053SRui Paulo { 276e28a4053SRui Paulo struct eap_sim_msg *msg; 277206b73d0SCy Schubert struct wpabuf *buf; 278e28a4053SRui Paulo 279e28a4053SRui Paulo wpa_printf(MSG_DEBUG, "EAP-SIM: Generating Re-authentication"); 280e28a4053SRui Paulo 281f05cddf9SRui Paulo if (random_get_bytes(data->nonce_s, EAP_SIM_NONCE_S_LEN)) 282e28a4053SRui Paulo return NULL; 283e28a4053SRui Paulo wpa_hexdump_key(MSG_MSGDUMP, "EAP-SIM: NONCE_S", 284e28a4053SRui Paulo data->nonce_s, EAP_SIM_NONCE_S_LEN); 285e28a4053SRui Paulo 286e28a4053SRui Paulo eap_sim_derive_keys(data->mk, data->k_encr, data->k_aut, data->msk, 287e28a4053SRui Paulo data->emsk); 288e28a4053SRui Paulo eap_sim_derive_keys_reauth(data->counter, sm->identity, 289e28a4053SRui Paulo sm->identity_len, data->nonce_s, data->mk, 290e28a4053SRui Paulo data->msk, data->emsk); 291e28a4053SRui Paulo 292e28a4053SRui Paulo msg = eap_sim_msg_init(EAP_CODE_REQUEST, id, EAP_TYPE_SIM, 293e28a4053SRui Paulo EAP_SIM_SUBTYPE_REAUTHENTICATION); 294e28a4053SRui Paulo 295e28a4053SRui Paulo if (eap_sim_build_encr(sm, data, msg, data->counter, data->nonce_s)) { 296e28a4053SRui Paulo eap_sim_msg_free(msg); 297e28a4053SRui Paulo return NULL; 298e28a4053SRui Paulo } 299e28a4053SRui Paulo 300c1d255d3SCy Schubert if (sm->cfg->eap_sim_aka_result_ind) { 301e28a4053SRui Paulo wpa_printf(MSG_DEBUG, " AT_RESULT_IND"); 302e28a4053SRui Paulo eap_sim_msg_add(msg, EAP_SIM_AT_RESULT_IND, 0, NULL, 0); 303e28a4053SRui Paulo } 304e28a4053SRui Paulo 305e28a4053SRui Paulo wpa_printf(MSG_DEBUG, " AT_MAC"); 306e28a4053SRui Paulo eap_sim_msg_add_mac(msg, EAP_SIM_AT_MAC); 307206b73d0SCy Schubert buf = eap_sim_msg_finish(msg, EAP_TYPE_SIM, data->k_aut, NULL, 0); 308206b73d0SCy Schubert 309206b73d0SCy Schubert /* Remember this MAC before sending it to the peer. This MAC is used for 310206b73d0SCy Schubert * Session-Id calculation after receiving response from the peer and 311206b73d0SCy Schubert * after all other checks pass. */ 312206b73d0SCy Schubert os_memcpy(data->reauth_mac, 313206b73d0SCy Schubert wpabuf_head_u8(buf) + wpabuf_len(buf) - EAP_SIM_MAC_LEN, 314206b73d0SCy Schubert EAP_SIM_MAC_LEN); 315206b73d0SCy Schubert 316206b73d0SCy Schubert return buf; 317e28a4053SRui Paulo } 318e28a4053SRui Paulo 319e28a4053SRui Paulo 320e28a4053SRui Paulo static struct wpabuf * eap_sim_build_notification(struct eap_sm *sm, 321e28a4053SRui Paulo struct eap_sim_data *data, 322e28a4053SRui Paulo u8 id) 323e28a4053SRui Paulo { 324e28a4053SRui Paulo struct eap_sim_msg *msg; 325e28a4053SRui Paulo 326e28a4053SRui Paulo wpa_printf(MSG_DEBUG, "EAP-SIM: Generating Notification"); 327e28a4053SRui Paulo msg = eap_sim_msg_init(EAP_CODE_REQUEST, id, EAP_TYPE_SIM, 328e28a4053SRui Paulo EAP_SIM_SUBTYPE_NOTIFICATION); 329e28a4053SRui Paulo wpa_printf(MSG_DEBUG, " AT_NOTIFICATION (%d)", data->notification); 330e28a4053SRui Paulo eap_sim_msg_add(msg, EAP_SIM_AT_NOTIFICATION, data->notification, 331e28a4053SRui Paulo NULL, 0); 332e28a4053SRui Paulo if (data->use_result_ind) { 333e28a4053SRui Paulo if (data->reauth) { 334e28a4053SRui Paulo wpa_printf(MSG_DEBUG, " AT_IV"); 335e28a4053SRui Paulo wpa_printf(MSG_DEBUG, " AT_ENCR_DATA"); 336e28a4053SRui Paulo eap_sim_msg_add_encr_start(msg, EAP_SIM_AT_IV, 337e28a4053SRui Paulo EAP_SIM_AT_ENCR_DATA); 338e28a4053SRui Paulo wpa_printf(MSG_DEBUG, " *AT_COUNTER (%u)", 339e28a4053SRui Paulo data->counter); 340e28a4053SRui Paulo eap_sim_msg_add(msg, EAP_SIM_AT_COUNTER, data->counter, 341e28a4053SRui Paulo NULL, 0); 342e28a4053SRui Paulo 343e28a4053SRui Paulo if (eap_sim_msg_add_encr_end(msg, data->k_encr, 344e28a4053SRui Paulo EAP_SIM_AT_PADDING)) { 345e28a4053SRui Paulo wpa_printf(MSG_WARNING, "EAP-SIM: Failed to " 346e28a4053SRui Paulo "encrypt AT_ENCR_DATA"); 347e28a4053SRui Paulo eap_sim_msg_free(msg); 348e28a4053SRui Paulo return NULL; 349e28a4053SRui Paulo } 350e28a4053SRui Paulo } 351e28a4053SRui Paulo 352e28a4053SRui Paulo wpa_printf(MSG_DEBUG, " AT_MAC"); 353e28a4053SRui Paulo eap_sim_msg_add_mac(msg, EAP_SIM_AT_MAC); 354e28a4053SRui Paulo } 3555b9c547cSRui Paulo return eap_sim_msg_finish(msg, EAP_TYPE_SIM, data->k_aut, NULL, 0); 356e28a4053SRui Paulo } 357e28a4053SRui Paulo 358e28a4053SRui Paulo 359e28a4053SRui Paulo static struct wpabuf * eap_sim_buildReq(struct eap_sm *sm, void *priv, u8 id) 360e28a4053SRui Paulo { 361e28a4053SRui Paulo struct eap_sim_data *data = priv; 362e28a4053SRui Paulo 363e28a4053SRui Paulo switch (data->state) { 364e28a4053SRui Paulo case START: 365e28a4053SRui Paulo return eap_sim_build_start(sm, data, id); 366e28a4053SRui Paulo case CHALLENGE: 367e28a4053SRui Paulo return eap_sim_build_challenge(sm, data, id); 368e28a4053SRui Paulo case REAUTH: 369e28a4053SRui Paulo return eap_sim_build_reauth(sm, data, id); 370e28a4053SRui Paulo case NOTIFICATION: 371e28a4053SRui Paulo return eap_sim_build_notification(sm, data, id); 372e28a4053SRui Paulo default: 373e28a4053SRui Paulo wpa_printf(MSG_DEBUG, "EAP-SIM: Unknown state %d in " 374e28a4053SRui Paulo "buildReq", data->state); 375e28a4053SRui Paulo break; 376e28a4053SRui Paulo } 377e28a4053SRui Paulo return NULL; 378e28a4053SRui Paulo } 379e28a4053SRui Paulo 380e28a4053SRui Paulo 381c1d255d3SCy Schubert static bool eap_sim_check(struct eap_sm *sm, void *priv, 382e28a4053SRui Paulo struct wpabuf *respData) 383e28a4053SRui Paulo { 384e28a4053SRui Paulo const u8 *pos; 385e28a4053SRui Paulo size_t len; 386e28a4053SRui Paulo 387e28a4053SRui Paulo pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_SIM, respData, &len); 388e28a4053SRui Paulo if (pos == NULL || len < 3) { 389e28a4053SRui Paulo wpa_printf(MSG_INFO, "EAP-SIM: Invalid frame"); 390c1d255d3SCy Schubert return true; 391e28a4053SRui Paulo } 392e28a4053SRui Paulo 393c1d255d3SCy Schubert return false; 394f05cddf9SRui Paulo } 395f05cddf9SRui Paulo 396f05cddf9SRui Paulo 397c1d255d3SCy Schubert static bool eap_sim_unexpected_subtype(struct eap_sim_data *data, 398f05cddf9SRui Paulo u8 subtype) 399f05cddf9SRui Paulo { 400e28a4053SRui Paulo if (subtype == EAP_SIM_SUBTYPE_CLIENT_ERROR) 401c1d255d3SCy Schubert return false; 402e28a4053SRui Paulo 403e28a4053SRui Paulo switch (data->state) { 404e28a4053SRui Paulo case START: 405e28a4053SRui Paulo if (subtype != EAP_SIM_SUBTYPE_START) { 406e28a4053SRui Paulo wpa_printf(MSG_INFO, "EAP-SIM: Unexpected response " 407e28a4053SRui Paulo "subtype %d", subtype); 408c1d255d3SCy Schubert return true; 409e28a4053SRui Paulo } 410e28a4053SRui Paulo break; 411e28a4053SRui Paulo case CHALLENGE: 412e28a4053SRui Paulo if (subtype != EAP_SIM_SUBTYPE_CHALLENGE) { 413e28a4053SRui Paulo wpa_printf(MSG_INFO, "EAP-SIM: Unexpected response " 414e28a4053SRui Paulo "subtype %d", subtype); 415c1d255d3SCy Schubert return true; 416e28a4053SRui Paulo } 417e28a4053SRui Paulo break; 418e28a4053SRui Paulo case REAUTH: 419e28a4053SRui Paulo if (subtype != EAP_SIM_SUBTYPE_REAUTHENTICATION) { 420e28a4053SRui Paulo wpa_printf(MSG_INFO, "EAP-SIM: Unexpected response " 421e28a4053SRui Paulo "subtype %d", subtype); 422c1d255d3SCy Schubert return true; 423e28a4053SRui Paulo } 424e28a4053SRui Paulo break; 425e28a4053SRui Paulo case NOTIFICATION: 426e28a4053SRui Paulo if (subtype != EAP_SIM_SUBTYPE_NOTIFICATION) { 427e28a4053SRui Paulo wpa_printf(MSG_INFO, "EAP-SIM: Unexpected response " 428e28a4053SRui Paulo "subtype %d", subtype); 429c1d255d3SCy Schubert return true; 430e28a4053SRui Paulo } 431e28a4053SRui Paulo break; 432e28a4053SRui Paulo default: 433e28a4053SRui Paulo wpa_printf(MSG_INFO, "EAP-SIM: Unexpected state (%d) for " 434e28a4053SRui Paulo "processing a response", data->state); 435c1d255d3SCy Schubert return true; 436e28a4053SRui Paulo } 437e28a4053SRui Paulo 438c1d255d3SCy Schubert return false; 439e28a4053SRui Paulo } 440e28a4053SRui Paulo 441e28a4053SRui Paulo 442e28a4053SRui Paulo static int eap_sim_supported_ver(struct eap_sim_data *data, int version) 443e28a4053SRui Paulo { 444e28a4053SRui Paulo return version == EAP_SIM_VERSION; 445e28a4053SRui Paulo } 446e28a4053SRui Paulo 447e28a4053SRui Paulo 448e28a4053SRui Paulo static void eap_sim_process_start(struct eap_sm *sm, 449e28a4053SRui Paulo struct eap_sim_data *data, 450e28a4053SRui Paulo struct wpabuf *respData, 451e28a4053SRui Paulo struct eap_sim_attrs *attr) 452e28a4053SRui Paulo { 453*a90b9d01SCy Schubert const u8 *identity; 454e28a4053SRui Paulo size_t identity_len; 455e28a4053SRui Paulo u8 ver_list[2]; 456f05cddf9SRui Paulo u8 *new_identity; 457f05cddf9SRui Paulo char *username; 458e28a4053SRui Paulo 459e28a4053SRui Paulo wpa_printf(MSG_DEBUG, "EAP-SIM: Receive start response"); 460e28a4053SRui Paulo 461f05cddf9SRui Paulo if (data->start_round == 0) { 462f05cddf9SRui Paulo /* 463f05cddf9SRui Paulo * Special case for AT_COUNTER_TOO_SMALL recovery - no identity 464f05cddf9SRui Paulo * was requested since we already know it. 465f05cddf9SRui Paulo */ 466f05cddf9SRui Paulo goto skip_id_update; 467f05cddf9SRui Paulo } 468f05cddf9SRui Paulo 469*a90b9d01SCy Schubert if ((sm->cfg->eap_sim_id & 0x04) && 470*a90b9d01SCy Schubert (!attr->identity || attr->identity_len == 0)) 471*a90b9d01SCy Schubert goto skip_id_attr; 472*a90b9d01SCy Schubert 473f05cddf9SRui Paulo /* 474*a90b9d01SCy Schubert * Unless explicitly configured otherwise, we always request identity 475*a90b9d01SCy Schubert * in SIM/Start, so the peer is required to have replied with one. 476f05cddf9SRui Paulo */ 477f05cddf9SRui Paulo if (!attr->identity || attr->identity_len == 0) { 478f05cddf9SRui Paulo wpa_printf(MSG_DEBUG, "EAP-SIM: Peer did not provide any " 479f05cddf9SRui Paulo "identity"); 480f05cddf9SRui Paulo goto failed; 481f05cddf9SRui Paulo } 482f05cddf9SRui Paulo 483f05cddf9SRui Paulo new_identity = os_malloc(attr->identity_len); 484f05cddf9SRui Paulo if (new_identity == NULL) 485f05cddf9SRui Paulo goto failed; 486e28a4053SRui Paulo os_free(sm->identity); 487f05cddf9SRui Paulo sm->identity = new_identity; 488f05cddf9SRui Paulo os_memcpy(sm->identity, attr->identity, attr->identity_len); 489e28a4053SRui Paulo sm->identity_len = attr->identity_len; 490e28a4053SRui Paulo 491*a90b9d01SCy Schubert skip_id_attr: 492*a90b9d01SCy Schubert if (sm->sim_aka_permanent[0]) { 493*a90b9d01SCy Schubert identity = (const u8 *) sm->sim_aka_permanent; 494*a90b9d01SCy Schubert identity_len = os_strlen(sm->sim_aka_permanent); 495*a90b9d01SCy Schubert } else { 496*a90b9d01SCy Schubert identity = sm->identity; 497*a90b9d01SCy Schubert identity_len = sm->identity_len; 498*a90b9d01SCy Schubert } 499e28a4053SRui Paulo wpa_hexdump_ascii(MSG_DEBUG, "EAP-SIM: Identity", 500*a90b9d01SCy Schubert identity, identity_len); 501*a90b9d01SCy Schubert username = sim_get_username(identity, identity_len); 502f05cddf9SRui Paulo if (username == NULL) 503f05cddf9SRui Paulo goto failed; 504e28a4053SRui Paulo 505f05cddf9SRui Paulo if (username[0] == EAP_SIM_REAUTH_ID_PREFIX) { 506f05cddf9SRui Paulo wpa_printf(MSG_DEBUG, "EAP-SIM: Reauth username '%s'", 507f05cddf9SRui Paulo username); 508f05cddf9SRui Paulo data->reauth = eap_sim_db_get_reauth_entry( 509c1d255d3SCy Schubert sm->cfg->eap_sim_db_priv, username); 510f05cddf9SRui Paulo os_free(username); 511f05cddf9SRui Paulo if (data->reauth == NULL) { 512f05cddf9SRui Paulo wpa_printf(MSG_DEBUG, "EAP-SIM: Unknown reauth " 513f05cddf9SRui Paulo "identity - request full auth identity"); 514f05cddf9SRui Paulo /* Remain in START state for another round */ 515f05cddf9SRui Paulo return; 516f05cddf9SRui Paulo } 517*a90b9d01SCy Schubert 518*a90b9d01SCy Schubert if (data->reauth->counter > 519*a90b9d01SCy Schubert sm->cfg->eap_sim_aka_fast_reauth_limit && 520*a90b9d01SCy Schubert (sm->cfg->eap_sim_id & 0x04)) { 521*a90b9d01SCy Schubert wpa_printf(MSG_DEBUG, 522*a90b9d01SCy Schubert "EAP-SIM: Too many fast re-authentication attemps - fall back to full authentication"); 523*a90b9d01SCy Schubert wpa_printf(MSG_DEBUG, 524*a90b9d01SCy Schubert "EAP-SIM: Permanent identity recognized - skip new Identity query"); 525*a90b9d01SCy Schubert os_strlcpy(data->permanent, 526*a90b9d01SCy Schubert data->reauth->permanent, 527*a90b9d01SCy Schubert sizeof(data->permanent)); 528*a90b9d01SCy Schubert os_strlcpy(sm->sim_aka_permanent, 529*a90b9d01SCy Schubert data->reauth->permanent, 530*a90b9d01SCy Schubert sizeof(sm->sim_aka_permanent)); 531*a90b9d01SCy Schubert eap_sim_db_remove_reauth( 532*a90b9d01SCy Schubert sm->cfg->eap_sim_db_priv, 533*a90b9d01SCy Schubert data->reauth); 534*a90b9d01SCy Schubert data->reauth = NULL; 535*a90b9d01SCy Schubert goto skip_id_update; 536*a90b9d01SCy Schubert } 537*a90b9d01SCy Schubert 538*a90b9d01SCy Schubert wpa_printf(MSG_DEBUG, 539*a90b9d01SCy Schubert "EAP-SIM: Using fast re-authentication (counter=%d)", 540*a90b9d01SCy Schubert data->reauth->counter); 541f05cddf9SRui Paulo os_strlcpy(data->permanent, data->reauth->permanent, 542f05cddf9SRui Paulo sizeof(data->permanent)); 543f05cddf9SRui Paulo data->counter = data->reauth->counter; 544f05cddf9SRui Paulo os_memcpy(data->mk, data->reauth->mk, EAP_SIM_MK_LEN); 545e28a4053SRui Paulo eap_sim_state(data, REAUTH); 546e28a4053SRui Paulo return; 547e28a4053SRui Paulo } 548e28a4053SRui Paulo 549f05cddf9SRui Paulo if (username[0] == EAP_SIM_PSEUDONYM_PREFIX) { 550f05cddf9SRui Paulo const char *permanent; 551f05cddf9SRui Paulo wpa_printf(MSG_DEBUG, "EAP-SIM: Pseudonym username '%s'", 552f05cddf9SRui Paulo username); 553f05cddf9SRui Paulo permanent = eap_sim_db_get_permanent( 554c1d255d3SCy Schubert sm->cfg->eap_sim_db_priv, username); 555f05cddf9SRui Paulo os_free(username); 556f05cddf9SRui Paulo if (permanent == NULL) { 557f05cddf9SRui Paulo wpa_printf(MSG_DEBUG, "EAP-SIM: Unknown pseudonym " 558f05cddf9SRui Paulo "identity - request permanent identity"); 559f05cddf9SRui Paulo /* Remain in START state for another round */ 560f05cddf9SRui Paulo return; 561f05cddf9SRui Paulo } 562f05cddf9SRui Paulo os_strlcpy(data->permanent, permanent, 563f05cddf9SRui Paulo sizeof(data->permanent)); 564f05cddf9SRui Paulo } else if (username[0] == EAP_SIM_PERMANENT_PREFIX) { 565f05cddf9SRui Paulo wpa_printf(MSG_DEBUG, "EAP-SIM: Permanent username '%s'", 566f05cddf9SRui Paulo username); 567f05cddf9SRui Paulo os_strlcpy(data->permanent, username, sizeof(data->permanent)); 568f05cddf9SRui Paulo os_free(username); 569*a90b9d01SCy Schubert #ifdef CRYPTO_RSA_OAEP_SHA256 570*a90b9d01SCy Schubert } else if (sm->identity_len > 1 && sm->identity[0] == '\0') { 571*a90b9d01SCy Schubert char *enc_id, *pos, *end; 572*a90b9d01SCy Schubert size_t enc_id_len; 573*a90b9d01SCy Schubert u8 *decoded_id; 574*a90b9d01SCy Schubert size_t decoded_id_len; 575*a90b9d01SCy Schubert struct wpabuf *enc, *dec; 576*a90b9d01SCy Schubert u8 *new_id; 577*a90b9d01SCy Schubert 578*a90b9d01SCy Schubert os_free(username); 579*a90b9d01SCy Schubert if (!sm->cfg->imsi_privacy_key) { 580*a90b9d01SCy Schubert wpa_printf(MSG_DEBUG, 581*a90b9d01SCy Schubert "EAP-SIM: Received encrypted identity, but no IMSI privacy key configured to decrypt it"); 582*a90b9d01SCy Schubert goto failed; 583*a90b9d01SCy Schubert } 584*a90b9d01SCy Schubert 585*a90b9d01SCy Schubert enc_id = (char *) &sm->identity[1]; 586*a90b9d01SCy Schubert end = (char *) &sm->identity[sm->identity_len]; 587*a90b9d01SCy Schubert for (pos = enc_id; pos < end; pos++) { 588*a90b9d01SCy Schubert if (*pos == ',') 589*a90b9d01SCy Schubert break; 590*a90b9d01SCy Schubert } 591*a90b9d01SCy Schubert enc_id_len = pos - enc_id; 592*a90b9d01SCy Schubert 593*a90b9d01SCy Schubert wpa_hexdump_ascii(MSG_DEBUG, 594*a90b9d01SCy Schubert "EAP-SIM: Encrypted permanent identity", 595*a90b9d01SCy Schubert enc_id, enc_id_len); 596*a90b9d01SCy Schubert decoded_id = base64_decode(enc_id, enc_id_len, &decoded_id_len); 597*a90b9d01SCy Schubert if (!decoded_id) { 598*a90b9d01SCy Schubert wpa_printf(MSG_DEBUG, 599*a90b9d01SCy Schubert "EAP-SIM: Could not base64 decode encrypted identity"); 600*a90b9d01SCy Schubert goto failed; 601*a90b9d01SCy Schubert } 602*a90b9d01SCy Schubert wpa_hexdump(MSG_DEBUG, 603*a90b9d01SCy Schubert "EAP-SIM: Decoded encrypted permanent identity", 604*a90b9d01SCy Schubert decoded_id, decoded_id_len); 605*a90b9d01SCy Schubert enc = wpabuf_alloc_copy(decoded_id, decoded_id_len); 606*a90b9d01SCy Schubert os_free(decoded_id); 607*a90b9d01SCy Schubert if (!enc) 608*a90b9d01SCy Schubert goto failed; 609*a90b9d01SCy Schubert dec = crypto_rsa_oaep_sha256_decrypt(sm->cfg->imsi_privacy_key, 610*a90b9d01SCy Schubert enc); 611*a90b9d01SCy Schubert wpabuf_free(enc); 612*a90b9d01SCy Schubert if (!dec) { 613*a90b9d01SCy Schubert wpa_printf(MSG_DEBUG, 614*a90b9d01SCy Schubert "EAP-SIM: Failed to decrypt encrypted identity"); 615*a90b9d01SCy Schubert goto failed; 616*a90b9d01SCy Schubert } 617*a90b9d01SCy Schubert wpa_hexdump_ascii(MSG_DEBUG, "EAP-SIM: Decrypted permanent identity", 618*a90b9d01SCy Schubert wpabuf_head(dec), wpabuf_len(dec)); 619*a90b9d01SCy Schubert username = sim_get_username(wpabuf_head(dec), wpabuf_len(dec)); 620*a90b9d01SCy Schubert if (!username) { 621*a90b9d01SCy Schubert wpabuf_free(dec); 622*a90b9d01SCy Schubert goto failed; 623*a90b9d01SCy Schubert } 624*a90b9d01SCy Schubert new_id = os_memdup(wpabuf_head(dec), wpabuf_len(dec)); 625*a90b9d01SCy Schubert if (!new_id) { 626*a90b9d01SCy Schubert wpabuf_free(dec); 627*a90b9d01SCy Schubert goto failed; 628*a90b9d01SCy Schubert } 629*a90b9d01SCy Schubert os_free(sm->identity); 630*a90b9d01SCy Schubert sm->identity = new_id; 631*a90b9d01SCy Schubert sm->identity_len = wpabuf_len(dec); 632*a90b9d01SCy Schubert wpabuf_free(dec); 633*a90b9d01SCy Schubert os_strlcpy(data->permanent, username, sizeof(data->permanent)); 634*a90b9d01SCy Schubert os_free(username); 635*a90b9d01SCy Schubert #endif /* CRYPTO_RSA_OAEP_SHA256 */ 636f05cddf9SRui Paulo } else { 637f05cddf9SRui Paulo wpa_printf(MSG_DEBUG, "EAP-SIM: Unrecognized username '%s'", 638f05cddf9SRui Paulo username); 639f05cddf9SRui Paulo os_free(username); 640f05cddf9SRui Paulo goto failed; 641f05cddf9SRui Paulo } 642f05cddf9SRui Paulo 643f05cddf9SRui Paulo skip_id_update: 644f05cddf9SRui Paulo /* Full authentication */ 645f05cddf9SRui Paulo 646e28a4053SRui Paulo if (attr->nonce_mt == NULL || attr->selected_version < 0) { 647e28a4053SRui Paulo wpa_printf(MSG_DEBUG, "EAP-SIM: Start/Response missing " 648e28a4053SRui Paulo "required attributes"); 649f05cddf9SRui Paulo goto failed; 650e28a4053SRui Paulo } 651e28a4053SRui Paulo 652e28a4053SRui Paulo if (!eap_sim_supported_ver(data, attr->selected_version)) { 653e28a4053SRui Paulo wpa_printf(MSG_DEBUG, "EAP-SIM: Peer selected unsupported " 654e28a4053SRui Paulo "version %d", attr->selected_version); 655f05cddf9SRui Paulo goto failed; 656e28a4053SRui Paulo } 657e28a4053SRui Paulo 658e28a4053SRui Paulo data->counter = 0; /* reset re-auth counter since this is full auth */ 659e28a4053SRui Paulo data->reauth = NULL; 660e28a4053SRui Paulo 661e28a4053SRui Paulo data->num_chal = eap_sim_db_get_gsm_triplets( 662c1d255d3SCy Schubert sm->cfg->eap_sim_db_priv, data->permanent, EAP_SIM_MAX_CHAL, 663e28a4053SRui Paulo (u8 *) data->rand, (u8 *) data->kc, (u8 *) data->sres, sm); 664e28a4053SRui Paulo if (data->num_chal == EAP_SIM_DB_PENDING) { 665e28a4053SRui Paulo wpa_printf(MSG_DEBUG, "EAP-SIM: GSM authentication triplets " 666e28a4053SRui Paulo "not yet available - pending request"); 667e28a4053SRui Paulo sm->method_pending = METHOD_PENDING_WAIT; 668e28a4053SRui Paulo return; 669e28a4053SRui Paulo } 670e28a4053SRui Paulo if (data->num_chal < 2) { 671e28a4053SRui Paulo wpa_printf(MSG_INFO, "EAP-SIM: Failed to get GSM " 672e28a4053SRui Paulo "authentication triplets for the peer"); 673f05cddf9SRui Paulo goto failed; 674e28a4053SRui Paulo } 675e28a4053SRui Paulo 6764bc52338SCy Schubert if (data->permanent[0] == EAP_SIM_PERMANENT_PREFIX) 6774bc52338SCy Schubert os_strlcpy(sm->imsi, &data->permanent[1], sizeof(sm->imsi)); 6784bc52338SCy Schubert 679e28a4053SRui Paulo identity_len = sm->identity_len; 680e28a4053SRui Paulo while (identity_len > 0 && sm->identity[identity_len - 1] == '\0') { 681e28a4053SRui Paulo wpa_printf(MSG_DEBUG, "EAP-SIM: Workaround - drop last null " 682e28a4053SRui Paulo "character from identity"); 683e28a4053SRui Paulo identity_len--; 684e28a4053SRui Paulo } 685e28a4053SRui Paulo wpa_hexdump_ascii(MSG_DEBUG, "EAP-SIM: Identity for MK derivation", 686e28a4053SRui Paulo sm->identity, identity_len); 687e28a4053SRui Paulo 688e28a4053SRui Paulo os_memcpy(data->nonce_mt, attr->nonce_mt, EAP_SIM_NONCE_MT_LEN); 689e28a4053SRui Paulo WPA_PUT_BE16(ver_list, EAP_SIM_VERSION); 690e28a4053SRui Paulo eap_sim_derive_mk(sm->identity, identity_len, attr->nonce_mt, 691e28a4053SRui Paulo attr->selected_version, ver_list, sizeof(ver_list), 692e28a4053SRui Paulo data->num_chal, (const u8 *) data->kc, data->mk); 693e28a4053SRui Paulo eap_sim_derive_keys(data->mk, data->k_encr, data->k_aut, data->msk, 694e28a4053SRui Paulo data->emsk); 695e28a4053SRui Paulo 696e28a4053SRui Paulo eap_sim_state(data, CHALLENGE); 697f05cddf9SRui Paulo return; 698f05cddf9SRui Paulo 699f05cddf9SRui Paulo failed: 700f05cddf9SRui Paulo data->notification = EAP_SIM_GENERAL_FAILURE_BEFORE_AUTH; 701f05cddf9SRui Paulo eap_sim_state(data, NOTIFICATION); 702e28a4053SRui Paulo } 703e28a4053SRui Paulo 704e28a4053SRui Paulo 705e28a4053SRui Paulo static void eap_sim_process_challenge(struct eap_sm *sm, 706e28a4053SRui Paulo struct eap_sim_data *data, 707e28a4053SRui Paulo struct wpabuf *respData, 708e28a4053SRui Paulo struct eap_sim_attrs *attr) 709e28a4053SRui Paulo { 710e28a4053SRui Paulo if (attr->mac == NULL || 711e28a4053SRui Paulo eap_sim_verify_mac(data->k_aut, respData, attr->mac, 712e28a4053SRui Paulo (u8 *) data->sres, 713e28a4053SRui Paulo data->num_chal * EAP_SIM_SRES_LEN)) { 714e28a4053SRui Paulo wpa_printf(MSG_WARNING, "EAP-SIM: Challenge message " 715e28a4053SRui Paulo "did not include valid AT_MAC"); 716f05cddf9SRui Paulo data->notification = EAP_SIM_GENERAL_FAILURE_BEFORE_AUTH; 717f05cddf9SRui Paulo eap_sim_state(data, NOTIFICATION); 718e28a4053SRui Paulo return; 719e28a4053SRui Paulo } 720e28a4053SRui Paulo 721e28a4053SRui Paulo wpa_printf(MSG_DEBUG, "EAP-SIM: Challenge response includes the " 722e28a4053SRui Paulo "correct AT_MAC"); 723c1d255d3SCy Schubert if (sm->cfg->eap_sim_aka_result_ind && attr->result_ind) { 724e28a4053SRui Paulo data->use_result_ind = 1; 725e28a4053SRui Paulo data->notification = EAP_SIM_SUCCESS; 726e28a4053SRui Paulo eap_sim_state(data, NOTIFICATION); 727e28a4053SRui Paulo } else 728e28a4053SRui Paulo eap_sim_state(data, SUCCESS); 729e28a4053SRui Paulo 730e28a4053SRui Paulo if (data->next_pseudonym) { 731c1d255d3SCy Schubert eap_sim_db_add_pseudonym(sm->cfg->eap_sim_db_priv, 732c1d255d3SCy Schubert data->permanent, 733e28a4053SRui Paulo data->next_pseudonym); 734e28a4053SRui Paulo data->next_pseudonym = NULL; 735e28a4053SRui Paulo } 736e28a4053SRui Paulo if (data->next_reauth_id) { 737c1d255d3SCy Schubert eap_sim_db_add_reauth(sm->cfg->eap_sim_db_priv, data->permanent, 738e28a4053SRui Paulo data->next_reauth_id, data->counter + 1, 739e28a4053SRui Paulo data->mk); 740e28a4053SRui Paulo data->next_reauth_id = NULL; 741e28a4053SRui Paulo } 742e28a4053SRui Paulo } 743e28a4053SRui Paulo 744e28a4053SRui Paulo 745e28a4053SRui Paulo static void eap_sim_process_reauth(struct eap_sm *sm, 746e28a4053SRui Paulo struct eap_sim_data *data, 747e28a4053SRui Paulo struct wpabuf *respData, 748e28a4053SRui Paulo struct eap_sim_attrs *attr) 749e28a4053SRui Paulo { 750e28a4053SRui Paulo struct eap_sim_attrs eattr; 751e28a4053SRui Paulo u8 *decrypted = NULL; 752e28a4053SRui Paulo 753e28a4053SRui Paulo if (attr->mac == NULL || 754e28a4053SRui Paulo eap_sim_verify_mac(data->k_aut, respData, attr->mac, data->nonce_s, 755e28a4053SRui Paulo EAP_SIM_NONCE_S_LEN)) { 756e28a4053SRui Paulo wpa_printf(MSG_WARNING, "EAP-SIM: Re-authentication message " 757e28a4053SRui Paulo "did not include valid AT_MAC"); 758e28a4053SRui Paulo goto fail; 759e28a4053SRui Paulo } 760e28a4053SRui Paulo 761e28a4053SRui Paulo if (attr->encr_data == NULL || attr->iv == NULL) { 762e28a4053SRui Paulo wpa_printf(MSG_WARNING, "EAP-SIM: Reauthentication " 763e28a4053SRui Paulo "message did not include encrypted data"); 764e28a4053SRui Paulo goto fail; 765e28a4053SRui Paulo } 766e28a4053SRui Paulo 767e28a4053SRui Paulo decrypted = eap_sim_parse_encr(data->k_encr, attr->encr_data, 768e28a4053SRui Paulo attr->encr_data_len, attr->iv, &eattr, 769e28a4053SRui Paulo 0); 770e28a4053SRui Paulo if (decrypted == NULL) { 771e28a4053SRui Paulo wpa_printf(MSG_WARNING, "EAP-SIM: Failed to parse encrypted " 772e28a4053SRui Paulo "data from reauthentication message"); 773e28a4053SRui Paulo goto fail; 774e28a4053SRui Paulo } 775e28a4053SRui Paulo 776e28a4053SRui Paulo if (eattr.counter != data->counter) { 777e28a4053SRui Paulo wpa_printf(MSG_WARNING, "EAP-SIM: Re-authentication message " 778e28a4053SRui Paulo "used incorrect counter %u, expected %u", 779e28a4053SRui Paulo eattr.counter, data->counter); 780e28a4053SRui Paulo goto fail; 781e28a4053SRui Paulo } 782e28a4053SRui Paulo os_free(decrypted); 783e28a4053SRui Paulo decrypted = NULL; 784e28a4053SRui Paulo 785e28a4053SRui Paulo wpa_printf(MSG_DEBUG, "EAP-SIM: Re-authentication response includes " 786e28a4053SRui Paulo "the correct AT_MAC"); 787f05cddf9SRui Paulo 788f05cddf9SRui Paulo if (eattr.counter_too_small) { 789f05cddf9SRui Paulo wpa_printf(MSG_DEBUG, "EAP-AKA: Re-authentication response " 790f05cddf9SRui Paulo "included AT_COUNTER_TOO_SMALL - starting full " 791f05cddf9SRui Paulo "authentication"); 792f05cddf9SRui Paulo data->start_round = -1; 793f05cddf9SRui Paulo eap_sim_state(data, START); 794f05cddf9SRui Paulo return; 795f05cddf9SRui Paulo } 796f05cddf9SRui Paulo 797c1d255d3SCy Schubert if (sm->cfg->eap_sim_aka_result_ind && attr->result_ind) { 798e28a4053SRui Paulo data->use_result_ind = 1; 799e28a4053SRui Paulo data->notification = EAP_SIM_SUCCESS; 800e28a4053SRui Paulo eap_sim_state(data, NOTIFICATION); 801e28a4053SRui Paulo } else 802e28a4053SRui Paulo eap_sim_state(data, SUCCESS); 803e28a4053SRui Paulo 804e28a4053SRui Paulo if (data->next_reauth_id) { 805c1d255d3SCy Schubert eap_sim_db_add_reauth(sm->cfg->eap_sim_db_priv, data->permanent, 806f05cddf9SRui Paulo data->next_reauth_id, 807e28a4053SRui Paulo data->counter + 1, data->mk); 808e28a4053SRui Paulo data->next_reauth_id = NULL; 809e28a4053SRui Paulo } else { 810c1d255d3SCy Schubert eap_sim_db_remove_reauth(sm->cfg->eap_sim_db_priv, 811c1d255d3SCy Schubert data->reauth); 812e28a4053SRui Paulo data->reauth = NULL; 813e28a4053SRui Paulo } 814e28a4053SRui Paulo 815e28a4053SRui Paulo return; 816e28a4053SRui Paulo 817e28a4053SRui Paulo fail: 818f05cddf9SRui Paulo data->notification = EAP_SIM_GENERAL_FAILURE_BEFORE_AUTH; 819f05cddf9SRui Paulo eap_sim_state(data, NOTIFICATION); 820c1d255d3SCy Schubert eap_sim_db_remove_reauth(sm->cfg->eap_sim_db_priv, data->reauth); 821e28a4053SRui Paulo data->reauth = NULL; 822e28a4053SRui Paulo os_free(decrypted); 823e28a4053SRui Paulo } 824e28a4053SRui Paulo 825e28a4053SRui Paulo 826e28a4053SRui Paulo static void eap_sim_process_client_error(struct eap_sm *sm, 827e28a4053SRui Paulo struct eap_sim_data *data, 828e28a4053SRui Paulo struct wpabuf *respData, 829e28a4053SRui Paulo struct eap_sim_attrs *attr) 830e28a4053SRui Paulo { 831e28a4053SRui Paulo wpa_printf(MSG_DEBUG, "EAP-SIM: Client reported error %d", 832e28a4053SRui Paulo attr->client_error_code); 833e28a4053SRui Paulo if (data->notification == EAP_SIM_SUCCESS && data->use_result_ind) 834e28a4053SRui Paulo eap_sim_state(data, SUCCESS); 835e28a4053SRui Paulo else 836e28a4053SRui Paulo eap_sim_state(data, FAILURE); 837e28a4053SRui Paulo } 838e28a4053SRui Paulo 839e28a4053SRui Paulo 840e28a4053SRui Paulo static void eap_sim_process_notification(struct eap_sm *sm, 841e28a4053SRui Paulo struct eap_sim_data *data, 842e28a4053SRui Paulo struct wpabuf *respData, 843e28a4053SRui Paulo struct eap_sim_attrs *attr) 844e28a4053SRui Paulo { 845e28a4053SRui Paulo wpa_printf(MSG_DEBUG, "EAP-SIM: Client replied to notification"); 846e28a4053SRui Paulo if (data->notification == EAP_SIM_SUCCESS && data->use_result_ind) 847e28a4053SRui Paulo eap_sim_state(data, SUCCESS); 848e28a4053SRui Paulo else 849e28a4053SRui Paulo eap_sim_state(data, FAILURE); 850e28a4053SRui Paulo } 851e28a4053SRui Paulo 852e28a4053SRui Paulo 853e28a4053SRui Paulo static void eap_sim_process(struct eap_sm *sm, void *priv, 854e28a4053SRui Paulo struct wpabuf *respData) 855e28a4053SRui Paulo { 856e28a4053SRui Paulo struct eap_sim_data *data = priv; 857e28a4053SRui Paulo const u8 *pos, *end; 858e28a4053SRui Paulo u8 subtype; 859e28a4053SRui Paulo size_t len; 860e28a4053SRui Paulo struct eap_sim_attrs attr; 861e28a4053SRui Paulo 862e28a4053SRui Paulo pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_SIM, respData, &len); 863e28a4053SRui Paulo if (pos == NULL || len < 3) 864e28a4053SRui Paulo return; 865e28a4053SRui Paulo 866e28a4053SRui Paulo end = pos + len; 867e28a4053SRui Paulo subtype = *pos; 868e28a4053SRui Paulo pos += 3; 869e28a4053SRui Paulo 870f05cddf9SRui Paulo if (eap_sim_unexpected_subtype(data, subtype)) { 871f05cddf9SRui Paulo wpa_printf(MSG_DEBUG, "EAP-SIM: Unrecognized or unexpected " 872f05cddf9SRui Paulo "EAP-SIM Subtype in EAP Response"); 873f05cddf9SRui Paulo data->notification = EAP_SIM_GENERAL_FAILURE_BEFORE_AUTH; 874f05cddf9SRui Paulo eap_sim_state(data, NOTIFICATION); 875f05cddf9SRui Paulo return; 876f05cddf9SRui Paulo } 877f05cddf9SRui Paulo 878e28a4053SRui Paulo if (eap_sim_parse_attr(pos, end, &attr, 0, 0)) { 879e28a4053SRui Paulo wpa_printf(MSG_DEBUG, "EAP-SIM: Failed to parse attributes"); 880f05cddf9SRui Paulo if (subtype != EAP_SIM_SUBTYPE_CLIENT_ERROR && 881f05cddf9SRui Paulo (data->state == START || data->state == CHALLENGE || 882f05cddf9SRui Paulo data->state == REAUTH)) { 883f05cddf9SRui Paulo data->notification = 884f05cddf9SRui Paulo EAP_SIM_GENERAL_FAILURE_BEFORE_AUTH; 885f05cddf9SRui Paulo eap_sim_state(data, NOTIFICATION); 886f05cddf9SRui Paulo return; 887f05cddf9SRui Paulo } 888e28a4053SRui Paulo eap_sim_state(data, FAILURE); 889e28a4053SRui Paulo return; 890e28a4053SRui Paulo } 891e28a4053SRui Paulo 892e28a4053SRui Paulo if (subtype == EAP_SIM_SUBTYPE_CLIENT_ERROR) { 893e28a4053SRui Paulo eap_sim_process_client_error(sm, data, respData, &attr); 894e28a4053SRui Paulo return; 895e28a4053SRui Paulo } 896e28a4053SRui Paulo 897e28a4053SRui Paulo switch (data->state) { 898e28a4053SRui Paulo case START: 899e28a4053SRui Paulo eap_sim_process_start(sm, data, respData, &attr); 900e28a4053SRui Paulo break; 901e28a4053SRui Paulo case CHALLENGE: 902e28a4053SRui Paulo eap_sim_process_challenge(sm, data, respData, &attr); 903e28a4053SRui Paulo break; 904e28a4053SRui Paulo case REAUTH: 905e28a4053SRui Paulo eap_sim_process_reauth(sm, data, respData, &attr); 906e28a4053SRui Paulo break; 907e28a4053SRui Paulo case NOTIFICATION: 908e28a4053SRui Paulo eap_sim_process_notification(sm, data, respData, &attr); 909e28a4053SRui Paulo break; 910e28a4053SRui Paulo default: 911e28a4053SRui Paulo wpa_printf(MSG_DEBUG, "EAP-SIM: Unknown state %d in " 912e28a4053SRui Paulo "process", data->state); 913e28a4053SRui Paulo break; 914e28a4053SRui Paulo } 915e28a4053SRui Paulo } 916e28a4053SRui Paulo 917e28a4053SRui Paulo 918c1d255d3SCy Schubert static bool eap_sim_isDone(struct eap_sm *sm, void *priv) 919e28a4053SRui Paulo { 920e28a4053SRui Paulo struct eap_sim_data *data = priv; 921e28a4053SRui Paulo return data->state == SUCCESS || data->state == FAILURE; 922e28a4053SRui Paulo } 923e28a4053SRui Paulo 924e28a4053SRui Paulo 925e28a4053SRui Paulo static u8 * eap_sim_getKey(struct eap_sm *sm, void *priv, size_t *len) 926e28a4053SRui Paulo { 927e28a4053SRui Paulo struct eap_sim_data *data = priv; 928e28a4053SRui Paulo u8 *key; 929e28a4053SRui Paulo 930e28a4053SRui Paulo if (data->state != SUCCESS) 931e28a4053SRui Paulo return NULL; 932e28a4053SRui Paulo 93385732ac8SCy Schubert key = os_memdup(data->msk, EAP_SIM_KEYING_DATA_LEN); 934e28a4053SRui Paulo if (key == NULL) 935e28a4053SRui Paulo return NULL; 936e28a4053SRui Paulo *len = EAP_SIM_KEYING_DATA_LEN; 937e28a4053SRui Paulo return key; 938e28a4053SRui Paulo } 939e28a4053SRui Paulo 940e28a4053SRui Paulo 941e28a4053SRui Paulo static u8 * eap_sim_get_emsk(struct eap_sm *sm, void *priv, size_t *len) 942e28a4053SRui Paulo { 943e28a4053SRui Paulo struct eap_sim_data *data = priv; 944e28a4053SRui Paulo u8 *key; 945e28a4053SRui Paulo 946e28a4053SRui Paulo if (data->state != SUCCESS) 947e28a4053SRui Paulo return NULL; 948e28a4053SRui Paulo 94985732ac8SCy Schubert key = os_memdup(data->emsk, EAP_EMSK_LEN); 950e28a4053SRui Paulo if (key == NULL) 951e28a4053SRui Paulo return NULL; 952e28a4053SRui Paulo *len = EAP_EMSK_LEN; 953e28a4053SRui Paulo return key; 954e28a4053SRui Paulo } 955e28a4053SRui Paulo 956e28a4053SRui Paulo 957c1d255d3SCy Schubert static bool eap_sim_isSuccess(struct eap_sm *sm, void *priv) 958e28a4053SRui Paulo { 959e28a4053SRui Paulo struct eap_sim_data *data = priv; 960e28a4053SRui Paulo return data->state == SUCCESS; 961e28a4053SRui Paulo } 962e28a4053SRui Paulo 963e28a4053SRui Paulo 9645b9c547cSRui Paulo static u8 * eap_sim_get_session_id(struct eap_sm *sm, void *priv, size_t *len) 9655b9c547cSRui Paulo { 9665b9c547cSRui Paulo struct eap_sim_data *data = priv; 9675b9c547cSRui Paulo u8 *id; 9685b9c547cSRui Paulo 9695b9c547cSRui Paulo if (data->state != SUCCESS) 9705b9c547cSRui Paulo return NULL; 9715b9c547cSRui Paulo 972206b73d0SCy Schubert if (!data->reauth) 9735b9c547cSRui Paulo *len = 1 + data->num_chal * GSM_RAND_LEN + EAP_SIM_NONCE_MT_LEN; 974206b73d0SCy Schubert else 975206b73d0SCy Schubert *len = 1 + EAP_SIM_NONCE_S_LEN + EAP_SIM_MAC_LEN; 9765b9c547cSRui Paulo id = os_malloc(*len); 9775b9c547cSRui Paulo if (id == NULL) 9785b9c547cSRui Paulo return NULL; 9795b9c547cSRui Paulo 9805b9c547cSRui Paulo id[0] = EAP_TYPE_SIM; 981206b73d0SCy Schubert if (!data->reauth) { 9825b9c547cSRui Paulo os_memcpy(id + 1, data->rand, data->num_chal * GSM_RAND_LEN); 983206b73d0SCy Schubert os_memcpy(id + 1 + data->num_chal * GSM_RAND_LEN, 984206b73d0SCy Schubert data->nonce_mt, EAP_SIM_NONCE_MT_LEN); 985206b73d0SCy Schubert } else { 986206b73d0SCy Schubert os_memcpy(id + 1, data->nonce_s, EAP_SIM_NONCE_S_LEN); 987206b73d0SCy Schubert os_memcpy(id + 1 + EAP_SIM_NONCE_S_LEN, data->reauth_mac, 988206b73d0SCy Schubert EAP_SIM_MAC_LEN); 989206b73d0SCy Schubert 990206b73d0SCy Schubert } 9915b9c547cSRui Paulo wpa_hexdump(MSG_DEBUG, "EAP-SIM: Derived Session-Id", id, *len); 9925b9c547cSRui Paulo 9935b9c547cSRui Paulo return id; 9945b9c547cSRui Paulo } 9955b9c547cSRui Paulo 9965b9c547cSRui Paulo 997e28a4053SRui Paulo int eap_server_sim_register(void) 998e28a4053SRui Paulo { 999e28a4053SRui Paulo struct eap_method *eap; 1000e28a4053SRui Paulo 1001e28a4053SRui Paulo eap = eap_server_method_alloc(EAP_SERVER_METHOD_INTERFACE_VERSION, 1002e28a4053SRui Paulo EAP_VENDOR_IETF, EAP_TYPE_SIM, "SIM"); 1003e28a4053SRui Paulo if (eap == NULL) 1004e28a4053SRui Paulo return -1; 1005e28a4053SRui Paulo 1006e28a4053SRui Paulo eap->init = eap_sim_init; 1007e28a4053SRui Paulo eap->reset = eap_sim_reset; 1008e28a4053SRui Paulo eap->buildReq = eap_sim_buildReq; 1009e28a4053SRui Paulo eap->check = eap_sim_check; 1010e28a4053SRui Paulo eap->process = eap_sim_process; 1011e28a4053SRui Paulo eap->isDone = eap_sim_isDone; 1012e28a4053SRui Paulo eap->getKey = eap_sim_getKey; 1013e28a4053SRui Paulo eap->isSuccess = eap_sim_isSuccess; 1014e28a4053SRui Paulo eap->get_emsk = eap_sim_get_emsk; 10155b9c547cSRui Paulo eap->getSessionId = eap_sim_get_session_id; 1016e28a4053SRui Paulo 1017780fb4a2SCy Schubert return eap_server_method_register(eap); 1018e28a4053SRui Paulo } 1019