xref: /titanic_50/usr/src/cmd/ssh/sftp/sftp.c (revision 7c478bd95313f5f23a4c958a745db2134aa03244)
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