# 5.4 ACE Types

_Peios / Advanced Peios / PCDS / Security Descriptor_

> Every ACE type and body layout — the single-SID, object, callback and resource-attribute families, the AceType constants, and the ACL revision rules.

An Access Control Entry (ACE) is a single rule in an ACL. Each ACE has a header, an access mask, and a principal SID, with optional extensions for object-type and conditional ACEs.

## 5.4.1 ACE header

Every ACE begins with a 4-byte header:

| Offset | Size | Field | Description |
|---|---|---|---|
| 0 | 1 | AceType | Identifies the ACE type. |
| 1 | 1 | AceFlags | Inheritance and audit flags. |
| 2 | 2 | AceSize | Total size of the ACE in bytes, including the header. MUST be a multiple of 4. |

## 5.4.2 ACE body layouts

The ACE header is followed by a type-specific body. Every multibyte integer in
the body is little-endian.

### 5.4.2.1 Single-SID ACE family

The following ACE types share the same binary layout:

- `ACCESS_ALLOWED_ACE`
- `ACCESS_DENIED_ACE`
- `SYSTEM_AUDIT_ACE`
- `SYSTEM_ALARM_ACE`
- `SYSTEM_MANDATORY_LABEL_ACE`
- `SYSTEM_SCOPED_POLICY_ID_ACE`
- `SYSTEM_PROCESS_TRUST_LABEL_ACE`

Layout:

| Offset | Size | Field | Description |
|---|---|---|---|
| 0 | 4 | AceHeader | Standard ACE header. |
| 4 | 4 | Mask | Access mask. |
| 8 | variable | Sid | Principal SID. Consumes the remainder of the ACE. |

Parsing rules:

- `AceSize` MUST be at least 16 bytes (header + mask + minimum SID).
- The SID MUST consume the remainder of the ACE exactly.

### 5.4.2.2 Object ACE family

The following ACE types share the object-ACE binary layout:

- `ACCESS_ALLOWED_OBJECT_ACE`
- `ACCESS_DENIED_OBJECT_ACE`
- `SYSTEM_AUDIT_OBJECT_ACE`
- `SYSTEM_ALARM_OBJECT_ACE`

Layout:

| Offset | Size | Field | Description |
|---|---|---|---|
| 0 | 4 | AceHeader | Standard ACE header. |
| 4 | 4 | Mask | Access mask. |
| 8 | 4 | Flags | Bitfield describing which GUIDs are present. |
| 12 | 0 or 16 | ObjectType | Present when `ACE_OBJECT_TYPE_PRESENT` is set. |
| 12 or 28 | 0 or 16 | InheritedObjectType | Present when `ACE_INHERITED_OBJECT_TYPE_PRESENT` is set. |
| variable | variable | Sid | Principal SID. Begins immediately after the optional GUID fields and consumes the remainder of the ACE. |

Object ACE flags:

| Flag | Value | Description |
|---|---|---|
| `ACE_OBJECT_TYPE_PRESENT` | 0x00000001 | `ObjectType` GUID is present. |
| `ACE_INHERITED_OBJECT_TYPE_PRESENT` | 0x00000002 | `InheritedObjectType` GUID is present. |

Parsing rules:

- `AceSize` MUST be large enough to contain the header, mask, flags, all GUIDs selected by `Flags`, and a complete SID.
- Unknown bits in `Flags` MUST be ignored.
- If neither GUID-presence bit is set, the ACE has no GUID fields and behaves like the corresponding basic ACE.
- GUID fields are opaque 16-byte values at this layer. Their interpretation is described in the Peios Kernel TRM §3.8.5.

### 5.4.2.3 Callback ACE family

The following ACE types extend the corresponding non-callback ACE layout by
appending `ApplicationData` at the end of the ACE:

- `ACCESS_ALLOWED_CALLBACK_ACE`
- `ACCESS_DENIED_CALLBACK_ACE`
- `SYSTEM_AUDIT_CALLBACK_ACE`
- `SYSTEM_ALARM_CALLBACK_ACE`
- `ACCESS_ALLOWED_CALLBACK_OBJECT_ACE`
- `ACCESS_DENIED_CALLBACK_OBJECT_ACE`
- `SYSTEM_AUDIT_CALLBACK_OBJECT_ACE`
- `SYSTEM_ALARM_CALLBACK_OBJECT_ACE`

For non-object callback ACEs, the body layout is:

| Offset | Size | Field | Description |
|---|---|---|---|
| 0 | 4 | AceHeader | Standard ACE header. |
| 4 | 4 | Mask | Access mask. |
| 8 | variable | Sid | Principal SID. |
| variable | variable | ApplicationData | Trailing type-specific bytes. Consumes the remainder of the ACE. |

