Peios Learn
Products
PePeios pkpekit PvProvium UDUniversal Directory TrTrail PrProject WiWispist
Using Peios Security Basics Technical Documentation Source
Using Peios Security Basics Technical Documentation Source
Peios

Identity

Single-page view · as markdown

Identity in Peios

Peios / Peios Security Fundamentals / Identity

Every action on a running Peios system happens on behalf of a principal — a user, a service, a machine, or a well-known system actor. Identity is the unit of policy: every access decision starts with "who is asking?", and the kernel answers that question the same way for files, registry keys, processes, sockets, and tokens themselves.

The shape of an identity #

Identity in Peios has four layers, each with a clear job.

LayerRole
PrincipalThe thing being identified — a user account, a group, a service, a machine.
SID (Security Identifier)The unique name for a principal. SIDs are hierarchical strings like S-1-5-21-...-1001.
TokenThe runtime object that carries an identity into the kernel. Every thread has one.
Logon sessionThe authentication event a token belongs to. Sessions link tokens back to "who logged in, when, and how".

These four words appear throughout the Peios documentation, and they are not interchangeable. A principal is the long-lived thing in the directory; a SID is its name; a token is its instance for one run; a session is what produced that token.

How an identity gets into the kernel #

flowchart LR
    A["Principal"] -->|"identity asserted by"| B["Principal source"]
    B -->|"verified identity"| C["authd"]
    C -->|creates| D["Logon session"]
    C -->|mints| E["Token"]
    E -->|installed on| F["Process"]
    F -->|"presents at every syscall"| G["Access decision"]

When a user signs in, authentication happens in three steps:

  1. A principal source verifies the credential and asserts who the principal is — the local principal source for this machine's own accounts, or a directory-backed source on a domain-joined machine.
  2. authd, the authentication authority, creates a logon session: a kernel object that records the authentication event (who, how, when).
  3. authd mints a token carrying the user's SID, group SIDs, integrity level, privileges, and a reference to the session. The token becomes the user's runtime identity — and its privileges and integrity level come from this machine's policy for the principal, not from the source that identified them.

The client that requested the logon — login on a console, for example — installs the token on the session's first process, and every child process inherits it across fork. Threads that need to act on behalf of someone else (for example, a service handling a user request) can temporarily swap in an impersonation token while keeping their original — see Impersonation.

The token is the authoritative carrier. Any other identity values a process can observe — including the numeric IDs that surface through standard Linux system calls — are derived from the token, never the other way around. See Linux compatibility for how that projection works.

Identity vs authorisation #

An identity by itself grants nothing. A token says who a thread is; the Kernel Access Control Subsystem (KACS) decides what it can do, by comparing the token against each object's security descriptor. The same token will see different rights on different objects, and the same object will grant different rights to different tokens.

The rest of the security documentation is organised around this separation:

  • Identity says "who". This topic, plus Tokens and Logon sessions.
  • Authorisation says "what". Security descriptors and Access decisions.

If a request fails unexpectedly, you almost always need to look at both sides: which identity made the request, and which access rule rejected it. Pages in both halves cross-link where the two meet.

What identity does not include #

A few things often get bundled with identity that are kept deliberately separate in Peios:

  • Privileges are not identity. A token carries a set of privileges, but a privilege gates a specific operation (loading a driver, taking ownership of an object), independent of who you are. See Privileges.
  • Integrity level is not identity. It is a separate axis on the token that controls write-up restrictions. Two users at different integrity levels are still two distinct identities; the integrity level is an additional constraint on top.
  • Process integrity (PIP) is not identity at all. PIP is a property of the binary the process is running, set by the kernel from the binary's signature. It controls who can inspect or signal the process, independent of who the process is acting as. See Process integrity protection.

These distinctions matter because they fail in different ways. An "access denied" caused by the integrity level looks like an identity problem but is not.

Where to start #

If you want to understand how SIDs are constructed, read SIDs. For the catalog of built-in principals and their fixed SIDs, read Well-known principals.

If you want the typed attributes a token carries beyond identity — the inputs to attribute-based access decisions — read Claims on a token.

