[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@
This commit is contained in:
Damien Miller 2010-05-21 14:57:35 +10:00
parent d530f5f471
commit 388f6fc485
8 changed files with 141 additions and 20 deletions

View File

@ -23,6 +23,12 @@
the server. the server.
motivated by and with feedback from markus@ 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 20100511
- (dtucker) [Makefile.in] Bug #1770: Link libopenbsd-compat twice to solve - (dtucker) [Makefile.in] Bug #1770: Link libopenbsd-compat twice to solve

View File

@ -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. 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 A server may reply with a MUX_S_OK, a MUX_S_REMOTE_PORT, a
MUX_S_FAILURE. 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 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_EXIT_MESSAGE 0x80000004
#define MUX_S_ALIVE 0x80000005 #define MUX_S_ALIVE 0x80000005
#define MUX_S_SESSION_OPENED 0x80000006 #define MUX_S_SESSION_OPENED 0x80000006
#define MUX_S_REMOTE_PORT 0x80000007
#define MUX_FWD_LOCAL 1 #define MUX_FWD_LOCAL 1
#define MUX_FWD_REMOTE 2 #define MUX_FWD_REMOTE 2
@ -193,4 +200,4 @@ XXX server->client error/warning notifications
XXX port0 rfwd (need custom response message) XXX port0 rfwd (need custom response message)
XXX send signals via mux 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 $

View File

@ -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 <ylo@cs.hut.fi> * Author: Tatu Ylonen <ylo@cs.hut.fi>
@ -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_ALIVE_CHECK 2 /* Check master is alive */
#define SSHMUX_COMMAND_TERMINATE 3 /* Ask master to exit */ #define SSHMUX_COMMAND_TERMINATE 3 /* Ask master to exit */
#define SSHMUX_COMMAND_STDIO_FWD 4 /* Open stdio fwd (ssh -W) */ #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 muxserver_listen(void);
void muxclient(const char *); void muxclient(const char *);

113
mux.c
View File

@ -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 <djm@openbsd.org> * Copyright (c) 2002-2008 Damien Miller <djm@openbsd.org>
* *
@ -71,6 +71,7 @@
#include "xmalloc.h" #include "xmalloc.h"
#include "log.h" #include "log.h"
#include "ssh.h" #include "ssh.h"
#include "ssh2.h"
#include "pathnames.h" #include "pathnames.h"
#include "misc.h" #include "misc.h"
#include "match.h" #include "match.h"
@ -109,6 +110,13 @@ struct mux_session_confirm_ctx {
u_int rid; 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 */ /* fd to control socket */
int muxserver_sock = -1; int muxserver_sock = -1;
@ -144,6 +152,7 @@ struct mux_master_state {
#define MUX_S_EXIT_MESSAGE 0x80000004 #define MUX_S_EXIT_MESSAGE 0x80000004
#define MUX_S_ALIVE 0x80000005 #define MUX_S_ALIVE 0x80000005
#define MUX_S_SESSION_OPENED 0x80000006 #define MUX_S_SESSION_OPENED 0x80000006
#define MUX_S_REMOTE_PORT 0x80000007
/* type codes for MUX_C_OPEN_FWD and MUX_C_CLOSE_FWD */ /* type codes for MUX_C_OPEN_FWD and MUX_C_CLOSE_FWD */
#define MUX_FWD_LOCAL 1 #define MUX_FWD_LOCAL 1
@ -557,6 +566,61 @@ compare_forward(Forward *a, Forward *b)
return 1; 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 static int
process_mux_open_fwd(u_int rid, Channel *c, Buffer *m, Buffer *r) 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) { ftype != MUX_FWD_DYNAMIC) {
logit("%s: invalid forwarding type %u", __func__, ftype); logit("%s: invalid forwarding type %u", __func__, ftype);
invalid: invalid:
xfree(fwd.listen_host); if (fwd.listen_host)
xfree(fwd.connect_host); xfree(fwd.listen_host);
if (fwd.connect_host)
xfree(fwd.connect_host);
buffer_put_int(r, MUX_S_FAILURE); buffer_put_int(r, MUX_S_FAILURE);
buffer_put_int(r, rid); buffer_put_int(r, rid);
buffer_put_cstring(r, "Invalid forwarding request"); buffer_put_cstring(r, "Invalid forwarding request");
return 0; return 0;
} }
/* XXX support rport0 forwarding with reply of port assigned */ if (fwd.listen_port >= 65536) {
if (fwd.listen_port == 0 || fwd.listen_port >= 65536) {
logit("%s: invalid listen port %u", __func__, logit("%s: invalid listen port %u", __func__,
fwd.listen_port); fwd.listen_port);
goto invalid; goto invalid;
@ -635,8 +700,17 @@ process_mux_open_fwd(u_int rid, Channel *c, Buffer *m, Buffer *r)
case MUX_FWD_REMOTE: case MUX_FWD_REMOTE:
for (i = 0; i < options.num_remote_forwards; i++) { for (i = 0; i < options.num_remote_forwards; i++) {
if (compare_forward(&fwd, if (compare_forward(&fwd,
options.remote_forwards + i)) options.remote_forwards + i)) {
goto exists; 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; break;
} }
@ -668,14 +742,24 @@ process_mux_open_fwd(u_int rid, Channel *c, Buffer *m, Buffer *r)
add_local_forward(&options, &fwd); add_local_forward(&options, &fwd);
freefwd = 0; freefwd = 0;
} else { } else {
/* XXX wait for remote to confirm */ struct mux_channel_confirm_ctx *fctx;
if (options.num_remote_forwards + 1 >= if (options.num_remote_forwards + 1 >=
SSH_MAX_FORWARDS_PER_DIRECTION || SSH_MAX_FORWARDS_PER_DIRECTION ||
channel_request_remote_forwarding(fwd.listen_host, channel_request_remote_forwarding(fwd.listen_host,
fwd.listen_port, fwd.connect_host, fwd.connect_port) < 0) fwd.listen_port, fwd.connect_host, fwd.connect_port) < 0)
goto fail; goto fail;
add_remote_forward(&options, &fwd); 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; 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, MUX_S_OK);
buffer_put_int(r, rid); buffer_put_int(r, rid);
@ -1392,6 +1476,15 @@ mux_client_request_forward(int fd, u_int ftype, Forward *fwd)
switch (type) { switch (type) {
case MUX_S_OK: case MUX_S_OK:
break; 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: case MUX_S_PERMISSION_DENIED:
e = buffer_get_string(&m, NULL); e = buffer_get_string(&m, NULL);
buffer_free(&m); buffer_free(&m);
@ -1758,6 +1851,10 @@ muxclient(const char *path)
mux_client_request_terminate(sock); mux_client_request_terminate(sock);
fprintf(stderr, "Exit request sent.\r\n"); fprintf(stderr, "Exit request sent.\r\n");
exit(0); 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: case SSHMUX_COMMAND_OPEN:
if (mux_client_request_forwards(sock) != 0) { if (mux_client_request_forwards(sock) != 0) {
error("%s: master forward request failed", __func__); error("%s: master forward request failed", __func__);

View File

@ -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 <ylo@cs.hut.fi> * Author: Tatu Ylonen <ylo@cs.hut.fi>
* Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
@ -283,6 +283,7 @@ add_remote_forward(Options *options, const Forward *newfwd)
fwd->listen_port = newfwd->listen_port; fwd->listen_port = newfwd->listen_port;
fwd->connect_host = newfwd->connect_host; fwd->connect_host = newfwd->connect_host;
fwd->connect_port = newfwd->connect_port; fwd->connect_port = newfwd->connect_port;
fwd->allocated_port = 0;
} }
static void static void

View File

@ -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 <ylo@cs.hut.fi> * Author: Tatu Ylonen <ylo@cs.hut.fi>
@ -23,6 +23,7 @@ typedef struct {
int listen_port; /* Port to forward. */ int listen_port; /* Port to forward. */
char *connect_host; /* Host to connect. */ char *connect_host; /* Host to connect. */
int connect_port; /* Port to connect on connect_host. */ int connect_port; /* Port to connect on connect_host. */
int allocated_port; /* Dynamically allocated listen port */
} Forward; } Forward;
/* Data structure for representing option data. */ /* Data structure for representing option data. */

11
ssh.1
View File

@ -34,8 +34,8 @@
.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF .\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. .\" 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 $ .\" $OpenBSD: ssh.1,v 1.305 2010/05/16 12:55:51 markus Exp $
.Dd $Mdocdate: March 26 2010 $ .Dd $Mdocdate: May 16 2010 $
.Dt SSH 1 .Dt SSH 1
.Os .Os
.Sh NAME .Sh NAME
@ -421,7 +421,9 @@ option is specified, the
argument is interpreted and passed to the master process. argument is interpreted and passed to the master process.
Valid commands are: Valid commands are:
.Dq check .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 .Dq exit
(request the master to exit). (request the master to exit).
.It Fl o Ar option .It Fl o Ar option
@ -557,6 +559,9 @@ argument is
.Ql 0 , .Ql 0 ,
the listen port will be dynamically allocated on the server and reported the listen port will be dynamically allocated on the server and reported
to the client at run time. 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 .It Fl S Ar ctl_path
Specifies the location of a control socket for connection sharing, Specifies the location of a control socket for connection sharing,
or the string or the string

9
ssh.c
View File

@ -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 <ylo@cs.hut.fi> * Author: Tatu Ylonen <ylo@cs.hut.fi>
* Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
@ -327,6 +327,8 @@ main(int ac, char **av)
fatal("Multiplexing command already specified"); fatal("Multiplexing command already specified");
if (strcmp(optarg, "check") == 0) if (strcmp(optarg, "check") == 0)
muxclient_command = SSHMUX_COMMAND_ALIVE_CHECK; muxclient_command = SSHMUX_COMMAND_ALIVE_CHECK;
else if (strcmp(optarg, "forward") == 0)
muxclient_command = SSHMUX_COMMAND_FORWARD;
else if (strcmp(optarg, "exit") == 0) else if (strcmp(optarg, "exit") == 0)
muxclient_command = SSHMUX_COMMAND_TERMINATE; muxclient_command = SSHMUX_COMMAND_TERMINATE;
else else
@ -877,9 +879,10 @@ ssh_confirm_remote_forward(int type, u_int32_t seq, void *ctxt)
type == SSH2_MSG_REQUEST_SUCCESS ? "success" : "failure", type == SSH2_MSG_REQUEST_SUCCESS ? "success" : "failure",
rfwd->listen_port, rfwd->connect_host, rfwd->connect_port); rfwd->listen_port, rfwd->connect_host, rfwd->connect_port);
if (type == SSH2_MSG_REQUEST_SUCCESS && rfwd->listen_port == 0) { 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", logit("Allocated port %u for remote forward to %s:%d",
packet_get_int(), rfwd->allocated_port,
rfwd->connect_host, rfwd->connect_port); rfwd->connect_host, rfwd->connect_port);
} }
if (type == SSH2_MSG_REQUEST_FAILURE) { if (type == SSH2_MSG_REQUEST_FAILURE) {