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