From 96e2a330b86f1b2ffd31a70a9bb9565cace0d68f Mon Sep 17 00:00:00 2001 From: 21pages Date: Fri, 17 Jul 2026 15:52:49 +0800 Subject: [PATCH] restrict switch sides to remote desktop sessions (#15610) * fix: restrict switch sides to remote desktop sessions Reject switch sides requests outside authenticated remote desktop sessions, and reject switch sides responses that try to carry non-remote login types. Add scope coverage so file transfer, terminal, view camera, and port forward sessions cannot use switch sides. Signed-off-by: 21pages * fix review: consume switch sides UUID before rejecting response Signed-off-by: 21pages --------- Signed-off-by: 21pages --- src/server/connection.rs | 31 ++++++++++++++++++++++++++++++- 1 file changed, 30 insertions(+), 1 deletion(-) diff --git a/src/server/connection.rs b/src/server/connection.rs index bdf9cba8b..409d0e9fd 100644 --- a/src/server/connection.rs +++ b/src/server/connection.rs @@ -2807,7 +2807,6 @@ impl Connection { #[cfg(feature = "flutter")] #[cfg(not(any(target_os = "android", target_os = "ios")))] if let Some(lr) = _s.lr.clone().take() { - self.handle_login_request_without_validation(&lr).await; SWITCH_SIDES_UUID .lock() .unwrap() @@ -2816,6 +2815,15 @@ impl Connection { if let Ok(uuid) = uuid::Uuid::from_slice(_s.uuid.to_vec().as_ref()) { if let Some((_instant, uuid_old)) = uuid_old { if uuid == uuid_old { + if lr.union.is_some() { + log::warn!( + "Rejected switch sides response for non-remote-desktop session; closing connection" + ); + self.send_login_error("Connection not allowed").await; + return false; + } + self.reset_session_scope_for_login(); + self.handle_login_request_without_validation(&lr).await; self.from_switch = true; self.set_conn_audit_primary_auth(ConnAuditPrimaryAuth::SwitchSides); if !self.send_logon_response_and_keep_alive().await { @@ -5656,6 +5664,7 @@ impl Connection { Some(misc::Union::ChangeDisplayResolution(_)) => "misc.change_display_resolution", Some(misc::Union::MessageQuery(_)) => "misc.message_query", Some(misc::Union::FollowCurrentDisplay(_)) => "misc.follow_current_display", + Some(misc::Union::SwitchSidesRequest(_)) => "misc.switch_sides_request", Some(_) => "misc.other", None => "misc.empty", } @@ -6773,6 +6782,10 @@ mod test { misc_msg(|m| m.set_capture_displays(CaptureDisplays::new())), Some("misc.capture_displays"), ), + ( + misc_msg(|m| m.set_switch_sides_request(SwitchSidesRequest::new())), + Some("misc.switch_sides_request"), + ), (msg(|m| m.set_clipboard(Clipboard::new())), None), ( msg(|m| m.set_multi_clipboards(MultiClipboards::new())), @@ -6817,6 +6830,10 @@ mod test { misc_msg(|m| m.set_toggle_privacy_mode(TogglePrivacyMode::new())), Some("misc.toggle_privacy_mode"), ), + ( + misc_msg(|m| m.set_switch_sides_request(SwitchSidesRequest::new())), + Some("misc.switch_sides_request"), + ), (misc_msg(|m| m.set_chat_message(ChatMessage::new())), None), (msg(|m| m.set_clipboard(Clipboard::new())), None), ( @@ -6902,6 +6919,10 @@ mod test { msg(|m| m.set_terminal_action(TerminalAction::new())), Some("terminal_action"), ), + ( + misc_msg(|m| m.set_switch_sides_request(SwitchSidesRequest::new())), + Some("misc.switch_sides_request"), + ), ], ), ( @@ -6912,6 +6933,10 @@ mod test { None, ), (msg(|m| m.set_terminal_action(TerminalAction::new())), None), + ( + misc_msg(|m| m.set_switch_sides_request(SwitchSidesRequest::new())), + None, + ), ], ), ( @@ -6931,6 +6956,10 @@ mod test { msg(|m| m.set_screenshot_request(ScreenshotRequest::new())), Some("screenshot_request"), ), + ( + misc_msg(|m| m.set_switch_sides_request(SwitchSidesRequest::new())), + Some("misc.switch_sides_request"), + ), (misc_msg(|m| m.set_refresh_video(true)), None), (misc_msg(|m| m.set_refresh_video_display(0)), None), (