1 /*
   2  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
   3  * Use is subject to license terms.
   4  */
   5 /*
   6  * scp - secure remote copy.  This is basically patched BSD rcp which
   7  * uses ssh to do the data transfer (instead of using rcmd).
   8  *
   9  * NOTE: This version should NOT be suid root.  (This uses ssh to
  10  * do the transfer and ssh has the necessary privileges.)
  11  *
  12  * 1995 Timo Rinne <tri@iki.fi>, Tatu Ylonen <ylo@cs.hut.fi>
  13  *
  14  * As far as I am concerned, the code I have written for this software
  15  * can be used freely for any purpose.  Any derived versions of this
  16  * software must be clearly marked as such, and if the derived work is
  17  * incompatible with the protocol description in the RFC file, it must be
  18  * called by a name other than "ssh" or "Secure Shell".
  19  */
  20 /*
  21  * Copyright (c) 1999 Theo de Raadt.  All rights reserved.
  22  * Copyright (c) 1999 Aaron Campbell.  All rights reserved.
  23  *
  24  * Redistribution and use in source and binary forms, with or without
  25  * modification, are permitted provided that the following conditions
  26  * are met:
  27  * 1. Redistributions of source code must retain the above copyright
  28  *    notice, this list of conditions and the following disclaimer.
  29  * 2. Redistributions in binary form must reproduce the above copyright
  30  *    notice, this list of conditions and the following disclaimer in the
  31  *    documentation and/or other materials provided with the distribution.
  32  *
  33  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
  34  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
  35  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
  36  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
  37  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
  38  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
  39  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
  40  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  41  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
  42  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  43  */
  44 
  45 /*
  46  * Parts from:
  47  *
  48  * Copyright (c) 1983, 1990, 1992, 1993, 1995
  49  *      The Regents of the University of California.  All rights reserved.
  50  *
  51  * Redistribution and use in source and binary forms, with or without
  52  * modification, are permitted provided that the following conditions
  53  * are met:
  54  * 1. Redistributions of source code must retain the above copyright
  55  *    notice, this list of conditions and the following disclaimer.
  56  * 2. Redistributions in binary form must reproduce the above copyright
  57  *    notice, this list of conditions and the following disclaimer in the
  58  *    documentation and/or other materials provided with the distribution.
  59  * 3. All advertising materials mentioning features or use of this software
  60  *    must display the following acknowledgement:
  61  *      This product includes software developed by the University of
  62  *      California, Berkeley and its contributors.
  63  * 4. Neither the name of the University nor the names of its contributors
  64  *    may be used to endorse or promote products derived from this software
  65  *    without specific prior written permission.
  66  *
  67  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
  68  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  69  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  70  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
  71  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  72  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
  73  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
  74  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
  75  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
  76  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  77  * SUCH DAMAGE.
  78  *
  79  */
  80 
  81 #include "includes.h"
  82 RCSID("$OpenBSD: scp.c,v 1.91 2002/06/19 00:27:55 deraadt Exp $");
  83 
  84 #include "xmalloc.h"
  85 #include "atomicio.h"
  86 #include "pathnames.h"
  87 #include "log.h"
  88 #include "misc.h"
  89 
  90 #ifdef HAVE___PROGNAME
  91 extern char *__progname;
  92 #else
  93 char *__progname;
  94 #endif
  95 
  96 /* For progressmeter() -- number of seconds before xfer considered "stalled" */
  97 #define STALLTIME       5
  98 /* alarm() interval for updating progress meter */
  99 #define PROGRESSTIME    1
 100 
 101 /* Visual statistics about files as they are transferred. */
 102 void progressmeter(int);
 103 
 104 /* Returns width of the terminal (for progress meter calculations). */
 105 int getttywidth(void);
 106 int do_cmd(char *host, char *remuser, char *cmd, int *fdin, int *fdout,
 107     int argc);
 108 
 109 /* Struct for addargs */
 110 arglist args;
 111 
 112 /* Time a transfer started. */
 113 static struct timeval start;
 114 
 115 /* Number of bytes of current file transferred so far. */
 116 volatile off_t statbytes;
 117 
 118 /* Total size of current file. */
 119 off_t totalbytes = 0;
 120 
 121 /* Name of current file being transferred. */
 122 char *curfile;
 123 
 124 /* This is set to non-zero to enable verbose mode. */
 125 int verbose_mode = 0;
 126 
 127 /* This is set to zero if the progressmeter is not desired. */
 128 int showprogress = 1;
 129 
 130 /* This is the program to execute for the secured connection. ("ssh" or -S) */
 131 char *ssh_program = _PATH_SSH_PROGRAM;
 132 
 133 /* This is used to store the pid of ssh_program */
 134 static pid_t do_cmd_pid = -1;
 135 
 136 static void
 137 killchild(int signo)
 138 {
 139         if (do_cmd_pid > 1) {
 140                 kill(do_cmd_pid, signo ? signo : SIGTERM);
 141                 waitpid(do_cmd_pid, NULL, 0);
 142         }
 143 
 144         if (signo)
 145                 _exit(1);
 146         exit(1);
 147 }
 148 
 149 /*
 150  * Run a command via fork(2)/exec(2). This can be a local-to-local copy via
 151  * cp(1) or one side of a remote-to-remote copy. We must not use system(3) here
 152  * because we don't want filenames to go through a command expansion in the
 153  * underlying shell. Note that the user can create a filename that is a piece of
 154  * shell code itself and this must not be executed.
 155  */
 156 static int
 157 do_local_cmd(arglist *a)
 158 {
 159         uint_t i;
 160         int status;
 161         pid_t pid;
 162 
 163         if (a->num == 0)
 164                 fatal("do_local_cmd: no arguments");
 165 
 166         if (verbose_mode) {
 167                 fprintf(stderr, gettext("Executing:"));
 168                 for (i = 0; i < a->num; i++)
 169                         fprintf(stderr, " %s", a->list[i]);
 170                 fprintf(stderr, "\n");
 171         }
 172         if ((pid = fork()) == -1)
 173                 fatal("do_local_cmd: fork: %s", strerror(errno));
 174 
 175         if (pid == 0) {
 176                 execvp(a->list[0], a->list);
 177                 perror(a->list[0]);
 178                 exit(1);
 179         }
 180 
 181         do_cmd_pid = pid;
 182         signal(SIGTERM, killchild);
 183         signal(SIGINT, killchild);
 184         signal(SIGHUP, killchild);
 185 
 186         while (waitpid(pid, &status, 0) == -1)
 187                 if (errno != EINTR)
 188                         fatal("do_local_cmd: waitpid: %s", strerror(errno));
 189 
 190         do_cmd_pid = -1;
 191 
 192         if (!WIFEXITED(status) || WEXITSTATUS(status) != 0)
 193                 return (-1);
 194 
 195         return (0);
 196 }
 197 
 198 /*
 199  * This function executes the given command as the specified user on the
 200  * given host.  This returns < 0 if execution fails, and >= 0 otherwise. This
 201  * assigns the input and output file descriptors on success.
 202  */
 203 
 204 int
 205 do_cmd(char *host, char *remuser, char *cmd, int *fdin, int *fdout, int argc)
 206 {
 207         int pin[2], pout[2], reserved[2];
 208 
 209         if (verbose_mode)
 210                 fprintf(stderr,
 211                         gettext("Executing: program %s host %s, "
 212                                 "user %s, command %s\n"),
 213                         ssh_program, host,
 214                         remuser ? remuser : gettext("(unspecified)"), cmd);
 215 
 216         /*
 217          * Reserve two descriptors so that the real pipes won't get
 218          * descriptors 0 and 1 because that will screw up dup2 below.
 219          */
 220         pipe(reserved);
 221 
 222         /* Create a socket pair for communicating with ssh. */
 223         if (pipe(pin) < 0)
 224                 fatal("pipe: %s", strerror(errno));
 225         if (pipe(pout) < 0)
 226                 fatal("pipe: %s", strerror(errno));
 227 
 228         /* Free the reserved descriptors. */
 229         close(reserved[0]);
 230         close(reserved[1]);
 231 
 232         /* For a child to execute the command on the remote host using ssh. */
 233         if ((do_cmd_pid = fork()) == 0)  {
 234                 /* Child. */
 235                 close(pin[1]);
 236                 close(pout[0]);
 237                 dup2(pin[0], 0);
 238                 dup2(pout[1], 1);
 239                 close(pin[0]);
 240                 close(pout[1]);
 241 
 242                 args.list[0] = ssh_program;
 243                 if (remuser != NULL)
 244                         addargs(&args, "-l%s", remuser);
 245                 addargs(&args, "%s", host);
 246                 addargs(&args, "%s", cmd);
 247 
 248                 execvp(ssh_program, args.list);
 249                 perror(ssh_program);
 250                 exit(1);
 251         } else if (do_cmd_pid == (pid_t)-1) {
 252                 /* fork() failed */
 253                 fatal("fork: %s", strerror(errno));
 254         }
 255 
 256         /* Parent.  Close the other side, and return the local side. */
 257         close(pin[0]);
 258         *fdout = pin[1];
 259         close(pout[1]);
 260         *fdin = pout[0];
 261         return (0);
 262 }
 263 
 264 typedef struct {
 265         int cnt;
 266         char *buf;
 267 } BUF;
 268 
 269 BUF *allocbuf(BUF *, int, int);
 270 void lostconn(int);
 271 void nospace(void);
 272 int okname(char *);
 273 void run_err(const char *, ...);
 274 void verifydir(char *);
 275 
 276 struct passwd *pwd;
 277 uid_t userid;
 278 int errs, remin, remout;
 279 int pflag, iamremote, iamrecursive, targetshouldbedirectory;
 280 
 281 #define CMDNEEDS        64
 282 char cmd[CMDNEEDS];             /* must hold "rcp -r -p -d\0" */
 283 
 284 int response(void);
 285 void rsource(char *, struct stat *);
 286 void sink(int, char *[]);
 287 void source(int, char *[]);
 288 void tolocal(int, char *[]);
 289 void toremote(char *, int, char *[]);
 290 void usage(void);
 291 
 292 int
 293 main(argc, argv)
 294         int argc;
 295         char *argv[];
 296 {
 297         int ch, fflag, tflag, status;
 298         char *targ;
 299         extern char *optarg;
 300         extern int optind;
 301 
 302         __progname = get_progname(argv[0]);
 303 
 304         g11n_setlocale(LC_ALL, "");
 305 
 306         args.list = NULL;
 307         addargs(&args, "ssh");              /* overwritten with ssh_program */
 308         addargs(&args, "-x");
 309         addargs(&args, "-oForwardAgent no");
 310         addargs(&args, "-oClearAllForwardings yes");
 311 
 312         fflag = tflag = 0;
 313         while ((ch = getopt(argc, argv, "dfprtvBCc:i:P:q46S:o:F:")) != -1)
 314                 switch (ch) {
 315                 /* User-visible flags. */
 316                 case '4':
 317                 case '6':
 318                 case 'C':
 319                         addargs(&args, "-%c", ch);
 320                         break;
 321                 case 'o':
 322                 case 'c':
 323                 case 'i':
 324                 case 'F':
 325                         addargs(&args, "-%c%s", ch, optarg);
 326                         break;
 327                 case 'P':
 328                         addargs(&args, "-p%s", optarg);
 329                         break;
 330                 case 'B':
 331                         addargs(&args, "-oBatchmode yes");
 332                         break;
 333                 case 'p':
 334                         pflag = 1;
 335                         break;
 336                 case 'r':
 337                         iamrecursive = 1;
 338                         break;
 339                 case 'S':
 340                         ssh_program = xstrdup(optarg);
 341                         break;
 342                 case 'v':
 343                         addargs(&args, "-v");
 344                         verbose_mode = 1;
 345                         break;
 346                 case 'q':
 347                         showprogress = 0;
 348                         break;
 349 
 350                 /* Server options. */
 351                 case 'd':
 352                         targetshouldbedirectory = 1;
 353                         break;
 354                 case 'f':       /* "from" */
 355                         iamremote = 1;
 356                         fflag = 1;
 357                         break;
 358                 case 't':       /* "to" */
 359                         iamremote = 1;
 360                         tflag = 1;
 361 #ifdef HAVE_CYGWIN
 362                         setmode(0, O_BINARY);
 363 #endif
 364                         break;
 365                 default:
 366                         usage();
 367                 }
 368         argc -= optind;
 369         argv += optind;
 370 
 371         if ((pwd = getpwuid(userid = getuid())) == NULL)
 372                 fatal("unknown user %d", (int)userid);
 373 
 374         if (!isatty(STDERR_FILENO))
 375                 showprogress = 0;
 376 
 377         remin = STDIN_FILENO;
 378         remout = STDOUT_FILENO;
 379 
 380         if (fflag) {
 381                 /* Follow "protocol", send data. */
 382                 (void) response();
 383                 source(argc, argv);
 384                 exit(errs != 0);
 385         }
 386         if (tflag) {
 387                 /* Receive data. */
 388                 sink(argc, argv);
 389                 exit(errs != 0);
 390         }
 391         if (argc < 2)
 392                 usage();
 393         if (argc > 2)
 394                 targetshouldbedirectory = 1;
 395 
 396         remin = remout = -1;
 397         do_cmd_pid = (pid_t)-1;
 398 
 399         /* Command to be executed on remote system using "ssh". */
 400         (void) snprintf(cmd, sizeof (cmd), "scp%s%s%s%s",
 401             verbose_mode ? " -v" : "",
 402             iamrecursive ? " -r" : "", pflag ? " -p" : "",
 403             targetshouldbedirectory ? " -d" : "");
 404 
 405         (void) signal(SIGPIPE, lostconn);
 406 
 407         if ((targ = colon(argv[argc - 1])))     /* Dest is remote host. */
 408                 toremote(targ, argc, argv);
 409         else {
 410                 if (targetshouldbedirectory)
 411                         verifydir(argv[argc - 1]);
 412                 tolocal(argc, argv);    /* Dest is local host. */
 413         }
 414         /*
 415          * Finally check the exit status of the ssh process, if one was forked
 416          * and no error has occurred yet
 417          */
 418         if (do_cmd_pid != (pid_t)-1 && errs == 0) {
 419                 if (remin != -1) {
 420                         (void) close(remin);
 421                 }
 422                 if (remout != -1) {
 423                         (void) close(remout);
 424                 }
 425                 if (waitpid(do_cmd_pid, &status, 0) == -1) {
 426                         errs = 1;
 427                 } else if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) {
 428                         errs = 1;
 429                 }
 430         }
 431 
 432         return (errs != 0);
 433 }
 434 
 435 void
 436 toremote(targ, argc, argv)
 437         char *targ, *argv[];
 438         int argc;
 439 {
 440         int i, len;
 441         char *bp, *host, *src, *suser, *thost, *tuser, *arg;
 442         arglist alist;
 443 
 444         memset(&alist, '\0', sizeof (alist));
 445         alist.list = NULL;
 446 
 447         *targ++ = 0;
 448         if (*targ == 0)
 449                 targ = ".";
 450 
 451         arg = xstrdup(argv[argc - 1]);
 452         if ((thost = strchr(arg, '@'))) {
 453                 /* user@host */
 454                 *thost++ = 0;
 455                 tuser = arg;
 456                 if (*tuser == '\0')
 457                         tuser = NULL;
 458                 else if (!okname(tuser))
 459                         exit(1);
 460         } else {
 461                 thost = arg;
 462                 tuser = NULL;
 463         }
 464 
 465         if (tuser != NULL && !okname(tuser)) {
 466                 xfree(arg);
 467                 return;
 468         }
 469 
 470         for (i = 0; i < argc - 1; i++) {
 471                 src = colon(argv[i]);
 472                 if (src) {      /* remote to remote */
 473                         freeargs(&alist);
 474                         addargs(&alist, "%s", ssh_program);
 475                         if (verbose_mode)
 476                                 addargs(&alist, "-v");
 477                         addargs(&alist, "-x");
 478                         addargs(&alist, "-oClearAllForwardings yes");
 479                         addargs(&alist, "-n");
 480 
 481                         *src++ = 0;
 482                         if (*src == 0)
 483                                 src = ".";
 484                         host = strchr(argv[i], '@');
 485 
 486                         if (host) {
 487                                 *host++ = 0;
 488                                 host = cleanhostname(host);
 489                                 suser = argv[i];
 490                                 if (*suser == '\0')
 491                                         suser = pwd->pw_name;
 492                                 else if (!okname(suser))
 493                                         continue;
 494                                 addargs(&alist, "-l");
 495                                 addargs(&alist, "%s", suser);
 496                         } else {
 497                                 host = cleanhostname(argv[i]);
 498                         }
 499                         addargs(&alist, "%s", host);
 500                         addargs(&alist, "%s", cmd);
 501                         addargs(&alist, "%s", src);
 502                         addargs(&alist, "%s%s%s:%s",
 503                                     tuser ? tuser : "", tuser ? "@" : "",
 504                                     thost, targ);
 505                         if (do_local_cmd(&alist) != 0)
 506                                 errs = 1;
 507                 } else {        /* local to remote */
 508                         if (remin == -1) {
 509                                 len = strlen(targ) + CMDNEEDS + 20;
 510                                 bp = xmalloc(len);
 511                                 (void) snprintf(bp, len, "%s -t %s", cmd, targ);
 512                                 host = cleanhostname(thost);
 513                                 if (do_cmd(host, tuser, bp, &remin,
 514                                     &remout, argc) < 0)
 515                                         exit(1);
 516                                 if (response() < 0)
 517                                         exit(1);
 518                                 (void) xfree(bp);
 519                         }
 520                         source(1, argv + i);
 521                 }
 522         }
 523 }
 524 
 525 void
 526 tolocal(argc, argv)
 527         int argc;
 528         char *argv[];
 529 {
 530         int i, len;
 531         char *bp, *host, *src, *suser;
 532         arglist alist;
 533 
 534         memset(&alist, '\0', sizeof (alist));
 535         alist.list = NULL;
 536 
 537         for (i = 0; i < argc - 1; i++) {
 538                 if (!(src = colon(argv[i]))) {  /* Local to local. */
 539                         freeargs(&alist);
 540                         addargs(&alist, "%s", _PATH_CP);
 541                         if (iamrecursive)
 542                                 addargs(&alist, "-r");
 543                         if (pflag)
 544                                 addargs(&alist, "-p");
 545                         addargs(&alist, "%s", argv[i]);
 546                         addargs(&alist, "%s", argv[argc-1]);
 547                         if (do_local_cmd(&alist))
 548                                 ++errs;
 549                         continue;
 550                 }
 551                 *src++ = 0;
 552                 if (*src == 0)
 553                         src = ".";
 554                 if ((host = strchr(argv[i], '@')) == NULL) {
 555                         host = argv[i];
 556                         suser = NULL;
 557                 } else {
 558                         *host++ = 0;
 559                         suser = argv[i];
 560                         if (*suser == '\0')
 561                                 suser = pwd->pw_name;
 562                         else if (!okname(suser))
 563                                 continue;
 564                 }
 565                 host = cleanhostname(host);
 566                 len = strlen(src) + CMDNEEDS + 20;
 567                 bp = xmalloc(len);
 568                 (void) snprintf(bp, len, "%s -f %s", cmd, src);
 569                 if (do_cmd(host, suser, bp, &remin, &remout, argc) < 0) {
 570                         (void) xfree(bp);
 571                         ++errs;
 572                         continue;
 573                 }
 574                 xfree(bp);
 575                 sink(1, argv + argc - 1);
 576                 (void) close(remin);
 577                 remin = remout = -1;
 578         }
 579 }
 580 
 581 void
 582 source(argc, argv)
 583         int argc;
 584         char *argv[];
 585 {
 586         struct stat stb;
 587         static BUF buffer;
 588         BUF *bp;
 589         off_t i, amt, result;
 590         int fd, haderr, indx;
 591         char *last, *name, buf[2048];
 592         int len;
 593 
 594         for (indx = 0; indx < argc; ++indx) {
 595                 name = argv[indx];
 596                 statbytes = 0;
 597                 len = strlen(name);
 598                 while (len > 1 && name[len-1] == '/')
 599                         name[--len] = '\0';
 600                 if (strchr(name, '\n') != NULL) {
 601                         run_err("%s: skipping, filename contains a newline",
 602                             name);
 603                         goto next;
 604                 }
 605                 if ((fd = open(name, O_RDONLY, 0)) < 0)
 606                         goto syserr;
 607                 if (fstat(fd, &stb) < 0) {
 608 syserr:                 run_err("%s: %s", name, strerror(errno));
 609                         goto next;
 610                 }
 611                 switch (stb.st_mode & S_IFMT) {
 612                 case S_IFREG:
 613                         break;
 614                 case S_IFDIR:
 615                         if (iamrecursive) {
 616                                 rsource(name, &stb);
 617                                 goto next;
 618                         }
 619                         /* FALLTHROUGH */
 620                 default:
 621                         run_err("%s: not a regular file", name);
 622                         goto next;
 623                 }
 624                 if ((last = strrchr(name, '/')) == NULL)
 625                         last = name;
 626                 else
 627                         ++last;
 628                 curfile = last;
 629                 if (pflag) {
 630                         /*
 631                          * Make it compatible with possible future
 632                          * versions expecting microseconds.
 633                          */
 634                         (void) snprintf(buf, sizeof (buf), "T%lu 0 %lu 0\n",
 635                             (ulong_t)stb.st_mtime,
 636                             (ulong_t)stb.st_atime);
 637                         (void) atomicio(write, remout, buf, strlen(buf));
 638                         if (response() < 0)
 639                                 goto next;
 640                 }
 641 #define FILEMODEMASK    (S_ISUID|S_ISGID|S_IRWXU|S_IRWXG|S_IRWXO)
 642 #ifdef HAVE_LONG_LONG_INT
 643                 snprintf(buf, sizeof (buf), "C%04o %lld %s\n",
 644                     (uint_t)(stb.st_mode & FILEMODEMASK),
 645                     (long long)stb.st_size, last);
 646 #else
 647                 /* XXX: Handle integer overflow? */
 648                 snprintf(buf, sizeof (buf), "C%04o %lu %s\n",
 649                     (uint_t)(stb.st_mode & FILEMODEMASK),
 650                     (ulong_t)stb.st_size, last);
 651 #endif
 652                 if (verbose_mode) {
 653                         fprintf(stderr, gettext("Sending file modes: %s"), buf);
 654                         fflush(stderr);
 655                 }
 656                 (void) atomicio(write, remout, buf, strlen(buf));
 657                 if (response() < 0)
 658                         goto next;
 659                 if ((bp = allocbuf(&buffer, fd, 2048)) == NULL) {
 660 next:                   (void) close(fd);
 661                         continue;
 662                 }
 663                 if (showprogress) {
 664                         totalbytes = stb.st_size;
 665                         progressmeter(-1);
 666                 }
 667                 /* Keep writing after an error so that we stay sync'd up. */
 668                 for (haderr = i = 0; i < stb.st_size; i += bp->cnt) {
 669                         amt = bp->cnt;
 670                         if (i + amt > stb.st_size)
 671                                 amt = stb.st_size - i;
 672                         if (!haderr) {
 673                                 result = atomicio(read, fd, bp->buf, amt);
 674                                 if (result != amt)
 675                                         haderr = result >= 0 ? EIO : errno;
 676                         }
 677                         if (haderr)
 678                                 (void) atomicio(write, remout, bp->buf, amt);
 679                         else {
 680                                 result = atomicio(write, remout, bp->buf, amt);
 681                                 if (result != amt)
 682                                         haderr = result >= 0 ? EIO : errno;
 683                                 statbytes += result;
 684                         }
 685                 }
 686                 if (showprogress)
 687                         progressmeter(1);
 688 
 689                 if (close(fd) < 0 && !haderr)
 690                         haderr = errno;
 691                 if (!haderr)
 692                         (void) atomicio(write, remout, "", 1);
 693                 else
 694                         run_err("%s: %s", name, strerror(haderr));
 695                 (void) response();
 696         }
 697 }
 698 
 699 void
 700 rsource(name, statp)
 701         char *name;
 702         struct stat *statp;
 703 {
 704         DIR *dirp;
 705         struct dirent *dp;
 706         char *last, *vect[1], path[1100];
 707 
 708         if (!(dirp = opendir(name))) {
 709                 run_err("%s: %s", name, strerror(errno));
 710                 return;
 711         }
 712         last = strrchr(name, '/');
 713         if (last == 0)
 714                 last = name;
 715         else
 716                 last++;
 717         if (pflag) {
 718                 (void) snprintf(path, sizeof (path), "T%lu 0 %lu 0\n",
 719                     (ulong_t)statp->st_mtime,
 720                     (ulong_t)statp->st_atime);
 721                 (void) atomicio(write, remout, path, strlen(path));
 722                 if (response() < 0) {
 723                         closedir(dirp);
 724                         return;
 725                 }
 726         }
 727         (void) snprintf(path, sizeof (path), "D%04o %d %.1024s\n",
 728             (uint_t)(statp->st_mode & FILEMODEMASK), 0, last);
 729         if (verbose_mode)
 730                 fprintf(stderr, gettext("Entering directory: %s"), path);
 731         (void) atomicio(write, remout, path, strlen(path));
 732         if (response() < 0) {
 733                 closedir(dirp);
 734                 return;
 735         }
 736         while ((dp = readdir(dirp)) != NULL) {
 737                 if (dp->d_ino == 0)
 738                         continue;
 739                 if ((strcmp(dp->d_name, ".") == 0) ||
 740                     (strcmp(dp->d_name, "..") == 0))
 741                         continue;
 742                 if (strlen(name) + 1 + strlen(dp->d_name) >=
 743                     sizeof (path) - 1) {
 744                         run_err("%s/%s: name too long", name, dp->d_name);
 745                         continue;
 746                 }
 747                 (void) snprintf(path, sizeof (path), "%s/%s", name, dp->d_name);
 748                 vect[0] = path;
 749                 source(1, vect);
 750         }
 751         (void) closedir(dirp);
 752         (void) atomicio(write, remout, "E\n", 2);
 753         (void) response();
 754 }
 755 
 756 void
 757 sink(argc, argv)
 758         int argc;
 759         char *argv[];
 760 {
 761         static BUF buffer;
 762         struct stat stb;
 763         enum {
 764                 YES, NO, DISPLAYED
 765         } wrerr;
 766         BUF *bp;
 767         off_t i, j;
 768         int amt, count, exists, first, mask, mode, ofd, omode;
 769         off_t size;
 770         int setimes, targisdir, wrerrno = 0;
 771         char ch, *cp, *np, *targ, *why, *vect[1], buf[2048];
 772         struct timeval tv[2];
 773 
 774 #define atime   tv[0]
 775 #define mtime   tv[1]
 776 #define SCREWUP(str)    { why = str; goto screwup; }
 777 
 778         setimes = targisdir = 0;
 779         mask = umask(0);
 780         if (!pflag)
 781                 (void) umask(mask);
 782         if (argc != 1) {
 783                 run_err("ambiguous target");
 784                 exit(1);
 785         }
 786         targ = *argv;
 787         if (targetshouldbedirectory)
 788                 verifydir(targ);
 789 
 790         (void) atomicio(write, remout, "", 1);
 791         if (stat(targ, &stb) == 0 && S_ISDIR(stb.st_mode))
 792                 targisdir = 1;
 793         for (first = 1; ; first = 0) {
 794                 cp = buf;
 795                 if (atomicio(read, remin, cp, 1) <= 0)
 796                         return;
 797                 if (*cp++ == '\n')
 798                         SCREWUP("unexpected <newline>")
 799                 do {
 800                         if (atomicio(read, remin, &ch, sizeof (ch)) !=
 801                             sizeof (ch))
 802                                 SCREWUP("lost connection")
 803                         *cp++ = ch;
 804                 } while (cp < &buf[sizeof (buf) - 1] && ch != '\n');
 805                 *cp = 0;
 806 
 807                 if (buf[0] == '\01' || buf[0] == '\02') {
 808                         if (iamremote == 0)
 809                                 (void) atomicio(write, STDERR_FILENO,
 810                                     buf + 1, strlen(buf + 1));
 811                         if (buf[0] == '\02')
 812                                 exit(1);
 813                         ++errs;
 814                         continue;
 815                 }
 816                 if (buf[0] == 'E') {
 817                         (void) atomicio(write, remout, "", 1);
 818                         return;
 819                 }
 820                 if (ch == '\n')
 821                         *--cp = 0;
 822 
 823                 cp = buf;
 824                 if (*cp == 'T') {
 825                         setimes++;
 826                         cp++;
 827                         mtime.tv_sec = strtol(cp, &cp, 10);
 828                         if (!cp || *cp++ != ' ')
 829                                 SCREWUP("mtime.sec not delimited")
 830                         mtime.tv_usec = strtol(cp, &cp, 10);
 831                         if (!cp || *cp++ != ' ')
 832                                 SCREWUP("mtime.usec not delimited")
 833                         atime.tv_sec = strtol(cp, &cp, 10);
 834                         if (!cp || *cp++ != ' ')
 835                                 SCREWUP("atime.sec not delimited")
 836                         atime.tv_usec = strtol(cp, &cp, 10);
 837                         if (!cp || *cp++ != '\0')
 838                                 SCREWUP("atime.usec not delimited")
 839                         (void) atomicio(write, remout, "", 1);
 840                         continue;
 841                 }
 842                 if (*cp != 'C' && *cp != 'D') {
 843                         /*
 844                          * Check for the case "rcp remote:foo\* local:bar".
 845                          * In this case, the line "No match." can be returned
 846                          * by the shell before the rcp command on the remote is
 847                          * executed so the ^Aerror_message convention isn't
 848                          * followed.
 849                          */
 850                         if (first) {
 851                                 run_err("%s", cp);
 852                                 exit(1);
 853                         }
 854                         SCREWUP("expected control record")
 855                 }
 856                 mode = 0;
 857                 for (++cp; cp < buf + 5; cp++) {
 858                         if (*cp < '0' || *cp > '7')
 859                                 SCREWUP("bad mode")
 860                         mode = (mode << 3) | (*cp - '0');
 861                 }
 862                 if (*cp++ != ' ')
 863                         SCREWUP("mode not delimited")
 864 
 865                 for (size = 0; isdigit(*cp); )
 866                         size = size * 10 + (*cp++ - '0');
 867                 if (*cp++ != ' ')
 868                         SCREWUP("size not delimited")
 869                 if ((strchr(cp, '/') != NULL) || (strcmp(cp, "..") == 0)) {
 870                         run_err("error: unexpected filename: %s", cp);
 871                         exit(1);
 872                 }
 873                 if (targisdir) {
 874                         static char *namebuf;
 875                         static int cursize;
 876                         size_t need;
 877 
 878                         need = strlen(targ) + strlen(cp) + 250;
 879                         if (need > cursize) {
 880                                 if (namebuf)
 881                                         xfree(namebuf);
 882                                 namebuf = xmalloc(need);
 883                                 cursize = need;
 884                         }
 885                         (void) snprintf(namebuf, need, "%s%s%s", targ,
 886                             strcmp(targ, "/") ? "/" : "", cp);
 887                         np = namebuf;
 888                 } else
 889                         np = targ;
 890                 curfile = cp;
 891                 exists = stat(np, &stb) == 0;
 892                 if (buf[0] == 'D') {
 893                         int mod_flag = pflag;
 894                         if (!iamrecursive)
 895                                 SCREWUP("received directory without -r");
 896                         if (exists) {
 897                                 if (!S_ISDIR(stb.st_mode)) {
 898                                         errno = ENOTDIR;
 899                                         goto bad;
 900                                 }
 901                                 if (pflag)
 902                                         (void) chmod(np, mode);
 903                         } else {
 904                                 /*
 905                                  * Handle copying from a read-only
 906                                  * directory
 907                                  */
 908                                 mod_flag = 1;
 909                                 if (mkdir(np, mode | S_IRWXU) < 0)
 910                                         goto bad;
 911                         }
 912                         vect[0] = xstrdup(np);
 913                         sink(1, vect);
 914                         if (setimes) {
 915                                 setimes = 0;
 916                                 if (utimes(vect[0], tv) < 0)
 917                                         run_err("%s: set times: %s",
 918                                             vect[0], strerror(errno));
 919                         }
 920                         if (mod_flag)
 921                                 (void) chmod(vect[0], mode);
 922                         if (vect[0])
 923                                 xfree(vect[0]);
 924                         continue;
 925                 }
 926                 omode = mode;
 927                 mode |= S_IWRITE;
 928                 if ((ofd = open(np, O_WRONLY|O_CREAT, mode)) < 0) {
 929 bad:                    run_err("%s: %s", np, strerror(errno));
 930                         continue;
 931                 }
 932                 (void) atomicio(write, remout, "", 1);
 933                 if ((bp = allocbuf(&buffer, ofd, 4096)) == NULL) {
 934                         (void) close(ofd);
 935                         continue;
 936                 }
 937                 wrerr = NO;
 938 
 939                 if (showprogress) {
 940                         totalbytes = size;
 941                         progressmeter(-1);
 942                 }
 943                 statbytes = 0;
 944                 for (i = 0; i < size; i += bp->cnt) {
 945                         amt = bp->cnt;
 946                         cp = bp->buf;
 947                         if (i + amt > size)
 948                                 amt = size - i;
 949                         count = amt;
 950                         do {
 951                                 j = read(remin, cp, amt);
 952                                 if (j == -1 && (errno == EINTR ||
 953                                     errno == EAGAIN)) {
 954                                         continue;
 955                                 } else if (j <= 0) {
 956                                         run_err("%s", j ? strerror(errno) :
 957                                             "dropped connection");
 958                                         exit(1);
 959                                 }
 960                                 amt -= j;
 961                                 cp += j;
 962                                 statbytes += j;
 963                         } while (amt > 0);
 964                         /* Keep reading so we stay sync'd up. */
 965                         if (wrerr == NO) {
 966                                 j = atomicio(write, ofd, bp->buf,
 967                                     count);
 968                                 if (j != count) {
 969                                         wrerr = YES;
 970                                         wrerrno = j >= 0 ? EIO : errno;
 971                                 }
 972                         }
 973                 }
 974                 if (showprogress)
 975                         progressmeter(1);
 976                 if (ftruncate(ofd, size)) {
 977                         run_err("%s: truncate: %s", np, strerror(errno));
 978                         wrerr = DISPLAYED;
 979                 }
 980                 if (pflag) {
 981                         if (exists || omode != mode)
 982 #ifdef HAVE_FCHMOD
 983                                 if (fchmod(ofd, omode)) {
 984 #else /* HAVE_FCHMOD */
 985                                 if (chmod(np, omode)) {
 986 #endif /* HAVE_FCHMOD */
 987                                         run_err("%s: set mode: %s",
 988                                             np, strerror(errno));
 989                                         wrerr = DISPLAYED;
 990                                 }
 991                 } else {
 992                         if (!exists && omode != mode)
 993 #ifdef HAVE_FCHMOD
 994                                 if (fchmod(ofd, omode & ~mask)) {
 995 #else /* HAVE_FCHMOD */
 996                                 if (chmod(np, omode & ~mask)) {
 997 #endif /* HAVE_FCHMOD */
 998                                         run_err("%s: set mode: %s",
 999                                             np, strerror(errno));
1000                                         wrerr = DISPLAYED;
1001                                 }
1002                 }
1003                 if (close(ofd) == -1) {
1004                         wrerr = YES;
1005                         wrerrno = errno;
1006                 }
1007                 (void) response();
1008                 if (setimes && wrerr == NO) {
1009                         setimes = 0;
1010                         if (utimes(np, tv) < 0) {
1011                                 run_err("%s: set times: %s",
1012                                     np, strerror(errno));
1013                                 wrerr = DISPLAYED;
1014                         }
1015                 }
1016                 switch (wrerr) {
1017                 case YES:
1018                         run_err("%s: %s", np, strerror(wrerrno));
1019                         break;
1020                 case NO:
1021                         (void) atomicio(write, remout, "", 1);
1022                         break;
1023                 case DISPLAYED:
1024                         break;
1025                 }
1026         }
1027 screwup:
1028         run_err("protocol error: %s", why);
1029         exit(1);
1030 }
1031 
1032 int
1033 response(void)
1034 {
1035         char ch, *cp, resp, rbuf[2048];
1036 
1037         if (atomicio(read, remin, &resp, sizeof (resp)) != sizeof (resp))
1038                 lostconn(0);
1039 
1040         cp = rbuf;
1041         switch (resp) {
1042         case 0:         /* ok */
1043                 return (0);
1044         default:
1045                 *cp++ = resp;
1046                 /* FALLTHROUGH */
1047         case 1:         /* error, followed by error msg */
1048         case 2:         /* fatal error, "" */
1049                 do {
1050                         if (atomicio(read, remin, &ch, sizeof (ch)) !=
1051                             sizeof (ch))
1052                                 lostconn(0);
1053                         *cp++ = ch;
1054                 } while (cp < &rbuf[sizeof (rbuf) - 1] && ch != '\n');
1055 
1056                 if (!iamremote)
1057                         (void) atomicio(write, STDERR_FILENO, rbuf, cp - rbuf);
1058                 ++errs;
1059                 if (resp == 1)
1060                         return (-1);
1061                 exit(1);
1062         }
1063         /* NOTREACHED */
1064 }
1065 
1066 void
1067 usage(void)
1068 {
1069         (void) fprintf(stderr,
1070             gettext(
1071                 "Usage: scp [-pqrvBC46] [-F config] [-S program] [-P port]\n"
1072                 "           [-c cipher] [-i identity] [-o option]\n"
1073                 "           [[user@]host1:]file1 [...] "
1074                 "[[user@]host2:]file2\n"));
1075         exit(1);
1076 }
1077 
1078 /* PRINTFLIKE1 */
1079 void
1080 run_err(const char *fmt, ...)
1081 {
1082         static FILE *fp;
1083         va_list ap;
1084 
1085         ++errs;
1086 
1087         if (!iamremote) {
1088                 va_start(ap, fmt);
1089                 vfprintf(stderr, fmt, ap);
1090                 va_end(ap);
1091                 fprintf(stderr, "\n");
1092         }
1093 
1094         if (fp == NULL && !(fp = fdopen(remout, "w")))
1095                 return;
1096 
1097         (void) fprintf(fp, "%c", 0x01);
1098         (void) fprintf(fp, "scp: ");
1099         va_start(ap, fmt);
1100         (void) vfprintf(fp, fmt, ap);
1101         va_end(ap);
1102         (void) fprintf(fp, "\n");
1103         (void) fflush(fp);
1104 
1105 }
1106 
1107 void
1108 verifydir(cp)
1109         char *cp;
1110 {
1111         struct stat stb;
1112 
1113         if (!stat(cp, &stb)) {
1114                 if (S_ISDIR(stb.st_mode))
1115                         return;
1116                 errno = ENOTDIR;
1117         }
1118         run_err("%s: %s", cp, strerror(errno));
1119         exit(1);
1120 }
1121 
1122 int
1123 okname(cp0)
1124         char *cp0;
1125 {
1126         int c;
1127         char *cp;
1128 
1129         cp = cp0;
1130         do {
1131                 c = (int)*cp;
1132                 if (c & 0200)
1133                         goto bad;
1134                 if (!isalpha(c) && !isdigit(c)) {
1135                         switch (c) {
1136                                 case '\'':
1137                                 case '"':
1138                                 case '`':
1139                                 case ' ':
1140                                 case '#':
1141                                         goto bad;
1142                                 default:
1143                                         break;
1144                         }
1145                 }
1146         } while (*++cp);
1147         return (1);
1148 
1149 bad:    fprintf(stderr, gettext("%s: invalid user name\n"), cp0);
1150         return (0);
1151 }
1152 
1153 BUF *
1154 allocbuf(bp, fd, blksize)
1155         BUF *bp;
1156         int fd, blksize;
1157 {
1158         size_t size;
1159 #ifdef HAVE_STRUCT_STAT_ST_BLKSIZE
1160         struct stat stb;
1161 
1162         if (fstat(fd, &stb) < 0) {
1163                 run_err("fstat: %s", strerror(errno));
1164                 return (0);
1165         }
1166         if (stb.st_blksize == 0)
1167                 size = blksize;
1168         else
1169                 size = blksize + (stb.st_blksize - blksize % stb.st_blksize) %
1170                     stb.st_blksize;
1171 #else /* HAVE_STRUCT_STAT_ST_BLKSIZE */
1172         size = blksize;
1173 #endif /* HAVE_STRUCT_STAT_ST_BLKSIZE */
1174         if (bp->cnt >= size)
1175                 return (bp);
1176         if (bp->buf == NULL)
1177                 bp->buf = xmalloc(size);
1178         else
1179                 bp->buf = xrealloc(bp->buf, size);
1180         memset(bp->buf, 0, size);
1181         bp->cnt = size;
1182         return (bp);
1183 }
1184 
1185 void
1186 lostconn(signo)
1187         int signo;
1188 {
1189         if (!iamremote)
1190                 write(STDERR_FILENO, "lost connection\n", 16);
1191         if (signo)
1192                 _exit(1);
1193         else
1194                 exit(1);
1195 }
1196 
1197 static void
1198 updateprogressmeter(int ignore)
1199 {
1200         int save_errno = errno;
1201 
1202         progressmeter(0);
1203         signal(SIGALRM, updateprogressmeter);
1204         alarm(PROGRESSTIME);
1205         errno = save_errno;
1206 }
1207 
1208 static int
1209 foregroundproc(void)
1210 {
1211         static pid_t pgrp = -1;
1212         int ctty_pgrp;
1213 
1214         if (pgrp == -1)
1215                 pgrp = getpgrp();
1216 
1217 #ifdef HAVE_TCGETPGRP
1218         return ((ctty_pgrp = tcgetpgrp(STDOUT_FILENO)) != -1 &&
1219                 ctty_pgrp == pgrp);
1220 #else
1221         return ((ioctl(STDOUT_FILENO, TIOCGPGRP, &ctty_pgrp) != -1 &&
1222             ctty_pgrp == pgrp));
1223 #endif
1224 }
1225 
1226 void
1227 progressmeter(int flag)
1228 {
1229         static const char prefixes[] = " KMGTP";
1230         static struct timeval lastupdate;
1231         static off_t lastsize;
1232         struct timeval now, td, wait;
1233         off_t cursize, abbrevsize;
1234         double elapsed;
1235         int ratio, barlength, i, remaining;
1236         char buf[512];
1237 
1238         if (flag == -1) {
1239                 (void) gettimeofday(&start, (struct timezone *)0);
1240                 lastupdate = start;
1241                 lastsize = 0;
1242         }
1243         if (foregroundproc() == 0)
1244                 return;
1245 
1246         (void) gettimeofday(&now, (struct timezone *)0);
1247         cursize = statbytes;
1248         if (totalbytes != 0) {
1249                 ratio = (int)(100.0 * cursize / totalbytes);
1250                 ratio = MAX(ratio, 0);
1251                 ratio = MIN(ratio, 100);
1252         } else
1253                 ratio = 100;
1254 
1255         snprintf(buf, sizeof (buf), "\r%-20.20s %3d%% ", curfile, ratio);
1256 
1257         barlength = getttywidth() - 51;
1258         if (barlength > 0) {
1259                 i = barlength * ratio / 100;
1260                 snprintf(buf + strlen(buf), sizeof (buf) - strlen(buf),
1261                     "|%.*s%*s|", i,
1262                     "*******************************************************"
1263                     "*******************************************************"
1264                     "*******************************************************"
1265                     "*******************************************************"
1266                     "*******************************************************"
1267                     "*******************************************************"
1268                     "*******************************************************",
1269                     barlength - i, "");
1270         }
1271         i = 0;
1272         abbrevsize = cursize;
1273         while (abbrevsize >= 100000 && i < strlen(prefixes) - 1) {
1274                 i++;
1275                 abbrevsize >>= 10;
1276         }
1277         snprintf(buf + strlen(buf), sizeof (buf) - strlen(buf), " %5lu %c%c ",
1278             (unsigned long) abbrevsize, prefixes[i],
1279             prefixes[i] == ' ' ? ' ' : 'B');
1280 
1281         timersub(&now, &lastupdate, &wait);
1282         if (cursize > lastsize) {
1283                 lastupdate = now;
1284                 lastsize = cursize;
1285                 if (wait.tv_sec >= STALLTIME) {
1286                         start.tv_sec += wait.tv_sec;
1287                         start.tv_usec += wait.tv_usec;
1288                 }
1289                 wait.tv_sec = 0;
1290         }
1291         timersub(&now, &start, &td);
1292         elapsed = td.tv_sec + (td.tv_usec / 1000000.0);
1293 
1294         if (flag != 1 &&
1295             (statbytes <= 0 || elapsed <= 0.0 || cursize > totalbytes)) {
1296                 snprintf(buf + strlen(buf), sizeof (buf) - strlen(buf),
1297                     "   --:-- ETA");
1298         } else if (wait.tv_sec >= STALLTIME) {
1299                 snprintf(buf + strlen(buf), sizeof (buf) - strlen(buf),
1300                     " - stalled -");
1301         } else {
1302                 if (flag != 1)
1303                         remaining = (int)(totalbytes / (statbytes / elapsed) -
1304                             elapsed);
1305                 else
1306                         remaining = (int)elapsed;
1307 
1308                 i = remaining / 3600;
1309                 if (i)
1310                         snprintf(buf + strlen(buf), sizeof (buf) - strlen(buf),
1311                             "%2d:", i);
1312                 else
1313                         snprintf(buf + strlen(buf), sizeof (buf) - strlen(buf),
1314                             "   ");
1315                 i = remaining % 3600;
1316                 snprintf(buf + strlen(buf), sizeof (buf) - strlen(buf),
1317                     "%02d:%02d%s", i / 60, i % 60,
1318                     (flag != 1) ? " ETA" : "    ");
1319         }
1320         atomicio(write, fileno(stdout), buf, strlen(buf));
1321 
1322         if (flag == -1) {
1323                 mysignal(SIGALRM, updateprogressmeter);
1324                 alarm(PROGRESSTIME);
1325         } else if (flag == 1) {
1326                 alarm(0);
1327                 atomicio(write, fileno(stdout), "\n", 1);
1328                 statbytes = 0;
1329         }
1330 }
1331 
1332 int
1333 getttywidth(void)
1334 {
1335         struct winsize winsize;
1336 
1337         if (ioctl(fileno(stdout), TIOCGWINSZ, &winsize) != -1)
1338                 return (winsize.ws_col ? winsize.ws_col : 80);
1339         else
1340                 return (80);
1341 }