A process holds CAP_NET_ADMIN in its permitted and inheritable sets and then executes a helper binary that has no file capabilities of its own. Why does the helper start with nothing, and which of the Linux capability sets exists to fix exactly this case?
answer
- five sets, not one
- the name promises more than it delivers
- the file has to agree at exec
- a set added later to carry it through
- a ceiling that only ever shrinks
basics
~20 sA thread carries five capability sets: permitted, effective, inheritable, bounding and ambient. Across execve of a file with no capabilities, the inheritable set grants nothing on its own — it must be matched by the file. The ambient set, added in Linux 4.3, is what carries privilege through.
solid answer
~60 sThe inheritable set is badly named: it does not mean "my children keep this". At `execve()`, the kernel intersects the process's inheritable set with the *file's* inheritable set, so a binary with no capability attribute contributes zero and the new permitted set comes out empty. That was the long-standing gap — you could not hand a capability to an ordinary, unmodified program without either making it setuid root or running `setcap` on it. Linux 4.3 added the ambient set to close it: capabilities raised there, with `prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_RAISE, ...)`, survive `execve` of a file that carries no capabilities and land in the child's permitted and effective sets. A capability may only be ambient if it is in both permitted and inheritable, and the whole ambient set is wiped when a set-user-ID or file-capability binary is executed, so it cannot be used to smuggle privilege into a privileged program. Above all of these sits the bounding set — a per-thread ceiling that is inherited, can only shrink, and caps what any exec can ever grant.
code
bash · 3 linescapsh --print
grep -E '^Cap(Inh|Prm|Eff|Bnd|Amb)' /proc/self/status
capsh --decode=0000000000000400go deeper
Learn the names of the five sets and the one headline fact: putting a capability in the inheritable set does not, by itself, give it to a program you execute.
Explain the exec handshake — the thread's inheritable set is intersected with the file's inheritable set — and say what the ambient set was introduced to solve.
Diagnose a real service that lost a privilege: read CapPrm, CapAmb and CapBnd of the running process, and distinguish never-granted from granted-then-bounded before changing anything.
Own the guarantee. Decide which capabilities are removed from the bounding set fleet-wide so no descendant can regain them, and require that services obtain privilege through a declared ambient grant rather than capabilities baked onto binaries.
## The five sets Every thread carries five capability bitmasks, visible as `CapPrm`, `CapEff`, `CapInh`, `CapBnd` and `CapAmb` in `/proc/<pid>/status`: - **Permitted** — the capabilities the thread is allowed to have. It is the reservoir; a capability not in permitted can never be made effective. - **Effective** — the subset the kernel actually checks right now. Capability-aware programs keep this empty most of the time and raise a capability only around the privileged call. - **Inheritable** — capabilities that *may* be picked up across `exec`, but only in cooperation with the file being executed. - **Bounding** — a ceiling. Anything cleared here can never appear in permitted again, for this thread or any descendant. - **Ambient** — capabilities that survive `exec` of an ordinary, unprivileged file. ## What happens at execve The kernel recomputes the sets using the file's own capability attribute (its permitted set F(p), inheritable set F(i), and effective flag). Simplified: - new permitted = (thread inheritable ∧ file inheritable) ∨ (file permitted ∧ bounding) ∨ new ambient - new effective = file effective flag ? new permitted : new ambient - inheritable and bounding are carried over unchanged Read the first line against the scenario. The helper binary has no capability attribute, so F(i) and F(p) are both zero. The first term collapses, the second collapses, and unless something is in the ambient set the new permitted set is empty. The process holding `CAP_NET_ADMIN` in permitted and inheritable achieves nothing at all. This is the single most misunderstood point in the model. "Inheritable" does not describe what a child inherits from a parent; it describes one half of a handshake in which the executable must also opt in by listing the same capability in its own inheritable set. Both sides must agree. ## Why the ambient set was added Before Linux 4.3 there were only two ways to run an ordinary program with a capability: give the file a capability attribute with `setcap`, or make it setuid root and have it drop down. Both put privilege on disk, and neither works for a service manager that wants to start an arbitrary, unmodified binary with one narrow privilege. The ambient set fixes that. A privileged parent raises the capability there and then execs the target; the capability lands in the child's permitted and effective sets even though the file carries nothing: ``` capsh --keep=1 --user=nobody \ --inh=cap_net_bind_service --addamb=cap_net_bind_service \ -- -c 'grep ^Cap /proc/self/status' ``` The rules that keep it safe are worth naming: - a capability can only be raised in ambient if it is already in **both** permitted and inheritable; - dropping it from permitted or inheritable drops it from ambient automatically; - the entire ambient set is **cleared** when a set-user-ID, set-group-ID, or file-capability program is executed, so you cannot bolt extra privilege onto a program that is already doing its own privileged transition; - setting `PR_SET_NO_NEW_PRIVS` also prevents privilege gains at exec. ## The bounding set as a ceiling The bounding set is different in kind from the other four: it never grants anything, it only forbids. Clearing a capability from it requires `CAP_SETPCAP`, is irreversible for that thread, and is inherited by every descendant — including across `exec` of a binary whose file permitted set contains the capability, because the bounding mask is applied to that term too. That property is what makes it the right tool for a hard guarantee. Saying "this service tree can never load a kernel module" is a statement about the bounding set, not about the permitted set, because permitted can be replenished by executing a file with capabilities while bounding cannot. ## Inspecting it in practice `capsh --print` shows all the sets for the current shell; `getpcaps <pid>` shows them for another process; `capsh --decode=0000000000000400` turns a hex mask from `/proc/<pid>/status` into names. When a service unexpectedly lacks a privilege, reading `CapPrm` and `CapBnd` of the running process usually settles in one step whether the capability was never granted or was granted and then bounded away.
- Why is the ambient set cleared when a set-user-ID binary is executed?Because the program is already performing its own privilege transition and was never written to expect extra capabilities on top. Letting a caller inject ambient capabilities into a setuid program would create a new escalation channel: the caller chooses privileges, the program runs as another user, and the two combine in ways the author never audited. Clearing ambient keeps the two mechanisms from stacking.
- What guarantee does dropping a capability from the bounding set give that dropping it from permitted does not?Permanence and reach. A permitted set can be refilled by executing a file that carries the capability in its own permitted set, so dropping it there is only a statement about right now. The bounding set masks that term at every future exec, applies to all descendants, and cannot be raised again — which is why it is what you use for "this tree can never do X".
- If a capability is in a process's permitted set but not its effective set, what can the process do with it?Nothing yet, but it can raise it. The kernel checks the effective set only, so a permitted-but-not-effective capability is a privilege held in reserve. Capability-aware programs exploit that deliberately: run with an empty effective set, raise the capability with libcap around the one syscall that needs it, then lower it again, shrinking the window in which a bug can be exploited.
saying these in an interview costs you the question
- Inheritable means children automatically keep the capability
- The bounding set grants capabilities to a process
- Ambient capabilities survive exec of a setuid binary
- Effective and permitted are the same thing
- Dropping from permitted is permanent for descendants