rlogind.c revision 22455
11592Srgrimes/*-
21592Srgrimes * Copyright (c) 1983, 1988, 1989, 1993
31592Srgrimes *	The Regents of the University of California.  All rights reserved.
41592Srgrimes *
51592Srgrimes * Redistribution and use in source and binary forms, with or without
61592Srgrimes * modification, are permitted provided that the following conditions
71592Srgrimes * are met:
81592Srgrimes * 1. Redistributions of source code must retain the above copyright
91592Srgrimes *    notice, this list of conditions and the following disclaimer.
101592Srgrimes * 2. Redistributions in binary form must reproduce the above copyright
111592Srgrimes *    notice, this list of conditions and the following disclaimer in the
121592Srgrimes *    documentation and/or other materials provided with the distribution.
131592Srgrimes * 3. All advertising materials mentioning features or use of this software
141592Srgrimes *    must display the following acknowledgement:
151592Srgrimes *	This product includes software developed by the University of
161592Srgrimes *	California, Berkeley and its contributors.
171592Srgrimes * 4. Neither the name of the University nor the names of its contributors
181592Srgrimes *    may be used to endorse or promote products derived from this software
191592Srgrimes *    without specific prior written permission.
201592Srgrimes *
211592Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
221592Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
231592Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
241592Srgrimes * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
251592Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
261592Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
271592Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
281592Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
291592Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
301592Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
311592Srgrimes * SUCH DAMAGE.
3218471Swosch *
3321673Sjkh *	$FreeBSD: head/libexec/rlogind/rlogind.c 22455 1997-02-09 04:18:43Z imp $
341592Srgrimes */
351592Srgrimes
361592Srgrimes#ifndef lint
371592Srgrimesstatic char copyright[] =
381592Srgrimes"@(#) Copyright (c) 1983, 1988, 1989, 1993\n\
391592Srgrimes	The Regents of the University of California.  All rights reserved.\n";
401592Srgrimes#endif /* not lint */
411592Srgrimes
421592Srgrimes#ifndef lint
431592Srgrimesstatic char sccsid[] = "@(#)rlogind.c	8.1 (Berkeley) 6/4/93";
441592Srgrimes#endif /* not lint */
451592Srgrimes
461592Srgrimes/*
471592Srgrimes * remote login server:
481592Srgrimes *	\0
491592Srgrimes *	remuser\0
501592Srgrimes *	locuser\0
511592Srgrimes *	terminal_type/speed\0
521592Srgrimes *	data
531592Srgrimes */
541592Srgrimes
551592Srgrimes#define	FD_SETSIZE	16		/* don't need many bits for select */
561592Srgrimes#include <sys/param.h>
571592Srgrimes#include <sys/stat.h>
581592Srgrimes#include <sys/ioctl.h>
591592Srgrimes#include <signal.h>
601592Srgrimes#include <termios.h>
611592Srgrimes
621592Srgrimes#include <sys/socket.h>
631592Srgrimes#include <netinet/in.h>
641592Srgrimes#include <netinet/in_systm.h>
651592Srgrimes#include <netinet/ip.h>
6611486Sdg#include <netinet/tcp.h>
671592Srgrimes#include <arpa/inet.h>
681592Srgrimes#include <netdb.h>
691592Srgrimes
701592Srgrimes#include <pwd.h>
711592Srgrimes#include <syslog.h>
721592Srgrimes#include <errno.h>
731592Srgrimes#include <stdio.h>
741592Srgrimes#include <unistd.h>
751592Srgrimes#include <stdlib.h>
761592Srgrimes#include <string.h>
771592Srgrimes#include "pathnames.h"
781592Srgrimes
791592Srgrimes#ifndef TIOCPKT_WINDOW
801592Srgrimes#define TIOCPKT_WINDOW 0x80
811592Srgrimes#endif
821592Srgrimes
831592Srgrimes#ifdef	KERBEROS
8414024Smarkm#include <des.h>
851592Srgrimes#include <kerberosIV/krb.h>
861592Srgrimes#define	SECURE_MESSAGE "This rlogin session is using DES encryption for all transmissions.\r\n"
871592Srgrimes
881592SrgrimesAUTH_DAT	*kdata;
891592SrgrimesKTEXT		ticket;
901592Srgrimesu_char		auth_buf[sizeof(AUTH_DAT)];
911592Srgrimesu_char		tick_buf[sizeof(KTEXT_ST)];
921592SrgrimesKey_schedule	schedule;
931592Srgrimesint		doencrypt, retval, use_kerberos, vacuous;
941592Srgrimes
9511486Sdg#define		ARGSTR			"Dalnkvx"
961592Srgrimes#else
9711486Sdg#define		ARGSTR			"Daln"
981592Srgrimes#endif	/* KERBEROS */
991592Srgrimes
1001592Srgrimeschar	*env[2];
1011592Srgrimes#define	NMAX 30
1021592Srgrimeschar	lusername[NMAX+1], rusername[NMAX+1];
1031592Srgrimesstatic	char term[64] = "TERM=";
1041592Srgrimes#define	ENVSIZE	(sizeof("TERM=")-1)	/* skip null for concatenation */
1051592Srgrimesint	keepalive = 1;
1061592Srgrimesint	check_all = 0;
10711486Sdgint	no_delay;
1081592Srgrimes
1091592Srgrimesstruct	passwd *pwd;
1101592Srgrimes
1111592Srgrimesvoid	doit __P((int, struct sockaddr_in *));
1121592Srgrimesint	control __P((int, char *, int));
1131592Srgrimesvoid	protocol __P((int, int));
1141592Srgrimesvoid	cleanup __P((int));
1151592Srgrimesvoid	fatal __P((int, char *, int));
1161592Srgrimesint	do_rlogin __P((struct sockaddr_in *));
1171592Srgrimesvoid	getstr __P((char *, int, char *));
1181592Srgrimesvoid	setup_term __P((int));
1191592Srgrimesint	do_krb_login __P((struct sockaddr_in *));
1201592Srgrimesvoid	usage __P((void));
1211592Srgrimesint	local_domain __P((char *));
1221592Srgrimeschar	*topdomain __P((char *));
1231592Srgrimes
1241592Srgrimesint
1251592Srgrimesmain(argc, argv)
1261592Srgrimes	int argc;
1271592Srgrimes	char *argv[];
1281592Srgrimes{
1291592Srgrimes	extern int __check_rhosts_file;
1301592Srgrimes	struct sockaddr_in from;
1311592Srgrimes	int ch, fromlen, on;
1321592Srgrimes
1331592Srgrimes	openlog("rlogind", LOG_PID | LOG_CONS, LOG_AUTH);
1341592Srgrimes
1351592Srgrimes	opterr = 0;
1361592Srgrimes	while ((ch = getopt(argc, argv, ARGSTR)) != EOF)
1371592Srgrimes		switch (ch) {
13811486Sdg		case 'D':
13911486Sdg			no_delay = 1;
14011486Sdg			break;
1411592Srgrimes		case 'a':
1421592Srgrimes			check_all = 1;
1431592Srgrimes			break;
1441592Srgrimes		case 'l':
1451592Srgrimes			__check_rhosts_file = 0;
1461592Srgrimes			break;
1471592Srgrimes		case 'n':
1481592Srgrimes			keepalive = 0;
1491592Srgrimes			break;
1501592Srgrimes#ifdef KERBEROS
1511592Srgrimes		case 'k':
1521592Srgrimes			use_kerberos = 1;
1531592Srgrimes			break;
1541592Srgrimes		case 'v':
1551592Srgrimes			vacuous = 1;
1561592Srgrimes			break;
1571592Srgrimes#ifdef CRYPT
1581592Srgrimes		case 'x':
1591592Srgrimes			doencrypt = 1;
1601592Srgrimes			break;
1611592Srgrimes#endif
1621592Srgrimes#endif
1631592Srgrimes		case '?':
1641592Srgrimes		default:
1651592Srgrimes			usage();
1661592Srgrimes			break;
1671592Srgrimes		}
1681592Srgrimes	argc -= optind;
1691592Srgrimes	argv += optind;
1701592Srgrimes
1711592Srgrimes#ifdef	KERBEROS
1721592Srgrimes	if (use_kerberos && vacuous) {
1731592Srgrimes		usage();
1741592Srgrimes		fatal(STDERR_FILENO, "only one of -k and -v allowed", 0);
1751592Srgrimes	}
1761592Srgrimes#endif
1771592Srgrimes	fromlen = sizeof (from);
1781592Srgrimes	if (getpeername(0, (struct sockaddr *)&from, &fromlen) < 0) {
1791592Srgrimes		syslog(LOG_ERR,"Can't get peer name of remote host: %m");
1801592Srgrimes		fatal(STDERR_FILENO, "Can't get peer name of remote host", 1);
1811592Srgrimes	}
1821592Srgrimes	on = 1;
1831592Srgrimes	if (keepalive &&
1841592Srgrimes	    setsockopt(0, SOL_SOCKET, SO_KEEPALIVE, &on, sizeof (on)) < 0)
1851592Srgrimes		syslog(LOG_WARNING, "setsockopt (SO_KEEPALIVE): %m");
18611486Sdg	if (no_delay &&
18711486Sdg	    setsockopt(0, IPPROTO_TCP, TCP_NODELAY, &on, sizeof(on)) < 0)
18811486Sdg		syslog(LOG_WARNING, "setsockopt (TCP_NODELAY): %m");
1891592Srgrimes	on = IPTOS_LOWDELAY;
1901592Srgrimes	if (setsockopt(0, IPPROTO_IP, IP_TOS, (char *)&on, sizeof(int)) < 0)
1911592Srgrimes		syslog(LOG_WARNING, "setsockopt (IP_TOS): %m");
19211486Sdg
1931592Srgrimes	doit(0, &from);
1941592Srgrimes}
1951592Srgrimes
1961592Srgrimesint	child;
1971592Srgrimesint	netf;
1981592Srgrimeschar	line[MAXPATHLEN];
1991592Srgrimesint	confirmed;
2001592Srgrimes
2011592Srgrimesstruct winsize win = { 0, 0, 0, 0 };
2021592Srgrimes
2031592Srgrimes
2041592Srgrimesvoid
2051592Srgrimesdoit(f, fromp)
2061592Srgrimes	int f;
2071592Srgrimes	struct sockaddr_in *fromp;
2081592Srgrimes{
2091592Srgrimes	int master, pid, on = 1;
2101592Srgrimes	int authenticated = 0;
2111592Srgrimes	register struct hostent *hp;
2121592Srgrimes	char hostname[2 * MAXHOSTNAMELEN + 1];
2131592Srgrimes	char c;
2141592Srgrimes
2151592Srgrimes	alarm(60);
2161592Srgrimes	read(f, &c, 1);
2171592Srgrimes
2181592Srgrimes	if (c != 0)
2191592Srgrimes		exit(1);
2201592Srgrimes#ifdef	KERBEROS
2211592Srgrimes	if (vacuous)
2221592Srgrimes		fatal(f, "Remote host requires Kerberos authentication", 0);
2231592Srgrimes#endif
2241592Srgrimes
2251592Srgrimes	alarm(0);
2261592Srgrimes	fromp->sin_port = ntohs((u_short)fromp->sin_port);
2271592Srgrimes	hp = gethostbyaddr((char *)&fromp->sin_addr, sizeof(struct in_addr),
2281592Srgrimes	    fromp->sin_family);
2291592Srgrimes	if (hp)
2301592Srgrimes		(void)strcpy(hostname, hp->h_name);
2311592Srgrimes	else
2321592Srgrimes		(void)strcpy(hostname, inet_ntoa(fromp->sin_addr));
2331592Srgrimes
2341592Srgrimes#ifdef	KERBEROS
2351592Srgrimes	if (use_kerberos) {
2361592Srgrimes		retval = do_krb_login(fromp);
2371592Srgrimes		if (retval == 0)
2381592Srgrimes			authenticated++;
2391592Srgrimes		else if (retval > 0)
2401592Srgrimes			fatal(f, krb_err_txt[retval], 0);
2411592Srgrimes		write(f, &c, 1);
2421592Srgrimes		confirmed = 1;		/* we sent the null! */
2431592Srgrimes	} else
2441592Srgrimes#endif
2451592Srgrimes	{
2461592Srgrimes		if (fromp->sin_family != AF_INET ||
2471592Srgrimes		    fromp->sin_port >= IPPORT_RESERVED ||
2481592Srgrimes		    fromp->sin_port < IPPORT_RESERVED/2) {
2491592Srgrimes			syslog(LOG_NOTICE, "Connection from %s on illegal port",
2501592Srgrimes				inet_ntoa(fromp->sin_addr));
2511592Srgrimes			fatal(f, "Permission denied", 0);
2521592Srgrimes		}
2531592Srgrimes#ifdef IP_OPTIONS
2541592Srgrimes		{
25522455Simp		u_char optbuf[BUFSIZ/3];
25622455Simp		int optsize = sizeof(optbuf), ipproto, i;
2571592Srgrimes		struct protoent *ip;
2581592Srgrimes
2591592Srgrimes		if ((ip = getprotobyname("ip")) != NULL)
2601592Srgrimes			ipproto = ip->p_proto;
2611592Srgrimes		else
2621592Srgrimes			ipproto = IPPROTO_IP;
2631592Srgrimes		if (getsockopt(0, ipproto, IP_OPTIONS, (char *)optbuf,
2641592Srgrimes		    &optsize) == 0 && optsize != 0) {
26522455Simp			for (i = 0; i < optsize; ) {
26622455Simp				u_char c = optbuf[i];
26722455Simp				if (c == IPOPT_LSRR || c == IPOPT_SSRR) {
26822455Simp					syslog(LOG_NOTICE,
26922455Simp						"Connection refused from %s with IP option %s",
27022455Simp						inet_ntoa(fromp->sin_addr),
27122455Simp						c == IPOPT_LSRR ? "LSRR" : "SSRR");
27222455Simp					exit(1);
27322455Simp				}
27422455Simp				if (c == IPOPT_EOL)
27522455Simp					break;
27622455Simp				i += (c == IPOPT_NOP) ? 1 : optbuf[i+1];
2771592Srgrimes			}
2781592Srgrimes		}
2791592Srgrimes		}
2801592Srgrimes#endif
2811592Srgrimes		if (do_rlogin(fromp) == 0)
2821592Srgrimes			authenticated++;
2831592Srgrimes	}
2841592Srgrimes	if (confirmed == 0) {
2851592Srgrimes		write(f, "", 1);
2861592Srgrimes		confirmed = 1;		/* we sent the null! */
2871592Srgrimes	}
2881592Srgrimes#ifdef	KERBEROS
2891592Srgrimes#ifdef	CRYPT
2901592Srgrimes	if (doencrypt)
2911592Srgrimes		(void) des_write(f, SECURE_MESSAGE, sizeof(SECURE_MESSAGE) - 1);
2921592Srgrimes#endif
2931592Srgrimes#endif
2941592Srgrimes	netf = f;
2951592Srgrimes
2961592Srgrimes	pid = forkpty(&master, line, NULL, &win);
2971592Srgrimes	if (pid < 0) {
2981592Srgrimes		if (errno == ENOENT)
2991592Srgrimes			fatal(f, "Out of ptys", 0);
3001592Srgrimes		else
3011592Srgrimes			fatal(f, "Forkpty", 1);
3021592Srgrimes	}
3031592Srgrimes	if (pid == 0) {
3041592Srgrimes		if (f > 2)	/* f should always be 0, but... */
3051592Srgrimes			(void) close(f);
3061592Srgrimes		setup_term(0);
30712575Snate		 if (*lusername=='-') {
3082076Sguido			syslog(LOG_ERR, "tried to pass user \"%s\" to login",
3092076Sguido			       lusername);
3102076Sguido			fatal(STDERR_FILENO, "invalid user", 0);
3112076Sguido		}
3121592Srgrimes		if (authenticated) {
3131592Srgrimes#ifdef	KERBEROS
3141592Srgrimes			if (use_kerberos && (pwd->pw_uid == 0))
3151592Srgrimes				syslog(LOG_INFO|LOG_AUTH,
3161592Srgrimes				    "ROOT Kerberos login from %s.%s@%s on %s\n",
3171592Srgrimes				    kdata->pname, kdata->pinst, kdata->prealm,
3181592Srgrimes				    hostname);
3191592Srgrimes#endif
3201592Srgrimes
3211592Srgrimes			execl(_PATH_LOGIN, "login", "-p",
3221592Srgrimes			    "-h", hostname, "-f", lusername, (char *)NULL);
3231592Srgrimes		} else
3241592Srgrimes			execl(_PATH_LOGIN, "login", "-p",
3251592Srgrimes			    "-h", hostname, lusername, (char *)NULL);
3261592Srgrimes		fatal(STDERR_FILENO, _PATH_LOGIN, 1);
3271592Srgrimes		/*NOTREACHED*/
3281592Srgrimes	}
3291592Srgrimes#ifdef	CRYPT
3301592Srgrimes#ifdef	KERBEROS
3311592Srgrimes	/*
3321592Srgrimes	 * If encrypted, don't turn on NBIO or the des read/write
3331592Srgrimes	 * routines will croak.
3341592Srgrimes	 */
3351592Srgrimes
3361592Srgrimes	if (!doencrypt)
3371592Srgrimes#endif
3381592Srgrimes#endif
3391592Srgrimes		ioctl(f, FIONBIO, &on);
3401592Srgrimes	ioctl(master, FIONBIO, &on);
3411592Srgrimes	ioctl(master, TIOCPKT, &on);
3421592Srgrimes	signal(SIGCHLD, cleanup);
3431592Srgrimes	protocol(f, master);
3441592Srgrimes	signal(SIGCHLD, SIG_IGN);
3451592Srgrimes	cleanup(0);
3461592Srgrimes}
3471592Srgrimes
3481592Srgrimeschar	magic[2] = { 0377, 0377 };
3491592Srgrimeschar	oobdata[] = {TIOCPKT_WINDOW};
3501592Srgrimes
3511592Srgrimes/*
3521592Srgrimes * Handle a "control" request (signaled by magic being present)
3531592Srgrimes * in the data stream.  For now, we are only willing to handle
3541592Srgrimes * window size changes.
3551592Srgrimes */
3561592Srgrimesint
3571592Srgrimescontrol(pty, cp, n)
3581592Srgrimes	int pty;
3591592Srgrimes	char *cp;
3601592Srgrimes	int n;
3611592Srgrimes{
3621592Srgrimes	struct winsize w;
3631592Srgrimes
3641592Srgrimes	if (n < 4+sizeof (w) || cp[2] != 's' || cp[3] != 's')
3651592Srgrimes		return (0);
3661592Srgrimes	oobdata[0] &= ~TIOCPKT_WINDOW;	/* we know he heard */
3671592Srgrimes	bcopy(cp+4, (char *)&w, sizeof(w));
3681592Srgrimes	w.ws_row = ntohs(w.ws_row);
3691592Srgrimes	w.ws_col = ntohs(w.ws_col);
3701592Srgrimes	w.ws_xpixel = ntohs(w.ws_xpixel);
3711592Srgrimes	w.ws_ypixel = ntohs(w.ws_ypixel);
3721592Srgrimes	(void)ioctl(pty, TIOCSWINSZ, &w);
3731592Srgrimes	return (4+sizeof (w));
3741592Srgrimes}
3751592Srgrimes
3761592Srgrimes/*
3771592Srgrimes * rlogin "protocol" machine.
3781592Srgrimes */
3791592Srgrimesvoid
3801592Srgrimesprotocol(f, p)
3811592Srgrimes	register int f, p;
3821592Srgrimes{
3831592Srgrimes	char pibuf[1024+1], fibuf[1024], *pbp, *fbp;
3841592Srgrimes	register pcc = 0, fcc = 0;
3851592Srgrimes	int cc, nfd, n;
3861592Srgrimes	char cntl;
3871592Srgrimes
3881592Srgrimes	/*
3891592Srgrimes	 * Must ignore SIGTTOU, otherwise we'll stop
3901592Srgrimes	 * when we try and set slave pty's window shape
3911592Srgrimes	 * (our controlling tty is the master pty).
3921592Srgrimes	 */
3931592Srgrimes	(void) signal(SIGTTOU, SIG_IGN);
3941592Srgrimes	send(f, oobdata, 1, MSG_OOB);	/* indicate new rlogin */
3951592Srgrimes	if (f > p)
3961592Srgrimes		nfd = f + 1;
3971592Srgrimes	else
3981592Srgrimes		nfd = p + 1;
3991592Srgrimes	if (nfd > FD_SETSIZE) {
4001592Srgrimes		syslog(LOG_ERR, "select mask too small, increase FD_SETSIZE");
4011592Srgrimes		fatal(f, "internal error (select mask too small)", 0);
4021592Srgrimes	}
4031592Srgrimes	for (;;) {
4041592Srgrimes		fd_set ibits, obits, ebits, *omask;
4051592Srgrimes
4061592Srgrimes		FD_ZERO(&ebits);
4071592Srgrimes		FD_ZERO(&ibits);
4081592Srgrimes		FD_ZERO(&obits);
4091592Srgrimes		omask = (fd_set *)NULL;
4101592Srgrimes		if (fcc) {
4111592Srgrimes			FD_SET(p, &obits);
4121592Srgrimes			omask = &obits;
4131592Srgrimes		} else
4141592Srgrimes			FD_SET(f, &ibits);
4151592Srgrimes		if (pcc >= 0)
4161592Srgrimes			if (pcc) {
4171592Srgrimes				FD_SET(f, &obits);
4181592Srgrimes				omask = &obits;
4191592Srgrimes			} else
4201592Srgrimes				FD_SET(p, &ibits);
4211592Srgrimes		FD_SET(p, &ebits);
4221592Srgrimes		if ((n = select(nfd, &ibits, omask, &ebits, 0)) < 0) {
4231592Srgrimes			if (errno == EINTR)
4241592Srgrimes				continue;
4251592Srgrimes			fatal(f, "select", 1);
4261592Srgrimes		}
4271592Srgrimes		if (n == 0) {
4281592Srgrimes			/* shouldn't happen... */
4291592Srgrimes			sleep(5);
4301592Srgrimes			continue;
4311592Srgrimes		}
4321592Srgrimes#define	pkcontrol(c)	((c)&(TIOCPKT_FLUSHWRITE|TIOCPKT_NOSTOP|TIOCPKT_DOSTOP))
4331592Srgrimes		if (FD_ISSET(p, &ebits)) {
4341592Srgrimes			cc = read(p, &cntl, 1);
4351592Srgrimes			if (cc == 1 && pkcontrol(cntl)) {
4361592Srgrimes				cntl |= oobdata[0];
4371592Srgrimes				send(f, &cntl, 1, MSG_OOB);
4381592Srgrimes				if (cntl & TIOCPKT_FLUSHWRITE) {
4391592Srgrimes					pcc = 0;
4401592Srgrimes					FD_CLR(p, &ibits);
4411592Srgrimes				}
4421592Srgrimes			}
4431592Srgrimes		}
4441592Srgrimes		if (FD_ISSET(f, &ibits)) {
4451592Srgrimes#ifdef	CRYPT
4461592Srgrimes#ifdef	KERBEROS
4471592Srgrimes			if (doencrypt)
4481592Srgrimes				fcc = des_read(f, fibuf, sizeof(fibuf));
4491592Srgrimes			else
4501592Srgrimes#endif
4511592Srgrimes#endif
4521592Srgrimes				fcc = read(f, fibuf, sizeof(fibuf));
4531592Srgrimes			if (fcc < 0 && errno == EWOULDBLOCK)
4541592Srgrimes				fcc = 0;
4551592Srgrimes			else {
4561592Srgrimes				register char *cp;
4571592Srgrimes				int left, n;
4581592Srgrimes
4591592Srgrimes				if (fcc <= 0)
4601592Srgrimes					break;
4611592Srgrimes				fbp = fibuf;
4621592Srgrimes
4631592Srgrimes			top:
4641592Srgrimes				for (cp = fibuf; cp < fibuf+fcc-1; cp++)
4651592Srgrimes					if (cp[0] == magic[0] &&
4661592Srgrimes					    cp[1] == magic[1]) {
4671592Srgrimes						left = fcc - (cp-fibuf);
4681592Srgrimes						n = control(p, cp, left);
4691592Srgrimes						if (n) {
4701592Srgrimes							left -= n;
4711592Srgrimes							if (left > 0)
4721592Srgrimes								bcopy(cp+n, cp, left);
4731592Srgrimes							fcc -= n;
4741592Srgrimes							goto top; /* n^2 */
4751592Srgrimes						}
4761592Srgrimes					}
4771592Srgrimes				FD_SET(p, &obits);		/* try write */
4781592Srgrimes			}
4791592Srgrimes		}
4801592Srgrimes
4811592Srgrimes		if (FD_ISSET(p, &obits) && fcc > 0) {
4821592Srgrimes			cc = write(p, fbp, fcc);
4831592Srgrimes			if (cc > 0) {
4841592Srgrimes				fcc -= cc;
4851592Srgrimes				fbp += cc;
4861592Srgrimes			}
4871592Srgrimes		}
4881592Srgrimes
4891592Srgrimes		if (FD_ISSET(p, &ibits)) {
4901592Srgrimes			pcc = read(p, pibuf, sizeof (pibuf));
4911592Srgrimes			pbp = pibuf;
4921592Srgrimes			if (pcc < 0 && errno == EWOULDBLOCK)
4931592Srgrimes				pcc = 0;
4941592Srgrimes			else if (pcc <= 0)
4951592Srgrimes				break;
4961592Srgrimes			else if (pibuf[0] == 0) {
4971592Srgrimes				pbp++, pcc--;
4981592Srgrimes#ifdef	CRYPT
4991592Srgrimes#ifdef	KERBEROS
5001592Srgrimes				if (!doencrypt)
5011592Srgrimes#endif
5021592Srgrimes#endif
5031592Srgrimes					FD_SET(f, &obits);	/* try write */
5041592Srgrimes			} else {
5051592Srgrimes				if (pkcontrol(pibuf[0])) {
5061592Srgrimes					pibuf[0] |= oobdata[0];
5071592Srgrimes					send(f, &pibuf[0], 1, MSG_OOB);
5081592Srgrimes				}
5091592Srgrimes				pcc = 0;
5101592Srgrimes			}
5111592Srgrimes		}
5121592Srgrimes		if ((FD_ISSET(f, &obits)) && pcc > 0) {
5131592Srgrimes#ifdef	CRYPT
5141592Srgrimes#ifdef	KERBEROS
5151592Srgrimes			if (doencrypt)
5161592Srgrimes				cc = des_write(f, pbp, pcc);
5171592Srgrimes			else
5181592Srgrimes#endif
5191592Srgrimes#endif
5201592Srgrimes				cc = write(f, pbp, pcc);
5211592Srgrimes			if (cc < 0 && errno == EWOULDBLOCK) {
5221592Srgrimes				/*
5231592Srgrimes				 * This happens when we try write after read
5241592Srgrimes				 * from p, but some old kernels balk at large
5251592Srgrimes				 * writes even when select returns true.
5261592Srgrimes				 */
5271592Srgrimes				if (!FD_ISSET(p, &ibits))
5281592Srgrimes					sleep(5);
5291592Srgrimes				continue;
5301592Srgrimes			}
5311592Srgrimes			if (cc > 0) {
5321592Srgrimes				pcc -= cc;
5331592Srgrimes				pbp += cc;
5341592Srgrimes			}
5351592Srgrimes		}
5361592Srgrimes	}
5371592Srgrimes}
5381592Srgrimes
5391592Srgrimesvoid
5401592Srgrimescleanup(signo)
5411592Srgrimes	int signo;
5421592Srgrimes{
5431592Srgrimes	char *p;
5441592Srgrimes
5451592Srgrimes	p = line + sizeof(_PATH_DEV) - 1;
5461592Srgrimes	if (logout(p))
5471592Srgrimes		logwtmp(p, "", "");
5481592Srgrimes	(void)chmod(line, 0666);
5491592Srgrimes	(void)chown(line, 0, 0);
5501592Srgrimes	*p = 'p';
5511592Srgrimes	(void)chmod(line, 0666);
5521592Srgrimes	(void)chown(line, 0, 0);
5531592Srgrimes	shutdown(netf, 2);
5541592Srgrimes	exit(1);
5551592Srgrimes}
5561592Srgrimes
5571592Srgrimesvoid
5581592Srgrimesfatal(f, msg, syserr)
5591592Srgrimes	int f;
5601592Srgrimes	char *msg;
5611592Srgrimes	int syserr;
5621592Srgrimes{
5631592Srgrimes	int len;
5641592Srgrimes	char buf[BUFSIZ], *bp = buf;
5651592Srgrimes
5661592Srgrimes	/*
5671592Srgrimes	 * Prepend binary one to message if we haven't sent
5681592Srgrimes	 * the magic null as confirmation.
5691592Srgrimes	 */
5701592Srgrimes	if (!confirmed)
5711592Srgrimes		*bp++ = '\01';		/* error indicator */
5721592Srgrimes	if (syserr)
5731592Srgrimes		len = sprintf(bp, "rlogind: %s: %s.\r\n",
5741592Srgrimes		    msg, strerror(errno));
5751592Srgrimes	else
5761592Srgrimes		len = sprintf(bp, "rlogind: %s.\r\n", msg);
5771592Srgrimes	(void) write(f, buf, bp + len - buf);
5781592Srgrimes	exit(1);
5791592Srgrimes}
5801592Srgrimes
5811592Srgrimesint
5821592Srgrimesdo_rlogin(dest)
5831592Srgrimes	struct sockaddr_in *dest;
5841592Srgrimes{
5851592Srgrimes	getstr(rusername, sizeof(rusername), "remuser too long");
5861592Srgrimes	getstr(lusername, sizeof(lusername), "locuser too long");
5871592Srgrimes	getstr(term+ENVSIZE, sizeof(term)-ENVSIZE, "Terminal type too long");
5881592Srgrimes
5891592Srgrimes	pwd = getpwnam(lusername);
5901592Srgrimes	if (pwd == NULL)
5911592Srgrimes		return (-1);
5921592Srgrimes	/* XXX why don't we syslog() failure? */
59312434Speter	return (iruserok(dest->sin_addr.s_addr, pwd->pw_uid == 0,
59412434Speter		rusername, lusername));
5951592Srgrimes}
5961592Srgrimes
5971592Srgrimesvoid
5981592Srgrimesgetstr(buf, cnt, errmsg)
5991592Srgrimes	char *buf;
6001592Srgrimes	int cnt;
6011592Srgrimes	char *errmsg;
6021592Srgrimes{
6031592Srgrimes	char c;
6041592Srgrimes
6051592Srgrimes	do {
6061592Srgrimes		if (read(0, &c, 1) != 1)
6071592Srgrimes			exit(1);
6081592Srgrimes		if (--cnt < 0)
6091592Srgrimes			fatal(STDOUT_FILENO, errmsg, 0);
6101592Srgrimes		*buf++ = c;
6111592Srgrimes	} while (c != 0);
6121592Srgrimes}
6131592Srgrimes
6141592Srgrimesextern	char **environ;
6151592Srgrimes
6161592Srgrimesvoid
6171592Srgrimessetup_term(fd)
6181592Srgrimes	int fd;
6191592Srgrimes{
6201592Srgrimes	register char *cp = index(term+ENVSIZE, '/');
6211592Srgrimes	char *speed;
6221592Srgrimes	struct termios tt;
6231592Srgrimes
6241592Srgrimes#ifndef notyet
6251592Srgrimes	tcgetattr(fd, &tt);
6261592Srgrimes	if (cp) {
6271592Srgrimes		*cp++ = '\0';
6281592Srgrimes		speed = cp;
6291592Srgrimes		cp = index(speed, '/');
6301592Srgrimes		if (cp)
6311592Srgrimes			*cp++ = '\0';
6321592Srgrimes		cfsetspeed(&tt, atoi(speed));
6331592Srgrimes	}
6341592Srgrimes
6351592Srgrimes	tt.c_iflag = TTYDEF_IFLAG;
6361592Srgrimes	tt.c_oflag = TTYDEF_OFLAG;
6371592Srgrimes	tt.c_lflag = TTYDEF_LFLAG;
6381592Srgrimes	tcsetattr(fd, TCSAFLUSH, &tt);
6391592Srgrimes#else
6401592Srgrimes	if (cp) {
6411592Srgrimes		*cp++ = '\0';
6421592Srgrimes		speed = cp;
6431592Srgrimes		cp = index(speed, '/');
6441592Srgrimes		if (cp)
6451592Srgrimes			*cp++ = '\0';
6461592Srgrimes		tcgetattr(fd, &tt);
6471592Srgrimes		cfsetspeed(&tt, atoi(speed));
6481592Srgrimes		tcsetattr(fd, TCSAFLUSH, &tt);
6491592Srgrimes	}
6501592Srgrimes#endif
6511592Srgrimes
6521592Srgrimes	env[0] = term;
6531592Srgrimes	env[1] = 0;
6541592Srgrimes	environ = env;
6551592Srgrimes}
6561592Srgrimes
6571592Srgrimes#ifdef	KERBEROS
6581592Srgrimes#define	VERSION_SIZE	9
6591592Srgrimes
6601592Srgrimes/*
6611592Srgrimes * Do the remote kerberos login to the named host with the
6621592Srgrimes * given inet address
6631592Srgrimes *
6641592Srgrimes * Return 0 on valid authorization
6651592Srgrimes * Return -1 on valid authentication, no authorization
6661592Srgrimes * Return >0 for error conditions
6671592Srgrimes */
6681592Srgrimesint
6691592Srgrimesdo_krb_login(dest)
6701592Srgrimes	struct sockaddr_in *dest;
6711592Srgrimes{
6721592Srgrimes	int rc;
6731592Srgrimes	char instance[INST_SZ], version[VERSION_SIZE];
6741592Srgrimes	long authopts = 0L;	/* !mutual */
6751592Srgrimes	struct sockaddr_in faddr;
6761592Srgrimes
6771592Srgrimes	kdata = (AUTH_DAT *) auth_buf;
6781592Srgrimes	ticket = (KTEXT) tick_buf;
6791592Srgrimes
6801592Srgrimes	instance[0] = '*';
6811592Srgrimes	instance[1] = '\0';
6821592Srgrimes
6831592Srgrimes#ifdef	CRYPT
6841592Srgrimes	if (doencrypt) {
6851592Srgrimes		rc = sizeof(faddr);
6861592Srgrimes		if (getsockname(0, (struct sockaddr *)&faddr, &rc))
6871592Srgrimes			return (-1);
6881592Srgrimes		authopts = KOPT_DO_MUTUAL;
6891592Srgrimes		rc = krb_recvauth(
6901592Srgrimes			authopts, 0,
6911592Srgrimes			ticket, "rcmd",
6921592Srgrimes			instance, dest, &faddr,
6931592Srgrimes			kdata, "", schedule, version);
69413881Smarkm		 des_set_key_krb(&kdata->session, schedule);
6951592Srgrimes
6961592Srgrimes	} else
6971592Srgrimes#endif
6981592Srgrimes		rc = krb_recvauth(
6991592Srgrimes			authopts, 0,
7001592Srgrimes			ticket, "rcmd",
7011592Srgrimes			instance, dest, (struct sockaddr_in *) 0,
70218449Spst			kdata, "", NULL, version);
7031592Srgrimes
7041592Srgrimes	if (rc != KSUCCESS)
7051592Srgrimes		return (rc);
7061592Srgrimes
7071592Srgrimes	getstr(lusername, sizeof(lusername), "locuser");
7081592Srgrimes	/* get the "cmd" in the rcmd protocol */
7091592Srgrimes	getstr(term+ENVSIZE, sizeof(term)-ENVSIZE, "Terminal type");
7101592Srgrimes
7111592Srgrimes	pwd = getpwnam(lusername);
7121592Srgrimes	if (pwd == NULL)
7131592Srgrimes		return (-1);
7141592Srgrimes
7151592Srgrimes	/* returns nonzero for no access */
7161592Srgrimes	if (kuserok(kdata, lusername) != 0)
7171592Srgrimes		return (-1);
7188870Srgrimes
7191592Srgrimes	return (0);
7201592Srgrimes
7211592Srgrimes}
7221592Srgrimes#endif /* KERBEROS */
7231592Srgrimes
7241592Srgrimesvoid
7251592Srgrimesusage()
7261592Srgrimes{
7271592Srgrimes#ifdef KERBEROS
72811486Sdg	syslog(LOG_ERR, "usage: rlogind [-Daln] [-k | -v]");
7291592Srgrimes#else
73011486Sdg	syslog(LOG_ERR, "usage: rlogind [-Daln]");
7311592Srgrimes#endif
7321592Srgrimes}
7331592Srgrimes
7341592Srgrimes/*
7351592Srgrimes * Check whether host h is in our local domain,
7361592Srgrimes * defined as sharing the last two components of the domain part,
7371592Srgrimes * or the entire domain part if the local domain has only one component.
7381592Srgrimes * If either name is unqualified (contains no '.'),
7391592Srgrimes * assume that the host is local, as it will be
7401592Srgrimes * interpreted as such.
7411592Srgrimes */
7421592Srgrimesint
7431592Srgrimeslocal_domain(h)
7441592Srgrimes	char *h;
7451592Srgrimes{
7461592Srgrimes	char localhost[MAXHOSTNAMELEN];
7471592Srgrimes	char *p1, *p2;
7481592Srgrimes
7491592Srgrimes	localhost[0] = 0;
7501592Srgrimes	(void) gethostname(localhost, sizeof(localhost));
7511592Srgrimes	p1 = topdomain(localhost);
7521592Srgrimes	p2 = topdomain(h);
7531592Srgrimes	if (p1 == NULL || p2 == NULL || !strcasecmp(p1, p2))
7541592Srgrimes		return (1);
7551592Srgrimes	return (0);
7561592Srgrimes}
7571592Srgrimes
7581592Srgrimeschar *
7591592Srgrimestopdomain(h)
7601592Srgrimes	char *h;
7611592Srgrimes{
7621592Srgrimes	register char *p;
7631592Srgrimes	char *maybe = NULL;
7641592Srgrimes	int dots = 0;
7651592Srgrimes
7661592Srgrimes	for (p = h + strlen(h); p >= h; p--) {
7671592Srgrimes		if (*p == '.') {
7681592Srgrimes			if (++dots == 2)
7691592Srgrimes				return (p);
7701592Srgrimes			maybe = p;
7711592Srgrimes		}
7721592Srgrimes	}
7731592Srgrimes	return (maybe);
7741592Srgrimes}
775