Protocol 2 remote forwarding patch

Tomoyuki Murakami tomoyuki at pobox.com
Sun Feb 11 02:32:01 EST 2001


Hi all,

I'm very new in this list, as looking for codes to plug up the lack of
functionality of "Protocol 2 Remote Forwardig".
Fortunately, I could find it in MARC's archive. Mr. Jarno Huuskonen
posted the codes in Sept, last year, and I tried applying it to my
FreeBSD box environment.
I couldn't apply an original patch, of course, for incompatibility of
virsion. The original is openssh-2.2.0p1 and my is openssh-2.3.0 in
FreeBSD souce tree(src/crypto/openssh).  I made some fix to it to do
with my FreeBSD and confirmed work in a base line.

An attachment is a patch for FreeBSD-current(4.2) but, applying to
main source-stream(openssh-2.3.0.tgz) is also available.
I do not have an OpenBSD environment, and I have not tested in.

Thanks,
-- Tomo.
-------------- next part --------------
diff -ru openssh.orig/auth2.c openssh/auth2.c
--- openssh.orig/auth2.c	Wed Dec  6 20:11:25 2000
+++ openssh/auth2.c	Sat Feb 10 00:06:24 2001
@@ -60,6 +60,7 @@
 extern ServerOptions options;
 extern unsigned char *session_id2;
 extern int session_id2_len;
+extern int user_authenticated_as_root;  /* Jarno: from channels.c */
 
 static Authctxt	*x_authctxt = NULL;
 static int one = 1;
@@ -282,6 +283,13 @@
 	/* Log before sending the reply */
 	userauth_log(authctxt, authenticated, method);
 	userauth_reply(authctxt, authenticated);
+
+	if (authenticated == 1 &&
+	    authctxt->pw && authctxt->pw->pw_uid == (uid_t)0) {
+		user_authenticated_as_root = 1;
+	} else {
+		user_authenticated_as_root = 0;
+	}
 
 	xfree(service);
 	xfree(user);
diff -ru openssh.orig/channels.c openssh/channels.c
--- openssh.orig/channels.c	Tue Dec  5 20:11:48 2000
+++ openssh/channels.c	Sat Feb 10 00:00:54 2001
@@ -73,6 +73,8 @@
  */
 static Channel *channels = NULL;
 