If you want the runtime mechanics — how a token is composed, how it survives fork and exec, and how it gets adjusted — read Tokens.

If you are debugging an unexpected denial, the Inspecting tokens, sessions, and processes topic shows how to see the identity attached to any running thread.

SIDs

Peios / Peios Security Fundamentals / Identity

A SID (Security Identifier) is the unique name for a principal. Every user, group, service, machine, and well-known system actor has exactly one SID, and that SID is how every other part of Peios refers to them: ACEs in a security descriptor name SIDs, tokens carry SIDs, audit events log SIDs.

Two SIDs are equal only if their binary encodings match byte-for-byte. There is no normalisation and no case folding. A SID is its bytes.

The string form #

The form you will see in logs, configuration, and almost every administrative tool is the string form:

S-1-5-21-3623811015-3361044348-30300820-1001

The pieces:

PositionMeaning
SConstant marker — every SID string starts with S.
1Revision number. Always 1 in Peios.
5Identifier authority. A small integer naming the SID's overall namespace.
21-3623811015-3361044348-30300820Sub-authorities. A sequence of integers that locate this principal within the authority's namespace.
1001Relative identifier (RID). The last sub-authority. Distinguishes individual principals within a domain.

A SID has between 1 and 15 sub-authorities. Most have between 1 and 7. The RID is just whatever sub-authority comes last; "RID" is a name for its role in domain SIDs, not a separate field.

When the authority is written in hex #

The identifier authority is a 48-bit value. When it fits in 32 bits — which it does for every authority Peios uses — it is written in decimal. If the upper 16 bits are nonzero, the authority is instead written as a 0x-prefixed 12-digit hexadecimal value:

S-1-0x000000123456-1-2-3

You will not see this form in practice on Peios. It exists for parity with external systems that allocate from the upper authority range.

The binary form #

On the wire (in security descriptors, in tokens, in audit events), a SID is a packed byte structure:

+----+----+----+----+----+----+----+----+
| 01 | NN | AA   AA   AA   AA   AA   AA |
+----+----+----+----+----+----+----+----+
| sub_1 (4 bytes, little-endian)         |
+----+----+----+----+----+----+----+----+
| sub_2 (4 bytes, little-endian)         |
+----+----+----+----+----+----+----+----+
|                  ...                   |
+----+----+----+----+----+----+----+----+
| sub_N (4 bytes, little-endian)         |
+----+----+----+----+----+----+----+----+

The first byte is the revision (0x01), the second the sub-authority count, then the six-byte authority, then the sub-authorities. The canonical byte-level layout is in the wire formats reference, under "SIDs in wire format".

The mixed endianness — big-endian for the authority, little-endian for the sub-authorities — is the one detail worth memorising. The rest of KACS is uniformly little-endian; the SID authority is the exception, for compatibility with the way SIDs travel across federation boundaries.

A SID is between 8 bytes (no sub-authorities, very rare) and 68 bytes (15 sub-authorities) long.

Comparison #

Two SIDs are equal if and only if their binary representations are identical byte-for-byte.

This rule has consequences worth knowing:

  • The string form is for humans. Two strings that look equivalent might not encode to the same bytes if one was reconstructed by hand and got a leading zero wrong, or if one used hex authority where decimal would do. Always compare in binary.
  • There is no case folding. SIDs do not contain letters in their numeric form, so this rarely matters, but tools that print SIDs alongside resolved names — for example BUILTIN\Administrators for S-1-5-32-544 — must compare the SID, not the name.
  • The kernel only compares for equality. When KACS matches a SID in an ACE against the SIDs in a token, it tests the full encoded bytes for equality — there is no "same domain" or "prefix match" at the access-check level. Userspace tooling can parse SIDs and compare them structurally for its own purposes (listing all users in a domain, filtering by authority, reporting), and is expected to. That parsing is on top of the byte representation; the underlying bytes are still what define identity.

If you are writing code that handles SIDs, store them as their binary form whenever possible and convert to strings only at the UI boundary.

SID patterns you will recognise #

