windows-namespace-request-sys 0.2.0

Owned, marshalable parameter sets for synchronous Win32 namespace calls.
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
// Copyright (c) Mike Grier.

//! Tests for security-attribute capture.
//!
//! The descriptors here are built by hand rather than parsed from SDDL,
//! because SDDL only ever produces the self-relative form and the **absolute**
//! form -- a struct of raw pointers into the builder's own storage -- is the
//! case capture exists for.

use std::ffi::c_void;
use std::ptr;

use windows_sys::Win32::Foundation::{FALSE, GENERIC_ALL, TRUE};
use windows_sys::Win32::Security::{
    ACL, ACL_REVISION, AddAccessAllowedAce, CreateWellKnownSid, InitializeAcl,
    InitializeSecurityDescriptor, MakeSelfRelativeSD, SECURITY_ATTRIBUTES, SECURITY_DESCRIPTOR,
    SECURITY_MAX_SID_SIZE, SetSecurityDescriptorDacl, SetSecurityDescriptorGroup,
    SetSecurityDescriptorOwner, WinWorldSid,
};

use super::{
    AclState, SELF_RELATIVE_ALIGNMENT, SecurityAttributes, SecurityCaptureFailure,
    SecurityDescriptor,
};
use crate::buffer::AlignedBuffer;

/// The revision every descriptor built here declares.
///
/// windows-sys does not export `SECURITY_DESCRIPTOR_REVISION`, so it is named
/// here rather than written as a bare literal at each use.
const SECURITY_DESCRIPTOR_REVISION: u32 = 1;

/// An ACL, like a self-relative descriptor, must be DWORD-aligned. The `ACL`
/// struct's own alignment is only 2, so it is stated rather than inferred.
const ACL_ALIGNMENT: usize = align_of::<u32>();

/// An empty ACL is a header and nothing else, rounded up to a DWORD.
const EMPTY_ACL_BYTES: u32 = 8;

/// Room for the header plus one access-allowed ACE naming the World SID.
const ONE_ACE_ACL_BYTES: u32 = 256;

/// An absolute security descriptor, kept alive together with everything it
/// points at.
///
/// This is exactly the shape that makes capture necessary: `descriptor` holds
/// raw pointers into `sid` and `acl`, so copying the struct alone would copy a
/// set of references to storage the caller owns.
pub(crate) struct Absolute {
    descriptor: Box<SECURITY_DESCRIPTOR>,
    _sid: AlignedBuffer,
    _acl: Option<AlignedBuffer>,
}

impl Absolute {
    /// A descriptor with an owner and group but no DACL at all.
    fn without_dacl() -> Self {
        let mut sid = world_sid();
        let mut descriptor = new_descriptor();

        set_owner_and_group(&mut descriptor, &mut sid);

        Self {
            descriptor,
            _sid: sid,
            _acl: None,
        }
    }

    /// A descriptor whose DACL is present and NULL: everyone gets everything.
    fn with_null_dacl() -> Self {
        let mut absolute = Self::without_dacl();

        // SAFETY: the descriptor is initialised and writable; a null ACL with
        // present set is the documented NULL-DACL form.
        let set = unsafe {
            SetSecurityDescriptorDacl(
                descriptor_ptr(&mut absolute.descriptor),
                TRUE,
                ptr::null_mut(),
                FALSE,
            )
        };
        assert_ne!(set, FALSE, "set a NULL DACL");

        absolute
    }

    /// A descriptor whose DACL is present and has no entries: nobody gets
    /// anything.
    fn with_empty_dacl() -> Self {
        let mut absolute = Self::without_dacl();
        let mut acl = new_acl(EMPTY_ACL_BYTES);

        // SAFETY: acl is an initialised ACL of the stated size, and the
        // descriptor is initialised and writable.
        let set = unsafe {
            SetSecurityDescriptorDacl(
                descriptor_ptr(&mut absolute.descriptor),
                TRUE,
                acl.as_mut_ptr().cast::<ACL>(),
                FALSE,
            )
        };
        assert_ne!(set, FALSE, "set an empty DACL");

        absolute._acl = Some(acl);
        absolute
    }