+int user_authenticated_as_root;  /* set to true if user is root. */
+
 /*
  * Size of the channel array.  All slots of the array must always be
  * initialized (at least the type field); unused slots are marked with type
@@ -608,13 +610,17 @@
 		    "connect from %.200s port %d",
 		    c->listening_port, c->path, c->host_port,
 		    remote_hostname, remote_port);
-		newch = channel_new("direct-tcpip",
+		newch = channel_new((c->type == SSH2_CHANNEL_PORT_LISTENER) ?
+				    "forwarded-tcpip" : "direct-tcpip",
 		    SSH_CHANNEL_OPENING, newsock, newsock, -1,
 		    c->local_window_max, c->local_maxpacket,
 		    0, xstrdup(buf), 1);
 		if (compat20) {
 			packet_start(SSH2_MSG_CHANNEL_OPEN);
-			packet_put_cstring("direct-tcpip");
+			if (c->type == SSH2_CHANNEL_PORT_LISTENER)
+				packet_put_cstring("forwarded-tcpip");
+			else
+				packet_put_cstring("direct-tcpip");
 			packet_put_int(newch);
 			packet_put_int(c->local_window_max);
 			packet_put_int(c->local_maxpacket);
@@ -820,10 +826,12 @@
 	channel_pre[SSH_CHANNEL_OPEN] =			&channel_pre_open_20;
 	channel_pre[SSH_CHANNEL_X11_OPEN] =		&channel_pre_x11_open;
 	channel_pre[SSH_CHANNEL_PORT_LISTENER] =	&channel_pre_listener;
+	channel_pre[SSH2_CHANNEL_PORT_LISTENER] =	&channel_pre_listener;
 	channel_pre[SSH_CHANNEL_X11_LISTENER] =		&channel_pre_listener;
 
 	channel_post[SSH_CHANNEL_OPEN] =		&channel_post_open_2;
 	channel_post[SSH_CHANNEL_PORT_LISTENER] =	&channel_post_port_listener;
+	channel_post[SSH2_CHANNEL_PORT_LISTENER] =	&channel_post_port_listener;
 	channel_post[SSH_CHANNEL_X11_LISTENER] =	&channel_post_x11_listener;
 }
 
@@ -1309,6 +1317,96 @@
 	c->remote_window += adjust;
 }
 
+/* Jarno Huuskonen: This is called when server receives 
+ * SSH2_MSG_GLOBAL_REQUEST. Handles both "tcpip-forward" and
+ * "cancel-tcpip-forward" requests.
+ */
+void 
+channel_server_global_request(int type, int plen, void *ctxt)
+{
+	char *rtype;
+	char want_reply;
+	int success = 0;
+
+	rtype = packet_get_string(NULL);
+	want_reply = packet_get_char();
+	
+	if ( strcmp(rtype, "tcpip-forward") == 0 ) {
+		char *address_to_bind;
+		int port_to_bind;
+
+		address_to_bind = packet_get_string(NULL);
+		port_to_bind = packet_get_int();
+
+		/* Check if the client is allowed to forward (this port) */
+		if ( port_to_bind < IPPORT_RESERVED && !user_authenticated_as_root ) {
+			log("User tries to forward privileged port %d", port_to_bind);
+			packet_send_debug("Requested forwarding of port %d but user is not root.", port_to_bind);
+			success = 0;
+		}
+		else {
+			/* Start listening on the port */
+			channel_request_local_forwarding( port_to_bind, address_to_bind,
+																				port_to_bind, 1, 
+																				1 /* ssh2_remote_fwd*/);
+			/* NOT REACHED if error (disconnects). 
+			 * Note: if error xfree not called
+			 * for address_to_bind
+			 */
+			success = 1;
+		}
+
+		xfree( address_to_bind );
+	}
+
+	/* TODO: This is untested !!! create some test code !!!*/
+	if ( strcmp(rtype, "cancel-tcpip-forward") == 0 ) {
+		char *address_to_bind;
+		int port_to_bind;
+		int chan;
+
+		address_to_bind = packet_get_string(NULL);
+		port_to_bind = packet_get_int();
+
+		/* Lookup the channel listening for this port:
+			 First see if the channel type is SSH2_CHANNEL_PORT_LISTENER and then 
+			 compare port/addr.
+			 TODO: Is it safe to use strcmp on address_to_bind ?
+		 */
+		for (chan = 0; chan < channels_alloc; chan++) {
+			if ( channels[chan].type == SSH2_CHANNEL_PORT_LISTENER ) {
+				if ( channels[chan].listening_port == port_to_bind &&
+						 (strcmp(address_to_bind, channels[chan].path) == 0) )
+					break;
+			}
+		}
+		
+		if ( chan < channels_alloc ) {
+			/* We have a winner --> close the channel*/
+			channel_free( channels[chan].self );
+			success = 1;
+		}
+		else {
+			debug("Invalid cancel-tcpip-forward request: Couldn't find channel.");
+		}
+		xfree( address_to_bind );
+	}
+
+	/* Client requested a reply */
+	if ( want_reply ) {
+		if ( success ) {
+			packet_start(SSH2_MSG_REQUEST_SUCCESS);
+		}
+		else {
+			packet_start(SSH2_MSG_REQUEST_FAILURE);
+		}
+		/* Now send the SUCCESS/FAILURE */
+		packet_send();
+		packet_write_wait();
+	}
+	xfree(rtype);
+}
+
 /*
  * Stops listening for channels, and removes any unix domain sockets that we
  * might have.
@@ -1326,6 +1424,7 @@
 			channel_free(i);
 			break;
 		case SSH_CHANNEL_PORT_LISTENER:
+		case SSH2_CHANNEL_PORT_LISTENER:
 		case SSH_CHANNEL_X11_LISTENER:
 			close(channels[i].sock);
 			channel_free(i);
@@ -1369,6 +1468,7 @@
 		case SSH_CHANNEL_FREE:
 		case SSH_CHANNEL_X11_LISTENER:
 		case SSH_CHANNEL_PORT_LISTENER:
+		case SSH2_CHANNEL_PORT_LISTENER:
 		case SSH_CHANNEL_CLOSED:
 		case SSH_CHANNEL_AUTH_SOCKET:
 			continue;
@@ -1414,6 +1514,7 @@
 		case SSH_CHANNEL_FREE:
 		case SSH_CHANNEL_X11_LISTENER:
 		case SSH_CHANNEL_PORT_LISTENER:
+		case SSH2_CHANNEL_PORT_LISTENER:
 		case SSH_CHANNEL_CLOSED:
 		case SSH_CHANNEL_AUTH_SOCKET:
 			continue;
@@ -1449,7 +1550,8 @@
 
 void
 channel_request_local_forwarding(u_short port, const char *host,
-				 u_short host_port, int gateway_ports)
+				 u_short host_port, int gateway_ports,
+				 int ssh2_remote_fwd)
 {
 	int success, ch, sock, on = 1;
 	struct addrinfo hints, *ai, *aitop;
@@ -1512,7 +1614,8 @@
 		}
 		/* Allocate a channel number for the socket. */
 		ch = channel_new(
-		    "port listener", SSH_CHANNEL_PORT_LISTENER,
+		    "port listener", ssh2_remote_fwd ?
+		    SSH2_CHANNEL_PORT_LISTENER : SSH_CHANNEL_PORT_LISTENER,
 		    sock, sock, -1,
 		    CHAN_TCP_WINDOW_DEFAULT, CHAN_TCP_PACKET_DEFAULT,
 		    0, xstrdup("port listener"), 1);
