2 * hostapd / EAP-SIM database/authenticator gateway
3 * Copyright (c) 2005-2010, 2012, Jouni Malinen <j@w1.fi>
5 * This software may be distributed under the terms of the BSD license.
6 * See README for more details.
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.
22 #include "crypto/random.h"
23 #include "eap_common/eap_sim_common.h"
24 #include "eap_server/eap_sim_db.h"
27 struct eap_sim_pseudonym {
28 struct eap_sim_pseudonym *next;
34 struct eap_sim_db_pending {
35 struct eap_sim_db_pending *next;
38 enum { PENDING, SUCCESS, FAILURE } state;
40 struct os_time timestamp;
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];
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];
60 struct eap_sim_db_data {
64 void (*get_complete_cb)(void *ctx, void *session_ctx);
66 struct eap_sim_pseudonym *pseudonyms;
67 struct eap_sim_reauth *reauths;
68 struct eap_sim_db_pending *pending;
72 static struct eap_sim_db_pending *
73 eap_sim_db_get_pending(struct eap_sim_db_data *data, const u8 *imsi,
74 size_t imsi_len, int aka)
76 struct eap_sim_db_pending *entry, *prev = NULL;
78 entry = data->pending;
80 if (entry->aka == aka && entry->imsi_len == imsi_len &&
81 os_memcmp(entry->imsi, imsi, imsi_len) == 0) {
83 prev->next = entry->next;
85 data->pending = entry->next;
95 static void eap_sim_db_add_pending(struct eap_sim_db_data *data,
96 struct eap_sim_db_pending *entry)
98 entry->next = data->pending;
99 data->pending = entry;
103 static void eap_sim_db_sim_resp_auth(struct eap_sim_db_data *data,
104 const char *imsi, char *buf)
106 char *start, *end, *pos;
107 struct eap_sim_db_pending *entry;
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)
116 entry = eap_sim_db_get_pending(data, (u8 *) imsi, os_strlen(imsi), 0);
118 wpa_printf(MSG_DEBUG, "EAP-SIM DB: No pending entry for the "
119 "received message found");
124 if (os_strncmp(start, "FAILURE", 7) == 0) {
125 wpa_printf(MSG_DEBUG, "EAP-SIM DB: External server reported "
127 entry->state = FAILURE;
128 eap_sim_db_add_pending(data, entry);
129 data->get_complete_cb(data->ctx, entry->cb_session_ctx);
134 while (num_chal < EAP_SIM_MAX_CHAL) {
135 end = os_strchr(start, ' ');
139 pos = os_strchr(start, ':');
143 if (hexstr2bin(start, entry->u.sim.kc[num_chal],
148 pos = os_strchr(start, ':');
152 if (hexstr2bin(start, entry->u.sim.sres[num_chal],
157 if (hexstr2bin(start, entry->u.sim.rand[num_chal],
167 entry->u.sim.num_chal = num_chal;
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);
177 wpa_printf(MSG_DEBUG, "EAP-SIM DB: Failed to parse response string");
182 static void eap_sim_db_aka_resp_auth(struct eap_sim_db_data *data,
183 const char *imsi, char *buf)
186 struct eap_sim_db_pending *entry;
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)
194 entry = eap_sim_db_get_pending(data, (u8 *) imsi, os_strlen(imsi), 1);
196 wpa_printf(MSG_DEBUG, "EAP-SIM DB: No pending entry for the "
197 "received message found");
202 if (os_strncmp(start, "FAILURE", 7) == 0) {
203 wpa_printf(MSG_DEBUG, "EAP-SIM DB: External server reported "
205 entry->state = FAILURE;
206 eap_sim_db_add_pending(data, entry);
207 data->get_complete_cb(data->ctx, entry->cb_session_ctx);
211 end = os_strchr(start, ' ');
215 if (hexstr2bin(start, entry->u.aka.rand, EAP_AKA_RAND_LEN))
219 end = os_strchr(start, ' ');
223 if (hexstr2bin(start, entry->u.aka.autn, EAP_AKA_AUTN_LEN))
227 end = os_strchr(start, ' ');
231 if (hexstr2bin(start, entry->u.aka.ik, EAP_AKA_IK_LEN))
235 end = os_strchr(start, ' ');
239 if (hexstr2bin(start, entry->u.aka.ck, EAP_AKA_CK_LEN))
243 end = os_strchr(start, ' ');
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;
257 if (hexstr2bin(start, entry->u.aka.res, entry->u.aka.res_len))
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);
268 wpa_printf(MSG_DEBUG, "EAP-SIM DB: Failed to parse response string");
273 static void eap_sim_db_receive(int sock, void *eloop_ctx, void *sock_ctx)
275 struct eap_sim_db_data *data = eloop_ctx;
276 char buf[1000], *pos, *cmd, *imsi;
279 res = recv(sock, buf, sizeof(buf), 0);
282 wpa_hexdump_ascii_key(MSG_MSGDUMP, "EAP-SIM DB: Received from an "
283 "external source", (u8 *) buf, res);
286 if (res >= (int) sizeof(buf))
287 res = sizeof(buf) - 1;
290 if (data->get_complete_cb == NULL) {
291 wpa_printf(MSG_DEBUG, "EAP-SIM DB: No get_complete_cb "
296 /* <cmd> <IMSI> ... */
299 pos = os_strchr(cmd, ' ');
304 pos = os_strchr(imsi, ' ');
308 wpa_printf(MSG_DEBUG, "EAP-SIM DB: External response=%s for IMSI %s",
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);
316 wpa_printf(MSG_INFO, "EAP-SIM DB: Unknown external response "
321 wpa_printf(MSG_DEBUG, "EAP-SIM DB: Failed to parse response string");
325 static int eap_sim_db_open_socket(struct eap_sim_db_data *data)
327 struct sockaddr_un addr;
328 static int counter = 0;
330 if (os_strncmp(data->fname, "unix:", 5) != 0)
333 data->sock = socket(PF_UNIX, SOCK_DGRAM, 0);
334 if (data->sock < 0) {
335 perror("socket(eap_sim_db)");
339 os_memset(&addr, 0, sizeof(addr));
340 addr.sun_family = AF_UNIX;
341 os_snprintf(addr.sun_path, sizeof(addr.sun_path),
342 "/tmp/eap_sim_db_%d-%d", getpid(), counter++);
343 data->local_sock = os_strdup(addr.sun_path);
344 if (bind(data->sock, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
345 perror("bind(eap_sim_db)");
351 os_memset(&addr, 0, sizeof(addr));
352 addr.sun_family = AF_UNIX;
353 os_strlcpy(addr.sun_path, data->fname + 5, sizeof(addr.sun_path));
354 if (connect(data->sock, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
355 perror("connect(eap_sim_db)");
356 wpa_hexdump_ascii(MSG_INFO, "HLR/AuC GW socket",
357 (u8 *) addr.sun_path,
358 os_strlen(addr.sun_path));
364 eloop_register_read_sock(data->sock, eap_sim_db_receive, data, NULL);
370 static void eap_sim_db_close_socket(struct eap_sim_db_data *data)
372 if (data->sock >= 0) {
373 eloop_unregister_read_sock(data->sock);
377 if (data->local_sock) {
378 unlink(data->local_sock);
379 os_free(data->local_sock);
380 data->local_sock = NULL;
386 * eap_sim_db_init - Initialize EAP-SIM DB / authentication gateway interface
387 * @config: Configuration data (e.g., file name)
388 * @get_complete_cb: Callback function for reporting availability of triplets
389 * @ctx: Context pointer for get_complete_cb
390 * Returns: Pointer to a private data structure or %NULL on failure
392 void * eap_sim_db_init(const char *config,
393 void (*get_complete_cb)(void *ctx, void *session_ctx),
396 struct eap_sim_db_data *data;
398 data = os_zalloc(sizeof(*data));
403 data->get_complete_cb = get_complete_cb;
405 data->fname = os_strdup(config);
406 if (data->fname == NULL)
409 if (os_strncmp(data->fname, "unix:", 5) == 0) {
410 if (eap_sim_db_open_socket(data))
417 eap_sim_db_close_socket(data);
418 os_free(data->fname);
424 static void eap_sim_db_free_pseudonym(struct eap_sim_pseudonym *p)
426 os_free(p->identity);
427 os_free(p->pseudonym);
432 static void eap_sim_db_free_reauth(struct eap_sim_reauth *r)
434 os_free(r->identity);
435 os_free(r->reauth_id);
441 * eap_sim_db_deinit - Deinitialize EAP-SIM DB/authentication gw interface
442 * @priv: Private data pointer from eap_sim_db_init()
444 void eap_sim_db_deinit(void *priv)
446 struct eap_sim_db_data *data = priv;
447 struct eap_sim_pseudonym *p, *prev;
448 struct eap_sim_reauth *r, *prevr;
449 struct eap_sim_db_pending *pending, *prev_pending;
451 eap_sim_db_close_socket(data);
452 os_free(data->fname);
454 p = data->pseudonyms;
458 eap_sim_db_free_pseudonym(prev);
465 eap_sim_db_free_reauth(prevr);
468 pending = data->pending;
470 prev_pending = pending;
471 pending = pending->next;
472 os_free(prev_pending);
479 static int eap_sim_db_send(struct eap_sim_db_data *data, const char *msg,
484 if (send(data->sock, msg, len, 0) < 0) {
486 perror("send[EAP-SIM DB UNIX]");
489 if (_errno == ENOTCONN || _errno == EDESTADDRREQ || _errno == EINVAL ||
490 _errno == ECONNREFUSED) {
491 /* Try to reconnect */
492 eap_sim_db_close_socket(data);
493 if (eap_sim_db_open_socket(data) < 0)
495 wpa_printf(MSG_DEBUG, "EAP-SIM DB: Reconnected to the "
497 if (send(data->sock, msg, len, 0) < 0) {
498 perror("send[EAP-SIM DB UNIX]");
507 static void eap_sim_db_expire_pending(struct eap_sim_db_data *data)
509 /* TODO: add limit for maximum length for pending list; remove latest
510 * (i.e., last) entry from the list if the limit is reached; could also
511 * use timeout to expire pending entries */
516 * eap_sim_db_get_gsm_triplets - Get GSM triplets
517 * @priv: Private data pointer from eap_sim_db_init()
518 * @identity: User name identity
519 * @identity_len: Length of identity in bytes
520 * @max_chal: Maximum number of triplets
521 * @_rand: Buffer for RAND values
522 * @kc: Buffer for Kc values
523 * @sres: Buffer for SRES values
524 * @cb_session_ctx: Session callback context for get_complete_cb()
525 * Returns: Number of triplets received (has to be less than or equal to
526 * max_chal), -1 (EAP_SIM_DB_FAILURE) on error (e.g., user not found), or
527 * -2 (EAP_SIM_DB_PENDING) if results are not yet available. In this case, the
528 * callback function registered with eap_sim_db_init() will be called once the
529 * results become available.
531 * In most cases, the user name is '1' | IMSI, i.e., 1 followed by the IMSI in
534 * When using an external server for GSM triplets, this function can always
535 * start a request and return EAP_SIM_DB_PENDING immediately if authentication
536 * triplets are not available. Once the triplets are received, callback
537 * function registered with eap_sim_db_init() is called to notify EAP state
538 * machine to reprocess the message. This eap_sim_db_get_gsm_triplets()
539 * function will then be called again and the newly received triplets will then
540 * be given to the caller.
542 int eap_sim_db_get_gsm_triplets(void *priv, const u8 *identity,
543 size_t identity_len, int max_chal,
544 u8 *_rand, u8 *kc, u8 *sres,
545 void *cb_session_ctx)
547 struct eap_sim_db_data *data = priv;
548 struct eap_sim_db_pending *entry;
553 if (identity_len < 2 || identity[0] != EAP_SIM_PERMANENT_PREFIX) {
554 wpa_hexdump_ascii(MSG_DEBUG, "EAP-SIM DB: unexpected identity",
555 identity, identity_len);
556 return EAP_SIM_DB_FAILURE;
560 for (i = 0; i < identity_len; i++) {
561 if (identity[i] == '@') {
566 if (identity_len + 1 > sizeof(entry->imsi)) {
567 wpa_hexdump_ascii(MSG_DEBUG, "EAP-SIM DB: unexpected identity",
568 identity, identity_len);
569 return EAP_SIM_DB_FAILURE;
571 wpa_hexdump_ascii(MSG_DEBUG, "EAP-SIM DB: Get GSM triplets for IMSI",
572 identity, identity_len);
574 entry = eap_sim_db_get_pending(data, identity, identity_len, 0);
577 if (entry->state == FAILURE) {
578 wpa_printf(MSG_DEBUG, "EAP-SIM DB: Pending entry -> "
581 return EAP_SIM_DB_FAILURE;
584 if (entry->state == PENDING) {
585 wpa_printf(MSG_DEBUG, "EAP-SIM DB: Pending entry -> "
587 eap_sim_db_add_pending(data, entry);
588 return EAP_SIM_DB_PENDING;
591 wpa_printf(MSG_DEBUG, "EAP-SIM DB: Pending entry -> "
592 "%d challenges", entry->u.sim.num_chal);
593 num_chal = entry->u.sim.num_chal;
594 if (num_chal > max_chal)
596 os_memcpy(_rand, entry->u.sim.rand, num_chal * GSM_RAND_LEN);
597 os_memcpy(sres, entry->u.sim.sres,
598 num_chal * EAP_SIM_SRES_LEN);
599 os_memcpy(kc, entry->u.sim.kc, num_chal * EAP_SIM_KC_LEN);
604 if (data->sock < 0) {
605 if (eap_sim_db_open_socket(data) < 0)
606 return EAP_SIM_DB_FAILURE;
609 len = os_snprintf(msg, sizeof(msg), "SIM-REQ-AUTH ");
610 if (len < 0 || len + identity_len >= sizeof(msg))
611 return EAP_SIM_DB_FAILURE;
612 os_memcpy(msg + len, identity, identity_len);
614 ret = os_snprintf(msg + len, sizeof(msg) - len, " %d", max_chal);
615 if (ret < 0 || (size_t) ret >= sizeof(msg) - len)
616 return EAP_SIM_DB_FAILURE;
619 wpa_hexdump(MSG_DEBUG, "EAP-SIM DB: requesting SIM authentication "
620 "data for IMSI", identity, identity_len);
621 if (eap_sim_db_send(data, msg, len) < 0)
622 return EAP_SIM_DB_FAILURE;
624 entry = os_zalloc(sizeof(*entry));
626 return EAP_SIM_DB_FAILURE;
628 os_get_time(&entry->timestamp);
629 os_memcpy(entry->imsi, identity, identity_len);
630 entry->imsi_len = identity_len;
631 entry->cb_session_ctx = cb_session_ctx;
632 entry->state = PENDING;
633 eap_sim_db_add_pending(data, entry);
634 eap_sim_db_expire_pending(data);
636 return EAP_SIM_DB_PENDING;
640 static struct eap_sim_pseudonym *
641 eap_sim_db_get_pseudonym(struct eap_sim_db_data *data, const u8 *identity,
646 struct eap_sim_pseudonym *p;
648 if (identity_len == 0 ||
649 (identity[0] != EAP_SIM_PSEUDONYM_PREFIX &&
650 identity[0] != EAP_AKA_PSEUDONYM_PREFIX &&
651 identity[0] != EAP_AKA_PRIME_PSEUDONYM_PREFIX))
654 /* Remove possible realm from identity */
656 while (len < identity_len) {
657 if (identity[len] == '@')
662 pseudonym = os_malloc(len + 1);
663 if (pseudonym == NULL)
665 os_memcpy(pseudonym, identity, len);
666 pseudonym[len] = '\0';
668 p = data->pseudonyms;
670 if (os_strcmp(p->pseudonym, pseudonym) == 0)
681 static struct eap_sim_pseudonym *
682 eap_sim_db_get_pseudonym_id(struct eap_sim_db_data *data, const u8 *identity,
685 struct eap_sim_pseudonym *p;
687 if (identity_len == 0 ||
688 (identity[0] != EAP_SIM_PERMANENT_PREFIX &&
689 identity[0] != EAP_AKA_PERMANENT_PREFIX &&
690 identity[0] != EAP_AKA_PRIME_PERMANENT_PREFIX))
693 p = data->pseudonyms;
695 if (identity_len == p->identity_len &&
696 os_memcmp(p->identity, identity, identity_len) == 0)
705 static struct eap_sim_reauth *
706 eap_sim_db_get_reauth(struct eap_sim_db_data *data, const u8 *identity,
711 struct eap_sim_reauth *r;
713 if (identity_len == 0 ||
714 (identity[0] != EAP_SIM_REAUTH_ID_PREFIX &&
715 identity[0] != EAP_AKA_REAUTH_ID_PREFIX &&
716 identity[0] != EAP_AKA_PRIME_REAUTH_ID_PREFIX))
719 /* Remove possible realm from identity */
721 while (len < identity_len) {
722 if (identity[len] == '@')
727 reauth_id = os_malloc(len + 1);
728 if (reauth_id == NULL)
730 os_memcpy(reauth_id, identity, len);
731 reauth_id[len] = '\0';
735 if (os_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 os_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','6'] | IMSI, i.e., 1 followed by
784 * the IMSI in ASCII format for EAP-SIM, ['2','3','7'] | pseudonym, or
785 * ['4','5','7'] | reauth_id.
787 int eap_sim_db_identity_known(void *priv, const u8 *identity,
790 struct eap_sim_db_data *data = priv;
792 if (identity == NULL || identity_len < 2)
795 if (identity[0] == EAP_SIM_PSEUDONYM_PREFIX ||
796 identity[0] == EAP_AKA_PSEUDONYM_PREFIX ||
797 identity[0] == EAP_AKA_PRIME_PSEUDONYM_PREFIX) {
798 struct eap_sim_pseudonym *p =
799 eap_sim_db_get_pseudonym(data, identity, identity_len);
803 if (identity[0] == EAP_SIM_REAUTH_ID_PREFIX ||
804 identity[0] == EAP_AKA_REAUTH_ID_PREFIX ||
805 identity[0] == EAP_AKA_PRIME_REAUTH_ID_PREFIX) {
806 struct eap_sim_reauth *r =
807 eap_sim_db_get_reauth(data, identity, identity_len);
811 if (identity[0] != EAP_SIM_PERMANENT_PREFIX &&
812 identity[0] != EAP_AKA_PERMANENT_PREFIX &&
813 identity[0] != EAP_AKA_PRIME_PERMANENT_PREFIX) {
814 /* Unknown identity prefix */
818 /* TODO: Should consider asking HLR/AuC gateway whether this permanent
819 * identity is known. If it is, EAP-SIM/AKA can skip identity request.
820 * In case of EAP-AKA, this would reduce number of needed round-trips.
821 * Ideally, this would be done with one wait, i.e., just request
822 * authentication data and store it for the next use. This would then
823 * need to use similar pending-request functionality as the normal
824 * request for authentication data at later phase.
830 static char * eap_sim_db_get_next(struct eap_sim_db_data *data, char prefix)
832 char *id, *pos, *end;
835 if (random_get_bytes(buf, sizeof(buf)))
837 id = os_malloc(sizeof(buf) * 2 + 2);
842 end = id + sizeof(buf) * 2 + 2;
844 pos += wpa_snprintf_hex(pos, end - pos, buf, sizeof(buf));
851 * eap_sim_db_get_next_pseudonym - EAP-SIM DB: Get next pseudonym
852 * @priv: Private data pointer from eap_sim_db_init()
853 * @method: EAP method (SIM/AKA/AKA')
854 * Returns: Next pseudonym (allocated string) or %NULL on failure
856 * This function is used to generate a pseudonym for EAP-SIM. The returned
857 * pseudonym is not added to database at this point; it will need to be added
858 * with eap_sim_db_add_pseudonym() once the authentication has been completed
859 * successfully. Caller is responsible for freeing the returned buffer.
861 char * eap_sim_db_get_next_pseudonym(void *priv, enum eap_sim_db_method method)
863 struct eap_sim_db_data *data = priv;
864 char prefix = EAP_SIM_REAUTH_ID_PREFIX;
868 prefix = EAP_SIM_PSEUDONYM_PREFIX;
871 prefix = EAP_AKA_PSEUDONYM_PREFIX;
873 case EAP_SIM_DB_AKA_PRIME:
874 prefix = EAP_AKA_PRIME_PSEUDONYM_PREFIX;
878 return eap_sim_db_get_next(data, prefix);
883 * eap_sim_db_get_next_reauth_id - EAP-SIM DB: Get next reauth_id
884 * @priv: Private data pointer from eap_sim_db_init()
885 * @method: EAP method (SIM/AKA/AKA')
886 * Returns: Next reauth_id (allocated string) or %NULL on failure
888 * This function is used to generate a fast re-authentication identity for
889 * EAP-SIM. The returned reauth_id is not added to database at this point; it
890 * will need to be added with eap_sim_db_add_reauth() once the authentication
891 * has been completed successfully. Caller is responsible for freeing the
894 char * eap_sim_db_get_next_reauth_id(void *priv, enum eap_sim_db_method method)
896 struct eap_sim_db_data *data = priv;
897 char prefix = EAP_SIM_REAUTH_ID_PREFIX;
901 prefix = EAP_SIM_REAUTH_ID_PREFIX;
904 prefix = EAP_AKA_REAUTH_ID_PREFIX;
906 case EAP_SIM_DB_AKA_PRIME:
907 prefix = EAP_AKA_PRIME_REAUTH_ID_PREFIX;
911 return eap_sim_db_get_next(data, prefix);
916 * eap_sim_db_add_pseudonym - EAP-SIM DB: Add new pseudonym
917 * @priv: Private data pointer from eap_sim_db_init()
918 * @identity: Identity of the user (may be permanent identity or pseudonym)
919 * @identity_len: Length of identity
920 * @pseudonym: Pseudonym for this user. This needs to be an allocated buffer,
921 * e.g., return value from eap_sim_db_get_next_pseudonym(). Caller must not
923 * Returns: 0 on success, -1 on failure
925 * This function adds a new pseudonym for EAP-SIM user. EAP-SIM DB is
926 * responsible of freeing pseudonym buffer once it is not needed anymore.
928 int eap_sim_db_add_pseudonym(void *priv, const u8 *identity,
929 size_t identity_len, char *pseudonym)
931 struct eap_sim_db_data *data = priv;
932 struct eap_sim_pseudonym *p;
933 wpa_hexdump_ascii(MSG_DEBUG, "EAP-SIM DB: Add pseudonym for identity",
934 identity, identity_len);
935 wpa_printf(MSG_DEBUG, "EAP-SIM DB: Pseudonym: %s", pseudonym);
937 /* TODO: could store last two pseudonyms */
938 p = eap_sim_db_get_pseudonym(data, identity, identity_len);
940 p = eap_sim_db_get_pseudonym_id(data, identity, identity_len);
943 wpa_printf(MSG_DEBUG, "EAP-SIM DB: Replacing previous "
944 "pseudonym: %s", p->pseudonym);
945 os_free(p->pseudonym);
946 p->pseudonym = pseudonym;
950 p = os_zalloc(sizeof(*p));
956 p->next = data->pseudonyms;
957 p->identity = os_malloc(identity_len);
958 if (p->identity == NULL) {
963 os_memcpy(p->identity, identity, identity_len);
964 p->identity_len = identity_len;
965 p->pseudonym = pseudonym;
966 data->pseudonyms = p;
968 wpa_printf(MSG_DEBUG, "EAP-SIM DB: Added new pseudonym entry");
973 static struct eap_sim_reauth *
974 eap_sim_db_add_reauth_data(struct eap_sim_db_data *data, const u8 *identity,
975 size_t identity_len, char *reauth_id, u16 counter)
977 struct eap_sim_reauth *r;
979 wpa_hexdump_ascii(MSG_DEBUG, "EAP-SIM DB: Add reauth_id for identity",
980 identity, identity_len);
981 wpa_printf(MSG_DEBUG, "EAP-SIM DB: reauth_id: %s", reauth_id);
983 r = eap_sim_db_get_reauth(data, identity, identity_len);
985 r = eap_sim_db_get_reauth_id(data, identity, identity_len);
988 wpa_printf(MSG_DEBUG, "EAP-SIM DB: Replacing previous "
989 "reauth_id: %s", r->reauth_id);
990 os_free(r->reauth_id);
991 r->reauth_id = reauth_id;
993 r = os_zalloc(sizeof(*r));
999 r->next = data->reauths;
1000 r->identity = os_malloc(identity_len);
1001 if (r->identity == NULL) {
1006 os_memcpy(r->identity, identity, identity_len);
1007 r->identity_len = identity_len;
1008 r->reauth_id = reauth_id;
1010 wpa_printf(MSG_DEBUG, "EAP-SIM DB: Added new reauth entry");
1013 r->counter = counter;
1020 * eap_sim_db_add_reauth - EAP-SIM DB: Add new re-authentication entry
1021 * @priv: Private data pointer from eap_sim_db_init()
1022 * @identity: Identity of the user (may be permanent identity or pseudonym)
1023 * @identity_len: Length of identity
1024 * @reauth_id: reauth_id for this user. This needs to be an allocated buffer,
1025 * e.g., return value from eap_sim_db_get_next_reauth_id(). Caller must not
1027 * @counter: AT_COUNTER value for fast re-authentication
1028 * @mk: 16-byte MK from the previous full authentication or %NULL
1029 * Returns: 0 on success, -1 on failure
1031 * This function adds a new re-authentication entry for an EAP-SIM user.
1032 * EAP-SIM DB is responsible of freeing reauth_id buffer once it is not needed
1035 int eap_sim_db_add_reauth(void *priv, const u8 *identity,
1036 size_t identity_len, char *reauth_id, u16 counter,
1039 struct eap_sim_db_data *data = priv;
1040 struct eap_sim_reauth *r;
1042 r = eap_sim_db_add_reauth_data(data, identity, identity_len, reauth_id,
1047 os_memcpy(r->mk, mk, EAP_SIM_MK_LEN);
1054 #ifdef EAP_SERVER_AKA_PRIME
1056 * eap_sim_db_add_reauth_prime - EAP-AKA' DB: Add new re-authentication entry
1057 * @priv: Private data pointer from eap_sim_db_init()
1058 * @identity: Identity of the user (may be permanent identity or pseudonym)
1059 * @identity_len: Length of identity
1060 * @reauth_id: reauth_id for this user. This needs to be an allocated buffer,
1061 * e.g., return value from eap_sim_db_get_next_reauth_id(). Caller must not
1063 * @counter: AT_COUNTER value for fast re-authentication
1064 * @k_encr: K_encr from the previous full authentication
1065 * @k_aut: K_aut from the previous full authentication
1066 * @k_re: 32-byte K_re from the previous full authentication
1067 * Returns: 0 on success, -1 on failure
1069 * This function adds a new re-authentication entry for an EAP-AKA' user.
1070 * EAP-SIM DB is responsible of freeing reauth_id buffer once it is not needed
1073 int eap_sim_db_add_reauth_prime(void *priv, const u8 *identity,
1074 size_t identity_len, char *reauth_id,
1075 u16 counter, const u8 *k_encr, const u8 *k_aut,
1078 struct eap_sim_db_data *data = priv;
1079 struct eap_sim_reauth *r;
1081 r = eap_sim_db_add_reauth_data(data, identity, identity_len, reauth_id,
1087 os_memcpy(r->k_encr, k_encr, EAP_SIM_K_ENCR_LEN);
1088 os_memcpy(r->k_aut, k_aut, EAP_AKA_PRIME_K_AUT_LEN);
1089 os_memcpy(r->k_re, k_re, EAP_AKA_PRIME_K_RE_LEN);
1093 #endif /* EAP_SERVER_AKA_PRIME */
1097 * eap_sim_db_get_permanent - EAP-SIM DB: Get permanent identity
1098 * @priv: Private data pointer from eap_sim_db_init()
1099 * @identity: Identity of the user (may be permanent identity or pseudonym)
1100 * @identity_len: Length of identity
1101 * @len: Buffer for length of the returned permanent identity
1102 * Returns: Pointer to the permanent identity, or %NULL if not found
1104 const u8 * eap_sim_db_get_permanent(void *priv, const u8 *identity,
1105 size_t identity_len, size_t *len)
1107 struct eap_sim_db_data *data = priv;
1108 struct eap_sim_pseudonym *p;
1110 if (identity == NULL)
1113 p = eap_sim_db_get_pseudonym(data, identity, identity_len);
1115 p = eap_sim_db_get_pseudonym_id(data, identity, identity_len);
1119 *len = p->identity_len;
1125 * eap_sim_db_get_reauth_entry - EAP-SIM DB: Get re-authentication entry
1126 * @priv: Private data pointer from eap_sim_db_init()
1127 * @identity: Identity of the user (may be permanent identity, pseudonym, or
1129 * @identity_len: Length of identity
1130 * Returns: Pointer to the re-auth entry, or %NULL if not found
1132 struct eap_sim_reauth *
1133 eap_sim_db_get_reauth_entry(void *priv, const u8 *identity,
1134 size_t identity_len)
1136 struct eap_sim_db_data *data = priv;
1137 struct eap_sim_reauth *r;
1139 if (identity == NULL)
1141 r = eap_sim_db_get_reauth(data, identity, identity_len);
1143 r = eap_sim_db_get_reauth_id(data, identity, identity_len);
1149 * eap_sim_db_remove_reauth - EAP-SIM DB: Remove re-authentication entry
1150 * @priv: Private data pointer from eap_sim_db_init()
1151 * @reauth: Pointer to re-authentication entry from
1152 * eap_sim_db_get_reauth_entry()
1154 void eap_sim_db_remove_reauth(void *priv, struct eap_sim_reauth *reauth)
1156 struct eap_sim_db_data *data = priv;
1157 struct eap_sim_reauth *r, *prev = NULL;
1162 prev->next = r->next;
1164 data->reauths = r->next;
1165 eap_sim_db_free_reauth(r);
1175 * eap_sim_db_get_aka_auth - Get AKA authentication values
1176 * @priv: Private data pointer from eap_sim_db_init()
1177 * @identity: User name identity
1178 * @identity_len: Length of identity in bytes
1179 * @_rand: Buffer for RAND value
1180 * @autn: Buffer for AUTN value
1181 * @ik: Buffer for IK value
1182 * @ck: Buffer for CK value
1183 * @res: Buffer for RES value
1184 * @res_len: Buffer for RES length
1185 * @cb_session_ctx: Session callback context for get_complete_cb()
1186 * Returns: 0 on success, -1 (EAP_SIM_DB_FAILURE) on error (e.g., user not
1187 * found), or -2 (EAP_SIM_DB_PENDING) if results are not yet available. In this
1188 * case, the callback function registered with eap_sim_db_init() will be
1189 * called once the results become available.
1191 * In most cases, the user name is '0' | IMSI, i.e., 0 followed by the IMSI in
1192 * ASCII format for EAP-AKA and '6' | IMSI for EAP-AKA'.
1194 * When using an external server for AKA authentication, this function can
1195 * always start a request and return EAP_SIM_DB_PENDING immediately if
1196 * authentication triplets are not available. Once the authentication data are
1197 * received, callback function registered with eap_sim_db_init() is called to
1198 * notify EAP state machine to reprocess the message. This
1199 * eap_sim_db_get_aka_auth() function will then be called again and the newly
1200 * received triplets will then be given to the caller.
1202 int eap_sim_db_get_aka_auth(void *priv, const u8 *identity,
1203 size_t identity_len, u8 *_rand, u8 *autn, u8 *ik,
1204 u8 *ck, u8 *res, size_t *res_len,
1205 void *cb_session_ctx)
1207 struct eap_sim_db_data *data = priv;
1208 struct eap_sim_db_pending *entry;
1213 if (identity_len < 2 || identity == NULL ||
1214 (identity[0] != EAP_AKA_PERMANENT_PREFIX &&
1215 identity[0] != EAP_AKA_PRIME_PERMANENT_PREFIX)) {
1216 wpa_hexdump_ascii(MSG_DEBUG, "EAP-SIM DB: unexpected identity",
1217 identity, identity_len);
1218 return EAP_SIM_DB_FAILURE;
1222 for (i = 0; i < identity_len; i++) {
1223 if (identity[i] == '@') {
1228 if (identity_len + 1 > sizeof(entry->imsi)) {
1229 wpa_hexdump_ascii(MSG_DEBUG, "EAP-SIM DB: unexpected identity",
1230 identity, identity_len);
1231 return EAP_SIM_DB_FAILURE;
1233 wpa_hexdump_ascii(MSG_DEBUG, "EAP-SIM DB: Get AKA auth for IMSI",
1234 identity, identity_len);
1236 entry = eap_sim_db_get_pending(data, identity, identity_len, 1);
1238 if (entry->state == FAILURE) {
1240 wpa_printf(MSG_DEBUG, "EAP-SIM DB: Failure");
1241 return EAP_SIM_DB_FAILURE;
1244 if (entry->state == PENDING) {
1245 eap_sim_db_add_pending(data, entry);
1246 wpa_printf(MSG_DEBUG, "EAP-SIM DB: Pending");
1247 return EAP_SIM_DB_PENDING;
1250 wpa_printf(MSG_DEBUG, "EAP-SIM DB: Returning successfully "
1251 "received authentication data");
1252 os_memcpy(_rand, entry->u.aka.rand, EAP_AKA_RAND_LEN);
1253 os_memcpy(autn, entry->u.aka.autn, EAP_AKA_AUTN_LEN);
1254 os_memcpy(ik, entry->u.aka.ik, EAP_AKA_IK_LEN);
1255 os_memcpy(ck, entry->u.aka.ck, EAP_AKA_CK_LEN);
1256 os_memcpy(res, entry->u.aka.res, EAP_AKA_RES_MAX_LEN);
1257 *res_len = entry->u.aka.res_len;
1262 if (data->sock < 0) {
1263 if (eap_sim_db_open_socket(data) < 0)
1264 return EAP_SIM_DB_FAILURE;
1267 len = os_snprintf(msg, sizeof(msg), "AKA-REQ-AUTH ");
1268 if (len < 0 || len + identity_len >= sizeof(msg))
1269 return EAP_SIM_DB_FAILURE;
1270 os_memcpy(msg + len, identity, identity_len);
1271 len += identity_len;
1273 wpa_hexdump(MSG_DEBUG, "EAP-SIM DB: requesting AKA authentication "
1274 "data for IMSI", identity, identity_len);
1275 if (eap_sim_db_send(data, msg, len) < 0)
1276 return EAP_SIM_DB_FAILURE;
1278 entry = os_zalloc(sizeof(*entry));
1280 return EAP_SIM_DB_FAILURE;
1282 os_get_time(&entry->timestamp);
1284 os_memcpy(entry->imsi, identity, identity_len);
1285 entry->imsi_len = identity_len;
1286 entry->cb_session_ctx = cb_session_ctx;
1287 entry->state = PENDING;
1288 eap_sim_db_add_pending(data, entry);
1289 eap_sim_db_expire_pending(data);
1291 return EAP_SIM_DB_PENDING;
1296 * eap_sim_db_resynchronize - Resynchronize AKA AUTN
1297 * @priv: Private data pointer from eap_sim_db_init()
1298 * @identity: User name identity
1299 * @identity_len: Length of identity in bytes
1300 * @auts: AUTS value from the peer
1301 * @_rand: RAND value used in the rejected message
1302 * Returns: 0 on success, -1 on failure
1304 * This function is called when the peer reports synchronization failure in the
1305 * AUTN value by sending AUTS. The AUTS and RAND values should be sent to
1306 * HLR/AuC to allow it to resynchronize with the peer. After this,
1307 * eap_sim_db_get_aka_auth() will be called again to to fetch updated
1308 * RAND/AUTN values for the next challenge.
1310 int eap_sim_db_resynchronize(void *priv, const u8 *identity,
1311 size_t identity_len, const u8 *auts,
1314 struct eap_sim_db_data *data = priv;
1317 if (identity_len < 2 || identity == NULL ||
1318 (identity[0] != EAP_AKA_PERMANENT_PREFIX &&
1319 identity[0] != EAP_AKA_PRIME_PERMANENT_PREFIX)) {
1320 wpa_hexdump_ascii(MSG_DEBUG, "EAP-SIM DB: unexpected identity",
1321 identity, identity_len);
1326 for (i = 0; i < identity_len; i++) {
1327 if (identity[i] == '@') {
1332 if (identity_len > 20) {
1333 wpa_hexdump_ascii(MSG_DEBUG, "EAP-SIM DB: unexpected identity",
1334 identity, identity_len);
1338 if (data->sock >= 0) {
1342 len = os_snprintf(msg, sizeof(msg), "AKA-AUTS ");
1343 if (len < 0 || len + identity_len >= sizeof(msg))
1345 os_memcpy(msg + len, identity, identity_len);
1346 len += identity_len;
1348 ret = os_snprintf(msg + len, sizeof(msg) - len, " ");
1349 if (ret < 0 || (size_t) ret >= sizeof(msg) - len)
1352 len += wpa_snprintf_hex(msg + len, sizeof(msg) - len,
1353 auts, EAP_AKA_AUTS_LEN);
1354 ret = os_snprintf(msg + len, sizeof(msg) - len, " ");
1355 if (ret < 0 || (size_t) ret >= sizeof(msg) - len)
1358 len += wpa_snprintf_hex(msg + len, sizeof(msg) - len,
1359 _rand, EAP_AKA_RAND_LEN);
1360 wpa_hexdump(MSG_DEBUG, "EAP-SIM DB: reporting AKA AUTS for "
1361 "IMSI", identity, identity_len);
1362 if (eap_sim_db_send(data, msg, len) < 0)