xref: /openbsd-src/lib/libc/net/rcmdsh.c (revision dcbdbe15d55f1f30a7e526fb6417dee87960fe4d)
1 /*	$OpenBSD: rcmdsh.c,v 1.1 1996/08/22 20:11:20 millert Exp $	*/
2 
3 /*
4  * This is an rcmd() replacement originally by
5  * Chris Siebenmann <cks@utcc.utoronto.ca>.
6  */
7 
8 #if defined(LIBC_SCCS) && !defined(lint)
9 static char *rcsid = "$OpenBSD: rcmdsh.c,v 1.1 1996/08/22 20:11:20 millert Exp $";
10 #endif /* LIBC_SCCS and not lint */
11 
12 #include      <sys/types.h>
13 #include      <sys/socket.h>
14 #include      <sys/wait.h>
15 #include      <signal.h>
16 #include      <errno.h>
17 #include      <netdb.h>
18 #include      <stdio.h>
19 #include      <string.h>
20 #include      <pwd.h>
21 #include      <paths.h>
22 
23 /*
24  * This is a replacement rcmd() function that uses the rsh(1)
25  * program in place of a direct rcmd(3) function call so as to
26  * avoid having to be root.  Note that rport is ignored.
27  */
28 int
29 rcmdsh(ahost, rport, locuser, remuser, cmd, rshprog)
30 	char **ahost;
31 	u_short	rport;
32 	char *locuser, *remuser, *cmd;
33 	char *rshprog;
34 {
35 	struct hostent *hp;
36 	int cpid, sp[2];
37 	char *p;
38 	struct passwd *pw;
39 
40 	/* What rsh/shell to use. */
41 	if (rshprog == NULL)
42 		rshprog = _PATH_RSH;
43 
44 	/* locuser must exist on this host. */
45 	if ((pw = getpwnam(locuser)) == NULL) {
46 		(void) fprintf(stderr, "rcmdsh: unknown user: %s\n", locuser);
47 		return(-1);
48 	}
49 
50 	/* Validate remote hostname. */
51 	if ((hp = gethostbyname(*ahost)) == NULL) {
52 		herror(*ahost);
53 		return(-1);
54 	}
55 	*ahost = hp->h_name;
56 
57 	/* Get a socketpair we'll use for stdin and stdout. */
58 	if (socketpair(AF_UNIX, SOCK_STREAM, 0, sp) < 0) {
59 		perror("rcmdsh: socketpair");
60 		return(-1);
61 	}
62 
63 	cpid = fork();
64 	if (cpid < 0) {
65 		perror("rcmdsh: fork failed");
66 		return(-1);
67 	} else if (cpid == 0) {
68 		/*
69 		 * Child.  We use sp[1] to be stdin/stdout, and close sp[0].
70 		 */
71 		(void) close(sp[0]);
72 		if (dup2(sp[1], 0) < 0 || dup2(0, 1) < 0 || dup2(0, 2) < 0) {
73 			perror("rcmdsh: dup2 failed");
74 			_exit(255);
75 		}
76 		/* Fork again to lose parent. */
77 		cpid = fork();
78 		if (cpid < 0) {
79 			perror("rcmdsh: fork to lose parent failed");
80 			_exit(255);
81 		}
82 		if (cpid > 0)
83 			_exit(0);
84 
85 		/* In grandchild here.  Become local user for rshprog. */
86 		if (setuid(pw->pw_uid)) {
87 			(void) fprintf(stderr, "rcmdsh: setuid(%u): %s\n",
88 				       pw->pw_uid, strerror(errno));
89 			_exit(255);
90 		}
91 
92 		/*
93 		 * If remote host is "localhost" and local and remote user
94 		 * are the same, avoid running remote shell for efficiency.
95 		 */
96 		if (!strcmp(*ahost, "localhost") && !strcmp(locuser, remuser)) {
97 			if (pw->pw_shell[0] == '\0')
98 				rshprog = _PATH_BSHELL;
99 			else
100 				rshprog = pw->pw_shell;
101 			p = strrchr(rshprog, '/');
102 			execlp(rshprog, p ? p+1 : rshprog, "-c", cmd,
103 			       (char *) NULL);
104 		} else {
105 			p = strrchr(rshprog, '/');
106 			execlp(rshprog, p ? p+1 : rshprog, *ahost, "-l",
107 			       remuser, cmd, (char *) NULL);
108 		}
109 		(void) fprintf(stderr, "rcmdsh: execlp %s failed: %s\n",
110 			       rshprog, strerror(errno));
111 		_exit(255);
112 	} else if (cpid > 0) {
113 		/* Parent. close sp[1], return sp[0]. */
114 		(void) close(sp[1]);
115 		/* Reap child. */
116 		(void) wait(NULL);
117 		return(sp[0]);
118 	}
119 	/*NOTREACHED*/
120 }
121