1189251Ssam/* 2189251Ssam * EAP peer method: EAP-AKA (RFC 4187) and EAP-AKA' (draft-arkko-eap-aka-kdf) 3189251Ssam * Copyright (c) 2004-2008, Jouni Malinen <j@w1.fi> 4189251Ssam * 5189251Ssam * This program is free software; you can redistribute it and/or modify 6189251Ssam * it under the terms of the GNU General Public License version 2 as 7189251Ssam * published by the Free Software Foundation. 8189251Ssam * 9189251Ssam * Alternatively, this software may be distributed under the terms of BSD 10189251Ssam * license. 11189251Ssam * 12189251Ssam * See README and COPYING for more details. 13189251Ssam */ 14189251Ssam 15189251Ssam#include "includes.h" 16189251Ssam 17189251Ssam#include "common.h" 18189251Ssam#include "pcsc_funcs.h" 19214734Srpaulo#include "crypto/crypto.h" 20214734Srpaulo#include "crypto/sha1.h" 21214734Srpaulo#include "crypto/sha256.h" 22214734Srpaulo#include "crypto/milenage.h" 23189251Ssam#include "eap_common/eap_sim_common.h" 24214734Srpaulo#include "eap_config.h" 25214734Srpaulo#include "eap_i.h" 26189251Ssam 27189251Ssam 28189251Ssamstruct eap_aka_data { 29189251Ssam u8 ik[EAP_AKA_IK_LEN], ck[EAP_AKA_CK_LEN], res[EAP_AKA_RES_MAX_LEN]; 30189251Ssam size_t res_len; 31189251Ssam u8 nonce_s[EAP_SIM_NONCE_S_LEN]; 32189251Ssam u8 mk[EAP_SIM_MK_LEN]; 33189251Ssam u8 k_aut[EAP_AKA_PRIME_K_AUT_LEN]; 34189251Ssam u8 k_encr[EAP_SIM_K_ENCR_LEN]; 35189251Ssam u8 k_re[EAP_AKA_PRIME_K_RE_LEN]; /* EAP-AKA' only */ 36189251Ssam u8 msk[EAP_SIM_KEYING_DATA_LEN]; 37189251Ssam u8 emsk[EAP_EMSK_LEN]; 38189251Ssam u8 rand[EAP_AKA_RAND_LEN], autn[EAP_AKA_AUTN_LEN]; 39189251Ssam u8 auts[EAP_AKA_AUTS_LEN]; 40189251Ssam 41189251Ssam int num_id_req, num_notification; 42189251Ssam u8 *pseudonym; 43189251Ssam size_t pseudonym_len; 44189251Ssam u8 *reauth_id; 45189251Ssam size_t reauth_id_len; 46189251Ssam int reauth; 47189251Ssam unsigned int counter, counter_too_small; 48189251Ssam u8 *last_eap_identity; 49189251Ssam size_t last_eap_identity_len; 50189251Ssam enum { 51189251Ssam CONTINUE, RESULT_SUCCESS, RESULT_FAILURE, SUCCESS, FAILURE 52189251Ssam } state; 53189251Ssam 54189251Ssam struct wpabuf *id_msgs; 55189251Ssam int prev_id; 56189251Ssam int result_ind, use_result_ind; 57189251Ssam u8 eap_method; 58189251Ssam u8 *network_name; 59189251Ssam size_t network_name_len; 60189251Ssam u16 kdf; 61189251Ssam int kdf_negotiation; 62189251Ssam}; 63189251Ssam 64189251Ssam 65189251Ssam#ifndef CONFIG_NO_STDOUT_DEBUG 66189251Ssamstatic const char * eap_aka_state_txt(int state) 67189251Ssam{ 68189251Ssam switch (state) { 69189251Ssam case CONTINUE: 70189251Ssam return "CONTINUE"; 71189251Ssam case RESULT_SUCCESS: 72189251Ssam return "RESULT_SUCCESS"; 73189251Ssam case RESULT_FAILURE: 74189251Ssam return "RESULT_FAILURE"; 75189251Ssam case SUCCESS: 76189251Ssam return "SUCCESS"; 77189251Ssam case FAILURE: 78189251Ssam return "FAILURE"; 79189251Ssam default: 80189251Ssam return "?"; 81189251Ssam } 82189251Ssam} 83189251Ssam#endif /* CONFIG_NO_STDOUT_DEBUG */ 84189251Ssam 85189251Ssam 86189251Ssamstatic void eap_aka_state(struct eap_aka_data *data, int state) 87189251Ssam{ 88189251Ssam wpa_printf(MSG_DEBUG, "EAP-AKA: %s -> %s", 89189251Ssam eap_aka_state_txt(data->state), 90189251Ssam eap_aka_state_txt(state)); 91189251Ssam data->state = state; 92189251Ssam} 93189251Ssam 94189251Ssam 95189251Ssamstatic void * eap_aka_init(struct eap_sm *sm) 96189251Ssam{ 97189251Ssam struct eap_aka_data *data; 98189251Ssam const char *phase1 = eap_get_config_phase1(sm); 99189251Ssam 100189251Ssam data = os_zalloc(sizeof(*data)); 101189251Ssam if (data == NULL) 102189251Ssam return NULL; 103189251Ssam 104189251Ssam data->eap_method = EAP_TYPE_AKA; 105189251Ssam 106189251Ssam eap_aka_state(data, CONTINUE); 107189251Ssam data->prev_id = -1; 108189251Ssam 109189251Ssam data->result_ind = phase1 && os_strstr(phase1, "result_ind=1") != NULL; 110189251Ssam 111189251Ssam return data; 112189251Ssam} 113189251Ssam 114189251Ssam 115189251Ssam#ifdef EAP_AKA_PRIME 116189251Ssamstatic void * eap_aka_prime_init(struct eap_sm *sm) 117189251Ssam{ 118189251Ssam struct eap_aka_data *data = eap_aka_init(sm); 119189251Ssam if (data == NULL) 120189251Ssam return NULL; 121189251Ssam data->eap_method = EAP_TYPE_AKA_PRIME; 122189251Ssam return data; 123189251Ssam} 124189251Ssam#endif /* EAP_AKA_PRIME */ 125189251Ssam 126189251Ssam 127189251Ssamstatic void eap_aka_deinit(struct eap_sm *sm, void *priv) 128189251Ssam{ 129189251Ssam struct eap_aka_data *data = priv; 130189251Ssam if (data) { 131189251Ssam os_free(data->pseudonym); 132189251Ssam os_free(data->reauth_id); 133189251Ssam os_free(data->last_eap_identity); 134189251Ssam wpabuf_free(data->id_msgs); 135189251Ssam os_free(data->network_name); 136189251Ssam os_free(data); 137189251Ssam } 138189251Ssam} 139189251Ssam 140189251Ssam 141189251Ssamstatic int eap_aka_umts_auth(struct eap_sm *sm, struct eap_aka_data *data) 142189251Ssam{ 143189251Ssam struct eap_peer_config *conf; 144189251Ssam 145189251Ssam wpa_printf(MSG_DEBUG, "EAP-AKA: UMTS authentication algorithm"); 146189251Ssam 147189251Ssam conf = eap_get_config(sm); 148189251Ssam if (conf == NULL) 149189251Ssam return -1; 150189251Ssam if (conf->pcsc) { 151189251Ssam return scard_umts_auth(sm->scard_ctx, data->rand, 152189251Ssam data->autn, data->res, &data->res_len, 153189251Ssam data->ik, data->ck, data->auts); 154189251Ssam } 155189251Ssam 156189251Ssam#ifdef CONFIG_USIM_SIMULATOR 157189251Ssam if (conf->password) { 158189251Ssam u8 opc[16], k[16], sqn[6]; 159189251Ssam const char *pos; 160189251Ssam wpa_printf(MSG_DEBUG, "EAP-AKA: Use internal Milenage " 161189251Ssam "implementation for UMTS authentication"); 162189251Ssam if (conf->password_len < 78) { 163189251Ssam wpa_printf(MSG_DEBUG, "EAP-AKA: invalid Milenage " 164189251Ssam "password"); 165189251Ssam return -1; 166189251Ssam } 167189251Ssam pos = (const char *) conf->password; 168189251Ssam if (hexstr2bin(pos, k, 16)) 169189251Ssam return -1; 170189251Ssam pos += 32; 171189251Ssam if (*pos != ':') 172189251Ssam return -1; 173189251Ssam pos++; 174189251Ssam 175189251Ssam if (hexstr2bin(pos, opc, 16)) 176189251Ssam return -1; 177189251Ssam pos += 32; 178189251Ssam if (*pos != ':') 179189251Ssam return -1; 180189251Ssam pos++; 181189251Ssam 182189251Ssam if (hexstr2bin(pos, sqn, 6)) 183189251Ssam return -1; 184189251Ssam 185189251Ssam return milenage_check(opc, k, sqn, data->rand, data->autn, 186189251Ssam data->ik, data->ck, 187189251Ssam data->res, &data->res_len, data->auts); 188189251Ssam } 189189251Ssam#endif /* CONFIG_USIM_SIMULATOR */ 190189251Ssam 191189251Ssam#ifdef CONFIG_USIM_HARDCODED 192189251Ssam wpa_printf(MSG_DEBUG, "EAP-AKA: Use hardcoded Kc and SRES values for " 193189251Ssam "testing"); 194189251Ssam 195189251Ssam /* These hardcoded Kc and SRES values are used for testing. 196189251Ssam * Could consider making them configurable. */ 197189251Ssam os_memset(data->res, '2', EAP_AKA_RES_MAX_LEN); 198189251Ssam data->res_len = EAP_AKA_RES_MAX_LEN; 199189251Ssam os_memset(data->ik, '3', EAP_AKA_IK_LEN); 200189251Ssam os_memset(data->ck, '4', EAP_AKA_CK_LEN); 201189251Ssam { 202189251Ssam u8 autn[EAP_AKA_AUTN_LEN]; 203189251Ssam os_memset(autn, '1', EAP_AKA_AUTN_LEN); 204189251Ssam if (os_memcmp(autn, data->autn, EAP_AKA_AUTN_LEN) != 0) { 205189251Ssam wpa_printf(MSG_WARNING, "EAP-AKA: AUTN did not match " 206189251Ssam "with expected value"); 207189251Ssam return -1; 208189251Ssam } 209189251Ssam } 210189251Ssam#if 0 211189251Ssam { 212189251Ssam static int test_resync = 1; 213189251Ssam if (test_resync) { 214189251Ssam /* Test Resynchronization */ 215189251Ssam test_resync = 0; 216189251Ssam return -2; 217189251Ssam } 218189251Ssam } 219189251Ssam#endif 220189251Ssam return 0; 221189251Ssam 222189251Ssam#else /* CONFIG_USIM_HARDCODED */ 223189251Ssam 224189251Ssam wpa_printf(MSG_DEBUG, "EAP-AKA: No UMTS authentication algorith " 225189251Ssam "enabled"); 226189251Ssam return -1; 227189251Ssam 228189251Ssam#endif /* CONFIG_USIM_HARDCODED */ 229189251Ssam} 230189251Ssam 231189251Ssam 232189251Ssam#define CLEAR_PSEUDONYM 0x01 233189251Ssam#define CLEAR_REAUTH_ID 0x02 234189251Ssam#define CLEAR_EAP_ID 0x04 235189251Ssam 236189251Ssamstatic void eap_aka_clear_identities(struct eap_aka_data *data, int id) 237189251Ssam{ 238189251Ssam wpa_printf(MSG_DEBUG, "EAP-AKA: forgetting old%s%s%s", 239189251Ssam id & CLEAR_PSEUDONYM ? " pseudonym" : "", 240189251Ssam id & CLEAR_REAUTH_ID ? " reauth_id" : "", 241189251Ssam id & CLEAR_EAP_ID ? " eap_id" : ""); 242189251Ssam if (id & CLEAR_PSEUDONYM) { 243189251Ssam os_free(data->pseudonym); 244189251Ssam data->pseudonym = NULL; 245189251Ssam data->pseudonym_len = 0; 246189251Ssam } 247189251Ssam if (id & CLEAR_REAUTH_ID) { 248189251Ssam os_free(data->reauth_id); 249189251Ssam data->reauth_id = NULL; 250189251Ssam data->reauth_id_len = 0; 251189251Ssam } 252189251Ssam if (id & CLEAR_EAP_ID) { 253189251Ssam os_free(data->last_eap_identity); 254189251Ssam data->last_eap_identity = NULL; 255189251Ssam data->last_eap_identity_len = 0; 256189251Ssam } 257189251Ssam} 258189251Ssam 259189251Ssam 260189251Ssamstatic int eap_aka_learn_ids(struct eap_aka_data *data, 261189251Ssam struct eap_sim_attrs *attr) 262189251Ssam{ 263189251Ssam if (attr->next_pseudonym) { 264189251Ssam os_free(data->pseudonym); 265189251Ssam data->pseudonym = os_malloc(attr->next_pseudonym_len); 266189251Ssam if (data->pseudonym == NULL) { 267189251Ssam wpa_printf(MSG_INFO, "EAP-AKA: (encr) No memory for " 268189251Ssam "next pseudonym"); 269189251Ssam return -1; 270189251Ssam } 271189251Ssam os_memcpy(data->pseudonym, attr->next_pseudonym, 272189251Ssam attr->next_pseudonym_len); 273189251Ssam data->pseudonym_len = attr->next_pseudonym_len; 274189251Ssam wpa_hexdump_ascii(MSG_DEBUG, 275189251Ssam "EAP-AKA: (encr) AT_NEXT_PSEUDONYM", 276189251Ssam data->pseudonym, 277189251Ssam data->pseudonym_len); 278189251Ssam } 279189251Ssam 280189251Ssam if (attr->next_reauth_id) { 281189251Ssam os_free(data->reauth_id); 282189251Ssam data->reauth_id = os_malloc(attr->next_reauth_id_len); 283189251Ssam if (data->reauth_id == NULL) { 284189251Ssam wpa_printf(MSG_INFO, "EAP-AKA: (encr) No memory for " 285189251Ssam "next reauth_id"); 286189251Ssam return -1; 287189251Ssam } 288189251Ssam os_memcpy(data->reauth_id, attr->next_reauth_id, 289189251Ssam attr->next_reauth_id_len); 290189251Ssam data->reauth_id_len = attr->next_reauth_id_len; 291189251Ssam wpa_hexdump_ascii(MSG_DEBUG, 292189251Ssam "EAP-AKA: (encr) AT_NEXT_REAUTH_ID", 293189251Ssam data->reauth_id, 294189251Ssam data->reauth_id_len); 295189251Ssam } 296189251Ssam 297189251Ssam return 0; 298189251Ssam} 299189251Ssam 300189251Ssam 301189251Ssamstatic int eap_aka_add_id_msg(struct eap_aka_data *data, 302189251Ssam const struct wpabuf *msg) 303189251Ssam{ 304189251Ssam if (msg == NULL) 305189251Ssam return -1; 306189251Ssam 307189251Ssam if (data->id_msgs == NULL) { 308189251Ssam data->id_msgs = wpabuf_dup(msg); 309189251Ssam return data->id_msgs == NULL ? -1 : 0; 310189251Ssam } 311189251Ssam 312189251Ssam if (wpabuf_resize(&data->id_msgs, wpabuf_len(msg)) < 0) 313189251Ssam return -1; 314189251Ssam wpabuf_put_buf(data->id_msgs, msg); 315189251Ssam 316189251Ssam return 0; 317189251Ssam} 318189251Ssam 319189251Ssam 320189251Ssamstatic void eap_aka_add_checkcode(struct eap_aka_data *data, 321189251Ssam struct eap_sim_msg *msg) 322189251Ssam{ 323189251Ssam const u8 *addr; 324189251Ssam size_t len; 325189251Ssam u8 hash[SHA256_MAC_LEN]; 326189251Ssam 327189251Ssam wpa_printf(MSG_DEBUG, " AT_CHECKCODE"); 328189251Ssam 329189251Ssam if (data->id_msgs == NULL) { 330189251Ssam /* 331189251Ssam * No EAP-AKA/Identity packets were exchanged - send empty 332189251Ssam * checkcode. 333189251Ssam */ 334189251Ssam eap_sim_msg_add(msg, EAP_SIM_AT_CHECKCODE, 0, NULL, 0); 335189251Ssam return; 336189251Ssam } 337189251Ssam 338189251Ssam /* Checkcode is SHA1/SHA256 hash over all EAP-AKA/Identity packets. */ 339189251Ssam addr = wpabuf_head(data->id_msgs); 340189251Ssam len = wpabuf_len(data->id_msgs); 341189251Ssam wpa_hexdump(MSG_MSGDUMP, "EAP-AKA: AT_CHECKCODE data", addr, len); 342189251Ssam#ifdef EAP_AKA_PRIME 343189251Ssam if (data->eap_method == EAP_TYPE_AKA_PRIME) 344189251Ssam sha256_vector(1, &addr, &len, hash); 345189251Ssam else 346189251Ssam#endif /* EAP_AKA_PRIME */ 347189251Ssam sha1_vector(1, &addr, &len, hash); 348189251Ssam 349189251Ssam eap_sim_msg_add(msg, EAP_SIM_AT_CHECKCODE, 0, hash, 350189251Ssam data->eap_method == EAP_TYPE_AKA_PRIME ? 351189251Ssam EAP_AKA_PRIME_CHECKCODE_LEN : EAP_AKA_CHECKCODE_LEN); 352189251Ssam} 353189251Ssam 354189251Ssam 355189251Ssamstatic int eap_aka_verify_checkcode(struct eap_aka_data *data, 356189251Ssam const u8 *checkcode, size_t checkcode_len) 357189251Ssam{ 358189251Ssam const u8 *addr; 359189251Ssam size_t len; 360189251Ssam u8 hash[SHA256_MAC_LEN]; 361189251Ssam size_t hash_len; 362189251Ssam 363189251Ssam if (checkcode == NULL) 364189251Ssam return -1; 365189251Ssam 366189251Ssam if (data->id_msgs == NULL) { 367189251Ssam if (checkcode_len != 0) { 368189251Ssam wpa_printf(MSG_DEBUG, "EAP-AKA: Checkcode from server " 369189251Ssam "indicates that AKA/Identity messages were " 370189251Ssam "used, but they were not"); 371189251Ssam return -1; 372189251Ssam } 373189251Ssam return 0; 374189251Ssam } 375189251Ssam 376189251Ssam hash_len = data->eap_method == EAP_TYPE_AKA_PRIME ? 377189251Ssam EAP_AKA_PRIME_CHECKCODE_LEN : EAP_AKA_CHECKCODE_LEN; 378189251Ssam 379189251Ssam if (checkcode_len != hash_len) { 380189251Ssam wpa_printf(MSG_DEBUG, "EAP-AKA: Checkcode from server " 381189251Ssam "indicates that AKA/Identity message were not " 382189251Ssam "used, but they were"); 383189251Ssam return -1; 384189251Ssam } 385189251Ssam 386189251Ssam /* Checkcode is SHA1/SHA256 hash over all EAP-AKA/Identity packets. */ 387189251Ssam addr = wpabuf_head(data->id_msgs); 388189251Ssam len = wpabuf_len(data->id_msgs); 389189251Ssam#ifdef EAP_AKA_PRIME 390189251Ssam if (data->eap_method == EAP_TYPE_AKA_PRIME) 391189251Ssam sha256_vector(1, &addr, &len, hash); 392189251Ssam else 393189251Ssam#endif /* EAP_AKA_PRIME */ 394189251Ssam sha1_vector(1, &addr, &len, hash); 395189251Ssam 396189251Ssam if (os_memcmp(hash, checkcode, hash_len) != 0) { 397189251Ssam wpa_printf(MSG_DEBUG, "EAP-AKA: Mismatch in AT_CHECKCODE"); 398189251Ssam return -1; 399189251Ssam } 400189251Ssam 401189251Ssam return 0; 402189251Ssam} 403189251Ssam 404189251Ssam 405189251Ssamstatic struct wpabuf * eap_aka_client_error(struct eap_aka_data *data, u8 id, 406189251Ssam int err) 407189251Ssam{ 408189251Ssam struct eap_sim_msg *msg; 409189251Ssam 410189251Ssam eap_aka_state(data, FAILURE); 411189251Ssam data->num_id_req = 0; 412189251Ssam data->num_notification = 0; 413189251Ssam 414189251Ssam msg = eap_sim_msg_init(EAP_CODE_RESPONSE, id, data->eap_method, 415189251Ssam EAP_AKA_SUBTYPE_CLIENT_ERROR); 416189251Ssam eap_sim_msg_add(msg, EAP_SIM_AT_CLIENT_ERROR_CODE, err, NULL, 0); 417189251Ssam return eap_sim_msg_finish(msg, NULL, NULL, 0); 418189251Ssam} 419189251Ssam 420189251Ssam 421189251Ssamstatic struct wpabuf * eap_aka_authentication_reject(struct eap_aka_data *data, 422189251Ssam u8 id) 423189251Ssam{ 424189251Ssam struct eap_sim_msg *msg; 425189251Ssam 426189251Ssam eap_aka_state(data, FAILURE); 427189251Ssam data->num_id_req = 0; 428189251Ssam data->num_notification = 0; 429189251Ssam 430189251Ssam wpa_printf(MSG_DEBUG, "Generating EAP-AKA Authentication-Reject " 431189251Ssam "(id=%d)", id); 432189251Ssam msg = eap_sim_msg_init(EAP_CODE_RESPONSE, id, data->eap_method, 433189251Ssam EAP_AKA_SUBTYPE_AUTHENTICATION_REJECT); 434189251Ssam return eap_sim_msg_finish(msg, NULL, NULL, 0); 435189251Ssam} 436189251Ssam 437189251Ssam 438189251Ssamstatic struct wpabuf * eap_aka_synchronization_failure( 439189251Ssam struct eap_aka_data *data, u8 id) 440189251Ssam{ 441189251Ssam struct eap_sim_msg *msg; 442189251Ssam 443189251Ssam data->num_id_req = 0; 444189251Ssam data->num_notification = 0; 445189251Ssam 446189251Ssam wpa_printf(MSG_DEBUG, "Generating EAP-AKA Synchronization-Failure " 447189251Ssam "(id=%d)", id); 448189251Ssam msg = eap_sim_msg_init(EAP_CODE_RESPONSE, id, data->eap_method, 449189251Ssam EAP_AKA_SUBTYPE_SYNCHRONIZATION_FAILURE); 450189251Ssam wpa_printf(MSG_DEBUG, " AT_AUTS"); 451189251Ssam eap_sim_msg_add_full(msg, EAP_SIM_AT_AUTS, data->auts, 452189251Ssam EAP_AKA_AUTS_LEN); 453189251Ssam return eap_sim_msg_finish(msg, NULL, NULL, 0); 454189251Ssam} 455189251Ssam 456189251Ssam 457189251Ssamstatic struct wpabuf * eap_aka_response_identity(struct eap_sm *sm, 458189251Ssam struct eap_aka_data *data, 459189251Ssam u8 id, 460189251Ssam enum eap_sim_id_req id_req) 461189251Ssam{ 462189251Ssam const u8 *identity = NULL; 463189251Ssam size_t identity_len = 0; 464189251Ssam struct eap_sim_msg *msg; 465189251Ssam 466189251Ssam data->reauth = 0; 467189251Ssam if (id_req == ANY_ID && data->reauth_id) { 468189251Ssam identity = data->reauth_id; 469189251Ssam identity_len = data->reauth_id_len; 470189251Ssam data->reauth = 1; 471189251Ssam } else if ((id_req == ANY_ID || id_req == FULLAUTH_ID) && 472189251Ssam data->pseudonym) { 473189251Ssam identity = data->pseudonym; 474189251Ssam identity_len = data->pseudonym_len; 475189251Ssam eap_aka_clear_identities(data, CLEAR_REAUTH_ID); 476189251Ssam } else if (id_req != NO_ID_REQ) { 477189251Ssam identity = eap_get_config_identity(sm, &identity_len); 478189251Ssam if (identity) { 479189251Ssam eap_aka_clear_identities(data, CLEAR_PSEUDONYM | 480189251Ssam CLEAR_REAUTH_ID); 481189251Ssam } 482189251Ssam } 483189251Ssam if (id_req != NO_ID_REQ) 484189251Ssam eap_aka_clear_identities(data, CLEAR_EAP_ID); 485189251Ssam 486189251Ssam wpa_printf(MSG_DEBUG, "Generating EAP-AKA Identity (id=%d)", id); 487189251Ssam msg = eap_sim_msg_init(EAP_CODE_RESPONSE, id, data->eap_method, 488189251Ssam EAP_AKA_SUBTYPE_IDENTITY); 489189251Ssam 490189251Ssam if (identity) { 491189251Ssam wpa_hexdump_ascii(MSG_DEBUG, " AT_IDENTITY", 492189251Ssam identity, identity_len); 493189251Ssam eap_sim_msg_add(msg, EAP_SIM_AT_IDENTITY, identity_len, 494189251Ssam identity, identity_len); 495189251Ssam } 496189251Ssam 497189251Ssam return eap_sim_msg_finish(msg, NULL, NULL, 0); 498189251Ssam} 499189251Ssam 500189251Ssam 501189251Ssamstatic struct wpabuf * eap_aka_response_challenge(struct eap_aka_data *data, 502189251Ssam u8 id) 503189251Ssam{ 504189251Ssam struct eap_sim_msg *msg; 505189251Ssam 506189251Ssam wpa_printf(MSG_DEBUG, "Generating EAP-AKA Challenge (id=%d)", id); 507189251Ssam msg = eap_sim_msg_init(EAP_CODE_RESPONSE, id, data->eap_method, 508189251Ssam EAP_AKA_SUBTYPE_CHALLENGE); 509189251Ssam wpa_printf(MSG_DEBUG, " AT_RES"); 510189251Ssam eap_sim_msg_add(msg, EAP_SIM_AT_RES, data->res_len * 8, 511189251Ssam data->res, data->res_len); 512189251Ssam eap_aka_add_checkcode(data, msg); 513189251Ssam if (data->use_result_ind) { 514189251Ssam wpa_printf(MSG_DEBUG, " AT_RESULT_IND"); 515189251Ssam eap_sim_msg_add(msg, EAP_SIM_AT_RESULT_IND, 0, NULL, 0); 516189251Ssam } 517189251Ssam wpa_printf(MSG_DEBUG, " AT_MAC"); 518189251Ssam eap_sim_msg_add_mac(msg, EAP_SIM_AT_MAC); 519189251Ssam return eap_sim_msg_finish(msg, data->k_aut, (u8 *) "", 0); 520189251Ssam} 521189251Ssam 522189251Ssam 523189251Ssamstatic struct wpabuf * eap_aka_response_reauth(struct eap_aka_data *data, 524189251Ssam u8 id, int counter_too_small, 525189251Ssam const u8 *nonce_s) 526189251Ssam{ 527189251Ssam struct eap_sim_msg *msg; 528189251Ssam unsigned int counter; 529189251Ssam 530189251Ssam wpa_printf(MSG_DEBUG, "Generating EAP-AKA Reauthentication (id=%d)", 531189251Ssam id); 532189251Ssam msg = eap_sim_msg_init(EAP_CODE_RESPONSE, id, data->eap_method, 533189251Ssam EAP_AKA_SUBTYPE_REAUTHENTICATION); 534189251Ssam wpa_printf(MSG_DEBUG, " AT_IV"); 535189251Ssam wpa_printf(MSG_DEBUG, " AT_ENCR_DATA"); 536189251Ssam eap_sim_msg_add_encr_start(msg, EAP_SIM_AT_IV, EAP_SIM_AT_ENCR_DATA); 537189251Ssam 538189251Ssam if (counter_too_small) { 539189251Ssam wpa_printf(MSG_DEBUG, " *AT_COUNTER_TOO_SMALL"); 540189251Ssam eap_sim_msg_add(msg, EAP_SIM_AT_COUNTER_TOO_SMALL, 0, NULL, 0); 541189251Ssam counter = data->counter_too_small; 542189251Ssam } else 543189251Ssam counter = data->counter; 544189251Ssam 545189251Ssam wpa_printf(MSG_DEBUG, " *AT_COUNTER %d", counter); 546189251Ssam eap_sim_msg_add(msg, EAP_SIM_AT_COUNTER, counter, NULL, 0); 547189251Ssam 548189251Ssam if (eap_sim_msg_add_encr_end(msg, data->k_encr, EAP_SIM_AT_PADDING)) { 549189251Ssam wpa_printf(MSG_WARNING, "EAP-AKA: Failed to encrypt " 550189251Ssam "AT_ENCR_DATA"); 551189251Ssam eap_sim_msg_free(msg); 552189251Ssam return NULL; 553189251Ssam } 554189251Ssam eap_aka_add_checkcode(data, msg); 555189251Ssam if (data->use_result_ind) { 556189251Ssam wpa_printf(MSG_DEBUG, " AT_RESULT_IND"); 557189251Ssam eap_sim_msg_add(msg, EAP_SIM_AT_RESULT_IND, 0, NULL, 0); 558189251Ssam } 559189251Ssam wpa_printf(MSG_DEBUG, " AT_MAC"); 560189251Ssam eap_sim_msg_add_mac(msg, EAP_SIM_AT_MAC); 561189251Ssam return eap_sim_msg_finish(msg, data->k_aut, nonce_s, 562189251Ssam EAP_SIM_NONCE_S_LEN); 563189251Ssam} 564189251Ssam 565189251Ssam 566189251Ssamstatic struct wpabuf * eap_aka_response_notification(struct eap_aka_data *data, 567189251Ssam u8 id, u16 notification) 568189251Ssam{ 569189251Ssam struct eap_sim_msg *msg; 570189251Ssam u8 *k_aut = (notification & 0x4000) == 0 ? data->k_aut : NULL; 571189251Ssam 572189251Ssam wpa_printf(MSG_DEBUG, "Generating EAP-AKA Notification (id=%d)", id); 573189251Ssam msg = eap_sim_msg_init(EAP_CODE_RESPONSE, id, data->eap_method, 574189251Ssam EAP_AKA_SUBTYPE_NOTIFICATION); 575189251Ssam if (k_aut && data->reauth) { 576189251Ssam wpa_printf(MSG_DEBUG, " AT_IV"); 577189251Ssam wpa_printf(MSG_DEBUG, " AT_ENCR_DATA"); 578189251Ssam eap_sim_msg_add_encr_start(msg, EAP_SIM_AT_IV, 579189251Ssam EAP_SIM_AT_ENCR_DATA); 580189251Ssam wpa_printf(MSG_DEBUG, " *AT_COUNTER %d", data->counter); 581189251Ssam eap_sim_msg_add(msg, EAP_SIM_AT_COUNTER, data->counter, 582189251Ssam NULL, 0); 583189251Ssam if (eap_sim_msg_add_encr_end(msg, data->k_encr, 584189251Ssam EAP_SIM_AT_PADDING)) { 585189251Ssam wpa_printf(MSG_WARNING, "EAP-AKA: Failed to encrypt " 586189251Ssam "AT_ENCR_DATA"); 587189251Ssam eap_sim_msg_free(msg); 588189251Ssam return NULL; 589189251Ssam } 590189251Ssam } 591189251Ssam if (k_aut) { 592189251Ssam wpa_printf(MSG_DEBUG, " AT_MAC"); 593189251Ssam eap_sim_msg_add_mac(msg, EAP_SIM_AT_MAC); 594189251Ssam } 595189251Ssam return eap_sim_msg_finish(msg, k_aut, (u8 *) "", 0); 596189251Ssam} 597189251Ssam 598189251Ssam 599189251Ssamstatic struct wpabuf * eap_aka_process_identity(struct eap_sm *sm, 600189251Ssam struct eap_aka_data *data, 601189251Ssam u8 id, 602189251Ssam const struct wpabuf *reqData, 603189251Ssam struct eap_sim_attrs *attr) 604189251Ssam{ 605189251Ssam int id_error; 606189251Ssam struct wpabuf *buf; 607189251Ssam 608189251Ssam wpa_printf(MSG_DEBUG, "EAP-AKA: subtype Identity"); 609189251Ssam 610189251Ssam id_error = 0; 611189251Ssam switch (attr->id_req) { 612189251Ssam case NO_ID_REQ: 613189251Ssam break; 614189251Ssam case ANY_ID: 615189251Ssam if (data->num_id_req > 0) 616189251Ssam id_error++; 617189251Ssam data->num_id_req++; 618189251Ssam break; 619189251Ssam case FULLAUTH_ID: 620189251Ssam if (data->num_id_req > 1) 621189251Ssam id_error++; 622189251Ssam data->num_id_req++; 623189251Ssam break; 624189251Ssam case PERMANENT_ID: 625189251Ssam if (data->num_id_req > 2) 626189251Ssam id_error++; 627189251Ssam data->num_id_req++; 628189251Ssam break; 629189251Ssam } 630189251Ssam if (id_error) { 631189251Ssam wpa_printf(MSG_INFO, "EAP-AKA: Too many ID requests " 632189251Ssam "used within one authentication"); 633189251Ssam return eap_aka_client_error(data, id, 634189251Ssam EAP_AKA_UNABLE_TO_PROCESS_PACKET); 635189251Ssam } 636189251Ssam 637189251Ssam buf = eap_aka_response_identity(sm, data, id, attr->id_req); 638189251Ssam 639189251Ssam if (data->prev_id != id) { 640189251Ssam eap_aka_add_id_msg(data, reqData); 641189251Ssam eap_aka_add_id_msg(data, buf); 642189251Ssam data->prev_id = id; 643189251Ssam } 644189251Ssam 645189251Ssam return buf; 646189251Ssam} 647189251Ssam 648189251Ssam 649189251Ssamstatic int eap_aka_verify_mac(struct eap_aka_data *data, 650189251Ssam const struct wpabuf *req, 651189251Ssam const u8 *mac, const u8 *extra, 652189251Ssam size_t extra_len) 653189251Ssam{ 654189251Ssam if (data->eap_method == EAP_TYPE_AKA_PRIME) 655189251Ssam return eap_sim_verify_mac_sha256(data->k_aut, req, mac, extra, 656189251Ssam extra_len); 657189251Ssam return eap_sim_verify_mac(data->k_aut, req, mac, extra, extra_len); 658189251Ssam} 659189251Ssam 660189251Ssam 661189251Ssam#ifdef EAP_AKA_PRIME 662189251Ssamstatic struct wpabuf * eap_aka_prime_kdf_select(struct eap_aka_data *data, 663189251Ssam u8 id, u16 kdf) 664189251Ssam{ 665189251Ssam struct eap_sim_msg *msg; 666189251Ssam 667189251Ssam data->kdf_negotiation = 1; 668189251Ssam data->kdf = kdf; 669189251Ssam wpa_printf(MSG_DEBUG, "Generating EAP-AKA Challenge (id=%d) (KDF " 670189251Ssam "select)", id); 671189251Ssam msg = eap_sim_msg_init(EAP_CODE_RESPONSE, id, data->eap_method, 672189251Ssam EAP_AKA_SUBTYPE_CHALLENGE); 673189251Ssam wpa_printf(MSG_DEBUG, " AT_KDF"); 674189251Ssam eap_sim_msg_add(msg, EAP_SIM_AT_KDF, kdf, NULL, 0); 675189251Ssam return eap_sim_msg_finish(msg, NULL, NULL, 0); 676189251Ssam} 677189251Ssam 678189251Ssam 679189251Ssamstatic struct wpabuf * eap_aka_prime_kdf_neg(struct eap_aka_data *data, 680189251Ssam u8 id, struct eap_sim_attrs *attr) 681189251Ssam{ 682189251Ssam size_t i; 683189251Ssam 684189251Ssam for (i = 0; i < attr->kdf_count; i++) { 685189251Ssam if (attr->kdf[i] == EAP_AKA_PRIME_KDF) 686189251Ssam return eap_aka_prime_kdf_select(data, id, 687189251Ssam EAP_AKA_PRIME_KDF); 688189251Ssam } 689189251Ssam 690189251Ssam /* No matching KDF found - fail authentication as if AUTN had been 691189251Ssam * incorrect */ 692189251Ssam return eap_aka_authentication_reject(data, id); 693189251Ssam} 694189251Ssam 695189251Ssam 696189251Ssamstatic int eap_aka_prime_kdf_valid(struct eap_aka_data *data, 697189251Ssam struct eap_sim_attrs *attr) 698189251Ssam{ 699189251Ssam size_t i, j; 700189251Ssam 701189251Ssam if (attr->kdf_count == 0) 702189251Ssam return 0; 703189251Ssam 704189251Ssam /* The only allowed (and required) duplication of a KDF is the addition 705189251Ssam * of the selected KDF into the beginning of the list. */ 706189251Ssam 707189251Ssam if (data->kdf_negotiation) { 708189251Ssam if (attr->kdf[0] != data->kdf) { 709189251Ssam wpa_printf(MSG_WARNING, "EAP-AKA': The server did not " 710189251Ssam "accept the selected KDF"); 711189251Ssam return 0; 712189251Ssam } 713189251Ssam 714189251Ssam for (i = 1; i < attr->kdf_count; i++) { 715189251Ssam if (attr->kdf[i] == data->kdf) 716189251Ssam break; 717189251Ssam } 718189251Ssam if (i == attr->kdf_count && 719189251Ssam attr->kdf_count < EAP_AKA_PRIME_KDF_MAX) { 720189251Ssam wpa_printf(MSG_WARNING, "EAP-AKA': The server did not " 721189251Ssam "duplicate the selected KDF"); 722189251Ssam return 0; 723189251Ssam } 724189251Ssam 725189251Ssam /* TODO: should check that the list is identical to the one 726189251Ssam * used in the previous Challenge message apart from the added 727189251Ssam * entry in the beginning. */ 728189251Ssam } 729189251Ssam 730189251Ssam for (i = data->kdf ? 1 : 0; i < attr->kdf_count; i++) { 731189251Ssam for (j = i + 1; j < attr->kdf_count; j++) { 732189251Ssam if (attr->kdf[i] == attr->kdf[j]) { 733189251Ssam wpa_printf(MSG_WARNING, "EAP-AKA': The server " 734189251Ssam "included a duplicated KDF"); 735189251Ssam return 0; 736189251Ssam } 737189251Ssam } 738189251Ssam } 739189251Ssam 740189251Ssam return 1; 741189251Ssam} 742189251Ssam#endif /* EAP_AKA_PRIME */ 743189251Ssam 744189251Ssam 745189251Ssamstatic struct wpabuf * eap_aka_process_challenge(struct eap_sm *sm, 746189251Ssam struct eap_aka_data *data, 747189251Ssam u8 id, 748189251Ssam const struct wpabuf *reqData, 749189251Ssam struct eap_sim_attrs *attr) 750189251Ssam{ 751189251Ssam const u8 *identity; 752189251Ssam size_t identity_len; 753189251Ssam int res; 754189251Ssam struct eap_sim_attrs eattr; 755189251Ssam 756189251Ssam wpa_printf(MSG_DEBUG, "EAP-AKA: subtype Challenge"); 757189251Ssam 758189251Ssam if (attr->checkcode && 759189251Ssam eap_aka_verify_checkcode(data, attr->checkcode, 760189251Ssam attr->checkcode_len)) { 761189251Ssam wpa_printf(MSG_WARNING, "EAP-AKA: Invalid AT_CHECKCODE in the " 762189251Ssam "message"); 763189251Ssam return eap_aka_client_error(data, id, 764189251Ssam EAP_AKA_UNABLE_TO_PROCESS_PACKET); 765189251Ssam } 766189251Ssam 767189251Ssam#ifdef EAP_AKA_PRIME 768189251Ssam if (data->eap_method == EAP_TYPE_AKA_PRIME) { 769189251Ssam if (!attr->kdf_input || attr->kdf_input_len == 0) { 770189251Ssam wpa_printf(MSG_WARNING, "EAP-AKA': Challenge message " 771189251Ssam "did not include non-empty AT_KDF_INPUT"); 772189251Ssam /* Fail authentication as if AUTN had been incorrect */ 773189251Ssam return eap_aka_authentication_reject(data, id); 774189251Ssam } 775189251Ssam os_free(data->network_name); 776189251Ssam data->network_name = os_malloc(attr->kdf_input_len); 777189251Ssam if (data->network_name == NULL) { 778189251Ssam wpa_printf(MSG_WARNING, "EAP-AKA': No memory for " 779189251Ssam "storing Network Name"); 780189251Ssam return eap_aka_authentication_reject(data, id); 781189251Ssam } 782189251Ssam os_memcpy(data->network_name, attr->kdf_input, 783189251Ssam attr->kdf_input_len); 784189251Ssam data->network_name_len = attr->kdf_input_len; 785189251Ssam wpa_hexdump_ascii(MSG_DEBUG, "EAP-AKA': Network Name " 786189251Ssam "(AT_KDF_INPUT)", 787189251Ssam data->network_name, data->network_name_len); 788189251Ssam /* TODO: check Network Name per 3GPP.33.402 */ 789189251Ssam 790189251Ssam if (!eap_aka_prime_kdf_valid(data, attr)) 791189251Ssam return eap_aka_authentication_reject(data, id); 792189251Ssam 793189251Ssam if (attr->kdf[0] != EAP_AKA_PRIME_KDF) 794189251Ssam return eap_aka_prime_kdf_neg(data, id, attr); 795189251Ssam 796189251Ssam data->kdf = EAP_AKA_PRIME_KDF; 797189251Ssam wpa_printf(MSG_DEBUG, "EAP-AKA': KDF %d selected", data->kdf); 798189251Ssam } 799189251Ssam 800189251Ssam if (data->eap_method == EAP_TYPE_AKA && attr->bidding) { 801189251Ssam u16 flags = WPA_GET_BE16(attr->bidding); 802189251Ssam if ((flags & EAP_AKA_BIDDING_FLAG_D) && 803189251Ssam eap_allowed_method(sm, EAP_VENDOR_IETF, 804189251Ssam EAP_TYPE_AKA_PRIME)) { 805189251Ssam wpa_printf(MSG_WARNING, "EAP-AKA: Bidding down from " 806189251Ssam "AKA' to AKA detected"); 807189251Ssam /* Fail authentication as if AUTN had been incorrect */ 808189251Ssam return eap_aka_authentication_reject(data, id); 809189251Ssam } 810189251Ssam } 811189251Ssam#endif /* EAP_AKA_PRIME */ 812189251Ssam 813189251Ssam data->reauth = 0; 814189251Ssam if (!attr->mac || !attr->rand || !attr->autn) { 815189251Ssam wpa_printf(MSG_WARNING, "EAP-AKA: Challenge message " 816189251Ssam "did not include%s%s%s", 817189251Ssam !attr->mac ? " AT_MAC" : "", 818189251Ssam !attr->rand ? " AT_RAND" : "", 819189251Ssam !attr->autn ? " AT_AUTN" : ""); 820189251Ssam return eap_aka_client_error(data, id, 821189251Ssam EAP_AKA_UNABLE_TO_PROCESS_PACKET); 822189251Ssam } 823189251Ssam os_memcpy(data->rand, attr->rand, EAP_AKA_RAND_LEN); 824189251Ssam os_memcpy(data->autn, attr->autn, EAP_AKA_AUTN_LEN); 825189251Ssam 826189251Ssam res = eap_aka_umts_auth(sm, data); 827189251Ssam if (res == -1) { 828189251Ssam wpa_printf(MSG_WARNING, "EAP-AKA: UMTS authentication " 829189251Ssam "failed (AUTN)"); 830189251Ssam return eap_aka_authentication_reject(data, id); 831189251Ssam } else if (res == -2) { 832189251Ssam wpa_printf(MSG_WARNING, "EAP-AKA: UMTS authentication " 833189251Ssam "failed (AUTN seq# -> AUTS)"); 834189251Ssam return eap_aka_synchronization_failure(data, id); 835189251Ssam } else if (res) { 836189251Ssam wpa_printf(MSG_WARNING, "EAP-AKA: UMTS authentication failed"); 837189251Ssam return eap_aka_client_error(data, id, 838189251Ssam EAP_AKA_UNABLE_TO_PROCESS_PACKET); 839189251Ssam } 840189251Ssam#ifdef EAP_AKA_PRIME 841189251Ssam if (data->eap_method == EAP_TYPE_AKA_PRIME) { 842189251Ssam /* Note: AUTN = (SQN ^ AK) || AMF || MAC which gives us the 843189251Ssam * needed 6-octet SQN ^ AK for CK',IK' derivation */ 844189251Ssam u16 amf = WPA_GET_BE16(data->autn + 6); 845189251Ssam if (!(amf & 0x8000)) { 846189251Ssam wpa_printf(MSG_WARNING, "EAP-AKA': AMF separation bit " 847189251Ssam "not set (AMF=0x%4x)", amf); 848189251Ssam return eap_aka_authentication_reject(data, id); 849189251Ssam } 850189251Ssam eap_aka_prime_derive_ck_ik_prime(data->ck, data->ik, 851189251Ssam data->autn, 852189251Ssam data->network_name, 853189251Ssam data->network_name_len); 854189251Ssam } 855189251Ssam#endif /* EAP_AKA_PRIME */ 856189251Ssam if (data->last_eap_identity) { 857189251Ssam identity = data->last_eap_identity; 858189251Ssam identity_len = data->last_eap_identity_len; 859189251Ssam } else if (data->pseudonym) { 860189251Ssam identity = data->pseudonym; 861189251Ssam identity_len = data->pseudonym_len; 862189251Ssam } else 863189251Ssam identity = eap_get_config_identity(sm, &identity_len); 864189251Ssam wpa_hexdump_ascii(MSG_DEBUG, "EAP-AKA: Selected identity for MK " 865189251Ssam "derivation", identity, identity_len); 866189251Ssam if (data->eap_method == EAP_TYPE_AKA_PRIME) { 867189251Ssam eap_aka_prime_derive_keys(identity, identity_len, data->ik, 868189251Ssam data->ck, data->k_encr, data->k_aut, 869189251Ssam data->k_re, data->msk, data->emsk); 870189251Ssam } else { 871189251Ssam eap_aka_derive_mk(identity, identity_len, data->ik, data->ck, 872189251Ssam data->mk); 873189251Ssam eap_sim_derive_keys(data->mk, data->k_encr, data->k_aut, 874189251Ssam data->msk, data->emsk); 875189251Ssam } 876189251Ssam if (eap_aka_verify_mac(data, reqData, attr->mac, (u8 *) "", 0)) { 877189251Ssam wpa_printf(MSG_WARNING, "EAP-AKA: Challenge message " 878189251Ssam "used invalid AT_MAC"); 879189251Ssam return eap_aka_client_error(data, id, 880189251Ssam EAP_AKA_UNABLE_TO_PROCESS_PACKET); 881189251Ssam } 882189251Ssam 883189251Ssam /* Old reauthentication and pseudonym identities must not be used 884189251Ssam * anymore. In other words, if no new identities are received, full 885189251Ssam * authentication will be used on next reauthentication. */ 886189251Ssam eap_aka_clear_identities(data, CLEAR_PSEUDONYM | CLEAR_REAUTH_ID | 887189251Ssam CLEAR_EAP_ID); 888189251Ssam 889189251Ssam if (attr->encr_data) { 890189251Ssam u8 *decrypted; 891189251Ssam decrypted = eap_sim_parse_encr(data->k_encr, attr->encr_data, 892189251Ssam attr->encr_data_len, attr->iv, 893189251Ssam &eattr, 0); 894189251Ssam if (decrypted == NULL) { 895189251Ssam return eap_aka_client_error( 896189251Ssam data, id, EAP_AKA_UNABLE_TO_PROCESS_PACKET); 897189251Ssam } 898189251Ssam eap_aka_learn_ids(data, &eattr); 899189251Ssam os_free(decrypted); 900189251Ssam } 901189251Ssam 902189251Ssam if (data->result_ind && attr->result_ind) 903189251Ssam data->use_result_ind = 1; 904189251Ssam 905189251Ssam if (data->state != FAILURE && data->state != RESULT_FAILURE) { 906189251Ssam eap_aka_state(data, data->use_result_ind ? 907189251Ssam RESULT_SUCCESS : SUCCESS); 908189251Ssam } 909189251Ssam 910189251Ssam data->num_id_req = 0; 911189251Ssam data->num_notification = 0; 912189251Ssam /* RFC 4187 specifies that counter is initialized to one after 913189251Ssam * fullauth, but initializing it to zero makes it easier to implement 914189251Ssam * reauth verification. */ 915189251Ssam data->counter = 0; 916189251Ssam return eap_aka_response_challenge(data, id); 917189251Ssam} 918189251Ssam 919189251Ssam 920189251Ssamstatic int eap_aka_process_notification_reauth(struct eap_aka_data *data, 921189251Ssam struct eap_sim_attrs *attr) 922189251Ssam{ 923189251Ssam struct eap_sim_attrs eattr; 924189251Ssam u8 *decrypted; 925189251Ssam 926189251Ssam if (attr->encr_data == NULL || attr->iv == NULL) { 927189251Ssam wpa_printf(MSG_WARNING, "EAP-AKA: Notification message after " 928189251Ssam "reauth did not include encrypted data"); 929189251Ssam return -1; 930189251Ssam } 931189251Ssam 932189251Ssam decrypted = eap_sim_parse_encr(data->k_encr, attr->encr_data, 933189251Ssam attr->encr_data_len, attr->iv, &eattr, 934189251Ssam 0); 935189251Ssam if (decrypted == NULL) { 936189251Ssam wpa_printf(MSG_WARNING, "EAP-AKA: Failed to parse encrypted " 937189251Ssam "data from notification message"); 938189251Ssam return -1; 939189251Ssam } 940189251Ssam 941189251Ssam if (eattr.counter < 0 || (size_t) eattr.counter != data->counter) { 942189251Ssam wpa_printf(MSG_WARNING, "EAP-AKA: Counter in notification " 943189251Ssam "message does not match with counter in reauth " 944189251Ssam "message"); 945189251Ssam os_free(decrypted); 946189251Ssam return -1; 947189251Ssam } 948189251Ssam 949189251Ssam os_free(decrypted); 950189251Ssam return 0; 951189251Ssam} 952189251Ssam 953189251Ssam 954189251Ssamstatic int eap_aka_process_notification_auth(struct eap_aka_data *data, 955189251Ssam const struct wpabuf *reqData, 956189251Ssam struct eap_sim_attrs *attr) 957189251Ssam{ 958189251Ssam if (attr->mac == NULL) { 959189251Ssam wpa_printf(MSG_INFO, "EAP-AKA: no AT_MAC in after_auth " 960189251Ssam "Notification message"); 961189251Ssam return -1; 962189251Ssam } 963189251Ssam 964189251Ssam if (eap_aka_verify_mac(data, reqData, attr->mac, (u8 *) "", 0)) { 965189251Ssam wpa_printf(MSG_WARNING, "EAP-AKA: Notification message " 966189251Ssam "used invalid AT_MAC"); 967189251Ssam return -1; 968189251Ssam } 969189251Ssam 970189251Ssam if (data->reauth && 971189251Ssam eap_aka_process_notification_reauth(data, attr)) { 972189251Ssam wpa_printf(MSG_WARNING, "EAP-AKA: Invalid notification " 973189251Ssam "message after reauth"); 974189251Ssam return -1; 975189251Ssam } 976189251Ssam 977189251Ssam return 0; 978189251Ssam} 979189251Ssam 980189251Ssam 981189251Ssamstatic struct wpabuf * eap_aka_process_notification( 982189251Ssam struct eap_sm *sm, struct eap_aka_data *data, u8 id, 983189251Ssam const struct wpabuf *reqData, struct eap_sim_attrs *attr) 984189251Ssam{ 985189251Ssam wpa_printf(MSG_DEBUG, "EAP-AKA: subtype Notification"); 986189251Ssam if (data->num_notification > 0) { 987189251Ssam wpa_printf(MSG_INFO, "EAP-AKA: too many notification " 988189251Ssam "rounds (only one allowed)"); 989189251Ssam return eap_aka_client_error(data, id, 990189251Ssam EAP_AKA_UNABLE_TO_PROCESS_PACKET); 991189251Ssam } 992189251Ssam data->num_notification++; 993189251Ssam if (attr->notification == -1) { 994189251Ssam wpa_printf(MSG_INFO, "EAP-AKA: no AT_NOTIFICATION in " 995189251Ssam "Notification message"); 996189251Ssam return eap_aka_client_error(data, id, 997189251Ssam EAP_AKA_UNABLE_TO_PROCESS_PACKET); 998189251Ssam } 999189251Ssam 1000189251Ssam if ((attr->notification & 0x4000) == 0 && 1001189251Ssam eap_aka_process_notification_auth(data, reqData, attr)) { 1002189251Ssam return eap_aka_client_error(data, id, 1003189251Ssam EAP_AKA_UNABLE_TO_PROCESS_PACKET); 1004189251Ssam } 1005189251Ssam 1006189251Ssam eap_sim_report_notification(sm->msg_ctx, attr->notification, 1); 1007189251Ssam if (attr->notification >= 0 && attr->notification < 32768) { 1008189251Ssam eap_aka_state(data, FAILURE); 1009189251Ssam } else if (attr->notification == EAP_SIM_SUCCESS && 1010189251Ssam data->state == RESULT_SUCCESS) 1011189251Ssam eap_aka_state(data, SUCCESS); 1012189251Ssam return eap_aka_response_notification(data, id, attr->notification); 1013189251Ssam} 1014189251Ssam 1015189251Ssam 1016189251Ssamstatic struct wpabuf * eap_aka_process_reauthentication( 1017189251Ssam struct eap_sm *sm, struct eap_aka_data *data, u8 id, 1018189251Ssam const struct wpabuf *reqData, struct eap_sim_attrs *attr) 1019189251Ssam{ 1020189251Ssam struct eap_sim_attrs eattr; 1021189251Ssam u8 *decrypted; 1022189251Ssam 1023189251Ssam wpa_printf(MSG_DEBUG, "EAP-AKA: subtype Reauthentication"); 1024189251Ssam 1025189251Ssam if (attr->checkcode && 1026189251Ssam eap_aka_verify_checkcode(data, attr->checkcode, 1027189251Ssam attr->checkcode_len)) { 1028189251Ssam wpa_printf(MSG_WARNING, "EAP-AKA: Invalid AT_CHECKCODE in the " 1029189251Ssam "message"); 1030189251Ssam return eap_aka_client_error(data, id, 1031189251Ssam EAP_AKA_UNABLE_TO_PROCESS_PACKET); 1032189251Ssam } 1033189251Ssam 1034189251Ssam if (data->reauth_id == NULL) { 1035189251Ssam wpa_printf(MSG_WARNING, "EAP-AKA: Server is trying " 1036189251Ssam "reauthentication, but no reauth_id available"); 1037189251Ssam return eap_aka_client_error(data, id, 1038189251Ssam EAP_AKA_UNABLE_TO_PROCESS_PACKET); 1039189251Ssam } 1040189251Ssam 1041189251Ssam data->reauth = 1; 1042189251Ssam if (eap_aka_verify_mac(data, reqData, attr->mac, (u8 *) "", 0)) { 1043189251Ssam wpa_printf(MSG_WARNING, "EAP-AKA: Reauthentication " 1044189251Ssam "did not have valid AT_MAC"); 1045189251Ssam return eap_aka_client_error(data, id, 1046189251Ssam EAP_AKA_UNABLE_TO_PROCESS_PACKET); 1047189251Ssam } 1048189251Ssam 1049189251Ssam if (attr->encr_data == NULL || attr->iv == NULL) { 1050189251Ssam wpa_printf(MSG_WARNING, "EAP-AKA: Reauthentication " 1051189251Ssam "message did not include encrypted data"); 1052189251Ssam return eap_aka_client_error(data, id, 1053189251Ssam EAP_AKA_UNABLE_TO_PROCESS_PACKET); 1054189251Ssam } 1055189251Ssam 1056189251Ssam decrypted = eap_sim_parse_encr(data->k_encr, attr->encr_data, 1057189251Ssam attr->encr_data_len, attr->iv, &eattr, 1058189251Ssam 0); 1059189251Ssam if (decrypted == NULL) { 1060189251Ssam wpa_printf(MSG_WARNING, "EAP-AKA: Failed to parse encrypted " 1061189251Ssam "data from reauthentication message"); 1062189251Ssam return eap_aka_client_error(data, id, 1063189251Ssam EAP_AKA_UNABLE_TO_PROCESS_PACKET); 1064189251Ssam } 1065189251Ssam 1066189251Ssam if (eattr.nonce_s == NULL || eattr.counter < 0) { 1067189251Ssam wpa_printf(MSG_INFO, "EAP-AKA: (encr) No%s%s in reauth packet", 1068189251Ssam !eattr.nonce_s ? " AT_NONCE_S" : "", 1069189251Ssam eattr.counter < 0 ? " AT_COUNTER" : ""); 1070189251Ssam os_free(decrypted); 1071189251Ssam return eap_aka_client_error(data, id, 1072189251Ssam EAP_AKA_UNABLE_TO_PROCESS_PACKET); 1073189251Ssam } 1074189251Ssam 1075189251Ssam if (eattr.counter < 0 || (size_t) eattr.counter <= data->counter) { 1076189251Ssam struct wpabuf *res; 1077189251Ssam wpa_printf(MSG_INFO, "EAP-AKA: (encr) Invalid counter " 1078189251Ssam "(%d <= %d)", eattr.counter, data->counter); 1079189251Ssam data->counter_too_small = eattr.counter; 1080189251Ssam 1081189251Ssam /* Reply using Re-auth w/ AT_COUNTER_TOO_SMALL. The current 1082189251Ssam * reauth_id must not be used to start a new reauthentication. 1083189251Ssam * However, since it was used in the last EAP-Response-Identity 1084189251Ssam * packet, it has to saved for the following fullauth to be 1085189251Ssam * used in MK derivation. */ 1086189251Ssam os_free(data->last_eap_identity); 1087189251Ssam data->last_eap_identity = data->reauth_id; 1088189251Ssam data->last_eap_identity_len = data->reauth_id_len; 1089189251Ssam data->reauth_id = NULL; 1090189251Ssam data->reauth_id_len = 0; 1091189251Ssam 1092189251Ssam res = eap_aka_response_reauth(data, id, 1, eattr.nonce_s); 1093189251Ssam os_free(decrypted); 1094189251Ssam 1095189251Ssam return res; 1096189251Ssam } 1097189251Ssam data->counter = eattr.counter; 1098189251Ssam 1099189251Ssam os_memcpy(data->nonce_s, eattr.nonce_s, EAP_SIM_NONCE_S_LEN); 1100189251Ssam wpa_hexdump(MSG_DEBUG, "EAP-AKA: (encr) AT_NONCE_S", 1101189251Ssam data->nonce_s, EAP_SIM_NONCE_S_LEN); 1102189251Ssam 1103189251Ssam if (data->eap_method == EAP_TYPE_AKA_PRIME) { 1104189251Ssam eap_aka_prime_derive_keys_reauth(data->k_re, data->counter, 1105189251Ssam data->reauth_id, 1106189251Ssam data->reauth_id_len, 1107189251Ssam data->nonce_s, 1108189251Ssam data->msk, data->emsk); 1109189251Ssam } else { 1110189251Ssam eap_sim_derive_keys_reauth(data->counter, data->reauth_id, 1111189251Ssam data->reauth_id_len, 1112189251Ssam data->nonce_s, data->mk, 1113189251Ssam data->msk, data->emsk); 1114189251Ssam } 1115189251Ssam eap_aka_clear_identities(data, CLEAR_REAUTH_ID | CLEAR_EAP_ID); 1116189251Ssam eap_aka_learn_ids(data, &eattr); 1117189251Ssam 1118189251Ssam if (data->result_ind && attr->result_ind) 1119189251Ssam data->use_result_ind = 1; 1120189251Ssam 1121189251Ssam if (data->state != FAILURE && data->state != RESULT_FAILURE) { 1122189251Ssam eap_aka_state(data, data->use_result_ind ? 1123189251Ssam RESULT_SUCCESS : SUCCESS); 1124189251Ssam } 1125189251Ssam 1126189251Ssam data->num_id_req = 0; 1127189251Ssam data->num_notification = 0; 1128189251Ssam if (data->counter > EAP_AKA_MAX_FAST_REAUTHS) { 1129189251Ssam wpa_printf(MSG_DEBUG, "EAP-AKA: Maximum number of " 1130189251Ssam "fast reauths performed - force fullauth"); 1131189251Ssam eap_aka_clear_identities(data, CLEAR_REAUTH_ID | CLEAR_EAP_ID); 1132189251Ssam } 1133189251Ssam os_free(decrypted); 1134189251Ssam return eap_aka_response_reauth(data, id, 0, data->nonce_s); 1135189251Ssam} 1136189251Ssam 1137189251Ssam 1138189251Ssamstatic struct wpabuf * eap_aka_process(struct eap_sm *sm, void *priv, 1139189251Ssam struct eap_method_ret *ret, 1140189251Ssam const struct wpabuf *reqData) 1141189251Ssam{ 1142189251Ssam struct eap_aka_data *data = priv; 1143189251Ssam const struct eap_hdr *req; 1144189251Ssam u8 subtype, id; 1145189251Ssam struct wpabuf *res; 1146189251Ssam const u8 *pos; 1147189251Ssam struct eap_sim_attrs attr; 1148189251Ssam size_t len; 1149189251Ssam 1150189251Ssam wpa_hexdump_buf(MSG_DEBUG, "EAP-AKA: EAP data", reqData); 1151189251Ssam if (eap_get_config_identity(sm, &len) == NULL) { 1152189251Ssam wpa_printf(MSG_INFO, "EAP-AKA: Identity not configured"); 1153189251Ssam eap_sm_request_identity(sm); 1154189251Ssam ret->ignore = TRUE; 1155189251Ssam return NULL; 1156189251Ssam } 1157189251Ssam 1158189251Ssam pos = eap_hdr_validate(EAP_VENDOR_IETF, data->eap_method, reqData, 1159189251Ssam &len); 1160189251Ssam if (pos == NULL || len < 1) { 1161189251Ssam ret->ignore = TRUE; 1162189251Ssam return NULL; 1163189251Ssam } 1164189251Ssam req = wpabuf_head(reqData); 1165189251Ssam id = req->identifier; 1166189251Ssam len = be_to_host16(req->length); 1167189251Ssam 1168189251Ssam ret->ignore = FALSE; 1169189251Ssam ret->methodState = METHOD_MAY_CONT; 1170189251Ssam ret->decision = DECISION_FAIL; 1171189251Ssam ret->allowNotifications = TRUE; 1172189251Ssam 1173189251Ssam subtype = *pos++; 1174189251Ssam wpa_printf(MSG_DEBUG, "EAP-AKA: Subtype=%d", subtype); 1175189251Ssam pos += 2; /* Reserved */ 1176189251Ssam 1177189251Ssam if (eap_sim_parse_attr(pos, wpabuf_head_u8(reqData) + len, &attr, 1178189251Ssam data->eap_method == EAP_TYPE_AKA_PRIME ? 2 : 1, 1179189251Ssam 0)) { 1180189251Ssam res = eap_aka_client_error(data, id, 1181189251Ssam EAP_AKA_UNABLE_TO_PROCESS_PACKET); 1182189251Ssam goto done; 1183189251Ssam } 1184189251Ssam 1185189251Ssam switch (subtype) { 1186189251Ssam case EAP_AKA_SUBTYPE_IDENTITY: 1187189251Ssam res = eap_aka_process_identity(sm, data, id, reqData, &attr); 1188189251Ssam break; 1189189251Ssam case EAP_AKA_SUBTYPE_CHALLENGE: 1190189251Ssam res = eap_aka_process_challenge(sm, data, id, reqData, &attr); 1191189251Ssam break; 1192189251Ssam case EAP_AKA_SUBTYPE_NOTIFICATION: 1193189251Ssam res = eap_aka_process_notification(sm, data, id, reqData, 1194189251Ssam &attr); 1195189251Ssam break; 1196189251Ssam case EAP_AKA_SUBTYPE_REAUTHENTICATION: 1197189251Ssam res = eap_aka_process_reauthentication(sm, data, id, reqData, 1198189251Ssam &attr); 1199189251Ssam break; 1200189251Ssam case EAP_AKA_SUBTYPE_CLIENT_ERROR: 1201189251Ssam wpa_printf(MSG_DEBUG, "EAP-AKA: subtype Client-Error"); 1202189251Ssam res = eap_aka_client_error(data, id, 1203189251Ssam EAP_AKA_UNABLE_TO_PROCESS_PACKET); 1204189251Ssam break; 1205189251Ssam default: 1206189251Ssam wpa_printf(MSG_DEBUG, "EAP-AKA: Unknown subtype=%d", subtype); 1207189251Ssam res = eap_aka_client_error(data, id, 1208189251Ssam EAP_AKA_UNABLE_TO_PROCESS_PACKET); 1209189251Ssam break; 1210189251Ssam } 1211189251Ssam 1212189251Ssamdone: 1213189251Ssam if (data->state == FAILURE) { 1214189251Ssam ret->decision = DECISION_FAIL; 1215189251Ssam ret->methodState = METHOD_DONE; 1216189251Ssam } else if (data->state == SUCCESS) { 1217189251Ssam ret->decision = data->use_result_ind ? 1218189251Ssam DECISION_UNCOND_SUCC : DECISION_COND_SUCC; 1219189251Ssam /* 1220189251Ssam * It is possible for the server to reply with AKA 1221189251Ssam * Notification, so we must allow the method to continue and 1222189251Ssam * not only accept EAP-Success at this point. 1223189251Ssam */ 1224189251Ssam ret->methodState = data->use_result_ind ? 1225189251Ssam METHOD_DONE : METHOD_MAY_CONT; 1226189251Ssam } else if (data->state == RESULT_FAILURE) 1227189251Ssam ret->methodState = METHOD_CONT; 1228189251Ssam else if (data->state == RESULT_SUCCESS) 1229189251Ssam ret->methodState = METHOD_CONT; 1230189251Ssam 1231189251Ssam if (ret->methodState == METHOD_DONE) { 1232189251Ssam ret->allowNotifications = FALSE; 1233189251Ssam } 1234189251Ssam 1235189251Ssam return res; 1236189251Ssam} 1237189251Ssam 1238189251Ssam 1239189251Ssamstatic Boolean eap_aka_has_reauth_data(struct eap_sm *sm, void *priv) 1240189251Ssam{ 1241189251Ssam struct eap_aka_data *data = priv; 1242189251Ssam return data->pseudonym || data->reauth_id; 1243189251Ssam} 1244189251Ssam 1245189251Ssam 1246189251Ssamstatic void eap_aka_deinit_for_reauth(struct eap_sm *sm, void *priv) 1247189251Ssam{ 1248189251Ssam struct eap_aka_data *data = priv; 1249189251Ssam eap_aka_clear_identities(data, CLEAR_EAP_ID); 1250189251Ssam data->prev_id = -1; 1251189251Ssam wpabuf_free(data->id_msgs); 1252189251Ssam data->id_msgs = NULL; 1253189251Ssam data->use_result_ind = 0; 1254189251Ssam data->kdf_negotiation = 0; 1255189251Ssam} 1256189251Ssam 1257189251Ssam 1258189251Ssamstatic void * eap_aka_init_for_reauth(struct eap_sm *sm, void *priv) 1259189251Ssam{ 1260189251Ssam struct eap_aka_data *data = priv; 1261189251Ssam data->num_id_req = 0; 1262189251Ssam data->num_notification = 0; 1263189251Ssam eap_aka_state(data, CONTINUE); 1264189251Ssam return priv; 1265189251Ssam} 1266189251Ssam 1267189251Ssam 1268189251Ssamstatic const u8 * eap_aka_get_identity(struct eap_sm *sm, void *priv, 1269189251Ssam size_t *len) 1270189251Ssam{ 1271189251Ssam struct eap_aka_data *data = priv; 1272189251Ssam 1273189251Ssam if (data->reauth_id) { 1274189251Ssam *len = data->reauth_id_len; 1275189251Ssam return data->reauth_id; 1276189251Ssam } 1277189251Ssam 1278189251Ssam if (data->pseudonym) { 1279189251Ssam *len = data->pseudonym_len; 1280189251Ssam return data->pseudonym; 1281189251Ssam } 1282189251Ssam 1283189251Ssam return NULL; 1284189251Ssam} 1285189251Ssam 1286189251Ssam 1287189251Ssamstatic Boolean eap_aka_isKeyAvailable(struct eap_sm *sm, void *priv) 1288189251Ssam{ 1289189251Ssam struct eap_aka_data *data = priv; 1290189251Ssam return data->state == SUCCESS; 1291189251Ssam} 1292189251Ssam 1293189251Ssam 1294189251Ssamstatic u8 * eap_aka_getKey(struct eap_sm *sm, void *priv, size_t *len) 1295189251Ssam{ 1296189251Ssam struct eap_aka_data *data = priv; 1297189251Ssam u8 *key; 1298189251Ssam 1299189251Ssam if (data->state != SUCCESS) 1300189251Ssam return NULL; 1301189251Ssam 1302189251Ssam key = os_malloc(EAP_SIM_KEYING_DATA_LEN); 1303189251Ssam if (key == NULL) 1304189251Ssam return NULL; 1305189251Ssam 1306189251Ssam *len = EAP_SIM_KEYING_DATA_LEN; 1307189251Ssam os_memcpy(key, data->msk, EAP_SIM_KEYING_DATA_LEN); 1308189251Ssam 1309189251Ssam return key; 1310189251Ssam} 1311189251Ssam 1312189251Ssam 1313189251Ssamstatic u8 * eap_aka_get_emsk(struct eap_sm *sm, void *priv, size_t *len) 1314189251Ssam{ 1315189251Ssam struct eap_aka_data *data = priv; 1316189251Ssam u8 *key; 1317189251Ssam 1318189251Ssam if (data->state != SUCCESS) 1319189251Ssam return NULL; 1320189251Ssam 1321189251Ssam key = os_malloc(EAP_EMSK_LEN); 1322189251Ssam if (key == NULL) 1323189251Ssam return NULL; 1324189251Ssam 1325189251Ssam *len = EAP_EMSK_LEN; 1326189251Ssam os_memcpy(key, data->emsk, EAP_EMSK_LEN); 1327189251Ssam 1328189251Ssam return key; 1329189251Ssam} 1330189251Ssam 1331189251Ssam 1332189251Ssamint eap_peer_aka_register(void) 1333189251Ssam{ 1334189251Ssam struct eap_method *eap; 1335189251Ssam int ret; 1336189251Ssam 1337189251Ssam eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION, 1338189251Ssam EAP_VENDOR_IETF, EAP_TYPE_AKA, "AKA"); 1339189251Ssam if (eap == NULL) 1340189251Ssam return -1; 1341189251Ssam 1342189251Ssam eap->init = eap_aka_init; 1343189251Ssam eap->deinit = eap_aka_deinit; 1344189251Ssam eap->process = eap_aka_process; 1345189251Ssam eap->isKeyAvailable = eap_aka_isKeyAvailable; 1346189251Ssam eap->getKey = eap_aka_getKey; 1347189251Ssam eap->has_reauth_data = eap_aka_has_reauth_data; 1348189251Ssam eap->deinit_for_reauth = eap_aka_deinit_for_reauth; 1349189251Ssam eap->init_for_reauth = eap_aka_init_for_reauth; 1350189251Ssam eap->get_identity = eap_aka_get_identity; 1351189251Ssam eap->get_emsk = eap_aka_get_emsk; 1352189251Ssam 1353189251Ssam ret = eap_peer_method_register(eap); 1354189251Ssam if (ret) 1355189251Ssam eap_peer_method_free(eap); 1356189251Ssam return ret; 1357189251Ssam} 1358189251Ssam 1359189251Ssam 1360189251Ssam#ifdef EAP_AKA_PRIME 1361189251Ssamint eap_peer_aka_prime_register(void) 1362189251Ssam{ 1363189251Ssam struct eap_method *eap; 1364189251Ssam int ret; 1365189251Ssam 1366189251Ssam eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION, 1367189251Ssam EAP_VENDOR_IETF, EAP_TYPE_AKA_PRIME, 1368189251Ssam "AKA'"); 1369189251Ssam if (eap == NULL) 1370189251Ssam return -1; 1371189251Ssam 1372189251Ssam eap->init = eap_aka_prime_init; 1373189251Ssam eap->deinit = eap_aka_deinit; 1374189251Ssam eap->process = eap_aka_process; 1375189251Ssam eap->isKeyAvailable = eap_aka_isKeyAvailable; 1376189251Ssam eap->getKey = eap_aka_getKey; 1377189251Ssam eap->has_reauth_data = eap_aka_has_reauth_data; 1378189251Ssam eap->deinit_for_reauth = eap_aka_deinit_for_reauth; 1379189251Ssam eap->init_for_reauth = eap_aka_init_for_reauth; 1380189251Ssam eap->get_identity = eap_aka_get_identity; 1381189251Ssam eap->get_emsk = eap_aka_get_emsk; 1382189251Ssam 1383189251Ssam ret = eap_peer_method_register(eap); 1384189251Ssam if (ret) 1385189251Ssam eap_peer_method_free(eap); 1386189251Ssam 1387189251Ssam return ret; 1388189251Ssam} 1389189251Ssam#endif /* EAP_AKA_PRIME */ 1390