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
ᐧ
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.