5.4.2 Access Rights

Registry rights occupy the Windows bit positions, so a Security Descriptor carrying registry ACEs is binary-compatible with one written by Windows tooling. That is a registry.pol and Samba requirement, not an aesthetic choice.

The values are in §5.A. In summary: six specific rights in bits 0–5 (KEY_QUERY_VALUE, KEY_SET_VALUE, KEY_CREATE_SUB_KEY, KEY_ENUMERATE_SUB_KEYS, KEY_NOTIFY, KEY_CREATE_LINK), the four standard rights DELETE, READ_CONTROL, WRITE_DAC and WRITE_OWNER, and ACCESS_SYSTEM_SECURITY for the SACL, which is itself gated by SeSecurityPrivilege.

There is no execute right on a key.

5.4.2.1 Convenience masks and generic mapping #

KEY_READ, KEY_WRITE and KEY_ALL_ACCESS are concrete masks, not generic bits — they are unions of the rights above and are usable directly.

Separately, LCS accepts the raw KACS generic bits GENERIC_READ, GENERIC_WRITE, GENERIC_EXECUTE and GENERIC_ALL in a caller's desired_access and in ACE masks in a Security Descriptor. Those are mapped through the registry generic mapping before AccessCheck sees them. GENERIC_READ maps to KEY_READ, GENERIC_WRITE to KEY_WRITE, GENERIC_ALL to KEY_ALL_ACCESS, and GENERIC_EXECUTE maps to zero, because there is nothing to execute.

5.4.2.2 Validating what a caller asks for #

desired_access is validated before path resolution or AccessCheck.

  • Zero is EINVAL. A caller must ask for something.
  • Any bit outside the valid caller mask is EINVAL.
  • MAXIMUM_ALLOWED may appear alone or combined with anything else.
  • SYNCHRONIZE (0x00100000) is not a registry right, so it is an unknown bit and fails EINVAL.

The valid caller mask is a single constant, REG_VALID_DESIRED_ACCESS_MASK (§5.A): the six specific rights, the four standard rights, ACCESS_SYSTEM_SECURITY, MAXIMUM_ALLOWED and the four generic bits.

5.4.2.3 Validating what a source returns #

Source-supplied Security Descriptors are validated too, because a source is trusted for its data but not for its arithmetic (§5.8.4).

An ACE mask may contain concrete registry rights and raw generic bits. It may not contain MAXIMUM_ALLOWED — that is a request, not a grant, and it is meaningless in an ACE. After generic mapping, an ACE mask must be a subset of the concrete registry rights plus ACCESS_SYSTEM_SECURITY.

A descriptor that breaks either rule is malformed source data: the operation fails closed with EIO and an LCS_SOURCE_VALIDATION_FAILURE audit event is emitted (§5.4.4).

Two further constants, REG_VALID_MAPPED_ACCESS_MASK and REG_VALID_ACE_ACCESS_MASK, express those two bounds.

5.4.2.4 Which right each operation needs #

OperationRequired
REG_IOC_QUERY_VALUE, QUERY_VALUES_BATCH, ENUM_VALUESKEY_QUERY_VALUE
REG_IOC_SET_VALUE, DELETE_VALUE, BLANKET_TOMBSTONE, FLUSHKEY_SET_VALUE
REG_IOC_ENUM_SUBKEYSKEY_ENUMERATE_SUB_KEYS
REG_IOC_QUERY_KEY_INFOREAD_CONTROL
REG_IOC_DELETE_KEY, HIDE_KEYDELETE
REG_IOC_NOTIFYKEY_NOTIFY
REG_IOC_GET_SECURITYREAD_CONTROL for owner, group and DACL; ACCESS_SYSTEM_SECURITY for the SACL
REG_IOC_SET_SECURITYWRITE_OWNER for owner or group; WRITE_DAC for the DACL; ACCESS_SYSTEM_SECURITY for the SACL
REG_IOC_BACKUPSeBackupPrivilege, no per-key check
REG_IOC_RESTORESeRestorePrivilege, no per-key check
creating a keyKEY_CREATE_SUB_KEY on the parent
creating a symlink keyKEY_CREATE_SUB_KEY and KEY_CREATE_LINK on the parent, plus SeTcbPrivilege or Administrators

Where a REG_IOC_SET_SECURITY or GET_SECURITY request names several components, every right those components imply must be present before the source is contacted.

REG_IOC_FLUSH requires KEY_SET_VALUE because a flush is only meaningful to a caller that wrote something and wants it durable. Requiring a write right stops an unprivileged reader using flush as a disk-I/O amplifier.

5.4.2.5 Enumeration exposes names, not contents #

REG_IOC_ENUM_SUBKEYS performs no per-child access check. Every visible child is returned, whatever the caller's access to it. The caller learns names, and must open each child separately — with a real AccessCheck — to read anything.

Subtree watches work the same way: a watcher is told a descendant was created without a check on that descendant. Structure visibility is deliberately weaker than content visibility, matching RegNotifyChangeKeyValue and RegEnumKeyEx.

Edit this page