identd.c revision 1.21
1/*	$OpenBSD: identd.c,v 1.21 2013/10/24 02:55:50 deraadt Exp $ */
2
3/*
4 * Copyright (c) 2013 David Gwynne <dlg@openbsd.org>
5 *
6 * Permission to use, copy, modify, and distribute this software for any
7 * purpose with or without fee is hereby granted, provided that the above
8 * copyright notice and this permission notice appear in all copies.
9 *
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 */
18
19#include <sys/types.h>
20#include <sys/ioctl.h>
21#include <sys/socket.h>
22#include <sys/socketvar.h>
23#include <sys/stat.h>
24#include <sys/sysctl.h>
25#include <sys/uio.h>
26
27#include <netinet/in.h>
28#include <netinet/tcp.h>
29#include <netinet/tcp_timer.h>
30#include <netinet/tcp_var.h>
31
32#include <netdb.h>
33
34#include <err.h>
35#include <ctype.h>
36#include <errno.h>
37#include <event.h>
38#include <fcntl.h>
39#include <pwd.h>
40#include <stdio.h>
41#include <stdlib.h>
42#include <string.h>
43#include <syslog.h>
44#include <unistd.h>
45
46#define IDENTD_USER "_identd"
47
48#define DOTNOIDENT ".noident"
49
50#define TIMEOUT_MIN 4
51#define TIMEOUT_MAX 240
52#define TIMEOUT_DEFAULT 120
53#define INPUT_MAX 256
54
55enum ident_client_state {
56	S_BEGINNING = 0,
57	S_SERVER_PORT,
58	S_PRE_COMMA,
59	S_POST_COMMA,
60	S_CLIENT_PORT,
61	S_PRE_EOL,
62	S_EOL,
63
64	S_DEAD,
65	S_QUEUED
66};
67
68#define E_NONE		0
69#define E_NOUSER	1
70#define E_UNKNOWN	2
71#define E_HIDDEN	3
72
73struct ident_client {
74	struct {
75		/* from the socket */
76		struct sockaddr_storage ss;
77		socklen_t len;
78
79		/* from the request */
80		u_int port;
81	} client, server;
82	SIMPLEQ_ENTRY(ident_client) entry;
83	enum ident_client_state state;
84	struct event ev;
85	struct event tmo;
86	size_t rxbytes;
87
88	char *buf;
89	size_t buflen;
90	size_t bufoff;
91	uid_t uid;
92};
93
94struct ident_resolver {
95	SIMPLEQ_ENTRY(ident_resolver) entry;
96	char *buf;
97	size_t buflen;
98	u_int error;
99};
100
101struct identd_listener {
102	struct event ev, pause;
103};
104
105void	parent_rd(int, short, void *);
106void	parent_wr(int, short, void *);
107int	parent_username(struct ident_resolver *, struct passwd *);
108int	parent_uid(struct ident_resolver *, struct passwd *);
109int	parent_token(struct ident_resolver *, struct passwd *);
110void	parent_noident(struct ident_resolver *, struct passwd *);
111
112void	child_rd(int, short, void *);
113void	child_wr(int, short, void *);
114
115void	identd_listen(const char *, const char *, int);
116void	identd_paused(int, short, void *);
117void	identd_accept(int, short, void *);
118int	identd_error(struct ident_client *, const char *);
119void	identd_close(struct ident_client *);
120void	identd_timeout(int, short, void *);
121void	identd_request(int, short, void *);
122enum ident_client_state
123	identd_parse(struct ident_client *, int);
124void	identd_resolving(int, short, void *);
125void	identd_response(int, short, void *);
126int	fetchuid(struct ident_client *);
127
128const char *gethost(struct sockaddr_storage *);
129const char *getport(struct sockaddr_storage *);
130const char *gentoken(void);
131
132struct loggers {
133	void (*err)(int, const char *, ...);
134	void (*errx)(int, const char *, ...);
135	void (*warn)(const char *, ...);
136	void (*warnx)(const char *, ...);
137	void (*notice)(const char *, ...);
138	void (*debug)(const char *, ...);
139};
140
141const struct loggers conslogger = {
142	err,
143	errx,
144	warn,
145	warnx,
146	warnx, /* notice */
147	warnx /* debug */
148};
149
150void	syslog_err(int, const char *, ...);
151void	syslog_errx(int, const char *, ...);
152void	syslog_warn(const char *, ...);
153void	syslog_warnx(const char *, ...);
154void	syslog_notice(const char *, ...);
155void	syslog_debug(const char *, ...);
156void	syslog_vstrerror(int, int, const char *, va_list);
157
158const struct loggers syslogger = {
159	syslog_err,
160	syslog_errx,
161	syslog_warn,
162	syslog_warnx,
163	syslog_notice,
164	syslog_debug
165};
166
167const struct loggers *logger = &conslogger;
168
169#define lerr(_e, _f...) logger->err((_e), _f)
170#define lerrx(_e, _f...) logger->errx((_e), _f)
171#define lwarn(_f...) logger->warn(_f)
172#define lwarnx(_f...) logger->warnx(_f)
173#define lnotice(_f...) logger->notice(_f)
174#define ldebug(_f...) logger->debug(_f)
175
176#define sa(_ss) ((struct sockaddr *)(_ss))
177
178__dead void
179usage(void)
180{
181	extern char *__progname;
182	fprintf(stderr, "usage: %s [-46deHhNn] [-l address] [-t timeout]\n",
183	    __progname);
184	exit(1);
185}
186
187struct timeval timeout = { TIMEOUT_DEFAULT, 0 };
188int debug = 0;
189int noident = 0;
190int on = 1;
191int unknown_err = 0;
192int hideall = 0;
193
194int (*parent_uprintf)(struct ident_resolver *, struct passwd *) =
195    parent_username;
196
197struct event proc_rd, proc_wr;
198union {
199	struct {
200		SIMPLEQ_HEAD(, ident_resolver) replies;
201	} parent;
202	struct {
203		SIMPLEQ_HEAD(, ident_client) pushing, popping;
204	} child;
205} sc;
206
207int
208main(int argc, char *argv[])
209{
210	extern char *__progname;
211	const char *errstr = NULL;
212
213	int		 c;
214	struct passwd	*pw;
215
216	char *addr = NULL;
217	int family = AF_UNSPEC;
218
219	int pair[2];
220	pid_t parent;
221	int sibling;
222
223	while ((c = getopt(argc, argv, "46deHhl:Nnp:t:")) != -1) {
224		switch (c) {
225		case '4':
226			family = AF_INET;
227			break;
228		case '6':
229			family = AF_INET6;
230			break;
231		case 'd':
232			debug = 1;
233			break;
234		case 'e':
235			unknown_err = 1;
236			break;
237		case 'H':
238			hideall = 1;
239			/* FALLTHROUGH */
240		case 'h':
241			parent_uprintf = parent_token;
242			break;
243		case 'l':
244			addr = optarg;
245			break;
246		case 'N':
247			noident = 1;
248			break;
249		case 'n':
250			parent_uprintf = parent_uid;
251			break;
252		case 't':
253			timeout.tv_sec = strtonum(optarg,
254			    TIMEOUT_MIN, TIMEOUT_MAX, &errstr);
255			if (errstr != NULL)
256				errx(1, "timeout %s is %s", optarg, errstr);
257			break;
258		default:
259			usage();
260			/* NOTREACHED */
261		}
262	}
263
264	argc -= optind;
265	argv += optind;
266
267	if (argc != 0)
268		usage();
269
270	if (geteuid() != 0)
271		errx(1, "need root privileges");
272
273	if (socketpair(AF_UNIX, SOCK_SEQPACKET, PF_UNSPEC, pair) == -1)
274		err(1, "socketpair");
275
276	pw = getpwnam(IDENTD_USER);
277	if (pw == NULL)
278		err(1, "no %s user", IDENTD_USER);
279
280	if (!debug && daemon(1, 0) == -1)
281		err(1, "daemon");
282
283	parent = fork();
284	switch (parent) {
285	case -1:
286		lerr(1, "fork");
287
288	case 0:
289		/* child */
290		setproctitle("listener");
291		close(pair[1]);
292		sibling = pair[0];
293		break;
294
295	default:
296		/* parent */
297		setproctitle("resolver");
298		close(pair[0]);
299		sibling = pair[1];
300		break;
301	}
302
303	if (!debug) {
304		openlog(__progname, LOG_PID|LOG_NDELAY, LOG_DAEMON);
305		tzset();
306		logger = &syslogger;
307	}
308
309	event_init();
310
311	if (ioctl(sibling, FIONBIO, &on) == -1)
312		lerr(1, "sibling ioctl(FIONBIO)");
313
314	if (parent) {
315		SIMPLEQ_INIT(&sc.parent.replies);
316
317		event_set(&proc_rd, sibling, EV_READ | EV_PERSIST,
318		    parent_rd, NULL);
319		event_set(&proc_wr, sibling, EV_WRITE,
320		    parent_wr, NULL);
321	} else {
322		SIMPLEQ_INIT(&sc.child.pushing);
323		SIMPLEQ_INIT(&sc.child.popping);
324
325		identd_listen(addr, "auth", family);
326
327		if (chroot(pw->pw_dir) == -1)
328			lerr(1, "chroot(%s)", pw->pw_dir);
329
330		if (chdir("/") == -1)
331			lerr(1, "chdir(%s)", pw->pw_dir);
332
333		event_set(&proc_rd, sibling, EV_READ | EV_PERSIST,
334		    child_rd, NULL);
335		event_set(&proc_wr, sibling, EV_WRITE,
336		    child_wr, NULL);
337	}
338
339	if (setgroups(1, &pw->pw_gid) ||
340	    setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) ||
341	    setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid))
342		lerr(1, "unable to revoke privs");
343
344	event_add(&proc_rd, NULL);
345	event_dispatch();
346	return (0);
347}
348
349void
350parent_rd(int fd, short events, void *arg)
351{
352	struct ident_resolver *r;
353	struct passwd *pw;
354	ssize_t n;
355	uid_t uid;
356
357	n = read(fd, &uid, sizeof(uid));
358	switch (n) {
359	case -1:
360		switch (errno) {
361		case EAGAIN:
362		case EINTR:
363			return;
364		default:
365			lerr(1, "parent read");
366		}
367		break;
368	case 0:
369		lerrx(1, "child has gone");
370	case sizeof(uid):
371		break;
372	default:
373		lerrx(1, "unexpected %zd data from child", n);
374	}
375
376	r = calloc(1, sizeof(*r));
377	if (r == NULL)
378		lerr(1, "resolver alloc");
379
380	pw = getpwuid(uid);
381	if (pw == NULL && !hideall) {
382		r->error = E_NOUSER;
383		goto done;
384	}
385
386	if (noident && !hideall) {
387		parent_noident(r, pw);
388		if (r->error != E_NONE)
389			goto done;
390	}
391
392	n = (*parent_uprintf)(r, pw);
393	if (n == -1) {
394		r->error = E_UNKNOWN;
395		goto done;
396	}
397
398	r->buflen = n;
399
400done:
401	SIMPLEQ_INSERT_TAIL(&sc.parent.replies, r, entry);
402	event_add(&proc_wr, NULL);
403}
404
405int
406parent_username(struct ident_resolver *r, struct passwd *pw)
407{
408	return (asprintf(&r->buf, "%s", pw->pw_name));
409}
410
411int
412parent_uid(struct ident_resolver *r, struct passwd *pw)
413{
414	return (asprintf(&r->buf, "%u", (u_int)pw->pw_uid));
415}
416
417int
418parent_token(struct ident_resolver *r, struct passwd *pw)
419{
420	const char *token;
421	int rv;
422
423	token = gentoken();
424	rv = asprintf(&r->buf, "%s", token);
425	if (rv != -1) {
426		if (pw)
427			lnotice("token %s == uid %u (%s)", token,
428			    (u_int)pw->pw_uid, pw->pw_name);
429		else
430			lnotice("token %s == NO USER", token);
431	}
432
433	return (rv);
434}
435
436void
437parent_noident(struct ident_resolver *r, struct passwd *pw)
438{
439	char path[MAXPATHLEN];
440	struct stat st;
441	int rv;
442
443	rv = snprintf(path, sizeof(path), "%s/%s", pw->pw_dir, DOTNOIDENT);
444	if (rv == -1 || rv >= sizeof(path)) {
445		r->error = E_UNKNOWN;
446		return;
447	}
448
449	if (stat(path, &st) == -1)
450		return;
451
452	r->error = E_HIDDEN;
453}
454
455void
456parent_wr(int fd, short events, void *arg)
457{
458	struct ident_resolver *r = SIMPLEQ_FIRST(&sc.parent.replies);
459	struct iovec iov[2];
460	int iovcnt = 0;
461	ssize_t n;
462
463	iov[iovcnt].iov_base = &r->error;
464	iov[iovcnt].iov_len = sizeof(r->error);
465	iovcnt++;
466
467	if (r->buflen > 0) {
468		iov[iovcnt].iov_base = r->buf;
469		iov[iovcnt].iov_len = r->buflen;
470		iovcnt++;
471	}
472
473	n = writev(fd, iov, iovcnt);
474	if (n == -1) {
475		if (errno == EAGAIN) {
476			event_add(&proc_wr, NULL);
477			return;
478		}
479		lerr(1, "parent write");
480	}
481
482	if (n != sizeof(r->error) + r->buflen)
483		lerrx(1, "unexpected parent write length %zd", n);
484
485	SIMPLEQ_REMOVE_HEAD(&sc.parent.replies, entry);
486
487	if (r->buflen > 0)
488		free(r->buf);
489
490	free(r);
491}
492
493void
494child_rd(int fd, short events, void *arg)
495{
496	struct ident_client *c;
497	struct {
498		u_int error;
499		char buf[512];
500	} reply;
501	ssize_t n;
502
503	n = read(fd, &reply, sizeof(reply));
504	switch (n) {
505	case -1:
506		switch (errno) {
507		case EAGAIN:
508		case EINTR:
509			return;
510		default:
511			lerr(1, "child read");
512		}
513		break;
514	case 0:
515		lerrx(1, "parent has gone");
516	default:
517		break;
518	}
519
520	c = SIMPLEQ_FIRST(&sc.child.popping);
521	if (c == NULL)
522		lerrx(1, "unsolicited data from parent");
523
524	SIMPLEQ_REMOVE_HEAD(&sc.child.popping, entry);
525
526	if (n < sizeof(reply.error))
527		lerrx(1, "short data from parent");
528
529	/* check if something went wrong while the parent was working */
530	if (c->state == S_DEAD) {
531		free(c);
532		return;
533	}
534	c->state = S_DEAD;
535
536	switch (reply.error) {
537	case E_NONE:
538		n = asprintf(&c->buf, "%u , %u : USERID : UNIX : %s\r\n",
539		    c->server.port, c->client.port, reply.buf);
540		break;
541	case E_NOUSER:
542		n = asprintf(&c->buf, "%u , %u : ERROR : %s\r\n",
543		    c->server.port, c->client.port,
544		    unknown_err ? "UNKNOWN-ERROR" : "NO-USER");
545		break;
546	case E_UNKNOWN:
547		n = asprintf(&c->buf, "%u , %u : ERROR : UNKNOWN-ERROR\r\n",
548		    c->server.port, c->client.port);
549		break;
550	case E_HIDDEN:
551		n = asprintf(&c->buf, "%u , %u : ERROR : HIDDEN-USER\r\n",
552		    c->server.port, c->client.port);
553		break;
554	default:
555		lerrx(1, "unexpected error from parent %u", reply.error);
556	}
557	if (n == -1)
558		goto fail;
559
560	c->buflen = n;
561
562	fd = EVENT_FD(&c->ev);
563	event_del(&c->ev);
564	event_set(&c->ev, fd, EV_READ | EV_WRITE | EV_PERSIST,
565	    identd_response, c);
566	event_add(&c->ev, NULL);
567	return;
568
569fail:
570	identd_close(c);
571}
572
573void
574child_wr(int fd, short events, void *arg)
575{
576	struct ident_client *c = SIMPLEQ_FIRST(&sc.child.pushing);
577	const char *errstr = NULL;
578	ssize_t n;
579
580	n = write(fd, &c->uid, sizeof(c->uid));
581	switch (n) {
582	case -1:
583		switch (errno) {
584		case EAGAIN:
585			event_add(&proc_wr, NULL);
586			return;
587		case ENOBUFS: /* parent has a backlog of requests */
588			errstr = "UNKNOWN-ERROR";
589			break;
590		default:
591			lerr(1, "child write");
592		}
593		break;
594	case sizeof(c->uid):
595		break;
596	default:
597		lerrx(1, "unexpected child write length %zd", n);
598	}
599
600	SIMPLEQ_REMOVE_HEAD(&sc.child.pushing, entry);
601	if (errstr == NULL)
602		SIMPLEQ_INSERT_TAIL(&sc.child.popping, c, entry);
603	else if (identd_error(c, errstr) == -1)
604		identd_close(c);
605
606	if (!SIMPLEQ_EMPTY(&sc.child.pushing))
607		event_add(&proc_wr, NULL);
608}
609
610void
611identd_listen(const char *addr, const char *port, int family)
612{
613	struct identd_listener *l = NULL;
614
615	struct addrinfo hints, *res, *res0;
616	int error, s;
617	const char *cause = NULL;
618
619	memset(&hints, 0, sizeof(hints));
620	hints.ai_family = family;
621	hints.ai_socktype = SOCK_STREAM;
622	hints.ai_flags = AI_PASSIVE;
623
624	error = getaddrinfo(addr, port, &hints, &res0);
625	if (error)
626		lerrx(1, "%s/%s: %s", addr, port, gai_strerror(error));
627
628	for (res = res0; res != NULL; res = res->ai_next) {
629		s = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
630		if (s == -1) {
631			cause = "socket";
632			continue;
633		}
634
635		if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR,
636		    &on, sizeof(on)) == -1)
637			err(1, "listener setsockopt(SO_REUSEADDR)");
638
639		if (bind(s, res->ai_addr, res->ai_addrlen) == -1) {
640			int serrno = errno;
641
642			cause = "bind";
643			close(s);
644			errno = serrno;
645			continue;
646		}
647
648		if (ioctl(s, FIONBIO, &on) == -1)
649			err(1, "listener ioctl(FIONBIO)");
650
651		if (listen(s, 5) == -1)
652			err(1, "listen");
653
654		l = calloc(1, sizeof(*l));
655		if (l == NULL)
656			err(1, "listener ev alloc");
657
658		event_set(&l->ev, s, EV_READ | EV_PERSIST, identd_accept, l);
659		event_add(&l->ev, NULL);
660		evtimer_set(&l->pause, identd_paused, l);
661	}
662	if (l == NULL)
663		err(1, "%s", cause);
664
665	freeaddrinfo(res0);
666}
667
668void
669identd_paused(int fd, short events, void *arg)
670{
671	struct identd_listener *l = arg;
672	event_add(&l->ev, NULL);
673}
674
675void
676identd_accept(int fd, short events, void *arg)
677{
678	struct identd_listener *l = arg;
679	struct sockaddr_storage ss;
680	struct timeval pause = { 1, 0 };
681	struct ident_client *c = NULL;
682	socklen_t len;
683	int s;
684
685	len = sizeof(ss);
686	s = accept(fd, sa(&ss), &len);
687	if (s == -1) {
688		switch (errno) {
689		case EINTR:
690		case EWOULDBLOCK:
691		case ECONNABORTED:
692			return;
693		case EMFILE:
694		case ENFILE:
695			event_del(&l->ev);
696			evtimer_add(&l->pause, &pause);
697			return;
698		default:
699			lerr(1, "accept");
700		}
701	}
702
703	if (ioctl(s, FIONBIO, &on) == -1)
704		lerr(1, "client ioctl(FIONBIO)");
705
706	c = calloc(1, sizeof(*c));
707	if (c == NULL) {
708		lwarn("client alloc");
709		close(fd);
710		return;
711	}
712
713	memcpy(&c->client.ss, &ss, len);
714	c->client.len = len;
715	ldebug("client: %s", gethost(&ss));
716
717	/* lookup the local ip it connected to */
718	c->server.len = sizeof(c->server.ss);
719	if (getsockname(s, sa(&c->server.ss), &c->server.len) == -1)
720		lerr(1, "getsockname");
721
722	event_set(&c->ev, s, EV_READ | EV_PERSIST, identd_request, c);
723	event_add(&c->ev, NULL);
724
725	event_set(&c->tmo, s, 0, identd_timeout, c);
726	event_add(&c->tmo, &timeout);
727}
728
729void
730identd_timeout(int fd, short events, void *arg)
731{
732	struct ident_client *c = arg;
733
734	event_del(&c->ev);
735	close(fd);
736	free(c->buf);
737
738	if (c->state == S_QUEUED) /* it is queued for resolving */
739		c->state = S_DEAD;
740	else
741		free(c);
742}
743
744void
745identd_request(int fd, short events, void *arg)
746{
747	struct ident_client *c = arg;
748	char buf[64];
749	ssize_t n, i;
750	char *errstr = unknown_err ? "UNKNOWN-ERROR" : "INVALID-PORT";
751
752	n = read(fd, buf, sizeof(buf));
753	switch (n) {
754	case -1:
755		switch (errno) {
756		case EINTR:
757		case EAGAIN:
758			return;
759		default:
760			lwarn("%s read", gethost(&c->client.ss));
761			goto fail;
762		}
763		break;
764
765	case 0:
766		ldebug("%s closed connection", gethost(&c->client.ss));
767		goto fail;
768	default:
769		break;
770	}
771
772	c->rxbytes += n;
773	if (c->rxbytes >= INPUT_MAX)
774		goto fail;
775
776	for (i = 0; c->state < S_EOL && i < n; i++)
777		c->state = identd_parse(c, buf[i]);
778
779	if (c->state == S_DEAD)
780		goto error;
781	if (c->state != S_EOL)
782		return;
783
784	if (c->server.port < 1 || c->client.port < 1)
785		goto error;
786
787	if (fetchuid(c) == -1) {
788		errstr = unknown_err ? "UNKNOWN-ERROR" : "NO-USER";
789		goto error;
790	}
791
792	SIMPLEQ_INSERT_TAIL(&sc.child.pushing, c, entry);
793	c->state = S_QUEUED;
794
795	event_del(&c->ev);
796	event_set(&c->ev, fd, EV_READ | EV_PERSIST, identd_resolving, c);
797	event_add(&c->ev, NULL);
798
799	event_add(&proc_wr, NULL);
800	return;
801
802error:
803	if (identd_error(c, errstr) == -1)
804		goto fail;
805
806	return;
807
808fail:
809	identd_close(c);
810}
811
812int
813identd_error(struct ident_client *c, const char *errstr)
814{
815	int fd = EVENT_FD(&c->ev);
816	ssize_t n;
817
818	n = asprintf(&c->buf, "%u , %u : ERROR : %s\r\n",
819	    c->server.port, c->client.port, errstr);
820	if (n == -1)
821		return (-1);
822
823	c->buflen = n;
824
825	event_del(&c->ev);
826	event_set(&c->ev, fd, EV_READ | EV_WRITE | EV_PERSIST,
827	    identd_response, c);
828	event_add(&c->ev, NULL);
829
830	return (0);
831}
832
833void
834identd_close(struct ident_client *c)
835{
836	int fd = EVENT_FD(&c->ev);
837
838	evtimer_del(&c->tmo);
839	event_del(&c->ev);
840	close(fd);
841	free(c->buf);
842	free(c);
843}
844
845void
846identd_resolving(int fd, short events, void *arg)
847{
848	struct ident_client *c = arg;
849	char buf[64];
850	ssize_t n;
851
852	/*
853	 * something happened while we're waiting for the parent to lookup
854	 * the user.
855	 */
856
857	n = read(fd, buf, sizeof(buf));
858	switch (n) {
859	case -1:
860		switch (errno) {
861		case EINTR:
862		case EAGAIN:
863			return;
864		default:
865			lerrx(1, "resolving read");
866		}
867		/* NOTREACHED */
868	case 0:
869		ldebug("%s closed connection during resolving",
870		    gethost(&c->client.ss));
871		break;
872	default:
873		c->rxbytes += n;
874		if (c->rxbytes >= INPUT_MAX)
875			break;
876
877		/* ignore extra input */
878		return;
879	}
880
881	evtimer_del(&c->tmo);
882	event_del(&c->ev);
883	close(fd);
884	c->state = S_DEAD; /* on the resolving queue */
885}
886
887enum ident_client_state
888identd_parse(struct ident_client *c, int ch)
889{
890	enum ident_client_state s = c->state;
891
892	switch (s) {
893	case S_BEGINNING:
894		/* ignore leading space */
895		if (ch == '\t' || ch == ' ')
896			return (s);
897
898		if (ch == '0' || !isdigit(ch))
899			return (S_DEAD);
900
901		c->server.port = ch - '0';
902		return (S_SERVER_PORT);
903
904	case S_SERVER_PORT:
905		if (ch == '\t' || ch == ' ')
906			return (S_PRE_COMMA);
907		if (ch == ',')
908			return (S_POST_COMMA);
909
910		if (!isdigit(ch))
911			return (S_DEAD);
912
913		c->server.port *= 10;
914		c->server.port += ch - '0';
915		if (c->server.port > 65535)
916			return (S_DEAD);
917
918		return (s);
919
920	case S_PRE_COMMA:
921		if (ch == '\t' || ch == ' ')
922			return (s);
923		if (ch == ',')
924			return (S_POST_COMMA);
925
926		return (S_DEAD);
927
928	case S_POST_COMMA:
929		if (ch == '\t' || ch == ' ')
930			return (s);
931
932		if (ch == '0' || !isdigit(ch))
933			return (S_DEAD);
934
935		c->client.port = ch - '0';
936		return (S_CLIENT_PORT);
937
938	case S_CLIENT_PORT:
939		if (ch == '\t' || ch == ' ')
940			return (S_PRE_EOL);
941		if (ch == '\r' || ch == '\n')
942			return (S_EOL);
943
944		if (!isdigit(ch))
945			return (S_DEAD);
946
947		c->client.port *= 10;
948		c->client.port += ch - '0';
949		if (c->client.port > 65535)
950			return (S_DEAD);
951
952		return (s);
953
954	case S_PRE_EOL:
955		if (ch == '\t' || ch == ' ')
956			return (s);
957		if (ch == '\r' || ch == '\n')
958			return (S_EOL);
959
960		return (S_DEAD);
961
962	case S_EOL:
963		/* ignore trailing garbage */
964		return (s);
965
966	default:
967		return (S_DEAD);
968	}
969}
970
971void
972identd_response(int fd, short events, void *arg)
973{
974	struct ident_client *c = arg;
975	char buf[64];
976	ssize_t n;
977
978	if (events & EV_READ) {
979		n = read(fd, buf, sizeof(buf));
980		switch (n) {
981		case -1:
982			switch (errno) {
983			case EINTR:
984			case EAGAIN:
985				/* meh, try a write */
986				break;
987			default:
988				lerrx(1, "response read");
989			}
990			break;
991		case 0:
992			ldebug("%s closed connection during response",
993			    gethost(&c->client.ss));
994			goto done;
995		default:
996			c->rxbytes += n;
997			if (c->rxbytes >= INPUT_MAX)
998				goto done;
999
1000			/* ignore extra input */
1001			break;
1002		}
1003	}
1004
1005	if (!(events & EV_WRITE))
1006		return; /* try again later */
1007
1008	n = write(fd, c->buf + c->bufoff, c->buflen - c->bufoff);
1009	if (n == -1) {
1010		switch (errno) {
1011		case EAGAIN:
1012			return; /* try again later */
1013		default:
1014			lerr(1, "response write");
1015		}
1016	}
1017
1018	c->bufoff += n;
1019	if (c->bufoff != c->buflen)
1020		return; /* try again later */
1021
1022done:
1023	identd_close(c);
1024}
1025
1026void
1027syslog_vstrerror(int e, int priority, const char *fmt, va_list ap)
1028{
1029	char *s;
1030
1031	if (vasprintf(&s, fmt, ap) == -1) {
1032		syslog(LOG_EMERG, "unable to alloc in syslog_vstrerror");
1033		exit(1);
1034	}
1035	syslog(priority, "%s: %s", s, strerror(e));
1036	free(s);
1037}
1038
1039void
1040syslog_err(int ecode, const char *fmt, ...)
1041{
1042	va_list ap;
1043
1044	va_start(ap, fmt);
1045	syslog_vstrerror(errno, LOG_EMERG, fmt, ap);
1046	va_end(ap);
1047	exit(ecode);
1048}
1049
1050void
1051syslog_errx(int ecode, const char *fmt, ...)
1052{
1053	va_list ap;
1054
1055	va_start(ap, fmt);
1056	vsyslog(LOG_WARNING, fmt, ap);
1057	va_end(ap);
1058	exit(ecode);
1059}
1060
1061void
1062syslog_warn(const char *fmt, ...)
1063{
1064	va_list ap;
1065
1066	va_start(ap, fmt);
1067	syslog_vstrerror(errno, LOG_WARNING, fmt, ap);
1068	va_end(ap);
1069}
1070
1071void
1072syslog_warnx(const char *fmt, ...)
1073{
1074	va_list ap;
1075
1076	va_start(ap, fmt);
1077	vsyslog(LOG_WARNING, fmt, ap);
1078	va_end(ap);
1079}
1080
1081void
1082syslog_notice(const char *fmt, ...)
1083{
1084	va_list ap;
1085
1086	va_start(ap, fmt);
1087	vsyslog(LOG_NOTICE, fmt, ap);
1088	va_end(ap);
1089}
1090
1091void
1092syslog_debug(const char *fmt, ...)
1093{
1094	va_list ap;
1095
1096	if (!debug)
1097		return;
1098
1099	va_start(ap, fmt);
1100	vsyslog(LOG_DEBUG, fmt, ap);
1101	va_end(ap);
1102}
1103
1104const char *
1105gethost(struct sockaddr_storage *ss)
1106{
1107	struct sockaddr *sa = (struct sockaddr *)ss;
1108	static char buf[NI_MAXHOST];
1109
1110	if (getnameinfo(sa, sa->sa_len, buf, sizeof(buf),
1111	    NULL, 0, NI_NUMERICHOST) != 0)
1112		return ("(unknown)");
1113
1114	return (buf);
1115}
1116
1117const char *
1118getport(struct sockaddr_storage *ss)
1119{
1120	struct sockaddr *sa = (struct sockaddr *)ss;
1121	static char buf[NI_MAXSERV];
1122
1123	if (getnameinfo(sa, sa->sa_len, NULL, 0, buf, sizeof(buf),
1124	    NI_NUMERICSERV) != 0)
1125		return ("(unknown)");
1126
1127	return (buf);
1128}
1129
1130const char *
1131gentoken(void)
1132{
1133	static char buf[21];
1134	u_int32_t r;
1135	int i;
1136
1137	buf[0] = 'a' + arc4random_uniform(26);
1138	for (i = 1; i < sizeof(buf) - 1; i++) {
1139		r = arc4random_uniform(36);
1140		buf[i] = (r < 26 ? 'a' : '0' - 26) + r;
1141	}
1142	buf[i] = '\0';
1143
1144	return (buf);
1145}
1146
1147int
1148fetchuid(struct ident_client *c)
1149{
1150	struct tcp_ident_mapping tir;
1151	int mib[] = { CTL_NET, PF_INET, IPPROTO_TCP, TCPCTL_IDENT };
1152	struct sockaddr_in *s4;
1153	struct sockaddr_in6 *s6;
1154	int err = 0;
1155	size_t len;
1156
1157	memset(&tir, 0, sizeof(tir));
1158	memcpy(&tir.faddr, &c->client.ss, sizeof(tir.faddr));
1159	memcpy(&tir.laddr, &c->server.ss, sizeof(tir.laddr));
1160
1161	switch (c->server.ss.ss_family) {
1162	case AF_INET:
1163		s4 = (struct sockaddr_in *)&tir.faddr;
1164		s4->sin_port = htons(c->client.port);
1165
1166		s4 = (struct sockaddr_in *)&tir.laddr;
1167		s4->sin_port = htons(c->server.port);
1168		break;
1169	case AF_INET6:
1170		s6 = (struct sockaddr_in6 *)&tir.faddr;
1171		s6->sin6_port = htons(c->client.port);
1172
1173		s6 = (struct sockaddr_in6 *)&tir.laddr;
1174		s6->sin6_port = htons(c->server.port);
1175		break;
1176	default:
1177		lerrx(1, "unexpected family %d", c->server.ss.ss_family);
1178	}
1179
1180	len = sizeof(tir);
1181	err = sysctl(mib, sizeof(mib) / sizeof(mib[0]), &tir, &len, NULL, 0);
1182	if (err == -1)
1183		lerr(1, "sysctl");
1184
1185	if (tir.ruid == -1)
1186		return (-1);
1187
1188	c->uid = tir.ruid;
1189	return (0);
1190}
1191