Portforwarding using the control master. (patch attached)

dvorak dvorak at xs4all.nl
Fri Jun 20 01:42:22 EST 2008


Hi all,

Attached is the patch I have written, it is a bit larger then expected
due to moving a lot from the code in client_process_control to its own
function (client_process_sshmux_command_open).

also attached is a simple script to test the functionality usage:
(sh PROX.raw ; cat ) | socat - UNIX-CONNECT:./ctl

where ./ctl is the path to the control master. It tries to open a 
connection to 127.0.0.1 1234.

In case the patch is something usefull to have in OpenSSH I'll gladly
help to get the code up to accepted standards.

And as always, comments are most welcome.


Kind Regards, 
  dvorak

> currently I am considering writing a patch for OpenSSH that will allow
> portforwarding using the control_master unix domain socket. The idea is
> to introduce an extra SSHMUX command, SSHMUX_COMMAND_SOCKS, which will 
> then pass control to the normal socks functions used for dynamic
> forwarding.
> 
> The main reason for me to write this patch are:
> - some more control over who gets to connect to portforwardings.
> (the control_master has uid control build in, while everyone can
> connect to the (dynamic) port forwardings.
> 
> - easier to keep an overview, remembering that master-%r@%h:%p
> allows forwarding to ports from that machine is easier then keeping
> track of all the different ports.
> 
> To be actually able to use it (since SSHMUX is an openssh only thing
> as far as I can tell). I'll write a patch to socat as well.
-------------- next part --------------
diff -u openssh-4.9p1/clientloop.c openssh-4.9p1-mux-socks/clientloop.c
--- openssh-4.9p1/clientloop.c	2008-03-07 08:33:30.000000000 +0100
+++ openssh-4.9p1-mux-socks/clientloop.c	2008-06-19 19:25:28.000000000 +0200
@@ -712,124 +712,18 @@
 }
 
 static void
