fix(clipboard): unix, check file transfer permission on incoming file… (#16333)

* 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) <noreply@anthropic.com>

* 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) <noreply@anthropic.com>

* style: drop redundant doc comment

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
RustDesk
2026-09-24 21:10:54 +08:00
committed by GitHub
co-authored by Claude Opus 5.5
parent 58ff78a823
commit f299fb9906
2 changed files with 63 additions and 1 deletions
+56
View File
@@ -224,6 +224,13 @@ pub fn msg_2_clip(msg: Cliprdr) -> Option<ClipboardFile> {
}
}
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![] }));
}
}
+7 -1
View File
@@ -3387,13 +3387,19 @@ impl Connection {
.collect::<Vec<(String, i64)>>(),
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")]