Skip to content

Commit 0293c19

Browse files
committed
upstream: Add a sshd_config UnusedConnectionTimeout option to terminate
client connections that have no open channels for some length of time. This complements the recently-added ChannelTimeout option that terminates inactive channels after a timeout. ok markus@ OpenBSD-Commit-ID: ca983be74c0350364c11f8ba3bd692f6f24f5da9
1 parent 8ec2e31 commit 0293c19

File tree

4 files changed

+88
-10
lines changed

4 files changed

+88
-10
lines changed

servconf.c

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11

2-
/* $OpenBSD: servconf.c,v 1.389 2023/01/06 02:47:18 djm Exp $ */
2+
/* $OpenBSD: servconf.c,v 1.390 2023/01/17 09:44:48 djm Exp $ */
33
/*
44
* Copyright (c) 1995 Tatu Ylonen <[email protected]>, Espoo, Finland
55
* All rights reserved
@@ -198,6 +198,7 @@ initialize_server_options(ServerOptions *options)
198198
options->required_rsa_size = -1;
199199
options->channel_timeouts = NULL;
200200
options->num_channel_timeouts = 0;
201+
options->unused_connection_timeout = -1;
201202
}
202203

203204
/* Returns 1 if a string option is unset or set to "none" or 0 otherwise. */
@@ -446,6 +447,8 @@ fill_default_server_options(ServerOptions *options)
446447
options->sk_provider = xstrdup("internal");
447448
if (options->required_rsa_size == -1)
448449
options->required_rsa_size = SSH_RSA_MINIMUM_MODULUS_SIZE;
450+
if (options->unused_connection_timeout == -1)
451+
options->unused_connection_timeout = 0;
449452

450453
assemble_algorithms(options);
451454

@@ -529,7 +532,7 @@ typedef enum {
529532
sStreamLocalBindMask, sStreamLocalBindUnlink,
530533
sAllowStreamLocalForwarding, sFingerprintHash, sDisableForwarding,
531534
sExposeAuthInfo, sRDomain, sPubkeyAuthOptions, sSecurityKeyProvider,
532-
sRequiredRSASize, sChannelTimeout,
535+
sRequiredRSASize, sChannelTimeout, sUnusedConnectionTimeout,
533536
sDeprecated, sIgnore, sUnsupported
534537
} ServerOpCodes;
535538

@@ -691,6 +694,7 @@ static struct {
691694
{ "securitykeyprovider", sSecurityKeyProvider, SSHCFG_GLOBAL },
692695
{ "requiredrsasize", sRequiredRSASize, SSHCFG_ALL },
693696
{ "channeltimeout", sChannelTimeout, SSHCFG_ALL },
697+
{ "unusedconnectiontimeout", sUnusedConnectionTimeout, SSHCFG_ALL },
694698
{ NULL, sBadOption, 0 }
695699
};
696700

@@ -2537,6 +2541,17 @@ process_server_config_line_depth(ServerOptions *options, char *line,
25372541
}
25382542
break;
25392543

2544+
case sUnusedConnectionTimeout:
2545+
intptr = &options->unused_connection_timeout;
2546+
/* peek at first arg for "none" so we can reuse parse_time */
2547+
if (av[0] != NULL && strcasecmp(av[0], "none") == 0) {
2548+
(void)argv_next(&ac, &av); /* consume arg */
2549+
if (*activep)
2550+
*intptr = 0;
2551+
break;
2552+
}
2553+
goto parse_time;
2554+
25402555
case sDeprecated:
25412556
case sIgnore:
25422557
case sUnsupported:
@@ -2709,6 +2724,7 @@ copy_set_server_options(ServerOptions *dst, ServerOptions *src, int preauth)
27092724
M_CP_INTOPT(rekey_interval);
27102725
M_CP_INTOPT(log_level);
27112726
M_CP_INTOPT(required_rsa_size);
2727+
M_CP_INTOPT(unused_connection_timeout);
27122728

