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