For callback object ACEs, the body layout is:

| Offset | Size | Field | Description |
|---|---|---|---|
| 0 | 4 | AceHeader | Standard ACE header. |
| 4 | 4 | Mask | Access mask. |
| 8 | 4 | Flags | Object ACE flags. |
| 12 | 0 or 16 | ObjectType | Present when `ACE_OBJECT_TYPE_PRESENT` is set. |
| 12 or 28 | 0 or 16 | InheritedObjectType | Present when `ACE_INHERITED_OBJECT_TYPE_PRESENT` is set. |
| variable | variable | Sid | Principal SID. |
| variable | variable | ApplicationData | Trailing type-specific bytes. Consumes the remainder of the ACE. |

Parsing rules:

- The SID begins after the fixed fields and any optional GUIDs, exactly as in the corresponding non-callback ACE family.
- `ApplicationData` MAY be empty. Semantics for empty or malformed callback payloads are defined by the relevant subsystem.
- For conditional ACEs, `ApplicationData` carries the conditional expression bytecode defined in the Conditional ACE Bytecode Reference.

### 5.4.2.4 Resource attribute ACE

`SYSTEM_RESOURCE_ATTRIBUTE_ACE` uses the single-SID ACE prefix followed by
trailing application data:

| Offset | Size | Field | Description |
|---|---|---|---|
| 0 | 4 | AceHeader | Standard ACE header. |
| 4 | 4 | Mask | Reserved for compatibility. Not used for access decisions. |
| 8 | variable | Sid | MUST be Everyone (`S-1-1-0`). |
| variable | variable | ApplicationData | One claim entry using §5.9. Consumes the remainder of the ACE. |

Parsing rules:

- The SID MUST be Everyone.
- `ApplicationData` MUST contain exactly one claim entry using §5.9.

## 5.4.3 DACL ACE types

### 5.4.3.1 Basic ACEs

| Structure | Value | Effect |
|---|---|---|
| ACCESS_ALLOWED_ACE | 0x00 | Grants the specified rights to the SID. |
| ACCESS_DENIED_ACE | 0x01 | Denies the specified rights to the SID. |

### 5.4.3.2 Object-type ACEs

Extend basic ACEs with one or two GUIDs that scope the rule to a specific property or object class. Used for Active Directory access control.

| Structure | Value | Effect |
|---|---|---|
| ACCESS_ALLOWED_OBJECT_ACE | 0x05 | Grants rights scoped to a property/class GUID. |
| ACCESS_DENIED_OBJECT_ACE | 0x06 | Denies rights scoped to a property/class GUID. |

The ObjectType GUID identifies the property or property set the ACE applies to. The InheritedObjectType GUID restricts inheritance to child objects of a specific class. Either or both GUIDs MAY be absent (indicated by a flags field), in which case the ACE behaves like a basic ACE for that dimension.

### 5.4.3.3 Conditional ACEs

Extend basic and object-type ACEs with a conditional expression. The ACE only takes effect if the expression evaluates to TRUE against the caller's token attributes and the object's resource attributes.

| Structure | Value | Effect |
|---|---|---|
| ACCESS_ALLOWED_CALLBACK_ACE | 0x09 | Conditional allow. |
| ACCESS_DENIED_CALLBACK_ACE | 0x0A | Conditional deny. |
| ACCESS_ALLOWED_CALLBACK_OBJECT_ACE | 0x0B | Conditional allow, scoped to GUID. |
| ACCESS_DENIED_CALLBACK_OBJECT_ACE | 0x0C | Conditional deny, scoped to GUID. |

> [!NOTE]
> The term "callback" is historical. KACS evaluates conditional expressions inline during AccessCheck. The name is preserved for binary format compatibility. UI and UX layers MAY refer to callback ACEs exclusively as "conditional ACEs" for simplicity.

## 5.4.4 SACL ACE types

### 5.4.4.1 Audit ACEs

Trigger audit log entries when matching access attempts occur. The AceFlags field carries SUCCESSFUL_ACCESS_ACE_FLAG (0x40) and/or FAILED_ACCESS_ACE_FLAG (0x80).

| Structure | Value | Effect |
|---|---|---|
| SYSTEM_AUDIT_ACE | 0x02 | Audit access matching the SID and mask. |
| SYSTEM_AUDIT_OBJECT_ACE | 0x07 | Audit access scoped to a GUID. |
| SYSTEM_AUDIT_CALLBACK_ACE | 0x0D | Conditional audit. |
| SYSTEM_AUDIT_CALLBACK_OBJECT_ACE | 0x0F | Conditional audit, scoped to GUID. |

