From f11b23346309e4d5138e733a49321aedd6eeaa2f Mon Sep 17 00:00:00 2001 From: "dtucker@openbsd.org" Date: Fri, 3 Jul 2020 05:09:06 +0000 Subject: [PATCH] upstream: Only reset the serveralive check when we receive traffic from the server and ignore traffic from a port forwarding client, preventing a client from keeping a connection alive when it should be terminated. Based on a patch from jxraynor at gmail.com via openssh-unix-dev and bz#2265, ok djm@ OpenBSD-Commit-ID: a941a575a5cbc244c0ef5d7abd0422bbf02c2dcd --- clientloop.c | 36 ++++++++++++++++++++++-------------- 1 file changed, 22 insertions(+), 14 deletions(-) diff --git a/clientloop.c b/clientloop.c index da396c72a..c82b084da 100644 --- a/clientloop.c +++ b/clientloop.c @@ -1,4 +1,4 @@ -/* $OpenBSD: clientloop.c,v 1.344 2020/04/24 02:19:40 dtucker Exp $ */ +/* $OpenBSD: clientloop.c,v 1.345 2020/07/03 05:09:06 dtucker Exp $ */ /* * Author: Tatu Ylonen * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland @@ -162,6 +162,7 @@ static int connection_out; /* Connection to server (output). */ static int need_rekeying; /* Set to non-zero if rekeying is requested. */ static int session_closed; /* In SSH2: login session closed. */ static u_int x11_refuse_time; /* If >0, refuse x11 opens after this time. */ +static time_t server_alive_time; /* Time to do server_alive_check */ static void client_init_dispatch(struct ssh *ssh); int session_ident = -1; @@ -466,6 +467,13 @@ client_global_request_reply(int type, u_int32_t seq, struct ssh *ssh) return 0; } +static void +schedule_server_alive_check(void) +{ + if (options.server_alive_interval > 0) + server_alive_time = monotime() + options.server_alive_interval; +} + static void server_alive_check(struct ssh *ssh) { @@ -482,6 +490,7 @@ server_alive_check(struct ssh *ssh) fatal("%s: send packet: %s", __func__, ssh_err(r)); /* Insert an empty placeholder to maintain ordering */ client_register_global_confirm(NULL, NULL); + schedule_server_alive_check(); } /* @@ -495,7 +504,7 @@ client_wait_until_can_do_something(struct ssh *ssh, { struct timeval tv, *tvp; int timeout_secs; - time_t minwait_secs = 0, server_alive_time = 0, now = monotime(); + time_t minwait_secs = 0, now = monotime(); int r, ret; /* Add any selections by the channel mechanism. */ @@ -524,10 +533,8 @@ client_wait_until_can_do_something(struct ssh *ssh, */ timeout_secs = INT_MAX; /* we use INT_MAX to mean no timeout */ - if (options.server_alive_interval > 0) { - timeout_secs = options.server_alive_interval; - server_alive_time = now + options.server_alive_interval; - } + if (options.server_alive_interval > 0) + timeout_secs = MAXIMUM(server_alive_time - now, 0); if (options.rekey_interval > 0 && !rekeying) timeout_secs = MINIMUM(timeout_secs, ssh_packet_get_rekey_timeout(ssh)); @@ -557,7 +564,6 @@ client_wait_until_can_do_something(struct ssh *ssh, */ memset(*readsetp, 0, *nallocp); memset(*writesetp, 0, *nallocp); - if (errno == EINTR) return; /* Note: we might still have data in the buffers. */ @@ -565,15 +571,14 @@ client_wait_until_can_do_something(struct ssh *ssh, "select: %s\r\n", strerror(errno))) != 0) fatal("%s: buffer error: %s", __func__, ssh_err(r)); quit_pending = 1; - } else if (ret == 0) { + } else if (options.server_alive_interval > 0 && !FD_ISSET(connection_in, + *readsetp) && monotime() >= server_alive_time) /* - * Timeout. Could have been either keepalive or rekeying. - * Keepalive we check here, rekeying is checked in clientloop. + * ServerAlive check is needed. We can't rely on the select + * timing out since traffic on the client side such as port + * forwards can keep waking it up. */ - if (server_alive_time != 0 && server_alive_time <= monotime()) - server_alive_check(ssh); - } - + server_alive_check(ssh); } static void @@ -613,6 +618,7 @@ client_process_net_input(struct ssh *ssh, fd_set *readset) * the packet subsystem. */ if (FD_ISSET(connection_in, readset)) { + schedule_server_alive_check(); /* Read as much as possible. */ len = read(connection_in, buf, sizeof(buf)); if (len == 0) { @@ -1314,6 +1320,8 @@ client_loop(struct ssh *ssh, int have_pty, int escape_char_arg, client_channel_closed, 0); } + schedule_server_alive_check(); + /* Main loop of the client for the interactive session mode. */ while (!quit_pending) {