]> git.ipfire.org Git - thirdparty/hostap.git/blame - hostapd/hlr_auc_gw.c
hlr_auc_gw: Add support for processing command line operations
[thirdparty/hostap.git] / hostapd / hlr_auc_gw.c
CommitLineData
6fc6879b
JM
1/*
2 * HLR/AuC testing gateway for hostapd EAP-SIM/AKA database/authenticator
9a50ee6d 3 * Copyright (c) 2005-2007, 2012-2013, Jouni Malinen <j@w1.fi>
6fc6879b 4 *
0f3d578e
JM
5 * This software may be distributed under the terms of the BSD license.
6 * See README for more details.
6fc6879b
JM
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 *
9a50ee6d 33 * An example implementation here reads GSM authentication triplets from a
6fc6879b
JM
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
5daba48c
JM
37 * implementation and for EAP-SIM/AKA/AKA' testing.
38 *
9a50ee6d
JM
39 * For a stronger example design, Milenage and GSM-Milenage algorithms can be
40 * used to dynamically generate authenticatipn information for EAP-AKA/AKA' and
41 * EAP-SIM, respectively, if Ki is known.
42 *
5daba48c
JM
43 * SQN generation follows the not time-based Profile 2 described in
44 * 3GPP TS 33.102 Annex C.3.2. The length of IND is 5 bits by default, but this
45 * can be changed with a command line options if needed.
6fc6879b
JM
46 */
47
48#include "includes.h"
49#include <sys/un.h>
597d5d1b
JM
50#ifdef CONFIG_SQLITE
51#include <sqlite3.h>
52#endif /* CONFIG_SQLITE */
6fc6879b
JM
53
54#include "common.h"
43df4cc2 55#include "crypto/milenage.h"
3642c431 56#include "crypto/random.h"
6fc6879b
JM
57
58static const char *default_socket_path = "/tmp/hlr_auc_gw.sock";
59static const char *socket_path;
60static int serv_sock = -1;
53368613
JM
61static char *milenage_file = NULL;
62static int update_milenage = 0;
63static int sqn_changes = 0;
5daba48c 64static int ind_len = 5;
3e6547b5 65static int stdout_debug = 1;
6fc6879b
JM
66
67/* GSM triplets */
68struct gsm_triplet {
69 struct gsm_triplet *next;
70 char imsi[20];
71 u8 kc[8];
72 u8 sres[4];
73 u8 _rand[16];
74};
75
76static struct gsm_triplet *gsm_db = NULL, *gsm_db_pos = NULL;
77
78/* OPc and AMF parameters for Milenage (Example algorithms for AKA). */
79struct milenage_parameters {
80 struct milenage_parameters *next;
81 char imsi[20];
82 u8 ki[16];
83 u8 opc[16];
84 u8 amf[2];
85 u8 sqn[6];
4309ca51 86 int set;
6fc6879b
JM
87};
88
89static struct milenage_parameters *milenage_db = NULL;
90
91#define EAP_SIM_MAX_CHAL 3
92
93#define EAP_AKA_RAND_LEN 16
94#define EAP_AKA_AUTN_LEN 16
95#define EAP_AKA_AUTS_LEN 14
96#define EAP_AKA_RES_MAX_LEN 16
97#define EAP_AKA_IK_LEN 16
98#define EAP_AKA_CK_LEN 16
99
100
597d5d1b
JM
101#ifdef CONFIG_SQLITE
102
103static sqlite3 *sqlite_db = NULL;
104static struct milenage_parameters db_tmp_milenage;
105
106
107static int db_table_exists(sqlite3 *db, const char *name)
108{
109 char cmd[128];
110 os_snprintf(cmd, sizeof(cmd), "SELECT 1 FROM %s;", name);
111 return sqlite3_exec(db, cmd, NULL, NULL, NULL) == SQLITE_OK;
112}
113
114
115static int db_table_create_milenage(sqlite3 *db)
116{
117 char *err = NULL;
118 const char *sql =
119 "CREATE TABLE milenage("
120 " imsi INTEGER PRIMARY KEY NOT NULL,"
121 " ki CHAR(32) NOT NULL,"
122 " opc CHAR(32) NOT NULL,"
123 " amf CHAR(4) NOT NULL,"
124 " sqn CHAR(12) NOT NULL"
125 ");";
126
127 printf("Adding database table for milenage information\n");
128 if (sqlite3_exec(db, sql, NULL, NULL, &err) != SQLITE_OK) {
129 printf("SQLite error: %s\n", err);
130 sqlite3_free(err);
131 return -1;
132 }
133
134 return 0;
135}
136
137
138static sqlite3 * db_open(const char *db_file)
139{
140 sqlite3 *db;
141
142 if (sqlite3_open(db_file, &db)) {
143 printf("Failed to open database %s: %s\n",
144 db_file, sqlite3_errmsg(db));
145 sqlite3_close(db);
146 return NULL;
147 }
148
149 if (!db_table_exists(db, "milenage") &&
150 db_table_create_milenage(db) < 0) {
151 sqlite3_close(db);
152 return NULL;
153 }
154
155 return db;
156}
157
158
159static int get_milenage_cb(void *ctx, int argc, char *argv[], char *col[])
160{
161 struct milenage_parameters *m = ctx;
162 int i;
163
4309ca51
JM
164 m->set = 1;
165
597d5d1b
JM
166 for (i = 0; i < argc; i++) {
167 if (os_strcmp(col[i], "ki") == 0 && argv[i] &&
168 hexstr2bin(argv[i], m->ki, sizeof(m->ki))) {
169 printf("Invalid ki value in database\n");
170 return -1;
171 }
172
173 if (os_strcmp(col[i], "opc") == 0 && argv[i] &&
174 hexstr2bin(argv[i], m->opc, sizeof(m->opc))) {
175 printf("Invalid opcvalue in database\n");
176 return -1;
177 }
178
179 if (os_strcmp(col[i], "amf") == 0 && argv[i] &&
180 hexstr2bin(argv[i], m->amf, sizeof(m->amf))) {
181 printf("Invalid amf value in database\n");
182 return -1;
183 }
184
185 if (os_strcmp(col[i], "sqn") == 0 && argv[i] &&
186 hexstr2bin(argv[i], m->sqn, sizeof(m->sqn))) {
187 printf("Invalid sqn value in database\n");
188 return -1;
189 }
190 }
191
192 return 0;
193}
194
195
196static struct milenage_parameters * db_get_milenage(const char *imsi_txt)
197{
198 char cmd[128];
199 unsigned long long imsi;
200
201 os_memset(&db_tmp_milenage, 0, sizeof(db_tmp_milenage));
202 imsi = atoll(imsi_txt);
203 os_snprintf(db_tmp_milenage.imsi, sizeof(db_tmp_milenage.imsi),
204 "%llu", imsi);
205 os_snprintf(cmd, sizeof(cmd),
206 "SELECT ki,opc,amf,sqn FROM milenage WHERE imsi=%llu;",
207 imsi);
208 if (sqlite3_exec(sqlite_db, cmd, get_milenage_cb, &db_tmp_milenage,
209 NULL) != SQLITE_OK)
210 return NULL;
211
4309ca51
JM
212 if (!db_tmp_milenage.set)
213 return NULL;
597d5d1b
JM
214 return &db_tmp_milenage;
215}
216
217
218static int db_update_milenage_sqn(struct milenage_parameters *m)
219{
220 char cmd[128], val[13], *pos;
221
3e6547b5
JM
222 if (sqlite_db == NULL)
223 return 0;
224
597d5d1b
JM
225 pos = val;
226 pos += wpa_snprintf_hex(pos, sizeof(val), m->sqn, 6);
227 *pos = '\0';
228 os_snprintf(cmd, sizeof(cmd),
229 "UPDATE milenage SET sqn='%s' WHERE imsi=%s;",
230 val, m->imsi);
231 if (sqlite3_exec(sqlite_db, cmd, NULL, NULL, NULL) != SQLITE_OK) {
232 printf("Failed to update SQN in database for IMSI %s\n",
233 m->imsi);
234 return -1;
235 }
236 return 0;
237}
238
239#endif /* CONFIG_SQLITE */
240
241
6fc6879b
JM
242static int open_socket(const char *path)
243{
244 struct sockaddr_un addr;
245 int s;
246
247 s = socket(PF_UNIX, SOCK_DGRAM, 0);
248 if (s < 0) {
249 perror("socket(PF_UNIX)");
250 return -1;
251 }
252
253 memset(&addr, 0, sizeof(addr));
254 addr.sun_family = AF_UNIX;
255 os_strlcpy(addr.sun_path, path, sizeof(addr.sun_path));
256 if (bind(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
9d053747 257 perror("hlr-auc-gw: bind(PF_UNIX)");
6fc6879b
JM
258 close(s);
259 return -1;
260 }
261
262 return s;
263}
264
265
266static int read_gsm_triplets(const char *fname)
267{
268 FILE *f;
269 char buf[200], *pos, *pos2;
270 struct gsm_triplet *g = NULL;
271 int line, ret = 0;
272
273 if (fname == NULL)
274 return -1;
275
276 f = fopen(fname, "r");
277 if (f == NULL) {
278 printf("Could not open GSM tripler data file '%s'\n", fname);
279 return -1;
280 }
281
282 line = 0;
283 while (fgets(buf, sizeof(buf), f)) {
284 line++;
285
286 /* Parse IMSI:Kc:SRES:RAND */
287 buf[sizeof(buf) - 1] = '\0';
288 if (buf[0] == '#')
289 continue;
290 pos = buf;
291 while (*pos != '\0' && *pos != '\n')
292 pos++;
293 if (*pos == '\n')
294 *pos = '\0';
295 pos = buf;
296 if (*pos == '\0')
297 continue;
298
299 g = os_zalloc(sizeof(*g));
300 if (g == NULL) {
301 ret = -1;
302 break;
303 }
304
305 /* IMSI */
306 pos2 = strchr(pos, ':');
307 if (pos2 == NULL) {
308 printf("%s:%d - Invalid IMSI (%s)\n",
309 fname, line, pos);
310 ret = -1;
311 break;
312 }
313 *pos2 = '\0';
314 if (strlen(pos) >= sizeof(g->imsi)) {
315 printf("%s:%d - Too long IMSI (%s)\n",
316 fname, line, pos);
317 ret = -1;
318 break;
319 }
320 os_strlcpy(g->imsi, pos, sizeof(g->imsi));
321 pos = pos2 + 1;
322
323 /* Kc */
324 pos2 = strchr(pos, ':');
325 if (pos2 == NULL) {
326 printf("%s:%d - Invalid Kc (%s)\n", fname, line, pos);
327 ret = -1;
328 break;
329 }
330 *pos2 = '\0';
331 if (strlen(pos) != 16 || hexstr2bin(pos, g->kc, 8)) {
332 printf("%s:%d - Invalid Kc (%s)\n", fname, line, pos);
333 ret = -1;
334 break;
335 }
336 pos = pos2 + 1;
337
338 /* SRES */
339 pos2 = strchr(pos, ':');
340 if (pos2 == NULL) {
341 printf("%s:%d - Invalid SRES (%s)\n", fname, line,
342 pos);
343 ret = -1;
344 break;
345 }
346 *pos2 = '\0';
347 if (strlen(pos) != 8 || hexstr2bin(pos, g->sres, 4)) {
348 printf("%s:%d - Invalid SRES (%s)\n", fname, line,
349 pos);
350 ret = -1;
351 break;
352 }
353 pos = pos2 + 1;
354
355 /* RAND */
356 pos2 = strchr(pos, ':');
357 if (pos2)
358 *pos2 = '\0';
359 if (strlen(pos) != 32 || hexstr2bin(pos, g->_rand, 16)) {
360 printf("%s:%d - Invalid RAND (%s)\n", fname, line,
361 pos);
362 ret = -1;
363 break;
364 }
365 pos = pos2 + 1;
366
367 g->next = gsm_db;
368 gsm_db = g;
369 g = NULL;
370 }
057a92ec 371 os_free(g);
6fc6879b
JM
372
373 fclose(f);
374
375 return ret;
376}
377
378
379static struct gsm_triplet * get_gsm_triplet(const char *imsi)
380{
381 struct gsm_triplet *g = gsm_db_pos;
382
383 while (g) {
384 if (strcmp(g->imsi, imsi) == 0) {
385 gsm_db_pos = g->next;
386 return g;
387 }
388 g = g->next;
389 }
390
391 g = gsm_db;
392 while (g && g != gsm_db_pos) {
393 if (strcmp(g->imsi, imsi) == 0) {
394 gsm_db_pos = g->next;
395 return g;
396 }
397 g = g->next;
398 }
399
400 return NULL;
401}
402
403
404static int read_milenage(const char *fname)
405{
406 FILE *f;
407 char buf[200], *pos, *pos2;
408 struct milenage_parameters *m = NULL;
409 int line, ret = 0;
410
411 if (fname == NULL)
412 return -1;
413
414 f = fopen(fname, "r");
415 if (f == NULL) {
416 printf("Could not open Milenage data file '%s'\n", fname);
417 return -1;
418 }
419
420 line = 0;
421 while (fgets(buf, sizeof(buf), f)) {
422 line++;
423
424 /* Parse IMSI Ki OPc AMF SQN */
425 buf[sizeof(buf) - 1] = '\0';
426 if (buf[0] == '#')
427 continue;
428 pos = buf;
429 while (*pos != '\0' && *pos != '\n')
430 pos++;
431 if (*pos == '\n')
432 *pos = '\0';
433 pos = buf;
434 if (*pos == '\0')
435 continue;
436
437 m = os_zalloc(sizeof(*m));
438 if (m == NULL) {
439 ret = -1;
440 break;
441 }
442
443 /* IMSI */
444 pos2 = strchr(pos, ' ');
445 if (pos2 == NULL) {
446 printf("%s:%d - Invalid IMSI (%s)\n",
447 fname, line, pos);
448 ret = -1;
449 break;
450 }
451 *pos2 = '\0';
452 if (strlen(pos) >= sizeof(m->imsi)) {
453 printf("%s:%d - Too long IMSI (%s)\n",
454 fname, line, pos);
455 ret = -1;
456 break;
457 }
458 os_strlcpy(m->imsi, pos, sizeof(m->imsi));
459 pos = pos2 + 1;
460
461 /* Ki */
462 pos2 = strchr(pos, ' ');
463 if (pos2 == NULL) {
464 printf("%s:%d - Invalid Ki (%s)\n", fname, line, pos);
465 ret = -1;
466 break;
467 }
468 *pos2 = '\0';
469 if (strlen(pos) != 32 || hexstr2bin(pos, m->ki, 16)) {
470 printf("%s:%d - Invalid Ki (%s)\n", fname, line, pos);
471 ret = -1;
472 break;
473 }
474 pos = pos2 + 1;
475
476 /* OPc */
477 pos2 = strchr(pos, ' ');
478 if (pos2 == NULL) {
479 printf("%s:%d - Invalid OPc (%s)\n", fname, line, pos);
480 ret = -1;
481 break;
482 }
483 *pos2 = '\0';
484 if (strlen(pos) != 32 || hexstr2bin(pos, m->opc, 16)) {
485 printf("%s:%d - Invalid OPc (%s)\n", fname, line, pos);
486 ret = -1;
487 break;
488 }
489 pos = pos2 + 1;
490
491 /* AMF */
492 pos2 = strchr(pos, ' ');
493 if (pos2 == NULL) {
494 printf("%s:%d - Invalid AMF (%s)\n", fname, line, pos);
495 ret = -1;
496 break;
497 }
498 *pos2 = '\0';
499 if (strlen(pos) != 4 || hexstr2bin(pos, m->amf, 2)) {
500 printf("%s:%d - Invalid AMF (%s)\n", fname, line, pos);
501 ret = -1;
502 break;
503 }
504 pos = pos2 + 1;
505
506 /* SQN */
507 pos2 = strchr(pos, ' ');
508 if (pos2)
509 *pos2 = '\0';
510 if (strlen(pos) != 12 || hexstr2bin(pos, m->sqn, 6)) {
511 printf("%s:%d - Invalid SEQ (%s)\n", fname, line, pos);
512 ret = -1;
513 break;
514 }
515 pos = pos2 + 1;
516
517 m->next = milenage_db;
518 milenage_db = m;
519 m = NULL;
520 }
057a92ec 521 os_free(m);
6fc6879b
JM
522
523 fclose(f);
524
525 return ret;
526}
527
528
53368613
JM
529static void update_milenage_file(const char *fname)
530{
531 FILE *f, *f2;
532 char buf[500], *pos;
533 char *end = buf + sizeof(buf);
534 struct milenage_parameters *m;
535 size_t imsi_len;
536
537 f = fopen(fname, "r");
538 if (f == NULL) {
539 printf("Could not open Milenage data file '%s'\n", fname);
540 return;
541 }
542
543 snprintf(buf, sizeof(buf), "%s.new", fname);
544 f2 = fopen(buf, "w");
545 if (f2 == NULL) {
546 printf("Could not write Milenage data file '%s'\n", buf);
547 fclose(f);
548 return;
549 }
550
551 while (fgets(buf, sizeof(buf), f)) {
552 /* IMSI Ki OPc AMF SQN */
553 buf[sizeof(buf) - 1] = '\0';
554
555 pos = strchr(buf, ' ');
556 if (buf[0] == '#' || pos == NULL || pos - buf >= 20)
557 goto no_update;
558
559 imsi_len = pos - buf;
560
561 for (m = milenage_db; m; m = m->next) {
562 if (strncmp(buf, m->imsi, imsi_len) == 0 &&
563 m->imsi[imsi_len] == '\0')
564 break;
565 }
566
567 if (!m)
568 goto no_update;
569
570 pos = buf;
571 pos += snprintf(pos, end - pos, "%s ", m->imsi);
572 pos += wpa_snprintf_hex(pos, end - pos, m->ki, 16);
573 *pos++ = ' ';
574 pos += wpa_snprintf_hex(pos, end - pos, m->opc, 16);
575 *pos++ = ' ';
576 pos += wpa_snprintf_hex(pos, end - pos, m->amf, 2);
577 *pos++ = ' ';
578 pos += wpa_snprintf_hex(pos, end - pos, m->sqn, 6);
579 *pos++ = '\n';
580
581 no_update:
582 fprintf(f2, "%s", buf);
583 }
584
585 fclose(f2);
586 fclose(f);
587
588 snprintf(buf, sizeof(buf), "%s.bak", fname);
589 if (rename(fname, buf) < 0) {
590 perror("rename");
591 return;
592 }
593
594 snprintf(buf, sizeof(buf), "%s.new", fname);
595 if (rename(buf, fname) < 0) {
596 perror("rename");
597 return;
598 }
599
600}
601
602
6fc6879b
JM
603static struct milenage_parameters * get_milenage(const char *imsi)
604{
605 struct milenage_parameters *m = milenage_db;
606
607 while (m) {
608 if (strcmp(m->imsi, imsi) == 0)
609 break;
610 m = m->next;
611 }
612
597d5d1b
JM
613#ifdef CONFIG_SQLITE
614 if (!m)
615 m = db_get_milenage(imsi);
616#endif /* CONFIG_SQLITE */
617
6fc6879b
JM
618 return m;
619}
620
621
3e6547b5 622static int sim_req_auth(char *imsi, char *resp, size_t resp_len)
6fc6879b
JM
623{
624 int count, max_chal, ret;
625 char *pos;
3e6547b5 626 char *rpos, *rend;
6fc6879b
JM
627 struct milenage_parameters *m;
628 struct gsm_triplet *g;
629
3e6547b5 630 resp[0] = '\0';
6fc6879b
JM
631
632 pos = strchr(imsi, ' ');
633 if (pos) {
634 *pos++ = '\0';
635 max_chal = atoi(pos);
636 if (max_chal < 1 || max_chal < EAP_SIM_MAX_CHAL)
637 max_chal = EAP_SIM_MAX_CHAL;
638 } else
639 max_chal = EAP_SIM_MAX_CHAL;
640
3e6547b5
JM
641 rend = resp + resp_len;
642 rpos = resp;
6fc6879b
JM
643 ret = snprintf(rpos, rend - rpos, "SIM-RESP-AUTH %s", imsi);
644 if (ret < 0 || ret >= rend - rpos)
3e6547b5 645 return -1;
6fc6879b
JM
646 rpos += ret;
647
648 m = get_milenage(imsi);
649 if (m) {
650 u8 _rand[16], sres[4], kc[8];
651 for (count = 0; count < max_chal; count++) {
3642c431 652 if (random_get_bytes(_rand, 16) < 0)
3e6547b5 653 return -1;
6fc6879b
JM
654 gsm_milenage(m->opc, m->ki, _rand, sres, kc);
655 *rpos++ = ' ';
656 rpos += wpa_snprintf_hex(rpos, rend - rpos, kc, 8);
657 *rpos++ = ':';
658 rpos += wpa_snprintf_hex(rpos, rend - rpos, sres, 4);
659 *rpos++ = ':';
660 rpos += wpa_snprintf_hex(rpos, rend - rpos, _rand, 16);
661 }
662 *rpos = '\0';
3e6547b5 663 return 0;
6fc6879b
JM
664 }
665
666 count = 0;
667 while (count < max_chal && (g = get_gsm_triplet(imsi))) {
668 if (strcmp(g->imsi, imsi) != 0)
669 continue;
670
671 if (rpos < rend)
672 *rpos++ = ' ';
673 rpos += wpa_snprintf_hex(rpos, rend - rpos, g->kc, 8);
674 if (rpos < rend)
675 *rpos++ = ':';
676 rpos += wpa_snprintf_hex(rpos, rend - rpos, g->sres, 4);
677 if (rpos < rend)
678 *rpos++ = ':';
679 rpos += wpa_snprintf_hex(rpos, rend - rpos, g->_rand, 16);
680 count++;
681 }
682
683 if (count == 0) {
684 printf("No GSM triplets found for %s\n", imsi);
685 ret = snprintf(rpos, rend - rpos, " FAILURE");
686 if (ret < 0 || ret >= rend - rpos)
3e6547b5 687 return -1;
6fc6879b
JM
688 rpos += ret;
689 }
690
3e6547b5 691 return 0;
6fc6879b
JM
692}
693
694
5daba48c
JM
695static void inc_sqn(u8 *sqn)
696{
697 u64 val, seq, ind;
698
699 /*
700 * SQN = SEQ | IND = SEQ1 | SEQ2 | IND
701 *
702 * The mechanism used here is not time-based, so SEQ2 is void and
703 * SQN = SEQ1 | IND. The length of IND is ind_len bits and the length
704 * of SEQ1 is 48 - ind_len bits.
705 */
706
707 /* Increment both SEQ and IND by one */
708 val = ((u64) WPA_GET_BE32(sqn) << 16) | ((u64) WPA_GET_BE16(sqn + 4));
709 seq = (val >> ind_len) + 1;
710 ind = (val + 1) & ((1 << ind_len) - 1);
711 val = (seq << ind_len) | ind;
712 WPA_PUT_BE32(sqn, val >> 16);
713 WPA_PUT_BE16(sqn + 4, val & 0xffff);
714}
715
716
3e6547b5 717static int aka_req_auth(char *imsi, char *resp, size_t resp_len)
6fc6879b
JM
718{
719 /* AKA-RESP-AUTH <IMSI> <RAND> <AUTN> <IK> <CK> <RES> */
3e6547b5 720 char *pos, *end;
6fc6879b
JM
721 u8 _rand[EAP_AKA_RAND_LEN];
722 u8 autn[EAP_AKA_AUTN_LEN];
723 u8 ik[EAP_AKA_IK_LEN];
724 u8 ck[EAP_AKA_CK_LEN];
725 u8 res[EAP_AKA_RES_MAX_LEN];
726 size_t res_len;
727 int ret;
728 struct milenage_parameters *m;
e43bd7a1 729 int failed = 0;
6fc6879b
JM
730
731 m = get_milenage(imsi);
732 if (m) {
3642c431 733 if (random_get_bytes(_rand, EAP_AKA_RAND_LEN) < 0)
3e6547b5 734 return -1;
6fc6879b 735 res_len = EAP_AKA_RES_MAX_LEN;
5daba48c 736 inc_sqn(m->sqn);
597d5d1b
JM
737#ifdef CONFIG_SQLITE
738 db_update_milenage_sqn(m);
739#endif /* CONFIG_SQLITE */
53368613 740 sqn_changes = 1;
3e6547b5
JM
741 if (stdout_debug) {
742 printf("AKA: Milenage with SQN=%02x%02x%02x%02x%02x%02x\n",
743 m->sqn[0], m->sqn[1], m->sqn[2],
744 m->sqn[3], m->sqn[4], m->sqn[5]);
745 }
6fc6879b
JM
746 milenage_generate(m->opc, m->amf, m->ki, m->sqn, _rand,
747 autn, ik, ck, res, &res_len);
748 } else {
749 printf("Unknown IMSI: %s\n", imsi);
750#ifdef AKA_USE_FIXED_TEST_VALUES
751 printf("Using fixed test values for AKA\n");
752 memset(_rand, '0', EAP_AKA_RAND_LEN);
753 memset(autn, '1', EAP_AKA_AUTN_LEN);
754 memset(ik, '3', EAP_AKA_IK_LEN);
755 memset(ck, '4', EAP_AKA_CK_LEN);
756 memset(res, '2', EAP_AKA_RES_MAX_LEN);
757 res_len = EAP_AKA_RES_MAX_LEN;
758#else /* AKA_USE_FIXED_TEST_VALUES */
e43bd7a1 759 failed = 1;
6fc6879b
JM
760#endif /* AKA_USE_FIXED_TEST_VALUES */
761 }
762
3e6547b5
JM
763 pos = resp;
764 end = resp + resp_len;
6fc6879b
JM
765 ret = snprintf(pos, end - pos, "AKA-RESP-AUTH %s ", imsi);
766 if (ret < 0 || ret >= end - pos)
3e6547b5 767 return -1;
6fc6879b 768 pos += ret;
e43bd7a1
JM
769 if (failed) {
770 ret = snprintf(pos, end - pos, "FAILURE");
771 if (ret < 0 || ret >= end - pos)
3e6547b5 772 return -1;
e43bd7a1 773 pos += ret;
3e6547b5 774 return 0;
e43bd7a1 775 }
6fc6879b
JM
776 pos += wpa_snprintf_hex(pos, end - pos, _rand, EAP_AKA_RAND_LEN);
777 *pos++ = ' ';
778 pos += wpa_snprintf_hex(pos, end - pos, autn, EAP_AKA_AUTN_LEN);
779 *pos++ = ' ';
780 pos += wpa_snprintf_hex(pos, end - pos, ik, EAP_AKA_IK_LEN);
781 *pos++ = ' ';
782 pos += wpa_snprintf_hex(pos, end - pos, ck, EAP_AKA_CK_LEN);
783 *pos++ = ' ';
784 pos += wpa_snprintf_hex(pos, end - pos, res, res_len);
785
3e6547b5 786 return 0;
6fc6879b
JM
787}
788
789
3e6547b5 790static int aka_auts(char *imsi, char *resp, size_t resp_len)
6fc6879b 791{
2b16c01c 792 char *auts, *__rand;
6fc6879b
JM
793 u8 _auts[EAP_AKA_AUTS_LEN], _rand[EAP_AKA_RAND_LEN], sqn[6];
794 struct milenage_parameters *m;
795
3e6547b5
JM
796 resp[0] = '\0';
797
6fc6879b
JM
798 /* AKA-AUTS <IMSI> <AUTS> <RAND> */
799
800 auts = strchr(imsi, ' ');
801 if (auts == NULL)
3e6547b5 802 return -1;
6fc6879b
JM
803 *auts++ = '\0';
804
2b16c01c 805 __rand = strchr(auts, ' ');
6689218e 806 if (__rand == NULL)
3e6547b5 807 return -1;
2b16c01c 808 *__rand++ = '\0';
6fc6879b 809
3e6547b5
JM
810 if (stdout_debug) {
811 printf("AKA-AUTS: IMSI=%s AUTS=%s RAND=%s\n",
812 imsi, auts, __rand);
813 }
6fc6879b 814 if (hexstr2bin(auts, _auts, EAP_AKA_AUTS_LEN) ||
2b16c01c 815 hexstr2bin(__rand, _rand, EAP_AKA_RAND_LEN)) {
6fc6879b 816 printf("Could not parse AUTS/RAND\n");
3e6547b5 817 return -1;
6fc6879b
JM
818 }
819
820 m = get_milenage(imsi);
821 if (m == NULL) {
822 printf("Unknown IMSI: %s\n", imsi);
3e6547b5 823 return -1;
6fc6879b
JM
824 }
825
826 if (milenage_auts(m->opc, m->ki, _rand, _auts, sqn)) {
827 printf("AKA-AUTS: Incorrect MAC-S\n");
828 } else {
829 memcpy(m->sqn, sqn, 6);
3e6547b5
JM
830 if (stdout_debug) {
831 printf("AKA-AUTS: Re-synchronized: "
832 "SQN=%02x%02x%02x%02x%02x%02x\n",
833 sqn[0], sqn[1], sqn[2], sqn[3], sqn[4], sqn[5]);
834 }
597d5d1b
JM
835#ifdef CONFIG_SQLITE
836 db_update_milenage_sqn(m);
837#endif /* CONFIG_SQLITE */
53368613 838 sqn_changes = 1;
6fc6879b 839 }
3e6547b5
JM
840
841 return 0;
842}
843
844
845static int process_cmd(char *cmd, char *resp, size_t resp_len)
846{
847 if (os_strncmp(cmd, "SIM-REQ-AUTH ", 13) == 0)
848 return sim_req_auth(cmd + 13, resp, resp_len);
849
850 if (os_strncmp(cmd, "AKA-REQ-AUTH ", 13) == 0)
851 return aka_req_auth(cmd + 13, resp, resp_len);
852
853 if (os_strncmp(cmd, "AKA-AUTS ", 9) == 0)
854 return aka_auts(cmd + 9, resp, resp_len);
855
856 printf("Unknown request: %s\n", cmd);
857 return -1;
6fc6879b
JM
858}
859
860
861static int process(int s)
862{
3e6547b5 863 char buf[1000], resp[1000];
6fc6879b
JM
864 struct sockaddr_un from;
865 socklen_t fromlen;
866 ssize_t res;
867
868 fromlen = sizeof(from);
869 res = recvfrom(s, buf, sizeof(buf), 0, (struct sockaddr *) &from,
870 &fromlen);
871 if (res < 0) {
872 perror("recvfrom");
873 return -1;
874 }
875
876 if (res == 0)
877 return 0;
878
879 if ((size_t) res >= sizeof(buf))
880 res = sizeof(buf) - 1;
881 buf[res] = '\0';
882
883 printf("Received: %s\n", buf);
884
3e6547b5
JM
885 if (process_cmd(buf, resp, sizeof(resp)) < 0) {
886 printf("Failed to process request\n");
887 return -1;
888 }
889
890 if (resp[0] == '\0') {
891 printf("No response\n");
892 return 0;
893 }
894
895 printf("Send: %s\n", resp);
896
897 if (sendto(s, resp, os_strlen(resp), 0, (struct sockaddr *) &from,
898 fromlen) < 0)
899 perror("send");
6fc6879b
JM
900
901 return 0;
902}
903
904
905static void cleanup(void)
906{
907 struct gsm_triplet *g, *gprev;
908 struct milenage_parameters *m, *prev;
909
53368613
JM
910 if (update_milenage && milenage_file && sqn_changes)
911 update_milenage_file(milenage_file);
912
6fc6879b
JM
913 g = gsm_db;
914 while (g) {
915 gprev = g;
916 g = g->next;
057a92ec 917 os_free(gprev);
6fc6879b
JM
918 }
919
920 m = milenage_db;
921 while (m) {
922 prev = m;
923 m = m->next;
057a92ec 924 os_free(prev);
6fc6879b
JM
925 }
926
3e6547b5
JM
927 if (serv_sock >= 0)
928 close(serv_sock);
929 if (socket_path)
930 unlink(socket_path);
597d5d1b
JM
931
932#ifdef CONFIG_SQLITE
933 if (sqlite_db) {
934 sqlite3_close(sqlite_db);
935 sqlite_db = NULL;
936 }
937#endif /* CONFIG_SQLITE */
6fc6879b
JM
938}
939
940
941static void handle_term(int sig)
942{
943 printf("Signal %d - terminate\n", sig);
944 exit(0);
945}
946
947
948static void usage(void)
949{
950 printf("HLR/AuC testing gateway for hostapd EAP-SIM/AKA "
951 "database/authenticator\n"
9a50ee6d 952 "Copyright (c) 2005-2007, 2012-2013, Jouni Malinen <j@w1.fi>\n"
6fc6879b
JM
953 "\n"
954 "usage:\n"
53368613 955 "hlr_auc_gw [-hu] [-s<socket path>] [-g<triplet file>] "
5daba48c 956 "[-m<milenage file>] \\\n"
3e6547b5 957 " [-D<DB file>] [-i<IND len in bits>] [command]\n"
6fc6879b
JM
958 "\n"
959 "options:\n"
960 " -h = show this usage help\n"
53368613 961 " -u = update SQN in Milenage file on exit\n"
6fc6879b
JM
962 " -s<socket path> = path for UNIX domain socket\n"
963 " (default: %s)\n"
964 " -g<triplet file> = path for GSM authentication triplets\n"
5daba48c 965 " -m<milenage file> = path for Milenage keys\n"
597d5d1b 966 " -D<DB file> = path to SQLite database\n"
3e6547b5
JM
967 " -i<IND len in bits> = IND length for SQN (default: 5)\n"
968 "\n"
969 "If the optional command argument, like "
970 "\"AKA-REQ-AUTH <IMSI>\" is used, a single\n"
971 "command is processed with response sent to stdout. Otherwise, "
972 "hlr_auc_gw opens\n"
973 "a control interface and processes commands sent through it "
974 "(e.g., by EAP server\n"
975 "in hostapd).\n",
6fc6879b
JM
976 default_socket_path);
977}
978
979
980int main(int argc, char *argv[])
981{
982 int c;
6fc6879b 983 char *gsm_triplet_file = NULL;
597d5d1b 984 char *sqlite_db_file = NULL;
3e6547b5 985 int ret = 0;
6fc6879b 986
057a92ec
JM
987 if (os_program_init())
988 return -1;
989
6fc6879b
JM
990 socket_path = default_socket_path;
991
992 for (;;) {
597d5d1b 993 c = getopt(argc, argv, "D:g:hi:m:s:u");
6fc6879b
JM
994 if (c < 0)
995 break;
996 switch (c) {
597d5d1b
JM
997 case 'D':
998#ifdef CONFIG_SQLITE
999 sqlite_db_file = optarg;
1000 break;
1001#else /* CONFIG_SQLITE */
1002 printf("No SQLite support included in the build\n");
1003 return -1;
1004#endif /* CONFIG_SQLITE */
6fc6879b
JM
1005 case 'g':
1006 gsm_triplet_file = optarg;
1007 break;
1008 case 'h':
1009 usage();
1010 return 0;
5daba48c
JM
1011 case 'i':
1012 ind_len = atoi(optarg);
1013 if (ind_len < 0 || ind_len > 32) {
1014 printf("Invalid IND length\n");
1015 return -1;
1016 }
1017 break;
6fc6879b
JM
1018 case 'm':
1019 milenage_file = optarg;
1020 break;
1021 case 's':
1022 socket_path = optarg;
1023 break;
53368613
JM
1024 case 'u':
1025 update_milenage = 1;
1026 break;
6fc6879b
JM
1027 default:
1028 usage();
1029 return -1;
1030 }
1031 }
1032
597d5d1b
JM
1033 if (!gsm_triplet_file && !milenage_file && !sqlite_db_file) {
1034 usage();
1035 return -1;
1036 }
1037
1038#ifdef CONFIG_SQLITE
1039 if (sqlite_db_file && (sqlite_db = db_open(sqlite_db_file)) == NULL)
1040 return -1;
1041#endif /* CONFIG_SQLITE */
1042
6fc6879b
JM
1043 if (gsm_triplet_file && read_gsm_triplets(gsm_triplet_file) < 0)
1044 return -1;
1045
1046 if (milenage_file && read_milenage(milenage_file) < 0)
1047 return -1;
1048
3e6547b5
JM
1049 if (optind == argc) {
1050 serv_sock = open_socket(socket_path);
1051 if (serv_sock < 0)
1052 return -1;
6fc6879b 1053
3e6547b5 1054 printf("Listening for requests on %s\n", socket_path);
6fc6879b 1055
3e6547b5
JM
1056 atexit(cleanup);
1057 signal(SIGTERM, handle_term);
1058 signal(SIGINT, handle_term);
6fc6879b 1059
3e6547b5
JM
1060 for (;;)
1061 process(serv_sock);
1062 } else {
1063 char buf[1000];
1064 socket_path = NULL;
1065 stdout_debug = 0;
1066 if (process_cmd(argv[optind], buf, sizeof(buf)) < 0) {
1067 printf("FAIL\n");
1068 ret = -1;
1069 } else {
1070 printf("%s\n", buf);
1071 }
1072 cleanup();
1073 }
6fc6879b 1074
597d5d1b
JM
1075#ifdef CONFIG_SQLITE
1076 if (sqlite_db) {
1077 sqlite3_close(sqlite_db);
1078 sqlite_db = NULL;
1079 }
1080#endif /* CONFIG_SQLITE */
1081
057a92ec
JM
1082 os_program_deinit();
1083
3e6547b5 1084 return ret;
6fc6879b 1085}