xref: /openbsd-src/usr.sbin/identd/identd.c (revision 515e489c3d599b9cfcdf9cf6842ac49f92e154d6)
1*515e489cSderaadt /*	$OpenBSD: identd.c,v 1.40 2019/07/03 03:24:03 deraadt Exp $ */
2c32efdd3Sdlg 
3c32efdd3Sdlg /*
4c32efdd3Sdlg  * Copyright (c) 2013 David Gwynne <dlg@openbsd.org>
5c32efdd3Sdlg  *
6c32efdd3Sdlg  * Permission to use, copy, modify, and distribute this software for any
7c32efdd3Sdlg  * purpose with or without fee is hereby granted, provided that the above
8c32efdd3Sdlg  * copyright notice and this permission notice appear in all copies.
9c32efdd3Sdlg  *
10c32efdd3Sdlg  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11c32efdd3Sdlg  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12c32efdd3Sdlg  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13c32efdd3Sdlg  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14c32efdd3Sdlg  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15c32efdd3Sdlg  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16c32efdd3Sdlg  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17c32efdd3Sdlg  */
18c32efdd3Sdlg 
19c32efdd3Sdlg #include <sys/types.h>
2053ea3549Sderaadt #include <sys/ioctl.h>
21c32efdd3Sdlg #include <sys/socket.h>
22ce704e6aSmpi #include <sys/queue.h>
234298a39aSdlg #include <sys/stat.h>
24c32efdd3Sdlg #include <sys/sysctl.h>
25c32efdd3Sdlg #include <sys/uio.h>
26c32efdd3Sdlg 
27c32efdd3Sdlg #include <netinet/in.h>
28c32efdd3Sdlg #include <netinet/tcp.h>
29c32efdd3Sdlg #include <netinet/tcp_timer.h>
30c32efdd3Sdlg #include <netinet/tcp_var.h>
31c32efdd3Sdlg 
32c32efdd3Sdlg #include <netdb.h>
33c32efdd3Sdlg 
34c32efdd3Sdlg #include <err.h>
35c32efdd3Sdlg #include <ctype.h>
36c32efdd3Sdlg #include <errno.h>
37c32efdd3Sdlg #include <event.h>
384fe4baadSdlg #include <fcntl.h>
39c32efdd3Sdlg #include <pwd.h>
40c32efdd3Sdlg #include <stdio.h>
41b9fc9a72Sderaadt #include <limits.h>
42c32efdd3Sdlg #include <stdlib.h>
43be9b181dSdlg #include <stdarg.h>
44c32efdd3Sdlg #include <string.h>
4554ea7419Sdlg #include <signal.h>
46c32efdd3Sdlg #include <syslog.h>
47c32efdd3Sdlg #include <unistd.h>
48c32efdd3Sdlg 
49c32efdd3Sdlg #define IDENTD_USER "_identd"
50c32efdd3Sdlg 
514fe4baadSdlg #define DOTNOIDENT ".noident"
524fe4baadSdlg 
53c32efdd3Sdlg #define TIMEOUT_MIN 4
54c32efdd3Sdlg #define TIMEOUT_MAX 240
55c32efdd3Sdlg #define TIMEOUT_DEFAULT 120
56acc4509dSdlg #define INPUT_MAX 256
57c32efdd3Sdlg 
58c32efdd3Sdlg enum ident_client_state {
59c32efdd3Sdlg 	S_BEGINNING = 0,
60c32efdd3Sdlg 	S_SERVER_PORT,
61c32efdd3Sdlg 	S_PRE_COMMA,
62c32efdd3Sdlg 	S_POST_COMMA,
63c32efdd3Sdlg 	S_CLIENT_PORT,
64c32efdd3Sdlg 	S_PRE_EOL,
65c32efdd3Sdlg 	S_EOL,
66c32efdd3Sdlg 
67c32efdd3Sdlg 	S_DEAD,
689a4217c3Sdlg 	S_QUEUED
69c32efdd3Sdlg };
70c32efdd3Sdlg 
71c32efdd3Sdlg #define E_NONE		0
72c32efdd3Sdlg #define E_NOUSER	1
73c32efdd3Sdlg #define E_UNKNOWN	2
74c32efdd3Sdlg #define E_HIDDEN	3
75c32efdd3Sdlg 
76c32efdd3Sdlg struct ident_client {
77c32efdd3Sdlg 	struct {
78c32efdd3Sdlg 		/* from the socket */
79c32efdd3Sdlg 		struct sockaddr_storage ss;
80c32efdd3Sdlg 		socklen_t len;
81c32efdd3Sdlg 
82c32efdd3Sdlg 		/* from the request */
83c32efdd3Sdlg 		u_int port;
84c32efdd3Sdlg 	} client, server;
85c32efdd3Sdlg 	SIMPLEQ_ENTRY(ident_client) entry;
86c32efdd3Sdlg 	enum ident_client_state state;
87c32efdd3Sdlg 	struct event ev;
889a4217c3Sdlg 	struct event tmo;
89acc4509dSdlg 	size_t rxbytes;
90c32efdd3Sdlg 
91c32efdd3Sdlg 	char *buf;
92c32efdd3Sdlg 	size_t buflen;
93c32efdd3Sdlg 	size_t bufoff;
94c32efdd3Sdlg 	uid_t uid;
95c32efdd3Sdlg };
96c32efdd3Sdlg 
97c32efdd3Sdlg struct ident_resolver {
98c32efdd3Sdlg 	SIMPLEQ_ENTRY(ident_resolver) entry;
99c32efdd3Sdlg 	char *buf;
100c32efdd3Sdlg 	size_t buflen;
101c32efdd3Sdlg 	u_int error;
102c32efdd3Sdlg };
103c32efdd3Sdlg 
104ff28ff65Sdlg struct identd_listener {
105ff28ff65Sdlg 	struct event ev, pause;
106ff28ff65Sdlg };
107ff28ff65Sdlg 
108c32efdd3Sdlg void	parent_rd(int, short, void *);
109c32efdd3Sdlg void	parent_wr(int, short, void *);
11053389e0dSdlg int	parent_username(struct ident_resolver *, struct passwd *);
11153389e0dSdlg int	parent_uid(struct ident_resolver *, struct passwd *);
1123f52e118Sdlg int	parent_token(struct ident_resolver *, struct passwd *);
1134fe4baadSdlg void	parent_noident(struct ident_resolver *, struct passwd *);
114c32efdd3Sdlg 
115c32efdd3Sdlg void	child_rd(int, short, void *);
116c32efdd3Sdlg void	child_wr(int, short, void *);
117c32efdd3Sdlg 
118c32efdd3Sdlg void	identd_listen(const char *, const char *, int);
119ff28ff65Sdlg void	identd_paused(int, short, void *);
120c32efdd3Sdlg void	identd_accept(int, short, void *);
121181dd5c9Sdlg int	identd_error(struct ident_client *, const char *);
122181dd5c9Sdlg void	identd_close(struct ident_client *);
1239a4217c3Sdlg void	identd_timeout(int, short, void *);
124c32efdd3Sdlg void	identd_request(int, short, void *);
125c32efdd3Sdlg enum ident_client_state
126c32efdd3Sdlg 	identd_parse(struct ident_client *, int);
127c32efdd3Sdlg void	identd_resolving(int, short, void *);
128c32efdd3Sdlg void	identd_response(int, short, void *);
129c32efdd3Sdlg int	fetchuid(struct ident_client *);
130c32efdd3Sdlg 
131c32efdd3Sdlg const char *gethost(struct sockaddr_storage *);
1323f52e118Sdlg const char *gentoken(void);
133c32efdd3Sdlg 
134c32efdd3Sdlg struct loggers {
135f7986388Sflorian 	__dead void (*err)(int, const char *, ...)
136f7986388Sflorian 	    __attribute__((__format__ (printf, 2, 3)));
137f7986388Sflorian 	__dead void (*errx)(int, const char *, ...)
138f7986388Sflorian 	    __attribute__((__format__ (printf, 2, 3)));
139f7986388Sflorian 	void (*warn)(const char *, ...)
140f7986388Sflorian 	    __attribute__((__format__ (printf, 1, 2)));
141f7986388Sflorian 	void (*warnx)(const char *, ...)
142f7986388Sflorian 	    __attribute__((__format__ (printf, 1, 2)));
143f7986388Sflorian 	void (*notice)(const char *, ...)
144f7986388Sflorian 	    __attribute__((__format__ (printf, 1, 2)));
145f7986388Sflorian 	void (*debug)(const char *, ...)
146f7986388Sflorian 	    __attribute__((__format__ (printf, 1, 2)));
147c32efdd3Sdlg };
148c32efdd3Sdlg 
149c32efdd3Sdlg const struct loggers conslogger = {
150c32efdd3Sdlg 	err,
151c32efdd3Sdlg 	errx,
152c32efdd3Sdlg 	warn,
153c32efdd3Sdlg 	warnx,
1543f52e118Sdlg 	warnx, /* notice */
155c32efdd3Sdlg 	warnx /* debug */
156c32efdd3Sdlg };
157c32efdd3Sdlg 
158f7986388Sflorian __dead void	syslog_err(int, const char *, ...)
159f7986388Sflorian 		    __attribute__((__format__ (printf, 2, 3)));
160f7986388Sflorian __dead void	syslog_errx(int, const char *, ...)
161f7986388Sflorian 		    __attribute__((__format__ (printf, 2, 3)));
162f7986388Sflorian void		syslog_warn(const char *, ...)
163f7986388Sflorian 		    __attribute__((__format__ (printf, 1, 2)));
164f7986388Sflorian void		syslog_warnx(const char *, ...)
165f7986388Sflorian 		    __attribute__((__format__ (printf, 1, 2)));
166f7986388Sflorian void		syslog_notice(const char *, ...)
167f7986388Sflorian 		    __attribute__((__format__ (printf, 1, 2)));
168f7986388Sflorian void		syslog_debug(const char *, ...)
169f7986388Sflorian 		    __attribute__((__format__ (printf, 1, 2)));
170f7986388Sflorian void		syslog_vstrerror(int, int, const char *, va_list)
171f7986388Sflorian 		    __attribute__((__format__ (printf, 3, 0)));
172c32efdd3Sdlg 
173c32efdd3Sdlg const struct loggers syslogger = {
174c32efdd3Sdlg 	syslog_err,
175c32efdd3Sdlg 	syslog_errx,
176c32efdd3Sdlg 	syslog_warn,
177c32efdd3Sdlg 	syslog_warnx,
1783f52e118Sdlg 	syslog_notice,
179c32efdd3Sdlg 	syslog_debug
180c32efdd3Sdlg };
181c32efdd3Sdlg 
182c32efdd3Sdlg const struct loggers *logger = &conslogger;
183c32efdd3Sdlg 
184c32efdd3Sdlg #define lerr(_e, _f...) logger->err((_e), _f)
185c32efdd3Sdlg #define lerrx(_e, _f...) logger->errx((_e), _f)
186c32efdd3Sdlg #define lwarn(_f...) logger->warn(_f)
187c32efdd3Sdlg #define lwarnx(_f...) logger->warnx(_f)
1883f52e118Sdlg #define lnotice(_f...) logger->notice(_f)
189c32efdd3Sdlg #define ldebug(_f...) logger->debug(_f)
190c32efdd3Sdlg 
191c32efdd3Sdlg #define sa(_ss) ((struct sockaddr *)(_ss))
192c32efdd3Sdlg 
193ffa0ebfeSderaadt static __dead void
usage(void)194c32efdd3Sdlg usage(void)
195c32efdd3Sdlg {
196c32efdd3Sdlg 	extern char *__progname;
1974cc355a6Sokan 	fprintf(stderr, "usage: %s [-46deHhNn] [-l address] [-t timeout]\n",
1986cde5dafSdlg 	    __progname);
199c32efdd3Sdlg 	exit(1);
200c32efdd3Sdlg }
201c32efdd3Sdlg 
202c32efdd3Sdlg struct timeval timeout = { TIMEOUT_DEFAULT, 0 };
203c32efdd3Sdlg int debug = 0;
2044fe4baadSdlg int noident = 0;
2056b9316d1Ssthen int unknown_err = 0;
2064cc355a6Sokan int hideall = 0;
207c32efdd3Sdlg 
20853389e0dSdlg int (*parent_uprintf)(struct ident_resolver *, struct passwd *) =
20953389e0dSdlg     parent_username;
21053389e0dSdlg 
211c32efdd3Sdlg struct event proc_rd, proc_wr;
212c32efdd3Sdlg union {
213c32efdd3Sdlg 	struct {
214c32efdd3Sdlg 		SIMPLEQ_HEAD(, ident_resolver) replies;
215c32efdd3Sdlg 	} parent;
216c32efdd3Sdlg 	struct {
217c32efdd3Sdlg 		SIMPLEQ_HEAD(, ident_client) pushing, popping;
218c32efdd3Sdlg 	} child;
219c32efdd3Sdlg } sc;
220c32efdd3Sdlg 
221c32efdd3Sdlg int
main(int argc,char * argv[])222c32efdd3Sdlg main(int argc, char *argv[])
223c32efdd3Sdlg {
224c32efdd3Sdlg 	extern char *__progname;
225c32efdd3Sdlg 	const char *errstr = NULL;
226c32efdd3Sdlg 
227c32efdd3Sdlg 	int		 c;
228c32efdd3Sdlg 	struct passwd	*pw;
229c32efdd3Sdlg 
230c32efdd3Sdlg 	char *addr = NULL;
231c32efdd3Sdlg 	int family = AF_UNSPEC;
232c32efdd3Sdlg 
233c32efdd3Sdlg 	int pair[2];
234c32efdd3Sdlg 	pid_t parent;
235c32efdd3Sdlg 	int sibling;
236c32efdd3Sdlg 
237cb94ec52Stobias 	while ((c = getopt(argc, argv, "46deHhl:Nnt:")) != -1) {
238c32efdd3Sdlg 		switch (c) {
239c32efdd3Sdlg 		case '4':
240c32efdd3Sdlg 			family = AF_INET;
241c32efdd3Sdlg 			break;
242c32efdd3Sdlg 		case '6':
243c32efdd3Sdlg 			family = AF_INET6;
244c32efdd3Sdlg 			break;
245c32efdd3Sdlg 		case 'd':
246c32efdd3Sdlg 			debug = 1;
247c32efdd3Sdlg 			break;
2486b9316d1Ssthen 		case 'e':
2496b9316d1Ssthen 			unknown_err = 1;
2506b9316d1Ssthen 			break;
2514cc355a6Sokan 		case 'H':
2524cc355a6Sokan 			hideall = 1;
2534cc355a6Sokan 			/* FALLTHROUGH */
2543f52e118Sdlg 		case 'h':
2553f52e118Sdlg 			parent_uprintf = parent_token;
2563f52e118Sdlg 			break;
257c32efdd3Sdlg 		case 'l':
258c32efdd3Sdlg 			addr = optarg;
259c32efdd3Sdlg 			break;
2604fe4baadSdlg 		case 'N':
2614fe4baadSdlg 			noident = 1;
2624fe4baadSdlg 			break;
26353389e0dSdlg 		case 'n':
26453389e0dSdlg 			parent_uprintf = parent_uid;
26553389e0dSdlg 			break;
266c32efdd3Sdlg 		case 't':
267c32efdd3Sdlg 			timeout.tv_sec = strtonum(optarg,
268c32efdd3Sdlg 			    TIMEOUT_MIN, TIMEOUT_MAX, &errstr);
269c32efdd3Sdlg 			if (errstr != NULL)
270c32efdd3Sdlg 				errx(1, "timeout %s is %s", optarg, errstr);
271c32efdd3Sdlg 			break;
272c32efdd3Sdlg 		default:
273c32efdd3Sdlg 			usage();
274c32efdd3Sdlg 			/* NOTREACHED */
275c32efdd3Sdlg 		}
276c32efdd3Sdlg 	}
277c32efdd3Sdlg 
278c32efdd3Sdlg 	argc -= optind;
279c32efdd3Sdlg 	argv += optind;
280c32efdd3Sdlg 
281c32efdd3Sdlg 	if (argc != 0)
282c32efdd3Sdlg 		usage();
283c32efdd3Sdlg 
284c32efdd3Sdlg 	if (geteuid() != 0)
285c32efdd3Sdlg 		errx(1, "need root privileges");
286c32efdd3Sdlg 
2874c23dba0Sdlg 	if (socketpair(AF_UNIX, SOCK_SEQPACKET | SOCK_NONBLOCK,
2884c23dba0Sdlg 	    PF_UNSPEC, pair) == -1)
289c32efdd3Sdlg 		err(1, "socketpair");
290c32efdd3Sdlg 
291c32efdd3Sdlg 	pw = getpwnam(IDENTD_USER);
292c32efdd3Sdlg 	if (pw == NULL)
29303436e2cSclaudio 		errx(1, "no %s user", IDENTD_USER);
294c32efdd3Sdlg 
295c32efdd3Sdlg 	if (!debug && daemon(1, 0) == -1)
296c32efdd3Sdlg 		err(1, "daemon");
297c32efdd3Sdlg 
298c32efdd3Sdlg 	parent = fork();
299c32efdd3Sdlg 	switch (parent) {
300c32efdd3Sdlg 	case -1:
301c32efdd3Sdlg 		lerr(1, "fork");
302c32efdd3Sdlg 
303c32efdd3Sdlg 	case 0:
304c32efdd3Sdlg 		/* child */
305c32efdd3Sdlg 		setproctitle("listener");
306c32efdd3Sdlg 		close(pair[1]);
307c32efdd3Sdlg 		sibling = pair[0];
308c32efdd3Sdlg 		break;
309c32efdd3Sdlg 
310c32efdd3Sdlg 	default:
311c32efdd3Sdlg 		/* parent */
312c32efdd3Sdlg 		setproctitle("resolver");
313c32efdd3Sdlg 		close(pair[0]);
314c32efdd3Sdlg 		sibling = pair[1];
315c32efdd3Sdlg 		break;
316c32efdd3Sdlg 	}
317c32efdd3Sdlg 
318c32efdd3Sdlg 	if (!debug) {
319c32efdd3Sdlg 		openlog(__progname, LOG_PID|LOG_NDELAY, LOG_DAEMON);
320c32efdd3Sdlg 		tzset();
321c32efdd3Sdlg 		logger = &syslogger;
322c32efdd3Sdlg 	}
323c32efdd3Sdlg 
324c32efdd3Sdlg 	event_init();
325c32efdd3Sdlg 
32654ea7419Sdlg 	if (signal(SIGPIPE, SIG_IGN) == SIG_ERR)
32754ea7419Sdlg 		lerr(1, "signal(SIGPIPE)");
32854ea7419Sdlg 
329c32efdd3Sdlg 	if (parent) {
330095634e3Sjca 		if (pledge("stdio getpw rpath id", NULL) == -1)
33164e16a71Sdoug 			err(1, "pledge");
33264e16a71Sdoug 
333c32efdd3Sdlg 		SIMPLEQ_INIT(&sc.parent.replies);
334c32efdd3Sdlg 
335c32efdd3Sdlg 		event_set(&proc_rd, sibling, EV_READ | EV_PERSIST,
336c32efdd3Sdlg 		    parent_rd, NULL);
337c32efdd3Sdlg 		event_set(&proc_wr, sibling, EV_WRITE,
338c32efdd3Sdlg 		    parent_wr, NULL);
339c32efdd3Sdlg 	} else {
340c32efdd3Sdlg 		SIMPLEQ_INIT(&sc.child.pushing);
341c32efdd3Sdlg 		SIMPLEQ_INIT(&sc.child.popping);
342c32efdd3Sdlg 
343986c812aSdlg 		identd_listen(addr, "auth", family);
344c32efdd3Sdlg 
345c32efdd3Sdlg 		if (chroot(pw->pw_dir) == -1)
346c32efdd3Sdlg 			lerr(1, "chroot(%s)", pw->pw_dir);
347c32efdd3Sdlg 
348c32efdd3Sdlg 		if (chdir("/") == -1)
349c32efdd3Sdlg 			lerr(1, "chdir(%s)", pw->pw_dir);
350c32efdd3Sdlg 
351c32efdd3Sdlg 		event_set(&proc_rd, sibling, EV_READ | EV_PERSIST,
352c32efdd3Sdlg 		    child_rd, NULL);
353c32efdd3Sdlg 		event_set(&proc_wr, sibling, EV_WRITE,
354c32efdd3Sdlg 		    child_wr, NULL);
355c32efdd3Sdlg 	}
356c32efdd3Sdlg 
357c32efdd3Sdlg 	if (setgroups(1, &pw->pw_gid) ||
358c32efdd3Sdlg 	    setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) ||
359c32efdd3Sdlg 	    setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid))
360c32efdd3Sdlg 		lerr(1, "unable to revoke privs");
361c32efdd3Sdlg 
36264e16a71Sdoug 	if (parent) {
36364e16a71Sdoug 		if (noident) {
36464e16a71Sdoug 			if (pledge("stdio getpw rpath", NULL) == -1)
36564e16a71Sdoug 				err(1, "pledge");
36664e16a71Sdoug 		} else {
36764e16a71Sdoug 			if (pledge("stdio getpw", NULL) == -1)
36864e16a71Sdoug 				err(1, "pledge");
36964e16a71Sdoug 		}
37064e16a71Sdoug 	}
37164e16a71Sdoug 
372c32efdd3Sdlg 	event_add(&proc_rd, NULL);
373c32efdd3Sdlg 	event_dispatch();
374c32efdd3Sdlg 	return (0);
375c32efdd3Sdlg }
376c32efdd3Sdlg 
377c32efdd3Sdlg void
parent_rd(int fd,short events,void * arg)378c32efdd3Sdlg parent_rd(int fd, short events, void *arg)
379c32efdd3Sdlg {
380c32efdd3Sdlg 	struct ident_resolver *r;
381c32efdd3Sdlg 	struct passwd *pw;
382c32efdd3Sdlg 	ssize_t n;
383c32efdd3Sdlg 	uid_t uid;
384c32efdd3Sdlg 
385c32efdd3Sdlg 	n = read(fd, &uid, sizeof(uid));
386c32efdd3Sdlg 	switch (n) {
387c32efdd3Sdlg 	case -1:
388c32efdd3Sdlg 		switch (errno) {
389c32efdd3Sdlg 		case EAGAIN:
390c32efdd3Sdlg 		case EINTR:
391c32efdd3Sdlg 			return;
392c32efdd3Sdlg 		default:
393c32efdd3Sdlg 			lerr(1, "parent read");
394c32efdd3Sdlg 		}
395c32efdd3Sdlg 		break;
396c32efdd3Sdlg 	case 0:
397c32efdd3Sdlg 		lerrx(1, "child has gone");
398c32efdd3Sdlg 	case sizeof(uid):
399c32efdd3Sdlg 		break;
400c32efdd3Sdlg 	default:
401c32efdd3Sdlg 		lerrx(1, "unexpected %zd data from child", n);
402c32efdd3Sdlg 	}
403c32efdd3Sdlg 
404c32efdd3Sdlg 	r = calloc(1, sizeof(*r));
405c32efdd3Sdlg 	if (r == NULL)
406c32efdd3Sdlg 		lerr(1, "resolver alloc");
407c32efdd3Sdlg 
408c32efdd3Sdlg 	pw = getpwuid(uid);
4094cc355a6Sokan 	if (pw == NULL && !hideall) {
410c32efdd3Sdlg 		r->error = E_NOUSER;
4114fe4baadSdlg 		goto done;
4124fe4baadSdlg 	}
4134fe4baadSdlg 
4144cc355a6Sokan 	if (noident && !hideall) {
4154fe4baadSdlg 		parent_noident(r, pw);
4164fe4baadSdlg 		if (r->error != E_NONE)
4174fe4baadSdlg 			goto done;
4184fe4baadSdlg 	}
4194fe4baadSdlg 
42053389e0dSdlg 	n = (*parent_uprintf)(r, pw);
4214fe4baadSdlg 	if (n == -1) {
422c32efdd3Sdlg 		r->error = E_UNKNOWN;
4234fe4baadSdlg 		goto done;
4244fe4baadSdlg 	}
4254fe4baadSdlg 
426c78ca77fSdlg 	r->buflen = n + 1;
4274fe4baadSdlg 
4284fe4baadSdlg done:
4294fe4baadSdlg 	SIMPLEQ_INSERT_TAIL(&sc.parent.replies, r, entry);
4304fe4baadSdlg 	event_add(&proc_wr, NULL);
4314fe4baadSdlg }
4324fe4baadSdlg 
43353389e0dSdlg int
parent_username(struct ident_resolver * r,struct passwd * pw)43453389e0dSdlg parent_username(struct ident_resolver *r, struct passwd *pw)
43553389e0dSdlg {
43653389e0dSdlg 	return (asprintf(&r->buf, "%s", pw->pw_name));
43753389e0dSdlg }
43853389e0dSdlg 
43953389e0dSdlg int
parent_uid(struct ident_resolver * r,struct passwd * pw)44053389e0dSdlg parent_uid(struct ident_resolver *r, struct passwd *pw)
44153389e0dSdlg {
44253389e0dSdlg 	return (asprintf(&r->buf, "%u", (u_int)pw->pw_uid));
44353389e0dSdlg }
44453389e0dSdlg 
4453f52e118Sdlg int
parent_token(struct ident_resolver * r,struct passwd * pw)4463f52e118Sdlg parent_token(struct ident_resolver *r, struct passwd *pw)
4473f52e118Sdlg {
4483f52e118Sdlg 	const char *token;
4493f52e118Sdlg 	int rv;
4503f52e118Sdlg 
4513f52e118Sdlg 	token = gentoken();
4523f52e118Sdlg 	rv = asprintf(&r->buf, "%s", token);
4533f52e118Sdlg 	if (rv != -1) {
4544cc355a6Sokan 		if (pw)
4553f52e118Sdlg 			lnotice("token %s == uid %u (%s)", token,
4563f52e118Sdlg 			    (u_int)pw->pw_uid, pw->pw_name);
4574cc355a6Sokan 		else
4584cc355a6Sokan 			lnotice("token %s == NO USER", token);
4593f52e118Sdlg 	}
4603f52e118Sdlg 
4613f52e118Sdlg 	return (rv);
4623f52e118Sdlg }
4633f52e118Sdlg 
4644fe4baadSdlg void
parent_noident(struct ident_resolver * r,struct passwd * pw)4654fe4baadSdlg parent_noident(struct ident_resolver *r, struct passwd *pw)
4664fe4baadSdlg {
467b9fc9a72Sderaadt 	char path[PATH_MAX];
4684298a39aSdlg 	struct stat st;
4694fe4baadSdlg 	int rv;
4704fe4baadSdlg 
4714fe4baadSdlg 	rv = snprintf(path, sizeof(path), "%s/%s", pw->pw_dir, DOTNOIDENT);
472*515e489cSderaadt 	if (rv < 0 || rv >= sizeof(path)) {
4734fe4baadSdlg 		r->error = E_UNKNOWN;
4744fe4baadSdlg 		return;
4754fe4baadSdlg 	}
4764fe4baadSdlg 
4774298a39aSdlg 	if (stat(path, &st) == -1)
4784fe4baadSdlg 		return;
4794fe4baadSdlg 
4804fe4baadSdlg 	r->error = E_HIDDEN;
481c32efdd3Sdlg }
482c32efdd3Sdlg 
483c32efdd3Sdlg void
parent_wr(int fd,short events,void * arg)484c32efdd3Sdlg parent_wr(int fd, short events, void *arg)
485c32efdd3Sdlg {
486c32efdd3Sdlg 	struct ident_resolver *r = SIMPLEQ_FIRST(&sc.parent.replies);
487c32efdd3Sdlg 	struct iovec iov[2];
488c32efdd3Sdlg 	int iovcnt = 0;
489c32efdd3Sdlg 	ssize_t n;
490c32efdd3Sdlg 
491c32efdd3Sdlg 	iov[iovcnt].iov_base = &r->error;
492c32efdd3Sdlg 	iov[iovcnt].iov_len = sizeof(r->error);
493c32efdd3Sdlg 	iovcnt++;
494c32efdd3Sdlg 
495c32efdd3Sdlg 	if (r->buflen > 0) {
496c32efdd3Sdlg 		iov[iovcnt].iov_base = r->buf;
497c32efdd3Sdlg 		iov[iovcnt].iov_len = r->buflen;
498c32efdd3Sdlg 		iovcnt++;
499c32efdd3Sdlg 	}
500c32efdd3Sdlg 
501c32efdd3Sdlg 	n = writev(fd, iov, iovcnt);
502c32efdd3Sdlg 	if (n == -1) {
5032807f553Sdlg 		switch (errno) {
5042807f553Sdlg 		case EINTR:
5052807f553Sdlg 		case EAGAIN:
506c32efdd3Sdlg 			event_add(&proc_wr, NULL);
507c32efdd3Sdlg 			return;
5082807f553Sdlg 		default:
509c32efdd3Sdlg 			lerr(1, "parent write");
510c32efdd3Sdlg 		}
5112807f553Sdlg 	}
512c32efdd3Sdlg 
513c32efdd3Sdlg 	if (n != sizeof(r->error) + r->buflen)
514c32efdd3Sdlg 		lerrx(1, "unexpected parent write length %zd", n);
515c32efdd3Sdlg 
516c32efdd3Sdlg 	SIMPLEQ_REMOVE_HEAD(&sc.parent.replies, entry);
517c32efdd3Sdlg 
518c32efdd3Sdlg 	if (r->buflen > 0)
519c32efdd3Sdlg 		free(r->buf);
520c32efdd3Sdlg 
521c32efdd3Sdlg 	free(r);
522e36d0f04Sdlg 
523e36d0f04Sdlg 	if (!SIMPLEQ_EMPTY(&sc.parent.replies))
524e36d0f04Sdlg 		event_add(&proc_wr, NULL);
525c32efdd3Sdlg }
526c32efdd3Sdlg 
527c32efdd3Sdlg void
child_rd(int fd,short events,void * arg)528c32efdd3Sdlg child_rd(int fd, short events, void *arg)
529c32efdd3Sdlg {
530c32efdd3Sdlg 	struct ident_client *c;
531c32efdd3Sdlg 	struct {
532c32efdd3Sdlg 		u_int error;
533c32efdd3Sdlg 		char buf[512];
534c32efdd3Sdlg 	} reply;
535c32efdd3Sdlg 	ssize_t n;
536c32efdd3Sdlg 
537c32efdd3Sdlg 	n = read(fd, &reply, sizeof(reply));
538c32efdd3Sdlg 	switch (n) {
539c32efdd3Sdlg 	case -1:
540c32efdd3Sdlg 		switch (errno) {
541c32efdd3Sdlg 		case EAGAIN:
542c32efdd3Sdlg 		case EINTR:
543c32efdd3Sdlg 			return;
544c32efdd3Sdlg 		default:
545c32efdd3Sdlg 			lerr(1, "child read");
546c32efdd3Sdlg 		}
547c32efdd3Sdlg 		break;
548c32efdd3Sdlg 	case 0:
549c32efdd3Sdlg 		lerrx(1, "parent has gone");
550c32efdd3Sdlg 	default:
551c32efdd3Sdlg 		break;
552c32efdd3Sdlg 	}
553c32efdd3Sdlg 
554c32efdd3Sdlg 	c = SIMPLEQ_FIRST(&sc.child.popping);
555c32efdd3Sdlg 	if (c == NULL)
556c32efdd3Sdlg 		lerrx(1, "unsolicited data from parent");
557c32efdd3Sdlg 
558c32efdd3Sdlg 	SIMPLEQ_REMOVE_HEAD(&sc.child.popping, entry);
559c32efdd3Sdlg 
560c32efdd3Sdlg 	if (n < sizeof(reply.error))
561c32efdd3Sdlg 		lerrx(1, "short data from parent");
562c32efdd3Sdlg 
563c32efdd3Sdlg 	/* check if something went wrong while the parent was working */
5649a4217c3Sdlg 	if (c->state == S_DEAD) {
565c32efdd3Sdlg 		free(c);
566c32efdd3Sdlg 		return;
567c32efdd3Sdlg 	}
5689a4217c3Sdlg 	c->state = S_DEAD;
569c32efdd3Sdlg 
570c32efdd3Sdlg 	switch (reply.error) {
571c32efdd3Sdlg 	case E_NONE:
572c32efdd3Sdlg 		n = asprintf(&c->buf, "%u , %u : USERID : UNIX : %s\r\n",
573c32efdd3Sdlg 		    c->server.port, c->client.port, reply.buf);
574c32efdd3Sdlg 		break;
575c32efdd3Sdlg 	case E_NOUSER:
5766b9316d1Ssthen 		n = asprintf(&c->buf, "%u , %u : ERROR : %s\r\n",
5776b9316d1Ssthen 		    c->server.port, c->client.port,
5786b9316d1Ssthen 		    unknown_err ? "UNKNOWN-ERROR" : "NO-USER");
579c32efdd3Sdlg 		break;
580c32efdd3Sdlg 	case E_UNKNOWN:
581c32efdd3Sdlg 		n = asprintf(&c->buf, "%u , %u : ERROR : UNKNOWN-ERROR\r\n",
582c32efdd3Sdlg 		    c->server.port, c->client.port);
583c32efdd3Sdlg 		break;
584c32efdd3Sdlg 	case E_HIDDEN:
585c32efdd3Sdlg 		n = asprintf(&c->buf, "%u , %u : ERROR : HIDDEN-USER\r\n",
586c32efdd3Sdlg 		    c->server.port, c->client.port);
587c32efdd3Sdlg 		break;
588c32efdd3Sdlg 	default:
589c32efdd3Sdlg 		lerrx(1, "unexpected error from parent %u", reply.error);
590c32efdd3Sdlg 	}
591c32efdd3Sdlg 	if (n == -1)
592c32efdd3Sdlg 		goto fail;
593c32efdd3Sdlg 
594c32efdd3Sdlg 	c->buflen = n;
595c32efdd3Sdlg 
596c32efdd3Sdlg 	fd = EVENT_FD(&c->ev);
597c32efdd3Sdlg 	event_del(&c->ev);
598c32efdd3Sdlg 	event_set(&c->ev, fd, EV_READ | EV_WRITE | EV_PERSIST,
599c32efdd3Sdlg 	    identd_response, c);
6009a4217c3Sdlg 	event_add(&c->ev, NULL);
601c32efdd3Sdlg 	return;
602c32efdd3Sdlg 
603c32efdd3Sdlg fail:
604181dd5c9Sdlg 	identd_close(c);
605c32efdd3Sdlg }
606c32efdd3Sdlg 
607c32efdd3Sdlg void
child_wr(int fd,short events,void * arg)608c32efdd3Sdlg child_wr(int fd, short events, void *arg)
609c32efdd3Sdlg {
610c32efdd3Sdlg 	struct ident_client *c = SIMPLEQ_FIRST(&sc.child.pushing);
611181dd5c9Sdlg 	const char *errstr = NULL;
612c32efdd3Sdlg 	ssize_t n;
613c32efdd3Sdlg 
614c32efdd3Sdlg 	n = write(fd, &c->uid, sizeof(c->uid));
615c32efdd3Sdlg 	switch (n) {
616c32efdd3Sdlg 	case -1:
617181dd5c9Sdlg 		switch (errno) {
6182807f553Sdlg 		case EINTR:
619181dd5c9Sdlg 		case EAGAIN:
620c32efdd3Sdlg 			event_add(&proc_wr, NULL);
621c32efdd3Sdlg 			return;
622181dd5c9Sdlg 		case ENOBUFS: /* parent has a backlog of requests */
623181dd5c9Sdlg 			errstr = "UNKNOWN-ERROR";
624181dd5c9Sdlg 			break;
625181dd5c9Sdlg 		default:
626c32efdd3Sdlg 			lerr(1, "child write");
627181dd5c9Sdlg 		}
628181dd5c9Sdlg 		break;
629c32efdd3Sdlg 	case sizeof(c->uid):
630c32efdd3Sdlg 		break;
631c32efdd3Sdlg 	default:
632c32efdd3Sdlg 		lerrx(1, "unexpected child write length %zd", n);
633c32efdd3Sdlg 	}
634c32efdd3Sdlg 
635c32efdd3Sdlg 	SIMPLEQ_REMOVE_HEAD(&sc.child.pushing, entry);
636181dd5c9Sdlg 	if (errstr == NULL)
637c32efdd3Sdlg 		SIMPLEQ_INSERT_TAIL(&sc.child.popping, c, entry);
638181dd5c9Sdlg 	else if (identd_error(c, errstr) == -1)
639181dd5c9Sdlg 		identd_close(c);
640c32efdd3Sdlg 
641c32efdd3Sdlg 	if (!SIMPLEQ_EMPTY(&sc.child.pushing))
642c32efdd3Sdlg 		event_add(&proc_wr, NULL);
643c32efdd3Sdlg }
644c32efdd3Sdlg 
645c32efdd3Sdlg void
identd_listen(const char * addr,const char * port,int family)646c32efdd3Sdlg identd_listen(const char *addr, const char *port, int family)
647c32efdd3Sdlg {
648ff28ff65Sdlg 	struct identd_listener *l = NULL;
649c32efdd3Sdlg 
650c32efdd3Sdlg 	struct addrinfo hints, *res, *res0;
651397cfbf5Sderaadt 	int error, s;
652c32efdd3Sdlg 	const char *cause = NULL;
6534c23dba0Sdlg 	int on = 1;
654c32efdd3Sdlg 
655c32efdd3Sdlg 	memset(&hints, 0, sizeof(hints));
656c32efdd3Sdlg 	hints.ai_family = family;
657c32efdd3Sdlg 	hints.ai_socktype = SOCK_STREAM;
658c32efdd3Sdlg 	hints.ai_flags = AI_PASSIVE;
659c32efdd3Sdlg 
660c32efdd3Sdlg 	error = getaddrinfo(addr, port, &hints, &res0);
661c32efdd3Sdlg 	if (error)
662c32efdd3Sdlg 		lerrx(1, "%s/%s: %s", addr, port, gai_strerror(error));
663c32efdd3Sdlg 
664c32efdd3Sdlg 	for (res = res0; res != NULL; res = res->ai_next) {
6654c23dba0Sdlg 		s = socket(res->ai_family, res->ai_socktype | SOCK_NONBLOCK,
6664c23dba0Sdlg 		    res->ai_protocol);
667c32efdd3Sdlg 		if (s == -1) {
668c32efdd3Sdlg 			cause = "socket";
669c32efdd3Sdlg 			continue;
670c32efdd3Sdlg 		}
671c32efdd3Sdlg 
672c32efdd3Sdlg 		if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR,
673c32efdd3Sdlg 		    &on, sizeof(on)) == -1)
674c32efdd3Sdlg 			err(1, "listener setsockopt(SO_REUSEADDR)");
675c32efdd3Sdlg 
676c32efdd3Sdlg 		if (bind(s, res->ai_addr, res->ai_addrlen) == -1) {
677397cfbf5Sderaadt 			int serrno = errno;
678397cfbf5Sderaadt 
679c32efdd3Sdlg 			cause = "bind";
680c32efdd3Sdlg 			close(s);
681c32efdd3Sdlg 			errno = serrno;
682c32efdd3Sdlg 			continue;
683c32efdd3Sdlg 		}
684c32efdd3Sdlg 
685c32efdd3Sdlg 		if (listen(s, 5) == -1)
686c32efdd3Sdlg 			err(1, "listen");
687c32efdd3Sdlg 
688ff28ff65Sdlg 		l = calloc(1, sizeof(*l));
689ff28ff65Sdlg 		if (l == NULL)
690c32efdd3Sdlg 			err(1, "listener ev alloc");
691c32efdd3Sdlg 
692ff28ff65Sdlg 		event_set(&l->ev, s, EV_READ | EV_PERSIST, identd_accept, l);
693ff28ff65Sdlg 		event_add(&l->ev, NULL);
694ff28ff65Sdlg 		evtimer_set(&l->pause, identd_paused, l);
695c32efdd3Sdlg 	}
696ff28ff65Sdlg 	if (l == NULL)
697c32efdd3Sdlg 		err(1, "%s", cause);
698c32efdd3Sdlg 
699c32efdd3Sdlg 	freeaddrinfo(res0);
700c32efdd3Sdlg }
701c32efdd3Sdlg 
702c32efdd3Sdlg void
identd_paused(int fd,short events,void * arg)703ff28ff65Sdlg identd_paused(int fd, short events, void *arg)
704ff28ff65Sdlg {
705ff28ff65Sdlg 	struct identd_listener *l = arg;
706ff28ff65Sdlg 	event_add(&l->ev, NULL);
707ff28ff65Sdlg }
708ff28ff65Sdlg 
709ff28ff65Sdlg void
identd_accept(int fd,short events,void * arg)710c32efdd3Sdlg identd_accept(int fd, short events, void *arg)
711c32efdd3Sdlg {
712ff28ff65Sdlg 	struct identd_listener *l = arg;
713c32efdd3Sdlg 	struct sockaddr_storage ss;
714ff28ff65Sdlg 	struct timeval pause = { 1, 0 };
715c32efdd3Sdlg 	struct ident_client *c = NULL;
716c32efdd3Sdlg 	socklen_t len;
717c32efdd3Sdlg 	int s;
718c32efdd3Sdlg 
719c32efdd3Sdlg 	len = sizeof(ss);
7204c23dba0Sdlg 	s = accept4(fd, sa(&ss), &len, SOCK_NONBLOCK);
721c32efdd3Sdlg 	if (s == -1) {
722c32efdd3Sdlg 		switch (errno) {
723c32efdd3Sdlg 		case EINTR:
724c32efdd3Sdlg 		case EWOULDBLOCK:
725c32efdd3Sdlg 		case ECONNABORTED:
726c32efdd3Sdlg 			return;
727ff28ff65Sdlg 		case EMFILE:
728ff28ff65Sdlg 		case ENFILE:
729ff28ff65Sdlg 			event_del(&l->ev);
730ff28ff65Sdlg 			evtimer_add(&l->pause, &pause);
731ff28ff65Sdlg 			return;
732c32efdd3Sdlg 		default:
733c32efdd3Sdlg 			lerr(1, "accept");
734c32efdd3Sdlg 		}
735c32efdd3Sdlg 	}
736c32efdd3Sdlg 
737c32efdd3Sdlg 	c = calloc(1, sizeof(*c));
738c32efdd3Sdlg 	if (c == NULL) {
739c32efdd3Sdlg 		lwarn("client alloc");
740c32efdd3Sdlg 		close(fd);
741c32efdd3Sdlg 		return;
742c32efdd3Sdlg 	}
743c32efdd3Sdlg 
744c32efdd3Sdlg 	memcpy(&c->client.ss, &ss, len);
745c32efdd3Sdlg 	c->client.len = len;
746c32efdd3Sdlg 	ldebug("client: %s", gethost(&ss));
747c32efdd3Sdlg 
748c32efdd3Sdlg 	/* lookup the local ip it connected to */
749c32efdd3Sdlg 	c->server.len = sizeof(c->server.ss);
750c32efdd3Sdlg 	if (getsockname(s, sa(&c->server.ss), &c->server.len) == -1)
751c32efdd3Sdlg 		lerr(1, "getsockname");
752c32efdd3Sdlg 
753c32efdd3Sdlg 	event_set(&c->ev, s, EV_READ | EV_PERSIST, identd_request, c);
7549a4217c3Sdlg 	event_add(&c->ev, NULL);
7559a4217c3Sdlg 
756270badb5Sdlg 	evtimer_set(&c->tmo, identd_timeout, c);
757270badb5Sdlg 	evtimer_add(&c->tmo, &timeout);
7589a4217c3Sdlg }
7599a4217c3Sdlg 
7609a4217c3Sdlg void
identd_timeout(int fd,short events,void * arg)7619a4217c3Sdlg identd_timeout(int fd, short events, void *arg)
7629a4217c3Sdlg {
7639a4217c3Sdlg 	struct ident_client *c = arg;
7649a4217c3Sdlg 
7659a4217c3Sdlg 	event_del(&c->ev);
7669a4217c3Sdlg 	close(fd);
7679a4217c3Sdlg 	free(c->buf);
7689a4217c3Sdlg 
7699a4217c3Sdlg 	if (c->state == S_QUEUED) /* it is queued for resolving */
7709a4217c3Sdlg 		c->state = S_DEAD;
7719a4217c3Sdlg 	else
7729a4217c3Sdlg 		free(c);
773c32efdd3Sdlg }
774c32efdd3Sdlg 
775c32efdd3Sdlg void
identd_request(int fd,short events,void * arg)776c32efdd3Sdlg identd_request(int fd, short events, void *arg)
777c32efdd3Sdlg {
778c32efdd3Sdlg 	struct ident_client *c = arg;
7794f91e8dcSdlg 	unsigned char buf[64];
780c32efdd3Sdlg 	ssize_t n, i;
7816b9316d1Ssthen 	char *errstr = unknown_err ? "UNKNOWN-ERROR" : "INVALID-PORT";
782c32efdd3Sdlg 
783c32efdd3Sdlg 	n = read(fd, buf, sizeof(buf));
784c32efdd3Sdlg 	switch (n) {
785c32efdd3Sdlg 	case -1:
786c32efdd3Sdlg 		switch (errno) {
787c32efdd3Sdlg 		case EINTR:
788c32efdd3Sdlg 		case EAGAIN:
789c32efdd3Sdlg 			return;
790c32efdd3Sdlg 		default:
791c32efdd3Sdlg 			lwarn("%s read", gethost(&c->client.ss));
792c32efdd3Sdlg 			goto fail;
793c32efdd3Sdlg 		}
794c32efdd3Sdlg 		break;
795c32efdd3Sdlg 
796c32efdd3Sdlg 	case 0:
797c32efdd3Sdlg 		ldebug("%s closed connection", gethost(&c->client.ss));
798c32efdd3Sdlg 		goto fail;
799c32efdd3Sdlg 	default:
800c32efdd3Sdlg 		break;
801c32efdd3Sdlg 	}
802c32efdd3Sdlg 
803acc4509dSdlg 	c->rxbytes += n;
804acc4509dSdlg 	if (c->rxbytes >= INPUT_MAX)
805acc4509dSdlg 		goto fail;
806acc4509dSdlg 
807c32efdd3Sdlg 	for (i = 0; c->state < S_EOL && i < n; i++)
808c32efdd3Sdlg 		c->state = identd_parse(c, buf[i]);
809c32efdd3Sdlg 
810c32efdd3Sdlg 	if (c->state == S_DEAD)
811c32efdd3Sdlg 		goto error;
812c32efdd3Sdlg 	if (c->state != S_EOL)
813c32efdd3Sdlg 		return;
814c32efdd3Sdlg 
815c32efdd3Sdlg 	if (c->server.port < 1 || c->client.port < 1)
816c32efdd3Sdlg 		goto error;
817c32efdd3Sdlg 
818c32efdd3Sdlg 	if (fetchuid(c) == -1) {
8196b9316d1Ssthen 		errstr = unknown_err ? "UNKNOWN-ERROR" : "NO-USER";
820c32efdd3Sdlg 		goto error;
821c32efdd3Sdlg 	}
822c32efdd3Sdlg 
823c32efdd3Sdlg 	SIMPLEQ_INSERT_TAIL(&sc.child.pushing, c, entry);
8249a4217c3Sdlg 	c->state = S_QUEUED;
825c32efdd3Sdlg 
8269a4217c3Sdlg 	event_del(&c->ev);
827c32efdd3Sdlg 	event_set(&c->ev, fd, EV_READ | EV_PERSIST, identd_resolving, c);
8289a4217c3Sdlg 	event_add(&c->ev, NULL);
829c32efdd3Sdlg 
830c32efdd3Sdlg 	event_add(&proc_wr, NULL);
831c32efdd3Sdlg 	return;
832c32efdd3Sdlg 
833c32efdd3Sdlg error:
834181dd5c9Sdlg 	if (identd_error(c, errstr) == -1)
835181dd5c9Sdlg 		goto fail;
836181dd5c9Sdlg 
837181dd5c9Sdlg 	return;
838181dd5c9Sdlg 
839181dd5c9Sdlg fail:
840181dd5c9Sdlg 	identd_close(c);
841181dd5c9Sdlg }
842181dd5c9Sdlg 
843181dd5c9Sdlg int
identd_error(struct ident_client * c,const char * errstr)844181dd5c9Sdlg identd_error(struct ident_client *c, const char *errstr)
845181dd5c9Sdlg {
846181dd5c9Sdlg 	int fd = EVENT_FD(&c->ev);
847181dd5c9Sdlg 	ssize_t n;
848181dd5c9Sdlg 
849c32efdd3Sdlg 	n = asprintf(&c->buf, "%u , %u : ERROR : %s\r\n",
850c32efdd3Sdlg 	    c->server.port, c->client.port, errstr);
851c32efdd3Sdlg 	if (n == -1)
852181dd5c9Sdlg 		return (-1);
853c32efdd3Sdlg 
854c32efdd3Sdlg 	c->buflen = n;
855c32efdd3Sdlg 
856c32efdd3Sdlg 	event_del(&c->ev);
857c32efdd3Sdlg 	event_set(&c->ev, fd, EV_READ | EV_WRITE | EV_PERSIST,
858c32efdd3Sdlg 	    identd_response, c);
8599a4217c3Sdlg 	event_add(&c->ev, NULL);
860c32efdd3Sdlg 
861181dd5c9Sdlg 	return (0);
862181dd5c9Sdlg }
863181dd5c9Sdlg 
864181dd5c9Sdlg void
identd_close(struct ident_client * c)865181dd5c9Sdlg identd_close(struct ident_client *c)
866181dd5c9Sdlg {
867181dd5c9Sdlg 	int fd = EVENT_FD(&c->ev);
868181dd5c9Sdlg 
8699a4217c3Sdlg 	evtimer_del(&c->tmo);
870c32efdd3Sdlg 	event_del(&c->ev);
871c32efdd3Sdlg 	close(fd);
872181dd5c9Sdlg 	free(c->buf);
873c32efdd3Sdlg 	free(c);
874c32efdd3Sdlg }
875c32efdd3Sdlg 
876c32efdd3Sdlg void
identd_resolving(int fd,short events,void * arg)877c32efdd3Sdlg identd_resolving(int fd, short events, void *arg)
878c32efdd3Sdlg {
879c32efdd3Sdlg 	struct ident_client *c = arg;
880c32efdd3Sdlg 	char buf[64];
881c32efdd3Sdlg 	ssize_t n;
882c32efdd3Sdlg 
883c32efdd3Sdlg 	/*
884c32efdd3Sdlg 	 * something happened while we're waiting for the parent to lookup
885c32efdd3Sdlg 	 * the user.
886c32efdd3Sdlg 	 */
887c32efdd3Sdlg 
888c32efdd3Sdlg 	n = read(fd, buf, sizeof(buf));
889c32efdd3Sdlg 	switch (n) {
890c32efdd3Sdlg 	case -1:
891c32efdd3Sdlg 		switch (errno) {
892c32efdd3Sdlg 		case EINTR:
893c32efdd3Sdlg 		case EAGAIN:
894c32efdd3Sdlg 			return;
895c32efdd3Sdlg 		default:
8962807f553Sdlg 			lwarn("resolving read");
8972807f553Sdlg 			break;
898c32efdd3Sdlg 		}
8992807f553Sdlg 		break;
900c32efdd3Sdlg 	case 0:
901c32efdd3Sdlg 		ldebug("%s closed connection during resolving",
902c32efdd3Sdlg 		    gethost(&c->client.ss));
903c32efdd3Sdlg 		break;
9049a4217c3Sdlg 	default:
905acc4509dSdlg 		c->rxbytes += n;
906acc4509dSdlg 		if (c->rxbytes >= INPUT_MAX)
907acc4509dSdlg 			break;
908acc4509dSdlg 
9099a4217c3Sdlg 		/* ignore extra input */
9109a4217c3Sdlg 		return;
911c32efdd3Sdlg 	}
912c32efdd3Sdlg 
9139a4217c3Sdlg 	evtimer_del(&c->tmo);
914c32efdd3Sdlg 	event_del(&c->ev);
915c32efdd3Sdlg 	close(fd);
9169a4217c3Sdlg 	c->state = S_DEAD; /* on the resolving queue */
917c32efdd3Sdlg }
918c32efdd3Sdlg 
919c32efdd3Sdlg enum ident_client_state
identd_parse(struct ident_client * c,int ch)920c32efdd3Sdlg identd_parse(struct ident_client *c, int ch)
921c32efdd3Sdlg {
922c32efdd3Sdlg 	enum ident_client_state s = c->state;
923c32efdd3Sdlg 
924c32efdd3Sdlg 	switch (s) {
925c32efdd3Sdlg 	case S_BEGINNING:
926c32efdd3Sdlg 		/* ignore leading space */
927c32efdd3Sdlg 		if (ch == '\t' || ch == ' ')
928c32efdd3Sdlg 			return (s);
929c32efdd3Sdlg 
930c32efdd3Sdlg 		if (ch == '0' || !isdigit(ch))
931c32efdd3Sdlg 			return (S_DEAD);
932c32efdd3Sdlg 
933c32efdd3Sdlg 		c->server.port = ch - '0';
934c32efdd3Sdlg 		return (S_SERVER_PORT);
935c32efdd3Sdlg 
936c32efdd3Sdlg 	case S_SERVER_PORT:
937c32efdd3Sdlg 		if (ch == '\t' || ch == ' ')
938c32efdd3Sdlg 			return (S_PRE_COMMA);
939c32efdd3Sdlg 		if (ch == ',')
940c32efdd3Sdlg 			return (S_POST_COMMA);
941c32efdd3Sdlg 
942c32efdd3Sdlg 		if (!isdigit(ch))
943c32efdd3Sdlg 			return (S_DEAD);
944c32efdd3Sdlg 
945c32efdd3Sdlg 		c->server.port *= 10;
946c32efdd3Sdlg 		c->server.port += ch - '0';
947c32efdd3Sdlg 		if (c->server.port > 65535)
948c32efdd3Sdlg 			return (S_DEAD);
949c32efdd3Sdlg 
950c32efdd3Sdlg 		return (s);
951c32efdd3Sdlg 
952c32efdd3Sdlg 	case S_PRE_COMMA:
953c32efdd3Sdlg 		if (ch == '\t' || ch == ' ')
954c32efdd3Sdlg 			return (s);
955c32efdd3Sdlg 		if (ch == ',')
956c32efdd3Sdlg 			return (S_POST_COMMA);
957c32efdd3Sdlg 
958c32efdd3Sdlg 		return (S_DEAD);
959c32efdd3Sdlg 
960c32efdd3Sdlg 	case S_POST_COMMA:
961c32efdd3Sdlg 		if (ch == '\t' || ch == ' ')
962c32efdd3Sdlg 			return (s);
963c32efdd3Sdlg 
964c32efdd3Sdlg 		if (ch == '0' || !isdigit(ch))
965c32efdd3Sdlg 			return (S_DEAD);
966c32efdd3Sdlg 
967c32efdd3Sdlg 		c->client.port = ch - '0';
968c32efdd3Sdlg 		return (S_CLIENT_PORT);
969c32efdd3Sdlg 
970c32efdd3Sdlg 	case S_CLIENT_PORT:
971c32efdd3Sdlg 		if (ch == '\t' || ch == ' ')
972c32efdd3Sdlg 			return (S_PRE_EOL);
973c32efdd3Sdlg 		if (ch == '\r' || ch == '\n')
974c32efdd3Sdlg 			return (S_EOL);
975c32efdd3Sdlg 
976c32efdd3Sdlg 		if (!isdigit(ch))
977c32efdd3Sdlg 			return (S_DEAD);
978c32efdd3Sdlg 
979c32efdd3Sdlg 		c->client.port *= 10;
980c32efdd3Sdlg 		c->client.port += ch - '0';
981c32efdd3Sdlg 		if (c->client.port > 65535)
982c32efdd3Sdlg 			return (S_DEAD);
983c32efdd3Sdlg 
984c32efdd3Sdlg 		return (s);
985c32efdd3Sdlg 
986c32efdd3Sdlg 	case S_PRE_EOL:
987c32efdd3Sdlg 		if (ch == '\t' || ch == ' ')
988c32efdd3Sdlg 			return (s);
989c32efdd3Sdlg 		if (ch == '\r' || ch == '\n')
990c32efdd3Sdlg 			return (S_EOL);
991c32efdd3Sdlg 
992c32efdd3Sdlg 		return (S_DEAD);
993c32efdd3Sdlg 
994c32efdd3Sdlg 	case S_EOL:
995c32efdd3Sdlg 		/* ignore trailing garbage */
996c32efdd3Sdlg 		return (s);
997c32efdd3Sdlg 
998c32efdd3Sdlg 	default:
999c32efdd3Sdlg 		return (S_DEAD);
1000c32efdd3Sdlg 	}
1001c32efdd3Sdlg }
1002c32efdd3Sdlg 
1003c32efdd3Sdlg void
identd_response(int fd,short events,void * arg)1004c32efdd3Sdlg identd_response(int fd, short events, void *arg)
1005c32efdd3Sdlg {
1006c32efdd3Sdlg 	struct ident_client *c = arg;
1007c32efdd3Sdlg 	char buf[64];
1008c32efdd3Sdlg 	ssize_t n;
1009c32efdd3Sdlg 
1010c32efdd3Sdlg 	if (events & EV_READ) {
1011c32efdd3Sdlg 		n = read(fd, buf, sizeof(buf));
1012c32efdd3Sdlg 		switch (n) {
1013c32efdd3Sdlg 		case -1:
1014c32efdd3Sdlg 			switch (errno) {
1015c32efdd3Sdlg 			case EINTR:
1016c32efdd3Sdlg 			case EAGAIN:
1017c32efdd3Sdlg 				/* meh, try a write */
1018c32efdd3Sdlg 				break;
1019c32efdd3Sdlg 			default:
10202807f553Sdlg 				lwarn("response read");
10212807f553Sdlg 				goto done;
1022c32efdd3Sdlg 			}
1023c32efdd3Sdlg 			break;
1024c32efdd3Sdlg 		case 0:
1025c32efdd3Sdlg 			ldebug("%s closed connection during response",
1026c32efdd3Sdlg 			    gethost(&c->client.ss));
1027c32efdd3Sdlg 			goto done;
1028c32efdd3Sdlg 		default:
1029acc4509dSdlg 			c->rxbytes += n;
1030acc4509dSdlg 			if (c->rxbytes >= INPUT_MAX)
1031acc4509dSdlg 				goto done;
1032acc4509dSdlg 
10339a4217c3Sdlg 			/* ignore extra input */
1034c32efdd3Sdlg 			break;
1035c32efdd3Sdlg 		}
1036c32efdd3Sdlg 	}
1037c32efdd3Sdlg 
1038c32efdd3Sdlg 	if (!(events & EV_WRITE))
1039c32efdd3Sdlg 		return; /* try again later */
1040c32efdd3Sdlg 
1041c32efdd3Sdlg 	n = write(fd, c->buf + c->bufoff, c->buflen - c->bufoff);
1042c32efdd3Sdlg 	if (n == -1) {
1043c32efdd3Sdlg 		switch (errno) {
10442807f553Sdlg 		case EINTR:
1045c32efdd3Sdlg 		case EAGAIN:
1046c32efdd3Sdlg 			return; /* try again later */
104754ea7419Sdlg 		case EPIPE:
104854ea7419Sdlg 			goto done;
1049c32efdd3Sdlg 		default:
105054ea7419Sdlg 			lwarn("response write");
105154ea7419Sdlg 			goto done;
1052c32efdd3Sdlg 		}
1053c32efdd3Sdlg 	}
1054c32efdd3Sdlg 
1055c32efdd3Sdlg 	c->bufoff += n;
1056c32efdd3Sdlg 	if (c->bufoff != c->buflen)
1057c32efdd3Sdlg 		return; /* try again later */
1058c32efdd3Sdlg 
1059c32efdd3Sdlg done:
1060181dd5c9Sdlg 	identd_close(c);
1061c32efdd3Sdlg }
1062c32efdd3Sdlg 
1063c32efdd3Sdlg void
syslog_vstrerror(int e,int priority,const char * fmt,va_list ap)1064c32efdd3Sdlg syslog_vstrerror(int e, int priority, const char *fmt, va_list ap)
1065c32efdd3Sdlg {
1066c32efdd3Sdlg 	char *s;
1067c32efdd3Sdlg 
1068c32efdd3Sdlg 	if (vasprintf(&s, fmt, ap) == -1) {
1069c32efdd3Sdlg 		syslog(LOG_EMERG, "unable to alloc in syslog_vstrerror");
1070c32efdd3Sdlg 		exit(1);
1071c32efdd3Sdlg 	}
1072c32efdd3Sdlg 	syslog(priority, "%s: %s", s, strerror(e));
1073c32efdd3Sdlg 	free(s);
1074c32efdd3Sdlg }
1075c32efdd3Sdlg 
1076c32efdd3Sdlg void
syslog_err(int ecode,const char * fmt,...)1077c32efdd3Sdlg syslog_err(int ecode, const char *fmt, ...)
1078c32efdd3Sdlg {
1079c32efdd3Sdlg 	va_list ap;
1080c32efdd3Sdlg 
1081c32efdd3Sdlg 	va_start(ap, fmt);
1082a52b97d1Sflorian 	syslog_vstrerror(errno, LOG_CRIT, fmt, ap);
1083c32efdd3Sdlg 	va_end(ap);
1084c32efdd3Sdlg 	exit(ecode);
1085c32efdd3Sdlg }
1086c32efdd3Sdlg 
1087c32efdd3Sdlg void
syslog_errx(int ecode,const char * fmt,...)1088c32efdd3Sdlg syslog_errx(int ecode, const char *fmt, ...)
1089c32efdd3Sdlg {
1090c32efdd3Sdlg 	va_list ap;
1091c32efdd3Sdlg 
1092c32efdd3Sdlg 	va_start(ap, fmt);
1093a52b97d1Sflorian 	vsyslog(LOG_CRIT, fmt, ap);
1094c32efdd3Sdlg 	va_end(ap);
1095c32efdd3Sdlg 	exit(ecode);
1096c32efdd3Sdlg }
1097c32efdd3Sdlg 
1098c32efdd3Sdlg void
syslog_warn(const char * fmt,...)1099c32efdd3Sdlg syslog_warn(const char *fmt, ...)
1100c32efdd3Sdlg {
1101c32efdd3Sdlg 	va_list ap;
1102c32efdd3Sdlg 
1103c32efdd3Sdlg 	va_start(ap, fmt);
1104a52b97d1Sflorian 	syslog_vstrerror(errno, LOG_ERR, fmt, ap);
1105c32efdd3Sdlg 	va_end(ap);
1106c32efdd3Sdlg }
1107c32efdd3Sdlg 
1108c32efdd3Sdlg void
syslog_warnx(const char * fmt,...)1109c32efdd3Sdlg syslog_warnx(const char *fmt, ...)
1110c32efdd3Sdlg {
1111c32efdd3Sdlg 	va_list ap;
1112c32efdd3Sdlg 
1113c32efdd3Sdlg 	va_start(ap, fmt);
1114a52b97d1Sflorian 	vsyslog(LOG_ERR, fmt, ap);
1115c32efdd3Sdlg 	va_end(ap);
1116c32efdd3Sdlg }
1117c32efdd3Sdlg 
1118c32efdd3Sdlg void
syslog_notice(const char * fmt,...)11193f52e118Sdlg syslog_notice(const char *fmt, ...)
1120c32efdd3Sdlg {
1121c32efdd3Sdlg 	va_list ap;
1122c32efdd3Sdlg 
1123c32efdd3Sdlg 	va_start(ap, fmt);
11243f52e118Sdlg 	vsyslog(LOG_NOTICE, fmt, ap);
1125c32efdd3Sdlg 	va_end(ap);
1126c32efdd3Sdlg }
1127c32efdd3Sdlg 
1128c32efdd3Sdlg void
syslog_debug(const char * fmt,...)1129c32efdd3Sdlg syslog_debug(const char *fmt, ...)
1130c32efdd3Sdlg {
1131c32efdd3Sdlg 	va_list ap;
1132c32efdd3Sdlg 
1133c32efdd3Sdlg 	if (!debug)
1134c32efdd3Sdlg 		return;
1135c32efdd3Sdlg 
1136c32efdd3Sdlg 	va_start(ap, fmt);
1137c32efdd3Sdlg 	vsyslog(LOG_DEBUG, fmt, ap);
1138c32efdd3Sdlg 	va_end(ap);
1139c32efdd3Sdlg }
1140c32efdd3Sdlg 
1141c32efdd3Sdlg const char *
gethost(struct sockaddr_storage * ss)1142c32efdd3Sdlg gethost(struct sockaddr_storage *ss)
1143c32efdd3Sdlg {
1144c32efdd3Sdlg 	struct sockaddr *sa = (struct sockaddr *)ss;
1145c32efdd3Sdlg 	static char buf[NI_MAXHOST];
1146c32efdd3Sdlg 
1147c32efdd3Sdlg 	if (getnameinfo(sa, sa->sa_len, buf, sizeof(buf),
1148c32efdd3Sdlg 	    NULL, 0, NI_NUMERICHOST) != 0)
1149c32efdd3Sdlg 		return ("(unknown)");
1150c32efdd3Sdlg 
1151c32efdd3Sdlg 	return (buf);
1152c32efdd3Sdlg }
1153c32efdd3Sdlg 
1154c32efdd3Sdlg const char *
gentoken(void)11553f52e118Sdlg gentoken(void)
11563f52e118Sdlg {
11573f52e118Sdlg 	static char buf[21];
11583f52e118Sdlg 	u_int32_t r;
11593f52e118Sdlg 	int i;
11603f52e118Sdlg 
11613f52e118Sdlg 	buf[0] = 'a' + arc4random_uniform(26);
11623f52e118Sdlg 	for (i = 1; i < sizeof(buf) - 1; i++) {
11633f52e118Sdlg 		r = arc4random_uniform(36);
11643f52e118Sdlg 		buf[i] = (r < 26 ? 'a' : '0' - 26) + r;
11653f52e118Sdlg 	}
11663f52e118Sdlg 	buf[i] = '\0';
11673f52e118Sdlg 
11683f52e118Sdlg 	return (buf);
11693f52e118Sdlg }
11703f52e118Sdlg 
1171c32efdd3Sdlg int
fetchuid(struct ident_client * c)1172c32efdd3Sdlg fetchuid(struct ident_client *c)
1173c32efdd3Sdlg {
1174c32efdd3Sdlg 	struct tcp_ident_mapping tir;
1175c32efdd3Sdlg 	int mib[] = { CTL_NET, PF_INET, IPPROTO_TCP, TCPCTL_IDENT };
1176c32efdd3Sdlg 	struct sockaddr_in *s4;
1177c32efdd3Sdlg 	struct sockaddr_in6 *s6;
1178c32efdd3Sdlg 	int err = 0;
1179c32efdd3Sdlg 	size_t len;
1180c32efdd3Sdlg 
1181c32efdd3Sdlg 	memset(&tir, 0, sizeof(tir));
1182b8347973Sdlg 	memcpy(&tir.faddr, &c->client.ss, sizeof(tir.faddr));
1183b8347973Sdlg 	memcpy(&tir.laddr, &c->server.ss, sizeof(tir.laddr));
1184c32efdd3Sdlg 
1185c32efdd3Sdlg 	switch (c->server.ss.ss_family) {
1186c32efdd3Sdlg 	case AF_INET:
1187c32efdd3Sdlg 		s4 = (struct sockaddr_in *)&tir.faddr;
1188c32efdd3Sdlg 		s4->sin_port = htons(c->client.port);
1189c32efdd3Sdlg 
1190c32efdd3Sdlg 		s4 = (struct sockaddr_in *)&tir.laddr;
1191c32efdd3Sdlg 		s4->sin_port = htons(c->server.port);
1192c32efdd3Sdlg 		break;
1193c32efdd3Sdlg 	case AF_INET6:
1194c32efdd3Sdlg 		s6 = (struct sockaddr_in6 *)&tir.faddr;
1195c32efdd3Sdlg 		s6->sin6_port = htons(c->client.port);
1196c32efdd3Sdlg 
1197c32efdd3Sdlg 		s6 = (struct sockaddr_in6 *)&tir.laddr;
1198c32efdd3Sdlg 		s6->sin6_port = htons(c->server.port);
1199c32efdd3Sdlg 		break;
1200c32efdd3Sdlg 	default:
1201c32efdd3Sdlg 		lerrx(1, "unexpected family %d", c->server.ss.ss_family);
1202c32efdd3Sdlg 	}
1203c32efdd3Sdlg 
1204c32efdd3Sdlg 	len = sizeof(tir);
1205c32efdd3Sdlg 	err = sysctl(mib, sizeof(mib) / sizeof(mib[0]), &tir, &len, NULL, 0);
1206c32efdd3Sdlg 	if (err == -1)
1207c32efdd3Sdlg 		lerr(1, "sysctl");
1208c32efdd3Sdlg 
1209c32efdd3Sdlg 	if (tir.ruid == -1)
1210c32efdd3Sdlg 		return (-1);
1211c32efdd3Sdlg 
1212c32efdd3Sdlg 	c->uid = tir.ruid;
1213c32efdd3Sdlg 	return (0);
1214c32efdd3Sdlg }
1215