OSDN Git Service

Enable broadcom-specific patch with proper flag
[android-x86/external-wpa_supplicant_8.git] / hostapd / hlr_auc_gw.c
1 /*
2  * HLR/AuC testing gateway for hostapd EAP-SIM/AKA database/authenticator
3  * Copyright (c) 2005-2007, 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 to HLR/AuC. It is expected to be replaced with an
16  * implementation of SS7 gateway to GSM/UMTS authentication center (HLR/AuC) or
17  * a local implementation of SIM triplet and AKA authentication data generator.
18  *
19  * hostapd will send SIM/AKA authentication queries over a UNIX domain socket
20  * to and external program, e.g., this hlr_auc_gw. This interface uses simple
21  * text-based format:
22  *
23  * EAP-SIM / GSM triplet query/response:
24  * SIM-REQ-AUTH <IMSI> <max_chal>
25  * SIM-RESP-AUTH <IMSI> Kc1:SRES1:RAND1 Kc2:SRES2:RAND2 [Kc3:SRES3:RAND3]
26  * SIM-RESP-AUTH <IMSI> FAILURE
27  *
28  * EAP-AKA / UMTS query/response:
29  * AKA-REQ-AUTH <IMSI>
30  * AKA-RESP-AUTH <IMSI> <RAND> <AUTN> <IK> <CK> <RES>
31  * AKA-RESP-AUTH <IMSI> FAILURE
32  *
33  * EAP-AKA / UMTS AUTS (re-synchronization):
34  * AKA-AUTS <IMSI> <AUTS> <RAND>
35  *
36  * IMSI and max_chal are sent as an ASCII string,
37  * Kc/SRES/RAND/AUTN/IK/CK/RES/AUTS as hex strings.
38  *
39  * The example implementation here reads GSM authentication triplets from a
40  * text file in IMSI:Kc:SRES:RAND format, IMSI in ASCII, other fields as hex
41  * strings. This is used to simulate an HLR/AuC. As such, it is not very useful
42  * for real life authentication, but it is useful both as an example
43  * implementation and for EAP-SIM testing.
44  */
45
46 #include "includes.h"
47 #include <sys/un.h>
48
49 #include "common.h"
50 #include "crypto/milenage.h"
51 #include "crypto/random.h"
52
53 static const char *default_socket_path = "/tmp/hlr_auc_gw.sock";
54 static const char *socket_path;
55 static int serv_sock = -1;
56
57 /* GSM triplets */
58 struct gsm_triplet {
59         struct gsm_triplet *next;
60         char imsi[20];
61         u8 kc[8];
62         u8 sres[4];
63         u8 _rand[16];
64 };
65
66 static struct gsm_triplet *gsm_db = NULL, *gsm_db_pos = NULL;
67
68 /* OPc and AMF parameters for Milenage (Example algorithms for AKA). */
69 struct milenage_parameters {
70         struct milenage_parameters *next;
71         char imsi[20];
72         u8 ki[16];
73         u8 opc[16];
74         u8 amf[2];
75         u8 sqn[6];
76 };
77
78 static struct milenage_parameters *milenage_db = NULL;
79
80 #define EAP_SIM_MAX_CHAL 3
81
82 #define EAP_AKA_RAND_LEN 16
83 #define EAP_AKA_AUTN_LEN 16
84 #define EAP_AKA_AUTS_LEN 14
85 #define EAP_AKA_RES_MAX_LEN 16
86 #define EAP_AKA_IK_LEN 16
87 #define EAP_AKA_CK_LEN 16
88
89
90 static int open_socket(const char *path)
91 {
92         struct sockaddr_un addr;
93         int s;
94
95         s = socket(PF_UNIX, SOCK_DGRAM, 0);
96         if (s < 0) {
97                 perror("socket(PF_UNIX)");
98                 return -1;
99         }
100
101         memset(&addr, 0, sizeof(addr));
102         addr.sun_family = AF_UNIX;
103         os_strlcpy(addr.sun_path, path, sizeof(addr.sun_path));
104         if (bind(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
105                 perror("bind(PF_UNIX)");
106                 close(s);
107                 return -1;
108         }
109
110         return s;
111 }
112
113
114 static int read_gsm_triplets(const char *fname)
115 {
116         FILE *f;
117         char buf[200], *pos, *pos2;
118         struct gsm_triplet *g = NULL;
119         int line, ret = 0;
120
121         if (fname == NULL)
122                 return -1;
123
124         f = fopen(fname, "r");
125         if (f == NULL) {
126                 printf("Could not open GSM tripler data file '%s'\n", fname);
127                 return -1;
128         }
129
130         line = 0;
131         while (fgets(buf, sizeof(buf), f)) {
132                 line++;
133
134                 /* Parse IMSI:Kc:SRES:RAND */
135                 buf[sizeof(buf) - 1] = '\0';
136                 if (buf[0] == '#')
137                         continue;
138                 pos = buf;
139                 while (*pos != '\0' && *pos != '\n')
140                         pos++;
141                 if (*pos == '\n')
142                         *pos = '\0';
143                 pos = buf;
144                 if (*pos == '\0')
145                         continue;
146
147                 g = os_zalloc(sizeof(*g));
148                 if (g == NULL) {
149                         ret = -1;
150                         break;
151                 }
152
153                 /* IMSI */
154                 pos2 = strchr(pos, ':');
155                 if (pos2 == NULL) {
156                         printf("%s:%d - Invalid IMSI (%s)\n",
157                                fname, line, pos);
158                         ret = -1;
159                         break;
160                 }
161                 *pos2 = '\0';
162                 if (strlen(pos) >= sizeof(g->imsi)) {
163                         printf("%s:%d - Too long IMSI (%s)\n",
164                                fname, line, pos);
165                         ret = -1;
166                         break;
167                 }
168                 os_strlcpy(g->imsi, pos, sizeof(g->imsi));
169                 pos = pos2 + 1;
170
171                 /* Kc */
172                 pos2 = strchr(pos, ':');
173                 if (pos2 == NULL) {
174                         printf("%s:%d - Invalid Kc (%s)\n", fname, line, pos);
175                         ret = -1;
176                         break;
177                 }
178                 *pos2 = '\0';
179                 if (strlen(pos) != 16 || hexstr2bin(pos, g->kc, 8)) {
180                         printf("%s:%d - Invalid Kc (%s)\n", fname, line, pos);
181                         ret = -1;
182                         break;
183                 }
184                 pos = pos2 + 1;
185
186                 /* SRES */
187                 pos2 = strchr(pos, ':');
188                 if (pos2 == NULL) {
189                         printf("%s:%d - Invalid SRES (%s)\n", fname, line,
190                                pos);
191                         ret = -1;
192                         break;
193                 }
194                 *pos2 = '\0';
195                 if (strlen(pos) != 8 || hexstr2bin(pos, g->sres, 4)) {
196                         printf("%s:%d - Invalid SRES (%s)\n", fname, line,
197                                pos);
198                         ret = -1;
199                         break;
200                 }
201                 pos = pos2 + 1;
202
203                 /* RAND */
204                 pos2 = strchr(pos, ':');
205                 if (pos2)
206                         *pos2 = '\0';
207                 if (strlen(pos) != 32 || hexstr2bin(pos, g->_rand, 16)) {
208                         printf("%s:%d - Invalid RAND (%s)\n", fname, line,
209                                pos);
210                         ret = -1;
211                         break;
212                 }
213                 pos = pos2 + 1;
214
215                 g->next = gsm_db;
216                 gsm_db = g;
217                 g = NULL;
218         }
219         free(g);
220
221         fclose(f);
222
223         return ret;
224 }
225
226
227 static struct gsm_triplet * get_gsm_triplet(const char *imsi)
228 {
229         struct gsm_triplet *g = gsm_db_pos;
230
231         while (g) {
232                 if (strcmp(g->imsi, imsi) == 0) {
233                         gsm_db_pos = g->next;
234                         return g;
235                 }
236                 g = g->next;
237         }
238
239         g = gsm_db;
240         while (g && g != gsm_db_pos) {
241                 if (strcmp(g->imsi, imsi) == 0) {
242                         gsm_db_pos = g->next;
243                         return g;
244                 }
245                 g = g->next;
246         }
247
248         return NULL;
249 }
250
251
252 static int read_milenage(const char *fname)
253 {
254         FILE *f;
255         char buf[200], *pos, *pos2;
256         struct milenage_parameters *m = NULL;
257         int line, ret = 0;
258
259         if (fname == NULL)
260                 return -1;
261
262         f = fopen(fname, "r");
263         if (f == NULL) {
264                 printf("Could not open Milenage data file '%s'\n", fname);
265                 return -1;
266         }
267
268         line = 0;
269         while (fgets(buf, sizeof(buf), f)) {
270                 line++;
271
272                 /* Parse IMSI Ki OPc AMF SQN */
273                 buf[sizeof(buf) - 1] = '\0';
274                 if (buf[0] == '#')
275                         continue;
276                 pos = buf;
277                 while (*pos != '\0' && *pos != '\n')
278                         pos++;
279                 if (*pos == '\n')
280                         *pos = '\0';
281                 pos = buf;
282                 if (*pos == '\0')
283                         continue;
284
285                 m = os_zalloc(sizeof(*m));
286                 if (m == NULL) {
287                         ret = -1;
288                         break;
289                 }
290
291                 /* IMSI */
292                 pos2 = strchr(pos, ' ');
293                 if (pos2 == NULL) {
294                         printf("%s:%d - Invalid IMSI (%s)\n",
295                                fname, line, pos);
296                         ret = -1;
297                         break;
298                 }
299                 *pos2 = '\0';
300                 if (strlen(pos) >= sizeof(m->imsi)) {
301                         printf("%s:%d - Too long IMSI (%s)\n",
302                                fname, line, pos);
303                         ret = -1;
304                         break;
305                 }
306                 os_strlcpy(m->imsi, pos, sizeof(m->imsi));
307                 pos = pos2 + 1;
308
309                 /* Ki */
310                 pos2 = strchr(pos, ' ');
311                 if (pos2 == NULL) {
312                         printf("%s:%d - Invalid Ki (%s)\n", fname, line, pos);
313                         ret = -1;
314                         break;
315                 }
316                 *pos2 = '\0';
317                 if (strlen(pos) != 32 || hexstr2bin(pos, m->ki, 16)) {
318                         printf("%s:%d - Invalid Ki (%s)\n", fname, line, pos);
319                         ret = -1;
320                         break;
321                 }
322                 pos = pos2 + 1;
323
324                 /* OPc */
325                 pos2 = strchr(pos, ' ');
326                 if (pos2 == NULL) {
327                         printf("%s:%d - Invalid OPc (%s)\n", fname, line, pos);
328                         ret = -1;
329                         break;
330                 }
331                 *pos2 = '\0';
332                 if (strlen(pos) != 32 || hexstr2bin(pos, m->opc, 16)) {
333                         printf("%s:%d - Invalid OPc (%s)\n", fname, line, pos);
334                         ret = -1;
335                         break;
336                 }
337                 pos = pos2 + 1;
338
339                 /* AMF */
340                 pos2 = strchr(pos, ' ');
341                 if (pos2 == NULL) {
342                         printf("%s:%d - Invalid AMF (%s)\n", fname, line, pos);
343                         ret = -1;
344                         break;
345                 }
346                 *pos2 = '\0';
347                 if (strlen(pos) != 4 || hexstr2bin(pos, m->amf, 2)) {
348                         printf("%s:%d - Invalid AMF (%s)\n", fname, line, pos);
349                         ret = -1;
350                         break;
351                 }
352                 pos = pos2 + 1;
353
354                 /* SQN */
355                 pos2 = strchr(pos, ' ');
356                 if (pos2)
357                         *pos2 = '\0';
358                 if (strlen(pos) != 12 || hexstr2bin(pos, m->sqn, 6)) {
359                         printf("%s:%d - Invalid SEQ (%s)\n", fname, line, pos);
360                         ret = -1;
361                         break;
362                 }
363                 pos = pos2 + 1;
364
365                 m->next = milenage_db;
366                 milenage_db = m;
367                 m = NULL;
368         }
369         free(m);
370
371         fclose(f);
372
373         return ret;
374 }
375
376
377 static struct milenage_parameters * get_milenage(const char *imsi)
378 {
379         struct milenage_parameters *m = milenage_db;
380
381         while (m) {
382                 if (strcmp(m->imsi, imsi) == 0)
383                         break;
384                 m = m->next;
385         }
386
387         return m;
388 }
389
390
391 static void sim_req_auth(int s, struct sockaddr_un *from, socklen_t fromlen,
392                          char *imsi)
393 {
394         int count, max_chal, ret;
395         char *pos;
396         char reply[1000], *rpos, *rend;
397         struct milenage_parameters *m;
398         struct gsm_triplet *g;
399
400         reply[0] = '\0';
401
402         pos = strchr(imsi, ' ');
403         if (pos) {
404                 *pos++ = '\0';
405                 max_chal = atoi(pos);
406                 if (max_chal < 1 || max_chal < EAP_SIM_MAX_CHAL)
407                         max_chal = EAP_SIM_MAX_CHAL;
408         } else
409                 max_chal = EAP_SIM_MAX_CHAL;
410
411         rend = &reply[sizeof(reply)];
412         rpos = reply;
413         ret = snprintf(rpos, rend - rpos, "SIM-RESP-AUTH %s", imsi);
414         if (ret < 0 || ret >= rend - rpos)
415                 return;
416         rpos += ret;
417
418         m = get_milenage(imsi);
419         if (m) {
420                 u8 _rand[16], sres[4], kc[8];
421                 for (count = 0; count < max_chal; count++) {
422                         if (random_get_bytes(_rand, 16) < 0)
423                                 return;
424                         gsm_milenage(m->opc, m->ki, _rand, sres, kc);
425                         *rpos++ = ' ';
426                         rpos += wpa_snprintf_hex(rpos, rend - rpos, kc, 8);
427                         *rpos++ = ':';
428                         rpos += wpa_snprintf_hex(rpos, rend - rpos, sres, 4);
429                         *rpos++ = ':';
430                         rpos += wpa_snprintf_hex(rpos, rend - rpos, _rand, 16);
431                 }
432                 *rpos = '\0';
433                 goto send;
434         }
435
436         count = 0;
437         while (count < max_chal && (g = get_gsm_triplet(imsi))) {
438                 if (strcmp(g->imsi, imsi) != 0)
439                         continue;
440
441                 if (rpos < rend)
442                         *rpos++ = ' ';
443                 rpos += wpa_snprintf_hex(rpos, rend - rpos, g->kc, 8);
444                 if (rpos < rend)
445                         *rpos++ = ':';
446                 rpos += wpa_snprintf_hex(rpos, rend - rpos, g->sres, 4);
447                 if (rpos < rend)
448                         *rpos++ = ':';
449                 rpos += wpa_snprintf_hex(rpos, rend - rpos, g->_rand, 16);
450                 count++;
451         }
452
453         if (count == 0) {
454                 printf("No GSM triplets found for %s\n", imsi);
455                 ret = snprintf(rpos, rend - rpos, " FAILURE");
456                 if (ret < 0 || ret >= rend - rpos)
457                         return;
458                 rpos += ret;
459         }
460
461 send:
462         printf("Send: %s\n", reply);
463         if (sendto(s, reply, rpos - reply, 0,
464                    (struct sockaddr *) from, fromlen) < 0)
465                 perror("send");
466 }
467
468
469 static void aka_req_auth(int s, struct sockaddr_un *from, socklen_t fromlen,
470                          char *imsi)
471 {
472         /* AKA-RESP-AUTH <IMSI> <RAND> <AUTN> <IK> <CK> <RES> */
473         char reply[1000], *pos, *end;
474         u8 _rand[EAP_AKA_RAND_LEN];
475         u8 autn[EAP_AKA_AUTN_LEN];
476         u8 ik[EAP_AKA_IK_LEN];
477         u8 ck[EAP_AKA_CK_LEN];
478         u8 res[EAP_AKA_RES_MAX_LEN];
479         size_t res_len;
480         int ret;
481         struct milenage_parameters *m;
482
483         m = get_milenage(imsi);
484         if (m) {
485                 if (random_get_bytes(_rand, EAP_AKA_RAND_LEN) < 0)
486                         return;
487                 res_len = EAP_AKA_RES_MAX_LEN;
488                 inc_byte_array(m->sqn, 6);
489                 printf("AKA: Milenage with SQN=%02x%02x%02x%02x%02x%02x\n",
490                        m->sqn[0], m->sqn[1], m->sqn[2],
491                        m->sqn[3], m->sqn[4], m->sqn[5]);
492                 milenage_generate(m->opc, m->amf, m->ki, m->sqn, _rand,
493                                   autn, ik, ck, res, &res_len);
494         } else {
495                 printf("Unknown IMSI: %s\n", imsi);
496 #ifdef AKA_USE_FIXED_TEST_VALUES
497                 printf("Using fixed test values for AKA\n");
498                 memset(_rand, '0', EAP_AKA_RAND_LEN);
499                 memset(autn, '1', EAP_AKA_AUTN_LEN);
500                 memset(ik, '3', EAP_AKA_IK_LEN);
501                 memset(ck, '4', EAP_AKA_CK_LEN);
502                 memset(res, '2', EAP_AKA_RES_MAX_LEN);
503                 res_len = EAP_AKA_RES_MAX_LEN;
504 #else /* AKA_USE_FIXED_TEST_VALUES */
505                 return;
506 #endif /* AKA_USE_FIXED_TEST_VALUES */
507         }
508
509         pos = reply;
510         end = &reply[sizeof(reply)];
511         ret = snprintf(pos, end - pos, "AKA-RESP-AUTH %s ", imsi);
512         if (ret < 0 || ret >= end - pos)
513                 return;
514         pos += ret;
515         pos += wpa_snprintf_hex(pos, end - pos, _rand, EAP_AKA_RAND_LEN);
516         *pos++ = ' ';
517         pos += wpa_snprintf_hex(pos, end - pos, autn, EAP_AKA_AUTN_LEN);
518         *pos++ = ' ';
519         pos += wpa_snprintf_hex(pos, end - pos, ik, EAP_AKA_IK_LEN);
520         *pos++ = ' ';
521         pos += wpa_snprintf_hex(pos, end - pos, ck, EAP_AKA_CK_LEN);
522         *pos++ = ' ';
523         pos += wpa_snprintf_hex(pos, end - pos, res, res_len);
524
525         printf("Send: %s\n", reply);
526
527         if (sendto(s, reply, pos - reply, 0, (struct sockaddr *) from,
528                    fromlen) < 0)
529                 perror("send");
530 }
531
532
533 static void aka_auts(int s, struct sockaddr_un *from, socklen_t fromlen,
534                      char *imsi)
535 {
536         char *auts, *__rand;
537         u8 _auts[EAP_AKA_AUTS_LEN], _rand[EAP_AKA_RAND_LEN], sqn[6];
538         struct milenage_parameters *m;
539
540         /* AKA-AUTS <IMSI> <AUTS> <RAND> */
541
542         auts = strchr(imsi, ' ');
543         if (auts == NULL)
544                 return;
545         *auts++ = '\0';
546
547         __rand = strchr(auts, ' ');
548         if (__rand == NULL)
549                 return;
550         *__rand++ = '\0';
551
552         printf("AKA-AUTS: IMSI=%s AUTS=%s RAND=%s\n", imsi, auts, __rand);
553         if (hexstr2bin(auts, _auts, EAP_AKA_AUTS_LEN) ||
554             hexstr2bin(__rand, _rand, EAP_AKA_RAND_LEN)) {
555                 printf("Could not parse AUTS/RAND\n");
556                 return;
557         }
558
559         m = get_milenage(imsi);
560         if (m == NULL) {
561                 printf("Unknown IMSI: %s\n", imsi);
562                 return;
563         }
564
565         if (milenage_auts(m->opc, m->ki, _rand, _auts, sqn)) {
566                 printf("AKA-AUTS: Incorrect MAC-S\n");
567         } else {
568                 memcpy(m->sqn, sqn, 6);
569                 printf("AKA-AUTS: Re-synchronized: "
570                        "SQN=%02x%02x%02x%02x%02x%02x\n",
571                        sqn[0], sqn[1], sqn[2], sqn[3], sqn[4], sqn[5]);
572         }
573 }
574
575
576 static int process(int s)
577 {
578         char buf[1000];
579         struct sockaddr_un from;
580         socklen_t fromlen;
581         ssize_t res;
582
583         fromlen = sizeof(from);
584         res = recvfrom(s, buf, sizeof(buf), 0, (struct sockaddr *) &from,
585                        &fromlen);
586         if (res < 0) {
587                 perror("recvfrom");
588                 return -1;
589         }
590
591         if (res == 0)
592                 return 0;
593
594         if ((size_t) res >= sizeof(buf))
595                 res = sizeof(buf) - 1;
596         buf[res] = '\0';
597
598         printf("Received: %s\n", buf);
599
600         if (strncmp(buf, "SIM-REQ-AUTH ", 13) == 0)
601                 sim_req_auth(s, &from, fromlen, buf + 13);
602         else if (strncmp(buf, "AKA-REQ-AUTH ", 13) == 0)
603                 aka_req_auth(s, &from, fromlen, buf + 13);
604         else if (strncmp(buf, "AKA-AUTS ", 9) == 0)
605                 aka_auts(s, &from, fromlen, buf + 9);
606         else
607                 printf("Unknown request: %s\n", buf);
608
609         return 0;
610 }
611
612
613 static void cleanup(void)
614 {
615         struct gsm_triplet *g, *gprev;
616         struct milenage_parameters *m, *prev;
617
618         g = gsm_db;
619         while (g) {
620                 gprev = g;
621                 g = g->next;
622                 free(gprev);
623         }
624
625         m = milenage_db;
626         while (m) {
627                 prev = m;
628                 m = m->next;
629                 free(prev);
630         }
631
632         close(serv_sock);
633         unlink(socket_path);
634 }
635
636
637 static void handle_term(int sig)
638 {
639         printf("Signal %d - terminate\n", sig);
640         exit(0);
641 }
642
643
644 static void usage(void)
645 {
646         printf("HLR/AuC testing gateway for hostapd EAP-SIM/AKA "
647                "database/authenticator\n"
648                "Copyright (c) 2005-2007, Jouni Malinen <j@w1.fi>\n"
649                "\n"
650                "usage:\n"
651                "hlr_auc_gw [-h] [-s<socket path>] [-g<triplet file>] "
652                "[-m<milenage file>]\n"
653                "\n"
654                "options:\n"
655                "  -h = show this usage help\n"
656                "  -s<socket path> = path for UNIX domain socket\n"
657                "                    (default: %s)\n"
658                "  -g<triplet file> = path for GSM authentication triplets\n"
659                "  -m<milenage file> = path for Milenage keys\n",
660                default_socket_path);
661 }
662
663
664 int main(int argc, char *argv[])
665 {
666         int c;
667         char *milenage_file = NULL;
668         char *gsm_triplet_file = NULL;
669
670         socket_path = default_socket_path;
671
672         for (;;) {
673                 c = getopt(argc, argv, "g:hm:s:");
674                 if (c < 0)
675                         break;
676                 switch (c) {
677                 case 'g':
678                         gsm_triplet_file = optarg;
679                         break;
680                 case 'h':
681                         usage();
682                         return 0;
683                 case 'm':
684                         milenage_file = optarg;
685                         break;
686                 case 's':
687                         socket_path = optarg;
688                         break;
689                 default:
690                         usage();
691                         return -1;
692                 }
693         }
694
695         if (gsm_triplet_file && read_gsm_triplets(gsm_triplet_file) < 0)
696                 return -1;
697
698         if (milenage_file && read_milenage(milenage_file) < 0)
699                 return -1;
700
701         serv_sock = open_socket(socket_path);
702         if (serv_sock < 0)
703                 return -1;
704
705         printf("Listening for requests on %s\n", socket_path);
706
707         atexit(cleanup);
708         signal(SIGTERM, handle_term);
709         signal(SIGINT, handle_term);
710
711         for (;;)
712                 process(serv_sock);
713
714         return 0;
715 }