    /// A descriptor whose DACL grants the World SID everything.
    pub(crate) fn with_populated_dacl() -> Self {
        let mut absolute = Self::without_dacl();
        let mut acl = new_acl(ONE_ACE_ACL_BYTES);
        let mut sid = world_sid();

        // SAFETY: acl is an initialised ACL with room to spare, and sid holds a
        // well-known SID that Windows just wrote.
        let added = unsafe {
            AddAccessAllowedAce(
                acl.as_mut_ptr().cast::<ACL>(),
                ACL_REVISION,
                GENERIC_ALL,
                sid.as_mut_ptr().cast::<c_void>(),
            )
        };
        assert_ne!(added, FALSE, "add an access-allowed ACE");

        // SAFETY: as in with_empty_dacl.
        let set = unsafe {
            SetSecurityDescriptorDacl(
                descriptor_ptr(&mut absolute.descriptor),
                TRUE,
                acl.as_mut_ptr().cast::<ACL>(),
                FALSE,
            )
        };
        assert_ne!(set, FALSE, "set a populated DACL");

        absolute._acl = Some(acl);
        absolute
    }

    pub(crate) fn as_ptr(&self) -> *const c_void {
        ptr::from_ref(self.descriptor.as_ref()).cast::<c_void>()
    }

    /// The same descriptor, converted to self-relative form by Windows.
    ///
    /// This is what a caller who already holds a self-relative descriptor looks
    /// like to capture.
    fn to_self_relative(&self) -> AlignedBuffer {
        let mut length: u32 = 0;
        // SAFETY: the null destination is the documented sizing call.
        let sized = unsafe {
            MakeSelfRelativeSD(self.as_ptr().cast_mut(), ptr::null_mut(), &raw mut length)
        };
        assert_eq!(sized, FALSE, "the sizing call is expected to fail");

        let mut blob = AlignedBuffer::zeroed(length as usize, SELF_RELATIVE_ALIGNMENT);
        // SAFETY: blob is writable for exactly the reported length.
        let converted = unsafe {
            MakeSelfRelativeSD(
                self.as_ptr().cast_mut(),
                blob.as_mut_ptr().cast::<c_void>(),
                &raw mut length,
            )
        };
        assert_ne!(converted, FALSE, "convert to self-relative form");

        blob
    }
}

fn descriptor_ptr(descriptor: &mut SECURITY_DESCRIPTOR) -> *mut c_void {
    ptr::from_mut(descriptor).cast::<c_void>()
}

fn new_descriptor() -> Box<SECURITY_DESCRIPTOR> {
    let mut descriptor = Box::new(unsafe { std::mem::zeroed::<SECURITY_DESCRIPTOR>() });

    // SAFETY: the box is writable and large enough for a descriptor, which is
    // what InitializeSecurityDescriptor requires.
    let initialised = unsafe {
        InitializeSecurityDescriptor(
            descriptor_ptr(&mut descriptor),
            SECURITY_DESCRIPTOR_REVISION,
        )
    };
    assert_ne!(initialised, FALSE, "initialise an absolute descriptor");

    descriptor
}

fn set_owner_and_group(descriptor: &mut SECURITY_DESCRIPTOR, sid: &mut AlignedBuffer) {
    // SAFETY: the descriptor is initialised, and sid holds a SID Windows wrote.
    let owner = unsafe {
        SetSecurityDescriptorOwner(
            descriptor_ptr(descriptor),
            sid.as_mut_ptr().cast::<c_void>(),
            FALSE,
        )
    };
    assert_ne!(owner, FALSE, "set the owner");

    // SAFETY: as above.
    let group = unsafe {
        SetSecurityDescriptorGroup(
            descriptor_ptr(descriptor),
            sid.as_mut_ptr().cast::<c_void>(),
            FALSE,
        )
    };
    assert_ne!(group, FALSE, "set the group");
}

fn new_acl(bytes: u32) -> AlignedBuffer {
    let mut acl = AlignedBuffer::zeroed(bytes as usize, ACL_ALIGNMENT);

    // SAFETY: acl is writable for exactly `bytes` bytes, which is what
    // InitializeAcl is told to use.
    let initialised = unsafe { InitializeAcl(acl.as_mut_ptr().cast::<ACL>(), bytes, ACL_REVISION) };
    assert_ne!(initialised, FALSE, "initialise an ACL");

    acl
}

fn world_sid() -> AlignedBuffer {
    let mut sid = AlignedBuffer::zeroed(SECURITY_MAX_SID_SIZE as usize, 8);
    let mut length = SECURITY_MAX_SID_SIZE;

    // SAFETY: sid is writable for length bytes, which is the documented maximum
    // any SID needs.
    let created = unsafe {
        CreateWellKnownSid(
            WinWorldSid,
            ptr::null_mut(),
            sid.as_mut_ptr().cast::<c_void>(),
            &raw mut length,
        )
    };
    assert_ne!(created, FALSE, "create the World SID");

    sid
}

