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