ProxyCommand that returns a socket

Igor Bukanov igor at mir2.org
Fri Jun 21 15:55:43 EST 2013


Thanks, I think this is exactly what I was looking for!

On 21 June 2013 02:01, Damien Miller <djm at mindrot.org> wrote:
> 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