diff --git a/ChangeLog b/ChangeLog index a485000a8..fffbd727e 100644 --- a/ChangeLog +++ b/ChangeLog @@ -23,6 +23,12 @@ the server. motivated by and with feedback from markus@ + - markus@cvs.openbsd.org 2010/05/16 12:55:51 + [PROTOCOL.mux clientloop.h mux.c readconf.c readconf.h ssh.1 ssh.c] + mux support for remote forwarding with dynamic port allocation, + use with + LPORT=`ssh -S muxsocket -R0:localhost:25 -O forward somehost` + feedback and ok djm@ 20100511 - (dtucker) [Makefile.in] Bug #1770: Link libopenbsd-compat twice to solve diff --git a/PROTOCOL.mux b/PROTOCOL.mux index d22f7379c..1d8c463a7 100644 --- a/PROTOCOL.mux +++ b/PROTOCOL.mux @@ -109,8 +109,14 @@ A client may request the master to establish a port forward: forwarding type may be MUX_FWD_LOCAL, MUX_FWD_REMOTE, MUX_FWD_DYNAMIC. -A server may reply with a MUX_S_OK, a MUX_S_PERMISSION_DENIED or a -MUX_S_FAILURE. +A server may reply with a MUX_S_OK, a MUX_S_REMOTE_PORT, a +MUX_S_PERMISSION_DENIED or a MUX_S_FAILURE. + +For dynamically allocated listen port the server replies with + + uint32 MUX_S_REMOTE_PORT + uint32 client request id + uint32 allocated remote listen port 5. Requesting closure of port forwards @@ -178,6 +184,7 @@ The MUX_S_PERMISSION_DENIED and MUX_S_FAILURE include a reason: #define MUX_S_EXIT_MESSAGE 0x80000004 #define MUX_S_ALIVE 0x80000005 #define MUX_S_SESSION_OPENED 0x80000006 +#define MUX_S_REMOTE_PORT 0x80000007 #define MUX_FWD_LOCAL 1 #define MUX_FWD_REMOTE 2 @@ -193,4 +200,4 @@ XXX server->client error/warning notifications XXX port0 rfwd (need custom response message) XXX send signals via mux -$OpenBSD: PROTOCOL.mux,v 1.1 2010/01/26 01:28:35 djm Exp $ +$OpenBSD: PROTOCOL.mux,v 1.2 2010/05/16 12:55:51 markus Exp $ diff --git a/clientloop.h b/clientloop.h index 0b8257b99..a5bc246a3 100644 --- a/clientloop.h +++ b/clientloop.h @@ -1,4 +1,4 @@ -/* $OpenBSD: clientloop.h,v 1.23 2010/01/26 01:28:35 djm Exp $ */ +/* $OpenBSD: clientloop.h,v 1.24 2010/05/16 12:55:51 markus Exp $ */ /* * Author: Tatu Ylonen @@ -63,6 +63,7 @@ void client_register_global_confirm(global_confirm_cb *, void *); #define SSHMUX_COMMAND_ALIVE_CHECK 2 /* Check master is alive */ #define SSHMUX_COMMAND_TERMINATE 3 /* Ask master to exit */ #define SSHMUX_COMMAND_STDIO_FWD 4 /* Open stdio fwd (ssh -W) */ +#define SSHMUX_COMMAND_FORWARD 5 /* Forward only, no command */ void muxserver_listen(void); void muxclient(const char *); diff --git a/mux.c b/mux.c index 18dfd99f3..3f5babccc 100644 --- a/mux.c +++ b/mux.c @@ -1,4 +1,4 @@ -/* $OpenBSD: mux.c,v 1.17 2010/05/14 23:29:23 djm Exp $ */ +/* $OpenBSD: mux.c,v 1.18 2010/05/16 12:55:51 markus Exp $ */ /* * Copyright (c) 2002-2008 Damien Miller * @@ -71,6 +71,7 @@ #include "xmalloc.h" #include "log.h" #include "ssh.h" +#include "ssh2.h" #include "pathnames.h" #include "misc.h" #include "match.h" @@ -109,6 +110,13 @@ struct mux_session_confirm_ctx { u_int rid; }; +/* Context for global channel callback */ +struct mux_channel_confirm_ctx { + u_int cid; /* channel id */ + u_int rid; /* request id */ + int fid; /* forward id */ +}; + /* fd to control socket */ int muxserver_sock = -1; @@ -144,6 +152,7 @@ struct mux_master_state { #define MUX_S_EXIT_MESSAGE 0x80000004 #define MUX_S_ALIVE 0x80000005 #define MUX_S_SESSION_OPENED 0x80000006 +#define MUX_S_REMOTE_PORT 0x80000007 /* type codes for MUX_C_OPEN_FWD and MUX_C_CLOSE_FWD */ #define MUX_FWD_LOCAL 1 @@ -557,6 +566,61 @@ compare_forward(Forward *a, Forward *b) return 1; } +static void +mux_confirm_remote_forward(int type, u_int32_t seq, void *ctxt) +{ + struct mux_channel_confirm_ctx *fctx = ctxt; + char *failmsg = NULL; + Forward *rfwd; + Channel *c; + Buffer out; + + if ((c = channel_by_id(fctx->cid)) == NULL) { + /* no channel for reply */ + error("%s: unknown channel", __func__); + return; + } + buffer_init(&out); + if (fctx->fid >= options.num_remote_forwards) { + xasprintf(&failmsg, "unknown forwarding id %d", fctx->fid); + goto fail; + } + rfwd = &options.remote_forwards[fctx->fid]; + debug("%s: %s for: listen %d, connect %s:%d", __func__, + type == SSH2_MSG_REQUEST_SUCCESS ? "success" : "failure", + rfwd->listen_port, rfwd->connect_host, rfwd->connect_port); + if (type == SSH2_MSG_REQUEST_SUCCESS) { + if (rfwd->listen_port == 0) { + rfwd->allocated_port = packet_get_int(); + logit("Allocated port %u for mux remote forward" + " to %s:%d", rfwd->allocated_port, + rfwd->connect_host, rfwd->connect_port); + buffer_put_int(&out, MUX_S_REMOTE_PORT); + buffer_put_int(&out, fctx->rid); + buffer_put_int(&out, rfwd->allocated_port); + } else { + buffer_put_int(&out, MUX_S_OK); + buffer_put_int(&out, fctx->rid); + } + goto out; + } else { + xasprintf(&failmsg, "remote port forwarding failed for " + "listen port %d", rfwd->listen_port); + } + fail: + error("%s: %s", __func__, failmsg); + buffer_put_int(&out, MUX_S_FAILURE); + buffer_put_int(&out, fctx->rid); + buffer_put_cstring(&out, failmsg); + xfree(failmsg); + out: + buffer_put_string(&c->output, buffer_ptr(&out), buffer_len(&out)); + buffer_free(&out); + if (c->mux_pause <= 0) + fatal("%s: mux_pause %d", __func__, c->mux_pause); + c->mux_pause = 0; /* start processing messages again */ +} + static int process_mux_open_fwd(u_int rid, Channel *c, Buffer *m, Buffer *r) { @@ -592,15 +656,16 @@ process_mux_open_fwd(u_int rid, Channel *c, Buffer *m, Buffer *r) ftype != MUX_FWD_DYNAMIC) { logit("%s: invalid forwarding type %u", __func__, ftype); invalid: - xfree(fwd.listen_host); - xfree(fwd.connect_host); + if (fwd.listen_host) + xfree(fwd.listen_host); + if (fwd.connect_host) + xfree(fwd.connect_host); buffer_put_int(r, MUX_S_FAILURE); buffer_put_int(r, rid); buffer_put_cstring(r, "Invalid forwarding request"); return 0; } - /* XXX support rport0 forwarding with reply of port assigned */ - if (fwd.listen_port == 0 || fwd.listen_port >= 65536) { + if (fwd.listen_port >= 65536) { logit("%s: invalid listen port %u", __func__, fwd.listen_port); goto invalid; @@ -635,8 +700,17 @@ process_mux_open_fwd(u_int rid, Channel *c, Buffer *m, Buffer *r) case MUX_FWD_REMOTE: for (i = 0; i < options.num_remote_forwards; i++) { if (compare_forward(&fwd, - options.remote_forwards + i)) - goto exists; + options.remote_forwards + i)) { + if (fwd.listen_port != 0) + goto exists; + debug2("%s: found allocated port", + __func__); + buffer_put_int(r, MUX_S_REMOTE_PORT); + buffer_put_int(r, rid); + buffer_put_int(r, + options.remote_forwards[i].allocated_port); + goto out; + } } break; } @@ -668,14 +742,24 @@ process_mux_open_fwd(u_int rid, Channel *c, Buffer *m, Buffer *r) add_local_forward(&options, &fwd); freefwd = 0; } else { - /* XXX wait for remote to confirm */ + struct mux_channel_confirm_ctx *fctx; + if (options.num_remote_forwards + 1 >= SSH_MAX_FORWARDS_PER_DIRECTION || channel_request_remote_forwarding(fwd.listen_host, fwd.listen_port, fwd.connect_host, fwd.connect_port) < 0) goto fail; add_remote_forward(&options, &fwd); + fctx = xcalloc(1, sizeof(*fctx)); + fctx->cid = c->self; + fctx->rid = rid; + fctx->fid = options.num_remote_forwards-1; + client_register_global_confirm(mux_confirm_remote_forward, + fctx); freefwd = 0; + c->mux_pause = 1; /* wait for mux_confirm_remote_forward */ + /* delayed reply in mux_confirm_remote_forward */ + goto out; } buffer_put_int(r, MUX_S_OK); buffer_put_int(r, rid); @@ -1392,6 +1476,15 @@ mux_client_request_forward(int fd, u_int ftype, Forward *fwd) switch (type) { case MUX_S_OK: break; + case MUX_S_REMOTE_PORT: + fwd->allocated_port = buffer_get_int(&m); + logit("Allocated port %u for remote forward to %s:%d", + fwd->allocated_port, + fwd->connect_host ? fwd->connect_host : "", + fwd->connect_port); + if (muxclient_command == SSHMUX_COMMAND_FORWARD) + fprintf(stdout, "%u\n", fwd->allocated_port); + break; case MUX_S_PERMISSION_DENIED: e = buffer_get_string(&m, NULL); buffer_free(&m); @@ -1758,6 +1851,10 @@ muxclient(const char *path) mux_client_request_terminate(sock); fprintf(stderr, "Exit request sent.\r\n"); exit(0); + case SSHMUX_COMMAND_FORWARD: + if (mux_client_request_forwards(sock) != 0) + fatal("%s: master forward request failed", __func__); + exit(0); case SSHMUX_COMMAND_OPEN: if (mux_client_request_forwards(sock) != 0) { error("%s: master forward request failed", __func__); diff --git a/readconf.c b/readconf.c index 8bdc8caf1..4bc98b77e 100644 --- a/readconf.c +++ b/readconf.c @@ -1,4 +1,4 @@ -/* $OpenBSD: readconf.c,v 1.183 2010/02/08 10:50:20 markus Exp $ */ +/* $OpenBSD: readconf.c,v 1.184 2010/05/16 12:55:51 markus Exp $ */ /* * Author: Tatu Ylonen * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland @@ -283,6 +283,7 @@ add_remote_forward(Options *options, const Forward *newfwd) fwd->listen_port = newfwd->listen_port; fwd->connect_host = newfwd->connect_host; fwd->connect_port = newfwd->connect_port; + fwd->allocated_port = 0; } static void diff --git a/readconf.h b/readconf.h index 4264751c5..4fb29e2fa 100644 --- a/readconf.h +++ b/readconf.h @@ -1,4 +1,4 @@ -/* $OpenBSD: readconf.h,v 1.82 2010/02/08 10:50:20 markus Exp $ */ +/* $OpenBSD: readconf.h,v 1.83 2010/05/16 12:55:51 markus Exp $ */ /* * Author: Tatu Ylonen @@ -23,6 +23,7 @@ typedef struct { int listen_port; /* Port to forward. */ char *connect_host; /* Host to connect. */ int connect_port; /* Port to connect on connect_host. */ + int allocated_port; /* Dynamically allocated listen port */ } Forward; /* Data structure for representing option data. */ diff --git a/ssh.1 b/ssh.1 index 34bddbcbc..2a0fd5ddd 100644 --- a/ssh.1 +++ b/ssh.1 @@ -34,8 +34,8 @@ .\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF .\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. .\" -.\" $OpenBSD: ssh.1,v 1.304 2010/03/26 06:54:36 jmc Exp $ -.Dd $Mdocdate: March 26 2010 $ +.\" $OpenBSD: ssh.1,v 1.305 2010/05/16 12:55:51 markus Exp $ +.Dd $Mdocdate: May 16 2010 $ .Dt SSH 1 .Os .Sh NAME @@ -421,7 +421,9 @@ option is specified, the argument is interpreted and passed to the master process. Valid commands are: .Dq check -(check that the master process is running) and +(check that the master process is running), +.Dq forward +(request forwardings without command execution) and .Dq exit (request the master to exit). .It Fl o Ar option @@ -557,6 +559,9 @@ argument is .Ql 0 , the listen port will be dynamically allocated on the server and reported to the client at run time. +When used together with +.Ic -O forward +the allocated port will be printed to the standard output. .It Fl S Ar ctl_path Specifies the location of a control socket for connection sharing, or the string diff --git a/ssh.c b/ssh.c index ee224e9ff..228afecf4 100644 --- a/ssh.c +++ b/ssh.c @@ -1,4 +1,4 @@ -/* $OpenBSD: ssh.c,v 1.337 2010/05/14 23:29:23 djm Exp $ */ +/* $OpenBSD: ssh.c,v 1.338 2010/05/16 12:55:51 markus Exp $ */ /* * Author: Tatu Ylonen * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland @@ -327,6 +327,8 @@ main(int ac, char **av) fatal("Multiplexing command already specified"); if (strcmp(optarg, "check") == 0) muxclient_command = SSHMUX_COMMAND_ALIVE_CHECK; + else if (strcmp(optarg, "forward") == 0) + muxclient_command = SSHMUX_COMMAND_FORWARD; else if (strcmp(optarg, "exit") == 0) muxclient_command = SSHMUX_COMMAND_TERMINATE; else @@ -877,9 +879,10 @@ ssh_confirm_remote_forward(int type, u_int32_t seq, void *ctxt) type == SSH2_MSG_REQUEST_SUCCESS ? "success" : "failure", rfwd->listen_port, rfwd->connect_host, rfwd->connect_port); if (type == SSH2_MSG_REQUEST_SUCCESS && rfwd->listen_port == 0) { + rfwd->allocated_port = packet_get_int(); logit("Allocated port %u for remote forward to %s:%d", - packet_get_int(), - rfwd->connect_host, rfwd->connect_port); + rfwd->allocated_port, + rfwd->connect_host, rfwd->connect_port); } if (type == SSH2_MSG_REQUEST_FAILURE) {