OSDN Git Service

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