xref: /netbsd-src/libexec/rexecd/rexecd.c (revision 23c8222edbfb0f0932d88a8351d3a0cf817dfb9e)
1 /*	$NetBSD: rexecd.c,v 1.18 2004/05/11 08:07:37 kleink Exp $	*/
2 
3 /*
4  * Copyright (c) 1983, 1993
5  *	The Regents of the University of California.  All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  * 3. Neither the name of the University nor the names of its contributors
16  *    may be used to endorse or promote products derived from this software
17  *    without specific prior written permission.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
20  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
23  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29  * SUCH DAMAGE.
30  */
31 
32 #include <sys/cdefs.h>
33 #ifndef lint
34 __COPYRIGHT("@(#) Copyright (c) 1983, 1993\n\
35 	The Regents of the University of California.  All rights reserved.\n");
36 #if 0
37 static char sccsid[] = "from: @(#)rexecd.c	8.1 (Berkeley) 6/4/93";
38 #else
39 __RCSID("$NetBSD: rexecd.c,v 1.18 2004/05/11 08:07:37 kleink Exp $");
40 #endif
41 #endif /* not lint */
42 
43 #include <sys/param.h>
44 #include <sys/ioctl.h>
45 #include <sys/socket.h>
46 #include <sys/syslog.h>
47 #include <sys/time.h>
48 
49 #include <netinet/in.h>
50 
51 #include <err.h>
52 #include <errno.h>
53 #include <netdb.h>
54 #include <paths.h>
55 #include <pwd.h>
56 #include <signal.h>
57 #include <stdarg.h>
58 #include <stdio.h>
59 #include <stdlib.h>
60 #include <string.h>
61 #include <unistd.h>
62 #include <poll.h>
63 
64 void error __P((const char *, ...))
65      __attribute__((__format__(__printf__, 1, 2)));
66 int main __P((int, char **));
67 void doit __P((int, struct sockaddr *));
68 void getstr __P((char *, int, char *));
69 
70 char	username[32 + 1] = "USER=";
71 char	logname[32 + 3 + 1] = "LOGNAME=";
72 char	homedir[PATH_MAX + 1] = "HOME=";
73 char	shell[PATH_MAX + 1] = "SHELL=";
74 char	path[sizeof(_PATH_DEFPATH) + sizeof("PATH=")] = "PATH=";
75 char	*envinit[] = { homedir, shell, path, username, logname, 0 };
76 char	**environ;
77 int	dolog;
78 
79 /*
80  * remote execute server:
81  *	username\0
82  *	password\0
83  *	command\0
84  *	data
85  */
86 int
87 main(argc, argv)
88 	int argc;
89 	char **argv;
90 {
91 	struct sockaddr_storage from;
92 	int fromlen, ch;
93 
94 	while ((ch = getopt(argc, argv, "l")) != -1)
95 		switch (ch) {
96 		case 'l':
97 			dolog = 1;
98 			openlog("rexecd", LOG_PID, LOG_DAEMON);
99 			break;
100 		default:
101 			exit(1);
102 		}
103 
104 	fromlen = sizeof (from);
105 	if (getpeername(0, (struct sockaddr *)&from, &fromlen) < 0)
106 		err(1, "getpeername");
107 
108 	doit(0, (struct sockaddr *)&from);
109 	exit(0);
110 }
111 
112 void
113 doit(f, fromp)
114 	int f;
115 	struct sockaddr *fromp;
116 {
117 	struct pollfd fds[2];
118 	char cmdbuf[NCARGS+1], *namep;
119 	const char *cp;
120 	char user[16], pass[16];
121 	char buf[BUFSIZ], sig;
122 	struct passwd *pwd;
123 	int s = -1; /* XXX gcc */
124 	int pv[2], pid, cc;
125 	int one = 1;
126 	in_port_t port;
127 
128 	(void)signal(SIGINT, SIG_DFL);
129 	(void)signal(SIGQUIT, SIG_DFL);
130 	(void)signal(SIGTERM, SIG_DFL);
131 	dup2(f, 0);
132 	dup2(f, 1);
133 	dup2(f, 2);
134 	(void)alarm(60);
135 	port = 0;
136 	for (;;) {
137 		char c;
138 		if (read(f, &c, 1) != 1) {
139 			if (dolog)
140 				syslog(LOG_ERR,
141 				    "initial read failed");
142 			exit(1);
143 		}
144 		if (c == 0)
145 			break;
146 		port = port * 10 + c - '0';
147 	}
148 	(void)alarm(0);
149 	if (port != 0) {
150 		s = socket(fromp->sa_family, SOCK_STREAM, 0);
151 		if (s < 0) {
152 			if (dolog)
153 				syslog(LOG_ERR, "socket: %m");
154 			exit(1);
155 		}
156 		(void)alarm(60);
157 		switch (fromp->sa_family) {
158 		case AF_INET:
159 			((struct sockaddr_in *)fromp)->sin_port = htons(port);
160 			break;
161 		case AF_INET6:
162 			((struct sockaddr_in6 *)fromp)->sin6_port = htons(port);
163 			break;
164 		default:
165 			syslog(LOG_ERR, "unsupported address family");
166 			exit(1);
167 		}
168 		if (connect(s, (struct sockaddr *)fromp, fromp->sa_len) < 0) {
169 			if (dolog)
170 				syslog(LOG_ERR, "connect: %m");
171 			exit(1);
172 		}
173 		(void)alarm(0);
174 	}
175 	getstr(user, sizeof(user), "username");
176 	getstr(pass, sizeof(pass), "password");
177 	getstr(cmdbuf, sizeof(cmdbuf), "command");
178 	setpwent();
179 	pwd = getpwnam(user);
180 	if (pwd == NULL) {
181 		error("Login incorrect.\n");
182 		if (dolog)
183 			syslog(LOG_ERR, "no such user %s", user);
184 		exit(1);
185 	}
186 	endpwent();
187 	if (*pwd->pw_passwd != '\0') {
188 		namep = crypt(pass, pwd->pw_passwd);
189 		if (strcmp(namep, pwd->pw_passwd)) {
190 			error("Password incorrect.\n");	/* XXX: wrong! */
191 			if (dolog)
192 				syslog(LOG_ERR, "incorrect password for %s",
193 				    user);
194 			exit(1);
195 		}
196 	} else
197 		(void)crypt("dummy password", "PA");	/* must always crypt */
198 	if (chdir(pwd->pw_dir) < 0) {
199 		error("No remote directory.\n");
200 		if (dolog)
201 			syslog(LOG_ERR, "%s does not exist for %s", pwd->pw_dir,
202 			    user);
203 		exit(1);
204 	}
205 	(void)write(2, "\0", 1);
206 	if (port) {
207 		if (pipe(pv) < 0 || (pid = fork()) == -1) {
208 			error("Try again.\n");
209 			if (dolog)
210 				syslog(LOG_ERR,"pipe or fork failed for %s: %m",
211 				    user);
212 			exit(1);
213 		}
214 		if (pid) {
215 			(void)close(0);
216 			(void)close(1);
217 			(void)close(2);
218 			(void)close(f);
219 			(void)close(pv[1]);
220 			fds[0].fd = s;
221 			fds[1].fd = pv[0];
222 			fds[0].events = fds[1].events = POLLIN;
223 			if (ioctl(pv[1], FIONBIO, (char *)&one) < 0)
224 				_exit(1);
225 			/* should set s nbio! */
226 			do {
227 				if (poll(fds, 2, 0) < 0) {
228 					close(s);
229 					close(pv[0]);
230 					_exit(1);
231 				}
232 				if (fds[0].revents & POLLIN) {
233 					if (read(s, &sig, 1) <= 0)
234 						fds[0].events = 0;
235 					else
236 						killpg(pid, sig);
237 				}
238 				if (fds[1].revents & POLLIN) {
239 					cc = read(pv[0], buf, sizeof (buf));
240 					if (cc <= 0) {
241 						shutdown(s, 1+1);
242 						fds[1].events = 0;
243 					} else
244 						(void)write(s, buf, cc);
245 				}
246 			} while ((fds[0].events | fds[1].events) & POLLIN);
247 			_exit(0);
248 		}
249 		(void)close(s);
250 		(void)close(pv[0]);
251 		if (dup2(pv[1], 2) < 0) {
252 			error("Try again.\n");
253 			if (dolog)
254 				syslog(LOG_ERR, "dup2 failed for %s", user);
255 			exit(1);
256 		}
257 	}
258 	if (*pwd->pw_shell == '\0')
259 		pwd->pw_shell = _PATH_BSHELL;
260 	if (f > 2)
261 		(void)close(f);
262 	if (setsid() < 0 ||
263 	    setlogin(pwd->pw_name) < 0 ||
264 	    initgroups(pwd->pw_name, pwd->pw_gid) < 0 ||
265 	    setgid((gid_t)pwd->pw_gid) < 0 ||
266 	    setuid((uid_t)pwd->pw_uid) < 0) {
267 		error("Try again.\n");
268 		if (dolog)
269 			syslog(LOG_ERR, "could not set permissions for %s: %m",
270 			    user);
271 		exit(1);
272 	}
273 	(void)strlcat(path, _PATH_DEFPATH, sizeof(path));
274 	environ = envinit;
275 	strlcat(homedir, pwd->pw_dir, sizeof(homedir));
276 	strlcat(shell, pwd->pw_shell, sizeof(shell));
277 	strlcat(username, pwd->pw_name, sizeof(username));
278 	strlcat(logname, pwd->pw_name, sizeof(logname));
279 	cp = strrchr(pwd->pw_shell, '/');
280 	if (cp)
281 		cp++;
282 	else
283 		cp = pwd->pw_shell;
284 	if (dolog)
285 		syslog(LOG_INFO, "running command for %s: %s", user, cmdbuf);
286 	execl(pwd->pw_shell, cp, "-c", cmdbuf, 0);
287 	perror(pwd->pw_shell);
288 	if (dolog)
289 		syslog(LOG_ERR, "execl failed for %s: %m", user);
290 	exit(1);
291 }
292 
293 void
294 error(const char *fmt, ...)
295 {
296 	char buf[BUFSIZ];
297 	va_list ap;
298 
299 	va_start(ap, fmt);
300 	buf[0] = 1;
301 	(void)vsnprintf(buf+1, sizeof(buf) - 1, fmt, ap);
302 	(void)write(2, buf, strlen(buf));
303 	va_end(ap);
304 }
305 
306 void
307 getstr(buf, cnt, err)
308 	char *buf;
309 	int cnt;
310 	char *err;
311 {
312 	char c;
313 
314 	do {
315 		if (read(0, &c, 1) != 1)
316 			exit(1);
317 		*buf++ = c;
318 		if (--cnt == 0) {
319 			error("%s too long\n", err);
320 			exit(1);
321 		}
322 	} while (c != 0);
323 }
324