ProxyCommand that returns a socket

Damien Miller djm at mindrot.org
Fri Jun 21 10:01:21 EST 2013


On Thu, 20 Jun 2013, Igor Bukanov wrote:

> Hello,
> 
> My usage of ProxyCommand just calls the nc utility with various
> parameters. That in turn after the initial setup just copies copies
> the data from the network socket to stdin/stdout. This useless coping
> can be avoided if ssh has an option to receive the socket from the
> proxy command. I suppose it can improve network error reporting as ssh
> would talk directly to the network socket rather than a proxy, right?

I have a patch that adds a ProxyUseFdpass that might do what you want.

When ProxyUseFdpass=yes, the ProxyCommand is expected to pass back a
connected socket and exit. ssh will then use this socket directly.

This is somewhat more optional for cases where the proxy is only
needed at the beginning of the connection.

Index: readconf.c
===================================================================
RCS file: /var/cvs/openssh/readconf.c,v
retrieving revision 1.184
diff -u -p -r1.184 readconf.c
--- readconf.c	5 Jun 2013 22:22:47 -0000	1.184
+++ readconf.c	21 Jun 2013 00:00:29 -0000
@@ -137,7 +137,7 @@ typedef enum {
 	oHashKnownHosts,
 	oTunnel, oTunnelDevice, oLocalCommand, oPermitLocalCommand,
 	oVisualHostKey, oUseRoaming, oZeroKnowledgePasswordAuthentication,
-	oKexAlgorithms, oIPQoS, oRequestTTY, oIgnoreUnknown,
+	oKexAlgorithms, oIPQoS, oRequestTTY, oIgnoreUnknown, oProxyUseFdpass,
 	oIgnoredUnknownOption, oDeprecated, oUnsupported
 } OpCodes;
 
@@ -249,6 +249,7 @@ static struct {
 	{ "kexalgorithms", oKexAlgorithms },
 	{ "ipqos", oIPQoS },
 	{ "requesttty", oRequestTTY },
+	{ "proxyusefdpass", oProxyUseFdpass },
 	{ "ignoreunknown", oIgnoreUnknown },
 
 	{ NULL, oBadOption }
@@ -1074,6 +1075,10 @@ parse_int:
 		charptr = &options->ignored_unknown;
 		goto parse_string;
 
+	case oProxyUseFdpass:
+		intptr = &options->proxy_use_fdpass;
+		goto parse_flag;
+
 	case oDeprecated:
 		debug("%s line %d: Deprecated option \"%s\"",
 		    filename, linenum, keyword);
@@ -1235,6 +1240,7 @@ initialize_options(Options * options)
 	options->ip_qos_interactive = -1;
 	options->ip_qos_bulk = -1;
 	options->request_tty = -1;
+	options->proxy_use_fdpass = -1;
 	options->ignored_unknown = NULL;
 }
 
@@ -1387,6 +1393,8 @@ fill_default_options(Options * options)
 		options->ip_qos_bulk = IPTOS_THROUGHPUT;
 	if (options->request_tty == -1)
 		options->request_tty = REQUEST_TTY_AUTO;
+	if (options->proxy_use_fdpass == -1)
+		options->proxy_use_fdpass = 0;
 	/* options->local_command should not be set by default */
 	/* options->proxy_command should not be set by default */
 	/* options->user will be set in the main program if appropriate */
Index: readconf.h
===================================================================
RCS file: /var/cvs/openssh/readconf.h,v
retrieving revision 1.87
diff -u -p -r1.87 readconf.h
--- readconf.h	16 May 2013 10:30:03 -0000	1.87
+++ readconf.h	21 Jun 2013 00:00:29 -0000
@@ -138,6 +138,8 @@ typedef struct {
 
 	int	request_tty;
 
+	int	proxy_use_fdpass;
+
 	char	*ignored_unknown; /* Pattern list of unknown tokens to ignore */
 }       Options;
 
Index: ssh_config.5
===================================================================
RCS file: /var/cvs/openssh/ssh_config.5,v
retrieving revision 1.164
diff -u -p -r1.164 ssh_config.5
--- ssh_config.5	16 May 2013 10:30:32 -0000	1.164
+++ ssh_config.5	21 Jun 2013 00:00:29 -0000
@@ -931,6 +931,14 @@ For example, the following directive wou
 .Bd -literal -offset 3n
 ProxyCommand /usr/bin/nc -X connect -x 192.0.2.0:8080 %h %p
 .Ed
+.It Cm ProxyUseFdpass
+Specifies that the a
+.Cm ProxyCommand
+will pass a connected file descriptor back to
+.Nm ssh
+instead of continuing to execute and pass data.
+The default is
+.Dq no .
 .It Cm PubkeyAuthentication
 Specifies whether to try public key authentication.
 The argument to this keyword must be
Index: sshconnect.c
===================================================================
RCS file: /var/cvs/openssh/sshconnect.c,v
retrieving revision 1.211
diff -u -p -r1.211 sshconnect.c
--- sshconnect.c	1 Jun 2013 21:31:19 -0000	1.211
+++ sshconnect.c	21 Jun 2013 00:00:29 -0000
@@ -59,6 +59,7 @@
 #include "misc.h"
 #include "dns.h"
 #include "roaming.h"
