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:
| Bit | File | Active Directory | Registry |
|---|---|---|---|
| 0x0001 | file_read_data | create_child | query_value |
| 0x0010 | file_write_ea | read_prop | notify |
| 0x0020 | file_execute | write_prop | create_link |
| 0x0100 | file_write_attributes | control_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")
| Bit | Action |
|---|---|
| 0x00010000 | delete |
| 0x00020000 | read_control |
| 0x00040000 | write_dac |
| 0x00080000 | write_owner |
| 0x00100000 | synchronize |
| 0x0001 | file_read_data |
| 0x0002 | file_write_data |
| 0x0004 | file_append_data |
| 0x0008 | file_read_ea |
| 0x0010 | file_write_ea |
| 0x0020 | file_execute |
| 0x0040 | file_delete_child |
| 0x0080 | file_read_attributes |
| 0x0100 | file_write_attributes |
AD mapping (ad_mapping(), namespace "ad")
| Bit | Action | MS-DTYP name |
|---|---|---|
| 0x0001 | create_child | ADS_RIGHT_DS_CREATE_CHILD |
| 0x0002 | delete_child | ADS_RIGHT_DS_DELETE_CHILD |
| 0x0004 | list_children | ADS_RIGHT_ACTRL_DS_LIST |
| 0x0008 | self_write | ADS_RIGHT_DS_SELF |
| 0x0010 | read_prop | ADS_RIGHT_DS_READ_PROP |
| 0x0020 | write_prop | ADS_RIGHT_DS_WRITE_PROP |
| 0x0040 | delete_tree | ADS_RIGHT_DS_DELETE_TREE |
| 0x0080 | list_object | ADS_RIGHT_DS_LIST_OBJECT |
| 0x0100 | control_access | ADS_RIGHT_DS_CONTROL_ACCESS |
Plus the 5 standard rights shared with file mapping.
Registry mapping (registry_mapping(), namespace "reg")
| Bit | Action |
|---|---|
| 0x0001 | query_value |
| 0x0002 | set_value |
| 0x0004 | create_sub_key |
| 0x0008 | enumerate_sub_keys |
| 0x0010 | notify |
| 0x0020 | create_link |
Plus the 5 standard rights.