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

Logon Sessions

Single-page view · as markdown

Logon sessions

Peios / Peios Security Fundamentals / Logon Sessions

A logon session is the kernel object that records one authentication event. It exists from the moment a principal successfully signs in to the moment every token derived from that sign-in is released. Every token references exactly one session through its auth_id field, and the session ties together everything that came out of that sign-in: the primary tokens, the impersonation tokens derived from them, the linked Full/Limited pair when one is established.

Sessions exist to answer two questions cleanly:

  • "Did all of these tokens come from the same sign-in?"
  • "When this principal logs out, what should we tear down?"

The first matters for audit and for revocation. The second matters because logging out has to do more than just close a shell — it has to release Kerberos tickets, drop linked-pair associations, fire the right events. Sessions are the unit at which those teardowns happen.

What a session contains #

A logon session is a small kernel object — fewer fields than a token, no privileges or groups of its own. Its job is to identify the event, not the principal.

FieldWhat it is
session_idA unique LUID identifying the session. Same as the auth_id on every token belonging to the session.
logon_typeWhat kind of sign-in produced this session — Interactive, Network, Service, Batch, etc. See Logon types.
user_sidThe principal who signed in. The same SID appears in user_sid on every token of the session.
auth_packageA string identifying the authentication mechanism (e.g. "Kerberos", "NTLM", "local"). Informational; not used by AccessCheck.
created_atTimestamp of the sign-in.
logon_sidA per-session SID derived from the session_id. See below.

The session is the unit of identity provenance. If you want to answer "where did this token come from?", the path is: token → auth_id → session → logon_type, auth_package, created_at, user_sid. Everything an audit log needs to reconstruct a sign-in is reachable from the session.

The logon SID #

Every session has its own SID, called the logon SID:

S-1-5-5-X-Y

Where X and Y are the high and low 32 bits of the session's LUID. The logon SID is unique to the session — two different logins by the same user produce two different logon SIDs, because they have different session IDs.

Every token of the session carries its logon SID in two places: the dedicated logon_sid field, and as an entry in the groups list with the SE_GROUP_LOGON_ID flag set. Both views point at the same value. The flag exists so the access check can identify the logon SID without needing to know its specific value.

The logon SID is what lets you write an ACE that targets "the specific sign-in that did this" rather than "this user". Common uses:

  • Per-session temporary objects. Grant access to the logon SID only. When the session ends, the SID becomes unreachable (no future token will ever carry it), so the object is effectively cleaned up at logout from an access-control perspective.
  • Service-to-user isolation in shared sessions. Two services running for the same user but in different sessions can use the logon SID to keep their per-session state apart even though the user SIDs are identical.
  • Audit anchoring. An audit event records the logon SID alongside the user SID. The two together let an investigator distinguish "this user did this" from "this specific sign-in did this".

The logon SID cannot be disabled (SE_GROUP_LOGON_ID is a mandatory-equivalent flag), and it cannot be removed from the token. It is intrinsic to the token.

Sessions and tokens #

A session can have many tokens. Every primary token of a process belonging to the session, every impersonation token derived from one of those primaries, every duplicated or filtered copy that shares the same auth_id — all of them are tokens of that session.

flowchart LR
    A["Logon session, auth_id=42"] --> B["Primary token (user's shell)"]
    A --> C["Primary token (child process)"]
    A --> D["Impersonation token (service handling this user)"]
    A --> E["Filtered token (restricted sandbox of B)"]
    A --> F["Full token (linked pair)"]
    A --> G["Limited token (linked pair)"]

The kernel keeps a reference count of how many tokens belong to a session. When the count drops to zero — every token of the session has been released — the session itself is destroyed and a logon-session-destroyed event is emitted.

The implication is that destroying a session is not a primitive operation. There is no kacs_destroy_session syscall. You destroy a session by ensuring every token belonging to it is released, which usually means killing every process running on those tokens. Session revocation — what authd does when an administrator forces a logout — is implemented this way in user space.

Bootstrap sessions #

Two sessions exist before authd is up, created by the kernel during boot:

SessionIDPurpose
SYSTEM session0The session of the SYSTEM token, attached to init and inherited by every process until authd assigns real tokens.
Anonymous session998The session of the Anonymous token, used as the user SID for Anonymous-level impersonation.

