1 /* 2 * Copyright (c) 1983, 1990, 1992, 1993 3 * The Regents of the University of California. All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 3. All advertising materials mentioning features or use of this software 14 * must display the following acknowledgement: 15 * This product includes software developed by the University of 16 * California, Berkeley and its contributors. 17 * 4. Neither the name of the University nor the names of its contributors 18 * may be used to endorse or promote products derived from this software 19 * without specific prior written permission. 20 * 21 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 24 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 31 * SUCH DAMAGE. 32 */ 33 34 #include "rcp_locl.h" 35 #include <getarg.h> 36 37 #define RSH_PROGRAM "rsh" 38 39 struct passwd *pwd; 40 uid_t userid; 41 int errs, remin, remout; 42 int pflag, iamremote, iamrecursive, targetshouldbedirectory; 43 int doencrypt, noencrypt; 44 int usebroken, usekrb4, usekrb5, forwardtkt; 45 char *port; 46 int eflag = 0; 47 48 #define CMDNEEDS 64 49 char cmd[CMDNEEDS]; /* must hold "rcp -r -p -d\0" */ 50 51 int response (void); 52 void rsource (char *, struct stat *); 53 void sink (int, char *[]); 54 void source (int, char *[]); 55 void tolocal (int, char *[]); 56 void toremote (char *, int, char *[]); 57 58 int do_cmd(char *host, char *remuser, char *cmd, int *fdin, int *fdout); 59 60 static int fflag, tflag; 61 62 static int version_flag, help_flag; 63 64 struct getargs args[] = { 65 { NULL, '4', arg_flag, &usekrb4, "use Kerberos 4 authentication" }, 66 { NULL, '5', arg_flag, &usekrb5, "use Kerberos 5 authentication" }, 67 { NULL, 'F', arg_flag, &forwardtkt, "forward credentials" }, 68 { NULL, 'K', arg_flag, &usebroken, "use BSD authentication" }, 69 { NULL, 'P', arg_string, &port, "non-default port", "port" }, 70 { NULL, 'p', arg_flag, &pflag, "preserve file permissions" }, 71 { NULL, 'r', arg_flag, &iamrecursive, "recursive mode" }, 72 { NULL, 'x', arg_flag, &doencrypt, "use encryption" }, 73 { NULL, 'z', arg_flag, &noencrypt, "don't encrypt" }, 74 { NULL, 'd', arg_flag, &targetshouldbedirectory }, 75 { NULL, 'e', arg_flag, &eflag, "passed to rsh" }, 76 { NULL, 'f', arg_flag, &fflag }, 77 { NULL, 't', arg_flag, &tflag }, 78 { "version", 0, arg_flag, &version_flag }, 79 { "help", 0, arg_flag, &help_flag } 80 }; 81 82 static void 83 usage (int ret) 84 { 85 arg_printusage (args, 86 sizeof(args) / sizeof(args[0]), 87 NULL, 88 "file1 file2|file... directory"); 89 exit (ret); 90 } 91 92 int 93 main(int argc, char **argv) 94 { 95 char *targ; 96 int optind = 0; 97 98 setprogname(argv[0]); 99 if (getarg (args, sizeof(args) / sizeof(args[0]), argc, argv, 100 &optind)) 101 usage (1); 102 if(help_flag) 103 usage(0); 104 if (version_flag) { 105 print_version (NULL); 106 return 0; 107 } 108 109 iamremote = (fflag || tflag); 110 111 argc -= optind; 112 argv += optind; 113 114 if ((pwd = getpwuid(userid = getuid())) == NULL) 115 errx(1, "unknown user %d", (int)userid); 116 117 remin = STDIN_FILENO; /* XXX */ 118 remout = STDOUT_FILENO; 119 120 if (fflag) { /* Follow "protocol", send data. */ 121 response(); 122 if (setuid(userid) < 0) 123 errx(1, "setuid failed"); 124 source(argc, argv); 125 exit(errs); 126 } 127 128 if (tflag) { /* Receive data. */ 129 if (setuid(userid) < 0) 130 errx(1, "setuid failed"); 131 sink(argc, argv); 132 exit(errs); 133 } 134 135 if (argc < 2) 136 usage(1); 137 if (argc > 2) 138 targetshouldbedirectory = 1; 139 140 remin = remout = -1; 141 /* Command to be executed on remote system using "rsh". */ 142 snprintf(cmd, sizeof(cmd), 143 "rcp%s%s%s", iamrecursive ? " -r" : "", 144 pflag ? " -p" : "", targetshouldbedirectory ? " -d" : ""); 145 146 signal(SIGPIPE, lostconn); 147 148 if ((targ = colon(argv[argc - 1]))) /* Dest is remote host. */ 149 toremote(targ, argc, argv); 150 else { 151 tolocal(argc, argv); /* Dest is local host. */ 152 if (targetshouldbedirectory) 153 verifydir(argv[argc - 1]); 154 } 155 exit(errs); 156 } 157 158 void 159 toremote(char *targ, int argc, char **argv) 160 { 161 int i; 162 char *bp, *host, *src, *suser, *thost, *tuser; 163 164 *targ++ = 0; 165 if (*targ == 0) 166 targ = "."; 167 168 if ((thost = strchr(argv[argc - 1], '@'))) { 169 /* user@host */ 170 *thost++ = 0; 171 tuser = argv[argc - 1]; 172 if (*tuser == '\0') 173 tuser = NULL; 174 else if (!okname(tuser)) 175 exit(1); 176 } else { 177 thost = argv[argc - 1]; 178 tuser = NULL; 179 } 180 181 for (i = 0; i < argc - 1; i++) { 182 src = colon(argv[i]); 183 if (src) { /* remote to remote */ 184 int ret; 185 *src++ = 0; 186 if (*src == 0) 187 src = "."; 188 host = strchr(argv[i], '@'); 189 if (host) { 190 *host++ = '\0'; 191 suser = argv[i]; 192 if (*suser == '\0') 193 suser = pwd->pw_name; 194 else if (!okname(suser)) 195 continue; 196 ret = asprintf(&bp, 197 "%s%s %s -l %s -n %s %s '%s%s%s:%s'", 198 _PATH_RSH, eflag ? " -e" : "", 199 host, suser, cmd, src, 200 tuser ? tuser : "", tuser ? "@" : "", 201 thost, targ); 202 } else { 203 ret = asprintf(&bp, 204 "exec %s%s %s -n %s %s '%s%s%s:%s'", 205 _PATH_RSH, eflag ? " -e" : "", 206 argv[i], cmd, src, 207 tuser ? tuser : "", tuser ? "@" : "", 208 thost, targ); 209 } 210 if (ret == -1) 211 err (1, "malloc"); 212 susystem(bp, userid); 213 free(bp); 214 } else { /* local to remote */ 215 if (remin == -1) { 216 if (asprintf(&bp, "%s -t %s", cmd, targ) == -1) 217 err (1, "malloc"); 218 host = thost; 219 220 if (do_cmd(host, tuser, bp, &remin, &remout) < 0) 221 exit(1); 222 223 if (response() < 0) 224 exit(1); 225 free(bp); 226 if (setuid(userid) < 0) 227 errx(1, "setuid failed"); 228 } 229 source(1, argv+i); 230 } 231 } 232 } 233 234 void 235 tolocal(int argc, char **argv) 236 { 237 int i; 238 char *bp, *host, *src, *suser; 239 240 for (i = 0; i < argc - 1; i++) { 241 int ret; 242 243 if (!(src = colon(argv[i]))) { /* Local to local. */ 244 ret = asprintf(&bp, "exec %s%s%s %s %s", _PATH_CP, 245 iamrecursive ? " -PR" : "", pflag ? " -p" : "", 246 argv[i], argv[argc - 1]); 247 if (ret == -1) 248 err (1, "malloc"); 249 if (susystem(bp, userid)) 250 ++errs; 251 free(bp); 252 continue; 253 } 254 *src++ = 0; 255 if (*src == 0) 256 src = "."; 257 if ((host = strchr(argv[i], '@')) == NULL) { 258 host = argv[i]; 259 suser = pwd->pw_name; 260 } else { 261 *host++ = 0; 262 suser = argv[i]; 263 if (*suser == '\0') 264 suser = pwd->pw_name; 265 else if (!okname(suser)) 266 continue; 267 } 268 ret = asprintf(&bp, "%s -f %s", cmd, src); 269 if (ret == -1) 270 err (1, "malloc"); 271 if (do_cmd(host, suser, bp, &remin, &remout) < 0) { 272 free(bp); 273 ++errs; 274 continue; 275 } 276 free(bp); 277 sink(1, argv + argc - 1); 278 if (seteuid(0) < 0) 279 exit(1); 280 close(remin); 281 remin = remout = -1; 282 } 283 } 284 285 void 286 source(int argc, char **argv) 287 { 288 struct stat stb; 289 static BUF buffer; 290 BUF *bp; 291 off_t i; 292 int amt, fd, haderr, indx, result; 293 char *last, *name, buf[BUFSIZ]; 294 295 for (indx = 0; indx < argc; ++indx) { 296 name = argv[indx]; 297 if ((fd = open(name, O_RDONLY, 0)) < 0) 298 goto syserr; 299 if (fstat(fd, &stb)) { 300 syserr: run_err("%s: %s", name, strerror(errno)); 301 goto next; 302 } 303 switch (stb.st_mode & S_IFMT) { 304 case S_IFREG: 305 break; 306 case S_IFDIR: 307 if (iamrecursive) { 308 rsource(name, &stb); 309 goto next; 310 } 311 /* FALLTHROUGH */ 312 default: 313 run_err("%s: not a regular file", name); 314 goto next; 315 } 316 if ((last = strrchr(name, '/')) == NULL) 317 last = name; 318 else 319 ++last; 320 if (pflag) { 321 /* 322 * Make it compatible with possible future 323 * versions expecting microseconds. 324 */ 325 snprintf(buf, sizeof(buf), "T%ld 0 %ld 0\n", 326 (long)stb.st_mtime, 327 (long)stb.st_atime); 328 write(remout, buf, strlen(buf)); 329 if (response() < 0) 330 goto next; 331 } 332 #undef MODEMASK 333 #define MODEMASK (S_ISUID|S_ISGID|S_ISVTX|S_IRWXU|S_IRWXG|S_IRWXO) 334 snprintf(buf, sizeof(buf), "C%04o %lu %s\n", 335 stb.st_mode & MODEMASK, 336 (unsigned long)stb.st_size, 337 last); 338 write(remout, buf, strlen(buf)); 339 if (response() < 0) 340 goto next; 341 if ((bp = allocbuf(&buffer, fd, BUFSIZ)) == NULL) { 342 next: close(fd); 343 continue; 344 } 345 346 /* Keep writing after an error so that we stay sync'd up. */ 347 for (haderr = i = 0; i < stb.st_size; i += bp->cnt) { 348 amt = bp->cnt; 349 if (i + amt > stb.st_size) 350 amt = stb.st_size - i; 351 if (!haderr) { 352 result = read(fd, bp->buf, amt); 353 if (result != amt) 354 haderr = result >= 0 ? EIO : errno; 355 } 356 if (haderr) 357 write(remout, bp->buf, amt); 358 else { 359 result = write(remout, bp->buf, amt); 360 if (result != amt) 361 haderr = result >= 0 ? EIO : errno; 362 } 363 } 364 if (close(fd) && !haderr) 365 haderr = errno; 366 if (!haderr) 367 write(remout, "", 1); 368 else 369 run_err("%s: %s", name, strerror(haderr)); 370 response(); 371 } 372 } 373 374 void 375 rsource(char *name, struct stat *statp) 376 { 377 DIR *dirp; 378 struct dirent *dp; 379 char *last, *vect[1], path[MAXPATHLEN]; 380 381 if (!(dirp = opendir(name))) { 382 run_err("%s: %s", name, strerror(errno)); 383 return; 384 } 385 last = strrchr(name, '/'); 386 if (last == 0) 387 last = name; 388 else 389 last++; 390 if (pflag) { 391 snprintf(path, sizeof(path), "T%ld 0 %ld 0\n", 392 (long)statp->st_mtime, 393 (long)statp->st_atime); 394 write(remout, path, strlen(path)); 395 if (response() < 0) { 396 closedir(dirp); 397 return; 398 } 399 } 400 snprintf(path, sizeof(path), 401 "D%04o %d %s\n", statp->st_mode & MODEMASK, 0, last); 402 write(remout, path, strlen(path)); 403 if (response() < 0) { 404 closedir(dirp); 405 return; 406 } 407 while ((dp = readdir(dirp))) { 408 if (dp->d_ino == 0) 409 continue; 410 if (!strcmp(dp->d_name, ".") || !strcmp(dp->d_name, "..")) 411 continue; 412 if (strlen(name) + 1 + strlen(dp->d_name) >= MAXPATHLEN - 1) { 413 run_err("%s/%s: name too long", name, dp->d_name); 414 continue; 415 } 416 snprintf(path, sizeof(path), "%s/%s", name, dp->d_name); 417 vect[0] = path; 418 source(1, vect); 419 } 420 closedir(dirp); 421 write(remout, "E\n", 2); 422 response(); 423 } 424 425 void 426 sink(int argc, char **argv) 427 { 428 static BUF buffer; 429 struct stat stb; 430 struct timeval tv[2]; 431 enum { YES, NO, DISPLAYED } wrerr; 432 BUF *bp; 433 off_t i, j, size; 434 int amt, count, exists, first, mask, mode, ofd, omode; 435 int setimes, targisdir, wrerrno = 0; 436 char ch, *cp, *np, *targ, *why, *vect[1], buf[BUFSIZ]; 437 438 #define atime tv[0] 439 #define mtime tv[1] 440 #define SCREWUP(str) { why = str; goto screwup; } 441 442 setimes = targisdir = 0; 443 mask = umask(0); 444 if (!pflag) 445 umask(mask); 446 if (argc != 1) { 447 run_err("ambiguous target"); 448 exit(1); 449 } 450 targ = *argv; 451 if (targetshouldbedirectory) 452 verifydir(targ); 453 write(remout, "", 1); 454 if (stat(targ, &stb) == 0 && S_ISDIR(stb.st_mode)) 455 targisdir = 1; 456 for (first = 1;; first = 0) { 457 cp = buf; 458 if (read(remin, cp, 1) <= 0) 459 return; 460 if (*cp++ == '\n') 461 SCREWUP("unexpected <newline>"); 462 do { 463 if (read(remin, &ch, sizeof(ch)) != sizeof(ch)) 464 SCREWUP("lost connection"); 465 *cp++ = ch; 466 } while (cp < &buf[BUFSIZ - 1] && ch != '\n'); 467 *cp = 0; 468 469 if (buf[0] == '\01' || buf[0] == '\02') { 470 if (iamremote == 0) 471 write(STDERR_FILENO, 472 buf + 1, strlen(buf + 1)); 473 if (buf[0] == '\02') 474 exit(1); 475 ++errs; 476 continue; 477 } 478 if (buf[0] == 'E') { 479 write(remout, "", 1); 480 return; 481 } 482 483 if (ch == '\n') 484 *--cp = 0; 485 486 cp = buf; 487 if (*cp == 'T') { 488 setimes++; 489 cp++; 490 mtime.tv_sec = strtol(cp, &cp, 10); 491 if (!cp || *cp++ != ' ') 492 SCREWUP("mtime.sec not delimited"); 493 mtime.tv_usec = strtol(cp, &cp, 10); 494 if (!cp || *cp++ != ' ') 495 SCREWUP("mtime.usec not delimited"); 496 atime.tv_sec = strtol(cp, &cp, 10); 497 if (!cp || *cp++ != ' ') 498 SCREWUP("atime.sec not delimited"); 499 atime.tv_usec = strtol(cp, &cp, 10); 500 if (!cp || *cp++ != '\0') 501 SCREWUP("atime.usec not delimited"); 502 write(remout, "", 1); 503 continue; 504 } 505 if (*cp != 'C' && *cp != 'D') { 506 /* 507 * Check for the case "rcp remote:foo\* local:bar". 508 * In this case, the line "No match." can be returned 509 * by the shell before the rcp command on the remote is 510 * executed so the ^Aerror_message convention isn't 511 * followed. 512 */ 513 if (first) { 514 run_err("%s", cp); 515 exit(1); 516 } 517 SCREWUP("expected control record"); 518 } 519 mode = 0; 520 for (++cp; cp < buf + 5; cp++) { 521 if (*cp < '0' || *cp > '7') 522 SCREWUP("bad mode"); 523 mode = (mode << 3) | (*cp - '0'); 524 } 525 if (*cp++ != ' ') 526 SCREWUP("mode not delimited"); 527 528 for (size = 0; isdigit((unsigned char)*cp);) 529 size = size * 10 + (*cp++ - '0'); 530 if (*cp++ != ' ') 531 SCREWUP("size not delimited"); 532 if (targisdir) { 533 static char *namebuf; 534 static int cursize; 535 size_t need; 536 537 need = strlen(targ) + strlen(cp) + 250; 538 if (need > cursize) { 539 if (!(namebuf = malloc(need))) 540 run_err("%s", strerror(errno)); 541 } 542 snprintf(namebuf, need, "%s%s%s", targ, 543 *targ ? "/" : "", cp); 544 np = namebuf; 545 } else 546 np = targ; 547 exists = stat(np, &stb) == 0; 548 if (buf[0] == 'D') { 549 int mod_flag = pflag; 550 if (exists) { 551 if (!S_ISDIR(stb.st_mode)) { 552 errno = ENOTDIR; 553 goto bad; 554 } 555 if (pflag) 556 chmod(np, mode); 557 } else { 558 /* Handle copying from a read-only directory */ 559 mod_flag = 1; 560 if (mkdir(np, mode | S_IRWXU) < 0) 561 goto bad; 562 } 563 vect[0] = np; 564 sink(1, vect); 565 if (setimes) { 566 setimes = 0; 567 if (utimes(np, tv) < 0) 568 run_err("%s: set times: %s", 569 np, strerror(errno)); 570 } 571 if (mod_flag) 572 chmod(np, mode); 573 continue; 574 } 575 omode = mode; 576 mode |= S_IWRITE; 577 if ((ofd = open(np, O_WRONLY|O_CREAT, mode)) < 0) { 578 bad: run_err("%s: %s", np, strerror(errno)); 579 continue; 580 } 581 write(remout, "", 1); 582 if ((bp = allocbuf(&buffer, ofd, BUFSIZ)) == NULL) { 583 close(ofd); 584 continue; 585 } 586 cp = bp->buf; 587 wrerr = NO; 588 for (count = i = 0; i < size; i += BUFSIZ) { 589 amt = BUFSIZ; 590 if (i + amt > size) 591 amt = size - i; 592 count += amt; 593 if((j = net_read(remin, cp, amt)) != amt) { 594 run_err("%s", j ? strerror(errno) : 595 "dropped connection"); 596 exit(1); 597 } 598 amt -= j; 599 cp += j; 600 if (count == bp->cnt) { 601 /* Keep reading so we stay sync'd up. */ 602 if (wrerr == NO) { 603 j = write(ofd, bp->buf, count); 604 if (j != count) { 605 wrerr = YES; 606 wrerrno = j >= 0 ? EIO : errno; 607 } 608 } 609 count = 0; 610 cp = bp->buf; 611 } 612 } 613 if (count != 0 && wrerr == NO && 614 (j = write(ofd, bp->buf, count)) != count) { 615 wrerr = YES; 616 wrerrno = j >= 0 ? EIO : errno; 617 } 618 if (ftruncate(ofd, size)) { 619 run_err("%s: truncate: %s", np, strerror(errno)); 620 wrerr = DISPLAYED; 621 } 622 if (pflag) { 623 if (exists || omode != mode) 624 if (fchmod(ofd, omode)) 625 run_err("%s: set mode: %s", 626 np, strerror(errno)); 627 } else { 628 if (!exists && omode != mode) 629 if (fchmod(ofd, omode & ~mask)) 630 run_err("%s: set mode: %s", 631 np, strerror(errno)); 632 } 633 close(ofd); 634 response(); 635 if (setimes && wrerr == NO) { 636 setimes = 0; 637 if (utimes(np, tv) < 0) { 638 run_err("%s: set times: %s", 639 np, strerror(errno)); 640 wrerr = DISPLAYED; 641 } 642 } 643 switch(wrerr) { 644 case YES: 645 run_err("%s: %s", np, strerror(wrerrno)); 646 break; 647 case NO: 648 write(remout, "", 1); 649 break; 650 case DISPLAYED: 651 break; 652 } 653 } 654 screwup: 655 run_err("protocol error: %s", why); 656 exit(1); 657 } 658 659 int 660 response(void) 661 { 662 char ch, *cp, resp, rbuf[BUFSIZ]; 663 664 if (read(remin, &resp, sizeof(resp)) != sizeof(resp)) 665 lostconn(0); 666 667 cp = rbuf; 668 switch(resp) { 669 case 0: /* ok */ 670 return (0); 671 default: 672 *cp++ = resp; 673 /* FALLTHROUGH */ 674 case 1: /* error, followed by error msg */ 675 case 2: /* fatal error, "" */ 676 do { 677 if (read(remin, &ch, sizeof(ch)) != sizeof(ch)) 678 lostconn(0); 679 *cp++ = ch; 680 } while (cp < &rbuf[BUFSIZ] && ch != '\n'); 681 682 if (!iamremote) 683 write(STDERR_FILENO, rbuf, cp - rbuf); 684 ++errs; 685 if (resp == 1) 686 return (-1); 687 exit(1); 688 } 689 /* NOTREACHED */ 690 } 691 692 #include <stdarg.h> 693 694 void 695 run_err(const char *fmt, ...) 696 { 697 static FILE *fp; 698 va_list ap; 699 700 ++errs; 701 if (fp == NULL && !(fp = fdopen(remout, "w"))) 702 return; 703 va_start(ap, fmt); 704 fprintf(fp, "%c", 0x01); 705 fprintf(fp, "rcp: "); 706 vfprintf(fp, fmt, ap); 707 fprintf(fp, "\n"); 708 fflush(fp); 709 va_end(ap); 710 711 if (!iamremote) { 712 va_start(ap, fmt); 713 vwarnx(fmt, ap); 714 va_end(ap); 715 } 716 } 717 718 /* 719 * This function executes the given command as the specified user on the 720 * given host. This returns < 0 if execution fails, and >= 0 otherwise. This 721 * assigns the input and output file descriptors on success. 722 * 723 * If it cannot create necessary pipes it exits with error message. 724 */ 725 726 int 727 do_cmd(char *host, char *remuser, char *cmd, int *fdin, int *fdout) 728 { 729 int pin[2], pout[2], reserved[2]; 730 731 /* 732 * Reserve two descriptors so that the real pipes won't get 733 * descriptors 0 and 1 because that will screw up dup2 below. 734 */ 735 pipe(reserved); 736 737 /* Create a socket pair for communicating with rsh. */ 738 if (pipe(pin) < 0) { 739 perror("pipe"); 740 exit(255); 741 } 742 if (pipe(pout) < 0) { 743 perror("pipe"); 744 exit(255); 745 } 746 747 /* Free the reserved descriptors. */ 748 close(reserved[0]); 749 close(reserved[1]); 750 751 /* For a child to execute the command on the remote host using rsh. */ 752 if (fork() == 0) { 753 char *args[100]; 754 unsigned int i; 755 756 /* Child. */ 757 close(pin[1]); 758 close(pout[0]); 759 dup2(pin[0], 0); 760 dup2(pout[1], 1); 761 close(pin[0]); 762 close(pout[1]); 763 764 i = 0; 765 args[i++] = RSH_PROGRAM; 766 if (usekrb4) 767 args[i++] = "-4"; 768 if (usekrb5) 769 args[i++] = "-5"; 770 if (usebroken) 771 args[i++] = "-K"; 772 if (doencrypt) 773 args[i++] = "-x"; 774 if (forwardtkt) 775 args[i++] = "-F"; 776 if (noencrypt) 777 args[i++] = "-z"; 778 if (port != NULL) { 779 args[i++] = "-p"; 780 args[i++] = port; 781 } 782 if (eflag) 783 args[i++] = "-e"; 784 if (remuser != NULL) { 785 args[i++] = "-l"; 786 args[i++] = remuser; 787 } 788 args[i++] = host; 789 args[i++] = cmd; 790 args[i++] = NULL; 791 792 execvp(RSH_PROGRAM, args); 793 perror(RSH_PROGRAM); 794 exit(1); 795 } 796 /* Parent. Close the other side, and return the local side. */ 797 close(pin[0]); 798 *fdout = pin[1]; 799 close(pout[1]); 800 *fdin = pout[0]; 801 return 0; 802 } 803