NT ↔ POSIX ACL Translation
Translating between Windows Security Descriptors and POSIX ACLs is the core
problem in Samba, NFS-ACL, and CIFS-to-NFSv4 interoperability. The
nt_posix_translate example demonstrates three approaches, from lossy to
lossless.
Run the example:
cargo run -p bac-examples --example nt_posix_translate
The impedance mismatch
Windows NT ACLs and POSIX.1e ACLs model different permission universes:
| Aspect | NT | POSIX |
|---|---|---|
| Permission granularity | 32-bit mask (14+ distinct rights) | 3-bit rwx |
| Deny support | Explicit deny ACEs | None |
| Inheritance | Per-ACE flags (CI, OI, NP, IO) | Default ACL only |
| Audit | SACL with audit ACEs | None |
| Integrity | Mandatory integrity labels | None |
| Principal identifier | SID (structured binary) | String (user/group name) |
The mapping from NT to POSIX is inherently lossy — information is destroyed in both directions.
Path 1: Direct translation (lossy)
The simplest approach maps each allow ACE to a POSIX entry:
FILE_READ_DATA→ readFILE_WRITE_DATA | FILE_APPEND_DATA→ writeFILE_EXECUTE→ execute
SIDs are mapped to POSIX names via an identity map (analogous to Samba’s idmap or SSSD).
What’s lost: deny ACEs (no POSIX equivalent), standard rights (READ_CONTROL, WRITE_DAC, WRITE_OWNER, SYNCHRONIZE), extended attributes (FILE_READ_EA, FILE_WRITE_EA), FILE_DELETE_CHILD, inheritance flags, SACL, integrity labels.
Path 2: Out-of-band metadata (lossless)
Like Samba’s security.NTACL extended attribute: store the original NT SD
binary alongside the POSIX ACL. The POSIX ACL is the live access control
for POSIX clients, but the full SD is preserved for lossless restoration
when a Windows client requests it.
┌─────────────┐ ┌──────────────────┐
│ POSIX ACL │ │ security.NTACL │
│ (live) │ │ (268 bytes blob) │
│ user::rwx │ │ Full SD with │
│ group::rw- │ │ deny ACEs, SACL, │
│ other::r-- │ │ control flags... │
└─────────────┘ └──────────────────┘
Pros: Complete preservation of the original SD. Restore is always
lossless.
Cons: Requires out-of-band storage. The POSIX ACL and NT SD can
diverge if modified independently.
Path 3: PermissionSet with opaque masks (lossless per-ACE)
Encode the full 32-bit AccessMask per-ACE as nt_raw:SID:0xMASK inside the
acls-rs PermissionSet, alongside the decomposed win:* permissions. No
out-of-band storage needed — everything lives within the PermissionSet model.
PermissionSet {
win:file_read_data ← decomposed (for querying)
win:read_control ← decomposed
...
nt_raw:S-1-5-32-544:0x001F01FF ← original mask (for restoration)
nt_raw:S-1-1-0:0x00000081 ← original mask
}
Pros: Self-contained, no out-of-band storage. Lossless for the ACEs
that match the token.
Cons: Only preserves allow ACEs for the queried token’s SIDs (not the
full SD). Deny ACEs and SACL are not captured.
Choosing an approach
| Approach | Fidelity | Storage | Use case |
|---|---|---|---|
| Direct | Lossy | None | Quick mapping, POSIX-only consumers |
| Out-of-band | Full SD | xattr/sidecar | Samba, dual-protocol file servers |
| PermissionSet | Per-ACE | In-memory | Application-level policy comparison |