1214501Srpaulo/* 2214501Srpaulo * hostapd / EAP-AKA (RFC 4187) and EAP-AKA' (draft-arkko-eap-aka-kdf) 3214501Srpaulo * Copyright (c) 2005-2008, Jouni Malinen <j@w1.fi> 4214501Srpaulo * 5214501Srpaulo * This program is free software; you can redistribute it and/or modify 6214501Srpaulo * it under the terms of the GNU General Public License version 2 as 7214501Srpaulo * published by the Free Software Foundation. 8214501Srpaulo * 9214501Srpaulo * Alternatively, this software may be distributed under the terms of BSD 10214501Srpaulo * license. 11214501Srpaulo * 12214501Srpaulo * See README and COPYING for more details. 13214501Srpaulo */ 14214501Srpaulo 15214501Srpaulo#include "includes.h" 16214501Srpaulo 17214501Srpaulo#include "common.h" 18214501Srpaulo#include "crypto/sha256.h" 19214501Srpaulo#include "crypto/crypto.h" 20214501Srpaulo#include "eap_common/eap_sim_common.h" 21214501Srpaulo#include "eap_server/eap_i.h" 22214501Srpaulo#include "eap_server/eap_sim_db.h" 23214501Srpaulo 24214501Srpaulo 25214501Srpaulostruct eap_aka_data { 26214501Srpaulo u8 mk[EAP_SIM_MK_LEN]; 27214501Srpaulo u8 nonce_s[EAP_SIM_NONCE_S_LEN]; 28214501Srpaulo u8 k_aut[EAP_AKA_PRIME_K_AUT_LEN]; 29214501Srpaulo u8 k_encr[EAP_SIM_K_ENCR_LEN]; 30214501Srpaulo u8 k_re[EAP_AKA_PRIME_K_RE_LEN]; /* EAP-AKA' only */ 31214501Srpaulo u8 msk[EAP_SIM_KEYING_DATA_LEN]; 32214501Srpaulo u8 emsk[EAP_EMSK_LEN]; 33214501Srpaulo u8 rand[EAP_AKA_RAND_LEN]; 34214501Srpaulo u8 autn[EAP_AKA_AUTN_LEN]; 35214501Srpaulo u8 ck[EAP_AKA_CK_LEN]; 36214501Srpaulo u8 ik[EAP_AKA_IK_LEN]; 37214501Srpaulo u8 res[EAP_AKA_RES_MAX_LEN]; 38214501Srpaulo size_t res_len; 39214501Srpaulo enum { 40214501Srpaulo IDENTITY, CHALLENGE, REAUTH, NOTIFICATION, SUCCESS, FAILURE 41214501Srpaulo } state; 42214501Srpaulo char *next_pseudonym; 43214501Srpaulo char *next_reauth_id; 44214501Srpaulo u16 counter; 45214501Srpaulo struct eap_sim_reauth *reauth; 46214501Srpaulo int auts_reported; /* whether the current AUTS has been reported to the 47214501Srpaulo * eap_sim_db */ 48214501Srpaulo u16 notification; 49214501Srpaulo int use_result_ind; 50214501Srpaulo 51214501Srpaulo struct wpabuf *id_msgs; 52214501Srpaulo int pending_id; 53214501Srpaulo u8 eap_method; 54214501Srpaulo u8 *network_name; 55214501Srpaulo size_t network_name_len; 56214501Srpaulo u16 kdf; 57214501Srpaulo}; 58214501Srpaulo 59214501Srpaulo 60214501Srpaulostatic void eap_aka_determine_identity(struct eap_sm *sm, 61214501Srpaulo struct eap_aka_data *data, 62214501Srpaulo int before_identity, int after_reauth); 63214501Srpaulo 64214501Srpaulo 65214501Srpaulostatic const char * eap_aka_state_txt(int state) 66214501Srpaulo{ 67214501Srpaulo switch (state) { 68214501Srpaulo case IDENTITY: 69214501Srpaulo return "IDENTITY"; 70214501Srpaulo case CHALLENGE: 71214501Srpaulo return "CHALLENGE"; 72214501Srpaulo case REAUTH: 73214501Srpaulo return "REAUTH"; 74214501Srpaulo case SUCCESS: 75214501Srpaulo return "SUCCESS"; 76214501Srpaulo case FAILURE: 77214501Srpaulo return "FAILURE"; 78214501Srpaulo case NOTIFICATION: 79214501Srpaulo return "NOTIFICATION"; 80214501Srpaulo default: 81214501Srpaulo return "Unknown?!"; 82214501Srpaulo } 83214501Srpaulo} 84214501Srpaulo 85214501Srpaulo 86214501Srpaulostatic void eap_aka_state(struct eap_aka_data *data, int state) 87214501Srpaulo{ 88214501Srpaulo wpa_printf(MSG_DEBUG, "EAP-AKA: %s -> %s", 89214501Srpaulo eap_aka_state_txt(data->state), 90214501Srpaulo eap_aka_state_txt(state)); 91214501Srpaulo data->state = state; 92214501Srpaulo} 93214501Srpaulo 94214501Srpaulo 95214501Srpaulostatic void * eap_aka_init(struct eap_sm *sm) 96214501Srpaulo{ 97214501Srpaulo struct eap_aka_data *data; 98214501Srpaulo 99214501Srpaulo if (sm->eap_sim_db_priv == NULL) { 100214501Srpaulo wpa_printf(MSG_WARNING, "EAP-AKA: eap_sim_db not configured"); 101214501Srpaulo return NULL; 102214501Srpaulo } 103214501Srpaulo 104214501Srpaulo data = os_zalloc(sizeof(*data)); 105214501Srpaulo if (data == NULL) 106214501Srpaulo return NULL; 107214501Srpaulo 108214501Srpaulo data->eap_method = EAP_TYPE_AKA; 109214501Srpaulo 110214501Srpaulo data->state = IDENTITY; 111214501Srpaulo eap_aka_determine_identity(sm, data, 1, 0); 112214501Srpaulo data->pending_id = -1; 113214501Srpaulo 114214501Srpaulo return data; 115214501Srpaulo} 116214501Srpaulo 117214501Srpaulo 118214501Srpaulo#ifdef EAP_SERVER_AKA_PRIME 119214501Srpaulostatic void * eap_aka_prime_init(struct eap_sm *sm) 120214501Srpaulo{ 121214501Srpaulo struct eap_aka_data *data; 122214501Srpaulo /* TODO: make ANID configurable; see 3GPP TS 24.302 */ 123214501Srpaulo char *network_name = "WLAN"; 124214501Srpaulo 125214501Srpaulo if (sm->eap_sim_db_priv == NULL) { 126214501Srpaulo wpa_printf(MSG_WARNING, "EAP-AKA: eap_sim_db not configured"); 127214501Srpaulo return NULL; 128214501Srpaulo } 129214501Srpaulo 130214501Srpaulo data = os_zalloc(sizeof(*data)); 131214501Srpaulo if (data == NULL) 132214501Srpaulo return NULL; 133214501Srpaulo 134214501Srpaulo data->eap_method = EAP_TYPE_AKA_PRIME; 135214501Srpaulo data->network_name = os_malloc(os_strlen(network_name)); 136214501Srpaulo if (data->network_name == NULL) { 137214501Srpaulo os_free(data); 138214501Srpaulo return NULL; 139214501Srpaulo } 140214501Srpaulo 141214501Srpaulo data->network_name_len = os_strlen(network_name); 142214501Srpaulo os_memcpy(data->network_name, network_name, data->network_name_len); 143214501Srpaulo 144214501Srpaulo data->state = IDENTITY; 145214501Srpaulo eap_aka_determine_identity(sm, data, 1, 0); 146214501Srpaulo data->pending_id = -1; 147214501Srpaulo 148214501Srpaulo return data; 149214501Srpaulo} 150214501Srpaulo#endif /* EAP_SERVER_AKA_PRIME */ 151214501Srpaulo 152214501Srpaulo 153214501Srpaulostatic void eap_aka_reset(struct eap_sm *sm, void *priv) 154214501Srpaulo{ 155214501Srpaulo struct eap_aka_data *data = priv; 156214501Srpaulo os_free(data->next_pseudonym); 157214501Srpaulo os_free(data->next_reauth_id); 158214501Srpaulo wpabuf_free(data->id_msgs); 159214501Srpaulo os_free(data->network_name); 160214501Srpaulo os_free(data); 161214501Srpaulo} 162214501Srpaulo 163214501Srpaulo 164214501Srpaulostatic int eap_aka_add_id_msg(struct eap_aka_data *data, 165214501Srpaulo const struct wpabuf *msg) 166214501Srpaulo{ 167214501Srpaulo if (msg == NULL) 168214501Srpaulo return -1; 169214501Srpaulo 170214501Srpaulo if (data->id_msgs == NULL) { 171214501Srpaulo data->id_msgs = wpabuf_dup(msg); 172214501Srpaulo return data->id_msgs == NULL ? -1 : 0; 173214501Srpaulo } 174214501Srpaulo 175214501Srpaulo if (wpabuf_resize(&data->id_msgs, wpabuf_len(msg)) < 0) 176214501Srpaulo return -1; 177214501Srpaulo wpabuf_put_buf(data->id_msgs, msg); 178214501Srpaulo 179214501Srpaulo return 0; 180214501Srpaulo} 181214501Srpaulo 182214501Srpaulo 183214501Srpaulostatic void eap_aka_add_checkcode(struct eap_aka_data *data, 184214501Srpaulo struct eap_sim_msg *msg) 185214501Srpaulo{ 186214501Srpaulo const u8 *addr; 187214501Srpaulo size_t len; 188214501Srpaulo u8 hash[SHA256_MAC_LEN]; 189214501Srpaulo 190214501Srpaulo wpa_printf(MSG_DEBUG, " AT_CHECKCODE"); 191214501Srpaulo 192214501Srpaulo if (data->id_msgs == NULL) { 193214501Srpaulo /* 194214501Srpaulo * No EAP-AKA/Identity packets were exchanged - send empty 195214501Srpaulo * checkcode. 196214501Srpaulo */ 197214501Srpaulo eap_sim_msg_add(msg, EAP_SIM_AT_CHECKCODE, 0, NULL, 0); 198214501Srpaulo return; 199214501Srpaulo } 200214501Srpaulo 201214501Srpaulo /* Checkcode is SHA1 hash over all EAP-AKA/Identity packets. */ 202214501Srpaulo addr = wpabuf_head(data->id_msgs); 203214501Srpaulo len = wpabuf_len(data->id_msgs); 204214501Srpaulo wpa_hexdump(MSG_MSGDUMP, "EAP-AKA: AT_CHECKCODE data", addr, len); 205214501Srpaulo if (data->eap_method == EAP_TYPE_AKA_PRIME) 206214501Srpaulo sha256_vector(1, &addr, &len, hash); 207214501Srpaulo else 208214501Srpaulo sha1_vector(1, &addr, &len, hash); 209214501Srpaulo 210214501Srpaulo eap_sim_msg_add(msg, EAP_SIM_AT_CHECKCODE, 0, hash, 211214501Srpaulo data->eap_method == EAP_TYPE_AKA_PRIME ? 212214501Srpaulo EAP_AKA_PRIME_CHECKCODE_LEN : EAP_AKA_CHECKCODE_LEN); 213214501Srpaulo} 214214501Srpaulo 215214501Srpaulo 216214501Srpaulostatic int eap_aka_verify_checkcode(struct eap_aka_data *data, 217214501Srpaulo const u8 *checkcode, size_t checkcode_len) 218214501Srpaulo{ 219214501Srpaulo const u8 *addr; 220214501Srpaulo size_t len; 221214501Srpaulo u8 hash[SHA256_MAC_LEN]; 222214501Srpaulo size_t hash_len; 223214501Srpaulo 224214501Srpaulo if (checkcode == NULL) 225214501Srpaulo return -1; 226214501Srpaulo 227214501Srpaulo if (data->id_msgs == NULL) { 228214501Srpaulo if (checkcode_len != 0) { 229214501Srpaulo wpa_printf(MSG_DEBUG, "EAP-AKA: Checkcode from peer " 230214501Srpaulo "indicates that AKA/Identity messages were " 231214501Srpaulo "used, but they were not"); 232214501Srpaulo return -1; 233214501Srpaulo } 234214501Srpaulo return 0; 235214501Srpaulo } 236214501Srpaulo 237214501Srpaulo hash_len = data->eap_method == EAP_TYPE_AKA_PRIME ? 238214501Srpaulo EAP_AKA_PRIME_CHECKCODE_LEN : EAP_AKA_CHECKCODE_LEN; 239214501Srpaulo 240214501Srpaulo if (checkcode_len != hash_len) { 241214501Srpaulo wpa_printf(MSG_DEBUG, "EAP-AKA: Checkcode from peer indicates " 242214501Srpaulo "that AKA/Identity message were not used, but they " 243214501Srpaulo "were"); 244214501Srpaulo return -1; 245214501Srpaulo } 246214501Srpaulo 247214501Srpaulo /* Checkcode is SHA1 hash over all EAP-AKA/Identity packets. */ 248214501Srpaulo addr = wpabuf_head(data->id_msgs); 249214501Srpaulo len = wpabuf_len(data->id_msgs); 250214501Srpaulo if (data->eap_method == EAP_TYPE_AKA_PRIME) 251214501Srpaulo sha256_vector(1, &addr, &len, hash); 252214501Srpaulo else 253214501Srpaulo sha1_vector(1, &addr, &len, hash); 254214501Srpaulo 255214501Srpaulo if (os_memcmp(hash, checkcode, hash_len) != 0) { 256214501Srpaulo wpa_printf(MSG_DEBUG, "EAP-AKA: Mismatch in AT_CHECKCODE"); 257214501Srpaulo return -1; 258214501Srpaulo } 259214501Srpaulo 260214501Srpaulo return 0; 261214501Srpaulo} 262214501Srpaulo 263214501Srpaulo 264214501Srpaulostatic struct wpabuf * eap_aka_build_identity(struct eap_sm *sm, 265214501Srpaulo struct eap_aka_data *data, u8 id) 266214501Srpaulo{ 267214501Srpaulo struct eap_sim_msg *msg; 268214501Srpaulo struct wpabuf *buf; 269214501Srpaulo 270214501Srpaulo wpa_printf(MSG_DEBUG, "EAP-AKA: Generating Identity"); 271214501Srpaulo msg = eap_sim_msg_init(EAP_CODE_REQUEST, id, data->eap_method, 272214501Srpaulo EAP_AKA_SUBTYPE_IDENTITY); 273214501Srpaulo if (eap_sim_db_identity_known(sm->eap_sim_db_priv, sm->identity, 274214501Srpaulo sm->identity_len)) { 275214501Srpaulo wpa_printf(MSG_DEBUG, " AT_PERMANENT_ID_REQ"); 276214501Srpaulo eap_sim_msg_add(msg, EAP_SIM_AT_PERMANENT_ID_REQ, 0, NULL, 0); 277214501Srpaulo } else { 278214501Srpaulo /* 279214501Srpaulo * RFC 4187, Chap. 4.1.4 recommends that identity from EAP is 280214501Srpaulo * ignored and the AKA/Identity is used to request the 281214501Srpaulo * identity. 282214501Srpaulo */ 283214501Srpaulo wpa_printf(MSG_DEBUG, " AT_ANY_ID_REQ"); 284214501Srpaulo eap_sim_msg_add(msg, EAP_SIM_AT_ANY_ID_REQ, 0, NULL, 0); 285214501Srpaulo } 286214501Srpaulo buf = eap_sim_msg_finish(msg, NULL, NULL, 0); 287214501Srpaulo if (eap_aka_add_id_msg(data, buf) < 0) { 288214501Srpaulo wpabuf_free(buf); 289214501Srpaulo return NULL; 290214501Srpaulo } 291214501Srpaulo data->pending_id = id; 292214501Srpaulo return buf; 293214501Srpaulo} 294214501Srpaulo 295214501Srpaulo 296214501Srpaulostatic int eap_aka_build_encr(struct eap_sm *sm, struct eap_aka_data *data, 297214501Srpaulo struct eap_sim_msg *msg, u16 counter, 298214501Srpaulo const u8 *nonce_s) 299214501Srpaulo{ 300214501Srpaulo os_free(data->next_pseudonym); 301214501Srpaulo data->next_pseudonym = 302214501Srpaulo eap_sim_db_get_next_pseudonym(sm->eap_sim_db_priv, 1); 303214501Srpaulo os_free(data->next_reauth_id); 304214501Srpaulo if (data->counter <= EAP_AKA_MAX_FAST_REAUTHS) { 305214501Srpaulo data->next_reauth_id = 306214501Srpaulo eap_sim_db_get_next_reauth_id(sm->eap_sim_db_priv, 1); 307214501Srpaulo } else { 308214501Srpaulo wpa_printf(MSG_DEBUG, "EAP-AKA: Max fast re-authentication " 309214501Srpaulo "count exceeded - force full authentication"); 310214501Srpaulo data->next_reauth_id = NULL; 311214501Srpaulo } 312214501Srpaulo 313214501Srpaulo if (data->next_pseudonym == NULL && data->next_reauth_id == NULL && 314214501Srpaulo counter == 0 && nonce_s == NULL) 315214501Srpaulo return 0; 316214501Srpaulo 317214501Srpaulo wpa_printf(MSG_DEBUG, " AT_IV"); 318214501Srpaulo wpa_printf(MSG_DEBUG, " AT_ENCR_DATA"); 319214501Srpaulo eap_sim_msg_add_encr_start(msg, EAP_SIM_AT_IV, EAP_SIM_AT_ENCR_DATA); 320214501Srpaulo 321214501Srpaulo if (counter > 0) { 322214501Srpaulo wpa_printf(MSG_DEBUG, " *AT_COUNTER (%u)", counter); 323214501Srpaulo eap_sim_msg_add(msg, EAP_SIM_AT_COUNTER, counter, NULL, 0); 324214501Srpaulo } 325214501Srpaulo 326214501Srpaulo if (nonce_s) { 327214501Srpaulo wpa_printf(MSG_DEBUG, " *AT_NONCE_S"); 328214501Srpaulo eap_sim_msg_add(msg, EAP_SIM_AT_NONCE_S, 0, nonce_s, 329214501Srpaulo EAP_SIM_NONCE_S_LEN); 330214501Srpaulo } 331214501Srpaulo 332214501Srpaulo if (data->next_pseudonym) { 333214501Srpaulo wpa_printf(MSG_DEBUG, " *AT_NEXT_PSEUDONYM (%s)", 334214501Srpaulo data->next_pseudonym); 335214501Srpaulo eap_sim_msg_add(msg, EAP_SIM_AT_NEXT_PSEUDONYM, 336214501Srpaulo os_strlen(data->next_pseudonym), 337214501Srpaulo (u8 *) data->next_pseudonym, 338214501Srpaulo os_strlen(data->next_pseudonym)); 339214501Srpaulo } 340214501Srpaulo 341214501Srpaulo if (data->next_reauth_id) { 342214501Srpaulo wpa_printf(MSG_DEBUG, " *AT_NEXT_REAUTH_ID (%s)", 343214501Srpaulo data->next_reauth_id); 344214501Srpaulo eap_sim_msg_add(msg, EAP_SIM_AT_NEXT_REAUTH_ID, 345214501Srpaulo os_strlen(data->next_reauth_id), 346214501Srpaulo (u8 *) data->next_reauth_id, 347214501Srpaulo os_strlen(data->next_reauth_id)); 348214501Srpaulo } 349214501Srpaulo 350214501Srpaulo if (eap_sim_msg_add_encr_end(msg, data->k_encr, EAP_SIM_AT_PADDING)) { 351214501Srpaulo wpa_printf(MSG_WARNING, "EAP-AKA: Failed to encrypt " 352214501Srpaulo "AT_ENCR_DATA"); 353214501Srpaulo return -1; 354214501Srpaulo } 355214501Srpaulo 356214501Srpaulo return 0; 357214501Srpaulo} 358214501Srpaulo 359214501Srpaulo 360214501Srpaulostatic struct wpabuf * eap_aka_build_challenge(struct eap_sm *sm, 361214501Srpaulo struct eap_aka_data *data, 362214501Srpaulo u8 id) 363214501Srpaulo{ 364214501Srpaulo struct eap_sim_msg *msg; 365214501Srpaulo 366214501Srpaulo wpa_printf(MSG_DEBUG, "EAP-AKA: Generating Challenge"); 367214501Srpaulo msg = eap_sim_msg_init(EAP_CODE_REQUEST, id, data->eap_method, 368214501Srpaulo EAP_AKA_SUBTYPE_CHALLENGE); 369214501Srpaulo wpa_printf(MSG_DEBUG, " AT_RAND"); 370214501Srpaulo eap_sim_msg_add(msg, EAP_SIM_AT_RAND, 0, data->rand, EAP_AKA_RAND_LEN); 371214501Srpaulo wpa_printf(MSG_DEBUG, " AT_AUTN"); 372214501Srpaulo eap_sim_msg_add(msg, EAP_SIM_AT_AUTN, 0, data->autn, EAP_AKA_AUTN_LEN); 373214501Srpaulo if (data->eap_method == EAP_TYPE_AKA_PRIME) { 374214501Srpaulo if (data->kdf) { 375214501Srpaulo /* Add the selected KDF into the beginning */ 376214501Srpaulo wpa_printf(MSG_DEBUG, " AT_KDF"); 377214501Srpaulo eap_sim_msg_add(msg, EAP_SIM_AT_KDF, data->kdf, 378214501Srpaulo NULL, 0); 379214501Srpaulo } 380214501Srpaulo wpa_printf(MSG_DEBUG, " AT_KDF"); 381214501Srpaulo eap_sim_msg_add(msg, EAP_SIM_AT_KDF, EAP_AKA_PRIME_KDF, 382214501Srpaulo NULL, 0); 383214501Srpaulo wpa_printf(MSG_DEBUG, " AT_KDF_INPUT"); 384214501Srpaulo eap_sim_msg_add(msg, EAP_SIM_AT_KDF_INPUT, 385214501Srpaulo data->network_name_len, 386214501Srpaulo data->network_name, data->network_name_len); 387214501Srpaulo } 388214501Srpaulo 389214501Srpaulo if (eap_aka_build_encr(sm, data, msg, 0, NULL)) { 390214501Srpaulo eap_sim_msg_free(msg); 391214501Srpaulo return NULL; 392214501Srpaulo } 393214501Srpaulo 394214501Srpaulo eap_aka_add_checkcode(data, msg); 395214501Srpaulo 396214501Srpaulo if (sm->eap_sim_aka_result_ind) { 397214501Srpaulo wpa_printf(MSG_DEBUG, " AT_RESULT_IND"); 398214501Srpaulo eap_sim_msg_add(msg, EAP_SIM_AT_RESULT_IND, 0, NULL, 0); 399214501Srpaulo } 400214501Srpaulo 401214501Srpaulo#ifdef EAP_SERVER_AKA_PRIME 402214501Srpaulo if (data->eap_method == EAP_TYPE_AKA) { 403214501Srpaulo u16 flags = 0; 404214501Srpaulo int i; 405214501Srpaulo int aka_prime_preferred = 0; 406214501Srpaulo 407214501Srpaulo i = 0; 408214501Srpaulo while (sm->user && i < EAP_MAX_METHODS && 409214501Srpaulo (sm->user->methods[i].vendor != EAP_VENDOR_IETF || 410214501Srpaulo sm->user->methods[i].method != EAP_TYPE_NONE)) { 411214501Srpaulo if (sm->user->methods[i].vendor == EAP_VENDOR_IETF) { 412214501Srpaulo if (sm->user->methods[i].method == 413214501Srpaulo EAP_TYPE_AKA) 414214501Srpaulo break; 415214501Srpaulo if (sm->user->methods[i].method == 416214501Srpaulo EAP_TYPE_AKA_PRIME) { 417214501Srpaulo aka_prime_preferred = 1; 418214501Srpaulo break; 419214501Srpaulo } 420214501Srpaulo } 421214501Srpaulo i++; 422214501Srpaulo } 423214501Srpaulo 424214501Srpaulo if (aka_prime_preferred) 425214501Srpaulo flags |= EAP_AKA_BIDDING_FLAG_D; 426214501Srpaulo eap_sim_msg_add(msg, EAP_SIM_AT_BIDDING, flags, NULL, 0); 427214501Srpaulo } 428214501Srpaulo#endif /* EAP_SERVER_AKA_PRIME */ 429214501Srpaulo 430214501Srpaulo wpa_printf(MSG_DEBUG, " AT_MAC"); 431214501Srpaulo eap_sim_msg_add_mac(msg, EAP_SIM_AT_MAC); 432214501Srpaulo return eap_sim_msg_finish(msg, data->k_aut, NULL, 0); 433214501Srpaulo} 434214501Srpaulo 435214501Srpaulo 436214501Srpaulostatic struct wpabuf * eap_aka_build_reauth(struct eap_sm *sm, 437214501Srpaulo struct eap_aka_data *data, u8 id) 438214501Srpaulo{ 439214501Srpaulo struct eap_sim_msg *msg; 440214501Srpaulo 441214501Srpaulo wpa_printf(MSG_DEBUG, "EAP-AKA: Generating Re-authentication"); 442214501Srpaulo 443214501Srpaulo if (os_get_random(data->nonce_s, EAP_SIM_NONCE_S_LEN)) 444214501Srpaulo return NULL; 445214501Srpaulo wpa_hexdump_key(MSG_MSGDUMP, "EAP-AKA: NONCE_S", 446214501Srpaulo data->nonce_s, EAP_SIM_NONCE_S_LEN); 447214501Srpaulo 448214501Srpaulo if (data->eap_method == EAP_TYPE_AKA_PRIME) { 449214501Srpaulo eap_aka_prime_derive_keys_reauth(data->k_re, data->counter, 450214501Srpaulo sm->identity, 451214501Srpaulo sm->identity_len, 452214501Srpaulo data->nonce_s, 453214501Srpaulo data->msk, data->emsk); 454214501Srpaulo } else { 455214501Srpaulo eap_sim_derive_keys(data->mk, data->k_encr, data->k_aut, 456214501Srpaulo data->msk, data->emsk); 457214501Srpaulo eap_sim_derive_keys_reauth(data->counter, sm->identity, 458214501Srpaulo sm->identity_len, data->nonce_s, 459214501Srpaulo data->mk, data->msk, data->emsk); 460214501Srpaulo } 461214501Srpaulo 462214501Srpaulo msg = eap_sim_msg_init(EAP_CODE_REQUEST, id, data->eap_method, 463214501Srpaulo EAP_AKA_SUBTYPE_REAUTHENTICATION); 464214501Srpaulo 465214501Srpaulo if (eap_aka_build_encr(sm, data, msg, data->counter, data->nonce_s)) { 466214501Srpaulo eap_sim_msg_free(msg); 467214501Srpaulo return NULL; 468214501Srpaulo } 469214501Srpaulo 470214501Srpaulo eap_aka_add_checkcode(data, msg); 471214501Srpaulo 472214501Srpaulo if (sm->eap_sim_aka_result_ind) { 473214501Srpaulo wpa_printf(MSG_DEBUG, " AT_RESULT_IND"); 474214501Srpaulo eap_sim_msg_add(msg, EAP_SIM_AT_RESULT_IND, 0, NULL, 0); 475214501Srpaulo } 476214501Srpaulo 477214501Srpaulo wpa_printf(MSG_DEBUG, " AT_MAC"); 478214501Srpaulo eap_sim_msg_add_mac(msg, EAP_SIM_AT_MAC); 479214501Srpaulo return eap_sim_msg_finish(msg, data->k_aut, NULL, 0); 480214501Srpaulo} 481214501Srpaulo 482214501Srpaulo 483214501Srpaulostatic struct wpabuf * eap_aka_build_notification(struct eap_sm *sm, 484214501Srpaulo struct eap_aka_data *data, 485214501Srpaulo u8 id) 486214501Srpaulo{ 487214501Srpaulo struct eap_sim_msg *msg; 488214501Srpaulo 489214501Srpaulo wpa_printf(MSG_DEBUG, "EAP-AKA: Generating Notification"); 490214501Srpaulo msg = eap_sim_msg_init(EAP_CODE_REQUEST, id, data->eap_method, 491214501Srpaulo EAP_AKA_SUBTYPE_NOTIFICATION); 492214501Srpaulo wpa_printf(MSG_DEBUG, " AT_NOTIFICATION (%d)", data->notification); 493214501Srpaulo eap_sim_msg_add(msg, EAP_SIM_AT_NOTIFICATION, data->notification, 494214501Srpaulo NULL, 0); 495214501Srpaulo if (data->use_result_ind) { 496214501Srpaulo if (data->reauth) { 497214501Srpaulo wpa_printf(MSG_DEBUG, " AT_IV"); 498214501Srpaulo wpa_printf(MSG_DEBUG, " AT_ENCR_DATA"); 499214501Srpaulo eap_sim_msg_add_encr_start(msg, EAP_SIM_AT_IV, 500214501Srpaulo EAP_SIM_AT_ENCR_DATA); 501214501Srpaulo wpa_printf(MSG_DEBUG, " *AT_COUNTER (%u)", 502214501Srpaulo data->counter); 503214501Srpaulo eap_sim_msg_add(msg, EAP_SIM_AT_COUNTER, data->counter, 504214501Srpaulo NULL, 0); 505214501Srpaulo 506214501Srpaulo if (eap_sim_msg_add_encr_end(msg, data->k_encr, 507214501Srpaulo EAP_SIM_AT_PADDING)) { 508214501Srpaulo wpa_printf(MSG_WARNING, "EAP-AKA: Failed to " 509214501Srpaulo "encrypt AT_ENCR_DATA"); 510214501Srpaulo eap_sim_msg_free(msg); 511214501Srpaulo return NULL; 512214501Srpaulo } 513214501Srpaulo } 514214501Srpaulo 515214501Srpaulo wpa_printf(MSG_DEBUG, " AT_MAC"); 516214501Srpaulo eap_sim_msg_add_mac(msg, EAP_SIM_AT_MAC); 517214501Srpaulo } 518214501Srpaulo return eap_sim_msg_finish(msg, data->k_aut, NULL, 0); 519214501Srpaulo} 520214501Srpaulo 521214501Srpaulo 522214501Srpaulostatic struct wpabuf * eap_aka_buildReq(struct eap_sm *sm, void *priv, u8 id) 523214501Srpaulo{ 524214501Srpaulo struct eap_aka_data *data = priv; 525214501Srpaulo 526214501Srpaulo data->auts_reported = 0; 527214501Srpaulo switch (data->state) { 528214501Srpaulo case IDENTITY: 529214501Srpaulo return eap_aka_build_identity(sm, data, id); 530214501Srpaulo case CHALLENGE: 531214501Srpaulo return eap_aka_build_challenge(sm, data, id); 532214501Srpaulo case REAUTH: 533214501Srpaulo return eap_aka_build_reauth(sm, data, id); 534214501Srpaulo case NOTIFICATION: 535214501Srpaulo return eap_aka_build_notification(sm, data, id); 536214501Srpaulo default: 537214501Srpaulo wpa_printf(MSG_DEBUG, "EAP-AKA: Unknown state %d in " 538214501Srpaulo "buildReq", data->state); 539214501Srpaulo break; 540214501Srpaulo } 541214501Srpaulo return NULL; 542214501Srpaulo} 543214501Srpaulo 544214501Srpaulo 545214501Srpaulostatic Boolean eap_aka_check(struct eap_sm *sm, void *priv, 546214501Srpaulo struct wpabuf *respData) 547214501Srpaulo{ 548214501Srpaulo struct eap_aka_data *data = priv; 549214501Srpaulo const u8 *pos; 550214501Srpaulo size_t len; 551214501Srpaulo 552214501Srpaulo pos = eap_hdr_validate(EAP_VENDOR_IETF, data->eap_method, respData, 553214501Srpaulo &len); 554214501Srpaulo if (pos == NULL || len < 3) { 555214501Srpaulo wpa_printf(MSG_INFO, "EAP-AKA: Invalid frame"); 556214501Srpaulo return TRUE; 557214501Srpaulo } 558214501Srpaulo 559214501Srpaulo return FALSE; 560214501Srpaulo} 561214501Srpaulo 562214501Srpaulo 563214501Srpaulostatic Boolean eap_aka_subtype_ok(struct eap_aka_data *data, u8 subtype) 564214501Srpaulo{ 565214501Srpaulo if (subtype == EAP_AKA_SUBTYPE_CLIENT_ERROR || 566214501Srpaulo subtype == EAP_AKA_SUBTYPE_AUTHENTICATION_REJECT) 567214501Srpaulo return FALSE; 568214501Srpaulo 569214501Srpaulo switch (data->state) { 570214501Srpaulo case IDENTITY: 571214501Srpaulo if (subtype != EAP_AKA_SUBTYPE_IDENTITY) { 572214501Srpaulo wpa_printf(MSG_INFO, "EAP-AKA: Unexpected response " 573214501Srpaulo "subtype %d", subtype); 574214501Srpaulo return TRUE; 575214501Srpaulo } 576214501Srpaulo break; 577214501Srpaulo case CHALLENGE: 578214501Srpaulo if (subtype != EAP_AKA_SUBTYPE_CHALLENGE && 579214501Srpaulo subtype != EAP_AKA_SUBTYPE_SYNCHRONIZATION_FAILURE) { 580214501Srpaulo wpa_printf(MSG_INFO, "EAP-AKA: Unexpected response " 581214501Srpaulo "subtype %d", subtype); 582214501Srpaulo return TRUE; 583214501Srpaulo } 584214501Srpaulo break; 585214501Srpaulo case REAUTH: 586214501Srpaulo if (subtype != EAP_AKA_SUBTYPE_REAUTHENTICATION) { 587214501Srpaulo wpa_printf(MSG_INFO, "EAP-AKA: Unexpected response " 588214501Srpaulo "subtype %d", subtype); 589214501Srpaulo return TRUE; 590214501Srpaulo } 591214501Srpaulo break; 592214501Srpaulo case NOTIFICATION: 593214501Srpaulo if (subtype != EAP_AKA_SUBTYPE_NOTIFICATION) { 594214501Srpaulo wpa_printf(MSG_INFO, "EAP-AKA: Unexpected response " 595214501Srpaulo "subtype %d", subtype); 596214501Srpaulo return TRUE; 597214501Srpaulo } 598214501Srpaulo break; 599214501Srpaulo default: 600214501Srpaulo wpa_printf(MSG_INFO, "EAP-AKA: Unexpected state (%d) for " 601214501Srpaulo "processing a response", data->state); 602214501Srpaulo return TRUE; 603214501Srpaulo } 604214501Srpaulo 605214501Srpaulo return FALSE; 606214501Srpaulo} 607214501Srpaulo 608214501Srpaulo 609214501Srpaulostatic void eap_aka_determine_identity(struct eap_sm *sm, 610214501Srpaulo struct eap_aka_data *data, 611214501Srpaulo int before_identity, int after_reauth) 612214501Srpaulo{ 613214501Srpaulo const u8 *identity; 614214501Srpaulo size_t identity_len; 615214501Srpaulo int res; 616214501Srpaulo 617214501Srpaulo identity = NULL; 618214501Srpaulo identity_len = 0; 619214501Srpaulo 620214501Srpaulo if (after_reauth && data->reauth) { 621214501Srpaulo identity = data->reauth->identity; 622214501Srpaulo identity_len = data->reauth->identity_len; 623214501Srpaulo } else if (sm->identity && sm->identity_len > 0 && 624214501Srpaulo sm->identity[0] == EAP_AKA_PERMANENT_PREFIX) { 625214501Srpaulo identity = sm->identity; 626214501Srpaulo identity_len = sm->identity_len; 627214501Srpaulo } else { 628214501Srpaulo identity = eap_sim_db_get_permanent(sm->eap_sim_db_priv, 629214501Srpaulo sm->identity, 630214501Srpaulo sm->identity_len, 631214501Srpaulo &identity_len); 632214501Srpaulo if (identity == NULL) { 633214501Srpaulo data->reauth = eap_sim_db_get_reauth_entry( 634214501Srpaulo sm->eap_sim_db_priv, sm->identity, 635214501Srpaulo sm->identity_len); 636214501Srpaulo if (data->reauth && 637214501Srpaulo data->reauth->aka_prime != 638214501Srpaulo (data->eap_method == EAP_TYPE_AKA_PRIME)) { 639214501Srpaulo wpa_printf(MSG_DEBUG, "EAP-AKA: Reauth data " 640214501Srpaulo "was for different AKA version"); 641214501Srpaulo data->reauth = NULL; 642214501Srpaulo } 643214501Srpaulo if (data->reauth) { 644214501Srpaulo wpa_printf(MSG_DEBUG, "EAP-AKA: Using fast " 645214501Srpaulo "re-authentication"); 646214501Srpaulo identity = data->reauth->identity; 647214501Srpaulo identity_len = data->reauth->identity_len; 648214501Srpaulo data->counter = data->reauth->counter; 649214501Srpaulo if (data->eap_method == EAP_TYPE_AKA_PRIME) { 650214501Srpaulo os_memcpy(data->k_encr, 651214501Srpaulo data->reauth->k_encr, 652214501Srpaulo EAP_SIM_K_ENCR_LEN); 653214501Srpaulo os_memcpy(data->k_aut, 654214501Srpaulo data->reauth->k_aut, 655214501Srpaulo EAP_AKA_PRIME_K_AUT_LEN); 656214501Srpaulo os_memcpy(data->k_re, 657214501Srpaulo data->reauth->k_re, 658214501Srpaulo EAP_AKA_PRIME_K_RE_LEN); 659214501Srpaulo } else { 660214501Srpaulo os_memcpy(data->mk, data->reauth->mk, 661214501Srpaulo EAP_SIM_MK_LEN); 662214501Srpaulo } 663214501Srpaulo } 664214501Srpaulo } 665214501Srpaulo } 666214501Srpaulo 667214501Srpaulo if (identity == NULL || 668214501Srpaulo eap_sim_db_identity_known(sm->eap_sim_db_priv, sm->identity, 669214501Srpaulo sm->identity_len) < 0) { 670214501Srpaulo if (before_identity) { 671214501Srpaulo wpa_printf(MSG_DEBUG, "EAP-AKA: Permanent user name " 672214501Srpaulo "not known - send AKA-Identity request"); 673214501Srpaulo eap_aka_state(data, IDENTITY); 674214501Srpaulo return; 675214501Srpaulo } else { 676214501Srpaulo wpa_printf(MSG_DEBUG, "EAP-AKA: Unknown whether the " 677214501Srpaulo "permanent user name is known; try to use " 678214501Srpaulo "it"); 679214501Srpaulo /* eap_sim_db_get_aka_auth() will report failure, if 680214501Srpaulo * this identity is not known. */ 681214501Srpaulo } 682214501Srpaulo } 683214501Srpaulo 684214501Srpaulo wpa_hexdump_ascii(MSG_DEBUG, "EAP-AKA: Identity", 685214501Srpaulo identity, identity_len); 686214501Srpaulo 687214501Srpaulo if (!after_reauth && data->reauth) { 688214501Srpaulo eap_aka_state(data, REAUTH); 689214501Srpaulo return; 690214501Srpaulo } 691214501Srpaulo 692214501Srpaulo res = eap_sim_db_get_aka_auth(sm->eap_sim_db_priv, identity, 693214501Srpaulo identity_len, data->rand, data->autn, 694214501Srpaulo data->ik, data->ck, data->res, 695214501Srpaulo &data->res_len, sm); 696214501Srpaulo if (res == EAP_SIM_DB_PENDING) { 697214501Srpaulo wpa_printf(MSG_DEBUG, "EAP-AKA: AKA authentication data " 698214501Srpaulo "not yet available - pending request"); 699214501Srpaulo sm->method_pending = METHOD_PENDING_WAIT; 700214501Srpaulo return; 701214501Srpaulo } 702214501Srpaulo 703214501Srpaulo#ifdef EAP_SERVER_AKA_PRIME 704214501Srpaulo if (data->eap_method == EAP_TYPE_AKA_PRIME) { 705214501Srpaulo /* Note: AUTN = (SQN ^ AK) || AMF || MAC which gives us the 706214501Srpaulo * needed 6-octet SQN ^AK for CK',IK' derivation */ 707214501Srpaulo eap_aka_prime_derive_ck_ik_prime(data->ck, data->ik, 708214501Srpaulo data->autn, 709214501Srpaulo data->network_name, 710214501Srpaulo data->network_name_len); 711214501Srpaulo } 712214501Srpaulo#endif /* EAP_SERVER_AKA_PRIME */ 713214501Srpaulo 714214501Srpaulo data->reauth = NULL; 715214501Srpaulo data->counter = 0; /* reset re-auth counter since this is full auth */ 716214501Srpaulo 717214501Srpaulo if (res != 0) { 718214501Srpaulo wpa_printf(MSG_INFO, "EAP-AKA: Failed to get AKA " 719214501Srpaulo "authentication data for the peer"); 720214501Srpaulo data->notification = EAP_SIM_GENERAL_FAILURE_BEFORE_AUTH; 721214501Srpaulo eap_aka_state(data, NOTIFICATION); 722214501Srpaulo return; 723214501Srpaulo } 724214501Srpaulo if (sm->method_pending == METHOD_PENDING_WAIT) { 725214501Srpaulo wpa_printf(MSG_DEBUG, "EAP-AKA: AKA authentication data " 726214501Srpaulo "available - abort pending wait"); 727214501Srpaulo sm->method_pending = METHOD_PENDING_NONE; 728214501Srpaulo } 729214501Srpaulo 730214501Srpaulo identity_len = sm->identity_len; 731214501Srpaulo while (identity_len > 0 && sm->identity[identity_len - 1] == '\0') { 732214501Srpaulo wpa_printf(MSG_DEBUG, "EAP-AKA: Workaround - drop last null " 733214501Srpaulo "character from identity"); 734214501Srpaulo identity_len--; 735214501Srpaulo } 736214501Srpaulo wpa_hexdump_ascii(MSG_DEBUG, "EAP-AKA: Identity for MK derivation", 737214501Srpaulo sm->identity, identity_len); 738214501Srpaulo 739214501Srpaulo if (data->eap_method == EAP_TYPE_AKA_PRIME) { 740214501Srpaulo eap_aka_prime_derive_keys(identity, identity_len, data->ik, 741214501Srpaulo data->ck, data->k_encr, data->k_aut, 742214501Srpaulo data->k_re, data->msk, data->emsk); 743214501Srpaulo } else { 744214501Srpaulo eap_aka_derive_mk(sm->identity, identity_len, data->ik, 745214501Srpaulo data->ck, data->mk); 746214501Srpaulo eap_sim_derive_keys(data->mk, data->k_encr, data->k_aut, 747214501Srpaulo data->msk, data->emsk); 748214501Srpaulo } 749214501Srpaulo 750214501Srpaulo eap_aka_state(data, CHALLENGE); 751214501Srpaulo} 752214501Srpaulo 753214501Srpaulo 754214501Srpaulostatic void eap_aka_process_identity(struct eap_sm *sm, 755214501Srpaulo struct eap_aka_data *data, 756214501Srpaulo struct wpabuf *respData, 757214501Srpaulo struct eap_sim_attrs *attr) 758214501Srpaulo{ 759214501Srpaulo wpa_printf(MSG_DEBUG, "EAP-AKA: Processing Identity"); 760214501Srpaulo 761214501Srpaulo if (attr->mac || attr->iv || attr->encr_data) { 762214501Srpaulo wpa_printf(MSG_WARNING, "EAP-AKA: Unexpected attribute " 763214501Srpaulo "received in EAP-Response/AKA-Identity"); 764214501Srpaulo data->notification = EAP_SIM_GENERAL_FAILURE_BEFORE_AUTH; 765214501Srpaulo eap_aka_state(data, NOTIFICATION); 766214501Srpaulo return; 767214501Srpaulo } 768214501Srpaulo 769214501Srpaulo if (attr->identity) { 770214501Srpaulo os_free(sm->identity); 771214501Srpaulo sm->identity = os_malloc(attr->identity_len); 772214501Srpaulo if (sm->identity) { 773214501Srpaulo os_memcpy(sm->identity, attr->identity, 774214501Srpaulo attr->identity_len); 775214501Srpaulo sm->identity_len = attr->identity_len; 776214501Srpaulo } 777214501Srpaulo } 778214501Srpaulo 779214501Srpaulo eap_aka_determine_identity(sm, data, 0, 0); 780214501Srpaulo if (eap_get_id(respData) == data->pending_id) { 781214501Srpaulo data->pending_id = -1; 782214501Srpaulo eap_aka_add_id_msg(data, respData); 783214501Srpaulo } 784214501Srpaulo} 785214501Srpaulo 786214501Srpaulo 787214501Srpaulostatic int eap_aka_verify_mac(struct eap_aka_data *data, 788214501Srpaulo const struct wpabuf *req, 789214501Srpaulo const u8 *mac, const u8 *extra, 790214501Srpaulo size_t extra_len) 791214501Srpaulo{ 792214501Srpaulo if (data->eap_method == EAP_TYPE_AKA_PRIME) 793214501Srpaulo return eap_sim_verify_mac_sha256(data->k_aut, req, mac, extra, 794214501Srpaulo extra_len); 795214501Srpaulo return eap_sim_verify_mac(data->k_aut, req, mac, extra, extra_len); 796214501Srpaulo} 797214501Srpaulo 798214501Srpaulo 799214501Srpaulostatic void eap_aka_process_challenge(struct eap_sm *sm, 800214501Srpaulo struct eap_aka_data *data, 801214501Srpaulo struct wpabuf *respData, 802214501Srpaulo struct eap_sim_attrs *attr) 803214501Srpaulo{ 804214501Srpaulo const u8 *identity; 805214501Srpaulo size_t identity_len; 806214501Srpaulo 807214501Srpaulo wpa_printf(MSG_DEBUG, "EAP-AKA: Processing Challenge"); 808214501Srpaulo 809214501Srpaulo#ifdef EAP_SERVER_AKA_PRIME 810214501Srpaulo#if 0 811214501Srpaulo /* KDF negotiation; to be enabled only after more than one KDF is 812214501Srpaulo * supported */ 813214501Srpaulo if (data->eap_method == EAP_TYPE_AKA_PRIME && 814214501Srpaulo attr->kdf_count == 1 && attr->mac == NULL) { 815214501Srpaulo if (attr->kdf[0] != EAP_AKA_PRIME_KDF) { 816214501Srpaulo wpa_printf(MSG_WARNING, "EAP-AKA': Peer selected " 817214501Srpaulo "unknown KDF"); 818214501Srpaulo data->notification = 819214501Srpaulo EAP_SIM_GENERAL_FAILURE_BEFORE_AUTH; 820214501Srpaulo eap_aka_state(data, NOTIFICATION); 821214501Srpaulo return; 822214501Srpaulo } 823214501Srpaulo 824214501Srpaulo data->kdf = attr->kdf[0]; 825214501Srpaulo 826214501Srpaulo /* Allow negotiation to continue with the selected KDF by 827214501Srpaulo * sending another Challenge message */ 828214501Srpaulo wpa_printf(MSG_DEBUG, "EAP-AKA': KDF %d selected", data->kdf); 829214501Srpaulo return; 830214501Srpaulo } 831214501Srpaulo#endif 832214501Srpaulo#endif /* EAP_SERVER_AKA_PRIME */ 833214501Srpaulo 834214501Srpaulo if (attr->checkcode && 835214501Srpaulo eap_aka_verify_checkcode(data, attr->checkcode, 836214501Srpaulo attr->checkcode_len)) { 837214501Srpaulo wpa_printf(MSG_WARNING, "EAP-AKA: Invalid AT_CHECKCODE in the " 838214501Srpaulo "message"); 839214501Srpaulo data->notification = EAP_SIM_GENERAL_FAILURE_BEFORE_AUTH; 840214501Srpaulo eap_aka_state(data, NOTIFICATION); 841214501Srpaulo return; 842214501Srpaulo } 843214501Srpaulo if (attr->mac == NULL || 844214501Srpaulo eap_aka_verify_mac(data, respData, attr->mac, NULL, 0)) { 845214501Srpaulo wpa_printf(MSG_WARNING, "EAP-AKA: Challenge message " 846214501Srpaulo "did not include valid AT_MAC"); 847214501Srpaulo data->notification = EAP_SIM_GENERAL_FAILURE_BEFORE_AUTH; 848214501Srpaulo eap_aka_state(data, NOTIFICATION); 849214501Srpaulo return; 850214501Srpaulo } 851214501Srpaulo 852214501Srpaulo /* 853214501Srpaulo * AT_RES is padded, so verify that there is enough room for RES and 854214501Srpaulo * that the RES length in bits matches with the expected RES. 855214501Srpaulo */ 856214501Srpaulo if (attr->res == NULL || attr->res_len < data->res_len || 857214501Srpaulo attr->res_len_bits != data->res_len * 8 || 858214501Srpaulo os_memcmp(attr->res, data->res, data->res_len) != 0) { 859214501Srpaulo wpa_printf(MSG_WARNING, "EAP-AKA: Challenge message did not " 860214501Srpaulo "include valid AT_RES (attr len=%lu, res len=%lu " 861214501Srpaulo "bits, expected %lu bits)", 862214501Srpaulo (unsigned long) attr->res_len, 863214501Srpaulo (unsigned long) attr->res_len_bits, 864214501Srpaulo (unsigned long) data->res_len * 8); 865214501Srpaulo data->notification = EAP_SIM_GENERAL_FAILURE_BEFORE_AUTH; 866214501Srpaulo eap_aka_state(data, NOTIFICATION); 867214501Srpaulo return; 868214501Srpaulo } 869214501Srpaulo 870214501Srpaulo wpa_printf(MSG_DEBUG, "EAP-AKA: Challenge response includes the " 871214501Srpaulo "correct AT_MAC"); 872214501Srpaulo if (sm->eap_sim_aka_result_ind && attr->result_ind) { 873214501Srpaulo data->use_result_ind = 1; 874214501Srpaulo data->notification = EAP_SIM_SUCCESS; 875214501Srpaulo eap_aka_state(data, NOTIFICATION); 876214501Srpaulo } else 877214501Srpaulo eap_aka_state(data, SUCCESS); 878214501Srpaulo 879214501Srpaulo identity = eap_sim_db_get_permanent(sm->eap_sim_db_priv, sm->identity, 880214501Srpaulo sm->identity_len, &identity_len); 881214501Srpaulo if (identity == NULL) { 882214501Srpaulo identity = sm->identity; 883214501Srpaulo identity_len = sm->identity_len; 884214501Srpaulo } 885214501Srpaulo 886214501Srpaulo if (data->next_pseudonym) { 887214501Srpaulo eap_sim_db_add_pseudonym(sm->eap_sim_db_priv, identity, 888214501Srpaulo identity_len, 889214501Srpaulo data->next_pseudonym); 890214501Srpaulo data->next_pseudonym = NULL; 891214501Srpaulo } 892214501Srpaulo if (data->next_reauth_id) { 893214501Srpaulo if (data->eap_method == EAP_TYPE_AKA_PRIME) { 894214501Srpaulo#ifdef EAP_SERVER_AKA_PRIME 895214501Srpaulo eap_sim_db_add_reauth_prime(sm->eap_sim_db_priv, 896214501Srpaulo identity, 897214501Srpaulo identity_len, 898214501Srpaulo data->next_reauth_id, 899214501Srpaulo data->counter + 1, 900214501Srpaulo data->k_encr, data->k_aut, 901214501Srpaulo data->k_re); 902214501Srpaulo#endif /* EAP_SERVER_AKA_PRIME */ 903214501Srpaulo } else { 904214501Srpaulo eap_sim_db_add_reauth(sm->eap_sim_db_priv, identity, 905214501Srpaulo identity_len, 906214501Srpaulo data->next_reauth_id, 907214501Srpaulo data->counter + 1, 908214501Srpaulo data->mk); 909214501Srpaulo } 910214501Srpaulo data->next_reauth_id = NULL; 911214501Srpaulo } 912214501Srpaulo} 913214501Srpaulo 914214501Srpaulo 915214501Srpaulostatic void eap_aka_process_sync_failure(struct eap_sm *sm, 916214501Srpaulo struct eap_aka_data *data, 917214501Srpaulo struct wpabuf *respData, 918214501Srpaulo struct eap_sim_attrs *attr) 919214501Srpaulo{ 920214501Srpaulo wpa_printf(MSG_DEBUG, "EAP-AKA: Processing Synchronization-Failure"); 921214501Srpaulo 922214501Srpaulo if (attr->auts == NULL) { 923214501Srpaulo wpa_printf(MSG_WARNING, "EAP-AKA: Synchronization-Failure " 924214501Srpaulo "message did not include valid AT_AUTS"); 925214501Srpaulo data->notification = EAP_SIM_GENERAL_FAILURE_BEFORE_AUTH; 926214501Srpaulo eap_aka_state(data, NOTIFICATION); 927214501Srpaulo return; 928214501Srpaulo } 929214501Srpaulo 930214501Srpaulo /* Avoid re-reporting AUTS when processing pending EAP packet by 931214501Srpaulo * maintaining a local flag stating whether this AUTS has already been 932214501Srpaulo * reported. */ 933214501Srpaulo if (!data->auts_reported && 934214501Srpaulo eap_sim_db_resynchronize(sm->eap_sim_db_priv, sm->identity, 935214501Srpaulo sm->identity_len, attr->auts, 936214501Srpaulo data->rand)) { 937214501Srpaulo wpa_printf(MSG_WARNING, "EAP-AKA: Resynchronization failed"); 938214501Srpaulo data->notification = EAP_SIM_GENERAL_FAILURE_BEFORE_AUTH; 939214501Srpaulo eap_aka_state(data, NOTIFICATION); 940214501Srpaulo return; 941214501Srpaulo } 942214501Srpaulo data->auts_reported = 1; 943214501Srpaulo 944214501Srpaulo /* Try again after resynchronization */ 945214501Srpaulo eap_aka_determine_identity(sm, data, 0, 0); 946214501Srpaulo} 947214501Srpaulo 948214501Srpaulo 949214501Srpaulostatic void eap_aka_process_reauth(struct eap_sm *sm, 950214501Srpaulo struct eap_aka_data *data, 951214501Srpaulo struct wpabuf *respData, 952214501Srpaulo struct eap_sim_attrs *attr) 953214501Srpaulo{ 954214501Srpaulo struct eap_sim_attrs eattr; 955214501Srpaulo u8 *decrypted = NULL; 956214501Srpaulo const u8 *identity, *id2; 957214501Srpaulo size_t identity_len, id2_len; 958214501Srpaulo 959214501Srpaulo wpa_printf(MSG_DEBUG, "EAP-AKA: Processing Reauthentication"); 960214501Srpaulo 961214501Srpaulo if (attr->mac == NULL || 962214501Srpaulo eap_aka_verify_mac(data, respData, attr->mac, data->nonce_s, 963214501Srpaulo EAP_SIM_NONCE_S_LEN)) { 964214501Srpaulo wpa_printf(MSG_WARNING, "EAP-AKA: Re-authentication message " 965214501Srpaulo "did not include valid AT_MAC"); 966214501Srpaulo goto fail; 967214501Srpaulo } 968214501Srpaulo 969214501Srpaulo if (attr->encr_data == NULL || attr->iv == NULL) { 970214501Srpaulo wpa_printf(MSG_WARNING, "EAP-AKA: Reauthentication " 971214501Srpaulo "message did not include encrypted data"); 972214501Srpaulo goto fail; 973214501Srpaulo } 974214501Srpaulo 975214501Srpaulo decrypted = eap_sim_parse_encr(data->k_encr, attr->encr_data, 976214501Srpaulo attr->encr_data_len, attr->iv, &eattr, 977214501Srpaulo 0); 978214501Srpaulo if (decrypted == NULL) { 979214501Srpaulo wpa_printf(MSG_WARNING, "EAP-AKA: Failed to parse encrypted " 980214501Srpaulo "data from reauthentication message"); 981214501Srpaulo goto fail; 982214501Srpaulo } 983214501Srpaulo 984214501Srpaulo if (eattr.counter != data->counter) { 985214501Srpaulo wpa_printf(MSG_WARNING, "EAP-AKA: Re-authentication message " 986214501Srpaulo "used incorrect counter %u, expected %u", 987214501Srpaulo eattr.counter, data->counter); 988214501Srpaulo goto fail; 989214501Srpaulo } 990214501Srpaulo os_free(decrypted); 991214501Srpaulo decrypted = NULL; 992214501Srpaulo 993214501Srpaulo wpa_printf(MSG_DEBUG, "EAP-AKA: Re-authentication response includes " 994214501Srpaulo "the correct AT_MAC"); 995214501Srpaulo 996214501Srpaulo if (eattr.counter_too_small) { 997214501Srpaulo wpa_printf(MSG_DEBUG, "EAP-AKA: Re-authentication response " 998214501Srpaulo "included AT_COUNTER_TOO_SMALL - starting full " 999214501Srpaulo "authentication"); 1000214501Srpaulo eap_aka_determine_identity(sm, data, 0, 1); 1001214501Srpaulo return; 1002214501Srpaulo } 1003214501Srpaulo 1004214501Srpaulo if (sm->eap_sim_aka_result_ind && attr->result_ind) { 1005214501Srpaulo data->use_result_ind = 1; 1006214501Srpaulo data->notification = EAP_SIM_SUCCESS; 1007214501Srpaulo eap_aka_state(data, NOTIFICATION); 1008214501Srpaulo } else 1009214501Srpaulo eap_aka_state(data, SUCCESS); 1010214501Srpaulo 1011214501Srpaulo if (data->reauth) { 1012214501Srpaulo identity = data->reauth->identity; 1013214501Srpaulo identity_len = data->reauth->identity_len; 1014214501Srpaulo } else { 1015214501Srpaulo identity = sm->identity; 1016214501Srpaulo identity_len = sm->identity_len; 1017214501Srpaulo } 1018214501Srpaulo 1019214501Srpaulo id2 = eap_sim_db_get_permanent(sm->eap_sim_db_priv, identity, 1020214501Srpaulo identity_len, &id2_len); 1021214501Srpaulo if (id2) { 1022214501Srpaulo identity = id2; 1023214501Srpaulo identity_len = id2_len; 1024214501Srpaulo } 1025214501Srpaulo 1026214501Srpaulo if (data->next_pseudonym) { 1027214501Srpaulo eap_sim_db_add_pseudonym(sm->eap_sim_db_priv, identity, 1028214501Srpaulo identity_len, data->next_pseudonym); 1029214501Srpaulo data->next_pseudonym = NULL; 1030214501Srpaulo } 1031214501Srpaulo if (data->next_reauth_id) { 1032214501Srpaulo if (data->eap_method == EAP_TYPE_AKA_PRIME) { 1033214501Srpaulo#ifdef EAP_SERVER_AKA_PRIME 1034214501Srpaulo eap_sim_db_add_reauth_prime(sm->eap_sim_db_priv, 1035214501Srpaulo identity, 1036214501Srpaulo identity_len, 1037214501Srpaulo data->next_reauth_id, 1038214501Srpaulo data->counter + 1, 1039214501Srpaulo data->k_encr, data->k_aut, 1040214501Srpaulo data->k_re); 1041214501Srpaulo#endif /* EAP_SERVER_AKA_PRIME */ 1042214501Srpaulo } else { 1043214501Srpaulo eap_sim_db_add_reauth(sm->eap_sim_db_priv, identity, 1044214501Srpaulo identity_len, 1045214501Srpaulo data->next_reauth_id, 1046214501Srpaulo data->counter + 1, 1047214501Srpaulo data->mk); 1048214501Srpaulo } 1049214501Srpaulo data->next_reauth_id = NULL; 1050214501Srpaulo } else { 1051214501Srpaulo eap_sim_db_remove_reauth(sm->eap_sim_db_priv, data->reauth); 1052214501Srpaulo data->reauth = NULL; 1053214501Srpaulo } 1054214501Srpaulo 1055214501Srpaulo return; 1056214501Srpaulo 1057214501Srpaulofail: 1058214501Srpaulo data->notification = EAP_SIM_GENERAL_FAILURE_BEFORE_AUTH; 1059214501Srpaulo eap_aka_state(data, NOTIFICATION); 1060214501Srpaulo eap_sim_db_remove_reauth(sm->eap_sim_db_priv, data->reauth); 1061214501Srpaulo data->reauth = NULL; 1062214501Srpaulo os_free(decrypted); 1063214501Srpaulo} 1064214501Srpaulo 1065214501Srpaulo 1066214501Srpaulostatic void eap_aka_process_client_error(struct eap_sm *sm, 1067214501Srpaulo struct eap_aka_data *data, 1068214501Srpaulo struct wpabuf *respData, 1069214501Srpaulo struct eap_sim_attrs *attr) 1070214501Srpaulo{ 1071214501Srpaulo wpa_printf(MSG_DEBUG, "EAP-AKA: Client reported error %d", 1072214501Srpaulo attr->client_error_code); 1073214501Srpaulo if (data->notification == EAP_SIM_SUCCESS && data->use_result_ind) 1074214501Srpaulo eap_aka_state(data, SUCCESS); 1075214501Srpaulo else 1076214501Srpaulo eap_aka_state(data, FAILURE); 1077214501Srpaulo} 1078214501Srpaulo 1079214501Srpaulo 1080214501Srpaulostatic void eap_aka_process_authentication_reject( 1081214501Srpaulo struct eap_sm *sm, struct eap_aka_data *data, 1082214501Srpaulo struct wpabuf *respData, struct eap_sim_attrs *attr) 1083214501Srpaulo{ 1084214501Srpaulo wpa_printf(MSG_DEBUG, "EAP-AKA: Client rejected authentication"); 1085214501Srpaulo eap_aka_state(data, FAILURE); 1086214501Srpaulo} 1087214501Srpaulo 1088214501Srpaulo 1089214501Srpaulostatic void eap_aka_process_notification(struct eap_sm *sm, 1090214501Srpaulo struct eap_aka_data *data, 1091214501Srpaulo struct wpabuf *respData, 1092214501Srpaulo struct eap_sim_attrs *attr) 1093214501Srpaulo{ 1094214501Srpaulo wpa_printf(MSG_DEBUG, "EAP-AKA: Client replied to notification"); 1095214501Srpaulo if (data->notification == EAP_SIM_SUCCESS && data->use_result_ind) 1096214501Srpaulo eap_aka_state(data, SUCCESS); 1097214501Srpaulo else 1098214501Srpaulo eap_aka_state(data, FAILURE); 1099214501Srpaulo} 1100214501Srpaulo 1101214501Srpaulo 1102214501Srpaulostatic void eap_aka_process(struct eap_sm *sm, void *priv, 1103214501Srpaulo struct wpabuf *respData) 1104214501Srpaulo{ 1105214501Srpaulo struct eap_aka_data *data = priv; 1106214501Srpaulo const u8 *pos, *end; 1107214501Srpaulo u8 subtype; 1108214501Srpaulo size_t len; 1109214501Srpaulo struct eap_sim_attrs attr; 1110214501Srpaulo 1111214501Srpaulo pos = eap_hdr_validate(EAP_VENDOR_IETF, data->eap_method, respData, 1112214501Srpaulo &len); 1113214501Srpaulo if (pos == NULL || len < 3) 1114214501Srpaulo return; 1115214501Srpaulo 1116214501Srpaulo end = pos + len; 1117214501Srpaulo subtype = *pos; 1118214501Srpaulo pos += 3; 1119214501Srpaulo 1120214501Srpaulo if (eap_aka_subtype_ok(data, subtype)) { 1121214501Srpaulo wpa_printf(MSG_DEBUG, "EAP-AKA: Unrecognized or unexpected " 1122214501Srpaulo "EAP-AKA Subtype in EAP Response"); 1123214501Srpaulo data->notification = EAP_SIM_GENERAL_FAILURE_BEFORE_AUTH; 1124214501Srpaulo eap_aka_state(data, NOTIFICATION); 1125214501Srpaulo return; 1126214501Srpaulo } 1127214501Srpaulo 1128214501Srpaulo if (eap_sim_parse_attr(pos, end, &attr, 1129214501Srpaulo data->eap_method == EAP_TYPE_AKA_PRIME ? 2 : 1, 1130214501Srpaulo 0)) { 1131214501Srpaulo wpa_printf(MSG_DEBUG, "EAP-AKA: Failed to parse attributes"); 1132214501Srpaulo data->notification = EAP_SIM_GENERAL_FAILURE_BEFORE_AUTH; 1133214501Srpaulo eap_aka_state(data, NOTIFICATION); 1134214501Srpaulo return; 1135214501Srpaulo } 1136214501Srpaulo 1137214501Srpaulo if (subtype == EAP_AKA_SUBTYPE_CLIENT_ERROR) { 1138214501Srpaulo eap_aka_process_client_error(sm, data, respData, &attr); 1139214501Srpaulo return; 1140214501Srpaulo } 1141214501Srpaulo 1142214501Srpaulo if (subtype == EAP_AKA_SUBTYPE_AUTHENTICATION_REJECT) { 1143214501Srpaulo eap_aka_process_authentication_reject(sm, data, respData, 1144214501Srpaulo &attr); 1145214501Srpaulo return; 1146214501Srpaulo } 1147214501Srpaulo 1148214501Srpaulo switch (data->state) { 1149214501Srpaulo case IDENTITY: 1150214501Srpaulo eap_aka_process_identity(sm, data, respData, &attr); 1151214501Srpaulo break; 1152214501Srpaulo case CHALLENGE: 1153214501Srpaulo if (subtype == EAP_AKA_SUBTYPE_SYNCHRONIZATION_FAILURE) { 1154214501Srpaulo eap_aka_process_sync_failure(sm, data, respData, 1155214501Srpaulo &attr); 1156214501Srpaulo } else { 1157214501Srpaulo eap_aka_process_challenge(sm, data, respData, &attr); 1158214501Srpaulo } 1159214501Srpaulo break; 1160214501Srpaulo case REAUTH: 1161214501Srpaulo eap_aka_process_reauth(sm, data, respData, &attr); 1162214501Srpaulo break; 1163214501Srpaulo case NOTIFICATION: 1164214501Srpaulo eap_aka_process_notification(sm, data, respData, &attr); 1165214501Srpaulo break; 1166214501Srpaulo default: 1167214501Srpaulo wpa_printf(MSG_DEBUG, "EAP-AKA: Unknown state %d in " 1168214501Srpaulo "process", data->state); 1169214501Srpaulo break; 1170214501Srpaulo } 1171214501Srpaulo} 1172214501Srpaulo 1173214501Srpaulo 1174214501Srpaulostatic Boolean eap_aka_isDone(struct eap_sm *sm, void *priv) 1175214501Srpaulo{ 1176214501Srpaulo struct eap_aka_data *data = priv; 1177214501Srpaulo return data->state == SUCCESS || data->state == FAILURE; 1178214501Srpaulo} 1179214501Srpaulo 1180214501Srpaulo 1181214501Srpaulostatic u8 * eap_aka_getKey(struct eap_sm *sm, void *priv, size_t *len) 1182214501Srpaulo{ 1183214501Srpaulo struct eap_aka_data *data = priv; 1184214501Srpaulo u8 *key; 1185214501Srpaulo 1186214501Srpaulo if (data->state != SUCCESS) 1187214501Srpaulo return NULL; 1188214501Srpaulo 1189214501Srpaulo key = os_malloc(EAP_SIM_KEYING_DATA_LEN); 1190214501Srpaulo if (key == NULL) 1191214501Srpaulo return NULL; 1192214501Srpaulo os_memcpy(key, data->msk, EAP_SIM_KEYING_DATA_LEN); 1193214501Srpaulo *len = EAP_SIM_KEYING_DATA_LEN; 1194214501Srpaulo return key; 1195214501Srpaulo} 1196214501Srpaulo 1197214501Srpaulo 1198214501Srpaulostatic u8 * eap_aka_get_emsk(struct eap_sm *sm, void *priv, size_t *len) 1199214501Srpaulo{ 1200214501Srpaulo struct eap_aka_data *data = priv; 1201214501Srpaulo u8 *key; 1202214501Srpaulo 1203214501Srpaulo if (data->state != SUCCESS) 1204214501Srpaulo return NULL; 1205214501Srpaulo 1206214501Srpaulo key = os_malloc(EAP_EMSK_LEN); 1207214501Srpaulo if (key == NULL) 1208214501Srpaulo return NULL; 1209214501Srpaulo os_memcpy(key, data->emsk, EAP_EMSK_LEN); 1210214501Srpaulo *len = EAP_EMSK_LEN; 1211214501Srpaulo return key; 1212214501Srpaulo} 1213214501Srpaulo 1214214501Srpaulo 1215214501Srpaulostatic Boolean eap_aka_isSuccess(struct eap_sm *sm, void *priv) 1216214501Srpaulo{ 1217214501Srpaulo struct eap_aka_data *data = priv; 1218214501Srpaulo return data->state == SUCCESS; 1219214501Srpaulo} 1220214501Srpaulo 1221214501Srpaulo 1222214501Srpauloint eap_server_aka_register(void) 1223214501Srpaulo{ 1224214501Srpaulo struct eap_method *eap; 1225214501Srpaulo int ret; 1226214501Srpaulo 1227214501Srpaulo eap = eap_server_method_alloc(EAP_SERVER_METHOD_INTERFACE_VERSION, 1228214501Srpaulo EAP_VENDOR_IETF, EAP_TYPE_AKA, "AKA"); 1229214501Srpaulo if (eap == NULL) 1230214501Srpaulo return -1; 1231214501Srpaulo 1232214501Srpaulo eap->init = eap_aka_init; 1233214501Srpaulo eap->reset = eap_aka_reset; 1234214501Srpaulo eap->buildReq = eap_aka_buildReq; 1235214501Srpaulo eap->check = eap_aka_check; 1236214501Srpaulo eap->process = eap_aka_process; 1237214501Srpaulo eap->isDone = eap_aka_isDone; 1238214501Srpaulo eap->getKey = eap_aka_getKey; 1239214501Srpaulo eap->isSuccess = eap_aka_isSuccess; 1240214501Srpaulo eap->get_emsk = eap_aka_get_emsk; 1241214501Srpaulo 1242214501Srpaulo ret = eap_server_method_register(eap); 1243214501Srpaulo if (ret) 1244214501Srpaulo eap_server_method_free(eap); 1245214501Srpaulo return ret; 1246214501Srpaulo} 1247214501Srpaulo 1248214501Srpaulo 1249214501Srpaulo#ifdef EAP_SERVER_AKA_PRIME 1250214501Srpauloint eap_server_aka_prime_register(void) 1251214501Srpaulo{ 1252214501Srpaulo struct eap_method *eap; 1253214501Srpaulo int ret; 1254214501Srpaulo 1255214501Srpaulo eap = eap_server_method_alloc(EAP_SERVER_METHOD_INTERFACE_VERSION, 1256214501Srpaulo EAP_VENDOR_IETF, EAP_TYPE_AKA_PRIME, 1257214501Srpaulo "AKA'"); 1258214501Srpaulo if (eap == NULL) 1259214501Srpaulo return -1; 1260214501Srpaulo 1261214501Srpaulo eap->init = eap_aka_prime_init; 1262214501Srpaulo eap->reset = eap_aka_reset; 1263214501Srpaulo eap->buildReq = eap_aka_buildReq; 1264214501Srpaulo eap->check = eap_aka_check; 1265214501Srpaulo eap->process = eap_aka_process; 1266214501Srpaulo eap->isDone = eap_aka_isDone; 1267214501Srpaulo eap->getKey = eap_aka_getKey; 1268214501Srpaulo eap->isSuccess = eap_aka_isSuccess; 1269214501Srpaulo eap->get_emsk = eap_aka_get_emsk; 1270214501Srpaulo 1271214501Srpaulo ret = eap_server_method_register(eap); 1272214501Srpaulo if (ret) 1273214501Srpaulo eap_server_method_free(eap); 1274214501Srpaulo 1275214501Srpaulo return ret; 1276214501Srpaulo} 1277214501Srpaulo#endif /* EAP_SERVER_AKA_PRIME */ 1278