Mailing List Archive

[openssh] 02/03: upstream: add a "global" ChannelTimeout type to ssh(1) and sshd(8)
This is an automated email from the git hooks/post-receive script.

djm pushed a commit to branch master
in repository openssh.

commit b31b12d28de96e1d43581d32f34da8db27e11c03
Author: djm@openbsd.org <djm@openbsd.org>
Date: Tue Jan 9 22:19:00 2024 +0000

upstream: add a "global" ChannelTimeout type to ssh(1) and sshd(8)

that watches all open channels and will close all open channels if there is
no traffic on any of them for the specified interval. This is in addition to
the existing per-channel timeouts added a few releases ago.

This supports use-cases like having a session + x11 forwarding channel
open where one may be idle for an extended period but the other is
actively used. The global timeout would allow closing both channels when
both have been idle for too long.

ok dtucker@

OpenBSD-Commit-ID: 0054157d24d2eaa5dc1a9a9859afefc13d1d7eb3
---
channels.c | 74 +++++++++++++++++++++++++++++++++++++++++++++--------------
ssh_config.5 | 22 +++++++++++++-----
sshd_config.5 | 44 ++++++++++++++++++++---------------
3 files changed, 99 insertions(+), 41 deletions(-)

diff --git a/channels.c b/channels.c
index 20f31dad..6862556b 100644
--- a/channels.c
+++ b/channels.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: channels.c,v 1.435 2023/12/18 14:47:20 djm Exp $ */
+/* $OpenBSD: channels.c,v 1.436 2024/01/09 22:19:00 djm Exp $ */
/*
* Author: Tatu Ylonen <ylo@cs.hut.fi>
* Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
@@ -214,6 +214,9 @@ struct ssh_channels {
/* Channel timeouts by type */
struct ssh_channel_timeout *timeouts;
size_t ntimeouts;
+ /* Global timeout for all OPEN channels */
+ int global_deadline;
+ time_t lastused;
};

/* helper */
@@ -316,6 +319,11 @@ channel_add_timeout(struct ssh *ssh, const char *type_pattern,
{
struct ssh_channels *sc = ssh->chanctxt;

+ if (strcmp(type_pattern, "global") == 0) {
+ debug2_f("global channel timeout %d seconds", timeout_secs);
+ sc->global_deadline = timeout_secs;
+ return;
+ }
debug2_f("channel type \"%s\" timeout %d seconds",
type_pattern, timeout_secs);
sc->timeouts = xrecallocarray(sc->timeouts, sc->ntimeouts,
@@ -376,6 +384,38 @@ channel_set_xtype(struct ssh *ssh, int id, const char *xctype)
c->inactive_deadline);
}

+/*
+ * update "last used" time on a channel.
+ * NB. nothing else should update lastused except to clear it.
+ */
+static void
+channel_set_used_time(struct ssh *ssh, Channel *c)
+{
+ ssh->chanctxt->lastused = monotime();
+ if (c != NULL)
+ c->lastused = ssh->chanctxt->lastused;
+}
+
+/*
+ * Get the time at which a channel is due to time out for inactivity.
+ * Returns 0 if the channel is not due to time out ever.
+ */
+static time_t
+channel_get_expiry(struct ssh *ssh, Channel *c)
+{
+ struct ssh_channels *sc = ssh->chanctxt;
+ time_t expiry = 0, channel_expiry;
+
+ if (sc->lastused != 0 && sc->global_deadline != 0)
+ expiry = sc->lastused + sc->global_deadline;
+ if (c->lastused != 0 && c->inactive_deadline != 0) {
+ channel_expiry = c->lastused + c->inactive_deadline;
+ if (expiry == 0 || channel_expiry < expiry)
+ expiry = channel_expiry;
+ }
+ return expiry;
+}
+
/*
* Register filedescriptors for a channel, used when allocating a channel or
* when the channel consumer/producer is ready, e.g. shell exec'd
@@ -441,6 +481,8 @@ channel_register_fds(struct ssh *ssh, Channel *c, int rfd, int wfd, int efd,
if (efd != -1)
set_nonblock(efd);
}
+ /* channel might be entering a larval state, so reset global timeout */
+ channel_set_used_time(ssh, NULL);
}

