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