This checks the entire shadow(5) 2nd field, which is more than just
a hash.
Reported-by: Tobias Stoeckmann <tobias@stoeckmann.org>
Signed-off-by: Alejandro Colomar <alx@kernel.org>
It represents a passwordless account.
That is discouraged, but accepted.
Fixes: c44f1e096a19 (2025-07-20; "chpasswd: Check hash before write when using -e")
Link: <https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=1124835>
Reported-by: Marc 'Zugschlus' Haber <mh+githubvisible@zugschlus.de>
Reported-by: "Serge E. Hallyn" <serge@hallyn.com>
Reported-by: Adam Williamson <awilliam@redhat.com>
Co-authored-by: "Serge E. Hallyn" <serge@hallyn.com>
Signed-off-by: Alejandro Colomar <alx@kernel.org>
This wasn't only an optimization; it also skipped some checks that were
now spuriously triggering errors. We may be able to get rid of the
optimizations, but that will need more analysis. For now, let's revert
to a known-good state.
Fixes: 6a8a25dc7de6 (2025-10-15; "src/usermod.c: Remove optimizations")
Reverts: 6a8a25dc7de6 (2025-10-15; "src/usermod.c: Remove optimizations")
Closes: <https://github.com/shadow-maint/shadow/issues/1509>
Reported-by: Adam Williamson <awilliam@redhat.com>
Signed-off-by: Alejandro Colomar <alx@kernel.org>
Reproducer:
$ useradd foo
$ grep foo /etc/passwd /etc/shadow
/etc/passwd:foo:x:1001:1001::/home/foo:/usr/bin/bash
/etc/shadow:foo:!:20458:0:99999:7:::
$ usermod -U testuser
usermod: unlocking the user's password would result in a passwordless account.
You should set a password with usermod -p to unlock this user's password.
$ echo $?
0
$ grep foo /etc/passwd /etc/shadow
/etc/passwd:foo:x:1001:1001::/home/foo:/usr/bin/bash
/etc/shadow:foo:!:20458:0:99999:7:::
The program failed (didn't change anything, and reported the problem to
stderr) but reported success (0). After this patch, the error is
reported as E_PASSWORDLESS (20).
Closes: <https://github.com/shadow-maint/shadow/issues/1479>
Reported-by: Tobias Stoeckmann <tobias@stoeckmann.org>
Acked-by: Tobias Stoeckmann <tobias@stoeckmann.org>
Signed-off-by: Alejandro Colomar <alx@kernel.org>
The lrename function follows symlinks when renaming files. Since the
source is a temporary file and the target is the database file itself,
which is opened with O_NOFOLLOW, this function is only useful for an
attacker who manages to win some form of race.
Fixes: 0fa908302660 (2007-10-07; "[svn-upgrade] Integrating new upstream version, shadow (4.0.16)")
Fixes: 391a3847157c (2010-03-04; "2010-01-30 Paweł Hajdan, Jr. <phajdan.jr@gentoo.org>")
Reviewed-by: Alejandro Colomar <alx@kernel.org>
Signed-off-by: Tobias Stoeckmann <tobias@stoeckmann.org>
Since tmpf has been already renamed to target at this point, call utime
with target instead of tmpf.
Fixes: f8732b17dd1d (2026-01-14; "lib/commonio.c: Use unpredictable temporary names")
Reported-by: Alejandro Colomar <alx@kernel.org>
Signed-off-by: Tobias Stoeckmann <tobias@stoeckmann.org>
The fmkomstemp call requires a suffix of XXXXXX for correct operation.
Do so in TCB case as well.
Note: If something fails and the file resides in this directory, it
could be interpreted as a username. Use the ',' character as an illegal
character to prevent shadow tools from erroneously accessing this file
and assuming that the user actually exists.
Fixes: a5b3d56e2902 (2026-01-09; "vipw: Use fmkomstemp for temporary file")
Reported-by: Alejandro Colomar <alx@kernel.org>
Signed-off-by: Tobias Stoeckmann <tobias@stoeckmann.org>
This is a safer approach, which handles cases in which a file would have
less permissions for a group than others.
A rare edge case, but let's be safe than sorry.
Reported-by: Alejandro Colomar <alx@kernel.org>
Reviewed-by: Alejandro Colomar <alx@kernel.org>
Signed-off-by: Tobias Stoeckmann <tobias@stoeckmann.org>
Use file descriptor functions when file descriptor is available, instead
of path based operations. The latter resolve symbolic links and are
prone to race conditions.
Reported-by: Alejandro Colomar <alx@kernel.org>
Reviewed-by: Alejandro Colomar <alx@kernel.org>
Signed-off-by: Tobias Stoeckmann <tobias@stoeckmann.org>
Make sure that enough bytes exist for file name of temporary file which
is used to construct the next database file.
While at it, use a better variable name.
Signed-off-by: Tobias Stoeckmann <tobias@stoeckmann.org>
Make sure that an attacker with sufficient privileges cannot simply
create a file with expected temporary name to retrieve content of
previous and/or future database.
Signed-off-by: Tobias Stoeckmann <tobias@stoeckmann.org>
Accessing and setting shadow_progname is not as straight-forward as it
might seem due to the way of linking libshadow_la with libsubid and
programs.
Enforce the usage of log_get_progname to make this less messy.
With last entry of shadowlog_internal.h gone, remove the file entirely.
Signed-off-by: Tobias Stoeckmann <tobias@stoeckmann.org>
Accessing this variable directly is a recipe for disaster, because
binaries and libraries can have different versions in them due to how
libshadow_la linking is performed.
Make sure that at least NULL check is always performed by calling the
proper getter function.
Signed-off-by: Tobias Stoeckmann <tobias@stoeckmann.org>
Do not call any shadowlog functions directly from program source files
which are also linked with libsubid.
Both, the program and the library, will have their own version of the
static variables within shadowlog.c and thus would have different
logging mechanisms.
Use subid_init instead.
Reviewed-by: Alejandro Colomar <alx@kernel.org>
Signed-off-by: Tobias Stoeckmann <tobias@stoeckmann.org>
Since this is no signal handler anymore, allow regular exit routine to
flush stderr etc.
Reviewed-by: Alejandro Colomar <alx@kernel.org>
Reviewed-by: Ruihan Li <lrh2000@pku.edu.cn>
Signed-off-by: Tobias Stoeckmann <tobias@stoeckmann.org>
The pid_child is never 0 when reaching kill_child, since kill_child
is called within an if-block which checks explicitly for pid_child not
being 0.
Reviewed-by: Alejandro Colomar <alx@kernel.org>
Reviewed-by: Ruihan Li <lrh2000@pku.edu.cn>
Signed-off-by: Tobias Stoeckmann <tobias@stoeckmann.org>
The pid_child can be passed into kill_child, since it is no signal
handler anymore.
Reviewed-by: Alejandro Colomar <alx@kernel.org>
Reviewed-by: Ruihan Li <lrh2000@pku.edu.cn>
Signed-off-by: Tobias Stoeckmann <tobias@stoeckmann.org>
Since kill_child is no signal handler any longer, it is safe to call the
gettext macros directly and only when needed.
Reviewed-by: Alejandro Colomar <alx@kernel.org>
Reviewed-by: Ruihan Li <lrh2000@pku.edu.cn>
Signed-off-by: Tobias Stoeckmann <tobias@stoeckmann.org>
This simplifies the alarm handler to just set a volatile
sig_atomic_t like catch_signals does, which makes the handler way
easier to review.
Reviewed-by: Alejandro Colomar <alx@kernel.org>
Reviewed-by: Ruihan Li <lrh2000@pku.edu.cn>
Signed-off-by: Tobias Stoeckmann <tobias@stoeckmann.org>
Only these shared variables can be safely written to by signal handlers.
Reviewed-by: Alejandro Colomar <alx@kernel.org>
Reviewed-by: Ruihan Li <lrh2000@pku.edu.cn>
Signed-off-by: Tobias Stoeckmann <tobias@stoeckmann.org>
It could happen that, if SIGCHLD was set to SIG_IGN before calling vipw,
the forked child is already gone before SIGCHLD is set to SIG_DFL after
the fork.
Prevent this race condition and also properly set up SIGCHLD for child
handling within the fork, even though system() should take care of that.
Reviewed-by: Alejandro Colomar <alx@kernel.org>
Signed-off-by: Tobias Stoeckmann <tobias@stoeckmann.org>
The reset_selinux flag is always true, so it can be removed.
Remove all functions which are not used anymore as well.
Reviewed-by: Alejandro Colomar <alx@kernel.org>
Signed-off-by: Tobias Stoeckmann <tobias@stoeckmann.org>
This is widely accepted as an invalid hash, to remove password access
for an account (that is, no passwords will match the "hash").
Fixes: c44f1e096a19 (2025-07-20; "chpasswd: Check hash before write when using -e")
Closes: <https://github.com/shadow-maint/shadow/issues/1483>
Closes: <https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=1124835>
Reported-by: Chris Hofstaedtler <zeha@debian.org>
Reviewed-by: Chris Hofstaedtler <zeha@debian.org>
Cc: vinz <mmpx09@protonmail.com>
Signed-off-by: Alejandro Colomar <alx@kernel.org>
A leading '!' means that the account is locked.
Fixes: c44f1e096a19 (2025-07-20; "chpasswd: Check hash before write when using -e")
Link: <https://github.com/shadow-maint/shadow/issues/1483>
Link: <https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=1124835>
Reported-by: Chris Hofstaedtler <zeha@debian.org>
Reviewed-by: Chris Hofstaedtler <zeha@debian.org>
Cc: vinz <mmpx09@protonmail.com>
Signed-off-by: Alejandro Colomar <alx@kernel.org>
If TCB is not in use, the whole configuration section is a stub,
containing no useful information. Make it conditional so it
disappears if TCB is not in use.
Signed-off-by: Tobias Stoeckmann <tobias@stoeckmann.org>
Add comprehensive test for the groupmod -U option when provided with a
list of users to set group membership. This test verifies:
- Setting initial group membership with multiple users
- Proper membership verification in both group and gshadow entries
- Updating group membership by modifying the user list
- Correct handling of membership changes in group databases
Signed-off-by: Iker Pedrosa <ipedrosa@redhat.com>
GShadowEntry administrators and members represent a list of usernames,
not a single string. Thus, set them to `list[str]`. This fixes type
safety and clarifies the expected data structure.
Fixes: 458700b5d670 (2025-09-10; "tests/system/framework/: fix Python linter issues")
Signed-off-by: Iker Pedrosa <ipedrosa@redhat.com>
Breaking changes:
- Remove support for escaped newlines in configuration files.
It never worked correctly.
b0a7ce58b924 (2025-12-05; "lib/, po/: Remove fgetsx() and fputsx()")
- Some user names and group names are too dangerous and are rejected,
even with --badname.
25aea7422615 (2025-12-25; "lib/chkname.c, src/: Strictly disallow really bad names")
Future breaking changes:
- SHA512 and SHA256 will be supported unconditionally in the next
release. The build-time flag '--with-sha-crypt' will be removed.
See <https://github.com/shadow-maint/shadow/pull/1452>.
Support:
- Several years ago, there were talks about deprecating su(1) and
login(1), back when this project was maintained as part of Debian.
However, nothing was clearly stated, and there were doubts about the
status of these programs. Let's clarify them now.
Our implementations of su(1) and login(1) are fully supported, and we
don't have any plans to remove them. They are NOT deprecated.
See <https://github.com/shadow-maint/shadow/issues/464>.
Deprecations:
- groupmems(8)
The program will be removed in a future release.
See <https://github.com/shadow-maint/shadow/issues/1343>.
- logoutd(8)
The program will be removed in the next release.
See <https://github.com/shadow-maint/shadow/issues/999>,
and <https://github.com/shadow-maint/shadow/pull/1344>.
- DES
This hashing algorithm has been deprecated for a long time,
and support for it will be removed in a future release.
See <https://github.com/shadow-maint/shadow/pull/1456>
- MD5
This hashing algorithm has been deprecated for a long time,
and support for it will be removed in a future release.
See <https://github.com/shadow-maint/shadow/pull/1457>
- login.defs(5): MD_CRYPT_ENAB
This feature had been deprecated for decades. It will be
removed in a future release.
The command-line equivalents (-m, --md5) of this feature in
chpasswd(8) and chgpasswd(8) will also be removed in a future
release.
See <https://github.com/shadow-maint/shadow/pull/1455>.
- login.defs(5): PASS_MAX_LEN
This feature is ignored except for DES. Once DES is removed,
it makes no sense keeping it. It may be removed in a future
release.
- Password aging
Scientific research shows that periodic password expiration
leads to predictable password patterns, and that even in a
theoretical scenario where that wouldn't happen the gains in
security are mathematically negligible.
<https://people.scs.carleton.ca/~paulv/papers/expiration-authorcopy.pdf>
Modern security standards, such as NIST SP 800-63B-4 in the USA,
prohibit periodic password expiration.
<https://pages.nist.gov/800-63-4/sp800-63b.html#passwordver>
<https://pages.nist.gov/800-63-FAQ/#q-b05>
<https://www.ncsc.gov.uk/collection/passwords/updating-your-approach#PasswordGuidance:UpdatingYourApproach-Don'tenforceregularpasswordexpiry>
To align with these, we're deprecating the ability to
periodically expire passwords. The specifics and long-term
roadmap are currently being discussed, and we invite feedback
from users, particularly from those in regulated environments.
See <https://github.com/shadow-maint/shadow/pull/1432>.
This deprecation includes the following programs and features:
expiry(1)
chage(1):
-I,--inactive (also the interactive version)
-m,--mindays (also the interactive version)
-M,--maxdays (also the interactive version)
-W,--warndays (also the interactive version)
passwd(1):
-k,--keep-tokens
-n,--mindays
-x,--maxdays
-i,--inactive
-w,--warndays
useradd(8):
-f,--inactive
usermod(8):
-f,--inactive
login.defs(5):
PASS_MIN_DAYS
PASS_MAX_DAYS
PASS_WARN_AGE
/etc/default/useradd:
INACTIVE
shadow(5):
sp_lstchg: Restrict to just the values 0 and empty.
sp_min
sp_max
sp_warn
sp_inact
We recognize that many users operate in environments with
regulatory or contractual requirements that still mandate
password aging. To minimize disruption, these features will
remain functional for a significant period. However, we
encourage administrators to review their internal policies,
talk to their regulators if appropriate, and participate in the
roadmap discussion linked above.
Co-authored-by: Iker Pedrosa <ipedrosa@redhat.com>
Signed-off-by: Alejandro Colomar <alx@kernel.org>
I don't know what this commit does, to be honest. I just
did './autogen.sh && make && make dist' and committed the
changes to .po files. Why? I don't know.
BTW, I kept out some changes that were actually bad.
Signed-off-by: Alejandro Colomar <alx@kernel.org>
I don't know what this commit does, to be honest. I just
did './autogen.sh && make && make dist' and committed the
changes to .pot files. Why? I don't know.
Signed-off-by: Alejandro Colomar <alx@kernel.org>
While the empty one is more correct, {0} will also work, and will
likely silence diagnostics in old compiler versions.
Empty compound literals are only supported in GCC since commit
gcc.git 14cfa01755a6 (2022-08-25; "c: Support C2x empty initializer braces")
Reported-by: Serge Hallyn <serge@hallyn.com>
Signed-off-by: Alejandro Colomar <alx@kernel.org>
Only one opening bracket is used before two closing brackets are
encountered for "(--user)".
Drop redundant ones within the file.
Signed-off-by: Tobias Stoeckmann <tobias@stoeckmann.org>
Use tab instead of spaces to comply with rest of files.
Fixes: 923aeac250d0 (2025-07-04; "man/: update `--root` flag with no SELinux support")
Signed-off-by: Tobias Stoeckmann <tobias@stoeckmann.org>
The usage message of sg and synopsis of its manual page diverged. The
difference was even noted in a comment, instead of fixing it.
Synchronize both, add information about hidden options and document
what they do.
Signed-off-by: Tobias Stoeckmann <tobias@stoeckmann.org>