chflags -RL and symlinks pointing outside the traversal: preserve behavior or add a Capsicum guardrail?
Jitendra Bhati <[email protected]>
| Newsgroups | gmane.os.freebsd.devel.hackers |
|---|---|
| Message-ID | <CAEqhz_Vq2sJt3mWqBFB8bDQmx5YO8Vbu=0A3J4AkMgng2buAtA@mail.gmail.com> |
Hi all, As part of GSoC 2026 I'm capsicumizing chflags(1), using the new fts_openat(3) API: https://github.com/freebsd/freebsd-src/pull/2375 This raises a backward-compatibility question that Alan Somers and I think warrants wider input. In physical modes (-P, -h, and the default without -L) the traversal now runs inside Capsicum capability mode: chflags pre-opens a directory descriptor per argument, calls cap_enter(), and operates via: chflagsat(fts_parent->fts_dirfd, fts_name, ...); This confines chflags to the hierarchies named on the command line. The complication is -L (FTS_LOGICAL), where chflags follows symlinks. A followed symlink can point outside the traversal root, e.g.: file foo/bar/baz/link -> ../../../file Running: chflags -RL <flags> foo/bar today follows link and changes the flags on file, which lives outside foo/bar. Capability mode would block that access, because file isn't reachable from the descriptor opened for foo/bar. For now the PR preserves existing behavior: when -L is in effect it skips cap_enter() and keeps the historical path-based call: chflagsat(AT_FDCWD, p->fts_accpath, ...); So -L is not capsicumized, but nothing regresses. The open question: is "following a symlink out of the named hierarchy" behavior worth preserving, or is it exactly the kind of thing a Capsicum-ized chflags should refuse? A tree that redirects chflags at arbitrary files via a crafted symlink is arguably a threat we'd want to guard against. But it's also a long-standing, documented -L semantic, and changing it would be a POLA violation for anyone relying on it. Options: Keep -L as-is (path-based, not confined) - current PR behavior. Capsicumize -L too, and treat out-of-hierarchy symlink targets as an error. Capsicumize -L but keep out-of-hierarchy targets working by opening a descriptor for the resolved target's parent (loses most of the confinement benefit for -L). I lean toward (1) for this PR and revisiting -L separately, but I'd appreciate opinions on whether the stricter behavior is worth the compatibility break. Thanks, Jitendra Bhati ᐧ