diff options
Diffstat (limited to 'crypto/openssh/scp.c')
| -rw-r--r-- | crypto/openssh/scp.c | 163 |
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); } |
