1/* $NetBSD: rsh.c,v 1.39 2021/04/13 06:25:49 mrg 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 40static char sccsid[] = "@(#)rsh.c 8.4 (Berkeley) 4/29/95"; 41#else 42__RCSID("$NetBSD: rsh.c,v 1.39 2021/04/13 06:25:49 mrg 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 */ 74int remerr; 75 76static int sigs[] = { SIGINT, SIGTERM, SIGQUIT }; 77 78static char *copyargs(char **); 79static void sendsig(int); 80static int checkfd(struct pollfd *, int); 81static void talk(int, sigset_t *, pid_t, int); 82__dead static void usage(void); 83#ifdef IN_RCMD 84int orcmd(char **, int, const char *, 85 const char *, const char *, int *); 86int orcmd_af(char **, int, const char *, 87 const char *, const char *, int *, int); 88static int relay_signal; 89#endif 90 91int 92main(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 302static int 303checkfd(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 335static void 336talk(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 368rewrite: 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 } 390done: 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 439static void 440sendsig(int sig) 441{ 442 char signo; 443 444 signo = sig; 445 (void)write(remerr, &signo, 1); 446} 447 448static char * 449copyargs(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 (cc == 0) 458 usage(); 459 if (!(args = malloc((u_int)cc))) 460 err(1, "malloc"); 461 ep = args + cc; 462 for (p = args, *p = '\0', ap = argv; *ap; ++ap) { 463 (void)strlcpy(p, *ap, ep - p); 464 p += strlen(p); 465 if (ap[1]) 466 *p++ = ' '; 467 } 468 *p = '\0'; 469 return (args); 470} 471 472static void 473usage(void) 474{ 475 476 (void)fprintf(stderr, 477 "usage: %s [-46dn] [-l login] [-p port]%s [login@]host command\n", 478 getprogname(), 479#ifdef IN_RCMD 480 " [-u locuser]" 481#else 482 "" 483#endif 484 ); 485 exit(1); 486} 487