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");
946
* eap_sim_db_add_reauth - EAP-SIM DB: Add new re-authentication entry
947
* @priv: Private data pointer from eap_sim_db_init()
948
* @identity: Identity of the user (may be permanent identity or pseudonym)
949
* @identity_len: Length of identity
950
* @reauth_id: reauth_id for this user. This needs to be an allocated buffer,
951
* e.g., return value from eap_sim_db_get_next_reauth_id(). Caller must not
953
* @mk: 16-byte MK from the previous full authentication
954
* Returns: 0 on success, -1 on failure
956
* This function adds a new re-authentication entry for an EAP-SIM user.
957
* EAP-SIM DB is responsible of freeing reauth_id buffer once it is not needed
960
int eap_sim_db_add_reauth(void *priv, const u8 *identity,
961
size_t identity_len, char *reauth_id, u16 counter,
964
struct eap_sim_db_data *data = priv;
965
struct eap_sim_reauth *r;
966
wpa_hexdump_ascii(MSG_DEBUG, "EAP-SIM DB: Add reauth_id for identity",
967
identity, identity_len);
968
wpa_printf(MSG_DEBUG, "EAP-SIM DB: reauth_id: %s", reauth_id);
970
r = eap_sim_db_get_reauth(data, identity, identity_len);
972
r = eap_sim_db_get_reauth_id(data, identity, identity_len);
975
wpa_printf(MSG_DEBUG, "EAP-SIM DB: Replacing previous "
976
"reauth_id: %s", r->reauth_id);
977
os_free(r->reauth_id);
978
r->reauth_id = reauth_id;
980
r = os_zalloc(sizeof(*r));
986
r->next = data->reauths;
987
r->identity = os_malloc(identity_len);
988
if (r->identity == NULL) {
993
os_memcpy(r->identity, identity, identity_len);
994
r->identity_len = identity_len;
995
r->reauth_id = reauth_id;
997
wpa_printf(MSG_DEBUG, "EAP-SIM DB: Added new reauth entry");
1000
r->counter = counter;
1001
os_memcpy(r->mk, mk, EAP_SIM_MK_LEN);
1008
* eap_sim_db_get_permanent - EAP-SIM DB: Get permanent identity
1009
* @priv: Private data pointer from eap_sim_db_init()
1010
* @identity: Identity of the user (may be permanent identity or pseudonym)
1011
* @identity_len: Length of identity
1012
* @len: Buffer for length of the returned permanent identity
1013
* Returns: Pointer to the permanent identity, or %NULL if not found
1015
const u8 * eap_sim_db_get_permanent(void *priv, const u8 *identity,
1016
size_t identity_len, size_t *len)
1018
struct eap_sim_db_data *data = priv;
1019
struct eap_sim_pseudonym *p;
1021
if (identity == NULL)
1024
p = eap_sim_db_get_pseudonym(data, identity, identity_len);
1026
p = eap_sim_db_get_pseudonym_id(data, identity, identity_len);
1030
*len = p->identity_len;
1036
* eap_sim_db_get_reauth_entry - EAP-SIM DB: Get re-authentication entry
1037
* @priv: Private data pointer from eap_sim_db_init()
1038
* @identity: Identity of the user (may be permanent identity, pseudonym, or
1040
* @identity_len: Length of identity
1041
* @len: Buffer for length of the returned permanent identity
1042
* Returns: Pointer to the re-auth entry, or %NULL if not found
1044
struct eap_sim_reauth *
1045
eap_sim_db_get_reauth_entry(void *priv, const u8 *identity,
1046
size_t identity_len)
1048
struct eap_sim_db_data *data = priv;
1049
struct eap_sim_reauth *r;
1051
if (identity == NULL)
1053
r = eap_sim_db_get_reauth(data, identity, identity_len);
1055
r = eap_sim_db_get_reauth_id(data, identity, identity_len);
1061
* eap_sim_db_remove_reauth - EAP-SIM DB: Remove re-authentication entry
1062
* @priv: Private data pointer from eap_sim_db_init()
1063
* @reauth: Pointer to re-authentication entry from
1064
* eap_sim_db_get_reauth_entry()
1066
void eap_sim_db_remove_reauth(void *priv, struct eap_sim_reauth *reauth)
1068
struct eap_sim_db_data *data = priv;
1069
struct eap_sim_reauth *r, *prev = NULL;
1074
prev->next = r->next;
1076
data->reauths = r->next;
1077
eap_sim_db_free_reauth(r);
1087
* eap_sim_db_get_aka_auth - Get AKA authentication values
1088
* @priv: Private data pointer from eap_sim_db_init()
1089
* @identity: User name identity
1090
* @identity_len: Length of identity in bytes
1091
* @_rand: Buffer for RAND value
1092
* @autn: Buffer for AUTN value
1093
* @ik: Buffer for IK value
1094
* @ck: Buffer for CK value
1095
* @res: Buffer for RES value
1096
* @res_len: Buffer for RES length
1097
* @cb_session_ctx: Session callback context for get_complete_cb()
1098
* Returns: 0 on success, -1 (EAP_SIM_DB_FAILURE) on error (e.g., user not
1099
* found), or -2 (EAP_SIM_DB_PENDING) if results are not yet available. In this
1100
* case, the callback function registered with eap_sim_db_init() will be
1101
* called once the results become available.
1103
* In most cases, the user name is '0' | IMSI, i.e., 0 followed by the IMSI in
1106
* When using an external server for AKA authentication, this function can
1107
* always start a request and return EAP_SIM_DB_PENDING immediately if
1108
* authentication triplets are not available. Once the authentication data are
1109
* received, callback function registered with eap_sim_db_init() is called to
1110
* notify EAP state machine to reprocess the message. This
1111
* eap_sim_db_get_aka_auth() function will then be called again and the newly
1112
* received triplets will then be given to the caller.
1114
int eap_sim_db_get_aka_auth(void *priv, const u8 *identity,
1115
size_t identity_len, u8 *_rand, u8 *autn, u8 *ik,
1116
u8 *ck, u8 *res, size_t *res_len,
1117
void *cb_session_ctx)
1119
struct eap_sim_db_data *data = priv;
1120
struct eap_sim_db_pending *entry;
1125
if (identity_len < 2 || identity == NULL ||
1126
identity[0] != EAP_AKA_PERMANENT_PREFIX) {
1127
wpa_hexdump_ascii(MSG_DEBUG, "EAP-SIM DB: unexpected identity",
1128
identity, identity_len);
1129
return EAP_SIM_DB_FAILURE;
1133
for (i = 0; i < identity_len; i++) {
1134
if (identity[i] == '@') {
1139
if (identity_len + 1 > sizeof(entry->imsi)) {
1140
wpa_hexdump_ascii(MSG_DEBUG, "EAP-SIM DB: unexpected identity",
1141
identity, identity_len);
1142
return EAP_SIM_DB_FAILURE;
1144
wpa_hexdump_ascii(MSG_DEBUG, "EAP-SIM DB: Get AKA auth for IMSI",
1145
identity, identity_len);
1147
entry = eap_sim_db_get_pending(data, identity, identity_len, 1);
1149
if (entry->state == FAILURE) {
1151
wpa_printf(MSG_DEBUG, "EAP-SIM DB: Failure");
1152
return EAP_SIM_DB_FAILURE;
1155
if (entry->state == PENDING) {
1156
eap_sim_db_add_pending(data, entry);
1157
wpa_printf(MSG_DEBUG, "EAP-SIM DB: Pending");
1158
return EAP_SIM_DB_PENDING;
1161
wpa_printf(MSG_DEBUG, "EAP-SIM DB: Returning successfully "
1162
"received authentication data");
1163
os_memcpy(_rand, entry->u.aka.rand, EAP_AKA_RAND_LEN);
1164
os_memcpy(autn, entry->u.aka.autn, EAP_AKA_AUTN_LEN);
1165
os_memcpy(ik, entry->u.aka.ik, EAP_AKA_IK_LEN);
1166
os_memcpy(ck, entry->u.aka.ck, EAP_AKA_CK_LEN);
1167
os_memcpy(res, entry->u.aka.res, EAP_AKA_RES_MAX_LEN);
1168
*res_len = entry->u.aka.res_len;
1173
if (data->sock < 0) {
1174
if (eap_sim_db_open_socket(data) < 0)
1175
return EAP_SIM_DB_FAILURE;
1178
len = os_snprintf(msg, sizeof(msg), "AKA-REQ-AUTH ");
1179
if (len < 0 || len + identity_len >= sizeof(msg))
1180
return EAP_SIM_DB_FAILURE;
1181
os_memcpy(msg + len, identity, identity_len);
1182
len += identity_len;
1184
wpa_hexdump(MSG_DEBUG, "EAP-SIM DB: requesting AKA authentication "
1185
"data for IMSI", identity, identity_len);
1186
if (eap_sim_db_send(data, msg, len) < 0)
1187
return EAP_SIM_DB_FAILURE;
1189
entry = os_zalloc(sizeof(*entry));
1191
return EAP_SIM_DB_FAILURE;
1193
os_get_time(&entry->timestamp);
1195
os_memcpy(entry->imsi, identity, identity_len);
1196
entry->imsi_len = identity_len;
1197
entry->cb_session_ctx = cb_session_ctx;
1198
entry->state = PENDING;
1199
eap_sim_db_add_pending(data, entry);
1200
eap_sim_db_expire_pending(data);
1202
return EAP_SIM_DB_PENDING;
1207
* eap_sim_db_resynchronize - Resynchronize AKA AUTN
1208
* @priv: Private data pointer from eap_sim_db_init()
1209
* @identity: User name identity
1210
* @identity_len: Length of identity in bytes
1211
* @auts: AUTS value from the peer
1212
* @_rand: RAND value used in the rejected message
1213
* Returns: 0 on success, -1 on failure
1215
* This function is called when the peer reports synchronization failure in the
1216
* AUTN value by sending AUTS. The AUTS and RAND values should be sent to
1217
* HLR/AuC to allow it to resynchronize with the peer. After this,
1218
* eap_sim_db_get_aka_auth() will be called again to to fetch updated
1219
* RAND/AUTN values for the next challenge.
1221
int eap_sim_db_resynchronize(void *priv, const u8 *identity,
1222
size_t identity_len, const u8 *auts,
1225
struct eap_sim_db_data *data = priv;
1228
if (identity_len < 2 || identity == NULL ||
1229
identity[0] != EAP_AKA_PERMANENT_PREFIX) {
1230
wpa_hexdump_ascii(MSG_DEBUG, "EAP-SIM DB: unexpected identity",
1231
identity, identity_len);
1236
for (i = 0; i < identity_len; i++) {
1237
if (identity[i] == '@') {
1242
if (identity_len > 20) {
1243
wpa_hexdump_ascii(MSG_DEBUG, "EAP-SIM DB: unexpected identity",
1244
identity, identity_len);
1248
if (data->sock >= 0) {
1252
len = os_snprintf(msg, sizeof(msg), "AKA-AUTS ");
1253
if (len < 0 || len + identity_len >= sizeof(msg))
1255
os_memcpy(msg + len, identity, identity_len);
1256
len += identity_len;
1258
ret = os_snprintf(msg + len, sizeof(msg) - len, " ");
1259
if (ret < 0 || (size_t) ret >= sizeof(msg) - len)
1262
len += wpa_snprintf_hex(msg + len, sizeof(msg) - len,
1263
auts, EAP_AKA_AUTS_LEN);
1264
ret = os_snprintf(msg + len, sizeof(msg) - len, " ");
1265
if (ret < 0 || (size_t) ret >= sizeof(msg) - len)
1268
len += wpa_snprintf_hex(msg + len, sizeof(msg) - len,
1269
_rand, EAP_AKA_RAND_LEN);
1270
wpa_hexdump(MSG_DEBUG, "EAP-SIM DB: reporting AKA AUTS for "
1271
"IMSI", identity, identity_len);
1272
if (eap_sim_db_send(data, msg, len) < 0)