- 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@
This commit is contained in:
parent
c4bb91c79c
commit
e11e1ea5d4
|
@ -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
|
||||
|
|
63
clientloop.c
63
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 <ylo@cs.hut.fi>
|
||||
* Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, 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);
|
||||
|
|
36
readconf.c
36
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 <ylo@cs.hut.fi>
|
||||
* Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, 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)
|
||||
|
|
|
@ -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 <ylo@cs.hut.fi>
|
||||
|
@ -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;
|
||||
|
||||
|
|
117
ssh.c
117
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 <ylo@cs.hut.fi>
|
||||
* Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, 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)
|
||||
|
|
26
ssh_config.5
26
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
|
||||
|
|
Loading…
Reference in New Issue