hostapd: Update vendor branch to 0.6.10
[dragonfly.git] / contrib / hostapd / src / eap_server / eap_sim_db.c
blobed0bd3cd133271a377d8def782266735d24beb03
1 /*
2 * hostapd / EAP-SIM database/authenticator gateway
3 * Copyright (c) 2005-2007, Jouni Malinen <j@w1.fi>
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License version 2 as
7 * published by the Free Software Foundation.
9 * Alternatively, this software may be distributed under the terms of BSD
10 * license.
12 * See README and COPYING for more details.
14 * This is an example implementation of the EAP-SIM/AKA database/authentication
15 * gateway interface that is using an external program as an SS7 gateway to
16 * GSM/UMTS authentication center (HLR/AuC). hlr_auc_gw is an example
17 * implementation of such a gateway program. This eap_sim_db.c takes care of
18 * EAP-SIM/AKA pseudonyms and re-auth identities. It can be used with different
19 * gateway implementations for HLR/AuC access. Alternatively, it can also be
20 * completely replaced if the in-memory database of pseudonyms/re-auth
21 * identities is not suitable for some cases.
24 #include "includes.h"
25 #include <sys/un.h>
27 #include "common.h"
28 #include "eap_common/eap_sim_common.h"
29 #include "eap_server/eap_sim_db.h"
30 #include "eloop.h"
32 struct eap_sim_pseudonym {
33 struct eap_sim_pseudonym *next;
34 u8 *identity;
35 size_t identity_len;
36 char *pseudonym;
39 struct eap_sim_db_pending {
40 struct eap_sim_db_pending *next;
41 u8 imsi[20];
42 size_t imsi_len;
43 enum { PENDING, SUCCESS, FAILURE } state;
44 void *cb_session_ctx;
45 struct os_time timestamp;
46 int aka;
47 union {
48 struct {
49 u8 kc[EAP_SIM_MAX_CHAL][EAP_SIM_KC_LEN];
50 u8 sres[EAP_SIM_MAX_CHAL][EAP_SIM_SRES_LEN];
51 u8 rand[EAP_SIM_MAX_CHAL][GSM_RAND_LEN];
52 int num_chal;
53 } sim;
54 struct {
55 u8 rand[EAP_AKA_RAND_LEN];
56 u8 autn[EAP_AKA_AUTN_LEN];
57 u8 ik[EAP_AKA_IK_LEN];
58 u8 ck[EAP_AKA_CK_LEN];
59 u8 res[EAP_AKA_RES_MAX_LEN];
60 size_t res_len;
61 } aka;
62 } u;
65 struct eap_sim_db_data {
66 int sock;
67 char *fname;
68 char *local_sock;
69 void (*get_complete_cb)(void *ctx, void *session_ctx);
70 void *ctx;
71 struct eap_sim_pseudonym *pseudonyms;
72 struct eap_sim_reauth *reauths;
73 struct eap_sim_db_pending *pending;
77 static struct eap_sim_db_pending *
78 eap_sim_db_get_pending(struct eap_sim_db_data *data, const u8 *imsi,
79 size_t imsi_len, int aka)
81 struct eap_sim_db_pending *entry, *prev = NULL;
83 entry = data->pending;
84 while (entry) {
85 if (entry->aka == aka && entry->imsi_len == imsi_len &&
86 os_memcmp(entry->imsi, imsi, imsi_len) == 0) {
87 if (prev)
88 prev->next = entry->next;
89 else
90 data->pending = entry->next;
91 break;
93 prev = entry;
94 entry = entry->next;
96 return entry;
100 static void eap_sim_db_add_pending(struct eap_sim_db_data *data,
101 struct eap_sim_db_pending *entry)
103 entry->next = data->pending;
104 data->pending = entry;
108 static void eap_sim_db_sim_resp_auth(struct eap_sim_db_data *data,
109 const char *imsi, char *buf)
111 char *start, *end, *pos;
112 struct eap_sim_db_pending *entry;
113 int num_chal;
116 * SIM-RESP-AUTH <IMSI> Kc(i):SRES(i):RAND(i) ...
117 * SIM-RESP-AUTH <IMSI> FAILURE
118 * (IMSI = ASCII string, Kc/SRES/RAND = hex string)
121 entry = eap_sim_db_get_pending(data, (u8 *) imsi, os_strlen(imsi), 0);
122 if (entry == NULL) {
123 wpa_printf(MSG_DEBUG, "EAP-SIM DB: No pending entry for the "
124 "received message found");
125 return;
128 start = buf;
129 if (os_strncmp(start, "FAILURE", 7) == 0) {
130 wpa_printf(MSG_DEBUG, "EAP-SIM DB: External server reported "
131 "failure");
132 entry->state = FAILURE;
133 eap_sim_db_add_pending(data, entry);
134 data->get_complete_cb(data->ctx, entry->cb_session_ctx);
135 return;
138 num_chal = 0;
139 while (num_chal < EAP_SIM_MAX_CHAL) {
140 end = os_strchr(start, ' ');
141 if (end)
142 *end = '\0';
144 pos = os_strchr(start, ':');
145 if (pos == NULL)
146 goto parse_fail;
147 *pos = '\0';
148 if (hexstr2bin(start, entry->u.sim.kc[num_chal],
149 EAP_SIM_KC_LEN))
150 goto parse_fail;
152 start = pos + 1;
153 pos = os_strchr(start, ':');
154 if (pos == NULL)
155 goto parse_fail;
156 *pos = '\0';
157 if (hexstr2bin(start, entry->u.sim.sres[num_chal],
158 EAP_SIM_SRES_LEN))
159 goto parse_fail;
161 start = pos + 1;
162 if (hexstr2bin(start, entry->u.sim.rand[num_chal],
163 GSM_RAND_LEN))
164 goto parse_fail;
166 num_chal++;
167 if (end == NULL)
168 break;
169 else
170 start = end + 1;
172 entry->u.sim.num_chal = num_chal;
174 entry->state = SUCCESS;
175 wpa_printf(MSG_DEBUG, "EAP-SIM DB: Authentication data parsed "
176 "successfully - callback");
177 eap_sim_db_add_pending(data, entry);
178 data->get_complete_cb(data->ctx, entry->cb_session_ctx);
179 return;
181 parse_fail:
182 wpa_printf(MSG_DEBUG, "EAP-SIM DB: Failed to parse response string");
183 os_free(entry);
187 static void eap_sim_db_aka_resp_auth(struct eap_sim_db_data *data,
188 const char *imsi, char *buf)
190 char *start, *end;
191 struct eap_sim_db_pending *entry;
194 * AKA-RESP-AUTH <IMSI> <RAND> <AUTN> <IK> <CK> <RES>
195 * AKA-RESP-AUTH <IMSI> FAILURE
196 * (IMSI = ASCII string, RAND/AUTN/IK/CK/RES = hex string)
199 entry = eap_sim_db_get_pending(data, (u8 *) imsi, os_strlen(imsi), 1);
200 if (entry == NULL) {
201 wpa_printf(MSG_DEBUG, "EAP-SIM DB: No pending entry for the "
202 "received message found");
203 return;
206 start = buf;
207 if (os_strncmp(start, "FAILURE", 7) == 0) {
208 wpa_printf(MSG_DEBUG, "EAP-SIM DB: External server reported "
209 "failure");
210 entry->state = FAILURE;
211 eap_sim_db_add_pending(data, entry);
212 data->get_complete_cb(data->ctx, entry->cb_session_ctx);
213 return;
216 end = os_strchr(start, ' ');
217 if (end == NULL)
218 goto parse_fail;
219 *end = '\0';
220 if (hexstr2bin(start, entry->u.aka.rand, EAP_AKA_RAND_LEN))
221 goto parse_fail;
223 start = end + 1;
224 end = os_strchr(start, ' ');
225 if (end == NULL)
226 goto parse_fail;
227 *end = '\0';
228 if (hexstr2bin(start, entry->u.aka.autn, EAP_AKA_AUTN_LEN))
229 goto parse_fail;
231 start = end + 1;
232 end = os_strchr(start, ' ');
233 if (end == NULL)
234 goto parse_fail;
235 *end = '\0';
236 if (hexstr2bin(start, entry->u.aka.ik, EAP_AKA_IK_LEN))
237 goto parse_fail;
239 start = end + 1;
240 end = os_strchr(start, ' ');
241 if (end == NULL)
242 goto parse_fail;
243 *end = '\0';
244 if (hexstr2bin(start, entry->u.aka.ck, EAP_AKA_CK_LEN))
245 goto parse_fail;
247 start = end + 1;
248 end = os_strchr(start, ' ');
249 if (end)
250 *end = '\0';
251 else {
252 end = start;
253 while (*end)
254 end++;
256 entry->u.aka.res_len = (end - start) / 2;
257 if (entry->u.aka.res_len > EAP_AKA_RES_MAX_LEN) {
258 wpa_printf(MSG_DEBUG, "EAP-SIM DB: Too long RES");
259 entry->u.aka.res_len = 0;
260 goto parse_fail;
262 if (hexstr2bin(start, entry->u.aka.res, entry->u.aka.res_len))
263 goto parse_fail;
265 entry->state = SUCCESS;
266 wpa_printf(MSG_DEBUG, "EAP-SIM DB: Authentication data parsed "
267 "successfully - callback");
268 eap_sim_db_add_pending(data, entry);
269 data->get_complete_cb(data->ctx, entry->cb_session_ctx);
270 return;
272 parse_fail:
273 wpa_printf(MSG_DEBUG, "EAP-SIM DB: Failed to parse response string");
274 os_free(entry);
278 static void eap_sim_db_receive(int sock, void *eloop_ctx, void *sock_ctx)
280 struct eap_sim_db_data *data = eloop_ctx;
281 char buf[1000], *pos, *cmd, *imsi;
282 int res;
284 res = recv(sock, buf, sizeof(buf), 0);
285 if (res < 0)
286 return;
287 wpa_hexdump_ascii_key(MSG_MSGDUMP, "EAP-SIM DB: Received from an "
288 "external source", (u8 *) buf, res);
289 if (res == 0)
290 return;
291 if (res >= (int) sizeof(buf))
292 res = sizeof(buf) - 1;
293 buf[res] = '\0';
295 if (data->get_complete_cb == NULL) {
296 wpa_printf(MSG_DEBUG, "EAP-SIM DB: No get_complete_cb "
297 "registered");
298 return;
301 /* <cmd> <IMSI> ... */
303 cmd = buf;
304 pos = os_strchr(cmd, ' ');
305 if (pos == NULL)
306 goto parse_fail;
307 *pos = '\0';
308 imsi = pos + 1;
309 pos = os_strchr(imsi, ' ');
310 if (pos == NULL)
311 goto parse_fail;
312 *pos = '\0';
313 wpa_printf(MSG_DEBUG, "EAP-SIM DB: External response=%s for IMSI %s",
314 cmd, imsi);
316 if (os_strcmp(cmd, "SIM-RESP-AUTH") == 0)
317 eap_sim_db_sim_resp_auth(data, imsi, pos + 1);
318 else if (os_strcmp(cmd, "AKA-RESP-AUTH") == 0)
319 eap_sim_db_aka_resp_auth(data, imsi, pos + 1);
320 else
321 wpa_printf(MSG_INFO, "EAP-SIM DB: Unknown external response "
322 "'%s'", cmd);
323 return;
325 parse_fail:
326 wpa_printf(MSG_DEBUG, "EAP-SIM DB: Failed to parse response string");
330 static int eap_sim_db_open_socket(struct eap_sim_db_data *data)
332 struct sockaddr_un addr;
333 static int counter = 0;
335 if (os_strncmp(data->fname, "unix:", 5) != 0)
336 return -1;
338 data->sock = socket(PF_UNIX, SOCK_DGRAM, 0);
339 if (data->sock < 0) {
340 perror("socket(eap_sim_db)");
341 return -1;
344 os_memset(&addr, 0, sizeof(addr));
345 addr.sun_family = AF_UNIX;
346 os_snprintf(addr.sun_path, sizeof(addr.sun_path),
347 "/tmp/eap_sim_db_%d-%d", getpid(), counter++);
348 data->local_sock = os_strdup(addr.sun_path);
349 if (bind(data->sock, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
350 perror("bind(eap_sim_db)");
351 close(data->sock);
352 data->sock = -1;
353 return -1;
356 os_memset(&addr, 0, sizeof(addr));
357 addr.sun_family = AF_UNIX;
358 os_strlcpy(addr.sun_path, data->fname + 5, sizeof(addr.sun_path));
359 if (connect(data->sock, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
360 perror("connect(eap_sim_db)");
361 wpa_hexdump_ascii(MSG_INFO, "HLR/AuC GW socket",
362 (u8 *) addr.sun_path,
363 os_strlen(addr.sun_path));
364 close(data->sock);
365 data->sock = -1;
366 return -1;
369 eloop_register_read_sock(data->sock, eap_sim_db_receive, data, NULL);
371 return 0;
375 static void eap_sim_db_close_socket(struct eap_sim_db_data *data)
377 if (data->sock >= 0) {
378 eloop_unregister_read_sock(data->sock);
379 close(data->sock);
380 data->sock = -1;
382 if (data->local_sock) {
383 unlink(data->local_sock);
384 os_free(data->local_sock);
385 data->local_sock = NULL;
391 * eap_sim_db_init - Initialize EAP-SIM DB / authentication gateway interface
392 * @config: Configuration data (e.g., file name)
393 * @get_complete_cb: Callback function for reporting availability of triplets
394 * @ctx: Context pointer for get_complete_cb
395 * Returns: Pointer to a private data structure or %NULL on failure
397 void * eap_sim_db_init(const char *config,
398 void (*get_complete_cb)(void *ctx, void *session_ctx),
399 void *ctx)
401 struct eap_sim_db_data *data;
403 data = os_zalloc(sizeof(*data));
404 if (data == NULL)
405 return NULL;
407 data->sock = -1;
408 data->get_complete_cb = get_complete_cb;
409 data->ctx = ctx;
410 data->fname = os_strdup(config);
411 if (data->fname == NULL)
412 goto fail;
414 if (os_strncmp(data->fname, "unix:", 5) == 0) {
415 if (eap_sim_db_open_socket(data))
416 goto fail;
419 return data;
421 fail:
422 eap_sim_db_close_socket(data);
423 os_free(data->fname);
424 os_free(data);
425 return NULL;
429 static void eap_sim_db_free_pseudonym(struct eap_sim_pseudonym *p)
431 os_free(p->identity);
432 os_free(p->pseudonym);
433 os_free(p);
437 static void eap_sim_db_free_reauth(struct eap_sim_reauth *r)
439 os_free(r->identity);
440 os_free(r->reauth_id);
441 os_free(r);
446 * eap_sim_db_deinit - Deinitialize EAP-SIM DB/authentication gw interface
447 * @priv: Private data pointer from eap_sim_db_init()
449 void eap_sim_db_deinit(void *priv)
451 struct eap_sim_db_data *data = priv;
452 struct eap_sim_pseudonym *p, *prev;
453 struct eap_sim_reauth *r, *prevr;
454 struct eap_sim_db_pending *pending, *prev_pending;
456 eap_sim_db_close_socket(data);
457 os_free(data->fname);
459 p = data->pseudonyms;
460 while (p) {
461 prev = p;
462 p = p->next;
463 eap_sim_db_free_pseudonym(prev);
466 r = data->reauths;
467 while (r) {
468 prevr = r;
469 r = r->next;
470 eap_sim_db_free_reauth(prevr);
473 pending = data->pending;
474 while (pending) {
475 prev_pending = pending;
476 pending = pending->next;
477 os_free(prev_pending);
480 os_free(data);
484 static int eap_sim_db_send(struct eap_sim_db_data *data, const char *msg,
485 size_t len)
487 int _errno = 0;
489 if (send(data->sock, msg, len, 0) < 0) {
490 _errno = errno;
491 perror("send[EAP-SIM DB UNIX]");
494 if (_errno == ENOTCONN || _errno == EDESTADDRREQ || _errno == EINVAL ||
495 _errno == ECONNREFUSED) {
496 /* Try to reconnect */
497 eap_sim_db_close_socket(data);
498 if (eap_sim_db_open_socket(data) < 0)
499 return -1;
500 wpa_printf(MSG_DEBUG, "EAP-SIM DB: Reconnected to the "
501 "external server");
502 if (send(data->sock, msg, len, 0) < 0) {
503 perror("send[EAP-SIM DB UNIX]");
504 return -1;
508 return 0;
512 static void eap_sim_db_expire_pending(struct eap_sim_db_data *data)
514 /* TODO: add limit for maximum length for pending list; remove latest
515 * (i.e., last) entry from the list if the limit is reached; could also
516 * use timeout to expire pending entries */
521 * eap_sim_db_get_gsm_triplets - Get GSM triplets
522 * @priv: Private data pointer from eap_sim_db_init()
523 * @identity: User name identity
524 * @identity_len: Length of identity in bytes
525 * @max_chal: Maximum number of triplets
526 * @_rand: Buffer for RAND values
527 * @kc: Buffer for Kc values
528 * @sres: Buffer for SRES values
529 * @cb_session_ctx: Session callback context for get_complete_cb()
530 * Returns: Number of triplets received (has to be less than or equal to
531 * max_chal), -1 (EAP_SIM_DB_FAILURE) on error (e.g., user not found), or
532 * -2 (EAP_SIM_DB_PENDING) if results are not yet available. In this case, the
533 * callback function registered with eap_sim_db_init() will be called once the
534 * results become available.
536 * In most cases, the user name is '1' | IMSI, i.e., 1 followed by the IMSI in
537 * ASCII format.
539 * When using an external server for GSM triplets, this function can always
540 * start a request and return EAP_SIM_DB_PENDING immediately if authentication
541 * triplets are not available. Once the triplets are received, callback
542 * function registered with eap_sim_db_init() is called to notify EAP state
543 * machine to reprocess the message. This eap_sim_db_get_gsm_triplets()
544 * function will then be called again and the newly received triplets will then
545 * be given to the caller.
547 int eap_sim_db_get_gsm_triplets(void *priv, const u8 *identity,
548 size_t identity_len, int max_chal,
549 u8 *_rand, u8 *kc, u8 *sres,
550 void *cb_session_ctx)
552 struct eap_sim_db_data *data = priv;
553 struct eap_sim_db_pending *entry;
554 int len, ret;
555 size_t i;
556 char msg[40];
558 if (identity_len < 2 || identity[0] != EAP_SIM_PERMANENT_PREFIX) {
559 wpa_hexdump_ascii(MSG_DEBUG, "EAP-SIM DB: unexpected identity",
560 identity, identity_len);
561 return EAP_SIM_DB_FAILURE;
563 identity++;
564 identity_len--;
565 for (i = 0; i < identity_len; i++) {
566 if (identity[i] == '@') {
567 identity_len = i;
568 break;
571 if (identity_len + 1 > sizeof(entry->imsi)) {
572 wpa_hexdump_ascii(MSG_DEBUG, "EAP-SIM DB: unexpected identity",
573 identity, identity_len);
574 return EAP_SIM_DB_FAILURE;
576 wpa_hexdump_ascii(MSG_DEBUG, "EAP-SIM DB: Get GSM triplets for IMSI",
577 identity, identity_len);
579 entry = eap_sim_db_get_pending(data, identity, identity_len, 0);
580 if (entry) {
581 int num_chal;
582 if (entry->state == FAILURE) {
583 wpa_printf(MSG_DEBUG, "EAP-SIM DB: Pending entry -> "
584 "failure");
585 os_free(entry);
586 return EAP_SIM_DB_FAILURE;
589 if (entry->state == PENDING) {
590 wpa_printf(MSG_DEBUG, "EAP-SIM DB: Pending entry -> "
591 "still pending");
592 eap_sim_db_add_pending(data, entry);
593 return EAP_SIM_DB_PENDING;
596 wpa_printf(MSG_DEBUG, "EAP-SIM DB: Pending entry -> "
597 "%d challenges", entry->u.sim.num_chal);
598 num_chal = entry->u.sim.num_chal;
599 if (num_chal > max_chal)
600 num_chal = max_chal;
601 os_memcpy(_rand, entry->u.sim.rand, num_chal * GSM_RAND_LEN);
602 os_memcpy(sres, entry->u.sim.sres,
603 num_chal * EAP_SIM_SRES_LEN);
604 os_memcpy(kc, entry->u.sim.kc, num_chal * EAP_SIM_KC_LEN);
605 os_free(entry);
606 return num_chal;
609 if (data->sock < 0) {
610 if (eap_sim_db_open_socket(data) < 0)
611 return EAP_SIM_DB_FAILURE;
614 len = os_snprintf(msg, sizeof(msg), "SIM-REQ-AUTH ");
615 if (len < 0 || len + identity_len >= sizeof(msg))
616 return EAP_SIM_DB_FAILURE;
617 os_memcpy(msg + len, identity, identity_len);
618 len += identity_len;
619 ret = os_snprintf(msg + len, sizeof(msg) - len, " %d", max_chal);
620 if (ret < 0 || (size_t) ret >= sizeof(msg) - len)
621 return EAP_SIM_DB_FAILURE;
622 len += ret;
624 wpa_hexdump(MSG_DEBUG, "EAP-SIM DB: requesting SIM authentication "
625 "data for IMSI", identity, identity_len);
626 if (eap_sim_db_send(data, msg, len) < 0)
627 return EAP_SIM_DB_FAILURE;
629 entry = os_zalloc(sizeof(*entry));
630 if (entry == NULL)
631 return EAP_SIM_DB_FAILURE;
633 os_get_time(&entry->timestamp);
634 os_memcpy(entry->imsi, identity, identity_len);
635 entry->imsi_len = identity_len;
636 entry->cb_session_ctx = cb_session_ctx;
637 entry->state = PENDING;
638 eap_sim_db_add_pending(data, entry);
639 eap_sim_db_expire_pending(data);
641 return EAP_SIM_DB_PENDING;
645 static struct eap_sim_pseudonym *
646 eap_sim_db_get_pseudonym(struct eap_sim_db_data *data, const u8 *identity,
647 size_t identity_len)
649 char *pseudonym;
650 size_t len;
651 struct eap_sim_pseudonym *p;
653 if (identity_len == 0 ||
654 (identity[0] != EAP_SIM_PSEUDONYM_PREFIX &&
655 identity[0] != EAP_AKA_PSEUDONYM_PREFIX))
656 return NULL;
658 /* Remove possible realm from identity */
659 len = 0;
660 while (len < identity_len) {
661 if (identity[len] == '@')
662 break;
663 len++;
666 pseudonym = os_malloc(len + 1);
667 if (pseudonym == NULL)
668 return NULL;
669 os_memcpy(pseudonym, identity, len);
670 pseudonym[len] = '\0';
672 p = data->pseudonyms;
673 while (p) {
674 if (os_strcmp(p->pseudonym, pseudonym) == 0)
675 break;
676 p = p->next;
679 os_free(pseudonym);
681 return p;
685 static struct eap_sim_pseudonym *
686 eap_sim_db_get_pseudonym_id(struct eap_sim_db_data *data, const u8 *identity,
687 size_t identity_len)
689 struct eap_sim_pseudonym *p;
691 if (identity_len == 0 ||
692 (identity[0] != EAP_SIM_PERMANENT_PREFIX &&
693 identity[0] != EAP_AKA_PERMANENT_PREFIX))
694 return NULL;
696 p = data->pseudonyms;
697 while (p) {
698 if (identity_len == p->identity_len &&
699 os_memcmp(p->identity, identity, identity_len) == 0)
700 break;
701 p = p->next;
704 return p;
708 static struct eap_sim_reauth *
709 eap_sim_db_get_reauth(struct eap_sim_db_data *data, const u8 *identity,
710 size_t identity_len)
712 char *reauth_id;
713 size_t len;
714 struct eap_sim_reauth *r;
716 if (identity_len == 0 ||
717 (identity[0] != EAP_SIM_REAUTH_ID_PREFIX &&
718 identity[0] != EAP_AKA_REAUTH_ID_PREFIX))
719 return NULL;
721 /* Remove possible realm from identity */
722 len = 0;
723 while (len < identity_len) {
724 if (identity[len] == '@')
725 break;
726 len++;
729 reauth_id = os_malloc(len + 1);
730 if (reauth_id == NULL)
731 return NULL;
732 os_memcpy(reauth_id, identity, len);
733 reauth_id[len] = '\0';
735 r = data->reauths;
736 while (r) {
737 if (os_strcmp(r->reauth_id, reauth_id) == 0)
738 break;
739 r = r->next;
742 os_free(reauth_id);
744 return r;
748 static struct eap_sim_reauth *
749 eap_sim_db_get_reauth_id(struct eap_sim_db_data *data, const u8 *identity,
750 size_t identity_len)
752 struct eap_sim_pseudonym *p;
753 struct eap_sim_reauth *r;
755 if (identity_len == 0)
756 return NULL;
758 p = eap_sim_db_get_pseudonym(data, identity, identity_len);
759 if (p == NULL)
760 p = eap_sim_db_get_pseudonym_id(data, identity, identity_len);
761 if (p) {
762 identity = p->identity;
763 identity_len = p->identity_len;
766 r = data->reauths;
767 while (r) {
768 if (identity_len == r->identity_len &&
769 os_memcmp(r->identity, identity, identity_len) == 0)
770 break;
771 r = r->next;
774 return r;
779 * eap_sim_db_identity_known - Verify whether the given identity is known
780 * @priv: Private data pointer from eap_sim_db_init()
781 * @identity: User name identity
782 * @identity_len: Length of identity in bytes
783 * Returns: 0 if the user is found or -1 on failure
785 * In most cases, the user name is ['0','1'] | IMSI, i.e., 1 followed by the
786 * IMSI in ASCII format, ['2','3'] | pseudonym, or ['4','5'] | reauth_id.
788 int eap_sim_db_identity_known(void *priv, const u8 *identity,
789 size_t identity_len)
791 struct eap_sim_db_data *data = priv;
793 if (identity == NULL || identity_len < 2)
794 return -1;
796 if (identity[0] == EAP_SIM_PSEUDONYM_PREFIX ||
797 identity[0] == EAP_AKA_PSEUDONYM_PREFIX) {
798 struct eap_sim_pseudonym *p =
799 eap_sim_db_get_pseudonym(data, identity, identity_len);
800 return p ? 0 : -1;
803 if (identity[0] == EAP_SIM_REAUTH_ID_PREFIX ||
804 identity[0] == EAP_AKA_REAUTH_ID_PREFIX) {
805 struct eap_sim_reauth *r =
806 eap_sim_db_get_reauth(data, identity, identity_len);
807 return r ? 0 : -1;
810 if (identity[0] != EAP_SIM_PERMANENT_PREFIX &&
811 identity[0] != EAP_AKA_PERMANENT_PREFIX) {
812 /* Unknown identity prefix */
813 return -1;
816 /* TODO: Should consider asking HLR/AuC gateway whether this permanent
817 * identity is known. If it is, EAP-SIM/AKA can skip identity request.
818 * In case of EAP-AKA, this would reduce number of needed round-trips.
819 * Ideally, this would be done with one wait, i.e., just request
820 * authentication data and store it for the next use. This would then
821 * need to use similar pending-request functionality as the normal
822 * request for authentication data at later phase.
824 return -1;
828 static char * eap_sim_db_get_next(struct eap_sim_db_data *data, char prefix)
830 char *id, *pos, *end;
831 u8 buf[10];
833 if (os_get_random(buf, sizeof(buf)))
834 return NULL;
835 id = os_malloc(sizeof(buf) * 2 + 2);
836 if (id == NULL)
837 return NULL;
839 pos = id;
840 end = id + sizeof(buf) * 2 + 2;
841 *pos++ = prefix;
842 pos += wpa_snprintf_hex(pos, end - pos, buf, sizeof(buf));
844 return id;
849 * eap_sim_db_get_next_pseudonym - EAP-SIM DB: Get next pseudonym
850 * @priv: Private data pointer from eap_sim_db_init()
851 * @aka: Using EAP-AKA instead of EAP-SIM
852 * Returns: Next pseudonym (allocated string) or %NULL on failure
854 * This function is used to generate a pseudonym for EAP-SIM. The returned
855 * pseudonym is not added to database at this point; it will need to be added
856 * with eap_sim_db_add_pseudonym() once the authentication has been completed
857 * successfully. Caller is responsible for freeing the returned buffer.
859 char * eap_sim_db_get_next_pseudonym(void *priv, int aka)
861 struct eap_sim_db_data *data = priv;
862 return eap_sim_db_get_next(data, aka ? EAP_AKA_PSEUDONYM_PREFIX :
863 EAP_SIM_PSEUDONYM_PREFIX);
868 * eap_sim_db_get_next_reauth_id - EAP-SIM DB: Get next reauth_id
869 * @priv: Private data pointer from eap_sim_db_init()
870 * @aka: Using EAP-AKA instead of EAP-SIM
871 * Returns: Next reauth_id (allocated string) or %NULL on failure
873 * This function is used to generate a fast re-authentication identity for
874 * EAP-SIM. The returned reauth_id is not added to database at this point; it
875 * will need to be added with eap_sim_db_add_reauth() once the authentication
876 * has been completed successfully. Caller is responsible for freeing the
877 * returned buffer.
879 char * eap_sim_db_get_next_reauth_id(void *priv, int aka)
881 struct eap_sim_db_data *data = priv;
882 return eap_sim_db_get_next(data, aka ? EAP_AKA_REAUTH_ID_PREFIX :
883 EAP_SIM_REAUTH_ID_PREFIX);
888 * eap_sim_db_add_pseudonym - EAP-SIM DB: Add new pseudonym
889 * @priv: Private data pointer from eap_sim_db_init()
890 * @identity: Identity of the user (may be permanent identity or pseudonym)
891 * @identity_len: Length of identity
892 * @pseudonym: Pseudonym for this user. This needs to be an allocated buffer,
893 * e.g., return value from eap_sim_db_get_next_pseudonym(). Caller must not
894 * free it.
895 * Returns: 0 on success, -1 on failure
897 * This function adds a new pseudonym for EAP-SIM user. EAP-SIM DB is
898 * responsible of freeing pseudonym buffer once it is not needed anymore.
900 int eap_sim_db_add_pseudonym(void *priv, const u8 *identity,
901 size_t identity_len, char *pseudonym)
903 struct eap_sim_db_data *data = priv;
904 struct eap_sim_pseudonym *p;
905 wpa_hexdump_ascii(MSG_DEBUG, "EAP-SIM DB: Add pseudonym for identity",
906 identity, identity_len);
907 wpa_printf(MSG_DEBUG, "EAP-SIM DB: Pseudonym: %s", pseudonym);
909 /* TODO: could store last two pseudonyms */
910 p = eap_sim_db_get_pseudonym(data, identity, identity_len);
911 if (p == NULL)
912 p = eap_sim_db_get_pseudonym_id(data, identity, identity_len);
914 if (p) {
915 wpa_printf(MSG_DEBUG, "EAP-SIM DB: Replacing previous "
916 "pseudonym: %s", p->pseudonym);
917 os_free(p->pseudonym);
918 p->pseudonym = pseudonym;
919 return 0;
922 p = os_zalloc(sizeof(*p));
923 if (p == NULL) {
924 os_free(pseudonym);
925 return -1;
928 p->next = data->pseudonyms;
929 p->identity = os_malloc(identity_len);
930 if (p->identity == NULL) {
931 os_free(p);
932 os_free(pseudonym);
933 return -1;
935 os_memcpy(p->identity, identity, identity_len);
936 p->identity_len = identity_len;
937 p->pseudonym = pseudonym;
938 data->pseudonyms = p;
940 wpa_printf(MSG_DEBUG, "EAP-SIM DB: Added new pseudonym entry");
941 return 0;
945 static struct eap_sim_reauth *
946 eap_sim_db_add_reauth_data(struct eap_sim_db_data *data, const u8 *identity,
947 size_t identity_len, char *reauth_id, u16 counter)
949 struct eap_sim_reauth *r;
951 wpa_hexdump_ascii(MSG_DEBUG, "EAP-SIM DB: Add reauth_id for identity",
952 identity, identity_len);
953 wpa_printf(MSG_DEBUG, "EAP-SIM DB: reauth_id: %s", reauth_id);
955 r = eap_sim_db_get_reauth(data, identity, identity_len);
956 if (r == NULL)
957 r = eap_sim_db_get_reauth_id(data, identity, identity_len);
959 if (r) {
960 wpa_printf(MSG_DEBUG, "EAP-SIM DB: Replacing previous "
961 "reauth_id: %s", r->reauth_id);
962 os_free(r->reauth_id);
963 r->reauth_id = reauth_id;
964 } else {
965 r = os_zalloc(sizeof(*r));
966 if (r == NULL) {
967 os_free(reauth_id);
968 return NULL;
971 r->next = data->reauths;
972 r->identity = os_malloc(identity_len);
973 if (r->identity == NULL) {
974 os_free(r);
975 os_free(reauth_id);
976 return NULL;
978 os_memcpy(r->identity, identity, identity_len);
979 r->identity_len = identity_len;
980 r->reauth_id = reauth_id;
981 data->reauths = r;
982 wpa_printf(MSG_DEBUG, "EAP-SIM DB: Added new reauth entry");
985 r->counter = counter;
987 return r;
992 * eap_sim_db_add_reauth - EAP-SIM DB: Add new re-authentication entry
993 * @priv: Private data pointer from eap_sim_db_init()
994 * @identity: Identity of the user (may be permanent identity or pseudonym)
995 * @identity_len: Length of identity
996 * @reauth_id: reauth_id for this user. This needs to be an allocated buffer,
997 * e.g., return value from eap_sim_db_get_next_reauth_id(). Caller must not
998 * free it.
999 * @counter: AT_COUNTER value for fast re-authentication
1000 * @mk: 16-byte MK from the previous full authentication or %NULL
1001 * Returns: 0 on success, -1 on failure
1003 * This function adds a new re-authentication entry for an EAP-SIM user.
1004 * EAP-SIM DB is responsible of freeing reauth_id buffer once it is not needed
1005 * anymore.
1007 int eap_sim_db_add_reauth(void *priv, const u8 *identity,
1008 size_t identity_len, char *reauth_id, u16 counter,
1009 const u8 *mk)
1011 struct eap_sim_db_data *data = priv;
1012 struct eap_sim_reauth *r;
1014 r = eap_sim_db_add_reauth_data(data, identity, identity_len, reauth_id,
1015 counter);
1016 if (r == NULL)
1017 return -1;
1019 os_memcpy(r->mk, mk, EAP_SIM_MK_LEN);
1020 r->aka_prime = 0;
1022 return 0;
1026 #ifdef EAP_AKA_PRIME
1028 * eap_sim_db_add_reauth_prime - EAP-AKA' DB: Add new re-authentication entry
1029 * @priv: Private data pointer from eap_sim_db_init()
1030 * @identity: Identity of the user (may be permanent identity or pseudonym)
1031 * @identity_len: Length of identity
1032 * @reauth_id: reauth_id for this user. This needs to be an allocated buffer,
1033 * e.g., return value from eap_sim_db_get_next_reauth_id(). Caller must not
1034 * free it.
1035 * @counter: AT_COUNTER value for fast re-authentication
1036 * @k_encr: K_encr from the previous full authentication
1037 * @k_aut: K_aut from the previous full authentication
1038 * @k_re: 32-byte K_re from the previous full authentication
1039 * Returns: 0 on success, -1 on failure
1041 * This function adds a new re-authentication entry for an EAP-AKA' user.
1042 * EAP-SIM DB is responsible of freeing reauth_id buffer once it is not needed
1043 * anymore.
1045 int eap_sim_db_add_reauth_prime(void *priv, const u8 *identity,
1046 size_t identity_len, char *reauth_id,
1047 u16 counter, const u8 *k_encr, const u8 *k_aut,
1048 const u8 *k_re)
1050 struct eap_sim_db_data *data = priv;
1051 struct eap_sim_reauth *r;
1053 r = eap_sim_db_add_reauth_data(data, identity, identity_len, reauth_id,
1054 counter);
1055 if (r == NULL)
1056 return -1;
1058 r->aka_prime = 1;
1059 os_memcpy(r->k_encr, k_encr, EAP_SIM_K_ENCR_LEN);
1060 os_memcpy(r->k_aut, k_aut, EAP_AKA_PRIME_K_AUT_LEN);
1061 os_memcpy(r->k_re, k_re, EAP_AKA_PRIME_K_RE_LEN);
1063 return 0;
1065 #endif /* EAP_AKA_PRIME */
1069 * eap_sim_db_get_permanent - EAP-SIM DB: Get permanent identity
1070 * @priv: Private data pointer from eap_sim_db_init()
1071 * @identity: Identity of the user (may be permanent identity or pseudonym)
1072 * @identity_len: Length of identity
1073 * @len: Buffer for length of the returned permanent identity
1074 * Returns: Pointer to the permanent identity, or %NULL if not found
1076 const u8 * eap_sim_db_get_permanent(void *priv, const u8 *identity,
1077 size_t identity_len, size_t *len)
1079 struct eap_sim_db_data *data = priv;
1080 struct eap_sim_pseudonym *p;
1082 if (identity == NULL)
1083 return NULL;
1085 p = eap_sim_db_get_pseudonym(data, identity, identity_len);
1086 if (p == NULL)
1087 p = eap_sim_db_get_pseudonym_id(data, identity, identity_len);
1088 if (p == NULL)
1089 return NULL;
1091 *len = p->identity_len;
1092 return p->identity;
1097 * eap_sim_db_get_reauth_entry - EAP-SIM DB: Get re-authentication entry
1098 * @priv: Private data pointer from eap_sim_db_init()
1099 * @identity: Identity of the user (may be permanent identity, pseudonym, or
1100 * reauth_id)
1101 * @identity_len: Length of identity
1102 * Returns: Pointer to the re-auth entry, or %NULL if not found
1104 struct eap_sim_reauth *
1105 eap_sim_db_get_reauth_entry(void *priv, const u8 *identity,
1106 size_t identity_len)
1108 struct eap_sim_db_data *data = priv;
1109 struct eap_sim_reauth *r;
1111 if (identity == NULL)
1112 return NULL;
1113 r = eap_sim_db_get_reauth(data, identity, identity_len);
1114 if (r == NULL)
1115 r = eap_sim_db_get_reauth_id(data, identity, identity_len);
1116 return r;
1121 * eap_sim_db_remove_reauth - EAP-SIM DB: Remove re-authentication entry
1122 * @priv: Private data pointer from eap_sim_db_init()
1123 * @reauth: Pointer to re-authentication entry from
1124 * eap_sim_db_get_reauth_entry()
1126 void eap_sim_db_remove_reauth(void *priv, struct eap_sim_reauth *reauth)
1128 struct eap_sim_db_data *data = priv;
1129 struct eap_sim_reauth *r, *prev = NULL;
1130 r = data->reauths;
1131 while (r) {
1132 if (r == reauth) {
1133 if (prev)
1134 prev->next = r->next;
1135 else
1136 data->reauths = r->next;
1137 eap_sim_db_free_reauth(r);
1138 return;
1140 prev = r;
1141 r = r->next;
1147 * eap_sim_db_get_aka_auth - Get AKA authentication values
1148 * @priv: Private data pointer from eap_sim_db_init()
1149 * @identity: User name identity
1150 * @identity_len: Length of identity in bytes
1151 * @_rand: Buffer for RAND value
1152 * @autn: Buffer for AUTN value
1153 * @ik: Buffer for IK value
1154 * @ck: Buffer for CK value
1155 * @res: Buffer for RES value
1156 * @res_len: Buffer for RES length
1157 * @cb_session_ctx: Session callback context for get_complete_cb()
1158 * Returns: 0 on success, -1 (EAP_SIM_DB_FAILURE) on error (e.g., user not
1159 * found), or -2 (EAP_SIM_DB_PENDING) if results are not yet available. In this
1160 * case, the callback function registered with eap_sim_db_init() will be
1161 * called once the results become available.
1163 * In most cases, the user name is '0' | IMSI, i.e., 0 followed by the IMSI in
1164 * ASCII format.
1166 * When using an external server for AKA authentication, this function can
1167 * always start a request and return EAP_SIM_DB_PENDING immediately if
1168 * authentication triplets are not available. Once the authentication data are
1169 * received, callback function registered with eap_sim_db_init() is called to
1170 * notify EAP state machine to reprocess the message. This
1171 * eap_sim_db_get_aka_auth() function will then be called again and the newly
1172 * received triplets will then be given to the caller.
1174 int eap_sim_db_get_aka_auth(void *priv, const u8 *identity,
1175 size_t identity_len, u8 *_rand, u8 *autn, u8 *ik,
1176 u8 *ck, u8 *res, size_t *res_len,
1177 void *cb_session_ctx)
1179 struct eap_sim_db_data *data = priv;
1180 struct eap_sim_db_pending *entry;
1181 int len;
1182 size_t i;
1183 char msg[40];
1185 if (identity_len < 2 || identity == NULL ||
1186 identity[0] != EAP_AKA_PERMANENT_PREFIX) {
1187 wpa_hexdump_ascii(MSG_DEBUG, "EAP-SIM DB: unexpected identity",
1188 identity, identity_len);
1189 return EAP_SIM_DB_FAILURE;
1191 identity++;
1192 identity_len--;
1193 for (i = 0; i < identity_len; i++) {
1194 if (identity[i] == '@') {
1195 identity_len = i;
1196 break;
1199 if (identity_len + 1 > sizeof(entry->imsi)) {
1200 wpa_hexdump_ascii(MSG_DEBUG, "EAP-SIM DB: unexpected identity",
1201 identity, identity_len);
1202 return EAP_SIM_DB_FAILURE;
1204 wpa_hexdump_ascii(MSG_DEBUG, "EAP-SIM DB: Get AKA auth for IMSI",
1205 identity, identity_len);
1207 entry = eap_sim_db_get_pending(data, identity, identity_len, 1);
1208 if (entry) {
1209 if (entry->state == FAILURE) {
1210 os_free(entry);
1211 wpa_printf(MSG_DEBUG, "EAP-SIM DB: Failure");
1212 return EAP_SIM_DB_FAILURE;
1215 if (entry->state == PENDING) {
1216 eap_sim_db_add_pending(data, entry);
1217 wpa_printf(MSG_DEBUG, "EAP-SIM DB: Pending");
1218 return EAP_SIM_DB_PENDING;
1221 wpa_printf(MSG_DEBUG, "EAP-SIM DB: Returning successfully "
1222 "received authentication data");
1223 os_memcpy(_rand, entry->u.aka.rand, EAP_AKA_RAND_LEN);
1224 os_memcpy(autn, entry->u.aka.autn, EAP_AKA_AUTN_LEN);
1225 os_memcpy(ik, entry->u.aka.ik, EAP_AKA_IK_LEN);
1226 os_memcpy(ck, entry->u.aka.ck, EAP_AKA_CK_LEN);
1227 os_memcpy(res, entry->u.aka.res, EAP_AKA_RES_MAX_LEN);
1228 *res_len = entry->u.aka.res_len;
1229 os_free(entry);
1230 return 0;
1233 if (data->sock < 0) {
1234 if (eap_sim_db_open_socket(data) < 0)
1235 return EAP_SIM_DB_FAILURE;
1238 len = os_snprintf(msg, sizeof(msg), "AKA-REQ-AUTH ");
1239 if (len < 0 || len + identity_len >= sizeof(msg))
1240 return EAP_SIM_DB_FAILURE;
1241 os_memcpy(msg + len, identity, identity_len);
1242 len += identity_len;
1244 wpa_hexdump(MSG_DEBUG, "EAP-SIM DB: requesting AKA authentication "
1245 "data for IMSI", identity, identity_len);
1246 if (eap_sim_db_send(data, msg, len) < 0)
1247 return EAP_SIM_DB_FAILURE;
1249 entry = os_zalloc(sizeof(*entry));
1250 if (entry == NULL)
1251 return EAP_SIM_DB_FAILURE;
1253 os_get_time(&entry->timestamp);
1254 entry->aka = 1;
1255 os_memcpy(entry->imsi, identity, identity_len);
1256 entry->imsi_len = identity_len;
1257 entry->cb_session_ctx = cb_session_ctx;
1258 entry->state = PENDING;
1259 eap_sim_db_add_pending(data, entry);
1260 eap_sim_db_expire_pending(data);
1262 return EAP_SIM_DB_PENDING;
1267 * eap_sim_db_resynchronize - Resynchronize AKA AUTN
1268 * @priv: Private data pointer from eap_sim_db_init()
1269 * @identity: User name identity
1270 * @identity_len: Length of identity in bytes
1271 * @auts: AUTS value from the peer
1272 * @_rand: RAND value used in the rejected message
1273 * Returns: 0 on success, -1 on failure
1275 * This function is called when the peer reports synchronization failure in the
1276 * AUTN value by sending AUTS. The AUTS and RAND values should be sent to
1277 * HLR/AuC to allow it to resynchronize with the peer. After this,
1278 * eap_sim_db_get_aka_auth() will be called again to to fetch updated
1279 * RAND/AUTN values for the next challenge.
1281 int eap_sim_db_resynchronize(void *priv, const u8 *identity,
1282 size_t identity_len, const u8 *auts,
1283 const u8 *_rand)
1285 struct eap_sim_db_data *data = priv;
1286 size_t i;
1288 if (identity_len < 2 || identity == NULL ||
1289 identity[0] != EAP_AKA_PERMANENT_PREFIX) {
1290 wpa_hexdump_ascii(MSG_DEBUG, "EAP-SIM DB: unexpected identity",
1291 identity, identity_len);
1292 return -1;
1294 identity++;
1295 identity_len--;
1296 for (i = 0; i < identity_len; i++) {
1297 if (identity[i] == '@') {
1298 identity_len = i;
1299 break;
1302 if (identity_len > 20) {
1303 wpa_hexdump_ascii(MSG_DEBUG, "EAP-SIM DB: unexpected identity",
1304 identity, identity_len);
1305 return -1;
1308 if (data->sock >= 0) {
1309 char msg[100];
1310 int len, ret;
1312 len = os_snprintf(msg, sizeof(msg), "AKA-AUTS ");
1313 if (len < 0 || len + identity_len >= sizeof(msg))
1314 return -1;
1315 os_memcpy(msg + len, identity, identity_len);
1316 len += identity_len;
1318 ret = os_snprintf(msg + len, sizeof(msg) - len, " ");
1319 if (ret < 0 || (size_t) ret >= sizeof(msg) - len)
1320 return -1;
1321 len += ret;
1322 len += wpa_snprintf_hex(msg + len, sizeof(msg) - len,
1323 auts, EAP_AKA_AUTS_LEN);
1324 ret = os_snprintf(msg + len, sizeof(msg) - len, " ");
1325 if (ret < 0 || (size_t) ret >= sizeof(msg) - len)
1326 return -1;
1327 len += ret;
1328 len += wpa_snprintf_hex(msg + len, sizeof(msg) - len,
1329 _rand, EAP_AKA_RAND_LEN);
1330 wpa_hexdump(MSG_DEBUG, "EAP-SIM DB: reporting AKA AUTS for "
1331 "IMSI", identity, identity_len);
1332 if (eap_sim_db_send(data, msg, len) < 0)
1333 return -1;
1336 return 0;