xref: /netbsd/libexec/identd/identd.c (revision bf9ec67e)
1 /*	$NetBSD: identd.c,v 1.12 2002/05/26 00:02:07 wiz Exp $	*/
2 
3 /*
4 ** identd.c                       A TCP/IP link identification protocol server
5 **
6 ** This program is in the public domain and may be used freely by anyone
7 ** who wants to.
8 **
9 ** Last update: 7 Oct 1993
10 **
11 ** Please send bug fixes/bug reports to: Peter Eriksson <pen@lysator.liu.se>
12 */
13 
14 #if defined(IRIX) || defined(SVR4) || defined(NeXT) || (defined(sco) && sco >= 42) || defined(_AIX4) || defined(__NetBSD__) || defined(__FreeBSD__) || defined(ultrix)
15 #  define SIGRETURN_TYPE void
16 #  define SIGRETURN_TYPE_IS_VOID
17 #else
18 #  define SIGRETURN_TYPE int
19 #endif
20 
21 #ifdef SVR4
22 #  define STRNET
23 #endif
24 
25 #ifdef NeXT31
26 #  include <libc.h>
27 #endif
28 
29 #ifdef sco
30 #  define USE_SIGALARM
31 #endif
32 
33 #include <stdio.h>
34 #include <ctype.h>
35 #include <errno.h>
36 #include <netdb.h>
37 #include <signal.h>
38 #include <fcntl.h>
39 
40 #include <sys/types.h>
41 #include <sys/param.h>
42 #include <sys/ioctl.h>
43 #include <sys/socket.h>
44 #ifndef _AUX_SOURCE
45 #  include <sys/file.h>
46 #endif
47 #include <sys/time.h>
48 #include <sys/wait.h>
49 
50 #include <pwd.h>
51 #include <grp.h>
52 
53 #include <netinet/in.h>
54 
55 #ifndef HPUX7
56 #  include <arpa/inet.h>
57 #endif
58 
59 #ifdef _AIX32
60 # include <sys/select.h>
61 #endif
62 
63 #if defined(MIPS) || defined(BSD43)
64 extern int errno;
65 #endif
66 
67 #if defined(SOLARIS) || defined(__NetBSD__) || defined(__FreeBSD__) || defined(__linux__) || defined(_AIX)
68 #  include <unistd.h>
69 #  include <stdlib.h>
70 #  include <string.h>
71 #endif
72 
73 #include "identd.h"
74 #include "error.h"
75 #include "paths.h"
76 
77 
78 /* Antique unixes do not have these things defined... */
79 #ifndef FD_SETSIZE
80 #  define FD_SETSIZE 256
81 #endif
82 
83 #ifndef FD_SET
84 #  ifndef NFDBITS
85 #    define NFDBITS   	(sizeof(int) * NBBY)  /* bits per mask */
86 #  endif
87 #  define FD_SET(n, p)  ((p)->fds_bits[(n)/NFDBITS] |= (1 << ((n) % NFDBITS)))
88 #endif
89 
90 #ifndef FD_ZERO
91 #  define FD_ZERO(p)        bzero((char *)(p), sizeof(*(p)))
92 #endif
93 
94 
95 char *path_unix = (char *) NULL;
96 char *path_kmem = (char *) NULL;
97 
98 int verbose_flag = 0;
99 int debug_flag   = 0;
100 int syslog_flag  = 0;
101 int multi_flag   = 0;
102 int other_flag   = 0;
103 int unknown_flag = 0;
104 int noident_flag = 0;
105 int crypto_flag  = 0;
106 int liar_flag    = 0;
107 
108 int lport = 0;
109 int fport = 0;
110 
111 char *charset_name = (char *) NULL;
112 char *indirect_host = (char *) NULL;
113 char *indirect_password = (char *) NULL;
114 char *lie_string = (char *) NULL;
115 
116 #ifdef ALLOW_FORMAT
117     int format_flag = 0;
118     char *format = "%u";
119 #endif
120 
121 static int child_pid;
122 
123 #ifdef LOG_DAEMON
124 static int syslog_facility = LOG_DAEMON;
125 #endif
126 
127 static int comparemem __P((void *, void *, int));
128 char *clearmem __P((void *, int));
129 static SIGRETURN_TYPE child_handler __P((int));
130 int main __P((int, char *[]));
131 
132 /*
133 ** The structure passing convention for GCC is incompatible with
134 ** Suns own C compiler, so we define our own inet_ntoa() function.
135 ** (This should only affect GCC version 1 I think, a well, this works
136 ** for version 2 also so why bother.. :-)
137 */
138 #if defined(__GNUC__) && defined(__sparc__) && !defined(NeXT)
139 
140 #ifdef inet_ntoa
141 #undef inet_ntoa
142 #endif
143 
144 char *inet_ntoa(ad)
145     struct in_addr ad;
146 {
147     unsigned long int s_ad;
148     int a, b, c, d;
149     static char addr[20];
150 
151     s_ad = ad.s_addr;
152     d = s_ad % 256;
153     s_ad /= 256;
154     c = s_ad % 256;
155     s_ad /= 256;
156     b = s_ad % 256;
157     a = s_ad / 256;
158     sprintf(addr, "%d.%d.%d.%d", a, b, c, d);
159 
160     return addr;
161 }
162 #endif
163 
164 static int comparemem(vp1, vp2, len)
165      void *vp1;
166      void *vp2;
167      int len;
168 {
169     unsigned char *p1 = (unsigned char *) vp1;
170     unsigned char *p2 = (unsigned char *) vp2;
171     int c;
172 
173     while (len-- > 0)
174 	if ((c = (int) *p1++ - (int) *p2++) != 0)
175 	    return c;
176 
177     return 0;
178 }
179 
180 /*
181 ** Return the name of the connecting host, or the IP number as a string.
182 */
183 char *gethost(addr)
184     struct in_addr *addr;
185 {
186     int i;
187     struct hostent *hp;
188 
189 
190     hp = gethostbyaddr((char *) addr, sizeof(struct in_addr), AF_INET);
191     if (hp)
192     {
193 	char *hname = strdup(hp->h_name);
194 
195 	if (! hname) {
196 	    syslog(LOG_ERR, "strdup(%s): %m", hp->h_name);
197 	    exit(1);
198 	}
199 	/* Found a IP -> Name match, now try the reverse for security reasons */
200 	hp = gethostbyname(hname);
201 	(void) free(hname);
202 	if (hp)
203 #ifdef h_addr
204 	    for (i = 0; hp->h_addr_list[i]; i++)
205 		if (comparemem(hp->h_addr_list[i],
206 			       (unsigned char *) addr,
207 			       (int) sizeof(struct in_addr)) == 0)
208 		    return (char *) hp->h_name;
209 #else
210 	if (comparemem(hp->h_addr, addr, sizeof(struct in_addr)) == 0)
211 	    return hp->h_name;
212 #endif
213   }
214 
215   return inet_ntoa(*addr);
216 }
217 
218 #ifdef USE_SIGALARM
219 /*
220 ** Exit cleanly after our time's up.
221 */
222 static SIGRETURN_TYPE
223 alarm_handler(int s)
224 {
225     if (syslog_flag)
226 	syslog(LOG_DEBUG, "SIGALRM triggered, exiting");
227 
228     exit(0);
229 }
230 #endif
231 
232 
233 #if !defined(hpux) && !defined(__hpux) && !defined(SVR4) && \
234     !defined(_CRAY) && !defined(sco) && !defined(LINUX)
235 /*
236 ** This is used to clean up zombie child processes
237 ** if the -w or -b options are used.
238 */
239 static SIGRETURN_TYPE
240 child_handler(dummy)
241 	int dummy;
242 {
243 #if defined(NeXT) || (defined(__sgi) && defined(__SVR3))
244     union wait status;
245 #else
246     int status;
247 #endif
248     int saved_errno = errno;
249 
250     while (wait3(&status, WNOHANG, NULL) > 0)
251 	;
252 
253     errno = saved_errno;
254 
255 #ifndef SIGRETURN_TYPE_IS_VOID
256     return 0;
257 #endif
258 }
259 #endif
260 
261 
262 char *clearmem(vbp, len)
263      void *vbp;
264      int len;
265 {
266     char *bp = (char *) vbp;
267     char *cp;
268 
269     cp = bp;
270     while (len-- > 0)
271 	*cp++ = 0;
272 
273     return bp;
274 }
275 
276 
277 /*
278 ** Main entry point into this daemon
279 */
280 int main(argc,argv)
281     int argc;
282     char *argv[];
283 {
284     int i, len;
285     struct sockaddr_in sin;
286     struct in_addr laddr, faddr;
287 #ifndef USE_SIGALARM
288     struct timeval tv;
289 #endif
290     int one = 1;
291 
292     int background_flag = 0;
293     int timeout = 0;
294     char *portno = "113";
295     char *bind_address = (char *) NULL;
296     int set_uid = 0;
297     int set_gid = 0;
298     int inhibit_default_config = 0;
299     int opt_count = 0;		/* Count of option flags */
300 
301 #ifdef __convex__
302     argc--;    /* get rid of extra argument passed by inetd */
303 #endif
304 
305 
306     if (isatty(0))
307 	background_flag = 1;
308 
309     /*
310     ** Prescan the arguments for "-f<config-file>" switches
311     */
312     inhibit_default_config = 0;
313     for (i = 1; i < argc && argv[i][0] == '-'; i++)
314 	if (argv[i][1] == 'f')
315 	    inhibit_default_config = 1;
316 
317     /*
318     ** Parse the default config file - if it exists
319     */
320     if (!inhibit_default_config)
321 	parse_config(NULL, 1);
322 
323     /*
324     ** Parse the command line arguments
325     */
326     for (i = 1; i < argc && argv[i][0] == '-'; i++) {
327 	opt_count++;
328 	switch (argv[i][1])
329 	{
330 	  case 'b':    /* Start as standalone daemon */
331 	    background_flag = 1;
332 	    break;
333 
334 	  case 'w':    /* Start from Inetd, wait mode */
335 	    background_flag = 2;
336 	    break;
337 
338 	  case 'i':    /* Start from Inetd, nowait mode */
339 	    background_flag = 0;
340 	    break;
341 
342 	  case 't':
343 	    timeout = atoi(argv[i]+2);
344 	    break;
345 
346 	  case 'p':
347 	    portno = argv[i]+2;
348 	    break;
349 
350 	  case 'a':
351 	    bind_address = argv[i]+2;
352 	    break;
353 
354 	  case 'u':
355 	    if (isdigit(argv[i][2]))
356 		set_uid = atoi(argv[i]+2);
357 	    else
358 	    {
359 		struct passwd *pwd;
360 
361 		pwd = getpwnam(argv[i]+2);
362 		if (!pwd)
363 		    ERROR1("no such user (%s) for -u option", argv[i]+2);
364 		else
365 		{
366 		    set_uid = pwd->pw_uid;
367 		    set_gid = pwd->pw_gid;
368 		}
369 	    }
370 	    break;
371 
372 	  case 'g':
373 	    if (isdigit(argv[i][2]))
374 		set_gid = atoi(argv[i]+2);
375 	    else
376 	    {
377 		struct group *grp;
378 
379 		grp = getgrnam(argv[i]+2);
380 		if (!grp)
381 		    ERROR1("no such group (%s) for -g option", argv[i]+2);
382 		else
383 		    set_gid = grp->gr_gid;
384 	    }
385 	    break;
386 
387 	  case 'c':
388 	    charset_name = argv[i]+2;
389 	    break;
390 
391 	  case 'r':
392 	    indirect_host = argv[i]+2;
393 	    break;
394 
395 	  case 'l':    /* Use the Syslog daemon for logging */
396 	    syslog_flag++;
397 #ifdef LOG_DAEMON
398 	    openlog("identd", LOG_PID, syslog_facility);
399 #else
400 	    openlog("identd", LOG_PID);
401 #endif
402 	    break;
403 
404 	  case 'o':
405 	    other_flag = 1;
406 	    break;
407 
408 	  case 'e':
409 	    unknown_flag = 1;
410 	    break;
411 
412 	  case 'V':    /* Give version of this daemon */
413 	    printf("[in.identd, version %s]\r\n", version);
414 	    exit(0);
415 	    break;
416 
417 	  case 'v':    /* Be verbose */
418 	    verbose_flag++;
419 	    break;
420 
421 	  case 'd':    /* Enable debugging */
422 	    debug_flag++;
423 	    break;
424 
425 	  case 'm':    /* Enable multiline queries */
426 	    multi_flag++;
427 	    break;
428 
429 	  case 'N':    /* Enable users ".noident" files */
430 	    noident_flag++;
431 	    break;
432 
433 #ifdef INCLUDE_CRYPT
434           case 'C':    /* Enable encryption. */
435 	    {
436 		FILE *keyfile;
437 
438 		if (argv[i][2])
439 		    keyfile = fopen(argv[i]+2, "r");
440 		else
441 		    keyfile = fopen(PATH_DESKEY, "r");
442 
443 		if (keyfile == NULL)
444 		{
445 		    ERROR("cannot open key file for option -C");
446 		}
447 		else
448 		{
449 		    char buf[1024];
450 
451 		    if (fgets(buf, 1024, keyfile) == NULL)
452 		    {
453 			ERROR("cannot read key file for option -C");
454 		    }
455 		    else
456 		    {
457 			init_encryption(buf);
458 			crypto_flag++;
459 		    }
460 		    fclose(keyfile);
461 		}
462 	    }
463             break;
464 #endif
465 
466 #ifdef ALLOW_FORMAT
467 	  case 'n': /* Compatibility flag - just send the user number */
468 	    format_flag = 1;
469 	    format = "%U";
470 	    break;
471 
472           case 'F':    /* Output format */
473 	    format_flag = 1;
474 	    format = argv[i]+2;
475 	    break;
476 #endif
477 
478 	  case 'L':	/* lie brazenly */
479 	    liar_flag = 1;
480 	    if (*(argv[i]+2) != '\0')
481 		lie_string = argv[i]+2;
482 	    else
483 #ifdef DEFAULT_LIE_USER
484 		lie_string = DEFAULT_LIE_USER;
485 #else
486 		ERROR("-L specified with no user name");
487 #endif
488 	    break;
489 
490 	  default:
491 	    ERROR1("Bad option %s", argv[i]);
492 	    break;
493 	}
494     }
495 
496 #if defined(_AUX_SOURCE) || defined (SUNOS35)
497     /* A/UX 2.0* & SunOS 3.5 calls us with an argument XXXXXXXX.YYYY
498     ** where XXXXXXXXX is the hexadecimal version of the callers
499     ** IP number, and YYYY is the port/socket or something.
500     ** It seems to be impossible to pass arguments to a daemon started
501     ** by inetd.
502     **
503     ** Just in case it is started from something else, then we only
504     ** skip the argument if no option flags have been seen.
505     */
506     if (opt_count == 0)
507 	argc--;
508 #endif
509 
510     /*
511     ** Path to kernel namelist file specified on command line
512     */
513     if (i < argc)
514 	path_unix = argv[i++];
515 
516     /*
517     ** Path to kernel memory device specified on command line
518     */
519     if (i < argc)
520 	path_kmem = argv[i++];
521 
522 
523     if (i < argc)
524 	ERROR1("Too many arguments: ignored from %s", argv[i]);
525 
526 
527     /*
528     ** We used to call k_open here. But then the file descriptor
529     ** kd->fd open on /dev/kmem is shared by all child processes.
530     ** From the fork(2) man page:
531     **      o  The child process has its own copy of the parent's descriptors.  These
532     **         descriptors reference the same underlying objects.  For instance, file
533     **         pointers in file objects are shared between the child and the parent
534     **         so that an lseek(2) on a descriptor in the child process can affect a
535     **         subsequent read(2) or write(2) by the parent.
536     ** Thus with concurrent (simultaneous) identd client processes,
537     ** they step on each other's toes when they use kvm_read.
538     **
539     ** Calling k_open here was a mistake for another reason too: we
540     ** did not yet honor -u and -g options. Presumably we are
541     ** running as root (unless the in.identd file is setuid), and
542     ** then we can open kmem regardless of -u and -g values.
543     **
544     **
545     ** Open the kernel memory device and read the nlist table
546     **
547     **     if (k_open() < 0)
548     ** 		ERROR("main: k_open");
549     */
550 
551     /*
552     ** Do the special handling needed for the "-b" flag
553     */
554     if (background_flag == 1)
555     {
556 	struct sockaddr_in addr;
557 	struct servent *sp;
558 	int fd;
559 
560 
561 	if (!debug_flag)
562 	{
563 	    if (fork())
564 		exit(0);
565 
566 	    close(0);
567 	    close(1);
568 	    close(2);
569 
570 	    if (fork())
571 		exit(0);
572 	}
573 
574 	fd = socket(AF_INET, SOCK_STREAM, 0);
575 	if (fd == -1)
576 	    ERROR("main: socket");
577 
578 	if (fd != 0)
579 	    dup2(fd, 0);
580 
581 	clearmem((void *) &addr, (int) sizeof(addr));
582 
583 	addr.sin_family = AF_INET;
584 	if (bind_address == (char *) NULL)
585 	    addr.sin_addr.s_addr = htonl(INADDR_ANY);
586 	else
587 	{
588 	    if (isdigit(bind_address[0]))
589 		addr.sin_addr.s_addr = inet_addr(bind_address);
590 	    else
591 	    {
592 		struct hostent *hp;
593 
594 		hp = gethostbyname(bind_address);
595 		if (!hp)
596 		    ERROR1("no such address (%s) for -a switch", bind_address);
597 
598 		/* This is ugly, should use memcpy() or bcopy() but... */
599 		addr.sin_addr.s_addr = * (unsigned long *) (hp->h_addr);
600 	    }
601 	}
602 
603 	if (isdigit(portno[0]))
604 	    addr.sin_port = htons(atoi(portno));
605 	else
606 	{
607 	    sp = getservbyname(portno, "tcp");
608 	    if (sp == (struct servent *) NULL)
609 		ERROR1("main: getservbyname: %s", portno);
610 	    addr.sin_port = sp->s_port;
611 	}
612 
613 #ifdef SO_REUSEADDR
614 	setsockopt(0, SOL_SOCKET, SO_REUSEADDR, (void *) &one, sizeof(one));
615 #endif
616 
617 	if (bind(0, (struct sockaddr *) &addr, sizeof(addr)) < 0)
618 	    ERROR("main: bind");
619     }
620 
621     if (background_flag)
622     {
623       if (listen(0, 3) < 0)
624 	ERROR("main: listen");
625     }
626 
627     if (set_gid)
628     {
629 	if (setgid(set_gid) == -1)
630 	    ERROR("main: setgid");
631 	/* Call me paranoid... PSz */
632 	if (getgid() != set_gid)
633 	    ERROR2("main: setgid failed: wanted %d, got GID %d", set_gid, getgid());
634 	if (getegid() != set_gid)
635 	    ERROR2("main: setgid failed: wanted %d, got EGID %d", set_gid, getegid());
636     }
637 
638     if (set_uid)
639     {
640 	if (setuid(set_uid) == -1)
641 	    ERROR("main: setuid");
642 	/* Call me paranoid... PSz */
643 	if (getuid() != set_uid)
644 	    ERROR2("main: setuid failed: wanted %d, got UID %d", set_uid, getuid());
645 	if (geteuid() != set_uid)
646 	    ERROR2("main: setuid failed: wanted %d, got EUID %d", set_uid, geteuid());
647     }
648 
649     /*
650     ** Do some special handling if the "-b" or "-w" flags are used
651     */
652     if (background_flag)
653     {
654 	int nfds, fd;
655 	fd_set read_set;
656 	struct sockaddr sad;
657 	int sadlen;
658 
659 
660 	/*
661 	** Set up the SIGCHLD signal child termination handler so
662 	** that we can avoid zombie processes hanging around and
663 	** handle childs terminating before being able to complete the
664 	** handshake.
665 	*/
666 #if (defined(SVR4) || defined(hpux) || defined(__hpux) || defined(IRIX) || \
667      defined(_CRAY) || defined(_AUX_SOURCE) || defined(sco) || \
668 	 defined(LINUX))
669 	signal(SIGCHLD, SIG_IGN);
670 #else
671 	signal(SIGCHLD, child_handler);
672 #endif
673 
674 	/*
675 	** Loop and dispatch client handling processes
676 	*/
677 	do
678 	{
679 #ifdef USE_SIGALARM
680 	    /*
681 	    ** Terminate if we've been idle for 'timeout' seconds
682 	    */
683 	    if (background_flag == 2 && timeout)
684 	    {
685 		signal(SIGALRM, alarm_handler);
686 		alarm(timeout);
687 	    }
688 #endif
689 
690 	    /*
691 	    ** Wait for a connection request to occur.
692 	    ** Ignore EINTR (Interrupted System Call).
693 	    */
694 	    do
695 	    {
696 		FD_ZERO(&read_set);
697 		FD_SET(0, &read_set);
698 
699 #ifndef USE_SIGALARM
700 		if (timeout)
701 		{
702 		    tv.tv_sec = timeout;
703 		    tv.tv_usec = 0;
704 #ifdef __hpux
705 		    nfds = select(FD_SETSIZE,
706 				  (int *) &read_set, NULL, NULL, &tv);
707 #else
708 		    nfds = select(FD_SETSIZE, &read_set, NULL, NULL, &tv);
709 #endif
710 		}
711 		else
712 #endif
713 
714 #ifdef __hpux
715 		nfds = select(FD_SETSIZE, (int *) &read_set, NULL, NULL, NULL);
716 #else
717 		nfds = select(FD_SETSIZE, &read_set, NULL, NULL, NULL);
718 #endif
719 	    } while (nfds < 0  && errno == EINTR);
720 
721 	    /*
722 	    ** An error occurred in select? Just die
723 	    */
724 	    if (nfds < 0)
725 		ERROR("main: select");
726 
727 	    /*
728 	    ** Timeout limit reached. Exit nicely
729 	    */
730 	    if (nfds == 0)
731 		exit(0);
732 
733 #ifdef USE_SIGALARM
734 	    /*
735 	    ** Disable the alarm timeout
736 	    */
737 	    alarm(0);
738 #endif
739 
740 	    /*
741 	    ** Accept the new client
742 	    */
743 	    sadlen = sizeof(sad);
744 	    errno = 0;
745 	    fd = accept(0, &sad, &sadlen);
746 	    if (fd == -1)
747 		ERROR1("main: accept. errno = %d", errno);
748 
749 	    /*
750 	    ** And fork, then close the fd if we are the parent.
751 	    */
752 	    child_pid = fork();
753 	} while (child_pid && (close(fd), 1));
754 
755 	/*
756 	** We are now in child, the parent has returned to "do" above.
757 	*/
758 	if (dup2(fd, 0) == -1)
759 	    ERROR("main: dup2: failed fd 0");
760 
761 	if (dup2(fd, 1) == -1)
762 	    ERROR("main: dup2: failed fd 1");
763 
764 	if (dup2(fd, 2) == -1)
765 	    ERROR("main: dup2: failed fd 2");
766     }
767 
768     /*
769     ** Get foreign internet address
770     */
771     len = sizeof(sin);
772     if (getpeername(0, (struct sockaddr *) &sin, &len) == -1)
773     {
774 	/*
775 	** A user has tried to start us from the command line or
776 	** the network link died, in which case this message won't
777 	** reach to other end anyway, so lets give the poor user some
778 	** errors.
779 	*/
780 	perror("in.identd: getpeername()");
781 	exit(1);
782     }
783 
784     faddr = sin.sin_addr;
785 
786 
787 #ifdef STRONG_LOG
788     if (syslog_flag)
789 	syslog(LOG_INFO, "Connection from %s", gethost(&faddr));
790 #endif
791 
792 
793     /*
794     ** Get local internet address
795     */
796     len = sizeof(sin);
797 #ifdef ATTSVR4
798     if (t_getsockname(0, (struct sockaddr *) &sin, &len) == -1)
799 #else
800     if (getsockname(0, (struct sockaddr *) &sin, &len) == -1)
801 #endif
802     {
803 	/*
804 	** We can just die here, because if this fails then the
805 	** network has died and we haven't got anyone to return
806 	** errors to.
807 	*/
808 	exit(1);
809     }
810     laddr = sin.sin_addr;
811 
812 
813     /*
814     ** Get the local/foreign port pair from the luser
815     */
816     parse(stdin, &laddr, &faddr);
817 
818     exit(0);
819 }
820