1 | /* $NetBSD: rsh.c,v 1.38 2014/11/26 23:44:21 enami Exp $ */ |
2 | |
3 | /*- |
4 | * Copyright (c) 1983, 1990, 1993, 1994 |
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, 1990, 1993, 1994\ |
35 | The Regents of the University of California. All rights reserved." ); |
36 | #endif /* not lint */ |
37 | |
38 | #ifndef lint |
39 | #if 0 |
40 | static char sccsid[] = "@(#)rsh.c 8.4 (Berkeley) 4/29/95" ; |
41 | #else |
42 | __RCSID("$NetBSD: rsh.c,v 1.38 2014/11/26 23:44:21 enami Exp $" ); |
43 | #endif |
44 | #endif /* not lint */ |
45 | |
46 | #include <sys/types.h> |
47 | #include <sys/socket.h> |
48 | #include <sys/ioctl.h> |
49 | #include <sys/file.h> |
50 | #include <poll.h> |
51 | |
52 | #include <netinet/in.h> |
53 | #include <netinet/tcp.h> |
54 | #include <netdb.h> |
55 | |
56 | #include <err.h> |
57 | #include <errno.h> |
58 | #include <limits.h> |
59 | #include <pwd.h> |
60 | #include <signal.h> |
61 | #include <stdarg.h> |
62 | #include <stdio.h> |
63 | #include <stdlib.h> |
64 | #include <string.h> |
65 | #include <unistd.h> |
66 | |
67 | #include "pathnames.h" |
68 | #include "getport.h" |
69 | |
70 | |
71 | /* |
72 | * rsh - remote shell |
73 | */ |
74 | int remerr; |
75 | |
76 | static int sigs[] = { SIGINT, SIGTERM, SIGQUIT }; |
77 | |
78 | static char *copyargs(char **); |
79 | static void sendsig(int); |
80 | static int checkfd(struct pollfd *, int); |
81 | static void talk(int, sigset_t *, pid_t, int); |
82 | __dead static void usage(void); |
83 | #ifdef IN_RCMD |
84 | int orcmd(char **, int, const char *, |
85 | const char *, const char *, int *); |
86 | int orcmd_af(char **, int, const char *, |
87 | const char *, const char *, int *, int); |
88 | static int relay_signal; |
89 | #endif |
90 | |
91 | int |
92 | main(int argc, char **argv) |
93 | { |
94 | struct passwd *pw; |
95 | struct servent *sp; |
96 | sigset_t oset, nset; |
97 | struct protoent *proto; |
98 | |
99 | #ifdef IN_RCMD |
100 | char *locuser = 0, *loop, *relay; |
101 | #endif /* IN_RCMD */ |
102 | int argoff, asrsh, ch, dflag, nflag, one, rem; |
103 | size_t i; |
104 | int family = AF_UNSPEC; |
105 | pid_t pid; |
106 | uid_t uid; |
107 | char *args, *host, *p, *user, *name; |
108 | |
109 | argoff = asrsh = dflag = nflag = 0; |
110 | one = 1; |
111 | host = user = NULL; |
112 | sp = NULL; |
113 | |
114 | #ifndef IN_RCMD |
115 | /* |
116 | * If called as something other than "rsh" use it as the host name, |
117 | * only for rsh. |
118 | */ |
119 | if (strcmp(getprogname(), "rsh" ) == 0) |
120 | asrsh = 1; |
121 | else { |
122 | host = strdup(getprogname()); |
123 | if (host == NULL) |
124 | err(1, NULL); |
125 | } |
126 | #endif /* IN_RCMD */ |
127 | |
128 | /* handle "rsh host flags" */ |
129 | if (!host && argc > 2 && argv[1][0] != '-') { |
130 | host = argv[1]; |
131 | argoff = 1; |
132 | } |
133 | |
134 | #ifdef IN_RCMD |
135 | if ((relay = getenv("RCMD_RELAY_SIGNAL" )) && strcmp(relay, "YES" ) == 0) |
136 | relay_signal = 1; |
137 | if ((loop = getenv("RCMD_LOOP" )) && strcmp(loop, "YES" ) == 0) |
138 | warnx("rcmd appears to be looping!" ); |
139 | |
140 | setenv("RCMD_LOOP" , "YES" , 1); |
141 | |
142 | # define OPTIONS "468KLdel:np:u:w" |
143 | |
144 | #else /* IN_RCMD */ |
145 | |
146 | # define OPTIONS "468KLdel:np:w" |
147 | |
148 | #endif /* IN_RCMD */ |
149 | |
150 | if (!(pw = getpwuid(uid = getuid()))) |
151 | errx(1, "unknown user id" ); |
152 | |
153 | if ((name = strdup(pw->pw_name)) == NULL) |
154 | err(1, "malloc" ); |
155 | while ((ch = getopt(argc - argoff, argv + argoff, OPTIONS)) != -1) |
156 | switch (ch) { |
157 | case '4': |
158 | family = AF_INET; |
159 | break; |
160 | case '6': |
161 | family = AF_INET6; |
162 | break; |
163 | case 'K': |
164 | break; |
165 | case 'L': /* -8Lew are ignored to allow rlogin aliases */ |
166 | case 'e': |
167 | case 'w': |
168 | case '8': |
169 | break; |
170 | case 'd': |
171 | dflag = 1; |
172 | break; |
173 | case 'l': |
174 | user = optarg; |
175 | break; |
176 | case 'n': |
177 | nflag = 1; |
178 | break; |
179 | case 'p': |
180 | sp = getport(optarg, "tcp" ); |
181 | break; |
182 | #ifdef IN_RCMD |
183 | case 'u': |
184 | if (getuid() != 0 && optarg && name && |
185 | strcmp(name, optarg) != 0) |
186 | errx(1,"only super user can use the -u option" ); |
187 | locuser = optarg; |
188 | break; |
189 | #endif /* IN_RCMD */ |
190 | case '?': |
191 | default: |
192 | usage(); |
193 | } |
194 | optind += argoff; |
195 | |
196 | /* if haven't gotten a host yet, do so */ |
197 | if (!host && !(host = argv[optind++])) |
198 | usage(); |
199 | |
200 | /* if no further arguments, must have been called as rlogin. */ |
201 | if (!argv[optind]) { |
202 | #ifdef IN_RCMD |
203 | usage(); |
204 | #else |
205 | if (asrsh) |
206 | *argv = __UNCONST("rlogin" ); |
207 | setuid(uid); |
208 | execv(_PATH_RLOGIN, argv); |
209 | err(1, "can't exec %s" , _PATH_RLOGIN); |
210 | #endif |
211 | } |
212 | |
213 | argc -= optind; |
214 | argv += optind; |
215 | |
216 | /* Accept user1@host format, though "-l user2" overrides user1 */ |
217 | p = strchr(host, '@'); |
218 | if (p) { |
219 | *p = '\0'; |
220 | if (!user && p > host) |
221 | user = host; |
222 | host = p + 1; |
223 | if (*host == '\0') |
224 | usage(); |
225 | } |
226 | if (!user) |
227 | user = name; |
228 | |
229 | |
230 | args = copyargs(argv); |
231 | |
232 | if (sp == NULL) |
233 | sp = getservbyname("shell" , "tcp" ); |
234 | if (sp == NULL) |
235 | errx(1, "shell/tcp: unknown service" ); |
236 | |
237 | |
238 | #ifdef IN_RCMD |
239 | rem = orcmd_af(&host, sp->s_port, locuser ? locuser : |
240 | #else |
241 | rem = rcmd_af(&host, sp->s_port, |
242 | #endif |
243 | name, user, args, &remerr, family); |
244 | (void)free(name); |
245 | |
246 | if (rem < 0) |
247 | exit(1); |
248 | |
249 | if (remerr < 0) |
250 | errx(1, "can't establish stderr" ); |
251 | if (dflag) { |
252 | if (setsockopt(rem, SOL_SOCKET, SO_DEBUG, &one, |
253 | sizeof(one)) < 0) |
254 | warn("setsockopt remote" ); |
255 | if (setsockopt(remerr, SOL_SOCKET, SO_DEBUG, &one, |
256 | sizeof(one)) < 0) |
257 | warn("setsockopt stderr" ); |
258 | } |
259 | proto = getprotobyname("tcp" ); |
260 | setsockopt(rem, proto->p_proto, TCP_NODELAY, &one, sizeof(one)); |
261 | setsockopt(remerr, proto->p_proto, TCP_NODELAY, &one, sizeof(one)); |
262 | |
263 | |
264 | (void)setuid(uid); |
265 | |
266 | (void)sigemptyset(&nset); |
267 | for (i = 0; i < sizeof(sigs) / sizeof(sigs[0]); i++) |
268 | (void)sigaddset(&nset, sigs[i]); |
269 | |
270 | (void)sigprocmask(SIG_BLOCK, &nset, &oset); |
271 | |
272 | #ifdef IN_RCMD |
273 | if (!relay_signal) |
274 | #endif |
275 | for (i = 0; i < sizeof(sigs) / sizeof(sigs[0]); i++) { |
276 | struct sigaction sa; |
277 | |
278 | if (sa.sa_handler != SIG_IGN) { |
279 | sa.sa_handler = sendsig; |
280 | (void)sigaction(sigs[i], &sa, NULL); |
281 | } |
282 | } |
283 | |
284 | if (!nflag) { |
285 | pid = fork(); |
286 | if (pid < 0) |
287 | err(1, "fork" ); |
288 | } |
289 | else |
290 | pid = -1; |
291 | |
292 | (void)ioctl(remerr, FIONBIO, &one); |
293 | (void)ioctl(rem, FIONBIO, &one); |
294 | |
295 | talk(nflag, &oset, pid, rem); |
296 | |
297 | if (!nflag) |
298 | (void)kill(pid, SIGKILL); |
299 | exit(0); |
300 | } |
301 | |
302 | static int |
303 | checkfd(struct pollfd *fdp, int outfd) |
304 | { |
305 | int nr, nw; |
306 | char buf[BUFSIZ]; |
307 | |
308 | if (fdp->revents & (POLLNVAL|POLLERR|POLLHUP)) |
309 | return -1; |
310 | |
311 | if ((fdp->revents & POLLIN) == 0) |
312 | return 0; |
313 | |
314 | errno = 0; |
315 | nr = read(fdp->fd, buf, sizeof buf); |
316 | |
317 | if (nr <= 0) { |
318 | if (errno != EAGAIN) |
319 | return -1; |
320 | else |
321 | return 0; |
322 | } |
323 | else { |
324 | char *bc = buf; |
325 | while (nr) { |
326 | if ((nw = write(outfd, bc, nr)) <= 0) |
327 | return -1; |
328 | nr -= nw; |
329 | bc += nw; |
330 | } |
331 | return 0; |
332 | } |
333 | } |
334 | |
335 | static void |
336 | talk(int nflag, sigset_t *oset, __pid_t pid, int rem) |
337 | { |
338 | int nr, nw, nfds; |
339 | struct pollfd fds[3], *fdp = &fds[0]; |
340 | char *bp, buf[BUFSIZ]; |
341 | |
342 | if (!nflag && pid == 0) { |
343 | (void)close(remerr); |
344 | |
345 | fdp->events = POLLOUT|POLLNVAL|POLLERR|POLLHUP; |
346 | fdp->fd = rem; |
347 | nr = 0; |
348 | bp = buf; |
349 | |
350 | for (;;) { |
351 | errno = 0; |
352 | |
353 | if (nr == 0) { |
354 | if ((nr = read(0, buf, sizeof buf)) == 0) |
355 | goto done; |
356 | if (nr == -1) { |
357 | if (errno == EIO) |
358 | goto done; |
359 | if (errno == EINTR) { |
360 | nr = 0; |
361 | continue; |
362 | } |
363 | err(1, "read" ); |
364 | } |
365 | bp = buf; |
366 | } |
367 | |
368 | rewrite: if (poll(fdp, 1, INFTIM) == -1) { |
369 | if (errno != EINTR) |
370 | err(1, "poll" ); |
371 | goto rewrite; |
372 | } |
373 | |
374 | if (fdp->revents & (POLLNVAL|POLLERR|POLLHUP)) |
375 | err(1, "poll" ); |
376 | |
377 | if ((fdp->revents & POLLOUT) == 0) |
378 | goto rewrite; |
379 | |
380 | nw = write(rem, bp, nr); |
381 | |
382 | if (nw < 0) { |
383 | if (errno == EAGAIN) |
384 | continue; |
385 | err(1, "write" ); |
386 | } |
387 | bp += nw; |
388 | nr -= nw; |
389 | } |
390 | done: |
391 | (void)shutdown(rem, 1); |
392 | exit(0); |
393 | } |
394 | |
395 | fdp = &fds[1]; |
396 | nfds = 2; |
397 | fds[0].events = 0; |
398 | #ifdef IN_RCMD |
399 | if (relay_signal) { |
400 | fdp = &fds[0]; |
401 | nfds = 3; |
402 | fds[0].events = POLLIN|POLLNVAL|POLLERR|POLLHUP; |
403 | fds[0].fd = 2; |
404 | } else |
405 | #endif |
406 | (void)sigprocmask(SIG_SETMASK, oset, NULL); |
407 | fds[1].events = fds[2].events = POLLIN|POLLNVAL|POLLERR|POLLHUP; |
408 | fds[1].fd = remerr; |
409 | fds[2].fd = rem; |
410 | do { |
411 | if (poll(fdp, nfds, INFTIM) == -1) { |
412 | if (errno != EINTR) |
413 | err(1, "poll" ); |
414 | continue; |
415 | } |
416 | if ((fds[1].events != 0 && checkfd(&fds[1], 2) == -1) |
417 | #ifdef IN_RCMD |
418 | || (fds[0].events != 0 && checkfd(&fds[0], remerr) == -1) |
419 | #endif |
420 | ) { |
421 | nfds--; |
422 | fds[1].events = 0; |
423 | #ifdef IN_RCMD |
424 | if (relay_signal) { |
425 | nfds--; |
426 | fds[0].events = 0; |
427 | } |
428 | #endif |
429 | fdp = &fds[2]; |
430 | } |
431 | if (fds[2].events != 0 && checkfd(&fds[2], 1) == -1) { |
432 | nfds--; |
433 | fds[2].events = 0; |
434 | } |
435 | } |
436 | while (nfds); |
437 | } |
438 | |
439 | static void |
440 | sendsig(int sig) |
441 | { |
442 | char signo; |
443 | |
444 | signo = sig; |
445 | (void)write(remerr, &signo, 1); |
446 | } |
447 | |
448 | static char * |
449 | copyargs(char **argv) |
450 | { |
451 | int cc; |
452 | char **ap, *args, *p, *ep; |
453 | |
454 | cc = 0; |
455 | for (ap = argv; *ap; ++ap) |
456 | cc += strlen(*ap) + 1; |
457 | if (!(args = malloc((u_int)cc))) |
458 | err(1, "malloc" ); |
459 | ep = args + cc; |
460 | for (p = args, *p = '\0', ap = argv; *ap; ++ap) { |
461 | (void)strlcpy(p, *ap, ep - p); |
462 | p += strlen(p); |
463 | if (ap[1]) |
464 | *p++ = ' '; |
465 | } |
466 | *p = '\0'; |
467 | return (args); |
468 | } |
469 | |
470 | static void |
471 | usage(void) |
472 | { |
473 | |
474 | (void)fprintf(stderr, |
475 | "usage: %s [-46dn] [-l login] [-p port]%s [login@]host command\n" , |
476 | getprogname(), |
477 | #ifdef IN_RCMD |
478 | " [-u locuser]" |
479 | #else |
480 | "" |
481 | #endif |
482 | ); |
483 | exit(1); |
484 | } |
485 | |