mirror of
https://github.com/uutils/shadow.git
synced 2026-10-16 02:48:14 -07:00
* pwconv, pwunconv, grpconv, grpunconv: convert to and from shadow files
These four complete the set both Debian and Fedora ship: every tool the two
distributions agree on now exists here. pwconv moves password hashes out of
the world-readable /etc/passwd into /etc/shadow, creating it if need be, and
reconciles the two; pwunconv merges them back and removes /etc/shadow;
grpconv and grpunconv do the same for the group files. They are one engine
with a selector, and the other three crates are the selector.
The order of writes is the substance of the change. pwconv writes shadow
before passwd: the hashes are copied into the shadow file and only then
replaced by `x`, so a failure between the two writes leaves them where they
were rather than nowhere. pwunconv writes passwd before it removes shadow:
for a moment the hashes exist twice, which is recoverable, where the other
order would have a moment with none. The removal happens while the lock on
the shadow file is still held, so no other tool can slip in between the last
read of the file and its disappearance.
What pwconv does was established by running the GNU tool:
- A hash found in passwd beside an existing shadow line is the newer of the
two -- it got there through a tool or an edit that did not know about
shadow -- so it replaces the shadow hash and the change is dated; the
aging is kept.
- A passwd line with no shadow line gets one, dated today, with the aging
from PASS_MIN_DAYS, PASS_MAX_DAYS and PASS_WARN_AGE. The hash field is
copied as it stands, an `x` included: an `x` in the hash field matches no
password, which is the honest state of an account whose hash was lost.
- A shadow line for an account no longer in passwd is dropped.
- A consistent system is left byte for byte alone.
A shadow file this run creates is made 0640 root:shadow, the layout the
distributions ship, with the `shadow` group looked up in the tree being
converted rather than through the name service: under --prefix or --root the
host's shadow group says nothing about the target's. Where the tree has no
such group the file stays 0600 root, the atomic writer's default.
The e2e suite uses the other tools as witnesses rather than only reading
files back: after pwunconv and pwconv the hash is byte for byte what it was,
passwd -S still reports the account as having a password, and after
grpunconv and grpconv sg still admits a non-member with the group password
and still refuses a wrong one.
Verified: 878 tests on debian, alpine and fedora; make check clean including
the unprivileged run; 356 e2e assertions against a real install; 51 GNU
comparisons; 31 arm64 assertions under emulation, the round trip included.
* tests: hand children their stdin as a file, not a pipe
The integration binary died on two CI runners with SIGPIPE: "write on a
pipe with no one to read". Not a failing test -- the test runner itself was
killed, part way through, and every test after it never ran.
Two things combine. The batch tools exit as soon as an input line is bad,
so a child fed "staff\n" has closed its stdin within a millisecond of
starting. And the `uumain` that `#[uucore::main]` generates resets SIGPIPE
to its default disposition every time it runs, in-process, for GNU pipeline
compatibility; several tests call `uumain` in-process -- deliberately, where
the tool would exec -- so once any of them has run, the whole test binary
has SIGPIPE at default. From then on a stdin write that lands after the
child has gone is not an EPIPE the helper could handle but a signal that
ends the process. The window is a few microseconds wide, which is why it
showed up on loaded CI runners and never locally.
The helpers now write the input to a file in the prefix tree and hand the
child that file as its stdin. A file has no reader to lose, so the class is
gone regardless of what the signal disposition is at the time. Applied to
chpasswd's helper too, which had the same shape and the same latent hazard.
Fifteen runs of the integration binary at sixteen threads after the change:
no failures. The race could not be reproduced on demand before it, so that
number is not a before-and-after; the diagnosis rests on uucore's source and
on the two CI logs.
389 B
389 B
grpconv(8) - convert to and from shadow passwords and groups
NAME
grpconv - see pwconv(8)
DESCRIPTION
grpconv is one of the four conversion tools described together in pwconv(8): pwconv, pwunconv, grpconv and grpunconv. They are one program with a selector, and share their options, exit codes and order-of-write guarantees.
SEE ALSO
pwconv(8)