rlogind.c revision 96196
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#ifndef lint
42static const char copyright[] =
43"@(#) Copyright (c) 1983, 1988, 1989, 1993\n\
44	The Regents of the University of California.  All rights reserved.\n";
45#endif /* not lint */
46
47#ifndef lint
48#if 0
49static const char sccsid[] = "@(#)rlogind.c	8.1 (Berkeley) 6/4/93";
50#endif
51static const char rcsid[] =
52  "$FreeBSD: head/libexec/rlogind/rlogind.c 96196 2002-05-08 00:47:01Z des $";
53#endif /* not lint */
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			"Dalnx"
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	int ch, fromlen, on;
144
145	openlog("rlogind", LOG_PID | LOG_CONS, LOG_AUTH);
146
147	opterr = 0;
148	while ((ch = getopt(argc, argv, ARGSTR)) != -1)
149		switch (ch) {
150		case 'D':
151			no_delay = 1;
152			break;
153		case 'a':
154			check_all = 1;
155			break;
156		case 'l':
157			__check_rhosts_file = 0;
158			break;
159		case 'n':
160			keepalive = 0;
161			break;
162#ifdef CRYPT
163		case 'x':
164			doencrypt = 1;
165			break;
166#endif
167		case '?':
168		default:
169			usage();
170			break;
171		}
172	argc -= optind;
173	argv += optind;
174
175	fromlen = sizeof (from);
176	if (getpeername(0, (struct sockaddr *)&from, &fromlen) < 0) {
177		syslog(LOG_ERR,"Can't get peer name of remote host: %m");
178		fatal(STDERR_FILENO, "Can't get peer name of remote host", 1);
179	}
180	on = 1;
181	if (keepalive &&
182	    setsockopt(0, SOL_SOCKET, SO_KEEPALIVE, &on, sizeof (on)) < 0)
183		syslog(LOG_WARNING, "setsockopt (SO_KEEPALIVE): %m");
184	if (no_delay &&
185	    setsockopt(0, IPPROTO_TCP, TCP_NODELAY, &on, sizeof(on)) < 0)
186		syslog(LOG_WARNING, "setsockopt (TCP_NODELAY): %m");
187	if (from.su_family == AF_INET)
188      {
189	on = IPTOS_LOWDELAY;
190	if (setsockopt(0, IPPROTO_IP, IP_TOS, (char *)&on, sizeof(int)) < 0)
191		syslog(LOG_WARNING, "setsockopt (IP_TOS): %m");
192      }
193
194	doit(0, &from);
195	return 0;
196}
197
198int	child;
199int	netf;
200char	line[MAXPATHLEN];
201int	confirmed;
202
203struct winsize win = { 0, 0, 0, 0 };
204
205
206void
207doit(int f, union sockunion *fromp)
208{
209	int master, pid, on = 1;
210	int authenticated = 0;
211	char hostname[2 * MAXHOSTNAMELEN + 1];
212	char nameinfo[2 * INET6_ADDRSTRLEN + 1];
213	char c;
214
215	alarm(60);
216	read(f, &c, 1);
217
218	if (c != 0)
219		exit(1);
220
221	alarm(0);
222
223	realhostname_sa(hostname, sizeof(hostname) - 1,
224			    (struct sockaddr *)fromp, fromp->su_len);
225	/* error check ? */
226	fromp->su_port = ntohs((u_short)fromp->su_port);
227	hostname[sizeof(hostname) - 1] = '\0';
228
229	{
230		if ((fromp->su_family != AF_INET
231#ifdef INET6
232		  && fromp->su_family != AF_INET6
233#endif
234		     ) ||
235		    fromp->su_port >= IPPORT_RESERVED ||
236		    fromp->su_port < IPPORT_RESERVED/2) {
237			getnameinfo((struct sockaddr *)fromp,
238				    fromp->su_len,
239				    nameinfo, sizeof(nameinfo), NULL, 0,
240				    NI_NUMERICHOST|NI_WITHSCOPEID);
241			/* error check ? */
242			syslog(LOG_NOTICE, "Connection from %s on illegal port",
243			       nameinfo);
244			fatal(f, "Permission denied", 0);
245		}
246#ifdef IP_OPTIONS
247		if (fromp->su_family == AF_INET)
248	      {
249		u_char optbuf[BUFSIZ/3];
250		int optsize = sizeof(optbuf), ipproto, i;
251		struct protoent *ip;
252
253		if ((ip = getprotobyname("ip")) != NULL)
254			ipproto = ip->p_proto;
255		else
256			ipproto = IPPROTO_IP;
257		if (getsockopt(0, ipproto, IP_OPTIONS, (char *)optbuf,
258		    &optsize) == 0 && optsize != 0) {
259			for (i = 0; i < optsize; ) {
260				u_char c = optbuf[i];
261				if (c == IPOPT_LSRR || c == IPOPT_SSRR) {
262					syslog(LOG_NOTICE,
263						"Connection refused from %s with IP option %s",
264						inet_ntoa(fromp->su_sin.sin_addr),
265						c == IPOPT_LSRR ? "LSRR" : "SSRR");
266					exit(1);
267				}
268				if (c == IPOPT_EOL)
269					break;
270				i += (c == IPOPT_NOP) ? 1 : optbuf[i+1];
271			}
272		}
273	      }
274#endif
275		if (do_rlogin(fromp) == 0)
276			authenticated++;
277	}
278	if (confirmed == 0) {
279		write(f, "", 1);
280		confirmed = 1;		/* we sent the null! */
281	}
282#ifdef	CRYPT
283	if (doencrypt)
284		(void) des_enc_write(f,
285				     SECURE_MESSAGE,
286				     strlen(SECURE_MESSAGE),
287				     schedule, &kdata->session);
288#endif
289	netf = f;
290
291	pid = forkpty(&master, line, NULL, &win);
292	if (pid < 0) {
293		if (errno == ENOENT)
294			fatal(f, "Out of ptys", 0);
295		else
296			fatal(f, "Forkpty", 1);
297	}
298	if (pid == 0) {
299		if (f > 2)	/* f should always be 0, but... */
300			(void) close(f);
301		setup_term(0);
302		 if (*lusername=='-') {
303			syslog(LOG_ERR, "tried to pass user \"%s\" to login",
304			       lusername);
305			fatal(STDERR_FILENO, "invalid user", 0);
306		}
307		if (authenticated) {
308			execl(_PATH_LOGIN, "login", "-p",
309			    "-h", hostname, "-f", lusername, (char *)NULL);
310		} else
311			execl(_PATH_LOGIN, "login", "-p",
312			    "-h", hostname, lusername, (char *)NULL);
313		fatal(STDERR_FILENO, _PATH_LOGIN, 1);
314		/*NOTREACHED*/
315	}
316#ifdef	CRYPT
317	/*
318	 * If encrypted, don't turn on NBIO or the des read/write
319	 * routines will croak.
320	 */
321
322	if (!doencrypt)
323#endif
324		ioctl(f, FIONBIO, &on);
325	ioctl(master, FIONBIO, &on);
326	ioctl(master, TIOCPKT, &on);
327	signal(SIGCHLD, cleanup);
328	protocol(f, master);
329	signal(SIGCHLD, SIG_IGN);
330	cleanup(0);
331}
332
333char	magic[2] = { 0377, 0377 };
334char	oobdata[] = {TIOCPKT_WINDOW};
335
336/*
337 * Handle a "control" request (signaled by magic being present)
338 * in the data stream.  For now, we are only willing to handle
339 * window size changes.
340 */
341int
342control(int pty, char *cp, int n)
343{
344	struct winsize w;
345
346	if (n < 4+sizeof (w) || cp[2] != 's' || cp[3] != 's')
347		return (0);
348	oobdata[0] &= ~TIOCPKT_WINDOW;	/* we know he heard */
349	bcopy(cp+4, (char *)&w, sizeof(w));
350	w.ws_row = ntohs(w.ws_row);
351	w.ws_col = ntohs(w.ws_col);
352	w.ws_xpixel = ntohs(w.ws_xpixel);
353	w.ws_ypixel = ntohs(w.ws_ypixel);
354	(void)ioctl(pty, TIOCSWINSZ, &w);
355	return (4+sizeof (w));
356}
357
358/*
359 * rlogin "protocol" machine.
360 */
361void
362protocol(int f, int p)
363{
364	char pibuf[1024+1], fibuf[1024], *pbp = NULL, *fbp = NULL;
365	int pcc = 0, fcc = 0;
366	int cc, nfd, n;
367	char cntl;
368
369	/*
370	 * Must ignore SIGTTOU, otherwise we'll stop
371	 * when we try and set slave pty's window shape
372	 * (our controlling tty is the master pty).
373	 */
374	(void) signal(SIGTTOU, SIG_IGN);
375	send(f, oobdata, 1, MSG_OOB);	/* indicate new rlogin */
376	if (f > p)
377		nfd = f + 1;
378	else
379		nfd = p + 1;
380	if (nfd > FD_SETSIZE) {
381		syslog(LOG_ERR, "select mask too small, increase FD_SETSIZE");
382		fatal(f, "internal error (select mask too small)", 0);
383	}
384	for (;;) {
385		fd_set ibits, obits, ebits, *omask;
386
387		FD_ZERO(&ebits);
388		FD_ZERO(&ibits);
389		FD_ZERO(&obits);
390		omask = (fd_set *)NULL;
391		if (fcc) {
392			FD_SET(p, &obits);
393			omask = &obits;
394		} else
395			FD_SET(f, &ibits);
396		if (pcc >= 0) {
397			if (pcc) {
398				FD_SET(f, &obits);
399				omask = &obits;
400			} else
401				FD_SET(p, &ibits);
402		}
403		FD_SET(p, &ebits);
404		if ((n = select(nfd, &ibits, omask, &ebits, 0)) < 0) {
405			if (errno == EINTR)
406				continue;
407			fatal(f, "select", 1);
408		}
409		if (n == 0) {
410			/* shouldn't happen... */
411			sleep(5);
412			continue;
413		}
414#define	pkcontrol(c)	((c)&(TIOCPKT_FLUSHWRITE|TIOCPKT_NOSTOP|TIOCPKT_DOSTOP))
415		if (FD_ISSET(p, &ebits)) {
416			cc = read(p, &cntl, 1);
417			if (cc == 1 && pkcontrol(cntl)) {
418				cntl |= oobdata[0];
419				send(f, &cntl, 1, MSG_OOB);
420				if (cntl & TIOCPKT_FLUSHWRITE) {
421					pcc = 0;
422					FD_CLR(p, &ibits);
423				}
424			}
425		}
426		if (FD_ISSET(f, &ibits)) {
427#ifdef	CRYPT
428			if (doencrypt)
429				fcc = des_enc_read(f, fibuf, sizeof(fibuf),
430					schedule, &kdata->session);
431			else
432#endif
433				fcc = read(f, fibuf, sizeof(fibuf));
434			if (fcc < 0 && errno == EWOULDBLOCK)
435				fcc = 0;
436			else {
437				char *cp;
438				int left, n;
439
440				if (fcc <= 0)
441					break;
442				fbp = fibuf;
443
444			top:
445				for (cp = fibuf; cp < fibuf+fcc-1; cp++)
446					if (cp[0] == magic[0] &&
447					    cp[1] == magic[1]) {
448						left = fcc - (cp-fibuf);
449						n = control(p, cp, left);
450						if (n) {
451							left -= n;
452							if (left > 0)
453								bcopy(cp+n, cp, left);
454							fcc -= n;
455							goto top; /* n^2 */
456						}
457					}
458				FD_SET(p, &obits);		/* try write */
459			}
460		}
461
462		if (FD_ISSET(p, &obits) && fcc > 0) {
463			cc = write(p, fbp, fcc);
464			if (cc > 0) {
465				fcc -= cc;
466				fbp += cc;
467			}
468		}
469
470		if (FD_ISSET(p, &ibits)) {
471			pcc = read(p, pibuf, sizeof (pibuf));
472			pbp = pibuf;
473			if (pcc < 0 && errno == EWOULDBLOCK)
474				pcc = 0;
475			else if (pcc <= 0)
476				break;
477			else if (pibuf[0] == 0) {
478				pbp++, pcc--;
479#ifdef	CRYPT
480				if (!doencrypt)
481#endif
482					FD_SET(f, &obits);	/* try write */
483			} else {
484				if (pkcontrol(pibuf[0])) {
485					pibuf[0] |= oobdata[0];
486					send(f, &pibuf[0], 1, MSG_OOB);
487				}
488				pcc = 0;
489			}
490		}
491		if ((FD_ISSET(f, &obits)) && pcc > 0) {
492#ifdef	CRYPT
493			if (doencrypt)
494				cc = des_enc_write(f, pbp, pcc,
495					schedule, &kdata->session);
496			else
497#endif
498				cc = write(f, pbp, pcc);
499			if (cc < 0 && errno == EWOULDBLOCK) {
500				/*
501				 * This happens when we try write after read
502				 * from p, but some old kernels balk at large
503				 * writes even when select returns true.
504				 */
505				if (!FD_ISSET(p, &ibits))
506					sleep(5);
507				continue;
508			}
509			if (cc > 0) {
510				pcc -= cc;
511				pbp += cc;
512			}
513		}
514	}
515}
516
517void
518cleanup(int signo)
519{
520	char *p;
521
522	p = line + sizeof(_PATH_DEV) - 1;
523	if (logout(p))
524		logwtmp(p, "", "");
525	(void)chflags(line, 0);
526	(void)chmod(line, 0666);
527	(void)chown(line, 0, 0);
528	*p = 'p';
529	(void)chflags(line, 0);
530	(void)chmod(line, 0666);
531	(void)chown(line, 0, 0);
532	shutdown(netf, 2);
533	exit(1);
534}
535
536void
537fatal(int f, char *msg, int syserr)
538{
539	int len;
540	char buf[BUFSIZ], *bp = buf;
541
542	/*
543	 * Prepend binary one to message if we haven't sent
544	 * the magic null as confirmation.
545	 */
546	if (!confirmed)
547		*bp++ = '\01';		/* error indicator */
548	if (syserr)
549		len = snprintf(bp, sizeof(buf), "rlogind: %s: %s.\r\n",
550		    msg, strerror(errno));
551	else
552		len = snprintf(bp, sizeof(buf), "rlogind: %s.\r\n", msg);
553	if (len < 0)
554		len = 0;
555	(void) write(f, buf, bp + len - buf);
556	exit(1);
557}
558
559int
560do_rlogin(union sockunion *dest)
561{
562
563	getstr(rusername, sizeof(rusername), "remuser too long");
564	getstr(lusername, sizeof(lusername), "locuser too long");
565	getstr(term+ENVSIZE, sizeof(term)-ENVSIZE, "Terminal type too long");
566
567	pwd = getpwnam(lusername);
568	if (pwd == NULL)
569		return (-1);
570	/* XXX why don't we syslog() failure? */
571
572	return (iruserok_sa(dest, dest->su_len, pwd->pw_uid == 0, rusername,
573			    lusername));
574}
575
576void
577getstr(char *buf, int cnt, char *errmsg)
578{
579	char c;
580
581	do {
582		if (read(STDIN_FILENO, &c, 1) != 1)
583			exit(1);
584		if (--cnt < 0)
585			fatal(STDOUT_FILENO, errmsg, 0);
586		*buf++ = c;
587	} while (c != 0);
588}
589
590extern	char **environ;
591
592void
593setup_term(int fd)
594{
595	char *cp = index(term+ENVSIZE, '/');
596	char *speed;
597	struct termios tt;
598
599#ifndef notyet
600	tcgetattr(fd, &tt);
601	if (cp) {
602		*cp++ = '\0';
603		speed = cp;
604		cp = index(speed, '/');
605		if (cp)
606			*cp++ = '\0';
607		cfsetspeed(&tt, atoi(speed));
608	}
609
610	tt.c_iflag = TTYDEF_IFLAG;
611	tt.c_oflag = TTYDEF_OFLAG;
612	tt.c_lflag = TTYDEF_LFLAG;
613	tcsetattr(fd, TCSAFLUSH, &tt);
614#else
615	if (cp) {
616		*cp++ = '\0';
617		speed = cp;
618		cp = index(speed, '/');
619		if (cp)
620			*cp++ = '\0';
621		tcgetattr(fd, &tt);
622		cfsetspeed(&tt, atoi(speed));
623		tcsetattr(fd, TCSAFLUSH, &tt);
624	}
625#endif
626
627	env[0] = term;
628	env[1] = 0;
629	environ = env;
630}
631
632void
633usage(void)
634{
635	syslog(LOG_ERR, "usage: rlogind [-" ARGSTR "]");
636}
637