- markus@cvs.openbsd.org 2011/09/23 07:45:05
     [mux.c readconf.h channels.h compat.h compat.c ssh.c readconf.c channels.c     version.h]
     unbreak remote portforwarding with dynamic allocated listen ports:
     1) send the actual listen port in the open message (instead of 0).
        this allows multiple forwardings with a dynamic listen port
     2) update the matching permit-open entry, so we can identify where
        to connect to
     report: den at skbkontur.ru and P. Szczygielski
     feedback and ok djm@
diff --git a/ChangeLog b/ChangeLog
index 2e1780a..461c3c1 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -5,6 +5,16 @@
      [channels.c auth-options.c servconf.c channels.h sshd.8]
      Add wildcard support to PermitOpen, allowing things like "PermitOpen
      localhost:*".  bz #1857, ok djm markus.
+   - markus@cvs.openbsd.org 2011/09/23 07:45:05
+     [mux.c readconf.h channels.h compat.h compat.c ssh.c readconf.c channels.c
+     version.h]
+     unbreak remote portforwarding with dynamic allocated listen ports:
+     1) send the actual listen port in the open message (instead of 0).
+        this allows multiple forwardings with a dynamic listen port
+     2) update the matching permit-open entry, so we can identify where
+        to connect to
+     report: den at skbkontur.ru and P. Szczygielski
+     feedback and ok djm@
 
 20110929
  - (djm) [configure.ac defines.h] No need to detect sizeof(char); patch
