1/*
2 * hostapd / EAP-SIM database/authenticator gateway
3 * Copyright (c) 2005-2007, Jouni Malinen <j@w1.fi>
4 *
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.
8 *
9 * Alternatively, this software may be distributed under the terms of BSD
10 * license.
11 *
12 * See README and COPYING for more details.
13 *
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.
22 */
23
24#include "includes.h"
25#include <sys/un.h>
26
27#include "common.h"
28#include "eap_common/eap_sim_common.h"
29#include "eap_server/eap_sim_db.h"
30#include "eloop.h"
31
32struct eap_sim_pseudonym {
33	struct eap_sim_pseudonym *next;
34	u8 *identity;
35	size_t identity_len;
36	char *pseudonym;
37};
38
39struct eap_sim_db_pending {
40	struct eap_sim_db_pending *next;
41	u8 imsi[20];
42	size_t imsi_len;
43	enum { PENDING, SUCCESS, FAILURE } state;
44	void *cb_session_ctx;
45	struct os_time timestamp;
46	int aka;
47	union {
48		struct {
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];
52			int num_chal;
53		} sim;
54		struct {
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];
60			size_t res_len;
61		} aka;
62	} u;
63};
64
65struct eap_sim_db_data {
66	int sock;
67	char *fname;
68	char *local_sock;
69	void (*get_complete_cb)(void *ctx, void *session_ctx);
70	void *ctx;
71	struct eap_sim_pseudonym *pseudonyms;
72	struct eap_sim_reauth *reauths;
73	struct eap_sim_db_pending *pending;
74};
75
76
77static struct eap_sim_db_pending *
78eap_sim_db_get_pending(struct eap_sim_db_data *data, const u8 *imsi,
79		       size_t imsi_len, int aka)
80{
81	struct eap_sim_db_pending *entry, *prev = NULL;
82
83	entry = data->pending;
84	while (entry) {
85		if (entry->aka == aka && entry->imsi_len == imsi_len &&
86		    os_memcmp(entry->imsi, imsi, imsi_len) == 0) {
87			if (prev)
88				prev->next = entry->next;
89			else
90				data->pending = entry->next;
91			break;
92		}
93		prev = entry;
94		entry = entry->next;
95	}
96	return entry;
97}
98
99
100static void eap_sim_db_add_pending(struct eap_sim_db_data *data,
101				   struct eap_sim_db_pending *entry)
102{
103	entry->next = data->pending;
104	data->pending = entry;
105}
106
107
108static void eap_sim_db_sim_resp_auth(struct eap_sim_db_data *data,
109				     const char *imsi, char *buf)
110{
111	char *start, *end, *pos;
112	struct eap_sim_db_pending *entry;
113	int num_chal;
114
115	/*
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)
119	 */
120
121	entry = eap_sim_db_get_pending(data, (u8 *) imsi, os_strlen(imsi), 0);
122	if (entry == NULL) {
123		wpa_printf(MSG_DEBUG, "EAP-SIM DB: No pending entry for the "
124			   "received message found");
125		return;
126	}
127
128	start = buf;
129	if (os_strncmp(start, "FAILURE", 7) == 0) {
130		wpa_printf(MSG_DEBUG, "EAP-SIM DB: External server reported "
131			   "failure");
132		entry->state = FAILURE;
133		eap_sim_db_add_pending(data, entry);
134		data->get_complete_cb(data->ctx, entry->cb_session_ctx);
135		return;
136	}
137
138	num_chal = 0;
139	while (num_chal < EAP_SIM_MAX_CHAL) {
140		end = os_strchr(start, ' ');
141		if (end)
142			*end = '\0';
143
144		pos = os_strchr(start, ':');
145		if (pos == NULL)
146			goto parse_fail;
147		*pos = '\0';
148		if (hexstr2bin(start, entry->u.sim.kc[num_chal],
149			       EAP_SIM_KC_LEN))
150			goto parse_fail;
151
152		start = pos + 1;
153		pos = os_strchr(start, ':');
154		if (pos == NULL)
155			goto parse_fail;
156		*pos = '\0';
157		if (hexstr2bin(start, entry->u.sim.sres[num_chal],
158			       EAP_SIM_SRES_LEN))
159			goto parse_fail;
160
161		start = pos + 1;
162		if (hexstr2bin(start, entry->u.sim.rand[num_chal],
163			       GSM_RAND_LEN))
164			goto parse_fail;
165
166		num_chal++;
167		if (end == NULL)
168			break;
169		else
170			start = end + 1;
171	}
172	entry->u.sim.num_chal = num_chal;
173
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);
179	return;
180
181parse_fail:
182	wpa_printf(MSG_DEBUG, "EAP-SIM DB: Failed to parse response string");
183	os_free(entry);
184}
185
186
187static void eap_sim_db_aka_resp_auth(struct eap_sim_db_data *data,
188				     const char *imsi, char *buf)
189{
190	char *start, *end;
191	struct eap_sim_db_pending *entry;
192
193	/*
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)
197	 */
198
199	entry = eap_sim_db_get_pending(data, (u8 *) imsi, os_strlen(imsi), 1);
200	if (entry == NULL) {
201		wpa_printf(MSG_DEBUG, "EAP-SIM DB: No pending entry for the "
202			   "received message found");
203		return;
204	}
205
206	start = buf;
207	if (os_strncmp(start, "FAILURE", 7) == 0) {
208		wpa_printf(MSG_DEBUG, "EAP-SIM DB: External server reported "
209			   "failure");
210		entry->state = FAILURE;
211		eap_sim_db_add_pending(data, entry);
212		data->get_complete_cb(data->ctx, entry->cb_session_ctx);
213		return;
214	}
215
216	end = os_strchr(start, ' ');
217	if (end == NULL)
218		goto parse_fail;
219	*end = '\0';
220	if (hexstr2bin(start, entry->u.aka.rand, EAP_AKA_RAND_LEN))
221		goto parse_fail;
222
223	start = end + 1;
224	end = os_strchr(start, ' ');
225	if (end == NULL)
226		goto parse_fail;
227	*end = '\0';
228	if (hexstr2bin(start, entry->u.aka.autn, EAP_AKA_AUTN_LEN))
229		goto parse_fail;
230
231	start = end + 1;
232	end = os_strchr(start, ' ');
233	if (end == NULL)
234		goto parse_fail;
235	*end = '\0';
236	if (hexstr2bin(start, entry->u.aka.ik, EAP_AKA_IK_LEN))
237		goto parse_fail;
238
239	start = end + 1;
240	end = os_strchr(start, ' ');
241	if (end == NULL)
242		goto parse_fail;
243	*end = '\0';
244	if (hexstr2bin(start, entry->u.aka.ck, EAP_AKA_CK_LEN))
245		goto parse_fail;
246
247	start = end + 1;
248	end = os_strchr(start, ' ');
249	if (end)
250		*end = '\0';
251	else {
252		end = start;
253		while (*end)
254			end++;
255	}
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;
260		goto parse_fail;
261	}
262	if (hexstr2bin(start, entry->u.aka.res, entry->u.aka.res_len))
263		goto parse_fail;
264
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);
270	return;
271
272parse_fail:
273	wpa_printf(MSG_DEBUG, "EAP-SIM DB: Failed to parse response string");
274	os_free(entry);
275}
276
277
278static void eap_sim_db_receive(int sock, void *eloop_ctx, void *sock_ctx)
279{
280	struct eap_sim_db_data *data = eloop_ctx;
281	char buf[1000], *pos, *cmd, *imsi;
282	int res;
283
284	res = recv(sock, buf, sizeof(buf), 0);
285	if (res < 0)
286		return;
287	wpa_hexdump_ascii_key(MSG_MSGDUMP, "EAP-SIM DB: Received from an "
288			      "external source", (u8 *) buf, res);
289	if (res == 0)
290		return;
291	if (res >= (int) sizeof(buf))
292		res = sizeof(buf) - 1;
293	buf[res] = '\0';
294
295	if (data->get_complete_cb == NULL) {
296		wpa_printf(MSG_DEBUG, "EAP-SIM DB: No get_complete_cb "
297			   "registered");
298		return;
299	}
300
301	/* <cmd> <IMSI> ... */
302
303	cmd = buf;
304	pos = os_strchr(cmd, ' ');
305	if (pos == NULL)
306		goto parse_fail;
307	*pos = '\0';
308	imsi = pos + 1;
309	pos = os_strchr(imsi, ' ');
310	if (pos == NULL)
311		goto parse_fail;
312	*pos = '\0';
313	wpa_printf(MSG_DEBUG, "EAP-SIM DB: External response=%s for IMSI %s",
314		   cmd, imsi);
315
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);
320	else
321		wpa_printf(MSG_INFO, "EAP-SIM DB: Unknown external response "
322			   "'%s'", cmd);
323	return;
324
325parse_fail:
326	wpa_printf(MSG_DEBUG, "EAP-SIM DB: Failed to parse response string");
327}
328
329
330static int eap_sim_db_open_socket(struct eap_sim_db_data *data)
331{
332	struct sockaddr_un addr;
333	static int counter = 0;
334
335	if (os_strncmp(data->fname, "unix:", 5) != 0)
336		return -1;
337
338	data->sock = socket(PF_UNIX, SOCK_DGRAM, 0);
339	if (data->sock < 0) {
340		perror("socket(eap_sim_db)");
341		return -1;
342	}
343
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)");
351		close(data->sock);
352		data->sock = -1;
353		return -1;
354	}
355
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));
364		close(data->sock);
365		data->sock = -1;
366		return -1;
367	}
368
369	eloop_register_read_sock(data->sock, eap_sim_db_receive, data, NULL);
370
371	return 0;
372}
373
374
375static void eap_sim_db_close_socket(struct eap_sim_db_data *data)
376{
377	if (data->sock >= 0) {
378		eloop_unregister_read_sock(data->sock);
379		close(data->sock);
380		data->sock = -1;
381	}
382	if (data->local_sock) {
383		unlink(data->local_sock);
384		os_free(data->local_sock);
385		data->local_sock = NULL;
386	}
387}
388
389
390/**
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
396 */
397void * eap_sim_db_init(const char *config,
398		       void (*get_complete_cb)(void *ctx, void *session_ctx),
399		       void *ctx)
400{
401	struct eap_sim_db_data *data;
402
403	data = os_zalloc(sizeof(*data));
404	if (data == NULL)
405		return NULL;
406
407	data->sock = -1;
408	data->get_complete_cb = get_complete_cb;
409	data->ctx = ctx;
410	data->fname = os_strdup(config);
411	if (data->fname == NULL)
412		goto fail;
413
414	if (os_strncmp(data->fname, "unix:", 5) == 0) {
415		if (eap_sim_db_open_socket(data))
416			goto fail;
417	}
418
419	return data;
420
421fail:
422	eap_sim_db_close_socket(data);
423	os_free(data->fname);
424	os_free(data);
425	return NULL;
426}
427
428
429static void eap_sim_db_free_pseudonym(struct eap_sim_pseudonym *p)
430{
431	os_free(p->identity);
432	os_free(p->pseudonym);
433	os_free(p);
434}
435
436
437static void eap_sim_db_free_reauth(struct eap_sim_reauth *r)
438{
439	os_free(r->identity);
440	os_free(r->reauth_id);
441	os_free(r);
442}
443
444
445/**
446 * eap_sim_db_deinit - Deinitialize EAP-SIM DB/authentication gw interface
447 * @priv: Private data pointer from eap_sim_db_init()
448 */
449void eap_sim_db_deinit(void *priv)
450{
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;
455
456	eap_sim_db_close_socket(data);
457	os_free(data->fname);
458
459	p = data->pseudonyms;
460	while (p) {
461		prev = p;
462		p = p->next;
463		eap_sim_db_free_pseudonym(prev);
464	}
465
466	r = data->reauths;
467	while (r) {
468		prevr = r;
469		r = r->next;
470		eap_sim_db_free_reauth(prevr);
471	}
472
473	pending = data->pending;
474	while (pending) {
475		prev_pending = pending;
476		pending = pending->next;
477		os_free(prev_pending);
478	}
479
480	os_free(data);
481}
482
483
484static int eap_sim_db_send(struct eap_sim_db_data *data, const char *msg,
485			   size_t len)
486{
487	int _errno = 0;
488
489	if (send(data->sock, msg, len, 0) < 0) {
490		_errno = errno;
491		perror("send[EAP-SIM DB UNIX]");
492	}
493
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)
499			return -1;
500		wpa_printf(MSG_DEBUG, "EAP-SIM DB: Reconnected to the "
501			   "external server");
502		if (send(data->sock, msg, len, 0) < 0) {
503			perror("send[EAP-SIM DB UNIX]");
504			return -1;
505		}
506	}
507
508	return 0;
509}
510
511
512static void eap_sim_db_expire_pending(struct eap_sim_db_data *data)
513{
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 */
517}
518
519
520/**
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.
535 *
536 * In most cases, the user name is '1' | IMSI, i.e., 1 followed by the IMSI in
537 * ASCII format.
538 *
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.
546 */
547int 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)
551{
552	struct eap_sim_db_data *data = priv;
553	struct eap_sim_db_pending *entry;
554	int len, ret;
555	size_t i;
556	char msg[40];
557
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;
562	}
563	identity++;
564	identity_len--;
565	for (i = 0; i < identity_len; i++) {
566		if (identity[i] == '@') {
567			identity_len = i;
568			break;
569		}
570	}
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;
575	}
576	wpa_hexdump_ascii(MSG_DEBUG, "EAP-SIM DB: Get GSM triplets for IMSI",
577			  identity, identity_len);
578
579	entry = eap_sim_db_get_pending(data, identity, identity_len, 0);
580	if (entry) {
581		int num_chal;
582		if (entry->state == FAILURE) {
583			wpa_printf(MSG_DEBUG, "EAP-SIM DB: Pending entry -> "
584				   "failure");
585			os_free(entry);
586			return EAP_SIM_DB_FAILURE;
587		}
588
589		if (entry->state == PENDING) {
590			wpa_printf(MSG_DEBUG, "EAP-SIM DB: Pending entry -> "
591				   "still pending");
592			eap_sim_db_add_pending(data, entry);
593			return EAP_SIM_DB_PENDING;
594		}
595
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)
600			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);
605		os_free(entry);
606		return num_chal;
607	}
608
609	if (data->sock < 0) {
610		if (eap_sim_db_open_socket(data) < 0)
611			return EAP_SIM_DB_FAILURE;
612	}
613
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);
618	len += 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;
622	len += ret;
623
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;
628
629	entry = os_zalloc(sizeof(*entry));
630	if (entry == NULL)
631		return EAP_SIM_DB_FAILURE;
632
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);
640
641	return EAP_SIM_DB_PENDING;
642}
643
644
645static struct eap_sim_pseudonym *
646eap_sim_db_get_pseudonym(struct eap_sim_db_data *data, const u8 *identity,
647			 size_t identity_len)
648{
649	char *pseudonym;
650	size_t len;
651	struct eap_sim_pseudonym *p;
652
653	if (identity_len == 0 ||
654	    (identity[0] != EAP_SIM_PSEUDONYM_PREFIX &&
655	     identity[0] != EAP_AKA_PSEUDONYM_PREFIX))
656		return NULL;
657
658	/* Remove possible realm from identity */
659	len = 0;
660	while (len < identity_len) {
661		if (identity[len] == '@')
662			break;
663		len++;
664	}
665
666	pseudonym = os_malloc(len + 1);
667	if (pseudonym == NULL)
668		return NULL;
669	os_memcpy(pseudonym, identity, len);
670	pseudonym[len] = '\0';
671
672	p = data->pseudonyms;
673	while (p) {
674		if (os_strcmp(p->pseudonym, pseudonym) == 0)
675			break;
676		p = p->next;
677	}
678
679	os_free(pseudonym);
680
681	return p;
682}
683
684
685static struct eap_sim_pseudonym *
686eap_sim_db_get_pseudonym_id(struct eap_sim_db_data *data, const u8 *identity,
687			    size_t identity_len)
688{
689	struct eap_sim_pseudonym *p;
690
691	if (identity_len == 0 ||
692	    (identity[0] != EAP_SIM_PERMANENT_PREFIX &&
693	     identity[0] != EAP_AKA_PERMANENT_PREFIX))
694		return NULL;
695
696	p = data->pseudonyms;
697	while (p) {
698		if (identity_len == p->identity_len &&
699		    os_memcmp(p->identity, identity, identity_len) == 0)
700			break;
701		p = p->next;
702	}
703
704	return p;
705}
706
707
708static struct eap_sim_reauth *
709eap_sim_db_get_reauth(struct eap_sim_db_data *data, const u8 *identity,
710		      size_t identity_len)
711{
712	char *reauth_id;
713	size_t len;
714	struct eap_sim_reauth *r;
715
716	if (identity_len == 0 ||
717	    (identity[0] != EAP_SIM_REAUTH_ID_PREFIX &&
718	     identity[0] != EAP_AKA_REAUTH_ID_PREFIX))
719		return NULL;
720
721	/* Remove possible realm from identity */
722	len = 0;
723	while (len < identity_len) {
724		if (identity[len] == '@')
725			break;
726		len++;
727	}
728
729	reauth_id = os_malloc(len + 1);
730	if (reauth_id == NULL)
731		return NULL;
732	os_memcpy(reauth_id, identity, len);
733	reauth_id[len] = '\0';
734
735	r = data->reauths;
736	while (r) {
737		if (os_strcmp(r->reauth_id, reauth_id) == 0)
738			break;
739		r = r->next;
740	}
741
742	os_free(reauth_id);
743
744	return r;
745}
746
747
748static struct eap_sim_reauth *
749eap_sim_db_get_reauth_id(struct eap_sim_db_data *data, const u8 *identity,
750			 size_t identity_len)
751{
752	struct eap_sim_pseudonym *p;
753	struct eap_sim_reauth *r;
754
755	if (identity_len == 0)
756		return NULL;
757
758	p = eap_sim_db_get_pseudonym(data, identity, identity_len);
759	if (p == NULL)
760		p = eap_sim_db_get_pseudonym_id(data, identity, identity_len);
761	if (p) {
762		identity = p->identity;
763		identity_len = p->identity_len;
764	}
765
766	r = data->reauths;
767	while (r) {
768		if (identity_len == r->identity_len &&
769		    os_memcmp(r->identity, identity, identity_len) == 0)
770			break;
771		r = r->next;
772	}
773
774	return r;
775}
776
777
778/**
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
784 *
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.
787 */
788int eap_sim_db_identity_known(void *priv, const u8 *identity,
789			      size_t identity_len)
790{
791	struct eap_sim_db_data *data = priv;
792
793	if (identity == NULL || identity_len < 2)
794		return -1;
795
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);
800		return p ? 0 : -1;
801	}
802
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);
807		return r ? 0 : -1;
808	}
809
810	if (identity[0] != EAP_SIM_PERMANENT_PREFIX &&
811	    identity[0] != EAP_AKA_PERMANENT_PREFIX) {
812		/* Unknown identity prefix */
813		return -1;
814	}
815
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.
823	 */
824	return -1;
825}
826
827
828static char * eap_sim_db_get_next(struct eap_sim_db_data *data, char prefix)
829{
830	char *id, *pos, *end;
831	u8 buf[10];
832
833	if (os_get_random(buf, sizeof(buf)))
834		return NULL;
835	id = os_malloc(sizeof(buf) * 2 + 2);
836	if (id == NULL)
837		return NULL;
838
839	pos = id;
840	end = id + sizeof(buf) * 2 + 2;
841	*pos++ = prefix;
842	pos += wpa_snprintf_hex(pos, end - pos, buf, sizeof(buf));
843
844	return id;
845}
846
847
848/**
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
853 *
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.
858 */
859char * eap_sim_db_get_next_pseudonym(void *priv, int aka)
860{
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);
864}
865
866
867/**
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
872 *
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
877 * returned buffer.
878 */
879char * eap_sim_db_get_next_reauth_id(void *priv, int aka)
880{
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);
884}
885
886
887/**
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
894 * free it.
895 * Returns: 0 on success, -1 on failure
896 *
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.
899 */
900int eap_sim_db_add_pseudonym(void *priv, const u8 *identity,
901			     size_t identity_len, char *pseudonym)
902{
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);
908
909	/* TODO: could store last two pseudonyms */
910	p = eap_sim_db_get_pseudonym(data, identity, identity_len);
911	if (p == NULL)
912		p = eap_sim_db_get_pseudonym_id(data, identity, identity_len);
913
914	if (p) {
915		wpa_printf(MSG_DEBUG, "EAP-SIM DB: Replacing previous "
916			   "pseudonym: %s", p->pseudonym);
917		os_free(p->pseudonym);
918		p->pseudonym = pseudonym;
919		return 0;
920	}
921
922	p = os_zalloc(sizeof(*p));
923	if (p == NULL) {
924		os_free(pseudonym);
925		return -1;
926	}
927
928	p->next = data->pseudonyms;
929	p->identity = os_malloc(identity_len);
930	if (p->identity == NULL) {
931		os_free(p);
932		os_free(pseudonym);
933		return -1;
934	}
935	os_memcpy(p->identity, identity, identity_len);
936	p->identity_len = identity_len;
937	p->pseudonym = pseudonym;
938	data->pseudonyms = p;
939
940	wpa_printf(MSG_DEBUG, "EAP-SIM DB: Added new pseudonym entry");
941	return 0;
942}
943
944
945static struct eap_sim_reauth *
946eap_sim_db_add_reauth_data(struct eap_sim_db_data *data, const u8 *identity,
947			   size_t identity_len, char *reauth_id, u16 counter)
948{
949	struct eap_sim_reauth *r;
950
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);
954
955	r = eap_sim_db_get_reauth(data, identity, identity_len);
956	if (r == NULL)
957		r = eap_sim_db_get_reauth_id(data, identity, identity_len);
958
959	if (r) {
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;
964	} else {
965		r = os_zalloc(sizeof(*r));
966		if (r == NULL) {
967			os_free(reauth_id);
968			return NULL;
969		}
970
971		r->next = data->reauths;
972		r->identity = os_malloc(identity_len);
973		if (r->identity == NULL) {
974			os_free(r);
975			os_free(reauth_id);
976			return NULL;
977		}
978		os_memcpy(r->identity, identity, identity_len);
979		r->identity_len = identity_len;
980		r->reauth_id = reauth_id;
981		data->reauths = r;
982		wpa_printf(MSG_DEBUG, "EAP-SIM DB: Added new reauth entry");
983	}
984
985	r->counter = counter;
986
987	return r;
988}
989
990
991/**
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
998 * free it.
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
1002 *
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
1005 * anymore.
1006 */
1007int eap_sim_db_add_reauth(void *priv, const u8 *identity,
1008			  size_t identity_len, char *reauth_id, u16 counter,
1009			  const u8 *mk)
1010{
1011	struct eap_sim_db_data *data = priv;
1012	struct eap_sim_reauth *r;
1013
1014	r = eap_sim_db_add_reauth_data(data, identity, identity_len, reauth_id,
1015				       counter);
1016	if (r == NULL)
1017		return -1;
1018
1019	os_memcpy(r->mk, mk, EAP_SIM_MK_LEN);
1020	r->aka_prime = 0;
1021
1022	return 0;
1023}
1024
1025
1026#ifdef EAP_SERVER_AKA_PRIME
1027/**
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
1034 * free it.
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
1040 *
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
1043 * anymore.
1044 */
1045int 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,
1048				const u8 *k_re)
1049{
1050	struct eap_sim_db_data *data = priv;
1051	struct eap_sim_reauth *r;
1052
1053	r = eap_sim_db_add_reauth_data(data, identity, identity_len, reauth_id,
1054				       counter);
1055	if (r == NULL)
1056		return -1;
1057
1058	r->aka_prime = 1;
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);
1062
1063	return 0;
1064}
1065#endif /* EAP_SERVER_AKA_PRIME */
1066
1067
1068/**
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
1075 */
1076const u8 * eap_sim_db_get_permanent(void *priv, const u8 *identity,
1077				    size_t identity_len, size_t *len)
1078{
1079	struct eap_sim_db_data *data = priv;
1080	struct eap_sim_pseudonym *p;
1081
1082	if (identity == NULL)
1083		return NULL;
1084
1085	p = eap_sim_db_get_pseudonym(data, identity, identity_len);
1086	if (p == NULL)
1087		p = eap_sim_db_get_pseudonym_id(data, identity, identity_len);
1088	if (p == NULL)
1089		return NULL;
1090
1091	*len = p->identity_len;
1092	return p->identity;
1093}
1094
1095
1096/**
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
1100 * reauth_id)
1101 * @identity_len: Length of identity
1102 * Returns: Pointer to the re-auth entry, or %NULL if not found
1103 */
1104struct eap_sim_reauth *
1105eap_sim_db_get_reauth_entry(void *priv, const u8 *identity,
1106			    size_t identity_len)
1107{
1108	struct eap_sim_db_data *data = priv;
1109	struct eap_sim_reauth *r;
1110
1111	if (identity == NULL)
1112		return NULL;
1113	r = eap_sim_db_get_reauth(data, identity, identity_len);
1114	if (r == NULL)
1115		r = eap_sim_db_get_reauth_id(data, identity, identity_len);
1116	return r;
1117}
1118
1119
1120/**
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()
1125 */
1126void eap_sim_db_remove_reauth(void *priv, struct eap_sim_reauth *reauth)
1127{
1128	struct eap_sim_db_data *data = priv;
1129	struct eap_sim_reauth *r, *prev = NULL;
1130	r = data->reauths;
1131	while (r) {
1132		if (r == reauth) {
1133			if (prev)
1134				prev->next = r->next;
1135			else
1136				data->reauths = r->next;
1137			eap_sim_db_free_reauth(r);
1138			return;
1139		}
1140		prev = r;
1141		r = r->next;
1142	}
1143}
1144
1145
1146/**
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.
1162 *
1163 * In most cases, the user name is '0' | IMSI, i.e., 0 followed by the IMSI in
1164 * ASCII format.
1165 *
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.
1173 */
1174int 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)
1178{
1179	struct eap_sim_db_data *data = priv;
1180	struct eap_sim_db_pending *entry;
1181	int len;
1182	size_t i;
1183	char msg[40];
1184
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;
1190	}
1191	identity++;
1192	identity_len--;
1193	for (i = 0; i < identity_len; i++) {
1194		if (identity[i] == '@') {
1195			identity_len = i;
1196			break;
1197		}
1198	}
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;
1203	}
1204	wpa_hexdump_ascii(MSG_DEBUG, "EAP-SIM DB: Get AKA auth for IMSI",
1205			  identity, identity_len);
1206
1207	entry = eap_sim_db_get_pending(data, identity, identity_len, 1);
1208	if (entry) {
1209		if (entry->state == FAILURE) {
1210			os_free(entry);
1211			wpa_printf(MSG_DEBUG, "EAP-SIM DB: Failure");
1212			return EAP_SIM_DB_FAILURE;
1213		}
1214
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;
1219		}
1220
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;
1229		os_free(entry);
1230		return 0;
1231	}
1232
1233	if (data->sock < 0) {
1234		if (eap_sim_db_open_socket(data) < 0)
1235			return EAP_SIM_DB_FAILURE;
1236	}
1237
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;
1243
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;
1248
1249	entry = os_zalloc(sizeof(*entry));
1250	if (entry == NULL)
1251		return EAP_SIM_DB_FAILURE;
1252
1253	os_get_time(&entry->timestamp);
1254	entry->aka = 1;
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);
1261
1262	return EAP_SIM_DB_PENDING;
1263}
1264
1265
1266/**
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
1274 *
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.
1280 */
1281int eap_sim_db_resynchronize(void *priv, const u8 *identity,
1282			     size_t identity_len, const u8 *auts,
1283			     const u8 *_rand)
1284{
1285	struct eap_sim_db_data *data = priv;
1286	size_t i;
1287
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);
1292		return -1;
1293	}
1294	identity++;
1295	identity_len--;
1296	for (i = 0; i < identity_len; i++) {
1297		if (identity[i] == '@') {
1298			identity_len = i;
1299			break;
1300		}
1301	}
1302	if (identity_len > 20) {
1303		wpa_hexdump_ascii(MSG_DEBUG, "EAP-SIM DB: unexpected identity",
1304				  identity, identity_len);
1305		return -1;
1306	}
1307
1308	if (data->sock >= 0) {
1309		char msg[100];
1310		int len, ret;
1311
1312		len = os_snprintf(msg, sizeof(msg), "AKA-AUTS ");
1313		if (len < 0 || len + identity_len >= sizeof(msg))
1314			return -1;
1315		os_memcpy(msg + len, identity, identity_len);
1316		len += identity_len;
1317
1318		ret = os_snprintf(msg + len, sizeof(msg) - len, " ");
1319		if (ret < 0 || (size_t) ret >= sizeof(msg) - len)
1320			return -1;
1321		len += ret;
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)
1326			return -1;
1327		len += ret;
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)
1333			return -1;
1334	}
1335
1336	return 0;
1337}
1338