]> git.ipfire.org Git - thirdparty/openssh-portable.git/commitdiff
- djm@cvs.openbsd.org 2011/09/09 22:46:44
authorDamien Miller <djm@mindrot.org>
Thu, 22 Sep 2011 11:38:52 +0000 (21:38 +1000)
committerDamien Miller <djm@mindrot.org>
Thu, 22 Sep 2011 11:38:52 +0000 (21:38 +1000)
     [channels.c channels.h clientloop.h mux.c ssh.c]
     support for cancelling local and remote port forwards via the multiplex
     socket. Use ssh -O cancel -L xx:xx:xx -R yy:yy:yy user@host" to request
     the cancellation of the specified forwardings; ok markus@

ChangeLog
channels.c
channels.h
clientloop.h
mux.c
ssh.c

index dfce828228e4a94b196e6ec5c897515c37796b43..b31ee81cd335a25055f95054bae34423134816d1 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
      [sshd.c]
      kill the preauth privsep child on fatal errors in the monitor;
      ok markus@
+   - djm@cvs.openbsd.org 2011/09/09 22:46:44
+     [channels.c channels.h clientloop.h mux.c ssh.c]
+     support for cancelling local and remote port forwards via the multiplex
+     socket. Use ssh -O cancel -L xx:xx:xx -R yy:yy:yy user@host" to request
+     the cancellation of the specified forwardings; ok markus@
 
 20110909
  - (dtucker) [entropy.h] Bug #1932: remove old definition of init_rng.  From
