xref: /csrg-svn/libexec/rlogind/rlogind.c (revision 18482)
16446Swnj #ifndef lint
2*18482Ssam static	char sccsid[] = "@(#)rlogind.c	4.24 (Berkeley) 03/23/85";
36446Swnj #endif
46446Swnj 
516369Skarels /*
616369Skarels  * remote login server:
716369Skarels  *	remuser\0
816369Skarels  *	locuser\0
918357Ssam  *	terminal info\0
1016369Skarels  *	data
1116369Skarels  */
1216369Skarels 
136446Swnj #include <stdio.h>
146446Swnj #include <sys/types.h>
156446Swnj #include <sys/stat.h>
166446Swnj #include <sys/socket.h>
1713554Ssam #include <sys/wait.h>
1818357Ssam #include <sys/file.h>
199208Ssam 
209208Ssam #include <netinet/in.h>
219208Ssam 
226446Swnj #include <errno.h>
236446Swnj #include <pwd.h>
246446Swnj #include <signal.h>
256446Swnj #include <sgtty.h>
266446Swnj #include <stdio.h>
278380Ssam #include <netdb.h>
2817187Sralph #include <syslog.h>
2918357Ssam #include <strings.h>
306446Swnj 
316446Swnj extern	errno;
3210417Ssam int	reapchild();
336446Swnj struct	passwd *getpwnam();
3418357Ssam char	*crypt(), *malloc();
3516369Skarels 
366446Swnj main(argc, argv)
376446Swnj 	int argc;
386446Swnj 	char **argv;
396446Swnj {
4017156Ssam 	int on = 1, options = 0, fromlen;
416446Swnj 	struct sockaddr_in from;
426446Swnj 
4316369Skarels 	fromlen = sizeof (from);
4416369Skarels 	if (getpeername(0, &from, &fromlen) < 0) {
4516369Skarels 		fprintf(stderr, "%s: ", argv[0]);
4616369Skarels 		perror("getpeername");
4716369Skarels 		_exit(1);
488380Ssam 	}
4917156Ssam 	if (setsockopt(0, SOL_SOCKET, SO_KEEPALIVE, &on, sizeof (on)) < 0) {
5017187Sralph 		openlog(argv[0], LOG_PID, 0);
5117187Sralph 		syslog(LOG_WARNING, "setsockopt (SO_KEEPALIVE): %m");
526446Swnj 	}
5316369Skarels 	doit(0, &from);
546446Swnj }
556446Swnj 
566446Swnj int	child;
576446Swnj int	cleanup();
586446Swnj int	netf;
596446Swnj extern	errno;
606446Swnj char	*line;
616446Swnj 
626446Swnj doit(f, fromp)
636446Swnj 	int f;
646446Swnj 	struct sockaddr_in *fromp;
656446Swnj {
6618357Ssam 	int i, p, t, pid, on = 1;
6718357Ssam 	register struct hostent *hp;
688380Ssam 	char c;
696446Swnj 
706446Swnj 	alarm(60);
716446Swnj 	read(f, &c, 1);
726446Swnj 	if (c != 0)
736446Swnj 		exit(1);
746446Swnj 	alarm(0);
7516227Skarels 	fromp->sin_port = ntohs((u_short)fromp->sin_port);
768380Ssam 	hp = gethostbyaddr(&fromp->sin_addr, sizeof (struct in_addr),
778380Ssam 		fromp->sin_family);
7811345Ssam 	if (hp == 0) {
7916227Skarels 		char buf[BUFSIZ];
8011345Ssam 
8111345Ssam 		fatal(f, sprintf(buf, "Host name for your address (%s) unknown",
8218357Ssam 			inet_ntoa(fromp->sin_addr)));
8311345Ssam 	}
846446Swnj 	if (fromp->sin_family != AF_INET ||
8516227Skarels 	    fromp->sin_port >= IPPORT_RESERVED)
869242Ssam 		fatal(f, "Permission denied");
876446Swnj 	write(f, "", 1);
886446Swnj 	for (c = 'p'; c <= 's'; c++) {
896446Swnj 		struct stat stb;
906446Swnj 		line = "/dev/ptyXX";
916446Swnj 		line[strlen("/dev/pty")] = c;
926446Swnj 		line[strlen("/dev/ptyp")] = '0';
936446Swnj 		if (stat(line, &stb) < 0)
946446Swnj 			break;
956446Swnj 		for (i = 0; i < 16; i++) {
966446Swnj 			line[strlen("/dev/ptyp")] = "0123456789abcdef"[i];
976446Swnj 			p = open(line, 2);
986446Swnj 			if (p > 0)
996446Swnj 				goto gotpty;
1006446Swnj 		}
1016446Swnj 	}
1029242Ssam 	fatal(f, "All network ports in use");
1039242Ssam 	/*NOTREACHED*/
1046446Swnj gotpty:
10516227Skarels 	netf = f;
1066446Swnj 	line[strlen("/dev/")] = 't';
1076446Swnj #ifdef DEBUG
1086446Swnj 	{ int tt = open("/dev/tty", 2);
1096446Swnj 	  if (tt > 0) {
1106446Swnj 		ioctl(tt, TIOCNOTTY, 0);
1116446Swnj 		close(tt);
1126446Swnj 	  }
1136446Swnj 	}
1146446Swnj #endif
1156446Swnj 	t = open(line, 2);
1169242Ssam 	if (t < 0)
1179242Ssam 		fatalperror(f, line, errno);
1186446Swnj 	{ struct sgttyb b;
1196446Swnj 	  gtty(t, &b); b.sg_flags = RAW|ANYP; stty(t, &b);
1206446Swnj 	}
1219242Ssam 	pid = fork();
1229242Ssam 	if (pid < 0)
1239242Ssam 		fatalperror(f, "", errno);
12418357Ssam 	if (pid == 0) {
12518357Ssam 		close(f), close(p);
12618357Ssam 		dup2(t, 0), dup2(t, 1), dup2(t, 2);
12716227Skarels 		close(t);
12818357Ssam 		execl("/bin/login", "login", "-r", hp->h_name, 0);
12918357Ssam 		fatalperror(2, "/bin/login", errno);
13018357Ssam 		/*NOTREACHED*/
13118357Ssam 	}
13218357Ssam 	close(t);
13318357Ssam 	ioctl(f, FIONBIO, &on);
13418357Ssam 	ioctl(p, FIONBIO, &on);
13518357Ssam 	ioctl(p, TIOCPKT, &on);
13618357Ssam 	signal(SIGTSTP, SIG_IGN);
13718357Ssam 	signal(SIGCHLD, cleanup);
13818357Ssam 	protocol(f, p);
13918357Ssam 	cleanup();
14018357Ssam }
1419242Ssam 
14218357Ssam char	magic[2] = { 0377, 0377 };
14318357Ssam 
14418357Ssam /*
14518357Ssam  * Handle a "control" request (signaled by magic being present)
14618357Ssam  * in the data stream.  For now, we are only willing to handle
14718357Ssam  * window size changes.
14818357Ssam  */
14918357Ssam control(pty, cp, n)
15018357Ssam 	int pty;
15118357Ssam 	char *cp;
15218357Ssam 	int n;
15318357Ssam {
15418357Ssam 	struct winsize *wp;
15518357Ssam 
15618357Ssam 	if (n < 4+sizeof (*wp) || cp[2] != 's' || cp[3] != 's')
15718357Ssam 		return (0);
15818357Ssam 	wp = (struct winsize *)(cp+4);
15918357Ssam 	wp->ws_row = ntohs(wp->ws_row);
16018357Ssam 	wp->ws_col = ntohs(wp->ws_col);
16118357Ssam 	wp->ws_xpixel = ntohs(wp->ws_xpixel);
16218357Ssam 	wp->ws_ypixel = ntohs(wp->ws_ypixel);
16318357Ssam 	(void)ioctl(pty, TIOCSWINSZ, wp);
16418357Ssam 	return (4+sizeof (*wp));
16518357Ssam }
16618357Ssam 
16718357Ssam /*
16818357Ssam  * rlogin "protocol" machine.
16918357Ssam  */
17018357Ssam protocol(f, p)
17118357Ssam 	int f, p;
17218357Ssam {
17318357Ssam 	char pibuf[1024], fibuf[1024], *pbp, *fbp;
17418357Ssam 	register pcc = 0, fcc = 0;
17518357Ssam 	int cc, stop = TIOCPKT_DOSTOP;
17618357Ssam 
177*18482Ssam 	/*
178*18482Ssam 	 * This is a hack for the TIOCSWINSZ calls
179*18482Ssam 	 * (csh pgrp manipulation appears to cause
180*18482Ssam 	 * trouble).
181*18482Ssam 	 */
182*18482Ssam 	(void) signal(SIGTTOU, SIG_IGN);		/* XXX */
18318357Ssam 	for (;;) {
18418357Ssam 		int ibits = 0, obits = 0;
18518357Ssam 
18618357Ssam 		if (fcc)
18718357Ssam 			obits |= (1<<p);
18818357Ssam 		else
18918357Ssam 			ibits |= (1<<f);
19018357Ssam 		if (pcc >= 0)
19118357Ssam 			if (pcc)
19218357Ssam 				obits |= (1<<f);
1939242Ssam 			else
19418357Ssam 				ibits |= (1<<p);
19518357Ssam 		if (select(16, &ibits, &obits, 0, 0) < 0) {
19618357Ssam 			if (errno == EINTR)
1976446Swnj 				continue;
19818357Ssam 			fatalperror(f, "select", errno);
19918357Ssam 		}
20018357Ssam 		if (ibits == 0 && obits == 0) {
20118357Ssam 			/* shouldn't happen... */
20218357Ssam 			sleep(5);
20318357Ssam 			continue;
20418357Ssam 		}
20518357Ssam 		if (ibits & (1<<f)) {
20618357Ssam 			fcc = read(f, fibuf, sizeof (fibuf));
20718357Ssam 			if (fcc < 0 && errno == EWOULDBLOCK)
20818357Ssam 				fcc = 0;
20918357Ssam 			else {
21018357Ssam 				register char *cp;
21118357Ssam 				int left, n;
21218357Ssam 
21318357Ssam 				if (fcc <= 0)
21416227Skarels 					break;
21518357Ssam 				fbp = fibuf;
21618357Ssam 			top:
21718357Ssam 				for (cp = fibuf; cp < fibuf+fcc; cp++)
21818357Ssam 					if (cp[0] == magic[0] &&
21918357Ssam 					    cp[1] == magic[1]) {
22018357Ssam 						left = fcc - (cp-fibuf);
22118357Ssam 						n = control(p, cp, left);
22218357Ssam 						if (n) {
22318357Ssam 							left -= n;
22418357Ssam 							if (left > 0)
22518357Ssam 								bcopy(cp, cp+n, left);
22618357Ssam 							fcc -= n;
22718357Ssam 							goto top; /* n^2 */
22818357Ssam 						}
2296446Swnj 					}
2306446Swnj 			}
23118357Ssam 		}
23218357Ssam 		if (ibits & (1<<p)) {
23318357Ssam 			pcc = read(p, pibuf, sizeof (pibuf));
23418357Ssam 			pbp = pibuf;
23518357Ssam 			if (pcc < 0 && errno == EWOULDBLOCK)
23618357Ssam 				pcc = 0;
23718357Ssam 			else if (pcc <= 0)
23818357Ssam 				break;
23918357Ssam 			else if (pibuf[0] == 0)
24018357Ssam 				pbp++, pcc--;
24118357Ssam 			else {
24218357Ssam #define	pkcontrol(c)	((c)&(TIOCPKT_FLUSHWRITE|TIOCPKT_NOSTOP|TIOCPKT_DOSTOP))
24318357Ssam 				if (pkcontrol(pibuf[0])) {
24418357Ssam 				/* The following 3 lines do nothing. */
24518357Ssam 					int nstop = pibuf[0] &
24618357Ssam 					    (TIOCPKT_NOSTOP|TIOCPKT_DOSTOP);
24718357Ssam 
24818357Ssam 					if (nstop)
24918357Ssam 						stop = nstop;
25018357Ssam 					pibuf[0] |= nstop;
25118357Ssam 					send(f, &pibuf[0], 1, MSG_OOB);
25216227Skarels 				}
25318357Ssam 				pcc = 0;
2546446Swnj 			}
25518357Ssam 		}
25618357Ssam 		if ((obits & (1<<f)) && pcc > 0) {
25718357Ssam 			cc = write(f, pbp, pcc);
25818357Ssam 			if (cc < 0 && errno == EWOULDBLOCK) {
25918357Ssam 				/* also shouldn't happen */
26018357Ssam 				sleep(5);
26118357Ssam 				continue;
2626446Swnj 			}
26318357Ssam 			if (cc > 0) {
26418357Ssam 				pcc -= cc;
26518357Ssam 				pbp += cc;
26618357Ssam 			}
2676446Swnj 		}
26818357Ssam 		if ((obits & (1<<p)) && fcc > 0) {
26918357Ssam 			cc = write(p, fbp, fcc);
27018357Ssam 			if (cc > 0) {
27118357Ssam 				fcc -= cc;
27218357Ssam 				fbp += cc;
27318357Ssam 			}
27418357Ssam 		}
2756446Swnj 	}
2766446Swnj }
2776446Swnj 
2786446Swnj cleanup()
2796446Swnj {
2806446Swnj 
2816446Swnj 	rmut();
28210009Ssam 	vhangup();		/* XXX */
28310192Ssam 	shutdown(netf, 2);
2846446Swnj 	kill(0, SIGKILL);
2856446Swnj 	exit(1);
2866446Swnj }
2876446Swnj 
2889242Ssam fatal(f, msg)
2899242Ssam 	int f;
2909242Ssam 	char *msg;
2919242Ssam {
2929242Ssam 	char buf[BUFSIZ];
2939242Ssam 
2949242Ssam 	buf[0] = '\01';		/* error indicator */
29513554Ssam 	(void) sprintf(buf + 1, "rlogind: %s.\r\n", msg);
2969242Ssam 	(void) write(f, buf, strlen(buf));
2979242Ssam 	exit(1);
2989242Ssam }
2999242Ssam 
3009242Ssam fatalperror(f, msg, errno)
3019242Ssam 	int f;
3029242Ssam 	char *msg;
3039242Ssam 	int errno;
3049242Ssam {
3059242Ssam 	char buf[BUFSIZ];
30616227Skarels 	extern int sys_nerr;
3079242Ssam 	extern char *sys_errlist[];
3089242Ssam 
30918357Ssam 	if ((unsigned)errno < sys_nerr)
31016227Skarels 		(void) sprintf(buf, "%s: %s", msg, sys_errlist[errno]);
31116227Skarels 	else
31216227Skarels 		(void) sprintf(buf, "%s: Error %d", msg, errno);
3139242Ssam 	fatal(f, buf);
3149242Ssam }
3159242Ssam 
3166446Swnj #include <utmp.h>
3176446Swnj 
3186446Swnj struct	utmp wtmp;
3196446Swnj char	wtmpf[]	= "/usr/adm/wtmp";
3206446Swnj char	utmp[] = "/etc/utmp";
3216446Swnj #define SCPYN(a, b)	strncpy(a, b, sizeof(a))
3226446Swnj #define SCMPN(a, b)	strncmp(a, b, sizeof(a))
3236446Swnj 
3246446Swnj rmut()
3256446Swnj {
3266446Swnj 	register f;
3276446Swnj 	int found = 0;
3286446Swnj 
32918357Ssam 	f = open(utmp, O_RDWR);
3306446Swnj 	if (f >= 0) {
3316446Swnj 		while(read(f, (char *)&wtmp, sizeof(wtmp)) == sizeof(wtmp)) {
3326446Swnj 			if (SCMPN(wtmp.ut_line, line+5) || wtmp.ut_name[0]==0)
3336446Swnj 				continue;
33418357Ssam 			lseek(f, -(long)sizeof(wtmp), L_INCR);
3356446Swnj 			SCPYN(wtmp.ut_name, "");
33612681Ssam 			SCPYN(wtmp.ut_host, "");
3376446Swnj 			time(&wtmp.ut_time);
3386446Swnj 			write(f, (char *)&wtmp, sizeof(wtmp));
3396446Swnj 			found++;
3406446Swnj 		}
3416446Swnj 		close(f);
3426446Swnj 	}
3436446Swnj 	if (found) {
34418357Ssam 		f = open(wtmpf, O_WRONLY);
3456446Swnj 		if (f >= 0) {
3466446Swnj 			SCPYN(wtmp.ut_line, line+5);
3476446Swnj 			SCPYN(wtmp.ut_name, "");
34812681Ssam 			SCPYN(wtmp.ut_host, "");
3496446Swnj 			time(&wtmp.ut_time);
35018357Ssam 			lseek(f, (long)0, L_XTND);
3516446Swnj 			write(f, (char *)&wtmp, sizeof(wtmp));
3526446Swnj 			close(f);
3536446Swnj 		}
3546446Swnj 	}
3556446Swnj 	chmod(line, 0666);
3566446Swnj 	chown(line, 0, 0);
3576446Swnj 	line[strlen("/dev/")] = 'p';
3586446Swnj 	chmod(line, 0666);
3596446Swnj 	chown(line, 0, 0);
3606446Swnj }
361