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