1*7c478bd9Sstevel@tonic-gate /* 2*7c478bd9Sstevel@tonic-gate * Copyright 2004 Sun Microsystems, Inc. All rights reserved. 3*7c478bd9Sstevel@tonic-gate * Use is subject to license terms. 4*7c478bd9Sstevel@tonic-gate */ 5*7c478bd9Sstevel@tonic-gate /* 6*7c478bd9Sstevel@tonic-gate * Copyright (c) 2001,2002 Damien Miller. All rights reserved. 7*7c478bd9Sstevel@tonic-gate * 8*7c478bd9Sstevel@tonic-gate * Redistribution and use in source and binary forms, with or without 9*7c478bd9Sstevel@tonic-gate * modification, are permitted provided that the following conditions 10*7c478bd9Sstevel@tonic-gate * are met: 11*7c478bd9Sstevel@tonic-gate * 1. Redistributions of source code must retain the above copyright 12*7c478bd9Sstevel@tonic-gate * notice, this list of conditions and the following disclaimer. 13*7c478bd9Sstevel@tonic-gate * 2. Redistributions in binary form must reproduce the above copyright 14*7c478bd9Sstevel@tonic-gate * notice, this list of conditions and the following disclaimer in the 15*7c478bd9Sstevel@tonic-gate * documentation and/or other materials provided with the distribution. 16*7c478bd9Sstevel@tonic-gate * 17*7c478bd9Sstevel@tonic-gate * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 18*7c478bd9Sstevel@tonic-gate * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 19*7c478bd9Sstevel@tonic-gate * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 20*7c478bd9Sstevel@tonic-gate * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 21*7c478bd9Sstevel@tonic-gate * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 22*7c478bd9Sstevel@tonic-gate * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 23*7c478bd9Sstevel@tonic-gate * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 24*7c478bd9Sstevel@tonic-gate * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 25*7c478bd9Sstevel@tonic-gate * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 26*7c478bd9Sstevel@tonic-gate * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27*7c478bd9Sstevel@tonic-gate */ 28*7c478bd9Sstevel@tonic-gate 29*7c478bd9Sstevel@tonic-gate #include "includes.h" 30*7c478bd9Sstevel@tonic-gate 31*7c478bd9Sstevel@tonic-gate RCSID("$OpenBSD: sftp.c,v 1.31 2002/07/25 01:16:59 mouring Exp $"); 32*7c478bd9Sstevel@tonic-gate 33*7c478bd9Sstevel@tonic-gate #pragma ident "%Z%%M% %I% %E% SMI" 34*7c478bd9Sstevel@tonic-gate 35*7c478bd9Sstevel@tonic-gate /* XXX: short-form remote directory listings (like 'ls -C') */ 36*7c478bd9Sstevel@tonic-gate 37*7c478bd9Sstevel@tonic-gate #include "buffer.h" 38*7c478bd9Sstevel@tonic-gate #include "xmalloc.h" 39*7c478bd9Sstevel@tonic-gate #include "log.h" 40*7c478bd9Sstevel@tonic-gate #include "pathnames.h" 41*7c478bd9Sstevel@tonic-gate #include "misc.h" 42*7c478bd9Sstevel@tonic-gate 43*7c478bd9Sstevel@tonic-gate #include "sftp.h" 44*7c478bd9Sstevel@tonic-gate #include "sftp-common.h" 45*7c478bd9Sstevel@tonic-gate #include "sftp-client.h" 46*7c478bd9Sstevel@tonic-gate #include "sftp-int.h" 47*7c478bd9Sstevel@tonic-gate 48*7c478bd9Sstevel@tonic-gate #ifdef HAVE___PROGNAME 49*7c478bd9Sstevel@tonic-gate extern char *__progname; 50*7c478bd9Sstevel@tonic-gate #else 51*7c478bd9Sstevel@tonic-gate char *__progname; 52*7c478bd9Sstevel@tonic-gate #endif 53*7c478bd9Sstevel@tonic-gate 54*7c478bd9Sstevel@tonic-gate FILE* infile; 55*7c478bd9Sstevel@tonic-gate size_t copy_buffer_len = 32768; 56*7c478bd9Sstevel@tonic-gate size_t num_requests = 16; 57*7c478bd9Sstevel@tonic-gate 58*7c478bd9Sstevel@tonic-gate static void 59*7c478bd9Sstevel@tonic-gate connect_to_server(char *path, char **args, int *in, int *out, pid_t *sshpid) 60*7c478bd9Sstevel@tonic-gate { 61*7c478bd9Sstevel@tonic-gate int c_in, c_out; 62*7c478bd9Sstevel@tonic-gate 63*7c478bd9Sstevel@tonic-gate #ifdef USE_PIPES 64*7c478bd9Sstevel@tonic-gate int pin[2], pout[2]; 65*7c478bd9Sstevel@tonic-gate 66*7c478bd9Sstevel@tonic-gate if ((pipe(pin) == -1) || (pipe(pout) == -1)) 67*7c478bd9Sstevel@tonic-gate fatal("pipe: %s", strerror(errno)); 68*7c478bd9Sstevel@tonic-gate *in = pin[0]; 69*7c478bd9Sstevel@tonic-gate *out = pout[1]; 70*7c478bd9Sstevel@tonic-gate c_in = pout[0]; 71*7c478bd9Sstevel@tonic-gate c_out = pin[1]; 72*7c478bd9Sstevel@tonic-gate #else /* USE_PIPES */ 73*7c478bd9Sstevel@tonic-gate int inout[2]; 74*7c478bd9Sstevel@tonic-gate 75*7c478bd9Sstevel@tonic-gate if (socketpair(AF_UNIX, SOCK_STREAM, 0, inout) == -1) 76*7c478bd9Sstevel@tonic-gate fatal("socketpair: %s", strerror(errno)); 77*7c478bd9Sstevel@tonic-gate *in = *out = inout[0]; 78*7c478bd9Sstevel@tonic-gate c_in = c_out = inout[1]; 79*7c478bd9Sstevel@tonic-gate #endif /* USE_PIPES */ 80*7c478bd9Sstevel@tonic-gate 81*7c478bd9Sstevel@tonic-gate if ((*sshpid = fork()) == -1) 82*7c478bd9Sstevel@tonic-gate fatal("fork: %s", strerror(errno)); 83*7c478bd9Sstevel@tonic-gate else if (*sshpid == 0) { 84*7c478bd9Sstevel@tonic-gate if ((dup2(c_in, STDIN_FILENO) == -1) || 85*7c478bd9Sstevel@tonic-gate (dup2(c_out, STDOUT_FILENO) == -1)) { 86*7c478bd9Sstevel@tonic-gate fprintf(stderr, "dup2: %s\n", strerror(errno)); 87*7c478bd9Sstevel@tonic-gate exit(1); 88*7c478bd9Sstevel@tonic-gate } 89*7c478bd9Sstevel@tonic-gate close(*in); 90*7c478bd9Sstevel@tonic-gate close(*out); 91*7c478bd9Sstevel@tonic-gate close(c_in); 92*7c478bd9Sstevel@tonic-gate close(c_out); 93*7c478bd9Sstevel@tonic-gate execv(path, args); 94*7c478bd9Sstevel@tonic-gate fprintf(stderr, "exec: %s: %s\n", path, strerror(errno)); 95*7c478bd9Sstevel@tonic-gate exit(1); 96*7c478bd9Sstevel@tonic-gate } 97*7c478bd9Sstevel@tonic-gate 98*7c478bd9Sstevel@tonic-gate close(c_in); 99*7c478bd9Sstevel@tonic-gate close(c_out); 100*7c478bd9Sstevel@tonic-gate } 101*7c478bd9Sstevel@tonic-gate 102*7c478bd9Sstevel@tonic-gate static void 103*7c478bd9Sstevel@tonic-gate usage(void) 104*7c478bd9Sstevel@tonic-gate { 105*7c478bd9Sstevel@tonic-gate extern char *__progname; 106*7c478bd9Sstevel@tonic-gate 107*7c478bd9Sstevel@tonic-gate fprintf(stderr, 108*7c478bd9Sstevel@tonic-gate gettext("Usage: %s [-vC1] [-b batchfile] [-o option] " 109*7c478bd9Sstevel@tonic-gate "[-s subsystem|path]\n" 110*7c478bd9Sstevel@tonic-gate " [-F config] [-P direct server path] [-S program] " 111*7c478bd9Sstevel@tonic-gate "[-B buffer_size]\n" 112*7c478bd9Sstevel@tonic-gate " [-R num_requests] [user@]host[:file [file]]\n"), 113*7c478bd9Sstevel@tonic-gate __progname); 114*7c478bd9Sstevel@tonic-gate exit(1); 115*7c478bd9Sstevel@tonic-gate } 116*7c478bd9Sstevel@tonic-gate 117*7c478bd9Sstevel@tonic-gate int 118*7c478bd9Sstevel@tonic-gate main(int argc, char **argv) 119*7c478bd9Sstevel@tonic-gate { 120*7c478bd9Sstevel@tonic-gate int in, out, ch; 121*7c478bd9Sstevel@tonic-gate pid_t sshpid; 122*7c478bd9Sstevel@tonic-gate char *host, *userhost, *cp, *file2; 123*7c478bd9Sstevel@tonic-gate int debug_level = 0, sshver = 2; 124*7c478bd9Sstevel@tonic-gate char *file1 = NULL, *sftp_server = NULL; 125*7c478bd9Sstevel@tonic-gate char *ssh_program = _PATH_SSH_PROGRAM, *sftp_direct = NULL; 126*7c478bd9Sstevel@tonic-gate LogLevel ll = SYSLOG_LEVEL_INFO; 127*7c478bd9Sstevel@tonic-gate arglist args; 128*7c478bd9Sstevel@tonic-gate extern int optind; 129*7c478bd9Sstevel@tonic-gate extern char *optarg; 130*7c478bd9Sstevel@tonic-gate 131*7c478bd9Sstevel@tonic-gate __progname = get_progname(argv[0]); 132*7c478bd9Sstevel@tonic-gate 133*7c478bd9Sstevel@tonic-gate (void) g11n_setlocale(LC_ALL, ""); 134*7c478bd9Sstevel@tonic-gate args.list = NULL; 135*7c478bd9Sstevel@tonic-gate addargs(&args, "ssh"); /* overwritten with ssh_program */ 136*7c478bd9Sstevel@tonic-gate addargs(&args, "-oForwardX11 no"); 137*7c478bd9Sstevel@tonic-gate addargs(&args, "-oForwardAgent no"); 138*7c478bd9Sstevel@tonic-gate addargs(&args, "-oClearAllForwardings yes"); 139*7c478bd9Sstevel@tonic-gate ll = SYSLOG_LEVEL_INFO; 140*7c478bd9Sstevel@tonic-gate infile = stdin; /* Read from STDIN unless changed by -b */ 141*7c478bd9Sstevel@tonic-gate 142*7c478bd9Sstevel@tonic-gate while ((ch = getopt(argc, argv, "1hvCo:s:S:b:B:F:P:R:")) != -1) { 143*7c478bd9Sstevel@tonic-gate switch (ch) { 144*7c478bd9Sstevel@tonic-gate case 'C': 145*7c478bd9Sstevel@tonic-gate addargs(&args, "-C"); 146*7c478bd9Sstevel@tonic-gate break; 147*7c478bd9Sstevel@tonic-gate case 'v': 148*7c478bd9Sstevel@tonic-gate if (debug_level < 3) { 149*7c478bd9Sstevel@tonic-gate addargs(&args, "-v"); 150*7c478bd9Sstevel@tonic-gate ll = SYSLOG_LEVEL_DEBUG1 + debug_level; 151*7c478bd9Sstevel@tonic-gate } 152*7c478bd9Sstevel@tonic-gate debug_level++; 153*7c478bd9Sstevel@tonic-gate break; 154*7c478bd9Sstevel@tonic-gate case 'F': 155*7c478bd9Sstevel@tonic-gate case 'o': 156*7c478bd9Sstevel@tonic-gate addargs(&args, "-%c%s", ch, optarg); 157*7c478bd9Sstevel@tonic-gate break; 158*7c478bd9Sstevel@tonic-gate case '1': 159*7c478bd9Sstevel@tonic-gate sshver = 1; 160*7c478bd9Sstevel@tonic-gate if (sftp_server == NULL) 161*7c478bd9Sstevel@tonic-gate sftp_server = _PATH_SFTP_SERVER; 162*7c478bd9Sstevel@tonic-gate break; 163*7c478bd9Sstevel@tonic-gate case 's': 164*7c478bd9Sstevel@tonic-gate sftp_server = optarg; 165*7c478bd9Sstevel@tonic-gate break; 166*7c478bd9Sstevel@tonic-gate case 'S': 167*7c478bd9Sstevel@tonic-gate ssh_program = optarg; 168*7c478bd9Sstevel@tonic-gate break; 169*7c478bd9Sstevel@tonic-gate case 'b': 170*7c478bd9Sstevel@tonic-gate if (infile == stdin) { 171*7c478bd9Sstevel@tonic-gate infile = fopen(optarg, "r"); 172*7c478bd9Sstevel@tonic-gate if (infile == NULL) 173*7c478bd9Sstevel@tonic-gate fatal("%s (%s).", strerror(errno), optarg); 174*7c478bd9Sstevel@tonic-gate } else 175*7c478bd9Sstevel@tonic-gate fatal("Filename already specified."); 176*7c478bd9Sstevel@tonic-gate break; 177*7c478bd9Sstevel@tonic-gate case 'P': 178*7c478bd9Sstevel@tonic-gate sftp_direct = optarg; 179*7c478bd9Sstevel@tonic-gate break; 180*7c478bd9Sstevel@tonic-gate case 'B': 181*7c478bd9Sstevel@tonic-gate copy_buffer_len = strtol(optarg, &cp, 10); 182*7c478bd9Sstevel@tonic-gate if (copy_buffer_len == 0 || *cp != '\0') 183*7c478bd9Sstevel@tonic-gate fatal("Invalid buffer size \"%s\"", optarg); 184*7c478bd9Sstevel@tonic-gate break; 185*7c478bd9Sstevel@tonic-gate case 'R': 186*7c478bd9Sstevel@tonic-gate num_requests = strtol(optarg, &cp, 10); 187*7c478bd9Sstevel@tonic-gate if (num_requests == 0 || *cp != '\0') 188*7c478bd9Sstevel@tonic-gate fatal("Invalid number of requests \"%s\"", 189*7c478bd9Sstevel@tonic-gate optarg); 190*7c478bd9Sstevel@tonic-gate break; 191*7c478bd9Sstevel@tonic-gate case 'h': 192*7c478bd9Sstevel@tonic-gate default: 193*7c478bd9Sstevel@tonic-gate usage(); 194*7c478bd9Sstevel@tonic-gate } 195*7c478bd9Sstevel@tonic-gate } 196*7c478bd9Sstevel@tonic-gate 197*7c478bd9Sstevel@tonic-gate log_init(argv[0], ll, SYSLOG_FACILITY_USER, 1); 198*7c478bd9Sstevel@tonic-gate 199*7c478bd9Sstevel@tonic-gate if (sftp_direct == NULL) { 200*7c478bd9Sstevel@tonic-gate if (optind == argc || argc > (optind + 2)) 201*7c478bd9Sstevel@tonic-gate usage(); 202*7c478bd9Sstevel@tonic-gate 203*7c478bd9Sstevel@tonic-gate userhost = xstrdup(argv[optind]); 204*7c478bd9Sstevel@tonic-gate file2 = argv[optind+1]; 205*7c478bd9Sstevel@tonic-gate 206*7c478bd9Sstevel@tonic-gate if ((cp = colon(userhost)) != NULL) { 207*7c478bd9Sstevel@tonic-gate *cp++ = '\0'; 208*7c478bd9Sstevel@tonic-gate file1 = cp; 209*7c478bd9Sstevel@tonic-gate } 210*7c478bd9Sstevel@tonic-gate 211*7c478bd9Sstevel@tonic-gate if ((host = strchr(userhost, '@')) == NULL) 212*7c478bd9Sstevel@tonic-gate host = userhost; 213*7c478bd9Sstevel@tonic-gate else { 214*7c478bd9Sstevel@tonic-gate *host++ = '\0'; 215*7c478bd9Sstevel@tonic-gate if (!userhost[0]) { 216*7c478bd9Sstevel@tonic-gate fprintf(stderr, gettext("Missing username\n")); 217*7c478bd9Sstevel@tonic-gate usage(); 218*7c478bd9Sstevel@tonic-gate } 219*7c478bd9Sstevel@tonic-gate addargs(&args, "-l%s",userhost); 220*7c478bd9Sstevel@tonic-gate } 221*7c478bd9Sstevel@tonic-gate 222*7c478bd9Sstevel@tonic-gate host = cleanhostname(host); 223*7c478bd9Sstevel@tonic-gate if (!*host) { 224*7c478bd9Sstevel@tonic-gate fprintf(stderr, gettext("Missing hostname\n")); 225*7c478bd9Sstevel@tonic-gate usage(); 226*7c478bd9Sstevel@tonic-gate } 227*7c478bd9Sstevel@tonic-gate 228*7c478bd9Sstevel@tonic-gate addargs(&args, "-oProtocol %d", sshver); 229*7c478bd9Sstevel@tonic-gate 230*7c478bd9Sstevel@tonic-gate /* no subsystem if the server-spec contains a '/' */ 231*7c478bd9Sstevel@tonic-gate if (sftp_server == NULL || strchr(sftp_server, '/') == NULL) 232*7c478bd9Sstevel@tonic-gate addargs(&args, "-s"); 233*7c478bd9Sstevel@tonic-gate 234*7c478bd9Sstevel@tonic-gate addargs(&args, "%s", host); 235*7c478bd9Sstevel@tonic-gate addargs(&args, "%s", (sftp_server != NULL ? 236*7c478bd9Sstevel@tonic-gate sftp_server : "sftp")); 237*7c478bd9Sstevel@tonic-gate args.list[0] = ssh_program; 238*7c478bd9Sstevel@tonic-gate 239*7c478bd9Sstevel@tonic-gate fprintf(stderr, gettext("Connecting to %s...\n"), host); 240*7c478bd9Sstevel@tonic-gate connect_to_server(ssh_program, args.list, &in, &out, 241*7c478bd9Sstevel@tonic-gate &sshpid); 242*7c478bd9Sstevel@tonic-gate } else { 243*7c478bd9Sstevel@tonic-gate args.list = NULL; 244*7c478bd9Sstevel@tonic-gate addargs(&args, "sftp-server"); 245*7c478bd9Sstevel@tonic-gate 246*7c478bd9Sstevel@tonic-gate fprintf(stderr, gettext("Attaching to %s...\n"), sftp_direct); 247*7c478bd9Sstevel@tonic-gate connect_to_server(sftp_direct, args.list, &in, &out, 248*7c478bd9Sstevel@tonic-gate &sshpid); 249*7c478bd9Sstevel@tonic-gate } 250*7c478bd9Sstevel@tonic-gate 251*7c478bd9Sstevel@tonic-gate interactive_loop(in, out, file1, file2); 252*7c478bd9Sstevel@tonic-gate 253*7c478bd9Sstevel@tonic-gate #if !defined(USE_PIPES) 254*7c478bd9Sstevel@tonic-gate shutdown(in, SHUT_RDWR); 255*7c478bd9Sstevel@tonic-gate shutdown(out, SHUT_RDWR); 256*7c478bd9Sstevel@tonic-gate #endif 257*7c478bd9Sstevel@tonic-gate 258*7c478bd9Sstevel@tonic-gate close(in); 259*7c478bd9Sstevel@tonic-gate close(out); 260*7c478bd9Sstevel@tonic-gate if (infile != stdin) 261*7c478bd9Sstevel@tonic-gate fclose(infile); 262*7c478bd9Sstevel@tonic-gate 263*7c478bd9Sstevel@tonic-gate while (waitpid(sshpid, NULL, 0) == -1) 264*7c478bd9Sstevel@tonic-gate if (errno != EINTR) 265*7c478bd9Sstevel@tonic-gate fatal("Couldn't wait for ssh process: %s", 266*7c478bd9Sstevel@tonic-gate strerror(errno)); 267*7c478bd9Sstevel@tonic-gate 268*7c478bd9Sstevel@tonic-gate return (0); 269*7c478bd9Sstevel@tonic-gate } 270