3.10.1 Credential Projection

KACS tokens are the sole identity-based authorization mechanism, and Linux applications do not know tokens exist. They call getuid(), getgid() and getgroups(), read /proc/self/status, and assume those numbers determine their access. KACS projects token identity onto standard Linux credentials so unmodified applications work.

When a token is installed on a process, the process's Linux credentials are set to match. The numbers themselves are already on the token: the user SID's projected uid, the primary group SID's projected gid, and a projected supplementary gid per group SID are all computed by authd when the token is minted, and KACS copies them.

KACS never resolves a SID to a number itself. It holds no directory handle and consults nothing at install time — which is what makes projection cheap enough to do on every credential change, and what keeps a name-service outage from being able to change what a running process may do.

How a SID becomes a number is therefore not KACS's rule to state, and this chapter deliberately does not restate it. The authority is the principal source interface's numeric scope (PSPU §2): identifiers are computed — an authority grants a source a band (base, count) and derives base + r from a relative identifier — rather than looked up per principal, and a source's assertion outside its band is refused rather than clamped.

65534 does appear in KACS, but not as a "no attribute was set" fallback: it is ANONYMOUS_PROJECTED_ID, what the projected-id accessors return for the anonymous identity and for an invalid token pointer. It is a sentinel for no identity, not a default for an identity whose number could not be found.

The consequences are mostly convenient ones. No process runs as UID 0 unless it holds the SYSTEM token — enforced, not merely expected: a token creation naming a projected UID of 0 with any user SID other than S-1-5-18 is rejected, and the projection path refuses it again at install time. Home directories work naturally, because getpwuid(getuid()) returns the right answer when the UID is real and consistent with NSS. And different services get different UIDs, which is incidental defence in depth alongside KACS's own enforcement.

3.10.1.1 Projection is one-way #

Token state flows into credential fields and never the reverse. The projected credentials are observational compatibility data; the token is the authority. The setuid family restores the old credential rather than deriving a token from it (§3.10.3).

Projection reflects all groups regardless of enabled state, so adjusting groups never triggers recalculation.

Projected credentials reflect the effective token — the impersonated one during impersonation, the primary one otherwise. When a service thread impersonates a client and creates a file, the file is owned by the client's projected UID, quota is charged to the client, and audit attributes to the client.

The two accessors deliberately disagree during impersonation. current_fsuid() reads the projected UID from the effective credential, so it yields the client's UID; getuid() reads the primary credential's UID, so it yields the service's. During impersonation getuid() returns the service and current_fsuid() returns the client, and that is the intended behaviour rather than an inconsistency.

One caveat applies to a credential carrying no token at all — a blank credential, or one created before KACS initialised. current_fsuid() falls back to cred->fsuid in that case, and the capability switchboard denies before consulting the ALLOW list (§3.10.2), so Linux DAC becomes authoritative for such a task.

3.10.1.2 Precomputed values #

Every token carries precomputed projected UID and GID values, calculated by authd at creation and stored on the token. KACS never resolves a SID-to-UID mapping at runtime; the accessors are pure field reads.

Because SIDs are one namespace while Linux UIDs and GIDs are two, authd allocates from a single unified counter across all principal types rather than a separate one per namespace, so every SID projects to a unique number whichever Linux namespace it lands in. A user and a group can never collide on a number, which is what lets one SID answer both getuid() and getgid() questions without ambiguity.

Edit this page