upstream: add ChannelTimeout support to the client, mirroring the

same option in the server. ok markus@

OpenBSD-Commit-ID: 55630b26f390ac063980cfe7ad8c54b03284ef02
This commit is contained in:
djm@openbsd.org 2023-10-11 22:42:26 +00:00 committed by Damien Miller
parent 76e91e7238
commit a752a6c0e1
No known key found for this signature in database
8 changed files with 180 additions and 47 deletions

View File

@ -1,4 +1,4 @@
/* $OpenBSD: clientloop.c,v 1.398 2023/09/10 03:51:55 djm Exp $ */ /* $OpenBSD: clientloop.c,v 1.399 2023/10/11 22:42:26 djm 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
@ -1801,7 +1801,7 @@ client_request_x11(struct ssh *ssh, const char *request_type, int rchan)
sock = x11_connect_display(ssh); sock = x11_connect_display(ssh);
if (sock < 0) if (sock < 0)
return NULL; return NULL;
c = channel_new(ssh, "x11", c = channel_new(ssh, "x11-connection",
SSH_CHANNEL_X11_OPEN, sock, sock, -1, SSH_CHANNEL_X11_OPEN, sock, sock, -1,
CHAN_TCP_WINDOW_DEFAULT, CHAN_X11_PACKET_DEFAULT, 0, "x11", 1); CHAN_TCP_WINDOW_DEFAULT, CHAN_X11_PACKET_DEFAULT, 0, "x11", 1);
c->force_drain = 1; c->force_drain = 1;
@ -1836,7 +1836,7 @@ client_request_agent(struct ssh *ssh, const char *request_type, int rchan)
else else
debug2_fr(r, "ssh_agent_bind_hostkey"); debug2_fr(r, "ssh_agent_bind_hostkey");
c = channel_new(ssh, "authentication agent connection", c = channel_new(ssh, "agent-connection",
SSH_CHANNEL_OPEN, sock, sock, -1, SSH_CHANNEL_OPEN, sock, sock, -1,
CHAN_X11_WINDOW_DEFAULT, CHAN_TCP_PACKET_DEFAULT, 0, CHAN_X11_WINDOW_DEFAULT, CHAN_TCP_PACKET_DEFAULT, 0,
"authentication agent connection", 1); "authentication agent connection", 1);
@ -1864,7 +1864,7 @@ client_request_tun_fwd(struct ssh *ssh, int tun_mode,
} }
debug("Tunnel forwarding using interface %s", ifname); debug("Tunnel forwarding using interface %s", ifname);
c = channel_new(ssh, "tun", SSH_CHANNEL_OPENING, fd, fd, -1, c = channel_new(ssh, "tun-connection", SSH_CHANNEL_OPENING, fd, fd, -1,
CHAN_TCP_WINDOW_DEFAULT, CHAN_TCP_PACKET_DEFAULT, 0, "tun", 1); CHAN_TCP_WINDOW_DEFAULT, CHAN_TCP_PACKET_DEFAULT, 0, "tun", 1);
c->datagram = 1; c->datagram = 1;

39
misc.c
View File

@ -1,4 +1,4 @@
/* $OpenBSD: misc.c,v 1.187 2023/08/28 03:31:16 djm Exp $ */ /* $OpenBSD: misc.c,v 1.188 2023/10/11 22:42:26 djm Exp $ */
/* /*
* Copyright (c) 2000 Markus Friedl. All rights reserved. * Copyright (c) 2000 Markus Friedl. All rights reserved.
* Copyright (c) 2005-2020 Damien Miller. All rights reserved. * Copyright (c) 2005-2020 Damien Miller. All rights reserved.
@ -2493,6 +2493,43 @@ format_absolute_time(uint64_t t, char *buf, size_t len)
strftime(buf, len, "%Y-%m-%dT%H:%M:%S", &tm); strftime(buf, len, "%Y-%m-%dT%H:%M:%S", &tm);
} }
/*
* Parse a "pattern=interval" clause (e.g. a ChannelTimeout).
* Returns 0 on success or non-zero on failure.
* Caller must free *typep.
*/
int
parse_pattern_interval(const char *s, char **typep, int *secsp)
{
char *cp, *sdup;
int secs;
if (typep != NULL)
*typep = NULL;
if (secsp != NULL)
*secsp = 0;
if (s == NULL)
return -1;
sdup = xstrdup(s);
if ((cp = strchr(sdup, '=')) == NULL || cp == sdup) {
free(sdup);
return -1;
}
*cp++ = '\0';
if ((secs = convtime(cp)) < 0) {
free(sdup);
return -1;
}
/* success */
if (typep != NULL)
*typep = xstrdup(sdup);
if (secsp != NULL)
*secsp = secs;
free(sdup);
return 0;
}
/* check if path is absolute */ /* check if path is absolute */
int int
path_absolute(const char *path) path_absolute(const char *path)

