All notable changes to this project will be documented in this file.
The format is based on Keep a Changelog, and this project adheres to Semantic Versioning.
audit.ymlworkflow for dailycargo-auditsecurity advisory checks (#155)- AT_EXECFN validation in multicall binary: rejects spoofed
argv[0]in setuid context by comparing against the kernel-recorded executable path (#154)
- AT_EXECFN check uses
rustix::param::linux_execfn()instead of unsafelibc::getauxval— zero new unsafe for this feature - All workspace crate versions aligned to 0.2.0
usermod -p/--passwordflag for setting pre-hashed passwords (#114)- End-to-end deployment tests in Docker: 117 assertions covering symlink dispatch, setuid, PAM, Landlock, nscd, and Ansible interop (#102, #115)
- Docker multi-distro CI in GitHub Actions (debian, alpine, fedora)
- Shell completion generation for bash, zsh, fish (#106)
- Renovate for automated dependency updates
rust-toolchain.tomlfor contributor conveniencefeat_common_corefeature alias (all 14 tools)
- Cargo.toml metadata aligned with uutils ecosystem conventions
- Tool crate descriptions normalized to
"tool ~ (shadow-rs) verb phrase"format - Edition 2024 consistently applied across root and workspace packages
make installnow defaults to 14 standalone per-tool binaries with least-privilege setuid layout matching GNU shadow-utils (#138). Onlypasswd/chfn/chsh/newgrpare setuid-root; the other 10 are0755. The previous multicall install is available asmake install-multicall.nixcrate fully replaced byrustix(raw syscalls, no libc overhead).libckept only for PAM FFI, crypt(3) FFI, and process-wide POSIX wrappers (setuid/sigprocmask/getpwuid_r) (#140)uucoreupgraded from 0.7 to 0.8 (#150)- Repo transferred from
shadow-utils-rs/shadow-rstouutils/shadow-rs
- PAM password buffers zeroed immediately after use (
zeroize) initgroups()called in newgrp before exec (prevents supplementary group leak)SignalBlockerscoped to file-mutation critical sections only; dropped before long-running operations (home deletion, recursive chown, skel copy)UmaskGuardmarked!Send/!SyncviaPhantomData<Rc<()>>(thread-safety)newgrpuses targeted hardening (suppress_core_dumps+sanitized_env) instead ofharden_process()to avoid leakingRLIMIT_FSIZEto exec'd shellatomic_writeretries once on stale temp file from prior crashcrypt(3)wrapper documented as non-thread-safe (uses global state)- Centralized hardening utilities in
shadow_core::hardening(deduplicated from per-tool copies) println!/eprintln!replaced with non-panicking writes (#141)- Unwind tables suppressed via
-C force-unwind-tables=no(#143)
- Password hash validation rejects
:,\n,\r(field injection prevention) - Error on missing shadow entry in usermod (was silent no-op)
days_since_epoch()centralized in shadow-core (was duplicated)
- All 14 shadow-utils tools implemented as drop-in replacements:
passwd,useradd,userdel,usermod,groupadd,groupdel,groupmod,pwck,grpck,chage,chpasswd,chfn,chsh,newgrp - Single multicall binary with symlink dispatch (894 KB stripped)
- PAM integration for password authentication and changes
- Atomic file writes with lock-via-hard-link pattern (TOCTOU resistant)
- Stale lock detection via ESRCH-only PID checking
- Password zeroing via
zeroizecrate - Core dump suppression and file size limit hardening
- Environment sanitization (safe for setuid-root context)
- Signal blocking during critical file operations
- SELinux file context support (best-effort via external tools)
- Audit logging to syslog and auditd
- subuid/subgid allocation for rootless containers (useradd)
- Recursive chown on UID change (usermod)
- Proper date validation with leap year and month-length rules
- GNU-compatible output and exit codes for all tools
- 580+ unit tests, property-based tests (proptest), 6 fuzz targets
- Integration tests for 14 tools
- Docker test matrix: Debian (glibc), Alpine (musl), Fedora (SELinux)
- CI gates: fmt, clippy, test, MSRV (1.94.0), cargo-deny
- Debian and RPM packaging
- Man pages for all 14 tools
- GNU compatibility test suite and PAM end-to-end test
unsafe_code = "deny"enforced at workspace level (only PAM/crypt FFI exempted)dead_code = "deny"enforced at workspace level- O_EXCL temp files (symlink attack prevention)
- Umask guard (RAII) for restrictive file permissions
- GPL clean-room development (MIT license, no GPL source referenced)
- Reviewed by GitHub Copilot (automated) and Google Gemini CLI (manual)
- 20+ security findings addressed across 4 review rounds