xref: /freebsd/usr.sbin/inetd/builtins.c (revision 5521ff5a4d1929056e7ffc982fac3341ca54df7c)
1 /*-
2  * Copyright (c) 1983, 1991, 1993, 1994
3  *	The Regents of the University of California.  All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24  * SUCH DAMAGE.
25  *
26  * $FreeBSD$
27  *
28  */
29 
30 #include <sys/filio.h>
31 #include <sys/ioccom.h>
32 #include <sys/param.h>
33 #include <sys/stat.h>
34 #include <sys/socket.h>
35 #include <sys/sysctl.h>
36 #include <sys/ucred.h>
37 #include <sys/uio.h>
38 #include <sys/utsname.h>
39 
40 #include <ctype.h>
41 #include <err.h>
42 #include <errno.h>
43 #include <fcntl.h>
44 #include <limits.h>
45 #include <pwd.h>
46 #include <signal.h>
47 #include <stdlib.h>
48 #include <string.h>
49 #include <sysexits.h>
50 #include <syslog.h>
51 #include <unistd.h>
52 
53 #include "inetd.h"
54 
55 void		chargen_dg __P((int, struct servtab *));
56 void		chargen_stream __P((int, struct servtab *));
57 void		daytime_dg __P((int, struct servtab *));
58 void		daytime_stream __P((int, struct servtab *));
59 void		discard_dg __P((int, struct servtab *));
60 void		discard_stream __P((int, struct servtab *));
61 void		echo_dg __P((int, struct servtab *));
62 void		echo_stream __P((int, struct servtab *));
63 static int	getline __P((int, char *, int));
64 void		iderror __P((int, int, int, const char *));
65 void		ident_stream __P((int, struct servtab *));
66 void		initring __P((void));
67 unsigned long	machtime __P((void));
68 void		machtime_dg __P((int, struct servtab *));
69 void		machtime_stream __P((int, struct servtab *));
70 
71 char ring[128];
72 char *endring;
73 
74 
75 struct biltin biltins[] = {
76 	/* Echo received data */
77 	{ "echo",	SOCK_STREAM,	1, -1,	echo_stream },
78 	{ "echo",	SOCK_DGRAM,	0, 1,	echo_dg },
79 
80 	/* Internet /dev/null */
81 	{ "discard",	SOCK_STREAM,	1, -1,	discard_stream },
82 	{ "discard",	SOCK_DGRAM,	0, 1,	discard_dg },
83 
84 	/* Return 32 bit time since 1900 */
85 	{ "time",	SOCK_STREAM,	0, -1,	machtime_stream },
86 	{ "time",	SOCK_DGRAM,	0, 1,	machtime_dg },
87 
88 	/* Return human-readable time */
89 	{ "daytime",	SOCK_STREAM,	0, -1,	daytime_stream },
90 	{ "daytime",	SOCK_DGRAM,	0, 1,	daytime_dg },
91 
92 	/* Familiar character generator */
93 	{ "chargen",	SOCK_STREAM,	1, -1,	chargen_stream },
94 	{ "chargen",	SOCK_DGRAM,	0, 1,	chargen_dg },
95 
96 	{ "tcpmux",	SOCK_STREAM,	1, -1,	(bi_fn_t *)tcpmux },
97 
98 	{ "auth",	SOCK_STREAM,	1, -1,	ident_stream },
99 
100 	{ NULL,		0,		0, 0,	NULL }
101 };
102 
103 /*
104  * RFC864 Character Generator Protocol. Generates character data without
105  * any regard for input.
106  */
107 
108 void
109 initring()
110 {
111 	int i;
112 
113 	endring = ring;
114 
115 	for (i = 0; i <= 128; ++i)
116 		if (isprint(i))
117 			*endring++ = i;
118 }
119 
120 /* ARGSUSED */
121 void
122 chargen_dg(s, sep)		/* Character generator */
123 	int s;
124 	struct servtab *sep;
125 {
126 	struct sockaddr_storage ss;
127 	static char *rs;
128 	int len;
129 	socklen_t size;
130 	char text[LINESIZ+2];
131 
132 	if (endring == 0) {
133 		initring();
134 		rs = ring;
135 	}
136 
137 	size = sizeof(ss);
138 	if (recvfrom(s, text, sizeof(text), 0,
139 		     (struct sockaddr *)&ss, &size) < 0)
140 		return;
141 
142 	if (check_loop((struct sockaddr *)&ss, sep))
143 		return;
144 
145 	if ((len = endring - rs) >= LINESIZ)
146 		memmove(text, rs, LINESIZ);
147 	else {
148 		memmove(text, rs, len);
149 		memmove(text + len, ring, LINESIZ - len);
150 	}
151 	if (++rs == endring)
152 		rs = ring;
153 	text[LINESIZ] = '\r';
154 	text[LINESIZ + 1] = '\n';
155 	(void) sendto(s, text, sizeof(text), 0, (struct sockaddr *)&ss, size);
156 }
157 
158 /* ARGSUSED */
159 void
160 chargen_stream(s, sep)		/* Character generator */
161 	int s;
162 	struct servtab *sep;
163 {
164 	int len;
165 	char *rs, text[LINESIZ+2];
166 
167 	inetd_setproctitle(sep->se_service, s);
168 
169 	if (!endring) {
170 		initring();
171 		rs = ring;
172 	}
173 
174 	text[LINESIZ] = '\r';
175 	text[LINESIZ + 1] = '\n';
176 	for (rs = ring;;) {
177 		if ((len = endring - rs) >= LINESIZ)
178 			memmove(text, rs, LINESIZ);
179 		else {
180 			memmove(text, rs, len);
181 			memmove(text + len, ring, LINESIZ - len);
182 		}
183 		if (++rs == endring)
184 			rs = ring;
185 		if (write(s, text, sizeof(text)) != sizeof(text))
186 			break;
187 	}
188 	exit(0);
189 }
190 
191 /*
192  * RFC867 Daytime Protocol. Sends the current date and time as an ascii
193  * character string without any regard for input.
194  */
195 
196 /* ARGSUSED */
197 void
198 daytime_dg(s, sep)		/* Return human-readable time of day */
199 	int s;
200 	struct servtab *sep;
201 {
202 	char buffer[256];
203 	time_t now;
204 	struct sockaddr_storage ss;
205 	socklen_t size;
206 
207 	now = time((time_t *) 0);
208 
209 	size = sizeof(ss);
210 	if (recvfrom(s, buffer, sizeof(buffer), 0,
211 		     (struct sockaddr *)&ss, &size) < 0)
212 		return;
213 
214 	if (check_loop((struct sockaddr *)&ss, sep))
215 		return;
216 
217 	(void) sprintf(buffer, "%.24s\r\n", ctime(&now));
218 	(void) sendto(s, buffer, strlen(buffer), 0,
219 		      (struct sockaddr *)&ss, size);
220 }
221 
222 /* ARGSUSED */
223 void
224 daytime_stream(s, sep)		/* Return human-readable time of day */
225 	int s;
226 	struct servtab *sep;
227 {
228 	char buffer[256];
229 	time_t now;
230 
231 	now = time((time_t *) 0);
232 
233 	(void) sprintf(buffer, "%.24s\r\n", ctime(&now));
234 	(void) send(s, buffer, strlen(buffer), MSG_EOF);
235 }
236 
237 /*
238  * RFC863 Discard Protocol. Any data received is thrown away and no response
239  * is sent.
240  */
241 
242 /* ARGSUSED */
243 void
244 discard_dg(s, sep)		/* Discard service -- ignore data */
245 	int s;
246 	struct servtab *sep;
247 {
248 	char buffer[BUFSIZE];
249 
250 	(void) read(s, buffer, sizeof(buffer));
251 }
252 
253 /* ARGSUSED */
254 void
255 discard_stream(s, sep)		/* Discard service -- ignore data */
256 	int s;
257 	struct servtab *sep;
258 {
259 	int ret;
260 	char buffer[BUFSIZE];
261 
262 	inetd_setproctitle(sep->se_service, s);
263 	while (1) {
264 		while ((ret = read(s, buffer, sizeof(buffer))) > 0)
265 			;
266 		if (ret == 0 || errno != EINTR)
267 			break;
268 	}
269 	exit(0);
270 }
271 
272 /*
273  * RFC862 Echo Protocol. Any data received is sent back to the sender as
274  * received.
275  */
276 
277 /* ARGSUSED */
278 void
279 echo_dg(s, sep)			/* Echo service -- echo data back */
280 	int s;
281 	struct servtab *sep;
282 {
283 	char buffer[65536]; /* Should be sizeof(max datagram). */
284 	int i;
285 	socklen_t size;
286 	struct sockaddr_storage ss;
287 
288 	size = sizeof(ss);
289 	if ((i = recvfrom(s, buffer, sizeof(buffer), 0,
290 			  (struct sockaddr *)&ss, &size)) < 0)
291 		return;
292 
293 	if (check_loop((struct sockaddr *)&ss, sep))
294 		return;
295 
296 	(void) sendto(s, buffer, i, 0, (struct sockaddr *)&ss, size);
297 }
298 
299 /* ARGSUSED */
300 void
301 echo_stream(s, sep)		/* Echo service -- echo data back */
302 	int s;
303 	struct servtab *sep;
304 {
305 	char buffer[BUFSIZE];
306 	int i;
307 
308 	inetd_setproctitle(sep->se_service, s);
309 	while ((i = read(s, buffer, sizeof(buffer))) > 0 &&
310 	    write(s, buffer, i) > 0)
311 		;
312 	exit(0);
313 }
314 
315 /*
316  * RFC1413 Identification Protocol. Given a TCP port number pair, return a
317  * character string which identifies the owner of that connection on the
318  * server's system. Extended to allow for ~/.fakeid support and ~/.noident
319  * support.
320  */
321 
322 /* RFC 1413 says the following are the only errors you can return. */
323 #define ID_INVALID	"INVALID-PORT"	/* Port number improperly specified. */
324 #define ID_NOUSER	"NO-USER"	/* Port not in use/not identifable. */
325 #define ID_HIDDEN	"HIDDEN-USER"	/* Hiden at user's request. */
326 #define ID_UNKNOWN	"UNKNOWN-ERROR"	/* Everything else. */
327 
328 /* ARGSUSED */
329 void
330 iderror(lport, fport, s, er)	/* Generic ident_stream error-sending func */
331 	int lport, fport, s;
332 	const char *er;
333 {
334 	char *p;
335 
336 	asprintf(&p, "%d , %d : ERROR : %s\r\n", lport, fport, er);
337 	if (p == NULL) {
338 		syslog(LOG_ERR, "asprintf: %m");
339 		exit(EX_OSERR);
340 	}
341 	send(s, p, strlen(p), MSG_EOF);
342 	free(p);
343 
344 	exit(0);
345 }
346 
347 /* ARGSUSED */
348 void
349 ident_stream(s, sep)		/* Ident service (AKA "auth") */
350 	int s;
351 	struct servtab *sep;
352 {
353 	struct utsname un;
354 	struct stat sb;
355 	struct sockaddr_in sin4[2];
356 #ifdef INET6
357 	struct sockaddr_in6 sin6[2];
358 #endif
359 	struct sockaddr_storage ss[2];
360 	struct xucred uc;
361 	struct timeval tv = {
362 		10,
363 		0
364 	}, to;
365 	struct passwd *pw = NULL;
366 	fd_set fdset;
367 	char buf[BUFSIZE], *p, **av, *osname = NULL, e;
368 	char idbuf[MAXLOGNAME] = ""; /* Big enough to hold uid in decimal. */
369 	socklen_t socklen;
370 	ssize_t ssize;
371 	size_t size, bufsiz;
372 	int c, fflag = 0, nflag = 0, rflag = 0, argc = 0;
373 	int gflag = 0, iflag = 0, Fflag = 0, getcredfail = 0, onreadlen;
374 	u_short lport, fport;
375 
376 	inetd_setproctitle(sep->se_service, s);
377 	/*
378 	 * Reset getopt() since we are a fork() but not an exec() from
379 	 * a parent which used getopt() already.
380 	 */
381 	optind = 1;
382 	optreset = 1;
383 	/*
384 	 * Take the internal argument vector and count it out to make an
385 	 * argument count for getopt. This can be used for any internal
386 	 * service to read arguments and use getopt() easily.
387 	 */
388 	for (av = sep->se_argv; *av; av++)
389 		argc++;
390 	if (argc) {
391 		int sec, usec;
392 		size_t i;
393 		u_int32_t rnd32;
394 
395 		while ((c = getopt(argc, sep->se_argv, "d:fFgino:rt:")) != -1)
396 			switch (c) {
397 			case 'd':
398 				if (!gflag)
399 					strlcpy(idbuf, optarg, sizeof(idbuf));
400 				break;
401 			case 'f':
402 				fflag = 1;
403 				break;
404 			case 'F':
405 				fflag = 1;
406 				Fflag=1;
407 				break;
408 			case 'g':
409 				gflag = 1;
410 				rnd32 = 0;	/* Shush, compiler. */
411 				/*
412 				 * The number of bits in "rnd32" divided
413 				 * by the number of bits needed per iteration
414 				 * gives a more optimal way to reload the
415 				 * random number only when necessary.
416 				 *
417 				 * 32 bits from arc4random corrisponds to
418 				 * about 6 base-36 digits, so we reseed evey 6.
419 				 */
420 				for (i = 0; i < sizeof(idbuf) - 1; i++) {
421 					static const char *const base36 =
422 					    "0123456789"
423 					    "abcdefghijklmnopqrstuvwxyz";
424 					if (i % 6 == 0)
425 						rnd32 = arc4random();
426 					idbuf[i] = base36[rnd32 % 36];
427 					rnd32 /= 36;
428 				}
429 				idbuf[i] = '\0';
430 				break;
431 			case 'i':
432 				iflag = 1;
433 				break;
434 			case 'n':
435 				nflag = 1;
436 				break;
437 			case 'o':
438 				osname = optarg;
439 				break;
440 			case 'r':
441 				rflag = 1;
442 				break;
443 			case 't':
444 				switch (sscanf(optarg, "%d.%d", &sec, &usec)) {
445 				case 2:
446 					tv.tv_usec = usec;
447 					/* FALLTHROUGH */
448 				case 1:
449 					tv.tv_sec = sec;
450 					break;
451 				default:
452 					if (debug)
453 						warnx("bad -t argument");
454 					break;
455 				}
456 				break;
457 			default:
458 				break;
459 			}
460 	}
461 	if (osname == NULL) {
462 		if (uname(&un) == -1)
463 			iderror(0, 0, s, ID_UNKNOWN);
464 		osname = un.sysname;
465 	}
466 
467 	/*
468 	 * We're going to prepare for and execute reception of a
469 	 * packet of data from the user. The data is in the format
470 	 * "local_port , foreign_port\r\n" (with local being the
471 	 * server's port and foreign being the client's.)
472 	 */
473 	gettimeofday(&to, NULL);
474 	to.tv_sec += tv.tv_sec;
475 	to.tv_usec += tv.tv_usec;
476 	if (to.tv_usec >= 1000000) {
477 		to.tv_usec -= 1000000;
478 		to.tv_sec++;
479 	}
480 
481 	size = 0;
482 	bufsiz = sizeof(buf) - 1;
483 	FD_ZERO(&fdset);
484  	while (bufsiz > 0) {
485 		gettimeofday(&tv, NULL);
486 		tv.tv_sec = to.tv_sec - tv.tv_sec;
487 		tv.tv_usec = to.tv_usec - tv.tv_usec;
488 		if (tv.tv_usec < 0) {
489 			tv.tv_usec += 1000000;
490 			tv.tv_sec--;
491 		}
492 		if (tv.tv_sec < 0)
493 			break;
494 		FD_SET(s, &fdset);
495 		if (select(s + 1, &fdset, NULL, NULL, &tv) == -1)
496 			iderror(0, 0, s, ID_UNKNOWN);
497 		if (ioctl(s, FIONREAD, &onreadlen) == -1)
498 			iderror(0, 0, s, ID_UNKNOWN);
499 		if ((size_t)onreadlen > bufsiz)
500 			onreadlen = bufsiz;
501 		ssize = read(s, &buf[size], (size_t)onreadlen);
502 		if (ssize == -1)
503 			iderror(0, 0, s, ID_UNKNOWN);
504 		else if (ssize == 0)
505 			break;
506 		bufsiz -= ssize;
507 		size += ssize;
508 		if (memchr(&buf[size - ssize], '\n', ssize) != NULL)
509 			break;
510  	}
511 	buf[size] = '\0';
512 	/* Read two characters, and check for a delimiting character */
513 	if (sscanf(buf, "%hu , %hu%c", &lport, &fport, &e) != 3 || isdigit(e))
514 		iderror(0, 0, s, ID_INVALID);
515 
516 	/* Send garbage? */
517 	if (gflag)
518 		goto printit;
519 
520 	/*
521 	 * If not "real" (-r), send a HIDDEN-USER error for everything.
522 	 * If -d is used to set a fallback username, this is used to
523 	 * override it, and the fallback is returned instead.
524 	 */
525 	if (!rflag) {
526 		if (*idbuf == '\0')
527 			iderror(lport, fport, s, ID_HIDDEN);
528 		goto printit;
529 	}
530 
531 	/*
532 	 * We take the input and construct an array of two sockaddr_ins
533 	 * which contain the local address information and foreign
534 	 * address information, respectively, used to look up the
535 	 * credentials for the socket (which are returned by the
536 	 * sysctl "net.inet.tcp.getcred" when we call it.)
537 	 */
538 	socklen = sizeof(ss[0]);
539 	if (getsockname(s, (struct sockaddr *)&ss[0], &socklen) == -1)
540 		iderror(lport, fport, s, ID_UNKNOWN);
541 	socklen = sizeof(ss[1]);
542 	if (getpeername(s, (struct sockaddr *)&ss[1], &socklen) == -1)
543 		iderror(lport, fport, s, ID_UNKNOWN);
544 	if (ss[0].ss_family != ss[1].ss_family)
545 		iderror(lport, fport, s, ID_UNKNOWN);
546 	size = sizeof(uc);
547 	switch (ss[0].ss_family) {
548 	case AF_INET:
549 		sin4[0] = *(struct sockaddr_in *)&ss[0];
550 		sin4[0].sin_port = htons(lport);
551 		sin4[1] = *(struct sockaddr_in *)&ss[1];
552 		sin4[1].sin_port = htons(fport);
553 		if (sysctlbyname("net.inet.tcp.getcred", &uc, &size, sin4,
554 				 sizeof(sin4)) == -1)
555 			getcredfail = errno;
556 		break;
557 #ifdef INET6
558 	case AF_INET6:
559 		sin6[0] = *(struct sockaddr_in6 *)&ss[0];
560 		sin6[0].sin6_port = htons(lport);
561 		sin6[1] = *(struct sockaddr_in6 *)&ss[1];
562 		sin6[1].sin6_port = htons(fport);
563 		if (sysctlbyname("net.inet6.tcp6.getcred", &uc, &size, sin6,
564 				 sizeof(sin6)) == -1)
565 			getcredfail = errno;
566 		break;
567 #endif
568 	default: /* should not reach here */
569 		getcredfail = EAFNOSUPPORT;
570 		break;
571 	}
572 	if (getcredfail != 0) {
573 		if (*idbuf == '\0')
574 			iderror(lport, fport, s,
575 			    getcredfail == ENOENT ? ID_NOUSER : ID_UNKNOWN);
576 		goto printit;
577 	}
578 
579 	/* Look up the pw to get the username and home directory*/
580 	errno = 0;
581 	pw = getpwuid(uc.cr_uid);
582 	if (pw == NULL)
583 		iderror(lport, fport, s, errno == 0 ? ID_NOUSER : ID_UNKNOWN);
584 
585 	if (iflag)
586 		snprintf(idbuf, sizeof(idbuf), "%u", (unsigned)pw->pw_uid);
587 	else
588 		strlcpy(idbuf, pw->pw_name, sizeof(idbuf));
589 
590 	/*
591 	 * If enabled, we check for a file named ".noident" in the user's
592 	 * home directory. If found, we return HIDDEN-USER.
593 	 */
594 	if (nflag) {
595 		if (asprintf(&p, "%s/.noident", pw->pw_dir) == -1)
596 			iderror(lport, fport, s, ID_UNKNOWN);
597 		if (lstat(p, &sb) == 0) {
598 			free(p);
599 			iderror(lport, fport, s, ID_HIDDEN);
600 		}
601 		free(p);
602 	}
603 
604 	/*
605 	 * Here, if enabled, we read a user's ".fakeid" file in their
606 	 * home directory. It consists of a line containing the name
607 	 * they want.
608 	 */
609 	if (fflag) {
610 		int fakeid_fd;
611 
612 		/*
613 		 * Here we set ourself to effectively be the user, so we don't
614 		 * open any files we have no permission to open, especially
615 		 * symbolic links to sensitive root-owned files or devices.
616 		 */
617 		if (initgroups(pw->pw_name, pw->pw_gid) == -1)
618 			iderror(lport, fport, s, ID_UNKNOWN);
619 		if (seteuid(pw->pw_uid) == -1)
620 			iderror(lport, fport, s, ID_UNKNOWN);
621 		/*
622 		 * We can't stat() here since that would be a race
623 		 * condition.
624 		 * Therefore, we open the file we have permissions to open
625 		 * and if it's not a regular file, we close it and end up
626 		 * returning the user's real username.
627 		 */
628 		if (asprintf(&p, "%s/.fakeid", pw->pw_dir) == -1)
629 			iderror(lport, fport, s, ID_UNKNOWN);
630 		fakeid_fd = open(p, O_RDONLY | O_NONBLOCK);
631 		free(p);
632 		if (fakeid_fd == -1 || fstat(fakeid_fd, &sb) == -1 ||
633 		    !S_ISREG(sb.st_mode))
634 			goto fakeid_fail;
635 
636 		if ((ssize = read(fakeid_fd, buf, sizeof(buf) - 1)) < 0)
637 			goto fakeid_fail;
638 		buf[ssize] = '\0';
639 
640 		/*
641 		 * Usually, the file will have the desired identity
642 		 * in the form "identity\n". Allow for leading white
643 		 * space and trailing white space/end of line.
644 		 */
645 		p = buf;
646 		p += strspn(p, " \t");
647 		p[strcspn(p, " \t\r\n")] = '\0';
648 		if (strlen(p) > MAXLOGNAME - 1) /* Too long (including nul)? */
649 			p[MAXLOGNAME - 1] = '\0';
650 
651 		/*
652 		 * If the name is a zero-length string or matches it
653 		 * the id or name of another user (unless permitted by -F)
654 		 * then it is invalid.
655 		 */
656 		if (*p == '\0')
657 			goto fakeid_fail;
658 		if (!Fflag) {
659 			if (iflag) {
660 				if (p[strspn(p, "0123456789")] == '\0' &&
661 				    getpwuid(atoi(p)) != NULL)
662 					goto fakeid_fail;
663 			} else {
664 				if (getpwnam(p) != NULL)
665 					goto fakeid_fail;
666 			}
667 		}
668 
669 		strlcpy(idbuf, p, sizeof(idbuf));
670 
671 fakeid_fail:
672 		if (fakeid_fd != -1)
673 			close(fakeid_fd);
674 	}
675 
676 printit:
677 	/* Finally, we make and send the reply. */
678 	if (asprintf(&p, "%d , %d : USERID : %s : %s\r\n", lport, fport, osname,
679 	    idbuf) == -1) {
680 		syslog(LOG_ERR, "asprintf: %m");
681 		exit(EX_OSERR);
682 	}
683 	send(s, p, strlen(p), MSG_EOF);
684 	free(p);
685 
686 	exit(0);
687 }
688 
689 /*
690  * RFC738 Time Server.
691  * Return a machine readable date and time, in the form of the
692  * number of seconds since midnight, Jan 1, 1900.  Since gettimeofday
693  * returns the number of seconds since midnight, Jan 1, 1970,
694  * we must add 2208988800 seconds to this figure to make up for
695  * some seventy years Bell Labs was asleep.
696  */
697 
698 unsigned long
699 machtime()
700 {
701 	struct timeval tv;
702 
703 	if (gettimeofday(&tv, (struct timezone *)NULL) < 0) {
704 		if (debug)
705 			warnx("unable to get time of day");
706 		return (0L);
707 	}
708 #define	OFFSET ((u_long)25567 * 24*60*60)
709 	return (htonl((long)(tv.tv_sec + OFFSET)));
710 #undef OFFSET
711 }
712 
713 /* ARGSUSED */
714 void
715 machtime_dg(s, sep)
716 	int s;
717 	struct servtab *sep;
718 {
719 	unsigned long result;
720 	struct sockaddr_storage ss;
721 	socklen_t size;
722 
723 	size = sizeof(ss);
724 	if (recvfrom(s, (char *)&result, sizeof(result), 0,
725 		     (struct sockaddr *)&ss, &size) < 0)
726 		return;
727 
728 	if (check_loop((struct sockaddr *)&ss, sep))
729 		return;
730 
731 	result = machtime();
732 	(void) sendto(s, (char *) &result, sizeof(result), 0,
733 		      (struct sockaddr *)&ss, size);
734 }
735 
736 /* ARGSUSED */
737 void
738 machtime_stream(s, sep)
739 	int s;
740 	struct servtab *sep;
741 {
742 	unsigned long result;
743 
744 	result = machtime();
745 	(void) send(s, (char *) &result, sizeof(result), MSG_EOF);
746 }
747 
748 /*
749  * RFC1078 TCP Port Service Multiplexer (TCPMUX). Service connections to
750  * services based on the service name sent.
751  *
752  *  Based on TCPMUX.C by Mark K. Lottor November 1988
753  *  sri-nic::ps:<mkl>tcpmux.c
754  */
755 
756 #define MAX_SERV_LEN	(256+2)		/* 2 bytes for \r\n */
757 #define strwrite(fd, buf)	(void) write(fd, buf, sizeof(buf)-1)
758 
759 static int		/* # of characters upto \r,\n or \0 */
760 getline(fd, buf, len)
761 	int fd;
762 	char *buf;
763 	int len;
764 {
765 	int count = 0, n;
766 	struct sigaction sa;
767 
768 	sa.sa_flags = 0;
769 	sigemptyset(&sa.sa_mask);
770 	sa.sa_handler = SIG_DFL;
771 	sigaction(SIGALRM, &sa, (struct sigaction *)0);
772 	do {
773 		alarm(10);
774 		n = read(fd, buf, len-count);
775 		alarm(0);
776 		if (n == 0)
777 			return (count);
778 		if (n < 0)
779 			return (-1);
780 		while (--n >= 0) {
781 			if (*buf == '\r' || *buf == '\n' || *buf == '\0')
782 				return (count);
783 			count++;
784 			buf++;
785 		}
786 	} while (count < len);
787 	return (count);
788 }
789 
790 struct servtab *
791 tcpmux(s)
792 	int s;
793 {
794 	struct servtab *sep;
795 	char service[MAX_SERV_LEN+1];
796 	int len;
797 
798 	/* Get requested service name */
799 	if ((len = getline(s, service, MAX_SERV_LEN)) < 0) {
800 		strwrite(s, "-Error reading service name\r\n");
801 		return (NULL);
802 	}
803 	service[len] = '\0';
804 
805 	if (debug)
806 		warnx("tcpmux: someone wants %s", service);
807 
808 	/*
809 	 * Help is a required command, and lists available services,
810 	 * one per line.
811 	 */
812 	if (!strcasecmp(service, "help")) {
813 		for (sep = servtab; sep; sep = sep->se_next) {
814 			if (!ISMUX(sep))
815 				continue;
816 			(void)write(s,sep->se_service,strlen(sep->se_service));
817 			strwrite(s, "\r\n");
818 		}
819 		return (NULL);
820 	}
821 
822 	/* Try matching a service in inetd.conf with the request */
823 	for (sep = servtab; sep; sep = sep->se_next) {
824 		if (!ISMUX(sep))
825 			continue;
826 		if (!strcasecmp(service, sep->se_service)) {
827 			if (ISMUXPLUS(sep)) {
828 				strwrite(s, "+Go\r\n");
829 			}
830 			return (sep);
831 		}
832 	}
833 	strwrite(s, "-Service not available\r\n");
834 	return (NULL);
835 }
836