### 5.4.4.2 Alarm ACEs (continuous auditing)

> [!NOTE]
> These ACE types are reserved but unimplemented in the reference model. KACS repurposes them for continuous per-operation auditing: unlike standard audit ACEs (which emit a single event at handle creation), alarm ACEs configure per-operation audit masks that persist on the open handle. This creates no interoperability conflict because external sources never contain alarm ACEs.

| Structure | Value | Effect |
|---|---|---|
| SYSTEM_ALARM_ACE | 0x03 | Continuous audit for matching SID and mask. |
| SYSTEM_ALARM_OBJECT_ACE | 0x08 | Continuous audit scoped to a GUID. |
| SYSTEM_ALARM_CALLBACK_ACE | 0x0E | Conditional continuous audit. |
| SYSTEM_ALARM_CALLBACK_OBJECT_ACE | 0x10 | Conditional continuous audit, scoped to GUID. |

### 5.4.4.3 Mandatory label ACE

Defines the object's integrity level for MIC. Conforming producers SHOULD emit
at most one non-inherit-only mandatory-label ACE per SACL. Imported or existing
SACLs MAY contain multiple mandatory-label ACEs; MIC uses the first
non-inherit-only mandatory-label ACE as described in the Peios Kernel TRM
§3.8.3. Inherit-only
mandatory-label ACEs do not apply to the current object. The SID encodes the
integrity level. The access mask encodes the MIC policy (which operations are
blocked for non-dominant callers).

| Structure | Value | Effect |
|---|---|---|
| SYSTEM_MANDATORY_LABEL_ACE | 0x11 | Sets the object's integrity level and MIC policy. |

### 5.4.4.4 Resource attribute ACE

Attaches name-value attributes to the object for conditional ACE evaluation. The ACE's SID is always Everyone (`S-1-1-0`).

| Structure | Value | Effect |
|---|---|---|
| SYSTEM_RESOURCE_ATTRIBUTE_ACE | 0x12 | Defines a resource attribute on the object. |

### 5.4.4.5 Scoped policy ID ACE

References a central access policy by SID. During AccessCheck, the referenced policy's rules are evaluated in addition to the object's own DACL.

| Structure | Value | Effect |
|---|---|---|
| SYSTEM_SCOPED_POLICY_ID_ACE | 0x13 | References a central access policy. |

### 5.4.4.6 Process trust label ACE

Defines the object's PIP trust level. The SID encodes the PIP type and trust level. The access mask specifies the exact rights that non-dominant callers are allowed.

| Structure | Value | Effect |
|---|---|---|
| SYSTEM_PROCESS_TRUST_LABEL_ACE | 0x14 | Sets the object's PIP trust level. |

## 5.4.5 Allocated but unimplemented ACE types

Two values in the range have a name but no KACS behaviour.

| Structure | Value | Notes |
|---|---|---|
| ACCESS_ALLOWED_COMPOUND_ACE | 0x04 | Never implemented. Reserved. |
| SYSTEM_ACCESS_FILTER_ACE | 0x15 | Defined by MS-DTYP. Named by the KACS ABI for format parity; no KACS semantics. |

`ACCESS_ALLOWED_COMPOUND_ACE` was specified and then abandoned before any
conforming system implemented it. Nothing produces it.

`SYSTEM_ACCESS_FILTER_ACE` is different: it is a live MS-DTYP ACE type
that KACS has not implemented. The kernel ABI defines a constant for it
(`KACS_ACE_TYPE_SYSTEM_ACCESS_FILTER`) so that a decoder can put a name
to the byte, but no section of this specification assigns it meaning. A 0x15 ACE therefore takes the
unrecognised-ACE path described at the end of this section: skipped
during evaluation, preserved byte-for-byte on round-trip. An
implementation MUST NOT grant, deny, audit, or filter access on the
basis of a 0x15 ACE.

Values above 0x15 are unallocated.

## 5.4.6 AceType constants

The tables above name each ACE **structure**. The `AceType` header field
carries a constant with its own name, which is what a reader decoding a
descriptor by hand will be holding. Both spellings, in value order:

