1 /* $OpenBSD: identd.c,v 1.19 2013/04/29 06:32:11 jmc Exp $ */
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>
24 #include <sys/sysctl.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>
47 #define IDENTD_USER "_identd"
49 #define DOTNOIDENT ".noident"
52 #define TIMEOUT_MAX 240
53 #define TIMEOUT_DEFAULT 120
56 enum ident_client_state
{
77 struct sockaddr_storage ss
;
80 /* from the request */
83 SIMPLEQ_ENTRY(ident_client
) entry
;
84 enum ident_client_state state
;
95 struct ident_resolver
{
96 SIMPLEQ_ENTRY(ident_resolver
) entry
;
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);
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
= {
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
= {
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))
182 extern char *__progname
;
183 fprintf(stderr
, "usage: %s [-46dehNn] [-l address] [-t timeout]\n",
188 struct timeval timeout
= { TIMEOUT_DEFAULT
, 0 };
194 int (*parent_uprintf
)(struct ident_resolver
*, struct passwd
*) =
197 struct event proc_rd
, proc_wr
;
200 SIMPLEQ_HEAD(, ident_resolver
) replies
;
203 SIMPLEQ_HEAD(, ident_client
) pushing
, popping
;
208 main(int argc
, char *argv
[])
210 extern char *__progname
;
211 const char *errstr
= NULL
;
217 int family
= AF_UNSPEC
;
223 while ((c
= getopt(argc
, argv
, "46dehl:Nnp:t:")) != -1) {
238 parent_uprintf
= parent_token
;
247 parent_uprintf
= parent_uid
;
250 timeout
.tv_sec
= strtonum(optarg
,
251 TIMEOUT_MIN
, TIMEOUT_MAX
, &errstr
);
253 errx(1, "timeout %s is %s", optarg
, errstr
);
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
);
275 err(1, "no %s user", IDENTD_USER
);
277 if (!debug
&& daemon(1, 0) == -1)
287 setproctitle("listener");
294 setproctitle("resolver");
301 openlog(__progname
, LOG_PID
|LOG_NDELAY
, LOG_DAEMON
);
308 if (ioctl(sibling
, FIONBIO
, &on
) == -1)
309 lerr(1, "sibling ioctl(FIONBIO)");
312 SIMPLEQ_INIT(&sc
.parent
.replies
);
314 event_set(&proc_rd
, sibling
, EV_READ
| EV_PERSIST
,
316 event_set(&proc_wr
, sibling
, EV_WRITE
,
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
,
332 event_set(&proc_wr
, sibling
, EV_WRITE
,
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
);
347 parent_rd(int fd
, short events
, void *arg
)
349 struct ident_resolver
*r
;
354 n
= read(fd
, &uid
, sizeof(uid
));
362 lerr(1, "parent read");
366 lerrx(1, "child has gone");
370 lerrx(1, "unexpected %zd data from child", n
);
373 r
= calloc(1, sizeof(*r
));
375 lerr(1, "resolver alloc");
384 parent_noident(r
, pw
);
385 if (r
->error
!= E_NONE
)
389 n
= (*parent_uprintf
)(r
, pw
);
391 r
->error
= E_UNKNOWN
;
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
)
421 rv
= asprintf(&r
->buf
, "%s", token
);
423 lnotice("token %s == uid %u (%s)", token
,
424 (u_int
)pw
->pw_uid
, pw
->pw_name
);
431 parent_noident(struct ident_resolver
*r
, struct passwd
*pw
)
433 char path
[MAXPATHLEN
];
437 rv
= snprintf(path
, sizeof(path
), "%s/%s", pw
->pw_dir
, DOTNOIDENT
);
438 if (rv
== -1 || rv
>= sizeof(path
)) {
439 r
->error
= E_UNKNOWN
;
443 if (stat(path
, &st
) == -1)
450 parent_wr(int fd
, short events
, void *arg
)
452 struct ident_resolver
*r
= SIMPLEQ_FIRST(&sc
.parent
.replies
);
457 iov
[iovcnt
].iov_base
= &r
->error
;
458 iov
[iovcnt
].iov_len
= sizeof(r
->error
);
462 iov
[iovcnt
].iov_base
= r
->buf
;
463 iov
[iovcnt
].iov_len
= r
->buflen
;
467 n
= writev(fd
, iov
, iovcnt
);
469 if (errno
== EAGAIN
) {
470 event_add(&proc_wr
, NULL
);
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
);
488 child_rd(int fd
, short events
, void *arg
)
490 struct ident_client
*c
;
497 n
= read(fd
, &reply
, sizeof(reply
));
505 lerr(1, "child read");
509 lerrx(1, "parent has gone");
514 c
= SIMPLEQ_FIRST(&sc
.child
.popping
);
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
) {
530 switch (reply
.error
) {
532 n
= asprintf(&c
->buf
, "%u , %u : USERID : UNIX : %s\r\n",
533 c
->server
.port
, c
->client
.port
, reply
.buf
);
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");
541 n
= asprintf(&c
->buf
, "%u , %u : ERROR : UNKNOWN-ERROR\r\n",
542 c
->server
.port
, c
->client
.port
);
545 n
= asprintf(&c
->buf
, "%u , %u : ERROR : HIDDEN-USER\r\n",
546 c
->server
.port
, c
->client
.port
);
549 lerrx(1, "unexpected error from parent %u", reply
.error
);
556 fd
= EVENT_FD(&c
->ev
);
558 event_set(&c
->ev
, fd
, EV_READ
| EV_WRITE
| EV_PERSIST
,
560 event_add(&c
->ev
, NULL
);
568 child_wr(int fd
, short events
, void *arg
)
570 struct ident_client
*c
= SIMPLEQ_FIRST(&sc
.child
.pushing
);
571 const char *errstr
= NULL
;
574 n
= write(fd
, &c
->uid
, sizeof(c
->uid
));
579 event_add(&proc_wr
, NULL
);
581 case ENOBUFS
: /* parent has a backlog of requests */
582 errstr
= "UNKNOWN-ERROR";
585 lerr(1, "child write");
591 lerrx(1, "unexpected child write length %zd", n
);
594 SIMPLEQ_REMOVE_HEAD(&sc
.child
.pushing
, entry
);
596 SIMPLEQ_INSERT_TAIL(&sc
.child
.popping
, c
, entry
);
597 else if (identd_error(c
, errstr
) == -1)
600 if (!SIMPLEQ_EMPTY(&sc
.child
.pushing
))
601 event_add(&proc_wr
, NULL
);
605 identd_listen(const char *addr
, const char *port
, int family
)
607 struct identd_listener
*l
= NULL
;
609 struct addrinfo hints
, *res
, *res0
;
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
);
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
);
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) {
642 if (ioctl(s
, FIONBIO
, &on
) == -1)
643 err(1, "listener ioctl(FIONBIO)");
645 if (listen(s
, 5) == -1)
648 l
= calloc(1, sizeof(*l
));
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
);
663 identd_paused(int fd
, short events
, void *arg
)
665 struct identd_listener
*l
= arg
;
666 event_add(&l
->ev
, NULL
);
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
;
680 s
= accept(fd
, sa(&ss
), &len
);
690 evtimer_add(&l
->pause
, &pause
);
697 if (ioctl(s
, FIONBIO
, &on
) == -1)
698 lerr(1, "client ioctl(FIONBIO)");
700 c
= calloc(1, sizeof(*c
));
702 lwarn("client alloc");
707 memcpy(&c
->client
.ss
, &ss
, 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
);
724 identd_timeout(int fd
, short events
, void *arg
)
726 struct ident_client
*c
= arg
;
732 if (c
->state
== S_QUEUED
) /* it is queued for resolving */
739 identd_request(int fd
, short events
, void *arg
)
741 struct ident_client
*c
= arg
;
744 char *errstr
= unknown_err
? "UNKNOWN-ERROR" : "INVALID-PORT";
746 n
= read(fd
, buf
, sizeof(buf
));
754 lwarn("%s read", gethost(&c
->client
.ss
));
760 ldebug("%s closed connection", gethost(&c
->client
.ss
));
767 if (c
->rxbytes
>= INPUT_MAX
)
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
)
775 if (c
->state
!= S_EOL
)
778 if (c
->server
.port
< 1 || c
->client
.port
< 1)
781 if (fetchuid(c
) == -1) {
782 errstr
= unknown_err
? "UNKNOWN-ERROR" : "NO-USER";
786 SIMPLEQ_INSERT_TAIL(&sc
.child
.pushing
, c
, entry
);
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
);
797 if (identd_error(c
, errstr
) == -1)
807 identd_error(struct ident_client
*c
, const char *errstr
)
809 int fd
= EVENT_FD(&c
->ev
);
812 n
= asprintf(&c
->buf
, "%u , %u : ERROR : %s\r\n",
813 c
->server
.port
, c
->client
.port
, errstr
);
820 event_set(&c
->ev
, fd
, EV_READ
| EV_WRITE
| EV_PERSIST
,
822 event_add(&c
->ev
, NULL
);
828 identd_close(struct ident_client
*c
)
830 int fd
= EVENT_FD(&c
->ev
);
832 evtimer_del(&c
->tmo
);
840 identd_resolving(int fd
, short events
, void *arg
)
842 struct ident_client
*c
= arg
;
847 * something happened while we're waiting for the parent to lookup
851 n
= read(fd
, buf
, sizeof(buf
));
859 lerrx(1, "resolving read");
863 ldebug("%s closed connection during resolving",
864 gethost(&c
->client
.ss
));
868 if (c
->rxbytes
>= INPUT_MAX
)
871 /* ignore extra input */
875 evtimer_del(&c
->tmo
);
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
;
888 /* ignore leading space */
889 if (ch
== '\t' || ch
== ' ')
892 if (ch
== '0' || !isdigit(ch
))
895 c
->server
.port
= ch
- '0';
896 return (S_SERVER_PORT
);
899 if (ch
== '\t' || ch
== ' ')
900 return (S_PRE_COMMA
);
902 return (S_POST_COMMA
);
907 c
->server
.port
*= 10;
908 c
->server
.port
+= ch
- '0';
909 if (c
->server
.port
> 65535)
915 if (ch
== '\t' || ch
== ' ')
918 return (S_POST_COMMA
);
923 if (ch
== '\t' || ch
== ' ')
926 if (ch
== '0' || !isdigit(ch
))
929 c
->client
.port
= ch
- '0';
930 return (S_CLIENT_PORT
);
933 if (ch
== '\t' || ch
== ' ')
935 if (ch
== '\r' || ch
== '\n')
941 c
->client
.port
*= 10;
942 c
->client
.port
+= ch
- '0';
943 if (c
->client
.port
> 65535)
949 if (ch
== '\t' || ch
== ' ')
951 if (ch
== '\r' || ch
== '\n')
957 /* ignore trailing garbage */
966 identd_response(int fd
, short events
, void *arg
)
968 struct ident_client
*c
= arg
;
972 if (events
& EV_READ
) {
973 n
= read(fd
, buf
, sizeof(buf
));
979 /* meh, try a write */
982 lerrx(1, "response read");
986 ldebug("%s closed connection during response",
987 gethost(&c
->client
.ss
));
991 if (c
->rxbytes
>= INPUT_MAX
)
994 /* ignore extra input */
999 if (!(events
& EV_WRITE
))
1000 return; /* try again later */
1002 n
= write(fd
, c
->buf
+ c
->bufoff
, c
->buflen
- c
->bufoff
);
1006 return; /* try again later */
1008 lerr(1, "response write");
1013 if (c
->bufoff
!= c
->buflen
)
1014 return; /* try again later */
1021 syslog_vstrerror(int e
, int priority
, const char *fmt
, va_list ap
)
1025 if (vasprintf(&s
, fmt
, ap
) == -1) {
1026 syslog(LOG_EMERG
, "unable to alloc in syslog_vstrerror");
1029 syslog(priority
, "%s: %s", s
, strerror(e
));
1034 syslog_err(int ecode
, const char *fmt
, ...)
1039 syslog_vstrerror(errno
, LOG_EMERG
, fmt
, ap
);
1045 syslog_errx(int ecode
, const char *fmt
, ...)
1050 vsyslog(LOG_WARNING
, fmt
, ap
);
1056 syslog_warn(const char *fmt
, ...)
1061 syslog_vstrerror(errno
, LOG_WARNING
, fmt
, ap
);
1066 syslog_warnx(const char *fmt
, ...)
1071 vsyslog(LOG_WARNING
, fmt
, ap
);
1076 syslog_notice(const char *fmt
, ...)
1081 vsyslog(LOG_NOTICE
, fmt
, ap
);
1086 syslog_debug(const char *fmt
, ...)
1094 vsyslog(LOG_DEBUG
, fmt
, ap
);
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)");
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)");
1127 static char buf
[21];
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
;
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
;
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
) {
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
);
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
);
1171 lerrx(1, "unexpected family %d", c
->server
.ss
.ss_family
);
1175 err
= sysctl(mib
, sizeof(mib
) / sizeof(mib
[0]), &tir
, &len
, NULL
, 0);