3.2.2 Token Structure
Token fields fall into three mutability classes. Fixed fields are set at creation and never change — every security-critical identity field is fixed. Adjustable fields can be modified at runtime through the adjustment operations (§3.2.5). One-way fields can be set or tightened but never cleared or loosened.
3.2.2.1 Identity core (fixed) #
| Field | Type | Description |
|---|---|---|
user_sid | SID | The token's primary identity. |
user_deny_only | bool | When true, the user SID matches only deny ACEs, never allow ACEs. Set at creation by CreateToken or FilterToken. True whenever write_restricted is true. |
groups | SID_AND_ATTRIBUTES[] | Group memberships. The set of SIDs is fixed at creation; the per-group attribute flags are adjustable. |
restricted_sids | SID_AND_ATTRIBUTES[]? | Secondary SID list for restricted tokens, null on unrestricted ones. Set at creation by CreateToken or FilterToken. AccessCheck treats the list as presence-based: a restricting SID participates whenever it is present, and neither SE_GROUP_ENABLED nor SE_GROUP_USE_FOR_DENY_ONLY affects restricted-pass matching. |
write_restricted | bool | When true, the restricted SID check applies only to write access. Set at creation by CreateToken or FilterToken. |
The logon SID — S-1-5-5-X-Y, tying the token to its
LogonSession — is not stored as a token field at all. It is derived
from the session on every read, and materialised once in groups
carrying SE_GROUP_LOGON_ID, which is where AccessCheck finds it.
The group SID set is fixed at creation — adjustment never adds or
removes a SID. Individual groups can be enabled or disabled by
modifying SE_GROUP_ENABLED, within two limits: a mandatory group
(SE_GROUP_MANDATORY) cannot be disabled, and a deny-only group
(SE_GROUP_USE_FOR_DENY_ONLY) cannot be re-enabled.
A token holds at most 1024 group entries including the kernel-injected logon SID, so CreateToken accepts at most 1023 caller-supplied groups.
3.2.2.2 Token type (fixed) #
| Field | Type | Description |
|---|---|---|
token_type | enum | Primary or Impersonation. |
impersonation_level | enum | Anonymous, Identification, Impersonation, or Delegation. Primary tokens always carry Anonymous. |
3.2.2.3 Integrity (fixed) #
| Field | Type | Description |
|---|---|---|
integrity_level | uint | Numeric integrity level. Standard values are 0 (Untrusted), 4096 (Low), 8192 (Medium), 12288 (High), 16384 (System), but any unsigned integer is valid and is compared numerically against object labels. |
mandatory_policy | flags | NO_WRITE_UP (0x0001) and NEW_PROCESS_MIN (0x0002). Per-token MIC enforcement policy, set at creation. |
mandatory_policy is immutable: a process cannot change its own MIC
constraints at runtime, neither loosening nor tightening them. This is
a deliberate departure from MS-DTYP, where the mandatory policy is
runtime-modifiable — which reduces MIC to a constraint that stops only
processes not actively trying to bypass it. Immutability is what makes
MIC a real boundary here, and it is also what allows the impersonation
integrity ceiling (§3.5) to be enforced unconditionally.
3.2.2.4 Privileges (adjustable) #
Each privilege on a token has four independent states.
Present — the privilege exists on the token. A present privilege can be removed permanently, but no privilege can be added after creation. Enabled — the privilege is currently active; only present privileges can be enabled or disabled. Enabled by default — the creation-time enabled state, which adjustment can restore. Used — the privilege has been exercised during this token's lifetime; monotonic, and never cleared once set.
The lifecycle runs: present and disabled, to enabled, to used, then optionally disabled, then optionally removed permanently. Removal clears the privilege from the present, enabled, and enabled-by-default states together.
The four states are encoded as four 64-bit bitmasks, one bit per defined privilege, which makes a privilege check constant-time and a multi-privilege operation atomic.
3.2.2.5 Elevation (one-way) #
| Field | Type | Description |
|---|---|---|
elevation_type | enum | Default (non-elevated), Full (elevated), or Limited (filtered). |
A token is created as Default. Only KACS_IOC_LINK_TOKENS sets Full
or Limited, when a linked pair is established, and once set neither
reverts to Default on that token object. The role is sticky: relinking
can replace the partner but never converts Full to Limited or the
reverse. DuplicateToken and FilterToken produce new token objects
whose elevation_type starts again at Default, because a new token is
not part of any linked pair.
Linked pairs are associated at the LogonSession level rather than stored on individual tokens; §3.2.6 describes the pairing mechanism.
3.2.2.6 Default object security (adjustable) #
| Field | Description |
|---|---|
owner_sid_index | Index into [user_sid, groups...] selecting the default owner SID for new objects: 0 is the user SID, 1..N are groups[0..N-1]. The referenced SID is the user SID or a group carrying SE_GROUP_OWNER. |
primary_group_index | Index into [user_sid, groups...] selecting the default primary group. The referenced SID is the user SID or any group SID on the token. |
default_dacl | The DACL applied to objects this token creates when no explicit descriptor is supplied. |
Storing indices rather than SID copies keeps the owner and primary group consistent with the group array they name.
3.2.2.7 Metadata (fixed) #
| Field | Type | Description |
|---|---|---|
token_id | LUID | Unique identifier for this token instance. |
token_guid | UUID | 128-bit identifier for this token instance, generated by the kernel at creation and immutable. Used by KMES and other kernel-internal consumers for identity stamping and event correlation. |
auth_id | LUID | The LogonSession LUID, linking the token to the authentication event that produced it. |
source | TOKEN_SOURCE | Who minted the token: an 8-character name plus a LUID. |
created_at | timestamp | When the original token was minted by CreateToken. Copied unchanged by DuplicateToken, FilterToken, and NEW_PROCESS_MIN, so it tracks original minting rather than duplication. |
expiration | timestamp | When the token becomes invalid; zero means no expiry. Not enforced by AccessCheck. |
origin | LUID | The originating LogonSession for derived tokens, such as S4U or network logon. |
3.2.2.8 Mutation tracking (adjustable) #
| Field | Description |
|---|---|
modified_id | Counter incremented on any token adjustment. |
Marking a privilege used is audit and accounting state rather than an
access-decision input, so it stays monotonic but does not bump
modified_id. Setting the elevation type is the one other mutation
that leaves the counter alone.
The counter is intended as a cache invalidation key — a modified_id
that has changed since a cached decision was taken means the decision
is stale. Nothing currently reads it for that purpose: it is
maintained on every adjustment and reported through the statistics
query class, and no cache anywhere invalidates on it.
3.2.2.9 Interactivity scope (adjustable) #
| Field | Description |
|---|---|
interactivity_scope | 0 for services, which have no interactive environment; 1 and above for interactive and remote user environments. Changing it requires SeTcbPrivilege. |
3.2.2.10 Claims and security attributes (fixed) #
| Field | Type | Description |
|---|---|---|
user_claims | CLAIM_ATTRIBUTES[] | Name-value pairs from the user's directory object, fed into conditional ACE evaluation. |
device_claims | CLAIM_ATTRIBUTES[] | Name-value pairs from the machine's directory object. |
3.2.2.11 LCS registry credentials (fixed) #
| Field | Type | Description |
|---|---|---|
lcs_scope_guids | GUID[] | Ordered private registry scope GUIDs used by LCS private hive routing. LCS checks the list in order before falling back to global hives. |
lcs_private_layers | string[] | Registry layer names visible to this token even when disabled globally, using LCS layer-name syntax and matching rules. |
These are KACS-owned credential material belonging to LCS. They are
fixed at creation and copied by duplication and filtering, and
attaching them is authorized by the same trusted-minting gate as the
rest of CreateToken — only a caller holding SeCreateTokenPrivilege
can create a token carrying them.
3.2.2.12 Device identity (fixed) #
| Field | Type | Description |
|---|---|---|
device_groups | SID_AND_ATTRIBUTES[]? | The machine's group memberships, for compound identity. |
restricted_device_groups | SID_AND_ATTRIBUTES[]? | Filtered device groups for restricted tokens. |
3.2.2.13 Confinement (fixed) #
| Field | Type | Description |
|---|---|---|
confinement_sid | SID? | Places the token in a default-deny sandbox; null means unconfined. When set, AccessCheck switches to default-deny and access requires an explicit grant to this SID or to a SID present in confinement_capabilities. |
confinement_capabilities | SID_AND_ATTRIBUTES[] | Declared access capabilities for a confined process, empty if none. The attributes field is carried for wire-format uniformity only: AccessCheck treats capability membership as presence-based and consults neither SE_GROUP_ENABLED nor SE_GROUP_USE_FOR_DENY_ONLY when matching confinement SIDs. |
isolation_boundary | bool | Adds namespace filtering on top of confinement, making objects outside the boundary invisible rather than merely denied. Requires confinement_sid. Settable at creation but not enforced. |
confinement_exempt | bool | Escape hatch: confinement restrictions are not evaluated at all. |
ALL_APPLICATION_PACKAGES participates only when it is present in
confinement_capabilities. KACS never synthesises it, and equally
never rejects an otherwise valid confined token merely because it is
present — strict confinement is expressed by the caller omitting it.
Deciding which capabilities a package token receives is authd's job
and policy tooling's, not the kernel's.
3.2.2.14 Audit (fixed) #
| Field | Type | Description |
|---|---|---|
audit_policy | u32 | Per-token audit overrides as a bitmask, fixed at creation — no adjustment operation exists. |
| Flag | Value | Description |
|---|---|---|
OBJECT_ACCESS_SUCCESS | 0x0001 | Audit successful object access. |
OBJECT_ACCESS_FAILURE | 0x0002 | Audit failed object access. |
PRIVILEGE_USE_SUCCESS | 0x0004 | Audit successful privilege exercises: the privilege contributed requested bits that survive into the final granted result. |
PRIVILEGE_USE_FAILURE | 0x0008 | Audit failed privilege exercises: the privilege contributed requested bits during evaluation, but they do not survive into the final result. |
The policy is additive. It forces audit events that system-wide policy would not generate, and cannot suppress events that system-wide policy requires. It follows impersonation: when service A impersonates client B and B's token has a category enabled, operations during impersonation are audited under B's identity. The creation default is 0.
3.2.2.15 Credential projection (fixed) #
| Field | Description |
|---|---|
projected_uid | Linux UID for the user SID, precomputed by authd when the token is minted (PSPU §2); 65534 for the anonymous identity. |
projected_gid | Linux primary GID, precomputed the same way; 65534 for the anonymous identity. |
projected_supplementary_gids | Linux supplementary GIDs, one per group SID, precomputed the same way. |
authd computes these at token creation and they are stored on the token; KACS never resolves a SID-to-UID mapping at runtime. Projection reflects all groups regardless of enabled state, so adjusting groups does not trigger recalculation. §3.10 covers how the projection is used.
3.2.2.16 Token security (adjustable) #
| Field | Description |
|---|---|
security_descriptor | The token's own SD, controlling who may query, adjust, duplicate, or impersonate it. |
3.2.2.17 Internal #
| Field | Description |
|---|---|
refcount | Reference count; the token is freed when the last reference drops. Not exposed to userspace. |