Both are created by direct kernel initialisation — they do not go through kacs_create_session. They are also never destroyed during a running system's lifetime: the SYSTEM token always exists somewhere, and the Anonymous token is a singleton.

You will see these IDs in audit logs and /sys/kernel/security/kacs/sessions listings. They are not bugs.

Sessions are not durable #

A session exists in the kernel only while it has active tokens. There is no on-disk session table. The session ID space is not stable across boots — after a reboot, the same user signing in again gets a different session ID, and a new logon SID.

This matters in two practical ways:

  • You cannot reference a session across a reboot. An ACL entry that targets the logon SID of "the previous boot's session 5" is meaningless after the next boot. The SID format will look syntactically valid, but no token will ever match it.
  • Per-session state should be in memory, not on disk. Anything intended to survive a single sign-in should be keyed on the user SID, not the logon SID.

Where to start #

If you want the catalog of logon types — what Interactive, Network, Service, Batch, NewCredentials, and the rest each mean — read Logon types.

If you want the creation, destruction, and revocation mechanics — including the logon-session-destroyed event and what authd does for forced logout — read Session lifecycle.

If you want to see which sessions are currently active on a running system, read Inspecting tokens, sessions, and processes.

To work with sessions from a shell — list them, see their processes, create and destroy them — read The logonse command.

Logon types

Peios / Peios Security Fundamentals / Logon Sessions

Every logon session carries a logon_type — a single number, set by authd at session creation, that classifies the nature of the sign-in. The type is informational from KACS's point of view: AccessCheck does not branch on it. But it is recorded in every audit event that references the session, and it is what audit consumers and SIEM tools use to distinguish "the user logged in at the console" from "the user logged in over the network" from "a service started under this account".

There are six types in v0.20.

The six types #

The numeric values are also catalogued in Other constants.

ValueNameWhat it means
2InteractiveThe user signed in at the console, an SSH session, or some other interactive channel. The most common type for human users.
3NetworkThe user authenticated to access network resources without an interactive sign-in. Typical for SMB, RPC, federated services.
4BatchA scheduled job. The principal is logged in to run a task at a specific time, not by a user actively present.
5ServiceA service started under a specific principal. Used for the long-lived service-account model.
8NetworkCleartextA network logon where the credential was transmitted in cleartext over the wire. The session is otherwise a Network session; the type distinction exists for audit.
9NewCredentialsA session created to use different credentials when reaching out to remote resources, while keeping the local identity unchanged. The local thread continues to act as its primary token; outbound network requests carry the alternative credentials.

Values 1, 6, 7, and 10 onward are reserved and not used in v0.20.

When each type is used #

Interactive #

The default for any sign-in where a human is at the keyboard. Console logins, SSH connections, terminal services, the lock-screen unlock — all Interactive.

You will see Interactive in audit logs whenever a real user begins a session. If your audit policy distinguishes "human did something" from "automation did something", the Interactive type is the marker for the human side.

Network #