/*
@@ -1197,7 +1239,7 @@ channel_set_fds(struct ssh *ssh, int id, int rfd, int wfd, int efd,

channel_register_fds(ssh, c, rfd, wfd, efd, extusage, nonblock, is_tty);
c->type = SSH_CHANNEL_OPEN;
- c->lastused = monotime();
+ channel_set_used_time(ssh, c);
c->local_window = c->local_window_max = window_max;

if ((r = sshpkt_start(ssh, SSH2_MSG_CHANNEL_WINDOW_ADJUST)) != 0 ||
@@ -1368,7 +1410,7 @@ channel_pre_x11_open(struct ssh *ssh, Channel *c)

if (ret == 1) {
c->type = SSH_CHANNEL_OPEN;
- c->lastused = monotime();
+ channel_set_used_time(ssh, c);
channel_pre_open(ssh, c);
} else if (ret == -1) {
logit("X11 connection rejected because of wrong "
@@ -2016,7 +2058,7 @@ channel_post_connecting(struct ssh *ssh, Channel *c)
c->self, c->connect_ctx.host, c->connect_ctx.port);
channel_connect_ctx_free(&c->connect_ctx);
c->type = SSH_CHANNEL_OPEN;
- c->lastused = monotime();
+ channel_set_used_time(ssh, c);
if (isopen) {
/* no message necessary */
} else {
@@ -2108,7 +2150,7 @@ channel_handle_rfd(struct ssh *ssh, Channel *c)
goto rfail;
}
if (nr != 0)
- c->lastused = monotime();
+ channel_set_used_time(ssh, c);
return 1;
}

