kern: do not allow unprivileged chroot when pwd is shared Unprivileged chroot(2), fchroot(2), chaltroot(2), and fchaltroot(2) rely on PROC_NO_NEW_PRIVS to make set-ID images harmless under the new root. But the pwddesc can be shared by rfork(2) without RFFDG with a process that has not enabled PROC_NO_NEW_PRIVS, and the change of the root affects that process as well. It can then execute a set-ID image whose ELF interpreter is resolved under the root controlled by the caller. Refuse the unprivileged operations when the pwddesc is shared. Processes that start sharing it later are created by the caller and inherit PROC_NO_NEW_PRIVS. Sponsored by: The FreeBSD Foundation MFC after: 1 week Differential revision: https://reviews.freebsd.org/D60035 pwddesc: add sharecount The pd_refcount field counts both sharing processes and the transient pdhold() references taken by sysctls and mountcheckdirs(), so it cannot answer "is this pwddesc shared". As a result pdunshare() made needless copies while the pwddesc was held, and a copy made while mountcheckdirs() held it lost the cwd/root update. Add pd_sharecount, counting only processes using the pwddesc, and use it for the sharing decision. Existing pd_refcount still manages the lifetime. Sponsored by: The FreeBSD Foundation MFC after: 1 week Differential revision: https://reviews.freebsd.org/D60035 cap_enter(2): stricter checks - single-thread the process Make cap_enter() a serialization point, ensuring that no threads are executing with old credentials cached after the syscall returned. - disallow entering capability mode if the file descriptor table or pwd are shared. They are shared after rfork(2), and allowing it lets another process grant additional privileges to the process in capability mode. cap_enter.2: document new behavior, in particular EBUSY Sponsored by: The FreeBSD Foundation MFC after: 1 week Differential revision: https://reviews.freebsd.org/D60035
Details
- Reviewers
markj olce - Group Reviewers
secteam - Commits
- rG82bbfe16ae1e: cap_enter.2: document new behavior, in particular EBUSY
rGb357c9444899: cap_enter(2): stricter checks
rGaff8d265fa1c: chroot.2: document EPERM for shared pwd
rGe95e010fd0de: kern: do not allow unprivileged chroot when pwd is shared
rG5276fba9fd55: pwddesc: add sharecount
rGf809179ba981: kern/vfs_syscalls.c: extract privilege check from kern_chroot()
Diff Detail
- Repository
- rG FreeBSD src repository
- Lint
Lint Skipped - Unit
Tests Skipped
Event Timeline
Rebase. Put this series first, ahead of D60035, in particular, this diff no longer references altroot and should cleanly apply to stock main.
I think this can be committed to main after review since unprivileged chroot is disabled by default, but it deserves an SA IMO.
I believe the cap_enter() change does not need to be an SA, there is no trust boundary being crossed. If I fork and exec a process which enters a sandbox, then I can also, for example, preload a DSO which turns cap_enter() into a no-op.
| sys/kern/kern_descrip.c | ||
|---|---|---|
| 2561 | Slightly cleaner would be to set old = refcount_acquire(&pdp->pd_sharecount) and assert old > 0. | |
| 2880 | This assertion is redundant I think. | |
| 2881 | ||
| sys/kern/vfs_syscalls.c | ||
| 986 | How is this synchronized with a concurrent fork? | |
| sys/kern/vfs_syscalls.c | ||
|---|---|---|
| 986 | It is not, but the child inherits P2_NO_NEW_PRIVS if we see 1. I added a comment explaining the handling of the race. | |