Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

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:

  1. No DACL present → grant all
  2. Empty DACL → deny all (except owner implicit READ_CONTROL | WRITE_DAC)
  3. Walk ACEs in order, skip INHERIT_ONLY
  4. Deny ACEs block; allow ACEs accumulate
  5. 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 SystemMandatoryLabel ACE, denies write/read/execute when token integrity is below object integrity.
  • Privilege handling: SeSecurityPrivilege grants ACCESS_SYSTEM_SECURITY; SeTakeOwnershipPrivilege grants 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 satisfied
  • granted_mask: AccessMask — accumulated granted bits (after denials)
  • denied_mask: AccessMask — accumulated denied bits