Compare commits

...

354 Commits

Author SHA1 Message Date
Serge Hallyn
6f5a158b06 man/po: copy over login.defs.d/*.xml
When doing update-po, we copy man/*.xml into a tempdir, but
some of those files reference login.defs.d/*.xml, so copy
over those as well.

Signed-off-by: Serge Hallyn <serge@hallyn.com>
Tested-by: Alejandro Colomar <alx@kernel.org>
2026-01-25 20:36:26 -06:00
Alejandro Colomar
6be13b2f84 man/shadow.5.xml: Document "*"
Signed-off-by: Alejandro Colomar <alx@kernel.org>
2026-01-23 16:42:06 +01:00
Alejandro Colomar
9a86c515a1 lib/chkhash.c: is_valid_hash(): Comment meaning of !hash and *
Signed-off-by: Alejandro Colomar <alx@kernel.org>
2026-01-23 16:42:06 +01:00
Alejandro Colomar
bce404a7a8 lib/chkhash.c: is_valid_hash(): Update comment
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>
2026-01-23 16:42:06 +01:00
Alejandro Colomar
9b67543987 lib/chkhash.c: is_valid_hash(): Accept an empty hash
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>
2026-01-23 16:42:06 +01:00
Iker Pedrosa
0e167e2a02 tests/system/framework/utils/tools.py: apply style fix for tuple unpacking
Signed-off-by: Iker Pedrosa <ipedrosa@redhat.com>
2026-01-23 15:51:42 +01:00
Alejandro Colomar
958b485999 Revert "src/usermod.c: Remove optimizations"
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>
2026-01-19 10:13:52 +01:00
Alejandro Colomar
a49d2acdb4 src/usermod.c: -U: Report E_PASSWORDLESS on error due to passwordless account
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>
2026-01-15 13:16:17 +01:00
Tobias Stoeckmann
b43089bcf1 lib/commonio.c: Drop lrename
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>
2026-01-14 23:56:46 +01:00
Tobias Stoeckmann
6cd3dcbbcc lib/commonio.c: Call utime for correct target
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>
2026-01-14 23:43:01 +01:00
Tobias Stoeckmann
65668fe093 vipw: Set correct pattern for fmkomstemp
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>
2026-01-14 23:41:40 +01:00
Tobias Stoeckmann
f4777ac542 vipw: Set ownership, then mode
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>
2026-01-14 20:56:24 +01:00
Tobias Stoeckmann
976b7bffde vipw: Prefer fchmod/fchown over chmod/chown
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>
2026-01-14 20:56:24 +01:00
Tobias Stoeckmann
c745eea4a4 lib/commonio.c: Larger buffer size for file names
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>
2026-01-14 13:05:45 +01:00
Tobias Stoeckmann
f8732b17dd lib/commonio.c: Use unpredictable temporary names
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>
2026-01-14 13:05:45 +01:00
Tobias Stoeckmann
a472091869 lib/commonio.c: Unlink backup file on error
It doesn't make sense to keep a file around if it's not even a proper
backup.

Signed-off-by: Tobias Stoeckmann <tobias@stoeckmann.org>
2026-01-14 13:05:45 +01:00
Tobias Stoeckmann
d5638a5b2b lib/shadowlog_internal.h: Hide shadow_progname
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>
2026-01-12 14:27:05 +01:00
Tobias Stoeckmann
ecaae2f8cd lib/shadowlog_internal.h: Drop shadow_logfd
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>
2026-01-12 14:27:05 +01:00
Tobias Stoeckmann
828f465cd2 libsubid: Avoid shadow_logfd as variable name
A global shadow_logfd variable exists, so this effectively shadows it.

Signed-off-by: Tobias Stoeckmann <tobias@stoeckmann.org>
2026-01-12 14:27:05 +01:00
Iker Pedrosa
eec97ce4c5 Remove unused logoutd utility
Link: <https://github.com/shadow-maint/shadow/issues/999>
Signed-off-by: Iker Pedrosa <ipedrosa@redhat.com>
2026-01-12 13:27:48 +01:00
Tobias Stoeckmann
f051423a5a src/: Properly set up libsubid tools
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>
2026-01-12 01:56:27 +01:00
Tobias Stoeckmann
bd5fadaf29 su: Use exit instead of _exit in kill_child
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>
2026-01-11 15:46:59 +01:00
Tobias Stoeckmann
268961e044 su: Mark kill_child with NORETURN
Reviewed-by: Alejandro Colomar <alx@kernel.org>
Reviewed-by: Ruihan Li <lrh2000@pku.edu.cn>
Signed-off-by: Tobias Stoeckmann <tobias@stoeckmann.org>
2026-01-11 15:46:59 +01:00
Tobias Stoeckmann
cb31772d7f su: Remove dead code
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>
2026-01-11 15:46:59 +01:00
Tobias Stoeckmann
67bcd85b77 su: Turn pid_child into local variable
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>
2026-01-11 15:46:59 +01:00
Tobias Stoeckmann
2852d7aeed su: Drop unneeded char buffers
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>
2026-01-11 15:46:59 +01:00
Tobias Stoeckmann
b42b1cc9b9 su: Kill child outside of signal handler
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>
2026-01-11 15:46:59 +01:00
Tobias Stoeckmann
8800423a8a su: Turn caught into volatile sig_atomic_t
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>
2026-01-11 15:46:59 +01:00
Tobias Stoeckmann
df90ed8a43 su: Fix typos in comment
Reviewed-by: Alejandro Colomar <alx@kernel.org>
Reviewed-by: Ruihan Li <lrh2000@pku.edu.cn>
Signed-off-by: Tobias Stoeckmann <tobias@stoeckmann.org>
2026-01-11 15:46:59 +01:00
Tobias Stoeckmann
e37e43fd0a lib/motd.c: Remove unused include
Signed-off-by: Tobias Stoeckmann <tobias@stoeckmann.org>
2026-01-11 03:05:31 +01:00
Tobias Stoeckmann
69a8862c27 vipw: set SIGCHLD before fork
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>
2026-01-09 14:15:18 +01:00
Tobias Stoeckmann
a5b3d56e29 vipw: Use fmkomstemp for temporary file
Avoid fixed and thus predictable temporary file names. Especially avoid
just opening already existing ones.

Reviewed-by: Alejandro Colomar <alx@kernel.org>
Signed-off-by: Tobias Stoeckmann <tobias@stoeckmann.org>
2026-01-09 14:13:59 +01:00
Tobias Stoeckmann
413c4908c8 Fix typos
Typos found with codespell

Signed-off-by: Tobias Stoeckmann <tobias@stoeckmann.org>
2026-01-08 22:28:13 +01:00
Tobias Stoeckmann
78120f17df configure.ac: Drop libattr linking
The libattr dependency is no longer needed.

Reviewed-by: Alejandro Colomar <alx@kernel.org>
Signed-off-by: Tobias Stoeckmann <tobias@stoeckmann.org>
2026-01-08 09:12:02 +01:00
Tobias Stoeckmann
c8b7b5ef0d lib/copydir.c: Drop reset_selinux
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>
2026-01-08 09:12:02 +01:00
Alejandro Colomar
ddc2549f87 lib/chkhash.c: is_valid_hash(): Accept '*' as the hash
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>
2026-01-07 20:01:02 -06:00
Alejandro Colomar
87ec7a52ab lib/chkhash.c: is_valid_hash(): Accept a leading '!'
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>
2026-01-07 20:01:02 -06:00
Tobias Stoeckmann
6d983709fa man/chage.1: Drop empty configuration section
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>
2026-01-05 19:24:26 +01:00
Tobias Stoeckmann
01f705ad50 man/groupmems: Fix grammar
Signed-off-by: Tobias Stoeckmann <tobias@stoeckmann.org>
2026-01-04 18:37:36 +01:00
Hadi Chokr
3e8c105f07 src/useradd: Support config for creating home dirs as Btrfs subvolumes
Closes: #1162

Co-authored-by: Neal Gompa <ngompa@velocitylimitless.com>
Signed-off-by: Hadi Chokr <hadichokr@icloud.com>
Signed-off-by: Neal Gompa <ngompa@velocitylimitless.com>
Reviewed-by: Alejandro Colomar <alx@kernel.org>
2026-01-02 23:52:59 +01:00
Iker Pedrosa
a0d5a6165d tests/system/tests/test_groupmod.py: add test for groupmod -U with user list
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>
2026-01-01 15:44:52 +01:00
Iker Pedrosa
ce9e598fac tests/system/framework/utils/tools.py: fix GShadowEntry
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>
2026-01-01 15:44:52 +01:00
Alejandro Colomar
d7ce7e868e
Release 4.19.0
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>
2025-12-30 17:11:54 +01:00
Alejandro Colomar
8718efad46
po/: Update
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>
2025-12-30 17:11:54 +01:00
Alejandro Colomar
15b548a0e1
po/shadow.pot: Update
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>
2025-12-30 17:11:54 +01:00
Alejandro Colomar
9214a8e44d lib/: Use non-empty compound literals
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>
2025-12-30 10:07:45 -06:00
Serge Hallyn
eef769836f Add ubuntu-22.04 to the CI test matrix
we need to check against older compilers.

Signed-off-by: Serge Hallyn <serge@hallyn.com>
2025-12-30 17:00:49 +01:00
Tobias Stoeckmann
e04a87a6d2 man/lastlog.8: Drop redundant closing brackets
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>
2025-12-30 14:13:36 +01:00
Tobias Stoeckmann
304e32de4b man/: Fix SELinux note formatting
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>
2025-12-30 14:13:01 +01:00
Tobias Stoeckmann
47be361b86 sg: Properly document synopsis and options
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>
2025-12-29 23:49:00 +01:00
Tobias Stoeckmann
55fc8efd09 sg: group is not optional
The group argument is not optional, so do not specify it as such in the
manual page.

Signed-off-by: Tobias Stoeckmann <tobias@stoeckmann.org>
2025-12-29 23:49:00 +01:00
Tobias Stoeckmann
dc2cbf9ec4 sg: Fix grammar in documentation
Use "an" in front of sg due to its pronounciation. Also, start a comment
with capital letter in its first sentence to comply with other comments.

No functional change.

Signed-off-by: Tobias Stoeckmann <tobias@stoeckmann.org>
2025-12-29 23:49:00 +01:00
Alejandro Colomar
ee7fa1dfc6 lib/, src/: Reduce scope of local variables
Signed-off-by: Alejandro Colomar <alx@kernel.org>
2025-12-28 21:19:22 -06:00
Alejandro Colomar
0ff87bf37a lib/, src/: Some empty lists have 0 elements, not 1 empty string
In general, empty fields in a CSV are errors.  However, in some cases,
we want to allow passing empty lists, and the way to encode that is as
an empty string.  This was accidentally broken in 4.17.0, when we
switched from using strtok(3) to strsep(3), without remembering to
special-case an empty CSV.

The bug affected directly groupadd(8) and groupmod(8).

The bug also affected the library function add_groups().  In systems
using PAM, that function is unused.  On systems without PAM, it is
called by the library function setup_uid_gid(), with the contents of the
"CONSOLE_GROUPS" configuration (login.defs) CSV string.

setup_uid_gid() is directly called by su(1) and login(1) on systems
without PAM.

setup_uid_gid() is also called by the library function expire().

expire() is directly called by expiry(1), su(1), and login(1).

This bug is a regression introduced in the release 4.17.0, and present
in the releases 4.17.{0..4} and 4.18.0.

Fixes: 90afe61003ef (2024-12-05; "lib/, src/: Use strsep(3) instead of strtok(3)")
Link: <https://github.com/shadow-maint/shadow/issues/1420>
Reported-by: Osark Vieira <https://github.com/osark084>
Signed-off-by: Alejandro Colomar <alx@kernel.org>
2025-12-28 21:19:22 -06:00
Alejandro Colomar
977d76760b lib/, src/: Add blocks
This is in preparation for the following patch.

Signed-off-by: Alejandro Colomar <alx@kernel.org>
2025-12-28 21:19:22 -06:00
Iker Pedrosa
07d9c2da21 tests/system/tests/test_groupadd.py: add test for groupadd -U with empty string
Test verifies that groupadd -U '' correctly creates group with no
members.

Signed-off-by: Iker Pedrosa <ipedrosa@redhat.com>
2025-12-28 21:08:51 -06:00
Iker Pedrosa
02be30544e tests/system/tests/test_groupmod.py: add test for groupmod -U with empty string
Test verifies that groupmod -U '' correctly clears group membership.

Signed-off-by: Iker Pedrosa <ipedrosa@redhat.com>
2025-12-28 21:08:51 -06:00
Alejandro Colomar
25aea74226 lib/chkname.c, src/: Strictly disallow really bad names
Some names are bad, and some names are really bad.  '--badname' should
only allow the mildly bad ones, which we can handle.  Some names are too
bad, and it's not possible to deal with them.  Reject them
unconditionally.

-  A leading '-' is too dangerous.  It breaks things like execve(2), and
   almost every command.

-  Spaces are used for delimiting lists of users and groups.

-  '"' is special in many languages, including the shell.  Having it in
   user names would be unnecessarily dangerous.

-  '#' is used for delimiting comments in several of our config files.
   Having it in usernames could result in incorrect configuration files.

-  "'" is special in many languages, including the shell.  Having it in
   user names would be unnecessarily dangerous.

-  ',' is used for delimiting lists of users and groups.

-  '/' is used for delimiting files, and thus could result in incorrect
   handling of users and groups.

-  ':' is the main delimiter in /etc/shadow and /etc/passwd.

-  ';' is special in many languages, including the shell.  Having it in
   user names would be unnecessarily dangerous.

There are other characters that we should disallow, but they need more
research to make sure we don't introduce regressions.  This set should
be less problematic.

Acked-by: Tobias Stoeckmann <tobias@stoeckmann.org>
Reviewed-by: Iker Pedrosa <ipedrosa@redhat.com>
Reviewed-by: Chris Hofstaedtler <zeha@debian.org>
Cc: Marc 'Zugschlus' Haber <mh+githubvisible@zugschlus.de>
Cc: Serge Hallyn <serge@hallyn.com>
Signed-off-by: Alejandro Colomar <alx@kernel.org>
2025-12-25 22:40:55 -06:00
Tobias Stoeckmann
9f8cf33704 userdel: Fix syslog message
Actually log the user name as done with stderr message.

Reviewed-by: Alejandro Colomar <alx@kernel.org>
Signed-off-by: Tobias Stoeckmann <tobias@stoeckmann.org>
2025-12-24 14:57:37 +01:00
Tobias Stoeckmann
f6d9e5c019 lib/run_part.c: Fix error message
The failing function call was wait, not waitpid.

Reviewed-by: Alejandro Colomar <alx@kernel.org>
Signed-off-by: Tobias Stoeckmann <tobias@stoeckmann.org>
2025-12-24 14:57:37 +01:00
Tobias Stoeckmann
cf90975d11 chage: Remove unneeded xstrdup calls
Duplicating name and hash is not needed here, because duplication
occurs in spw_update. You can detect the small memory leak with
tools like valgrind.

More importantly though, if xstrdup fails, it calls exit. The
update_age function is in the "criticial section" between
open_files and close_files, though. Correct error handling would
require fail_exit to release the held locks.

Signed-off-by: Tobias Stoeckmann <tobias@stoeckmann.org>
2025-12-23 22:45:19 +01:00
Iker Pedrosa
16a388c644 src/gpasswd.c: fix segfault in clean up callbacks
The gpasswd utility was segfaulting when cleanup functions were called
because these functions expect a pointer to `process_selinux` but was
being passed NULL. This caused a NULL pointer dereference.

This commits adds the pointer to `process_selinux` to clean up
functions making `gpasswd` consistent with other group utilities.

Reproduction steps:
$ useradd tuser
$ groupadd tuser
$ gpasswd -a tuser tgroup
Adding user tuser to group tgroup
Segmentation fault (core dumped)

Fixes: 4d431898bad8 (2025-10-07; "src/gpasswd.c: chroot or prefix SELinux file context")
Signed-off-by: Iker Pedrosa <ipedrosa@redhat.com>
2025-12-22 18:55:33 +01:00
Tobias Stoeckmann
fa88997719 lib/obscure.c: Introduce obscure_get_range
Unify the retrieval of PASS_MIN_LEN and PASS_MAX_LEN for output
in passwd and actual checks.

Fixes wrong output for minimum password lengths if no such
restriction is configured: 5 is printed, 0 is in effect.

How to reproduce:

1. Use passwd compiled without PAM support
2. Do not specify PASS_MIN_LEN in login.defs
3. Run passwd as a user and enter your old password, then
   - you will see that 5 characters are expected
   - you can just press enter twice

Signed-off-by: Tobias Stoeckmann <tobias@stoeckmann.org>
2025-12-20 20:22:50 +01:00
Tobias Stoeckmann
deb192fe78 lib/obscure.c: Fix PASS_MIN_LEN -1 case
The getdef_num implementation allows -1 to be specified in login.defs.
In general, -1 should be treated the same way as "not specified". In
this case, casting -1 to size_t leads to every password being "too
short."

Reviewed-by: Alejandro Colomar <alx@kernel.org>
Signed-off-by: Tobias Stoeckmann <tobias@stoeckmann.org>
2025-12-20 20:22:50 +01:00
Tobias Stoeckmann
7e34e2d06a gshadow: Open audit socket after check_fds
Protect gshadow the same way as newgrp.

Signed-off-by: Tobias Stoeckmann <tobias@stoeckmann.org>
2025-12-20 19:07:33 +01:00
Tobias Stoeckmann
ff555732f8 newgrp, sg: Open audit socket after check_fds
The check_fds function is supposed to ensure that fds 0, 1, and 2 are
opened in a well-defined state, i.e. either they are already connected
to supposed input/output files or will be connected to /dev/null if not.

Opening the audit socket before checking the fds allows the audit socket
to get one of these numbers.

Avoid this by opening the audit socket after the check.

In general, this check is already covered by system libraries, but this
proof of concept works for root user. Note the different states of the
file descriptor 2.

In bash or another shell that interprets `2>&-` as closing stderr with
shadow + audit support, e.g. Arch Linux:
```
sg bin 'ls -l /proc/self/fd'
sg bin 'ls -l /proc/self/fd' 2>/dev/null
sg bin 'ls -l /proc/self/fd' 2>&-
```

Signed-off-by: Tobias Stoeckmann <tobias@stoeckmann.org>
2025-12-20 19:07:33 +01:00
Tobias Stoeckmann
b236090cd0 etc/login.defs: Clarify PASS_MAX_LEN usage
The PASS_MAX_LEN option is only used with DES.

Signed-off-by: Tobias Stoeckmann <tobias@stoeckmann.org>
2025-12-19 17:01:11 +01:00
Tobias Stoeckmann
7255cd93a6 PASS_MAX_LEN.xml: Explain PASS_MAX_LEN use cases
The `PASS_MAX_LEN` is effectively only used for DES. Do not describe it
in a way that makes it sound like `MD_CRYPT_ENAB=yes` is required to
disable it. Any other `ENCRYPT_METHOD` disables it as well.

Also, even for DES, `PASS_MAX_LEN` requires `OBSCURE_CHECKS_ENAB` to
have any effect.

Even more, `PASS_MIN_LEN` and `PASS_MAX_LEN` are only used for
user passwords. Group passwords are not checked.

Note: All of this is actually true even if compiled with PAM if command
line arguments change root. But if compiled with PAM support, this
section is not added to manual pages... Since this is true for some
more files, it's not part of this commit.

Link to source files:
- lib/obscure.c line 133 stops further checks, including max length,
  if OBSCURE_CHECS_ENAB is not yes
- lib/obscure.c line 172 is only reached in case of DES
- src/passwd.c line 248 duplicates the check for output
- src/gpasswd.c has no reference to obscure

Signed-off-by: Tobias Stoeckmann <tobias@stoeckmann.org>
2025-12-19 17:01:11 +01:00
Tobias Stoeckmann
734fe78a74 man/passwd.5.xml: Use sentences for descriptions
Even though this is technically no sentence, it stays in sync with the
other file descriptions this way.

Signed-off-by: Tobias Stoeckmann <tobias@stoeckmann.org>
2025-12-19 16:48:08 +01:00
Tobias Stoeckmann
bbdfa2d66b tests: Fix typos in comments
No functional change

Signed-off-by: Tobias Stoeckmann <tobias@stoeckmann.org>
2025-12-19 16:48:08 +01:00
Tobias Stoeckmann
876e874684 Fix documentation style
Always start a sentence with lowercase letter after 'Note:', 'Warning:',
etc. This unifies all occurrences.

No functional change.

Signed-off-by: Tobias Stoeckmann <tobias@stoeckmann.org>
2025-12-19 16:48:08 +01:00
Tobias Stoeckmann
1031f5a0fb lib/, src/: Drop 10000 as infinity
If password aging should not be performed, disable it properly. Just
specifying a "long enough time" is not infinity.

Signed-off-by: Tobias Stoeckmann <tobias@stoeckmann.org>
2025-12-19 15:21:49 +01:00
Tobias Stoeckmann
07594bf4d0 pwunconv: Drop incorrect comment
Shadow password files do not necessarily need aging information.
Also, passwd has no aging information.

No conversion is performed, so drop the comment entirely.

Signed-off-by: Tobias Stoeckmann <tobias@stoeckmann.org>
2025-12-19 15:21:49 +01:00
Tobias Stoeckmann
d349e1ab7d passwd: Clarify password strength check comment
- The total number of password change tries can be configured
- Except min length, password strength checks can be disabled
- Even the root user can have password strength checks...
- ... except in some cases (stdin, command line arguments)

In general, this code does not run for PAM, except root directory
is modified through command line arguments by root user.

Signed-off-by: Tobias Stoeckmann <tobias@stoeckmann.org>
2025-12-19 15:13:27 +01:00
Tobias Stoeckmann
f34d12d523 ENCRYPT_METHOD.xml: Fix grammar
Use singular in these cases.

Signed-off-by: Tobias Stoeckmann <tobias@stoeckmann.org>
2025-12-19 14:57:13 +01:00
Tobias Stoeckmann
5369ad7566 lib/, src/: Fix formatting
Fix places where spaces were used instead of tabs.

No functional change.

Signed-off-by: Tobias Stoeckmann <tobias@stoeckmann.org>
2025-12-16 23:20:32 +01:00
Tobias Stoeckmann
f787a0c7d5 passwd: Fix TOCTOU race condition (no PAM)
The passwd tool checks if the password of a user may be changed before
locking the passwd/shadow files. This leaves a time window to perform
the same action twice (e.g. circumventing PASS_MIN_DAYS limit) or to
circumvent a locked password by an administrator.

Perform the check after the lock again. This keeps the behavior as it
is today for a user and also prevents the race condition.

Reviewed-by: Alejandro Colomar <alx@kernel.org>
Signed-off-by: Tobias Stoeckmann <tobias@stoeckmann.org>
2025-12-16 15:10:24 +01:00
Tobias Stoeckmann
27a69577f9 passwd: Use fail_exit in check_password
Calling fail_exit here prepares an upcoming commit to reuse the
functions when databases have been locked.

Reviewed-by: Alejandro Colomar <alx@kernel.org>
Signed-off-by: Tobias Stoeckmann <tobias@stoeckmann.org>
2025-12-16 15:10:24 +01:00
Tobias Stoeckmann
d37f63a2b3 passwd: check_password: Use shadow entry for logs
Always use the name in shadow entry for logging. This reduces the
amount of data retrieved from password entry to bare minimum, i.e.
passing through into library call.

Reviewed-by: Alejandro Colomar <alx@kernel.org>
Signed-off-by: Tobias Stoeckmann <tobias@stoeckmann.org>
2025-12-16 15:10:24 +01:00
Tobias Stoeckmann
5719f1ec53 passwd: Unify (un)locking routines
Make sure that passwd and shadow are always opened in the correct
order to avoid possible dead locks with other tools:

- Lock passwd first, then shadow
- Unlock shadow first, then passwd

The passwd utility may work without a shadow entry. In that case, it
operates on the passwd file. But to figure this out, the shadow file
must have been opened and thus locked already. Unconditionally open the
passwd file first, even though it's not needed most of the time.

Reviewed-by: Alejandro Colomar <alx@kernel.org>
Signed-off-by: Tobias Stoeckmann <tobias@stoeckmann.org>
2025-12-16 13:22:43 +01:00
Tobias Stoeckmann
969698a55d passwd: Call fail_exit on pwd lock failure
At this point, shadow might be already locked if update_noshadow is
called as fallback within update_shadow. Make sure that unlock is
called before exit.

Fixes: 45c6603cc86c (2007-10-07; "[svn-upgrade] Integrating new upstream version, shadow (19990709)")
Reviewed-by: Alejandro Colomar <alx@kernel.org>
Signed-off-by: Tobias Stoeckmann <tobias@stoeckmann.org>
2025-12-16 13:22:43 +01:00
Tobias Stoeckmann
1229582881 passwd: Fix style
No functional change

Reviewed-by: Alejandro Colomar <alx@kernel.org>
Signed-off-by: Tobias Stoeckmann <tobias@stoeckmann.org>
2025-12-16 13:22:43 +01:00
Tobias Stoeckmann
cb1f101d78 passwd: Pass process_selinux flag directly
No need to re-evaluate option_flags in functions. Unifies checks and
simplifies code.

Reviewed-by: Alejandro Colomar <alx@kernel.org>
Signed-off-by: Tobias Stoeckmann <tobias@stoeckmann.org>
2025-12-15 16:52:15 +01:00
Tobias Stoeckmann
d5c03d66c0 newusers: Allow creation without aging information
If PASS_MAX_DAYS is not set, newusers falls back to 10000 days, which is
considered "unlimited" in some parts of the source tree. All other tools
fall back to -1, which truely implies unlimited.

Sync newusers with all other shadow tools.

How to reproduce:

1. Remove or comment out PASS_MAX_DAYS from /etc/login.defs
2. Run `newusers <<< user:pass:1234🔢:/home/user:/bin/bash`
3. Check user line in /etc/shadow
```
/etc/shadow:user:HASH:19721:0:10000:7:::
```

Max days are set to 10000. Instead, this should be:

```
/etc/shadow:user:HASH:19721:0::7:::
```

Reviewed-by: Alejandro Colomar <alx@kernel.org>
Signed-off-by: Tobias Stoeckmann <tobias@stoeckmann.org>
2025-12-15 14:51:28 +01:00
Tobias Stoeckmann
fd4a810504 newusers: Add test for creation without aging
If PASS_MAX_DAYS is not set, newusers should not set max password age.

Signed-off-by: Tobias Stoeckmann <tobias@stoeckmann.org>
2025-12-15 14:51:28 +01:00
Tobias Stoeckmann
7f59cb5ab3 lib/pwd2spwd.c: Set no aging information
The pwd_to_spwd routine claims that fields without corresponding
information in the password file are set to uninitialized values,
but sets some aging information. These cannot be available in
struct passwd.

Also, the code is only used in passwd to temporarily hold the
new password. All other values are copied from an existing entry
later on. If no entry exists, all values are dismissed anyway.

Clarify that everything is uninitialized except name and password.
Gets rid of magic value 10000 for sp_max.

Signed-off-by: Tobias Stoeckmann <tobias@stoeckmann.org>
2025-12-15 11:38:34 +01:00
Tobias Stoeckmann
d82cdfaf4c lib/pwd2spwd.c: Simplify style
No need for a nested block here.

Signed-off-by: Tobias Stoeckmann <tobias@stoeckmann.org>
2025-12-15 11:38:34 +01:00
Mike Gilbert
f973c406c5 lib/xgetXXbyYY.c: include stdint.h for SIZE_MAX
Fixes build failure:
```
In file included from xgetgrnam.c:40:
xgetXXbyYY.c: In function ‘xgetgrnam’:
xgetXXbyYY.c:83:31: error: ‘SIZE_MAX’ undeclared (first use in this function)
   83 |                 if (length == SIZE_MAX) {
      |                               ^~~~~~~~
```

Signed-off-by: Mike Gilbert <floppym@gentoo.org>
2025-12-13 21:46:32 +01:00
Iker Pedrosa
e78742e553 src/chsh.c: add argument for fail_exit()
Fixes: 374ca05c282f (2025-10-07; "src/chsh.c: SELinux file context for fail_exit()")
Reported-by: Michael Vetter <jubalh@iodoru.org>
Signed-off-by: Iker Pedrosa <ipedrosa@redhat.com>
2025-12-10 13:32:41 +01:00
Frans Spiesschaert
0598c7828d updated Dutch translation 2025-12-09 14:37:24 +01:00
Serge Hallyn
5a5b776bd3 (pre-)Release 4.19.0-rc1
First pre-release of 'herve', 4.19.0

Signed-off-by: Serge Hallyn <serge@hallyn.com>
2025-12-07 10:08:19 -06:00
Alejandro Colomar
6a2e15c73b lib/tcbfuncs.c: rmdir_leading(): Create string just once
Signed-off-by: Alejandro Colomar <alx@kernel.org>
2025-12-06 22:38:06 -06:00
Alejandro Colomar
4cf430b309 lib/tcbfuncs.c: rmdir_leading(): Constify input
Signed-off-by: Alejandro Colomar <alx@kernel.org>
2025-12-06 22:38:06 -06:00
Alejandro Colomar
e7771cc196 lib/search/: lsearch_T(): Don't return anything
We don't use this value.  This silences a diagnostic about the unused
return value.

Signed-off-by: Alejandro Colomar <alx@kernel.org>
2025-12-05 21:22:45 -06:00
Alejandro Colomar
e3c217ec9e lib/: Rename XCALLOC() => xcalloc_T()
The 'T' in the name notes that this API is a type-safe variant of the
API it wraps.  This makes the names more explicative.

Signed-off-by: Alejandro Colomar <alx@kernel.org>
2025-12-05 21:22:45 -06:00
Alejandro Colomar
bc4e3712a6 lib/: Rename CALLOC() => calloc_T()
The 'T' in the name notes that this API is a type-safe variant of the
API it wraps.  This makes the names more explicative.

Signed-off-by: Alejandro Colomar <alx@kernel.org>
2025-12-05 21:22:45 -06:00
Alejandro Colomar
0d9193dcf6 lib/: Rename XMALLOC() => xmalloc_T()
The 'T' in the name notes that this API is a type-safe variant of the
API it wraps.  This makes the names more explicative.

Signed-off-by: Alejandro Colomar <alx@kernel.org>
2025-12-05 21:22:45 -06:00
Alejandro Colomar
0afe216953 lib/: Rename MALLOC() => malloc_T()
The 'T' in the name notes that this API is a type-safe variant of the
API it wraps.  This makes the names more explicative.

Signed-off-by: Alejandro Colomar <alx@kernel.org>
2025-12-05 21:22:45 -06:00
Alejandro Colomar
01a0aac628 lib/: Rename XREALLOC() => xrealloc_T()
The 'T' in the name notes that this API is a type-safe variant of the
API it wraps.  This makes the names more explicative.

Signed-off-by: Alejandro Colomar <alx@kernel.org>
2025-12-05 21:22:45 -06:00
Alejandro Colomar
e010eb2d4e lib/: Rename REALLOC() => realloc_T()
The 'T' in the name notes that this API is a type-safe variant of the
API it wraps.  This makes the names more explicative.

Signed-off-by: Alejandro Colomar <alx@kernel.org>
2025-12-05 21:22:45 -06:00
Alejandro Colomar
3de69c9668 lib/, src/: Rename REALLOCF() => reallocf_T()
The 'T' in the name notes that this API is a type-safe variant of the
API it wraps.  This makes the names more explicative.

Signed-off-by: Alejandro Colomar <alx@kernel.org>
2025-12-05 21:22:45 -06:00
Alejandro Colomar
d0a0c35cd8 lib/alloc/: REALLOC[F](): Move _Generic(3) to separate line
This should be more readable.

Signed-off-by: Alejandro Colomar <alx@kernel.org>
2025-12-05 21:22:45 -06:00
Alejandro Colomar
9705effba5 lib/: Use compound literals to avoid casts
Casts are unsafe.

Compound literals also have the ability of converting values, but they
don't have the unwanted effects on safety --casts disable most useful
diagnostics--.

Compound literals are lvalues, which means their address can be taken,
and they can also be assigned to.  To avoid this, we force lvalue
conversion through a statement expression.

Signed-off-by: Alejandro Colomar <alx@kernel.org>
2025-12-05 21:22:45 -06:00
Alejandro Colomar
882fbdf99b lib/alloc/: reallocarray[f]_(): Add helper macros to handle n?:1
Signed-off-by: Alejandro Colomar <alx@kernel.org>
2025-12-05 21:22:45 -06:00
Alejandro Colomar
5e30a34a93 lib/gshadow.c: fgetsgent(): Don't use static variables
BTW, getline(3) says we are responsible for free(3)ing the buffer on
error.

Reported-by: Chris Hofstaedtler <zeha@debian.org>
Signed-off-by: Alejandro Colomar <alx@kernel.org>
2025-12-05 19:23:18 -06:00
Alejandro Colomar
c6178b722f lib/: Use getline(3) instead of its pattern
Signed-off-by: Alejandro Colomar <alx@kernel.org>
2025-12-05 19:23:18 -06:00
Alejandro Colomar
b0a7ce58b9 lib/, po/: Remove fgetsx() and fputsx()
It seems they never worked correctly.  Let's keep it simple and remove
support for escaped newlines.

Closes: <https://github.com/shadow-maint/shadow/issues/1055>
Reported-by: Chris Hofstaedtler <zeha@debian.org>
Signed-off-by: Alejandro Colomar <alx@kernel.org>
2025-12-05 19:23:18 -06:00
Iker Pedrosa
73895aa11b tests/system/tests/test_newgrp.py: change to new group
Change to a new group using `newgrp`.

Signed-off-by: Iker Pedrosa <ipedrosa@redhat.com>
2025-12-05 16:35:36 -06:00
Iker Pedrosa
3ac5c8a320 tests/system/framework/roles/shadow.py: implement binding for newgrp
Signed-off-by: Iker Pedrosa <ipedrosa@redhat.com>
2025-12-05 16:35:36 -06:00
Iker Pedrosa
dfa2697442 share/ansible/: Make sure expect is found in Alpine
Signed-off-by: Iker Pedrosa <ipedrosa@redhat.com>
2025-12-05 16:35:36 -06:00
Iker Pedrosa
cbde9b27c5 share/ansible/: install expect package
Signed-off-by: Iker Pedrosa <ipedrosa@redhat.com>
2025-12-05 16:35:36 -06:00
Iker Pedrosa
06df19464c tests/system/tests/test_groupmems.py: add user to group as root user
This is the transformation to Python of the test located in
`tests/grouptools/groupmems/01_groupmems_root_add_user/groupmems.test`,
which checks that `groupmems` is able to add a user to a group running
as the root user.

Signed-off-by: Iker Pedrosa <ipedrosa@redhat.com>
2025-12-05 16:35:36 -06:00
Iker Pedrosa
79d1e36384 tests/system/framework/roles/shadow.py: implement binding for groupmems
Signed-off-by: Iker Pedrosa <ipedrosa@redhat.com>
2025-12-05 16:35:36 -06:00
Iker Pedrosa
67f0c6a9cd share/ansible/: create the groupmems PAM service file
Temporary workaround to create the groupmems PAM service file. Fedora
will soon provide this service file and we'll be able to remove the
workaround.

Signed-off-by: Iker Pedrosa <ipedrosa@redhat.com>
2025-12-05 16:35:36 -06:00
Iker Pedrosa
6ad9c56b0d tests/system/tests/test_newusers.py: create multiple users using file input
This is the transformation to Python of the test located in
`tests/newusers/20_multiple_users/newusers.test`, which checks that
`newusers` is able to create new users from file.

Signed-off-by: Iker Pedrosa <ipedrosa@redhat.com>
2025-12-05 16:35:36 -06:00
Iker Pedrosa
32399e9584 tests/system/tests/test_newusers.py: create multiple users from stdin
This is the transformation to Python of the test located in
`tests/newusers/35_read_from_stdin/newusers.test`, which checks that
`newusers` is able to create new users from stdin.

Signed-off-by: Iker Pedrosa <ipedrosa@redhat.com>
2025-12-05 16:35:36 -06:00
Iker Pedrosa
b3d63ebabf tests/system/framework/roles/shadow.py: implement binding for newusers
Signed-off-by: Iker Pedrosa <ipedrosa@redhat.com>
2025-12-05 16:35:36 -06:00
Iker Pedrosa
193e351afc share/ansible/: create the newusers PAM service file
Temporary workaround to create the newusers PAM service file. Fedora
will soon provide this service file and we'll be able to remove the
workaround.

Signed-off-by: Iker Pedrosa <ipedrosa@redhat.com>
2025-12-05 16:35:36 -06:00
Alejandro Colomar
8a5d35cca4 lib/search/: Use (void)0 within _Generic(3) to avoid -Wunused-value diagnostics
Signed-off-by: Alejandro Colomar <alx@kernel.org>
2025-12-05 09:21:50 -06:00
Alejandro Colomar
40be785f8a lib/sssd.h: sssd_flush_cache(): Define as static inline function
Being a macro, the unused return value triggers a diagnostic.
This probably needs further investigation, but we have the issue linked
below for that.

Link: <https://github.com/shadow-maint/shadow/issues/1221>
Reviewed-by: Serge Hallyn <serge@hallyn.com>
Signed-off-by: Alejandro Colomar <alx@kernel.org>
2025-12-05 09:21:50 -06:00
Alejandro Colomar
e91d31a97f autogen.sh: CFLAGS: Promose some -Wunused-* to an error
Reviewed-by: Serge Hallyn <serge@hallyn.com>
Signed-off-by: Alejandro Colomar <alx@kernel.org>
2025-12-05 09:21:50 -06:00
Alejandro Colomar
01db0fa627 lib/prefix_flag.c: Add [[gnu::unused]] to variable used in conditionally-compiled code
Reviewed-by: Serge Hallyn <serge@hallyn.com>
Signed-off-by: Alejandro Colomar <alx@kernel.org>
2025-12-05 09:21:50 -06:00
Alejandro Colomar
04f60165ae lib/, src/: Add [[gnu::unused]] to parameters used in conditionally-compiled code
Reviewed-by: Serge Hallyn <serge@hallyn.com>
Signed-off-by: Alejandro Colomar <alx@kernel.org>
2025-12-05 09:21:50 -06:00
Alejandro Colomar
3d39ab73f0 lib/logind.c: Unname unused function parameter
[Serge: preference to keep the names as a comment, but ok]
Reviewed-by: Serge Hallyn <serge@hallyn.com>
Signed-off-by: Alejandro Colomar <alx@kernel.org>
2025-12-05 09:21:50 -06:00
Alejandro Colomar
9e711e2240 lib/, src/ tests/: Unname unused parameters in callbacks
[Serge: preference to keep the names as a comment, but ok]
Reviewed-by: Serge Hallyn <serge@hallyn.com>
Signed-off-by: Alejandro Colomar <alx@kernel.org>
2025-12-05 09:21:50 -06:00
Alejandro Colomar
0663ba2095 src/: Unname unused parameter of main()
Reviewed-by: Serge Hallyn <serge@hallyn.com>
Signed-off-by: Alejandro Colomar <alx@kernel.org>
2025-12-05 09:21:50 -06:00
Antonio Terceiro
a6f5520d1a newusers: allow not passing a password
A possible use case for this is wanting to add subuid/subgid entries for
an existing user. This change makes it possible to pass `username::::::`
to newusers; the empty password will be ignored an everything else will
be done. Currently this fails miserably, as PAM errors out on a empty
password.

Signed-off-by: Antonio Terceiro <terceiro@debian.org>
2025-12-05 08:51:32 -06:00
Alejandro Colomar
2f18e7c0b6 lib/{alloc,search}/: Use typeas() to add support for arbitrary types
Signed-off-by: Alejandro Colomar <alx@kernel.org>
2025-12-05 08:34:32 -06:00
Alejandro Colomar
97d3c09a3d lib/sizeof.h: typeas(): Add macro
This macro is like typeof(), but requires that the input is a type name.

This macro is useful because it parenthesizes the type name, which
allows one to create pointers to arbitrary types.  The following code
works for simple types:

	T  x;
	T  *p;

For example, that would work fine for 'int':

	int  x;
	int  *p;

However, that wouldn't work for types such as 'int[3]':

	int[3]  x;
	int[3]  *p;

But if we do instead

	typeas(T)  x;
	typeas(T)  *p;

now we can use 'int[3]' just fine:

	typeof((int[3]){})  x;
	typeof((int[3]){})  *p;

To understand this, we create a compound literal of type int[3], with
default values (zero, zero, zero), then get it's type, which is
obviously int[3].  And then we use that to declare a variable x of that
type, and a pointer p, which has type 'int(*)[3]'.

This would also work with something simpler.  One could use typeof(T)
directly:

	typeof(T)  x;
	typeof(T)  *p;

However, typeof() doesn't require that the input is a type; it accepts
arbitrary input.  The following is valid C:

	typeof(42)  x;

For our macro MALLOC(), where we want the second argument to be a type
name, we want to require that the argument is a type name.  For that, we
need to use typeas().

Reported-by: Alejandro Colomar <alx@kernel.org>
Suggested-by: Thiago Adams <thiago.adams@gmail.com>
Suggested-by: Martin Uecker <uecker@tugraz.at>
Signed-off-by: Alejandro Colomar <alx@kernel.org>
2025-12-05 08:34:32 -06:00
Alejandro Colomar
9eec343964 lib/search/: Split APIs
Have xxx_T() macros that only add type safety to the function they wrap.

Have XXX() macros that do more magic; in this case, they deduce the
comparison function that is appropriate for the type.

This will allow using the xxx_T() macros in more complex cases, where
the function can't be deduced just from the type.

Signed-off-by: Alejandro Colomar <alx@kernel.org>
2025-12-05 08:34:32 -06:00
Alejandro Colomar
cf8b68153e lib/search/: Simplify CMP()
Signed-off-by: Alejandro Colomar <alx@kernel.org>
2025-12-05 08:34:32 -06:00
Alejandro Colomar
f45082a1e9 lib/search/, lib/, src/: Add a type parameter to the type-safe macros
This simplifies the implementation, allowing us to use _Generic(3) to
replace _Static_assert(3), is_same_type(), and local variables.

Local variables in macros can cause issues when nesting such macros.

This also makes the code more readable, by having the type explicitly
stated at call site.

Signed-off-by: Alejandro Colomar <alx@kernel.org>
2025-12-05 08:34:32 -06:00
Alejandro Colomar
97380c811a lib/: Use a consistent name for macro arguments representing a type name
Signed-off-by: Alejandro Colomar <alx@kernel.org>
2025-12-05 08:34:32 -06:00
Alejandro Colomar
dcc0db3fdb src/: Fix uninitialized flags, and use const appropriately
In all these functions, we were setting the flags to 'true' in
process_flags() if the appropriate command-line flag was specified.
However, they were never being defined to 'false', which should be the
default value.  Initialize these flags to false.

Fixes: c0c9485d9a5a (2025-10-07; "src/useradd.c: chroot or prefix SELinux file context")
Link: <https://github.com/shadow-maint/shadow/pull/1396>
Reviewed-by: Serge Hallyn <serge@hallyn.com>
Cc: Iker Pedrosa <ipedrosa@redhat.com>
Signed-off-by: Alejandro Colomar <alx@kernel.org>
2025-12-05 14:57:42 +01:00
Serge Hallyn
423368e457 Update po and potfiles
I just did a 'make dist', then committed the updates to the po and
shadow.pot files.

Closes #1359

Signed-off-by: Serge Hallyn <serge@hallyn.com>
CC: Chris Hofstaedtler <zeha@debian.org>
2025-12-05 07:42:19 -06:00
Iker Pedrosa
f4c9f5e83e useradd: fix uninitialized flags causing aarch64 failure
Initialize option_flags structure to prevent garbage memory values in
`flags.chroot` and `flags.prefix` fields. Uninitialized memory caused
architecture-specific failures where process_selinux evaluation
differed between x86_64 and aarch64, leading to `pw_close()` failures
when SELinux contexts weren't properly managed.

Fixes: c0c9485d (2025-04-25; "src/useradd.c: chroot or prefix SELinux file context")
Link: <https://bodhi.fedoraproject.org/updates/FEDORA-2025-3d835cfb15>
Signed-off-by: Iker Pedrosa <ipedrosa@redhat.com>
2025-12-05 13:40:08 +01:00
Alejandro Colomar
5813461aa5 autogen.sh: CFLAGS: Promote -Wdiscarded-qualifiers to an error
Signed-off-by: Alejandro Colomar <alx@kernel.org>
2025-12-04 20:35:51 -06:00
Alejandro Colomar
68346aa406 lib/string/strspn/: Add missing const
Closes: <https://github.com/shadow-maint/shadow/issues/1218>
Reported-by: Chris Hofstaedtler <zeha@debian.org>
Signed-off-by: Alejandro Colomar <alx@kernel.org>
2025-12-04 20:35:51 -06:00
Alejandro Colomar
0844e244e6 src/usermod.c: $user_newhome: Remove all trailing '/'s
FTR: I'm not entirely sure if an empty string can arrive here.  It might
be that the streq() check is dead code, but I'm not sure, so I put it.
It also makes the code more robust.

Signed-off-by: Alejandro Colomar <alx@kernel.org>
2025-12-04 20:12:50 -06:00
Alejandro Colomar
73cad5c903 configure.ac, Makefile.am: Propagate ./configure flags to 'distcheck'
'make distcheck' runs ./configure (among other things).  That command
should inherit the flags passed on the command line, as they are the
flags necessary to build in the current system.

This also allows testing different configurations in the same system.
Until now, we only tested the default automagic configuration.  With
this change, one can test the same default automagic configuration, by
not passing any flags to ./configure, or they can test more specific
configurations, by passing flags.

This allows removing the hardcoded AM_DISTCHECK_CONFIGURE_FLAGS in the
"Makefile.am".

Signed-off-by: Alejandro Colomar <alx@kernel.org>
2025-12-04 20:07:22 -06:00
Alejandro Colomar
e562faf109 lib/: Use simple assignment instead of memcpy(3)
memcpy(3) is overkill, and much more dangerous than simple assignment.
Simple assignment adds type safety, and removes any possibility of
buffer overflow due to accidentally specifying a wrong size.

Signed-off-by: Alejandro Colomar <alx@kernel.org>
2025-11-28 13:40:58 -06:00
Alejandro Colomar
ceaca8460f lib/subordinateio.c: append_range(): Use reallocf(3)-like calling conventions
By returning the new pointer, we can simplify the implementation,
and we avoid a return-by-parameter.

Signed-off-by: Alejandro Colomar <alx@kernel.org>
2025-11-28 13:24:03 -06:00
Alejandro Colomar
64bdbe08f6 lib/subordinateio.c: Use REALLOCF() instead of its pattern
Signed-off-by: Alejandro Colomar <alx@kernel.org>
2025-11-28 13:24:03 -06:00
Alejandro Colomar
aee5c0f4f0 tests/unit/: Unname unused parameters in callbacks
This silences diagnostics about unused parameters.

Tested-by: Silvan Mosberger <github@infinisil.com>
Signed-off-by: Alejandro Colomar <alx@kernel.org>
2025-11-28 08:41:54 -06:00
Alejandro Colomar
15aa496900 tests/unit/test_xaprintf.c: Use assert_string_equal()
This produces more useful test results.

With assert_true(streq(...)), we only see the line of code that
triggered the failure, while assert_string_equal() shows the contents
of the strings.  See the following example:

	alx@devuan:~/tmp$ cat cmocka.c
	#include <string.h>

	#include <stdarg.h>
	#include <stddef.h>
	#include <setjmp.h>
	#include <stdint.h>
	#include <cmocka.h>

	#define streq(a,b)  (!strcmp(a,b))

	static void a(void **)
	{
		const char *s = "foo";

		assert_true(streq(s, "bar"));
	}

	static void b(void **)
	{
		const char *s = "foo";

		assert_string_equal(s, "bar");
	}

	int
	main(void)
	{

		const struct CMUnitTest tests[] = {
			cmocka_unit_test(a),
			cmocka_unit_test(b),
		};

		return cmocka_run_group_tests(tests, NULL, NULL);
	}
	alx@devuan:~/tmp$ gcc cmocka.c -lcmocka
	alx@devuan:~/tmp$ ./a.out
	[==========] tests: Running 2 test(s).
	[ RUN      ] a
	[  ERROR   ] --- streq(s, "bar")
	[   LINE   ] --- cmocka.c:15: error: Failure!
	[  FAILED  ] a
	[ RUN      ] b
	[  ERROR   ] --- "foo" != "bar"
	[   LINE   ] --- cmocka.c:22: error: Failure!
	[  FAILED  ] b
	[==========] tests: 2 test(s) run.
	[  PASSED  ] 0 test(s).
	[  FAILED  ] tests: 2 test(s), listed below:
	[  FAILED  ] a
	[  FAILED  ] b

	 2 FAILED TEST(S)

Tested-by: Silvan Mosberger <github@infinisil.com>
Signed-off-by: Alejandro Colomar <alx@kernel.org>
2025-11-28 08:41:54 -06:00
Alejandro Colomar
1ebca415b8 lib/, src/: Consistently use NULL with fgets(3)
fgets(3) returns either NULL or the input pointer.  Checking for NULL is
more explicit, and simpler.

<stddef.h> is the header that provides NULL; add it where appropriate.

The meat of this patch can be approximated with the following semantic
patch:

	$ cat ~/tmp/spatch/fgets_null.sp
	@@
	expression a, b, c;
	@@

	- fgets(a, b, c) == a
	+ fgets(a, b, c) != NULL

	@@
	expression a, b, c;
	@@

	- fgetsx(a, b, c) == a
	+ fgetsx(a, b, c) != NULL

	@@
	expression a, b, c, p;
	@@

	- p->cio_fgets(a, b, c) == a
	+ p->cio_fgets(a, b, c) != NULL

	@@
	expression a, b, c;
	@@

	- fgets(a, b, c) != a
	+ fgets(a, b, c) == NULL

	@@
	expression a, b, c;
	@@

	- fgetsx(a, b, c) != a
	+ fgetsx(a, b, c) == NULL

	@@
	expression a, b, c, p;
	@@

	- p->cio_fgets(a, b, c) != a
	+ p->cio_fgets(a, b, c) == NUL

Applied as

	$ find contrib/ lib* src/ -type f \
	| xargs spatch --sp-file ~/tmp/spatch/fgets_null.sp --in-place;

The differences between the actual patch and the approximation via the
semantic patch from above are includes, whitespace, braces, and a case
where there was an implicit pointer-to-bool comparison which I made
explicit.  When reviewing, it'll be useful to use git-diff(1) with '-w'
and '--color-words=.'.

Signed-off-by: Alejandro Colomar <alx@kernel.org>
2025-11-28 08:39:37 -06:00
Alejandro Colomar
c85a4d3ba2 lib/, src/: Remove useless casts in fgets(3)
This patch can be replicated with the following semantic patch:

	$ cat fgets_cast.sp
	@@
	expression a, b, c;
	@@

	- fgets(a, (int) (b), c)
	+ fgets(a, b, c)

	@@
	expression a, b, c;
	@@

	- fgets(a, (int) b, c)
	+ fgets(a, b, c)

	@@
	expression a, b, c;
	@@

	- fgetsx(a, (int) (b), c)
	+ fgetsx(a, b, c)

	@@
	expression a, b, c;
	@@

	- fgetsx(a, (int) b, c)
	+ fgetsx(a, b, c)

	@@
	expression a, b, c, p;
	@@

	- p->cio_fgets(a, (int) (b), c)
	+ p->cio_fgets(a, b, c)

	@@
	expression a, b, c, p;
	@@

	- p->cio_fgets(a, (int) b, c)
	+ p->cio_fgets(a, b, c)

which is applied as:

	$ find lib* src/ -type f \
	| xargs spatch --sp-file ~/tmp/spatch/fgets_cast.sp --in-place;

Signed-off-by: Alejandro Colomar <alx@kernel.org>
2025-11-28 08:39:37 -06:00
Alejandro Colomar
d98ccc195e lib/, src/: Consistently use sizeof() as if it were a function
sizeof(foo)

-  No spaces.
	Not:  sizeof (foo)
-  Parentheses.
	Not:  sizeof foo
-  No parentheses wrapping sizeof itself
	Not:  (sizeof foo)

This patch can be approximated with the following semantic patch:

	$ cat ~/tmp/spatch/sizeof.sp
	@@
	identifier a, b;
	@@

	- sizeof a->b
	+ sizeof(a->b)

	@@
	identifier a, b;
	@@

	- sizeof a.b
	+ sizeof(a.b)

	@@
	identifier x;
	@@

	- sizeof x
	+ sizeof(x)

	@@
	identifier x;
	@@

	- sizeof *x
	+ sizeof(*x)

	@@
	identifier x;
	@@

	- (sizeof(x))
	+ sizeof(x)

	@@
	identifier x;
	@@

	- (sizeof(*x))
	+ sizeof(*x)

Applied as

	$ find contrib/ lib* src/ -type f \
	| xargs spatch --sp-file ~/tmp/spatch/sizeof.sp --in-place;

The differences between the actual patch and the approximation via the
semantic patch from above are whitespace only.  When reviewing, it'll
be useful to diff with '-w'.

Link: <https://lkml.org/lkml/2012/7/11/103>
Signed-off-by: Alejandro Colomar <alx@kernel.org>
2025-11-28 08:39:37 -06:00
Iker Pedrosa
2098f4dfeb doc/contributions/coding_style.md: Python code
Document coding style for Python code used in system tests.

Signed-off-by: Iker Pedrosa <ipedrosa@redhat.com>
2025-11-27 21:57:55 -06:00
Iker Pedrosa
e53545851d doc/contributions/ci.md: document system tests
Extend the system tests section.

Signed-off-by: Iker Pedrosa <ipedrosa@redhat.com>
2025-11-27 21:57:55 -06:00
Iker Pedrosa
77cfeacf60 doc/contributions/build_install.md: container troubleshooting
Extend the container section to document the container troubleshooting.

Signed-off-by: Iker Pedrosa <ipedrosa@redhat.com>
2025-11-27 21:57:55 -06:00
Iker Pedrosa
899e7048f2 doc/contributions/tests.md: add Python system tests
Document the new Python system tests:
- Benefits
- Contribution guidance
- How to setup the testing environment
- Test configuration and execution
- Advanced testing features
- Development patterns
- Debugging information
- Troubleshooting & FAQs

Signed-off-by: Iker Pedrosa <ipedrosa@redhat.com>
2025-11-27 21:57:55 -06:00
Alejandro Colomar
910f54c083 lib/string/: strerrno(): Use statement expression to perform lvalue conversion
Compound literals are lvalues.  This means it's possible to take their
address.  That is, it would be possible (albeit nonsensical) to do

	&strerrno();

It is also possible to assign to them (albeit also nonsensical):

	strerrno() = NULL;

The statement expression performs lvalue conversion, which turns the
lvalue into an "rvalue", as expected, and disallows all those issues.

Signed-off-by: Alejandro Colomar <alx@kernel.org>
2025-11-27 21:26:49 -06:00
Alejandro Colomar
4f523497df lib/string/README: Document *_a() macros
Signed-off-by: Alejandro Colomar <alx@kernel.org>
2025-11-27 20:50:48 -06:00
Alejandro Colomar
0b75eb75e5 */: s/STRNEQ/strneq_a/
Signed-off-by: Alejandro Colomar <alx@kernel.org>
2025-11-27 20:50:48 -06:00
Alejandro Colomar
501f6efe85 */: s/DAY_TO_STR/day_to_str_a/
Signed-off-by: Alejandro Colomar <alx@kernel.org>
2025-11-27 20:50:48 -06:00
Alejandro Colomar
af3f3202d0 */: s/STRSEP2ARR/strsep2arr_a/
This name better reflects that it handles arrays, and doesn't shout.

