eap_sim_db.c revision 04949598a23f501be6eec21697465fd46a28840a
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	data->local_sock = os_strdup(addr.sun_path);
344	if (bind(data->sock, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
345		perror("bind(eap_sim_db)");
346		close(data->sock);
347		data->sock = -1;
348		return -1;
349	}
350
351	os_memset(&addr, 0, sizeof(addr));
352	addr.sun_family = AF_UNIX;
353	os_strlcpy(addr.sun_path, data->fname + 5, sizeof(addr.sun_path));
354	if (connect(data->sock, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
355		perror("connect(eap_sim_db)");
356		wpa_hexdump_ascii(MSG_INFO, "HLR/AuC GW socket",
357				  (u8 *) addr.sun_path,
358				  os_strlen(addr.sun_path));
359		close(data->sock);
360		data->sock = -1;
361		return -1;
362	}
363
364	eloop_register_read_sock(data->sock, eap_sim_db_receive, data, NULL);
365
366	return 0;
367}
368
369
370static void eap_sim_db_close_socket(struct eap_sim_db_data *data)
371{
372	if (data->sock >= 0) {
373		eloop_unregister_read_sock(data->sock);
374		close(data->sock);
375		data->sock = -1;
376	}
377	if (data->local_sock) {
378		unlink(data->local_sock);
379		os_free(data->local_sock);
380		data->local_sock = NULL;
381	}
382}
383
384
385/**
386 * eap_sim_db_init - Initialize EAP-SIM DB / authentication gateway interface
387 * @config: Configuration data (e.g., file name)
388 * @get_complete_cb: Callback function for reporting availability of triplets
389 * @ctx: Context pointer for get_complete_cb
390 * Returns: Pointer to a private data structure or %NULL on failure
391 */
392void * eap_sim_db_init(const char *config,
393		       void (*get_complete_cb)(void *ctx, void *session_ctx),
394		       void *ctx)
395{
396	struct eap_sim_db_data *data;
397
398	data = os_zalloc(sizeof(*data));
399	if (data == NULL)
400		return NULL;
401
402	data->sock = -1;
403	data->get_complete_cb = get_complete_cb;
404	data->ctx = ctx;
405	data->fname = os_strdup(config);
406	if (data->fname == NULL)
407		goto fail;
408
409	if (os_strncmp(data->fname, "unix:", 5) == 0) {
410		if (eap_sim_db_open_socket(data))
411			goto fail;
412	}
413
414	return data;
415
416fail:
417	eap_sim_db_close_socket(data);
418	os_free(data->fname);
419	os_free(data);
420	return NULL;
421}
422
423
424static void eap_sim_db_free_pseudonym(struct eap_sim_pseudonym *p)
425{
426	os_free(p->identity);
427	os_free(p->pseudonym);
428	os_free(p);
429}
430
431
432static void eap_sim_db_free_reauth(struct eap_sim_reauth *r)
433{
434	os_free(r->identity);
435	os_free(r->reauth_id);
436	os_free(r);
437}
438
439
440/**
441 * eap_sim_db_deinit - Deinitialize EAP-SIM DB/authentication gw interface
442 * @priv: Private data pointer from eap_sim_db_init()
443 */
444void eap_sim_db_deinit(void *priv)
445{
446	struct eap_sim_db_data *data = priv;
447	struct eap_sim_pseudonym *p, *prev;
448	struct eap_sim_reauth *r, *prevr;
449	struct eap_sim_db_pending *pending, *prev_pending;
450
451	eap_sim_db_close_socket(data);
452	os_free(data->fname);
453
454	p = data->pseudonyms;
455	while (p) {
456		prev = p;
457		p = p->next;
458		eap_sim_db_free_pseudonym(prev);
459	}
460
461	r = data->reauths;
462	while (r) {
463		prevr = r;
464		r = r->next;
465		eap_sim_db_free_reauth(prevr);
466	}
467
468	pending = data->pending;
469	while (pending) {
470		prev_pending = pending;
471		pending = pending->next;
472		os_free(prev_pending);
473	}
474
475	os_free(data);
476}
477
478
479static int eap_sim_db_send(struct eap_sim_db_data *data, const char *msg,
480			   size_t len)
481{
482	int _errno = 0;
483
484	if (send(data->sock, msg, len, 0) < 0) {
485		_errno = errno;
486		perror("send[EAP-SIM DB UNIX]");
487	}
488
489	if (_errno == ENOTCONN || _errno == EDESTADDRREQ || _errno == EINVAL ||
490	    _errno == ECONNREFUSED) {
491		/* Try to reconnect */
492		eap_sim_db_close_socket(data);
493		if (eap_sim_db_open_socket(data) < 0)
494			return -1;
495		wpa_printf(MSG_DEBUG, "EAP-SIM DB: Reconnected to the "
496			   "external server");
497		if (send(data->sock, msg, len, 0) < 0) {
498			perror("send[EAP-SIM DB UNIX]");
499			return -1;
500		}
501	}
502
503	return 0;
504}
505
506
507static void eap_sim_db_expire_pending(struct eap_sim_db_data *data)
508{
509	/* TODO: add limit for maximum length for pending list; remove latest
510	 * (i.e., last) entry from the list if the limit is reached; could also
511	 * use timeout to expire pending entries */
512}
513
514
515/**
516 * eap_sim_db_get_gsm_triplets - Get GSM triplets
517 * @priv: Private data pointer from eap_sim_db_init()
518 * @identity: User name identity
519 * @identity_len: Length of identity in bytes
520 * @max_chal: Maximum number of triplets
521 * @_rand: Buffer for RAND values
522 * @kc: Buffer for Kc values
523 * @sres: Buffer for SRES values
524 * @cb_session_ctx: Session callback context for get_complete_cb()
525 * Returns: Number of triplets received (has to be less than or equal to
526 * max_chal), -1 (EAP_SIM_DB_FAILURE) on error (e.g., user not found), or
527 * -2 (EAP_SIM_DB_PENDING) if results are not yet available. In this case, the
528 * callback function registered with eap_sim_db_init() will be called once the
529 * results become available.
530 *
531 * In most cases, the user name is '1' | IMSI, i.e., 1 followed by the IMSI in
532 * ASCII format.
533 *
534 * When using an external server for GSM triplets, this function can always
535 * start a request and return EAP_SIM_DB_PENDING immediately if authentication
536 * triplets are not available. Once the triplets are received, callback
537 * function registered with eap_sim_db_init() is called to notify EAP state
538 * machine to reprocess the message. This eap_sim_db_get_gsm_triplets()
539 * function will then be called again and the newly received triplets will then
540 * be given to the caller.
541 */
542int eap_sim_db_get_gsm_triplets(void *priv, const u8 *identity,
543				size_t identity_len, int max_chal,
544				u8 *_rand, u8 *kc, u8 *sres,
545				void *cb_session_ctx)
546{
547	struct eap_sim_db_data *data = priv;
548	struct eap_sim_db_pending *entry;
549	int len, ret;
550	size_t i;
551	char msg[40];
552
553	if (identity_len < 2 || identity[0] != EAP_SIM_PERMANENT_PREFIX) {
554		wpa_hexdump_ascii(MSG_DEBUG, "EAP-SIM DB: unexpected identity",
555				  identity, identity_len);
556		return EAP_SIM_DB_FAILURE;
557	}
558	identity++;
559	identity_len--;
560	for (i = 0; i < identity_len; i++) {
561		if (identity[i] == '@') {
562			identity_len = i;
563			break;
564		}
565	}
566	if (identity_len + 1 > sizeof(entry->imsi)) {
567		wpa_hexdump_ascii(MSG_DEBUG, "EAP-SIM DB: unexpected identity",
568				  identity, identity_len);
569		return EAP_SIM_DB_FAILURE;
570	}
571	wpa_hexdump_ascii(MSG_DEBUG, "EAP-SIM DB: Get GSM triplets for IMSI",
572			  identity, identity_len);
573
574	entry = eap_sim_db_get_pending(data, identity, identity_len, 0);
575	if (entry) {
576		int num_chal;
577		if (entry->state == FAILURE) {
578			wpa_printf(MSG_DEBUG, "EAP-SIM DB: Pending entry -> "
579				   "failure");
580			os_free(entry);
581			return EAP_SIM_DB_FAILURE;
582		}
583
584		if (entry->state == PENDING) {
585			wpa_printf(MSG_DEBUG, "EAP-SIM DB: Pending entry -> "
586				   "still pending");
587			eap_sim_db_add_pending(data, entry);
588			return EAP_SIM_DB_PENDING;
589		}
590
591		wpa_printf(MSG_DEBUG, "EAP-SIM DB: Pending entry -> "
592			   "%d challenges", entry->u.sim.num_chal);
593		num_chal = entry->u.sim.num_chal;
594		if (num_chal > max_chal)
595			num_chal = max_chal;
596		os_memcpy(_rand, entry->u.sim.rand, num_chal * GSM_RAND_LEN);
597		os_memcpy(sres, entry->u.sim.sres,
598			  num_chal * EAP_SIM_SRES_LEN);
599		os_memcpy(kc, entry->u.sim.kc, num_chal * EAP_SIM_KC_LEN);
600		os_free(entry);
601		return num_chal;
602	}
603
604	if (data->sock < 0) {
605		if (eap_sim_db_open_socket(data) < 0)
606			return EAP_SIM_DB_FAILURE;
607	}
608
609	len = os_snprintf(msg, sizeof(msg), "SIM-REQ-AUTH ");
610	if (len < 0 || len + identity_len >= sizeof(msg))
611		return EAP_SIM_DB_FAILURE;
612	os_memcpy(msg + len, identity, identity_len);
613	len += identity_len;
614	ret = os_snprintf(msg + len, sizeof(msg) - len, " %d", max_chal);
615	if (ret < 0 || (size_t) ret >= sizeof(msg) - len)
616		return EAP_SIM_DB_FAILURE;
617	len += ret;
618
619	wpa_hexdump(MSG_DEBUG, "EAP-SIM DB: requesting SIM authentication "
620		    "data for IMSI", identity, identity_len);
621	if (eap_sim_db_send(data, msg, len) < 0)
622		return EAP_SIM_DB_FAILURE;
623
624	entry = os_zalloc(sizeof(*entry));
625	if (entry == NULL)
626		return EAP_SIM_DB_FAILURE;
627
628	os_get_time(&entry->timestamp);
629	os_memcpy(entry->imsi, identity, identity_len);
630	entry->imsi_len = identity_len;
631	entry->cb_session_ctx = cb_session_ctx;
632	entry->state = PENDING;
633	eap_sim_db_add_pending(data, entry);
634	eap_sim_db_expire_pending(data);
635
636	return EAP_SIM_DB_PENDING;
637}
638
639
640static struct eap_sim_pseudonym *
641eap_sim_db_get_pseudonym(struct eap_sim_db_data *data, const u8 *identity,
642			 size_t identity_len)
643{
644	char *pseudonym;
645	size_t len;
646	struct eap_sim_pseudonym *p;
647
648	if (identity_len == 0 ||
649	    (identity[0] != EAP_SIM_PSEUDONYM_PREFIX &&
650	     identity[0] != EAP_AKA_PSEUDONYM_PREFIX &&
651	     identity[0] != EAP_AKA_PRIME_PSEUDONYM_PREFIX))
652		return NULL;
653
654	/* Remove possible realm from identity */
655	len = 0;
656	while (len < identity_len) {
657		if (identity[len] == '@')
658			break;
659		len++;
660	}
661
662	pseudonym = os_malloc(len + 1);
663	if (pseudonym == NULL)
664		return NULL;
665	os_memcpy(pseudonym, identity, len);
666	pseudonym[len] = '\0';
667
668	p = data->pseudonyms;
669	while (p) {
670		if (os_strcmp(p->pseudonym, pseudonym) == 0)
671			break;
672		p = p->next;
673	}
674
675	os_free(pseudonym);
676
677	return p;
678}
679
680
681static struct eap_sim_pseudonym *
682eap_sim_db_get_pseudonym_id(struct eap_sim_db_data *data, const u8 *identity,
683			    size_t identity_len)
684{
685	struct eap_sim_pseudonym *p;
686
687	if (identity_len == 0 ||
688	    (identity[0] != EAP_SIM_PERMANENT_PREFIX &&
689	     identity[0] != EAP_AKA_PERMANENT_PREFIX &&
690	     identity[0] != EAP_AKA_PRIME_PERMANENT_PREFIX))
691		return NULL;
692
693	p = data->pseudonyms;
694	while (p) {
695		if (identity_len == p->identity_len &&
696		    os_memcmp(p->identity, identity, identity_len) == 0)
697			break;
698		p = p->next;
699	}
700
701	return p;
702}
703
704
705static struct eap_sim_reauth *
706eap_sim_db_get_reauth(struct eap_sim_db_data *data, const u8 *identity,
707		      size_t identity_len)
708{
709	char *reauth_id;
710	size_t len;
711	struct eap_sim_reauth *r;
712
713	if (identity_len == 0 ||
714	    (identity[0] != EAP_SIM_REAUTH_ID_PREFIX &&
715	     identity[0] != EAP_AKA_REAUTH_ID_PREFIX &&
716	     identity[0] != EAP_AKA_PRIME_REAUTH_ID_PREFIX))
717		return NULL;
718
719	/* Remove possible realm from identity */
720	len = 0;
721	while (len < identity_len) {
722		if (identity[len] == '@')
723			break;
724		len++;
725	}
726
727	reauth_id = os_malloc(len + 1);
728	if (reauth_id == NULL)
729		return NULL;
730	os_memcpy(reauth_id, identity, len);
731	reauth_id[len] = '\0';
732
733	r = data->reauths;
734	while (r) {
735		if (os_strcmp(r->reauth_id, reauth_id) == 0)
736			break;
737		r = r->next;
738	}
739
740	os_free(reauth_id);
741
742	return r;
743}
744
745
746static struct eap_sim_reauth *
747eap_sim_db_get_reauth_id(struct eap_sim_db_data *data, const u8 *identity,
748			 size_t identity_len)
749{
750	struct eap_sim_pseudonym *p;
751	struct eap_sim_reauth *r;
752
753	if (identity_len == 0)
754		return NULL;
755
756	p = eap_sim_db_get_pseudonym(data, identity, identity_len);
757	if (p == NULL)
758		p = eap_sim_db_get_pseudonym_id(data, identity, identity_len);
759	if (p) {
760		identity = p->identity;
761		identity_len = p->identity_len;
762	}
763
764	r = data->reauths;
765	while (r) {
766		if (identity_len == r->identity_len &&
767		    os_memcmp(r->identity, identity, identity_len) == 0)
768			break;
769		r = r->next;
770	}
771
772	return r;
773}
774
775
776/**
777 * eap_sim_db_identity_known - Verify whether the given identity is known
778 * @priv: Private data pointer from eap_sim_db_init()
779 * @identity: User name identity
780 * @identity_len: Length of identity in bytes
781 * Returns: 0 if the user is found or -1 on failure
782 *
783 * In most cases, the user name is ['0','1','6'] | IMSI, i.e., 1 followed by
784 * the IMSI in ASCII format for EAP-SIM, ['2','3','7'] | pseudonym, or
785 * ['4','5','7'] | reauth_id.
786 */
787int eap_sim_db_identity_known(void *priv, const u8 *identity,
788			      size_t identity_len)
789{
790	struct eap_sim_db_data *data = priv;
791
792	if (identity == NULL || identity_len < 2)
793		return -1;
794
795	if (identity[0] == EAP_SIM_PSEUDONYM_PREFIX ||
796	    identity[0] == EAP_AKA_PSEUDONYM_PREFIX ||
797	    identity[0] == EAP_AKA_PRIME_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	    identity[0] == EAP_AKA_PRIME_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	    identity[0] != EAP_AKA_PRIME_PERMANENT_PREFIX) {
814		/* Unknown identity prefix */
815		return -1;
816	}
817
818	/* TODO: Should consider asking HLR/AuC gateway whether this permanent
819	 * identity is known. If it is, EAP-SIM/AKA can skip identity request.
820	 * In case of EAP-AKA, this would reduce number of needed round-trips.
821	 * Ideally, this would be done with one wait, i.e., just request
822	 * authentication data and store it for the next use. This would then
823	 * need to use similar pending-request functionality as the normal
824	 * request for authentication data at later phase.
825	 */
826	return -1;
827}
828
829
830static char * eap_sim_db_get_next(struct eap_sim_db_data *data, char prefix)
831{
832	char *id, *pos, *end;
833	u8 buf[10];
834
835	if (random_get_bytes(buf, sizeof(buf)))
836		return NULL;
837	id = os_malloc(sizeof(buf) * 2 + 2);
838	if (id == NULL)
839		return NULL;
840
841	pos = id;
842	end = id + sizeof(buf) * 2 + 2;
843	*pos++ = prefix;
844	pos += wpa_snprintf_hex(pos, end - pos, buf, sizeof(buf));
845
846	return id;
847}
848
849
850/**
851 * eap_sim_db_get_next_pseudonym - EAP-SIM DB: Get next pseudonym
852 * @priv: Private data pointer from eap_sim_db_init()
853 * @method: EAP method (SIM/AKA/AKA')
854 * Returns: Next pseudonym (allocated string) or %NULL on failure
855 *
856 * This function is used to generate a pseudonym for EAP-SIM. The returned
857 * pseudonym is not added to database at this point; it will need to be added
858 * with eap_sim_db_add_pseudonym() once the authentication has been completed
859 * successfully. Caller is responsible for freeing the returned buffer.
860 */
861char * eap_sim_db_get_next_pseudonym(void *priv, enum eap_sim_db_method method)
862{
863	struct eap_sim_db_data *data = priv;
864	char prefix = EAP_SIM_REAUTH_ID_PREFIX;
865
866	switch (method) {
867	case EAP_SIM_DB_SIM:
868		prefix = EAP_SIM_PSEUDONYM_PREFIX;
869		break;
870	case EAP_SIM_DB_AKA:
871		prefix = EAP_AKA_PSEUDONYM_PREFIX;
872		break;
873	case EAP_SIM_DB_AKA_PRIME:
874		prefix = EAP_AKA_PRIME_PSEUDONYM_PREFIX;
875		break;
876	}
877
878	return eap_sim_db_get_next(data, prefix);
879}
880
881
882/**
883 * eap_sim_db_get_next_reauth_id - EAP-SIM DB: Get next reauth_id
884 * @priv: Private data pointer from eap_sim_db_init()
885 * @method: EAP method (SIM/AKA/AKA')
886 * Returns: Next reauth_id (allocated string) or %NULL on failure
887 *
888 * This function is used to generate a fast re-authentication identity for
889 * EAP-SIM. The returned reauth_id is not added to database at this point; it
890 * will need to be added with eap_sim_db_add_reauth() once the authentication
891 * has been completed successfully. Caller is responsible for freeing the
892 * returned buffer.
893 */
894char * eap_sim_db_get_next_reauth_id(void *priv, enum eap_sim_db_method method)
895{
896	struct eap_sim_db_data *data = priv;
897	char prefix = EAP_SIM_REAUTH_ID_PREFIX;
898
899	switch (method) {
900	case EAP_SIM_DB_SIM:
901		prefix = EAP_SIM_REAUTH_ID_PREFIX;
902		break;
903	case EAP_SIM_DB_AKA:
904		prefix = EAP_AKA_REAUTH_ID_PREFIX;
905		break;
906	case EAP_SIM_DB_AKA_PRIME:
907		prefix = EAP_AKA_PRIME_REAUTH_ID_PREFIX;
908		break;
909	}
910
911	return eap_sim_db_get_next(data, prefix);
912}
913
914
915/**
916 * eap_sim_db_add_pseudonym - EAP-SIM DB: Add new pseudonym
917 * @priv: Private data pointer from eap_sim_db_init()
918 * @identity: Identity of the user (may be permanent identity or pseudonym)
919 * @identity_len: Length of identity
920 * @pseudonym: Pseudonym for this user. This needs to be an allocated buffer,
921 * e.g., return value from eap_sim_db_get_next_pseudonym(). Caller must not
922 * free it.
923 * Returns: 0 on success, -1 on failure
924 *
925 * This function adds a new pseudonym for EAP-SIM user. EAP-SIM DB is
926 * responsible of freeing pseudonym buffer once it is not needed anymore.
927 */
928int eap_sim_db_add_pseudonym(void *priv, const u8 *identity,
929			     size_t identity_len, char *pseudonym)
930{
931	struct eap_sim_db_data *data = priv;
932	struct eap_sim_pseudonym *p;
933	wpa_hexdump_ascii(MSG_DEBUG, "EAP-SIM DB: Add pseudonym for identity",
934			  identity, identity_len);
935	wpa_printf(MSG_DEBUG, "EAP-SIM DB: Pseudonym: %s", pseudonym);
936
937	/* TODO: could store last two pseudonyms */
938	p = eap_sim_db_get_pseudonym(data, identity, identity_len);
939	if (p == NULL)
940		p = eap_sim_db_get_pseudonym_id(data, identity, identity_len);
941
942	if (p) {
943		wpa_printf(MSG_DEBUG, "EAP-SIM DB: Replacing previous "
944			   "pseudonym: %s", p->pseudonym);
945		os_free(p->pseudonym);
946		p->pseudonym = pseudonym;
947		return 0;
948	}
949
950	p = os_zalloc(sizeof(*p));
951	if (p == NULL) {
952		os_free(pseudonym);
953		return -1;
954	}
955
956	p->next = data->pseudonyms;
957	p->identity = os_malloc(identity_len);
958	if (p->identity == NULL) {
959		os_free(p);
960		os_free(pseudonym);
961		return -1;
962	}
963	os_memcpy(p->identity, identity, identity_len);
964	p->identity_len = identity_len;
965	p->pseudonym = pseudonym;
966	data->pseudonyms = p;
967
968	wpa_printf(MSG_DEBUG, "EAP-SIM DB: Added new pseudonym entry");
969	return 0;
970}
971
972
973static struct eap_sim_reauth *
974eap_sim_db_add_reauth_data(struct eap_sim_db_data *data, const u8 *identity,
975			   size_t identity_len, char *reauth_id, u16 counter)
976{
977	struct eap_sim_reauth *r;
978
979	wpa_hexdump_ascii(MSG_DEBUG, "EAP-SIM DB: Add reauth_id for identity",
980			  identity, identity_len);
981	wpa_printf(MSG_DEBUG, "EAP-SIM DB: reauth_id: %s", reauth_id);
982
983	r = eap_sim_db_get_reauth(data, identity, identity_len);
984	if (r == NULL)
985		r = eap_sim_db_get_reauth_id(data, identity, identity_len);
986
987	if (r) {
988		wpa_printf(MSG_DEBUG, "EAP-SIM DB: Replacing previous "
989			   "reauth_id: %s", r->reauth_id);
990		os_free(r->reauth_id);
991		r->reauth_id = reauth_id;
992	} else {
993		r = os_zalloc(sizeof(*r));
994		if (r == NULL) {
995			os_free(reauth_id);
996			return NULL;
997		}
998
999		r->next = data->reauths;
1000		r->identity = os_malloc(identity_len);
1001		if (r->identity == NULL) {
1002			os_free(r);
1003			os_free(reauth_id);
1004			return NULL;
1005		}
1006		os_memcpy(r->identity, identity, identity_len);
1007		r->identity_len = identity_len;
1008		r->reauth_id = reauth_id;
1009		data->reauths = r;
1010		wpa_printf(MSG_DEBUG, "EAP-SIM DB: Added new reauth entry");
1011	}
1012
1013	r->counter = counter;
1014
1015	return r;
1016}
1017
1018
1019/**
1020 * eap_sim_db_add_reauth - EAP-SIM DB: Add new re-authentication entry
1021 * @priv: Private data pointer from eap_sim_db_init()
1022 * @identity: Identity of the user (may be permanent identity or pseudonym)
1023 * @identity_len: Length of identity
1024 * @reauth_id: reauth_id for this user. This needs to be an allocated buffer,
1025 * e.g., return value from eap_sim_db_get_next_reauth_id(). Caller must not
1026 * free it.
1027 * @counter: AT_COUNTER value for fast re-authentication
1028 * @mk: 16-byte MK from the previous full authentication or %NULL
1029 * Returns: 0 on success, -1 on failure
1030 *
1031 * This function adds a new re-authentication entry for an EAP-SIM user.
1032 * EAP-SIM DB is responsible of freeing reauth_id buffer once it is not needed
1033 * anymore.
1034 */
1035int eap_sim_db_add_reauth(void *priv, const u8 *identity,
1036			  size_t identity_len, char *reauth_id, u16 counter,
1037			  const u8 *mk)
1038{
1039	struct eap_sim_db_data *data = priv;
1040	struct eap_sim_reauth *r;
1041
1042	r = eap_sim_db_add_reauth_data(data, identity, identity_len, reauth_id,
1043				       counter);
1044	if (r == NULL)
1045		return -1;
1046
1047	os_memcpy(r->mk, mk, EAP_SIM_MK_LEN);
1048	r->aka_prime = 0;
1049
1050	return 0;
1051}
1052
1053
1054#ifdef EAP_SERVER_AKA_PRIME
1055/**
1056 * eap_sim_db_add_reauth_prime - EAP-AKA' DB: Add new re-authentication entry
1057 * @priv: Private data pointer from eap_sim_db_init()
1058 * @identity: Identity of the user (may be permanent identity or pseudonym)
1059 * @identity_len: Length of identity
1060 * @reauth_id: reauth_id for this user. This needs to be an allocated buffer,
1061 * e.g., return value from eap_sim_db_get_next_reauth_id(). Caller must not
1062 * free it.
1063 * @counter: AT_COUNTER value for fast re-authentication
1064 * @k_encr: K_encr from the previous full authentication
1065 * @k_aut: K_aut from the previous full authentication
1066 * @k_re: 32-byte K_re from the previous full authentication
1067 * Returns: 0 on success, -1 on failure
1068 *
1069 * This function adds a new re-authentication entry for an EAP-AKA' user.
1070 * EAP-SIM DB is responsible of freeing reauth_id buffer once it is not needed
1071 * anymore.
1072 */
1073int eap_sim_db_add_reauth_prime(void *priv, const u8 *identity,
1074				size_t identity_len, char *reauth_id,
1075				u16 counter, const u8 *k_encr, const u8 *k_aut,
1076				const u8 *k_re)
1077{
1078	struct eap_sim_db_data *data = priv;
1079	struct eap_sim_reauth *r;
1080
1081	r = eap_sim_db_add_reauth_data(data, identity, identity_len, reauth_id,
1082				       counter);
1083	if (r == NULL)
1084		return -1;
1085
1086	r->aka_prime = 1;
1087	os_memcpy(r->k_encr, k_encr, EAP_SIM_K_ENCR_LEN);
1088	os_memcpy(r->k_aut, k_aut, EAP_AKA_PRIME_K_AUT_LEN);
1089	os_memcpy(r->k_re, k_re, EAP_AKA_PRIME_K_RE_LEN);
1090
1091	return 0;
1092}
1093#endif /* EAP_SERVER_AKA_PRIME */
1094
1095
1096/**
1097 * eap_sim_db_get_permanent - EAP-SIM DB: Get permanent identity
1098 * @priv: Private data pointer from eap_sim_db_init()
1099 * @identity: Identity of the user (may be permanent identity or pseudonym)
1100 * @identity_len: Length of identity
1101 * @len: Buffer for length of the returned permanent identity
1102 * Returns: Pointer to the permanent identity, or %NULL if not found
1103 */
1104const u8 * eap_sim_db_get_permanent(void *priv, const u8 *identity,
1105				    size_t identity_len, size_t *len)
1106{
1107	struct eap_sim_db_data *data = priv;
1108	struct eap_sim_pseudonym *p;
1109
1110	if (identity == NULL)
1111		return NULL;
1112
1113	p = eap_sim_db_get_pseudonym(data, identity, identity_len);
1114	if (p == NULL)
1115		p = eap_sim_db_get_pseudonym_id(data, identity, identity_len);
1116	if (p == NULL)
1117		return NULL;
1118
1119	*len = p->identity_len;
1120	return p->identity;
1121}
1122
1123
1124/**
1125 * eap_sim_db_get_reauth_entry - EAP-SIM DB: Get re-authentication entry
1126 * @priv: Private data pointer from eap_sim_db_init()
1127 * @identity: Identity of the user (may be permanent identity, pseudonym, or
1128 * reauth_id)
1129 * @identity_len: Length of identity
1130 * Returns: Pointer to the re-auth entry, or %NULL if not found
1131 */
1132struct eap_sim_reauth *
1133eap_sim_db_get_reauth_entry(void *priv, const u8 *identity,
1134			    size_t identity_len)
1135{
1136	struct eap_sim_db_data *data = priv;
1137	struct eap_sim_reauth *r;
1138
1139	if (identity == NULL)
1140		return NULL;
1141	r = eap_sim_db_get_reauth(data, identity, identity_len);
1142	if (r == NULL)
1143		r = eap_sim_db_get_reauth_id(data, identity, identity_len);
1144	return r;
1145}
1146
1147
1148/**
1149 * eap_sim_db_remove_reauth - EAP-SIM DB: Remove re-authentication entry
1150 * @priv: Private data pointer from eap_sim_db_init()
1151 * @reauth: Pointer to re-authentication entry from
1152 * eap_sim_db_get_reauth_entry()
1153 */
1154void eap_sim_db_remove_reauth(void *priv, struct eap_sim_reauth *reauth)
1155{
1156	struct eap_sim_db_data *data = priv;
1157	struct eap_sim_reauth *r, *prev = NULL;
1158	r = data->reauths;
1159	while (r) {
1160		if (r == reauth) {
1161			if (prev)
1162				prev->next = r->next;
1163			else
1164				data->reauths = r->next;
1165			eap_sim_db_free_reauth(r);
1166			return;
1167		}
1168		prev = r;
1169		r = r->next;
1170	}
1171}
1172
1173
1174/**
1175 * eap_sim_db_get_aka_auth - Get AKA authentication values
1176 * @priv: Private data pointer from eap_sim_db_init()
1177 * @identity: User name identity
1178 * @identity_len: Length of identity in bytes
1179 * @_rand: Buffer for RAND value
1180 * @autn: Buffer for AUTN value
1181 * @ik: Buffer for IK value
1182 * @ck: Buffer for CK value
1183 * @res: Buffer for RES value
1184 * @res_len: Buffer for RES length
1185 * @cb_session_ctx: Session callback context for get_complete_cb()
1186 * Returns: 0 on success, -1 (EAP_SIM_DB_FAILURE) on error (e.g., user not
1187 * found), or -2 (EAP_SIM_DB_PENDING) if results are not yet available. In this
1188 * case, the callback function registered with eap_sim_db_init() will be
1189 * called once the results become available.
1190 *
1191 * In most cases, the user name is '0' | IMSI, i.e., 0 followed by the IMSI in
1192 * ASCII format for EAP-AKA and '6' | IMSI for EAP-AKA'.
1193 *
1194 * When using an external server for AKA authentication, this function can
1195 * always start a request and return EAP_SIM_DB_PENDING immediately if
1196 * authentication triplets are not available. Once the authentication data are
1197 * received, callback function registered with eap_sim_db_init() is called to
1198 * notify EAP state machine to reprocess the message. This
1199 * eap_sim_db_get_aka_auth() function will then be called again and the newly
1200 * received triplets will then be given to the caller.
1201 */
1202int eap_sim_db_get_aka_auth(void *priv, const u8 *identity,
1203			    size_t identity_len, u8 *_rand, u8 *autn, u8 *ik,
1204			    u8 *ck, u8 *res, size_t *res_len,
1205			    void *cb_session_ctx)
1206{
1207	struct eap_sim_db_data *data = priv;
1208	struct eap_sim_db_pending *entry;
1209	int len;
1210	size_t i;
1211	char msg[40];
1212
1213	if (identity_len < 2 || identity == NULL ||
1214	    (identity[0] != EAP_AKA_PERMANENT_PREFIX &&
1215	     identity[0] != EAP_AKA_PRIME_PERMANENT_PREFIX)) {
1216		wpa_hexdump_ascii(MSG_DEBUG, "EAP-SIM DB: unexpected identity",
1217				  identity, identity_len);
1218		return EAP_SIM_DB_FAILURE;
1219	}
1220	identity++;
1221	identity_len--;
1222	for (i = 0; i < identity_len; i++) {
1223		if (identity[i] == '@') {
1224			identity_len = i;
1225			break;
1226		}
1227	}
1228	if (identity_len + 1 > sizeof(entry->imsi)) {
1229		wpa_hexdump_ascii(MSG_DEBUG, "EAP-SIM DB: unexpected identity",
1230				  identity, identity_len);
1231		return EAP_SIM_DB_FAILURE;
1232	}
1233	wpa_hexdump_ascii(MSG_DEBUG, "EAP-SIM DB: Get AKA auth for IMSI",
1234			  identity, identity_len);
1235
1236	entry = eap_sim_db_get_pending(data, identity, identity_len, 1);
1237	if (entry) {
1238		if (entry->state == FAILURE) {
1239			os_free(entry);
1240			wpa_printf(MSG_DEBUG, "EAP-SIM DB: Failure");
1241			return EAP_SIM_DB_FAILURE;
1242		}
1243
1244		if (entry->state == PENDING) {
1245			eap_sim_db_add_pending(data, entry);
1246			wpa_printf(MSG_DEBUG, "EAP-SIM DB: Pending");
1247			return EAP_SIM_DB_PENDING;
1248		}
1249
1250		wpa_printf(MSG_DEBUG, "EAP-SIM DB: Returning successfully "
1251			   "received authentication data");
1252		os_memcpy(_rand, entry->u.aka.rand, EAP_AKA_RAND_LEN);
1253		os_memcpy(autn, entry->u.aka.autn, EAP_AKA_AUTN_LEN);
1254		os_memcpy(ik, entry->u.aka.ik, EAP_AKA_IK_LEN);
1255		os_memcpy(ck, entry->u.aka.ck, EAP_AKA_CK_LEN);
1256		os_memcpy(res, entry->u.aka.res, EAP_AKA_RES_MAX_LEN);
1257		*res_len = entry->u.aka.res_len;
1258		os_free(entry);
1259		return 0;
1260	}
1261
1262	if (data->sock < 0) {
1263		if (eap_sim_db_open_socket(data) < 0)
1264			return EAP_SIM_DB_FAILURE;
1265	}
1266
1267	len = os_snprintf(msg, sizeof(msg), "AKA-REQ-AUTH ");
1268	if (len < 0 || len + identity_len >= sizeof(msg))
1269		return EAP_SIM_DB_FAILURE;
1270	os_memcpy(msg + len, identity, identity_len);
1271	len += identity_len;
1272
1273	wpa_hexdump(MSG_DEBUG, "EAP-SIM DB: requesting AKA authentication "
1274		    "data for IMSI", identity, identity_len);
1275	if (eap_sim_db_send(data, msg, len) < 0)
1276		return EAP_SIM_DB_FAILURE;
1277
1278	entry = os_zalloc(sizeof(*entry));
1279	if (entry == NULL)
1280		return EAP_SIM_DB_FAILURE;
1281
1282	os_get_time(&entry->timestamp);
1283	entry->aka = 1;
1284	os_memcpy(entry->imsi, identity, identity_len);
1285	entry->imsi_len = identity_len;
1286	entry->cb_session_ctx = cb_session_ctx;
1287	entry->state = PENDING;
1288	eap_sim_db_add_pending(data, entry);
1289	eap_sim_db_expire_pending(data);
1290
1291	return EAP_SIM_DB_PENDING;
1292}
1293
1294
1295/**
1296 * eap_sim_db_resynchronize - Resynchronize AKA AUTN
1297 * @priv: Private data pointer from eap_sim_db_init()
1298 * @identity: User name identity
1299 * @identity_len: Length of identity in bytes
1300 * @auts: AUTS value from the peer
1301 * @_rand: RAND value used in the rejected message
1302 * Returns: 0 on success, -1 on failure
1303 *
1304 * This function is called when the peer reports synchronization failure in the
1305 * AUTN value by sending AUTS. The AUTS and RAND values should be sent to
1306 * HLR/AuC to allow it to resynchronize with the peer. After this,
1307 * eap_sim_db_get_aka_auth() will be called again to to fetch updated
1308 * RAND/AUTN values for the next challenge.
1309 */
1310int eap_sim_db_resynchronize(void *priv, const u8 *identity,
1311			     size_t identity_len, const u8 *auts,
1312			     const u8 *_rand)
1313{
1314	struct eap_sim_db_data *data = priv;
1315	size_t i;
1316
1317	if (identity_len < 2 || identity == NULL ||
1318	    (identity[0] != EAP_AKA_PERMANENT_PREFIX &&
1319	     identity[0] != EAP_AKA_PRIME_PERMANENT_PREFIX)) {
1320		wpa_hexdump_ascii(MSG_DEBUG, "EAP-SIM DB: unexpected identity",
1321				  identity, identity_len);
1322		return -1;
1323	}
1324	identity++;
1325	identity_len--;
1326	for (i = 0; i < identity_len; i++) {
1327		if (identity[i] == '@') {
1328			identity_len = i;
1329			break;
1330		}
1331	}
1332	if (identity_len > 20) {
1333		wpa_hexdump_ascii(MSG_DEBUG, "EAP-SIM DB: unexpected identity",
1334				  identity, identity_len);
1335		return -1;
1336	}
1337
1338	if (data->sock >= 0) {
1339		char msg[100];
1340		int len, ret;
1341
1342		len = os_snprintf(msg, sizeof(msg), "AKA-AUTS ");
1343		if (len < 0 || len + identity_len >= sizeof(msg))
1344			return -1;
1345		os_memcpy(msg + len, identity, identity_len);
1346		len += identity_len;
1347
1348		ret = os_snprintf(msg + len, sizeof(msg) - len, " ");
1349		if (ret < 0 || (size_t) ret >= sizeof(msg) - len)
1350			return -1;
1351		len += ret;
1352		len += wpa_snprintf_hex(msg + len, sizeof(msg) - len,
1353					auts, EAP_AKA_AUTS_LEN);
1354		ret = os_snprintf(msg + len, sizeof(msg) - len, " ");
1355		if (ret < 0 || (size_t) ret >= sizeof(msg) - len)
1356			return -1;
1357		len += ret;
1358		len += wpa_snprintf_hex(msg + len, sizeof(msg) - len,
1359					_rand, EAP_AKA_RAND_LEN);
1360		wpa_hexdump(MSG_DEBUG, "EAP-SIM DB: reporting AKA AUTS for "
1361			    "IMSI", identity, identity_len);
1362		if (eap_sim_db_send(data, msg, len) < 0)
1363			return -1;
1364	}
1365
1366	return 0;
1367}
1368