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