Skip to content

Commit

Permalink
Adds support for SharedDirectoryTruncateRequest and SharedDirectoryTr…
Browse files Browse the repository at this point in the history
…uncateResponse messages
  • Loading branch information
Isaiah Becker-Mayer committed Mar 30, 2024
1 parent 0767676 commit 3d429af
Show file tree
Hide file tree
Showing 11 changed files with 530 additions and 94 deletions.
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:
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

0 comments on commit 3d429af

Please sign in to comment.