index 24d4a9f4256f2b53708658ff4bc334b1cf9d78dd..0f7e1a872aa812d0a2e00c62aa6c5d61b6eef17a 100644 (file)
@@ -1,4 +1,4 @@
-/* $OpenBSD: channels.c,v 1.311 2011/06/22 22:08:42 djm Exp $ */
+/* $OpenBSD: channels.c,v 1.312 2011/09/09 22:46:44 djm Exp $ */
 /*
  * Author: Tatu Ylonen <ylo@cs.hut.fi>
  * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
@@ -302,6 +302,8 @@ channel_new(char *ctype, int type, int rfd, int wfd, int efd,
        buffer_init(&c->output);
        buffer_init(&c->extended);
        c->path = NULL;
+       c->listening_addr = NULL;
+       c->listening_port = 0;
        c->ostate = CHAN_OUTPUT_OPEN;
        c->istate = CHAN_INPUT_OPEN;
        c->flags = 0;
@@ -411,6 +413,10 @@ channel_free(Channel *c)
                xfree(c->path);
                c->path = NULL;
        }
+       if (c->listening_addr) {
+               xfree(c->listening_addr);
+               c->listening_addr = NULL;
+       }
        while ((cc = TAILQ_FIRST(&c->status_confirms)) != NULL) {
                if (cc->abandon_cb != NULL)
                        cc->abandon_cb(c, cc->ctx);
@@ -2634,6 +2640,46 @@ channel_set_af(int af)
        IPv4or6 = af;
 }
 
+
+/*
+ * Determine whether or not a port forward listens to loopback, the
+ * specified address or wildcard. On the client, a specified bind
+ * address will always override gateway_ports. On the server, a
+ * gateway_ports of 1 (``yes'') will override the client's specification
+ * and force a wildcard bind, whereas a value of 2 (``clientspecified'')
+ * will bind to whatever address the client asked for.
+ *
+ * Special-case listen_addrs are:
+ *
+ * "0.0.0.0"               -> wildcard v4/v6 if SSH_OLD_FORWARD_ADDR
+ * "" (empty string), "*"  -> wildcard v4/v6
+ * "localhost"             -> loopback v4/v6
+ */
+static const char *
+channel_fwd_bind_addr(const char *listen_addr, int *wildcardp,
+    int is_client, int gateway_ports)
+{
+       const char *addr = NULL;
+       int wildcard = 0;
+
+       if (listen_addr == NULL) {
+               /* No address specified: default to gateway_ports setting */
+               if (gateway_ports)
+                       wildcard = 1;
+       } else if (gateway_ports || is_client) {
+               if (((datafellows & SSH_OLD_FORWARD_ADDR) &&
+                   strcmp(listen_addr, "0.0.0.0") == 0 && is_client == 0) ||
+                   *listen_addr == '\0' || strcmp(listen_addr, "*") == 0 ||
+                   (!is_client && gateway_ports == 1))
+                       wildcard = 1;
+               else if (strcmp(listen_addr, "localhost") != 0)
+                       addr = listen_addr;
+       }
+       if (wildcardp != NULL)
+               *wildcardp = wildcard;
+       return addr;
+}
+
 static int
 channel_setup_fwd_listener(int type, const char *listen_addr,
     u_short listen_port, int *allocated_listen_port,
@@ -2659,36 +2705,9 @@ channel_setup_fwd_listener(int type, const char *listen_addr,
                return 0;
        }
 
-       /*
-        * Determine whether or not a port forward listens to loopback,
-        * specified address or wildcard. On the client, a specified bind
-        * address will always override gateway_ports. On the server, a
-        * gateway_ports of 1 (``yes'') will override the client's
-        * specification and force a wildcard bind, whereas a value of 2
-        * (``clientspecified'') will bind to whatever address the client
-        * asked for.
-        *
-        * Special-case listen_addrs are:
-        *
-        * "0.0.0.0"               -> wildcard v4/v6 if SSH_OLD_FORWARD_ADDR
-        * "" (empty string), "*"  -> wildcard v4/v6
-        * "localhost"             -> loopback v4/v6
-        */
-       addr = NULL;
-       if (listen_addr == NULL) {
-               /* No address specified: default to gateway_ports setting */
-               if (gateway_ports)
-                       wildcard = 1;
-       } else if (gateway_ports || is_client) {
-               if (((datafellows & SSH_OLD_FORWARD_ADDR) &&
-                   strcmp(listen_addr, "0.0.0.0") == 0 && is_client == 0) ||
-                   *listen_addr == '\0' || strcmp(listen_addr, "*") == 0 ||
-                   (!is_client && gateway_ports == 1))
-                       wildcard = 1;
-               else if (strcmp(listen_addr, "localhost") != 0)
-                       addr = listen_addr;
-       }
-
+       /* Determine the bind address, cf. channel_fwd_bind_addr() comment */
+       addr = channel_fwd_bind_addr(listen_addr, &wildcard,
+           is_client, gateway_ports);
        debug3("channel_setup_fwd_listener: type %d wildcard %d addr %s",
            type, wildcard, (addr == NULL) ? "NULL" : addr);
 
@@ -2793,6 +2812,7 @@ channel_setup_fwd_listener(int type, const char *listen_addr,
                c->path = xstrdup(host);
                c->host_port = port_to_connect;
                c->listening_port = listen_port;
+               c->listening_addr = addr == NULL ? NULL : xstrdup(addr);
                success = 1;
        }
        if (success == 0)
@@ -2810,9 +2830,36 @@ channel_cancel_rport_listener(const char *host, u_short port)
 
        for (i = 0; i < channels_alloc; i++) {
                Channel *c = channels[i];
+               if (c == NULL || c->type != SSH_CHANNEL_RPORT_LISTENER)
+                       continue;
+               if (strcmp(c->path, host) == 0 && c->listening_port == port) {
+                       debug2("%s: close channel %d", __func__, i);
+                       channel_free(c);
+                       found = 1;
+               }
+       }
+
+       return (found);
+}
+
+int
+channel_cancel_lport_listener(const char *lhost, u_short lport,
+    u_short cport, int gateway_ports)
+{
+       u_int i;
+       int found = 0;
+       const char *addr = channel_fwd_bind_addr(lhost, NULL, 1, gateway_ports);
 
-               if (c != NULL && c->type == SSH_CHANNEL_RPORT_LISTENER &&
-                   strcmp(c->path, host) == 0 && c->listening_port == port) {
+       for (i = 0; i < channels_alloc; i++) {
+               Channel *c = channels[i];
+               if (c == NULL || c->type != SSH_CHANNEL_PORT_LISTENER)
+                       continue;
+               if (c->listening_port != lport || c->host_port != cport)
+                       continue;
+               if ((c->listening_addr == NULL && addr != NULL) ||
+                   (c->listening_addr != NULL && addr == NULL))
+                       continue;
+               if (addr == NULL || strcmp(c->listening_addr, addr) == 0) {
                        debug2("%s: close channel %d", __func__, i);
                        channel_free(c);
                        found = 1;
@@ -2842,11 +2889,31 @@ channel_setup_remote_fwd_listener(const char *listen_address,
            NULL, 0, gateway_ports);
 }
 
+/*
+ * Translate the requested rfwd listen host to something usable for
+ * this server.
+ */
+static const char *
+channel_rfwd_bind_host(const char *listen_host)
+{
+       if (listen_host == NULL) {
+               if (datafellows & SSH_BUG_RFWD_ADDR)
+                       return "127.0.0.1";
+               else
+                       return "localhost";
+       } else if (*listen_host == '\0' || strcmp(listen_host, "*") == 0) {
+               if (datafellows & SSH_BUG_RFWD_ADDR)
+                       return "0.0.0.0";
+               else
+                       return "";
+       } else
+               return listen_host;
+}
+
 /*
  * Initiate forwarding of connections to port "port" on remote host through
  * the secure channel to host:port from local side.
  */
-
 int
 channel_request_remote_forwarding(const char *listen_host, u_short listen_port,
     const char *host_to_connect, u_short port_to_connect)
@@ -2855,25 +2922,10 @@ channel_request_remote_forwarding(const char *listen_host, u_short listen_port,
 
        /* Send the forward request to the remote side. */
        if (compat20) {
-               const char *address_to_bind;
-               if (listen_host == NULL) {
-                       if (datafellows & SSH_BUG_RFWD_ADDR)
-                               address_to_bind = "127.0.0.1";
-                       else
-                               address_to_bind = "localhost";
-               } else if (*listen_host == '\0' ||
-                          strcmp(listen_host, "*") == 0) {
-                       if (datafellows & SSH_BUG_RFWD_ADDR)
-                               address_to_bind = "0.0.0.0";
-                       else
-                               address_to_bind = "";
-               } else
-                       address_to_bind = listen_host;
-
                packet_start(SSH2_MSG_GLOBAL_REQUEST);
                packet_put_cstring("tcpip-forward");
-               packet_put_char(1);                     /* boolean: want reply */
-               packet_put_cstring(address_to_bind);
+               packet_put_char(1);             /* boolean: want reply */
+               packet_put_cstring(channel_rfwd_bind_host(listen_host));
                packet_put_int(listen_port);
                packet_send();
                packet_write_wait();
@@ -2917,13 +2969,13 @@ channel_request_remote_forwarding(const char *listen_host, u_short listen_port,
  * Request cancellation of remote forwarding of connection host:port from
  * local side.
  */
-void
+int
 channel_request_rforward_cancel(const char *host, u_short port)
 {
        int i;
 
        if (!compat20)
-               return;
+               return -1;
 
        for (i = 0; i < num_permitted_opens; i++) {
                if (permitted_opens[i].host_to_connect != NULL &&
@@ -2932,12 +2984,12 @@ channel_request_rforward_cancel(const char *host, u_short port)
        }
        if (i >= num_permitted_opens) {
                debug("%s: requested forward not found", __func__);
-               return;
+               return -1;
        }
        packet_start(SSH2_MSG_GLOBAL_REQUEST);
        packet_put_cstring("cancel-tcpip-forward");
        packet_put_char(0);
-       packet_put_cstring(host == NULL ? "" : host);
+       packet_put_cstring(channel_rfwd_bind_host(host));
        packet_put_int(port);
        packet_send();
 
@@ -2945,6 +2997,8 @@ channel_request_rforward_cancel(const char *host, u_short port)
        permitted_opens[i].port_to_connect = 0;
        xfree(permitted_opens[i].host_to_connect);
        permitted_opens[i].host_to_connect = NULL;
+
+       return 0;
 }
 
 /*
index e2941c85ad60c57302b422c8022ac740e5147c69..37af3228915ca896b807271ae606096134da1ec5 100644 (file)
@@ -1,4 +1,4 @@
-/* $OpenBSD: channels.h,v 1.105 2011/06/22 22:08:42 djm Exp $ */
+/* $OpenBSD: channels.h,v 1.106 2011/09/09 22:46:44 djm Exp $ */
 
 /*
  * Author: Tatu Ylonen <ylo@cs.hut.fi>
@@ -116,6 +116,7 @@ struct Channel {
        char    *path;
                /* path for unix domain sockets, or host name for forwards */
        int     listening_port; /* port being listened for forwards */
+       char   *listening_addr; /* addr being listened for forwards */
        int     host_port;      /* remote port to connect for forwards */
        char   *remote_name;    /* remote hostname */
 
@@ -261,9 +262,10 @@ int         channel_request_remote_forwarding(const char *, u_short,
             const char *, u_short);
 int     channel_setup_local_fwd_listener(const char *, u_short,
             const char *, u_short, int);
-void    channel_request_rforward_cancel(const char *host, u_short port);
+int     channel_request_rforward_cancel(const char *host, u_short port);
 int     channel_setup_remote_fwd_listener(const char *, u_short, int *, int);
 int     channel_cancel_rport_listener(const char *, u_short);
+int     channel_cancel_lport_listener(const char *, u_short, u_short, int);
 
 /* x11 forwarding */
 
index a259b5e14b2187cd6d07b0106e4c497198479049..3bb794879c5b747dc8180be50712a4dcf5aa80ed 100644 (file)
@@ -1,4 +1,4 @@
-/* $OpenBSD: clientloop.h,v 1.28 2011/06/22 22:08:42 djm Exp $ */
+/* $OpenBSD: clientloop.h,v 1.29 2011/09/09 22:46:44 djm Exp $ */
 
 /*
  * Author: Tatu Ylonen <ylo@cs.hut.fi>
@@ -70,6 +70,7 @@ void client_expect_confirm(int, const char *, enum confirm_action);
 #define SSHMUX_COMMAND_STDIO_FWD       4       /* Open stdio fwd (ssh -W) */
 #define SSHMUX_COMMAND_FORWARD         5       /* Forward only, no command */
 #define SSHMUX_COMMAND_STOP            6       /* Disable mux but not conn */
+#define SSHMUX_COMMAND_CANCEL_FWD      7       /* Cancel forwarding(s) */
 
 void   muxserver_listen(void);
 void   muxclient(const char *);
diff --git a/mux.c b/mux.c
index add0e26b1b91834931d7b66ef24e38408d169716..6b63d813bfc50f188d6478aa91a56f8d22285ba8 100644 (file)
--- a/mux.c
+++ b/mux.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: mux.c,v 1.29 2011/06/22 22:08:42 djm Exp $ */
+/* $OpenBSD: mux.c,v 1.30 2011/09/09 22:46:44 djm Exp $ */
 /*
  * Copyright (c) 2002-2008 Damien Miller <djm@openbsd.org>
  *
@@ -777,10 +777,11 @@ process_mux_open_fwd(u_int rid, Channel *c, Buffer *m, Buffer *r)
 static int
 process_mux_close_fwd(u_int rid, Channel *c, Buffer *m, Buffer *r)
 {
-       Forward fwd;
+       Forward fwd, *found_fwd;
        char *fwd_desc = NULL;
+       const char *error_reason = NULL;
        u_int ftype;
-       int ret = 0;
+       int i, ret = 0;
 
        fwd.listen_host = fwd.connect_host = NULL;
        if (buffer_get_int_ret(&ftype, m) != 0 ||
@@ -802,14 +803,66 @@ process_mux_close_fwd(u_int rid, Channel *c, Buffer *m, Buffer *r)
                fwd.connect_host = NULL;
        }
 
-       debug2("%s: channel %d: request %s", __func__, c->self,
+       debug2("%s: channel %d: request cancel %s", __func__, c->self,
            (fwd_desc = format_forward(ftype, &fwd)));
 
-       /* XXX implement this */
-       buffer_put_int(r, MUX_S_FAILURE);
-       buffer_put_int(r, rid);
-       buffer_put_cstring(r, "unimplemented");
+       /* make sure this has been requested */
+       found_fwd = NULL;
+       switch (ftype) {
+       case MUX_FWD_LOCAL:
+       case MUX_FWD_DYNAMIC:
+               for (i = 0; i < options.num_local_forwards; i++) {
+                       if (compare_forward(&fwd,
+                           options.local_forwards + i)) {
+                               found_fwd = options.local_forwards + i;
+                               break;
+                       }
+               }
+               break;
+       case MUX_FWD_REMOTE:
+               for (i = 0; i < options.num_remote_forwards; i++) {
+                       if (compare_forward(&fwd,
+                           options.remote_forwards + i)) {
+                               found_fwd = options.remote_forwards + i;
+                               break;
+                       }
+               }
+               break;
+       }
 
+       if (found_fwd == NULL)
+               error_reason = "port not forwarded";
+       else if (ftype == MUX_FWD_REMOTE) {
+               /*
+                * This shouldn't fail unless we confused the host/port
+                * between options.remote_forwards and permitted_opens.
+                */
+               if (channel_request_rforward_cancel(fwd.listen_host,
+                   fwd.listen_port) == -1)
+                       error_reason = "port not in permitted opens";
+       } else {        /* local and dynamic forwards */
+               /* Ditto */
+               if (channel_cancel_lport_listener(fwd.listen_host,
+                   fwd.listen_port, fwd.connect_port,
+                   options.gateway_ports) == -1)
+                       error_reason = "port not found";
+       }
+
+       if (error_reason == NULL) {
+               buffer_put_int(r, MUX_S_OK);
+               buffer_put_int(r, rid);
+
+               if (found_fwd->listen_host != NULL)
+                       xfree(found_fwd->listen_host);
+               if (found_fwd->connect_host != NULL)
+                       xfree(found_fwd->connect_host);
+               found_fwd->listen_host = found_fwd->connect_host = NULL;
+               found_fwd->listen_port = found_fwd->connect_port = 0;
+       } else {
+               buffer_put_int(r, MUX_S_FAILURE);
+               buffer_put_int(r, rid);
+               buffer_put_cstring(r, error_reason);
+       }
  out:
        if (fwd_desc != NULL)
                xfree(fwd_desc);
@@ -1537,18 +1590,19 @@ mux_client_request_terminate(int fd)
 }
 
 static int
-mux_client_request_forward(int fd, u_int ftype, Forward *fwd)
+mux_client_forward(int fd, int cancel_flag, u_int ftype, Forward *fwd)
 {
        Buffer m;
        char *e, *fwd_desc;
        u_int type, rid;
 
        fwd_desc = format_forward(ftype, fwd);
-       debug("Requesting %s", fwd_desc);
+       debug("Requesting %s %s",
+           cancel_flag ? "cancellation of" : "forwarding of", fwd_desc);
        xfree(fwd_desc);
 
        buffer_init(&m);
-       buffer_put_int(&m, MUX_C_OPEN_FWD);
+       buffer_put_int(&m, cancel_flag ? MUX_C_CLOSE_FWD : MUX_C_OPEN_FWD);
        buffer_put_int(&m, muxclient_request_id);
        buffer_put_int(&m, ftype);
        buffer_put_cstring(&m,
@@ -1577,6 +1631,8 @@ mux_client_request_forward(int fd, u_int ftype, Forward *fwd)
        case MUX_S_OK:
                break;
        case MUX_S_REMOTE_PORT:
+               if (cancel_flag)
+                       fatal("%s: got MUX_S_REMOTE_PORT for cancel", __func__);
                fwd->allocated_port = buffer_get_int(&m);
                logit("Allocated port %u for remote forward to %s:%d",
                    fwd->allocated_port,
@@ -1606,27 +1662,28 @@ mux_client_request_forward(int fd, u_int ftype, Forward *fwd)
 }
 
 static int
-mux_client_request_forwards(int fd)
+mux_client_forwards(int fd, int cancel_flag)
 {
-       int i;
+       int i, ret = 0;
 
-       debug3("%s: requesting forwardings: %d local, %d remote", __func__,
+       debug3("%s: %s forwardings: %d local, %d remote", __func__,
+           cancel_flag ? "cancel" : "request",
            options.num_local_forwards, options.num_remote_forwards);
 
        /* XXX ExitOnForwardingFailure */
        for (i = 0; i < options.num_local_forwards; i++) {
-               if (mux_client_request_forward(fd,
+               if (mux_client_forward(fd, cancel_flag,
                    options.local_forwards[i].connect_port == 0 ?
                    MUX_FWD_DYNAMIC : MUX_FWD_LOCAL,
                    options.local_forwards + i) != 0)
-                       return -1;
+                       ret = -1;
        }
        for (i = 0; i < options.num_remote_forwards; i++) {
-               if (mux_client_request_forward(fd, MUX_FWD_REMOTE,
+               if (mux_client_forward(fd, cancel_flag, MUX_FWD_REMOTE,
                    options.remote_forwards + i) != 0)
-                       return -1;
+                       ret = -1;
        }
-       return 0;
+       return ret;
 }
 
 static int
@@ -2014,11 +2071,11 @@ muxclient(const char *path)
                fprintf(stderr, "Exit request sent.\r\n");
                exit(0);
        case SSHMUX_COMMAND_FORWARD:
-               if (mux_client_request_forwards(sock) != 0)
+               if (mux_client_forwards(sock, 0) != 0)
                        fatal("%s: master forward request failed", __func__);
                exit(0);
        case SSHMUX_COMMAND_OPEN:
-               if (mux_client_request_forwards(sock) != 0) {
+               if (mux_client_forwards(sock, 0) != 0) {
                        error("%s: master forward request failed", __func__);
                        return;
                }
@@ -2031,6 +2088,11 @@ muxclient(const char *path)
                mux_client_request_stop_listening(sock);
                fprintf(stderr, "Stop listening request sent.\r\n");
                exit(0);
+       case SSHMUX_COMMAND_CANCEL_FWD:
+               if (mux_client_forwards(sock, 1) != 0)
+                       error("%s: master cancel forward request failed",
+                           __func__);
+               exit(0);
        default:
                fatal("unrecognised muxclient_command %d", muxclient_command);
        }
diff --git a/ssh.c b/ssh.c
index c717dcf1d4636f91228ec8613abd7079a20792e4..f437dec1c6be664b9498e095ef3de9a1a6df9dd3 100644 (file)
--- a/ssh.c
+++ b/ssh.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: ssh.c,v 1.364 2011/08/02 23:15:03 djm Exp $ */
+/* $OpenBSD: ssh.c,v 1.365 2011/09/09 22:46:44 djm Exp $ */
 /*
  * Author: Tatu Ylonen <ylo@cs.hut.fi>
  * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
@@ -377,6 +377,8 @@ main(int ac, char **av)
                                muxclient_command = SSHMUX_COMMAND_TERMINATE;
                        else if (strcmp(optarg, "stop") == 0)
                                muxclient_command = SSHMUX_COMMAND_STOP;
+                       else if (strcmp(optarg, "cancel") == 0)
+                               muxclient_command = SSHMUX_COMMAND_CANCEL_FWD;
                        else
                                fatal("Invalid multiplex command.");
                        break;