]> git.ipfire.org Git - thirdparty/hostap.git/blame - src/hlr_auc_gw/hlr_auc_gw.c
dbus: Remove unneeded typecast
[thirdparty/hostap.git] / src / hlr_auc_gw / hlr_auc_gw.c
CommitLineData
6fc6879b
JM
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
52static const char *default_socket_path = "/tmp/hlr_auc_gw.sock";
53static const char *socket_path;
54static int serv_sock = -1;
55
56/* GSM triplets */
57struct 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
65static struct gsm_triplet *gsm_db = NULL, *gsm_db_pos = NULL;
66
67/* OPc and AMF parameters for Milenage (Example algorithms for AKA). */
68struct 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
77static 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
89static 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
113static 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
226static 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
251static 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
376static 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
390static 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
460send:
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
468static 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
532static void aka_auts(int s, struct sockaddr_un *from, socklen_t fromlen,
533 char *imsi)
534{
2b16c01c 535 char *auts, *__rand;
6fc6879b
JM
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
2b16c01c 546 __rand = strchr(auts, ' ');
6fc6879b
JM
547 if (rand == NULL)
548 return;
2b16c01c 549 *__rand++ = '\0';
6fc6879b 550
2b16c01c 551 printf("AKA-AUTS: IMSI=%s AUTS=%s RAND=%s\n", imsi, auts, __rand);
6fc6879b 552 if (hexstr2bin(auts, _auts, EAP_AKA_AUTS_LEN) ||
2b16c01c 553 hexstr2bin(__rand, _rand, EAP_AKA_RAND_LEN)) {
6fc6879b
JM
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
575static 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
612static 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
636static void handle_term(int sig)
637{
638 printf("Signal %d - terminate\n", sig);
639 exit(0);
640}
641
642
643static 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
663int 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}