Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adds support for SharedDirectoryTruncateRequest and SharedDirectoryTruncateResponse messages #40068

Merged
merged 2 commits into from
Apr 5, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 36 additions & 0 deletions lib/srv/desktop/rdp/rdpclient/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -622,6 +622,15 @@ func (c *Client) startInputStreaming(stopCh chan struct{}) error {
return trace.Errorf("SharedDirectoryMoveResponse failed: %v", errCode)
}
}
case tdp.SharedDirectoryTruncateResponse:
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we need some backwards compat logic here, or is the thought that we will never send a response to an old w_d_s that doesn't understand it because said w_d_s would have never sent the initial request?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we will never send a response to an old w_d_s that doesn't understand it because said w_d_s would have never sent the initial request?

Yes, only a w_d_s that sends a truncate request will ever receive a truncate response, and all w_d_s's that send truncate requests can handle truncate responses.

if c.cfg.AllowDirectorySharing {
if errCode := C.client_handle_tdp_sd_truncate_response(C.ulong(c.handle), C.CGOSharedDirectoryTruncateResponse{
completion_id: C.uint32_t(m.CompletionID),
err_code: m.ErrCode,
}); errCode != C.ErrCodeSuccess {
return trace.Errorf("SharedDirectoryTruncateResponse failed: %v", errCode)
}
}
case tdp.RDPResponsePDU:
pduLen := uint32(len(m))
if pduLen == 0 {
Expand Down Expand Up @@ -963,6 +972,33 @@ func (c *Client) sharedDirectoryMoveRequest(req tdp.SharedDirectoryMoveRequest)

}

//export cgo_tdp_sd_truncate_request
func cgo_tdp_sd_truncate_request(handle C.uintptr_t, req *C.CGOSharedDirectoryTruncateRequest) C.CGOErrCode {
client, err := toClient(handle)
if err != nil {
return C.ErrCodeFailure
}
return client.sharedDirectoryTruncateRequest(tdp.SharedDirectoryTruncateRequest{
CompletionID: uint32(req.completion_id),
DirectoryID: uint32(req.directory_id),
Path: C.GoString(req.path),
EndOfFile: uint32(req.end_of_file),
})
}

func (c *Client) sharedDirectoryTruncateRequest(req tdp.SharedDirectoryTruncateRequest) C.CGOErrCode {
if !c.cfg.AllowDirectorySharing {
return C.ErrCodeFailure
}

if err := c.cfg.Conn.WriteMessage(req); err != nil {
c.cfg.Log.Errorf("failed to send SharedDirectoryTruncateRequest: %v", err)
return C.ErrCodeFailure
}
return C.ErrCodeSuccess

}

