From cae4ba1304b94cdf4ac037ea2afe02947c1a7cee Mon Sep 17 00:00:00 2001 From: Damien Miller Date: Sun, 17 Apr 2011 22:42:43 +0000 Subject: allow graceful shutdown of multiplexing: request that a mux server removes its listener socket and refuse future multiplexing requests; ok markus@ --- usr.bin/ssh/PROTOCOL.mux | 19 ++++++++--- usr.bin/ssh/clientloop.c | 34 ++++++++++++++----- usr.bin/ssh/clientloop.h | 4 ++- usr.bin/ssh/mux.c | 86 +++++++++++++++++++++++++++++++++++++++++++++++- usr.bin/ssh/ssh.1 | 6 ++-- usr.bin/ssh/ssh.c | 4 ++- 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 * Copyright (c) 1995 Tatu Ylonen , 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 @@ -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 * @@ -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 * Copyright (c) 1995 Tatu Ylonen , 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; -- cgit v1.2.3