+#include "monitor_fdpass.h"
 #include "ssh2.h"
 #include "version.h"
 
@@ -78,16 +79,113 @@ extern uid_t original_effective_uid;
 static int show_other_keys(struct hostkeys *, Key *);
 static void warn_changed_key(Key *);
 
+/* Expand a proxy command */
+static char *
+expand_proxy_command(const char *proxy_command, const char *user,
+    const char *host, int port)
+{
+	char *tmp, *ret, strport[NI_MAXSERV];
+
+	snprintf(strport, sizeof strport, "%hu", port);
+	xasprintf(&tmp, "exec %s", proxy_command);
+	ret = percent_expand(tmp, "h", host, "p", strport,
+	    "r", options.user, (char *)NULL);
+	free(tmp);
+	return ret;
+}
+
+/*
+ * Connect to the given ssh server using a proxy command that passes a
+ * a connected fd back to us.
+ */
+static int
+ssh_proxy_fdpass_connect(const char *host, u_short port,
+    const char *proxy_command)
+{
+	char *command_string;
+	int sp[2], sock;
+	pid_t pid;
+	char *shell;
+
+	if ((shell = getenv("SHELL")) == NULL)
+		shell = _PATH_BSHELL;
+
+	if (socketpair(AF_UNIX, SOCK_STREAM, 0, sp) < 0)
+		fatal("Could not create socketpair to communicate with "
+		    "proxy dialer: %.100s", strerror(errno));
+
+	command_string = expand_proxy_command(proxy_command, options.user,
+	    host, port);
+	debug("Executing proxy dialer command: %.500s", command_string);
+
+	/* Fork and execute the proxy command. */
+	if ((pid = fork()) == 0) {
+		char *argv[10];
+
+		/* Child.  Permanently give up superuser privileges. */
+		permanently_drop_suid(original_real_uid);
+
+		close(sp[1]);
+		/* Redirect stdin and stdout. */
+		if (sp[0] != 0) {
+			if (dup2(sp[0], 0) < 0)
+				perror("dup2 stdin");
+		}
+		if (sp[0] != 1) {
+			if (dup2(sp[0], 1) < 0)
+				perror("dup2 stdout");
+		}
+		if (sp[0] >= 2)
+			close(sp[0]);
+
+		/*
+		 * Stderr is left as it is so that error messages get
+		 * printed on the user's terminal.
+		 */
+		argv[0] = shell;
+		argv[1] = "-c";
+		argv[2] = command_string;
+		argv[3] = NULL;
+
+		/*
+		 * Execute the proxy command.
+		 * Note that we gave up any extra privileges above.
+		 */
+		execv(argv[0], argv);
+		perror(argv[0]);
+		exit(1);
+	}
+	/* Parent. */
+	if (pid < 0)
+		fatal("fork failed: %.100s", strerror(errno));
+	close(sp[0]);
+	free(command_string);
+
+	if ((sock = mm_receive_fd(sp[1])) == -1)
+		fatal("proxy dialer did not pass back a connection");
+
+	while (waitpid(pid, NULL, 0) == -1)
+		if (errno != EINTR)
+			fatal("Couldn't wait for child: %s", strerror(errno));
+
+	/* Set the connection file descriptors. */
+	packet_set_connection(sock, sock);
+	packet_set_timeout(options.server_alive_interval,
+	    options.server_alive_count_max);
+
+	return 0;
+}
+
 /*
  * Connect to the given ssh server using a proxy command.
  */
 static int
 ssh_proxy_connect(const char *host, u_short port, const char *proxy_command)
 {
-	char *command_string, *tmp;
+	char *command_string;
 	int pin[2], pout[2];
 	pid_t pid;
-	char *shell, strport[NI_MAXSERV];
+	char *shell;
 
 	if (!strcmp(proxy_command, "-")) {
 		packet_set_connection(STDIN_FILENO, STDOUT_FILENO);
@@ -96,29 +194,19 @@ ssh_proxy_connect(const char *host, u_sh
 		return 0;
 	}
 
+	if (options.proxy_use_fdpass)
+		return ssh_proxy_fdpass_connect(host, port, proxy_command);
+
 	if ((shell = getenv("SHELL")) == NULL || *shell == '\0')
 		shell = _PATH_BSHELL;
 
-	/* Convert the port number into a string. */
-	snprintf(strport, sizeof strport, "%hu", port);
-
-	/*
-	 * Build the final command string in the buffer by making the
-	 * appropriate substitutions to the given proxy command.
-	 *
-	 * Use "exec" to avoid "sh -c" processes on some platforms
-	 * (e.g. Solaris)
-	 */
-	xasprintf(&tmp, "exec %s", proxy_command);
-	command_string = percent_expand(tmp, "h", host, "p", strport,
-	    "r", options.user, (char *)NULL);
-	free(tmp);
-
 	/* Create pipes for communicating with the proxy. */
 	if (pipe(pin) < 0 || pipe(pout) < 0)
 		fatal("Could not create pipes to communicate with the proxy: %.100s",
 		    strerror(errno));
 
+	command_string = expand_proxy_command(proxy_command, options.user,
+	    host, port);
 	debug("Executing proxy command: %.500s", command_string);
 
 	/* Fork and execute the proxy command. */


More information about the openssh-unix-dev mailing list