summaryrefslogtreecommitdiff
path: root/scp.c
diff options
context:
space:
mode:
authordjm@openbsd.org <djm@openbsd.org>2021-08-07 00:06:30 +0000
committerDamien Miller <djm@mindrot.org>2021-08-07 10:20:31 +1000
commit318c06bb04ee21a0cfa6b6022a201eacaa53f388 (patch)
tree1f169cfc9fd0ecc9b1e66925a786b3139bbe62da /scp.c
parentde7115b373ba0be3861c65de9b606a3e0e9d29a3 (diff)
downloadopenssh-git-318c06bb04ee21a0cfa6b6022a201eacaa53f388.tar.gz
upstream: use sftp_client crossloading to implement scp -3
feedback/ok markus@ OpenBSD-Commit-ID: 7db4c0086cfc12afc9cfb71d4c2fd3c7e9416ee9
Diffstat (limited to 'scp.c')
-rw-r--r--scp.c229
1 files changed, 189 insertions, 40 deletions
diff --git a/scp.c b/scp.c
index 3125d336..9be41a26 100644
--- a/scp.c
+++ b/scp.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: scp.c,v 1.218 2021/08/07 00:00:33 djm Exp $ */
+/* $OpenBSD: scp.c,v 1.219 2021/08/07 00:06:30 djm Exp $ */
/*
* scp - secure remote copy. This is basically patched BSD rcp which
* uses ssh to do the data transfer (instead of using rcmd).
@@ -139,7 +139,7 @@ extern char *__progname;
#define COPY_BUFLEN 16384
int do_cmd(char *program, char *host, char *remuser, int port, char *cmd,
- int *fdin, int *fdout);
+ int *fdin, int *fdout, pid_t *pidp);
int do_cmd2(char *host, char *remuser, int port, char *cmd,
int fdin, int fdout);
@@ -175,6 +175,7 @@ char *ssh_program = _PATH_SSH_PROGRAM;
/* This is used to store the pid of ssh_program */
pid_t do_cmd_pid = -1;
+pid_t do_cmd_pid2 = -1;
/* Needed for sftp */
volatile sig_atomic_t interrupted = 0;
@@ -189,6 +190,10 @@ killchild(int signo)
kill(do_cmd_pid, signo ? signo : SIGTERM);
waitpid(do_cmd_pid, NULL, 0);
}
+ if (do_cmd_pid2 > 1) {
+ kill(do_cmd_pid2, signo ? signo : SIGTERM);
+ waitpid(do_cmd_pid2, NULL, 0);
+ }
if (signo)
_exit(1);
@@ -196,19 +201,26 @@ killchild(int signo)
}
static void
-suspchild(int signo)
+suspone(int pid, int signo)
{
int status;
- if (do_cmd_pid > 1) {
- kill(do_cmd_pid, signo);
- while (waitpid(do_cmd_pid, &status, WUNTRACED) == -1 &&
+ if (pid > 1) {
+ kill(pid, signo);
+ while (waitpid(pid, &status, WUNTRACED) == -1 &&
errno == EINTR)
;
- kill(getpid(), SIGSTOP);
}
}
+static void
+suspchild(int signo)
+{
+ suspone(do_cmd_pid, signo);
+ suspone(do_cmd_pid2, signo);
+ kill(getpid(), SIGSTOP);
+}
+
static int
do_local_cmd(arglist *a)
{
@@ -259,7 +271,7 @@ do_local_cmd(arglist *a)
int
do_cmd(char *program, char *host, char *remuser, int port, char *cmd,
- int *fdin, int *fdout)
+ int *fdin, int *fdout, int *pid)
{
int pin[2], pout[2], reserved[2];
@@ -294,8 +306,8 @@ do_cmd(char *program, char *host, char *remuser, int port, char *cmd,
ssh_signal(SIGTTOU, suspchild);
/* Fork a child to execute the command on the remote host using ssh. */
- do_cmd_pid = fork();
- if (do_cmd_pid == 0) {
+ *pid = fork();
+ if (*pid == 0) {
/* Child. */
close(pin[1]);
close(pout[0]);
@@ -320,7 +332,7 @@ do_cmd(char *program, char *host, char *remuser, int port, char *cmd,
execvp(program, args.list);
perror(program);
exit(1);
- } else if (do_cmd_pid == -1) {
+ } else if (*pid == -1) {
fatal("fork: %s", strerror(errno));
}
/* Parent. Close the other side, and return the local side. */
@@ -340,10 +352,11 @@ do_cmd(char *program, char *host, char *remuser, int port, char *cmd,
* This way the input and output of two commands can be connected.
*/
int
-do_cmd2(char *host, char *remuser, int port, char *cmd, int fdin, int fdout)
+do_cmd2(char *host, char *remuser, int port, char *cmd,
+ int fdin, int fdout)
{
- pid_t pid;
int status;
+ pid_t pid;
if (verbose_mode)
fmprintf(stderr,
@@ -403,7 +416,7 @@ void verifydir(char *);
struct passwd *pwd;
uid_t userid;
-int errs, remin, remout;
+int errs, remin, remout, remin2, remout2;
int Tflag, pflag, iamremote, iamrecursive, targetshouldbedirectory;
#define CMDNEEDS 64
@@ -424,6 +437,8 @@ void usage(void);
void source_sftp(int, char *, char *, struct sftp_conn *, char **);
void sink_sftp(int, char *, const char *, struct sftp_conn *);
+void throughlocal_sftp(struct sftp_conn *, struct sftp_conn *,
+ char *, char *, char **);
int
main(int argc, char **argv)
@@ -943,22 +958,23 @@ brace_expand(const char *pattern, char ***patternsp, size_t *npatternsp)
}
static struct sftp_conn *
-do_sftp_connect(char *host, char *user, int port, char *sftp_direct)
+do_sftp_connect(char *host, char *user, int port, char *sftp_direct,
+ int *reminp, int *remoutp, int *pidp)
{
if (sftp_direct == NULL) {
addargs(&args, "-s");
if (do_cmd(ssh_program, host, user, port, "sftp",
- &remin, &remout) < 0)
+ reminp, remoutp, pidp) < 0)
return NULL;
} else {
args.list = NULL;
addargs(&args, "sftp-server");
if (do_cmd(sftp_direct, host, NULL, -1, "sftp",
- &remin, &remout) < 0)
+ reminp, remoutp, pidp) < 0)
return NULL;
}
- return do_init(remin, remout, 32768, 64, limit_kbps);
+ return do_init(*reminp, *remoutp, 32768, 64, limit_kbps);
}
void
@@ -968,9 +984,9 @@ toremote(int argc, char **argv, enum scp_mode_e mode, char *sftp_direct)
char *bp, *tuser, *thost, *targ;
char *remote_path = NULL;
int sport = -1, tport = -1;
- struct sftp_conn *conn = NULL;
+ struct sftp_conn *conn = NULL, *conn2 = NULL;
arglist alist;
- int i, r;
+ int i, r, status;
u_int j;
memset(&alist, '\0', sizeof(alist));
@@ -1011,21 +1027,64 @@ toremote(int argc, char **argv, enum scp_mode_e mode, char *sftp_direct)
continue;
}
if (host && throughlocal) { /* extended remote to remote */
- /* XXX uses scp; need to support SFTP remote-remote */
- xasprintf(&bp, "%s -f %s%s", cmd,
- *src == '-' ? "-- " : "", src);
- if (do_cmd(ssh_program, host, suser, sport, bp,
- &remin, &remout) < 0)
- exit(1);
- free(bp);
- xasprintf(&bp, "%s -t %s%s", cmd,
- *targ == '-' ? "-- " : "", targ);
- if (do_cmd2(thost, tuser, tport, bp, remin, remout) < 0)
- exit(1);
- free(bp);
- (void) close(remin);
- (void) close(remout);
- remin = remout = -1;
+ if (mode == MODE_SFTP) {
+ if (remin == -1) {
+ /* Connect to dest now */
+ conn = do_sftp_connect(thost, tuser,
+ tport, sftp_direct,
+ &remin, &remout, &do_cmd_pid);
+ if (conn == NULL) {
+ fatal("Unable to open "
+ "destination connection");
+ }
+ debug3_f("origin in %d out %d pid %ld",
+ remin, remout, (long)do_cmd_pid);
+ }
+ /*
+ * XXX remember suser/host/sport and only
+ * reconnect if they change between arguments.
+ * would save reconnections for cases like
+ * scp -3 hosta:/foo hosta:/bar hostb:
+ */
+ /* Connect to origin now */
+ conn2 = do_sftp_connect(host, suser,
+ sport, sftp_direct,
+ &remin2, &remout2, &do_cmd_pid2);
+ if (conn2 == NULL) {
+ fatal("Unable to open "
+ "source connection");
+ }
+ debug3_f("destination in %d out %d pid %ld",
+ remin2, remout2, (long)do_cmd_pid2);
+ throughlocal_sftp(conn2, conn, src, targ,
+ &remote_path);
+ (void) close(remin2);
+ (void) close(remout2);
+ remin2 = remout2 = -1;
+ if (waitpid(do_cmd_pid2, &status, 0) == -1)
+ ++errs;
+ else if (!WIFEXITED(status) ||
+ WEXITSTATUS(status) != 0)
+ ++errs;
+ do_cmd_pid2 = -1;
+ continue;
+ } else {
+ xasprintf(&bp, "%s -f %s%s", cmd,
+ *src == '-' ? "-- " : "", src);
+ if (do_cmd(ssh_program, host, suser, sport,
+ bp, &remin, &remout, &do_cmd_pid) < 0)
+ exit(1);
+ free(bp);
+ xasprintf(&bp, "%s -t %s%s", cmd,
+ *targ == '-' ? "-- " : "", targ);
+ if (do_cmd2(thost, tuser, tport, bp,
+ remin, remout) < 0)
+ exit(1);
+ free(bp);
+ (void) close(remin);
+ (void) close(remout);
+ remin = remout = -1;
+ }
} else if (host) { /* standard remote to remote */
/*
* Second remote user is passed to first remote side
@@ -1074,7 +1133,8 @@ toremote(int argc, char **argv, enum scp_mode_e mode, char *sftp_direct)
if (remin == -1) {
/* Connect to remote now */
conn = do_sftp_connect(thost, tuser,
- tport, sftp_direct);
+ tport, sftp_direct,
+ &remin, &remout, &do_cmd_pid);
if (conn == NULL) {
fatal("Unable to open sftp "
"connection");
@@ -1091,7 +1151,7 @@ toremote(int argc, char **argv, enum scp_mode_e mode, char *sftp_direct)
xasprintf(&bp, "%s -t %s%s", cmd,
*targ == '-' ? "-- " : "", targ);
if (do_cmd(ssh_program, thost, tuser, tport, bp,
- &remin, &remout) < 0)
+ &remin, &remout, &do_cmd_pid) < 0)
exit(1);
if (response() < 0)
exit(1);
@@ -1156,7 +1216,8 @@ tolocal(int argc, char **argv, enum scp_mode_e mode, char *sftp_direct)
}
/* Remote to local. */
if (mode == MODE_SFTP) {
- conn = do_sftp_connect(host, suser, sport, sftp_direct);
+ conn = do_sftp_connect(host, suser, sport,
+ sftp_direct, &remin, &remout, &do_cmd_pid);
if (conn == NULL) {
error("Couldn't make sftp connection "
"to server");
@@ -1176,8 +1237,8 @@ tolocal(int argc, char **argv, enum scp_mode_e mode, char *sftp_direct)
/* SCP */
xasprintf(&bp, "%s -f %s%s",
cmd, *src == '-' ? "-- " : "", src);
- if (do_cmd(ssh_program, host, suser, sport, bp, &remin,
- &remout) < 0) {
+ if (do_cmd(ssh_program, host, suser, sport, bp,
+ &remin, &remout, &do_cmd_pid) < 0) {
free(bp);
++errs;
continue;
@@ -1808,6 +1869,94 @@ screwup:
exit(1);
}
+void
+throughlocal_sftp(struct sftp_conn *from, struct sftp_conn *to,
+ char *src, char *targ, char **to_remote_path)
+{
+ char *target = NULL, *filename = NULL, *abs_dst = NULL;
+ char *abs_src = NULL, *tmp = NULL, *from_remote_path;
+ glob_t g;
+ int i, r, targetisdir, err = 0;
+
+ if (*to_remote_path == NULL) {
+ *to_remote_path = do_realpath(to, ".");
+ if (*to_remote_path == NULL) {
+ fatal("Unable to determine destination remote "
+ "working directory");
+ }
+ }
+
+ if ((from_remote_path = do_realpath(from, ".")) == NULL) {
+ fatal("Unable to determine source remote "
+ "working directory");
+ }
+
+ if ((filename = basename(src)) == NULL)
+ fatal("basename %s: %s", src, strerror(errno));
+
+ abs_src = xstrdup(src);
+ abs_src = make_absolute(abs_src, from_remote_path);
+ free(from_remote_path);
+ target = xstrdup(targ);
+ target = make_absolute(target, *to_remote_path);
+ memset(&g, 0, sizeof(g));
+
+ targetisdir = remote_is_dir(to, target);
+ if (!targetisdir && targetshouldbedirectory) {
+ error("Destination path \"%s\" is not a directory", target);
+ err = -1;
+ goto out;
+ }
+
+ debug3_f("copying remote %s to remote %s", abs_src, target);
+ if ((r = remote_glob(from, abs_src, GLOB_MARK, NULL, &g)) != 0) {
+ if (r == GLOB_NOSPACE)
+ error("Too many glob matches for \"%s\".", abs_src);
+ else
+ error("File \"%s\" not found.", abs_src);
+ err = -1;
+ goto out;
+ }
+
+ for (i = 0; g.gl_pathv[i] && !interrupted; i++) {
+ tmp = xstrdup(g.gl_pathv[i]);
+ if ((filename = basename(tmp)) == NULL) {
+ error("basename %s: %s", tmp, strerror(errno));
+ err = -1;
+ goto out;
+ }
+
+ if (targetisdir)
+ abs_dst = path_append(target, filename);
+ else
+ abs_dst = xstrdup(target);
+
+ debug("Fetching %s to %s\n", g.gl_pathv[i], abs_dst);
+ if (globpath_is_dir(g.gl_pathv[i]) && iamrecursive) {
+ if (crossload_dir(from, to, g.gl_pathv[i], abs_dst,
+ NULL, pflag, 1) == -1)
+ err = -1;
+ } else {
+ if (do_crossload(from, to, g.gl_pathv[i], abs_dst, NULL,
+ pflag) == -1)
+ err = -1;
+ }
+ free(abs_dst);
+ abs_dst = NULL;
+ free(tmp);
+ tmp = NULL;
+ }
+
+out:
+ free(abs_src);
+ free(abs_dst);
+ free(target);
+ free(tmp);
+ globfree(&g);
+ if (err == -1)
+ fatal("Failed to download file '%s'", src);
+}
+
int
response(void)
{