version 1.30.2.1, 2000/09/01 18:23:22 |
version 1.30.2.2, 2000/11/08 21:31:12 |
|
|
/* |
/* |
|
* scp - secure remote copy. This is basically patched BSD rcp which |
|
* uses ssh to do the data transfer (instead of using rcmd). |
* |
* |
* scp - secure remote copy. This is basically patched BSD rcp which uses ssh |
* NOTE: This version should NOT be suid root. (This uses ssh to |
* to do the data transfer (instead of using rcmd). |
* do the transfer and ssh has the necessary privileges.) |
* |
* |
* NOTE: This version should NOT be suid root. (This uses ssh to 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> |
* |
* |
*/ |
* As far as I am concerned, the code I have written for this software |
|
* can be used freely for any purpose. Any derived versions of this |
|
* software must be clearly marked as such, and if the derived work is |
|
* incompatible with the protocol description in the RFC file, it must be |
|
* called by a name other than "ssh" or "Secure Shell". |
|
*/ |
|
/* |
|
* Copyright (c) 1999 Theo de Raadt. All rights reserved. |
|
* Copyright (c) 1999 Aaron Campbell. All rights reserved. |
|
* |
|
* Redistribution and use in source and binary forms, with or without |
|
* modification, are permitted provided that the following conditions |
|
* are met: |
|
* 1. Redistributions of source code must retain the above copyright |
|
* notice, this list of conditions and the following disclaimer. |
|
* 2. Redistributions in binary form must reproduce the above copyright |
|
* notice, this list of conditions and the following disclaimer in the |
|
* documentation and/or other materials provided with the distribution. |
|
* |
|
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR |
|
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES |
|
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. |
|
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, |
|
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT |
|
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
|
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
|
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF |
|
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
|
*/ |
|
|
/* |
/* |
* Parts from: |
* Parts from: |
|
|
|
|
#include "ssh.h" |
#include "ssh.h" |
#include "xmalloc.h" |
#include "xmalloc.h" |
#include <utime.h> |
|
|
|
#define _PATH_CP "cp" |
#define _PATH_CP "cp" |
|
|
|
|
int getttywidth(void); |
int getttywidth(void); |
int do_cmd(char *host, char *remuser, char *cmd, int *fdin, int *fdout, int argc); |
int do_cmd(char *host, char *remuser, char *cmd, int *fdin, int *fdout, int argc); |
|
|
|
/* setup arguments for the call to ssh */ |
|
void addargs(char *fmt, ...) __attribute__((format(printf, 1, 2))); |
|
|
/* Time a transfer started. */ |
/* Time a transfer started. */ |
static struct timeval start; |
static struct timeval start; |
|
|
|
|
/* Name of current file being transferred. */ |
/* Name of current file being transferred. */ |
char *curfile; |
char *curfile; |
|
|
/* This is set to non-zero if IPv4 is desired. */ |
|
int IPv4 = 0; |
|
|
|
/* This is set to non-zero if IPv6 is desired. */ |
|
int IPv6 = 0; |
|
|
|
/* This is set to non-zero to enable verbose mode. */ |
/* This is set to non-zero to enable verbose mode. */ |
int verbose_mode = 0; |
int verbose_mode = 0; |
|
|
|
|
/* This is set to zero if the progressmeter is not desired. */ |
/* This is set to zero if the progressmeter is not desired. */ |
int showprogress = 1; |
int showprogress = 1; |
|
|
/* This is set to non-zero if running in batch mode (that is, password |
|
and passphrase queries are not allowed). */ |
|
int batchmode = 0; |
|
|
|
/* This is set to the cipher type string if given on the command line. */ |
|
char *cipher = NULL; |
|
|
|
/* This is set to the RSA authentication identity file name if given on |
|
the command line. */ |
|
char *identity = NULL; |
|
|
|
/* This is the port to use in contacting the remote site (is non-NULL). */ |
|
char *port = NULL; |
|
|
|
/* This is the program to execute for the secured connection. ("ssh" or -S) */ |
/* This is the program to execute for the secured connection. ("ssh" or -S) */ |
char *ssh_program = SSH_PROGRAM; |
char *ssh_program = SSH_PROGRAM; |
|
|
|
/* This is the list of arguments that scp passes to ssh */ |
|
struct { |
|
char **list; |
|
int num; |
|
int nalloc; |
|
} args; |
|
|
/* |
/* |
* This function executes the given command as the specified user on the |
* This function executes the given command as the specified user on the |
* given host. This returns < 0 if execution fails, and >= 0 otherwise. This |
* given host. This returns < 0 if execution fails, and >= 0 otherwise. This |
|
|
int pin[2], pout[2], reserved[2]; |
int pin[2], pout[2], reserved[2]; |
|
|
if (verbose_mode) |
if (verbose_mode) |
fprintf(stderr, "Executing: host %s, user %s, command %s\n", |
fprintf(stderr, "Executing: program %s host %s, user %s, command %s\n", |
host, remuser ? remuser : "(unspecified)", cmd); |
ssh_program, host, remuser ? remuser : "(unspecified)", cmd); |
|
|
/* |
/* |
* Reserve two descriptors so that the real pipes won't get |
* Reserve two descriptors so that the real pipes won't get |
|
|
close(reserved[1]); |
close(reserved[1]); |
|
|
/* For a child to execute the command on the remote host using ssh. */ |
/* For a child to execute the command on the remote host using ssh. */ |
if (fork() == 0) { |
if (fork() == 0) { |
char *args[100]; /* XXX careful */ |
|
unsigned int i; |
|
|
|
/* Child. */ |
/* Child. */ |
close(pin[1]); |
close(pin[1]); |
close(pout[0]); |
close(pout[0]); |
|
|
close(pin[0]); |
close(pin[0]); |
close(pout[1]); |
close(pout[1]); |
|
|
i = 0; |
args.list[0] = ssh_program; |
args[i++] = ssh_program; |
if (remuser != NULL) |
args[i++] = "-x"; |
addargs("-l%s", remuser); |
args[i++] = "-oFallBackToRsh no"; |
addargs("%s", host); |
if (IPv4) |
addargs("%s", cmd); |
args[i++] = "-4"; |
|
if (IPv6) |
|
args[i++] = "-6"; |
|
if (verbose_mode) |
|
args[i++] = "-v"; |
|
if (compress) |
|
args[i++] = "-C"; |
|
if (batchmode) |
|
args[i++] = "-oBatchMode yes"; |
|
if (cipher != NULL) { |
|
args[i++] = "-c"; |
|
args[i++] = cipher; |
|
} |
|
if (identity != NULL) { |
|
args[i++] = "-i"; |
|
args[i++] = identity; |
|
} |
|
if (port != NULL) { |
|
args[i++] = "-p"; |
|
args[i++] = port; |
|
} |
|
if (remuser != NULL) { |
|
args[i++] = "-l"; |
|
args[i++] = remuser; |
|
} |
|
args[i++] = host; |
|
args[i++] = cmd; |
|
args[i++] = NULL; |
|
|
|
execvp(ssh_program, args); |
execvp(ssh_program, args.list); |
perror(ssh_program); |
perror(ssh_program); |
exit(1); |
exit(1); |
} |
} |
|
|
extern char *optarg; |
extern char *optarg; |
extern int optind; |
extern int optind; |
|
|
|
args.list = NULL; |
|
addargs("ssh"); /* overwritten with ssh_program */ |
|
addargs("-x"); |
|
addargs("-oFallBackToRsh no"); |
|
|
fflag = tflag = 0; |
fflag = tflag = 0; |
while ((ch = getopt(argc, argv, "dfprtvBCc:i:P:q46S")) != EOF) |
while ((ch = getopt(argc, argv, "dfprtvBCc:i:P:q46S:o:")) != EOF) |
switch (ch) { |
switch (ch) { |
/* User-visible flags. */ |
/* User-visible flags. */ |
case '4': |
case '4': |
IPv4 = 1; |
|
break; |
|
case '6': |
case '6': |
IPv6 = 1; |
case 'C': |
|
addargs("-%c", ch); |
break; |
break; |
case 'p': |
case 'o': |
pflag = 1; |
case 'c': |
|
case 'i': |
|
addargs("-%c%s", ch, optarg); |
break; |
break; |
case 'P': |
case 'P': |
port = optarg; |
addargs("-p%s", optarg); |
break; |
break; |
|
case 'B': |
|
addargs("-oBatchmode yes"); |
|
break; |
|
case 'p': |
|
pflag = 1; |
|
break; |
case 'r': |
case 'r': |
iamrecursive = 1; |
iamrecursive = 1; |
break; |
break; |
case 'S': |
case 'S': |
ssh_program = optarg; |
ssh_program = xstrdup(optarg); |
break; |
break; |
|
case 'v': |
|
verbose_mode = 1; |
|
break; |
|
case 'q': |
|
showprogress = 0; |
|
break; |
|
|
/* Server options. */ |
/* Server options. */ |
case 'd': |
case 'd': |
|
|
iamremote = 1; |
iamremote = 1; |
tflag = 1; |
tflag = 1; |
break; |
break; |
case 'c': |
|
cipher = optarg; |
|
break; |
|
case 'i': |
|
identity = optarg; |
|
break; |
|
case 'v': |
|
verbose_mode = 1; |
|
break; |
|
case 'B': |
|
batchmode = 1; |
|
break; |
|
case 'C': |
|
compress = 1; |
|
break; |
|
case 'q': |
|
showprogress = 0; |
|
break; |
|
case '?': |
case '?': |
default: |
default: |
usage(); |
usage(); |
|
|
off_t size; |
off_t size; |
int setimes, targisdir, wrerrno = 0; |
int setimes, targisdir, wrerrno = 0; |
char ch, *cp, *np, *targ, *why, *vect[1], buf[2048]; |
char ch, *cp, *np, *targ, *why, *vect[1], buf[2048]; |
struct utimbuf ut; |
|
int dummy_usec; |
int dummy_usec; |
|
struct timeval tv[2]; |
|
|
#define SCREWUP(str) { why = str; goto screwup; } |
#define SCREWUP(str) { why = str; goto screwup; } |
|
|
|
|
if (*cp == 'T') { |
if (*cp == 'T') { |
setimes++; |
setimes++; |
cp++; |
cp++; |
getnum(ut.modtime); |
getnum(tv[1].tv_sec); |
if (*cp++ != ' ') |
if (*cp++ != ' ') |
SCREWUP("mtime.sec not delimited"); |
SCREWUP("mtime.sec not delimited"); |
getnum(dummy_usec); |
getnum(dummy_usec); |
|
tv[1].tv_usec = 0; |
if (*cp++ != ' ') |
if (*cp++ != ' ') |
SCREWUP("mtime.usec not delimited"); |
SCREWUP("mtime.usec not delimited"); |
getnum(ut.actime); |
getnum(tv[0].tv_sec); |
if (*cp++ != ' ') |
if (*cp++ != ' ') |
SCREWUP("atime.sec not delimited"); |
SCREWUP("atime.sec not delimited"); |
getnum(dummy_usec); |
getnum(dummy_usec); |
|
tv[0].tv_usec = 0; |
if (*cp++ != '\0') |
if (*cp++ != '\0') |
SCREWUP("atime.usec not delimited"); |
SCREWUP("atime.usec not delimited"); |
(void) atomicio(write, remout, "", 1); |
(void) atomicio(write, remout, "", 1); |
|
|
sink(1, vect); |
sink(1, vect); |
if (setimes) { |
if (setimes) { |
setimes = 0; |
setimes = 0; |
if (utime(np, &ut) < 0) |
if (utimes(np, tv) < 0) |
run_err("%s: set times: %s", |
run_err("%s: set times: %s", |
np, strerror(errno)); |
np, strerror(errno)); |
} |
} |
|
|
amt = size - i; |
amt = size - i; |
count += amt; |
count += amt; |
do { |
do { |
j = atomicio(read, remin, cp, amt); |
j = read(remin, cp, amt); |
if (j <= 0) { |
if (j == -1 && (errno == EINTR || errno == EAGAIN)) { |
|
continue; |
|
} else if (j <= 0) { |
run_err("%s", j ? strerror(errno) : |
run_err("%s", j ? strerror(errno) : |
"dropped connection"); |
"dropped connection"); |
exit(1); |
exit(1); |
|
|
(void) response(); |
(void) response(); |
if (setimes && wrerr == NO) { |
if (setimes && wrerr == NO) { |
setimes = 0; |
setimes = 0; |
if (utime(np, &ut) < 0) { |
if (utimes(np, tv) < 0) { |
run_err("%s: set times: %s", |
run_err("%s: set times: %s", |
np, strerror(errno)); |
np, strerror(errno)); |
wrerr = DISPLAYED; |
wrerr = DISPLAYED; |
|
|
return (winsize.ws_col ? winsize.ws_col : 80); |
return (winsize.ws_col ? winsize.ws_col : 80); |
else |
else |
return (80); |
return (80); |
|
} |
|
|
|
void |
|
addargs(char *fmt, ...) |
|
{ |
|
va_list ap; |
|
char buf[1024]; |
|
|
|
va_start(ap, fmt); |
|
vsnprintf(buf, sizeof(buf), fmt, ap); |
|
va_end(ap); |
|
|
|
if (args.list == NULL) { |
|
args.nalloc = 32; |
|
args.num = 0; |
|
args.list = xmalloc(args.nalloc * sizeof(char *)); |
|
} else if (args.num+2 >= args.nalloc) { |
|
args.nalloc *= 2; |
|
args.list = xrealloc(args.list, args.nalloc * sizeof(char *)); |
|
} |
|
args.list[args.num++] = xstrdup(buf); |
|
args.list[args.num] = NULL; |
} |
} |