xref: /freebsd/contrib/wpa/hostapd/hlr_auc_gw.c (revision 884a2a699669ec61e2366e3e358342dbc94be24a)
1 /*
2  * HLR/AuC testing gateway for hostapd EAP-SIM/AKA database/authenticator
3  * Copyright (c) 2005-2007, Jouni Malinen <j@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 
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@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