builtins.c revision 61099
1139825Simp/*-
241502Swpaul * Copyright (c) 1983, 1991, 1993, 1994
341502Swpaul *	The Regents of the University of California.  All rights reserved.
441502Swpaul *
541502Swpaul * Redistribution and use in source and binary forms, with or without
641502Swpaul * modification, are permitted provided that the following conditions
741502Swpaul * are met:
841502Swpaul * 1. Redistributions of source code must retain the above copyright
941502Swpaul *    notice, this list of conditions and the following disclaimer.
1041502Swpaul * 2. Redistributions in binary form must reproduce the above copyright
1141502Swpaul *    notice, this list of conditions and the following disclaimer in the
1241502Swpaul *    documentation and/or other materials provided with the distribution.
1341502Swpaul *
1441502Swpaul * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
1541502Swpaul * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1641502Swpaul * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1741502Swpaul * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
1841502Swpaul * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
1941502Swpaul * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2041502Swpaul * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2141502Swpaul * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2241502Swpaul * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2341502Swpaul * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2441502Swpaul * SUCH DAMAGE.
2541502Swpaul *
2641502Swpaul * $FreeBSD: head/usr.sbin/inetd/builtins.c 61099 2000-05-30 22:51:05Z green $
2741502Swpaul *
2841502Swpaul */
2941502Swpaul
3041502Swpaul#include <sys/filio.h>
3141502Swpaul#include <sys/ioccom.h>
3241502Swpaul#include <sys/param.h>
33122678Sobrien#include <sys/stat.h>
34122678Sobrien#include <sys/socket.h>
35122678Sobrien#include <sys/sysctl.h>
3641502Swpaul#include <sys/ucred.h>
3741502Swpaul#include <sys/uio.h>
3841502Swpaul#include <sys/utsname.h>
3941502Swpaul
4041502Swpaul#include <ctype.h>
4141502Swpaul#include <err.h>
4241502Swpaul#include <errno.h>
4341502Swpaul#include <limits.h>
4441502Swpaul#include <pwd.h>
4541502Swpaul#include <signal.h>
4641502Swpaul#include <stdlib.h>
47131503Sbms#include <string.h>
4841502Swpaul#include <sysexits.h>
4941502Swpaul#include <syslog.h>
5041502Swpaul#include <unistd.h>
5141502Swpaul
5241502Swpaul#include "inetd.h"
5341502Swpaul
5441502Swpaulextern int	 debug;
5541502Swpaulextern struct servtab *servtab;
5641502Swpaul
5741502Swpaulchar ring[128];
5841502Swpaulchar *endring;
5941502Swpaul
6041502Swpaulint check_loop __P((struct sockaddr *, struct servtab *sep));
6141502Swpaulvoid inetd_setproctitle __P((char *, int));
6241502Swpaul
6341502Swpaulstruct biltin biltins[] = {
6441502Swpaul	/* Echo received data */
6541502Swpaul	{ "echo",	SOCK_STREAM,	1, -1,	echo_stream },
6641502Swpaul	{ "echo",	SOCK_DGRAM,	0, 1,	echo_dg },
6741502Swpaul
6841502Swpaul	/* Internet /dev/null */
69129878Sphk	{ "discard",	SOCK_STREAM,	1, -1,	discard_stream },
7041502Swpaul	{ "discard",	SOCK_DGRAM,	0, 1,	discard_dg },
7141502Swpaul
7241502Swpaul	/* Return 32 bit time since 1970 */
7341502Swpaul	{ "time",	SOCK_STREAM,	0, -1,	machtime_stream },
7441502Swpaul	{ "time",	SOCK_DGRAM,	0, 1,	machtime_dg },
7541502Swpaul
7641502Swpaul	/* Return human-readable time */
77147256Sbrooks	{ "daytime",	SOCK_STREAM,	0, -1,	daytime_stream },
7841502Swpaul	{ "daytime",	SOCK_DGRAM,	0, 1,	daytime_dg },
7941502Swpaul
8041502Swpaul	/* Familiar character generator */
81131503Sbms	{ "chargen",	SOCK_STREAM,	1, -1,	chargen_stream },
82131503Sbms	{ "chargen",	SOCK_DGRAM,	0, 1,	chargen_dg },
8341502Swpaul
8449610Swpaul	{ "tcpmux",	SOCK_STREAM,	1, -1,	(void (*)())tcpmux },
8549610Swpaul
8649610Swpaul	{ "auth",	SOCK_STREAM,	1, -1,	ident_stream },
8741502Swpaul
8851432Swpaul	{ NULL }
8951432Swpaul};
9051432Swpaul
91119288Simp/*
92119288Simp * RFC864 Character Generator Protocol. Generates character data without
9341502Swpaul * any regard for input.
9441502Swpaul */
9541502Swpaul
9641502Swpaulvoid
9741502Swpaulinitring()
98113506Smdodd{
99113506Smdodd	int i;
10059758Speter
10159758Speter	endring = ring;
10251432Swpaul
10351432Swpaul	for (i = 0; i <= 128; ++i)
10451432Swpaul		if (isprint(i))
105110168Ssilby			*endring++ = i;
106110168Ssilby}
10741502Swpaul
10841502Swpaul/* ARGSUSED */
10941502Swpaulvoid
11041502Swpaulchargen_dg(s, sep)		/* Character generator */
11141502Swpaul	int s;
11241502Swpaul	struct servtab *sep;
11341502Swpaul{
11441502Swpaul	struct sockaddr_storage ss;
11562653Swpaul	static char *rs;
11662653Swpaul	int len;
117110170Ssilby	socklen_t size;
118110170Ssilby	char text[LINESIZ+2];
119110170Ssilby
120110170Ssilby	if (endring == 0) {
12144238Swpaul		initring();
12244238Swpaul		rs = ring;
12344238Swpaul	}
12444238Swpaul
12541502Swpaul	size = sizeof(ss);
12641502Swpaul	if (recvfrom(s, text, sizeof(text), 0,
12741502Swpaul		     (struct sockaddr *)&ss, &size) < 0)
128142407Simp		return;
129142407Simp
130142407Simp	if (check_loop((struct sockaddr *)&ss, sep))
13141502Swpaul		return;
132142407Simp
133142407Simp	if ((len = endring - rs) >= LINESIZ)
134142407Simp		memmove(text, rs, LINESIZ);
13541502Swpaul	else {
136142407Simp		memmove(text, rs, len);
137142407Simp		memmove(text + len, ring, LINESIZ - len);
138142407Simp	}
139142407Simp	if (++rs == endring)
140142407Simp		rs = ring;
141142407Simp	text[LINESIZ] = '\r';
142142407Simp	text[LINESIZ + 1] = '\n';
143142407Simp	(void) sendto(s, text, sizeof(text), 0, (struct sockaddr *)&ss, size);
144142407Simp}
145142407Simp
146142407Simp/* ARGSUSED */
147142407Simpvoid
148142407Simpchargen_stream(s, sep)		/* Character generator */
149142407Simp	int s;
150142407Simp	struct servtab *sep;
15141502Swpaul{
152110168Ssilby	int len;
153142407Simp	char *rs, text[LINESIZ+2];
154142407Simp
155110168Ssilby	inetd_setproctitle(sep->se_service, s);
156142407Simp
157142407Simp	if (!endring) {
158142407Simp		initring();
159142407Simp		rs = ring;
160142407Simp	}
16141502Swpaul
162142407Simp	text[LINESIZ] = '\r';
163142407Simp	text[LINESIZ + 1] = '\n';
164142407Simp	for (rs = ring;;) {
165142407Simp		if ((len = endring - rs) >= LINESIZ)
166142407Simp			memmove(text, rs, LINESIZ);
16741502Swpaul		else {
16849610Swpaul			memmove(text, rs, len);
16949610Swpaul			memmove(text + len, ring, LINESIZ - len);
17049610Swpaul		}
17149610Swpaul		if (++rs == endring)
17249610Swpaul			rs = ring;
17349610Swpaul		if (write(s, text, sizeof(text)) != sizeof(text))
17449610Swpaul			break;
17549610Swpaul	}
17649610Swpaul	exit(0);
17749610Swpaul}
17849610Swpaul
17949610Swpaul/*
18049610Swpaul * RFC867 Daytime Protocol. Sends the current date and time as an ascii
18149610Swpaul * character string without any regard for input.
18251432Swpaul */
18351432Swpaul
18451432Swpaul/* ARGSUSED */
18551432Swpaulvoid
18651432Swpauldaytime_dg(s, sep)		/* Return human-readable time of day */
18751432Swpaul	int s;
18851432Swpaul	struct servtab *sep;
18951432Swpaul{
19051432Swpaul	char buffer[256];
19151432Swpaul	time_t clock;
19249610Swpaul	struct sockaddr_storage ss;
19349610Swpaul	socklen_t size;
19449610Swpaul
19549610Swpaul	clock = time((time_t *) 0);
19651455Swpaul
19749610Swpaul	size = sizeof(ss);
19849610Swpaul	if (recvfrom(s, buffer, sizeof(buffer), 0,
19949610Swpaul		     (struct sockaddr *)&ss, &size) < 0)
20049610Swpaul		return;
20149610Swpaul
20249610Swpaul	if (check_loop((struct sockaddr *)&ss, sep))
203113506Smdodd		return;
20451473Swpaul
20549610Swpaul	(void) sprintf(buffer, "%.24s\r\n", ctime(&clock));
20641502Swpaul	(void) sendto(s, buffer, strlen(buffer), 0,
20741502Swpaul		      (struct sockaddr *)&ss, size);
208105221Sphk}
20941502Swpaul
21041502Swpaul/* ARGSUSED */
21141502Swpaulvoid
212105221Sphkdaytime_stream(s, sep)		/* Return human-readable time of day */
21341502Swpaul	int s;
21441502Swpaul	struct servtab *sep;
21541502Swpaul{
216105221Sphk	char buffer[256];
21741502Swpaul	time_t clock;
21841502Swpaul
21941502Swpaul	clock = time((time_t *) 0);
220105221Sphk
22141502Swpaul	(void) sprintf(buffer, "%.24s\r\n", ctime(&clock));
22241502Swpaul	(void) send(s, buffer, strlen(buffer), MSG_EOF);
22341502Swpaul}
224105221Sphk
22541502Swpaul/*
22641502Swpaul * RFC863 Discard Protocol. Any data received is thrown away and no response
22741502Swpaul * is sent.
228105221Sphk */
22941502Swpaul
23041502Swpaul/* ARGSUSED */
23141502Swpaulvoid
232105221Sphkdiscard_dg(s, sep)		/* Discard service -- ignore data */
23341502Swpaul	int s;
23441502Swpaul	struct servtab *sep;
23541502Swpaul{
236105221Sphk	char buffer[BUFSIZE];
23741502Swpaul
238110168Ssilby	(void) read(s, buffer, sizeof(buffer));
23941502Swpaul}
24041502Swpaul
24141502Swpaul/* ARGSUSED */
242102336Salfredvoid
243131503Sbmsdiscard_stream(s, sep)		/* Discard service -- ignore data */
24441502Swpaul	int s;
245131503Sbms	struct servtab *sep;
24641502Swpaul{
24741502Swpaul	int ret;
24841502Swpaul	char buffer[BUFSIZE];
24941502Swpaul
25041502Swpaul	inetd_setproctitle(sep->se_service, s);
25141502Swpaul	while (1) {
25241502Swpaul		while ((ret = read(s, buffer, sizeof(buffer))) > 0)
25341502Swpaul			;
25441502Swpaul		if (ret == 0 || errno != EINTR)
25541502Swpaul			break;
25641502Swpaul	}
25741502Swpaul	exit(0);
25841502Swpaul}
25941502Swpaul
260102336Salfred/*
261131503Sbms * RFC862 Echo Protocol. Any data received is sent back to the sender as
26241502Swpaul * received.
263131503Sbms */
26441502Swpaul
26541502Swpaul/* ARGSUSED */
26641502Swpaulvoid
26741502Swpaulecho_dg(s, sep)			/* Echo service -- echo data back */
268131503Sbms	int s;
26941502Swpaul	struct servtab *sep;
270131503Sbms{
27141502Swpaul	char buffer[BUFSIZE];
272131503Sbms	int i;
27341502Swpaul	socklen_t size;
27441502Swpaul	struct sockaddr_storage ss;
27541502Swpaul
27641502Swpaul	size = sizeof(ss);
27741502Swpaul	if ((i = recvfrom(s, buffer, sizeof(buffer), 0,
27841502Swpaul			  (struct sockaddr *)&ss, &size)) < 0)
279110168Ssilby		return;
28041502Swpaul
28141502Swpaul	if (check_loop((struct sockaddr *)&ss, sep))
28241502Swpaul		return;
28341502Swpaul
284102336Salfred	(void) sendto(s, buffer, i, 0, (struct sockaddr *)&ss, size);
285131503Sbms}
286131503Sbms
28741502Swpaul/* ARGSUSED */
288131503Sbmsvoid
28941502Swpaulecho_stream(s, sep)		/* Echo service -- echo data back */
290131503Sbms	int s;
29141502Swpaul	struct servtab *sep;
29241502Swpaul{
29341502Swpaul	char buffer[BUFSIZE];
29441502Swpaul	int i;
295131503Sbms
29641502Swpaul	inetd_setproctitle(sep->se_service, s);
29741502Swpaul	while ((i = read(s, buffer, sizeof(buffer))) > 0 &&
29841502Swpaul	    write(s, buffer, i) > 0)
299131503Sbms		;
30041502Swpaul	exit(0);
30141502Swpaul}
30241502Swpaul
30341502Swpaul/*
304131503Sbms * RFC1413 Identification Protocol. Given a TCP port number pair, return a
30541502Swpaul * character string which identifies the owner of that connection on the
30641502Swpaul * server's system. Extended to allow for ~/.fakeid support and ~/.noident
30741502Swpaul * support.
30841502Swpaul */
30941502Swpaul
310131503Sbms/* ARGSUSED */
31141502Swpaulvoid
31241502Swpauliderror(lport, fport, s, er)	/* Generic ident_stream error-sending func */
31341502Swpaul	int lport, fport, s, er;
31441502Swpaul{
31541502Swpaul	char *p;
31641502Swpaul
31741502Swpaul	asprintf(&p, "%d , %d : ERROR : %s\r\n", lport, fport,
31841502Swpaul	    er == -1 ? "HIDDEN-USER" : er ? strerror(er) : "UNKNOWN-ERROR");
31941502Swpaul	if (p == NULL) {
32041502Swpaul		syslog(LOG_ERR, "asprintf: %m");
32141502Swpaul		exit(EX_OSERR);
322109058Smbr	}
32341502Swpaul	send(s, p, strlen(p), MSG_EOF);
32441502Swpaul	free(p);
32541502Swpaul
32641502Swpaul	exit(0);
32741502Swpaul}
32841502Swpaul
32941502Swpaul/* ARGSUSED */
33041502Swpaulvoid
33141502Swpaulident_stream(s, sep)		/* Ident service (AKA "auth") */
33241502Swpaul	int s;
33341502Swpaul	struct servtab *sep;
33441502Swpaul{
33541502Swpaul	struct utsname un;
33641502Swpaul	struct stat sb;
33741502Swpaul	struct sockaddr_in sin[2];
33841502Swpaul#ifdef INET6
33941502Swpaul	struct sockaddr_in6 sin6[2];
34041502Swpaul#endif
34141502Swpaul	struct sockaddr_storage ss[2];
34241502Swpaul	struct ucred uc;
34341502Swpaul	struct timeval tv = {
34441502Swpaul		10,
34541502Swpaul		0
34641502Swpaul	};
34741502Swpaul	struct passwd *pw = NULL;
34841502Swpaul	fd_set fdset;
34941502Swpaul	char buf[BUFSIZE], *cp = NULL, *p, **av, *osname = NULL, garbage[7];
35041502Swpaul	char *fallback = NULL;
35141502Swpaul	socklen_t socklen;
35241502Swpaul	ssize_t ssize;
35341502Swpaul	size_t size;
35441502Swpaul	int c, fflag = 0, nflag = 0, rflag = 0, argc = 0, usedfallback = 0;
35541502Swpaul	int gflag = 0, getcredfail = 0, onreadlen;
35641502Swpaul	u_short lport, fport;
35741502Swpaul
35841502Swpaul	inetd_setproctitle(sep->se_service, s);
359131503Sbms	/*
360131503Sbms	 * Reset getopt() since we are a fork() but not an exec() from
36141502Swpaul	 * a parent which used getopt() already.
362110168Ssilby	 */
363110168Ssilby	optind = 1;
364131518Sbms	optreset = 1;
36541502Swpaul	/*
366131503Sbms	 * Take the internal argument vector and count it out to make an
367110168Ssilby	 * argument count for getopt. This can be used for any internal
368110168Ssilby	 * service to read arguments and use getopt() easily.
369110168Ssilby	 */
370131503Sbms	for (av = sep->se_argv; *av; av++)
371110168Ssilby		argc++;
372110168Ssilby	if (argc) {
373131503Sbms		int sec, usec;
374110168Ssilby		size_t i;
375110168Ssilby		u_int32_t random;
376110168Ssilby
377110168Ssilby		while ((c = getopt(argc, sep->se_argv, "d:fgno:rt:")) != -1)
378110168Ssilby			switch (c) {
379110168Ssilby			case 'd':
380110168Ssilby				fallback = optarg;
381131503Sbms				break;
382110168Ssilby			case 'f':
383110168Ssilby				fflag = 1;
384110168Ssilby				break;
385110168Ssilby			case 'g':
38641502Swpaul				gflag = 1;
38741502Swpaul				random = 0;	/* Shush, compiler. */
38841502Swpaul				/*
389102336Salfred				 * The number of bits in "random" divided
390131503Sbms				 * by the number of bits needed per iteration
391131503Sbms				 * gives a more optimal way to reload the
39241502Swpaul				 * random number only when necessary.
39341502Swpaul				 *
39441502Swpaul				 * I'm using base-36, so I need at least 6
39541502Swpaul				 * bits; round it up to 8 bits to make it
396131503Sbms				 * easier.
39741502Swpaul				 */
39841502Swpaul				for (i = 0; i < sizeof(garbage) - 1; i++) {
39941502Swpaul					const char *const base36 =
400131503Sbms					    "0123456789"
401131503Sbms					    "abcdefghijklmnopqrstuvwxyz";
40241502Swpaul					if (i % (sizeof(random) * 8 / 8) == 0)
40341502Swpaul						random = arc4random();
40441502Swpaul					garbage[i] =
40541502Swpaul					    base36[(random & 0xff) % 36];
40641502Swpaul					random >>= 8;
40741502Swpaul				}
40841502Swpaul				garbage[i] = '\0';
40941502Swpaul				break;
41041502Swpaul			case 'n':
41141502Swpaul				nflag = 1;
41241502Swpaul				break;
41341502Swpaul			case 'o':
41441502Swpaul				osname = optarg;
41541502Swpaul				break;
41641502Swpaul			case 'r':
41741502Swpaul				rflag = 1;
41841502Swpaul				break;
419131503Sbms			case 't':
42041502Swpaul				switch (sscanf(optarg, "%d.%d", &sec, &usec)) {
42141502Swpaul				case 2:
422131503Sbms					tv.tv_usec = usec;
42341502Swpaul				case 1:
424110168Ssilby					tv.tv_sec = sec;
425110168Ssilby					break;
426131518Sbms				default:
42741502Swpaul					if (debug)
428131503Sbms						warnx("bad -t argument");
429110168Ssilby					break;
430131503Sbms				}
431110168Ssilby				break;
432131503Sbms			default:
433110168Ssilby				break;
434110168Ssilby			}
435110168Ssilby	}
436110168Ssilby	if (osname == NULL) {
437110168Ssilby		if (uname(&un) == -1)
438110168Ssilby			iderror(0, 0, s, errno);
439110168Ssilby		osname = un.sysname;
440110168Ssilby	}
441110168Ssilby	socklen = sizeof(ss[0]);
442110168Ssilby	if (getsockname(s, (struct sockaddr *)&ss[0], &socklen) == -1)
443110168Ssilby		iderror(0, 0, s, errno);
444131503Sbms	socklen = sizeof(ss[1]);
445110168Ssilby	if (getpeername(s, (struct sockaddr *)&ss[1], &socklen) == -1)
446110168Ssilby		iderror(0, 0, s, errno);
447110168Ssilby	/*
448102336Salfred	 * We're going to prepare for and execute reception of a
449131517Sbms	 * packet of data from the user. The data is in the format
45051432Swpaul	 * "local_port , foreign_port\r\n" (with local being the
45141502Swpaul	 * server's port and foreign being the client's.)
452131503Sbms	 */
45341502Swpaul	FD_ZERO(&fdset);
454110168Ssilby	FD_SET(s, &fdset);
455131503Sbms	if (select(s + 1, &fdset, NULL, NULL, &tv) == -1)
456131518Sbms		iderror(0, 0, s, errno);
457131518Sbms	if (ioctl(s, FIONREAD, &onreadlen) == -1)
458131518Sbms		iderror(0, 0, s, errno);
459131518Sbms	if (onreadlen >= sizeof(buf))
460131503Sbms		onreadlen = sizeof(buf) - 1;
461131503Sbms	ssize = read(s, buf, (size_t)onreadlen);
462131503Sbms	if (ssize == -1)
463110168Ssilby		iderror(0, 0, s, errno);
46441502Swpaul	buf[ssize] = '\0';
46551432Swpaul	if (sscanf(buf, "%hu , %hu", &lport, &fport) != 2)
46641502Swpaul		iderror(0, 0, s, 0);
46741502Swpaul	if (gflag) {
46841502Swpaul		cp = garbage;
469131518Sbms		goto printit;
470131503Sbms	}
47141502Swpaul
47241502Swpaul	/*
473102336Salfred	 * If not "real" (-r), send a HIDDEN-USER error for everything.
474131503Sbms	 * If -d is used to set a fallback username, this is used to
47551432Swpaul	 * override it, and the fallback is returned instead.
47641502Swpaul	 */
477131503Sbms	if (!rflag) {
47841502Swpaul		if (fallback == NULL)
479110168Ssilby			iderror(lport, fport, s, -1);
480131503Sbms		else {
481131503Sbms			cp = fallback;
482131503Sbms			goto printit;
483131503Sbms		}
484131503Sbms	}
485131503Sbms
486110168Ssilby	/*
48741502Swpaul	 * We take the input and construct an array of two sockaddr_ins
48851432Swpaul	 * which contain the local address information and foreign
48941502Swpaul	 * address information, respectively, used to look up the
49041502Swpaul	 * credentials for the socket (which are returned by the
49141502Swpaul	 * sysctl "net.inet.tcp.getcred" when we call it.) The
49241502Swpaul	 * arrays have been filled in above via get{peer,sock}name(),
493131503Sbms	 * so right here we are only setting the ports.
49451432Swpaul	 */
49551432Swpaul	if (ss[0].ss_family != ss[1].ss_family)
496102336Salfred		iderror(lport, fport, s, errno);
497131503Sbms	size = sizeof(uc);
49851432Swpaul	switch (ss[0].ss_family) {
49951432Swpaul	case AF_INET:
500131503Sbms		sin[0] = *(struct sockaddr_in *)&ss[0];
50151432Swpaul		sin[0].sin_port = htons(lport);
50251432Swpaul		sin[1] = *(struct sockaddr_in *)&ss[1];
50351432Swpaul		sin[1].sin_port = htons(fport);
50441502Swpaul		if (sysctlbyname("net.inet.tcp.getcred", &uc, &size, sin,
50541502Swpaul				 sizeof(sin)) == -1)
50641502Swpaul			getcredfail = 1;
50741502Swpaul		break;
50841502Swpaul#ifdef INET6
509102336Salfred	case AF_INET6:
510131503Sbms		sin6[0] = *(struct sockaddr_in6 *)&ss[0];
51141502Swpaul		sin6[0].sin6_port = htons(lport);
512147256Sbrooks		sin6[1] = *(struct sockaddr_in6 *)&ss[1];
51341502Swpaul		sin6[1].sin6_port = htons(fport);
514131503Sbms		if (sysctlbyname("net.inet6.tcp6.getcred", &uc, &size, sin6,
51541502Swpaul				 sizeof(sin6)) == -1)
516131503Sbms			getcredfail = 1;
51741502Swpaul		break;
51841502Swpaul#endif
519131518Sbms	default: /* should not reach here */
52041502Swpaul		getcredfail = 1;
52141502Swpaul		break;
52241502Swpaul	}
52341502Swpaul	if (getcredfail != 0) {
52441502Swpaul		if (fallback == NULL)		/* Use a default, if asked to */
52541502Swpaul			iderror(lport, fport, s, errno);
52641502Swpaul		usedfallback = 1;
52741502Swpaul	} else {
52841502Swpaul		/* Look up the pw to get the username */
52941502Swpaul		pw = getpwuid(uc.cr_uid);
53041502Swpaul	}
531131503Sbms	if (pw == NULL && !usedfallback)		/* No such user... */
53241502Swpaul		iderror(lport, fport, s, errno);
53341502Swpaul	/*
53441502Swpaul	 * If enabled, we check for a file named ".noident" in the user's
535131503Sbms	 * home directory. If found, we return HIDDEN-USER.
53672084Sphk	 */
53741502Swpaul	if (nflag && !usedfallback) {
53841502Swpaul		if (asprintf(&p, "%s/.noident", pw->pw_dir) == -1)
539130270Snaddy			iderror(lport, fport, s, errno);
540130270Snaddy		if (lstat(p, &sb) == 0) {
54141502Swpaul			free(p);
54241502Swpaul			iderror(lport, fport, s, -1);
54341502Swpaul		}
54441502Swpaul		free(p);
54541502Swpaul	}
54641502Swpaul	/*
54741502Swpaul	 * Here, if enabled, we read a user's ".fakeid" file in their
54841502Swpaul	 * home directory. It consists of a line containing the name
54941502Swpaul	 * they want.
55041502Swpaul	 */
55141502Swpaul	if (fflag && !usedfallback) {
55241502Swpaul		FILE *fakeid = NULL;
55341502Swpaul
55441502Swpaul		if (asprintf(&p, "%s/.fakeid", pw->pw_dir) == -1)
55541502Swpaul			iderror(lport, fport, s, errno);
55641502Swpaul		/*
55741502Swpaul		 * Here we set ourself to effectively be the user, so we don't
55841502Swpaul		 * open any files we have no permission to open, especially
55941502Swpaul		 * symbolic links to sensitive root-owned files or devices.
56041502Swpaul		 */
56141502Swpaul		seteuid(pw->pw_uid);
56241502Swpaul		setegid(pw->pw_gid);
563102336Salfred		/*
564131503Sbms		 * If we were to lstat() here, it would do no good, since it
56541502Swpaul		 * would introduce a race condition and could be defeated.
566131517Sbms		 * Therefore, we open the file we have permissions to open
56741502Swpaul		 * and if it's not a regular file, we close it and end up
568131518Sbms		 * returning the user's real username.
569131518Sbms		 */
57041502Swpaul		fakeid = fopen(p, "r");
57141502Swpaul		free(p);
57241502Swpaul		if (fakeid != NULL &&
57341502Swpaul		    fstat(fileno(fakeid), &sb) != -1 && S_ISREG(sb.st_mode)) {
57441502Swpaul			buf[sizeof(buf) - 1] = '\0';
57551432Swpaul			if (fgets(buf, sizeof(buf), fakeid) == NULL) {
57641502Swpaul				cp = pw->pw_name;
57741502Swpaul				fclose(fakeid);
57841502Swpaul				goto printit;
57941502Swpaul			}
58041502Swpaul			fclose(fakeid);
58141502Swpaul			/*
58241502Swpaul			 * Usually, the file will have the desired identity
58341502Swpaul			 * in the form "identity\n", so we use strtok() to
584102336Salfred			 * end the string (which fgets() doesn't do.)
585131503Sbms			 */
58641502Swpaul			buf[strcspn(buf, "\r\n")] = '\0';
587131517Sbms			cp = buf;
58841502Swpaul			/* Allow for beginning white space... */
589131844Sbms			while (isspace(*cp))
590131518Sbms				cp++;
59141502Swpaul			/* ...and ending white space. */
59241502Swpaul			cp[strcspn(cp, " \t")] = '\0';
59341502Swpaul			/* User names of >16 characters are invalid */
59441502Swpaul			if (strlen(cp) > 16)
59541502Swpaul				cp[16] = '\0';
59641502Swpaul			/*
59741502Swpaul			 * If the name is a zero-length string or matches
598107220Ssilby			 * the name of another user, it's invalid, so
599107220Ssilby			 * we will return their real identity instead.
600107220Ssilby			 */
601107220Ssilby
602107220Ssilby			if (!*cp || getpwnam(cp))
603131503Sbms				cp = getpwuid(uc.cr_uid)->pw_name;
604131503Sbms		} else
605107220Ssilby			cp = pw->pw_name;
606107220Ssilby	} else if (!usedfallback)
607107220Ssilby		cp = pw->pw_name;
60841502Swpaul	else
60941502Swpaul		cp = fallback;
61041502Swpaulprintit:
61141502Swpaul	/* Finally, we make and send the reply. */
61241502Swpaul	if (asprintf(&p, "%d , %d : USERID : %s : %s\r\n", lport, fport, osname,
61341502Swpaul	    cp) == -1) {
61441502Swpaul		syslog(LOG_ERR, "asprintf: %m");
61541502Swpaul		exit(EX_OSERR);
61641502Swpaul	}
617102336Salfred	send(s, p, strlen(p), MSG_EOF);
618131503Sbms	free(p);
61941502Swpaul
620131503Sbms	exit(0);
62141502Swpaul}
622131503Sbms
62349610Swpaul/*
62449610Swpaul * RFC738 Time Server.
62549610Swpaul * Return a machine readable date and time, in the form of the
626142398Simp * number of seconds since midnight, Jan 1, 1900.  Since gettimeofday
62741502Swpaul * returns the number of seconds since midnight, Jan 1, 1970,
62841502Swpaul * we must add 2208988800 seconds to this figure to make up for
62941502Swpaul * some seventy years Bell Labs was asleep.
63041502Swpaul */
631131503Sbms
63241502Swpaulunsigned long
63341502Swpaulmachtime()
63441502Swpaul{
63541502Swpaul	struct timeval tv;
63641502Swpaul
63741502Swpaul	if (gettimeofday(&tv, (struct timezone *)NULL) < 0) {
638102336Salfred		if (debug)
639102336Salfred			warnx("unable to get time of day");
64049610Swpaul		return (0L);
64141502Swpaul	}
64267087Swpaul#define	OFFSET ((u_long)25567 * 24*60*60)
64341502Swpaul	return (htonl((long)(tv.tv_sec + OFFSET)));
64441502Swpaul#undef OFFSET
64541502Swpaul}
64649610Swpaul
64741502Swpaul/* ARGSUSED */
64849610Swpaulvoid
64949610Swpaulmachtime_dg(s, sep)
65041502Swpaul	int s;
65193818Sjhb	struct servtab *sep;
652131518Sbms{
65341502Swpaul	unsigned long result;
65441502Swpaul	struct sockaddr_storage ss;
65541502Swpaul	socklen_t size;
65672813Swpaul
657107220Ssilby	size = sizeof(ss);
65841502Swpaul	if (recvfrom(s, (char *)&result, sizeof(result), 0,
65949610Swpaul		     (struct sockaddr *)&ss, &size) < 0)
660127135Snjl		return;
66149610Swpaul
66249610Swpaul	if (check_loop((struct sockaddr *)&ss, sep))
66349610Swpaul		return;
66449610Swpaul
66541502Swpaul	result = machtime();
66641502Swpaul	(void) sendto(s, (char *) &result, sizeof(result), 0,
66741502Swpaul		      (struct sockaddr *)&ss, size);
66849610Swpaul}
66949610Swpaul
67041502Swpaul/* ARGSUSED */
67141502Swpaulvoid
67249610Swpaulmachtime_stream(s, sep)
673127135Snjl	int s;
67449610Swpaul	struct servtab *sep;
67549610Swpaul{
67649610Swpaul	unsigned long result;
67741502Swpaul
67849610Swpaul	result = machtime();
67941502Swpaul	(void) send(s, (char *) &result, sizeof(result), MSG_EOF);
68041502Swpaul}
68141502Swpaul
68276586Swpaul/*
68376586Swpaul * RFC1078 TCP Port Service Multiplexer (TCPMUX). Service connections to
68476586Swpaul * services based on the service name sent.
68576586Swpaul *
68676586Swpaul *  Based on TCPMUX.C by Mark K. Lottor November 1988
68776586Swpaul *  sri-nic::ps:<mkl>tcpmux.c
68876586Swpaul */
68941502Swpaul
69041502Swpaul#define MAX_SERV_LEN	(256+2)		/* 2 bytes for \r\n */
69141502Swpaul#define strwrite(fd, buf)	(void) write(fd, buf, sizeof(buf)-1)
692131503Sbms
693110168Ssilbystatic int		/* # of characters upto \r,\n or \0 */
694110168Ssilbygetline(fd, buf, len)
695110168Ssilby	int fd;
696131503Sbms	char *buf;
697110168Ssilby	int len;
698110168Ssilby{
699110168Ssilby	int count = 0, n;
70041502Swpaul	struct sigaction sa;
70141502Swpaul
70241502Swpaul	sa.sa_flags = 0;
70341502Swpaul	sigemptyset(&sa.sa_mask);
70441502Swpaul	sa.sa_handler = SIG_DFL;
70541502Swpaul	sigaction(SIGALRM, &sa, (struct sigaction *)0);
70641502Swpaul	do {
70741502Swpaul		alarm(10);
70841502Swpaul		n = read(fd, buf, len-count);
70941502Swpaul		alarm(0);
71041502Swpaul		if (n == 0)
71141502Swpaul			return (count);
71241502Swpaul		if (n < 0)
71341502Swpaul			return (-1);
71451432Swpaul		while (--n >= 0) {
71551657Swpaul			if (*buf == '\r' || *buf == '\n' || *buf == '\0')
71651432Swpaul				return (count);
71751432Swpaul			count++;
71841502Swpaul			buf++;
71949610Swpaul		}
72049610Swpaul	} while (count < len);
72141502Swpaul	return (count);
72241502Swpaul}
72341502Swpaul
72441502Swpaulstruct servtab *
725147256Sbrookstcpmux(s)
726147256Sbrooks	int s;
727147256Sbrooks{
728147256Sbrooks	struct servtab *sep;
729147256Sbrooks	char service[MAX_SERV_LEN+1];
730147256Sbrooks	int len;
73141502Swpaul
732121816Sbrooks	/* Get requested service name */
73341502Swpaul	if ((len = getline(s, service, MAX_SERV_LEN)) < 0) {
73441502Swpaul		strwrite(s, "-Error reading service name\r\n");
73541502Swpaul		return (NULL);
73641502Swpaul	}
73741502Swpaul	service[len] = '\0';
73841502Swpaul
73941502Swpaul	if (debug)
740132986Smlaier		warnx("tcpmux: someone wants %s", service);
74143515Swpaul
742132986Smlaier	/*
743128118Sru	 * Help is a required command, and lists available services,
744128118Sru	 * one per line.
745128118Sru	 */
746128118Sru	if (!strcasecmp(service, "help")) {
74741502Swpaul		for (sep = servtab; sep; sep = sep->se_next) {
748131503Sbms			if (!ISMUX(sep))
74951432Swpaul				continue;
75051432Swpaul			(void)write(s,sep->se_service,strlen(sep->se_service));
75141502Swpaul			strwrite(s, "\r\n");
75249610Swpaul		}
75341502Swpaul		return (NULL);
75441502Swpaul	}
75541502Swpaul
75651432Swpaul	/* Try matching a service in inetd.conf with the request */
75741502Swpaul	for (sep = servtab; sep; sep = sep->se_next) {
758131503Sbms		if (!ISMUX(sep))
759106936Ssam			continue;
76041502Swpaul		if (!strcasecmp(service, sep->se_service)) {
761131844Sbms			if (ISMUXPLUS(sep)) {
762131844Sbms				strwrite(s, "+Go\r\n");
763113609Snjl			}
764131518Sbms			return (sep);
765112872Snjl		}
766112872Snjl	}
767112872Snjl	strwrite(s, "-Service not available\r\n");
768112872Snjl	return (NULL);
769113609Snjl}
770147256Sbrooks