Protocol 2 remote forwarding patch (again)
Jarno Huuskonen
jhuuskon at messi.uku.fi
Wed Sep 20 05:20:29 EST 2000
Hi,
I'm sending you the little bit of code that I've made to get remote
portforwarding working (protocol 2). Unfortunately at the moment I have no
time to make any improvements to it.
I'll try to explain the changes I've made so hopefully it'll be easier for
someone to finish this.
ssh2 CLIENT changes:
ssh.c:
- added init_remote_fwd(void)
- call init_remote_fwd from ssh_session2
- changed channel_request_local_forwarding calls (added one extra parameter)
(see below)
channels.c:
- changed channel_request_remote_forwarding:
- if compat20 send SSH2_MSG_GLOBAL_REQUEST/tcpip-forward (doesn't want
a reply from the server because correctly handling all possible cases
might be tricky (rekey msgs))
- protocol1 handle SSH_MSG_FAILURE, because commercial server sends it
if portforwarding is not allowed
- added client_forwarded_tcpip_request. It's called when the server sends
SSH2_MSG_GLOBAL_REQUEST/forwarded-tcpip (some1 connects to the remote
socket). This just tries to validate the request and creates a new channel.
channels.h:
- client_forwarded_tcpip_request prototype
clientloop.c:
- modified client_input_channel_open to handle forwarded-tcpip message
(just calls client_forwarded_tcpip_request)
ssh2 SERVER changes:
auth2.c:
- modified input_userauth_request: if the user is authenticated as root
then user_authenticated_as_root flag is set to true.
channels.c:
- user_authenticated_as_root flag. Server checks this when remote forwarding
is requested to see if a reserved port can be forwarded.
- channel_post_port_listener: If the channel type is SSH2_CHANNEL_PORT_LISTENER
then sends forwarded-tcpip message (instead of direct-tcpip).
- Added pre/post handlers for the new SSH2_CHANNEL_PORT_LISTENER channel type
- channel_server_global_request: This function handles tcpip-forward/cancel-
tcpip-forward messages. (NOTE: handling cancel-tcpip-forward message is
untested).
- channel_request_local_forwarding:
added ssh2_remote_fwd parameter which is set to true when the server creates
remote forward listener. (This is needed so channel_post_port_listener knows
to send forwarded-tcpip msg).
channels.h:
- added the new SSH2_CHANNEL_PORT_LISTENER define
- channel_server_global_request prototype
- modified channel_request_local_forwarding prototype ssh2_remote_fwd param.
serverloop.c:
- added handling of SSH2_MSG_GLOBAL_REQUEST messages
(channel_server_global_request)
Stuff that needs more work:
- Test "cancel-tcpip-forward" handling.
- Make logging consistent
- Also somekind of access control for portforwarding might be nice
(Is this of any use when users have shell access ?)
Cheers,
- Jarno
--
Jarno Huuskonen - System Administrator | Jarno.Huuskonen at uku.fi
University of Kuopio - Computer Center | Work: +358 17 162822
PO BOX 1627, 70211 Kuopio, Finland | Mobile: +358 40 5388169
-------------- next part --------------
diff -u -r openssh-2.2.0p1/auth2.c openssh-2.2.0p1-jh/auth2.c
--- openssh-2.2.0p1/auth2.c Wed Aug 23 03:46:24 2000
+++ openssh-2.2.0p1-jh/auth2.c Tue Sep 19 18:41:43 2000
@@ -65,6 +65,7 @@
extern ServerOptions options;
extern unsigned char *session_id2;
extern int session_id2_len;
+extern int user_authenticated_as_root; /* Jarno: From channels.c */
/* protocol */
@@ -239,6 +240,14 @@
packet_put_char(0); /* XXX partial success, unused */
packet_send();
packet_write_wait();
+ }
+
+ /* Jarno: Set the user_authenticated_as_root flag */
+ if ( authenticated && pw && pw->pw_uid == (uid_t)0 ) {
+ user_authenticated_as_root = 1;
+ }
+ else {
+ user_authenticated_as_root = 0;
}
xfree(service);
diff -u -r openssh-2.2.0p1/channels.c openssh-2.2.0p1-jh/channels.c
--- openssh-2.2.0p1/channels.c Wed Aug 23 03:46:24 2000
+++ openssh-2.2.0p1-jh/channels.c Tue Sep 19 18:41:43 2000
@@ -56,6 +56,10 @@
*/
static Channel *channels = NULL;
+int user_authenticated_as_root; /* Set to true if user is root. Checked
+ * if the user can forward privileged ports
+ */
+
/*
* 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
@@ -586,13 +590,20 @@
"connect from %.200s port %d",
c->listening_port, c->path, c->host_port,
remote_hostname, remote_port);
- newch = channel_new("direct-tcpip",
+ /* Jarno: If the channel is SSH2 port listener (server) then send
+ * forwarded-tcpip message.
+ */
+ 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));
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);
@@ -798,10 +809,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;
}
@@ -1287,6 +1300,97 @@
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)
+{
+ 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.
@@ -1304,6 +1408,7 @@
channel_free(i);
break;
case SSH_CHANNEL_PORT_LISTENER:
+ case SSH2_CHANNEL_PORT_LISTENER: /* Jarno */
case SSH_CHANNEL_X11_LISTENER:
close(channels[i].sock);
channel_free(i);
@@ -1347,6 +1452,7 @@
case SSH_CHANNEL_FREE:
case SSH_CHANNEL_X11_LISTENER:
case SSH_CHANNEL_PORT_LISTENER:
+ case SSH2_CHANNEL_PORT_LISTENER: /* Jarno */
case SSH_CHANNEL_CLOSED:
case SSH_CHANNEL_AUTH_SOCKET:
continue;
@@ -1392,6 +1498,7 @@
case SSH_CHANNEL_FREE:
case SSH_CHANNEL_X11_LISTENER:
case SSH_CHANNEL_PORT_LISTENER:
+ case SSH2_CHANNEL_PORT_LISTENER: /* Jarno */
case SSH_CHANNEL_CLOSED:
case SSH_CHANNEL_AUTH_SOCKET:
continue;
@@ -1424,10 +1531,9 @@
* Initiate forwarding of connections to local port "port" through the secure
* channel to host:port from remote side.
*/
-
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;
@@ -1494,7 +1600,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"));
@@ -1518,38 +1625,149 @@
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";
packet_start(SSH2_MSG_GLOBAL_REQUEST);
packet_put_cstring("tcpip-forward");
- packet_put_char(0); /* boolean: want reply */
+
+ /* Don't ask for a reply because: while waiting for a reply server can
+ send rekey-msg and handling that correctly might be messy.
+ Not requesting a reply is not the best solution: We have no way of
+ know if the server doesn't allow port forwarding.
+ */
+ packet_put_char(0); /* Boolean 1 asks for 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 */
+
+ /*
+ type = packet_read(&payload_len);
+ switch (type) {
+ case SSH2_MSG_REQUEST_SUCCESS:
+ success = 1;
+ break;
+ case SSH2_MSG_REQUEST_FAILURE:
+ log("Warning: Server doesn't do port forwarding.");
+ break;
+ default:
+ packet_disconnect("Protocol error for port forward request: received packet type %d.", type);
+ }
+ */
+ }
+ 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); /* Expect reply from server */
+ 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));
+ 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;
+}
+
/*
* This is called after receiving CHANNEL_FORWARDING_REQUEST. This initates
* listening for the port, and sends back a success reply (or disconnect
@@ -1577,7 +1795,10 @@
/*
* 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);
@@ -1633,12 +1854,12 @@
return sock;
}
+
/*
* This is called after receiving PORT_OPEN message. This attempts to
* connect to the given host:port, and sends back CHANNEL_OPEN_CONFIRMATION
* or CHANNEL_OPEN_FAILURE.
*/
-
void
channel_input_port_open(int type, int plen)
{
@@ -1649,7 +1870,7 @@
/* Get remote channel number. */
remote_channel = packet_get_int();
-
+
/* Get host name to connect to. */
host = packet_get_string(&host_len);
diff -u -r openssh-2.2.0p1/channels.h openssh-2.2.0p1-jh/channels.h
--- openssh-2.2.0p1/channels.h Wed Aug 23 03:46:24 2000
+++ openssh-2.2.0p1-jh/channels.h Tue Sep 19 21:12:37 2000
@@ -15,7 +15,13 @@
#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 /* Jarno: protocol 2 remote port
+ * listener. (needs different type
+ * because with protocol 2 remote
+ * forward the server sends
+ * forwarded-tcpip (not direct-tcpip)
+ */
+#define SSH_CHANNEL_MAX_TYPE 12
/*
* Data structure for channel data. This is iniailized in channel_allocate
@@ -99,8 +105,12 @@
void channel_input_open_failure(int type, int plen);
void channel_input_port_open(int type, int plen);
void channel_input_window_adjust(int type, int plen);
+
void channel_input_open(int type, int plen);
+/* Jarno Huuskonen: */
+void channel_server_global_request(int type, int plen);
+
/* Sets specific protocol options. */
void channel_set_options(int hostname_in_open);
@@ -157,9 +167,12 @@
* channel to host:port from remote side. This never returns if there was an
* error.
*/
+/* Jarno: Added ssh2_remote_fwd flag. Used when protocol2 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
@@ -170,6 +183,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 -u -r openssh-2.2.0p1/clientloop.c openssh-2.2.0p1-jh/clientloop.c
--- openssh-2.2.0p1/clientloop.c Wed Aug 23 03:46:24 2000
+++ openssh-2.2.0p1-jh/clientloop.c Tue Sep 19 18:41:43 2000
@@ -993,6 +993,12 @@
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) {
int sock;
char *originator;
@@ -1035,7 +1041,8 @@
packet_start(SSH2_MSG_CHANNEL_OPEN_FAILURE);
packet_put_int(rchan);
packet_put_int(SSH2_OPEN_ADMINISTRATIVELY_PROHIBITED);
- packet_put_cstring("bla bla");
+ packet_put_cstring("bla bla"); /* TODO: Perhaps a little better
+ explanation */
packet_put_cstring("");
packet_send();
}
diff -u -r openssh-2.2.0p1/serverloop.c openssh-2.2.0p1-jh/serverloop.c
--- openssh-2.2.0p1/serverloop.c Tue Jul 11 10:31:38 2000
+++ openssh-2.2.0p1-jh/serverloop.c Tue Sep 19 18:41:43 2000
@@ -723,10 +723,13 @@
/* XXX check permission */
if (no_port_forwarding_flag) {
+ packet_send_debug("Server configuration rejects port forwardings.");
+ debug("Port forwarding disabled in server configuration.");
xfree(target);
xfree(originator);
return -1;
}
+
sock = channel_connect_to(target, target_port);
xfree(target);
xfree(originator);
@@ -819,6 +822,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 -u -r openssh-2.2.0p1/ssh.c openssh-2.2.0p1-jh/ssh.c
--- openssh-2.2.0p1/ssh.c Tue Aug 29 03:33:51 2000
+++ openssh-2.2.0p1-jh/ssh.c Tue Sep 19 18:41:43 2000
@@ -834,7 +834,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. */
@@ -890,7 +890,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);
}
}
@@ -972,7 +990,8 @@
/* should be pre-session */
init_local_fwd();
-
+ init_remote_fwd();
+
/* If requested, let ssh continue in the background. */
if (fork_after_authentication_flag)
if (daemon(1, 1) < 0)
More information about the openssh-unix-dev
mailing list