1/* 2 * hostapd / EAP-SIM database/authenticator gateway 3 * Copyright (c) 2005-2007, 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 return NULL; 652 653 /* Remove possible realm from identity */ 654 len = 0; 655 while (len < identity_len) { 656 if (identity[len] == '@') 657 break; 658 len++; 659 } 660 661 pseudonym = os_malloc(len + 1); 662 if (pseudonym == NULL) 663 return NULL; 664 os_memcpy(pseudonym, identity, len); 665 pseudonym[len] = '\0'; 666 667 p = data->pseudonyms; 668 while (p) { 669 if (os_strcmp(p->pseudonym, pseudonym) == 0) 670 break; 671 p = p->next; 672 } 673 674 os_free(pseudonym); 675 676 return p; 677} 678 679 680static struct eap_sim_pseudonym * 681eap_sim_db_get_pseudonym_id(struct eap_sim_db_data *data, const u8 *identity, 682 size_t identity_len) 683{ 684 struct eap_sim_pseudonym *p; 685 686 if (identity_len == 0 || 687 (identity[0] != EAP_SIM_PERMANENT_PREFIX && 688 identity[0] != EAP_AKA_PERMANENT_PREFIX)) 689 return NULL; 690 691 p = data->pseudonyms; 692 while (p) { 693 if (identity_len == p->identity_len && 694 os_memcmp(p->identity, identity, identity_len) == 0) 695 break; 696 p = p->next; 697 } 698 699 return p; 700} 701 702 703static struct eap_sim_reauth * 704eap_sim_db_get_reauth(struct eap_sim_db_data *data, const u8 *identity, 705 size_t identity_len) 706{ 707 char *reauth_id; 708 size_t len; 709 struct eap_sim_reauth *r; 710 711 if (identity_len == 0 || 712 (identity[0] != EAP_SIM_REAUTH_ID_PREFIX && 713 identity[0] != EAP_AKA_REAUTH_ID_PREFIX)) 714 return NULL; 715 716 /* Remove possible realm from identity */ 717 len = 0; 718 while (len < identity_len) { 719 if (identity[len] == '@') 720 break; 721 len++; 722 } 723 724 reauth_id = os_malloc(len + 1); 725 if (reauth_id == NULL) 726 return NULL; 727 os_memcpy(reauth_id, identity, len); 728 reauth_id[len] = '\0'; 729 730 r = data->reauths; 731 while (r) { 732 if (os_strcmp(r->reauth_id, reauth_id) == 0) 733 break; 734 r = r->next; 735 } 736 737 os_free(reauth_id); 738 739 return r; 740} 741 742 743static struct eap_sim_reauth * 744eap_sim_db_get_reauth_id(struct eap_sim_db_data *data, const u8 *identity, 745 size_t identity_len) 746{ 747 struct eap_sim_pseudonym *p; 748 struct eap_sim_reauth *r; 749 750 if (identity_len == 0) 751 return NULL; 752 753 p = eap_sim_db_get_pseudonym(data, identity, identity_len); 754 if (p == NULL) 755 p = eap_sim_db_get_pseudonym_id(data, identity, identity_len); 756 if (p) { 757 identity = p->identity; 758 identity_len = p->identity_len; 759 } 760 761 r = data->reauths; 762 while (r) { 763 if (identity_len == r->identity_len && 764 os_memcmp(r->identity, identity, identity_len) == 0) 765 break; 766 r = r->next; 767 } 768 769 return r; 770} 771 772 773/** 774 * eap_sim_db_identity_known - Verify whether the given identity is known 775 * @priv: Private data pointer from eap_sim_db_init() 776 * @identity: User name identity 777 * @identity_len: Length of identity in bytes 778 * Returns: 0 if the user is found or -1 on failure 779 * 780 * In most cases, the user name is ['0','1'] | IMSI, i.e., 1 followed by the 781 * IMSI in ASCII format, ['2','3'] | pseudonym, or ['4','5'] | reauth_id. 782 */ 783int eap_sim_db_identity_known(void *priv, const u8 *identity, 784 size_t identity_len) 785{ 786 struct eap_sim_db_data *data = priv; 787 788 if (identity == NULL || identity_len < 2) 789 return -1; 790 791 if (identity[0] == EAP_SIM_PSEUDONYM_PREFIX || 792 identity[0] == EAP_AKA_PSEUDONYM_PREFIX) { 793 struct eap_sim_pseudonym *p = 794 eap_sim_db_get_pseudonym(data, identity, identity_len); 795 return p ? 0 : -1; 796 } 797 798 if (identity[0] == EAP_SIM_REAUTH_ID_PREFIX || 799 identity[0] == EAP_AKA_REAUTH_ID_PREFIX) { 800 struct eap_sim_reauth *r = 801 eap_sim_db_get_reauth(data, identity, identity_len); 802 return r ? 0 : -1; 803 } 804 805 if (identity[0] != EAP_SIM_PERMANENT_PREFIX && 806 identity[0] != EAP_AKA_PERMANENT_PREFIX) { 807 /* Unknown identity prefix */ 808 return -1; 809 } 810 811 /* TODO: Should consider asking HLR/AuC gateway whether this permanent 812 * identity is known. If it is, EAP-SIM/AKA can skip identity request. 813 * In case of EAP-AKA, this would reduce number of needed round-trips. 814 * Ideally, this would be done with one wait, i.e., just request 815 * authentication data and store it for the next use. This would then 816 * need to use similar pending-request functionality as the normal 817 * request for authentication data at later phase. 818 */ 819 return -1; 820} 821 822 823static char * eap_sim_db_get_next(struct eap_sim_db_data *data, char prefix) 824{ 825 char *id, *pos, *end; 826 u8 buf[10]; 827 828 if (random_get_bytes(buf, sizeof(buf))) 829 return NULL; 830 id = os_malloc(sizeof(buf) * 2 + 2); 831 if (id == NULL) 832 return NULL; 833 834 pos = id; 835 end = id + sizeof(buf) * 2 + 2; 836 *pos++ = prefix; 837 pos += wpa_snprintf_hex(pos, end - pos, buf, sizeof(buf)); 838 839 return id; 840} 841 842 843/** 844 * eap_sim_db_get_next_pseudonym - EAP-SIM DB: Get next pseudonym 845 * @priv: Private data pointer from eap_sim_db_init() 846 * @aka: Using EAP-AKA instead of EAP-SIM 847 * Returns: Next pseudonym (allocated string) or %NULL on failure 848 * 849 * This function is used to generate a pseudonym for EAP-SIM. The returned 850 * pseudonym is not added to database at this point; it will need to be added 851 * with eap_sim_db_add_pseudonym() once the authentication has been completed 852 * successfully. Caller is responsible for freeing the returned buffer. 853 */ 854char * eap_sim_db_get_next_pseudonym(void *priv, int aka) 855{ 856 struct eap_sim_db_data *data = priv; 857 return eap_sim_db_get_next(data, aka ? EAP_AKA_PSEUDONYM_PREFIX : 858 EAP_SIM_PSEUDONYM_PREFIX); 859} 860 861 862/** 863 * eap_sim_db_get_next_reauth_id - EAP-SIM DB: Get next reauth_id 864 * @priv: Private data pointer from eap_sim_db_init() 865 * @aka: Using EAP-AKA instead of EAP-SIM 866 * Returns: Next reauth_id (allocated string) or %NULL on failure 867 * 868 * This function is used to generate a fast re-authentication identity for 869 * EAP-SIM. The returned reauth_id is not added to database at this point; it 870 * will need to be added with eap_sim_db_add_reauth() once the authentication 871 * has been completed successfully. Caller is responsible for freeing the 872 * returned buffer. 873 */ 874char * eap_sim_db_get_next_reauth_id(void *priv, int aka) 875{ 876 struct eap_sim_db_data *data = priv; 877 return eap_sim_db_get_next(data, aka ? EAP_AKA_REAUTH_ID_PREFIX : 878 EAP_SIM_REAUTH_ID_PREFIX); 879} 880 881 882/** 883 * eap_sim_db_add_pseudonym - EAP-SIM DB: Add new pseudonym 884 * @priv: Private data pointer from eap_sim_db_init() 885 * @identity: Identity of the user (may be permanent identity or pseudonym) 886 * @identity_len: Length of identity 887 * @pseudonym: Pseudonym for this user. This needs to be an allocated buffer, 888 * e.g., return value from eap_sim_db_get_next_pseudonym(). Caller must not 889 * free it. 890 * Returns: 0 on success, -1 on failure 891 * 892 * This function adds a new pseudonym for EAP-SIM user. EAP-SIM DB is 893 * responsible of freeing pseudonym buffer once it is not needed anymore. 894 */ 895int eap_sim_db_add_pseudonym(void *priv, const u8 *identity, 896 size_t identity_len, char *pseudonym) 897{ 898 struct eap_sim_db_data *data = priv; 899 struct eap_sim_pseudonym *p; 900 wpa_hexdump_ascii(MSG_DEBUG, "EAP-SIM DB: Add pseudonym for identity", 901 identity, identity_len); 902 wpa_printf(MSG_DEBUG, "EAP-SIM DB: Pseudonym: %s", pseudonym); 903 904 /* TODO: could store last two pseudonyms */ 905 p = eap_sim_db_get_pseudonym(data, identity, identity_len); 906 if (p == NULL) 907 p = eap_sim_db_get_pseudonym_id(data, identity, identity_len); 908 909 if (p) { 910 wpa_printf(MSG_DEBUG, "EAP-SIM DB: Replacing previous " 911 "pseudonym: %s", p->pseudonym); 912 os_free(p->pseudonym); 913 p->pseudonym = pseudonym; 914 return 0; 915 } 916 917 p = os_zalloc(sizeof(*p)); 918 if (p == NULL) { 919 os_free(pseudonym); 920 return -1; 921 } 922 923 p->next = data->pseudonyms; 924 p->identity = os_malloc(identity_len); 925 if (p->identity == NULL) { 926 os_free(p); 927 os_free(pseudonym); 928 return -1; 929 } 930 os_memcpy(p->identity, identity, identity_len); 931 p->identity_len = identity_len; 932 p->pseudonym = pseudonym; 933 data->pseudonyms = p; 934 935 wpa_printf(MSG_DEBUG, "EAP-SIM DB: Added new pseudonym entry"); 936 return 0; 937} 938 939 940static struct eap_sim_reauth * 941eap_sim_db_add_reauth_data(struct eap_sim_db_data *data, const u8 *identity, 942 size_t identity_len, char *reauth_id, u16 counter) 943{ 944 struct eap_sim_reauth *r; 945 946 wpa_hexdump_ascii(MSG_DEBUG, "EAP-SIM DB: Add reauth_id for identity", 947 identity, identity_len); 948 wpa_printf(MSG_DEBUG, "EAP-SIM DB: reauth_id: %s", reauth_id); 949 950 r = eap_sim_db_get_reauth(data, identity, identity_len); 951 if (r == NULL) 952 r = eap_sim_db_get_reauth_id(data, identity, identity_len); 953 954 if (r) { 955 wpa_printf(MSG_DEBUG, "EAP-SIM DB: Replacing previous " 956 "reauth_id: %s", r->reauth_id); 957 os_free(r->reauth_id); 958 r->reauth_id = reauth_id; 959 } else { 960 r = os_zalloc(sizeof(*r)); 961 if (r == NULL) { 962 os_free(reauth_id); 963 return NULL; 964 } 965 966 r->next = data->reauths; 967 r->identity = os_malloc(identity_len); 968 if (r->identity == NULL) { 969 os_free(r); 970 os_free(reauth_id); 971 return NULL; 972 } 973 os_memcpy(r->identity, identity, identity_len); 974 r->identity_len = identity_len; 975 r->reauth_id = reauth_id; 976 data->reauths = r; 977 wpa_printf(MSG_DEBUG, "EAP-SIM DB: Added new reauth entry"); 978 } 979 980 r->counter = counter; 981 982 return r; 983} 984 985 986/** 987 * eap_sim_db_add_reauth - EAP-SIM DB: Add new re-authentication entry 988 * @priv: Private data pointer from eap_sim_db_init() 989 * @identity: Identity of the user (may be permanent identity or pseudonym) 990 * @identity_len: Length of identity 991 * @reauth_id: reauth_id for this user. This needs to be an allocated buffer, 992 * e.g., return value from eap_sim_db_get_next_reauth_id(). Caller must not 993 * free it. 994 * @counter: AT_COUNTER value for fast re-authentication 995 * @mk: 16-byte MK from the previous full authentication or %NULL 996 * Returns: 0 on success, -1 on failure 997 * 998 * This function adds a new re-authentication entry for an EAP-SIM user. 999 * EAP-SIM DB is responsible of freeing reauth_id buffer once it is not needed 1000 * anymore. 1001 */ 1002int eap_sim_db_add_reauth(void *priv, const u8 *identity, 1003 size_t identity_len, char *reauth_id, u16 counter, 1004 const u8 *mk) 1005{ 1006 struct eap_sim_db_data *data = priv; 1007 struct eap_sim_reauth *r; 1008 1009 r = eap_sim_db_add_reauth_data(data, identity, identity_len, reauth_id, 1010 counter); 1011 if (r == NULL) 1012 return -1; 1013 1014 os_memcpy(r->mk, mk, EAP_SIM_MK_LEN); 1015 r->aka_prime = 0; 1016 1017 return 0; 1018} 1019 1020 1021#ifdef EAP_SERVER_AKA_PRIME 1022/** 1023 * eap_sim_db_add_reauth_prime - EAP-AKA' DB: Add new re-authentication entry 1024 * @priv: Private data pointer from eap_sim_db_init() 1025 * @identity: Identity of the user (may be permanent identity or pseudonym) 1026 * @identity_len: Length of identity 1027 * @reauth_id: reauth_id for this user. This needs to be an allocated buffer, 1028 * e.g., return value from eap_sim_db_get_next_reauth_id(). Caller must not 1029 * free it. 1030 * @counter: AT_COUNTER value for fast re-authentication 1031 * @k_encr: K_encr from the previous full authentication 1032 * @k_aut: K_aut from the previous full authentication 1033 * @k_re: 32-byte K_re from the previous full authentication 1034 * Returns: 0 on success, -1 on failure 1035 * 1036 * This function adds a new re-authentication entry for an EAP-AKA' user. 1037 * EAP-SIM DB is responsible of freeing reauth_id buffer once it is not needed 1038 * anymore. 1039 */ 1040int eap_sim_db_add_reauth_prime(void *priv, const u8 *identity, 1041 size_t identity_len, char *reauth_id, 1042 u16 counter, const u8 *k_encr, const u8 *k_aut, 1043 const u8 *k_re) 1044{ 1045 struct eap_sim_db_data *data = priv; 1046 struct eap_sim_reauth *r; 1047 1048 r = eap_sim_db_add_reauth_data(data, identity, identity_len, reauth_id, 1049 counter); 1050 if (r == NULL) 1051 return -1; 1052 1053 r->aka_prime = 1; 1054 os_memcpy(r->k_encr, k_encr, EAP_SIM_K_ENCR_LEN); 1055 os_memcpy(r->k_aut, k_aut, EAP_AKA_PRIME_K_AUT_LEN); 1056 os_memcpy(r->k_re, k_re, EAP_AKA_PRIME_K_RE_LEN); 1057 1058 return 0; 1059} 1060#endif /* EAP_SERVER_AKA_PRIME */ 1061 1062 1063/** 1064 * eap_sim_db_get_permanent - EAP-SIM DB: Get permanent identity 1065 * @priv: Private data pointer from eap_sim_db_init() 1066 * @identity: Identity of the user (may be permanent identity or pseudonym) 1067 * @identity_len: Length of identity 1068 * @len: Buffer for length of the returned permanent identity 1069 * Returns: Pointer to the permanent identity, or %NULL if not found 1070 */ 1071const u8 * eap_sim_db_get_permanent(void *priv, const u8 *identity, 1072 size_t identity_len, size_t *len) 1073{ 1074 struct eap_sim_db_data *data = priv; 1075 struct eap_sim_pseudonym *p; 1076 1077 if (identity == NULL) 1078 return NULL; 1079 1080 p = eap_sim_db_get_pseudonym(data, identity, identity_len); 1081 if (p == NULL) 1082 p = eap_sim_db_get_pseudonym_id(data, identity, identity_len); 1083 if (p == NULL) 1084 return NULL; 1085 1086 *len = p->identity_len; 1087 return p->identity; 1088} 1089 1090 1091/** 1092 * eap_sim_db_get_reauth_entry - EAP-SIM DB: Get re-authentication entry 1093 * @priv: Private data pointer from eap_sim_db_init() 1094 * @identity: Identity of the user (may be permanent identity, pseudonym, or 1095 * reauth_id) 1096 * @identity_len: Length of identity 1097 * Returns: Pointer to the re-auth entry, or %NULL if not found 1098 */ 1099struct eap_sim_reauth * 1100eap_sim_db_get_reauth_entry(void *priv, const u8 *identity, 1101 size_t identity_len) 1102{ 1103 struct eap_sim_db_data *data = priv; 1104 struct eap_sim_reauth *r; 1105 1106 if (identity == NULL) 1107 return NULL; 1108 r = eap_sim_db_get_reauth(data, identity, identity_len); 1109 if (r == NULL) 1110 r = eap_sim_db_get_reauth_id(data, identity, identity_len); 1111 return r; 1112} 1113 1114 1115/** 1116 * eap_sim_db_remove_reauth - EAP-SIM DB: Remove re-authentication entry 1117 * @priv: Private data pointer from eap_sim_db_init() 1118 * @reauth: Pointer to re-authentication entry from 1119 * eap_sim_db_get_reauth_entry() 1120 */ 1121void eap_sim_db_remove_reauth(void *priv, struct eap_sim_reauth *reauth) 1122{ 1123 struct eap_sim_db_data *data = priv; 1124 struct eap_sim_reauth *r, *prev = NULL; 1125 r = data->reauths; 1126 while (r) { 1127 if (r == reauth) { 1128 if (prev) 1129 prev->next = r->next; 1130 else 1131 data->reauths = r->next; 1132 eap_sim_db_free_reauth(r); 1133 return; 1134 } 1135 prev = r; 1136 r = r->next; 1137 } 1138} 1139 1140 1141/** 1142 * eap_sim_db_get_aka_auth - Get AKA authentication values 1143 * @priv: Private data pointer from eap_sim_db_init() 1144 * @identity: User name identity 1145 * @identity_len: Length of identity in bytes 1146 * @_rand: Buffer for RAND value 1147 * @autn: Buffer for AUTN value 1148 * @ik: Buffer for IK value 1149 * @ck: Buffer for CK value 1150 * @res: Buffer for RES value 1151 * @res_len: Buffer for RES length 1152 * @cb_session_ctx: Session callback context for get_complete_cb() 1153 * Returns: 0 on success, -1 (EAP_SIM_DB_FAILURE) on error (e.g., user not 1154 * found), or -2 (EAP_SIM_DB_PENDING) if results are not yet available. In this 1155 * case, the callback function registered with eap_sim_db_init() will be 1156 * called once the results become available. 1157 * 1158 * In most cases, the user name is '0' | IMSI, i.e., 0 followed by the IMSI in 1159 * ASCII format. 1160 * 1161 * When using an external server for AKA authentication, this function can 1162 * always start a request and return EAP_SIM_DB_PENDING immediately if 1163 * authentication triplets are not available. Once the authentication data are 1164 * received, callback function registered with eap_sim_db_init() is called to 1165 * notify EAP state machine to reprocess the message. This 1166 * eap_sim_db_get_aka_auth() function will then be called again and the newly 1167 * received triplets will then be given to the caller. 1168 */ 1169int eap_sim_db_get_aka_auth(void *priv, const u8 *identity, 1170 size_t identity_len, u8 *_rand, u8 *autn, u8 *ik, 1171 u8 *ck, u8 *res, size_t *res_len, 1172 void *cb_session_ctx) 1173{ 1174 struct eap_sim_db_data *data = priv; 1175 struct eap_sim_db_pending *entry; 1176 int len; 1177 size_t i; 1178 char msg[40]; 1179 1180 if (identity_len < 2 || identity == NULL || 1181 identity[0] != EAP_AKA_PERMANENT_PREFIX) { 1182 wpa_hexdump_ascii(MSG_DEBUG, "EAP-SIM DB: unexpected identity", 1183 identity, identity_len); 1184 return EAP_SIM_DB_FAILURE; 1185 } 1186 identity++; 1187 identity_len--; 1188 for (i = 0; i < identity_len; i++) { 1189 if (identity[i] == '@') { 1190 identity_len = i; 1191 break; 1192 } 1193 } 1194 if (identity_len + 1 > sizeof(entry->imsi)) { 1195 wpa_hexdump_ascii(MSG_DEBUG, "EAP-SIM DB: unexpected identity", 1196 identity, identity_len); 1197 return EAP_SIM_DB_FAILURE; 1198 } 1199 wpa_hexdump_ascii(MSG_DEBUG, "EAP-SIM DB: Get AKA auth for IMSI", 1200 identity, identity_len); 1201 1202 entry = eap_sim_db_get_pending(data, identity, identity_len, 1); 1203 if (entry) { 1204 if (entry->state == FAILURE) { 1205 os_free(entry); 1206 wpa_printf(MSG_DEBUG, "EAP-SIM DB: Failure"); 1207 return EAP_SIM_DB_FAILURE; 1208 } 1209 1210 if (entry->state == PENDING) { 1211 eap_sim_db_add_pending(data, entry); 1212 wpa_printf(MSG_DEBUG, "EAP-SIM DB: Pending"); 1213 return EAP_SIM_DB_PENDING; 1214 } 1215 1216 wpa_printf(MSG_DEBUG, "EAP-SIM DB: Returning successfully " 1217 "received authentication data"); 1218 os_memcpy(_rand, entry->u.aka.rand, EAP_AKA_RAND_LEN); 1219 os_memcpy(autn, entry->u.aka.autn, EAP_AKA_AUTN_LEN); 1220 os_memcpy(ik, entry->u.aka.ik, EAP_AKA_IK_LEN); 1221 os_memcpy(ck, entry->u.aka.ck, EAP_AKA_CK_LEN); 1222 os_memcpy(res, entry->u.aka.res, EAP_AKA_RES_MAX_LEN); 1223 *res_len = entry->u.aka.res_len; 1224 os_free(entry); 1225 return 0; 1226 } 1227 1228 if (data->sock < 0) { 1229 if (eap_sim_db_open_socket(data) < 0) 1230 return EAP_SIM_DB_FAILURE; 1231 } 1232 1233 len = os_snprintf(msg, sizeof(msg), "AKA-REQ-AUTH "); 1234 if (len < 0 || len + identity_len >= sizeof(msg)) 1235 return EAP_SIM_DB_FAILURE; 1236 os_memcpy(msg + len, identity, identity_len); 1237 len += identity_len; 1238 1239 wpa_hexdump(MSG_DEBUG, "EAP-SIM DB: requesting AKA authentication " 1240 "data for IMSI", identity, identity_len); 1241 if (eap_sim_db_send(data, msg, len) < 0) 1242 return EAP_SIM_DB_FAILURE; 1243 1244 entry = os_zalloc(sizeof(*entry)); 1245 if (entry == NULL) 1246 return EAP_SIM_DB_FAILURE; 1247 1248 os_get_time(&entry->timestamp); 1249 entry->aka = 1; 1250 os_memcpy(entry->imsi, identity, identity_len); 1251 entry->imsi_len = identity_len; 1252 entry->cb_session_ctx = cb_session_ctx; 1253 entry->state = PENDING; 1254 eap_sim_db_add_pending(data, entry); 1255 eap_sim_db_expire_pending(data); 1256 1257 return EAP_SIM_DB_PENDING; 1258} 1259 1260 1261/** 1262 * eap_sim_db_resynchronize - Resynchronize AKA AUTN 1263 * @priv: Private data pointer from eap_sim_db_init() 1264 * @identity: User name identity 1265 * @identity_len: Length of identity in bytes 1266 * @auts: AUTS value from the peer 1267 * @_rand: RAND value used in the rejected message 1268 * Returns: 0 on success, -1 on failure 1269 * 1270 * This function is called when the peer reports synchronization failure in the 1271 * AUTN value by sending AUTS. The AUTS and RAND values should be sent to 1272 * HLR/AuC to allow it to resynchronize with the peer. After this, 1273 * eap_sim_db_get_aka_auth() will be called again to to fetch updated 1274 * RAND/AUTN values for the next challenge. 1275 */ 1276int eap_sim_db_resynchronize(void *priv, const u8 *identity, 1277 size_t identity_len, const u8 *auts, 1278 const u8 *_rand) 1279{ 1280 struct eap_sim_db_data *data = priv; 1281 size_t i; 1282 1283 if (identity_len < 2 || identity == NULL || 1284 identity[0] != EAP_AKA_PERMANENT_PREFIX) { 1285 wpa_hexdump_ascii(MSG_DEBUG, "EAP-SIM DB: unexpected identity", 1286 identity, identity_len); 1287 return -1; 1288 } 1289 identity++; 1290 identity_len--; 1291 for (i = 0; i < identity_len; i++) { 1292 if (identity[i] == '@') { 1293 identity_len = i; 1294 break; 1295 } 1296 } 1297 if (identity_len > 20) { 1298 wpa_hexdump_ascii(MSG_DEBUG, "EAP-SIM DB: unexpected identity", 1299 identity, identity_len); 1300 return -1; 1301 } 1302 1303 if (data->sock >= 0) { 1304 char msg[100]; 1305 int len, ret; 1306 1307 len = os_snprintf(msg, sizeof(msg), "AKA-AUTS "); 1308 if (len < 0 || len + identity_len >= sizeof(msg)) 1309 return -1; 1310 os_memcpy(msg + len, identity, identity_len); 1311 len += identity_len; 1312 1313 ret = os_snprintf(msg + len, sizeof(msg) - len, " "); 1314 if (ret < 0 || (size_t) ret >= sizeof(msg) - len) 1315 return -1; 1316 len += ret; 1317 len += wpa_snprintf_hex(msg + len, sizeof(msg) - len, 1318 auts, EAP_AKA_AUTS_LEN); 1319 ret = os_snprintf(msg + len, sizeof(msg) - len, " "); 1320 if (ret < 0 || (size_t) ret >= sizeof(msg) - len) 1321 return -1; 1322 len += ret; 1323 len += wpa_snprintf_hex(msg + len, sizeof(msg) - len, 1324 _rand, EAP_AKA_RAND_LEN); 1325 wpa_hexdump(MSG_DEBUG, "EAP-SIM DB: reporting AKA AUTS for " 1326 "IMSI", identity, identity_len); 1327 if (eap_sim_db_send(data, msg, len) < 0) 1328 return -1; 1329 } 1330 1331 return 0; 1332} 1333