This case is slightly different, as this macro does a little bit more
than just enforcing arrays.  It changes the return value too.  However,
that is related-enough to the handling of arrays that I'm inclined to
accept it as a minor inconsistency.

Signed-off-by: Alejandro Colomar <alx@kernel.org>
2025-11-27 20:50:48 -06:00
Alejandro Colomar
a2207ea60a */: s/STRSEP2LS/strsep2ls_a/
This name better reflects that it handles arrays, and doesn't shout.

Signed-off-by: Alejandro Colomar <alx@kernel.org>
2025-11-27 20:50:48 -06:00
Alejandro Colomar
c1c685b5ff */: s/STRFTIME/strftime_a/
This name better reflects that it handles arrays, and doesn't shout.

Signed-off-by: Alejandro Colomar <alx@kernel.org>
2025-11-27 20:50:48 -06:00
Alejandro Colomar
cd3a5077b0 */: s/MEMZERO/memzero_a/
This name better reflects that it handles arrays, and doesn't shout.

Signed-off-by: Alejandro Colomar <alx@kernel.org>
2025-11-27 20:50:48 -06:00
Alejandro Colomar
fb9f7f51a8 */: s/STRTCPY/strtcpy_a/
This name better reflects that it handles arrays, and doesn't shout.

Signed-off-by: Alejandro Colomar <alx@kernel.org>
2025-11-27 20:50:48 -06:00
Alejandro Colomar
d13bb54184 */: s/STRNCPY/strncpy_a/
This name better reflects that it handles arrays, and doesn't shout.

Signed-off-by: Alejandro Colomar <alx@kernel.org>
2025-11-27 20:50:48 -06:00
Alejandro Colomar
4ddc73dbaf */: s/STRNCAT/strncat_a/
This name better reflects that it handles arrays, and doesn't shout.

Signed-off-by: Alejandro Colomar <alx@kernel.org>
2025-11-27 20:50:48 -06:00
Alejandro Colomar
ce044d1791 */: s/SNPRINTF/stprintf_a/
This name better reflects that it handles arrays, and doesn't shout.

Signed-off-by: Alejandro Colomar <alx@kernel.org>
2025-11-27 20:50:48 -06:00
Alejandro Colomar
56df586d1e */: s/STRNDUP/strndup_a/ s/XSTRNDUP/xstrndup_a/
This name better reflects that it handles arrays, and doesn't shout.

Signed-off-by: Alejandro Colomar <alx@kernel.org>
2025-11-27 20:50:48 -06:00
Alejandro Colomar
1c57e03090 */: s/STRNDUPA/strndupa_a/
This name better reflects that it handles arrays, and doesn't shout.

Signed-off-by: Alejandro Colomar <alx@kernel.org>
2025-11-27 20:50:48 -06:00
Alejandro Colomar
b5c5791fe7 */: s/SIZEOF_ARRAY/sizeof_a/
This name better reflects that it handles arrays, and doesn't shout.

Signed-off-by: Alejandro Colomar <alx@kernel.org>
2025-11-27 20:50:48 -06:00
Alejandro Colomar
bd87198678 */: s/READLINKNUL/readlinknul_a/
This name better reflects that it handles arrays, and doesn't shout.

Signed-off-by: Alejandro Colomar <alx@kernel.org>
2025-11-27 20:50:48 -06:00
Alejandro Colomar
0ce748ee0d src/: usage(): Use [[noreturn]]
This silences false-positive diagnostics in Clang.

Signed-off-by: Alejandro Colomar <alx@kernel.org>
2025-11-27 20:35:26 -06:00
Alejandro Colomar
16e54cd9ec lib/attr.h: ATTR_STRING(): Use it also with Clang
Signed-off-by: Alejandro Colomar <alx@kernel.org>
2025-11-27 20:35:26 -06:00
Alejandro Colomar
9eff3c9680 lib/attr.h: ATTR_ALLOC_SIZE(): Use it also with Clang
Signed-off-by: Alejandro Colomar <alx@kernel.org>
2025-11-27 20:35:26 -06:00
Alejandro Colomar
73a9d5c352 lib/attr.h: ATTR_ACCESS(): Use it also with Clang
Signed-off-by: Alejandro Colomar <alx@kernel.org>
2025-11-27 20:35:26 -06:00
Alejandro Colomar
12a702e815 lib/attr.h: format_attr(): Use it also with Clang
Signed-off-by: Alejandro Colomar <alx@kernel.org>
2025-11-27 20:35:26 -06:00
Alejandro Colomar
4500013326 lib/attr.h: NORETURN: Implement with [[noreturn]]
This is a C23 standard attribute with the same semantics.

Signed-off-by: Alejandro Colomar <alx@kernel.org>
2025-11-27 20:35:26 -06:00
Alejandro Colomar
c07265c16f lib/attr.h: MAYBE_UNUSED: Implement with [[maybe_unused]]
This is a C23 standard attribute with the same semantics.

Signed-off-by: Alejandro Colomar <alx@kernel.org>
2025-11-27 20:35:26 -06:00
Alejandro Colomar
38499ca529 lib/atoi/, */: Move all str2i() macros together with a2i()
Signed-off-by: Alejandro Colomar <alx@kernel.org>
2025-11-27 20:23:43 -06:00
Alejandro Colomar
f3e67583b4 lib/atoi/, */: Move all a2i() macros to the same file
Signed-off-by: Alejandro Colomar <alx@kernel.org>
2025-11-27 20:23:43 -06:00
Alejandro Colomar
ee575de9ae lib/atoi/: a2i(): Re-implement with a statement expression
Synopsis
	int a2i(typename T, T *restrict n, QChar *s,
	        QChar **_Nullable restrict endp, int base,
	        T min, T max);

Description
	This macro converts the initial portion of the string pointed to
	by 's' to an integer of base 'base', ensure that the number is
	in the range [min, max], and store it in *n.

	It is similar to NetBSD's strtoi(3) and strtou(3), which
	themselves are similar to strtol(3) and strtoul(3).

Arguments
	T
		The integer type used for the number.
	n
		A pointer to an integer.  The parsed number will be
		stored there.
	s
		See strtol(3).
	endp
		See strtol(3).  A difference with strtol(3) is that this
		macro is const-correct.  If 's' has type 'const char *',
		then 'endp' must have type 'const char **', whereas if
		's' has type 'char *', 'endp' must have type 'char **'.
	base
		See strtol(3).
	min
	max
		See strtoi(3) and strtou(3).

		An important difference with NetBSD's strtou(3) is that
		a2i() (with an unsigned type T) doesn't intepret any
		negative numbers as if they were large positive numbers.
		a2i() respects the limits [min, max] as one would
		intuitively expect.

Return value
	On success, 0 is returned.
	On error, -1 is returned and errno is set to indicate the error.

Errors
	See strtoi(3) and strtou(3).

Examples
	if (a2i(pid_t, &pid, s, &s, 10, 1, _Maxof(pid_t)) == -1)
		goto err;

Signed-off-by: Alejandro Colomar <alx@kernel.org>
2025-11-27 20:23:43 -06:00
Alejandro Colomar
f382a7cfb3 lib/typetraits.h: QChar_of(): Add macro
This macro is useful to implement QChar versions of functions.
See ISO C23 for a description of what QChar is.

Signed-off-by: Alejandro Colomar <alx@kernel.org>
2025-11-27 20:23:43 -06:00
sgakerru
6134359b28 useradd: tests for supplements groups
For regular and system user cases
2025-11-26 17:08:14 +01:00
sgakerru
ee8264351e useradd: fix test 69_useradd_default_GROUPS_name
Missing files have been added for the test to work: `gshadow`, `passwd`, `shadow`.
Without them, the foo user was created with a different UID and thus the test failed.

And other minor improvements, such as removing extra spaces and adding empty lines.
2025-11-26 17:08:14 +01:00
sgakerru
2561023064 src/useradd.c: Do not automatically add supplements groups for system users 2025-11-26 17:08:14 +01:00
sgakerru
8562d13402 src/useradd.c: Use free_list() to free user groups list 2025-11-26 17:08:14 +01:00
sgakerru
04cadad093 lib/list.c: free_list(): Add function 2025-11-26 17:08:14 +01:00
akshay
e7ccd3df68 groupadd: clarify -U option help text
Align wording with groupmod to reduce ambiguity in -U option description.
2025-11-18 13:43:35 +01:00
Alejandro Colomar
7d4362ff9d tests/unit/test_exit_if_null.c: Test through XMALLOC() instead of xaprintf()
Both are indirect tests for exit_if_null(), but through XMALLOC() we
can test it more robustly, as we don't need to wrap vasprintf(3) to
make it fail.  It's trivial to make MALLOC(3) fail: pass a huge size.

The tests with xaprintf() were failing on Nix.  I suspect the compiler
was inlining aggressively, and as a result, the interposition of
vasprintf(3) in cmocka wasn't actually working.  The approach with
XMALLOC() seems to work on Nix, as we don't need to interpose malloc(3).
We still need to interpose exit(3), but for some reason that works fine.

Closes: <https://github.com/shadow-maint/shadow/issues/1382>
Reported-by: Silvan Mosberger <github@infinisil.com>
Tested-by: Silvan Mosberger <github@infinisil.com>
Reviewed-by: Iker Pedrosa <ipedrosa@redhat.com>
Signed-off-by: Alejandro Colomar <alx@kernel.org>
2025-11-06 13:31:01 +01:00
Alejandro Colomar
17ad06b379 tests/unit/: Use more generic strings and names for testing exit_if_null()
This test is actually for exit_if_null(), not xaprintf().  Rename the
test file and functions, and make strings more generic.

Tested-by: Silvan Mosberger <github@infinisil.com>
Reviewed-by: Iker Pedrosa <ipedrosa@redhat.com>
Signed-off-by: Alejandro Colomar <alx@kernel.org>
2025-11-06 13:31:01 +01:00
Alejandro Colomar
a97f4fc0ad lib/, src/: Remove unused parameter $2 of audit_logger()
Signed-off-by: Alejandro Colomar <alx@kernel.org>
2025-11-06 12:50:49 +01:00
Alejandro Colomar
2af4c8477d lib/defines.h: LOG_NOWAIT: Remove unused macro
Signed-off-by: Alejandro Colomar <alx@kernel.org>
2025-11-06 12:50:49 +01:00
Alejandro Colomar
58c5139c70 lib/defines.h: NGROUPS_MAX: Remove unused macro
BTW, it's already defined in <limits.h>, so even if we used it,
we shouldn't define it here.

Signed-off-by: Alejandro Colomar <alx@kernel.org>
2025-11-06 12:50:49 +01:00
Alejandro Colomar
7a16bd3f83 lib/, src/: Remove unused parameter $3 of passwd_check()
Signed-off-by: Alejandro Colomar <alx@kernel.org>
2025-11-06 12:50:49 +01:00
Alejandro Colomar
0798100e9e lib/obscure.c: Remove unused parameter $1 of palindrome()
Signed-off-by: Alejandro Colomar <alx@kernel.org>
2025-11-06 12:50:49 +01:00
Alejandro Colomar
8f784fed8e lib/: Remove unused parameter $3 of password_check() and propagate
Propagate the removal of dead code to its callers, which were only
passing the parameter to this function.

Signed-off-by: Alejandro Colomar <alx@kernel.org>
2025-11-06 12:50:49 +01:00
Alejandro Colomar
d049315a13 lib/copydir.c: Remove unused parameter $2 of copy_hardlink()
Signed-off-by: Alejandro Colomar <alx@kernel.org>
2025-11-06 12:50:49 +01:00
Alejandro Colomar
655059cdef lib/copydir.c: Remove unused parameter $3 of copy_symlink()
Signed-off-by: Alejandro Colomar <alx@kernel.org>
2025-11-06 12:50:49 +01:00
Alejandro Colomar
8bbf8b33fe src/gpasswd.c: Remove unused parameter $1 of check_perms()
Fixes: 53e1eb404541 (2024-07-01; "src/: Remove dead code")
Signed-off-by: Alejandro Colomar <alx@kernel.org>
2025-11-06 12:50:49 +01:00
Alejandro Colomar
270a11ccc0 src/useradd.c: Remove unused variable
Fixes: d91b22cc2f6d (2024-07-08; "lib/, src/: Use stpsep() instead of its pattern")
Signed-off-by: Alejandro Colomar <alx@kernel.org>
2025-11-06 12:50:49 +01:00
Alejandro Colomar
90467efb07 lib/, src/: Use strerrno() instead of its pattern
Signed-off-by: Alejandro Colomar <alx@kernel.org>
2025-11-04 10:11:16 +01:00
Alejandro Colomar
8f58cc428c lib/string/: strerrno(): Add macro
Signed-off-by: Alejandro Colomar <alx@kernel.org>
2025-11-04 10:11:16 +01:00
Alejandro Colomar
65289d6848 lib/getdef.h: Add missing includes
Signed-off-by: Alejandro Colomar <alx@kernel.org>
2025-11-04 10:11:16 +01:00
Alejandro Colomar
9bf3ddc8df lib/utmp.c: ttyname_ra(): Add macro
And use it instead of its pattern.

This macro enforces correct use of ttyname_r(3) with arrays.

Reviewed-by: Serge Hallyn <serge@hallyn.com>
Signed-off-by: Alejandro Colomar <alx@kernel.org>
2025-11-04 02:56:30 +01:00
Alejandro Colomar
5686ab8744 lib/utmp.c: is_my_tty(): Use ttyname_r(3) to make it re-entrant
Reviewed-by: Serge Hallyn <serge@hallyn.com>
Signed-off-by: Alejandro Colomar <alx@kernel.org>
2025-11-04 02:56:30 +01:00
Alejandro Colomar
9327afaff8 lib/utmp.c: is_my_tty(): Rename local variable
This name makes the function definition more readable.

Reviewed-by: Serge Hallyn <serge@hallyn.com>
Signed-off-by: Alejandro Colomar <alx@kernel.org>
2025-11-04 02:56:30 +01:00
Alejandro Colomar
df72355684 lib/utmp.c: is_my_tty(): Don't cache ttyname(3).
The method for checking for truncation was quite weird.  By not caching
ttyname(3), we use it directly, without needing a temporary copy, which
removes opportunities for bugs.

Reviewed-by: Serge Hallyn <serge@hallyn.com>
Signed-off-by: Alejandro Colomar <alx@kernel.org>
2025-11-04 02:56:30 +01:00
Alejandro Colomar
655ffcbe1b lib/, src/, tests/: Move x*() definitions to non-x* header files
Now that all of these are one-liners, they don't need a separate header
file.  Compact stuff.

Signed-off-by: Alejandro Colomar <alx@kernel.org>
2025-10-27 14:32:06 +01:00
Alejandro Colomar
e5b6968406 lib/string/strtok/: xastrsep2ls() Reimplement in terms of exit_if_null()
Signed-off-by: Alejandro Colomar <alx@kernel.org>
2025-10-27 14:32:06 +01:00
Alejandro Colomar
77fd7f66eb lib/string/strdup/xstrndup.h: Add STRNDUP()
Signed-off-by: Alejandro Colomar <alx@kernel.org>
2025-10-27 14:32:06 +01:00
Alejandro Colomar
b14d14a405 lib/string/strdup/: XSTRNDUP(): Reimplement in terms of exit_if_null()
This is much simpler.

Signed-off-by: Alejandro Colomar <alx@kernel.org>
2025-10-27 14:32:06 +01:00
Alejandro Colomar
e645cc0d28 lib/string/sprintf/, tests/: xaprintf(): Reimplement in terms of exit_if_null()
Signed-off-by: Alejandro Colomar <alx@kernel.org>
2025-10-27 14:32:06 +01:00
Alejandro Colomar
335995a8ad lib/alloc/x/: X*ALLOC(): Reimplement in terms of exit_if_null()
Signed-off-by: Alejandro Colomar <alx@kernel.org>
2025-10-27 14:32:06 +01:00
Alejandro Colomar
64a2a85fb9 lib/string/strdup/: xstrdup(): Reimplement xstrdup() in terms of exit_if_null()
Signed-off-by: Alejandro Colomar <alx@kernel.org>
2025-10-27 14:32:06 +01:00
Alejandro Colomar
90da3213e8 lib/: exit_if_null(): Add macro to exit(3) on error
Writing an x*() variant function of several functions is unnecessary.
It's simpler to write a generic exit_if_null() macro that can be chained
with any other calls.  With such a macro, the x*() variants can be
implemented as one-liner macros that are much easier to read:

For example:

	#define xmalloc(size)  exit_if_null(malloc(size))

If an error is detected, log an error, and exit(13).  About why 13, I
don't really know.  It's just what was used previously in xmalloc().

Signed-off-by: Alejandro Colomar <alx@kernel.org>
2025-10-27 14:32:06 +01:00
Alejandro Colomar
8f0fa6ddff lib/, src/: Use STRNEQ() instead of their pattern
Signed-off-by: Alejandro Colomar <alx@kernel.org>
2025-10-18 14:08:47 -05:00
Alejandro Colomar
7f9f3fa0b1 lib/string/strcmp/: strneq(), STRNEQ(): Add APIs
Signed-off-by: Alejandro Colomar <alx@kernel.org>
2025-10-18 14:08:47 -05:00
Alejandro Colomar
82f73b89b2 lib/attr.h: ATTR_NONSTRING: Add attribute [[gnu::nonstring]]
Signed-off-by: Alejandro Colomar <alx@kernel.org>
2025-10-18 14:08:47 -05:00
Alejandro Colomar
6d02454bbb lib/attr.h: __has_c_attribute(): Define fallback
This allows using __has_c_attribute() in compilers that don't have it.

Signed-off-by: Alejandro Colomar <alx@kernel.org>
2025-10-18 14:08:47 -05:00
Alejandro Colomar
49a1995049 lib/, src/: Use strncmp(3) instead of explicit byte comparisons
This is simpler to read, IMO.

Signed-off-by: Alejandro Colomar <alx@kernel.org>
2025-10-18 14:08:47 -05:00
Alejandro Colomar
db2a06ec24 lib/commonio.[ch]: struct commonio_ops: Add prefix 'cio_' to structure members
This structure has members that are named like libc APIs.

libc is allowed to provide any functions as macros (7.1.4p1 in C23).

This means that libc is allowed to provide a free(3) macro, which could
look like

	#define free(p)  __free(p)

And that would be expanded by the preprocessor in our code, turning our
structure members into some code that won't work (or even worse, it
might misbehave).

So, fix this undefined behavior.

Signed-off-by: Alejandro Colomar <alx@kernel.org>
2025-10-18 12:41:23 -05:00
Serge Hallyn
c80d2cc226
Merge pull request #1258 from ikerexxe/useradd-chroot
src/useradd.c: chroot or prefix SELinux file context
2025-10-18 11:48:09 -05:00
Alejandro Colomar
9278dc7942 src/usermod.c: Remove optimizations
These optimizations checked if the old value is the same as the new
value, and skip such changes.  This was unnecessary, and added
complexity to the source code.

Signed-off-by: Alejandro Colomar <alx@kernel.org>
2025-10-16 09:37:30 +02:00
Alejandro Colomar
e5f37f3e4b src/usermod.c: Remove 'no changes' informative output
No news is good news.

Debian needs to parse this message to ignore it, or alternatively check
if the call will be a no-op (which we already do) and skip the call.
If we remove this output, we're allowing Debian to remove that
complexity in their wrapper.

We don't expect this output to be very useful for interactive use
either.

Also, this message was changed from stderr to stdout recently, so we
don't need to worry about old scripts that might break due to this
change.  If there were scripts relying on that, they would have been
broken already in the previous change.

Closes: <https://github.com/shadow-maint/shadow/issues/1361>
Reported-by: Marc Haber <githubvisible@zugschlus.de>
Cc: <https://github.com/cachius>
Signed-off-by: Alejandro Colomar <alx@kernel.org>
2025-10-16 09:37:30 +02:00
Iker Pedrosa
923aeac250 man/: update --root flag with no SELinux support
Signed-off-by: Iker Pedrosa <ipedrosa@redhat.com>
2025-10-07 11:05:39 +02:00
Iker Pedrosa
4d431898ba src/gpasswd.c: chroot or prefix SELinux file context
Do not process SELinux file context during file closure when chroot or
prefix options are selected.

Signed-off-by: Iker Pedrosa <ipedrosa@redhat.com>
2025-10-07 11:05:39 +02:00
Iker Pedrosa
48c84bbc87 src/pwunconv.c: SELinux file context for fail_exit()
Do not process SELinux file context when running fail_exit() when chroot
or prefix options are selected.

Signed-off-by: Iker Pedrosa <ipedrosa@redhat.com>
2025-10-07 11:05:39 +02:00
Iker Pedrosa
3e8645b164 src/pwunconv.c: chroot or prefix SELinux file context
Do not process SELinux file context during file closure when chroot or
prefix options are selected.

Signed-off-by: Iker Pedrosa <ipedrosa@redhat.com>
2025-10-07 11:05:38 +02:00
Iker Pedrosa
310ee1ca67 src/pwconv.c: SELinux file context for fail_exit()
Do not process SELinux file context when running fail_exit() when chroot
or prefix options are selected.

Signed-off-by: Iker Pedrosa <ipedrosa@redhat.com>
2025-10-07 11:05:38 +02:00
Iker Pedrosa
5353db6897 src/pwconv.c: chroot or prefix SELinux file context
Do not process SELinux file context during file closure when chroot or
prefix options are selected.

Signed-off-by: Iker Pedrosa <ipedrosa@redhat.com>
2025-10-07 11:05:38 +02:00
Iker Pedrosa
1d92fe1a18 src/pwck.c: SELinux file context for fail_exit()
Do not process SELinux file context when running fail_exit() when chroot
or prefix options are selected.

Signed-off-by: Iker Pedrosa <ipedrosa@redhat.com>
2025-10-07 11:05:38 +02:00
Iker Pedrosa
de5e98e1c4 src/pwck.c: chroot or prefix SELinux file context
Do not process SELinux file context during file closure when chroot or
prefix options are selected.

Signed-off-by: Iker Pedrosa <ipedrosa@redhat.com>
2025-10-07 11:05:38 +02:00
Iker Pedrosa
ddb2029047 src/passwd.c: SELinux file context for fail_exit()
Do not process SELinux file context when running fail_exit() when chroot
or prefix options are selected.

