diff --git a/crates/ironrdp-rdpdr/src/backend/mod.rs b/crates/ironrdp-rdpdr/src/backend/mod.rs index 85f4e4f2..aa3d6dee 100644 --- a/crates/ironrdp-rdpdr/src/backend/mod.rs +++ b/crates/ironrdp-rdpdr/src/backend/mod.rs @@ -1,7 +1,7 @@ pub mod noop; use crate::pdu::{ - efs::{DeviceControlRequest, ServerDeviceAnnounceResponse}, + efs::{DeviceControlRequest, FilesystemRequest, ServerDeviceAnnounceResponse}, esc::{ScardCall, ScardIoCtlCode}, }; use core::fmt; @@ -12,4 +12,5 @@ use ironrdp_svc::AsAny; pub trait RdpdrBackend: AsAny + fmt::Debug + Send { fn handle_server_device_announce_response(&mut self, pdu: ServerDeviceAnnounceResponse) -> PduResult<()>; fn handle_scard_call(&mut self, req: DeviceControlRequest, call: ScardCall) -> PduResult<()>; + fn handle_fs_request(&mut self, req: FilesystemRequest) -> PduResult<()>; } diff --git a/crates/ironrdp-rdpdr/src/backend/noop.rs b/crates/ironrdp-rdpdr/src/backend/noop.rs index 94e04f06..3865733f 100644 --- a/crates/ironrdp-rdpdr/src/backend/noop.rs +++ b/crates/ironrdp-rdpdr/src/backend/noop.rs @@ -18,4 +18,7 @@ impl RdpdrBackend for NoopRdpdrBackend { fn handle_scard_call(&mut self, _req: DeviceControlRequest, _call: ScardCall) -> PduResult<()> { Ok(()) } + fn handle_fs_request(&mut self, _req: crate::pdu::efs::FilesystemRequest) -> PduResult<()> { + Ok(()) + } } diff --git a/crates/ironrdp-rdpdr/src/lib.rs b/crates/ironrdp-rdpdr/src/lib.rs index 4f72f9e0..54abc905 100644 --- a/crates/ironrdp-rdpdr/src/lib.rs +++ b/crates/ironrdp-rdpdr/src/lib.rs @@ -10,7 +10,7 @@ extern crate tracing; pub mod backend; pub mod pdu; -use crate::pdu::efs::MajorFunction; +use crate::pdu::efs::FilesystemRequest; pub use backend::{noop::NoopRdpdrBackend, RdpdrBackend}; use ironrdp_pdu::{cursor::ReadCursor, decode_cursor, gcc::ChannelName, other_err, PduResult}; use ironrdp_svc::{impl_as_any, CompressionCondition, StaticVirtualChannelProcessor, SvcMessage}; @@ -135,13 +135,13 @@ impl Rdpdr { fn handle_device_io_request( &mut self, - pdu: DeviceIoRequest, - payload: &mut ReadCursor<'_>, + dev_io_req: DeviceIoRequest, + src: &mut ReadCursor<'_>, ) -> PduResult> { - match self.device_list.for_device_type(pdu.device_id)? { + match self.device_list.for_device_type(dev_io_req.device_id)? { DeviceType::Smartcard => { - let req = DeviceControlRequest::::decode(pdu, payload)?; - let call = ScardCall::decode(req.io_control_code, payload)?; + let req = DeviceControlRequest::::decode(dev_io_req, src)?; + let call = ScardCall::decode(req.io_control_code, src)?; debug!(?req); debug!(?req.io_control_code, ?call); @@ -151,24 +151,17 @@ impl Rdpdr { Ok(Vec::new()) } DeviceType::Filesystem => { - debug!("received filesystem packet"); - match pdu.major_function { - MajorFunction::Create => todo!(), - MajorFunction::Close => todo!(), - MajorFunction::Read => todo!(), - MajorFunction::Write => todo!(), - MajorFunction::DeviceControl => todo!(), - MajorFunction::QueryVolumeInformation => todo!(), - MajorFunction::SetVolumeInformation => todo!(), - MajorFunction::QueryInformation => todo!(), - MajorFunction::SetInformation => todo!(), - MajorFunction::DirectoryControl => todo!(), - MajorFunction::LockControl => todo!(), - } + let req = FilesystemRequest::decode(dev_io_req, src)?; + + debug!(?req); + + self.backend.handle_fs_request(req)?; + + Ok(Vec::new()) } _ => { // This should never happen, as we only announce devices that we support. - warn!(?pdu, "received packet for unsupported device type"); + warn!(?dev_io_req, "received packet for unsupported device type"); Ok(Vec::new()) } } @@ -211,7 +204,8 @@ impl StaticVirtualChannelProcessor for Rdpdr { | RdpdrPdu::ClientDeviceListAnnounce(_) | RdpdrPdu::VersionAndIdPdu(_) | RdpdrPdu::CoreCapability(_) - | RdpdrPdu::DeviceControlResponse(_) => Err(other_err!("Rdpdr", "received unexpected packet")), + | RdpdrPdu::DeviceControlResponse(_) + | RdpdrPdu::DeviceCreateResponse(_) => 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 e2de4ec8..856df8ca 100644 --- a/crates/ironrdp-rdpdr/src/pdu/efs.rs +++ b/crates/ironrdp-rdpdr/src/pdu/efs.rs @@ -5,8 +5,9 @@ use super::esc::rpce; use super::{PacketId, SharedHeader}; use bitflags::bitflags; +use core::fmt; use ironrdp_pdu::cursor::{ReadCursor, WriteCursor}; -use ironrdp_pdu::utils::{encoded_str_len, write_string_to_cursor, CharacterSet}; +use ironrdp_pdu::utils::{encoded_str_len, from_utf16_bytes, write_string_to_cursor, CharacterSet}; use ironrdp_pdu::{cast_length, ensure_size, invalid_message_err, read_padding, write_padding, PduError, PduResult}; use std::fmt::Debug; use std::mem::size_of; @@ -952,7 +953,7 @@ impl ServerDeviceAnnounceResponse { pub fn decode(src: &mut ReadCursor<'_>) -> PduResult { ensure_size!(ctx: Self::NAME, in: src, size: Self::FIXED_PART_SIZE); let device_id = src.read_u32(); - let result_code = NtStatus::try_from(src.read_u32())?; + let result_code = NtStatus::from(src.read_u32()); Ok(Self { device_id, result_code }) } @@ -968,51 +969,66 @@ impl ServerDeviceAnnounceResponse { /// This enum includes some basic ones for communicating with the RDP server. /// /// [2.3.1 NTSTATUS Values]: https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-erref/596a1078-e883-4972-9bbc-49e60bebca55 -#[derive(Debug, PartialEq, Copy, Clone)] -#[repr(u32)] -pub enum NtStatus { - Success = 0x0000_0000, - Unsuccessful = 0xC000_0001, - NotImplemented = 0xC000_0002, - NoMoreFiles = 0x8000_0006, - ObjectNameCollision = 0xC000_0035, - AccessDenied = 0xC000_0022, - NotADirectory = 0xC000_0103, - NoSuchFile = 0xC000_000F, - NotSupported = 0xC000_00BB, - DirectoryNotEmpty = 0xC000_0101, +#[derive(Clone, Copy, PartialEq, Eq)] +pub struct NtStatus(u32); + +impl NtStatus { + /// STATUS_SUCCESS + pub const SUCCESS: Self = Self(0x0000_0000); + /// STATUS_UNSUCCESSFUL + pub const UNSUCCESSFUL: Self = Self(0xC000_0001); + /// STATUS_NOT_IMPLEMENTED + pub const NOT_IMPLEMENTED: Self = Self(0xC000_0002); + /// STATUS_NO_MORE_FILES + pub const NO_MORE_FILES: Self = Self(0x8000_0006); + /// STATUS_OBJECT_NAME_COLLISION + pub const OBJECT_NAME_COLLISION: Self = Self(0xC000_0035); + /// STATUS_ACCESS_DENIED + pub const ACCESS_DENIED: Self = Self(0xC000_0022); + /// STATUS_NOT_A_DIRECTORY + pub const NOT_A_DIRECTORY: Self = Self(0xC000_0103); + /// STATUS_NO_SUCH_FILE + pub const NO_SUCH_FILE: Self = Self(0xC000_000F); + /// STATUS_NOT_SUPPORTED + pub const NOT_SUPPORTED: Self = Self(0xC000_00BB); + /// STATUS_DIRECTORY_NOT_EMPTY + pub const DIRECTORY_NOT_EMPTY: Self = Self(0xC000_0101); } -impl TryFrom for NtStatus { - type Error = PduError; - - fn try_from(value: u32) -> Result { - match value { - 0x0000_0000 => Ok(NtStatus::Success), - 0xC000_0001 => Ok(NtStatus::Unsuccessful), - 0xC000_0002 => Ok(NtStatus::NotImplemented), - 0x8000_0006 => Ok(NtStatus::NoMoreFiles), - 0xC000_0035 => Ok(NtStatus::ObjectNameCollision), - 0xC000_0022 => Ok(NtStatus::AccessDenied), - 0xC000_0103 => Ok(NtStatus::NotADirectory), - 0xC000_000F => Ok(NtStatus::NoSuchFile), - 0xC000_00BB => Ok(NtStatus::NotSupported), - 0xC000_0101 => Ok(NtStatus::DirectoryNotEmpty), - _ => Err(invalid_message_err!("try_from", "NtStatus", "unsupported value")), +impl Debug for NtStatus { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match *self { + NtStatus::SUCCESS => write!(f, "STATUS_SUCCESS"), + NtStatus::UNSUCCESSFUL => write!(f, "STATUS_UNSUCCESSFUL"), + NtStatus::NOT_IMPLEMENTED => write!(f, "STATUS_NOT_IMPLEMENTED"), + NtStatus::NO_MORE_FILES => write!(f, "STATUS_NO_MORE_FILES"), + NtStatus::OBJECT_NAME_COLLISION => write!(f, "STATUS_OBJECT_NAME_COLLISION"), + NtStatus::ACCESS_DENIED => write!(f, "STATUS_ACCESS_DENIED"), + NtStatus::NOT_A_DIRECTORY => write!(f, "STATUS_NOT_A_DIRECTORY"), + NtStatus::NO_SUCH_FILE => write!(f, "STATUS_NO_SUCH_FILE"), + NtStatus::NOT_SUPPORTED => write!(f, "STATUS_NOT_SUPPORTED"), + NtStatus::DIRECTORY_NOT_EMPTY => write!(f, "STATUS_DIRECTORY_NOT_EMPTY"), + _ => write!(f, "{:#010X}", self.0), } } } +impl From for NtStatus { + fn from(value: u32) -> Self { + Self(value) + } +} + impl From for u32 { fn from(status: NtStatus) -> Self { - status as u32 + status.0 } } /// [2.2.1.4 Device I/O Request (DR_DEVICE_IOREQUEST)] /// /// [2.2.1.4 Device I/O Request (DR_DEVICE_IOREQUEST)]: https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-rdpefs/a087ffa8-d0d5-4874-ac7b-0494f63e2d5d -#[derive(Debug)] +#[derive(Debug, Clone)] pub struct DeviceIoRequest { pub device_id: u32, pub file_id: u32, @@ -1265,11 +1281,19 @@ impl DeviceIoResponse { const NAME: &str = "DR_DEVICE_IOCOMPLETION"; const FIXED_PART_SIZE: usize = size_of::() * 3; // DeviceId, CompletionId, IoStatus + pub fn new(device_io_request: DeviceIoRequest, io_status: NtStatus) -> Self { + Self { + device_id: device_io_request.device_id, + completion_id: device_io_request.completion_id, + io_status, + } + } + pub fn decode(src: &mut ReadCursor<'_>) -> PduResult { ensure_size!(ctx: "DeviceIoResponse", in: src, size: Self::FIXED_PART_SIZE); let device_id = src.read_u32(); let completion_id = src.read_u32(); - let io_status = NtStatus::try_from(src.read_u32())?; + let io_status = NtStatus::from(src.read_u32()); Ok(Self { device_id, @@ -1290,3 +1314,276 @@ impl DeviceIoResponse { Self::FIXED_PART_SIZE } } + +#[derive(Debug)] +pub enum FilesystemRequest { + DeviceCreateRequest(DeviceCreateRequest), +} + +impl FilesystemRequest { + pub fn decode(dev_io_req: DeviceIoRequest, src: &mut ReadCursor<'_>) -> PduResult { + match dev_io_req.major_function { + MajorFunction::Create => Ok(Self::DeviceCreateRequest(DeviceCreateRequest::decode(dev_io_req, src)?)), + MajorFunction::Close => todo!(), + MajorFunction::Read => todo!(), + MajorFunction::Write => todo!(), + MajorFunction::DeviceControl => todo!(), + MajorFunction::QueryVolumeInformation => todo!(), + MajorFunction::SetVolumeInformation => todo!(), + MajorFunction::QueryInformation => todo!(), + MajorFunction::SetInformation => todo!(), + MajorFunction::DirectoryControl => todo!(), + MajorFunction::LockControl => todo!(), + } + } +} + +/// [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)] +/// +/// [2.2.3.3.1 Server Create Drive Request (DR_DRIVE_CREATE_REQ)]: https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-rdpefs/95b16fd0-d530-407c-a310-adedc85e9897 +/// [2.2.1.4.1 Device Create Request (DR_CREATE_REQ)]: https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-rdpefs/5f71f6d2-d9ff-40c2-bdb5-a739447d3c3e +#[derive(Debug)] +pub struct DeviceCreateRequest { + /// The MajorFunction field in this header MUST be set to IRP_MJ_CREATE. + pub device_io_request: DeviceIoRequest, + pub desired_access: DesiredAccess, + pub allocation_size: u64, + pub file_attributes: FileAttributes, + pub shared_access: SharedAccess, + pub create_disposition: CreateDisposition, + pub create_options: CreateOptions, + pub path: String, +} + +impl DeviceCreateRequest { + const FIXED_PART_SIZE: usize = 4 // DesiredAccess + + 8 // AllocationSize + + 4 // FileAttributes + + 4 // SharedAccess + + 4 // CreateDisposition + + 4 // CreateOptions + + 4; // PathLength + + fn decode(dev_io_req: DeviceIoRequest, src: &mut ReadCursor<'_>) -> PduResult { + ensure_size!(ctx: "DeviceCreateRequest", in: src, size: Self::FIXED_PART_SIZE); + let desired_access = DesiredAccess::from_bits_retain(src.read_u32()); + let allocation_size = src.read_u64(); + let file_attributes = FileAttributes::from_bits_retain(src.read_u32()); + let shared_access = SharedAccess::from_bits_retain(src.read_u32()); + let create_disposition = CreateDisposition::from_bits_retain(src.read_u32()); + let create_options = CreateOptions::from_bits_retain(src.read_u32()); + 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)); + + Ok(Self { + device_io_request: dev_io_req, + desired_access, + allocation_size, + file_attributes, + shared_access, + create_disposition, + create_options, + path, + }) + } +} + +bitflags! { + /// DesiredAccess can be interpreted as either + /// [2.2.13.1.1 File_Pipe_Printer_Access_Mask \[MS-SMB2\]] or [2.2.13.1.2 Directory_Access_Mask \[MS-SMB2\]] + /// + /// This implements the combination of the two. For flags where the names and/or functions are distinct between the two, + /// the names are appended with an "_OR_", and the File_Pipe_Printer_Access_Mask functionality is described on the top line comment, + /// and the Directory_Access_Mask functionality is described on the bottom (2nd) line comment. + /// + /// [2.2.13.1.1 File_Pipe_Printer_Access_Mask \[MS-SMB2\]]: https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-smb2/77b36d0f-6016-458a-a7a0-0f4a72ae1534 + /// [2.2.13.1.2 Directory_Access_Mask \[MS-SMB2\]]: https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-smb2/0a5934b1-80f1-4da0-b1bf-5e021c309b71 + #[derive(Debug, Clone)] + pub struct DesiredAccess: u32 { + /// This value indicates the right to read data from the file or named pipe. + /// + /// This value indicates the right to enumerate the contents of the directory. + const FILE_READ_DATA_OR_FILE_LIST_DIRECTORY = 0x00000001; + /// This value indicates the right to write data into the file or named pipe beyond the end of the file. + /// + /// This value indicates the right to create a file under the directory. + const FILE_WRITE_DATA_OR_FILE_ADD_FILE = 0x00000002; + /// This value indicates the right to append data into the file or named pipe. + /// + /// This value indicates the right to add a sub-directory under the directory. + const FILE_APPEND_DATA_OR_FILE_ADD_SUBDIRECTORY = 0x00000004; + /// This value indicates the right to read the extended attributes of the file or named pipe. + const FILE_READ_EA = 0x00000008; + /// This value indicates the right to write or change the extended attributes to the file or named pipe. + const FILE_WRITE_EA = 0x00000010; + /// This value indicates the right to traverse this directory if the server enforces traversal checking. + const FILE_TRAVERSE = 0x00000020; + /// This value indicates the right to delete entries within a directory. + const FILE_DELETE_CHILD = 0x00000040; + /// This value indicates the right to execute the file/directory. + const FILE_EXECUTE = 0x00000020; + /// This value indicates the right to read the attributes of the file/directory. + const FILE_READ_ATTRIBUTES = 0x00000080; + /// This value indicates the right to change the attributes of the file/directory. + const FILE_WRITE_ATTRIBUTES = 0x00000100; + /// This value indicates the right to delete the file/directory. + const DELETE = 0x00010000; + /// This value indicates the right to read the security descriptor for the file/directory or named pipe. + const READ_CONTROL = 0x00020000; + /// This value indicates the right to change the discretionary access control list (DACL) in the security descriptor for the file/directory or named pipe. For the DACL data pub structure, see ACL in [MS-DTYP]. + const WRITE_DAC = 0x00040000; + /// This value indicates the right to change the owner in the security descriptor for the file/directory or named pipe. + const WRITE_OWNER = 0x00080000; + /// SMB2 clients set this flag to any value. SMB2 servers SHOULD ignore this flag. + const SYNCHRONIZE = 0x00100000; + /// This value indicates the right to read or change the system access control list (SACL) in the security descriptor for the file/directory or named pipe. For the SACL data pub structure, see ACL in [MS-DTYP]. + const ACCESS_SYSTEM_SECURITY = 0x01000000; + /// This value indicates that the client is requesting an open to the file with the highest level of access the client has on this file. If no access is granted for the client on this file, the server MUST fail the open with STATUS_ACCESS_DENIED. + const MAXIMUM_ALLOWED = 0x02000000; + /// This value indicates a request for all the access flags that are previously listed except MAXIMUM_ALLOWED and ACCESS_SYSTEM_SECURITY. + const GENERIC_ALL = 0x10000000; + /// This value indicates a request for the following combination of access flags listed above: FILE_READ_ATTRIBUTES| FILE_EXECUTE| SYNCHRONIZE| READ_CONTROL. + const GENERIC_EXECUTE = 0x20000000; + /// This value indicates a request for the following combination of access flags listed above: FILE_WRITE_DATA| FILE_APPEND_DATA| FILE_WRITE_ATTRIBUTES| FILE_WRITE_EA| SYNCHRONIZE| READ_CONTROL. + const GENERIC_WRITE = 0x40000000; + /// This value indicates a request for the following combination of access flags listed above: FILE_READ_DATA| FILE_READ_ATTRIBUTES| FILE_READ_EA| SYNCHRONIZE| READ_CONTROL. + const GENERIC_READ = 0x80000000; + } +} + +bitflags! { + /// [2.6 File Attributes \[MS-FSCC\]] + /// + /// [2.6 File Attributes \[MS-FSCC\]]: https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-fscc/ca28ec38-f155-4768-81d6-4bfeb8586fc9 + #[derive(Debug, Clone)] + pub struct FileAttributes: u32 { + const FILE_ATTRIBUTE_READONLY = 0x00000001; + const FILE_ATTRIBUTE_HIDDEN = 0x00000002; + const FILE_ATTRIBUTE_SYSTEM = 0x00000004; + const FILE_ATTRIBUTE_DIRECTORY = 0x00000010; + const FILE_ATTRIBUTE_ARCHIVE = 0x00000020; + const FILE_ATTRIBUTE_NORMAL = 0x00000080; + const FILE_ATTRIBUTE_TEMPORARY = 0x00000100; + const FILE_ATTRIBUTE_SPARSE_FILE = 0x00000200; + const FILE_ATTRIBUTE_REPARSE_POINT = 0x00000400; + const FILE_ATTRIBUTE_COMPRESSED = 0x00000800; + const FILE_ATTRIBUTE_OFFLINE = 0x00001000; + const FILE_ATTRIBUTE_NOT_CONTENT_INDEXED = 0x00002000; + const FILE_ATTRIBUTE_ENCRYPTED = 0x00004000; + const FILE_ATTRIBUTE_INTEGRITY_STREAM = 0x00008000; + const FILE_ATTRIBUTE_NO_SCRUB_DATA = 0x00020000; + const FILE_ATTRIBUTE_RECALL_ON_OPEN = 0x00040000; + const FILE_ATTRIBUTE_PINNED = 0x00080000; + const FILE_ATTRIBUTE_UNPINNED = 0x00100000; + const FILE_ATTRIBUTE_RECALL_ON_DATA_ACCESS = 0x00400000; + } +} + +bitflags! { + /// Specified in [2.2.13 SMB2 CREATE Request] + /// + /// [2.2.13 SMB2 CREATE Request]: https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-smb2/e8fb45c1-a03d-44ca-b7ae-47385cfd7997 + #[derive(Debug, Clone)] + pub struct SharedAccess: u32 { + const FILE_SHARE_READ = 0x00000001; + const FILE_SHARE_WRITE = 0x00000002; + const FILE_SHARE_DELETE = 0x00000004; + } +} + +bitflags! { + /// Defined in [2.2.13 SMB2 CREATE Request] + /// + /// See FreeRDP's [drive_file.c] for context about how these should be interpreted. + /// + /// [2.2.13 SMB2 CREATE Request]: https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-smb2/e8fb45c1-a03d-44ca-b7ae-47385cfd7997 + /// [drive_file.c]: https://github.com/FreeRDP/FreeRDP/blob/511444a65e7aa2f537c5e531fa68157a50c1bd4d/channels/drive/client/drive_file.c#L207 + #[derive(PartialEq, Eq, Debug, Clone)] + pub struct CreateDisposition: u32 { + const FILE_SUPERSEDE = 0x00000000; + const FILE_OPEN = 0x00000001; + const FILE_CREATE = 0x00000002; + const FILE_OPEN_IF = 0x00000003; + const FILE_OVERWRITE = 0x00000004; + const FILE_OVERWRITE_IF = 0x00000005; + } +} + +bitflags! { + /// Defined in [2.2.13 SMB2 CREATE Request] + /// + /// [2.2.13 SMB2 CREATE Request]: https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-smb2/e8fb45c1-a03d-44ca-b7ae-47385cfd7997 + #[derive(Debug, Clone)] + pub struct CreateOptions: u32 { + const FILE_DIRECTORY_FILE = 0x00000001; + const FILE_WRITE_THROUGH = 0x00000002; + const FILE_SEQUENTIAL_ONLY = 0x00000004; + const FILE_NO_INTERMEDIATE_BUFFERING = 0x00000008; + const FILE_SYNCHRONOUS_IO_ALERT = 0x00000010; + const FILE_SYNCHRONOUS_IO_NONALERT = 0x00000020; + const FILE_NON_DIRECTORY_FILE = 0x00000040; + const FILE_COMPLETE_IF_OPLOCKED = 0x00000100; + const FILE_NO_EA_KNOWLEDGE = 0x00000200; + const FILE_RANDOM_ACCESS = 0x00000800; + const FILE_DELETE_ON_CLOSE = 0x00001000; + const FILE_OPEN_BY_FILE_ID = 0x00002000; + const FILE_OPEN_FOR_BACKUP_INTENT = 0x00004000; + const FILE_NO_COMPRESSION = 0x00008000; + const FILE_OPEN_REMOTE_INSTANCE = 0x00000400; + const FILE_OPEN_REQUIRING_OPLOCK = 0x00010000; + const FILE_DISALLOW_EXCLUSIVE = 0x00020000; + const FILE_RESERVE_OPFILTER = 0x00100000; + const FILE_OPEN_REPARSE_POINT = 0x00200000; + const FILE_OPEN_NO_RECALL = 0x00400000; + const FILE_OPEN_FOR_FREE_SPACE_QUERY = 0x00800000; + } +} + +/// [2.2.1.5.1 Device Create Response (DR_CREATE_RSP)] +/// +/// [2.2.1.5.1 Device Create Response (DR_CREATE_RSP)]: https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-rdpefs/99e5fca5-b37a-41e4-bc69-8d7da7860f76 +#[derive(Debug)] +pub struct DeviceCreateResponse { + pub device_io_reply: DeviceIoResponse, + pub file_id: u32, + pub information: Information, +} + +impl DeviceCreateResponse { + const NAME: &str = "DR_CREATE_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_reply.encode(dst)?; + dst.write_u32(self.file_id); + dst.write_u8(self.information.bits()); + Ok(()) + } + + pub fn size(&self) -> usize { + self.device_io_reply.size() // DeviceIoReply + + 4 // FileId + + 1 // Information + } +} + +bitflags! { + /// Defined in [2.2.1.5.1 Device Create Response (DR_CREATE_RSP)] + /// + /// [2.2.1.5.1 Device Create Response (DR_CREATE_RSP)]: https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-rdpefs/99e5fca5-b37a-41e4-bc69-8d7da7860f76 + #[derive(Debug)] + pub struct Information: u8 { + /// A new file was created. + const FILE_SUPERSEDED = 0x00000000; + /// An existing file was opened. + const FILE_OPENED = 0x00000001; + /// An existing file was overwritten. + const FILE_OVERWRITTEN = 0x00000003; + } +} diff --git a/crates/ironrdp-rdpdr/src/pdu/mod.rs b/crates/ironrdp-rdpdr/src/pdu/mod.rs index 4edbcd2a..b7c02a8e 100644 --- a/crates/ironrdp-rdpdr/src/pdu/mod.rs +++ b/crates/ironrdp-rdpdr/src/pdu/mod.rs @@ -6,7 +6,7 @@ use ironrdp_pdu::{ensure_size, invalid_message_err, PduDecode, PduEncode, PduErr use self::efs::{ ClientDeviceListAnnounce, ClientNameRequest, CoreCapability, CoreCapabilityKind, DeviceControlResponse, - DeviceIoRequest, ServerDeviceAnnounceResponse, VersionAndIdPdu, VersionAndIdPduKind, + DeviceCreateResponse, DeviceIoRequest, ServerDeviceAnnounceResponse, VersionAndIdPdu, VersionAndIdPduKind, }; pub mod efs; @@ -21,6 +21,7 @@ pub enum RdpdrPdu { ServerDeviceAnnounceResponse(ServerDeviceAnnounceResponse), DeviceIoRequest(DeviceIoRequest), DeviceControlResponse(DeviceControlResponse), + DeviceCreateResponse(DeviceCreateResponse), /// TODO: temporary value for development, this should be removed Unimplemented, } @@ -69,7 +70,7 @@ impl RdpdrPdu { component: Component::RdpdrCtypCore, packet_id: PacketId::CoreDeviceIoRequest, }, - RdpdrPdu::DeviceControlResponse(_) => SharedHeader { + RdpdrPdu::DeviceControlResponse(_) | RdpdrPdu::DeviceCreateResponse(_) => SharedHeader { component: Component::RdpdrCtypCore, packet_id: PacketId::CoreDeviceIoCompletion, }, @@ -109,6 +110,7 @@ impl PduEncode for RdpdrPdu { RdpdrPdu::ServerDeviceAnnounceResponse(pdu) => pdu.encode(dst), RdpdrPdu::DeviceIoRequest(pdu) => pdu.encode(dst), RdpdrPdu::DeviceControlResponse(pdu) => pdu.encode(dst), + RdpdrPdu::DeviceCreateResponse(pdu) => pdu.encode(dst), RdpdrPdu::Unimplemented => Ok(()), } } @@ -122,6 +124,7 @@ impl PduEncode for RdpdrPdu { RdpdrPdu::ServerDeviceAnnounceResponse(pdu) => pdu.name(), RdpdrPdu::DeviceIoRequest(pdu) => pdu.name(), RdpdrPdu::DeviceControlResponse(pdu) => pdu.name(), + RdpdrPdu::DeviceCreateResponse(pdu) => pdu.name(), RdpdrPdu::Unimplemented => "Unimplemented", } } @@ -136,6 +139,7 @@ impl PduEncode for RdpdrPdu { RdpdrPdu::ServerDeviceAnnounceResponse(pdu) => pdu.size(), RdpdrPdu::DeviceIoRequest(pdu) => pdu.size(), RdpdrPdu::DeviceControlResponse(pdu) => pdu.size(), + RdpdrPdu::DeviceCreateResponse(pdu) => pdu.size(), RdpdrPdu::Unimplemented => 0, } } @@ -165,6 +169,9 @@ impl fmt::Debug for RdpdrPdu { Self::DeviceControlResponse(it) => { write!(f, "RdpdrPdu({:?})", it) } + Self::DeviceCreateResponse(it) => { + write!(f, "RdpdrPdu({:?})", it) + } Self::Unimplemented => { write!(f, "RdpdrPdu::Unimplemented") } @@ -172,6 +179,18 @@ impl fmt::Debug for RdpdrPdu { } } +impl From for RdpdrPdu { + fn from(value: DeviceControlResponse) -> Self { + Self::DeviceControlResponse(value) + } +} + +impl From for RdpdrPdu { + fn from(value: DeviceCreateResponse) -> Self { + Self::DeviceCreateResponse(value) + } +} + /// [2.2.1.1 Shared Header (RDPDR_HEADER)](https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-rdpefs/29d4108f-8163-4a67-8271-e48c4b9c2a7c) /// A header that is shared by all RDPDR PDUs. #[derive(Debug)] diff --git a/xtask/src/check.rs b/xtask/src/check.rs index 14fa3d18..ee5e17d4 100644 --- a/xtask/src/check.rs +++ b/xtask/src/check.rs @@ -45,7 +45,6 @@ const EXTRA_LINTS: &[&str] = &[ "clippy::string_to_string", // TODO: "clippy::std_instead_of_alloc", // TODO: "clippy::std_instead_of_core", - "clippy::unreadable_literal", "clippy::separated_literal_suffix", "clippy::unused_self", // TODO: "clippy::use_self", // NOTE(@CBenoit): not sure about that one