On this page
Who can manage a service
A service is a securable object. Just as a file or a registry key carries a security descriptor that decides who may touch it, a service carries one that decides who may start, stop, query, or reload it. peinit enforces it on every control command — there is no command that skips the check.
This is the second, independent half of the security model. The first half, Service identity and privileges, is about what a service can do (its token). This page is about who can manage the service — a completely separate question, answered by a completely separate descriptor.
Two descriptors, two questions
A service is associated with two descriptors that are easy to conflate but answer different questions and are enforced by different components:
| Descriptor | Question | Stored | Enforced by |
|---|---|---|---|
| Registry key SD | Who can read or edit the definition? | On the Machine\System\Services\<name> key |
LCS, at key-open time |
| ServiceSecurity SD | Who can manage the running service? | As the ServiceSecurity binary value on that key |
peinit, on every control command |
The registry key SD is ordinary registry access control and not peinit's concern — peinit reads definitions as SYSTEM, which has full access. The ServiceSecurity SD is peinit's domain, and the rest of this page is about it.
They are genuinely independent. An administrator might be able to query a service's status (ServiceSecurity grants SERVICE_QUERY_STATUS) but not read its configuration (the registry key SD denies read) — or the reverse. Runtime control and configuration access are separate concerns, and both combinations are valid.
Service access rights
The ServiceSecurity descriptor grants these rights:
| Right | Bit | Grants |
|---|---|---|
SERVICE_QUERY_STATUS |
0x0001 | Query state, PID, cause, health, warnings. |
SERVICE_START |
0x0002 | Start the service. |
SERVICE_STOP |
0x0004 | Stop the service. |
SERVICE_INTERROGATE |
0x0008 | Reload the service. |
SERVICE_ALL_ACCESS |
0x000F | All of the above — the "full access" granted to SYSTEM by default. |
restart requires both SERVICE_STOP and SERVICE_START, since it is a stop followed by a start. reset requires SERVICE_STOP.
When peinit evaluates the descriptor it maps the generic rights as follows, so a descriptor written with generic rights behaves sensibly:
| Generic | Maps to |
|---|---|
GENERIC_READ |
SERVICE_QUERY_STATUS |
GENERIC_WRITE |
SERVICE_START | SERVICE_STOP | SERVICE_INTERROGATE |
GENERIC_EXECUTE |
SERVICE_START | SERVICE_STOP | SERVICE_INTERROGATE |
GENERIC_ALL |
SERVICE_ALL_ACCESS |
How a command is authorised
Every control command runs the same gate:
- peinit captures the caller's token from the kernel (
kacs_open_peer_token) — the caller's effective identity at connection time, so if the caller is impersonating, the impersonated identity is what is checked. - peinit resolves the target service and its ServiceSecurity descriptor.
- peinit runs AccessCheck: the caller's token against the descriptor, for the right the command needs.
- Denied → return
ACCESS_DENIEDand log the attempt (caller SID, target service, requested right). - Granted → execute the command.
The default descriptor
If a service has no ServiceSecurity value, it inherits its parent key's. If no ancestor sets one either, peinit applies a built-in default:
- SYSTEM (
S-1-5-18) — full access. - Administrators (
S-1-5-32-544) — query and stop only.
So out of the box, administrators can see and stop a service but not start or reload it unless a descriptor grants more — a conservative default that you widen deliberately.
ServiceSecurity is hot-reloaded: a change to the value in the registry takes effect on the next control request, with no service restart. peinit picks the change up through a registry notification. This is why ServiceSecurity is in its own mutability class — access policy should be able to change without disturbing a running service.
The system control descriptor
Some operations are not about any one service — shutdown and reload-config act on the whole system. These are checked against peinit's own descriptor, stored at Machine\System\Init\ControlSecurity:
| Right | Bit | Grants |
|---|---|---|
SYSTEM_SHUTDOWN |
0x0001 | Initiate poweroff, reboot, or halt. |
SYSTEM_RELOAD_CONFIG |
0x0002 | Re-read all definitions and rebuild the graph. |
Its generic mapping deliberately gives GENERIC_READ nothing — there is no "read" of the system control object, only the two actions:
| Generic | Maps to |
|---|---|
GENERIC_READ |
(nothing) |
GENERIC_WRITE |
SYSTEM_RELOAD_CONFIG |
GENERIC_EXECUTE |
SYSTEM_SHUTDOWN |
GENERIC_ALL |
SYSTEM_SHUTDOWN | SYSTEM_RELOAD_CONFIG |
The default grants SYSTEM full access and Administrators both rights. peinit loads this descriptor at boot and hot-reloads it on registry change, exactly like ServiceSecurity.
The list command filters, it does not deny
list is access-control-aware in a quieter way: it returns only the services the caller has SERVICE_QUERY_STATUS on, and simply omits the rest. A caller with no query rights gets an empty list, not a denial. This means a low-privilege principal cannot even enumerate the services it cannot see — the existence of a service is itself information the descriptor controls.
The boundaries that hold
A few invariants are worth stating outright, because they are what make this trustworthy:
- peinit never bypasses AccessCheck for a control operation. No backdoor, no override flag, no "trust localhost."
- The descriptors are the only policy inputs. peinit consults the ServiceSecurity and ControlSecurity descriptors and nothing else — not config files, not environment variables, not hardcoded lists.
- One service's state is never exposed to another without a check.
statusis per-service access-controlled;listfilters.
Where to start
For the other half of the security model — what a service can reach once it is running — read Service identity and privileges.
For the commands these rights gate, read Controlling services.
For the descriptor and AccessCheck machinery itself, read Security descriptors and Access decisions.