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)