3
misc.h
View File

@ -1,4 +1,4 @@
/* $OpenBSD: misc.h,v 1.105 2023/08/28 03:31:16 djm Exp $ */ /* $OpenBSD: misc.h,v 1.106 2023/10/11 22:42:26 djm Exp $ */
/* /*
* Author: Tatu Ylonen <ylo@cs.hut.fi> * Author: Tatu Ylonen <ylo@cs.hut.fi>
@ -95,6 +95,7 @@ int valid_env_name(const char *);
const char *atoi_err(const char *, int *); const char *atoi_err(const char *, int *);
int parse_absolute_time(const char *, uint64_t *); int parse_absolute_time(const char *, uint64_t *);
void format_absolute_time(uint64_t, char *, size_t); void format_absolute_time(uint64_t, char *, size_t);
int parse_pattern_interval(const char *, char **, int *);
int path_absolute(const char *); int path_absolute(const char *);
int stdfd_devnull(int, int, int); int stdfd_devnull(int, int, int);
int lib_contains_symbol(const char *, const char *); int lib_contains_symbol(const char *, const char *);

View File

@ -1,4 +1,4 @@
/* $OpenBSD: readconf.c,v 1.381 2023/08/28 03:31:16 djm Exp $ */ /* $OpenBSD: readconf.c,v 1.382 2023/10/11 22:42:26 djm 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
@ -178,7 +178,7 @@ typedef enum {
oFingerprintHash, oUpdateHostkeys, oHostbasedAcceptedAlgorithms, oFingerprintHash, oUpdateHostkeys, oHostbasedAcceptedAlgorithms,
oPubkeyAcceptedAlgorithms, oCASignatureAlgorithms, oProxyJump, oPubkeyAcceptedAlgorithms, oCASignatureAlgorithms, oProxyJump,
oSecurityKeyProvider, oKnownHostsCommand, oRequiredRSASize, oSecurityKeyProvider, oKnownHostsCommand, oRequiredRSASize,
oEnableEscapeCommandline, oObscureKeystrokeTiming, oEnableEscapeCommandline, oObscureKeystrokeTiming, oChannelTimeout,
oIgnore, oIgnoredUnknownOption, oDeprecated, oUnsupported oIgnore, oIgnoredUnknownOption, oDeprecated, oUnsupported
} OpCodes; } OpCodes;
@ -328,6 +328,7 @@ static struct {
{ "requiredrsasize", oRequiredRSASize }, { "requiredrsasize", oRequiredRSASize },
{ "enableescapecommandline", oEnableEscapeCommandline }, { "enableescapecommandline", oEnableEscapeCommandline },
{ "obscurekeystroketiming", oObscureKeystrokeTiming }, { "obscurekeystroketiming", oObscureKeystrokeTiming },
{ "channeltimeout", oChannelTimeout },
{ NULL, oBadOption } { NULL, oBadOption }
}; };
@ -2323,6 +2324,31 @@ parse_pubkey_algos:
*intptr = value; *intptr = value;
break; break;
case oChannelTimeout:
uvalue = options->num_channel_timeouts;
i = 0;
while ((arg = argv_next(&ac, &av)) != NULL) {
/* Allow "none" only in first position */
if (strcasecmp(arg, "none") == 0) {
if (i > 0 || ac > 0) {
error("%s line %d: keyword %s \"none\" "
"argument must appear alone.",
filename, linenum, keyword);
goto out;
}
} else if (parse_pattern_interval(arg,
NULL, NULL) != 0) {
fatal("%s line %d: invalid channel timeout %s",
filename, linenum, arg);
}
if (!*activep || uvalue != 0)
continue;
opt_array_append(filename, linenum, keyword,
&options->channel_timeouts,
&options->num_channel_timeouts, arg);
}
break;
case oDeprecated: case oDeprecated:
debug("%s line %d: Deprecated option \"%s\"", debug("%s line %d: Deprecated option \"%s\"",
filename, linenum, keyword); filename, linenum, keyword);
@ -2575,6 +2601,8 @@ initialize_options(Options * options)
options->enable_escape_commandline = -1; options->enable_escape_commandline = -1;
options->obscure_keystroke_timing_interval = -1; options->obscure_keystroke_timing_interval = -1;
options->tag = NULL; options->tag = NULL;
options->channel_timeouts = NULL;
options->num_channel_timeouts = 0;
} }
/* /*
@ -2815,6 +2843,16 @@ fill_default_options(Options * options)
v = NULL; \ v = NULL; \
} \ } \
} while(0) } while(0)
#define CLEAR_ON_NONE_ARRAY(v, nv, none) \
do { \
if (options->nv == 1 && \
strcasecmp(options->v[0], none) == 0) { \
free(options->v[0]); \
free(options->v); \
options->v = NULL; \
options->nv = 0; \
} \
} while (0)
CLEAR_ON_NONE(options->local_command); CLEAR_ON_NONE(options->local_command);
CLEAR_ON_NONE(options->remote_command); CLEAR_ON_NONE(options->remote_command);
CLEAR_ON_NONE(options->proxy_command); CLEAR_ON_NONE(options->proxy_command);
@ -2823,6 +2861,9 @@ fill_default_options(Options * options)
CLEAR_ON_NONE(options->pkcs11_provider); CLEAR_ON_NONE(options->pkcs11_provider);
CLEAR_ON_NONE(options->sk_provider); CLEAR_ON_NONE(options->sk_provider);
CLEAR_ON_NONE(options->known_hosts_command); CLEAR_ON_NONE(options->known_hosts_command);
CLEAR_ON_NONE_ARRAY(channel_timeouts, num_channel_timeouts, "none");
#undef CLEAR_ON_NONE
#undef CLEAR_ON_NONE_ARRAY
if (options->jump_host != NULL && if (options->jump_host != NULL &&
strcmp(options->jump_host, "none") == 0 && strcmp(options->jump_host, "none") == 0 &&
options->jump_port == 0 && options->jump_user == NULL) { options->jump_port == 0 && options->jump_user == NULL) {
@ -3527,6 +3568,8 @@ dump_client_config(Options *o, const char *host)
dump_cfg_strarray(oSetEnv, o->num_setenv, o->setenv); dump_cfg_strarray(oSetEnv, o->num_setenv, o->setenv);
dump_cfg_strarray_oneline(oLogVerbose, dump_cfg_strarray_oneline(oLogVerbose,
o->num_log_verbose, o->log_verbose); o->num_log_verbose, o->log_verbose);
dump_cfg_strarray_oneline(oChannelTimeout,
o->num_channel_timeouts, o->channel_timeouts);
/* Special cases */ /* Special cases */

