summaryrefslogtreecommitdiffhomepage
path: root/scp.c
diff options
context:
space:
mode:
authorMatt Johnston <matt@ucc.asn.au>2006-03-08 14:20:24 +0000
committerMatt Johnston <matt@ucc.asn.au>2006-03-08 14:20:24 +0000
commit3a5c4b68bb375a97b2d5551ad06ef428bc980d6f (patch)
treedc960c631f9a9afa2fbbef7d02d522f77dc2831f /scp.c
parenta7a5365af25daaf3142b367df63035b9cacc7032 (diff)
Update to scp from OpenSSH portable 4.3p2
--HG-- extra : convert_revision : e2bccdd6ba49edd575b1d0272c7dfee5f9f7acf6
Diffstat (limited to 'scp.c')
-rw-r--r--scp.c343
1 files changed, 195 insertions, 148 deletions
diff --git a/scp.c b/scp.c
index 7ee25f9..7923996 100644
--- a/scp.c
+++ b/scp.c
@@ -71,40 +71,20 @@
*/
#include "includes.h"
+/*RCSID("$OpenBSD: scp.c,v 1.130 2006/01/31 10:35:43 djm Exp $");*/
+
#include "atomicio.h"
#include "compat.h"
#include "scpmisc.h"
#include "progressmeter.h"
-#define _PATH_CP "/bin/cp"
-
-#ifndef TIMEVAL_TO_TIMESPEC
-#define TIMEVAL_TO_TIMESPEC(tv, ts) { \
- (ts)->tv_sec = (tv)->tv_sec; \
- (ts)->tv_nsec = (tv)->tv_usec * 1000; \
-}
-#endif
-
-#ifndef timersub
-#define timersub(tvp, uvp, vvp) \
- do { \
- (vvp)->tv_sec = (tvp)->tv_sec - (uvp)->tv_sec; \
- (vvp)->tv_usec = (tvp)->tv_usec - (uvp)->tv_usec; \
- if ((vvp)->tv_usec < 0) { \
- (vvp)->tv_sec--; \
- (vvp)->tv_usec += 1000000; \
- } \
- } while (/* CONSTCOND */ 0)
-#endif /* timersub */
-
-
void bwlimit(int);
/* Struct for addargs */
arglist args;
/* Bandwidth limit */
-off_t limitbw = 0;
+off_t limit_rate = 0;
/* Name of current file being transferred. */
char *curfile;
@@ -112,10 +92,8 @@ char *curfile;
/* This is set to non-zero to enable verbose mode. */
int verbose_mode = 0;
-#ifdef PROGRESS_METER
/* This is set to zero if the progressmeter is not desired. */
int showprogress = 1;
-#endif
/* This is the program to execute for the secured connection. ("ssh" or -S) */
char *ssh_program = _PATH_SSH_PROGRAM;
@@ -126,10 +104,56 @@ pid_t do_cmd_pid = -1;
static void
killchild(int signo)
{
- if (do_cmd_pid > 1)
- kill(do_cmd_pid, signo);
+ if (do_cmd_pid > 1) {
+ kill(do_cmd_pid, signo ? signo : SIGTERM);
+ waitpid(do_cmd_pid, NULL, 0);
+ }
+
+ if (signo)
+ _exit(1);
+ exit(1);
+}
+
+static int
+do_local_cmd(arglist *a)
+{
+ u_int i;
+ int status;
+ pid_t pid;
+
+ if (a->num == 0)
+ fatal("do_local_cmd: no arguments");
+
+ if (verbose_mode) {
+ fprintf(stderr, "Executing:");
+ for (i = 0; i < a->num; i++)
+ fprintf(stderr, " %s", a->list[i]);
+ fprintf(stderr, "\n");
+ }
+ if ((pid = fork()) == -1)
+ fatal("do_local_cmd: fork: %s", strerror(errno));
+
+ if (pid == 0) {
+ execvp(a->list[0], a->list);
+ perror(a->list[0]);
+ exit(1);
+ }
+
+ do_cmd_pid = pid;
+ signal(SIGTERM, killchild);
+ signal(SIGINT, killchild);
+ signal(SIGHUP, killchild);
+
+ while (waitpid(pid, &status, 0) == -1)
+ if (errno != EINTR)
+ fatal("do_local_cmd: waitpid: %s", strerror(errno));
+
+ do_cmd_pid = -1;
+
+ if (!WIFEXITED(status) || WEXITSTATUS(status) != 0)
+ return (-1);
- _exit(1);
+ return (0);
}
/*
@@ -156,11 +180,10 @@ do_cmd(char *host, char *remuser, char *cmd, int *fdin, int *fdout, int argc)
pipe(reserved);
/* Create a socket pair for communicating with ssh. */
- if (pipe(pin) < 0 || pipe(pout) < 0)
- {
- fprintf(stderr, "Fatal error: pipe: %s\n", strerror(errno));
- exit(1);
- }
+ if (pipe(pin) < 0)
+ fatal("pipe: %s", strerror(errno));
+ if (pipe(pout) < 0)
+ fatal("pipe: %s", strerror(errno));
/* Free the reserved descriptors. */
close(reserved[0]);
@@ -169,19 +192,20 @@ do_cmd(char *host, char *remuser, char *cmd, int *fdin, int *fdout, int argc)
/* uClinux needs to build the args here before vforking,
otherwise we do it later on. */
#ifdef __uClinux__
- args.list[0] = ssh_program;
- if (remuser != NULL)
- addargs(&args, "-l%s", remuser);
- addargs(&args, "%s", host);
- addargs(&args, "%s", cmd);
+ replacearg(&args, 0, "%s", ssh_program);
+ if (remuser != NULL)
+ addargs(&args, "-l%s", remuser);
+ addargs(&args, "%s", host);
+ addargs(&args, "%s", cmd);
#endif /* __uClinux__ */
/* Fork a child to execute the command on the remote host using ssh. */
-#ifdef __uClinux__
+#ifndef __uClinux__
do_cmd_pid = vfork();
#else
do_cmd_pid = fork();
#endif /* __uClinux__ */
+
if (do_cmd_pid == 0) {
/* Child. */
close(pin[1]);
@@ -192,38 +216,39 @@ do_cmd(char *host, char *remuser, char *cmd, int *fdin, int *fdout, int argc)
close(pout[1]);
#ifndef __uClinux__
- args.list[0] = ssh_program;
- if (remuser != NULL) {
- addargs(&args, "-l");
- addargs(&args, "%s", remuser);
- }
+ replacearg(&args, 0, "%s", ssh_program);
+ if (remuser != NULL)
+ addargs(&args, "-l%s", remuser);
addargs(&args, "%s", host);
addargs(&args, "%s", cmd);
-#endif
+#endif /* __uClinux__ */
execvp(ssh_program, args.list);
perror(ssh_program);
exit(1);
} else if (do_cmd_pid == -1) {
- fprintf(stderr, "Fatal error: fork: %s\n", strerror(errno));
- exit(1);
+ fatal("fork: %s", strerror(errno));
}
+
#ifdef __uClinux__
/* clean up command */
/* pop cmd */
- xfree(args->list[--args->num]);
- args->list[args->num]=NULL;
+ xfree(args->list[args->num-1]);
+ args->list[args->num-1]=NULL;
+ args->num--;
/* pop host */
- xfree(args->list[--args->num-1]);
- args->list[args->num]=NULL;
+ xfree(args->list[args->num-1]);
+ args->list[args->num-1]=NULL;
+ args->num--;
/* pop user */
if (remuser != NULL) {
- xfree(args->list[--args->num-1]);
- args->list[args->num]=NULL;
+ xfree(args->list[args->num-1]);
+ args->list[args->num-1]=NULL;
+ args->num--;
}
#endif /* __uClinux__ */
-
+
/* Parent. Close the other side, and return the local side. */
close(pin[0]);
*fdout = pin[1];
@@ -236,7 +261,7 @@ do_cmd(char *host, char *remuser, char *cmd, int *fdin, int *fdout, int argc)
}
typedef struct {
- int cnt;
+ size_t cnt;
char *buf;
} BUF;
@@ -267,6 +292,7 @@ void usage(void);
#if defined(DBMULTI_scp) && defined(DROPBEAR_MULTI)
int scp_main(int argc, char **argv)
#else
+int
main(int argc, char **argv)
#endif
{
@@ -276,10 +302,15 @@ main(int argc, char **argv)
extern char *optarg;
extern int optind;
+ /* Ensure that fds 0, 1 and 2 are open or directed to /dev/null */
+ sanitise_stdfd();
+
+ memset(&args, '\0', sizeof(args));
args.list = NULL;
- addargs(&args, "ssh"); /* overwritten with ssh_program */
+ addargs(&args, "%s", ssh_program);
addargs(&args, "-x");
addargs(&args, "-oForwardAgent no");
+ addargs(&args, "-oPermitLocalCommand no");
addargs(&args, "-oClearAllForwardings yes");
fflag = tflag = 0;
@@ -309,7 +340,7 @@ main(int argc, char **argv)
speed = strtod(optarg, &endp);
if (speed <= 0 || *endp != '\0')
usage();
- limitbw = speed * 1024;
+ limit_rate = speed * 1024;
break;
case 'p':
pflag = 1;
@@ -326,6 +357,7 @@ main(int argc, char **argv)
break;
#ifdef PROGRESS_METER
case 'q':
+ addargs(&args, "-q");
showprogress = 0;
break;
#endif
@@ -351,14 +383,11 @@ main(int argc, char **argv)
argc -= optind;
argv += optind;
- if ((pwd = getpwuid(userid = getuid())) == NULL) {
- fprintf(stderr, "unknown user %u", (u_int) userid);
- }
+ if ((pwd = getpwuid(userid = getuid())) == NULL)
+ fatal("unknown user %u", (u_int) userid);
-#ifdef PROGRESS_METER
if (!isatty(STDERR_FILENO))
showprogress = 0;
-#endif
remin = STDIN_FILENO;
remout = STDOUT_FILENO;
@@ -392,9 +421,9 @@ main(int argc, char **argv)
if ((targ = colon(argv[argc - 1]))) /* Dest is remote host. */
toremote(targ, argc, argv);
else {
- tolocal(argc, argv); /* Dest is local host. */
if (targetshouldbedirectory)
verifydir(argv[argc - 1]);
+ tolocal(argc, argv); /* Dest is local host. */
}
/*
* Finally check the exit status of the ssh process, if one was forked
@@ -414,78 +443,76 @@ main(int argc, char **argv)
}
exit(errs != 0);
}
-#endif /* DBMULTI stuff */
+#endif /* DBMULTI_scp stuff */
void
toremote(char *targ, int argc, char **argv)
{
int i, len;
- char *bp, *host, *src, *suser, *thost, *tuser;
+ char *bp, *host, *src, *suser, *thost, *tuser, *arg;
+ arglist alist;
+
+ memset(&alist, '\0', sizeof(alist));
+ alist.list = NULL;
*targ++ = 0;
if (*targ == 0)
targ = ".";
- if ((thost = strrchr(argv[argc - 1], '@'))) {
+ arg = xstrdup(argv[argc - 1]);
+ if ((thost = strrchr(arg, '@'))) {
/* user@host */
*thost++ = 0;
- tuser = argv[argc - 1];
+ tuser = arg;
if (*tuser == '\0')
tuser = NULL;
} else {
- thost = argv[argc - 1];
+ thost = arg;
tuser = NULL;
}
+ if (tuser != NULL && !okname(tuser)) {
+ xfree(arg);
+ return;
+ }
+
for (i = 0; i < argc - 1; i++) {
src = colon(argv[i]);
if (src) { /* remote to remote */
- static char *ssh_options =
- "-x -o'ClearAllForwardings yes'";
+ freeargs(&alist);
+ addargs(&alist, "%s", ssh_program);
+ if (verbose_mode)
+ addargs(&alist, "-v");
+ addargs(&alist, "-x");
+ addargs(&alist, "-oClearAllForwardings yes");
+ addargs(&alist, "-n");
+
*src++ = 0;
if (*src == 0)
src = ".";
host = strrchr(argv[i], '@');
- len = strlen(ssh_program) + strlen(argv[i]) +
- strlen(src) + (tuser ? strlen(tuser) : 0) +
- strlen(thost) + strlen(targ) +
- strlen(ssh_options) + CMDNEEDS + 20;
- bp = xmalloc(len);
+
if (host) {
*host++ = 0;
host = cleanhostname(host);
suser = argv[i];
if (*suser == '\0')
suser = pwd->pw_name;
- else if (!okname(suser)) {
- xfree(bp);
+ else if (!okname(suser))
continue;
- }
- if (tuser && !okname(tuser)) {
- xfree(bp);
- continue;
- }
- snprintf(bp, len,
- "%s%s %s -n "
- "-l %s %s %s %s '%s%s%s:%s'",
- ssh_program, verbose_mode ? " -v" : "",
- ssh_options, suser, host, cmd, src,
- tuser ? tuser : "", tuser ? "@" : "",
- thost, targ);
+ addargs(&alist, "-l");
+ addargs(&alist, "%s", suser);
} else {
host = cleanhostname(argv[i]);
- snprintf(bp, len,
- "exec %s%s %s -n %s "
- "%s %s '%s%s%s:%s'",
- ssh_program, verbose_mode ? " -v" : "",
- ssh_options, host, cmd, src,
- tuser ? tuser : "", tuser ? "@" : "",
- thost, targ);
}
- if (verbose_mode)
- fprintf(stderr, "Executing: %s\n", bp);
- (void) system(bp);
- (void) xfree(bp);
+ addargs(&alist, "%s", host);
+ addargs(&alist, "%s", cmd);
+ addargs(&alist, "%s", src);
+ addargs(&alist, "%s%s%s:%s",
+ tuser ? tuser : "", tuser ? "@" : "",
+ thost, targ);
+ if (do_local_cmd(&alist) != 0)
+ errs = 1;
} else { /* local to remote */
if (remin == -1) {
len = strlen(targ) + CMDNEEDS + 20;
@@ -509,20 +536,23 @@ tolocal(int argc, char **argv)
{
int i, len;
char *bp, *host, *src, *suser;
+ arglist alist;
+
+ memset(&alist, '\0', sizeof(alist));
+ alist.list = NULL;
for (i = 0; i < argc - 1; i++) {
if (!(src = colon(argv[i]))) { /* Local to local. */
- len = strlen(_PATH_CP) + strlen(argv[i]) +
- strlen(argv[argc - 1]) + 20;
- bp = xmalloc(len);
- (void) snprintf(bp, len, "exec %s%s%s %s %s", _PATH_CP,
- iamrecursive ? " -r" : "", pflag ? " -p" : "",
- argv[i], argv[argc - 1]);
- if (verbose_mode)
- fprintf(stderr, "Executing: %s\n", bp);
- if (system(bp))
+ freeargs(&alist);
+ addargs(&alist, "%s", _PATH_CP);
+ if (iamrecursive)
+ addargs(&alist, "-r");
+ if (pflag)
+ addargs(&alist, "-p");
+ addargs(&alist, "%s", argv[i]);
+ addargs(&alist, "%s", argv[argc-1]);
+ if (do_local_cmd(&alist))
++errs;
- (void) xfree(bp);
continue;
}
*src++ = 0;
@@ -559,8 +589,9 @@ source(int argc, char **argv)
struct stat stb;
static BUF buffer;
BUF *bp;
- off_t i, amt, result, statbytes;
- int fd, haderr, indx;
+ off_t i, amt, statbytes;
+ size_t result;
+ int fd = -1, haderr, indx;
char *last, *name, buf[2048];
int len;
@@ -614,7 +645,7 @@ syserr: run_err("%s: %s", name, strerror(errno));
#define FILEMODEMASK (S_ISUID|S_ISGID|S_IRWXU|S_IRWXG|S_IRWXO)
snprintf(buf, sizeof buf, "C%04o %lld %s\n",
(u_int) (stb.st_mode & FILEMODEMASK),
- (int64_t)stb.st_size, last);
+ (long long)stb.st_size, last);
if (verbose_mode) {
fprintf(stderr, "Sending file modes: %s", buf);
}
@@ -622,10 +653,13 @@ syserr: run_err("%s: %s", name, strerror(errno));
if (response() < 0)
goto next;
if ((bp = allocbuf(&buffer, fd, 2048)) == NULL) {
-next: (void) close(fd);
+next: if (fd != -1) {
+ (void) close(fd);
+ fd = -1;
+ }
continue;
}
-#ifdef PROGRESS_METER
+#if PROGRESS_METER
if (showprogress)
start_progress_meter(curfile, stb.st_size, &statbytes);
#endif
@@ -637,17 +671,17 @@ next: (void) close(fd);
if (!haderr) {
result = atomicio(read, fd, bp->buf, amt);
if (result != amt)
- haderr = result >= 0 ? EIO : errno;
+ haderr = errno;
}
if (haderr)
(void) atomicio(vwrite, remout, bp->buf, amt);
else {
result = atomicio(vwrite, remout, bp->buf, amt);
if (result != amt)
- haderr = result >= 0 ? EIO : errno;
+ haderr = errno;
statbytes += result;
}
- if (limitbw)
+ if (limit_rate)
bwlimit(amt);
}
#ifdef PROGRESS_METER
@@ -655,8 +689,11 @@ next: (void) close(fd);
stop_progress_meter();
#endif
- if (close(fd) < 0 && !haderr)
- haderr = errno;
+ if (fd != -1) {
+ if (close(fd) < 0 && !haderr)
+ haderr = errno;
+ fd = -1;
+ }
if (!haderr)
(void) atomicio(vwrite, remout, "", 1);
else
@@ -723,7 +760,7 @@ bwlimit(int amount)
{
static struct timeval bwstart, bwend;
static int lamt, thresh = 16384;
- uint64_t wait;
+ u_int64_t waitlen;
struct timespec ts, rm;
if (!timerisset(&bwstart)) {
@@ -741,10 +778,10 @@ bwlimit(int amount)
return;
lamt *= 8;
- wait = (double)1000000L * lamt / limitbw;
+ waitlen = (double)1000000L * lamt / limit_rate;
- bwstart.tv_sec = wait / 1000000L;
- bwstart.tv_usec = wait % 1000000L;
+ bwstart.tv_sec = waitlen / 1000000L;
+ bwstart.tv_usec = waitlen % 1000000L;
if (timercmp(&bwstart, &bwend, >)) {
timersub(&bwstart, &bwend, &bwend);
@@ -781,8 +818,9 @@ sink(int argc, char **argv)
YES, NO, DISPLAYED
} wrerr;
BUF *bp;
- off_t i, j;
- int amt, count, exists, first, mask, mode, ofd, omode;
+ off_t i;
+ size_t j, count;
+ int amt, exists, first, mask, mode, ofd, omode;
off_t size, statbytes;
int setimes, targisdir, wrerrno = 0;
char ch, *cp, *np, *targ, *why, *vect[1], buf[2048];
@@ -790,7 +828,7 @@ sink(int argc, char **argv)
#define atime tv[0]
#define mtime tv[1]
-#define SCREWUP(str) do { why = str; goto screwup; } while (0)
+#define SCREWUP(str) { why = str; goto screwup; }
setimes = targisdir = 0;
mask = umask(0);
@@ -809,7 +847,7 @@ sink(int argc, char **argv)
targisdir = 1;
for (first = 1;; first = 0) {
cp = buf;
- if (atomicio(read, remin, cp, 1) <= 0)
+ if (atomicio(read, remin, cp, 1) != 1)
return;
if (*cp++ == '\n')
SCREWUP("unexpected <newline>");
@@ -819,6 +857,8 @@ sink(int argc, char **argv)
*cp++ = ch;
} while (cp < &buf[sizeof(buf) - 1] && ch != '\n');
*cp = 0;
+ if (verbose_mode)
+ fprintf(stderr, "Sink: %s", buf);
if (buf[0] == '\01' || buf[0] == '\02') {
if (iamremote == 0)
@@ -882,9 +922,13 @@ sink(int argc, char **argv)
size = size * 10 + (*cp++ - '0');
if (*cp++ != ' ')
SCREWUP("size not delimited");
+ if ((strchr(cp, '/') != NULL) || (strcmp(cp, "..") == 0)) {
+ run_err("error: unexpected filename: %s", cp);
+ exit(1);
+ }
if (targisdir) {
static char *namebuf;
- static int cursize;
+ static size_t cursize;
size_t need;
need = strlen(targ) + strlen(cp) + 250;
@@ -903,6 +947,8 @@ sink(int argc, char **argv)
exists = stat(np, &stb) == 0;
if (buf[0] == 'D') {
int mod_flag = pflag;
+ if (!iamrecursive)
+ SCREWUP("received directory without -r");
if (exists) {
if (!S_ISDIR(stb.st_mode)) {
errno = ENOTDIR;
@@ -956,11 +1002,8 @@ bad: run_err("%s: %s", np, strerror(errno));
amt = size - i;
count += amt;
do {
- j = read(remin, cp, amt);
- if (j == -1 && (errno == EINTR ||
- errno == EAGAIN)) {
- continue;
- } else if (j <= 0) {
+ j = atomicio(read, remin, cp, amt);
+ if (j == 0) {
run_err("%s", j ? strerror(errno) :
"dropped connection");
exit(1);
@@ -969,17 +1012,17 @@ bad: run_err("%s: %s", np, strerror(errno));
cp += j;
statbytes += j;
} while (amt > 0);
-
- if (limitbw)
+
+ if (limit_rate)
bwlimit(4096);
if (count == bp->cnt) {
/* Keep reading so we stay sync'd up. */
if (wrerr == NO) {
- j = atomicio(vwrite, ofd, bp->buf, count);
- if (j != count) {
+ if (atomicio(vwrite, ofd, bp->buf,
+ count) != count) {
wrerr = YES;
- wrerrno = j >= 0 ? EIO : errno;
+ wrerrno = errno;
}
}
count = 0;
@@ -991,9 +1034,9 @@ bad: run_err("%s: %s", np, strerror(errno));
stop_progress_meter();
#endif
if (count != 0 && wrerr == NO &&
- (j = atomicio(vwrite, ofd, bp->buf, count)) != count) {
+ atomicio(vwrite, ofd, bp->buf, count) != count) {
wrerr = YES;
- wrerrno = j >= 0 ? EIO : errno;
+ wrerrno = errno;
}
if (wrerr == NO && ftruncate(ofd, size) != 0) {
run_err("%s: truncate: %s", np, strerror(errno));
@@ -1002,21 +1045,25 @@ bad: run_err("%s: %s", np, strerror(errno));
if (pflag) {
if (exists || omode != mode)
#ifdef HAVE_FCHMOD
- if (fchmod(ofd, omode))
+ if (fchmod(ofd, omode)) {
#else /* HAVE_FCHMOD */
- if (chmod(np, omode))
+ if (chmod(np, omode)) {
#endif /* HAVE_FCHMOD */
run_err("%s: set mode: %s",
np, strerror(errno));
+ wrerr = DISPLAYED;
+ }
} else {
if (!exists && omode != mode)
#ifdef HAVE_FCHMOD
- if (fchmod(ofd, omode & ~mask))
+ if (fchmod(ofd, omode & ~mask)) {
#else /* HAVE_FCHMOD */
- if (chmod(np, omode & ~mask))
+ if (chmod(np, omode & ~mask)) {
#endif /* HAVE_FCHMOD */
run_err("%s: set mode: %s",
np, strerror(errno));
+ wrerr = DISPLAYED;
+ }
}
if (close(ofd) == -1) {
wrerr = YES;
@@ -1084,8 +1131,8 @@ void
usage(void)
{
(void) fprintf(stderr,
- "usage: scp [-pqrvBC1246] [-F config] [-S program] [-P port]\n"
- " [-c cipher] [-i identity] [-l limit] [-o option]\n"
+ "usage: scp [-1246BCpqrv] [-c cipher] [-F ssh_config] [-i identity_file]\n"
+ " [-l limit] [-o ssh_option] [-P port] [-S program]\n"
" [[user@]host1:]file1 [...] [[user@]host2:]file2\n");
exit(1);
}
@@ -1126,7 +1173,7 @@ verifydir(char *cp)
errno = ENOTDIR;
}
run_err("%s: %s", cp, strerror(errno));
- exit(1);
+ killchild(0);
}
int