eap_sim_db.c revision 8d520ff1dc2da35cdca849e982051b86468016d8
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 "crypto/random.h"
29#include "eap_common/eap_sim_common.h"
30#include "eap_server/eap_sim_db.h"
31#include "eloop.h"
32
33struct eap_sim_pseudonym {
34	struct eap_sim_pseudonym *next;
35	u8 *identity;
36	size_t identity_len;
37	char *pseudonym;
38};
39
40struct eap_sim_db_pending {
41	struct eap_sim_db_pending *next;
42	u8 imsi[20];
43	size_t imsi_len;
44	enum { PENDING, SUCCESS, FAILURE } state;
45	void *cb_session_ctx;
46	struct os_time timestamp;
47	int aka;
48	union {
49		struct {
50			u8 kc[EAP_SIM_MAX_CHAL][EAP_SIM_KC_LEN];
51			u8 sres[EAP_SIM_MAX_CHAL][EAP_SIM_SRES_LEN];
52			u8 rand[EAP_SIM_MAX_CHAL][GSM_RAND_LEN];
53			int num_chal;
54		} sim;
55		struct {
56			u8 rand[EAP_AKA_RAND_LEN];
57			u8 autn[EAP_AKA_AUTN_LEN];
58			u8 ik[EAP_AKA_IK_LEN];
59			u8 ck[EAP_AKA_CK_LEN];
60			u8 res[EAP_AKA_RES_MAX_LEN];
61			size_t res_len;
62		} aka;
63	} u;
64};
65
66struct eap_sim_db_data {
67	int sock;
68	char *fname;
69	char *local_sock;
70	void (*get_complete_cb)(void *ctx, void *session_ctx);
71	void *ctx;
72	struct eap_sim_pseudonym *pseudonyms;
73	struct eap_sim_reauth *reauths;
74	struct eap_sim_db_pending *pending;
75};
76
77
78static struct eap_sim_db_pending *
79eap_sim_db_get_pending(struct eap_sim_db_data *data, const u8 *imsi,
80		       size_t imsi_len, int aka)
81{
82	struct eap_sim_db_pending *entry, *prev = NULL;
83
84	entry = data->pending;
85	while (entry) {
86		if (entry->aka == aka && entry->imsi_len == imsi_len &&
87		    os_memcmp(entry->imsi, imsi, imsi_len) == 0) {
88			if (prev)
89				prev->next = entry->next;
90			else
91				data->pending = entry->next;
92			break;
93		}
94		prev = entry;
95		entry = entry->next;
96	}
97	return entry;
98}
99
100
101static void eap_sim_db_add_pending(struct eap_sim_db_data *data,
102				   struct eap_sim_db_pending *entry)
103{
104	entry->next = data->pending;
105	data->pending = entry;
106}
107
108
109static void eap_sim_db_sim_resp_auth(struct eap_sim_db_data *data,
110				     const char *imsi, char *buf)
111{
112	char *start, *end, *pos;
113	struct eap_sim_db_pending *entry;
114	int num_chal;
115
116	/*
117	 * SIM-RESP-AUTH <IMSI> Kc(i):SRES(i):RAND(i) ...
118	 * SIM-RESP-AUTH <IMSI> FAILURE
119	 * (IMSI = ASCII string, Kc/SRES/RAND = hex string)
120	 */
121
122	entry = eap_sim_db_get_pending(data, (u8 *) imsi, os_strlen(imsi), 0);
123	if (entry == NULL) {
124		wpa_printf(MSG_DEBUG, "EAP-SIM DB: No pending entry for the "
125			   "received message found");
126		return;
127	}
128
129	start = buf;
130	if (os_strncmp(start, "FAILURE", 7) == 0) {
131		wpa_printf(MSG_DEBUG, "EAP-SIM DB: External server reported "
132			   "failure");
133		entry->state = FAILURE;
134		eap_sim_db_add_pending(data, entry);
135		data->get_complete_cb(data->ctx, entry->cb_session_ctx);
136		return;
137	}
138
139	num_chal = 0;
140	while (num_chal < EAP_SIM_MAX_CHAL) {
141		end = os_strchr(start, ' ');
142		if (end)
143			*end = '\0';
144
145		pos = os_strchr(start, ':');
146		if (pos == NULL)
147			goto parse_fail;
148		*pos = '\0';
149		if (hexstr2bin(start, entry->u.sim.kc[num_chal],
150			       EAP_SIM_KC_LEN))
151			goto parse_fail;
152
153		start = pos + 1;
154		pos = os_strchr(start, ':');
155		if (pos == NULL)
156			goto parse_fail;
157		*pos = '\0';
158		if (hexstr2bin(start, entry->u.sim.sres[num_chal],
159			       EAP_SIM_SRES_LEN))
160			goto parse_fail;
161
162		start = pos + 1;
163		if (hexstr2bin(start, entry->u.sim.rand[num_chal],
164			       GSM_RAND_LEN))
165			goto parse_fail;
166
167		num_chal++;
168		if (end == NULL)
169			break;
170		else
171			start = end + 1;
172	}
173	entry->u.sim.num_chal = num_chal;
174
175	entry->state = SUCCESS;
176	wpa_printf(MSG_DEBUG, "EAP-SIM DB: Authentication data parsed "
177		   "successfully - callback");
178	eap_sim_db_add_pending(data, entry);
179	data->get_complete_cb(data->ctx, entry->cb_session_ctx);
180	return;
181
182parse_fail:
183	wpa_printf(MSG_DEBUG, "EAP-SIM DB: Failed to parse response string");
184	os_free(entry);
185}
186
187
188static void eap_sim_db_aka_resp_auth(struct eap_sim_db_data *data,
189				     const char *imsi, char *buf)
190{
191	char *start, *end;
192	struct eap_sim_db_pending *entry;
193
194	/*
195	 * AKA-RESP-AUTH <IMSI> <RAND> <AUTN> <IK> <CK> <RES>
196	 * AKA-RESP-AUTH <IMSI> FAILURE
197	 * (IMSI = ASCII string, RAND/AUTN/IK/CK/RES = hex string)
198	 */
199
200	entry = eap_sim_db_get_pending(data, (u8 *) imsi, os_strlen(imsi), 1);
201	if (entry == NULL) {
202		wpa_printf(MSG_DEBUG, "EAP-SIM DB: No pending entry for the "
203			   "received message found");
204		return;
205	}
206
207	start = buf;
208	if (os_strncmp(start, "FAILURE", 7) == 0) {
209		wpa_printf(MSG_DEBUG, "EAP-SIM DB: External server reported "
210			   "failure");
211		entry->state = FAILURE;
212		eap_sim_db_add_pending(data, entry);
213		data->get_complete_cb(data->ctx, entry->cb_session_ctx);
214		return;
215	}
216
217	end = os_strchr(start, ' ');
218	if (end == NULL)
219		goto parse_fail;
220	*end = '\0';
221	if (hexstr2bin(start, entry->u.aka.rand, EAP_AKA_RAND_LEN))
222		goto parse_fail;
223
224	start = end + 1;
225	end = os_strchr(start, ' ');
226	if (end == NULL)
227		goto parse_fail;
228	*end = '\0';
229	if (hexstr2bin(start, entry->u.aka.autn, EAP_AKA_AUTN_LEN))
230		goto parse_fail;
231
232	start = end + 1;
233	end = os_strchr(start, ' ');
234	if (end == NULL)
235		goto parse_fail;
236	*end = '\0';
237	if (hexstr2bin(start, entry->u.aka.ik, EAP_AKA_IK_LEN))
238		goto parse_fail;
239
240	start = end + 1;
241	end = os_strchr(start, ' ');
242	if (end == NULL)
243		goto parse_fail;
244	*end = '\0';
245	if (hexstr2bin(start, entry->u.aka.ck, EAP_AKA_CK_LEN))
246		goto parse_fail;
247
248	start = end + 1;
249	end = os_strchr(start, ' ');
250	if (end)
251		*end = '\0';
252	else {
253		end = start;
254		while (*end)
255			end++;
256	}
257	entry->u.aka.res_len = (end - start) / 2;
258	if (entry->u.aka.res_len > EAP_AKA_RES_MAX_LEN) {
259		wpa_printf(MSG_DEBUG, "EAP-SIM DB: Too long RES");
260		entry->u.aka.res_len = 0;
261		goto parse_fail;
262	}
263	if (hexstr2bin(start, entry->u.aka.res, entry->u.aka.res_len))
264		goto parse_fail;
265
266	entry->state = SUCCESS;
267	wpa_printf(MSG_DEBUG, "EAP-SIM DB: Authentication data parsed "
268		   "successfully - callback");
269	eap_sim_db_add_pending(data, entry);
270	data->get_complete_cb(data->ctx, entry->cb_session_ctx);
271	return;
272
273parse_fail:
274	wpa_printf(MSG_DEBUG, "EAP-SIM DB: Failed to parse response string");
275	os_free(entry);
276}
277
278
279static void eap_sim_db_receive(int sock, void *eloop_ctx, void *sock_ctx)
280{
281	struct eap_sim_db_data *data = eloop_ctx;
282	char buf[1000], *pos, *cmd, *imsi;
283	int res;
284
285	res = recv(sock, buf, sizeof(buf), 0);
286	if (res < 0)
287		return;
288	wpa_hexdump_ascii_key(MSG_MSGDUMP, "EAP-SIM DB: Received from an "
289			      "external source", (u8 *) buf, res);
290	if (res == 0)
291		return;
292	if (res >= (int) sizeof(buf))
293		res = sizeof(buf) - 1;
294	buf[res] = '\0';
295
296	if (data->get_complete_cb == NULL) {
297		wpa_printf(MSG_DEBUG, "EAP-SIM DB: No get_complete_cb "
298			   "registered");
299		return;
300	}
301
302	/* <cmd> <IMSI> ... */
303
304	cmd = buf;
305	pos = os_strchr(cmd, ' ');
306	if (pos == NULL)
307		goto parse_fail;
308	*pos = '\0';
309	imsi = pos + 1;
310	pos = os_strchr(imsi, ' ');
311	if (pos == NULL)
312		goto parse_fail;
313	*pos = '\0';
314	wpa_printf(MSG_DEBUG, "EAP-SIM DB: External response=%s for IMSI %s",
315		   cmd, imsi);
316
317	if (os_strcmp(cmd, "SIM-RESP-AUTH") == 0)
318		eap_sim_db_sim_resp_auth(data, imsi, pos + 1);
319	else if (os_strcmp(cmd, "AKA-RESP-AUTH") == 0)
320		eap_sim_db_aka_resp_auth(data, imsi, pos + 1);
321	else
322		wpa_printf(MSG_INFO, "EAP-SIM DB: Unknown external response "
323			   "'%s'", cmd);
324	return;
325
326parse_fail:
327	wpa_printf(MSG_DEBUG, "EAP-SIM DB: Failed to parse response string");
328}
329
330
331static int eap_sim_db_open_socket(struct eap_sim_db_data *data)
332{
333	struct sockaddr_un addr;
334	static int counter = 0;
335
336	if (os_strncmp(data->fname, "unix:", 5) != 0)
337		return -1;
338
339	data->sock = socket(PF_UNIX, SOCK_DGRAM, 0);
340	if (data->sock < 0) {
341		perror("socket(eap_sim_db)");
342		return -1;
343	}
344
345	os_memset(&addr, 0, sizeof(addr));
346	addr.sun_family = AF_UNIX;
347	os_snprintf(addr.sun_path, sizeof(addr.sun_path),
348		    "/tmp/eap_sim_db_%d-%d", getpid(), counter++);
349	data->local_sock = os_strdup(addr.sun_path);
350	if (bind(data->sock, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
351		perror("bind(eap_sim_db)");
352		close(data->sock);
353		data->sock = -1;
354		return -1;
355	}
356
357	os_memset(&addr, 0, sizeof(addr));
358	addr.sun_family = AF_UNIX;
359	os_strlcpy(addr.sun_path, data->fname + 5, sizeof(addr.sun_path));
360	if (connect(data->sock, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
361		perror("connect(eap_sim_db)");
362		wpa_hexdump_ascii(MSG_INFO, "HLR/AuC GW socket",
363				  (u8 *) addr.sun_path,
364				  os_strlen(addr.sun_path));
365		close(data->sock);
366		data->sock = -1;
367		return -1;
368	}
369
370	eloop_register_read_sock(data->sock, eap_sim_db_receive, data, NULL);
371
372	return 0;
373}
374
375
376static void eap_sim_db_close_socket(struct eap_sim_db_data *data)
377{
378	if (data->sock >= 0) {
379		eloop_unregister_read_sock(data->sock);
380		close(data->sock);
381		data->sock = -1;
382	}
383	if (data->local_sock) {
384		unlink(data->local_sock);
385		os_free(data->local_sock);
386		data->local_sock = NULL;
387	}
388}
389
390
391/**
392 * eap_sim_db_init - Initialize EAP-SIM DB / authentication gateway interface
393 * @config: Configuration data (e.g., file name)
394 * @get_complete_cb: Callback function for reporting availability of triplets
395 * @ctx: Context pointer for get_complete_cb
396 * Returns: Pointer to a private data structure or %NULL on failure
397 */
398void * eap_sim_db_init(const char *config,
399		       void (*get_complete_cb)(void *ctx, void *session_ctx),
400		       void *ctx)
401{
402	struct eap_sim_db_data *data;
403
404	data = os_zalloc(sizeof(*data));
405	if (data == NULL)
406		return NULL;
407
408	data->sock = -1;
409	data->get_complete_cb = get_complete_cb;
410	data->ctx = ctx;
411	data->fname = os_strdup(config);
412	if (data->fname == NULL)
413		goto fail;
414
415	if (os_strncmp(data->fname, "unix:", 5) == 0) {
416		if (eap_sim_db_open_socket(data))
417			goto fail;
418	}
419
420	return data;
421
422fail:
423	eap_sim_db_close_socket(data);
424	os_free(data->fname);
425	os_free(data);
426	return NULL;
427}
428
429
430static void eap_sim_db_free_pseudonym(struct eap_sim_pseudonym *p)
431{
432	os_free(p->identity);
433	os_free(p->pseudonym);
434	os_free(p);
435}
436
437
438static void eap_sim_db_free_reauth(struct eap_sim_reauth *r)
439{
440	os_free(r->identity);
441	os_free(r->reauth_id);
442	os_free(r);
443}
444
445
446/**
447 * eap_sim_db_deinit - Deinitialize EAP-SIM DB/authentication gw interface
448 * @priv: Private data pointer from eap_sim_db_init()
449 */
450void eap_sim_db_deinit(void *priv)
451{
452	struct eap_sim_db_data *data = priv;
453	struct eap_sim_pseudonym *p, *prev;
454	struct eap_sim_reauth *r, *prevr;
455	struct eap_sim_db_pending *pending, *prev_pending;
456
457	eap_sim_db_close_socket(data);
458	os_free(data->fname);
459
460	p = data->pseudonyms;
461	while (p) {
462		prev = p;
463		p = p->next;
464		eap_sim_db_free_pseudonym(prev);
465	}
466
467	r = data->reauths;
468	while (r) {
469		prevr = r;
470		r = r->next;
471		eap_sim_db_free_reauth(prevr);
472	}
473
474	pending = data->pending;
475	while (pending) {
476		prev_pending = pending;
477		pending = pending->next;
478		os_free(prev_pending);
479	}
480
481	os_free(data);
482}
483
484
485static int eap_sim_db_send(struct eap_sim_db_data *data, const char *msg,
486			   size_t len)
487{
488	int _errno = 0;
489
490	if (send(data->sock, msg, len, 0) < 0) {
491		_errno = errno;
492		perror("send[EAP-SIM DB UNIX]");
493	}
494
495	if (_errno == ENOTCONN || _errno == EDESTADDRREQ || _errno == EINVAL ||
496	    _errno == ECONNREFUSED) {
497		/* Try to reconnect */
498		eap_sim_db_close_socket(data);
499		if (eap_sim_db_open_socket(data) < 0)
500			return -1;
501		wpa_printf(MSG_DEBUG, "EAP-SIM DB: Reconnected to the "
502			   "external server");
503		if (send(data->sock, msg, len, 0) < 0) {
504			perror("send[EAP-SIM DB UNIX]");
505			return -1;
506		}
507	}
508
509	return 0;
510}
511
512
513static void eap_sim_db_expire_pending(struct eap_sim_db_data *data)
514{
515	/* TODO: add limit for maximum length for pending list; remove latest
516	 * (i.e., last) entry from the list if the limit is reached; could also
517	 * use timeout to expire pending entries */
518}
519
520
521/**
522 * eap_sim_db_get_gsm_triplets - Get GSM triplets
523 * @priv: Private data pointer from eap_sim_db_init()
524 * @identity: User name identity
525 * @identity_len: Length of identity in bytes
526 * @max_chal: Maximum number of triplets
527 * @_rand: Buffer for RAND values
528 * @kc: Buffer for Kc values
529 * @sres: Buffer for SRES values
530 * @cb_session_ctx: Session callback context for get_complete_cb()
531 * Returns: Number of triplets received (has to be less than or equal to
532 * max_chal), -1 (EAP_SIM_DB_FAILURE) on error (e.g., user not found), or
533 * -2 (EAP_SIM_DB_PENDING) if results are not yet available. In this case, the
534 * callback function registered with eap_sim_db_init() will be called once the
535 * results become available.
536 *
537 * In most cases, the user name is '1' | IMSI, i.e., 1 followed by the IMSI in
538 * ASCII format.
539 *
540 * When using an external server for GSM triplets, this function can always
541 * start a request and return EAP_SIM_DB_PENDING immediately if authentication
542 * triplets are not available. Once the triplets are received, callback
543 * function registered with eap_sim_db_init() is called to notify EAP state
544 * machine to reprocess the message. This eap_sim_db_get_gsm_triplets()
545 * function will then be called again and the newly received triplets will then
546 * be given to the caller.
547 */
548int eap_sim_db_get_gsm_triplets(void *priv, const u8 *identity,
549				size_t identity_len, int max_chal,
550				u8 *_rand, u8 *kc, u8 *sres,
551				void *cb_session_ctx)
552{
553	struct eap_sim_db_data *data = priv;
554	struct eap_sim_db_pending *entry;
555	int len, ret;
556	size_t i;
557	char msg[40];
558
559	if (identity_len < 2 || identity[0] != EAP_SIM_PERMANENT_PREFIX) {
560		wpa_hexdump_ascii(MSG_DEBUG, "EAP-SIM DB: unexpected identity",
561				  identity, identity_len);
562		return EAP_SIM_DB_FAILURE;
563	}
564	identity++;
565	identity_len--;
566	for (i = 0; i < identity_len; i++) {
567		if (identity[i] == '@') {
568			identity_len = i;
569			break;
570		}
571	}
572	if (identity_len + 1 > sizeof(entry->imsi)) {
573		wpa_hexdump_ascii(MSG_DEBUG, "EAP-SIM DB: unexpected identity",
574				  identity, identity_len);
575		return EAP_SIM_DB_FAILURE;
576	}
577	wpa_hexdump_ascii(MSG_DEBUG, "EAP-SIM DB: Get GSM triplets for IMSI",
578			  identity, identity_len);
579
580	entry = eap_sim_db_get_pending(data, identity, identity_len, 0);
581	if (entry) {
582		int num_chal;
583		if (entry->state == FAILURE) {
584			wpa_printf(MSG_DEBUG, "EAP-SIM DB: Pending entry -> "
585				   "failure");
586			os_free(entry);
587			return EAP_SIM_DB_FAILURE;
588		}
589
590		if (entry->state == PENDING) {
591			wpa_printf(MSG_DEBUG, "EAP-SIM DB: Pending entry -> "
592				   "still pending");
593			eap_sim_db_add_pending(data, entry);
594			return EAP_SIM_DB_PENDING;
595		}
596
597		wpa_printf(MSG_DEBUG, "EAP-SIM DB: Pending entry -> "
598			   "%d challenges", entry->u.sim.num_chal);
599		num_chal = entry->u.sim.num_chal;
600		if (num_chal > max_chal)
601			num_chal = max_chal;
602		os_memcpy(_rand, entry->u.sim.rand, num_chal * GSM_RAND_LEN);
603		os_memcpy(sres, entry->u.sim.sres,
604			  num_chal * EAP_SIM_SRES_LEN);
605		os_memcpy(kc, entry->u.sim.kc, num_chal * EAP_SIM_KC_LEN);
606		os_free(entry);
607		return num_chal;
608	}
609
610	if (data->sock < 0) {
611		if (eap_sim_db_open_socket(data) < 0)
612			return EAP_SIM_DB_FAILURE;
613	}
614
615	len = os_snprintf(msg, sizeof(msg), "SIM-REQ-AUTH ");
616	if (len < 0 || len + identity_len >= sizeof(msg))
617		return EAP_SIM_DB_FAILURE;
618	os_memcpy(msg + len, identity, identity_len);
619	len += identity_len;
620	ret = os_snprintf(msg + len, sizeof(msg) - len, " %d", max_chal);
621	if (ret < 0 || (size_t) ret >= sizeof(msg) - len)
622		return EAP_SIM_DB_FAILURE;
623	len += ret;
624
625	wpa_hexdump(MSG_DEBUG, "EAP-SIM DB: requesting SIM authentication "
626		    "data for IMSI", identity, identity_len);
627	if (eap_sim_db_send(data, msg, len) < 0)
628		return EAP_SIM_DB_FAILURE;
629
630	entry = os_zalloc(sizeof(*entry));
631	if (entry == NULL)
632		return EAP_SIM_DB_FAILURE;
633
634	os_get_time(&entry->timestamp);
635	os_memcpy(entry->imsi, identity, identity_len);
636	entry->imsi_len = identity_len;
637	entry->cb_session_ctx = cb_session_ctx;
638	entry->state = PENDING;
639	eap_sim_db_add_pending(data, entry);
640	eap_sim_db_expire_pending(data);
641
642	return EAP_SIM_DB_PENDING;
643}
644
645
646static struct eap_sim_pseudonym *
647eap_sim_db_get_pseudonym(struct eap_sim_db_data *data, const u8 *identity,
648			 size_t identity_len)
649{
650	char *pseudonym;
651	size_t len;
652	struct eap_sim_pseudonym *p;
653
654	if (identity_len == 0 ||
655	    (identity[0] != EAP_SIM_PSEUDONYM_PREFIX &&
656	     identity[0] != EAP_AKA_PSEUDONYM_PREFIX))
657		return NULL;
658
659	/* Remove possible realm from identity */
660	len = 0;
661	while (len < identity_len) {
662		if (identity[len] == '@')
663			break;
664		len++;
665	}
666
667	pseudonym = os_malloc(len + 1);
668	if (pseudonym == NULL)
669		return NULL;
670	os_memcpy(pseudonym, identity, len);
671	pseudonym[len] = '\0';
672
673	p = data->pseudonyms;
674	while (p) {
675		if (os_strcmp(p->pseudonym, pseudonym) == 0)
676			break;
677		p = p->next;
678	}
679
680	os_free(pseudonym);
681
682	return p;
683}
684
685
686static struct eap_sim_pseudonym *
687eap_sim_db_get_pseudonym_id(struct eap_sim_db_data *data, const u8 *identity,
688			    size_t identity_len)
689{
690	struct eap_sim_pseudonym *p;
691
692	if (identity_len == 0 ||
693	    (identity[0] != EAP_SIM_PERMANENT_PREFIX &&
694	     identity[0] != EAP_AKA_PERMANENT_PREFIX))
695		return NULL;
696
697	p = data->pseudonyms;
698	while (p) {
699		if (identity_len == p->identity_len &&
700		    os_memcmp(p->identity, identity, identity_len) == 0)
701			break;
702		p = p->next;
703	}
704
705	return p;
706}
707
708
709static struct eap_sim_reauth *
710eap_sim_db_get_reauth(struct eap_sim_db_data *data, const u8 *identity,
711		      size_t identity_len)
712{
713	char *reauth_id;
714	size_t len;
715	struct eap_sim_reauth *r;
716
717	if (identity_len == 0 ||
718	    (identity[0] != EAP_SIM_REAUTH_ID_PREFIX &&
719	     identity[0] != EAP_AKA_REAUTH_ID_PREFIX))
720		return NULL;
721
722	/* Remove possible realm from identity */
723	len = 0;
724	while (len < identity_len) {
725		if (identity[len] == '@')
726			break;
727		len++;
728	}
729
730	reauth_id = os_malloc(len + 1);
731	if (reauth_id == NULL)
732		return NULL;
733	os_memcpy(reauth_id, identity, len);
734	reauth_id[len] = '\0';
735
736	r = data->reauths;
737	while (r) {
738		if (os_strcmp(r->reauth_id, reauth_id) == 0)
739			break;
740		r = r->next;
741	}
742
743	os_free(reauth_id);
744
745	return r;
746}
747
748
749static struct eap_sim_reauth *
750eap_sim_db_get_reauth_id(struct eap_sim_db_data *data, const u8 *identity,
751			 size_t identity_len)
752{
753	struct eap_sim_pseudonym *p;
754	struct eap_sim_reauth *r;
755
756	if (identity_len == 0)
757		return NULL;
758
759	p = eap_sim_db_get_pseudonym(data, identity, identity_len);
760	if (p == NULL)
761		p = eap_sim_db_get_pseudonym_id(data, identity, identity_len);
762	if (p) {
763		identity = p->identity;
764		identity_len = p->identity_len;
765	}
766
767	r = data->reauths;
768	while (r) {
769		if (identity_len == r->identity_len &&
770		    os_memcmp(r->identity, identity, identity_len) == 0)
771			break;
772		r = r->next;
773	}
774
775	return r;
776}
777
778
779/**
780 * eap_sim_db_identity_known - Verify whether the given identity is known
781 * @priv: Private data pointer from eap_sim_db_init()
782 * @identity: User name identity
783 * @identity_len: Length of identity in bytes
784 * Returns: 0 if the user is found or -1 on failure
785 *
786 * In most cases, the user name is ['0','1'] | IMSI, i.e., 1 followed by the
787 * IMSI in ASCII format, ['2','3'] | pseudonym, or ['4','5'] | reauth_id.
788 */
789int eap_sim_db_identity_known(void *priv, const u8 *identity,
790			      size_t identity_len)
791{
792	struct eap_sim_db_data *data = priv;
793
794	if (identity == NULL || identity_len < 2)
795		return -1;
796
797	if (identity[0] == EAP_SIM_PSEUDONYM_PREFIX ||
798	    identity[0] == EAP_AKA_PSEUDONYM_PREFIX) {
799		struct eap_sim_pseudonym *p =
800			eap_sim_db_get_pseudonym(data, identity, identity_len);
801		return p ? 0 : -1;
802	}
803
804	if (identity[0] == EAP_SIM_REAUTH_ID_PREFIX ||
805	    identity[0] == EAP_AKA_REAUTH_ID_PREFIX) {
806		struct eap_sim_reauth *r =
807			eap_sim_db_get_reauth(data, identity, identity_len);
808		return r ? 0 : -1;
809	}
810
811	if (identity[0] != EAP_SIM_PERMANENT_PREFIX &&
812	    identity[0] != EAP_AKA_PERMANENT_PREFIX) {
813		/* Unknown identity prefix */
814		return -1;
815	}
816
817	/* TODO: Should consider asking HLR/AuC gateway whether this permanent
818	 * identity is known. If it is, EAP-SIM/AKA can skip identity request.
819	 * In case of EAP-AKA, this would reduce number of needed round-trips.
820	 * Ideally, this would be done with one wait, i.e., just request
821	 * authentication data and store it for the next use. This would then
822	 * need to use similar pending-request functionality as the normal
823	 * request for authentication data at later phase.
824	 */
825	return -1;
826}
827
828
829static char * eap_sim_db_get_next(struct eap_sim_db_data *data, char prefix)
830{
831	char *id, *pos, *end;
832	u8 buf[10];
833
834	if (random_get_bytes(buf, sizeof(buf)))
835		return NULL;
836	id = os_malloc(sizeof(buf) * 2 + 2);
837	if (id == NULL)
838		return NULL;
839
840	pos = id;
841	end = id + sizeof(buf) * 2 + 2;
842	*pos++ = prefix;
843	pos += wpa_snprintf_hex(pos, end - pos, buf, sizeof(buf));
844
845	return id;
846}
847
848
849/**
850 * eap_sim_db_get_next_pseudonym - EAP-SIM DB: Get next pseudonym
851 * @priv: Private data pointer from eap_sim_db_init()
852 * @aka: Using EAP-AKA instead of EAP-SIM
853 * Returns: Next pseudonym (allocated string) or %NULL on failure
854 *
855 * This function is used to generate a pseudonym for EAP-SIM. The returned
856 * pseudonym is not added to database at this point; it will need to be added
857 * with eap_sim_db_add_pseudonym() once the authentication has been completed
858 * successfully. Caller is responsible for freeing the returned buffer.
859 */
860char * eap_sim_db_get_next_pseudonym(void *priv, int aka)
861{
862	struct eap_sim_db_data *data = priv;
863	return eap_sim_db_get_next(data, aka ? EAP_AKA_PSEUDONYM_PREFIX :
864				   EAP_SIM_PSEUDONYM_PREFIX);
865}
866
867
868/**
869 * eap_sim_db_get_next_reauth_id - EAP-SIM DB: Get next reauth_id
870 * @priv: Private data pointer from eap_sim_db_init()
871 * @aka: Using EAP-AKA instead of EAP-SIM
872 * Returns: Next reauth_id (allocated string) or %NULL on failure
873 *
874 * This function is used to generate a fast re-authentication identity for
875 * EAP-SIM. The returned reauth_id is not added to database at this point; it
876 * will need to be added with eap_sim_db_add_reauth() once the authentication
877 * has been completed successfully. Caller is responsible for freeing the
878 * returned buffer.
879 */
880char * eap_sim_db_get_next_reauth_id(void *priv, int aka)
881{
882	struct eap_sim_db_data *data = priv;
883	return eap_sim_db_get_next(data, aka ? EAP_AKA_REAUTH_ID_PREFIX :
884				   EAP_SIM_REAUTH_ID_PREFIX);
885}
886
887
888/**
889 * eap_sim_db_add_pseudonym - EAP-SIM DB: Add new pseudonym
890 * @priv: Private data pointer from eap_sim_db_init()
891 * @identity: Identity of the user (may be permanent identity or pseudonym)
892 * @identity_len: Length of identity
893 * @pseudonym: Pseudonym for this user. This needs to be an allocated buffer,
894 * e.g., return value from eap_sim_db_get_next_pseudonym(). Caller must not
895 * free it.
896 * Returns: 0 on success, -1 on failure
897 *
898 * This function adds a new pseudonym for EAP-SIM user. EAP-SIM DB is
899 * responsible of freeing pseudonym buffer once it is not needed anymore.
900 */
901int eap_sim_db_add_pseudonym(void *priv, const u8 *identity,
902			     size_t identity_len, char *pseudonym)
903{
904	struct eap_sim_db_data *data = priv;
905	struct eap_sim_pseudonym *p;
906	wpa_hexdump_ascii(MSG_DEBUG, "EAP-SIM DB: Add pseudonym for identity",
907			  identity, identity_len);
908	wpa_printf(MSG_DEBUG, "EAP-SIM DB: Pseudonym: %s", pseudonym);
909
910	/* TODO: could store last two pseudonyms */
911	p = eap_sim_db_get_pseudonym(data, identity, identity_len);
912	if (p == NULL)
913		p = eap_sim_db_get_pseudonym_id(data, identity, identity_len);
914
915	if (p) {
916		wpa_printf(MSG_DEBUG, "EAP-SIM DB: Replacing previous "
917			   "pseudonym: %s", p->pseudonym);
918		os_free(p->pseudonym);
919		p->pseudonym = pseudonym;
920		return 0;
921	}
922
923	p = os_zalloc(sizeof(*p));
924	if (p == NULL) {
925		os_free(pseudonym);
926		return -1;
927	}
928
929	p->next = data->pseudonyms;
930	p->identity = os_malloc(identity_len);
931	if (p->identity == NULL) {
932		os_free(p);
933		os_free(pseudonym);
934		return -1;
935	}
936	os_memcpy(p->identity, identity, identity_len);
937	p->identity_len = identity_len;
938	p->pseudonym = pseudonym;
939	data->pseudonyms = p;
940
941	wpa_printf(MSG_DEBUG, "EAP-SIM DB: Added new pseudonym entry");
942	return 0;
943}
944
945
946static struct eap_sim_reauth *
947eap_sim_db_add_reauth_data(struct eap_sim_db_data *data, const u8 *identity,
948			   size_t identity_len, char *reauth_id, u16 counter)
949{
950	struct eap_sim_reauth *r;
951
952	wpa_hexdump_ascii(MSG_DEBUG, "EAP-SIM DB: Add reauth_id for identity",
953			  identity, identity_len);
954	wpa_printf(MSG_DEBUG, "EAP-SIM DB: reauth_id: %s", reauth_id);
955
956	r = eap_sim_db_get_reauth(data, identity, identity_len);
957	if (r == NULL)
958		r = eap_sim_db_get_reauth_id(data, identity, identity_len);
959
960	if (r) {
961		wpa_printf(MSG_DEBUG, "EAP-SIM DB: Replacing previous "
962			   "reauth_id: %s", r->reauth_id);
963		os_free(r->reauth_id);
964		r->reauth_id = reauth_id;
965	} else {
966		r = os_zalloc(sizeof(*r));
967		if (r == NULL) {
968			os_free(reauth_id);
969			return NULL;
970		}
971
972		r->next = data->reauths;
973		r->identity = os_malloc(identity_len);
974		if (r->identity == NULL) {
975			os_free(r);
976			os_free(reauth_id);
977			return NULL;
978		}
979		os_memcpy(r->identity, identity, identity_len);
980		r->identity_len = identity_len;
981		r->reauth_id = reauth_id;
982		data->reauths = r;
983		wpa_printf(MSG_DEBUG, "EAP-SIM DB: Added new reauth entry");
984	}
985
986	r->counter = counter;
987
988	return r;
989}
990
991
992/**
993 * eap_sim_db_add_reauth - EAP-SIM DB: Add new re-authentication entry
994 * @priv: Private data pointer from eap_sim_db_init()
995 * @identity: Identity of the user (may be permanent identity or pseudonym)
996 * @identity_len: Length of identity
997 * @reauth_id: reauth_id for this user. This needs to be an allocated buffer,
998 * e.g., return value from eap_sim_db_get_next_reauth_id(). Caller must not
999 * free it.
1000 * @counter: AT_COUNTER value for fast re-authentication
1001 * @mk: 16-byte MK from the previous full authentication or %NULL
1002 * Returns: 0 on success, -1 on failure
1003 *
1004 * This function adds a new re-authentication entry for an EAP-SIM user.
1005 * EAP-SIM DB is responsible of freeing reauth_id buffer once it is not needed
1006 * anymore.
1007 */
1008int eap_sim_db_add_reauth(void *priv, const u8 *identity,
1009			  size_t identity_len, char *reauth_id, u16 counter,
1010			  const u8 *mk)
1011{
1012	struct eap_sim_db_data *data = priv;
1013	struct eap_sim_reauth *r;
1014
1015	r = eap_sim_db_add_reauth_data(data, identity, identity_len, reauth_id,
1016				       counter);
1017	if (r == NULL)
1018		return -1;
1019
1020	os_memcpy(r->mk, mk, EAP_SIM_MK_LEN);
1021	r->aka_prime = 0;
1022
1023	return 0;
1024}
1025
1026
1027#ifdef EAP_SERVER_AKA_PRIME
1028/**
1029 * eap_sim_db_add_reauth_prime - EAP-AKA' DB: Add new re-authentication entry
1030 * @priv: Private data pointer from eap_sim_db_init()
1031 * @identity: Identity of the user (may be permanent identity or pseudonym)
1032 * @identity_len: Length of identity
1033 * @reauth_id: reauth_id for this user. This needs to be an allocated buffer,
1034 * e.g., return value from eap_sim_db_get_next_reauth_id(). Caller must not
1035 * free it.
1036 * @counter: AT_COUNTER value for fast re-authentication
1037 * @k_encr: K_encr from the previous full authentication
1038 * @k_aut: K_aut from the previous full authentication
1039 * @k_re: 32-byte K_re from the previous full authentication
1040 * Returns: 0 on success, -1 on failure
1041 *
1042 * This function adds a new re-authentication entry for an EAP-AKA' user.
1043 * EAP-SIM DB is responsible of freeing reauth_id buffer once it is not needed
1044 * anymore.
1045 */
1046int eap_sim_db_add_reauth_prime(void *priv, const u8 *identity,
1047				size_t identity_len, char *reauth_id,
1048				u16 counter, const u8 *k_encr, const u8 *k_aut,
1049				const u8 *k_re)
1050{
1051	struct eap_sim_db_data *data = priv;
1052	struct eap_sim_reauth *r;
1053
1054	r = eap_sim_db_add_reauth_data(data, identity, identity_len, reauth_id,
1055				       counter);
1056	if (r == NULL)
1057		return -1;
1058
1059	r->aka_prime = 1;
1060	os_memcpy(r->k_encr, k_encr, EAP_SIM_K_ENCR_LEN);
1061	os_memcpy(r->k_aut, k_aut, EAP_AKA_PRIME_K_AUT_LEN);
1062	os_memcpy(r->k_re, k_re, EAP_AKA_PRIME_K_RE_LEN);
1063
1064	return 0;
1065}
1066#endif /* EAP_SERVER_AKA_PRIME */
1067
1068
1069/**
1070 * eap_sim_db_get_permanent - EAP-SIM DB: Get permanent identity
1071 * @priv: Private data pointer from eap_sim_db_init()
1072 * @identity: Identity of the user (may be permanent identity or pseudonym)
1073 * @identity_len: Length of identity
1074 * @len: Buffer for length of the returned permanent identity
1075 * Returns: Pointer to the permanent identity, or %NULL if not found
1076 */
1077const u8 * eap_sim_db_get_permanent(void *priv, const u8 *identity,
1078				    size_t identity_len, size_t *len)
1079{
1080	struct eap_sim_db_data *data = priv;
1081	struct eap_sim_pseudonym *p;
1082
1083	if (identity == NULL)
1084		return NULL;
1085
1086	p = eap_sim_db_get_pseudonym(data, identity, identity_len);
1087	if (p == NULL)
1088		p = eap_sim_db_get_pseudonym_id(data, identity, identity_len);
1089	if (p == NULL)
1090		return NULL;
1091
1092	*len = p->identity_len;
1093	return p->identity;
1094}
1095
1096
1097/**
1098 * eap_sim_db_get_reauth_entry - EAP-SIM DB: Get re-authentication entry
1099 * @priv: Private data pointer from eap_sim_db_init()
1100 * @identity: Identity of the user (may be permanent identity, pseudonym, or
1101 * reauth_id)
1102 * @identity_len: Length of identity
1103 * Returns: Pointer to the re-auth entry, or %NULL if not found
1104 */
1105struct eap_sim_reauth *
1106eap_sim_db_get_reauth_entry(void *priv, const u8 *identity,
1107			    size_t identity_len)
1108{
1109	struct eap_sim_db_data *data = priv;
1110	struct eap_sim_reauth *r;
1111
1112	if (identity == NULL)
1113		return NULL;
1114	r = eap_sim_db_get_reauth(data, identity, identity_len);
1115	if (r == NULL)
1116		r = eap_sim_db_get_reauth_id(data, identity, identity_len);
1117	return r;
1118}
1119
1120
1121/**
1122 * eap_sim_db_remove_reauth - EAP-SIM DB: Remove re-authentication entry
1123 * @priv: Private data pointer from eap_sim_db_init()
1124 * @reauth: Pointer to re-authentication entry from
1125 * eap_sim_db_get_reauth_entry()
1126 */
1127void eap_sim_db_remove_reauth(void *priv, struct eap_sim_reauth *reauth)
1128{
1129	struct eap_sim_db_data *data = priv;
1130	struct eap_sim_reauth *r, *prev = NULL;
1131	r = data->reauths;
1132	while (r) {
1133		if (r == reauth) {
1134			if (prev)
1135				prev->next = r->next;
1136			else
1137				data->reauths = r->next;
1138			eap_sim_db_free_reauth(r);
1139			return;
1140		}
1141		prev = r;
1142		r = r->next;
1143	}
1144}
1145
1146
1147/**
1148 * eap_sim_db_get_aka_auth - Get AKA authentication values
1149 * @priv: Private data pointer from eap_sim_db_init()
1150 * @identity: User name identity
1151 * @identity_len: Length of identity in bytes
1152 * @_rand: Buffer for RAND value
1153 * @autn: Buffer for AUTN value
1154 * @ik: Buffer for IK value
1155 * @ck: Buffer for CK value
1156 * @res: Buffer for RES value
1157 * @res_len: Buffer for RES length
1158 * @cb_session_ctx: Session callback context for get_complete_cb()
1159 * Returns: 0 on success, -1 (EAP_SIM_DB_FAILURE) on error (e.g., user not
1160 * found), or -2 (EAP_SIM_DB_PENDING) if results are not yet available. In this
1161 * case, the callback function registered with eap_sim_db_init() will be
1162 * called once the results become available.
1163 *
1164 * In most cases, the user name is '0' | IMSI, i.e., 0 followed by the IMSI in
1165 * ASCII format.
1166 *
1167 * When using an external server for AKA authentication, this function can
1168 * always start a request and return EAP_SIM_DB_PENDING immediately if
1169 * authentication triplets are not available. Once the authentication data are
1170 * received, callback function registered with eap_sim_db_init() is called to
1171 * notify EAP state machine to reprocess the message. This
1172 * eap_sim_db_get_aka_auth() function will then be called again and the newly
1173 * received triplets will then be given to the caller.
1174 */
1175int eap_sim_db_get_aka_auth(void *priv, const u8 *identity,
1176			    size_t identity_len, u8 *_rand, u8 *autn, u8 *ik,
1177			    u8 *ck, u8 *res, size_t *res_len,
1178			    void *cb_session_ctx)
1179{
1180	struct eap_sim_db_data *data = priv;
1181	struct eap_sim_db_pending *entry;
1182	int len;
1183	size_t i;
1184	char msg[40];
1185
1186	if (identity_len < 2 || identity == NULL ||
1187	    identity[0] != EAP_AKA_PERMANENT_PREFIX) {
1188		wpa_hexdump_ascii(MSG_DEBUG, "EAP-SIM DB: unexpected identity",
1189				  identity, identity_len);
1190		return EAP_SIM_DB_FAILURE;
1191	}
1192	identity++;
1193	identity_len--;
1194	for (i = 0; i < identity_len; i++) {
1195		if (identity[i] == '@') {
1196			identity_len = i;
1197			break;
1198		}
1199	}
1200	if (identity_len + 1 > sizeof(entry->imsi)) {
1201		wpa_hexdump_ascii(MSG_DEBUG, "EAP-SIM DB: unexpected identity",
1202				  identity, identity_len);
1203		return EAP_SIM_DB_FAILURE;
1204	}
1205	wpa_hexdump_ascii(MSG_DEBUG, "EAP-SIM DB: Get AKA auth for IMSI",
1206			  identity, identity_len);
1207
1208	entry = eap_sim_db_get_pending(data, identity, identity_len, 1);
1209	if (entry) {
1210		if (entry->state == FAILURE) {
1211			os_free(entry);
1212			wpa_printf(MSG_DEBUG, "EAP-SIM DB: Failure");
1213			return EAP_SIM_DB_FAILURE;
1214		}
1215
1216		if (entry->state == PENDING) {
1217			eap_sim_db_add_pending(data, entry);
1218			wpa_printf(MSG_DEBUG, "EAP-SIM DB: Pending");
1219			return EAP_SIM_DB_PENDING;
1220		}
1221
1222		wpa_printf(MSG_DEBUG, "EAP-SIM DB: Returning successfully "
1223			   "received authentication data");
1224		os_memcpy(_rand, entry->u.aka.rand, EAP_AKA_RAND_LEN);
1225		os_memcpy(autn, entry->u.aka.autn, EAP_AKA_AUTN_LEN);
1226		os_memcpy(ik, entry->u.aka.ik, EAP_AKA_IK_LEN);
1227		os_memcpy(ck, entry->u.aka.ck, EAP_AKA_CK_LEN);
1228		os_memcpy(res, entry->u.aka.res, EAP_AKA_RES_MAX_LEN);
1229		*res_len = entry->u.aka.res_len;
1230		os_free(entry);
1231		return 0;
1232	}
1233
1234	if (data->sock < 0) {
1235		if (eap_sim_db_open_socket(data) < 0)
1236			return EAP_SIM_DB_FAILURE;
1237	}
1238
1239	len = os_snprintf(msg, sizeof(msg), "AKA-REQ-AUTH ");
1240	if (len < 0 || len + identity_len >= sizeof(msg))
1241		return EAP_SIM_DB_FAILURE;
1242	os_memcpy(msg + len, identity, identity_len);
1243	len += identity_len;
1244
1245	wpa_hexdump(MSG_DEBUG, "EAP-SIM DB: requesting AKA authentication "
1246		    "data for IMSI", identity, identity_len);
1247	if (eap_sim_db_send(data, msg, len) < 0)
1248		return EAP_SIM_DB_FAILURE;
1249
1250	entry = os_zalloc(sizeof(*entry));
1251	if (entry == NULL)
1252		return EAP_SIM_DB_FAILURE;
1253
1254	os_get_time(&entry->timestamp);
1255	entry->aka = 1;
1256	os_memcpy(entry->imsi, identity, identity_len);
1257	entry->imsi_len = identity_len;
1258	entry->cb_session_ctx = cb_session_ctx;
1259	entry->state = PENDING;
1260	eap_sim_db_add_pending(data, entry);
1261	eap_sim_db_expire_pending(data);
1262
1263	return EAP_SIM_DB_PENDING;
1264}
1265
1266
1267/**
1268 * eap_sim_db_resynchronize - Resynchronize AKA AUTN
1269 * @priv: Private data pointer from eap_sim_db_init()
1270 * @identity: User name identity
1271 * @identity_len: Length of identity in bytes
1272 * @auts: AUTS value from the peer
1273 * @_rand: RAND value used in the rejected message
1274 * Returns: 0 on success, -1 on failure
1275 *
1276 * This function is called when the peer reports synchronization failure in the
1277 * AUTN value by sending AUTS. The AUTS and RAND values should be sent to
1278 * HLR/AuC to allow it to resynchronize with the peer. After this,
1279 * eap_sim_db_get_aka_auth() will be called again to to fetch updated
1280 * RAND/AUTN values for the next challenge.
1281 */
1282int eap_sim_db_resynchronize(void *priv, const u8 *identity,
1283			     size_t identity_len, const u8 *auts,
1284			     const u8 *_rand)
1285{
1286	struct eap_sim_db_data *data = priv;
1287	size_t i;
1288
1289	if (identity_len < 2 || identity == NULL ||
1290	    identity[0] != EAP_AKA_PERMANENT_PREFIX) {
1291		wpa_hexdump_ascii(MSG_DEBUG, "EAP-SIM DB: unexpected identity",
1292				  identity, identity_len);
1293		return -1;
1294	}
1295	identity++;
1296	identity_len--;
1297	for (i = 0; i < identity_len; i++) {
1298		if (identity[i] == '@') {
1299			identity_len = i;
1300			break;
1301		}
1302	}
1303	if (identity_len > 20) {
1304		wpa_hexdump_ascii(MSG_DEBUG, "EAP-SIM DB: unexpected identity",
1305				  identity, identity_len);
1306		return -1;
1307	}
1308
1309	if (data->sock >= 0) {
1310		char msg[100];
1311		int len, ret;
1312
1313		len = os_snprintf(msg, sizeof(msg), "AKA-AUTS ");
1314		if (len < 0 || len + identity_len >= sizeof(msg))
1315			return -1;
1316		os_memcpy(msg + len, identity, identity_len);
1317		len += identity_len;
1318
1319		ret = os_snprintf(msg + len, sizeof(msg) - len, " ");
1320		if (ret < 0 || (size_t) ret >= sizeof(msg) - len)
1321			return -1;
1322		len += ret;
1323		len += wpa_snprintf_hex(msg + len, sizeof(msg) - len,
1324					auts, EAP_AKA_AUTS_LEN);
1325		ret = os_snprintf(msg + len, sizeof(msg) - len, " ");
1326		if (ret < 0 || (size_t) ret >= sizeof(msg) - len)
1327			return -1;
1328		len += ret;
1329		len += wpa_snprintf_hex(msg + len, sizeof(msg) - len,
1330					_rand, EAP_AKA_RAND_LEN);
1331		wpa_hexdump(MSG_DEBUG, "EAP-SIM DB: reporting AKA AUTS for "
1332			    "IMSI", identity, identity_len);
1333		if (eap_sim_db_send(data, msg, len) < 0)
1334			return -1;
1335	}
1336
1337	return 0;
1338}
1339