fn capture(absolute: &Absolute) -> SecurityDescriptor {
    // SAFETY: the descriptor and everything it names outlive this call.
    unsafe { SecurityDescriptor::capture(absolute.as_ptr()) }.expect("capture the descriptor")
}

#[test]
fn an_absolute_descriptor_without_a_dacl_captures_as_absent() {
    let absolute = Absolute::without_dacl();

    let captured = capture(&absolute);

    assert_eq!(captured.dacl().expect("read the DACL"), AclState::Absent);
}

#[test]
fn an_absolute_descriptor_with_a_null_dacl_captures_as_null() {
    let absolute = Absolute::with_null_dacl();

    let captured = capture(&absolute);

    assert_eq!(captured.dacl().expect("read the DACL"), AclState::Null);
}

#[test]
fn an_absolute_descriptor_with_an_empty_dacl_captures_as_empty() {
    let absolute = Absolute::with_empty_dacl();

    let captured = capture(&absolute);

    assert_eq!(captured.dacl().expect("read the DACL"), AclState::Empty);
}

#[test]
fn the_three_dacl_outcomes_stay_distinct() {
    // The point of the type: these are three different grants, and a
    // representation that ran any pair of them together would silently change
    // what the resulting object permits.
    let absent = capture(&Absolute::without_dacl())
        .dacl()
        .expect("read the DACL");
    let null = capture(&Absolute::with_null_dacl())
        .dacl()
        .expect("read the DACL");
    let empty = capture(&Absolute::with_empty_dacl())
        .dacl()
        .expect("read the DACL");

    assert_ne!(absent, null);
    assert_ne!(absent, empty);
    assert_ne!(
        null, empty,
        "a NULL DACL allows all; an empty DACL allows none"
    );
}

#[test]
fn an_absolute_descriptor_with_a_populated_dacl_reports_its_entry_count() {
    let absolute = Absolute::with_populated_dacl();

    let captured = capture(&absolute);

    assert_eq!(
        captured.dacl().expect("read the DACL"),
        AclState::Populated(1)
    );
}

#[test]
fn a_descriptor_without_a_sacl_captures_as_absent() {
    let absolute = Absolute::with_populated_dacl();

    let captured = capture(&absolute);

    assert_eq!(captured.sacl().expect("read the SACL"), AclState::Absent);
}

#[test]
fn a_captured_descriptor_is_dword_aligned() {
    let captured = capture(&Absolute::with_populated_dacl());

    assert_eq!(
        captured.as_ptr() as usize % SELF_RELATIVE_ALIGNMENT,
        0,
        "a self-relative descriptor must be DWORD-aligned, which a boxed byte \
         slice would not guarantee"
    );
    assert!(!captured.is_empty());
    assert_eq!(captured.len(), captured.as_bytes().len());
}

#[test]
fn a_self_relative_descriptor_is_captured_byte_for_byte() {
    let absolute = Absolute::with_populated_dacl();
    let self_relative = absolute.to_self_relative();

    // SAFETY: the blob outlives this call and holds a valid descriptor.
    let captured = unsafe { SecurityDescriptor::capture(self_relative.as_ptr().cast::<c_void>()) }
        .expect("capture a self-relative descriptor");

    assert_eq!(captured.as_bytes(), self_relative.as_slice());
    assert_eq!(
        captured.dacl().expect("read the DACL"),
        AclState::Populated(1)
    );
}

#[test]
fn the_capture_survives_the_caller_dropping_everything_it_was_built_from() {
    // The property the whole design rests on: an absolute descriptor's owner,
    // group and DACL live in the caller's storage, so a capture that merely
    // copied the struct would be left holding dangling pointers.
    let captured = {
        let absolute = Absolute::with_populated_dacl();
        capture(&absolute)
    };

    assert_eq!(
        captured.dacl().expect("read the DACL"),
        AclState::Populated(1)
    );
    assert_eq!(captured.sacl().expect("read the SACL"), AclState::Absent);
}

#[test]
fn a_malformed_descriptor_is_refused_at_capture() {
    let zeroed = AlignedBuffer::zeroed(size_of::<SECURITY_DESCRIPTOR>(), SELF_RELATIVE_ALIGNMENT);

    // SAFETY: the buffer outlives the call; its contents are not a valid
    // descriptor, which is what this asserts.
    let error = unsafe { SecurityDescriptor::capture(zeroed.as_ptr().cast::<c_void>()) }
        .expect_err("a zeroed descriptor has revision 0 and cannot be valid");

    assert_eq!(error.failure(), SecurityCaptureFailure::InvalidDescriptor);
    assert!(
        error.to_string().contains("IsValidSecurityDescriptor"),
        "unexpected message: {error}"
    );
}

