- 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@
diff --git a/ChangeLog b/ChangeLog
index dfce828..b31ee81 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -51,6 +51,11 @@
[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
diff --git a/channels.c b/channels.c
index 24d4a9f..0f7e1a8 100644
--- a/channels.c
+++ b/channels.c
@@ -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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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;
+ }
+ }
- if (c != NULL && c->type == SSH_CHANNEL_RPORT_LISTENER &&
- strcmp(c->path, host) == 0 && c->listening_port == port) {
+ 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);
+
+ 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;
@@ -2843,10 +2890,30 @@
}
/*
+ * 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 @@
/* 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 @@
* 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 @@
}
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 @@
permitted_opens[i].port_to_connect = 0;
xfree(permitted_opens[i].host_to_connect);
permitted_opens[i].host_to_connect = NULL;
+
+ return 0;
}
/*
diff --git a/channels.h b/channels.h
index e2941c8..37af322 100644
--- a/channels.h
+++ b/channels.h
@@ -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 @@
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 @@
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 */
diff --git a/clientloop.h b/clientloop.h
index a259b5e..3bb7948 100644
--- a/clientloop.h
+++ b/clientloop.h
@@ -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 @@
#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 add0e26..6b63d81 100644
--- 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 @@
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 @@
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 @@
}
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 @@
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 @@
}
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 @@
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 @@
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 c717dcf..f437dec 100644
--- 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 @@
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;