2 * hostapd / EAP-SIM database/authenticator gateway
3 * Copyright (c) 2005-2006, Jouni Malinen <j@w1.fi>
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.
9 * Alternatively, this software may be distributed under the terms of BSD
12 * See README and COPYING for more details.
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.
28 #include "eap_sim_common.h"
29 #include "eap_sim_db.h"
32 struct eap_sim_pseudonym {
33 struct eap_sim_pseudonym *next;
39 struct eap_sim_db_pending {
40 struct eap_sim_db_pending *next;
43 enum { PENDING, SUCCESS, FAILURE } state;
45 struct os_time timestamp;
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];
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];
65 struct eap_sim_db_data {
69 void (*get_complete_cb)(void *ctx, void *session_ctx);
71 struct eap_sim_pseudonym *pseudonyms;
72 struct eap_sim_reauth *reauths;
73 struct eap_sim_db_pending *pending;
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)
81 struct eap_sim_db_pending *entry, *prev = NULL;
83 entry = data->pending;
85 if (entry->aka == aka && entry->imsi_len == imsi_len &&
86 memcmp(entry->imsi, imsi, imsi_len) == 0) {
88 prev->next = entry->next;
90 data->pending = entry->next;
100 static void eap_sim_db_add_pending(struct eap_sim_db_data *data,
101 struct eap_sim_db_pending *entry)
103 entry->next = data->pending;
104 data->pending = entry;
108 static void eap_sim_db_sim_resp_auth(struct eap_sim_db_data *data,
109 const char *imsi, char *buf)
111 char *start, *end, *pos;
112 struct eap_sim_db_pending *entry;
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)
121 entry = eap_sim_db_get_pending(data, (u8 *) imsi, strlen(imsi), 0);
123 wpa_printf(MSG_DEBUG, "EAP-SIM DB: No pending entry for the "
124 "received message found");
129 if (strncmp(start, "FAILURE", 7) == 0) {
130 wpa_printf(MSG_DEBUG, "EAP-SIM DB: External server reported "
132 entry->state = FAILURE;
133 eap_sim_db_add_pending(data, entry);
134 data->get_complete_cb(data->ctx, entry->cb_session_ctx);
139 while (num_chal < EAP_SIM_MAX_CHAL) {
140 end = strchr(start, ' ');
144 pos = strchr(start, ':');
148 if (hexstr2bin(start, entry->u.sim.kc[num_chal],
153 pos = strchr(start, ':');
157 if (hexstr2bin(start, entry->u.sim.sres[num_chal],
162 if (hexstr2bin(start, entry->u.sim.rand[num_chal],
172 entry->u.sim.num_chal = num_chal;
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);
182 wpa_printf(MSG_DEBUG, "EAP-SIM DB: Failed to parse response string");
187 static void eap_sim_db_aka_resp_auth(struct eap_sim_db_data *data,
188 const char *imsi, char *buf)
191 struct eap_sim_db_pending *entry;
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)
199 entry = eap_sim_db_get_pending(data, (u8 *) imsi, strlen(imsi), 1);
201 wpa_printf(MSG_DEBUG, "EAP-SIM DB: No pending entry for the "
202 "received message found");
207 if (strncmp(start, "FAILURE", 7) == 0) {
208 wpa_printf(MSG_DEBUG, "EAP-SIM DB: External server reported "
210 entry->state = FAILURE;
211 eap_sim_db_add_pending(data, entry);
212 data->get_complete_cb(data->ctx, entry->cb_session_ctx);
216 end = strchr(start, ' ');
220 if (hexstr2bin(start, entry->u.aka.rand, EAP_AKA_RAND_LEN))
224 end = strchr(start, ' ');
228 if (hexstr2bin(start, entry->u.aka.autn, EAP_AKA_AUTN_LEN))
232 end = strchr(start, ' ');
236 if (hexstr2bin(start, entry->u.aka.ik, EAP_AKA_IK_LEN))
240 end = strchr(start, ' ');
244 if (hexstr2bin(start, entry->u.aka.ck, EAP_AKA_CK_LEN))
248 end = strchr(start, ' ');
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;
262 if (hexstr2bin(start, entry->u.aka.res, entry->u.aka.res_len))
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);
273 wpa_printf(MSG_DEBUG, "EAP-SIM DB: Failed to parse response string");
278 static void eap_sim_db_receive(int sock, void *eloop_ctx, void *sock_ctx)
280 struct eap_sim_db_data *data = eloop_ctx;
281 char buf[1000], *pos, *cmd, *imsi;
284 res = recv(sock, buf, sizeof(buf), 0);
287 wpa_hexdump_ascii_key(MSG_MSGDUMP, "EAP-SIM DB: Received from an "
288 "external source", (u8 *) buf, res);
291 if (res >= (int) sizeof(buf))
292 res = sizeof(buf) - 1;
295 if (data->get_complete_cb == NULL) {
296 wpa_printf(MSG_DEBUG, "EAP-SIM DB: No get_complete_cb "
301 /* <cmd> <IMSI> ... */
304 pos = strchr(cmd, ' ');
309 pos = strchr(imsi, ' ');
313 wpa_printf(MSG_DEBUG, "EAP-SIM DB: External response=%s for IMSI %s",
316 if (strcmp(cmd, "SIM-RESP-AUTH") == 0)
317 eap_sim_db_sim_resp_auth(data, imsi, pos + 1);
318 else if (strcmp(cmd, "AKA-RESP-AUTH") == 0)
319 eap_sim_db_aka_resp_auth(data, imsi, pos + 1);
321 wpa_printf(MSG_INFO, "EAP-SIM DB: Unknown external response "
326 wpa_printf(MSG_DEBUG, "EAP-SIM DB: Failed to parse response string");
330 static int eap_sim_db_open_socket(struct eap_sim_db_data *data)
332 struct sockaddr_un addr;
333 static int counter = 0;
335 if (strncmp(data->fname, "unix:", 5) != 0)
338 data->sock = socket(PF_UNIX, SOCK_DGRAM, 0);
339 if (data->sock < 0) {
340 perror("socket(eap_sim_db)");
344 memset(&addr, 0, sizeof(addr));
345 addr.sun_family = AF_UNIX;
346 snprintf(addr.sun_path, sizeof(addr.sun_path),
347 "/tmp/eap_sim_db_%d-%d", getpid(), counter++);
348 data->local_sock = strdup(addr.sun_path);
349 if (bind(data->sock, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
350 perror("bind(eap_sim_db)");
356 memset(&addr, 0, sizeof(addr));
357 addr.sun_family = AF_UNIX;
358 snprintf(addr.sun_path, sizeof(addr.sun_path), "%s", data->fname + 5);
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, strlen(addr.sun_path));
368 eloop_register_read_sock(data->sock, eap_sim_db_receive, data, NULL);
374 static void eap_sim_db_close_socket(struct eap_sim_db_data *data)
376 if (data->sock >= 0) {
377 eloop_unregister_read_sock(data->sock);
381 if (data->local_sock) {
382 unlink(data->local_sock);
383 free(data->local_sock);
384 data->local_sock = NULL;
390 * eap_sim_db_init - Initialize EAP-SIM DB / authentication gateway interface
391 * @config: Configuration data (e.g., file name)
392 * @get_complete_cb: Callback function for reporting availability of triplets
393 * @ctx: Context pointer for get_complete_cb
394 * Returns: Pointer to a private data structure or %NULL on failure
396 void * eap_sim_db_init(const char *config,
397 void (*get_complete_cb)(void *ctx, void *session_ctx),
400 struct eap_sim_db_data *data;
402 data = wpa_zalloc(sizeof(*data));
407 data->get_complete_cb = get_complete_cb;
409 data->fname = strdup(config);
410 if (data->fname == NULL)
413 if (strncmp(data->fname, "unix:", 5) == 0) {
414 if (eap_sim_db_open_socket(data))
421 eap_sim_db_close_socket(data);
428 static void eap_sim_db_free_pseudonym(struct eap_sim_pseudonym *p)
436 static void eap_sim_db_free_reauth(struct eap_sim_reauth *r)
445 * eap_sim_db_deinit - Deinitialize EAP-SIM DB/authentication gw interface
446 * @priv: Private data pointer from eap_sim_db_init()
448 void eap_sim_db_deinit(void *priv)
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;
455 eap_sim_db_close_socket(data);
458 p = data->pseudonyms;
462 eap_sim_db_free_pseudonym(prev);
469 eap_sim_db_free_reauth(prevr);
472 pending = data->pending;
474 prev_pending = pending;
475 pending = pending->next;
483 static int eap_sim_db_send(struct eap_sim_db_data *data, const char *msg,
488 if (send(data->sock, msg, len, 0) < 0) {
490 perror("send[EAP-SIM DB UNIX]");
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)
499 wpa_printf(MSG_DEBUG, "EAP-SIM DB: Reconnected to the "
501 if (send(data->sock, msg, len, 0) < 0) {
502 perror("send[EAP-SIM DB UNIX]");
511 static void eap_sim_db_expire_pending(struct eap_sim_db_data *data)
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 */
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.
535 * In most cases, the user name is '1' | IMSI, i.e., 1 followed by the IMSI in
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.
546 int 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)
551 struct eap_sim_db_data *data = priv;
552 struct eap_sim_db_pending *entry;
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;
564 for (i = 0; i < identity_len; i++) {
565 if (identity[i] == '@') {
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;
575 wpa_hexdump_ascii(MSG_DEBUG, "EAP-SIM DB: Get GSM triplets for IMSI",
576 identity, identity_len);
578 entry = eap_sim_db_get_pending(data, identity, identity_len, 0);
581 if (entry->state == FAILURE) {
582 wpa_printf(MSG_DEBUG, "EAP-SIM DB: Pending entry -> "
585 return EAP_SIM_DB_FAILURE;
588 if (entry->state == PENDING) {
589 wpa_printf(MSG_DEBUG, "EAP-SIM DB: Pending entry -> "
591 eap_sim_db_add_pending(data, entry);
592 return EAP_SIM_DB_PENDING;
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)
600 memcpy(_rand, entry->u.sim.rand, num_chal * GSM_RAND_LEN);
601 memcpy(sres, entry->u.sim.sres, num_chal * EAP_SIM_SRES_LEN);
602 memcpy(kc, entry->u.sim.kc, num_chal * EAP_SIM_KC_LEN);
607 if (data->sock < 0) {
608 if (eap_sim_db_open_socket(data) < 0)
609 return EAP_SIM_DB_FAILURE;
612 len = snprintf(msg, sizeof(msg), "SIM-REQ-AUTH ");
613 if (len < 0 || len + identity_len >= sizeof(msg))
614 return EAP_SIM_DB_FAILURE;
615 memcpy(msg + len, identity, identity_len);
617 ret = snprintf(msg + len, sizeof(msg) - len, " %d", max_chal);
618 if (ret < 0 || (size_t) ret >= sizeof(msg) - len)
619 return EAP_SIM_DB_FAILURE;
622 wpa_hexdump(MSG_DEBUG, "EAP-SIM DB: requesting SIM authentication "
623 "data for IMSI", identity, identity_len);
624 if (eap_sim_db_send(data, msg, len) < 0)
625 return EAP_SIM_DB_FAILURE;
627 entry = wpa_zalloc(sizeof(*entry));
629 return EAP_SIM_DB_FAILURE;
631 os_get_time(&entry->timestamp);
632 memcpy(entry->imsi, identity, identity_len);
633 entry->imsi_len = identity_len;
634 entry->cb_session_ctx = cb_session_ctx;
635 entry->state = PENDING;
636 eap_sim_db_add_pending(data, entry);
637 eap_sim_db_expire_pending(data);
639 return EAP_SIM_DB_PENDING;
643 static struct eap_sim_pseudonym *
644 eap_sim_db_get_pseudonym(struct eap_sim_db_data *data, const u8 *identity,
649 struct eap_sim_pseudonym *p;
651 if (identity_len == 0 ||
652 (identity[0] != EAP_SIM_PSEUDONYM_PREFIX &&
653 identity[0] != EAP_AKA_PSEUDONYM_PREFIX))
656 /* Remove possible realm from identity */
658 while (len < identity_len) {
659 if (identity[len] == '@')
664 pseudonym = malloc(len + 1);
665 if (pseudonym == NULL)
667 memcpy(pseudonym, identity, len);
668 pseudonym[len] = '\0';
670 p = data->pseudonyms;
672 if (strcmp(p->pseudonym, pseudonym) == 0)
683 static struct eap_sim_pseudonym *
684 eap_sim_db_get_pseudonym_id(struct eap_sim_db_data *data, const u8 *identity,
687 struct eap_sim_pseudonym *p;
689 if (identity_len == 0 ||
690 (identity[0] != EAP_SIM_PERMANENT_PREFIX &&
691 identity[0] != EAP_AKA_PERMANENT_PREFIX))
694 p = data->pseudonyms;
696 if (identity_len == p->identity_len &&
697 memcmp(p->identity, identity, identity_len) == 0)
706 static struct eap_sim_reauth *
707 eap_sim_db_get_reauth(struct eap_sim_db_data *data, const u8 *identity,
712 struct eap_sim_reauth *r;
714 if (identity_len == 0 ||
715 (identity[0] != EAP_SIM_REAUTH_ID_PREFIX &&
716 identity[0] != EAP_AKA_REAUTH_ID_PREFIX))
719 /* Remove possible realm from identity */
721 while (len < identity_len) {
722 if (identity[len] == '@')
727 reauth_id = malloc(len + 1);
728 if (reauth_id == NULL)
730 memcpy(reauth_id, identity, len);
731 reauth_id[len] = '\0';
735 if (strcmp(r->reauth_id, reauth_id) == 0)
746 static struct eap_sim_reauth *
747 eap_sim_db_get_reauth_id(struct eap_sim_db_data *data, const u8 *identity,
750 struct eap_sim_pseudonym *p;
751 struct eap_sim_reauth *r;
753 if (identity_len == 0)
756 p = eap_sim_db_get_pseudonym(data, identity, identity_len);
758 p = eap_sim_db_get_pseudonym_id(data, identity, identity_len);
760 identity = p->identity;
761 identity_len = p->identity_len;
766 if (identity_len == r->identity_len &&
767 memcmp(r->identity, identity, identity_len) == 0)
777 * eap_sim_db_identity_known - Verify whether the given identity is known
778 * @priv: Private data pointer from eap_sim_db_init()
779 * @identity: User name identity
780 * @identity_len: Length of identity in bytes
781 * Returns: 0 if the user is found or -1 on failure
783 * In most cases, the user name is ['0','1'] | IMSI, i.e., 1 followed by the
784 * IMSI in ASCII format, ['2','3'] | pseudonym, or ['4','5'] | reauth_id.
786 int eap_sim_db_identity_known(void *priv, const u8 *identity,
789 struct eap_sim_db_data *data = priv;
791 if (identity == NULL || identity_len < 2)
794 if (identity[0] == EAP_SIM_PSEUDONYM_PREFIX ||
795 identity[0] == EAP_AKA_PSEUDONYM_PREFIX) {
796 struct eap_sim_pseudonym *p =
797 eap_sim_db_get_pseudonym(data, identity, identity_len);
801 if (identity[0] == EAP_SIM_REAUTH_ID_PREFIX ||
802 identity[0] == EAP_AKA_REAUTH_ID_PREFIX) {
803 struct eap_sim_reauth *r =
804 eap_sim_db_get_reauth(data, identity, identity_len);
808 if (identity[0] != EAP_SIM_PERMANENT_PREFIX &&
809 identity[0] != EAP_AKA_PERMANENT_PREFIX) {
810 /* Unknown identity prefix */
814 /* TODO: Should consider asking HLR/AuC gateway whether this permanent
815 * identity is known. If it is, EAP-SIM/AKA can skip identity request.
816 * In case of EAP-AKA, this would reduce number of needed round-trips.
817 * Ideally, this would be done with one wait, i.e., just request
818 * authentication data and store it for the next use. This would then
819 * need to use similar pending-request functionality as the normal
820 * request for authentication data at later phase.
826 static char * eap_sim_db_get_next(struct eap_sim_db_data *data, char prefix)
828 char *id, *pos, *end;
831 if (hostapd_get_rand(buf, sizeof(buf)))
833 id = malloc(sizeof(buf) * 2 + 2);
838 end = id + sizeof(buf) * 2 + 2;
840 pos += wpa_snprintf_hex(pos, end - pos, buf, sizeof(buf));
847 * eap_sim_db_get_next_pseudonym - EAP-SIM DB: Get next pseudonym
848 * @priv: Private data pointer from eap_sim_db_init()
849 * @aka: Using EAP-AKA instead of EAP-SIM
850 * Returns: Next pseudonym (allocated string) or %NULL on failure
852 * This function is used to generate a pseudonym for EAP-SIM. The returned
853 * pseudonym is not added to database at this point; it will need to be added
854 * with eap_sim_db_add_pseudonym() once the authentication has been completed
855 * successfully. Caller is responsible for freeing the returned buffer.
857 char * eap_sim_db_get_next_pseudonym(void *priv, int aka)
859 struct eap_sim_db_data *data = priv;
860 return eap_sim_db_get_next(data, aka ? EAP_AKA_PSEUDONYM_PREFIX :
861 EAP_SIM_PSEUDONYM_PREFIX);
866 * eap_sim_db_get_next_reauth_id - EAP-SIM DB: Get next reauth_id
867 * @priv: Private data pointer from eap_sim_db_init()
868 * @aka: Using EAP-AKA instead of EAP-SIM
869 * Returns: Next reauth_id (allocated string) or %NULL on failure
871 * This function is used to generate a fast re-authentication identity for
872 * EAP-SIM. The returned reauth_id is not added to database at this point; it
873 * will need to be added with eap_sim_db_add_reauth() once the authentication
874 * has been completed successfully. Caller is responsible for freeing the
877 char * eap_sim_db_get_next_reauth_id(void *priv, int aka)
879 struct eap_sim_db_data *data = priv;
880 return eap_sim_db_get_next(data, aka ? EAP_AKA_REAUTH_ID_PREFIX :
881 EAP_SIM_REAUTH_ID_PREFIX);
886 * eap_sim_db_add_pseudonym - EAP-SIM DB: Add new pseudonym
887 * @priv: Private data pointer from eap_sim_db_init()
888 * @identity: Identity of the user (may be permanent identity or pseudonym)
889 * @identity_len: Length of identity
890 * @pseudonym: Pseudonym for this user. This needs to be an allocated buffer,
891 * e.g., return value from eap_sim_db_get_next_pseudonym(). Caller must not
893 * Returns: 0 on success, -1 on failure
895 * This function adds a new pseudonym for EAP-SIM user. EAP-SIM DB is
896 * responsible of freeing pseudonym buffer once it is not needed anymore.
898 int eap_sim_db_add_pseudonym(void *priv, const u8 *identity,
899 size_t identity_len, char *pseudonym)
901 struct eap_sim_db_data *data = priv;
902 struct eap_sim_pseudonym *p;
903 wpa_hexdump_ascii(MSG_DEBUG, "EAP-SIM DB: Add pseudonym for identity",
904 identity, identity_len);
905 wpa_printf(MSG_DEBUG, "EAP-SIM DB: Pseudonym: %s", pseudonym);
907 /* TODO: could store last two pseudonyms */
908 p = eap_sim_db_get_pseudonym(data, identity, identity_len);
910 p = eap_sim_db_get_pseudonym_id(data, identity, identity_len);
913 wpa_printf(MSG_DEBUG, "EAP-SIM DB: Replacing previous "
914 "pseudonym: %s", p->pseudonym);
916 p->pseudonym = pseudonym;
920 p = wpa_zalloc(sizeof(*p));
926 p->next = data->pseudonyms;
927 p->identity = malloc(identity_len);
928 if (p->identity == NULL) {
933 memcpy(p->identity, identity, identity_len);
934 p->identity_len = identity_len;
935 p->pseudonym = pseudonym;
936 data->pseudonyms = p;
938 wpa_printf(MSG_DEBUG, "EAP-SIM DB: Added new pseudonym entry");
944 * eap_sim_db_add_reauth - EAP-SIM DB: Add new re-authentication entry
945 * @priv: Private data pointer from eap_sim_db_init()
946 * @identity: Identity of the user (may be permanent identity or pseudonym)
947 * @identity_len: Length of identity
948 * @reauth_id: reauth_id for this user. This needs to be an allocated buffer,
949 * e.g., return value from eap_sim_db_get_next_reauth_id(). Caller must not
951 * @mk: 16-byte MK from the previous full authentication
952 * Returns: 0 on success, -1 on failure
954 * This function adds a new re-authentication entry for an EAP-SIM user.
955 * EAP-SIM DB is responsible of freeing reauth_id buffer once it is not needed
958 int eap_sim_db_add_reauth(void *priv, const u8 *identity,
959 size_t identity_len, char *reauth_id, u16 counter,
962 struct eap_sim_db_data *data = priv;
963 struct eap_sim_reauth *r;
964 wpa_hexdump_ascii(MSG_DEBUG, "EAP-SIM DB: Add reauth_id for identity",
965 identity, identity_len);
966 wpa_printf(MSG_DEBUG, "EAP-SIM DB: reauth_id: %s", reauth_id);
968 r = eap_sim_db_get_reauth(data, identity, identity_len);
970 r = eap_sim_db_get_reauth_id(data, identity, identity_len);
973 wpa_printf(MSG_DEBUG, "EAP-SIM DB: Replacing previous "
974 "reauth_id: %s", r->reauth_id);
976 r->reauth_id = reauth_id;
978 r = wpa_zalloc(sizeof(*r));
984 r->next = data->reauths;
985 r->identity = malloc(identity_len);
986 if (r->identity == NULL) {
991 memcpy(r->identity, identity, identity_len);
992 r->identity_len = identity_len;
993 r->reauth_id = reauth_id;
995 wpa_printf(MSG_DEBUG, "EAP-SIM DB: Added new reauth entry");
998 r->counter = counter;
999 memcpy(r->mk, mk, EAP_SIM_MK_LEN);
1006 * eap_sim_db_get_permanent - EAP-SIM DB: Get permanent identity
1007 * @priv: Private data pointer from eap_sim_db_init()
1008 * @identity: Identity of the user (may be permanent identity or pseudonym)
1009 * @identity_len: Length of identity
1010 * @len: Buffer for length of the returned permanent identity
1011 * Returns: Pointer to the permanent identity, or %NULL if not found
1013 const u8 * eap_sim_db_get_permanent(void *priv, const u8 *identity,
1014 size_t identity_len, size_t *len)
1016 struct eap_sim_db_data *data = priv;
1017 struct eap_sim_pseudonym *p;
1019 if (identity == NULL)
1022 p = eap_sim_db_get_pseudonym(data, identity, identity_len);
1024 p = eap_sim_db_get_pseudonym_id(data, identity, identity_len);
1028 *len = p->identity_len;
1034 * eap_sim_db_get_reauth_entry - EAP-SIM DB: Get re-authentication entry
1035 * @priv: Private data pointer from eap_sim_db_init()
1036 * @identity: Identity of the user (may be permanent identity, pseudonym, or
1038 * @identity_len: Length of identity
1039 * @len: Buffer for length of the returned permanent identity
1040 * Returns: Pointer to the re-auth entry, or %NULL if not found
1042 struct eap_sim_reauth *
1043 eap_sim_db_get_reauth_entry(void *priv, const u8 *identity,
1044 size_t identity_len)
1046 struct eap_sim_db_data *data = priv;
1047 struct eap_sim_reauth *r;
1049 if (identity == NULL)
1051 r = eap_sim_db_get_reauth(data, identity, identity_len);
1053 r = eap_sim_db_get_reauth_id(data, identity, identity_len);
1059 * eap_sim_db_remove_reauth - EAP-SIM DB: Remove re-authentication entry
1060 * @priv: Private data pointer from eap_sim_db_init()
1061 * @reauth: Pointer to re-authentication entry from
1062 * eap_sim_db_get_reauth_entry()
1064 void eap_sim_db_remove_reauth(void *priv, struct eap_sim_reauth *reauth)
1066 struct eap_sim_db_data *data = priv;
1067 struct eap_sim_reauth *r, *prev = NULL;
1072 prev->next = r->next;
1074 data->reauths = r->next;
1075 eap_sim_db_free_reauth(r);
1085 * eap_sim_db_get_aka_auth - Get AKA authentication values
1086 * @priv: Private data pointer from eap_sim_db_init()
1087 * @identity: User name identity
1088 * @identity_len: Length of identity in bytes
1089 * @_rand: Buffer for RAND value
1090 * @autn: Buffer for AUTN value
1091 * @ik: Buffer for IK value
1092 * @ck: Buffer for CK value
1093 * @res: Buffer for RES value
1094 * @res_len: Buffer for RES length
1095 * @cb_session_ctx: Session callback context for get_complete_cb()
1096 * Returns: 0 on success, -1 (EAP_SIM_DB_FAILURE) on error (e.g., user not
1097 * found), or -2 (EAP_SIM_DB_PENDING) if results are not yet available. In this
1098 * case, the callback function registered with eap_sim_db_init() will be
1099 * called once the results become available.
1101 * In most cases, the user name is '0' | IMSI, i.e., 0 followed by the IMSI in
1104 * When using an external server for AKA authentication, this function can
1105 * always start a request and return EAP_SIM_DB_PENDING immediately if
1106 * authentication triplets are not available. Once the authentication data are
1107 * received, callback function registered with eap_sim_db_init() is called to
1108 * notify EAP state machine to reprocess the message. This
1109 * eap_sim_db_get_aka_auth() function will then be called again and the newly
1110 * received triplets will then be given to the caller.
1112 int eap_sim_db_get_aka_auth(void *priv, const u8 *identity,
1113 size_t identity_len, u8 *_rand, u8 *autn, u8 *ik,
1114 u8 *ck, u8 *res, size_t *res_len,
1115 void *cb_session_ctx)
1117 struct eap_sim_db_data *data = priv;
1118 struct eap_sim_db_pending *entry;
1123 if (identity_len < 2 || identity == NULL ||
1124 identity[0] != EAP_AKA_PERMANENT_PREFIX) {
1125 wpa_hexdump_ascii(MSG_DEBUG, "EAP-SIM DB: unexpected identity",
1126 identity, identity_len);
1127 return EAP_SIM_DB_FAILURE;
1131 for (i = 0; i < identity_len; i++) {
1132 if (identity[i] == '@') {
1137 if (identity_len + 1 > sizeof(entry->imsi)) {
1138 wpa_hexdump_ascii(MSG_DEBUG, "EAP-SIM DB: unexpected identity",
1139 identity, identity_len);
1140 return EAP_SIM_DB_FAILURE;
1142 wpa_hexdump_ascii(MSG_DEBUG, "EAP-SIM DB: Get AKA auth for IMSI",
1143 identity, identity_len);
1145 entry = eap_sim_db_get_pending(data, identity, identity_len, 1);
1147 if (entry->state == FAILURE) {
1149 wpa_printf(MSG_DEBUG, "EAP-SIM DB: Failure");
1150 return EAP_SIM_DB_FAILURE;
1153 if (entry->state == PENDING) {
1154 eap_sim_db_add_pending(data, entry);
1155 wpa_printf(MSG_DEBUG, "EAP-SIM DB: Pending");
1156 return EAP_SIM_DB_PENDING;
1159 wpa_printf(MSG_DEBUG, "EAP-SIM DB: Returning successfully "
1160 "received authentication data");
1161 memcpy(_rand, entry->u.aka.rand, EAP_AKA_RAND_LEN);
1162 memcpy(autn, entry->u.aka.autn, EAP_AKA_AUTN_LEN);
1163 memcpy(ik, entry->u.aka.ik, EAP_AKA_IK_LEN);
1164 memcpy(ck, entry->u.aka.ck, EAP_AKA_CK_LEN);
1165 memcpy(res, entry->u.aka.res, EAP_AKA_RES_MAX_LEN);
1166 *res_len = entry->u.aka.res_len;
1171 if (data->sock < 0) {
1172 if (eap_sim_db_open_socket(data) < 0)
1173 return EAP_SIM_DB_FAILURE;
1176 len = snprintf(msg, sizeof(msg), "AKA-REQ-AUTH ");
1177 if (len < 0 || len + identity_len >= sizeof(msg))
1178 return EAP_SIM_DB_FAILURE;
1179 memcpy(msg + len, identity, identity_len);
1180 len += identity_len;
1182 wpa_hexdump(MSG_DEBUG, "EAP-SIM DB: requesting AKA authentication "
1183 "data for IMSI", identity, identity_len);
1184 if (eap_sim_db_send(data, msg, len) < 0)
1185 return EAP_SIM_DB_FAILURE;
1187 entry = wpa_zalloc(sizeof(*entry));
1189 return EAP_SIM_DB_FAILURE;
1191 os_get_time(&entry->timestamp);
1193 memcpy(entry->imsi, identity, identity_len);
1194 entry->imsi_len = identity_len;
1195 entry->cb_session_ctx = cb_session_ctx;
1196 entry->state = PENDING;
1197 eap_sim_db_add_pending(data, entry);
1198 eap_sim_db_expire_pending(data);
1200 return EAP_SIM_DB_PENDING;
1205 * eap_sim_db_resynchronize - Resynchronize AKA AUTN
1206 * @priv: Private data pointer from eap_sim_db_init()
1207 * @identity: User name identity
1208 * @identity_len: Length of identity in bytes
1209 * @auts: AUTS value from the peer
1210 * @_rand: RAND value used in the rejected message
1211 * Returns: 0 on success, -1 on failure
1213 * This function is called when the peer reports synchronization failure in the
1214 * AUTN value by sending AUTS. The AUTS and RAND values should be sent to
1215 * HLR/AuC to allow it to resynchronize with the peer. After this,
1216 * eap_sim_db_get_aka_auth() will be called again to to fetch updated
1217 * RAND/AUTN values for the next challenge.
1219 int eap_sim_db_resynchronize(void *priv, const u8 *identity,
1220 size_t identity_len, const u8 *auts,
1223 struct eap_sim_db_data *data = priv;
1226 if (identity_len < 2 || identity == NULL ||
1227 identity[0] != EAP_AKA_PERMANENT_PREFIX) {
1228 wpa_hexdump_ascii(MSG_DEBUG, "EAP-SIM DB: unexpected identity",
1229 identity, identity_len);
1234 for (i = 0; i < identity_len; i++) {
1235 if (identity[i] == '@') {
1240 if (identity_len > 20) {
1241 wpa_hexdump_ascii(MSG_DEBUG, "EAP-SIM DB: unexpected identity",
1242 identity, identity_len);
1246 if (data->sock >= 0) {
1250 len = snprintf(msg, sizeof(msg), "AKA-AUTS ");
1251 if (len < 0 || len + identity_len >= sizeof(msg))
1253 memcpy(msg + len, identity, identity_len);
1254 len += identity_len;
1256 ret = snprintf(msg + len, sizeof(msg) - len, " ");
1257 if (ret < 0 || (size_t) ret >= sizeof(msg) - len)
1260 len += wpa_snprintf_hex(msg + len, sizeof(msg) - len,
1261 auts, EAP_AKA_AUTS_LEN);
1262 ret = snprintf(msg + len, sizeof(msg) - len, " ");
1263 if (ret < 0 || (size_t) ret >= sizeof(msg) - len)
1266 len += wpa_snprintf_hex(msg + len, sizeof(msg) - len,
1267 _rand, EAP_AKA_RAND_LEN);
1268 wpa_hexdump(MSG_DEBUG, "EAP-SIM DB: reporting AKA AUTS for "
1269 "IMSI", identity, identity_len);
1270 if (eap_sim_db_send(data, msg, len) < 0)