1use binrw::prelude::*;
4use binrw::{NullWideString, io::TakeSeekExt};
5use modular_bitfield::prelude::*;
6use smb_dtyp::{
7 binrw_util::prelude::*,
8 security::{ACL, ClaimSecurityAttributeRelativeV1, SID},
9};
10
11#[smb_dtyp::mbitfield]
15pub struct TreeConnectRequestFlags {
16 pub cluster_reconnect: bool,
18 pub redirect_to_owner: bool,
20 pub extension_present: bool,
22 #[skip]
23 __: B13,
24}
25
26#[smb_request(size = 9)]
35pub struct TreeConnectRequest {
36 pub flags: TreeConnectRequestFlags,
38 #[bw(calc = PosMarker::default())]
39 #[br(temp)]
40 _path_offset: PosMarker<u16>,
41 #[bw(try_calc = buffer.size().try_into())]
42 #[br(temp)]
43 path_length: u16,
44
45 #[br(if(flags.extension_present()))]
47 #[br(temp)]
48 #[bw(calc = if tree_connect_contexts.is_empty() { None } else { Some(PosMarker::default()) })]
49 tree_connect_context_offset: Option<PosMarker<u32>>,
50
51 #[br(if(flags.extension_present()))]
52 #[bw(if(!tree_connect_contexts.is_empty()))]
53 #[bw(calc = if tree_connect_contexts.is_empty() { None } else { Some(tree_connect_contexts.len().try_into().unwrap()) })]
54 #[br(temp)]
55 tree_connect_context_count: Option<u16>,
56
57 #[br(if(flags.extension_present()))]
58 #[bw(if(!tree_connect_contexts.is_empty()))]
59 #[bw(calc = Some([0u8; 10]))]
60 #[br(temp)]
61 _reserved: Option<[u8; 10]>,
62 #[brw(little)]
66 #[br(args { size: SizedStringSize::bytes16(path_length) })]
67 #[bw(write_with = PosMarker::write_aoff, args(&_path_offset))]
68 pub buffer: SizedWideString,
70
71 #[br(if(flags.extension_present()))]
73 #[br(seek_before = tree_connect_context_offset.unwrap().seek_relative(true))]
74 #[br(count = tree_connect_context_count.unwrap_or(0))]
75 #[bw(if(!tree_connect_contexts.is_empty()))]
76 #[bw(write_with = PosMarker::write_aoff_m, args(tree_connect_context_offset.as_ref()))]
77 tree_connect_contexts: Vec<TreeConnectContext>,
78}
79
80#[smb_request_binrw]
86pub struct TreeConnectContext {
87 #[bw(calc = 1)]
89 #[br(assert(context_type == 1))]
90 context_type: u16,
91 data_length: u16,
93 reserved: u32,
94 data: RemotedIdentityTreeConnect,
95}
96
97macro_rules! make_remoted_identity_connect{
98 (
99 $($field:ident: $value:ty),*
100 ) => {
101 pastey::paste! {
102
103#[binwrite]
104#[derive(Debug, BinRead, PartialEq, Eq)]
105pub struct RemotedIdentityTreeConnect {
112 #[bw(calc = PosMarker::new(1))]
113 #[br(assert(_ticket_type.value == 1))]
114 _ticket_type: PosMarker<u16>,
115 ticket_size: u16,
117
118 $(
120 #[bw(calc = PosMarker::default())]
121 #[br(temp)]
122 [<_$field _offset>]: PosMarker<u16>,
123 )*
124
125 $(
127 #[br(seek_before = _ticket_type.seek_from([<_$field _offset>].value as u64))]
128 #[bw(write_with = PosMarker::write_roff_b, args(&[<_$field _offset>], &_ticket_type))]
129 $field: $value,
130 )*
131}
132 }
133 }
134}
135
136make_remoted_identity_connect! {
137 user: SidAttrData,
138 user_name: NullWideString,
139 domain: NullWideString,
140 groups: SidArrayData,
141 restricted_groups: SidArrayData,
142 privileges: PrivilegeArrayData,
143 primary_group: SidArrayData,
144 owner: BlobData<SID>,
145 default_dacl: BlobData<ACL>,
146 device_groups: SidArrayData,
147 user_claims: BlobData<ClaimSecurityAttributeRelativeV1>,
148 device_claims: BlobData<ClaimSecurityAttributeRelativeV1>
149}
150
151#[binrw::binrw]
155#[derive(Debug, PartialEq, Eq)]
156pub struct BlobData<T>
157where
158 T: BinRead + BinWrite,
159 for<'a> <T as BinRead>::Args<'a>: Default,
160 for<'b> <T as BinWrite>::Args<'b>: Default,
161{
162 blob_size: PosMarker<u16>,
164 #[br(map_stream = |s| s.take_seek(blob_size.value as u64))]
165 pub blob_data: T,
166}
167
168#[binrw::binrw]
170#[derive(Debug, PartialEq, Eq)]
171pub struct ArrayData<T>
172where
173 T: BinRead + BinWrite + 'static,
174 for<'a> <T as BinRead>::Args<'a>: Default + Clone,
175 for<'b> <T as BinWrite>::Args<'b>: Default + Clone,
176{
177 #[bw(try_calc = list.len().try_into())]
178 lcount: u16,
179 #[br(count = lcount)]
180 pub list: Vec<T>,
181}
182
183#[binrw::binrw]
187#[derive(Debug, PartialEq, Eq)]
188pub struct SidAttrData {
189 pub sid_data: SID,
191 pub attr: SidAttrSeGroup,
193}
194
195type SidArrayData = ArrayData<SidAttrData>;
196
197#[smb_dtyp::mbitfield]
201pub struct SidAttrSeGroup {
202 pub mandatory: bool,
204 pub enabled_by_default: bool,
206 pub group_enabled: bool,
208 pub group_owner: bool,
210 pub group_use_for_deny_only: bool,
212 pub group_integrity: bool,
214 pub group_integrity_enabled: bool,
216 #[skip]
217 __: B21,
218 pub group_logon_id: B4,
220}
221
222#[binrw::binrw]
224#[derive(Debug, PartialEq, Eq)]
225pub struct LuidAttrData {
226 pub luid: u64,
228 pub attr: LsaprLuidAttributes,
230}
231
232mod lsapr_luid_attributes {
233 use super::*;
234 #[smb_dtyp::mbitfield]
238 pub struct LsaprLuidAttributes {
239 pub is_default: bool,
241 pub is_enabled: bool,
243 #[skip]
244 __: B30,
245 }
246}
247
248use lsapr_luid_attributes::LsaprLuidAttributes;
249use smb_msg_derive::*;
250
251type PrivilegeData = BlobData<LuidAttrData>;
252
253type PrivilegeArrayData = ArrayData<PrivilegeData>;
254
255impl TreeConnectRequest {
256 pub fn new(name: &str) -> TreeConnectRequest {
257 TreeConnectRequest {
258 flags: TreeConnectRequestFlags::new(),
259 buffer: name.into(),
260 tree_connect_contexts: vec![],
261 }
262 }
263}
264
265#[smb_response(size = 16)]
271pub struct TreeConnectResponse {
272 pub share_type: ShareType,
274 reserved: u8,
275 pub share_flags: ShareFlags,
277 pub capabilities: TreeCapabilities,
279 pub maximal_access: u32,
281}
282
283#[derive(BitfieldSpecifier, Debug, Clone, Copy)]
285#[bits = 4]
286pub enum ShareCacheMode {
287 Manual,
289 Auto,
291 Vdo,
293 NoCache,
295 All = 0xf,
296}
297
298#[smb_dtyp::mbitfield]
302pub struct ShareFlags {
303 pub dfs: bool,
305 pub dfs_root: bool,
307 #[skip]
308 __: B2,
309 pub caching_mode: ShareCacheMode,
311
312 pub restrict_exclusive_opens: bool,
314 pub force_shared_delete: bool,
316 pub allow_namespace_caching: bool,
318 pub access_based_directory_enum: bool,
320 pub force_levelii_oplock: bool,
322 pub enable_hash_v1: bool,
324 pub enable_hash_v2: bool,
326 pub encrypt_data: bool,
328
329 #[skip]
330 __: B2,
331 pub identity_remoting: bool,
333 #[skip]
334 __: B1,
335 pub compress_data: bool,
337 pub isolated_transport: bool,
339 #[skip]
340 __: B10,
341}
342
343#[smb_dtyp::mbitfield]
347pub struct TreeCapabilities {
348 #[skip]
349 __: B3,
350 pub dfs: bool,
352 pub continuous_availability: bool,
354 pub scaleout: bool,
356 pub cluster: bool,
358 pub asymmetric: bool,
360
361 pub redirect_to_owner: bool,
363 #[skip]
364 __: B23,
365}
366
367#[smb_response_binrw]
371#[derive(Clone, Copy)]
372#[brw(repr(u8))]
373pub enum ShareType {
374 Disk = 0x1,
376 Pipe = 0x2,
378 Print = 0x3,
380}
381
382#[smb_request(size = 4)]
389#[derive(Default)]
390pub struct TreeDisconnectRequest {
391 reserved: u16,
392}
393
394#[smb_response(size = 4)]
400#[derive(Default)]
401pub struct TreeDisconnectResponse {
402 reserved: u16,
403}
404
405#[cfg(test)]
406mod tests {
407 use crate::*;
408
409 use super::*;
410
411 test_request! {
413 TreeConnect {
414 flags: TreeConnectRequestFlags::new(),
415 buffer: r"\\adc.aviv.local\IPC$".into(),
416 tree_connect_contexts: vec![],
417 } => "0900000048002a005c005c006100640063002e0061007600690076002e006c006f00630061006c005c004900500043002400"
418 }
419
420 test_binrw_response! {
421 struct TreeConnectResponse {
422 share_type: ShareType::Disk,
423 share_flags: ShareFlags::new().with_access_based_directory_enum(true),
424 capabilities: TreeCapabilities::new(),
425 maximal_access: 0x001f01ff,
426 } => "100001000008000000000000ff011f00"
427 }
428}