4.6 Service Security Descriptors
A service carries two independent descriptors, and they answer different questions.
The registry key descriptor on Machine\System\Services\<name>
controls who may read and write the service's definition. It is
enforced by LCS at key-open time and is not peinit's concern — peinit
reads definitions as SYSTEM.
The ServiceSecurity descriptor controls who may perform runtime
operations on the service through the control interface. It is stored
as a binary ServiceSecurity value on the same registry key, but
enforced by peinit rather than by LCS.
The two are genuinely independent. An administrator might be able to query a service's status without being able to read its configuration, or the reverse. Runtime control and configuration access are separate concerns and there is no reason for one to imply the other.
4.6.1 Access rights #
Restart requires SERVICE_START and SERVICE_STOP together.
Reset
requires SERVICE_STOP, because clearing a Failed or Abandoned state is
the tail of stopping something rather than the head of starting it.
The generic mapping peinit passes to AccessCheck:
4.6.2 Inheritance and the default #
A service whose definition carries no ServiceSecurity value takes the
one on Machine\System\Services itself. The lookup is a single step to
that key, not a walk up the hierarchy, which is exact for the flat
layout definitions actually use. That includes the compiled-in registryd,
which takes it once Phase 2 has read the key and on every reload after;
a registry definition of registryd (§2.3) may give it a descriptor of its
own like any other service.
If that key has no ServiceSecurity either, peinit applies a built-in
default:
O:SY G:BA D:(A;;0x000F;;;SY)(A;;0x000F;;;BA)(A;;0x0001;;;AU) S:(AU;FA;0x000F;;;WD)
SYSTEM and Administrators both get SERVICE_ALL_ACCESS. The SACL audits
every refusal, for everyone, which is what records a denial (below). An
administrator who may stop any service and shut the machine down gains
nothing by being unable to start one, so the default does not attempt a
narrower grant; a service that wants one carries its own
ServiceSecurity value.
Every authenticated principal gets SERVICE_QUERY_STATUS: what a service
is doing is not, by default, a secret from the people using the machine,
and a service whose state is a secret carries a ServiceSecurity that says so.
4.6.3 The check #
When a control command arrives, peinit:
- Takes the caller's token, captured when the connection was accepted.
- Resolves the target service and its ServiceSecurity descriptor. A
command naming no definition and no addressable definition-removed
entry returns
UNKNOWN_SERVICE; peinit does not invent a descriptor to check against. - Calls AccessCheck with the caller's token, that descriptor, the
generic mapping above, the right the command needs, and an audit
context naming the service,
{kind: "service", service: {name}}. - On denial, returns
ACCESS_DENIED. The attempt is recorded by KACS, not by peinit: when the descriptor's SACL asks for it — and the built-in default's does — the check writes akacs.audit.access.checkedrecord withobject.kindservice,object.service.name, the caller's token, and the access requested and granted (§8.4). - On grant, proceeds.
4.6.4 The SACL is the audit policy, and who may drop it #
A service's SACL decides which of its decisions are recorded. A descriptor written to the registry is used as it is: one with no SACL records nothing, and one that narrows the default's audits less.
ServiceSecurity is a whole registry value. Whoever may write it — on
the service's key, or on Machine\System\Services, whose value every
service without its own takes — may replace the descriptor, SACL
included, without holding SeSecurityPrivilege, which is what changing
an object's SACL otherwise needs. So those keys must be writable only by
principals trusted to change audit policy. By default they are: both
inherit the Machine hive root, which grants write to SYSTEM and
Administrators alone, and Administrators hold SeSecurityPrivilege.
Delegate a service through its ServiceSecurity DACL, never by
loosening the key's own descriptor; and a tool that changes only the
DACL must write the SACL back unchanged. The same holds for the control
descriptor in Machine\System\Init (§10.2).
4.6.5 Hot reload #
ServiceSecurity changes take effect on the next control request, with
no restart. A registry change notification triggers a configuration
reload, and the reload re-reads every descriptor. There is no cached
decision to invalidate — the check runs against the current descriptor
every time.
4.6.6 Filtering, not denying #
list returns only the services the caller has SERVICE_QUERY_STATUS
on. Services the caller cannot query are omitted, not denied: a
caller with no query rights anywhere receives an empty list and a
successful response. The denials are not surfaced to the caller; each
service's SACL decides whether KACS records them.
What the filtering protects is a service's state, which is what
SERVICE_QUERY_STATUS grants. That a service exists is not protected:
a command naming one is answered UNKNOWN_SERVICE or ACCESS_DENIED
according to whether it exists, and the definitions under
Machine\System\Services are readable. A client that can read the
definitions may therefore show a service list left out as one whose
state the caller may not see.