`pam` is not a default cargo feature, so every build path except the e2e
image compiled the interactive change path out of `passwd`. The tool
could report and lock accounts but not change a password:
$ passwd alice
shadow-rs: PAM support is not compiled in — cannot change password
interactively
That covered the release archive, `make install` (the default install of
14 standalone binaries) and `make install-multicall`. Only the e2e image
passed --features pam, and its assertions stop at `passwd -S`, which
works without PAM — hence nobody noticed.
Enable the feature in the release build and in both Makefile targets, and
install libpam0g-dev on the release runner. The PAM headers are already a
documented build requirement.
Add an e2e assertion that the installed passwd is linked against libpam,
so this cannot regress silently again.
Closes#220.
Address Copilot review round 2:
- Remove PROFILE variable and hardcode --release / target/release/.
Cargo maps "dev" profile to target/debug/ (not target/dev/), so
PROFILE=dev would silently break install. Nobody installs debug
builds of setuid-root tools.
- Fix stale comments in Makefile and README that referenced the old
"chmod on symlink follows through" approach. The current code uses
install -Dm4755 directly.
Address review comments on #139:
- build: replace 14-call loop with a single
`cargo build --workspace --bins --exclude shadow-rs` invocation,
eliminating per-tool Cargo startup/metadata overhead. Matches the
uutils/coreutils GNUmakefile pattern.
- install: use `install -Dm4755` / `install -Dm0755` to set mode atomically
during install instead of post-install chmod. Errors no longer swallowed:
if install can't set the setuid bit, `make install` now fails loudly.
- install-multicall: same — `install -Dm4755` sets the shadow-rs binary
setuid in one step.
Switch the default install to build and install 14 standalone per-tool
binaries with a least-privilege setuid layout matching GNU shadow-utils.
Only passwd/chfn/chsh/newgrp are setuid-root; the other 10 tools are 0755.
The previous multicall install is preserved as `make install-multicall`
for container/embedded use cases where the ~14x disk savings matter and
the enlarged setuid attack surface is acceptable.
The old layout chmod'd symlinks pointing to the multicall binary, which
(because chmod follows symlinks) marked the underlying ELF setuid-root.
That meant all 14 tools ran with euid=0 when invoked via symlink. Each
tool's internal getuid() check was defense-in-depth, not OS-level least
privilege. The standalone layout gives the latter.
Raised by @oech3 in uutils/coreutils#11828.
- Fix cargo build --release --workspace in test scripts
- Document setuid-on-symlinks as intentional in Makefile
- Warn (not abort) on subid allocation failure in useradd
- Propagate subid file read errors instead of unwrap_or_default
- Update SELinux docs to describe best-effort behavior
- Add env_clear() to getfattr subprocess in selinux.rs
- Use hardening::sanitized_env() in nscd.rs instead of local reimpl
- Create testuser in pam-e2e.sh, add cleanup
- Document su-as-root limitation in pam-e2e.sh
- Fix set -e exit code capture in gnu-compat.sh
- Add 5 date validation unit tests in chage
- Wire audit::log_user_event into all 7 tools