Signed-off-by: Iker Pedrosa <ipedrosa@redhat.com>
2025-10-07 11:05:38 +02:00
Iker Pedrosa
68a1c6b4d0 src/passwd.c: chroot or prefix SELinux file context
Do not process SELinux file context during file closure when chroot or
prefix options are selected.

Signed-off-by: Iker Pedrosa <ipedrosa@redhat.com>
2025-10-07 11:05:38 +02:00
Iker Pedrosa
24180caf21 src/chsh.c: SELinux file context for fail_exit()
Do not process SELinux file context when running fail_exit() when chroot
or prefix options are selected.

Signed-off-by: Iker Pedrosa <ipedrosa@redhat.com>
2025-10-07 11:05:38 +02:00
Iker Pedrosa
ad50e05910 src/chsh.c: chroot or prefix SELinux file context
Do not process SELinux file context during file closure when chroot or
prefix options are selected.

Signed-off-by: Iker Pedrosa <ipedrosa@redhat.com>
2025-10-07 11:05:38 +02:00
Iker Pedrosa
88503aebb1 src/chpasswd.c: SELinux file context for fail_exit()
Do not process SELinux file context when running fail_exit() when chroot
or prefix options are selected.

Signed-off-by: Iker Pedrosa <ipedrosa@redhat.com>
2025-10-07 11:05:38 +02:00
Iker Pedrosa
d0f98b70ec src/chpasswd.c: chroot or prefix SELinux file context
Do not process SELinux file context during file closure when chroot or
prefix options are selected.

Signed-off-by: Iker Pedrosa <ipedrosa@redhat.com>
2025-10-07 11:05:38 +02:00
Iker Pedrosa
1af3d27453 src/chfn.c: SELinux file context for fail_exit()
Do not process SELinux file context when running fail_exit() when chroot
or prefix options are selected.

Signed-off-by: Iker Pedrosa <ipedrosa@redhat.com>
2025-10-07 11:05:38 +02:00
Iker Pedrosa
961aec9a15 src/chfn.c: chroot or prefix SELinux file context
Do not process SELinux file context during file closure when chroot or
prefix options are selected.

Signed-off-by: Iker Pedrosa <ipedrosa@redhat.com>
2025-10-07 11:05:38 +02:00
Iker Pedrosa
85c550784c src/chage.c: SELinux file context for fail_exit()
Do not process SELinux file context when running fail_exit() when chroot
or prefix options are selected.

Signed-off-by: Iker Pedrosa <ipedrosa@redhat.com>
2025-10-07 11:05:38 +02:00
Iker Pedrosa
5299e2017e src/chage.c: chroot or prefix SELinux file context
Do not process SELinux file context during file closure when chroot or
prefix options are selected.

Signed-off-by: Iker Pedrosa <ipedrosa@redhat.com>
2025-10-07 11:05:38 +02:00
Iker Pedrosa
4946b027bb src/grpunconv.c: SELinux file context for fail_exit()
Do not process SELinux file context when running fail_exit() when chroot
or prefix options are selected.

Signed-off-by: Iker Pedrosa <ipedrosa@redhat.com>
2025-10-07 11:05:37 +02:00
Iker Pedrosa
2728940161 src/grpunconv.c: chroot or prefix SELinux file context
Do not process SELinux file context during file closure when chroot or
prefix options are selected.

Signed-off-by: Iker Pedrosa <ipedrosa@redhat.com>
2025-10-07 11:04:40 +02:00
Iker Pedrosa
f4473af508 src/grpconv.c: SELinux file context for fail_exit()
Do not process SELinux file context when running fail_exit() when chroot
or prefix options are selected.

Signed-off-by: Iker Pedrosa <ipedrosa@redhat.com>
2025-10-07 11:04:40 +02:00
Iker Pedrosa
d597081324 src/grpconv.c: chroot or prefix SELinux file context
Do not process SELinux file context during file closure when chroot or
prefix options are selected.

Signed-off-by: Iker Pedrosa <ipedrosa@redhat.com>
2025-10-07 11:04:40 +02:00
Iker Pedrosa
7af581cc86 src/grpck.c: SELinux file context for fail_exit()
Do not process SELinux file context when running fail_exit() when chroot
or prefix options are selected.

Signed-off-by: Iker Pedrosa <ipedrosa@redhat.com>
2025-10-07 11:04:40 +02:00
Iker Pedrosa
0f4f676492 src/grpck.c: chroot or prefix SELinux file context
Do not process SELinux file context during file closure when chroot or
prefix options are selected.

Signed-off-by: Iker Pedrosa <ipedrosa@redhat.com>
2025-10-07 11:04:40 +02:00
Iker Pedrosa
74018ef083 src/groupmems.c: SELinux file context for fail_exit()
Do not process SELinux file context when running fail_exit() when chroot
or prefix options are selected.

Signed-off-by: Iker Pedrosa <ipedrosa@redhat.com>
2025-10-07 11:04:40 +02:00
Iker Pedrosa
d71786e5c4 src/groupmems.c: chroot or prefix SELinux file context
Do not process SELinux file context during file closure when chroot or
prefix options are selected.

Signed-off-by: Iker Pedrosa <ipedrosa@redhat.com>
2025-10-07 11:04:40 +02:00
Iker Pedrosa
3d4a4233ff src/chgpasswd.c: SELinux file context for fail_exit()
Do not process SELinux file context when running fail_exit() when chroot
or prefix options are selected.

Signed-off-by: Iker Pedrosa <ipedrosa@redhat.com>
2025-10-07 11:04:40 +02:00
Iker Pedrosa
791c53c01d src/chgpasswd.c: chroot or prefix SELinux file context
Do not process SELinux file context during file closure when chroot or
prefix options are selected.

Signed-off-by: Iker Pedrosa <ipedrosa@redhat.com>
2025-10-07 11:04:40 +02:00
Iker Pedrosa
d7071cac29 src/groupdel.c: chroot or prefix SELinux file context
Do not process SELinux file context during file closure when chroot or
prefix options are selected.

Signed-off-by: Iker Pedrosa <ipedrosa@redhat.com>
2025-10-07 11:04:40 +02:00
Iker Pedrosa
cda2ecf4ab src/groupmod.c: chroot or prefix SELinux file context
Do not process SELinux file context during file closure when chroot or
prefix options are selected.

Signed-off-by: Iker Pedrosa <ipedrosa@redhat.com>
2025-10-07 11:04:40 +02:00
Iker Pedrosa
a1d49a4e8d src/groupadd.c: chroot or prefix SELinux file context
Do not process SELinux file context during file closure when chroot or
prefix options are selected.

Closes: https://github.com/shadow-maint/shadow/issues/940
Signed-off-by: Iker Pedrosa <ipedrosa@redhat.com>
2025-10-07 11:04:40 +02:00
Iker Pedrosa
db1e4e3631 lib/: add SELinux control flag in cleanup_unlock_*()
Expand cleanup_unlock_passwd(), cleanup_unlock_shadow(),
cleanup_unlock_group() and cleanup_unlock_gshadow() interfaces to add a
control flag for SELinux file context processing.

Signed-off-by: Iker Pedrosa <ipedrosa@redhat.com>
2025-10-07 11:04:40 +02:00
Iker Pedrosa
df9c80e713 src/newusers.c: SELinux file context for fail_exit()
Do not process SELinux file context when running fail_exit() when chroot
or prefix options are selected.

Signed-off-by: Iker Pedrosa <ipedrosa@redhat.com>
2025-10-07 11:04:40 +02:00
Iker Pedrosa
6c5ff23fb3 src/newusers.c: chroot or prefix SELinux file context
Do not process SELinux file context during file closure when chroot or
prefix options are selected.

Signed-off-by: Iker Pedrosa <ipedrosa@redhat.com>
2025-10-07 11:04:40 +02:00
Iker Pedrosa
5827712bff src/userdel.c: SELinux file context for fail_exit()
Do not process SELinux file context when running fail_exit() when chroot
or prefix options are selected.

Signed-off-by: Iker Pedrosa <ipedrosa@redhat.com>
2025-10-07 11:04:40 +02:00
Iker Pedrosa
e62bc901c8 src/userdel.c: chroot or prefix SELinux file context
Do not process SELinux file context during file closure when chroot or
prefix options are selected.

Signed-off-by: Iker Pedrosa <ipedrosa@redhat.com>
2025-10-07 11:04:40 +02:00
Iker Pedrosa
df2be62b8e src/userdel.c: replace global variable by flags structure
Signed-off-by: Iker Pedrosa <ipedrosa@redhat.com>
2025-10-07 11:04:39 +02:00
Iker Pedrosa
c76fd6cf0d src/usermod.c: SELinux file context for fail_exit()
Do not process SELinux file context when running fail_exit() when chroot
or prefix options are selected.

Signed-off-by: Iker Pedrosa <ipedrosa@redhat.com>
2025-10-07 11:04:39 +02:00
Iker Pedrosa
666d724a51 src/usermod.c: chroot or prefix SELinux file context
Do not process SELinux file context during file closure when chroot or
prefix options are selected.

Signed-off-by: Iker Pedrosa <ipedrosa@redhat.com>
2025-10-07 11:04:39 +02:00
Iker Pedrosa
09a739d050 src/useradd.c: SELinux file context for fail_exit()
Do not process SELinux file context when running fail_exit() when chroot
or prefix options are selected.

Signed-off-by: Iker Pedrosa <ipedrosa@redhat.com>
2025-10-07 11:04:39 +02:00
Iker Pedrosa
9f3008824f src/useradd.c: SELinux file context for home and mail
Do not process SELinux file context when creating home and mail folders
when chroot or prefix options are selected.

Closes: https://github.com/shadow-maint/shadow/issues/940
Signed-off-by: Iker Pedrosa <ipedrosa@redhat.com>
2025-10-07 11:04:39 +02:00
Iker Pedrosa
c0c9485d9a src/useradd.c: chroot or prefix SELinux file context
Do not process SELinux file context during file closure when chroot or
prefix options are selected.

Closes: https://github.com/shadow-maint/shadow/issues/940
Signed-off-by: Iker Pedrosa <ipedrosa@redhat.com>
2025-10-07 11:04:39 +02:00
Iker Pedrosa
efef4d0635 lib/, src/: add SELinux control flag in sub_gid_unlock()
Expand sub_gid_unlock() interface to add a control flag for SELinux file
context processing.

Signed-off-by: Iker Pedrosa <ipedrosa@redhat.com>
2025-10-07 11:04:39 +02:00
Iker Pedrosa
06c5ae8c28 lib/, src/: add SELinux control flag in sub_uid_unlock()
Expand sub_uid_unlock() interface to add a control flag for SELinux file
context processing.

Signed-off-by: Iker Pedrosa <ipedrosa@redhat.com>
2025-10-07 11:04:39 +02:00
Iker Pedrosa
b33683a353 src/vipw.c: add SELinux control flag in unlock
All unlock functions require the SELinux control flag, thus add it as an
argument.

Signed-off-by: Iker Pedrosa <ipedrosa@redhat.com>
2025-10-07 11:04:39 +02:00
Iker Pedrosa
046c60df09 lib/, src/: add SELinux control flag in sgr_unlock()
Expand sgr_unlock() interface to add a control flag for SELinux file
context processing.

Signed-off-by: Iker Pedrosa <ipedrosa@redhat.com>
2025-10-07 11:04:39 +02:00
Iker Pedrosa
8f3657088c lib/, src/: add SELinux control flag in gr_unlock()
Expand gr_unlock() interface to add a control flag for SELinux file
context processing.

Signed-off-by: Iker Pedrosa <ipedrosa@redhat.com>
2025-10-07 11:04:39 +02:00
Iker Pedrosa
fe00a581c3 lib/, src/: add SELinux control flag in spw_unlock()
Expand spw_unlock() interface to add a control flag for SELinux file
context processing.

Signed-off-by: Iker Pedrosa <ipedrosa@redhat.com>
2025-10-07 11:04:39 +02:00
Iker Pedrosa
6396ff013c lib/, src/: add SELinux control flag in pw_unlock()
Expand pw_unlock() interface to add a control flag for SELinux file
context processing.

Signed-off-by: Iker Pedrosa <ipedrosa@redhat.com>
2025-10-07 11:04:39 +02:00
Iker Pedrosa
185991a5cd lib/: add SELinux control flag in commonio_unlock()
Expand commonio_unlock() interface to add a control flag for SELinux
file context processing.

Signed-off-by: Iker Pedrosa <ipedrosa@redhat.com>
2025-10-07 11:04:39 +02:00
Iker Pedrosa
987a993087 lib/, src/: add SELinux control flag in sub_gid_close()
Expand sub_gid_close() interface to add a control flag for SELinux file
context processing.

Signed-off-by: Iker Pedrosa <ipedrosa@redhat.com>
2025-10-07 11:04:39 +02:00
Iker Pedrosa
f8a459f929 lib/, src/: add SELinux control flag in sub_uid_close()
Expand sub_uid_close() interface to add a control flag for SELinux file
context processing.

Signed-off-by: Iker Pedrosa <ipedrosa@redhat.com>
2025-10-07 11:04:39 +02:00
Iker Pedrosa
ddd112714e lib/, src/: add SELinux control flag in sgr_close()
Expand sgr_close() interface to add a control flag for SELinux file
context processing.

Signed-off-by: Iker Pedrosa <ipedrosa@redhat.com>
2025-10-07 11:04:37 +02:00
Iker Pedrosa
073df98f0c lib/, src/: add SELinux control flag in gr_close()
Expand gr_close() interface to add a control flag for SELinux file
context processing.

Signed-off-by: Iker Pedrosa <ipedrosa@redhat.com>
2025-10-07 11:03:48 +02:00
Iker Pedrosa
b14465a3ab lib/, src/: add SELinux control flag in spw_close()
Expand spw_close() interface to add a control flag for SELinux file
context processing.

Signed-off-by: Iker Pedrosa <ipedrosa@redhat.com>
2025-10-07 11:03:48 +02:00
Iker Pedrosa
2a837a24d1 lib/, src/: add SELinux control flag in pw_close()
Expand pw_close() interface to add a control flag for SELinux file
context processing.

Signed-off-by: Iker Pedrosa <ipedrosa@redhat.com>
2025-10-07 11:03:48 +02:00
Iker Pedrosa
db0e0b9112 lib/: add SELinux control flag in commonio_close()
Expand commonio_close() interface to add a control flag for SELinux file
context processing.

Signed-off-by: Iker Pedrosa <ipedrosa@redhat.com>
2025-10-07 11:03:48 +02:00
Alejandro Colomar
3ea6df885d lib/: Use libc _FILE_SHADOW from <paths.h>
Signed-off-by: Alejandro Colomar <alx@kernel.org>
2025-10-07 11:03:09 +02:00
Alejandro Colomar
5c872e3424 lib/shadow/, lib/, po/: sgetspent(): Move to under lib/shadow/shadow/
Signed-off-by: Alejandro Colomar <alx@kernel.org>
2025-10-07 11:03:09 +02:00
Alejandro Colomar
996b197a80 lib/shadow/, lib/, po/: sgetpwent(): Move to under lib/shadow/passwd/
Signed-off-by: Alejandro Colomar <alx@kernel.org>
2025-10-07 11:03:09 +02:00
Alejandro Colomar
2c4c72a221 lib/shadow/, lib/, po/: sgetgrent(): Move to under lib/shadow/group/
Signed-off-by: Alejandro Colomar <alx@kernel.org>
2025-10-07 11:03:09 +02:00
Alejandro Colomar
8584448deb lib/: GSHADOW: Remove unused macro
And with it, the file that defines it, which does nothing else.

Signed-off-by: Alejandro Colomar <alx@kernel.org>
2025-10-07 11:03:09 +02:00
Alejandro Colomar
ead65ba876 lib/shadow/, lib/: getsgent(): Move to separate file
Signed-off-by: Alejandro Colomar <alx@kernel.org>
2025-10-07 11:03:09 +02:00
Alejandro Colomar
51efe36f2c lib/shadow/, lib/, src/: getsgnam(): Move to separate file
Signed-off-by: Alejandro Colomar <alx@kernel.org>
2025-10-07 11:03:09 +02:00
Alejandro Colomar
c0a2db8739 lib/shadow/, lib/: sgetsgent(): Move to separate file
Signed-off-by: Alejandro Colomar <alx@kernel.org>
2025-10-07 11:03:09 +02:00
Alejandro Colomar
a3aeee0878 lib/shadow/, lib/: fgetsgent(): Move to separate file
Signed-off-by: Alejandro Colomar <alx@kernel.org>
2025-10-07 11:03:09 +02:00
Alejandro Colomar
1a272e720f lib/shadow/, lib/, src/: struct sgrp: Move to separate file
Signed-off-by: Alejandro Colomar <alx@kernel.org>
2025-10-07 11:03:09 +02:00
Alejandro Colomar
bbd5e1305f lib/shadow/, lib/: setsgent(): Move to separate file
Signed-off-by: Alejandro Colomar <alx@kernel.org>
2025-10-07 11:03:09 +02:00
Alejandro Colomar
7a96b90ec8 lib/shadow/, lib/, src/: Use _PATH_GSHADOW from <paths.h>
This macro is provided by glibc (but not musl) as _PATH_GSHADOW in
<paths.h>.  Let's use that macro, and define it only if libc doesn't
provide it.

Signed-off-by: Alejandro Colomar <alx@kernel.org>
2025-10-07 11:03:09 +02:00
Alejandro Colomar
1517519ec2 lib/shadow/, lib/, src/: endsgent(): Move to separate file
Signed-off-by: Alejandro Colomar <alx@kernel.org>
2025-10-07 11:03:09 +02:00
Alejandro Colomar
712e94a2d0 lib/shadow/, lib/: gshadow: Move to separate file and rename
Signed-off-by: Alejandro Colomar <alx@kernel.org>
2025-10-07 11:03:09 +02:00
Alejandro Colomar
27097d59d1 lib/shadow/, lib/: putsgent(): Move to separate file
Signed-off-by: Alejandro Colomar <alx@kernel.org>
2025-10-07 11:03:09 +02:00
Alexey Tikhonov
ce66b8d5eb pwck/grpck: only force nscd/sssd caches flush if anything was changed 2025-10-06 10:36:55 +02:00
Alexey Tikhonov
920b218142 Make sure 'sss_cache' can get both 'U' and 'G' args
Fixes: 59e5eef38f89 (2024-07-03; "contrib, lib/, src/, tests/: Use stpcpy(3) instead of its pattern")

Reviewed-by: Alejandro Colomar <alx@kernel.org>
2025-10-06 10:36:55 +02:00
Frans Spiesschaert
99c757b6a4 po/nl.po: Update
Closes: <https://github.com/shadow-maint/shadow/issues/1360>
Signed-off-by: Frans Spiesschaert <Frans.Spiesschaert@yucom.be>
Cc: Chris Hofstaedtler <zeha@debian.org>
Signed-off-by: Alejandro Colomar <alx@kernel.org>
2025-10-06 10:13:56 +02:00
Alejandro Colomar
0c9c46a518 lib/string/README: Add guidelines for using strings
Suggested-by: Iker Pedrosa <ipedrosa@redhat.com>
Suggested-by: Serge Hallyn <serge@hallyn.com>
Suggested-by: Evgeny Grin (Karlson2k) <k2k@drgrin.dev>
Suggested-by: Lukas Slebodnik <lslebodn@fedoraproject.org>
Signed-off-by: Alejandro Colomar <alx@kernel.org>
2025-09-22 09:48:39 +02:00
Alejandro Colomar
8b19796746 lib/, src/: Use consistent style using strchr(3) in conditionals
While the return value is a pointer, it can be interpreted as a boolean
value meaning "found".  In general, we use explicit comparisons of
pointers to NULL, but in this specific case, let's use that
interpretation, and make an exception, using an implicit conversion to
boolean.

For negative matches, use
	if (!strchr(...))

For positive matches, use
	if (strchr(...))

For positive matches, when a variable is also set, use
	while (NULL != (p = strchr(...)))

Signed-off-by: Alejandro Colomar <alx@kernel.org>
2025-09-21 18:55:20 -05:00
Alejandro Colomar
79ddd58ef6 configure.ac, lib/: Use _PATH_WTMP from <paths.h>
That's the libc macro for this file.

Signed-off-by: Alejandro Colomar <alx@kernel.org>
2025-09-15 11:47:22 +02:00
Alejandro Colomar
72a2b8a10d configure.ac, lib/, src/: Use _PATH_LASTLOG from <paths.h>
That's the libc macro for this file.

Signed-off-by: Alejandro Colomar <alx@kernel.org>
2025-09-15 11:47:22 +02:00
Anders Blomdell
d955317f23 When using nss-module, avoid using '/etc/sub[ug]id'
Use 'want_sub[ug]id_file' to short-circuit acess to '/etc/sub[ug]id' when
nss-moduleis active
2025-09-10 08:28:13 -05:00
Anders Blomdell
15540c0d1f Factor out 'want_sub[ug]ids' and rename to 'want_sub[ug]id_file'
Move 'want_sub[ug]ids' from 'src/newusers.c' to 'lib/subordinateio.[ch]'
and rename them to 'want_sub[ug]id_file' to clearly indicate that it
refers to the '/etc/sub[ug]id' and not to subids in general.
2025-09-10 08:28:13 -05:00
Alejandro Colomar
3f5ae5d5f1 src/su.c: Fix incorrect (non-matching) parentheses
Fixes: 45c6603cc86c (2007-10-07; "[svn-upgrade] Integrating new upstream version, shadow (19990709)")
Closes: <https://github.com/shadow-maint/shadow/issues/1310>
Signed-off-by: Alejandro Colomar <alx@kernel.org>
2025-09-10 14:33:40 +02:00
Iker Pedrosa
2fd29cd841 .github/workflows/static-code-analysis.yml: add Python linters
Add flake8, pycodestyle, isort, black and mypy in CI for Python linting.

Signed-off-by: Iker Pedrosa <ipedrosa@redhat.com>
2025-09-10 09:56:36 +02:00
Iker Pedrosa
f385e3d915 tests/system/tests/: fix Python linter issues
Fix issues reported by flake8, pycodestyle, isort, black and mypy.

Signed-off-by: Iker Pedrosa <ipedrosa@redhat.com>
2025-09-10 09:56:36 +02:00
Iker Pedrosa
458700b5d6 tests/system/framework/: fix Python linter issues
Fix issues reported by flake8, pycodestyle, isort, black and mypy.

Signed-off-by: Iker Pedrosa <ipedrosa@redhat.com>
2025-09-10 09:56:36 +02:00
Iker Pedrosa
08028bb1a8 tests/system/pyproject.toml: add mypy rules
Signed-off-by: Iker Pedrosa <ipedrosa@redhat.com>
2025-09-10 09:56:36 +02:00
Georg Pfuetzenreuter
d001e4a02b man/chsh: deduplicate shells text
Restructure the paragraphs to avoid duplication of text inside multiple
conditions, making maintenance easier and avoiding accidental
duplication in the rendered output.

Signed-off-by: Georg Pfuetzenreuter <mail@georg-pfuetzenreuter.net>
2025-09-01 14:41:19 +02:00
Georg Pfuetzenreuter
c82e7c3833 man/chsh: remove duplicate paragraph
The section about the risk of placing a restricted shell was duplicated
in the rendered manual page if the "without_vendordir" condition
matched.

Fixes: a27d5c51f1f3 ("Supporting vendor given -shells- configuration file")
Signed-off-by: Georg Pfuetzenreuter <mail@georg-pfuetzenreuter.net>
2025-09-01 14:41:19 +02:00
Alejandro Colomar
02e9308925 lib/subordinateio.c: list_owner_ranges(): Fix duplicate range when username matches ID
Fixes: 3ec32f9975f2 (2022-07-20; "subordinateio: also compare the owner ID")
Closes: <https://github.com/shadow-maint/shadow/issues/1339>
Link: <https://github.com/cri-o/cri-o/issues/9416>
Reported-by: Anatolii Bazko <abazko@redhat.com>
Signed-off-by: Alejandro Colomar <alx@kernel.org>
2025-08-28 20:27:52 +02:00
Iker Pedrosa
ee25805be2 share/ansible/: fix Debian 13 build
Install `gpg` package as Debian 13 container image stopped installing it
by default, making the CI fail.

Closes: <https://github.com/shadow-maint/shadow/issues/1335>
Signed-off-by: Iker Pedrosa <ipedrosa@redhat.com>
2025-08-18 11:02:07 +02:00
Evgeny Grin (Karlson2k)
76448ca21e lib/utmp.c: Add explicit include <stdlib.h> for free() and other functions
Signed-off-by: Evgeny Grin (Karlson2k) <k2k@drgrin.dev>
2025-08-11 10:18:19 +02:00
Evgeny Grin (Karlson2k)
c6053cdd14 lib/utmp.c: Add ATTR_MALLOC(free) attribute
Signed-off-by: Evgeny Grin (Karlson2k) <k2k@drgrin.dev>
2025-08-11 10:18:19 +02:00
Alejandro Colomar
cb1749d7f3 lib/utmp.c: get_session_host(): Fix memory leak
Fixes: f40bdfa66a3a (2023-08-02; "libmisc: implement `get_session_host()`")
Closes: <https://github.com/shadow-maint/shadow/issues/1291>
Signed-off-by: Alejandro Colomar <alx@kernel.org>
2025-08-11 10:18:19 +02:00
Evgeny Grin (Karlson2k)
5958f656cc lib/utmp.c: Fix use of last utmp entry instead of patrial-match entry
The pointer returned by getutxent() function may always point to
the same shared and reused buffer.

Instead of copying the utmp entry pointer value the content of utmp
entry must be copied otherwise the next call of getutxent() will
overwrite previously found entry.

This commit has no optimisations to highlight what is really fixed.

Fixes: 841776561f56bae7382c6bd47e428201a155d39c (09-08-2025; "lib/utmp.c: Fix umtp entry search")
Signed-off-by: Evgeny Grin (Karlson2k) <k2k@drgrin.dev>
2025-08-10 15:58:21 +02:00
Alejandro Colomar
bff2961dbe src/chfn.c: Use stpeprintf() to improve readability
This allows us to split the formation of the string into several
s*printf() calls.

Shorten comment, to make it fit in one line.

Signed-off-by: Alejandro Colomar <alx@kernel.org>
2025-08-09 18:00:36 -05:00
Alejandro Colomar
d4b0418b4b src/chfn.c: Simplify checking for a long GECOS field
Use a buffer of the exact size we want, and let SNPRINTF() decide if it
fits or not.

BTW, the old check seemed to be wrong: it wasn't accounting for the
commas in the 80-character limit, but that didn't make much sense.

Signed-off-by: Alejandro Colomar <alx@kernel.org>
2025-08-09 18:00:36 -05:00
Alejandro Colomar
46ffafc8c8 src/chfn.c: slop: Reduce buffer size
We never use more than BUFSIZ.  (And we could use way less than that.)

Signed-off-by: Alejandro Colomar <alx@kernel.org>
2025-08-09 18:00:36 -05:00
Alejandro Colomar
9c61f5f0e4 src/chfn.c: Write an empty string if there's nothing in the GECOS field
Otherwise, the buffer would contain garbage.

Signed-off-by: Alejandro Colomar <alx@kernel.org>
2025-08-09 18:00:36 -05:00
Alejandro Colomar
874c81bde0 src/chfn.c: Use strsep(3) and strcpy(3) instead of its pattern
This wrapper was very weird, and it's simpler to open-code the calls to
strsep(3) and strcpy(3) instead.

Signed-off-by: Alejandro Colomar <alx@kernel.org>
2025-08-09 18:00:36 -05:00
Alejandro Colomar
51fa767a75 src/chfn.c: Do not allow the 'slop' fields to appear before any non-slop gecos fields
According to the Wikipedia page for the 'Gecos field', the "typical"
format for the GECOS field is a comma-delimited list with this order:

1)  User's full name (or application name, if the account is for a program)
2)  Building and room number or contact person
3)  Office telephone number
4)  Home telephone number
5+) Any other contact information (pager number, fax, external e-mail address, etc.)

But our code supported the "other contact information", which we call
slop, and which is composed of an arbitrary number of key=value fields,
to appear before any of the other 4 fields.

This seems to be undocumented, and none of the documentation I've found
for the GECOS field in any systems I checked claims to support this.
By removing support for those, we can significantly simplify the
copy_field() function, which was quite unreadable.

After this patch, the GECOS field is treated as a CSV, blindly copying
the fields as they appear, where the first 4 fields are as specified
above, and anything after them is the slop (5+ fields, any other contact
information).

Signed-off-by: Alejandro Colomar <alx@kernel.org>
2025-08-09 18:00:36 -05:00
Alejandro Colomar
3d5f1e88d5 lib/agetpass.c: Pass "" instead of NULL as an ignored prompt
This is safer, since in general, readpassphrase(3) does not accept
a null pointer as input.

This was discovered thanks to Chris Bazley's _Optional qualifier, which
I'm testing at the moment.

Signed-off-by: Alejandro Colomar <alx@kernel.org>
2025-08-09 17:13:31 -05:00
Alejandro Colomar
2ff16902d1 lib/, src/: Reorder while() conditions for safety
In conditions that perform simple assignment (=) before comparison,
it's safer to put the comparison first, as a mistake would result in a
compiler error, as opposed to assigning something incorrect.
It's also more readable, IMO.

Signed-off-by: Alejandro Colomar <alx@kernel.org>
2025-08-09 17:03:22 -05:00
Evgeny Grin (Karlson2k)
841776561f lib/utmp.c: Fix umtp entry search
Updated utmp entry search algorithm to follow GNU/Linux description:
https://man7.org/linux/man-pages/man5/utmp.5.html#DESCRIPTION

An entry is found by looking for matching PID.  If several such entries
found (for example, due to cleanup failure of old entries) then first
entry with both matching PID and matching 'ut_line' (current terminal)
is used.  If not entry has matching 'ut_line' then first entry with
matching PID is used (if getty/init process does not set 'ut_line').

When no single entry is matched by PID, then but at least one entry is
matched current terminal the the first such entry is selected (if getty
does not set correct PID).

This commit uses non-portable Elvis operator is it is already used
everywhere in the code.

Signed-off-by: Evgeny Grin (Karlson2k) <k2k@drgrin.dev>
2025-08-09 11:18:51 +02:00
Alejandro Colomar
c1678a9e27 src/groupmod.c: --help: wfix
Closes: <https://github.com/shadow-maint/shadow/issues/1313>
Reported-by: Arshdeep Singh <arshdeepsinghsahni8@gmail.com>
Signed-off-by: Alejandro Colomar <alx@kernel.org>
2025-08-07 15:46:17 +02:00
Evgeny Grin (Karlson2k)
014b6a6d89 configure.ac: cosmetics - unified check for $enable_logind value
Signed-off-by: Evgeny Grin (Karlson2k) <k2k@drgrin.dev>
2025-08-02 12:09:13 +02:00
Evgeny Grin (Karlson2k)
324d070ff8 configure.ac: Make sure that logind is enabled if requested, make --enable-logind default
Before this commit, if configured with --enable-logind, but libsystemd
is not found, configure silently succeed, however logind is efficiently
disabled.
With this commit, the configure fails if logind is not explicitly
disabled and libsystemd is not found.
--disable-logind is mandatory if logind integration should not be used.

Automatic detection is disabled by Alejandro Colomar's request.
Extra help in the error message is added by lslebodn's request.

Signed-off-by: Evgeny Grin (Karlson2k) <k2k@drgrin.dev>
2025-08-02 12:09:13 +02:00
Evgeny Grin (Karlson2k)
048083ab92 configure.ac: Improve formatting for libsystemd check
Improve formatting and readability of single configure check.
Also remove unneeded overquoting of "LIBSYSTEMD=-lsystemd".

Signed-off-by: Evgeny Grin (Karlson2k) <k2k@drgrin.dev>
2025-08-02 12:09:13 +02:00
Evgeny Grin (Karlson2k)
27437f1f7a configure.ac: Add check for value given for --enable-logind
Fail with error if wrong value is provided.

Signed-off-by: Evgeny Grin (Karlson2k) <k2k@drgrin.dev>
2025-08-02 12:09:13 +02:00
Evgeny Grin (Karlson2k)
ccc40e7d72 configure: Document --enable-logind behaviour correctly
The code does not enabled logind unconditionally by default.  Instead
configure checks for logind (libsystemd) availability and enables it
only if found.

Signed-off-by: Evgeny Grin (Karlson2k) <k2k@drgrin.dev>
2025-08-02 12:09:13 +02:00
Samuel Thibault
1f617f8aa7 hurd: do not include sys/prctl.h when it is not available 2025-07-22 13:41:16 +02:00
vinz
c44f1e096a chpasswd: Check hash before write when using -e
Add is_valid_hash to prevent adding a bad hash in /etc/shadow (and so prevent user to be lock) when using chpasswd -e

    # before
    echo 'vinz:test123' | chpasswd -e
    grep vinz /etc/shadow
    vinz:test123:20280:0:99999:7:::

    # now
    echo 'vinz:test123' | sudo ./chpasswd -e
    chpasswd: (line 1, user vinz) invalid password hash
    chpasswd: error detected, changes ignored
2025-07-20 21:57:14 +02:00
Evgeny Grin (Karlson2k)
2e7db49128 configure: Print configuration summary to the log (in addtion to stdout)
Signed-off-by: Ontogeny Grin (Karlson2k) <k2k@drgrin.dev>
Reviewed-by: Alejandro Colomar <alx@kernel.org>
2025-07-19 06:42:33 +02:00
Evgeny Grin (Karlson2k)
cce24c7b65 configure: Fix outdated and non-portable 'test' syntax
Signed-off-by: Evgeny Grin (Karlson2k) <k2k@drgrin.dev>
Reviewed-by: Alejandro Colomar <alx@kernel.org>
2025-07-19 06:42:33 +02:00
Evgeny Grin (Karlson2k)
10ca98bd11 configure: Unify checks for variable values
This is a workaround for broken shells, which incorrectly performs
'test "$var" = "value"' when variable is empty or not set.
Also this is a guard for variable values that may break "test", like
"!", "-z", "-n".

Signed-off-by: Evgeny Grin (Karlson2k) <k2k@drgrin.dev>
Reviewed-by: Alejandro Colomar <alx@kernel.org>
2025-07-19 06:42:33 +02:00
Evgeny Grin (Karlson2k)
ced73a7779 configure: Move helper files to 'build-aux/'
Grouped autoconf settings.

Signed-off-by: Evgeny Grin (Karlson2k) <k2k@drgrin.dev>
2025-07-19 06:42:33 +02:00
Evgeny Grin (Karlson2k)
743c18706f Makefile.am: Fix libtool warning
When running 'autoconf -vi' libtoolize always prints suggestion to
add this variable.

Signed-off-by: Evgeny Grin (Karlson2k) <k2k@drgrin.dev>
2025-07-19 06:42:33 +02:00
Evgeny Grin (Karlson2k)
c458839aa6 configure: Fix quoting of the "#" symbol
[[]] means "use literally, without expansion and substitution".
# symbol potentially could be interpreted as a comment.
Also fixed one check with indented " #include <security/pam_appl.h>"
which is not correct C syntax.

Signed-off-by: Evgeny Grin (Karlson2k) <k2k@drgrin.dev>
2025-07-19 06:42:33 +02:00
Evgeny Grin (Karlson2k)
08ec7536e4 configure: Move AC_ARG_ENABLE. It cannot be conditional.
AC_ARG_ENABLE() expands to nothing where it is used, but adds arguments
parsing, help message and other related things.
It does not make any sense to put this macro into if branch.  It may
also confuse the reader.

Signed-off-by: Evgeny Grin (Karlson2k) <k2k@drgrin.dev>
2025-07-19 06:42:33 +02:00
Evgeny Grin (Karlson2k)
630fb7ece1 configure: Unify M4 quoting
Always quoting of all arguments is recommended by autoconf manual.

The commit is checked by autoreconf -v before and after commit.
Resulting configure is identical (excluding some newlines).

Signed-off-by: Evgeny Grin (Karlson2k) <k2k@drgrin.dev>
2025-07-19 06:42:33 +02:00
Evgeny Grin (Karlson2k)
c3878390a6 configure: Fix incorrect use of AM_CONDITIONAL
AM_CONDITIONAL() must not be used in shell's if branches.  Instead it
must be specified one time only (per conditional variable) with test
"something" as a second parameter.
See https://www.gnu.org/software/automake/manual/html_node/Usage-of-Conditionals.html#index-AM_005fCONDITIONAL-2

Signed-off-by: Evgeny Grin (Karlson2k) <k2k@drgrin.dev>
Reviewed-by: Alejandro Colomar <alx@kernel.org>
2025-07-19 06:42:33 +02:00
Evgeny Grin (Karlson2k)
50f88ab8be configure: Remove duplicated check and unused Makefile substitution
Lines were incorrectly added by 5cd04d03f94622c12220d4a6352824af081b8531
The check is fully duplicated and does nothing except setting wrong
variable LIYESCRYPT.  Such variable was never used in the project.

Signed-off-by: Evgeny Grin (Karlson2k) <k2k@drgrin.dev>
2025-07-19 06:42:33 +02:00
Evgeny Grin (Karlson2k)
90042ad434 src/login.c: Fix checking whether 'login' is started as 'init'
When PAM is not used, login does not fork itself so its own PID should
be checked instead of parent PID.

Fixes: b44a6c316d96ab038492c63443156810670d176d (26-12-2007; "If started as init, login and sulogin need to start a new session.")
Signed-off-by: Evgeny Grin (Karlson2k) <k2k@drgrin.dev>
2025-07-18 21:55:10 +02:00
Evgeny Grin (Karlson2k)
06cdfd27a4 lib/, src/: Fix utmp update: use initial PID
The correct utmp update functionality was broken mainly by commit
91fc51387ca5341e1a1f778a967886c5fe589cb8, which moved update of utmp
after forking (when PAM is used).  It was also misinterpretation of
GNU/Linux utmp specifications, where is specified that ut_pid must be
the PID of the **login** process (not a PID of any forked process).
Wrong ut_pid also prevents utmp cleanup, which performed by init process
and should clean entry with the same ut_pid as started login process.
GNU/Linux description of utmp updates can be found at the next url:
https://man7.org/linux/man-pages/man5/utmp.5.html

Signed-off-by: Evgeny Grin (Karlson2k) <k2k@drgrin.dev>
2025-07-18 21:55:10 +02:00
Evgeny Grin (Karlson2k)
ec5a5a549c lib/utmp.c: Additional refactoring for readability
Signed-off-by: Evgeny Grin (Karlson2k) <k2k@drgrin.dev>
2025-07-18 21:55:10 +02:00
Evgeny Grin (Karlson2k)
212d75c150 lib/utmp.c: Refactoring for readability
Signed-off-by: Evgeny Grin (Karlson2k) <k2k@drgrin.dev>
2025-07-18 21:55:10 +02:00
Evgeny Grin (Karlson2k)
59c5acb5de lib/utmp.c: Align generated "ut_id" with modern software
Modern software (systemd, utemper) usually use full "ut_line" as "ut_id"
if string is up to four chars or last four chars if "ut_line" is longer.
For reference:
* libutemper (used by many graphical terminal emulator applications
  (konsole, xterm, others) to deal with utmp file):
  4caa8ab94f/libutempter/utempter.c (L99-L103)
