Changeset View
Changeset View
Standalone View
Standalone View
head/crypto/openssh/scp.c
/* $OpenBSD: scp.c,v 1.197 2018/06/01 04:31:48 dtucker Exp $ */ | /* $OpenBSD: scp.c,v 1.203 2019/01/27 07:14:11 jmc Exp $ */ | ||||
/* | /* | ||||
* scp - secure remote copy. This is basically patched BSD rcp which | * scp - secure remote copy. This is basically patched BSD rcp which | ||||
* uses ssh to do the data transfer (instead of using rcmd). | * uses ssh to do the data transfer (instead of using rcmd). | ||||
* | * | ||||
* NOTE: This version should NOT be suid root. (This uses ssh to | * NOTE: This version should NOT be suid root. (This uses ssh to | ||||
* do the transfer and ssh has the necessary privileges.) | * do the transfer and ssh has the necessary privileges.) | ||||
* | * | ||||
* 1995 Timo Rinne <tri@iki.fi>, Tatu Ylonen <ylo@cs.hut.fi> | * 1995 Timo Rinne <tri@iki.fi>, Tatu Ylonen <ylo@cs.hut.fi> | ||||
▲ Show 20 Lines • Show All 79 Lines • ▼ Show 20 Lines | |||||
#endif | #endif | ||||
#include <sys/wait.h> | #include <sys/wait.h> | ||||
#include <sys/uio.h> | #include <sys/uio.h> | ||||
#include <ctype.h> | #include <ctype.h> | ||||
#include <dirent.h> | #include <dirent.h> | ||||
#include <errno.h> | #include <errno.h> | ||||
#include <fcntl.h> | #include <fcntl.h> | ||||
#include <fnmatch.h> | |||||
#include <limits.h> | #include <limits.h> | ||||
#include <locale.h> | #include <locale.h> | ||||
#include <pwd.h> | #include <pwd.h> | ||||
#include <signal.h> | #include <signal.h> | ||||
#include <stdarg.h> | #include <stdarg.h> | ||||
#ifdef HAVE_STDINT_H | #ifdef HAVE_STDINT_H | ||||
#include <stdint.h> | #include <stdint.h> | ||||
#endif | #endif | ||||
▲ Show 20 Lines • Show All 265 Lines • ▼ Show 20 Lines | |||||
void lostconn(int); | void lostconn(int); | ||||
int okname(char *); | int okname(char *); | ||||
void run_err(const char *,...); | void run_err(const char *,...); | ||||
void verifydir(char *); | void verifydir(char *); | ||||
struct passwd *pwd; | struct passwd *pwd; | ||||
uid_t userid; | uid_t userid; | ||||
int errs, remin, remout; | int errs, remin, remout; | ||||
int pflag, iamremote, iamrecursive, targetshouldbedirectory; | int Tflag, pflag, iamremote, iamrecursive, targetshouldbedirectory; | ||||
#define CMDNEEDS 64 | #define CMDNEEDS 64 | ||||
char cmd[CMDNEEDS]; /* must hold "rcp -r -p -d\0" */ | char cmd[CMDNEEDS]; /* must hold "rcp -r -p -d\0" */ | ||||
int response(void); | int response(void); | ||||
void rsource(char *, struct stat *); | void rsource(char *, struct stat *); | ||||
void sink(int, char *[]); | void sink(int, char *[], const char *); | ||||
void source(int, char *[]); | void source(int, char *[]); | ||||
void tolocal(int, char *[]); | void tolocal(int, char *[]); | ||||
void toremote(int, char *[]); | void toremote(int, char *[]); | ||||
void usage(void); | void usage(void); | ||||
int | int | ||||
main(int argc, char **argv) | main(int argc, char **argv) | ||||
{ | { | ||||
Show All 22 Lines | main(int argc, char **argv) | ||||
addargs(&args, "%s", ssh_program); | addargs(&args, "%s", ssh_program); | ||||
addargs(&args, "-x"); | addargs(&args, "-x"); | ||||
addargs(&args, "-oForwardAgent=no"); | addargs(&args, "-oForwardAgent=no"); | ||||
addargs(&args, "-oPermitLocalCommand=no"); | addargs(&args, "-oPermitLocalCommand=no"); | ||||
addargs(&args, "-oClearAllForwardings=yes"); | addargs(&args, "-oClearAllForwardings=yes"); | ||||
addargs(&args, "-oRemoteCommand=none"); | addargs(&args, "-oRemoteCommand=none"); | ||||
addargs(&args, "-oRequestTTY=no"); | addargs(&args, "-oRequestTTY=no"); | ||||
fflag = tflag = 0; | fflag = Tflag = tflag = 0; | ||||
while ((ch = getopt(argc, argv, "dfl:prtvBCc:i:P:q12346S:o:F:")) != -1) | while ((ch = getopt(argc, argv, | ||||
"dfl:prtTvBCc:i:P:q12346S:o:F:")) != -1) { | |||||
switch (ch) { | switch (ch) { | ||||
/* User-visible flags. */ | /* User-visible flags. */ | ||||
case '1': | case '1': | ||||
fatal("SSH protocol v.1 is no longer supported"); | fatal("SSH protocol v.1 is no longer supported"); | ||||
break; | break; | ||||
case '2': | case '2': | ||||
/* Ignored */ | /* Ignored */ | ||||
break; | break; | ||||
▲ Show 20 Lines • Show All 62 Lines • ▼ Show 20 Lines | case 'f': /* "from" */ | ||||
break; | break; | ||||
case 't': /* "to" */ | case 't': /* "to" */ | ||||
iamremote = 1; | iamremote = 1; | ||||
tflag = 1; | tflag = 1; | ||||
#ifdef HAVE_CYGWIN | #ifdef HAVE_CYGWIN | ||||
setmode(0, O_BINARY); | setmode(0, O_BINARY); | ||||
#endif | #endif | ||||
break; | break; | ||||
case 'T': | |||||
Tflag = 1; | |||||
break; | |||||
default: | default: | ||||
usage(); | usage(); | ||||
} | } | ||||
} | |||||
argc -= optind; | argc -= optind; | ||||
argv += optind; | argv += optind; | ||||
if ((pwd = getpwuid(userid = getuid())) == NULL) | if ((pwd = getpwuid(userid = getuid())) == NULL) | ||||
fatal("unknown user %u", (u_int) userid); | fatal("unknown user %u", (u_int) userid); | ||||
if (!isatty(STDOUT_FILENO)) | if (!isatty(STDOUT_FILENO)) | ||||
showprogress = 0; | showprogress = 0; | ||||
Show All 14 Lines | #endif | ||||
if (fflag) { | if (fflag) { | ||||
/* Follow "protocol", send data. */ | /* Follow "protocol", send data. */ | ||||
(void) response(); | (void) response(); | ||||
source(argc, argv); | source(argc, argv); | ||||
exit(errs != 0); | exit(errs != 0); | ||||
} | } | ||||
if (tflag) { | if (tflag) { | ||||
/* Receive data. */ | /* Receive data. */ | ||||
sink(argc, argv); | sink(argc, argv, NULL); | ||||
exit(errs != 0); | exit(errs != 0); | ||||
} | } | ||||
if (argc < 2) | if (argc < 2) | ||||
usage(); | usage(); | ||||
if (argc > 2) | if (argc > 2) | ||||
targetshouldbedirectory = 1; | targetshouldbedirectory = 1; | ||||
remin = remout = -1; | remin = remout = -1; | ||||
▲ Show 20 Lines • Show All 240 Lines • ▼ Show 20 Lines | for (i = 0; i < argc - 1; i++) { | ||||
xasprintf(&bp, "%s -f %s%s", | xasprintf(&bp, "%s -f %s%s", | ||||
cmd, *src == '-' ? "-- " : "", src); | cmd, *src == '-' ? "-- " : "", src); | ||||
if (do_cmd(host, suser, sport, bp, &remin, &remout) < 0) { | if (do_cmd(host, suser, sport, bp, &remin, &remout) < 0) { | ||||
free(bp); | free(bp); | ||||
++errs; | ++errs; | ||||
continue; | continue; | ||||
} | } | ||||
free(bp); | free(bp); | ||||
sink(1, argv + argc - 1); | sink(1, argv + argc - 1, src); | ||||
(void) close(remin); | (void) close(remin); | ||||
remin = remout = -1; | remin = remout = -1; | ||||
} | } | ||||
free(suser); | free(suser); | ||||
free(host); | free(host); | ||||
free(src); | free(src); | ||||
} | } | ||||
▲ Show 20 Lines • Show All 159 Lines • ▼ Show 20 Lines | |||||
} | } | ||||
#define TYPE_OVERFLOW(type, val) \ | #define TYPE_OVERFLOW(type, val) \ | ||||
((sizeof(type) == 4 && (val) > INT32_MAX) || \ | ((sizeof(type) == 4 && (val) > INT32_MAX) || \ | ||||
(sizeof(type) == 8 && (val) > INT64_MAX) || \ | (sizeof(type) == 8 && (val) > INT64_MAX) || \ | ||||
(sizeof(type) != 4 && sizeof(type) != 8)) | (sizeof(type) != 4 && sizeof(type) != 8)) | ||||
void | void | ||||
sink(int argc, char **argv) | sink(int argc, char **argv, const char *src) | ||||
{ | { | ||||
static BUF buffer; | static BUF buffer; | ||||
struct stat stb; | struct stat stb; | ||||
enum { | enum { | ||||
YES, NO, DISPLAYED | YES, NO, DISPLAYED | ||||
} wrerr; | } wrerr; | ||||
BUF *bp; | BUF *bp; | ||||
off_t i; | off_t i; | ||||
size_t j, count; | size_t j, count; | ||||
int amt, exists, first, ofd; | int amt, exists, first, ofd; | ||||
mode_t mode, omode, mask; | mode_t mode, omode, mask; | ||||
off_t size, statbytes; | off_t size, statbytes; | ||||
unsigned long long ull; | unsigned long long ull; | ||||
int setimes, targisdir, wrerrno = 0; | int setimes, targisdir, wrerrno = 0; | ||||
char ch, *cp, *np, *targ, *why, *vect[1], buf[2048], visbuf[2048]; | char ch, *cp, *np, *targ, *why, *vect[1], buf[2048], visbuf[2048]; | ||||
char *src_copy = NULL, *restrict_pattern = NULL; | |||||
struct timeval tv[2]; | struct timeval tv[2]; | ||||
#define atime tv[0] | #define atime tv[0] | ||||
#define mtime tv[1] | #define mtime tv[1] | ||||
#define SCREWUP(str) { why = str; goto screwup; } | #define SCREWUP(str) { why = str; goto screwup; } | ||||
if (TYPE_OVERFLOW(time_t, 0) || TYPE_OVERFLOW(off_t, 0)) | if (TYPE_OVERFLOW(time_t, 0) || TYPE_OVERFLOW(off_t, 0)) | ||||
SCREWUP("Unexpected off_t/time_t size"); | SCREWUP("Unexpected off_t/time_t size"); | ||||
setimes = targisdir = 0; | setimes = targisdir = 0; | ||||
mask = umask(0); | mask = umask(0); | ||||
if (!pflag) | if (!pflag) | ||||
(void) umask(mask); | (void) umask(mask); | ||||
if (argc != 1) { | if (argc != 1) { | ||||
run_err("ambiguous target"); | run_err("ambiguous target"); | ||||
exit(1); | exit(1); | ||||
} | } | ||||
targ = *argv; | targ = *argv; | ||||
if (targetshouldbedirectory) | if (targetshouldbedirectory) | ||||
verifydir(targ); | verifydir(targ); | ||||
(void) atomicio(vwrite, remout, "", 1); | (void) atomicio(vwrite, remout, "", 1); | ||||
if (stat(targ, &stb) == 0 && S_ISDIR(stb.st_mode)) | if (stat(targ, &stb) == 0 && S_ISDIR(stb.st_mode)) | ||||
targisdir = 1; | targisdir = 1; | ||||
if (src != NULL && !iamrecursive && !Tflag) { | |||||
/* | |||||
* Prepare to try to restrict incoming filenames to match | |||||
* the requested destination file glob. | |||||
*/ | |||||
if ((src_copy = strdup(src)) == NULL) | |||||
fatal("strdup failed"); | |||||
if ((restrict_pattern = strrchr(src_copy, '/')) != NULL) { | |||||
*restrict_pattern++ = '\0'; | |||||
} | |||||
} | |||||
for (first = 1;; first = 0) { | for (first = 1;; first = 0) { | ||||
cp = buf; | cp = buf; | ||||
if (atomicio(read, remin, cp, 1) != 1) | if (atomicio(read, remin, cp, 1) != 1) | ||||
return; | return; | ||||
if (*cp++ == '\n') | if (*cp++ == '\n') | ||||
SCREWUP("unexpected <newline>"); | SCREWUP("unexpected <newline>"); | ||||
do { | do { | ||||
if (atomicio(read, remin, &ch, sizeof(ch)) != sizeof(ch)) | if (atomicio(read, remin, &ch, sizeof(ch)) != sizeof(ch)) | ||||
▲ Show 20 Lines • Show All 88 Lines • ▼ Show 20 Lines | if (TYPE_OVERFLOW(off_t, ull)) | ||||
SCREWUP("size out of range"); | SCREWUP("size out of range"); | ||||
size = (off_t)ull; | size = (off_t)ull; | ||||
if (*cp == '\0' || strchr(cp, '/') != NULL || | if (*cp == '\0' || strchr(cp, '/') != NULL || | ||||
strcmp(cp, ".") == 0 || strcmp(cp, "..") == 0) { | strcmp(cp, ".") == 0 || strcmp(cp, "..") == 0) { | ||||
run_err("error: unexpected filename: %s", cp); | run_err("error: unexpected filename: %s", cp); | ||||
exit(1); | exit(1); | ||||
} | } | ||||
if (restrict_pattern != NULL && | |||||
fnmatch(restrict_pattern, cp, 0) != 0) | |||||
SCREWUP("filename does not match request"); | |||||
if (targisdir) { | if (targisdir) { | ||||
static char *namebuf; | static char *namebuf; | ||||
static size_t cursize; | static size_t cursize; | ||||
size_t need; | size_t need; | ||||
need = strlen(targ) + strlen(cp) + 250; | need = strlen(targ) + strlen(cp) + 250; | ||||
if (need > cursize) { | if (need > cursize) { | ||||
free(namebuf); | free(namebuf); | ||||
Show All 21 Lines | if (buf[0] == 'D') { | ||||
} else { | } else { | ||||
/* Handle copying from a read-only | /* Handle copying from a read-only | ||||
directory */ | directory */ | ||||
mod_flag = 1; | mod_flag = 1; | ||||
if (mkdir(np, mode | S_IRWXU) < 0) | if (mkdir(np, mode | S_IRWXU) < 0) | ||||
goto bad; | goto bad; | ||||
} | } | ||||
vect[0] = xstrdup(np); | vect[0] = xstrdup(np); | ||||
sink(1, vect); | sink(1, vect, src); | ||||
if (setimes) { | if (setimes) { | ||||
setimes = 0; | setimes = 0; | ||||
if (utimes(vect[0], tv) < 0) | if (utimes(vect[0], tv) < 0) | ||||
run_err("%s: set times: %s", | run_err("%s: set times: %s", | ||||
vect[0], strerror(errno)); | vect[0], strerror(errno)); | ||||
} | } | ||||
if (mod_flag) | if (mod_flag) | ||||
(void) chmod(vect[0], mode); | (void) chmod(vect[0], mode); | ||||
▲ Show 20 Lines • Show All 151 Lines • ▼ Show 20 Lines | response(void) | ||||
} | } | ||||
/* NOTREACHED */ | /* NOTREACHED */ | ||||
} | } | ||||
void | void | ||||
usage(void) | usage(void) | ||||
{ | { | ||||
(void) fprintf(stderr, | (void) fprintf(stderr, | ||||
"usage: scp [-346BCpqrv] [-c cipher] [-F ssh_config] [-i identity_file]\n" | "usage: scp [-346BCpqrTv] [-c cipher] [-F ssh_config] [-i identity_file]\n" | ||||
" [-l limit] [-o ssh_option] [-P port] [-S program] source ... target\n"); | " [-l limit] [-o ssh_option] [-P port] [-S program] source ... target\n"); | ||||
exit(1); | exit(1); | ||||
} | } | ||||
void | void | ||||
run_err(const char *fmt,...) | run_err(const char *fmt,...) | ||||
{ | { | ||||
static FILE *fp; | static FILE *fp; | ||||
▲ Show 20 Lines • Show All 99 Lines • Show Last 20 Lines |