From 22c97f1539816e2df00e91d023243b7d8d67a557 Mon Sep 17 00:00:00 2001 From: Damien Miller Date: Fri, 16 Apr 2010 15:53:23 +1000 Subject: - djm@cvs.openbsd.org 2010/04/10 02:08:44 [clientloop.c] bz#1698: kill channel when pty allocation requests fail. Fixed stuck client if the server refuses pty allocation. ok dtucker@ "think so" markus@ --- clientloop.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'clientloop.c') diff --git a/clientloop.c b/clientloop.c index 9ab56b44c..76de37213 100644 --- a/clientloop.c +++ b/clientloop.c @@ -1,4 +1,4 @@ -/* $OpenBSD: clientloop.c,v 1.219 2010/03/13 21:10:38 djm Exp $ */ +/* $OpenBSD: clientloop.c,v 1.220 2010/04/10 02:08:44 djm Exp $ */ /* * Author: Tatu Ylonen * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland @@ -1912,7 +1912,7 @@ client_session2_setup(int id, int want_tty, int want_subsystem, memset(&ws, 0, sizeof(ws)); channel_request_start(id, "pty-req", 1); - client_expect_confirm(id, "PTY allocation", 0); + client_expect_confirm(id, "PTY allocation", 1); packet_put_cstring(term != NULL ? term : ""); packet_put_int((u_int)ws.ws_col); packet_put_int((u_int)ws.ws_row); -- cgit v1.2.3 From 1ab6a51f9b258a6e844f1ee442c15aec7fcb6a72 Mon Sep 17 00:00:00 2001 From: Damien Miller Date: Sat, 26 Jun 2010 10:02:24 +1000 Subject: - djm@cvs.openbsd.org 2010/06/25 23:10:30 [ssh.c] log the hostname and address that we connected to at LogLevel=verbose after authentication is successful to mitigate "phishing" attacks by servers with trusted keys that accept authentication silently and automatically before presenting fake password/passphrase prompts; "nice!" markus@ --- ChangeLog | 7 +++++++ clientloop.c | 26 ++++++++++++++++++++------ clientloop.h | 4 ++-- mux.c | 9 ++++++--- readconf.c | 14 +++++++++++--- readconf.h | 3 ++- ssh.c | 9 ++++++--- ssh_config.5 | 14 ++++++++++++-- 8 files changed, 66 insertions(+), 20 deletions(-) (limited to 'clientloop.c') diff --git a/ChangeLog b/ChangeLog index b0f82de2f..d86960e31 100644 --- a/ChangeLog +++ b/ChangeLog @@ -74,6 +74,13 @@ servers with trusted keys that accept authentication silently and automatically before presenting fake password/passphrase prompts; "nice!" markus@ + - djm@cvs.openbsd.org 2010/06/25 23:10:30 + [ssh.c] + log the hostname and address that we connected to at LogLevel=verbose + after authentication is successful to mitigate "phishing" attacks by + servers with trusted keys that accept authentication silently and + automatically before presenting fake password/passphrase prompts; + "nice!" markus@ 20100622 - (djm) [loginrec.c] crank LINFO_NAMESIZE (username length) to 512 diff --git a/clientloop.c b/clientloop.c index 76de37213..5608bcc2e 100644 --- a/clientloop.c +++ b/clientloop.c @@ -1,4 +1,4 @@ -/* $OpenBSD: clientloop.c,v 1.220 2010/04/10 02:08:44 djm Exp $ */ +/* $OpenBSD: clientloop.c,v 1.221 2010/06/25 23:15:36 djm Exp $ */ /* * Author: Tatu Ylonen * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland @@ -155,11 +155,12 @@ static int stdin_eof; /* EOF has been encountered on stderr. */ static Buffer stdin_buffer; /* Buffer for stdin data. */ static Buffer stdout_buffer; /* Buffer for stdout data. */ static Buffer stderr_buffer; /* Buffer for stderr data. */ -static u_int buffer_high;/* Soft max buffer size. */ +static u_int buffer_high; /* Soft max buffer size. */ static int connection_in; /* Connection to server (input). */ static int connection_out; /* Connection to server (output). */ static int need_rekeying; /* Set to non-zero if rekeying is requested. */ -static int session_closed = 0; /* In SSH2: login session closed. */ +static int session_closed; /* In SSH2: login session closed. */ +static int x11_refuse_time; /* If >0, refuse x11 opens after this time. */ static void client_init_dispatch(void); int session_ident = -1; @@ -254,7 +255,7 @@ get_current_time(void) #define SSH_X11_PROTO "MIT-MAGIC-COOKIE-1" void client_x11_get_proto(const char *display, const char *xauth_path, - u_int trusted, char **_proto, char **_data) + u_int trusted, u_int timeout, char **_proto, char **_data) { char cmd[1024]; char line[512]; @@ -264,6 +265,7 @@ client_x11_get_proto(const char *display, const char *xauth_path, int got_data = 0, generated = 0, do_unlink = 0, i; char *xauthdir, *xauthfile; struct stat st; + u_int now; xauthdir = xauthfile = NULL; *_proto = proto; @@ -299,11 +301,18 @@ client_x11_get_proto(const char *display, const char *xauth_path, xauthdir); snprintf(cmd, sizeof(cmd), "%s -f %s generate %s " SSH_X11_PROTO - " untrusted timeout 1200 2>" _PATH_DEVNULL, - xauth_path, xauthfile, display); + " untrusted timeout %u 2>" _PATH_DEVNULL, + xauth_path, xauthfile, display, timeout); debug2("x11_get_proto: %s", cmd); if (system(cmd) == 0) generated = 1; + if (x11_refuse_time == 0) { + now = time(NULL) + 1; + if (UINT_MAX - timeout < now) + x11_refuse_time = UINT_MAX; + else + x11_refuse_time = now + timeout; + } } } @@ -1686,6 +1695,11 @@ client_request_x11(const char *request_type, int rchan) "malicious server."); return NULL; } + if (x11_refuse_time != 0 && time(NULL) >= x11_refuse_time) { + verbose("Rejected X11 connection after ForwardX11Timeout " + "expired"); + return NULL; + } originator = packet_get_string(NULL); if (datafellows & SSH_BUG_X11FWD) { debug2("buggy server: x11 request w/o originator_port"); diff --git a/clientloop.h b/clientloop.h index a5bc246a3..52115db6e 100644 --- a/clientloop.h +++ b/clientloop.h @@ -1,4 +1,4 @@ -/* $OpenBSD: clientloop.h,v 1.24 2010/05/16 12:55:51 markus Exp $ */ +/* $OpenBSD: clientloop.h,v 1.25 2010/06/25 23:15:36 djm Exp $ */ /* * Author: Tatu Ylonen @@ -39,7 +39,7 @@ /* Client side main loop for the interactive session. */ int client_loop(int, int, int); -void client_x11_get_proto(const char *, const char *, u_int, +void client_x11_get_proto(const char *, const char *, u_int, u_int, char **, char **); void client_global_request_reply_fwd(int, u_int32_t, void *); void client_session2_setup(int, int, int, const char *, struct termios *, diff --git a/mux.c b/mux.c index fdf0385e0..5c3857ee8 100644 --- a/mux.c +++ b/mux.c @@ -1,4 +1,4 @@ -/* $OpenBSD: mux.c,v 1.20 2010/06/25 07:14:46 djm Exp $ */ +/* $OpenBSD: mux.c,v 1.21 2010/06/25 23:15:36 djm Exp $ */ /* * Copyright (c) 2002-2008 Damien Miller * @@ -1107,11 +1107,14 @@ mux_session_confirm(int id, int success, void *arg) display = getenv("DISPLAY"); if (cctx->want_x_fwd && options.forward_x11 && display != NULL) { char *proto, *data; + /* Get reasonable local authentication information. */ client_x11_get_proto(display, options.xauth_location, - options.forward_x11_trusted, &proto, &data); + options.forward_x11_trusted, options.forward_x11_timeout, + &proto, &data); /* Request forwarding with authentication spoofing. */ - debug("Requesting X11 forwarding with authentication spoofing."); + debug("Requesting X11 forwarding with authentication " + "spoofing."); x11_request_forwarding_with_spoofing(id, display, proto, data); /* XXX wait for reply */ } diff --git a/readconf.c b/readconf.c index aae9cef4b..da48ae7da 100644 --- a/readconf.c +++ b/readconf.c @@ -1,4 +1,4 @@ -/* $OpenBSD: readconf.c,v 1.185 2010/06/25 07:14:46 djm Exp $ */ +/* $OpenBSD: readconf.c,v 1.186 2010/06/25 23:15:36 djm Exp $ */ /* * Author: Tatu Ylonen * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland @@ -110,8 +110,8 @@ typedef enum { oBadOption, - oForwardAgent, oForwardX11, oForwardX11Trusted, oGatewayPorts, - oExitOnForwardFailure, + oForwardAgent, oForwardX11, oForwardX11Trusted, oForwardX11Timeout, + oGatewayPorts, oExitOnForwardFailure, oPasswordAuthentication, oRSAAuthentication, oChallengeResponseAuthentication, oXAuthLocation, oIdentityFile, oHostName, oPort, oCipher, oRemoteForward, oLocalForward, @@ -143,6 +143,7 @@ static struct { { "forwardagent", oForwardAgent }, { "forwardx11", oForwardX11 }, { "forwardx11trusted", oForwardX11Trusted }, + { "forwardx11timeout", oForwardX11Timeout }, { "exitonforwardfailure", oExitOnForwardFailure }, { "xauthlocation", oXAuthLocation }, { "gatewayports", oGatewayPorts }, @@ -414,6 +415,10 @@ parse_flag: case oForwardX11Trusted: intptr = &options->forward_x11_trusted; goto parse_flag; + + case oForwardX11Timeout: + intptr = &options->forward_x11_timeout; + goto parse_time; case oGatewayPorts: intptr = &options->gateway_ports; @@ -1018,6 +1023,7 @@ initialize_options(Options * options) options->forward_agent = -1; options->forward_x11 = -1; options->forward_x11_trusted = -1; + options->forward_x11_timeout = -1; options->exit_on_forward_failure = -1; options->xauth_location = NULL; options->gateway_ports = -1; @@ -1104,6 +1110,8 @@ fill_default_options(Options * options) options->forward_x11 = 0; if (options->forward_x11_trusted == -1) options->forward_x11_trusted = 0; + if (options->forward_x11_timeout == -1) + options->forward_x11_timeout = 1200; if (options->exit_on_forward_failure == -1) options->exit_on_forward_failure = 0; if (options->xauth_location == NULL) diff --git a/readconf.h b/readconf.h index 3c8eae9d2..66acafdef 100644 --- a/readconf.h +++ b/readconf.h @@ -1,4 +1,4 @@ -/* $OpenBSD: readconf.h,v 1.84 2010/06/25 07:14:46 djm Exp $ */ +/* $OpenBSD: readconf.h,v 1.85 2010/06/25 23:15:36 djm Exp $ */ /* * Author: Tatu Ylonen @@ -32,6 +32,7 @@ typedef struct { typedef struct { int forward_agent; /* Forward authentication agent. */ int forward_x11; /* Forward X11 display. */ + int forward_x11_timeout; /* Expiration for Cookies */ int forward_x11_trusted; /* Trust Forward X11 display. */ int exit_on_forward_failure; /* Exit if bind(2) fails for -L/-R */ char *xauth_location; /* Location for xauth program */ diff --git a/ssh.c b/ssh.c index 6537ad9a2..d8f0b214f 100644 --- a/ssh.c +++ b/ssh.c @@ -1,4 +1,4 @@ -/* $OpenBSD: ssh.c,v 1.339 2010/06/25 23:10:30 djm Exp $ */ +/* $OpenBSD: ssh.c,v 1.340 2010/06/25 23:15:36 djm Exp $ */ /* * Author: Tatu Ylonen * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland @@ -1103,7 +1103,9 @@ ssh_session(void) char *proto, *data; /* Get reasonable local authentication information. */ client_x11_get_proto(display, options.xauth_location, - options.forward_x11_trusted, &proto, &data); + options.forward_x11_trusted, + options.forward_x11_timeout, + &proto, &data); /* Request forwarding with authentication spoofing. */ debug("Requesting X11 forwarding with authentication " "spoofing."); @@ -1199,7 +1201,8 @@ ssh_session2_setup(int id, int success, void *arg) char *proto, *data; /* Get reasonable local authentication information. */ client_x11_get_proto(display, options.xauth_location, - options.forward_x11_trusted, &proto, &data); + options.forward_x11_trusted, + options.forward_x11_timeout, &proto, &data); /* Request forwarding with authentication spoofing. */ debug("Requesting X11 forwarding with authentication " "spoofing."); diff --git a/ssh_config.5 b/ssh_config.5 index 2df948e62..6968a4488 100644 --- a/ssh_config.5 +++ b/ssh_config.5 @@ -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_config.5,v 1.133 2010/04/16 06:45:01 jmc Exp $ -.Dd $Mdocdate: April 16 2010 $ +.\" $OpenBSD: ssh_config.5,v 1.134 2010/06/25 23:15:36 djm Exp $ +.Dd $Mdocdate: June 25 2010 $ .Dt SSH_CONFIG 5 .Os .Sh NAME @@ -432,6 +432,16 @@ An attacker may then be able to perform activities such as keystroke monitoring if the .Cm ForwardX11Trusted option is also enabled. +.It Cm ForwardX11Timeout +Specify a timeout for untrusted X11 forwarding using the format described in +.Sx TIME FORMATS +section of +.Xr sshd_config 5 . +X11 connections received by +.Xr ssh 1 +after this time will be refused. +The default is to disable untrusted X11 forwarding after twenty minutes has +elapsed. .It Cm ForwardX11Trusted If this option is set to .Dq yes , -- cgit v1.2.3 From e11e1ea5d475ee8be0038d64aa3e47c776295ac2 Mon Sep 17 00:00:00 2001 From: Damien Miller Date: Tue, 3 Aug 2010 16:04:46 +1000 Subject: - djm@cvs.openbsd.org 2010/07/19 09:15:12 [clientloop.c readconf.c readconf.h ssh.c ssh_config.5] add a "ControlPersist" option that automatically starts a background ssh(1) multiplex master when connecting. This connection can stay alive indefinitely, or can be set to automatically close after a user-specified duration of inactivity. bz#1330 - patch by dwmw2 AT infradead.org, but further hacked on by wmertens AT cisco.com, apb AT cequrux.com, martin-mindrot-bugzilla AT earth.li and myself; "looks ok" markus@ --- ChangeLog | 8 ++++ clientloop.c | 63 ++++++++++++++++++++++++++++++-- readconf.c | 36 +++++++++++++++++- readconf.h | 4 +- ssh.c | 117 ++++++++++++++++++++++++++++++++++++++++++++++++----------- ssh_config.5 | 26 ++++++++++++- 6 files changed, 223 insertions(+), 31 deletions(-) (limited to 'clientloop.c') diff --git a/ChangeLog b/ChangeLog index f4fb5f05f..b43074ec9 100644 --- a/ChangeLog +++ b/ChangeLog @@ -16,6 +16,14 @@ bz#1797: fix swapped args in upload_dir_internal(), breaking recursive upload depth checks and causing verbose printing of transfers to always be turned on; patch from imorgan AT nas.nasa.gov + - djm@cvs.openbsd.org 2010/07/19 09:15:12 + [clientloop.c readconf.c readconf.h ssh.c ssh_config.5] + add a "ControlPersist" option that automatically starts a background + ssh(1) multiplex master when connecting. This connection can stay alive + indefinitely, or can be set to automatically close after a user-specified + duration of inactivity. bz#1330 - patch by dwmw2 AT infradead.org, but + further hacked on by wmertens AT cisco.com, apb AT cequrux.com, + martin-mindrot-bugzilla AT earth.li and myself; "looks ok" markus@ 20100819 - (dtucker) [contrib/ssh-copy-ud.1] Bug #1786: update ssh-copy-id.1 with more diff --git a/clientloop.c b/clientloop.c index 5608bcc2e..de7979366 100644 --- a/clientloop.c +++ b/clientloop.c @@ -1,4 +1,4 @@ -/* $OpenBSD: clientloop.c,v 1.221 2010/06/25 23:15:36 djm Exp $ */ +/* $OpenBSD: clientloop.c,v 1.222 2010/07/19 09:15:12 djm Exp $ */ /* * Author: Tatu Ylonen * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland @@ -145,6 +145,9 @@ static volatile sig_atomic_t received_signal = 0; /* Flag indicating whether the user's terminal is in non-blocking mode. */ static int in_non_blocking_mode = 0; +/* Time when backgrounded control master using ControlPersist should exit */ +static time_t control_persist_exit_time = 0; + /* Common data for the client loop code. */ volatile sig_atomic_t quit_pending; /* Set non-zero to quit the loop. */ static int escape_char1; /* Escape character. (proto1 only) */ @@ -252,6 +255,34 @@ get_current_time(void) return (double) tv.tv_sec + (double) tv.tv_usec / 1000000.0; } +/* + * Sets control_persist_exit_time to the absolute time when the + * backgrounded control master should exit due to expiry of the + * ControlPersist timeout. Sets it to 0 if we are not a backgrounded + * control master process, or if there is no ControlPersist timeout. + */ +static void +set_control_persist_exit_time(void) +{ + if (muxserver_sock == -1 || !options.control_persist + || options.control_persist_timeout == 0) + /* not using a ControlPersist timeout */ + control_persist_exit_time = 0; + else if (channel_still_open()) { + /* some client connections are still open */ + if (control_persist_exit_time > 0) + debug2("%s: cancel scheduled exit", __func__); + control_persist_exit_time = 0; + } else if (control_persist_exit_time <= 0) { + /* a client connection has recently closed */ + control_persist_exit_time = time(NULL) + + (time_t)options.control_persist_timeout; + debug2("%s: schedule exit in %d seconds", __func__, + options.control_persist_timeout); + } + /* else we are already counting down to the timeout */ +} + #define SSH_X11_PROTO "MIT-MAGIC-COOKIE-1" void client_x11_get_proto(const char *display, const char *xauth_path, @@ -533,6 +564,7 @@ client_wait_until_can_do_something(fd_set **readsetp, fd_set **writesetp, int *maxfdp, u_int *nallocp, int rekeying) { struct timeval tv, *tvp; + int timeout_secs; int ret; /* Add any selections by the channel mechanism. */ @@ -576,16 +608,27 @@ client_wait_until_can_do_something(fd_set **readsetp, fd_set **writesetp, /* * Wait for something to happen. This will suspend the process until * some selected descriptor can be read, written, or has some other - * event pending. + * event pending, or a timeout expires. */ - if (options.server_alive_interval == 0 || !compat20) + timeout_secs = INT_MAX; /* we use INT_MAX to mean no timeout */ + if (options.server_alive_interval > 0 && compat20) + timeout_secs = options.server_alive_interval; + set_control_persist_exit_time(); + if (control_persist_exit_time > 0) { + timeout_secs = MIN(timeout_secs, + control_persist_exit_time - time(NULL)); + if (timeout_secs < 0) + timeout_secs = 0; + } + if (timeout_secs == INT_MAX) tvp = NULL; else { - tv.tv_sec = options.server_alive_interval; + tv.tv_sec = timeout_secs; tv.tv_usec = 0; tvp = &tv; } + ret = select((*maxfdp)+1, *readsetp, *writesetp, NULL, tvp); if (ret < 0) { char buf[100]; @@ -1478,6 +1521,18 @@ client_loop(int have_pty, int escape_char_arg, int ssh2_chan_id) */ if (FD_ISSET(connection_out, writeset)) packet_write_poll(); + + /* + * If we are a backgrounded control master, and the + * timeout has expired without any active client + * connections, then quit. + */ + if (control_persist_exit_time > 0) { + if (time(NULL) >= control_persist_exit_time) { + debug("ControlPersist timeout expired"); + break; + } + } } if (readset) xfree(readset); diff --git a/readconf.c b/readconf.c index da48ae7da..0296590e2 100644 --- a/readconf.c +++ b/readconf.c @@ -1,4 +1,4 @@ -/* $OpenBSD: readconf.c,v 1.186 2010/06/25 23:15:36 djm Exp $ */ +/* $OpenBSD: readconf.c,v 1.187 2010/07/19 09:15:12 djm Exp $ */ /* * Author: Tatu Ylonen * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland @@ -128,7 +128,8 @@ typedef enum { oEnableSSHKeysign, oRekeyLimit, oVerifyHostKeyDNS, oConnectTimeout, oAddressFamily, oGssAuthentication, oGssDelegateCreds, oServerAliveInterval, oServerAliveCountMax, oIdentitiesOnly, - oSendEnv, oControlPath, oControlMaster, oHashKnownHosts, + oSendEnv, oControlPath, oControlMaster, oControlPersist, + oHashKnownHosts, oTunnel, oTunnelDevice, oLocalCommand, oPermitLocalCommand, oVisualHostKey, oUseRoaming, oZeroKnowledgePasswordAuthentication, oDeprecated, oUnsupported @@ -225,6 +226,7 @@ static struct { { "sendenv", oSendEnv }, { "controlpath", oControlPath }, { "controlmaster", oControlMaster }, + { "controlpersist", oControlPersist }, { "hashknownhosts", oHashKnownHosts }, { "tunnel", oTunnel }, { "tunneldevice", oTunnelDevice }, @@ -882,6 +884,30 @@ parse_int: *intptr = value; break; + case oControlPersist: + /* no/false/yes/true, or a time spec */ + intptr = &options->control_persist; + arg = strdelim(&s); + if (!arg || *arg == '\0') + fatal("%.200s line %d: Missing ControlPersist" + " argument.", filename, linenum); + value = 0; + value2 = 0; /* timeout */ + if (strcmp(arg, "no") == 0 || strcmp(arg, "false") == 0) + value = 0; + else if (strcmp(arg, "yes") == 0 || strcmp(arg, "true") == 0) + value = 1; + else if ((value2 = convtime(arg)) >= 0) + value = 1; + else + fatal("%.200s line %d: Bad ControlPersist argument.", + filename, linenum); + if (*activep && *intptr == -1) { + *intptr = value; + options->control_persist_timeout = value2; + } + break; + case oHashKnownHosts: intptr = &options->hash_known_hosts; goto parse_flag; @@ -1083,6 +1109,8 @@ initialize_options(Options * options) options->num_send_env = 0; options->control_path = NULL; options->control_master = -1; + options->control_persist = -1; + options->control_persist_timeout = 0; options->hash_known_hosts = -1; options->tun_open = -1; options->tun_local = -1; @@ -1218,6 +1246,10 @@ fill_default_options(Options * options) options->server_alive_count_max = 3; if (options->control_master == -1) options->control_master = 0; + if (options->control_persist == -1) { + options->control_persist = 0; + options->control_persist_timeout = 0; + } if (options->hash_known_hosts == -1) options->hash_known_hosts = 0; if (options->tun_open == -1) diff --git a/readconf.h b/readconf.h index 66acafdef..95d104674 100644 --- a/readconf.h +++ b/readconf.h @@ -1,4 +1,4 @@ -/* $OpenBSD: readconf.h,v 1.85 2010/06/25 23:15:36 djm Exp $ */ +/* $OpenBSD: readconf.h,v 1.86 2010/07/19 09:15:12 djm Exp $ */ /* * Author: Tatu Ylonen @@ -114,6 +114,8 @@ typedef struct { char *control_path; int control_master; + int control_persist; /* ControlPersist flag */ + int control_persist_timeout; /* ControlPersist timeout (seconds) */ int hash_known_hosts; diff --git a/ssh.c b/ssh.c index 61fe10df0..249be2db8 100644 --- a/ssh.c +++ b/ssh.c @@ -1,4 +1,4 @@ -/* $OpenBSD: ssh.c,v 1.343 2010/07/12 22:41:13 djm Exp $ */ +/* $OpenBSD: ssh.c,v 1.344 2010/07/19 09:15:12 djm Exp $ */ /* * Author: Tatu Ylonen * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland @@ -127,6 +127,15 @@ int no_shell_flag = 0; */ int stdin_null_flag = 0; +/* + * Flag indicating that the current process should be backgrounded and + * a new slave launched in the foreground for ControlPersist. + */ +int need_controlpersist_detach = 0; + +/* Copies of flags for ControlPersist foreground slave */ +int ostdin_null_flag, ono_shell_flag, ono_tty_flag, otty_flag; + /* * Flag indicating that ssh should fork after authentication. This is useful * so that the passphrase can be entered manually, and then ssh goes to the @@ -877,6 +886,50 @@ main(int ac, char **av) return exit_status; } +static void +control_persist_detach(void) +{ + pid_t pid; + + debug("%s: backgrounding master process", __func__); + + /* + * master (current process) into the background, and make the + * foreground process a client of the backgrounded master. + */ + switch ((pid = fork())) { + case -1: + fatal("%s: fork: %s", __func__, strerror(errno)); + case 0: + /* Child: master process continues mainloop */ + break; + default: + /* Parent: set up mux slave to connect to backgrounded master */ + debug2("%s: background process is %ld", __func__, (long)pid); + stdin_null_flag = ostdin_null_flag; + no_shell_flag = ono_shell_flag; + no_tty_flag = ono_tty_flag; + tty_flag = otty_flag; + close(muxserver_sock); + muxserver_sock = -1; + muxclient(options.control_path); + /* muxclient() doesn't return on success. */ + fatal("Failed to connect to new control master"); + } +} + +/* Do fork() after authentication. Used by "ssh -f" */ +static void +fork_postauth(void) +{ + if (need_controlpersist_detach) + control_persist_detach(); + debug("forking to background"); + fork_after_authentication_flag = 0; + if (daemon(1, 1) < 0) + fatal("daemon() failed: %.200s", strerror(errno)); +} + /* Callback for remote forward global requests */ static void ssh_confirm_remote_forward(int type, u_int32_t seq, void *ctxt) @@ -904,12 +957,8 @@ ssh_confirm_remote_forward(int type, u_int32_t seq, void *ctxt) } if (++remote_forward_confirms_received == options.num_remote_forwards) { debug("All remote forwarding requests processed"); - if (fork_after_authentication_flag) { - fork_after_authentication_flag = 0; - if (daemon(1, 1) < 0) - fatal("daemon() failed: %.200s", - strerror(errno)); - } + if (fork_after_authentication_flag) + fork_postauth(); } } @@ -1153,12 +1202,13 @@ ssh_session(void) * If requested and we are not interested in replies to remote * forwarding requests, then let ssh continue in the background. */ - if (fork_after_authentication_flag && - (!options.exit_on_forward_failure || - options.num_remote_forwards == 0)) { - fork_after_authentication_flag = 0; - if (daemon(1, 1) < 0) - fatal("daemon() failed: %.200s", strerror(errno)); + if (fork_after_authentication_flag) { + if (options.exit_on_forward_failure && + options.num_remote_forwards > 0) { + debug("deferring postauth fork until remote forward " + "confirmation received"); + } else + fork_postauth(); } /* @@ -1281,6 +1331,31 @@ ssh_session2(void) /* XXX should be pre-session */ ssh_init_forwarding(); + /* Start listening for multiplex clients */ + muxserver_listen(); + + /* + * If we are in control persist mode, then prepare to background + * ourselves and have a foreground client attach as a control + * slave. NB. we must save copies of the flags that we override for + * the backgrounding, since we defer attachment of the slave until + * after the connection is fully established (in particular, + * async rfwd replies have been received for ExitOnForwardFailure). + */ + if (options.control_persist && muxserver_sock != -1) { + ostdin_null_flag = stdin_null_flag; + ono_shell_flag = no_shell_flag; + ono_tty_flag = no_tty_flag; + otty_flag = tty_flag; + stdin_null_flag = 1; + no_shell_flag = 1; + no_tty_flag = 1; + tty_flag = 0; + if (!fork_after_authentication_flag) + need_controlpersist_detach = 1; + fork_after_authentication_flag = 1; + } + if (!no_shell_flag || (datafellows & SSH_BUG_DUMMYCHAN)) id = ssh_session2_open(); @@ -1299,19 +1374,17 @@ ssh_session2(void) options.permit_local_command) ssh_local_cmd(options.local_command); - /* Start listening for multiplex clients */ - muxserver_listen(); - /* * If requested and we are not interested in replies to remote * forwarding requests, then let ssh continue in the background. */ - if (fork_after_authentication_flag && - (!options.exit_on_forward_failure || - options.num_remote_forwards == 0)) { - fork_after_authentication_flag = 0; - if (daemon(1, 1) < 0) - fatal("daemon() failed: %.200s", strerror(errno)); + if (fork_after_authentication_flag) { + if (options.exit_on_forward_failure && + options.num_remote_forwards > 0) { + debug("deferring postauth fork until remote forward " + "confirmation received"); + } else + fork_postauth(); } if (options.use_roaming) diff --git a/ssh_config.5 b/ssh_config.5 index e7bb21ebb..04df8184c 100644 --- a/ssh_config.5 +++ b/ssh_config.5 @@ -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_config.5,v 1.136 2010/07/12 22:41:13 djm Exp $ -.Dd $Mdocdate: July 12 2010 $ +.\" $OpenBSD: ssh_config.5,v 1.137 2010/07/19 09:15:12 djm Exp $ +.Dd $Mdocdate: July 19 2010 $ .Dt SSH_CONFIG 5 .Os .Sh NAME @@ -319,6 +319,28 @@ It is recommended that any used for opportunistic connection sharing include at least %h, %p, and %r. This ensures that shared connections are uniquely identified. +.It Cm ControlPersist +When used in conjunction with +.Cm ControlMaster , +specifies that the master connection should remain open +in the background (waiting for future client connections) +after the initial client connection has been closed. +If set to +.Dq no , +then the master connection will not be placed into the background, +and will close as soon as the initial client connection is closed. +If set to +.Dq yes , +then the master connection will remain in the background indefinitely +(until killed or closed via a mechanism such as the +.Xr ssh 1 +.Dq Fl O No exit +option). +If set to a time in seconds, or a time in any of the formats documented in +.Xr sshd_config 5 , +then the backgrounded master connection will automatically terminate +after it has remained idle (with no client connections) for the +specified time. .It Cm DynamicForward Specifies that a TCP port on the local machine be forwarded over the secure channel, and the application -- cgit v1.2.3