rlogind.c revision 146075
1/*-
2 * Copyright (c) 1983, 1988, 1989, 1993
3 *	The Regents of the University of California.  All rights reserved.
4 * Copyright (c) 2002 Networks Associates Technology, Inc.
5 * All rights reserved.
6 *
7 * Portions of this software were developed for the FreeBSD Project by
8 * ThinkSec AS and NAI Labs, the Security Research Division of Network
9 * Associates, Inc.  under DARPA/SPAWAR contract N66001-01-C-8035
10 * ("CBOSS"), as part of the DARPA CHATS research program.
11 *
12 * Redistribution and use in source and binary forms, with or without
13 * modification, are permitted provided that the following conditions
14 * are met:
15 * 1. Redistributions of source code must retain the above copyright
16 *    notice, this list of conditions and the following disclaimer.
17 * 2. Redistributions in binary form must reproduce the above copyright
18 *    notice, this list of conditions and the following disclaimer in the
19 *    documentation and/or other materials provided with the distribution.
20 * 3. All advertising materials mentioning features or use of this software
21 *    must display the following acknowledgement:
22 *	This product includes software developed by the University of
23 *	California, Berkeley and its contributors.
24 * 4. Neither the name of the University nor the names of its contributors
25 *    may be used to endorse or promote products derived from this software
26 *    without specific prior written permission.
27 *
28 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
29 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
30 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
31 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
32 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
33 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
34 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
35 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
36 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
37 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
38 * SUCH DAMAGE.
39 */
40
41#if 0
42#ifndef lint
43static const char copyright[] =
44"@(#) Copyright (c) 1983, 1988, 1989, 1993\n\
45	The Regents of the University of California.  All rights reserved.\n";
46#endif /* not lint */
47
48#ifndef lint
49static const char sccsid[] = "@(#)rlogind.c	8.1 (Berkeley) 6/4/93";
50#endif /* not lint */
51#endif
52#include <sys/cdefs.h>
53__FBSDID("$FreeBSD: head/libexec/rlogind/rlogind.c 146075 2005-05-11 02:43:04Z jmallett $");
54
55/*
56 * remote login server:
57 *	\0
58 *	remuser\0
59 *	locuser\0
60 *	terminal_type/speed\0
61 *	data
62 */
63
64#define	FD_SETSIZE	16		/* don't need many bits for select */
65#include <sys/types.h>
66#include <sys/param.h>
67#include <sys/stat.h>
68#include <sys/ioctl.h>
69#include <signal.h>
70#include <termios.h>
71
72#include <sys/socket.h>
73#include <netinet/in.h>
74#include <netinet/in_systm.h>
75#include <netinet/ip.h>
76#include <netinet/tcp.h>
77#include <arpa/inet.h>
78#include <netdb.h>
79
80#include <errno.h>
81#include <libutil.h>
82#include <paths.h>
83#include <pwd.h>
84#include <syslog.h>
85#include <stdio.h>
86#include <stdlib.h>
87#include <string.h>
88#include <unistd.h>
89
90
91#ifndef TIOCPKT_WINDOW
92#define TIOCPKT_WINDOW 0x80
93#endif
94
95#define		ARGSTR			"Daln"
96
97/* wrapper for KAME-special getnameinfo() */
98#ifndef NI_WITHSCOPEID
99#define	NI_WITHSCOPEID	0
100#endif
101
102char	*env[2];
103#define	NMAX 30
104char	lusername[NMAX+1], rusername[NMAX+1];
105static	char term[64] = "TERM=";
106#define	ENVSIZE	(sizeof("TERM=")-1)	/* skip null for concatenation */
107int	keepalive = 1;
108int	check_all = 0;
109int	no_delay;
110
111struct	passwd *pwd;
112
113union sockunion {
114	struct sockinet {
115		u_char si_len;
116		u_char si_family;
117		u_short si_port;
118	} su_si;
119	struct sockaddr_in  su_sin;
120	struct sockaddr_in6 su_sin6;
121};
122#define su_len		su_si.si_len
123#define su_family	su_si.si_family
124#define su_port		su_si.si_port
125
126void	doit(int, union sockunion *);
127int	control(int, char *, int);
128void	protocol(int, int);
129void	cleanup(int);
130void	fatal(int, char *, int);
131int	do_rlogin(union sockunion *);
132void	getstr(char *, int, char *);
133void	setup_term(int);
134int	do_krb_login(struct sockaddr_in *);
135void	usage(void);
136
137
138int
139main(int argc, char *argv[])
140{
141	extern int __check_rhosts_file;
142	union sockunion from;
143	socklen_t fromlen;
144	int ch, on;
145
146	openlog("rlogind", LOG_PID | LOG_CONS, LOG_AUTH);
147
148	opterr = 0;
149	while ((ch = getopt(argc, argv, ARGSTR)) != -1)
150		switch (ch) {
151		case 'D':
152			no_delay = 1;
153			break;
154		case 'a':
155			check_all = 1;
156			break;
157		case 'l':
158			__check_rhosts_file = 0;
159			break;
160		case 'n':
161			keepalive = 0;
162			break;
163		case '?':
164		default:
165			usage();
166			break;
167		}
168	argc -= optind;
169	argv += optind;
170
171	fromlen = sizeof (from);
172	if (getpeername(0, (struct sockaddr *)&from, &fromlen) < 0) {
173		syslog(LOG_ERR,"Can't get peer name of remote host: %m");
174		fatal(STDERR_FILENO, "Can't get peer name of remote host", 1);
175	}
176	on = 1;
177	if (keepalive &&
178	    setsockopt(0, SOL_SOCKET, SO_KEEPALIVE, &on, sizeof (on)) < 0)
179		syslog(LOG_WARNING, "setsockopt (SO_KEEPALIVE): %m");
180	if (no_delay &&
181	    setsockopt(0, IPPROTO_TCP, TCP_NODELAY, &on, sizeof(on)) < 0)
182		syslog(LOG_WARNING, "setsockopt (TCP_NODELAY): %m");
183	if (from.su_family == AF_INET)
184      {
185	on = IPTOS_LOWDELAY;
186	if (setsockopt(0, IPPROTO_IP, IP_TOS, (char *)&on, sizeof(int)) < 0)
187		syslog(LOG_WARNING, "setsockopt (IP_TOS): %m");
188      }
189
190	doit(0, &from);
191	return 0;
192}
193
194int	child;
195int	netf;
196char	line[MAXPATHLEN];
197int	confirmed;
198
199struct winsize win = { 0, 0, 0, 0 };
200
201
202void
203doit(int f, union sockunion *fromp)
204{
205	int master, pid, on = 1;
206	int authenticated = 0;
207	char hostname[2 * MAXHOSTNAMELEN + 1];
208	char nameinfo[2 * INET6_ADDRSTRLEN + 1];
209	char c;
210
211	alarm(60);
212	read(f, &c, 1);
213
214	if (c != 0)
215		exit(1);
216
217	alarm(0);
218
219	realhostname_sa(hostname, sizeof(hostname) - 1,
220			    (struct sockaddr *)fromp, fromp->su_len);
221	/* error check ? */
222	fromp->su_port = ntohs((u_short)fromp->su_port);
223	hostname[sizeof(hostname) - 1] = '\0';
224
225	{
226		if ((fromp->su_family != AF_INET
227#ifdef INET6
228		  && fromp->su_family != AF_INET6
229#endif
230		     ) ||
231		    fromp->su_port >= IPPORT_RESERVED ||
232		    fromp->su_port < IPPORT_RESERVED/2) {
233			getnameinfo((struct sockaddr *)fromp,
234				    fromp->su_len,
235				    nameinfo, sizeof(nameinfo), NULL, 0,
236				    NI_NUMERICHOST|NI_WITHSCOPEID);
237			/* error check ? */
238			syslog(LOG_NOTICE, "Connection from %s on illegal port",
239			       nameinfo);
240			fatal(f, "Permission denied", 0);
241		}
242#ifdef IP_OPTIONS
243		if (fromp->su_family == AF_INET)
244	      {
245		u_char optbuf[BUFSIZ/3];
246		socklen_t optsize = sizeof(optbuf);
247		int ipproto, i;
248		struct protoent *ip;
249
250		if ((ip = getprotobyname("ip")) != NULL)
251			ipproto = ip->p_proto;
252		else
253			ipproto = IPPROTO_IP;
254		if (getsockopt(0, ipproto, IP_OPTIONS, (char *)optbuf,
255		    &optsize) == 0 && optsize != 0) {
256			for (i = 0; i < optsize; ) {
257				u_char c = optbuf[i];
258				if (c == IPOPT_LSRR || c == IPOPT_SSRR) {
259					syslog(LOG_NOTICE,
260						"Connection refused from %s with IP option %s",
261						inet_ntoa(fromp->su_sin.sin_addr),
262						c == IPOPT_LSRR ? "LSRR" : "SSRR");
263					exit(1);
264				}
265				if (c == IPOPT_EOL)
266					break;
267				i += (c == IPOPT_NOP) ? 1 : optbuf[i+1];
268			}
269		}
270	      }
271#endif
272		if (do_rlogin(fromp) == 0)
273			authenticated++;
274	}
275	if (confirmed == 0) {
276		write(f, "", 1);
277		confirmed = 1;		/* we sent the null! */
278	}
279	netf = f;
280
281	pid = forkpty(&master, line, NULL, &win);
282	if (pid < 0) {
283		if (errno == ENOENT)
284			fatal(f, "Out of ptys", 0);
285		else
286			fatal(f, "Forkpty", 1);
287	}
288	if (pid == 0) {
289		if (f > 2)	/* f should always be 0, but... */
290			(void) close(f);
291		setup_term(0);
292		 if (*lusername=='-') {
293			syslog(LOG_ERR, "tried to pass user \"%s\" to login",
294			       lusername);
295			fatal(STDERR_FILENO, "invalid user", 0);
296		}
297		if (authenticated) {
298			execl(_PATH_LOGIN, "login", "-p",
299			    "-h", hostname, "-f", lusername, (char *)NULL);
300		} else
301			execl(_PATH_LOGIN, "login", "-p",
302			    "-h", hostname, lusername, (char *)NULL);
303		fatal(STDERR_FILENO, _PATH_LOGIN, 1);
304		/*NOTREACHED*/
305	}
306	ioctl(f, FIONBIO, &on);
307	ioctl(master, FIONBIO, &on);
308	ioctl(master, TIOCPKT, &on);
309	signal(SIGCHLD, cleanup);
310	protocol(f, master);
311	signal(SIGCHLD, SIG_IGN);
312	cleanup(0);
313}
314
315char	magic[2] = { 0377, 0377 };
316char	oobdata[] = {TIOCPKT_WINDOW};
317
318/*
319 * Handle a "control" request (signaled by magic being present)
320 * in the data stream.  For now, we are only willing to handle
321 * window size changes.
322 */
323int
324control(int pty, char *cp, int n)
325{
326	struct winsize w;
327
328	if (n < 4 + (int)sizeof(w) || cp[2] != 's' || cp[3] != 's')
329		return (0);
330	oobdata[0] &= ~TIOCPKT_WINDOW;	/* we know he heard */
331	bcopy(cp+4, (char *)&w, sizeof(w));
332	w.ws_row = ntohs(w.ws_row);
333	w.ws_col = ntohs(w.ws_col);
334	w.ws_xpixel = ntohs(w.ws_xpixel);
335	w.ws_ypixel = ntohs(w.ws_ypixel);
336	(void)ioctl(pty, TIOCSWINSZ, &w);
337	return (4+sizeof (w));
338}
339
340/*
341 * rlogin "protocol" machine.
342 */
343void
344protocol(int f, int p)
345{
346	char pibuf[1024+1], fibuf[1024], *pbp = NULL, *fbp = NULL;
347	int pcc = 0, fcc = 0;
348	int cc, nfd, n;
349	char cntl;
350
351	/*
352	 * Must ignore SIGTTOU, otherwise we'll stop
353	 * when we try and set slave pty's window shape
354	 * (our controlling tty is the master pty).
355	 */
356	(void) signal(SIGTTOU, SIG_IGN);
357	send(f, oobdata, 1, MSG_OOB);	/* indicate new rlogin */
358	if (f > p)
359		nfd = f + 1;
360	else
361		nfd = p + 1;
362	if (nfd > FD_SETSIZE) {
363		syslog(LOG_ERR, "select mask too small, increase FD_SETSIZE");
364		fatal(f, "internal error (select mask too small)", 0);
365	}
366	for (;;) {
367		fd_set ibits, obits, ebits, *omask;
368
369		FD_ZERO(&ebits);
370		FD_ZERO(&ibits);
371		FD_ZERO(&obits);
372		omask = (fd_set *)NULL;
373		if (fcc) {
374			FD_SET(p, &obits);
375			omask = &obits;
376		} else
377			FD_SET(f, &ibits);
378		if (pcc >= 0) {
379			if (pcc) {
380				FD_SET(f, &obits);
381				omask = &obits;
382			} else
383				FD_SET(p, &ibits);
384		}
385		FD_SET(p, &ebits);
386		if ((n = select(nfd, &ibits, omask, &ebits, 0)) < 0) {
387			if (errno == EINTR)
388				continue;
389			fatal(f, "select", 1);
390		}
391		if (n == 0) {
392			/* shouldn't happen... */
393			sleep(5);
394			continue;
395		}
396#define	pkcontrol(c)	((c)&(TIOCPKT_FLUSHWRITE|TIOCPKT_NOSTOP|TIOCPKT_DOSTOP))
397		if (FD_ISSET(p, &ebits)) {
398			cc = read(p, &cntl, 1);
399			if (cc == 1 && pkcontrol(cntl)) {
400				cntl |= oobdata[0];
401				send(f, &cntl, 1, MSG_OOB);
402				if (cntl & TIOCPKT_FLUSHWRITE) {
403					pcc = 0;
404					FD_CLR(p, &ibits);
405				}
406			}
407		}
408		if (FD_ISSET(f, &ibits)) {
409			fcc = read(f, fibuf, sizeof(fibuf));
410			if (fcc < 0 && errno == EWOULDBLOCK)
411				fcc = 0;
412			else {
413				char *cp;
414				int left, n;
415
416				if (fcc <= 0)
417					break;
418				fbp = fibuf;
419
420			top:
421				for (cp = fibuf; cp < fibuf+fcc-1; cp++)
422					if (cp[0] == magic[0] &&
423					    cp[1] == magic[1]) {
424						left = fcc - (cp-fibuf);
425						n = control(p, cp, left);
426						if (n) {
427							left -= n;
428							if (left > 0)
429								bcopy(cp+n, cp, left);
430							fcc -= n;
431							goto top; /* n^2 */
432						}
433					}
434				FD_SET(p, &obits);		/* try write */
435			}
436		}
437
438		if (FD_ISSET(p, &obits) && fcc > 0) {
439			cc = write(p, fbp, fcc);
440			if (cc > 0) {
441				fcc -= cc;
442				fbp += cc;
443			}
444		}
445
446		if (FD_ISSET(p, &ibits)) {
447			pcc = read(p, pibuf, sizeof (pibuf));
448			pbp = pibuf;
449			if (pcc < 0 && errno == EWOULDBLOCK)
450				pcc = 0;
451			else if (pcc <= 0)
452				break;
453			else if (pibuf[0] == 0) {
454				pbp++, pcc--;
455				FD_SET(f, &obits);	/* try write */
456			} else {
457				if (pkcontrol(pibuf[0])) {
458					pibuf[0] |= oobdata[0];
459					send(f, &pibuf[0], 1, MSG_OOB);
460				}
461				pcc = 0;
462			}
463		}
464		if ((FD_ISSET(f, &obits)) && pcc > 0) {
465			cc = write(f, pbp, pcc);
466			if (cc < 0 && errno == EWOULDBLOCK) {
467				/*
468				 * This happens when we try write after read
469				 * from p, but some old kernels balk at large
470				 * writes even when select returns true.
471				 */
472				if (!FD_ISSET(p, &ibits))
473					sleep(5);
474				continue;
475			}
476			if (cc > 0) {
477				pcc -= cc;
478				pbp += cc;
479			}
480		}
481	}
482}
483
484void
485cleanup(int signo)
486{
487	char *p;
488
489	p = line + sizeof(_PATH_DEV) - 1;
490	if (logout(p))
491		logwtmp(p, "", "");
492	(void)chflags(line, 0);
493	(void)chmod(line, 0666);
494	(void)chown(line, 0, 0);
495	*p = 'p';
496	(void)chflags(line, 0);
497	(void)chmod(line, 0666);
498	(void)chown(line, 0, 0);
499	shutdown(netf, SHUT_RDWR);
500	exit(1);
501}
502
503void
504fatal(int f, char *msg, int syserr)
505{
506	int len;
507	char buf[BUFSIZ], *bp = buf;
508
509	/*
510	 * Prepend binary one to message if we haven't sent
511	 * the magic null as confirmation.
512	 */
513	if (!confirmed)
514		*bp++ = '\01';		/* error indicator */
515	if (syserr)
516		len = snprintf(bp, sizeof(buf), "rlogind: %s: %s.\r\n",
517		    msg, strerror(errno));
518	else
519		len = snprintf(bp, sizeof(buf), "rlogind: %s.\r\n", msg);
520	if (len < 0)
521		len = 0;
522	(void) write(f, buf, bp + len - buf);
523	exit(1);
524}
525
526int
527do_rlogin(union sockunion *dest)
528{
529
530	getstr(rusername, sizeof(rusername), "remuser too long");
531	getstr(lusername, sizeof(lusername), "locuser too long");
532	getstr(term+ENVSIZE, sizeof(term)-ENVSIZE, "Terminal type too long");
533
534	pwd = getpwnam(lusername);
535	if (pwd == NULL)
536		return (-1);
537	/* XXX why don't we syslog() failure? */
538
539	return (iruserok_sa(dest, dest->su_len, pwd->pw_uid == 0, rusername,
540			    lusername));
541}
542
543void
544getstr(char *buf, int cnt, char *errmsg)
545{
546	char c;
547
548	do {
549		if (read(STDIN_FILENO, &c, 1) != 1)
550			exit(1);
551		if (--cnt < 0)
552			fatal(STDOUT_FILENO, errmsg, 0);
553		*buf++ = c;
554	} while (c != 0);
555}
556
557extern	char **environ;
558
559void
560setup_term(int fd)
561{
562	char *cp = index(term+ENVSIZE, '/');
563	char *speed;
564	struct termios tt;
565
566#ifndef notyet
567	tcgetattr(fd, &tt);
568	if (cp) {
569		*cp++ = '\0';
570		speed = cp;
571		cp = index(speed, '/');
572		if (cp)
573			*cp++ = '\0';
574		cfsetspeed(&tt, atoi(speed));
575	}
576
577	tt.c_iflag = TTYDEF_IFLAG;
578	tt.c_oflag = TTYDEF_OFLAG;
579	tt.c_lflag = TTYDEF_LFLAG;
580	tcsetattr(fd, TCSAFLUSH, &tt);
581#else
582	if (cp) {
583		*cp++ = '\0';
584		speed = cp;
585		cp = index(speed, '/');
586		if (cp)
587			*cp++ = '\0';
588		tcgetattr(fd, &tt);
589		cfsetspeed(&tt, atoi(speed));
590		tcsetattr(fd, TCSAFLUSH, &tt);
591	}
592#endif
593
594	env[0] = term;
595	env[1] = 0;
596	environ = env;
597}
598
599void
600usage(void)
601{
602	syslog(LOG_ERR, "usage: rlogind [-" ARGSTR "]");
603}
604