smb_msg/
tree_connect.rs

1use binrw::prelude::*;
2use binrw::{NullWideString, io::TakeSeekExt};
3use modular_bitfield::prelude::*;
4use smb_dtyp::{
5    binrw_util::prelude::*,
6    security::{ACL, ClaimSecurityAttributeRelativeV1, SID},
7};
8
9#[bitfield]
10#[derive(BinWrite, BinRead, Debug, Default, Clone, Copy, PartialEq, Eq)]
11#[bw(map = |&x| Self::into_bytes(x))]
12#[br(map = Self::from_bytes)]
13pub struct TreeConnectRequestFlags {
14    pub cluster_reconnect: bool,
15    pub redirect_to_owner: bool,
16    pub extension_present: bool,
17    #[skip]
18    __: B13,
19}
20
21/// Tree Connect Request
22///
23/// Supports both the base and extension variants.
24/// - On read, uses extension iff `flags.extension_present()` - parses just like the server intends.
25/// - On write, uses extension iff `tree_connect_contexts` is non-empty.
26#[binrw::binrw]
27#[derive(Debug, PartialEq, Eq)]
28pub struct TreeConnectRequest {
29    #[bw(calc = 9)]
30    #[br(assert(_structure_size == 9))]
31    _structure_size: u16,
32    pub flags: TreeConnectRequestFlags,
33    #[bw(calc = PosMarker::default())]
34    _path_offset: PosMarker<u16>,
35    #[bw(try_calc = buffer.size().try_into())]
36    path_length: u16,
37
38    // -- Extension --
39    #[br(if(flags.extension_present()))]
40    #[bw(calc = if tree_connect_contexts.is_empty() { None } else { Some(PosMarker::default()) })]
41    tree_connect_context_offset: Option<PosMarker<u32>>,
42
43    #[br(if(flags.extension_present()))]
44    #[bw(if(!tree_connect_contexts.is_empty()))]
45    #[bw(calc = if tree_connect_contexts.is_empty() { None } else { Some(tree_connect_contexts.len().try_into().unwrap()) })]
46    tree_connect_context_count: Option<u16>,
47
48    #[br(if(flags.extension_present()))]
49    #[bw(if(!tree_connect_contexts.is_empty()))]
50    #[bw(calc = Some([0u8; 10]))]
51    _reserved: Option<[u8; 10]>,
52    // -- Extension End --
53    // ------------------------------------------------
54    // -- Base --
55    #[brw(little)]
56    #[br(args { size: SizedStringSize::bytes16(path_length) })]
57    #[bw(write_with = PosMarker::write_aoff, args(&_path_offset))]
58    pub buffer: SizedWideString,
59
60    // -- Extension --
61    #[br(if(flags.extension_present()))]
62    #[br(seek_before = tree_connect_context_offset.unwrap().seek_relative(true))]
63    #[br(count = tree_connect_context_count.unwrap_or(0))]
64    #[bw(if(!tree_connect_contexts.is_empty()))]
65    #[bw(write_with = PosMarker::write_aoff_m, args(tree_connect_context_offset.as_ref()))]
66    tree_connect_contexts: Vec<TreeConnectContext>,
67}
68
69#[binrw::binrw]
70#[derive(Debug, PartialEq, Eq)]
71pub struct TreeConnectContext {
72    /// MS-SMB2 2.2.9.2: Must be set to SMB2_REMOTED_IDENTITY_TREE_CONNECT_CONTEXT_ID = 1.
73    #[bw(calc = 1)]
74    #[br(assert(context_type == 1))]
75    context_type: u16,
76    data_length: u16,
77    reserved: u32,
78    data: RemotedIdentityTreeConnect,
79}
80
81macro_rules! make_remoted_identity_connect{
82    (
83        $($field:ident: $value:ty),*
84    ) => {
85        pastey::paste! {
86
87#[binwrite]
88#[derive(Debug, BinRead, PartialEq, Eq)]
89pub struct RemotedIdentityTreeConnect {
90    // MS-SMB2 2.2.9.2.1: Must be set to 0x1.
91    #[bw(calc = PosMarker::new(1))]
92    #[br(assert(_ticket_type.value == 1))]
93    _ticket_type: PosMarker<u16>,
94    ticket_size: u16,
95
96    // Offsets
97    $(
98        #[bw(calc = PosMarker::default())]
99        [<_$field _offset>]: PosMarker<u16>,
100    )*
101
102    // Values
103    $(
104        #[br(seek_before = _ticket_type.seek_from([<_$field _offset>].value as u64))]
105        #[bw(write_with = PosMarker::write_roff_b, args(&[<_$field _offset>], &_ticket_type))]
106        $field: $value,
107    )*
108}
109        }
110    }
111}
112
113make_remoted_identity_connect! {
114    user: SidAttrData,
115    user_name: NullWideString,
116    domain: NullWideString,
117    groups: SidArrayData,
118    restricted_groups: SidArrayData,
119    privileges: PrivilegeArrayData,
120    primary_group: SidArrayData,
121    owner: BlobData<SID>,
122    default_dacl: BlobData<ACL>,
123    device_groups: SidArrayData,
124    user_claims: BlobData<ClaimSecurityAttributeRelativeV1>,
125    device_claims: BlobData<ClaimSecurityAttributeRelativeV1>
126}
127
128#[binrw::binrw]
129#[derive(Debug, PartialEq, Eq)]
130pub struct BlobData<T>
131where
132    T: BinRead + BinWrite,
133    for<'a> <T as BinRead>::Args<'a>: Default,
134    for<'b> <T as BinWrite>::Args<'b>: Default,
135{
136    blob_size: PosMarker<u16>,
137    #[br(map_stream = |s| s.take_seek(blob_size.value as u64))]
138    pub blob_data: T,
139}
140
141#[binrw::binrw]
142#[derive(Debug, PartialEq, Eq)]
143pub struct ArrayData<T>
144where
145    T: BinRead + BinWrite + 'static,
146    for<'a> <T as BinRead>::Args<'a>: Default + Clone,
147    for<'b> <T as BinWrite>::Args<'b>: Default + Clone,
148{
149    #[bw(try_calc = list.len().try_into())]
150    lcount: u16,
151    #[br(count = lcount)]
152    pub list: Vec<T>,
153}
154
155#[binrw::binrw]
156#[derive(Debug, PartialEq, Eq)]
157pub struct SidAttrData {
158    pub sid_data: SID,
159    pub attr: SidAttrSeGroup,
160}
161
162type SidArrayData = ArrayData<SidAttrData>;
163
164#[bitfield]
165#[derive(BinWrite, BinRead, Debug, Default, Clone, Copy, PartialEq, Eq)]
166#[bw(map = |&x| Self::into_bytes(x))]
167#[br(map = Self::from_bytes)]
168pub struct SidAttrSeGroup {
169    pub mandatory: bool,
170    pub enabled_by_default: bool,
171    pub group_enabled: bool,
172    pub group_owner: bool,
173    pub group_use_for_deny_only: bool,
174    pub group_integrity: bool,
175    pub group_integrity_enabled: bool,
176    #[skip]
177    __: B21,
178    pub group_logon_id: B4,
179}
180
181#[binrw::binrw]
182#[derive(Debug, PartialEq, Eq)]
183pub struct LuidAttrData {
184    pub luid: u64,
185    pub attr: LsaprLuidAttributes,
186}
187
188#[allow(clippy::identity_op)]
189mod lsapr_luid_attributes {
190    use super::*;
191    /// [MS-LSAD 2.2.5.4](https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-lsad/03c834c0-f310-4e0c-832e-b6e7688364d1)
192    #[bitfield]
193    #[derive(BinWrite, BinRead, Debug, Default, Clone, Copy, PartialEq, Eq)]
194    #[br(map = Self::from_bytes)]
195    pub struct LsaprLuidAttributes {
196        pub is_default: bool,
197        pub is_enabled: bool,
198        #[skip]
199        __: B30,
200    }
201}
202
203use lsapr_luid_attributes::LsaprLuidAttributes;
204
205type PrivilegeData = BlobData<LuidAttrData>;
206
207type PrivilegeArrayData = ArrayData<PrivilegeData>;
208
209impl TreeConnectRequest {
210    pub fn new(name: &str) -> TreeConnectRequest {
211        TreeConnectRequest {
212            flags: TreeConnectRequestFlags::new(),
213            buffer: name.into(),
214            tree_connect_contexts: vec![],
215        }
216    }
217}
218
219#[binrw::binrw]
220#[derive(Debug, PartialEq, Eq)]
221pub struct TreeConnectResponse {
222    #[bw(calc = 16)]
223    #[br(assert(_structure_size == 16))]
224    _structure_size: u16,
225    pub share_type: ShareType,
226    #[bw(calc = 0)]
227    _reserved: u8,
228    pub share_flags: ShareFlags,
229    pub capabilities: TreeCapabilities,
230    pub maximal_access: u32,
231}
232
233#[derive(BitfieldSpecifier, Debug, Clone, Copy)]
234#[bits = 4]
235pub enum ShareCacheMode {
236    Manual,
237    Auto,
238    Vdo,
239    NoCache,
240    All = 0xf,
241}
242
243#[bitfield]
244#[derive(BinWrite, BinRead, Debug, Default, Clone, Copy, PartialEq, Eq)]
245#[bw(map = |&x| Self::into_bytes(x))]
246#[br(map = Self::from_bytes)]
247pub struct ShareFlags {
248    pub dfs: bool,
249    pub dfs_root: bool,
250    #[skip]
251    __: B2,
252    pub caching_mode: ShareCacheMode,
253
254    pub restrict_exclusive_opens: bool,
255    pub force_shared_delete: bool,
256    pub allow_namespace_caching: bool,
257    pub access_based_directory_enum: bool,
258    pub force_levelii_oplock: bool,
259    pub enable_hash_v1: bool,
260    pub enable_hash_v2: bool,
261    pub encrypt_data: bool,
262
263    #[skip]
264    __: B2,
265    pub identity_remoting: bool,
266    #[skip]
267    __: B1,
268    pub compress_data: bool,
269    pub isolated_transport: bool,
270    #[skip]
271    __: B10,
272}
273
274#[bitfield]
275#[derive(BinWrite, BinRead, Debug, Default, Clone, Copy, PartialEq, Eq)]
276#[bw(map = |&x| Self::into_bytes(x))]
277#[br(map = Self::from_bytes)]
278pub struct TreeCapabilities {
279    #[skip]
280    __: B3,
281    pub dfs: bool,
282    pub continuous_availability: bool,
283    pub scaleout: bool,
284    pub cluster: bool,
285    pub asymmetric: bool,
286
287    pub redirect_to_owner: bool,
288    #[skip]
289    __: B23,
290}
291
292#[binrw::binrw]
293#[derive(Debug, PartialEq, Eq, Clone, Copy)]
294#[brw(repr(u8))]
295pub enum ShareType {
296    Disk = 0x1,
297    Pipe = 0x2,
298    Print = 0x3,
299}
300
301#[binrw::binrw]
302#[derive(Debug, Default)]
303pub struct TreeDisconnectRequest {
304    #[bw(calc = 4)]
305    #[br(assert(_structure_size == 4))]
306    _structure_size: u16,
307    #[bw(calc = 0)]
308    _reserved: u16,
309}
310
311#[binrw::binrw]
312#[derive(Debug)]
313pub struct TreeDisconnectResponse {
314    #[bw(calc = 4)]
315    #[br(assert(_structure_size == 4))]
316    _structure_size: u16,
317    #[bw(calc = 0)]
318    _reserved: u16,
319}
320
321#[cfg(test)]
322mod tests {
323    use smb_tests::*;
324
325    use crate::*;
326
327    use super::*;
328
329    // TODO(test): Add tests with tree connect contexts.
330    test_request! {
331        TreeConnect {
332            flags: TreeConnectRequestFlags::new(),
333            buffer: r"\\adc.aviv.local\IPC$".into(),
334            tree_connect_contexts: vec![],
335        } => "0900000048002a005c005c006100640063002e0061007600690076002e006c006f00630061006c005c004900500043002400"
336    }
337
338    test_binrw! {
339        struct TreeConnectResponse {
340            share_type: ShareType::Disk,
341            share_flags: ShareFlags::new().with_access_based_directory_enum(true),
342            capabilities: TreeCapabilities::new(),
343            maximal_access: 0x001f01ff,
344        } => "100001000008000000000000ff011f00"
345    }
346}