rexecd.c revision 1.19 1 /* $NetBSD: rexecd.c,v 1.19 2005/01/08 03:14:02 ginsbach 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.19 2005/01/08 03:14:02 ginsbach 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(const char *, ...)
65 __attribute__((__format__(__printf__, 1, 2)));
66 int main(int, char *[]);
67 void doit(int, struct sockaddr *);
68 void getstr(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(int argc, char *argv[])
88 {
89 struct sockaddr_storage from;
90 int fromlen, ch;
91
92 while ((ch = getopt(argc, argv, "l")) != -1)
93 switch (ch) {
94 case 'l':
95 dolog = 1;
96 openlog("rexecd", LOG_PID, LOG_DAEMON);
97 break;
98 default:
99 exit(1);
100 }
101
102 fromlen = sizeof (from);
103 if (getpeername(0, (struct sockaddr *)&from, &fromlen) < 0)
104 err(1, "getpeername");
105
106 doit(0, (struct sockaddr *)&from);
107 exit(0);
108 }
109
110 void
111 doit(int f, struct sockaddr *fromp)
112 {
113 struct pollfd fds[2];
114 char cmdbuf[NCARGS+1], *namep;
115 const char *cp;
116 char user[16], pass[16];
117 char buf[BUFSIZ], sig;
118 struct passwd *pwd;
119 int s = -1; /* XXX gcc */
120 int pv[2], pid, cc;
121 int one = 1;
122 in_port_t port;
123
124 (void)signal(SIGINT, SIG_DFL);
125 (void)signal(SIGQUIT, SIG_DFL);
126 (void)signal(SIGTERM, SIG_DFL);
127 dup2(f, STDIN_FILENO);
128 dup2(f, STDOUT_FILENO);
129 dup2(f, STDERR_FILENO);
130 (void)alarm(60);
131 port = 0;
132 for (;;) {
133 char c;
134 if (read(f, &c, STDIN_FILENO) != 1) {
135 if (dolog)
136 syslog(LOG_ERR,
137 "initial read failed");
138 exit(1);
139 }
140 if (c == 0)
141 break;
142 port = port * 10 + c - '0';
143 }
144 (void)alarm(0);
145 if (port != 0) {
146 s = socket(fromp->sa_family, SOCK_STREAM, 0);
147 if (s < 0) {
148 if (dolog)
149 syslog(LOG_ERR, "socket: %m");
150 exit(1);
151 }
152 (void)alarm(60);
153 switch (fromp->sa_family) {
154 case AF_INET:
155 ((struct sockaddr_in *)fromp)->sin_port = htons(port);
156 break;
157 case AF_INET6:
158 ((struct sockaddr_in6 *)fromp)->sin6_port = htons(port);
159 break;
160 default:
161 syslog(LOG_ERR, "unsupported address family");
162 exit(1);
163 }
164 if (connect(s, (struct sockaddr *)fromp, fromp->sa_len) < 0) {
165 if (dolog)
166 syslog(LOG_ERR, "connect: %m");
167 exit(1);
168 }
169 (void)alarm(0);
170 }
171 getstr(user, sizeof(user), "username");
172 getstr(pass, sizeof(pass), "password");
173 getstr(cmdbuf, sizeof(cmdbuf), "command");
174 setpwent();
175 pwd = getpwnam(user);
176 if (pwd == NULL) {
177 error("Login incorrect.\n");
178 if (dolog)
179 syslog(LOG_ERR, "no such user %s", user);
180 exit(1);
181 }
182 endpwent();
183 if (*pwd->pw_passwd != '\0') {
184 namep = crypt(pass, pwd->pw_passwd);
185 if (strcmp(namep, pwd->pw_passwd)) {
186 error("Password incorrect.\n"); /* XXX: wrong! */
187 if (dolog)
188 syslog(LOG_ERR, "incorrect password for %s",
189 user);
190 exit(1);
191 }
192 } else
193 (void)crypt("dummy password", "PA"); /* must always crypt */
194 if (chdir(pwd->pw_dir) < 0) {
195 error("No remote directory.\n");
196 if (dolog)
197 syslog(LOG_ERR, "%s does not exist for %s", pwd->pw_dir,
198 user);
199 exit(1);
200 }
201 (void)write(STDERR_FILENO, "\0", 1);
202 if (port) {
203 if (pipe(pv) < 0 || (pid = fork()) == -1) {
204 error("Try again.\n");
205 if (dolog)
206 syslog(LOG_ERR,"pipe or fork failed for %s: %m",
207 user);
208 exit(1);
209 }
210 if (pid) {
211 (void)close(STDIN_FILENO);
212 (void)close(STDOUT_FILENO);
213 (void)close(STDERR_FILENO);
214 (void)close(f);
215 (void)close(pv[1]);
216 fds[0].fd = s;
217 fds[1].fd = pv[0];
218 fds[0].events = fds[1].events = POLLIN;
219 if (ioctl(pv[1], FIONBIO, (char *)&one) < 0)
220 _exit(1);
221 /* should set s nbio! */
222 do {
223 if (poll(fds, 2, 0) < 0) {
224 close(s);
225 close(pv[0]);
226 _exit(1);
227 }
228 if (fds[0].revents & POLLIN) {
229 if (read(s, &sig, 1) <= 0)
230 fds[0].events = 0;
231 else
232 killpg(pid, sig);
233 }
234 if (fds[1].revents & POLLIN) {
235 cc = read(pv[0], buf, sizeof (buf));
236 if (cc <= 0) {
237 shutdown(s, 1+1);
238 fds[1].events = 0;
239 } else
240 (void)write(s, buf, cc);
241 }
242 } while ((fds[0].events | fds[1].events) & POLLIN);
243 _exit(0);
244 }
245 (void)close(s);
246 (void)close(pv[0]);
247 if (dup2(pv[1], 2) < 0) {
248 error("Try again.\n");
249 if (dolog)
250 syslog(LOG_ERR, "dup2 failed for %s", user);
251 exit(1);
252 }
253 }
254 if (*pwd->pw_shell == '\0')
255 pwd->pw_shell = _PATH_BSHELL;
256 if (f > 2)
257 (void)close(f);
258 if (setsid() < 0 ||
259 setlogin(pwd->pw_name) < 0 ||
260 initgroups(pwd->pw_name, pwd->pw_gid) < 0 ||
261 setgid((gid_t)pwd->pw_gid) < 0 ||
262 setuid((uid_t)pwd->pw_uid) < 0) {
263 error("Try again.\n");
264 if (dolog)
265 syslog(LOG_ERR, "could not set permissions for %s: %m",
266 user);
267 exit(1);
268 }
269 (void)strlcat(path, _PATH_DEFPATH, sizeof(path));
270 environ = envinit;
271 strlcat(homedir, pwd->pw_dir, sizeof(homedir));
272 strlcat(shell, pwd->pw_shell, sizeof(shell));
273 strlcat(username, pwd->pw_name, sizeof(username));
274 strlcat(logname, pwd->pw_name, sizeof(logname));
275 cp = strrchr(pwd->pw_shell, '/');
276 if (cp)
277 cp++;
278 else
279 cp = pwd->pw_shell;
280 if (dolog)
281 syslog(LOG_INFO, "running command for %s: %s", user, cmdbuf);
282 execl(pwd->pw_shell, cp, "-c", cmdbuf, 0);
283 perror(pwd->pw_shell);
284 if (dolog)
285 syslog(LOG_ERR, "execl failed for %s: %m", user);
286 exit(1);
287 }
288
289 void
290 error(const char *fmt, ...)
291 {
292 char buf[BUFSIZ];
293 va_list ap;
294
295 va_start(ap, fmt);
296 buf[0] = 1;
297 (void)vsnprintf(buf+1, sizeof(buf) - 1, fmt, ap);
298 (void)write(STDERR_FILENO, buf, strlen(buf));
299 va_end(ap);
300 }
301
302 void
303 getstr(char *buf, int cnt, char *err)
304 {
305 char c;
306
307 do {
308 if (read(STDIN_FILENO, &c, 1) != 1)
309 exit(1);
310 *buf++ = c;
311 if (--cnt == 0) {
312 error("%s too long\n", err);
313 exit(1);
314 }
315 } while (c != 0);
316 }
317