fix(daemon): trust the kernel overflow uid as an ancestor owner inside a restricted user namespace - #2103
Conversation
…e a restricted user namespace posix_directory_owner_trusted only accepted an ancestor owned by uid 0 or the caller's own euid. Inside a single-uid Docker userns-remap (and some WSL2 devcontainer setups), uid 0 has no entry in the container's uid map, so the kernel renders any host-owned path whose real owner is unmapped, including real root which owns "/", "/home" and "/tmp", as the kernel overflow uid instead. Every ancestor of a private cache directory then rendered as that overflow uid, so the trust check refused it and the daemon could never secure its private directory: every path failed with "secure CLI coordination could not be created". Extend posix_directory_owner_trusted to also accept the kernel's own overflow uid (read once from /proc/sys/kernel/overflowuid, falling back to the 65534 default if unreadable), but only when this process's own user namespace genuinely has no mapping for uid 0 (read from /proc/self/uid_map). Outside a restricted namespace the overflow uid names an ordinary account (services dropped to "nobody"), not a stand-in for unmapped root, so trusting it unconditionally would let anything writable by such an account own a trusted ancestor. The leaf private directory's own ownership check is untouched: it still requires an exact match against the caller's euid before it is chmod'd to 0700. Tests: two new Linux-only, root-required tests (fabricating an arbitrary-uid ancestor needs CAP_CHOWN). One confirms an overflow-uid ancestor is admitted when the process's own namespace has no mapping for uid 0; the other confirms the same ancestor still stays refused outside one, so the fix cannot regress the ordinary case. A real restricted namespace needs CAP_SYS_ADMIN to construct, which this sandbox does not grant even inside a fresh container (confirmed live: unshare --user fails EPERM here), so the namespace state is pinned through a test-only seam instead of unshare(1), consistent with how the file's own Windows tests already pin non-constructible OS state. Fixes DeusData#1830 Signed-off-by: Amir Fathi <amirfathi.me@gmail.com>
|
Thanks for opening this — it has been seen, and it is queued. This note is automated, but it is not a brush-off: it exists so you know where your PR stands instead of having to guess from silence. Current review status: working through a backlog. What that means for this PR, concretely:
Things that will genuinely speed it up whenever review does happen:
If this fixes a bug, a reproduction we can run is worth more than a description of the symptom. Thanks for contributing, and sorry in advance for the wait. |
|
Thank you for contributing the restricted-user-namespace case and the focused IPC tests. We need more time for a careful review of this ownership-policy change before giving a decision. The review queue is currently full, so detailed feedback may take a little time. We are working through it carefully and appreciate the work you have put into supporting the project. Thank you for your patience. |
What does this PR do?
Inside a single-uid Docker userns-remap container (or some WSL2 devcontainer
setups),
cbmfails to start with "secure CLI coordination could not becreated" on every path. The ancestor-owner trust check in
posix_directory_owner_trusted(src/daemon/ipc.c) only accepts anancestor owned by uid 0 or the caller's own euid. With a single-uid
mapping, uid 0 has no entry in the container's uid map, so the kernel
renders any unmapped host owner, including real root which owns
/,/homeand/tmp, as the kernel overflow uid (65534 by default,configurable) instead. Neither branch matches, so every ancestor is
refused.
The fix extends the trust check to also accept the overflow uid, read
once from
/proc/sys/kernel/overflowuid, but only when this process'sown namespace genuinely has no mapping for uid 0 (read from
/proc/self/uid_map). Outside a restricted namespace the overflow uid isan ordinary account (a service dropped to "nobody"), not a stand-in for
unmapped root, so it is not trusted unconditionally. The leaf private
directory's own ownership check is untouched: it still requires an exact
match against the caller's euid before it is chmod'd to 0700.
Checklist
git commit -s)scripts/test.sh --suites daemon_ipc, 49/49)clang-format-20 --dry-run --Werror,cppcheck)Verification
daemon_ipc_posix_private_directory_admits_overflow_uid_ancestor_inside_restricted_usernsgoes red when just this fix's hunk in
posix_directory_owner_trustedisreverted (
ASSERT(secured)), and passes with it restored...._refuses_overflow_uid_ancestor_outside_restricted_usernsisa negative control: it stays green either way (the ordinary case is
unchanged), and I confirmed it is not vacuous by temporarily making the
trust check always return
true, which turns it red.scripts/test.sh --suites daemon_ipc(49/49),clang-format-20and
cppcheckall clean.Building a real restricted user namespace needs
CAP_SYS_ADMIN, whichis unavailable here even inside a fresh container (
unshare --userfails
EPERM), so both new tests fabricate the ownership conditiondirectly and pin the namespace state through a test-only seam rather
than a real namespace.
Fixes #1830