@@ -1536,15 +1639,12 @@
 				  u_short port_to_connect)
 {
 	int payload_len;
+	int type;
+	int success = 0;
 	/* Record locally that connection to this host/port is permitted. */
 	if (num_permitted_opens >= SSH_MAX_FORWARDS_PER_DIRECTION)
 		fatal("channel_request_remote_forwarding: too many forwards");
 
-	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++;
-
 	/* Send the forward request to the remote side. */
 	if (compat20) {
 		const char *address_to_bind = "0.0.0.0";
@@ -1553,19 +1653,109 @@
 		packet_put_char(0);			/* boolean: want reply */
 		packet_put_cstring(address_to_bind);
 		packet_put_int(listen_port);
-	} else {
+		packet_send();
+		packet_write_wait();
+		success = 1; /* assume that server accepts the request and put
+				the forward request to permitted_opens */
+	} else {  /* protocol 1 */
 		packet_start(SSH_CMSG_PORT_FORWARD_REQUEST);
 		packet_put_int(listen_port);
 		packet_put_cstring(host_to_connect);
 		packet_put_int(port_to_connect);
 		packet_send();
 		packet_write_wait();
-		/*
-		 * Wait for response from the remote side.  It will send a disconnect
-		 * message on failure, and we will never see it here.
+		/* Jarno: Server can send SSH_SMSG_FAILURE if it won't do port
+		 *        forwardings. Read the server reply.
 		 */
-		packet_read_expect(&payload_len, SSH_SMSG_SUCCESS);
+		type = packet_read(&payload_len);
+		switch (type) {
+		case SSH_SMSG_SUCCESS:
+			success = 1;
+			break;
+		case SSH_SMSG_FAILURE:
+			log("Warning: Server doesn't do port forwarding.");
+			break;
+		default:
+			/* Unknown packet */
+			packet_disconnect("Protocol error for port forward request: received packet type %d.", type);
+		}
+	}
+
+	if ( success ) {
+		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++;
+	}
+}
+
+/* Jarno Huuskonen:
+ * This gets called after ssh client has received 
+ * SSH2_MSG_GLOBAL_REQUEST type "forwarded-tcpip".
+ *
+ * returns new channel if OK or NULL for failure.
+ */
+Channel*
+client_forwarded_tcpip_request(const char *request_type, int rchan,
+			       int rwindow, int rmaxpack)
+{
+	Channel* c = NULL;
+	int sock;
+	char *listen_address;      /* Remote (server) address that is listening
+				      for the connection */
+	int listen_port;
+	char* originator_address;   /* Address of the client connecting to 
+				       listen_address */
+	int originator_port;        /* Client port */
+
+	unsigned int client_len, connected_len;
+
+	int newch;
+	int i;
+
+	debug("ssh2 server tries to open forwarded-tcpip channel.");
+
+	/* Get rest of the packet */
+	listen_address = packet_get_string(&connected_len);
+	listen_port = packet_get_int();
+	originator_address = packet_get_string(&client_len);
+	originator_port = packet_get_int();
+	packet_done();
+
+	/* Check if we have requested this remote forwarding 
+	 * Note: this is not fool proof, because we don't ask the server to
+	 * acknowledge our remote forward request.
+	 */
+	for (i = 0; i<num_permitted_opens; i++) {
+		if ( permitted_opens[i].listen_port == listen_port ) {
+			break;
+		}
+	}
+
+	/* We haven't requested the connection to be forwarded ! */
+	if ( i >= num_permitted_opens ) {
+		log("Received request to open remote forwarded channel (%d) but the request was denied", rchan);
+		return NULL;
 	}
+
+	/* TODO: Somekind of access control ??
+	 * Maybe tcp_wrappers/username/group based access control ??
+	 */
+
+	/* Open socket and allocate a channel for it */
+	sock = channel_connect_to(permitted_opens[i].host_to_connect, 
+				  permitted_opens[i].port_to_connect);
+
+	if ( sock >= 0 ) {
+		newch = channel_new("forwarded-tcpip", SSH_CHANNEL_OPEN,
+				    sock, sock, -1, 4*1024, 32*1024, 0, 
+				    xstrdup(originator_address), 1);
+		c = channel_lookup( newch );
+	}
+	/* client_input_channel_open calls xfree(request_type) Don't call it here */
+	xfree(originator_address);
+	xfree(listen_address);
+	return c;
 }
 
 /*
@@ -1595,7 +1785,11 @@
 	/*
 	 * Initiate forwarding,
 	 */
