xref: /openssh-portable/scp.c (revision 31d8d231)
1*31d8d231Sdjm@openbsd.org /* $OpenBSD: scp.c,v 1.214 2021/04/03 06:18:40 djm Exp $ */
2d4a8b7e3SDamien Miller /*
3e4340be5SDamien Miller  * scp - secure remote copy.  This is basically patched BSD rcp which
4e4340be5SDamien Miller  * uses ssh to do the data transfer (instead of using rcmd).
595def098SDamien Miller  *
6e4340be5SDamien Miller  * NOTE: This version should NOT be suid root.  (This uses ssh to
7e4340be5SDamien Miller  * do the transfer and ssh has the necessary privileges.)
895def098SDamien Miller  *
995def098SDamien Miller  * 1995 Timo Rinne <tri@iki.fi>, Tatu Ylonen <ylo@cs.hut.fi>
1095def098SDamien Miller  *
11e4340be5SDamien Miller  * As far as I am concerned, the code I have written for this software
12e4340be5SDamien Miller  * can be used freely for any purpose.  Any derived versions of this
13e4340be5SDamien Miller  * software must be clearly marked as such, and if the derived work is
14e4340be5SDamien Miller  * incompatible with the protocol description in the RFC file, it must be
15e4340be5SDamien Miller  * called by a name other than "ssh" or "Secure Shell".
16e4340be5SDamien Miller  */
17e4340be5SDamien Miller /*
18e4340be5SDamien Miller  * Copyright (c) 1999 Theo de Raadt.  All rights reserved.
19e4340be5SDamien Miller  * Copyright (c) 1999 Aaron Campbell.  All rights reserved.
20e4340be5SDamien Miller  *
21e4340be5SDamien Miller  * Redistribution and use in source and binary forms, with or without
22e4340be5SDamien Miller  * modification, are permitted provided that the following conditions
23e4340be5SDamien Miller  * are met:
24e4340be5SDamien Miller  * 1. Redistributions of source code must retain the above copyright
25e4340be5SDamien Miller  *    notice, this list of conditions and the following disclaimer.
26e4340be5SDamien Miller  * 2. Redistributions in binary form must reproduce the above copyright
27e4340be5SDamien Miller  *    notice, this list of conditions and the following disclaimer in the
28e4340be5SDamien Miller  *    documentation and/or other materials provided with the distribution.
29e4340be5SDamien Miller  *
30e4340be5SDamien Miller  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
31e4340be5SDamien Miller  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
32e4340be5SDamien Miller  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
33e4340be5SDamien Miller  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
34e4340be5SDamien Miller  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
35e4340be5SDamien Miller  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
36e4340be5SDamien Miller  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
37e4340be5SDamien Miller  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
38e4340be5SDamien Miller  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
39e4340be5SDamien Miller  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
40d4a8b7e3SDamien Miller  */
41d4a8b7e3SDamien Miller 
42d4a8b7e3SDamien Miller /*
43ad833b3eSDamien Miller  * Parts from:
44ad833b3eSDamien Miller  *
45d4a8b7e3SDamien Miller  * Copyright (c) 1983, 1990, 1992, 1993, 1995
46d4a8b7e3SDamien Miller  *	The Regents of the University of California.  All rights reserved.
47d4a8b7e3SDamien Miller  *
48d4a8b7e3SDamien Miller  * Redistribution and use in source and binary forms, with or without
49d4a8b7e3SDamien Miller  * modification, are permitted provided that the following conditions
50d4a8b7e3SDamien Miller  * are met:
51d4a8b7e3SDamien Miller  * 1. Redistributions of source code must retain the above copyright
52d4a8b7e3SDamien Miller  *    notice, this list of conditions and the following disclaimer.
53d4a8b7e3SDamien Miller  * 2. Redistributions in binary form must reproduce the above copyright
54d4a8b7e3SDamien Miller  *    notice, this list of conditions and the following disclaimer in the
55d4a8b7e3SDamien Miller  *    documentation and/or other materials provided with the distribution.
56dafb12edSDamien Miller  * 3. Neither the name of the University nor the names of its contributors
57d4a8b7e3SDamien Miller  *    may be used to endorse or promote products derived from this software
58d4a8b7e3SDamien Miller  *    without specific prior written permission.
59d4a8b7e3SDamien Miller  *
60d4a8b7e3SDamien Miller  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
61d4a8b7e3SDamien Miller  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
62d4a8b7e3SDamien Miller  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
63d4a8b7e3SDamien Miller  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
64d4a8b7e3SDamien Miller  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
65d4a8b7e3SDamien Miller  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
66d4a8b7e3SDamien Miller  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
67d4a8b7e3SDamien Miller  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
68d4a8b7e3SDamien Miller  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
69d4a8b7e3SDamien Miller  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
70d4a8b7e3SDamien Miller  * SUCH DAMAGE.
71d4a8b7e3SDamien Miller  *
72d4a8b7e3SDamien Miller  */
73d4a8b7e3SDamien Miller 
74d4a8b7e3SDamien Miller #include "includes.h"
759cf6d077SDamien Miller 
769cf6d077SDamien Miller #include <sys/types.h>
77f17883e6SDamien Miller #ifdef HAVE_SYS_STAT_H
78f17883e6SDamien Miller # include <sys/stat.h>
79f17883e6SDamien Miller #endif
803eb14491STim Rice #ifdef HAVE_POLL_H
813eb14491STim Rice #include <poll.h>
823eb14491STim Rice #else
83ccb13eedSDarren Tucker # ifdef HAVE_SYS_POLL_H
84c77cadbfSDamien Miller #  include <sys/poll.h>
85ccb13eedSDarren Tucker # endif
863eb14491STim Rice #endif
879aec9194SDamien Miller #ifdef HAVE_SYS_TIME_H
889aec9194SDamien Miller # include <sys/time.h>
899aec9194SDamien Miller #endif
909cf6d077SDamien Miller #include <sys/wait.h>
91d7834353SDamien Miller #include <sys/uio.h>
9288f254b9SDamien Miller 
93c7b06369SDamien Miller #include <ctype.h>
9488f254b9SDamien Miller #include <dirent.h>
9539972493SDarren Tucker #include <errno.h>
9657cf6385SDamien Miller #include <fcntl.h>
970e3c5bc5SDarren Tucker #ifdef HAVE_FNMATCH_H
98391ffc4bSdjm@openbsd.org #include <fnmatch.h>
990e3c5bc5SDarren Tucker #endif
1002ae4f337Sderaadt@openbsd.org #include <limits.h>
1010e059cdfSschwarze@openbsd.org #include <locale.h>
1029f2abc47SDamien Miller #include <pwd.h>
1036ff3caddSDamien Miller #include <signal.h>
1045d19626aSDarren Tucker #include <stdarg.h>
105329037e3SDarren Tucker #ifdef HAVE_STDINT_H
10606643718Smillert@openbsd.org # include <stdint.h>
107329037e3SDarren Tucker #endif
108a7a73ee3SDamien Miller #include <stdio.h>
109e7a1e5cfSDamien Miller #include <stdlib.h>
110e3476ed0SDamien Miller #include <string.h>
1115598b4f1SDamien Miller #include <time.h>
112e6b3b610SDamien Miller #include <unistd.h>
11363b4bcd0SDamien Miller #if defined(HAVE_STRNVIS) && defined(HAVE_VIS_H) && !defined(BROKEN_STRNVIS)
114bed63112SDarren Tucker #include <vis.h>
115bed63112SDarren Tucker #endif
116d4a8b7e3SDamien Miller 
117d4a8b7e3SDamien Miller #include "xmalloc.h"
118887669efSmillert@openbsd.org #include "ssh.h"
119226cfa03SBen Lindstrom #include "atomicio.h"
120226cfa03SBen Lindstrom #include "pathnames.h"
121226cfa03SBen Lindstrom #include "log.h"
12293c17d9eSKevin Steves #include "misc.h"
12362d57f60SDamien Miller #include "progressmeter.h"
1240e059cdfSschwarze@openbsd.org #include "utf8.h"
125d4a8b7e3SDamien Miller 
12649a79c09SBen Lindstrom extern char *__progname;
12749a79c09SBen Lindstrom 
128c77cadbfSDamien Miller #define COPY_BUFLEN	16384
129c77cadbfSDamien Miller 
130887669efSmillert@openbsd.org int do_cmd(char *host, char *remuser, int port, char *cmd, int *fdin, int *fdout);
131887669efSmillert@openbsd.org int do_cmd2(char *host, char *remuser, int port, char *cmd, int fdin, int fdout);
13207aa132aSDamien Miller 
133387c4726SBen Lindstrom /* Struct for addargs */
134387c4726SBen Lindstrom arglist args;
13587dc0a41SDamien Miller arglist remote_remote_args;
136874d77bbSDamien Miller 
137ff74d748SDamien Miller /* Bandwidth limit */
13865e42f87SDamien Miller long long limit_kbps = 0;
13965e42f87SDamien Miller struct bwlimit bwlimit;
140ff74d748SDamien Miller 
141d4a8b7e3SDamien Miller /* Name of current file being transferred. */
142d4a8b7e3SDamien Miller char *curfile;
143d4a8b7e3SDamien Miller 
144d4a8b7e3SDamien Miller /* This is set to non-zero to enable verbose mode. */
14595def098SDamien Miller int verbose_mode = 0;
146d4a8b7e3SDamien Miller 
147d4a8b7e3SDamien Miller /* This is set to zero if the progressmeter is not desired. */
148d4a8b7e3SDamien Miller int showprogress = 1;
149d4a8b7e3SDamien Miller 
150f1211436SDamien Miller /*
151f1211436SDamien Miller  * This is set to non-zero if remote-remote copy should be piped
152f1211436SDamien Miller  * through this process.
153f1211436SDamien Miller  */
154f1211436SDamien Miller int throughlocal = 0;
155f1211436SDamien Miller 
156887669efSmillert@openbsd.org /* Non-standard port to use for the ssh connection or -1. */
157887669efSmillert@openbsd.org int sshport = -1;
158887669efSmillert@openbsd.org 
159ad833b3eSDamien Miller /* This is the program to execute for the secured connection. ("ssh" or -S) */
160e566230cSDarren Tucker char *ssh_program = _PATH_SSH_PROGRAM;
161ad833b3eSDamien Miller 
162007eb912SBen Lindstrom /* This is used to store the pid of ssh_program */
163b69aaa8dSDamien Miller pid_t do_cmd_pid = -1;
164b69aaa8dSDamien Miller 
165b69aaa8dSDamien Miller static void
killchild(int signo)166b69aaa8dSDamien Miller killchild(int signo)
167b69aaa8dSDamien Miller {
168ba66df81SDarren Tucker 	if (do_cmd_pid > 1) {
169fc4f2dd3SDarren Tucker 		kill(do_cmd_pid, signo ? signo : SIGTERM);
170ba66df81SDarren Tucker 		waitpid(do_cmd_pid, NULL, 0);
171ba66df81SDarren Tucker 	}
172b69aaa8dSDamien Miller 
173fc4f2dd3SDarren Tucker 	if (signo)
174b69aaa8dSDamien Miller 		_exit(1);
175fc4f2dd3SDarren Tucker 	exit(1);
176b69aaa8dSDamien Miller }
177007eb912SBen Lindstrom 
1780979b409SDamien Miller static void
suspchild(int signo)1790979b409SDamien Miller suspchild(int signo)
1800979b409SDamien Miller {
1810979b409SDamien Miller 	int status;
1820979b409SDamien Miller 
1830979b409SDamien Miller 	if (do_cmd_pid > 1) {
1840979b409SDamien Miller 		kill(do_cmd_pid, signo);
1850979b409SDamien Miller 		while (waitpid(do_cmd_pid, &status, WUNTRACED) == -1 &&
1860979b409SDamien Miller 		    errno == EINTR)
1870979b409SDamien Miller 			;
1880979b409SDamien Miller 		kill(getpid(), SIGSTOP);
1890979b409SDamien Miller 	}
1900979b409SDamien Miller }
1910979b409SDamien Miller 
1923eec6b73SDamien Miller static int
do_local_cmd(arglist * a)1933eec6b73SDamien Miller do_local_cmd(arglist *a)
1943eec6b73SDamien Miller {
1953eec6b73SDamien Miller 	u_int i;
1963eec6b73SDamien Miller 	int status;
1973eec6b73SDamien Miller 	pid_t pid;
1983eec6b73SDamien Miller 
1993eec6b73SDamien Miller 	if (a->num == 0)
2003eec6b73SDamien Miller 		fatal("do_local_cmd: no arguments");
2013eec6b73SDamien Miller 
2023eec6b73SDamien Miller 	if (verbose_mode) {
2033eec6b73SDamien Miller 		fprintf(stderr, "Executing:");
2043eec6b73SDamien Miller 		for (i = 0; i < a->num; i++)
2050e059cdfSschwarze@openbsd.org 			fmprintf(stderr, " %s", a->list[i]);
2063eec6b73SDamien Miller 		fprintf(stderr, "\n");
2073eec6b73SDamien Miller 	}
2083eec6b73SDamien Miller 	if ((pid = fork()) == -1)
2093eec6b73SDamien Miller 		fatal("do_local_cmd: fork: %s", strerror(errno));
2103eec6b73SDamien Miller 
2113eec6b73SDamien Miller 	if (pid == 0) {
2123eec6b73SDamien Miller 		execvp(a->list[0], a->list);
2133eec6b73SDamien Miller 		perror(a->list[0]);
2143eec6b73SDamien Miller 		exit(1);
2153eec6b73SDamien Miller 	}
2163eec6b73SDamien Miller 
2173eec6b73SDamien Miller 	do_cmd_pid = pid;
2183bf2a6acSdtucker@openbsd.org 	ssh_signal(SIGTERM, killchild);
2193bf2a6acSdtucker@openbsd.org 	ssh_signal(SIGINT, killchild);
2203bf2a6acSdtucker@openbsd.org 	ssh_signal(SIGHUP, killchild);
2213eec6b73SDamien Miller 
2223eec6b73SDamien Miller 	while (waitpid(pid, &status, 0) == -1)
2233eec6b73SDamien Miller 		if (errno != EINTR)
2243eec6b73SDamien Miller 			fatal("do_local_cmd: waitpid: %s", strerror(errno));
2253eec6b73SDamien Miller 
2263eec6b73SDamien Miller 	do_cmd_pid = -1;
2273eec6b73SDamien Miller 
2283eec6b73SDamien Miller 	if (!WIFEXITED(status) || WEXITSTATUS(status) != 0)
2293eec6b73SDamien Miller 		return (-1);
2303eec6b73SDamien Miller 
2313eec6b73SDamien Miller 	return (0);
2323eec6b73SDamien Miller }
2333eec6b73SDamien Miller 
2345428f646SDamien Miller /*
2355428f646SDamien Miller  * This function executes the given command as the specified user on the
2365428f646SDamien Miller  * given host.  This returns < 0 if execution fails, and >= 0 otherwise. This
2375428f646SDamien Miller  * assigns the input and output file descriptors on success.
2385428f646SDamien Miller  */
239d4a8b7e3SDamien Miller 
24095def098SDamien Miller int
do_cmd(char * host,char * remuser,int port,char * cmd,int * fdin,int * fdout)241887669efSmillert@openbsd.org do_cmd(char *host, char *remuser, int port, char *cmd, int *fdin, int *fdout)
242d4a8b7e3SDamien Miller {
243d4a8b7e3SDamien Miller 	int pin[2], pout[2], reserved[2];
244d4a8b7e3SDamien Miller 
24595def098SDamien Miller 	if (verbose_mode)
2460e059cdfSschwarze@openbsd.org 		fmprintf(stderr,
2472772a3f8SBen Lindstrom 		    "Executing: program %s host %s, user %s, command %s\n",
2482772a3f8SBen Lindstrom 		    ssh_program, host,
2492772a3f8SBen Lindstrom 		    remuser ? remuser : "(unspecified)", cmd);
250d4a8b7e3SDamien Miller 
251887669efSmillert@openbsd.org 	if (port == -1)
252887669efSmillert@openbsd.org 		port = sshport;
253887669efSmillert@openbsd.org 
2545428f646SDamien Miller 	/*
2555428f646SDamien Miller 	 * Reserve two descriptors so that the real pipes won't get
2565428f646SDamien Miller 	 * descriptors 0 and 1 because that will screw up dup2 below.
2575428f646SDamien Miller 	 */
2584d28fa78Sderaadt@openbsd.org 	if (pipe(reserved) == -1)
25940b5985fSDamien Miller 		fatal("pipe: %s", strerror(errno));
260d4a8b7e3SDamien Miller 
261d4a8b7e3SDamien Miller 	/* Create a socket pair for communicating with ssh. */
2624d28fa78Sderaadt@openbsd.org 	if (pipe(pin) == -1)
263d4a8b7e3SDamien Miller 		fatal("pipe: %s", strerror(errno));
2644d28fa78Sderaadt@openbsd.org 	if (pipe(pout) == -1)
265d4a8b7e3SDamien Miller 		fatal("pipe: %s", strerror(errno));
266d4a8b7e3SDamien Miller 
267d4a8b7e3SDamien Miller 	/* Free the reserved descriptors. */
268d4a8b7e3SDamien Miller 	close(reserved[0]);
269d4a8b7e3SDamien Miller 	close(reserved[1]);
270d4a8b7e3SDamien Miller 
2713bf2a6acSdtucker@openbsd.org 	ssh_signal(SIGTSTP, suspchild);
2723bf2a6acSdtucker@openbsd.org 	ssh_signal(SIGTTIN, suspchild);
2733bf2a6acSdtucker@openbsd.org 	ssh_signal(SIGTTOU, suspchild);
2740979b409SDamien Miller 
275b2cdcb50SDamien Miller 	/* Fork a child to execute the command on the remote host using ssh. */
276007eb912SBen Lindstrom 	do_cmd_pid = fork();
277007eb912SBen Lindstrom 	if (do_cmd_pid == 0) {
278d4a8b7e3SDamien Miller 		/* Child. */
279d4a8b7e3SDamien Miller 		close(pin[1]);
280d4a8b7e3SDamien Miller 		close(pout[0]);
281d4a8b7e3SDamien Miller 		dup2(pin[0], 0);
282d4a8b7e3SDamien Miller 		dup2(pout[1], 1);
283d4a8b7e3SDamien Miller 		close(pin[0]);
284d4a8b7e3SDamien Miller 		close(pout[1]);
285d4a8b7e3SDamien Miller 
2863eec6b73SDamien Miller 		replacearg(&args, 0, "%s", ssh_program);
287887669efSmillert@openbsd.org 		if (port != -1) {
288887669efSmillert@openbsd.org 			addargs(&args, "-p");
289887669efSmillert@openbsd.org 			addargs(&args, "%d", port);
290887669efSmillert@openbsd.org 		}
291b8c884a0SDarren Tucker 		if (remuser != NULL) {
292b8c884a0SDarren Tucker 			addargs(&args, "-l");
293b8c884a0SDarren Tucker 			addargs(&args, "%s", remuser);
294b8c884a0SDarren Tucker 		}
295b8c884a0SDarren Tucker 		addargs(&args, "--");
296387c4726SBen Lindstrom 		addargs(&args, "%s", host);
297387c4726SBen Lindstrom 		addargs(&args, "%s", cmd);
298d4a8b7e3SDamien Miller 
299874d77bbSDamien Miller 		execvp(ssh_program, args.list);
300ad833b3eSDamien Miller 		perror(ssh_program);
301d4a8b7e3SDamien Miller 		exit(1);
302007eb912SBen Lindstrom 	} else if (do_cmd_pid == -1) {
303007eb912SBen Lindstrom 		fatal("fork: %s", strerror(errno));
304d4a8b7e3SDamien Miller 	}
305d4a8b7e3SDamien Miller 	/* Parent.  Close the other side, and return the local side. */
306d4a8b7e3SDamien Miller 	close(pin[0]);
307d4a8b7e3SDamien Miller 	*fdout = pin[1];
308d4a8b7e3SDamien Miller 	close(pout[1]);
309d4a8b7e3SDamien Miller 	*fdin = pout[0];
3103bf2a6acSdtucker@openbsd.org 	ssh_signal(SIGTERM, killchild);
3113bf2a6acSdtucker@openbsd.org 	ssh_signal(SIGINT, killchild);
3123bf2a6acSdtucker@openbsd.org 	ssh_signal(SIGHUP, killchild);
313d4a8b7e3SDamien Miller 	return 0;
314d4a8b7e3SDamien Miller }
315d4a8b7e3SDamien Miller 
316f1211436SDamien Miller /*
317001aa554Sdjm@openbsd.org  * This function executes a command similar to do_cmd(), but expects the
318f1211436SDamien Miller  * input and output descriptors to be setup by a previous call to do_cmd().
319f1211436SDamien Miller  * This way the input and output of two commands can be connected.
320f1211436SDamien Miller  */
321f1211436SDamien Miller int
do_cmd2(char * host,char * remuser,int port,char * cmd,int fdin,int fdout)322887669efSmillert@openbsd.org do_cmd2(char *host, char *remuser, int port, char *cmd, int fdin, int fdout)
323f1211436SDamien Miller {
324f1211436SDamien Miller 	pid_t pid;
325f1211436SDamien Miller 	int status;
326f1211436SDamien Miller 
327f1211436SDamien Miller 	if (verbose_mode)
3280e059cdfSschwarze@openbsd.org 		fmprintf(stderr,
329f1211436SDamien Miller 		    "Executing: 2nd program %s host %s, user %s, command %s\n",
330f1211436SDamien Miller 		    ssh_program, host,
331f1211436SDamien Miller 		    remuser ? remuser : "(unspecified)", cmd);
332f1211436SDamien Miller 
333887669efSmillert@openbsd.org 	if (port == -1)
334887669efSmillert@openbsd.org 		port = sshport;
335887669efSmillert@openbsd.org 
336f1211436SDamien Miller 	/* Fork a child to execute the command on the remote host using ssh. */
337f1211436SDamien Miller 	pid = fork();
338f1211436SDamien Miller 	if (pid == 0) {
339f1211436SDamien Miller 		dup2(fdin, 0);
340f1211436SDamien Miller 		dup2(fdout, 1);
341f1211436SDamien Miller 
342f1211436SDamien Miller 		replacearg(&args, 0, "%s", ssh_program);
343887669efSmillert@openbsd.org 		if (port != -1) {
344887669efSmillert@openbsd.org 			addargs(&args, "-p");
345887669efSmillert@openbsd.org 			addargs(&args, "%d", port);
346887669efSmillert@openbsd.org 		}
347f1211436SDamien Miller 		if (remuser != NULL) {
348f1211436SDamien Miller 			addargs(&args, "-l");
349f1211436SDamien Miller 			addargs(&args, "%s", remuser);
350f1211436SDamien Miller 		}
351ea14103cSmarkus@openbsd.org 		addargs(&args, "-oBatchMode=yes");
352f1211436SDamien Miller 		addargs(&args, "--");
353f1211436SDamien Miller 		addargs(&args, "%s", host);
354f1211436SDamien Miller 		addargs(&args, "%s", cmd);
355f1211436SDamien Miller 
356f1211436SDamien Miller 		execvp(ssh_program, args.list);
357f1211436SDamien Miller 		perror(ssh_program);
358f1211436SDamien Miller 		exit(1);
359f1211436SDamien Miller 	} else if (pid == -1) {
360f1211436SDamien Miller 		fatal("fork: %s", strerror(errno));
361f1211436SDamien Miller 	}
362f1211436SDamien Miller 	while (waitpid(pid, &status, 0) == -1)
363f1211436SDamien Miller 		if (errno != EINTR)
364f1211436SDamien Miller 			fatal("do_cmd2: waitpid: %s", strerror(errno));
365f1211436SDamien Miller 	return 0;
366f1211436SDamien Miller }
367f1211436SDamien Miller 
368d4a8b7e3SDamien Miller typedef struct {
369eccb9de7SDamien Miller 	size_t cnt;
370d4a8b7e3SDamien Miller 	char *buf;
371d4a8b7e3SDamien Miller } BUF;
372d4a8b7e3SDamien Miller 
373d4a8b7e3SDamien Miller BUF *allocbuf(BUF *, int, int);
374d4a8b7e3SDamien Miller void lostconn(int);
375d4a8b7e3SDamien Miller int okname(char *);
3765ad3c3a3Smillert@openbsd.org void run_err(const char *,...)
3775ad3c3a3Smillert@openbsd.org     __attribute__((__format__ (printf, 1, 2)))
3785ad3c3a3Smillert@openbsd.org     __attribute__((__nonnull__ (1)));
3795ad3c3a3Smillert@openbsd.org int note_err(const char *,...)
3805ad3c3a3Smillert@openbsd.org     __attribute__((__format__ (printf, 1, 2)));
381d4a8b7e3SDamien Miller void verifydir(char *);
382d4a8b7e3SDamien Miller 
383d4a8b7e3SDamien Miller struct passwd *pwd;
384d4a8b7e3SDamien Miller uid_t userid;
385d4a8b7e3SDamien Miller int errs, remin, remout;
386391ffc4bSdjm@openbsd.org int Tflag, pflag, iamremote, iamrecursive, targetshouldbedirectory;
387d4a8b7e3SDamien Miller 
388d4a8b7e3SDamien Miller #define	CMDNEEDS	64
389d4a8b7e3SDamien Miller char cmd[CMDNEEDS];		/* must hold "rcp -r -p -d\0" */
390d4a8b7e3SDamien Miller 
391d4a8b7e3SDamien Miller int response(void);
392d4a8b7e3SDamien Miller void rsource(char *, struct stat *);
393391ffc4bSdjm@openbsd.org void sink(int, char *[], const char *);
394d4a8b7e3SDamien Miller void source(int, char *[]);
395d4a8b7e3SDamien Miller void tolocal(int, char *[]);
396887669efSmillert@openbsd.org void toremote(int, char *[]);
397d4a8b7e3SDamien Miller void usage(void);
398d4a8b7e3SDamien Miller 
399d4a8b7e3SDamien Miller int
main(int argc,char ** argv)40065d1f576SDamien Miller main(int argc, char **argv)
401d4a8b7e3SDamien Miller {
4023d1a9f4dSDamien Miller 	int ch, fflag, tflag, status, n;
403887669efSmillert@openbsd.org 	char **newargv;
40465e42f87SDamien Miller 	const char *errstr;
405d4a8b7e3SDamien Miller 	extern char *optarg;
406d4a8b7e3SDamien Miller 	extern int optind;
407d4a8b7e3SDamien Miller 
408ce321d8aSDarren Tucker 	/* Ensure that fds 0, 1 and 2 are open or directed to /dev/null */
409ce321d8aSDarren Tucker 	sanitise_stdfd();
410ce321d8aSDarren Tucker 
41142c5ec4bSDamien Miller 	seed_rng();
41242c5ec4bSDamien Miller 
413dda78a03SDamien Miller 	msetlocale();
4140e059cdfSschwarze@openbsd.org 
4153d1a9f4dSDamien Miller 	/* Copy argv, because we modify it */
4169136ec13Sderaadt@openbsd.org 	newargv = xcalloc(MAXIMUM(argc + 1, 1), sizeof(*newargv));
4173d1a9f4dSDamien Miller 	for (n = 0; n < argc; n++)
4183d1a9f4dSDamien Miller 		newargv[n] = xstrdup(argv[n]);
4193d1a9f4dSDamien Miller 	argv = newargv;
4203d1a9f4dSDamien Miller 
42159d3d5b8SDamien Miller 	__progname = ssh_get_progname(argv[0]);
42249a79c09SBen Lindstrom 
4233eec6b73SDamien Miller 	memset(&args, '\0', sizeof(args));
42487dc0a41SDamien Miller 	memset(&remote_remote_args, '\0', sizeof(remote_remote_args));
42587dc0a41SDamien Miller 	args.list = remote_remote_args.list = NULL;
4263eec6b73SDamien Miller 	addargs(&args, "%s", ssh_program);
427387c4726SBen Lindstrom 	addargs(&args, "-x");
42887dc0a41SDamien Miller 	addargs(&args, "-oPermitLocalCommand=no");
42987dc0a41SDamien Miller 	addargs(&args, "-oClearAllForwardings=yes");
43077e05394Sdjm@openbsd.org 	addargs(&args, "-oRemoteCommand=none");
43177e05394Sdjm@openbsd.org 	addargs(&args, "-oRequestTTY=no");
432874d77bbSDamien Miller 
433391ffc4bSdjm@openbsd.org 	fflag = Tflag = tflag = 0;
434391ffc4bSdjm@openbsd.org 	while ((ch = getopt(argc, argv,
435a8732d74Sdjm@openbsd.org 	    "12346ABCTdfpqrtvF:J:P:S:c:i:l:o:")) != -1) {
43695def098SDamien Miller 		switch (ch) {
43795def098SDamien Miller 		/* User-visible flags. */
4388e12147dSDamien Miller 		case '1':
439a3710d5dSdjm@openbsd.org 			fatal("SSH protocol v.1 is no longer supported");
440a3710d5dSdjm@openbsd.org 			break;
4418e12147dSDamien Miller 		case '2':
442a3710d5dSdjm@openbsd.org 			/* Ignored */
443a3710d5dSdjm@openbsd.org 			break;
444a8732d74Sdjm@openbsd.org 		case 'A':
44534132e54SDamien Miller 		case '4':
44634132e54SDamien Miller 		case '6':
447874d77bbSDamien Miller 		case 'C':
448387c4726SBen Lindstrom 			addargs(&args, "-%c", ch);
44987dc0a41SDamien Miller 			addargs(&remote_remote_args, "-%c", ch);
450874d77bbSDamien Miller 			break;
451f1211436SDamien Miller 		case '3':
452f1211436SDamien Miller 			throughlocal = 1;
453f1211436SDamien Miller 			break;
454874d77bbSDamien Miller 		case 'o':
455874d77bbSDamien Miller 		case 'c':
456874d77bbSDamien Miller 		case 'i':
4571e243241SBen Lindstrom 		case 'F':
458622dedf1Stb@openbsd.org 		case 'J':
45987dc0a41SDamien Miller 			addargs(&remote_remote_args, "-%c", ch);
46087dc0a41SDamien Miller 			addargs(&remote_remote_args, "%s", optarg);
461b8c884a0SDarren Tucker 			addargs(&args, "-%c", ch);
462b8c884a0SDarren Tucker 			addargs(&args, "%s", optarg);
463874d77bbSDamien Miller 			break;
464874d77bbSDamien Miller 		case 'P':
465887669efSmillert@openbsd.org 			sshport = a2port(optarg);
466887669efSmillert@openbsd.org 			if (sshport <= 0)
467887669efSmillert@openbsd.org 				fatal("bad port \"%s\"\n", optarg);
468874d77bbSDamien Miller 			break;
469874d77bbSDamien Miller 		case 'B':
47087dc0a41SDamien Miller 			addargs(&remote_remote_args, "-oBatchmode=yes");
47187dc0a41SDamien Miller 			addargs(&args, "-oBatchmode=yes");
47234132e54SDamien Miller 			break;
473ff74d748SDamien Miller 		case 'l':
47465e42f87SDamien Miller 			limit_kbps = strtonum(optarg, 1, 100 * 1024 * 1024,
47565e42f87SDamien Miller 			    &errstr);
47665e42f87SDamien Miller 			if (errstr != NULL)
477ff74d748SDamien Miller 				usage();
47865e42f87SDamien Miller 			limit_kbps *= 1024; /* kbps */
47965e42f87SDamien Miller 			bandwidth_limit_init(&bwlimit, limit_kbps, COPY_BUFLEN);
480ff74d748SDamien Miller 			break;
481d4a8b7e3SDamien Miller 		case 'p':
482d4a8b7e3SDamien Miller 			pflag = 1;
483d4a8b7e3SDamien Miller 			break;
484d4a8b7e3SDamien Miller 		case 'r':
485d4a8b7e3SDamien Miller 			iamrecursive = 1;
486d4a8b7e3SDamien Miller 			break;
487ad833b3eSDamien Miller 		case 'S':
488874d77bbSDamien Miller 			ssh_program = xstrdup(optarg);
489874d77bbSDamien Miller 			break;
490874d77bbSDamien Miller 		case 'v':
4919cc94646SBen Lindstrom 			addargs(&args, "-v");
49287dc0a41SDamien Miller 			addargs(&remote_remote_args, "-v");
493874d77bbSDamien Miller 			verbose_mode = 1;
494874d77bbSDamien Miller 			break;
495874d77bbSDamien Miller 		case 'q':
496c1f2792bSDamien Miller 			addargs(&args, "-q");
49787dc0a41SDamien Miller 			addargs(&remote_remote_args, "-q");
498874d77bbSDamien Miller 			showprogress = 0;
499ad833b3eSDamien Miller 			break;
500ad833b3eSDamien Miller 
501d4a8b7e3SDamien Miller 		/* Server options. */
502d4a8b7e3SDamien Miller 		case 'd':
503d4a8b7e3SDamien Miller 			targetshouldbedirectory = 1;
504d4a8b7e3SDamien Miller 			break;
505d4a8b7e3SDamien Miller 		case 'f':	/* "from" */
506d4a8b7e3SDamien Miller 			iamremote = 1;
507d4a8b7e3SDamien Miller 			fflag = 1;
508d4a8b7e3SDamien Miller 			break;
509d4a8b7e3SDamien Miller 		case 't':	/* "to" */
510d4a8b7e3SDamien Miller 			iamremote = 1;
511d4a8b7e3SDamien Miller 			tflag = 1;
512402b3319SDamien Miller #ifdef HAVE_CYGWIN
513402b3319SDamien Miller 			setmode(0, O_BINARY);
514402b3319SDamien Miller #endif
515d4a8b7e3SDamien Miller 			break;
516391ffc4bSdjm@openbsd.org 		case 'T':
517391ffc4bSdjm@openbsd.org 			Tflag = 1;
518391ffc4bSdjm@openbsd.org 			break;
519d4a8b7e3SDamien Miller 		default:
520d4a8b7e3SDamien Miller 			usage();
521d4a8b7e3SDamien Miller 		}
522391ffc4bSdjm@openbsd.org 	}
523d4a8b7e3SDamien Miller 	argc -= optind;
524d4a8b7e3SDamien Miller 	argv += optind;
525d4a8b7e3SDamien Miller 
526a8732d74Sdjm@openbsd.org 	/* Do this last because we want the user to be able to override it */
527a8732d74Sdjm@openbsd.org 	addargs(&args, "-oForwardAgent=no");
528a8732d74Sdjm@openbsd.org 
529d4a8b7e3SDamien Miller 	if ((pwd = getpwuid(userid = getuid())) == NULL)
53003a29bafSDarren Tucker 		fatal("unknown user %u", (u_int) userid);
531d4a8b7e3SDamien Miller 
5326ec2fbecSDarren Tucker 	if (!isatty(STDOUT_FILENO))
533d4a8b7e3SDamien Miller 		showprogress = 0;
534d4a8b7e3SDamien Miller 
5359080bd0bSderaadt@openbsd.org 	if (pflag) {
5369080bd0bSderaadt@openbsd.org 		/* Cannot pledge: -p allows setuid/setgid files... */
5379080bd0bSderaadt@openbsd.org 	} else {
5389080bd0bSderaadt@openbsd.org 		if (pledge("stdio rpath wpath cpath fattr tty proc exec",
5399080bd0bSderaadt@openbsd.org 		    NULL) == -1) {
5409080bd0bSderaadt@openbsd.org 			perror("pledge");
5419080bd0bSderaadt@openbsd.org 			exit(1);
5429080bd0bSderaadt@openbsd.org 		}
5439080bd0bSderaadt@openbsd.org 	}
5449080bd0bSderaadt@openbsd.org 
545d4a8b7e3SDamien Miller 	remin = STDIN_FILENO;
546d4a8b7e3SDamien Miller 	remout = STDOUT_FILENO;
547d4a8b7e3SDamien Miller 
54895def098SDamien Miller 	if (fflag) {
54995def098SDamien Miller 		/* Follow "protocol", send data. */
550d4a8b7e3SDamien Miller 		(void) response();
551d4a8b7e3SDamien Miller 		source(argc, argv);
552d4a8b7e3SDamien Miller 		exit(errs != 0);
553d4a8b7e3SDamien Miller 	}
55495def098SDamien Miller 	if (tflag) {
55595def098SDamien Miller 		/* Receive data. */
556391ffc4bSdjm@openbsd.org 		sink(argc, argv, NULL);
557