6.7 Access Decisions
Every access decision is recorded the same way: by the kernel's access audit, under the SACL of the object decided on. That holds for a decision a userspace component makes about objects it guards itself — a service manager's services, an event store's namespaces — as much as for a file. "Who was refused what" is then one query, and a SACL governs a daemon's objects as it governs a file.
6.7.1 A component that guards objects #
An emitter that decides access to objects of its own:
- MUST make each decision through the kernel's access check, against the object's security descriptor;
- MUST pass the object's identity as the check's audit context (below);
- MUST NOT write an event of its own recording the decision, whether it granted or refused. It MAY write a log line about a refusal for its own debugging; that line is not the audit record.
6.7.2 The audit context #
The audit context is one MessagePack map:
| Key | Value |
|---|---|
kind | The kind of object, a kebab-case string: service, job |
<kind> | Optional. A map of the object's identifying fields, under a key equal to the value of kind |
{kind: "service", service: {name: "jellyfin"}}
The map MUST contain kind, MAY contain the map named by it, and MUST
contain nothing else. The kernel rejects a context that is not exactly
this shape: kind must be a string matching the segment grammar of
§6.3, the map under the kind's key must not be empty, and every key in
that map must match the same grammar, because each becomes a segment of
object.<kind>.<key>. A context with no body map is accepted; an empty
body map is not. Each field in that map MUST be a field the
component's fragment defines beneath object.<kind>, and MUST be marked
asserted there (§6.10).
The kernel's record of the decision then carries object.kind and the
object's fields as object.service.name and so on, beside the subject,
the access requested and granted, and the trigger, exactly as for any
other object. How the kernel's access-check interface accepts and copies
the context is described in the Peios Kernel TRM.
6.7.3 Asserted fields #
The kernel cannot verify that a descriptor belongs to the service a component names. A field the kernel copies from userspace is therefore the component's claim: it is the identity of what the component actually enforced, but the kernel did not observe it.
A kernel-originated record that carries any asserted value MUST carry
fields.attestation.userspace with the value true. A record that
carries none MUST omit the field. A record written by userspace never
carries it, because the origin class in its header (§6.4) already says
that the whole payload is the emitter's word.
A consumer MUST NOT present an asserted field as observed by the kernel.
6.7.4 The emitter and the subject #
On a decision a component requests for a client, the record's header
names the component, as emitter.*, and the payload's subject.* names
the client whose access was checked (§6.4). The component's process
details are emitter.process.*.
6.7.5 Default SACLs #
A component that guards objects SHOULD give each a default SACL with an audit ACE that records failed access by Everyone, so that a refusal is recorded without configuration. An administrator can narrow or remove it per object like any other SACL. The exception is a kind of object so numerous that recording every refusal would bury the record, which is why files and registry keys carry no default SACL.
A component that stores a descriptor where writing the stored value replaces the SACL without the privilege that normally guards a SACL — for example, a whole descriptor kept as one registry value — MUST allow that store to be written only by principals trusted to change audit policy. A tool that edits such a descriptor's DACL MUST keep its SACL intact.