-/* $OpenBSD: servconf.c,v 1.389 2023/01/06 02:47:18 djm Exp $ */
+/* $OpenBSD: servconf.c,v 1.390 2023/01/17 09:44:48 djm Exp $ */
/*
* Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
* All rights reserved
options->required_rsa_size = -1;
options->channel_timeouts = NULL;
options->num_channel_timeouts = 0;
+ options->unused_connection_timeout = -1;
}
/* Returns 1 if a string option is unset or set to "none" or 0 otherwise. */
options->sk_provider = xstrdup("internal");
if (options->required_rsa_size == -1)
options->required_rsa_size = SSH_RSA_MINIMUM_MODULUS_SIZE;
+ if (options->unused_connection_timeout == -1)
+ options->unused_connection_timeout = 0;
assemble_algorithms(options);
sStreamLocalBindMask, sStreamLocalBindUnlink,
sAllowStreamLocalForwarding, sFingerprintHash, sDisableForwarding,
sExposeAuthInfo, sRDomain, sPubkeyAuthOptions, sSecurityKeyProvider,
- sRequiredRSASize, sChannelTimeout,
+ sRequiredRSASize, sChannelTimeout, sUnusedConnectionTimeout,
sDeprecated, sIgnore, sUnsupported
} ServerOpCodes;
{ "securitykeyprovider", sSecurityKeyProvider, SSHCFG_GLOBAL },
{ "requiredrsasize", sRequiredRSASize, SSHCFG_ALL },
{ "channeltimeout", sChannelTimeout, SSHCFG_ALL },
+ { "unusedconnectiontimeout", sUnusedConnectionTimeout, SSHCFG_ALL },
{ NULL, sBadOption, 0 }
};
}
break;
+ case sUnusedConnectionTimeout:
+ intptr = &options->unused_connection_timeout;
+ /* peek at first arg for "none" so we can reuse parse_time */
+ if (av[0] != NULL && strcasecmp(av[0], "none") == 0) {
+ (void)argv_next(&ac, &av); /* consume arg */
+ if (*activep)
+ *intptr = 0;
+ break;
+ }
+ goto parse_time;
+
case sDeprecated:
case sIgnore:
case sUnsupported:
M_CP_INTOPT(rekey_interval);
M_CP_INTOPT(log_level);
M_CP_INTOPT(required_rsa_size);
+ M_CP_INTOPT(unused_connection_timeout);
/*
* The bind_mask is a mode_t that may be unsigned, so we can't use
static void
dump_cfg_int(ServerOpCodes code, int val)
{
+ if (code == sUnusedConnectionTimeout && val == 0) {
+ printf("%s none\n", lookup_opcode_name(code));
+ return;
+ }
printf("%s %d\n", lookup_opcode_name(code), val);
}
dump_cfg_int(sClientAliveCountMax, o->client_alive_count_max);
dump_cfg_int(sRequiredRSASize, o->required_rsa_size);
dump_cfg_oct(sStreamLocalBindMask, o->fwd_opts.streamlocal_bind_mask);
+ dump_cfg_int(sUnusedConnectionTimeout, o->unused_connection_timeout);
/* formatted integer arguments */
dump_cfg_fmtint(sPermitRootLogin, o->permit_root_login);
-/* $OpenBSD: servconf.h,v 1.158 2023/01/06 02:47:19 djm Exp $ */
+/* $OpenBSD: servconf.h,v 1.159 2023/01/17 09:44:48 djm Exp $ */
/*
* Author: Tatu Ylonen <ylo@cs.hut.fi>
char **channel_timeouts; /* inactivity timeout by channel type */
u_int num_channel_timeouts;
+
+ int unused_connection_timeout;
} ServerOptions;
/* Information about the incoming connection as used by Match */
-/* $OpenBSD: serverloop.c,v 1.233 2023/01/06 02:38:23 djm Exp $ */
+/* $OpenBSD: serverloop.c,v 1.234 2023/01/17 09:44:48 djm Exp $ */
/*
* Author: Tatu Ylonen <ylo@cs.hut.fi>
* Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
int *conn_in_readyp, int *conn_out_readyp)
{
struct timespec timeout;
+ char remote_id[512];
int ret;
int client_alive_scheduled = 0;
u_int p;
- /* time we last heard from the client OR sent a keepalive */
- static time_t last_client_time;
+ time_t now;
+ static time_t last_client_time, unused_connection_expiry;
*conn_in_readyp = *conn_out_readyp = 0;
/* Prepare channel poll. First two pollfd entries are reserved */
ptimeout_init(&timeout);
channel_prepare_poll(ssh, pfdp, npfd_allocp, npfd_activep, 2, &timeout);
+ now = monotime();
if (*npfd_activep < 2)
fatal_f("bad npfd %u", *npfd_activep); /* shouldn't happen */
if (options.rekey_interval > 0 && !ssh_packet_is_rekeying(ssh)) {
ssh_packet_get_rekey_timeout(ssh));
}
+ /*
+ * If no channels are open and UnusedConnectionTimeout is set, then
+ * start the clock to terminate the connection.
+ */
+ if (options.unused_connection_timeout != 0) {
+ if (channel_still_open(ssh) || unused_connection_expiry == 0) {
+ unused_connection_expiry = now +
+ options.unused_connection_timeout;
+ }
+ ptimeout_deadline_monotime(&timeout, unused_connection_expiry);
+ }
+
/*
* if using client_alive, set the max timeout accordingly,
* and indicate that this particular timeout was for client
* analysis more difficult, but we're not doing it yet.
*/
if (options.client_alive_interval) {
+ /* Time we last heard from the client OR sent a keepalive */
if (last_client_time == 0)
- last_client_time = monotime();
+ last_client_time = now;
ptimeout_deadline_sec(&timeout, options.client_alive_interval);
/* XXX ? deadline_monotime(last_client_time + alive_interval) */
client_alive_scheduled = 1;
*conn_in_readyp = (*pfdp)[0].revents != 0;
*conn_out_readyp = (*pfdp)[1].revents != 0;
+ now = monotime(); /* need to reset after ppoll() */
/* ClientAliveInterval probing */
if (client_alive_scheduled) {
- time_t now = monotime();
if (ret == 0 &&
now > last_client_time + options.client_alive_interval) {
/* ppoll timed out and we're due to probe */
last_client_time = now;
}
}
+
+ /* UnusedConnectionTimeout handling */
+ if (unused_connection_expiry != 0 &&
+ now > unused_connection_expiry && !channel_still_open(ssh)) {
+ sshpkt_fmt_connection_id(ssh, remote_id, sizeof(remote_id));
+ logit("terminating inactive connection from %s", remote_id);
+ cleanup_exit(255);
+ }
}
/*
.\" (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.345 2023/01/06 08:44:11 jmc Exp $
-.Dd $Mdocdate: January 6 2023 $
+.\" $OpenBSD: sshd_config.5,v 1.346 2023/01/17 09:44:48 djm Exp $
+.Dd $Mdocdate: January 17 2023 $
.Dt SSHD_CONFIG 5
.Os
.Sh NAME
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
.Cm AuthorizedPrincipalsFile ,
.Cm Banner ,
.Cm CASignatureAlgorithms ,
+.Cm ChannelTimeout ,
.Cm ChrootDirectory ,
.Cm ClientAliveCountMax ,
.Cm ClientAliveInterval ,
.Cm StreamLocalBindMask ,
.Cm StreamLocalBindUnlink ,
.Cm TrustedUserCAKeys ,
+.Cm UnusedConnectionTimeout ,
.Cm X11DisplayOffset ,
.Cm X11Forwarding
and
.Cm TrustedUserCAKeys .
For more details on certificates, see the CERTIFICATES section in
.Xr ssh-keygen 1 .
+.It Cm UnusedConnectionTimeout
+Specifies whether and how quickly
+.Xr sshd 8
+should close client connections with no open channels.
+Open channels include active shell, command execution or subsystem
+sessions, connected network, socket, agent of X11 forwardings.
+Forwarding listeners, such as those from the
+.Xr ssh 1
+.Fl R
+flag are not considered as open channels and do not prevent the timeout.
+The timeout value
+is specified in seconds or may use any of the units documented in the
+.Sx TIME FORMATS
+section.
+.Pp
+Note that this timeout starts when the client connection completes
+user authentication but before the client has an opportunity to open any
+channels.
+Caution should be used when using short timeout values, as they may not
+provide sufficient time for the client to request and open its channels
+before terminating the connection.
+.Pp
+The default
+.Cm none
+is to never expire connections for having no open channels.
+This option may be useful in conjunction with
+.Cm ChannelTimeout .
.It Cm UseDNS
Specifies whether
.Xr sshd 8