5.3.5 Private Layers

A private layer is a disabled layer attached to a thread's credentials. It is invisible during ordinary resolution and treated as enabled when resolving on behalf of a thread whose token names it.

That covers three things a shared registry otherwise cannot do: giving one session experimental settings without affecting others; injecting test configuration without touching the shared tree; and giving a container a different view of the registry without a separate hive.

5.3.5.1 Resolution #

A private layer participates in normal precedence ordering. A disabled layer with precedence 5 attached to a thread resolves at precedence 5, competing with everything else at that level. It is not an overlay on top; it is a layer that only that thread can see.

The activity test is exactly: a layer is active for a thread if it is globally enabled, or its name appears in that thread's private layer set. Name matching uses Unicode Simple Case Folding, like every other layer name comparison.

5.3.5.2 Attachment #

Private layer names reach a thread through the KACS token's LCS credential extension — the same versioned block that carries scope GUIDs for private hives (§5.2.2). LCS reads the credentials from the effective token on each operation and passes them into resolution.

Private layers are therefore per-thread, not per-process: threads in one process can hold different private layer sets through different impersonation tokens.

5.3.5.3 Two things about the caps #

MaxPrivateLayersPerToken, default 16, is described as a limit on attachment. It is not enforced there. KACS applies its own hard cap of 256 names when it parses the token specification, and the configurable LCS limit is applied later, when LCS acquires a thread's private credentials for an operation.

The consequence is that a token carrying seventeen private layers is accepted by KACS and then fails every LCS operation, rather than being refused when it was built. Reading LCS's configured limits from KACS would invert the dependency between the two, so the cap stays where it can be read.

The failure is E2BIG. It was EACCES, which read as an access-control denial and sent anyone debugging it towards descriptors and privileges rather than towards a count that was fixed when the token was assembled, possibly in another process. MaxScopeGUIDsPerToken shares the check and the errno. A missing token is still EACCES, because that one is an access decision.

KACS also deduplicates private layer names using ASCII case-insensitive comparison, where LCS matches them with Unicode Simple Case Folding. Two names that LCS would treat as one layer can both sit on a token.

5.3.5.4 The privilege that is not checked #

Attaching a private layer whose precedence is above 0 ought to require SeTcbPrivilege — otherwise an unprivileged process can attach an existing high-precedence disabled layer to its own credentials and see, and potentially influence, Group Policy-tier configuration.

That check does not exist. KACS never consults the LCS layer table when parsing the credential extension, and there is no precedence lookup and no privilege test anywhere on the attachment path. What does gate attachment is SeCreateTokenPrivilege, because private layer names can only enter a token when the token is created — the same blanket gate that governs scope GUIDs.

Edit this page