xref: /csrg-svn/libexec/rshd/rshd.c (revision 10596)
16454Swnj #ifndef lint
2*10596Ssam static char sccsid[] = "@(#)rshd.c	4.11 83/01/22";
36454Swnj #endif
46454Swnj 
56454Swnj #include <sys/ioctl.h>
66454Swnj #include <sys/param.h>
76454Swnj #include <sys/socket.h>
89212Ssam 
99212Ssam #include <netinet/in.h>
109212Ssam 
119212Ssam #include <stdio.h>
126454Swnj #include <errno.h>
136454Swnj #include <pwd.h>
146454Swnj #include <wait.h>
156454Swnj #include <signal.h>
168380Ssam #include <netdb.h>
176454Swnj 
186454Swnj int	errno;
1910416Ssam int	reapchild();
208380Ssam struct	sockaddr_in sin = { AF_INET };
216454Swnj struct	passwd *getpwnam();
228380Ssam char	*index(), *rindex(), *sprintf();
2310273Ssam int	options;
246454Swnj /* VARARGS 1 */
256454Swnj int	error();
266454Swnj /*
276454Swnj  * remote execute server:
286454Swnj  *	remuser\0
296454Swnj  *	locuser\0
306454Swnj  *	command\0
316454Swnj  *	data
326454Swnj  */
336454Swnj main(argc, argv)
346454Swnj 	int argc;
356454Swnj 	char **argv;
366454Swnj {
376454Swnj 	int f;
386454Swnj 	struct sockaddr_in from;
398380Ssam 	struct servent *sp;
406454Swnj 
418380Ssam 	sp = getservbyname("shell", "tcp");
428380Ssam 	if (sp == 0) {
438380Ssam 		fprintf(stderr, "rshd: tcp/shell: unknown service\n");
448380Ssam 		exit(1);
458380Ssam 	}
466454Swnj #ifndef DEBUG
476454Swnj 	if (fork())
486454Swnj 		exit(0);
496454Swnj 	for (f = 0; f < 10; f++)
506454Swnj 		(void) close(f);
516454Swnj 	(void) open("/", 0);
526454Swnj 	(void) dup2(0, 1);
536454Swnj 	(void) dup2(0, 2);
546454Swnj 	{ int t = open("/dev/tty", 2);
556454Swnj 	  if (t >= 0) {
566454Swnj 		ioctl(t, TIOCNOTTY, (char *)0);
576454Swnj 		(void) close(t);
586454Swnj 	  }
596454Swnj 	}
606454Swnj #endif
619967Ssam 	sin.sin_port = sp->s_port;
626454Swnj 	argc--, argv++;
639212Ssam 	if (argc > 0 && !strcmp(argv[0], "-d")) {
6410273Ssam 		options |= SO_DEBUG;
6510273Ssam 		argc--, argv++;
6610273Ssam 	}
6710273Ssam 	if (argc > 0) {
688380Ssam 		int port = atoi(argv[0]);
698380Ssam 
708380Ssam 		if (port < 0) {
718380Ssam 			fprintf(stderr, "%s: bad port #\n", argv[0]);
728380Ssam 			exit(1);
738380Ssam 		}
749967Ssam 		sin.sin_port = htons((u_short)port);
758380Ssam 		argc--, argv++;
768380Ssam 	}
779260Ssam 	f = socket(AF_INET, SOCK_STREAM, 0, 0);
789212Ssam 	if (f < 0) {
799212Ssam 		perror("rshd: socket");
809212Ssam 		exit(1);
819212Ssam 	}
8210273Ssam 	if (options & SO_DEBUG && setsockopt(f, SOL_SOCKET, SO_DEBUG, 0, 0) < 0)
8310273Ssam 		perror("rshd: setsockopt (SO_DEBUG)");
8410273Ssam #ifdef notdef
8510273Ssam 	if (setsockopt(f, SOL_SOCKET, SO_KEEPALIVE, 0, 0) < 0)
8610273Ssam 		perror("rshd: setsockopt (SO_KEEPALIVE)");
8710273Ssam #endif
889212Ssam 	if (bind(f, (caddr_t)&sin, sizeof (sin), 0) < 0) {
899212Ssam 		perror("rshd: bind");
909212Ssam 		exit(1);
919212Ssam 	}
9210586Ssam 	sigset(SIGCHLD, reapchild);
939212Ssam 	listen(f, 10);
946454Swnj 	for (;;) {
959212Ssam 		int g, len = sizeof (from);
969212Ssam 
979212Ssam 		g = accept(f, &from, &len, 0);
989212Ssam 		if (g < 0) {
9910416Ssam 			if (errno == EINTR)
10010416Ssam 				continue;
10110273Ssam 			perror("rshd: accept");
1026454Swnj 			continue;
1036454Swnj 		}
1049212Ssam 		if (fork() == 0) {
1059212Ssam 			close(f);
1069212Ssam 			doit(g, &from);
1079212Ssam 		}
1089212Ssam 		close(g);
1096454Swnj 	}
1106454Swnj }
1116454Swnj 
11210416Ssam reapchild()
11310416Ssam {
11410416Ssam 	union wait status;
11510416Ssam 
11610416Ssam 	while (wait3(&status, WNOHANG, 0) > 0)
11710416Ssam 		;
11810416Ssam }
11910416Ssam 
1206454Swnj char	username[20] = "USER=";
1216454Swnj char	homedir[64] = "HOME=";
1226454Swnj char	shell[64] = "SHELL=";
1236454Swnj char	*envinit[] =
1246454Swnj 	    {homedir, shell, "PATH=:/usr/ucb:/bin:/usr/bin", username, 0};
1256454Swnj char	**environ;
1266454Swnj 
1276454Swnj doit(f, fromp)
1286454Swnj 	int f;
1296454Swnj 	struct sockaddr_in *fromp;
1306454Swnj {
1316454Swnj 	char cmdbuf[NCARGS+1], *cp;
1326454Swnj 	char locuser[16], remuser[16];
1336454Swnj 	struct passwd *pwd;
134*10596Ssam 	int s, backoff;
1358380Ssam 	struct hostent *hp;
1366454Swnj 	short port;
1376454Swnj 	int pv[2], pid, ready, readfrom, cc;
1386454Swnj 	char buf[BUFSIZ], sig;
1396454Swnj 	int one = 1;
1406454Swnj 
1416454Swnj 	(void) signal(SIGINT, SIG_DFL);
1426454Swnj 	(void) signal(SIGQUIT, SIG_DFL);
1436454Swnj 	(void) signal(SIGTERM, SIG_DFL);
14410273Ssam #ifndef DEBUG
1456454Swnj 	{ int t = open("/dev/tty", 2);
1466454Swnj 	  if (t >= 0) {
1476454Swnj 		ioctl(t, TIOCNOTTY, (char *)0);
1486454Swnj 		(void) close(t);
1496454Swnj 	  }
1506454Swnj 	}
1516454Swnj #endif
1526454Swnj 	fromp->sin_port = ntohs((u_short)fromp->sin_port);
1536454Swnj 	if (fromp->sin_family != AF_INET ||
154*10596Ssam 	    fromp->sin_port >= IPPORT_RESERVED) {
155*10596Ssam 		fprintf(stderr, "rshd: malformed from address\n");
1566454Swnj 		exit(1);
157*10596Ssam 	}
1586454Swnj 	(void) alarm(60);
1596454Swnj 	port = 0;
1606454Swnj 	for (;;) {
1616454Swnj 		char c;
162*10596Ssam 		if (read(f, &c, 1) != 1) {
163*10596Ssam 			int how = 1+1;
164*10596Ssam 
165*10596Ssam 			perror("rshd: read");
166*10596Ssam 			shutdown(f, &how);
1676454Swnj 			exit(1);
168*10596Ssam 		}
1696454Swnj 		if (c == 0)
1706454Swnj 			break;
1716454Swnj 		port = port * 10 + c - '0';
1726454Swnj 	}
1736454Swnj 	(void) alarm(0);
1746454Swnj 	if (port != 0) {
175*10596Ssam 		int lport = IPPORT_RESERVED - 1, retryshift;
17610273Ssam 		s = rresvport(&lport);
177*10596Ssam 		if (s < 0) {
178*10596Ssam 			perror("rshd: can't get stderr port");
1796454Swnj 			exit(1);
180*10596Ssam 		}
181*10596Ssam 		if (port >= IPPORT_RESERVED) {
182*10596Ssam 			fprintf(stderr, "rshd: 2nd port not reserved\n");
183*10596Ssam 			exit(1);
184*10596Ssam 		}
1859243Ssam 		fromp->sin_port = htons((u_short)port);
186*10596Ssam 		for (backoff = 1; backoff != 0; backoff <<= 1) {
187*10596Ssam 			(void) alarm(60);
188*10596Ssam 			if (connect(s, fromp, sizeof (*fromp), 0) >= 0)
189*10596Ssam 				break;
190*10596Ssam 			(void) alarm(0);
191*10596Ssam 			sleep(backoff);
192*10596Ssam 		}
193*10596Ssam 		if (backoff == 0) {
194*10596Ssam 			perror("rshd: connect");
1956454Swnj 			exit(1);
196*10596Ssam 		}
1976454Swnj 	}
198*10596Ssam 	dup2(f, 0);
199*10596Ssam 	dup2(f, 1);
200*10596Ssam 	dup2(f, 2);
2018380Ssam 	hp = gethostbyaddr(&fromp->sin_addr, sizeof (struct in_addr),
2028380Ssam 		fromp->sin_family);
2038380Ssam 	if (hp == 0) {
2046454Swnj 		error("Host name for your address unknown\n");
2056454Swnj 		exit(1);
2066454Swnj 	}
2076454Swnj 	getstr(remuser, sizeof(remuser), "remuser");
2086454Swnj 	getstr(locuser, sizeof(locuser), "locuser");
2096454Swnj 	getstr(cmdbuf, sizeof(cmdbuf), "command");
2106454Swnj 	setpwent();
2116454Swnj 	pwd = getpwnam(locuser);
2126454Swnj 	if (pwd == NULL) {
2136454Swnj 		error("Login incorrect.\n");
2146454Swnj 		exit(1);
2156454Swnj 	}
2166454Swnj 	endpwent();
2176454Swnj 	if (chdir(pwd->pw_dir) < 0) {
2186454Swnj 		error("No remote directory.\n");
2196454Swnj 		exit(1);
2206454Swnj 	}
2218380Ssam 	if (ruserok(hp->h_name, remuser, locuser) < 0) {
2226454Swnj 		error("Permission denied.\n");
2236454Swnj 		exit(1);
2246454Swnj 	}
2256454Swnj 	(void) write(2, "\0", 1);
2266454Swnj 	if (port) {
2276454Swnj 		if (pipe(pv) < 0) {
2286454Swnj 			error("Can't make pipe.\n");
2296454Swnj 			exit(1);
2306454Swnj 		}
2316454Swnj 		pid = fork();
2326454Swnj 		if (pid == -1)  {
2336454Swnj 			error("Try again.\n");
2346454Swnj 			exit(1);
2356454Swnj 		}
2366454Swnj 		if (pid) {
2376454Swnj 			(void) close(0); (void) close(1); (void) close(2);
2386454Swnj 			(void) close(f); (void) close(pv[1]);
2396454Swnj 			readfrom = (1<<s) | (1<<pv[0]);
2406454Swnj 			ioctl(pv[1], FIONBIO, (char *)&one);
2416454Swnj 			/* should set s nbio! */
2426454Swnj 			do {
2436454Swnj 				ready = readfrom;
2449212Ssam 				if (select(16, &ready, 0, 0, 0) < 0)
2459212Ssam 					break;
2466454Swnj 				if (ready & (1<<s)) {
2476454Swnj 					if (read(s, &sig, 1) <= 0)
2486454Swnj 						readfrom &= ~(1<<s);
2496454Swnj 					else
2506454Swnj 						killpg(pid, sig);
2516454Swnj 				}
2526454Swnj 				if (ready & (1<<pv[0])) {
2536454Swnj 					cc = read(pv[0], buf, sizeof (buf));
2546454Swnj 					if (cc <= 0) {
25510194Ssam 						shutdown(s, 1+1);
2566454Swnj 						readfrom &= ~(1<<pv[0]);
2576454Swnj 					} else
2586454Swnj 						(void) write(s, buf, cc);
2596454Swnj 				}
2606454Swnj 			} while (readfrom);
2616454Swnj 			exit(0);
2626454Swnj 		}
2636454Swnj 		setpgrp(0, getpid());
2646454Swnj 		(void) close(s); (void) close(pv[0]);
2656454Swnj 		dup2(pv[1], 2);
2666454Swnj 	}
2676454Swnj 	if (*pwd->pw_shell == '\0')
2686454Swnj 		pwd->pw_shell = "/bin/sh";
2696454Swnj 	(void) close(f);
2709212Ssam 	initgroups(pwd->pw_name, pwd->pw_gid);
2716454Swnj 	(void) setuid(pwd->pw_uid);
2726454Swnj 	(void) setgid(pwd->pw_gid);
2736454Swnj 	environ = envinit;
2746454Swnj 	strncat(homedir, pwd->pw_dir, sizeof(homedir)-6);
2756454Swnj 	strncat(shell, pwd->pw_shell, sizeof(shell)-7);
2766454Swnj 	strncat(username, pwd->pw_name, sizeof(username)-6);
2776454Swnj 	cp = rindex(pwd->pw_shell, '/');
2786454Swnj 	if (cp)
2796454Swnj 		cp++;
2806454Swnj 	else
2816454Swnj 		cp = pwd->pw_shell;
2826454Swnj 	execl(pwd->pw_shell, cp, "-c", cmdbuf, 0);
2836454Swnj 	perror(pwd->pw_shell);
2846454Swnj 	exit(1);
2856454Swnj protofail:
2866454Swnj 	error("rsh: protocol failure detected by remote\n");
2876454Swnj 	exit(1);
2886454Swnj }
2896454Swnj 
2906454Swnj /* VARARGS 1 */
2916454Swnj error(fmt)
2926454Swnj 	char *fmt;
2936454Swnj {
2946454Swnj 	char buf[BUFSIZ];
2956454Swnj 
2966454Swnj 	buf[0] = 1;
2976454Swnj 	(void) sprintf(buf+1, fmt);
2986454Swnj 	(void) write(2, buf, strlen(buf));
2996454Swnj }
3006454Swnj 
3016454Swnj getstr(buf, cnt, err)
3026454Swnj 	char *buf;
3036454Swnj 	int cnt;
3046454Swnj 	char *err;
3056454Swnj {
3066454Swnj 	char c;
3076454Swnj 
3086454Swnj 	do {
3096454Swnj 		if (read(0, &c, 1) != 1)
3106454Swnj 			exit(1);
3116454Swnj 		*buf++ = c;
3126454Swnj 		if (--cnt == 0) {
3136454Swnj 			error("%s too long\n", err);
3146454Swnj 			exit(1);
3156454Swnj 		}
3166454Swnj 	} while (c != 0);
3176454Swnj }
318