1. Overview
The PolicySecurity.cpp implementation securely reads RootGuard policy files from a single validated descriptor, limits policy size, optionally enforces root ownership/permissions, and detects in-place modification during the read.
The file integrates the following components, APIs, or runtime concepts:
- open(2)
- fstat(2)
- read(2)
- O_NOFOLLOW
- O_CLOEXEC
2. Descriptor Ownership
A small UniqueFd RAII class closes the file descriptor automatically and disables copying. System-call failures are converted into std::runtime_error with the current errno description.
3. Secure Open
The policy is opened with O_RDONLY | O_CLOEXEC | O_NOFOLLOW. Symlink targets are therefore rejected at open time, and the descriptor cannot leak into unrelated exec’d children.
4. File Validation
The first fstat() requires a regular file and a nonnegative size no larger than 1 MiB. When secure ownership is requested, UID must be 0 and group/other write bits must be clear.
5. Bounded Read
Content is read in 4096-byte chunks, retrying interrupted reads. The implementation rechecks the 1 MiB bound while accumulating data instead of trusting only the initial file size.
6. Modification Detection
A second fstat() is compared with the initial device, inode, size, modification time, and change time. Any difference causes the read to fail with “policy changed during read” semantics.
7. Runtime and Security Considerations
- TOCTOU resistance: validation and reading occur on the same descriptor.
- Symlink resistance:
O_NOFOLLOWprevents policy indirection. - Mutation detection: post-read metadata comparison detects concurrent replacement/modification.
- Resource bound: policy input is capped at 1 MiB.
