xref: /netbsd-src/libexec/identd/identd.c (revision 2a399c6883d870daece976daec6ffa7bb7f934ce)
1 /*	$NetBSD: identd.c,v 1.8 1997/10/08 07:07:49 mrg 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: 22 April 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(__NetBSD__)
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 #include <sys/types.h>
26 #include <sys/param.h>
27 #include <sys/ioctl.h>
28 #include <sys/socket.h>
29 #ifndef _AUX_SOURCE
30 #  include <sys/file.h>
31 #endif
32 #include <sys/time.h>
33 #include <sys/wait.h>
34 
35 #include <stdio.h>
36 #include <string.h>
37 #include <ctype.h>
38 #include <errno.h>
39 #include <netdb.h>
40 #include <signal.h>
41 #include <fcntl.h>
42 #include <stdlib.h>
43 #include <unistd.h>
44 
45 #include <pwd.h>
46 #include <grp.h>
47 
48 #include <netinet/in.h>
49 
50 #ifndef HPUX7
51 #  include <arpa/inet.h>
52 #endif
53 
54 #if defined(MIPS) || defined(BSD43)
55 extern int errno;
56 #endif
57 
58 #include "identd.h"
59 #include "error.h"
60 
61 /* Antique unixes do not have these things defined... */
62 #ifndef FD_SETSIZE
63 #  define FD_SETSIZE 256
64 #endif
65 
66 #ifndef FD_SET
67 #  ifndef NFDBITS
68 #    define NFDBITS   	(sizeof(int) * NBBY)  /* bits per mask */
69 #  endif
70 #  define FD_SET(n, p)  ((p)->fds_bits[(n)/NFDBITS] |= (1 << ((n) % NFDBITS)))
71 #endif
72 
73 #ifndef FD_ZERO
74 #  define FD_ZERO(p)        bzero((char *)(p), sizeof(*(p)))
75 #endif
76 
77 extern char *version;
78 
79 char *path_unix = NULL;
80 char *path_kmem = NULL;
81 
82 int verbose_flag = 0;
83 int debug_flag   = 0;
84 int syslog_flag  = 0;
85 int multi_flag   = 0;
86 int other_flag   = 0;
87 int unknown_flag = 0;
88 int number_flag  = 0;
89 int noident_flag = 0;
90 
91 int lport = 0;
92 int fport = 0;
93 
94 char *charset_name = NULL;
95 char *indirect_host = NULL;
96 char *indirect_password = NULL;
97 
98 static int child_pid;
99 
100 #ifdef LOG_DAEMON
101 static int syslog_facility = LOG_DAEMON;
102 #endif
103 
104 char *clearmem __P((char *, int));
105 static SIGRETURN_TYPE alarm_handler __P((int));
106 int main __P((int, char *[]));
107 
108 /*
109 ** The structure passing convention for GCC is incompatible with
110 ** Suns own C compiler, so we define our own inet_ntoa() function.
111 ** (This should only affect GCC version 1 I think, a well, this works
112 ** for version 2 also so why bother.. :-)
113 */
114 #if defined(__GNUC__) && defined(__sparc__)
115 
116 #ifdef inet_ntoa
117 #undef inet_ntoa
118 #endif
119 
120 char *inet_ntoa(ad)
121   struct in_addr ad;
122 {
123   unsigned long int s_ad;
124   int a, b, c, d;
125   static char addr[20];
126 
127   s_ad = ad.s_addr;
128   d = s_ad % 256;
129   s_ad /= 256;
130   c = s_ad % 256;
131   s_ad /= 256;
132   b = s_ad % 256;
133   a = s_ad / 256;
134   sprintf(addr, "%d.%d.%d.%d", a, b, c, d);
135 
136   return addr;
137 }
138 #endif
139 
140 
141 /*
142 ** Return the name of the connecting host, or the IP number as a string.
143 */
144 char *gethost(addr)
145   struct in_addr *addr;
146 {
147   struct hostent *hp;
148 
149 
150   hp = gethostbyaddr((char *) addr, sizeof(struct in_addr), AF_INET);
151   if (hp)
152     return hp->h_name;
153   else
154     return inet_ntoa(*addr);
155 }
156 
157 /*
158 ** Exit cleanly after our time's up.
159 */
160 static SIGRETURN_TYPE
161 alarm_handler(dummy)
162   int dummy;
163 {
164   if (syslog_flag)
165     syslog(LOG_DEBUG, "SIGALRM triggered, exiting");
166 
167   exit(0);
168 }
169 
170 #if !defined(hpux) && !defined(__hpux) && !defined(SVR4) && !defined(__NetBSD__) || defined(_CRAY)
171 /*
172 ** This is used to clean up zombie child processes
173 ** if the -w or -b options are used.
174 */
175 static SIGRETURN_TYPE
176 child_handler()
177 {
178 #if defined(IRIX) || defined(NeXT)
179   union wait status;
180 #else
181   int status;
182 #endif
183 
184   while (wait3(&status, WNOHANG, NULL) > 0)
185     ;
186 
187 #ifndef SIGRETURN_TYPE_IS_VOID
188   return 0;
189 #endif
190 }
191 #endif
192 
193 char *clearmem(bp, len)
194   char *bp;
195   int len;
196 {
197   char *cp;
198 
199   cp = bp;
200   while (len-- > 0)
201     *cp++ = 0;
202 
203   return bp;
204 }
205 
206 
207 /*
208 ** Main entry point into this daemon
209 */
210 int main(argc,argv)
211   int argc;
212   char *argv[];
213 {
214   int i, len;
215   struct sockaddr_in sin;
216   struct in_addr laddr, faddr;
217   struct timeval tv;
218 
219   int background_flag = 0;
220   int timeout = 0;
221   char *portno = "113";
222   char *bind_address = NULL;
223   int set_uid = 0;
224   int set_gid = 0;
225   int inhibit_default_config = 0;
226   int opt_count = 0;		/* Count of option flags */
227 
228 #ifdef __convex__
229   argc--;    /* get rid of extra argument passed by inetd */
230 #endif
231 
232   /*
233   ** Prescan the arguments for "-f<config-file>" switches
234   */
235   inhibit_default_config = 0;
236   for (i = 1; i < argc && argv[i][0] == '-'; i++)
237     if (argv[i][1] == 'f')
238       inhibit_default_config = 1;
239 
240   /*
241   ** Parse the default config file - if it exists
242   */
243   if (!inhibit_default_config)
244     parse_config(NULL, 1);
245 
246   /*
247   ** Parse the command line arguments
248   */
249   for (i = 1; i < argc && argv[i][0] == '-'; i++) {
250     opt_count++;
251     switch (argv[i][1])
252     {
253       case 'b':    /* Start as standalone daemon */
254         background_flag = 1;
255 	break;
256 
257       case 'w':    /* Start from Inetd, wait mode */
258 	background_flag = 2;
259 	break;
260 
261       case 'i':    /* Start from Inetd, nowait mode */
262 	background_flag = 0;
263 	break;
264 
265       case 't':
266 	timeout = atoi(argv[i]+2);
267 	break;
268 
269       case 'p':
270 	portno = argv[i]+2;
271 	break;
272 
273       case 'a':
274 	bind_address = argv[i]+2;
275 	break;
276 
277       case 'u':
278 	if (isdigit(argv[i][2]))
279 	  set_uid = atoi(argv[i]+2);
280 	else
281  	{
282 	  struct passwd *pwd;
283 
284 	  pwd = getpwnam(argv[i]+2);
285 	  if (!pwd)
286 	    ERROR1("no such user (%s) for -u option", argv[i]+2);
287 	  else
288 	  {
289 	    set_uid = pwd->pw_uid;
290 	    set_gid = pwd->pw_gid;
291 	  }
292 	}
293 	break;
294 
295       case 'g':
296 	if (isdigit(argv[i][2]))
297 	  set_gid = atoi(argv[i]+2);
298 	else
299  	{
300 	  struct group *grp;
301 
302 	  grp = getgrnam(argv[i]+2);
303 	  if (!grp)
304 	    ERROR1("no such group (%s) for -g option", argv[i]+2);
305 	  else
306 	    set_gid = grp->gr_gid;
307 	}
308 	break;
309 
310       case 'c':
311 	charset_name = argv[i]+2;
312 	break;
313 
314       case 'r':
315 	indirect_host = argv[i]+2;
316 	break;
317 
318       case 'l':    /* Use the Syslog daemon for logging */
319 	syslog_flag++;
320 	break;
321 
322       case 'o':
323 	other_flag = 1;
324 	break;
325 
326       case 'e':
327 	unknown_flag = 1;
328 	break;
329 
330       case 'n':
331 	number_flag = 1;
332 	break;
333 
334       case 'V':    /* Give version of this daemon */
335 	printf("[in.identd, version %s]\r\n", version);
336 	exit(0);
337 	break;
338 
339       case 'v':    /* Be verbose */
340 	verbose_flag++;
341 	break;
342 
343       case 'd':    /* Enable debugging */
344 	debug_flag++;
345 	break;
346 
347       case 'm':    /* Enable multiline queries */
348 	multi_flag++;
349 	break;
350 
351       case 'N':    /* Enable users ".noident" files */
352 	noident_flag++;
353 	break;
354     }
355   }
356 
357 #if defined(_AUX_SOURCE) || defined (SUNOS35)
358   /* A/UX 2.0* & SunOS 3.5 calls us with an argument XXXXXXXX.YYYY
359   ** where XXXXXXXXX is the hexadecimal version of the callers
360   ** IP number, and YYYY is the port/socket or something.
361   ** It seems to be impossible to pass arguments to a daemon started
362   ** by inetd.
363   **
364   ** Just in case it is started from something else, then we only
365   ** skip the argument if no option flags have been seen.
366   */
367   if (opt_count == 0)
368     argc--;
369 #endif
370 
371   /*
372   ** Path to kernel namelist file specified on command line
373   */
374   if (i < argc)
375     path_unix = argv[i++];
376 
377   /*
378   ** Path to kernel memory device specified on command line
379   */
380   if (i < argc)
381     path_kmem = argv[i++];
382 
383 
384   /*
385   ** Open the kernel memory device and read the nlist table
386   */
387   if (k_open() < 0)
388       ERROR("main: k_open");
389 
390   /*
391   ** Do the special handling needed for the "-b" flag
392   */
393   if (background_flag == 1)
394   {
395     struct sockaddr_in addr;
396     struct servent *sp;
397     int fd;
398 
399 
400     if (fork())
401       exit(0);
402 
403     close(0);
404     close(1);
405     close(2);
406 
407     if (fork())
408       exit(0);
409 
410     fd = socket(AF_INET, SOCK_STREAM, 0);
411     if (fd == -1)
412       ERROR("main: socket");
413 
414     if (fd != 0)
415       dup2(fd, 0);
416 
417     clearmem((char *)&addr, sizeof(addr));
418 
419     addr.sin_len = sizeof(struct sockaddr_in);
420     addr.sin_family = AF_INET;
421     if (bind_address == NULL)
422       addr.sin_addr.s_addr = htonl(INADDR_ANY);
423     else
424     {
425       if (inet_aton(bind_address, &addr.sin_addr) == 0)
426       {
427 	struct hostent *hp;
428 
429 	hp = gethostbyname(bind_address);
430 	if (!hp)
431 	  ERROR1("no such address (%s) for -a switch", bind_address);
432 
433 	memcpy(&addr.sin_addr, hp->h_addr, sizeof(addr.sin_addr));
434       }
435     }
436 
437     if (isdigit(portno[0]))
438       addr.sin_port = htons(atoi(portno));
439     else
440     {
441       sp = getservbyname(portno, "tcp");
442       if (sp == NULL)
443 	ERROR1("main: getservbyname: %s", portno);
444       addr.sin_port = sp->s_port;
445     }
446 
447     if (bind(0, (struct sockaddr *) &addr, sizeof(addr)) < 0)
448       ERROR("main: bind");
449 
450     if (listen(0, 3) < 0)
451       ERROR("main: listen");
452   }
453 
454   if (set_gid)
455     if (setgid(set_gid) == -1)
456       ERROR("main: setgid");
457 
458   if (set_uid)
459     if (setuid(set_uid) == -1)
460       ERROR("main: setuid");
461 
462   /*
463   ** Do some special handling if the "-b" or "-w" flags are used
464   */
465   if (background_flag)
466   {
467     int nfds, fd;
468     fd_set read_set;
469 
470 
471     /*
472     ** Set up the SIGCHLD signal child termination handler so
473     ** that we can avoid zombie processes hanging around and
474     ** handle childs terminating before being able to complete the
475     ** handshake.
476     */
477 #if (defined(SVR4) || defined(hpux) || defined(__hpux) || \
478      defined(__NetBSD__) || defined(_CRAY) || defined(_AUX_SOURCE))
479     signal(SIGCHLD, SIG_IGN);
480 #else
481     signal(SIGCHLD, (SIGRETURN_TYPE (*)()) child_handler);
482 #endif
483 
484     /*
485     ** Loop and dispatch client handling processes
486     */
487     do
488     {
489       /*
490       ** Terminate if we've been idle for 'timeout' seconds
491       */
492       if (background_flag == 2 && timeout)
493       {
494 	signal(SIGALRM, alarm_handler);
495 	alarm(timeout);
496       }
497 
498       /*
499       ** Wait for a connection request to occur.
500       ** Ignore EINTR (Interrupted System Call).
501       */
502       do
503       {
504 	FD_ZERO(&read_set);
505 	FD_SET(0, &read_set);
506 
507 	if (timeout)
508 	{
509 	  tv.tv_sec = timeout;
510 	  tv.tv_usec = 0;
511 	  nfds = select(FD_SETSIZE, &read_set, NULL, NULL, &tv);
512 	}
513 	else
514 
515 	nfds = select(FD_SETSIZE, &read_set, NULL, NULL, NULL);
516       } while (nfds < 0  && errno == EINTR);
517 
518       /*
519       ** An error occured in select? Just die
520       */
521       if (nfds < 0)
522 	ERROR("main: select");
523 
524       /*
525       ** Timeout limit reached. Exit nicely
526       */
527       if (nfds == 0)
528 	exit(0);
529 
530       /*
531       ** Disable the alarm timeout
532       */
533       alarm(0);
534 
535       /*
536       ** Accept the new client
537       */
538       fd = accept(0, NULL, NULL);
539       if (fd == -1)
540 	ERROR1("main: accept. errno = %d", errno);
541 
542       /*
543       ** And fork, then close the fd if we are the parent.
544       */
545       child_pid = fork();
546     } while (child_pid && (close(fd), 1));
547 
548     /*
549     ** We are now in child, the parent has returned to "do" above.
550     */
551     if (dup2(fd, 0) == -1)
552       ERROR("main: dup2: failed fd 0");
553 
554     if (dup2(fd, 1) == -1)
555       ERROR("main: dup2: failed fd 1");
556 
557     if (dup2(fd, 2) == -1)
558       ERROR("main: dup2: failed fd 2");
559   }
560 
561   /*
562   ** Get foreign internet address
563   */
564   len = sizeof(sin);
565   if (getpeername(0, (struct sockaddr *) &sin, &len) == -1)
566   {
567     /*
568     ** A user has tried to start us from the command line or
569     ** the network link died, in which case this message won't
570     ** reach to other end anyway, so lets give the poor user some
571     ** errors.
572     */
573     perror("in.identd: getpeername()");
574     exit(1);
575   }
576 
577   faddr = sin.sin_addr;
578 
579 
580   /*
581   ** Open the connection to the Syslog daemon if requested
582   */
583   if (syslog_flag)
584   {
585 #ifdef LOG_DAEMON
586     openlog("identd", LOG_PID, syslog_facility);
587 #else
588     openlog("identd", LOG_PID);
589 #endif
590 
591     syslog(LOG_INFO, "Connection from %s", gethost(&faddr));
592   }
593 
594 
595   /*
596   ** Get local internet address
597   */
598   len = sizeof(sin);
599 #ifdef ATTSVR4
600   if (t_getsockname(0, (struct sockaddr *) &sin, &len) == -1)
601 #else
602   if (getsockname(0, (struct sockaddr *) &sin, &len) == -1)
603 #endif
604   {
605     /*
606     ** We can just die here, because if this fails then the
607     ** network has died and we haven't got anyone to return
608     ** errors to.
609     */
610     exit(1);
611   }
612   laddr = sin.sin_addr;
613 
614 
615   /*
616   ** Get the local/foreign port pair from the luser
617   */
618   parse(stdin, &laddr, &faddr);
619 
620   exit(0);
621 }
622