27132729
/*
27142730
* The bind_mask is a mode_t that may be unsigned, so we can't use
@@ -2861,6 +2877,10 @@ fmt_intarg(ServerOpCodes code, int val)
28612877
static void
28622878
dump_cfg_int(ServerOpCodes code, int val)
28632879
{
2880+
if (code == sUnusedConnectionTimeout && val == 0) {
2881+
printf("%s none\n", lookup_opcode_name(code));
2882+
return;
2883+
}
28642884
printf("%s %d\n", lookup_opcode_name(code), val);
28652885
}
28662886

@@ -2977,6 +2997,7 @@ dump_config(ServerOptions *o)
29772997
dump_cfg_int(sClientAliveCountMax, o->client_alive_count_max);
29782998
dump_cfg_int(sRequiredRSASize, o->required_rsa_size);
29792999
dump_cfg_oct(sStreamLocalBindMask, o->fwd_opts.streamlocal_bind_mask);
3000+
dump_cfg_int(sUnusedConnectionTimeout, o->unused_connection_timeout);
29803001

29813002
/* formatted integer arguments */
29823003
dump_cfg_fmtint(sPermitRootLogin, o->permit_root_login);

servconf.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
/* $OpenBSD: servconf.h,v 1.158 2023/01/06 02:47:19 djm Exp $ */
1+
/* $OpenBSD: servconf.h,v 1.159 2023/01/17 09:44:48 djm Exp $ */
22

33
/*
44
* Author: Tatu Ylonen <[email protected]>
@@ -233,6 +233,8 @@ typedef struct {
233233

234234
char **channel_timeouts; /* inactivity timeout by channel type */
235235
u_int num_channel_timeouts;
236+
237+
int unused_connection_timeout;
236238
} ServerOptions;
237239

238240
/* Information about the incoming connection as used by Match */

serverloop.c

Lines changed: 28 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
/* $OpenBSD: serverloop.c,v 1.233 2023/01/06 02:38:23 djm Exp $ */
1+
/* $OpenBSD: serverloop.c,v 1.234 2023/01/17 09:44:48 djm Exp $ */
22
/*
33
* Author: Tatu Ylonen <[email protected]>
44
* Copyright (c) 1995 Tatu Ylonen <[email protected]>, Espoo, Finland
@@ -172,24 +172,38 @@ wait_until_can_do_something(struct ssh *ssh,
172172
int *conn_in_readyp, int *conn_out_readyp)
173173
{
174174
struct timespec timeout;
175+
char remote_id[512];
175176
int ret;
176177
int client_alive_scheduled = 0;
177178
u_int p;
178-
/* time we last heard from the client OR sent a keepalive */
179-
static time_t last_client_time;
179+
time_t now;
180+
static time_t last_client_time, unused_connection_expiry;
180181

181182
*conn_in_readyp = *conn_out_readyp = 0;
182183

183184
/* Prepare channel poll. First two pollfd entries are reserved */
184185
ptimeout_init(&timeout);
185186
channel_prepare_poll(ssh, pfdp, npfd_allocp, npfd_activep, 2, &timeout);
187+
now = monotime();
186188
if (*npfd_activep < 2)
187189
fatal_f("bad npfd %u", *npfd_activep); /* shouldn't happen */
188190
if (options.rekey_interval > 0 && !ssh_packet_is_rekeying(ssh)) {
189191
ptimeout_deadline_sec(&timeout,
190192
ssh_packet_get_rekey_timeout(ssh));
191193
}
192194

