Access Checks
The win-sd crate provides three access-check methods, from simple to full:
Simple check
check_access takes a slice of token SIDs and a desired access mask.
Implements the basic MS-DTYP §2.5.3.2 algorithm:
- No DACL present → grant all
- Empty DACL → deny all (except owner implicit READ_CONTROL | WRITE_DAC)
- Walk ACEs in order, skip INHERIT_ONLY
- Deny ACEs block; allow ACEs accumulate
- All desired bits must be granted and none denied
#![allow(unused)]
fn main() {
use win_sd::{SecurityDescriptorBuilder, AccessMask, Sid};
let sd = SecurityDescriptorBuilder::new()
.owner(Sid::local_system())
.deny(Sid::anonymous(), AccessMask::FILE_ALL_ACCESS)
.allow(Sid::administrators(), AccessMask::FILE_ALL_ACCESS)
.build();
let result = sd.check_access(
&[Sid::administrators(), Sid::everyone()],
AccessMask::FILE_READ_DATA,
);
assert!(result.granted);
}
Object ACE check
check_access_object adds GUID filtering for Object ACEs:
#![allow(unused)]
fn main() {
use win_sd::{SecurityDescriptorBuilder, AccessMask, Sid, Ace, Guid, WinAcl};
let guid: Guid = "bf967aba-0de6-11d0-a285-00aa003049e2".parse().unwrap();
let ace = Ace::allow_object(
Sid::administrators(),
AccessMask::new(0x0010),
Some(guid),
None,
);
let mut sd = win_sd::SecurityDescriptor::new();
let mut dacl = WinAcl::new();
dacl.add_ace(ace);
sd.set_dacl(dacl);
// Matches only when the requested object type GUID matches
let result = sd.check_access_object(
&[Sid::administrators()],
AccessMask::new(0x0010),
Some(&guid),
);
assert!(result.granted);
}
Full token-based check
check_access_full supports the complete Windows access-check algorithm:
- Mandatory integrity check: scans SACL for
SystemMandatoryLabelACE, denies write/read/execute when token integrity is below object integrity. - Privilege handling:
SeSecurityPrivilegegrants ACCESS_SYSTEM_SECURITY;SeTakeOwnershipPrivilegegrants WRITE_OWNER. - Generic rights expansion: expands GENERIC_READ/WRITE/EXECUTE/ALL via
GenericMapping. - MAXIMUM_ALLOWED mode: when desired mask contains MAXIMUM_ALLOWED, computes the full set of grantable rights.
- Callback ACE evaluation: evaluates conditional expressions via
AttributeContext. Fail-secure: allow ACEs without a parseable condition are skipped; deny ACEs without a condition still deny. - Restricted tokens: second DACL walk against restricted SIDs, intersected with the primary result.
#![allow(unused)]
fn main() {
use win_sd::prelude::*;
use win_sd::token::{AccessToken, Privilege};
let sd = SecurityDescriptorBuilder::new()
.owner(Sid::local_system())
.allow(Sid::everyone(), AccessMask::FILE_ALL_ACCESS)
.mandatory_label(IntegrityLevel::High, IntegrityPolicy::NO_WRITE_UP)
.build();
// High-integrity token can write
let high = AccessToken::new(Sid::everyone())
.with_integrity(IntegrityLevel::High);
let result = sd.check_access_full(&high, AccessMask::FILE_WRITE_DATA, None, None, None);
assert!(result.granted);
// Medium-integrity token cannot (NO_WRITE_UP)
let medium = AccessToken::new(Sid::everyone())
.with_integrity(IntegrityLevel::Medium);
let result = sd.check_access_full(&medium, AccessMask::FILE_WRITE_DATA, None, None, None);
assert!(!result.granted);
}
AccessCheckResult
All three methods return AccessCheckResult:
granted: bool— whether the full desired mask was satisfiedgranted_mask: AccessMask— accumulated granted bits (after denials)denied_mask: AccessMask— accumulated denied bits