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