From f299fb9906006247863250d7dfaa016f29eef7a4 Mon Sep 17 00:00:00 2001 From: RustDesk <71636191+rustdesk@users.noreply.github.com> Date: Thu, 24 Sep 2026 21:10:54 +0800 Subject: [PATCH] =?UTF-8?q?fix(clipboard):=20unix,=20check=20file=20transf?= =?UTF-8?q?er=20permission=20on=20incoming=20file=E2=80=A6=20(#16333)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fix(clipboard): unix, check file transfer permission on incoming file clipboard The Cliprdr arm served incoming file clipboard messages on Linux/macOS without checking the file transfer permission, which the controlled side can turn off mid-session. can_sub_file_clipboard_service() only gates the outgoing service, so a peer could keep pushing files in and keep requesting the host's copied file list and contents. Check file_transfer_enabled() on every message. It is the same permission the client and the Windows cm use for file copy-paste. Co-Authored-By: Claude Opus 5.5 (1M context) * fix(clipboard): honor one-way file transfer on incoming file requests One-way file transfer was only enforced on the outgoing side: the host never announces its clipboard files. A controller could still send FormatDataRequest / FileContentsRequest directly, and the host answered them, from the live clipboard on Windows and from serv_files on Linux/macOS. Drop those two requests when one-way file transfer is on, before any platform handling. The messages that carry the peer's own files to this side are untouched. Co-Authored-By: Claude Opus 5.5 (1M context) * style: drop redundant doc comment Co-Authored-By: Claude Opus 5.5 (1M context) --------- Co-authored-by: Claude Opus 5.5 (1M context) --- src/clipboard_file.rs | 56 ++++++++++++++++++++++++++++++++++++++++ src/server/connection.rs | 8 +++++- 2 files changed, 63 insertions(+), 1 deletion(-) diff --git a/src/clipboard_file.rs b/src/clipboard_file.rs index f4458b098..b50f3c828 100644 --- a/src/clipboard_file.rs +++ b/src/clipboard_file.rs @@ -224,6 +224,13 @@ pub fn msg_2_clip(msg: Cliprdr) -> Option { } } +pub fn is_file_data_request(msg: &Cliprdr) -> bool { + matches!( + msg.union, + Some(cliprdr::Union::FormatDataRequest(_)) | Some(cliprdr::Union::FileContentsRequest(_)) + ) +} + #[cfg(feature = "unix-file-copy-paste")] pub mod unix_file_clip { use super::*; @@ -433,3 +440,52 @@ pub mod unix_file_clip { vec![] } } + +#[cfg(test)] +mod tests { + use super::*; + + fn is_request(clip: ClipboardFile) -> bool { + match clip_2_msg(clip).union { + Some(message::Union::Cliprdr(msg)) => is_file_data_request(&msg), + _ => false, + } + } + + #[test] + fn test_is_file_data_request() { + assert!(is_request(ClipboardFile::FormatDataRequest { + requested_format_id: 0, + })); + assert!(is_request(ClipboardFile::FileContentsRequest { + stream_id: 0, + list_index: 0, + dw_flags: 0, + n_position_low: 0, + n_position_high: 0, + cb_requested: 0, + have_clip_data_id: false, + clip_data_id: 0, + })); + + // Messages that carry the peer's own clipboard files must stay allowed. + assert!(!is_request(ClipboardFile::MonitorReady)); + assert!(!is_request(ClipboardFile::FormatList { + format_list: vec![], + })); + assert!(!is_request(ClipboardFile::FormatListResponse { + msg_flags: 0 + })); + assert!(!is_request(ClipboardFile::FormatDataResponse { + msg_flags: 0, + format_data: vec![], + })); + assert!(!is_request(ClipboardFile::FileContentsResponse { + msg_flags: 0, + stream_id: 0, + requested_data: vec![], + })); + assert!(!is_request(ClipboardFile::TryEmpty)); + assert!(!is_request(ClipboardFile::Files { files: vec![] })); + } +} diff --git a/src/server/connection.rs b/src/server/connection.rs index 7ed59b40c..ed4a1112c 100644 --- a/src/server/connection.rs +++ b/src/server/connection.rs @@ -3387,13 +3387,19 @@ impl Connection { .collect::>(), json!({}), ); + } else if is_file_data_request(&clip) + && crate::get_builtin_option(keys::OPTION_ONE_WAY_FILE_TRANSFER) == "Y" + { + // One-way file transfer: never serve this side's clipboard files to the peer. } else if let Some(clip) = msg_2_clip(clip) { #[cfg(target_os = "windows")] { self.send_to_cm(ipc::Data::ClipboardFile(clip)); } #[cfg(feature = "unix-file-copy-paste")] - if crate::is_support_file_copy_paste(&self.lr.version) { + if crate::is_support_file_copy_paste(&self.lr.version) + && self.file_transfer_enabled() + { let mut out_msgs = vec![]; #[cfg(target_os = "macos")]