gpasswd is a fifteenth tool, so a minor bump rather than a patch.
Also corrects the three static-musl sections for it. gpasswd is setuid
and authenticates a group administrator against the group password
through crypt(3) rather than PAM, so unlike chfn and chsh it keeps
working in the musl build; it resolves the caller through getpwuid_r, so
it joins the five tools that do not see directory users there; and it can
neither verify nor set a yescrypt group password. The counts around those
paragraphs were left at fourteen tools.
The README's multicall paragraph still named four setuid applets.
* gpasswd: added implementation of gpasswd
* gpasswd: rebase onto main and port to the current APIs
Rebases jlesage's implementation onto a main that moved a long way under
it, and carries it onto the interfaces that landed since the branch was
cut. The tool's behaviour is unchanged and still matches GNU on every
path probed.
What the rebase had to resolve:
- shadow-rs.spec and debian/ were revived by the merge; both were
deleted because neither could build.
- The standalone dispatch() and hand-written --list predate the single
applets() table, which now drives both. gpasswd joins the table.
- The five shadow-core parser features it asked for no longer exist.
- The fourteen [[test]] targets are one; test_gpasswd joins it.
- README, Makefile and PLATFORM-SUPPORT described the pre-#239
privilege model, in which every applet ran with euid 0.
What changed beyond the rebase:
- gpasswd keeps its privilege in the multicall build. It is setuid in
GNU and the branch marks it setuid for the per-tool install, but
SETUID_APPLETS still listed four names, so the multicall build would
have dropped privilege and no group administrator could have used it
-- silently.
- Comments in /etc/group survive. It read entries only and wrote them
back, which erases every comment, blank line and NIS compatibility
line in the file. The locked-file transaction keeps them, and commits
group and gshadow together so a bad value cannot leave the pair
disagreeing.
- The duplicated EchoGuard and password reader are gone, replaced by
shadow_core::tty, which blocks interrupts for the read so Ctrl-C at
the prompt cannot leave the terminal with echo off.
- The print macros are replaced. They panic when the stream is closed,
which a setuid-root tool must not do part way through a change.
These were the only five in the repository and no lint caught them,
so clippy::print_stdout and print_stderr are now denied
workspace-wide, with the test crate exempt.
The GNU comparison suite gains gpasswd: the unknown-group, unknown-user
and non-member exit codes, all three of which match.
CONTRIBUTING's licence list also said ISC, BSD-2-Clause, CC0-1.0, Zlib
and MPL-2.0 were acceptable, which stopped being true when deny.toml was
narrowed to the graph. It now says what deny.toml enforces.
---------
Co-authored-by: Pierre Warnier <pierre@warnier.net>
Two defects in what shipped yesterday.
0.3.0's glibc archives require GLIBC_2.39 and will not start on Debian
12, Ubuntu 22.04 or RHEL 9 -- all of which ran 0.2.2. Pinning the release
runner to Ubuntu 24.04 in #272 let Rust's std link pidfd_spawnp and
pidfd_getpid; those two are the only symbols in the binary above 2.34.
The build now happens inside an ubuntu:22.04 container on the same
runner, which is where the container support in dist earns its keep: the
runner stays current while the floor goes back to GLIBC_2.34, exactly
what 0.2.2 required. Verified by building in that container and dumping
the versioned symbols, and by running the result on a glibc 2.35 host
where the 0.3.0 archive refuses to start. The static musl archive was
never affected.
The PAM test for an unconfigured service could hang rather than fail. On
Debian /etc/pam.d/other falls through to common-auth, which is pam_unix,
so it prompts: the conversation then blocks reading stdin. CI never saw
it because its stdin is not a terminal, but `make check` on a developer's
machine hung indefinitely. The test is removed with a note saying why,
and the permit and deny tests -- which are the ones that matter and which
never converse -- stay.
Thirty-eight commits since 0.2.2, none of them in the changelog: the
Unreleased section described the musl archive and nothing else. It now
covers the lot, under the headings the file already uses.
The release itself is the reason to do this now. The aarch64 archive
support merged on Friday and has never run: the configuration is
verified and dist plan lists all three archives, but no tag has yet
driven the pipeline.
Highlights, all documented in full in the changelog:
- --root performs a real chroot(2) in every tool, where eight of the
thirteen treated it as a path prefix.
- passwd -l is idempotent, and chage -l prints what chage(1) prints.
- chpasswd takes its hashing scheme from ENCRYPT_METHOD.
- chsh no longer answers differently for a path that exists and one
that does not, which made a setuid-root tool a filesystem oracle.
- login.defs numbers are read in octal and hexadecimal as documented.
- ID allocation consults the name service.
- An aarch64 archive, and the user-facing tools install into bin.
* ci: run the root test suite on pull requests, restrict token permissions
About three quarters of the integration suite is root-only and returns
silently when not root, reported as ok. The only job that runs as root was
the Docker matrix, gated on push, so a pull request effectively tested a
quarter of the suite and regressions surfaced only after merge. The matrix
now runs on pull requests too; each distro takes a couple of minutes.
The MSRV job checks --all-targets, so the test code is covered by the
minimum supported version too, and both workflows declare least-privilege
token permissions instead of inheriting the default scope.
* ci: word-boundary the tooling-artifact patterns and cover two more forms
The script's own comment claimed the patterns were word-boundaried; they
were not, so several of them matched as substrings inside ordinary
identifiers and English words, failing any change that happened to touch
one. The alternation is now anchored with \b.
Two forms that slipped through are added: a model name carrying a version
suffix, and the co-author trailer a bot appends to a commit.
CONTRIBUTING.md becomes exempt from the added-lines scan, because it
states the project's policy on such tools and therefore has to be able to
name them; the CI workflow no longer needs an exemption, since nothing in
it matches.
* deps: drop the unused clap derive feature, tighten the license policy
Nothing in the tree derives clap's Parser/Args/Subcommand/ValueEnum, so
the derive feature only pulled clap_derive and heck into the build graph.
It is not the cause of the syn 2/3 duplicate: syn 3 arrives through
uucore -> fluent -> displaydoc and syn 2 through landlock -> enumflags2,
neither of which is ours to move. The deny skip now records that, and
matches any 2.x rather than one stale patch version that no longer exists
in the lockfile.
MPL-2.0 leaves the license allowlist -- it is file-level copyleft and this
project ships MIT only, so a dependency needing it should be a decision
rather than a default -- and a crate from outside crates.io is now denied
instead of warned about.
First release to ship prebuilt binaries, and the first to carry a
working passwd in them: 0.2.1 and earlier had no assets at all, and the
archive the new pipeline first produced was built without PAM.
Closes#221.
Renovate's single-package update (#198) could not land this: clap_derive
4.6.4 requires syn >=3.0.2 while the lockfile pinned syn 3.0.0 (via
thiserror-impl), so the conservative 'cargo update -p clap --precise'
had no solvable resolution. Update both together (clap 4.6.3 -> 4.6.4,
syn 3.0.0 -> 3.0.3). cargo deny check passes with the syn skip added in
#194.