* systemd:
  uses full name of the device:
  8013beb4a2/units/getty%40.service.in (L41-L44)
  or **last** four characters:
  https://www.freedesktop.org/software/systemd/man/latest/systemd.exec.html#UtmpIdentifier=

The code in the commit is optimised for visual C code size.

Signed-off-by: Evgeny Grin (Karlson2k) <k2k@drgrin.dev>
Reviewed-by: Alejandro Colomar <alx@kernel.org>
2025-07-18 11:52:19 +02:00
Alejandro Colomar
4f476f98d9 lib/, src/: Use printf(3) instead of its pattern
Signed-off-by: Alejandro Colomar <alx@kernel.org>
2025-07-17 12:57:50 +02:00
Alejandro Colomar
897b92f795 src/vipw.c: usage(): Print everything to the same stream
Closes: <https://github.com/shadow-maint/shadow/issues/1288>
Signed-off-by: Alejandro Colomar <alx@kernel.org>
2025-07-17 12:57:50 +02:00
Evgeny Grin (Karlson2k)
f86d60f9d3 lib/utmp.c: Fixed generated strings for "ut_id"
When no "ut_id" is provided by previous utmp file entry, "login" must
generate its own "ut_id". Historically tty number  was used for it.
However, if current device name is three characters or shorter, then
empty "ut_id" is produced or even garbage is copied from uninitialised
part of the "line".
This commit fixes it.
This patch uses unportable C extension Elvis operator as it is already
used everywhere in the code.

Signed-off-by: Evgeny Grin (Karlson2k) <k2k@drgrin.dev>
2025-07-17 12:55:02 +02:00
Alejandro Colomar
dc2c85d754 lib/shadow/grp/: agetgroups(): Fix possible buffer overrun on non-Linux systems
Linux seems to at least write one group always from getgroups(2).
However, POSIX doesn't guarantee this, and a system might have 0 groups.

	It is implementation‐defined whether getgroups() also returns
	the effective group ID in the grouplist array.

Considering such a system, the call getgroups(0,NULL) could indeed
return 0, and the second call to getgroups might return a higher value,
if the group list has grown in between (race condition).  If this is the
case, we'd return an array of 0 elements (or 1, due to the MALLOC()
trick to avoid calling it with 0), with no elements filled, but where
ngids has been updated to have a positive value.  When the caller of
agetgroups() reads the array, they'd overrun the buffer.

Fixes: 05322ed89a1c (2025-01-24; "lib/shadow/grp/: agetgroups(): Add function")
Fixes: de941a7601f8 (2025-01-24; "lib/, src/: Simplify allocation of buffer")
Signed-off-by: Alejandro Colomar <alx@kernel.org>
2025-07-16 08:36:01 -05:00
Evgeny Grin (Karlson2k)
eb71706b1c */: Fix including <config.h> as system header
"config.h" is a locally generated header. It must be included as
'#include "config.h"'.
It is already included correctly in some sources files. This commit
unifies the way how "config.h" is included.

Signed-off-by: Evgeny Grin (Karlson2k) <k2k@drgrin.dev>
2025-07-16 14:12:40 +02:00
Evgeny Grin (Karlson2k)
9195f3807d lib/utmp.c: check parent PID too when looking for utmp entry
'login' binary may fork itself before this check, so the entry may
contain parent PID instead of current process PID
2025-07-13 21:25:08 +02:00
Alejandro Colomar
36debf3ccf tests/unit/test_xaprintf.c: Fix test by using streq() instead of strcmp(3)
Fixes: 423fd652b563 (2025-06-03; "lib/string/sprintf/, tests/unit/: Transform x[v]asprintf() into x[v]aprintf()")
Closes: <https://github.com/shadow-maint/shadow/issues/1279>
Reported-by: Timo Gurr <timo.gurr@gmail.com>
Signed-off-by: Alejandro Colomar <alx@kernel.org>
2025-07-02 17:41:21 +02:00
496 changed files with 15279 additions and 10789 deletions

View File

@ -22,5 +22,5 @@ runs:
sudo apt-mark hold grub-efi-amd64-bin grub-efi-amd64-signed
sudo apt-get update
sudo apt-get -y dist-upgrade
sudo apt-get -y install ubuntu-dev-tools automake autopoint xsltproc gettext expect libtool libbsd-dev libltdl-dev pkgconf
sudo apt-get -y install ubuntu-dev-tools automake autopoint xsltproc gettext expect libtool libbsd-dev libltdl-dev libsystemd-dev pkgconf
sudo apt-get -y build-dep shadow

View File

@ -10,7 +10,10 @@ on:
jobs:
build:
runs-on: ubuntu-latest
strategy:
matrix:
os: [ubuntu-latest, ubuntu-22.04]
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v3
- name: debug
@ -62,7 +65,7 @@ jobs:
tar -zxf $f
d=$(basename $f .tar.gz)
cd $d
./configure
./configure --disable-logind
make -j5
make check

View File

@ -56,3 +56,49 @@ jobs:
with:
severity: warning
token: ${{ secrets.GITHUB_TOKEN }}
python-linter:
runs-on: ubuntu-latest
permissions:
contents: read
steps:
- uses: actions/setup-python@v5
- name: Checkout repository
uses: actions/checkout@v4
- name: Setup virtual environment
working-directory: ./tests/system
run: |
sudo apt-get update
pip3 install virtualenv
python3 -m venv .venv
source .venv/bin/activate
pip3 install -r ./requirements.txt
pip3 install flake8 pycodestyle isort mypy black
- name: flake8
if: always()
working-directory: ./tests/system
run: source .venv/bin/activate && flake8 .
- name: pycodestyle
if: always()
working-directory: ./tests/system
run: source .venv/bin/activate && pycodestyle .
- name: isort
if: always()
working-directory: ./tests/system
run: source .venv/bin/activate && isort --check-only .
- name: black
if: always()
working-directory: ./tests/system
run: source .venv/bin/activate && black --check --diff .
- name: mypy
if: always()
working-directory: ./tests/system
run: source .venv/bin/activate && mypy --install-types --non-interactive tests

10
.gitignore vendored
View File

