Merge from vendor branch LIBARCHIVE:
[dragonfly.git] / contrib / hostapd-0.5.8 / hlr_auc_gw.c
1 /*
2  * HLR/AuC testing gateway for hostapd EAP-SIM/AKA database/authenticator
3  * Copyright (c) 2005-2006, Jouni Malinen <j@w1.fi>
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License version 2 as
7  * published by the Free Software Foundation.
8  *
9  * Alternatively, this software may be distributed under the terms of BSD
10  * license.
11  *
12  * See README and COPYING for more details.
13  *
14  * This is an example implementation of the EAP-SIM/AKA database/authentication
15  * gateway interface 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 <stdlib.h>
47 #include <stdio.h>
48 #include <unistd.h>
49 #include <signal.h>
50 #include <string.h>
51 #include <sys/types.h>
52 #include <sys/socket.h>
53 #include <sys/un.h>
54
55 #include "common.h"
56 #include "milenage.h"
57
58 static const char *default_socket_path = "/tmp/hlr_auc_gw.sock";
59 static const char *socket_path;
60 static const char *default_gsm_triplet_file = "hostapd.sim_db";
61 static const char *gsm_triplet_file;
62 static int serv_sock = -1;
63
64 /* OPc and AMF parameters for Milenage (Example algorithms for AKA). */
65 struct milenage_parameters {
66         struct milenage_parameters *next;
67         char imsi[20];
68         u8 ki[16];
69         u8 opc[16];
70         u8 amf[2];
71         u8 sqn[6];
72 };
73
74 static struct milenage_parameters *milenage_db = NULL;
75
76 #define EAP_SIM_MAX_CHAL 3
77
78 #define EAP_AKA_RAND_LEN 16
79 #define EAP_AKA_AUTN_LEN 16
80 #define EAP_AKA_AUTS_LEN 14
81 #define EAP_AKA_RES_MAX_LEN 16
82 #define EAP_AKA_IK_LEN 16
83 #define EAP_AKA_CK_LEN 16
84
85
86 static int open_socket(const char *path)
87 {
88         struct sockaddr_un addr;
89         int s;
90
91         s = socket(PF_UNIX, SOCK_DGRAM, 0);
92         if (s < 0) {
93                 perror("socket(PF_UNIX)");
94                 return -1;
95         }
96
97         memset(&addr, 0, sizeof(addr));
98         addr.sun_family = AF_UNIX;
99         strncpy(addr.sun_path, path, sizeof(addr.sun_path));
100         if (bind(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
101                 perror("bind(PF_UNIX)");
102                 close(s);
103                 return -1;
104         }
105
106         return s;
107 }
108
109
110 static int read_milenage(const char *fname)
111 {
112         FILE *f;
113         char buf[200], *pos, *pos2;
114         struct milenage_parameters *m = NULL;
115         int line, ret = 0;
116
117         if (fname == NULL)
118                 return -1;
119
120         f = fopen(fname, "r");
121         if (f == NULL) {
122                 printf("Could not open Milenage data file '%s'\n", fname);
123                 return -1;
124         }
125
126         line = 0;
127         while (fgets(buf, sizeof(buf), f)) {
128                 line++;
129
130                 /* Parse IMSI Ki OPc AMF SQN */
131                 buf[sizeof(buf) - 1] = '\0';
132                 if (buf[0] == '#')
133                         continue;
134                 pos = buf;
135                 while (*pos != '\0' && *pos != '\n')
136                         pos++;
137                 if (*pos == '\n')
138                         *pos = '\0';
139                 pos = buf;
140                 if (*pos == '\0')
141                         continue;
142
143                 m = os_zalloc(sizeof(*m));
144                 if (m == NULL) {
145                         ret = -1;
146                         break;
147                 }
148
149                 /* IMSI */
150                 pos2 = strchr(pos, ' ');
151                 if (pos2 == NULL) {
152                         printf("%s:%d - Invalid IMSI (%s)\n",
153                                fname, line, pos);
154                         ret = -1;
155                         break;
156                 }
157                 *pos2 = '\0';
158                 if (strlen(pos) >= sizeof(m->imsi)) {
159                         printf("%s:%d - Too long IMSI (%s)\n",
160                                fname, line, pos);
161                         ret = -1;
162                         break;
163                 }
164                 strncpy(m->imsi, pos, sizeof(m->imsi));
165                 pos = pos2 + 1;
166
167                 /* Ki */
168                 pos2 = strchr(pos, ' ');
169                 if (pos2 == NULL) {
170                         printf("%s:%d - Invalid Ki (%s)\n", fname, line, pos);
171                         ret = -1;
172                         break;
173                 }
174                 *pos2 = '\0';
175                 if (strlen(pos) != 32 || hexstr2bin(pos, m->ki, 16)) {
176                         printf("%s:%d - Invalid Ki (%s)\n", fname, line, pos);
177                         ret = -1;
178                         break;
179                 }
180                 pos = pos2 + 1;
181
182                 /* OPc */
183                 pos2 = strchr(pos, ' ');
184                 if (pos2 == NULL) {
185                         printf("%s:%d - Invalid OPc (%s)\n", fname, line, pos);
186                         ret = -1;
187                         break;
188                 }
189                 *pos2 = '\0';
190                 if (strlen(pos) != 32 || hexstr2bin(pos, m->opc, 16)) {
191                         printf("%s:%d - Invalid OPc (%s)\n", fname, line, pos);
192                         ret = -1;
193                         break;
194                 }
195                 pos = pos2 + 1;
196
197                 /* AMF */
198                 pos2 = strchr(pos, ' ');
199                 if (pos2 == NULL) {
200                         printf("%s:%d - Invalid AMF (%s)\n", fname, line, pos);
201                         ret = -1;
202                         break;
203                 }
204                 *pos2 = '\0';
205                 if (strlen(pos) != 4 || hexstr2bin(pos, m->amf, 2)) {
206                         printf("%s:%d - Invalid AMF (%s)\n", fname, line, pos);
207                         ret = -1;
208                         break;
209                 }
210                 pos = pos2 + 1;
211
212                 /* SQN */
213                 pos2 = strchr(pos, ' ');
214                 if (pos2)
215                         *pos2 = '\0';
216                 if (strlen(pos) != 12 || hexstr2bin(pos, m->sqn, 6)) {
217                         printf("%s:%d - Invalid SEQ (%s)\n", fname, line, pos);
218                         ret = -1;
219                         break;
220                 }
221                 pos = pos2 + 1;
222
223                 m->next = milenage_db;
224                 milenage_db = m;
225                 m = NULL;
226         }
227         free(m);
228
229         fclose(f);
230
231         return ret;
232 }
233
234
235 static struct milenage_parameters * get_milenage(const char *imsi)
236 {
237         struct milenage_parameters *m = milenage_db;
238
239         while (m) {
240                 if (strcmp(m->imsi, imsi) == 0)
241                         break;
242                 m = m->next;
243         }
244
245         return m;
246 }
247
248
249 static void sim_req_auth(int s, struct sockaddr_un *from, socklen_t fromlen,
250                          char *imsi)
251 {
252         FILE *f;
253         int count, max_chal, ret;
254         char buf[80], *pos;
255         char reply[1000], *rpos, *rend;
256         struct milenage_parameters *m;
257
258         reply[0] = '\0';
259
260         pos = strchr(imsi, ' ');
261         if (pos) {
262                 *pos++ = '\0';
263                 max_chal = atoi(pos);
264                 if (max_chal < 1 || max_chal < EAP_SIM_MAX_CHAL)
265                         max_chal = EAP_SIM_MAX_CHAL;
266         } else
267                 max_chal = EAP_SIM_MAX_CHAL;
268
269         rend = &reply[sizeof(reply)];
270         rpos = reply;
271         ret = snprintf(rpos, rend - rpos, "SIM-RESP-AUTH %s", imsi);
272         if (ret < 0 || ret >= rend - rpos)
273                 return;
274         rpos += ret;
275
276         m = get_milenage(imsi);
277         if (m) {
278                 u8 _rand[16], sres[4], kc[8];
279                 for (count = 0; count < max_chal; count++) {
280                         os_get_random(_rand, 16);
281                         gsm_milenage(m->opc, m->ki, _rand, sres, kc);
282                         *rpos++ = ' ';
283                         rpos += wpa_snprintf_hex(rpos, rend - rpos, kc, 8);
284                         *rpos++ = ':';
285                         rpos += wpa_snprintf_hex(rpos, rend - rpos, sres, 4);
286                         *rpos++ = ':';
287                         rpos += wpa_snprintf_hex(rpos, rend - rpos, _rand, 16);
288                 }
289                 *rpos = '\0';
290                 goto send;
291         }
292
293         /* TODO: could read triplet file into memory during startup and then
294          * have pointer for IMSI to allow more than three first entries to be
295          * used. */
296         f = fopen(gsm_triplet_file, "r");
297         if (f == NULL) {
298                 printf("Could not open GSM triplet file '%s'\n",
299                        gsm_triplet_file);
300                 ret = snprintf(rpos, rend - rpos, " FAILURE");
301                 if (ret < 0 || ret >= rend - rpos)
302                         return;
303                 rpos += ret;
304                 goto send;
305         }
306
307         count = 0;
308         while (count < max_chal && fgets(buf, sizeof(buf), f)) {
309                 /* Parse IMSI:Kc:SRES:RAND and match IMSI with identity. */
310                 buf[sizeof(buf) - 1] = '\0';
311                 pos = buf;
312                 while (*pos != '\0' && *pos != '\n')
313                         pos++;
314                 if (*pos == '\n')
315                         *pos = '\0';
316                 if (pos - buf < 60 || pos[0] == '#')
317                         continue;
318
319                 pos = strchr(buf, ':');
320                 if (pos == NULL)
321                         continue;
322                 *pos++ = '\0';
323                 if (strcmp(buf, imsi) != 0)
324                         continue;
325
326                 ret = snprintf(rpos, rend - rpos, " %s", pos);
327                 if (ret < 0 || ret >= rend - rpos) {
328                         fclose(f);
329                         return;
330                 }
331                 rpos += ret;
332                 count++;
333         }
334
335         fclose(f);
336
337         if (count == 0) {
338                 printf("No GSM triplets found for %s\n", imsi);
339                 ret = snprintf(rpos, rend - rpos, " FAILURE");
340                 if (ret < 0 || ret >= rend - rpos)
341                         return;
342                 rpos += ret;
343         }
344
345 send:
346         printf("Send: %s\n", reply);
347         if (sendto(s, reply, rpos - reply, 0,
348                    (struct sockaddr *) from, fromlen) < 0)
349                 perror("send");
350 }
351
352
353 static void aka_req_auth(int s, struct sockaddr_un *from, socklen_t fromlen,
354                          char *imsi)
355 {
356         /* AKA-RESP-AUTH <IMSI> <RAND> <AUTN> <IK> <CK> <RES> */
357         char reply[1000], *pos, *end;
358         u8 _rand[EAP_AKA_RAND_LEN];
359         u8 autn[EAP_AKA_AUTN_LEN];
360         u8 ik[EAP_AKA_IK_LEN];
361         u8 ck[EAP_AKA_CK_LEN];
362         u8 res[EAP_AKA_RES_MAX_LEN];
363         size_t res_len;
364         int ret;
365         struct milenage_parameters *m;
366
367         m = get_milenage(imsi);
368         if (m) {
369                 os_get_random(_rand, EAP_AKA_RAND_LEN);
370                 res_len = EAP_AKA_RES_MAX_LEN;
371                 inc_byte_array(m->sqn, 6);
372                 printf("AKA: Milenage with SQN=%02x%02x%02x%02x%02x%02x\n",
373                        m->sqn[0], m->sqn[1], m->sqn[2],
374                        m->sqn[3], m->sqn[4], m->sqn[5]);
375                 milenage_generate(m->opc, m->amf, m->ki, m->sqn, _rand,
376                                   autn, ik, ck, res, &res_len);
377         } else {
378                 printf("Unknown IMSI: %s\n", imsi);
379 #ifdef AKA_USE_FIXED_TEST_VALUES
380                 printf("Using fixed test values for AKA\n");
381                 memset(_rand, '0', EAP_AKA_RAND_LEN);
382                 memset(autn, '1', EAP_AKA_AUTN_LEN);
383                 memset(ik, '3', EAP_AKA_IK_LEN);
384                 memset(ck, '4', EAP_AKA_CK_LEN);
385                 memset(res, '2', EAP_AKA_RES_MAX_LEN);
386                 res_len = EAP_AKA_RES_MAX_LEN;
387 #else /* AKA_USE_FIXED_TEST_VALUES */
388                 return;
389 #endif /* AKA_USE_FIXED_TEST_VALUES */
390         }
391
392         pos = reply;
393         end = &reply[sizeof(reply)];
394         ret = snprintf(pos, end - pos, "AKA-RESP-AUTH %s ", imsi);
395         if (ret < 0 || ret >= end - pos)
396                 return;
397         pos += ret;
398         pos += wpa_snprintf_hex(pos, end - pos, _rand, EAP_AKA_RAND_LEN);
399         *pos++ = ' ';
400         pos += wpa_snprintf_hex(pos, end - pos, autn, EAP_AKA_AUTN_LEN);
401         *pos++ = ' ';
402         pos += wpa_snprintf_hex(pos, end - pos, ik, EAP_AKA_IK_LEN);
403         *pos++ = ' ';
404         pos += wpa_snprintf_hex(pos, end - pos, ck, EAP_AKA_CK_LEN);
405         *pos++ = ' ';
406         pos += wpa_snprintf_hex(pos, end - pos, res, res_len);
407
408         printf("Send: %s\n", reply);
409
410         if (sendto(s, reply, pos - reply, 0, (struct sockaddr *) from,
411                    fromlen) < 0)
412                 perror("send");
413 }
414
415
416 static void aka_auts(int s, struct sockaddr_un *from, socklen_t fromlen,
417                      char *imsi)
418 {
419         char *auts, *rand;
420         u8 _auts[EAP_AKA_AUTS_LEN], _rand[EAP_AKA_RAND_LEN], sqn[6];
421         struct milenage_parameters *m;
422
423         /* AKA-AUTS <IMSI> <AUTS> <RAND> */
424
425         auts = strchr(imsi, ' ');
426         if (auts == NULL)
427                 return;
428         *auts++ = '\0';
429
430         rand = strchr(auts, ' ');
431         if (rand == NULL)
432                 return;
433         *rand++ = '\0';
434
435         printf("AKA-AUTS: IMSI=%s AUTS=%s RAND=%s\n", imsi, auts, rand);
436         if (hexstr2bin(auts, _auts, EAP_AKA_AUTS_LEN) ||
437             hexstr2bin(rand, _rand, EAP_AKA_RAND_LEN)) {
438                 printf("Could not parse AUTS/RAND\n");
439                 return;
440         }
441
442         m = get_milenage(imsi);
443         if (m == NULL) {
444                 printf("Unknown IMSI: %s\n", imsi);
445                 return;
446         }
447
448         if (milenage_auts(m->opc, m->ki, _rand, _auts, sqn)) {
449                 printf("AKA-AUTS: Incorrect MAC-S\n");
450         } else {
451                 memcpy(m->sqn, sqn, 6);
452                 printf("AKA-AUTS: Re-synchronized: "
453                        "SQN=%02x%02x%02x%02x%02x%02x\n",
454                        sqn[0], sqn[1], sqn[2], sqn[3], sqn[4], sqn[5]);
455         }
456 }
457
458
459 static int process(int s)
460 {
461         char buf[1000];
462         struct sockaddr_un from;
463         socklen_t fromlen;
464         ssize_t res;
465
466         fromlen = sizeof(from);
467         res = recvfrom(s, buf, sizeof(buf), 0, (struct sockaddr *) &from,
468                        &fromlen);
469         if (res < 0) {
470                 perror("recvfrom");
471                 return -1;
472         }
473
474         if (res == 0)
475                 return 0;
476
477         if ((size_t) res >= sizeof(buf))
478                 res = sizeof(buf) - 1;
479         buf[res] = '\0';
480
481         printf("Received: %s\n", buf);
482
483         if (strncmp(buf, "SIM-REQ-AUTH ", 13) == 0)
484                 sim_req_auth(s, &from, fromlen, buf + 13);
485         else if (strncmp(buf, "AKA-REQ-AUTH ", 13) == 0)
486                 aka_req_auth(s, &from, fromlen, buf + 13);
487         else if (strncmp(buf, "AKA-AUTS ", 9) == 0)
488                 aka_auts(s, &from, fromlen, buf + 9);
489         else
490                 printf("Unknown request: %s\n", buf);
491
492         return 0;
493 }
494
495
496 static void cleanup(void)
497 {
498         struct milenage_parameters *m, *prev;
499
500         m = milenage_db;
501         while (m) {
502                 prev = m;
503                 m = m->next;
504                 free(prev);
505         }
506
507         close(serv_sock);
508         unlink(socket_path);
509 }
510
511
512 static void handle_term(int sig)
513 {
514         printf("Signal %d - terminate\n", sig);
515         exit(0);
516 }
517
518
519 static void usage(void)
520 {
521         printf("HLR/AuC testing gateway for hostapd EAP-SIM/AKA "
522                "database/authenticator\n"
523                "Copyright (c) 2005-2006, Jouni Malinen <j@w1.fi>\n"
524                "\n"
525                "usage:\n"
526                "hlr_auc_gw [-h] [-s<socket path>] [-g<triplet file>] "
527                "[-m<milenage file>]\n"
528                "\n"
529                "options:\n"
530                "  -h = show this usage help\n"
531                "  -s<socket path> = path for UNIX domain socket\n"
532                "                    (default: %s)\n"
533                "  -g<triplet file> = path for GSM authentication triplets\n"
534                "                     (default: %s)\n"
535                "  -m<milenage file> = path for Milenage keys\n",
536                default_socket_path, default_gsm_triplet_file);
537 }
538
539
540 int main(int argc, char *argv[])
541 {
542         int c;
543         char *milenage_file = NULL;
544
545         socket_path = default_socket_path;
546         gsm_triplet_file = default_gsm_triplet_file;
547
548         for (;;) {
549                 c = getopt(argc, argv, "g:hm:s:");
550                 if (c < 0)
551                         break;
552                 switch (c) {
553                 case 'g':
554                         gsm_triplet_file = optarg;
555                         break;
556                 case 'h':
557                         usage();
558                         return 0;
559                 case 'm':
560                         milenage_file = optarg;
561                         break;
562                 case 's':
563                         socket_path = optarg;
564                         break;
565                 default:
566                         usage();
567                         return -1;
568                 }
569         }
570
571         if (milenage_file && read_milenage(milenage_file) < 0)
572                 return -1;
573
574         serv_sock = open_socket(socket_path);
575         if (serv_sock < 0)
576                 return -1;
577
578         printf("Listening for requests on %s\n", socket_path);
579
580         atexit(cleanup);
581         signal(SIGTERM, handle_term);
582         signal(SIGINT, handle_term);
583
584         for (;;)
585                 process(serv_sock);
586
587         return 0;
588 }