]> CyberLeo.Net >> Repos - FreeBSD/releng/7.2.git/blob - contrib/hostapd/eap_sim_db.c
Create releng/7.2 from stable/7 in preparation for 7.2-RELEASE.
[FreeBSD/releng/7.2.git] / contrib / hostapd / eap_sim_db.c
1 /*
2  * hostapd / EAP-SIM database/authenticator gateway
3  * Copyright (c) 2005-2006, Jouni Malinen <j@w1.fi>
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License version 2 as
7  * published by the Free Software Foundation.
8  *
9  * Alternatively, this software may be distributed under the terms of BSD
10  * license.
11  *
12  * See README and COPYING for more details.
13  *
14  * This is an example implementation of the EAP-SIM/AKA database/authentication
15  * gateway interface that is using an external program as an SS7 gateway to
16  * GSM/UMTS authentication center (HLR/AuC). hlr_auc_gw is an example
17  * implementation of such a gateway program. This eap_sim_db.c takes care of
18  * EAP-SIM/AKA pseudonyms and re-auth identities. It can be used with different
19  * gateway implementations for HLR/AuC access. Alternatively, it can also be
20  * completely replaced if the in-memory database of pseudonyms/re-auth
21  * identities is not suitable for some cases.
22  */
23
24 #include "includes.h"
25 #include <sys/un.h>
26
27 #include "common.h"
28 #include "eap_sim_common.h"
29 #include "eap_sim_db.h"
30 #include "eloop.h"
31
32 struct eap_sim_pseudonym {
33         struct eap_sim_pseudonym *next;
34         u8 *identity;
35         size_t identity_len;
36         char *pseudonym;
37 };
38
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;
63 };
64
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;
74 };
75
76
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)
80 {
81         struct eap_sim_db_pending *entry, *prev = NULL;
82
83         entry = data->pending;
84         while (entry) {
85                 if (entry->aka == aka && entry->imsi_len == imsi_len &&
86                     memcmp(entry->imsi, imsi, imsi_len) == 0) {
87                         if (prev)
88                                 prev->next = entry->next;
89                         else
90                                 data->pending = entry->next;
91                         break;
92                 }
93                 prev = entry;
94                 entry = entry->next;
95         }
96         return entry;
97 }
98
99
100 static void eap_sim_db_add_pending(struct eap_sim_db_data *data,
101                                    struct eap_sim_db_pending *entry)
102 {
103         entry->next = data->pending;
104         data->pending = entry;
105 }
106
107
108 static void eap_sim_db_sim_resp_auth(struct eap_sim_db_data *data,
109                                      const char *imsi, char *buf)
110 {
111         char *start, *end, *pos;
112         struct eap_sim_db_pending *entry;
113         int num_chal;
114
115         /*
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)
119          */
120
121         entry = eap_sim_db_get_pending(data, (u8 *) imsi, 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;
126         }
127
128         start = buf;
129         if (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;
136         }
137
138         num_chal = 0;
139         while (num_chal < EAP_SIM_MAX_CHAL) {
140                 end = strchr(start, ' ');
141                 if (end)
142                         *end = '\0';
143
144                 pos = 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;
151
152                 start = pos + 1;
153                 pos = 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;
160
161                 start = pos + 1;
162                 if (hexstr2bin(start, entry->u.sim.rand[num_chal],
163                                GSM_RAND_LEN))
164                         goto parse_fail;
165
166                 num_chal++;
167                 if (end == NULL)
168                         break;
169                 else
170                         start = end + 1;
171         }
172         entry->u.sim.num_chal = num_chal;
173
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;
180
181 parse_fail:
182         wpa_printf(MSG_DEBUG, "EAP-SIM DB: Failed to parse response string");
183         free(entry);
184 }
185
186
187 static void eap_sim_db_aka_resp_auth(struct eap_sim_db_data *data,
188                                      const char *imsi, char *buf)
189 {
190         char *start, *end;
191         struct eap_sim_db_pending *entry;
192
193         /*
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)
197          */
198
199         entry = eap_sim_db_get_pending(data, (u8 *) imsi, 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;
204         }
205
206         start = buf;
207         if (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;
214         }
215
216         end = 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;
222
223         start = end + 1;
224         end = 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;
230
231         start = end + 1;
232         end = 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;
238
239         start = end + 1;
240         end = 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;
246
247         start = end + 1;
248         end = strchr(start, ' ');
249         if (end)
250                 *end = '\0';
251         else {
252                 end = start;
253                 while (*end)
254                         end++;
255         }
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;
261         }
262         if (hexstr2bin(start, entry->u.aka.res, entry->u.aka.res_len))
263                 goto parse_fail;
264
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;
271
272 parse_fail:
273         wpa_printf(MSG_DEBUG, "EAP-SIM DB: Failed to parse response string");
274         free(entry);
275 }
276
277
278 static void eap_sim_db_receive(int sock, void *eloop_ctx, void *sock_ctx)
279 {
280         struct eap_sim_db_data *data = eloop_ctx;
281         char buf[1000], *pos, *cmd, *imsi;
282         int res;
283
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';
294
295         if (data->get_complete_cb == NULL) {
296                 wpa_printf(MSG_DEBUG, "EAP-SIM DB: No get_complete_cb "
297                            "registered");
298                 return;
299         }
300
301         /* <cmd> <IMSI> ... */
302
303         cmd = buf;
304         pos = strchr(cmd, ' ');
305         if (pos == NULL)
306                 goto parse_fail;
307         *pos = '\0';
308         imsi = pos + 1;
309         pos = 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);
315
316         if (strcmp(cmd, "SIM-RESP-AUTH") == 0)
317                 eap_sim_db_sim_resp_auth(data, imsi, pos + 1);
318         else if (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;
324
325 parse_fail:
326         wpa_printf(MSG_DEBUG, "EAP-SIM DB: Failed to parse response string");
327 }
328
329
330 static int eap_sim_db_open_socket(struct eap_sim_db_data *data)
331 {
332         struct sockaddr_un addr;
333         static int counter = 0;
334
335         if (strncmp(data->fname, "unix:", 5) != 0)
336                 return -1;
337
338         data->sock = socket(PF_UNIX, SOCK_DGRAM, 0);
339         if (data->sock < 0) {
340                 perror("socket(eap_sim_db)");
341                 return -1;
342         }
343
344         memset(&addr, 0, sizeof(addr));
345         addr.sun_family = AF_UNIX;
346         snprintf(addr.sun_path, sizeof(addr.sun_path),
347                  "/tmp/eap_sim_db_%d-%d", getpid(), counter++);
348         data->local_sock = 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;
354         }
355
356         memset(&addr, 0, sizeof(addr));
357         addr.sun_family = AF_UNIX;
358         snprintf(addr.sun_path, sizeof(addr.sun_path), "%s", data->fname + 5);
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, strlen(addr.sun_path));
363                 close(data->sock);
364                 data->sock = -1;
365                 return -1;
366         }
367
368         eloop_register_read_sock(data->sock, eap_sim_db_receive, data, NULL);
369
370         return 0;
371 }
372
373
374 static void eap_sim_db_close_socket(struct eap_sim_db_data *data)
375 {
376         if (data->sock >= 0) {
377                 eloop_unregister_read_sock(data->sock);
378                 close(data->sock);
379                 data->sock = -1;
380         }
381         if (data->local_sock) {
382                 unlink(data->local_sock);
383                 free(data->local_sock);
384                 data->local_sock = NULL;
385         }
386 }
387
388
389 /**
390  * eap_sim_db_init - Initialize EAP-SIM DB / authentication gateway interface
391  * @config: Configuration data (e.g., file name)
392  * @get_complete_cb: Callback function for reporting availability of triplets
393  * @ctx: Context pointer for get_complete_cb
394  * Returns: Pointer to a private data structure or %NULL on failure
395  */
396 void * eap_sim_db_init(const char *config,
397                        void (*get_complete_cb)(void *ctx, void *session_ctx),
398                        void *ctx)
399 {
400         struct eap_sim_db_data *data;
401
402         data = wpa_zalloc(sizeof(*data));
403         if (data == NULL)
404                 return NULL;
405
406         data->sock = -1;
407         data->get_complete_cb = get_complete_cb;
408         data->ctx = ctx;
409         data->fname = strdup(config);
410         if (data->fname == NULL)
411                 goto fail;
412
413         if (strncmp(data->fname, "unix:", 5) == 0) {
414                 if (eap_sim_db_open_socket(data))
415                         goto fail;
416         }
417
418         return data;
419
420 fail:
421         eap_sim_db_close_socket(data);
422         free(data->fname);
423         free(data);
424         return NULL;
425 }
426
427
428 static void eap_sim_db_free_pseudonym(struct eap_sim_pseudonym *p)
429 {
430         free(p->identity);
431         free(p->pseudonym);
432         free(p);
433 }
434
435
436 static void eap_sim_db_free_reauth(struct eap_sim_reauth *r)
437 {
438         free(r->identity);
439         free(r->reauth_id);
440         free(r);
441 }
442
443
444 /**
445  * eap_sim_db_deinit - Deinitialize EAP-SIM DB/authentication gw interface
446  * @priv: Private data pointer from eap_sim_db_init()
447  */
448 void eap_sim_db_deinit(void *priv)
449 {
450         struct eap_sim_db_data *data = priv;
451         struct eap_sim_pseudonym *p, *prev;
452         struct eap_sim_reauth *r, *prevr;
453         struct eap_sim_db_pending *pending, *prev_pending;
454
455         eap_sim_db_close_socket(data);
456         free(data->fname);
457
458         p = data->pseudonyms;
459         while (p) {
460                 prev = p;
461                 p = p->next;
462                 eap_sim_db_free_pseudonym(prev);
463         }
464
465         r = data->reauths;
466         while (r) {
467                 prevr = r;
468                 r = r->next;
469                 eap_sim_db_free_reauth(prevr);
470         }
471
472         pending = data->pending;
473         while (pending) {
474                 prev_pending = pending;
475                 pending = pending->next;
476                 free(prev_pending);
477         }
478
479         free(data);
480 }
481
482
483 static int eap_sim_db_send(struct eap_sim_db_data *data, const char *msg,
484                            size_t len)
485 {
486         int _errno = 0;
487
488         if (send(data->sock, msg, len, 0) < 0) {
489                 _errno = errno;
490                 perror("send[EAP-SIM DB UNIX]");
491         }
492
493         if (_errno == ENOTCONN || _errno == EDESTADDRREQ || _errno == EINVAL ||
494             _errno == ECONNREFUSED) {
495                 /* Try to reconnect */
496                 eap_sim_db_close_socket(data);
497                 if (eap_sim_db_open_socket(data) < 0)
498                         return -1;
499                 wpa_printf(MSG_DEBUG, "EAP-SIM DB: Reconnected to the "
500                            "external server");
501                 if (send(data->sock, msg, len, 0) < 0) {
502                         perror("send[EAP-SIM DB UNIX]");
503                         return -1;
504                 }
505         }
506
507         return 0;
508 }
509
510
511 static void eap_sim_db_expire_pending(struct eap_sim_db_data *data)
512 {
513         /* TODO: add limit for maximum length for pending list; remove latest
514          * (i.e., last) entry from the list if the limit is reached; could also
515          * use timeout to expire pending entries */
516 }
517
518
519 /**
520  * eap_sim_db_get_gsm_triplets - Get GSM triplets
521  * @priv: Private data pointer from eap_sim_db_init()
522  * @identity: User name identity
523  * @identity_len: Length of identity in bytes
524  * @max_chal: Maximum number of triplets
525  * @_rand: Buffer for RAND values
526  * @kc: Buffer for Kc values
527  * @sres: Buffer for SRES values
528  * @cb_session_ctx: Session callback context for get_complete_cb()
529  * Returns: Number of triplets received (has to be less than or equal to
530  * max_chal), -1 (EAP_SIM_DB_FAILURE) on error (e.g., user not found), or
531  * -2 (EAP_SIM_DB_PENDING) if results are not yet available. In this case, the
532  * callback function registered with eap_sim_db_init() will be called once the
533  * results become available.
534  *
535  * In most cases, the user name is '1' | IMSI, i.e., 1 followed by the IMSI in
536  * ASCII format.
537  *
538  * When using an external server for GSM triplets, this function can always
539  * start a request and return EAP_SIM_DB_PENDING immediately if authentication
540  * triplets are not available. Once the triplets are received, callback
541  * function registered with eap_sim_db_init() is called to notify EAP state
542  * machine to reprocess the message. This eap_sim_db_get_gsm_triplets()
543  * function will then be called again and the newly received triplets will then
544  * be given to the caller.
545  */
546 int eap_sim_db_get_gsm_triplets(void *priv, const u8 *identity,
547                                 size_t identity_len, int max_chal,
548                                 u8 *_rand, u8 *kc, u8 *sres,
549                                 void *cb_session_ctx)
550 {
551         struct eap_sim_db_data *data = priv;
552         struct eap_sim_db_pending *entry;
553         int len, ret;
554         size_t i;
555         char msg[40];
556
557         if (identity_len < 2 || identity[0] != EAP_SIM_PERMANENT_PREFIX) {
558                 wpa_hexdump_ascii(MSG_DEBUG, "EAP-SIM DB: unexpected identity",
559                                   identity, identity_len);
560                 return EAP_SIM_DB_FAILURE;
561         }
562         identity++;
563         identity_len--;
564         for (i = 0; i < identity_len; i++) {
565                 if (identity[i] == '@') {
566                         identity_len = i;
567                         break;
568                 }
569         }
570         if (identity_len + 1 > sizeof(entry->imsi)) {
571                 wpa_hexdump_ascii(MSG_DEBUG, "EAP-SIM DB: unexpected identity",
572                                   identity, identity_len);
573                 return EAP_SIM_DB_FAILURE;
574         }
575         wpa_hexdump_ascii(MSG_DEBUG, "EAP-SIM DB: Get GSM triplets for IMSI",
576                           identity, identity_len);
577
578         entry = eap_sim_db_get_pending(data, identity, identity_len, 0);
579         if (entry) {
580                 int num_chal;
581                 if (entry->state == FAILURE) {
582                         wpa_printf(MSG_DEBUG, "EAP-SIM DB: Pending entry -> "
583                                    "failure");
584                         free(entry);
585                         return EAP_SIM_DB_FAILURE;
586                 }
587
588                 if (entry->state == PENDING) {
589                         wpa_printf(MSG_DEBUG, "EAP-SIM DB: Pending entry -> "
590                                    "still pending");
591                         eap_sim_db_add_pending(data, entry);
592                         return EAP_SIM_DB_PENDING;
593                 }
594
595                 wpa_printf(MSG_DEBUG, "EAP-SIM DB: Pending entry -> "
596                            "%d challenges", entry->u.sim.num_chal);
597                 num_chal = entry->u.sim.num_chal;
598                 if (num_chal > max_chal)
599                         num_chal = max_chal;
600                 memcpy(_rand, entry->u.sim.rand, num_chal * GSM_RAND_LEN);
601                 memcpy(sres, entry->u.sim.sres, num_chal * EAP_SIM_SRES_LEN);
602                 memcpy(kc, entry->u.sim.kc, num_chal * EAP_SIM_KC_LEN);
603                 free(entry);
604                 return num_chal;
605         }
606
607         if (data->sock < 0) {
608                 if (eap_sim_db_open_socket(data) < 0)
609                         return EAP_SIM_DB_FAILURE;
610         }
611
612         len = snprintf(msg, sizeof(msg), "SIM-REQ-AUTH ");
613         if (len < 0 || len + identity_len >= sizeof(msg))
614                 return EAP_SIM_DB_FAILURE;
615         memcpy(msg + len, identity, identity_len);
616         len += identity_len;
617         ret = snprintf(msg + len, sizeof(msg) - len, " %d", max_chal);
618         if (ret < 0 || (size_t) ret >= sizeof(msg) - len)
619                 return EAP_SIM_DB_FAILURE;
620         len += ret;
621
622         wpa_hexdump(MSG_DEBUG, "EAP-SIM DB: requesting SIM authentication "
623                     "data for IMSI", identity, identity_len);
624         if (eap_sim_db_send(data, msg, len) < 0)
625                 return EAP_SIM_DB_FAILURE;
626
627         entry = wpa_zalloc(sizeof(*entry));
628         if (entry == NULL)
629                 return EAP_SIM_DB_FAILURE;
630
631         os_get_time(&entry->timestamp);
632         memcpy(entry->imsi, identity, identity_len);
633         entry->imsi_len = identity_len;
634         entry->cb_session_ctx = cb_session_ctx;
635         entry->state = PENDING;
636         eap_sim_db_add_pending(data, entry);
637         eap_sim_db_expire_pending(data);
638
639         return EAP_SIM_DB_PENDING;
640 }
641
642
643 static struct eap_sim_pseudonym *
644 eap_sim_db_get_pseudonym(struct eap_sim_db_data *data, const u8 *identity,
645                          size_t identity_len)
646 {
647         char *pseudonym;
648         size_t len;
649         struct eap_sim_pseudonym *p;
650
651         if (identity_len == 0 ||
652             (identity[0] != EAP_SIM_PSEUDONYM_PREFIX &&
653              identity[0] != EAP_AKA_PSEUDONYM_PREFIX))
654                 return NULL;
655
656         /* Remove possible realm from identity */
657         len = 0;
658         while (len < identity_len) {
659                 if (identity[len] == '@')
660                         break;
661                 len++;
662         }
663
664         pseudonym = malloc(len + 1);
665         if (pseudonym == NULL)
666                 return NULL;
667         memcpy(pseudonym, identity, len);
668         pseudonym[len] = '\0';
669
670         p = data->pseudonyms;
671         while (p) {
672                 if (strcmp(p->pseudonym, pseudonym) == 0)
673                         break;
674                 p = p->next;
675         }
676
677         free(pseudonym);
678
679         return p;
680 }
681
682
683 static struct eap_sim_pseudonym *
684 eap_sim_db_get_pseudonym_id(struct eap_sim_db_data *data, const u8 *identity,
685                             size_t identity_len)
686 {
687         struct eap_sim_pseudonym *p;
688
689         if (identity_len == 0 ||
690             (identity[0] != EAP_SIM_PERMANENT_PREFIX &&
691              identity[0] != EAP_AKA_PERMANENT_PREFIX))
692                 return NULL;
693
694         p = data->pseudonyms;
695         while (p) {
696                 if (identity_len == p->identity_len &&
697                     memcmp(p->identity, identity, identity_len) == 0)
698                         break;
699                 p = p->next;
700         }
701
702         return p;
703 }
704
705
706 static struct eap_sim_reauth *
707 eap_sim_db_get_reauth(struct eap_sim_db_data *data, const u8 *identity,
708                       size_t identity_len)
709 {
710         char *reauth_id;
711         size_t len;
712         struct eap_sim_reauth *r;
713
714         if (identity_len == 0 ||
715             (identity[0] != EAP_SIM_REAUTH_ID_PREFIX &&
716              identity[0] != EAP_AKA_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 = malloc(len + 1);
728         if (reauth_id == NULL)
729                 return NULL;
730         memcpy(reauth_id, identity, len);
731         reauth_id[len] = '\0';
732
733         r = data->reauths;
734         while (r) {
735                 if (strcmp(r->reauth_id, reauth_id) == 0)
736                         break;
737                 r = r->next;
738         }
739
740         free(reauth_id);
741
742         return r;
743 }
744
745
746 static struct eap_sim_reauth *
747 eap_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                     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'] | IMSI, i.e., 1 followed by the
784  * IMSI in ASCII format, ['2','3'] | pseudonym, or ['4','5'] | reauth_id.
785  */
786 int eap_sim_db_identity_known(void *priv, const u8 *identity,
787                               size_t identity_len)
788 {
789         struct eap_sim_db_data *data = priv;
790
791         if (identity == NULL || identity_len < 2)
792                 return -1;
793
794         if (identity[0] == EAP_SIM_PSEUDONYM_PREFIX ||
795             identity[0] == EAP_AKA_PSEUDONYM_PREFIX) {
796                 struct eap_sim_pseudonym *p =
797                         eap_sim_db_get_pseudonym(data, identity, identity_len);
798                 return p ? 0 : -1;
799         }
800
801         if (identity[0] == EAP_SIM_REAUTH_ID_PREFIX ||
802             identity[0] == EAP_AKA_REAUTH_ID_PREFIX) {
803                 struct eap_sim_reauth *r =
804                         eap_sim_db_get_reauth(data, identity, identity_len);
805                 return r ? 0 : -1;
806         }
807
808         if (identity[0] != EAP_SIM_PERMANENT_PREFIX &&
809             identity[0] != EAP_AKA_PERMANENT_PREFIX) {
810                 /* Unknown identity prefix */
811                 return -1;
812         }
813
814         /* TODO: Should consider asking HLR/AuC gateway whether this permanent
815          * identity is known. If it is, EAP-SIM/AKA can skip identity request.
816          * In case of EAP-AKA, this would reduce number of needed round-trips.
817          * Ideally, this would be done with one wait, i.e., just request
818          * authentication data and store it for the next use. This would then
819          * need to use similar pending-request functionality as the normal
820          * request for authentication data at later phase.
821          */
822         return -1;
823 }
824
825
826 static char * eap_sim_db_get_next(struct eap_sim_db_data *data, char prefix)
827 {
828         char *id, *pos, *end;
829         u8 buf[10];
830
831         if (hostapd_get_rand(buf, sizeof(buf)))
832                 return NULL;
833         id = malloc(sizeof(buf) * 2 + 2);
834         if (id == NULL)
835                 return NULL;
836
837         pos = id;
838         end = id + sizeof(buf) * 2 + 2;
839         *pos++ = prefix;
840         pos += wpa_snprintf_hex(pos, end - pos, buf, sizeof(buf));
841         
842         return id;
843 }
844
845
846 /**
847  * eap_sim_db_get_next_pseudonym - EAP-SIM DB: Get next pseudonym
848  * @priv: Private data pointer from eap_sim_db_init()
849  * @aka: Using EAP-AKA instead of EAP-SIM
850  * Returns: Next pseudonym (allocated string) or %NULL on failure
851  *
852  * This function is used to generate a pseudonym for EAP-SIM. The returned
853  * pseudonym is not added to database at this point; it will need to be added
854  * with eap_sim_db_add_pseudonym() once the authentication has been completed
855  * successfully. Caller is responsible for freeing the returned buffer.
856  */
857 char * eap_sim_db_get_next_pseudonym(void *priv, int aka)
858 {
859         struct eap_sim_db_data *data = priv;
860         return eap_sim_db_get_next(data, aka ? EAP_AKA_PSEUDONYM_PREFIX :
861                                    EAP_SIM_PSEUDONYM_PREFIX);
862 }
863
864
865 /**
866  * eap_sim_db_get_next_reauth_id - EAP-SIM DB: Get next reauth_id
867  * @priv: Private data pointer from eap_sim_db_init()
868  * @aka: Using EAP-AKA instead of EAP-SIM
869  * Returns: Next reauth_id (allocated string) or %NULL on failure
870  *
871  * This function is used to generate a fast re-authentication identity for
872  * EAP-SIM. The returned reauth_id is not added to database at this point; it
873  * will need to be added with eap_sim_db_add_reauth() once the authentication
874  * has been completed successfully. Caller is responsible for freeing the
875  * returned buffer.
876  */
877 char * eap_sim_db_get_next_reauth_id(void *priv, int aka)
878 {
879         struct eap_sim_db_data *data = priv;
880         return eap_sim_db_get_next(data, aka ? EAP_AKA_REAUTH_ID_PREFIX :
881                                    EAP_SIM_REAUTH_ID_PREFIX);
882 }
883
884
885 /**
886  * eap_sim_db_add_pseudonym - EAP-SIM DB: Add new pseudonym
887  * @priv: Private data pointer from eap_sim_db_init()
888  * @identity: Identity of the user (may be permanent identity or pseudonym)
889  * @identity_len: Length of identity
890  * @pseudonym: Pseudonym for this user. This needs to be an allocated buffer,
891  * e.g., return value from eap_sim_db_get_next_pseudonym(). Caller must not
892  * free it.
893  * Returns: 0 on success, -1 on failure
894  *
895  * This function adds a new pseudonym for EAP-SIM user. EAP-SIM DB is
896  * responsible of freeing pseudonym buffer once it is not needed anymore.
897  */
898 int eap_sim_db_add_pseudonym(void *priv, const u8 *identity,
899                              size_t identity_len, char *pseudonym)
900 {
901         struct eap_sim_db_data *data = priv;
902         struct eap_sim_pseudonym *p;
903         wpa_hexdump_ascii(MSG_DEBUG, "EAP-SIM DB: Add pseudonym for identity",
904                           identity, identity_len);
905         wpa_printf(MSG_DEBUG, "EAP-SIM DB: Pseudonym: %s", pseudonym);
906
907         /* TODO: could store last two pseudonyms */
908         p = eap_sim_db_get_pseudonym(data, identity, identity_len);
909         if (p == NULL)
910                 p = eap_sim_db_get_pseudonym_id(data, identity, identity_len);
911
912         if (p) {
913                 wpa_printf(MSG_DEBUG, "EAP-SIM DB: Replacing previous "
914                            "pseudonym: %s", p->pseudonym);
915                 free(p->pseudonym);
916                 p->pseudonym = pseudonym;
917                 return 0;
918         }
919
920         p = wpa_zalloc(sizeof(*p));
921         if (p == NULL) {
922                 free(pseudonym);
923                 return -1;
924         }
925
926         p->next = data->pseudonyms;
927         p->identity = malloc(identity_len);
928         if (p->identity == NULL) {
929                 free(p);
930                 free(pseudonym);
931                 return -1;
932         }
933         memcpy(p->identity, identity, identity_len);
934         p->identity_len = identity_len;
935         p->pseudonym = pseudonym;
936         data->pseudonyms = p;
937
938         wpa_printf(MSG_DEBUG, "EAP-SIM DB: Added new pseudonym entry");
939         return 0;
940 }
941
942
943 /**
944  * eap_sim_db_add_reauth - EAP-SIM DB: Add new re-authentication entry
945  * @priv: Private data pointer from eap_sim_db_init()
946  * @identity: Identity of the user (may be permanent identity or pseudonym)
947  * @identity_len: Length of identity
948  * @reauth_id: reauth_id for this user. This needs to be an allocated buffer,
949  * e.g., return value from eap_sim_db_get_next_reauth_id(). Caller must not
950  * free it.
951  * @mk: 16-byte MK from the previous full authentication
952  * Returns: 0 on success, -1 on failure
953  *
954  * This function adds a new re-authentication entry for an EAP-SIM user.
955  * EAP-SIM DB is responsible of freeing reauth_id buffer once it is not needed
956  * anymore.
957  */
958 int eap_sim_db_add_reauth(void *priv, const u8 *identity,
959                           size_t identity_len, char *reauth_id, u16 counter,
960                           const u8 *mk)
961 {
962         struct eap_sim_db_data *data = priv;
963         struct eap_sim_reauth *r;
964         wpa_hexdump_ascii(MSG_DEBUG, "EAP-SIM DB: Add reauth_id for identity",
965                           identity, identity_len);
966         wpa_printf(MSG_DEBUG, "EAP-SIM DB: reauth_id: %s", reauth_id);
967
968         r = eap_sim_db_get_reauth(data, identity, identity_len);
969         if (r == NULL)
970                 r = eap_sim_db_get_reauth_id(data, identity, identity_len);
971
972         if (r) {
973                 wpa_printf(MSG_DEBUG, "EAP-SIM DB: Replacing previous "
974                            "reauth_id: %s", r->reauth_id);
975                 free(r->reauth_id);
976                 r->reauth_id = reauth_id;
977         } else {
978                 r = wpa_zalloc(sizeof(*r));
979                 if (r == NULL) {
980                         free(reauth_id);
981                         return -1;
982                 }
983
984                 r->next = data->reauths;
985                 r->identity = malloc(identity_len);
986                 if (r->identity == NULL) {
987                         free(r);
988                         free(reauth_id);
989                         return -1;
990                 }
991                 memcpy(r->identity, identity, identity_len);
992                 r->identity_len = identity_len;
993                 r->reauth_id = reauth_id;
994                 data->reauths = r;
995                 wpa_printf(MSG_DEBUG, "EAP-SIM DB: Added new reauth entry");
996         }
997
998         r->counter = counter;
999         memcpy(r->mk, mk, EAP_SIM_MK_LEN);
1000
1001         return 0;
1002 }
1003
1004
1005 /**
1006  * eap_sim_db_get_permanent - EAP-SIM DB: Get permanent identity
1007  * @priv: Private data pointer from eap_sim_db_init()
1008  * @identity: Identity of the user (may be permanent identity or pseudonym)
1009  * @identity_len: Length of identity
1010  * @len: Buffer for length of the returned permanent identity
1011  * Returns: Pointer to the permanent identity, or %NULL if not found
1012  */
1013 const u8 * eap_sim_db_get_permanent(void *priv, const u8 *identity,
1014                                     size_t identity_len, size_t *len)
1015 {
1016         struct eap_sim_db_data *data = priv;
1017         struct eap_sim_pseudonym *p;
1018
1019         if (identity == NULL)
1020                 return NULL;
1021
1022         p = eap_sim_db_get_pseudonym(data, identity, identity_len);
1023         if (p == NULL)
1024                 p = eap_sim_db_get_pseudonym_id(data, identity, identity_len);
1025         if (p == NULL)
1026                 return NULL;
1027
1028         *len = p->identity_len;
1029         return p->identity;
1030 }
1031
1032
1033 /**
1034  * eap_sim_db_get_reauth_entry - EAP-SIM DB: Get re-authentication entry
1035  * @priv: Private data pointer from eap_sim_db_init()
1036  * @identity: Identity of the user (may be permanent identity, pseudonym, or
1037  * reauth_id)
1038  * @identity_len: Length of identity
1039  * @len: Buffer for length of the returned permanent identity
1040  * Returns: Pointer to the re-auth entry, or %NULL if not found
1041  */
1042 struct eap_sim_reauth *
1043 eap_sim_db_get_reauth_entry(void *priv, const u8 *identity,
1044                             size_t identity_len)
1045 {
1046         struct eap_sim_db_data *data = priv;
1047         struct eap_sim_reauth *r;
1048
1049         if (identity == NULL)
1050                 return NULL;
1051         r = eap_sim_db_get_reauth(data, identity, identity_len);
1052         if (r == NULL)
1053                 r = eap_sim_db_get_reauth_id(data, identity, identity_len);
1054         return r;
1055 }
1056
1057
1058 /**
1059  * eap_sim_db_remove_reauth - EAP-SIM DB: Remove re-authentication entry
1060  * @priv: Private data pointer from eap_sim_db_init()
1061  * @reauth: Pointer to re-authentication entry from
1062  * eap_sim_db_get_reauth_entry()
1063  */
1064 void eap_sim_db_remove_reauth(void *priv, struct eap_sim_reauth *reauth)
1065 {
1066         struct eap_sim_db_data *data = priv;
1067         struct eap_sim_reauth *r, *prev = NULL;
1068         r = data->reauths;
1069         while (r) {
1070                 if (r == reauth) {
1071                         if (prev)
1072                                 prev->next = r->next;
1073                         else
1074                                 data->reauths = r->next;
1075                         eap_sim_db_free_reauth(r);
1076                         return;
1077                 }
1078                 prev = r;
1079                 r = r->next;
1080         }
1081 }
1082
1083
1084 /**
1085  * eap_sim_db_get_aka_auth - Get AKA authentication values
1086  * @priv: Private data pointer from eap_sim_db_init()
1087  * @identity: User name identity
1088  * @identity_len: Length of identity in bytes
1089  * @_rand: Buffer for RAND value
1090  * @autn: Buffer for AUTN value
1091  * @ik: Buffer for IK value
1092  * @ck: Buffer for CK value
1093  * @res: Buffer for RES value
1094  * @res_len: Buffer for RES length
1095  * @cb_session_ctx: Session callback context for get_complete_cb()
1096  * Returns: 0 on success, -1 (EAP_SIM_DB_FAILURE) on error (e.g., user not
1097  * found), or -2 (EAP_SIM_DB_PENDING) if results are not yet available. In this
1098  * case, the callback function registered with eap_sim_db_init() will be
1099  * called once the results become available.
1100  *
1101  * In most cases, the user name is '0' | IMSI, i.e., 0 followed by the IMSI in
1102  * ASCII format.
1103  *
1104  * When using an external server for AKA authentication, this function can
1105  * always start a request and return EAP_SIM_DB_PENDING immediately if
1106  * authentication triplets are not available. Once the authentication data are
1107  * received, callback function registered with eap_sim_db_init() is called to
1108  * notify EAP state machine to reprocess the message. This
1109  * eap_sim_db_get_aka_auth() function will then be called again and the newly
1110  * received triplets will then be given to the caller.
1111  */
1112 int eap_sim_db_get_aka_auth(void *priv, const u8 *identity,
1113                             size_t identity_len, u8 *_rand, u8 *autn, u8 *ik,
1114                             u8 *ck, u8 *res, size_t *res_len,
1115                             void *cb_session_ctx)
1116 {
1117         struct eap_sim_db_data *data = priv;
1118         struct eap_sim_db_pending *entry;
1119         int len;
1120         size_t i;
1121         char msg[40];
1122
1123         if (identity_len < 2 || identity == NULL ||
1124             identity[0] != EAP_AKA_PERMANENT_PREFIX) {
1125                 wpa_hexdump_ascii(MSG_DEBUG, "EAP-SIM DB: unexpected identity",
1126                                   identity, identity_len);
1127                 return EAP_SIM_DB_FAILURE;
1128         }
1129         identity++;
1130         identity_len--;
1131         for (i = 0; i < identity_len; i++) {
1132                 if (identity[i] == '@') {
1133                         identity_len = i;
1134                         break;
1135                 }
1136         }
1137         if (identity_len + 1 > sizeof(entry->imsi)) {
1138                 wpa_hexdump_ascii(MSG_DEBUG, "EAP-SIM DB: unexpected identity",
1139                                   identity, identity_len);
1140                 return EAP_SIM_DB_FAILURE;
1141         }
1142         wpa_hexdump_ascii(MSG_DEBUG, "EAP-SIM DB: Get AKA auth for IMSI",
1143                           identity, identity_len);
1144
1145         entry = eap_sim_db_get_pending(data, identity, identity_len, 1);
1146         if (entry) {
1147                 if (entry->state == FAILURE) {
1148                         free(entry);
1149                         wpa_printf(MSG_DEBUG, "EAP-SIM DB: Failure");
1150                         return EAP_SIM_DB_FAILURE;
1151                 }
1152
1153                 if (entry->state == PENDING) {
1154                         eap_sim_db_add_pending(data, entry);
1155                         wpa_printf(MSG_DEBUG, "EAP-SIM DB: Pending");
1156                         return EAP_SIM_DB_PENDING;
1157                 }
1158
1159                 wpa_printf(MSG_DEBUG, "EAP-SIM DB: Returning successfully "
1160                            "received authentication data");
1161                 memcpy(_rand, entry->u.aka.rand, EAP_AKA_RAND_LEN);
1162                 memcpy(autn, entry->u.aka.autn, EAP_AKA_AUTN_LEN);
1163                 memcpy(ik, entry->u.aka.ik, EAP_AKA_IK_LEN);
1164                 memcpy(ck, entry->u.aka.ck, EAP_AKA_CK_LEN);
1165                 memcpy(res, entry->u.aka.res, EAP_AKA_RES_MAX_LEN);
1166                 *res_len = entry->u.aka.res_len;
1167                 free(entry);
1168                 return 0;
1169         }
1170
1171         if (data->sock < 0) {
1172                 if (eap_sim_db_open_socket(data) < 0)
1173                         return EAP_SIM_DB_FAILURE;
1174         }
1175
1176         len = snprintf(msg, sizeof(msg), "AKA-REQ-AUTH ");
1177         if (len < 0 || len + identity_len >= sizeof(msg))
1178                 return EAP_SIM_DB_FAILURE;
1179         memcpy(msg + len, identity, identity_len);
1180         len += identity_len;
1181
1182         wpa_hexdump(MSG_DEBUG, "EAP-SIM DB: requesting AKA authentication "
1183                     "data for IMSI", identity, identity_len);
1184         if (eap_sim_db_send(data, msg, len) < 0)
1185                 return EAP_SIM_DB_FAILURE;
1186
1187         entry = wpa_zalloc(sizeof(*entry));
1188         if (entry == NULL)
1189                 return EAP_SIM_DB_FAILURE;
1190
1191         os_get_time(&entry->timestamp);
1192         entry->aka = 1;
1193         memcpy(entry->imsi, identity, identity_len);
1194         entry->imsi_len = identity_len;
1195         entry->cb_session_ctx = cb_session_ctx;
1196         entry->state = PENDING;
1197         eap_sim_db_add_pending(data, entry);
1198         eap_sim_db_expire_pending(data);
1199
1200         return EAP_SIM_DB_PENDING;
1201 }
1202
1203
1204 /**
1205  * eap_sim_db_resynchronize - Resynchronize AKA AUTN
1206  * @priv: Private data pointer from eap_sim_db_init()
1207  * @identity: User name identity
1208  * @identity_len: Length of identity in bytes
1209  * @auts: AUTS value from the peer
1210  * @_rand: RAND value used in the rejected message
1211  * Returns: 0 on success, -1 on failure
1212  *
1213  * This function is called when the peer reports synchronization failure in the
1214  * AUTN value by sending AUTS. The AUTS and RAND values should be sent to
1215  * HLR/AuC to allow it to resynchronize with the peer. After this,
1216  * eap_sim_db_get_aka_auth() will be called again to to fetch updated
1217  * RAND/AUTN values for the next challenge.
1218  */
1219 int eap_sim_db_resynchronize(void *priv, const u8 *identity,
1220                              size_t identity_len, const u8 *auts,
1221                              const u8 *_rand)
1222 {
1223         struct eap_sim_db_data *data = priv;
1224         size_t i;
1225
1226         if (identity_len < 2 || identity == NULL ||
1227             identity[0] != EAP_AKA_PERMANENT_PREFIX) {
1228                 wpa_hexdump_ascii(MSG_DEBUG, "EAP-SIM DB: unexpected identity",
1229                                   identity, identity_len);
1230                 return -1;
1231         }
1232         identity++;
1233         identity_len--;
1234         for (i = 0; i < identity_len; i++) {
1235                 if (identity[i] == '@') {
1236                         identity_len = i;
1237                         break;
1238                 }
1239         }
1240         if (identity_len > 20) {
1241                 wpa_hexdump_ascii(MSG_DEBUG, "EAP-SIM DB: unexpected identity",
1242                                   identity, identity_len);
1243                 return -1;
1244         }
1245
1246         if (data->sock >= 0) {
1247                 char msg[100];
1248                 int len, ret;
1249
1250                 len = snprintf(msg, sizeof(msg), "AKA-AUTS ");
1251                 if (len < 0 || len + identity_len >= sizeof(msg))
1252                         return -1;
1253                 memcpy(msg + len, identity, identity_len);
1254                 len += identity_len;
1255
1256                 ret = snprintf(msg + len, sizeof(msg) - len, " ");
1257                 if (ret < 0 || (size_t) ret >= sizeof(msg) - len)
1258                         return -1;
1259                 len += ret;
1260                 len += wpa_snprintf_hex(msg + len, sizeof(msg) - len,
1261                                         auts, EAP_AKA_AUTS_LEN);
1262                 ret = snprintf(msg + len, sizeof(msg) - len, " ");
1263                 if (ret < 0 || (size_t) ret >= sizeof(msg) - len)
1264                         return -1;
1265                 len += ret;
1266                 len += wpa_snprintf_hex(msg + len, sizeof(msg) - len,
1267                                         _rand, EAP_AKA_RAND_LEN);
1268                 wpa_hexdump(MSG_DEBUG, "EAP-SIM DB: reporting AKA AUTS for "
1269                             "IMSI", identity, identity_len);
1270                 if (eap_sim_db_send(data, msg, len) < 0)
1271                         return -1;
1272         }
1273
1274         return 0;
1275 }