1214501Srpaulo/* 2214501Srpaulo * hostapd / EAP-SIM (RFC 4186) 3252726Srpaulo * Copyright (c) 2005-2012, Jouni Malinen <j@w1.fi> 4214501Srpaulo * 5252726Srpaulo * This software may be distributed under the terms of the BSD license. 6252726Srpaulo * See README for more details. 7214501Srpaulo */ 8214501Srpaulo 9214501Srpaulo#include "includes.h" 10214501Srpaulo 11214501Srpaulo#include "common.h" 12252726Srpaulo#include "crypto/random.h" 13214501Srpaulo#include "eap_server/eap_i.h" 14214501Srpaulo#include "eap_common/eap_sim_common.h" 15214501Srpaulo#include "eap_server/eap_sim_db.h" 16214501Srpaulo 17214501Srpaulo 18214501Srpaulostruct eap_sim_data { 19214501Srpaulo u8 mk[EAP_SIM_MK_LEN]; 20214501Srpaulo u8 nonce_mt[EAP_SIM_NONCE_MT_LEN]; 21214501Srpaulo u8 nonce_s[EAP_SIM_NONCE_S_LEN]; 22214501Srpaulo u8 k_aut[EAP_SIM_K_AUT_LEN]; 23214501Srpaulo u8 k_encr[EAP_SIM_K_ENCR_LEN]; 24214501Srpaulo u8 msk[EAP_SIM_KEYING_DATA_LEN]; 25214501Srpaulo u8 emsk[EAP_EMSK_LEN]; 26214501Srpaulo u8 kc[EAP_SIM_MAX_CHAL][EAP_SIM_KC_LEN]; 27214501Srpaulo u8 sres[EAP_SIM_MAX_CHAL][EAP_SIM_SRES_LEN]; 28214501Srpaulo u8 rand[EAP_SIM_MAX_CHAL][GSM_RAND_LEN]; 29214501Srpaulo int num_chal; 30214501Srpaulo enum { 31214501Srpaulo START, CHALLENGE, REAUTH, NOTIFICATION, SUCCESS, FAILURE 32214501Srpaulo } state; 33214501Srpaulo char *next_pseudonym; 34214501Srpaulo char *next_reauth_id; 35214501Srpaulo u16 counter; 36214501Srpaulo struct eap_sim_reauth *reauth; 37214501Srpaulo u16 notification; 38214501Srpaulo int use_result_ind; 39252726Srpaulo int start_round; 40252726Srpaulo char permanent[20]; /* Permanent username */ 41214501Srpaulo}; 42214501Srpaulo 43214501Srpaulo 44214501Srpaulostatic const char * eap_sim_state_txt(int state) 45214501Srpaulo{ 46214501Srpaulo switch (state) { 47214501Srpaulo case START: 48214501Srpaulo return "START"; 49214501Srpaulo case CHALLENGE: 50214501Srpaulo return "CHALLENGE"; 51214501Srpaulo case REAUTH: 52214501Srpaulo return "REAUTH"; 53214501Srpaulo case SUCCESS: 54214501Srpaulo return "SUCCESS"; 55214501Srpaulo case FAILURE: 56214501Srpaulo return "FAILURE"; 57214501Srpaulo case NOTIFICATION: 58214501Srpaulo return "NOTIFICATION"; 59214501Srpaulo default: 60214501Srpaulo return "Unknown?!"; 61214501Srpaulo } 62214501Srpaulo} 63214501Srpaulo 64214501Srpaulo 65214501Srpaulostatic void eap_sim_state(struct eap_sim_data *data, int state) 66214501Srpaulo{ 67214501Srpaulo wpa_printf(MSG_DEBUG, "EAP-SIM: %s -> %s", 68214501Srpaulo eap_sim_state_txt(data->state), 69214501Srpaulo eap_sim_state_txt(state)); 70214501Srpaulo data->state = state; 71214501Srpaulo} 72214501Srpaulo 73214501Srpaulo 74214501Srpaulostatic void * eap_sim_init(struct eap_sm *sm) 75214501Srpaulo{ 76214501Srpaulo struct eap_sim_data *data; 77214501Srpaulo 78214501Srpaulo if (sm->eap_sim_db_priv == NULL) { 79214501Srpaulo wpa_printf(MSG_WARNING, "EAP-SIM: eap_sim_db not configured"); 80214501Srpaulo return NULL; 81214501Srpaulo } 82214501Srpaulo 83214501Srpaulo data = os_zalloc(sizeof(*data)); 84214501Srpaulo if (data == NULL) 85214501Srpaulo return NULL; 86214501Srpaulo data->state = START; 87214501Srpaulo 88214501Srpaulo return data; 89214501Srpaulo} 90214501Srpaulo 91214501Srpaulo 92214501Srpaulostatic void eap_sim_reset(struct eap_sm *sm, void *priv) 93214501Srpaulo{ 94214501Srpaulo struct eap_sim_data *data = priv; 95214501Srpaulo os_free(data->next_pseudonym); 96214501Srpaulo os_free(data->next_reauth_id); 97214501Srpaulo os_free(data); 98214501Srpaulo} 99214501Srpaulo 100214501Srpaulo 101214501Srpaulostatic struct wpabuf * eap_sim_build_start(struct eap_sm *sm, 102214501Srpaulo struct eap_sim_data *data, u8 id) 103214501Srpaulo{ 104214501Srpaulo struct eap_sim_msg *msg; 105214501Srpaulo u8 ver[2]; 106214501Srpaulo 107214501Srpaulo wpa_printf(MSG_DEBUG, "EAP-SIM: Generating Start"); 108214501Srpaulo msg = eap_sim_msg_init(EAP_CODE_REQUEST, id, EAP_TYPE_SIM, 109214501Srpaulo EAP_SIM_SUBTYPE_START); 110252726Srpaulo data->start_round++; 111252726Srpaulo if (data->start_round == 1) { 112214501Srpaulo /* 113214501Srpaulo * RFC 4186, Chap. 4.2.4 recommends that identity from EAP is 114214501Srpaulo * ignored and the SIM/Start is used to request the identity. 115214501Srpaulo */ 116214501Srpaulo wpa_printf(MSG_DEBUG, " AT_ANY_ID_REQ"); 117214501Srpaulo eap_sim_msg_add(msg, EAP_SIM_AT_ANY_ID_REQ, 0, NULL, 0); 118252726Srpaulo } else if (data->start_round > 3) { 119252726Srpaulo /* Cannot use more than three rounds of Start messages */ 120252726Srpaulo eap_sim_msg_free(msg); 121252726Srpaulo return NULL; 122252726Srpaulo } else if (data->start_round == 0) { 123252726Srpaulo /* 124252726Srpaulo * This is a special case that is used to recover from 125252726Srpaulo * AT_COUNTER_TOO_SMALL during re-authentication. Since we 126252726Srpaulo * already know the identity of the peer, there is no need to 127252726Srpaulo * request any identity in this case. 128252726Srpaulo */ 129252726Srpaulo } else if (sm->identity && sm->identity_len > 0 && 130252726Srpaulo sm->identity[0] == EAP_SIM_REAUTH_ID_PREFIX) { 131252726Srpaulo /* Reauth id may have expired - try fullauth */ 132252726Srpaulo wpa_printf(MSG_DEBUG, " AT_FULLAUTH_ID_REQ"); 133252726Srpaulo eap_sim_msg_add(msg, EAP_SIM_AT_FULLAUTH_ID_REQ, 0, NULL, 0); 134252726Srpaulo } else { 135252726Srpaulo wpa_printf(MSG_DEBUG, " AT_PERMANENT_ID_REQ"); 136252726Srpaulo eap_sim_msg_add(msg, EAP_SIM_AT_PERMANENT_ID_REQ, 0, NULL, 0); 137214501Srpaulo } 138214501Srpaulo wpa_printf(MSG_DEBUG, " AT_VERSION_LIST"); 139214501Srpaulo ver[0] = 0; 140214501Srpaulo ver[1] = EAP_SIM_VERSION; 141214501Srpaulo eap_sim_msg_add(msg, EAP_SIM_AT_VERSION_LIST, sizeof(ver), 142214501Srpaulo ver, sizeof(ver)); 143214501Srpaulo return eap_sim_msg_finish(msg, NULL, NULL, 0); 144214501Srpaulo} 145214501Srpaulo 146214501Srpaulo 147214501Srpaulostatic int eap_sim_build_encr(struct eap_sm *sm, struct eap_sim_data *data, 148214501Srpaulo struct eap_sim_msg *msg, u16 counter, 149214501Srpaulo const u8 *nonce_s) 150214501Srpaulo{ 151214501Srpaulo os_free(data->next_pseudonym); 152252726Srpaulo if (nonce_s == NULL) { 153252726Srpaulo data->next_pseudonym = 154252726Srpaulo eap_sim_db_get_next_pseudonym(sm->eap_sim_db_priv, 155252726Srpaulo EAP_SIM_DB_SIM); 156252726Srpaulo } else { 157252726Srpaulo /* Do not update pseudonym during re-authentication */ 158252726Srpaulo data->next_pseudonym = NULL; 159252726Srpaulo } 160214501Srpaulo os_free(data->next_reauth_id); 161214501Srpaulo if (data->counter <= EAP_SIM_MAX_FAST_REAUTHS) { 162214501Srpaulo data->next_reauth_id = 163252726Srpaulo eap_sim_db_get_next_reauth_id(sm->eap_sim_db_priv, 164252726Srpaulo EAP_SIM_DB_SIM); 165214501Srpaulo } else { 166214501Srpaulo wpa_printf(MSG_DEBUG, "EAP-SIM: Max fast re-authentication " 167214501Srpaulo "count exceeded - force full authentication"); 168214501Srpaulo data->next_reauth_id = NULL; 169214501Srpaulo } 170214501Srpaulo 171214501Srpaulo if (data->next_pseudonym == NULL && data->next_reauth_id == NULL && 172214501Srpaulo counter == 0 && nonce_s == NULL) 173214501Srpaulo return 0; 174214501Srpaulo 175214501Srpaulo wpa_printf(MSG_DEBUG, " AT_IV"); 176214501Srpaulo wpa_printf(MSG_DEBUG, " AT_ENCR_DATA"); 177214501Srpaulo eap_sim_msg_add_encr_start(msg, EAP_SIM_AT_IV, EAP_SIM_AT_ENCR_DATA); 178214501Srpaulo 179214501Srpaulo if (counter > 0) { 180214501Srpaulo wpa_printf(MSG_DEBUG, " *AT_COUNTER (%u)", counter); 181214501Srpaulo eap_sim_msg_add(msg, EAP_SIM_AT_COUNTER, counter, NULL, 0); 182214501Srpaulo } 183214501Srpaulo 184214501Srpaulo if (nonce_s) { 185214501Srpaulo wpa_printf(MSG_DEBUG, " *AT_NONCE_S"); 186214501Srpaulo eap_sim_msg_add(msg, EAP_SIM_AT_NONCE_S, 0, nonce_s, 187214501Srpaulo EAP_SIM_NONCE_S_LEN); 188214501Srpaulo } 189214501Srpaulo 190214501Srpaulo if (data->next_pseudonym) { 191214501Srpaulo wpa_printf(MSG_DEBUG, " *AT_NEXT_PSEUDONYM (%s)", 192214501Srpaulo data->next_pseudonym); 193214501Srpaulo eap_sim_msg_add(msg, EAP_SIM_AT_NEXT_PSEUDONYM, 194214501Srpaulo os_strlen(data->next_pseudonym), 195214501Srpaulo (u8 *) data->next_pseudonym, 196214501Srpaulo os_strlen(data->next_pseudonym)); 197214501Srpaulo } 198214501Srpaulo 199214501Srpaulo if (data->next_reauth_id) { 200214501Srpaulo wpa_printf(MSG_DEBUG, " *AT_NEXT_REAUTH_ID (%s)", 201214501Srpaulo data->next_reauth_id); 202214501Srpaulo eap_sim_msg_add(msg, EAP_SIM_AT_NEXT_REAUTH_ID, 203214501Srpaulo os_strlen(data->next_reauth_id), 204214501Srpaulo (u8 *) data->next_reauth_id, 205214501Srpaulo os_strlen(data->next_reauth_id)); 206214501Srpaulo } 207214501Srpaulo 208214501Srpaulo if (eap_sim_msg_add_encr_end(msg, data->k_encr, EAP_SIM_AT_PADDING)) { 209214501Srpaulo wpa_printf(MSG_WARNING, "EAP-SIM: Failed to encrypt " 210214501Srpaulo "AT_ENCR_DATA"); 211214501Srpaulo return -1; 212214501Srpaulo } 213214501Srpaulo 214214501Srpaulo return 0; 215214501Srpaulo} 216214501Srpaulo 217214501Srpaulo 218214501Srpaulostatic struct wpabuf * eap_sim_build_challenge(struct eap_sm *sm, 219214501Srpaulo struct eap_sim_data *data, 220214501Srpaulo u8 id) 221214501Srpaulo{ 222214501Srpaulo struct eap_sim_msg *msg; 223214501Srpaulo 224214501Srpaulo wpa_printf(MSG_DEBUG, "EAP-SIM: Generating Challenge"); 225214501Srpaulo msg = eap_sim_msg_init(EAP_CODE_REQUEST, id, EAP_TYPE_SIM, 226214501Srpaulo EAP_SIM_SUBTYPE_CHALLENGE); 227214501Srpaulo wpa_printf(MSG_DEBUG, " AT_RAND"); 228214501Srpaulo eap_sim_msg_add(msg, EAP_SIM_AT_RAND, 0, (u8 *) data->rand, 229214501Srpaulo data->num_chal * GSM_RAND_LEN); 230214501Srpaulo 231214501Srpaulo if (eap_sim_build_encr(sm, data, msg, 0, NULL)) { 232214501Srpaulo eap_sim_msg_free(msg); 233214501Srpaulo return NULL; 234214501Srpaulo } 235214501Srpaulo 236214501Srpaulo if (sm->eap_sim_aka_result_ind) { 237214501Srpaulo wpa_printf(MSG_DEBUG, " AT_RESULT_IND"); 238214501Srpaulo eap_sim_msg_add(msg, EAP_SIM_AT_RESULT_IND, 0, NULL, 0); 239214501Srpaulo } 240214501Srpaulo 241214501Srpaulo wpa_printf(MSG_DEBUG, " AT_MAC"); 242214501Srpaulo eap_sim_msg_add_mac(msg, EAP_SIM_AT_MAC); 243214501Srpaulo return eap_sim_msg_finish(msg, data->k_aut, data->nonce_mt, 244214501Srpaulo EAP_SIM_NONCE_MT_LEN); 245214501Srpaulo} 246214501Srpaulo 247214501Srpaulo 248214501Srpaulostatic struct wpabuf * eap_sim_build_reauth(struct eap_sm *sm, 249214501Srpaulo struct eap_sim_data *data, u8 id) 250214501Srpaulo{ 251214501Srpaulo struct eap_sim_msg *msg; 252214501Srpaulo 253214501Srpaulo wpa_printf(MSG_DEBUG, "EAP-SIM: Generating Re-authentication"); 254214501Srpaulo 255252726Srpaulo if (random_get_bytes(data->nonce_s, EAP_SIM_NONCE_S_LEN)) 256214501Srpaulo return NULL; 257214501Srpaulo wpa_hexdump_key(MSG_MSGDUMP, "EAP-SIM: NONCE_S", 258214501Srpaulo data->nonce_s, EAP_SIM_NONCE_S_LEN); 259214501Srpaulo 260214501Srpaulo eap_sim_derive_keys(data->mk, data->k_encr, data->k_aut, data->msk, 261214501Srpaulo data->emsk); 262214501Srpaulo eap_sim_derive_keys_reauth(data->counter, sm->identity, 263214501Srpaulo sm->identity_len, data->nonce_s, data->mk, 264214501Srpaulo data->msk, data->emsk); 265214501Srpaulo 266214501Srpaulo msg = eap_sim_msg_init(EAP_CODE_REQUEST, id, EAP_TYPE_SIM, 267214501Srpaulo EAP_SIM_SUBTYPE_REAUTHENTICATION); 268214501Srpaulo 269214501Srpaulo if (eap_sim_build_encr(sm, data, msg, data->counter, data->nonce_s)) { 270214501Srpaulo eap_sim_msg_free(msg); 271214501Srpaulo return NULL; 272214501Srpaulo } 273214501Srpaulo 274214501Srpaulo if (sm->eap_sim_aka_result_ind) { 275214501Srpaulo wpa_printf(MSG_DEBUG, " AT_RESULT_IND"); 276214501Srpaulo eap_sim_msg_add(msg, EAP_SIM_AT_RESULT_IND, 0, NULL, 0); 277214501Srpaulo } 278214501Srpaulo 279214501Srpaulo wpa_printf(MSG_DEBUG, " AT_MAC"); 280214501Srpaulo eap_sim_msg_add_mac(msg, EAP_SIM_AT_MAC); 281214501Srpaulo return eap_sim_msg_finish(msg, data->k_aut, NULL, 0); 282214501Srpaulo} 283214501Srpaulo 284214501Srpaulo 285214501Srpaulostatic struct wpabuf * eap_sim_build_notification(struct eap_sm *sm, 286214501Srpaulo struct eap_sim_data *data, 287214501Srpaulo u8 id) 288214501Srpaulo{ 289214501Srpaulo struct eap_sim_msg *msg; 290214501Srpaulo 291214501Srpaulo wpa_printf(MSG_DEBUG, "EAP-SIM: Generating Notification"); 292214501Srpaulo msg = eap_sim_msg_init(EAP_CODE_REQUEST, id, EAP_TYPE_SIM, 293214501Srpaulo EAP_SIM_SUBTYPE_NOTIFICATION); 294214501Srpaulo wpa_printf(MSG_DEBUG, " AT_NOTIFICATION (%d)", data->notification); 295214501Srpaulo eap_sim_msg_add(msg, EAP_SIM_AT_NOTIFICATION, data->notification, 296214501Srpaulo NULL, 0); 297214501Srpaulo if (data->use_result_ind) { 298214501Srpaulo if (data->reauth) { 299214501Srpaulo wpa_printf(MSG_DEBUG, " AT_IV"); 300214501Srpaulo wpa_printf(MSG_DEBUG, " AT_ENCR_DATA"); 301214501Srpaulo eap_sim_msg_add_encr_start(msg, EAP_SIM_AT_IV, 302214501Srpaulo EAP_SIM_AT_ENCR_DATA); 303214501Srpaulo wpa_printf(MSG_DEBUG, " *AT_COUNTER (%u)", 304214501Srpaulo data->counter); 305214501Srpaulo eap_sim_msg_add(msg, EAP_SIM_AT_COUNTER, data->counter, 306214501Srpaulo NULL, 0); 307214501Srpaulo 308214501Srpaulo if (eap_sim_msg_add_encr_end(msg, data->k_encr, 309214501Srpaulo EAP_SIM_AT_PADDING)) { 310214501Srpaulo wpa_printf(MSG_WARNING, "EAP-SIM: Failed to " 311214501Srpaulo "encrypt AT_ENCR_DATA"); 312214501Srpaulo eap_sim_msg_free(msg); 313214501Srpaulo return NULL; 314214501Srpaulo } 315214501Srpaulo } 316214501Srpaulo 317214501Srpaulo wpa_printf(MSG_DEBUG, " AT_MAC"); 318214501Srpaulo eap_sim_msg_add_mac(msg, EAP_SIM_AT_MAC); 319214501Srpaulo } 320214501Srpaulo return eap_sim_msg_finish(msg, data->k_aut, NULL, 0); 321214501Srpaulo} 322214501Srpaulo 323214501Srpaulo 324214501Srpaulostatic struct wpabuf * eap_sim_buildReq(struct eap_sm *sm, void *priv, u8 id) 325214501Srpaulo{ 326214501Srpaulo struct eap_sim_data *data = priv; 327214501Srpaulo 328214501Srpaulo switch (data->state) { 329214501Srpaulo case START: 330214501Srpaulo return eap_sim_build_start(sm, data, id); 331214501Srpaulo case CHALLENGE: 332214501Srpaulo return eap_sim_build_challenge(sm, data, id); 333214501Srpaulo case REAUTH: 334214501Srpaulo return eap_sim_build_reauth(sm, data, id); 335214501Srpaulo case NOTIFICATION: 336214501Srpaulo return eap_sim_build_notification(sm, data, id); 337214501Srpaulo default: 338214501Srpaulo wpa_printf(MSG_DEBUG, "EAP-SIM: Unknown state %d in " 339214501Srpaulo "buildReq", data->state); 340214501Srpaulo break; 341214501Srpaulo } 342214501Srpaulo return NULL; 343214501Srpaulo} 344214501Srpaulo 345214501Srpaulo 346214501Srpaulostatic Boolean eap_sim_check(struct eap_sm *sm, void *priv, 347214501Srpaulo struct wpabuf *respData) 348214501Srpaulo{ 349214501Srpaulo const u8 *pos; 350214501Srpaulo size_t len; 351214501Srpaulo 352214501Srpaulo pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_SIM, respData, &len); 353214501Srpaulo if (pos == NULL || len < 3) { 354214501Srpaulo wpa_printf(MSG_INFO, "EAP-SIM: Invalid frame"); 355214501Srpaulo return TRUE; 356214501Srpaulo } 357214501Srpaulo 358252726Srpaulo return FALSE; 359252726Srpaulo} 360252726Srpaulo 361252726Srpaulo 362252726Srpaulostatic Boolean eap_sim_unexpected_subtype(struct eap_sim_data *data, 363252726Srpaulo u8 subtype) 364252726Srpaulo{ 365214501Srpaulo if (subtype == EAP_SIM_SUBTYPE_CLIENT_ERROR) 366214501Srpaulo return FALSE; 367214501Srpaulo 368214501Srpaulo switch (data->state) { 369214501Srpaulo case START: 370214501Srpaulo if (subtype != EAP_SIM_SUBTYPE_START) { 371214501Srpaulo wpa_printf(MSG_INFO, "EAP-SIM: Unexpected response " 372214501Srpaulo "subtype %d", subtype); 373214501Srpaulo return TRUE; 374214501Srpaulo } 375214501Srpaulo break; 376214501Srpaulo case CHALLENGE: 377214501Srpaulo if (subtype != EAP_SIM_SUBTYPE_CHALLENGE) { 378214501Srpaulo wpa_printf(MSG_INFO, "EAP-SIM: Unexpected response " 379214501Srpaulo "subtype %d", subtype); 380214501Srpaulo return TRUE; 381214501Srpaulo } 382214501Srpaulo break; 383214501Srpaulo case REAUTH: 384214501Srpaulo if (subtype != EAP_SIM_SUBTYPE_REAUTHENTICATION) { 385214501Srpaulo wpa_printf(MSG_INFO, "EAP-SIM: Unexpected response " 386214501Srpaulo "subtype %d", subtype); 387214501Srpaulo return TRUE; 388214501Srpaulo } 389214501Srpaulo break; 390214501Srpaulo case NOTIFICATION: 391214501Srpaulo if (subtype != EAP_SIM_SUBTYPE_NOTIFICATION) { 392214501Srpaulo wpa_printf(MSG_INFO, "EAP-SIM: Unexpected response " 393214501Srpaulo "subtype %d", subtype); 394214501Srpaulo return TRUE; 395214501Srpaulo } 396214501Srpaulo break; 397214501Srpaulo default: 398214501Srpaulo wpa_printf(MSG_INFO, "EAP-SIM: Unexpected state (%d) for " 399214501Srpaulo "processing a response", data->state); 400214501Srpaulo return TRUE; 401214501Srpaulo } 402214501Srpaulo 403214501Srpaulo return FALSE; 404214501Srpaulo} 405214501Srpaulo 406214501Srpaulo 407214501Srpaulostatic int eap_sim_supported_ver(struct eap_sim_data *data, int version) 408214501Srpaulo{ 409214501Srpaulo return version == EAP_SIM_VERSION; 410214501Srpaulo} 411214501Srpaulo 412214501Srpaulo 413214501Srpaulostatic void eap_sim_process_start(struct eap_sm *sm, 414214501Srpaulo struct eap_sim_data *data, 415214501Srpaulo struct wpabuf *respData, 416214501Srpaulo struct eap_sim_attrs *attr) 417214501Srpaulo{ 418214501Srpaulo size_t identity_len; 419214501Srpaulo u8 ver_list[2]; 420252726Srpaulo u8 *new_identity; 421252726Srpaulo char *username; 422214501Srpaulo 423214501Srpaulo wpa_printf(MSG_DEBUG, "EAP-SIM: Receive start response"); 424214501Srpaulo 425252726Srpaulo if (data->start_round == 0) { 426252726Srpaulo /* 427252726Srpaulo * Special case for AT_COUNTER_TOO_SMALL recovery - no identity 428252726Srpaulo * was requested since we already know it. 429252726Srpaulo */ 430252726Srpaulo goto skip_id_update; 431214501Srpaulo } 432214501Srpaulo 433252726Srpaulo /* 434252726Srpaulo * We always request identity in SIM/Start, so the peer is required to 435252726Srpaulo * have replied with one. 436252726Srpaulo */ 437252726Srpaulo if (!attr->identity || attr->identity_len == 0) { 438252726Srpaulo wpa_printf(MSG_DEBUG, "EAP-SIM: Peer did not provide any " 439252726Srpaulo "identity"); 440252726Srpaulo goto failed; 441214501Srpaulo } 442214501Srpaulo 443252726Srpaulo new_identity = os_malloc(attr->identity_len); 444252726Srpaulo if (new_identity == NULL) 445252726Srpaulo goto failed; 446252726Srpaulo os_free(sm->identity); 447252726Srpaulo sm->identity = new_identity; 448252726Srpaulo os_memcpy(sm->identity, attr->identity, attr->identity_len); 449252726Srpaulo sm->identity_len = attr->identity_len; 450214501Srpaulo 451214501Srpaulo wpa_hexdump_ascii(MSG_DEBUG, "EAP-SIM: Identity", 452252726Srpaulo sm->identity, sm->identity_len); 453252726Srpaulo username = sim_get_username(sm->identity, sm->identity_len); 454252726Srpaulo if (username == NULL) 455252726Srpaulo goto failed; 456214501Srpaulo 457252726Srpaulo if (username[0] == EAP_SIM_REAUTH_ID_PREFIX) { 458252726Srpaulo wpa_printf(MSG_DEBUG, "EAP-SIM: Reauth username '%s'", 459252726Srpaulo username); 460252726Srpaulo data->reauth = eap_sim_db_get_reauth_entry( 461252726Srpaulo sm->eap_sim_db_priv, username); 462252726Srpaulo os_free(username); 463252726Srpaulo if (data->reauth == NULL) { 464252726Srpaulo wpa_printf(MSG_DEBUG, "EAP-SIM: Unknown reauth " 465252726Srpaulo "identity - request full auth identity"); 466252726Srpaulo /* Remain in START state for another round */ 467252726Srpaulo return; 468252726Srpaulo } 469252726Srpaulo wpa_printf(MSG_DEBUG, "EAP-SIM: Using fast re-authentication"); 470252726Srpaulo os_strlcpy(data->permanent, data->reauth->permanent, 471252726Srpaulo sizeof(data->permanent)); 472252726Srpaulo data->counter = data->reauth->counter; 473252726Srpaulo os_memcpy(data->mk, data->reauth->mk, EAP_SIM_MK_LEN); 474214501Srpaulo eap_sim_state(data, REAUTH); 475214501Srpaulo return; 476214501Srpaulo } 477214501Srpaulo 478252726Srpaulo if (username[0] == EAP_SIM_PSEUDONYM_PREFIX) { 479252726Srpaulo const char *permanent; 480252726Srpaulo wpa_printf(MSG_DEBUG, "EAP-SIM: Pseudonym username '%s'", 481252726Srpaulo username); 482252726Srpaulo permanent = eap_sim_db_get_permanent( 483252726Srpaulo sm->eap_sim_db_priv, username); 484252726Srpaulo os_free(username); 485252726Srpaulo if (permanent == NULL) { 486252726Srpaulo wpa_printf(MSG_DEBUG, "EAP-SIM: Unknown pseudonym " 487252726Srpaulo "identity - request permanent identity"); 488252726Srpaulo /* Remain in START state for another round */ 489252726Srpaulo return; 490252726Srpaulo } 491252726Srpaulo os_strlcpy(data->permanent, permanent, 492252726Srpaulo sizeof(data->permanent)); 493252726Srpaulo } else if (username[0] == EAP_SIM_PERMANENT_PREFIX) { 494252726Srpaulo wpa_printf(MSG_DEBUG, "EAP-SIM: Permanent username '%s'", 495252726Srpaulo username); 496252726Srpaulo os_strlcpy(data->permanent, username, sizeof(data->permanent)); 497252726Srpaulo os_free(username); 498252726Srpaulo } else { 499252726Srpaulo wpa_printf(MSG_DEBUG, "EAP-SIM: Unrecognized username '%s'", 500252726Srpaulo username); 501252726Srpaulo os_free(username); 502252726Srpaulo goto failed; 503252726Srpaulo } 504252726Srpaulo 505252726Srpauloskip_id_update: 506252726Srpaulo /* Full authentication */ 507252726Srpaulo 508214501Srpaulo if (attr->nonce_mt == NULL || attr->selected_version < 0) { 509214501Srpaulo wpa_printf(MSG_DEBUG, "EAP-SIM: Start/Response missing " 510214501Srpaulo "required attributes"); 511252726Srpaulo goto failed; 512214501Srpaulo } 513214501Srpaulo 514214501Srpaulo if (!eap_sim_supported_ver(data, attr->selected_version)) { 515214501Srpaulo wpa_printf(MSG_DEBUG, "EAP-SIM: Peer selected unsupported " 516214501Srpaulo "version %d", attr->selected_version); 517252726Srpaulo goto failed; 518214501Srpaulo } 519214501Srpaulo 520214501Srpaulo data->counter = 0; /* reset re-auth counter since this is full auth */ 521214501Srpaulo data->reauth = NULL; 522214501Srpaulo 523214501Srpaulo data->num_chal = eap_sim_db_get_gsm_triplets( 524252726Srpaulo sm->eap_sim_db_priv, data->permanent, EAP_SIM_MAX_CHAL, 525214501Srpaulo (u8 *) data->rand, (u8 *) data->kc, (u8 *) data->sres, sm); 526214501Srpaulo if (data->num_chal == EAP_SIM_DB_PENDING) { 527214501Srpaulo wpa_printf(MSG_DEBUG, "EAP-SIM: GSM authentication triplets " 528214501Srpaulo "not yet available - pending request"); 529214501Srpaulo sm->method_pending = METHOD_PENDING_WAIT; 530214501Srpaulo return; 531214501Srpaulo } 532214501Srpaulo if (data->num_chal < 2) { 533214501Srpaulo wpa_printf(MSG_INFO, "EAP-SIM: Failed to get GSM " 534214501Srpaulo "authentication triplets for the peer"); 535252726Srpaulo goto failed; 536214501Srpaulo } 537214501Srpaulo 538214501Srpaulo identity_len = sm->identity_len; 539214501Srpaulo while (identity_len > 0 && sm->identity[identity_len - 1] == '\0') { 540214501Srpaulo wpa_printf(MSG_DEBUG, "EAP-SIM: Workaround - drop last null " 541214501Srpaulo "character from identity"); 542214501Srpaulo identity_len--; 543214501Srpaulo } 544214501Srpaulo wpa_hexdump_ascii(MSG_DEBUG, "EAP-SIM: Identity for MK derivation", 545214501Srpaulo sm->identity, identity_len); 546214501Srpaulo 547214501Srpaulo os_memcpy(data->nonce_mt, attr->nonce_mt, EAP_SIM_NONCE_MT_LEN); 548214501Srpaulo WPA_PUT_BE16(ver_list, EAP_SIM_VERSION); 549214501Srpaulo eap_sim_derive_mk(sm->identity, identity_len, attr->nonce_mt, 550214501Srpaulo attr->selected_version, ver_list, sizeof(ver_list), 551214501Srpaulo data->num_chal, (const u8 *) data->kc, data->mk); 552214501Srpaulo eap_sim_derive_keys(data->mk, data->k_encr, data->k_aut, data->msk, 553214501Srpaulo data->emsk); 554214501Srpaulo 555214501Srpaulo eap_sim_state(data, CHALLENGE); 556252726Srpaulo return; 557252726Srpaulo 558252726Srpaulofailed: 559252726Srpaulo data->notification = EAP_SIM_GENERAL_FAILURE_BEFORE_AUTH; 560252726Srpaulo eap_sim_state(data, NOTIFICATION); 561214501Srpaulo} 562214501Srpaulo 563214501Srpaulo 564214501Srpaulostatic void eap_sim_process_challenge(struct eap_sm *sm, 565214501Srpaulo struct eap_sim_data *data, 566214501Srpaulo struct wpabuf *respData, 567214501Srpaulo struct eap_sim_attrs *attr) 568214501Srpaulo{ 569214501Srpaulo if (attr->mac == NULL || 570214501Srpaulo eap_sim_verify_mac(data->k_aut, respData, attr->mac, 571214501Srpaulo (u8 *) data->sres, 572214501Srpaulo data->num_chal * EAP_SIM_SRES_LEN)) { 573214501Srpaulo wpa_printf(MSG_WARNING, "EAP-SIM: Challenge message " 574214501Srpaulo "did not include valid AT_MAC"); 575252726Srpaulo data->notification = EAP_SIM_GENERAL_FAILURE_BEFORE_AUTH; 576252726Srpaulo eap_sim_state(data, NOTIFICATION); 577214501Srpaulo return; 578214501Srpaulo } 579214501Srpaulo 580214501Srpaulo wpa_printf(MSG_DEBUG, "EAP-SIM: Challenge response includes the " 581214501Srpaulo "correct AT_MAC"); 582214501Srpaulo if (sm->eap_sim_aka_result_ind && attr->result_ind) { 583214501Srpaulo data->use_result_ind = 1; 584214501Srpaulo data->notification = EAP_SIM_SUCCESS; 585214501Srpaulo eap_sim_state(data, NOTIFICATION); 586214501Srpaulo } else 587214501Srpaulo eap_sim_state(data, SUCCESS); 588214501Srpaulo 589214501Srpaulo if (data->next_pseudonym) { 590252726Srpaulo eap_sim_db_add_pseudonym(sm->eap_sim_db_priv, data->permanent, 591214501Srpaulo data->next_pseudonym); 592214501Srpaulo data->next_pseudonym = NULL; 593214501Srpaulo } 594214501Srpaulo if (data->next_reauth_id) { 595252726Srpaulo eap_sim_db_add_reauth(sm->eap_sim_db_priv, data->permanent, 596214501Srpaulo data->next_reauth_id, data->counter + 1, 597214501Srpaulo data->mk); 598214501Srpaulo data->next_reauth_id = NULL; 599214501Srpaulo } 600214501Srpaulo} 601214501Srpaulo 602214501Srpaulo 603214501Srpaulostatic void eap_sim_process_reauth(struct eap_sm *sm, 604214501Srpaulo struct eap_sim_data *data, 605214501Srpaulo struct wpabuf *respData, 606214501Srpaulo struct eap_sim_attrs *attr) 607214501Srpaulo{ 608214501Srpaulo struct eap_sim_attrs eattr; 609214501Srpaulo u8 *decrypted = NULL; 610214501Srpaulo 611214501Srpaulo if (attr->mac == NULL || 612214501Srpaulo eap_sim_verify_mac(data->k_aut, respData, attr->mac, data->nonce_s, 613214501Srpaulo EAP_SIM_NONCE_S_LEN)) { 614214501Srpaulo wpa_printf(MSG_WARNING, "EAP-SIM: Re-authentication message " 615214501Srpaulo "did not include valid AT_MAC"); 616214501Srpaulo goto fail; 617214501Srpaulo } 618214501Srpaulo 619214501Srpaulo if (attr->encr_data == NULL || attr->iv == NULL) { 620214501Srpaulo wpa_printf(MSG_WARNING, "EAP-SIM: Reauthentication " 621214501Srpaulo "message did not include encrypted data"); 622214501Srpaulo goto fail; 623214501Srpaulo } 624214501Srpaulo 625214501Srpaulo decrypted = eap_sim_parse_encr(data->k_encr, attr->encr_data, 626214501Srpaulo attr->encr_data_len, attr->iv, &eattr, 627214501Srpaulo 0); 628214501Srpaulo if (decrypted == NULL) { 629214501Srpaulo wpa_printf(MSG_WARNING, "EAP-SIM: Failed to parse encrypted " 630214501Srpaulo "data from reauthentication message"); 631214501Srpaulo goto fail; 632214501Srpaulo } 633214501Srpaulo 634214501Srpaulo if (eattr.counter != data->counter) { 635214501Srpaulo wpa_printf(MSG_WARNING, "EAP-SIM: Re-authentication message " 636214501Srpaulo "used incorrect counter %u, expected %u", 637214501Srpaulo eattr.counter, data->counter); 638214501Srpaulo goto fail; 639214501Srpaulo } 640214501Srpaulo os_free(decrypted); 641214501Srpaulo decrypted = NULL; 642214501Srpaulo 643214501Srpaulo wpa_printf(MSG_DEBUG, "EAP-SIM: Re-authentication response includes " 644214501Srpaulo "the correct AT_MAC"); 645252726Srpaulo 646252726Srpaulo if (eattr.counter_too_small) { 647252726Srpaulo wpa_printf(MSG_DEBUG, "EAP-AKA: Re-authentication response " 648252726Srpaulo "included AT_COUNTER_TOO_SMALL - starting full " 649252726Srpaulo "authentication"); 650252726Srpaulo data->start_round = -1; 651252726Srpaulo eap_sim_state(data, START); 652252726Srpaulo return; 653252726Srpaulo } 654252726Srpaulo 655214501Srpaulo if (sm->eap_sim_aka_result_ind && attr->result_ind) { 656214501Srpaulo data->use_result_ind = 1; 657214501Srpaulo data->notification = EAP_SIM_SUCCESS; 658214501Srpaulo eap_sim_state(data, NOTIFICATION); 659214501Srpaulo } else 660214501Srpaulo eap_sim_state(data, SUCCESS); 661214501Srpaulo 662214501Srpaulo if (data->next_reauth_id) { 663252726Srpaulo eap_sim_db_add_reauth(sm->eap_sim_db_priv, data->permanent, 664252726Srpaulo data->next_reauth_id, 665214501Srpaulo data->counter + 1, data->mk); 666214501Srpaulo data->next_reauth_id = NULL; 667214501Srpaulo } else { 668214501Srpaulo eap_sim_db_remove_reauth(sm->eap_sim_db_priv, data->reauth); 669214501Srpaulo data->reauth = NULL; 670214501Srpaulo } 671214501Srpaulo 672214501Srpaulo return; 673214501Srpaulo 674214501Srpaulofail: 675252726Srpaulo data->notification = EAP_SIM_GENERAL_FAILURE_BEFORE_AUTH; 676252726Srpaulo eap_sim_state(data, NOTIFICATION); 677214501Srpaulo eap_sim_db_remove_reauth(sm->eap_sim_db_priv, data->reauth); 678214501Srpaulo data->reauth = NULL; 679214501Srpaulo os_free(decrypted); 680214501Srpaulo} 681214501Srpaulo 682214501Srpaulo 683214501Srpaulostatic void eap_sim_process_client_error(struct eap_sm *sm, 684214501Srpaulo struct eap_sim_data *data, 685214501Srpaulo struct wpabuf *respData, 686214501Srpaulo struct eap_sim_attrs *attr) 687214501Srpaulo{ 688214501Srpaulo wpa_printf(MSG_DEBUG, "EAP-SIM: Client reported error %d", 689214501Srpaulo attr->client_error_code); 690214501Srpaulo if (data->notification == EAP_SIM_SUCCESS && data->use_result_ind) 691214501Srpaulo eap_sim_state(data, SUCCESS); 692214501Srpaulo else 693214501Srpaulo eap_sim_state(data, FAILURE); 694214501Srpaulo} 695214501Srpaulo 696214501Srpaulo 697214501Srpaulostatic void eap_sim_process_notification(struct eap_sm *sm, 698214501Srpaulo struct eap_sim_data *data, 699214501Srpaulo struct wpabuf *respData, 700214501Srpaulo struct eap_sim_attrs *attr) 701214501Srpaulo{ 702214501Srpaulo wpa_printf(MSG_DEBUG, "EAP-SIM: Client replied to notification"); 703214501Srpaulo if (data->notification == EAP_SIM_SUCCESS && data->use_result_ind) 704214501Srpaulo eap_sim_state(data, SUCCESS); 705214501Srpaulo else 706214501Srpaulo eap_sim_state(data, FAILURE); 707214501Srpaulo} 708214501Srpaulo 709214501Srpaulo 710214501Srpaulostatic void eap_sim_process(struct eap_sm *sm, void *priv, 711214501Srpaulo struct wpabuf *respData) 712214501Srpaulo{ 713214501Srpaulo struct eap_sim_data *data = priv; 714214501Srpaulo const u8 *pos, *end; 715214501Srpaulo u8 subtype; 716214501Srpaulo size_t len; 717214501Srpaulo struct eap_sim_attrs attr; 718214501Srpaulo 719214501Srpaulo pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_SIM, respData, &len); 720214501Srpaulo if (pos == NULL || len < 3) 721214501Srpaulo return; 722214501Srpaulo 723214501Srpaulo end = pos + len; 724214501Srpaulo subtype = *pos; 725214501Srpaulo pos += 3; 726214501Srpaulo 727252726Srpaulo if (eap_sim_unexpected_subtype(data, subtype)) { 728252726Srpaulo wpa_printf(MSG_DEBUG, "EAP-SIM: Unrecognized or unexpected " 729252726Srpaulo "EAP-SIM Subtype in EAP Response"); 730252726Srpaulo data->notification = EAP_SIM_GENERAL_FAILURE_BEFORE_AUTH; 731252726Srpaulo eap_sim_state(data, NOTIFICATION); 732252726Srpaulo return; 733252726Srpaulo } 734252726Srpaulo 735214501Srpaulo if (eap_sim_parse_attr(pos, end, &attr, 0, 0)) { 736214501Srpaulo wpa_printf(MSG_DEBUG, "EAP-SIM: Failed to parse attributes"); 737252726Srpaulo if (subtype != EAP_SIM_SUBTYPE_CLIENT_ERROR && 738252726Srpaulo (data->state == START || data->state == CHALLENGE || 739252726Srpaulo data->state == REAUTH)) { 740252726Srpaulo data->notification = 741252726Srpaulo EAP_SIM_GENERAL_FAILURE_BEFORE_AUTH; 742252726Srpaulo eap_sim_state(data, NOTIFICATION); 743252726Srpaulo return; 744252726Srpaulo } 745214501Srpaulo eap_sim_state(data, FAILURE); 746214501Srpaulo return; 747214501Srpaulo } 748214501Srpaulo 749214501Srpaulo if (subtype == EAP_SIM_SUBTYPE_CLIENT_ERROR) { 750214501Srpaulo eap_sim_process_client_error(sm, data, respData, &attr); 751214501Srpaulo return; 752214501Srpaulo } 753214501Srpaulo 754214501Srpaulo switch (data->state) { 755214501Srpaulo case START: 756214501Srpaulo eap_sim_process_start(sm, data, respData, &attr); 757214501Srpaulo break; 758214501Srpaulo case CHALLENGE: 759214501Srpaulo eap_sim_process_challenge(sm, data, respData, &attr); 760214501Srpaulo break; 761214501Srpaulo case REAUTH: 762214501Srpaulo eap_sim_process_reauth(sm, data, respData, &attr); 763214501Srpaulo break; 764214501Srpaulo case NOTIFICATION: 765214501Srpaulo eap_sim_process_notification(sm, data, respData, &attr); 766214501Srpaulo break; 767214501Srpaulo default: 768214501Srpaulo wpa_printf(MSG_DEBUG, "EAP-SIM: Unknown state %d in " 769214501Srpaulo "process", data->state); 770214501Srpaulo break; 771214501Srpaulo } 772214501Srpaulo} 773214501Srpaulo 774214501Srpaulo 775214501Srpaulostatic Boolean eap_sim_isDone(struct eap_sm *sm, void *priv) 776214501Srpaulo{ 777214501Srpaulo struct eap_sim_data *data = priv; 778214501Srpaulo return data->state == SUCCESS || data->state == FAILURE; 779214501Srpaulo} 780214501Srpaulo 781214501Srpaulo 782214501Srpaulostatic u8 * eap_sim_getKey(struct eap_sm *sm, void *priv, size_t *len) 783214501Srpaulo{ 784214501Srpaulo struct eap_sim_data *data = priv; 785214501Srpaulo u8 *key; 786214501Srpaulo 787214501Srpaulo if (data->state != SUCCESS) 788214501Srpaulo return NULL; 789214501Srpaulo 790214501Srpaulo key = os_malloc(EAP_SIM_KEYING_DATA_LEN); 791214501Srpaulo if (key == NULL) 792214501Srpaulo return NULL; 793214501Srpaulo os_memcpy(key, data->msk, EAP_SIM_KEYING_DATA_LEN); 794214501Srpaulo *len = EAP_SIM_KEYING_DATA_LEN; 795214501Srpaulo return key; 796214501Srpaulo} 797214501Srpaulo 798214501Srpaulo 799214501Srpaulostatic u8 * eap_sim_get_emsk(struct eap_sm *sm, void *priv, size_t *len) 800214501Srpaulo{ 801214501Srpaulo struct eap_sim_data *data = priv; 802214501Srpaulo u8 *key; 803214501Srpaulo 804214501Srpaulo if (data->state != SUCCESS) 805214501Srpaulo return NULL; 806214501Srpaulo 807214501Srpaulo key = os_malloc(EAP_EMSK_LEN); 808214501Srpaulo if (key == NULL) 809214501Srpaulo return NULL; 810214501Srpaulo os_memcpy(key, data->emsk, EAP_EMSK_LEN); 811214501Srpaulo *len = EAP_EMSK_LEN; 812214501Srpaulo return key; 813214501Srpaulo} 814214501Srpaulo 815214501Srpaulo 816214501Srpaulostatic Boolean eap_sim_isSuccess(struct eap_sm *sm, void *priv) 817214501Srpaulo{ 818214501Srpaulo struct eap_sim_data *data = priv; 819214501Srpaulo return data->state == SUCCESS; 820214501Srpaulo} 821214501Srpaulo 822214501Srpaulo 823214501Srpauloint eap_server_sim_register(void) 824214501Srpaulo{ 825214501Srpaulo struct eap_method *eap; 826214501Srpaulo int ret; 827214501Srpaulo 828214501Srpaulo eap = eap_server_method_alloc(EAP_SERVER_METHOD_INTERFACE_VERSION, 829214501Srpaulo EAP_VENDOR_IETF, EAP_TYPE_SIM, "SIM"); 830214501Srpaulo if (eap == NULL) 831214501Srpaulo return -1; 832214501Srpaulo 833214501Srpaulo eap->init = eap_sim_init; 834214501Srpaulo eap->reset = eap_sim_reset; 835214501Srpaulo eap->buildReq = eap_sim_buildReq; 836214501Srpaulo eap->check = eap_sim_check; 837214501Srpaulo eap->process = eap_sim_process; 838214501Srpaulo eap->isDone = eap_sim_isDone; 839214501Srpaulo eap->getKey = eap_sim_getKey; 840214501Srpaulo eap->isSuccess = eap_sim_isSuccess; 841214501Srpaulo eap->get_emsk = eap_sim_get_emsk; 842214501Srpaulo 843214501Srpaulo ret = eap_server_method_register(eap); 844214501Srpaulo if (ret) 845214501Srpaulo eap_server_method_free(eap); 846214501Srpaulo return ret; 847214501Srpaulo} 848