xref: /csrg-svn/libexec/rlogind/rlogind.c (revision 32116)
121174Sdist /*
221174Sdist  * Copyright (c) 1983 Regents of the University of California.
321174Sdist  * All rights reserved.  The Berkeley software License Agreement
421174Sdist  * specifies the terms and conditions for redistribution.
521174Sdist  */
621174Sdist 
76446Swnj #ifndef lint
821174Sdist char copyright[] =
921174Sdist "@(#) Copyright (c) 1983 Regents of the University of California.\n\
1021174Sdist  All rights reserved.\n";
1121174Sdist #endif not lint
126446Swnj 
1321174Sdist #ifndef lint
14*32116Skarels static char sccsid[] = "@(#)rlogind.c	5.13 (Berkeley) 09/09/87";
1521174Sdist #endif not lint
1621174Sdist 
1716369Skarels /*
1816369Skarels  * remote login server:
1916369Skarels  *	remuser\0
2016369Skarels  *	locuser\0
2118357Ssam  *	terminal info\0
2216369Skarels  *	data
2316369Skarels  */
2416369Skarels 
256446Swnj #include <stdio.h>
266446Swnj #include <sys/types.h>
276446Swnj #include <sys/stat.h>
286446Swnj #include <sys/socket.h>
2913554Ssam #include <sys/wait.h>
3018357Ssam #include <sys/file.h>
319208Ssam 
329208Ssam #include <netinet/in.h>
339208Ssam 
346446Swnj #include <errno.h>
356446Swnj #include <pwd.h>
366446Swnj #include <signal.h>
376446Swnj #include <sgtty.h>
386446Swnj #include <stdio.h>
398380Ssam #include <netdb.h>
4017187Sralph #include <syslog.h>
4118357Ssam #include <strings.h>
426446Swnj 
4324723Smckusick # ifndef TIOCPKT_WINDOW
4424723Smckusick # define TIOCPKT_WINDOW 0x80
4524723Smckusick # endif TIOCPKT_WINDOW
4624723Smckusick 
476446Swnj extern	errno;
4810417Ssam int	reapchild();
496446Swnj struct	passwd *getpwnam();
5024723Smckusick char	*malloc();
5116369Skarels 
526446Swnj main(argc, argv)
536446Swnj 	int argc;
546446Swnj 	char **argv;
556446Swnj {
5617156Ssam 	int on = 1, options = 0, fromlen;
576446Swnj 	struct sockaddr_in from;
586446Swnj 
5924855Seric 	openlog("rlogind", LOG_PID | LOG_AUTH, LOG_AUTH);
6016369Skarels 	fromlen = sizeof (from);
6116369Skarels 	if (getpeername(0, &from, &fromlen) < 0) {
6216369Skarels 		fprintf(stderr, "%s: ", argv[0]);
6316369Skarels 		perror("getpeername");
6416369Skarels 		_exit(1);
658380Ssam 	}
6617156Ssam 	if (setsockopt(0, SOL_SOCKET, SO_KEEPALIVE, &on, sizeof (on)) < 0) {
6717187Sralph 		syslog(LOG_WARNING, "setsockopt (SO_KEEPALIVE): %m");
686446Swnj 	}
6916369Skarels 	doit(0, &from);
706446Swnj }
716446Swnj 
726446Swnj int	child;
736446Swnj int	cleanup();
746446Swnj int	netf;
756446Swnj extern	errno;
766446Swnj char	*line;
7724724Smckusick extern	char	*inet_ntoa();
786446Swnj 
7924889Smckusick struct winsize win = { 0, 0, 0, 0 };
8024723Smckusick 
8124889Smckusick 
826446Swnj doit(f, fromp)
836446Swnj 	int f;
846446Swnj 	struct sockaddr_in *fromp;
856446Swnj {
8618357Ssam 	int i, p, t, pid, on = 1;
8718357Ssam 	register struct hostent *hp;
8824724Smckusick 	struct hostent hostent;
898380Ssam 	char c;
906446Swnj 
916446Swnj 	alarm(60);
926446Swnj 	read(f, &c, 1);
936446Swnj 	if (c != 0)
946446Swnj 		exit(1);
956446Swnj 	alarm(0);
9616227Skarels 	fromp->sin_port = ntohs((u_short)fromp->sin_port);
978380Ssam 	hp = gethostbyaddr(&fromp->sin_addr, sizeof (struct in_addr),
988380Ssam 		fromp->sin_family);
9911345Ssam 	if (hp == 0) {
10024724Smckusick 		/*
10124724Smckusick 		 * Only the name is used below.
10224724Smckusick 		 */
10324724Smckusick 		hp = &hostent;
10424724Smckusick 		hp->h_name = inet_ntoa(fromp->sin_addr);
10511345Ssam 	}
1066446Swnj 	if (fromp->sin_family != AF_INET ||
107*32116Skarels 	    fromp->sin_port >= IPPORT_RESERVED ||
108*32116Skarels 	    fromp->sin_port < IPPORT_RESERVED/2)
1099242Ssam 		fatal(f, "Permission denied");
1106446Swnj 	write(f, "", 1);
1116446Swnj 	for (c = 'p'; c <= 's'; c++) {
1126446Swnj 		struct stat stb;
1136446Swnj 		line = "/dev/ptyXX";
1146446Swnj 		line[strlen("/dev/pty")] = c;
1156446Swnj 		line[strlen("/dev/ptyp")] = '0';
1166446Swnj 		if (stat(line, &stb) < 0)
1176446Swnj 			break;
1186446Swnj 		for (i = 0; i < 16; i++) {
1196446Swnj 			line[strlen("/dev/ptyp")] = "0123456789abcdef"[i];
1206446Swnj 			p = open(line, 2);
1216446Swnj 			if (p > 0)
1226446Swnj 				goto gotpty;
1236446Swnj 		}
1246446Swnj 	}
12524723Smckusick 	fatal(f, "Out of ptys");
1269242Ssam 	/*NOTREACHED*/
1276446Swnj gotpty:
12824889Smckusick 	(void) ioctl(p, TIOCSWINSZ, &win);
12916227Skarels 	netf = f;
1306446Swnj 	line[strlen("/dev/")] = 't';
1316446Swnj #ifdef DEBUG
1326446Swnj 	{ int tt = open("/dev/tty", 2);
1336446Swnj 	  if (tt > 0) {
1346446Swnj 		ioctl(tt, TIOCNOTTY, 0);
1356446Swnj 		close(tt);
1366446Swnj 	  }
1376446Swnj 	}
1386446Swnj #endif
1396446Swnj 	t = open(line, 2);
1409242Ssam 	if (t < 0)
1419242Ssam 		fatalperror(f, line, errno);
1426446Swnj 	{ struct sgttyb b;
1436446Swnj 	  gtty(t, &b); b.sg_flags = RAW|ANYP; stty(t, &b);
1446446Swnj 	}
1459242Ssam 	pid = fork();
1469242Ssam 	if (pid < 0)
1479242Ssam 		fatalperror(f, "", errno);
14818357Ssam 	if (pid == 0) {
14918357Ssam 		close(f), close(p);
15018357Ssam 		dup2(t, 0), dup2(t, 1), dup2(t, 2);
15116227Skarels 		close(t);
15218357Ssam 		execl("/bin/login", "login", "-r", hp->h_name, 0);
15318357Ssam 		fatalperror(2, "/bin/login", errno);
15418357Ssam 		/*NOTREACHED*/
15518357Ssam 	}
15618357Ssam 	close(t);
15718357Ssam 	ioctl(f, FIONBIO, &on);
15818357Ssam 	ioctl(p, FIONBIO, &on);
15918357Ssam 	ioctl(p, TIOCPKT, &on);
16018357Ssam 	signal(SIGTSTP, SIG_IGN);
16118357Ssam 	signal(SIGCHLD, cleanup);
16224724Smckusick 	setpgrp(0, 0);
16318357Ssam 	protocol(f, p);
16430600Smckusick 	signal(SIGCHLD, SIG_IGN);
16518357Ssam 	cleanup();
16618357Ssam }
1679242Ssam 
16818357Ssam char	magic[2] = { 0377, 0377 };
16925423Skarels char	oobdata[] = {TIOCPKT_WINDOW};
17018357Ssam 
17118357Ssam /*
17218357Ssam  * Handle a "control" request (signaled by magic being present)
17318357Ssam  * in the data stream.  For now, we are only willing to handle
17418357Ssam  * window size changes.
17518357Ssam  */
17618357Ssam control(pty, cp, n)
17718357Ssam 	int pty;
17818357Ssam 	char *cp;
17918357Ssam 	int n;
18018357Ssam {
18128705Smckusick 	struct winsize w;
18218357Ssam 
18328705Smckusick 	if (n < 4+sizeof (w) || cp[2] != 's' || cp[3] != 's')
18418357Ssam 		return (0);
18525423Skarels 	oobdata[0] &= ~TIOCPKT_WINDOW;	/* we know he heard */
18628705Smckusick 	bcopy(cp+4, (char *)&w, sizeof(w));
18728705Smckusick 	w.ws_row = ntohs(w.ws_row);
18828705Smckusick 	w.ws_col = ntohs(w.ws_col);
18928705Smckusick 	w.ws_xpixel = ntohs(w.ws_xpixel);
19028705Smckusick 	w.ws_ypixel = ntohs(w.ws_ypixel);
19128705Smckusick 	(void)ioctl(pty, TIOCSWINSZ, &w);
19228705Smckusick 	return (4+sizeof (w));
19318357Ssam }
19418357Ssam 
19518357Ssam /*
19618357Ssam  * rlogin "protocol" machine.
19718357Ssam  */
19818357Ssam protocol(f, p)
19918357Ssam 	int f, p;
20018357Ssam {
20118357Ssam 	char pibuf[1024], fibuf[1024], *pbp, *fbp;
20218357Ssam 	register pcc = 0, fcc = 0;
20325423Skarels 	int cc;
20425740Skarels 	char cntl;
20518357Ssam 
20618482Ssam 	/*
20718484Ssam 	 * Must ignore SIGTTOU, otherwise we'll stop
20818484Ssam 	 * when we try and set slave pty's window shape
20925423Skarels 	 * (our controlling tty is the master pty).
21018482Ssam 	 */
21118484Ssam 	(void) signal(SIGTTOU, SIG_IGN);
21225423Skarels 	send(f, oobdata, 1, MSG_OOB);	/* indicate new rlogin */
21318357Ssam 	for (;;) {
21425740Skarels 		int ibits, obits, ebits;
21518357Ssam 
21625740Skarels 		ibits = 0;
21725740Skarels 		obits = 0;
21818357Ssam 		if (fcc)
21918357Ssam 			obits |= (1<<p);
22018357Ssam 		else
22118357Ssam 			ibits |= (1<<f);
22218357Ssam 		if (pcc >= 0)
22318357Ssam 			if (pcc)
22418357Ssam 				obits |= (1<<f);
2259242Ssam 			else
22618357Ssam 				ibits |= (1<<p);
22725740Skarels 		ebits = (1<<p);
22825740Skarels 		if (select(16, &ibits, &obits, &ebits, 0) < 0) {
22918357Ssam 			if (errno == EINTR)
2306446Swnj 				continue;
23118357Ssam 			fatalperror(f, "select", errno);
23218357Ssam 		}
23325740Skarels 		if (ibits == 0 && obits == 0 && ebits == 0) {
23418357Ssam 			/* shouldn't happen... */
23518357Ssam 			sleep(5);
23618357Ssam 			continue;
23718357Ssam 		}
23825740Skarels #define	pkcontrol(c)	((c)&(TIOCPKT_FLUSHWRITE|TIOCPKT_NOSTOP|TIOCPKT_DOSTOP))
23925740Skarels 		if (ebits & (1<<p)) {
24025740Skarels 			cc = read(p, &cntl, 1);
24125740Skarels 			if (cc == 1 && pkcontrol(cntl)) {
24225740Skarels 				cntl |= oobdata[0];
24325740Skarels 				send(f, &cntl, 1, MSG_OOB);
24425740Skarels 				if (cntl & TIOCPKT_FLUSHWRITE) {
24525740Skarels 					pcc = 0;
24625740Skarels 					ibits &= ~(1<<p);
24725740Skarels 				}
24825740Skarels 			}
24925740Skarels 		}
25018357Ssam 		if (ibits & (1<<f)) {
25118357Ssam 			fcc = read(f, fibuf, sizeof (fibuf));
25218357Ssam 			if (fcc < 0 && errno == EWOULDBLOCK)
25318357Ssam 				fcc = 0;
25418357Ssam 			else {
25518357Ssam 				register char *cp;
25618357Ssam 				int left, n;
25718357Ssam 
25818357Ssam 				if (fcc <= 0)
25916227Skarels 					break;
26018357Ssam 				fbp = fibuf;
26124723Smckusick 
26218357Ssam 			top:
26325423Skarels 				for (cp = fibuf; cp < fibuf+fcc-1; cp++)
26418357Ssam 					if (cp[0] == magic[0] &&
26518357Ssam 					    cp[1] == magic[1]) {
26618357Ssam 						left = fcc - (cp-fibuf);
26718357Ssam 						n = control(p, cp, left);
26818357Ssam 						if (n) {
26918357Ssam 							left -= n;
27018357Ssam 							if (left > 0)
27125423Skarels 								bcopy(cp+n, cp, left);
27218357Ssam 							fcc -= n;
27318357Ssam 							goto top; /* n^2 */
27425423Skarels 						}
27525423Skarels 					}
27625423Skarels 			}
27725423Skarels 		}
27824723Smckusick 
27924723Smckusick 		if ((obits & (1<<p)) && fcc > 0) {
28025423Skarels 			cc = write(p, fbp, fcc);
28124723Smckusick 			if (cc > 0) {
28224723Smckusick 				fcc -= cc;
28324723Smckusick 				fbp += cc;
2846446Swnj 			}
28518357Ssam 		}
28624723Smckusick 
28718357Ssam 		if (ibits & (1<<p)) {
28818357Ssam 			pcc = read(p, pibuf, sizeof (pibuf));
28918357Ssam 			pbp = pibuf;
29018357Ssam 			if (pcc < 0 && errno == EWOULDBLOCK)
29118357Ssam 				pcc = 0;
29218357Ssam 			else if (pcc <= 0)
29318357Ssam 				break;
29418357Ssam 			else if (pibuf[0] == 0)
29518357Ssam 				pbp++, pcc--;
29618357Ssam 			else {
29718357Ssam 				if (pkcontrol(pibuf[0])) {
29825423Skarels 					pibuf[0] |= oobdata[0];
29918357Ssam 					send(f, &pibuf[0], 1, MSG_OOB);
30016227Skarels 				}
30118357Ssam 				pcc = 0;
3026446Swnj 			}
30318357Ssam 		}
30418357Ssam 		if ((obits & (1<<f)) && pcc > 0) {
30525423Skarels 			cc = write(f, pbp, pcc);
30625423Skarels 			if (cc < 0 && errno == EWOULDBLOCK) {
30725423Skarels 				/* also shouldn't happen */
30825423Skarels 				sleep(5);
30925423Skarels 				continue;
31025423Skarels 			}
31118357Ssam 			if (cc > 0) {
31218357Ssam 				pcc -= cc;
31318357Ssam 				pbp += cc;
31418357Ssam 			}
3156446Swnj 		}
3166446Swnj 	}
3176446Swnj }
3186446Swnj 
3196446Swnj cleanup()
3206446Swnj {
3216446Swnj 
3226446Swnj 	rmut();
32310009Ssam 	vhangup();		/* XXX */
32410192Ssam 	shutdown(netf, 2);
3256446Swnj 	exit(1);
3266446Swnj }
3276446Swnj 
3289242Ssam fatal(f, msg)
3299242Ssam 	int f;
3309242Ssam 	char *msg;
3319242Ssam {
3329242Ssam 	char buf[BUFSIZ];
3339242Ssam 
3349242Ssam 	buf[0] = '\01';		/* error indicator */
33513554Ssam 	(void) sprintf(buf + 1, "rlogind: %s.\r\n", msg);
3369242Ssam 	(void) write(f, buf, strlen(buf));
3379242Ssam 	exit(1);
3389242Ssam }
3399242Ssam 
3409242Ssam fatalperror(f, msg, errno)
3419242Ssam 	int f;
3429242Ssam 	char *msg;
3439242Ssam 	int errno;
3449242Ssam {
3459242Ssam 	char buf[BUFSIZ];
34616227Skarels 	extern int sys_nerr;
3479242Ssam 	extern char *sys_errlist[];
3489242Ssam 
34918357Ssam 	if ((unsigned)errno < sys_nerr)
35016227Skarels 		(void) sprintf(buf, "%s: %s", msg, sys_errlist[errno]);
35116227Skarels 	else
35216227Skarels 		(void) sprintf(buf, "%s: Error %d", msg, errno);
3539242Ssam 	fatal(f, buf);
3549242Ssam }
3559242Ssam 
3566446Swnj #include <utmp.h>
3576446Swnj 
3586446Swnj struct	utmp wtmp;
3596446Swnj char	wtmpf[]	= "/usr/adm/wtmp";
36023566Sbloom char	utmpf[] = "/etc/utmp";
3616446Swnj #define SCPYN(a, b)	strncpy(a, b, sizeof(a))
3626446Swnj #define SCMPN(a, b)	strncmp(a, b, sizeof(a))
3636446Swnj 
3646446Swnj rmut()
3656446Swnj {
3666446Swnj 	register f;
3676446Swnj 	int found = 0;
36823566Sbloom 	struct utmp *u, *utmp;
36923566Sbloom 	int nutmp;
37023566Sbloom 	struct stat statbf;
3716446Swnj 
37223566Sbloom 	f = open(utmpf, O_RDWR);
3736446Swnj 	if (f >= 0) {
37423566Sbloom 		fstat(f, &statbf);
37523566Sbloom 		utmp = (struct utmp *)malloc(statbf.st_size);
37623566Sbloom 		if (!utmp)
37723566Sbloom 			syslog(LOG_ERR, "utmp malloc failed");
37823566Sbloom 		if (statbf.st_size && utmp) {
37923566Sbloom 			nutmp = read(f, utmp, statbf.st_size);
38023566Sbloom 			nutmp /= sizeof(struct utmp);
38123566Sbloom 
38223566Sbloom 			for (u = utmp ; u < &utmp[nutmp] ; u++) {
38323566Sbloom 				if (SCMPN(u->ut_line, line+5) ||
38423566Sbloom 				    u->ut_name[0]==0)
38523566Sbloom 					continue;
38623566Sbloom 				lseek(f, ((long)u)-((long)utmp), L_SET);
38723566Sbloom 				SCPYN(u->ut_name, "");
38823566Sbloom 				SCPYN(u->ut_host, "");
38923566Sbloom 				time(&u->ut_time);
39023566Sbloom 				write(f, (char *)u, sizeof(wtmp));
39123566Sbloom 				found++;
39223566Sbloom 			}
3936446Swnj 		}
3946446Swnj 		close(f);
3956446Swnj 	}
3966446Swnj 	if (found) {
39723566Sbloom 		f = open(wtmpf, O_WRONLY|O_APPEND);
3986446Swnj 		if (f >= 0) {
3996446Swnj 			SCPYN(wtmp.ut_line, line+5);
4006446Swnj 			SCPYN(wtmp.ut_name, "");
40112681Ssam 			SCPYN(wtmp.ut_host, "");
4026446Swnj 			time(&wtmp.ut_time);
4036446Swnj 			write(f, (char *)&wtmp, sizeof(wtmp));
4046446Swnj 			close(f);
4056446Swnj 		}
4066446Swnj 	}
4076446Swnj 	chmod(line, 0666);
4086446Swnj 	chown(line, 0, 0);
4096446Swnj 	line[strlen("/dev/")] = 'p';
4106446Swnj 	chmod(line, 0666);
4116446Swnj 	chown(line, 0, 0);
4126446Swnj }
413