aboutsummaryrefslogtreecommitdiff
path: root/crypto/openssh/scp.c
diff options
context:
space:
mode:
Diffstat (limited to 'crypto/openssh/scp.c')
-rw-r--r--crypto/openssh/scp.c163
1 files changed, 122 insertions, 41 deletions
diff --git a/crypto/openssh/scp.c b/crypto/openssh/scp.c
index f9ca5d393294..1adff5cec222 100644
--- a/crypto/openssh/scp.c
+++ b/crypto/openssh/scp.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: scp.c,v 1.248 2022/05/13 06:31:50 djm Exp $ */
+/* $OpenBSD: scp.c,v 1.252 2023/01/10 23:22:15 millert Exp $ */
/*
* scp - secure remote copy. This is basically patched BSD rcp which
* uses ssh to do the data transfer (instead of using rcmd).
@@ -106,6 +106,9 @@
#include <libgen.h>
#endif
#include <limits.h>
+#ifdef HAVE_UTIL_H
+# include <util.h>
+#endif
#include <locale.h>
#include <pwd.h>
#include <signal.h>
@@ -176,6 +179,10 @@ char *ssh_program = _PATH_SSH_PROGRAM;
pid_t do_cmd_pid = -1;
pid_t do_cmd_pid2 = -1;
+/* SFTP copy parameters */
+size_t sftp_copy_buflen;
+size_t sftp_nrequests;
+
/* Needed for sftp */
volatile sig_atomic_t interrupted = 0;
@@ -272,7 +279,11 @@ int
do_cmd(char *program, char *host, char *remuser, int port, int subsystem,
char *cmd, int *fdin, int *fdout, pid_t *pid)
{
- int pin[2], pout[2], reserved[2];
+#ifdef USE_PIPES
+ int pin[2], pout[2];
+#else
+ int sv[2];
+#endif
if (verbose_mode)
fmprintf(stderr,
@@ -283,22 +294,14 @@ do_cmd(char *program, char *host, char *remuser, int port, int subsystem,
if (port == -1)
port = sshport;
- /*
- * Reserve two descriptors so that the real pipes won't get
- * descriptors 0 and 1 because that will screw up dup2 below.
- */
- if (pipe(reserved) == -1)
+#ifdef USE_PIPES
+ if (pipe(pin) == -1 || pipe(pout) == -1)
fatal("pipe: %s", strerror(errno));
-
+#else
/* Create a socket pair for communicating with ssh. */
- if (pipe(pin) == -1)
- fatal("pipe: %s", strerror(errno));
- if (pipe(pout) == -1)
- fatal("pipe: %s", strerror(errno));
-
- /* Free the reserved descriptors. */
- close(reserved[0]);
- close(reserved[1]);
+ if (socketpair(AF_UNIX, SOCK_STREAM, 0, sv) == -1)
+ fatal("socketpair: %s", strerror(errno));
+#endif
ssh_signal(SIGTSTP, suspchild);
ssh_signal(SIGTTIN, suspchild);
@@ -306,15 +309,30 @@ do_cmd(char *program, char *host, char *remuser, int port, int subsystem,
/* Fork a child to execute the command on the remote host using ssh. */
*pid = fork();
- if (*pid == 0) {
+ switch (*pid) {
+ case -1:
+ fatal("fork: %s", strerror(errno));
+ case 0:
/* Child. */
+#ifdef USE_PIPES
+ if (dup2(pin[0], STDIN_FILENO) == -1 ||
+ dup2(pout[1], STDOUT_FILENO) == -1) {
+ error("dup2: %s", strerror(errno));
+ _exit(1);
+ }
+ close(pin[0]);
close(pin[1]);
close(pout[0]);
- dup2(pin[0], 0);
- dup2(pout[1], 1);
- close(pin[0]);
close(pout[1]);
-
+#else
+ if (dup2(sv[0], STDIN_FILENO) == -1 ||
+ dup2(sv[0], STDOUT_FILENO) == -1) {
+ error("dup2: %s", strerror(errno));
+ _exit(1);
+ }
+ close(sv[0]);
+ close(sv[1]);
+#endif
replacearg(&args, 0, "%s", program);
if (port != -1) {
addargs(&args, "-p");
@@ -332,19 +350,24 @@ do_cmd(char *program, char *host, char *remuser, int port, int subsystem,
execvp(program, args.list);
perror(program);
- exit(1);
- } else if (*pid == -1) {
- fatal("fork: %s", strerror(errno));
+ _exit(1);
+ default:
+ /* Parent. Close the other side, and return the local side. */
+#ifdef USE_PIPES
+ close(pin[0]);
+ close(pout[1]);
+ *fdout = pin[1];
+ *fdin = pout[0];
+#else
+ close(sv[0]);
+ *fdin = sv[1];
+ *fdout = sv[1];
+#endif
+ ssh_signal(SIGTERM, killchild);
+ ssh_signal(SIGINT, killchild);
+ ssh_signal(SIGHUP, killchild);
+ return 0;
}
- /* Parent. Close the other side, and return the local side. */
- close(pin[0]);
- *fdout = pin[1];
- close(pout[1]);
- *fdin = pout[0];
- ssh_signal(SIGTERM, killchild);
- ssh_signal(SIGINT, killchild);
- ssh_signal(SIGHUP, killchild);
- return 0;
}
/*
@@ -444,13 +467,14 @@ void throughlocal_sftp(struct sftp_conn *, struct sftp_conn *,
int
main(int argc, char **argv)
{
- int ch, fflag, tflag, status, n;
+ int ch, fflag, tflag, status, r, n;
char **newargv, *argv0;
const char *errstr;
extern char *optarg;
extern int optind;
enum scp_mode_e mode = MODE_SFTP;
char *sftp_direct = NULL;
+ long long llv;
/* Ensure that fds 0, 1 and 2 are open or directed to /dev/null */
sanitise_stdfd();
@@ -480,7 +504,7 @@ main(int argc, char **argv)
fflag = Tflag = tflag = 0;
while ((ch = getopt(argc, argv,
- "12346ABCTdfOpqRrstvD:F:J:M:P:S:c:i:l:o:")) != -1) {
+ "12346ABCTdfOpqRrstvD:F:J:M:P:S:c:i:l:o:X:")) != -1) {
switch (ch) {
/* User-visible flags. */
case '1':
@@ -561,6 +585,31 @@ main(int argc, char **argv)
addargs(&remote_remote_args, "-q");
showprogress = 0;
break;
+ case 'X':
+ /* Please keep in sync with sftp.c -X */
+ if (strncmp(optarg, "buffer=", 7) == 0) {
+ r = scan_scaled(optarg + 7, &llv);
+ if (r == 0 && (llv <= 0 || llv > 256 * 1024)) {
+ r = -1;
+ errno = EINVAL;
+ }
+ if (r == -1) {
+ fatal("Invalid buffer size \"%s\": %s",
+ optarg + 7, strerror(errno));
+ }
+ sftp_copy_buflen = (size_t)llv;
+ } else if (strncmp(optarg, "nrequests=", 10) == 0) {
+ llv = strtonum(optarg + 10, 1, 256 * 1024,
+ &errstr);
+ if (errstr != NULL) {
+ fatal("Invalid number of requests "
+ "\"%s\": %s", optarg + 10, errstr);
+ }
+ sftp_nrequests = (size_t)llv;
+ } else {
+ fatal("Invalid -X option");
+ }
+ break;
/* Server options. */
case 'd':
@@ -972,7 +1021,8 @@ do_sftp_connect(char *host, char *user, int port, char *sftp_direct,
reminp, remoutp, pidp) < 0)
return NULL;
}
- return do_init(*reminp, *remoutp, 32768, 64, limit_kbps);
+ return do_init(*reminp, *remoutp,
+ sftp_copy_buflen, sftp_nrequests, limit_kbps);
}
void
@@ -1505,7 +1555,8 @@ sink_sftp(int argc, char *dst, const char *src, struct sftp_conn *conn)
}
debug3_f("copying remote %s to local %s", abs_src, dst);
- if ((r = remote_glob(conn, abs_src, GLOB_MARK, NULL, &g)) != 0) {
+ if ((r = remote_glob(conn, abs_src, GLOB_NOCHECK|GLOB_MARK,
+ NULL, &g)) != 0) {
if (r == GLOB_NOSPACE)
error("%s: too many glob matches", src);
else
@@ -1514,6 +1565,20 @@ sink_sftp(int argc, char *dst, const char *src, struct sftp_conn *conn)
goto out;
}
+ /* Did we actually get any matches back from the glob? */
+ if (g.gl_matchc == 0 && g.gl_pathc == 1 && g.gl_pathv[0] != 0) {
+ /*
+ * If nothing matched but a path returned, then it's probably
+ * a GLOB_NOCHECK result. Check whether the unglobbed path
+ * exists so we can give a nice error message early.
+ */
+ if (do_stat(conn, g.gl_pathv[0], 1) == NULL) {
+ error("%s: %s", src, strerror(ENOENT));
+ err = -1;
+ goto out;
+ }
+ }
+
if ((r = stat(dst, &st)) != 0)
debug2_f("stat local \"%s\": %s", dst, strerror(errno));
dst_is_dir = r == 0 && S_ISDIR(st.st_mode);
@@ -1731,7 +1796,8 @@ sink(int argc, char **argv, const char *src)
}
if (npatterns > 0) {
for (n = 0; n < npatterns; n++) {
- if (fnmatch(patterns[n], cp, 0) == 0)
+ if (strcmp(patterns[n], cp) == 0 ||
+ fnmatch(patterns[n], cp, 0) == 0)
break;
}
if (n >= npatterns)
@@ -1922,7 +1988,8 @@ throughlocal_sftp(struct sftp_conn *from, struct sftp_conn *to,
}
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 = remote_glob(from, abs_src, GLOB_NOCHECK|GLOB_MARK,
+ NULL, &g)) != 0) {
if (r == GLOB_NOSPACE)
error("%s: too many glob matches", src);
else
@@ -1931,6 +1998,20 @@ throughlocal_sftp(struct sftp_conn *from, struct sftp_conn *to,
goto out;
}
+ /* Did we actually get any matches back from the glob? */
+ if (g.gl_matchc == 0 && g.gl_pathc == 1 && g.gl_pathv[0] != 0) {
+ /*
+ * If nothing matched but a path returned, then it's probably
+ * a GLOB_NOCHECK result. Check whether the unglobbed path
+ * exists so we can give a nice error message early.
+ */
+ if (do_stat(from, g.gl_pathv[0], 1) == NULL) {
+ error("%s: %s", src, strerror(ENOENT));
+ err = -1;
+ goto out;
+ }
+ }
+
for (i = 0; g.gl_pathv[i] && !interrupted; i++) {
tmp = xstrdup(g.gl_pathv[i]);
if ((filename = basename(tmp)) == NULL) {
@@ -2013,8 +2094,8 @@ usage(void)
{
(void) fprintf(stderr,
"usage: scp [-346ABCOpqRrsTv] [-c cipher] [-D sftp_server_path] [-F ssh_config]\n"
- " [-i identity_file] [-J destination] [-l limit]\n"
- " [-o ssh_option] [-P port] [-S program] source ... target\n");
+ " [-i identity_file] [-J destination] [-l limit] [-o ssh_option]\n"
+ " [-P port] [-S program] [-X sftp_option] source ... target\n");
exit(1);
}