1189251Ssam/* 2189251Ssam * hostapd / EAP-SIM database/authenticator gateway 3252726Srpaulo * Copyright (c) 2005-2010, 2012, Jouni Malinen <j@w1.fi> 4189251Ssam * 5252726Srpaulo * This software may be distributed under the terms of the BSD license. 6252726Srpaulo * See README for more details. 7189251Ssam * 8189251Ssam * This is an example implementation of the EAP-SIM/AKA database/authentication 9189251Ssam * gateway interface that is using an external program as an SS7 gateway to 10189251Ssam * GSM/UMTS authentication center (HLR/AuC). hlr_auc_gw is an example 11189251Ssam * implementation of such a gateway program. This eap_sim_db.c takes care of 12189251Ssam * EAP-SIM/AKA pseudonyms and re-auth identities. It can be used with different 13189251Ssam * gateway implementations for HLR/AuC access. Alternatively, it can also be 14189251Ssam * completely replaced if the in-memory database of pseudonyms/re-auth 15189251Ssam * identities is not suitable for some cases. 16189251Ssam */ 17189251Ssam 18189251Ssam#include "includes.h" 19189251Ssam#include <sys/un.h> 20252726Srpaulo#ifdef CONFIG_SQLITE 21252726Srpaulo#include <sqlite3.h> 22252726Srpaulo#endif /* CONFIG_SQLITE */ 23189251Ssam 24189251Ssam#include "common.h" 25252726Srpaulo#include "crypto/random.h" 26189251Ssam#include "eap_common/eap_sim_common.h" 27189251Ssam#include "eap_server/eap_sim_db.h" 28189251Ssam#include "eloop.h" 29189251Ssam 30189251Ssamstruct eap_sim_pseudonym { 31189251Ssam struct eap_sim_pseudonym *next; 32252726Srpaulo char *permanent; /* permanent username */ 33252726Srpaulo char *pseudonym; /* pseudonym username */ 34189251Ssam}; 35189251Ssam 36189251Ssamstruct eap_sim_db_pending { 37189251Ssam struct eap_sim_db_pending *next; 38252726Srpaulo char imsi[20]; 39189251Ssam enum { PENDING, SUCCESS, FAILURE } state; 40189251Ssam void *cb_session_ctx; 41189251Ssam struct os_time timestamp; 42189251Ssam int aka; 43189251Ssam union { 44189251Ssam struct { 45189251Ssam u8 kc[EAP_SIM_MAX_CHAL][EAP_SIM_KC_LEN]; 46189251Ssam u8 sres[EAP_SIM_MAX_CHAL][EAP_SIM_SRES_LEN]; 47189251Ssam u8 rand[EAP_SIM_MAX_CHAL][GSM_RAND_LEN]; 48189251Ssam int num_chal; 49189251Ssam } sim; 50189251Ssam struct { 51189251Ssam u8 rand[EAP_AKA_RAND_LEN]; 52189251Ssam u8 autn[EAP_AKA_AUTN_LEN]; 53189251Ssam u8 ik[EAP_AKA_IK_LEN]; 54189251Ssam u8 ck[EAP_AKA_CK_LEN]; 55189251Ssam u8 res[EAP_AKA_RES_MAX_LEN]; 56189251Ssam size_t res_len; 57189251Ssam } aka; 58189251Ssam } u; 59189251Ssam}; 60189251Ssam 61189251Ssamstruct eap_sim_db_data { 62189251Ssam int sock; 63189251Ssam char *fname; 64189251Ssam char *local_sock; 65189251Ssam void (*get_complete_cb)(void *ctx, void *session_ctx); 66189251Ssam void *ctx; 67189251Ssam struct eap_sim_pseudonym *pseudonyms; 68189251Ssam struct eap_sim_reauth *reauths; 69189251Ssam struct eap_sim_db_pending *pending; 70252726Srpaulo#ifdef CONFIG_SQLITE 71252726Srpaulo sqlite3 *sqlite_db; 72252726Srpaulo char db_tmp_identity[100]; 73252726Srpaulo char db_tmp_pseudonym_str[100]; 74252726Srpaulo struct eap_sim_pseudonym db_tmp_pseudonym; 75252726Srpaulo struct eap_sim_reauth db_tmp_reauth; 76252726Srpaulo#endif /* CONFIG_SQLITE */ 77189251Ssam}; 78189251Ssam 79189251Ssam 80252726Srpaulo#ifdef CONFIG_SQLITE 81252726Srpaulo 82252726Srpaulostatic int db_table_exists(sqlite3 *db, const char *name) 83252726Srpaulo{ 84252726Srpaulo char cmd[128]; 85252726Srpaulo os_snprintf(cmd, sizeof(cmd), "SELECT 1 FROM %s;", name); 86252726Srpaulo return sqlite3_exec(db, cmd, NULL, NULL, NULL) == SQLITE_OK; 87252726Srpaulo} 88252726Srpaulo 89252726Srpaulo 90252726Srpaulostatic int db_table_create_pseudonym(sqlite3 *db) 91252726Srpaulo{ 92252726Srpaulo char *err = NULL; 93252726Srpaulo const char *sql = 94252726Srpaulo "CREATE TABLE pseudonyms(" 95252726Srpaulo " permanent CHAR(21) PRIMARY KEY," 96252726Srpaulo " pseudonym CHAR(21) NOT NULL" 97252726Srpaulo ");"; 98252726Srpaulo 99252726Srpaulo wpa_printf(MSG_DEBUG, "EAP-SIM DB: Adding database table for " 100252726Srpaulo "pseudonym information"); 101252726Srpaulo if (sqlite3_exec(db, sql, NULL, NULL, &err) != SQLITE_OK) { 102252726Srpaulo wpa_printf(MSG_ERROR, "EAP-SIM DB: SQLite error: %s", err); 103252726Srpaulo sqlite3_free(err); 104252726Srpaulo return -1; 105252726Srpaulo } 106252726Srpaulo 107252726Srpaulo return 0; 108252726Srpaulo} 109252726Srpaulo 110252726Srpaulo 111252726Srpaulostatic int db_table_create_reauth(sqlite3 *db) 112252726Srpaulo{ 113252726Srpaulo char *err = NULL; 114252726Srpaulo const char *sql = 115252726Srpaulo "CREATE TABLE reauth(" 116252726Srpaulo " permanent CHAR(21) PRIMARY KEY," 117252726Srpaulo " reauth_id CHAR(21) NOT NULL," 118252726Srpaulo " counter INTEGER," 119252726Srpaulo " mk CHAR(40)," 120252726Srpaulo " k_encr CHAR(32)," 121252726Srpaulo " k_aut CHAR(64)," 122252726Srpaulo " k_re CHAR(64)" 123252726Srpaulo ");"; 124252726Srpaulo 125252726Srpaulo wpa_printf(MSG_DEBUG, "EAP-SIM DB: Adding database table for " 126252726Srpaulo "reauth information"); 127252726Srpaulo if (sqlite3_exec(db, sql, NULL, NULL, &err) != SQLITE_OK) { 128252726Srpaulo wpa_printf(MSG_ERROR, "EAP-SIM DB: SQLite error: %s", err); 129252726Srpaulo sqlite3_free(err); 130252726Srpaulo return -1; 131252726Srpaulo } 132252726Srpaulo 133252726Srpaulo return 0; 134252726Srpaulo} 135252726Srpaulo 136252726Srpaulo 137252726Srpaulostatic sqlite3 * db_open(const char *db_file) 138252726Srpaulo{ 139252726Srpaulo sqlite3 *db; 140252726Srpaulo 141252726Srpaulo if (sqlite3_open(db_file, &db)) { 142252726Srpaulo wpa_printf(MSG_ERROR, "EAP-SIM DB: Failed to open database " 143252726Srpaulo "%s: %s", db_file, sqlite3_errmsg(db)); 144252726Srpaulo sqlite3_close(db); 145252726Srpaulo return NULL; 146252726Srpaulo } 147252726Srpaulo 148252726Srpaulo if (!db_table_exists(db, "pseudonyms") && 149252726Srpaulo db_table_create_pseudonym(db) < 0) { 150252726Srpaulo sqlite3_close(db); 151252726Srpaulo return NULL; 152252726Srpaulo } 153252726Srpaulo 154252726Srpaulo if (!db_table_exists(db, "reauth") && 155252726Srpaulo db_table_create_reauth(db) < 0) { 156252726Srpaulo sqlite3_close(db); 157252726Srpaulo return NULL; 158252726Srpaulo } 159252726Srpaulo 160252726Srpaulo return db; 161252726Srpaulo} 162252726Srpaulo 163252726Srpaulo 164252726Srpaulostatic int valid_db_string(const char *str) 165252726Srpaulo{ 166252726Srpaulo const char *pos = str; 167252726Srpaulo while (*pos) { 168252726Srpaulo if ((*pos < '0' || *pos > '9') && 169252726Srpaulo (*pos < 'a' || *pos > 'f')) 170252726Srpaulo return 0; 171252726Srpaulo pos++; 172252726Srpaulo } 173252726Srpaulo return 1; 174252726Srpaulo} 175252726Srpaulo 176252726Srpaulo 177252726Srpaulostatic int db_add_pseudonym(struct eap_sim_db_data *data, 178252726Srpaulo const char *permanent, char *pseudonym) 179252726Srpaulo{ 180252726Srpaulo char cmd[128]; 181252726Srpaulo char *err = NULL; 182252726Srpaulo 183252726Srpaulo if (!valid_db_string(permanent) || !valid_db_string(pseudonym)) { 184252726Srpaulo os_free(pseudonym); 185252726Srpaulo return -1; 186252726Srpaulo } 187252726Srpaulo 188252726Srpaulo os_snprintf(cmd, sizeof(cmd), "INSERT OR REPLACE INTO pseudonyms " 189252726Srpaulo "(permanent, pseudonym) VALUES ('%s', '%s');", 190252726Srpaulo permanent, pseudonym); 191252726Srpaulo os_free(pseudonym); 192252726Srpaulo if (sqlite3_exec(data->sqlite_db, cmd, NULL, NULL, &err) != SQLITE_OK) 193252726Srpaulo { 194252726Srpaulo wpa_printf(MSG_ERROR, "EAP-SIM DB: SQLite error: %s", err); 195252726Srpaulo sqlite3_free(err); 196252726Srpaulo return -1; 197252726Srpaulo } 198252726Srpaulo 199252726Srpaulo return 0; 200252726Srpaulo} 201252726Srpaulo 202252726Srpaulo 203252726Srpaulostatic int get_pseudonym_cb(void *ctx, int argc, char *argv[], char *col[]) 204252726Srpaulo{ 205252726Srpaulo struct eap_sim_db_data *data = ctx; 206252726Srpaulo int i; 207252726Srpaulo 208252726Srpaulo for (i = 0; i < argc; i++) { 209252726Srpaulo if (os_strcmp(col[i], "permanent") == 0 && argv[i]) { 210252726Srpaulo os_strlcpy(data->db_tmp_identity, argv[i], 211252726Srpaulo sizeof(data->db_tmp_identity)); 212252726Srpaulo } 213252726Srpaulo } 214252726Srpaulo 215252726Srpaulo return 0; 216252726Srpaulo} 217252726Srpaulo 218252726Srpaulo 219252726Srpaulostatic char * 220252726Srpaulodb_get_pseudonym(struct eap_sim_db_data *data, const char *pseudonym) 221252726Srpaulo{ 222252726Srpaulo char cmd[128]; 223252726Srpaulo 224252726Srpaulo if (!valid_db_string(pseudonym)) 225252726Srpaulo return NULL; 226252726Srpaulo os_memset(&data->db_tmp_identity, 0, sizeof(data->db_tmp_identity)); 227252726Srpaulo os_snprintf(cmd, sizeof(cmd), 228252726Srpaulo "SELECT permanent FROM pseudonyms WHERE pseudonym='%s';", 229252726Srpaulo pseudonym); 230252726Srpaulo if (sqlite3_exec(data->sqlite_db, cmd, get_pseudonym_cb, data, NULL) != 231252726Srpaulo SQLITE_OK) 232252726Srpaulo return NULL; 233252726Srpaulo if (data->db_tmp_identity[0] == '\0') 234252726Srpaulo return NULL; 235252726Srpaulo return data->db_tmp_identity; 236252726Srpaulo} 237252726Srpaulo 238252726Srpaulo 239252726Srpaulostatic int db_add_reauth(struct eap_sim_db_data *data, const char *permanent, 240252726Srpaulo char *reauth_id, u16 counter, const u8 *mk, 241252726Srpaulo const u8 *k_encr, const u8 *k_aut, const u8 *k_re) 242252726Srpaulo{ 243252726Srpaulo char cmd[2000], *pos, *end; 244252726Srpaulo char *err = NULL; 245252726Srpaulo 246252726Srpaulo if (!valid_db_string(permanent) || !valid_db_string(reauth_id)) { 247252726Srpaulo os_free(reauth_id); 248252726Srpaulo return -1; 249252726Srpaulo } 250252726Srpaulo 251252726Srpaulo pos = cmd; 252252726Srpaulo end = pos + sizeof(cmd); 253252726Srpaulo pos += os_snprintf(pos, end - pos, "INSERT OR REPLACE INTO reauth " 254252726Srpaulo "(permanent, reauth_id, counter%s%s%s%s) " 255252726Srpaulo "VALUES ('%s', '%s', %u", 256252726Srpaulo mk ? ", mk" : "", 257252726Srpaulo k_encr ? ", k_encr" : "", 258252726Srpaulo k_aut ? ", k_aut" : "", 259252726Srpaulo k_re ? ", k_re" : "", 260252726Srpaulo permanent, reauth_id, counter); 261252726Srpaulo os_free(reauth_id); 262252726Srpaulo 263252726Srpaulo if (mk) { 264252726Srpaulo pos += os_snprintf(pos, end - pos, ", '"); 265252726Srpaulo pos += wpa_snprintf_hex(pos, end - pos, mk, EAP_SIM_MK_LEN); 266252726Srpaulo pos += os_snprintf(pos, end - pos, "'"); 267252726Srpaulo } 268252726Srpaulo 269252726Srpaulo if (k_encr) { 270252726Srpaulo pos += os_snprintf(pos, end - pos, ", '"); 271252726Srpaulo pos += wpa_snprintf_hex(pos, end - pos, k_encr, 272252726Srpaulo EAP_SIM_K_ENCR_LEN); 273252726Srpaulo pos += os_snprintf(pos, end - pos, "'"); 274252726Srpaulo } 275252726Srpaulo 276252726Srpaulo if (k_aut) { 277252726Srpaulo pos += os_snprintf(pos, end - pos, ", '"); 278252726Srpaulo pos += wpa_snprintf_hex(pos, end - pos, k_aut, 279252726Srpaulo EAP_AKA_PRIME_K_AUT_LEN); 280252726Srpaulo pos += os_snprintf(pos, end - pos, "'"); 281252726Srpaulo } 282252726Srpaulo 283252726Srpaulo if (k_re) { 284252726Srpaulo pos += os_snprintf(pos, end - pos, ", '"); 285252726Srpaulo pos += wpa_snprintf_hex(pos, end - pos, k_re, 286252726Srpaulo EAP_AKA_PRIME_K_RE_LEN); 287252726Srpaulo pos += os_snprintf(pos, end - pos, "'"); 288252726Srpaulo } 289252726Srpaulo 290252726Srpaulo os_snprintf(pos, end - pos, ");"); 291252726Srpaulo 292252726Srpaulo if (sqlite3_exec(data->sqlite_db, cmd, NULL, NULL, &err) != SQLITE_OK) 293252726Srpaulo { 294252726Srpaulo wpa_printf(MSG_ERROR, "EAP-SIM DB: SQLite error: %s", err); 295252726Srpaulo sqlite3_free(err); 296252726Srpaulo return -1; 297252726Srpaulo } 298252726Srpaulo 299252726Srpaulo return 0; 300252726Srpaulo} 301252726Srpaulo 302252726Srpaulo 303252726Srpaulostatic int get_reauth_cb(void *ctx, int argc, char *argv[], char *col[]) 304252726Srpaulo{ 305252726Srpaulo struct eap_sim_db_data *data = ctx; 306252726Srpaulo int i; 307252726Srpaulo struct eap_sim_reauth *reauth = &data->db_tmp_reauth; 308252726Srpaulo 309252726Srpaulo for (i = 0; i < argc; i++) { 310252726Srpaulo if (os_strcmp(col[i], "permanent") == 0 && argv[i]) { 311252726Srpaulo os_strlcpy(data->db_tmp_identity, argv[i], 312252726Srpaulo sizeof(data->db_tmp_identity)); 313252726Srpaulo reauth->permanent = data->db_tmp_identity; 314252726Srpaulo } else if (os_strcmp(col[i], "counter") == 0 && argv[i]) { 315252726Srpaulo reauth->counter = atoi(argv[i]); 316252726Srpaulo } else if (os_strcmp(col[i], "mk") == 0 && argv[i]) { 317252726Srpaulo hexstr2bin(argv[i], reauth->mk, sizeof(reauth->mk)); 318252726Srpaulo } else if (os_strcmp(col[i], "k_encr") == 0 && argv[i]) { 319252726Srpaulo hexstr2bin(argv[i], reauth->k_encr, 320252726Srpaulo sizeof(reauth->k_encr)); 321252726Srpaulo } else if (os_strcmp(col[i], "k_aut") == 0 && argv[i]) { 322252726Srpaulo hexstr2bin(argv[i], reauth->k_aut, 323252726Srpaulo sizeof(reauth->k_aut)); 324252726Srpaulo } else if (os_strcmp(col[i], "k_re") == 0 && argv[i]) { 325252726Srpaulo hexstr2bin(argv[i], reauth->k_re, 326252726Srpaulo sizeof(reauth->k_re)); 327252726Srpaulo } 328252726Srpaulo } 329252726Srpaulo 330252726Srpaulo return 0; 331252726Srpaulo} 332252726Srpaulo 333252726Srpaulo 334252726Srpaulostatic struct eap_sim_reauth * 335252726Srpaulodb_get_reauth(struct eap_sim_db_data *data, const char *reauth_id) 336252726Srpaulo{ 337252726Srpaulo char cmd[256]; 338252726Srpaulo 339252726Srpaulo if (!valid_db_string(reauth_id)) 340252726Srpaulo return NULL; 341252726Srpaulo os_memset(&data->db_tmp_reauth, 0, sizeof(data->db_tmp_reauth)); 342252726Srpaulo os_strlcpy(data->db_tmp_pseudonym_str, reauth_id, 343252726Srpaulo sizeof(data->db_tmp_pseudonym_str)); 344252726Srpaulo data->db_tmp_reauth.reauth_id = data->db_tmp_pseudonym_str; 345252726Srpaulo os_snprintf(cmd, sizeof(cmd), 346252726Srpaulo "SELECT * FROM reauth WHERE reauth_id='%s';", reauth_id); 347252726Srpaulo if (sqlite3_exec(data->sqlite_db, cmd, get_reauth_cb, data, NULL) != 348252726Srpaulo SQLITE_OK) 349252726Srpaulo return NULL; 350252726Srpaulo if (data->db_tmp_reauth.permanent == NULL) 351252726Srpaulo return NULL; 352252726Srpaulo return &data->db_tmp_reauth; 353252726Srpaulo} 354252726Srpaulo 355252726Srpaulo 356252726Srpaulostatic void db_remove_reauth(struct eap_sim_db_data *data, 357252726Srpaulo struct eap_sim_reauth *reauth) 358252726Srpaulo{ 359252726Srpaulo char cmd[256]; 360252726Srpaulo 361252726Srpaulo if (!valid_db_string(reauth->permanent)) 362252726Srpaulo return; 363252726Srpaulo os_snprintf(cmd, sizeof(cmd), 364252726Srpaulo "DELETE FROM reauth WHERE permanent='%s';", 365252726Srpaulo reauth->permanent); 366252726Srpaulo sqlite3_exec(data->sqlite_db, cmd, NULL, NULL, NULL); 367252726Srpaulo} 368252726Srpaulo 369252726Srpaulo#endif /* CONFIG_SQLITE */ 370252726Srpaulo 371252726Srpaulo 372189251Ssamstatic struct eap_sim_db_pending * 373252726Srpauloeap_sim_db_get_pending(struct eap_sim_db_data *data, const char *imsi, int aka) 374189251Ssam{ 375189251Ssam struct eap_sim_db_pending *entry, *prev = NULL; 376189251Ssam 377189251Ssam entry = data->pending; 378189251Ssam while (entry) { 379252726Srpaulo if (entry->aka == aka && os_strcmp(entry->imsi, imsi) == 0) { 380189251Ssam if (prev) 381189251Ssam prev->next = entry->next; 382189251Ssam else 383189251Ssam data->pending = entry->next; 384189251Ssam break; 385189251Ssam } 386189251Ssam prev = entry; 387189251Ssam entry = entry->next; 388189251Ssam } 389189251Ssam return entry; 390189251Ssam} 391189251Ssam 392189251Ssam 393189251Ssamstatic void eap_sim_db_add_pending(struct eap_sim_db_data *data, 394189251Ssam struct eap_sim_db_pending *entry) 395189251Ssam{ 396189251Ssam entry->next = data->pending; 397189251Ssam data->pending = entry; 398189251Ssam} 399189251Ssam 400189251Ssam 401189251Ssamstatic void eap_sim_db_sim_resp_auth(struct eap_sim_db_data *data, 402189251Ssam const char *imsi, char *buf) 403189251Ssam{ 404189251Ssam char *start, *end, *pos; 405189251Ssam struct eap_sim_db_pending *entry; 406189251Ssam int num_chal; 407189251Ssam 408189251Ssam /* 409189251Ssam * SIM-RESP-AUTH <IMSI> Kc(i):SRES(i):RAND(i) ... 410189251Ssam * SIM-RESP-AUTH <IMSI> FAILURE 411189251Ssam * (IMSI = ASCII string, Kc/SRES/RAND = hex string) 412189251Ssam */ 413189251Ssam 414252726Srpaulo entry = eap_sim_db_get_pending(data, imsi, 0); 415189251Ssam if (entry == NULL) { 416189251Ssam wpa_printf(MSG_DEBUG, "EAP-SIM DB: No pending entry for the " 417189251Ssam "received message found"); 418189251Ssam return; 419189251Ssam } 420189251Ssam 421189251Ssam start = buf; 422189251Ssam if (os_strncmp(start, "FAILURE", 7) == 0) { 423189251Ssam wpa_printf(MSG_DEBUG, "EAP-SIM DB: External server reported " 424189251Ssam "failure"); 425189251Ssam entry->state = FAILURE; 426189251Ssam eap_sim_db_add_pending(data, entry); 427189251Ssam data->get_complete_cb(data->ctx, entry->cb_session_ctx); 428189251Ssam return; 429189251Ssam } 430189251Ssam 431189251Ssam num_chal = 0; 432189251Ssam while (num_chal < EAP_SIM_MAX_CHAL) { 433189251Ssam end = os_strchr(start, ' '); 434189251Ssam if (end) 435189251Ssam *end = '\0'; 436189251Ssam 437189251Ssam pos = os_strchr(start, ':'); 438189251Ssam if (pos == NULL) 439189251Ssam goto parse_fail; 440189251Ssam *pos = '\0'; 441189251Ssam if (hexstr2bin(start, entry->u.sim.kc[num_chal], 442189251Ssam EAP_SIM_KC_LEN)) 443189251Ssam goto parse_fail; 444189251Ssam 445189251Ssam start = pos + 1; 446189251Ssam pos = os_strchr(start, ':'); 447189251Ssam if (pos == NULL) 448189251Ssam goto parse_fail; 449189251Ssam *pos = '\0'; 450189251Ssam if (hexstr2bin(start, entry->u.sim.sres[num_chal], 451189251Ssam EAP_SIM_SRES_LEN)) 452189251Ssam goto parse_fail; 453189251Ssam 454189251Ssam start = pos + 1; 455189251Ssam if (hexstr2bin(start, entry->u.sim.rand[num_chal], 456189251Ssam GSM_RAND_LEN)) 457189251Ssam goto parse_fail; 458189251Ssam 459189251Ssam num_chal++; 460189251Ssam if (end == NULL) 461189251Ssam break; 462189251Ssam else 463189251Ssam start = end + 1; 464189251Ssam } 465189251Ssam entry->u.sim.num_chal = num_chal; 466189251Ssam 467189251Ssam entry->state = SUCCESS; 468189251Ssam wpa_printf(MSG_DEBUG, "EAP-SIM DB: Authentication data parsed " 469189251Ssam "successfully - callback"); 470189251Ssam eap_sim_db_add_pending(data, entry); 471189251Ssam data->get_complete_cb(data->ctx, entry->cb_session_ctx); 472189251Ssam return; 473189251Ssam 474189251Ssamparse_fail: 475189251Ssam wpa_printf(MSG_DEBUG, "EAP-SIM DB: Failed to parse response string"); 476189251Ssam os_free(entry); 477189251Ssam} 478189251Ssam 479189251Ssam 480189251Ssamstatic void eap_sim_db_aka_resp_auth(struct eap_sim_db_data *data, 481189251Ssam const char *imsi, char *buf) 482189251Ssam{ 483189251Ssam char *start, *end; 484189251Ssam struct eap_sim_db_pending *entry; 485189251Ssam 486189251Ssam /* 487189251Ssam * AKA-RESP-AUTH <IMSI> <RAND> <AUTN> <IK> <CK> <RES> 488189251Ssam * AKA-RESP-AUTH <IMSI> FAILURE 489189251Ssam * (IMSI = ASCII string, RAND/AUTN/IK/CK/RES = hex string) 490189251Ssam */ 491189251Ssam 492252726Srpaulo entry = eap_sim_db_get_pending(data, imsi, 1); 493189251Ssam if (entry == NULL) { 494189251Ssam wpa_printf(MSG_DEBUG, "EAP-SIM DB: No pending entry for the " 495189251Ssam "received message found"); 496189251Ssam return; 497189251Ssam } 498189251Ssam 499189251Ssam start = buf; 500189251Ssam if (os_strncmp(start, "FAILURE", 7) == 0) { 501189251Ssam wpa_printf(MSG_DEBUG, "EAP-SIM DB: External server reported " 502189251Ssam "failure"); 503189251Ssam entry->state = FAILURE; 504189251Ssam eap_sim_db_add_pending(data, entry); 505189251Ssam data->get_complete_cb(data->ctx, entry->cb_session_ctx); 506189251Ssam return; 507189251Ssam } 508189251Ssam 509189251Ssam end = os_strchr(start, ' '); 510189251Ssam if (end == NULL) 511189251Ssam goto parse_fail; 512189251Ssam *end = '\0'; 513189251Ssam if (hexstr2bin(start, entry->u.aka.rand, EAP_AKA_RAND_LEN)) 514189251Ssam goto parse_fail; 515189251Ssam 516189251Ssam start = end + 1; 517189251Ssam end = os_strchr(start, ' '); 518189251Ssam if (end == NULL) 519189251Ssam goto parse_fail; 520189251Ssam *end = '\0'; 521189251Ssam if (hexstr2bin(start, entry->u.aka.autn, EAP_AKA_AUTN_LEN)) 522189251Ssam goto parse_fail; 523189251Ssam 524189251Ssam start = end + 1; 525189251Ssam end = os_strchr(start, ' '); 526189251Ssam if (end == NULL) 527189251Ssam goto parse_fail; 528189251Ssam *end = '\0'; 529189251Ssam if (hexstr2bin(start, entry->u.aka.ik, EAP_AKA_IK_LEN)) 530189251Ssam goto parse_fail; 531189251Ssam 532189251Ssam start = end + 1; 533189251Ssam end = os_strchr(start, ' '); 534189251Ssam if (end == NULL) 535189251Ssam goto parse_fail; 536189251Ssam *end = '\0'; 537189251Ssam if (hexstr2bin(start, entry->u.aka.ck, EAP_AKA_CK_LEN)) 538189251Ssam goto parse_fail; 539189251Ssam 540189251Ssam start = end + 1; 541189251Ssam end = os_strchr(start, ' '); 542189251Ssam if (end) 543189251Ssam *end = '\0'; 544189251Ssam else { 545189251Ssam end = start; 546189251Ssam while (*end) 547189251Ssam end++; 548189251Ssam } 549189251Ssam entry->u.aka.res_len = (end - start) / 2; 550189251Ssam if (entry->u.aka.res_len > EAP_AKA_RES_MAX_LEN) { 551189251Ssam wpa_printf(MSG_DEBUG, "EAP-SIM DB: Too long RES"); 552189251Ssam entry->u.aka.res_len = 0; 553189251Ssam goto parse_fail; 554189251Ssam } 555189251Ssam if (hexstr2bin(start, entry->u.aka.res, entry->u.aka.res_len)) 556189251Ssam goto parse_fail; 557189251Ssam 558189251Ssam entry->state = SUCCESS; 559189251Ssam wpa_printf(MSG_DEBUG, "EAP-SIM DB: Authentication data parsed " 560189251Ssam "successfully - callback"); 561189251Ssam eap_sim_db_add_pending(data, entry); 562189251Ssam data->get_complete_cb(data->ctx, entry->cb_session_ctx); 563189251Ssam return; 564189251Ssam 565189251Ssamparse_fail: 566189251Ssam wpa_printf(MSG_DEBUG, "EAP-SIM DB: Failed to parse response string"); 567189251Ssam os_free(entry); 568189251Ssam} 569189251Ssam 570189251Ssam 571189251Ssamstatic void eap_sim_db_receive(int sock, void *eloop_ctx, void *sock_ctx) 572189251Ssam{ 573189251Ssam struct eap_sim_db_data *data = eloop_ctx; 574189251Ssam char buf[1000], *pos, *cmd, *imsi; 575189251Ssam int res; 576189251Ssam 577189251Ssam res = recv(sock, buf, sizeof(buf), 0); 578189251Ssam if (res < 0) 579189251Ssam return; 580189251Ssam wpa_hexdump_ascii_key(MSG_MSGDUMP, "EAP-SIM DB: Received from an " 581189251Ssam "external source", (u8 *) buf, res); 582189251Ssam if (res == 0) 583189251Ssam return; 584189251Ssam if (res >= (int) sizeof(buf)) 585189251Ssam res = sizeof(buf) - 1; 586189251Ssam buf[res] = '\0'; 587189251Ssam 588189251Ssam if (data->get_complete_cb == NULL) { 589189251Ssam wpa_printf(MSG_DEBUG, "EAP-SIM DB: No get_complete_cb " 590189251Ssam "registered"); 591189251Ssam return; 592189251Ssam } 593189251Ssam 594189251Ssam /* <cmd> <IMSI> ... */ 595189251Ssam 596189251Ssam cmd = buf; 597189251Ssam pos = os_strchr(cmd, ' '); 598189251Ssam if (pos == NULL) 599189251Ssam goto parse_fail; 600189251Ssam *pos = '\0'; 601189251Ssam imsi = pos + 1; 602189251Ssam pos = os_strchr(imsi, ' '); 603189251Ssam if (pos == NULL) 604189251Ssam goto parse_fail; 605189251Ssam *pos = '\0'; 606189251Ssam wpa_printf(MSG_DEBUG, "EAP-SIM DB: External response=%s for IMSI %s", 607189251Ssam cmd, imsi); 608189251Ssam 609189251Ssam if (os_strcmp(cmd, "SIM-RESP-AUTH") == 0) 610189251Ssam eap_sim_db_sim_resp_auth(data, imsi, pos + 1); 611189251Ssam else if (os_strcmp(cmd, "AKA-RESP-AUTH") == 0) 612189251Ssam eap_sim_db_aka_resp_auth(data, imsi, pos + 1); 613189251Ssam else 614189251Ssam wpa_printf(MSG_INFO, "EAP-SIM DB: Unknown external response " 615189251Ssam "'%s'", cmd); 616189251Ssam return; 617189251Ssam 618189251Ssamparse_fail: 619189251Ssam wpa_printf(MSG_DEBUG, "EAP-SIM DB: Failed to parse response string"); 620189251Ssam} 621189251Ssam 622189251Ssam 623189251Ssamstatic int eap_sim_db_open_socket(struct eap_sim_db_data *data) 624189251Ssam{ 625189251Ssam struct sockaddr_un addr; 626189251Ssam static int counter = 0; 627189251Ssam 628189251Ssam if (os_strncmp(data->fname, "unix:", 5) != 0) 629189251Ssam return -1; 630189251Ssam 631189251Ssam data->sock = socket(PF_UNIX, SOCK_DGRAM, 0); 632189251Ssam if (data->sock < 0) { 633189251Ssam perror("socket(eap_sim_db)"); 634189251Ssam return -1; 635189251Ssam } 636189251Ssam 637189251Ssam os_memset(&addr, 0, sizeof(addr)); 638189251Ssam addr.sun_family = AF_UNIX; 639189251Ssam os_snprintf(addr.sun_path, sizeof(addr.sun_path), 640189251Ssam "/tmp/eap_sim_db_%d-%d", getpid(), counter++); 641252726Srpaulo os_free(data->local_sock); 642189251Ssam data->local_sock = os_strdup(addr.sun_path); 643189251Ssam if (bind(data->sock, (struct sockaddr *) &addr, sizeof(addr)) < 0) { 644189251Ssam perror("bind(eap_sim_db)"); 645189251Ssam close(data->sock); 646189251Ssam data->sock = -1; 647189251Ssam return -1; 648189251Ssam } 649189251Ssam 650189251Ssam os_memset(&addr, 0, sizeof(addr)); 651189251Ssam addr.sun_family = AF_UNIX; 652189251Ssam os_strlcpy(addr.sun_path, data->fname + 5, sizeof(addr.sun_path)); 653189251Ssam if (connect(data->sock, (struct sockaddr *) &addr, sizeof(addr)) < 0) { 654189251Ssam perror("connect(eap_sim_db)"); 655189251Ssam wpa_hexdump_ascii(MSG_INFO, "HLR/AuC GW socket", 656189251Ssam (u8 *) addr.sun_path, 657189251Ssam os_strlen(addr.sun_path)); 658189251Ssam close(data->sock); 659189251Ssam data->sock = -1; 660189251Ssam return -1; 661189251Ssam } 662189251Ssam 663189251Ssam eloop_register_read_sock(data->sock, eap_sim_db_receive, data, NULL); 664189251Ssam 665189251Ssam return 0; 666189251Ssam} 667189251Ssam 668189251Ssam 669189251Ssamstatic void eap_sim_db_close_socket(struct eap_sim_db_data *data) 670189251Ssam{ 671189251Ssam if (data->sock >= 0) { 672189251Ssam eloop_unregister_read_sock(data->sock); 673189251Ssam close(data->sock); 674189251Ssam data->sock = -1; 675189251Ssam } 676189251Ssam if (data->local_sock) { 677189251Ssam unlink(data->local_sock); 678189251Ssam os_free(data->local_sock); 679189251Ssam data->local_sock = NULL; 680189251Ssam } 681189251Ssam} 682189251Ssam 683189251Ssam 684189251Ssam/** 685189251Ssam * eap_sim_db_init - Initialize EAP-SIM DB / authentication gateway interface 686189251Ssam * @config: Configuration data (e.g., file name) 687189251Ssam * @get_complete_cb: Callback function for reporting availability of triplets 688189251Ssam * @ctx: Context pointer for get_complete_cb 689189251Ssam * Returns: Pointer to a private data structure or %NULL on failure 690189251Ssam */ 691252726Srpaulostruct eap_sim_db_data * 692252726Srpauloeap_sim_db_init(const char *config, 693252726Srpaulo void (*get_complete_cb)(void *ctx, void *session_ctx), 694252726Srpaulo void *ctx) 695189251Ssam{ 696189251Ssam struct eap_sim_db_data *data; 697252726Srpaulo char *pos; 698189251Ssam 699189251Ssam data = os_zalloc(sizeof(*data)); 700189251Ssam if (data == NULL) 701189251Ssam return NULL; 702189251Ssam 703189251Ssam data->sock = -1; 704189251Ssam data->get_complete_cb = get_complete_cb; 705189251Ssam data->ctx = ctx; 706189251Ssam data->fname = os_strdup(config); 707189251Ssam if (data->fname == NULL) 708189251Ssam goto fail; 709252726Srpaulo pos = os_strstr(data->fname, " db="); 710252726Srpaulo if (pos) { 711252726Srpaulo *pos = '\0'; 712252726Srpaulo#ifdef CONFIG_SQLITE 713252726Srpaulo pos += 4; 714252726Srpaulo data->sqlite_db = db_open(pos); 715252726Srpaulo if (data->sqlite_db == NULL) 716252726Srpaulo goto fail; 717252726Srpaulo#endif /* CONFIG_SQLITE */ 718252726Srpaulo } 719189251Ssam 720189251Ssam if (os_strncmp(data->fname, "unix:", 5) == 0) { 721252726Srpaulo if (eap_sim_db_open_socket(data)) { 722252726Srpaulo wpa_printf(MSG_DEBUG, "EAP-SIM DB: External database " 723252726Srpaulo "connection not available - will retry " 724252726Srpaulo "later"); 725252726Srpaulo } 726189251Ssam } 727189251Ssam 728189251Ssam return data; 729189251Ssam 730189251Ssamfail: 731189251Ssam eap_sim_db_close_socket(data); 732189251Ssam os_free(data->fname); 733189251Ssam os_free(data); 734189251Ssam return NULL; 735189251Ssam} 736189251Ssam 737189251Ssam 738189251Ssamstatic void eap_sim_db_free_pseudonym(struct eap_sim_pseudonym *p) 739189251Ssam{ 740252726Srpaulo os_free(p->permanent); 741189251Ssam os_free(p->pseudonym); 742189251Ssam os_free(p); 743189251Ssam} 744189251Ssam 745189251Ssam 746189251Ssamstatic void eap_sim_db_free_reauth(struct eap_sim_reauth *r) 747189251Ssam{ 748252726Srpaulo os_free(r->permanent); 749189251Ssam os_free(r->reauth_id); 750189251Ssam os_free(r); 751189251Ssam} 752189251Ssam 753189251Ssam 754189251Ssam/** 755189251Ssam * eap_sim_db_deinit - Deinitialize EAP-SIM DB/authentication gw interface 756189251Ssam * @priv: Private data pointer from eap_sim_db_init() 757189251Ssam */ 758189251Ssamvoid eap_sim_db_deinit(void *priv) 759189251Ssam{ 760189251Ssam struct eap_sim_db_data *data = priv; 761189251Ssam struct eap_sim_pseudonym *p, *prev; 762189251Ssam struct eap_sim_reauth *r, *prevr; 763189251Ssam struct eap_sim_db_pending *pending, *prev_pending; 764189251Ssam 765252726Srpaulo#ifdef CONFIG_SQLITE 766252726Srpaulo if (data->sqlite_db) { 767252726Srpaulo sqlite3_close(data->sqlite_db); 768252726Srpaulo data->sqlite_db = NULL; 769252726Srpaulo } 770252726Srpaulo#endif /* CONFIG_SQLITE */ 771252726Srpaulo 772189251Ssam eap_sim_db_close_socket(data); 773189251Ssam os_free(data->fname); 774189251Ssam 775189251Ssam p = data->pseudonyms; 776189251Ssam while (p) { 777189251Ssam prev = p; 778189251Ssam p = p->next; 779189251Ssam eap_sim_db_free_pseudonym(prev); 780189251Ssam } 781189251Ssam 782189251Ssam r = data->reauths; 783189251Ssam while (r) { 784189251Ssam prevr = r; 785189251Ssam r = r->next; 786189251Ssam eap_sim_db_free_reauth(prevr); 787189251Ssam } 788189251Ssam 789189251Ssam pending = data->pending; 790189251Ssam while (pending) { 791189251Ssam prev_pending = pending; 792189251Ssam pending = pending->next; 793189251Ssam os_free(prev_pending); 794189251Ssam } 795189251Ssam 796189251Ssam os_free(data); 797189251Ssam} 798189251Ssam 799189251Ssam 800189251Ssamstatic int eap_sim_db_send(struct eap_sim_db_data *data, const char *msg, 801189251Ssam size_t len) 802189251Ssam{ 803189251Ssam int _errno = 0; 804189251Ssam 805189251Ssam if (send(data->sock, msg, len, 0) < 0) { 806189251Ssam _errno = errno; 807189251Ssam perror("send[EAP-SIM DB UNIX]"); 808189251Ssam } 809189251Ssam 810189251Ssam if (_errno == ENOTCONN || _errno == EDESTADDRREQ || _errno == EINVAL || 811189251Ssam _errno == ECONNREFUSED) { 812189251Ssam /* Try to reconnect */ 813189251Ssam eap_sim_db_close_socket(data); 814189251Ssam if (eap_sim_db_open_socket(data) < 0) 815189251Ssam return -1; 816189251Ssam wpa_printf(MSG_DEBUG, "EAP-SIM DB: Reconnected to the " 817189251Ssam "external server"); 818189251Ssam if (send(data->sock, msg, len, 0) < 0) { 819189251Ssam perror("send[EAP-SIM DB UNIX]"); 820189251Ssam return -1; 821189251Ssam } 822189251Ssam } 823189251Ssam 824189251Ssam return 0; 825189251Ssam} 826189251Ssam 827189251Ssam 828189251Ssamstatic void eap_sim_db_expire_pending(struct eap_sim_db_data *data) 829189251Ssam{ 830189251Ssam /* TODO: add limit for maximum length for pending list; remove latest 831189251Ssam * (i.e., last) entry from the list if the limit is reached; could also 832189251Ssam * use timeout to expire pending entries */ 833189251Ssam} 834189251Ssam 835189251Ssam 836189251Ssam/** 837189251Ssam * eap_sim_db_get_gsm_triplets - Get GSM triplets 838252726Srpaulo * @data: Private data pointer from eap_sim_db_init() 839252726Srpaulo * @username: Permanent username (prefix | IMSI) 840189251Ssam * @max_chal: Maximum number of triplets 841189251Ssam * @_rand: Buffer for RAND values 842189251Ssam * @kc: Buffer for Kc values 843189251Ssam * @sres: Buffer for SRES values 844189251Ssam * @cb_session_ctx: Session callback context for get_complete_cb() 845189251Ssam * Returns: Number of triplets received (has to be less than or equal to 846189251Ssam * max_chal), -1 (EAP_SIM_DB_FAILURE) on error (e.g., user not found), or 847189251Ssam * -2 (EAP_SIM_DB_PENDING) if results are not yet available. In this case, the 848189251Ssam * callback function registered with eap_sim_db_init() will be called once the 849189251Ssam * results become available. 850189251Ssam * 851189251Ssam * When using an external server for GSM triplets, this function can always 852189251Ssam * start a request and return EAP_SIM_DB_PENDING immediately if authentication 853189251Ssam * triplets are not available. Once the triplets are received, callback 854189251Ssam * function registered with eap_sim_db_init() is called to notify EAP state 855189251Ssam * machine to reprocess the message. This eap_sim_db_get_gsm_triplets() 856189251Ssam * function will then be called again and the newly received triplets will then 857189251Ssam * be given to the caller. 858189251Ssam */ 859252726Srpauloint eap_sim_db_get_gsm_triplets(struct eap_sim_db_data *data, 860252726Srpaulo const char *username, int max_chal, 861189251Ssam u8 *_rand, u8 *kc, u8 *sres, 862189251Ssam void *cb_session_ctx) 863189251Ssam{ 864189251Ssam struct eap_sim_db_pending *entry; 865189251Ssam int len, ret; 866189251Ssam char msg[40]; 867252726Srpaulo const char *imsi; 868252726Srpaulo size_t imsi_len; 869189251Ssam 870252726Srpaulo if (username == NULL || username[0] != EAP_SIM_PERMANENT_PREFIX || 871252726Srpaulo username[1] == '\0' || os_strlen(username) > sizeof(entry->imsi)) { 872252726Srpaulo wpa_printf(MSG_DEBUG, "EAP-SIM DB: unexpected username '%s'", 873252726Srpaulo username); 874189251Ssam return EAP_SIM_DB_FAILURE; 875189251Ssam } 876252726Srpaulo imsi = username + 1; 877252726Srpaulo wpa_printf(MSG_DEBUG, "EAP-SIM DB: Get GSM triplets for IMSI '%s'", 878252726Srpaulo imsi); 879189251Ssam 880252726Srpaulo entry = eap_sim_db_get_pending(data, imsi, 0); 881189251Ssam if (entry) { 882189251Ssam int num_chal; 883189251Ssam if (entry->state == FAILURE) { 884189251Ssam wpa_printf(MSG_DEBUG, "EAP-SIM DB: Pending entry -> " 885189251Ssam "failure"); 886189251Ssam os_free(entry); 887189251Ssam return EAP_SIM_DB_FAILURE; 888189251Ssam } 889189251Ssam 890189251Ssam if (entry->state == PENDING) { 891189251Ssam wpa_printf(MSG_DEBUG, "EAP-SIM DB: Pending entry -> " 892189251Ssam "still pending"); 893189251Ssam eap_sim_db_add_pending(data, entry); 894189251Ssam return EAP_SIM_DB_PENDING; 895189251Ssam } 896189251Ssam 897189251Ssam wpa_printf(MSG_DEBUG, "EAP-SIM DB: Pending entry -> " 898189251Ssam "%d challenges", entry->u.sim.num_chal); 899189251Ssam num_chal = entry->u.sim.num_chal; 900189251Ssam if (num_chal > max_chal) 901189251Ssam num_chal = max_chal; 902189251Ssam os_memcpy(_rand, entry->u.sim.rand, num_chal * GSM_RAND_LEN); 903189251Ssam os_memcpy(sres, entry->u.sim.sres, 904189251Ssam num_chal * EAP_SIM_SRES_LEN); 905189251Ssam os_memcpy(kc, entry->u.sim.kc, num_chal * EAP_SIM_KC_LEN); 906189251Ssam os_free(entry); 907189251Ssam return num_chal; 908189251Ssam } 909189251Ssam 910189251Ssam if (data->sock < 0) { 911189251Ssam if (eap_sim_db_open_socket(data) < 0) 912189251Ssam return EAP_SIM_DB_FAILURE; 913189251Ssam } 914189251Ssam 915252726Srpaulo imsi_len = os_strlen(imsi); 916189251Ssam len = os_snprintf(msg, sizeof(msg), "SIM-REQ-AUTH "); 917252726Srpaulo if (len < 0 || len + imsi_len >= sizeof(msg)) 918189251Ssam return EAP_SIM_DB_FAILURE; 919252726Srpaulo os_memcpy(msg + len, imsi, imsi_len); 920252726Srpaulo len += imsi_len; 921189251Ssam ret = os_snprintf(msg + len, sizeof(msg) - len, " %d", max_chal); 922189251Ssam if (ret < 0 || (size_t) ret >= sizeof(msg) - len) 923189251Ssam return EAP_SIM_DB_FAILURE; 924189251Ssam len += ret; 925189251Ssam 926252726Srpaulo wpa_printf(MSG_DEBUG, "EAP-SIM DB: requesting SIM authentication " 927252726Srpaulo "data for IMSI '%s'", imsi); 928189251Ssam if (eap_sim_db_send(data, msg, len) < 0) 929189251Ssam return EAP_SIM_DB_FAILURE; 930189251Ssam 931189251Ssam entry = os_zalloc(sizeof(*entry)); 932189251Ssam if (entry == NULL) 933189251Ssam return EAP_SIM_DB_FAILURE; 934189251Ssam 935189251Ssam os_get_time(&entry->timestamp); 936252726Srpaulo os_strlcpy(entry->imsi, imsi, sizeof(entry->imsi)); 937189251Ssam entry->cb_session_ctx = cb_session_ctx; 938189251Ssam entry->state = PENDING; 939189251Ssam eap_sim_db_add_pending(data, entry); 940189251Ssam eap_sim_db_expire_pending(data); 941189251Ssam 942189251Ssam return EAP_SIM_DB_PENDING; 943189251Ssam} 944189251Ssam 945189251Ssam 946189251Ssamstatic char * eap_sim_db_get_next(struct eap_sim_db_data *data, char prefix) 947189251Ssam{ 948189251Ssam char *id, *pos, *end; 949189251Ssam u8 buf[10]; 950189251Ssam 951252726Srpaulo if (random_get_bytes(buf, sizeof(buf))) 952189251Ssam return NULL; 953189251Ssam id = os_malloc(sizeof(buf) * 2 + 2); 954189251Ssam if (id == NULL) 955189251Ssam return NULL; 956189251Ssam 957189251Ssam pos = id; 958189251Ssam end = id + sizeof(buf) * 2 + 2; 959189251Ssam *pos++ = prefix; 960189251Ssam pos += wpa_snprintf_hex(pos, end - pos, buf, sizeof(buf)); 961189251Ssam 962189251Ssam return id; 963189251Ssam} 964189251Ssam 965189251Ssam 966189251Ssam/** 967189251Ssam * eap_sim_db_get_next_pseudonym - EAP-SIM DB: Get next pseudonym 968252726Srpaulo * @data: Private data pointer from eap_sim_db_init() 969252726Srpaulo * @method: EAP method (SIM/AKA/AKA') 970189251Ssam * Returns: Next pseudonym (allocated string) or %NULL on failure 971189251Ssam * 972189251Ssam * This function is used to generate a pseudonym for EAP-SIM. The returned 973189251Ssam * pseudonym is not added to database at this point; it will need to be added 974189251Ssam * with eap_sim_db_add_pseudonym() once the authentication has been completed 975189251Ssam * successfully. Caller is responsible for freeing the returned buffer. 976189251Ssam */ 977252726Srpaulochar * eap_sim_db_get_next_pseudonym(struct eap_sim_db_data *data, 978252726Srpaulo enum eap_sim_db_method method) 979189251Ssam{ 980252726Srpaulo char prefix = EAP_SIM_REAUTH_ID_PREFIX; 981252726Srpaulo 982252726Srpaulo switch (method) { 983252726Srpaulo case EAP_SIM_DB_SIM: 984252726Srpaulo prefix = EAP_SIM_PSEUDONYM_PREFIX; 985252726Srpaulo break; 986252726Srpaulo case EAP_SIM_DB_AKA: 987252726Srpaulo prefix = EAP_AKA_PSEUDONYM_PREFIX; 988252726Srpaulo break; 989252726Srpaulo case EAP_SIM_DB_AKA_PRIME: 990252726Srpaulo prefix = EAP_AKA_PRIME_PSEUDONYM_PREFIX; 991252726Srpaulo break; 992252726Srpaulo } 993252726Srpaulo 994252726Srpaulo return eap_sim_db_get_next(data, prefix); 995189251Ssam} 996189251Ssam 997189251Ssam 998189251Ssam/** 999189251Ssam * eap_sim_db_get_next_reauth_id - EAP-SIM DB: Get next reauth_id 1000252726Srpaulo * @data: Private data pointer from eap_sim_db_init() 1001252726Srpaulo * @method: EAP method (SIM/AKA/AKA') 1002189251Ssam * Returns: Next reauth_id (allocated string) or %NULL on failure 1003189251Ssam * 1004189251Ssam * This function is used to generate a fast re-authentication identity for 1005189251Ssam * EAP-SIM. The returned reauth_id is not added to database at this point; it 1006189251Ssam * will need to be added with eap_sim_db_add_reauth() once the authentication 1007189251Ssam * has been completed successfully. Caller is responsible for freeing the 1008189251Ssam * returned buffer. 1009189251Ssam */ 1010252726Srpaulochar * eap_sim_db_get_next_reauth_id(struct eap_sim_db_data *data, 1011252726Srpaulo enum eap_sim_db_method method) 1012189251Ssam{ 1013252726Srpaulo char prefix = EAP_SIM_REAUTH_ID_PREFIX; 1014252726Srpaulo 1015252726Srpaulo switch (method) { 1016252726Srpaulo case EAP_SIM_DB_SIM: 1017252726Srpaulo prefix = EAP_SIM_REAUTH_ID_PREFIX; 1018252726Srpaulo break; 1019252726Srpaulo case EAP_SIM_DB_AKA: 1020252726Srpaulo prefix = EAP_AKA_REAUTH_ID_PREFIX; 1021252726Srpaulo break; 1022252726Srpaulo case EAP_SIM_DB_AKA_PRIME: 1023252726Srpaulo prefix = EAP_AKA_PRIME_REAUTH_ID_PREFIX; 1024252726Srpaulo break; 1025252726Srpaulo } 1026252726Srpaulo 1027252726Srpaulo return eap_sim_db_get_next(data, prefix); 1028189251Ssam} 1029189251Ssam 1030189251Ssam 1031189251Ssam/** 1032189251Ssam * eap_sim_db_add_pseudonym - EAP-SIM DB: Add new pseudonym 1033252726Srpaulo * @data: Private data pointer from eap_sim_db_init() 1034252726Srpaulo * @permanent: Permanent username 1035189251Ssam * @pseudonym: Pseudonym for this user. This needs to be an allocated buffer, 1036189251Ssam * e.g., return value from eap_sim_db_get_next_pseudonym(). Caller must not 1037189251Ssam * free it. 1038189251Ssam * Returns: 0 on success, -1 on failure 1039189251Ssam * 1040189251Ssam * This function adds a new pseudonym for EAP-SIM user. EAP-SIM DB is 1041189251Ssam * responsible of freeing pseudonym buffer once it is not needed anymore. 1042189251Ssam */ 1043252726Srpauloint eap_sim_db_add_pseudonym(struct eap_sim_db_data *data, 1044252726Srpaulo const char *permanent, char *pseudonym) 1045189251Ssam{ 1046189251Ssam struct eap_sim_pseudonym *p; 1047252726Srpaulo wpa_printf(MSG_DEBUG, "EAP-SIM DB: Add pseudonym '%s' for permanent " 1048252726Srpaulo "username '%s'", pseudonym, permanent); 1049189251Ssam 1050189251Ssam /* TODO: could store last two pseudonyms */ 1051252726Srpaulo#ifdef CONFIG_SQLITE 1052252726Srpaulo if (data->sqlite_db) 1053252726Srpaulo return db_add_pseudonym(data, permanent, pseudonym); 1054252726Srpaulo#endif /* CONFIG_SQLITE */ 1055252726Srpaulo for (p = data->pseudonyms; p; p = p->next) { 1056252726Srpaulo if (os_strcmp(permanent, p->permanent) == 0) 1057252726Srpaulo break; 1058252726Srpaulo } 1059189251Ssam if (p) { 1060189251Ssam wpa_printf(MSG_DEBUG, "EAP-SIM DB: Replacing previous " 1061189251Ssam "pseudonym: %s", p->pseudonym); 1062189251Ssam os_free(p->pseudonym); 1063189251Ssam p->pseudonym = pseudonym; 1064189251Ssam return 0; 1065189251Ssam } 1066189251Ssam 1067189251Ssam p = os_zalloc(sizeof(*p)); 1068189251Ssam if (p == NULL) { 1069189251Ssam os_free(pseudonym); 1070189251Ssam return -1; 1071189251Ssam } 1072189251Ssam 1073189251Ssam p->next = data->pseudonyms; 1074252726Srpaulo p->permanent = os_strdup(permanent); 1075252726Srpaulo if (p->permanent == NULL) { 1076189251Ssam os_free(p); 1077189251Ssam os_free(pseudonym); 1078189251Ssam return -1; 1079189251Ssam } 1080189251Ssam p->pseudonym = pseudonym; 1081189251Ssam data->pseudonyms = p; 1082189251Ssam 1083189251Ssam wpa_printf(MSG_DEBUG, "EAP-SIM DB: Added new pseudonym entry"); 1084189251Ssam return 0; 1085189251Ssam} 1086189251Ssam 1087189251Ssam 1088189251Ssamstatic struct eap_sim_reauth * 1089252726Srpauloeap_sim_db_add_reauth_data(struct eap_sim_db_data *data, 1090252726Srpaulo const char *permanent, 1091252726Srpaulo char *reauth_id, u16 counter) 1092189251Ssam{ 1093189251Ssam struct eap_sim_reauth *r; 1094189251Ssam 1095252726Srpaulo for (r = data->reauths; r; r = r->next) { 1096252726Srpaulo if (os_strcmp(r->permanent, permanent) == 0) 1097252726Srpaulo break; 1098252726Srpaulo } 1099189251Ssam 1100189251Ssam if (r) { 1101189251Ssam wpa_printf(MSG_DEBUG, "EAP-SIM DB: Replacing previous " 1102189251Ssam "reauth_id: %s", r->reauth_id); 1103189251Ssam os_free(r->reauth_id); 1104189251Ssam r->reauth_id = reauth_id; 1105189251Ssam } else { 1106189251Ssam r = os_zalloc(sizeof(*r)); 1107189251Ssam if (r == NULL) { 1108189251Ssam os_free(reauth_id); 1109189251Ssam return NULL; 1110189251Ssam } 1111189251Ssam 1112189251Ssam r->next = data->reauths; 1113252726Srpaulo r->permanent = os_strdup(permanent); 1114252726Srpaulo if (r->permanent == NULL) { 1115189251Ssam os_free(r); 1116189251Ssam os_free(reauth_id); 1117189251Ssam return NULL; 1118189251Ssam } 1119189251Ssam r->reauth_id = reauth_id; 1120189251Ssam data->reauths = r; 1121189251Ssam wpa_printf(MSG_DEBUG, "EAP-SIM DB: Added new reauth entry"); 1122189251Ssam } 1123189251Ssam 1124189251Ssam r->counter = counter; 1125189251Ssam 1126189251Ssam return r; 1127189251Ssam} 1128189251Ssam 1129189251Ssam 1130189251Ssam/** 1131189251Ssam * eap_sim_db_add_reauth - EAP-SIM DB: Add new re-authentication entry 1132189251Ssam * @priv: Private data pointer from eap_sim_db_init() 1133252726Srpaulo * @permanent: Permanent username 1134189251Ssam * @identity_len: Length of identity 1135189251Ssam * @reauth_id: reauth_id for this user. This needs to be an allocated buffer, 1136189251Ssam * e.g., return value from eap_sim_db_get_next_reauth_id(). Caller must not 1137189251Ssam * free it. 1138189251Ssam * @counter: AT_COUNTER value for fast re-authentication 1139189251Ssam * @mk: 16-byte MK from the previous full authentication or %NULL 1140189251Ssam * Returns: 0 on success, -1 on failure 1141189251Ssam * 1142189251Ssam * This function adds a new re-authentication entry for an EAP-SIM user. 1143189251Ssam * EAP-SIM DB is responsible of freeing reauth_id buffer once it is not needed 1144189251Ssam * anymore. 1145189251Ssam */ 1146252726Srpauloint eap_sim_db_add_reauth(struct eap_sim_db_data *data, const char *permanent, 1147252726Srpaulo char *reauth_id, u16 counter, const u8 *mk) 1148189251Ssam{ 1149189251Ssam struct eap_sim_reauth *r; 1150189251Ssam 1151252726Srpaulo wpa_printf(MSG_DEBUG, "EAP-SIM DB: Add reauth_id '%s' for permanent " 1152252726Srpaulo "identity '%s'", reauth_id, permanent); 1153252726Srpaulo 1154252726Srpaulo#ifdef CONFIG_SQLITE 1155252726Srpaulo if (data->sqlite_db) 1156252726Srpaulo return db_add_reauth(data, permanent, reauth_id, counter, mk, 1157252726Srpaulo NULL, NULL, NULL); 1158252726Srpaulo#endif /* CONFIG_SQLITE */ 1159252726Srpaulo r = eap_sim_db_add_reauth_data(data, permanent, reauth_id, counter); 1160189251Ssam if (r == NULL) 1161189251Ssam return -1; 1162189251Ssam 1163189251Ssam os_memcpy(r->mk, mk, EAP_SIM_MK_LEN); 1164189251Ssam 1165189251Ssam return 0; 1166189251Ssam} 1167189251Ssam 1168189251Ssam 1169214734Srpaulo#ifdef EAP_SERVER_AKA_PRIME 1170189251Ssam/** 1171189251Ssam * eap_sim_db_add_reauth_prime - EAP-AKA' DB: Add new re-authentication entry 1172252726Srpaulo * @data: Private data pointer from eap_sim_db_init() 1173252726Srpaulo * @permanent: Permanent username 1174189251Ssam * @reauth_id: reauth_id for this user. This needs to be an allocated buffer, 1175189251Ssam * e.g., return value from eap_sim_db_get_next_reauth_id(). Caller must not 1176189251Ssam * free it. 1177189251Ssam * @counter: AT_COUNTER value for fast re-authentication 1178189251Ssam * @k_encr: K_encr from the previous full authentication 1179189251Ssam * @k_aut: K_aut from the previous full authentication 1180189251Ssam * @k_re: 32-byte K_re from the previous full authentication 1181189251Ssam * Returns: 0 on success, -1 on failure 1182189251Ssam * 1183189251Ssam * This function adds a new re-authentication entry for an EAP-AKA' user. 1184189251Ssam * EAP-SIM DB is responsible of freeing reauth_id buffer once it is not needed 1185189251Ssam * anymore. 1186189251Ssam */ 1187252726Srpauloint eap_sim_db_add_reauth_prime(struct eap_sim_db_data *data, 1188252726Srpaulo const char *permanent, char *reauth_id, 1189252726Srpaulo u16 counter, const u8 *k_encr, 1190252726Srpaulo const u8 *k_aut, const u8 *k_re) 1191189251Ssam{ 1192189251Ssam struct eap_sim_reauth *r; 1193189251Ssam 1194252726Srpaulo wpa_printf(MSG_DEBUG, "EAP-SIM DB: Add reauth_id '%s' for permanent " 1195252726Srpaulo "identity '%s'", reauth_id, permanent); 1196252726Srpaulo 1197252726Srpaulo#ifdef CONFIG_SQLITE 1198252726Srpaulo if (data->sqlite_db) 1199252726Srpaulo return db_add_reauth(data, permanent, reauth_id, counter, NULL, 1200252726Srpaulo k_encr, k_aut, k_re); 1201252726Srpaulo#endif /* CONFIG_SQLITE */ 1202252726Srpaulo r = eap_sim_db_add_reauth_data(data, permanent, reauth_id, counter); 1203189251Ssam if (r == NULL) 1204189251Ssam return -1; 1205189251Ssam 1206189251Ssam os_memcpy(r->k_encr, k_encr, EAP_SIM_K_ENCR_LEN); 1207189251Ssam os_memcpy(r->k_aut, k_aut, EAP_AKA_PRIME_K_AUT_LEN); 1208189251Ssam os_memcpy(r->k_re, k_re, EAP_AKA_PRIME_K_RE_LEN); 1209189251Ssam 1210189251Ssam return 0; 1211189251Ssam} 1212214734Srpaulo#endif /* EAP_SERVER_AKA_PRIME */ 1213189251Ssam 1214189251Ssam 1215189251Ssam/** 1216189251Ssam * eap_sim_db_get_permanent - EAP-SIM DB: Get permanent identity 1217252726Srpaulo * @data: Private data pointer from eap_sim_db_init() 1218252726Srpaulo * @pseudonym: Pseudonym username 1219252726Srpaulo * Returns: Pointer to permanent username or %NULL if not found 1220189251Ssam */ 1221252726Srpauloconst char * 1222252726Srpauloeap_sim_db_get_permanent(struct eap_sim_db_data *data, const char *pseudonym) 1223189251Ssam{ 1224189251Ssam struct eap_sim_pseudonym *p; 1225189251Ssam 1226252726Srpaulo#ifdef CONFIG_SQLITE 1227252726Srpaulo if (data->sqlite_db) 1228252726Srpaulo return db_get_pseudonym(data, pseudonym); 1229252726Srpaulo#endif /* CONFIG_SQLITE */ 1230189251Ssam 1231252726Srpaulo p = data->pseudonyms; 1232252726Srpaulo while (p) { 1233252726Srpaulo if (os_strcmp(p->pseudonym, pseudonym) == 0) 1234252726Srpaulo return p->permanent; 1235252726Srpaulo p = p->next; 1236252726Srpaulo } 1237189251Ssam 1238252726Srpaulo return NULL; 1239189251Ssam} 1240189251Ssam 1241189251Ssam 1242189251Ssam/** 1243189251Ssam * eap_sim_db_get_reauth_entry - EAP-SIM DB: Get re-authentication entry 1244252726Srpaulo * @data: Private data pointer from eap_sim_db_init() 1245252726Srpaulo * @reauth_id: Fast re-authentication username 1246189251Ssam * Returns: Pointer to the re-auth entry, or %NULL if not found 1247189251Ssam */ 1248189251Ssamstruct eap_sim_reauth * 1249252726Srpauloeap_sim_db_get_reauth_entry(struct eap_sim_db_data *data, 1250252726Srpaulo const char *reauth_id) 1251189251Ssam{ 1252189251Ssam struct eap_sim_reauth *r; 1253189251Ssam 1254252726Srpaulo#ifdef CONFIG_SQLITE 1255252726Srpaulo if (data->sqlite_db) 1256252726Srpaulo return db_get_reauth(data, reauth_id); 1257252726Srpaulo#endif /* CONFIG_SQLITE */ 1258252726Srpaulo 1259252726Srpaulo r = data->reauths; 1260252726Srpaulo while (r) { 1261252726Srpaulo if (os_strcmp(r->reauth_id, reauth_id) == 0) 1262252726Srpaulo break; 1263252726Srpaulo r = r->next; 1264252726Srpaulo } 1265252726Srpaulo 1266189251Ssam return r; 1267189251Ssam} 1268189251Ssam 1269189251Ssam 1270189251Ssam/** 1271189251Ssam * eap_sim_db_remove_reauth - EAP-SIM DB: Remove re-authentication entry 1272252726Srpaulo * @data: Private data pointer from eap_sim_db_init() 1273189251Ssam * @reauth: Pointer to re-authentication entry from 1274189251Ssam * eap_sim_db_get_reauth_entry() 1275189251Ssam */ 1276252726Srpaulovoid eap_sim_db_remove_reauth(struct eap_sim_db_data *data, 1277252726Srpaulo struct eap_sim_reauth *reauth) 1278189251Ssam{ 1279189251Ssam struct eap_sim_reauth *r, *prev = NULL; 1280252726Srpaulo#ifdef CONFIG_SQLITE 1281252726Srpaulo if (data->sqlite_db) { 1282252726Srpaulo db_remove_reauth(data, reauth); 1283252726Srpaulo return; 1284252726Srpaulo } 1285252726Srpaulo#endif /* CONFIG_SQLITE */ 1286189251Ssam r = data->reauths; 1287189251Ssam while (r) { 1288189251Ssam if (r == reauth) { 1289189251Ssam if (prev) 1290189251Ssam prev->next = r->next; 1291189251Ssam else 1292189251Ssam data->reauths = r->next; 1293189251Ssam eap_sim_db_free_reauth(r); 1294189251Ssam return; 1295189251Ssam } 1296189251Ssam prev = r; 1297189251Ssam r = r->next; 1298189251Ssam } 1299189251Ssam} 1300189251Ssam 1301189251Ssam 1302189251Ssam/** 1303189251Ssam * eap_sim_db_get_aka_auth - Get AKA authentication values 1304252726Srpaulo * @data: Private data pointer from eap_sim_db_init() 1305252726Srpaulo * @username: Permanent username (prefix | IMSI) 1306189251Ssam * @_rand: Buffer for RAND value 1307189251Ssam * @autn: Buffer for AUTN value 1308189251Ssam * @ik: Buffer for IK value 1309189251Ssam * @ck: Buffer for CK value 1310189251Ssam * @res: Buffer for RES value 1311189251Ssam * @res_len: Buffer for RES length 1312189251Ssam * @cb_session_ctx: Session callback context for get_complete_cb() 1313189251Ssam * Returns: 0 on success, -1 (EAP_SIM_DB_FAILURE) on error (e.g., user not 1314189251Ssam * found), or -2 (EAP_SIM_DB_PENDING) if results are not yet available. In this 1315189251Ssam * case, the callback function registered with eap_sim_db_init() will be 1316189251Ssam * called once the results become available. 1317189251Ssam * 1318189251Ssam * When using an external server for AKA authentication, this function can 1319189251Ssam * always start a request and return EAP_SIM_DB_PENDING immediately if 1320189251Ssam * authentication triplets are not available. Once the authentication data are 1321189251Ssam * received, callback function registered with eap_sim_db_init() is called to 1322189251Ssam * notify EAP state machine to reprocess the message. This 1323189251Ssam * eap_sim_db_get_aka_auth() function will then be called again and the newly 1324189251Ssam * received triplets will then be given to the caller. 1325189251Ssam */ 1326252726Srpauloint eap_sim_db_get_aka_auth(struct eap_sim_db_data *data, const char *username, 1327252726Srpaulo u8 *_rand, u8 *autn, u8 *ik, u8 *ck, 1328252726Srpaulo u8 *res, size_t *res_len, void *cb_session_ctx) 1329189251Ssam{ 1330189251Ssam struct eap_sim_db_pending *entry; 1331189251Ssam int len; 1332189251Ssam char msg[40]; 1333252726Srpaulo const char *imsi; 1334252726Srpaulo size_t imsi_len; 1335189251Ssam 1336252726Srpaulo if (username == NULL || 1337252726Srpaulo (username[0] != EAP_AKA_PERMANENT_PREFIX && 1338252726Srpaulo username[0] != EAP_AKA_PRIME_PERMANENT_PREFIX) || 1339252726Srpaulo username[1] == '\0' || os_strlen(username) > sizeof(entry->imsi)) { 1340252726Srpaulo wpa_printf(MSG_DEBUG, "EAP-SIM DB: unexpected username '%s'", 1341252726Srpaulo username); 1342189251Ssam return EAP_SIM_DB_FAILURE; 1343189251Ssam } 1344252726Srpaulo imsi = username + 1; 1345252726Srpaulo wpa_printf(MSG_DEBUG, "EAP-SIM DB: Get AKA auth for IMSI '%s'", 1346252726Srpaulo imsi); 1347189251Ssam 1348252726Srpaulo entry = eap_sim_db_get_pending(data, imsi, 1); 1349189251Ssam if (entry) { 1350189251Ssam if (entry->state == FAILURE) { 1351189251Ssam os_free(entry); 1352189251Ssam wpa_printf(MSG_DEBUG, "EAP-SIM DB: Failure"); 1353189251Ssam return EAP_SIM_DB_FAILURE; 1354189251Ssam } 1355189251Ssam 1356189251Ssam if (entry->state == PENDING) { 1357189251Ssam eap_sim_db_add_pending(data, entry); 1358189251Ssam wpa_printf(MSG_DEBUG, "EAP-SIM DB: Pending"); 1359189251Ssam return EAP_SIM_DB_PENDING; 1360189251Ssam } 1361189251Ssam 1362189251Ssam wpa_printf(MSG_DEBUG, "EAP-SIM DB: Returning successfully " 1363189251Ssam "received authentication data"); 1364189251Ssam os_memcpy(_rand, entry->u.aka.rand, EAP_AKA_RAND_LEN); 1365189251Ssam os_memcpy(autn, entry->u.aka.autn, EAP_AKA_AUTN_LEN); 1366189251Ssam os_memcpy(ik, entry->u.aka.ik, EAP_AKA_IK_LEN); 1367189251Ssam os_memcpy(ck, entry->u.aka.ck, EAP_AKA_CK_LEN); 1368189251Ssam os_memcpy(res, entry->u.aka.res, EAP_AKA_RES_MAX_LEN); 1369189251Ssam *res_len = entry->u.aka.res_len; 1370189251Ssam os_free(entry); 1371189251Ssam return 0; 1372189251Ssam } 1373189251Ssam 1374189251Ssam if (data->sock < 0) { 1375189251Ssam if (eap_sim_db_open_socket(data) < 0) 1376189251Ssam return EAP_SIM_DB_FAILURE; 1377189251Ssam } 1378189251Ssam 1379252726Srpaulo imsi_len = os_strlen(imsi); 1380189251Ssam len = os_snprintf(msg, sizeof(msg), "AKA-REQ-AUTH "); 1381252726Srpaulo if (len < 0 || len + imsi_len >= sizeof(msg)) 1382189251Ssam return EAP_SIM_DB_FAILURE; 1383252726Srpaulo os_memcpy(msg + len, imsi, imsi_len); 1384252726Srpaulo len += imsi_len; 1385189251Ssam 1386252726Srpaulo wpa_printf(MSG_DEBUG, "EAP-SIM DB: requesting AKA authentication " 1387252726Srpaulo "data for IMSI '%s'", imsi); 1388189251Ssam if (eap_sim_db_send(data, msg, len) < 0) 1389189251Ssam return EAP_SIM_DB_FAILURE; 1390189251Ssam 1391189251Ssam entry = os_zalloc(sizeof(*entry)); 1392189251Ssam if (entry == NULL) 1393189251Ssam return EAP_SIM_DB_FAILURE; 1394189251Ssam 1395189251Ssam os_get_time(&entry->timestamp); 1396189251Ssam entry->aka = 1; 1397252726Srpaulo os_strlcpy(entry->imsi, imsi, sizeof(entry->imsi)); 1398189251Ssam entry->cb_session_ctx = cb_session_ctx; 1399189251Ssam entry->state = PENDING; 1400189251Ssam eap_sim_db_add_pending(data, entry); 1401189251Ssam eap_sim_db_expire_pending(data); 1402189251Ssam 1403189251Ssam return EAP_SIM_DB_PENDING; 1404189251Ssam} 1405189251Ssam 1406189251Ssam 1407189251Ssam/** 1408189251Ssam * eap_sim_db_resynchronize - Resynchronize AKA AUTN 1409252726Srpaulo * @data: Private data pointer from eap_sim_db_init() 1410252726Srpaulo * @username: Permanent username 1411189251Ssam * @auts: AUTS value from the peer 1412189251Ssam * @_rand: RAND value used in the rejected message 1413189251Ssam * Returns: 0 on success, -1 on failure 1414189251Ssam * 1415189251Ssam * This function is called when the peer reports synchronization failure in the 1416189251Ssam * AUTN value by sending AUTS. The AUTS and RAND values should be sent to 1417189251Ssam * HLR/AuC to allow it to resynchronize with the peer. After this, 1418189251Ssam * eap_sim_db_get_aka_auth() will be called again to to fetch updated 1419189251Ssam * RAND/AUTN values for the next challenge. 1420189251Ssam */ 1421252726Srpauloint eap_sim_db_resynchronize(struct eap_sim_db_data *data, 1422252726Srpaulo const char *username, 1423252726Srpaulo const u8 *auts, const u8 *_rand) 1424189251Ssam{ 1425252726Srpaulo const char *imsi; 1426252726Srpaulo size_t imsi_len; 1427189251Ssam 1428252726Srpaulo if (username == NULL || 1429252726Srpaulo (username[0] != EAP_AKA_PERMANENT_PREFIX && 1430252726Srpaulo username[0] != EAP_AKA_PRIME_PERMANENT_PREFIX) || 1431252726Srpaulo username[1] == '\0' || os_strlen(username) > 20) { 1432252726Srpaulo wpa_printf(MSG_DEBUG, "EAP-SIM DB: unexpected username '%s'", 1433252726Srpaulo username); 1434189251Ssam return -1; 1435189251Ssam } 1436252726Srpaulo imsi = username + 1; 1437252726Srpaulo wpa_printf(MSG_DEBUG, "EAP-SIM DB: Get AKA auth for IMSI '%s'", 1438252726Srpaulo imsi); 1439189251Ssam 1440189251Ssam if (data->sock >= 0) { 1441189251Ssam char msg[100]; 1442189251Ssam int len, ret; 1443189251Ssam 1444252726Srpaulo imsi_len = os_strlen(imsi); 1445189251Ssam len = os_snprintf(msg, sizeof(msg), "AKA-AUTS "); 1446252726Srpaulo if (len < 0 || len + imsi_len >= sizeof(msg)) 1447189251Ssam return -1; 1448252726Srpaulo os_memcpy(msg + len, imsi, imsi_len); 1449252726Srpaulo len += imsi_len; 1450189251Ssam 1451189251Ssam ret = os_snprintf(msg + len, sizeof(msg) - len, " "); 1452189251Ssam if (ret < 0 || (size_t) ret >= sizeof(msg) - len) 1453189251Ssam return -1; 1454189251Ssam len += ret; 1455189251Ssam len += wpa_snprintf_hex(msg + len, sizeof(msg) - len, 1456189251Ssam auts, EAP_AKA_AUTS_LEN); 1457189251Ssam ret = os_snprintf(msg + len, sizeof(msg) - len, " "); 1458189251Ssam if (ret < 0 || (size_t) ret >= sizeof(msg) - len) 1459189251Ssam return -1; 1460189251Ssam len += ret; 1461189251Ssam len += wpa_snprintf_hex(msg + len, sizeof(msg) - len, 1462189251Ssam _rand, EAP_AKA_RAND_LEN); 1463252726Srpaulo wpa_printf(MSG_DEBUG, "EAP-SIM DB: reporting AKA AUTS for " 1464252726Srpaulo "IMSI '%s'", imsi); 1465189251Ssam if (eap_sim_db_send(data, msg, len) < 0) 1466189251Ssam return -1; 1467189251Ssam } 1468189251Ssam 1469189251Ssam return 0; 1470189251Ssam} 1471252726Srpaulo 1472252726Srpaulo 1473252726Srpaulo/** 1474252726Srpaulo * sim_get_username - Extract username from SIM identity 1475252726Srpaulo * @identity: Identity 1476252726Srpaulo * @identity_len: Identity length 1477252726Srpaulo * Returns: Allocated buffer with the username part of the identity 1478252726Srpaulo * 1479252726Srpaulo * Caller is responsible for freeing the returned buffer with os_free(). 1480252726Srpaulo */ 1481252726Srpaulochar * sim_get_username(const u8 *identity, size_t identity_len) 1482252726Srpaulo{ 1483252726Srpaulo char *username; 1484252726Srpaulo size_t pos; 1485252726Srpaulo 1486252726Srpaulo if (identity == NULL) 1487252726Srpaulo return NULL; 1488252726Srpaulo 1489252726Srpaulo for (pos = 0; pos < identity_len; pos++) { 1490252726Srpaulo if (identity[pos] == '@' || identity[pos] == '\0') 1491252726Srpaulo break; 1492252726Srpaulo } 1493252726Srpaulo 1494252726Srpaulo username = os_malloc(pos + 1); 1495252726Srpaulo if (username == NULL) 1496252726Srpaulo return NULL; 1497252726Srpaulo os_memcpy(username, identity, pos); 1498252726Srpaulo username[pos] = '\0'; 1499252726Srpaulo 1500252726Srpaulo return username; 1501252726Srpaulo} 1502