diff --git a/channels.c b/channels.c
index 00e9af8..f6e9b4d 100644
--- a/channels.c
+++ b/channels.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: channels.c,v 1.314 2011/09/23 00:22:04 dtucker Exp $ */
+/* $OpenBSD: channels.c,v 1.315 2011/09/23 07:45:05 markus Exp $ */
 /*
  * Author: Tatu Ylonen <ylo@cs.hut.fi>
  * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
@@ -2814,8 +2814,12 @@
 		    0, "port listener", 1);
 		c->path = xstrdup(host);
 		c->host_port = port_to_connect;
-		c->listening_port = listen_port;
 		c->listening_addr = addr == NULL ? NULL : xstrdup(addr);
+		if (listen_port == 0 && allocated_listen_port != NULL &&
+		    !(datafellows & SSH_BUG_DYNAMIC_RPORT))
+			c->listening_port = *allocated_listen_port;
+		else
+			c->listening_port = listen_port;
 		success = 1;
 	}
 	if (success == 0)
@@ -2924,12 +2928,14 @@
 /*
  * Initiate forwarding of connections to port "port" on remote host through
  * the secure channel to host:port from local side.
+ * Returns handle (index) for updating the dynamic listen port with
+ * channel_update_permitted_opens().
  */
 int
 channel_request_remote_forwarding(const char *listen_host, u_short listen_port,
     const char *host_to_connect, u_short port_to_connect)
 {
-	int type, success = 0;
+	int type, success = 0, idx = -1;
 
 	/* Send the forward request to the remote side. */
 	if (compat20) {
@@ -2968,12 +2974,12 @@
 		/* Record that connection to this host/port is permitted. */
 		permitted_opens = xrealloc(permitted_opens,
 		    num_permitted_opens + 1, sizeof(*permitted_opens));
-		permitted_opens[num_permitted_opens].host_to_connect = xstrdup(host_to_connect);
-		permitted_opens[num_permitted_opens].port_to_connect = port_to_connect;
-		permitted_opens[num_permitted_opens].listen_port = listen_port;
-		num_permitted_opens++;
+		idx = num_permitted_opens++;
+		permitted_opens[idx].host_to_connect = xstrdup(host_to_connect);
+		permitted_opens[idx].port_to_connect = port_to_connect;
+		permitted_opens[idx].listen_port = listen_port;
 	}
-	return (success ? 0 : -1);
+	return (idx);
 }
 
 /*
@@ -3078,6 +3084,35 @@
 	all_opens_permitted = 0;
 }
 
+/*
+ * Update the listen port for a dynamic remote forward, after
+ * the actual 'newport' has been allocated. If 'newport' < 0 is
+ * passed then they entry will be invalidated.
+ */
+void
+channel_update_permitted_opens(int idx, int newport)
+{
+	if (idx < 0 || idx >= num_permitted_opens) {
+		debug("channel_update_permitted_opens: index out of range:"
+		    " %d num_permitted_opens %d", idx, num_permitted_opens);
+		return;
+	}
+	debug("%s allowed port %d for forwarding to host %s port %d",
+	    newport > 0 ? "Updating" : "Removing",
+	    newport,
+	    permitted_opens[idx].host_to_connect,
+	    permitted_opens[idx].port_to_connect);
+	if (newport >= 0)  {
+		permitted_opens[idx].listen_port = 
+		    (datafellows & SSH_BUG_DYNAMIC_RPORT) ? 0 : newport;
+	} else {
+		permitted_opens[idx].listen_port = 0;
+		permitted_opens[idx].port_to_connect = 0;
+		xfree(permitted_opens[idx].host_to_connect);
+		permitted_opens[idx].host_to_connect = NULL;
+	}
+}
+
 int
 channel_add_adm_permitted_opens(char *host, int port)
 {
diff --git a/channels.h b/channels.h
index 6f316c8..c1f01c4 100644
--- a/channels.h
+++ b/channels.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: channels.h,v 1.108 2011/09/23 00:22:04 dtucker Exp $ */
+/* $OpenBSD: channels.h,v 1.109 2011/09/23 07:45:05 markus Exp $ */
 
 /*
  * Author: Tatu Ylonen <ylo@cs.hut.fi>
@@ -253,6 +253,7 @@
 void     channel_permit_all_opens(void);
 void	 channel_add_permitted_opens(char *, int);
 int	 channel_add_adm_permitted_opens(char *, int);
+void	 channel_update_permitted_opens(int, int);
 void	 channel_clear_permitted_opens(void);
 void	 channel_clear_adm_permitted_opens(void);
 void 	 channel_print_adm_permitted_opens(void);
diff --git a/compat.c b/compat.c
index df3541d..0dc089f 100644
--- a/compat.c
+++ b/compat.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: compat.c,v 1.78 2008/09/11 14:22:37 markus Exp $ */
+/* $OpenBSD: compat.c,v 1.79 2011/09/23 07:45:05 markus Exp $ */
 /*
  * Copyright (c) 1999, 2000, 2001, 2002 Markus Friedl.  All rights reserved.
  *
@@ -92,6 +92,7 @@
 		{ "OpenSSH_3.*",	SSH_OLD_FORWARD_ADDR },
 		{ "Sun_SSH_1.0*",	SSH_BUG_NOREKEY|SSH_BUG_EXTEOF},
 		{ "OpenSSH_4*",		0 },
+		{ "OpenSSH_5*",		SSH_NEW_OPENSSH|SSH_BUG_DYNAMIC_RPORT},
 		{ "OpenSSH*",		SSH_NEW_OPENSSH },
 		{ "*MindTerm*",		0 },
 		{ "2.1.0*",		SSH_BUG_SIGBLOB|SSH_BUG_HMAC|
diff --git a/compat.h b/compat.h
index 16cf282..3ae5d9c 100644
--- a/compat.h
+++ b/compat.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: compat.h,v 1.42 2008/09/11 14:22:37 markus Exp $ */
+/* $OpenBSD: compat.h,v 1.43 2011/09/23 07:45:05 markus Exp $ */
 
 /*
  * Copyright (c) 1999, 2000, 2001 Markus Friedl.  All rights reserved.
@@ -58,6 +58,7 @@
 #define SSH_OLD_FORWARD_ADDR	0x01000000
 #define SSH_BUG_RFWD_ADDR	0x02000000
 #define SSH_NEW_OPENSSH		0x04000000
+#define SSH_BUG_DYNAMIC_RPORT	0x08000000
 
 void     enable_compat13(void);
 void     enable_compat20(void);
diff --git a/mux.c b/mux.c
index 6b63d81..52aec62 100644
--- a/mux.c
+++ b/mux.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: mux.c,v 1.30 2011/09/09 22:46:44 djm Exp $ */
+/* $OpenBSD: mux.c,v 1.31 2011/09/23 07:45:05 markus Exp $ */
 /*
  * Copyright (c) 2002-2008 Damien Miller <djm@openbsd.org>
  *
@@ -601,12 +601,16 @@
 			buffer_put_int(&out, MUX_S_REMOTE_PORT);
 			buffer_put_int(&out, fctx->rid);
 			buffer_put_int(&out, rfwd->allocated_port);
+			channel_update_permitted_opens(rfwd->handle,
+			   rfwd->allocated_port);
 		} else {
 			buffer_put_int(&out, MUX_S_OK);
 			buffer_put_int(&out, fctx->rid);
 		}
 		goto out;
 	} else {
+		if (rfwd->listen_port == 0)
+			channel_update_permitted_opens(rfwd->handle, -1);
 		xasprintf(&failmsg, "remote port forwarding failed for "
 		    "listen port %d", rfwd->listen_port);
 	}
@@ -745,8 +749,9 @@
 	} else {
 		struct mux_channel_confirm_ctx *fctx;
 
-		if (channel_request_remote_forwarding(fwd.listen_host,
-		    fwd.listen_port, fwd.connect_host, fwd.connect_port) < 0)
+		fwd.handle = channel_request_remote_forwarding(fwd.listen_host,
+		    fwd.listen_port, fwd.connect_host, fwd.connect_port);
+		if (fwd.handle < 0)
 			goto fail;
 		add_remote_forward(&options, &fwd);
 		fctx = xcalloc(1, sizeof(*fctx));
@@ -781,7 +786,7 @@
 	char *fwd_desc = NULL;
 	const char *error_reason = NULL;
 	u_int ftype;
-	int i, ret = 0;
+	int i, listen_port, ret = 0;
 
 	fwd.listen_host = fwd.connect_host = NULL;
 	if (buffer_get_int_ret(&ftype, m) != 0 ||
@@ -836,9 +841,13 @@
 		/*
 		 * This shouldn't fail unless we confused the host/port
 		 * between options.remote_forwards and permitted_opens.
+		 * However, for dynamic allocated listen ports we need
+		 * to lookup the actual listen port.
 		 */
+	        listen_port = (fwd.listen_port == 0) ?
+		    found_fwd->allocated_port : fwd.listen_port;
 		if (channel_request_rforward_cancel(fwd.listen_host,
-		    fwd.listen_port) == -1)
+		    listen_port) == -1)
 			error_reason = "port not in permitted opens";
 	} else {	/* local and dynamic forwards */
 		/* Ditto */
diff --git a/readconf.c b/readconf.c
index 91dfa56..097bb05 100644
--- a/readconf.c
+++ b/readconf.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: readconf.c,v 1.193 2011/05/24 07:15:47 djm Exp $ */
+/* $OpenBSD: readconf.c,v 1.194 2011/09/23 07:45:05 markus Exp $ */
 /*
  * Author: Tatu Ylonen <ylo@cs.hut.fi>
  * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
@@ -294,6 +294,7 @@
 	fwd->listen_port = newfwd->listen_port;
 	fwd->connect_host = newfwd->connect_host;
 	fwd->connect_port = newfwd->connect_port;
+	fwd->handle = newfwd->handle;
 	fwd->allocated_port = 0;
 }
 
diff --git a/readconf.h b/readconf.h
index 5944cff..be30ee0 100644
--- a/readconf.h
+++ b/readconf.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: readconf.h,v 1.90 2011/05/24 07:15:47 djm Exp $ */
+/* $OpenBSD: readconf.h,v 1.91 2011/09/23 07:45:05 markus Exp $ */
 
 /*
  * Author: Tatu Ylonen <ylo@cs.hut.fi>
@@ -24,6 +24,7 @@
 	char	 *connect_host;		/* Host to connect. */
 	int	  connect_port;		/* Port to connect on connect_host. */
 	int	  allocated_port;	/* Dynamically allocated listen port */
+	int	  handle;		/* Handle for dynamic listen ports */
 }       Forward;
 /* Data structure for representing option data. */
 
