sync
[bitrig.git] / usr.sbin / identd / identd.c
blob64bae013d5bbc916e634e07b67d735bd4ac3c9ab
1 /* $OpenBSD: identd.c,v 1.19 2013/04/29 06:32:11 jmc Exp $ */
3 /*
4 * Copyright (c) 2013 David Gwynne <dlg@openbsd.org>
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.
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.
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>
27 #include <netinet/in.h>
28 #include <netinet/ip_var.h>
29 #include <netinet/tcp.h>
30 #include <netinet/tcp_timer.h>
31 #include <netinet/tcp_var.h>
33 #include <netdb.h>
35 #include <err.h>
36 #include <ctype.h>
37 #include <errno.h>
38 #include <event.h>
39 #include <fcntl.h>
40 #include <pwd.h>
41 #include <stdio.h>
42 #include <stdlib.h>
43 #include <string.h>
44 #include <syslog.h>
45 #include <unistd.h>
47 #define IDENTD_USER "_identd"
49 #define DOTNOIDENT ".noident"
51 #define TIMEOUT_MIN 4
52 #define TIMEOUT_MAX 240
53 #define TIMEOUT_DEFAULT 120
54 #define INPUT_MAX 256
56 enum ident_client_state {
57 S_BEGINNING = 0,
58 S_SERVER_PORT,
59 S_PRE_COMMA,
60 S_POST_COMMA,
61 S_CLIENT_PORT,
62 S_PRE_EOL,
63 S_EOL,
65 S_DEAD,
66 S_QUEUED
69 #define E_NONE 0
70 #define E_NOUSER 1
71 #define E_UNKNOWN 2
72 #define E_HIDDEN 3
74 struct ident_client {
75 struct {
76 /* from the socket */
77 struct sockaddr_storage ss;
78 socklen_t len;
80 /* from the request */
81 u_int port;
82 } client, server;
83 SIMPLEQ_ENTRY(ident_client) entry;
84 enum ident_client_state state;
85 struct event ev;
86 struct event tmo;
87 size_t rxbytes;
89 char *buf;
90 size_t buflen;
91 size_t bufoff;
92 uid_t uid;
95 struct ident_resolver {
96 SIMPLEQ_ENTRY(ident_resolver) entry;
97 char *buf;
98 size_t buflen;
99 u_int error;
102 struct identd_listener {
103 struct event ev, pause;
106 void parent_rd(int, short, void *);
107 void parent_wr(int, short, void *);
108 int parent_username(struct ident_resolver *, struct passwd *);
109 int parent_uid(struct ident_resolver *, struct passwd *);
110 int parent_token(struct ident_resolver *, struct passwd *);
111 void parent_noident(struct ident_resolver *, struct passwd *);
113 void child_rd(int, short, void *);
114 void child_wr(int, short, void *);
116 void identd_listen(const char *, const char *, int);
117 void identd_paused(int, short, void *);
118 void identd_accept(int, short, void *);
119 int identd_error(struct ident_client *, const char *);
120 void identd_close(struct ident_client *);
121 void identd_timeout(int, short, void *);
122 void identd_request(int, short, void *);
123 enum ident_client_state
124 identd_parse(struct ident_client *, int);
125 void identd_resolving(int, short, void *);
126 void identd_response(int, short, void *);
127 int fetchuid(struct ident_client *);
129 const char *gethost(struct sockaddr_storage *);
130 const char *getport(struct sockaddr_storage *);
131 const char *gentoken(void);
133 struct loggers {
134 void (*err)(int, const char *, ...);
135 void (*errx)(int, const char *, ...);
136 void (*warn)(const char *, ...);
137 void (*warnx)(const char *, ...);
138 void (*notice)(const char *, ...);
139 void (*debug)(const char *, ...);
142 const struct loggers conslogger = {
143 err,
144 errx,
145 warn,
146 warnx,
147 warnx, /* notice */
148 warnx /* debug */
151 void syslog_err(int, const char *, ...);
152 void syslog_errx(int, const char *, ...);
153 void syslog_warn(const char *, ...);
154 void syslog_warnx(const char *, ...);
155 void syslog_notice(const char *, ...);
156 void syslog_debug(const char *, ...);
157 void syslog_vstrerror(int, int, const char *, va_list);
159 const struct loggers syslogger = {
160 syslog_err,
161 syslog_errx,
162 syslog_warn,
163 syslog_warnx,
164 syslog_notice,
165 syslog_debug
168 const struct loggers *logger = &conslogger;
170 #define lerr(_e, _f...) logger->err((_e), _f)
171 #define lerrx(_e, _f...) logger->errx((_e), _f)
172 #define lwarn(_f...) logger->warn(_f)
173 #define lwarnx(_f...) logger->warnx(_f)
174 #define lnotice(_f...) logger->notice(_f)
175 #define ldebug(_f...) logger->debug(_f)
177 #define sa(_ss) ((struct sockaddr *)(_ss))
179 __dead void
180 usage(void)
182 extern char *__progname;
183 fprintf(stderr, "usage: %s [-46dehNn] [-l address] [-t timeout]\n",
184 __progname);
185 exit(1);
188 struct timeval timeout = { TIMEOUT_DEFAULT, 0 };
189 int debug = 0;
190 int noident = 0;
191 int on = 1;
192 int unknown_err = 0;
194 int (*parent_uprintf)(struct ident_resolver *, struct passwd *) =
195 parent_username;
197 struct event proc_rd, proc_wr;
198 union {
199 struct {
200 SIMPLEQ_HEAD(, ident_resolver) replies;
201 } parent;
202 struct {
203 SIMPLEQ_HEAD(, ident_client) pushing, popping;
204 } child;
205 } sc;
208 main(int argc, char *argv[])
210 extern char *__progname;
211 const char *errstr = NULL;
213 int c;
214 struct passwd *pw;
216 char *addr = NULL;
217 int family = AF_UNSPEC;
219 int pair[2];
220 pid_t parent;
221 int sibling;
223 while ((c = getopt(argc, argv, "46dehl:Nnp:t:")) != -1) {
224 switch (c) {
225 case '4':
226 family = AF_INET;
227 break;
228 case '6':
229 family = AF_INET6;
230 break;
231 case 'd':
232 debug = 1;
233 break;
234 case 'e':
235 unknown_err = 1;
236 break;
237 case 'h':
238 parent_uprintf = parent_token;
239 break;
240 case 'l':
241 addr = optarg;
242 break;
243 case 'N':
244 noident = 1;
245 break;
246 case 'n':
247 parent_uprintf = parent_uid;
248 break;
249 case 't':
250 timeout.tv_sec = strtonum(optarg,
251 TIMEOUT_MIN, TIMEOUT_MAX, &errstr);
252 if (errstr != NULL)
253 errx(1, "timeout %s is %s", optarg, errstr);
254 break;
255 default:
256 usage();
257 /* NOTREACHED */
261 argc -= optind;
262 argv += optind;
264 if (argc != 0)
265 usage();
267 if (geteuid() != 0)
268 errx(1, "need root privileges");
270 if (socketpair(AF_UNIX, SOCK_SEQPACKET, PF_UNSPEC, pair) == -1)
271 err(1, "socketpair");
273 pw = getpwnam(IDENTD_USER);
274 if (pw == NULL)
275 err(1, "no %s user", IDENTD_USER);
277 if (!debug && daemon(1, 0) == -1)
278 err(1, "daemon");
280 parent = fork();
281 switch (parent) {
282 case -1:
283 lerr(1, "fork");
285 case 0:
286 /* child */
287 setproctitle("listener");
288 close(pair[1]);
289 sibling = pair[0];
290 break;
292 default:
293 /* parent */
294 setproctitle("resolver");
295 close(pair[0]);
296 sibling = pair[1];
297 break;
300 if (!debug) {
301 openlog(__progname, LOG_PID|LOG_NDELAY, LOG_DAEMON);
302 tzset();
303 logger = &syslogger;
306 event_init();
308 if (ioctl(sibling, FIONBIO, &on) == -1)
309 lerr(1, "sibling ioctl(FIONBIO)");
311 if (parent) {
312 SIMPLEQ_INIT(&sc.parent.replies);
314 event_set(&proc_rd, sibling, EV_READ | EV_PERSIST,
315 parent_rd, NULL);
316 event_set(&proc_wr, sibling, EV_WRITE,
317 parent_wr, NULL);
318 } else {
319 SIMPLEQ_INIT(&sc.child.pushing);
320 SIMPLEQ_INIT(&sc.child.popping);
322 identd_listen(addr, "auth", family);
324 if (chroot(pw->pw_dir) == -1)
325 lerr(1, "chroot(%s)", pw->pw_dir);
327 if (chdir("/") == -1)
328 lerr(1, "chdir(%s)", pw->pw_dir);
330 event_set(&proc_rd, sibling, EV_READ | EV_PERSIST,
331 child_rd, NULL);
332 event_set(&proc_wr, sibling, EV_WRITE,
333 child_wr, NULL);
336 if (setgroups(1, &pw->pw_gid) ||
337 setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) ||
338 setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid))
339 lerr(1, "unable to revoke privs");
341 event_add(&proc_rd, NULL);
342 event_dispatch();
343 return (0);
346 void
347 parent_rd(int fd, short events, void *arg)
349 struct ident_resolver *r;
350 struct passwd *pw;
351 ssize_t n;
352 uid_t uid;
354 n = read(fd, &uid, sizeof(uid));
355 switch (n) {
356 case -1:
357 switch (errno) {
358 case EAGAIN:
359 case EINTR:
360 return;
361 default:
362 lerr(1, "parent read");
364 break;
365 case 0:
366 lerrx(1, "child has gone");
367 case sizeof(uid):
368 break;
369 default:
370 lerrx(1, "unexpected %zd data from child", n);
373 r = calloc(1, sizeof(*r));
374 if (r == NULL)
375 lerr(1, "resolver alloc");
377 pw = getpwuid(uid);
378 if (pw == NULL) {
379 r->error = E_NOUSER;
380 goto done;
383 if (noident) {
384 parent_noident(r, pw);
385 if (r->error != E_NONE)
386 goto done;
389 n = (*parent_uprintf)(r, pw);
390 if (n == -1) {
391 r->error = E_UNKNOWN;
392 goto done;
395 r->buflen = n;
397 done:
398 SIMPLEQ_INSERT_TAIL(&sc.parent.replies, r, entry);
399 event_add(&proc_wr, NULL);
403 parent_username(struct ident_resolver *r, struct passwd *pw)
405 return (asprintf(&r->buf, "%s", pw->pw_name));
409 parent_uid(struct ident_resolver *r, struct passwd *pw)
411 return (asprintf(&r->buf, "%u", (u_int)pw->pw_uid));
415 parent_token(struct ident_resolver *r, struct passwd *pw)
417 const char *token;
418 int rv;
420 token = gentoken();
421 rv = asprintf(&r->buf, "%s", token);
422 if (rv != -1) {
423 lnotice("token %s == uid %u (%s)", token,
424 (u_int)pw->pw_uid, pw->pw_name);
427 return (rv);
430 void
431 parent_noident(struct ident_resolver *r, struct passwd *pw)
433 char path[MAXPATHLEN];
434 struct stat st;
435 int rv;
437 rv = snprintf(path, sizeof(path), "%s/%s", pw->pw_dir, DOTNOIDENT);
438 if (rv == -1 || rv >= sizeof(path)) {
439 r->error = E_UNKNOWN;
440 return;
443 if (stat(path, &st) == -1)
444 return;
446 r->error = E_HIDDEN;
449 void
450 parent_wr(int fd, short events, void *arg)
452 struct ident_resolver *r = SIMPLEQ_FIRST(&sc.parent.replies);
453 struct iovec iov[2];
454 int iovcnt = 0;
455 ssize_t n;
457 iov[iovcnt].iov_base = &r->error;
458 iov[iovcnt].iov_len = sizeof(r->error);
459 iovcnt++;
461 if (r->buflen > 0) {
462 iov[iovcnt].iov_base = r->buf;
463 iov[iovcnt].iov_len = r->buflen;
464 iovcnt++;
467 n = writev(fd, iov, iovcnt);
468 if (n == -1) {
469 if (errno == EAGAIN) {
470 event_add(&proc_wr, NULL);
471 return;
473 lerr(1, "parent write");
476 if (n != sizeof(r->error) + r->buflen)
477 lerrx(1, "unexpected parent write length %zd", n);
479 SIMPLEQ_REMOVE_HEAD(&sc.parent.replies, entry);
481 if (r->buflen > 0)
482 free(r->buf);
484 free(r);
487 void
488 child_rd(int fd, short events, void *arg)
490 struct ident_client *c;
491 struct {
492 u_int error;
493 char buf[512];
494 } reply;
495 ssize_t n;
497 n = read(fd, &reply, sizeof(reply));
498 switch (n) {
499 case -1:
500 switch (errno) {
501 case EAGAIN:
502 case EINTR:
503 return;
504 default:
505 lerr(1, "child read");
507 break;
508 case 0:
509 lerrx(1, "parent has gone");
510 default:
511 break;
514 c = SIMPLEQ_FIRST(&sc.child.popping);
515 if (c == NULL)
516 lerrx(1, "unsolicited data from parent");
518 SIMPLEQ_REMOVE_HEAD(&sc.child.popping, entry);
520 if (n < sizeof(reply.error))
521 lerrx(1, "short data from parent");
523 /* check if something went wrong while the parent was working */
524 if (c->state == S_DEAD) {
525 free(c);
526 return;
528 c->state = S_DEAD;
530 switch (reply.error) {
531 case E_NONE:
532 n = asprintf(&c->buf, "%u , %u : USERID : UNIX : %s\r\n",
533 c->server.port, c->client.port, reply.buf);
534 break;
535 case E_NOUSER:
536 n = asprintf(&c->buf, "%u , %u : ERROR : %s\r\n",
537 c->server.port, c->client.port,
538 unknown_err ? "UNKNOWN-ERROR" : "NO-USER");
539 break;
540 case E_UNKNOWN:
541 n = asprintf(&c->buf, "%u , %u : ERROR : UNKNOWN-ERROR\r\n",
542 c->server.port, c->client.port);
543 break;
544 case E_HIDDEN:
545 n = asprintf(&c->buf, "%u , %u : ERROR : HIDDEN-USER\r\n",
546 c->server.port, c->client.port);
547 break;
548 default:
549 lerrx(1, "unexpected error from parent %u", reply.error);
551 if (n == -1)
552 goto fail;
554 c->buflen = n;
556 fd = EVENT_FD(&c->ev);
557 event_del(&c->ev);
558 event_set(&c->ev, fd, EV_READ | EV_WRITE | EV_PERSIST,
559 identd_response, c);
560 event_add(&c->ev, NULL);
561 return;
563 fail:
564 identd_close(c);
567 void
568 child_wr(int fd, short events, void *arg)
570 struct ident_client *c = SIMPLEQ_FIRST(&sc.child.pushing);
571 const char *errstr = NULL;
572 ssize_t n;
574 n = write(fd, &c->uid, sizeof(c->uid));
575 switch (n) {
576 case -1:
577 switch (errno) {
578 case EAGAIN:
579 event_add(&proc_wr, NULL);
580 return;
581 case ENOBUFS: /* parent has a backlog of requests */
582 errstr = "UNKNOWN-ERROR";
583 break;
584 default:
585 lerr(1, "child write");
587 break;
588 case sizeof(c->uid):
589 break;
590 default:
591 lerrx(1, "unexpected child write length %zd", n);
594 SIMPLEQ_REMOVE_HEAD(&sc.child.pushing, entry);
595 if (errstr == NULL)
596 SIMPLEQ_INSERT_TAIL(&sc.child.popping, c, entry);
597 else if (identd_error(c, errstr) == -1)
598 identd_close(c);
600 if (!SIMPLEQ_EMPTY(&sc.child.pushing))
601 event_add(&proc_wr, NULL);
604 void
605 identd_listen(const char *addr, const char *port, int family)
607 struct identd_listener *l = NULL;
609 struct addrinfo hints, *res, *res0;
610 int error, s;
611 const char *cause = NULL;
613 memset(&hints, 0, sizeof(hints));
614 hints.ai_family = family;
615 hints.ai_socktype = SOCK_STREAM;
616 hints.ai_flags = AI_PASSIVE;
618 error = getaddrinfo(addr, port, &hints, &res0);
619 if (error)
620 lerrx(1, "%s/%s: %s", addr, port, gai_strerror(error));
622 for (res = res0; res != NULL; res = res->ai_next) {
623 s = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
624 if (s == -1) {
625 cause = "socket";
626 continue;
629 if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR,
630 &on, sizeof(on)) == -1)
631 err(1, "listener setsockopt(SO_REUSEADDR)");
633 if (bind(s, res->ai_addr, res->ai_addrlen) == -1) {
634 int serrno = errno;
636 cause = "bind";
637 close(s);
638 errno = serrno;
639 continue;
642 if (ioctl(s, FIONBIO, &on) == -1)
643 err(1, "listener ioctl(FIONBIO)");
645 if (listen(s, 5) == -1)
646 err(1, "listen");
648 l = calloc(1, sizeof(*l));
649 if (l == NULL)
650 err(1, "listener ev alloc");
652 event_set(&l->ev, s, EV_READ | EV_PERSIST, identd_accept, l);
653 event_add(&l->ev, NULL);
654 evtimer_set(&l->pause, identd_paused, l);
656 if (l == NULL)
657 err(1, "%s", cause);
659 freeaddrinfo(res0);
662 void
663 identd_paused(int fd, short events, void *arg)
665 struct identd_listener *l = arg;
666 event_add(&l->ev, NULL);
669 void
670 identd_accept(int fd, short events, void *arg)
672 struct identd_listener *l = arg;
673 struct sockaddr_storage ss;
674 struct timeval pause = { 1, 0 };
675 struct ident_client *c = NULL;
676 socklen_t len;
677 int s;
679 len = sizeof(ss);
680 s = accept(fd, sa(&ss), &len);
681 if (s == -1) {
682 switch (errno) {
683 case EINTR:
684 case EWOULDBLOCK:
685 case ECONNABORTED:
686 return;
687 case EMFILE:
688 case ENFILE:
689 event_del(&l->ev);
690 evtimer_add(&l->pause, &pause);
691 return;
692 default:
693 lerr(1, "accept");
697 if (ioctl(s, FIONBIO, &on) == -1)
698 lerr(1, "client ioctl(FIONBIO)");
700 c = calloc(1, sizeof(*c));
701 if (c == NULL) {
702 lwarn("client alloc");
703 close(fd);
704 return;
707 memcpy(&c->client.ss, &ss, len);
708 c->client.len = len;
709 ldebug("client: %s", gethost(&ss));
711 /* lookup the local ip it connected to */
712 c->server.len = sizeof(c->server.ss);
713 if (getsockname(s, sa(&c->server.ss), &c->server.len) == -1)
714 lerr(1, "getsockname");
716 event_set(&c->ev, s, EV_READ | EV_PERSIST, identd_request, c);
717 event_add(&c->ev, NULL);
719 event_set(&c->tmo, s, 0, identd_timeout, c);
720 event_add(&c->tmo, &timeout);
723 void
724 identd_timeout(int fd, short events, void *arg)
726 struct ident_client *c = arg;
728 event_del(&c->ev);
729 close(fd);
730 free(c->buf);
732 if (c->state == S_QUEUED) /* it is queued for resolving */
733 c->state = S_DEAD;
734 else
735 free(c);
738 void
739 identd_request(int fd, short events, void *arg)
741 struct ident_client *c = arg;
742 char buf[64];
743 ssize_t n, i;
744 char *errstr = unknown_err ? "UNKNOWN-ERROR" : "INVALID-PORT";
746 n = read(fd, buf, sizeof(buf));
747 switch (n) {
748 case -1:
749 switch (errno) {
750 case EINTR:
751 case EAGAIN:
752 return;
753 default:
754 lwarn("%s read", gethost(&c->client.ss));
755 goto fail;
757 break;
759 case 0:
760 ldebug("%s closed connection", gethost(&c->client.ss));
761 goto fail;
762 default:
763 break;
766 c->rxbytes += n;
767 if (c->rxbytes >= INPUT_MAX)
768 goto fail;
770 for (i = 0; c->state < S_EOL && i < n; i++)
771 c->state = identd_parse(c, buf[i]);
773 if (c->state == S_DEAD)
774 goto error;
775 if (c->state != S_EOL)
776 return;
778 if (c->server.port < 1 || c->client.port < 1)
779 goto error;
781 if (fetchuid(c) == -1) {
782 errstr = unknown_err ? "UNKNOWN-ERROR" : "NO-USER";
783 goto error;
786 SIMPLEQ_INSERT_TAIL(&sc.child.pushing, c, entry);
787 c->state = S_QUEUED;
789 event_del(&c->ev);
790 event_set(&c->ev, fd, EV_READ | EV_PERSIST, identd_resolving, c);
791 event_add(&c->ev, NULL);
793 event_add(&proc_wr, NULL);
794 return;
796 error:
797 if (identd_error(c, errstr) == -1)
798 goto fail;
800 return;
802 fail:
803 identd_close(c);
807 identd_error(struct ident_client *c, const char *errstr)
809 int fd = EVENT_FD(&c->ev);
810 ssize_t n;
812 n = asprintf(&c->buf, "%u , %u : ERROR : %s\r\n",
813 c->server.port, c->client.port, errstr);
814 if (n == -1)
815 return (-1);
817 c->buflen = n;
819 event_del(&c->ev);
820 event_set(&c->ev, fd, EV_READ | EV_WRITE | EV_PERSIST,
821 identd_response, c);
822 event_add(&c->ev, NULL);
824 return (0);
827 void
828 identd_close(struct ident_client *c)
830 int fd = EVENT_FD(&c->ev);
832 evtimer_del(&c->tmo);
833 event_del(&c->ev);
834 close(fd);
835 free(c->buf);
836 free(c);
839 void
840 identd_resolving(int fd, short events, void *arg)
842 struct ident_client *c = arg;
843 char buf[64];
844 ssize_t n;
847 * something happened while we're waiting for the parent to lookup
848 * the user.
851 n = read(fd, buf, sizeof(buf));
852 switch (n) {
853 case -1:
854 switch (errno) {
855 case EINTR:
856 case EAGAIN:
857 return;
858 default:
859 lerrx(1, "resolving read");
861 /* NOTREACHED */
862 case 0:
863 ldebug("%s closed connection during resolving",
864 gethost(&c->client.ss));
865 break;
866 default:
867 c->rxbytes += n;
868 if (c->rxbytes >= INPUT_MAX)
869 break;
871 /* ignore extra input */
872 return;
875 evtimer_del(&c->tmo);
876 event_del(&c->ev);
877 close(fd);
878 c->state = S_DEAD; /* on the resolving queue */
881 enum ident_client_state
882 identd_parse(struct ident_client *c, int ch)
884 enum ident_client_state s = c->state;
886 switch (s) {
887 case S_BEGINNING:
888 /* ignore leading space */
889 if (ch == '\t' || ch == ' ')
890 return (s);
892 if (ch == '0' || !isdigit(ch))
893 return (S_DEAD);
895 c->server.port = ch - '0';
896 return (S_SERVER_PORT);
898 case S_SERVER_PORT:
899 if (ch == '\t' || ch == ' ')
900 return (S_PRE_COMMA);
901 if (ch == ',')
902 return (S_POST_COMMA);
904 if (!isdigit(ch))
905 return (S_DEAD);
907 c->server.port *= 10;
908 c->server.port += ch - '0';
909 if (c->server.port > 65535)
910 return (S_DEAD);
912 return (s);
914 case S_PRE_COMMA:
915 if (ch == '\t' || ch == ' ')
916 return (s);
917 if (ch == ',')
918 return (S_POST_COMMA);
920 return (S_DEAD);
922 case S_POST_COMMA:
923 if (ch == '\t' || ch == ' ')
924 return (s);
926 if (ch == '0' || !isdigit(ch))
927 return (S_DEAD);
929 c->client.port = ch - '0';
930 return (S_CLIENT_PORT);
932 case S_CLIENT_PORT:
933 if (ch == '\t' || ch == ' ')
934 return (S_PRE_EOL);
935 if (ch == '\r' || ch == '\n')
936 return (S_EOL);
938 if (!isdigit(ch))
939 return (S_DEAD);
941 c->client.port *= 10;
942 c->client.port += ch - '0';
943 if (c->client.port > 65535)
944 return (S_DEAD);
946 return (s);
948 case S_PRE_EOL:
949 if (ch == '\t' || ch == ' ')
950 return (s);
951 if (ch == '\r' || ch == '\n')
952 return (S_EOL);
954 return (S_DEAD);
956 case S_EOL:
957 /* ignore trailing garbage */
958 return (s);
960 default:
961 return (S_DEAD);
965 void
966 identd_response(int fd, short events, void *arg)
968 struct ident_client *c = arg;
969 char buf[64];
970 ssize_t n;
972 if (events & EV_READ) {
973 n = read(fd, buf, sizeof(buf));
974 switch (n) {
975 case -1:
976 switch (errno) {
977 case EINTR:
978 case EAGAIN:
979 /* meh, try a write */
980 break;
981 default:
982 lerrx(1, "response read");
984 break;
985 case 0:
986 ldebug("%s closed connection during response",
987 gethost(&c->client.ss));
988 goto done;
989 default:
990 c->rxbytes += n;
991 if (c->rxbytes >= INPUT_MAX)
992 goto done;
994 /* ignore extra input */
995 break;
999 if (!(events & EV_WRITE))
1000 return; /* try again later */
1002 n = write(fd, c->buf + c->bufoff, c->buflen - c->bufoff);
1003 if (n == -1) {
1004 switch (errno) {
1005 case EAGAIN:
1006 return; /* try again later */
1007 default:
1008 lerr(1, "response write");
1012 c->bufoff += n;
1013 if (c->bufoff != c->buflen)
1014 return; /* try again later */
1016 done:
1017 identd_close(c);
1020 void
1021 syslog_vstrerror(int e, int priority, const char *fmt, va_list ap)
1023 char *s;
1025 if (vasprintf(&s, fmt, ap) == -1) {
1026 syslog(LOG_EMERG, "unable to alloc in syslog_vstrerror");
1027 exit(1);
1029 syslog(priority, "%s: %s", s, strerror(e));
1030 free(s);
1033 void
1034 syslog_err(int ecode, const char *fmt, ...)
1036 va_list ap;
1038 va_start(ap, fmt);
1039 syslog_vstrerror(errno, LOG_EMERG, fmt, ap);
1040 va_end(ap);
1041 exit(ecode);
1044 void
1045 syslog_errx(int ecode, const char *fmt, ...)
1047 va_list ap;
1049 va_start(ap, fmt);
1050 vsyslog(LOG_WARNING, fmt, ap);
1051 va_end(ap);
1052 exit(ecode);
1055 void
1056 syslog_warn(const char *fmt, ...)
1058 va_list ap;
1060 va_start(ap, fmt);
1061 syslog_vstrerror(errno, LOG_WARNING, fmt, ap);
1062 va_end(ap);
1065 void
1066 syslog_warnx(const char *fmt, ...)
1068 va_list ap;
1070 va_start(ap, fmt);
1071 vsyslog(LOG_WARNING, fmt, ap);
1072 va_end(ap);
1075 void
1076 syslog_notice(const char *fmt, ...)
1078 va_list ap;
1080 va_start(ap, fmt);
1081 vsyslog(LOG_NOTICE, fmt, ap);
1082 va_end(ap);
1085 void
1086 syslog_debug(const char *fmt, ...)
1088 va_list ap;
1090 if (!debug)
1091 return;
1093 va_start(ap, fmt);
1094 vsyslog(LOG_DEBUG, fmt, ap);
1095 va_end(ap);
1098 const char *
1099 gethost(struct sockaddr_storage *ss)
1101 struct sockaddr *sa = (struct sockaddr *)ss;
1102 static char buf[NI_MAXHOST];
1104 if (getnameinfo(sa, sa->sa_len, buf, sizeof(buf),
1105 NULL, 0, NI_NUMERICHOST) != 0)
1106 return ("(unknown)");
1108 return (buf);
1111 const char *
1112 getport(struct sockaddr_storage *ss)
1114 struct sockaddr *sa = (struct sockaddr *)ss;
1115 static char buf[NI_MAXSERV];
1117 if (getnameinfo(sa, sa->sa_len, NULL, 0, buf, sizeof(buf),
1118 NI_NUMERICSERV) != 0)
1119 return ("(unknown)");
1121 return (buf);
1124 const char *
1125 gentoken(void)
1127 static char buf[21];
1128 u_int32_t r;
1129 int i;
1131 buf[0] = 'a' + arc4random_uniform(26);
1132 for (i = 1; i < sizeof(buf) - 1; i++) {
1133 r = arc4random_uniform(36);
1134 buf[i] = (r < 26 ? 'a' : '0' - 26) + r;
1136 buf[i] = '\0';
1138 return (buf);
1142 fetchuid(struct ident_client *c)
1144 struct tcp_ident_mapping tir;
1145 int mib[] = { CTL_NET, PF_INET, IPPROTO_TCP, TCPCTL_IDENT };
1146 struct sockaddr_in *s4;
1147 struct sockaddr_in6 *s6;
1148 int err = 0;
1149 size_t len;
1151 memset(&tir, 0, sizeof(tir));
1152 memcpy(&tir.faddr, &c->client.ss, sizeof(tir.faddr));
1153 memcpy(&tir.laddr, &c->server.ss, sizeof(tir.laddr));
1155 switch (c->server.ss.ss_family) {
1156 case AF_INET:
1157 s4 = (struct sockaddr_in *)&tir.faddr;
1158 s4->sin_port = htons(c->client.port);
1160 s4 = (struct sockaddr_in *)&tir.laddr;
1161 s4->sin_port = htons(c->server.port);
1162 break;
1163 case AF_INET6:
1164 s6 = (struct sockaddr_in6 *)&tir.faddr;
1165 s6->sin6_port = htons(c->client.port);
1167 s6 = (struct sockaddr_in6 *)&tir.laddr;
1168 s6->sin6_port = htons(c->server.port);
1169 break;
1170 default:
1171 lerrx(1, "unexpected family %d", c->server.ss.ss_family);
1174 len = sizeof(tir);
1175 err = sysctl(mib, sizeof(mib) / sizeof(mib[0]), &tir, &len, NULL, 0);
1176 if (err == -1)
1177 lerr(1, "sysctl");
1179 if (tir.ruid == -1)
1180 return (-1);
1182 c->uid = tir.ruid;
1183 return (0);