From c2f57f5bcb1fd5c2c41922a39d26a2ec709759e4 Mon Sep 17 00:00:00 2001 From: Isaiah Becker-Mayer Date: Thu, 2 Nov 2023 04:54:03 +0000 Subject: [PATCH] feat(rdpdr): handling for DeviceCloseRequest and DeviceCloseResponse (#257) --- crates/ironrdp-rdpdr/src/lib.rs | 12 +++-- crates/ironrdp-rdpdr/src/pdu/efs.rs | 80 ++++++++++++++++++++++++++--- crates/ironrdp-rdpdr/src/pdu/mod.rs | 26 ++++++++-- 3 files changed, 103 insertions(+), 15 deletions(-) diff --git a/crates/ironrdp-rdpdr/src/lib.rs b/crates/ironrdp-rdpdr/src/lib.rs index a9c49738..67420fd4 100644 --- a/crates/ironrdp-rdpdr/src/lib.rs +++ b/crates/ironrdp-rdpdr/src/lib.rs @@ -38,7 +38,10 @@ pub use self::backend::RdpdrBackend; /// [\[MS-RDPEFS\] Appendix A<1>]: https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-rdpefs/fd28bfd9-dae2-4a78-abe1-b4efa208b7aa#Appendix_A_1 #[derive(Debug)] pub struct Rdpdr { - /// TODO: explain what this is + /// The name of the computer that is running the client. + /// + /// Any directories shared will be displayed by File Explorer + /// as " on ". computer_name: String, capabilities: Capabilities, /// Pre-configured list of devices to announce to the server. @@ -54,6 +57,8 @@ impl Rdpdr { pub const NAME: ChannelName = ChannelName::from_static(b"rdpdr\0\0\0"); /// Creates a new [`Rdpdr`]. + /// + /// See [`Rdpdr::computer_name`]. pub fn new(backend: Box, computer_name: String) -> Self { Self { computer_name, @@ -212,9 +217,8 @@ impl StaticVirtualChannelProcessor for Rdpdr { | RdpdrPdu::CoreCapability(_) | RdpdrPdu::DeviceControlResponse(_) | RdpdrPdu::DeviceCreateResponse(_) - | RdpdrPdu::ClientDriveQueryInformationResponse(_) => { - Err(other_err!("Rdpdr", "received unexpected packet")) - } + | RdpdrPdu::ClientDriveQueryInformationResponse(_) + | RdpdrPdu::DeviceCloseResponse(_) => Err(other_err!("Rdpdr", "received unexpected packet")), } } } diff --git a/crates/ironrdp-rdpdr/src/pdu/efs.rs b/crates/ironrdp-rdpdr/src/pdu/efs.rs index 32dbf3ea..008096f2 100644 --- a/crates/ironrdp-rdpdr/src/pdu/efs.rs +++ b/crates/ironrdp-rdpdr/src/pdu/efs.rs @@ -1346,23 +1346,20 @@ impl DeviceIoResponse { pub enum ServerDriveIoRequest { ServerCreateDriveRequest(DeviceCreateRequest), ServerDriveQueryInformationRequest(ServerDriveQueryInformationRequest), + DeviceCloseRequest(DeviceCloseRequest), } impl ServerDriveIoRequest { pub fn decode(dev_io_req: DeviceIoRequest, src: &mut ReadCursor<'_>) -> PduResult { match dev_io_req.major_function { - MajorFunction::Create => Ok(Self::ServerCreateDriveRequest(DeviceCreateRequest::decode( - dev_io_req, src, - )?)), - MajorFunction::Close => todo!(), + MajorFunction::Create => Ok(DeviceCreateRequest::decode(dev_io_req, src)?.into()), + MajorFunction::Close => Ok(DeviceCloseRequest::decode(dev_io_req).into()), MajorFunction::Read => todo!(), MajorFunction::Write => todo!(), MajorFunction::DeviceControl => todo!(), MajorFunction::QueryVolumeInformation => todo!(), MajorFunction::SetVolumeInformation => todo!(), - MajorFunction::QueryInformation => Ok(Self::ServerDriveQueryInformationRequest( - ServerDriveQueryInformationRequest::decode(dev_io_req, src)?, - )), + MajorFunction::QueryInformation => Ok(ServerDriveQueryInformationRequest::decode(dev_io_req, src)?.into()), MajorFunction::SetInformation => todo!(), MajorFunction::DirectoryControl => todo!(), MajorFunction::LockControl => todo!(), @@ -1370,6 +1367,24 @@ impl ServerDriveIoRequest { } } +impl From for ServerDriveIoRequest { + fn from(req: DeviceCreateRequest) -> Self { + Self::ServerCreateDriveRequest(req) + } +} + +impl From for ServerDriveIoRequest { + fn from(req: ServerDriveQueryInformationRequest) -> Self { + Self::ServerDriveQueryInformationRequest(req) + } +} + +impl From for ServerDriveIoRequest { + fn from(req: DeviceCloseRequest) -> Self { + Self::DeviceCloseRequest(req) + } +} + /// [2.2.3.3.1] Server Create Drive Request (DR_DRIVE_CREATE_REQ) /// and [2.2.1.4.1] Device Create Request (DR_CREATE_REQ) /// @@ -1408,7 +1423,9 @@ impl DeviceCreateRequest { let path_length: usize = cast_length!("DeviceCreateRequest", "path_length", src.read_u32())?; ensure_size!(ctx: "DeviceCreateRequest", in: src, size: path_length); - let path = from_utf16_bytes(src.read_slice(path_length)); + let path = from_utf16_bytes(src.read_slice(path_length)) + .trim_end_matches('\0') + .into(); Ok(Self { device_io_request: dev_io_req, @@ -1867,3 +1884,50 @@ impl FileAttributeTagInformation { + 4 // ReparseTag } } + +/// [2.2.1.4.2] Device Close Request (DR_CLOSE_REQ) +/// +/// [2.2.1.4.2]: https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-rdpefs/3ec6627f-9e0f-4941-a828-3fc6ed63d9e7 +#[derive(Debug)] +pub struct DeviceCloseRequest { + pub device_io_request: DeviceIoRequest, + // Padding (32 bytes): ignored as per FreeRDP: + // https://github.com/FreeRDP/FreeRDP/blob/511444a65e7aa2f537c5e531fa68157a50c1bd4d/channels/drive/client/drive_main.c#L236 +} + +impl DeviceCloseRequest { + pub fn decode(dev_io_req: DeviceIoRequest) -> Self { + Self { + device_io_request: dev_io_req, + } + } +} + +/// [2.2.1.5.2] Device Close Response (DR_CLOSE_RSP) +/// +/// [2.2.1.5.2]: https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-rdpefs/0dae7031-cfd8-4f14-908c-ec06e14997b5 +#[derive(Debug)] +pub struct DeviceCloseResponse { + pub device_io_response: DeviceIoResponse, + // Padding (4 bytes): An array of 4 bytes. Reserved. This field can be set to any value and MUST be ignored. +} + +impl DeviceCloseResponse { + const NAME: &str = "DR_CLOSE_RSP"; + + pub fn name(&self) -> &'static str { + Self::NAME + } + + pub fn encode(&self, dst: &mut WriteCursor<'_>) -> PduResult<()> { + ensure_size!(in: dst, size: self.size()); + self.device_io_response.encode(dst)?; + dst.write_u32(0); // Padding + Ok(()) + } + + pub fn size(&self) -> usize { + self.device_io_response.size() // DeviceIoResponse + + 4 // Padding + } +} diff --git a/crates/ironrdp-rdpdr/src/pdu/mod.rs b/crates/ironrdp-rdpdr/src/pdu/mod.rs index 2926d97d..2fbfa585 100644 --- a/crates/ironrdp-rdpdr/src/pdu/mod.rs +++ b/crates/ironrdp-rdpdr/src/pdu/mod.rs @@ -6,8 +6,8 @@ use ironrdp_pdu::{ensure_size, invalid_message_err, PduDecode, PduEncode, PduErr use self::efs::{ ClientDeviceListAnnounce, ClientDriveQueryInformationResponse, ClientNameRequest, CoreCapability, - CoreCapabilityKind, DeviceControlResponse, DeviceCreateResponse, DeviceIoRequest, ServerDeviceAnnounceResponse, - VersionAndIdPdu, VersionAndIdPduKind, + CoreCapabilityKind, DeviceCloseResponse, DeviceControlResponse, DeviceCreateResponse, DeviceIoRequest, + ServerDeviceAnnounceResponse, VersionAndIdPdu, VersionAndIdPduKind, }; pub mod efs; @@ -24,6 +24,7 @@ pub enum RdpdrPdu { DeviceControlResponse(DeviceControlResponse), DeviceCreateResponse(DeviceCreateResponse), ClientDriveQueryInformationResponse(ClientDriveQueryInformationResponse), + DeviceCloseResponse(DeviceCloseResponse), /// TODO: temporary value for development, this should be removed Unimplemented, } @@ -74,7 +75,8 @@ impl RdpdrPdu { }, RdpdrPdu::DeviceControlResponse(_) | RdpdrPdu::DeviceCreateResponse(_) - | RdpdrPdu::ClientDriveQueryInformationResponse(_) => SharedHeader { + | RdpdrPdu::ClientDriveQueryInformationResponse(_) + | RdpdrPdu::DeviceCloseResponse(_) => SharedHeader { component: Component::RdpdrCtypCore, packet_id: PacketId::CoreDeviceIoCompletion, }, @@ -116,6 +118,7 @@ impl PduEncode for RdpdrPdu { RdpdrPdu::DeviceControlResponse(pdu) => pdu.encode(dst), RdpdrPdu::DeviceCreateResponse(pdu) => pdu.encode(dst), RdpdrPdu::ClientDriveQueryInformationResponse(pdu) => pdu.encode(dst), + RdpdrPdu::DeviceCloseResponse(pdu) => pdu.encode(dst), RdpdrPdu::Unimplemented => Ok(()), } } @@ -131,6 +134,7 @@ impl PduEncode for RdpdrPdu { RdpdrPdu::DeviceControlResponse(pdu) => pdu.name(), RdpdrPdu::DeviceCreateResponse(pdu) => pdu.name(), RdpdrPdu::ClientDriveQueryInformationResponse(pdu) => pdu.name(), + RdpdrPdu::DeviceCloseResponse(pdu) => pdu.name(), RdpdrPdu::Unimplemented => "Unimplemented", } } @@ -147,6 +151,7 @@ impl PduEncode for RdpdrPdu { RdpdrPdu::DeviceControlResponse(pdu) => pdu.size(), RdpdrPdu::DeviceCreateResponse(pdu) => pdu.size(), RdpdrPdu::ClientDriveQueryInformationResponse(pdu) => pdu.size(), + RdpdrPdu::DeviceCloseResponse(pdu) => pdu.size(), RdpdrPdu::Unimplemented => 0, } } @@ -182,6 +187,9 @@ impl fmt::Debug for RdpdrPdu { Self::ClientDriveQueryInformationResponse(it) => { write!(f, "RdpdrPdu({:?})", it) } + Self::DeviceCloseResponse(it) => { + write!(f, "RdpdrPdu({:?})", it) + } Self::Unimplemented => { write!(f, "RdpdrPdu::Unimplemented") } @@ -201,6 +209,18 @@ impl From for RdpdrPdu { } } +impl From for RdpdrPdu { + fn from(value: ClientDriveQueryInformationResponse) -> Self { + Self::ClientDriveQueryInformationResponse(value) + } +} + +impl From for RdpdrPdu { + fn from(value: DeviceCloseResponse) -> Self { + Self::DeviceCloseResponse(value) + } +} + /// [2.2.1.1] Shared Header (RDPDR_HEADER), a header that is shared by all RDPDR PDUs. /// /// [2.2.1.1]: https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-rdpefs/29d4108f-8163-4a67-8271-e48c4b9c2a7c