builtins.c revision 48981
1#include <sys/param.h>
2#include <sys/stat.h>
3#include <sys/socket.h>
4#include <sys/sysctl.h>
5#include <sys/ucred.h>
6#include <sys/uio.h>
7
8#include <ctype.h>
9#include <err.h>
10#include <errno.h>
11#include <limits.h>
12#include <pwd.h>
13#include <signal.h>
14#include <string.h>
15#include <unistd.h>
16
17#include "inetd.h"
18
19extern int	 debug;
20extern struct servtab *servtab;
21
22char ring[128];
23char *endring;
24
25int check_loop __P((struct sockaddr_in *, struct servtab *sep));
26void inetd_setproctitle __P((char *, int));
27
28struct biltin biltins[] = {
29	/* Echo received data */
30	{ "echo",	SOCK_STREAM,	1, -1,	echo_stream },
31	{ "echo",	SOCK_DGRAM,	0, 1,	echo_dg },
32
33	/* Internet /dev/null */
34	{ "discard",	SOCK_STREAM,	1, -1,	discard_stream },
35	{ "discard",	SOCK_DGRAM,	0, 1,	discard_dg },
36
37	/* Return 32 bit time since 1970 */
38	{ "time",	SOCK_STREAM,	0, -1,	machtime_stream },
39	{ "time",	SOCK_DGRAM,	0, 1,	machtime_dg },
40
41	/* Return human-readable time */
42	{ "daytime",	SOCK_STREAM,	0, -1,	daytime_stream },
43	{ "daytime",	SOCK_DGRAM,	0, 1,	daytime_dg },
44
45	/* Familiar character generator */
46	{ "chargen",	SOCK_STREAM,	1, -1,	chargen_stream },
47	{ "chargen",	SOCK_DGRAM,	0, 1,	chargen_dg },
48
49	{ "tcpmux",	SOCK_STREAM,	1, -1,	(void (*)())tcpmux },
50
51	{ "auth",	SOCK_STREAM,	1, -1,	ident_stream },
52
53	{ NULL }
54};
55
56void
57initring()
58{
59	int i;
60
61	endring = ring;
62
63	for (i = 0; i <= 128; ++i)
64		if (isprint(i))
65			*endring++ = i;
66}
67
68/* ARGSUSED */
69void
70chargen_dg(s, sep)		/* Character generator */
71	int s;
72	struct servtab *sep;
73{
74	struct sockaddr_in sin;
75	static char *rs;
76	int len, size;
77	char text[LINESIZ+2];
78
79	if (endring == 0) {
80		initring();
81		rs = ring;
82	}
83
84	size = sizeof(sin);
85	if (recvfrom(s, text, sizeof(text), 0,
86		     (struct sockaddr *)&sin, &size) < 0)
87		return;
88
89	if (check_loop(&sin, sep))
90		return;
91
92	if ((len = endring - rs) >= LINESIZ)
93		memmove(text, rs, LINESIZ);
94	else {
95		memmove(text, rs, len);
96		memmove(text + len, ring, LINESIZ - len);
97	}
98	if (++rs == endring)
99		rs = ring;
100	text[LINESIZ] = '\r';
101	text[LINESIZ + 1] = '\n';
102	(void) sendto(s, text, sizeof(text), 0,
103		      (struct sockaddr *)&sin, sizeof(sin));
104}
105
106/* ARGSUSED */
107void
108chargen_stream(s, sep)		/* Character generator */
109	int s;
110	struct servtab *sep;
111{
112	int len;
113	char *rs, text[LINESIZ+2];
114
115	inetd_setproctitle(sep->se_service, s);
116
117	if (!endring) {
118		initring();
119		rs = ring;
120	}
121
122	text[LINESIZ] = '\r';
123	text[LINESIZ + 1] = '\n';
124	for (rs = ring;;) {
125		if ((len = endring - rs) >= LINESIZ)
126			memmove(text, rs, LINESIZ);
127		else {
128			memmove(text, rs, len);
129			memmove(text + len, ring, LINESIZ - len);
130		}
131		if (++rs == endring)
132			rs = ring;
133		if (write(s, text, sizeof(text)) != sizeof(text))
134			break;
135	}
136	exit(0);
137}
138
139/* ARGSUSED */
140void
141daytime_dg(s, sep)		/* Return human-readable time of day */
142	int s;
143	struct servtab *sep;
144{
145	char buffer[256];
146	time_t clock;
147	struct sockaddr_in sin;
148	int size;
149
150	clock = time((time_t *) 0);
151
152	size = sizeof(sin);
153	if (recvfrom(s, buffer, sizeof(buffer), 0,
154		     (struct sockaddr *)&sin, &size) < 0)
155		return;
156
157	if (check_loop(&sin, sep))
158		return;
159
160	(void) sprintf(buffer, "%.24s\r\n", ctime(&clock));
161	(void) sendto(s, buffer, strlen(buffer), 0,
162		      (struct sockaddr *)&sin, sizeof(sin));
163}
164
165/* ARGSUSED */
166void
167daytime_stream(s, sep)		/* Return human-readable time of day */
168	int s;
169	struct servtab *sep;
170{
171	char buffer[256];
172	time_t clock;
173
174	clock = time((time_t *) 0);
175
176	(void) sprintf(buffer, "%.24s\r\n", ctime(&clock));
177	(void) write(s, buffer, strlen(buffer));
178}
179
180/* ARGSUSED */
181void
182discard_dg(s, sep)		/* Discard service -- ignore data */
183	int s;
184	struct servtab *sep;
185{
186	char buffer[BUFSIZE];
187
188	(void) read(s, buffer, sizeof(buffer));
189}
190
191/* ARGSUSED */
192void
193discard_stream(s, sep)		/* Discard service -- ignore data */
194	int s;
195	struct servtab *sep;
196{
197	int ret;
198	char buffer[BUFSIZE];
199
200	inetd_setproctitle(sep->se_service, s);
201	while (1) {
202		while ((ret = read(s, buffer, sizeof(buffer))) > 0)
203			;
204		if (ret == 0 || errno != EINTR)
205			break;
206	}
207	exit(0);
208}
209
210/* ARGSUSED */
211void
212echo_dg(s, sep)			/* Echo service -- echo data back */
213	int s;
214	struct servtab *sep;
215{
216	char buffer[BUFSIZE];
217	int i, size;
218	struct sockaddr_in sin;
219
220	size = sizeof(sin);
221	if ((i = recvfrom(s, buffer, sizeof(buffer), 0,
222			  (struct sockaddr *)&sin, &size)) < 0)
223		return;
224
225	if (check_loop(&sin, sep))
226		return;
227
228	(void) sendto(s, buffer, i, 0, (struct sockaddr *)&sin,
229		      sizeof(sin));
230}
231
232/* ARGSUSED */
233void
234echo_stream(s, sep)		/* Echo service -- echo data back */
235	int s;
236	struct servtab *sep;
237{
238	char buffer[BUFSIZE];
239	int i;
240
241	inetd_setproctitle(sep->se_service, s);
242	while ((i = read(s, buffer, sizeof(buffer))) > 0 &&
243	    write(s, buffer, i) > 0)
244		;
245	exit(0);
246}
247
248/* ARGSUSED */
249void
250iderror(lport, fport, fp, er)
251	int lport, fport, er;
252	FILE *fp;
253{
254	fprintf(fp, "%d , %d : ERROR : %s\r\n", lport, fport,
255	    er == -1 ? "HIDDEN-USER" : er ? strerror(er) : "UNKNOWN-ERROR");
256	fflush(fp);
257	fclose(fp);
258
259	exit(0);
260}
261
262/* ARGSUSED */
263void
264ident_stream(s, sep)		/* Ident service */
265	int s;
266	struct servtab *sep;
267{
268	struct sockaddr_in sin[2];
269	struct ucred uc;
270	struct passwd *pw;
271	FILE *fp;
272	char buf[BUFSIZE], *cp, **av;
273	int len, c, rflag = 0, fflag = 0, argc = 0;
274	u_short lport, fport;
275
276	inetd_setproctitle(sep->se_service, s);
277	optind = 1;
278	optreset = 1;
279	for (av = sep->se_argv; *av; av++)
280		argc++;
281	if (argc) {
282		while ((c = getopt(argc, sep->se_argv, "fr")) != -1)
283			switch (c) {
284			case 'f':
285				fflag = 1;
286				break;
287			case 'r':
288				rflag = 1;
289				break;
290			default:
291				break;
292			}
293	}
294	fp = fdopen(s, "r+");
295	len = sizeof(sin[0]);
296	if (getsockname(s, (struct sockaddr *)&sin[0], &len) == -1)
297		iderror(0, 0, fp, errno);
298	len = sizeof(sin[1]);
299	if (getpeername(s, (struct sockaddr *)&sin[1], &len) == -1)
300		iderror(0, 0, fp, errno);
301	errno = 0;
302	if (fgets(buf, sizeof(buf), fp) == NULL)
303		iderror(0, 0, fp, errno);
304	buf[BUFSIZE - 1] = '\0';
305	strtok(buf, "\r\n");
306	cp = strtok(buf, ",");
307	if (cp == NULL || sscanf(cp, "%hu", &lport) != 1)
308		iderror(0, 0, fp, 0);
309	cp = strtok(NULL, ",");
310	if (cp == NULL || sscanf(cp, "%hu", &fport) != 1)
311		iderror(0, 0, fp, 0);
312	if (!rflag)
313		iderror(lport, fport, fp, -1);
314	sin[0].sin_port = htons(lport);
315	sin[1].sin_port = htons(fport);
316	len = sizeof(uc);
317	if (sysctlbyname("net.inet.tcp.getcred", &uc, &len, sin,
318	    sizeof(sin)) == -1)
319		iderror(lport, fport, fp, errno);
320	pw = getpwuid(uc.cr_uid);
321	if (pw == NULL)
322		iderror(lport, fport, fp, errno);
323	if (fflag) {
324		FILE *fakeid = NULL;
325		char fakeid_path[PATH_MAX];
326		struct stat sb;
327		seteuid(pw->pw_uid);
328		setegid(pw->pw_gid);
329		snprintf(fakeid_path, sizeof(fakeid_path), "%s/.fakeid",
330		    pw->pw_dir);
331		if ((fakeid = fopen(fakeid_path, "r")) != NULL &&
332		    fstat(fileno(fakeid), &sb) != -1 && S_ISREG(sb.st_mode)) {
333			buf[sizeof(buf) - 1] = '\0';
334			if (fgets(buf, sizeof(buf), fakeid) == NULL) {
335				cp = pw->pw_name;
336				fclose(fakeid);
337				goto printit;
338			}
339			fclose(fakeid);
340			strtok(buf, "\r\n");
341			if (strlen(buf) > 16)
342				buf[16] = '\0';
343			cp = buf;
344			while (isspace(*cp))
345				cp++;
346			strtok(cp, " \t");
347			if (!*cp || getpwnam(cp))
348				cp = getpwuid(uc.cr_uid)->pw_name;
349		} else
350			cp = pw->pw_name;
351	} else
352		cp = pw->pw_name;
353printit:
354	fprintf(fp, "%d , %d : USERID : FreeBSD :%s\r\n", lport, fport,
355	    cp);
356	fflush(fp);
357	fclose(fp);
358
359	exit(0);
360}
361
362/*
363 * Return a machine readable date and time, in the form of the
364 * number of seconds since midnight, Jan 1, 1900.  Since gettimeofday
365 * returns the number of seconds since midnight, Jan 1, 1970,
366 * we must add 2208988800 seconds to this figure to make up for
367 * some seventy years Bell Labs was asleep.
368 */
369
370unsigned long
371machtime()
372{
373	struct timeval tv;
374
375	if (gettimeofday(&tv, (struct timezone *)NULL) < 0) {
376		if (debug)
377			warnx("unable to get time of day");
378		return (0L);
379	}
380#define	OFFSET ((u_long)25567 * 24*60*60)
381	return (htonl((long)(tv.tv_sec + OFFSET)));
382#undef OFFSET
383}
384
385/* ARGSUSED */
386void
387machtime_dg(s, sep)
388	int s;
389	struct servtab *sep;
390{
391	unsigned long result;
392	struct sockaddr_in sin;
393	int size;
394
395	size = sizeof(sin);
396	if (recvfrom(s, (char *)&result, sizeof(result), 0,
397		     (struct sockaddr *)&sin, &size) < 0)
398		return;
399
400	if (check_loop(&sin, sep))
401		return;
402
403	result = machtime();
404	(void) sendto(s, (char *) &result, sizeof(result), 0,
405		      (struct sockaddr *)&sin, sizeof(sin));
406}
407
408/* ARGSUSED */
409void
410machtime_stream(s, sep)
411	int s;
412	struct servtab *sep;
413{
414	unsigned long result;
415
416	result = machtime();
417	(void) write(s, (char *) &result, sizeof(result));
418}
419
420/*
421 *  Based on TCPMUX.C by Mark K. Lottor November 1988
422 *  sri-nic::ps:<mkl>tcpmux.c
423 */
424
425#define MAX_SERV_LEN	(256+2)		/* 2 bytes for \r\n */
426#define strwrite(fd, buf)	(void) write(fd, buf, sizeof(buf)-1)
427
428static int		/* # of characters upto \r,\n or \0 */
429getline(fd, buf, len)
430	int fd;
431	char *buf;
432	int len;
433{
434	int count = 0, n;
435	struct sigaction sa;
436
437	sa.sa_flags = 0;
438	sigemptyset(&sa.sa_mask);
439	sa.sa_handler = SIG_DFL;
440	sigaction(SIGALRM, &sa, (struct sigaction *)0);
441	do {
442		alarm(10);
443		n = read(fd, buf, len-count);
444		alarm(0);
445		if (n == 0)
446			return (count);
447		if (n < 0)
448			return (-1);
449		while (--n >= 0) {
450			if (*buf == '\r' || *buf == '\n' || *buf == '\0')
451				return (count);
452			count++;
453			buf++;
454		}
455	} while (count < len);
456	return (count);
457}
458
459struct servtab *
460tcpmux(s)
461	int s;
462{
463	struct servtab *sep;
464	char service[MAX_SERV_LEN+1];
465	int len;
466
467	/* Get requested service name */
468	if ((len = getline(s, service, MAX_SERV_LEN)) < 0) {
469		strwrite(s, "-Error reading service name\r\n");
470		return (NULL);
471	}
472	service[len] = '\0';
473
474	if (debug)
475		warnx("tcpmux: someone wants %s", service);
476
477	/*
478	 * Help is a required command, and lists available services,
479	 * one per line.
480	 */
481	if (!strcasecmp(service, "help")) {
482		for (sep = servtab; sep; sep = sep->se_next) {
483			if (!ISMUX(sep))
484				continue;
485			(void)write(s,sep->se_service,strlen(sep->se_service));
486			strwrite(s, "\r\n");
487		}
488		return (NULL);
489	}
490
491	/* Try matching a service in inetd.conf with the request */
492	for (sep = servtab; sep; sep = sep->se_next) {
493		if (!ISMUX(sep))
494			continue;
495		if (!strcasecmp(service, sep->se_service)) {
496			if (ISMUXPLUS(sep)) {
497				strwrite(s, "+Go\r\n");
498			}
499			return (sep);
500		}
501	}
502	strwrite(s, "-Service not available\r\n");
503	return (NULL);
504}
505
506