// GetClientLastActive returns the time of the last recorded activity.
// For RDP, "activity" is defined as user-input messages
// (mouse move, button press, etc.)
Expand Down
36 changes: 36 additions & 0 deletions lib/srv/desktop/rdp/rdpclient/src/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -400,6 +400,10 @@ impl Client {
Client::handle_tdp_sd_move_response(x224_processor.clone(), res)
.await?;
}
ClientFunction::HandleTdpSdTruncateResponse(res) => {
Client::handle_tdp_sd_truncate_response(x224_processor.clone(), res)
.await?;
}
ClientFunction::WriteCliprdr(f) => {
Client::write_cliprdr(x224_processor.clone(), &mut write_stream, f)
.await?;
Expand Down Expand Up @@ -699,6 +703,21 @@ impl Client {
.await?
}

async fn handle_tdp_sd_truncate_response(
x224_processor: Arc<Mutex<x224::Processor>>,
res: tdp::SharedDirectoryTruncateResponse,
) -> ClientResult<()> {
global::TOKIO_RT
.spawn_blocking(move || {
debug!("received tdp: {:?}", res);
let mut x224_processor = Self::x224_lock(&x224_processor)?;
let rdpdr = Self::rdpdr_backend(&mut x224_processor)?;
rdpdr.handle_tdp_sd_truncate_response(res)?;
Ok(())
})
.await?
}

async fn add_drive(
x224_processor: Arc<Mutex<x224::Processor>>,
sda: tdp::SharedDirectoryAnnounce,
Expand Down Expand Up @@ -864,6 +883,8 @@ enum ClientFunction {
HandleTdpSdWriteResponse(tdp::SharedDirectoryWriteResponse),
/// Corresponds to [`Client::handle_tdp_sd_move_response`]
HandleTdpSdMoveResponse(tdp::SharedDirectoryMoveResponse),
/// Corresponds to [`Client::handle_tdp_sd_truncate_response`]
HandleTdpSdTruncateResponse(tdp::SharedDirectoryTruncateResponse),
/// Corresponds to [`Client::write_cliprdr`]
WriteCliprdr(Box<dyn ClipboardFn>),
/// Corresponds to [`Client::update_clipboard`]
Expand Down Expand Up @@ -1042,6 +1063,21 @@ impl ClientHandle {
.await
}

pub fn handle_tdp_sd_truncate_response(
&self,
res: tdp::SharedDirectoryTruncateResponse,
) -> ClientResult<()> {
self.blocking_send(ClientFunction::HandleTdpSdTruncateResponse(res))
}

pub async fn handle_tdp_sd_truncate_response_async(
&self,
res: tdp::SharedDirectoryTruncateResponse,
) -> ClientResult<()> {
self.send(ClientFunction::HandleTdpSdTruncateResponse(res))
.await
}

pub fn write_cliprdr(&self, f: Box<dyn ClipboardFn>) -> ClientResult<()> {
self.blocking_send(ClientFunction::WriteCliprdr(f))
}
Expand Down
36 changes: 34 additions & 2 deletions lib/srv/desktop/rdp/rdpclient/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,8 @@ use rdpdr::path::UnixPath;
use rdpdr::tdp::{
FileSystemObject, FileType, SharedDirectoryAcknowledge, SharedDirectoryCreateResponse,
SharedDirectoryDeleteResponse, SharedDirectoryInfoResponse, SharedDirectoryListResponse,
SharedDirectoryMoveResponse, SharedDirectoryReadResponse, SharedDirectoryWriteResponse,
TdpErrCode,
SharedDirectoryMoveResponse, SharedDirectoryReadResponse, SharedDirectoryTruncateResponse,
SharedDirectoryWriteResponse, TdpErrCode,
};
use std::ffi::CString;
use std::fmt::Debug;
Expand Down Expand Up @@ -347,6 +347,24 @@ pub unsafe extern "C" fn client_handle_tdp_sd_move_response(
)
}

/// client_handle_tdp_sd_truncate_response handles a TDP Shared Directory Truncate Response
/// message
///
/// # Safety
///
/// `cgo_handle` must be a valid handle.
#[no_mangle]
pub unsafe extern "C" fn client_handle_tdp_sd_truncate_response(
cgo_handle: CgoHandle,
res: CGOSharedDirectoryTruncateResponse,
) -> CGOErrCode {
handle_operation(
cgo_handle,
"client_handle_tdp_sd_truncate_response",
move |client_handle| client_handle.handle_tdp_sd_truncate_response(res),
)
}

/// client_handle_tdp_rdp_response_pdu handles a TDP RDP Response PDU message. It takes a raw encoded RDP PDU
/// created by the ironrdp client on the frontend and sends it directly to the RDP server.
///
Expand Down Expand Up @@ -651,6 +669,16 @@ pub struct CGOSharedDirectoryListRequest {
pub path: *const c_char,
}

#[repr(C)]
pub struct CGOSharedDirectoryTruncateRequest {
pub completion_id: u32,
pub directory_id: u32,
pub path: *const c_char,
pub end_of_file: u32,
}

pub type CGOSharedDirectoryTruncateResponse = SharedDirectoryTruncateResponse;

// These functions are defined on the Go side.
// Look for functions with '//export funcname' comments.
extern "C" {
Expand Down Expand Up @@ -695,6 +723,10 @@ extern "C" {
cgo_handle: CgoHandle,
req: *mut CGOSharedDirectoryMoveRequest,
) -> CGOErrCode;
fn cgo_tdp_sd_truncate_request(
cgo_handle: CgoHandle,
req: *mut CGOSharedDirectoryTruncateRequest,
) -> CGOErrCode;
}

/// A [cgo.Handle] passed to us by Go.
Expand Down
7 changes: 7 additions & 0 deletions lib/srv/desktop/rdp/rdpclient/src/rdpdr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,13 @@ impl TeleportRdpdrBackend {
) -> PduResult<()> {
self.fs.handle_tdp_sd_move_response(tdp_resp)
}

pub fn handle_tdp_sd_truncate_response(
&mut self,
tdp_resp: tdp::SharedDirectoryTruncateResponse,
) -> PduResult<()> {
self.fs.handle_tdp_sd_truncate_response(tdp_resp)
}
}

/// A generic error type for the TeleportRdpdrBackend that can contain any arbitrary error message.
Expand Down
101 changes: 96 additions & 5 deletions lib/srv/desktop/rdp/rdpclient/src/rdpdr/filesystem.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,15 +19,16 @@ use super::{
use crate::{
cgo_tdp_sd_acknowledge, cgo_tdp_sd_create_request, cgo_tdp_sd_delete_request,
cgo_tdp_sd_info_request, cgo_tdp_sd_list_request, cgo_tdp_sd_move_request,
cgo_tdp_sd_read_request, cgo_tdp_sd_write_request, client::ClientHandle, CGOErrCode, CgoHandle,
cgo_tdp_sd_read_request, cgo_tdp_sd_truncate_request, cgo_tdp_sd_write_request,
client::ClientHandle, CGOErrCode, CgoHandle,
};
use ironrdp_pdu::{cast_length, custom_err, other_err, PduResult};
use ironrdp_rdpdr::pdu::{
self,
efs::{self, NtStatus},
esc,
};
use log::debug;
use log::{debug, warn};
use std::collections::HashMap;
use std::convert::TryInto;

Expand All @@ -51,6 +52,7 @@ pub struct FilesystemBackend {
pending_sd_read_resp_handlers: ResponseCache<tdp::SharedDirectoryReadResponse>,
pending_sd_write_resp_handlers: ResponseCache<tdp::SharedDirectoryWriteResponse>,
pending_sd_move_resp_handlers: ResponseCache<tdp::SharedDirectoryMoveResponse>,
pending_sd_truncate_resp_handlers: ResponseCache<tdp::SharedDirectoryTruncateResponse>,
}

impl FilesystemBackend {
Expand All @@ -66,6 +68,7 @@ impl FilesystemBackend {
pending_sd_read_resp_handlers: ResponseCache::new(),
pending_sd_write_resp_handlers: ResponseCache::new(),
pending_sd_move_resp_handlers: ResponseCache::new(),
pending_sd_truncate_resp_handlers: ResponseCache::new(),
}
}

Expand Down Expand Up @@ -595,9 +598,10 @@ impl FilesystemBackend {
}
}
}
efs::FileInformationClass::Basic(_)
| efs::FileInformationClass::EndOfFile(_)
| efs::FileInformationClass::Allocation(_) => {
efs::FileInformationClass::EndOfFile(ref eof) => {
self.tdp_sd_truncate(rdp_req.clone(), eof, io_status)
}
efs::FileInformationClass::Basic(_) | efs::FileInformationClass::Allocation(_) => {
// Each of these ask us to change something we don't have control over at the browser
// level, so we just do nothing and send back a success.
// https://github.com/FreeRDP/FreeRDP/blob/dfa231c0a55b005af775b833f92f6bcd30363d77/channels/drive/client/drive_file.c#L579
Expand Down Expand Up @@ -844,6 +848,59 @@ impl FilesystemBackend {
}
}

/// Helper function for sending a [`tdp::SharedDirectoryMoveRequest`] to the browser
/// and handling the [`tdp::SharedDirectoryMoveResponse`] that is received in response.
fn tdp_sd_truncate(
&mut self,
rdp_req: efs::ServerDriveSetInformationRequest,
eof: &efs::FileEndOfFileInformation,
io_status: NtStatus,
) -> PduResult<()> {
if let Some(file) = self.file_cache.get(rdp_req.device_io_request.file_id) {
let end_of_file = eof.end_of_file;
self.send_tdp_truncate_request(tdp::SharedDirectoryTruncateRequest {
completion_id: rdp_req.device_io_request.completion_id,
directory_id: rdp_req.device_io_request.device_id,
path: file.path.clone(),
end_of_file: cast_length!("end_of_file", end_of_file)?,
})?;

self.pending_sd_truncate_resp_handlers.insert(
rdp_req.device_io_request.completion_id,
SharedDirectoryTruncateResponseHandler::new(
move |this: &mut FilesystemBackend,
res: tdp::SharedDirectoryTruncateResponse|
-> PduResult<()> {
if res.err_code != TdpErrCode::Nil {
return this
.send_rdp_set_info_response(&rdp_req, NtStatus::UNSUCCESSFUL);
}

let io_status = if let Some(file) =
this.file_cache.get_mut(rdp_req.device_io_request.file_id)
{
// Truncate succeeded, update our internal books to reflect the new size.
file.fso.size = cast_length!("end_of_file", end_of_file)?;
io_status
} else {
// This shouldn't happen.
warn!("file unexpectedly not found in cache after truncate");
NtStatus::UNSUCCESSFUL
};

this.send_rdp_set_info_response(&rdp_req, io_status)
},
),
);

return Ok(());
}

// This shouldn't happen.
warn!("attempted to truncate a file that wasn't in the file cache");
self.send_rdp_set_info_response(&rdp_req, NtStatus::UNSUCCESSFUL)
}

/// Helper function for sending a [`tdp::SharedDirectoryMoveRequest`] to the browser
/// and handling the [`tdp::SharedDirectoryMoveResponse`] that is received in response.
fn tdp_sd_move(
Expand Down Expand Up @@ -913,6 +970,23 @@ impl FilesystemBackend {
}
}

/// Sends a [`tdp::SharedDirectoryTruncateRequest`] to the browser.
fn send_tdp_truncate_request(
&self,
tdp_req: tdp::SharedDirectoryTruncateRequest,
) -> PduResult<()> {
debug!("sending tdp: {:?}", tdp_req);
let mut req = tdp_req.into_cgo()?;
let err = unsafe { cgo_tdp_sd_truncate_request(self.cgo_handle, req.cgo()) };
match err {
CGOErrCode::ErrCodeSuccess => Ok(()),
_ => Err(custom_err!(FilesystemBackendError(format!(
"call to tdp_sd_truncate_request failed: {:?}",
err
)))),
}
}

/// Sends a [`tdp::SharedDirectoryCreateRequest`] to the browser.
fn send_tdp_sd_create_request(
&self,
Expand Down Expand Up @@ -1142,6 +1216,22 @@ impl FilesystemBackend {
}
}

pub fn handle_tdp_sd_truncate_response(
&mut self,
tdp_resp: tdp::SharedDirectoryTruncateResponse,
) -> PduResult<()> {
match self
.pending_sd_truncate_resp_handlers
.remove(&tdp_resp.completion_id)
{
Some(handler) => handler.call(self, tdp_resp),
None => Err(custom_err!(FilesystemBackendError(format!(
"received invalid completion id: {}",
tdp_resp.completion_id
)))),
}
}

/// Helper function for sending an RDP [`efs::DeviceCreateResponse`] based on an RDP [`efs::DeviceCreateRequest`].
fn send_rdp_device_create_response(
&self,
Expand Down Expand Up @@ -1690,6 +1780,7 @@ type SharedDirectoryListResponseHandler = ResponseHandler<tdp::SharedDirectoryLi
type SharedDirectoryReadResponseHandler = ResponseHandler<tdp::SharedDirectoryReadResponse>;
type SharedDirectoryWriteResponseHandler = ResponseHandler<tdp::SharedDirectoryWriteResponse>;
type SharedDirectoryMoveResponseHandler = ResponseHandler<tdp::SharedDirectoryMoveResponse>;
type SharedDirectoryTruncateResponseHandler = ResponseHandler<tdp::SharedDirectoryTruncateResponse>;

type CompletionId = u32;

Expand Down
Loading
Loading