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