identd.c revision 1.21 1 /* $NetBSD: identd.c,v 1.21 2004/01/31 22:03:31 christos Exp $ */
2
3 /*
4 * identd.c - TCP/IP Ident protocol server.
5 *
6 * This software is in the public domain.
7 * Written by Peter Postma <peter (at) pointless.nl>
8 */
9
10 #include <sys/types.h>
11 #include <sys/socket.h>
12 #include <sys/stat.h>
13 #include <sys/param.h>
14 #include <sys/sysctl.h>
15 #include <sys/wait.h>
16
17 #include <netinet/in.h>
18 #include <netinet/ip_var.h>
19 #include <netinet/tcp.h>
20 #include <netinet/tcp_timer.h>
21 #include <netinet/tcp_var.h>
22
23 #include <arpa/inet.h>
24
25 #include <ctype.h>
26 #include <err.h>
27 #include <errno.h>
28 #include <fcntl.h>
29 #include <grp.h>
30 #include <netdb.h>
31 #include <poll.h>
32 #include <pwd.h>
33 #include <signal.h>
34 #include <stdio.h>
35 #include <stdlib.h>
36 #include <string.h>
37 #include <syslog.h>
38 #include <unistd.h>
39
40 __RCSID("$NetBSD: identd.c,v 1.21 2004/01/31 22:03:31 christos Exp $");
41
42 #define OPSYS_NAME "UNIX"
43 #define IDENT_SERVICE "auth"
44 #define TIMEOUT 30 /* seconds */
45
46 static int idhandle(int, const char *, const char *, const char *,
47 const char *, int);
48 static void idparse(int, int, int, const char *, const char *, const char *);
49 static void iderror(int, int, int, const char *);
50 static const char *gethost(struct sockaddr_storage *);
51 static int *socketsetup(const char *, const char *, int);
52 static int sysctl_getuid(struct sockaddr_storage *, socklen_t, uid_t *);
53 static int check_noident(const char *);
54 static int check_userident(const char *, char *, size_t);
55 static void random_string(char *, size_t);
56 static void change_format(const char *, struct passwd *, char *, size_t);
57 static void timeout_handler(int);
58 static void waitchild(int);
59 static void fatal(const char *);
60
61 static int bflag, eflag, fflag, Fflag, iflag, Iflag;
62 static int lflag, Lflag, nflag, Nflag, rflag;
63
64 int
65 main(int argc, char *argv[])
66 {
67 int IPv4or6, ch, *socks, timeout;
68 char *address, *charset, *fmt;
69 const char *osname, *portno;
70 char *p;
71 char user[LOGIN_NAME_MAX];
72 struct group *grp;
73 struct passwd *pw;
74 gid_t gid;
75 uid_t uid;
76
77 IPv4or6 = AF_UNSPEC;
78 osname = OPSYS_NAME;
79 portno = IDENT_SERVICE;
80 timeout = TIMEOUT;
81 address = NULL;
82 charset = NULL;
83 fmt = NULL;
84 socks = NULL;
85 gid = uid = 0;
86 bflag = eflag = fflag = Fflag = iflag = Iflag = 0;
87 lflag = Lflag = nflag = Nflag = rflag = 0;
88
89 /* Started from a tty? then run as daemon */
90 if (isatty(0))
91 bflag = 1;
92
93 /* Parse arguments */
94 while ((ch = getopt(argc, argv, "46a:bceF:f:g:IiL:lNno:p:rt:u:")) != -1)
95 switch (ch) {
96 case '4':
97 IPv4or6 = AF_INET;
98 break;
99 case '6':
100 IPv4or6 = AF_INET6;
101 break;
102 case 'a':
103 address = optarg;
104 break;
105 case 'b':
106 bflag = 1;
107 break;
108 case 'c':
109 charset = optarg;
110 break;
111 case 'e':
112 eflag = 1;
113 break;
114 case 'F':
115 Fflag = 1;
116 fmt = optarg;
117 break;
118 case 'f':
119 fflag = 1;
120 (void)strlcpy(user, optarg, sizeof(user));
121 break;
122 case 'g':
123 gid = (gid_t)strtol(optarg, &p, 0);
124 if (*p != '\0') {
125 if ((grp = getgrnam(optarg)) != NULL)
126 gid = grp->gr_gid;
127 else
128 errx(1, "No such group '%s'", optarg);
129 }
130 break;
131 case 'I':
132 Iflag = 1;
133 /* FALLTHROUGH */
134 case 'i':
135 iflag = 1;
136 break;
137 case 'L':
138 Lflag = 1;
139 (void)strlcpy(user, optarg, sizeof(user));
140 break;
141 case 'l':
142 lflag = 1;
143 break;
144 case 'N':
145 Nflag = 1;
146 break;
147 case 'n':
148 nflag = 1;
149 break;
150 case 'o':
151 osname = optarg;
152 break;
153 case 'p':
154 portno = optarg;
155 break;
156 case 'r':
157 rflag = 1;
158 break;
159 case 't':
160 timeout = (int)strtol(optarg, &p, 0);
161 if (*p != '\0')
162 errx(1, "Invalid timeout value '%s'", optarg);
163 break;
164 case 'u':
165 uid = (uid_t)strtol(optarg, &p, 0);
166 if (*p != '\0') {
167 if ((pw = getpwnam(optarg)) != NULL) {
168 uid = pw->pw_uid;
169 gid = pw->pw_gid;
170 } else
171 errx(1, "No such user '%s'", optarg);
172 }
173 break;
174 default:
175 exit(1);
176 }
177
178 if (lflag)
179 openlog("identd", LOG_PID, LOG_DAEMON);
180
181 /* Setup sockets if -b flag */
182 if (bflag) {
183 socks = socketsetup(address, portno, IPv4or6);
184 if (socks == NULL)
185 return 1;
186 }
187
188 /* Switch to another uid/gid ? */
189 if (gid && setgid(gid) == -1) {
190 if (lflag)
191 syslog(LOG_ERR, "setgid: %m");
192 if (bflag)
193 warn("setgid");
194 exit(1);
195 }
196 if (uid && setuid(uid) == -1) {
197 if (lflag)
198 syslog(LOG_ERR, "setuid: %m");
199 if (bflag)
200 warn("setuid");
201 exit(1);
202 }
203
204 /* Daemonize, setup pollfds and start mainloop if -b flag */
205 if (bflag) {
206 int fd, i, nfds, rv;
207 struct pollfd *rfds;
208
209 (void)signal(SIGCHLD, waitchild);
210 (void)daemon(0, 0);
211
212 rfds = malloc(*socks * sizeof(struct pollfd));
213 if (rfds == NULL)
214 fatal("malloc");
215 nfds = *socks;
216 for (i = 0; i < nfds; i++) {
217 rfds[i].fd = socks[i+1];
218 rfds[i].events = POLLIN;
219 rfds[i].revents = 0;
220 }
221 /* Mainloop for daemon */
222 for (;;) {
223 rv = poll(rfds, nfds, INFTIM);
224 if (rv < 0 && errno == EINTR)
225 continue;
226 if (rv < 0)
227 fatal("poll");
228 for (i = 0; i < nfds; i++) {
229 if (rfds[i].revents & POLLIN) {
230 fd = accept(rfds[i].fd, NULL, NULL);
231 if (fd < 0) {
232 if (lflag)
233 syslog(LOG_ERR,
234 "accept: %m");
235 continue;
236 }
237 switch (fork()) {
238 case -1: /* error */
239 if (lflag)
240 syslog(LOG_ERR,
241 "fork: %m");
242 (void)sleep(1);
243 break;
244 case 0: /* child */
245 (void)idhandle(fd, charset,
246 fmt, osname, user, timeout);
247 _exit(0);
248 default: /* parent */
249 (void)close(fd);
250 }
251 }
252 }
253 }
254 } else
255 (void)idhandle(STDIN_FILENO, charset, fmt, osname, user,
256 timeout);
257
258 return 0;
259 }
260
261 static int
262 idhandle(int fd, const char *charset, const char *fmt, const char *osname,
263 const char *user, int timeout)
264 {
265 struct sockaddr_storage ss[2];
266 char userbuf[LOGIN_NAME_MAX];
267 char idbuf[LOGIN_NAME_MAX];
268 char buf[BUFSIZ], *p;
269 int n, lport, fport;
270 struct passwd *pw;
271 socklen_t len;
272 uid_t uid;
273
274 lport = fport = 0;
275
276 (void)strlcpy(idbuf, user, sizeof(idbuf));
277 (void)signal(SIGALRM, timeout_handler);
278 (void)alarm(timeout);
279
280 /* Get foreign internet address */
281 len = sizeof(ss[0]);
282 if (getpeername(fd, (struct sockaddr *)&ss[0], &len) < 0)
283 fatal("getpeername");
284
285 if (lflag)
286 syslog(LOG_INFO, "Connection from %s", gethost(&ss[0]));
287
288 /* Get local internet address */
289 len = sizeof(ss[1]);
290 if (getsockname(fd, (struct sockaddr *)&ss[1], &len) < 0)
291 fatal("getsockname");
292
293 /* Be sure to have the same address family's */
294 if (ss[0].ss_family != ss[1].ss_family) {
295 if (lflag)
296 syslog(LOG_ERR, "Foreign/local AF are different!");
297 return 1;
298 }
299
300 /* Receive data from the client */
301 if ((n = recv(fd, buf, sizeof(buf) - 1, 0)) < 0) {
302 fatal("recv");
303 } else if (n == 0) {
304 if (lflag)
305 syslog(LOG_NOTICE, "recv: EOF");
306 iderror(fd, 0, 0, "UNKNOWN-ERROR");
307 return 1;
308 }
309 buf[n] = '\0';
310
311 /* Get local and remote ports from the received data */
312 p = buf;
313 while (*p != '\0' && isspace(*p))
314 p++;
315 if ((p = strtok(p, " \t,")) != NULL) {
316 lport = atoi(p);
317 if ((p = strtok(NULL, " \t,")) != NULL)
318 fport = atoi(p);
319 }
320
321 /* Are the ports valid? */
322 if (lport < 1 || lport > 65535 || fport < 1 || fport > 65535) {
323 if (lflag)
324 syslog(LOG_NOTICE, "Invalid port(s): %d, %d from %s",
325 lport, fport, gethost(&ss[0]));
326 iderror(fd, 0, 0, eflag ? "UNKNOWN-ERROR" : "INVALID-PORT");
327 return 1;
328 }
329
330 /* If there is a 'lie' user enabled, then handle it now and quit */
331 if (Lflag) {
332 if (lflag)
333 syslog(LOG_NOTICE, "Lying with name %s to %s",
334 idbuf, gethost(&ss[0]));
335 idparse(fd, lport, fport, charset, osname, idbuf);
336 return 0;
337 }
338
339 /* Protocol dependent stuff */
340 switch (ss[0].ss_family) {
341 case AF_INET:
342 ((struct sockaddr_in *)&ss[0])->sin_port = htons(fport);
343 ((struct sockaddr_in *)&ss[1])->sin_port = htons(lport);
344 break;
345 case AF_INET6:
346 ((struct sockaddr_in6 *)&ss[0])->sin6_port = htons(fport);
347 ((struct sockaddr_in6 *)&ss[1])->sin6_port = htons(lport);
348 break;
349 default:
350 if (lflag)
351 syslog(LOG_ERR, "Unsupported protocol, proto no. %d",
352 ss[0].ss_family);
353 return 1;
354 }
355
356 /* Do sysctl call */
357 if (sysctl_getuid(ss, sizeof(ss), &uid) == -1) {
358 if (lflag)
359 syslog(LOG_ERR, "sysctl: %m");
360 if (fflag) {
361 if (lflag)
362 syslog(LOG_NOTICE, "Using fallback name %s "
363 "to %s", idbuf, gethost(&ss[0]));
364 idparse(fd, lport, fport, charset, osname, idbuf);
365 return 0;
366 }
367 iderror(fd, lport, fport, eflag ? "UNKNOWN-ERROR" : "NO-USER");
368 return 1;
369 }
370
371 /* Get username with the uid */
372 if ((pw = getpwuid(uid)) == NULL) {
373 if (lflag)
374 syslog(LOG_ERR, "Couldn't map uid (%u) to name", uid);
375 (void)snprintf(idbuf, sizeof(idbuf), "%u", uid);
376 idparse(fd, lport, fport, charset, osname, idbuf);
377 return 0;
378 }
379
380 if (lflag)
381 syslog(LOG_INFO, "Successfull lookup: %d, %d: %s for %s",
382 lport, fport, pw->pw_name, gethost(&ss[0]));
383
384 /* No ident enabled? */
385 if (Nflag && check_noident(pw->pw_dir)) {
386 if (lflag)
387 syslog(LOG_NOTICE, "Returning HIDDEN-USER for user %s"
388 " to %s", pw->pw_name, gethost(&ss[0]));
389 iderror(fd, lport, fport, "HIDDEN-USER");
390 return 1;
391 }
392
393 /* User ident enabled ? */
394 if (iflag && check_userident(pw->pw_dir, idbuf, sizeof(idbuf))) {
395 (void)strlcpy(userbuf, pw->pw_name, sizeof(userbuf));
396 if (!Iflag) {
397 if (strspn(idbuf, "0123456789") &&
398 getpwuid(atoi(idbuf)) != NULL)
399 (void)strlcpy(idbuf, userbuf, sizeof(idbuf));
400 else if (getpwnam(idbuf) != NULL)
401 (void)strlcpy(idbuf, userbuf, sizeof(idbuf));
402 }
403 if (lflag)
404 syslog(LOG_NOTICE, "Returning user specified '%s' for "
405 "user %s to %s", idbuf, userbuf, gethost(&ss[0]));
406 idparse(fd, lport, fport, charset, osname, idbuf);
407 return 0;
408 }
409
410 /* Send random crap? */
411 if (rflag) {
412 /* Random number or string? */
413 if (nflag)
414 (void)snprintf(idbuf, sizeof(idbuf), "%u",
415 (unsigned int)(arc4random() % 65535));
416 else
417 random_string(idbuf, sizeof(idbuf));
418
419 if (lflag)
420 syslog(LOG_NOTICE, "Returning random '%s' for user %s"
421 " to %s", idbuf, pw->pw_name, gethost(&ss[0]));
422 idparse(fd, lport, fport, charset, osname, idbuf);
423 return 0;
424 }
425
426 /* Return number? */
427 if (nflag)
428 (void)snprintf(idbuf, sizeof(idbuf), "%u", uid);
429 else
430 (void)strlcpy(idbuf, pw->pw_name, sizeof(idbuf));
431
432 if (Fflag) {
433 /* RFC 1413 says that 512 is the limit */
434 change_format(fmt, pw, buf, 512);
435 idparse(fd, lport, fport, charset, osname, buf);
436 } else
437 idparse(fd, lport, fport, charset, osname, idbuf);
438
439 return 0;
440 }
441
442 /* Send/parse the ident result */
443 static void
444 idparse(int fd, int lport, int fport, const char *charset, const char *osname,
445 const char *user)
446 {
447 char *p;
448
449 if (asprintf(&p, "%d , %d : USERID : %s%s%s : %s\r\n", lport, fport,
450 osname, charset ? " , " : "", charset ? charset : "", user) < 0)
451 fatal("asprintf");
452 if (send(fd, p, strlen(p), 0) < 0) {
453 free(p);
454 fatal("send");
455 }
456 free(p);
457 }
458
459 /* Return a specified ident error */
460 static void
461 iderror(int fd, int lport, int fport, const char *error)
462 {
463 char *p;
464
465 if (asprintf(&p, "%d , %d : ERROR : %s\r\n", lport, fport, error) < 0)
466 fatal("asprintf");
467 if (send(fd, p, strlen(p), 0) < 0) {
468 free(p);
469 fatal("send");
470 }
471 free(p);
472 }
473
474 /* Return the IP address of the connecting host */
475 static const char *
476 gethost(struct sockaddr_storage *ss)
477 {
478 static char host[NI_MAXHOST];
479
480 if (getnameinfo((struct sockaddr *)ss, ss->ss_len, host,
481 sizeof(host), NULL, 0, NI_NUMERICHOST) == 0)
482 return host;
483
484 return "UNKNOWN";
485 }
486
487 /* Setup sockets, for daemon mode */
488 static int *
489 socketsetup(const char *address, const char *port, int af)
490 {
491 struct addrinfo hints, *res, *res0;
492 int error, maxs, *s, *socks, y = 1;
493 const char *cause = NULL;
494
495 (void)memset(&hints, 0, sizeof(hints));
496 hints.ai_flags = AI_PASSIVE;
497 hints.ai_family = af;
498 hints.ai_socktype = SOCK_STREAM;
499 error = getaddrinfo(address, port, &hints, &res0);
500 if (error) {
501 if (lflag)
502 syslog(LOG_ERR, "getaddrinfo: %s", gai_strerror(error));
503 errx(1, "%s", gai_strerror(error));
504 /*NOTREACHED*/
505 }
506
507 /* Count max number of sockets we may open */
508 for (maxs = 0, res = res0; res != NULL; res = res->ai_next)
509 maxs++;
510
511 socks = malloc((maxs + 1) * sizeof(int));
512 if (socks == NULL) {
513 if (lflag)
514 syslog(LOG_ERR, "malloc: %m");
515 err(1, "malloc");
516 /* NOTREACHED */
517 }
518
519 *socks = 0;
520 s = socks + 1;
521 for (res = res0; res != NULL; res = res->ai_next) {
522 *s = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
523 if (*s < 0) {
524 cause = "socket";
525 continue;
526 }
527 (void)setsockopt(*s, SOL_SOCKET, SO_REUSEADDR, &y, sizeof(y));
528 if (bind(*s, res->ai_addr, res->ai_addrlen) < 0) {
529 cause = "bind";
530 (void)close(*s);
531 continue;
532 }
533 if (listen(*s, 5) < 0) {
534 cause = "listen";
535 (void)close(*s);
536 continue;
537 }
538 *socks = *socks + 1;
539 s++;
540 }
541
542 if (*socks == 0) {
543 free(socks);
544 if (lflag)
545 syslog(LOG_ERR, "%s: %m", cause);
546 err(1, "%s", cause);
547 /* NOTREACHED */
548 }
549 if (res0)
550 freeaddrinfo(res0);
551
552 return socks;
553 }
554
555 /* Return the UID for the connection owner */
556 static int
557 sysctl_getuid(struct sockaddr_storage *ss, socklen_t len, uid_t *uid)
558 {
559 int mib[4];
560 uid_t myuid;
561 size_t uidlen;
562
563 uidlen = sizeof(myuid);
564
565 mib[0] = CTL_NET;
566 mib[1] = ss->ss_family;
567 mib[2] = IPPROTO_TCP;
568 mib[3] = TCPCTL_IDENT;
569
570 if (sysctl(mib, sizeof(mib)/ sizeof(int), &myuid, &uidlen, ss, len) < 0)
571 return -1;
572 *uid = myuid;
573
574 return 0;
575 }
576
577 /* Check if a .noident file exists in the user home directory */
578 static int
579 check_noident(const char *homedir)
580 {
581 struct stat sb;
582 char *path;
583 int ret;
584
585 if (homedir == NULL)
586 return 0;
587 if (asprintf(&path, "%s/.noident", homedir) < 0)
588 return 0;
589 ret = stat(path, &sb);
590
591 free(path);
592 return (ret == 0);
593 }
594
595 /*
596 * Check if a .ident file exists in the user home directory and
597 * return the contents of that file.
598 */
599 static int
600 check_userident(const char *homedir, char *username, size_t len)
601 {
602 struct stat sb;
603 char *path, *p;
604 int fd, n;
605
606 if (len == 0 || homedir == NULL)
607 return 0;
608 if (asprintf(&path, "%s/.ident", homedir) < 0)
609 return 0;
610 if ((fd = open(path, O_RDONLY|O_NONBLOCK|O_NOFOLLOW, 0)) < 0) {
611 free(path);
612 return 0;
613 }
614 if (fstat(fd, &sb) < 0 || !S_ISREG(sb.st_mode)) {
615 (void)close(fd);
616 free(path);
617 return 0;
618 }
619 if ((n = read(fd, username, len - 1)) < 1) {
620 (void)close(fd);
621 free(path);
622 return 0;
623 }
624 username[n] = '\0';
625
626 if ((p = strpbrk(username, "\r\n")))
627 *p = '\0';
628
629 (void)close(fd);
630 free(path);
631 return 1;
632 }
633
634 /* Generate a random string */
635 static void
636 random_string(char *str, size_t len)
637 {
638 static const char chars[] = "abcdefghijklmnopqrstuvwxyz1234567890";
639 char *p;
640
641 if (len == 0)
642 return;
643 for (p = str; len > 1; len--)
644 *p++ = chars[arc4random() % (sizeof(chars) - 1)];
645 *p = '\0';
646 }
647
648 /* Change the output format */
649 static void
650 change_format(const char *format, struct passwd *pw, char *dest, size_t len)
651 {
652 struct group *gr;
653 const char *cp;
654 char **gmp;
655 int bp;
656
657 if (len == 0)
658 return;
659 if ((gr = getgrgid(pw->pw_gid)) == NULL)
660 return;
661
662 for (bp = 0, cp = format; *cp != '\0' && bp < 490; cp++) {
663 if (*cp != '%') {
664 dest[bp++] = *cp;
665 continue;
666 }
667 if (*++cp == '\0')
668 break;
669 switch (*cp) {
670 case 'u':
671 (void)snprintf(&dest[bp], len - bp, "%.*s", 490 - bp,
672 pw->pw_name);
673 break;
674 case 'U':
675 (void)snprintf(&dest[bp], len - bp, "%d", pw->pw_uid);
676 break;
677 case 'g':
678 (void)snprintf(&dest[bp], len - bp, "%.*s", 490 - bp,
679 gr->gr_name);
680 break;
681 case 'G':
682 (void)snprintf(&dest[bp], len - bp, "%d", gr->gr_gid);
683 break;
684 case 'l':
685 (void)snprintf(&dest[bp], len - bp, "%.*s", 490 - bp,
686 gr->gr_name);
687 bp += strlen(&dest[bp]);
688 if (bp >= 490)
689 break;
690 setgrent();
691 while ((gr = getgrent()) != NULL) {
692 if (gr->gr_gid == pw->pw_gid)
693 continue;
694 for (gmp = gr->gr_mem; *gmp && **gmp; gmp++) {
695 if (strcmp(*gmp, pw->pw_name) == 0) {
696 (void)snprintf(&dest[bp],
697 len - bp, ",%.*s",
698 490 - bp, gr->gr_name);
699 bp += strlen(&dest[bp]);
700 break;
701 }
702 }
703 if (bp >= 490)
704 break;
705 }
706 endgrent();
707 break;
708 case 'L':
709 (void)snprintf(&dest[bp], len - bp, "%u", gr->gr_gid);
710 bp += strlen(&dest[bp]);
711 if (bp >= 490)
712 break;
713 setgrent();
714 while ((gr = getgrent()) != NULL) {
715 if (gr->gr_gid == pw->pw_gid)
716 continue;
717 for (gmp = gr->gr_mem; *gmp && **gmp; gmp++) {
718 if (strcmp(*gmp, pw->pw_name) == 0) {
719 (void)snprintf(&dest[bp],
720 len - bp, ",%u",
721 gr->gr_gid);
722 bp += strlen(&dest[bp]);
723 break;
724 }
725 }
726 if (bp >= 490)
727 break;
728 }
729 endgrent();
730 break;
731 default:
732 dest[bp] = *cp;
733 dest[bp+1] = '\0';
734 break;
735 }
736 bp += strlen(&dest[bp]);
737 }
738 if (bp >= 490) {
739 (void)snprintf(&dest[490], len - 490, "...");
740 bp = 493;
741 }
742 dest[bp] = '\0';
743 }
744
745 /* Just exit when we caught SIGALRM */
746 static void
747 timeout_handler(int s)
748 {
749 if (lflag)
750 syslog(LOG_DEBUG, "SIGALRM triggered, exiting...");
751 exit(1);
752 }
753
754 /* This is to clean up zombie processes when in daemon mode */
755 static void
756 waitchild(int s)
757 {
758 while (waitpid(-1, NULL, WNOHANG) > 0)
759 continue;
760 }
761
762 /* Report errno through syslog and quit */
763 static void
764 fatal(const char *func)
765 {
766 if (lflag)
767 syslog(LOG_ERR, "%s: %m", func);
768 exit(1);
769 }
770