-client_process_control(fd_set *readset)
+client_process_sshmux_command_open (fd_set *readset, int client_fd, u_int flags)
 {
-	Buffer m;
+        Buffer m;
 	Channel *c;
-	int client_fd, new_fd[3], ver, allowed, window, packetmax;
-	socklen_t addrlen;
-	struct sockaddr_storage addr;
+	int new_fd[3], ver, window, packetmax;
 	struct confirm_ctx *cctx;
 	char *cmd;
-	u_int i, j, len, env_len, command, flags;
-	uid_t euid;
-	gid_t egid;
-
-	/*
-	 * Accept connection on control socket
-	 */
-	if (control_fd == -1 || !FD_ISSET(control_fd, readset))
-		return;
-
-	memset(&addr, 0, sizeof(addr));
-	addrlen = sizeof(addr);
-	if ((client_fd = accept(control_fd,
-	    (struct sockaddr*)&addr, &addrlen)) == -1) {
-		error("%s accept: %s", __func__, strerror(errno));
-		return;
-	}
-
-	if (getpeereid(client_fd, &euid, &egid) < 0) {
-		error("%s getpeereid failed: %s", __func__, strerror(errno));
-		close(client_fd);
-		return;
-	}
-	if ((euid != 0) && (getuid() != euid)) {
-		error("control mode uid mismatch: peer euid %u != uid %u",
-		    (u_int) euid, (u_int) getuid());
-		close(client_fd);
-		return;
-	}
-
-	unset_nonblock(client_fd);
-
-	/* Read command */
-	buffer_init(&m);
-	if (ssh_msg_recv(client_fd, &m) == -1) {
-		error("%s: client msg_recv failed", __func__);
-		close(client_fd);
-		buffer_free(&m);
-		return;
-	}
-	if ((ver = buffer_get_char(&m)) != SSHMUX_VER) {
-		error("%s: wrong client version %d", __func__, ver);
-		buffer_free(&m);
-		close(client_fd);
-		return;
-	}
-
-	allowed = 1;
-	command = buffer_get_int(&m);
-	flags = buffer_get_int(&m);
-
-	buffer_clear(&m);
-
-	switch (command) {
-	case SSHMUX_COMMAND_OPEN:
-		if (options.control_master == SSHCTL_MASTER_ASK ||
-		    options.control_master == SSHCTL_MASTER_AUTO_ASK)
-			allowed = ask_permission("Allow shared connection "
-			    "to %s? ", host);
-		/* continue below */
-		break;
-	case SSHMUX_COMMAND_TERMINATE:
-		if (options.control_master == SSHCTL_MASTER_ASK ||
-		    options.control_master == SSHCTL_MASTER_AUTO_ASK)
-			allowed = ask_permission("Terminate shared connection "
-			    "to %s? ", host);
-		if (allowed)
-			quit_pending = 1;
-		/* FALLTHROUGH */
-	case SSHMUX_COMMAND_ALIVE_CHECK:
-		/* Reply for SSHMUX_COMMAND_TERMINATE and ALIVE_CHECK */
-		buffer_clear(&m);
-		buffer_put_int(&m, allowed);
-		buffer_put_int(&m, getpid());
-		if (ssh_msg_send(client_fd, SSHMUX_VER, &m) == -1) {
-			error("%s: client msg_send failed", __func__);
-			close(client_fd);
-			buffer_free(&m);
-			return;
-		}
-		buffer_free(&m);
-		close(client_fd);
-		return;
-	default:
-		error("Unsupported command %d", command);
-		buffer_free(&m);
-		close(client_fd);
-		return;
-	}
+	u_int i, j, len, env_len;
 
+        buffer_init (&m);
 	/* Reply for SSHMUX_COMMAND_OPEN */
-	buffer_clear(&m);
-	buffer_put_int(&m, allowed);
-	buffer_put_int(&m, getpid());
-	if (ssh_msg_send(client_fd, SSHMUX_VER, &m) == -1) {
-		error("%s: client msg_send failed", __func__);
-		close(client_fd);
-		buffer_free(&m);
-		return;
-	}
 
-	if (!allowed) {
-		error("Refused control connection");
-		close(client_fd);
-		buffer_free(&m);
-		return;
-	}
-
-	buffer_clear(&m);
 	if (ssh_msg_recv(client_fd, &m) == -1) {
 		error("%s: client msg_recv failed", __func__);
 		close(client_fd);
@@ -944,6 +838,175 @@
 }
 
 static void
+client_process_sshmux_command_socks (fd_set *readset, int client_fd, u_int flags)
+{
+        Buffer m;
+        Channel *nc;
+        int ver;
+
+        buffer_init (&m);
+	/* Reply for SSHMUX_COMMAND_SOCKS */
+
+	if (ssh_msg_recv(client_fd, &m) == -1) {
+		error("%s: client msg_recv failed", __func__);
+		close(client_fd);
+		buffer_free(&m);
+		return;
+	}
+
+	if ((ver = buffer_get_char(&m)) != SSHMUX_VER) {
+		error("%s: wrong client version %d", __func__, ver);
+		buffer_free(&m);
+		close(client_fd);
+		return;
+	}
+
+        /* we now hand the rest of the connection of the normal socks handler */
+
+        nc = channel_new ("dynamic-tcpip", SSH_CHANNEL_DYNAMIC, client_fd,
+            client_fd, -1, CHAN_TCP_WINDOW_DEFAULT, CHAN_TCP_PACKET_DEFAULT,
+            0, "dynamic-tcpip", 1);
+        nc->listening_port = 0;
+        nc->host_port = 0;
+        strlcpy(nc->path, "socks", sizeof(nc->path));
+
+        nc->delayed = 1;
+
+        buffer_free (&m);
+}
+
+static void
+client_process_control(fd_set *readset)
+{
+        Buffer m;
+	int client_fd, ver, allowed;
+	socklen_t addrlen;
+	struct sockaddr_storage addr;
+	u_int command, flags;
+        uid_t euid;
+        gid_t egid;
+
+
+	/*
+	 * Accept connection on control socket
+	 */
+	if (control_fd == -1 || !FD_ISSET(control_fd, readset))
+		return;
+
+	memset(&addr, 0, sizeof(addr));
+	addrlen = sizeof(addr);
+	if ((client_fd = accept(control_fd,
+	    (struct sockaddr*)&addr, &addrlen)) == -1) {
+		error("%s accept: %s", __func__, strerror(errno));
+		return;
+	}
+
+	if (getpeereid(client_fd, &euid, &egid) < 0) {
+		error("%s getpeereid failed: %s", __func__, strerror(errno));
+		close(client_fd);
+		return;
+	}
+	if ((euid != 0) && (getuid() != euid)) {
+		error("control mode uid mismatch: peer euid %u != uid %u",
+		    (u_int) euid, (u_int) getuid());
+		close(client_fd);
+		return;
+	}
+
+	unset_nonblock(client_fd);
+
+	/* Read command */
+	buffer_init(&m);
+	if (ssh_msg_recv(client_fd, &m) == -1) {
+		error("%s: client msg_recv failed", __func__);
+		close(client_fd);
+		buffer_free(&m);
+		return;
+	}
+	if ((ver = buffer_get_char(&m)) != SSHMUX_VER) {
+		error("%s: wrong client version %d", __func__, ver);
+		buffer_free(&m);
+		close(client_fd);
+		return;
+	}
+
+	allowed = 1;
+	command = buffer_get_int(&m);
+	flags = buffer_get_int(&m);
+
+	buffer_clear(&m);
+
+	switch (command) {
+	case SSHMUX_COMMAND_OPEN:
+		if (options.control_master == SSHCTL_MASTER_ASK ||
+		    options.control_master == SSHCTL_MASTER_AUTO_ASK)
+			allowed = ask_permission("Allow shared connection "
+			    "to %s? ", host);
+		break;
+	case SSHMUX_COMMAND_TERMINATE:
+		if (options.control_master == SSHCTL_MASTER_ASK ||
+		    options.control_master == SSHCTL_MASTER_AUTO_ASK)
+			allowed = ask_permission("Terminate shared connection "
+			    "to %s? ", host);
+		if (allowed)
+			quit_pending = 1;
+                break;
+	case SSHMUX_COMMAND_ALIVE_CHECK:
+                break;
+        case SSHMUX_COMMAND_SOCKS:
+		if (options.control_master == SSHCTL_MASTER_ASK ||
+		    options.control_master == SSHCTL_MASTER_AUTO_ASK)
+			allowed = ask_permission("Allow shared network connection "
+			    "to %s? ", host);
+		break;
+	default:
+		error("Unsupported command %d", command);
+		buffer_free(&m);
+		close(client_fd);
+		return;
+	}
+
+        /* Common reply for all commands */
+        buffer_clear(&m);
+        buffer_put_int(&m, allowed);
+        buffer_put_int(&m, getpid());
+        if (ssh_msg_send(client_fd, SSHMUX_VER, &m) == -1) {
+                error("%s: client msg_send failed", __func__);
+                close(client_fd);
+                buffer_free(&m);
+                return;
+        }
+
+        if (command == SSHMUX_COMMAND_OPEN) {
+                if (!allowed) {
+                        error("Refused control connection");
+                        buffer_free(&m);
+                        close(client_fd);
+                        return;
+                }
+                buffer_free(&m);
+                client_process_sshmux_command_open (readset, client_fd, flags);
+                return;
+        }
+        if (command == SSHMUX_COMMAND_SOCKS) {
+                if (!allowed) {
+                        error("Refused control connection");
+                        buffer_free(&m);
+                        close(client_fd);
+                        return;
+                }
+                buffer_free(&m);
+                client_process_sshmux_command_socks (readset, client_fd, flags);
+                return;
+        }
+
+        buffer_free(&m);
+        close(client_fd);
+        return;
+}
+
+
+static void
 process_cmdline(void)
 {
 	void (*handler)(int);
diff -u openssh-4.9p1/clientloop.h openssh-4.9p1-mux-socks/clientloop.h
--- openssh-4.9p1/clientloop.h	2007-08-08 06:32:41.000000000 +0200
+++ openssh-4.9p1-mux-socks/clientloop.h	2008-06-19 18:20:32.000000000 +0200
@@ -53,6 +53,7 @@
 #define SSHMUX_COMMAND_OPEN		1	/* Open new connection */
 #define SSHMUX_COMMAND_ALIVE_CHECK	2	/* Check master is alive */
 #define SSHMUX_COMMAND_TERMINATE	3	/* Ask master to exit */
+#define SSHMUX_COMMAND_SOCKS            4       /* Open new network connection */
 
 #define SSHMUX_FLAG_TTY			(1)	/* Request tty on open */
 #define SSHMUX_FLAG_SUBSYS		(1<<1)	/* Subsystem request on open */
-------------- next part --------------
# first packet to establish the command
printf "\x00\x00\x00\x09"       # ssh packet length
printf "\x01"                   # MUX version 1
printf "\x00\x00\x00\x04"       # command = SSHMUX_COMMAND_SOCKS
printf "\x00\x00\x00\x00"       # flags (unused)

# second packet, just version, SSH_COMMAND_OPEN uses it so we do to
printf "\x00\x00\x00\x01"       # ssh packet length
printf "\x01"                   # MUX version 1

# the rest is just socks4 protocol
printf "\x04"                   # version 4
printf "\x01"                   # command connect
printf "\x04\xd2"               # port (1234)
printf "\x7f\x00\x00\x01"       # ip (127.0.0.1)
printf "anonymous\x00"               # username


More information about the openssh-unix-dev mailing list