@ -21,24 +21,16 @@ test-driver
/ABOUT-NLS
/aclocal.m4
/autom4te.cache
/compile
/config.cache
/config.guess
/config.h
/config.h.in
/config.log
/config.rpath
/config.status
/config.sub
/configure
/depcomp
/install-sh
/libtool
/ltmain.sh
/m4
/missing
/build-aux/
/stamp-h1
/test-driver
/ylwrap
/po/*.header

View File

@ -1,4 +1,5 @@
## Process this file with automake to produce Makefile.in
ACLOCAL_AMFLAGS = -I m4
SUBDIRS = lib

View File

@ -5,12 +5,21 @@ autoreconf -v -f --install "$(dirname "$0")" || exit 1
CFLAGS="-O2"
CFLAGS="$CFLAGS -Wall"
CFLAGS="$CFLAGS -Wextra"
CFLAGS="$CFLAGS -Werror=discarded-qualifiers"
CFLAGS="$CFLAGS -Werror=implicit-function-declaration"
CFLAGS="$CFLAGS -Werror=implicit-int"
CFLAGS="$CFLAGS -Werror=incompatible-pointer-types"
CFLAGS="$CFLAGS -Werror=int-conversion"
CFLAGS="$CFLAGS -Werror=sign-compare"
CFLAGS="$CFLAGS -Werror=sizeof-pointer-div"
CFLAGS="$CFLAGS -Werror=unused-but-set-parameter"
CFLAGS="$CFLAGS -Werror=unused-function"
CFLAGS="$CFLAGS -Werror=unused-label"
CFLAGS="$CFLAGS -Werror=unused-local-typedefs"
CFLAGS="$CFLAGS -Werror=unused-parameter"
CFLAGS="$CFLAGS -Werror=unused-variable"
CFLAGS="$CFLAGS -Werror=unused-const-variable=1"
CFLAGS="$CFLAGS -Werror=unused-value"
CFLAGS="$CFLAGS -Wno-expansion-to-defined"
CFLAGS="$CFLAGS -Wno-unknown-attributes"
CFLAGS="$CFLAGS -Wno-unknown-warning-option"
@ -18,6 +27,7 @@ CFLAGS="$CFLAGS -Wno-unknown-warning-option"
"$(dirname "$0")"/configure \
CFLAGS="$CFLAGS" \
--enable-lastlog \
--disable-logind \
--enable-man \
--enable-maintainer-mode \
--enable-shared \

View File

@ -1,24 +1,29 @@
dnl Process this file with autoconf to produce a configure script.
AC_PREREQ([2.69])
m4_define([libsubid_abi_major], 5)
m4_define([libsubid_abi_minor], 0)
m4_define([libsubid_abi_micro], 0)
m4_define([libsubid_abi_major], [5])
m4_define([libsubid_abi_minor], [0])
m4_define([libsubid_abi_micro], [0])
m4_define([libsubid_abi], [libsubid_abi_major.libsubid_abi_minor.libsubid_abi_micro])
AC_INIT([shadow], [4.18.0], [pkg-shadow-devel@lists.alioth.debian.org], [],
AC_INIT([shadow], [4.19.0], [pkg-shadow-devel@lists.alioth.debian.org], [],
[https://github.com/shadow-maint/shadow])
AM_INIT_AUTOMAKE([1.11 foreign dist-xz subdir-objects tar-pax])
AC_CONFIG_MACRO_DIRS([m4])
AM_SILENT_RULES([yes])
AC_CONFIG_AUX_DIR([build-aux])
AC_CONFIG_HEADERS([config.h])
AM_INIT_AUTOMAKE([1.11 foreign dist-xz subdir-objects tar-pax])
AM_SILENT_RULES([yes])
AC_SUBST([AM_DISTCHECK_CONFIGURE_FLAGS], ["$ac_configure_args"])
AC_SUBST([LIBSUBID_ABI_MAJOR], [libsubid_abi_major])
AC_SUBST([LIBSUBID_ABI_MINOR], [libsubid_abi_minor])
AC_SUBST([LIBSUBID_ABI_MICRO], [libsubid_abi_micro])
AC_SUBST([LIBSUBID_ABI], [libsubid_abi])
dnl Some hacks...
test "$prefix" = "NONE" && prefix="/usr"
test "$prefix" = "/usr" && exec_prefix=""
test "x$prefix" = "xNONE" && prefix="/usr"
test "X$prefix" = "X/usr" && exec_prefix=""
AC_USE_SYSTEM_EXTENSIONS
@ -36,15 +41,15 @@ LT_LIB_DLLOAD
dnl Checks for libraries.
dnl shadow now uses the libc's shadow implementation
AC_CHECK_HEADER([shadow.h],,[AC_MSG_ERROR([You need a libc with shadow.h])])
AC_CHECK_HEADER([shadow.h],[],[AC_MSG_ERROR([You need a libc with shadow.h])])
AC_CHECK_FUNCS(arc4random_buf \
AC_CHECK_FUNCS([arc4random_buf \
getentropy getrandom \
lckpwdf lutimes \
updwtmpx innetgr \
getspnam_r \
rpmatch \
memset_explicit explicit_bzero stpecpy stpeprintf)
memset_explicit explicit_bzero stpecpy stpeprintf])
AC_SYS_LARGEFILE
dnl Checks for typedefs, structures, and compiler characteristics.
@ -55,227 +60,230 @@ AC_CHECK_MEMBERS([struct utmpx.ut_name,
struct utmpx.ut_addr,
struct utmpx.ut_addr_v6,
struct utmpx.ut_time,
struct utmpx.ut_xtime],,,[[#include <utmpx.h>]])
struct utmpx.ut_xtime],[],[],[[#include <utmpx.h>]])
dnl Checks for library functions.
AC_FUNC_UTIME_NULL
AC_REPLACE_FUNCS(putgrent putpwent putspent)
AC_REPLACE_FUNCS(sgetgrent sgetpwent sgetspent)
AC_REPLACE_FUNCS([putgrent putpwent putspent])
AC_REPLACE_FUNCS([sgetgrent sgetpwent sgetspent])
AC_CHECK_FUNC(setpgrp)
AC_CHECK_FUNC(secure_getenv, [AC_DEFINE(HAS_SECURE_GETENV,
1,
[Defined to 1 if you have the declaration of 'secure_getenv'])])
AC_CHECK_FUNC([setpgrp])
AC_CHECK_FUNC([secure_getenv],
[AC_DEFINE([HAS_SECURE_GETENV],[1],[Defined to 1 if you have the declaration of 'secure_getenv'])]
)
AC_CACHE_CHECK([location of shared mail directory], shadow_cv_maildir,
AC_CACHE_CHECK([location of shared mail directory], [shadow_cv_maildir],
[for shadow_cv_maildir in /var/mail /var/spool/mail /usr/spool/mail /usr/mail none; do
if test -d $shadow_cv_maildir; then
break
fi
done])
if test $shadow_cv_maildir != none; then
AC_DEFINE_UNQUOTED(MAIL_SPOOL_DIR, "$shadow_cv_maildir",
AC_DEFINE_UNQUOTED([MAIL_SPOOL_DIR], ["$shadow_cv_maildir"],
[Location of system mail spool directory.])
fi
AC_CACHE_CHECK([location of user mail file], shadow_cv_mailfile,
AC_CACHE_CHECK([location of user mail file], [shadow_cv_mailfile],
[for shadow_cv_mailfile in Mailbox mailbox Mail mail .mail none; do
if test -f $HOME/$shadow_cv_mailfile; then
break
fi
done])
if test $shadow_cv_mailfile != none; then
AC_DEFINE_UNQUOTED(MAIL_SPOOL_FILE, "$shadow_cv_mailfile",
AC_DEFINE_UNQUOTED([MAIL_SPOOL_FILE], ["$shadow_cv_mailfile"],
[Name of user's mail spool file if stored in user's home directory.])
fi
AC_CACHE_CHECK([location of faillog/lastlog/wtmp], shadow_cv_logdir,
AC_CACHE_CHECK([location of faillog/lastlog/wtmp], [shadow_cv_logdir],
[for shadow_cv_logdir in /var/log /var/adm /usr/adm /etc; do
if test -d $shadow_cv_logdir; then
break
fi
done])
AC_DEFINE_UNQUOTED(_WTMP_FILE, "$shadow_cv_logdir/wtmp",
[Path for wtmp file.])
AC_DEFINE_UNQUOTED(LASTLOG_FILE, "$shadow_cv_logdir/lastlog",
[Path for lastlog file.])
AC_DEFINE_UNQUOTED(FAILLOG_FILE, "$shadow_cv_logdir/faillog",
AC_DEFINE_UNQUOTED([FAILLOG_FILE], ["$shadow_cv_logdir/faillog"],
[Path for faillog file.])
AC_DEFINE_UNQUOTED(PASSWD_PROGRAM, "$exec_prefix/bin/passwd",
AC_DEFINE_UNQUOTED([PASSWD_PROGRAM], ["$exec_prefix/bin/passwd"],
[Path to passwd program.])
AC_ARG_ENABLE(shadowgrp,
AC_ARG_ENABLE([shadowgrp],
[AS_HELP_STRING([--enable-shadowgrp], [enable shadow group support @<:@default=yes@:>@])],
[case "${enableval}" in
yes) enable_shadowgrp="yes" ;;
no) enable_shadowgrp="no" ;;
*) AC_MSG_ERROR(bad value ${enableval} for --enable-shadowgrp) ;;
*) AC_MSG_ERROR([bad value ${enableval} for --enable-shadowgrp]) ;;
esac],
[enable_shadowgrp="yes"]
)
AC_ARG_ENABLE(man,
AC_ARG_ENABLE([man],
[AS_HELP_STRING([--enable-man],
[regenerate roff man pages from Docbook @<:@default=no@:>@])],
[enable_man="${enableval}"],
[enable_man="no"]
)
AC_ARG_ENABLE(account-tools-setuid,
AC_ARG_ENABLE([account-tools-setuid],
[AS_HELP_STRING([--enable-account-tools-setuid],
[Install the user and group management tools setuid and authenticate the callers. This requires --with-libpam.])],
[case "${enableval}" in
yes) enable_acct_tools_setuid="yes" ;;
no) enable_acct_tools_setuid="no" ;;
*) AC_MSG_ERROR(bad value ${enableval} for --enable-account-tools-setuid)
*) AC_MSG_ERROR([bad value ${enableval} for --enable-account-tools-setuid])
;;
esac],
[enable_acct_tools_setuid="no"]
)
AC_ARG_ENABLE(subordinate-ids,
AC_ARG_ENABLE([subordinate-ids],
[AS_HELP_STRING([--enable-subordinate-ids],
[support subordinate ids @<:@default=yes@:>@])],
[enable_subids="${enableval}"],
[enable_subids="maybe"]
)
AC_ARG_ENABLE(lastlog,
AC_ARG_ENABLE([lastlog],
[AS_HELP_STRING([--enable-lastlog],
[enable lastlog @<:@default=no@:>@])],
[enable_lastlog="${enableval}"],
[enable_lastlog="no"]
)
AC_ARG_ENABLE(logind,
[AS_HELP_STRING([--enable-logind],
[enable logind @<:@default=yes@:>@])],
[enable_logind="${enableval}"],
AC_ARG_ENABLE([logind],
[AS_HELP_STRING([--disable-logind],
[disable logind integration])],
[
AS_CASE([${enableval}],
[yes],[],
[no],[],
[AC_MSG_ERROR([bad parameter value for --enable-logind=${enableval}. ]dnl
[Supported values are: --enable-logind (or --enable-logind=yes) and --disable-logind (or --enable-logind=no).])]
)
],
[enable_logind="yes"]
)
AC_ARG_WITH(audit,
AC_ARG_WITH([audit],
[AS_HELP_STRING([--with-audit], [use auditing support @<:@default=yes if found@:>@])],
[with_audit=$withval], [with_audit=maybe])
AC_ARG_WITH(libpam,
AC_ARG_WITH([libpam],
[AS_HELP_STRING([--with-libpam], [use libpam for PAM support @<:@default=yes if found@:>@])],
[with_libpam=$withval], [with_libpam=maybe])
AC_ARG_WITH(btrfs,
AC_ARG_WITH([btrfs],
[AS_HELP_STRING([--with-btrfs], [add BtrFS support @<:@default=yes if found@:>@])],
[with_btrfs=$withval], [with_btrfs=maybe])
AC_ARG_WITH(selinux,
AC_ARG_WITH([selinux],
[AS_HELP_STRING([--with-selinux], [use SELinux support @<:@default=yes if found@:>@])],
[with_selinux=$withval], [with_selinux=maybe])
AC_ARG_WITH(acl,
AC_ARG_WITH([acl],
[AS_HELP_STRING([--with-acl], [use ACL support @<:@default=yes if found@:>@])],
[with_acl=$withval], [with_acl=maybe])
AC_ARG_WITH(attr,
AC_ARG_WITH([attr],
[AS_HELP_STRING([--with-attr], [use Extended Attribute support @<:@default=yes if found@:>@])],
[with_attr=$withval], [with_attr=maybe])
AC_ARG_WITH(skey,
AC_ARG_WITH([skey],
[AS_HELP_STRING([--with-skey], [use S/Key support @<:@default=no@:>@])],
[with_skey=$withval], [with_skey=no])
AC_ARG_WITH(tcb,
AC_ARG_WITH([tcb],
[AS_HELP_STRING([--with-tcb], [use tcb support (incomplete) @<:@default=yes if found@:>@])],
[with_tcb=$withval], [with_tcb=maybe])
AC_ARG_WITH(sha-crypt,
AC_ARG_WITH([sha-crypt],
[AS_HELP_STRING([--with-sha-crypt], [allow the SHA256 and SHA512 password encryption algorithms @<:@default=yes@:>@])],
[with_sha_crypt=$withval], [with_sha_crypt=yes])
AC_ARG_WITH(bcrypt,
AC_ARG_WITH([bcrypt],
[AS_HELP_STRING([--with-bcrypt], [allow the bcrypt password encryption algorithm @<:@default=no@:>@])],
[with_bcrypt=$withval], [with_bcrypt=no])
AC_ARG_WITH(yescrypt,
AC_ARG_WITH([yescrypt],
[AS_HELP_STRING([--with-yescrypt], [allow the yescrypt password encryption algorithm @<:@default=no@:>@])],
[with_yescrypt=$withval], [with_yescrypt=no])
AC_ARG_WITH(nscd,
AC_ARG_WITH([nscd],
[AS_HELP_STRING([--with-nscd], [enable support for nscd @<:@default=yes@:>@])],
[with_nscd=$withval], [with_nscd=yes])
AC_ARG_WITH(sssd,
AC_ARG_WITH([sssd],
[AS_HELP_STRING([--with-sssd], [enable support for flushing sssd caches @<:@default=yes@:>@])],
[with_sssd=$withval], [with_sssd=yes])
AC_ARG_WITH(group-name-max-length,
AC_ARG_WITH([group-name-max-length],
[AS_HELP_STRING([--with-group-name-max-length], [set max group name length @<:@default=32@:>@])],
[with_group_name_max_length=$withval], [with_group_name_max_length=yes])
AC_ARG_WITH(su,
AC_ARG_WITH([su],
[AS_HELP_STRING([--with-su], [build and install su program and man page @<:@default=yes@:>@])],
[with_su=$withval], [with_su=yes])
AC_ARG_WITH(libbsd,
AC_ARG_WITH([libbsd],
[AS_HELP_STRING([--with-libbsd], [use libbsd support @<:@default=yes if found@:>@])],
[with_libbsd=$withval], [with_libbsd=yes])
if test "$with_group_name_max_length" = "no" ; then
if test "X$with_group_name_max_length" = "Xno" ; then
with_group_name_max_length=0
elif test "$with_group_name_max_length" = "yes" ; then
elif test "X$with_group_name_max_length" = "Xyes" ; then
with_group_name_max_length=32
fi
AC_DEFINE_UNQUOTED(GROUP_NAME_MAX_LENGTH, $with_group_name_max_length, [max group name length])
AC_SUBST(GROUP_NAME_MAX_LENGTH)
AC_DEFINE_UNQUOTED([GROUP_NAME_MAX_LENGTH], [$with_group_name_max_length], [max group name length])
AC_SUBST([GROUP_NAME_MAX_LENGTH])
GROUP_NAME_MAX_LENGTH="$with_group_name_max_length"
AM_CONDITIONAL(USE_SHA_CRYPT, test "x$with_sha_crypt" = "xyes")
if test "$with_sha_crypt" = "yes"; then
AC_DEFINE(USE_SHA_CRYPT, 1, [Define to allow the SHA256 and SHA512 password encryption algorithms])
AM_CONDITIONAL([USE_SHA_CRYPT], [test "x$with_sha_crypt" = "xyes"])
if test "X$with_sha_crypt" = "Xyes"; then
AC_DEFINE([USE_SHA_CRYPT], [1], [Define to allow the SHA256 and SHA512 password encryption algorithms])
fi
AM_CONDITIONAL(USE_BCRYPT, test "x$with_bcrypt" = "xyes")
if test "$with_bcrypt" = "yes"; then
AC_DEFINE(USE_BCRYPT, 1, [Define to allow the bcrypt password encryption algorithm])
AM_CONDITIONAL([USE_BCRYPT], [test "x$with_bcrypt" = "xyes"])
if test "X$with_bcrypt" = "Xyes"; then
AC_DEFINE([USE_BCRYPT], [1], [Define to allow the bcrypt password encryption algorithm])
fi
AM_CONDITIONAL(USE_YESCRYPT, test "x$with_yescrypt" = "xyes")
if test "$with_yescrypt" = "yes"; then
AC_DEFINE(USE_YESCRYPT, 1, [Define to allow the yescrypt password encryption algorithm])
AM_CONDITIONAL([USE_YESCRYPT], [test "x$with_yescrypt" = "xyes"])
if test "X$with_yescrypt" = "Xyes"; then
AC_DEFINE([USE_YESCRYPT], [1], [Define to allow the yescrypt password encryption algorithm])
fi
if test "$with_nscd" = "yes"; then
AC_CHECK_FUNC(posix_spawn,
[AC_DEFINE(USE_NSCD, 1, [Define to support flushing of nscd caches])],
if test "X$with_nscd" = "Xyes"; then
AC_CHECK_FUNC([posix_spawn],
[AC_DEFINE([USE_NSCD], [1], [Define to support flushing of nscd caches])],
[AC_MSG_ERROR([posix_spawn is needed for nscd support])])
fi
if test "$with_sssd" = "yes"; then
AC_CHECK_FUNC(posix_spawn,
[AC_DEFINE(USE_SSSD, 1, [Define to support flushing of sssd caches])],
if test "X$with_sssd" = "Xyes"; then
AC_CHECK_FUNC([posix_spawn],
[AC_DEFINE([USE_SSSD], [1], [Define to support flushing of sssd caches])],
[AC_MSG_ERROR([posix_spawn is needed for sssd support])])
fi
AS_IF([test "$with_su" != "no"], AC_DEFINE(WITH_SU, 1, [Build with su]))
AS_IF([test "$with_su" != "no"], [AC_DEFINE([WITH_SU], [1], [Build with su])])
AM_CONDITIONAL([WITH_SU], [test "x$with_su" != "xno"])
dnl Check for some functions in libc first, only if not found check for
dnl other libraries. This should prevent linking libnsl if not really
dnl needed (Linux glibc, Irix), but still link it if needed (Solaris).
AC_SEARCH_LIBS(gethostbyname, nsl)
AC_SEARCH_LIBS([gethostbyname], [nsl])
PKG_CHECK_MODULES([CMOCKA], [cmocka], [have_cmocka="yes"],
[AC_MSG_WARN([libcmocka not found, cmocka tests will not be built])])
AM_CONDITIONAL([HAVE_CMOCKA], [test x$have_cmocka = xyes])
AC_ARG_ENABLE([vendordir],
[AS_HELP_STRING([--enable-vendordir=DIR], [Directory for distribution provided configuration files])],[],[])
AC_CHECK_LIB([econf],[econf_readDirs],[LIBECONF="-leconf"],[LIBECONF=""])
if test -n "$LIBECONF"; then
AC_DEFINE_UNQUOTED([VENDORDIR], ["$enable_vendordir"],
[Directory for distribution provided configuration files])
ECONF_CPPFLAGS="-DUSE_ECONF=1"
AC_ARG_ENABLE([vendordir],
AS_HELP_STRING([--enable-vendordir=DIR], [Directory for distribution provided configuration files]),,[])
fi
AC_SUBST(ECONF_CPPFLAGS)
AC_SUBST(LIBECONF)
AC_SUBST([ECONF_CPPFLAGS])
AC_SUBST([LIBECONF])
AC_SUBST([VENDORDIR], [$enable_vendordir])
if test "x$enable_vendordir" != x; then
AC_DEFINE(HAVE_VENDORDIR, 1, [Define to support vendor settings.])
AC_DEFINE([HAVE_VENDORDIR], [1], [Define to support vendor settings.])
fi
AM_CONDITIONAL([HAVE_VENDORDIR], [test "x$enable_vendordir" != x])
if test "$enable_shadowgrp" = "yes"; then
AC_DEFINE(SHADOWGRP, 1, [Define to support the shadow group file.])
if test "X$enable_shadowgrp" = "Xyes"; then
AC_DEFINE([SHADOWGRP], [1], [Define to support the shadow group file.])
fi
AM_CONDITIONAL(SHADOWGRP, test "x$enable_shadowgrp" = "xyes")
AM_CONDITIONAL([SHADOWGRP], [test "x$enable_shadowgrp" = "xyes"])
if test "$enable_man" = "yes"; then
if test "X$enable_man" = "Xyes"; then
dnl
dnl Check for xsltproc
dnl
@ -287,21 +295,21 @@ if test "$enable_man" = "yes"; then
dnl check for DocBook DTD and stylesheets in the local catalog.
JH_CHECK_XML_CATALOG([-//OASIS//DTD DocBook XML V4.5//EN],
[DocBook XML DTD V4.5], [], enable_man=no)
[DocBook XML DTD V4.5], [], [enable_man=no])
JH_CHECK_XML_CATALOG([http://docbook.sourceforge.net/release/xsl/current/manpages/docbook.xsl],
[DocBook XSL Stylesheets >= 1.70.1], [], enable_man=no)
[DocBook XSL Stylesheets >= 1.70.1], [], [enable_man=no])
fi
AM_CONDITIONAL(ENABLE_REGENERATE_MAN, test "x$enable_man" != "xno")
AM_CONDITIONAL([ENABLE_REGENERATE_MAN], [test "x$enable_man" != "xno"])
if test "$enable_subids" != "no"; then
dnl
dnl FIXME: check if 32 bit UIDs/GIDs are supported by libc
dnl
AC_CHECK_SIZEOF([uid_t],, [#include "sys/types.h"])
AC_CHECK_SIZEOF([gid_t],, [#include "sys/types.h"])
AC_CHECK_SIZEOF([uid_t],[], [[#include "sys/types.h"]])
AC_CHECK_SIZEOF([gid_t],[], [[#include "sys/types.h"]])
if test "$ac_cv_sizeof_uid_t" -ge 4 && test "$ac_cv_sizeof_gid_t" -ge 4; then
AC_DEFINE(ENABLE_SUBIDS, 1, [Define to support the subordinate IDs.])
AC_DEFINE([ENABLE_SUBIDS], [1], [Define to support the subordinate IDs.])
enable_subids="yes"
else
if test "x$enable_subids" = "xyes"; then
@ -310,55 +318,54 @@ if test "$enable_subids" != "no"; then
enable_subids="no"
fi
fi
AM_CONDITIONAL(ENABLE_SUBIDS, test "x$enable_subids" != "xno")
AM_CONDITIONAL([ENABLE_SUBIDS], [test "x$enable_subids" != "xno"])
if test "$enable_lastlog" = "yes" && test "$ac_cv_header_lastlog_h" = "yes"; then
AC_CACHE_CHECK(for ll_host in struct lastlog,
ac_cv_struct_lastlog_ll_host,
AC_COMPILE_IFELSE([AC_LANG_PROGRAM([#include <lastlog.h>],
[struct lastlog ll; char *cp = ll.ll_host;]
)],
if test "X$enable_lastlog" = "Xyes" && test "X$ac_cv_header_lastlog_h" = "Xyes"; then
AC_CACHE_CHECK([for ll_host in struct lastlog],
[ac_cv_struct_lastlog_ll_host],
[AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include <lastlog.h>]],
[struct lastlog ll; char *cp = ll.ll_host;])],
[ac_cv_struct_lastlog_ll_host=yes],
[ac_cv_struct_lastlog_ll_host=no]
)
[ac_cv_struct_lastlog_ll_host=no])
]
)
if test "$ac_cv_struct_lastlog_ll_host" = "yes"; then
AC_DEFINE(HAVE_LL_HOST, 1,
if test "X$ac_cv_struct_lastlog_ll_host" = "Xyes"; then
AC_DEFINE([HAVE_LL_HOST], [1],
[Define if struct lastlog has ll_host])
AC_DEFINE(ENABLE_LASTLOG, 1, [Define to support lastlog.])
AC_DEFINE([ENABLE_LASTLOG], [1], [Define to support lastlog.])
enable_lastlog="yes"
else
AC_MSG_ERROR([Cannot enable support for lastlog on systems where the data structures aren't available])
enable_lastlog="no"
fi
fi
AM_CONDITIONAL(ENABLE_LASTLOG, test "x$enable_lastlog" != "xno")
AM_CONDITIONAL([ENABLE_LASTLOG], [test "x$enable_lastlog" != "xno"])
AC_SUBST(LIBSYSTEMD)
if test "$enable_logind" = "yes"; then
AC_CHECK_LIB(systemd, sd_session_get_remote_host,
[enable_logind="yes"; [LIBSYSTEMD=-lsystemd];
AC_DEFINE(ENABLE_LOGIND, 1,
[Define to manage session support with logind.])],
[enable_logind="no"])
AC_SUBST([LIBSYSTEMD])
if test "X$enable_logind" = "Xyes"; then
AC_CHECK_LIB([systemd], [sd_session_get_remote_host],
[
LIBSYSTEMD=-lsystemd
AC_DEFINE([ENABLE_LOGIND], [1], [Define to manage session support with logind.])
],[
AC_MSG_ERROR([libsystemd not found. If logind integration is not required, disable it using the --disable-logind option. ]dnl
[ If logind integration is required, consider installing systemd or another package that provides libsystemd.])
]
)
fi
AM_CONDITIONAL(ENABLE_LOGIND, test "x$enable_logind" != "xno")
AM_CONDITIONAL([ENABLE_LOGIND], [test "X$enable_logind" = "Xyes"])
AC_SUBST(LIBCRYPT)
AC_CHECK_LIB(crypt, crypt, [LIBCRYPT=-lcrypt],
AC_CHECK_LIB([crypt], [crypt], [LIBCRYPT=-lcrypt],
[AC_MSG_ERROR([crypt() not found])])
AC_SUBST([LIBCRYPT])
AC_SUBST(LIYESCRYPT)
AC_CHECK_LIB(crypt, crypt, [LIYESCRYPT=-lcrypt],
[AC_MSG_ERROR([crypt() not found])])
AC_SUBST(LIBBSD)
AC_SUBST([LIBBSD])
if test "$with_libbsd" != "no"; then
AC_SEARCH_LIBS([readpassphrase], [bsd], [], [
AC_MSG_ERROR([readpassphrase() is missing, either from libc or libbsd])
])
AS_IF([test "$ac_cv_search_readpassphrase" = "-lbsd"], [
AS_IF([test "X$ac_cv_search_readpassphrase" = "X-lbsd"], [
PKG_CHECK_MODULES([LIBBSD], [libbsd-overlay])
])
dnl Make sure either the libc or libbsd provide the header.
@ -369,29 +376,29 @@ if test "$with_libbsd" != "no"; then
AC_MSG_ERROR([readpassphrase.h is missing])
])
CFLAGS="$save_CFLAGS"
AC_DEFINE(WITH_LIBBSD, 1, [Build shadow with libbsd support])
AC_DEFINE([WITH_LIBBSD], [1], [Build shadow with libbsd support])
else
AC_DEFINE(WITH_LIBBSD, 0, [Build shadow without libbsd support])
AC_DEFINE([WITH_LIBBSD], [0], [Build shadow without libbsd support])
fi
AM_CONDITIONAL(WITH_LIBBSD, test x$with_libbsd = xyes)
AM_CONDITIONAL([WITH_LIBBSD], [test x$with_libbsd = xyes])
AC_SUBST(LIBACL)
AC_SUBST([LIBACL])
if test "$with_acl" != "no"; then
AC_CHECK_HEADERS(acl/libacl.h attr/error_context.h, [acl_header="yes"], [acl_header="no"])
if test "$acl_header$with_acl" = "noyes" ; then
AC_CHECK_HEADERS([acl/libacl.h attr/error_context.h], [acl_header="yes"], [acl_header="no"])
if test "X$acl_header$with_acl" = "Xnoyes" ; then
AC_MSG_ERROR([acl/libacl.h or attr/error_context.h is missing])
elif test "$acl_header" = "yes" ; then
AC_CHECK_LIB(acl, perm_copy_file,
[AC_CHECK_LIB(acl, perm_copy_fd,
elif test "X$acl_header" = "Xyes" ; then
AC_CHECK_LIB([acl], [perm_copy_file],
[AC_CHECK_LIB([acl], [perm_copy_fd],
[acl_lib="yes"],
[acl_lib="no"])],
[acl_lib="no"])
if test "$acl_lib$with_acl" = "noyes" ; then
if test "X$acl_lib$with_acl" = "Xnoyes" ; then
AC_MSG_ERROR([libacl not found])
elif test "$acl_lib" = "no" ; then
elif test "X$acl_lib" = "Xno" ; then
with_acl="no"
else
AC_DEFINE(WITH_ACL, 1,
AC_DEFINE([WITH_ACL], [1],
[Build shadow with ACL support])
LIBACL="-lacl"
with_acl="yes"
@ -401,55 +408,29 @@ if test "$with_acl" != "no"; then
fi
fi
AC_SUBST(LIBATTR)
if test "$with_attr" != "no"; then
AC_CHECK_HEADERS(attr/libattr.h attr/error_context.h, [attr_header="yes"], [attr_header="no"])
if test "$attr_header$with_attr" = "noyes" ; then
AC_MSG_ERROR([attr/libattr.h or attr/error_context.h is missing])
elif test "$attr_header" = "yes" ; then
AC_CHECK_LIB(attr, attr_copy_file,
[AC_CHECK_LIB(attr, attr_copy_fd,
[attr_lib="yes"],
[attr_lib="no"])],
[attr_lib="no"])
if test "$attr_lib$with_attr" = "noyes" ; then
AC_MSG_ERROR([libattr not found])
elif test "$attr_lib" = "no" ; then
with_attr="no"
else
AC_DEFINE(WITH_ATTR, 1,
[Build shadow with Extended Attributes support])
LIBATTR="-lattr"
with_attr="yes"
fi
else
with_attr="no"
fi
fi
AC_SUBST(LIBAUDIT)
AC_SUBST([LIBAUDIT])
if test "$with_audit" != "no"; then
AC_CHECK_HEADER(libaudit.h, [audit_header="yes"], [audit_header="no"])
if test "$audit_header$with_audit" = "noyes" ; then
AC_CHECK_HEADER([libaudit.h], [audit_header="yes"], [audit_header="no"])
if test "X$audit_header$with_audit" = "Xnoyes" ; then
AC_MSG_ERROR([libaudit.h is missing])
elif test "$audit_header" = "yes"; then
AC_CHECK_DECL(AUDIT_ADD_USER,,[audit_header="no"],[#include <libaudit.h>])
AC_CHECK_DECL(AUDIT_DEL_USER,,[audit_header="no"],[#include <libaudit.h>])
AC_CHECK_DECL(AUDIT_ADD_GROUP,,[audit_header="no"],[#include <libaudit.h>])
AC_CHECK_DECL(AUDIT_DEL_GROUP,,[audit_header="no"],[#include <libaudit.h>])
if test "$audit_header$with_audit" = "noyes" ; then
elif test "X$audit_header" = "Xyes"; then
AC_CHECK_DECL([AUDIT_ADD_USER],[],[audit_header="no"],[[#include <libaudit.h>]])
AC_CHECK_DECL([AUDIT_DEL_USER],[],[audit_header="no"],[[#include <libaudit.h>]])
AC_CHECK_DECL([AUDIT_ADD_GROUP],[],[audit_header="no"],[[#include <libaudit.h>]])
AC_CHECK_DECL([AUDIT_DEL_GROUP],[],[audit_header="no"],[[#include <libaudit.h>]])
if test "X$audit_header$with_audit" = "Xnoyes" ; then
AC_MSG_ERROR([AUDIT_ADD_USER AUDIT_DEL_USER AUDIT_ADD_GROUP or AUDIT_DEL_GROUP missing from libaudit.h])
fi
fi
if test "$audit_header" = "yes"; then
AC_CHECK_LIB(audit, audit_log_acct_message,
if test "X$audit_header" = "Xyes"; then
AC_CHECK_LIB([audit], [audit_log_acct_message],
[audit_lib="yes"], [audit_lib="no"])
if test "$audit_lib$with_audit" = "noyes" ; then
if test "X$audit_lib$with_audit" = "Xnoyes" ; then
AC_MSG_ERROR([libaudit not found])
elif test "$audit_lib" = "no" ; then
elif test "X$audit_lib" = "Xno" ; then
with_audit="no"
else
AC_DEFINE(WITH_AUDIT, 1,
AC_DEFINE([WITH_AUDIT], [1],
[Define if you want to enable Audit messages])
LIBAUDIT="-laudit"
with_audit="yes"
@ -462,43 +443,43 @@ fi
if test "$with_btrfs" != "no"; then
AC_CHECK_HEADERS([sys/statfs.h linux/magic.h linux/btrfs_tree.h], \
[btrfs_headers="yes"], [btrfs_headers="no"])
if test "$btrfs_headers$with_btrfs" = "noyes" ; then
if test "X$btrfs_headers$with_btrfs" = "Xnoyes" ; then
AC_MSG_ERROR([One of sys/statfs.h linux/magic.h linux/btrfs_tree.h is missing])
fi
if test "$btrfs_headers" = "yes" ; then
AC_DEFINE(WITH_BTRFS, 1, [Build shadow with BtrFS support])
if test "X$btrfs_headers" = "Xyes" ; then
AC_DEFINE([WITH_BTRFS], [1], [Build shadow with BtrFS support])
with_btrfs="yes"
fi
fi
AM_CONDITIONAL(WITH_BTRFS, test x$with_btrfs = xyes)
AM_CONDITIONAL([WITH_BTRFS], [test x$with_btrfs = xyes])
AC_SUBST(LIBSELINUX)
AC_SUBST(LIBSEMANAGE)
AC_SUBST([LIBSELINUX])
AC_SUBST([LIBSEMANAGE])
if test "$with_selinux" != "no"; then
AC_CHECK_HEADERS(selinux/selinux.h, [selinux_header="yes"], [selinux_header="no"])
if test "$selinux_header$with_selinux" = "noyes" ; then
AC_CHECK_HEADERS([selinux/selinux.h], [selinux_header="yes"], [selinux_header="no"])
if test "X$selinux_header$with_selinux" = "Xnoyes" ; then
AC_MSG_ERROR([selinux/selinux.h is missing])
fi
AC_CHECK_HEADERS(semanage/semanage.h, [semanage_header="yes"], [semanage_header="no"])
if test "$semanage_header$with_selinux" = "noyes" ; then
AC_CHECK_HEADERS([semanage/semanage.h], [semanage_header="yes"], [semanage_header="no"])
if test "X$semanage_header$with_selinux" = "Xnoyes" ; then
AC_MSG_ERROR([semanage/semanage.h is missing])
fi
if test "$selinux_header$semanage_header" = "yesyes" ; then
AC_CHECK_LIB(selinux, is_selinux_enabled, [selinux_lib="yes"], [selinux_lib="no"])
if test "$selinux_lib$with_selinux" = "noyes" ; then
if test "X$selinux_header$semanage_header" = "Xyesyes" ; then
AC_CHECK_LIB([selinux], [is_selinux_enabled], [selinux_lib="yes"], [selinux_lib="no"])
if test "X$selinux_lib$with_selinux" = "Xnoyes" ; then
AC_MSG_ERROR([libselinux not found])
fi
AC_CHECK_LIB(semanage, semanage_connect, [semanage_lib="yes"], [semanage_lib="no"])
if test "$semanage_lib$with_selinux" = "noyes" ; then
AC_CHECK_LIB([semanage], [semanage_connect], [semanage_lib="yes"], [semanage_lib="no"])
if test "X$semanage_lib$with_selinux" = "Xnoyes" ; then
AC_MSG_ERROR([libsemanage not found])
fi
if test "$selinux_lib$semanage_lib" = "yesyes" ; then
AC_DEFINE(WITH_SELINUX, 1,
if test "X$selinux_lib$semanage_lib" = "Xyesyes" ; then
AC_DEFINE([WITH_SELINUX], [1],
[Build shadow with SELinux support])
LIBSELINUX="-lselinux"
LIBSEMANAGE="-lsemanage"
@ -511,19 +492,19 @@ if test "$with_selinux" != "no"; then
fi
fi
AC_SUBST(LIBTCB)
AC_SUBST([LIBTCB])
if test "$with_tcb" != "no"; then
AC_CHECK_HEADERS(tcb.h, [tcb_header="yes"], [tcb_header="no"])
if test "$tcb_header$with_tcb" = "noyes" ; then
AC_CHECK_HEADERS([tcb.h], [tcb_header="yes"], [tcb_header="no"])
if test "X$tcb_header$with_tcb" = "Xnoyes" ; then
AC_MSG_ERROR([tcb.h is missing])
elif test "$tcb_header" = "yes" ; then
AC_CHECK_LIB(tcb, tcb_is_suspect, [tcb_lib="yes"], [tcb_lib="no"])
if test "$tcb_lib$with_tcb" = "noyes" ; then
elif test "X$tcb_header" = "Xyes" ; then
AC_CHECK_LIB([tcb], [tcb_is_suspect], [tcb_lib="yes"], [tcb_lib="no"])
if test "X$tcb_lib$with_tcb" = "Xnoyes" ; then
AC_MSG_ERROR([libtcb not found])
elif test "$tcb_lib" = "no" ; then
elif test "X$tcb_lib" = "Xno" ; then
with_tcb="no"
else
AC_DEFINE(WITH_TCB, 1, [Build shadow with tcb support (incomplete)])
AC_DEFINE([WITH_TCB], [1], [Build shadow with tcb support (incomplete)])
LIBTCB="-ltcb"
with_tcb="yes"
fi
@ -531,39 +512,39 @@ if test "$with_tcb" != "no"; then
with_tcb="no"
fi
fi
AM_CONDITIONAL(WITH_TCB, test x$with_tcb = xyes)
AM_CONDITIONAL([WITH_TCB], [test x$with_tcb = xyes])
AC_SUBST(LIBPAM)
AC_SUBST([LIBPAM])
if test "$with_libpam" != "no"; then
AC_CHECK_LIB(pam, pam_start,
AC_CHECK_LIB([pam], [pam_start],
[pam_lib="yes"], [pam_lib="no"])
if test "$pam_lib$with_libpam" = "noyes" ; then
AC_MSG_ERROR(libpam not found)
if test "X$pam_lib$with_libpam" = "Xnoyes" ; then
AC_MSG_ERROR([libpam not found])
fi
LIBPAM="-lpam"
pam_conv_function="no"
AC_CHECK_LIB(pam, openpam_ttyconv,
AC_CHECK_LIB([pam], [openpam_ttyconv],
[pam_conv_function="openpam_ttyconv"],
AC_CHECK_LIB(pam_misc, misc_conv,
[pam_conv_function="misc_conv"; LIBPAM="$LIBPAM -lpam_misc"])
[AC_CHECK_LIB([pam_misc], [misc_conv],
[pam_conv_function="misc_conv"; LIBPAM="$LIBPAM -lpam_misc"])]
)
if test "$pam_conv_function$with_libpam" = "noyes" ; then
AC_MSG_ERROR(PAM conversation function not found)
if test "X$pam_conv_function$with_libpam" = "Xnoyes" ; then
AC_MSG_ERROR([PAM conversation function not found])
fi
pam_headers_found=no
AC_CHECK_HEADERS( [security/openpam.h security/pam_misc.h],
[ pam_headers_found=yes ; break ], [],
[ #include <security/pam_appl.h> ] )
if test "$pam_headers_found$with_libpam" = "noyes" ; then
AC_MSG_ERROR(PAM headers not found)
[[#include <security/pam_appl.h>]] )
if test "X$pam_headers_found$with_libpam" = "Xnoyes" ; then
AC_MSG_ERROR([PAM headers not found])
fi
if test "$pam_lib$pam_headers_found" = "yesyes" -a "$pam_conv_function" != "no" ; then
if test "X$pam_lib$pam_headers_found" = "Xyesyes" && test "$pam_conv_function" != "no" ; then
with_libpam="yes"
else
with_libpam="no"
@ -571,87 +552,86 @@ if test "$with_libpam" != "no"; then
fi
fi
dnl Now with_libpam is either yes or no
if test "$with_libpam" = "yes"; then
if test "X$with_libpam" = "Xyes"; then
AC_CHECK_DECLS([PAM_ESTABLISH_CRED,
PAM_DELETE_CRED,
PAM_NEW_AUTHTOK_REQD,
PAM_DATA_SILENT],
[], [], [#include <security/pam_appl.h>])
[], [], [[#include <security/pam_appl.h>]])
save_libs=$LIBS
LIBS="$LIBS $LIBPAM"
# We do not use AC_CHECK_FUNCS to avoid duplicated definition with
# Linux PAM.
AC_CHECK_FUNC(pam_fail_delay, [AC_DEFINE(HAS_PAM_FAIL_DELAY, 1, [Define to 1 if you have the declaration of 'pam_fail_delay'])])
AC_CHECK_FUNC([pam_fail_delay], [AC_DEFINE([HAS_PAM_FAIL_DELAY], [1], [Define to 1 if you have the declaration of 'pam_fail_delay'])])
LIBS=$save_libs
AC_DEFINE(USE_PAM, 1, [Define to support Pluggable Authentication Modules])
AC_DEFINE_UNQUOTED(SHADOW_PAM_CONVERSATION, [$pam_conv_function],[PAM conversation to use])
AM_CONDITIONAL(USE_PAM, [true])
AC_DEFINE([USE_PAM], [1], [Define to support Pluggable Authentication Modules])
AC_DEFINE_UNQUOTED([SHADOW_PAM_CONVERSATION], [$pam_conv_function],[PAM conversation to use])
AC_MSG_CHECKING(use login and su access checking if PAM not used)
AC_MSG_RESULT(no)
AC_MSG_CHECKING([use login and su access checking if PAM not used])
AC_MSG_RESULT([no])
else
AC_DEFINE(SU_ACCESS, 1, [Define to support /etc/suauth su access control.])
AM_CONDITIONAL(USE_PAM, [false])
AC_MSG_CHECKING(use login and su access checking if PAM not used)
AC_MSG_RESULT(yes)
AC_DEFINE([SU_ACCESS], [1], [Define to support /etc/suauth su access control.])
AC_MSG_CHECKING([use login and su access checking if PAM not used])
AC_MSG_RESULT([yes])
fi
AM_CONDITIONAL([USE_PAM], [test "X$with_libpam" = "Xyes"])
if test "$enable_acct_tools_setuid" != "no"; then
if test "$with_libpam" != "yes"; then
if test "$enable_acct_tools_setuid" = "yes"; then
AC_MSG_ERROR(PAM support is required for --enable-account-tools-setuid)
if test "X$enable_acct_tools_setuid" = "Xyes"; then
AC_MSG_ERROR([PAM support is required for --enable-account-tools-setuid])
else
enable_acct_tools_setuid="no"
fi
else
enable_acct_tools_setuid="yes"
fi
if test "$enable_acct_tools_setuid" = "yes"; then
AC_DEFINE(ACCT_TOOLS_SETUID,
1,
if test "X$enable_acct_tools_setuid" = "Xyes"; then
AC_DEFINE([ACCT_TOOLS_SETUID],
[1],
[Define if account management tools should be installed setuid and authenticate the callers])
fi
fi
AM_CONDITIONAL(ACCT_TOOLS_SETUID, test "x$enable_acct_tools_setuid" = "xyes")
AM_CONDITIONAL([ACCT_TOOLS_SETUID], [test "x$enable_acct_tools_setuid" = "xyes"])
AC_ARG_WITH(fcaps,
AC_ARG_WITH([fcaps],
[AS_HELP_STRING([--with-fcaps], [use file capabilities instead of suid binaries for newuidmap/newgidmap @<:@default=no@:>@])],
[with_fcaps=$withval], [with_fcaps=no])
AM_CONDITIONAL(FCAPS, test "x$with_fcaps" = "xyes")
AM_CONDITIONAL([FCAPS], [test "x$with_fcaps" = "xyes"])
if test "x$with_fcaps" = "xyes"; then
AC_CHECK_PROGS(capcmd, "setcap")
AC_CHECK_PROGS([capcmd], ["setcap"])
if test "x$capcmd" = "x" ; then
AC_MSG_ERROR([setcap command not available])
fi
fi
AC_SUBST(LIBSKEY)
AC_SUBST(LIBMD)
if test "$with_skey" = "yes"; then
AC_CHECK_LIB(md, MD5Init, [LIBMD=-lmd])
AC_CHECK_LIB(skey, skeychallenge, [LIBSKEY=-lskey],
AC_SUBST([LIBSKEY])
AC_SUBST([LIBMD])
if test "X$with_skey" = "Xyes"; then
AC_CHECK_LIB([md], [MD5Init], [LIBMD=-lmd])
AC_CHECK_LIB([skey], [skeychallenge], [LIBSKEY=-lskey],
[AC_MSG_ERROR([libskey missing. You can download S/Key source code from http://rsync1.it.gentoo.org/gentoo/distfiles/skey-1.1.5.tar.bz2])])
AC_DEFINE(SKEY, 1, [Define to support S/Key logins.])
AC_DEFINE([SKEY], [1], [Define to support S/Key logins.])
AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[
#include <stdio.h>
#include <skey.h>
]], [[
skeychallenge((void*)0, (void*)0, (void*)0, 0);
]])],[AC_DEFINE(SKEY_BSD_STYLE, 1, [Define to support newer BSD S/Key API])],[])
]])],[AC_DEFINE([SKEY_BSD_STYLE], [1], [Define to support newer BSD S/Key API])],[])
fi
AC_CHECK_FUNC(fgetpwent_r, [AC_DEFINE(HAVE_FGETPWENT_R, 1, [Defined to 1 if you have the declaration of 'fgetpwent_r'])])
AC_CHECK_FUNC([fgetpwent_r], [AC_DEFINE([HAVE_FGETPWENT_R], [1], [Defined to 1 if you have the declaration of 'fgetpwent_r'])])
AC_DEFINE_UNQUOTED(SHELL, ["$SHELL"], [The default shell.])
AC_DEFINE_UNQUOTED([SHELL], ["$SHELL"], [The default shell.])
AM_GNU_GETTEXT_VERSION([0.19])
AM_GNU_GETTEXT([external], [need-ngettext])
AM_CONDITIONAL(USE_NLS, test "x$USE_NLS" = "xyes")
AM_CONDITIONAL([USE_NLS], [test "x$USE_NLS" = "xyes"])
AC_CONFIG_FILES([
Makefile
@ -690,30 +670,28 @@ AC_CONFIG_FILES([
])
AC_OUTPUT
echo
echo "shadow will be compiled with the following features:"
echo
echo " auditing support: $with_audit"
echo " PAM support: $with_libpam"
if test "$with_libpam" = "yes"; then
echo " suid account management tools: $enable_acct_tools_setuid"
fi
echo " SELinux support: $with_selinux"
echo " BtrFS support: $with_btrfs"
echo " ACL support: $with_acl"
echo " Extended Attributes support: $with_attr"
echo " tcb support (incomplete): $with_tcb"
echo " shadow group support: $enable_shadowgrp"
echo " S/Key support: $with_skey"
echo " SHA passwords encryption: $with_sha_crypt"
echo " bcrypt passwords encryption: $with_bcrypt"
echo " yescrypt passwords encryption: $with_yescrypt"
echo " nscd support: $with_nscd"
echo " sssd support: $with_sssd"
echo " subordinate IDs support: $enable_subids"
echo " enable lastlog: $enable_lastlog"
echo " enable logind: $enable_logind"
echo " use file caps: $with_fcaps"
echo " install su: $with_su"
echo " enabled vendor dir: $enable_vendordir"
echo
AC_MSG_NOTICE([shadow ${PACKAGE_VERSION} has been configured with the following features:
auditing support: $with_audit
PAM support: $with_libpam
suid account management tools: $enable_acct_tools_setuid
SELinux support: $with_selinux
BtrFS support: $with_btrfs
ACL support: $with_acl
Extended Attributes support: $with_attr
tcb support (incomplete): $with_tcb
shadow group support: $enable_shadowgrp
S/Key support: $with_skey
SHA passwords encryption: $with_sha_crypt
bcrypt passwords encryption: $with_bcrypt
yescrypt passwords encryption: $with_yescrypt
nscd support: $with_nscd
sssd support: $with_sssd
subordinate IDs support: $enable_subids
enable lastlog: $enable_lastlog
enable logind: $enable_logind
use file caps: $with_fcaps
install su: $with_su
enabled vendor dir: $enable_vendordir
])

View File

@ -346,7 +346,7 @@
return value points to static data whose content is overwritten by
each call.
Warning: The key space consists of 2**56 equal 7.2e16 possible values.
Warning: the key space consists of 2**56 equal 7.2e16 possible values.
Exhaustive searches of this key space are possible using massively
parallel computers. Software, such as crack(1), is available which
will search the portion of this key space that is generally used by
@ -996,7 +996,7 @@
sudo for the shadow suite, is available as at:
<ftp://sunsite.unc.edu/pub/Linux/system/Admin/sudo-1.2-shadow.tgz>
Warning: When you install sudo your /etc/sudoers file will be replaced
Warning: when you install sudo your /etc/sudoers file will be replaced
with a default one, so you need to make a backup of it if you have
added anything to the default one. (you could also edit the Makefile
and remove the line that copies the default file to /etc).

View File

@ -73,3 +73,31 @@ were running some of the CI checks locally:
```
share/container-build.sh
```
### Container troubleshooting
When working with containers for testing or development,
you may encounter issues.
Here are common troubleshooting steps:
**Post-test inspection:**
- **Container persistence**: after tests complete, containers are left running
to allow inspection of the test environment and debugging of any failures.
This enables you to examine logs, file states, and system configuration
that existed when tests ran.
**Container management:**
- **List containers**: `docker ps -a` to see all containers and their status.
- **Access container**: `docker exec -it <container-name> bash` to get shell access.
- **Container logs**: `docker logs <container-name>` to view container output.
- **Remove containers**: `docker rm <container-name>` to clean up stopped containers.
**Common issues:**
- **Container not found**: ensure you've run the Ansible playbook
to create the required containers.
- **Permission issues**: verify the container has proper privileges
for user/group operations.
- **Network connectivity**: check that containers can communicate
if tests involve network operations.
- **Resource constraints**: ensure sufficient disk space and memory
for container operations.

View File

@ -12,9 +12,27 @@ be triggered locally by following the instructions specified in the
## System tests
The project is tested on Ubuntu. For that purpose it is built & installed in
this distribution in a VM. You can run this step locally by following the
instructions provided in the [Tests](tests.md#system-tests) page.
The project runs system tests to verify functionality
across different environments
using two complementary approaches:
### Bash system tests
Legacy Bash system tests run on Ubuntu in a VM environment.
These provide coverage for Ubuntu-specific scenarios and legacy test cases.
You can run this step locally by following the instructions provided
in the [Tests](tests.md#bash-system-tests) page.
### Python system tests
The new Python system tests use pytest and pytest-mh,
running across multiple distributions (Fedora, Debian, Alpine, openSUSE)
in containerized environments.
These tests provide cross-distribution compatibility
and improved environment management compared to the Bash tests.
For local execution of Python system tests,
follow the instructions in the [Tests](tests.md#python-system-tests) page.
## Static code analysis

View File

@ -1,12 +1,96 @@
# Coding style
The Shadow project is developed in C,
with Python used for testing purposes.
Each language follows its own established conventions and style guidelines.
## C code
* For a general guidance refer to the
[Linux kernel coding style](https://www.kernel.org/doc/html/latest/process/coding-style.html)
* Patches that change the existing coding style are not welcome, as they make
downstream porting harder for the distributions
## Indentation
### Indentation
Tabs are preferred over spaces for indentation. Loading the `.editorconfig`
file in your preferred IDE may help you configure it.
## Python code
Python code in the Shadow project is primarily found
in the system test framework (`tests/system/`).
Follow these conventions for consistency:
### General conventions
* **PEP 8 compliance**: follow [PEP 8](https://pep8.org/) style guidelines.
* **Code quality enforcement**: all Python code must pass flake8, pycodestyle, isort, mypy, and black checks.
* **Import organization**: use absolute imports with `from __future__ import annotations`.
* **Type hints**: use modern type hints (e.g., `str | None` instead of `Optional[str]`).
* **Line length**: maximum 119 characters per line.
* **Configuration**: all formatting and linting settings are defined in `tests/system/pyproject.toml`.
### Test code style
**File and test naming:**
* Test files: `test_<command>.py` (e.g., `test_useradd.py`).
* Test functions: `test_<command>__<specific_behavior>` using double underscores.
* Use descriptive names that clearly indicate what is being tested.
**Test structure (AAA pattern):**
```python
@pytest.mark.topology(KnownTopology.Shadow)
def test_useradd__add_user(shadow: Shadow):
"""
:title: Descriptive test title
:setup:
1. Setup steps
:steps:
1. Test steps
:expectedresults:
1. Expected outcomes
:customerscenario: False
"""
# Arrange
setup_code_here()
# Act
result = shadow.command_to_test()
# Assert
assert result is not None, "Descriptive failure message"
```
**Avoiding flakiness:**
* Use deterministic test data (avoid random values).
* Clean up test artifacts properly (handled automatically by framework).
* Use appropriate timeouts for time-sensitive operations.
* Leverage the framework's automatic backup/restore functionality.
### Formatting and imports
**Required tools:**
* **flake8**: for style guide enforcement and error detection.
* **pycodestyle**: for PEP 8 style checking.
* **isort**: for import sorting with profiles that work well with Black.
* **Black**: for consistent code formatting.
* **mypy**: for static type checking.
**Import order:**
1. Standard library imports.
2. Third-party imports (`pytest`, `pytest_mh`).
3. Local framework imports (`framework.*`).
### Error handling and logging
**Error handling:**
* Prefer explicit exceptions over silent failures.
* Use `ProcessError` for command execution failures.
* Provide context in error messages.
**Logging guidance:**
* Use structured logging for test utilities in `tests/system/framework/`.
* Include relevant context (command, parameters, expected vs actual results).
* Leverage the framework's automatic artifact collection for debugging.

View File

@ -1,6 +1,8 @@
# Tests
Currently, shadow provides unit and system tests.
Currently, shadow provides unit tests and system tests.
System tests are split in two:
Bash system tests and Python system tests.
## Unit tests
@ -20,15 +22,243 @@ make check
## System tests
These type of tests are written in shell. Unfortunately, the testing framework
is tightly coupled to the Ubuntu distribution and it can only be run in this
distribution. Besides, if anything fails during the execution the system can
be left in an unstable state. Taking that into account you shouldn't run this
workflow in your host machine, we recommend to use a disposable system like a
VM or a container instead.
System tests verify the behavior of shadow utilities
in a complete system environment.
There are two types of system tests available.
You can execute system tests by running:
### Bash system tests
These tests are written in shell
and are tightly coupled to the Ubuntu distribution.
They can only be run on Ubuntu,
and if anything fails during execution,
the system can be left in an unstable state.
You shouldn't run this workflow on your host machine—we recommend using a disposable system
like a VM or a container instead.
You can execute Bash system tests by running:
```
cd tests && ./run_all
```
### Python system tests
The new system tests use Python,
[pytest](https://pytest.org/)
and [pytest-mh](https://pytest-mh.readthedocs.io/).
The Python framework provides several advantages over Bash tests:
**Cross-distribution compatibility**: works on all distributions in CI
(Alpine, Debian, Fedora and openSUSE)
unlike Bash tests which only run on Ubuntu.
**Proper environment management**: ensures clean setup and teardown
with environment restoration even when tests fail.
**Improved test maintainability**: provides a rich, high-level API
that reduces complex test code
and makes tests easier to understand.
**Automated artifact collection**: automatically collects logs and artifacts
when tests finish (even if they fail).
**Flexible infrastructure**: supports both VMs and containers for testing
with local execution capabilities.
**Future extensibility**: enables potential filtering by ticket
or importance and other advanced features.
[Read more about the benefits and rationale](https://github.com/shadow-maint/shadow/issues/835).
**For all new contributions,
we recommend using the Python system test framework.**
#### Contribution guidance
The framework is under active development
and welcomes contributions for missing features.
When contributing:
1. **Framework improvements**: enhance the infrastructure in `tests/system/framework/`.
2. **Proposing new tests**: place them in `tests/system/tests/`.
Review existing examples in `tests/system/tests/`
to understand the established patterns and conventions.
3. **Missing features**: if you're comfortable implementing missing pytest-mh features,
contributions are encouraged.
#### Running tests
##### Environment setup
A testing environment is required to run the system test framework.
While the same system can be used,
a disposable environment (container or VM) is recommended.
The default configuration relies on a pre-built container named `builder`
that must be available in your container environment.
This container contains:
- A complete shadow-utils installation
- All required system dependencies
**Setup steps:**
1. **Build the container environment**: follow the instructions in [build_install.md](build_install.md)
to create the required `builder` container.
2. **Verify container availability**: ensure the `builder` container is accessible via Docker.
The default `mhc.yaml` configuration expects this container
to be available as `builder`.
To ensure dependency isolation and system protection,
run the tests within a Python virtual environment:
```bash
cd tests/system
python -m venv .venv
source .venv/bin/activate
pip install -r ./requirements.txt
```
##### Configuration
The default configuration located in `tests/system` should be enough
if you are using containers.
If you run another environment you'll need to tune `tests/system/mhc.yaml`.
Instructions are available in the
[pytest-mh mhc.yaml documentation](https://pytest-mh.readthedocs.io/en/latest/articles/mhc-yaml.html).
##### Execution
Run all Python system tests:
```bash
pytest --mh-config=mhc.yaml --mh-lazy-ssh -v
```
Run tests in a file:
```bash
pytest --mh-config=mhc.yaml --mh-lazy-ssh -v tests/test_useradd.py
```
Run a single test case:
```bash
pytest --mh-config=mhc.yaml --mh-lazy-ssh -v -k test_useradd__add_user
```
**Command options explained:**
- `--mh-config=mhc.yaml`: specifies the multihost configuration file
- `--mh-lazy-ssh`: enables lazy SSH connections for better performance
- `-v`: verbose output showing individual test results
- `-k`: filters tests by name pattern
For additional running options,
see the [pytest-mh running tests documentation](https://pytest-mh.readthedocs.io/en/latest/articles/running-tests.html).
#### Advanced testing features
The Python testing framework includes several advanced features
for comprehensive testing:
**Test markers:**
- `@pytest.mark.topology(KnownTopology.Shadow)`: specifies the test topology.
- `@pytest.mark.builtwith("gshadow")`: skips tests when specific features are unavailable.
- `@pytest.mark.parametrize()`: extensive parameterized testing
for edge cases and different inputs.
**Rich command parsing:**
All shadow command outputs are parsed into structured Python objects
using the `jc` library:
- `PasswdEntry`, `ShadowEntry`, `GroupEntry`, `GShadowEntry` for file entries
- `ID` command parsing with group membership verification
- Structured error handling and validation
#### Test development patterns
When writing new Python tests,
follow these established patterns:
**Basic test structure:**
```python
@pytest.mark.topology(KnownTopology.Shadow)
def test_command__specific_behavior(shadow: Shadow):
"""
:title: Descriptive test title
:setup:
1. Setup steps
:steps:
1. Test steps
:expectedresults:
1. Expected outcomes
:customerscenario: False
"""
# Arrange
shadow.useradd("testuser")
# Act
result = shadow.tools.getent.passwd("testuser")
# Assert
assert result is not None, "User should exist"
assert result.name == "testuser", "Username should match"
```
**Distribution-specific testing:**
```python
if "Debian" in shadow.host.distro_name:
assert result.shell == "/bin/sh"
else:
assert result.shell == "/bin/bash"
```
**Parameterized testing:**
```python
@pytest.mark.parametrize(
"input_date, expected_epoch",
[
("1970-01-01", 0),
("2025-01-01", 20089),
("0", 0),
],
)
def test_date_handling(shadow: Shadow, input_date: str, expected_epoch: int):
# Test implementation
```
#### Debugging information
**Test artifacts:**
- **Location**: `tests/system/artifacts/` directory.
- **Content**: automatically collected logs and system state information.
- **Collection**: happens automatically when tests finish,
regardless of success or failure.
**System state management:**
The framework automatically manages shadow system files:
- **Backup**: complete backup of `/etc/passwd`, `/etc/shadow`, `/etc/group`, `/etc/gshadow`, `/home`
before tests.
- **Restore**: automatic restoration after each test.
- **Verification**: file mismatch detection
to ensure unmodified files remain unchanged.
**Common debugging steps:**
1. **Check container status**: ensure the `builder` container is running and accessible.
2. **Verify artifacts**: examine collected logs in the `artifacts/` directory.
3. **Manual testing**: access the container directly
to reproduce issues manually.
4. **File state**: check if unexpected file modifications caused test failures.
#### Troubleshooting & FAQs
**Common pitfalls:**
- **Missing virtual environment**: always activate the Python virtual environment
before running tests.
- **Mismatched environment**: ensure your `mhc.yaml` configuration matches your testing environment.
- **Container vs host confusion**: remember that tests run inside containers by default;
adjust expectations accordingly.
**Tips for disposable environments:**
- Use containers or VMs to avoid system state pollution.
- Ensure proper cleanup in the framework between test runs.
- Verify container/VM has necessary privileges for user/group operations.
For additional information about the testing framework,
see the [pytest-mh documentation](https://pytest-mh.readthedocs.io).

View File

@ -6,7 +6,7 @@
#
# Delay in seconds before being allowed another attempt after a login failure
# Note: When PAM is used, some modules may enforce a minimum delay (e.g.
# Note: when PAM is used, some modules may enforce a minimum delay (e.g.
# pam_unix(8) enforces a 2s delay)
#
FAIL_DELAY 3
@ -276,7 +276,7 @@ PASS_ALWAYS_WARN yes
#
# Number of significant characters in the password for crypt().
# Default is 8, don't change unless your crypt() is better.
# Ignored if MD5_CRYPT_ENAB set to "yes".
# Only used for DES encryption algorithm.
#
#PASS_MAX_LEN 8
@ -308,7 +308,7 @@ CHFN_RESTRICT rwh
# Set to "no" if you need to copy encrypted passwords to other systems
# which don't understand the new algorithm. Default is "no".
#
# Note: If you use PAM, it is recommended to use a value consistent with
# Note: if you use PAM, it is recommended to use a value consistent with
# the PAM modules configuration.
#
# This variable is deprecated. You should use ENCRYPT_METHOD instead.
@ -326,7 +326,7 @@ CHFN_RESTRICT rwh
# MD5 and DES should not be used for new hashes, see crypt(5) for recommendations.
# Overrides the MD5_CRYPT_ENAB option
#
# Note: If you use PAM, it is recommended to use a value consistent with
# Note: if you use PAM, it is recommended to use a value consistent with
# the PAM modules configuration.
#
#ENCRYPT_METHOD DES
@ -467,7 +467,7 @@ PREVENT_NO_AUTH superuser
# Used in pam_timestamp module to calculate the keyed-hash message
# authentication code.
#
# Note: It is recommended to check hmac(3) to see the possible algorithms
# Note: it is recommended to check hmac(3) to see the possible algorithms
# that are available in your system.
#
#HMAC_CRYPTO_ALGO SHA512

View File

@ -37,30 +37,10 @@ libshadow_la_SOURCES = \
alloc/realloc.h \
alloc/reallocf.c \
alloc/reallocf.h \
alloc/x/xcalloc.c \
alloc/x/xcalloc.h \
alloc/x/xmalloc.c \
alloc/x/xmalloc.h \
alloc/x/xrealloc.c \
alloc/x/xrealloc.h \
atoi/a2i/a2i.c \
atoi/a2i/a2i.h \
atoi/a2i/a2s.c \
atoi/a2i/a2s.h \
atoi/a2i/a2s_c.c \
atoi/a2i/a2s_c.h \
atoi/a2i/a2s_nc.c \
atoi/a2i/a2s_nc.h \
atoi/a2i/a2u.c \
atoi/a2i/a2u.h \
atoi/a2i/a2u_c.c \
atoi/a2i/a2u_c.h \
atoi/a2i/a2u_nc.c \
atoi/a2i/a2u_nc.h \
atoi/a2i.c \
atoi/a2i.h \
atoi/getnum.c \
atoi/getnum.h \
atoi/str2i.c \
atoi/str2i.h \
atoi/strtoi/strtoi.c \
atoi/strtoi/strtoi.h \
atoi/strtoi/strtou.c \
@ -75,6 +55,8 @@ libshadow_la_SOURCES = \
cast.h \
chkname.c \
chkname.h \
chkhash.c \
chkhash.h \
chowndir.c \
chowntty.c \
cleanup.c \
@ -88,6 +70,8 @@ libshadow_la_SOURCES = \
defines.h \
encrypt.c \
env.c \
exit_if_null.c \
exit_if_null.h \
exitcodes.h \
faillog.h \
failure.c \
@ -99,7 +83,6 @@ libshadow_la_SOURCES = \
find_new_uid.c \
find_new_sub_gids.c \
find_new_sub_uids.c \
fputsx.c \
fs/mkstemp/fmkomstemp.c \
fs/mkstemp/fmkomstemp.h \
fs/mkstemp/mkomstemp.c \
@ -117,7 +100,6 @@ libshadow_la_SOURCES = \
groupio.c \
groupmem.c \
groupio.h \
gshadow.c \
hushed.c \
idmapping.h \
idmapping.c \
@ -165,18 +147,38 @@ libshadow_la_SOURCES = \
semanage.c \
setugid.c \
setupenv.c \
sgetgrent.c \
sgetpwent.c \
sgetspent.c \
sgroupio.c \
sgroupio.h\
sgroupio.h \
shadow/group/sgetgrent.c \
shadow/group/sgetgrent.h \
shadow/grp/agetgroups.c \
shadow/grp/agetgroups.h \
shadow/gshadow/endsgent.c \
shadow/gshadow/endsgent.h \
shadow/gshadow/fgetsgent.c \
shadow/gshadow/fgetsgent.h \
shadow/gshadow/getsgent.c \
shadow/gshadow/getsgent.h \
shadow/gshadow/getsgnam.c \
shadow/gshadow/getsgnam.h \
shadow/gshadow/gshadow.c \
shadow/gshadow/gshadow.h \
shadow/gshadow/putsgent.c \
shadow/gshadow/putsgent.h \
shadow/gshadow/setsgent.c \
shadow/gshadow/setsgent.h \
shadow/gshadow/sgetsgent.c \
shadow/gshadow/sgetsgent.h \
shadow/gshadow/sgrp.c \
shadow/gshadow/sgrp.h \
shadow/passwd/sgetpwent.c \
shadow/passwd/sgetpwent.h \
shadow/shadow/sgetspent.c \
shadow/shadow/sgetspent.h \
shadowio.c \
shadowio.h \
shadowlog.c \
shadowlog.h \
shadowlog_internal.h \
shadowmem.c \
shell.c \
sizeof.h \
@ -199,8 +201,6 @@ libshadow_la_SOURCES = \
string/sprintf/snprintf.h \
string/sprintf/stpeprintf.c \
string/sprintf/stpeprintf.h \
string/sprintf/xaprintf.c \
string/sprintf/xaprintf.h \
string/strchr/strchrcnt.c \
string/strchr/strchrcnt.h \
string/strchr/strchrscnt.c \
@ -213,6 +213,8 @@ libshadow_la_SOURCES = \
string/strcmp/strcaseprefix.h \
string/strcmp/streq.c \
string/strcmp/streq.h \
string/strcmp/strneq.c \
string/strcmp/strneq.h \
string/strcmp/strprefix.c \
string/strcmp/strprefix.h \
string/strcpy/stpecpy.c \
@ -223,12 +225,14 @@ libshadow_la_SOURCES = \
string/strcpy/strncpy.h \
string/strcpy/strtcpy.c \
string/strcpy/strtcpy.h \
string/strdup/strdup.c \
string/strdup/strdup.h \
string/strdup/strndupa.c \
string/strdup/strndupa.h \
string/strdup/xstrdup.c \
string/strdup/xstrdup.h \
string/strdup/xstrndup.c \
string/strdup/xstrndup.h \
string/strdup/strndup.c \
string/strdup/strndup.h \
string/strerrno.c \
string/strerrno.h \
string/strftime.c \
string/strftime.h \
string/strspn/stpspn.c \
@ -249,8 +253,6 @@ libshadow_la_SOURCES = \
string/strtok/strsep2arr.h \
string/strtok/strsep2ls.c \
string/strtok/strsep2ls.h \
string/strtok/xastrsep2ls.c \
string/strtok/xastrsep2ls.h \
strtoday.c \
sub.c \
subordinateio.h \
@ -306,5 +308,4 @@ endif
EXTRA_DIST = \
.indent.pro \
gshadow_.h \
xgetXXbyYY.c

View File

@ -6,7 +6,7 @@
// SPDX-License-Identifier: BSD-3-Clause
#include <config.h>
#include "config.h"
#if !defined(USE_PAM)
@ -24,6 +24,8 @@
#include "shadow/grp/agetgroups.h"
#include "shadowlog.h"
#include "string/strchr/strchrscnt.h"
#include "string/strcmp/streq.h"
#include "string/strerrno.h"
/*
@ -34,8 +36,7 @@
int
add_groups(const char *list)
{
char *g, *p, *dup;
FILE *shadow_logfd = log_get_logfd();
char *dup;
gid_t *gids;
size_t n;
@ -43,29 +44,34 @@ add_groups(const char *list)
if (gids == NULL)
return -1;
gids = REALLOCF(gids, n + strchrscnt(list, ",:") + 1, gid_t);
gids = reallocf_T(gids, n + strchrscnt(list, ",:") + 1, gid_t);
if (gids == NULL)
return -1;
p = dup = strdup(list);
dup = strdup(list);
if (dup == NULL)
goto free_gids;
while (NULL != (g = strsep(&p, ",:"))) {
struct group *grp;
if (!streq(dup, "")) {
char *g, *p;
grp = getgrnam(g); /* local, no need for xgetgrnam */
if (NULL == grp) {
fprintf(shadow_logfd, _("Warning: unknown group %s\n"), g);
continue;
p = dup;
while (NULL != (g = strsep(&p, ",:"))) {
struct group *grp;
grp = getgrnam(g); /* local, no need for xgetgrnam */
if (NULL == grp) {
fprintf(log_get_logfd(), _("Warning: unknown group %s\n"), g);
continue;
}
LSEARCH(gid_t, &grp->gr_gid, gids, &n);
}
LSEARCH(&grp->gr_gid, gids, &n);
}
free(dup);
if (setgroups(n, gids) == -1) {
fprintf(shadow_logfd, "setgroups: %s\n", strerror(errno));
fprintf(log_get_logfd(), "setgroups: %s\n", strerrno());
goto free_gids;
}

View File

@ -2,7 +2,7 @@
// SPDX-License-Identifier: BSD-3-Clause
#include <config.h>
#include "config.h"
#include "adds.h"

