mirror of
https://github.com/uutils/shadow.git
synced 2026-10-16 02:48:14 -07:00
The setuid helpers that write /proc/<pid>/uid_map and gid_map: what Podman and rootless Docker call for every container they start. The kernel lets an unprivileged process map exactly one id into a namespace it created, its own; everything a container needs beyond that takes the privilege these carry, and they hand it out only within the ranges /etc/subuid and /etc/subgid grant the caller. Fedora ships them in shadow-utils; with these two, every tool the two reference distributions ship from the suite exists here. What is checked before anything is written was established by running the GNU helpers against a real user namespace, never by reading them: the operands must be a target and plain-decimal triples, a zero count is the overflow message; every outside range must sit inside one granted range -- an entry by login name or by numeric uid -- or be the caller's own id mapped once; root is exempt from neither, as newuidmap(1) says; and the target process must belong to the caller by real uid, account uid and the owner of /proc/<pid>, with all three named when they disagree. Overlapping ranges are refused by name; the kernel would refuse them with a bare EINVAL, so the message costs nothing and changes no accepted outcome. The map file is opened through a descriptor held on /proc/<pid>, so a pid recycled between the check and the write cannot redirect it, and the `fd:N` form takes such a descriptor from a caller that did its own checks. It is re-opened through /proc/self/fd rather than adopted, which keeps the tool free of unsafe. The tests do the real thing. A test user creates a namespace with unshare and the helper runs as that user, unprivileged, against it: the own-id mapping lands and the kernel's uid_map reads it back, the second write is refused by the kernel and reported, the fd form works, and every refusal is checked against the same live target. That runs on glibc and musl alike. The Debian image gains the uidmap package so tests/gnu-compat.sh can hold the helpers against GNU's. They take no --help -- any bad command line is the usage text and exit 1 -- so the comparison is on the refusals the two share rather than on help. Verified: make check clean with the unprivileged run; 912 tests on alpine and 934 on fedora with PAM; 393 e2e assertions against a real install, the kernel-visible writes included; 57 GNU comparisons; 34 arm64 assertions under emulation.