1 /*	$OpenBSD: identd.c,v 1.41 2004/11/17 01:47:20 itojun Exp $	*/
2 
3 /*
4  * This program is in the public domain and may be used freely by anyone
5  * who wants to.
6  *
7  * Please send bug fixes/bug reports to: Peter Eriksson <pen@lysator.liu.se>
8  */
9 
10 #include <sys/types.h>
11 #include <sys/param.h>
12 #include <sys/ioctl.h>
13 #include <sys/socket.h>
14 #include <sys/file.h>
15 #include <sys/time.h>
16 #include <sys/wait.h>
17 
18 #include <netinet/in.h>
19 #include <arpa/inet.h>
20 
21 #include <stdio.h>
22 #include <stdlib.h>
23 #include <poll.h>
24 #include <string.h>
25 #include <stdarg.h>
26 #include <ctype.h>
27 #include <errno.h>
28 #include <netdb.h>
29 #include <syslog.h>
30 #include <signal.h>
31 #include <fcntl.h>
32 #include <unistd.h>
33 #include <pwd.h>
34 #include <grp.h>
35 
36 #include "identd.h"
37 
38 extern char *__progname;
39 
40 int	verbose_flag;
41 int	debug_flag;
42 int	syslog_flag;
43 int	multi_flag;
44 int	unknown_flag;
45 int	number_flag;
46 int	noident_flag;
47 int	userident_flag;
48 int	token_flag;
49 int	no_user_token_flag;
50 
51 int	lport;
52 int	fport;
53 
54 const  char *opsys_name = "UNIX";
55 const  char *charset_sep = "";
56 char   *charset_name = "";
57 
58 static pid_t child_pid;
59 
60 void		usage(void);
61 char *		gethost(struct sockaddr_storage *ss);
62 
63 void
usage(void)64 usage(void)
65 {
66 	syslog(LOG_ERR,
67 	    "%s [-i | -w | -b] [-t seconds] [-u uid] [-g gid] [-p port] "
68 	    "[-a address] [-c charset] [-noelvmNUdh]", __progname);
69 	exit(2);
70 }
71 
72 /*
73  * Return the name of the connecting host, or the IP number as a string.
74  */
75 char *
gethost4_addr(struct in_addr * addr)76 gethost4_addr(struct in_addr *addr)
77 {
78 	struct hostent *hp;
79 
80 	hp = gethostbyaddr((char *) addr, sizeof(struct in_addr), AF_INET);
81 	if (hp)
82 		return hp->h_name;
83 	return inet_ntoa(*addr);
84 }
85 
86 char *
gethost(struct sockaddr_storage * ss)87 gethost(struct sockaddr_storage *ss)
88 {
89 	if (ss->ss_family == AF_INET6)
90 		return (gethost6((struct sockaddr_in6 *)ss));
91 	return (gethost4((struct sockaddr_in *)ss));
92 }
93 
94 char *
gethost4(struct sockaddr_in * sin)95 gethost4(struct sockaddr_in *sin)
96 {
97 	struct hostent *hp;
98 
99 	hp = gethostbyaddr((char *)&sin->sin_addr, sizeof(struct in_addr), AF_INET);
100 	if (hp)
101 		return hp->h_name;
102 	return inet_ntoa(sin->sin_addr);
103 }
104 
105 /*
106  * Return the name of the connecting host, or the IP number as a string.
107  */
108 char *
gethost6(struct sockaddr_in6 * addr)109 gethost6(struct sockaddr_in6 *addr)
110 {
111 	static char hbuf[2][NI_MAXHOST];
112 #ifdef NI_WITHSCOPEID
113 	const int niflags = NI_NUMERICHOST | NI_WITHSCOPEID;
114 #else
115 	const int niflags = NI_NUMERICHOST;
116 #endif
117 	static int bb = 0;
118 	int error;
119 
120 	bb = (bb+1)%2;
121 	error = getnameinfo((struct sockaddr *)addr, addr->sin6_len,
122 	    hbuf[bb], sizeof(hbuf[bb]), NULL, 0, niflags);
123 	if (error != 0) {
124 		syslog(LOG_ERR, "getnameinfo failed (%s)", gai_strerror(error));
125 		strlcpy(hbuf[bb], "UNKNOWN", sizeof(hbuf[bb]));
126 	}
127 	return(hbuf[bb]);
128 }
129 
130 volatile sig_atomic_t alarm_fired;
131 
132 /*
133  * Exit cleanly after our time's up.
134  */
135 /* ARGSUSED */
136 static void
alarm_handler(int notused)137 alarm_handler(int notused)
138 {
139 	alarm_fired = 1;
140 }
141 
142 /*
143  * Main entry point into this daemon
144  */
145 int
main(int argc,char * argv[])146 main(int argc, char *argv[])
147 {
148 	struct sockaddr_storage sa, sa2;
149 	/* struct sockaddr_in sin;*/
150 	struct sockaddr_in *sin;
151 	struct sockaddr_in6 *sin6;
152 	struct in_addr laddr, faddr;
153 	struct in6_addr laddr6, faddr6;
154 	struct passwd *pwd;
155 	struct group *grp;
156 	int	background_flag = 0, timeout = 0, ch;
157 	char   *portno = "auth";
158 	char   *bind_address = NULL;
159 	uid_t   set_uid = 0;
160 	gid_t   set_gid = 0;
161 	extern char *optarg;
162 	socklen_t len;
163 
164 	openlog(__progname, LOG_PID, LOG_DAEMON);
165 
166 	/* runs as _identd if possible, fallback to "nobody" */
167 	if (getuid() == 0) {
168 		if ((pwd = getpwnam(DEFAULT_UID)) == NULL)
169 			pwd = getpwnam("nobody");
170 		if (pwd == NULL)
171 			error("no such user: neither %s nor nobody",
172 			    DEFAULT_UID);
173 		set_uid = pwd->pw_uid;
174 		set_gid = pwd->pw_gid;
175 	}
176 
177 	/*
178 	 * Parse the command line arguments
179 	 */
180 	while ((ch = getopt(argc, argv, "hHbwit:p:a:u:g:c:loenvdmNU")) != -1) {
181 		switch (ch) {
182 		case 'h':
183 			token_flag = 1;
184 			break;
185 		case 'H':
186 			no_user_token_flag = token_flag = 1;
187 			break;
188 		case 'b':	/* Start as standalone daemon */
189 			background_flag = 1;
190 			break;
191 		case 'w':	/* Start from Inetd, wait mode */
192 			background_flag = 2;
193 			break;
194 		case 'i':	/* Start from Inetd, nowait mode */
195 			background_flag = 0;
196 			break;
197 		case 't':
198 			timeout = atoi(optarg);
199 			break;
200 		case 'p':
201 			portno = optarg;
202 			break;
203 		case 'a':
204 			bind_address = optarg;
205 			break;
206 		case 'u':
207 			pwd = getpwnam(optarg);
208 			if (pwd == NULL && isdigit(optarg[0])) {
209 				set_uid = atoi(optarg);
210 				if ((pwd = getpwuid(set_uid)) == NULL)
211 					break;
212 			}
213 			if (pwd == NULL)
214 				error("no such user (%s) for -u option",
215 				    optarg);
216 			else {
217 				set_uid = pwd->pw_uid;
218 				if (set_gid == 0)
219 					set_gid = pwd->pw_gid;
220 			}
221 			break;
222 		case 'g':
223 			grp = getgrnam(optarg);
224 			if (grp == NULL && isdigit(optarg[0])) {
225 				set_gid = atoi(optarg);
226 				break;
227 			}
228 			grp = getgrnam(optarg);
229 			if (!grp)
230 				error("no such group (%s) for -g option", optarg);
231 			else
232 				set_gid = grp->gr_gid;
233 			break;
234 		case 'c':
235 			charset_name = optarg;
236 			charset_sep = " , ";
237 			break;
238 		case 'l':	/* Use the Syslog daemon for logging */
239 			syslog_flag++;
240 			break;
241 		case 'o':
242 			opsys_name = "OTHER";
243 			break;
244 		case 'e':
245 			unknown_flag = 1;
246 			break;
247 		case 'n':
248 			number_flag = 1;
249 			break;
250 		case 'v':	/* Be verbose */
251 			verbose_flag++;
252 			break;
253 		case 'd':	/* Enable debugging */
254 			debug_flag++;
255 			break;
256 		case 'm':	/* Enable multiline queries */
257 			multi_flag++;
258 			break;
259 		case 'N':	/* Enable users ".noident" files */
260 			noident_flag++;
261 			break;
262 		case 'U':	/* Enable user ".ident" files */
263 			userident_flag++;
264 			break;
265 		default:
266 			usage();
267 		}
268 	}
269 
270 	/*
271 	 * Do the special handling needed for the "-b" flag
272 	 */
273 	if (background_flag == 1) {
274 		struct sockaddr_in addr;
275 		struct servent *sp;
276 		int	fd;
277 
278 		if (fork())
279 			exit(0);
280 
281 		close(0);
282 		close(1);
283 		close(2);
284 
285 		if (fork())
286 			exit(0);
287 
288 		fd = socket(AF_INET, SOCK_STREAM, 0);
289 		if (fd == -1)
290 			error("main: socket");
291 
292 		if (fd != 0)
293 			dup2(fd, 0);
294 
295 		memset(&addr, 0, sizeof(addr));
296 
297 		addr.sin_len = sizeof(struct sockaddr_in);
298 		addr.sin_family = AF_INET;
299 		if (bind_address == NULL)
300 			addr.sin_addr.s_addr = htonl(INADDR_ANY);
301 		else {
302 			if (inet_aton(bind_address, &addr.sin_addr) == 0) {
303 				struct hostent *hp;
304 
305 				hp = gethostbyname(bind_address);
306 				if (!hp)
307 					error("no such address (%s) for -a switch",
308 					    bind_address);
309 				memcpy(&addr.sin_addr, hp->h_addr,
310 				    sizeof(addr.sin_addr));
311 			}
312 		}
313 
314 		if (isdigit(portno[0]))
315 			addr.sin_port = htons(atoi(portno));
316 		else {
317 			sp = getservbyname(portno, "tcp");
318 			if (sp == NULL)
319 				error("main: getservbyname: %s", portno);
320 			addr.sin_port = sp->s_port;
321 		}
322 
323 		if (bind(0, (struct sockaddr *) &addr, sizeof(addr)) < 0)
324 			error("main: bind");
325 
326 		if (listen(0, 3) < 0)
327 			error("main: listen");
328 	}
329 	if (set_gid) {
330 		if (setegid(set_gid) == -1)
331 			error("main: setgid");
332 		if (setgid(set_gid) == -1)
333 			error("main: setgid");
334 	}
335 	if (set_uid) {
336 		if (seteuid(set_uid) == -1)
337 			error("main: setuid");
338 		if (setuid(set_uid) == -1)
339 			error("main: setuid");
340 	}
341 	/*
342 	 * Do some special handling if the "-b" or "-w" flags are used
343 	 */
344 	if (background_flag) {
345 		int	nfds, fd;
346 		struct	pollfd pfd[1];
347 
348 		/*
349 		 * Loop and dispatch client handling processes
350 		 */
351 		do {
352 			/*
353 			 * Terminate if we've been idle for 'timeout' seconds
354 			 */
355 			if (background_flag == 2 && timeout) {
356 				signal(SIGALRM, alarm_handler);
357 				alarm(timeout);
358 			}
359 
360 			/*
361 			 * Wait for a connection request to occur.
362 			 * Ignore EINTR (Interrupted System Call).
363 			 */
364 			do {
365 				if (alarm_fired) {
366 					if (syslog_flag)
367 						syslog(LOG_DEBUG,
368 						    "SIGALRM triggered, exiting");
369 					exit(0);
370 				}
371 
372 				pfd[0].fd = 0;
373 				pfd[0].events = POLLIN;
374 
375 				if (timeout)
376 					nfds = poll(pfd, 1, timeout * 1000);
377 				else
378 					nfds = poll(pfd, 1, INFTIM);
379 			} while (nfds < 0 && errno == EINTR);
380 
381 			/*
382 			 * An error occurred in select? Just die
383 			 */
384 			if (nfds < 0)
385 				error("main: select");
386 
387 			/*
388 			 * Timeout limit reached. Exit nicely
389 			 */
390 			if (nfds == 0)
391 				exit(0);
392 
393 			/*
394 			 * Disable the alarm timeout
395 			 */
396 			alarm(0);
397 
398 			/*
399 			 * Accept the new client
400 			 */
401 			fd = accept(0, NULL, NULL);
402 			if (fd == -1)
403 				error("main: accept. errno = %d", errno);
404 
405 			/*
406 			 * And fork, then close the fd if we are the parent.
407 			 */
408 			child_pid = fork();
409 			while (waitpid(-1, NULL, WNOHANG) > 0)
410 				;
411 		} while (child_pid && (close(fd), 1));
412 
413 		/*
414 		 * We are now in child, the parent has returned to "do" above.
415 		 */
416 		if (dup2(fd, 0) == -1)
417 			error("main: dup2: failed fd 0");
418 
419 		if (dup2(fd, 1) == -1)
420 			error("main: dup2: failed fd 1");
421 
422 		if (dup2(fd, 2) == -1)
423 			error("main: dup2: failed fd 2");
424 	}
425 	/*
426 	 * Get foreign internet address
427 	 */
428 	len = sizeof(sa);
429 	if (getpeername(0, (struct sockaddr *) &sa, &len) == -1) {
430 		/*
431 		 * A user has tried to start us from the command line or
432 		 * the network link died, in which case this message won't
433 		 * reach to other end anyway, so lets give the poor user some
434 		 * errors.
435 		 */
436 		perror("in.identd: getpeername()");
437 		exit(1);
438 	}
439 	if (sa.ss_family == AF_INET6) {
440 		sin6 = (struct sockaddr_in6 *)&sa;
441 		faddr6 = sin6->sin6_addr;
442 	} else {
443 		sin = (struct sockaddr_in *)&sa;
444 		faddr = sin->sin_addr;
445 	}
446 
447 	/*
448 	 * Open the connection to the Syslog daemon if requested
449 	 */
450 	if (syslog_flag)
451 		syslog(LOG_INFO, "Connection from %s", gethost(&sa));
452 
453 	/*
454 	 * Get local internet address
455 	 */
456 	len = sizeof(sa2);
457 	if (getsockname(0, (struct sockaddr *) &sa2, &len) == -1) {
458 		/*
459 		 * We can just die here, because if this fails then the
460 		 * network has died and we haven't got anyone to return
461 		 * errors to.
462 		 */
463 		exit(1);
464 	}
465 	/* are we V4 or V6? */
466 	if (sa2.ss_family == AF_INET6) {
467 		sin6 = (struct sockaddr_in6 *)&sa2;
468 		laddr6 = sin6->sin6_addr;
469 		/*
470 		 * Get the local/foreign port pair from the luser
471 		 */
472 		parse6(STDIN_FILENO, (struct sockaddr_in6 *)&sa2,
473 		    (struct sockaddr_in6 *)&sa);
474 	} else {
475 		sin = (struct sockaddr_in *)&sa2;
476 		laddr = sin->sin_addr;
477 		/*
478 		 * Get the local/foreign port pair from the luser
479 		 */
480 		parse(STDIN_FILENO, &laddr, &faddr);
481 	}
482 
483 	exit(0);
484 }
485 
486 void
error(char * fmt,...)487 error(char *fmt, ...)
488 {
489 	va_list ap, ap2;
490 
491 	va_start(ap, fmt);
492 
493 	if (syslog_flag) {
494 		va_copy(ap2, ap);
495 		syslog(LOG_ERR, fmt, ap2);
496 		va_end(ap2);
497 	}
498 	if (debug_flag) {
499 		fprintf(stderr, "%d , %d : ERROR : X-DBG : ", lport, fport);
500 		fprintf(stderr, fmt, ap);
501 		perror(": ");
502 	} else
503 		printf("%d , %d : ERROR : UNKNOWN-ERROR\r\n", lport, fport);
504 	va_end(ap);
505 	exit(1);
506 }
507