The sub-authorities of a SID encode meaning by convention, not by parse rule. A few patterns appear so often that you will start to recognise them by shape:

PatternMeaning
S-1-5-32-...A built-in alias group under the NT Authority — local administrators, users, guests, etc.
S-1-5-21-W-X-Y-...A principal in a specific domain. The three numbers after 21 identify the domain; the last sub-authority is the RID.
S-1-5-5-X-YA logon SID. Created per authentication event; lets ACEs target "the specific login session that did this".
S-1-16-NAn integrity level. Used as a label in a SACL, not in a DACL.
S-1-19-T-LA process integrity protection (PIP) trust label. Used in SACLs on objects that opt in to PIP.
S-1-5-80-h1-h2-h3-h4-h5A service SID, derived from a service name. Lets ACEs target a specific service independent of the account it runs under.
S-1-15-3-h1-...-h8A capability SID, derived from a capability name. Used in confinement to grant a specific capability to a sandboxed application.

The full catalog of named SIDs is in Well-known principals. The derivation rules for service SIDs and capability SIDs — where the hash values actually come from — are documented under their respective topics.

What a SID does not tell you #

A SID identifies a principal but says nothing else about them. From a SID alone you cannot tell:

  • Which groups the principal is a member of (the token's group list does that).
  • What integrity level the principal runs at (the token's integrity_level field).
  • What privileges the principal has (the token's privilege bitmask).
  • Whether the principal is currently logged in (the logon session does that).

A SID is just the name. Everything else about a principal lives somewhere else — usually on the token that carries the SID at runtime.

Where to go next #

For the catalog of SIDs whose values are fixed by the system — Everyone, SYSTEM, the BUILTIN aliases, and the rest — read Well-known principals.

For the typed attributes that travel alongside SIDs on a token, read Claims on a token.

For how a SID is carried into every access decision at runtime, read Tokens.

Well-known principals

Peios / Peios Security Fundamentals / Identity

A well-known principal is a principal whose SID is fixed by the system rather than allocated when an account is created. Some name a single specific entity (SYSTEM, Anonymous). Some name a category (Everyone, Authenticated Users). Some are placeholders that get rewritten during inheritance (CREATOR OWNER). All of them have the same purpose: they let you write ACEs that target a role without naming a specific user.

You will recognise the common ones quickly; the full catalog exists so you can look up the rest when they appear in an audit log or an ACE.

How to read the catalog #

Each well-known SID has a fixed numeric value, a conventional name, and a defined behaviour during access checks. Some of them are not real principals at all — they are placeholders or labels that the access check treats specially. Where that distinction matters, it is called out.

The catalog is organised by SID authority, because that is how the SIDs are structured and how you will tend to recognise them.

Universal SIDs (S-1-0, S-1-1, S-1-2, S-1-3) #

These authorities are defined globally — not tied to any specific machine or domain — and exist for cross-system meaning.

SIDNameBehaviour
S-1-0-0NobodyMatches nothing. Used to write an ACE that can never apply, usually as a deliberate placeholder.
S-1-1-0EveryoneMatches every token, including Anonymous. The broadest possible grant.
S-1-2-0LocalMatches any token created by a local logon (not a network logon).
S-1-2-1Console LogonMatches a token created by a physical or console session.
S-1-3-0Creator OwnerPlaceholder. Inheritable ACEs containing this SID are rewritten to the new object's owner SID when the child is created. Does not match anything at access-check time.
S-1-3-1Creator GroupPlaceholder. Same as Creator Owner but for the primary group.
S-1-3-4Owner RightsSpecial. When present in a DACL, it suppresses the implicit READ_CONTROL and WRITE_DAC grants that the owner would otherwise receive. See Ownership and implicit rights.

The placeholder SIDs (S-1-3-0 and S-1-3-1) are the trickiest. They never grant or deny anything at access-check time. Their job is to make inheritable ACEs portable across owners — you write an ACE that grants WRITE to Creator Owner, and when a child object is created, that ACE is rewritten to grant WRITE to whoever owns the child.

NT Authority SIDs (S-1-5) #

This is the largest group of well-known SIDs and where most of the principals you will use day-to-day live.

Logon classifiers #

SIDNameBehaviour
S-1-5-2NetworkMatches a token created by a network logon.
S-1-5-4InteractiveMatches a token created by an interactive logon (console, RDP, SSH).
S-1-5-6ServiceMatches a token created for a service.
S-1-5-7AnonymousThe user SID for tokens at the Anonymous impersonation level.
S-1-5-11Authenticated UsersMatches every successfully authenticated token. Excludes Anonymous.

The pair S-1-1-0 (Everyone) and S-1-5-11 (Authenticated Users) is the most common distinction worth getting right. Everyone includes Anonymous; Authenticated Users excludes it. Anything reachable by Anonymous is reachable by an unauthenticated network peer, which is almost never what you want.

System actors #

SIDNameBehaviour
S-1-5-18Local System (SYSTEM)The kernel and TCB services run as SYSTEM. The most privileged identity on the local machine.
S-1-5-19Local ServiceBuilt-in service account with reduced privileges.
S-1-5-20Network ServiceBuilt-in service account that can authenticate outward to remote machines.

Built-in aliases #

The sub-authority 32 under NT Authority is the "BUILTIN" domain, which holds aliases for common administrative roles. The full alias list includes a few dozen entries; the ones you will see most often:

SIDName
S-1-5-32-544BUILTIN\Administrators
S-1-5-32-545BUILTIN\Users
S-1-5-32-546BUILTIN\Guests
S-1-5-32-551BUILTIN\Backup Operators

Logon sessions and domains #

PatternMeaning
S-1-5-5-X-YA specific logon session. X and Y are derived from the session's LUID. Always carried in the token of any thread belonging to that session.
S-1-5-21-DA1-DA2-DA3-RIDA principal in a specific domain. DA1-DA2-DA3 identifies the domain; RID identifies the principal within it.
S-1-5-21-DA1-DA2-DA3-500The Administrator account of that domain (RID 500 is reserved).
S-1-5-21-DA1-DA2-DA3-512Domain Admins.
S-1-5-21-DA1-DA2-DA3-513Domain Users.
S-1-5-21-DA1-DA2-DA3-515Domain Computers — the machine accounts in the domain.

The full set of reserved RIDs under S-1-5-21-* follows administrative convention. Domain-specific principals (your actual users) get RIDs allocated above 1000.

Service SIDs #

A SID derived from a service's name, used to grant a service ACL entries independent of the account it runs under.

PatternMeaning
S-1-5-80-h1-h2-h3-h4-h5A service SID. The five hash values come from the SHA-1 of the UTF-16LE uppercased service name, split into five little-endian 32-bit integers.

Service SIDs let you say "grant the loregd service read access to this file" without saying "grant SYSTEM read access" or "grant a specific user read access" — both of which would be too broad.

Mandatory integrity labels (S-1-16) #

These are not principals. They are labels carried in a token's integrity level field and in an object's mandatory label ACE.

SIDLevel
S-1-16-0Untrusted
S-1-16-4096Low
S-1-16-8192Medium
S-1-16-12288High
S-1-16-16384System

These five are the standard, well-known levels. The level is really the SID's single sub-authority as an unsigned integer, compared numerically, so any S-1-16-<n> with exactly one sub-authority is valid — non-standard values such as medium-plus (S-1-16-8448) and protected (S-1-16-20480) appear for Windows interop.

Integrity levels are a vertical axis on top of identity. A user at Medium integrity and the same user at High integrity have the same SID but different integrity labels. See Mandatory integrity control for how the levels interact with access checks.

Process trust labels (S-1-19) #

Like integrity labels, these are not principals. They label processes by signing trust for Process integrity protection.

PatternMeaning
S-1-19-T-LPIP trust label. T is the PIP type (0 None, 512 Protected, 1024 Isolated). L is the trust level within that type.

Common combinations:

SIDTrust meaning
S-1-19-0-0No PIP protection. Default for unsigned processes.
S-1-19-512-1024Protected, Authenticode. Third-party signed binaries.
S-1-19-512-2048Protected, App. Peios-distributed applications.
S-1-19-512-4096Protected, Peios. Core Peios components.
S-1-19-512-8192Protected, PeiosTcb. Trusted computing base.

Confinement and capability SIDs (S-1-15) #

These are used by Confinement to label sandboxed applications and the capabilities they have been granted.

Confinement domains #

SIDName
S-1-15-2-1ALL_APPLICATION_PACKAGES — matches every confined application in normal confinement mode.
S-1-15-2-2ALL_RESTRICTED_APPLICATION_PACKAGES — matches confined applications in both normal and strict modes.

Well-known capabilities #

SIDCapability
S-1-15-3-1internetClient — outbound network.
S-1-15-3-2internetClientServer — inbound and outbound network.
S-1-15-3-3privateNetworkClientServer — LAN/private network.
S-1-15-3-8enterpriseAuthentication — domain credential access.
S-1-15-3-9sharedUserCertificates — certificate store access.
S-1-15-3-10removableStorage — removable media access.

Derived capability SIDs #

For application-defined capabilities beyond the well-known set, the SID is derived from the capability name:

PatternMeaning
S-1-15-3-h1-h2-h3-h4-h5-h6-h7-h8A derived capability SID. The eight values come from the SHA-256 of the capability name. The same name always produces the same SID.

Special placeholder SIDs #

A few SIDs never appear as a real principal but are recognised by the access check.

SIDNameHow it is used
S-1-3-0Creator OwnerSubstituted at inheritance time.
S-1-3-1Creator GroupSubstituted at inheritance time.
S-1-3-4Owner RightsSuppresses owner implicit rights when present in a DACL.
S-1-5-10Principal SelfA placeholder for "the principal this object is about". The access check substitutes the caller-supplied self_sid parameter for this SID at evaluation time. Used in directory-style objects where an ACE says "the user can modify their own properties".

These are the cases where a SID in an ACE does not literally mean "match a token whose user SID is this value". They are special-cased by the access check.

Which ones to use in practice #

The catalog above is comprehensive. In practice, day-to-day administrative work touches a much smaller set:

  • BUILTIN\Administrators and SYSTEM for default protective DACLs.
  • Authenticated Users for "any logged-in user should be able to read this".
  • Everyone only when you actually mean "including unauthenticated network peers".
  • Creator Owner in inheritable ACEs that should follow the owner of a child object.
  • Owner Rights when you want to suppress the owner's default grants on a sensitive object.
  • Specific user/group SIDs allocated by the directory (the S-1-5-21-...-RID form) for everyone else.

The remaining well-known SIDs — service SIDs, capability SIDs, integrity labels, PIP labels — show up in specialised contexts that have their own topics in these docs.

Where to go next #

For the attributes a token carries beyond fixed identity — the other input to attribute-based access rules — read Claims on a token.

For the exact numeric values in machine-readable form, see the Well-known SIDs reference.

Claims on a token

Peios / Peios Security Fundamentals / Identity

A claim is a typed key-value attribute that carries information about a principal beyond its identity. The user's department, the machine's compliance status, the time-of-day window for which a session is valid — all of these are claims. They are the raw material that conditional ACEs use to make access decisions about more than just "who is this".

Tokens carry two sets of claims:

  • User claims — attributes about the user. Populated at authentication time by authd from the user's directory object.
  • Device claims — attributes about the machine. Populated similarly from the machine's directory object.

Both sets are read-only after the token is minted. Adjusting a token's privileges or groups does not touch its claims; only re-authentication does.

What a claim looks like #

A single claim has three things:

  • A name — a string, such as Department or Clearance. In conditional expressions the name is qualified by the set it belongs to: @User.Department.
  • A value — typed (see below). May be a single value or an array of values.
  • A set of flags that control how the claim participates in access checks.

Value types #

A claim's value is always one of:

TypeNotes
INT64A signed 64-bit integer.
UINT64An unsigned 64-bit integer.
STRINGA UTF-16LE string.
SIDA binary SID.
BOOLEANTRUE or FALSE.
OCTETA raw byte string.

Mixed-type arrays are not allowed. A claim either holds one STRING, three STRINGs, or one INT64 — never two strings and an integer.

Claim flags #

Each claim carries flags that change how it behaves during access evaluation.

FlagEffect
DISABLEDThe claim is invisible to all conditional expressions. Effectively, the attribute does not exist.
USE_FOR_DENY_ONLYThe claim is invisible to conditions on allow ACEs, but visible to conditions on deny ACEs. This is the conservative downgrade — the claim cannot grant new access but can still trigger denials.
MANDATORYThe claim cannot be removed or modified without the SeTcbPrivilege. Used for claims that the system itself depends on.
CASE_SENSITIVEString and octet comparisons against this claim are case-sensitive. Default is case-insensitive.

The first two flags (DISABLED, USE_FOR_DENY_ONLY) are the security-relevant ones. They give administrators a way to neutralise a claim (for example, for a logon at a lower trust level) without rewriting the user's directory object.

How claims get onto a token #

flowchart LR
    A["User object"] -->|directory| B["authd"]
    C["Machine object"] -->|directory| B
    B -->|at authentication| D["Token"]
    D -->|user_claims| E["@User namespace"]
    D -->|device_claims| F["@Device namespace"]

When a user authenticates, authd reads:

  • The user's directory object — name, group memberships, any attributes the directory schema defines. Anything the schema marks as a claim attribute is copied into the token as a user claim.
  • The machine's directory object — same, but for the device. Anything the schema marks as a device claim is copied into the token as a device claim.

Both sets travel with the token for its entire lifetime.

The directory schema decides what is and is not a claim. A claim that is defined in the schema but absent from a specific user object becomes a missing claim on that user's token — not an empty one, not a default value. Conditional expressions distinguish between the two.

Where claims get used #

Claims do nothing by themselves. They are inputs to conditional ACEs — ACEs whose grant or deny is gated by an expression that references attributes.

A conditional expression refers to a claim by namespace and name:

  • @User.Department — a claim on the caller's token, user-claims set.
  • @Device.Compliance — a claim on the caller's token, device-claims set.
  • @Resource.Classification — an attribute on the object being accessed.
  • @Local.Time — a per-request attribute supplied by the caller of AccessCheck.

The first two are the token claim sets described above. The last two share the expression syntax but hold different kinds of data:

  • @Resource values come from resource attributes in the object's SACL, not the caller's token. See Resource attributes.
  • @Local values are supplied by the caller as a per-access-check parameter. They never live on a token. They exist for runtime context that does not fit into a directory.

The full grammar of conditional expressions — comparison operators, the three-valued logic (TRUE / FALSE / UNKNOWN), the way missing claims affect the outcome — lives in Conditional ACEs.

Missing claims fail closed #

One behaviour is worth knowing before you get to the conditional ACEs page: a missing claim is not a failure. A conditional expression that references a claim the token does not carry evaluates to UNKNOWN. UNKNOWN on an allow ACE means "skip this ACE"; UNKNOWN on a deny ACE means "treat this as deny". So a missing claim never accidentally grants access — it can only fail closed.

This is why USE_FOR_DENY_ONLY exists. It is the deliberate version of the same idea: take a claim that grants access and demote it to a claim that can only revoke access.

What claims are not #

A few things claims look like at a glance but are not:

  • Claims are not groups. A group is a SID in the token's group list; group membership is presence-or-absence. A claim is a typed attribute with a value. The two appear in different parts of the token and are matched by different parts of an ACE.
  • Claims are not privileges. Privileges are a fixed system bitmask gating specific operations. Claims are administrator-defined attributes with arbitrary names.
  • Claims are not the audit log. Claims describe the principal; the audit log describes what the principal did. The audit-event subject record does include the principal's claims (so audit consumers can write rules over them), but a claim is not itself an event.

Where to go next #

For how the token that carries these claims is built, adjusted, and destroyed, read Tokens.

For the expression grammar that consumes claims — operators, three-valued logic, worked examples — read Conditional ACEs.

Peios Learn — documentation for the Peios project.

Built with Trail.