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