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