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