-	channel_request_local_forwarding(port, hostname, host_port, gateway_ports);
+	/* Jarno: the last parameter is used to signal if this is protocol 2
+	 *        server listening for remote forward --> false 
+	 */
+	channel_request_local_forwarding(port, hostname, host_port,
+					 gateway_ports, 0);
 
 	/* Free the argument string. */
 	xfree(hostname);
diff -ru openssh.orig/channels.h openssh/channels.h
--- openssh.orig/channels.h	Tue Dec  5 20:11:48 2000
+++ openssh/channels.h	Sat Feb 10 00:01:29 2001
@@ -49,7 +49,8 @@
 #define SSH_CHANNEL_INPUT_DRAINING	8	/* sending remaining data to conn */
 #define SSH_CHANNEL_OUTPUT_DRAINING	9	/* sending remaining data to app */
 #define SSH_CHANNEL_LARVAL		10	/* larval session */
-#define SSH_CHANNEL_MAX_TYPE		11
+#define SSH2_CHANNEL_PORT_LISTENER	11
+#define SSH_CHANNEL_MAX_TYPE		12
 
 /*
  * Data structure for channel data.  This is iniailized in channel_allocate
@@ -149,6 +150,9 @@
 void	channel_input_window_adjust(int type, int plen, void *ctxt);
 void	channel_input_open(int type, int plen, void *ctxt);
 
+/* Jarno Huuskonen: */
+void    channel_server_global_request(int type, int plen, void *ctxt);
+
 /* Sets specific protocol options. */
 void    channel_set_options(int hostname_in_open);
 
@@ -205,9 +209,12 @@
  * channel to host:port from remote side.  This never returns if there was an
  * error.
  */
