builtins.c revision 69144
1128723Sru/*-
2185562Sluigi * Copyright (c) 1983, 1991, 1993, 1994
3128723Sru *	The Regents of the University of California.  All rights reserved.
4128723Sru *
5128723Sru * Redistribution and use in source and binary forms, with or without
6128723Sru * modification, are permitted provided that the following conditions
7128723Sru * are met:
8128723Sru * 1. Redistributions of source code must retain the above copyright
9128723Sru *    notice, this list of conditions and the following disclaimer.
10128723Sru * 2. Redistributions in binary form must reproduce the above copyright
11128723Sru *    notice, this list of conditions and the following disclaimer in the
12128723Sru *    documentation and/or other materials provided with the distribution.
13128723Sru *
14128723Sru * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15128723Sru * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16128723Sru * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17128723Sru * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18128723Sru * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19128722Sru * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20185562Sluigi * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21185562Sluigi * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22138048Sjhb * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23128722Sru * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24185562Sluigi * SUCH DAMAGE.
25186598Sluigi *
26185562Sluigi * $FreeBSD: head/usr.sbin/inetd/builtins.c 69144 2000-11-25 04:13:05Z green $
27185562Sluigi *
28185562Sluigi */
29185562Sluigi
30185562Sluigi#include <sys/filio.h>
31185562Sluigi#include <sys/ioccom.h>
32185562Sluigi#include <sys/param.h>
33185562Sluigi#include <sys/stat.h>
34185579Sluigi#include <sys/socket.h>
35185579Sluigi#include <sys/sysctl.h>
36185579Sluigi#include <sys/ucred.h>
37185579Sluigi#include <sys/uio.h>
38185579Sluigi#include <sys/utsname.h>
39185579Sluigi
40185579Sluigi#include <ctype.h>
41185562Sluigi#include <err.h>
42185579Sluigi#include <errno.h>
43185579Sluigi#include <fcntl.h>
44185562Sluigi#include <limits.h>
45185562Sluigi#include <pwd.h>
46185562Sluigi#include <signal.h>
47185562Sluigi#include <stdlib.h>
48185562Sluigi#include <string.h>
49185562Sluigi#include <sysexits.h>
50185562Sluigi#include <syslog.h>
51185562Sluigi#include <unistd.h>
52185562Sluigi
53185562Sluigi#include "inetd.h"
54185562Sluigi
55185562Sluigiextern int	 debug;
56185562Sluigiextern struct servtab *servtab;
57185562Sluigi
58185562Sluigichar ring[128];
59185562Sluigichar *endring;
60185562Sluigi
61185562Sluigiint check_loop __P((struct sockaddr *, struct servtab *sep));
62185562Sluigivoid inetd_setproctitle __P((char *, int));
63185562Sluigi
64185562Sluigistruct biltin biltins[] = {
65185562Sluigi	/* Echo received data */
66185562Sluigi	{ "echo",	SOCK_STREAM,	1, -1,	echo_stream },
67185562Sluigi	{ "echo",	SOCK_DGRAM,	0, 1,	echo_dg },
68185562Sluigi
69185562Sluigi	/* Internet /dev/null */
70185562Sluigi	{ "discard",	SOCK_STREAM,	1, -1,	discard_stream },
71185562Sluigi	{ "discard",	SOCK_DGRAM,	0, 1,	discard_dg },
72185562Sluigi
73185562Sluigi	/* Return 32 bit time since 1970 */
74185562Sluigi	{ "time",	SOCK_STREAM,	0, -1,	machtime_stream },
75185562Sluigi	{ "time",	SOCK_DGRAM,	0, 1,	machtime_dg },
76185562Sluigi
77185562Sluigi	/* Return human-readable time */
78185562Sluigi	{ "daytime",	SOCK_STREAM,	0, -1,	daytime_stream },
79185562Sluigi	{ "daytime",	SOCK_DGRAM,	0, 1,	daytime_dg },
80185562Sluigi
81185562Sluigi	/* Familiar character generator */
82185562Sluigi	{ "chargen",	SOCK_STREAM,	1, -1,	chargen_stream },
83185562Sluigi	{ "chargen",	SOCK_DGRAM,	0, 1,	chargen_dg },
84185562Sluigi
85185562Sluigi	{ "tcpmux",	SOCK_STREAM,	1, -1,	(void (*)())tcpmux },
86185562Sluigi
87185562Sluigi	{ "auth",	SOCK_STREAM,	1, -1,	ident_stream },
88185562Sluigi
89185562Sluigi	{ NULL }
90185562Sluigi};
91185562Sluigi
92185562Sluigi/*
93185562Sluigi * RFC864 Character Generator Protocol. Generates character data without
94185562Sluigi * any regard for input.
95185562Sluigi */
96185562Sluigi
97185776Ssobomaxvoid
98185562Sluigiinitring()
99185562Sluigi{
100185562Sluigi	int i;
101185562Sluigi
102185562Sluigi	endring = ring;
103185562Sluigi
104185562Sluigi	for (i = 0; i <= 128; ++i)
105185562Sluigi		if (isprint(i))
106185562Sluigi			*endring++ = i;
107185562Sluigi}
108185562Sluigi
109185562Sluigi/* ARGSUSED */
110185562Sluigivoid
111185562Sluigichargen_dg(s, sep)		/* Character generator */
112185562Sluigi	int s;
113185562Sluigi	struct servtab *sep;
114185562Sluigi{
115185562Sluigi	struct sockaddr_storage ss;
116185562Sluigi	static char *rs;
117185562Sluigi	int len;
118185562Sluigi	socklen_t size;
119185562Sluigi	char text[LINESIZ+2];
120185562Sluigi
121185562Sluigi	if (endring == 0) {
122185562Sluigi		initring();
123185562Sluigi		rs = ring;
124185562Sluigi	}
125185562Sluigi
126185562Sluigi	size = sizeof(ss);
127185562Sluigi	if (recvfrom(s, text, sizeof(text), 0,
128185579Sluigi		     (struct sockaddr *)&ss, &size) < 0)
129185562Sluigi		return;
130185562Sluigi
131185562Sluigi	if (check_loop((struct sockaddr *)&ss, sep))
132129239Sru		return;
133129239Sru
134129239Sru	if ((len = endring - rs) >= LINESIZ)
135128722Sru		memmove(text, rs, LINESIZ);
136129239Sru	else {
137185579Sluigi		memmove(text, rs, len);
138128722Sru		memmove(text + len, ring, LINESIZ - len);
139129239Sru	}
140128722Sru	if (++rs == endring)
141129239Sru		rs = ring;
142129239Sru	text[LINESIZ] = '\r';
143129239Sru	text[LINESIZ + 1] = '\n';
144128722Sru	(void) sendto(s, text, sizeof(text), 0, (struct sockaddr *)&ss, size);
145185562Sluigi}
146129239Sru
147128722Sru/* ARGSUSED */
148128723Sruvoid
149185562Sluigichargen_stream(s, sep)		/* Character generator */
150185562Sluigi	int s;
151185562Sluigi	struct servtab *sep;
152185562Sluigi{
153128723Sru	int len;
154185579Sluigi	char *rs, text[LINESIZ+2];
155185579Sluigi
156185579Sluigi	inetd_setproctitle(sep->se_service, s);
157185579Sluigi
158185562Sluigi	if (!endring) {
159185562Sluigi		initring();
160185562Sluigi		rs = ring;
161185579Sluigi	}
162185579Sluigi
163185579Sluigi	text[LINESIZ] = '\r';
164185562Sluigi	text[LINESIZ + 1] = '\n';
165128722Sru	for (rs = ring;;) {
166185562Sluigi		if ((len = endring - rs) >= LINESIZ)
167129239Sru			memmove(text, rs, LINESIZ);
168129239Sru		else {
169128722Sru			memmove(text, rs, len);
170128723Sru			memmove(text + len, ring, LINESIZ - len);
171185562Sluigi		}
172128723Sru		if (++rs == endring)
173128723Sru			rs = ring;
174128723Sru		if (write(s, text, sizeof(text)) != sizeof(text))
175185562Sluigi			break;
176185562Sluigi	}
177185562Sluigi	exit(0);
178185562Sluigi}
179185562Sluigi
180128723Sru/*
181129239Sru * RFC867 Daytime Protocol. Sends the current date and time as an ascii
182129239Sru * character string without any regard for input.
183129239Sru */
184129239Sru
185129239Sru/* ARGSUSED */
186129239Sruvoid
187128722Srudaytime_dg(s, sep)		/* Return human-readable time of day */
188185562Sluigi	int s;
189185562Sluigi	struct servtab *sep;
190185562Sluigi{
191129239Sru	char buffer[256];
192129239Sru	time_t clock;
193129239Sru	struct sockaddr_storage ss;
194129239Sru	socklen_t size;
195129239Sru
196185562Sluigi	clock = time((time_t *) 0);
197185562Sluigi
198185562Sluigi	size = sizeof(ss);
199185562Sluigi	if (recvfrom(s, buffer, sizeof(buffer), 0,
200185562Sluigi		     (struct sockaddr *)&ss, &size) < 0)
201185562Sluigi		return;
202129239Sru
203129239Sru	if (check_loop((struct sockaddr *)&ss, sep))
204129239Sru		return;
205129239Sru
206185562Sluigi	(void) sprintf(buffer, "%.24s\r\n", ctime(&clock));
207128722Sru	(void) sendto(s, buffer, strlen(buffer), 0,
208185562Sluigi		      (struct sockaddr *)&ss, size);
209185562Sluigi}
210128722Sru
211138048Sjhb/* ARGSUSED */
212185562Sluigivoid
213185562Sluigidaytime_stream(s, sep)		/* Return human-readable time of day */
214185562Sluigi	int s;
215167914Sthomas	struct servtab *sep;
216130632Sphk{
217138048Sjhb	char buffer[256];
218185562Sluigi	time_t clock;
219185562Sluigi
220185562Sluigi	clock = time((time_t *) 0);
221185562Sluigi
222185562Sluigi	(void) sprintf(buffer, "%.24s\r\n", ctime(&clock));
223185562Sluigi	(void) send(s, buffer, strlen(buffer), MSG_EOF);
224185562Sluigi}
225185562Sluigi
226185562Sluigi/*
227185562Sluigi * RFC863 Discard Protocol. Any data received is thrown away and no response
228185562Sluigi * is sent.
229185562Sluigi */
230185562Sluigi
231185562Sluigi/* ARGSUSED */
232129239Sruvoid
233185562Sluigidiscard_dg(s, sep)		/* Discard service -- ignore data */
234185562Sluigi	int s;
235185562Sluigi	struct servtab *sep;
236185562Sluigi{
237185562Sluigi	char buffer[BUFSIZE];
238185562Sluigi
239185562Sluigi	(void) read(s, buffer, sizeof(buffer));
240185562Sluigi}
241185562Sluigi
242185562Sluigi/* ARGSUSED */
243185562Sluigivoid
244185562Sluigidiscard_stream(s, sep)		/* Discard service -- ignore data */
245185562Sluigi	int s;
246185562Sluigi	struct servtab *sep;
247185562Sluigi{
248185562Sluigi	int ret;
249185562Sluigi	char buffer[BUFSIZE];
250185562Sluigi
251185562Sluigi	inetd_setproctitle(sep->se_service, s);
252185562Sluigi	while (1) {
253185562Sluigi		while ((ret = read(s, buffer, sizeof(buffer))) > 0)
254185562Sluigi			;
255185562Sluigi		if (ret == 0 || errno != EINTR)
256185562Sluigi			break;
257185562Sluigi	}
258185562Sluigi	exit(0);
259185562Sluigi}
260185562Sluigi
261129239Sru/*
262129239Sru * RFC862 Echo Protocol. Any data received is sent back to the sender as
263185562Sluigi * received.
264185562Sluigi */
265185562Sluigi
266185562Sluigi/* ARGSUSED */
267185562Sluigivoid
268185562Sluigiecho_dg(s, sep)			/* Echo service -- echo data back */
269129239Sru	int s;
270185562Sluigi	struct servtab *sep;
271129239Sru{
272185579Sluigi	char buffer[BUFSIZE];
273185579Sluigi	int i;
274185562Sluigi	socklen_t size;
275185579Sluigi	struct sockaddr_storage ss;
276185562Sluigi
277185562Sluigi	size = sizeof(ss);
278186598Sluigi	if ((i = recvfrom(s, buffer, sizeof(buffer), 0,
279186598Sluigi			  (struct sockaddr *)&ss, &size)) < 0)
280185562Sluigi		return;
281185579Sluigi
282186598Sluigi	if (check_loop((struct sockaddr *)&ss, sep))
283139948Speadar		return;
284139948Speadar
285185562Sluigi	(void) sendto(s, buffer, i, 0, (struct sockaddr *)&ss, size);
286185562Sluigi}
287185562Sluigi
288185562Sluigi/* ARGSUSED */
289185562Sluigivoid
290185562Sluigiecho_stream(s, sep)		/* Echo service -- echo data back */
291139948Speadar	int s;
292129239Sru	struct servtab *sep;
293129239Sru{
294185562Sluigi	char buffer[BUFSIZE];
295185562Sluigi	int i;
296129239Sru
297185562Sluigi	inetd_setproctitle(sep->se_service, s);
298185562Sluigi	while ((i = read(s, buffer, sizeof(buffer))) > 0 &&
299185562Sluigi	    write(s, buffer, i) > 0)
300185562Sluigi		;
301185562Sluigi	exit(0);
302185562Sluigi}
303185562Sluigi
304129239Sru/*
305129239Sru * RFC1413 Identification Protocol. Given a TCP port number pair, return a
306129239Sru * character string which identifies the owner of that connection on the
307185562Sluigi * server's system. Extended to allow for ~/.fakeid support and ~/.noident
308185562Sluigi * support.
309185562Sluigi */
310185562Sluigi
311129239Sru/* ARGSUSED */
312185562Sluigivoid
313185562Sluigiiderror(lport, fport, s, er)	/* Generic ident_stream error-sending func */
314185562Sluigi	int lport, fport, s, er;
315185562Sluigi{
316129239Sru	char *p;
317185562Sluigi
318185562Sluigi	asprintf(&p, "%d , %d : ERROR : %s\r\n", lport, fport,
319185562Sluigi	    er == -1 ? "HIDDEN-USER" : er ? strerror(er) : "UNKNOWN-ERROR");
320185562Sluigi	if (p == NULL) {
321185562Sluigi		syslog(LOG_ERR, "asprintf: %m");
322185562Sluigi		exit(EX_OSERR);
323185562Sluigi	}
324129239Sru	send(s, p, strlen(p), MSG_EOF);
325129239Sru	free(p);
326129239Sru
327185562Sluigi	exit(0);
328185562Sluigi}
329186598Sluigi
330185562Sluigi/* ARGSUSED */
331185562Sluigivoid
332185562Sluigiident_stream(s, sep)		/* Ident service (AKA "auth") */
333129239Sru	int s;
334129239Sru	struct servtab *sep;
335129239Sru{
336129239Sru	struct utsname un;
337185562Sluigi	struct stat sb;
338185562Sluigi	struct sockaddr_in sin[2];
339130343Sphk#ifdef INET6
340185562Sluigi	struct sockaddr_in6 sin6[2];
341130343Sphk#endif
342185562Sluigi	struct sockaddr_storage ss[2];
343185562Sluigi	struct ucred uc;
344185562Sluigi	struct timeval tv = {
345185562Sluigi		10,
346185562Sluigi		0
347185562Sluigi	}, to;
348185562Sluigi	struct passwd *pw = NULL;
349185562Sluigi	fd_set fdset;
350129239Sru	char buf[BUFSIZE], *cp = NULL, *p, **av, *osname = NULL, garbage[7], e;
351129239Sru	char *fallback = NULL;
352130343Sphk	socklen_t socklen;
353185562Sluigi	ssize_t ssize;
354185562Sluigi	size_t size, bufsiz;
355185562Sluigi	int c, fflag = 0, nflag = 0, rflag = 0, argc = 0, usedfallback = 0;
356185562Sluigi	int gflag = 0, getcredfail = 0, onreadlen;
357128722Sru	u_short lport, fport;
358129239Sru
359129239Sru	inetd_setproctitle(sep->se_service, s);
360128722Sru	/*
361129239Sru	 * Reset getopt() since we are a fork() but not an exec() from
362130343Sphk	 * a parent which used getopt() already.
363129239Sru	 */
364185562Sluigi	optind = 1;
365128722Sru	optreset = 1;
366185562Sluigi	/*
367185562Sluigi	 * Take the internal argument vector and count it out to make an
368185562Sluigi	 * argument count for getopt. This can be used for any internal
369129239Sru	 * service to read arguments and use getopt() easily.
370185562Sluigi	 */
371185562Sluigi	for (av = sep->se_argv; *av; av++)
372185562Sluigi		argc++;
373185562Sluigi	if (argc) {
374185562Sluigi		int sec, usec;
375185562Sluigi		size_t i;
376186598Sluigi		u_int32_t random;
377185562Sluigi
378185562Sluigi		while ((c = getopt(argc, sep->se_argv, "d:fgno:rt:")) != -1)
379185562Sluigi			switch (c) {
380185562Sluigi			case 'd':
381185562Sluigi				fallback = optarg;
382185562Sluigi				break;
383185562Sluigi			case 'f':
384185562Sluigi				fflag = 1;
385185562Sluigi				break;
386185562Sluigi			case 'g':
387128722Sru				gflag = 1;
388185562Sluigi				random = 0;	/* Shush, compiler. */
389185562Sluigi				/*
390185562Sluigi				 * The number of bits in "random" divided
391185562Sluigi				 * by the number of bits needed per iteration
392138048Sjhb				 * gives a more optimal way to reload the
393129239Sru				 * random number only when necessary.
394130343Sphk				 *
395185562Sluigi				 * I'm using base-36, so I need at least 6
396138048Sjhb				 * bits; round it up to 8 bits to make it
397185562Sluigi				 * easier.
398185562Sluigi				 */
399185562Sluigi				for (i = 0; i < sizeof(garbage) - 1; i++) {
400185562Sluigi					const char *const base36 =
401185562Sluigi					    "0123456789"
402185562Sluigi					    "abcdefghijklmnopqrstuvwxyz";
403185562Sluigi					if (i % (sizeof(random) * 8 / 8) == 0)
404185562Sluigi						random = arc4random();
405185562Sluigi					garbage[i] =
406185562Sluigi					    base36[(random & 0xff) % 36];
407185562Sluigi					random >>= 8;
408185562Sluigi				}
409185562Sluigi				garbage[i] = '\0';
410185562Sluigi				break;
411185562Sluigi			case 'n':
412129239Sru				nflag = 1;
413185562Sluigi				break;
414185562Sluigi			case 'o':
415185562Sluigi				osname = optarg;
416228738Sjhb				break;
417185562Sluigi			case 'r':
418185562Sluigi				rflag = 1;
419185562Sluigi				break;
420185562Sluigi			case 't':
421185562Sluigi				switch (sscanf(optarg, "%d.%d", &sec, &usec)) {
422185562Sluigi				case 2:
423185562Sluigi					tv.tv_usec = usec;
424185562Sluigi				case 1:
425185562Sluigi					tv.tv_sec = sec;
426185562Sluigi					break;
427185562Sluigi				default:
428185562Sluigi					if (debug)
429185562Sluigi						warnx("bad -t argument");
430185562Sluigi					break;
431185562Sluigi				}
432185562Sluigi				break;
433185562Sluigi			default:
434185562Sluigi				break;
435185562Sluigi			}
436185562Sluigi	}
437129239Sru	if (osname == NULL) {
438185562Sluigi		if (uname(&un) == -1)
439185562Sluigi			iderror(0, 0, s, errno);
440185562Sluigi		osname = un.sysname;
441185562Sluigi	}
442185562Sluigi	socklen = sizeof(ss[0]);
443185562Sluigi	if (getsockname(s, (struct sockaddr *)&ss[0], &socklen) == -1)
444185562Sluigi		iderror(0, 0, s, errno);
445185562Sluigi	socklen = sizeof(ss[1]);
446185562Sluigi	if (getpeername(s, (struct sockaddr *)&ss[1], &socklen) == -1)
447185562Sluigi		iderror(0, 0, s, errno);
448129239Sru	/*
449129239Sru	 * We're going to prepare for and execute reception of a
450185562Sluigi	 * packet of data from the user. The data is in the format
451185562Sluigi	 * "local_port , foreign_port\r\n" (with local being the
452185562Sluigi	 * server's port and foreign being the client's.)
453185562Sluigi	 */
454185562Sluigi	gettimeofday(&to, NULL);
455185562Sluigi	to.tv_sec += tv.tv_sec;
456185562Sluigi	if ((to.tv_usec += tv.tv_usec) >= 1000000) {
457129239Sru		to.tv_usec -= 1000000;
458129239Sru		to.tv_sec++;
459129239Sru	}
460129239Sru
461185562Sluigi	size = 0;
462185562Sluigi	bufsiz = sizeof(buf) - 1;
463185562Sluigi	FD_ZERO(&fdset);
464185562Sluigi 	while (bufsiz > 0 && (size == 0 || buf[size - 1] != '\n')) {
465185562Sluigi		gettimeofday(&tv, NULL);
466185562Sluigi		tv.tv_sec = to.tv_sec - tv.tv_sec;
467185562Sluigi		tv.tv_usec = to.tv_usec - tv.tv_usec;
468185562Sluigi		if (tv.tv_usec < 0) {
469129239Sru			tv.tv_usec += 1000000;
470129239Sru			tv.tv_sec--;
471129239Sru		}
472185562Sluigi		if (tv.tv_sec < 0)
473185562Sluigi			break;
474185562Sluigi		FD_SET(s, &fdset);
475185562Sluigi		if (select(s + 1, &fdset, NULL, NULL, &tv) == -1)
476185562Sluigi			iderror(0, 0, s, errno);
477185562Sluigi		if (ioctl(s, FIONREAD, &onreadlen) == -1)
478185562Sluigi			iderror(0, 0, s, errno);
479185562Sluigi		if (onreadlen > bufsiz)
480129239Sru			onreadlen = bufsiz;
481129239Sru		ssize = read(s, &buf[size], (size_t)onreadlen);
482185562Sluigi		if (ssize == -1)
483185562Sluigi			iderror(0, 0, s, errno);
484185562Sluigi		else if (ssize == 0)
485185562Sluigi			break;
486185562Sluigi		bufsiz -= ssize;
487129239Sru		size += ssize;
488129239Sru 	}
489185562Sluigi	buf[size] = '\0';
490129239Sru	/* Read two characters, and check for a delimiting character */
491185562Sluigi	if (sscanf(buf, "%hu , %hu%c", &lport, &fport, &e) != 3 || isdigit(e))
492185339Sluigi		iderror(0, 0, s, 0);
493185346Sluigi	if (gflag) {
494185339Sluigi		cp = garbage;
495129239Sru		goto printit;
496128722Sru	}
497128723Sru
498128723Sru	/*
499185562Sluigi	 * If not "real" (-r), send a HIDDEN-USER error for everything.
500185562Sluigi	 * If -d is used to set a fallback username, this is used to
501185562Sluigi	 * override it, and the fallback is returned instead.
502185562Sluigi	 */
503185562Sluigi	if (!rflag) {
504185562Sluigi		if (fallback == NULL)
505185562Sluigi			iderror(lport, fport, s, -1);
506128723Sru		else {
507128722Sru			cp = fallback;
508128723Sru			goto printit;
509185562Sluigi		}
510185562Sluigi	}
511185562Sluigi
512128723Sru	/*
513129239Sru	 * We take the input and construct an array of two sockaddr_ins
514129239Sru	 * which contain the local address information and foreign
515129239Sru	 * address information, respectively, used to look up the
516129239Sru	 * credentials for the socket (which are returned by the
517185579Sluigi	 * sysctl "net.inet.tcp.getcred" when we call it.) The
518128722Sru	 * arrays have been filled in above via get{peer,sock}name(),
519129239Sru	 * so right here we are only setting the ports.
520185579Sluigi	 */
521128722Sru	if (ss[0].ss_family != ss[1].ss_family)
522185579Sluigi		iderror(lport, fport, s, errno);
523185579Sluigi	size = sizeof(uc);
524185579Sluigi	switch (ss[0].ss_family) {
525185579Sluigi	case AF_INET:
526185579Sluigi		sin[0] = *(struct sockaddr_in *)&ss[0];
527185579Sluigi		sin[0].sin_port = htons(lport);
528185579Sluigi		sin[1] = *(struct sockaddr_in *)&ss[1];
529185579Sluigi		sin[1].sin_port = htons(fport);
530185579Sluigi		if (sysctlbyname("net.inet.tcp.getcred", &uc, &size, sin,
531129239Sru				 sizeof(sin)) == -1)
532129239Sru			getcredfail = 1;
533185579Sluigi		break;
534185579Sluigi#ifdef INET6
535128722Sru	case AF_INET6:
536185562Sluigi		sin6[0] = *(struct sockaddr_in6 *)&ss[0];
537130343Sphk		sin6[0].sin6_port = htons(lport);
538129239Sru		sin6[1] = *(struct sockaddr_in6 *)&ss[1];
539129239Sru		sin6[1].sin6_port = htons(fport);
540129239Sru		if (sysctlbyname("net.inet6.tcp6.getcred", &uc, &size, sin6,
541129239Sru				 sizeof(sin6)) == -1)
542129239Sru			getcredfail = 1;
543128722Sru		break;
544130343Sphk#endif
545130343Sphk	default: /* should not reach here */
546129239Sru		getcredfail = 1;
547129239Sru		break;
548129239Sru	}
549129239Sru	if (getcredfail != 0) {
550185562Sluigi		if (fallback == NULL)		/* Use a default, if asked to */
551130343Sphk			iderror(lport, fport, s, errno);
552128722Sru		usedfallback = 1;
553128723Sru	} else {
554128722Sru		/* Look up the pw to get the username */
555185562Sluigi		pw = getpwuid(uc.cr_uid);
556185562Sluigi	}
557185562Sluigi	if (pw == NULL && !usedfallback)		/* No such user... */
558185562Sluigi		iderror(lport, fport, s, errno);
559185562Sluigi	/*
560185562Sluigi	 * If enabled, we check for a file named ".noident" in the user's
561185562Sluigi	 * home directory. If found, we return HIDDEN-USER.
562185562Sluigi	 */
563129239Sru	if (nflag && !usedfallback) {
564129239Sru		if (asprintf(&p, "%s/.noident", pw->pw_dir) == -1)
565129239Sru			iderror(lport, fport, s, errno);
566129239Sru		if (lstat(p, &sb) == 0) {
567185562Sluigi			free(p);
568185562Sluigi			iderror(lport, fport, s, -1);
569185562Sluigi		}
570185562Sluigi		free(p);
571185562Sluigi	}
572185562Sluigi	/*
573129239Sru	 * Here, if enabled, we read a user's ".fakeid" file in their
574129239Sru	 * home directory. It consists of a line containing the name
575129239Sru	 * they want.
576129239Sru	 */
577129239Sru	if (fflag && !usedfallback) {
578129239Sru		FILE *fakeid = NULL;
579129239Sru		int fakeid_fd;
580129239Sru
581129239Sru		if (asprintf(&p, "%s/.fakeid", pw->pw_dir) == -1)
582185562Sluigi			iderror(lport, fport, s, errno);
583129239Sru		/*
584129239Sru		 * Here we set ourself to effectively be the user, so we don't
585129239Sru		 * open any files we have no permission to open, especially
586128722Sru		 * symbolic links to sensitive root-owned files or devices.
587185562Sluigi		 */
588185562Sluigi		if (initgroups(pw->pw_name, pw->pw_gid) == -1)
589185562Sluigi			iderror(lport, fport, s, errno);
590185562Sluigi		seteuid(pw->pw_uid);
591186598Sluigi		/*
592185562Sluigi		 * If we were to lstat() here, it would do no good, since it
593186598Sluigi		 * would introduce a race condition and could be defeated.
594186598Sluigi		 * Therefore, we open the file we have permissions to open
595186598Sluigi		 * and if it's not a regular file, we close it and end up
596185562Sluigi		 * returning the user's real username.
597128722Sru		 */
598128722Sru		fakeid_fd = open(p, O_RDONLY | O_NONBLOCK);
599128723Sru		free(p);
600128722Sru		if ((fakeid = fdopen(fakeid_fd, "r")) != NULL &&
601185562Sluigi		    fstat(fileno(fakeid), &sb) != -1 && S_ISREG(sb.st_mode)) {
602185562Sluigi			buf[sizeof(buf) - 1] = '\0';
603185579Sluigi			if (fgets(buf, sizeof(buf), fakeid) == NULL) {
604185579Sluigi				cp = pw->pw_name;
605185579Sluigi				fclose(fakeid);
606185562Sluigi				goto printit;
607186598Sluigi			}
608185579Sluigi			fclose(fakeid);
609186598Sluigi			/*
610185579Sluigi			 * Usually, the file will have the desired identity
611186598Sluigi			 * in the form "identity\n", so we use strcspn() to
612186598Sluigi			 * end the string (which fgets() doesn't do.)
613186598Sluigi			 */
614186598Sluigi			buf[strcspn(buf, "\r\n")] = '\0';
615185579Sluigi			cp = buf;
616185562Sluigi			/* Allow for beginning white space... */
617185562Sluigi			while (isspace(*cp))
618185562Sluigi				cp++;
619185562Sluigi			/* ...and ending white space. */
620185562Sluigi			cp[strcspn(cp, " \t")] = '\0';
621185562Sluigi			/* User names of >16 characters are invalid */
622185562Sluigi			if (strlen(cp) > 16)
623185562Sluigi				cp[16] = '\0';
624185562Sluigi			/*
625185562Sluigi			 * If the name is a zero-length string or matches
626186598Sluigi			 * the name of another user, it's invalid, so
627186598Sluigi			 * we will return their real identity instead.
628186598Sluigi			 */
629186598Sluigi
630185579Sluigi			if (!*cp || getpwnam(cp)) {
631185579Sluigi				pw = getpwuid(uc.cr_uid);
632185579Sluigi				if (pw == NULL)
633186598Sluigi					iderror(lport, fport, s, errno);
634186598Sluigi				cp = pw->pw_name;
635186598Sluigi			}
636186598Sluigi		} else
637139948Speadar			cp = pw->pw_name;
638185562Sluigi		if (fakeid_fd != -1)
639185562Sluigi			close(fakeid_fd);
640185562Sluigi	} else if (!usedfallback)
641185562Sluigi		cp = pw->pw_name;
642185562Sluigi	else
643185562Sluigi		cp = fallback;
644185562Sluigiprintit:
645186598Sluigi	/* Finally, we make and send the reply. */
646185562Sluigi	if (asprintf(&p, "%d , %d : USERID : %s : %s\r\n", lport, fport, osname,
647185562Sluigi	    cp) == -1) {
648186598Sluigi		syslog(LOG_ERR, "asprintf: %m");
649128722Sru		exit(EX_OSERR);
650128722Sru	}
651128722Sru	send(s, p, strlen(p), MSG_EOF);
652185579Sluigi	free(p);
653185579Sluigi
654185579Sluigi	exit(0);
655128722Sru}
656185579Sluigi
657128723Sru/*
658185562Sluigi * RFC738 Time Server.
659185562Sluigi * Return a machine readable date and time, in the form of the
660185562Sluigi * number of seconds since midnight, Jan 1, 1900.  Since gettimeofday
661185562Sluigi * returns the number of seconds since midnight, Jan 1, 1970,
662128723Sru * we must add 2208988800 seconds to this figure to make up for
663128723Sru * some seventy years Bell Labs was asleep.
664128723Sru */
665128722Sru
666129239Sruunsigned long
667129239Srumachtime()
668185562Sluigi{
669129239Sru	struct timeval tv;
670185579Sluigi
671185579Sluigi	if (gettimeofday(&tv, (struct timezone *)NULL) < 0) {
672185579Sluigi		if (debug)
673129239Sru			warnx("unable to get time of day");
674128722Sru		return (0L);
675185562Sluigi	}
676128723Sru#define	OFFSET ((u_long)25567 * 24*60*60)
677137298Skeramida	return (htonl((long)(tv.tv_sec + OFFSET)));
678128723Sru#undef OFFSET
679129239Sru}
680129239Sru
681185562Sluigi/* ARGSUSED */
682185562Sluigivoid
683machtime_dg(s, sep)
684	int s;
685	struct servtab *sep;
686{
687	unsigned long result;
688	struct sockaddr_storage ss;
689	socklen_t size;
690
691	size = sizeof(ss);
692	if (recvfrom(s, (char *)&result, sizeof(result), 0,
693		     (struct sockaddr *)&ss, &size) < 0)
694		return;
695
696	if (check_loop((struct sockaddr *)&ss, sep))
697		return;
698
699	result = machtime();
700	(void) sendto(s, (char *) &result, sizeof(result), 0,
701		      (struct sockaddr *)&ss, size);
702}
703
704/* ARGSUSED */
705void
706machtime_stream(s, sep)
707	int s;
708	struct servtab *sep;
709{
710	unsigned long result;
711
712	result = machtime();
713	(void) send(s, (char *) &result, sizeof(result), MSG_EOF);
714}
715
716/*
717 * RFC1078 TCP Port Service Multiplexer (TCPMUX). Service connections to
718 * services based on the service name sent.
719 *
720 *  Based on TCPMUX.C by Mark K. Lottor November 1988
721 *  sri-nic::ps:<mkl>tcpmux.c
722 */
723
724#define MAX_SERV_LEN	(256+2)		/* 2 bytes for \r\n */
725#define strwrite(fd, buf)	(void) write(fd, buf, sizeof(buf)-1)
726
727static int		/* # of characters upto \r,\n or \0 */
728getline(fd, buf, len)
729	int fd;
730	char *buf;
731	int len;
732{
733	int count = 0, n;
734	struct sigaction sa;
735
736	sa.sa_flags = 0;
737	sigemptyset(&sa.sa_mask);
738	sa.sa_handler = SIG_DFL;
739	sigaction(SIGALRM, &sa, (struct sigaction *)0);
740	do {
741		alarm(10);
742		n = read(fd, buf, len-count);
743		alarm(0);
744		if (n == 0)
745			return (count);
746		if (n < 0)
747			return (-1);
748		while (--n >= 0) {
749			if (*buf == '\r' || *buf == '\n' || *buf == '\0')
750				return (count);
751			count++;
752			buf++;
753		}
754	} while (count < len);
755	return (count);
756}
757
758struct servtab *
759tcpmux(s)
760	int s;
761{
762	struct servtab *sep;
763	char service[MAX_SERV_LEN+1];
764	int len;
765
766	/* Get requested service name */
767	if ((len = getline(s, service, MAX_SERV_LEN)) < 0) {
768		strwrite(s, "-Error reading service name\r\n");
769		return (NULL);
770	}
771	service[len] = '\0';
772
773	if (debug)
774		warnx("tcpmux: someone wants %s", service);
775
776	/*
777	 * Help is a required command, and lists available services,
778	 * one per line.
779	 */
780	if (!strcasecmp(service, "help")) {
781		for (sep = servtab; sep; sep = sep->se_next) {
782			if (!ISMUX(sep))
783				continue;
784			(void)write(s,sep->se_service,strlen(sep->se_service));
785			strwrite(s, "\r\n");
786		}
787		return (NULL);
788	}
789
790	/* Try matching a service in inetd.conf with the request */
791	for (sep = servtab; sep; sep = sep->se_next) {
792		if (!ISMUX(sep))
793			continue;
794		if (!strcasecmp(service, sep->se_service)) {
795			if (ISMUXPLUS(sep)) {
796				strwrite(s, "+Go\r\n");
797			}
798			return (sep);
799		}
800	}
801	strwrite(s, "-Service not available\r\n");
802	return (NULL);
803}
804