#[test]
fn a_clone_equals_its_original_and_is_independent() {
    let captured = capture(&Absolute::with_populated_dacl());

    let clone = captured.clone();

    assert_eq!(clone, captured);
    assert_ne!(clone.as_ptr(), captured.as_ptr());
    drop(captured);
    assert_eq!(clone.dacl().expect("read the DACL"), AclState::Populated(1));
}

#[test]
fn descriptors_differing_in_their_dacl_are_not_equal() {
    let null = capture(&Absolute::with_null_dacl());
    let empty = capture(&Absolute::with_empty_dacl());

    assert_ne!(null, empty);
}

#[test]
fn null_attributes_are_the_caller_declining_rather_than_an_error() {
    // SAFETY: a null pointer is checked, never dereferenced.
    let captured = unsafe { SecurityAttributes::capture(ptr::null()) }
        .expect("a null lpSecurityAttributes is not a failure");

    assert!(
        captured.is_none(),
        "no attributes at all differs from attributes carrying no descriptor"
    );
}

#[test]
fn attributes_with_no_descriptor_keep_the_inheritance_choice() {
    let raw = SECURITY_ATTRIBUTES {
        nLength: size_of::<SECURITY_ATTRIBUTES>() as u32,
        lpSecurityDescriptor: ptr::null_mut(),
        bInheritHandle: TRUE,
    };

    // SAFETY: raw outlives the call and names no descriptor.
    let captured = unsafe { SecurityAttributes::capture(&raw) }
        .expect("capture attributes")
        .expect("the attributes were supplied");

    assert!(captured.descriptor().is_none());
    assert!(captured.inherit_handle());
}

#[test]
fn attributes_with_a_descriptor_capture_both_parts() {
    let absolute = Absolute::with_populated_dacl();
    let raw = SECURITY_ATTRIBUTES {
        nLength: size_of::<SECURITY_ATTRIBUTES>() as u32,
        lpSecurityDescriptor: absolute.as_ptr().cast_mut(),
        bInheritHandle: FALSE,
    };

    // SAFETY: raw and the descriptor it names both outlive the call.
    let captured = unsafe { SecurityAttributes::capture(&raw) }
        .expect("capture attributes")
        .expect("the attributes were supplied");

    assert!(!captured.inherit_handle());
    assert_eq!(
        captured
            .descriptor()
            .expect("a descriptor was supplied")
            .dacl()
            .expect("read the DACL"),
        AclState::Populated(1)
    );
}

#[test]
fn to_raw_rebuilds_the_win32_struct_from_the_capture() {
    let absolute = Absolute::with_populated_dacl();
    let captured = SecurityAttributes::new(Some(capture(&absolute)), true);

    let raw = captured.to_raw();

    assert_eq!(raw.nLength as usize, size_of::<SECURITY_ATTRIBUTES>());
    assert_eq!(raw.bInheritHandle, TRUE);
    assert_eq!(
        raw.lpSecurityDescriptor.cast_const(),
        captured
            .descriptor()
            .expect("a descriptor was supplied")
            .as_ptr()
    );
}

#[test]
fn to_raw_reports_a_null_descriptor_when_none_was_captured() {
    let captured = SecurityAttributes::new(None, false);

    let raw = captured.to_raw();

    assert!(raw.lpSecurityDescriptor.is_null());
    assert_eq!(raw.bInheritHandle, FALSE);
}

#[test]
fn a_capture_moves_and_shares_across_threads() {
    const fn assert_send<T: Send>() {}
    const fn assert_sync<T: Sync>() {}

    assert_send::<SecurityDescriptor>();
    assert_sync::<SecurityDescriptor>();
    assert_send::<SecurityAttributes>();
    assert_sync::<SecurityAttributes>();

    let captured = SecurityAttributes::new(Some(capture(&Absolute::with_populated_dacl())), true);

    let observed = std::thread::spawn(move || {
        captured
            .descriptor()
            .expect("a descriptor was supplied")
            .dacl()
            .expect("read the DACL")
    })
    .join()
    .expect("the worker did not panic");

    assert_eq!(observed, AclState::Populated(1));
}