View File

@ -1,4 +1,4 @@
/* $OpenBSD: readconf.h,v 1.152 2023/08/28 03:31:16 djm Exp $ */ /* $OpenBSD: readconf.h,v 1.153 2023/10/11 22:42:26 djm Exp $ */
/* /*
* Author: Tatu Ylonen <ylo@cs.hut.fi> * Author: Tatu Ylonen <ylo@cs.hut.fi>
@ -182,6 +182,9 @@ typedef struct {
int enable_escape_commandline; /* ~C commandline */ int enable_escape_commandline; /* ~C commandline */
int obscure_keystroke_timing_interval; int obscure_keystroke_timing_interval;
char **channel_timeouts; /* inactivity timeout by channel type */
u_int num_channel_timeouts;
char *ignored_unknown; /* Pattern list of unknown tokens to ignore */ char *ignored_unknown; /* Pattern list of unknown tokens to ignore */
} Options; } Options;

View File

@ -1,4 +1,4 @@
/* $OpenBSD: servconf.c,v 1.402 2023/09/08 06:34:24 djm Exp $ */ /* $OpenBSD: servconf.c,v 1.403 2023/10/11 22:42:26 djm Exp $ */
/* /*
* Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
* All rights reserved * All rights reserved
@ -956,39 +956,6 @@ process_permitopen(struct ssh *ssh, ServerOptions *options)
options->num_permitted_listens); options->num_permitted_listens);
} }
/* Parse a ChannelTimeout clause "pattern=interval" */
static int
parse_timeout(const char *s, char **typep, int *secsp)
{
char *cp, *sdup;
int secs;
if (typep != NULL)
*typep = NULL;
if (secsp != NULL)
*secsp = 0;
if (s == NULL)
return -1;
sdup = xstrdup(s);
if ((cp = strchr(sdup, '=')) == NULL || cp == sdup) {
free(sdup);
return -1;
}
*cp++ = '\0';
if ((secs = convtime(cp)) < 0) {
free(sdup);
return -1;
}
/* success */
if (typep != NULL)
*typep = xstrdup(sdup);
if (secsp != NULL)
*secsp = secs;
free(sdup);
return 0;
}
void void
process_channel_timeouts(struct ssh *ssh, ServerOptions *options) process_channel_timeouts(struct ssh *ssh, ServerOptions *options)
{ {
@ -999,7 +966,7 @@ process_channel_timeouts(struct ssh *ssh, ServerOptions *options)
debug3_f("setting %u timeouts", options->num_channel_timeouts); debug3_f("setting %u timeouts", options->num_channel_timeouts);
channel_clear_timeouts(ssh); channel_clear_timeouts(ssh);
for (i = 0; i < options->num_channel_timeouts; i++) { for (i = 0; i < options->num_channel_timeouts; i++) {
if (parse_timeout(options->channel_timeouts[i], if (parse_pattern_interval(options->channel_timeouts[i],
&type, &secs) != 0) { &type, &secs) != 0) {
fatal_f("internal error: bad timeout %s", fatal_f("internal error: bad timeout %s",
options->channel_timeouts[i]); options->channel_timeouts[i]);
@ -2549,7 +2516,8 @@ process_server_config_line_depth(ServerOptions *options, char *line,
filename, linenum, keyword); filename, linenum, keyword);
goto out; goto out;
} }
} else if (parse_timeout(arg, NULL, NULL) != 0) { } else if (parse_pattern_interval(arg,
NULL, NULL) != 0) {
fatal("%s line %d: invalid channel timeout %s", fatal("%s line %d: invalid channel timeout %s",
filename, linenum, arg); filename, linenum, arg);
} }

16
ssh.c
View File

@ -1,4 +1,4 @@
/* $OpenBSD: ssh.c,v 1.594 2023/09/03 23:59:32 djm Exp $ */ /* $OpenBSD: ssh.c,v 1.595 2023/10/11 22:42:26 djm 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
@ -1573,6 +1573,20 @@ main(int ac, char **av)
else else
timeout_ms = options.connection_timeout * 1000; timeout_ms = options.connection_timeout * 1000;
/* Apply channels timeouts, if set */
channel_clear_timeouts(ssh);
for (j = 0; j < options.num_channel_timeouts; j++) {
debug3("applying channel timeout %s",
options.channel_timeouts[j]);
if (parse_pattern_interval(options.channel_timeouts[j],
&cp, &i) != 0) {
fatal_f("internal error: bad timeout %s",
options.channel_timeouts[j]);
}
channel_add_timeout(ssh, cp, i);
free(cp);
}
/* Open a connection to the remote host. */ /* Open a connection to the remote host. */
if (ssh_connect(ssh, host, options.host_arg, addrs, &hostaddr, if (ssh_connect(ssh, host, options.host_arg, addrs, &hostaddr,
options.port, options.connection_attempts, options.port, options.connection_attempts,

View File

@ -33,7 +33,7 @@
.\" (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_config.5,v 1.389 2023/10/11 06:40:54 djm Exp $ .\" $OpenBSD: ssh_config.5,v 1.390 2023/10/11 22:42:26 djm Exp $
.Dd $Mdocdate: October 11 2023 $ .Dd $Mdocdate: October 11 2023 $
.Dt SSH_CONFIG 5 .Dt SSH_CONFIG 5
.Os .Os
@ -455,6 +455,73 @@ Multiple
.Cm CertificateFile .Cm CertificateFile
directives will add to the list of certificates used for directives will add to the list of certificates used for
authentication. authentication.
.It Cm ChannelTimeout
Specifies whether and how quickly
.Xr ssh 1
should close inactive channels.
Timeouts are specified as one or more
.Dq type=interval
pairs separated by whitespace, where the
.Dq type
must be a channel type name (as described in the table below), optionally
containing wildcard characters.
.Pp
The timeout value
.Dq interval
is specified in seconds or may use any of the units documented in the
.Sx TIME FORMATS
section.
For example,
.Dq session=5m
would cause the interactive session to terminate after five minutes of
inactivity.
Specifying a zero value disables the inactivity timeout.
.Pp
The available channel types include:
.Bl -tag -width Ds
.It Cm agent-connection
Open connections to
.Xr ssh-agent 1 .
.It Cm direct-tcpip , Cm direct-streamlocal@openssh.com
Open TCP or Unix socket (respectively) connections that have
been established from a
.Xr ssh 1
local forwarding, i.e.\&
.Cm LocalForward
or
.Cm DynamicForward .
.It Cm forwarded-tcpip , Cm forwarded-streamlocal@openssh.com
Open TCP or Unix socket (respectively) connections that have been
established to a
.Xr sshd 8
listening on behalf of a
.Xr ssh 1
remote forwarding, i.e.\&
.Cm RemoteForward .
.It Cm session
The interactive main session, including shell session, command execution,
.Xr scp 1 ,
.Xr sftp 1 ,
etc.
.It Cm tun-connection
Open
.Cm TunnelForward
connections.
.It Cm x11-connection
Open X11 forwarding sessions.
.El
.Pp
Note that in all the above cases, terminating an inactive session does not
guarantee to remove all resources associated with the session, e.g. shell
processes or X11 clients relating to the session may continue to execute.
.Pp
Moreover, terminating an inactive channel or session does not necessarily
close the SSH connection, nor does it prevent a client from
requesting another channel of the same type.
In particular, expiring an inactive forwarding session does not prevent
another identical forwarding from being subsequently created.
.Pp
The default is not to expire channels of any type for inactivity.
.It Cm CheckHostIP .It Cm CheckHostIP
If set to If set to
.Cm yes , .Cm yes ,