Skip to content

Commit 58c3df8

Browse files
feat(cliprdr): add clipboard data locking methods (#1064)
Per [MS-RDPECLIP sections 2.2.4.6 and 2.2.4.7][lock-spec], the Local Clipboard Owner may lock the Shared Clipboard Owner's clipboard data before requesting file contents to ensure data stability during multi-request transfers. This enables server implementations to safely request file data from clients when handling clipboard paste operations. --------- Co-authored-by: lamco-office <office@lamco.io>
1 parent 71245d5 commit 58c3df8

File tree

6 files changed

+65
-1
lines changed

6 files changed

+65
-1
lines changed

crates/ironrdp-client/src/rdp.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -565,6 +565,14 @@ async fn active_session(
565565
Some(cliprdr.initiate_paste(format)
566566
.map_err(|e| session::custom_err!("CLIPRDR", e))?)
567567
}
568+
ClipboardMessage::SendLockClipboard { clip_data_id } => {
569+
Some(cliprdr.lock_clipboard(clip_data_id)
570+
.map_err(|e| session::custom_err!("CLIPRDR", e))?)
571+
}
572+
ClipboardMessage::SendUnlockClipboard { clip_data_id } => {
573+
Some(cliprdr.unlock_clipboard(clip_data_id)
574+
.map_err(|e| session::custom_err!("CLIPRDR", e))?)
575+
}
568576
ClipboardMessage::Error(e) => {
569577
error!("Clipboard backend error: {}", e);
570578
None

crates/ironrdp-cliprdr/src/backend.rs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,16 @@ pub enum ClipboardMessage {
3333
/// received.
3434
SendInitiatePaste(ClipboardFormatId),
3535

36+
/// Sent by clipboard backend when clipboard data should be locked on the remote.
37+
///
38+
/// Implementation should send lock clipboard data PDU on `CLIPRDR` SVC when received.
39+
SendLockClipboard { clip_data_id: u32 },
40+
41+
/// Sent by clipboard backend when clipboard data should be unlocked on the remote.
42+
///
43+
/// Implementation should send unlock clipboard data PDU on `CLIPRDR` SVC when received.
44+
SendUnlockClipboard { clip_data_id: u32 },
45+
3646
/// Failure received from the OS clipboard event loop.
3747
///
3848
/// Client implementation should log/display this error.

crates/ironrdp-cliprdr/src/lib.rs

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ use ironrdp_svc::{
1515
};
1616
use pdu::{
1717
Capabilities, ClientTemporaryDirectory, ClipboardFormat, ClipboardFormatId, ClipboardGeneralCapabilityFlags,
18-
ClipboardPdu, ClipboardProtocolVersion, FileContentsResponse, FormatDataRequest, FormatListResponse,
18+
ClipboardPdu, ClipboardProtocolVersion, FileContentsResponse, FormatDataRequest, FormatListResponse, LockDataId,
1919
OwnedFormatDataResponse,
2020
};
2121
use tracing::{error, info};
@@ -276,6 +276,32 @@ impl<R: Role> Cliprdr<R> {
276276

277277
Ok(vec![into_cliprdr_message(pdu)].into())
278278
}
279+
280+
/// [2.2.4.6] Lock Clipboard Data PDU (CLIPRDR_LOCK_CLIPDATA)
281+
///
282+
/// Locks clipboard data on the remote before file transfer. Should be called before
283+
/// requesting file contents to ensure data stability during transfer.
284+
///
285+
/// [2.2.4.6]: https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-rdpeclip/150bac72-bc7f-42e5-9e8e-cb5a0ddc7dbc
286+
pub fn lock_clipboard(&self, clip_data_id: u32) -> PduResult<CliprdrSvcMessages<R>> {
287+
ready_guard!(self, lock_clipboard);
288+
289+
let pdu = ClipboardPdu::LockData(LockDataId(clip_data_id));
290+
Ok(vec![into_cliprdr_message(pdu)].into())
291+
}
292+
293+
/// [2.2.4.7] Unlock Clipboard Data PDU (CLIPRDR_UNLOCK_CLIPDATA)
294+
///
295+
/// Unlocks previously locked clipboard data. Should be called after file transfer
296+
/// operations complete.
297+
///
298+
/// [2.2.4.7]: https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-rdpeclip/e587a20c-fb7c-47d1-8698-4bcb92c48a38
299+
pub fn unlock_clipboard(&self, clip_data_id: u32) -> PduResult<CliprdrSvcMessages<R>> {
300+
ready_guard!(self, unlock_clipboard);
301+
302+
let pdu = ClipboardPdu::UnlockData(LockDataId(clip_data_id));
303+
Ok(vec![into_cliprdr_message(pdu)].into())
304+
}
279305
}
280306

281307
impl<R: Role> SvcProcessor for Cliprdr<R> {

crates/ironrdp-server/src/server.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -552,6 +552,10 @@ impl RdpServer {
552552
ClipboardMessage::SendInitiateCopy(formats) => cliprdr.initiate_copy(&formats),
553553
ClipboardMessage::SendFormatData(data) => cliprdr.submit_format_data(data),
554554
ClipboardMessage::SendInitiatePaste(format) => cliprdr.initiate_paste(format),
555+
ClipboardMessage::SendLockClipboard { clip_data_id } => cliprdr.lock_clipboard(clip_data_id),
556+
ClipboardMessage::SendUnlockClipboard { clip_data_id } => {
557+
cliprdr.unlock_clipboard(clip_data_id)
558+
}
555559
ClipboardMessage::Error(error) => {
556560
error!(?error, "Handling clipboard event");
557561
continue;

crates/ironrdp-web/src/session.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -526,6 +526,14 @@ impl iron_remote_desktop::Session for Session {
526526
cliprdr.initiate_paste(format)
527527
.context("CLIPRDR initiate paste")?
528528
),
529+
ClipboardMessage::SendLockClipboard { clip_data_id } => Some(
530+
cliprdr.lock_clipboard(clip_data_id)
531+
.context("CLIPRDR lock clipboard")?
532+
),
533+
ClipboardMessage::SendUnlockClipboard { clip_data_id } => Some(
534+
cliprdr.unlock_clipboard(clip_data_id)
535+
.context("CLIPRDR unlock clipboard")?
536+
),
529537
ClipboardMessage::Error(e) => {
530538
error!("Clipboard backend error: {}", e);
531539
None

ffi/src/clipboard/message.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,12 @@ pub mod ffi {
1414
ironrdp::cliprdr::backend::ClipboardMessage::SendInitiatePaste(_) => {
1515
ClipboardMessageType::SendInitiatePaste
1616
}
17+
ironrdp::cliprdr::backend::ClipboardMessage::SendLockClipboard { .. } => {
18+
ClipboardMessageType::SendLockClipboard
19+
}
20+
ironrdp::cliprdr::backend::ClipboardMessage::SendUnlockClipboard { .. } => {
21+
ClipboardMessageType::SendUnlockClipboard
22+
}
1723
ironrdp::cliprdr::backend::ClipboardMessage::Error(_) => ClipboardMessageType::Error,
1824
}
1925
}
@@ -51,6 +57,8 @@ pub mod ffi {
5157
SendInitiateCopy,
5258
SendFormatData,
5359
SendInitiatePaste,
60+
SendLockClipboard,
61+
SendUnlockClipboard,
5462
Error,
5563
}
5664

0 commit comments

Comments
 (0)