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