hlr_auc_gw.c revision 61d9df3e62aaa0e87ad05452fcb95142159a17b6
1/* 2 * HLR/AuC testing gateway for hostapd EAP-SIM/AKA database/authenticator 3 * Copyright (c) 2005-2007, 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 to HLR/AuC. It is expected to be replaced with an 10 * implementation of SS7 gateway to GSM/UMTS authentication center (HLR/AuC) or 11 * a local implementation of SIM triplet and AKA authentication data generator. 12 * 13 * hostapd will send SIM/AKA authentication queries over a UNIX domain socket 14 * to and external program, e.g., this hlr_auc_gw. This interface uses simple 15 * text-based format: 16 * 17 * EAP-SIM / GSM triplet query/response: 18 * SIM-REQ-AUTH <IMSI> <max_chal> 19 * SIM-RESP-AUTH <IMSI> Kc1:SRES1:RAND1 Kc2:SRES2:RAND2 [Kc3:SRES3:RAND3] 20 * SIM-RESP-AUTH <IMSI> FAILURE 21 * 22 * EAP-AKA / UMTS query/response: 23 * AKA-REQ-AUTH <IMSI> 24 * AKA-RESP-AUTH <IMSI> <RAND> <AUTN> <IK> <CK> <RES> 25 * AKA-RESP-AUTH <IMSI> FAILURE 26 * 27 * EAP-AKA / UMTS AUTS (re-synchronization): 28 * AKA-AUTS <IMSI> <AUTS> <RAND> 29 * 30 * IMSI and max_chal are sent as an ASCII string, 31 * Kc/SRES/RAND/AUTN/IK/CK/RES/AUTS as hex strings. 32 * 33 * The example implementation here reads GSM authentication triplets from a 34 * text file in IMSI:Kc:SRES:RAND format, IMSI in ASCII, other fields as hex 35 * strings. This is used to simulate an HLR/AuC. As such, it is not very useful 36 * for real life authentication, but it is useful both as an example 37 * implementation and for EAP-SIM/AKA/AKA' testing. 38 * 39 * SQN generation follows the not time-based Profile 2 described in 40 * 3GPP TS 33.102 Annex C.3.2. The length of IND is 5 bits by default, but this 41 * can be changed with a command line options if needed. 42 */ 43 44#include "includes.h" 45#include <sys/un.h> 46#ifdef CONFIG_SQLITE 47#include <sqlite3.h> 48#endif /* CONFIG_SQLITE */ 49 50#include "common.h" 51#include "crypto/milenage.h" 52#include "crypto/random.h" 53 54static const char *default_socket_path = "/tmp/hlr_auc_gw.sock"; 55static const char *socket_path; 56static int serv_sock = -1; 57static char *milenage_file = NULL; 58static int update_milenage = 0; 59static int sqn_changes = 0; 60static int ind_len = 5; 61 62/* GSM triplets */ 63struct gsm_triplet { 64 struct gsm_triplet *next; 65 char imsi[20]; 66 u8 kc[8]; 67 u8 sres[4]; 68 u8 _rand[16]; 69}; 70 71static struct gsm_triplet *gsm_db = NULL, *gsm_db_pos = NULL; 72 73/* OPc and AMF parameters for Milenage (Example algorithms for AKA). */ 74struct milenage_parameters { 75 struct milenage_parameters *next; 76 char imsi[20]; 77 u8 ki[16]; 78 u8 opc[16]; 79 u8 amf[2]; 80 u8 sqn[6]; 81}; 82 83static struct milenage_parameters *milenage_db = NULL; 84 85#define EAP_SIM_MAX_CHAL 3 86 87#define EAP_AKA_RAND_LEN 16 88#define EAP_AKA_AUTN_LEN 16 89#define EAP_AKA_AUTS_LEN 14 90#define EAP_AKA_RES_MAX_LEN 16 91#define EAP_AKA_IK_LEN 16 92#define EAP_AKA_CK_LEN 16 93 94 95#ifdef CONFIG_SQLITE 96 97static sqlite3 *sqlite_db = NULL; 98static struct milenage_parameters db_tmp_milenage; 99 100 101static int db_table_exists(sqlite3 *db, const char *name) 102{ 103 char cmd[128]; 104 os_snprintf(cmd, sizeof(cmd), "SELECT 1 FROM %s;", name); 105 return sqlite3_exec(db, cmd, NULL, NULL, NULL) == SQLITE_OK; 106} 107 108 109static int db_table_create_milenage(sqlite3 *db) 110{ 111 char *err = NULL; 112 const char *sql = 113 "CREATE TABLE milenage(" 114 " imsi INTEGER PRIMARY KEY NOT NULL," 115 " ki CHAR(32) NOT NULL," 116 " opc CHAR(32) NOT NULL," 117 " amf CHAR(4) NOT NULL," 118 " sqn CHAR(12) NOT NULL" 119 ");"; 120 121 printf("Adding database table for milenage information\n"); 122 if (sqlite3_exec(db, sql, NULL, NULL, &err) != SQLITE_OK) { 123 printf("SQLite error: %s\n", err); 124 sqlite3_free(err); 125 return -1; 126 } 127 128 return 0; 129} 130 131 132static sqlite3 * db_open(const char *db_file) 133{ 134 sqlite3 *db; 135 136 if (sqlite3_open(db_file, &db)) { 137 printf("Failed to open database %s: %s\n", 138 db_file, sqlite3_errmsg(db)); 139 sqlite3_close(db); 140 return NULL; 141 } 142 143 if (!db_table_exists(db, "milenage") && 144 db_table_create_milenage(db) < 0) { 145 sqlite3_close(db); 146 return NULL; 147 } 148 149 return db; 150} 151 152 153static int get_milenage_cb(void *ctx, int argc, char *argv[], char *col[]) 154{ 155 struct milenage_parameters *m = ctx; 156 int i; 157 158 for (i = 0; i < argc; i++) { 159 if (os_strcmp(col[i], "ki") == 0 && argv[i] && 160 hexstr2bin(argv[i], m->ki, sizeof(m->ki))) { 161 printf("Invalid ki value in database\n"); 162 return -1; 163 } 164 165 if (os_strcmp(col[i], "opc") == 0 && argv[i] && 166 hexstr2bin(argv[i], m->opc, sizeof(m->opc))) { 167 printf("Invalid opcvalue in database\n"); 168 return -1; 169 } 170 171 if (os_strcmp(col[i], "amf") == 0 && argv[i] && 172 hexstr2bin(argv[i], m->amf, sizeof(m->amf))) { 173 printf("Invalid amf value in database\n"); 174 return -1; 175 } 176 177 if (os_strcmp(col[i], "sqn") == 0 && argv[i] && 178 hexstr2bin(argv[i], m->sqn, sizeof(m->sqn))) { 179 printf("Invalid sqn value in database\n"); 180 return -1; 181 } 182 } 183 184 return 0; 185} 186 187 188static struct milenage_parameters * db_get_milenage(const char *imsi_txt) 189{ 190 char cmd[128]; 191 unsigned long long imsi; 192 193 os_memset(&db_tmp_milenage, 0, sizeof(db_tmp_milenage)); 194 imsi = atoll(imsi_txt); 195 os_snprintf(db_tmp_milenage.imsi, sizeof(db_tmp_milenage.imsi), 196 "%llu", imsi); 197 os_snprintf(cmd, sizeof(cmd), 198 "SELECT ki,opc,amf,sqn FROM milenage WHERE imsi=%llu;", 199 imsi); 200 if (sqlite3_exec(sqlite_db, cmd, get_milenage_cb, &db_tmp_milenage, 201 NULL) != SQLITE_OK) 202 return NULL; 203 204 return &db_tmp_milenage; 205} 206 207 208static int db_update_milenage_sqn(struct milenage_parameters *m) 209{ 210 char cmd[128], val[13], *pos; 211 212 pos = val; 213 pos += wpa_snprintf_hex(pos, sizeof(val), m->sqn, 6); 214 *pos = '\0'; 215 os_snprintf(cmd, sizeof(cmd), 216 "UPDATE milenage SET sqn='%s' WHERE imsi=%s;", 217 val, m->imsi); 218 if (sqlite3_exec(sqlite_db, cmd, NULL, NULL, NULL) != SQLITE_OK) { 219 printf("Failed to update SQN in database for IMSI %s\n", 220 m->imsi); 221 return -1; 222 } 223 return 0; 224} 225 226#endif /* CONFIG_SQLITE */ 227 228 229static int open_socket(const char *path) 230{ 231 struct sockaddr_un addr; 232 int s; 233 234 s = socket(PF_UNIX, SOCK_DGRAM, 0); 235 if (s < 0) { 236 perror("socket(PF_UNIX)"); 237 return -1; 238 } 239 240 memset(&addr, 0, sizeof(addr)); 241 addr.sun_family = AF_UNIX; 242 os_strlcpy(addr.sun_path, path, sizeof(addr.sun_path)); 243 if (bind(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) { 244 perror("hlr-auc-gw: bind(PF_UNIX)"); 245 close(s); 246 return -1; 247 } 248 249 return s; 250} 251 252 253static int read_gsm_triplets(const char *fname) 254{ 255 FILE *f; 256 char buf[200], *pos, *pos2; 257 struct gsm_triplet *g = NULL; 258 int line, ret = 0; 259 260 if (fname == NULL) 261 return -1; 262 263 f = fopen(fname, "r"); 264 if (f == NULL) { 265 printf("Could not open GSM tripler data file '%s'\n", fname); 266 return -1; 267 } 268 269 line = 0; 270 while (fgets(buf, sizeof(buf), f)) { 271 line++; 272 273 /* Parse IMSI:Kc:SRES:RAND */ 274 buf[sizeof(buf) - 1] = '\0'; 275 if (buf[0] == '#') 276 continue; 277 pos = buf; 278 while (*pos != '\0' && *pos != '\n') 279 pos++; 280 if (*pos == '\n') 281 *pos = '\0'; 282 pos = buf; 283 if (*pos == '\0') 284 continue; 285 286 g = os_zalloc(sizeof(*g)); 287 if (g == NULL) { 288 ret = -1; 289 break; 290 } 291 292 /* IMSI */ 293 pos2 = strchr(pos, ':'); 294 if (pos2 == NULL) { 295 printf("%s:%d - Invalid IMSI (%s)\n", 296 fname, line, pos); 297 ret = -1; 298 break; 299 } 300 *pos2 = '\0'; 301 if (strlen(pos) >= sizeof(g->imsi)) { 302 printf("%s:%d - Too long IMSI (%s)\n", 303 fname, line, pos); 304 ret = -1; 305 break; 306 } 307 os_strlcpy(g->imsi, pos, sizeof(g->imsi)); 308 pos = pos2 + 1; 309 310 /* Kc */ 311 pos2 = strchr(pos, ':'); 312 if (pos2 == NULL) { 313 printf("%s:%d - Invalid Kc (%s)\n", fname, line, pos); 314 ret = -1; 315 break; 316 } 317 *pos2 = '\0'; 318 if (strlen(pos) != 16 || hexstr2bin(pos, g->kc, 8)) { 319 printf("%s:%d - Invalid Kc (%s)\n", fname, line, pos); 320 ret = -1; 321 break; 322 } 323 pos = pos2 + 1; 324 325 /* SRES */ 326 pos2 = strchr(pos, ':'); 327 if (pos2 == NULL) { 328 printf("%s:%d - Invalid SRES (%s)\n", fname, line, 329 pos); 330 ret = -1; 331 break; 332 } 333 *pos2 = '\0'; 334 if (strlen(pos) != 8 || hexstr2bin(pos, g->sres, 4)) { 335 printf("%s:%d - Invalid SRES (%s)\n", fname, line, 336 pos); 337 ret = -1; 338 break; 339 } 340 pos = pos2 + 1; 341 342 /* RAND */ 343 pos2 = strchr(pos, ':'); 344 if (pos2) 345 *pos2 = '\0'; 346 if (strlen(pos) != 32 || hexstr2bin(pos, g->_rand, 16)) { 347 printf("%s:%d - Invalid RAND (%s)\n", fname, line, 348 pos); 349 ret = -1; 350 break; 351 } 352 pos = pos2 + 1; 353 354 g->next = gsm_db; 355 gsm_db = g; 356 g = NULL; 357 } 358 os_free(g); 359 360 fclose(f); 361 362 return ret; 363} 364 365 366static struct gsm_triplet * get_gsm_triplet(const char *imsi) 367{ 368 struct gsm_triplet *g = gsm_db_pos; 369 370 while (g) { 371 if (strcmp(g->imsi, imsi) == 0) { 372 gsm_db_pos = g->next; 373 return g; 374 } 375 g = g->next; 376 } 377 378 g = gsm_db; 379 while (g && g != gsm_db_pos) { 380 if (strcmp(g->imsi, imsi) == 0) { 381 gsm_db_pos = g->next; 382 return g; 383 } 384 g = g->next; 385 } 386 387 return NULL; 388} 389 390 391static int read_milenage(const char *fname) 392{ 393 FILE *f; 394 char buf[200], *pos, *pos2; 395 struct milenage_parameters *m = NULL; 396 int line, ret = 0; 397 398 if (fname == NULL) 399 return -1; 400 401 f = fopen(fname, "r"); 402 if (f == NULL) { 403 printf("Could not open Milenage data file '%s'\n", fname); 404 return -1; 405 } 406 407 line = 0; 408 while (fgets(buf, sizeof(buf), f)) { 409 line++; 410 411 /* Parse IMSI Ki OPc AMF SQN */ 412 buf[sizeof(buf) - 1] = '\0'; 413 if (buf[0] == '#') 414 continue; 415 pos = buf; 416 while (*pos != '\0' && *pos != '\n') 417 pos++; 418 if (*pos == '\n') 419 *pos = '\0'; 420 pos = buf; 421 if (*pos == '\0') 422 continue; 423 424 m = os_zalloc(sizeof(*m)); 425 if (m == NULL) { 426 ret = -1; 427 break; 428 } 429 430 /* IMSI */ 431 pos2 = strchr(pos, ' '); 432 if (pos2 == NULL) { 433 printf("%s:%d - Invalid IMSI (%s)\n", 434 fname, line, pos); 435 ret = -1; 436 break; 437 } 438 *pos2 = '\0'; 439 if (strlen(pos) >= sizeof(m->imsi)) { 440 printf("%s:%d - Too long IMSI (%s)\n", 441 fname, line, pos); 442 ret = -1; 443 break; 444 } 445 os_strlcpy(m->imsi, pos, sizeof(m->imsi)); 446 pos = pos2 + 1; 447 448 /* Ki */ 449 pos2 = strchr(pos, ' '); 450 if (pos2 == NULL) { 451 printf("%s:%d - Invalid Ki (%s)\n", fname, line, pos); 452 ret = -1; 453 break; 454 } 455 *pos2 = '\0'; 456 if (strlen(pos) != 32 || hexstr2bin(pos, m->ki, 16)) { 457 printf("%s:%d - Invalid Ki (%s)\n", fname, line, pos); 458 ret = -1; 459 break; 460 } 461 pos = pos2 + 1; 462 463 /* OPc */ 464 pos2 = strchr(pos, ' '); 465 if (pos2 == NULL) { 466 printf("%s:%d - Invalid OPc (%s)\n", fname, line, pos); 467 ret = -1; 468 break; 469 } 470 *pos2 = '\0'; 471 if (strlen(pos) != 32 || hexstr2bin(pos, m->opc, 16)) { 472 printf("%s:%d - Invalid OPc (%s)\n", fname, line, pos); 473 ret = -1; 474 break; 475 } 476 pos = pos2 + 1; 477 478 /* AMF */ 479 pos2 = strchr(pos, ' '); 480 if (pos2 == NULL) { 481 printf("%s:%d - Invalid AMF (%s)\n", fname, line, pos); 482 ret = -1; 483 break; 484 } 485 *pos2 = '\0'; 486 if (strlen(pos) != 4 || hexstr2bin(pos, m->amf, 2)) { 487 printf("%s:%d - Invalid AMF (%s)\n", fname, line, pos); 488 ret = -1; 489 break; 490 } 491 pos = pos2 + 1; 492 493 /* SQN */ 494 pos2 = strchr(pos, ' '); 495 if (pos2) 496 *pos2 = '\0'; 497 if (strlen(pos) != 12 || hexstr2bin(pos, m->sqn, 6)) { 498 printf("%s:%d - Invalid SEQ (%s)\n", fname, line, pos); 499 ret = -1; 500 break; 501 } 502 pos = pos2 + 1; 503 504 m->next = milenage_db; 505 milenage_db = m; 506 m = NULL; 507 } 508 os_free(m); 509 510 fclose(f); 511 512 return ret; 513} 514 515 516static void update_milenage_file(const char *fname) 517{ 518 FILE *f, *f2; 519 char buf[500], *pos; 520 char *end = buf + sizeof(buf); 521 struct milenage_parameters *m; 522 size_t imsi_len; 523 524 f = fopen(fname, "r"); 525 if (f == NULL) { 526 printf("Could not open Milenage data file '%s'\n", fname); 527 return; 528 } 529 530 snprintf(buf, sizeof(buf), "%s.new", fname); 531 f2 = fopen(buf, "w"); 532 if (f2 == NULL) { 533 printf("Could not write Milenage data file '%s'\n", buf); 534 fclose(f); 535 return; 536 } 537 538 while (fgets(buf, sizeof(buf), f)) { 539 /* IMSI Ki OPc AMF SQN */ 540 buf[sizeof(buf) - 1] = '\0'; 541 542 pos = strchr(buf, ' '); 543 if (buf[0] == '#' || pos == NULL || pos - buf >= 20) 544 goto no_update; 545 546 imsi_len = pos - buf; 547 548 for (m = milenage_db; m; m = m->next) { 549 if (strncmp(buf, m->imsi, imsi_len) == 0 && 550 m->imsi[imsi_len] == '\0') 551 break; 552 } 553 554 if (!m) 555 goto no_update; 556 557 pos = buf; 558 pos += snprintf(pos, end - pos, "%s ", m->imsi); 559 pos += wpa_snprintf_hex(pos, end - pos, m->ki, 16); 560 *pos++ = ' '; 561 pos += wpa_snprintf_hex(pos, end - pos, m->opc, 16); 562 *pos++ = ' '; 563 pos += wpa_snprintf_hex(pos, end - pos, m->amf, 2); 564 *pos++ = ' '; 565 pos += wpa_snprintf_hex(pos, end - pos, m->sqn, 6); 566 *pos++ = '\n'; 567 568 no_update: 569 fprintf(f2, "%s", buf); 570 } 571 572 fclose(f2); 573 fclose(f); 574 575 snprintf(buf, sizeof(buf), "%s.bak", fname); 576 if (rename(fname, buf) < 0) { 577 perror("rename"); 578 return; 579 } 580 581 snprintf(buf, sizeof(buf), "%s.new", fname); 582 if (rename(buf, fname) < 0) { 583 perror("rename"); 584 return; 585 } 586 587} 588 589 590static struct milenage_parameters * get_milenage(const char *imsi) 591{ 592 struct milenage_parameters *m = milenage_db; 593 594 while (m) { 595 if (strcmp(m->imsi, imsi) == 0) 596 break; 597 m = m->next; 598 } 599 600#ifdef CONFIG_SQLITE 601 if (!m) 602 m = db_get_milenage(imsi); 603#endif /* CONFIG_SQLITE */ 604 605 return m; 606} 607 608 609static void sim_req_auth(int s, struct sockaddr_un *from, socklen_t fromlen, 610 char *imsi) 611{ 612 int count, max_chal, ret; 613 char *pos; 614 char reply[1000], *rpos, *rend; 615 struct milenage_parameters *m; 616 struct gsm_triplet *g; 617 618 reply[0] = '\0'; 619 620 pos = strchr(imsi, ' '); 621 if (pos) { 622 *pos++ = '\0'; 623 max_chal = atoi(pos); 624 if (max_chal < 1 || max_chal < EAP_SIM_MAX_CHAL) 625 max_chal = EAP_SIM_MAX_CHAL; 626 } else 627 max_chal = EAP_SIM_MAX_CHAL; 628 629 rend = &reply[sizeof(reply)]; 630 rpos = reply; 631 ret = snprintf(rpos, rend - rpos, "SIM-RESP-AUTH %s", imsi); 632 if (ret < 0 || ret >= rend - rpos) 633 return; 634 rpos += ret; 635 636 m = get_milenage(imsi); 637 if (m) { 638 u8 _rand[16], sres[4], kc[8]; 639 for (count = 0; count < max_chal; count++) { 640 if (random_get_bytes(_rand, 16) < 0) 641 return; 642 gsm_milenage(m->opc, m->ki, _rand, sres, kc); 643 *rpos++ = ' '; 644 rpos += wpa_snprintf_hex(rpos, rend - rpos, kc, 8); 645 *rpos++ = ':'; 646 rpos += wpa_snprintf_hex(rpos, rend - rpos, sres, 4); 647 *rpos++ = ':'; 648 rpos += wpa_snprintf_hex(rpos, rend - rpos, _rand, 16); 649 } 650 *rpos = '\0'; 651 goto send; 652 } 653 654 count = 0; 655 while (count < max_chal && (g = get_gsm_triplet(imsi))) { 656 if (strcmp(g->imsi, imsi) != 0) 657 continue; 658 659 if (rpos < rend) 660 *rpos++ = ' '; 661 rpos += wpa_snprintf_hex(rpos, rend - rpos, g->kc, 8); 662 if (rpos < rend) 663 *rpos++ = ':'; 664 rpos += wpa_snprintf_hex(rpos, rend - rpos, g->sres, 4); 665 if (rpos < rend) 666 *rpos++ = ':'; 667 rpos += wpa_snprintf_hex(rpos, rend - rpos, g->_rand, 16); 668 count++; 669 } 670 671 if (count == 0) { 672 printf("No GSM triplets found for %s\n", imsi); 673 ret = snprintf(rpos, rend - rpos, " FAILURE"); 674 if (ret < 0 || ret >= rend - rpos) 675 return; 676 rpos += ret; 677 } 678 679send: 680 printf("Send: %s\n", reply); 681 if (sendto(s, reply, rpos - reply, 0, 682 (struct sockaddr *) from, fromlen) < 0) 683 perror("send"); 684} 685 686 687static void inc_sqn(u8 *sqn) 688{ 689 u64 val, seq, ind; 690 691 /* 692 * SQN = SEQ | IND = SEQ1 | SEQ2 | IND 693 * 694 * The mechanism used here is not time-based, so SEQ2 is void and 695 * SQN = SEQ1 | IND. The length of IND is ind_len bits and the length 696 * of SEQ1 is 48 - ind_len bits. 697 */ 698 699 /* Increment both SEQ and IND by one */ 700 val = ((u64) WPA_GET_BE32(sqn) << 16) | ((u64) WPA_GET_BE16(sqn + 4)); 701 seq = (val >> ind_len) + 1; 702 ind = (val + 1) & ((1 << ind_len) - 1); 703 val = (seq << ind_len) | ind; 704 WPA_PUT_BE32(sqn, val >> 16); 705 WPA_PUT_BE16(sqn + 4, val & 0xffff); 706} 707 708 709static void aka_req_auth(int s, struct sockaddr_un *from, socklen_t fromlen, 710 char *imsi) 711{ 712 /* AKA-RESP-AUTH <IMSI> <RAND> <AUTN> <IK> <CK> <RES> */ 713 char reply[1000], *pos, *end; 714 u8 _rand[EAP_AKA_RAND_LEN]; 715 u8 autn[EAP_AKA_AUTN_LEN]; 716 u8 ik[EAP_AKA_IK_LEN]; 717 u8 ck[EAP_AKA_CK_LEN]; 718 u8 res[EAP_AKA_RES_MAX_LEN]; 719 size_t res_len; 720 int ret; 721 struct milenage_parameters *m; 722 int failed = 0; 723 724 m = get_milenage(imsi); 725 if (m) { 726 if (random_get_bytes(_rand, EAP_AKA_RAND_LEN) < 0) 727 return; 728 res_len = EAP_AKA_RES_MAX_LEN; 729 inc_sqn(m->sqn); 730#ifdef CONFIG_SQLITE 731 db_update_milenage_sqn(m); 732#endif /* CONFIG_SQLITE */ 733 sqn_changes = 1; 734 printf("AKA: Milenage with SQN=%02x%02x%02x%02x%02x%02x\n", 735 m->sqn[0], m->sqn[1], m->sqn[2], 736 m->sqn[3], m->sqn[4], m->sqn[5]); 737 milenage_generate(m->opc, m->amf, m->ki, m->sqn, _rand, 738 autn, ik, ck, res, &res_len); 739 } else { 740 printf("Unknown IMSI: %s\n", imsi); 741#ifdef AKA_USE_FIXED_TEST_VALUES 742 printf("Using fixed test values for AKA\n"); 743 memset(_rand, '0', EAP_AKA_RAND_LEN); 744 memset(autn, '1', EAP_AKA_AUTN_LEN); 745 memset(ik, '3', EAP_AKA_IK_LEN); 746 memset(ck, '4', EAP_AKA_CK_LEN); 747 memset(res, '2', EAP_AKA_RES_MAX_LEN); 748 res_len = EAP_AKA_RES_MAX_LEN; 749#else /* AKA_USE_FIXED_TEST_VALUES */ 750 failed = 1; 751#endif /* AKA_USE_FIXED_TEST_VALUES */ 752 } 753 754 pos = reply; 755 end = &reply[sizeof(reply)]; 756 ret = snprintf(pos, end - pos, "AKA-RESP-AUTH %s ", imsi); 757 if (ret < 0 || ret >= end - pos) 758 return; 759 pos += ret; 760 if (failed) { 761 ret = snprintf(pos, end - pos, "FAILURE"); 762 if (ret < 0 || ret >= end - pos) 763 return; 764 pos += ret; 765 goto done; 766 } 767 pos += wpa_snprintf_hex(pos, end - pos, _rand, EAP_AKA_RAND_LEN); 768 *pos++ = ' '; 769 pos += wpa_snprintf_hex(pos, end - pos, autn, EAP_AKA_AUTN_LEN); 770 *pos++ = ' '; 771 pos += wpa_snprintf_hex(pos, end - pos, ik, EAP_AKA_IK_LEN); 772 *pos++ = ' '; 773 pos += wpa_snprintf_hex(pos, end - pos, ck, EAP_AKA_CK_LEN); 774 *pos++ = ' '; 775 pos += wpa_snprintf_hex(pos, end - pos, res, res_len); 776 777done: 778 printf("Send: %s\n", reply); 779 780 if (sendto(s, reply, pos - reply, 0, (struct sockaddr *) from, 781 fromlen) < 0) 782 perror("send"); 783} 784 785 786static void aka_auts(int s, struct sockaddr_un *from, socklen_t fromlen, 787 char *imsi) 788{ 789 char *auts, *__rand; 790 u8 _auts[EAP_AKA_AUTS_LEN], _rand[EAP_AKA_RAND_LEN], sqn[6]; 791 struct milenage_parameters *m; 792 793 /* AKA-AUTS <IMSI> <AUTS> <RAND> */ 794 795 auts = strchr(imsi, ' '); 796 if (auts == NULL) 797 return; 798 *auts++ = '\0'; 799 800 __rand = strchr(auts, ' '); 801 if (__rand == NULL) 802 return; 803 *__rand++ = '\0'; 804 805 printf("AKA-AUTS: IMSI=%s AUTS=%s RAND=%s\n", imsi, auts, __rand); 806 if (hexstr2bin(auts, _auts, EAP_AKA_AUTS_LEN) || 807 hexstr2bin(__rand, _rand, EAP_AKA_RAND_LEN)) { 808 printf("Could not parse AUTS/RAND\n"); 809 return; 810 } 811 812 m = get_milenage(imsi); 813 if (m == NULL) { 814 printf("Unknown IMSI: %s\n", imsi); 815 return; 816 } 817 818 if (milenage_auts(m->opc, m->ki, _rand, _auts, sqn)) { 819 printf("AKA-AUTS: Incorrect MAC-S\n"); 820 } else { 821 memcpy(m->sqn, sqn, 6); 822 printf("AKA-AUTS: Re-synchronized: " 823 "SQN=%02x%02x%02x%02x%02x%02x\n", 824 sqn[0], sqn[1], sqn[2], sqn[3], sqn[4], sqn[5]); 825#ifdef CONFIG_SQLITE 826 db_update_milenage_sqn(m); 827#endif /* CONFIG_SQLITE */ 828 sqn_changes = 1; 829 } 830} 831 832 833static int process(int s) 834{ 835 char buf[1000]; 836 struct sockaddr_un from; 837 socklen_t fromlen; 838 ssize_t res; 839 840 fromlen = sizeof(from); 841 res = recvfrom(s, buf, sizeof(buf), 0, (struct sockaddr *) &from, 842 &fromlen); 843 if (res < 0) { 844 perror("recvfrom"); 845 return -1; 846 } 847 848 if (res == 0) 849 return 0; 850 851 if ((size_t) res >= sizeof(buf)) 852 res = sizeof(buf) - 1; 853 buf[res] = '\0'; 854 855 printf("Received: %s\n", buf); 856 857 if (strncmp(buf, "SIM-REQ-AUTH ", 13) == 0) 858 sim_req_auth(s, &from, fromlen, buf + 13); 859 else if (strncmp(buf, "AKA-REQ-AUTH ", 13) == 0) 860 aka_req_auth(s, &from, fromlen, buf + 13); 861 else if (strncmp(buf, "AKA-AUTS ", 9) == 0) 862 aka_auts(s, &from, fromlen, buf + 9); 863 else 864 printf("Unknown request: %s\n", buf); 865 866 return 0; 867} 868 869 870static void cleanup(void) 871{ 872 struct gsm_triplet *g, *gprev; 873 struct milenage_parameters *m, *prev; 874 875 if (update_milenage && milenage_file && sqn_changes) 876 update_milenage_file(milenage_file); 877 878 g = gsm_db; 879 while (g) { 880 gprev = g; 881 g = g->next; 882 os_free(gprev); 883 } 884 885 m = milenage_db; 886 while (m) { 887 prev = m; 888 m = m->next; 889 os_free(prev); 890 } 891 892 close(serv_sock); 893 unlink(socket_path); 894 895#ifdef CONFIG_SQLITE 896 if (sqlite_db) { 897 sqlite3_close(sqlite_db); 898 sqlite_db = NULL; 899 } 900#endif /* CONFIG_SQLITE */ 901} 902 903 904static void handle_term(int sig) 905{ 906 printf("Signal %d - terminate\n", sig); 907 exit(0); 908} 909 910 911static void usage(void) 912{ 913 printf("HLR/AuC testing gateway for hostapd EAP-SIM/AKA " 914 "database/authenticator\n" 915 "Copyright (c) 2005-2007, 2012, Jouni Malinen <j@w1.fi>\n" 916 "\n" 917 "usage:\n" 918 "hlr_auc_gw [-hu] [-s<socket path>] [-g<triplet file>] " 919 "[-m<milenage file>] \\\n" 920 " [-D<DB file>] [-i<IND len in bits>]\n" 921 "\n" 922 "options:\n" 923 " -h = show this usage help\n" 924 " -u = update SQN in Milenage file on exit\n" 925 " -s<socket path> = path for UNIX domain socket\n" 926 " (default: %s)\n" 927 " -g<triplet file> = path for GSM authentication triplets\n" 928 " -m<milenage file> = path for Milenage keys\n" 929 " -D<DB file> = path to SQLite database\n" 930 " -i<IND len in bits> = IND length for SQN (default: 5)\n", 931 default_socket_path); 932} 933 934 935int main(int argc, char *argv[]) 936{ 937 int c; 938 char *gsm_triplet_file = NULL; 939 char *sqlite_db_file = NULL; 940 941 if (os_program_init()) 942 return -1; 943 944 socket_path = default_socket_path; 945 946 for (;;) { 947 c = getopt(argc, argv, "D:g:hi:m:s:u"); 948 if (c < 0) 949 break; 950 switch (c) { 951 case 'D': 952#ifdef CONFIG_SQLITE 953 sqlite_db_file = optarg; 954 break; 955#else /* CONFIG_SQLITE */ 956 printf("No SQLite support included in the build\n"); 957 return -1; 958#endif /* CONFIG_SQLITE */ 959 case 'g': 960 gsm_triplet_file = optarg; 961 break; 962 case 'h': 963 usage(); 964 return 0; 965 case 'i': 966 ind_len = atoi(optarg); 967 if (ind_len < 0 || ind_len > 32) { 968 printf("Invalid IND length\n"); 969 return -1; 970 } 971 break; 972 case 'm': 973 milenage_file = optarg; 974 break; 975 case 's': 976 socket_path = optarg; 977 break; 978 case 'u': 979 update_milenage = 1; 980 break; 981 default: 982 usage(); 983 return -1; 984 } 985 } 986 987 if (!gsm_triplet_file && !milenage_file && !sqlite_db_file) { 988 usage(); 989 return -1; 990 } 991 992#ifdef CONFIG_SQLITE 993 if (sqlite_db_file && (sqlite_db = db_open(sqlite_db_file)) == NULL) 994 return -1; 995#endif /* CONFIG_SQLITE */ 996 997 if (gsm_triplet_file && read_gsm_triplets(gsm_triplet_file) < 0) 998 return -1; 999 1000 if (milenage_file && read_milenage(milenage_file) < 0) 1001 return -1; 1002 1003 serv_sock = open_socket(socket_path); 1004 if (serv_sock < 0) 1005 return -1; 1006 1007 printf("Listening for requests on %s\n", socket_path); 1008 1009 atexit(cleanup); 1010 signal(SIGTERM, handle_term); 1011 signal(SIGINT, handle_term); 1012 1013 for (;;) 1014 process(serv_sock); 1015 1016#ifdef CONFIG_SQLITE 1017 if (sqlite_db) { 1018 sqlite3_close(sqlite_db); 1019 sqlite_db = NULL; 1020 } 1021#endif /* CONFIG_SQLITE */ 1022 1023 os_program_deinit(); 1024 1025 return 0; 1026} 1027