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