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 "eap_common/eap_sim_common.h"
29 #include "eap_server/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 os_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
, os_strlen(imsi
), 0);
123 wpa_printf(MSG_DEBUG
, "EAP-SIM DB: No pending entry for the "
124 "received message found");
129 if (os_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
= os_strchr(start
, ' ');
144 pos
= os_strchr(start
, ':');
148 if (hexstr2bin(start
, entry
->u
.sim
.kc
[num_chal
],
153 pos
= os_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
, os_strlen(imsi
), 1);
201 wpa_printf(MSG_DEBUG
, "EAP-SIM DB: No pending entry for the "
202 "received message found");
207 if (os_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
= os_strchr(start
, ' ');
220 if (hexstr2bin(start
, entry
->u
.aka
.rand
, EAP_AKA_RAND_LEN
))
224 end
= os_strchr(start
, ' ');
228 if (hexstr2bin(start
, entry
->u
.aka
.autn
, EAP_AKA_AUTN_LEN
))
232 end
= os_strchr(start
, ' ');
236 if (hexstr2bin(start
, entry
->u
.aka
.ik
, EAP_AKA_IK_LEN
))
240 end
= os_strchr(start
, ' ');
244 if (hexstr2bin(start
, entry
->u
.aka
.ck
, EAP_AKA_CK_LEN
))
248 end
= os_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
= os_strchr(cmd
, ' ');
309 pos
= os_strchr(imsi
, ' ');
313 wpa_printf(MSG_DEBUG
, "EAP-SIM DB: External response=%s for IMSI %s",
316 if (os_strcmp(cmd
, "SIM-RESP-AUTH") == 0)
317 eap_sim_db_sim_resp_auth(data
, imsi
, pos
+ 1);
318 else if (os_strcmp(cmd
, "AKA-RESP-AUTH") == 0)
319 eap_sim_db_aka_resp_auth(data
, imsi
, pos
+ 1);
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 (os_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 os_memset(&addr
, 0, sizeof(addr
));
345 addr
.sun_family
= AF_UNIX
;
346 os_snprintf(addr
.sun_path
, sizeof(addr
.sun_path
),
347 "/tmp/eap_sim_db_%d-%d", getpid(), counter
++);
348 data
->local_sock
= os_strdup(addr
.sun_path
);
349 if (bind(data
->sock
, (struct sockaddr
*) &addr
, sizeof(addr
)) < 0) {
350 perror("bind(eap_sim_db)");
356 os_memset(&addr
, 0, sizeof(addr
));
357 addr
.sun_family
= AF_UNIX
;
358 os_strlcpy(addr
.sun_path
, data
->fname
+ 5, sizeof(addr
.sun_path
));
359 if (connect(data
->sock
, (struct sockaddr
*) &addr
, sizeof(addr
)) < 0) {
360 perror("connect(eap_sim_db)");
361 wpa_hexdump_ascii(MSG_INFO
, "HLR/AuC GW socket",
362 (u8
*) addr
.sun_path
,
363 os_strlen(addr
.sun_path
));
369 eloop_register_read_sock(data
->sock
, eap_sim_db_receive
, data
, NULL
);
375 static void eap_sim_db_close_socket(struct eap_sim_db_data
*data
)
377 if (data
->sock
>= 0) {
378 eloop_unregister_read_sock(data
->sock
);
382 if (data
->local_sock
) {
383 unlink(data
->local_sock
);
384 os_free(data
->local_sock
);
385 data
->local_sock
= NULL
;
391 * eap_sim_db_init - Initialize EAP-SIM DB / authentication gateway interface
392 * @config: Configuration data (e.g., file name)
393 * @get_complete_cb: Callback function for reporting availability of triplets
394 * @ctx: Context pointer for get_complete_cb
395 * Returns: Pointer to a private data structure or %NULL on failure
397 void * eap_sim_db_init(const char *config
,
398 void (*get_complete_cb
)(void *ctx
, void *session_ctx
),
401 struct eap_sim_db_data
*data
;
403 data
= os_zalloc(sizeof(*data
));
408 data
->get_complete_cb
= get_complete_cb
;
410 data
->fname
= os_strdup(config
);
411 if (data
->fname
== NULL
)
414 if (os_strncmp(data
->fname
, "unix:", 5) == 0) {
415 if (eap_sim_db_open_socket(data
))
422 eap_sim_db_close_socket(data
);
423 os_free(data
->fname
);
429 static void eap_sim_db_free_pseudonym(struct eap_sim_pseudonym
*p
)
431 os_free(p
->identity
);
432 os_free(p
->pseudonym
);
437 static void eap_sim_db_free_reauth(struct eap_sim_reauth
*r
)
439 os_free(r
->identity
);
440 os_free(r
->reauth_id
);
446 * eap_sim_db_deinit - Deinitialize EAP-SIM DB/authentication gw interface
447 * @priv: Private data pointer from eap_sim_db_init()
449 void eap_sim_db_deinit(void *priv
)
451 struct eap_sim_db_data
*data
= priv
;
452 struct eap_sim_pseudonym
*p
, *prev
;
453 struct eap_sim_reauth
*r
, *prevr
;
454 struct eap_sim_db_pending
*pending
, *prev_pending
;
456 eap_sim_db_close_socket(data
);
457 os_free(data
->fname
);
459 p
= data
->pseudonyms
;
463 eap_sim_db_free_pseudonym(prev
);
470 eap_sim_db_free_reauth(prevr
);
473 pending
= data
->pending
;
475 prev_pending
= pending
;
476 pending
= pending
->next
;
477 os_free(prev_pending
);
484 static int eap_sim_db_send(struct eap_sim_db_data
*data
, const char *msg
,
489 if (send(data
->sock
, msg
, len
, 0) < 0) {
491 perror("send[EAP-SIM DB UNIX]");
494 if (_errno
== ENOTCONN
|| _errno
== EDESTADDRREQ
|| _errno
== EINVAL
||
495 _errno
== ECONNREFUSED
) {
496 /* Try to reconnect */
497 eap_sim_db_close_socket(data
);
498 if (eap_sim_db_open_socket(data
) < 0)
500 wpa_printf(MSG_DEBUG
, "EAP-SIM DB: Reconnected to the "
502 if (send(data
->sock
, msg
, len
, 0) < 0) {
503 perror("send[EAP-SIM DB UNIX]");
512 static void eap_sim_db_expire_pending(struct eap_sim_db_data
*data
)
514 /* TODO: add limit for maximum length for pending list; remove latest
515 * (i.e., last) entry from the list if the limit is reached; could also
516 * use timeout to expire pending entries */
521 * eap_sim_db_get_gsm_triplets - Get GSM triplets
522 * @priv: Private data pointer from eap_sim_db_init()
523 * @identity: User name identity
524 * @identity_len: Length of identity in bytes
525 * @max_chal: Maximum number of triplets
526 * @_rand: Buffer for RAND values
527 * @kc: Buffer for Kc values
528 * @sres: Buffer for SRES values
529 * @cb_session_ctx: Session callback context for get_complete_cb()
530 * Returns: Number of triplets received (has to be less than or equal to
531 * max_chal), -1 (EAP_SIM_DB_FAILURE) on error (e.g., user not found), or
532 * -2 (EAP_SIM_DB_PENDING) if results are not yet available. In this case, the
533 * callback function registered with eap_sim_db_init() will be called once the
534 * results become available.
536 * In most cases, the user name is '1' | IMSI, i.e., 1 followed by the IMSI in
539 * When using an external server for GSM triplets, this function can always
540 * start a request and return EAP_SIM_DB_PENDING immediately if authentication
541 * triplets are not available. Once the triplets are received, callback
542 * function registered with eap_sim_db_init() is called to notify EAP state
543 * machine to reprocess the message. This eap_sim_db_get_gsm_triplets()
544 * function will then be called again and the newly received triplets will then
545 * be given to the caller.
547 int eap_sim_db_get_gsm_triplets(void *priv
, const u8
*identity
,
548 size_t identity_len
, int max_chal
,
549 u8
*_rand
, u8
*kc
, u8
*sres
,
550 void *cb_session_ctx
)
552 struct eap_sim_db_data
*data
= priv
;
553 struct eap_sim_db_pending
*entry
;
558 if (identity_len
< 2 || identity
[0] != EAP_SIM_PERMANENT_PREFIX
) {
559 wpa_hexdump_ascii(MSG_DEBUG
, "EAP-SIM DB: unexpected identity",
560 identity
, identity_len
);
561 return EAP_SIM_DB_FAILURE
;
565 for (i
= 0; i
< identity_len
; i
++) {
566 if (identity
[i
] == '@') {
571 if (identity_len
+ 1 > sizeof(entry
->imsi
)) {
572 wpa_hexdump_ascii(MSG_DEBUG
, "EAP-SIM DB: unexpected identity",
573 identity
, identity_len
);
574 return EAP_SIM_DB_FAILURE
;
576 wpa_hexdump_ascii(MSG_DEBUG
, "EAP-SIM DB: Get GSM triplets for IMSI",
577 identity
, identity_len
);
579 entry
= eap_sim_db_get_pending(data
, identity
, identity_len
, 0);
582 if (entry
->state
== FAILURE
) {
583 wpa_printf(MSG_DEBUG
, "EAP-SIM DB: Pending entry -> "
586 return EAP_SIM_DB_FAILURE
;
589 if (entry
->state
== PENDING
) {
590 wpa_printf(MSG_DEBUG
, "EAP-SIM DB: Pending entry -> "
592 eap_sim_db_add_pending(data
, entry
);
593 return EAP_SIM_DB_PENDING
;
596 wpa_printf(MSG_DEBUG
, "EAP-SIM DB: Pending entry -> "
597 "%d challenges", entry
->u
.sim
.num_chal
);
598 num_chal
= entry
->u
.sim
.num_chal
;
599 if (num_chal
> max_chal
)
601 os_memcpy(_rand
, entry
->u
.sim
.rand
, num_chal
* GSM_RAND_LEN
);
602 os_memcpy(sres
, entry
->u
.sim
.sres
,
603 num_chal
* EAP_SIM_SRES_LEN
);
604 os_memcpy(kc
, entry
->u
.sim
.kc
, num_chal
* EAP_SIM_KC_LEN
);
609 if (data
->sock
< 0) {
610 if (eap_sim_db_open_socket(data
) < 0)
611 return EAP_SIM_DB_FAILURE
;
614 len
= os_snprintf(msg
, sizeof(msg
), "SIM-REQ-AUTH ");
615 if (len
< 0 || len
+ identity_len
>= sizeof(msg
))
616 return EAP_SIM_DB_FAILURE
;
617 os_memcpy(msg
+ len
, identity
, identity_len
);
619 ret
= os_snprintf(msg
+ len
, sizeof(msg
) - len
, " %d", max_chal
);
620 if (ret
< 0 || (size_t) ret
>= sizeof(msg
) - len
)
621 return EAP_SIM_DB_FAILURE
;
624 wpa_hexdump(MSG_DEBUG
, "EAP-SIM DB: requesting SIM authentication "
625 "data for IMSI", identity
, identity_len
);
626 if (eap_sim_db_send(data
, msg
, len
) < 0)
627 return EAP_SIM_DB_FAILURE
;
629 entry
= os_zalloc(sizeof(*entry
));
631 return EAP_SIM_DB_FAILURE
;
633 os_get_time(&entry
->timestamp
);
634 os_memcpy(entry
->imsi
, identity
, identity_len
);
635 entry
->imsi_len
= identity_len
;
636 entry
->cb_session_ctx
= cb_session_ctx
;
637 entry
->state
= PENDING
;
638 eap_sim_db_add_pending(data
, entry
);
639 eap_sim_db_expire_pending(data
);
641 return EAP_SIM_DB_PENDING
;
645 static struct eap_sim_pseudonym
*
646 eap_sim_db_get_pseudonym(struct eap_sim_db_data
*data
, const u8
*identity
,
651 struct eap_sim_pseudonym
*p
;
653 if (identity_len
== 0 ||
654 (identity
[0] != EAP_SIM_PSEUDONYM_PREFIX
&&
655 identity
[0] != EAP_AKA_PSEUDONYM_PREFIX
))
658 /* Remove possible realm from identity */
660 while (len
< identity_len
) {
661 if (identity
[len
] == '@')
666 pseudonym
= os_malloc(len
+ 1);
667 if (pseudonym
== NULL
)
669 os_memcpy(pseudonym
, identity
, len
);
670 pseudonym
[len
] = '\0';
672 p
= data
->pseudonyms
;
674 if (os_strcmp(p
->pseudonym
, pseudonym
) == 0)
685 static struct eap_sim_pseudonym
*
686 eap_sim_db_get_pseudonym_id(struct eap_sim_db_data
*data
, const u8
*identity
,
689 struct eap_sim_pseudonym
*p
;
691 if (identity_len
== 0 ||
692 (identity
[0] != EAP_SIM_PERMANENT_PREFIX
&&
693 identity
[0] != EAP_AKA_PERMANENT_PREFIX
))
696 p
= data
->pseudonyms
;
698 if (identity_len
== p
->identity_len
&&
699 os_memcmp(p
->identity
, identity
, identity_len
) == 0)
708 static struct eap_sim_reauth
*
709 eap_sim_db_get_reauth(struct eap_sim_db_data
*data
, const u8
*identity
,
714 struct eap_sim_reauth
*r
;
716 if (identity_len
== 0 ||
717 (identity
[0] != EAP_SIM_REAUTH_ID_PREFIX
&&
718 identity
[0] != EAP_AKA_REAUTH_ID_PREFIX
))
721 /* Remove possible realm from identity */
723 while (len
< identity_len
) {
724 if (identity
[len
] == '@')
729 reauth_id
= os_malloc(len
+ 1);
730 if (reauth_id
== NULL
)
732 os_memcpy(reauth_id
, identity
, len
);
733 reauth_id
[len
] = '\0';
737 if (os_strcmp(r
->reauth_id
, reauth_id
) == 0)
748 static struct eap_sim_reauth
*
749 eap_sim_db_get_reauth_id(struct eap_sim_db_data
*data
, const u8
*identity
,
752 struct eap_sim_pseudonym
*p
;
753 struct eap_sim_reauth
*r
;
755 if (identity_len
== 0)
758 p
= eap_sim_db_get_pseudonym(data
, identity
, identity_len
);
760 p
= eap_sim_db_get_pseudonym_id(data
, identity
, identity_len
);
762 identity
= p
->identity
;
763 identity_len
= p
->identity_len
;
768 if (identity_len
== r
->identity_len
&&
769 os_memcmp(r
->identity
, identity
, identity_len
) == 0)
779 * eap_sim_db_identity_known - Verify whether the given identity is known
780 * @priv: Private data pointer from eap_sim_db_init()
781 * @identity: User name identity
782 * @identity_len: Length of identity in bytes
783 * Returns: 0 if the user is found or -1 on failure
785 * In most cases, the user name is ['0','1'] | IMSI, i.e., 1 followed by the
786 * IMSI in ASCII format, ['2','3'] | pseudonym, or ['4','5'] | reauth_id.
788 int eap_sim_db_identity_known(void *priv
, const u8
*identity
,
791 struct eap_sim_db_data
*data
= priv
;
793 if (identity
== NULL
|| identity_len
< 2)
796 if (identity
[0] == EAP_SIM_PSEUDONYM_PREFIX
||
797 identity
[0] == EAP_AKA_PSEUDONYM_PREFIX
) {
798 struct eap_sim_pseudonym
*p
=
799 eap_sim_db_get_pseudonym(data
, identity
, identity_len
);
803 if (identity
[0] == EAP_SIM_REAUTH_ID_PREFIX
||
804 identity
[0] == EAP_AKA_REAUTH_ID_PREFIX
) {
805 struct eap_sim_reauth
*r
=
806 eap_sim_db_get_reauth(data
, identity
, identity_len
);
810 if (identity
[0] != EAP_SIM_PERMANENT_PREFIX
&&
811 identity
[0] != EAP_AKA_PERMANENT_PREFIX
) {
812 /* Unknown identity prefix */
816 /* TODO: Should consider asking HLR/AuC gateway whether this permanent
817 * identity is known. If it is, EAP-SIM/AKA can skip identity request.
818 * In case of EAP-AKA, this would reduce number of needed round-trips.
819 * Ideally, this would be done with one wait, i.e., just request
820 * authentication data and store it for the next use. This would then
821 * need to use similar pending-request functionality as the normal
822 * request for authentication data at later phase.
828 static char * eap_sim_db_get_next(struct eap_sim_db_data
*data
, char prefix
)
830 char *id
, *pos
, *end
;
833 if (os_get_random(buf
, sizeof(buf
)))
835 id
= os_malloc(sizeof(buf
) * 2 + 2);
840 end
= id
+ sizeof(buf
) * 2 + 2;
842 pos
+= wpa_snprintf_hex(pos
, end
- pos
, buf
, sizeof(buf
));
849 * eap_sim_db_get_next_pseudonym - EAP-SIM DB: Get next pseudonym
850 * @priv: Private data pointer from eap_sim_db_init()
851 * @aka: Using EAP-AKA instead of EAP-SIM
852 * Returns: Next pseudonym (allocated string) or %NULL on failure
854 * This function is used to generate a pseudonym for EAP-SIM. The returned
855 * pseudonym is not added to database at this point; it will need to be added
856 * with eap_sim_db_add_pseudonym() once the authentication has been completed
857 * successfully. Caller is responsible for freeing the returned buffer.
859 char * eap_sim_db_get_next_pseudonym(void *priv
, int aka
)
861 struct eap_sim_db_data
*data
= priv
;
862 return eap_sim_db_get_next(data
, aka
? EAP_AKA_PSEUDONYM_PREFIX
:
863 EAP_SIM_PSEUDONYM_PREFIX
);
868 * eap_sim_db_get_next_reauth_id - EAP-SIM DB: Get next reauth_id
869 * @priv: Private data pointer from eap_sim_db_init()
870 * @aka: Using EAP-AKA instead of EAP-SIM
871 * Returns: Next reauth_id (allocated string) or %NULL on failure
873 * This function is used to generate a fast re-authentication identity for
874 * EAP-SIM. The returned reauth_id is not added to database at this point; it
875 * will need to be added with eap_sim_db_add_reauth() once the authentication
876 * has been completed successfully. Caller is responsible for freeing the
879 char * eap_sim_db_get_next_reauth_id(void *priv
, int aka
)
881 struct eap_sim_db_data
*data
= priv
;
882 return eap_sim_db_get_next(data
, aka
? EAP_AKA_REAUTH_ID_PREFIX
:
883 EAP_SIM_REAUTH_ID_PREFIX
);
888 * eap_sim_db_add_pseudonym - EAP-SIM DB: Add new pseudonym
889 * @priv: Private data pointer from eap_sim_db_init()
890 * @identity: Identity of the user (may be permanent identity or pseudonym)
891 * @identity_len: Length of identity
892 * @pseudonym: Pseudonym for this user. This needs to be an allocated buffer,
893 * e.g., return value from eap_sim_db_get_next_pseudonym(). Caller must not
895 * Returns: 0 on success, -1 on failure
897 * This function adds a new pseudonym for EAP-SIM user. EAP-SIM DB is
898 * responsible of freeing pseudonym buffer once it is not needed anymore.
900 int eap_sim_db_add_pseudonym(void *priv
, const u8
*identity
,
901 size_t identity_len
, char *pseudonym
)
903 struct eap_sim_db_data
*data
= priv
;
904 struct eap_sim_pseudonym
*p
;
905 wpa_hexdump_ascii(MSG_DEBUG
, "EAP-SIM DB: Add pseudonym for identity",
906 identity
, identity_len
);
907 wpa_printf(MSG_DEBUG
, "EAP-SIM DB: Pseudonym: %s", pseudonym
);
909 /* TODO: could store last two pseudonyms */
910 p
= eap_sim_db_get_pseudonym(data
, identity
, identity_len
);
912 p
= eap_sim_db_get_pseudonym_id(data
, identity
, identity_len
);
915 wpa_printf(MSG_DEBUG
, "EAP-SIM DB: Replacing previous "
916 "pseudonym: %s", p
->pseudonym
);
917 os_free(p
->pseudonym
);
918 p
->pseudonym
= pseudonym
;
922 p
= os_zalloc(sizeof(*p
));
928 p
->next
= data
->pseudonyms
;
929 p
->identity
= os_malloc(identity_len
);
930 if (p
->identity
== NULL
) {
935 os_memcpy(p
->identity
, identity
, identity_len
);
936 p
->identity_len
= identity_len
;
937 p
->pseudonym
= pseudonym
;
938 data
->pseudonyms
= p
;
940 wpa_printf(MSG_DEBUG
, "EAP-SIM DB: Added new pseudonym entry");
945 static struct eap_sim_reauth
*
946 eap_sim_db_add_reauth_data(struct eap_sim_db_data
*data
, const u8
*identity
,
947 size_t identity_len
, char *reauth_id
, u16 counter
)
949 struct eap_sim_reauth
*r
;
951 wpa_hexdump_ascii(MSG_DEBUG
, "EAP-SIM DB: Add reauth_id for identity",
952 identity
, identity_len
);
953 wpa_printf(MSG_DEBUG
, "EAP-SIM DB: reauth_id: %s", reauth_id
);
955 r
= eap_sim_db_get_reauth(data
, identity
, identity_len
);
957 r
= eap_sim_db_get_reauth_id(data
, identity
, identity_len
);
960 wpa_printf(MSG_DEBUG
, "EAP-SIM DB: Replacing previous "
961 "reauth_id: %s", r
->reauth_id
);
962 os_free(r
->reauth_id
);
963 r
->reauth_id
= reauth_id
;
965 r
= os_zalloc(sizeof(*r
));
971 r
->next
= data
->reauths
;
972 r
->identity
= os_malloc(identity_len
);
973 if (r
->identity
== NULL
) {
978 os_memcpy(r
->identity
, identity
, identity_len
);
979 r
->identity_len
= identity_len
;
980 r
->reauth_id
= reauth_id
;
982 wpa_printf(MSG_DEBUG
, "EAP-SIM DB: Added new reauth entry");
985 r
->counter
= counter
;
992 * eap_sim_db_add_reauth - EAP-SIM DB: Add new re-authentication entry
993 * @priv: Private data pointer from eap_sim_db_init()
994 * @identity: Identity of the user (may be permanent identity or pseudonym)
995 * @identity_len: Length of identity
996 * @reauth_id: reauth_id for this user. This needs to be an allocated buffer,
997 * e.g., return value from eap_sim_db_get_next_reauth_id(). Caller must not
999 * @counter: AT_COUNTER value for fast re-authentication
1000 * @mk: 16-byte MK from the previous full authentication or %NULL
1001 * Returns: 0 on success, -1 on failure
1003 * This function adds a new re-authentication entry for an EAP-SIM user.
1004 * EAP-SIM DB is responsible of freeing reauth_id buffer once it is not needed
1007 int eap_sim_db_add_reauth(void *priv
, const u8
*identity
,
1008 size_t identity_len
, char *reauth_id
, u16 counter
,
1011 struct eap_sim_db_data
*data
= priv
;
1012 struct eap_sim_reauth
*r
;
1014 r
= eap_sim_db_add_reauth_data(data
, identity
, identity_len
, reauth_id
,
1019 os_memcpy(r
->mk
, mk
, EAP_SIM_MK_LEN
);
1026 #ifdef EAP_SERVER_AKA_PRIME
1028 * eap_sim_db_add_reauth_prime - EAP-AKA' DB: Add new re-authentication entry
1029 * @priv: Private data pointer from eap_sim_db_init()
1030 * @identity: Identity of the user (may be permanent identity or pseudonym)
1031 * @identity_len: Length of identity
1032 * @reauth_id: reauth_id for this user. This needs to be an allocated buffer,
1033 * e.g., return value from eap_sim_db_get_next_reauth_id(). Caller must not
1035 * @counter: AT_COUNTER value for fast re-authentication
1036 * @k_encr: K_encr from the previous full authentication
1037 * @k_aut: K_aut from the previous full authentication
1038 * @k_re: 32-byte K_re from the previous full authentication
1039 * Returns: 0 on success, -1 on failure
1041 * This function adds a new re-authentication entry for an EAP-AKA' user.
1042 * EAP-SIM DB is responsible of freeing reauth_id buffer once it is not needed
1045 int eap_sim_db_add_reauth_prime(void *priv
, const u8
*identity
,
1046 size_t identity_len
, char *reauth_id
,
1047 u16 counter
, const u8
*k_encr
, const u8
*k_aut
,
1050 struct eap_sim_db_data
*data
= priv
;
1051 struct eap_sim_reauth
*r
;
1053 r
= eap_sim_db_add_reauth_data(data
, identity
, identity_len
, reauth_id
,
1059 os_memcpy(r
->k_encr
, k_encr
, EAP_SIM_K_ENCR_LEN
);
1060 os_memcpy(r
->k_aut
, k_aut
, EAP_AKA_PRIME_K_AUT_LEN
);
1061 os_memcpy(r
->k_re
, k_re
, EAP_AKA_PRIME_K_RE_LEN
);
1065 #endif /* EAP_SERVER_AKA_PRIME */
1069 * eap_sim_db_get_permanent - EAP-SIM DB: Get permanent identity
1070 * @priv: Private data pointer from eap_sim_db_init()
1071 * @identity: Identity of the user (may be permanent identity or pseudonym)
1072 * @identity_len: Length of identity
1073 * @len: Buffer for length of the returned permanent identity
1074 * Returns: Pointer to the permanent identity, or %NULL if not found
1076 const u8
* eap_sim_db_get_permanent(void *priv
, const u8
*identity
,
1077 size_t identity_len
, size_t *len
)
1079 struct eap_sim_db_data
*data
= priv
;
1080 struct eap_sim_pseudonym
*p
;
1082 if (identity
== NULL
)
1085 p
= eap_sim_db_get_pseudonym(data
, identity
, identity_len
);
1087 p
= eap_sim_db_get_pseudonym_id(data
, identity
, identity_len
);
1091 *len
= p
->identity_len
;
1097 * eap_sim_db_get_reauth_entry - EAP-SIM DB: Get re-authentication entry
1098 * @priv: Private data pointer from eap_sim_db_init()
1099 * @identity: Identity of the user (may be permanent identity, pseudonym, or
1101 * @identity_len: Length of identity
1102 * Returns: Pointer to the re-auth entry, or %NULL if not found
1104 struct eap_sim_reauth
*
1105 eap_sim_db_get_reauth_entry(void *priv
, const u8
*identity
,
1106 size_t identity_len
)
1108 struct eap_sim_db_data
*data
= priv
;
1109 struct eap_sim_reauth
*r
;
1111 if (identity
== NULL
)
1113 r
= eap_sim_db_get_reauth(data
, identity
, identity_len
);
1115 r
= eap_sim_db_get_reauth_id(data
, identity
, identity_len
);
1121 * eap_sim_db_remove_reauth - EAP-SIM DB: Remove re-authentication entry
1122 * @priv: Private data pointer from eap_sim_db_init()
1123 * @reauth: Pointer to re-authentication entry from
1124 * eap_sim_db_get_reauth_entry()
1126 void eap_sim_db_remove_reauth(void *priv
, struct eap_sim_reauth
*reauth
)
1128 struct eap_sim_db_data
*data
= priv
;
1129 struct eap_sim_reauth
*r
, *prev
= NULL
;
1134 prev
->next
= r
->next
;
1136 data
->reauths
= r
->next
;
1137 eap_sim_db_free_reauth(r
);
1147 * eap_sim_db_get_aka_auth - Get AKA authentication values
1148 * @priv: Private data pointer from eap_sim_db_init()
1149 * @identity: User name identity
1150 * @identity_len: Length of identity in bytes
1151 * @_rand: Buffer for RAND value
1152 * @autn: Buffer for AUTN value
1153 * @ik: Buffer for IK value
1154 * @ck: Buffer for CK value
1155 * @res: Buffer for RES value
1156 * @res_len: Buffer for RES length
1157 * @cb_session_ctx: Session callback context for get_complete_cb()
1158 * Returns: 0 on success, -1 (EAP_SIM_DB_FAILURE) on error (e.g., user not
1159 * found), or -2 (EAP_SIM_DB_PENDING) if results are not yet available. In this
1160 * case, the callback function registered with eap_sim_db_init() will be
1161 * called once the results become available.
1163 * In most cases, the user name is '0' | IMSI, i.e., 0 followed by the IMSI in
1166 * When using an external server for AKA authentication, this function can
1167 * always start a request and return EAP_SIM_DB_PENDING immediately if
1168 * authentication triplets are not available. Once the authentication data are
1169 * received, callback function registered with eap_sim_db_init() is called to
1170 * notify EAP state machine to reprocess the message. This
1171 * eap_sim_db_get_aka_auth() function will then be called again and the newly
1172 * received triplets will then be given to the caller.
1174 int eap_sim_db_get_aka_auth(void *priv
, const u8
*identity
,
1175 size_t identity_len
, u8
*_rand
, u8
*autn
, u8
*ik
,
1176 u8
*ck
, u8
*res
, size_t *res_len
,
1177 void *cb_session_ctx
)
1179 struct eap_sim_db_data
*data
= priv
;
1180 struct eap_sim_db_pending
*entry
;
1185 if (identity_len
< 2 || identity
== NULL
||
1186 identity
[0] != EAP_AKA_PERMANENT_PREFIX
) {
1187 wpa_hexdump_ascii(MSG_DEBUG
, "EAP-SIM DB: unexpected identity",
1188 identity
, identity_len
);
1189 return EAP_SIM_DB_FAILURE
;
1193 for (i
= 0; i
< identity_len
; i
++) {
1194 if (identity
[i
] == '@') {
1199 if (identity_len
+ 1 > sizeof(entry
->imsi
)) {
1200 wpa_hexdump_ascii(MSG_DEBUG
, "EAP-SIM DB: unexpected identity",
1201 identity
, identity_len
);
1202 return EAP_SIM_DB_FAILURE
;
1204 wpa_hexdump_ascii(MSG_DEBUG
, "EAP-SIM DB: Get AKA auth for IMSI",
1205 identity
, identity_len
);
1207 entry
= eap_sim_db_get_pending(data
, identity
, identity_len
, 1);
1209 if (entry
->state
== FAILURE
) {
1211 wpa_printf(MSG_DEBUG
, "EAP-SIM DB: Failure");
1212 return EAP_SIM_DB_FAILURE
;
1215 if (entry
->state
== PENDING
) {
1216 eap_sim_db_add_pending(data
, entry
);
1217 wpa_printf(MSG_DEBUG
, "EAP-SIM DB: Pending");
1218 return EAP_SIM_DB_PENDING
;
1221 wpa_printf(MSG_DEBUG
, "EAP-SIM DB: Returning successfully "
1222 "received authentication data");
1223 os_memcpy(_rand
, entry
->u
.aka
.rand
, EAP_AKA_RAND_LEN
);
1224 os_memcpy(autn
, entry
->u
.aka
.autn
, EAP_AKA_AUTN_LEN
);
1225 os_memcpy(ik
, entry
->u
.aka
.ik
, EAP_AKA_IK_LEN
);
1226 os_memcpy(ck
, entry
->u
.aka
.ck
, EAP_AKA_CK_LEN
);
1227 os_memcpy(res
, entry
->u
.aka
.res
, EAP_AKA_RES_MAX_LEN
);
1228 *res_len
= entry
->u
.aka
.res_len
;
1233 if (data
->sock
< 0) {
1234 if (eap_sim_db_open_socket(data
) < 0)
1235 return EAP_SIM_DB_FAILURE
;
1238 len
= os_snprintf(msg
, sizeof(msg
), "AKA-REQ-AUTH ");
1239 if (len
< 0 || len
+ identity_len
>= sizeof(msg
))
1240 return EAP_SIM_DB_FAILURE
;
1241 os_memcpy(msg
+ len
, identity
, identity_len
);
1242 len
+= identity_len
;
1244 wpa_hexdump(MSG_DEBUG
, "EAP-SIM DB: requesting AKA authentication "
1245 "data for IMSI", identity
, identity_len
);
1246 if (eap_sim_db_send(data
, msg
, len
) < 0)
1247 return EAP_SIM_DB_FAILURE
;
1249 entry
= os_zalloc(sizeof(*entry
));
1251 return EAP_SIM_DB_FAILURE
;
1253 os_get_time(&entry
->timestamp
);
1255 os_memcpy(entry
->imsi
, identity
, identity_len
);
1256 entry
->imsi_len
= identity_len
;
1257 entry
->cb_session_ctx
= cb_session_ctx
;
1258 entry
->state
= PENDING
;
1259 eap_sim_db_add_pending(data
, entry
);
1260 eap_sim_db_expire_pending(data
);
1262 return EAP_SIM_DB_PENDING
;
1267 * eap_sim_db_resynchronize - Resynchronize AKA AUTN
1268 * @priv: Private data pointer from eap_sim_db_init()
1269 * @identity: User name identity
1270 * @identity_len: Length of identity in bytes
1271 * @auts: AUTS value from the peer
1272 * @_rand: RAND value used in the rejected message
1273 * Returns: 0 on success, -1 on failure
1275 * This function is called when the peer reports synchronization failure in the
1276 * AUTN value by sending AUTS. The AUTS and RAND values should be sent to
1277 * HLR/AuC to allow it to resynchronize with the peer. After this,
1278 * eap_sim_db_get_aka_auth() will be called again to to fetch updated
1279 * RAND/AUTN values for the next challenge.
1281 int eap_sim_db_resynchronize(void *priv
, const u8
*identity
,
1282 size_t identity_len
, const u8
*auts
,
1285 struct eap_sim_db_data
*data
= priv
;
1288 if (identity_len
< 2 || identity
== NULL
||
1289 identity
[0] != EAP_AKA_PERMANENT_PREFIX
) {
1290 wpa_hexdump_ascii(MSG_DEBUG
, "EAP-SIM DB: unexpected identity",
1291 identity
, identity_len
);
1296 for (i
= 0; i
< identity_len
; i
++) {
1297 if (identity
[i
] == '@') {
1302 if (identity_len
> 20) {
1303 wpa_hexdump_ascii(MSG_DEBUG
, "EAP-SIM DB: unexpected identity",
1304 identity
, identity_len
);
1308 if (data
->sock
>= 0) {
1312 len
= os_snprintf(msg
, sizeof(msg
), "AKA-AUTS ");
1313 if (len
< 0 || len
+ identity_len
>= sizeof(msg
))
1315 os_memcpy(msg
+ len
, identity
, identity_len
);
1316 len
+= identity_len
;
1318 ret
= os_snprintf(msg
+ len
, sizeof(msg
) - len
, " ");
1319 if (ret
< 0 || (size_t) ret
>= sizeof(msg
) - len
)
1322 len
+= wpa_snprintf_hex(msg
+ len
, sizeof(msg
) - len
,
1323 auts
, EAP_AKA_AUTS_LEN
);
1324 ret
= os_snprintf(msg
+ len
, sizeof(msg
) - len
, " ");
1325 if (ret
< 0 || (size_t) ret
>= sizeof(msg
) - len
)
1328 len
+= wpa_snprintf_hex(msg
+ len
, sizeof(msg
) - len
,
1329 _rand
, EAP_AKA_RAND_LEN
);
1330 wpa_hexdump(MSG_DEBUG
, "EAP-SIM DB: reporting AKA AUTS for "
1331 "IMSI", identity
, identity_len
);
1332 if (eap_sim_db_send(data
, msg
, len
) < 0)