View File

@ -6,7 +6,7 @@
#define SHADOW_INCLUDE_LIB_ADDS_H_
#include <config.h>
#include "config.h"
#include <errno.h>
#include <limits.h>
@ -55,7 +55,7 @@ addslN(size_t n, long addend[n])
e = errno;
while (n > 1) {
QSORT(addend, n);
QSORT(long, addend, n);
errno = 0;
addend[0] = addsl2(addend[0], addend[--n]);

View File

@ -7,7 +7,7 @@
* SPDX-License-Identifier: BSD-3-Clause
*/
#include <config.h>
#include "config.h"
#include <sys/types.h>
#include <stdio.h>
@ -20,6 +20,7 @@
#include "defines.h"
#include "exitcodes.h"
#include "prototypes.h"
#include "shadow/gshadow/endsgent.h"
#ident "$Id$"

View File

@ -5,7 +5,7 @@
*/
#include <config.h>
#include "config.h"
#include "agetpass.h"
@ -106,7 +106,7 @@ agetpass_internal(const char *prompt, int flags)
* Let's add one more byte, and if the password uses it, it
* means the introduced password was longer than PASS_MAX.
*/
pass = MALLOC(PASS_MAX + 2, char);
pass = malloc_T(PASS_MAX + 2, char);
if (pass == NULL)
return NULL;
@ -135,7 +135,7 @@ agetpass(const char *prompt)
char *
agetpass_stdin()
{
return agetpass_internal(NULL, RPP_STDIN);
return agetpass_internal("", RPP_STDIN);
}
void

View File

@ -8,7 +8,7 @@
#define SHADOW_INCLUDE_LIB_AGETPASS_H_
#include <config.h>
#include "config.h"
#include "attr.h"
#include "defines.h"

View File

@ -6,6 +6,6 @@
// SPDX-License-Identifier: BSD-3-Clause
#include <config.h>
#include "config.h"
#include "alloc/calloc.h"

View File

@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: 2023-2024, Alejandro Colomar <alx@kernel.org>
// SPDX-FileCopyrightText: 2023-2025, Alejandro Colomar <alx@kernel.org>
// SPDX-License-Identifier: BSD-3-Clause
@ -6,15 +6,24 @@
#define SHADOW_INCLUDE_LIB_ALLOC_CALLOC_H_
#include <config.h>
#include "config.h"
#include <stdlib.h>
#include "exit_if_null.h"
#include "sizeof.h"
#define CALLOC(n, type) \
( \
(type *) calloc(n, sizeof(type)) \
)
// calloc_T - calloc type-safe
#define calloc_T(n, T) calloc_T_(n, typeas(T))
#define calloc_T_(n, T) \
({ \
(T *){calloc(n, sizeof(T))}; \
})
// xcalloc_T - exit-on-error calloc type-safe
#define xcalloc_T(n, T) exit_if_null(calloc_T(n, T))
#endif // include guard

View File

@ -6,7 +6,7 @@
// SPDX-License-Identifier: BSD-3-Clause
#include <config.h>
#include "config.h"
#include "alloc/malloc.h"

View File

@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: 2023-2024, Alejandro Colomar <alx@kernel.org>
// SPDX-FileCopyrightText: 2023-2025, Alejandro Colomar <alx@kernel.org>
// SPDX-License-Identifier: BSD-3-Clause
@ -6,19 +6,28 @@
#define SHADOW_INCLUDE_LIB_ALLOC_MALLOC_H_
#include <config.h>
#include "config.h"
#include <stdlib.h>
#include "attr.h"
#include "exit_if_null.h"
#include "sizeof.h"
#define MALLOC(n, type) \
( \
(type *) mallocarray(n, sizeof(type)) \
)
// malloc_T - malloc type-safe
#define malloc_T(n, T) malloc_T_(n, typeas(T))
#define malloc_T_(n, T) \
({ \
(T *){mallocarray(n, sizeof(T))}; \
})
// xmalloc_T - exit-on-error malloc type-safe
#define xmalloc_T(n, T) exit_if_null(malloc_T(n, T))
// mallocarray - malloc array
ATTR_ALLOC_SIZE(1, 2)
ATTR_MALLOC(free)
inline void *mallocarray(size_t nmemb, size_t size);

View File

@ -6,6 +6,6 @@
// SPDX-License-Identifier: BSD-3-Clause
#include <config.h>
#include "config.h"
#include "alloc/realloc.h"

View File

@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: 2023-2024, Alejandro Colomar <alx@kernel.org>
// SPDX-FileCopyrightText: 2023-2025, Alejandro Colomar <alx@kernel.org>
// SPDX-License-Identifier: BSD-3-Clause
@ -6,15 +6,27 @@
#define SHADOW_INCLUDE_LIB_ALLOC_REALLOC_H_
#include <config.h>
#include "config.h"
#include <stdlib.h>
#include "exit_if_null.h"
#include "sizeof.h"
#define REALLOC(p, n, type) \
( \
_Generic(p, type *: (type *) reallocarray(p, (n) ?: 1, sizeof(type))) \
)
// realloc_T - realloc type-safe
#define realloc_T(p, n, T) realloc_T_(p, n, typeas(T))
#define realloc_T_(p, n, T) \
({ \
_Generic(p, T *: (void)0); \
(T *){reallocarray_(p, n, sizeof(T))}; \
})
#define reallocarray_(p, n, size) reallocarray(p, (n) ?: 1, (size) ?: 1)
// xrealloc_T - exit-on-error realloc type-safe
#define xrealloc_T(p, n, T) exit_if_null(realloc_T(p, n, T))
#endif // include guard

View File

@ -6,7 +6,7 @@
// SPDX-License-Identifier: BSD-3-Clause
#include <config.h>
#include "config.h"
#include "alloc/reallocf.h"

View File

@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: 2023-2024, Alejandro Colomar <alx@kernel.org>
// SPDX-FileCopyrightText: 2023-2025, Alejandro Colomar <alx@kernel.org>
// SPDX-License-Identifier: BSD-3-Clause
@ -6,20 +6,27 @@
#define SHADOW_INCLUDE_LIB_ALLOC_REALLOCF_H_
#include <config.h>
#include "config.h"
#include <stddef.h>
#include <stdlib.h>
#include "attr.h"
#include "sizeof.h"
#define REALLOCF(p, n, type) \
( \
_Generic(p, type *: (type *) reallocarrayf(p, (n) ?: 1, sizeof(type)))\
)
// reallocf_T - realloc free-on-error type-safe
#define reallocf_T(p, n, T) reallocf_T_(p, n, typeas(T))
#define reallocf_T_(p, n, T) \
({ \
_Generic(p, T *: (void)0); \
(T *){reallocarrayf_(p, n, sizeof(T))}; \
})
#define reallocarrayf_(p, n, size) reallocarrayf(p, (n) ?: 1, (size) ?: 1)
// reallocarrayf - realloc array free-on-error
ATTR_ALLOC_SIZE(2, 3)
ATTR_MALLOC(free)
inline void *reallocarrayf(void *p, size_t nmemb, size_t size);

View File

@ -1,36 +0,0 @@
// SPDX-FileCopyrightText: 1990-1994, Julianne Frances Haugh
// SPDX-FileCopyrightText: 1996-1998, Marek Michałkiewicz
// SPDX-FileCopyrightText: 2003-2006, Tomasz Kłoczko
// SPDX-FileCopyrightText: 2008 , Nicolas François
// SPDX-FileCopyrightText: 2023-2024, Alejandro Colomar <alx@kernel.org>
// SPDX-License-Identifier: BSD-3-Clause
#include <config.h>
#include "alloc/x/xcalloc.h"
#include <stddef.h>
#include <stdlib.h>
#include <string.h>
#include "defines.h"
#include "shadowlog.h"
void *
xcalloc(size_t nmemb, size_t size)
{
void *p;
p = calloc(nmemb, size);
if (p == NULL)
goto x;
return p;
x:
fprintf(log_get_logfd(), _("%s: %s\n"),
log_get_progname(), strerror(errno));
exit(13);
}

View File

@ -1,28 +0,0 @@
// SPDX-FileCopyrightText: 2023-2024, Alejandro Colomar <alx@kernel.org>
// SPDX-License-Identifier: BSD-3-Clause
#ifndef SHADOW_INCLUDE_LIB_ALLOC_X_XCALLOC_H_
#define SHADOW_INCLUDE_LIB_ALLOC_X_XCALLOC_H_
#include <config.h>
#include <stddef.h>
#include <stdlib.h>
#include "attr.h"
#define XCALLOC(n, type) \
( \
(type *) xcalloc(n, sizeof(type)) \
)
ATTR_ALLOC_SIZE(1, 2)
ATTR_MALLOC(free)
void *xcalloc(size_t nmemb, size_t size);
#endif // include guard

View File

@ -1,16 +0,0 @@
// SPDX-FileCopyrightText: 1990-1994, Julianne Frances Haugh
// SPDX-FileCopyrightText: 1996-1998, Marek Michałkiewicz
// SPDX-FileCopyrightText: 2003-2006, Tomasz Kłoczko
// SPDX-FileCopyrightText: 2008 , Nicolas François
// SPDX-FileCopyrightText: 2023-2024, Alejandro Colomar <alx@kernel.org>
// SPDX-License-Identifier: BSD-3-Clause
#include <config.h>
#include "alloc/x/xmalloc.h"
#include <stddef.h>
extern inline void *xmallocarray(size_t nmemb, size_t size);

View File

@ -1,35 +0,0 @@
// SPDX-FileCopyrightText: 2023-2024, Alejandro Colomar <alx@kernel.org>
// SPDX-License-Identifier: BSD-3-Clause
#ifndef SHADOW_INCLUDE_LIB_ALLOC_X_XMALLOC_H_
#define SHADOW_INCLUDE_LIB_ALLOC_X_XMALLOC_H_
#include <config.h>
#include <stddef.h>
#include "alloc/x/xrealloc.h"
#include "attr.h"
#define XMALLOC(n, type) \
( \
(type *) xmallocarray(n, sizeof(type)) \
)
ATTR_ALLOC_SIZE(1, 2)
ATTR_MALLOC(free)
inline void *xmallocarray(size_t nmemb, size_t size);
inline void *
xmallocarray(size_t nmemb, size_t size)
{
return xreallocarray(NULL, nmemb, size);
}
#endif // include guard

View File

@ -1,35 +0,0 @@
// SPDX-FileCopyrightText: 1990-1994, Julianne Frances Haugh
// SPDX-FileCopyrightText: 1996-1998, Marek Michałkiewicz
// SPDX-FileCopyrightText: 2003-2006, Tomasz Kłoczko
// SPDX-FileCopyrightText: 2008 , Nicolas François
// SPDX-FileCopyrightText: 2023-2024, Alejandro Colomar <alx@kernel.org>
// SPDX-License-Identifier: BSD-3-Clause
#include <config.h>
#include "alloc/x/xrealloc.h"
#include <stddef.h>
#include <stdlib.h>
#include <string.h>
#include "alloc/reallocf.h"
#include "defines.h"
#include "shadowlog.h"
void *
xreallocarray(void *p, size_t nmemb, size_t size)
{
p = reallocarrayf(p, nmemb, size);
if (p == NULL)
goto x;
return p;
x:
fprintf(log_get_logfd(), _("%s: %s\n"),
log_get_progname(), strerror(errno));
exit(13);
}

View File

@ -1,31 +0,0 @@
// SPDX-FileCopyrightText: 2023-2024, Alejandro Colomar <alx@kernel.org>
// SPDX-License-Identifier: BSD-3-Clause
#ifndef SHADOW_INCLUDE_LIB_MALLOC_H_
#define SHADOW_INCLUDE_LIB_MALLOC_H_
#include <config.h>
#include <assert.h>
#include <errno.h>
#include <stddef.h>
#include <stdint.h>
#include <stdlib.h>
#include "attr.h"
#define XREALLOC(ptr, n, type) \
( \
_Generic(ptr, type *: (type *) xreallocarray(ptr, n, sizeof(type))) \
)
ATTR_ALLOC_SIZE(2, 3)
ATTR_MALLOC(free)
void *xreallocarray(void *p, size_t nmemb, size_t size);
#endif // include guard

View File

@ -1,8 +1,7 @@
// SPDX-FileCopyrightText: 2007-2009, Nicolas François
// SPDX-FileCopyrightText: 2023-2025, Alejandro Colomar <alx@kernel.org>
// SPDX-License-Identifier: BSD-3-Clause
#include <config.h>
#include "config.h"
#include "atoi/str2i.h"
#include "atoi/a2i.h"

69
lib/atoi/a2i.h Normal file
View File

@ -0,0 +1,69 @@
// SPDX-FileCopyrightText: 2023-2025, Alejandro Colomar <alx@kernel.org>
// SPDX-License-Identifier: BSD-3-Clause
#ifndef SHADOW_INCLUDE_LIB_ATOI_A2I_H_
#define SHADOW_INCLUDE_LIB_ATOI_A2I_H_
#include "config.h"
#include <errno.h>
#include <stddef.h>
#include "atoi/strtoi/strtoi.h"
#include "atoi/strtoi/strtou_noneg.h"
#include "typetraits.h"
// a2i - alpha to integer
#define a2i(T, n, s, endp, base, min, max) \
({ \
T *n_ = n; \
QChar_of(s) **endp_ = endp; \
T min_ = min; \
T max_ = max; \
\
int status; \
\
*n_ = _Generic((T){0}, \
short: strtoi_, \
int: strtoi_, \
long: strtoi_, \
long long: strtoi_, \
unsigned short: strtou_noneg, \
unsigned int: strtou_noneg, \
unsigned long: strtou_noneg, \
unsigned long long: strtou_noneg \
)(s, (char **) endp_, base, min_, max_, &status); \
\
if (status != 0) \
errno = status; \
-!!status; \
})
#define a2sh(...) a2i(short, __VA_ARGS__)
#define a2si(...) a2i(int, __VA_ARGS__)
#define a2sl(...) a2i(long, __VA_ARGS__)
#define a2sll(...) a2i(long long, __VA_ARGS__)
#define a2uh(...) a2i(unsigned short, __VA_ARGS__)
#define a2ui(...) a2i(unsigned int, __VA_ARGS__)
#define a2ul(...) a2i(unsigned long, __VA_ARGS__)
#define a2ull(...) a2i(unsigned long long, __VA_ARGS__)
#define str2i(T, ...) a2i(T, __VA_ARGS__, NULL, 0, type_min(T), type_max(T))
#define str2sh(...) str2i(short, __VA_ARGS__)
#define str2si(...) str2i(int, __VA_ARGS__)
#define str2sl(...) str2i(long, __VA_ARGS__)
#define str2sll(...) str2i(long long, __VA_ARGS__)
#define str2uh(...) str2i(unsigned short, __VA_ARGS__)
#define str2ui(...) str2i(unsigned int, __VA_ARGS__)
#define str2ul(...) str2i(unsigned long, __VA_ARGS__)
#define str2ull(...) str2i(unsigned long long, __VA_ARGS__)
#endif // include guard

View File

@ -1,62 +0,0 @@
// SPDX-FileCopyrightText: 2023-2024, Alejandro Colomar <alx@kernel.org>
// SPDX-License-Identifier: BSD-3-Clause
#ifndef SHADOW_INCLUDE_LIB_ATOI_A2I_A2I_H_
#define SHADOW_INCLUDE_LIB_ATOI_A2I_A2I_H_
#include <config.h>
#include "atoi/a2i/a2s_c.h"
#include "atoi/a2i/a2s_nc.h"
#include "atoi/a2i/a2u_c.h"
#include "atoi/a2i/a2u_nc.h"
/*
* See the manual of these macros in liba2i's documentation:
* <http://www.alejandro-colomar.es/share/dist/liba2i/git/HEAD/liba2i-HEAD.pdf>
*/
#define a2i(TYPE, n, s, ...) \
( \
_Generic((void (*)(TYPE, typeof(s))) 0, \
void (*)(short, const char *): a2sh_c, \
void (*)(short, const void *): a2sh_c, \
void (*)(short, char *): a2sh_nc, \
void (*)(short, void *): a2sh_nc, \
void (*)(int, const char *): a2si_c, \
void (*)(int, const void *): a2si_c, \
void (*)(int, char *): a2si_nc, \
void (*)(int, void *): a2si_nc, \
void (*)(long, const char *): a2sl_c, \
void (*)(long, const void *): a2sl_c, \
void (*)(long, char *): a2sl_nc, \
void (*)(long, void *): a2sl_nc, \
void (*)(long long, const char *): a2sll_c, \
void (*)(long long, const void *): a2sll_c, \
void (*)(long long, char *): a2sll_nc, \
void (*)(long long, void *): a2sll_nc, \
void (*)(unsigned short, const char *): a2uh_c, \
void (*)(unsigned short, const void *): a2uh_c, \
void (*)(unsigned short, char *): a2uh_nc, \
void (*)(unsigned short, void *): a2uh_nc, \
void (*)(unsigned int, const char *): a2ui_c, \
void (*)(unsigned int, const void *): a2ui_c, \
void (*)(unsigned int, char *): a2ui_nc, \
void (*)(unsigned int, void *): a2ui_nc, \
void (*)(unsigned long, const char *): a2ul_c, \
void (*)(unsigned long, const void *): a2ul_c, \
void (*)(unsigned long, char *): a2ul_nc, \
void (*)(unsigned long, void *): a2ul_nc, \
void (*)(unsigned long long, const char *): a2ull_c, \
void (*)(unsigned long long, const void *): a2ull_c, \
void (*)(unsigned long long, char *): a2ull_nc, \
void (*)(unsigned long long, void *): a2ull_nc \
)(n, s, __VA_ARGS__) \
)
#endif // include guard

View File

@ -1,7 +0,0 @@
// SPDX-FileCopyrightText: 2023-2024, Alejandro Colomar <alx@kernel.org>
// SPDX-License-Identifier: BSD-3-Clause
#include <config.h>
#include "atoi/a2i/a2s.h"

View File

@ -1,20 +0,0 @@
// SPDX-FileCopyrightText: 2023-2024, Alejandro Colomar <alx@kernel.org>
// SPDX-License-Identifier: BSD-3-Clause
#ifndef SHADOW_INCLUDE_LIB_ATOI_A2I_A2S_H_
#define SHADOW_INCLUDE_LIB_ATOI_A2I_A2S_H_
#include <config.h>
#include "atoi/a2i/a2i.h"
#define a2sh(...) a2i(short, __VA_ARGS__)
#define a2si(...) a2i(int, __VA_ARGS__)
#define a2sl(...) a2i(long, __VA_ARGS__)
#define a2sll(...) a2i(long long, __VA_ARGS__)
#endif // include guard

View File

@ -1,17 +0,0 @@
// SPDX-FileCopyrightText: 2023-2024, Alejandro Colomar <alx@kernel.org>
// SPDX-License-Identifier: BSD-3-Clause
#include <config.h>
#include "atoi/a2i/a2s_c.h"
extern inline int a2sh_c(short *restrict n, const char *s,
const char **restrict endp, int base, short min, short max);
extern inline int a2si_c(int *restrict n, const char *s,
const char **restrict endp, int base, int min, int max);
extern inline int a2sl_c(long *restrict n, const char *s,
const char **restrict endp, int base, long min, long max);
extern inline int a2sll_c(long long *restrict n, const char *s,
const char **restrict endp, int base, long long min, long long max);

View File

@ -1,64 +0,0 @@
// SPDX-FileCopyrightText: 2023-2024, Alejandro Colomar <alx@kernel.org>
// SPDX-License-Identifier: BSD-3-Clause
#ifndef SHADOW_INCLUDE_LIB_ATOI_A2I_A2S_C_H_
#define SHADOW_INCLUDE_LIB_ATOI_A2I_A2S_C_H_
#include <config.h>
#include <errno.h>
#include <inttypes.h>
#include "atoi/a2i/a2s_nc.h"
#include "attr.h"
ATTR_STRING(2) ATTR_ACCESS(write_only, 1) ATTR_ACCESS(write_only, 3)
inline int a2sh_c(short *restrict n, const char *s,
const char **restrict endp, int base, short min, short max);
ATTR_STRING(2) ATTR_ACCESS(write_only, 1) ATTR_ACCESS(write_only, 3)
inline int a2si_c(int *restrict n, const char *s,
const char **restrict endp, int base, int min, int max);
ATTR_STRING(2) ATTR_ACCESS(write_only, 1) ATTR_ACCESS(write_only, 3)
inline int a2sl_c(long *restrict n, const char *s,
const char **restrict endp, int base, long min, long max);
ATTR_STRING(2) ATTR_ACCESS(write_only, 1) ATTR_ACCESS(write_only, 3)
inline int a2sll_c(long long *restrict n, const char *s,
const char **restrict endp, int base, long long min, long long max);
inline int
a2sh_c(short *restrict n, const char *s,
const char **restrict endp, int base, short min, short max)
{
return a2sh_nc(n, (char *) s, (char **) endp, base, min, max);
}
inline int
a2si_c(int *restrict n, const char *s,
const char **restrict endp, int base, int min, int max)
{
return a2si_nc(n, (char *) s, (char **) endp, base, min, max);
}
inline int
a2sl_c(long *restrict n, const char *s,
const char **restrict endp, int base, long min, long max)
{
return a2sl_nc(n, (char *) s, (char **) endp, base, min, max);
}
inline int
a2sll_c(long long *restrict n, const char *s,
const char **restrict endp, int base, long long min, long long max)
{
return a2sll_nc(n, (char *) s, (char **) endp, base, min, max);
}
#endif // include guard

View File

@ -1,17 +0,0 @@
// SPDX-FileCopyrightText: 2023-2024, Alejandro Colomar <alx@kernel.org>
// SPDX-License-Identifier: BSD-3-Clause
#include <config.h>
#include "atoi/a2i/a2s_nc.h"
extern inline int a2sh_nc(short *restrict n, char *s,
char **restrict endp, int base, short min, short max);
extern inline int a2si_nc(int *restrict n, char *s,
char **restrict endp, int base, int min, int max);
extern inline int a2sl_nc(long *restrict n, char *s,
char **restrict endp, int base, long min, long max);
extern inline int a2sll_nc(long long *restrict n, char *s,
char **restrict endp, int base, long long min, long long max);

View File

@ -1,91 +0,0 @@
// SPDX-FileCopyrightText: 2023-2024, Alejandro Colomar <alx@kernel.org>
// SPDX-License-Identifier: BSD-3-Clause
#ifndef SHADOW_INCLUDE_LIB_ATOI_A2I_A2S_NC_H_
#define SHADOW_INCLUDE_LIB_ATOI_A2I_A2S_NC_H_
#include <config.h>
#include <errno.h>
#include "atoi/strtoi/strtoi.h"
#include "attr.h"
ATTR_STRING(2) ATTR_ACCESS(write_only, 1) ATTR_ACCESS(write_only, 3)
inline int a2sh_nc(short *restrict n, char *s,
char **restrict endp, int base, short min, short max);
ATTR_STRING(2) ATTR_ACCESS(write_only, 1) ATTR_ACCESS(write_only, 3)
inline int a2si_nc(int *restrict n, char *s,
char **restrict endp, int base, int min, int max);
ATTR_STRING(2) ATTR_ACCESS(write_only, 1) ATTR_ACCESS(write_only, 3)
inline int a2sl_nc(long *restrict n, char *s,
char **restrict endp, int base, long min, long max);
ATTR_STRING(2) ATTR_ACCESS(write_only, 1) ATTR_ACCESS(write_only, 3)
inline int a2sll_nc(long long *restrict n, char *s,
char **restrict endp, int base, long long min, long long max);
inline int
a2sh_nc(short *restrict n, char *s,
char **restrict endp, int base, short min, short max)
{
int status;
*n = strtoi_(s, endp, base, min, max, &status);
if (status != 0) {
errno = status;
return -1;
}
return 0;
}
inline int
a2si_nc(int *restrict n, char *s,
char **restrict endp, int base, int min, int max)
{
int status;
*n = strtoi_(s, endp, base, min, max, &status);
if (status != 0) {
errno = status;
return -1;
}
return 0;
}
inline int
a2sl_nc(long *restrict n, char *s,
char **restrict endp, int base, long min, long max)
{
int status;
*n = strtoi_(s, endp, base, min, max, &status);
if (status != 0) {
errno = status;
return -1;
}
return 0;
}
inline int
a2sll_nc(long long *restrict n, char *s,
char **restrict endp, int base, long long min, long long max)
{
int status;
*n = strtoi_(s, endp, base, min, max, &status);
if (status != 0) {
errno = status;
return -1;
}
return 0;
}
#endif // include guard

View File

@ -1,7 +0,0 @@
// SPDX-FileCopyrightText: 2023-2024, Alejandro Colomar <alx@kernel.org>
// SPDX-License-Identifier: BSD-3-Clause
#include <config.h>
#include "atoi/a2i/a2u.h"

View File

@ -1,20 +0,0 @@
// SPDX-FileCopyrightText: 2023-2024, Alejandro Colomar <alx@kernel.org>
// SPDX-License-Identifier: BSD-3-Clause
#ifndef SHADOW_INCLUDE_LIB_ATOI_A2I_A2U_H_
#define SHADOW_INCLUDE_LIB_ATOI_A2I_A2U_H_
#include <config.h>
#include "atoi/a2i/a2i.h"
#define a2uh(...) a2i(unsigned short, __VA_ARGS__)
#define a2ui(...) a2i(unsigned int, __VA_ARGS__)
#define a2ul(...) a2i(unsigned long, __VA_ARGS__)
#define a2ull(...) a2i(unsigned long long, __VA_ARGS__)
#endif // include guard

View File

@ -1,19 +0,0 @@
// SPDX-FileCopyrightText: 2023-2024, Alejandro Colomar <alx@kernel.org>
// SPDX-License-Identifier: BSD-3-Clause
#include <config.h>
#include "atoi/a2i/a2u_c.h"
extern inline int a2uh_c(unsigned short *restrict n, const char *s,
const char **restrict endp, int base, unsigned short min,
unsigned short max);
extern inline int a2ui_c(unsigned int *restrict n, const char *s,
const char **restrict endp, int base, unsigned int min, unsigned int max);
extern inline int a2ul_c(unsigned long *restrict n, const char *s,
const char **restrict endp, int base, unsigned long min, unsigned long max);
extern inline int a2ull_c(unsigned long long *restrict n, const char *s,
const char **restrict endp, int base, unsigned long long min,
unsigned long long max);

View File

@ -1,65 +0,0 @@
// SPDX-FileCopyrightText: 2023-2024, Alejandro Colomar <alx@kernel.org>
// SPDX-License-Identifier: BSD-3-Clause
#ifndef SHADOW_INCLUDE_LIB_ATOI_A2I_A2U_C_H_
#define SHADOW_INCLUDE_LIB_ATOI_A2I_A2U_C_H_
#include <config.h>
#include "atoi/a2i/a2u_nc.h"
#include "attr.h"
ATTR_STRING(2) ATTR_ACCESS(write_only, 1) ATTR_ACCESS(write_only, 3)
inline int a2uh_c(unsigned short *restrict n, const char *s,
const char **restrict endp, int base, unsigned short min,
unsigned short max);
ATTR_STRING(2) ATTR_ACCESS(write_only, 1) ATTR_ACCESS(write_only, 3)
inline int a2ui_c(unsigned int *restrict n, const char *s,
const char **restrict endp, int base, unsigned int min, unsigned int max);
ATTR_STRING(2) ATTR_ACCESS(write_only, 1) ATTR_ACCESS(write_only, 3)
inline int a2ul_c(unsigned long *restrict n, const char *s,
const char **restrict endp, int base, unsigned long min, unsigned long max);
ATTR_STRING(2) ATTR_ACCESS(write_only, 1) ATTR_ACCESS(write_only, 3)
inline int a2ull_c(unsigned long long *restrict n, const char *s,
const char **restrict endp, int base, unsigned long long min,
unsigned long long max);
inline int
a2uh_c(unsigned short *restrict n, const char *s,
const char **restrict endp, int base, unsigned short min,
unsigned short max)
{
return a2uh_nc(n, (char *) s, (char **) endp, base, min, max);
}
inline int
a2ui_c(unsigned int *restrict n, const char *s,
const char **restrict endp, int base, unsigned int min, unsigned int max)
{
return a2ui_nc(n, (char *) s, (char **) endp, base, min, max);
}
inline int
a2ul_c(unsigned long *restrict n, const char *s,
const char **restrict endp, int base, unsigned long min, unsigned long max)
{
return a2ul_nc(n, (char *) s, (char **) endp, base, min, max);
}
inline int
a2ull_c(unsigned long long *restrict n, const char *s,
const char **restrict endp, int base, unsigned long long min,
unsigned long long max)
{
return a2ull_nc(n, (char *) s, (char **) endp, base, min, max);
}
#endif // include guard

View File

@ -1,18 +0,0 @@
// SPDX-FileCopyrightText: 2023-2024, Alejandro Colomar <alx@kernel.org>
// SPDX-License-Identifier: BSD-3-Clause
#include <config.h>
#include "atoi/a2i/a2u_nc.h"
extern inline int a2uh_nc(unsigned short *restrict n, char *s,
char **restrict endp, int base, unsigned short min, unsigned short max);
extern inline int a2ui_nc(unsigned int *restrict n, char *s,
char **restrict endp, int base, unsigned int min, unsigned int max);
extern inline int a2ul_nc(unsigned long *restrict n, char *s,
char **restrict endp, int base, unsigned long min, unsigned long max);
extern inline int a2ull_nc(unsigned long long *restrict n, char *s,
char **restrict endp, int base, unsigned long long min,
unsigned long long max);

View File

@ -1,94 +0,0 @@
// SPDX-FileCopyrightText: 2023-2024, Alejandro Colomar <alx@kernel.org>
// SPDX-License-Identifier: BSD-3-Clause
#ifndef SHADOW_INCLUDE_LIB_ATOI_A2I_A2U_NC_H_
#define SHADOW_INCLUDE_LIB_ATOI_A2I_A2U_NC_H_
#include <config.h>
#include <errno.h>
#include "atoi/strtoi/strtou_noneg.h"
#include "attr.h"
ATTR_STRING(2) ATTR_ACCESS(write_only, 1) ATTR_ACCESS(write_only, 3)
inline int a2uh_nc(unsigned short *restrict n, char *s,
char **restrict endp, int base, unsigned short min, unsigned short max);
ATTR_STRING(2) ATTR_ACCESS(write_only, 1) ATTR_ACCESS(write_only, 3)
inline int a2ui_nc(unsigned int *restrict n, char *s,
char **restrict endp, int base, unsigned int min, unsigned int max);
ATTR_STRING(2) ATTR_ACCESS(write_only, 1) ATTR_ACCESS(write_only, 3)
inline int a2ul_nc(unsigned long *restrict n, char *s,
char **restrict endp, int base, unsigned long min, unsigned long max);
ATTR_STRING(2) ATTR_ACCESS(write_only, 1) ATTR_ACCESS(write_only, 3)
inline int a2ull_nc(unsigned long long *restrict n, char *s,
char **restrict endp, int base, unsigned long long min,
unsigned long long max);
inline int
a2uh_nc(unsigned short *restrict n, char *s,
char **restrict endp, int base, unsigned short min,
unsigned short max)
{
int status;
*n = strtou_noneg(s, endp, base, min, max, &status);
if (status != 0) {
errno = status;
return -1;
}
return 0;
}
inline int
a2ui_nc(unsigned int *restrict n, char *s,
char **restrict endp, int base, unsigned int min, unsigned int max)
{
int status;
*n = strtou_noneg(s, endp, base, min, max, &status);
if (status != 0) {
errno = status;
return -1;
}
return 0;
}
inline int
a2ul_nc(unsigned long *restrict n, char *s,
char **restrict endp, int base, unsigned long min, unsigned long max)
{
int status;
*n = strtou_noneg(s, endp, base, min, max, &status);
if (status != 0) {
errno = status;
return -1;
}
return 0;
}
inline int
a2ull_nc(unsigned long long *restrict n, char *s,
char **restrict endp, int base, unsigned long long min,
unsigned long long max)
{
int status;
*n = strtou_noneg(s, endp, base, min, max, &status);
if (status != 0) {
errno = status;
return -1;
}
return 0;
}
#endif // include guard

View File

@ -3,7 +3,7 @@
// SPDX-License-Identifier: BSD-3-Clause
#include <config.h>
#include "config.h"
#include <sys/types.h>

View File

@ -7,14 +7,13 @@
#define SHADOW_INCLUDE_LIB_ATOI_GETNUM_H_
#include <config.h>
#include "config.h"
#include <limits.h>
#include <stddef.h>
#include <sys/types.h>
#include "atoi/a2i/a2i.h"
#include "atoi/a2i/a2s.h"
#include "atoi/a2i.h"
#include "attr.h"
#include "typetraits.h"

View File

@ -1,31 +0,0 @@
// SPDX-FileCopyrightText: 2007-2009, Nicolas François
// SPDX-FileCopyrightText: 2023-2025, Alejandro Colomar <alx@kernel.org>
// SPDX-License-Identifier: BSD-3-Clause
#ifndef SHADOW_INCLUDE_LIB_ATOI_STR2I_H_
#define SHADOW_INCLUDE_LIB_ATOI_STR2I_H_
#include <config.h>
#include <stddef.h>
#include "atoi/a2i/a2i.h"
#include "typetraits.h"
#define str2i(T, ...) a2i(T, __VA_ARGS__, NULL, 0, type_min(T), type_max(T))
#define str2sh(...) str2i(short, __VA_ARGS__)
#define str2si(...) str2i(int, __VA_ARGS__)
#define str2sl(...) str2i(long, __VA_ARGS__)
#define str2sll(...) str2i(long long, __VA_ARGS__)
#define str2uh(...) str2i(unsigned short, __VA_ARGS__)
#define str2ui(...) str2i(unsigned int, __VA_ARGS__)
#define str2ul(...) str2i(unsigned long, __VA_ARGS__)
#define str2ull(...) str2i(unsigned long long, __VA_ARGS__)
#endif // include guard

View File

@ -2,7 +2,7 @@
// SPDX-License-Identifier: BSD-3-Clause
#include <config.h>
#include "config.h"
#include "atoi/strtoi/strtoi.h"

View File

@ -6,7 +6,7 @@
#define SHADOW_INCLUDE_LIB_ATOI_STRTOI_STRTOI_H_
#include <config.h>
#include "config.h"
#include <errno.h>
#include <inttypes.h>

View File

@ -2,7 +2,7 @@
// SPDX-License-Identifier: BSD-3-Clause
#include <config.h>
#include "config.h"
#include "atoi/strtoi/strtou.h"

View File

@ -6,7 +6,7 @@
#define SHADOW_INCLUDE_LIB_ATOI_STRTOI_STRTOU_H_
#include <config.h>
#include "config.h"
#include <errno.h>
#include <inttypes.h>

View File

@ -2,7 +2,7 @@
// SPDX-License-Identifier: BSD-3-Clause
#include <config.h>
#include "config.h"
#include "atoi/strtoi/strtou_noneg.h"

View File

@ -6,7 +6,7 @@
#define SHADOW_INCLUDE_LIB_ATOI_STRTOI_STRTOU_NONEG_H_
#include <config.h>
#include "config.h"
#include <errno.h>
#include <stddef.h>

View File

@ -5,17 +5,38 @@
#include "config.h"
#if (__GNUC__ >= 10)
# define MAYBE_UNUSED [[gnu::unused]]
# define NORETURN [[gnu::__noreturn__]]
# define format_attr(type, fmt, va) [[gnu::format(type, fmt, va)]]
# define ATTR_ACCESS(...) [[gnu::access(__VA_ARGS__)]]
# define ATTR_ALLOC_SIZE(...) [[gnu::alloc_size(__VA_ARGS__)]]
#if !defined(__has_c_attribute)
# define __has_c_attribute(x) 0
#endif
#if __has_c_attribute(maybe_unused)
# define MAYBE_UNUSED [[maybe_unused]]
#else
# define MAYBE_UNUSED
#endif
#if __has_c_attribute(noreturn)
# define NORETURN [[noreturn]]
#else
# define NORETURN
#endif
#if __has_c_attribute(gnu::format)
# define format_attr(type, fmt, va) [[gnu::format(type, fmt, va)]]
#else
# define format_attr(type, fmt, va)
#endif
#if __has_c_attribute(gnu::access)
# define ATTR_ACCESS(...) [[gnu::access(__VA_ARGS__)]]
#else
# define ATTR_ACCESS(...)
#endif
#if __has_c_attribute(gnu::alloc_size)
# define ATTR_ALLOC_SIZE(...) [[gnu::alloc_size(__VA_ARGS__)]]
#else
# define ATTR_ALLOC_SIZE(...)
#endif
@ -25,11 +46,17 @@
# define ATTR_MALLOC(deallocator)
#endif
#if (__GNUC__ >= 14)
#if __has_c_attribute(gnu::null_terminated_string_arg)
# define ATTR_STRING(i) [[gnu::null_terminated_string_arg(i)]]
#else
# define ATTR_STRING(i)
#endif
#if __has_c_attribute(gnu::nonstring)
# define ATTR_NONSTRING [[gnu::nonstring]]
#else
# define ATTR_NONSTRING
#endif
#endif // include guard

View File

@ -11,7 +11,7 @@
*
*/
#include <config.h>
#include "config.h"
#ifdef WITH_AUDIT
@ -56,13 +56,12 @@ void audit_help_open (void)
*
* type - type of message. A list of possible values is available in
* "audit-records.h" file.
* pgname - program's name
* op - operation. "adding user", "changing finger info", "deleting group"
* name - user's account or group name. If not available use NULL.
* id - uid or gid that the operation is being performed on. This is used
* only when user is NULL.
*/
void audit_logger (int type, MAYBE_UNUSED const char *pgname, const char *op,
void audit_logger (int type, const char *op,
const char *name, unsigned int id,
shadow_audit_result result)
{
@ -105,10 +104,10 @@ audit_logger_with_group(int type, const char *op, const char *name,
len = strnlen(grp, sizeof(enc_group)/2);
if (audit_value_needs_encoding(grp, len)) {
SNPRINTF(buf, "%s %s=%s", op, grp_type,
stprintf_a(buf, "%s %s=%s", op, grp_type,
audit_encode_value(enc_group, grp, len));
} else {
SNPRINTF(buf, "%s %s=\"%s\"", op, grp_type, grp);
stprintf_a(buf, "%s %s=\"%s\"", op, grp_type, grp);
}
audit_log_acct_message(audit_fd, type, NULL, buf, name, id,

View File

@ -11,7 +11,7 @@
* --marekm
*/
#include <config.h>
#include "config.h"
#include <stddef.h>
#include <stdlib.h>

View File

@ -5,7 +5,7 @@
*/
#include <config.h>
#include "config.h"
#ident "$Id$"

View File

@ -9,7 +9,7 @@
#define SHADOW_INCLUDE_LIB_BIT_H_
#include <config.h>
#include "config.h"
#include <limits.h>

View File

@ -6,7 +6,7 @@
#define SHADOW_INCLUDE_LIB_CAST_H_
#include <config.h>
#include "config.h"
#define const_cast(T, p) _Generic(p, const T: (T) (p))

82
lib/chkhash.c Normal file
View File

@ -0,0 +1,82 @@
#include "config.h"
#include "chkhash.h"
#include <regex.h>
#include <stdbool.h>
#include <stddef.h>
#include <string.h>
#include "string/strcmp/streq.h"
#include "string/strcmp/strprefix.h"
/*
* match_regex - return true if match, false if not
*/
bool
match_regex(const char *pattern, const char *string)
{
regex_t regex;
int result;
if (regcomp(&regex, pattern, REG_EXTENDED) != 0)
return false;
result = regexec(&regex, string, 0, NULL, 0);
regfree(&regex);
return result == 0;
}
/*
* is_valid_hash - check if the string is a valid shadow(5) 2nd field.
*
* regex from: https://man.archlinux.org/man/crypt.5.en
*/
bool
is_valid_hash(const char *hash)
{
// Password temporarily locked
hash = strprefix(hash, "!") ?: hash;
// Passwordless account; discouraged
if (streq(hash, ""))
return true;
// Password permanently locked (and forgotten)
if (streq(hash, "*"))
return true;
// Minimum hash length
if (strlen(hash) < 13)
return false;
// Yescrypt: $y$ + algorithm parameters + $ + salt + $ + 43-char (minimum) hash
if (match_regex("^\\$y\\$[./A-Za-z0-9]+\\$[./A-Za-z0-9]{1,86}\\$[./A-Za-z0-9]{43}$", hash))
return true;
// Bcrypt: $2[abxy]$ + 2-digit cost + $ + 53-char hash
if (match_regex("^\\$2[abxy]\\$[0-9]{2}\\$[./A-Za-z0-9]{53}$", hash))
return true;
// SHA-512: $6$ + salt + $ + 86-char hash
if (match_regex("^\\$6\\$(rounds=[1-9][0-9]{3,8}\\$)?[^$:\\n]{1,16}\\$[./A-Za-z0-9]{86}$", hash))
return true;
// SHA-256: $5$ + salt + $ + 43-char hash
if (match_regex("^\\$5\\$(rounds=[1-9][0-9]{3,8}\\$)?[^$:\\n]{1,16}\\$[./A-Za-z0-9]{43}$", hash))
return true;
// MD5: $1$ + salt + $ + 22-char hash
if (match_regex("^\\$1\\$[^$:\\n]{1,8}\\$[./A-Za-z0-9]{22}$", hash))
return true;
// DES: exactly 13 characters from [A-Za-z0-9./]
if (match_regex("^[./A-Za-z0-9]{13}$", hash))
return true;
// Not a valid hash
return false;
}

13
lib/chkhash.h Normal file
View File

@ -0,0 +1,13 @@
#ifndef SHADOW_INCLUDE_CHKHASH_H
#define SHADOW_INCLUDE_CHKHASH_H
#include "config.h"
#include <stdbool.h>
bool is_valid_hash(const char *hash);
#endif

View File

@ -13,12 +13,13 @@
* true - OK
* false - bad name
* errors:
* EINVAL Invalid name characters or sequences
* EINVAL Invalid name
* EILSEQ Invalid name character sequence (acceptable with --badname)
* EOVERFLOW Name longer than maximum size
*/
#include <config.h>
#include "config.h"
#ident "$Id$"
@ -27,12 +28,15 @@
#include <limits.h>
#include <stdbool.h>
#include <stddef.h>
#include <string.h>
#include <unistd.h>
#include "defines.h"
#include "chkname.h"
#include "string/ctype/strchrisascii/strchriscntrl.h"
#include "string/ctype/strisascii/strisdigit.h"
#include "string/strcmp/streq.h"
#include "string/strcmp/strcaseeq.h"
#ifndef LOGIN_NAME_MAX
@ -59,35 +63,37 @@ login_name_max_size(void)
static bool
is_valid_name(const char *name)
{
if (streq(name, "")
|| streq(name, ".")
|| streq(name, "..")
|| strspn(name, "-")
|| strpbrk(name, " \"#',/:;")
|| strchriscntrl(name)
|| strisdigit(name))
{
errno = EINVAL;
return false;
}
if (allow_bad_names) {
return true;
}
/*
* User/group names must match BRE regex:
* [a-zA-Z0-9_.][a-zA-Z0-9_.-]*$\?
*
* as a non-POSIX, extension, allow "$" as the last char for
* sake of Samba 3.x "add machine script"
*
* Also do not allow fully numeric names or just "." or "..".
*/
* User/group names must match BRE regex:
* [a-zA-Z0-9_.][a-zA-Z0-9_.-]*$\?
*
* as a non-POSIX, extension, allow "$" as the last char for
* sake of Samba 3.x "add machine script"
*/
if (strisdigit(name)) {
errno = EINVAL;
return false;
}
if (streq(name, "") ||
streq(name, ".") ||
streq(name, "..") ||
!((*name >= 'a' && *name <= 'z') ||
if (!((*name >= 'a' && *name <= 'z') ||
(*name >= 'A' && *name <= 'Z') ||
(*name >= '0' && *name <= '9') ||
*name == '_' ||
*name == '.'))
{
errno = EINVAL;
errno = EILSEQ;
return false;
}
@ -101,7 +107,7 @@ is_valid_name(const char *name)
streq(name, "$")
))
{
errno = EINVAL;
errno = EILSEQ;
return false;
}
}

View File

@ -21,7 +21,7 @@
*/
#include <config.h>
#include "config.h"
#include <stdbool.h>
#include <stddef.h>

View File

@ -7,7 +7,7 @@
* SPDX-License-Identifier: BSD-3-Clause
*/
#include <config.h>
#include "config.h"
#ident "$Id$"

View File

@ -7,7 +7,7 @@
* SPDX-License-Identifier: BSD-3-Clause
*/
#include <config.h>
#include "config.h"
#ident "$Id$"
@ -53,9 +53,8 @@ void chown_tty (const struct passwd *info)
if ( (fchown (STDIN_FILENO, info->pw_uid, gid) != 0)
|| (fchmod (STDIN_FILENO, getdef_num ("TTYPERM", 0600)) != 0)) {
int err = errno;
FILE *shadow_logfd = log_get_logfd();
fprintf (shadow_logfd,
fprintf (log_get_logfd(),
_("Unable to change owner or mode of tty stdin: %s"),
strerror (err));
SYSLOG ((LOG_WARN,

View File

@ -4,7 +4,7 @@
* SPDX-License-Identifier: BSD-3-Clause
*/
#include <config.h>
#include "config.h"
#include <assert.h>
#include <stdio.h>

View File

@ -4,7 +4,7 @@
* SPDX-License-Identifier: BSD-3-Clause
*/
#include <config.h>
#include "config.h"
#include <assert.h>
#include <stdio.h>
@ -27,7 +27,7 @@ void cleanup_report_add_group (void *group_name)
SYSLOG ((LOG_ERR, "failed to add group %s", name));
#ifdef WITH_AUDIT
audit_logger (AUDIT_ADD_GROUP, log_get_progname(),
audit_logger (AUDIT_ADD_GROUP,
"",
name, AUDIT_NO_ID,
SHADOW_AUDIT_FAILURE);
@ -45,7 +45,7 @@ void cleanup_report_del_group (void *group_name)
SYSLOG ((LOG_ERR, "failed to remove group %s", name));
#ifdef WITH_AUDIT
audit_logger (AUDIT_DEL_GROUP, log_get_progname(),
audit_logger (AUDIT_DEL_GROUP,
"",
name, AUDIT_NO_ID,
SHADOW_AUDIT_FAILURE);
@ -62,7 +62,7 @@ void cleanup_report_mod_group (void *cleanup_info)
gr_dbname (),
info->action));
#ifdef WITH_AUDIT
audit_logger (AUDIT_GRP_MGMT, log_get_progname(),
audit_logger (AUDIT_GRP_MGMT,
info->audit_msg,
info->name, AUDIT_NO_ID,
SHADOW_AUDIT_FAILURE);
@ -80,7 +80,7 @@ void cleanup_report_mod_gshadow (void *cleanup_info)
sgr_dbname (),
info->action));
#ifdef WITH_AUDIT
audit_logger (AUDIT_GRP_MGMT, log_get_progname(),
audit_logger (AUDIT_GRP_MGMT,
info->audit_msg,
info->name, AUDIT_NO_ID,
SHADOW_AUDIT_FAILURE);
@ -100,7 +100,7 @@ void cleanup_report_add_group_group (void *group_name)
SYSLOG ((LOG_ERR, "failed to add group %s to %s", name, gr_dbname ()));
#ifdef WITH_AUDIT
audit_logger (AUDIT_ADD_GROUP, log_get_progname(),
audit_logger (AUDIT_ADD_GROUP,
"adding-group",
name, AUDIT_NO_ID,
SHADOW_AUDIT_FAILURE);
@ -120,7 +120,7 @@ void cleanup_report_add_group_gshadow (void *group_name)
SYSLOG ((LOG_ERR, "failed to add group %s to %s", name, sgr_dbname ()));
#ifdef WITH_AUDIT
audit_logger (AUDIT_GRP_MGMT, log_get_progname(),
audit_logger (AUDIT_GRP_MGMT,
"adding-shadow-group",
name, AUDIT_NO_ID,
SHADOW_AUDIT_FAILURE);
@ -143,7 +143,7 @@ void cleanup_report_del_group_group (void *group_name)
"failed to remove group %s from %s",
name, gr_dbname ()));
#ifdef WITH_AUDIT
audit_logger (AUDIT_DEL_GROUP, log_get_progname(),
audit_logger (AUDIT_DEL_GROUP,
"removing-group",
name, AUDIT_NO_ID,
SHADOW_AUDIT_FAILURE);
@ -166,7 +166,7 @@ void cleanup_report_del_group_gshadow (void *group_name)
"failed to remove group %s from %s",
name, sgr_dbname ()));
#ifdef WITH_AUDIT
audit_logger (AUDIT_GRP_MGMT, log_get_progname(),
audit_logger (AUDIT_GRP_MGMT,
"removing-shadow-group",
name, AUDIT_NO_ID,
SHADOW_AUDIT_FAILURE);
@ -179,9 +179,11 @@ void cleanup_report_del_group_gshadow (void *group_name)
*
* It should be registered after the group file is successfully locked.
*/
void cleanup_unlock_group (MAYBE_UNUSED void *arg)
void cleanup_unlock_group (void *process_selinux)
{
if (gr_unlock () == 0) {
bool process = *((bool *) process_selinux);
if (gr_unlock (process) == 0) {
fprintf (log_get_logfd(),
_("%s: failed to unlock %s\n"),
log_get_progname(), gr_dbname ());
@ -199,9 +201,11 @@ void cleanup_unlock_group (MAYBE_UNUSED void *arg)
*
* It should be registered after the gshadow file is successfully locked.
*/
void cleanup_unlock_gshadow (MAYBE_UNUSED void *arg)
void cleanup_unlock_gshadow (void *process_selinux)
{
if (sgr_unlock () == 0) {
bool process = *((bool *) process_selinux);
if (sgr_unlock (process) == 0) {
fprintf (log_get_logfd(),
_("%s: failed to unlock %s\n"),
log_get_progname(), sgr_dbname ());

View File

@ -4,12 +4,11 @@
* SPDX-License-Identifier: BSD-3-Clause
*/
#include <config.h>
#include "config.h"
#include <assert.h>
#include <stdio.h>
#include "attr.h"
#include "defines.h"
#include "pwio.h"
#include "shadowio.h"
@ -27,7 +26,7 @@ void cleanup_report_add_user (void *user_name)
SYSLOG ((LOG_ERR, "failed to add user %s", name));
#ifdef WITH_AUDIT
audit_logger (AUDIT_ADD_USER, log_get_progname(),
audit_logger (AUDIT_ADD_USER,
"",
name, AUDIT_NO_ID,
SHADOW_AUDIT_FAILURE);
@ -44,7 +43,7 @@ void cleanup_report_mod_passwd (void *cleanup_info)
pw_dbname (),
info->action));
#ifdef WITH_AUDIT
audit_logger (AUDIT_USER_MGMT, log_get_progname(),
audit_logger (AUDIT_USER_MGMT,
info->audit_msg,
info->name, AUDIT_NO_ID,
SHADOW_AUDIT_FAILURE);
@ -64,7 +63,7 @@ void cleanup_report_add_user_passwd (void *user_name)
SYSLOG ((LOG_ERR, "failed to add user %s to %s", name, pw_dbname ()));
#ifdef WITH_AUDIT
audit_logger (AUDIT_ADD_USER, log_get_progname(),
audit_logger (AUDIT_ADD_USER,
"adding-user",
name, AUDIT_NO_ID,
SHADOW_AUDIT_FAILURE);
@ -84,7 +83,7 @@ void cleanup_report_add_user_shadow (void *user_name)
SYSLOG ((LOG_ERR, "failed to add user %s to %s", name, spw_dbname ()));
#ifdef WITH_AUDIT
audit_logger (AUDIT_USER_MGMT, log_get_progname(),
audit_logger (AUDIT_USER_MGMT,
"adding-shadow-user",
name, AUDIT_NO_ID,
SHADOW_AUDIT_FAILURE);
@ -96,9 +95,11 @@ void cleanup_report_add_user_shadow (void *user_name)
*
* It should be registered after the passwd database is successfully locked.
*/
void cleanup_unlock_passwd (MAYBE_UNUSED void *arg)
void cleanup_unlock_passwd (void *process_selinux)
{
if (pw_unlock () == 0) {
bool process = *((bool *) process_selinux);
if (pw_unlock (process) == 0) {
fprintf (log_get_logfd(),
_("%s: failed to unlock %s\n"),
log_get_progname(), pw_dbname ());
@ -115,9 +116,11 @@ void cleanup_unlock_passwd (MAYBE_UNUSED void *arg)
*
* It should be registered after the shadow database is successfully locked.
*/
void cleanup_unlock_shadow (MAYBE_UNUSED void *arg)
void cleanup_unlock_shadow (void *process_selinux)
{
if (spw_unlock () == 0) {
bool process = *((bool *) process_selinux);
if (spw_unlock (process) == 0) {
fprintf (log_get_logfd(),
_("%s: failed to unlock %s\n"),
log_get_progname(), spw_dbname ());

View File

@ -7,7 +7,7 @@
* SPDX-License-Identifier: BSD-3-Clause
*/
#include <config.h>
#include "config.h"
#ident "$Id$"
@ -16,6 +16,7 @@
#include <fcntl.h>
#include <limits.h>
#include <signal.h>
#include <stddef.h>
#include <stdio.h>
#include <string.h>
#include <sys/stat.h>
@ -23,32 +24,31 @@
#include <utime.h>
#include "alloc/malloc.h"
#include "alloc/reallocf.h"
#include "atoi/getnum.h"
#include "commonio.h"
#include "defines.h"
#include "fs/mkstemp/fmkomstemp.h"
#include "nscd.h"
#ifdef WITH_TCB
#include <tcb.h>
#endif /* WITH_TCB */
#include "prototypes.h"
#include "shadowlog_internal.h"
#include "shadowlog.h"
#include "sssd.h"
#include "string/memset/memzero.h"
#include "string/sprintf/aprintf.h"
#include "string/sprintf/snprintf.h"
#include "string/strcmp/streq.h"
#include "string/strcmp/strprefix.h"
#include "string/strerrno.h"
#include "string/strtok/stpsep.h"
/* local function prototypes */
static int lrename (const char *, const char *);
static int check_link_count (const char *file, bool log);
static int do_lock_file (const char *file, const char *lock, bool log);
static /*@null@*/ /*@dependent@*/FILE *fopen_set_perms (
const char *name,
const char *mode,
static /*@null@*/ /*@dependent@*/FILE *fmkstemp_set_perms (
char *name,
const struct stat *sb);
static int create_backup (const char *, FILE *);
static void free_linked_list (struct commonio_db *);
@ -68,50 +68,24 @@ static /*@dependent@*/ /*@null@*/struct commonio_entry *next_entry_by_name (
static int lock_count = 0;
static bool nscd_need_reload = false;
/*
* Simple rename(P) alternative that attempts to rename to symlink
* target.
*/
int lrename (const char *old, const char *new)
{
int res;
char *r = NULL;
struct stat sb;
if (lstat (new, &sb) == 0 && S_ISLNK (sb.st_mode)) {
r = realpath (new, NULL);
if (NULL == r) {
perror ("realpath in lrename()");
} else {
new = r;
}
}
res = rename (old, new);
free (r);
return res;
}
static int check_link_count (const char *file, bool log)
{
struct stat sb;
if (stat (file, &sb) != 0) {
if (log) {
(void) fprintf (shadow_logfd,
(void) fprintf (log_get_logfd(),
"%s: %s file stat error: %s\n",
shadow_progname, file, strerror (errno));
log_get_progname(), file, strerrno());
}
return 0;
}
if (sb.st_nlink != 2) {
if (log) {
fprintf(shadow_logfd,
fprintf(log_get_logfd(),
"%s: %s: lock file already used (nlink: %ju)\n",
shadow_progname, file, (uintmax_t) sb.st_nlink);
log_get_progname(), file, (uintmax_t) sb.st_nlink);
}
return 0;
}
@ -131,21 +105,21 @@ static int do_lock_file (const char *file, const char *lock, bool log)
fd = open (file, O_CREAT | O_TRUNC | O_WRONLY, 0600);
if (-1 == fd) {
if (log) {
(void) fprintf (shadow_logfd,
(void) fprintf (log_get_logfd(),
"%s: %s: %s\n",
shadow_progname, file, strerror (errno));
log_get_progname(), file, strerrno());
}
return 0;
}
pid = getpid ();
SNPRINTF(buf, "%lu", (unsigned long) pid);
stprintf_a(buf, "%lu", (unsigned long) pid);
len = (ssize_t) strlen (buf) + 1;
if (write_full(fd, buf, len) == -1) {
if (log) {
(void) fprintf (shadow_logfd,
(void) fprintf (log_get_logfd(),
"%s: %s file write error: %s\n",
shadow_progname, file, strerror (errno));
log_get_progname(), file, strerrno());
}
(void) close (fd);
unlink (file);
@ -153,9 +127,9 @@ static int do_lock_file (const char *file, const char *lock, bool log)
}
if (fdatasync (fd) == -1) {
if (log) {
(void) fprintf (shadow_logfd,
(void) fprintf (log_get_logfd(),
"%s: %s file sync error: %s\n",
shadow_progname, file, strerror (errno));
log_get_progname(), file, strerrno());
}
(void) close (fd);
unlink (file);
@ -172,21 +146,21 @@ static int do_lock_file (const char *file, const char *lock, bool log)
fd = open (lock, O_RDWR);
if (-1 == fd) {
if (log) {
(void) fprintf (shadow_logfd,
(void) fprintf (log_get_logfd(),
"%s: %s: %s\n",
shadow_progname, lock, strerror (errno));
log_get_progname(), lock, strerrno());
}
unlink (file);
errno = EINVAL;
return 0;
}
len = read (fd, buf, sizeof (buf) - 1);
len = read(fd, buf, sizeof(buf) - 1);
close (fd);
if (len <= 0) {
if (log) {
(void) fprintf (shadow_logfd,
(void) fprintf (log_get_logfd(),
"%s: existing lock file %s without a PID\n",
shadow_progname, lock);
log_get_progname(), lock);
}
unlink (file);
errno = EINVAL;
@ -195,9 +169,9 @@ static int do_lock_file (const char *file, const char *lock, bool log)
stpcpy(&buf[len], "");
if (get_pid(buf, &pid) == -1) {
if (log) {
(void) fprintf (shadow_logfd,
(void) fprintf (log_get_logfd(),
"%s: existing lock file %s with an invalid PID '%s'\n",
shadow_progname, lock, buf);
log_get_progname(), lock, buf);
}
unlink (file);
errno = EINVAL;
@ -205,9 +179,9 @@ static int do_lock_file (const char *file, const char *lock, bool log)
}
if (kill (pid, 0) == 0) {
if (log) {
(void) fprintf (shadow_logfd,
(void) fprintf (log_get_logfd(),
"%s: lock %s already used by PID %lu\n",
shadow_progname, lock, (unsigned long) pid);
log_get_progname(), lock, (unsigned long) pid);
}
unlink (file);
errno = EEXIST;
@ -215,9 +189,9 @@ static int do_lock_file (const char *file, const char *lock, bool log)
}
if (unlink (lock) != 0) {
if (log) {
(void) fprintf (shadow_logfd,
(void) fprintf (log_get_logfd(),
"%s: cannot get lock %s: %s\n",
shadow_progname, lock, strerror (errno));
log_get_progname(), lock, strerrno());
}
unlink (file);
return 0;
@ -228,9 +202,9 @@ static int do_lock_file (const char *file, const char *lock, bool log)
retval = check_link_count (file, log);
} else {
if (log) {
(void) fprintf (shadow_logfd,
(void) fprintf (log_get_logfd(),
"%s: cannot get lock %s: %s\n",
shadow_progname, lock, strerror (errno));
log_get_progname(), lock, strerrno());
}
}
@ -239,17 +213,13 @@ static int do_lock_file (const char *file, const char *lock, bool log)
}
static /*@null@*/ /*@dependent@*/FILE *fopen_set_perms (
const char *name,
const char *mode,
static /*@null@*/ /*@dependent@*/FILE *fmkstemp_set_perms (
char *name,
const struct stat *sb)
{
FILE *fp;
mode_t mask;
mask = umask (0777);
fp = fopen (name, mode);
(void) umask (mask);
fp = fmkomstemp(name, 0, 0600);
if (NULL == fp) {
return NULL;
}
@ -265,24 +235,26 @@ static /*@null@*/ /*@dependent@*/FILE *fopen_set_perms (
fail:
(void) fclose (fp);
/* fopen_set_perms is used for intermediate files */
/* fmkstemp_set_perms is used for intermediate files */
(void) unlink (name);
return NULL;
}
static int create_backup (const char *backup, FILE * fp)
static int create_backup (const char *name, FILE * fp)
{
char tmpf[PATH_MAX], target[PATH_MAX];
struct stat sb;
struct utimbuf ub;
FILE *bkfp;
int c;
stprintf_a(tmpf, "%s.cioXXXXXX", name);
if (fstat (fileno (fp), &sb) != 0) {
return -1;
}
bkfp = fopen_set_perms (backup, "w", &sb);
bkfp = fmkstemp_set_perms(tmpf, &sb);
if (NULL == bkfp) {
return -1;
}
@ -298,22 +270,28 @@ static int create_backup (const char *backup, FILE * fp)
}
if ((c != EOF) || (ferror (fp) != 0) || (fflush (bkfp) != 0)) {
(void) fclose (bkfp);
/* FIXME: unlink the backup file? */
unlink(tmpf);
return -1;
}
if (fsync (fileno (bkfp)) != 0) {
(void) fclose (bkfp);
/* FIXME: unlink the backup file? */
unlink(tmpf);
return -1;
}
if (fclose (bkfp) != 0) {
/* FIXME: unlink the backup file? */
unlink(tmpf);
return -1;
}
stprintf_a(target, "%s-", name);
if (rename(tmpf, target) != 0) {
unlink(tmpf);
return -1;
}
ub.actime = sb.st_atime;
ub.modtime = sb.st_mtime;
(void) utime (backup, &ub);
(void) utime(target, &ub);
return 0;
}
@ -328,9 +306,8 @@ static void free_linked_list (struct commonio_db *db)
free (p->line);
if (NULL != p->eptr) {
db->ops->free (p->eptr);
}
if (NULL != p->eptr)
db->ops->cio_free(p->eptr);
free (p);
}
@ -340,7 +317,7 @@ static void free_linked_list (struct commonio_db *db)
int commonio_setname (struct commonio_db *db, const char *name)
{
SNPRINTF(db->filename, "%s", name);
stprintf_a(db->filename, "%s", name);
db->setname = true;
return 1;
}
@ -402,9 +379,9 @@ int commonio_lock (struct commonio_db *db)
if (0 == lock_count) {
if (lckpwdf () == -1) {
if (geteuid () != 0) {
(void) fprintf (shadow_logfd,
(void) fprintf (log_get_logfd(),
"%s: Permission denied.\n",
shadow_progname);
log_get_progname());
}
return 0; /* failure */
}
@ -438,8 +415,8 @@ int commonio_lock (struct commonio_db *db)
}
/* no unnecessary retries on "permission denied" errors */
if (geteuid () != 0) {
(void) fprintf (shadow_logfd, "%s: Permission denied.\n",
shadow_progname);
(void) fprintf (log_get_logfd(), "%s: Permission denied.\n",
log_get_progname());
return 0;
}
}
@ -467,13 +444,13 @@ static void dec_lock_count (void)
}
int commonio_unlock (struct commonio_db *db)
int commonio_unlock (struct commonio_db *db, bool process_selinux)
{
char lock[1029];
if (db->isopen) {
db->readonly = true;
if (commonio_close (db) == 0) {
if (commonio_close (db, process_selinux) == 0) {
if (db->locked) {
dec_lock_count ();
}
@ -486,7 +463,7 @@ int commonio_unlock (struct commonio_db *db)
* then call ulckpwdf() (if used) on last unlock.
*/
db->locked = false;
SNPRINTF(lock, "%s.lock", db->filename);
stprintf_a(lock, "%s.lock", db->filename);
unlink (lock);
dec_lock_count ();
return 1;
@ -548,7 +525,7 @@ static void add_one_entry_nis (struct commonio_db *db,
struct commonio_entry *p;
for (p = db->head; NULL != p; p = p->next) {
if (name_is_nis (p->eptr ? db->ops->getname (p->eptr)
if (name_is_nis (p->eptr ? db->ops->cio_getname(p->eptr)
: p->line)) {
/*@-mustfreeonly@*/
newp->next = p;
@ -567,11 +544,9 @@ static void add_one_entry_nis (struct commonio_db *db,
}
#endif /* KEEP_NIS_AT_END */
/* Initial buffer size, as well as increment if not sufficient
(for reading very long lines in group files). */
#define BUFLEN 4096
int commonio_open (struct commonio_db *db, int mode)
int
commonio_open(struct commonio_db *db, int mode)
{
char *buf;
char *line;
@ -633,31 +608,12 @@ int commonio_open (struct commonio_db *db, int mode)
return 0;
}
buflen = BUFLEN;
buf = MALLOC(buflen, char);
if (NULL == buf)
goto cleanup_errno;
while (db->ops->fgets (buf, buflen, db->fp) == buf) {
buf = NULL;
while (getline(&buf, &buflen, db->fp) != -1) {
struct commonio_entry *p;
while ( (strrchr (buf, '\n') == NULL)
&& (feof (db->fp) == 0)) {
size_t len;
buflen += BUFLEN;
buf = REALLOCF(buf, buflen, char);
if (NULL == buf)
goto cleanup_errno;
len = strlen (buf);
if (db->ops->fgets (buf + len,
(int) (buflen - len),
db->fp) == NULL) {
goto cleanup_buf;
}
}
stpsep(buf, "\n");
if (stpsep(buf, "\n") == NULL)
goto cleanup_buf;
line = strdup (buf);
if (NULL == line) {
@ -667,16 +623,16 @@ int commonio_open (struct commonio_db *db, int mode)
if (name_is_nis (line)) {
eptr = NULL;
} else {
eptr = db->ops->parse (line);
eptr = db->ops->cio_parse(line);
if (NULL != eptr) {
eptr = db->ops->dup (eptr);
eptr = db->ops->cio_dup(eptr);
if (NULL == eptr) {
goto cleanup_line;
}
}
}
p = MALLOC(1, struct commonio_entry);
p = malloc_T(1, struct commonio_entry);
if (NULL == p) {
goto cleanup_entry;
}
@ -694,7 +650,7 @@ int commonio_open (struct commonio_db *db, int mode)
goto cleanup_errno;
}
if ((NULL != db->ops->open_hook) && (db->ops->open_hook () == 0)) {
if ((NULL != db->ops->cio_open_hook) && (db->ops->cio_open_hook() == 0)) {
goto cleanup_errno;
}
@ -702,9 +658,8 @@ int commonio_open (struct commonio_db *db, int mode)
return 1;
cleanup_entry:
if (NULL != eptr) {
db->ops->free (eptr);
}
if (NULL != eptr)
db->ops->cio_free(eptr);
cleanup_line:
free (line);
cleanup_buf:
@ -719,6 +674,7 @@ int commonio_open (struct commonio_db *db, int mode)
return 0;
}
/*
* Sort given db according to cmp function (usually compares uids)
*/
@ -752,7 +708,7 @@ commonio_sort (struct commonio_db *db, int (*cmp) (const void *, const void *))
return 0;
}
entries = MALLOC(n, struct commonio_entry *);
entries = malloc_T(n, struct commonio_entry *);
if (entries == NULL) {
return -1;
}
@ -771,7 +727,7 @@ commonio_sort (struct commonio_db *db, int (*cmp) (const void *, const void *))
entries[n] = ptr;
n++;
}
qsort (entries, n, sizeof (struct commonio_entry *), cmp);
qsort(entries, n, sizeof(struct commonio_entry *), cmp);
/* Take care of the head and tail separately */
db->head = entries[0];
@ -820,14 +776,14 @@ int commonio_sort_wrt (struct commonio_db *shadow,
if (NULL == pw_ptr->eptr) {
continue;
}
name = passwd->ops->getname (pw_ptr->eptr);
name = passwd->ops->cio_getname(pw_ptr->eptr);
for (spw_ptr = shadow->head;
NULL != spw_ptr;
spw_ptr = spw_ptr->next) {
if (NULL == spw_ptr->eptr) {
continue;
}
if (streq(name, shadow->ops->getname(spw_ptr->eptr)))
if (streq(name, shadow->ops->cio_getname(spw_ptr->eptr)))
break;
}
if (NULL == spw_ptr) {
@ -869,13 +825,13 @@ static int write_all (const struct commonio_db *db)
if (p->changed) {
eptr = p->eptr;
assert (NULL != eptr);
if (db->ops->put (eptr, db->fp) != 0) {
if (db->ops->cio_put(eptr, db->fp) != 0) {
return -1;
}
} else if (NULL != p->line) {
if (db->ops->fputs (p->line, db->fp) == EOF) {
if (fputs(p->line, db->fp) == EOF)
return -1;
}
if (putc ('\n', db->fp) == EOF) {
return -1;
}
@ -885,10 +841,11 @@ static int write_all (const struct commonio_db *db)
}
int commonio_close (struct commonio_db *db)
int
commonio_close(struct commonio_db *db, MAYBE_UNUSED bool process_selinux)
{
bool errors = false;
char buf[1024];
char tmpf[PATH_MAX];
struct stat sb;
if (!db->isopen) {
@ -905,11 +862,11 @@ int commonio_close (struct commonio_db *db)
goto success;
}
if ((NULL != db->ops->close_hook) && (db->ops->close_hook () == 0)) {
if ((NULL != db->ops->cio_close_hook) && (db->ops->cio_close_hook() == 0)) {
goto fail;
}
memzero (&sb, sizeof sb);
memzero(&sb, sizeof(sb));
if (NULL != db->fp) {
if (fstat (fileno (db->fp), &sb) != 0) {
(void) fclose (db->fp);
@ -920,18 +877,13 @@ int commonio_close (struct commonio_db *db)
/*
* Create backup file.
*/
if (SNPRINTF(buf, "%s-", db->filename) == -1) {
(void) fclose (db->fp);
db->fp = NULL;
goto fail;
}
#ifdef WITH_SELINUX
if (set_selinux_file_context (db->filename, S_IFREG) != 0) {
if (process_selinux
&& set_selinux_file_context (db->filename, S_IFREG) != 0) {
errors = true;
}
#endif
if (create_backup (buf, db->fp) != 0) {
if (create_backup(db->filename, db->fp) != 0) {
errors = true;
}
@ -942,7 +894,8 @@ int commonio_close (struct commonio_db *db)
db->fp = NULL;
#ifdef WITH_SELINUX
if (reset_selinux_file_context () != 0) {
if (process_selinux
&& reset_selinux_file_context () != 0) {
errors = true;
}
#endif
@ -957,16 +910,17 @@ int commonio_close (struct commonio_db *db)
sb.st_gid = db->st_gid;
}
if (SNPRINTF(buf, "%s+", db->filename) == -1)
if (stprintf_a(tmpf, "%s.cioXXXXXX", db->filename) == -1)
goto fail;
#ifdef WITH_SELINUX
if (set_selinux_file_context (db->filename, S_IFREG) != 0) {
if (process_selinux
&& set_selinux_file_context (db->filename, S_IFREG) != 0) {
errors = true;
}
#endif
db->fp = fopen_set_perms (buf, "w", &sb);
db->fp = fmkstemp_set_perms(tmpf, &sb);
if (NULL == db->fp) {
goto fail;
}
@ -990,16 +944,17 @@ int commonio_close (struct commonio_db *db)
db->fp = NULL;
if (errors) {
unlink (buf);
unlink(tmpf);
goto fail;
}
if (lrename (buf, db->filename) != 0) {
if (rename(tmpf, db->filename) != 0) {
goto fail;
}
#ifdef WITH_SELINUX
if (reset_selinux_file_context () != 0) {
if (process_selinux
&& reset_selinux_file_context () != 0) {
goto fail;
}
#endif
@ -1029,7 +984,7 @@ static /*@dependent@*/ /*@null@*/struct commonio_entry *next_entry_by_name (
for (p = pos; NULL != p; p = p->next) {
ep = p->eptr;
if ( (NULL != ep)
&& streq(db->ops->getname(ep), name)) {
&& streq(db->ops->cio_getname(ep), name)) {
break;
}
}
@ -1053,19 +1008,19 @@ int commonio_update (struct commonio_db *db, const void *eptr)
errno = EINVAL;
return 0;
}
nentry = db->ops->dup (eptr);
nentry = db->ops->cio_dup(eptr);
if (NULL == nentry) {
errno = ENOMEM;
return 0;
}
p = find_entry_by_name (db, db->ops->getname (eptr));
p = find_entry_by_name(db, db->ops->cio_getname(eptr));
if (NULL != p) {
if (next_entry_by_name (db, p->next, db->ops->getname (eptr)) != NULL) {
fprintf (shadow_logfd, _("Multiple entries named '%s' in %s. Please fix this with pwck or grpck.\n"), db->ops->getname (eptr), db->filename);
db->ops->free (nentry);
if (next_entry_by_name(db, p->next, db->ops->cio_getname(eptr)) != NULL) {
fprintf(log_get_logfd(), _("Multiple entries named '%s' in %s. Please fix this with pwck or grpck.\n"), db->ops->cio_getname(eptr), db->filename);
db->ops->cio_free(nentry);
return 0;
}
db->ops->free (p->eptr);
db->ops->cio_free(p->eptr);
p->eptr = nentry;
p->changed = true;
db->cursor = p;
@ -1074,9 +1029,9 @@ int commonio_update (struct commonio_db *db, const void *eptr)
return 1;
}
/* not found, new entry */
p = MALLOC(1, struct commonio_entry);
p = malloc_T(1, struct commonio_entry);
if (NULL == p) {
db->ops->free (nentry);
db->ops->cio_free(nentry);
errno = ENOMEM;
return 0;
}
@ -1105,15 +1060,15 @@ int commonio_append (struct commonio_db *db, const void *eptr)
errno = EINVAL;
return 0;
}
nentry = db->ops->dup (eptr);
nentry = db->ops->cio_dup(eptr);
if (NULL == nentry) {
errno = ENOMEM;
return 0;
}
/* new entry */
p = MALLOC(1, struct commonio_entry);
p = malloc_T(1, struct commonio_entry);
if (NULL == p) {
db->ops->free (nentry);
db->ops->cio_free(nentry);
errno = ENOMEM;
return 0;
}
@ -1166,7 +1121,7 @@ int commonio_remove (struct commonio_db *db, const char *name)
return 0;
}
if (next_entry_by_name (db, p->next, name) != NULL) {
fprintf (shadow_logfd, _("Multiple entries named '%s' in %s. Please fix this with pwck or grpck.\n"), name, db->filename);
fprintf (log_get_logfd(), _("Multiple entries named '%s' in %s. Please fix this with pwck or grpck.\n"), name, db->filename);
return 0;
}
@ -1174,9 +1129,8 @@ int commonio_remove (struct commonio_db *db, const char *name)
free (p->line);
if (NULL != p->eptr) {
db->ops->free (p->eptr);
}
if (NULL != p->eptr)
db->ops->cio_free(p->eptr);
free(p);

View File

@ -35,39 +35,30 @@ struct commonio_ops {
* Make a copy of the object (for example, struct passwd)
* and all strings pointed by it, in malloced memory.
*/
/*@null@*/ /*@only@*/void *(*dup) (const void *);
/*@null@*/ /*@only@*/void *(*cio_dup)(const void *);
/*
* free() the object including any strings pointed by it.
*/
void (*free)(/*@only@*/void *);
void (*cio_free)(/*@only@*/void *);
/*
* Return the name of the object (for example, pw_name
* for struct passwd).
*/
const char *(*getname) (const void *);
const char *(*cio_getname)(const void *);
/*
* Parse a string, return object (in static area -
* should be copied using the dup operation above).
*/
void *(*parse) (const char *);
void *(*cio_parse)(const char *);
/*
* Write the object to the file (this calls putpwent()
* for struct passwd, for example).
*/
int (*put) (const void *, FILE *);
/*
* fgets and fputs (can be replaced by versions that
* understand line continuation conventions).
*/
ATTR_ACCESS(write_only, 1, 2)
/*@null@*/char *(*fgets)(/*@returned@*/char *restrict s, int n,
FILE *restrict stream);
int (*fputs) (const char *, FILE *);
int (*cio_put)(const void *, FILE *);
/*
* open_hook and close_hook.
@ -75,8 +66,8 @@ struct commonio_ops {
* is open or before it is closed.
* They return 0 on failure and 1 on success.
*/
/*@null@*/int (*open_hook) (void);
/*@null@*/int (*close_hook) (void);
/*@null@*/int (*cio_open_hook)(void);
/*@null@*/int (*cio_close_hook)(void);
};
/*
@ -103,7 +94,7 @@ struct commonio_db {
#endif
/*
* Default permissions and owner for newly created data file.
*/
*/
mode_t st_mode;
uid_t st_uid;
gid_t st_gid;
@ -138,8 +129,8 @@ extern int commonio_append (struct commonio_db *, const void *);
extern int commonio_remove (struct commonio_db *, const char *);
extern int commonio_rewind (struct commonio_db *);
extern /*@observer@*/ /*@null@*/const void *commonio_next (struct commonio_db *);
extern int commonio_close (struct commonio_db *);
extern int commonio_unlock (struct commonio_db *);
extern int commonio_close (struct commonio_db *, bool);
extern int commonio_unlock (struct commonio_db *, bool);
extern void commonio_del_entry (struct commonio_db *,
const struct commonio_entry *);
extern int commonio_sort_wrt (struct commonio_db *shadow,

View File

@ -8,7 +8,7 @@
* SPDX-License-Identifier: BSD-3-Clause
*/
#include <config.h>
#include "config.h"
#include <stdio.h>
#include <string.h>
@ -52,7 +52,7 @@ is_listed(const char *cfgin, const char *tty, bool def)
if (*cons != '/') {
char *pbuf;
STRTCPY(buf, cons);
strtcpy_a(buf, cons);
pbuf = buf;
while (NULL != (s = strsep(&pbuf, ":"))) {
if (streq(s, tty)) {
@ -76,7 +76,7 @@ is_listed(const char *cfgin, const char *tty, bool def)
* See if this tty is listed in the console file.
*/
while (fgets (buf, sizeof (buf), fp) != NULL) {
while (fgets(buf, sizeof(buf), fp) != NULL) {
stpsep(buf, "\n");
if (streq(buf, tty)) {
(void) fclose (fp);

View File

@ -7,7 +7,7 @@
* SPDX-License-Identifier: BSD-3-Clause
*/
#include <config.h>
#include "config.h"
#ident "$Id$"
@ -19,7 +19,7 @@
#include <stdio.h>
#include <string.h>
#include "alloc/x/xmalloc.h"
#include "alloc/malloc.h"
#include "attr.h"
#include "fs/readlink/areadlink.h"
#include "prototypes.h"
@ -27,21 +27,16 @@
#ifdef WITH_SELINUX
#include <selinux/selinux.h>
#endif /* WITH_SELINUX */
#if defined(WITH_ACL) || defined(WITH_ATTR)
#if defined(WITH_ACL)
#include <stdarg.h>
#include <attr/error_context.h>
#endif /* WITH_ACL || WITH_ATTR */
#ifdef WITH_ACL
#include <acl/libacl.h>
#endif /* WITH_ACL */
#ifdef WITH_ATTR
#include <attr/libattr.h>
#endif /* WITH_ATTR */
#include "shadowlog.h"
#include "string/sprintf/aprintf.h"
#include "string/sprintf/xaprintf.h"
#include "string/strcmp/streq.h"
#include "string/strcmp/strprefix.h"
#include "string/strerrno.h"
static /*@null@*/const char *src_orig;
@ -63,29 +58,23 @@ struct path_info {
};
static int copy_entry (const struct path_info *src, const struct path_info *dst,
bool reset_selinux,
uid_t old_uid, uid_t new_uid,
gid_t old_gid, gid_t new_gid);
static int copy_dir (const struct path_info *src, const struct path_info *dst,
bool reset_selinux,
const struct stat *statp, const struct timespec mt[],
uid_t old_uid, uid_t new_uid,
gid_t old_gid, gid_t new_gid);
static int copy_symlink (const struct path_info *src, const struct path_info *dst,
MAYBE_UNUSED bool reset_selinux,
const struct stat *statp, const struct timespec mt[],
uid_t old_uid, uid_t new_uid,
gid_t old_gid, gid_t new_gid);
static int copy_hardlink (const struct path_info *dst,
MAYBE_UNUSED bool reset_selinux,
struct link_name *lp);
static int copy_special (const struct path_info *src, const struct path_info *dst,
bool reset_selinux,
const struct stat *statp, const struct timespec mt[],
uid_t old_uid, uid_t new_uid,
gid_t old_gid, gid_t new_gid);
static int copy_file (const struct path_info *src, const struct path_info *dst,
bool reset_selinux,
const struct stat *statp, const struct timespec mt[],
uid_t old_uid, uid_t new_uid,
gid_t old_gid, gid_t new_gid);
@ -96,15 +85,15 @@ static int fchown_if_needed (int fdst, const struct stat *statp,
uid_t old_uid, uid_t new_uid,
gid_t old_gid, gid_t new_gid);
#if defined(WITH_ACL) || defined(WITH_ATTR)
#if defined(WITH_ACL)
/*
* error_acl - format the error messages for the ACL and EQ libraries.
*/
format_attr(printf, 2, 3)
static void error_acl (MAYBE_UNUSED struct error_context *ctx, const char *fmt, ...)
static void
error_acl(struct error_context *, const char *fmt, ...)
{
va_list ap;
FILE *shadow_logfd = log_get_logfd();
/* ignore the case when destination does not support ACLs
* or extended attributes */
@ -114,18 +103,18 @@ static void error_acl (MAYBE_UNUSED struct error_context *ctx, const char *fmt,
}
va_start (ap, fmt);
(void) fprintf (shadow_logfd, _("%s: "), log_get_progname());
if (vfprintf (shadow_logfd, fmt, ap) != 0) {
(void) fputs (_(": "), shadow_logfd);
(void) fprintf (log_get_logfd(), _("%s: "), log_get_progname());
if (vfprintf (log_get_logfd(), fmt, ap) != 0) {
(void) fputs (_(": "), log_get_logfd());
}
(void) fprintf (shadow_logfd, "%s\n", strerror (errno));
(void) fprintf(log_get_logfd(), "%s\n", strerrno());
va_end (ap);
}
static struct error_context ctx = {
error_acl, NULL, NULL
};
#endif /* WITH_ACL || WITH_ATTR */
#endif /* WITH_ACL */
#ifdef WITH_ACL
static int perm_copy_path(const struct path_info *src,
@ -152,32 +141,6 @@ static int perm_copy_path(const struct path_info *src,
}
#endif /* WITH_ACL */
#ifdef WITH_ATTR
static int attr_copy_path(const struct path_info *src,
const struct path_info *dst,
int (*callback) (const char *, struct error_context *),
struct error_context *errctx)
{
int src_fd, dst_fd, ret;
src_fd = openat(src->dirfd, src->name, O_RDONLY | O_NOFOLLOW | O_NONBLOCK | O_CLOEXEC);
if (src_fd < 0) {
return -1;
}
dst_fd = openat(dst->dirfd, dst->name, O_RDONLY | O_NOFOLLOW | O_NONBLOCK | O_CLOEXEC);
if (dst_fd < 0) {
(void) close (src_fd);
return -1;
}
ret = attr_copy_fd(src->full_path, src_fd, dst->full_path, dst_fd, callback, errctx);
(void) close (src_fd);
(void) close (dst_fd);
return ret;
}
#endif /* WITH_ATTR */
/*
* remove_link - delete a link from the linked list
*/
@ -230,7 +193,7 @@ static /*@exposed@*/ /*@null@*/struct link_name *check_link (const char *name, c
return NULL;
}
lp = XMALLOC(1, struct link_name);
lp = xmalloc_T(1, struct link_name);
lp->ln_dev = sb->st_dev;
lp->ln_ino = sb->st_ino;
lp->ln_count = sb->st_nlink;
@ -242,7 +205,7 @@ static /*@exposed@*/ /*@null@*/struct link_name *check_link (const char *name, c
}
static int copy_tree_impl (const struct path_info *src, const struct path_info *dst,
bool copy_root, bool reset_selinux,
bool copy_root,
uid_t old_uid, uid_t new_uid,
gid_t old_gid, gid_t new_gid)
{
@ -270,8 +233,7 @@ static int copy_tree_impl (const struct path_info *src, const struct path_info *
return -1;
}
return copy_entry (src, dst, reset_selinux,
old_uid, new_uid, old_gid, new_gid);
return copy_entry (src, dst, old_uid, new_uid, old_gid, new_gid);
}
/*
@ -310,7 +272,7 @@ static int copy_tree_impl (const struct path_info *src, const struct path_info *
dst_orig = dst->full_path;
set_orig = true;
}
while ((0 == err) && (ent = readdir (dir)) != NULL) {
while (0 == err && NULL != (ent = readdir(dir))) {
char *src_name = NULL;
char *dst_name;
struct path_info src_entry, dst_entry;
@ -342,7 +304,7 @@ static int copy_tree_impl (const struct path_info *src, const struct path_info *
dst_entry.dirfd = dst_fd;
dst_entry.name = ent->d_name;
err = copy_entry(&src_entry, &dst_entry, reset_selinux,
err = copy_entry(&src_entry, &dst_entry,
old_uid, new_uid, old_gid, new_gid);
free(dst_name);
@ -398,7 +360,6 @@ skip:
* to -1.
*/
static int copy_entry (const struct path_info *src, const struct path_info *dst,
bool reset_selinux,
uid_t old_uid, uid_t new_uid,
gid_t old_gid, gid_t new_gid)
{
@ -420,7 +381,7 @@ static int copy_entry (const struct path_info *src, const struct path_info *dst,
mt[1].tv_nsec = sb.st_mtim.tv_nsec;
if (S_ISDIR (sb.st_mode)) {
err = copy_dir (src, dst, reset_selinux, &sb, mt,
err = copy_dir (src, dst, &sb, mt,
old_uid, new_uid, old_gid, new_gid);
}
@ -437,7 +398,7 @@ static int copy_entry (const struct path_info *src, const struct path_info *dst,
*/
else if (S_ISLNK (sb.st_mode)) {
err = copy_symlink (src, dst, reset_selinux, &sb, mt,
err = copy_symlink (src, dst, &sb, mt,
old_uid, new_uid, old_gid, new_gid);
}
@ -446,7 +407,7 @@ static int copy_entry (const struct path_info *src, const struct path_info *dst,
*/
else if ((lp = check_link (src->full_path, &sb)) != NULL) {
err = copy_hardlink (dst, reset_selinux, lp);
err = copy_hardlink (dst, lp);
}
/*
@ -456,7 +417,7 @@ static int copy_entry (const struct path_info *src, const struct path_info *dst,
*/
else if (!S_ISREG (sb.st_mode)) {
err = copy_special (src, dst, reset_selinux, &sb, mt,
err = copy_special (src, dst, &sb, mt,
old_uid, new_uid, old_gid, new_gid);
}
@ -466,7 +427,7 @@ static int copy_entry (const struct path_info *src, const struct path_info *dst,
*/
else {
err = copy_file (src, dst, reset_selinux, &sb, mt,
err = copy_file (src, dst, &sb, mt,
old_uid, new_uid, old_gid, new_gid);
}
@ -484,7 +445,6 @@ static int copy_entry (const struct path_info *src, const struct path_info *dst,
* Return 0 on success, -1 on error.
*/
static int copy_dir (const struct path_info *src, const struct path_info *dst,
bool reset_selinux,
const struct stat *statp, const struct timespec mt[],
uid_t old_uid, uid_t new_uid,
gid_t old_gid, gid_t new_gid)
@ -502,14 +462,14 @@ static int copy_dir (const struct path_info *src, const struct path_info *dst,
return -1;
}
#endif /* WITH_SELINUX */
/*
* If the destination is already a directory, don't change it
* but copy into it (recursively).
*/
if (fstatat(dst->dirfd, dst->name, &dst_sb, AT_SYMLINK_NOFOLLOW) == 0 && S_ISDIR(dst_sb.st_mode)) {
return (copy_tree_impl (src, dst, false, reset_selinux,
old_uid, new_uid, old_gid, new_gid) != 0);
}
/*
* If the destination is already a directory, don't change it
* but copy into it (recursively).
*/
if (fstatat(dst->dirfd, dst->name, &dst_sb, AT_SYMLINK_NOFOLLOW) == 0 && S_ISDIR(dst_sb.st_mode)) {
return (copy_tree_impl (src, dst, false,
old_uid, new_uid, old_gid, new_gid) != 0);
}
if ( (mkdirat (dst->dirfd, dst->name, 0700) != 0)
|| (chownat_if_needed (dst, statp,
@ -519,19 +479,7 @@ static int copy_dir (const struct path_info *src, const struct path_info *dst,
|| ( (perm_copy_path (src, dst, &ctx) != 0)
&& (errno != 0))
#endif /* WITH_ACL */
#ifdef WITH_ATTR
/*
* If the third parameter is NULL, all extended attributes
* except those that define Access Control Lists are copied.
* ACLs are excluded by default because copying them between
* file systems with and without ACL support needs some
* additional logic so that no unexpected permissions result.
*/
|| ( !reset_selinux
&& (attr_copy_path (src, dst, NULL, &ctx) != 0)
&& (errno != 0))
#endif /* WITH_ATTR */
|| (copy_tree_impl (src, dst, false, reset_selinux,
|| (copy_tree_impl (src, dst, false,
old_uid, new_uid, old_gid, new_gid) != 0)
|| (utimensat (dst->dirfd, dst->name, mt, AT_SYMLINK_NOFOLLOW) != 0)) {
err = -1;
@ -551,7 +499,6 @@ static int copy_dir (const struct path_info *src, const struct path_info *dst,
* Return 0 on success, -1 on error.
*/
static int copy_symlink (const struct path_info *src, const struct path_info *dst,
MAYBE_UNUSED bool reset_selinux,
const struct stat *statp, const struct timespec mt[],
uid_t old_uid, uid_t new_uid,
gid_t old_gid, gid_t new_gid)
@ -622,7 +569,6 @@ static int copy_symlink (const struct path_info *src, const struct path_info *ds
* Return 0 on success, -1 on error.
*/
static int copy_hardlink (const struct path_info *dst,
MAYBE_UNUSED bool reset_selinux,
struct link_name *lp)
{
/* FIXME: selinux, ACL, Extended Attributes needed? */
@ -653,8 +599,7 @@ static int copy_hardlink (const struct path_info *dst,
* Return 0 on success, -1 on error.
*/
static int
copy_special(const struct path_info *src, const struct path_info *dst,
bool reset_selinux,
copy_special(MAYBE_UNUSED const struct path_info *src, const struct path_info *dst,
const struct stat *statp, const struct timespec mt[],
uid_t old_uid, uid_t new_uid,
gid_t old_gid, gid_t new_gid)
@ -678,20 +623,6 @@ copy_special(const struct path_info *src, const struct path_info *dst,
return -1;
#endif
#if defined(WITH_ATTR)
/*
* If the third parameter is NULL, all extended attributes
* except those that define Access Control Lists are copied.
* ACLs are excluded by default because copying them between
* file systems with and without ACL support needs some
* additional logic so that no unexpected permissions result.
*/
if (!reset_selinux) {
if (attr_copy_path(src, dst, NULL, &ctx) == -1 && errno != 0)
return -1;
}
#endif
if (utimensat(dst->dirfd, dst->name, mt, AT_SYMLINK_NOFOLLOW) == -1)
return -1;
@ -709,7 +640,6 @@ copy_special(const struct path_info *src, const struct path_info *dst,
* Return 0 on success, -1 on error.
*/
static int copy_file (const struct path_info *src, const struct path_info *dst,
bool reset_selinux,
const struct stat *statp, const struct timespec mt[],
uid_t old_uid, uid_t new_uid,
gid_t old_gid, gid_t new_gid)
@ -737,18 +667,6 @@ static int copy_file (const struct path_info *src, const struct path_info *dst,
|| ( (perm_copy_fd (src->full_path, ifd, dst->full_path, ofd, &ctx) != 0)
&& (errno != 0))
#endif /* WITH_ACL */
#ifdef WITH_ATTR
/*
* If the third parameter is NULL, all extended attributes
* except those that define Access Control Lists are copied.
* ACLs are excluded by default because copying them between
* file systems with and without ACL support needs some
* additional logic so that no unexpected permissions result.
*/
|| ( !reset_selinux
&& (attr_copy_fd (src->full_path, ifd, dst->full_path, ofd, NULL, &ctx) != 0)
&& (errno != 0))
#endif /* WITH_ATTR */
) {
if (ofd >= 0) {
(void) close (ofd);
@ -761,7 +679,7 @@ static int copy_file (const struct path_info *src, const struct path_info *dst,
char buf[8192];
ssize_t cnt;
cnt = read (ifd, buf, sizeof buf);
cnt = read(ifd, buf, sizeof(buf));
if (cnt < 0) {
if (errno == EINTR) {
continue;
@ -801,7 +719,7 @@ static int chown_function ## _if_needed (type_dst dst, \
{ \
uid_t tmpuid = (uid_t) -1; \
gid_t tmpgid = (gid_t) -1; \
\
\
/* Use new_uid if old_uid is set to -1 or if the file was \
* owned by the user. */ \
if (((uid_t) -1 == old_uid) || (statp->st_uid == old_uid)) { \
@ -812,14 +730,14 @@ static int chown_function ## _if_needed (type_dst dst, \
if ((uid_t) -1 == tmpuid) { \
tmpuid = statp->st_uid; \
} \
\
\
if (((gid_t) -1 == old_gid) || (statp->st_gid == old_gid)) { \
tmpgid = new_gid; \
} \
if ((gid_t) -1 == tmpgid) { \
tmpgid = statp->st_gid; \
} \
\
\
return chown_function (dst, tmpuid, tmpgid); \
}
@ -860,9 +778,6 @@ static int chownat_if_needed (const struct path_info *dst,
* copy_tree() walks a directory tree and copies ordinary files
* as it goes.
*
* When reset_selinux is enabled, extended attributes (and thus
* SELinux attributes) are not copied.
*
* old_uid and new_uid are used to set the ownership of the copied
* files. Unless old_uid is set to -1, only the files owned by
* old_uid have their ownership changed to new_uid. In addition, if
@ -872,7 +787,7 @@ static int chownat_if_needed (const struct path_info *dst,
* old_gid/new_gid.
*/
int copy_tree (const char *src_root, const char *dst_root,
bool copy_root, bool reset_selinux,
bool copy_root,
uid_t old_uid, uid_t new_uid,
gid_t old_gid, gid_t new_gid)
{
@ -887,6 +802,5 @@ int copy_tree (const char *src_root, const char *dst_root,
.name = dst_root
};
return copy_tree_impl(&src, &dst, copy_root, reset_selinux,
old_uid, new_uid, old_gid, new_gid);
return copy_tree_impl(&src, &dst, copy_root, old_uid, new_uid, old_gid, new_gid);
}

View File

@ -1,7 +1,7 @@
// SPDX-FileCopyrightText: 2022-2024, Alejandro Colomar <alx@kernel.org>
// SPDX-License-Identifier: BSD-3-Clause
#include <config.h>
#include "config.h"
#ident "$Id$"

View File

@ -51,20 +51,9 @@
#include <dirent.h>
#include <shadow.h>
#if defined(SHADOWGRP)
#include "gshadow_.h"
#endif
#include <limits.h>
#ifndef NGROUPS_MAX
#ifdef NGROUPS
#define NGROUPS_MAX NGROUPS
#else
#define NGROUPS_MAX 64
#endif
#endif
#ifndef PATH_MAX
#define PATH_MAX 4096
#endif
@ -79,11 +68,6 @@
#define LOG_WARN LOG_WARNING
#endif
/* LOG_NOWAIT is deprecated */
#ifndef LOG_NOWAIT
#define LOG_NOWAIT 0
#endif
/* LOG_AUTH is deprecated, use LOG_AUTHPRIV instead */
#ifndef LOG_AUTHPRIV
#define LOG_AUTHPRIV LOG_AUTH
@ -97,7 +81,7 @@
syslogd should log the current system time for each event, and not
trust the formatted time received from the unix domain (or worse,
UDP) socket. -MM */
/* Avoid translated PAM error messages: Set LC_ALL to "C".
/* Avoid translated PAM error messages: set LC_ALL to "C".
* --Nekral */
#define SYSLOG(x) \
do { \
@ -123,7 +107,7 @@
in just one place. */
#ifndef SYSLOG_OPTIONS
/* #define SYSLOG_OPTIONS (LOG_PID | LOG_CONS | LOG_NOWAIT) */
/* #define SYSLOG_OPTIONS (LOG_PID | LOG_CONS) */
#define SYSLOG_OPTIONS (LOG_PID)
#endif
@ -160,10 +144,6 @@
#define GROUP_FILE "/etc/group"
#endif
#ifndef SHADOW_FILE
#define SHADOW_FILE "/etc/shadow"
#endif
#ifndef SUBUID_FILE
#define SUBUID_FILE "/etc/subuid"
#endif
@ -172,12 +152,6 @@
#define SUBGID_FILE "/etc/subgid"
#endif
#ifdef SHADOWGRP
#ifndef SGROUP_FILE
#define SGROUP_FILE "/etc/gshadow"
#endif
#endif
/*
* string to use for the pw_passwd field in /etc/passwd when using
* shadow passwords - most systems use "x" but there are a few

View File

@ -7,7 +7,7 @@
* SPDX-License-Identifier: BSD-3-Clause
*/
#include <config.h>
#include "config.h"
#ident "$Id$"
@ -16,7 +16,7 @@
#include "prototypes.h"
#include "defines.h"
#include "shadowlog_internal.h"
#include "shadowlog.h"
#include "string/strcmp/strprefix.h"
@ -64,7 +64,7 @@
method = &nummethod[0];
}
}
(void) fprintf (shadow_logfd,
(void) fprintf (log_get_logfd(),
_("crypt method not supported by libcrypt? (%s)\n"),
method);
errno = EINVAL;

View File

@ -7,7 +7,7 @@
* SPDX-License-Identifier: BSD-3-Clause
*/
#include <config.h>
#include "config.h"
#ident "$Id$"
@ -16,15 +16,16 @@
#include <stdlib.h>
#include <string.h>
#include "alloc/x/xmalloc.h"
#include "alloc/x/xrealloc.h"
#include "alloc/malloc.h"
#include "alloc/realloc.h"
#include "prototypes.h"
#include "defines.h"
#include "shadowlog.h"
#include "string/sprintf/aprintf.h"
#include "string/sprintf/snprintf.h"
#include "string/sprintf/xaprintf.h"
#include "string/sprintf/aprintf.h"
#include "string/strcmp/strprefix.h"
#include "string/strdup/xstrdup.h"
#include "string/strdup/strdup.h"
/*
@ -67,7 +68,7 @@ static const char *const noslash[] = {
*/
void initenv (void)
{
newenvp = XMALLOC(NEWENVP_STEP, char *);
newenvp = xmalloc_T(NEWENVP_STEP, char *);
*newenvp = NULL;
}
@ -134,7 +135,7 @@ void addenv (const char *string, /*@null@*/const char *value)
update_environ = (environ == newenvp);
newenvp = XREALLOC(newenvp, newenvc + NEWENVP_STEP, char *);
newenvp = xrealloc_T(newenvp, newenvc + NEWENVP_STEP, char *);
/*
* If this is our current environment, update
@ -163,13 +164,13 @@ void set_env (int argc, char *const *argv)
char *cp;
for (; argc > 0; argc--, argv++) {
if (strlen (*argv) >= sizeof variable) {
if (strlen(*argv) >= sizeof(variable)) {
continue; /* ignore long entries */
}
cp = strchr (*argv, '=');
if (NULL == cp) {
assert(SNPRINTF(variable, "L%d", noname) != -1);
assert(stprintf_a(variable, "L%d", noname) != -1);
noname++;
addenv (variable, *argv);
} else {
@ -226,9 +227,9 @@ void sanitize_env (void)
if (!strprefix(*cur, *bad)) {
continue;
}
if (strchr (*cur, '/') == NULL) {
if (!strchr(*cur, '/'))
continue; /* OK */
}
for (move = cur; NULL != *move; move++) {
*move = *(move + 1);
}

10
lib/exit_if_null.c Normal file
View File

@ -0,0 +1,10 @@
// SPDX-FileCopyrightText: 2023-2025, Alejandro Colomar <alx@kernel.org>
// SPDX-License-Identifier: BSD-3-Clause
#include "config.h"
#include "exit_if_null.h"
extern inline void exit_if_null_(void *p);

48
lib/exit_if_null.h Normal file
View File

@ -0,0 +1,48 @@
// SPDX-FileCopyrightText: 2023-2025, Alejandro Colomar <alx@kernel.org>
// SPDX-License-Identifier: BSD-3-Clause
#ifndef SHADOW_INCLUDE_LIB_EXIT_IF_NULL_H_
#define SHADOW_INCLUDE_LIB_EXIT_IF_NULL_H_
#include "config.h"
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include "shadowlog.h"
#include "string/strerrno.h"
/*
* This macro is used for implementing x*() variants of functions that
* allocate memory, such as xstrdup() for wrapping strdup(3). The macro
* returns the input pointer transparently, with the same type, but
* calls exit(3) if the input is a null pointer (thus, if the allocation
* failed).
*/
#define exit_if_null(p) \
({ \
__auto_type p_ = p; \
\
exit_if_null_(p_); \
p_; \
})
inline void exit_if_null_(void *p);
inline void
exit_if_null_(void *p)
{
if (p == NULL) {
fprintf(log_get_logfd(), "%s: %s\n", log_get_progname(), strerrno());
exit(13);
}
}
#endif // include guard

View File

@ -7,7 +7,7 @@
* SPDX-License-Identifier: BSD-3-Clause
*/
#include <config.h>
#include "config.h"
#ident "$Id$"
@ -34,7 +34,7 @@
void failure (uid_t uid, const char *tty, struct faillog *fl)
{
int fd;
off_t offset_uid = (off_t) (sizeof *fl) * uid;
off_t offset_uid = (off_t) sizeof(*fl) * uid;
/*
* Don't do anything if failure logging isn't set up.
@ -59,7 +59,7 @@ void failure (uid_t uid, const char *tty, struct faillog *fl)
*/
if ( (lseek (fd, offset_uid, SEEK_SET) != offset_uid)
|| (read (fd, fl, sizeof *fl) != (ssize_t) sizeof *fl)) {
|| (read(fd, fl, sizeof(*fl)) != (ssize_t) sizeof(*fl))) {
/* This is not necessarily a failure. The file is
* initially zero length.
*
@ -67,7 +67,7 @@ void failure (uid_t uid, const char *tty, struct faillog *fl)
* might reset the counter. But the new failure will be
* logged.
*/
memzero (fl, sizeof *fl);
memzero(fl, sizeof(*fl));
}
/*
@ -81,7 +81,7 @@ void failure (uid_t uid, const char *tty, struct faillog *fl)
fl->fail_cnt++;
}
STRTCPY(fl->fail_line, tty);
strtcpy_a(fl->fail_line, tty);
fl->fail_time = time(NULL);
/*
@ -92,7 +92,7 @@ void failure (uid_t uid, const char *tty, struct faillog *fl)
*/
if ( (lseek (fd, offset_uid, SEEK_SET) != offset_uid)
|| (write_full(fd, fl, sizeof *fl) == -1)) {
|| (write_full(fd, fl, sizeof(*fl)) == -1)) {
goto err_write;
}
@ -150,7 +150,7 @@ int failcheck (uid_t uid, struct faillog *fl, bool failed)
{
int fd;
struct faillog fail;
off_t offset_uid = (off_t) (sizeof *fl) * uid;
off_t offset_uid = (off_t) sizeof(*fl) * uid;
/*
* Suppress the check if the log file isn't there.
@ -182,7 +182,7 @@ int failcheck (uid_t uid, struct faillog *fl, bool failed)
*/
if ( (lseek (fd, offset_uid, SEEK_SET) != offset_uid)
|| (read (fd, fl, sizeof *fl) != (ssize_t) sizeof *fl)) {
|| (read(fd, fl, sizeof(*fl)) != (ssize_t) sizeof(*fl))) {
(void) close (fd);
return 1;
}
@ -204,7 +204,7 @@ int failcheck (uid_t uid, struct faillog *fl, bool failed)
fail.fail_cnt = 0;
if ( (lseek (fd, offset_uid, SEEK_SET) != offset_uid)
|| (write_full(fd, &fail, sizeof fail) == -1)) {
|| (write_full(fd, &fail, sizeof(fail)) == -1)) {
goto err_write;
}
@ -252,7 +252,7 @@ void failprint (const struct faillog *fail)
/*
* Print all information we have.
*/
STRFTIME(lasttimeb, "%c", tp);
strftime_a(lasttimeb, "%c", tp);
/*@-formatconst@*/
(void) printf (ngettext ("%d failure since last login.\n"

View File

@ -7,13 +7,14 @@
* SPDX-License-Identifier: BSD-3-Clause
*/
#include <config.h>
#include "config.h"
#include "fields.h"
#include <ctype.h>
#include <string.h>
#include <stddef.h>
#include <stdio.h>
#include <string.h>
#include "prototypes.h"
#include "string/ctype/strisascii/strisprint.h"
@ -62,15 +63,14 @@ change_field(char *buf, size_t maxsize, const char *prompt)
char newf[200];
char *cp;
if (maxsize > sizeof (newf)) {
maxsize = sizeof (newf);
if (maxsize > sizeof(newf)) {
maxsize = sizeof(newf);
}
printf ("\t%s [%s]: ", prompt, buf);
(void) fflush (stdout);
if (fgets (newf, maxsize, stdin) != newf) {
if (fgets(newf, maxsize, stdin) == NULL)
return;
}
if (stpsep(newf, "\n") == NULL)
return;

View File

@ -5,7 +5,7 @@
#define _SHADOW_INCLUDE_LIB_FIELDS_H_
#include <config.h>
#include "config.h"
#include <stddef.h>

View File

@ -6,7 +6,7 @@
* SPDX-License-Identifier: BSD-3-Clause
*/
#include <config.h>
#include "config.h"
#include <assert.h>
#include <stdint.h>
@ -18,16 +18,18 @@
#include "groupio.h"
#include "getdef.h"
#include "shadowlog.h"
#include "string/strerrno.h"
/*
* get_ranges - Get the minimum and maximum ID ranges for the search
*
* This function will return the minimum and maximum ranges for IDs
*
* 0: The function completed successfully
* EINVAL: The provided ranges are impossible (such as maximum < minimum)
* 0: the function completed successfully
* EINVAL: the provided ranges are impossible (such as maximum < minimum)
*
* preferred_min: The special-case minimum value for a specifically-
* preferred_min: the special-case minimum value for a specifically-
* requested ID, which may be lower than the standard min_id
*/
static int get_ranges (bool sys_group, gid_t *min_id, gid_t *max_id,
@ -54,11 +56,11 @@ static int get_ranges (bool sys_group, gid_t *min_id, gid_t *max_id,
/* Check that the ranges make sense */
if (*max_id < *min_id) {
(void) fprintf (log_get_logfd(),
_("%s: Invalid configuration: SYS_GID_MIN (%lu), "
"GID_MIN (%lu), SYS_GID_MAX (%lu)\n"),
log_get_progname(), (unsigned long) *min_id,
getdef_ulong ("GID_MIN", 1000UL),
(unsigned long) *max_id);
_("%s: Invalid configuration: SYS_GID_MIN (%lu), "
"GID_MIN (%lu), SYS_GID_MAX (%lu)\n"),
log_get_progname(), (unsigned long) *min_id,
getdef_ulong ("GID_MIN", 1000UL),
(unsigned long) *max_id);
return EINVAL;
}
/*
@ -233,11 +235,11 @@ int find_new_gid (bool sys_group,
*/
/* Create an array to hold all of the discovered GIDs */
used_gids = CALLOC (gid_max + 1, bool);
used_gids = calloc_T(gid_max + 1, bool);
if (NULL == used_gids) {
fprintf (log_get_logfd(),
_("%s: failed to allocate memory: %s\n"),
log_get_progname(), strerror (errno));
log_get_progname(), strerrno());
return -1;
}
@ -245,7 +247,7 @@ int find_new_gid (bool sys_group,
(void) gr_rewind ();
highest_found = gid_min;
lowest_found = gid_max;
while ((grp = gr_next ()) != NULL) {
while (NULL != (grp = gr_next())) {
/*
* Does this entry have a lower GID than the lowest we've found
* so far?

View File

@ -4,7 +4,7 @@
* SPDX-License-Identifier: BSD-3-Clause
*/
#include <config.h>
#include "config.h"
#ifdef ENABLE_SUBIDS

View File

@ -4,7 +4,7 @@
* SPDX-License-Identifier: BSD-3-Clause
*/
#include <config.h>
#include "config.h"
#ifdef ENABLE_SUBIDS

View File

@ -6,7 +6,7 @@
* SPDX-License-Identifier: BSD-3-Clause
*/
#include <config.h>
#include "config.h"
#include <assert.h>
#include <stdint.h>
@ -18,16 +18,17 @@
#include "pwio.h"
#include "getdef.h"
#include "shadowlog.h"
#include "string/strerrno.h"
/*
* get_ranges - Get the minimum and maximum ID ranges for the search
*
* This function will return the minimum and maximum ranges for IDs
*
* 0: The function completed successfully
* EINVAL: The provided ranges are impossible (such as maximum < minimum)
* 0: the function completed successfully
* EINVAL: the provided ranges are impossible (such as maximum < minimum)
*
* preferred_min: The special-case minimum value for a specifically-
* preferred_min: the special-case minimum value for a specifically-
* requested ID, which may be lower than the standard min_id
*/
static int get_ranges (bool sys_user, uid_t *min_id, uid_t *max_id,
@ -54,11 +55,11 @@ static int get_ranges (bool sys_user, uid_t *min_id, uid_t *max_id,
/* Check that the ranges make sense */
if (*max_id < *min_id) {
(void) fprintf (log_get_logfd(),
_("%s: Invalid configuration: SYS_UID_MIN (%lu), "
"UID_MIN (%lu), SYS_UID_MAX (%lu)\n"),
log_get_progname(), (unsigned long) *min_id,
getdef_ulong ("UID_MIN", 1000UL),
(unsigned long) *max_id);
_("%s: Invalid configuration: SYS_UID_MIN (%lu), "
"UID_MIN (%lu), SYS_UID_MAX (%lu)\n"),
log_get_progname(), (unsigned long) *min_id,
getdef_ulong ("UID_MIN", 1000UL),
(unsigned long) *max_id);
return EINVAL;
}
/*
@ -233,11 +234,11 @@ int find_new_uid(bool sys_user,
*/
/* Create an array to hold all of the discovered UIDs */
used_uids = CALLOC(uid_max + 1, bool);
used_uids = calloc_T(uid_max + 1, bool);
if (NULL == used_uids) {
fprintf (log_get_logfd(),
_("%s: failed to allocate memory: %s\n"),
log_get_progname(), strerror (errno));
log_get_progname(), strerrno());
return -1;
}
@ -245,7 +246,7 @@ int find_new_uid(bool sys_user,
(void) pw_rewind ();
highest_found = uid_min;
lowest_found = uid_max;
while ((pwd = pw_next ()) != NULL) {
while (NULL != (pwd = pw_next())) {
/*
* Does this entry have a lower UID than the lowest we've found
* so far?

View File

@ -1,67 +0,0 @@
/*
* SPDX-FileCopyrightText: 1990 - 1994, Julianne Frances Haugh
* SPDX-FileCopyrightText: 1996 - 1999, Marek Michałkiewicz
* SPDX-FileCopyrightText: 2005 , Tomasz Kłoczko
* SPDX-FileCopyrightText: 2008 , Nicolas François
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#include <config.h>
#include <stdio.h>
#include <string.h>
#include "defines.h"
#include "prototypes.h"
#include "string/strcmp/streq.h"
/*@null@*/char *
fgetsx(/*@returned@*/char *restrict buf, int cnt, FILE *restrict f)
{
char *cp = buf;
char *ep;
while (cnt > 0) {
if (fgets (cp, cnt, f) != cp) {
if (cp == buf) {
return NULL;
} else {
break;
}
}
ep = strrchr (cp, '\\');
if ((NULL != ep) && (*(ep + 1) == '\n')) {
cnt -= ep - cp;
if (cnt > 0)
cp = stpcpy(ep, "");
} else {
break;
}
}
return buf;
}
int fputsx (const char *s, FILE * stream)
{
int i;
for (i = 0; !streq(s, ""); i++, s++) {
if (putc (*s, stream) == EOF) {
return EOF;
}
#if 0 /* The standard getgr*() can't handle that. --marekm */
if (i > (BUFSIZ / 2)) {
if (putc ('\\', stream) == EOF ||
putc ('\n', stream) == EOF)
return EOF;
i = 0;
}
#endif
}
return 0;
}

View File

@ -2,7 +2,7 @@
// SPDX-License-Identifier: BSD-3-Clause
#include <config.h>
#include "config.h"
#include "fs/mkstemp/fmkomstemp.h"

View File

@ -6,7 +6,7 @@
#define SHADOW_INCLUDE_LIB_FS_MKSTEMP_FMKOMSTEMP_H_
#include <config.h>
#include "config.h"
#include <stdio.h>
#include <sys/types.h>

View File

@ -2,7 +2,7 @@
// SPDX-License-Identifier: BSD-3-Clause
#include <config.h>
#include "config.h"
#include "fs/mkstemp/mkomstemp.h"

View File

@ -6,7 +6,7 @@
#define SHADOW_INCLUDE_LIB_FS_MKSTEMP_MKOMSTEMP_H_
#include <config.h>
#include "config.h"
#include <stdlib.h>
#include <sys/stat.h>

View File

@ -2,7 +2,7 @@
// SPDX-License-Identifier: BSD-3-Clause
#include <config.h>
#include "config.h"
#include "fs/readlink/areadlink.h"

View File

@ -6,7 +6,7 @@
#define SHADOW_INCLUDE_LIB_FS_READLINK_AREADLINK_H_
#include <config.h>
#include "config.h"
#include <errno.h>
#include <stdbool.h>
@ -33,7 +33,7 @@ areadlink(const char *link)
int len;
char *buf;
buf = MALLOC(size, char);
buf = malloc_T(size, char);
if (NULL == buf)
return NULL;

View File

@ -2,7 +2,7 @@
// SPDX-License-Identifier: BSD-3-Clause
#include <config.h>
#include "config.h"
#include "fs/readlink/readlinknul.h"

View File

@ -6,7 +6,7 @@
#define SHADOW_INCLUDE_LIB_FS_READLINK_READLINKNUL_H_
#include <config.h>
#include "config.h"
#include <errno.h>
#include <string.h>
@ -17,7 +17,8 @@
#include "sizeof.h"
#define READLINKNUL(link, buf) readlinknul(link, buf, countof(buf))
// readlinknul_a - read link nul-terminate array
#define readlinknul_a(link, buf) readlinknul(link, buf, countof(buf))
ATTR_STRING(1)
@ -25,7 +26,7 @@ inline ssize_t readlinknul(const char *restrict link, char *restrict buf,
ssize_t size);
// Similar to readlink(2), but terminate the string.
// readlinknul - read link nul-terminate
inline ssize_t
readlinknul(const char *restrict link, char *restrict buf, ssize_t size)
{

View File

@ -3,7 +3,7 @@
// SPDX-License-Identifier: BSD-3-Clause
#include <config.h>
#include "config.h"
#include <limits.h>
#include <fcntl.h>
@ -15,6 +15,7 @@
#include "defines.h"
#include "prototypes.h"
#include "string/sprintf/snprintf.h"
#include "string/strerrno.h"
/*
@ -58,16 +59,16 @@ int open_pidfd(const char *pidstr)
if (get_pid(pidstr, &target) == -1)
return -ENOENT;
if (SNPRINTF(proc_dir_name, "/proc/%d/", target) == -1) {
if (stprintf_a(proc_dir_name, "/proc/%d/", target) == -1) {
fprintf(stderr, "snprintf of proc path failed for %d: %s\n",
target, strerror(errno));
target, strerrno());
return -EINVAL;
}
proc_dir_fd = open(proc_dir_name, O_DIRECTORY);
if (proc_dir_fd < 0) {
fprintf(stderr, _("Could not open proc directory for target %d: %s\n"),
target, strerror(errno));
target, strerrno());
return -EINVAL;
}
return proc_dir_fd;

Some files were not shown because too many files have changed in this diff Show More