]> git.ipfire.org Git - thirdparty/hostap.git/blame - src/eap_server/eap_sim_db.c
EAP-AKA' server: Fix identity for MK derivation
[thirdparty/hostap.git] / src / eap_server / eap_sim_db.c
CommitLineData
6fc6879b
JM
1/*
2 * hostapd / EAP-SIM database/authenticator gateway
3 * Copyright (c) 2005-2007, Jouni Malinen <j@w1.fi>
4 *
0f3d578e
JM
5 * This software may be distributed under the terms of the BSD license.
6 * See README for more details.
6fc6879b
JM
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"
3642c431 22#include "crypto/random.h"
6fc6879b
JM
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 return NULL;
652
653 /* Remove possible realm from identity */
654 len = 0;
655 while (len < identity_len) {
656 if (identity[len] == '@')
657 break;
658 len++;
659 }
660
661 pseudonym = os_malloc(len + 1);
662 if (pseudonym == NULL)
663 return NULL;
664 os_memcpy(pseudonym, identity, len);
665 pseudonym[len] = '\0';
666
667 p = data->pseudonyms;
668 while (p) {
669 if (os_strcmp(p->pseudonym, pseudonym) == 0)
670 break;
671 p = p->next;
672 }
673
674 os_free(pseudonym);
675
676 return p;
677}
678
679
680static struct eap_sim_pseudonym *
681eap_sim_db_get_pseudonym_id(struct eap_sim_db_data *data, const u8 *identity,
682 size_t identity_len)
683{
684 struct eap_sim_pseudonym *p;
685
686 if (identity_len == 0 ||
687 (identity[0] != EAP_SIM_PERMANENT_PREFIX &&
688 identity[0] != EAP_AKA_PERMANENT_PREFIX))
689 return NULL;
690
691 p = data->pseudonyms;
692 while (p) {
693 if (identity_len == p->identity_len &&
694 os_memcmp(p->identity, identity, identity_len) == 0)
695 break;
696 p = p->next;
697 }
698
699 return p;
700}
701
702
703static struct eap_sim_reauth *
704eap_sim_db_get_reauth(struct eap_sim_db_data *data, const u8 *identity,
705 size_t identity_len)
706{
707 char *reauth_id;
708 size_t len;
709 struct eap_sim_reauth *r;
710
711 if (identity_len == 0 ||
712 (identity[0] != EAP_SIM_REAUTH_ID_PREFIX &&
713 identity[0] != EAP_AKA_REAUTH_ID_PREFIX))
714 return NULL;
715
716 /* Remove possible realm from identity */
717 len = 0;
718 while (len < identity_len) {
719 if (identity[len] == '@')
720 break;
721 len++;
722 }
723
724 reauth_id = os_malloc(len + 1);
725 if (reauth_id == NULL)
726 return NULL;
727 os_memcpy(reauth_id, identity, len);
728 reauth_id[len] = '\0';
729
730 r = data->reauths;
731 while (r) {
732 if (os_strcmp(r->reauth_id, reauth_id) == 0)
733 break;
734 r = r->next;
735 }
736
737 os_free(reauth_id);
738
739 return r;
740}
741
742
743static struct eap_sim_reauth *
744eap_sim_db_get_reauth_id(struct eap_sim_db_data *data, const u8 *identity,
745 size_t identity_len)
746{
747 struct eap_sim_pseudonym *p;
748 struct eap_sim_reauth *r;
749
750 if (identity_len == 0)
751 return NULL;
752
753 p = eap_sim_db_get_pseudonym(data, identity, identity_len);
754 if (p == NULL)
755 p = eap_sim_db_get_pseudonym_id(data, identity, identity_len);
756 if (p) {
757 identity = p->identity;
758 identity_len = p->identity_len;
759 }
760
761 r = data->reauths;
762 while (r) {
763 if (identity_len == r->identity_len &&
764 os_memcmp(r->identity, identity, identity_len) == 0)
765 break;
766 r = r->next;
767 }
768
769 return r;
770}
771
772
773/**
774 * eap_sim_db_identity_known - Verify whether the given identity is known
775 * @priv: Private data pointer from eap_sim_db_init()
776 * @identity: User name identity
777 * @identity_len: Length of identity in bytes
778 * Returns: 0 if the user is found or -1 on failure
779 *
780 * In most cases, the user name is ['0','1'] | IMSI, i.e., 1 followed by the
781 * IMSI in ASCII format, ['2','3'] | pseudonym, or ['4','5'] | reauth_id.
782 */
783int eap_sim_db_identity_known(void *priv, const u8 *identity,
784 size_t identity_len)
785{
786 struct eap_sim_db_data *data = priv;
787
788 if (identity == NULL || identity_len < 2)
789 return -1;
790
791 if (identity[0] == EAP_SIM_PSEUDONYM_PREFIX ||
792 identity[0] == EAP_AKA_PSEUDONYM_PREFIX) {
793 struct eap_sim_pseudonym *p =
794 eap_sim_db_get_pseudonym(data, identity, identity_len);
795 return p ? 0 : -1;
796 }
797
798 if (identity[0] == EAP_SIM_REAUTH_ID_PREFIX ||
799 identity[0] == EAP_AKA_REAUTH_ID_PREFIX) {
800 struct eap_sim_reauth *r =
801 eap_sim_db_get_reauth(data, identity, identity_len);
802 return r ? 0 : -1;
803 }
804
805 if (identity[0] != EAP_SIM_PERMANENT_PREFIX &&
806 identity[0] != EAP_AKA_PERMANENT_PREFIX) {
807 /* Unknown identity prefix */
808 return -1;
809 }
810
811 /* TODO: Should consider asking HLR/AuC gateway whether this permanent
812 * identity is known. If it is, EAP-SIM/AKA can skip identity request.
813 * In case of EAP-AKA, this would reduce number of needed round-trips.
814 * Ideally, this would be done with one wait, i.e., just request
815 * authentication data and store it for the next use. This would then
816 * need to use similar pending-request functionality as the normal
817 * request for authentication data at later phase.
818 */
819 return -1;
820}
821
822
823static char * eap_sim_db_get_next(struct eap_sim_db_data *data, char prefix)
824{
825 char *id, *pos, *end;
826 u8 buf[10];
827
3642c431 828 if (random_get_bytes(buf, sizeof(buf)))
6fc6879b
JM
829 return NULL;
830 id = os_malloc(sizeof(buf) * 2 + 2);
831 if (id == NULL)
832 return NULL;
833
834 pos = id;
835 end = id + sizeof(buf) * 2 + 2;
836 *pos++ = prefix;
837 pos += wpa_snprintf_hex(pos, end - pos, buf, sizeof(buf));
838
839 return id;
840}
841
842
843/**
844 * eap_sim_db_get_next_pseudonym - EAP-SIM DB: Get next pseudonym
845 * @priv: Private data pointer from eap_sim_db_init()
846 * @aka: Using EAP-AKA instead of EAP-SIM
847 * Returns: Next pseudonym (allocated string) or %NULL on failure
848 *
849 * This function is used to generate a pseudonym for EAP-SIM. The returned
850 * pseudonym is not added to database at this point; it will need to be added
851 * with eap_sim_db_add_pseudonym() once the authentication has been completed
852 * successfully. Caller is responsible for freeing the returned buffer.
853 */
854char * eap_sim_db_get_next_pseudonym(void *priv, int aka)
855{
856 struct eap_sim_db_data *data = priv;
857 return eap_sim_db_get_next(data, aka ? EAP_AKA_PSEUDONYM_PREFIX :
858 EAP_SIM_PSEUDONYM_PREFIX);
859}
860
861
862/**
863 * eap_sim_db_get_next_reauth_id - EAP-SIM DB: Get next reauth_id
864 * @priv: Private data pointer from eap_sim_db_init()
865 * @aka: Using EAP-AKA instead of EAP-SIM
866 * Returns: Next reauth_id (allocated string) or %NULL on failure
867 *
868 * This function is used to generate a fast re-authentication identity for
869 * EAP-SIM. The returned reauth_id is not added to database at this point; it
870 * will need to be added with eap_sim_db_add_reauth() once the authentication
871 * has been completed successfully. Caller is responsible for freeing the
872 * returned buffer.
873 */
874char * eap_sim_db_get_next_reauth_id(void *priv, int aka)
875{
876 struct eap_sim_db_data *data = priv;
877 return eap_sim_db_get_next(data, aka ? EAP_AKA_REAUTH_ID_PREFIX :
878 EAP_SIM_REAUTH_ID_PREFIX);
879}
880
881
882/**
883 * eap_sim_db_add_pseudonym - EAP-SIM DB: Add new pseudonym
884 * @priv: Private data pointer from eap_sim_db_init()
885 * @identity: Identity of the user (may be permanent identity or pseudonym)
886 * @identity_len: Length of identity
887 * @pseudonym: Pseudonym for this user. This needs to be an allocated buffer,
888 * e.g., return value from eap_sim_db_get_next_pseudonym(). Caller must not
889 * free it.
890 * Returns: 0 on success, -1 on failure
891 *
892 * This function adds a new pseudonym for EAP-SIM user. EAP-SIM DB is
893 * responsible of freeing pseudonym buffer once it is not needed anymore.
894 */
895int eap_sim_db_add_pseudonym(void *priv, const u8 *identity,
896 size_t identity_len, char *pseudonym)
897{
898 struct eap_sim_db_data *data = priv;
899 struct eap_sim_pseudonym *p;
900 wpa_hexdump_ascii(MSG_DEBUG, "EAP-SIM DB: Add pseudonym for identity",
901 identity, identity_len);
902 wpa_printf(MSG_DEBUG, "EAP-SIM DB: Pseudonym: %s", pseudonym);
903
904 /* TODO: could store last two pseudonyms */
905 p = eap_sim_db_get_pseudonym(data, identity, identity_len);
906 if (p == NULL)
907 p = eap_sim_db_get_pseudonym_id(data, identity, identity_len);
908
909 if (p) {
910 wpa_printf(MSG_DEBUG, "EAP-SIM DB: Replacing previous "
911 "pseudonym: %s", p->pseudonym);
912 os_free(p->pseudonym);
913 p->pseudonym = pseudonym;
914 return 0;
915 }
916
917 p = os_zalloc(sizeof(*p));
918 if (p == NULL) {
919 os_free(pseudonym);
920 return -1;
921 }
922
923 p->next = data->pseudonyms;
924 p->identity = os_malloc(identity_len);
925 if (p->identity == NULL) {
926 os_free(p);
927 os_free(pseudonym);
928 return -1;
929 }
930 os_memcpy(p->identity, identity, identity_len);
931 p->identity_len = identity_len;
932 p->pseudonym = pseudonym;
933 data->pseudonyms = p;
934
935 wpa_printf(MSG_DEBUG, "EAP-SIM DB: Added new pseudonym entry");
936 return 0;
937}
938
939
9881795e
JM
940static struct eap_sim_reauth *
941eap_sim_db_add_reauth_data(struct eap_sim_db_data *data, const u8 *identity,
942 size_t identity_len, char *reauth_id, u16 counter)
6fc6879b 943{
6fc6879b 944 struct eap_sim_reauth *r;
9881795e 945
6fc6879b
JM
946 wpa_hexdump_ascii(MSG_DEBUG, "EAP-SIM DB: Add reauth_id for identity",
947 identity, identity_len);
948 wpa_printf(MSG_DEBUG, "EAP-SIM DB: reauth_id: %s", reauth_id);
949
950 r = eap_sim_db_get_reauth(data, identity, identity_len);
951 if (r == NULL)
952 r = eap_sim_db_get_reauth_id(data, identity, identity_len);
953
954 if (r) {
955 wpa_printf(MSG_DEBUG, "EAP-SIM DB: Replacing previous "
956 "reauth_id: %s", r->reauth_id);
957 os_free(r->reauth_id);
958 r->reauth_id = reauth_id;
959 } else {
960 r = os_zalloc(sizeof(*r));
961 if (r == NULL) {
962 os_free(reauth_id);
9881795e 963 return NULL;
6fc6879b
JM
964 }
965
966 r->next = data->reauths;
967 r->identity = os_malloc(identity_len);
968 if (r->identity == NULL) {
969 os_free(r);
970 os_free(reauth_id);
9881795e 971 return NULL;
6fc6879b
JM
972 }
973 os_memcpy(r->identity, identity, identity_len);
974 r->identity_len = identity_len;
975 r->reauth_id = reauth_id;
976 data->reauths = r;
977 wpa_printf(MSG_DEBUG, "EAP-SIM DB: Added new reauth entry");
978 }
979
980 r->counter = counter;
9881795e
JM
981
982 return r;
983}
984
985
986/**
987 * eap_sim_db_add_reauth - EAP-SIM DB: Add new re-authentication entry
988 * @priv: Private data pointer from eap_sim_db_init()
989 * @identity: Identity of the user (may be permanent identity or pseudonym)
990 * @identity_len: Length of identity
991 * @reauth_id: reauth_id for this user. This needs to be an allocated buffer,
992 * e.g., return value from eap_sim_db_get_next_reauth_id(). Caller must not
993 * free it.
a17df5fb 994 * @counter: AT_COUNTER value for fast re-authentication
9881795e
JM
995 * @mk: 16-byte MK from the previous full authentication or %NULL
996 * Returns: 0 on success, -1 on failure
997 *
998 * This function adds a new re-authentication entry for an EAP-SIM user.
999 * EAP-SIM DB is responsible of freeing reauth_id buffer once it is not needed
1000 * anymore.
1001 */
1002int eap_sim_db_add_reauth(void *priv, const u8 *identity,
1003 size_t identity_len, char *reauth_id, u16 counter,
1004 const u8 *mk)
1005{
1006 struct eap_sim_db_data *data = priv;
1007 struct eap_sim_reauth *r;
1008
1009 r = eap_sim_db_add_reauth_data(data, identity, identity_len, reauth_id,
1010 counter);
1011 if (r == NULL)
1012 return -1;
1013
6fc6879b 1014 os_memcpy(r->mk, mk, EAP_SIM_MK_LEN);
9881795e
JM
1015 r->aka_prime = 0;
1016
1017 return 0;
1018}
1019
1020
1e5839e0 1021#ifdef EAP_SERVER_AKA_PRIME
9881795e
JM
1022/**
1023 * eap_sim_db_add_reauth_prime - EAP-AKA' DB: Add new re-authentication entry
1024 * @priv: Private data pointer from eap_sim_db_init()
1025 * @identity: Identity of the user (may be permanent identity or pseudonym)
1026 * @identity_len: Length of identity
1027 * @reauth_id: reauth_id for this user. This needs to be an allocated buffer,
1028 * e.g., return value from eap_sim_db_get_next_reauth_id(). Caller must not
1029 * free it.
a17df5fb
JM
1030 * @counter: AT_COUNTER value for fast re-authentication
1031 * @k_encr: K_encr from the previous full authentication
1032 * @k_aut: K_aut from the previous full authentication
1033 * @k_re: 32-byte K_re from the previous full authentication
9881795e
JM
1034 * Returns: 0 on success, -1 on failure
1035 *
1036 * This function adds a new re-authentication entry for an EAP-AKA' user.
1037 * EAP-SIM DB is responsible of freeing reauth_id buffer once it is not needed
1038 * anymore.
1039 */
1040int eap_sim_db_add_reauth_prime(void *priv, const u8 *identity,
1041 size_t identity_len, char *reauth_id,
1042 u16 counter, const u8 *k_encr, const u8 *k_aut,
1043 const u8 *k_re)
1044{
1045 struct eap_sim_db_data *data = priv;
1046 struct eap_sim_reauth *r;
1047
1048 r = eap_sim_db_add_reauth_data(data, identity, identity_len, reauth_id,
1049 counter);
1050 if (r == NULL)
1051 return -1;
1052
1053 r->aka_prime = 1;
1054 os_memcpy(r->k_encr, k_encr, EAP_SIM_K_ENCR_LEN);
1055 os_memcpy(r->k_aut, k_aut, EAP_AKA_PRIME_K_AUT_LEN);
1056 os_memcpy(r->k_re, k_re, EAP_AKA_PRIME_K_RE_LEN);
6fc6879b
JM
1057
1058 return 0;
1059}
1e5839e0 1060#endif /* EAP_SERVER_AKA_PRIME */
6fc6879b
JM
1061
1062
1063/**
1064 * eap_sim_db_get_permanent - EAP-SIM DB: Get permanent identity
1065 * @priv: Private data pointer from eap_sim_db_init()
1066 * @identity: Identity of the user (may be permanent identity or pseudonym)
1067 * @identity_len: Length of identity
1068 * @len: Buffer for length of the returned permanent identity
1069 * Returns: Pointer to the permanent identity, or %NULL if not found
1070 */
1071const u8 * eap_sim_db_get_permanent(void *priv, const u8 *identity,
1072 size_t identity_len, size_t *len)
1073{
1074 struct eap_sim_db_data *data = priv;
1075 struct eap_sim_pseudonym *p;
1076
1077 if (identity == NULL)
1078 return NULL;
1079
1080 p = eap_sim_db_get_pseudonym(data, identity, identity_len);
1081 if (p == NULL)
1082 p = eap_sim_db_get_pseudonym_id(data, identity, identity_len);
1083 if (p == NULL)
1084 return NULL;
1085
1086 *len = p->identity_len;
1087 return p->identity;
1088}
1089
1090
1091/**
1092 * eap_sim_db_get_reauth_entry - EAP-SIM DB: Get re-authentication entry
1093 * @priv: Private data pointer from eap_sim_db_init()
1094 * @identity: Identity of the user (may be permanent identity, pseudonym, or
1095 * reauth_id)
1096 * @identity_len: Length of identity
6fc6879b
JM
1097 * Returns: Pointer to the re-auth entry, or %NULL if not found
1098 */
1099struct eap_sim_reauth *
1100eap_sim_db_get_reauth_entry(void *priv, const u8 *identity,
1101 size_t identity_len)
1102{
1103 struct eap_sim_db_data *data = priv;
1104 struct eap_sim_reauth *r;
1105
1106 if (identity == NULL)
1107 return NULL;
1108 r = eap_sim_db_get_reauth(data, identity, identity_len);
1109 if (r == NULL)
1110 r = eap_sim_db_get_reauth_id(data, identity, identity_len);
1111 return r;
1112}
1113
1114
1115/**
1116 * eap_sim_db_remove_reauth - EAP-SIM DB: Remove re-authentication entry
1117 * @priv: Private data pointer from eap_sim_db_init()
1118 * @reauth: Pointer to re-authentication entry from
1119 * eap_sim_db_get_reauth_entry()
1120 */
1121void eap_sim_db_remove_reauth(void *priv, struct eap_sim_reauth *reauth)
1122{
1123 struct eap_sim_db_data *data = priv;
1124 struct eap_sim_reauth *r, *prev = NULL;
1125 r = data->reauths;
1126 while (r) {
1127 if (r == reauth) {
1128 if (prev)
1129 prev->next = r->next;
1130 else
1131 data->reauths = r->next;
1132 eap_sim_db_free_reauth(r);
1133 return;
1134 }
1135 prev = r;
1136 r = r->next;
1137 }
1138}
1139
1140
1141/**
1142 * eap_sim_db_get_aka_auth - Get AKA authentication values
1143 * @priv: Private data pointer from eap_sim_db_init()
1144 * @identity: User name identity
1145 * @identity_len: Length of identity in bytes
1146 * @_rand: Buffer for RAND value
1147 * @autn: Buffer for AUTN value
1148 * @ik: Buffer for IK value
1149 * @ck: Buffer for CK value
1150 * @res: Buffer for RES value
1151 * @res_len: Buffer for RES length
1152 * @cb_session_ctx: Session callback context for get_complete_cb()
1153 * Returns: 0 on success, -1 (EAP_SIM_DB_FAILURE) on error (e.g., user not
1154 * found), or -2 (EAP_SIM_DB_PENDING) if results are not yet available. In this
1155 * case, the callback function registered with eap_sim_db_init() will be
1156 * called once the results become available.
1157 *
1158 * In most cases, the user name is '0' | IMSI, i.e., 0 followed by the IMSI in
1159 * ASCII format.
1160 *
1161 * When using an external server for AKA authentication, this function can
1162 * always start a request and return EAP_SIM_DB_PENDING immediately if
1163 * authentication triplets are not available. Once the authentication data are
1164 * received, callback function registered with eap_sim_db_init() is called to
1165 * notify EAP state machine to reprocess the message. This
1166 * eap_sim_db_get_aka_auth() function will then be called again and the newly
1167 * received triplets will then be given to the caller.
1168 */
1169int eap_sim_db_get_aka_auth(void *priv, const u8 *identity,
1170 size_t identity_len, u8 *_rand, u8 *autn, u8 *ik,
1171 u8 *ck, u8 *res, size_t *res_len,
1172 void *cb_session_ctx)
1173{
1174 struct eap_sim_db_data *data = priv;
1175 struct eap_sim_db_pending *entry;
1176 int len;
1177 size_t i;
1178 char msg[40];
1179
1180 if (identity_len < 2 || identity == NULL ||
1181 identity[0] != EAP_AKA_PERMANENT_PREFIX) {
1182 wpa_hexdump_ascii(MSG_DEBUG, "EAP-SIM DB: unexpected identity",
1183 identity, identity_len);
1184 return EAP_SIM_DB_FAILURE;
1185 }
1186 identity++;
1187 identity_len--;
1188 for (i = 0; i < identity_len; i++) {
1189 if (identity[i] == '@') {
1190 identity_len = i;
1191 break;
1192 }
1193 }
1194 if (identity_len + 1 > sizeof(entry->imsi)) {
1195 wpa_hexdump_ascii(MSG_DEBUG, "EAP-SIM DB: unexpected identity",
1196 identity, identity_len);
1197 return EAP_SIM_DB_FAILURE;
1198 }
1199 wpa_hexdump_ascii(MSG_DEBUG, "EAP-SIM DB: Get AKA auth for IMSI",
1200 identity, identity_len);
1201
1202 entry = eap_sim_db_get_pending(data, identity, identity_len, 1);
1203 if (entry) {
1204 if (entry->state == FAILURE) {
1205 os_free(entry);
1206 wpa_printf(MSG_DEBUG, "EAP-SIM DB: Failure");
1207 return EAP_SIM_DB_FAILURE;
1208 }
1209
1210 if (entry->state == PENDING) {
1211 eap_sim_db_add_pending(data, entry);
1212 wpa_printf(MSG_DEBUG, "EAP-SIM DB: Pending");
1213 return EAP_SIM_DB_PENDING;
1214 }
1215
1216 wpa_printf(MSG_DEBUG, "EAP-SIM DB: Returning successfully "
1217 "received authentication data");
1218 os_memcpy(_rand, entry->u.aka.rand, EAP_AKA_RAND_LEN);
1219 os_memcpy(autn, entry->u.aka.autn, EAP_AKA_AUTN_LEN);
1220 os_memcpy(ik, entry->u.aka.ik, EAP_AKA_IK_LEN);
1221 os_memcpy(ck, entry->u.aka.ck, EAP_AKA_CK_LEN);
1222 os_memcpy(res, entry->u.aka.res, EAP_AKA_RES_MAX_LEN);
1223 *res_len = entry->u.aka.res_len;
1224 os_free(entry);
1225 return 0;
1226 }
1227
1228 if (data->sock < 0) {
1229 if (eap_sim_db_open_socket(data) < 0)
1230 return EAP_SIM_DB_FAILURE;
1231 }
1232
1233 len = os_snprintf(msg, sizeof(msg), "AKA-REQ-AUTH ");
1234 if (len < 0 || len + identity_len >= sizeof(msg))
1235 return EAP_SIM_DB_FAILURE;
1236 os_memcpy(msg + len, identity, identity_len);
1237 len += identity_len;
1238
1239 wpa_hexdump(MSG_DEBUG, "EAP-SIM DB: requesting AKA authentication "
1240 "data for IMSI", identity, identity_len);
1241 if (eap_sim_db_send(data, msg, len) < 0)
1242 return EAP_SIM_DB_FAILURE;
1243
1244 entry = os_zalloc(sizeof(*entry));
1245 if (entry == NULL)
1246 return EAP_SIM_DB_FAILURE;
1247
1248 os_get_time(&entry->timestamp);
1249 entry->aka = 1;
1250 os_memcpy(entry->imsi, identity, identity_len);
1251 entry->imsi_len = identity_len;
1252 entry->cb_session_ctx = cb_session_ctx;
1253 entry->state = PENDING;
1254 eap_sim_db_add_pending(data, entry);
1255 eap_sim_db_expire_pending(data);
1256
1257 return EAP_SIM_DB_PENDING;
1258}
1259
1260
1261/**
1262 * eap_sim_db_resynchronize - Resynchronize AKA AUTN
1263 * @priv: Private data pointer from eap_sim_db_init()
1264 * @identity: User name identity
1265 * @identity_len: Length of identity in bytes
1266 * @auts: AUTS value from the peer
1267 * @_rand: RAND value used in the rejected message
1268 * Returns: 0 on success, -1 on failure
1269 *
1270 * This function is called when the peer reports synchronization failure in the
1271 * AUTN value by sending AUTS. The AUTS and RAND values should be sent to
1272 * HLR/AuC to allow it to resynchronize with the peer. After this,
1273 * eap_sim_db_get_aka_auth() will be called again to to fetch updated
1274 * RAND/AUTN values for the next challenge.
1275 */
1276int eap_sim_db_resynchronize(void *priv, const u8 *identity,
1277 size_t identity_len, const u8 *auts,
1278 const u8 *_rand)
1279{
1280 struct eap_sim_db_data *data = priv;
1281 size_t i;
1282
1283 if (identity_len < 2 || identity == NULL ||
1284 identity[0] != EAP_AKA_PERMANENT_PREFIX) {
1285 wpa_hexdump_ascii(MSG_DEBUG, "EAP-SIM DB: unexpected identity",
1286 identity, identity_len);
1287 return -1;
1288 }
1289 identity++;
1290 identity_len--;
1291 for (i = 0; i < identity_len; i++) {
1292 if (identity[i] == '@') {
1293 identity_len = i;
1294 break;
1295 }
1296 }
1297 if (identity_len > 20) {
1298 wpa_hexdump_ascii(MSG_DEBUG, "EAP-SIM DB: unexpected identity",
1299 identity, identity_len);
1300 return -1;
1301 }
1302
1303 if (data->sock >= 0) {
1304 char msg[100];
1305 int len, ret;
1306
1307 len = os_snprintf(msg, sizeof(msg), "AKA-AUTS ");
1308 if (len < 0 || len + identity_len >= sizeof(msg))
1309 return -1;
1310 os_memcpy(msg + len, identity, identity_len);
1311 len += identity_len;
1312
1313 ret = os_snprintf(msg + len, sizeof(msg) - len, " ");
1314 if (ret < 0 || (size_t) ret >= sizeof(msg) - len)
1315 return -1;
1316 len += ret;
1317 len += wpa_snprintf_hex(msg + len, sizeof(msg) - len,
1318 auts, EAP_AKA_AUTS_LEN);
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 _rand, EAP_AKA_RAND_LEN);
1325 wpa_hexdump(MSG_DEBUG, "EAP-SIM DB: reporting AKA AUTS for "
1326 "IMSI", identity, identity_len);
1327 if (eap_sim_db_send(data, msg, len) < 0)
1328 return -1;
1329 }
1330
1331 return 0;
1332}