# Logon Sessions

---

# Logon sessions

_Peios / Peios Security Fundamentals / Logon Sessions_

> A logon session is the kernel object recording one authentication event — every token belongs to exactly one, tied together by the session's logon SID.

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.

| Field | What it is |
|---|---|
| `session_id` | A unique LUID identifying the session. Same as the `auth_id` on every token belonging to the session. |
| `logon_type` | What kind of sign-in produced this session — Interactive, Network, Service, Batch, etc. See [Logon types](/peios/security-fundamentals/logon-sessions/logon-types.md). |
| `user_sid` | The principal who signed in. The same SID appears in `user_sid` on every token of the session. |
| `auth_package` | A string identifying the authentication mechanism (e.g. "Kerberos", "NTLM", "local"). Informational; not used by AccessCheck. |
| `created_at` | Timestamp of the sign-in. |
| `logon_sid` | A 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.

```mermaid
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:

| Session | ID | Purpose |
|---|---|---|
| **SYSTEM session** | 0 | The session of the SYSTEM token, attached to init and inherited by every process until authd assigns real tokens. |
| **Anonymous session** | 998 | The 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](/peios/security-fundamentals/logon-sessions/logon-types.md).

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](/peios/security-fundamentals/logon-sessions/lifecycle.md).

If you want to see which sessions are currently active on a running system, read [Inspecting tokens, sessions, and processes](/peios/security-fundamentals/inspecting/overview.md).

To work with sessions from a shell — list them, see their processes, create and destroy them — read [The logonse command](/peios/security-fundamentals/logon-sessions/logonse-command.md).

---

# Logon types

_Peios / Peios Security Fundamentals / Logon Sessions_

> Every logon session is tagged with a logon type — Interactive, Network, Batch, Service, NetworkCleartext, or NewCredentials — and what each type means.

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](/peios/using-peios/constants-and-catalogs/other-constants.md).

| Value | Name | What it means |
|---|---|---|
| 2 | **Interactive** | The user signed in at the console, an SSH session, or some other interactive channel. The most common type for human users. |
| 3 | **Network** | The user authenticated to access network resources without an interactive sign-in. Typical for SMB, RPC, federated services. |
| 4 | **Batch** | A scheduled job. The principal is logged in to run a task at a specific time, not by a user actively present. |
| 5 | **Service** | A service started under a specific principal. Used for the long-lived service-account model. |
| 8 | **NetworkCleartext** | A 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. |
| 9 | **NewCredentials** | A 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](/peios/security-fundamentals/logon-sessions/lifecycle.md).

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](/peios/security-fundamentals/identity/well-known-principals.md).

To see the logon type of each active session from a shell, read [The logonse command](/peios/security-fundamentals/logon-sessions/logonse-command.md).

---

# Session lifecycle

_Peios / Peios Security Fundamentals / Logon Sessions_

> A logon session is created by authd at successful authentication and destroyed when its last token reference drops — there is no kernel revocation primitive.

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:

| Field | Meaning |
|---|---|
| `logon_type` | The type — Interactive, Network, Service, Batch, etc. See [Logon types](/peios/security-fundamentals/logon-sessions/logon-types.md). |
| `auth_package` | A string identifying the authentication mechanism (informational). |
| `user_sid` | The 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`:

| Session | ID | Created by |
|---|---|---|
| **SYSTEM session** | 0 | Direct kernel init |
| **Anonymous session** | 998 | Direct 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](/peios/security-fundamentals/tokens/elevation.md)).
3. Emits a `logon-session-destroyed` event through KMES.

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

| Field | What it is |
|---|---|
| `session_id` | The destroyed session's ID. |
| `user_sid` | The principal. |
| `logon_type` | The type. |
| `auth_package` | The auth package string. |
| `created_at` | The 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](/peios/security-fundamentals/inspecting/overview.md).

## 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](/peios/security-fundamentals/tokens/lifecycle.md).

To list, create, and destroy sessions from a shell, read [The logonse command](/peios/security-fundamentals/logon-sessions/logonse-command.md).

---

# The logonse command

_Peios / Peios Security Fundamentals / Logon Sessions_

> The logonse command lists logon sessions and their processes, creates and destroys them, and sets a process's mitigation flags.

`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](/peios/security-fundamentals/inspecting/sessions.md) — 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
```

| Flag | Meaning |
|---|---|
| `--logon-type TYPE` | The kind of logon: `interactive`, `network`, `batch`, `service`, `network-cleartext`, or `new-credentials`. |
| `--auth-package STR` | The name of the authentication package that vouched for the logon. |
| `--user-sid SID` | The 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](/peios/using-peios/wire-formats-reference/token-and-session-specs.md) 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](/peios/security-fundamentals/logon-sessions/lifecycle.md).

## 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
```

| Flag | Meaning |
|---|---|
| `--pid PID` | The process to act on. |
| `--mitigations MASK` | The 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](/peios/security-fundamentals/process-mitigations/overview.md).

## Output options

| Flag | Effect |
|---|---|
| `--json` | Emit JSON instead of human-readable output. Accepted by every subcommand. |

## Exit status

| Code | Meaning |
|---|---|
| `0` | The operation succeeded. |
| `1` | A usage error, or the operation failed. |
