2 * hostapd / EAP-SIM database/authenticator gateway
3 * Copyright (c) 2005-2007, 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 "crypto/random.h"
29 #include "eap_common/eap_sim_common.h"
30 #include "eap_server/eap_sim_db.h"
33 struct eap_sim_pseudonym {
34 struct eap_sim_pseudonym *next;
40 struct eap_sim_db_pending {
41 struct eap_sim_db_pending *next;
44 enum { PENDING, SUCCESS, FAILURE } state;
46 struct os_time timestamp;
50 u8 kc[EAP_SIM_MAX_CHAL][EAP_SIM_KC_LEN];
51 u8 sres[EAP_SIM_MAX_CHAL][EAP_SIM_SRES_LEN];
52 u8 rand[EAP_SIM_MAX_CHAL][GSM_RAND_LEN];
56 u8 rand[EAP_AKA_RAND_LEN];
57 u8 autn[EAP_AKA_AUTN_LEN];
58 u8 ik[EAP_AKA_IK_LEN];
59 u8 ck[EAP_AKA_CK_LEN];
60 u8 res[EAP_AKA_RES_MAX_LEN];
66 struct eap_sim_db_data {
70 void (*get_complete_cb)(void *ctx, void *session_ctx);
72 struct eap_sim_pseudonym *pseudonyms;
73 struct eap_sim_reauth *reauths;
74 struct eap_sim_db_pending *pending;
78 static struct eap_sim_db_pending *
79 eap_sim_db_get_pending(struct eap_sim_db_data *data, const u8 *imsi,
80 size_t imsi_len, int aka)
82 struct eap_sim_db_pending *entry, *prev = NULL;
84 entry = data->pending;
86 if (entry->aka == aka && entry->imsi_len == imsi_len &&
87 os_memcmp(entry->imsi, imsi, imsi_len) == 0) {
89 prev->next = entry->next;
91 data->pending = entry->next;
101 static void eap_sim_db_add_pending(struct eap_sim_db_data *data,
102 struct eap_sim_db_pending *entry)
104 entry->next = data->pending;
105 data->pending = entry;
109 static void eap_sim_db_sim_resp_auth(struct eap_sim_db_data *data,
110 const char *imsi, char *buf)
112 char *start, *end, *pos;
113 struct eap_sim_db_pending *entry;
117 * SIM-RESP-AUTH <IMSI> Kc(i):SRES(i):RAND(i) ...
118 * SIM-RESP-AUTH <IMSI> FAILURE
119 * (IMSI = ASCII string, Kc/SRES/RAND = hex string)
122 entry = eap_sim_db_get_pending(data, (u8 *) imsi, os_strlen(imsi), 0);
124 wpa_printf(MSG_DEBUG, "EAP-SIM DB: No pending entry for the "
125 "received message found");
130 if (os_strncmp(start, "FAILURE", 7) == 0) {
131 wpa_printf(MSG_DEBUG, "EAP-SIM DB: External server reported "
133 entry->state = FAILURE;
134 eap_sim_db_add_pending(data, entry);
135 data->get_complete_cb(data->ctx, entry->cb_session_ctx);
140 while (num_chal < EAP_SIM_MAX_CHAL) {
141 end = os_strchr(start, ' ');
145 pos = os_strchr(start, ':');
149 if (hexstr2bin(start, entry->u.sim.kc[num_chal],
154 pos = os_strchr(start, ':');
158 if (hexstr2bin(start, entry->u.sim.sres[num_chal],
163 if (hexstr2bin(start, entry->u.sim.rand[num_chal],
173 entry->u.sim.num_chal = num_chal;
175 entry->state = SUCCESS;
176 wpa_printf(MSG_DEBUG, "EAP-SIM DB: Authentication data parsed "
177 "successfully - callback");
178 eap_sim_db_add_pending(data, entry);
179 data->get_complete_cb(data->ctx, entry->cb_session_ctx);
183 wpa_printf(MSG_DEBUG, "EAP-SIM DB: Failed to parse response string");
188 static void eap_sim_db_aka_resp_auth(struct eap_sim_db_data *data,
189 const char *imsi, char *buf)
192 struct eap_sim_db_pending *entry;
195 * AKA-RESP-AUTH <IMSI> <RAND> <AUTN> <IK> <CK> <RES>
196 * AKA-RESP-AUTH <IMSI> FAILURE
197 * (IMSI = ASCII string, RAND/AUTN/IK/CK/RES = hex string)
200 entry = eap_sim_db_get_pending(data, (u8 *) imsi, os_strlen(imsi), 1);
202 wpa_printf(MSG_DEBUG, "EAP-SIM DB: No pending entry for the "
203 "received message found");
208 if (os_strncmp(start, "FAILURE", 7) == 0) {
209 wpa_printf(MSG_DEBUG, "EAP-SIM DB: External server reported "
211 entry->state = FAILURE;
212 eap_sim_db_add_pending(data, entry);
213 data->get_complete_cb(data->ctx, entry->cb_session_ctx);
217 end = os_strchr(start, ' ');
221 if (hexstr2bin(start, entry->u.aka.rand, EAP_AKA_RAND_LEN))
225 end = os_strchr(start, ' ');
229 if (hexstr2bin(start, entry->u.aka.autn, EAP_AKA_AUTN_LEN))
233 end = os_strchr(start, ' ');
237 if (hexstr2bin(start, entry->u.aka.ik, EAP_AKA_IK_LEN))
241 end = os_strchr(start, ' ');
245 if (hexstr2bin(start, entry->u.aka.ck, EAP_AKA_CK_LEN))
249 end = os_strchr(start, ' ');
257 entry->u.aka.res_len = (end - start) / 2;
258 if (entry->u.aka.res_len > EAP_AKA_RES_MAX_LEN) {
259 wpa_printf(MSG_DEBUG, "EAP-SIM DB: Too long RES");
260 entry->u.aka.res_len = 0;
263 if (hexstr2bin(start, entry->u.aka.res, entry->u.aka.res_len))
266 entry->state = SUCCESS;
267 wpa_printf(MSG_DEBUG, "EAP-SIM DB: Authentication data parsed "
268 "successfully - callback");
269 eap_sim_db_add_pending(data, entry);
270 data->get_complete_cb(data->ctx, entry->cb_session_ctx);
274 wpa_printf(MSG_DEBUG, "EAP-SIM DB: Failed to parse response string");
279 static void eap_sim_db_receive(int sock, void *eloop_ctx, void *sock_ctx)
281 struct eap_sim_db_data *data = eloop_ctx;
282 char buf[1000], *pos, *cmd, *imsi;
285 res = recv(sock, buf, sizeof(buf), 0);
288 wpa_hexdump_ascii_key(MSG_MSGDUMP, "EAP-SIM DB: Received from an "
289 "external source", (u8 *) buf, res);
292 if (res >= (int) sizeof(buf))
293 res = sizeof(buf) - 1;
296 if (data->get_complete_cb == NULL) {
297 wpa_printf(MSG_DEBUG, "EAP-SIM DB: No get_complete_cb "
302 /* <cmd> <IMSI> ... */
305 pos = os_strchr(cmd, ' ');
310 pos = os_strchr(imsi, ' ');
314 wpa_printf(MSG_DEBUG, "EAP-SIM DB: External response=%s for IMSI %s",
317 if (os_strcmp(cmd, "SIM-RESP-AUTH") == 0)
318 eap_sim_db_sim_resp_auth(data, imsi, pos + 1);
319 else if (os_strcmp(cmd, "AKA-RESP-AUTH") == 0)
320 eap_sim_db_aka_resp_auth(data, imsi, pos + 1);
322 wpa_printf(MSG_INFO, "EAP-SIM DB: Unknown external response "
327 wpa_printf(MSG_DEBUG, "EAP-SIM DB: Failed to parse response string");
331 static int eap_sim_db_open_socket(struct eap_sim_db_data *data)
333 struct sockaddr_un addr;
334 static int counter = 0;
336 if (os_strncmp(data->fname, "unix:", 5) != 0)
339 data->sock = socket(PF_UNIX, SOCK_DGRAM, 0);
340 if (data->sock < 0) {
341 perror("socket(eap_sim_db)");
345 os_memset(&addr, 0, sizeof(addr));
346 addr.sun_family = AF_UNIX;
347 os_snprintf(addr.sun_path, sizeof(addr.sun_path),
348 "/tmp/eap_sim_db_%d-%d", getpid(), counter++);
349 data->local_sock = os_strdup(addr.sun_path);
350 if (bind(data->sock, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
351 perror("bind(eap_sim_db)");
357 os_memset(&addr, 0, sizeof(addr));
358 addr.sun_family = AF_UNIX;
359 os_strlcpy(addr.sun_path, data->fname + 5, sizeof(addr.sun_path));
360 if (connect(data->sock, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
361 perror("connect(eap_sim_db)");
362 wpa_hexdump_ascii(MSG_INFO, "HLR/AuC GW socket",
363 (u8 *) addr.sun_path,
364 os_strlen(addr.sun_path));
370 eloop_register_read_sock(data->sock, eap_sim_db_receive, data, NULL);
376 static void eap_sim_db_close_socket(struct eap_sim_db_data *data)
378 if (data->sock >= 0) {
379 eloop_unregister_read_sock(data->sock);
383 if (data->local_sock) {
384 unlink(data->local_sock);
385 os_free(data->local_sock);
386 data->local_sock = NULL;
392 * eap_sim_db_init - Initialize EAP-SIM DB / authentication gateway interface
393 * @config: Configuration data (e.g., file name)
394 * @get_complete_cb: Callback function for reporting availability of triplets
395 * @ctx: Context pointer for get_complete_cb
396 * Returns: Pointer to a private data structure or %NULL on failure
398 void * eap_sim_db_init(const char *config,
399 void (*get_complete_cb)(void *ctx, void *session_ctx),
402 struct eap_sim_db_data *data;
404 data = os_zalloc(sizeof(*data));
409 data->get_complete_cb = get_complete_cb;
411 data->fname = os_strdup(config);
412 if (data->fname == NULL)
415 if (os_strncmp(data->fname, "unix:", 5) == 0) {
416 if (eap_sim_db_open_socket(data))
423 eap_sim_db_close_socket(data);
424 os_free(data->fname);
430 static void eap_sim_db_free_pseudonym(struct eap_sim_pseudonym *p)
432 os_free(p->identity);
433 os_free(p->pseudonym);
438 static void eap_sim_db_free_reauth(struct eap_sim_reauth *r)
440 os_free(r->identity);
441 os_free(r->reauth_id);
447 * eap_sim_db_deinit - Deinitialize EAP-SIM DB/authentication gw interface
448 * @priv: Private data pointer from eap_sim_db_init()
450 void eap_sim_db_deinit(void *priv)
452 struct eap_sim_db_data *data = priv;
453 struct eap_sim_pseudonym *p, *prev;
454 struct eap_sim_reauth *r, *prevr;
455 struct eap_sim_db_pending *pending, *prev_pending;
457 eap_sim_db_close_socket(data);
458 os_free(data->fname);
460 p = data->pseudonyms;
464 eap_sim_db_free_pseudonym(prev);
471 eap_sim_db_free_reauth(prevr);
474 pending = data->pending;
476 prev_pending = pending;
477 pending = pending->next;
478 os_free(prev_pending);
485 static int eap_sim_db_send(struct eap_sim_db_data *data, const char *msg,
490 if (send(data->sock, msg, len, 0) < 0) {
492 perror("send[EAP-SIM DB UNIX]");
495 if (_errno == ENOTCONN || _errno == EDESTADDRREQ || _errno == EINVAL ||
496 _errno == ECONNREFUSED) {
497 /* Try to reconnect */
498 eap_sim_db_close_socket(data);
499 if (eap_sim_db_open_socket(data) < 0)
501 wpa_printf(MSG_DEBUG, "EAP-SIM DB: Reconnected to the "
503 if (send(data->sock, msg, len, 0) < 0) {
504 perror("send[EAP-SIM DB UNIX]");
513 static void eap_sim_db_expire_pending(struct eap_sim_db_data *data)
515 /* TODO: add limit for maximum length for pending list; remove latest
516 * (i.e., last) entry from the list if the limit is reached; could also
517 * use timeout to expire pending entries */
522 * eap_sim_db_get_gsm_triplets - Get GSM triplets
523 * @priv: Private data pointer from eap_sim_db_init()
524 * @identity: User name identity
525 * @identity_len: Length of identity in bytes
526 * @max_chal: Maximum number of triplets
527 * @_rand: Buffer for RAND values
528 * @kc: Buffer for Kc values
529 * @sres: Buffer for SRES values
530 * @cb_session_ctx: Session callback context for get_complete_cb()
531 * Returns: Number of triplets received (has to be less than or equal to
532 * max_chal), -1 (EAP_SIM_DB_FAILURE) on error (e.g., user not found), or
533 * -2 (EAP_SIM_DB_PENDING) if results are not yet available. In this case, the
534 * callback function registered with eap_sim_db_init() will be called once the
535 * results become available.
537 * In most cases, the user name is '1' | IMSI, i.e., 1 followed by the IMSI in
540 * When using an external server for GSM triplets, this function can always
541 * start a request and return EAP_SIM_DB_PENDING immediately if authentication
542 * triplets are not available. Once the triplets are received, callback
543 * function registered with eap_sim_db_init() is called to notify EAP state
544 * machine to reprocess the message. This eap_sim_db_get_gsm_triplets()
545 * function will then be called again and the newly received triplets will then
546 * be given to the caller.
548 int eap_sim_db_get_gsm_triplets(void *priv, const u8 *identity,
549 size_t identity_len, int max_chal,
550 u8 *_rand, u8 *kc, u8 *sres,
551 void *cb_session_ctx)
553 struct eap_sim_db_data *data = priv;
554 struct eap_sim_db_pending *entry;
559 if (identity_len < 2 || identity[0] != EAP_SIM_PERMANENT_PREFIX) {
560 wpa_hexdump_ascii(MSG_DEBUG, "EAP-SIM DB: unexpected identity",
561 identity, identity_len);
562 return EAP_SIM_DB_FAILURE;
566 for (i = 0; i < identity_len; i++) {
567 if (identity[i] == '@') {
572 if (identity_len + 1 > sizeof(entry->imsi)) {
573 wpa_hexdump_ascii(MSG_DEBUG, "EAP-SIM DB: unexpected identity",
574 identity, identity_len);
575 return EAP_SIM_DB_FAILURE;
577 wpa_hexdump_ascii(MSG_DEBUG, "EAP-SIM DB: Get GSM triplets for IMSI",
578 identity, identity_len);
580 entry = eap_sim_db_get_pending(data, identity, identity_len, 0);
583 if (entry->state == FAILURE) {
584 wpa_printf(MSG_DEBUG, "EAP-SIM DB: Pending entry -> "
587 return EAP_SIM_DB_FAILURE;
590 if (entry->state == PENDING) {
591 wpa_printf(MSG_DEBUG, "EAP-SIM DB: Pending entry -> "
593 eap_sim_db_add_pending(data, entry);
594 return EAP_SIM_DB_PENDING;
597 wpa_printf(MSG_DEBUG, "EAP-SIM DB: Pending entry -> "
598 "%d challenges", entry->u.sim.num_chal);
599 num_chal = entry->u.sim.num_chal;
600 if (num_chal > max_chal)
602 os_memcpy(_rand, entry->u.sim.rand, num_chal * GSM_RAND_LEN);
603 os_memcpy(sres, entry->u.sim.sres,
604 num_chal * EAP_SIM_SRES_LEN);
605 os_memcpy(kc, entry->u.sim.kc, num_chal * EAP_SIM_KC_LEN);
610 if (data->sock < 0) {
611 if (eap_sim_db_open_socket(data) < 0)
612 return EAP_SIM_DB_FAILURE;
615 len = os_snprintf(msg, sizeof(msg), "SIM-REQ-AUTH ");
616 if (len < 0 || len + identity_len >= sizeof(msg))
617 return EAP_SIM_DB_FAILURE;
618 os_memcpy(msg + len, identity, identity_len);
620 ret = os_snprintf(msg + len, sizeof(msg) - len, " %d", max_chal);
621 if (ret < 0 || (size_t) ret >= sizeof(msg) - len)
622 return EAP_SIM_DB_FAILURE;
625 wpa_hexdump(MSG_DEBUG, "EAP-SIM DB: requesting SIM authentication "
626 "data for IMSI", identity, identity_len);
627 if (eap_sim_db_send(data, msg, len) < 0)
628 return EAP_SIM_DB_FAILURE;
630 entry = os_zalloc(sizeof(*entry));
632 return EAP_SIM_DB_FAILURE;
634 os_get_time(&entry->timestamp);
635 os_memcpy(entry->imsi, identity, identity_len);
636 entry->imsi_len = identity_len;
637 entry->cb_session_ctx = cb_session_ctx;
638 entry->state = PENDING;
639 eap_sim_db_add_pending(data, entry);
640 eap_sim_db_expire_pending(data);
642 return EAP_SIM_DB_PENDING;
646 static struct eap_sim_pseudonym *
647 eap_sim_db_get_pseudonym(struct eap_sim_db_data *data, const u8 *identity,
652 struct eap_sim_pseudonym *p;
654 if (identity_len == 0 ||
655 (identity[0] != EAP_SIM_PSEUDONYM_PREFIX &&
656 identity[0] != EAP_AKA_PSEUDONYM_PREFIX))
659 /* Remove possible realm from identity */
661 while (len < identity_len) {
662 if (identity[len] == '@')
667 pseudonym = os_malloc(len + 1);
668 if (pseudonym == NULL)
670 os_memcpy(pseudonym, identity, len);
671 pseudonym[len] = '\0';
673 p = data->pseudonyms;
675 if (os_strcmp(p->pseudonym, pseudonym) == 0)
686 static struct eap_sim_pseudonym *
687 eap_sim_db_get_pseudonym_id(struct eap_sim_db_data *data, const u8 *identity,
690 struct eap_sim_pseudonym *p;
692 if (identity_len == 0 ||
693 (identity[0] != EAP_SIM_PERMANENT_PREFIX &&
694 identity[0] != EAP_AKA_PERMANENT_PREFIX))
697 p = data->pseudonyms;
699 if (identity_len == p->identity_len &&
700 os_memcmp(p->identity, identity, identity_len) == 0)
709 static struct eap_sim_reauth *
710 eap_sim_db_get_reauth(struct eap_sim_db_data *data, const u8 *identity,
715 struct eap_sim_reauth *r;
717 if (identity_len == 0 ||
718 (identity[0] != EAP_SIM_REAUTH_ID_PREFIX &&
719 identity[0] != EAP_AKA_REAUTH_ID_PREFIX))
722 /* Remove possible realm from identity */
724 while (len < identity_len) {
725 if (identity[len] == '@')
730 reauth_id = os_malloc(len + 1);
731 if (reauth_id == NULL)
733 os_memcpy(reauth_id, identity, len);
734 reauth_id[len] = '\0';
738 if (os_strcmp(r->reauth_id, reauth_id) == 0)
749 static struct eap_sim_reauth *
750 eap_sim_db_get_reauth_id(struct eap_sim_db_data *data, const u8 *identity,
753 struct eap_sim_pseudonym *p;
754 struct eap_sim_reauth *r;
756 if (identity_len == 0)
759 p = eap_sim_db_get_pseudonym(data, identity, identity_len);
761 p = eap_sim_db_get_pseudonym_id(data, identity, identity_len);
763 identity = p->identity;
764 identity_len = p->identity_len;
769 if (identity_len == r->identity_len &&
770 os_memcmp(r->identity, identity, identity_len) == 0)
780 * eap_sim_db_identity_known - Verify whether the given identity is known
781 * @priv: Private data pointer from eap_sim_db_init()
782 * @identity: User name identity
783 * @identity_len: Length of identity in bytes
784 * Returns: 0 if the user is found or -1 on failure
786 * In most cases, the user name is ['0','1'] | IMSI, i.e., 1 followed by the
787 * IMSI in ASCII format, ['2','3'] | pseudonym, or ['4','5'] | reauth_id.
789 int eap_sim_db_identity_known(void *priv, const u8 *identity,
792 struct eap_sim_db_data *data = priv;
794 if (identity == NULL || identity_len < 2)
797 if (identity[0] == EAP_SIM_PSEUDONYM_PREFIX ||
798 identity[0] == EAP_AKA_PSEUDONYM_PREFIX) {
799 struct eap_sim_pseudonym *p =
800 eap_sim_db_get_pseudonym(data, identity, identity_len);
804 if (identity[0] == EAP_SIM_REAUTH_ID_PREFIX ||
805 identity[0] == EAP_AKA_REAUTH_ID_PREFIX) {
806 struct eap_sim_reauth *r =
807 eap_sim_db_get_reauth(data, identity, identity_len);
811 if (identity[0] != EAP_SIM_PERMANENT_PREFIX &&
812 identity[0] != EAP_AKA_PERMANENT_PREFIX) {
813 /* Unknown identity prefix */
817 /* TODO: Should consider asking HLR/AuC gateway whether this permanent
818 * identity is known. If it is, EAP-SIM/AKA can skip identity request.
819 * In case of EAP-AKA, this would reduce number of needed round-trips.
820 * Ideally, this would be done with one wait, i.e., just request
821 * authentication data and store it for the next use. This would then
822 * need to use similar pending-request functionality as the normal
823 * request for authentication data at later phase.
829 static char * eap_sim_db_get_next(struct eap_sim_db_data *data, char prefix)
831 char *id, *pos, *end;
834 if (random_get_bytes(buf, sizeof(buf)))
836 id = os_malloc(sizeof(buf) * 2 + 2);
841 end = id + sizeof(buf) * 2 + 2;
843 pos += wpa_snprintf_hex(pos, end - pos, buf, sizeof(buf));
850 * eap_sim_db_get_next_pseudonym - EAP-SIM DB: Get next pseudonym
851 * @priv: Private data pointer from eap_sim_db_init()
852 * @aka: Using EAP-AKA instead of EAP-SIM
853 * Returns: Next pseudonym (allocated string) or %NULL on failure
855 * This function is used to generate a pseudonym for EAP-SIM. The returned
856 * pseudonym is not added to database at this point; it will need to be added
857 * with eap_sim_db_add_pseudonym() once the authentication has been completed
858 * successfully. Caller is responsible for freeing the returned buffer.
860 char * eap_sim_db_get_next_pseudonym(void *priv, int aka)
862 struct eap_sim_db_data *data = priv;
863 return eap_sim_db_get_next(data, aka ? EAP_AKA_PSEUDONYM_PREFIX :
864 EAP_SIM_PSEUDONYM_PREFIX);
869 * eap_sim_db_get_next_reauth_id - EAP-SIM DB: Get next reauth_id
870 * @priv: Private data pointer from eap_sim_db_init()
871 * @aka: Using EAP-AKA instead of EAP-SIM
872 * Returns: Next reauth_id (allocated string) or %NULL on failure
874 * This function is used to generate a fast re-authentication identity for
875 * EAP-SIM. The returned reauth_id is not added to database at this point; it
876 * will need to be added with eap_sim_db_add_reauth() once the authentication
877 * has been completed successfully. Caller is responsible for freeing the
880 char * eap_sim_db_get_next_reauth_id(void *priv, int aka)
882 struct eap_sim_db_data *data = priv;
883 return eap_sim_db_get_next(data, aka ? EAP_AKA_REAUTH_ID_PREFIX :
884 EAP_SIM_REAUTH_ID_PREFIX);
889 * eap_sim_db_add_pseudonym - EAP-SIM DB: Add new pseudonym
890 * @priv: Private data pointer from eap_sim_db_init()
891 * @identity: Identity of the user (may be permanent identity or pseudonym)
892 * @identity_len: Length of identity
893 * @pseudonym: Pseudonym for this user. This needs to be an allocated buffer,
894 * e.g., return value from eap_sim_db_get_next_pseudonym(). Caller must not
896 * Returns: 0 on success, -1 on failure
898 * This function adds a new pseudonym for EAP-SIM user. EAP-SIM DB is
899 * responsible of freeing pseudonym buffer once it is not needed anymore.
901 int eap_sim_db_add_pseudonym(void *priv, const u8 *identity,
902 size_t identity_len, char *pseudonym)
904 struct eap_sim_db_data *data = priv;
905 struct eap_sim_pseudonym *p;
906 wpa_hexdump_ascii(MSG_DEBUG, "EAP-SIM DB: Add pseudonym for identity",
907 identity, identity_len);
908 wpa_printf(MSG_DEBUG, "EAP-SIM DB: Pseudonym: %s", pseudonym);
910 /* TODO: could store last two pseudonyms */
911 p = eap_sim_db_get_pseudonym(data, identity, identity_len);
913 p = eap_sim_db_get_pseudonym_id(data, identity, identity_len);
916 wpa_printf(MSG_DEBUG, "EAP-SIM DB: Replacing previous "
917 "pseudonym: %s", p->pseudonym);
918 os_free(p->pseudonym);
919 p->pseudonym = pseudonym;
923 p = os_zalloc(sizeof(*p));
929 p->next = data->pseudonyms;
930 p->identity = os_malloc(identity_len);
931 if (p->identity == NULL) {
936 os_memcpy(p->identity, identity, identity_len);
937 p->identity_len = identity_len;
938 p->pseudonym = pseudonym;
939 data->pseudonyms = p;
941 wpa_printf(MSG_DEBUG, "EAP-SIM DB: Added new pseudonym entry");
946 static struct eap_sim_reauth *
947 eap_sim_db_add_reauth_data(struct eap_sim_db_data *data, const u8 *identity,
948 size_t identity_len, char *reauth_id, u16 counter)
950 struct eap_sim_reauth *r;
952 wpa_hexdump_ascii(MSG_DEBUG, "EAP-SIM DB: Add reauth_id for identity",
953 identity, identity_len);
954 wpa_printf(MSG_DEBUG, "EAP-SIM DB: reauth_id: %s", reauth_id);
956 r = eap_sim_db_get_reauth(data, identity, identity_len);
958 r = eap_sim_db_get_reauth_id(data, identity, identity_len);
961 wpa_printf(MSG_DEBUG, "EAP-SIM DB: Replacing previous "
962 "reauth_id: %s", r->reauth_id);
963 os_free(r->reauth_id);
964 r->reauth_id = reauth_id;
966 r = os_zalloc(sizeof(*r));
972 r->next = data->reauths;
973 r->identity = os_malloc(identity_len);
974 if (r->identity == NULL) {
979 os_memcpy(r->identity, identity, identity_len);
980 r->identity_len = identity_len;
981 r->reauth_id = reauth_id;
983 wpa_printf(MSG_DEBUG, "EAP-SIM DB: Added new reauth entry");
986 r->counter = counter;
993 * eap_sim_db_add_reauth - EAP-SIM DB: Add new re-authentication entry
994 * @priv: Private data pointer from eap_sim_db_init()
995 * @identity: Identity of the user (may be permanent identity or pseudonym)
996 * @identity_len: Length of identity
997 * @reauth_id: reauth_id for this user. This needs to be an allocated buffer,
998 * e.g., return value from eap_sim_db_get_next_reauth_id(). Caller must not
1000 * @counter: AT_COUNTER value for fast re-authentication
1001 * @mk: 16-byte MK from the previous full authentication or %NULL
1002 * Returns: 0 on success, -1 on failure
1004 * This function adds a new re-authentication entry for an EAP-SIM user.
1005 * EAP-SIM DB is responsible of freeing reauth_id buffer once it is not needed
1008 int eap_sim_db_add_reauth(void *priv, const u8 *identity,
1009 size_t identity_len, char *reauth_id, u16 counter,
1012 struct eap_sim_db_data *data = priv;
1013 struct eap_sim_reauth *r;
1015 r = eap_sim_db_add_reauth_data(data, identity, identity_len, reauth_id,
1020 os_memcpy(r->mk, mk, EAP_SIM_MK_LEN);
1027 #ifdef EAP_SERVER_AKA_PRIME
1029 * eap_sim_db_add_reauth_prime - EAP-AKA' DB: Add new re-authentication entry
1030 * @priv: Private data pointer from eap_sim_db_init()
1031 * @identity: Identity of the user (may be permanent identity or pseudonym)
1032 * @identity_len: Length of identity
1033 * @reauth_id: reauth_id for this user. This needs to be an allocated buffer,
1034 * e.g., return value from eap_sim_db_get_next_reauth_id(). Caller must not
1036 * @counter: AT_COUNTER value for fast re-authentication
1037 * @k_encr: K_encr from the previous full authentication
1038 * @k_aut: K_aut from the previous full authentication
1039 * @k_re: 32-byte K_re from the previous full authentication
1040 * Returns: 0 on success, -1 on failure
1042 * This function adds a new re-authentication entry for an EAP-AKA' user.
1043 * EAP-SIM DB is responsible of freeing reauth_id buffer once it is not needed
1046 int eap_sim_db_add_reauth_prime(void *priv, const u8 *identity,
1047 size_t identity_len, char *reauth_id,
1048 u16 counter, const u8 *k_encr, const u8 *k_aut,
1051 struct eap_sim_db_data *data = priv;
1052 struct eap_sim_reauth *r;
1054 r = eap_sim_db_add_reauth_data(data, identity, identity_len, reauth_id,
1060 os_memcpy(r->k_encr, k_encr, EAP_SIM_K_ENCR_LEN);
1061 os_memcpy(r->k_aut, k_aut, EAP_AKA_PRIME_K_AUT_LEN);
1062 os_memcpy(r->k_re, k_re, EAP_AKA_PRIME_K_RE_LEN);
1066 #endif /* EAP_SERVER_AKA_PRIME */
1070 * eap_sim_db_get_permanent - EAP-SIM DB: Get permanent identity
1071 * @priv: Private data pointer from eap_sim_db_init()
1072 * @identity: Identity of the user (may be permanent identity or pseudonym)
1073 * @identity_len: Length of identity
1074 * @len: Buffer for length of the returned permanent identity
1075 * Returns: Pointer to the permanent identity, or %NULL if not found
1077 const u8 * eap_sim_db_get_permanent(void *priv, const u8 *identity,
1078 size_t identity_len, size_t *len)
1080 struct eap_sim_db_data *data = priv;
1081 struct eap_sim_pseudonym *p;
1083 if (identity == NULL)
1086 p = eap_sim_db_get_pseudonym(data, identity, identity_len);
1088 p = eap_sim_db_get_pseudonym_id(data, identity, identity_len);
1092 *len = p->identity_len;
1098 * eap_sim_db_get_reauth_entry - EAP-SIM DB: Get re-authentication entry
1099 * @priv: Private data pointer from eap_sim_db_init()
1100 * @identity: Identity of the user (may be permanent identity, pseudonym, or
1102 * @identity_len: Length of identity
1103 * Returns: Pointer to the re-auth entry, or %NULL if not found
1105 struct eap_sim_reauth *
1106 eap_sim_db_get_reauth_entry(void *priv, const u8 *identity,
1107 size_t identity_len)
1109 struct eap_sim_db_data *data = priv;
1110 struct eap_sim_reauth *r;
1112 if (identity == NULL)
1114 r = eap_sim_db_get_reauth(data, identity, identity_len);
1116 r = eap_sim_db_get_reauth_id(data, identity, identity_len);
1122 * eap_sim_db_remove_reauth - EAP-SIM DB: Remove re-authentication entry
1123 * @priv: Private data pointer from eap_sim_db_init()
1124 * @reauth: Pointer to re-authentication entry from
1125 * eap_sim_db_get_reauth_entry()
1127 void eap_sim_db_remove_reauth(void *priv, struct eap_sim_reauth *reauth)
1129 struct eap_sim_db_data *data = priv;
1130 struct eap_sim_reauth *r, *prev = NULL;
1135 prev->next = r->next;
1137 data->reauths = r->next;
1138 eap_sim_db_free_reauth(r);
1148 * eap_sim_db_get_aka_auth - Get AKA authentication values
1149 * @priv: Private data pointer from eap_sim_db_init()
1150 * @identity: User name identity
1151 * @identity_len: Length of identity in bytes
1152 * @_rand: Buffer for RAND value
1153 * @autn: Buffer for AUTN value
1154 * @ik: Buffer for IK value
1155 * @ck: Buffer for CK value
1156 * @res: Buffer for RES value
1157 * @res_len: Buffer for RES length
1158 * @cb_session_ctx: Session callback context for get_complete_cb()
1159 * Returns: 0 on success, -1 (EAP_SIM_DB_FAILURE) on error (e.g., user not
1160 * found), or -2 (EAP_SIM_DB_PENDING) if results are not yet available. In this
1161 * case, the callback function registered with eap_sim_db_init() will be
1162 * called once the results become available.
1164 * In most cases, the user name is '0' | IMSI, i.e., 0 followed by the IMSI in
1167 * When using an external server for AKA authentication, this function can
1168 * always start a request and return EAP_SIM_DB_PENDING immediately if
1169 * authentication triplets are not available. Once the authentication data are
1170 * received, callback function registered with eap_sim_db_init() is called to
1171 * notify EAP state machine to reprocess the message. This
1172 * eap_sim_db_get_aka_auth() function will then be called again and the newly
1173 * received triplets will then be given to the caller.
1175 int eap_sim_db_get_aka_auth(void *priv, const u8 *identity,
1176 size_t identity_len, u8 *_rand, u8 *autn, u8 *ik,
1177 u8 *ck, u8 *res, size_t *res_len,
1178 void *cb_session_ctx)
1180 struct eap_sim_db_data *data = priv;
1181 struct eap_sim_db_pending *entry;
1186 if (identity_len < 2 || identity == NULL ||
1187 identity[0] != EAP_AKA_PERMANENT_PREFIX) {
1188 wpa_hexdump_ascii(MSG_DEBUG, "EAP-SIM DB: unexpected identity",
1189 identity, identity_len);
1190 return EAP_SIM_DB_FAILURE;
1194 for (i = 0; i < identity_len; i++) {
1195 if (identity[i] == '@') {
1200 if (identity_len + 1 > sizeof(entry->imsi)) {
1201 wpa_hexdump_ascii(MSG_DEBUG, "EAP-SIM DB: unexpected identity",
1202 identity, identity_len);
1203 return EAP_SIM_DB_FAILURE;
1205 wpa_hexdump_ascii(MSG_DEBUG, "EAP-SIM DB: Get AKA auth for IMSI",
1206 identity, identity_len);
1208 entry = eap_sim_db_get_pending(data, identity, identity_len, 1);
1210 if (entry->state == FAILURE) {
1212 wpa_printf(MSG_DEBUG, "EAP-SIM DB: Failure");
1213 return EAP_SIM_DB_FAILURE;
1216 if (entry->state == PENDING) {
1217 eap_sim_db_add_pending(data, entry);
1218 wpa_printf(MSG_DEBUG, "EAP-SIM DB: Pending");
1219 return EAP_SIM_DB_PENDING;
1222 wpa_printf(MSG_DEBUG, "EAP-SIM DB: Returning successfully "
1223 "received authentication data");
1224 os_memcpy(_rand, entry->u.aka.rand, EAP_AKA_RAND_LEN);
1225 os_memcpy(autn, entry->u.aka.autn, EAP_AKA_AUTN_LEN);
1226 os_memcpy(ik, entry->u.aka.ik, EAP_AKA_IK_LEN);
1227 os_memcpy(ck, entry->u.aka.ck, EAP_AKA_CK_LEN);
1228 os_memcpy(res, entry->u.aka.res, EAP_AKA_RES_MAX_LEN);
1229 *res_len = entry->u.aka.res_len;
1234 if (data->sock < 0) {
1235 if (eap_sim_db_open_socket(data) < 0)
1236 return EAP_SIM_DB_FAILURE;
1239 len = os_snprintf(msg, sizeof(msg), "AKA-REQ-AUTH ");
1240 if (len < 0 || len + identity_len >= sizeof(msg))
1241 return EAP_SIM_DB_FAILURE;
1242 os_memcpy(msg + len, identity, identity_len);
1243 len += identity_len;
1245 wpa_hexdump(MSG_DEBUG, "EAP-SIM DB: requesting AKA authentication "
1246 "data for IMSI", identity, identity_len);
1247 if (eap_sim_db_send(data, msg, len) < 0)
1248 return EAP_SIM_DB_FAILURE;
1250 entry = os_zalloc(sizeof(*entry));
1252 return EAP_SIM_DB_FAILURE;
1254 os_get_time(&entry->timestamp);
1256 os_memcpy(entry->imsi, identity, identity_len);
1257 entry->imsi_len = identity_len;
1258 entry->cb_session_ctx = cb_session_ctx;
1259 entry->state = PENDING;
1260 eap_sim_db_add_pending(data, entry);
1261 eap_sim_db_expire_pending(data);
1263 return EAP_SIM_DB_PENDING;
1268 * eap_sim_db_resynchronize - Resynchronize AKA AUTN
1269 * @priv: Private data pointer from eap_sim_db_init()
1270 * @identity: User name identity
1271 * @identity_len: Length of identity in bytes
1272 * @auts: AUTS value from the peer
1273 * @_rand: RAND value used in the rejected message
1274 * Returns: 0 on success, -1 on failure
1276 * This function is called when the peer reports synchronization failure in the
1277 * AUTN value by sending AUTS. The AUTS and RAND values should be sent to
1278 * HLR/AuC to allow it to resynchronize with the peer. After this,
1279 * eap_sim_db_get_aka_auth() will be called again to to fetch updated
1280 * RAND/AUTN values for the next challenge.
1282 int eap_sim_db_resynchronize(void *priv, const u8 *identity,
1283 size_t identity_len, const u8 *auts,
1286 struct eap_sim_db_data *data = priv;
1289 if (identity_len < 2 || identity == NULL ||
1290 identity[0] != EAP_AKA_PERMANENT_PREFIX) {
1291 wpa_hexdump_ascii(MSG_DEBUG, "EAP-SIM DB: unexpected identity",
1292 identity, identity_len);
1297 for (i = 0; i < identity_len; i++) {
1298 if (identity[i] == '@') {
1303 if (identity_len > 20) {
1304 wpa_hexdump_ascii(MSG_DEBUG, "EAP-SIM DB: unexpected identity",
1305 identity, identity_len);
1309 if (data->sock >= 0) {
1313 len = os_snprintf(msg, sizeof(msg), "AKA-AUTS ");
1314 if (len < 0 || len + identity_len >= sizeof(msg))
1316 os_memcpy(msg + len, identity, identity_len);
1317 len += identity_len;
1319 ret = os_snprintf(msg + len, sizeof(msg) - len, " ");
1320 if (ret < 0 || (size_t) ret >= sizeof(msg) - len)
1323 len += wpa_snprintf_hex(msg + len, sizeof(msg) - len,
1324 auts, EAP_AKA_AUTS_LEN);
1325 ret = os_snprintf(msg + len, sizeof(msg) - len, " ");
1326 if (ret < 0 || (size_t) ret >= sizeof(msg) - len)
1329 len += wpa_snprintf_hex(msg + len, sizeof(msg) - len,
1330 _rand, EAP_AKA_RAND_LEN);
1331 wpa_hexdump(MSG_DEBUG, "EAP-SIM DB: reporting AKA AUTS for "
1332 "IMSI", identity, identity_len);
1333 if (eap_sim_db_send(data, msg, len) < 0)