195+
/*
196+
* If no channels are open and UnusedConnectionTimeout is set, then
197+
* start the clock to terminate the connection.
198+
*/
199+
if (options.unused_connection_timeout != 0) {
200+
if (channel_still_open(ssh) || unused_connection_expiry == 0) {
201+
unused_connection_expiry = now +
202+
options.unused_connection_timeout;
203+
}
204+
ptimeout_deadline_monotime(&timeout, unused_connection_expiry);
205+
}
206+
193207
/*
194208
* if using client_alive, set the max timeout accordingly,
195209
* and indicate that this particular timeout was for client
@@ -199,8 +213,9 @@ wait_until_can_do_something(struct ssh *ssh,
199213
* analysis more difficult, but we're not doing it yet.
200214
*/
201215
if (options.client_alive_interval) {
216+
/* Time we last heard from the client OR sent a keepalive */
202217
if (last_client_time == 0)
203-
last_client_time = monotime();
218+
last_client_time = now;
204219
ptimeout_deadline_sec(&timeout, options.client_alive_interval);
205220
/* XXX ? deadline_monotime(last_client_time + alive_interval) */
206221
client_alive_scheduled = 1;
@@ -237,9 +252,9 @@ wait_until_can_do_something(struct ssh *ssh,
237252
*conn_in_readyp = (*pfdp)[0].revents != 0;
238253
*conn_out_readyp = (*pfdp)[1].revents != 0;
239254

255+
now = monotime(); /* need to reset after ppoll() */
240256
/* ClientAliveInterval probing */
241257
if (client_alive_scheduled) {
242-
time_t now = monotime();
243258
if (ret == 0 &&
244259
now > last_client_time + options.client_alive_interval) {
245260
/* ppoll timed out and we're due to probe */
@@ -250,6 +265,14 @@ wait_until_can_do_something(struct ssh *ssh,
250265
last_client_time = now;
251266
}
252267
}
268+
269+
/* UnusedConnectionTimeout handling */
270+
if (unused_connection_expiry != 0 &&
271+
now > unused_connection_expiry && !channel_still_open(ssh)) {
272+
sshpkt_fmt_connection_id(ssh, remote_id, sizeof(remote_id));
273+
logit("terminating inactive connection from %s", remote_id);
274+
cleanup_exit(255);
275+
}
253276
}
254277

255278
/*

sshd_config.5

Lines changed: 34 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,8 +33,8 @@
3333
.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
3434
.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
3535
.\"
36-
.\" $OpenBSD: sshd_config.5,v 1.345 2023/01/06 08:44:11 jmc Exp $
37-
.Dd $Mdocdate: January 6 2023 $
36+
.\" $OpenBSD: sshd_config.5,v 1.346 2023/01/17 09:44:48 djm Exp $
37+
.Dd $Mdocdate: January 17 2023 $
3838
.Dt SSHD_CONFIG 5
3939
.Os
4040
.Sh NAME
@@ -459,6 +459,9 @@ close the SSH connection, nor does it prevent a client from
459459
requesting another channel of the same type.
460460
In particular, expiring an inactive forwarding session does not prevent
461461
another identical forwarding from being subsequently created.
462+
See also
463+
.Cm UnusedConnectionTimeout ,
464+
which may be used in conjunction with this option.
462465
.Pp
463466
The default is not to expire channels of any type for inactivity.
464467
.It Cm ChrootDirectory
@@ -1256,6 +1259,7 @@ Available keywords are
12561259
.Cm AuthorizedPrincipalsFile ,
12571260
.Cm Banner ,
12581261
.Cm CASignatureAlgorithms ,
1262+
.Cm ChannelTimeout ,
12591263
.Cm ChrootDirectory ,
12601264
.Cm ClientAliveCountMax ,
12611265
.Cm ClientAliveInterval ,
@@ -1295,6 +1299,7 @@ Available keywords are
12951299
.Cm StreamLocalBindMask ,
12961300
.Cm StreamLocalBindUnlink ,
12971301
.Cm TrustedUserCAKeys ,
1302+
.Cm UnusedConnectionTimeout ,
12981303
.Cm X11DisplayOffset ,
12991304
.Cm X11Forwarding
13001305
and
@@ -1811,6 +1816,33 @@ for authentication using
18111816
.Cm TrustedUserCAKeys .
18121817
For more details on certificates, see the CERTIFICATES section in
18131818
.Xr ssh-keygen 1 .
1819+
.It Cm UnusedConnectionTimeout
1820+
Specifies whether and how quickly
1821+
.Xr sshd 8
1822+
should close client connections with no open channels.
1823+
Open channels include active shell, command execution or subsystem
1824+
sessions, connected network, socket, agent of X11 forwardings.
1825+
Forwarding listeners, such as those from the
1826+
.Xr ssh 1
1827+
.Fl R
1828+
flag are not considered as open channels and do not prevent the timeout.
1829+
The timeout value
1830+
is specified in seconds or may use any of the units documented in the
1831+
.Sx TIME FORMATS
1832+
section.
1833+
.Pp
1834+
Note that this timeout starts when the client connection completes
1835+
user authentication but before the client has an opportunity to open any
1836+
channels.
1837+
Caution should be used when using short timeout values, as they may not
1838+
provide sufficient time for the client to request and open its channels
1839+
before terminating the connection.
1840+
.Pp
1841+
The default
1842+
.Cm none
1843+
is to never expire connections for having no open channels.
1844+
This option may be useful in conjunction with
1845+
.Cm ChannelTimeout .
18141846
.It Cm UseDNS
18151847
Specifies whether
18161848
.Xr sshd 8

0 commit comments

Comments
 (0)