Re: chflags -RL and symlinks pointing outside the traversal: preserve behavior or add a Capsicum guardrail?
Mark Johnston <[email protected]>
| Newsgroups | gmane.os.freebsd.devel.hackers |
|---|---|
| Message-ID | <aoXVqgVVqhPqQFCh@nuc> |
On Wed, Aug 19, 2026 at 09:40:35AM -0600, Alan Somers wrote: > On Wed, Aug 19, 2026 at 8:37 AM Jitendra Bhati <[email protected]> > wrote: > > > 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 > > > > I vote for #2. Capsicumize the utility, and treat out-of-hierarchy > symlinks as an error. I think that real users will almost never want to > follow out of hierarchy symlinks. Also, even if this is a POLA violation, > it's extremely rare. With grep, I couldn't find any example of chflags -RL > in the base system. Github code search turned up a few. Some were MacOS > Homebrew recipes that invoke chflags right after installing the package. > Those uses surely would prefer not to follow out-of--hierarchy symlinks. > The most interesting use I found was from quBSD, a jail/bhyve manager. I > don't know if that project really intends to follow out-of-hierarchy > symlinks or not, but I've notified him. If we go this route, I think there does need to be a chicken switch for any users that are legitimately impacted and want an easy workaround. I'm not sure what that would look like: an environment variable, sysctl, new command-line option? Should any such chicken switch be global, i.e., should it apply to all capsicumized utilities which use fts? I presume you are planning to extend this to work with other fts-using utilities which might benefit more from running in a sandbox, like grep.