+/* Jarno: added ssh2_remote_fwd flag. used when protocol 2 server gets
+ *        tcpip-forward request.
+ */
 void
 channel_request_local_forwarding(u_short port, const char *host,
-    u_short remote_port, int gateway_ports);
+    u_short remote_port, int gateway_ports, int ssh2_remote_fwd);
 
 /*
  * Initiate forwarding of connections to port "port" on remote host through
@@ -218,6 +225,12 @@
 void
 channel_request_remote_forwarding(u_short port, const char *host,
     u_short remote_port);
+
+/* Jarno Huuskonen:
+ */
+Channel *
+client_forwarded_tcpip_request(const char *request_type, int rchan,
+			       int rwindow, int rmaxpack);
 
 /*
  * Permits opening to any host/port in SSH_MSG_PORT_OPEN.  This is usually
diff -ru openssh.orig/clientloop.c openssh/clientloop.c
--- openssh.orig/clientloop.c	Tue Dec  5 20:11:49 2000
+++ openssh/clientloop.c	Fri Feb  9 23:14:48 2001
@@ -1036,6 +1036,11 @@
 	debug("client_input_channel_open: ctype %s rchan %d win %d max %d",
 	    ctype, rchan, rwindow, rmaxpack);
 
+	/* Jarno: check if ssh2 server tries to open remote forward channel.
+	 */
+	if (strcmp(ctype, "forwarded-tcpip") == 0) {
+		c = client_forwarded_tcpip_request(ctype, rchan, rwindow, rmaxpack);
+	}
 	if (strcmp(ctype, "x11") == 0 && options.forward_x11) {
 		int sock;
 		char *originator;
diff -ru openssh.orig/serverloop.c openssh/serverloop.c
--- openssh.orig/serverloop.c	Tue Dec  5 20:11:55 2000
+++ openssh/serverloop.c	Fri Feb  9 23:20:21 2001
@@ -738,6 +738,8 @@
 
 	/* XXX check permission */
 	if (no_port_forwarding_flag || !options.allow_tcp_forwarding) {
+	  packet_send_debug("Server configuration rejects port forwardings.");
+	  debug("Port forwarding disabled in server configuration.");
 		xfree(target);
 		xfree(originator);
 		return -1;
@@ -836,6 +838,7 @@
 	dispatch_set(SSH2_MSG_CHANNEL_OPEN_FAILURE, &channel_input_open_failure);
 	dispatch_set(SSH2_MSG_CHANNEL_REQUEST, &channel_input_channel_request);
 	dispatch_set(SSH2_MSG_CHANNEL_WINDOW_ADJUST, &channel_input_window_adjust);
+	dispatch_set(SSH2_MSG_GLOBAL_REQUEST, &channel_server_global_request);
 }
 void
 server_init_dispatch_13()
diff -ru openssh.orig/ssh.c openssh/ssh.c
--- openssh.orig/ssh.c	Tue Dec  5 20:11:58 2000
+++ openssh/ssh.c	Fri Feb  9 23:27:08 2001
@@ -851,7 +851,7 @@
 		channel_request_local_forwarding(options.local_forwards[i].port,
 						 options.local_forwards[i].host,
 						 options.local_forwards[i].host_port,
-						 options.gateway_ports);
+						 options.gateway_ports, 0);
 	}
 
 	/* Initiate remote TCP/IP port forwardings. */
@@ -907,7 +907,25 @@
 		channel_request_local_forwarding(options.local_forwards[i].port,
 						 options.local_forwards[i].host,
 						 options.local_forwards[i].host_port,
-						 options.gateway_ports);
+						 options.gateway_ports, 0);
+	}
+}
+
+/* Jarno Huuskonen: ssh2 client calls this to initiate remote port forwarding
+ * requests.
+ */
+void
+init_remote_fwd(void)
+{
+	int i;
+	for (i = 0; i < options.num_remote_forwards; i++) {
+		debug("Connections to remote port %d forwarded to local address %.200s:%d",
+		      options.remote_forwards[i].port,
+		      options.remote_forwards[i].host,
+		      options.remote_forwards[i].host_port);
+		channel_request_remote_forwarding(options.remote_forwards[i].port,
+						  options.remote_forwards[i].host,
+						  options.remote_forwards[i].host_port);
 	}
 }
 
@@ -997,6 +1015,7 @@
 
 	/* should be pre-session */
 	init_local_fwd();
+	init_remote_fwd();
 	
 	/* If requested, let ssh continue in the background. */
 	if (fork_after_authentication_flag)


More information about the openssh-unix-dev mailing list