summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDamien Miller <djm@cvs.openbsd.org>2011-04-17 22:42:43 +0000
committerDamien Miller <djm@cvs.openbsd.org>2011-04-17 22:42:43 +0000
commitcae4ba1304b94cdf4ac037ea2afe02947c1a7cee (patch)
tree51a80302e13f2bab9565a6738d2bd197fbf997fc
parent9cb005d509dedaa074012f87db4b9dc88c03f1f1 (diff)
allow graceful shutdown of multiplexing: request that a mux server removes
its listener socket and refuse future multiplexing requests; ok markus@
-rw-r--r--usr.bin/ssh/PROTOCOL.mux19
-rw-r--r--usr.bin/ssh/clientloop.c34
-rw-r--r--usr.bin/ssh/clientloop.h4
-rw-r--r--usr.bin/ssh/mux.c86
-rw-r--r--usr.bin/ssh/ssh.16
-rw-r--r--usr.bin/ssh/ssh.c4
6 files changed, 135 insertions, 18 deletions
diff --git a/usr.bin/ssh/PROTOCOL.mux b/usr.bin/ssh/PROTOCOL.mux
index 2a5817bd7f8..05bb146907b 100644
--- a/usr.bin/ssh/PROTOCOL.mux
+++ b/usr.bin/ssh/PROTOCOL.mux
@@ -149,10 +149,21 @@ The client then sends its standard input and output file descriptors
The contents of "reserved" are currently ignored.
-A server may reply with a MUX_S_SESSION_OPEED, a MUX_S_PERMISSION_DENIED
+A server may reply with a MUX_S_SESSION_OPENED, a MUX_S_PERMISSION_DENIED
or a MUX_S_FAILURE.
-8. Status messages
+8. Requesting shutdown of mux listener
+
+A client may request the master to stop accepting new multiplexing requests
+and remove its listener socket.
+
+ uint32 MUX_C_STOP_LISTENING
+ uint32 request id
+
+A server may reply with a MUX_S_OK, a MUX_S_PERMISSION_DENIED or a
+MUX_S_FAILURE.
+
+9. Status messages
The MUX_S_OK message is empty:
@@ -178,6 +189,7 @@ The MUX_S_PERMISSION_DENIED and MUX_S_FAILURE include a reason:
#define MUX_C_OPEN_FWD 0x10000006
#define MUX_C_CLOSE_FWD 0x10000007
#define MUX_C_NEW_STDIO_FWD 0x10000008
+#define MUX_C_STOP_LISTENING 0x10000009
#define MUX_S_OK 0x80000001
#define MUX_S_PERMISSION_DENIED 0x80000002
#define MUX_S_FAILURE 0x80000003
@@ -192,7 +204,6 @@ The MUX_S_PERMISSION_DENIED and MUX_S_FAILURE include a reason:
XXX TODO
XXX extended status (e.g. report open channels / forwards)
-XXX graceful close (delete listening socket, but keep existing sessions active)
XXX lock (maybe)
XXX watch in/out traffic (pre/post crypto)
XXX inject packet (what about replies)
@@ -200,4 +211,4 @@ XXX server->client error/warning notifications
XXX port0 rfwd (need custom response message)
XXX send signals via mux
-$OpenBSD: PROTOCOL.mux,v 1.4 2011/01/31 21:42:15 djm Exp $
+$OpenBSD: PROTOCOL.mux,v 1.5 2011/04/17 22:42:41 djm Exp $
diff --git a/usr.bin/ssh/clientloop.c b/usr.bin/ssh/clientloop.c
index 080e3492e46..f3f68ca2b42 100644
--- a/usr.bin/ssh/clientloop.c
+++ b/usr.bin/ssh/clientloop.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: clientloop.c,v 1.231 2011/01/16 12:05:59 djm Exp $ */
+/* $OpenBSD: clientloop.c,v 1.232 2011/04/17 22:42:41 djm Exp $ */
/*
* Author: Tatu Ylonen <ylo@cs.hut.fi>
* Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
@@ -257,10 +257,10 @@ static void
set_control_persist_exit_time(void)
{
if (muxserver_sock == -1 || !options.control_persist
- || options.control_persist_timeout == 0)
+ || options.control_persist_timeout == 0) {
/* not using a ControlPersist timeout */
control_persist_exit_time = 0;
- else if (channel_still_open()) {
+ } else if (channel_still_open()) {
/* some client connections are still open */
if (control_persist_exit_time > 0)
debug2("%s: cancel scheduled exit", __func__);
@@ -1407,14 +1407,17 @@ client_loop(int have_pty, int escape_char_arg, int ssh2_chan_id)
if (compat20) {
session_ident = ssh2_chan_id;
- if (escape_char_arg != SSH_ESCAPECHAR_NONE)
- channel_register_filter(session_ident,
- client_simple_escape_filter, NULL,
- client_filter_cleanup,
- client_new_escape_filter_ctx(escape_char_arg));
- if (session_ident != -1)
+ if (session_ident != -1) {
+ if (escape_char_arg != SSH_ESCAPECHAR_NONE) {
+ channel_register_filter(session_ident,
+ client_simple_escape_filter, NULL,
+ client_filter_cleanup,
+ client_new_escape_filter_ctx(
+ escape_char_arg));
+ }
channel_register_cleanup(session_ident,
client_channel_closed, 0);
+ }
} else {
/* Check if we should immediately send eof on stdin. */
client_check_initial_eof_on_stdin();
@@ -2104,6 +2107,19 @@ client_init_dispatch(void)
client_init_dispatch_15();
}
+void
+client_stop_mux(void)
+{
+ if (options.control_path != NULL && muxserver_sock != -1)
+ unlink(options.control_path);
+ /*
+ * If we are in persist mode, signal that we should close when all
+ * active channels are closed.
+ */
+ if (options.control_persist)
+ session_closed = 1;
+}
+
/* client specific fatal cleanup */
void
cleanup_exit(int i)
diff --git a/usr.bin/ssh/clientloop.h b/usr.bin/ssh/clientloop.h
index 52115db6ec8..37d07290628 100644
--- a/usr.bin/ssh/clientloop.h
+++ b/usr.bin/ssh/clientloop.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: clientloop.h,v 1.25 2010/06/25 23:15:36 djm Exp $ */
+/* $OpenBSD: clientloop.h,v 1.26 2011/04/17 22:42:41 djm Exp $ */
/*
* Author: Tatu Ylonen <ylo@cs.hut.fi>
@@ -45,6 +45,7 @@ void client_global_request_reply_fwd(int, u_int32_t, void *);
void client_session2_setup(int, int, int, const char *, struct termios *,
int, Buffer *, char **);
int client_request_tun_fwd(int, int, int);
+void client_stop_mux(void);
/* Escape filter for protocol 2 sessions */
void *client_new_escape_filter_ctx(int);
@@ -64,6 +65,7 @@ void client_register_global_confirm(global_confirm_cb *, void *);
#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 */
+#define SSHMUX_COMMAND_STOP 6 /* Disable mux but not conn */
void muxserver_listen(void);
void muxclient(const char *);
diff --git a/usr.bin/ssh/mux.c b/usr.bin/ssh/mux.c
index 067e9d1a7c9..28daadbcd48 100644
--- a/usr.bin/ssh/mux.c
+++ b/usr.bin/ssh/mux.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: mux.c,v 1.24 2011/01/13 21:54:53 djm Exp $ */
+/* $OpenBSD: mux.c,v 1.25 2011/04/17 22:42:41 djm Exp $ */
/*
* Copyright (c) 2002-2008 Damien Miller <djm@openbsd.org>
*
@@ -129,6 +129,7 @@ struct mux_master_state {
#define MUX_C_OPEN_FWD 0x10000006
#define MUX_C_CLOSE_FWD 0x10000007
#define MUX_C_NEW_STDIO_FWD 0x10000008
+#define MUX_C_STOP_LISTENING 0x10000009
#define MUX_S_OK 0x80000001
#define MUX_S_PERMISSION_DENIED 0x80000002
#define MUX_S_FAILURE 0x80000003
@@ -151,6 +152,7 @@ static int process_mux_terminate(u_int, Channel *, Buffer *, Buffer *);
static int process_mux_open_fwd(u_int, Channel *, Buffer *, Buffer *);
static int process_mux_close_fwd(u_int, Channel *, Buffer *, Buffer *);
static int process_mux_stdio_fwd(u_int, Channel *, Buffer *, Buffer *);
+static int process_mux_stop_listening(u_int, Channel *, Buffer *, Buffer *);
static const struct {
u_int type;
@@ -163,6 +165,7 @@ static const struct {
{ MUX_C_OPEN_FWD, process_mux_open_fwd },
{ MUX_C_CLOSE_FWD, process_mux_close_fwd },
{ MUX_C_NEW_STDIO_FWD, process_mux_stdio_fwd },
+ { MUX_C_STOP_LISTENING, process_mux_stop_listening },
{ 0, NULL }
};
@@ -898,6 +901,39 @@ process_mux_stdio_fwd(u_int rid, Channel *c, Buffer *m, Buffer *r)
return 0;
}
+static int
+process_mux_stop_listening(u_int rid, Channel *c, Buffer *m, Buffer *r)
+{
+ debug("%s: channel %d: stop listening", __func__, c->self);
+
+ if (options.control_master == SSHCTL_MASTER_ASK ||
+ options.control_master == SSHCTL_MASTER_AUTO_ASK) {
+ if (!ask_permission("Disable further multiplexing on shared "
+ "connection to %s? ", host)) {
+ debug2("%s: stop listen refused by user", __func__);
+ buffer_put_int(r, MUX_S_PERMISSION_DENIED);
+ buffer_put_int(r, rid);
+ buffer_put_cstring(r, "Permission denied");
+ return 0;
+ }
+ }
+
+ if (mux_listener_channel != NULL) {
+ channel_free(mux_listener_channel);
+ client_stop_mux();
+ xfree(options.control_path);
+ options.control_path = NULL;
+ mux_listener_channel = NULL;
+ muxserver_sock = -1;
+ }
+
+ /* prepare reply */
+ buffer_put_int(r, MUX_S_OK);
+ buffer_put_int(r, rid);
+
+ return 0;
+}
+
/* Channel callbacks fired on read/write from mux slave fd */
static int
mux_master_read_cb(Channel *c)
@@ -1789,6 +1825,50 @@ mux_client_request_stdio_fwd(int fd)
fatal("%s: master returned unexpected message %u", __func__, type);
}
+static void
+mux_client_request_stop_listening(int fd)
+{
+ Buffer m;
+ char *e;
+ u_int type, rid;
+
+ debug3("%s: entering", __func__);
+
+ buffer_init(&m);
+ buffer_put_int(&m, MUX_C_STOP_LISTENING);
+ buffer_put_int(&m, muxclient_request_id);
+
+ if (mux_client_write_packet(fd, &m) != 0)
+ fatal("%s: write packet: %s", __func__, strerror(errno));
+
+ buffer_clear(&m);
+
+ /* Read their reply */
+ if (mux_client_read_packet(fd, &m) != 0)
+ fatal("%s: read from master failed: %s",
+ __func__, strerror(errno));
+
+ type = buffer_get_int(&m);
+ if ((rid = buffer_get_int(&m)) != muxclient_request_id)
+ fatal("%s: out of sequence reply: my id %u theirs %u",
+ __func__, muxclient_request_id, rid);
+ switch (type) {
+ case MUX_S_OK:
+ break;
+ case MUX_S_PERMISSION_DENIED:
+ e = buffer_get_string(&m, NULL);
+ fatal("Master refused stop listening request: %s", e);
+ case MUX_S_FAILURE:
+ e = buffer_get_string(&m, NULL);
+ fatal("%s: stop listening request failed: %s", __func__, e);
+ default:
+ fatal("%s: unexpected response from master 0x%08x",
+ __func__, type);
+ }
+ buffer_free(&m);
+ muxclient_request_id++;
+}
+
/* Multiplex client main loop. */
void
muxclient(const char *path)
@@ -1881,6 +1961,10 @@ muxclient(const char *path)
case SSHMUX_COMMAND_STDIO_FWD:
mux_client_request_stdio_fwd(sock);
exit(0);
+ case SSHMUX_COMMAND_STOP:
+ mux_client_request_stop_listening(sock);
+ fprintf(stderr, "Stop listening request sent.\r\n");
+ exit(0);
default:
fatal("unrecognised muxclient_command %d", muxclient_command);
}
diff --git a/usr.bin/ssh/ssh.1 b/usr.bin/ssh/ssh.1
index e3a42b5ad7d..1b0bcb781b8 100644
--- a/usr.bin/ssh/ssh.1
+++ b/usr.bin/ssh/ssh.1
@@ -33,8 +33,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.316 2010/11/18 15:01:00 jmc Exp $
-.Dd $Mdocdate: November 18 2010 $
+.\" $OpenBSD: ssh.1,v 1.317 2011/04/17 22:42:41 djm Exp $
+.Dd $Mdocdate: April 17 2011 $
.Dt SSH 1
.Os
.Sh NAME
@@ -395,6 +395,8 @@ Valid commands are:
(request forwardings without command execution) and
.Dq exit
(request the master to exit).
+.Dq stop
+(request the master to stop accepting further multiplexing requests).
.It Fl o Ar option
Can be used to give options in the format used in the configuration file.
This is useful for specifying options for which there is no separate
diff --git a/usr.bin/ssh/ssh.c b/usr.bin/ssh/ssh.c
index 4da221e9a2e..dd3b6b426c6 100644
--- a/usr.bin/ssh/ssh.c
+++ b/usr.bin/ssh/ssh.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: ssh.c,v 1.356 2011/01/06 22:23:53 djm Exp $ */
+/* $OpenBSD: ssh.c,v 1.357 2011/04/17 22:42:42 djm Exp $ */
/*
* Author: Tatu Ylonen <ylo@cs.hut.fi>
* Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
@@ -333,6 +333,8 @@ main(int ac, char **av)
muxclient_command = SSHMUX_COMMAND_FORWARD;
else if (strcmp(optarg, "exit") == 0)
muxclient_command = SSHMUX_COMMAND_TERMINATE;
+ else if (strcmp(optarg, "stop") == 0)
+ muxclient_command = SSHMUX_COMMAND_STOP;
else
fatal("Invalid multiplex command.");
break;