Used when a principal authenticates only to access a network resource — for example, a remote user mounting a file share. The session exists on the server, the user's identity is established there, but the user has no interactive presence on the machine. The session's tokens are typically short-lived (one connection's worth of work).

A Network session is what an inbound SMB connection produces, what an authenticated RPC call produces, what a federated service request produces. It is also what a host typically sees when a user reaches a service on it that is acting on the user's behalf.

Batch #

Scheduled tasks. The principal is "logged in" only in the sense that the system mints a token for the user so the task can run as them. There is no human presence. Once the task finishes, the session ends.

Batch sessions are useful for auditing scheduled work — distinguishing "the user typed this command" from "the user's scheduled task did this command".

Service #

The session under which a service runs. The service account signs in once at service start; the session lives for as long as the service runs. Most services on a Peios machine are Service-type sessions.

This is the type you will see most often in the session listing at /sys/kernel/security/kacs/sessions on a system without active interactive users — every running service contributes one Service-type session.

NetworkCleartext #

Used when the authentication protocol exposed the credential in cleartext on the wire. The semantic effect is identical to Network — it is a network sign-in producing a Network-style session — but the type difference exists so audit policy can flag the cleartext exposure.

You should rarely see this in normal operation. Its presence in an audit log is worth attention; it usually indicates that an older or misconfigured protocol is in use.

NewCredentials #

The unusual one. A NewCredentials session does not replace the calling thread's identity. Instead, it represents "I want to keep being myself locally but use these other credentials for outbound network calls". The thread's effective token retains the local user's SIDs and privileges for everything KACS evaluates locally, but any outbound credential-using request carries the alternative principal.

The pattern matters in environments where a user has local rights on one machine and different rights on another, and wants to keep both available without switching sessions.

Where the type appears #

The logon type is stamped in three places you will care about:

  • In the session itself, retrievable through /sys/kernel/security/kacs/sessions and the session inspection APIs.
  • In every token's session — via the token's auth_id field, which points to the session, which holds the type.
  • In audit events — every audit event that includes a subject also implicitly identifies the subject's session, and audit consumers can use that to retrieve the logon type for the event.

The type is not part of any token's SID list. There is no "Interactive" SID that gets added to a token's groups based on the logon type. The well-known SIDs like S-1-5-4 (Interactive) and S-1-5-2 (Network) are added to a token's groups by authd based on the logon type, but they are independent token attributes from that point on. A token with the Interactive group SID has it because authd put it there; the session's logon_type is the input that drove that decision.

This independence is intentional. A token can in principle carry the Interactive group without belonging to an Interactive-type session (rare, but legal). Audit and ACL evaluation use the group SID; provenance uses the session's type.

What the type does not affect #

AccessCheck does not read the logon type. The DACL walk, the MIC check, the PIP check, the restricted/confinement passes — none of them branch on logon_type.

If you want different access for "Interactive users" vs "Network users", you write ACEs that reference the well-known group SIDs (S-1-5-4, S-1-5-2, etc.). Those SIDs end up in the token because of the logon type, but the access check matches on the SID, not the type.

The type matters at the seams: when authd is making decisions about which groups to add to a token, when the audit subsystem is recording provenance, when a SIEM correlates events across sessions. Inside the access check itself, the type is invisible.

Where to go next #

For how a session is created, destroyed, and forcibly revoked, read Session lifecycle.

For the well-known group SIDs (S-1-5-4 Interactive, S-1-5-2 Network, and the rest) that authd derives from the logon type, read Well-known principals.

To see the logon type of each active session from a shell, read The logonse command.

Session lifecycle

Peios / Peios Security Fundamentals / Logon Sessions

A session's life is bracketed by two kernel events: a successful kacs_create_session call that brings it into existence, and the implicit drop of its last token reference that destroys it. There is nothing in between — no resize, no rename, no kernel-side timeout. Sessions are simple objects whose lifecycle is driven entirely by the tokens attached to them.

This page covers the three phases that matter: creation, destruction, and revocation (the userspace pattern for forcing a session to end before the user logs out voluntarily).

Creation #

A session is created by kacs_create_session. The call requires SeTcbPrivilege, so in practice the only callers are authd (every interactive and network sign-in) and peinit (services launched during boot before authd is available).

The call takes a wire-format specification with three fields:

FieldMeaning
logon_typeThe type — Interactive, Network, Service, Batch, etc. See Logon types.
auth_packageA string identifying the authentication mechanism (informational).
user_sidThe principal who signed in.

The kernel:

  1. Validates the inputs (well-formed SID, recognised logon type, sane string lengths).
  2. Allocates a session object with a fresh session_id LUID.
  3. Stamps created_at.
  4. Derives the logon SID S-1-5-5-X-Y from the session ID.
  5. Initialises the session's token reference count at zero.
  6. Returns the new session ID to the caller.

The session now exists but has no tokens. It is in a transient state: any subsequent kacs_create_token call that references this session_id in its auth_id field bumps the count, and the session is "live". If no token is ever created against the session — vanishingly rare — the session stays at refcount zero and is reaped after a brief grace period.

The grace period is what avoids a race: authd's flow is "create session, then mint primary token referencing it". Between those two calls the session has no tokens, and a strict "destroy when refcount hits zero" rule would tear it down before authd's second call. The kernel solves this by only triggering destruction on a refcount that transitions from positive to zero, not on a refcount that has been zero since creation. After a successful first attachment, normal destruction rules apply.

The boot sessions #

Two sessions exist without ever passing through kacs_create_session:

SessionIDCreated by
SYSTEM session0Direct kernel init
Anonymous session998Direct kernel init

Both are constructed in early boot, before any process exists. The SYSTEM session is attached to the kernel's bootstrap SYSTEM token, which init inherits and which propagates through every process until authd assigns real tokens. The Anonymous session backs the well-known Anonymous token used by Anonymous-level impersonation.

Neither is ever destroyed during the running system's lifetime. They are reference-counted like any other session, but their references never drop to zero — the SYSTEM token always has at least one attachment somewhere, and the Anonymous token is a kernel-internal singleton.

Destruction #

A session is destroyed when its token reference count, having been positive, drops to zero. The kernel:

  1. Removes the session from the session table.
  2. Tears down any linked-pair association it was holding (see Elevation and linked tokens).
  3. Emits a logon-session-destroyed event through KMES.

The event carries enough information for consumers to clean up downstream state:

FieldWhat it is
session_idThe destroyed session's ID.
user_sidThe principal.
logon_typeThe type.
auth_packageThe auth package string.
created_atThe session's creation timestamp.

The most important consumer of this event is authd itself. authd subscribes to logon-session-destroyed because it needs to release session-scoped state of its own: Kerberos tickets, cached directory data, any per-session credentials it has been holding. Without the subscription, authd would have no way to know that a session it created is gone.

Other consumers (audit pipelines, accounting tools, session-aware services) may also subscribe. The event is fire-and-forget — there is no acknowledgement, no retry, no replay. A consumer that misses an event misses it.

What ends a session #

A session ends only when every reference to it drops. The references are:

  • Every primary token attached to a process with auth_id = session_id.
  • Every impersonation token currently installed on any thread.
  • Every token fd open on a process's behalf.
  • The linked-pair association, while it exists.

Practically, this means a session ends when:

  1. Every process running on a token of the session has exited.
  2. Every thread that was impersonating a token of the session has reverted or exited.
  3. Every fd held on a token of the session has been closed.
  4. The linked pair, if any, has been dissolved.

The fourth condition is satisfied automatically when the session reaches refcount zero — the kernel dissolves the pair as part of destruction. The first three are user-space's responsibility.

For an ordinary logout, this happens naturally: the user's shell exits, child processes exit, the authority broker process closes its hold on the Full token. Once all of those happen, the kernel sees refcount zero, fires the event, frees the session.

Revocation: there is no kernel call #

There is no syscall to forcibly end a session. No kacs_destroy_session, no kacs_kill_session. The session model is reference-counted, and the only way to end one is to ensure every reference drops. (The one destroy syscall that exists, kacs_destroy_empty_session, is a rollback primitive for empty sessions only — it refuses with -EBUSY any session that still has live tokens. This is what logonse destroy wraps.)

Forced logout — an administrator deciding that user X should not be signed in any more — is therefore a userspace operation. The pattern, implemented by authd:

  1. Walk /proc/*/token to find tokens whose auth_id matches the target session.
  2. For each matching token, identify the process holding it.
  3. Send the appropriate signal to terminate the process (typically SIGTERM with a grace period, then SIGKILL if needed).
  4. Continue until no processes hold any token of the session.

Once the last process exits, the session reaches refcount zero, the event fires, and the user is logged out.

The kernel cooperates by exposing auth_id through token query interfaces (specifically the TokenStatistics query class via KACS_IOC_QUERY) and by providing the per-PID token handles at /proc/<pid>/token. authd uses both to do the walk.

There are two reasons the kernel does not provide a direct revoke:

  • No graceful path. A "kill the session" syscall would need to choose between killing every process holding any of its tokens (loss of work, possible data corruption) and merely refusing future operations, which leaves running processes with stale identity. User-space can stage the teardown — send SIGTERM, wait, escalate — in a way the kernel cannot.
  • Reference-counted identity is the simpler model. The rule "a session exists if and only if a token exists referencing it" has one fewer transition than "a session exists if and only if its tokens exist AND no revocation has been requested". Fewer transitions, fewer corner cases.

The cost is that revocation is observable: a thread can detect that its session is about to die (its parent process getting a TERM) before the kernel sees the session as gone. Anything that wants to enforce immediate revocation needs to design around that — typically by minimising the work a thread can do between receiving a signal and actually exiting.

Inspecting active sessions #

The kernel exposes the active session list at /sys/kernel/security/kacs/sessions. The file is a text listing, one session per line:

session_id=42 user_sid=<hex-encoded SID> logon_type=2 auth_package=<hex-encoded name> created_at=...

Lines are stable in their leading fields; new fields may be appended in a future version, so consumers must ignore unknown trailing fields. The file's own SD grants read to BUILTIN\Administrators and SYSTEM only.

This is the canonical way to enumerate sessions. Other tools — eventd consumers, who-equivalents, session monitors — can read it directly or through helpers that wrap it. See Inspecting tokens, sessions, and processes.

Where to go next #

For the token-side half of the same story — the references whose rise and fall drive a session's life — read Token lifecycle.

To list, create, and destroy sessions from a shell, read The logonse command.

The logonse command

Peios / Peios Security Fundamentals / Logon Sessions

logonse is the command-line tool for logon sessions — the kernel's records of authentication events that this topic describes. It lists the active sessions, shows which processes belong to one, creates and destroys sessions, and (as a related low-level job) sets a process's mitigation flags.

logonse subcommand [arguments]
$ logonse list
$ logonse show 4711

logonse is a low-level administrative and debugging tool. It requires a subcommand: list, show, create, destroy, or psb.

Listing sessions #

logonse list #

Enumerates the active logon sessions, and the process IDs in each.

$ logonse list
session 0     pids: [1, 2, 14, 22]
session 4711  pids: [820, 844, 901]

logonse show #

Shows the process IDs that belong to one session.

$ logonse show 4711

A caveat on list and show #

There is no syscall that enumerates logon sessions, and logonse does not read the kernel's sessions file (whose SD restricts it to Administrators and SYSTEM). logonse list and logonse show work by walking the running processes and reading each one's token to find which session it belongs to. That has two consequences worth knowing:

  • It is best-effort. A session that has no running process — held alive only by a token file descriptor somewhere — will not appear, because there is no process to find it through.
  • It is a snapshot under change. Processes start and exit while the walk runs, so the result is a close approximation of the moment, not a locked one.

For routine "who is signed in" use this is fine. For an authoritative listing, the kernel's own sessions surface — described in Inspecting sessions — is the source of record.

Creating and destroying sessions #

logonse create #

Creates a new logon session for a user, described by a logon type, an authentication-package name, and the user's SID.

$ logonse create --logon-type interactive --auth-package Negotiate --user-sid S-1-5-21-...-1001
FlagMeaning
--logon-type TYPEThe kind of logon: interactive, network, batch, service, network-cleartext, or new-credentials.
--auth-package STRThe name of the authentication package that vouched for the logon.
--user-sid SIDThe user the session belongs to, as an S-1-… SID or an SDDL alias such as BA.

On success logonse prints the new session's id. Creating a session is a privileged operation — minting authentication records is reserved for the components that legitimately do so.

Note

The tool builds the kernel's binary session spec from these fields for you. The underlying wire format is unchanged; only the command-line surface is typed.

logonse destroy #

Destroys a session — but only an empty one, with no tokens still referencing it.

$ logonse destroy 4711

A session with live tokens cannot be destroyed this way; its tokens must go first. See Session lifecycle.

Setting process mitigation flags #

logonse psb #

logonse psb sets the mitigation flags in a process's Process Security Block.

$ logonse psb --pid 4821 --mitigations 0x1c0
FlagMeaning
--pid PIDThe process to act on.
--mitigations MASKThe mitigation bitmask to apply, in hexadecimal or decimal.

This subcommand is about process hardening rather than logon sessions — it lives in logonse because both deal with low-level per-process kernel state. For what the mitigation flags mean and how they behave, see Process mitigations.

Output options #

FlagEffect
--jsonEmit JSON instead of human-readable output. Accepted by every subcommand.

Exit status #

CodeMeaning
0The operation succeeded.
1A usage error, or the operation failed.

Peios Learn — documentation for the Peios project.

Built with Trail.