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