builtins.c revision 63045
149004Sgreen/*-
249004Sgreen * Copyright (c) 1983, 1991, 1993, 1994
349004Sgreen *	The Regents of the University of California.  All rights reserved.
449004Sgreen *
549004Sgreen * Redistribution and use in source and binary forms, with or without
649004Sgreen * modification, are permitted provided that the following conditions
749004Sgreen * are met:
849004Sgreen * 1. Redistributions of source code must retain the above copyright
949004Sgreen *    notice, this list of conditions and the following disclaimer.
1049004Sgreen * 2. Redistributions in binary form must reproduce the above copyright
1149004Sgreen *    notice, this list of conditions and the following disclaimer in the
1249004Sgreen *    documentation and/or other materials provided with the distribution.
1349004Sgreen *
1449004Sgreen * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
1549004Sgreen * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1649004Sgreen * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1749004Sgreen * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
1849004Sgreen * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
1949004Sgreen * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2049004Sgreen * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2149004Sgreen * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2249004Sgreen * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2349004Sgreen * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2449004Sgreen * SUCH DAMAGE.
2549004Sgreen *
2650479Speter * $FreeBSD: head/usr.sbin/inetd/builtins.c 63045 2000-07-12 20:49:06Z dwmalone $
2749004Sgreen *
2849004Sgreen */
2949004Sgreen
3049004Sgreen#include <sys/filio.h>
3149004Sgreen#include <sys/ioccom.h>
3248981Ssheldonh#include <sys/param.h>
3348981Ssheldonh#include <sys/stat.h>
3448981Ssheldonh#include <sys/socket.h>
3548981Ssheldonh#include <sys/sysctl.h>
3648981Ssheldonh#include <sys/ucred.h>
3748981Ssheldonh#include <sys/uio.h>
3849004Sgreen#include <sys/utsname.h>
3948981Ssheldonh
4048981Ssheldonh#include <ctype.h>
4148981Ssheldonh#include <err.h>
4248981Ssheldonh#include <errno.h>
4348981Ssheldonh#include <limits.h>
4448981Ssheldonh#include <pwd.h>
4548981Ssheldonh#include <signal.h>
4649004Sgreen#include <stdlib.h>
4748981Ssheldonh#include <string.h>
4849030Ssheldonh#include <sysexits.h>
4949030Ssheldonh#include <syslog.h>
5048981Ssheldonh#include <unistd.h>
5148981Ssheldonh
5248981Ssheldonh#include "inetd.h"
5348981Ssheldonh
5448981Ssheldonhextern int	 debug;
5548981Ssheldonhextern struct servtab *servtab;
5648981Ssheldonh
5748981Ssheldonhchar ring[128];
5848981Ssheldonhchar *endring;
5948981Ssheldonh
6056590Sshinint check_loop __P((struct sockaddr *, struct servtab *sep));
6148981Ssheldonhvoid inetd_setproctitle __P((char *, int));
6248981Ssheldonh
6348981Ssheldonhstruct biltin biltins[] = {
6448981Ssheldonh	/* Echo received data */
6548981Ssheldonh	{ "echo",	SOCK_STREAM,	1, -1,	echo_stream },
6648981Ssheldonh	{ "echo",	SOCK_DGRAM,	0, 1,	echo_dg },
6748981Ssheldonh
6848981Ssheldonh	/* Internet /dev/null */
6948981Ssheldonh	{ "discard",	SOCK_STREAM,	1, -1,	discard_stream },
7048981Ssheldonh	{ "discard",	SOCK_DGRAM,	0, 1,	discard_dg },
7148981Ssheldonh
7248981Ssheldonh	/* Return 32 bit time since 1970 */
7348981Ssheldonh	{ "time",	SOCK_STREAM,	0, -1,	machtime_stream },
7448981Ssheldonh	{ "time",	SOCK_DGRAM,	0, 1,	machtime_dg },
7548981Ssheldonh
7648981Ssheldonh	/* Return human-readable time */
7748981Ssheldonh	{ "daytime",	SOCK_STREAM,	0, -1,	daytime_stream },
7848981Ssheldonh	{ "daytime",	SOCK_DGRAM,	0, 1,	daytime_dg },
7948981Ssheldonh
8048981Ssheldonh	/* Familiar character generator */
8148981Ssheldonh	{ "chargen",	SOCK_STREAM,	1, -1,	chargen_stream },
8248981Ssheldonh	{ "chargen",	SOCK_DGRAM,	0, 1,	chargen_dg },
8348981Ssheldonh
8448981Ssheldonh	{ "tcpmux",	SOCK_STREAM,	1, -1,	(void (*)())tcpmux },
8548981Ssheldonh
8648981Ssheldonh	{ "auth",	SOCK_STREAM,	1, -1,	ident_stream },
8748981Ssheldonh
8848981Ssheldonh	{ NULL }
8948981Ssheldonh};
9048981Ssheldonh
9149052Ssheldonh/*
9249052Ssheldonh * RFC864 Character Generator Protocol. Generates character data without
9349052Ssheldonh * any regard for input.
9449052Ssheldonh */
9549052Ssheldonh
9648981Ssheldonhvoid
9748981Ssheldonhinitring()
9848981Ssheldonh{
9948981Ssheldonh	int i;
10048981Ssheldonh
10148981Ssheldonh	endring = ring;
10248981Ssheldonh
10348981Ssheldonh	for (i = 0; i <= 128; ++i)
10448981Ssheldonh		if (isprint(i))
10548981Ssheldonh			*endring++ = i;
10648981Ssheldonh}
10748981Ssheldonh
10848981Ssheldonh/* ARGSUSED */
10948981Ssheldonhvoid
11048981Ssheldonhchargen_dg(s, sep)		/* Character generator */
11148981Ssheldonh	int s;
11248981Ssheldonh	struct servtab *sep;
11348981Ssheldonh{
11456590Sshin	struct sockaddr_storage ss;
11548981Ssheldonh	static char *rs;
11657857Sshin	int len;
11757857Sshin	socklen_t size;
11848981Ssheldonh	char text[LINESIZ+2];
11948981Ssheldonh
12048981Ssheldonh	if (endring == 0) {
12148981Ssheldonh		initring();
12248981Ssheldonh		rs = ring;
12348981Ssheldonh	}
12448981Ssheldonh
12556590Sshin	size = sizeof(ss);
12648981Ssheldonh	if (recvfrom(s, text, sizeof(text), 0,
12756590Sshin		     (struct sockaddr *)&ss, &size) < 0)
12848981Ssheldonh		return;
12948981Ssheldonh
13056590Sshin	if (check_loop((struct sockaddr *)&ss, sep))
13148981Ssheldonh		return;
13248981Ssheldonh
13348981Ssheldonh	if ((len = endring - rs) >= LINESIZ)
13448981Ssheldonh		memmove(text, rs, LINESIZ);
13548981Ssheldonh	else {
13648981Ssheldonh		memmove(text, rs, len);
13748981Ssheldonh		memmove(text + len, ring, LINESIZ - len);
13848981Ssheldonh	}
13948981Ssheldonh	if (++rs == endring)
14048981Ssheldonh		rs = ring;
14148981Ssheldonh	text[LINESIZ] = '\r';
14248981Ssheldonh	text[LINESIZ + 1] = '\n';
14357857Sshin	(void) sendto(s, text, sizeof(text), 0, (struct sockaddr *)&ss, size);
14448981Ssheldonh}
14548981Ssheldonh
14648981Ssheldonh/* ARGSUSED */
14748981Ssheldonhvoid
14848981Ssheldonhchargen_stream(s, sep)		/* Character generator */
14948981Ssheldonh	int s;
15048981Ssheldonh	struct servtab *sep;
15148981Ssheldonh{
15248981Ssheldonh	int len;
15348981Ssheldonh	char *rs, text[LINESIZ+2];
15448981Ssheldonh
15548981Ssheldonh	inetd_setproctitle(sep->se_service, s);
15648981Ssheldonh
15748981Ssheldonh	if (!endring) {
15848981Ssheldonh		initring();
15948981Ssheldonh		rs = ring;
16048981Ssheldonh	}
16148981Ssheldonh
16248981Ssheldonh	text[LINESIZ] = '\r';
16348981Ssheldonh	text[LINESIZ + 1] = '\n';
16448981Ssheldonh	for (rs = ring;;) {
16548981Ssheldonh		if ((len = endring - rs) >= LINESIZ)
16648981Ssheldonh			memmove(text, rs, LINESIZ);
16748981Ssheldonh		else {
16848981Ssheldonh			memmove(text, rs, len);
16948981Ssheldonh			memmove(text + len, ring, LINESIZ - len);
17048981Ssheldonh		}
17148981Ssheldonh		if (++rs == endring)
17248981Ssheldonh			rs = ring;
17348981Ssheldonh		if (write(s, text, sizeof(text)) != sizeof(text))
17448981Ssheldonh			break;
17548981Ssheldonh	}
17648981Ssheldonh	exit(0);
17748981Ssheldonh}
17848981Ssheldonh
17949052Ssheldonh/*
18049052Ssheldonh * RFC867 Daytime Protocol. Sends the current date and time as an ascii
18149052Ssheldonh * character string without any regard for input.
18249052Ssheldonh */
18349052Ssheldonh
18448981Ssheldonh/* ARGSUSED */
18548981Ssheldonhvoid
18648981Ssheldonhdaytime_dg(s, sep)		/* Return human-readable time of day */
18748981Ssheldonh	int s;
18848981Ssheldonh	struct servtab *sep;
18948981Ssheldonh{
19048981Ssheldonh	char buffer[256];
19148981Ssheldonh	time_t clock;
19256590Sshin	struct sockaddr_storage ss;
19357857Sshin	socklen_t size;
19448981Ssheldonh
19548981Ssheldonh	clock = time((time_t *) 0);
19648981Ssheldonh
19756590Sshin	size = sizeof(ss);
19848981Ssheldonh	if (recvfrom(s, buffer, sizeof(buffer), 0,
19956590Sshin		     (struct sockaddr *)&ss, &size) < 0)
20048981Ssheldonh		return;
20148981Ssheldonh
20256590Sshin	if (check_loop((struct sockaddr *)&ss, sep))
20348981Ssheldonh		return;
20448981Ssheldonh
20548981Ssheldonh	(void) sprintf(buffer, "%.24s\r\n", ctime(&clock));
20648981Ssheldonh	(void) sendto(s, buffer, strlen(buffer), 0,
20757857Sshin		      (struct sockaddr *)&ss, size);
20848981Ssheldonh}
20948981Ssheldonh
21048981Ssheldonh/* ARGSUSED */
21148981Ssheldonhvoid
21248981Ssheldonhdaytime_stream(s, sep)		/* Return human-readable time of day */
21348981Ssheldonh	int s;
21448981Ssheldonh	struct servtab *sep;
21548981Ssheldonh{
21648981Ssheldonh	char buffer[256];
21748981Ssheldonh	time_t clock;
21848981Ssheldonh
21948981Ssheldonh	clock = time((time_t *) 0);
22048981Ssheldonh
22148981Ssheldonh	(void) sprintf(buffer, "%.24s\r\n", ctime(&clock));
22258735Ssheldonh	(void) send(s, buffer, strlen(buffer), MSG_EOF);
22348981Ssheldonh}
22448981Ssheldonh
22549052Ssheldonh/*
22649052Ssheldonh * RFC863 Discard Protocol. Any data received is thrown away and no response
22749052Ssheldonh * is sent.
22849052Ssheldonh */
22949052Ssheldonh
23048981Ssheldonh/* ARGSUSED */
23148981Ssheldonhvoid
23248981Ssheldonhdiscard_dg(s, sep)		/* Discard service -- ignore data */
23348981Ssheldonh	int s;
23448981Ssheldonh	struct servtab *sep;
23548981Ssheldonh{
23648981Ssheldonh	char buffer[BUFSIZE];
23748981Ssheldonh
23848981Ssheldonh	(void) read(s, buffer, sizeof(buffer));
23948981Ssheldonh}
24048981Ssheldonh
24148981Ssheldonh/* ARGSUSED */
24248981Ssheldonhvoid
24348981Ssheldonhdiscard_stream(s, sep)		/* Discard service -- ignore data */
24448981Ssheldonh	int s;
24548981Ssheldonh	struct servtab *sep;
24648981Ssheldonh{
24748981Ssheldonh	int ret;
24848981Ssheldonh	char buffer[BUFSIZE];
24948981Ssheldonh
25048981Ssheldonh	inetd_setproctitle(sep->se_service, s);
25148981Ssheldonh	while (1) {
25248981Ssheldonh		while ((ret = read(s, buffer, sizeof(buffer))) > 0)
25348981Ssheldonh			;
25448981Ssheldonh		if (ret == 0 || errno != EINTR)
25548981Ssheldonh			break;
25648981Ssheldonh	}
25748981Ssheldonh	exit(0);
25848981Ssheldonh}
25948981Ssheldonh
26049052Ssheldonh/*
26149052Ssheldonh * RFC862 Echo Protocol. Any data received is sent back to the sender as
26249052Ssheldonh * received.
26349052Ssheldonh */
26449052Ssheldonh
26548981Ssheldonh/* ARGSUSED */
26648981Ssheldonhvoid
26748981Ssheldonhecho_dg(s, sep)			/* Echo service -- echo data back */
26848981Ssheldonh	int s;
26948981Ssheldonh	struct servtab *sep;
27048981Ssheldonh{
27148981Ssheldonh	char buffer[BUFSIZE];
27257857Sshin	int i;
27357857Sshin	socklen_t size;
27456590Sshin	struct sockaddr_storage ss;
27548981Ssheldonh
27656590Sshin	size = sizeof(ss);
27748981Ssheldonh	if ((i = recvfrom(s, buffer, sizeof(buffer), 0,
27856590Sshin			  (struct sockaddr *)&ss, &size)) < 0)
27948981Ssheldonh		return;
28048981Ssheldonh
28156590Sshin	if (check_loop((struct sockaddr *)&ss, sep))
28248981Ssheldonh		return;
28348981Ssheldonh
28457857Sshin	(void) sendto(s, buffer, i, 0, (struct sockaddr *)&ss, size);
28548981Ssheldonh}
28648981Ssheldonh
28748981Ssheldonh/* ARGSUSED */
28848981Ssheldonhvoid
28948981Ssheldonhecho_stream(s, sep)		/* Echo service -- echo data back */
29048981Ssheldonh	int s;
29148981Ssheldonh	struct servtab *sep;
29248981Ssheldonh{
29348981Ssheldonh	char buffer[BUFSIZE];
29448981Ssheldonh	int i;
29548981Ssheldonh
29648981Ssheldonh	inetd_setproctitle(sep->se_service, s);
29748981Ssheldonh	while ((i = read(s, buffer, sizeof(buffer))) > 0 &&
29848981Ssheldonh	    write(s, buffer, i) > 0)
29948981Ssheldonh		;
30048981Ssheldonh	exit(0);
30148981Ssheldonh}
30248981Ssheldonh
30349052Ssheldonh/*
30449052Ssheldonh * RFC1413 Identification Protocol. Given a TCP port number pair, return a
30549052Ssheldonh * character string which identifies the owner of that connection on the
30649057Sgreen * server's system. Extended to allow for ~/.fakeid support and ~/.noident
30749057Sgreen * support.
30849052Ssheldonh */
30949052Ssheldonh
31048981Ssheldonh/* ARGSUSED */
31148981Ssheldonhvoid
31249104Sgreeniderror(lport, fport, s, er)	/* Generic ident_stream error-sending func */
31349004Sgreen	int lport, fport, s, er;
31448981Ssheldonh{
31549004Sgreen	char *p;
31649004Sgreen
31749030Ssheldonh	asprintf(&p, "%d , %d : ERROR : %s\r\n", lport, fport,
31848981Ssheldonh	    er == -1 ? "HIDDEN-USER" : er ? strerror(er) : "UNKNOWN-ERROR");
31949030Ssheldonh	if (p == NULL) {
32049057Sgreen		syslog(LOG_ERR, "asprintf: %m");
32149030Ssheldonh		exit(EX_OSERR);
32249030Ssheldonh	}
32358735Ssheldonh	send(s, p, strlen(p), MSG_EOF);
32449004Sgreen	free(p);
32548981Ssheldonh
32648981Ssheldonh	exit(0);
32748981Ssheldonh}
32848981Ssheldonh
32948981Ssheldonh/* ARGSUSED */
33048981Ssheldonhvoid
33149104Sgreenident_stream(s, sep)		/* Ident service (AKA "auth") */
33248981Ssheldonh	int s;
33348981Ssheldonh	struct servtab *sep;
33448981Ssheldonh{
33549089Sgreen	struct utsname un;
33649089Sgreen	struct stat sb;
33748981Ssheldonh	struct sockaddr_in sin[2];
33857906Sshin#ifdef INET6
33956590Sshin	struct sockaddr_in6 sin6[2];
34057906Sshin#endif
34156590Sshin	struct sockaddr_storage ss[2];
34248981Ssheldonh	struct ucred uc;
34349004Sgreen	struct timeval tv = {
34449004Sgreen		10,
34549004Sgreen		0
34663045Sdwmalone	}, to;
34756298Sgreen	struct passwd *pw = NULL;
34849004Sgreen	fd_set fdset;
34963045Sdwmalone	char buf[BUFSIZE], *cp = NULL, *p, **av, *osname = NULL, garbage[7], e;
35056298Sgreen	char *fallback = NULL;
35161099Sgreen	socklen_t socklen;
35261099Sgreen	ssize_t ssize;
35363045Sdwmalone	size_t size, bufsiz;
35461098Sjhb	int c, fflag = 0, nflag = 0, rflag = 0, argc = 0, usedfallback = 0;
35561099Sgreen	int gflag = 0, getcredfail = 0, onreadlen;
35648981Ssheldonh	u_short lport, fport;
35748981Ssheldonh
35848981Ssheldonh	inetd_setproctitle(sep->se_service, s);
35949104Sgreen	/*
36049104Sgreen	 * Reset getopt() since we are a fork() but not an exec() from
36149104Sgreen	 * a parent which used getopt() already.
36249104Sgreen	 */
36348981Ssheldonh	optind = 1;
36448981Ssheldonh	optreset = 1;
36549104Sgreen	/*
36649104Sgreen	 * Take the internal argument vector and count it out to make an
36749104Sgreen	 * argument count for getopt. This can be used for any internal
36849104Sgreen	 * service to read arguments and use getopt() easily.
36949104Sgreen	 */
37048981Ssheldonh	for (av = sep->se_argv; *av; av++)
37148981Ssheldonh		argc++;
37248981Ssheldonh	if (argc) {
37349054Sgreen		int sec, usec;
37456298Sgreen		size_t i;
37556298Sgreen		u_int32_t random;
37649054Sgreen
37756298Sgreen		while ((c = getopt(argc, sep->se_argv, "d:fgno:rt:")) != -1)
37848981Ssheldonh			switch (c) {
37956298Sgreen			case 'd':
38056298Sgreen				fallback = optarg;
38156298Sgreen				break;
38248981Ssheldonh			case 'f':
38348981Ssheldonh				fflag = 1;
38448981Ssheldonh				break;
38556298Sgreen			case 'g':
38656298Sgreen				gflag = 1;
38756298Sgreen				random = 0;	/* Shush, compiler. */
38856303Sgreen				/*
38956303Sgreen				 * The number of bits in "random" divided
39056303Sgreen				 * by the number of bits needed per iteration
39156303Sgreen				 * gives a more optimal way to reload the
39256303Sgreen				 * random number only when necessary.
39356303Sgreen				 *
39456303Sgreen				 * I'm using base-36, so I need at least 6
39556303Sgreen				 * bits; round it up to 8 bits to make it
39656303Sgreen				 * easier.
39756303Sgreen				 */
39856298Sgreen				for (i = 0; i < sizeof(garbage) - 1; i++) {
39956303Sgreen					const char *const base36 =
40056303Sgreen					    "0123456789"
40156303Sgreen					    "abcdefghijklmnopqrstuvwxyz";
40256303Sgreen					if (i % (sizeof(random) * 8 / 8) == 0)
40356298Sgreen						random = arc4random();
40456303Sgreen					garbage[i] =
40556303Sgreen					    base36[(random & 0xff) % 36];
40656303Sgreen					random >>= 8;
40756298Sgreen				}
40856303Sgreen				garbage[i] = '\0';
40956298Sgreen				break;
41049057Sgreen			case 'n':
41149057Sgreen				nflag = 1;
41248981Ssheldonh				break;
41349004Sgreen			case 'o':
41449004Sgreen				osname = optarg;
41549004Sgreen				break;
41649057Sgreen			case 'r':
41749057Sgreen				rflag = 1;
41849057Sgreen				break;
41949051Ssheldonh			case 't':
42049030Ssheldonh				switch (sscanf(optarg, "%d.%d", &sec, &usec)) {
42149030Ssheldonh				case 2:
42249030Ssheldonh					tv.tv_usec = usec;
42349030Ssheldonh				case 1:
42449030Ssheldonh					tv.tv_sec = sec;
42549030Ssheldonh					break;
42649030Ssheldonh				default:
42749030Ssheldonh					if (debug)
42849030Ssheldonh						warnx("bad -t argument");
42949030Ssheldonh					break;
43049030Ssheldonh				}
43149054Sgreen				break;
43248981Ssheldonh			default:
43348981Ssheldonh				break;
43448981Ssheldonh			}
43548981Ssheldonh	}
43649004Sgreen	if (osname == NULL) {
43749033Sgreen		if (uname(&un) == -1)
43849004Sgreen			iderror(0, 0, s, errno);
43949004Sgreen		osname = un.sysname;
44049004Sgreen	}
44161099Sgreen	socklen = sizeof(ss[0]);
44261099Sgreen	if (getsockname(s, (struct sockaddr *)&ss[0], &socklen) == -1)
44349004Sgreen		iderror(0, 0, s, errno);
44461099Sgreen	socklen = sizeof(ss[1]);
44561099Sgreen	if (getpeername(s, (struct sockaddr *)&ss[1], &socklen) == -1)
44649004Sgreen		iderror(0, 0, s, errno);
44749104Sgreen	/*
44849104Sgreen	 * We're going to prepare for and execute reception of a
44949104Sgreen	 * packet of data from the user. The data is in the format
45049104Sgreen	 * "local_port , foreign_port\r\n" (with local being the
45149104Sgreen	 * server's port and foreign being the client's.)
45249104Sgreen	 */
45363045Sdwmalone	gettimeofday(&to, NULL);
45463045Sdwmalone	to.tv_sec += tv.tv_sec;
45563045Sdwmalone	if ((to.tv_usec += tv.tv_usec) >= 1000000) {
45663045Sdwmalone		to.tv_usec -= 1000000;
45763045Sdwmalone		to.tv_sec++;
45863045Sdwmalone	}
45963045Sdwmalone
46063045Sdwmalone	size = 0;
46163045Sdwmalone	bufsiz = sizeof(buf) - 1;
46249004Sgreen	FD_ZERO(&fdset);
46363045Sdwmalone 	while (bufsiz > 0 && (size == 0 || buf[size - 1] != '\n')) {
46463045Sdwmalone		gettimeofday(&tv, NULL);
46563045Sdwmalone		tv.tv_sec = to.tv_sec - tv.tv_sec;
46663045Sdwmalone		tv.tv_usec = to.tv_usec - tv.tv_usec;
46763045Sdwmalone		if (tv.tv_usec < 0) {
46863045Sdwmalone			tv.tv_usec += 1000000;
46963045Sdwmalone			tv.tv_sec--;
47063045Sdwmalone		}
47163045Sdwmalone		if (tv.tv_sec < 0)
47263045Sdwmalone			break;
47363045Sdwmalone		FD_SET(s, &fdset);
47463045Sdwmalone		if (select(s + 1, &fdset, NULL, NULL, &tv) == -1)
47563045Sdwmalone			iderror(0, 0, s, errno);
47663045Sdwmalone		if (ioctl(s, FIONREAD, &onreadlen) == -1)
47763045Sdwmalone			iderror(0, 0, s, errno);
47863045Sdwmalone		if (onreadlen > bufsiz)
47963045Sdwmalone			onreadlen = bufsiz;
48063045Sdwmalone		ssize = read(s, &buf[size], (size_t)onreadlen);
48163045Sdwmalone		if (ssize == -1)
48263045Sdwmalone			iderror(0, 0, s, errno);
48363045Sdwmalone		bufsiz -= ssize;
48463045Sdwmalone		size += ssize;
48563045Sdwmalone 	}
48663045Sdwmalone	buf[size] = '\0';
48763045Sdwmalone	/* Read two characters, and check for a delimiting character */
48863045Sdwmalone	if (sscanf(buf, "%hu , %hu%c", &lport, &fport, &e) != 3 || isdigit(e))
48949004Sgreen		iderror(0, 0, s, 0);
49056298Sgreen	if (gflag) {
49156298Sgreen		cp = garbage;
49256298Sgreen		goto printit;
49356298Sgreen	}
49456298Sgreen
49549104Sgreen	/*
49658712Sgreen	 * If not "real" (-r), send a HIDDEN-USER error for everything.
49758712Sgreen	 * If -d is used to set a fallback username, this is used to
49858712Sgreen	 * override it, and the fallback is returned instead.
49958712Sgreen	 */
50058712Sgreen	if (!rflag) {
50158712Sgreen		if (fallback == NULL)
50258712Sgreen			iderror(lport, fport, s, -1);
50358712Sgreen		else {
50458712Sgreen			cp = fallback;
50558712Sgreen			goto printit;
50658712Sgreen		}
50758712Sgreen	}
50858712Sgreen
50958712Sgreen	/*
51049104Sgreen	 * We take the input and construct an array of two sockaddr_ins
51149104Sgreen	 * which contain the local address information and foreign
51249104Sgreen	 * address information, respectively, used to look up the
51349104Sgreen	 * credentials for the socket (which are returned by the
51449104Sgreen	 * sysctl "net.inet.tcp.getcred" when we call it.) The
51549104Sgreen	 * arrays have been filled in above via get{peer,sock}name(),
51649104Sgreen	 * so right here we are only setting the ports.
51749104Sgreen	 */
51856590Sshin	if (ss[0].ss_family != ss[1].ss_family)
51956590Sshin		iderror(lport, fport, s, errno);
52061099Sgreen	size = sizeof(uc);
52156590Sshin	switch (ss[0].ss_family) {
52256590Sshin	case AF_INET:
52356590Sshin		sin[0] = *(struct sockaddr_in *)&ss[0];
52456590Sshin		sin[0].sin_port = htons(lport);
52556590Sshin		sin[1] = *(struct sockaddr_in *)&ss[1];
52656590Sshin		sin[1].sin_port = htons(fport);
52761099Sgreen		if (sysctlbyname("net.inet.tcp.getcred", &uc, &size, sin,
52856590Sshin				 sizeof(sin)) == -1)
52956590Sshin			getcredfail = 1;
53056590Sshin		break;
53156590Sshin#ifdef INET6
53256590Sshin	case AF_INET6:
53356590Sshin		sin6[0] = *(struct sockaddr_in6 *)&ss[0];
53456590Sshin		sin6[0].sin6_port = htons(lport);
53556590Sshin		sin6[1] = *(struct sockaddr_in6 *)&ss[1];
53656590Sshin		sin6[1].sin6_port = htons(fport);
53761099Sgreen		if (sysctlbyname("net.inet6.tcp6.getcred", &uc, &size, sin6,
53856590Sshin				 sizeof(sin6)) == -1)
53956590Sshin			getcredfail = 1;
54056590Sshin		break;
54156590Sshin#endif
54256590Sshin	default: /* should not reach here */
54356590Sshin		getcredfail = 1;
54456590Sshin		break;
54556590Sshin	}
54656590Sshin	if (getcredfail != 0) {
54756298Sgreen		if (fallback == NULL)		/* Use a default, if asked to */
54856298Sgreen			iderror(lport, fport, s, errno);
54956298Sgreen		usedfallback = 1;
55056298Sgreen	} else {
55156298Sgreen		/* Look up the pw to get the username */
55256298Sgreen		pw = getpwuid(uc.cr_uid);
55356298Sgreen	}
55456298Sgreen	if (pw == NULL && !usedfallback)		/* No such user... */
55549004Sgreen		iderror(lport, fport, s, errno);
55649104Sgreen	/*
55749104Sgreen	 * If enabled, we check for a file named ".noident" in the user's
55849104Sgreen	 * home directory. If found, we return HIDDEN-USER.
55949104Sgreen	 */
56056298Sgreen	if (nflag && !usedfallback) {
56149089Sgreen		if (asprintf(&p, "%s/.noident", pw->pw_dir) == -1)
56249057Sgreen			iderror(lport, fport, s, errno);
56349089Sgreen		if (lstat(p, &sb) == 0) {
56449089Sgreen			free(p);
56549057Sgreen			iderror(lport, fport, s, -1);
56649057Sgreen		}
56749089Sgreen		free(p);
56849057Sgreen	}
56949104Sgreen	/*
57049104Sgreen	 * Here, if enabled, we read a user's ".fakeid" file in their
57149104Sgreen	 * home directory. It consists of a line containing the name
57249104Sgreen	 * they want.
57349104Sgreen	 */
57456298Sgreen	if (fflag && !usedfallback) {
57549033Sgreen		FILE *fakeid = NULL;
57649033Sgreen
57749089Sgreen		if (asprintf(&p, "%s/.fakeid", pw->pw_dir) == -1)
57849057Sgreen			iderror(lport, fport, s, errno);
57949104Sgreen		/*
58049104Sgreen		 * Here we set ourself to effectively be the user, so we don't
58149104Sgreen		 * open any files we have no permission to open, especially
58249104Sgreen		 * symbolic links to sensitive root-owned files or devices.
58349104Sgreen		 */
58448981Ssheldonh		seteuid(pw->pw_uid);
58548981Ssheldonh		setegid(pw->pw_gid);
58649104Sgreen		/*
58749104Sgreen		 * If we were to lstat() here, it would do no good, since it
58849104Sgreen		 * would introduce a race condition and could be defeated.
58949104Sgreen		 * Therefore, we open the file we have permissions to open
59049104Sgreen		 * and if it's not a regular file, we close it and end up
59149104Sgreen		 * returning the user's real username.
59249104Sgreen		 */
59349089Sgreen		fakeid = fopen(p, "r");
59449089Sgreen		free(p);
59549089Sgreen		if (fakeid != NULL &&
59648981Ssheldonh		    fstat(fileno(fakeid), &sb) != -1 && S_ISREG(sb.st_mode)) {
59748981Ssheldonh			buf[sizeof(buf) - 1] = '\0';
59848981Ssheldonh			if (fgets(buf, sizeof(buf), fakeid) == NULL) {
59948981Ssheldonh				cp = pw->pw_name;
60048981Ssheldonh				fclose(fakeid);
60148981Ssheldonh				goto printit;
60248981Ssheldonh			}
60348981Ssheldonh			fclose(fakeid);
60449104Sgreen			/*
60549104Sgreen			 * Usually, the file will have the desired identity
60649104Sgreen			 * in the form "identity\n", so we use strtok() to
60749104Sgreen			 * end the string (which fgets() doesn't do.)
60849104Sgreen			 */
60961099Sgreen			buf[strcspn(buf, "\r\n")] = '\0';
61048981Ssheldonh			cp = buf;
61149104Sgreen			/* Allow for beginning white space... */
61248981Ssheldonh			while (isspace(*cp))
61348981Ssheldonh				cp++;
61449104Sgreen			/* ...and ending white space. */
61561099Sgreen			cp[strcspn(cp, " \t")] = '\0';
61661099Sgreen			/* User names of >16 characters are invalid */
61761099Sgreen			if (strlen(cp) > 16)
61861099Sgreen				cp[16] = '\0';
61949104Sgreen			/*
62049104Sgreen			 * If the name is a zero-length string or matches
62149104Sgreen			 * the name of another user, it's invalid, so
62249104Sgreen			 * we will return their real identity instead.
62349104Sgreen			 */
62449104Sgreen
62548981Ssheldonh			if (!*cp || getpwnam(cp))
62648981Ssheldonh				cp = getpwuid(uc.cr_uid)->pw_name;
62748981Ssheldonh		} else
62848981Ssheldonh			cp = pw->pw_name;
62956298Sgreen	} else if (!usedfallback)
63048981Ssheldonh		cp = pw->pw_name;
63156298Sgreen	else
63256298Sgreen		cp = fallback;
63348981Ssheldonhprintit:
63449104Sgreen	/* Finally, we make and send the reply. */
63549004Sgreen	if (asprintf(&p, "%d , %d : USERID : %s : %s\r\n", lport, fport, osname,
63649051Ssheldonh	    cp) == -1) {
63749057Sgreen		syslog(LOG_ERR, "asprintf: %m");
63849051Ssheldonh		exit(EX_OSERR);
63949051Ssheldonh	}
64058735Ssheldonh	send(s, p, strlen(p), MSG_EOF);
64149004Sgreen	free(p);
64248981Ssheldonh
64348981Ssheldonh	exit(0);
64448981Ssheldonh}
64548981Ssheldonh
64648981Ssheldonh/*
64749052Ssheldonh * RFC738 Time Server.
64848981Ssheldonh * Return a machine readable date and time, in the form of the
64948981Ssheldonh * number of seconds since midnight, Jan 1, 1900.  Since gettimeofday
65048981Ssheldonh * returns the number of seconds since midnight, Jan 1, 1970,
65148981Ssheldonh * we must add 2208988800 seconds to this figure to make up for
65248981Ssheldonh * some seventy years Bell Labs was asleep.
65348981Ssheldonh */
65448981Ssheldonh
65548981Ssheldonhunsigned long
65648981Ssheldonhmachtime()
65748981Ssheldonh{
65848981Ssheldonh	struct timeval tv;
65948981Ssheldonh
66048981Ssheldonh	if (gettimeofday(&tv, (struct timezone *)NULL) < 0) {
66148981Ssheldonh		if (debug)
66248981Ssheldonh			warnx("unable to get time of day");
66348981Ssheldonh		return (0L);
66448981Ssheldonh	}
66548981Ssheldonh#define	OFFSET ((u_long)25567 * 24*60*60)
66648981Ssheldonh	return (htonl((long)(tv.tv_sec + OFFSET)));
66748981Ssheldonh#undef OFFSET
66848981Ssheldonh}
66948981Ssheldonh
67048981Ssheldonh/* ARGSUSED */
67148981Ssheldonhvoid
67248981Ssheldonhmachtime_dg(s, sep)
67348981Ssheldonh	int s;
67448981Ssheldonh	struct servtab *sep;
67548981Ssheldonh{
67648981Ssheldonh	unsigned long result;
67756590Sshin	struct sockaddr_storage ss;
67857857Sshin	socklen_t size;
67948981Ssheldonh
68056590Sshin	size = sizeof(ss);
68148981Ssheldonh	if (recvfrom(s, (char *)&result, sizeof(result), 0,
68256590Sshin		     (struct sockaddr *)&ss, &size) < 0)
68348981Ssheldonh		return;
68448981Ssheldonh
68556590Sshin	if (check_loop((struct sockaddr *)&ss, sep))
68648981Ssheldonh		return;
68748981Ssheldonh
68848981Ssheldonh	result = machtime();
68948981Ssheldonh	(void) sendto(s, (char *) &result, sizeof(result), 0,
69057857Sshin		      (struct sockaddr *)&ss, size);
69148981Ssheldonh}
69248981Ssheldonh
69348981Ssheldonh/* ARGSUSED */
69448981Ssheldonhvoid
69548981Ssheldonhmachtime_stream(s, sep)
69648981Ssheldonh	int s;
69748981Ssheldonh	struct servtab *sep;
69848981Ssheldonh{
69948981Ssheldonh	unsigned long result;
70048981Ssheldonh
70148981Ssheldonh	result = machtime();
70258735Ssheldonh	(void) send(s, (char *) &result, sizeof(result), MSG_EOF);
70348981Ssheldonh}
70448981Ssheldonh
70548981Ssheldonh/*
70649052Ssheldonh * RFC1078 TCP Port Service Multiplexer (TCPMUX). Service connections to
70749052Ssheldonh * services based on the service name sent.
70849052Ssheldonh *
70948981Ssheldonh *  Based on TCPMUX.C by Mark K. Lottor November 1988
71048981Ssheldonh *  sri-nic::ps:<mkl>tcpmux.c
71148981Ssheldonh */
71248981Ssheldonh
71348981Ssheldonh#define MAX_SERV_LEN	(256+2)		/* 2 bytes for \r\n */
71448981Ssheldonh#define strwrite(fd, buf)	(void) write(fd, buf, sizeof(buf)-1)
71548981Ssheldonh
71648981Ssheldonhstatic int		/* # of characters upto \r,\n or \0 */
71748981Ssheldonhgetline(fd, buf, len)
71848981Ssheldonh	int fd;
71948981Ssheldonh	char *buf;
72048981Ssheldonh	int len;
72148981Ssheldonh{
72248981Ssheldonh	int count = 0, n;
72348981Ssheldonh	struct sigaction sa;
72448981Ssheldonh
72548981Ssheldonh	sa.sa_flags = 0;
72648981Ssheldonh	sigemptyset(&sa.sa_mask);
72748981Ssheldonh	sa.sa_handler = SIG_DFL;
72848981Ssheldonh	sigaction(SIGALRM, &sa, (struct sigaction *)0);
72948981Ssheldonh	do {
73048981Ssheldonh		alarm(10);
73148981Ssheldonh		n = read(fd, buf, len-count);
73248981Ssheldonh		alarm(0);
73348981Ssheldonh		if (n == 0)
73448981Ssheldonh			return (count);
73548981Ssheldonh		if (n < 0)
73648981Ssheldonh			return (-1);
73748981Ssheldonh		while (--n >= 0) {
73848981Ssheldonh			if (*buf == '\r' || *buf == '\n' || *buf == '\0')
73948981Ssheldonh				return (count);
74048981Ssheldonh			count++;
74148981Ssheldonh			buf++;
74248981Ssheldonh		}
74348981Ssheldonh	} while (count < len);
74448981Ssheldonh	return (count);
74548981Ssheldonh}
74648981Ssheldonh
74748981Ssheldonhstruct servtab *
74848981Ssheldonhtcpmux(s)
74948981Ssheldonh	int s;
75048981Ssheldonh{
75148981Ssheldonh	struct servtab *sep;
75248981Ssheldonh	char service[MAX_SERV_LEN+1];
75348981Ssheldonh	int len;
75448981Ssheldonh
75548981Ssheldonh	/* Get requested service name */
75648981Ssheldonh	if ((len = getline(s, service, MAX_SERV_LEN)) < 0) {
75748981Ssheldonh		strwrite(s, "-Error reading service name\r\n");
75848981Ssheldonh		return (NULL);
75948981Ssheldonh	}
76048981Ssheldonh	service[len] = '\0';
76148981Ssheldonh
76248981Ssheldonh	if (debug)
76348981Ssheldonh		warnx("tcpmux: someone wants %s", service);
76448981Ssheldonh
76548981Ssheldonh	/*
76648981Ssheldonh	 * Help is a required command, and lists available services,
76748981Ssheldonh	 * one per line.
76848981Ssheldonh	 */
76948981Ssheldonh	if (!strcasecmp(service, "help")) {
77048981Ssheldonh		for (sep = servtab; sep; sep = sep->se_next) {
77148981Ssheldonh			if (!ISMUX(sep))
77248981Ssheldonh				continue;
77348981Ssheldonh			(void)write(s,sep->se_service,strlen(sep->se_service));
77448981Ssheldonh			strwrite(s, "\r\n");
77548981Ssheldonh		}
77648981Ssheldonh		return (NULL);
77748981Ssheldonh	}
77848981Ssheldonh
77948981Ssheldonh	/* Try matching a service in inetd.conf with the request */
78048981Ssheldonh	for (sep = servtab; sep; sep = sep->se_next) {
78148981Ssheldonh		if (!ISMUX(sep))
78248981Ssheldonh			continue;
78348981Ssheldonh		if (!strcasecmp(service, sep->se_service)) {
78448981Ssheldonh			if (ISMUXPLUS(sep)) {
78548981Ssheldonh				strwrite(s, "+Go\r\n");
78648981Ssheldonh			}
78748981Ssheldonh			return (sep);
78848981Ssheldonh		}
78948981Ssheldonh	}
79048981Ssheldonh	strwrite(s, "-Service not available\r\n");
79148981Ssheldonh	return (NULL);
79248981Ssheldonh}
793