* shadow-core: link libcrypt only where it exists; refuse pam on static musl
musl implements crypt(3) inside libc and ships no libcrypt. Asking for
-lcrypt there made a cross-build for x86_64-unknown-linux-musl fall through
to the host's glibc-built libxcrypt and fail on glibc-only symbols; Alpine
only worked because musl-dev ships an empty libcrypt.a.
Linux-PAM loads its modules with dlopen, which static musl does not have,
and no distribution ships libpam.a. Building the pam feature into a
crt-static musl binary is therefore a configuration error; reject it at
compile time instead of producing a binary whose authentication path can
never work.
The crypt tests stated the musl behaviour wrongly (musl does honour
rounds=; it lacks yescrypt). They now assert the real behaviour per target
instead of skipping.
* release: publish a static musl archive next to the glibc one
dist cannot vary cargo features per target and pam must be off for musl,
so the musl archive is built by 'make dist-musl' and attached by dist as an
extra artifact of the same release. The target enforces the lockfile,
checks the binary has no DT_NEEDED entry and packs it with the licence,
changelog, README and PLATFORM-SUPPORT.md so the trade-off travels with
the binary. The archive name carries a -static suffix so it is never
mistaken for the glibc build.
CI runs the same make target on every pull request, smoke-tests the
archive, runs shadow-core's unit tests against static musl and checks that
the pam feature is rejected there, so a tag is never the first time the
archive is built.
* docs: describe both release archives and what the static musl one gives up
The README now lists the two archives side by side with the one question a
reader has to answer (is PAM, NSS or yescrypt needed on this host?), and
PLATFORM-SUPPORT.md is rewritten around the archive that now exists: how
it is built, which CI job guards it, and the exact effect of each gap.
CONTRIBUTING listed two unsafe-permitted modules; there are three.
* chore: use the canonical uutils/shadow repository URL
The repository is uutils/shadow; the old shadow-rs name is a redirect. The
README badges already used the new name while package metadata, the --help
footer and SECURITY.md still pointed at the old one.
Addresses Copilot review on PR #27:
1. Renamed is_root() → caller_is_root() using getuid() (real UID)
to correctly identify the actual caller in setuid-root context.
Admin operations (lock/unlock/delete/expire/aging) gate on caller.
Default PAM password change remains accessible to non-root users
changing their own password.
2. Fixed SECURITY.md: removed contradictory "open public issue" text,
replaced with private contact guidance per Copilot suggestion.
3. Removed unused thiserror dep from uu_passwd Cargo.toml.
Found by GitHub Copilot review on PRs #9, #10, #17.
CRITICAL: is_root() used geteuid() which is ALWAYS 0 when passwd is
installed setuid-root. Any unprivileged user could lock/unlock/delete
passwords and skip PAM authentication.
Fix: is_root() now uses getuid() (real UID) to check if the *caller*
is actually root, not just running a setuid binary.
Also:
- get_current_username() uses getuid() (real UID) for same reason
- Removed unused thiserror dependency from uu_passwd
- Fixed SECURITY.md placeholder text