On this page
Checking access
This guide walks a complete access decision: you have an object to protect, a caller to check, and you want KACS to tell you what they're allowed. The exhaustive detail lives in access.h and security.h; here we put them together.
When to use this
Reach for peios_access_check when your program is the resource manager — you own something that isn't a kernel object (a document, an API route, a record) and you want to gate it with Peios identities and rules. For actual kernel objects, don't pre-check; just perform the operation and let the kernel enforce.
Remember the check is advisory: it computes the answer, you enforce it.
Step 1 — describe what protects the object
An object is protected by a security descriptor. If you don't already have one, build it. Say the resource should be readable and writable by its owner and read-only for a "viewers" group:
/* An ACL: allow OWNER full, allow VIEWERS read. */
peios_acl_builder *acl = peios_acl_builder_new();
peios_acl_builder_allow(acl, owner_sid, owner_len, KACS_ACCESS_ALL, 0);
peios_acl_builder_allow(acl, viewers_sid, viewers_len, KACS_ACCESS_READ, 0);
size_t acl_len;
const void *acl_bytes = peios_acl_builder_bytes(acl, &acl_len);
/* Wrap it in a security descriptor with an owner. */
peios_sd_builder *sd = peios_sd_builder_new();
peios_sd_builder_owner(sd, owner_sid, owner_len);
peios_sd_builder_dacl(sd, acl_bytes, acl_len);
size_t sd_len;
const void *sd_bytes = peios_sd_builder_bytes(sd, &sd_len);
(You can also write the descriptor as SDDL text and parse it — often easier when the policy is fixed.)
Step 2 — identify the subject
The subject is a token. Most often it's the caller you opened from a socket, or your own effective token. In the request you pass either a token fd or -1 for your own effective token.
Step 3 — run the check
Fill in a request and call. desired is what you're testing; mapping folds any generic rights to the object class's specific bits (use the class's published mapping — here we'll treat the object like a file):
struct peios_access_request req = {
.token_fd = caller_fd, /* or -1 for my own token */
.sd = sd_bytes, .sd_len = sd_len,
.desired = KACS_ACCESS_READ | KACS_ACCESS_WRITE,
.mapping = peios_file_generic_mapping,
};
uint32_t granted = 0;
int rc = peios_access_check(&req, &granted, NULL);
Step 4 — interpret the result
if (rc == 0) {
/* Every desired right was granted. */
} else if (errno == EACCES) {
/* Denied. `granted` still holds what WAS allowed — e.g. maybe READ
succeeded but WRITE did not. Decide per-right from `granted`. */
bool may_read = granted & KACS_ACCESS_READ;
bool may_write = granted & KACS_ACCESS_WRITE;
} else {
/* A real error: bad token fd, malformed SD, etc. */
perror("access_check");
}
The key idea: a denial is not an error to log and bail on — it's the expected "no". And because granted is filled even on denial, you can ask for a broad set of rights in one call and read back exactly which subset the subject has, rather than probing right by right. Clean up the builders (peios_acl_builder_free, peios_sd_builder_free) and the token fd when done.
Per-property checks
If your object has properties or property-sets with their own object ACEs, evaluate the whole tree in one call with peios_access_check_list: supply an object-type tree and get one result per node, so you learn (for instance) that a caller may read most of an object but not one protected field — without a separate check per field.
Auditing a decision
Pass a non-NULL peios_access_audit to learn what a SYSTEM_AUDIT ACE match would log, and whether a staged central access policy would decide differently — the signal you watch when rolling out a policy change.
Next
access.hreference — every field of the request and the audit outputs.- Access decisions — the operator-side account of how KACS reaches a verdict (and how to debug a surprising denial).