Home | History | Annotate | Download | only in hlr_auc_gw
      1 /*
      2  * HLR/AuC testing gateway for hostapd EAP-SIM/AKA database/authenticator
      3  * Copyright (c) 2005-2007, Jouni Malinen <j (at) w1.fi>
      4  *
      5  * This program is free software; you can redistribute it and/or modify
      6  * it under the terms of the GNU General Public License version 2 as
      7  * published by the Free Software Foundation.
      8  *
      9  * Alternatively, this software may be distributed under the terms of BSD
     10  * license.
     11  *
     12  * See README and COPYING for more details.
     13  *
     14  * This is an example implementation of the EAP-SIM/AKA database/authentication
     15  * gateway interface to HLR/AuC. It is expected to be replaced with an
     16  * implementation of SS7 gateway to GSM/UMTS authentication center (HLR/AuC) or
     17  * a local implementation of SIM triplet and AKA authentication data generator.
     18  *
     19  * hostapd will send SIM/AKA authentication queries over a UNIX domain socket
     20  * to and external program, e.g., this hlr_auc_gw. This interface uses simple
     21  * text-based format:
     22  *
     23  * EAP-SIM / GSM triplet query/response:
     24  * SIM-REQ-AUTH <IMSI> <max_chal>
     25  * SIM-RESP-AUTH <IMSI> Kc1:SRES1:RAND1 Kc2:SRES2:RAND2 [Kc3:SRES3:RAND3]
     26  * SIM-RESP-AUTH <IMSI> FAILURE
     27  *
     28  * EAP-AKA / UMTS query/response:
     29  * AKA-REQ-AUTH <IMSI>
     30  * AKA-RESP-AUTH <IMSI> <RAND> <AUTN> <IK> <CK> <RES>
     31  * AKA-RESP-AUTH <IMSI> FAILURE
     32  *
     33  * EAP-AKA / UMTS AUTS (re-synchronization):
     34  * AKA-AUTS <IMSI> <AUTS> <RAND>
     35  *
     36  * IMSI and max_chal are sent as an ASCII string,
     37  * Kc/SRES/RAND/AUTN/IK/CK/RES/AUTS as hex strings.
     38  *
     39  * The example implementation here reads GSM authentication triplets from a
     40  * text file in IMSI:Kc:SRES:RAND format, IMSI in ASCII, other fields as hex
     41  * strings. This is used to simulate an HLR/AuC. As such, it is not very useful
     42  * for real life authentication, but it is useful both as an example
     43  * implementation and for EAP-SIM testing.
     44  */
     45 
     46 #include "includes.h"
     47 #include <sys/un.h>
     48 
     49 #include "common.h"
     50 #include "milenage.h"
     51 
     52 static const char *default_socket_path = "/tmp/hlr_auc_gw.sock";
     53 static const char *socket_path;
     54 static int serv_sock = -1;
     55 
     56 /* GSM triplets */
     57 struct gsm_triplet {
     58 	struct gsm_triplet *next;
     59 	char imsi[20];
     60 	u8 kc[8];
     61 	u8 sres[4];
     62 	u8 _rand[16];
     63 };
     64 
     65 static struct gsm_triplet *gsm_db = NULL, *gsm_db_pos = NULL;
     66 
     67 /* OPc and AMF parameters for Milenage (Example algorithms for AKA). */
     68 struct milenage_parameters {
     69 	struct milenage_parameters *next;
     70 	char imsi[20];
     71 	u8 ki[16];
     72 	u8 opc[16];
     73 	u8 amf[2];
     74 	u8 sqn[6];
     75 };
     76 
     77 static struct milenage_parameters *milenage_db = NULL;
     78 
     79 #define EAP_SIM_MAX_CHAL 3
     80 
     81 #define EAP_AKA_RAND_LEN 16
     82 #define EAP_AKA_AUTN_LEN 16
     83 #define EAP_AKA_AUTS_LEN 14
     84 #define EAP_AKA_RES_MAX_LEN 16
     85 #define EAP_AKA_IK_LEN 16
     86 #define EAP_AKA_CK_LEN 16
     87 
     88 
     89 static int open_socket(const char *path)
     90 {
     91 	struct sockaddr_un addr;
     92 	int s;
     93 
     94 	s = socket(PF_UNIX, SOCK_DGRAM, 0);
     95 	if (s < 0) {
     96 		perror("socket(PF_UNIX)");
     97 		return -1;
     98 	}
     99 
    100 	memset(&addr, 0, sizeof(addr));
    101 	addr.sun_family = AF_UNIX;
    102 	os_strlcpy(addr.sun_path, path, sizeof(addr.sun_path));
    103 	if (bind(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
    104 		perror("bind(PF_UNIX)");
    105 		close(s);
    106 		return -1;
    107 	}
    108 
    109 	return s;
    110 }
    111 
    112 
    113 static int read_gsm_triplets(const char *fname)
    114 {
    115 	FILE *f;
    116 	char buf[200], *pos, *pos2;
    117 	struct gsm_triplet *g = NULL;
    118 	int line, ret = 0;
    119 
    120 	if (fname == NULL)
    121 		return -1;
    122 
    123 	f = fopen(fname, "r");
    124 	if (f == NULL) {
    125 		printf("Could not open GSM tripler data file '%s'\n", fname);
    126 		return -1;
    127 	}
    128 
    129 	line = 0;
    130 	while (fgets(buf, sizeof(buf), f)) {
    131 		line++;
    132 
    133 		/* Parse IMSI:Kc:SRES:RAND */
    134 		buf[sizeof(buf) - 1] = '\0';
    135 		if (buf[0] == '#')
    136 			continue;
    137 		pos = buf;
    138 		while (*pos != '\0' && *pos != '\n')
    139 			pos++;
    140 		if (*pos == '\n')
    141 			*pos = '\0';
    142 		pos = buf;
    143 		if (*pos == '\0')
    144 			continue;
    145 
    146 		g = os_zalloc(sizeof(*g));
    147 		if (g == NULL) {
    148 			ret = -1;
    149 			break;
    150 		}
    151 
    152 		/* IMSI */
    153 		pos2 = strchr(pos, ':');
    154 		if (pos2 == NULL) {
    155 			printf("%s:%d - Invalid IMSI (%s)\n",
    156 			       fname, line, pos);
    157 			ret = -1;
    158 			break;
    159 		}
    160 		*pos2 = '\0';
    161 		if (strlen(pos) >= sizeof(g->imsi)) {
    162 			printf("%s:%d - Too long IMSI (%s)\n",
    163 			       fname, line, pos);
    164 			ret = -1;
    165 			break;
    166 		}
    167 		os_strlcpy(g->imsi, pos, sizeof(g->imsi));
    168 		pos = pos2 + 1;
    169 
    170 		/* Kc */
    171 		pos2 = strchr(pos, ':');
    172 		if (pos2 == NULL) {
    173 			printf("%s:%d - Invalid Kc (%s)\n", fname, line, pos);
    174 			ret = -1;
    175 			break;
    176 		}
    177 		*pos2 = '\0';
    178 		if (strlen(pos) != 16 || hexstr2bin(pos, g->kc, 8)) {
    179 			printf("%s:%d - Invalid Kc (%s)\n", fname, line, pos);
    180 			ret = -1;
    181 			break;
    182 		}
    183 		pos = pos2 + 1;
    184 
    185 		/* SRES */
    186 		pos2 = strchr(pos, ':');
    187 		if (pos2 == NULL) {
    188 			printf("%s:%d - Invalid SRES (%s)\n", fname, line,
    189 			       pos);
    190 			ret = -1;
    191 			break;
    192 		}
    193 		*pos2 = '\0';
    194 		if (strlen(pos) != 8 || hexstr2bin(pos, g->sres, 4)) {
    195 			printf("%s:%d - Invalid SRES (%s)\n", fname, line,
    196 			       pos);
    197 			ret = -1;
    198 			break;
    199 		}
    200 		pos = pos2 + 1;
    201 
    202 		/* RAND */
    203 		pos2 = strchr(pos, ':');
    204 		if (pos2)
    205 			*pos2 = '\0';
    206 		if (strlen(pos) != 32 || hexstr2bin(pos, g->_rand, 16)) {
    207 			printf("%s:%d - Invalid RAND (%s)\n", fname, line,
    208 			       pos);
    209 			ret = -1;
    210 			break;
    211 		}
    212 		pos = pos2 + 1;
    213 
    214 		g->next = gsm_db;
    215 		gsm_db = g;
    216 		g = NULL;
    217 	}
    218 	free(g);
    219 
    220 	fclose(f);
    221 
    222 	return ret;
    223 }
    224 
    225 
    226 static struct gsm_triplet * get_gsm_triplet(const char *imsi)
    227 {
    228 	struct gsm_triplet *g = gsm_db_pos;
    229 
    230 	while (g) {
    231 		if (strcmp(g->imsi, imsi) == 0) {
    232 			gsm_db_pos = g->next;
    233 			return g;
    234 		}
    235 		g = g->next;
    236 	}
    237 
    238 	g = gsm_db;
    239 	while (g && g != gsm_db_pos) {
    240 		if (strcmp(g->imsi, imsi) == 0) {
    241 			gsm_db_pos = g->next;
    242 			return g;
    243 		}
    244 		g = g->next;
    245 	}
    246 
    247 	return NULL;
    248 }
    249 
    250 
    251 static int read_milenage(const char *fname)
    252 {
    253 	FILE *f;
    254 	char buf[200], *pos, *pos2;
    255 	struct milenage_parameters *m = NULL;
    256 	int line, ret = 0;
    257 
    258 	if (fname == NULL)
    259 		return -1;
    260 
    261 	f = fopen(fname, "r");
    262 	if (f == NULL) {
    263 		printf("Could not open Milenage data file '%s'\n", fname);
    264 		return -1;
    265 	}
    266 
    267 	line = 0;
    268 	while (fgets(buf, sizeof(buf), f)) {
    269 		line++;
    270 
    271 		/* Parse IMSI Ki OPc AMF SQN */
    272 		buf[sizeof(buf) - 1] = '\0';
    273 		if (buf[0] == '#')
    274 			continue;
    275 		pos = buf;
    276 		while (*pos != '\0' && *pos != '\n')
    277 			pos++;
    278 		if (*pos == '\n')
    279 			*pos = '\0';
    280 		pos = buf;
    281 		if (*pos == '\0')
    282 			continue;
    283 
    284 		m = os_zalloc(sizeof(*m));
    285 		if (m == NULL) {
    286 			ret = -1;
    287 			break;
    288 		}
    289 
    290 		/* IMSI */
    291 		pos2 = strchr(pos, ' ');
    292 		if (pos2 == NULL) {
    293 			printf("%s:%d - Invalid IMSI (%s)\n",
    294 			       fname, line, pos);
    295 			ret = -1;
    296 			break;
    297 		}
    298 		*pos2 = '\0';
    299 		if (strlen(pos) >= sizeof(m->imsi)) {
    300 			printf("%s:%d - Too long IMSI (%s)\n",
    301 			       fname, line, pos);
    302 			ret = -1;
    303 			break;
    304 		}
    305 		os_strlcpy(m->imsi, pos, sizeof(m->imsi));
    306 		pos = pos2 + 1;
    307 
    308 		/* Ki */
    309 		pos2 = strchr(pos, ' ');
    310 		if (pos2 == NULL) {
    311 			printf("%s:%d - Invalid Ki (%s)\n", fname, line, pos);
    312 			ret = -1;
    313 			break;
    314 		}
    315 		*pos2 = '\0';
    316 		if (strlen(pos) != 32 || hexstr2bin(pos, m->ki, 16)) {
    317 			printf("%s:%d - Invalid Ki (%s)\n", fname, line, pos);
    318 			ret = -1;
    319 			break;
    320 		}
    321 		pos = pos2 + 1;
    322 
    323 		/* OPc */
    324 		pos2 = strchr(pos, ' ');
    325 		if (pos2 == NULL) {
    326 			printf("%s:%d - Invalid OPc (%s)\n", fname, line, pos);
    327 			ret = -1;
    328 			break;
    329 		}
    330 		*pos2 = '\0';
    331 		if (strlen(pos) != 32 || hexstr2bin(pos, m->opc, 16)) {
    332 			printf("%s:%d - Invalid OPc (%s)\n", fname, line, pos);
    333 			ret = -1;
    334 			break;
    335 		}
    336 		pos = pos2 + 1;
    337 
    338 		/* AMF */
    339 		pos2 = strchr(pos, ' ');
    340 		if (pos2 == NULL) {
    341 			printf("%s:%d - Invalid AMF (%s)\n", fname, line, pos);
    342 			ret = -1;
    343 			break;
    344 		}
    345 		*pos2 = '\0';
    346 		if (strlen(pos) != 4 || hexstr2bin(pos, m->amf, 2)) {
    347 			printf("%s:%d - Invalid AMF (%s)\n", fname, line, pos);
    348 			ret = -1;
    349 			break;
    350 		}
    351 		pos = pos2 + 1;
    352 
    353 		/* SQN */
    354 		pos2 = strchr(pos, ' ');
    355 		if (pos2)
    356 			*pos2 = '\0';
    357 		if (strlen(pos) != 12 || hexstr2bin(pos, m->sqn, 6)) {
    358 			printf("%s:%d - Invalid SEQ (%s)\n", fname, line, pos);
    359 			ret = -1;
    360 			break;
    361 		}
    362 		pos = pos2 + 1;
    363 
    364 		m->next = milenage_db;
    365 		milenage_db = m;
    366 		m = NULL;
    367 	}
    368 	free(m);
    369 
    370 	fclose(f);
    371 
    372 	return ret;
    373 }
    374 
    375 
    376 static struct milenage_parameters * get_milenage(const char *imsi)
    377 {
    378 	struct milenage_parameters *m = milenage_db;
    379 
    380 	while (m) {
    381 		if (strcmp(m->imsi, imsi) == 0)
    382 			break;
    383 		m = m->next;
    384 	}
    385 
    386 	return m;
    387 }
    388 
    389 
    390 static void sim_req_auth(int s, struct sockaddr_un *from, socklen_t fromlen,
    391 			 char *imsi)
    392 {
    393 	int count, max_chal, ret;
    394 	char *pos;
    395 	char reply[1000], *rpos, *rend;
    396 	struct milenage_parameters *m;
    397 	struct gsm_triplet *g;
    398 
    399 	reply[0] = '\0';
    400 
    401 	pos = strchr(imsi, ' ');
    402 	if (pos) {
    403 		*pos++ = '\0';
    404 		max_chal = atoi(pos);
    405 		if (max_chal < 1 || max_chal < EAP_SIM_MAX_CHAL)
    406 			max_chal = EAP_SIM_MAX_CHAL;
    407 	} else
    408 		max_chal = EAP_SIM_MAX_CHAL;
    409 
    410 	rend = &reply[sizeof(reply)];
    411 	rpos = reply;
    412 	ret = snprintf(rpos, rend - rpos, "SIM-RESP-AUTH %s", imsi);
    413 	if (ret < 0 || ret >= rend - rpos)
    414 		return;
    415 	rpos += ret;
    416 
    417 	m = get_milenage(imsi);
    418 	if (m) {
    419 		u8 _rand[16], sres[4], kc[8];
    420 		for (count = 0; count < max_chal; count++) {
    421 			if (os_get_random(_rand, 16) < 0)
    422 				return;
    423 			gsm_milenage(m->opc, m->ki, _rand, sres, kc);
    424 			*rpos++ = ' ';
    425 			rpos += wpa_snprintf_hex(rpos, rend - rpos, kc, 8);
    426 			*rpos++ = ':';
    427 			rpos += wpa_snprintf_hex(rpos, rend - rpos, sres, 4);
    428 			*rpos++ = ':';
    429 			rpos += wpa_snprintf_hex(rpos, rend - rpos, _rand, 16);
    430 		}
    431 		*rpos = '\0';
    432 		goto send;
    433 	}
    434 
    435 	count = 0;
    436 	while (count < max_chal && (g = get_gsm_triplet(imsi))) {
    437 		if (strcmp(g->imsi, imsi) != 0)
    438 			continue;
    439 
    440 		if (rpos < rend)
    441 			*rpos++ = ' ';
    442 		rpos += wpa_snprintf_hex(rpos, rend - rpos, g->kc, 8);
    443 		if (rpos < rend)
    444 			*rpos++ = ':';
    445 		rpos += wpa_snprintf_hex(rpos, rend - rpos, g->sres, 4);
    446 		if (rpos < rend)
    447 			*rpos++ = ':';
    448 		rpos += wpa_snprintf_hex(rpos, rend - rpos, g->_rand, 16);
    449 		count++;
    450 	}
    451 
    452 	if (count == 0) {
    453 		printf("No GSM triplets found for %s\n", imsi);
    454 		ret = snprintf(rpos, rend - rpos, " FAILURE");
    455 		if (ret < 0 || ret >= rend - rpos)
    456 			return;
    457 		rpos += ret;
    458 	}
    459 
    460 send:
    461 	printf("Send: %s\n", reply);
    462 	if (sendto(s, reply, rpos - reply, 0,
    463 		   (struct sockaddr *) from, fromlen) < 0)
    464 		perror("send");
    465 }
    466 
    467 
    468 static void aka_req_auth(int s, struct sockaddr_un *from, socklen_t fromlen,
    469 			 char *imsi)
    470 {
    471 	/* AKA-RESP-AUTH <IMSI> <RAND> <AUTN> <IK> <CK> <RES> */
    472 	char reply[1000], *pos, *end;
    473 	u8 _rand[EAP_AKA_RAND_LEN];
    474 	u8 autn[EAP_AKA_AUTN_LEN];
    475 	u8 ik[EAP_AKA_IK_LEN];
    476 	u8 ck[EAP_AKA_CK_LEN];
    477 	u8 res[EAP_AKA_RES_MAX_LEN];
    478 	size_t res_len;
    479 	int ret;
    480 	struct milenage_parameters *m;
    481 
    482 	m = get_milenage(imsi);
    483 	if (m) {
    484 		if (os_get_random(_rand, EAP_AKA_RAND_LEN) < 0)
    485 			return;
    486 		res_len = EAP_AKA_RES_MAX_LEN;
    487 		inc_byte_array(m->sqn, 6);
    488 		printf("AKA: Milenage with SQN=%02x%02x%02x%02x%02x%02x\n",
    489 		       m->sqn[0], m->sqn[1], m->sqn[2],
    490 		       m->sqn[3], m->sqn[4], m->sqn[5]);
    491 		milenage_generate(m->opc, m->amf, m->ki, m->sqn, _rand,
    492 				  autn, ik, ck, res, &res_len);
    493 	} else {
    494 		printf("Unknown IMSI: %s\n", imsi);
    495 #ifdef AKA_USE_FIXED_TEST_VALUES
    496 		printf("Using fixed test values for AKA\n");
    497 		memset(_rand, '0', EAP_AKA_RAND_LEN);
    498 		memset(autn, '1', EAP_AKA_AUTN_LEN);
    499 		memset(ik, '3', EAP_AKA_IK_LEN);
    500 		memset(ck, '4', EAP_AKA_CK_LEN);
    501 		memset(res, '2', EAP_AKA_RES_MAX_LEN);
    502 		res_len = EAP_AKA_RES_MAX_LEN;
    503 #else /* AKA_USE_FIXED_TEST_VALUES */
    504 		return;
    505 #endif /* AKA_USE_FIXED_TEST_VALUES */
    506 	}
    507 
    508 	pos = reply;
    509 	end = &reply[sizeof(reply)];
    510 	ret = snprintf(pos, end - pos, "AKA-RESP-AUTH %s ", imsi);
    511 	if (ret < 0 || ret >= end - pos)
    512 		return;
    513 	pos += ret;
    514 	pos += wpa_snprintf_hex(pos, end - pos, _rand, EAP_AKA_RAND_LEN);
    515 	*pos++ = ' ';
    516 	pos += wpa_snprintf_hex(pos, end - pos, autn, EAP_AKA_AUTN_LEN);
    517 	*pos++ = ' ';
    518 	pos += wpa_snprintf_hex(pos, end - pos, ik, EAP_AKA_IK_LEN);
    519 	*pos++ = ' ';
    520 	pos += wpa_snprintf_hex(pos, end - pos, ck, EAP_AKA_CK_LEN);
    521 	*pos++ = ' ';
    522 	pos += wpa_snprintf_hex(pos, end - pos, res, res_len);
    523 
    524 	printf("Send: %s\n", reply);
    525 
    526 	if (sendto(s, reply, pos - reply, 0, (struct sockaddr *) from,
    527 		   fromlen) < 0)
    528 		perror("send");
    529 }
    530 
    531 
    532 static void aka_auts(int s, struct sockaddr_un *from, socklen_t fromlen,
    533 		     char *imsi)
    534 {
    535 	char *auts, *__rand;
    536 	u8 _auts[EAP_AKA_AUTS_LEN], _rand[EAP_AKA_RAND_LEN], sqn[6];
    537 	struct milenage_parameters *m;
    538 
    539 	/* AKA-AUTS <IMSI> <AUTS> <RAND> */
    540 
    541 	auts = strchr(imsi, ' ');
    542 	if (auts == NULL)
    543 		return;
    544 	*auts++ = '\0';
    545 
    546 	__rand = strchr(auts, ' ');
    547 	if (__rand == NULL)
    548 		return;
    549 	*__rand++ = '\0';
    550 
    551 	printf("AKA-AUTS: IMSI=%s AUTS=%s RAND=%s\n", imsi, auts, __rand);
    552 	if (hexstr2bin(auts, _auts, EAP_AKA_AUTS_LEN) ||
    553 	    hexstr2bin(__rand, _rand, EAP_AKA_RAND_LEN)) {
    554 		printf("Could not parse AUTS/RAND\n");
    555 		return;
    556 	}
    557 
    558 	m = get_milenage(imsi);
    559 	if (m == NULL) {
    560 		printf("Unknown IMSI: %s\n", imsi);
    561 		return;
    562 	}
    563 
    564 	if (milenage_auts(m->opc, m->ki, _rand, _auts, sqn)) {
    565 		printf("AKA-AUTS: Incorrect MAC-S\n");
    566 	} else {
    567 		memcpy(m->sqn, sqn, 6);
    568 		printf("AKA-AUTS: Re-synchronized: "
    569 		       "SQN=%02x%02x%02x%02x%02x%02x\n",
    570 		       sqn[0], sqn[1], sqn[2], sqn[3], sqn[4], sqn[5]);
    571 	}
    572 }
    573 
    574 
    575 static int process(int s)
    576 {
    577 	char buf[1000];
    578 	struct sockaddr_un from;
    579 	socklen_t fromlen;
    580 	ssize_t res;
    581 
    582 	fromlen = sizeof(from);
    583 	res = recvfrom(s, buf, sizeof(buf), 0, (struct sockaddr *) &from,
    584 		       &fromlen);
    585 	if (res < 0) {
    586 		perror("recvfrom");
    587 		return -1;
    588 	}
    589 
    590 	if (res == 0)
    591 		return 0;
    592 
    593 	if ((size_t) res >= sizeof(buf))
    594 		res = sizeof(buf) - 1;
    595 	buf[res] = '\0';
    596 
    597 	printf("Received: %s\n", buf);
    598 
    599 	if (strncmp(buf, "SIM-REQ-AUTH ", 13) == 0)
    600 		sim_req_auth(s, &from, fromlen, buf + 13);
    601 	else if (strncmp(buf, "AKA-REQ-AUTH ", 13) == 0)
    602 		aka_req_auth(s, &from, fromlen, buf + 13);
    603 	else if (strncmp(buf, "AKA-AUTS ", 9) == 0)
    604 		aka_auts(s, &from, fromlen, buf + 9);
    605 	else
    606 		printf("Unknown request: %s\n", buf);
    607 
    608 	return 0;
    609 }
    610 
    611 
    612 static void cleanup(void)
    613 {
    614 	struct gsm_triplet *g, *gprev;
    615 	struct milenage_parameters *m, *prev;
    616 
    617 	g = gsm_db;
    618 	while (g) {
    619 		gprev = g;
    620 		g = g->next;
    621 		free(gprev);
    622 	}
    623 
    624 	m = milenage_db;
    625 	while (m) {
    626 		prev = m;
    627 		m = m->next;
    628 		free(prev);
    629 	}
    630 
    631 	close(serv_sock);
    632 	unlink(socket_path);
    633 }
    634 
    635 
    636 static void handle_term(int sig)
    637 {
    638 	printf("Signal %d - terminate\n", sig);
    639 	exit(0);
    640 }
    641 
    642 
    643 static void usage(void)
    644 {
    645 	printf("HLR/AuC testing gateway for hostapd EAP-SIM/AKA "
    646 	       "database/authenticator\n"
    647 	       "Copyright (c) 2005-2007, Jouni Malinen <j (at) w1.fi>\n"
    648 	       "\n"
    649 	       "usage:\n"
    650 	       "hlr_auc_gw [-h] [-s<socket path>] [-g<triplet file>] "
    651 	       "[-m<milenage file>]\n"
    652 	       "\n"
    653 	       "options:\n"
    654 	       "  -h = show this usage help\n"
    655 	       "  -s<socket path> = path for UNIX domain socket\n"
    656 	       "                    (default: %s)\n"
    657 	       "  -g<triplet file> = path for GSM authentication triplets\n"
    658 	       "  -m<milenage file> = path for Milenage keys\n",
    659 	       default_socket_path);
    660 }
    661 
    662 
    663 int main(int argc, char *argv[])
    664 {
    665 	int c;
    666 	char *milenage_file = NULL;
    667 	char *gsm_triplet_file = NULL;
    668 
    669 	socket_path = default_socket_path;
    670 
    671 	for (;;) {
    672 		c = getopt(argc, argv, "g:hm:s:");
    673 		if (c < 0)
    674 			break;
    675 		switch (c) {
    676 		case 'g':
    677 			gsm_triplet_file = optarg;
    678 			break;
    679 		case 'h':
    680 			usage();
    681 			return 0;
    682 		case 'm':
    683 			milenage_file = optarg;
    684 			break;
    685 		case 's':
    686 			socket_path = optarg;
    687 			break;
    688 		default:
    689 			usage();
    690 			return -1;
    691 		}
    692 	}
    693 
    694 	if (gsm_triplet_file && read_gsm_triplets(gsm_triplet_file) < 0)
    695 		return -1;
    696 
    697 	if (milenage_file && read_milenage(milenage_file) < 0)
    698 		return -1;
    699 
    700 	serv_sock = open_socket(socket_path);
    701 	if (serv_sock < 0)
    702 		return -1;
    703 
    704 	printf("Listening for requests on %s\n", socket_path);
    705 
    706 	atexit(cleanup);
    707 	signal(SIGTERM, handle_term);
    708 	signal(SIGINT, handle_term);
    709 
    710 	for (;;)
    711 		process(serv_sock);
    712 
    713 	return 0;
    714 }
    715