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