From cc00fb4c06a23a6fc673e0d254c101df2ad01423 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adrian=20M=C3=BCller?= Date: Thu, 28 Sep 2023 19:37:47 +0200 Subject: [PATCH 1/2] Enforce client timeout --- russh/src/client/mod.rs | 10 +++++++++- russh/src/server/mod.rs | 8 -------- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/russh/src/client/mod.rs b/russh/src/client/mod.rs index 05b9c9c7..075bb52d 100644 --- a/russh/src/client/mod.rs +++ b/russh/src/client/mod.rs @@ -103,7 +103,9 @@ use crate::key::PubKey; use crate::session::{CommonSession, EncryptedState, Exchange, Kex, KexDhDone, KexInit, NewKeys}; use crate::ssh_read::SshRead; use crate::sshbuffer::{SSHBuffer, SshId}; -use crate::{auth, msg, negotiation, ChannelId, ChannelOpenFailure, Disconnect, Limits, Sig}; +use crate::{ + auth, msg, negotiation, timeout, ChannelId, ChannelOpenFailure, Disconnect, Limits, Sig, +}; mod encrypted; mod kex; @@ -772,6 +774,8 @@ impl Session { pin!(reading); pin!(time_for_keepalive); + let delay = self.common.config.inactivity_timeout; + #[allow(clippy::panic)] // false positive in select! macro while !self.common.disconnected { tokio::select! { @@ -852,6 +856,10 @@ impl Session { } } } + _ = timeout(delay) => { + debug!("timeout"); + break + }, } self.flush()?; if !self.common.write_buffer.buffer.is_empty() { diff --git a/russh/src/server/mod.rs b/russh/src/server/mod.rs index 1e627e91..a0118080 100644 --- a/russh/src/server/mod.rs +++ b/russh/src/server/mod.rs @@ -650,14 +650,6 @@ thread_local! { static B2: RefCell = RefCell::new(CryptoVec::new()); } -pub(crate) async fn timeout(delay: Option) { - if let Some(delay) = delay { - tokio::time::sleep(delay).await - } else { - futures::future::pending().await - }; -} - async fn start_reading( mut stream_read: R, mut buffer: SSHBuffer, From f18c0f983d49690b14325af2f76553697162931c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adrian=20M=C3=BCller?= Date: Sat, 30 Sep 2023 11:05:46 +0200 Subject: [PATCH 2/2] Close Channels on Disconnect --- russh/src/client/mod.rs | 7 ++++--- russh/src/lib.rs | 8 ++++++++ russh/src/server/encrypted.rs | 4 ++-- 3 files changed, 14 insertions(+), 5 deletions(-) diff --git a/russh/src/client/mod.rs b/russh/src/client/mod.rs index 075bb52d..ddbd4b8a 100644 --- a/russh/src/client/mod.rs +++ b/russh/src/client/mod.rs @@ -882,9 +882,10 @@ impl Session { } } debug!("disconnected"); - if self.common.disconnected { - stream_write.shutdown().await.map_err(crate::Error::from)?; - } + self.receiver.close(); + self.inbound_channel_receiver.close(); + stream_write.shutdown().await.map_err(crate::Error::from)?; + Ok(()) } diff --git a/russh/src/lib.rs b/russh/src/lib.rs index 7594eced..a9eed8bf 100644 --- a/russh/src/lib.rs +++ b/russh/src/lib.rs @@ -465,3 +465,11 @@ impl ChannelParams { self.confirmed = true; } } + +pub(crate) async fn timeout(delay: Option) { + if let Some(delay) = delay { + tokio::time::sleep(delay).await + } else { + futures::future::pending().await + }; +} diff --git a/russh/src/server/encrypted.rs b/russh/src/server/encrypted.rs index e1122781..216aafed 100644 --- a/russh/src/server/encrypted.rs +++ b/russh/src/server/encrypted.rs @@ -46,7 +46,7 @@ impl Session { // Either this packet is a KEXINIT, in which case we start a key re-exchange. #[allow(clippy::unwrap_used)] - let mut enc = self.common.encrypted.as_mut().unwrap(); + let enc = self.common.encrypted.as_mut().unwrap(); if buf.first() == Some(&msg::KEXINIT) { debug!("Received rekeying request"); // If we're not currently rekeying, but `buf` is a rekey request @@ -142,7 +142,7 @@ impl Session { }; #[allow(clippy::unwrap_used)] - let mut enc = self.common.encrypted.as_mut().unwrap(); + let enc = self.common.encrypted.as_mut().unwrap(); // If we've successfully read a packet. match enc.state { EncryptedState::WaitingAuthServiceRequest {