diff --git a/ssh.c b/ssh.c
index f437dec..9cee959 100644
--- a/ssh.c
+++ b/ssh.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: ssh.c,v 1.365 2011/09/09 22:46:44 djm Exp $ */
+/* $OpenBSD: ssh.c,v 1.366 2011/09/23 07:45:05 markus Exp $ */
 /*
  * Author: Tatu Ylonen <ylo@cs.hut.fi>
  * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
@@ -1021,11 +1021,17 @@
 	debug("remote forward %s for: listen %d, connect %s:%d",
 	    type == SSH2_MSG_REQUEST_SUCCESS ? "success" : "failure",
 	    rfwd->listen_port, rfwd->connect_host, rfwd->connect_port);
-	if (type == SSH2_MSG_REQUEST_SUCCESS && rfwd->listen_port == 0) {
-		rfwd->allocated_port = packet_get_int();
-		logit("Allocated port %u for remote forward to %s:%d",
-		    rfwd->allocated_port,
-		    rfwd->connect_host, rfwd->connect_port);
+	if (rfwd->listen_port == 0) {
+		if (type == SSH2_MSG_REQUEST_SUCCESS) {
+			rfwd->allocated_port = packet_get_int();
+			logit("Allocated port %u for remote forward to %s:%d",
+			    rfwd->allocated_port,
+			    rfwd->connect_host, rfwd->connect_port);
+			channel_update_permitted_opens(rfwd->handle,
+			    rfwd->allocated_port);
+		} else {
+			channel_update_permitted_opens(rfwd->handle, -1);
+		}
 	}
 	
 	if (type == SSH2_MSG_REQUEST_FAILURE) {
@@ -1117,19 +1123,22 @@
 		    options.remote_forwards[i].listen_port,
 		    options.remote_forwards[i].connect_host,
 		    options.remote_forwards[i].connect_port);
-		if (channel_request_remote_forwarding(
+		options.remote_forwards[i].handle =
+		    channel_request_remote_forwarding(
 		    options.remote_forwards[i].listen_host,
 		    options.remote_forwards[i].listen_port,
 		    options.remote_forwards[i].connect_host,
-		    options.remote_forwards[i].connect_port) < 0) {
+		    options.remote_forwards[i].connect_port);
+		if (options.remote_forwards[i].handle < 0) {
 			if (options.exit_on_forward_failure)
 				fatal("Could not request remote forwarding.");
 			else
 				logit("Warning: Could not request remote "
 				    "forwarding.");
+		} else {
+			client_register_global_confirm(ssh_confirm_remote_forward,
+			    &options.remote_forwards[i]);
 		}
-		client_register_global_confirm(ssh_confirm_remote_forward,
-		    &options.remote_forwards[i]);
 	}
 
 	/* Initiate tunnel forwarding. */
diff --git a/version.h b/version.h
index 6a1acb3..0c0dfcb 100644
--- a/version.h
+++ b/version.h
@@ -1,6 +1,6 @@
-/* $OpenBSD: version.h,v 1.62 2011/08/02 23:13:01 djm Exp $ */
+/* $OpenBSD: version.h,v 1.63 2011/09/23 07:45:05 markus Exp $ */
 
-#define SSH_VERSION	"OpenSSH_5.9"
+#define SSH_VERSION	"OpenSSH_6.0-beta"
 
 #define SSH_PORTABLE	"p1"
 #define SSH_RELEASE	SSH_VERSION SSH_PORTABLE