Skip to content

Fix unusable SDDL ACE types TL and FL by adding ACE types 0x14 and 0x15 (Fixes #134) - #137

Merged
p0dalirius merged 1 commit into
mainfrom
bugfix-acetype-tl-fl
Jul 29, 2026
Merged

Fix unusable SDDL ACE types TL and FL by adding ACE types 0x14 and 0x15 (Fixes #134)#137
p0dalirius merged 1 commit into
mainfrom
bugfix-acetype-tl-fl

Conversation

@p0dalirius

Copy link
Copy Markdown
Collaborator

Linked Issue

Closes #134

Root Cause

SDDL_PROCESS_TRUST_LABEL ("TL") and SDDL_ACCESS_FILTER ("FL") were declared as SDDL constants, but their SDDLToACETypeMap entries were commented out because they referenced ACE_TYPE_SYSTEM_PROCESS_TRUST_LABEL and ACE_TYPE_SYSTEM_ACCESS_FILTER, identifiers that did not exist. The ACE type constant block in ace/acetype stops at ACE_TYPE_SYSTEM_SCOPED_POLICY_ID = 0x13, so there was nothing for the map to point at and the entries were commented rather than completed. Both SDDL tags were therefore dead code, and any descriptor using one was rejected outright.

The underlying reason the constants are absent is that MS-DTYP 2.4.4.1's AceType table does not list 0x14 or 0x15; they are defined only in winnt.h. A constant block transcribed from the spec stops exactly where this one does.

Fix Description

Three coordinated changes:

  1. ace/acetype gains ACE_TYPE_SYSTEM_PROCESS_TRUST_LABEL = 0x14 and ACE_TYPE_SYSTEM_ACCESS_FILTER = 0x15, with comments noting they come from winnt.h rather than MS-DTYP, plus their entries in the human-readable name map.
  2. The two SDDLToACETypeMap entries are uncommented and now resolve.
  3. Both types are added to the ACE type switches.

Point 3 is the part that makes this a real fix rather than a cosmetic one. Enabling the SDDL mapping alone would still have failed on the binary path, because the Unmarshal switch's default branch returns unknown ACE type: %d. Both structures carry an ACCESS_MASK followed by a SID — the same wire shape as SYSTEM_MANDATORY_LABEL and SYSTEM_SCOPED_POLICY_ID — so rather than duplicating those case bodies three times, the two new types are grouped onto the existing ACE_TYPE_SYSTEM_SCOPED_POLICY_ID case label in all three switches (Unmarshal, Marshal, DescribeList). That keeps the diff to a case label per switch and guarantees the three paths cannot drift apart for these types.

How Verified

Runtime, before the fix — verified with a standalone program, since a test referencing the new constants cannot compile against the pre-fix tree:

(TL;;;;;S-1-1-0)             REJECT  failed to parse SACL: failed to parse ACE #1 'TL;;;;;S-1-1-0': unknown ACE type: TL
(FL;;;;;S-1-1-0)             REJECT  failed to parse SACL: failed to parse ACE #1 'FL;;;;;S-1-1-0': unknown ACE type: FL
(ML;;;;;S-1-16-4096) control accept  -> S:(ML;;;;;LW)

The ML control shows the SACL path itself was already sound; only these two types were unreachable.

Runtime, after the fix — full SDDL → binary → SDDL round-trip:

S:(TL;;;;;WD)        -> S:(TL;;;;;WD)        OK
S:(FL;;;;;WD)        -> S:(FL;;;;;WD)        OK
S:(TL;;CCDC;;;WD)    -> S:(TL;;CCDC;;;WD)    OK
S:(ML;;;;;LW)        -> S:(ML;;;;;LW)        OK  (control)
S:(SP;;;;;S-1-17-1)  -> S:(SP;;;;;S-1-17-1)  OK  (control)

Repository suitego test ./... green.

Test Coverage

Added — in securitydescriptor/NtSecurityDescriptor_conditional_test.go:

  • TestACEType_TL_FL_RoundTrip — parses TL and FL from SDDL, asserts the resulting ACE type byte, then marshals and unmarshals and asserts the SDDL round-trips byte-identically. Covers a masked variant too. This is the test that would have caught the half fix: it exercises Marshal/Unmarshal, not just the SDDL map.
  • TestACEType_TL_FL_Names — pins the constants to 0x14/0x15 and asserts both resolve to a non-empty name.

Note on regression value: against the pre-fix tree these tests fail to compile rather than fail at runtime, because the constants they reference are part of the fix. The pre-fix behaviour was therefore verified with the standalone program quoted above rather than by running these tests.

Scope of Change

  • Files changed: ace/acetype/AccessControlEntryType.go, sddl/ace/acetype/AccessControlEntryType.go, ace/AccessControlEntry.go, securitydescriptor/NtSecurityDescriptor_conditional_test.go
  • Submodule pointer updated: no
  • Behavioral changes outside the bug fix: none. Two new constants, two new name-map rows, two previously commented map entries enabled, and two case labels added to existing switch cases. No existing ACE type changes behaviour.

Risk and Rollout

Additive: input that already parsed is unaffected, and input that previously errored may now succeed. The only shared code touched is three case labels, and the bodies they join are unchanged. Safe to merge without staged rollout.

Notes

Same defect class as #129, which added the missing ZD SDDL mapping — an SDDL tag with no path to an ACE type constant.

Two observations left deliberately out of scope:

  • In the DescribeList switch, case acetype.ACE_TYPE_SYSTEM_ALARM_CALLBACK_OBJECT: has an empty body immediately above case acetype.ACE_TYPE_SYSTEM_MANDATORY_LABEL:. Go does not fall through, so that ACE type describes nothing. Pre-existing and unrelated; worth its own issue if intended to be grouped.
  • securitydescriptor/NtSecurityDescriptor_test.go and sddl/sddl_functions_test.go are flagged by gofmt -l on main already and are untouched here.

Filed alongside this from the same investigation: #133 (SDDL SID alias table incomplete, fixed in #136) and #135 (undocumented conditional-expression tokens 0xfc/@TOKEN. and 0xa3/&).

…15 (Fixes #134)

SDDL_PROCESS_TRUST_LABEL ("TL") and SDDL_ACCESS_FILTER ("FL") were declared but
their SDDLToACETypeMap entries were commented out, because the referenced
constants ACE_TYPE_SYSTEM_PROCESS_TRUST_LABEL and ACE_TYPE_SYSTEM_ACCESS_FILTER
did not exist: the ACE type constant block stopped at 0x13. Both SDDL tags were
therefore dead code and any descriptor using them was rejected with
"unknown ACE type".

Add the two constants (0x14 and 0x15 per winnt.h, neither listed in MS-DTYP
2.4.4.1), their human-readable names, and enable the two SDDL map entries.

Enabling the SDDL mapping alone would have been a half fix: the Unmarshal switch
errors on an unhandled type via its default branch, so the binary path would still
have failed. Both ACE types carry an ACCESS_MASK followed by a SID, the same wire
shape as SYSTEM_MANDATORY_LABEL and SYSTEM_SCOPED_POLICY_ID, so they are grouped
onto the existing SYSTEM_SCOPED_POLICY_ID case in all three type switches
(Unmarshal, Marshal and DescribeList) rather than duplicating the bodies.
@p0dalirius
p0dalirius merged commit bdc0c82 into main Jul 29, 2026
5 checks passed
@p0dalirius
p0dalirius deleted the bugfix-acetype-tl-fl branch July 29, 2026 10:39
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant