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