On this page
- §13.1.1 Token lifecycle
- §13.1.1.1 kacs_open_self_token
- §13.1.1.2 kacs_open_process_token
- §13.1.1.3 kacs_open_thread_token
- §13.1.1.4 kacs_create_token
- §13.1.1.5 kacs_open_peer_token
- §13.1.2 Impersonation
- §13.1.2.1 kacs_impersonate_peer
- §13.1.2.2 kacs_revert
- §13.1.2.3 kacs_set_impersonation_level
- §13.1.3 LogonSession management
- §13.1.3.1 kacs_create_logon_session
- §13.1.3.2 kacs_destroy_empty_logon_session
- §13.1.4 Privilege operations
- §13.1.4.1 kacs_privilege_check
- §13.1.5 File operations
- §13.1.5.1 kacs_open
- §13.1.5.2 kacs_get_sd
- §13.1.5.3 kacs_set_sd
- §13.1.5.4 kacs_get_mount_policy
- §13.1.5.5 kacs_set_mount_policy
- §13.1.6 AccessCheck
- §13.1.6.1 kacs_access_check
- §13.1.6.2 kacs_access_check_list
- §13.1.7 Central Access and Auditing Policy
- §13.1.7.1 kacs_set_caap
- §13.1.8 PSB management
- §13.1.8.1 kacs_set_psb
Syscalls
KACS registers syscalls from a reserved block in the arch-specific syscall table. Each syscall does one thing with typed parameters — no multiplexing.
§13.1.1 Token lifecycle
§13.1.1.1 kacs_open_self_token
Opens the calling thread's effective token as a token fd.
| Parameter | Description |
|---|---|
flags |
KACS_REAL_TOKEN: return the primary token even if impersonating. 0: return the effective token. |
access_mask |
Desired access for the returned handle (TOKEN_QUERY, TOKEN_DUPLICATE, etc.). |
| Returns | Token fd on success, -errno on failure. |
No privilege required. The access mask is checked against the token's own SD.
§13.1.1.2 kacs_open_process_token
Opens the primary token of another process.
| Parameter | Description |
|---|---|
pidfd |
pidfd identifying the target process. |
access_mask |
Desired access for the returned handle. |
| Returns | Token fd on success, -errno on failure. |
Three checks: (1) PROCESS_QUERY_INFORMATION on the target process's SD, (2) PIP dominance over the target process, (3) the requested access_mask on the token's own SD.
The returned handle is a live reference — mutable fields (privilege enable/disable) are visible through this handle.
§13.1.1.3 kacs_open_thread_token
Opens the effective token of a specific thread.
| Parameter | Description |
|---|---|
pidfd |
pidfd identifying the target process. |
tid |
Thread ID within the target process. |
access_mask |
Desired access for the returned handle. |
| Returns | Token fd on success, -errno on failure. |
If the thread is impersonating, returns the impersonation token. Otherwise returns the process's primary token. Access checks: PROCESS_QUERY_INFORMATION on the target process's SD, PIP dominance, and the requested access_mask on the token's own SD.
§13.1.1.4 kacs_create_token
Mints a new token from a wire-format specification.
| Parameter | Description |
|---|---|
spec |
Wire-format token specification. |
spec_len |
Length in bytes. |
| Returns | Token fd on success, -errno on failure. |
Requires SeCreateTokenPrivilege. The kernel validates: all SIDs well-formed, owner/primary_group indices valid, auth_id references an existing LogonSession, Primary tokens have impersonation_level = Anonymous, write_restricted = true requires user_deny_only = true, isolation_boundary = true requires confinement_sid present, wire format _reserved1 (elevation_type) must be 0, and any LCS registry credential extension is structurally valid. See §4.4 for the full validation list.
The kernel MUST NOT authenticate the user, look up SIDs, or resolve mappings.
The kernel generates: token_id, token_guid (UUIDv4), modified_id (= token_id), created_at (current time), elevation_type (always Default), and the token's own SD (default template). The kernel derives the logon SID from logon_session_id (S-1-5-5-{high}-{low}) and appends it to the groups array with SE_GROUP_LOGON_ID. Callers MUST NOT include the logon SID in the supplied groups. owner_sid_index and primary_group_index are relative to the caller-supplied groups (0 = user SID, 1..N = caller's groups), not including the injected logon SID. See §13.7 for the wire format.
Because this syscall does not take a desired-access parameter, the returned
token fd always carries the fixed cached access mask TOKEN_ALL_ACCESS.
§13.1.1.5 kacs_open_peer_token
Extracts the peer's identity from a connected Unix socket.
| Parameter | Description |
|---|---|
sock_fd |
Connected Unix stream or seqpacket socket. |
| Returns | Token fd on success, -errno on failure. |
The peer identity is a snapshot captured at connect() time. The impersonation level stored on the socket determines the token's usability. No privilege required.
Only sockets with a KACS peer-token snapshot installed by the Unix
stream/seqpacket connect path are eligible. Unix datagram sockets,
socketpair-created sockets, and ancillary credential messages
(SCM_CREDENTIALS / SCM_SECURITY) do not create KACS peer tokens in
v0.22. Calling this syscall on a socket without a captured KACS peer token
fails closed with -EACCES.
Because this syscall does not take a desired-access parameter, the returned
token fd always carries the fixed cached access mask
TOKEN_QUERY | TOKEN_IMPERSONATE. This allows the caller to inspect the peer
identity and, if desired, pass the token to KACS_IOC_IMPERSONATE without
silently depending on unspecified handle widening.
§13.1.2 Impersonation
§13.1.2.1 kacs_impersonate_peer
Impersonates the peer's identity on the calling thread. Combines open + impersonate + close in a single kernel transition.
| Parameter | Description |
|---|---|
sock_fd |
Connected Unix stream or seqpacket socket. |
| Returns | Effective impersonation level (0-3) on success, -errno on failure. |
Returns the effective impersonation level: 0 (Anonymous), 1 (Identification), 2 (Impersonation), 3 (Delegation). The server SHOULD check the return value — a return of 1 (Identification) means the token is valid for identity queries but all AccessCheck evaluations will be denied. This avoids the diagnostic pitfall of silently failing every operation after a capped impersonation.
Follows the two-gate model (identity gate + integrity ceiling). The effective impersonation level is the minimum permitted by all constraints — if a gate caps the level, it is silently reduced. The call succeeds (no error for capping) but the return value reveals the effective level. Overwrites any existing impersonation (reverts first, then re-impersonates). Exception: restricted→unrestricted same-user impersonation is hard-denied (-EPERM) to prevent sandbox escape.
The call requires the same captured KACS peer-token state as
kacs_open_peer_token. Datagram sockets, socketpair-created sockets, and
ancillary credential messages are not socket-based impersonation authorities in
v0.22; they fail closed with -EACCES unless the caller uses an explicit
token fd through KACS_IOC_IMPERSONATE.
§13.1.2.2 kacs_revert
Reverts the calling thread to its primary token.
| Parameter | Description |
|---|---|
| (none) | |
| Returns | 0 on success (including if not impersonating). |
No privilege required.
§13.1.2.3 kacs_set_impersonation_level
Sets the maximum impersonation level a server MAY use. Called by the client before connect().
| Parameter | Description |
|---|---|
sock_fd |
Unconnected Unix stream or seqpacket socket. |
level |
ANONYMOUS (0), IDENTIFICATION (1), IMPERSONATION (2), DELEGATION (3). |
| Returns | 0 on success, -errno on failure. |
Default is IMPERSONATION.
§13.1.3 LogonSession management
§13.1.3.1 kacs_create_logon_session
Creates a new LogonSession.
| Parameter | Description |
|---|---|
spec |
Wire-format LogonSession specification (logon type, auth package, user SID). |
spec_len |
Length in bytes. |
| Returns | LogonSession ID (>= 0) on success, -errno on failure. |
Requires SeTcbPrivilege. The LogonSession receives an auto-generated logon SID (S-1-5-5-X-Y).
§13.1.3.2 kacs_destroy_empty_logon_session
Destroys a LogonSession that has not acquired any live token.
| Parameter | Description |
|---|---|
auth_id |
LogonSession ID (LUID) to destroy. |
| Returns | 0 on success, -errno on failure. |
Requires SeTcbPrivilege. The call succeeds only when the LogonSession exists,
has zero live tokens, has no linked-token state, and has no other in-flight
kernel references. On success, the kernel emits the normal
logon-session-destroyed KMES event. A nonexistent LogonSession returns
-ENOENT. A LogonSession with any live token, linked-token state, or other
in-flight kernel reference returns -EBUSY.
§13.1.4 Privilege operations
§13.1.4.1 kacs_privilege_check
Atomically checks whether the calling thread's effective token holds a set of privileges, marks them as used, and emits audit events.
| Parameter | Description |
|---|---|
privileges |
Pointer to array of privilege IDs to check. |
count |
Number of privileges in the array. |
flags |
0 or KACS_PRIVCHECK_ALL_REQUIRED. |
| Returns | 0 if all required privileges are present and enabled, -EPERM otherwise. |
When KACS_PRIVCHECK_ALL_REQUIRED is set (the default and only supported flag in v0.22), all listed privileges MUST be present and enabled for the check to succeed. On success, each checked privilege is atomically marked as used on the token. On failure, no privileges are marked used.
Audit behavior follows the token's audit_policy:
- If the check succeeds and
audit_policy & PRIVILEGE_USE_SUCCESS: emit privilege-use audit records for the checked privileges that actually succeeded. - If the check fails and
audit_policy & PRIVILEGE_USE_FAILURE: emit privilege-use audit records for the checked privileges that were missing or disabled.
The exact KMES payload schema pinned in Appendix A: Audit Event Schemas is bounded to AccessCheck-generated audit records. This syscall's audit-record schema remains outside that bounded appendix.
No privilege is required to call this syscall. TOKEN_QUERY is not required — the syscall operates on the caller's own effective token implicitly.
This is the mechanism for userspace services to exercise application-level privileges (SeSyncAgentPrivilege, SeManageVolumePrivilege, etc.) with proper audit trails. Kernel standalone privilege checks (SeDebugPrivilege in ptrace, SeShutdownPrivilege in shutdown) SHOULD also use this mechanism internally for audit consistency.
§13.1.5 File operations
§13.1.5.1 kacs_open
Opens or creates a file with an explicit desired access mask.
| Parameter | Description |
|---|---|
dirfd |
Base directory fd (or AT_FDCWD). |
path |
Pathname relative to dirfd. |
how |
Pointer to a struct kacs_open_how containing desired_access, create_disposition, create_options, sd pointer, sd_len, and flags. |
howsize |
Size of the kacs_open_how struct. Enables forward-compatible extensibility (following the openat2 pattern). |
status_out |
Out: creation status. NULL if not needed. |
| Returns | File fd on success, -errno on failure. |
The kacs_open_how struct packs all open parameters into a single extensible structure. Unknown trailing fields are rejected if non-zero, ensuring old userspace cannot accidentally trigger new behaviour.
Strict mode: every concrete bit in desired_access MUST be granted or the
open fails. If MAXIMUM_ALLOWED is present, it MUST be combined with at least
one concrete data/execute bit that defines the Linux fd mode. The concrete
data/execute bits MUST be granted, and the fd's cached KACS granted mask is
the maximum granted mask computed by AccessCheck.
§13.1.5.2 kacs_get_sd
Reads all or part of an object's security descriptor.
| Parameter | Description |
|---|---|
dirfd |
Base directory fd (or AT_FDCWD). |
path |
Pathname relative to dirfd. |
security_info |
Bitmask of which SD components to retrieve. |
buf |
Output buffer. |
buf_len |
Buffer size. 0 to probe the required size without writing. |
flags |
AT_EMPTY_PATH for O_PATH fds. |
| Returns | SD size in bytes on success (regardless of whether data was written). -ERANGE is NOT returned — a zero-length buffer is not an error, it is the probe mechanism. Other -errno values for real errors. |
Security information flags and required rights:
| Flag | Value | Required right |
|---|---|---|
| OWNER_SECURITY_INFORMATION | 0x01 | READ_CONTROL |
| GROUP_SECURITY_INFORMATION | 0x02 | READ_CONTROL |
| DACL_SECURITY_INFORMATION | 0x04 | READ_CONTROL |
| SACL_SECURITY_INFORMATION | 0x08 | ACCESS_SYSTEM_SECURITY |
| LABEL_SECURITY_INFORMATION | 0x10 | READ_CONTROL |
The returned buffer MUST contain one self-relative security descriptor subset.
Revision MUST be 1 and SE_SELF_RELATIVE MUST be set. Only the requested
components are included:
- OWNER/GROUP populate the corresponding SID offsets when requested.
- DACL populates the descriptor's DACL field when requested.
- SACL populates the descriptor's SACL field when requested.
- LABEL populates the descriptor's SACL field with the label subset only.
Requested components that are absent on the object are omitted from the subset descriptor (offset 0, corresponding PRESENT bit clear). The subset descriptor header is still returned even when every requested component is absent.
SACL_SECURITY_INFORMATION and LABEL_SECURITY_INFORMATION MUST NOT be
requested together. The combination is invalid and fails with -EINVAL.
For LABEL_SECURITY_INFORMATION, the label subset is defined as:
- if the object has an explicit mandatory-label ACE, the returned SACL contains
exactly the first non-inherit-only
SYSTEM_MANDATORY_LABEL_ACE; - if the object has no explicit mandatory-label ACE, the returned descriptor carries no SACL component.
With AT_EMPTY_PATH, the fd type determines behaviour: file fd checks the required right (READ_CONTROL or ACCESS_SYSTEM_SECURITY) against the fd's cached granted mask, O_PATH fd uses live AccessCheck against the file's SD, pidfd operates on the process SD (live AccessCheck), token fd operates on the token's own SD (live AccessCheck — the token fd's cached access mask is for token ioctls, not for SD queries).
§13.1.5.3 kacs_set_sd
Sets all or part of an object's security descriptor.
| Parameter | Description |
|---|---|
dirfd |
Base directory fd (or AT_FDCWD). |
path |
Pathname relative to dirfd. |
security_info |
Bitmask of which SD components to set. |
sd_buf |
Self-relative SD containing new values. |
sd_len |
Length in bytes. |
flags |
AT_EMPTY_PATH for O_PATH fds. |
| Returns | 0 on success, -errno on failure. |
Required rights per component: OWNER/GROUP require WRITE_OWNER, DACL requires WRITE_DAC, SACL requires ACCESS_SYSTEM_SECURITY, LABEL requires WRITE_OWNER + integrity constraints.
sd_buf MUST contain one self-relative security descriptor subset. Revision
MUST be 1 and SE_SELF_RELATIVE MUST be set. Only the components indicated by
security_info are read from the input descriptor; unindicated components are
ignored and the target object's existing values are preserved.
SACL_SECURITY_INFORMATION and LABEL_SECURITY_INFORMATION MUST NOT be set in
the same call. The combination is invalid and fails with -EINVAL.
For SACL_SECURITY_INFORMATION, the input descriptor's SACL replaces the
object's entire SACL.
For LABEL_SECURITY_INFORMATION, the input descriptor's SACL is interpreted as
the label subset only:
- no SACL component clears the object's explicit mandatory label and returns it to the default unlabeled MIC state (effective Medium with no-write-up);
- a present SACL MUST contain exactly one non-inherit-only
SYSTEM_MANDATORY_LABEL_ACEand no other ACEs; - that ACE replaces the object's explicit mandatory label while all non-label SACL ACEs are preserved unchanged.
With AT_EMPTY_PATH, the fd type determines behaviour: file fd checks the required right against the fd's cached granted mask, O_PATH fd uses live AccessCheck against the file's SD, pidfd operates on the process SD (live AccessCheck), token fd operates on the token's own SD (live AccessCheck).
§13.1.5.4 kacs_get_mount_policy
Queries the FACS mount-policy state for the superblock containing an fd.
| Parameter | Description |
|---|---|
fd |
Any fd, including O_PATH, naming an object on the target superblock. |
args |
Pointer to struct kacs_mount_policy_args. |
argsize |
Size of the caller's struct. Enables forward-compatible extensibility. |
| Returns | 0 on success, -errno on failure. |
Requires SeTcbPrivilege. On success, policy, flags, generation, and
template_sd_len are written back to args. If template_sd_ptr is non-null
and the caller's template_sd_len is large enough, the current template bytes
are copied to that buffer. If the caller's template buffer is absent or too
small, the syscall still succeeds, writes the required template length to
template_sd_len, and copies no template bytes.
§13.1.5.5 kacs_set_mount_policy
Sets the FACS mount-policy state for the superblock containing an fd.
| Parameter | Description |
|---|---|
fd |
Any fd, including O_PATH, naming an object on the target superblock. |
args |
Pointer to struct kacs_mount_policy_args. |
argsize |
Size of the caller's struct. Enables forward-compatible extensibility. |
| Returns | 0 on success, -errno on failure. |
Requires SeTcbPrivilege. policy MUST be one of facs_deny_missing,
facs_synthesize_ephemeral, or facs_synthesize_persistent. The public ABI
MUST reject unmanaged. flags and reserved padding fields MUST be zero.
facs_deny_missing requires template_sd_ptr == 0 and template_sd_len == 0
and clears the mount template. For synthesize-class policies,
template_sd_ptr == 0 and template_sd_len == 0 clears the mount template;
template_sd_ptr != 0 requires a non-zero template_sd_len no larger than 64
KiB and the pointed-to bytes MUST be one structurally valid complete
self-relative file SD. Any validation failure leaves the existing mount policy
unchanged.
§13.1.6 AccessCheck
§13.1.6.1 kacs_access_check
Evaluates AccessCheck for a userspace object manager.
| Parameter | Description |
|---|---|
args |
Pointer to a versioned struct (extensible via size field). |
| Returns | Granted access mask (>= 0) on success, -EACCES if any requested right denied, -errno for other errors. |
The args struct contains: token fd, SD pointer, desired access, GenericMapping (4 fields), optional self_sid, privilege_intent, optional object type list, optional local claims, pip_type, pip_trust, optional object_audit_context (opaque blob for audit event identification), granted_out pointer (optional — 0 = not used), continuous_audit_out pointer (optional), and staging_mismatch_out pointer (optional). local_claims uses the same length-prefixed claim-array wrapper described in §3.9. object_tree_ptr points to a flat preorder array of struct kacs_object_type_entry values defined in §13.7. pip_type/pip_trust default to the calling process's PSB values when zero. object_audit_context is optional — when null, audit events are still emitted but carry no object identification. The return value carries the granted mask directly; when granted_out_ptr is non-null, the granted mask is also written there (even on -EACCES, so the caller can see what was granted). staging_mismatch_out is written as 1 if the staged CAAP result differs from the effective result. In scalar mode this includes scalar grant deltas and audit deltas. When an object type list is present, it is also set if any node's staged granted mask differs from that node's effective granted mask.
This is the same AccessCheck pipeline used by FACS. It exists for userspace daemons that manage non-file objects (loregd, lpsd, eventd). The kernel runs the full pipeline including privilege-use tracking and the SACL audit walk. Audit events are emitted directly by the kernel through KMES — the caller provides object_audit_context (an opaque blob identifying the object being accessed, e.g., a registry key path) which is included in emitted audit events so the audit trail identifies which object the access decision was about. Exact AccessCheck audit event types and payload schemas are defined in Appendix A: Audit Event Schemas.
When an object type list is provided, kacs_access_check returns the root node's granted mask (the intersection of all nodes). For per-node results, use kacs_access_check_list.
§13.1.6.2 kacs_access_check_list
Evaluates AccessCheck with an object type list and returns per-node results.
| Parameter | Description |
|---|---|
args |
Pointer to a versioned struct. Same fields as kacs_access_check_args but object type list is mandatory. |
results |
Pointer to output array of {u32 granted, u32 status} pairs, one per node. |
results_count |
Number of entries in the results array. MUST equal the object type list node count. |
| Returns | 0 on success, -errno on failure. Per-node verdicts are in the output array. |
Each result entry contains the node's granted mask and a status (0 = granted, -EACCES = denied). A node is granted if (node.granted & mapped_desired) == mapped_desired. A denial on one node fails only that node — other nodes may still succeed.
Because kacs_access_check_list reuses struct kacs_access_check_args, the
shared optional outputs remain valid in list mode. In particular,
granted_out_ptr receives the root node's granted mask (the same intersection
value returned by scalar kacs_access_check for the same object type list).
continuous_audit_out_ptr and staging_mismatch_out_ptr keep their normal
meanings.
This is the AccessCheckResultList variant described in §10.10.
§13.1.7 Central Access and Auditing Policy
§13.1.7.1 kacs_set_caap
Pushes, replaces, or removes a CAAP in the kernel policy cache.
| Parameter | Description |
|---|---|
policy_sid |
Binary SID identifying the policy. |
policy_sid_len |
Length of the policy SID in bytes. |
spec |
Wire-format CAAP specification. NULL = remove. |
spec_len |
Length of spec. 0 = remove. |
| Returns | 0 on success, -errno on failure. |
Requires SeTcbPrivilege. See §10.8 for the wire format and cache semantics.
§13.1.8 PSB management
§13.1.8.1 kacs_set_psb
Sets mitigation fields on a process's PSB. PIP fields are not settable via syscall — they are determined exclusively by the kernel at exec time from the binary's cryptographic signature (see §6.1).
| Parameter | Description |
|---|---|
pidfd |
Target process (-1 = self). |
mitigations |
Bitmask of mitigations to enable (one-way). |
| Returns | 0 on success, -errno on failure. |
Mitigations are one-way: bits can only be turned on. Any process MAY set mitigations on itself (no privilege required). Setting mitigations on another process requires PROCESS_SET_INFORMATION on the target's process SD and PIP dominance.