1/* 2 * hostapd / EAP-SIM database/authenticator gateway 3 * Copyright (c) 2005-2010, 2012, Jouni Malinen <j@w1.fi> 4 * 5 * This software may be distributed under the terms of the BSD license. 6 * See README for more details. 7 * 8 * This is an example implementation of the EAP-SIM/AKA database/authentication 9 * gateway interface that is using an external program as an SS7 gateway to 10 * GSM/UMTS authentication center (HLR/AuC). hlr_auc_gw is an example 11 * implementation of such a gateway program. This eap_sim_db.c takes care of 12 * EAP-SIM/AKA pseudonyms and re-auth identities. It can be used with different 13 * gateway implementations for HLR/AuC access. Alternatively, it can also be 14 * completely replaced if the in-memory database of pseudonyms/re-auth 15 * identities is not suitable for some cases. 16 */ 17 18#include "includes.h" 19#include <sys/un.h> 20#ifdef CONFIG_SQLITE 21#include <sqlite3.h> 22#endif /* CONFIG_SQLITE */ 23 24#include "common.h" 25#include "crypto/random.h" 26#include "eap_common/eap_sim_common.h" 27#include "eap_server/eap_sim_db.h" 28#include "eloop.h" 29 30struct eap_sim_pseudonym { 31 struct eap_sim_pseudonym *next; 32 char *permanent; /* permanent username */ 33 char *pseudonym; /* pseudonym username */ 34}; 35 36struct eap_sim_db_pending { 37 struct eap_sim_db_pending *next; 38 char imsi[20]; 39 enum { PENDING, SUCCESS, FAILURE } state; 40 void *cb_session_ctx; 41 struct os_time timestamp; 42 int aka; 43 union { 44 struct { 45 u8 kc[EAP_SIM_MAX_CHAL][EAP_SIM_KC_LEN]; 46 u8 sres[EAP_SIM_MAX_CHAL][EAP_SIM_SRES_LEN]; 47 u8 rand[EAP_SIM_MAX_CHAL][GSM_RAND_LEN]; 48 int num_chal; 49 } sim; 50 struct { 51 u8 rand[EAP_AKA_RAND_LEN]; 52 u8 autn[EAP_AKA_AUTN_LEN]; 53 u8 ik[EAP_AKA_IK_LEN]; 54 u8 ck[EAP_AKA_CK_LEN]; 55 u8 res[EAP_AKA_RES_MAX_LEN]; 56 size_t res_len; 57 } aka; 58 } u; 59}; 60 61struct eap_sim_db_data { 62 int sock; 63 char *fname; 64 char *local_sock; 65 void (*get_complete_cb)(void *ctx, void *session_ctx); 66 void *ctx; 67 struct eap_sim_pseudonym *pseudonyms; 68 struct eap_sim_reauth *reauths; 69 struct eap_sim_db_pending *pending; 70#ifdef CONFIG_SQLITE 71 sqlite3 *sqlite_db; 72 char db_tmp_identity[100]; 73 char db_tmp_pseudonym_str[100]; 74 struct eap_sim_pseudonym db_tmp_pseudonym; 75 struct eap_sim_reauth db_tmp_reauth; 76#endif /* CONFIG_SQLITE */ 77}; 78 79 80#ifdef CONFIG_SQLITE 81 82static int db_table_exists(sqlite3 *db, const char *name) 83{ 84 char cmd[128]; 85 os_snprintf(cmd, sizeof(cmd), "SELECT 1 FROM %s;", name); 86 return sqlite3_exec(db, cmd, NULL, NULL, NULL) == SQLITE_OK; 87} 88 89 90static int db_table_create_pseudonym(sqlite3 *db) 91{ 92 char *err = NULL; 93 const char *sql = 94 "CREATE TABLE pseudonyms(" 95 " permanent CHAR(21) PRIMARY KEY," 96 " pseudonym CHAR(21) NOT NULL" 97 ");"; 98 99 wpa_printf(MSG_DEBUG, "EAP-SIM DB: Adding database table for " 100 "pseudonym information"); 101 if (sqlite3_exec(db, sql, NULL, NULL, &err) != SQLITE_OK) { 102 wpa_printf(MSG_ERROR, "EAP-SIM DB: SQLite error: %s", err); 103 sqlite3_free(err); 104 return -1; 105 } 106 107 return 0; 108} 109 110 111static int db_table_create_reauth(sqlite3 *db) 112{ 113 char *err = NULL; 114 const char *sql = 115 "CREATE TABLE reauth(" 116 " permanent CHAR(21) PRIMARY KEY," 117 " reauth_id CHAR(21) NOT NULL," 118 " counter INTEGER," 119 " mk CHAR(40)," 120 " k_encr CHAR(32)," 121 " k_aut CHAR(64)," 122 " k_re CHAR(64)" 123 ");"; 124 125 wpa_printf(MSG_DEBUG, "EAP-SIM DB: Adding database table for " 126 "reauth information"); 127 if (sqlite3_exec(db, sql, NULL, NULL, &err) != SQLITE_OK) { 128 wpa_printf(MSG_ERROR, "EAP-SIM DB: SQLite error: %s", err); 129 sqlite3_free(err); 130 return -1; 131 } 132 133 return 0; 134} 135 136 137static sqlite3 * db_open(const char *db_file) 138{ 139 sqlite3 *db; 140 141 if (sqlite3_open(db_file, &db)) { 142 wpa_printf(MSG_ERROR, "EAP-SIM DB: Failed to open database " 143 "%s: %s", db_file, sqlite3_errmsg(db)); 144 sqlite3_close(db); 145 return NULL; 146 } 147 148 if (!db_table_exists(db, "pseudonyms") && 149 db_table_create_pseudonym(db) < 0) { 150 sqlite3_close(db); 151 return NULL; 152 } 153 154 if (!db_table_exists(db, "reauth") && 155 db_table_create_reauth(db) < 0) { 156 sqlite3_close(db); 157 return NULL; 158 } 159 160 return db; 161} 162 163 164static int valid_db_string(const char *str) 165{ 166 const char *pos = str; 167 while (*pos) { 168 if ((*pos < '0' || *pos > '9') && 169 (*pos < 'a' || *pos > 'f')) 170 return 0; 171 pos++; 172 } 173 return 1; 174} 175 176 177static int db_add_pseudonym(struct eap_sim_db_data *data, 178 const char *permanent, char *pseudonym) 179{ 180 char cmd[128]; 181 char *err = NULL; 182 183 if (!valid_db_string(permanent) || !valid_db_string(pseudonym)) { 184 os_free(pseudonym); 185 return -1; 186 } 187 188 os_snprintf(cmd, sizeof(cmd), "INSERT OR REPLACE INTO pseudonyms " 189 "(permanent, pseudonym) VALUES ('%s', '%s');", 190 permanent, pseudonym); 191 os_free(pseudonym); 192 if (sqlite3_exec(data->sqlite_db, cmd, NULL, NULL, &err) != SQLITE_OK) 193 { 194 wpa_printf(MSG_ERROR, "EAP-SIM DB: SQLite error: %s", err); 195 sqlite3_free(err); 196 return -1; 197 } 198 199 return 0; 200} 201 202 203static int get_pseudonym_cb(void *ctx, int argc, char *argv[], char *col[]) 204{ 205 struct eap_sim_db_data *data = ctx; 206 int i; 207 208 for (i = 0; i < argc; i++) { 209 if (os_strcmp(col[i], "permanent") == 0 && argv[i]) { 210 os_strlcpy(data->db_tmp_identity, argv[i], 211 sizeof(data->db_tmp_identity)); 212 } 213 } 214 215 return 0; 216} 217 218 219static char * 220db_get_pseudonym(struct eap_sim_db_data *data, const char *pseudonym) 221{ 222 char cmd[128]; 223 224 if (!valid_db_string(pseudonym)) 225 return NULL; 226 os_memset(&data->db_tmp_identity, 0, sizeof(data->db_tmp_identity)); 227 os_snprintf(cmd, sizeof(cmd), 228 "SELECT permanent FROM pseudonyms WHERE pseudonym='%s';", 229 pseudonym); 230 if (sqlite3_exec(data->sqlite_db, cmd, get_pseudonym_cb, data, NULL) != 231 SQLITE_OK) 232 return NULL; 233 if (data->db_tmp_identity[0] == '\0') 234 return NULL; 235 return data->db_tmp_identity; 236} 237 238 239static int db_add_reauth(struct eap_sim_db_data *data, const char *permanent, 240 char *reauth_id, u16 counter, const u8 *mk, 241 const u8 *k_encr, const u8 *k_aut, const u8 *k_re) 242{ 243 char cmd[2000], *pos, *end; 244 char *err = NULL; 245 246 if (!valid_db_string(permanent) || !valid_db_string(reauth_id)) { 247 os_free(reauth_id); 248 return -1; 249 } 250 251 pos = cmd; 252 end = pos + sizeof(cmd); 253 pos += os_snprintf(pos, end - pos, "INSERT OR REPLACE INTO reauth " 254 "(permanent, reauth_id, counter%s%s%s%s) " 255 "VALUES ('%s', '%s', %u", 256 mk ? ", mk" : "", 257 k_encr ? ", k_encr" : "", 258 k_aut ? ", k_aut" : "", 259 k_re ? ", k_re" : "", 260 permanent, reauth_id, counter); 261 os_free(reauth_id); 262 263 if (mk) { 264 pos += os_snprintf(pos, end - pos, ", '"); 265 pos += wpa_snprintf_hex(pos, end - pos, mk, EAP_SIM_MK_LEN); 266 pos += os_snprintf(pos, end - pos, "'"); 267 } 268 269 if (k_encr) { 270 pos += os_snprintf(pos, end - pos, ", '"); 271 pos += wpa_snprintf_hex(pos, end - pos, k_encr, 272 EAP_SIM_K_ENCR_LEN); 273 pos += os_snprintf(pos, end - pos, "'"); 274 } 275 276 if (k_aut) { 277 pos += os_snprintf(pos, end - pos, ", '"); 278 pos += wpa_snprintf_hex(pos, end - pos, k_aut, 279 EAP_AKA_PRIME_K_AUT_LEN); 280 pos += os_snprintf(pos, end - pos, "'"); 281 } 282 283 if (k_re) { 284 pos += os_snprintf(pos, end - pos, ", '"); 285 pos += wpa_snprintf_hex(pos, end - pos, k_re, 286 EAP_AKA_PRIME_K_RE_LEN); 287 pos += os_snprintf(pos, end - pos, "'"); 288 } 289 290 os_snprintf(pos, end - pos, ");"); 291 292 if (sqlite3_exec(data->sqlite_db, cmd, NULL, NULL, &err) != SQLITE_OK) 293 { 294 wpa_printf(MSG_ERROR, "EAP-SIM DB: SQLite error: %s", err); 295 sqlite3_free(err); 296 return -1; 297 } 298 299 return 0; 300} 301 302 303static int get_reauth_cb(void *ctx, int argc, char *argv[], char *col[]) 304{ 305 struct eap_sim_db_data *data = ctx; 306 int i; 307 struct eap_sim_reauth *reauth = &data->db_tmp_reauth; 308 309 for (i = 0; i < argc; i++) { 310 if (os_strcmp(col[i], "permanent") == 0 && argv[i]) { 311 os_strlcpy(data->db_tmp_identity, argv[i], 312 sizeof(data->db_tmp_identity)); 313 reauth->permanent = data->db_tmp_identity; 314 } else if (os_strcmp(col[i], "counter") == 0 && argv[i]) { 315 reauth->counter = atoi(argv[i]); 316 } else if (os_strcmp(col[i], "mk") == 0 && argv[i]) { 317 hexstr2bin(argv[i], reauth->mk, sizeof(reauth->mk)); 318 } else if (os_strcmp(col[i], "k_encr") == 0 && argv[i]) { 319 hexstr2bin(argv[i], reauth->k_encr, 320 sizeof(reauth->k_encr)); 321 } else if (os_strcmp(col[i], "k_aut") == 0 && argv[i]) { 322 hexstr2bin(argv[i], reauth->k_aut, 323 sizeof(reauth->k_aut)); 324 } else if (os_strcmp(col[i], "k_re") == 0 && argv[i]) { 325 hexstr2bin(argv[i], reauth->k_re, 326 sizeof(reauth->k_re)); 327 } 328 } 329 330 return 0; 331} 332 333 334static struct eap_sim_reauth * 335db_get_reauth(struct eap_sim_db_data *data, const char *reauth_id) 336{ 337 char cmd[256]; 338 339 if (!valid_db_string(reauth_id)) 340 return NULL; 341 os_memset(&data->db_tmp_reauth, 0, sizeof(data->db_tmp_reauth)); 342 os_strlcpy(data->db_tmp_pseudonym_str, reauth_id, 343 sizeof(data->db_tmp_pseudonym_str)); 344 data->db_tmp_reauth.reauth_id = data->db_tmp_pseudonym_str; 345 os_snprintf(cmd, sizeof(cmd), 346 "SELECT * FROM reauth WHERE reauth_id='%s';", reauth_id); 347 if (sqlite3_exec(data->sqlite_db, cmd, get_reauth_cb, data, NULL) != 348 SQLITE_OK) 349 return NULL; 350 if (data->db_tmp_reauth.permanent == NULL) 351 return NULL; 352 return &data->db_tmp_reauth; 353} 354 355 356static void db_remove_reauth(struct eap_sim_db_data *data, 357 struct eap_sim_reauth *reauth) 358{ 359 char cmd[256]; 360 361 if (!valid_db_string(reauth->permanent)) 362 return; 363 os_snprintf(cmd, sizeof(cmd), 364 "DELETE FROM reauth WHERE permanent='%s';", 365 reauth->permanent); 366 sqlite3_exec(data->sqlite_db, cmd, NULL, NULL, NULL); 367} 368 369#endif /* CONFIG_SQLITE */ 370 371 372static struct eap_sim_db_pending * 373eap_sim_db_get_pending(struct eap_sim_db_data *data, const char *imsi, int aka) 374{ 375 struct eap_sim_db_pending *entry, *prev = NULL; 376 377 entry = data->pending; 378 while (entry) { 379 if (entry->aka == aka && os_strcmp(entry->imsi, imsi) == 0) { 380 if (prev) 381 prev->next = entry->next; 382 else 383 data->pending = entry->next; 384 break; 385 } 386 prev = entry; 387 entry = entry->next; 388 } 389 return entry; 390} 391 392 393static void eap_sim_db_add_pending(struct eap_sim_db_data *data, 394 struct eap_sim_db_pending *entry) 395{ 396 entry->next = data->pending; 397 data->pending = entry; 398} 399 400 401static void eap_sim_db_sim_resp_auth(struct eap_sim_db_data *data, 402 const char *imsi, char *buf) 403{ 404 char *start, *end, *pos; 405 struct eap_sim_db_pending *entry; 406 int num_chal; 407 408 /* 409 * SIM-RESP-AUTH <IMSI> Kc(i):SRES(i):RAND(i) ... 410 * SIM-RESP-AUTH <IMSI> FAILURE 411 * (IMSI = ASCII string, Kc/SRES/RAND = hex string) 412 */ 413 414 entry = eap_sim_db_get_pending(data, imsi, 0); 415 if (entry == NULL) { 416 wpa_printf(MSG_DEBUG, "EAP-SIM DB: No pending entry for the " 417 "received message found"); 418 return; 419 } 420 421 start = buf; 422 if (os_strncmp(start, "FAILURE", 7) == 0) { 423 wpa_printf(MSG_DEBUG, "EAP-SIM DB: External server reported " 424 "failure"); 425 entry->state = FAILURE; 426 eap_sim_db_add_pending(data, entry); 427 data->get_complete_cb(data->ctx, entry->cb_session_ctx); 428 return; 429 } 430 431 num_chal = 0; 432 while (num_chal < EAP_SIM_MAX_CHAL) { 433 end = os_strchr(start, ' '); 434 if (end) 435 *end = '\0'; 436 437 pos = os_strchr(start, ':'); 438 if (pos == NULL) 439 goto parse_fail; 440 *pos = '\0'; 441 if (hexstr2bin(start, entry->u.sim.kc[num_chal], 442 EAP_SIM_KC_LEN)) 443 goto parse_fail; 444 445 start = pos + 1; 446 pos = os_strchr(start, ':'); 447 if (pos == NULL) 448 goto parse_fail; 449 *pos = '\0'; 450 if (hexstr2bin(start, entry->u.sim.sres[num_chal], 451 EAP_SIM_SRES_LEN)) 452 goto parse_fail; 453 454 start = pos + 1; 455 if (hexstr2bin(start, entry->u.sim.rand[num_chal], 456 GSM_RAND_LEN)) 457 goto parse_fail; 458 459 num_chal++; 460 if (end == NULL) 461 break; 462 else 463 start = end + 1; 464 } 465 entry->u.sim.num_chal = num_chal; 466 467 entry->state = SUCCESS; 468 wpa_printf(MSG_DEBUG, "EAP-SIM DB: Authentication data parsed " 469 "successfully - callback"); 470 eap_sim_db_add_pending(data, entry); 471 data->get_complete_cb(data->ctx, entry->cb_session_ctx); 472 return; 473 474parse_fail: 475 wpa_printf(MSG_DEBUG, "EAP-SIM DB: Failed to parse response string"); 476 os_free(entry); 477} 478 479 480static void eap_sim_db_aka_resp_auth(struct eap_sim_db_data *data, 481 const char *imsi, char *buf) 482{ 483 char *start, *end; 484 struct eap_sim_db_pending *entry; 485 486 /* 487 * AKA-RESP-AUTH <IMSI> <RAND> <AUTN> <IK> <CK> <RES> 488 * AKA-RESP-AUTH <IMSI> FAILURE 489 * (IMSI = ASCII string, RAND/AUTN/IK/CK/RES = hex string) 490 */ 491 492 entry = eap_sim_db_get_pending(data, imsi, 1); 493 if (entry == NULL) { 494 wpa_printf(MSG_DEBUG, "EAP-SIM DB: No pending entry for the " 495 "received message found"); 496 return; 497 } 498 499 start = buf; 500 if (os_strncmp(start, "FAILURE", 7) == 0) { 501 wpa_printf(MSG_DEBUG, "EAP-SIM DB: External server reported " 502 "failure"); 503 entry->state = FAILURE; 504 eap_sim_db_add_pending(data, entry); 505 data->get_complete_cb(data->ctx, entry->cb_session_ctx); 506 return; 507 } 508 509 end = os_strchr(start, ' '); 510 if (end == NULL) 511 goto parse_fail; 512 *end = '\0'; 513 if (hexstr2bin(start, entry->u.aka.rand, EAP_AKA_RAND_LEN)) 514 goto parse_fail; 515 516 start = end + 1; 517 end = os_strchr(start, ' '); 518 if (end == NULL) 519 goto parse_fail; 520 *end = '\0'; 521 if (hexstr2bin(start, entry->u.aka.autn, EAP_AKA_AUTN_LEN)) 522 goto parse_fail; 523 524 start = end + 1; 525 end = os_strchr(start, ' '); 526 if (end == NULL) 527 goto parse_fail; 528 *end = '\0'; 529 if (hexstr2bin(start, entry->u.aka.ik, EAP_AKA_IK_LEN)) 530 goto parse_fail; 531 532 start = end + 1; 533 end = os_strchr(start, ' '); 534 if (end == NULL) 535 goto parse_fail; 536 *end = '\0'; 537 if (hexstr2bin(start, entry->u.aka.ck, EAP_AKA_CK_LEN)) 538 goto parse_fail; 539 540 start = end + 1; 541 end = os_strchr(start, ' '); 542 if (end) 543 *end = '\0'; 544 else { 545 end = start; 546 while (*end) 547 end++; 548 } 549 entry->u.aka.res_len = (end - start) / 2; 550 if (entry->u.aka.res_len > EAP_AKA_RES_MAX_LEN) { 551 wpa_printf(MSG_DEBUG, "EAP-SIM DB: Too long RES"); 552 entry->u.aka.res_len = 0; 553 goto parse_fail; 554 } 555 if (hexstr2bin(start, entry->u.aka.res, entry->u.aka.res_len)) 556 goto parse_fail; 557 558 entry->state = SUCCESS; 559 wpa_printf(MSG_DEBUG, "EAP-SIM DB: Authentication data parsed " 560 "successfully - callback"); 561 eap_sim_db_add_pending(data, entry); 562 data->get_complete_cb(data->ctx, entry->cb_session_ctx); 563 return; 564 565parse_fail: 566 wpa_printf(MSG_DEBUG, "EAP-SIM DB: Failed to parse response string"); 567 os_free(entry); 568} 569 570 571static void eap_sim_db_receive(int sock, void *eloop_ctx, void *sock_ctx) 572{ 573 struct eap_sim_db_data *data = eloop_ctx; 574 char buf[1000], *pos, *cmd, *imsi; 575 int res; 576 577 res = recv(sock, buf, sizeof(buf), 0); 578 if (res < 0) 579 return; 580 wpa_hexdump_ascii_key(MSG_MSGDUMP, "EAP-SIM DB: Received from an " 581 "external source", (u8 *) buf, res); 582 if (res == 0) 583 return; 584 if (res >= (int) sizeof(buf)) 585 res = sizeof(buf) - 1; 586 buf[res] = '\0'; 587 588 if (data->get_complete_cb == NULL) { 589 wpa_printf(MSG_DEBUG, "EAP-SIM DB: No get_complete_cb " 590 "registered"); 591 return; 592 } 593 594 /* <cmd> <IMSI> ... */ 595 596 cmd = buf; 597 pos = os_strchr(cmd, ' '); 598 if (pos == NULL) 599 goto parse_fail; 600 *pos = '\0'; 601 imsi = pos + 1; 602 pos = os_strchr(imsi, ' '); 603 if (pos == NULL) 604 goto parse_fail; 605 *pos = '\0'; 606 wpa_printf(MSG_DEBUG, "EAP-SIM DB: External response=%s for IMSI %s", 607 cmd, imsi); 608 609 if (os_strcmp(cmd, "SIM-RESP-AUTH") == 0) 610 eap_sim_db_sim_resp_auth(data, imsi, pos + 1); 611 else if (os_strcmp(cmd, "AKA-RESP-AUTH") == 0) 612 eap_sim_db_aka_resp_auth(data, imsi, pos + 1); 613 else 614 wpa_printf(MSG_INFO, "EAP-SIM DB: Unknown external response " 615 "'%s'", cmd); 616 return; 617 618parse_fail: 619 wpa_printf(MSG_DEBUG, "EAP-SIM DB: Failed to parse response string"); 620} 621 622 623static int eap_sim_db_open_socket(struct eap_sim_db_data *data) 624{ 625 struct sockaddr_un addr; 626 static int counter = 0; 627 628 if (os_strncmp(data->fname, "unix:", 5) != 0) 629 return -1; 630 631 data->sock = socket(PF_UNIX, SOCK_DGRAM, 0); 632 if (data->sock < 0) { 633 perror("socket(eap_sim_db)"); 634 return -1; 635 } 636 637 os_memset(&addr, 0, sizeof(addr)); 638 addr.sun_family = AF_UNIX; 639 os_snprintf(addr.sun_path, sizeof(addr.sun_path), 640 "/tmp/eap_sim_db_%d-%d", getpid(), counter++); 641 os_free(data->local_sock); 642 data->local_sock = os_strdup(addr.sun_path); 643 if (bind(data->sock, (struct sockaddr *) &addr, sizeof(addr)) < 0) { 644 perror("bind(eap_sim_db)"); 645 close(data->sock); 646 data->sock = -1; 647 return -1; 648 } 649 650 os_memset(&addr, 0, sizeof(addr)); 651 addr.sun_family = AF_UNIX; 652 os_strlcpy(addr.sun_path, data->fname + 5, sizeof(addr.sun_path)); 653 if (connect(data->sock, (struct sockaddr *) &addr, sizeof(addr)) < 0) { 654 perror("connect(eap_sim_db)"); 655 wpa_hexdump_ascii(MSG_INFO, "HLR/AuC GW socket", 656 (u8 *) addr.sun_path, 657 os_strlen(addr.sun_path)); 658 close(data->sock); 659 data->sock = -1; 660 return -1; 661 } 662 663 eloop_register_read_sock(data->sock, eap_sim_db_receive, data, NULL); 664 665 return 0; 666} 667 668 669static void eap_sim_db_close_socket(struct eap_sim_db_data *data) 670{ 671 if (data->sock >= 0) { 672 eloop_unregister_read_sock(data->sock); 673 close(data->sock); 674 data->sock = -1; 675 } 676 if (data->local_sock) { 677 unlink(data->local_sock); 678 os_free(data->local_sock); 679 data->local_sock = NULL; 680 } 681} 682 683 684/** 685 * eap_sim_db_init - Initialize EAP-SIM DB / authentication gateway interface 686 * @config: Configuration data (e.g., file name) 687 * @get_complete_cb: Callback function for reporting availability of triplets 688 * @ctx: Context pointer for get_complete_cb 689 * Returns: Pointer to a private data structure or %NULL on failure 690 */ 691struct eap_sim_db_data * 692eap_sim_db_init(const char *config, 693 void (*get_complete_cb)(void *ctx, void *session_ctx), 694 void *ctx) 695{ 696 struct eap_sim_db_data *data; 697 char *pos; 698 699 data = os_zalloc(sizeof(*data)); 700 if (data == NULL) 701 return NULL; 702 703 data->sock = -1; 704 data->get_complete_cb = get_complete_cb; 705 data->ctx = ctx; 706 data->fname = os_strdup(config); 707 if (data->fname == NULL) 708 goto fail; 709 pos = os_strstr(data->fname, " db="); 710 if (pos) { 711 *pos = '\0'; 712#ifdef CONFIG_SQLITE 713 pos += 4; 714 data->sqlite_db = db_open(pos); 715 if (data->sqlite_db == NULL) 716 goto fail; 717#endif /* CONFIG_SQLITE */ 718 } 719 720 if (os_strncmp(data->fname, "unix:", 5) == 0) { 721 if (eap_sim_db_open_socket(data)) { 722 wpa_printf(MSG_DEBUG, "EAP-SIM DB: External database " 723 "connection not available - will retry " 724 "later"); 725 } 726 } 727 728 return data; 729 730fail: 731 eap_sim_db_close_socket(data); 732 os_free(data->fname); 733 os_free(data); 734 return NULL; 735} 736 737 738static void eap_sim_db_free_pseudonym(struct eap_sim_pseudonym *p) 739{ 740 os_free(p->permanent); 741 os_free(p->pseudonym); 742 os_free(p); 743} 744 745 746static void eap_sim_db_free_reauth(struct eap_sim_reauth *r) 747{ 748 os_free(r->permanent); 749 os_free(r->reauth_id); 750 os_free(r); 751} 752 753 754/** 755 * eap_sim_db_deinit - Deinitialize EAP-SIM DB/authentication gw interface 756 * @priv: Private data pointer from eap_sim_db_init() 757 */ 758void eap_sim_db_deinit(void *priv) 759{ 760 struct eap_sim_db_data *data = priv; 761 struct eap_sim_pseudonym *p, *prev; 762 struct eap_sim_reauth *r, *prevr; 763 struct eap_sim_db_pending *pending, *prev_pending; 764 765#ifdef CONFIG_SQLITE 766 if (data->sqlite_db) { 767 sqlite3_close(data->sqlite_db); 768 data->sqlite_db = NULL; 769 } 770#endif /* CONFIG_SQLITE */ 771 772 eap_sim_db_close_socket(data); 773 os_free(data->fname); 774 775 p = data->pseudonyms; 776 while (p) { 777 prev = p; 778 p = p->next; 779 eap_sim_db_free_pseudonym(prev); 780 } 781 782 r = data->reauths; 783 while (r) { 784 prevr = r; 785 r = r->next; 786 eap_sim_db_free_reauth(prevr); 787 } 788 789 pending = data->pending; 790 while (pending) { 791 prev_pending = pending; 792 pending = pending->next; 793 os_free(prev_pending); 794 } 795 796 os_free(data); 797} 798 799 800static int eap_sim_db_send(struct eap_sim_db_data *data, const char *msg, 801 size_t len) 802{ 803 int _errno = 0; 804 805 if (send(data->sock, msg, len, 0) < 0) { 806 _errno = errno; 807 perror("send[EAP-SIM DB UNIX]"); 808 } 809 810 if (_errno == ENOTCONN || _errno == EDESTADDRREQ || _errno == EINVAL || 811 _errno == ECONNREFUSED) { 812 /* Try to reconnect */ 813 eap_sim_db_close_socket(data); 814 if (eap_sim_db_open_socket(data) < 0) 815 return -1; 816 wpa_printf(MSG_DEBUG, "EAP-SIM DB: Reconnected to the " 817 "external server"); 818 if (send(data->sock, msg, len, 0) < 0) { 819 perror("send[EAP-SIM DB UNIX]"); 820 return -1; 821 } 822 } 823 824 return 0; 825} 826 827 828static void eap_sim_db_expire_pending(struct eap_sim_db_data *data) 829{ 830 /* TODO: add limit for maximum length for pending list; remove latest 831 * (i.e., last) entry from the list if the limit is reached; could also 832 * use timeout to expire pending entries */ 833} 834 835 836/** 837 * eap_sim_db_get_gsm_triplets - Get GSM triplets 838 * @data: Private data pointer from eap_sim_db_init() 839 * @username: Permanent username (prefix | IMSI) 840 * @max_chal: Maximum number of triplets 841 * @_rand: Buffer for RAND values 842 * @kc: Buffer for Kc values 843 * @sres: Buffer for SRES values 844 * @cb_session_ctx: Session callback context for get_complete_cb() 845 * Returns: Number of triplets received (has to be less than or equal to 846 * max_chal), -1 (EAP_SIM_DB_FAILURE) on error (e.g., user not found), or 847 * -2 (EAP_SIM_DB_PENDING) if results are not yet available. In this case, the 848 * callback function registered with eap_sim_db_init() will be called once the 849 * results become available. 850 * 851 * When using an external server for GSM triplets, this function can always 852 * start a request and return EAP_SIM_DB_PENDING immediately if authentication 853 * triplets are not available. Once the triplets are received, callback 854 * function registered with eap_sim_db_init() is called to notify EAP state 855 * machine to reprocess the message. This eap_sim_db_get_gsm_triplets() 856 * function will then be called again and the newly received triplets will then 857 * be given to the caller. 858 */ 859int eap_sim_db_get_gsm_triplets(struct eap_sim_db_data *data, 860 const char *username, int max_chal, 861 u8 *_rand, u8 *kc, u8 *sres, 862 void *cb_session_ctx) 863{ 864 struct eap_sim_db_pending *entry; 865 int len, ret; 866 char msg[40]; 867 const char *imsi; 868 size_t imsi_len; 869 870 if (username == NULL || username[0] != EAP_SIM_PERMANENT_PREFIX || 871 username[1] == '\0' || os_strlen(username) > sizeof(entry->imsi)) { 872 wpa_printf(MSG_DEBUG, "EAP-SIM DB: unexpected username '%s'", 873 username); 874 return EAP_SIM_DB_FAILURE; 875 } 876 imsi = username + 1; 877 wpa_printf(MSG_DEBUG, "EAP-SIM DB: Get GSM triplets for IMSI '%s'", 878 imsi); 879 880 entry = eap_sim_db_get_pending(data, imsi, 0); 881 if (entry) { 882 int num_chal; 883 if (entry->state == FAILURE) { 884 wpa_printf(MSG_DEBUG, "EAP-SIM DB: Pending entry -> " 885 "failure"); 886 os_free(entry); 887 return EAP_SIM_DB_FAILURE; 888 } 889 890 if (entry->state == PENDING) { 891 wpa_printf(MSG_DEBUG, "EAP-SIM DB: Pending entry -> " 892 "still pending"); 893 eap_sim_db_add_pending(data, entry); 894 return EAP_SIM_DB_PENDING; 895 } 896 897 wpa_printf(MSG_DEBUG, "EAP-SIM DB: Pending entry -> " 898 "%d challenges", entry->u.sim.num_chal); 899 num_chal = entry->u.sim.num_chal; 900 if (num_chal > max_chal) 901 num_chal = max_chal; 902 os_memcpy(_rand, entry->u.sim.rand, num_chal * GSM_RAND_LEN); 903 os_memcpy(sres, entry->u.sim.sres, 904 num_chal * EAP_SIM_SRES_LEN); 905 os_memcpy(kc, entry->u.sim.kc, num_chal * EAP_SIM_KC_LEN); 906 os_free(entry); 907 return num_chal; 908 } 909 910 if (data->sock < 0) { 911 if (eap_sim_db_open_socket(data) < 0) 912 return EAP_SIM_DB_FAILURE; 913 } 914 915 imsi_len = os_strlen(imsi); 916 len = os_snprintf(msg, sizeof(msg), "SIM-REQ-AUTH "); 917 if (len < 0 || len + imsi_len >= sizeof(msg)) 918 return EAP_SIM_DB_FAILURE; 919 os_memcpy(msg + len, imsi, imsi_len); 920 len += imsi_len; 921 ret = os_snprintf(msg + len, sizeof(msg) - len, " %d", max_chal); 922 if (ret < 0 || (size_t) ret >= sizeof(msg) - len) 923 return EAP_SIM_DB_FAILURE; 924 len += ret; 925 926 wpa_printf(MSG_DEBUG, "EAP-SIM DB: requesting SIM authentication " 927 "data for IMSI '%s'", imsi); 928 if (eap_sim_db_send(data, msg, len) < 0) 929 return EAP_SIM_DB_FAILURE; 930 931 entry = os_zalloc(sizeof(*entry)); 932 if (entry == NULL) 933 return EAP_SIM_DB_FAILURE; 934 935 os_get_time(&entry->timestamp); 936 os_strlcpy(entry->imsi, imsi, sizeof(entry->imsi)); 937 entry->cb_session_ctx = cb_session_ctx; 938 entry->state = PENDING; 939 eap_sim_db_add_pending(data, entry); 940 eap_sim_db_expire_pending(data); 941 942 return EAP_SIM_DB_PENDING; 943} 944 945 946static char * eap_sim_db_get_next(struct eap_sim_db_data *data, char prefix) 947{ 948 char *id, *pos, *end; 949 u8 buf[10]; 950 951 if (random_get_bytes(buf, sizeof(buf))) 952 return NULL; 953 id = os_malloc(sizeof(buf) * 2 + 2); 954 if (id == NULL) 955 return NULL; 956 957 pos = id; 958 end = id + sizeof(buf) * 2 + 2; 959 *pos++ = prefix; 960 pos += wpa_snprintf_hex(pos, end - pos, buf, sizeof(buf)); 961 962 return id; 963} 964 965 966/** 967 * eap_sim_db_get_next_pseudonym - EAP-SIM DB: Get next pseudonym 968 * @data: Private data pointer from eap_sim_db_init() 969 * @method: EAP method (SIM/AKA/AKA') 970 * Returns: Next pseudonym (allocated string) or %NULL on failure 971 * 972 * This function is used to generate a pseudonym for EAP-SIM. The returned 973 * pseudonym is not added to database at this point; it will need to be added 974 * with eap_sim_db_add_pseudonym() once the authentication has been completed 975 * successfully. Caller is responsible for freeing the returned buffer. 976 */ 977char * eap_sim_db_get_next_pseudonym(struct eap_sim_db_data *data, 978 enum eap_sim_db_method method) 979{ 980 char prefix = EAP_SIM_REAUTH_ID_PREFIX; 981 982 switch (method) { 983 case EAP_SIM_DB_SIM: 984 prefix = EAP_SIM_PSEUDONYM_PREFIX; 985 break; 986 case EAP_SIM_DB_AKA: 987 prefix = EAP_AKA_PSEUDONYM_PREFIX; 988 break; 989 case EAP_SIM_DB_AKA_PRIME: 990 prefix = EAP_AKA_PRIME_PSEUDONYM_PREFIX; 991 break; 992 } 993 994 return eap_sim_db_get_next(data, prefix); 995} 996 997 998/** 999 * eap_sim_db_get_next_reauth_id - EAP-SIM DB: Get next reauth_id 1000 * @data: Private data pointer from eap_sim_db_init() 1001 * @method: EAP method (SIM/AKA/AKA') 1002 * Returns: Next reauth_id (allocated string) or %NULL on failure 1003 * 1004 * This function is used to generate a fast re-authentication identity for 1005 * EAP-SIM. The returned reauth_id is not added to database at this point; it 1006 * will need to be added with eap_sim_db_add_reauth() once the authentication 1007 * has been completed successfully. Caller is responsible for freeing the 1008 * returned buffer. 1009 */ 1010char * eap_sim_db_get_next_reauth_id(struct eap_sim_db_data *data, 1011 enum eap_sim_db_method method) 1012{ 1013 char prefix = EAP_SIM_REAUTH_ID_PREFIX; 1014 1015 switch (method) { 1016 case EAP_SIM_DB_SIM: 1017 prefix = EAP_SIM_REAUTH_ID_PREFIX; 1018 break; 1019 case EAP_SIM_DB_AKA: 1020 prefix = EAP_AKA_REAUTH_ID_PREFIX; 1021 break; 1022 case EAP_SIM_DB_AKA_PRIME: 1023 prefix = EAP_AKA_PRIME_REAUTH_ID_PREFIX; 1024 break; 1025 } 1026 1027 return eap_sim_db_get_next(data, prefix); 1028} 1029 1030 1031/** 1032 * eap_sim_db_add_pseudonym - EAP-SIM DB: Add new pseudonym 1033 * @data: Private data pointer from eap_sim_db_init() 1034 * @permanent: Permanent username 1035 * @pseudonym: Pseudonym for this user. This needs to be an allocated buffer, 1036 * e.g., return value from eap_sim_db_get_next_pseudonym(). Caller must not 1037 * free it. 1038 * Returns: 0 on success, -1 on failure 1039 * 1040 * This function adds a new pseudonym for EAP-SIM user. EAP-SIM DB is 1041 * responsible of freeing pseudonym buffer once it is not needed anymore. 1042 */ 1043int eap_sim_db_add_pseudonym(struct eap_sim_db_data *data, 1044 const char *permanent, char *pseudonym) 1045{ 1046 struct eap_sim_pseudonym *p; 1047 wpa_printf(MSG_DEBUG, "EAP-SIM DB: Add pseudonym '%s' for permanent " 1048 "username '%s'", pseudonym, permanent); 1049 1050 /* TODO: could store last two pseudonyms */ 1051#ifdef CONFIG_SQLITE 1052 if (data->sqlite_db) 1053 return db_add_pseudonym(data, permanent, pseudonym); 1054#endif /* CONFIG_SQLITE */ 1055 for (p = data->pseudonyms; p; p = p->next) { 1056 if (os_strcmp(permanent, p->permanent) == 0) 1057 break; 1058 } 1059 if (p) { 1060 wpa_printf(MSG_DEBUG, "EAP-SIM DB: Replacing previous " 1061 "pseudonym: %s", p->pseudonym); 1062 os_free(p->pseudonym); 1063 p->pseudonym = pseudonym; 1064 return 0; 1065 } 1066 1067 p = os_zalloc(sizeof(*p)); 1068 if (p == NULL) { 1069 os_free(pseudonym); 1070 return -1; 1071 } 1072 1073 p->next = data->pseudonyms; 1074 p->permanent = os_strdup(permanent); 1075 if (p->permanent == NULL) { 1076 os_free(p); 1077 os_free(pseudonym); 1078 return -1; 1079 } 1080 p->pseudonym = pseudonym; 1081 data->pseudonyms = p; 1082 1083 wpa_printf(MSG_DEBUG, "EAP-SIM DB: Added new pseudonym entry"); 1084 return 0; 1085} 1086 1087 1088static struct eap_sim_reauth * 1089eap_sim_db_add_reauth_data(struct eap_sim_db_data *data, 1090 const char *permanent, 1091 char *reauth_id, u16 counter) 1092{ 1093 struct eap_sim_reauth *r; 1094 1095 for (r = data->reauths; r; r = r->next) { 1096 if (os_strcmp(r->permanent, permanent) == 0) 1097 break; 1098 } 1099 1100 if (r) { 1101 wpa_printf(MSG_DEBUG, "EAP-SIM DB: Replacing previous " 1102 "reauth_id: %s", r->reauth_id); 1103 os_free(r->reauth_id); 1104 r->reauth_id = reauth_id; 1105 } else { 1106 r = os_zalloc(sizeof(*r)); 1107 if (r == NULL) { 1108 os_free(reauth_id); 1109 return NULL; 1110 } 1111 1112 r->next = data->reauths; 1113 r->permanent = os_strdup(permanent); 1114 if (r->permanent == NULL) { 1115 os_free(r); 1116 os_free(reauth_id); 1117 return NULL; 1118 } 1119 r->reauth_id = reauth_id; 1120 data->reauths = r; 1121 wpa_printf(MSG_DEBUG, "EAP-SIM DB: Added new reauth entry"); 1122 } 1123 1124 r->counter = counter; 1125 1126 return r; 1127} 1128 1129 1130/** 1131 * eap_sim_db_add_reauth - EAP-SIM DB: Add new re-authentication entry 1132 * @priv: Private data pointer from eap_sim_db_init() 1133 * @permanent: Permanent username 1134 * @identity_len: Length of identity 1135 * @reauth_id: reauth_id for this user. This needs to be an allocated buffer, 1136 * e.g., return value from eap_sim_db_get_next_reauth_id(). Caller must not 1137 * free it. 1138 * @counter: AT_COUNTER value for fast re-authentication 1139 * @mk: 16-byte MK from the previous full authentication or %NULL 1140 * Returns: 0 on success, -1 on failure 1141 * 1142 * This function adds a new re-authentication entry for an EAP-SIM user. 1143 * EAP-SIM DB is responsible of freeing reauth_id buffer once it is not needed 1144 * anymore. 1145 */ 1146int eap_sim_db_add_reauth(struct eap_sim_db_data *data, const char *permanent, 1147 char *reauth_id, u16 counter, const u8 *mk) 1148{ 1149 struct eap_sim_reauth *r; 1150 1151 wpa_printf(MSG_DEBUG, "EAP-SIM DB: Add reauth_id '%s' for permanent " 1152 "identity '%s'", reauth_id, permanent); 1153 1154#ifdef CONFIG_SQLITE 1155 if (data->sqlite_db) 1156 return db_add_reauth(data, permanent, reauth_id, counter, mk, 1157 NULL, NULL, NULL); 1158#endif /* CONFIG_SQLITE */ 1159 r = eap_sim_db_add_reauth_data(data, permanent, reauth_id, counter); 1160 if (r == NULL) 1161 return -1; 1162 1163 os_memcpy(r->mk, mk, EAP_SIM_MK_LEN); 1164 1165 return 0; 1166} 1167 1168 1169#ifdef EAP_SERVER_AKA_PRIME 1170/** 1171 * eap_sim_db_add_reauth_prime - EAP-AKA' DB: Add new re-authentication entry 1172 * @data: Private data pointer from eap_sim_db_init() 1173 * @permanent: Permanent username 1174 * @reauth_id: reauth_id for this user. This needs to be an allocated buffer, 1175 * e.g., return value from eap_sim_db_get_next_reauth_id(). Caller must not 1176 * free it. 1177 * @counter: AT_COUNTER value for fast re-authentication 1178 * @k_encr: K_encr from the previous full authentication 1179 * @k_aut: K_aut from the previous full authentication 1180 * @k_re: 32-byte K_re from the previous full authentication 1181 * Returns: 0 on success, -1 on failure 1182 * 1183 * This function adds a new re-authentication entry for an EAP-AKA' user. 1184 * EAP-SIM DB is responsible of freeing reauth_id buffer once it is not needed 1185 * anymore. 1186 */ 1187int eap_sim_db_add_reauth_prime(struct eap_sim_db_data *data, 1188 const char *permanent, char *reauth_id, 1189 u16 counter, const u8 *k_encr, 1190 const u8 *k_aut, const u8 *k_re) 1191{ 1192 struct eap_sim_reauth *r; 1193 1194 wpa_printf(MSG_DEBUG, "EAP-SIM DB: Add reauth_id '%s' for permanent " 1195 "identity '%s'", reauth_id, permanent); 1196 1197#ifdef CONFIG_SQLITE 1198 if (data->sqlite_db) 1199 return db_add_reauth(data, permanent, reauth_id, counter, NULL, 1200 k_encr, k_aut, k_re); 1201#endif /* CONFIG_SQLITE */ 1202 r = eap_sim_db_add_reauth_data(data, permanent, reauth_id, counter); 1203 if (r == NULL) 1204 return -1; 1205 1206 os_memcpy(r->k_encr, k_encr, EAP_SIM_K_ENCR_LEN); 1207 os_memcpy(r->k_aut, k_aut, EAP_AKA_PRIME_K_AUT_LEN); 1208 os_memcpy(r->k_re, k_re, EAP_AKA_PRIME_K_RE_LEN); 1209 1210 return 0; 1211} 1212#endif /* EAP_SERVER_AKA_PRIME */ 1213 1214 1215/** 1216 * eap_sim_db_get_permanent - EAP-SIM DB: Get permanent identity 1217 * @data: Private data pointer from eap_sim_db_init() 1218 * @pseudonym: Pseudonym username 1219 * Returns: Pointer to permanent username or %NULL if not found 1220 */ 1221const char * 1222eap_sim_db_get_permanent(struct eap_sim_db_data *data, const char *pseudonym) 1223{ 1224 struct eap_sim_pseudonym *p; 1225 1226#ifdef CONFIG_SQLITE 1227 if (data->sqlite_db) 1228 return db_get_pseudonym(data, pseudonym); 1229#endif /* CONFIG_SQLITE */ 1230 1231 p = data->pseudonyms; 1232 while (p) { 1233 if (os_strcmp(p->pseudonym, pseudonym) == 0) 1234 return p->permanent; 1235 p = p->next; 1236 } 1237 1238 return NULL; 1239} 1240 1241 1242/** 1243 * eap_sim_db_get_reauth_entry - EAP-SIM DB: Get re-authentication entry 1244 * @data: Private data pointer from eap_sim_db_init() 1245 * @reauth_id: Fast re-authentication username 1246 * Returns: Pointer to the re-auth entry, or %NULL if not found 1247 */ 1248struct eap_sim_reauth * 1249eap_sim_db_get_reauth_entry(struct eap_sim_db_data *data, 1250 const char *reauth_id) 1251{ 1252 struct eap_sim_reauth *r; 1253 1254#ifdef CONFIG_SQLITE 1255 if (data->sqlite_db) 1256 return db_get_reauth(data, reauth_id); 1257#endif /* CONFIG_SQLITE */ 1258 1259 r = data->reauths; 1260 while (r) { 1261 if (os_strcmp(r->reauth_id, reauth_id) == 0) 1262 break; 1263 r = r->next; 1264 } 1265 1266 return r; 1267} 1268 1269 1270/** 1271 * eap_sim_db_remove_reauth - EAP-SIM DB: Remove re-authentication entry 1272 * @data: Private data pointer from eap_sim_db_init() 1273 * @reauth: Pointer to re-authentication entry from 1274 * eap_sim_db_get_reauth_entry() 1275 */ 1276void eap_sim_db_remove_reauth(struct eap_sim_db_data *data, 1277 struct eap_sim_reauth *reauth) 1278{ 1279 struct eap_sim_reauth *r, *prev = NULL; 1280#ifdef CONFIG_SQLITE 1281 if (data->sqlite_db) { 1282 db_remove_reauth(data, reauth); 1283 return; 1284 } 1285#endif /* CONFIG_SQLITE */ 1286 r = data->reauths; 1287 while (r) { 1288 if (r == reauth) { 1289 if (prev) 1290 prev->next = r->next; 1291 else 1292 data->reauths = r->next; 1293 eap_sim_db_free_reauth(r); 1294 return; 1295 } 1296 prev = r; 1297 r = r->next; 1298 } 1299} 1300 1301 1302/** 1303 * eap_sim_db_get_aka_auth - Get AKA authentication values 1304 * @data: Private data pointer from eap_sim_db_init() 1305 * @username: Permanent username (prefix | IMSI) 1306 * @_rand: Buffer for RAND value 1307 * @autn: Buffer for AUTN value 1308 * @ik: Buffer for IK value 1309 * @ck: Buffer for CK value 1310 * @res: Buffer for RES value 1311 * @res_len: Buffer for RES length 1312 * @cb_session_ctx: Session callback context for get_complete_cb() 1313 * Returns: 0 on success, -1 (EAP_SIM_DB_FAILURE) on error (e.g., user not 1314 * found), or -2 (EAP_SIM_DB_PENDING) if results are not yet available. In this 1315 * case, the callback function registered with eap_sim_db_init() will be 1316 * called once the results become available. 1317 * 1318 * When using an external server for AKA authentication, this function can 1319 * always start a request and return EAP_SIM_DB_PENDING immediately if 1320 * authentication triplets are not available. Once the authentication data are 1321 * received, callback function registered with eap_sim_db_init() is called to 1322 * notify EAP state machine to reprocess the message. This 1323 * eap_sim_db_get_aka_auth() function will then be called again and the newly 1324 * received triplets will then be given to the caller. 1325 */ 1326int eap_sim_db_get_aka_auth(struct eap_sim_db_data *data, const char *username, 1327 u8 *_rand, u8 *autn, u8 *ik, u8 *ck, 1328 u8 *res, size_t *res_len, void *cb_session_ctx) 1329{ 1330 struct eap_sim_db_pending *entry; 1331 int len; 1332 char msg[40]; 1333 const char *imsi; 1334 size_t imsi_len; 1335 1336 if (username == NULL || 1337 (username[0] != EAP_AKA_PERMANENT_PREFIX && 1338 username[0] != EAP_AKA_PRIME_PERMANENT_PREFIX) || 1339 username[1] == '\0' || os_strlen(username) > sizeof(entry->imsi)) { 1340 wpa_printf(MSG_DEBUG, "EAP-SIM DB: unexpected username '%s'", 1341 username); 1342 return EAP_SIM_DB_FAILURE; 1343 } 1344 imsi = username + 1; 1345 wpa_printf(MSG_DEBUG, "EAP-SIM DB: Get AKA auth for IMSI '%s'", 1346 imsi); 1347 1348 entry = eap_sim_db_get_pending(data, imsi, 1); 1349 if (entry) { 1350 if (entry->state == FAILURE) { 1351 os_free(entry); 1352 wpa_printf(MSG_DEBUG, "EAP-SIM DB: Failure"); 1353 return EAP_SIM_DB_FAILURE; 1354 } 1355 1356 if (entry->state == PENDING) { 1357 eap_sim_db_add_pending(data, entry); 1358 wpa_printf(MSG_DEBUG, "EAP-SIM DB: Pending"); 1359 return EAP_SIM_DB_PENDING; 1360 } 1361 1362 wpa_printf(MSG_DEBUG, "EAP-SIM DB: Returning successfully " 1363 "received authentication data"); 1364 os_memcpy(_rand, entry->u.aka.rand, EAP_AKA_RAND_LEN); 1365 os_memcpy(autn, entry->u.aka.autn, EAP_AKA_AUTN_LEN); 1366 os_memcpy(ik, entry->u.aka.ik, EAP_AKA_IK_LEN); 1367 os_memcpy(ck, entry->u.aka.ck, EAP_AKA_CK_LEN); 1368 os_memcpy(res, entry->u.aka.res, EAP_AKA_RES_MAX_LEN); 1369 *res_len = entry->u.aka.res_len; 1370 os_free(entry); 1371 return 0; 1372 } 1373 1374 if (data->sock < 0) { 1375 if (eap_sim_db_open_socket(data) < 0) 1376 return EAP_SIM_DB_FAILURE; 1377 } 1378 1379 imsi_len = os_strlen(imsi); 1380 len = os_snprintf(msg, sizeof(msg), "AKA-REQ-AUTH "); 1381 if (len < 0 || len + imsi_len >= sizeof(msg)) 1382 return EAP_SIM_DB_FAILURE; 1383 os_memcpy(msg + len, imsi, imsi_len); 1384 len += imsi_len; 1385 1386 wpa_printf(MSG_DEBUG, "EAP-SIM DB: requesting AKA authentication " 1387 "data for IMSI '%s'", imsi); 1388 if (eap_sim_db_send(data, msg, len) < 0) 1389 return EAP_SIM_DB_FAILURE; 1390 1391 entry = os_zalloc(sizeof(*entry)); 1392 if (entry == NULL) 1393 return EAP_SIM_DB_FAILURE; 1394 1395 os_get_time(&entry->timestamp); 1396 entry->aka = 1; 1397 os_strlcpy(entry->imsi, imsi, sizeof(entry->imsi)); 1398 entry->cb_session_ctx = cb_session_ctx; 1399 entry->state = PENDING; 1400 eap_sim_db_add_pending(data, entry); 1401 eap_sim_db_expire_pending(data); 1402 1403 return EAP_SIM_DB_PENDING; 1404} 1405 1406 1407/** 1408 * eap_sim_db_resynchronize - Resynchronize AKA AUTN 1409 * @data: Private data pointer from eap_sim_db_init() 1410 * @username: Permanent username 1411 * @auts: AUTS value from the peer 1412 * @_rand: RAND value used in the rejected message 1413 * Returns: 0 on success, -1 on failure 1414 * 1415 * This function is called when the peer reports synchronization failure in the 1416 * AUTN value by sending AUTS. The AUTS and RAND values should be sent to 1417 * HLR/AuC to allow it to resynchronize with the peer. After this, 1418 * eap_sim_db_get_aka_auth() will be called again to to fetch updated 1419 * RAND/AUTN values for the next challenge. 1420 */ 1421int eap_sim_db_resynchronize(struct eap_sim_db_data *data, 1422 const char *username, 1423 const u8 *auts, const u8 *_rand) 1424{ 1425 const char *imsi; 1426 size_t imsi_len; 1427 1428 if (username == NULL || 1429 (username[0] != EAP_AKA_PERMANENT_PREFIX && 1430 username[0] != EAP_AKA_PRIME_PERMANENT_PREFIX) || 1431 username[1] == '\0' || os_strlen(username) > 20) { 1432 wpa_printf(MSG_DEBUG, "EAP-SIM DB: unexpected username '%s'", 1433 username); 1434 return -1; 1435 } 1436 imsi = username + 1; 1437 wpa_printf(MSG_DEBUG, "EAP-SIM DB: Get AKA auth for IMSI '%s'", 1438 imsi); 1439 1440 if (data->sock >= 0) { 1441 char msg[100]; 1442 int len, ret; 1443 1444 imsi_len = os_strlen(imsi); 1445 len = os_snprintf(msg, sizeof(msg), "AKA-AUTS "); 1446 if (len < 0 || len + imsi_len >= sizeof(msg)) 1447 return -1; 1448 os_memcpy(msg + len, imsi, imsi_len); 1449 len += imsi_len; 1450 1451 ret = os_snprintf(msg + len, sizeof(msg) - len, " "); 1452 if (ret < 0 || (size_t) ret >= sizeof(msg) - len) 1453 return -1; 1454 len += ret; 1455 len += wpa_snprintf_hex(msg + len, sizeof(msg) - len, 1456 auts, EAP_AKA_AUTS_LEN); 1457 ret = os_snprintf(msg + len, sizeof(msg) - len, " "); 1458 if (ret < 0 || (size_t) ret >= sizeof(msg) - len) 1459 return -1; 1460 len += ret; 1461 len += wpa_snprintf_hex(msg + len, sizeof(msg) - len, 1462 _rand, EAP_AKA_RAND_LEN); 1463 wpa_printf(MSG_DEBUG, "EAP-SIM DB: reporting AKA AUTS for " 1464 "IMSI '%s'", imsi); 1465 if (eap_sim_db_send(data, msg, len) < 0) 1466 return -1; 1467 } 1468 1469 return 0; 1470} 1471 1472 1473/** 1474 * sim_get_username - Extract username from SIM identity 1475 * @identity: Identity 1476 * @identity_len: Identity length 1477 * Returns: Allocated buffer with the username part of the identity 1478 * 1479 * Caller is responsible for freeing the returned buffer with os_free(). 1480 */ 1481char * sim_get_username(const u8 *identity, size_t identity_len) 1482{ 1483 size_t pos; 1484 1485 if (identity == NULL) 1486 return NULL; 1487 1488 for (pos = 0; pos < identity_len; pos++) { 1489 if (identity[pos] == '@' || identity[pos] == '\0') 1490 break; 1491 } 1492 1493 return dup_binstr(identity, pos); 1494} 1495