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