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