@@ -2134,7 +2176,7 @@ channel_handle_rfd(struct ssh *ssh, Channel *c)
}
return -1;
}
- c->lastused = monotime();
+ channel_set_used_time(ssh, c);
if (c->input_filter != NULL) {
if (c->input_filter(ssh, c, buf, len) == -1) {
debug2("channel %d: filter stops", c->self);
@@ -2215,7 +2257,7 @@ channel_handle_wfd(struct ssh *ssh, Channel *c)
}
return -1;
}
- c->lastused = monotime();
+ channel_set_used_time(ssh, c);
#ifndef BROKEN_TCGETATTR_ICANON
if (c->isatty && dlen >= 1 && buf[0] != '\r') {
if (tcgetattr(c->wfd, &tio) == 0 &&
@@ -2264,7 +2306,7 @@ channel_handle_efd_write(struct ssh *ssh, Channel *c)
if ((r = sshbuf_consume(c->extended, len)) != 0)
fatal_fr(r, "channel %i: consume", c->self);
c->local_consumed += len;
- c->lastused = monotime();
+ channel_set_used_time(ssh, c);
}
return 1;
}
@@ -2291,7 +2333,7 @@ channel_handle_efd_read(struct ssh *ssh, Channel *c)
channel_close_fd(ssh, c, &c->efd);
return 1;
}
- c->lastused = monotime();
+ channel_set_used_time(ssh, c);
if (c->extended_usage == CHAN_EXTENDED_IGNORE)
debug3("channel %d: discard efd", c->self);
else if ((r = sshbuf_put(c->extended, buf, len)) != 0)
@@ -2581,10 +2623,9 @@ channel_handler(struct ssh *ssh, int table, struct timespec *timeout)
continue;
}
if (ftab[c->type] != NULL) {
- if (table == CHAN_PRE &&
- c->type == SSH_CHANNEL_OPEN &&
- c->inactive_deadline != 0 && c->lastused != 0 &&
- now >= c->lastused + c->inactive_deadline) {
+ if (table == CHAN_PRE && c->type == SSH_CHANNEL_OPEN &&
+ channel_get_expiry(ssh, c) != 0 &&
+ now >= channel_get_expiry(ssh, c)) {
/* channel closed for inactivity */
verbose("channel %d: closing after %u seconds "
"of inactivity", c->self,
@@ -2596,10 +2637,9 @@ channel_handler(struct ssh *ssh, int table, struct timespec *timeout)
/* inactivity timeouts must interrupt poll() */
if (timeout != NULL &&
c->type == SSH_CHANNEL_OPEN &&
- c->lastused != 0 &&
- c->inactive_deadline != 0) {
+ channel_get_expiry(ssh, c) != 0) {
ptimeout_deadline_monotime(timeout,
- c->lastused + c->inactive_deadline);
+ channel_get_expiry(ssh, c));
}
} else if (timeout != NULL) {
/*
@@ -3558,7 +3598,7 @@ channel_input_open_confirmation(int type, u_int32_t seq, struct ssh *ssh)
c->open_confirm(ssh, c->self, 1, c->open_confirm_ctx);
debug2_f("channel %d: callback done", c->self);
}
- c->lastused = monotime();
+ channel_set_used_time(ssh, c);
debug2("channel %d: open confirm rwindow %u rmax %u", c->self,
c->remote_window, c->remote_maxpacket);
return 0;
diff --git a/ssh_config.5 b/ssh_config.5
index 4bbdfefd..15ad012f 100644
--- a/ssh_config.5
+++ b/ssh_config.5
@@ -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_config.5,v 1.391 2023/10/12 02:18:18 djm Exp $
-.Dd $Mdocdate: October 12 2023 $
+.\" $OpenBSD: ssh_config.5,v 1.392 2024/01/09 22:19:00 djm Exp $
+.Dd $Mdocdate: January 9 2024 $
.Dt SSH_CONFIG 5
.Os
.Sh NAME
@@ -463,8 +463,10 @@ 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.
+must be the special keyword
+.Dq global
+or a channel type name from the list below, optionally containing
+wildcard characters.
.Pp
The timeout value
.Dq interval
@@ -473,11 +475,19 @@ is specified in seconds or may use any of the units documented in the
section.
For example,
.Dq session=5m
-would cause the interactive session to terminate after five minutes of
+would cause interactive sessions to terminate after five minutes of
inactivity.
Specifying a zero value disables the inactivity timeout.
.Pp
-The available channel types include:
+The special timeout
+.Dq global
+Applies to all active channels, taken together.
+Traffic on any active channel will reset the timeout, but when the timeout
+expires then all open channels will be closed.
+Note that this global timeout is not matched by wildcards and must be
+specified explicitly.
+.Pp
+The available channel type names include:
.Bl -tag -width Ds
.It Cm agent-connection
Open connections to
diff --git a/sshd_config.5 b/sshd_config.5
index 7e1a56cd..ca5eeb59 100644
--- a/sshd_config.5
+++ b/sshd_config.5
@@ -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: sshd_config.5,v 1.350 2023/07/28 05:42:36 jmc Exp $
-.Dd $Mdocdate: July 28 2023 $
+.\" $OpenBSD: sshd_config.5,v 1.351 2024/01/09 22:19:00 djm Exp $
+.Dd $Mdocdate: January 9 2024 $
.Dt SSHD_CONFIG 5
.Os
.Sh NAME
@@ -409,8 +409,10 @@ 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.
+must be the special keyword
+.Dq global
+or a channel type name from the list below, optionally containing
+wildcard characters.
.Pp
The timeout value
.Dq interval
@@ -418,11 +420,20 @@ 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 all sessions to terminate after five minutes of inactivity.
+.Dq session=5m
+would cause interactive sessions to terminate after five minutes of
+inactivity.
Specifying a zero value disables the inactivity timeout.
.Pp
-The available channel types include:
+The special timeout
+.Dq global
+Applies to all active channels, taken together.
+Traffic on any active channel will reset the timeout, but when the timeout
+expires then all open channels will be closed.
+Note that this global timeout is not matched by wildcards and must be
+specified explicitly.
+.Pp
+The available channel type names include:
.Bl -tag -width Ds
.It Cm agent-connection
Open connections to
@@ -443,15 +454,15 @@ listening on behalf of a
.Xr ssh 1
remote forwarding, i.e.\&
.Cm RemoteForward .
-.It Cm session:command
-Command execution sessions.
-.It Cm session:shell
-Interactive shell sessions.
-.It Cm session:subsystem:...
-Subsystem sessions, e.g. for
+.It Cm session
+The interactive main session, including shell session, command execution,
+.Xr scp 1 ,
.Xr sftp 1 ,
-which could be identified as
-.Cm session:subsystem:sftp .
+etc.
+.It Cm tun-connection
+Open
+.Cm TunnelForward
+connections.
.It Cm x11-connection
Open X11 forwarding sessions.
.El
@@ -465,9 +476,6 @@ 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.
-See also
-.Cm UnusedConnectionTimeout ,
-which may be used in conjunction with this option.
.Pp
The default is not to expire channels of any type for inactivity.
.It Cm ChrootDirectory

--
To stop receiving notification emails like this one, please contact
djm@mindrot.org.
_______________________________________________
openssh-commits mailing list
openssh-commits@mindrot.org
https://lists.mindrot.org/mailman/listinfo/openssh-commits