SharedDirectoryMoveRequest and SharedDirectoryMoveResponse (#14959)

This commit is contained in:
Isaiah Becker-Mayer
2022-08-14 21:15:49 +00:00
committed by GitHub
parent eff38e2fa5
commit 321d3482dd
6 changed files with 507 additions and 136 deletions
+84 -51
View File
@@ -509,6 +509,16 @@ func (c *Client) start() {
return
}
}
case tdp.SharedDirectoryMoveResponse:
if c.cfg.AllowDirectorySharing {
if errCode := C.handle_tdp_sd_move_response(c.rustClient, C.CGOSharedDirectoryMoveResponse{
completion_id: C.uint32_t(m.CompletionID),
err_code: m.ErrCode,
}); errCode != C.ErrCodeSuccess {
c.cfg.Log.Errorf("SharedDirectoryMoveResponse failed: %v", errCode)
return
}
}
default:
c.cfg.Log.Warningf("Skipping unimplemented TDP message type %T", msg)
}
@@ -585,15 +595,15 @@ func tdp_sd_acknowledge(handle C.uintptr_t, ack *C.CGOSharedDirectoryAcknowledge
// sharedDirectoryAcknowledge is sent by the TDP server to the client
// to acknowledge that a SharedDirectoryAnnounce was received.
func (c *Client) sharedDirectoryAcknowledge(ack tdp.SharedDirectoryAcknowledge) C.CGOErrCode {
if c.cfg.AllowDirectorySharing {
if err := c.cfg.Conn.OutputMessage(ack); err != nil {
c.cfg.Log.Errorf("failed to send SharedDirectoryAcknowledge: %v", err)
return C.ErrCodeFailure
}
return C.ErrCodeSuccess
if !c.cfg.AllowDirectorySharing {
return C.ErrCodeFailure
}
return C.ErrCodeFailure
if err := c.cfg.Conn.OutputMessage(ack); err != nil {
c.cfg.Log.Errorf("failed to send SharedDirectoryAcknowledge: %v", err)
return C.ErrCodeFailure
}
return C.ErrCodeSuccess
}
//export tdp_sd_info_request
@@ -608,15 +618,15 @@ func tdp_sd_info_request(handle C.uintptr_t, req *C.CGOSharedDirectoryInfoReques
// sharedDirectoryInfoRequest is sent from the TDP server to the client
// to request information about a file or directory at a given path.
func (c *Client) sharedDirectoryInfoRequest(req tdp.SharedDirectoryInfoRequest) C.CGOErrCode {
if c.cfg.AllowDirectorySharing {
if err := c.cfg.Conn.OutputMessage(req); err != nil {
c.cfg.Log.Errorf("failed to send SharedDirectoryAcknowledge: %v", err)
return C.ErrCodeFailure
}
return C.ErrCodeSuccess
if !c.cfg.AllowDirectorySharing {
return C.ErrCodeFailure
}
return C.ErrCodeFailure
if err := c.cfg.Conn.OutputMessage(req); err != nil {
c.cfg.Log.Errorf("failed to send SharedDirectoryAcknowledge: %v", err)
return C.ErrCodeFailure
}
return C.ErrCodeSuccess
}
//export tdp_sd_create_request
@@ -632,15 +642,15 @@ func tdp_sd_create_request(handle C.uintptr_t, req *C.CGOSharedDirectoryCreateRe
// sharedDirectoryCreateRequest is sent by the TDP server to
// the client to request the creation of a new file or directory.
func (c *Client) sharedDirectoryCreateRequest(req tdp.SharedDirectoryCreateRequest) C.CGOErrCode {
if c.cfg.AllowDirectorySharing {
if err := c.cfg.Conn.OutputMessage(req); err != nil {
c.cfg.Log.Errorf("failed to send SharedDirectoryCreateRequest: %v", err)
return C.ErrCodeFailure
}
return C.ErrCodeSuccess
if !c.cfg.AllowDirectorySharing {
return C.ErrCodeFailure
}
return C.ErrCodeFailure
if err := c.cfg.Conn.OutputMessage(req); err != nil {
c.cfg.Log.Errorf("failed to send SharedDirectoryCreateRequest: %v", err)
return C.ErrCodeFailure
}
return C.ErrCodeSuccess
}
//export tdp_sd_delete_request
@@ -655,15 +665,15 @@ func tdp_sd_delete_request(handle C.uintptr_t, req *C.CGOSharedDirectoryDeleteRe
// sharedDirectoryDeleteRequest is sent by the TDP server to the client
// to request the deletion of a file or directory at path.
func (c *Client) sharedDirectoryDeleteRequest(req tdp.SharedDirectoryDeleteRequest) C.CGOErrCode {
if c.cfg.AllowDirectorySharing {
if err := c.cfg.Conn.OutputMessage(req); err != nil {
c.cfg.Log.Errorf("failed to send SharedDirectoryDeleteRequest: %v", err)
return C.ErrCodeFailure
}
return C.ErrCodeSuccess
if !c.cfg.AllowDirectorySharing {
return C.ErrCodeFailure
}
return C.ErrCodeFailure
if err := c.cfg.Conn.OutputMessage(req); err != nil {
c.cfg.Log.Errorf("failed to send SharedDirectoryDeleteRequest: %v", err)
return C.ErrCodeFailure
}
return C.ErrCodeSuccess
}
//export tdp_sd_list_request
@@ -678,15 +688,15 @@ func tdp_sd_list_request(handle C.uintptr_t, req *C.CGOSharedDirectoryListReques
// sharedDirectoryListRequest is sent by the TDP server to the client
// to request the contents of a directory.
func (c *Client) sharedDirectoryListRequest(req tdp.SharedDirectoryListRequest) C.CGOErrCode {
if c.cfg.AllowDirectorySharing {
if err := c.cfg.Conn.OutputMessage(req); err != nil {
c.cfg.Log.Errorf("failed to send SharedDirectoryListRequest: %v", err)
return C.ErrCodeFailure
}
return C.ErrCodeSuccess
if !c.cfg.AllowDirectorySharing {
return C.ErrCodeFailure
}
return C.ErrCodeFailure
if err := c.cfg.Conn.OutputMessage(req); err != nil {
c.cfg.Log.Errorf("failed to send SharedDirectoryListRequest: %v", err)
return C.ErrCodeFailure
}
return C.ErrCodeSuccess
}
//export tdp_sd_read_request
@@ -695,7 +705,6 @@ func tdp_sd_read_request(handle C.uintptr_t, req *C.CGOSharedDirectoryReadReques
CompletionID: uint32(req.completion_id),
DirectoryID: uint32(req.directory_id),
Path: C.GoString(req.path),
PathLength: uint32(req.path_length),
Offset: uint64(req.offset),
Length: uint32(req.length),
})
@@ -704,14 +713,15 @@ func tdp_sd_read_request(handle C.uintptr_t, req *C.CGOSharedDirectoryReadReques
// SharedDirectoryReadRequest is sent by the TDP server to the client
// to request the contents of a file.
func (c *Client) sharedDirectoryReadRequest(req tdp.SharedDirectoryReadRequest) C.CGOErrCode {
if c.cfg.AllowDirectorySharing {
if err := c.cfg.Conn.OutputMessage(req); err != nil {
c.cfg.Log.Errorf("failed to send SharedDirectoryReadRequest: %v", err)
return C.ErrCodeFailure
}
return C.ErrCodeSuccess
if !c.cfg.AllowDirectorySharing {
return C.ErrCodeFailure
}
return C.ErrCodeFailure
if err := c.cfg.Conn.OutputMessage(req); err != nil {
c.cfg.Log.Errorf("failed to send SharedDirectoryReadRequest: %v", err)
return C.ErrCodeFailure
}
return C.ErrCodeSuccess
}
//export tdp_sd_write_request
@@ -720,7 +730,6 @@ func tdp_sd_write_request(handle C.uintptr_t, req *C.CGOSharedDirectoryWriteRequ
CompletionID: uint32(req.completion_id),
DirectoryID: uint32(req.directory_id),
Offset: uint64(req.offset),
PathLength: uint32(req.path_length),
Path: C.GoString(req.path),
WriteDataLength: uint32(req.write_data_length),
WriteData: C.GoBytes(unsafe.Pointer(req.write_data), C.int(req.write_data_length)),
@@ -730,14 +739,38 @@ func tdp_sd_write_request(handle C.uintptr_t, req *C.CGOSharedDirectoryWriteRequ
// SharedDirectoryWriteRequest is sent by the TDP server to the client
// to write to a file.
func (c *Client) sharedDirectoryWriteRequest(req tdp.SharedDirectoryWriteRequest) C.CGOErrCode {
if c.cfg.AllowDirectorySharing {
if err := c.cfg.Conn.OutputMessage(req); err != nil {
c.cfg.Log.Errorf("failed to send SharedDirectoryWriteRequest: %v", err)
return C.ErrCodeFailure
}
return C.ErrCodeSuccess
if !c.cfg.AllowDirectorySharing {
return C.ErrCodeFailure
}
return C.ErrCodeFailure
if err := c.cfg.Conn.OutputMessage(req); err != nil {
c.cfg.Log.Errorf("failed to send SharedDirectoryWriteRequest: %v", err)
return C.ErrCodeFailure
}
return C.ErrCodeSuccess
}
//export tdp_sd_move_request
func tdp_sd_move_request(handle C.uintptr_t, req *C.CGOSharedDirectoryMoveRequest) C.CGOErrCode {
return cgo.Handle(handle).Value().(*Client).sharedDirectoryMoveRequest(tdp.SharedDirectoryMoveRequest{
CompletionID: uint32(req.completion_id),
DirectoryID: uint32(req.directory_id),
OriginalPath: C.GoString(req.original_path),
NewPath: C.GoString(req.new_path),
})
}
func (c *Client) sharedDirectoryMoveRequest(req tdp.SharedDirectoryMoveRequest) C.CGOErrCode {
if !c.cfg.AllowDirectorySharing {
return C.ErrCodeFailure
}
if err := c.cfg.Conn.OutputMessage(req); err != nil {
c.cfg.Log.Errorf("failed to send SharedDirectoryMoveRequest: %v", err)
return C.ErrCodeFailure
}
return C.ErrCodeSuccess
}
// close closes the RDP client connection and
+111
View File
@@ -504,6 +504,46 @@ fn connect_rdp_inner(
}
});
let tdp_sd_move_request = Box::new(move |req: SharedDirectoryMoveRequest| -> RdpResult<()> {
debug!("sending TDP SharedDirectoryMoveRequest: {:?}", req);
match req.original_path.to_cstring() {
Ok(original_path) => match req.new_path.to_cstring() {
Ok(new_path) => {
unsafe {
let err = tdp_sd_move_request(
go_ref,
&mut CGOSharedDirectoryMoveRequest {
completion_id: req.completion_id,
directory_id: req.directory_id,
original_path: original_path.as_ptr(),
new_path: new_path.as_ptr(),
},
);
if err != CGOErrCode::ErrCodeSuccess {
return Err(RdpError::TryError(String::from(
"call to tdp_sd_Move_failed",
)));
}
}
Ok(())
}
Err(_) => {
return Err(RdpError::TryError(format!(
"new_path contained characters that couldn't be converted to a C string: {:?}",
req.new_path
)));
}
},
Err(_) => {
return Err(RdpError::TryError(format!(
"original_path contained characters that couldn't be converted to a C string: {:?}",
req.original_path
)));
}
}
});
// Client for the "rdpdr" channel - smartcard emulation and drive redirection.
let rdpdr = rdpdr::Client::new(rdpdr::Config {
cert_der: params.cert_der,
@@ -517,6 +557,7 @@ fn connect_rdp_inner(
tdp_sd_list_request,
tdp_sd_read_request,
tdp_sd_write_request,
tdp_sd_move_request,
});
// Client for the "cliprdr" channel - clipboard sharing.
@@ -653,6 +694,13 @@ impl<S: Read + Write> RdpClient<S> {
self.rdpdr.handle_tdp_sd_write_response(res, &mut self.mcs)
}
pub fn handle_tdp_sd_move_response(
&mut self,
res: SharedDirectoryMoveResponse,
) -> RdpResult<()> {
self.rdpdr.handle_tdp_sd_move_response(res, &mut self.mcs)
}
pub fn shutdown(&mut self) -> RdpResult<()> {
self.mcs.shutdown()
}
@@ -1033,6 +1081,43 @@ pub unsafe extern "C" fn handle_tdp_sd_write_response(
}
}
/// handle_tdp_sd_move_response handles a TDP Shared Directory Move Response
/// message
///
/// # Safety
///
/// client_ptr MUST be a valid pointer.
/// (validity defined by https://doc.rust-lang.org/nightly/core/primitive.pointer.html#method.as_ref-1)
#[no_mangle]
pub unsafe extern "C" fn handle_tdp_sd_move_response(
client_ptr: *mut Client,
res: CGOSharedDirectoryMoveResponse,
) -> CGOErrCode {
// # Safety
//
// This function MUST NOT hang on to any of the pointers passed in to it after it returns.
// In other words, all pointer data that needs to persist after this function returns MUST
// be copied into Rust-owned memory.
let res: SharedDirectoryMoveResponse = res;
let client = match Client::from_ptr(client_ptr) {
Ok(client) => client,
Err(cgo_error) => {
return cgo_error;
}
};
let mut rdp_client = client.rdp_client.lock().unwrap();
match rdp_client.handle_tdp_sd_move_response(res) {
Ok(()) => CGOErrCode::ErrCodeSuccess,
Err(e) => {
error!("failed to handle Shared Directory Move Response: {:?}", e);
CGOErrCode::ErrCodeFailure
}
}
}
/// `read_rdp_output` reads incoming RDP bitmap frames from client at client_ref and forwards them to
/// handle_bitmap.
///
@@ -1641,6 +1726,24 @@ pub struct CGOSharedDirectoryListResponse {
fso_list: *mut CGOFileSystemObject,
}
/// SharedDirectoryMoveRequest is sent from the TDP server to the client
/// to request a file at original_path be moved to new_path.
#[derive(Debug)]
pub struct SharedDirectoryMoveRequest {
completion_id: u32,
directory_id: u32,
original_path: UnixPath,
new_path: UnixPath,
}
#[repr(C)]
pub struct CGOSharedDirectoryMoveRequest {
pub completion_id: u32,
pub directory_id: u32,
pub original_path: *const c_char,
pub new_path: *const c_char,
}
pub type CGOSharedDirectoryCreateResponse = SharedDirectoryCreateResponse;
/// SharedDirectoryDeleteRequest is sent by the TDP server to the client
/// to request the deletion of a file or directory at path.
@@ -1654,6 +1757,10 @@ pub type CGOSharedDirectoryDeleteResponse = SharedDirectoryCreateResponse;
/// to request the contents of a directory.
pub type SharedDirectoryListRequest = SharedDirectoryInfoRequest;
pub type CGOSharedDirectoryListRequest = CGOSharedDirectoryInfoRequest;
/// SharedDirectoryMoveResponse is sent by the TDP client to the server
/// to acknowledge a SharedDirectoryMoveRequest was received and executed.
pub type SharedDirectoryMoveResponse = SharedDirectoryCreateResponse;
pub type CGOSharedDirectoryMoveResponse = CGOSharedDirectoryCreateResponse;
// These functions are defined on the Go side. Look for functions with '//export funcname'
// comments.
@@ -1687,6 +1794,10 @@ extern "C" {
client_ref: usize,
req: *mut CGOSharedDirectoryWriteRequest,
) -> CGOErrCode;
fn tdp_sd_move_request(
client_ref: usize,
req: *mut CGOSharedDirectoryMoveRequest,
) -> CGOErrCode;
}
/// Payload is a generic type used to represent raw incoming RDP messages for parsing.
@@ -124,7 +124,7 @@ pub enum NTSTATUS {
/// 2.4 File Information Classes [MS-FSCC]
/// https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-fscc/4718fc40-e539-4014-8e33-b675af74e3e1
#[derive(FromPrimitive, Debug, PartialEq)]
#[derive(FromPrimitive, Debug, PartialEq, Clone)]
#[repr(u32)]
#[allow(clippy::enum_variant_names)]
pub enum FileInformationClassLevel {
+214 -67
View File
@@ -28,8 +28,9 @@ use crate::{
FileSystemObject, FileType, Payload, SharedDirectoryAcknowledge, SharedDirectoryCreateRequest,
SharedDirectoryCreateResponse, SharedDirectoryDeleteRequest, SharedDirectoryDeleteResponse,
SharedDirectoryInfoRequest, SharedDirectoryInfoResponse, SharedDirectoryListRequest,
SharedDirectoryListResponse, SharedDirectoryReadRequest, SharedDirectoryReadResponse,
SharedDirectoryWriteRequest, SharedDirectoryWriteResponse, TdpErrCode,
SharedDirectoryListResponse, SharedDirectoryMoveRequest, SharedDirectoryMoveResponse,
SharedDirectoryReadRequest, SharedDirectoryReadResponse, SharedDirectoryWriteRequest,
SharedDirectoryWriteResponse, TdpErrCode,
};
use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
@@ -78,6 +79,7 @@ pub struct Client {
tdp_sd_list_request: SharedDirectoryListRequestSender,
tdp_sd_read_request: SharedDirectoryReadRequestSender,
tdp_sd_write_request: SharedDirectoryWriteRequestSender,
tdp_sd_move_request: SharedDirectoryMoveRequestSender,
// CompletionId-indexed maps of handlers for tdp messages coming from the browser client.
pending_sd_info_resp_handlers: HashMap<u32, SharedDirectoryInfoResponseHandler>,
@@ -86,6 +88,7 @@ pub struct Client {
pending_sd_list_resp_handlers: HashMap<u32, SharedDirectoryListResponseHandler>,
pending_sd_read_resp_handlers: HashMap<u32, SharedDirectoryReadResponseHandler>,
pending_sd_write_resp_handlers: HashMap<u32, SharedDirectoryWriteResponseHandler>,
pending_sd_move_resp_handlers: HashMap<u32, SharedDirectoryMoveResponseHandler>,
}
pub struct Config {
@@ -101,6 +104,7 @@ pub struct Config {
pub tdp_sd_list_request: SharedDirectoryListRequestSender,
pub tdp_sd_read_request: SharedDirectoryReadRequestSender,
pub tdp_sd_write_request: SharedDirectoryWriteRequestSender,
pub tdp_sd_move_request: SharedDirectoryMoveRequestSender,
}
impl Client {
@@ -126,6 +130,7 @@ impl Client {
tdp_sd_list_request: cfg.tdp_sd_list_request,
tdp_sd_read_request: cfg.tdp_sd_read_request,
tdp_sd_write_request: cfg.tdp_sd_write_request,
tdp_sd_move_request: cfg.tdp_sd_move_request,
pending_sd_info_resp_handlers: HashMap::new(),
pending_sd_create_resp_handlers: HashMap::new(),
@@ -133,6 +138,7 @@ impl Client {
pending_sd_list_resp_handlers: HashMap::new(),
pending_sd_read_resp_handlers: HashMap::new(),
pending_sd_write_resp_handlers: HashMap::new(),
pending_sd_move_resp_handlers: HashMap::new(),
}
}
/// Reads raw RDP messages sent on the rdpdr virtual channel and replies as necessary.
@@ -695,46 +701,44 @@ impl Client {
let rdp_req = ServerDriveQueryVolumeInformationRequest::decode(device_io_request, payload)?;
debug!("received RDP: {:?}", rdp_req);
if let Some(dir) = self.file_cache.get(rdp_req.device_io_request.file_id) {
// TODO(isaiah): we should support all of the fs_info_class_lvls that FreeRDP does:
// https://github.com/FreeRDP/FreeRDP/blob/511444a65e7aa2f537c5e531fa68157a50c1bd4d/channels/drive/client/drive_main.c#L468
match rdp_req.fs_info_class_lvl {
let buffer = match rdp_req.fs_info_class_lvl {
FileSystemInformationClassLevel::FileFsVolumeInformation => {
let buffer = Some(FileSystemInformationClass::FileFsVolumeInformation(
Some(FileSystemInformationClass::FileFsVolumeInformation(
FileFsVolumeInformation::new(dir.fso.last_modified as i64),
));
return self.prep_query_vol_info_response(
&rdp_req.device_io_request,
NTSTATUS::STATUS_SUCCESS,
buffer,
);
))
}
FileSystemInformationClassLevel::FileFsAttributeInformation => {
let buffer = Some(FileSystemInformationClass::FileFsAttributeInformation(
Some(FileSystemInformationClass::FileFsAttributeInformation(
FileFsAttributeInformation::new(),
));
return self.prep_query_vol_info_response(
&rdp_req.device_io_request,
NTSTATUS::STATUS_SUCCESS,
buffer,
);
))
}
FileSystemInformationClassLevel::FileFsSizeInformation
| FileSystemInformationClassLevel::FileFsFullSizeInformation
| FileSystemInformationClassLevel::FileFsDeviceInformation => {
return Err(not_implemented_error(&format!(
"support for ServerDriveQueryVolumeInformationRequest with fs_info_class_lvl = {:?} is not implemented",
rdp_req.fs_info_class_lvl
)));
FileSystemInformationClassLevel::FileFsFullSizeInformation => {
Some(FileSystemInformationClass::FileFsFullSizeInformation(
FileFsFullSizeInformation::new(),
))
}
_ => {
// https://github.com/FreeRDP/FreeRDP/blob/511444a65e7aa2f537c5e531fa68157a50c1bd4d/channels/drive/client/drive_main.c#L574-L577
return self.prep_query_vol_info_response(
&rdp_req.device_io_request,
NTSTATUS::STATUS_UNSUCCESSFUL,
None,
);
FileSystemInformationClassLevel::FileFsDeviceInformation => {
Some(FileSystemInformationClass::FileFsDeviceInformation(
FileFsDeviceInformation::new(),
))
}
}
FileSystemInformationClassLevel::FileFsSizeInformation => Some(
FileSystemInformationClass::FileFsSizeInformation(FileFsSizeInformation::new()),
),
_ => None,
};
let io_status = if buffer.is_some() {
NTSTATUS::STATUS_SUCCESS
} else {
NTSTATUS::STATUS_UNSUCCESSFUL
};
return self.prep_query_vol_info_response(
&rdp_req.device_io_request,
io_status,
buffer,
);
}
// File not found in cache
@@ -765,6 +769,7 @@ impl Client {
self.tdp_sd_write(rdp_req)
}
#[allow(clippy::wildcard_in_or_patterns)]
fn process_irp_set_information(
&mut self,
device_io_request: DeviceIoRequest,
@@ -772,25 +777,33 @@ impl Client {
) -> RdpResult<Vec<Vec<u8>>> {
let rdp_req = ServerDriveSetInformationRequest::decode(device_io_request, payload)?;
let resp = match rdp_req.file_information_class_level {
match rdp_req.file_information_class_level {
FileInformationClassLevel::FileRenameInformation => match rdp_req.set_buffer {
FileInformationClass::FileRenameInformation(ref rename_info) => {
self.rename(rdp_req.clone(), rename_info)
}
_ => Err(invalid_data_error(
"FileInformationClass does not match FileInformationClassLevel",
)),
},
FileInformationClassLevel::FileBasicInformation
| FileInformationClassLevel::FileEndOfFileInformation
| FileInformationClassLevel::FileAllocationInformation
| FileInformationClassLevel::FileDispositionInformation => {
ClientDriveSetInformationResponse::new(&rdp_req, NTSTATUS::STATUS_SUCCESS)
| FileInformationClassLevel::FileAllocationInformation => {
// 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
self.prep_set_info_response(&rdp_req, NTSTATUS::STATUS_SUCCESS)
}
_ => {
return Err(not_implemented_error(&format!(
// TODO(isaiah) or TODO(lkozlowski): implement FileDispositionInformation as is the case in FreeRDP.
// Remove the #[allow(clippy::wildcard_in_or_patterns)] macro above this function once completed.
FileInformationClassLevel::FileDispositionInformation | _ => {
Err(not_implemented_error(&format!(
"support for ServerDriveSetInformationRequest with fs_info_class_lvl = {:?} is not implemented",
rdp_req.file_information_class_level
)));
)))
}
};
debug!("sending RDP: {:?}", resp);
let resp = self
.add_headers_and_chunkify(PacketId::PAKID_CORE_DEVICE_IOCOMPLETION, resp.encode()?)?;
Ok(resp)
}
}
pub fn write_client_device_list_announce<S: Read + Write>(
@@ -955,6 +968,30 @@ impl Client {
)))
}
pub fn handle_tdp_sd_move_response<S: Read + Write>(
&mut self,
res: SharedDirectoryMoveResponse,
mcs: &mut mcs::Client<S>,
) -> RdpResult<()> {
debug!("received TDP SharedDirectoryMoveResponse: {:?}", res);
if let Some(tdp_resp_handler) = self
.pending_sd_move_resp_handlers
.remove(&res.completion_id)
{
let rdp_responses = tdp_resp_handler(self, res)?;
let chan = &CHANNEL_NAME.to_string();
for resp in rdp_responses {
mcs.write(chan, resp)?;
}
return Ok(());
}
Err(try_error(&format!(
"received invalid completion id: {}",
res.completion_id
)))
}
fn prep_device_create_response(
&mut self,
req: &DeviceCreateRequest,
@@ -1134,6 +1171,18 @@ impl Client {
Ok(resp)
}
fn prep_set_info_response(
&mut self,
req: &ServerDriveSetInformationRequest,
io_status: NTSTATUS,
) -> RdpResult<Vec<Vec<u8>>> {
let resp = ClientDriveSetInformationResponse::new(req, io_status);
debug!("sending RDP: {:?}", resp);
let resp = self
.add_headers_and_chunkify(PacketId::PAKID_CORE_DEVICE_IOCOMPLETION, resp.encode()?)?;
Ok(resp)
}
/// Helper function for sending a TDP SharedDirectoryCreateRequest based on an
/// RDP DeviceCreateRequest and handling the TDP SharedDirectoryCreateResponse.
fn tdp_sd_create(
@@ -1316,6 +1365,97 @@ impl Client {
self.prep_write_response(rdp_req.device_io_request, NTSTATUS::STATUS_UNSUCCESSFUL, 0)
}
fn rename(
&mut self,
rdp_req: ServerDriveSetInformationRequest,
rename_info: &FileRenameInformation,
) -> RdpResult<Vec<Vec<u8>>> {
// https://github.com/FreeRDP/FreeRDP/blob/dfa231c0a55b005af775b833f92f6bcd30363d77/channels/drive/client/drive_file.c#L709
match rename_info.replace_if_exists {
Boolean::True => self.rename_replace_if_exists(rdp_req, rename_info),
Boolean::False => self.rename_dont_replace_if_exists(rdp_req, rename_info),
}
}
fn rename_replace_if_exists(
&mut self,
rdp_req: ServerDriveSetInformationRequest,
rename_info: &FileRenameInformation,
) -> RdpResult<Vec<Vec<u8>>> {
// If replace_if_exists is true, we can just send a TDP SharedDirectoryMoveRequest,
// which works like the unix `mv` utility (meaning it will automatically replace if exists).
self.tdp_sd_move(rdp_req, rename_info)
}
fn rename_dont_replace_if_exists(
&mut self,
rdp_req: ServerDriveSetInformationRequest,
rename_info: &FileRenameInformation,
) -> RdpResult<Vec<Vec<u8>>> {
let new_path = UnixPath::from(&rename_info.file_name);
// If replace_if_exists is false, first check if the new_path exists.
(self.tdp_sd_info_request)(SharedDirectoryInfoRequest {
completion_id: rdp_req.device_io_request.completion_id,
directory_id: rdp_req.device_io_request.device_id,
path: new_path,
})?;
let rename_info = (*rename_info).clone();
self.pending_sd_info_resp_handlers.insert(
rdp_req.device_io_request.completion_id,
Box::new(
move |cli: &mut Self,
res: SharedDirectoryInfoResponse|
-> RdpResult<Vec<Vec<u8>>> {
if res.err_code == TdpErrCode::DoesNotExist {
// If the file doesn't already exist, send a move request.
return cli.tdp_sd_move(rdp_req, &rename_info);
}
// If it does, send back a name collision error, as is done in FreeRDP.
cli.prep_set_info_response(&rdp_req, NTSTATUS::STATUS_OBJECT_NAME_COLLISION)
},
),
);
Ok(vec![])
}
fn tdp_sd_move(
&mut self,
rdp_req: ServerDriveSetInformationRequest,
rename_info: &FileRenameInformation,
) -> RdpResult<Vec<Vec<u8>>> {
if let Some(file) = self.file_cache.get(rdp_req.device_io_request.file_id) {
(self.tdp_sd_move_request)(SharedDirectoryMoveRequest {
completion_id: rdp_req.device_io_request.completion_id,
directory_id: rdp_req.device_io_request.device_id,
original_path: file.path.clone(),
new_path: UnixPath::from(&rename_info.file_name),
})?;
self.pending_sd_move_resp_handlers.insert(
rdp_req.device_io_request.completion_id,
Box::new(
move |cli: &mut Self,
res: SharedDirectoryMoveResponse|
-> RdpResult<Vec<Vec<u8>>> {
if res.err_code != TdpErrCode::Nil {
return cli
.prep_set_info_response(&rdp_req, NTSTATUS::STATUS_UNSUCCESSFUL);
}
cli.prep_set_info_response(&rdp_req, NTSTATUS::STATUS_SUCCESS)
},
),
);
return Ok(vec![]);
}
// File not found in cache
self.prep_set_info_response(&rdp_req, NTSTATUS::STATUS_UNSUCCESSFUL)
}
/// add_headers_and_chunkify takes an encoded PDU ready to be sent over a virtual channel (payload),
/// adds on the Shared Header based the passed packet_id, adds the appropriate (virtual) Channel PDU Header,
/// and splits the entire payload into chunks if the payload exceeds the maximum size.
@@ -1371,7 +1511,7 @@ impl Client {
/// | -------- | ------------- | ---------------------------------------------------------|
/// | 3 | IRP_MJ_CLOSE | The FCO is deleted from the cache |
/// | -------- | ------------- | ---------------------------------------------------------|
#[derive(Debug)]
#[derive(Debug, Clone)]
struct FileCacheObject {
path: UnixPath,
delete_pending: bool,
@@ -2236,9 +2376,8 @@ struct ServerDriveQueryInformationRequest {
impl ServerDriveQueryInformationRequest {
fn decode(device_io_request: DeviceIoRequest, payload: &mut Payload) -> RdpResult<Self> {
if let Some(file_info_class_lvl) =
FileInformationClassLevel::from_u32(payload.read_u32::<LittleEndian>()?)
{
let n = payload.read_u32::<LittleEndian>()?;
if let Some(file_info_class_lvl) = FileInformationClassLevel::from_u32(n) {
return Ok(Self {
device_io_request,
file_info_class_lvl,
@@ -2246,14 +2385,18 @@ impl ServerDriveQueryInformationRequest {
}
Err(invalid_data_error(
"received invalid FileInformationClass in ServerDriveQueryInformationRequest",
format!(
"received invalid FileInformationClass in ServerDriveQueryInformationRequest: {}",
n
)
.as_str(),
))
}
}
/// 2.4 File Information Classes [MS-FSCC]
/// https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-fscc/4718fc40-e539-4014-8e33-b675af74e3e1
#[derive(Debug)]
#[derive(Debug, Clone)]
#[allow(dead_code, clippy::enum_variant_names)]
enum FileInformationClass {
FileBasicInformation(FileBasicInformation),
@@ -2336,7 +2479,7 @@ impl FileInformationClass {
/// 2.4.7 FileBasicInformation [MS-FSCC]
/// https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-fscc/16023025-8a78-492f-8b96-c873b042ac50
#[derive(Debug)]
#[derive(Debug, Clone)]
struct FileBasicInformation {
creation_time: i64,
last_access_time: i64,
@@ -2385,7 +2528,7 @@ impl FileBasicInformation {
/// 2.4.41 FileStandardInformation [MS-FSCC]
/// https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-fscc/5afa7f66-619c-48f3-955f-68c4ece704ae
#[derive(Debug)]
#[derive(Debug, Clone)]
struct FileStandardInformation {
/// A 64-bit signed integer that contains the file allocation size, in bytes. The value of this field MUST be an
/// integer multiple of the cluster size.
@@ -2437,7 +2580,7 @@ impl FileStandardInformation {
/// 2.4.6 FileAttributeTagInformation [MS-FSCC]
/// https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-fscc/d295752f-ce89-4b98-8553-266d37c84f0e?redirectedfrom=MSDN
#[derive(Debug)]
#[derive(Debug, Clone)]
struct FileAttributeTagInformation {
file_attributes: flags::FileAttributes,
reparse_tag: u32,
@@ -2460,7 +2603,7 @@ impl FileAttributeTagInformation {
/// 2.1.8 Boolean
/// https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-fscc/8ce7b38c-d3cc-415d-ab39-944000ea77ff
#[derive(Debug, FromPrimitive, ToPrimitive)]
#[derive(Debug, FromPrimitive, ToPrimitive, PartialEq, Clone)]
#[repr(u8)]
enum Boolean {
True = 1,
@@ -2469,7 +2612,7 @@ enum Boolean {
/// 2.4.8 FileBothDirectoryInformation
/// https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-fscc/270df317-9ba5-4ccb-ba00-8d22be139bc5
#[derive(Debug)]
#[derive(Debug, Clone)]
struct FileBothDirectoryInformation {
next_entry_offset: u32,
file_index: u32,
@@ -2572,7 +2715,7 @@ impl FileBothDirectoryInformation {
/// 2.4.14 FileFullDirectoryInformation
/// https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-fscc/e8d926d1-3a22-4654-be9c-58317a85540b
#[derive(Debug)]
#[derive(Debug, Clone)]
struct FileFullDirectoryInformation {
next_entry_offset: u32,
file_index: u32,
@@ -2666,7 +2809,7 @@ impl FileFullDirectoryInformation {
// 2.4.13 FileEndOfFileInformation
// https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-fscc/75241cca-3167-472f-8058-a52d77c6bb17
#[derive(Debug)]
#[derive(Debug, Clone)]
struct FileEndOfFileInformation {
end_of_file: i64,
}
@@ -2692,7 +2835,7 @@ impl FileEndOfFileInformation {
// 2.4.11 FileDispositionInformation
// https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-fscc/12c3dd1c-14f6-4229-9d29-75fb2cb392f6
#[derive(Debug)]
#[derive(Debug, Clone)]
struct FileDispositionInformation {
delete_pending: u8,
}
@@ -2718,10 +2861,11 @@ impl FileDispositionInformation {
// 2.4.37 FileRenameInformation
// https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-fscc/1d2673a8-8fb9-4868-920a-775ccaa30cf8
#[derive(Debug)]
#[derive(Debug, Clone)]
struct FileRenameInformation {
replace_if_exists: Boolean,
file_name: String,
/// file_name is the relative path to the new location of the file
file_name: WindowsPath,
}
impl FileRenameInformation {
@@ -2738,7 +2882,7 @@ impl FileRenameInformation {
// RootDirectory. For network operations, this value MUST be zero.
w.write_u8(0)?;
w.write_u32::<LittleEndian>(self.file_name.len() as u32)?;
w.extend_from_slice(&util::to_unicode(&self.file_name, false));
w.extend_from_slice(&util::to_unicode(&self.file_name.path, false));
Ok(w)
}
@@ -2750,7 +2894,7 @@ impl FileRenameInformation {
let file_name_length = payload.read_u32::<LittleEndian>()?;
let mut file_name = vec![0u8; file_name_length as usize];
payload.read_exact(&mut file_name)?;
let file_name = util::from_unicode(file_name)?;
let file_name = WindowsPath::from(util::from_unicode(file_name)?);
Ok(Self {
replace_if_exists: Boolean::from_u8(replace_if_exists).unwrap(),
@@ -2765,7 +2909,7 @@ impl FileRenameInformation {
// 2.4.4 FileAllocationInformation
// https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-fscc/0201c69b-50db-412d-bab3-dd97aeede13b
#[derive(Debug)]
#[derive(Debug, Clone)]
struct FileAllocationInformation {
allocation_size: i64,
}
@@ -3388,7 +3532,7 @@ impl ClientDriveSetInformationResponse {
/// 2.2.3.3.9 Server Drive Set Information Request (DR_DRIVE_SET_INFORMATION_REQ)
/// https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-rdpefs/b5d3104b-0e42-4cf8-9059-e9fe86615e5c
#[derive(Debug)]
#[derive(Debug, Clone)]
struct ServerDriveSetInformationRequest {
/// The MajorFunction field in the DR_DEVICE_IOREQUEST header MUST be set to IRP_MJ_SET_INFORMATION.
device_io_request: DeviceIoRequest,
@@ -3743,6 +3887,7 @@ type SharedDirectoryDeleteRequestSender =
type SharedDirectoryListRequestSender = Box<dyn Fn(SharedDirectoryListRequest) -> RdpResult<()>>;
type SharedDirectoryReadRequestSender = Box<dyn Fn(SharedDirectoryReadRequest) -> RdpResult<()>>;
type SharedDirectoryWriteRequestSender = Box<dyn Fn(SharedDirectoryWriteRequest) -> RdpResult<()>>;
type SharedDirectoryMoveRequestSender = Box<dyn Fn(SharedDirectoryMoveRequest) -> RdpResult<()>>;
type SharedDirectoryInfoResponseHandler =
Box<dyn FnOnce(&mut Client, SharedDirectoryInfoResponse) -> RdpResult<Vec<Vec<u8>>>>;
@@ -3756,6 +3901,8 @@ type SharedDirectoryReadResponseHandler =
Box<dyn FnOnce(&mut Client, SharedDirectoryReadResponse) -> RdpResult<Vec<Vec<u8>>>>;
type SharedDirectoryWriteResponseHandler =
Box<dyn FnOnce(&mut Client, SharedDirectoryWriteResponse) -> RdpResult<Vec<Vec<u8>>>>;
type SharedDirectoryMoveResponseHandler =
Box<dyn FnOnce(&mut Client, SharedDirectoryMoveResponse) -> RdpResult<Vec<Vec<u8>>>>;
#[cfg(test)]
mod tests {
@@ -26,7 +26,13 @@ use std::ffi::{CString, NulError};
/// r"2018\January.xlsx": A relative path to a file in a subdirectory of the current directory.
#[derive(Debug, Clone)]
pub struct WindowsPath {
path: String,
pub path: String,
}
impl WindowsPath {
pub fn len(&self) -> u32 {
self.path.len() as u32
}
}
impl From<String> for WindowsPath {
+90 -16
View File
@@ -67,6 +67,8 @@ const (
TypeSharedDirectoryReadResponse = MessageType(20)
TypeSharedDirectoryWriteRequest = MessageType(21)
TypeSharedDirectoryWriteResponse = MessageType(22)
TypeSharedDirectoryMoveRequest = MessageType(23)
TypeSharedDirectoryMoveResponse = MessageType(24)
TypeSharedDirectoryListRequest = MessageType(25)
TypeSharedDirectoryListResponse = MessageType(26)
)
@@ -149,6 +151,10 @@ func decode(in peekReader) (Message, error) {
return decodeSharedDirectoryWriteRequest(in)
case TypeSharedDirectoryWriteResponse:
return decodeSharedDirectoryWriteResponse(in)
case TypeSharedDirectoryMoveRequest:
return decodeSharedDirectoryMoveRequest(in)
case TypeSharedDirectoryMoveResponse:
return decodeSharedDirectoryMoveResponse(in)
default:
return nil, trace.BadParameter("unsupported desktop protocol message type %d", t)
}
@@ -1120,7 +1126,6 @@ type SharedDirectoryReadRequest struct {
CompletionID uint32
DirectoryID uint32
Path string
PathLength uint32
Offset uint64
Length uint32
}
@@ -1149,7 +1154,7 @@ func decodeSharedDirectoryReadRequest(in peekReader) (SharedDirectoryReadRequest
return SharedDirectoryReadRequest{}, trace.BadParameter("got message type %v, expected TypeSharedDirectoryReadRequest(%v)", t, TypeSharedDirectoryReadRequest)
}
var completionID, directoryID, pathLength, length uint32
var completionID, directoryID, length uint32
var offset uint64
err = binary.Read(in, binary.BigEndian, &completionID)
@@ -1167,11 +1172,6 @@ func decodeSharedDirectoryReadRequest(in peekReader) (SharedDirectoryReadRequest
return SharedDirectoryReadRequest{}, trace.Wrap(err)
}
err = binary.Read(in, binary.BigEndian, &pathLength)
if err != nil {
return SharedDirectoryReadRequest{}, trace.Wrap(err)
}
err = binary.Read(in, binary.BigEndian, &offset)
if err != nil {
return SharedDirectoryReadRequest{}, trace.Wrap(err)
@@ -1186,7 +1186,6 @@ func decodeSharedDirectoryReadRequest(in peekReader) (SharedDirectoryReadRequest
CompletionID: completionID,
DirectoryID: directoryID,
Path: path,
PathLength: pathLength,
Offset: offset,
Length: length,
}, nil
@@ -1259,7 +1258,6 @@ type SharedDirectoryWriteRequest struct {
DirectoryID uint32
Offset uint64
Path string
PathLength uint32
WriteDataLength uint32
WriteData []byte
}
@@ -1291,7 +1289,7 @@ func decodeSharedDirectoryWriteRequest(in peekReader) (SharedDirectoryWriteReque
return SharedDirectoryWriteRequest{}, trace.BadParameter("got message type %v, expected TypeSharedDirectoryWriteRequest(%v)", t, TypeSharedDirectoryWriteRequest)
}
var completionID, directoryID, pathLength, writeDataLength uint32
var completionID, directoryID, writeDataLength uint32
var offset uint64
err = binary.Read(in, binary.BigEndian, &completionID)
@@ -1314,11 +1312,6 @@ func decodeSharedDirectoryWriteRequest(in peekReader) (SharedDirectoryWriteReque
return SharedDirectoryWriteRequest{}, trace.Wrap(err)
}
err = binary.Read(in, binary.BigEndian, &pathLength)
if err != nil {
return SharedDirectoryWriteRequest{}, trace.Wrap(err)
}
err = binary.Read(in, binary.BigEndian, &writeDataLength)
if err != nil {
return SharedDirectoryWriteRequest{}, trace.Wrap(err)
@@ -1333,7 +1326,6 @@ func decodeSharedDirectoryWriteRequest(in peekReader) (SharedDirectoryWriteReque
CompletionID: completionID,
DirectoryID: directoryID,
Path: path,
PathLength: pathLength,
Offset: offset,
WriteDataLength: writeDataLength,
WriteData: writeData,
@@ -1370,6 +1362,88 @@ func decodeSharedDirectoryWriteResponse(in peekReader) (SharedDirectoryWriteResp
return res, err
}
// SharedDirectoryMoveRequest is sent from the TDP server to the client
// to request a file at original_path be moved to new_path.
type SharedDirectoryMoveRequest struct {
CompletionID uint32
DirectoryID uint32
OriginalPath string
NewPath string
}
func (s SharedDirectoryMoveRequest) Encode() ([]byte, error) {
buf := new(bytes.Buffer)
buf.WriteByte(byte(TypeSharedDirectoryMoveRequest))
binary.Write(buf, binary.BigEndian, s.CompletionID)
binary.Write(buf, binary.BigEndian, s.DirectoryID)
if err := encodeString(buf, s.OriginalPath); err != nil {
return nil, trace.Wrap(err)
}
if err := encodeString(buf, s.NewPath); err != nil {
return nil, trace.Wrap(err)
}
return buf.Bytes(), nil
}
func decodeSharedDirectoryMoveRequest(in peekReader) (SharedDirectoryMoveRequest, error) {
t, err := in.ReadByte()
if err != nil {
return SharedDirectoryMoveRequest{}, trace.Wrap(err)
}
if t != byte(TypeSharedDirectoryMoveRequest) {
return SharedDirectoryMoveRequest{}, trace.BadParameter("got message type %v, expected TypeClientUsername(%v)", t, TypeClientUsername)
}
var completionID, directoryID uint32
err = binary.Read(in, binary.BigEndian, &completionID)
if err != nil {
return SharedDirectoryMoveRequest{}, trace.Wrap(err)
}
err = binary.Read(in, binary.BigEndian, &directoryID)
if err != nil {
return SharedDirectoryMoveRequest{}, trace.Wrap(err)
}
originalPath, err := decodeString(in, windowsMaxUsernameLength)
if err != nil {
return SharedDirectoryMoveRequest{}, trace.Wrap(err)
}
newPath, err := decodeString(in, windowsMaxUsernameLength)
if err != nil {
return SharedDirectoryMoveRequest{}, trace.Wrap(err)
}
return SharedDirectoryMoveRequest{
CompletionID: completionID,
DirectoryID: directoryID,
OriginalPath: originalPath,
NewPath: newPath,
}, nil
}
type SharedDirectoryMoveResponse struct {
CompletionID uint32
ErrCode uint32
}
func (s SharedDirectoryMoveResponse) Encode() ([]byte, error) {
buf := new(bytes.Buffer)
buf.WriteByte(byte(TypeSharedDirectoryMoveResponse))
binary.Write(buf, binary.BigEndian, s)
return buf.Bytes(), nil
}
func decodeSharedDirectoryMoveResponse(in peekReader) (SharedDirectoryMoveResponse, error) {
t, err := in.ReadByte()
if err != nil {
return SharedDirectoryMoveResponse{}, trace.Wrap(err)
}
if t != byte(TypeSharedDirectoryMoveResponse) {
return SharedDirectoryMoveResponse{}, trace.BadParameter("got message type %v, expected SharedDirectoryMoveResponse(%v)", t, TypeSharedDirectoryMoveResponse)
}
var res SharedDirectoryMoveResponse
err = binary.Read(in, binary.BigEndian, &res)
return res, err
}
// encodeString encodes strings for TDP. Strings are encoded as UTF-8 with
// a 32-bit length prefix (in bytes):
// https://github.com/gravitational/teleport/blob/master/rfd/0037-desktop-access-protocol.md#field-types