chflags -RL and symlinks pointing outside the traversal: preserve behavior or add a Capsicum guardrail?
- Reply: Alan Somers : "Re: chflags -RL and symlinks pointing outside the traversal: preserve behavior or add a Capsicum guardrail?"
- Reply: Jamie Landeg-Jones : "Re: chflags -RL and symlinks pointing outside the traversal: preserve behavior or add a Capsicum guardrail?"
- Go to: [ bottom of page ] [ top of archives ] [ this month ]
Date: Wed, 19 Aug 2026 14:37:03 UTC
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 ᐧ