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

acls-rs Bridge

The bridge module connects Windows access masks to the algebraic permission system in acls-rs. A PermissionMapping defines how bitmask values translate to named permissions — different object types use different mappings because the same bit positions carry different semantics.

PermissionMapping

The same bit can mean different things depending on the object type:

BitFileActive DirectoryRegistry
0x0001file_read_datacreate_childquery_value
0x0010file_write_earead_propnotify
0x0020file_executewrite_propcreate_link
0x0100file_write_attributescontrol_access

A PermissionMapping holds a namespace and a table of (bit, action_name) entries. Three pre-defined mappings cover the common cases:

#![allow(unused)]
fn main() {
use win_sd::bridge::{file_mapping, ad_mapping, registry_mapping};

let file = file_mapping();     // namespace "win", file semantics
let ad   = ad_mapping();       // namespace "ad",  AD/DS semantics
let reg  = registry_mapping(); // namespace "reg", registry semantics
}

Standard rights (bits 0x10000–0x100000: delete, read_control, write_dac, write_owner, synchronize) are shared across all three mappings.

Custom mappings can be built at runtime:

#![allow(unused)]
fn main() {
use acls_rs::permission::PermissionMapping;

let custom = PermissionMapping::new("myapp")
    .add(0x01, "view")
    .add(0x02, "edit")
    .add(0x04, "admin");
}

AccessMask ↔ PermissionSet

The default (no-argument) methods use the file mapping:

#![allow(unused)]
fn main() {
use win_sd::AccessMask;
use acls_rs::permission::AtomicPermission;

let set = AccessMask::FILE_GENERIC_READ.to_permission_set();
assert!(set.contains(&AtomicPermission::new("win", "file_read_data")));
assert!(set.contains(&AtomicPermission::new("win", "read_control")));

let mask = AccessMask::from_permission_set(&set);
assert!(mask.contains(AccessMask::FILE_READ_DATA));
}

For AD or registry objects, pass a mapping:

#![allow(unused)]
fn main() {
use win_sd::AccessMask;
use win_sd::bridge::ad_mapping;
use acls_rs::permission::AtomicPermission;

let mask = AccessMask::new(0x0030); // READ_PROP | WRITE_PROP
let set = mask.to_permission_set_with(&ad_mapping());
assert!(set.contains(&AtomicPermission::new("ad", "read_prop")));
assert!(set.contains(&AtomicPermission::new("ad", "write_prop")));

// Round-trip through the same mapping
let recovered = AccessMask::from_permission_set_with(&set, &ad_mapping());
assert_eq!(mask, recovered);
}

SecurityDescriptor ↔ PermissionSet

#![allow(unused)]
fn main() {
use win_sd::{SecurityDescriptorBuilder, AccessMask, Sid};
use win_sd::bridge::ad_mapping;
use acls_rs::permission::AtomicPermission;

let sd = SecurityDescriptorBuilder::new()
    .owner(Sid::local_system())
    .allow(Sid::everyone(), AccessMask::new(0x000F01B7))
    .build();

// File semantics (default)
let file_set = sd.to_permission_set(&[Sid::everyone()]);
assert!(file_set.contains(&AtomicPermission::new("win", "file_read_data")));

// AD semantics
let ad_set = sd.to_permission_set_with(&[Sid::everyone()], &ad_mapping());
assert!(ad_set.contains(&AtomicPermission::new("ad", "read_prop")));
assert!(ad_set.contains(&AtomicPermission::new("ad", "write_prop")));
assert!(ad_set.contains(&AtomicPermission::new("ad", "create_child")));
}

GrantDenialPair with explicit denials

Unlike POSIX ACLs (which have no explicit denials), Windows security descriptors can have both grants and denials:

#![allow(unused)]
fn main() {
use win_sd::{SecurityDescriptorBuilder, AccessMask, Sid};
use acls_rs::permission::AtomicPermission;

let sd = SecurityDescriptorBuilder::new()
    .deny(Sid::anonymous(), AccessMask::FILE_WRITE_DATA)
    .allow(Sid::everyone(), AccessMask::FILE_ALL_ACCESS)
    .build();

let gdp = sd.to_grant_denial_pair(&[Sid::anonymous(), Sid::everyone()]);
assert!(gdp.denials.contains(&AtomicPermission::new("win", "file_write_data")));

let effective = gdp.effective_permissions();
assert!(!effective.contains(&AtomicPermission::new("win", "file_write_data")));
assert!(effective.contains(&AtomicPermission::new("win", "file_read_data")));
}

The _with variant accepts a mapping:

#![allow(unused)]
fn main() {
use win_sd::bridge::ad_mapping;
use win_sd::{SecurityDescriptorBuilder, AccessMask, Sid};
use acls_rs::permission::AtomicPermission;

let sd = SecurityDescriptorBuilder::new()
    .deny(Sid::anonymous(), AccessMask::new(0x0020))
    .allow(Sid::everyone(), AccessMask::new(0x000F01B7))
    .build();
let gdp = sd.to_grant_denial_pair_with(
    &[Sid::anonymous(), Sid::everyone()],
    ad_mapping(),
);
assert!(gdp.denials.contains(
    &AtomicPermission::new("ad", "write_prop"),
));
}

Cross-namespace comparison

Both posix-acls and win-sd expose permissions through PermissionSet, enabling cross-system comparison:

#![allow(unused)]
fn main() {
use acls_rs::permission::AtomicPermission;

// These are in different namespaces — the caller maps between them
let nt_read = AtomicPermission::new("win", "file_read_data");
let posix_read = AtomicPermission::new("posix", "read");
let ad_read = AtomicPermission::new("ad", "read_prop");

// They are distinct values — no implicit equivalence
assert_ne!(nt_read, posix_read);
assert_ne!(nt_read, ad_read);
}

For direct translation between NT and POSIX permissions, see NT-POSIX Translation. For AD schema analysis with cross-model bridging, see AD Schema Access Control.

Mapping reference

File mapping (file_mapping(), namespace "win")

BitAction
0x00010000delete
0x00020000read_control
0x00040000write_dac
0x00080000write_owner
0x00100000synchronize
0x0001file_read_data
0x0002file_write_data
0x0004file_append_data
0x0008file_read_ea
0x0010file_write_ea
0x0020file_execute
0x0040file_delete_child
0x0080file_read_attributes
0x0100file_write_attributes

AD mapping (ad_mapping(), namespace "ad")

BitActionMS-DTYP name
0x0001create_childADS_RIGHT_DS_CREATE_CHILD
0x0002delete_childADS_RIGHT_DS_DELETE_CHILD
0x0004list_childrenADS_RIGHT_ACTRL_DS_LIST
0x0008self_writeADS_RIGHT_DS_SELF
0x0010read_propADS_RIGHT_DS_READ_PROP
0x0020write_propADS_RIGHT_DS_WRITE_PROP
0x0040delete_treeADS_RIGHT_DS_DELETE_TREE
0x0080list_objectADS_RIGHT_DS_LIST_OBJECT
0x0100control_accessADS_RIGHT_DS_CONTROL_ACCESS

Plus the 5 standard rights shared with file mapping.

Registry mapping (registry_mapping(), namespace "reg")

BitAction
0x0001query_value
0x0002set_value
0x0004create_sub_key
0x0008enumerate_sub_keys
0x0010notify
0x0020create_link

Plus the 5 standard rights.