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