| Value | AceType constant | Structure |
|---|---|---|
| 0x00 | `ACCESS_ALLOWED_ACE_TYPE` | `ACCESS_ALLOWED_ACE` |
| 0x01 | `ACCESS_DENIED_ACE_TYPE` | `ACCESS_DENIED_ACE` |
| 0x02 | `SYSTEM_AUDIT_ACE_TYPE` | `SYSTEM_AUDIT_ACE` |
| 0x03 | `SYSTEM_ALARM_ACE_TYPE` | `SYSTEM_ALARM_ACE` |
| 0x04 | `ACCESS_ALLOWED_COMPOUND_ACE_TYPE` | `ACCESS_ALLOWED_COMPOUND_ACE` |
| 0x05 | `ACCESS_ALLOWED_OBJECT_ACE_TYPE` | `ACCESS_ALLOWED_OBJECT_ACE` |
| 0x06 | `ACCESS_DENIED_OBJECT_ACE_TYPE` | `ACCESS_DENIED_OBJECT_ACE` |
| 0x07 | `SYSTEM_AUDIT_OBJECT_ACE_TYPE` | `SYSTEM_AUDIT_OBJECT_ACE` |
| 0x08 | `SYSTEM_ALARM_OBJECT_ACE_TYPE` | `SYSTEM_ALARM_OBJECT_ACE` |
| 0x09 | `ACCESS_ALLOWED_CALLBACK_ACE_TYPE` | `ACCESS_ALLOWED_CALLBACK_ACE` |
| 0x0A | `ACCESS_DENIED_CALLBACK_ACE_TYPE` | `ACCESS_DENIED_CALLBACK_ACE` |
| 0x0B | `ACCESS_ALLOWED_CALLBACK_OBJECT_ACE_TYPE` | `ACCESS_ALLOWED_CALLBACK_OBJECT_ACE` |
| 0x0C | `ACCESS_DENIED_CALLBACK_OBJECT_ACE_TYPE` | `ACCESS_DENIED_CALLBACK_OBJECT_ACE` |
| 0x0D | `SYSTEM_AUDIT_CALLBACK_ACE_TYPE` | `SYSTEM_AUDIT_CALLBACK_ACE` |
| 0x0E | `SYSTEM_ALARM_CALLBACK_ACE_TYPE` | `SYSTEM_ALARM_CALLBACK_ACE` |
| 0x0F | `SYSTEM_AUDIT_CALLBACK_OBJECT_ACE_TYPE` | `SYSTEM_AUDIT_CALLBACK_OBJECT_ACE` |
| 0x10 | `SYSTEM_ALARM_CALLBACK_OBJECT_ACE_TYPE` | `SYSTEM_ALARM_CALLBACK_OBJECT_ACE` |
| 0x11 | `SYSTEM_MANDATORY_LABEL_ACE_TYPE` | `SYSTEM_MANDATORY_LABEL_ACE` |
| 0x12 | `SYSTEM_RESOURCE_ATTRIBUTE_ACE_TYPE` | `SYSTEM_RESOURCE_ATTRIBUTE_ACE` |
| 0x13 | `SYSTEM_SCOPED_POLICY_ID_ACE_TYPE` | `SYSTEM_SCOPED_POLICY_ID_ACE` |
| 0x14 | `SYSTEM_PROCESS_TRUST_LABEL_ACE_TYPE` | `SYSTEM_PROCESS_TRUST_LABEL_ACE` |
| 0x15 | `SYSTEM_ACCESS_FILTER_ACE_TYPE` | `SYSTEM_ACCESS_FILTER_ACE` |

## 5.4.7 ACL revision

ACLs carry a revision number that constrains which ACE types MAY appear:

- **ACL_REVISION (0x02)** — basic ACE types (0x00, 0x01, 0x02, 0x03), mandatory label (0x11), resource attribute (0x12), scoped policy (0x13), and process trust label (0x14).
- **ACL_REVISION_DS (0x04)** — additionally permits object-type ACEs (0x05–0x08), callback ACEs (0x09–0x0C, 0x0D–0x10). Required for Active Directory access control.

When creating new ACLs containing only recognised ACE types, the revision MUST
be set to the minimum required by the ACE types present. When rewriting an
existing ACL while preserving one or more unrecognised ACE types, KACS MUST set
the revision to the greater of the minimum required by recognised ACE types
present and the source ACL revision. When parsing ACLs, KACS MUST NOT reject an
ACL based on revision-vs-ACE-type mismatch — accept permissively, write
correctly.

> [!NOTE]
> This diverges from strict MS-DTYP interpretation, where ACL_REVISION (0x02) does not permit SACL types 0x11-0x14. KACS accepts them under either revision to handle SDs that may have been constructed with a less strict tool. The evaluator handles all ACE types correctly regardless of ACL revision.

Unrecognised ACE types — every value not given semantics above, which
today means 0x04, 0x15, and everything from 0x16 up — MUST be silently
skipped during evaluation and preserved byte-for-byte during round-trip serialisation. The ACE's raw bytes (from `AceType` through `AceType + AceSize`) are stored opaquely and written back unchanged. ACEs with `AceSize` not a multiple of 4 MUST be rejected (the containing ACL is malformed).
