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