From a7f68f91584ffdd51a38b20c562e319e060ac53c Mon Sep 17 00:00:00 2001 From: Iker Pedrosa Date: Tue, 25 Mar 2025 16:54:24 +0100 Subject: [PATCH 01/30] Add pam dependency for _pam_confdir missing macro Resolves: #2354806 Signed-off-by: Iker Pedrosa --- shadow-utils.spec | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/shadow-utils.spec b/shadow-utils.spec index eb9aebe..b835dd5 100644 --- a/shadow-utils.spec +++ b/shadow-utils.spec @@ -1,7 +1,7 @@ Summary: Utilities for managing accounts and shadow password files Name: shadow-utils Version: 4.17.4 -Release: 1%{?dist} +Release: 2%{?dist} Epoch: 2 License: BSD-3-Clause AND GPL-2.0-or-later URL: https://github.com/shadow-maint/shadow @@ -18,10 +18,6 @@ Source7: passwd.pamd %global includesubiddir %{_includedir}/shadow # Fail linking if there are undefined symbols. %global _ld_strict_symbol_defs 1 -# workaround: _pam_confdir definition disappeared -%if ! %{defined _pam_confdir} -%define _pam_confdir /etc/pam.d -%endif ### Patches ### # Misc manual page changes - non-upstreamable @@ -56,6 +52,7 @@ BuildRequires: libtool BuildRequires: libxcrypt-devel BuildRequires: libxslt BuildRequires: make +BuildRequires: pam BuildRequires: pam-devel ### Provides ### @@ -279,6 +276,9 @@ rm -f $RPM_BUILD_ROOT/%{_libdir}/libsubid.a %{_libdir}/libsubid.so %changelog +* Tue Mar 25 2025 Iker Pedrosa - 2:4.17.4-2 +- Add pam dependency for _pam_confdir missing macro. Resolves: #2354806 + * Thu Mar 20 2025 Iker Pedrosa - 2:4.17.4-1 - Rebase to version 4.17.4. Resolves: #2353491 - Fixes problems with expiration dates From 92eaac9b2c03814446ff08a2835f8168b3dc5a57 Mon Sep 17 00:00:00 2001 From: Iker Pedrosa Date: Tue, 22 Apr 2025 11:31:29 +0200 Subject: [PATCH 02/30] FSWC: Migrate to lastlog2 Link: Resolves: #2361588 Signed-off-by: Iker Pedrosa --- shadow-utils.spec | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/shadow-utils.spec b/shadow-utils.spec index b835dd5..def827b 100644 --- a/shadow-utils.spec +++ b/shadow-utils.spec @@ -1,7 +1,7 @@ Summary: Utilities for managing accounts and shadow password files Name: shadow-utils Version: 4.17.4 -Release: 2%{?dist} +Release: 3%{?dist} Epoch: 2 License: BSD-3-Clause AND GPL-2.0-or-later URL: https://github.com/shadow-maint/shadow @@ -80,10 +80,9 @@ programs for managing user and group accounts. The pwconv command converts passwords to the shadow password format. The pwunconv command unconverts shadow passwords and generates a passwd file (a standard UNIX password file). The pwck command checks the integrity of password -and shadow files. The lastlog command prints out the last login times -for all users. The useradd, userdel, and usermod commands are used for -managing user accounts. The groupadd, groupdel, and groupmod commands -are used for managing group accounts. +and shadow files. The useradd, userdel, and usermod commands are used +for managing user accounts. The groupadd, groupdel, and groupmod +commands are used for managing group accounts. ### Subpackages ### @@ -117,7 +116,6 @@ rm lib/getdate.c autoreconf %configure \ --disable-account-tools-setuid \ - --enable-lastlog \ --enable-logind=no \ --enable-man \ --enable-shadowgrp \ @@ -224,7 +222,6 @@ rm -f $RPM_BUILD_ROOT/%{_libdir}/libsubid.a %{_bindir}/sg %attr(4755,root,root) %{_bindir}/chage %attr(4755,root,root) %{_bindir}/gpasswd -%{_bindir}/lastlog %attr(4755,root,root) %{_bindir}/newgrp %attr(0755,root,root) %caps(cap_setgid=ep) %{_bindir}/newgidmap %attr(0755,root,root) %caps(cap_setuid=ep) %{_bindir}/newuidmap @@ -262,7 +259,6 @@ rm -f $RPM_BUILD_ROOT/%{_libdir}/libsubid.a %{_mandir}/man8/chgpasswd.8* %{_mandir}/man8/newusers.8* %{_mandir}/man8/*conv.8* -%{_mandir}/man8/lastlog.8* %{_mandir}/man8/vipw.8* %{_mandir}/man8/vigr.8* @@ -276,6 +272,11 @@ rm -f $RPM_BUILD_ROOT/%{_libdir}/libsubid.a %{_libdir}/libsubid.so %changelog +* Tue Apr 22 2025 Iker Pedrosa - 2:4.17.4-3 +* FSWC: Migrate to lastlog2 + Link: + Resolves: #2361588 + * Tue Mar 25 2025 Iker Pedrosa - 2:4.17.4-2 - Add pam dependency for _pam_confdir missing macro. Resolves: #2354806 From 39c75f8e5825d87385d2a1d76b03f7e2da47b4b6 Mon Sep 17 00:00:00 2001 From: Iker Pedrosa Date: Tue, 15 Jul 2025 12:28:52 +0200 Subject: [PATCH 03/30] Rebuild for new side-tag for lastlog2 migration Signed-off-by: Iker Pedrosa --- shadow-utils.spec | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/shadow-utils.spec b/shadow-utils.spec index def827b..a3cc639 100644 --- a/shadow-utils.spec +++ b/shadow-utils.spec @@ -1,7 +1,7 @@ Summary: Utilities for managing accounts and shadow password files Name: shadow-utils Version: 4.17.4 -Release: 3%{?dist} +Release: 4%{?dist} Epoch: 2 License: BSD-3-Clause AND GPL-2.0-or-later URL: https://github.com/shadow-maint/shadow @@ -272,7 +272,7 @@ rm -f $RPM_BUILD_ROOT/%{_libdir}/libsubid.a %{_libdir}/libsubid.so %changelog -* Tue Apr 22 2025 Iker Pedrosa - 2:4.17.4-3 +* Tue Jul 15 2025 Iker Pedrosa - 2:4.17.4-4 * FSWC: Migrate to lastlog2 Link: Resolves: #2361588 From e1cfa31731cd68aa2d76ebfb3142037d16d3bb1e Mon Sep 17 00:00:00 2001 From: Iker Pedrosa Date: Fri, 18 Jul 2025 17:33:47 +0200 Subject: [PATCH 04/30] Stop assigning subids by default Resolves: CVE-2024-56433 and #2334168 Signed-off-by: Iker Pedrosa --- shadow-utils.login.defs | 4 ++-- shadow-utils.spec | 8 ++++++-- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/shadow-utils.login.defs b/shadow-utils.login.defs index e84c7ab..1e9129a 100644 --- a/shadow-utils.login.defs +++ b/shadow-utils.login.defs @@ -148,7 +148,7 @@ SYS_UID_MAX 999 # Extra per user uids SUB_UID_MIN 524288 SUB_UID_MAX 600100000 -SUB_UID_COUNT 65536 +SUB_UID_COUNT 0 # # Min/max values for automatic gid selection in groupadd(8) @@ -161,7 +161,7 @@ SYS_GID_MAX 999 # Extra per user group ids SUB_GID_MIN 524288 SUB_GID_MAX 600100000 -SUB_GID_COUNT 65536 +SUB_GID_COUNT 0 # # Max number of login(1) retries if password is bad diff --git a/shadow-utils.spec b/shadow-utils.spec index a3cc639..834ff15 100644 --- a/shadow-utils.spec +++ b/shadow-utils.spec @@ -1,7 +1,7 @@ Summary: Utilities for managing accounts and shadow password files Name: shadow-utils Version: 4.17.4 -Release: 4%{?dist} +Release: 5%{?dist} Epoch: 2 License: BSD-3-Clause AND GPL-2.0-or-later URL: https://github.com/shadow-maint/shadow @@ -272,8 +272,12 @@ rm -f $RPM_BUILD_ROOT/%{_libdir}/libsubid.a %{_libdir}/libsubid.so %changelog +* Fri Jul 18 2025 Iker Pedrosa - 2:4.17.4-5 +- Stop assigning subids by default + Resolves: CVE-2024-56433 and #2334168 + * Tue Jul 15 2025 Iker Pedrosa - 2:4.17.4-4 -* FSWC: Migrate to lastlog2 +- FSWC: Migrate to lastlog2 Link: Resolves: #2361588 From 4929903292e027ca15738a18554160ad9a796cc6 Mon Sep 17 00:00:00 2001 From: Iker Pedrosa Date: Fri, 18 Jul 2025 17:33:47 +0200 Subject: [PATCH 05/30] Stop assigning subids by default Resolves: CVE-2024-56433 and #2334169 Signed-off-by: Iker Pedrosa --- shadow-utils.login.defs | 4 ++-- shadow-utils.spec | 6 +++++- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/shadow-utils.login.defs b/shadow-utils.login.defs index e84c7ab..1e9129a 100644 --- a/shadow-utils.login.defs +++ b/shadow-utils.login.defs @@ -148,7 +148,7 @@ SYS_UID_MAX 999 # Extra per user uids SUB_UID_MIN 524288 SUB_UID_MAX 600100000 -SUB_UID_COUNT 65536 +SUB_UID_COUNT 0 # # Min/max values for automatic gid selection in groupadd(8) @@ -161,7 +161,7 @@ SYS_GID_MAX 999 # Extra per user group ids SUB_GID_MIN 524288 SUB_GID_MAX 600100000 -SUB_GID_COUNT 65536 +SUB_GID_COUNT 0 # # Max number of login(1) retries if password is bad diff --git a/shadow-utils.spec b/shadow-utils.spec index eb9aebe..b853fee 100644 --- a/shadow-utils.spec +++ b/shadow-utils.spec @@ -1,7 +1,7 @@ Summary: Utilities for managing accounts and shadow password files Name: shadow-utils Version: 4.17.4 -Release: 1%{?dist} +Release: 2%{?dist} Epoch: 2 License: BSD-3-Clause AND GPL-2.0-or-later URL: https://github.com/shadow-maint/shadow @@ -279,6 +279,10 @@ rm -f $RPM_BUILD_ROOT/%{_libdir}/libsubid.a %{_libdir}/libsubid.so %changelog +* Tue Jul 22 2025 Iker Pedrosa - 2:4.17.4-2 +- Stop assigning subids by default + Resolves: CVE-2024-56433 and #2334169 + * Thu Mar 20 2025 Iker Pedrosa - 2:4.17.4-1 - Rebase to version 4.17.4. Resolves: #2353491 - Fixes problems with expiration dates From 1f100b614dcbe4ea063620707f8aaaf0c83d7ef1 Mon Sep 17 00:00:00 2001 From: Iker Pedrosa Date: Tue, 22 Jul 2025 11:33:02 +0200 Subject: [PATCH 06/30] Rebase to version 4.18.0 Resolves: #2374710 Signed-off-by: Iker Pedrosa --- .gitignore | 2 + ...> shadow-4.18.0-account-tools-setuid.patch | 74 +++++++++---------- shadow-utils.spec | 18 ++--- sources | 4 +- 4 files changed, 50 insertions(+), 48 deletions(-) rename shadow-4.17.4-account-tools-setuid.patch => shadow-4.18.0-account-tools-setuid.patch (79%) diff --git a/.gitignore b/.gitignore index ff11943..f4e82f2 100644 --- a/.gitignore +++ b/.gitignore @@ -40,3 +40,5 @@ shadow-4.1.4.2.tar.bz2 /shadow-4.17.0.tar.xz.asc /shadow-4.17.4.tar.xz /shadow-4.17.4.tar.xz.asc +/shadow-4.18.0.tar.xz +/shadow-4.18.0.tar.xz.asc diff --git a/shadow-4.17.4-account-tools-setuid.patch b/shadow-4.18.0-account-tools-setuid.patch similarity index 79% rename from shadow-4.17.4-account-tools-setuid.patch rename to shadow-4.18.0-account-tools-setuid.patch index 4c1ff92..9c018f7 100644 --- a/shadow-4.17.4-account-tools-setuid.patch +++ b/shadow-4.18.0-account-tools-setuid.patch @@ -1,8 +1,8 @@ -diff -up shadow-4.15.0/src/chpasswd.c.account-tools-setuid shadow-4.15.0/src/chpasswd.c ---- shadow-4.15.0/src/chpasswd.c.account-tools-setuid 2024-03-08 22:27:04.000000000 +0100 -+++ shadow-4.15.0/src/chpasswd.c 2024-03-11 11:21:57.561150382 +0100 -@@ -443,9 +443,11 @@ int main (int argc, char **argv) - char *cp; +diff -up shadow-4.18.0-rc1/src/chpasswd.c.account-tools-setuid shadow-4.18.0-rc1/src/chpasswd.c +--- shadow-4.18.0-rc1/src/chpasswd.c.account-tools-setuid 2025-06-08 15:58:36.650175673 +0200 ++++ shadow-4.18.0-rc1/src/chpasswd.c 2025-06-09 10:09:17.902939220 +0200 +@@ -448,9 +448,11 @@ int main (int argc, char **argv) + char *newpwd; const char *salt; +#ifdef ACCT_TOOLS_SETUID @@ -13,7 +13,7 @@ diff -up shadow-4.15.0/src/chpasswd.c.account-tools-setuid shadow-4.15.0/src/chp bool errors = false; intmax_t line = 0; -@@ -469,19 +471,23 @@ int main (int argc, char **argv) +@@ -474,19 +476,23 @@ int main (int argc, char **argv) process_root_flag ("-R", argc, argv); prefix = process_prefix_flag ("-P", argc, argv); @@ -77,9 +77,9 @@ diff -up shadow-4.15.0/src/chpasswd.c.account-tools-setuid shadow-4.15.0/src/chp { /* Save the changes */ close_files (); -diff -up shadow-4.15.0/src/groupmems.c.account-tools-setuid shadow-4.15.0/src/groupmems.c ---- shadow-4.15.0/src/groupmems.c.account-tools-setuid 2024-03-08 22:27:04.000000000 +0100 -+++ shadow-4.15.0/src/groupmems.c 2024-03-11 11:16:18.365408572 +0100 +diff -up shadow-4.18.0-rc1/src/groupmems.c.account-tools-setuid shadow-4.18.0-rc1/src/groupmems.c +--- shadow-4.18.0-rc1/src/groupmems.c.account-tools-setuid 2025-06-08 15:58:36.651250652 +0200 ++++ shadow-4.18.0-rc1/src/groupmems.c 2025-06-09 10:08:59.836763261 +0200 @@ -14,9 +14,11 @@ #include #include @@ -92,7 +92,7 @@ diff -up shadow-4.15.0/src/groupmems.c.account-tools-setuid shadow-4.15.0/src/gr #include #include "alloc/x/xmalloc.h" -@@ -430,6 +432,7 @@ static void process_flags (int argc, cha +@@ -417,6 +419,7 @@ static void process_flags (int argc, cha static void check_perms (void) { if (!list) { @@ -100,7 +100,7 @@ diff -up shadow-4.15.0/src/groupmems.c.account-tools-setuid shadow-4.15.0/src/gr #ifdef USE_PAM pam_handle_t *pamh = NULL; int retval; -@@ -463,7 +466,8 @@ static void check_perms (void) +@@ -450,7 +453,8 @@ static void check_perms (void) fail_exit (1); } (void) pam_end (pamh, retval); @@ -110,10 +110,10 @@ diff -up shadow-4.15.0/src/groupmems.c.account-tools-setuid shadow-4.15.0/src/gr } } -diff -up shadow-4.15.0/src/newusers.c.account-tools-setuid shadow-4.15.0/src/newusers.c ---- shadow-4.15.0/src/newusers.c.account-tools-setuid 2024-03-08 22:27:04.000000000 +0100 -+++ shadow-4.15.0/src/newusers.c 2024-03-11 11:20:07.198909046 +0100 -@@ -59,6 +59,7 @@ +diff -up shadow-4.18.0-rc1/src/newusers.c.account-tools-setuid shadow-4.18.0-rc1/src/newusers.c +--- shadow-4.18.0-rc1/src/newusers.c.account-tools-setuid 2025-06-08 15:58:36.652250643 +0200 ++++ shadow-4.18.0-rc1/src/newusers.c 2025-06-09 10:08:59.836928828 +0200 +@@ -67,6 +67,7 @@ static const char Prog[] = "newusers"; static bool rflg = false; /* create a system account */ @@ -121,7 +121,7 @@ diff -up shadow-4.15.0/src/newusers.c.account-tools-setuid shadow-4.15.0/src/new #ifndef USE_PAM static /*@null@*//*@observer@*/char *crypt_method = NULL; #define cflg (NULL != crypt_method) -@@ -75,6 +76,7 @@ static long bcrypt_rounds = 13; +@@ -83,6 +84,7 @@ static long bcrypt_rounds = 13; static long yescrypt_cost = 5; #endif /* USE_YESCRYPT */ #endif /* !USE_PAM */ @@ -129,7 +129,7 @@ diff -up shadow-4.15.0/src/newusers.c.account-tools-setuid shadow-4.15.0/src/new static bool is_shadow; #ifdef SHADOWGRP -@@ -97,9 +99,11 @@ NORETURN static void fail_exit (int); +@@ -105,9 +107,11 @@ NORETURN static void fail_exit (int); static int add_group (const char *, const char *, gid_t *, gid_t); static int get_user_id (const char *, uid_t *); static int add_user (const char *, uid_t, gid_t); @@ -141,7 +141,7 @@ diff -up shadow-4.15.0/src/newusers.c.account-tools-setuid shadow-4.15.0/src/new static int add_passwd (struct passwd *, const char *); static void process_flags (int argc, char **argv); static void check_flags (void); -@@ -121,6 +125,7 @@ static void usage (int status) +@@ -129,6 +133,7 @@ static void usage (int status) "Options:\n"), Prog); (void) fputs (_(" -b, --badname allow bad names\n"), usageout); @@ -149,7 +149,7 @@ diff -up shadow-4.15.0/src/newusers.c.account-tools-setuid shadow-4.15.0/src/new #ifndef USE_PAM (void) fprintf (usageout, _(" -c, --crypt-method METHOD the crypt method (one of %s)\n"), -@@ -136,9 +141,11 @@ static void usage (int status) +@@ -144,9 +149,11 @@ static void usage (int status) #endif ); #endif /* !USE_PAM */ @@ -161,7 +161,7 @@ diff -up shadow-4.15.0/src/newusers.c.account-tools-setuid shadow-4.15.0/src/new #ifndef USE_PAM #if defined(USE_SHA_CRYPT) || defined(USE_BCRYPT) || defined(USE_YESCRYPT) (void) fputs (_(" -s, --sha-rounds number of rounds for the SHA, BCRYPT\n" -@@ -146,6 +153,7 @@ static void usage (int status) +@@ -154,6 +161,7 @@ static void usage (int status) usageout); #endif /* USE_SHA_CRYPT || USE_BCRYPT || USE_YESCRYPT */ #endif /* !USE_PAM */ @@ -169,7 +169,7 @@ diff -up shadow-4.15.0/src/newusers.c.account-tools-setuid shadow-4.15.0/src/new (void) fputs ("\n", usageout); exit (status); -@@ -405,6 +413,7 @@ static int add_user (const char *name, u +@@ -419,6 +427,7 @@ static int add_user (const char *name, u return (pw_update (&pwent) == 0) ? -1 : 0; } @@ -177,7 +177,7 @@ diff -up shadow-4.15.0/src/newusers.c.account-tools-setuid shadow-4.15.0/src/new #ifndef USE_PAM /* * update_passwd - update the password in the passwd entry -@@ -457,6 +466,7 @@ static int update_passwd (struct passwd +@@ -471,6 +480,7 @@ static int update_passwd (struct passwd return 0; } #endif /* !USE_PAM */ @@ -185,7 +185,7 @@ diff -up shadow-4.15.0/src/newusers.c.account-tools-setuid shadow-4.15.0/src/new /* * add_passwd - add or update the encrypted password -@@ -465,10 +475,13 @@ static int add_passwd (struct passwd *pw +@@ -479,10 +489,13 @@ static int add_passwd (struct passwd *pw { const struct spwd *sp; struct spwd spent; @@ -199,7 +199,7 @@ diff -up shadow-4.15.0/src/newusers.c.account-tools-setuid shadow-4.15.0/src/new #ifndef USE_PAM void *crypt_arg = NULL; if (NULL != crypt_method) { -@@ -505,13 +518,14 @@ static int add_passwd (struct passwd *pw +@@ -520,13 +533,14 @@ static int add_passwd (struct passwd *pw return update_passwd (pwd, password); } #endif /* USE_PAM */ @@ -215,7 +215,7 @@ diff -up shadow-4.15.0/src/newusers.c.account-tools-setuid shadow-4.15.0/src/new if (NULL != sp) { spent = *sp; if ( (NULL != crypt_method) -@@ -547,7 +561,7 @@ static int add_passwd (struct passwd *pw +@@ -563,7 +577,7 @@ static int add_passwd (struct passwd *pw if (!streq(pwd->pw_passwd, "x")) { return update_passwd (pwd, password); } @@ -224,7 +224,7 @@ diff -up shadow-4.15.0/src/newusers.c.account-tools-setuid shadow-4.15.0/src/new /* * If there is already a shadow entry, do not touch it. * If there is already a passwd entry with a password, do not -@@ -558,14 +572,14 @@ static int add_passwd (struct passwd *pw +@@ -574,14 +588,14 @@ static int add_passwd (struct passwd *pw || !streq(pwd->pw_passwd, "x")) { return 0; } @@ -241,7 +241,7 @@ diff -up shadow-4.15.0/src/newusers.c.account-tools-setuid shadow-4.15.0/src/new if ((crypt_method != NULL) && streq(crypt_method, "NONE")) { spent.sp_pwdp = (char *)password; } else { -@@ -610,35 +624,41 @@ static int add_passwd (struct passwd *pw +@@ -626,35 +640,41 @@ static int add_passwd (struct passwd *pw static void process_flags (int argc, char **argv) { int c; @@ -285,7 +285,7 @@ diff -up shadow-4.15.0/src/newusers.c.account-tools-setuid shadow-4.15.0/src/new "bhr", #endif long_options, NULL)) != -1) { -@@ -646,11 +666,13 @@ static void process_flags (int argc, cha +@@ -662,11 +682,13 @@ static void process_flags (int argc, cha case 'b': allow_bad_names = true; break; @@ -299,7 +299,7 @@ diff -up shadow-4.15.0/src/newusers.c.account-tools-setuid shadow-4.15.0/src/new case 'h': usage (EXIT_SUCCESS); break; -@@ -659,6 +681,7 @@ static void process_flags (int argc, cha +@@ -675,6 +697,7 @@ static void process_flags (int argc, cha break; case 'R': /* no-op, handled in process_root_flag () */ break; @@ -307,7 +307,7 @@ diff -up shadow-4.15.0/src/newusers.c.account-tools-setuid shadow-4.15.0/src/new #ifndef USE_PAM #if defined(USE_SHA_CRYPT) || defined(USE_BCRYPT) || defined(USE_YESCRYPT) case 's': -@@ -698,6 +721,7 @@ static void process_flags (int argc, cha +@@ -714,6 +737,7 @@ static void process_flags (int argc, cha break; #endif /* USE_SHA_CRYPT || USE_BCRYPT || USE_YESCRYPT */ #endif /* !USE_PAM */ @@ -315,7 +315,7 @@ diff -up shadow-4.15.0/src/newusers.c.account-tools-setuid shadow-4.15.0/src/new default: usage (EXIT_FAILURE); break; -@@ -730,6 +754,7 @@ static void process_flags (int argc, cha +@@ -746,6 +770,7 @@ static void process_flags (int argc, cha */ static void check_flags (void) { @@ -323,7 +323,7 @@ diff -up shadow-4.15.0/src/newusers.c.account-tools-setuid shadow-4.15.0/src/new #ifndef USE_PAM #if defined(USE_SHA_CRYPT) || defined(USE_BCRYPT) || defined(USE_YESCRYPT) if (sflg && !cflg) { -@@ -762,6 +787,7 @@ static void check_flags (void) +@@ -778,6 +803,7 @@ static void check_flags (void) } } #endif /* !USE_PAM */ @@ -331,7 +331,7 @@ diff -up shadow-4.15.0/src/newusers.c.account-tools-setuid shadow-4.15.0/src/new } /* -@@ -1052,12 +1078,14 @@ int main (int argc, char **argv) +@@ -1066,12 +1092,14 @@ int main (int argc, char **argv) intmax_t line = 0; uid_t uid; gid_t gid; @@ -346,7 +346,7 @@ diff -up shadow-4.15.0/src/newusers.c.account-tools-setuid shadow-4.15.0/src/new log_set_progname(Prog); log_set_logfd(stderr); -@@ -1195,6 +1223,7 @@ int main (int argc, char **argv) +@@ -1191,6 +1219,7 @@ int main (int argc, char **argv) } newpw = *pw; @@ -354,7 +354,7 @@ diff -up shadow-4.15.0/src/newusers.c.account-tools-setuid shadow-4.15.0/src/new #ifdef USE_PAM /* keep the list of user/password for later update by PAM */ nusers++; -@@ -1211,6 +1240,7 @@ int main (int argc, char **argv) +@@ -1207,6 +1236,7 @@ int main (int argc, char **argv) usernames[nusers-1] = xstrdup(fields[0]); passwords[nusers-1] = xstrdup(fields[1]); #endif /* USE_PAM */ @@ -362,7 +362,7 @@ diff -up shadow-4.15.0/src/newusers.c.account-tools-setuid shadow-4.15.0/src/new if (add_passwd (&newpw, fields[1]) != 0) { fprintf (stderr, _("%s: line %jd: can't update password\n"), -@@ -1327,6 +1357,7 @@ int main (int argc, char **argv) +@@ -1323,6 +1353,7 @@ int main (int argc, char **argv) nscd_flush_cache ("group"); sssd_flush_cache (SSSD_DB_PASSWD | SSSD_DB_GROUP); @@ -370,7 +370,7 @@ diff -up shadow-4.15.0/src/newusers.c.account-tools-setuid shadow-4.15.0/src/new #ifdef USE_PAM /* Now update the passwords using PAM */ for (size_t i = 0; i < nusers; i++) { -@@ -1339,6 +1370,7 @@ int main (int argc, char **argv) +@@ -1334,6 +1365,7 @@ int main (int argc, char **argv) } } #endif /* USE_PAM */ diff --git a/shadow-utils.spec b/shadow-utils.spec index 834ff15..5465e15 100644 --- a/shadow-utils.spec +++ b/shadow-utils.spec @@ -1,12 +1,12 @@ Summary: Utilities for managing accounts and shadow password files Name: shadow-utils -Version: 4.17.4 -Release: 5%{?dist} +Version: 4.18.0 +Release: 1%{?dist} Epoch: 2 License: BSD-3-Clause AND GPL-2.0-or-later URL: https://github.com/shadow-maint/shadow -Source0: https://github.com/shadow-maint/shadow/releases/download/4.17.4/shadow-4.17.4.tar.xz -Source1: https://github.com/shadow-maint/shadow/releases/download/4.17.4/shadow-4.17.4.tar.xz.asc +Source0: https://github.com/shadow-maint/shadow/releases/download/4.18.0/shadow-4.18.0.tar.xz +Source1: https://github.com/shadow-maint/shadow/releases/download/4.18.0/shadow-4.18.0.tar.xz.asc Source2: shadow-utils.useradd Source3: shadow-utils.login.defs Source4: shadow-bsd.txt @@ -23,7 +23,7 @@ Source7: passwd.pamd # Misc manual page changes - non-upstreamable Patch0: shadow-4.15.0-manfix.patch # Probably non-upstreamable -Patch1: shadow-4.17.4-account-tools-setuid.patch +Patch1: shadow-4.18.0-account-tools-setuid.patch ### Dependencies ### Requires: audit-libs >= 1.6.5 @@ -101,7 +101,7 @@ Requires: shadow-utils-subid = %{epoch}:%{version}-%{release} Development files for shadow-utils-subid. %prep -%autosetup -p 1 -S git -n shadow-4.17.4 +%autosetup -p 1 -S git -n shadow-4.18.0 iconv -f ISO88591 -t utf-8 doc/HOWTO > doc/HOWTO.utf8 cp -f doc/HOWTO.utf8 doc/HOWTO @@ -109,9 +109,6 @@ cp -f doc/HOWTO.utf8 doc/HOWTO cp -a %{SOURCE4} %{SOURCE5} . cp -a %{SOURCE6} man/login.defs.d/HOME_MODE.xml -# Force regeneration of getdate.c -rm lib/getdate.c - %build autoreconf %configure \ @@ -272,6 +269,9 @@ rm -f $RPM_BUILD_ROOT/%{_libdir}/libsubid.a %{_libdir}/libsubid.so %changelog +* Tue Jul 22 2025 Iker Pedrosa - 2:4.18.0-1 +- Rebase to version 4.18.0. Resolves: #2374710 + * Fri Jul 18 2025 Iker Pedrosa - 2:4.17.4-5 - Stop assigning subids by default Resolves: CVE-2024-56433 and #2334168 diff --git a/sources b/sources index 6e720c5..8e3da98 100644 --- a/sources +++ b/sources @@ -1,2 +1,2 @@ -SHA512 (shadow-4.17.4.tar.xz) = 06830f654650312a79ccd6d729a51808b324d594abf1c05d56a2d0880936df292ec5c9fd6c7f4ad59a6d0f2bf5be0af42afe6386c24c2c087fd64fff301bade3 -SHA512 (shadow-4.17.4.tar.xz.asc) = 24f14397a975e4b09be087705a96544ff8ad76e0aa8c708ed4a53db3a295ad0a33fd0797fc570dcbb2446d4e103a3e43922a93168f65012eba5d3fe31549ebdd +SHA512 (shadow-4.18.0.tar.xz) = e724670362949ccb570bb300322eb280c28ad609d631072b8db15a5e6eeba9fb9ac5d3df1df270f8b894235e4995b56c0e8f9aadb34d66bb5cea9b7a4f55e6ce +SHA512 (shadow-4.18.0.tar.xz.asc) = bb82b3d54eec0f11ace57609281974b315bba1240eafdae3b6513c7d7dfbcf10c3ae855c74c9eae33539709072046787e4b3e86dfb8bbfbf4bf23446daeb3422 From 654ff210dae918a9880cc1962b75b8a4c7ebab1f Mon Sep 17 00:00:00 2001 From: Fedora Release Engineering Date: Fri, 25 Jul 2025 18:12:54 +0000 Subject: [PATCH 07/30] Rebuilt for https://fedoraproject.org/wiki/Fedora_43_Mass_Rebuild --- shadow-utils.spec | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/shadow-utils.spec b/shadow-utils.spec index 5465e15..f2238ca 100644 --- a/shadow-utils.spec +++ b/shadow-utils.spec @@ -1,7 +1,7 @@ Summary: Utilities for managing accounts and shadow password files Name: shadow-utils Version: 4.18.0 -Release: 1%{?dist} +Release: 2%{?dist} Epoch: 2 License: BSD-3-Clause AND GPL-2.0-or-later URL: https://github.com/shadow-maint/shadow @@ -269,6 +269,9 @@ rm -f $RPM_BUILD_ROOT/%{_libdir}/libsubid.a %{_libdir}/libsubid.so %changelog +* Fri Jul 25 2025 Fedora Release Engineering - 2:4.18.0-2 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_43_Mass_Rebuild + * Tue Jul 22 2025 Iker Pedrosa - 2:4.18.0-1 - Rebase to version 4.18.0. Resolves: #2374710 From b75f74d3f1b086fd5e699e55f131ae9e1beca525 Mon Sep 17 00:00:00 2001 From: Alexey Tikhonov Date: Tue, 29 Jul 2025 20:20:15 +0200 Subject: [PATCH 08/30] Revert "Stop assigning subids by default" This reverts commit e1cfa31731cd68aa2d76ebfb3142037d16d3bb1e while CVE-2024-56433 is being disputed. Resolves: #2382662 --- shadow-utils.login.defs | 4 ++-- shadow-utils.spec | 6 +++++- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/shadow-utils.login.defs b/shadow-utils.login.defs index 1e9129a..e84c7ab 100644 --- a/shadow-utils.login.defs +++ b/shadow-utils.login.defs @@ -148,7 +148,7 @@ SYS_UID_MAX 999 # Extra per user uids SUB_UID_MIN 524288 SUB_UID_MAX 600100000 -SUB_UID_COUNT 0 +SUB_UID_COUNT 65536 # # Min/max values for automatic gid selection in groupadd(8) @@ -161,7 +161,7 @@ SYS_GID_MAX 999 # Extra per user group ids SUB_GID_MIN 524288 SUB_GID_MAX 600100000 -SUB_GID_COUNT 0 +SUB_GID_COUNT 65536 # # Max number of login(1) retries if password is bad diff --git a/shadow-utils.spec b/shadow-utils.spec index f2238ca..34baef4 100644 --- a/shadow-utils.spec +++ b/shadow-utils.spec @@ -1,7 +1,7 @@ Summary: Utilities for managing accounts and shadow password files Name: shadow-utils Version: 4.18.0 -Release: 2%{?dist} +Release: 3%{?dist} Epoch: 2 License: BSD-3-Clause AND GPL-2.0-or-later URL: https://github.com/shadow-maint/shadow @@ -269,6 +269,10 @@ rm -f $RPM_BUILD_ROOT/%{_libdir}/libsubid.a %{_libdir}/libsubid.so %changelog +* Tue Jul 29 2025 Alexey Tikhonov - 2:4.18.0-3 +- Revert "Stop assigning subids by default" + Resolves: #2382662 + * Fri Jul 25 2025 Fedora Release Engineering - 2:4.18.0-2 - Rebuilt for https://fedoraproject.org/wiki/Fedora_43_Mass_Rebuild From 9fbb086db0bf8f6b1436d76a5224e44ca5abf41b Mon Sep 17 00:00:00 2001 From: Iker Pedrosa Date: Mon, 18 Aug 2025 11:48:31 +0200 Subject: [PATCH 09/30] tests/: remove them These tests need to be migrated to TMT to continue running, but nobody is maintaining them so we've decided to remove them. Closes: 2383071 Signed-off-by: Iker Pedrosa --- tests/sanity/Makefile | 77 --- tests/sanity/PURPOSE | 10 - tests/sanity/runtest.sh | 24 - tests/sanity/sanity_test.py | 1013 ----------------------------------- tests/tests.yml | 13 - 5 files changed, 1137 deletions(-) delete mode 100644 tests/sanity/Makefile delete mode 100644 tests/sanity/PURPOSE delete mode 100755 tests/sanity/runtest.sh delete mode 100755 tests/sanity/sanity_test.py delete mode 100644 tests/tests.yml diff --git a/tests/sanity/Makefile b/tests/sanity/Makefile deleted file mode 100644 index 386221b..0000000 --- a/tests/sanity/Makefile +++ /dev/null @@ -1,77 +0,0 @@ -# Copyright (c) 2006 Red Hat, Inc. All rights reserved. This copyrighted material -# is made available to anyone wishing to use, modify, copy, or -# redistribute it subject to the terms and conditions of the GNU General -# Public License v.2. -# -# This program is distributed in the hope that it will be useful, but WITHOUT ANY -# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A -# PARTICULAR PURPOSE. See the GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -# -# Author: Jakub Hrozek - -#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~# -# Example Makefile for RHTS # -# This example is geared towards a test for a specific package # -# It does most of the work for you, but may require further coding # -#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~# - -# The toplevel namespace within which the test lives. -TOPLEVEL_NAMESPACE=CoreOS - -# The name of the package under test: -PACKAGE_NAME=shadow-utils - -# The path of the test below the package: -RELATIVE_PATH=sanity - -# Version of the Test. Used with make tag. -export TESTVERSION=1.1 - -# The combined namespace of the test. -export TEST=/$(TOPLEVEL_NAMESPACE)/$(PACKAGE_NAME)/$(RELATIVE_PATH) - -# A phony target is one that is not really the name of a file. -# It is just a name for some commands to be executed when you -# make an explicit request. There are two reasons to use a -# phony target: to avoid a conflict with a file of the same -# name, and to improve performance. -.PHONY: all install download clean - -# Executables to be built should be added here, they will be generated on the system under test. -BUILT_FILES= - -# Data files, .c files, scripts anything needed to either compile the test and/or run it. -FILES=$(METADATA) Makefile PURPOSE sanity_test.py runtest.sh - -run: $(FILES) build - ./runtest.sh - -build: $(BUILT_FILES) - chmod a+x ./sanity_test.py - chmod a+x ./runtest.sh - -clean: - rm -f *~ *.rpm $(BUILT_FILES) - -# Include Common Makefile -include /usr/share/rhts/lib/rhts-make.include - -# Generate the testinfo.desc here: -$(METADATA): Makefile - @touch $(METADATA) - @echo "Owner: Jakub Hrozek " > $(METADATA) - @echo "Name: $(TEST)" >> $(METADATA) - @echo "Path: $(TEST_DIR)" >> $(METADATA) - @echo "TestVersion: $(TESTVERSION)" >> $(METADATA) - @echo "License: GNU GPL" >> $(METADATA) - @echo "Description: Basic sanity test for shadow-utils" >> $(METADATA) - @echo "TestTime: 5m" >> $(METADATA) - @echo "RunFor: $(PACKAGE_NAME)" >> $(METADATA) - @echo "Requires: $(PACKAGE_NAME)" >> $(METADATA) - @echo "Requires: python" >> $(METADATA) - rhts-lint $(METADATA) - diff --git a/tests/sanity/PURPOSE b/tests/sanity/PURPOSE deleted file mode 100644 index 27062e1..0000000 --- a/tests/sanity/PURPOSE +++ /dev/null @@ -1,10 +0,0 @@ -This is a basic sanity test for the shadow-utils package. It is implemented -in python on top of the unittesting.py module. - -Its purpose is to ensure that the binaries in the shadow-utils package behave -as expected and its switches/options work correctly. - -For the most part, every binary in the shadow-utils package is represented by -a single class named Test, i.e. TestUsermod etc. There are some -exceptions, like TestUseraddWeirdNameTest though. - diff --git a/tests/sanity/runtest.sh b/tests/sanity/runtest.sh deleted file mode 100755 index cb2a2b5..0000000 --- a/tests/sanity/runtest.sh +++ /dev/null @@ -1,24 +0,0 @@ -#!/bin/bash -. /usr/bin/rhts-environment.sh -. /usr/share/beakerlib/beakerlib.sh || exit 1 - -rlJournalStart -rlFileBackup --clean /etc/default/useradd- /etc/default/useradd -setenforce 0 -python sanity_test.py -v -setenforce 1 -rlFileRestore - -EXIT=$? -if [[ $EXIT -eq 0 ]]; then - RESULT="PASS" -else - RESULT="FAIL" -fi - - -rlJournalEnd - -echo "Result: $RESULT" -echo "Exit: $EXIT" -report_result $TEST $RESULT $EXIT diff --git a/tests/sanity/sanity_test.py b/tests/sanity/sanity_test.py deleted file mode 100755 index e9c45c2..0000000 --- a/tests/sanity/sanity_test.py +++ /dev/null @@ -1,1013 +0,0 @@ -#!/usr/bin/env python -""" -A script that tests functionality of the shadow-utils package. - -Author: Jakub Hrozek, -License: GNU GPL v2 -Date: 2007 - -TODO: - * tests for password aging - * if something fails, print out the command issued for easier debugging - * test long options variants along with the short ones -""" - -import unittest -import pwd -import grp -import commands -import os -import os.path -import sys -import copy -import tempfile -import rpm -import shutil - -from UserDict import UserDict - -class RedHatVersion(object): - def __init__(self, type=None, version=None, release=None): - self.type = type - self.version = version - self.release = release - self.rhel = False - - def __eq__( self, other): - """ - Don't compare if either of the values is None - so we can do comparisons like 'is it fedora?' or 'is it rhel4?' - """ - ok = (self.type == other.type) - if ok == False: return False - - if self.version and other.version: - ok = (self.version == other.version) - if ok == False: return False - - if (self.release == other.release): - ok = (self.release == other.release) - - return ok - - def __ne__( self, other): - return not self.__eq__(other) - - def __get_fedora_info(self, mi): - return [ (h['version'],h['release']) for h in mi ][0] - - def __get_rhel_info(self, mi): - # The rules for RHEL versions are braindead..releases even more - ver_rpm, rel_rpm = [ (h['version'],h['release']) for h in mi ][0] - rhel_versions = { '3AS' : 3, '4AS' : 4, '5Server' : 5, '5Client' : 5, '6' : 6 } - if ver_rpm[:3] == '5.9' or ver_rpm[:1] == '6': # rhel6 prerelease and release hack - rhel_versions[ver_rpm] = 6 - if ver_rpm in rhel_versions.keys(): - return (rhel_versions[ver_rpm], rel_rpm) - - def is_rhel(self): - return self.rhel - - def get_info(self): - """ - Returns a tuple containing (type, version, release) of RHEL or Fedora. - Type is either RHEL or Fedora. - Returns None if it cannot parse the info - """ - - ts = rpm.TransactionSet() - mi = ts.dbMatch() - mi.pattern('name', rpm.RPMMIRE_GLOB, 'redhat-release*') - - if mi: - self.rhel = True - return ('RHEL',) + self.__get_rhel_info(mi) - else: - mi = ts.dbMatch('name','fedora-release') - self.rhel = False - if mi.count() != 0: - return ('Fedora',) + self.__get_fedora_info(mi) - - return None - - -class UserInfo(UserDict): - fields = { "pw_name" : 0, "pw_passwd" : 1, "pw_uid" : 2, "pw_gid" : 3, - "pw_gecos" : 4, "pw_dir" : 5, "pw_shell" : 6 } - - def __init__(self): - UserDict.__init__(self) - for f in UserInfo.fields: self[f] = None - - def __getitem__(self, key): - return UserDict.__getitem__(self, key) - - def __setitem__(self, key, value): - UserDict.__setitem__(self, key, value) - - def __cmp__(self, other): - return UserDict.__cmp__(self, other) - - def __repr__(self): - return " ; ".join( [ "%s => %s" % (k, v) for k, v in self.data.items() ] ) - - def __parse_info(self, struct): - for f in UserInfo.fields: - self[f] = struct[UserInfo.fields[f]] - - def get_info_uid(self, uid): - self.__parse_info(pwd.getpwuid(uid)) - - def get_info_name(self, name): - try: - self.__parse_info(pwd.getpwnam(name)) - except KeyError: - return None - - def lazy_compare(self, pattern): - """ Compare pattern against self. If any field in pattern is set - to None, it is automatically considered equal with the corresponding - field in self. """ - for field in UserInfo.fields: - if pattern[field] and pattern[field] != self[field]: - return False - - return True - -class GroupInfo(UserDict): - fields = { "gr_name" : 0, "gr_passwd" : 1, - "gr_gid" : 2, "gr_mem" : 3} - - def __init__(self): - UserDict.__init__(self) - for f in GroupInfo.fields: self[f] = None - - def __getitem__(self, key): - return UserDict.__getitem__(self, key) - - def __setitem__(self, key, value): - UserDict.__setitem__(self, key, value) - - def __cmp__(self, other): - return UserDict.__cmp__(self, other) - - def __repr__(self): - return " ; ".join( [ "%s => %s" % (k, v) for k, v in self.data.items() ] ) - - def __parse_info(self, struct): - for f in GroupInfo.fields: - self[f] = struct[GroupInfo.fields[f]] - - def get_info_gid(self, gid): - self.__parse_info(grp.getgrgid(gid)) - - def get_info_name(self, name): - self.__parse_info(grp.getgrnam(name)) - - def lazy_compare(self, pattern): - """ Compare pattern against self. If any field in pattern is set - to None, it is automatically considered equal with the corresponding - field in self. """ - for field in GroupInfo.fields: - if pattern[field] and pattern[field] != self[field]: - return False - - return True - -class LoginDefsParser(UserDict): - "A quick-n-dirty way how to fetch the defaults from /etc/login.defs into a dictionary" - - def __getitem__(self, key): - try: - return UserDict.__getitem__(self, key) - except KeyError: - # if a name-value is not defined in the config file, return defaults - if key == "CREATE_MAIL_SPOOL": - return "yes" - if key == "UMASK": - return "077" - - def __init__(self, path="/etc/login.defs",split=None): - self.path = path - UserDict.__init__(self) - try: - defs = open(path) - except IOError: - print "Could not open the config file %s" % (path) - - for line in defs: - if line.startswith('#'): continue - fields = line.split(split) - if len(fields) != 2: continue # yeah, we're dirty - self.data[fields[0]] = fields[1] - - def serialize(self): - output = open(self.path, "w+") - for k,v in self.data.items(): - output.write("%s=%s" % (k, v)) - - output.write("\n") - output.close() - -class TestUserInfo(unittest.TestCase): - def testLazyCompare(self): - """ (test sanity): Test comparing two UserInfo records """ - a = UserInfo() - a["pw_name"] = "foo" - a["pw_uid"] = 555 - b = copy.deepcopy(a) - c = UserInfo() - - self.assertEqual(a.lazy_compare(b), True) - self.assertEqual(a.lazy_compare(c), True) - - c["pw_name"] = "foo" - c["pw_uid"] = None - self.assertEqual(a.lazy_compare(c), True) - self.assertEqual(c.lazy_compare(a), False) - - c["pw_name"] = "bar" - self.assertNotEqual(a.lazy_compare(c), True) - - def testGetInfoUid(self): - """ (test sanity): Test getting user info based on his UID """ - a = UserInfo() - a.get_info_uid(0) - self.assertEqual(a["pw_name"], "root") - - def testGetInfoName(self): - """ (test sanity): Test getting user info based on his name """ - a = UserInfo() - a.get_info_name("root") - self.assertEqual(a["pw_uid"], 0) - -class ShadowUtilsTestBase: - """ Handy routines """ - def getDefaults(self): - # get the default values for so we can compare against that - (status, defaults_str) = commands.getstatusoutput('useradd -D') - if status != 0: - raise RuntimeError("Could not get the default values for useradd") - return dict([ rec.split("=") for rec in defaults_str.split("\n") ]) - - def getDefaultUserInfo(self, username): - expected = UserInfo() - defaults = self.getDefaults() - - expected["pw_name"] = username - expected["pw_dir"] = defaults["HOME"] + "/" + username - expected["pw_shell"] = defaults["SHELL"] - - return expected - -class TestUseradd(ShadowUtilsTestBase, unittest.TestCase): - def setUp(self): - self.username = "test-shadow-utils-useradd" - - def tearDown(self): - commands.getstatusoutput("userdel -r %s" % (self.username)) - - def testBasicAdd(self): - """ useradd: Tests basic adding of a user """ - expected = self.getDefaultUserInfo(self.username) - - runme = "useradd %s" % (self.username) - (status, output) = commands.getstatusoutput(runme) - self.failUnlessEqual(status, 0, output) - - created = UserInfo() - created.get_info_name(self.username) - self.assertEqual(created.lazy_compare(expected), True, "FAIL: Could not add a user\nIssued command: %s" % (runme)) - - def testExistingUser(self): - """ useradd: Test that user with an existing name cannot be added """ - (status, output) = commands.getstatusoutput("useradd %s" % (self.username)) - self.failUnlessEqual(status, 0, output) - self.assertNotEqual(commands.getstatusoutput("useradd %s" % (self.username))[0], 0, "FAIL: User that already exists added") - - def testCustomUID(self): - """ useradd: Adding an user with a specific UID """ - UID = 23456 # FIXME - test for a free UID slot first - - expected = self.getDefaultUserInfo(self.username) - expected["pw_uid"] = UID - - runme = "useradd %s -u %d" % (self.username, UID) - (status, output) = commands.getstatusoutput(runme) - self.failUnlessEqual(status, 0, "Issued command: %s\n" % (runme) + "Got from useradd: %s\n" % (output)) - - created = UserInfo() - created.get_info_name(self.username) - self.assertEqual(created.lazy_compare(expected), True, "FAIL: Could not add a user with a specific UID\nIssued command: %s" % (runme)) - - def testNegativeUID(self): - """ useradd: Tests that user cannot have a negative UID assigned """ - self.assertNotEqual(commands.getstatusoutput("useradd %s --uid -5" % (self.username))[0], 0, "FAIL: User with UID < 0 added") - - def testCustomExistingUID(self): - """ useradd: Adding a user with a specific existing UID """ - UID = 32112 - - expected = self.getDefaultUserInfo(self.username) - expected["pw_uid"] = UID - - (status_u, output_u) = commands.getstatusoutput("useradd %s -u %d" % (self.username, UID)) - - # must fail without -o flag - (status_u_no_o, output_u_no_o) = commands.getstatusoutput("useradd foo -u %d" % (UID)) - - # must pass with -o flag - (status_o, output_o) = commands.getstatusoutput("useradd foo -u %d -o" % (UID)) - - # clean up - (status, output) = commands.getstatusoutput("userdel -r foo") - - self.failUnlessEqual(status_u, 0, "FAIL: cannot add an user with a specified UID\n"+output_u) - self.assertEqual(status_o, 0, "FAIL: cannot add an user with an existing UID using the -o flag\n"+output_o) - self.failUnlessEqual(status, 0, output) - self.assertNotEqual(status_u_no_o, 0, "FAIL: user with an existing UID added\n"+output_u_no_o) - - def testCustomGID(self): - """ useradd: Adding an user with a specific GID """ - GID = 100 # users group should be everywhere - should we test before? - expected = self.getDefaultUserInfo(self.username) - expected["pw_gid"] = GID - - (status, output) = commands.getstatusoutput("useradd %s -g %d" % (self.username, GID)) - self.failUnlessEqual(status, 0, output) - - created = UserInfo() - created.get_info_name(self.username) - self.assertEqual(created.lazy_compare(expected), True, "FAIL: Could not add a user with a specific GID") - - def testCustomShell(self): - """ useradd: Adding an user with a specific login shell """ - shell = "/bin/ksh" - expected = self.getDefaultUserInfo(self.username) - expected["pw_shell"] = shell - - (status, output) = commands.getstatusoutput("useradd %s -s %s" % (self.username, shell)) - self.failUnlessEqual(status, 0, output) - - created = UserInfo() - created.get_info_name(self.username) - self.assertEqual(created.lazy_compare(expected), True, "FAIL: Could not add a user with a specific shell") - - def testCustomHome(self): - """ useradd: Adding an user with a specific home directory """ - home = "/tmp/useradd-test" - os.mkdir(home) - expected = self.getDefaultUserInfo(self.username) - expected["pw_dir"] = home - - (status, output) = commands.getstatusoutput("useradd %s -d %s" % (self.username, home)) - shutil.rmtree(home) - self.failUnlessEqual(status, 0, output) - - created = UserInfo() - created.get_info_name(self.username) - self.assertEqual(created.lazy_compare(expected), True, "FAIL: Could not add a user with a specific home") - - def testSystemAccount(self): - """ useradd: Adding a system user (UID < UID_MIN from /etc/login.defs) """ - defaults = LoginDefsParser() - - # system account with no home dir - expected = self.getDefaultUserInfo(self.username) - - (status, output) = commands.getstatusoutput("useradd -r %s" % (self.username)) - self.failUnlessEqual(status, 0, output) - - created = UserInfo() - created.get_info_name(self.username) - self.assertEqual(os.path.exists(created["pw_dir"]), False, "FAIL: System user has a home dir created") - self.assertEqual(created["pw_uid"] < defaults['UID_MIN'], True, "FAIL: System user has UID > UID_MIN") - self.assertEqual(created.lazy_compare(expected), True, "FAIL: Could not add a system user") - - def testAddToMoreGroups(self): - """ useradd: Creating an user that belongs to more than one group """ - (status, output) = commands.getstatusoutput("useradd -G bin %s" % (self.username)) - self.failUnlessEqual(status, 0, output) - - gr_bin = GroupInfo() - gr_bin.get_info_name("bin") - self.assertEqual(self.username in gr_bin["gr_mem"], True, "FAIL: User not in supplementary group after usermod -G -a") - - - def testAddWithCommonName(self): - """ useradd: Specifying a comment (user for account name) """ - comment = "zzzzzz" - (status, output) = commands.getstatusoutput("useradd -c %s %s" % (comment, self.username)) - self.failUnlessEqual(status, 0, output) - - created = UserInfo() - created.get_info_name(self.username) - self.assertEqual(created["pw_gecos"], comment, "FAIL: failed to create a user with a GECOS comment") - - def testHomePermissions(self): - """ useradd: Check if permissions on newly created home dir match the umask """ - defaults = LoginDefsParser() - - (status, output) = commands.getstatusoutput("useradd %s" % (self.username)) - self.failUnlessEqual(status, 0, output) - - created = UserInfo() - created.get_info_name(self.username) - - import stat - perm = os.stat(created["pw_dir"])[stat.ST_MODE] - mode = int(oct(perm & 0777)) - - self.assertEqual(defaults["UMASK"], "077", "FAIL: umask setting is not sane - is %s, should be 077" % (defaults["UMASK"])) - self.assertEqual(int(defaults["UMASK"]) + mode , 777, "FAIL: newly-created home dir does not match the umask") - - def testCreateMailSpool(self): - """ useradd: Check whether the mail spool gets created when told to""" - # set up creating of mail spool - defaults = LoginDefsParser("/etc/default/useradd", split="=") - - create_mail = defaults["CREATE_MAIL_SPOOL"] - defaults["CREATE_MAIL_SPOOL"] = "yes" - defaults.serialize() - - login_defs = LoginDefsParser() - - (status, output) = commands.getstatusoutput("useradd %s" % (self.username)) - self.failUnlessEqual(status, 0, output) - - # clean up - defaults["CREATE_MAIL_SPOOL"] = create_mail - defaults.serialize() - self.assertEqual(os.path.exists(login_defs["MAIL_DIR"] + "/" + self.username), True, "FAIL: useradd did not create mail spool") - - def testDefaultMailSettings(self): - """ useradd: Check whether the mail spool is on by default""" - defaults = LoginDefsParser("/etc/default/useradd", split="=") - self.assertEqual(defaults["CREATE_MAIL_SPOOL"], "yes\n") - - def testNoLastlog(self): - """ useradd: Check if the -l option prevents from being added to the lastlog """ - pass # FIXME - add some code here - - -class TestUseraddWeirdNameTest(unittest.TestCase, ShadowUtilsTestBase): - """ Tests addition/removal of usernames that have proven to be problematic in the past. - The reason to separate these from the main useradd test suite is to not run the setUp - and tearDown methods """ - - def addAndRemove(self, username, success=True): - expected = self.getDefaultUserInfo(username) - expected["pw_name"] = username - - (status, output) = commands.getstatusoutput("useradd %s" % (username)) - if success: - self.failUnlessEqual(status, 0, output) - else: - self.failIfEqual(status, 0, output) - return True - - created = UserInfo() - created.get_info_name(username) - self.assertEqual(created.lazy_compare(expected), True, "FAIL: TestUseraddWeirdName::addAndRemove - could not add a user") - - # the cleanup method won't help this time - (status, output) = commands.getstatusoutput("userdel -r %s" % (username)) - self.failUnlessEqual(status, 0, output) - - def testNumericName(self): - """ useradd: Test if an user with a purely numerical name can be added (123) """ - return self.addAndRemove("123") - - def testSambaName(self): - """ useradd: Test if an user with a name with a dollar at the end can be added (joepublic$ ) """ - return self.addAndRemove("joepublic$") - - def testDotInName(self): - """ useradd: Test if an user with a name with a dot in it can be added (joe.public ) """ - return self.addAndRemove("joe.public") - - def testAtInName(self): - """ useradd: Test if an user with an '@' in name can be added (joe@public.com) - should fail """ - return self.addAndRemove("joe@public.com", False) - - def testUppercase(self): - """ useradd: Test if an user with UPPERCASE or Uppercase name can be added """ - return self.addAndRemove("JOEPUBLIC") - return self.addAndRemove("Joepublic") - -class TestUseraddDefaultsChange(unittest.TestCase, ShadowUtilsTestBase): - def testDefaultsChange(self): - """ useradd: Test overriding default settings (shell, home dir, group) with a -D option """ - save = self.getDefaults() - - new_defs = dict() - new_defs["SHELL"] = "/bin/ksh" - new_defs["GROUP"] = "1" - new_defs["HOME"] = "/tmp" - - command = "useradd -D -s%s -g%s -b%s" % (new_defs["SHELL"], new_defs["GROUP"], new_defs["HOME"]) - (status, output) = commands.getstatusoutput(command) - self.failUnlessEqual(status, 0, output) - - overriden = self.getDefaults() - [ self.assertEqual(overriden[k], new_defs[k]) for k in new_defs.keys() ] - - command = "useradd -D -s%s -g%s -b%s" % (save["SHELL"], save["GROUP"], save["HOME"]) - (status, output) = commands.getstatusoutput(command) - self.failUnlessEqual(status, 0, output) - - -class TestUserdel(unittest.TestCase, ShadowUtilsTestBase): - def setUp(self): - self.username = "test-shadow-utils-userdel" - (status, output) = commands.getstatusoutput("useradd %s" % (self.username)) - self.failUnlessEqual(status, 0, output) - - def testRemoveUserGroup(self): - """ userdel: test if userdel removes user's group when he's deleted - regression test for #201379 """ - (status, output) = commands.getstatusoutput("userdel -r %s" % (self.username)) - self.failUnlessEqual(status, 0, output) - - # This would fail if we did not have the group removed - (status, output) = commands.getstatusoutput("useradd %s" % (self.username)) - self.failUnlessEqual(status, 0, output) - - (status, output) = commands.getstatusoutput("userdel -r %s" % (self.username)) - self.failUnlessEqual(status, 0, output) - -class TestUsermod(unittest.TestCase, ShadowUtilsTestBase): - def setUp(self): - self.username = "test-shadow-utils-usermod" - (status, output) = commands.getstatusoutput("useradd %s" % (self.username)) - self.failUnlessEqual(status, 0, output) - - def tearDown(self): - (status, output) = commands.getstatusoutput("userdel -r %s" % (self.username)) - self.failUnlessEqual(status, 0, output) - - def testAppendToSupplementaryGroup(self): - """ usermod: Test if a user can be added to a supplementary group """ - add_group = "additional_group" - (status, output) = commands.getstatusoutput("groupadd %s" % (add_group)) - self.failUnlessEqual(status, 0, output) - - (status_mod, output_mod) = commands.getstatusoutput("usermod -a -G %s %s" % (add_group, self.username)) - add_group_info = GroupInfo() - add_group_info.get_info_name(add_group) - (status, output) = commands.getstatusoutput("groupdel %s" % (add_group)) - - self.failUnlessEqual(status, 0, output) - self.failUnlessEqual(status_mod, 0, output_mod) - self.assertEqual(self.username in add_group_info["gr_mem"], True, "User not in supplementary group after usermod -G --append") - - - def testAppendToSupplementaryGroupLongOption(self): - """ usermod: Test if a user can be added to a supplementary group via --append rather that -a (regression test for 222540) """ - # this is known to not work on older RHELs - test what we are running - rhv = RedHatVersion() - runs = rhv.get_info() - if rhv.is_rhel(): - if runs[1] < 5: - print "This test makes sense for RHEL5+" - return - else: - if runs[1] < 6: - print "This test makes sense for Fedora 6+" - return - - type, release, version = RedHatVersion().get_info() - if RedHatVersion().is_rhel(): - if release < 5 or (release == 5 and version < 2): - print "This test makes sense for RHEL 5.2+" - return - - add_group = "additional_group" - (status, output) = commands.getstatusoutput("groupadd %s" % (add_group)) - self.failUnlessEqual(status, 0, output) - - (status_mod, output_mod) = commands.getstatusoutput("usermod --append -G %s %s" % (add_group, self.username)) - add_group_info = GroupInfo() - add_group_info.get_info_name(add_group) - (status, output) = commands.getstatusoutput("groupdel %s" % (add_group)) - - self.failUnlessEqual(status, 0, output) - self.failUnlessEqual(status_mod, 0, output_mod) - self.assertEqual(self.username in add_group_info["gr_mem"], True, "User not in supplementary group after usermod -G --append") - - - def testNameChange(self): - """ usermod: Test if the comment field (used as the Common Name) can be changed """ - new_comment = "zzzzzz" - - (status, output) = commands.getstatusoutput("usermod -c %s %s" % (new_comment, self.username)) - self.failUnlessEqual(status, 0, output) - - created = UserInfo() - created.get_info_name(self.username) - - self.assertEqual(created["pw_gecos"], new_comment) - - def testHomeChange(self): - """ usermod: Test if user's home directory can be changed """ - new_home = "/tmp" - created = UserInfo() - created.get_info_name(self.username) - old_home = created["pw_dir"] - - (status, output) = commands.getstatusoutput("usermod -d %s %s" % (new_home, self.username)) - self.failUnlessEqual(status, 0, output) - - created.get_info_name(self.username) - self.assertEqual(created["pw_dir"], new_home) - - # revert to old home so we can userdel -r in tearDown - (status, output) = commands.getstatusoutput("usermod -d %s %s" % (old_home, self.username)) - self.failUnlessEqual(status, 0, output) - - # FIXME - test if contents of /home directories are transferred with the -m option - # FIXME - test if new home is created if does not exist before - - def testGIDChange(self): - """ usermod: Test if user's gid can be changed. """ - new_group = "root" - # test non-existing group - (status_fail, output_fail) = commands.getstatusoutput("usermod -g no-such-group %s" % (self.username)) - (status, output) = commands.getstatusoutput("usermod -g %s %s" % (new_group, self.username)) - - created = UserInfo() - created.get_info_name(self.username) - - left = GroupInfo() - if left.get_info_name(self.username) == None: - (status_del, output_del) = commands.getstatusoutput("groupdel %s" % (self.username)) - self.failUnlessEqual(status_del, 0, output_del) - - self.failIfEqual(status_fail, 0, output_fail) - self.failUnlessEqual(status, 0, output) - self.assertEqual(created["pw_gid"], 0) #0 is root group - - def testLoginChange(self): - """ usermod: Test if user's login can be changed """ - new_login = "usermod-login-change" - user = UserInfo() - user.get_info_name(self.username) - uid = user["pw_uid"] # UID won't change even when login does - - # test changing to an existing user name - (status, output) = commands.getstatusoutput("usermod -l root %s" % (self.username)) - self.failIfEqual(status, 0, output) - - (status, output) = commands.getstatusoutput("usermod -l %s %s" % (new_login, self.username)) - self.failUnlessEqual(status, 0, output) - user.get_info_name(new_login) - self.assertEqual(user["pw_uid"], uid) - - # revert so we can userdel -r on tearDown - (status, output) = commands.getstatusoutput("usermod -l %s %s" % (self.username, new_login)) - self.failUnlessEqual(status, 0, output) - - def testShellChange(self): - """ usermod: Test if user's shell can be changed """ - new_shell = "/bin/sh" - - (status, output) = commands.getstatusoutput("usermod -s %s %s" % (new_shell, self.username)) - self.failUnlessEqual(status, 0, output) - - created = UserInfo() - created.get_info_name(self.username) - self.assertEqual(created["pw_shell"], new_shell) - -class TestGroupadd(unittest.TestCase, ShadowUtilsTestBase): - def setUp(self): - self.groupname = "test-shadow-utils-groups" - - def tearDown(self): - commands.getstatusoutput("groupdel %s" % (self.groupname)) - - def testAddGroup(self): - """ groupadd: Basic adding of a group """ - - expected = GroupInfo() - expected["gr_name"] = self.groupname - - (status, output) = commands.getstatusoutput("groupadd %s" % (self.groupname)) - self.failUnlessEqual(status, 0, output) - - created = GroupInfo() - created.get_info_name(self.groupname) - self.assertEqual(created.lazy_compare(expected), True, "FAIL: Could not add a group") - - def testAddSystemGroup(self): - """ groupadd: Adding a system group with gid < MIN_GID """ - - expected = GroupInfo() - expected["gr_name"] = self.groupname - defaults = LoginDefsParser() - - (status, output) = commands.getstatusoutput("groupadd -r %s" % (self.groupname)) - self.failUnlessEqual(status, 0, output) - - created = GroupInfo() - created.get_info_name(self.groupname) - self.assertEqual(created["gr_gid"] < defaults["GID_MIN"], True, "FAIL: System group has gid >= GID_MIN") - self.assertEqual(created.lazy_compare(expected), True, "FAIL: Could not add a system group") - - def testAddExistingGid(self): - """ groupadd: Test if we group with an existing GID can be added """ - (status, output) = commands.getstatusoutput("groupadd %s" % (self.groupname)) - self.failUnlessEqual(status, 0, output) - - gname = "%s-2" % (self.groupname) - - created = GroupInfo() - created.get_info_name(self.groupname) - - # no -o option -> this should fail - (status, output) = commands.getstatusoutput("groupadd -g%s %s" % (created["gr_gid"], gname)) - self.failIfEqual(status, 0, output) - - # override with -o option, should pass now - (status, output) = commands.getstatusoutput("groupadd -g%s -o %s" % (created["gr_gid"], gname)) - self.failUnlessEqual(status, 0, output) - - # test if the new GID is really the same - same_gid = GroupInfo() - same_gid.get_info_name(gname) - self.assertEqual(same_gid["gr_gid"], created["gr_gid"]) - - # clean up - (status, output) = commands.getstatusoutput("groupdel %s" % (gname)) - self.failUnlessEqual(status, 0, output) - - - def testOverrideDefaults(self): - """ groupadd: Test if the defaults can be overriden with the -K option """ - # this is known to not work on older RHELs - test what we are running - rhv = RedHatVersion() - runs = rhv.get_info() - if rhv.is_rhel(): - if runs[1] < 5: - print "This test makes sense for RHEL5+" - return - else: - if runs[1] < 6: - print "This test makes sense for Fedora 6+" - return - - - GID_MIN = 600 - GID_MAX = 625 - - (status, output) = commands.getstatusoutput("groupadd -K GID_MIN=%d -K GID_MAX=%d %s" % - (GID_MIN, GID_MAX, self.groupname)) - self.failUnlessEqual(status, 0, output) - - created = GroupInfo() - created.get_info_name(self.groupname) - self.assertEqual(GID_MIN <= created["gr_gid"] <= GID_MAX, True, "FAIL: created an user with UID of %d" % (created["gr_gid"])) - - - def testFOption(self): - """ groupadd: Tests the -f option of groupadd """ - (status, output) = commands.getstatusoutput("groupadd %s" % (self.groupname)) - self.failUnlessEqual(status, 0, output) - - (status, output) = commands.getstatusoutput("groupadd -f %s" % (self.groupname)) - self.assertEqual(status, 0, output) - -class TestGroupaddInvalidName(unittest.TestCase, ShadowUtilsTestBase): - def testGroupaddInvalidName(self): - """ groupadd: Test adding of a group with an invalid name """ - (status, output) = commands.getstatusoutput("groupadd foo?") - self.assertNotEqual(status, 0, output) - (status, output) = commands.getstatusoutput("groupadd aaaaabbbbbcccccdddddeeeeefffffggg") #33 chars - self.assertNotEqual(status, 0, output) - -class TestGroupaddValidName(unittest.TestCase, ShadowUtilsTestBase): - def testGroupaddValidName(self): - """ groupadd: Test adding and removing of groups with maximal valid name and name ending with $ """ - (status, output) = commands.getstatusoutput("groupadd aaaaabbbbbcccccdddddeeeeefffffgg") #32 chars - self.assertEqual(status, 0, output) - (status, output) = commands.getstatusoutput("groupadd aaaaabbbbbcccccdddddeeeeefffffg\$") #32 chars - self.assertEqual(status, 0, output) - (status, output) = commands.getstatusoutput("groupdel aaaaabbbbbcccccdddddeeeeefffffgg") #32 chars - self.assertEqual(status, 0, output) - (status, output) = commands.getstatusoutput("groupdel aaaaabbbbbcccccdddddeeeeefffffg\$") #32 chars - self.assertEqual(status, 0, output) - - -class TestGroupmod(unittest.TestCase, ShadowUtilsTestBase): - def setUp(self): - self.groupname = "test-shadow-utils-groups" - (status, output) = commands.getstatusoutput("groupadd %s" % (self.groupname)) - self.failUnlessEqual(status, 0, output) - - def tearDown(self): - commands.getstatusoutput("groupdel %s" % (self.groupname)) - - def testChangeGID(self): - """ groupmod: Test changing a gid of a group """ - expected = GroupInfo() - expected["gr_name"] = self.groupname - expected["gr_gid"] = 54321 - - (status, output) = commands.getstatusoutput("groupmod -g%d %s" % (expected["gr_gid"], self.groupname)) - self.failUnlessEqual(status, 0, output) - - created = GroupInfo() - created.get_info_name(self.groupname) - self.assertEqual(created.lazy_compare(expected), True, "FAIL: Could not change GID of an existing group") - - def testChangeGIDToExistingValue(self): - """ groupmod: Test changing GID to an existing value """ - second_name = "%s-2" % (self.groupname) - - created = GroupInfo() - created.get_info_name(self.groupname) - - expected = GroupInfo() - expected["gr_name"] = self.groupname - expected["gr_gid"] = created["gr_gid"] - - (status, output) = commands.getstatusoutput("groupadd %s" % (second_name)) - self.failUnlessEqual(status, 0, output) - - # try to assingn GID of the first group to the second - this should fail without the -o option - (status, output) = commands.getstatusoutput("groupmod -g%d %s" % (created["gr_gid"], second_name)) - self.failIfEqual(status, 0, output) - - # should pass with the -o option - (status, output) = commands.getstatusoutput("groupmod -g%d -o %s" % (created["gr_gid"], second_name)) - self.failUnlessEqual(status, 0, output) - - self.assertEqual(created.lazy_compare(expected), True, "FAIL: Could not change GID of an existing group to an existing one") - - # clean up - commands.getstatusoutput("groupdel %s" % (second_name)) - self.failUnlessEqual(status, 0, output) - - def testChangeGroupName(self): - """ groupmod: Test changing a group's name """ - second_name = "%s-2" % (self.groupname) - - created = GroupInfo() - created.get_info_name(self.groupname) - - (status, output) = commands.getstatusoutput("groupmod -n%s %s" % (second_name, self.groupname)) - self.failUnlessEqual(status, 0, output) - - changed = GroupInfo() - changed.get_info_gid(created["gr_gid"]) - self.assertEqual(changed["gr_name"], second_name) - self.assertEqual(changed["gr_gid"], created["gr_gid"]) - - # change back, so the group could be deleted by tearDown - (status, output) = commands.getstatusoutput("groupmod -n%s %s" % (self.groupname, second_name)) - self.failUnlessEqual(status, 0, output) - - def testChangeGroupNameExisting(self): - """ groupmod: Test changing a group's name to an existing one """ - existing = "bin" - (status, output) = commands.getstatusoutput("groupmod -n%s %s" % (existing, self.groupname)) - self.assertNotEqual(status, 0, output) # man groupmod -> 9: group name already in use - - def testChangeNonExistingGroup(self): - """ groupmod: Test properties of a non-existing group """ - nonexistent = "foobar" - (status, output) = commands.getstatusoutput("groupmod -nspameggs %s" % (nonexistent)) - self.assertNotEqual(status, 0, status) # man groupmod -> 6: specified group doesn't exist - -class TestGroupdel(unittest.TestCase, ShadowUtilsTestBase): - def testCorrectGroupdel(self): - """ groupdel: Basic usage of groupdel """ - self.groupname = "test-shadow-utils-groups" - (status, output) = commands.getstatusoutput("groupadd %s" % (self.groupname)) - self.failUnlessEqual(status, 0, output) - (status, output) = commands.getstatusoutput("groupdel %s" % (self.groupname)) - self.assertEqual(status, 0, output) - - def testGroupdelNoSuchGroup(self): - """ groupdel: Remove non-existing group """ - (status, output) = commands.getstatusoutput("groupdel foobar") - self.assertNotEqual(status, 0, output) - - def testRemovePrimaryGroup(self): - """ groupdel: Remove a primary group of an user """ - username = "test-groupdel-primary" - (status, output) = commands.getstatusoutput("useradd %s" % (username)) - self.failUnlessEqual(status, 0, output) - - (status, output) = commands.getstatusoutput("groupdel %s" % (username)) - self.assertNotEqual(status, 0, output) - - # clean up - (status, output) = commands.getstatusoutput("userdel -r %s" % (username)) - self.failUnlessEqual(status, 0, output) - -class TestPwckGrpck(unittest.TestCase): - def setUp(self): - self.passwd_path = tempfile.mktemp(suffix="test-pwck-passwd") - self.passwd_file = open(self.passwd_path, "w") - self.group_path = tempfile.mktemp(suffix="test-pwck-grp") - self.group_file = open(self.group_path, "w") - self.gshadow_path = tempfile.mktemp(suffix="test-pwck-gshadow") - self.gshadow_file = open(self.gshadow_path, "w") - - def tearDown(self): - self.passwd_file.close() - self.group_file.close() - self.gshadow_file.close() - - os.remove(self.passwd_path) - os.remove(self.group_path) - os.remove(self.gshadow_path) - - def runPwckCheck(self, passwd, group): - self.passwd_file.truncate() - self.group_file.truncate() - - self.passwd_file.write(passwd) - self.passwd_file.flush() - self.group_file.write(group) - self.group_file.flush() - - command = "pwck -r %s %s" % (self.passwd_path, self.group_path) - return commands.getstatusoutput(command) - - def runGrpCheck(self, group, gshadow): - self.group_file.truncate() - self.gshadow_file.truncate() - - self.gshadow_file.write(gshadow) - self.gshadow_file.flush() - - self.group_file.write(group) - self.group_file.flush() - - command = "grpck -r %s %s" % (self.group_path, self.gshadow_path) - return commands.getstatusoutput(command) - - - def testValidEntries(self): - """ pwck: a valid entry """ - status, output = self.runPwckCheck("foo:x:685:0::/dev/null:/bin/bash", "") - rhv = RedHatVersion() - runs = rhv.get_info() - if rhv.is_rhel(): - if runs[1] < 6: - self.assertEqual(status, 0, output) - else: - self.assertNotEqual(status, 0, output) - - def testNumberOfFields(self): - """ pwck: invalid number of fields in the record """ - not_enough = "foo:x:685:685::/dev/null" - too_many = "foo:x:685:685::/dev/null:/bin/bash:comment" - status, output = self.runPwckCheck(not_enough, "") - self.assertNotEqual(status, 0, output) - - status, output = self.runPwckCheck(too_many, "") - self.assertNotEqual(status, 0, output) - - def testUniqueUserName(self): - """ pwck: unique user name in the record """ - duplicate_username = "foo:x:685:685::/dev/null:/bin/bash\nfoo:x:686:686::/dev/null:/bin/bash" - status, output = self.runPwckCheck(duplicate_username, "") - self.assertNotEqual(status, 0, output) - - def testValidID(self): - """ pwck: invalid UID in the records """ - invalid_ids = [ "foo:x:-1:685::/dev/null:/bin/bash", "foo:x:blah:685::/dev/null:/bin/bash", "foo:x:1234567890:685::/dev/null:/bin/bash" ] - for record in invalid_ids: - status, output = self.runPwckCheck(record, "") - self.assertNotEqual(status, 0, record) - - - def testValidPrimaryGroup(self): - """ pwck: invalid primary group """ - invalid_groups = [ "foo:x:685:-1::/dev/null:/bin/bash", "foo:x:685:blah::/dev/null:/bin/bash", "foo:x:685:1234567890::/dev/null:/bin/bash" ] - for record in invalid_groups: - status, output = self.runPwckCheck("", record) - self.assertNotEqual(status, 0, output) - - def testValidHomeDir(self): - """ pwck: invalid home dir """ - for record in [ "foo:x:685:685::123:/bin/bash", "foo:x:685:685::/path/to/nowhere:/bin/bash", "foo:x:685:1234567890::!:/bin/bash" ]: - status, output = self.runPwckCheck(record, "") - self.assertNotEqual(status, 0, output) - - def testBZ164954(self): - """ grpck: regression test for BZ164954 """ - record = "root:x:0:root\nbin:x:1:root,bin,daemon\ndaemon:x:2:root,bin,daemon\nsys:x:3:root,bin,adm\nadm:x:4:root,adm,daemon" - status, output = self.runGrpCheck("", record) - self.assertNotEqual(status, 0, output) - -if __name__ == "__main__": - broken_on_rhel4 = { "TestUseradd" : [ "testCustomUID", "testCustomGID" ] } - - if os.getuid() != 0: - print "This test must be run as root" - sys.exit(1) - - unittest.main() - diff --git a/tests/tests.yml b/tests/tests.yml deleted file mode 100644 index 09f4769..0000000 --- a/tests/tests.yml +++ /dev/null @@ -1,13 +0,0 @@ ---- -# This first play always runs on the local staging system -- hosts: localhost - roles: - - role: standard-test-beakerlib - tags: - - classic - - atomic - tests: - - sanity - required_packages: - - shadow-utils # sanity test needs shadow-utils - - python # sanity test needs python From 5d57398d35ece8d92141cbc07e712f3d4bc3fcbe Mon Sep 17 00:00:00 2001 From: Iker Pedrosa Date: Fri, 31 Oct 2025 12:52:35 +0100 Subject: [PATCH 10/30] Stop setting SELinux labels in chroot and prefix environments Resolves: #2249524 Signed-off-by: Iker Pedrosa --- shadow-4.18.0-selinux-chroot-prefix.patch | 10433 ++++++++++++++++++++ shadow-utils.spec | 8 +- 2 files changed, 10440 insertions(+), 1 deletion(-) create mode 100644 shadow-4.18.0-selinux-chroot-prefix.patch diff --git a/shadow-4.18.0-selinux-chroot-prefix.patch b/shadow-4.18.0-selinux-chroot-prefix.patch new file mode 100644 index 0000000..a5db17b --- /dev/null +++ b/shadow-4.18.0-selinux-chroot-prefix.patch @@ -0,0 +1,10433 @@ +From 218cc94da84459bfa5b8417d933e8898e24ecff6 Mon Sep 17 00:00:00 2001 +From: Iker Pedrosa +Date: Fri, 25 Apr 2025 15:13:12 +0200 +Subject: [PATCH 01/15] 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 +--- + lib/commonio.c | 16 ++++++++++------ + lib/commonio.h | 2 +- + lib/groupio.c | 2 +- + lib/pwio.c | 2 +- + lib/sgroupio.c | 2 +- + lib/shadowio.c | 2 +- + lib/subordinateio.c | 4 ++-- + 7 files changed, 17 insertions(+), 13 deletions(-) + +diff --git a/lib/commonio.c b/lib/commonio.c +index 7304ec00..7ee6df4d 100644 +--- a/lib/commonio.c ++++ b/lib/commonio.c +@@ -473,7 +473,7 @@ int commonio_unlock (struct commonio_db *db) + + if (db->isopen) { + db->readonly = true; +- if (commonio_close (db) == 0) { ++ if (commonio_close (db, true) == 0) { + if (db->locked) { + dec_lock_count (); + } +@@ -885,7 +885,7 @@ static int write_all (const struct commonio_db *db) + } + + +-int commonio_close (struct commonio_db *db) ++int commonio_close (struct commonio_db *db, bool process_selinux) + { + bool errors = false; + char buf[1024]; +@@ -927,7 +927,8 @@ int commonio_close (struct commonio_db *db) + } + + #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 +@@ -942,7 +943,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 +@@ -961,7 +963,8 @@ int commonio_close (struct commonio_db *db) + 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 +@@ -999,7 +1002,8 @@ int commonio_close (struct commonio_db *db) + } + + #ifdef WITH_SELINUX +- if (reset_selinux_file_context () != 0) { ++ if (process_selinux ++ && reset_selinux_file_context () != 0) { + goto fail; + } + #endif +diff --git a/lib/commonio.h b/lib/commonio.h +index fedbefa3..dadc0500 100644 +--- a/lib/commonio.h ++++ b/lib/commonio.h +@@ -138,7 +138,7 @@ 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_close (struct commonio_db *, bool); + extern int commonio_unlock (struct commonio_db *); + extern void commonio_del_entry (struct commonio_db *, + const struct commonio_entry *); +diff --git a/lib/groupio.c b/lib/groupio.c +index 72391433..5076fd3d 100644 +--- a/lib/groupio.c ++++ b/lib/groupio.c +@@ -186,7 +186,7 @@ int gr_rewind (void) + + int gr_close (void) + { +- return commonio_close (&group_db); ++ return commonio_close (&group_db, true); + } + + int gr_unlock (void) +diff --git a/lib/pwio.c b/lib/pwio.c +index 415447d6..6f666ec6 100644 +--- a/lib/pwio.c ++++ b/lib/pwio.c +@@ -158,7 +158,7 @@ int pw_rewind (void) + + int pw_close (void) + { +- return commonio_close (&passwd_db); ++ return commonio_close (&passwd_db, true); + } + + int pw_unlock (void) +diff --git a/lib/sgroupio.c b/lib/sgroupio.c +index ef82976d..659dcd06 100644 +--- a/lib/sgroupio.c ++++ b/lib/sgroupio.c +@@ -278,7 +278,7 @@ int sgr_rewind (void) + + int sgr_close (void) + { +- return commonio_close (&gshadow_db); ++ return commonio_close (&gshadow_db, true); + } + + int sgr_unlock (void) +diff --git a/lib/shadowio.c b/lib/shadowio.c +index 8b1c1d2c..55d33da2 100644 +--- a/lib/shadowio.c ++++ b/lib/shadowio.c +@@ -195,7 +195,7 @@ int spw_close (void) + return 0; + } + #endif /* WITH_TCB */ +- retval = commonio_close (&shadow_db); ++ retval = commonio_close (&shadow_db, true); + #ifdef WITH_TCB + if (use_tcb && (shadowtcb_gain_priv () == SHADOWTCB_FAILURE)) { + return 0; +diff --git a/lib/subordinateio.c b/lib/subordinateio.c +index 27f888b9..3638ec61 100644 +--- a/lib/subordinateio.c ++++ b/lib/subordinateio.c +@@ -670,7 +670,7 @@ int sub_uid_remove (const char *owner, uid_t start, unsigned long count) + + int sub_uid_close (void) + { +- return commonio_close (&subordinate_uid_db); ++ return commonio_close (&subordinate_uid_db, true); + } + + int sub_uid_unlock (void) +@@ -777,7 +777,7 @@ int sub_gid_remove (const char *owner, gid_t start, unsigned long count) + + int sub_gid_close (void) + { +- return commonio_close (&subordinate_gid_db); ++ return commonio_close (&subordinate_gid_db, true); + } + + int sub_gid_unlock (void) +-- +2.50.0 + + +From 7e7dbe0e65d2795c15c42bb04c7c03b9ff86ea84 Mon Sep 17 00:00:00 2001 +From: Iker Pedrosa +Date: Fri, 25 Apr 2025 15:19:29 +0200 +Subject: [PATCH 02/15] 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 +--- + lib/pwio.c | 4 ++-- + lib/pwio.h | 3 ++- + src/chage.c | 2 +- + src/chfn.c | 2 +- + src/chpasswd.c | 2 +- + src/chsh.c | 2 +- + src/groupmod.c | 2 +- + src/newusers.c | 2 +- + src/passwd.c | 2 +- + src/pwck.c | 2 +- + src/pwconv.c | 2 +- + src/pwunconv.c | 2 +- + src/useradd.c | 2 +- + src/userdel.c | 6 +++--- + src/usermod.c | 2 +- + 15 files changed, 19 insertions(+), 18 deletions(-) + +diff --git a/lib/pwio.c b/lib/pwio.c +index 6f666ec6..c5c1cdb1 100644 +--- a/lib/pwio.c ++++ b/lib/pwio.c +@@ -156,9 +156,9 @@ int pw_rewind (void) + return commonio_next (&passwd_db); + } + +-int pw_close (void) ++int pw_close (bool process_selinux) + { +- return commonio_close (&passwd_db, true); ++ return commonio_close (&passwd_db, process_selinux); + } + + int pw_unlock (void) +diff --git a/lib/pwio.h b/lib/pwio.h +index 882a7c7a..b051a7e7 100644 +--- a/lib/pwio.h ++++ b/lib/pwio.h +@@ -14,8 +14,9 @@ + + #include + #include ++#include + +-extern int pw_close (void); ++extern int pw_close (bool process_selinux); + extern /*@observer@*/ /*@null@*/const struct passwd *pw_locate (const char *name); + extern /*@observer@*/ /*@null@*/const struct passwd *pw_locate_uid (uid_t uid); + extern int pw_lock (void); +diff --git a/src/chage.c b/src/chage.c +index 4b9c96d6..1ebe34c5 100644 +--- a/src/chage.c ++++ b/src/chage.c +@@ -557,7 +557,7 @@ static void close_files (void) + * Close the password file. If any entries were modified, the file + * will be re-written. + */ +- if (pw_close () == 0) { ++ if (pw_close (true) == 0) { + fprintf (stderr, _("%s: failure while writing changes to %s\n"), Prog, pw_dbname ()); + SYSLOG ((LOG_ERR, "failure while writing changes to %s", pw_dbname ())); + fail_exit (E_NOPERM); +diff --git a/src/chfn.c b/src/chfn.c +index 58502929..abf88cc8 100644 +--- a/src/chfn.c ++++ b/src/chfn.c +@@ -493,7 +493,7 @@ static void update_gecos (const char *user, char *gecos) + /* + * Changes have all been made, so commit them and unlock the file. + */ +- if (pw_close () == 0) { ++ if (pw_close (true) == 0) { + fprintf (stderr, _("%s: failure while writing changes to %s\n"), Prog, pw_dbname ()); + SYSLOG ((LOG_ERR, "failure while writing changes to %s", pw_dbname ())); + fail_exit (E_NOPERM); +diff --git a/src/chpasswd.c b/src/chpasswd.c +index 315e91ce..17f3d6d5 100644 +--- a/src/chpasswd.c ++++ b/src/chpasswd.c +@@ -393,7 +393,7 @@ static void close_files (void) + spw_locked = false; + } + +- if (pw_close () == 0) { ++ if (pw_close (true) == 0) { + fprintf (stderr, + _("%s: failure while writing changes to %s\n"), + Prog, pw_dbname ()); +diff --git a/src/chsh.c b/src/chsh.c +index 236ff784..b5ae28da 100644 +--- a/src/chsh.c ++++ b/src/chsh.c +@@ -428,7 +428,7 @@ static void update_shell (const char *user, char *newshell) + /* + * Changes have all been made, so commit them and unlock the file. + */ +- if (pw_close () == 0) { ++ if (pw_close (true) == 0) { + fprintf (stderr, _("%s: failure while writing changes to %s\n"), Prog, pw_dbname ()); + SYSLOG ((LOG_ERR, "failure while writing changes to %s", pw_dbname ())); + fail_exit (1); +diff --git a/src/groupmod.c b/src/groupmod.c +index cce6fd49..14b7e746 100644 +--- a/src/groupmod.c ++++ b/src/groupmod.c +@@ -534,7 +534,7 @@ static void close_files (void) + #endif /* SHADOWGRP */ + + if (gflg) { +- if (pw_close () == 0) { ++ if (pw_close (true) == 0) { + fprintf (stderr, + _("%s: failure while writing changes to %s\n"), + Prog, pw_dbname ()); +diff --git a/src/newusers.c b/src/newusers.c +index 38cbdddb..a738f944 100644 +--- a/src/newusers.c ++++ b/src/newusers.c +@@ -939,7 +939,7 @@ static void open_files (void) + */ + static void close_files (void) + { +- if (pw_close () == 0) { ++ if (pw_close (true) == 0) { + fprintf (stderr, _("%s: failure while writing changes to %s\n"), Prog, pw_dbname ()); + SYSLOG ((LOG_ERR, "failure while writing changes to %s", pw_dbname ())); + fail_exit (EXIT_FAILURE); +diff --git a/src/passwd.c b/src/passwd.c +index cdd817a2..8bdff6dd 100644 +--- a/src/passwd.c ++++ b/src/passwd.c +@@ -586,7 +586,7 @@ static void update_noshadow (void) + Prog, pw_dbname (), npw->pw_name); + fail_exit (E_FAILURE); + } +- if (pw_close () == 0) { ++ if (pw_close (true) == 0) { + (void) fprintf (stderr, + _("%s: failure while writing changes to %s\n"), + Prog, pw_dbname ()); +diff --git a/src/pwck.c b/src/pwck.c +index 6272839f..b1c6970f 100644 +--- a/src/pwck.c ++++ b/src/pwck.c +@@ -309,7 +309,7 @@ static void close_files (bool changed) + * changes to the files. + */ + if (changed) { +- if (pw_close () == 0) { ++ if (pw_close (true) == 0) { + fprintf (stderr, + _("%s: failure while writing changes to %s\n"), + Prog, pw_dbname ()); +diff --git a/src/pwconv.c b/src/pwconv.c +index 1bb92752..da3a5820 100644 +--- a/src/pwconv.c ++++ b/src/pwconv.c +@@ -281,7 +281,7 @@ int main (int argc, char **argv) + SYSLOG ((LOG_ERR, "failure while writing changes to %s", spw_dbname ())); + fail_exit (E_FAILURE); + } +- if (pw_close () == 0) { ++ if (pw_close (true) == 0) { + fprintf (stderr, + _("%s: failure while writing changes to %s\n"), + Prog, pw_dbname ()); +diff --git a/src/pwunconv.c b/src/pwunconv.c +index 2e4d3d75..8423cda4 100644 +--- a/src/pwunconv.c ++++ b/src/pwunconv.c +@@ -207,7 +207,7 @@ int main (int argc, char **argv) + + (void) spw_close (); /* was only open O_RDONLY */ + +- if (pw_close () == 0) { ++ if (pw_close (true) == 0) { + fprintf (stderr, + _("%s: failure while writing changes to %s\n"), + Prog, pw_dbname ()); +diff --git a/src/useradd.c b/src/useradd.c +index 36b9b8f8..8c9e9003 100644 +--- a/src/useradd.c ++++ b/src/useradd.c +@@ -1564,7 +1564,7 @@ static void process_flags (int argc, char **argv) + */ + static void close_files (void) + { +- if (pw_close () == 0) { ++ if (pw_close (true) == 0) { + fprintf (stderr, _("%s: failure while writing changes to %s\n"), Prog, pw_dbname ()); + SYSLOG ((LOG_ERR, "failure while writing changes to %s", pw_dbname ())); + fail_exit (E_PW_UPDATE); +diff --git a/src/userdel.c b/src/userdel.c +index 72ce11fa..b5a02b3c 100644 +--- a/src/userdel.c ++++ b/src/userdel.c +@@ -390,7 +390,7 @@ static void remove_usergroup (void) + */ + static void close_files (void) + { +- if (pw_close () == 0) { ++ if (pw_close (true) == 0) { + fprintf (stderr, _("%s: failure while writing changes to %s\n"), Prog, pw_dbname ()); + SYSLOG ((LOG_ERR, "failure while writing changes to %s", pw_dbname ())); + fail_exit (E_PW_UPDATE); +@@ -1046,7 +1046,7 @@ int main (int argc, char **argv) + pw_open(O_RDONLY); + pwd = pw_locate (user_name); /* we care only about local users */ + if (NULL == pwd) { +- pw_close(); ++ pw_close(true); + fprintf (stderr, _("%s: user '%s' does not exist\n"), + Prog, user_name); + #ifdef WITH_AUDIT +@@ -1065,7 +1065,7 @@ int main (int argc, char **argv) + } else { + user_home = xstrdup(pwd->pw_dir); + } +- pw_close(); ++ pw_close(true); + } + #ifdef WITH_TCB + if (shadowtcb_set_user (user_name) == SHADOWTCB_FAILURE) { +diff --git a/src/usermod.c b/src/usermod.c +index a0f4d7dd..180b7da0 100644 +--- a/src/usermod.c ++++ b/src/usermod.c +@@ -1469,7 +1469,7 @@ process_flags(int argc, char **argv) + */ + static void close_files (void) + { +- if (pw_close () == 0) { ++ if (pw_close (true) == 0) { + fprintf (stderr, + _("%s: failure while writing changes to %s\n"), + Prog, pw_dbname ()); +-- +2.50.0 + + +From abffb267659bb1f9d49af5cba69c7a9ba2489e3d Mon Sep 17 00:00:00 2001 +From: Iker Pedrosa +Date: Fri, 25 Apr 2025 15:23:11 +0200 +Subject: [PATCH 03/15] 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 +--- + lib/shadowio.c | 4 ++-- + lib/shadowio.h | 2 +- + src/chage.c | 2 +- + src/chpasswd.c | 2 +- + src/newusers.c | 2 +- + src/passwd.c | 4 ++-- + src/pwck.c | 4 ++-- + src/pwconv.c | 2 +- + src/pwunconv.c | 2 +- + src/useradd.c | 2 +- + src/userdel.c | 2 +- + src/usermod.c | 2 +- + 12 files changed, 15 insertions(+), 15 deletions(-) + +diff --git a/lib/shadowio.c b/lib/shadowio.c +index 55d33da2..a109576d 100644 +--- a/lib/shadowio.c ++++ b/lib/shadowio.c +@@ -185,7 +185,7 @@ int spw_rewind (void) + return commonio_next (&shadow_db); + } + +-int spw_close (void) ++int spw_close (bool process_selinux) + { + int retval = 0; + #ifdef WITH_TCB +@@ -195,7 +195,7 @@ int spw_close (void) + return 0; + } + #endif /* WITH_TCB */ +- retval = commonio_close (&shadow_db, true); ++ retval = commonio_close (&shadow_db, process_selinux); + #ifdef WITH_TCB + if (use_tcb && (shadowtcb_gain_priv () == SHADOWTCB_FAILURE)) { + return 0; +diff --git a/lib/shadowio.h b/lib/shadowio.h +index 4dbeb6d6..e64d8d60 100644 +--- a/lib/shadowio.h ++++ b/lib/shadowio.h +@@ -13,7 +13,7 @@ + + #include "defines.h" + +-extern int spw_close (void); ++extern int spw_close (bool process_selinux); + extern bool spw_file_present (void); + extern /*@observer@*/ /*@null@*/const struct spwd *spw_locate (const char *name); + extern int spw_lock (void); +diff --git a/src/chage.c b/src/chage.c +index 1ebe34c5..a59428c5 100644 +--- a/src/chage.c ++++ b/src/chage.c +@@ -546,7 +546,7 @@ static void close_files (void) + * Now close the shadow password file, which will cause all of the + * entries to be re-written. + */ +- if (spw_close () == 0) { ++ if (spw_close (true) == 0) { + fprintf (stderr, + _("%s: failure while writing changes to %s\n"), Prog, spw_dbname ()); + SYSLOG ((LOG_ERR, "failure while writing changes to %s", spw_dbname ())); +diff --git a/src/chpasswd.c b/src/chpasswd.c +index 17f3d6d5..e3d8dcef 100644 +--- a/src/chpasswd.c ++++ b/src/chpasswd.c +@@ -378,7 +378,7 @@ static void open_files (void) + static void close_files (void) + { + if (is_shadow_pwd) { +- if (spw_close () == 0) { ++ if (spw_close (true) == 0) { + fprintf (stderr, + _("%s: failure while writing changes to %s\n"), + Prog, spw_dbname ()); +diff --git a/src/newusers.c b/src/newusers.c +index a738f944..43672dd2 100644 +--- a/src/newusers.c ++++ b/src/newusers.c +@@ -952,7 +952,7 @@ static void close_files (void) + pw_locked = false; + + if (is_shadow) { +- if (spw_close () == 0) { ++ if (spw_close (true) == 0) { + fprintf (stderr, + _("%s: failure while writing changes to %s\n"), + Prog, spw_dbname ()); +diff --git a/src/passwd.c b/src/passwd.c +index 8bdff6dd..01efb285 100644 +--- a/src/passwd.c ++++ b/src/passwd.c +@@ -625,7 +625,7 @@ static void update_shadow (void) + sp = spw_locate (name); + if (NULL == sp) { + /* Try to update the password in /etc/passwd instead. */ +- (void) spw_close (); ++ (void) spw_close (true); + update_noshadow (); + if (spw_unlock () == 0) { + (void) fprintf (stderr, +@@ -681,7 +681,7 @@ static void update_shadow (void) + Prog, spw_dbname (), nsp->sp_namp); + fail_exit (E_FAILURE); + } +- if (spw_close () == 0) { ++ if (spw_close (true) == 0) { + (void) fprintf (stderr, + _("%s: failure while writing changes to %s\n"), + Prog, spw_dbname ()); +diff --git a/src/pwck.c b/src/pwck.c +index b1c6970f..718b639f 100644 +--- a/src/pwck.c ++++ b/src/pwck.c +@@ -320,7 +320,7 @@ static void close_files (bool changed) + } + fail_exit (E_CANTUPDATE); + } +- if (spw_opened && (spw_close () == 0)) { ++ if (spw_opened && (spw_close (true) == 0)) { + fprintf (stderr, + _("%s: failure while writing changes to %s\n"), + Prog, spw_dbname ()); +@@ -657,7 +657,7 @@ static void check_pw_file (bool *errors, bool *changed) + } + #ifdef WITH_TCB + if (getdef_bool ("USE_TCB") && spw_locked) { +- if (spw_opened && (spw_close () == 0)) { ++ if (spw_opened && (spw_close (true) == 0)) { + fprintf (stderr, + _("%s: failure while writing changes to %s\n"), + Prog, spw_dbname ()); +diff --git a/src/pwconv.c b/src/pwconv.c +index da3a5820..296b527a 100644 +--- a/src/pwconv.c ++++ b/src/pwconv.c +@@ -274,7 +274,7 @@ int main (int argc, char **argv) + } + } + +- if (spw_close () == 0) { ++ if (spw_close (true) == 0) { + fprintf (stderr, + _("%s: failure while writing changes to %s\n"), + Prog, spw_dbname ()); +diff --git a/src/pwunconv.c b/src/pwunconv.c +index 8423cda4..e15c8c06 100644 +--- a/src/pwunconv.c ++++ b/src/pwunconv.c +@@ -205,7 +205,7 @@ int main (int argc, char **argv) + } + } + +- (void) spw_close (); /* was only open O_RDONLY */ ++ (void) spw_close (true); /* was only open O_RDONLY */ + + if (pw_close (true) == 0) { + fprintf (stderr, +diff --git a/src/useradd.c b/src/useradd.c +index 8c9e9003..8a7df6cf 100644 +--- a/src/useradd.c ++++ b/src/useradd.c +@@ -1569,7 +1569,7 @@ static void close_files (void) + SYSLOG ((LOG_ERR, "failure while writing changes to %s", pw_dbname ())); + fail_exit (E_PW_UPDATE); + } +- if (is_shadow_pwd && (spw_close () == 0)) { ++ if (is_shadow_pwd && (spw_close (true) == 0)) { + fprintf (stderr, + _("%s: failure while writing changes to %s\n"), Prog, spw_dbname ()); + SYSLOG ((LOG_ERR, "failure while writing changes to %s", spw_dbname ())); +diff --git a/src/userdel.c b/src/userdel.c +index b5a02b3c..b87f2459 100644 +--- a/src/userdel.c ++++ b/src/userdel.c +@@ -403,7 +403,7 @@ static void close_files (void) + pw_locked = false; + + if (is_shadow_pwd) { +- if (spw_close () == 0) { ++ if (spw_close (true) == 0) { + fprintf (stderr, + _("%s: failure while writing changes to %s\n"), Prog, spw_dbname ()); + SYSLOG ((LOG_ERR, "failure while writing changes to %s", spw_dbname ())); +diff --git a/src/usermod.c b/src/usermod.c +index 180b7da0..d741508a 100644 +--- a/src/usermod.c ++++ b/src/usermod.c +@@ -1476,7 +1476,7 @@ static void close_files (void) + SYSLOG ((LOG_ERR, "failure while writing changes to %s", pw_dbname ())); + fail_exit (E_PW_UPDATE); + } +- if (is_shadow_pwd && (spw_close () == 0)) { ++ if (is_shadow_pwd && (spw_close (true) == 0)) { + fprintf (stderr, + _("%s: failure while writing changes to %s\n"), + Prog, spw_dbname ()); +-- +2.50.0 + + +From 16bd1d985900fe83a1f7c9001fa3b9f18e0f06f0 Mon Sep 17 00:00:00 2001 +From: Iker Pedrosa +Date: Fri, 25 Apr 2025 15:28:42 +0200 +Subject: [PATCH 04/15] 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 +--- + lib/groupio.c | 4 ++-- + lib/groupio.h | 3 ++- + src/chgpasswd.c | 2 +- + src/gpasswd.c | 4 ++-- + src/groupadd.c | 2 +- + src/groupdel.c | 2 +- + src/groupmems.c | 2 +- + src/groupmod.c | 2 +- + src/grpck.c | 2 +- + src/grpconv.c | 2 +- + src/grpunconv.c | 2 +- + src/newusers.c | 2 +- + src/useradd.c | 2 +- + src/userdel.c | 2 +- + src/usermod.c | 2 +- + 15 files changed, 18 insertions(+), 17 deletions(-) + +diff --git a/lib/groupio.c b/lib/groupio.c +index 5076fd3d..837e4c47 100644 +--- a/lib/groupio.c ++++ b/lib/groupio.c +@@ -184,9 +184,9 @@ int gr_rewind (void) + return commonio_next (&group_db); + } + +-int gr_close (void) ++int gr_close (bool process_selinux) + { +- return commonio_close (&group_db, true); ++ return commonio_close (&group_db, process_selinux); + } + + int gr_unlock (void) +diff --git a/lib/groupio.h b/lib/groupio.h +index 2014de0c..ef9242d0 100644 +--- a/lib/groupio.h ++++ b/lib/groupio.h +@@ -14,8 +14,9 @@ + + #include + #include ++#include + +-extern int gr_close (void); ++extern int gr_close (bool process_selinux); + extern /*@observer@*/ /*@null@*/const struct group *gr_locate (const char *name); + extern /*@observer@*/ /*@null@*/const struct group *gr_locate_gid (gid_t gid); + extern int gr_lock (void); +diff --git a/src/chgpasswd.c b/src/chgpasswd.c +index e7759657..9b686ef0 100644 +--- a/src/chgpasswd.c ++++ b/src/chgpasswd.c +@@ -397,7 +397,7 @@ static void close_files (void) + } + #endif + +- if (gr_close () == 0) { ++ if (gr_close (true) == 0) { + fprintf (stderr, + _("%s: failure while writing changes to %s\n"), + Prog, gr_dbname ()); +diff --git a/src/gpasswd.c b/src/gpasswd.c +index 460bd14c..1013f004 100644 +--- a/src/gpasswd.c ++++ b/src/gpasswd.c +@@ -593,7 +593,7 @@ static void log_gpasswd_success_group (MAYBE_UNUSED void *arg) + */ + static void close_files (void) + { +- if (gr_close () == 0) { ++ if (gr_close (true) == 0) { + fprintf (stderr, + _("%s: failure while writing changes to %s\n"), + Prog, gr_dbname ()); +@@ -724,7 +724,7 @@ static void get_group (struct group *gr) + gr->gr_passwd = xstrdup (tmpgr->gr_passwd); + gr->gr_mem = dup_list (tmpgr->gr_mem); + +- if (gr_close () == 0) { ++ if (gr_close (true) == 0) { + fprintf (stderr, + _("%s: failure while closing read-only %s\n"), + Prog, gr_dbname ()); +diff --git a/src/groupadd.c b/src/groupadd.c +index a0d5adeb..d9a9d36c 100644 +--- a/src/groupadd.c ++++ b/src/groupadd.c +@@ -274,7 +274,7 @@ check_new_name(void) + static void close_files (void) + { + /* First, write the changes in the regular group database */ +- if (gr_close () == 0) { ++ if (gr_close (true) == 0) { + fprintf (stderr, + _("%s: failure while writing changes to %s\n"), + Prog, gr_dbname ()); +diff --git a/src/groupdel.c b/src/groupdel.c +index 3d289fef..c52a2ae7 100644 +--- a/src/groupdel.c ++++ b/src/groupdel.c +@@ -149,7 +149,7 @@ static void grp_update (void) + static void close_files (void) + { + /* First, write the changes in the regular group database */ +- if (gr_close () == 0) { ++ if (gr_close (true) == 0) { + fprintf (stderr, + _("%s: failure while writing changes to %s\n"), + Prog, gr_dbname ()); +diff --git a/src/groupmems.c b/src/groupmems.c +index d37b237f..fff9ae10 100644 +--- a/src/groupmems.c ++++ b/src/groupmems.c +@@ -522,7 +522,7 @@ static void open_files (void) + + static void close_files (void) + { +- if ((gr_close () == 0) && !list) { ++ if ((gr_close (true) == 0) && !list) { + fprintf (stderr, _("%s: failure while writing changes to %s\n"), Prog, gr_dbname ()); + SYSLOG ((LOG_ERR, "failure while writing changes to %s", gr_dbname ())); + fail_exit (EXIT_GROUP_FILE); +diff --git a/src/groupmod.c b/src/groupmod.c +index 14b7e746..49b68c55 100644 +--- a/src/groupmod.c ++++ b/src/groupmod.c +@@ -481,7 +481,7 @@ static void process_flags (int argc, char **argv) + */ + static void close_files (void) + { +- if (gr_close () == 0) { ++ if (gr_close (true) == 0) { + fprintf (stderr, + _("%s: failure while writing changes to %s\n"), + Prog, gr_dbname ()); +diff --git a/src/grpck.c b/src/grpck.c +index 5fcf6fad..db8f1e68 100644 +--- a/src/grpck.c ++++ b/src/grpck.c +@@ -321,7 +321,7 @@ static void close_files (bool changed) + * changes to the files. + */ + if (changed) { +- if (gr_close () == 0) { ++ if (gr_close (true) == 0) { + fprintf (stderr, _("%s: failure while writing changes to %s\n"), + Prog, grp_file); + fail_exit (E_CANT_UPDATE); +diff --git a/src/grpconv.c b/src/grpconv.c +index 1b31cb85..e7928a4a 100644 +--- a/src/grpconv.c ++++ b/src/grpconv.c +@@ -241,7 +241,7 @@ int main (int argc, char **argv) + SYSLOG ((LOG_ERR, "failure while writing changes to %s", sgr_dbname ())); + fail_exit (3); + } +- if (gr_close () == 0) { ++ if (gr_close (true) == 0) { + fprintf (stderr, + _("%s: failure while writing changes to %s\n"), + Prog, gr_dbname ()); +diff --git a/src/grpunconv.c b/src/grpunconv.c +index ea65a329..63597da1 100644 +--- a/src/grpunconv.c ++++ b/src/grpunconv.c +@@ -193,7 +193,7 @@ int main (int argc, char **argv) + + (void) sgr_close (); /* was only open O_RDONLY */ + +- if (gr_close () == 0) { ++ if (gr_close (true) == 0) { + fprintf (stderr, + _("%s: failure while writing changes to %s\n"), + Prog, gr_dbname ()); +diff --git a/src/newusers.c b/src/newusers.c +index 43672dd2..29d3a536 100644 +--- a/src/newusers.c ++++ b/src/newusers.c +@@ -969,7 +969,7 @@ static void close_files (void) + spw_locked = false; + } + +- if (gr_close () == 0) { ++ if (gr_close (true) == 0) { + fprintf (stderr, + _("%s: failure while writing changes to %s\n"), + Prog, gr_dbname ()); +diff --git a/src/useradd.c b/src/useradd.c +index 8a7df6cf..f52e5483 100644 +--- a/src/useradd.c ++++ b/src/useradd.c +@@ -1664,7 +1664,7 @@ static void close_group_files (void) + if (!do_grp_update) + return; + +- if (gr_close() == 0) { ++ if (gr_close(true) == 0) { + fprintf(stderr, + _("%s: failure while writing changes to %s\n"), + Prog, gr_dbname()); +diff --git a/src/userdel.c b/src/userdel.c +index b87f2459..b0e0d541 100644 +--- a/src/userdel.c ++++ b/src/userdel.c +@@ -417,7 +417,7 @@ static void close_files (void) + spw_locked = false; + } + +- if (gr_close () == 0) { ++ if (gr_close (true) == 0) { + fprintf (stderr, _("%s: failure while writing changes to %s\n"), Prog, gr_dbname ()); + SYSLOG ((LOG_ERR, "failure while writing changes to %s", gr_dbname ())); + fail_exit (E_GRP_UPDATE); +diff --git a/src/usermod.c b/src/usermod.c +index d741508a..d1d26eaa 100644 +--- a/src/usermod.c ++++ b/src/usermod.c +@@ -1487,7 +1487,7 @@ static void close_files (void) + } + + if (Gflg || lflg) { +- if (gr_close () == 0) { ++ if (gr_close (true) == 0) { + fprintf (stderr, + _("%s: failure while writing changes to %s\n"), + Prog, gr_dbname ()); +-- +2.50.0 + + +From f4a67dd6281a513b56c15aa1a3f292ca05674f13 Mon Sep 17 00:00:00 2001 +From: Iker Pedrosa +Date: Fri, 25 Apr 2025 15:32:05 +0200 +Subject: [PATCH 05/15] 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 +--- + lib/sgroupio.c | 4 ++-- + lib/sgroupio.h | 2 +- + src/chgpasswd.c | 2 +- + src/gpasswd.c | 4 ++-- + src/groupadd.c | 2 +- + src/groupdel.c | 2 +- + src/groupmems.c | 2 +- + src/groupmod.c | 2 +- + src/grpck.c | 2 +- + src/grpconv.c | 2 +- + src/grpunconv.c | 2 +- + src/newusers.c | 2 +- + src/useradd.c | 2 +- + src/userdel.c | 2 +- + src/usermod.c | 2 +- + 15 files changed, 17 insertions(+), 17 deletions(-) + +diff --git a/lib/sgroupio.c b/lib/sgroupio.c +index 659dcd06..025584ad 100644 +--- a/lib/sgroupio.c ++++ b/lib/sgroupio.c +@@ -276,9 +276,9 @@ int sgr_rewind (void) + return commonio_next (&gshadow_db); + } + +-int sgr_close (void) ++int sgr_close (bool process_selinux) + { +- return commonio_close (&gshadow_db, true); ++ return commonio_close (&gshadow_db, process_selinux); + } + + int sgr_unlock (void) +diff --git a/lib/sgroupio.h b/lib/sgroupio.h +index 3474a985..0e25fc0b 100644 +--- a/lib/sgroupio.h ++++ b/lib/sgroupio.h +@@ -12,7 +12,7 @@ + #ifndef _SGROUPIO_H + #define _SGROUPIO_H + +-extern int sgr_close (void); ++extern int sgr_close (bool process_selinux); + extern bool sgr_file_present (void); + extern /*@observer@*/ /*@null@*/const struct sgrp *sgr_locate (const char *name); + extern int sgr_lock (void); +diff --git a/src/chgpasswd.c b/src/chgpasswd.c +index 9b686ef0..47402ecc 100644 +--- a/src/chgpasswd.c ++++ b/src/chgpasswd.c +@@ -381,7 +381,7 @@ static void close_files (void) + { + #ifdef SHADOWGRP + if (is_shadow_grp) { +- if (sgr_close () == 0) { ++ if (sgr_close (true) == 0) { + fprintf (stderr, + _("%s: failure while writing changes to %s\n"), + Prog, sgr_dbname ()); +diff --git a/src/gpasswd.c b/src/gpasswd.c +index 1013f004..a49b9140 100644 +--- a/src/gpasswd.c ++++ b/src/gpasswd.c +@@ -607,7 +607,7 @@ static void close_files (void) + + #ifdef SHADOWGRP + if (is_shadowgrp) { +- if (sgr_close () == 0) { ++ if (sgr_close (true) == 0) { + fprintf (stderr, + _("%s: failure while writing changes to %s\n"), + Prog, sgr_dbname ()); +@@ -762,7 +762,7 @@ static void get_group (struct group *gr) + sg->sg_adm[0] = NULL; + + } +- if (sgr_close () == 0) { ++ if (sgr_close (true) == 0) { + fprintf (stderr, + _("%s: failure while closing read-only %s\n"), + Prog, sgr_dbname ()); +diff --git a/src/groupadd.c b/src/groupadd.c +index d9a9d36c..7cfb0eb5 100644 +--- a/src/groupadd.c ++++ b/src/groupadd.c +@@ -295,7 +295,7 @@ static void close_files (void) + /* Now, write the changes in the shadow database */ + #ifdef SHADOWGRP + if (is_shadow_grp) { +- if (sgr_close () == 0) { ++ if (sgr_close (true) == 0) { + fprintf (stderr, + _("%s: failure while writing changes to %s\n"), + Prog, sgr_dbname ()); +diff --git a/src/groupdel.c b/src/groupdel.c +index c52a2ae7..56123a7b 100644 +--- a/src/groupdel.c ++++ b/src/groupdel.c +@@ -173,7 +173,7 @@ static void close_files (void) + /* Then, write the changes in the shadow database */ + #ifdef SHADOWGRP + if (is_shadow_grp) { +- if (sgr_close () == 0) { ++ if (sgr_close (true) == 0) { + fprintf (stderr, + _("%s: failure while writing changes to %s\n"), + Prog, sgr_dbname ()); +diff --git a/src/groupmems.c b/src/groupmems.c +index fff9ae10..5976b074 100644 +--- a/src/groupmems.c ++++ b/src/groupmems.c +@@ -538,7 +538,7 @@ static void close_files (void) + + #ifdef SHADOWGRP + if (is_shadowgrp) { +- if ((sgr_close () == 0) && !list) { ++ if ((sgr_close (true) == 0) && !list) { + fprintf (stderr, _("%s: failure while writing changes to %s\n"), Prog, sgr_dbname ()); + SYSLOG ((LOG_ERR, "failure while writing changes to %s", sgr_dbname ())); + fail_exit (EXIT_GROUP_FILE); +diff --git a/src/groupmod.c b/src/groupmod.c +index 49b68c55..3c258feb 100644 +--- a/src/groupmod.c ++++ b/src/groupmod.c +@@ -504,7 +504,7 @@ static void close_files (void) + #ifdef SHADOWGRP + if ( is_shadow_grp + && (pflg || nflg || user_list)) { +- if (sgr_close () == 0) { ++ if (sgr_close (true) == 0) { + fprintf (stderr, + _("%s: failure while writing changes to %s\n"), + Prog, sgr_dbname ()); +diff --git a/src/grpck.c b/src/grpck.c +index db8f1e68..872f882f 100644 +--- a/src/grpck.c ++++ b/src/grpck.c +@@ -327,7 +327,7 @@ static void close_files (bool changed) + fail_exit (E_CANT_UPDATE); + } + #ifdef SHADOWGRP +- if (is_shadow && (sgr_close () == 0)) { ++ if (is_shadow && (sgr_close (true) == 0)) { + fprintf (stderr, _("%s: failure while writing changes to %s\n"), + Prog, sgr_file); + fail_exit (E_CANT_UPDATE); +diff --git a/src/grpconv.c b/src/grpconv.c +index e7928a4a..d9c59fc9 100644 +--- a/src/grpconv.c ++++ b/src/grpconv.c +@@ -234,7 +234,7 @@ int main (int argc, char **argv) + } + } + +- if (sgr_close () == 0) { ++ if (sgr_close (true) == 0) { + fprintf (stderr, + _("%s: failure while writing changes to %s\n"), + Prog, sgr_dbname ()); +diff --git a/src/grpunconv.c b/src/grpunconv.c +index 63597da1..03fc824e 100644 +--- a/src/grpunconv.c ++++ b/src/grpunconv.c +@@ -191,7 +191,7 @@ int main (int argc, char **argv) + } + } + +- (void) sgr_close (); /* was only open O_RDONLY */ ++ (void) sgr_close (true); /* was only open O_RDONLY */ + + if (gr_close (true) == 0) { + fprintf (stderr, +diff --git a/src/newusers.c b/src/newusers.c +index 29d3a536..19a32863 100644 +--- a/src/newusers.c ++++ b/src/newusers.c +@@ -1002,7 +1002,7 @@ static void close_files (void) + + #ifdef SHADOWGRP + if (is_shadow_grp) { +- if (sgr_close () == 0) { ++ if (sgr_close (true) == 0) { + fprintf (stderr, + _("%s: failure while writing changes to %s\n"), + Prog, sgr_dbname ()); +diff --git a/src/useradd.c b/src/useradd.c +index f52e5483..7f596673 100644 +--- a/src/useradd.c ++++ b/src/useradd.c +@@ -1672,7 +1672,7 @@ static void close_group_files (void) + fail_exit(E_GRP_UPDATE); + } + #ifdef SHADOWGRP +- if (is_shadow_grp && sgr_close() == 0) { ++ if (is_shadow_grp && sgr_close(true) == 0) { + fprintf(stderr, + _("%s: failure while writing changes to %s\n"), + Prog, sgr_dbname()); +diff --git a/src/userdel.c b/src/userdel.c +index b0e0d541..1829ba4d 100644 +--- a/src/userdel.c ++++ b/src/userdel.c +@@ -431,7 +431,7 @@ static void close_files (void) + + #ifdef SHADOWGRP + if (is_shadow_grp) { +- if (sgr_close () == 0) { ++ if (sgr_close (true) == 0) { + fprintf (stderr, + _("%s: failure while writing changes to %s\n"), Prog, sgr_dbname ()); + SYSLOG ((LOG_ERR, "failure while writing changes to %s", sgr_dbname ())); +diff --git a/src/usermod.c b/src/usermod.c +index d1d26eaa..dbcb32b2 100644 +--- a/src/usermod.c ++++ b/src/usermod.c +@@ -1498,7 +1498,7 @@ static void close_files (void) + } + #ifdef SHADOWGRP + if (is_shadow_grp) { +- if (sgr_close () == 0) { ++ if (sgr_close (true) == 0) { + fprintf (stderr, + _("%s: failure while writing changes to %s\n"), + Prog, sgr_dbname ()); +-- +2.50.0 + + +From f3dce7ede9c3f267e810fec3500498e76a305d8a Mon Sep 17 00:00:00 2001 +From: Iker Pedrosa +Date: Fri, 25 Apr 2025 15:35:48 +0200 +Subject: [PATCH 06/15] 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 +--- + lib/subordinateio.c | 14 +++++++------- + lib/subordinateio.h | 2 +- + lib/user_busy.c | 10 +++++----- + src/newuidmap.c | 2 +- + src/newusers.c | 2 +- + src/useradd.c | 2 +- + src/userdel.c | 2 +- + src/usermod.c | 2 +- + 8 files changed, 18 insertions(+), 18 deletions(-) + +diff --git a/lib/subordinateio.c b/lib/subordinateio.c +index 3638ec61..206a1348 100644 +--- a/lib/subordinateio.c ++++ b/lib/subordinateio.c +@@ -590,7 +590,7 @@ static bool have_range(struct commonio_db *db, + + if (doclose) { + if (db == &subordinate_uid_db) +- sub_uid_close(); ++ sub_uid_close(true); + else + sub_gid_close(); + } +@@ -668,9 +668,9 @@ int sub_uid_remove (const char *owner, uid_t start, unsigned long count) + return remove_range (&subordinate_uid_db, owner, start, count); + } + +-int sub_uid_close (void) ++int sub_uid_close (bool process_selinux) + { +- return commonio_close (&subordinate_uid_db, true); ++ return commonio_close (&subordinate_uid_db, process_selinux); + } + + int sub_uid_unlock (void) +@@ -907,7 +907,7 @@ int list_owner_ranges(const char *owner, enum subid_type id_type, struct subid_r + + out: + if (id_type == ID_TYPE_UID) +- sub_uid_close(); ++ sub_uid_close(true); + else + sub_gid_close(); + +@@ -998,7 +998,7 @@ int find_subid_owners(unsigned long id, enum subid_type id_type, uid_t **uids) + } + + if (id_type == ID_TYPE_UID) +- sub_uid_close(); ++ sub_uid_close(true); + else + sub_gid_close(); + +@@ -1068,7 +1068,7 @@ bool new_subid_range(struct subordinate_range *range, enum subid_type id_type, b + + out: + if (id_type == ID_TYPE_UID) { +- sub_uid_close(); ++ sub_uid_close(true); + sub_uid_unlock(); + } else { + sub_gid_close(); +@@ -1118,7 +1118,7 @@ bool release_subid_range(struct subordinate_range *range, enum subid_type id_typ + ret = remove_range(db, range->owner, range->start, range->count) == 1; + + if (id_type == ID_TYPE_UID) { +- sub_uid_close(); ++ sub_uid_close(true); + sub_uid_unlock(); + } else { + sub_gid_close(); +diff --git a/lib/subordinateio.h b/lib/subordinateio.h +index 3d1a0547..d16c8b9e 100644 +--- a/lib/subordinateio.h ++++ b/lib/subordinateio.h +@@ -13,7 +13,7 @@ + + #include "../libsubid/subid.h" + +-extern int sub_uid_close(void); ++extern int sub_uid_close(bool process_selinux); + extern bool have_sub_uids(const char *owner, uid_t start, unsigned long count); + extern bool sub_uid_file_present (void); + extern bool local_sub_uid_assigned(const char *owner); +diff --git a/lib/user_busy.c b/lib/user_busy.c +index d689d34d..ab1dd5a4 100644 +--- a/lib/user_busy.c ++++ b/lib/user_busy.c +@@ -180,7 +180,7 @@ static int user_busy_processes (const char *name, uid_t uid) + if (proc == NULL) { + perror ("opendir /proc"); + #ifdef ENABLE_SUBIDS +- sub_uid_close(); ++ sub_uid_close(true); + #endif + return 0; + } +@@ -188,7 +188,7 @@ static int user_busy_processes (const char *name, uid_t uid) + perror ("stat (\"/\")"); + (void) closedir (proc); + #ifdef ENABLE_SUBIDS +- sub_uid_close(); ++ sub_uid_close(true); + #endif + return 0; + } +@@ -225,7 +225,7 @@ static int user_busy_processes (const char *name, uid_t uid) + if (check_status (name, tmp_d_name, uid) != 0) { + (void) closedir (proc); + #ifdef ENABLE_SUBIDS +- sub_uid_close(); ++ sub_uid_close(true); + #endif + fprintf (log_get_logfd(), + _("%s: user %s is currently used by process %d\n"), +@@ -248,7 +248,7 @@ static int user_busy_processes (const char *name, uid_t uid) + (void) closedir (proc); + (void) closedir (task_dir); + #ifdef ENABLE_SUBIDS +- sub_uid_close(); ++ sub_uid_close(true); + #endif + fprintf (log_get_logfd(), + _("%s: user %s is currently used by process %d\n"), +@@ -264,7 +264,7 @@ static int user_busy_processes (const char *name, uid_t uid) + + (void) closedir (proc); + #ifdef ENABLE_SUBIDS +- sub_uid_close(); ++ sub_uid_close(true); + #endif /* ENABLE_SUBIDS */ + return 0; + } +diff --git a/src/newuidmap.c b/src/newuidmap.c +index 894c5ec8..90bd6de5 100644 +--- a/src/newuidmap.c ++++ b/src/newuidmap.c +@@ -158,7 +158,7 @@ int main(int argc, char **argv) + verify_ranges(pw, ranges, mappings); + + write_mapping(proc_dir_fd, ranges, mappings, "uid_map", pw->pw_uid); +- sub_uid_close(); ++ sub_uid_close(true); + + return EXIT_SUCCESS; + } +diff --git a/src/newusers.c b/src/newusers.c +index 19a32863..d71a792b 100644 +--- a/src/newusers.c ++++ b/src/newusers.c +@@ -977,7 +977,7 @@ static void close_files (void) + fail_exit (EXIT_FAILURE); + } + #ifdef ENABLE_SUBIDS +- if (is_sub_uid && (sub_uid_close () == 0)) { ++ if (is_sub_uid && (sub_uid_close (true) == 0)) { + fprintf (stderr, + _("%s: failure while writing changes to %s\n"), Prog, sub_uid_dbname ()); + SYSLOG ((LOG_ERR, "failure while writing changes to %s", sub_uid_dbname ())); +diff --git a/src/useradd.c b/src/useradd.c +index 7f596673..971fec55 100644 +--- a/src/useradd.c ++++ b/src/useradd.c +@@ -1579,7 +1579,7 @@ static void close_files (void) + close_group_files (); + + #ifdef ENABLE_SUBIDS +- if (is_sub_uid && (sub_uid_close () == 0)) { ++ if (is_sub_uid && (sub_uid_close (true) == 0)) { + fprintf (stderr, + _("%s: failure while writing changes to %s\n"), Prog, sub_uid_dbname ()); + SYSLOG ((LOG_ERR, "failure while writing changes to %s", sub_uid_dbname ())); +diff --git a/src/userdel.c b/src/userdel.c +index 1829ba4d..8fe9f4e2 100644 +--- a/src/userdel.c ++++ b/src/userdel.c +@@ -449,7 +449,7 @@ static void close_files (void) + + #ifdef ENABLE_SUBIDS + if (is_sub_uid) { +- if (sub_uid_close () == 0) { ++ if (sub_uid_close (true) == 0) { + fprintf (stderr, _("%s: failure while writing changes to %s\n"), Prog, sub_uid_dbname ()); + SYSLOG ((LOG_ERR, "failure while writing changes to %s", sub_uid_dbname ())); + fail_exit (E_SUB_UID_UPDATE); +diff --git a/src/usermod.c b/src/usermod.c +index dbcb32b2..440c2406 100644 +--- a/src/usermod.c ++++ b/src/usermod.c +@@ -1561,7 +1561,7 @@ static void close_files (void) + + #ifdef ENABLE_SUBIDS + if (vflg || Vflg) { +- if (sub_uid_close () == 0) { ++ if (sub_uid_close (true) == 0) { + fprintf (stderr, _("%s: failure while writing changes to %s\n"), Prog, sub_uid_dbname ()); + SYSLOG ((LOG_ERR, "failure while writing changes to %s", sub_uid_dbname ())); + fail_exit (E_SUB_UID_UPDATE); +-- +2.50.0 + + +From ab2a2656e442043d5b39e1320552ba9850aabd27 Mon Sep 17 00:00:00 2001 +From: Iker Pedrosa +Date: Fri, 25 Apr 2025 15:38:18 +0200 +Subject: [PATCH 07/15] 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 +--- + lib/subordinateio.c | 14 +++++++------- + lib/subordinateio.h | 2 +- + src/newgidmap.c | 2 +- + src/newusers.c | 2 +- + src/useradd.c | 2 +- + src/userdel.c | 2 +- + src/usermod.c | 2 +- + 7 files changed, 13 insertions(+), 13 deletions(-) + +diff --git a/lib/subordinateio.c b/lib/subordinateio.c +index 206a1348..e2c2375a 100644 +--- a/lib/subordinateio.c ++++ b/lib/subordinateio.c +@@ -592,7 +592,7 @@ static bool have_range(struct commonio_db *db, + if (db == &subordinate_uid_db) + sub_uid_close(true); + else +- sub_gid_close(); ++ sub_gid_close(true); + } + + return ret; +@@ -775,9 +775,9 @@ int sub_gid_remove (const char *owner, gid_t start, unsigned long count) + return remove_range (&subordinate_gid_db, owner, start, count); + } + +-int sub_gid_close (void) ++int sub_gid_close (bool process_selinux) + { +- return commonio_close (&subordinate_gid_db, true); ++ return commonio_close (&subordinate_gid_db, process_selinux); + } + + int sub_gid_unlock (void) +@@ -909,7 +909,7 @@ out: + if (id_type == ID_TYPE_UID) + sub_uid_close(true); + else +- sub_gid_close(); ++ sub_gid_close(true); + + *in_ranges = ranges; + return count; +@@ -1000,7 +1000,7 @@ int find_subid_owners(unsigned long id, enum subid_type id_type, uid_t **uids) + if (id_type == ID_TYPE_UID) + sub_uid_close(true); + else +- sub_gid_close(); ++ sub_gid_close(true); + + return n; + } +@@ -1071,7 +1071,7 @@ out: + sub_uid_close(true); + sub_uid_unlock(); + } else { +- sub_gid_close(); ++ sub_gid_close(true); + sub_gid_unlock(); + } + +@@ -1121,7 +1121,7 @@ bool release_subid_range(struct subordinate_range *range, enum subid_type id_typ + sub_uid_close(true); + sub_uid_unlock(); + } else { +- sub_gid_close(); ++ sub_gid_close(true); + sub_gid_unlock(); + } + +diff --git a/lib/subordinateio.h b/lib/subordinateio.h +index d16c8b9e..63f5b1fb 100644 +--- a/lib/subordinateio.h ++++ b/lib/subordinateio.h +@@ -31,7 +31,7 @@ extern bool release_subid_range(struct subordinate_range *range, enum subid_type + extern int find_subid_owners(unsigned long id, enum subid_type id_type, uid_t **uids); + extern void free_subordinate_ranges(struct subordinate_range **ranges, int count); + +-extern int sub_gid_close(void); ++extern int sub_gid_close(bool process_selinux); + extern bool have_sub_gids(const char *owner, gid_t start, unsigned long count); + extern bool sub_gid_file_present (void); + extern bool local_sub_gid_assigned(const char *owner); +diff --git a/src/newgidmap.c b/src/newgidmap.c +index 3463e850..7ce22b5a 100644 +--- a/src/newgidmap.c ++++ b/src/newgidmap.c +@@ -230,7 +230,7 @@ int main(int argc, char **argv) + + write_setgroups(proc_dir_fd, allow_setgroups); + write_mapping(proc_dir_fd, ranges, mappings, "gid_map", pw->pw_uid); +- sub_gid_close(); ++ sub_gid_close(true); + + return EXIT_SUCCESS; + } +diff --git a/src/newusers.c b/src/newusers.c +index d71a792b..8cd11a71 100644 +--- a/src/newusers.c ++++ b/src/newusers.c +@@ -983,7 +983,7 @@ static void close_files (void) + SYSLOG ((LOG_ERR, "failure while writing changes to %s", sub_uid_dbname ())); + fail_exit (EXIT_FAILURE); + } +- if (is_sub_gid && (sub_gid_close () == 0)) { ++ if (is_sub_gid && (sub_gid_close (true) == 0)) { + fprintf (stderr, + _("%s: failure while writing changes to %s\n"), Prog, sub_gid_dbname ()); + SYSLOG ((LOG_ERR, "failure while writing changes to %s", sub_gid_dbname ())); +diff --git a/src/useradd.c b/src/useradd.c +index 971fec55..3d2fdb35 100644 +--- a/src/useradd.c ++++ b/src/useradd.c +@@ -1585,7 +1585,7 @@ static void close_files (void) + SYSLOG ((LOG_ERR, "failure while writing changes to %s", sub_uid_dbname ())); + fail_exit (E_SUB_UID_UPDATE); + } +- if (is_sub_gid && (sub_gid_close () == 0)) { ++ if (is_sub_gid && (sub_gid_close (true) == 0)) { + fprintf (stderr, + _("%s: failure while writing changes to %s\n"), Prog, sub_gid_dbname ()); + SYSLOG ((LOG_ERR, "failure while writing changes to %s", sub_gid_dbname ())); +diff --git a/src/userdel.c b/src/userdel.c +index 8fe9f4e2..7dcbd923 100644 +--- a/src/userdel.c ++++ b/src/userdel.c +@@ -463,7 +463,7 @@ static void close_files (void) + } + + if (is_sub_gid) { +- if (sub_gid_close () == 0) { ++ if (sub_gid_close (true) == 0) { + fprintf (stderr, _("%s: failure while writing changes to %s\n"), Prog, sub_gid_dbname ()); + SYSLOG ((LOG_ERR, "failure while writing changes to %s", sub_gid_dbname ())); + fail_exit (E_SUB_GID_UPDATE); +diff --git a/src/usermod.c b/src/usermod.c +index 440c2406..534bdc74 100644 +--- a/src/usermod.c ++++ b/src/usermod.c +@@ -1574,7 +1574,7 @@ static void close_files (void) + sub_uid_locked = false; + } + if (wflg || Wflg) { +- if (sub_gid_close () == 0) { ++ if (sub_gid_close (true) == 0) { + fprintf (stderr, _("%s: failure while writing changes to %s\n"), Prog, sub_gid_dbname ()); + SYSLOG ((LOG_ERR, "failure while writing changes to %s", sub_gid_dbname ())); + fail_exit (E_SUB_GID_UPDATE); +-- +2.50.0 + + +From 9161c63be728bfba7545bac1e4cbde8a069ad4e6 Mon Sep 17 00:00:00 2001 +From: Iker Pedrosa +Date: Fri, 25 Apr 2025 15:44:06 +0200 +Subject: [PATCH 08/15] 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 +--- + lib/commonio.c | 4 ++-- + lib/commonio.h | 2 +- + lib/groupio.c | 2 +- + lib/pwio.c | 2 +- + lib/sgroupio.c | 2 +- + lib/shadowio.c | 2 +- + lib/subordinateio.c | 4 ++-- + 7 files changed, 9 insertions(+), 9 deletions(-) + +diff --git a/lib/commonio.c b/lib/commonio.c +index 7ee6df4d..19653e7e 100644 +--- a/lib/commonio.c ++++ b/lib/commonio.c +@@ -467,13 +467,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, true) == 0) { ++ if (commonio_close (db, process_selinux) == 0) { + if (db->locked) { + dec_lock_count (); + } +diff --git a/lib/commonio.h b/lib/commonio.h +index dadc0500..8b8f5cf4 100644 +--- a/lib/commonio.h ++++ b/lib/commonio.h +@@ -139,7 +139,7 @@ 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 *, bool); +-extern int commonio_unlock (struct commonio_db *); ++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, +diff --git a/lib/groupio.c b/lib/groupio.c +index 837e4c47..6ad4ae69 100644 +--- a/lib/groupio.c ++++ b/lib/groupio.c +@@ -191,7 +191,7 @@ int gr_close (bool process_selinux) + + int gr_unlock (void) + { +- return commonio_unlock (&group_db); ++ return commonio_unlock (&group_db, true); + } + + void __gr_set_changed (void) +diff --git a/lib/pwio.c b/lib/pwio.c +index c5c1cdb1..c5dcb904 100644 +--- a/lib/pwio.c ++++ b/lib/pwio.c +@@ -163,7 +163,7 @@ int pw_close (bool process_selinux) + + int pw_unlock (void) + { +- return commonio_unlock (&passwd_db); ++ return commonio_unlock (&passwd_db, true); + } + + /*@null@*/struct commonio_entry *__pw_get_head (void) +diff --git a/lib/sgroupio.c b/lib/sgroupio.c +index 025584ad..dbabcc36 100644 +--- a/lib/sgroupio.c ++++ b/lib/sgroupio.c +@@ -283,7 +283,7 @@ int sgr_close (bool process_selinux) + + int sgr_unlock (void) + { +- return commonio_unlock (&gshadow_db); ++ return commonio_unlock (&gshadow_db, true); + } + + void __sgr_set_changed (void) +diff --git a/lib/shadowio.c b/lib/shadowio.c +index a109576d..9866af6d 100644 +--- a/lib/shadowio.c ++++ b/lib/shadowio.c +@@ -211,7 +211,7 @@ int spw_unlock (void) + + if (!getdef_bool ("USE_TCB")) { + #endif /* WITH_TCB */ +- return commonio_unlock (&shadow_db); ++ return commonio_unlock (&shadow_db, true); + #ifdef WITH_TCB + } + if (shadowtcb_drop_priv () == SHADOWTCB_FAILURE) { +diff --git a/lib/subordinateio.c b/lib/subordinateio.c +index e2c2375a..9587e1a9 100644 +--- a/lib/subordinateio.c ++++ b/lib/subordinateio.c +@@ -675,7 +675,7 @@ int sub_uid_close (bool process_selinux) + + int sub_uid_unlock (void) + { +- return commonio_unlock (&subordinate_uid_db); ++ return commonio_unlock (&subordinate_uid_db, true); + } + + uid_t sub_uid_find_free_range(uid_t min, uid_t max, unsigned long count) +@@ -782,7 +782,7 @@ int sub_gid_close (bool process_selinux) + + int sub_gid_unlock (void) + { +- return commonio_unlock (&subordinate_gid_db); ++ return commonio_unlock (&subordinate_gid_db, true); + } + + gid_t sub_gid_find_free_range(gid_t min, gid_t max, unsigned long count) +-- +2.50.0 + + +From 9609c3ba7d4097c2ff63ee6ccfa6b74c41fbde5e Mon Sep 17 00:00:00 2001 +From: Iker Pedrosa +Date: Fri, 25 Apr 2025 15:53:17 +0200 +Subject: [PATCH 09/15] 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 +--- + lib/cleanup_user.c | 2 +- + lib/lockpw.c | 4 ++-- + lib/pwio.c | 4 ++-- + lib/pwio.h | 2 +- + src/chage.c | 4 ++-- + src/chfn.c | 4 ++-- + src/chpasswd.c | 4 ++-- + src/chsh.c | 4 ++-- + src/newusers.c | 4 ++-- + src/passwd.c | 4 ++-- + src/pwck.c | 4 ++-- + src/pwconv.c | 4 ++-- + src/pwunconv.c | 4 ++-- + src/useradd.c | 4 ++-- + src/userdel.c | 4 ++-- + src/usermod.c | 4 ++-- + 16 files changed, 30 insertions(+), 30 deletions(-) + +diff --git a/lib/cleanup_user.c b/lib/cleanup_user.c +index 809caaf3..a661f5e9 100644 +--- a/lib/cleanup_user.c ++++ b/lib/cleanup_user.c +@@ -98,7 +98,7 @@ void cleanup_report_add_user_shadow (void *user_name) + */ + void cleanup_unlock_passwd (MAYBE_UNUSED void *arg) + { +- if (pw_unlock () == 0) { ++ if (pw_unlock (true) == 0) { + fprintf (log_get_logfd(), + _("%s: failed to unlock %s\n"), + log_get_progname(), pw_dbname ()); +diff --git a/lib/lockpw.c b/lib/lockpw.c +index 15e7c8f4..f73c434a 100644 +--- a/lib/lockpw.c ++++ b/lib/lockpw.c +@@ -56,7 +56,7 @@ int lckpwdf (void) + */ + + if (i == 15) { +- pw_unlock (); ++ pw_unlock (true); + return -1; + } + +@@ -78,7 +78,7 @@ int ulckpwdf (void) + * Unlock both files. + */ + +- return (pw_unlock () && spw_unlock ())? 0 : -1; ++ return (pw_unlock (true) && spw_unlock ())? 0 : -1; + } + #else + extern int ISO_C_forbids_an_empty_translation_unit; +diff --git a/lib/pwio.c b/lib/pwio.c +index c5dcb904..78146c37 100644 +--- a/lib/pwio.c ++++ b/lib/pwio.c +@@ -161,9 +161,9 @@ int pw_close (bool process_selinux) + return commonio_close (&passwd_db, process_selinux); + } + +-int pw_unlock (void) ++int pw_unlock (bool process_selinux) + { +- return commonio_unlock (&passwd_db, true); ++ return commonio_unlock (&passwd_db, process_selinux); + } + + /*@null@*/struct commonio_entry *__pw_get_head (void) +diff --git a/lib/pwio.h b/lib/pwio.h +index b051a7e7..0903b8bd 100644 +--- a/lib/pwio.h ++++ b/lib/pwio.h +@@ -26,7 +26,7 @@ extern /*@observer@*/ /*@null@*/const struct passwd *pw_next (void); + extern int pw_open (int mode); + extern int pw_remove (const char *name); + extern int pw_rewind (void); +-extern int pw_unlock (void); ++extern int pw_unlock (bool process_selinux); + extern int pw_update (const struct passwd *pw); + extern int pw_sort (void); + +diff --git a/src/chage.c b/src/chage.c +index a59428c5..d3a42ccf 100644 +--- a/src/chage.c ++++ b/src/chage.c +@@ -101,7 +101,7 @@ fail_exit (int code) + } + } + if (pw_locked) { +- if (pw_unlock () == 0) { ++ if (pw_unlock (true) == 0) { + fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, pw_dbname ()); + SYSLOG ((LOG_ERR, "failed to unlock %s", pw_dbname ())); + /* continue */ +@@ -568,7 +568,7 @@ static void close_files (void) + /* continue */ + } + spw_locked = false; +- if (pw_unlock () == 0) { ++ if (pw_unlock (true) == 0) { + fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, pw_dbname ()); + SYSLOG ((LOG_ERR, "failed to unlock %s", pw_dbname ())); + /* continue */ +diff --git a/src/chfn.c b/src/chfn.c +index abf88cc8..dca81486 100644 +--- a/src/chfn.c ++++ b/src/chfn.c +@@ -79,7 +79,7 @@ static void get_old_fields (const char *gecos); + static void fail_exit (int code) + { + if (pw_locked) { +- if (pw_unlock () == 0) { ++ if (pw_unlock (true) == 0) { + fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, pw_dbname ()); + SYSLOG ((LOG_ERR, "failed to unlock %s", pw_dbname ())); + /* continue */ +@@ -498,7 +498,7 @@ static void update_gecos (const char *user, char *gecos) + SYSLOG ((LOG_ERR, "failure while writing changes to %s", pw_dbname ())); + fail_exit (E_NOPERM); + } +- if (pw_unlock () == 0) { ++ if (pw_unlock (true) == 0) { + fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, pw_dbname ()); + SYSLOG ((LOG_ERR, "failed to unlock %s", pw_dbname ())); + /* continue */ +diff --git a/src/chpasswd.c b/src/chpasswd.c +index e3d8dcef..5323c520 100644 +--- a/src/chpasswd.c ++++ b/src/chpasswd.c +@@ -81,7 +81,7 @@ static void close_files (void); + static void fail_exit (int code) + { + if (pw_locked) { +- if (pw_unlock () == 0) { ++ if (pw_unlock (true) == 0) { + fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, pw_dbname ()); + SYSLOG ((LOG_ERR, "failed to unlock %s", pw_dbname ())); + /* continue */ +@@ -400,7 +400,7 @@ static void close_files (void) + SYSLOG ((LOG_ERR, "failure while writing changes to %s", pw_dbname ())); + fail_exit (1); + } +- if (pw_unlock () == 0) { ++ if (pw_unlock (true) == 0) { + fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, pw_dbname ()); + SYSLOG ((LOG_ERR, "failed to unlock %s", pw_dbname ())); + /* continue */ +diff --git a/src/chsh.c b/src/chsh.c +index b5ae28da..4403d412 100644 +--- a/src/chsh.c ++++ b/src/chsh.c +@@ -78,7 +78,7 @@ static void + fail_exit (int code) + { + if (pw_locked) { +- if (pw_unlock () == 0) { ++ if (pw_unlock (true) == 0) { + fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, pw_dbname ()); + SYSLOG ((LOG_ERR, "failed to unlock %s", pw_dbname ())); + /* continue */ +@@ -433,7 +433,7 @@ static void update_shell (const char *user, char *newshell) + SYSLOG ((LOG_ERR, "failure while writing changes to %s", pw_dbname ())); + fail_exit (1); + } +- if (pw_unlock () == 0) { ++ if (pw_unlock (true) == 0) { + fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, pw_dbname ()); + SYSLOG ((LOG_ERR, "failed to unlock %s", pw_dbname ())); + /* continue */ +diff --git a/src/newusers.c b/src/newusers.c +index 8cd11a71..669b6236 100644 +--- a/src/newusers.c ++++ b/src/newusers.c +@@ -172,7 +172,7 @@ static void fail_exit (int code) + } + } + if (pw_locked) { +- if (pw_unlock () == 0) { ++ if (pw_unlock (true) == 0) { + fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, pw_dbname ()); + SYSLOG ((LOG_ERR, "failed to unlock %s", pw_dbname ())); + /* continue */ +@@ -944,7 +944,7 @@ static void close_files (void) + SYSLOG ((LOG_ERR, "failure while writing changes to %s", pw_dbname ())); + fail_exit (EXIT_FAILURE); + } +- if (pw_unlock () == 0) { ++ if (pw_unlock (true) == 0) { + fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, pw_dbname ()); + SYSLOG ((LOG_ERR, "failed to unlock %s", pw_dbname ())); + /* continue */ +diff --git a/src/passwd.c b/src/passwd.c +index 01efb285..8a55d797 100644 +--- a/src/passwd.c ++++ b/src/passwd.c +@@ -483,7 +483,7 @@ static void + fail_exit (int status) + { + if (pw_locked) { +- if (pw_unlock () == 0) { ++ if (pw_unlock (true) == 0) { + (void) fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, pw_dbname ()); + SYSLOG ((LOG_ERR, "failed to unlock %s", pw_dbname ())); + /* continue */ +@@ -593,7 +593,7 @@ static void update_noshadow (void) + SYSLOG ((LOG_ERR, "failure while writing changes to %s", pw_dbname ())); + fail_exit (E_FAILURE); + } +- if (pw_unlock () == 0) { ++ if (pw_unlock (true) == 0) { + (void) fprintf (stderr, + _("%s: failed to unlock %s\n"), + Prog, pw_dbname ()); +diff --git a/src/pwck.c b/src/pwck.c +index 718b639f..f30c5289 100644 +--- a/src/pwck.c ++++ b/src/pwck.c +@@ -96,7 +96,7 @@ static void fail_exit (int code) + } + + if (pw_locked) { +- if (pw_unlock () == 0) { ++ if (pw_unlock (true) == 0) { + fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, pw_dbname ()); + if (use_system_pw_file) { + SYSLOG ((LOG_ERR, "failed to unlock %s", +@@ -351,7 +351,7 @@ static void close_files (bool changed) + } + spw_locked = false; + if (pw_locked) { +- if (pw_unlock () == 0) { ++ if (pw_unlock (true) == 0) { + fprintf (stderr, + _("%s: failed to unlock %s\n"), + Prog, pw_dbname ()); +diff --git a/src/pwconv.c b/src/pwconv.c +index 296b527a..82181bb4 100644 +--- a/src/pwconv.c ++++ b/src/pwconv.c +@@ -83,7 +83,7 @@ static void process_flags (int argc, char **argv); + static void fail_exit (int status) + { + if (pw_locked) { +- if (pw_unlock () == 0) { ++ if (pw_unlock (true) == 0) { + fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, pw_dbname ()); + SYSLOG ((LOG_ERR, "failed to unlock %s", pw_dbname ())); + /* continue */ +@@ -299,7 +299,7 @@ int main (int argc, char **argv) + /* continue */ + } + +- if (pw_unlock () == 0) { ++ if (pw_unlock (true) == 0) { + fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, pw_dbname ()); + SYSLOG ((LOG_ERR, "failed to unlock %s", pw_dbname ())); + /* continue */ +diff --git a/src/pwunconv.c b/src/pwunconv.c +index e15c8c06..72944fe5 100644 +--- a/src/pwunconv.c ++++ b/src/pwunconv.c +@@ -54,7 +54,7 @@ static void fail_exit (int status) + } + } + if (pw_locked) { +- if (pw_unlock () == 0) { ++ if (pw_unlock (true) == 0) { + fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, pw_dbname ()); + SYSLOG ((LOG_ERR, "failed to unlock %s", pw_dbname ())); + /* continue */ +@@ -227,7 +227,7 @@ int main (int argc, char **argv) + SYSLOG ((LOG_ERR, "failed to unlock %s", spw_dbname ())); + /* continue */ + } +- if (pw_unlock () == 0) { ++ if (pw_unlock (true) == 0) { + fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, pw_dbname ()); + SYSLOG ((LOG_ERR, "failed to unlock %s", pw_dbname ())); + /* continue */ +diff --git a/src/useradd.c b/src/useradd.c +index 3d2fdb35..aa070f2d 100644 +--- a/src/useradd.c ++++ b/src/useradd.c +@@ -270,7 +270,7 @@ static void fail_exit (int code) + SYSLOG((LOG_ERR, "failed to unlock %s", spw_dbname())); + /* continue */ + } +- if (pw_locked && pw_unlock() == 0) { ++ if (pw_locked && pw_unlock(true) == 0) { + fprintf(stderr, _("%s: failed to unlock %s\n"), Prog, pw_dbname()); + SYSLOG((LOG_ERR, "failed to unlock %s", pw_dbname())); + /* continue */ +@@ -1606,7 +1606,7 @@ static void close_files (void) + } + spw_locked = false; + } +- if (pw_unlock () == 0) { ++ if (pw_unlock (true) == 0) { + fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, pw_dbname ()); + SYSLOG ((LOG_ERR, "failed to unlock %s", pw_dbname ())); + #ifdef WITH_AUDIT +diff --git a/src/userdel.c b/src/userdel.c +index 7dcbd923..b4e87820 100644 +--- a/src/userdel.c ++++ b/src/userdel.c +@@ -395,7 +395,7 @@ static void close_files (void) + SYSLOG ((LOG_ERR, "failure while writing changes to %s", pw_dbname ())); + fail_exit (E_PW_UPDATE); + } +- if (pw_unlock () == 0) { ++ if (pw_unlock (true) == 0) { + fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, pw_dbname ()); + SYSLOG ((LOG_ERR, "failed to unlock %s", pw_dbname ())); + /* continue */ +@@ -484,7 +484,7 @@ static void close_files (void) + static void fail_exit (int code) + { + if (pw_locked) { +- if (pw_unlock () == 0) { ++ if (pw_unlock (true) == 0) { + fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, pw_dbname ()); + SYSLOG ((LOG_ERR, "failed to unlock %s", pw_dbname ())); + /* continue */ +diff --git a/src/usermod.c b/src/usermod.c +index 534bdc74..6f761404 100644 +--- a/src/usermod.c ++++ b/src/usermod.c +@@ -655,7 +655,7 @@ fail_exit (int code) + } + } + if (pw_locked) { +- if (pw_unlock () == 0) { ++ if (pw_unlock (true) == 0) { + fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, pw_dbname ()); + SYSLOG ((LOG_ERR, "failed to unlock %s", pw_dbname ())); + /* continue */ +@@ -1544,7 +1544,7 @@ static void close_files (void) + /* continue */ + } + } +- if (pw_unlock () == 0) { ++ if (pw_unlock (true) == 0) { + fprintf (stderr, + _("%s: failed to unlock %s\n"), + Prog, pw_dbname ()); +-- +2.50.0 + + +From bdce75b7b6f7ec69cda287b081de6ca83022ee79 Mon Sep 17 00:00:00 2001 +From: Iker Pedrosa +Date: Fri, 25 Apr 2025 15:57:21 +0200 +Subject: [PATCH 10/15] 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 +--- + lib/cleanup_user.c | 2 +- + lib/lockpw.c | 2 +- + lib/shadowio.c | 4 ++-- + lib/shadowio.h | 2 +- + src/chage.c | 4 ++-- + src/chpasswd.c | 4 ++-- + src/newusers.c | 4 ++-- + src/passwd.c | 6 +++--- + src/pwck.c | 8 ++++---- + src/pwconv.c | 4 ++-- + src/pwunconv.c | 4 ++-- + src/useradd.c | 4 ++-- + src/userdel.c | 4 ++-- + src/usermod.c | 4 ++-- + 14 files changed, 28 insertions(+), 28 deletions(-) + +diff --git a/lib/cleanup_user.c b/lib/cleanup_user.c +index a661f5e9..b3a5a66e 100644 +--- a/lib/cleanup_user.c ++++ b/lib/cleanup_user.c +@@ -117,7 +117,7 @@ void cleanup_unlock_passwd (MAYBE_UNUSED void *arg) + */ + void cleanup_unlock_shadow (MAYBE_UNUSED void *arg) + { +- if (spw_unlock () == 0) { ++ if (spw_unlock (true) == 0) { + fprintf (log_get_logfd(), + _("%s: failed to unlock %s\n"), + log_get_progname(), spw_dbname ()); +diff --git a/lib/lockpw.c b/lib/lockpw.c +index f73c434a..7c99d56e 100644 +--- a/lib/lockpw.c ++++ b/lib/lockpw.c +@@ -78,7 +78,7 @@ int ulckpwdf (void) + * Unlock both files. + */ + +- return (pw_unlock (true) && spw_unlock ())? 0 : -1; ++ return (pw_unlock (true) && spw_unlock (true))? 0 : -1; + } + #else + extern int ISO_C_forbids_an_empty_translation_unit; +diff --git a/lib/shadowio.c b/lib/shadowio.c +index 9866af6d..71089eda 100644 +--- a/lib/shadowio.c ++++ b/lib/shadowio.c +@@ -204,14 +204,14 @@ int spw_close (bool process_selinux) + return retval; + } + +-int spw_unlock (void) ++int spw_unlock (bool process_selinux) + { + #ifdef WITH_TCB + int retval = 0; + + if (!getdef_bool ("USE_TCB")) { + #endif /* WITH_TCB */ +- return commonio_unlock (&shadow_db, true); ++ return commonio_unlock (&shadow_db, process_selinux); + #ifdef WITH_TCB + } + if (shadowtcb_drop_priv () == SHADOWTCB_FAILURE) { +diff --git a/lib/shadowio.h b/lib/shadowio.h +index e64d8d60..be645d0e 100644 +--- a/lib/shadowio.h ++++ b/lib/shadowio.h +@@ -23,7 +23,7 @@ extern /*@observer@*/ /*@null@*/const struct spwd *spw_next (void); + extern int spw_open (int mode); + extern int spw_remove (const char *name); + extern int spw_rewind (void); +-extern int spw_unlock (void); ++extern int spw_unlock (bool process_selinux); + extern int spw_update (const struct spwd *sp); + extern int spw_sort (void); + +diff --git a/src/chage.c b/src/chage.c +index d3a42ccf..e77a46de 100644 +--- a/src/chage.c ++++ b/src/chage.c +@@ -94,7 +94,7 @@ static void + fail_exit (int code) + { + if (spw_locked) { +- if (spw_unlock () == 0) { ++ if (spw_unlock (true) == 0) { + fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, spw_dbname ()); + SYSLOG ((LOG_ERR, "failed to unlock %s", spw_dbname ())); + /* continue */ +@@ -562,7 +562,7 @@ static void close_files (void) + SYSLOG ((LOG_ERR, "failure while writing changes to %s", pw_dbname ())); + fail_exit (E_NOPERM); + } +- if (spw_unlock () == 0) { ++ if (spw_unlock (true) == 0) { + fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, spw_dbname ()); + SYSLOG ((LOG_ERR, "failed to unlock %s", spw_dbname ())); + /* continue */ +diff --git a/src/chpasswd.c b/src/chpasswd.c +index 5323c520..2656b039 100644 +--- a/src/chpasswd.c ++++ b/src/chpasswd.c +@@ -89,7 +89,7 @@ static void fail_exit (int code) + } + + if (spw_locked) { +- if (spw_unlock () == 0) { ++ if (spw_unlock (true) == 0) { + fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, spw_dbname ()); + SYSLOG ((LOG_ERR, "failed to unlock %s", spw_dbname ())); + /* continue */ +@@ -385,7 +385,7 @@ static void close_files (void) + SYSLOG ((LOG_ERR, "failure while writing changes to %s", spw_dbname ())); + fail_exit (1); + } +- if (spw_unlock () == 0) { ++ if (spw_unlock (true) == 0) { + fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, spw_dbname ()); + SYSLOG ((LOG_ERR, "failed to unlock %s", spw_dbname ())); + /* continue */ +diff --git a/src/newusers.c b/src/newusers.c +index 669b6236..4cbb1639 100644 +--- a/src/newusers.c ++++ b/src/newusers.c +@@ -165,7 +165,7 @@ static void usage (int status) + static void fail_exit (int code) + { + if (spw_locked) { +- if (spw_unlock () == 0) { ++ if (spw_unlock (true) == 0) { + fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, spw_dbname ()); + SYSLOG ((LOG_ERR, "failed to unlock %s", spw_dbname ())); + /* continue */ +@@ -959,7 +959,7 @@ static void close_files (void) + SYSLOG ((LOG_ERR, "failure while writing changes to %s", spw_dbname ())); + fail_exit (EXIT_FAILURE); + } +- if (spw_unlock () == 0) { ++ if (spw_unlock (true) == 0) { + fprintf (stderr, + _("%s: failed to unlock %s\n"), + Prog, spw_dbname ()); +diff --git a/src/passwd.c b/src/passwd.c +index 8a55d797..512dc572 100644 +--- a/src/passwd.c ++++ b/src/passwd.c +@@ -491,7 +491,7 @@ fail_exit (int status) + } + + if (spw_locked) { +- if (spw_unlock () == 0) { ++ if (spw_unlock (true) == 0) { + (void) fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, spw_dbname ()); + SYSLOG ((LOG_ERR, "failed to unlock %s", spw_dbname ())); + /* continue */ +@@ -627,7 +627,7 @@ static void update_shadow (void) + /* Try to update the password in /etc/passwd instead. */ + (void) spw_close (true); + update_noshadow (); +- if (spw_unlock () == 0) { ++ if (spw_unlock (true) == 0) { + (void) fprintf (stderr, + _("%s: failed to unlock %s\n"), + Prog, spw_dbname ()); +@@ -688,7 +688,7 @@ static void update_shadow (void) + SYSLOG ((LOG_ERR, "failure while writing changes to %s", spw_dbname ())); + fail_exit (E_FAILURE); + } +- if (spw_unlock () == 0) { ++ if (spw_unlock (true) == 0) { + (void) fprintf (stderr, + _("%s: failed to unlock %s\n"), + Prog, spw_dbname ()); +diff --git a/src/pwck.c b/src/pwck.c +index f30c5289..9c4cc29a 100644 +--- a/src/pwck.c ++++ b/src/pwck.c +@@ -85,7 +85,7 @@ extern int allow_bad_names; + static void fail_exit (int code) + { + if (spw_locked) { +- if (spw_unlock () == 0) { ++ if (spw_unlock (true) == 0) { + fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, spw_dbname ()); + if (use_system_spw_file) { + SYSLOG ((LOG_ERR, "failed to unlock %s", +@@ -338,7 +338,7 @@ static void close_files (bool changed) + * Don't be anti-social - unlock the files when you're done. + */ + if (spw_locked) { +- if (spw_unlock () == 0) { ++ if (spw_unlock (true) == 0) { + fprintf (stderr, + _("%s: failed to unlock %s\n"), + Prog, spw_dbname ()); +@@ -581,7 +581,7 @@ static void check_pw_file (bool *errors, bool *changed) + _("%s: cannot open %s\n"), + Prog, spw_dbname ()); + *errors = true; +- if (spw_unlock () == 0) { ++ if (spw_unlock (true) == 0) { + fprintf (stderr, + _("%s: failed to unlock %s\n"), + Prog, spw_dbname ()); +@@ -669,7 +669,7 @@ static void check_pw_file (bool *errors, bool *changed) + } else { + spw_opened = false; + } +- if (spw_unlock () == 0) { ++ if (spw_unlock (true) == 0) { + fprintf (stderr, + _("%s: failed to unlock %s\n"), + Prog, spw_dbname ()); +diff --git a/src/pwconv.c b/src/pwconv.c +index 82181bb4..b0d668b5 100644 +--- a/src/pwconv.c ++++ b/src/pwconv.c +@@ -91,7 +91,7 @@ static void fail_exit (int status) + } + + if (spw_locked) { +- if (spw_unlock () == 0) { ++ if (spw_unlock (true) == 0) { + fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, spw_dbname ()); + SYSLOG ((LOG_ERR, "failed to unlock %s", spw_dbname ())); + /* continue */ +@@ -305,7 +305,7 @@ int main (int argc, char **argv) + /* continue */ + } + +- if (spw_unlock () == 0) { ++ if (spw_unlock (true) == 0) { + fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, spw_dbname ()); + SYSLOG ((LOG_ERR, "failed to unlock %s", spw_dbname ())); + /* continue */ +diff --git a/src/pwunconv.c b/src/pwunconv.c +index 72944fe5..7a6d2be5 100644 +--- a/src/pwunconv.c ++++ b/src/pwunconv.c +@@ -47,7 +47,7 @@ static void process_flags (int argc, char **argv); + static void fail_exit (int status) + { + if (spw_locked) { +- if (spw_unlock () == 0) { ++ if (spw_unlock (true) == 0) { + fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, spw_dbname ()); + SYSLOG ((LOG_ERR, "failed to unlock %s", spw_dbname ())); + /* continue */ +@@ -222,7 +222,7 @@ int main (int argc, char **argv) + fail_exit (3); + } + +- if (spw_unlock () == 0) { ++ if (spw_unlock (true) == 0) { + fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, spw_dbname ()); + SYSLOG ((LOG_ERR, "failed to unlock %s", spw_dbname ())); + /* continue */ +diff --git a/src/useradd.c b/src/useradd.c +index aa070f2d..9c8b774f 100644 +--- a/src/useradd.c ++++ b/src/useradd.c +@@ -265,7 +265,7 @@ static void fail_exit (int code) + SYSLOG((LOG_ERR, "failed to remove %s", prefix_user_home)); + } + +- if (spw_locked && spw_unlock() == 0) { ++ if (spw_locked && spw_unlock(true) == 0) { + fprintf(stderr, _("%s: failed to unlock %s\n"), Prog, spw_dbname()); + SYSLOG((LOG_ERR, "failed to unlock %s", spw_dbname())); + /* continue */ +@@ -1593,7 +1593,7 @@ static void close_files (void) + } + #endif /* ENABLE_SUBIDS */ + if (is_shadow_pwd) { +- if (spw_unlock () == 0) { ++ if (spw_unlock (true) == 0) { + fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, spw_dbname ()); + SYSLOG ((LOG_ERR, "failed to unlock %s", spw_dbname ())); + #ifdef WITH_AUDIT +diff --git a/src/userdel.c b/src/userdel.c +index b4e87820..478c22d5 100644 +--- a/src/userdel.c ++++ b/src/userdel.c +@@ -409,7 +409,7 @@ static void close_files (void) + SYSLOG ((LOG_ERR, "failure while writing changes to %s", spw_dbname ())); + fail_exit (E_PW_UPDATE); + } +- if (spw_unlock () == 0) { ++ if (spw_unlock (true) == 0) { + fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, spw_dbname ()); + SYSLOG ((LOG_ERR, "failed to unlock %s", spw_dbname ())); + /* continue */ +@@ -498,7 +498,7 @@ static void fail_exit (int code) + } + } + if (spw_locked) { +- if (spw_unlock () == 0) { ++ if (spw_unlock (true) == 0) { + fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, spw_dbname ()); + SYSLOG ((LOG_ERR, "failed to unlock %s", spw_dbname ())); + /* continue */ +diff --git a/src/usermod.c b/src/usermod.c +index 6f761404..68373f35 100644 +--- a/src/usermod.c ++++ b/src/usermod.c +@@ -648,7 +648,7 @@ fail_exit (int code) + } + #endif + if (spw_locked) { +- if (spw_unlock () == 0) { ++ if (spw_unlock (true) == 0) { + fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, spw_dbname ()); + SYSLOG ((LOG_ERR, "failed to unlock %s", spw_dbname ())); + /* continue */ +@@ -1534,7 +1534,7 @@ static void close_files (void) + } + + if (is_shadow_pwd) { +- if (spw_unlock () == 0) { ++ if (spw_unlock (true) == 0) { + fprintf (stderr, + _("%s: failed to unlock %s\n"), + Prog, spw_dbname ()); +-- +2.50.0 + + +From 68eb3395a4bcbbdb820b6c8c17cc64fb08749d11 Mon Sep 17 00:00:00 2001 +From: Iker Pedrosa +Date: Fri, 25 Apr 2025 16:00:43 +0200 +Subject: [PATCH 11/15] 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 +--- + lib/cleanup_group.c | 2 +- + lib/groupio.c | 4 ++-- + lib/groupio.h | 2 +- + src/chgpasswd.c | 4 ++-- + src/groupmems.c | 4 ++-- + src/grpck.c | 4 ++-- + src/grpconv.c | 4 ++-- + src/grpunconv.c | 4 ++-- + src/newusers.c | 4 ++-- + src/useradd.c | 4 ++-- + src/userdel.c | 4 ++-- + src/usermod.c | 4 ++-- + 12 files changed, 22 insertions(+), 22 deletions(-) + +diff --git a/lib/cleanup_group.c b/lib/cleanup_group.c +index 54480092..a5846152 100644 +--- a/lib/cleanup_group.c ++++ b/lib/cleanup_group.c +@@ -181,7 +181,7 @@ void cleanup_report_del_group_gshadow (void *group_name) + */ + void cleanup_unlock_group (MAYBE_UNUSED void *arg) + { +- if (gr_unlock () == 0) { ++ if (gr_unlock (true) == 0) { + fprintf (log_get_logfd(), + _("%s: failed to unlock %s\n"), + log_get_progname(), gr_dbname ()); +diff --git a/lib/groupio.c b/lib/groupio.c +index 6ad4ae69..f21aa618 100644 +--- a/lib/groupio.c ++++ b/lib/groupio.c +@@ -189,9 +189,9 @@ int gr_close (bool process_selinux) + return commonio_close (&group_db, process_selinux); + } + +-int gr_unlock (void) ++int gr_unlock (bool process_selinux) + { +- return commonio_unlock (&group_db, true); ++ return commonio_unlock (&group_db, process_selinux); + } + + void __gr_set_changed (void) +diff --git a/lib/groupio.h b/lib/groupio.h +index ef9242d0..eae13cd3 100644 +--- a/lib/groupio.h ++++ b/lib/groupio.h +@@ -26,7 +26,7 @@ extern /*@observer@*/ /*@null@*/const struct group *gr_next (void); + extern int gr_open (int mode); + extern int gr_remove (const char *name); + extern int gr_rewind (void); +-extern int gr_unlock (void); ++extern int gr_unlock (bool process_selinux); + extern int gr_update (const struct group *gr); + extern int gr_sort (void); + +diff --git a/src/chgpasswd.c b/src/chgpasswd.c +index 47402ecc..84553d81 100644 +--- a/src/chgpasswd.c ++++ b/src/chgpasswd.c +@@ -82,7 +82,7 @@ static void close_files (void); + static void fail_exit (int code) + { + if (gr_locked) { +- if (gr_unlock () == 0) { ++ if (gr_unlock (true) == 0) { + fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, gr_dbname ()); + SYSLOG ((LOG_ERR, "failed to unlock %s", gr_dbname ())); + /* continue */ +@@ -404,7 +404,7 @@ static void close_files (void) + SYSLOG ((LOG_ERR, "failure while writing changes to %s", gr_dbname ())); + fail_exit (1); + } +- if (gr_unlock () == 0) { ++ if (gr_unlock (true) == 0) { + fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, gr_dbname ()); + SYSLOG ((LOG_ERR, "failed to unlock %s", gr_dbname ())); + /* continue */ +diff --git a/src/groupmems.c b/src/groupmems.c +index 5976b074..0c59474f 100644 +--- a/src/groupmems.c ++++ b/src/groupmems.c +@@ -457,7 +457,7 @@ static void check_perms (void) + static void fail_exit (int code) + { + if (gr_locked) { +- if (gr_unlock () == 0) { ++ if (gr_unlock (true) == 0) { + fprintf (stderr, + _("%s: failed to unlock %s\n"), + Prog, gr_dbname ()); +@@ -528,7 +528,7 @@ static void close_files (void) + fail_exit (EXIT_GROUP_FILE); + } + if (gr_locked) { +- if (gr_unlock () == 0) { ++ if (gr_unlock (true) == 0) { + fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, gr_dbname ()); + SYSLOG ((LOG_ERR, "failed to unlock %s", gr_dbname ())); + /* continue */ +diff --git a/src/grpck.c b/src/grpck.c +index 872f882f..bd2014fc 100644 +--- a/src/grpck.c ++++ b/src/grpck.c +@@ -92,7 +92,7 @@ static void check_sgr_file (bool *errors, bool *changed); + static void fail_exit (int status) + { + if (gr_locked) { +- if (gr_unlock () == 0) { ++ if (gr_unlock (true) == 0) { + fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, gr_dbname ()); + SYSLOG ((LOG_ERR, "failed to unlock %s", gr_dbname ())); + /* continue */ +@@ -349,7 +349,7 @@ static void close_files (bool changed) + } + #endif + if (gr_locked) { +- if (gr_unlock () == 0) { ++ if (gr_unlock (true) == 0) { + fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, gr_dbname ()); + SYSLOG ((LOG_ERR, "failed to unlock %s", gr_dbname ())); + /* continue */ +diff --git a/src/grpconv.c b/src/grpconv.c +index d9c59fc9..3160b8b1 100644 +--- a/src/grpconv.c ++++ b/src/grpconv.c +@@ -56,7 +56,7 @@ static void process_flags (int argc, char **argv); + static void fail_exit (int status) + { + if (gr_locked) { +- if (gr_unlock () == 0) { ++ if (gr_unlock (true) == 0) { + fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, gr_dbname ()); + SYSLOG ((LOG_ERR, "failed to unlock %s", gr_dbname ())); + /* continue */ +@@ -253,7 +253,7 @@ int main (int argc, char **argv) + SYSLOG ((LOG_ERR, "failed to unlock %s", sgr_dbname ())); + /* continue */ + } +- if (gr_unlock () == 0) { ++ if (gr_unlock (true) == 0) { + fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, gr_dbname ()); + SYSLOG ((LOG_ERR, "failed to unlock %s", gr_dbname ())); + /* continue */ +diff --git a/src/grpunconv.c b/src/grpunconv.c +index 03fc824e..085c48ac 100644 +--- a/src/grpunconv.c ++++ b/src/grpunconv.c +@@ -55,7 +55,7 @@ static void process_flags (int argc, char **argv); + static void fail_exit (int status) + { + if (gr_locked) { +- if (gr_unlock () == 0) { ++ if (gr_unlock (true) == 0) { + fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, gr_dbname ()); + SYSLOG ((LOG_ERR, "failed to unlock %s", gr_dbname ())); + /* continue */ +@@ -209,7 +209,7 @@ int main (int argc, char **argv) + fail_exit (3); + } + +- if (gr_unlock () == 0) { ++ if (gr_unlock (true) == 0) { + fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, gr_dbname ()); + SYSLOG ((LOG_ERR, "failed to unlock %s", gr_dbname ())); + /* continue */ +diff --git a/src/newusers.c b/src/newusers.c +index 4cbb1639..47f01661 100644 +--- a/src/newusers.c ++++ b/src/newusers.c +@@ -179,7 +179,7 @@ static void fail_exit (int code) + } + } + if (gr_locked) { +- if (gr_unlock () == 0) { ++ if (gr_unlock (true) == 0) { + fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, gr_dbname ()); + SYSLOG ((LOG_ERR, "failed to unlock %s", gr_dbname ())); + /* continue */ +@@ -991,7 +991,7 @@ static void close_files (void) + } + #endif /* ENABLE_SUBIDS */ + +- if (gr_unlock () == 0) { ++ if (gr_unlock (true) == 0) { + fprintf (stderr, + _("%s: failed to unlock %s\n"), + Prog, gr_dbname ()); +diff --git a/src/useradd.c b/src/useradd.c +index 9c8b774f..75cfbde2 100644 +--- a/src/useradd.c ++++ b/src/useradd.c +@@ -275,7 +275,7 @@ static void fail_exit (int code) + SYSLOG((LOG_ERR, "failed to unlock %s", pw_dbname())); + /* continue */ + } +- if (gr_locked && gr_unlock() == 0) { ++ if (gr_locked && gr_unlock(true) == 0) { + fprintf(stderr, _("%s: failed to unlock %s\n"), Prog, gr_dbname()); + SYSLOG((LOG_ERR, "failed to unlock %s", gr_dbname())); + /* continue */ +@@ -1690,7 +1690,7 @@ static void close_group_files (void) + */ + static void unlock_group_files (void) + { +- if (gr_unlock () == 0) { ++ if (gr_unlock (true) == 0) { + fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, gr_dbname ()); + SYSLOG ((LOG_ERR, "failed to unlock %s", gr_dbname ())); + #ifdef WITH_AUDIT +diff --git a/src/userdel.c b/src/userdel.c +index 478c22d5..12696732 100644 +--- a/src/userdel.c ++++ b/src/userdel.c +@@ -422,7 +422,7 @@ static void close_files (void) + SYSLOG ((LOG_ERR, "failure while writing changes to %s", gr_dbname ())); + fail_exit (E_GRP_UPDATE); + } +- if (gr_unlock () == 0) { ++ if (gr_unlock (true) == 0) { + fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, gr_dbname ()); + SYSLOG ((LOG_ERR, "failed to unlock %s", gr_dbname ())); + /* continue */ +@@ -491,7 +491,7 @@ static void fail_exit (int code) + } + } + if (gr_locked) { +- if (gr_unlock () == 0) { ++ if (gr_unlock (true) == 0) { + fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, gr_dbname ()); + SYSLOG ((LOG_ERR, "failed to unlock %s", gr_dbname ())); + /* continue */ +diff --git a/src/usermod.c b/src/usermod.c +index 68373f35..2e21a70f 100644 +--- a/src/usermod.c ++++ b/src/usermod.c +@@ -632,7 +632,7 @@ static void + fail_exit (int code) + { + if (gr_locked) { +- if (gr_unlock () == 0) { ++ if (gr_unlock (true) == 0) { + fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, gr_dbname ()); + SYSLOG ((LOG_ERR, "failed to unlock %s", gr_dbname ())); + /* continue */ +@@ -1522,7 +1522,7 @@ static void close_files (void) + } + } + #endif +- if (gr_unlock () == 0) { ++ if (gr_unlock (true) == 0) { + fprintf (stderr, + _("%s: failed to unlock %s\n"), + Prog, gr_dbname ()); +-- +2.50.0 + + +From 37b636524cfb7c9b7dce8c56484fb60795ef2b91 Mon Sep 17 00:00:00 2001 +From: Iker Pedrosa +Date: Fri, 25 Apr 2025 16:03:58 +0200 +Subject: [PATCH 12/15] 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 +--- + lib/cleanup_group.c | 2 +- + lib/sgroupio.c | 4 ++-- + lib/sgroupio.h | 2 +- + src/chgpasswd.c | 4 ++-- + src/groupmems.c | 4 ++-- + src/grpck.c | 4 ++-- + src/grpconv.c | 4 ++-- + src/grpunconv.c | 4 ++-- + src/newusers.c | 4 ++-- + src/useradd.c | 4 ++-- + src/userdel.c | 4 ++-- + src/usermod.c | 4 ++-- + 12 files changed, 22 insertions(+), 22 deletions(-) + +diff --git a/lib/cleanup_group.c b/lib/cleanup_group.c +index a5846152..dc9f1eb2 100644 +--- a/lib/cleanup_group.c ++++ b/lib/cleanup_group.c +@@ -201,7 +201,7 @@ void cleanup_unlock_group (MAYBE_UNUSED void *arg) + */ + void cleanup_unlock_gshadow (MAYBE_UNUSED void *arg) + { +- if (sgr_unlock () == 0) { ++ if (sgr_unlock (true) == 0) { + fprintf (log_get_logfd(), + _("%s: failed to unlock %s\n"), + log_get_progname(), sgr_dbname ()); +diff --git a/lib/sgroupio.c b/lib/sgroupio.c +index dbabcc36..4239e23d 100644 +--- a/lib/sgroupio.c ++++ b/lib/sgroupio.c +@@ -281,9 +281,9 @@ int sgr_close (bool process_selinux) + return commonio_close (&gshadow_db, process_selinux); + } + +-int sgr_unlock (void) ++int sgr_unlock (bool process_selinux) + { +- return commonio_unlock (&gshadow_db, true); ++ return commonio_unlock (&gshadow_db, process_selinux); + } + + void __sgr_set_changed (void) +diff --git a/lib/sgroupio.h b/lib/sgroupio.h +index 0e25fc0b..256f6d54 100644 +--- a/lib/sgroupio.h ++++ b/lib/sgroupio.h +@@ -22,7 +22,7 @@ extern /*@null@*/const struct sgrp *sgr_next (void); + extern int sgr_open (int mode); + extern int sgr_remove (const char *name); + extern int sgr_rewind (void); +-extern int sgr_unlock (void); ++extern int sgr_unlock (bool process_selinux); + extern int sgr_update (const struct sgrp *sg); + extern int sgr_sort (void); + +diff --git a/src/chgpasswd.c b/src/chgpasswd.c +index 84553d81..27094f50 100644 +--- a/src/chgpasswd.c ++++ b/src/chgpasswd.c +@@ -91,7 +91,7 @@ static void fail_exit (int code) + + #ifdef SHADOWGRP + if (sgr_locked) { +- if (sgr_unlock () == 0) { ++ if (sgr_unlock (true) == 0) { + fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, sgr_dbname ()); + SYSLOG ((LOG_ERR, "failed to unlock %s", sgr_dbname ())); + /* continue */ +@@ -388,7 +388,7 @@ static void close_files (void) + SYSLOG ((LOG_ERR, "failure while writing changes to %s", sgr_dbname ())); + fail_exit (1); + } +- if (sgr_unlock () == 0) { ++ if (sgr_unlock (true) == 0) { + fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, sgr_dbname ()); + SYSLOG ((LOG_ERR, "failed to unlock %s", sgr_dbname ())); + /* continue */ +diff --git a/src/groupmems.c b/src/groupmems.c +index 0c59474f..f39724c8 100644 +--- a/src/groupmems.c ++++ b/src/groupmems.c +@@ -468,7 +468,7 @@ static void fail_exit (int code) + + #ifdef SHADOWGRP + if (sgr_locked) { +- if (sgr_unlock () == 0) { ++ if (sgr_unlock (true) == 0) { + fprintf (stderr, + _("%s: failed to unlock %s\n"), + Prog, sgr_dbname ()); +@@ -544,7 +544,7 @@ static void close_files (void) + fail_exit (EXIT_GROUP_FILE); + } + if (sgr_locked) { +- if (sgr_unlock () == 0) { ++ if (sgr_unlock (true) == 0) { + fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, sgr_dbname ()); + SYSLOG ((LOG_ERR, "failed to unlock %s", sgr_dbname ())); + /* continue */ +diff --git a/src/grpck.c b/src/grpck.c +index bd2014fc..bc178d52 100644 +--- a/src/grpck.c ++++ b/src/grpck.c +@@ -101,7 +101,7 @@ static void fail_exit (int status) + + #ifdef SHADOWGRP + if (sgr_locked) { +- if (sgr_unlock () == 0) { ++ if (sgr_unlock (true) == 0) { + fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, sgr_dbname ()); + SYSLOG ((LOG_ERR, "failed to unlock %s", sgr_dbname ())); + /* continue */ +@@ -340,7 +340,7 @@ static void close_files (bool changed) + */ + #ifdef SHADOWGRP + if (sgr_locked) { +- if (sgr_unlock () == 0) { ++ if (sgr_unlock (true) == 0) { + fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, sgr_dbname ()); + SYSLOG ((LOG_ERR, "failed to unlock %s", sgr_dbname ())); + /* continue */ +diff --git a/src/grpconv.c b/src/grpconv.c +index 3160b8b1..5958d3f5 100644 +--- a/src/grpconv.c ++++ b/src/grpconv.c +@@ -64,7 +64,7 @@ static void fail_exit (int status) + } + + if (sgr_locked) { +- if (sgr_unlock () == 0) { ++ if (sgr_unlock (true) == 0) { + fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, sgr_dbname ()); + SYSLOG ((LOG_ERR, "failed to unlock %s", sgr_dbname ())); + /* continue */ +@@ -248,7 +248,7 @@ int main (int argc, char **argv) + SYSLOG ((LOG_ERR, "failure while writing changes to %s", gr_dbname ())); + fail_exit (3); + } +- if (sgr_unlock () == 0) { ++ if (sgr_unlock (true) == 0) { + fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, sgr_dbname ()); + SYSLOG ((LOG_ERR, "failed to unlock %s", sgr_dbname ())); + /* continue */ +diff --git a/src/grpunconv.c b/src/grpunconv.c +index 085c48ac..5256e0bc 100644 +--- a/src/grpunconv.c ++++ b/src/grpunconv.c +@@ -63,7 +63,7 @@ static void fail_exit (int status) + } + + if (sgr_locked) { +- if (sgr_unlock () == 0) { ++ if (sgr_unlock (true) == 0) { + fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, sgr_dbname ()); + SYSLOG ((LOG_ERR, "failed to unlock %s", sgr_dbname ())); + /* continue */ +@@ -215,7 +215,7 @@ int main (int argc, char **argv) + /* continue */ + } + +- if (sgr_unlock () == 0) { ++ if (sgr_unlock (true) == 0) { + fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, sgr_dbname ()); + SYSLOG ((LOG_ERR, "failed to unlock %s", sgr_dbname ())); + /* continue */ +diff --git a/src/newusers.c b/src/newusers.c +index 47f01661..261a5911 100644 +--- a/src/newusers.c ++++ b/src/newusers.c +@@ -187,7 +187,7 @@ static void fail_exit (int code) + } + #ifdef SHADOWGRP + if (sgr_locked) { +- if (sgr_unlock () == 0) { ++ if (sgr_unlock (true) == 0) { + fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, sgr_dbname ()); + SYSLOG ((LOG_ERR, "failed to unlock %s", sgr_dbname ())); + /* continue */ +@@ -1009,7 +1009,7 @@ static void close_files (void) + SYSLOG ((LOG_ERR, "failure while writing changes to %s", sgr_dbname ())); + fail_exit (EXIT_FAILURE); + } +- if (sgr_unlock () == 0) { ++ if (sgr_unlock (true) == 0) { + fprintf (stderr, + _("%s: failed to unlock %s\n"), + Prog, sgr_dbname ()); +diff --git a/src/useradd.c b/src/useradd.c +index 75cfbde2..cb7ac88e 100644 +--- a/src/useradd.c ++++ b/src/useradd.c +@@ -281,7 +281,7 @@ static void fail_exit (int code) + /* continue */ + } + #ifdef SHADOWGRP +- if (sgr_locked && sgr_unlock() == 0) { ++ if (sgr_locked && sgr_unlock(true) == 0) { + fprintf(stderr, _("%s: failed to unlock %s\n"), Prog, sgr_dbname()); + SYSLOG((LOG_ERR, "failed to unlock %s", sgr_dbname())); + /* continue */ +@@ -1704,7 +1704,7 @@ static void unlock_group_files (void) + gr_locked = false; + #ifdef SHADOWGRP + if (is_shadow_grp) { +- if (sgr_unlock () == 0) { ++ if (sgr_unlock (true) == 0) { + fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, sgr_dbname ()); + SYSLOG ((LOG_ERR, "failed to unlock %s", sgr_dbname ())); + #ifdef WITH_AUDIT +diff --git a/src/userdel.c b/src/userdel.c +index 12696732..80ed1a48 100644 +--- a/src/userdel.c ++++ b/src/userdel.c +@@ -438,7 +438,7 @@ static void close_files (void) + fail_exit (E_GRP_UPDATE); + } + +- if (sgr_unlock () == 0) { ++ if (sgr_unlock (true) == 0) { + fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, sgr_dbname ()); + SYSLOG ((LOG_ERR, "failed to unlock %s", sgr_dbname ())); + /* continue */ +@@ -506,7 +506,7 @@ static void fail_exit (int code) + } + #ifdef SHADOWGRP + if (sgr_locked) { +- if (sgr_unlock () == 0) { ++ if (sgr_unlock (true) == 0) { + fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, sgr_dbname ()); + SYSLOG ((LOG_ERR, "failed to unlock %s", sgr_dbname ())); + /* continue */ +diff --git a/src/usermod.c b/src/usermod.c +index 2e21a70f..69ea8e55 100644 +--- a/src/usermod.c ++++ b/src/usermod.c +@@ -640,7 +640,7 @@ fail_exit (int code) + } + #ifdef SHADOWGRP + if (sgr_locked) { +- if (sgr_unlock () == 0) { ++ if (sgr_unlock (true) == 0) { + fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, sgr_dbname ()); + SYSLOG ((LOG_ERR, "failed to unlock %s", sgr_dbname ())); + /* continue */ +@@ -1511,7 +1511,7 @@ static void close_files (void) + #endif + #ifdef SHADOWGRP + if (is_shadow_grp) { +- if (sgr_unlock () == 0) { ++ if (sgr_unlock (true) == 0) { + fprintf (stderr, + _("%s: failed to unlock %s\n"), + Prog, sgr_dbname ()); +-- +2.50.0 + + +From dee1a5a5782b8aafcc32c7f93a53ce591355d1b3 Mon Sep 17 00:00:00 2001 +From: Iker Pedrosa +Date: Fri, 25 Apr 2025 16:06:57 +0200 +Subject: [PATCH 13/15] 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 +--- + src/vipw.c | 10 +++++----- + 1 file changed, 5 insertions(+), 5 deletions(-) + +diff --git a/src/vipw.c b/src/vipw.c +index 3c840e9f..ba323e0e 100644 +--- a/src/vipw.c ++++ b/src/vipw.c +@@ -62,7 +62,7 @@ static const char *Prog; + static const char *filename, *fileeditname; + static bool filelocked = false; + static bool createedit = false; +-static int (*unlock) (void); ++static int (*unlock) (bool); + static bool quiet = false; + #ifdef WITH_TCB + static const char *user = NULL; +@@ -74,7 +74,7 @@ static bool tcb_mode = false; + static void usage (int status); + static int create_backup_file (FILE *, const char *, struct stat *); + static void vipwexit (const char *msg, int syserr, int ret); +-static void vipwedit (const char *, int (*)(void), int (*)(void)); ++static void vipwedit (const char *, int (*)(void), int (*)(bool)); + + /* + * usage - display usage message and exit +@@ -164,7 +164,7 @@ static void vipwexit (const char *msg, int syserr, int ret) + } + } + if (filelocked) { +- if ((*unlock) () == 0) { ++ if ((*unlock) (true) == 0) { + fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, fileeditname); + SYSLOG ((LOG_ERR, "failed to unlock %s", fileeditname)); + /* continue */ +@@ -195,7 +195,7 @@ static void vipwexit (const char *msg, int syserr, int ret) + * + */ + static void +-vipwedit (const char *file, int (*file_lock) (void), int (*file_unlock) (void)) ++vipwedit (const char *file, int (*file_lock) (void), int (*file_unlock) (bool)) + { + int status; + char *to_rename; +@@ -467,7 +467,7 @@ vipwedit (const char *file, int (*file_lock) (void), int (*file_unlock) (void)) + } + #endif /* WITH_TCB */ + +- if ((*file_unlock) () == 0) { ++ if ((*file_unlock) (true) == 0) { + fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, fileeditname); + SYSLOG ((LOG_ERR, "failed to unlock %s", fileeditname)); + /* continue */ +-- +2.50.0 + + +From f297173f0247b9288c424e765e972eb13d4c9ded Mon Sep 17 00:00:00 2001 +From: Iker Pedrosa +Date: Fri, 25 Apr 2025 16:10:09 +0200 +Subject: [PATCH 14/15] 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 +--- + lib/subordinateio.c | 12 ++++++------ + lib/subordinateio.h | 2 +- + src/newusers.c | 4 ++-- + src/useradd.c | 4 ++-- + src/userdel.c | 4 ++-- + src/usermod.c | 4 ++-- + 6 files changed, 15 insertions(+), 15 deletions(-) + +diff --git a/lib/subordinateio.c b/lib/subordinateio.c +index 9587e1a9..eaa2dc99 100644 +--- a/lib/subordinateio.c ++++ b/lib/subordinateio.c +@@ -673,9 +673,9 @@ int sub_uid_close (bool process_selinux) + return commonio_close (&subordinate_uid_db, process_selinux); + } + +-int sub_uid_unlock (void) ++int sub_uid_unlock (bool process_selinux) + { +- return commonio_unlock (&subordinate_uid_db, true); ++ return commonio_unlock (&subordinate_uid_db, process_selinux); + } + + uid_t sub_uid_find_free_range(uid_t min, uid_t max, unsigned long count) +@@ -1022,7 +1022,7 @@ bool new_subid_range(struct subordinate_range *range, enum subid_type id_type, b + } + if (!sub_uid_open(O_CREAT | O_RDWR)) { + printf("Failed opening subuids (errno %d)\n", errno); +- sub_uid_unlock(); ++ sub_uid_unlock(true); + return false; + } + db = &subordinate_uid_db; +@@ -1069,7 +1069,7 @@ bool new_subid_range(struct subordinate_range *range, enum subid_type id_type, b + out: + if (id_type == ID_TYPE_UID) { + sub_uid_close(true); +- sub_uid_unlock(); ++ sub_uid_unlock(true); + } else { + sub_gid_close(true); + sub_gid_unlock(); +@@ -1094,7 +1094,7 @@ bool release_subid_range(struct subordinate_range *range, enum subid_type id_typ + } + if (!sub_uid_open(O_CREAT | O_RDWR)) { + printf("Failed opening subuids (errno %d)\n", errno); +- sub_uid_unlock(); ++ sub_uid_unlock(true); + return false; + } + db = &subordinate_uid_db; +@@ -1119,7 +1119,7 @@ bool release_subid_range(struct subordinate_range *range, enum subid_type id_typ + + if (id_type == ID_TYPE_UID) { + sub_uid_close(true); +- sub_uid_unlock(); ++ sub_uid_unlock(true); + } else { + sub_gid_close(true); + sub_gid_unlock(); +diff --git a/lib/subordinateio.h b/lib/subordinateio.h +index 63f5b1fb..7914613d 100644 +--- a/lib/subordinateio.h ++++ b/lib/subordinateio.h +@@ -21,7 +21,7 @@ extern int sub_uid_lock (void); + extern int sub_uid_setdbname (const char *filename); + extern /*@observer@*/const char *sub_uid_dbname (void); + extern int sub_uid_open (int mode); +-extern int sub_uid_unlock (void); ++extern int sub_uid_unlock (bool process_selinux); + extern int sub_uid_add (const char *owner, uid_t start, unsigned long count); + extern int sub_uid_remove (const char *owner, uid_t start, unsigned long count); + extern uid_t sub_uid_find_free_range(uid_t min, uid_t max, unsigned long count); +diff --git a/src/newusers.c b/src/newusers.c +index 261a5911..65bedf9b 100644 +--- a/src/newusers.c ++++ b/src/newusers.c +@@ -196,7 +196,7 @@ static void fail_exit (int code) + #endif + #ifdef ENABLE_SUBIDS + if (sub_uid_locked) { +- if (sub_uid_unlock () == 0) { ++ if (sub_uid_unlock (true) == 0) { + fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, sub_uid_dbname ()); + SYSLOG ((LOG_ERR, "failed to unlock %s", sub_uid_dbname ())); + /* continue */ +@@ -1021,7 +1021,7 @@ static void close_files (void) + #endif + #ifdef ENABLE_SUBIDS + if (is_sub_uid) { +- if (sub_uid_unlock () == 0) { ++ if (sub_uid_unlock (true) == 0) { + fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, sub_uid_dbname ()); + SYSLOG ((LOG_ERR, "failed to unlock %s", sub_uid_dbname ())); + /* continue */ +diff --git a/src/useradd.c b/src/useradd.c +index cb7ac88e..f65d24d1 100644 +--- a/src/useradd.c ++++ b/src/useradd.c +@@ -288,7 +288,7 @@ static void fail_exit (int code) + } + #endif + #ifdef ENABLE_SUBIDS +- if (sub_uid_locked && sub_uid_unlock() == 0) { ++ if (sub_uid_locked && sub_uid_unlock(true) == 0) { + fprintf(stderr, _("%s: failed to unlock %s\n"), Prog, sub_uid_dbname()); + SYSLOG((LOG_ERR, "failed to unlock %s", sub_uid_dbname())); + /* continue */ +@@ -1623,7 +1623,7 @@ static void close_files (void) + + #ifdef ENABLE_SUBIDS + if (is_sub_uid) { +- if (sub_uid_unlock () == 0) { ++ if (sub_uid_unlock (true) == 0) { + fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, sub_uid_dbname ()); + SYSLOG ((LOG_ERR, "failed to unlock %s", sub_uid_dbname ())); + #ifdef WITH_AUDIT +diff --git a/src/userdel.c b/src/userdel.c +index 80ed1a48..68e53b85 100644 +--- a/src/userdel.c ++++ b/src/userdel.c +@@ -454,7 +454,7 @@ static void close_files (void) + SYSLOG ((LOG_ERR, "failure while writing changes to %s", sub_uid_dbname ())); + fail_exit (E_SUB_UID_UPDATE); + } +- if (sub_uid_unlock () == 0) { ++ if (sub_uid_unlock (true) == 0) { + fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, sub_uid_dbname ()); + SYSLOG ((LOG_ERR, "failed to unlock %s", sub_uid_dbname ())); + /* continue */ +@@ -515,7 +515,7 @@ static void fail_exit (int code) + #endif /* SHADOWGRP */ + #ifdef ENABLE_SUBIDS + if (sub_uid_locked) { +- if (sub_uid_unlock () == 0) { ++ if (sub_uid_unlock (true) == 0) { + fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, sub_uid_dbname ()); + SYSLOG ((LOG_ERR, "failed to unlock %s", sub_uid_dbname ())); + /* continue */ +diff --git a/src/usermod.c b/src/usermod.c +index 69ea8e55..fc6cc51c 100644 +--- a/src/usermod.c ++++ b/src/usermod.c +@@ -663,7 +663,7 @@ fail_exit (int code) + } + #ifdef ENABLE_SUBIDS + if (sub_uid_locked) { +- if (sub_uid_unlock () == 0) { ++ if (sub_uid_unlock (true) == 0) { + fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, sub_uid_dbname ()); + SYSLOG ((LOG_ERR, "failed to unlock %s", sub_uid_dbname ())); + /* continue */ +@@ -1566,7 +1566,7 @@ static void close_files (void) + SYSLOG ((LOG_ERR, "failure while writing changes to %s", sub_uid_dbname ())); + fail_exit (E_SUB_UID_UPDATE); + } +- if (sub_uid_unlock () == 0) { ++ if (sub_uid_unlock (true) == 0) { + fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, sub_uid_dbname ()); + SYSLOG ((LOG_ERR, "failed to unlock %s", sub_uid_dbname ())); + /* continue */ +-- +2.50.0 + + +From 7b2bbdd0347c34bc5e094caf26c55694fb5935e7 Mon Sep 17 00:00:00 2001 +From: Iker Pedrosa +Date: Fri, 25 Apr 2025 16:12:40 +0200 +Subject: [PATCH 15/15] 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 +--- + lib/subordinateio.c | 12 ++++++------ + lib/subordinateio.h | 2 +- + src/newusers.c | 4 ++-- + src/useradd.c | 4 ++-- + src/userdel.c | 4 ++-- + src/usermod.c | 4 ++-- + 6 files changed, 15 insertions(+), 15 deletions(-) + +diff --git a/lib/subordinateio.c b/lib/subordinateio.c +index eaa2dc99..d1ab03f3 100644 +--- a/lib/subordinateio.c ++++ b/lib/subordinateio.c +@@ -780,9 +780,9 @@ int sub_gid_close (bool process_selinux) + return commonio_close (&subordinate_gid_db, process_selinux); + } + +-int sub_gid_unlock (void) ++int sub_gid_unlock (bool process_selinux) + { +- return commonio_unlock (&subordinate_gid_db, true); ++ return commonio_unlock (&subordinate_gid_db, process_selinux); + } + + gid_t sub_gid_find_free_range(gid_t min, gid_t max, unsigned long count) +@@ -1034,7 +1034,7 @@ bool new_subid_range(struct subordinate_range *range, enum subid_type id_type, b + } + if (!sub_gid_open(O_CREAT | O_RDWR)) { + printf("Failed opening subgids (errno %d)\n", errno); +- sub_gid_unlock(); ++ sub_gid_unlock(true); + return false; + } + db = &subordinate_gid_db; +@@ -1072,7 +1072,7 @@ out: + sub_uid_unlock(true); + } else { + sub_gid_close(true); +- sub_gid_unlock(); ++ sub_gid_unlock(true); + } + + return ret; +@@ -1106,7 +1106,7 @@ bool release_subid_range(struct subordinate_range *range, enum subid_type id_typ + } + if (!sub_gid_open(O_CREAT | O_RDWR)) { + printf("Failed opening subgids (errno %d)\n", errno); +- sub_gid_unlock(); ++ sub_gid_unlock(true); + return false; + } + db = &subordinate_gid_db; +@@ -1122,7 +1122,7 @@ bool release_subid_range(struct subordinate_range *range, enum subid_type id_typ + sub_uid_unlock(true); + } else { + sub_gid_close(true); +- sub_gid_unlock(); ++ sub_gid_unlock(true); + } + + return ret; +diff --git a/lib/subordinateio.h b/lib/subordinateio.h +index 7914613d..1ca4b66d 100644 +--- a/lib/subordinateio.h ++++ b/lib/subordinateio.h +@@ -39,7 +39,7 @@ extern int sub_gid_lock (void); + extern int sub_gid_setdbname (const char *filename); + extern /*@observer@*/const char *sub_gid_dbname (void); + extern int sub_gid_open (int mode); +-extern int sub_gid_unlock (void); ++extern int sub_gid_unlock (bool process_selinux); + extern int sub_gid_add (const char *owner, gid_t start, unsigned long count); + extern int sub_gid_remove (const char *owner, gid_t start, unsigned long count); + extern uid_t sub_gid_find_free_range(gid_t min, gid_t max, unsigned long count); +diff --git a/src/newusers.c b/src/newusers.c +index 65bedf9b..4eb6065c 100644 +--- a/src/newusers.c ++++ b/src/newusers.c +@@ -203,7 +203,7 @@ static void fail_exit (int code) + } + } + if (sub_gid_locked) { +- if (sub_gid_unlock () == 0) { ++ if (sub_gid_unlock (true) == 0) { + fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, sub_gid_dbname ()); + SYSLOG ((LOG_ERR, "failed to unlock %s", sub_gid_dbname ())); + /* continue */ +@@ -1029,7 +1029,7 @@ static void close_files (void) + sub_uid_locked = false; + } + if (is_sub_gid) { +- if (sub_gid_unlock () == 0) { ++ if (sub_gid_unlock (true) == 0) { + fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, sub_gid_dbname ()); + SYSLOG ((LOG_ERR, "failed to unlock %s", sub_gid_dbname ())); + /* continue */ +diff --git a/src/useradd.c b/src/useradd.c +index f65d24d1..98c2d284 100644 +--- a/src/useradd.c ++++ b/src/useradd.c +@@ -293,7 +293,7 @@ static void fail_exit (int code) + SYSLOG((LOG_ERR, "failed to unlock %s", sub_uid_dbname())); + /* continue */ + } +- if (sub_gid_locked && sub_gid_unlock() == 0) { ++ if (sub_gid_locked && sub_gid_unlock(true) == 0) { + fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, sub_gid_dbname()); + SYSLOG ((LOG_ERR, "failed to unlock %s", sub_gid_dbname())); + /* continue */ +@@ -1637,7 +1637,7 @@ static void close_files (void) + sub_uid_locked = false; + } + if (is_sub_gid) { +- if (sub_gid_unlock () == 0) { ++ if (sub_gid_unlock (true) == 0) { + fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, sub_gid_dbname ()); + SYSLOG ((LOG_ERR, "failed to unlock %s", sub_gid_dbname ())); + #ifdef WITH_AUDIT +diff --git a/src/userdel.c b/src/userdel.c +index 68e53b85..524c96a6 100644 +--- a/src/userdel.c ++++ b/src/userdel.c +@@ -468,7 +468,7 @@ static void close_files (void) + SYSLOG ((LOG_ERR, "failure while writing changes to %s", sub_gid_dbname ())); + fail_exit (E_SUB_GID_UPDATE); + } +- if (sub_gid_unlock () == 0) { ++ if (sub_gid_unlock (true) == 0) { + fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, sub_gid_dbname ()); + SYSLOG ((LOG_ERR, "failed to unlock %s", sub_gid_dbname ())); + /* continue */ +@@ -522,7 +522,7 @@ static void fail_exit (int code) + } + } + if (sub_gid_locked) { +- if (sub_gid_unlock () == 0) { ++ if (sub_gid_unlock (true) == 0) { + fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, sub_gid_dbname ()); + SYSLOG ((LOG_ERR, "failed to unlock %s", sub_gid_dbname ())); + /* continue */ +diff --git a/src/usermod.c b/src/usermod.c +index fc6cc51c..68000a0e 100644 +--- a/src/usermod.c ++++ b/src/usermod.c +@@ -670,7 +670,7 @@ fail_exit (int code) + } + } + if (sub_gid_locked) { +- if (sub_gid_unlock () == 0) { ++ if (sub_gid_unlock (true) == 0) { + fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, sub_gid_dbname ()); + SYSLOG ((LOG_ERR, "failed to unlock %s", sub_gid_dbname ())); + /* continue */ +@@ -1579,7 +1579,7 @@ static void close_files (void) + SYSLOG ((LOG_ERR, "failure while writing changes to %s", sub_gid_dbname ())); + fail_exit (E_SUB_GID_UPDATE); + } +- if (sub_gid_unlock () == 0) { ++ if (sub_gid_unlock (true) == 0) { + fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, sub_gid_dbname ()); + SYSLOG ((LOG_ERR, "failed to unlock %s", sub_gid_dbname ())); + /* continue */ +-- +2.50.0 + +diff -up shadow-4.18.0/src/newusers.c.test-chroot2 shadow-4.18.0/src/newusers.c +--- shadow-4.18.0/src/newusers.c.test-chroot2 2025-07-08 11:22:16.257291774 +0200 ++++ shadow-4.18.0/src/newusers.c 2025-07-08 11:23:28.210122190 +0200 +@@ -60,6 +60,10 @@ + #include "string/strtok/stpsep.h" + #include "string/strtok/strsep2arr.h" + ++struct option_flags { ++ bool chroot; ++}; ++ + + /* + * Global variables +@@ -103,7 +107,7 @@ static bool sub_gid_locked = false; + + /* local function prototypes */ + NORETURN static void usage (int status); +-NORETURN static void fail_exit (int); ++NORETURN static void fail_exit (int, bool); + static int add_group (const char *, const char *, gid_t *, gid_t); + static int get_user_id (const char *, uid_t *); + static int add_user (const char *, uid_t, gid_t); +@@ -113,11 +117,11 @@ static int update_passwd (struct passwd + #endif /* !USE_PAM */ + #endif /* !ACCT_TOOLS_SETUID */ + static int add_passwd (struct passwd *, const char *); +-static void process_flags (int argc, char **argv); ++static void process_flags (int argc, char **argv, struct option_flags *flags); + static void check_flags (void); + static void check_perms (void); +-static void open_files (void); +-static void close_files (void); ++static void open_files (bool process_selinux); ++static void close_files (struct option_flags *flags); + + extern int allow_bad_names; + +@@ -170,24 +174,24 @@ static void usage (int status) + /* + * fail_exit - undo as much as possible + */ +-static void fail_exit (int code) ++static void fail_exit (int code, bool process_selinux) + { + if (spw_locked) { +- if (spw_unlock (true) == 0) { ++ if (spw_unlock (process_selinux) == 0) { + fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, spw_dbname ()); + SYSLOG ((LOG_ERR, "failed to unlock %s", spw_dbname ())); + /* continue */ + } + } + if (pw_locked) { +- if (pw_unlock (true) == 0) { ++ if (pw_unlock (process_selinux) == 0) { + fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, pw_dbname ()); + SYSLOG ((LOG_ERR, "failed to unlock %s", pw_dbname ())); + /* continue */ + } + } + if (gr_locked) { +- if (gr_unlock (true) == 0) { ++ if (gr_unlock (process_selinux) == 0) { + fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, gr_dbname ()); + SYSLOG ((LOG_ERR, "failed to unlock %s", gr_dbname ())); + /* continue */ +@@ -195,7 +199,7 @@ static void fail_exit (int code) + } + #ifdef SHADOWGRP + if (sgr_locked) { +- if (sgr_unlock (true) == 0) { ++ if (sgr_unlock (process_selinux) == 0) { + fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, sgr_dbname ()); + SYSLOG ((LOG_ERR, "failed to unlock %s", sgr_dbname ())); + /* continue */ +@@ -204,14 +208,14 @@ static void fail_exit (int code) + #endif + #ifdef ENABLE_SUBIDS + if (sub_uid_locked) { +- if (sub_uid_unlock (true) == 0) { ++ if (sub_uid_unlock (process_selinux) == 0) { + fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, sub_uid_dbname ()); + SYSLOG ((LOG_ERR, "failed to unlock %s", sub_uid_dbname ())); + /* continue */ + } + } + if (sub_gid_locked) { +- if (sub_gid_unlock (true) == 0) { ++ if (sub_gid_unlock (process_selinux) == 0) { + fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, sub_gid_dbname ()); + SYSLOG ((LOG_ERR, "failed to unlock %s", sub_gid_dbname ())); + /* continue */ +@@ -637,7 +641,7 @@ static int add_passwd (struct passwd *pw + * + * It will not return if an error is encountered. + */ +-static void process_flags (int argc, char **argv) ++static void process_flags (int argc, char **argv, struct option_flags *flags) + { + int c; + #ifndef ACCT_TOOLS_SETUID +@@ -696,6 +700,7 @@ static void process_flags (int argc, cha + rflg = true; + break; + case 'R': /* no-op, handled in process_root_flag () */ ++ flags->chroot = true; + break; + #ifndef ACCT_TOOLS_SETUID + #ifndef USE_PAM +@@ -755,7 +760,7 @@ static void process_flags (int argc, cha + + SNPRINTF(buf, "%s: %s", Prog, argv[1]); + perror (buf); +- fail_exit (EXIT_FAILURE); ++ fail_exit (EXIT_FAILURE, !flags->chroot); + } + } + +@@ -859,7 +864,7 @@ static void check_perms (void) + /* + * open_files - lock and open the password, group and shadow databases + */ +-static void open_files (void) ++static void open_files (bool process_selinux) + { + /* + * Lock the password files and open them for update. This will bring +@@ -871,7 +876,7 @@ static void open_files (void) + fprintf (stderr, + _("%s: cannot lock %s; try again later.\n"), + Prog, pw_dbname ()); +- fail_exit (EXIT_FAILURE); ++ fail_exit (EXIT_FAILURE, process_selinux); + } + pw_locked = true; + if (is_shadow) { +@@ -879,7 +884,7 @@ static void open_files (void) + fprintf (stderr, + _("%s: cannot lock %s; try again later.\n"), + Prog, spw_dbname ()); +- fail_exit (EXIT_FAILURE); ++ fail_exit (EXIT_FAILURE, process_selinux); + } + spw_locked = true; + } +@@ -887,7 +892,7 @@ static void open_files (void) + fprintf (stderr, + _("%s: cannot lock %s; try again later.\n"), + Prog, gr_dbname ()); +- fail_exit (EXIT_FAILURE); ++ fail_exit (EXIT_FAILURE, process_selinux); + } + gr_locked = true; + #ifdef SHADOWGRP +@@ -896,7 +901,7 @@ static void open_files (void) + fprintf (stderr, + _("%s: cannot lock %s; try again later.\n"), + Prog, sgr_dbname ()); +- fail_exit (EXIT_FAILURE); ++ fail_exit (EXIT_FAILURE, process_selinux); + } + sgr_locked = true; + } +@@ -907,7 +912,7 @@ static void open_files (void) + fprintf (stderr, + _("%s: cannot lock %s; try again later.\n"), + Prog, sub_uid_dbname ()); +- fail_exit (EXIT_FAILURE); ++ fail_exit (EXIT_FAILURE, process_selinux); + } + sub_uid_locked = true; + } +@@ -916,7 +921,7 @@ static void open_files (void) + fprintf (stderr, + _("%s: cannot lock %s; try again later.\n"), + Prog, sub_gid_dbname ()); +- fail_exit (EXIT_FAILURE); ++ fail_exit (EXIT_FAILURE, process_selinux); + } + sub_gid_locked = true; + } +@@ -924,20 +929,20 @@ static void open_files (void) + + if (pw_open (O_CREAT | O_RDWR) == 0) { + fprintf (stderr, _("%s: cannot open %s\n"), Prog, pw_dbname ()); +- fail_exit (EXIT_FAILURE); ++ fail_exit (EXIT_FAILURE, process_selinux); + } + if (is_shadow && (spw_open (O_CREAT | O_RDWR) == 0)) { + fprintf (stderr, _("%s: cannot open %s\n"), Prog, spw_dbname ()); +- fail_exit (EXIT_FAILURE); ++ fail_exit (EXIT_FAILURE, process_selinux); + } + if (gr_open (O_CREAT | O_RDWR) == 0) { + fprintf (stderr, _("%s: cannot open %s\n"), Prog, gr_dbname ()); +- fail_exit (EXIT_FAILURE); ++ fail_exit (EXIT_FAILURE, process_selinux); + } + #ifdef SHADOWGRP + if (is_shadow_grp && (sgr_open (O_CREAT | O_RDWR) == 0)) { + fprintf (stderr, _("%s: cannot open %s\n"), Prog, sgr_dbname ()); +- fail_exit (EXIT_FAILURE); ++ fail_exit (EXIT_FAILURE, process_selinux); + } + #endif + #ifdef ENABLE_SUBIDS +@@ -946,7 +951,7 @@ static void open_files (void) + fprintf (stderr, + _("%s: cannot open %s\n"), + Prog, sub_uid_dbname ()); +- fail_exit (EXIT_FAILURE); ++ fail_exit (EXIT_FAILURE, process_selinux); + } + } + if (is_sub_gid) { +@@ -954,7 +959,7 @@ static void open_files (void) + fprintf (stderr, + _("%s: cannot open %s\n"), + Prog, sub_gid_dbname ()); +- fail_exit (EXIT_FAILURE); ++ fail_exit (EXIT_FAILURE, process_selinux); + } + } + #endif /* ENABLE_SUBIDS */ +@@ -963,14 +968,18 @@ static void open_files (void) + /* + * close_files - close and unlock the password, group and shadow databases + */ +-static void close_files (void) ++static void close_files (struct option_flags *flags) + { +- if (pw_close (true) == 0) { ++ bool process_selinux; ++ ++ process_selinux = !flags->chroot; ++ ++ if (pw_close (process_selinux) == 0) { + fprintf (stderr, _("%s: failure while writing changes to %s\n"), Prog, pw_dbname ()); + SYSLOG ((LOG_ERR, "failure while writing changes to %s", pw_dbname ())); +- fail_exit (EXIT_FAILURE); ++ fail_exit (EXIT_FAILURE, process_selinux); + } +- if (pw_unlock (true) == 0) { ++ if (pw_unlock (process_selinux) == 0) { + fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, pw_dbname ()); + SYSLOG ((LOG_ERR, "failed to unlock %s", pw_dbname ())); + /* continue */ +@@ -978,14 +987,14 @@ static void close_files (void) + pw_locked = false; + + if (is_shadow) { +- if (spw_close (true) == 0) { ++ if (spw_close (process_selinux) == 0) { + fprintf (stderr, + _("%s: failure while writing changes to %s\n"), + Prog, spw_dbname ()); + SYSLOG ((LOG_ERR, "failure while writing changes to %s", spw_dbname ())); +- fail_exit (EXIT_FAILURE); ++ fail_exit (EXIT_FAILURE, process_selinux); + } +- if (spw_unlock (true) == 0) { ++ if (spw_unlock (process_selinux) == 0) { + fprintf (stderr, + _("%s: failed to unlock %s\n"), + Prog, spw_dbname ()); +@@ -995,29 +1004,29 @@ static void close_files (void) + spw_locked = false; + } + +- if (gr_close (true) == 0) { ++ if (gr_close (process_selinux) == 0) { + fprintf (stderr, + _("%s: failure while writing changes to %s\n"), + Prog, gr_dbname ()); + SYSLOG ((LOG_ERR, "failure while writing changes to %s", gr_dbname ())); +- fail_exit (EXIT_FAILURE); ++ fail_exit (EXIT_FAILURE, process_selinux); + } + #ifdef ENABLE_SUBIDS +- if (is_sub_uid && (sub_uid_close (true) == 0)) { ++ if (is_sub_uid && (sub_uid_close (process_selinux) == 0)) { + fprintf (stderr, + _("%s: failure while writing changes to %s\n"), Prog, sub_uid_dbname ()); + SYSLOG ((LOG_ERR, "failure while writing changes to %s", sub_uid_dbname ())); +- fail_exit (EXIT_FAILURE); ++ fail_exit (EXIT_FAILURE, process_selinux); + } +- if (is_sub_gid && (sub_gid_close (true) == 0)) { ++ if (is_sub_gid && (sub_gid_close (process_selinux) == 0)) { + fprintf (stderr, + _("%s: failure while writing changes to %s\n"), Prog, sub_gid_dbname ()); + SYSLOG ((LOG_ERR, "failure while writing changes to %s", sub_gid_dbname ())); +- fail_exit (EXIT_FAILURE); ++ fail_exit (EXIT_FAILURE, process_selinux); + } + #endif /* ENABLE_SUBIDS */ + +- if (gr_unlock (true) == 0) { ++ if (gr_unlock (process_selinux) == 0) { + fprintf (stderr, + _("%s: failed to unlock %s\n"), + Prog, gr_dbname ()); +@@ -1028,14 +1037,14 @@ static void close_files (void) + + #ifdef SHADOWGRP + if (is_shadow_grp) { +- if (sgr_close (true) == 0) { ++ if (sgr_close (process_selinux) == 0) { + fprintf (stderr, + _("%s: failure while writing changes to %s\n"), + Prog, sgr_dbname ()); + SYSLOG ((LOG_ERR, "failure while writing changes to %s", sgr_dbname ())); +- fail_exit (EXIT_FAILURE); ++ fail_exit (EXIT_FAILURE, process_selinux); + } +- if (sgr_unlock (true) == 0) { ++ if (sgr_unlock (process_selinux) == 0) { + fprintf (stderr, + _("%s: failed to unlock %s\n"), + Prog, sgr_dbname ()); +@@ -1047,7 +1056,7 @@ static void close_files (void) + #endif + #ifdef ENABLE_SUBIDS + if (is_sub_uid) { +- if (sub_uid_unlock (true) == 0) { ++ if (sub_uid_unlock (process_selinux) == 0) { + fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, sub_uid_dbname ()); + SYSLOG ((LOG_ERR, "failed to unlock %s", sub_uid_dbname ())); + /* continue */ +@@ -1055,7 +1064,7 @@ static void close_files (void) + sub_uid_locked = false; + } + if (is_sub_gid) { +- if (sub_gid_unlock (true) == 0) { ++ if (sub_gid_unlock (process_selinux) == 0) { + fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, sub_gid_dbname ()); + SYSLOG ((LOG_ERR, "failed to unlock %s", sub_gid_dbname ())); + /* continue */ +@@ -1100,6 +1109,8 @@ int main (int argc, char **argv) + size_t nusers = 0; + #endif /* USE_PAM */ + #endif /* ACCT_TOOLS_SETUID */ ++ struct option_flags flags; ++ bool process_selinux; + + log_set_progname(Prog); + log_set_logfd(stderr); +@@ -1113,7 +1124,8 @@ int main (int argc, char **argv) + + OPENLOG ("newusers"); + +- process_flags (argc, argv); ++ process_flags (argc, argv, &flags); ++ process_selinux = !flags.chroot; + + check_perms (); + +@@ -1127,7 +1139,7 @@ int main (int argc, char **argv) + is_sub_gid = sub_gid_file_present () && !rflg; + #endif /* ENABLE_SUBIDS */ + +- open_files (); ++ open_files (process_selinux); + + /* + * Read each line. The line has the same format as a password file +@@ -1143,13 +1155,13 @@ int main (int argc, char **argv) + if (stpsep(buf, "\n") == NULL && feof(stdin) == 0) { + fprintf (stderr, _("%s: line %jd: line too long\n"), + Prog, line); +- fail_exit (EXIT_FAILURE); ++ fail_exit (EXIT_FAILURE, process_selinux); + } + + if (STRSEP2ARR(buf, ":", fields) == -1) { + fprintf (stderr, _("%s: line %jd: invalid line\n"), + Prog, line); +- fail_exit (EXIT_FAILURE); ++ fail_exit (EXIT_FAILURE, process_selinux); + } + + /* +@@ -1161,14 +1173,14 @@ int main (int argc, char **argv) + fprintf (stderr, + _("%s: cannot update the entry of user %s (not in the passwd database)\n"), + Prog, fields[0]); +- fail_exit (EXIT_FAILURE); ++ fail_exit (EXIT_FAILURE, process_selinux); + } + + if (NULL == pw && get_user_id(fields[2], &uid) != 0) { + fprintf (stderr, + _("%s: line %jd: can't create user\n"), + Prog, line); +- fail_exit (EXIT_FAILURE); ++ fail_exit (EXIT_FAILURE, process_selinux); + } + + /* +@@ -1188,7 +1200,7 @@ int main (int argc, char **argv) + fprintf (stderr, + _("%s: line %jd: can't create group\n"), + Prog, line); +- fail_exit (EXIT_FAILURE); ++ fail_exit (EXIT_FAILURE, process_selinux); + } + + /* +@@ -1203,7 +1215,7 @@ int main (int argc, char **argv) + fprintf (stderr, + _("%s: line %jd: can't create user\n"), + Prog, line); +- fail_exit (EXIT_FAILURE); ++ fail_exit (EXIT_FAILURE, process_selinux); + } + + /* +@@ -1215,7 +1227,7 @@ int main (int argc, char **argv) + fprintf (stderr, + _("%s: line %jd: user '%s' does not exist in %s\n"), + Prog, line, fields[0], pw_dbname ()); +- fail_exit (EXIT_FAILURE); ++ fail_exit (EXIT_FAILURE, process_selinux); + } + newpw = *pw; + +@@ -1230,7 +1242,7 @@ int main (int argc, char **argv) + fprintf (stderr, + _("%s: line %jd: %s\n"), + Prog, line, strerror(errno)); +- fail_exit (EXIT_FAILURE); ++ fail_exit (EXIT_FAILURE, process_selinux); + } + lines[nusers-1] = line; + usernames[nusers-1] = xstrdup(fields[0]); +@@ -1241,7 +1253,7 @@ int main (int argc, char **argv) + fprintf (stderr, + _("%s: line %jd: can't update password\n"), + Prog, line); +- fail_exit (EXIT_FAILURE); ++ fail_exit (EXIT_FAILURE, process_selinux); + } + if (!streq(fields[4], "")) { + newpw.pw_gecos = fields[4]; +@@ -1264,7 +1276,7 @@ int main (int argc, char **argv) + fprintf(stderr, + _("%s: line %jd: homedir must be an absolute path\n"), + Prog, line); +- fail_exit (EXIT_FAILURE); ++ fail_exit (EXIT_FAILURE, process_selinux); + } + if (mkdir (newpw.pw_dir, mode) != 0) { + fprintf (stderr, +@@ -1272,7 +1284,7 @@ int main (int argc, char **argv) + Prog, line, newpw.pw_dir, + strerror (errno)); + if (errno != EEXIST) { +- fail_exit (EXIT_FAILURE); ++ fail_exit (EXIT_FAILURE, process_selinux); + } + } + if (chown(newpw.pw_dir, newpw.pw_uid, newpw.pw_gid) != 0) +@@ -1281,7 +1293,7 @@ int main (int argc, char **argv) + _("%s: line %jd: chown %s failed: %s\n"), + Prog, line, newpw.pw_dir, + strerror (errno)); +- fail_exit (EXIT_FAILURE); ++ fail_exit (EXIT_FAILURE, process_selinux); + } + } + +@@ -1292,7 +1304,7 @@ int main (int argc, char **argv) + fprintf (stderr, + _("%s: line %jd: can't update entry\n"), + Prog, line); +- fail_exit (EXIT_FAILURE); ++ fail_exit (EXIT_FAILURE, process_selinux); + } + + #ifdef ENABLE_SUBIDS +@@ -1307,14 +1319,14 @@ int main (int argc, char **argv) + fprintf (stderr, + _("%s: can't find subordinate user range\n"), + Prog); +- fail_exit (EXIT_FAILURE); ++ fail_exit (EXIT_FAILURE, process_selinux); + } + if (sub_uid_add(fields[0], sub_uid_start, sub_uid_count) == 0) + { + fprintf (stderr, + _("%s: failed to prepare new %s entry\n"), + Prog, sub_uid_dbname ()); +- fail_exit (EXIT_FAILURE); ++ fail_exit (EXIT_FAILURE, process_selinux); + } + } + +@@ -1328,13 +1340,13 @@ int main (int argc, char **argv) + fprintf (stderr, + _("%s: can't find subordinate group range\n"), + Prog); +- fail_exit (EXIT_FAILURE); ++ fail_exit (EXIT_FAILURE, process_selinux); + } + if (sub_gid_add(fields[0], sub_gid_start, sub_gid_count) == 0) { + fprintf (stderr, + _("%s: failed to prepare new %s entry\n"), + Prog, sub_uid_dbname ()); +- fail_exit (EXIT_FAILURE); ++ fail_exit (EXIT_FAILURE, process_selinux); + } + } + #endif /* ENABLE_SUBIDS */ +@@ -1347,7 +1359,7 @@ int main (int argc, char **argv) + * changes to be written out all at once, and then unlocked + * afterwards. + */ +- close_files (); ++ close_files (&flags); + + nscd_flush_cache ("passwd"); + nscd_flush_cache ("group"); +diff -up shadow-4.18.0/src/useradd.c.test-chroot2 shadow-4.18.0/src/useradd.c +--- shadow-4.18.0/src/useradd.c.test-chroot2 2025-07-08 11:22:16.257383838 +0200 ++++ shadow-4.18.0/src/useradd.c 2025-07-08 11:22:16.261386737 +0200 +@@ -92,6 +92,15 @@ + #ifndef LASTLOG_FILE + #define LASTLOG_FILE "/var/log/lastlog" + #endif ++ ++/* ++ * Structures ++ */ ++struct option_flags { ++ bool chroot; ++ bool prefix; ++}; ++ + /* + * Global variables + */ +@@ -219,40 +228,41 @@ static bool home_added = false; + #define DLOG_INIT "LOG_INIT" + + /* local function prototypes */ +-NORETURN static void fail_exit (int); +-static void get_defaults (void); ++NORETURN static void fail_exit (int, bool); ++static void get_defaults (struct option_flags *); + static void show_defaults (void); + static int set_defaults (void); +-static int get_groups (char *); +-static struct group * get_local_group (char * grp_name); ++static int get_groups (char *, struct option_flags *); ++static struct group * get_local_group (char * grp_name, bool process_selinux); + NORETURN static void usage (int status); + static void new_pwent (struct passwd *); + + static void new_spent (struct spwd *); +-static void grp_update (void); ++static void grp_update (bool); + +-static void process_flags (int argc, char **argv); +-static void close_files (void); +-static void close_group_files (void); +-static void unlock_group_files (void); +-static void open_files (void); +-static void open_group_files (void); +-static void open_shadow (void); ++static void process_flags (int argc, char **argv, struct option_flags *flags); ++static void close_files (struct option_flags *flags); ++static void close_group_files (bool process_selinux); ++static void unlock_group_files (bool process_selinux); ++static void open_files (bool process_selinux); ++static void open_group_files (bool process_selinux); ++static void open_shadow (bool process_selinux); + static void faillog_reset (uid_t); + #ifdef ENABLE_LASTLOG + static void lastlog_reset (uid_t); + #endif /* ENABLE_LASTLOG */ + static void tallylog_reset (const char *); +-static void usr_update (unsigned long subuid_count, unsigned long subgid_count); +-static void create_home (void); +-static void create_mail (void); ++static void usr_update (unsigned long subuid_count, unsigned long subgid_count, ++ struct option_flags *flags); ++static void create_home (struct option_flags *flags); ++static void create_mail (struct option_flags *flags); + static void check_uid_range(int rflg, uid_t user_id); + + + /* + * fail_exit - undo as much as possible + */ +-static void fail_exit (int code) ++static void fail_exit (int code, bool process_selinux) + { + #ifdef WITH_AUDIT + int type; +@@ -265,35 +275,35 @@ static void fail_exit (int code) + SYSLOG((LOG_ERR, "failed to remove %s", prefix_user_home)); + } + +- if (spw_locked && spw_unlock(true) == 0) { ++ if (spw_locked && spw_unlock(process_selinux) == 0) { + fprintf(stderr, _("%s: failed to unlock %s\n"), Prog, spw_dbname()); + SYSLOG((LOG_ERR, "failed to unlock %s", spw_dbname())); + /* continue */ + } +- if (pw_locked && pw_unlock(true) == 0) { ++ if (pw_locked && pw_unlock(process_selinux) == 0) { + fprintf(stderr, _("%s: failed to unlock %s\n"), Prog, pw_dbname()); + SYSLOG((LOG_ERR, "failed to unlock %s", pw_dbname())); + /* continue */ + } +- if (gr_locked && gr_unlock(true) == 0) { ++ if (gr_locked && gr_unlock(process_selinux) == 0) { + fprintf(stderr, _("%s: failed to unlock %s\n"), Prog, gr_dbname()); + SYSLOG((LOG_ERR, "failed to unlock %s", gr_dbname())); + /* continue */ + } + #ifdef SHADOWGRP +- if (sgr_locked && sgr_unlock(true) == 0) { ++ if (sgr_locked && sgr_unlock(process_selinux) == 0) { + fprintf(stderr, _("%s: failed to unlock %s\n"), Prog, sgr_dbname()); + SYSLOG((LOG_ERR, "failed to unlock %s", sgr_dbname())); + /* continue */ + } + #endif + #ifdef ENABLE_SUBIDS +- if (sub_uid_locked && sub_uid_unlock(true) == 0) { ++ if (sub_uid_locked && sub_uid_unlock(process_selinux) == 0) { + fprintf(stderr, _("%s: failed to unlock %s\n"), Prog, sub_uid_dbname()); + SYSLOG((LOG_ERR, "failed to unlock %s", sub_uid_dbname())); + /* continue */ + } +- if (sub_gid_locked && sub_gid_unlock(true) == 0) { ++ if (sub_gid_locked && sub_gid_unlock(process_selinux) == 0) { + fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, sub_gid_dbname()); + SYSLOG ((LOG_ERR, "failed to unlock %s", sub_gid_dbname())); + /* continue */ +@@ -322,7 +332,7 @@ static void fail_exit (int code) + * file does not exist. + */ + static void +-get_defaults(void) ++get_defaults(struct option_flags *flags) + { + FILE *fp; + char *default_file = USER_DEFAULTS_FILE; +@@ -377,7 +387,7 @@ get_defaults(void) + ccp = cp; + + if (streq(buf, DGROUPS)) { +- if (get_groups (cp) != 0) { ++ if (get_groups (cp, flags) != 0) { + fprintf (stderr, + _("%s: the '%s=' configuration in %s has an invalid group, ignoring the bad group\n"), + Prog, DGROUPS, default_file); +@@ -736,11 +746,14 @@ err_free_new: + * converts it to a NULL-terminated array. Any unknown group + * names are reported as errors. + */ +-static int get_groups (char *list) ++static int get_groups (char *list, struct option_flags *flags) + { + struct group *grp; + bool errors = false; + int ngroups = 0; ++ bool process_selinux; ++ ++ process_selinux = !flags->chroot && !flags->prefix; + + /* + * Free previous group list before creating a new one. +@@ -758,7 +771,7 @@ static int get_groups (char *list) + /* + * Open the group files + */ +- open_group_files (); ++ open_group_files (process_selinux); + + /* + * So long as there is some data to be converted, strip off +@@ -777,7 +790,7 @@ static int get_groups (char *list) + * Names starting with digits are treated as numerical + * GID values, otherwise the string is looked up as is. + */ +- grp = get_local_group(g); ++ grp = get_local_group(g, process_selinux); + + /* + * There must be a match, either by GID value or by +@@ -815,8 +828,8 @@ static int get_groups (char *list) + gr_free (grp); + } + +- close_group_files (); +- unlock_group_files (); ++ close_group_files (process_selinux); ++ unlock_group_files (process_selinux); + + user_groups[ngroups] = NULL; + +@@ -837,7 +850,7 @@ static int get_groups (char *list) + * If the name exists the group information is returned, otherwise NULL is + * returned. + */ +-static struct group * get_local_group(char * grp_name) ++static struct group * get_local_group(char * grp_name, bool process_selinux) + { + gid_t gid; + struct group *result_grp = NULL; +@@ -854,7 +867,7 @@ static struct group * get_local_group(ch + fprintf (stderr, + _("%s: Out of memory. Cannot find group '%s'.\n"), + Prog, grp_name); +- fail_exit (E_GRP_UPDATE); ++ fail_exit (E_GRP_UPDATE, process_selinux); + } + } + +@@ -986,7 +999,7 @@ static void new_spent (struct spwd *spen + * close_files() should be called afterwards to commit the changes + * and unlocking the group files. + */ +-static void grp_update (void) ++static void grp_update (bool process_selinux) + { + const struct group *grp; + struct group *ngrp; +@@ -1022,7 +1035,7 @@ static void grp_update (void) + _("%s: Out of memory. Cannot update %s.\n"), + Prog, gr_dbname ()); + SYSLOG ((LOG_ERR, "failed to prepare the new %s entry '%s'", gr_dbname (), user_name)); +- fail_exit (E_GRP_UPDATE); /* XXX */ ++ fail_exit (E_GRP_UPDATE, process_selinux); /* XXX */ + } + + /* +@@ -1035,7 +1048,7 @@ static void grp_update (void) + _("%s: failed to prepare the new %s entry '%s'\n"), + Prog, gr_dbname (), ngrp->gr_name); + SYSLOG ((LOG_ERR, "failed to prepare the new %s entry '%s'", gr_dbname (), user_name)); +- fail_exit (E_GRP_UPDATE); ++ fail_exit (E_GRP_UPDATE, process_selinux); + } + #ifdef WITH_AUDIT + audit_logger_with_group (AUDIT_USER_MGMT, +@@ -1085,7 +1098,7 @@ static void grp_update (void) + _("%s: Out of memory. Cannot update %s.\n"), + Prog, sgr_dbname ()); + SYSLOG ((LOG_ERR, "failed to prepare the new %s entry '%s'", sgr_dbname (), user_name)); +- fail_exit (E_GRP_UPDATE); /* XXX */ ++ fail_exit (E_GRP_UPDATE, process_selinux); /* XXX */ + } + + /* +@@ -1099,7 +1112,7 @@ static void grp_update (void) + Prog, sgr_dbname (), nsgrp->sg_namp); + SYSLOG ((LOG_ERR, "failed to prepare the new %s entry '%s'", sgr_dbname (), user_name)); + +- fail_exit (E_GRP_UPDATE); ++ fail_exit (E_GRP_UPDATE, process_selinux); + } + #ifdef WITH_AUDIT + audit_logger_with_group (AUDIT_USER_MGMT, +@@ -1121,7 +1134,7 @@ static void grp_update (void) + * the values that the user will be created with accordingly. The + * values are checked for sanity. + */ +-static void process_flags (int argc, char **argv) ++static void process_flags (int argc, char **argv, struct option_flags *flags) + { + const struct group *grp; + bool anyflag = false; +@@ -1296,7 +1309,7 @@ static void process_flags (int argc, cha + gflg = true; + break; + case 'G': +- if (get_groups (optarg) != 0) { ++ if (get_groups (optarg, flags) != 0) { + exit (E_NOTFOUND); + } + if (NULL != user_groups[0]) { +@@ -1356,8 +1369,10 @@ static void process_flags (int argc, cha + rflg = true; + break; + case 'R': /* no-op, handled in process_root_flag () */ ++ flags->chroot = true; + break; + case 'P': /* no-op, handled in process_prefix_flag () */ ++ flags->prefix = true; + break; + case 's': + if ( ( !VALID (optarg) ) +@@ -1562,38 +1577,42 @@ static void process_flags (int argc, cha + * close_files() closes all of the files that were opened for this + * new user. This causes any modified entries to be written out. + */ +-static void close_files (void) ++static void close_files (struct option_flags *flags) + { +- if (pw_close (true) == 0) { ++ bool process_selinux; ++ ++ process_selinux = !flags->chroot && !flags->prefix; ++ ++ if (pw_close (process_selinux) == 0) { + fprintf (stderr, _("%s: failure while writing changes to %s\n"), Prog, pw_dbname ()); + SYSLOG ((LOG_ERR, "failure while writing changes to %s", pw_dbname ())); +- fail_exit (E_PW_UPDATE); ++ fail_exit (E_PW_UPDATE, process_selinux); + } +- if (is_shadow_pwd && (spw_close (true) == 0)) { ++ if (is_shadow_pwd && (spw_close (process_selinux) == 0)) { + fprintf (stderr, + _("%s: failure while writing changes to %s\n"), Prog, spw_dbname ()); + SYSLOG ((LOG_ERR, "failure while writing changes to %s", spw_dbname ())); +- fail_exit (E_PW_UPDATE); ++ fail_exit (E_PW_UPDATE, process_selinux); + } + +- close_group_files (); ++ close_group_files (process_selinux); + + #ifdef ENABLE_SUBIDS +- if (is_sub_uid && (sub_uid_close (true) == 0)) { ++ if (is_sub_uid && (sub_uid_close (process_selinux) == 0)) { + fprintf (stderr, + _("%s: failure while writing changes to %s\n"), Prog, sub_uid_dbname ()); + SYSLOG ((LOG_ERR, "failure while writing changes to %s", sub_uid_dbname ())); +- fail_exit (E_SUB_UID_UPDATE); ++ fail_exit (E_SUB_UID_UPDATE, process_selinux); + } +- if (is_sub_gid && (sub_gid_close (true) == 0)) { ++ if (is_sub_gid && (sub_gid_close (process_selinux) == 0)) { + fprintf (stderr, + _("%s: failure while writing changes to %s\n"), Prog, sub_gid_dbname ()); + SYSLOG ((LOG_ERR, "failure while writing changes to %s", sub_gid_dbname ())); +- fail_exit (E_SUB_GID_UPDATE); ++ fail_exit (E_SUB_GID_UPDATE, process_selinux); + } + #endif /* ENABLE_SUBIDS */ + if (is_shadow_pwd) { +- if (spw_unlock (true) == 0) { ++ if (spw_unlock (process_selinux) == 0) { + fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, spw_dbname ()); + SYSLOG ((LOG_ERR, "failed to unlock %s", spw_dbname ())); + #ifdef WITH_AUDIT +@@ -1606,7 +1625,7 @@ static void close_files (void) + } + spw_locked = false; + } +- if (pw_unlock (true) == 0) { ++ if (pw_unlock (process_selinux) == 0) { + fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, pw_dbname ()); + SYSLOG ((LOG_ERR, "failed to unlock %s", pw_dbname ())); + #ifdef WITH_AUDIT +@@ -1619,11 +1638,11 @@ static void close_files (void) + } + pw_locked = false; + +- unlock_group_files (); ++ unlock_group_files (process_selinux); + + #ifdef ENABLE_SUBIDS + if (is_sub_uid) { +- if (sub_uid_unlock (true) == 0) { ++ if (sub_uid_unlock (process_selinux) == 0) { + fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, sub_uid_dbname ()); + SYSLOG ((LOG_ERR, "failed to unlock %s", sub_uid_dbname ())); + #ifdef WITH_AUDIT +@@ -1637,7 +1656,7 @@ static void close_files (void) + sub_uid_locked = false; + } + if (is_sub_gid) { +- if (sub_gid_unlock (true) == 0) { ++ if (sub_gid_unlock (process_selinux) == 0) { + fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, sub_gid_dbname ()); + SYSLOG ((LOG_ERR, "failed to unlock %s", sub_gid_dbname ())); + #ifdef WITH_AUDIT +@@ -1659,25 +1678,25 @@ static void close_files (void) + * close_group_files() closes all of the files that were opened related + * with groups. This causes any modified entries to be written out. + */ +-static void close_group_files (void) ++static void close_group_files (bool process_selinux) + { + if (!do_grp_update) + return; + +- if (gr_close(true) == 0) { ++ if (gr_close(process_selinux) == 0) { + fprintf(stderr, + _("%s: failure while writing changes to %s\n"), + Prog, gr_dbname()); + SYSLOG((LOG_ERR, "failure while writing changes to %s", gr_dbname())); +- fail_exit(E_GRP_UPDATE); ++ fail_exit(E_GRP_UPDATE, process_selinux); + } + #ifdef SHADOWGRP +- if (is_shadow_grp && sgr_close(true) == 0) { ++ if (is_shadow_grp && sgr_close(process_selinux) == 0) { + fprintf(stderr, + _("%s: failure while writing changes to %s\n"), + Prog, sgr_dbname()); + SYSLOG((LOG_ERR, "failure while writing changes to %s", sgr_dbname())); +- fail_exit(E_GRP_UPDATE); ++ fail_exit(E_GRP_UPDATE, process_selinux); + } + #endif /* SHADOWGRP */ + } +@@ -1688,9 +1707,9 @@ static void close_group_files (void) + * unlock_group_files() unlocks all of the files that were locked related + * with groups. This causes any modified entries to be written out. + */ +-static void unlock_group_files (void) ++static void unlock_group_files (bool process_selinux) + { +- if (gr_unlock (true) == 0) { ++ if (gr_unlock (process_selinux) == 0) { + fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, gr_dbname ()); + SYSLOG ((LOG_ERR, "failed to unlock %s", gr_dbname ())); + #ifdef WITH_AUDIT +@@ -1704,7 +1723,7 @@ static void unlock_group_files (void) + gr_locked = false; + #ifdef SHADOWGRP + if (is_shadow_grp) { +- if (sgr_unlock (true) == 0) { ++ if (sgr_unlock (process_selinux) == 0) { + fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, sgr_dbname ()); + SYSLOG ((LOG_ERR, "failed to unlock %s", sgr_dbname ())); + #ifdef WITH_AUDIT +@@ -1725,7 +1744,7 @@ static void unlock_group_files (void) + * + * open_files() opens the two password files. + */ +-static void open_files (void) ++static void open_files (bool process_selinux) + { + if (pw_lock () == 0) { + fprintf (stderr, +@@ -1736,12 +1755,12 @@ static void open_files (void) + pw_locked = true; + if (pw_open (O_CREAT | O_RDWR) == 0) { + fprintf (stderr, _("%s: cannot open %s\n"), Prog, pw_dbname ()); +- fail_exit (E_PW_UPDATE); ++ fail_exit (E_PW_UPDATE, process_selinux); + } + + /* shadow file will be opened by open_shadow(); */ + +- open_group_files (); ++ open_group_files (process_selinux); + + #ifdef ENABLE_SUBIDS + if (is_sub_uid) { +@@ -1749,14 +1768,14 @@ static void open_files (void) + fprintf (stderr, + _("%s: cannot lock %s; try again later.\n"), + Prog, sub_uid_dbname ()); +- fail_exit (E_SUB_UID_UPDATE); ++ fail_exit (E_SUB_UID_UPDATE, process_selinux); + } + sub_uid_locked = true; + if (sub_uid_open (O_CREAT | O_RDWR) == 0) { + fprintf (stderr, + _("%s: cannot open %s\n"), + Prog, sub_uid_dbname ()); +- fail_exit (E_SUB_UID_UPDATE); ++ fail_exit (E_SUB_UID_UPDATE, process_selinux); + } + } + if (is_sub_gid) { +@@ -1764,31 +1783,31 @@ static void open_files (void) + fprintf (stderr, + _("%s: cannot lock %s; try again later.\n"), + Prog, sub_gid_dbname ()); +- fail_exit (E_SUB_GID_UPDATE); ++ fail_exit (E_SUB_GID_UPDATE, process_selinux); + } + sub_gid_locked = true; + if (sub_gid_open (O_CREAT | O_RDWR) == 0) { + fprintf (stderr, + _("%s: cannot open %s\n"), + Prog, sub_gid_dbname ()); +- fail_exit (E_SUB_GID_UPDATE); ++ fail_exit (E_SUB_GID_UPDATE, process_selinux); + } + } + #endif /* ENABLE_SUBIDS */ + } + +-static void open_group_files (void) ++static void open_group_files (bool process_selinux) + { + if (gr_lock () == 0) { + fprintf (stderr, + _("%s: cannot lock %s; try again later.\n"), + Prog, gr_dbname ()); +- fail_exit (E_GRP_UPDATE); ++ fail_exit (E_GRP_UPDATE, process_selinux); + } + gr_locked = true; + if (gr_open (O_CREAT | O_RDWR) == 0) { + fprintf (stderr, _("%s: cannot open %s\n"), Prog, gr_dbname ()); +- fail_exit (E_GRP_UPDATE); ++ fail_exit (E_GRP_UPDATE, process_selinux); + } + + #ifdef SHADOWGRP +@@ -1797,20 +1816,20 @@ static void open_group_files (void) + fprintf (stderr, + _("%s: cannot lock %s; try again later.\n"), + Prog, sgr_dbname ()); +- fail_exit (E_GRP_UPDATE); ++ fail_exit (E_GRP_UPDATE, process_selinux); + } + sgr_locked = true; + if (sgr_open (O_CREAT | O_RDWR) == 0) { + fprintf (stderr, + _("%s: cannot open %s\n"), + Prog, sgr_dbname ()); +- fail_exit (E_GRP_UPDATE); ++ fail_exit (E_GRP_UPDATE, process_selinux); + } + } + #endif /* SHADOWGRP */ + } + +-static void open_shadow (void) ++static void open_shadow (bool process_selinux) + { + if (!is_shadow_pwd) { + return; +@@ -1819,14 +1838,14 @@ static void open_shadow (void) + fprintf (stderr, + _("%s: cannot lock %s; try again later.\n"), + Prog, spw_dbname ()); +- fail_exit (E_PW_UPDATE); ++ fail_exit (E_PW_UPDATE, process_selinux); + } + spw_locked = true; + if (spw_open (O_CREAT | O_RDWR) == 0) { + fprintf (stderr, + _("%s: cannot open %s\n"), + Prog, spw_dbname ()); +- fail_exit (E_PW_UPDATE); ++ fail_exit (E_PW_UPDATE, process_selinux); + } + } + +@@ -1880,7 +1899,7 @@ static void new_sgent (struct sgrp *sgen + * grp_add() writes the new records to the group files. + */ + +-static void grp_add (void) ++static void grp_add (bool process_selinux) + { + struct group grp; + +@@ -1909,7 +1928,7 @@ static void grp_add (void) + grp.gr_name, AUDIT_NO_ID, + SHADOW_AUDIT_FAILURE); + #endif +- fail_exit (E_GRP_UPDATE); ++ fail_exit (E_GRP_UPDATE, process_selinux); + } + #ifdef SHADOWGRP + /* +@@ -1925,7 +1944,7 @@ static void grp_add (void) + grp.gr_name, AUDIT_NO_ID, + SHADOW_AUDIT_FAILURE); + #endif +- fail_exit (E_GRP_UPDATE); ++ fail_exit (E_GRP_UPDATE, process_selinux); + } + #endif /* SHADOWGRP */ + SYSLOG ((LOG_INFO, "new group: name=%s, GID=%u", user_name, user_gid)); +@@ -2074,11 +2093,16 @@ static void tallylog_reset (const char * + * usr_update() creates the password file entries for this user + * and will update the group entries if required. + */ +-static void usr_update (unsigned long subuid_count, unsigned long subgid_count) ++static void ++usr_update (unsigned long subuid_count, unsigned long subgid_count, ++ struct option_flags *flags) + { + struct passwd pwent; + struct spwd spent; + char *tty; ++ bool process_selinux; ++ ++ process_selinux = !flags->chroot && !flags->prefix; + + /* + * Fill in the password structure with any new fields, making +@@ -2119,7 +2143,7 @@ static void usr_update (unsigned long su + fprintf (stderr, + _("%s: failed to prepare the new %s entry '%s'\n"), + Prog, pw_dbname (), pwent.pw_name); +- fail_exit (E_PW_UPDATE); ++ fail_exit (E_PW_UPDATE, process_selinux); + } + + /* +@@ -2129,7 +2153,7 @@ static void usr_update (unsigned long su + fprintf (stderr, + _("%s: failed to prepare the new %s entry '%s'\n"), + Prog, spw_dbname (), spent.sp_namp); +- fail_exit (E_PW_UPDATE); ++ fail_exit (E_PW_UPDATE, process_selinux); + } + #ifdef ENABLE_SUBIDS + if (is_sub_uid && !local_sub_uid_assigned(user_name) && +@@ -2137,14 +2161,14 @@ static void usr_update (unsigned long su + fprintf (stderr, + _("%s: failed to prepare the new %s entry\n"), + Prog, sub_uid_dbname ()); +- fail_exit (E_SUB_UID_UPDATE); ++ fail_exit (E_SUB_UID_UPDATE, process_selinux); + } + if (is_sub_gid && !local_sub_gid_assigned(user_name) && + (sub_gid_add(user_name, sub_gid_start, subgid_count) == 0)) { + fprintf (stderr, + _("%s: failed to prepare the new %s entry\n"), + Prog, sub_uid_dbname ()); +- fail_exit (E_SUB_GID_UPDATE); ++ fail_exit (E_SUB_GID_UPDATE, process_selinux); + } + #endif /* ENABLE_SUBIDS */ + +@@ -2163,7 +2187,7 @@ static void usr_update (unsigned long su + * Do any group file updates for this user. + */ + if (do_grp_update) { +- grp_update (); ++ grp_update (process_selinux); + } + } + +@@ -2174,11 +2198,14 @@ static void usr_update (unsigned long su + * already exist. It will be created mode 755 owned by the user + * with the user's default group. + */ +-static void create_home (void) ++static void create_home (struct option_flags *flags) + { + char path[strlen(prefix_user_home) + 2]; + char *bhome, *cp; + mode_t mode; ++ bool process_selinux; ++ ++ process_selinux = !flags->chroot && !flags->prefix; + + if (access (prefix_user_home, F_OK) == 0) + return; +@@ -2189,15 +2216,17 @@ static void create_home (void) + fprintf(stderr, + _("%s: error while duplicating string %s\n"), + Prog, user_home); +- fail_exit(E_HOMEDIR); ++ fail_exit(E_HOMEDIR, process_selinux); + } + + #ifdef WITH_SELINUX +- if (set_selinux_file_context(prefix_user_home, S_IFDIR) != 0) { +- fprintf(stderr, +- _("%s: cannot set SELinux context for home directory %s\n"), +- Prog, user_home); +- fail_exit(E_HOMEDIR); ++ if (process_selinux) { ++ if (set_selinux_file_context(prefix_user_home, S_IFDIR) != 0) { ++ fprintf(stderr, ++ _("%s: cannot set SELinux context for home directory %s\n"), ++ Prog, user_home); ++ fail_exit(E_HOMEDIR, process_selinux); ++ } + } + #endif + +@@ -2227,14 +2256,14 @@ static void create_home (void) + fprintf(stderr, + _("%s: error while duplicating string in BTRFS check %s\n"), + Prog, path); +- fail_exit(E_HOMEDIR); ++ fail_exit(E_HOMEDIR, process_selinux); + } + stpcpy(&btrfs_check[strlen(path) - strlen(cp) - 1], ""); + if (is_btrfs(btrfs_check) <= 0) { + fprintf(stderr, + _("%s: home directory \"%s\" must be mounted on BTRFS\n"), + Prog, path); +- fail_exit(E_HOMEDIR); ++ fail_exit(E_HOMEDIR, process_selinux); + } + free(btrfs_check); + // make subvolume to mount for user instead of directory +@@ -2242,7 +2271,7 @@ static void create_home (void) + fprintf(stderr, + _("%s: failed to create BTRFS subvolume: %s\n"), + Prog, path); +- fail_exit(E_HOMEDIR); ++ fail_exit(E_HOMEDIR, process_selinux); + } + } + else +@@ -2250,7 +2279,7 @@ static void create_home (void) + if (mkdir(path, 0) != 0) { + fprintf(stderr, _("%s: cannot create directory %s\n"), + Prog, path); +- fail_exit(E_HOMEDIR); ++ fail_exit(E_HOMEDIR, process_selinux); + } + if (chown(path, 0, 0) < 0) { + fprintf(stderr, +@@ -2278,12 +2307,14 @@ static void create_home (void) + user_name, user_id, SHADOW_AUDIT_SUCCESS); + #endif + #ifdef WITH_SELINUX +- /* Reset SELinux to create files with default contexts */ +- if (reset_selinux_file_context() != 0) { +- fprintf(stderr, +- _("%s: cannot reset SELinux file creation context\n"), +- Prog); +- fail_exit(E_HOMEDIR); ++ if (process_selinux) { ++ /* Reset SELinux to create files with default contexts */ ++ if (reset_selinux_file_context() != 0) { ++ fprintf(stderr, ++ _("%s: cannot reset SELinux file creation context\n"), ++ Prog); ++ fail_exit(E_HOMEDIR, process_selinux); ++ } + } + #endif + } +@@ -2295,7 +2326,7 @@ static void create_home (void) + * exist. It will be created mode 660 owned by the user and group + * 'mail' + */ +-static void create_mail (void) ++static void create_mail (struct option_flags *flags) + { + int fd; + char *file; +@@ -2303,6 +2334,9 @@ static void create_mail (void) + mode_t mode; + const char *spool; + struct group *gr; ++ bool process_selinux; ++ ++ process_selinux = !flags->chroot && !flags->prefix; + + if (!strcaseeq(create_mail_spool, "yes")) + return; +@@ -2322,11 +2356,13 @@ static void create_mail (void) + file = xaprintf("%s/%s", spool, user_name); + + #ifdef WITH_SELINUX +- if (set_selinux_file_context(file, S_IFREG) != 0) { +- fprintf(stderr, +- _("%s: cannot set SELinux context for mailbox file %s\n"), +- Prog, file); +- fail_exit(E_MAILBOXFILE); ++ if (process_selinux) { ++ if (set_selinux_file_context(file, S_IFREG) != 0) { ++ fprintf(stderr, ++ _("%s: cannot set SELinux context for mailbox file %s\n"), ++ Prog, file); ++ fail_exit(E_MAILBOXFILE, process_selinux); ++ } + } + #endif + +@@ -2362,12 +2398,14 @@ static void create_mail (void) + perror (_("Closing mailbox file")); + } + #ifdef WITH_SELINUX +- /* Reset SELinux to create files with default contexts */ +- if (reset_selinux_file_context() != 0) { +- fprintf(stderr, +- _("%s: cannot reset SELinux file creation context\n"), +- Prog); +- fail_exit(E_MAILBOXFILE); ++ if (process_selinux) { ++ /* Reset SELinux to create files with default contexts */ ++ if (reset_selinux_file_context() != 0) { ++ fprintf(stderr, ++ _("%s: cannot reset SELinux file creation context\n"), ++ Prog); ++ fail_exit(E_MAILBOXFILE, process_selinux); ++ } + } + #endif + } +@@ -2409,6 +2447,8 @@ int main (int argc, char **argv) + #endif + unsigned long subuid_count = 0; + unsigned long subgid_count = 0; ++ struct option_flags flags; ++ bool process_selinux; + + log_set_progname(Prog); + log_set_logfd(stderr); +@@ -2439,9 +2479,10 @@ int main (int argc, char **argv) + is_shadow_grp = sgr_file_present (); + #endif + +- get_defaults (); ++ get_defaults (&flags); + +- process_flags (argc, argv); ++ process_flags (argc, argv, &flags); ++ process_selinux = !flags.chroot && !flags.prefix; + + #ifdef ENABLE_SUBIDS + uid_min = getdef_ulong ("UID_MIN", 1000UL); +@@ -2470,7 +2511,7 @@ int main (int argc, char **argv) + fprintf (stderr, + _("%s: Cannot determine your user name.\n"), + Prog); +- fail_exit (1); ++ fail_exit (1, process_selinux); + } + + retval = pam_start (Prog, pampw?pampw->pw_name:"root", &conv, &pamh); +@@ -2491,7 +2532,7 @@ int main (int argc, char **argv) + if (NULL != pamh) { + (void) pam_end (pamh, retval); + } +- fail_exit (1); ++ fail_exit (1, process_selinux); + } + (void) pam_end (pamh, retval); + #endif /* USE_PAM */ +@@ -2515,7 +2556,7 @@ int main (int argc, char **argv) + */ + if (prefix_getpwnam (user_name) != NULL) { /* local, no need for xgetpwnam */ + fprintf (stderr, _("%s: user '%s' already exists\n"), Prog, user_name); +- fail_exit (E_NAME_IN_USE); ++ fail_exit (E_NAME_IN_USE, process_selinux); + } + + /* +@@ -2530,7 +2571,7 @@ int main (int argc, char **argv) + fprintf (stderr, + _("%s: group %s exists - if you want to add this user to that group, use -g.\n"), + Prog, user_name); +- fail_exit (E_NAME_IN_USE); ++ fail_exit (E_NAME_IN_USE, process_selinux); + } + } + +@@ -2543,7 +2584,7 @@ int main (int argc, char **argv) + * - flush nscd caches for passwd and group services, + * - then close and update the files. + */ +- open_files (); ++ open_files (process_selinux); + + if (!oflg) { + /* first, seek for a valid uid to use for this user. +@@ -2552,14 +2593,14 @@ int main (int argc, char **argv) + if (!uflg) { + if (find_new_uid (rflg, &user_id, NULL) < 0) { + fprintf (stderr, _("%s: can't create user\n"), Prog); +- fail_exit (E_UID_IN_USE); ++ fail_exit (E_UID_IN_USE, process_selinux); + } + } else { + if (prefix_getpwuid (user_id) != NULL) { + fprintf (stderr, + _("%s: UID %lu is not unique\n"), + Prog, (unsigned long) user_id); +- fail_exit (E_UID_IN_USE); ++ fail_exit (E_UID_IN_USE, process_selinux); + } + } + } +@@ -2572,11 +2613,11 @@ int main (int argc, char **argv) + fprintf (stderr, + _("%s: Failed to create tcb directory for %s\n"), + Prog, user_name); +- fail_exit (E_UID_IN_USE); ++ fail_exit (E_UID_IN_USE, process_selinux); + } + } + #endif +- open_shadow (); ++ open_shadow (process_selinux); + + /* do we have to add a group for that user? This is why we need to + * open the group files in the open_files() function --gafton */ +@@ -2585,9 +2626,9 @@ int main (int argc, char **argv) + fprintf (stderr, + _("%s: can't create group\n"), + Prog); +- fail_exit (4); ++ fail_exit (4, process_selinux); + } +- grp_add (); ++ grp_add (process_selinux); + } + + #ifdef ENABLE_SUBIDS +@@ -2596,7 +2637,7 @@ int main (int argc, char **argv) + fprintf (stderr, + _("%s: can't create subordinate user IDs\n"), + Prog); +- fail_exit(E_SUB_UID_UPDATE); ++ fail_exit(E_SUB_UID_UPDATE, process_selinux); + } + } + if (is_sub_gid && subgid_count != 0) { +@@ -2604,14 +2645,14 @@ int main (int argc, char **argv) + fprintf (stderr, + _("%s: can't create subordinate group IDs\n"), + Prog); +- fail_exit(E_SUB_GID_UPDATE); ++ fail_exit(E_SUB_GID_UPDATE, process_selinux); + } + } + #endif /* ENABLE_SUBIDS */ + +- usr_update (subuid_count, subgid_count); ++ usr_update (subuid_count, subgid_count, &flags); + +- close_files (); ++ close_files (&flags); + + nscd_flush_cache ("passwd"); + nscd_flush_cache ("group"); +@@ -2637,13 +2678,13 @@ int main (int argc, char **argv) + "add-selinux-user-mapping", + user_name, user_id, SHADOW_AUDIT_FAILURE); + #endif /* WITH_AUDIT */ +- fail_exit (E_SE_UPDATE); ++ fail_exit (E_SE_UPDATE, process_selinux); + } + } + #endif /* WITH_SELINUX */ + + if (mflg) { +- create_home (); ++ create_home (&flags); + if (home_added) { + copy_tree (def_template, prefix_user_home, false, true, + (uid_t)-1, user_id, (gid_t)-1, user_gid); +@@ -2660,7 +2701,7 @@ int main (int argc, char **argv) + + /* Do not create mail directory for system accounts */ + if (!rflg) { +- create_mail (); ++ create_mail (&flags); + } + + if (run_parts ("/etc/shadow-maint/useradd-post.d", user_name, +diff -up shadow-4.18.0/src/userdel.c.test-chroot2 shadow-4.18.0/src/userdel.c +--- shadow-4.18.0/src/userdel.c.test-chroot2 2025-07-08 11:22:16.257469516 +0200 ++++ shadow-4.18.0/src/userdel.c 2025-07-08 11:22:16.263226317 +0200 +@@ -73,6 +73,14 @@ + #endif /* ENABLE_SUBIDS */ + + /* ++ * Structures ++ */ ++struct option_flags { ++ bool chroot; ++ bool prefix; ++}; ++ ++/* + * Global variables + */ + static const char Prog[] = "userdel"; +@@ -87,7 +95,6 @@ static bool rflg = false; + #ifdef WITH_SELINUX + static bool Zflg = false; + #endif +-static bool Rflg = false; + + static bool is_shadow_pwd; + +@@ -109,12 +116,12 @@ static const char* prefix = ""; + + /* local function prototypes */ + static void usage (int status); +-static void update_groups (void); +-static void remove_usergroup (void); +-static void close_files (void); +-static void fail_exit (int); +-static void open_files (void); +-static void update_user (void); ++static void update_groups (bool process_selinux); ++static void remove_usergroup (bool process_selinux); ++static void close_files (struct option_flags *flags); ++static void fail_exit (int, bool); ++static void open_files (bool process_selinux); ++static void update_user (bool process_selinux); + static void user_cancel (const char *); + + #ifdef EXTRA_CHECK_HOME_DIR +@@ -162,7 +169,7 @@ static void usage (int status) + * name is their user name) and delete them too (only if USERGROUPS_ENAB + * is enabled). + */ +-static void update_groups (void) ++static void update_groups (bool process_selinux) + { + const struct group *grp; + struct group *ngrp; +@@ -219,7 +226,7 @@ static void update_groups (void) + } + + if (getdef_bool ("USERGROUPS_ENAB")) { +- remove_usergroup (); ++ remove_usergroup (process_selinux); + } + + #ifdef SHADOWGRP +@@ -291,7 +298,7 @@ static void update_groups (void) + * + it has no other members + * + it is not the primary group of any other user + */ +-static void remove_usergroup (void) ++static void remove_usergroup (bool process_selinux) + { + const struct group *grp; + const struct passwd *pwd = NULL; +@@ -346,7 +353,7 @@ static void remove_usergroup (void) + fprintf (stderr, + _("%s: cannot remove entry '%s' from %s\n"), + Prog, user_name, gr_dbname ()); +- fail_exit (E_GRP_UPDATE); ++ fail_exit (E_GRP_UPDATE, process_selinux); + } + + #ifdef WITH_AUDIT +@@ -365,7 +372,7 @@ static void remove_usergroup (void) + fprintf (stderr, + _("%s: cannot remove entry '%s' from %s\n"), + Prog, user_name, sgr_dbname ()); +- fail_exit (E_GRP_UPDATE); ++ fail_exit (E_GRP_UPDATE, process_selinux); + } + #ifdef WITH_AUDIT + audit_logger_with_group (AUDIT_GRP_MGMT, +@@ -388,14 +395,18 @@ static void remove_usergroup (void) + * close_files() closes all of the files that were opened for this + * new user. This causes any modified entries to be written out. + */ +-static void close_files (void) ++static void close_files (struct option_flags *flags) + { +- if (pw_close (true) == 0) { ++ bool process_selinux; ++ ++ process_selinux = !flags->chroot && !flags->prefix; ++ ++ if (pw_close (process_selinux) == 0) { + fprintf (stderr, _("%s: failure while writing changes to %s\n"), Prog, pw_dbname ()); + SYSLOG ((LOG_ERR, "failure while writing changes to %s", pw_dbname ())); +- fail_exit (E_PW_UPDATE); ++ fail_exit (E_PW_UPDATE, process_selinux); + } +- if (pw_unlock (true) == 0) { ++ if (pw_unlock (process_selinux) == 0) { + fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, pw_dbname ()); + SYSLOG ((LOG_ERR, "failed to unlock %s", pw_dbname ())); + /* continue */ +@@ -403,13 +414,13 @@ static void close_files (void) + pw_locked = false; + + if (is_shadow_pwd) { +- if (spw_close (true) == 0) { ++ if (spw_close (process_selinux) == 0) { + fprintf (stderr, + _("%s: failure while writing changes to %s\n"), Prog, spw_dbname ()); + SYSLOG ((LOG_ERR, "failure while writing changes to %s", spw_dbname ())); +- fail_exit (E_PW_UPDATE); ++ fail_exit (E_PW_UPDATE, process_selinux); + } +- if (spw_unlock (true) == 0) { ++ if (spw_unlock (process_selinux) == 0) { + fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, spw_dbname ()); + SYSLOG ((LOG_ERR, "failed to unlock %s", spw_dbname ())); + /* continue */ +@@ -417,12 +428,12 @@ static void close_files (void) + spw_locked = false; + } + +- if (gr_close (true) == 0) { ++ if (gr_close (process_selinux) == 0) { + fprintf (stderr, _("%s: failure while writing changes to %s\n"), Prog, gr_dbname ()); + SYSLOG ((LOG_ERR, "failure while writing changes to %s", gr_dbname ())); +- fail_exit (E_GRP_UPDATE); ++ fail_exit (E_GRP_UPDATE, process_selinux); + } +- if (gr_unlock (true) == 0) { ++ if (gr_unlock (process_selinux) == 0) { + fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, gr_dbname ()); + SYSLOG ((LOG_ERR, "failed to unlock %s", gr_dbname ())); + /* continue */ +@@ -431,14 +442,14 @@ static void close_files (void) + + #ifdef SHADOWGRP + if (is_shadow_grp) { +- if (sgr_close (true) == 0) { ++ if (sgr_close (process_selinux) == 0) { + fprintf (stderr, + _("%s: failure while writing changes to %s\n"), Prog, sgr_dbname ()); + SYSLOG ((LOG_ERR, "failure while writing changes to %s", sgr_dbname ())); +- fail_exit (E_GRP_UPDATE); ++ fail_exit (E_GRP_UPDATE, process_selinux); + } + +- if (sgr_unlock (true) == 0) { ++ if (sgr_unlock (process_selinux) == 0) { + fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, sgr_dbname ()); + SYSLOG ((LOG_ERR, "failed to unlock %s", sgr_dbname ())); + /* continue */ +@@ -449,12 +460,12 @@ static void close_files (void) + + #ifdef ENABLE_SUBIDS + if (is_sub_uid) { +- if (sub_uid_close (true) == 0) { ++ if (sub_uid_close (process_selinux) == 0) { + fprintf (stderr, _("%s: failure while writing changes to %s\n"), Prog, sub_uid_dbname ()); + SYSLOG ((LOG_ERR, "failure while writing changes to %s", sub_uid_dbname ())); +- fail_exit (E_SUB_UID_UPDATE); ++ fail_exit (E_SUB_UID_UPDATE, process_selinux); + } +- if (sub_uid_unlock (true) == 0) { ++ if (sub_uid_unlock (process_selinux) == 0) { + fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, sub_uid_dbname ()); + SYSLOG ((LOG_ERR, "failed to unlock %s", sub_uid_dbname ())); + /* continue */ +@@ -463,12 +474,12 @@ static void close_files (void) + } + + if (is_sub_gid) { +- if (sub_gid_close (true) == 0) { ++ if (sub_gid_close (process_selinux) == 0) { + fprintf (stderr, _("%s: failure while writing changes to %s\n"), Prog, sub_gid_dbname ()); + SYSLOG ((LOG_ERR, "failure while writing changes to %s", sub_gid_dbname ())); +- fail_exit (E_SUB_GID_UPDATE); ++ fail_exit (E_SUB_GID_UPDATE, process_selinux); + } +- if (sub_gid_unlock (true) == 0) { ++ if (sub_gid_unlock (process_selinux) == 0) { + fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, sub_gid_dbname ()); + SYSLOG ((LOG_ERR, "failed to unlock %s", sub_gid_dbname ())); + /* continue */ +@@ -481,24 +492,24 @@ static void close_files (void) + /* + * fail_exit - exit with a failure code after unlocking the files + */ +-static void fail_exit (int code) ++static void fail_exit (int code, bool process_selinux) + { + if (pw_locked) { +- if (pw_unlock (true) == 0) { ++ if (pw_unlock (process_selinux) == 0) { + fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, pw_dbname ()); + SYSLOG ((LOG_ERR, "failed to unlock %s", pw_dbname ())); + /* continue */ + } + } + if (gr_locked) { +- if (gr_unlock (true) == 0) { ++ if (gr_unlock (process_selinux) == 0) { + fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, gr_dbname ()); + SYSLOG ((LOG_ERR, "failed to unlock %s", gr_dbname ())); + /* continue */ + } + } + if (spw_locked) { +- if (spw_unlock (true) == 0) { ++ if (spw_unlock (process_selinux) == 0) { + fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, spw_dbname ()); + SYSLOG ((LOG_ERR, "failed to unlock %s", spw_dbname ())); + /* continue */ +@@ -506,7 +517,7 @@ static void fail_exit (int code) + } + #ifdef SHADOWGRP + if (sgr_locked) { +- if (sgr_unlock (true) == 0) { ++ if (sgr_unlock (process_selinux) == 0) { + fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, sgr_dbname ()); + SYSLOG ((LOG_ERR, "failed to unlock %s", sgr_dbname ())); + /* continue */ +@@ -515,14 +526,14 @@ static void fail_exit (int code) + #endif /* SHADOWGRP */ + #ifdef ENABLE_SUBIDS + if (sub_uid_locked) { +- if (sub_uid_unlock (true) == 0) { ++ if (sub_uid_unlock (process_selinux) == 0) { + fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, sub_uid_dbname ()); + SYSLOG ((LOG_ERR, "failed to unlock %s", sub_uid_dbname ())); + /* continue */ + } + } + if (sub_gid_locked) { +- if (sub_gid_unlock (true) == 0) { ++ if (sub_gid_unlock (process_selinux) == 0) { + fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, sub_gid_dbname ()); + SYSLOG ((LOG_ERR, "failed to unlock %s", sub_gid_dbname ())); + /* continue */ +@@ -545,45 +556,45 @@ static void fail_exit (int code) + * open_files() opens the two password files. + */ + +-static void open_files (void) ++static void open_files (bool process_selinux) + { + if (pw_lock () == 0) { + fprintf (stderr, + _("%s: cannot lock %s; try again later.\n"), + Prog, pw_dbname ()); +- fail_exit (E_PW_UPDATE); ++ fail_exit (E_PW_UPDATE, process_selinux); + } + pw_locked = true; + if (pw_open (O_CREAT | O_RDWR) == 0) { + fprintf (stderr, + _("%s: cannot open %s\n"), Prog, pw_dbname ()); +- fail_exit (E_PW_UPDATE); ++ fail_exit (E_PW_UPDATE, process_selinux); + } + if (is_shadow_pwd) { + if (spw_lock () == 0) { + fprintf (stderr, + _("%s: cannot lock %s; try again later.\n"), + Prog, spw_dbname ()); +- fail_exit (E_PW_UPDATE); ++ fail_exit (E_PW_UPDATE, process_selinux); + } + spw_locked = true; + if (spw_open (O_CREAT | O_RDWR) == 0) { + fprintf (stderr, + _("%s: cannot open %s\n"), + Prog, spw_dbname ()); +- fail_exit (E_PW_UPDATE); ++ fail_exit (E_PW_UPDATE, process_selinux); + } + } + if (gr_lock () == 0) { + fprintf (stderr, + _("%s: cannot lock %s; try again later.\n"), + Prog, gr_dbname ()); +- fail_exit (E_GRP_UPDATE); ++ fail_exit (E_GRP_UPDATE, process_selinux); + } + gr_locked = true; + if (gr_open (O_CREAT | O_RDWR) == 0) { + fprintf (stderr, _("%s: cannot open %s\n"), Prog, gr_dbname ()); +- fail_exit (E_GRP_UPDATE); ++ fail_exit (E_GRP_UPDATE, process_selinux); + } + #ifdef SHADOWGRP + if (is_shadow_grp) { +@@ -591,13 +602,13 @@ static void open_files (void) + fprintf (stderr, + _("%s: cannot lock %s; try again later.\n"), + Prog, sgr_dbname ()); +- fail_exit (E_GRP_UPDATE); ++ fail_exit (E_GRP_UPDATE, process_selinux); + } + sgr_locked= true; + if (sgr_open (O_CREAT | O_RDWR) == 0) { + fprintf (stderr, _("%s: cannot open %s\n"), + Prog, sgr_dbname ()); +- fail_exit (E_GRP_UPDATE); ++ fail_exit (E_GRP_UPDATE, process_selinux); + } + } + #endif /* SHADOWGRP */ +@@ -607,13 +618,13 @@ static void open_files (void) + fprintf (stderr, + _("%s: cannot lock %s; try again later.\n"), + Prog, sub_uid_dbname ()); +- fail_exit (E_SUB_UID_UPDATE); ++ fail_exit (E_SUB_UID_UPDATE, process_selinux); + } + sub_uid_locked = true; + if (sub_uid_open (O_CREAT | O_RDWR) == 0) { + fprintf (stderr, + _("%s: cannot open %s\n"), Prog, sub_uid_dbname ()); +- fail_exit (E_SUB_UID_UPDATE); ++ fail_exit (E_SUB_UID_UPDATE, process_selinux); + } + } + if (is_sub_gid) { +@@ -621,13 +632,13 @@ static void open_files (void) + fprintf (stderr, + _("%s: cannot lock %s; try again later.\n"), + Prog, sub_gid_dbname ()); +- fail_exit (E_SUB_GID_UPDATE); ++ fail_exit (E_SUB_GID_UPDATE, process_selinux); + } + sub_gid_locked = true; + if (sub_gid_open (O_CREAT | O_RDWR) == 0) { + fprintf (stderr, + _("%s: cannot open %s\n"), Prog, sub_gid_dbname ()); +- fail_exit (E_SUB_GID_UPDATE); ++ fail_exit (E_SUB_GID_UPDATE, process_selinux); + } + } + #endif /* ENABLE_SUBIDS */ +@@ -639,13 +650,13 @@ static void open_files (void) + * update_user() deletes the password file entries for this user + * and will update the group entries as required. + */ +-static void update_user (void) ++static void update_user (bool process_selinux) + { + if (pw_remove (user_name) == 0) { + fprintf (stderr, + _("%s: cannot remove entry '%s' from %s\n"), + Prog, user_name, pw_dbname ()); +- fail_exit (E_PW_UPDATE); ++ fail_exit (E_PW_UPDATE, process_selinux); + } + if ( is_shadow_pwd + && (spw_locate (user_name) != NULL) +@@ -653,20 +664,20 @@ static void update_user (void) + fprintf (stderr, + _("%s: cannot remove entry '%s' from %s\n"), + Prog, user_name, spw_dbname ()); +- fail_exit (E_PW_UPDATE); ++ fail_exit (E_PW_UPDATE, process_selinux); + } + #ifdef ENABLE_SUBIDS + if (is_sub_uid && sub_uid_remove(user_name, 0, ULONG_MAX) == 0) { + fprintf (stderr, + _("%s: cannot remove entry %lu from %s\n"), + Prog, (unsigned long)user_id, sub_uid_dbname ()); +- fail_exit (E_SUB_UID_UPDATE); ++ fail_exit (E_SUB_UID_UPDATE, process_selinux); + } + if (is_sub_gid && sub_gid_remove(user_name, 0, ULONG_MAX) == 0) { + fprintf (stderr, + _("%s: cannot remove entry %lu from %s\n"), + Prog, (unsigned long)user_id, sub_gid_dbname ()); +- fail_exit (E_SUB_GID_UPDATE); ++ fail_exit (E_SUB_GID_UPDATE, process_selinux); + } + #endif /* ENABLE_SUBIDS */ + #ifdef WITH_AUDIT +@@ -905,6 +916,8 @@ int main (int argc, char **argv) + int retval; + #endif /* USE_PAM */ + #endif /* ACCT_TOOLS_SETUID */ ++ struct option_flags flags; ++ bool process_selinux; + + log_set_progname(Prog); + log_set_logfd(stderr); +@@ -954,9 +967,10 @@ int main (int argc, char **argv) + rflg = true; + break; + case 'R': /* no-op, handled in process_root_flag () */ +- Rflg = true; ++ flags.chroot = true; + break; + case 'P': /* no-op, handled in process_prefix_flag () */ ++ flags.prefix = true; + break; + #ifdef WITH_SELINUX + case 'Z': +@@ -982,6 +996,7 @@ int main (int argc, char **argv) + } + } + } ++ process_selinux = !flags.chroot && !flags.prefix; + + if ((optind + 1) != argc) { + usage (E_USAGE); +@@ -1046,7 +1061,7 @@ int main (int argc, char **argv) + pw_open(O_RDONLY); + pwd = pw_locate (user_name); /* we care only about local users */ + if (NULL == pwd) { +- pw_close(true); ++ pw_close(process_selinux); + fprintf (stderr, _("%s: user '%s' does not exist\n"), + Prog, user_name); + #ifdef WITH_AUDIT +@@ -1065,7 +1080,7 @@ int main (int argc, char **argv) + } else { + user_home = xstrdup(pwd->pw_dir); + } +- pw_close(true); ++ pw_close(process_selinux); + } + #ifdef WITH_TCB + if (shadowtcb_set_user (user_name) == SHADOWTCB_FAILURE) { +@@ -1077,7 +1092,7 @@ int main (int argc, char **argv) + * Note: This is a best effort basis. The user may log in between, + * a cron job may be started on her behalf, etc. + */ +- if (streq(prefix, "") && !Rflg && user_busy(user_name, user_id) != 0) { ++ if (streq(prefix, "") && !flags.chroot && user_busy(user_name, user_id) != 0) { + if (!fflg) { + #ifdef WITH_AUDIT + audit_logger (AUDIT_DEL_USER, Prog, +@@ -1093,9 +1108,9 @@ int main (int argc, char **argv) + * Do the hard stuff - open the files, create the user entries, + * create the home directory, then close and update the files. + */ +- open_files (); +- update_user (); +- update_groups (); ++ open_files (process_selinux); ++ update_user (process_selinux); ++ update_groups (process_selinux); + + if (rflg) { + if (remove_mailbox ()) { +@@ -1203,7 +1218,7 @@ int main (int argc, char **argv) + "delete-selinux-user-mapping", + user_name, user_id, SHADOW_AUDIT_FAILURE); + #endif /* WITH_AUDIT */ +- fail_exit (E_SE_UPDATE); ++ fail_exit (E_SE_UPDATE, process_selinux); + } + } + #endif /* WITH_SELINUX */ +@@ -1214,7 +1229,7 @@ int main (int argc, char **argv) + */ + if (streq(prefix, "")) + user_cancel (user_name); +- close_files (); ++ close_files (&flags); + + if (run_parts ("/etc/shadow-maint/userdel-post.d", user_name, "userdel")) { + exit(1); +diff -up shadow-4.18.0/src/usermod.c.test-chroot2 shadow-4.18.0/src/usermod.c +--- shadow-4.18.0/src/usermod.c.test-chroot2 2025-07-08 11:22:16.257549046 +0200 ++++ shadow-4.18.0/src/usermod.c 2025-07-08 11:22:16.262408337 +0200 +@@ -97,6 +97,14 @@ + #define VALID(s) (!strpbrk(s, ":\n")) + + /* ++ * Structures ++ */ ++struct option_flags { ++ bool chroot; ++ bool prefix; ++}; ++ ++/* + * Global variables + */ + static const char Prog[] = "usermod"; +@@ -182,23 +190,23 @@ static bool sub_gid_locked = false; + /* local function prototypes */ + static int get_groups (char *); + NORETURN static void usage (int status); +-static void new_pwent (struct passwd *); +-static void new_spent (struct spwd *); +-NORETURN static void fail_exit (int); +-static void update_group_file(void); +-static void update_group(const struct group *grp); ++static void new_pwent (struct passwd *, bool); ++static void new_spent (struct spwd *, bool); ++NORETURN static void fail_exit (int, bool); ++static void update_group_file(bool); ++static void update_group(const struct group *grp, bool process_selinux); + + #ifdef SHADOWGRP +-static void update_gshadow_file(void); +-static void update_gshadow(const struct sgrp *sgrp); ++static void update_gshadow_file(bool process_selinux); ++static void update_gshadow(const struct sgrp *sgrp, bool process_selinux); + #endif +-static void grp_update (void); ++static void grp_update (bool process_selinux); + +-static void process_flags (int, char **); +-static void close_files (void); +-static void open_files (void); +-static void usr_update (void); +-static void move_home (void); ++static void process_flags (int, char **, struct option_flags *); ++static void close_files (struct option_flags *); ++static void open_files (bool process_selinux); ++static void usr_update (struct option_flags *flags); ++static void move_home (bool process_selinux); + #ifdef ENABLE_LASTLOG + static void update_lastlog (void); + #endif /* ENABLE_LASTLOG */ +@@ -466,7 +474,7 @@ static char *new_pw_passwd (char *pw_pas + * new_pwent() takes all of the values that have been entered and fills + * in a (struct passwd) with them. + */ +-static void new_pwent (struct passwd *pwent) ++static void new_pwent (struct passwd *pwent, bool process_selinux) + { + if (lflg) { + if (pw_locate (user_newname) != NULL) { +@@ -477,7 +485,7 @@ static void new_pwent (struct passwd *pw + fprintf (stderr, + _("%s: user '%s' already exists in %s\n"), + Prog, user_newname, pw_dbname ()); +- fail_exit (E_NAME_IN_USE); ++ fail_exit (E_NAME_IN_USE, process_selinux); + } + #ifdef WITH_AUDIT + audit_logger (AUDIT_USER_MGMT, Prog, +@@ -563,14 +571,14 @@ static void new_pwent (struct passwd *pw + * new_spent() takes all of the values that have been entered and fills + * in a (struct spwd) with them. + */ +-static void new_spent (struct spwd *spent) ++static void new_spent (struct spwd *spent, bool process_selinux) + { + if (lflg) { + if (spw_locate (user_newname) != NULL) { + fprintf (stderr, + _("%s: user '%s' already exists in %s\n"), + Prog, user_newname, spw_dbname ()); +- fail_exit (E_NAME_IN_USE); ++ fail_exit (E_NAME_IN_USE, process_selinux); + } + spent->sp_namp = xstrdup (user_newname); + } +@@ -629,10 +637,10 @@ static void new_spent (struct spwd *spen + */ + NORETURN + static void +-fail_exit (int code) ++fail_exit (int code, bool process_selinux) + { + if (gr_locked) { +- if (gr_unlock (true) == 0) { ++ if (gr_unlock (process_selinux) == 0) { + fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, gr_dbname ()); + SYSLOG ((LOG_ERR, "failed to unlock %s", gr_dbname ())); + /* continue */ +@@ -640,7 +648,7 @@ fail_exit (int code) + } + #ifdef SHADOWGRP + if (sgr_locked) { +- if (sgr_unlock (true) == 0) { ++ if (sgr_unlock (process_selinux) == 0) { + fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, sgr_dbname ()); + SYSLOG ((LOG_ERR, "failed to unlock %s", sgr_dbname ())); + /* continue */ +@@ -648,14 +656,14 @@ fail_exit (int code) + } + #endif + if (spw_locked) { +- if (spw_unlock (true) == 0) { ++ if (spw_unlock (process_selinux) == 0) { + fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, spw_dbname ()); + SYSLOG ((LOG_ERR, "failed to unlock %s", spw_dbname ())); + /* continue */ + } + } + if (pw_locked) { +- if (pw_unlock (true) == 0) { ++ if (pw_unlock (process_selinux) == 0) { + fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, pw_dbname ()); + SYSLOG ((LOG_ERR, "failed to unlock %s", pw_dbname ())); + /* continue */ +@@ -663,14 +671,14 @@ fail_exit (int code) + } + #ifdef ENABLE_SUBIDS + if (sub_uid_locked) { +- if (sub_uid_unlock (true) == 0) { ++ if (sub_uid_unlock (process_selinux) == 0) { + fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, sub_uid_dbname ()); + SYSLOG ((LOG_ERR, "failed to unlock %s", sub_uid_dbname ())); + /* continue */ + } + } + if (sub_gid_locked) { +- if (sub_gid_unlock (true) == 0) { ++ if (sub_gid_unlock (process_selinux) == 0) { + fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, sub_gid_dbname ()); + SYSLOG ((LOG_ERR, "failed to unlock %s", sub_gid_dbname ())); + /* continue */ +@@ -688,7 +696,7 @@ fail_exit (int code) + + + static void +-update_group_file(void) ++update_group_file(bool process_selinux) + { + const struct group *grp; + +@@ -697,12 +705,12 @@ update_group_file(void) + * the user is a member of. + */ + while ((grp = gr_next()) != NULL) +- update_group(grp); ++ update_group(grp, process_selinux); + } + + + static void +-update_group(const struct group *grp) ++update_group(const struct group *grp, bool process_selinux) + { + bool changed; + bool is_member; +@@ -735,7 +743,7 @@ update_group(const struct group *grp) + fprintf (stderr, + _("%s: Out of memory. Cannot update %s.\n"), + Prog, gr_dbname ()); +- fail_exit (E_GRP_UPDATE); ++ fail_exit (E_GRP_UPDATE, process_selinux); + } + + if (was_member) { +@@ -804,7 +812,7 @@ update_group(const struct group *grp) + _("%s: failed to prepare the new %s entry '%s'\n"), + Prog, gr_dbname (), ngrp->gr_name); + SYSLOG ((LOG_WARN, "failed to prepare the new %s entry '%s'", gr_dbname (), ngrp->gr_name)); +- fail_exit (E_GRP_UPDATE); ++ fail_exit (E_GRP_UPDATE, process_selinux); + } + + free_ngrp: +@@ -814,7 +822,7 @@ free_ngrp: + + #ifdef SHADOWGRP + static void +-update_gshadow_file(void) ++update_gshadow_file(bool process_selinux) + { + const struct sgrp *sgrp; + +@@ -823,14 +831,14 @@ update_gshadow_file(void) + * that the user is a member of. + */ + while ((sgrp = sgr_next()) != NULL) +- update_gshadow(sgrp); ++ update_gshadow(sgrp, process_selinux); + } + #endif /* SHADOWGRP */ + + + #ifdef SHADOWGRP + static void +-update_gshadow(const struct sgrp *sgrp) ++update_gshadow(const struct sgrp *sgrp, bool process_selinux) + { + bool changed; + bool is_member; +@@ -873,7 +881,7 @@ update_gshadow(const struct sgrp *sgrp) + fprintf (stderr, + _("%s: Out of memory. Cannot update %s.\n"), + Prog, sgr_dbname ()); +- fail_exit (E_GRP_UPDATE); ++ fail_exit (E_GRP_UPDATE, process_selinux); + } + + if (was_admin && lflg) { +@@ -960,7 +968,7 @@ update_gshadow(const struct sgrp *sgrp) + Prog, sgr_dbname (), nsgrp->sg_namp); + SYSLOG ((LOG_WARN, "failed to prepare the new %s entry '%s'", + sgr_dbname (), nsgrp->sg_namp)); +- fail_exit (E_GRP_UPDATE); ++ fail_exit (E_GRP_UPDATE, process_selinux); + } + + free_nsgrp: +@@ -975,12 +983,12 @@ free_nsgrp: + * grp_update() takes the secondary group set given in user_groups and + * adds the user to each group given by that set. + */ +-static void grp_update (void) ++static void grp_update (bool process_selinux) + { +- update_group_file(); ++ update_group_file(process_selinux); + #ifdef SHADOWGRP + if (is_shadow_grp) { +- update_gshadow_file(); ++ update_gshadow_file(process_selinux); + } + #endif + } +@@ -993,7 +1001,7 @@ static void grp_update (void) + * are checked for sanity. + */ + static void +-process_flags(int argc, char **argv) ++process_flags(int argc, char **argv, struct option_flags *flags) + { + struct stat st; + bool anyflag = false; +@@ -1158,8 +1166,10 @@ process_flags(int argc, char **argv) + rflg = true; + break; + case 'R': /* no-op, handled in process_root_flag () */ ++ flags->chroot = true; + break; + case 'P': /* no-op, handled in process_prefix_flag () */ ++ flags->prefix = true; + break; + case 's': + if ( ( !VALID (optarg) ) +@@ -1467,51 +1477,55 @@ process_flags(int argc, char **argv) + * close_files() closes all of the files that were opened for this new + * user. This causes any modified entries to be written out. + */ +-static void close_files (void) ++static void close_files (struct option_flags *flags) + { +- if (pw_close (true) == 0) { ++ bool process_selinux; ++ ++ process_selinux = !flags->chroot && !flags->prefix; ++ ++ if (pw_close (process_selinux) == 0) { + fprintf (stderr, + _("%s: failure while writing changes to %s\n"), + Prog, pw_dbname ()); + SYSLOG ((LOG_ERR, "failure while writing changes to %s", pw_dbname ())); +- fail_exit (E_PW_UPDATE); ++ fail_exit (E_PW_UPDATE, process_selinux); + } +- if (is_shadow_pwd && (spw_close (true) == 0)) { ++ if (is_shadow_pwd && (spw_close (process_selinux) == 0)) { + fprintf (stderr, + _("%s: failure while writing changes to %s\n"), + Prog, spw_dbname ()); + SYSLOG ((LOG_ERR, + "failure while writing changes to %s", + spw_dbname ())); +- fail_exit (E_PW_UPDATE); ++ fail_exit (E_PW_UPDATE, process_selinux); + } + + if (Gflg || lflg) { +- if (gr_close (true) == 0) { ++ if (gr_close (process_selinux) == 0) { + fprintf (stderr, + _("%s: failure while writing changes to %s\n"), + Prog, gr_dbname ()); + SYSLOG ((LOG_ERR, + "failure while writing changes to %s", + gr_dbname ())); +- fail_exit (E_GRP_UPDATE); ++ fail_exit (E_GRP_UPDATE, process_selinux); + } + #ifdef SHADOWGRP + if (is_shadow_grp) { +- if (sgr_close (true) == 0) { ++ if (sgr_close (process_selinux) == 0) { + fprintf (stderr, + _("%s: failure while writing changes to %s\n"), + Prog, sgr_dbname ()); + SYSLOG ((LOG_ERR, + "failure while writing changes to %s", + sgr_dbname ())); +- fail_exit (E_GRP_UPDATE); ++ fail_exit (E_GRP_UPDATE, process_selinux); + } + } + #endif + #ifdef SHADOWGRP + if (is_shadow_grp) { +- if (sgr_unlock (true) == 0) { ++ if (sgr_unlock (process_selinux) == 0) { + fprintf (stderr, + _("%s: failed to unlock %s\n"), + Prog, sgr_dbname ()); +@@ -1522,7 +1536,7 @@ static void close_files (void) + } + } + #endif +- if (gr_unlock (true) == 0) { ++ if (gr_unlock (process_selinux) == 0) { + fprintf (stderr, + _("%s: failed to unlock %s\n"), + Prog, gr_dbname ()); +@@ -1534,7 +1548,7 @@ static void close_files (void) + } + + if (is_shadow_pwd) { +- if (spw_unlock (true) == 0) { ++ if (spw_unlock (process_selinux) == 0) { + fprintf (stderr, + _("%s: failed to unlock %s\n"), + Prog, spw_dbname ()); +@@ -1544,7 +1558,7 @@ static void close_files (void) + /* continue */ + } + } +- if (pw_unlock (true) == 0) { ++ if (pw_unlock (process_selinux) == 0) { + fprintf (stderr, + _("%s: failed to unlock %s\n"), + Prog, pw_dbname ()); +@@ -1561,12 +1575,12 @@ static void close_files (void) + + #ifdef ENABLE_SUBIDS + if (vflg || Vflg) { +- if (sub_uid_close (true) == 0) { ++ if (sub_uid_close (process_selinux) == 0) { + fprintf (stderr, _("%s: failure while writing changes to %s\n"), Prog, sub_uid_dbname ()); + SYSLOG ((LOG_ERR, "failure while writing changes to %s", sub_uid_dbname ())); +- fail_exit (E_SUB_UID_UPDATE); ++ fail_exit (E_SUB_UID_UPDATE, process_selinux); + } +- if (sub_uid_unlock (true) == 0) { ++ if (sub_uid_unlock (process_selinux) == 0) { + fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, sub_uid_dbname ()); + SYSLOG ((LOG_ERR, "failed to unlock %s", sub_uid_dbname ())); + /* continue */ +@@ -1574,12 +1588,12 @@ static void close_files (void) + sub_uid_locked = false; + } + if (wflg || Wflg) { +- if (sub_gid_close (true) == 0) { ++ if (sub_gid_close (process_selinux) == 0) { + fprintf (stderr, _("%s: failure while writing changes to %s\n"), Prog, sub_gid_dbname ()); + SYSLOG ((LOG_ERR, "failure while writing changes to %s", sub_gid_dbname ())); +- fail_exit (E_SUB_GID_UPDATE); ++ fail_exit (E_SUB_GID_UPDATE, process_selinux); + } +- if (sub_gid_unlock (true) == 0) { ++ if (sub_gid_unlock (process_selinux) == 0) { + fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, sub_gid_dbname ()); + SYSLOG ((LOG_ERR, "failed to unlock %s", sub_gid_dbname ())); + /* continue */ +@@ -1604,33 +1618,33 @@ static void close_files (void) + * + * open_files() opens the two password files. + */ +-static void open_files (void) ++static void open_files (bool process_selinux) + { + if (pw_lock () == 0) { + fprintf (stderr, + _("%s: cannot lock %s; try again later.\n"), + Prog, pw_dbname ()); +- fail_exit (E_PW_UPDATE); ++ fail_exit (E_PW_UPDATE, process_selinux); + } + pw_locked = true; + if (pw_open (O_CREAT | O_RDWR) == 0) { + fprintf (stderr, + _("%s: cannot open %s\n"), + Prog, pw_dbname ()); +- fail_exit (E_PW_UPDATE); ++ fail_exit (E_PW_UPDATE, process_selinux); + } + if (is_shadow_pwd && (spw_lock () == 0)) { + fprintf (stderr, + _("%s: cannot lock %s; try again later.\n"), + Prog, spw_dbname ()); +- fail_exit (E_PW_UPDATE); ++ fail_exit (E_PW_UPDATE, process_selinux); + } + spw_locked = true; + if (is_shadow_pwd && (spw_open (O_CREAT | O_RDWR) == 0)) { + fprintf (stderr, + _("%s: cannot open %s\n"), + Prog, spw_dbname ()); +- fail_exit (E_PW_UPDATE); ++ fail_exit (E_PW_UPDATE, process_selinux); + } + + if (Gflg || lflg) { +@@ -1642,28 +1656,28 @@ static void open_files (void) + fprintf (stderr, + _("%s: cannot lock %s; try again later.\n"), + Prog, gr_dbname ()); +- fail_exit (E_GRP_UPDATE); ++ fail_exit (E_GRP_UPDATE, process_selinux); + } + gr_locked = true; + if (gr_open (O_CREAT | O_RDWR) == 0) { + fprintf (stderr, + _("%s: cannot open %s\n"), + Prog, gr_dbname ()); +- fail_exit (E_GRP_UPDATE); ++ fail_exit (E_GRP_UPDATE, process_selinux); + } + #ifdef SHADOWGRP + if (is_shadow_grp && (sgr_lock () == 0)) { + fprintf (stderr, + _("%s: cannot lock %s; try again later.\n"), + Prog, sgr_dbname ()); +- fail_exit (E_GRP_UPDATE); ++ fail_exit (E_GRP_UPDATE, process_selinux); + } + sgr_locked = true; + if (is_shadow_grp && (sgr_open (O_CREAT | O_RDWR) == 0)) { + fprintf (stderr, + _("%s: cannot open %s\n"), + Prog, sgr_dbname ()); +- fail_exit (E_GRP_UPDATE); ++ fail_exit (E_GRP_UPDATE, process_selinux); + } + #endif + } +@@ -1673,14 +1687,14 @@ static void open_files (void) + fprintf (stderr, + _("%s: cannot lock %s; try again later.\n"), + Prog, sub_uid_dbname ()); +- fail_exit (E_SUB_UID_UPDATE); ++ fail_exit (E_SUB_UID_UPDATE, process_selinux); + } + sub_uid_locked = true; + if (sub_uid_open (O_CREAT | O_RDWR) == 0) { + fprintf (stderr, + _("%s: cannot open %s\n"), + Prog, sub_uid_dbname ()); +- fail_exit (E_SUB_UID_UPDATE); ++ fail_exit (E_SUB_UID_UPDATE, process_selinux); + } + } + if (wflg || Wflg) { +@@ -1688,14 +1702,14 @@ static void open_files (void) + fprintf (stderr, + _("%s: cannot lock %s; try again later.\n"), + Prog, sub_gid_dbname ()); +- fail_exit (E_SUB_GID_UPDATE); ++ fail_exit (E_SUB_GID_UPDATE, process_selinux); + } + sub_gid_locked = true; + if (sub_gid_open (O_CREAT | O_RDWR) == 0) { + fprintf (stderr, + _("%s: cannot open %s\n"), + Prog, sub_gid_dbname ()); +- fail_exit (E_SUB_GID_UPDATE); ++ fail_exit (E_SUB_GID_UPDATE, process_selinux); + } + } + #endif /* ENABLE_SUBIDS */ +@@ -1707,13 +1721,15 @@ static void open_files (void) + * usr_update() creates the password file entries for this user and + * will update the group entries if required. + */ +-static void usr_update (void) ++static void usr_update (struct option_flags *flags) + { + struct passwd pwent; + const struct passwd *pwd; +- + struct spwd spent; + const struct spwd *spwd = NULL; ++ bool process_selinux; ++ ++ process_selinux = !flags->chroot && !flags->prefix; + + /* + * Locate the entry in /etc/passwd, which MUST exist. +@@ -1723,10 +1739,10 @@ static void usr_update (void) + fprintf (stderr, + _("%s: user '%s' does not exist in %s\n"), + Prog, user_name, pw_dbname ()); +- fail_exit (E_NOTFOUND); ++ fail_exit (E_NOTFOUND, process_selinux); + } + pwent = *pwd; +- new_pwent (&pwent); ++ new_pwent (&pwent, process_selinux); + + + /* If the shadow file does not exist, it won't be created */ +@@ -1735,7 +1751,7 @@ static void usr_update (void) + if (NULL != spwd) { + /* Update the shadow entry if it exists */ + spent = *spwd; +- new_spent (&spent); ++ new_spent (&spent, process_selinux); + } else if ( ( pflg + && streq(pwent.pw_passwd, SHADOW_PASSWD_STRING)) + || eflg || fflg) { +@@ -1766,7 +1782,7 @@ static void usr_update (void) + spent.sp_inact = -1; + spent.sp_expire = -1; + spent.sp_flag = SHADOW_SP_FLAG_UNSET; +- new_spent (&spent); ++ new_spent (&spent, process_selinux); + spwd = &spent; /* entry needs to be committed */ + } + } +@@ -1777,13 +1793,13 @@ static void usr_update (void) + fprintf (stderr, + _("%s: failed to prepare the new %s entry '%s'\n"), + Prog, pw_dbname (), pwent.pw_name); +- fail_exit (E_PW_UPDATE); ++ fail_exit (E_PW_UPDATE, process_selinux); + } + if (lflg && (pw_remove (user_name) == 0)) { + fprintf (stderr, + _("%s: cannot remove entry '%s' from %s\n"), + Prog, user_name, pw_dbname ()); +- fail_exit (E_PW_UPDATE); ++ fail_exit (E_PW_UPDATE, process_selinux); + } + } + if ((NULL != spwd) && (lflg || eflg || fflg || pflg || Lflg || Uflg)) { +@@ -1791,13 +1807,13 @@ static void usr_update (void) + fprintf (stderr, + _("%s: failed to prepare the new %s entry '%s'\n"), + Prog, spw_dbname (), spent.sp_namp); +- fail_exit (E_PW_UPDATE); ++ fail_exit (E_PW_UPDATE, process_selinux); + } + if (lflg && (spw_remove (user_name) == 0)) { + fprintf (stderr, + _("%s: cannot remove entry '%s' from %s\n"), + Prog, user_name, spw_dbname ()); +- fail_exit (E_PW_UPDATE); ++ fail_exit (E_PW_UPDATE, process_selinux); + } + } + } +@@ -1808,7 +1824,7 @@ static void usr_update (void) + * move_home() moves the user's home directory to a new location. The + * files will be copied if the directory cannot simply be renamed. + */ +-static void move_home (void) ++static void move_home (bool process_selinux) + { + struct stat sb; + +@@ -1820,7 +1836,7 @@ static void move_home (void) + fprintf (stderr, + _("%s: directory %s exists\n"), + Prog, user_newhome); +- fail_exit (E_HOMEDIR); ++ fail_exit (E_HOMEDIR, process_selinux); + } + + if (stat (prefix_user_home, &sb) == 0) { +@@ -1834,7 +1850,7 @@ static void move_home (void) + "not a directory. It is not removed and no " + "home directories are created.\n"), + Prog, user_home); +- fail_exit (E_HOMEDIR); ++ fail_exit (E_HOMEDIR, process_selinux); + } + + #ifdef WITH_AUDIT +@@ -1855,7 +1871,7 @@ static void move_home (void) + fprintf (stderr, + _("%s: Failed to change ownership of the home directory"), + Prog); +- fail_exit (E_HOMEDIR); ++ fail_exit (E_HOMEDIR, process_selinux); + } + #ifdef WITH_AUDIT + audit_logger (AUDIT_USER_MGMT, Prog, +@@ -1870,7 +1886,7 @@ static void move_home (void) + fprintf (stderr, + _("%s: error: cannot move subvolume from %s to %s - different device\n"), + Prog, prefix_user_home, prefix_user_newhome); +- fail_exit (E_HOMEDIR); ++ fail_exit (E_HOMEDIR, process_selinux); + } + #endif + +@@ -1901,7 +1917,7 @@ static void move_home (void) + fprintf (stderr, + _("%s: cannot rename directory %s to %s\n"), + Prog, prefix_user_home, prefix_user_newhome); +- fail_exit (E_HOMEDIR); ++ fail_exit (E_HOMEDIR, process_selinux); + } + } else { + fprintf (stderr, +@@ -2160,6 +2176,8 @@ int main (int argc, char **argv) + int retval; + #endif /* USE_PAM */ + #endif /* ACCT_TOOLS_SETUID */ ++ struct option_flags flags; ++ bool process_selinux; + + log_set_progname(Prog); + log_set_logfd(stderr); +@@ -2189,7 +2207,8 @@ int main (int argc, char **argv) + is_sub_gid = sub_gid_file_present (); + #endif /* ENABLE_SUBIDS */ + +- process_flags (argc, argv); ++ process_flags (argc, argv, &flags); ++ process_selinux = !flags.chroot && !flags.prefix; + + /* + * The home directory, the username and the user's UID should not +@@ -2251,13 +2270,13 @@ int main (int argc, char **argv) + * Do the hard stuff - open the files, change the user entries, + * change the home directory, then close and update the files. + */ +- open_files (); ++ open_files (process_selinux); + if ( cflg || dflg || eflg || fflg || gflg || Lflg || lflg || pflg + || sflg || uflg || Uflg) { +- usr_update (); ++ usr_update (&flags); + } + if (Gflg || lflg) { +- grp_update (); ++ grp_update (process_selinux); + } + #ifdef ENABLE_SUBIDS + if (Vflg) { +@@ -2273,7 +2292,7 @@ int main (int argc, char **argv) + (uintmax_t) ptr->range.first, + (uintmax_t) ptr->range.last, + sub_uid_dbname()); +- fail_exit (E_SUB_UID_UPDATE); ++ fail_exit (E_SUB_UID_UPDATE, process_selinux); + } + } + } +@@ -2290,7 +2309,7 @@ int main (int argc, char **argv) + (uintmax_t) ptr->range.first, + (uintmax_t) ptr->range.last, + sub_uid_dbname()); +- fail_exit (E_SUB_UID_UPDATE); ++ fail_exit (E_SUB_UID_UPDATE, process_selinux); + } + } + } +@@ -2307,7 +2326,7 @@ int main (int argc, char **argv) + (uintmax_t) ptr->range.first, + (uintmax_t) ptr->range.last, + sub_gid_dbname()); +- fail_exit (E_SUB_GID_UPDATE); ++ fail_exit (E_SUB_GID_UPDATE, process_selinux); + } + } + } +@@ -2324,12 +2343,12 @@ int main (int argc, char **argv) + (uintmax_t) ptr->range.first, + (uintmax_t) ptr->range.last, + sub_gid_dbname()); +- fail_exit (E_SUB_GID_UPDATE); ++ fail_exit (E_SUB_GID_UPDATE, process_selinux); + } + } + } + #endif /* ENABLE_SUBIDS */ +- close_files (); ++ close_files (&flags); + + #ifdef WITH_TCB + if ( (lflg || uflg) +@@ -2355,7 +2374,7 @@ int main (int argc, char **argv) + user_name, user_id, + SHADOW_AUDIT_FAILURE); + #endif /* WITH_AUDIT */ +- fail_exit (E_SE_UPDATE); ++ fail_exit (E_SE_UPDATE, process_selinux); + } + } else { + if (del_seuser (user_name) != 0) { +@@ -2368,14 +2387,14 @@ int main (int argc, char **argv) + user_name, user_id, + SHADOW_AUDIT_FAILURE); + #endif /* WITH_AUDIT */ +- fail_exit (E_SE_UPDATE); ++ fail_exit (E_SE_UPDATE, process_selinux); + } + } + } + #endif /* WITH_SELINUX */ + + if (mflg) { +- move_home (); ++ move_home (process_selinux); + } + + #ifndef NO_MOVE_MAILBOX +@@ -2420,7 +2439,7 @@ int main (int argc, char **argv) + fprintf (stderr, + _("%s: Failed to change ownership of the home directory"), + Prog); +- fail_exit (E_HOMEDIR); ++ fail_exit (E_HOMEDIR, process_selinux); + } + } + } + +diff -up shadow-4.18.0/lib/cleanup_group.c.test-chroot3 shadow-4.18.0/lib/cleanup_group.c +--- shadow-4.18.0/lib/cleanup_group.c.test-chroot3 2025-07-04 11:15:02.302179208 +0200 ++++ shadow-4.18.0/lib/cleanup_group.c 2025-07-04 11:15:02.311510389 +0200 +@@ -179,9 +179,11 @@ void cleanup_report_del_group_gshadow (v + * + * 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 (true) == 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 + * + * 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 (true) == 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 ()); +diff -up shadow-4.18.0/lib/cleanup_user.c.test-chroot3 shadow-4.18.0/lib/cleanup_user.c +--- shadow-4.18.0/lib/cleanup_user.c.test-chroot3 2025-07-04 11:15:02.297132547 +0200 ++++ shadow-4.18.0/lib/cleanup_user.c 2025-07-04 11:15:02.311622726 +0200 +@@ -96,9 +96,11 @@ void cleanup_report_add_user_shadow (voi + * + * 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 (true) == 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 +117,11 @@ void cleanup_unlock_passwd (MAYBE_UNUSED + * + * 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 (true) == 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 ()); +diff -up shadow-4.18.0/lib/prototypes.h.test-chroot3 shadow-4.18.0/lib/prototypes.h +--- shadow-4.18.0/lib/prototypes.h.test-chroot3 2025-06-24 22:28:01.058453223 +0200 ++++ shadow-4.18.0/lib/prototypes.h 2025-07-04 11:15:02.311703893 +0200 +@@ -92,11 +92,11 @@ void cleanup_report_del_group_gshadow (v + void cleanup_report_mod_passwd (void *cleanup_info); + void cleanup_report_mod_group (void *cleanup_info); + void cleanup_report_mod_gshadow (void *cleanup_info); +-void cleanup_unlock_group (/*@null@*/void *MAYBE_UNUSED); ++void cleanup_unlock_group (void *process_selinux); + #ifdef SHADOWGRP +-void cleanup_unlock_gshadow (/*@null@*/void *MAYBE_UNUSED); ++void cleanup_unlock_gshadow (void *process_selinux); + #endif +-void cleanup_unlock_passwd (/*@null@*/void *MAYBE_UNUSED); ++void cleanup_unlock_passwd (void *process_selinux); + + /* console.c */ + extern bool console (const char *); +diff -up shadow-4.18.0/src/chgpasswd.c.test-chroot3 shadow-4.18.0/src/chgpasswd.c +--- shadow-4.18.0/src/chgpasswd.c.test-chroot3 2025-07-04 11:15:02.302260729 +0200 ++++ shadow-4.18.0/src/chgpasswd.c 2025-07-04 11:15:02.312527205 +0200 +@@ -38,6 +38,12 @@ + #include "string/strcmp/streq.h" + #include "string/strtok/stpsep.h" + ++/* ++ * Structures ++ */ ++struct option_flags { ++ bool chroot; ++}; + + /* + * Global variables +@@ -68,21 +74,21 @@ static bool sgr_locked = false; + static bool gr_locked = false; + + /* local function prototypes */ +-NORETURN static void fail_exit (int code); ++NORETURN static void fail_exit (int code, bool process_selinux); + NORETURN static void usage (int status); +-static void process_flags (int argc, char **argv); ++static void process_flags (int argc, char **argv, struct option_flags *flags); + static void check_flags (void); + static void check_perms (void); +-static void open_files (void); +-static void close_files (void); ++static void open_files (bool process_selinux); ++static void close_files (struct option_flags *flags); + + /* + * fail_exit - exit with a failure code after unlocking the files + */ +-static void fail_exit (int code) ++static void fail_exit (int code, bool process_selinux) + { + if (gr_locked) { +- if (gr_unlock (true) == 0) { ++ if (gr_unlock (process_selinux) == 0) { + fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, gr_dbname ()); + SYSLOG ((LOG_ERR, "failed to unlock %s", gr_dbname ())); + /* continue */ +@@ -91,7 +97,7 @@ static void fail_exit (int code) + + #ifdef SHADOWGRP + if (sgr_locked) { +- if (sgr_unlock (true) == 0) { ++ if (sgr_unlock (process_selinux) == 0) { + fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, sgr_dbname ()); + SYSLOG ((LOG_ERR, "failed to unlock %s", sgr_dbname ())); + /* continue */ +@@ -149,7 +155,7 @@ usage (int status) + * + * It will not return if an error is encountered. + */ +-static void process_flags (int argc, char **argv) ++static void process_flags (int argc, char **argv, struct option_flags *flags) + { + int c; + #if defined(USE_SHA_CRYPT) || defined(USE_BCRYPT) || defined(USE_YESCRYPT) +@@ -187,6 +193,7 @@ static void process_flags (int argc, cha + md5flg = true; + break; + case 'R': /* no-op, handled in process_root_flag () */ ++ flags->chroot = true; + break; + #if defined(USE_SHA_CRYPT) || defined(USE_BCRYPT) || defined(USE_YESCRYPT) + case 's': +@@ -336,7 +343,7 @@ static void check_perms (void) + /* + * open_files - lock and open the group databases + */ +-static void open_files (void) ++static void open_files (bool process_selinux) + { + /* + * Lock the group file and open it for reading and writing. This will +@@ -346,13 +353,13 @@ static void open_files (void) + fprintf (stderr, + _("%s: cannot lock %s; try again later.\n"), + Prog, gr_dbname ()); +- fail_exit (1); ++ fail_exit (1, process_selinux); + } + gr_locked = true; + if (gr_open (O_CREAT | O_RDWR) == 0) { + fprintf (stderr, + _("%s: cannot open %s\n"), Prog, gr_dbname ()); +- fail_exit (1); ++ fail_exit (1, process_selinux); + } + + #ifdef SHADOWGRP +@@ -362,13 +369,13 @@ static void open_files (void) + fprintf (stderr, + _("%s: cannot lock %s; try again later.\n"), + Prog, sgr_dbname ()); +- fail_exit (1); ++ fail_exit (1, process_selinux); + } + sgr_locked = true; + if (sgr_open (O_CREAT | O_RDWR) == 0) { + fprintf (stderr, _("%s: cannot open %s\n"), + Prog, sgr_dbname ()); +- fail_exit (1); ++ fail_exit (1, process_selinux); + } + } + #endif +@@ -377,18 +384,21 @@ static void open_files (void) + /* + * close_files - close and unlock the group databases + */ +-static void close_files (void) ++static void close_files (struct option_flags *flags) + { ++ bool process_selinux; ++ ++ process_selinux = !flags->chroot; + #ifdef SHADOWGRP + if (is_shadow_grp) { +- if (sgr_close (true) == 0) { ++ if (sgr_close (process_selinux) == 0) { + fprintf (stderr, + _("%s: failure while writing changes to %s\n"), + Prog, sgr_dbname ()); + SYSLOG ((LOG_ERR, "failure while writing changes to %s", sgr_dbname ())); +- fail_exit (1); ++ fail_exit (1, process_selinux); + } +- if (sgr_unlock (true) == 0) { ++ if (sgr_unlock (process_selinux) == 0) { + fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, sgr_dbname ()); + SYSLOG ((LOG_ERR, "failed to unlock %s", sgr_dbname ())); + /* continue */ +@@ -397,14 +407,14 @@ static void close_files (void) + } + #endif + +- if (gr_close (true) == 0) { ++ if (gr_close (process_selinux) == 0) { + fprintf (stderr, + _("%s: failure while writing changes to %s\n"), + Prog, gr_dbname ()); + SYSLOG ((LOG_ERR, "failure while writing changes to %s", gr_dbname ())); +- fail_exit (1); ++ fail_exit (1, process_selinux); + } +- if (gr_unlock (true) == 0) { ++ if (gr_unlock (process_selinux) == 0) { + fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, gr_dbname ()); + SYSLOG ((LOG_ERR, "failed to unlock %s", gr_dbname ())); + /* continue */ +@@ -428,6 +438,8 @@ int main (int argc, char **argv) + struct group newgr; + bool errors = false; + intmax_t line = 0; ++ struct option_flags flags; ++ bool process_selinux; + + log_set_progname(Prog); + log_set_logfd(stderr); +@@ -444,7 +456,8 @@ int main (int argc, char **argv) + + process_root_flag ("-R", argc, argv); + +- process_flags (argc, argv); ++ process_flags (argc, argv, &flags); ++ process_selinux = !flags.chroot; + + OPENLOG (Prog); + +@@ -454,7 +467,7 @@ int main (int argc, char **argv) + is_shadow_grp = sgr_file_present (); + #endif + +- open_files (); ++ open_files (process_selinux); + + /* + * Read each line, separating the group name from the password. The +@@ -523,7 +536,7 @@ int main (int argc, char **argv) + fprintf (stderr, + _("%s: failed to crypt password with salt '%s': %s\n"), + Prog, salt, strerror (errno)); +- fail_exit (1); ++ fail_exit (1, process_selinux); + } + } + +@@ -624,10 +637,10 @@ int main (int argc, char **argv) + if (errors) { + fprintf (stderr, + _("%s: error detected, changes ignored\n"), Prog); +- fail_exit (1); ++ fail_exit (1, process_selinux); + } + +- close_files (); ++ close_files (&flags); + + nscd_flush_cache ("group"); + sssd_flush_cache (SSSD_DB_GROUP); +diff -up shadow-4.18.0/src/groupadd.c.test-chroot3 shadow-4.18.0/src/groupadd.c +--- shadow-4.18.0/src/groupadd.c.test-chroot3 2025-07-04 11:15:02.284218582 +0200 ++++ shadow-4.18.0/src/groupadd.c 2025-07-04 11:15:02.311806157 +0200 +@@ -54,6 +54,14 @@ + #define E_GRP_UPDATE 10 /* can't update group file */ + + /* ++ * Structures ++ */ ++struct option_flags { ++ bool chroot; ++ bool prefix; ++}; ++ ++/* + * Global variables + */ + static const char Prog[] = "groupadd"; +@@ -85,9 +93,9 @@ static void new_sgent (struct sgrp *sgen + #endif + static void grp_update (void); + static void check_new_name (void); +-static void close_files (void); +-static void open_files (void); +-static void process_flags (int argc, char **argv); ++static void close_files (struct option_flags *flags); ++static void open_files (struct option_flags *flags); ++static void process_flags (int argc, char **argv, struct option_flags *flags); + static void check_flags (void); + static void check_perms (void); + +@@ -271,10 +279,14 @@ check_new_name(void) + * close_files() closes all of the files that were opened for this new + * group. This causes any modified entries to be written out. + */ +-static void close_files (void) ++static void close_files (struct option_flags *flags) + { ++ bool process_selinux; ++ ++ process_selinux = !flags->chroot && !flags->prefix; ++ + /* First, write the changes in the regular group database */ +- if (gr_close (true) == 0) { ++ if (gr_close (process_selinux) == 0) { + fprintf (stderr, + _("%s: failure while writing changes to %s\n"), + Prog, gr_dbname ()); +@@ -289,13 +301,13 @@ static void close_files (void) + gr_dbname (), group_name, (unsigned int) group_id)); + del_cleanup (cleanup_report_add_group_group); + +- cleanup_unlock_group (NULL); ++ cleanup_unlock_group (&process_selinux); + del_cleanup (cleanup_unlock_group); + + /* Now, write the changes in the shadow database */ + #ifdef SHADOWGRP + if (is_shadow_grp) { +- if (sgr_close (true) == 0) { ++ if (sgr_close (process_selinux) == 0) { + fprintf (stderr, + _("%s: failure while writing changes to %s\n"), + Prog, sgr_dbname ()); +@@ -310,7 +322,7 @@ static void close_files (void) + sgr_dbname (), group_name)); + del_cleanup (cleanup_report_add_group_gshadow); + +- cleanup_unlock_gshadow (NULL); ++ cleanup_unlock_gshadow (&process_selinux); + del_cleanup (cleanup_unlock_gshadow); + } + #endif /* SHADOWGRP */ +@@ -326,8 +338,12 @@ static void close_files (void) + * + * open_files() opens the two group files. + */ +-static void open_files (void) ++static void open_files (struct option_flags *flags) + { ++ bool process_selinux; ++ ++ process_selinux = !flags->chroot && !flags->prefix; ++ + /* First, lock the databases */ + if (gr_lock () == 0) { + fprintf (stderr, +@@ -335,7 +351,7 @@ static void open_files (void) + Prog, gr_dbname ()); + fail_exit (E_GRP_UPDATE); + } +- add_cleanup (cleanup_unlock_group, NULL); ++ add_cleanup (cleanup_unlock_group, &process_selinux); + + #ifdef SHADOWGRP + if (is_shadow_grp) { +@@ -345,7 +361,7 @@ static void open_files (void) + Prog, sgr_dbname ()); + fail_exit (E_GRP_UPDATE); + } +- add_cleanup (cleanup_unlock_gshadow, NULL); ++ add_cleanup (cleanup_unlock_gshadow, &process_selinux); + } + #endif /* SHADOWGRP */ + +@@ -380,7 +396,7 @@ static void open_files (void) + * + * It will not return if an error is encountered. + */ +-static void process_flags (int argc, char **argv) ++static void process_flags (int argc, char **argv, struct option_flags *flags) + { + /* + * Parse the command line options. +@@ -455,8 +471,10 @@ static void process_flags (int argc, cha + rflg = true; + break; + case 'R': /* no-op, handled in process_root_flag () */ ++ flags->chroot = true; + break; + case 'P': /* no-op, handled in process_prefix_flag () */ ++ flags->prefix = true; + break; + case 'U': + user_list = optarg; +@@ -583,6 +601,8 @@ static void check_perms (void) + */ + int main (int argc, char **argv) + { ++ struct option_flags flags; ++ + log_set_progname(Prog); + log_set_logfd(stderr); + +@@ -608,7 +628,7 @@ int main (int argc, char **argv) + /* + * Parse the command line options. + */ +- process_flags (argc, argv); ++ process_flags (argc, argv, &flags); + + check_perms (); + +@@ -625,7 +645,7 @@ int main (int argc, char **argv) + * Do the hard stuff - open the files, create the group entries, + * then close and update the files. + */ +- open_files (); ++ open_files (&flags); + + if (!gflg) { + if (find_new_gid (rflg, &group_id, NULL) < 0) { +@@ -634,7 +654,7 @@ int main (int argc, char **argv) + } + + grp_update (); +- close_files (); ++ close_files (&flags); + if (run_parts ("/etc/shadow-maint/groupadd-post.d", group_name, + Prog)) { + exit(1); +diff -up shadow-4.18.0/src/groupdel.c.test-chroot3 shadow-4.18.0/src/groupdel.c +--- shadow-4.18.0/src/groupdel.c.test-chroot3 2025-07-04 11:15:02.284261326 +0200 ++++ shadow-4.18.0/src/groupdel.c 2025-07-04 11:15:02.312079796 +0200 +@@ -33,6 +33,12 @@ + #endif + #include "shadowlog.h" + #include "run_part.h" ++ ++struct option_flags { ++ bool chroot; ++ bool prefix; ++}; ++ + /* + * Global variables + */ +@@ -61,10 +67,10 @@ static bool is_shadow_grp; + /* local function prototypes */ + NORETURN static void usage (int status); + static void grp_update (void); +-static void close_files (void); +-static void open_files (void); ++static void close_files (struct option_flags *flags); ++static void open_files (struct option_flags *flags); + static void group_busy (gid_t gid); +-static void process_flags (int argc, char **argv); ++static void process_flags (int argc, char **argv, struct option_flags *flags); + + /* + * usage - display usage message and exit +@@ -146,10 +152,14 @@ static void grp_update (void) + * close_files() closes all of the files that were opened for this + * new group. This causes any modified entries to be written out. + */ +-static void close_files (void) ++static void close_files (struct option_flags *flags) + { ++ bool process_selinux; ++ ++ process_selinux = !flags->chroot && !flags->prefix; ++ + /* First, write the changes in the regular group database */ +- if (gr_close (true) == 0) { ++ if (gr_close (process_selinux) == 0) { + fprintf (stderr, + _("%s: failure while writing changes to %s\n"), + Prog, gr_dbname ()); +@@ -166,14 +176,14 @@ static void close_files (void) + group_name, gr_dbname ())); + del_cleanup (cleanup_report_del_group_group); + +- cleanup_unlock_group (NULL); ++ cleanup_unlock_group (&process_selinux); + del_cleanup (cleanup_unlock_group); + + + /* Then, write the changes in the shadow database */ + #ifdef SHADOWGRP + if (is_shadow_grp) { +- if (sgr_close (true) == 0) { ++ if (sgr_close (process_selinux) == 0) { + fprintf (stderr, + _("%s: failure while writing changes to %s\n"), + Prog, sgr_dbname ()); +@@ -190,7 +200,7 @@ static void close_files (void) + group_name, sgr_dbname ())); + del_cleanup (cleanup_report_del_group_gshadow); + +- cleanup_unlock_gshadow (NULL); ++ cleanup_unlock_gshadow (&process_selinux); + del_cleanup (cleanup_unlock_gshadow); + } + #endif /* SHADOWGRP */ +@@ -204,8 +214,12 @@ static void close_files (void) + * + * open_files() opens the two group files. + */ +-static void open_files (void) ++static void open_files (struct option_flags *flags) + { ++ bool process_selinux; ++ ++ process_selinux = !flags->chroot && !flags->prefix; ++ + /* First, lock the databases */ + if (gr_lock () == 0) { + fprintf (stderr, +@@ -213,7 +227,7 @@ static void open_files (void) + Prog, gr_dbname ()); + fail_exit (E_GRP_UPDATE); + } +- add_cleanup (cleanup_unlock_group, NULL); ++ add_cleanup (cleanup_unlock_group, &process_selinux); + #ifdef SHADOWGRP + if (is_shadow_grp) { + if (sgr_lock () == 0) { +@@ -222,7 +236,7 @@ static void open_files (void) + Prog, sgr_dbname ()); + fail_exit (E_GRP_UPDATE); + } +- add_cleanup (cleanup_unlock_gshadow, NULL); ++ add_cleanup (cleanup_unlock_gshadow, &process_selinux); + } + #endif + +@@ -296,7 +310,7 @@ static void group_busy (gid_t gid) + * + * It will not return if an error is encountered. + */ +-static void process_flags (int argc, char **argv) ++static void process_flags (int argc, char **argv, struct option_flags *flags) + { + /* + * Parse the command line options. +@@ -317,8 +331,10 @@ static void process_flags (int argc, cha + usage (E_SUCCESS); + /*@notreached@*/break; + case 'R': /* no-op, handled in process_root_flag () */ ++ flags->chroot = true; + break; + case 'P': /* no-op, handled in process_prefix_flag () */ ++ flags->prefix = true; + break; + case 'f': + check_group_busy = false; +@@ -352,6 +368,7 @@ int main (int argc, char **argv) + int retval; + #endif /* USE_PAM */ + #endif /* ACCT_TOOLS_SETUID */ ++ struct option_flags flags; + + log_set_progname(Prog); + log_set_logfd(stderr); +@@ -375,7 +392,7 @@ int main (int argc, char **argv) + fail_exit (1); + } + +- process_flags (argc, argv); ++ process_flags (argc, argv, &flags); + + #ifdef ACCT_TOOLS_SETUID + #ifdef USE_PAM +@@ -449,11 +466,11 @@ int main (int argc, char **argv) + * Do the hard stuff - open the files, delete the group entries, + * then close and update the files. + */ +- open_files (); ++ open_files (&flags); + + grp_update (); + +- close_files (); ++ close_files (&flags); + + if (run_parts ("/etc/shadow-maint/groupdel-post.d", group_name, + Prog)) { +diff -up shadow-4.18.0/src/groupmems.c.test-chroot3 shadow-4.18.0/src/groupmems.c +--- shadow-4.18.0/src/groupmems.c.test-chroot3 2025-07-04 11:15:02.302301588 +0200 ++++ shadow-4.18.0/src/groupmems.c 2025-07-04 11:15:57.404278255 +0200 +@@ -47,6 +47,13 @@ + #define EXIT_INVALID_GROUP 9 /* specified group does not exist */ + + /* ++ * Structures ++ */ ++struct option_flags { ++ bool chroot; ++}; ++ ++/* + * Global variables + */ + static const char Prog[] = "groupmems"; +@@ -68,15 +75,17 @@ static bool sgr_locked = false; + /* local function prototypes */ + static char *whoami (void); + static void add_user (const char *user, +- const struct group *grp); ++ const struct group *grp, ++ bool process_selinux); + static void remove_user (const char *user, +- const struct group *grp); +-static void purge_members (const struct group *grp); ++ const struct group *grp, ++ bool process_selinux); ++static void purge_members (const struct group *grp, bool process_selinux); + static void display_members (const char *const *members); + NORETURN static void usage (int status); +-static void process_flags (int argc, char **argv); +-static void check_perms (void); +-NORETURN static void fail_exit (int code); ++static void process_flags (int argc, char **argv, struct option_flags *flags); ++static void check_perms (bool process_selinux); ++NORETURN static void fail_exit (int code, bool process_selinux); + #define isroot() (getuid () == 0) + + static char *whoami (void) +@@ -99,7 +108,8 @@ static char *whoami (void) + * add_user - Add a user to the specified group + */ + static void add_user (const char *user, +- const struct group *grp) ++ const struct group *grp, ++ bool process_selinux) + { + struct group *newgrp; + +@@ -108,7 +118,7 @@ static void add_user (const char *user, + fprintf (stderr, + _("%s: user '%s' is already a member of '%s'\n"), + Prog, user, grp->gr_name); +- fail_exit (EXIT_MEMBER_EXISTS); ++ fail_exit (EXIT_MEMBER_EXISTS, process_selinux); + } + + newgrp = __gr_dup(grp); +@@ -116,7 +126,7 @@ static void add_user (const char *user, + fprintf (stderr, + _("%s: Out of memory. Cannot update %s.\n"), + Prog, gr_dbname ()); +- fail_exit (13); ++ fail_exit (13, process_selinux); + } + + /* Add the user to the /etc/group group */ +@@ -146,7 +156,7 @@ static void add_user (const char *user, + fprintf (stderr, + _("%s: Out of memory. Cannot update %s.\n"), + Prog, sgr_dbname ()); +- fail_exit (13); ++ fail_exit (13, process_selinux); + } + /* Add the user to the members */ + newsg->sg_mem = add_list (newsg->sg_mem, user); +@@ -157,7 +167,7 @@ static void add_user (const char *user, + fprintf (stderr, + _("%s: failed to prepare the new %s entry '%s'\n"), + Prog, sgr_dbname (), newsg->sg_namp); +- fail_exit (13); ++ fail_exit (13, process_selinux); + } + } + #endif +@@ -166,7 +176,7 @@ static void add_user (const char *user, + fprintf (stderr, + _("%s: failed to prepare the new %s entry '%s'\n"), + Prog, gr_dbname (), newgrp->gr_name); +- fail_exit (13); ++ fail_exit (13, process_selinux); + } + } + +@@ -174,7 +184,8 @@ static void add_user (const char *user, + * remove_user - Remove a user from a given group + */ + static void remove_user (const char *user, +- const struct group *grp) ++ const struct group *grp, ++ bool process_selinux) + { + struct group *newgrp; + +@@ -183,7 +194,7 @@ static void remove_user (const char *use + fprintf (stderr, + _("%s: user '%s' is not a member of '%s'\n"), + Prog, user, grp->gr_name); +- fail_exit (EXIT_NOT_MEMBER); ++ fail_exit (EXIT_NOT_MEMBER, process_selinux); + } + + newgrp = __gr_dup (grp); +@@ -191,7 +202,7 @@ static void remove_user (const char *use + fprintf (stderr, + _("%s: Out of memory. Cannot update %s.\n"), + Prog, gr_dbname ()); +- fail_exit (13); ++ fail_exit (13, process_selinux); + } + + /* Remove the user from the /etc/group group */ +@@ -221,7 +232,7 @@ static void remove_user (const char *use + fprintf (stderr, + _("%s: Out of memory. Cannot update %s.\n"), + Prog, sgr_dbname ()); +- fail_exit (13); ++ fail_exit (13, process_selinux); + } + /* Remove the user from the members */ + newsg->sg_mem = del_list (newsg->sg_mem, user); +@@ -233,7 +244,7 @@ static void remove_user (const char *use + fprintf (stderr, + _("%s: failed to prepare the new %s entry '%s'\n"), + Prog, sgr_dbname (), newsg->sg_namp); +- fail_exit (13); ++ fail_exit (13, process_selinux); + } + } + #endif +@@ -242,14 +253,14 @@ static void remove_user (const char *use + fprintf (stderr, + _("%s: failed to prepare the new %s entry '%s'\n"), + Prog, gr_dbname (), newgrp->gr_name); +- fail_exit (13); ++ fail_exit (13, process_selinux); + } + } + + /* + * purge_members - Remove every members of the specified group + */ +-static void purge_members (const struct group *grp) ++static void purge_members (const struct group *grp, bool process_selinux) + { + struct group *newgrp = __gr_dup (grp); + +@@ -257,7 +268,7 @@ static void purge_members (const struct + fprintf (stderr, + _("%s: Out of memory. Cannot update %s.\n"), + Prog, gr_dbname ()); +- fail_exit (13); ++ fail_exit (13, process_selinux); + } + + /* Remove all the members of the /etc/group group */ +@@ -288,7 +299,7 @@ static void purge_members (const struct + fprintf (stderr, + _("%s: Out of memory. Cannot update %s.\n"), + Prog, sgr_dbname ()); +- fail_exit (13); ++ fail_exit (13, process_selinux); + } + /* Remove all the members of the /etc/gshadow + * group */ +@@ -302,7 +313,7 @@ static void purge_members (const struct + fprintf (stderr, + _("%s: failed to prepare the new %s entry '%s'\n"), + Prog, sgr_dbname (), newsg->sg_namp); +- fail_exit (13); ++ fail_exit (13, process_selinux); + } + } + #endif +@@ -311,7 +322,7 @@ static void purge_members (const struct + fprintf (stderr, + _("%s: failed to prepare the new %s entry '%s'\n"), + Prog, gr_dbname (), newgrp->gr_name); +- fail_exit (13); ++ fail_exit (13, process_selinux); + } + } + +@@ -356,7 +367,7 @@ usage (int status) + /* + * process_flags - perform command line argument setting + */ +-static void process_flags (int argc, char **argv) ++static void process_flags (int argc, char **argv, struct option_flags *flags) + { + int c; + static struct option long_options[] = { +@@ -396,6 +407,7 @@ static void process_flags (int argc, cha + ++exclusive; + break; + case 'R': /* no-op, handled in process_root_flag () */ ++ flags->chroot = true; + break; + default: + usage (EXIT_USAGE); +@@ -411,12 +423,12 @@ static void process_flags (int argc, cha + && (getpwnam (adduser) == NULL)) { + fprintf (stderr, _("%s: user '%s' does not exist\n"), + Prog, adduser); +- fail_exit (EXIT_INVALID_USER); ++ fail_exit (EXIT_INVALID_USER, !flags->chroot); + } + + } + +-static void check_perms (void) ++static void check_perms (bool process_selinux) + { + if (!list) { + #ifdef ACCT_TOOLS_SETUID +@@ -430,7 +442,7 @@ static void check_perms (void) + fprintf (stderr, + _("%s: Cannot determine your user name.\n"), + Prog); +- fail_exit (1); ++ fail_exit (1, process_selinux); + } + + retval = pam_start (Prog, pampw->pw_name, &conv, &pamh); +@@ -450,7 +462,7 @@ static void check_perms (void) + if (NULL != pamh) { + (void) pam_end (pamh, retval); + } +- fail_exit (1); ++ fail_exit (1, process_selinux); + } + (void) pam_end (pamh, retval); + #endif /* USE_PAM */ +@@ -458,10 +470,10 @@ static void check_perms (void) + } + } + +-static void fail_exit (int code) ++static void fail_exit (int code, bool process_selinux) + { + if (gr_locked) { +- if (gr_unlock (true) == 0) { ++ if (gr_unlock (process_selinux) == 0) { + fprintf (stderr, + _("%s: failed to unlock %s\n"), + Prog, gr_dbname ()); +@@ -472,7 +484,7 @@ static void fail_exit (int code) + + #ifdef SHADOWGRP + if (sgr_locked) { +- if (sgr_unlock (true) == 0) { ++ if (sgr_unlock (process_selinux) == 0) { + fprintf (stderr, + _("%s: failed to unlock %s\n"), + Prog, sgr_dbname ()); +@@ -485,14 +497,14 @@ static void fail_exit (int code) + exit (code); + } + +-static void open_files (void) ++static void open_files (bool process_selinux) + { + if (!list) { + if (gr_lock () == 0) { + fprintf (stderr, + _("%s: cannot lock %s; try again later.\n"), + Prog, gr_dbname ()); +- fail_exit (EXIT_GROUP_FILE); ++ fail_exit (EXIT_GROUP_FILE, process_selinux); + } + gr_locked = true; + +@@ -502,7 +514,7 @@ static void open_files (void) + fprintf (stderr, + _("%s: cannot lock %s; try again later.\n"), + Prog, sgr_dbname ()); +- fail_exit (EXIT_GROUP_FILE); ++ fail_exit (EXIT_GROUP_FILE, process_selinux); + } + sgr_locked = true; + } +@@ -511,28 +523,32 @@ static void open_files (void) + + if (gr_open (list ? O_RDONLY : O_CREAT | O_RDWR) == 0) { + fprintf (stderr, _("%s: cannot open %s\n"), Prog, gr_dbname ()); +- fail_exit (EXIT_GROUP_FILE); ++ fail_exit (EXIT_GROUP_FILE, process_selinux); + } + + #ifdef SHADOWGRP + if (is_shadowgrp) { + if (sgr_open (list ? O_RDONLY : O_CREAT | O_RDWR) == 0) { + fprintf (stderr, _("%s: cannot open %s\n"), Prog, sgr_dbname ()); +- fail_exit (EXIT_GROUP_FILE); ++ fail_exit (EXIT_GROUP_FILE, process_selinux); + } + } + #endif + } + +-static void close_files (void) ++static void close_files (struct option_flags *flags) + { +- if ((gr_close (true) == 0) && !list) { ++ bool process_selinux; ++ ++ process_selinux = !flags->chroot; ++ ++ if ((gr_close (process_selinux) == 0) && !list) { + fprintf (stderr, _("%s: failure while writing changes to %s\n"), Prog, gr_dbname ()); + SYSLOG ((LOG_ERR, "failure while writing changes to %s", gr_dbname ())); +- fail_exit (EXIT_GROUP_FILE); ++ fail_exit (EXIT_GROUP_FILE, process_selinux); + } + if (gr_locked) { +- if (gr_unlock (true) == 0) { ++ if (gr_unlock (process_selinux) == 0) { + fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, gr_dbname ()); + SYSLOG ((LOG_ERR, "failed to unlock %s", gr_dbname ())); + /* continue */ +@@ -542,13 +558,13 @@ static void close_files (void) + + #ifdef SHADOWGRP + if (is_shadowgrp) { +- if ((sgr_close (true) == 0) && !list) { ++ if ((sgr_close (process_selinux) == 0) && !list) { + fprintf (stderr, _("%s: failure while writing changes to %s\n"), Prog, sgr_dbname ()); + SYSLOG ((LOG_ERR, "failure while writing changes to %s", sgr_dbname ())); +- fail_exit (EXIT_GROUP_FILE); ++ fail_exit (EXIT_GROUP_FILE, process_selinux); + } + if (sgr_locked) { +- if (sgr_unlock (true) == 0) { ++ if (sgr_unlock (process_selinux) == 0) { + fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, sgr_dbname ()); + SYSLOG ((LOG_ERR, "failed to unlock %s", sgr_dbname ())); + /* continue */ +@@ -563,6 +579,8 @@ int main (int argc, char **argv) + { + char *name; + const struct group *grp; ++ struct option_flags flags; ++ bool process_selinux; + + log_set_progname(Prog); + log_set_logfd(stderr); +@@ -579,44 +597,45 @@ int main (int argc, char **argv) + is_shadowgrp = sgr_file_present (); + #endif + +- process_flags (argc, argv); ++ process_flags (argc, argv, &flags); ++ process_selinux = !flags.chroot; + + if (NULL == thisgroup) { + name = whoami (); + if (!list && (NULL == name)) { + fprintf (stderr, _("%s: your groupname does not match your username\n"), Prog); +- fail_exit (EXIT_NOT_PRIMARY); ++ fail_exit (EXIT_NOT_PRIMARY, process_selinux); + } + } else { + name = thisgroup; + if (!list && !isroot ()) { + fprintf (stderr, _("%s: only root can use the -g/--group option\n"), Prog); +- fail_exit (EXIT_NOT_ROOT); ++ fail_exit (EXIT_NOT_ROOT, process_selinux); + } + } + +- check_perms (); ++ check_perms (process_selinux); + +- open_files (); ++ open_files (process_selinux); + + grp = gr_locate (name); + if (NULL == grp) { + fprintf (stderr, _("%s: group '%s' does not exist in %s\n"), + Prog, name, gr_dbname ()); +- fail_exit (EXIT_INVALID_GROUP); ++ fail_exit (EXIT_INVALID_GROUP, process_selinux); + } + + if (list) { + display_members ((const char *const *)grp->gr_mem); + } else if (NULL != adduser) { +- add_user (adduser, grp); ++ add_user (adduser, grp, process_selinux); + } else if (NULL != deluser) { +- remove_user (deluser, grp); ++ remove_user (deluser, grp, process_selinux); + } else if (purge) { +- purge_members (grp); ++ purge_members (grp, process_selinux); + } + +- close_files (); ++ close_files (&flags); + + exit (EXIT_SUCCESS); + } +diff -up shadow-4.18.0/src/groupmod.c.test-chroot3 shadow-4.18.0/src/groupmod.c +--- shadow-4.18.0/src/groupmod.c.test-chroot3 2025-07-04 11:15:02.284354823 +0200 ++++ shadow-4.18.0/src/groupmod.c 2025-07-04 11:15:02.311945828 +0200 +@@ -61,6 +61,10 @@ + #define E_PAM_USERNAME 12 /* can't determine your username for use with pam */ + #define E_PAM_ERROR 13 /* pam returned an error, see Syslog facility id groupmod */ + ++struct option_flags { ++ bool chroot; ++ bool prefix; ++}; + + /* + * Global variables +@@ -102,11 +106,11 @@ static void new_sgent (struct sgrp *); + static void grp_update (void); + static void check_new_gid (void); + static void check_new_name (void); +-static void process_flags (int, char **); +-static void lock_files (void); ++static void process_flags (int, char **, struct option_flags *); ++static void lock_files (struct option_flags *flags); + static void prepare_failure_reports (void); + static void open_files (void); +-static void close_files (void); ++static void close_files (struct option_flags *flags); + static void update_primary_groups (gid_t ogid, gid_t ngid); + + +@@ -405,7 +409,7 @@ check_new_name(void) + * values that the user will be created with accordingly. The values + * are checked for sanity. + */ +-static void process_flags (int argc, char **argv) ++static void process_flags (int argc, char **argv, struct option_flags *flags) + { + int c; + static struct option long_options[] = { +@@ -451,8 +455,10 @@ static void process_flags (int argc, cha + pflg = true; + break; + case 'R': /* no-op, handled in process_root_flag () */ ++ flags->chroot = true; + break; + case 'P': /* no-op, handled in process_prefix_flag () */ ++ flags->prefix = true; + break; + case 'U': + user_list = optarg; +@@ -479,9 +485,13 @@ static void process_flags (int argc, cha + * close_files() closes all of the files that were opened for this new + * group. This causes any modified entries to be written out. + */ +-static void close_files (void) ++static void close_files (struct option_flags *flags) + { +- if (gr_close (true) == 0) { ++ bool process_selinux; ++ ++ process_selinux = !flags->chroot && !flags->prefix; ++ ++ if (gr_close (process_selinux) == 0) { + fprintf (stderr, + _("%s: failure while writing changes to %s\n"), + Prog, gr_dbname ()); +@@ -498,13 +508,13 @@ static void close_files (void) + gr_dbname (), info_group.action)); + del_cleanup (cleanup_report_mod_group); + +- cleanup_unlock_group (NULL); ++ cleanup_unlock_group (&process_selinux); + del_cleanup (cleanup_unlock_group); + + #ifdef SHADOWGRP + if ( is_shadow_grp + && (pflg || nflg || user_list)) { +- if (sgr_close (true) == 0) { ++ if (sgr_close (process_selinux) == 0) { + fprintf (stderr, + _("%s: failure while writing changes to %s\n"), + Prog, sgr_dbname ()); +@@ -528,13 +538,13 @@ static void close_files (void) + sgr_dbname (), info_gshadow.action)); + del_cleanup (cleanup_report_mod_gshadow); + +- cleanup_unlock_gshadow (NULL); ++ cleanup_unlock_gshadow (&process_selinux); + del_cleanup (cleanup_unlock_gshadow); + } + #endif /* SHADOWGRP */ + + if (gflg) { +- if (pw_close (true) == 0) { ++ if (pw_close (process_selinux) == 0) { + fprintf (stderr, + _("%s: failure while writing changes to %s\n"), + Prog, pw_dbname ()); +@@ -551,7 +561,7 @@ static void close_files (void) + pw_dbname (), info_passwd.action)); + del_cleanup (cleanup_report_mod_passwd); + +- cleanup_unlock_passwd (NULL); ++ cleanup_unlock_passwd (&process_selinux); + del_cleanup (cleanup_unlock_passwd); + } + +@@ -657,15 +667,19 @@ static void prepare_failure_reports (voi + * + * lock_files() locks the group, gshadow, and passwd databases. + */ +-static void lock_files (void) ++static void lock_files (struct option_flags *flags) + { ++ bool process_selinux; ++ ++ process_selinux = !flags->chroot && !flags->prefix; ++ + if (gr_lock () == 0) { + fprintf (stderr, + _("%s: cannot lock %s; try again later.\n"), + Prog, gr_dbname ()); + exit (E_GRP_UPDATE); + } +- add_cleanup (cleanup_unlock_group, NULL); ++ add_cleanup (cleanup_unlock_group, &process_selinux); + + #ifdef SHADOWGRP + if ( is_shadow_grp +@@ -676,7 +690,7 @@ static void lock_files (void) + Prog, sgr_dbname ()); + exit (E_GRP_UPDATE); + } +- add_cleanup (cleanup_unlock_gshadow, NULL); ++ add_cleanup (cleanup_unlock_gshadow, &process_selinux); + } + #endif + +@@ -687,7 +701,7 @@ static void lock_files (void) + Prog, pw_dbname ()); + exit (E_GRP_UPDATE); + } +- add_cleanup (cleanup_unlock_passwd, NULL); ++ add_cleanup (cleanup_unlock_passwd, &process_selinux); + } + } + +@@ -771,6 +785,7 @@ int main (int argc, char **argv) + int retval; + #endif /* USE_PAM */ + #endif /* ACCT_TOOLS_SETUID */ ++ struct option_flags flags; + + log_set_progname(Prog); + log_set_logfd(stderr); +@@ -794,7 +809,7 @@ int main (int argc, char **argv) + exit (E_CLEANUP_SERVICE); + } + +- process_flags (argc, argv); ++ process_flags (argc, argv, &flags); + + #ifdef ACCT_TOOLS_SETUID + #ifdef USE_PAM +@@ -859,7 +874,7 @@ int main (int argc, char **argv) + check_new_name (); + } + +- lock_files (); ++ lock_files (&flags); + + /* + * Now if the group is not changed, it's our fault. +@@ -875,7 +890,7 @@ int main (int argc, char **argv) + + grp_update (); + +- close_files (); ++ close_files (&flags); + + nscd_flush_cache ("group"); + sssd_flush_cache (SSSD_DB_GROUP); +diff -up shadow-4.18.0/src/grpck.c.test-chroot3 shadow-4.18.0/src/grpck.c +--- shadow-4.18.0/src/grpck.c.test-chroot3 2025-07-04 11:15:02.302354224 +0200 ++++ shadow-4.18.0/src/grpck.c 2025-07-04 11:15:02.313643179 +0200 +@@ -44,6 +44,13 @@ + #define E_CANT_UPDATE 5 + + /* ++ * Structures ++ */ ++struct option_flags { ++ bool chroot; ++}; ++ ++/* + * Global variables + */ + static const char Prog[] = "grpck"; +@@ -64,19 +71,20 @@ static bool sort_mode = false; + static bool silence_warnings = false; + + /* local function prototypes */ +-static void fail_exit (int status); ++static void fail_exit (int status, bool process_selinux); + NORETURN static void usage (int status); + static void delete_member (char **, const char *); +-static void process_flags (int argc, char **argv); +-static void open_files (void); +-static void close_files (bool changed); ++static void process_flags (int argc, char **argv, struct option_flags *flags); ++static void open_files (bool process_selinux); ++static void close_files (bool changed, struct option_flags *flags); + static int check_members (const char *groupname, + char **members, + const char *fmt_info, + const char *fmt_prompt, + const char *fmt_syslog, + bool *errors); +-static void check_grp_file (bool *errors, bool *changed); ++static void check_grp_file (bool *errors, bool *changed, ++ struct option_flags *flags); + #ifdef SHADOWGRP + static void compare_members_lists (const char *groupname, + char **members, +@@ -89,10 +97,10 @@ static void check_sgr_file (bool *errors + /* + * fail_exit - exit with an error code after unlocking files + */ +-static void fail_exit (int status) ++static void fail_exit (int status, bool process_selinux) + { + if (gr_locked) { +- if (gr_unlock (true) == 0) { ++ if (gr_unlock (process_selinux) == 0) { + fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, gr_dbname ()); + SYSLOG ((LOG_ERR, "failed to unlock %s", gr_dbname ())); + /* continue */ +@@ -101,7 +109,7 @@ static void fail_exit (int status) + + #ifdef SHADOWGRP + if (sgr_locked) { +- if (sgr_unlock (true) == 0) { ++ if (sgr_unlock (process_selinux) == 0) { + fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, sgr_dbname ()); + SYSLOG ((LOG_ERR, "failed to unlock %s", sgr_dbname ())); + /* continue */ +@@ -172,7 +180,7 @@ static void delete_member (char **list, + * + * It will not return if an error is encountered. + */ +-static void process_flags (int argc, char **argv) ++static void process_flags (int argc, char **argv, struct option_flags *flags) + { + int c; + static struct option long_options[] = { +@@ -201,6 +209,7 @@ static void process_flags (int argc, cha + read_only = true; + break; + case 'R': /* no-op, handled in process_root_flag () */ ++ flags->chroot = true; + break; + case 's': + sort_mode = true; +@@ -257,7 +266,7 @@ static void process_flags (int argc, cha + * In read-only mode, the databases are not locked and are opened + * only for reading. + */ +-static void open_files (void) ++static void open_files (bool process_selinux) + { + /* + * Lock the files if we aren't in "read-only" mode +@@ -267,7 +276,7 @@ static void open_files (void) + fprintf (stderr, + _("%s: cannot lock %s; try again later.\n"), + Prog, grp_file); +- fail_exit (E_CANT_LOCK); ++ fail_exit (E_CANT_LOCK, process_selinux); + } + gr_locked = true; + #ifdef SHADOWGRP +@@ -276,7 +285,7 @@ static void open_files (void) + fprintf (stderr, + _("%s: cannot lock %s; try again later.\n"), + Prog, sgr_file); +- fail_exit (E_CANT_LOCK); ++ fail_exit (E_CANT_LOCK, process_selinux); + } + sgr_locked = true; + } +@@ -293,7 +302,7 @@ static void open_files (void) + if (use_system_grp_file) { + SYSLOG ((LOG_WARN, "cannot open %s", grp_file)); + } +- fail_exit (E_CANT_OPEN); ++ fail_exit (E_CANT_OPEN, process_selinux); + } + #ifdef SHADOWGRP + if (is_shadow && (sgr_open (read_only ? O_RDONLY : O_CREAT | O_RDWR) == 0)) { +@@ -302,7 +311,7 @@ static void open_files (void) + if (use_system_sgr_file) { + SYSLOG ((LOG_WARN, "cannot open %s", sgr_file)); + } +- fail_exit (E_CANT_OPEN); ++ fail_exit (E_CANT_OPEN, process_selinux); + } + #endif + } +@@ -314,23 +323,27 @@ static void open_files (void) + * changes are committed in the databases. The databases are + * unlocked anyway. + */ +-static void close_files (bool changed) ++static void close_files (bool changed, struct option_flags *flags) + { ++ bool process_selinux; ++ ++ process_selinux = !flags->chroot; ++ + /* + * All done. If there were no change we can just abandon any + * changes to the files. + */ + if (changed) { +- if (gr_close (true) == 0) { ++ if (gr_close (process_selinux) == 0) { + fprintf (stderr, _("%s: failure while writing changes to %s\n"), + Prog, grp_file); +- fail_exit (E_CANT_UPDATE); ++ fail_exit (E_CANT_UPDATE, process_selinux); + } + #ifdef SHADOWGRP +- if (is_shadow && (sgr_close (true) == 0)) { ++ if (is_shadow && (sgr_close (process_selinux) == 0)) { + fprintf (stderr, _("%s: failure while writing changes to %s\n"), + Prog, sgr_file); +- fail_exit (E_CANT_UPDATE); ++ fail_exit (E_CANT_UPDATE, process_selinux); + } + #endif + } +@@ -340,7 +353,7 @@ static void close_files (bool changed) + */ + #ifdef SHADOWGRP + if (sgr_locked) { +- if (sgr_unlock (true) == 0) { ++ if (sgr_unlock (process_selinux) == 0) { + fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, sgr_dbname ()); + SYSLOG ((LOG_ERR, "failed to unlock %s", sgr_dbname ())); + /* continue */ +@@ -349,7 +362,7 @@ static void close_files (bool changed) + } + #endif + if (gr_locked) { +- if (gr_unlock (true) == 0) { ++ if (gr_unlock (process_selinux) == 0) { + fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, gr_dbname ()); + SYSLOG ((LOG_ERR, "failed to unlock %s", gr_dbname ())); + /* continue */ +@@ -455,13 +468,16 @@ static void compare_members_lists (const + /* + * check_grp_file - check the content of the group file + */ +-static void check_grp_file (bool *errors, bool *changed) ++static void check_grp_file (bool *errors, bool *changed, struct option_flags *flags) + { + struct commonio_entry *gre, *tgre; + struct group *grp; + #ifdef SHADOWGRP + const struct sgrp *sgr; + #endif ++ bool process_selinux; ++ ++ process_selinux = !flags->chroot; + + /* + * Loop through the entire group file. +@@ -627,7 +643,7 @@ static void check_grp_file (bool *errors + fprintf (stderr, + _("%s: failed to prepare the new %s entry '%s'\n"), + Prog, sgr_dbname (), sg.sg_namp); +- fail_exit (E_CANT_UPDATE); ++ fail_exit (E_CANT_UPDATE, process_selinux); + } + /* remove password from /etc/group */ + gr = *grp; +@@ -636,7 +652,7 @@ static void check_grp_file (bool *errors + fprintf (stderr, + _("%s: failed to prepare the new %s entry '%s'\n"), + Prog, gr_dbname (), gr.gr_name); +- fail_exit (E_CANT_UPDATE); ++ fail_exit (E_CANT_UPDATE, process_selinux); + } + } + } else { +@@ -819,6 +835,8 @@ int main (int argc, char **argv) + { + bool errors = false; + bool changed = false; ++ struct option_flags flags; ++ bool process_selinux; + + log_set_progname(Prog); + log_set_logfd(stderr); +@@ -832,9 +850,10 @@ int main (int argc, char **argv) + OPENLOG (Prog); + + /* Parse the command line arguments */ +- process_flags (argc, argv); ++ process_flags (argc, argv, &flags); ++ process_selinux = !flags.chroot; + +- open_files (); ++ open_files (process_selinux); + + if (sort_mode) { + gr_sort (); +@@ -845,7 +864,7 @@ int main (int argc, char **argv) + changed = true; + #endif + } else { +- check_grp_file (&errors, &changed); ++ check_grp_file (&errors, &changed, &flags); + #ifdef SHADOWGRP + if (is_shadow) { + check_sgr_file (&errors, &changed); +@@ -854,7 +873,7 @@ int main (int argc, char **argv) + } + + /* Commit the change in the database if needed */ +- close_files (changed); ++ close_files (changed, &flags); + + if (!read_only) { + nscd_flush_cache ("group"); + +diff -up shadow-4.18.0/src/chage.c.test-chroot4 shadow-4.18.0/src/chage.c +--- shadow-4.18.0/src/chage.c.test-chroot4 2025-07-04 11:17:37.593707379 +0200 ++++ shadow-4.18.0/src/chage.c 2025-07-04 11:17:37.613081862 +0200 +@@ -42,6 +42,10 @@ + #include "tcbfuncs.h" + #endif + ++struct option_flags { ++ bool chroot; ++ bool prefix; ++}; + + /* + * Global variables +@@ -79,29 +83,29 @@ NORETURN static void usage (int status); + static int new_fields (void); + static void print_day_as_date (long day); + static void list_fields (void); +-static void process_flags (int argc, char **argv); ++static void process_flags (int argc, char **argv, struct option_flags *flags); + static void check_flags (int argc, int opt_index); +-static void check_perms (void); +-static void open_files (bool readonly); +-static void close_files (void); +-NORETURN static void fail_exit (int code); ++static void check_perms (struct option_flags *flags); ++static void open_files (bool readonly, struct option_flags *flags); ++static void close_files (struct option_flags *flags); ++NORETURN static void fail_exit (int code, bool process_selinux); + + /* + * fail_exit - do some cleanup and exit with the given error code + */ + NORETURN + static void +-fail_exit (int code) ++fail_exit (int code, bool process_selinux) + { + if (spw_locked) { +- if (spw_unlock (true) == 0) { ++ if (spw_unlock (process_selinux) == 0) { + fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, spw_dbname ()); + SYSLOG ((LOG_ERR, "failed to unlock %s", spw_dbname ())); + /* continue */ + } + } + if (pw_locked) { +- if (pw_unlock (true) == 0) { ++ if (pw_unlock (process_selinux) == 0) { + fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, pw_dbname ()); + SYSLOG ((LOG_ERR, "failed to unlock %s", pw_dbname ())); + /* continue */ +@@ -338,7 +342,7 @@ static void list_fields (void) + * + * It will not return if an error is encountered. + */ +-static void process_flags (int argc, char **argv) ++static void process_flags (int argc, char **argv, struct option_flags *flags) + { + /* + * Parse the command line options. +@@ -419,8 +423,10 @@ static void process_flags (int argc, cha + } + break; + case 'R': /* no-op, handled in process_root_flag () */ ++ flags->chroot = true; + break; + case 'P': /* no-op, handled in process_prefix_flag () */ ++ flags->prefix = true; + break; + case 'W': + Wflg = true; +@@ -472,8 +478,12 @@ static void check_flags (int argc, int o + * + * It will not return if the user is not allowed. + */ +-static void check_perms (void) ++static void check_perms (struct option_flags *flags) + { ++ bool process_selinux; ++ ++ process_selinux = !flags->chroot && !flags->prefix; ++ + /* + * An unprivileged user can ask for their own aging information, but + * only root can change it, or list another user's aging +@@ -482,7 +492,7 @@ static void check_perms (void) + + if (!amroot && !lflg) { + fprintf (stderr, _("%s: Permission denied.\n"), Prog); +- fail_exit (E_NOPERM); ++ fail_exit (E_NOPERM, process_selinux); + } + } + +@@ -492,8 +502,12 @@ static void check_perms (void) + * In read-only mode, the databases are not locked and are opened + * only for reading. + */ +-static void open_files (bool readonly) ++static void open_files (bool readonly, struct option_flags *flags) + { ++ bool process_selinux; ++ ++ process_selinux = !flags->chroot && !flags->prefix; ++ + /* + * Lock and open the password file. This loads all of the password + * file entries into memory. Then we get a pointer to the password +@@ -504,14 +518,14 @@ static void open_files (bool readonly) + fprintf (stderr, + _("%s: cannot lock %s; try again later.\n"), + Prog, pw_dbname ()); +- fail_exit (E_NOPERM); ++ fail_exit (E_NOPERM, process_selinux); + } + pw_locked = true; + } + if (pw_open (readonly ? O_RDONLY: O_CREAT | O_RDWR) == 0) { + fprintf (stderr, _("%s: cannot open %s\n"), Prog, pw_dbname ()); + SYSLOG ((LOG_WARN, "cannot open %s", pw_dbname ())); +- fail_exit (E_NOPERM); ++ fail_exit (E_NOPERM, process_selinux); + } + + /* +@@ -525,7 +539,7 @@ static void open_files (bool readonly) + fprintf (stderr, + _("%s: cannot lock %s; try again later.\n"), + Prog, spw_dbname ()); +- fail_exit (E_NOPERM); ++ fail_exit (E_NOPERM, process_selinux); + } + spw_locked = true; + } +@@ -533,42 +547,46 @@ static void open_files (bool readonly) + fprintf (stderr, + _("%s: cannot open %s\n"), Prog, spw_dbname ()); + SYSLOG ((LOG_WARN, "cannot open %s", spw_dbname ())); +- fail_exit (E_NOPERM); ++ fail_exit (E_NOPERM, process_selinux); + } + } + + /* + * close_files - close and unlock the password/shadow databases + */ +-static void close_files (void) ++static void close_files (struct option_flags *flags) + { ++ bool process_selinux; ++ ++ process_selinux = !flags->chroot && !flags->prefix; ++ + /* + * Now close the shadow password file, which will cause all of the + * entries to be re-written. + */ +- if (spw_close (true) == 0) { ++ if (spw_close (process_selinux) == 0) { + fprintf (stderr, + _("%s: failure while writing changes to %s\n"), Prog, spw_dbname ()); + SYSLOG ((LOG_ERR, "failure while writing changes to %s", spw_dbname ())); +- fail_exit (E_NOPERM); ++ fail_exit (E_NOPERM, process_selinux); + } + + /* + * Close the password file. If any entries were modified, the file + * will be re-written. + */ +- if (pw_close (true) == 0) { ++ if (pw_close (process_selinux) == 0) { + fprintf (stderr, _("%s: failure while writing changes to %s\n"), Prog, pw_dbname ()); + SYSLOG ((LOG_ERR, "failure while writing changes to %s", pw_dbname ())); +- fail_exit (E_NOPERM); ++ fail_exit (E_NOPERM, process_selinux); + } +- if (spw_unlock (true) == 0) { ++ if (spw_unlock (process_selinux) == 0) { + fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, spw_dbname ()); + SYSLOG ((LOG_ERR, "failed to unlock %s", spw_dbname ())); + /* continue */ + } + spw_locked = false; +- if (pw_unlock (true) == 0) { ++ if (pw_unlock (process_selinux) == 0) { + fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, pw_dbname ()); + SYSLOG ((LOG_ERR, "failed to unlock %s", pw_dbname ())); + /* continue */ +@@ -582,7 +600,8 @@ static void close_files (void) + * It will not return in case of error + */ + static void update_age (/*@null@*/const struct spwd *sp, +- /*@notnull@*/const struct passwd *pw) ++ /*@notnull@*/const struct passwd *pw, ++ bool process_selinux) + { + struct spwd spwent; + +@@ -603,7 +622,7 @@ static void update_age (/*@null@*/const + if (pw_update (&pwent) == 0) { + fprintf (stderr, + _("%s: failed to prepare the new %s entry '%s'\n"), Prog, pw_dbname (), pwent.pw_name); +- fail_exit (E_NOPERM); ++ fail_exit (E_NOPERM, process_selinux); + } + } else { + spwent.sp_namp = xstrdup (sp->sp_namp); +@@ -626,7 +645,7 @@ static void update_age (/*@null@*/const + if (spw_update (&spwent) == 0) { + fprintf (stderr, + _("%s: failed to prepare the new %s entry '%s'\n"), Prog, spw_dbname (), spwent.sp_namp); +- fail_exit (E_NOPERM); ++ fail_exit (E_NOPERM, process_selinux); + } + + } +@@ -712,6 +731,8 @@ int main (int argc, char **argv) + uid_t ruid; + gid_t rgid; + const struct passwd *pw; ++ struct option_flags flags; ++ bool process_selinux; + + sanitize_env (); + check_fds (); +@@ -740,9 +761,10 @@ int main (int argc, char **argv) + } + #endif + +- process_flags (argc, argv); ++ process_flags (argc, argv, &flags); ++ process_selinux = !flags.chroot && !flags.prefix; + +- check_perms (); ++ check_perms (&flags); + + if (!spw_file_present ()) { + fprintf (stderr, +@@ -753,13 +775,13 @@ int main (int argc, char **argv) + exit (E_SHADOW_NOTFOUND); + } + +- open_files (lflg); ++ open_files (lflg, &flags); + /* Drop privileges */ + if (lflg && ( (setregid (rgid, rgid) != 0) + || (setreuid (ruid, ruid) != 0))) { + fprintf (stderr, _("%s: failed to drop privileges (%s)\n"), + Prog, strerror (errno)); +- fail_exit (E_NOPERM); ++ fail_exit (E_NOPERM, process_selinux); + } + + pw = pw_locate (argv[optind]); +@@ -767,13 +789,13 @@ int main (int argc, char **argv) + fprintf (stderr, _("%s: user '%s' does not exist in %s\n"), + Prog, argv[optind], pw_dbname ()); + closelog (); +- fail_exit (E_NOPERM); ++ fail_exit (E_NOPERM, process_selinux); + } + + STRTCPY(user_name, pw->pw_name); + #ifdef WITH_TCB + if (shadowtcb_set_user (pw->pw_name) == SHADOWTCB_FAILURE) { +- fail_exit (E_NOPERM); ++ fail_exit (E_NOPERM, process_selinux); + } + #endif + user_uid = pw->pw_uid; +@@ -788,11 +810,11 @@ int main (int argc, char **argv) + if (lflg) { + if (!amroot && (ruid != user_uid)) { + fprintf (stderr, _("%s: Permission denied.\n"), Prog); +- fail_exit (E_NOPERM); ++ fail_exit (E_NOPERM, process_selinux); + } + /* Displaying fields is not of interest to audit */ + list_fields (); +- fail_exit (E_SUCCESS); ++ fail_exit (E_SUCCESS, process_selinux); + } + + /* +@@ -805,7 +827,7 @@ int main (int argc, char **argv) + if (new_fields () == 0) { + fprintf (stderr, _("%s: error changing fields\n"), + Prog); +- fail_exit (E_NOPERM); ++ fail_exit (E_NOPERM, process_selinux); + } + #ifdef WITH_AUDIT + else { +@@ -847,9 +869,9 @@ int main (int argc, char **argv) + #endif + } + +- update_age (sp, pw); ++ update_age (sp, pw, process_selinux); + +- close_files (); ++ close_files (&flags); + + SYSLOG ((LOG_INFO, "changed password expiry for %s", user_name)); + +diff -up shadow-4.18.0/src/chfn.c.test-chroot4 shadow-4.18.0/src/chfn.c +--- shadow-4.18.0/src/chfn.c.test-chroot4 2025-07-04 11:17:37.590501245 +0200 ++++ shadow-4.18.0/src/chfn.c 2025-07-04 11:17:37.613576062 +0200 +@@ -39,6 +39,9 @@ + #include "string/strdup/xstrdup.h" + #include "string/strtok/stpsep.h" + ++struct option_flags { ++ bool chroot; ++}; + + /* + * Global variables. +@@ -63,23 +66,23 @@ static bool pw_locked = false; + */ + + /* local function prototypes */ +-NORETURN static void fail_exit (int code); ++NORETURN static void fail_exit (int code, bool process_selinux); + NORETURN static void usage (int status); + static bool may_change_field (int); + static void new_fields (void); + static char *copy_field (char *, char *, char *); +-static void process_flags (int argc, char **argv); ++static void process_flags (int argc, char **argv, struct option_flags *flags); + static void check_perms (const struct passwd *pw); +-static void update_gecos (const char *user, char *gecos); ++static void update_gecos (const char *user, char *gecos, struct option_flags *flags); + static void get_old_fields (const char *gecos); + + /* + * fail_exit - exit with an error and do some cleanup + */ +-static void fail_exit (int code) ++static void fail_exit (int code, bool process_selinux) + { + if (pw_locked) { +- if (pw_unlock (true) == 0) { ++ if (pw_unlock (process_selinux) == 0) { + fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, pw_dbname ()); + SYSLOG ((LOG_ERR, "failed to unlock %s", pw_dbname ())); + /* continue */ +@@ -250,7 +253,7 @@ static char *copy_field (char *in, char + * + * It will not return if an error is encountered. + */ +-static void process_flags (int argc, char **argv) ++static void process_flags (int argc, char **argv, struct option_flags *flags) + { + int c; /* flag currently being processed */ + static struct option long_options[] = { +@@ -316,6 +319,7 @@ static void process_flags (int argc, cha + STRTCPY(roomno, optarg); + break; + case 'R': /* no-op, handled in process_root_flag () */ ++ flags->chroot = true; + break; + case 'u': + usage (E_SUCCESS); +@@ -423,10 +427,13 @@ static void check_perms (const struct pa + * + * Commit the user's entry after changing her gecos field. + */ +-static void update_gecos (const char *user, char *gecos) ++static void update_gecos (const char *user, char *gecos, struct option_flags *flags) + { + const struct passwd *pw; /* The user's password file entry */ + struct passwd pwent; /* modified password file entry */ ++ bool process_selinux; ++ ++ process_selinux = !flags->chroot; + + /* + * Before going any further, raise the ulimit to prevent colliding +@@ -437,7 +444,7 @@ static void update_gecos (const char *us + if (setuid (0) != 0) { + fputs (_("Cannot change ID to root.\n"), stderr); + SYSLOG ((LOG_ERR, "can't setuid(0)")); +- fail_exit (E_NOPERM); ++ fail_exit (E_NOPERM, process_selinux); + } + pwd_init (); + +@@ -449,13 +456,13 @@ static void update_gecos (const char *us + fprintf (stderr, + _("%s: cannot lock %s; try again later.\n"), + Prog, pw_dbname ()); +- fail_exit (E_NOPERM); ++ fail_exit (E_NOPERM, process_selinux); + } + pw_locked = true; + if (pw_open (O_CREAT | O_RDWR) == 0) { + fprintf (stderr, + _("%s: cannot open %s\n"), Prog, pw_dbname ()); +- fail_exit (E_NOPERM); ++ fail_exit (E_NOPERM, process_selinux); + } + + /* +@@ -469,7 +476,7 @@ static void update_gecos (const char *us + fprintf (stderr, + _("%s: user '%s' does not exist in %s\n"), + Prog, user, pw_dbname ()); +- fail_exit (E_NOPERM); ++ fail_exit (E_NOPERM, process_selinux); + } + + /* +@@ -487,18 +494,18 @@ static void update_gecos (const char *us + fprintf (stderr, + _("%s: failed to prepare the new %s entry '%s'\n"), + Prog, pw_dbname (), pwent.pw_name); +- fail_exit (E_NOPERM); ++ fail_exit (E_NOPERM, process_selinux); + } + + /* + * Changes have all been made, so commit them and unlock the file. + */ +- if (pw_close (true) == 0) { ++ if (pw_close (process_selinux) == 0) { + fprintf (stderr, _("%s: failure while writing changes to %s\n"), Prog, pw_dbname ()); + SYSLOG ((LOG_ERR, "failure while writing changes to %s", pw_dbname ())); +- fail_exit (E_NOPERM); ++ fail_exit (E_NOPERM, process_selinux); + } +- if (pw_unlock (true) == 0) { ++ if (pw_unlock (process_selinux) == 0) { + fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, pw_dbname ()); + SYSLOG ((LOG_ERR, "failed to unlock %s", pw_dbname ())); + /* continue */ +@@ -562,7 +569,7 @@ static void get_old_fields (const char * + * + * It will not return if a field is not valid. + */ +-static void check_fields (void) ++static void check_fields (bool process_selinux) + { + int err; + err = valid_field (fullnm, ":,=\n"); +@@ -570,7 +577,7 @@ static void check_fields (void) + fprintf (stderr, _("%s: name with non-ASCII characters: '%s'\n"), Prog, fullnm); + } else if (err < 0) { + fprintf (stderr, _("%s: invalid name: '%s'\n"), Prog, fullnm); +- fail_exit (E_NOPERM); ++ fail_exit (E_NOPERM, process_selinux); + } + err = valid_field (roomno, ":,=\n"); + if (err > 0) { +@@ -578,17 +585,17 @@ static void check_fields (void) + } else if (err < 0) { + fprintf (stderr, _("%s: invalid room number: '%s'\n"), + Prog, roomno); +- fail_exit (E_NOPERM); ++ fail_exit (E_NOPERM, process_selinux); + } + if (valid_field (workph, ":,=\n") != 0) { + fprintf (stderr, _("%s: invalid work phone: '%s'\n"), + Prog, workph); +- fail_exit (E_NOPERM); ++ fail_exit (E_NOPERM, process_selinux); + } + if (valid_field (homeph, ":,=\n") != 0) { + fprintf (stderr, _("%s: invalid home phone: '%s'\n"), + Prog, homeph); +- fail_exit (E_NOPERM); ++ fail_exit (E_NOPERM, process_selinux); + } + err = valid_field (slop, ":\n"); + if (err > 0) { +@@ -597,7 +604,7 @@ static void check_fields (void) + fprintf (stderr, + _("%s: '%s' contains illegal characters\n"), + Prog, slop); +- fail_exit (E_NOPERM); ++ fail_exit (E_NOPERM, process_selinux); + } + } + +@@ -622,6 +629,8 @@ int main (int argc, char **argv) + char new_gecos[BUFSIZ]; + char *user; + const struct passwd *pw; ++ struct option_flags flags; ++ bool process_selinux; + + sanitize_env (); + check_fds (); +@@ -644,7 +653,7 @@ int main (int argc, char **argv) + OPENLOG (Prog); + + /* parse the command line options */ +- process_flags (argc, argv); ++ process_flags (argc, argv, &flags); + + /* + * Get the name of the user to check. It is either the command line +@@ -653,14 +662,14 @@ int main (int argc, char **argv) + if (optind < argc) { + if (!is_valid_user_name (argv[optind])) { + fprintf (stderr, _("%s: Provided user name is not a valid name\n"), Prog); +- fail_exit (E_NOPERM); ++ fail_exit (E_NOPERM, process_selinux); + } + user = argv[optind]; + pw = xgetpwnam (user); + if (NULL == pw) { + fprintf (stderr, _("%s: user '%s' does not exist\n"), Prog, + user); +- fail_exit (E_NOPERM); ++ fail_exit (E_NOPERM, process_selinux); + } + } else { + pw = get_my_pwent (); +@@ -670,7 +679,7 @@ int main (int argc, char **argv) + Prog); + SYSLOG ((LOG_WARN, "Cannot determine the user name of the caller (UID %lu)", + (unsigned long) getuid ())); +- fail_exit (E_NOPERM); ++ fail_exit (E_NOPERM, process_selinux); + } + user = xstrdup (pw->pw_name); + } +@@ -695,7 +704,7 @@ int main (int argc, char **argv) + /* + * Check all of the fields for valid information + */ +- check_fields (); ++ check_fields (process_selinux); + + /* + * Build the new GECOS field by plastering all the pieces together, +@@ -704,14 +713,14 @@ int main (int argc, char **argv) + if ((strlen (fullnm) + strlen (roomno) + strlen (workph) + + strlen (homeph) + strlen (slop)) > (unsigned int) 80) { + fprintf (stderr, _("%s: fields too long\n"), Prog); +- fail_exit (E_NOPERM); ++ fail_exit (E_NOPERM, process_selinux); + } + SNPRINTF(new_gecos, "%s,%s,%s,%s%s%s", + fullnm, roomno, workph, homeph, + (!streq(slop, "")) ? "," : "", slop); + + /* Rewrite the user's gecos in the passwd file */ +- update_gecos (user, new_gecos); ++ update_gecos (user, new_gecos, &flags); + + SYSLOG ((LOG_INFO, "changed user '%s' information", user)); + +diff -up shadow-4.18.0/src/chpasswd.c.test-chroot4 shadow-4.18.0/src/chpasswd.c +--- shadow-4.18.0/src/chpasswd.c.test-chroot4 2025-07-04 11:17:37.593768325 +0200 ++++ shadow-4.18.0/src/chpasswd.c 2025-07-04 11:18:06.785652711 +0200 +@@ -38,6 +38,11 @@ + + #define IS_CRYPT_METHOD(str) ((crypt_method != NULL && streq(crypt_method, str)) ? true : false) + ++struct option_flags { ++ bool chroot; ++ bool prefix; ++}; ++ + /* + * Global variables + */ +@@ -67,21 +72,21 @@ static bool pw_locked = false; + static bool spw_locked = false; + + /* local function prototypes */ +-NORETURN static void fail_exit (int code); ++NORETURN static void fail_exit (int code, bool process_selinux); + NORETURN static void usage (int status); +-static void process_flags (int argc, char **argv); ++static void process_flags (int argc, char **argv, struct option_flags *flags); + static void check_flags (void); + static void check_perms (void); +-static void open_files (void); +-static void close_files (void); ++static void open_files (struct option_flags *flags); ++static void close_files (struct option_flags *flags); + + /* + * fail_exit - exit with a failure code after unlocking the files + */ +-static void fail_exit (int code) ++static void fail_exit (int code, bool process_selinux) + { + if (pw_locked) { +- if (pw_unlock (true) == 0) { ++ if (pw_unlock (process_selinux) == 0) { + fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, pw_dbname ()); + SYSLOG ((LOG_ERR, "failed to unlock %s", pw_dbname ())); + /* continue */ +@@ -89,7 +94,7 @@ static void fail_exit (int code) + } + + if (spw_locked) { +- if (spw_unlock (true) == 0) { ++ if (spw_unlock (process_selinux) == 0) { + fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, spw_dbname ()); + SYSLOG ((LOG_ERR, "failed to unlock %s", spw_dbname ())); + /* continue */ +@@ -147,7 +152,7 @@ usage (int status) + * + * It will not return if an error is encountered. + */ +-static void process_flags (int argc, char **argv) ++static void process_flags (int argc, char **argv, struct option_flags *flags) + { + int c; + #if defined(USE_SHA_CRYPT) || defined(USE_BCRYPT) || defined(USE_YESCRYPT) +@@ -187,8 +192,10 @@ static void process_flags (int argc, cha + md5flg = true; + break; + case 'R': /* no-op, handled in process_root_flag () */ ++ flags->chroot = true; + break; + case 'P': /* no-op, handled in process_prefix_flag () */ ++ flags->prefix = true; + break; + #if defined(USE_SHA_CRYPT) || defined(USE_BCRYPT) || defined(USE_YESCRYPT) + case 's': +@@ -335,8 +342,12 @@ static void check_perms (void) + /* + * open_files - lock and open the password databases + */ +-static void open_files (void) ++static void open_files (struct option_flags *flags) + { ++ bool process_selinux; ++ ++ process_selinux = !flags->chroot && !flags->prefix; ++ + /* + * Lock the password file and open it for reading and writing. This + * will bring all of the entries into memory where they may be updated. +@@ -345,13 +356,13 @@ static void open_files (void) + fprintf (stderr, + _("%s: cannot lock %s; try again later.\n"), + Prog, pw_dbname ()); +- fail_exit (1); ++ fail_exit (1, process_selinux); + } + pw_locked = true; + if (pw_open (O_CREAT | O_RDWR) == 0) { + fprintf (stderr, + _("%s: cannot open %s\n"), Prog, pw_dbname ()); +- fail_exit (1); ++ fail_exit (1, process_selinux); + } + + /* Do the same for the shadowed database, if it exist */ +@@ -360,14 +371,14 @@ static void open_files (void) + fprintf (stderr, + _("%s: cannot lock %s; try again later.\n"), + Prog, spw_dbname ()); +- fail_exit (1); ++ fail_exit (1, process_selinux); + } + spw_locked = true; + if (spw_open (O_CREAT | O_RDWR) == 0) { + fprintf (stderr, + _("%s: cannot open %s\n"), + Prog, spw_dbname ()); +- fail_exit (1); ++ fail_exit (1, process_selinux); + } + } + } +@@ -375,17 +386,21 @@ static void open_files (void) + /* + * close_files - close and unlock the password databases + */ +-static void close_files (void) ++static void close_files (struct option_flags *flags) + { ++ bool process_selinux; ++ ++ process_selinux = !flags->chroot && !flags->prefix; ++ + if (is_shadow_pwd) { +- if (spw_close (true) == 0) { ++ if (spw_close (process_selinux) == 0) { + fprintf (stderr, + _("%s: failure while writing changes to %s\n"), + Prog, spw_dbname ()); + SYSLOG ((LOG_ERR, "failure while writing changes to %s", spw_dbname ())); +- fail_exit (1); ++ fail_exit (1, process_selinux); + } +- if (spw_unlock (true) == 0) { ++ if (spw_unlock (process_selinux) == 0) { + fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, spw_dbname ()); + SYSLOG ((LOG_ERR, "failed to unlock %s", spw_dbname ())); + /* continue */ +@@ -393,14 +408,14 @@ static void close_files (void) + spw_locked = false; + } + +- if (pw_close (true) == 0) { ++ if (pw_close (process_selinux) == 0) { + fprintf (stderr, + _("%s: failure while writing changes to %s\n"), + Prog, pw_dbname ()); + SYSLOG ((LOG_ERR, "failure while writing changes to %s", pw_dbname ())); +- fail_exit (1); ++ fail_exit (1, process_selinux); + } +- if (pw_unlock (true) == 0) { ++ if (pw_unlock (process_selinux) == 0) { + fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, pw_dbname ()); + SYSLOG ((LOG_ERR, "failed to unlock %s", pw_dbname ())); + /* continue */ +@@ -456,6 +471,8 @@ int main (int argc, char **argv) + + bool errors = false; + intmax_t line = 0; ++ struct option_flags flags; ++ bool process_selinux; + + log_set_progname(Prog); + log_set_logfd(stderr); +@@ -470,7 +487,8 @@ int main (int argc, char **argv) + } + #endif /* WITH_SELINUX */ + +- process_flags (argc, argv); ++ process_flags (argc, argv, &flags); ++ process_selinux = !flags.chroot && !flags.prefix; + + salt = get_salt(); + process_root_flag ("-R", argc, argv); +@@ -496,7 +514,7 @@ int main (int argc, char **argv) + { + is_shadow_pwd = spw_file_present (); + +- open_files (); ++ open_files (&flags); + } + + /* +@@ -573,7 +591,7 @@ int main (int argc, char **argv) + fprintf (stderr, + _("%s: failed to crypt password with salt '%s': %s\n"), + Prog, salt, strerror (errno)); +- fail_exit (1); ++ fail_exit (1, process_selinux); + } + } + +@@ -690,7 +708,7 @@ int main (int argc, char **argv) + _("%s: error detected, changes ignored\n"), + Prog); + } +- fail_exit (1); ++ fail_exit (1, process_selinux); + } + + #ifdef ACCT_TOOLS_SETUID +@@ -700,7 +718,7 @@ int main (int argc, char **argv) + #endif /* ACCT_TOOLS_SETUID */ + { + /* Save the changes */ +- close_files (); ++ close_files (&flags); + } + + nscd_flush_cache ("passwd"); +diff -up shadow-4.18.0/src/grpconv.c.test-chroot4 shadow-4.18.0/src/grpconv.c +--- shadow-4.18.0/src/grpconv.c.test-chroot4 2025-07-04 11:17:37.598073521 +0200 ++++ shadow-4.18.0/src/grpconv.c 2025-07-04 11:17:37.612354766 +0200 +@@ -39,6 +39,12 @@ + #include "shadowlog.h" + #include "sssd.h" + ++/* ++ * Structures ++ */ ++struct option_flags { ++ bool chroot; ++}; + + /* + * Global variables +@@ -49,14 +55,14 @@ static bool gr_locked = false; + static bool sgr_locked = false; + + /* local function prototypes */ +-static void fail_exit (int status); ++static void fail_exit (int status, bool process_selinux); + static void usage (int status); +-static void process_flags (int argc, char **argv); ++static void process_flags (int argc, char **argv, struct option_flags *flags); + +-static void fail_exit (int status) ++static void fail_exit (int status, bool process_selinux) + { + if (gr_locked) { +- if (gr_unlock (true) == 0) { ++ if (gr_unlock (process_selinux) == 0) { + fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, gr_dbname ()); + SYSLOG ((LOG_ERR, "failed to unlock %s", gr_dbname ())); + /* continue */ +@@ -64,7 +70,7 @@ static void fail_exit (int status) + } + + if (sgr_locked) { +- if (sgr_unlock (true) == 0) { ++ if (sgr_unlock (process_selinux) == 0) { + fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, sgr_dbname ()); + SYSLOG ((LOG_ERR, "failed to unlock %s", sgr_dbname ())); + /* continue */ +@@ -93,7 +99,7 @@ static void usage (int status) + * + * It will not return if an error is encountered. + */ +-static void process_flags (int argc, char **argv) ++static void process_flags (int argc, char **argv, struct option_flags *flags) + { + /* + * Parse the command line options. +@@ -112,6 +118,7 @@ static void process_flags (int argc, cha + usage (E_SUCCESS); + /*@notreached@*/break; + case 'R': /* no-op, handled in process_root_flag () */ ++ flags->chroot = true; + break; + default: + usage (E_USAGE); +@@ -129,6 +136,8 @@ int main (int argc, char **argv) + struct group grent; + const struct sgrp *sg; + struct sgrp sgent; ++ struct option_flags flags; ++ bool process_selinux; + + log_set_progname(Prog); + log_set_logfd(stderr); +@@ -141,30 +150,31 @@ int main (int argc, char **argv) + + OPENLOG (Prog); + +- process_flags (argc, argv); ++ process_flags (argc, argv, &flags); ++ process_selinux = !flags.chroot; + + if (gr_lock () == 0) { + fprintf (stderr, + _("%s: cannot lock %s; try again later.\n"), + Prog, gr_dbname ()); +- fail_exit (5); ++ fail_exit (5, process_selinux); + } + gr_locked = true; + if (gr_open (O_CREAT | O_RDWR) == 0) { + fprintf (stderr, _("%s: cannot open %s\n"), Prog, gr_dbname ()); +- fail_exit (1); ++ fail_exit (1, process_selinux); + } + + if (sgr_lock () == 0) { + fprintf (stderr, + _("%s: cannot lock %s; try again later.\n"), + Prog, sgr_dbname ()); +- fail_exit (5); ++ fail_exit (5, process_selinux); + } + sgr_locked = true; + if (sgr_open (O_CREAT | O_RDWR) == 0) { + fprintf (stderr, _("%s: cannot open %s\n"), Prog, sgr_dbname ()); +- fail_exit (1); ++ fail_exit (1, process_selinux); + } + + /* +@@ -183,7 +193,7 @@ int main (int argc, char **argv) + fprintf (stderr, + _("%s: cannot remove entry '%s' from %s\n"), + Prog, sg->sg_namp, sgr_dbname ()); +- fail_exit (3); ++ fail_exit (3, process_selinux); + } + (void) sgr_rewind (); + } +@@ -221,7 +231,7 @@ int main (int argc, char **argv) + fprintf (stderr, + _("%s: failed to prepare the new %s entry '%s'\n"), + Prog, sgr_dbname (), sgent.sg_namp); +- fail_exit (3); ++ fail_exit (3, process_selinux); + } + /* remove password from /etc/group */ + grent = *gr; +@@ -230,30 +240,30 @@ int main (int argc, char **argv) + fprintf (stderr, + _("%s: failed to prepare the new %s entry '%s'\n"), + Prog, gr_dbname (), grent.gr_name); +- fail_exit (3); ++ fail_exit (3, process_selinux); + } + } + +- if (sgr_close (true) == 0) { ++ if (sgr_close (process_selinux) == 0) { + fprintf (stderr, + _("%s: failure while writing changes to %s\n"), + Prog, sgr_dbname ()); + SYSLOG ((LOG_ERR, "failure while writing changes to %s", sgr_dbname ())); +- fail_exit (3); ++ fail_exit (3, process_selinux); + } +- if (gr_close (true) == 0) { ++ if (gr_close (process_selinux) == 0) { + fprintf (stderr, + _("%s: failure while writing changes to %s\n"), + Prog, gr_dbname ()); + SYSLOG ((LOG_ERR, "failure while writing changes to %s", gr_dbname ())); +- fail_exit (3); ++ fail_exit (3, process_selinux); + } +- if (sgr_unlock (true) == 0) { ++ if (sgr_unlock (process_selinux) == 0) { + fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, sgr_dbname ()); + SYSLOG ((LOG_ERR, "failed to unlock %s", sgr_dbname ())); + /* continue */ + } +- if (gr_unlock (true) == 0) { ++ if (gr_unlock (process_selinux) == 0) { + fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, gr_dbname ()); + SYSLOG ((LOG_ERR, "failed to unlock %s", gr_dbname ())); + /* continue */ +diff -up shadow-4.18.0/src/grpunconv.c.test-chroot4 shadow-4.18.0/src/grpunconv.c +--- shadow-4.18.0/src/grpunconv.c.test-chroot4 2025-07-04 11:17:37.598110132 +0200 ++++ shadow-4.18.0/src/grpunconv.c 2025-07-04 11:17:37.612706894 +0200 +@@ -38,6 +38,12 @@ + #include "sgroupio.h" + #include "shadowlog.h" + ++/* ++ * Structures ++ */ ++struct option_flags { ++ bool chroot; ++}; + + /* + * Global variables +@@ -48,14 +54,14 @@ static bool gr_locked = false; + static bool sgr_locked = false; + + /* local function prototypes */ +-static void fail_exit (int status); ++static void fail_exit (int status, bool process_selinux); + static void usage (int status); +-static void process_flags (int argc, char **argv); ++static void process_flags (int argc, char **argv, struct option_flags *flags); + +-static void fail_exit (int status) ++static void fail_exit (int status, bool process_selinux) + { + if (gr_locked) { +- if (gr_unlock (true) == 0) { ++ if (gr_unlock (process_selinux) == 0) { + fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, gr_dbname ()); + SYSLOG ((LOG_ERR, "failed to unlock %s", gr_dbname ())); + /* continue */ +@@ -63,7 +69,7 @@ static void fail_exit (int status) + } + + if (sgr_locked) { +- if (sgr_unlock (true) == 0) { ++ if (sgr_unlock (process_selinux) == 0) { + fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, sgr_dbname ()); + SYSLOG ((LOG_ERR, "failed to unlock %s", sgr_dbname ())); + /* continue */ +@@ -92,7 +98,7 @@ static void usage (int status) + * + * It will not return if an error is encountered. + */ +-static void process_flags (int argc, char **argv) ++static void process_flags (int argc, char **argv, struct option_flags *flags) + { + /* + * Parse the command line options. +@@ -111,6 +117,7 @@ static void process_flags (int argc, cha + usage (E_SUCCESS); + /*@notreached@*/break; + case 'R': /* no-op, handled in process_root_flag () */ ++ flags->chroot = true; + break; + default: + usage (E_USAGE); +@@ -127,6 +134,8 @@ int main (int argc, char **argv) + const struct group *gr; + struct group grent; + const struct sgrp *sg; ++ struct option_flags flags; ++ bool process_selinux; + + log_set_progname(Prog); + log_set_logfd(stderr); +@@ -139,7 +148,8 @@ int main (int argc, char **argv) + + OPENLOG (Prog); + +- process_flags (argc, argv); ++ process_flags (argc, argv, &flags); ++ process_selinux = !flags.chroot; + + if (sgr_file_present () == 0) { + exit (0); /* no /etc/gshadow, nothing to do */ +@@ -149,26 +159,26 @@ int main (int argc, char **argv) + fprintf (stderr, + _("%s: cannot lock %s; try again later.\n"), + Prog, gr_dbname ()); +- fail_exit (5); ++ fail_exit (5, process_selinux); + } + gr_locked = true; + if (gr_open (O_CREAT | O_RDWR) == 0) { + fprintf (stderr, + _("%s: cannot open %s\n"), Prog, gr_dbname ()); +- fail_exit (1); ++ fail_exit (1, process_selinux); + } + + if (sgr_lock () == 0) { + fprintf (stderr, + _("%s: cannot lock %s; try again later.\n"), + Prog, sgr_dbname ()); +- fail_exit (5); ++ fail_exit (5, process_selinux); + } + sgr_locked = true; + if (sgr_open (O_RDONLY) == 0) { + fprintf (stderr, + _("%s: cannot open %s\n"), Prog, sgr_dbname ()); +- fail_exit (1); ++ fail_exit (1, process_selinux); + } + + /* +@@ -186,19 +196,19 @@ int main (int argc, char **argv) + fprintf (stderr, + _("%s: failed to prepare the new %s entry '%s'\n"), + Prog, gr_dbname (), grent.gr_name); +- fail_exit (3); ++ fail_exit (3, process_selinux); + } + } + } + +- (void) sgr_close (true); /* was only open O_RDONLY */ ++ (void) sgr_close (process_selinux); /* was only open O_RDONLY */ + +- if (gr_close (true) == 0) { ++ if (gr_close (process_selinux) == 0) { + fprintf (stderr, + _("%s: failure while writing changes to %s\n"), + Prog, gr_dbname ()); + SYSLOG ((LOG_ERR, "failure while writing changes to %s", gr_dbname ())); +- fail_exit (3); ++ fail_exit (3, process_selinux); + } + + if (unlink (SGROUP_FILE) != 0) { +@@ -206,16 +216,16 @@ int main (int argc, char **argv) + _("%s: cannot delete %s\n"), + Prog, SGROUP_FILE); + SYSLOG ((LOG_ERR, "cannot delete %s", SGROUP_FILE)); +- fail_exit (3); ++ fail_exit (3, process_selinux); + } + +- if (gr_unlock (true) == 0) { ++ if (gr_unlock (process_selinux) == 0) { + fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, gr_dbname ()); + SYSLOG ((LOG_ERR, "failed to unlock %s", gr_dbname ())); + /* continue */ + } + +- if (sgr_unlock (true) == 0) { ++ if (sgr_unlock (process_selinux) == 0) { + fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, sgr_dbname ()); + SYSLOG ((LOG_ERR, "failed to unlock %s", sgr_dbname ())); + /* continue */ + +From e7634eac3da8b6a2a046b7e8ab6148d07618c7f5 Mon Sep 17 00:00:00 2001 +From: Iker Pedrosa +Date: Thu, 3 Jul 2025 11:59:32 +0200 +Subject: [PATCH 01/12] 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 +--- + src/chsh.c | 25 +++++++++++++++++-------- + 1 file changed, 17 insertions(+), 8 deletions(-) + +diff --git a/src/chsh.c b/src/chsh.c +index 4403d412..f460e329 100644 +--- a/src/chsh.c ++++ b/src/chsh.c +@@ -47,6 +47,9 @@ + #define ETCDIR "/etc" + #endif + ++struct option_flags { ++ bool chroot; ++}; + + /* + * Global variables +@@ -66,9 +69,10 @@ NORETURN static void usage (int status); + static void new_fields (void); + static bool shell_is_listed (const char *); + static bool is_restricted_shell (const char *); +-static void process_flags (int argc, char **argv); ++static void process_flags (int argc, char **argv, struct option_flags *flags); + static void check_perms (const struct passwd *pw); +-static void update_shell (const char *user, char *loginsh); ++static void update_shell (const char *user, char *loginsh, ++ struct option_flags *flags); + + /* + * fail_exit - do some cleanup and exit with the given error code +@@ -217,7 +221,7 @@ static bool shell_is_listed (const char *sh) + * + * It will not return if an error is encountered. + */ +-static void process_flags (int argc, char **argv) ++static void process_flags (int argc, char **argv, struct option_flags *flags) + { + int c; + static struct option long_options[] = { +@@ -234,6 +238,7 @@ static void process_flags (int argc, char **argv) + usage (E_SUCCESS); + /*@notreached@*/break; + case 'R': /* no-op, handled in process_root_flag () */ ++ flags->chroot = true; + break; + case 's': + sflg = true; +@@ -359,10 +364,13 @@ static void check_perms (const struct passwd *pw) + * + * It will not return in case of error. + */ +-static void update_shell (const char *user, char *newshell) ++static void update_shell (const char *user, char *newshell, struct option_flags *flags) + { + const struct passwd *pw; /* Password entry from /etc/passwd */ + struct passwd pwent; /* New password entry */ ++ bool process_selinux; ++ ++ process_selinux = !flags->chroot; + + /* + * Before going any further, raise the ulimit to prevent +@@ -428,12 +436,12 @@ static void update_shell (const char *user, char *newshell) + /* + * Changes have all been made, so commit them and unlock the file. + */ +- if (pw_close (true) == 0) { ++ if (pw_close (process_selinux) == 0) { + fprintf (stderr, _("%s: failure while writing changes to %s\n"), Prog, pw_dbname ()); + SYSLOG ((LOG_ERR, "failure while writing changes to %s", pw_dbname ())); + fail_exit (1); + } +- if (pw_unlock (true) == 0) { ++ if (pw_unlock (process_selinux) == 0) { + fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, pw_dbname ()); + SYSLOG ((LOG_ERR, "failed to unlock %s", pw_dbname ())); + /* continue */ +@@ -451,6 +459,7 @@ int main (int argc, char **argv) + { + char *user; /* User name */ + const struct passwd *pw; /* Password entry from /etc/passwd */ ++ struct option_flags flags; + + sanitize_env (); + check_fds (); +@@ -472,7 +481,7 @@ int main (int argc, char **argv) + OPENLOG (Prog); + + /* parse the command line options */ +- process_flags (argc, argv); ++ process_flags (argc, argv, &flags); + + /* + * Get the name of the user to check. It is either the command line +@@ -555,7 +564,7 @@ int main (int argc, char **argv) + } + } + +- update_shell (user, loginsh); ++ update_shell (user, loginsh, &flags); + + SYSLOG ((LOG_INFO, "changed user '%s' shell to '%s'", user, loginsh)); + +-- +2.50.0 + + +From cbcf54b3e5c77a4992bca0ae4f32f0060d120884 Mon Sep 17 00:00:00 2001 +From: Iker Pedrosa +Date: Thu, 3 Jul 2025 12:03:30 +0200 +Subject: [PATCH 02/12] 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 +--- + src/chsh.c | 45 +++++++++++++++++++++++++-------------------- + 1 file changed, 25 insertions(+), 20 deletions(-) + +diff --git a/src/chsh.c b/src/chsh.c +index f460e329..77cd30e5 100644 +--- a/src/chsh.c ++++ b/src/chsh.c +@@ -64,13 +64,13 @@ static bool pw_locked = false; + /* external identifiers */ + + /* local function prototypes */ +-NORETURN static void fail_exit (int code); ++NORETURN static void fail_exit (int code, bool process_selinux); + NORETURN static void usage (int status); + static void new_fields (void); + static bool shell_is_listed (const char *); + static bool is_restricted_shell (const char *); + static void process_flags (int argc, char **argv, struct option_flags *flags); +-static void check_perms (const struct passwd *pw); ++static void check_perms (const struct passwd *pw, struct option_flags *flags); + static void update_shell (const char *user, char *loginsh, + struct option_flags *flags); + +@@ -79,10 +79,10 @@ static void update_shell (const char *user, char *loginsh, + */ + NORETURN + static void +-fail_exit (int code) ++fail_exit (int code, bool process_selinux) + { + if (pw_locked) { +- if (pw_unlock (true) == 0) { ++ if (pw_unlock (process_selinux) == 0) { + fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, pw_dbname ()); + SYSLOG ((LOG_ERR, "failed to unlock %s", pw_dbname ())); + /* continue */ +@@ -268,13 +268,16 @@ static void process_flags (int argc, char **argv, struct option_flags *flags) + * + * It will not return if the user is not allowed. + */ +-static void check_perms (const struct passwd *pw) ++static void check_perms (const struct passwd *pw, struct option_flags *flags) + { + #ifdef USE_PAM + pam_handle_t *pamh = NULL; + int retval; + struct passwd *pampw; + #endif ++ bool process_selinux; ++ ++ process_selinux = !flags->chroot; + + /* + * Non-privileged users are only allowed to change the shell if the +@@ -285,7 +288,7 @@ static void check_perms (const struct passwd *pw) + fprintf (stderr, + _("You may not change the shell for '%s'.\n"), + pw->pw_name); +- fail_exit (1); ++ fail_exit (1, process_selinux); + } + + /* +@@ -297,7 +300,7 @@ static void check_perms (const struct passwd *pw) + fprintf (stderr, + _("You may not change the shell for '%s'.\n"), + pw->pw_name); +- fail_exit (1); ++ fail_exit (1, process_selinux); + } + #ifdef WITH_SELINUX + /* +@@ -310,7 +313,7 @@ static void check_perms (const struct passwd *pw) + fprintf (stderr, + _("You may not change the shell for '%s'.\n"), + pw->pw_name); +- fail_exit (1); ++ fail_exit (1, process_selinux); + } + #endif + +@@ -381,7 +384,7 @@ static void update_shell (const char *user, char *newshell, struct option_flags + if (setuid (0) != 0) { + SYSLOG ((LOG_ERR, "can't setuid(0)")); + fputs (_("Cannot change ID to root.\n"), stderr); +- fail_exit (1); ++ fail_exit (1, process_selinux); + } + pwd_init (); + +@@ -392,13 +395,13 @@ static void update_shell (const char *user, char *newshell, struct option_flags + if (pw_lock () == 0) { + fprintf (stderr, _("%s: cannot lock %s; try again later.\n"), + Prog, pw_dbname ()); +- fail_exit (1); ++ fail_exit (1, process_selinux); + } + pw_locked = true; + if (pw_open (O_CREAT | O_RDWR) == 0) { + fprintf (stderr, _("%s: cannot open %s\n"), Prog, pw_dbname ()); + SYSLOG ((LOG_WARN, "cannot open %s", pw_dbname ())); +- fail_exit (1); ++ fail_exit (1, process_selinux); + } + + /* +@@ -412,7 +415,7 @@ static void update_shell (const char *user, char *newshell, struct option_flags + fprintf (stderr, + _("%s: user '%s' does not exist in %s\n"), + Prog, user, pw_dbname ()); +- fail_exit (1); ++ fail_exit (1, process_selinux); + } + + /* +@@ -430,7 +433,7 @@ static void update_shell (const char *user, char *newshell, struct option_flags + fprintf (stderr, + _("%s: failed to prepare the new %s entry '%s'\n"), + Prog, pw_dbname (), pwent.pw_name); +- fail_exit (1); ++ fail_exit (1, process_selinux); + } + + /* +@@ -439,7 +442,7 @@ static void update_shell (const char *user, char *newshell, struct option_flags + if (pw_close (process_selinux) == 0) { + fprintf (stderr, _("%s: failure while writing changes to %s\n"), Prog, pw_dbname ()); + SYSLOG ((LOG_ERR, "failure while writing changes to %s", pw_dbname ())); +- fail_exit (1); ++ fail_exit (1, process_selinux); + } + if (pw_unlock (process_selinux) == 0) { + fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, pw_dbname ()); +@@ -460,6 +463,7 @@ int main (int argc, char **argv) + char *user; /* User name */ + const struct passwd *pw; /* Password entry from /etc/passwd */ + struct option_flags flags; ++ bool process_selinux; + + sanitize_env (); + check_fds (); +@@ -482,6 +486,7 @@ int main (int argc, char **argv) + + /* parse the command line options */ + process_flags (argc, argv, &flags); ++ process_selinux = !flags.chroot; + + /* + * Get the name of the user to check. It is either the command line +@@ -490,14 +495,14 @@ int main (int argc, char **argv) + if (optind < argc) { + if (!is_valid_user_name (argv[optind])) { + fprintf (stderr, _("%s: Provided user name is not a valid name\n"), Prog); +- fail_exit (1); ++ fail_exit (1, process_selinux); + } + user = argv[optind]; + pw = xgetpwnam (user); + if (NULL == pw) { + fprintf (stderr, + _("%s: user '%s' does not exist\n"), Prog, user); +- fail_exit (1); ++ fail_exit (1, process_selinux); + } + } else { + pw = get_my_pwent (); +@@ -507,12 +512,12 @@ int main (int argc, char **argv) + Prog); + SYSLOG ((LOG_WARN, "Cannot determine the user name of the caller (UID %lu)", + (unsigned long) getuid ())); +- fail_exit (1); ++ fail_exit (1, process_selinux); + } + user = xstrdup (pw->pw_name); + } + +- check_perms (pw); ++ check_perms (pw, &flags); + + /* + * Now get the login shell. Either get it from the password +@@ -539,7 +544,7 @@ int main (int argc, char **argv) + */ + if (valid_field (loginsh, ":,=\n") != 0) { + fprintf (stderr, _("%s: Invalid entry: %s\n"), Prog, loginsh); +- fail_exit (1); ++ fail_exit (1, process_selinux); + } + if (!streq(loginsh, "") + && (loginsh[0] != '/' +@@ -550,7 +555,7 @@ int main (int argc, char **argv) + fprintf (stderr, _("%s: Warning: %s is an invalid shell\n"), Prog, loginsh); + } else { + fprintf (stderr, _("%s: %s is an invalid shell\n"), Prog, loginsh); +- fail_exit (1); ++ fail_exit (1, process_selinux); + } + } + +-- +2.50.0 + + +From 58600326353a2198df7f8753e2ad8ffa216b68cc Mon Sep 17 00:00:00 2001 +From: Iker Pedrosa +Date: Thu, 3 Jul 2025 12:35:44 +0200 +Subject: [PATCH 03/12] 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 +--- + src/passwd.c | 41 ++++++++++++++++++++++++++++------------- + 1 file changed, 28 insertions(+), 13 deletions(-) + +diff --git a/src/passwd.c b/src/passwd.c +index 512dc572..6d6f319e 100644 +--- a/src/passwd.c ++++ b/src/passwd.c +@@ -53,6 +53,12 @@ + #define E_PWDBUSY 5 /* passwd file busy, try again later */ + #define E_BAD_ARG 6 /* invalid argument to option */ + #define E_PAM_ERR 10 /* PAM returned an error */ ++ ++struct option_flags { ++ bool chroot; ++ bool prefix; ++}; ++ + /* + * Global variables + */ +@@ -131,9 +137,9 @@ static void print_status (const struct passwd *); + NORETURN static void fail_exit (int); + NORETURN static void oom (void); + static char *update_crypt_pw (char *); +-static void update_noshadow (void); ++static void update_noshadow (struct option_flags *flags); + +-static void update_shadow (void); ++static void update_shadow (struct option_flags *flags); + + /* + * usage - print command usage and exit +@@ -549,10 +555,13 @@ static char *update_crypt_pw (char *cp) + } + + +-static void update_noshadow (void) ++static void update_noshadow (struct option_flags *flags) + { + const struct passwd *pw; + struct passwd *npw; ++ bool process_selinux; ++ ++ process_selinux = !flags->chroot && !flags->prefix; + + if (pw_lock () == 0) { + (void) fprintf (stderr, +@@ -586,14 +595,14 @@ static void update_noshadow (void) + Prog, pw_dbname (), npw->pw_name); + fail_exit (E_FAILURE); + } +- if (pw_close (true) == 0) { ++ if (pw_close (process_selinux) == 0) { + (void) fprintf (stderr, + _("%s: failure while writing changes to %s\n"), + Prog, pw_dbname ()); + SYSLOG ((LOG_ERR, "failure while writing changes to %s", pw_dbname ())); + fail_exit (E_FAILURE); + } +- if (pw_unlock (true) == 0) { ++ if (pw_unlock (process_selinux) == 0) { + (void) fprintf (stderr, + _("%s: failed to unlock %s\n"), + Prog, pw_dbname ()); +@@ -603,10 +612,13 @@ static void update_noshadow (void) + pw_locked = false; + } + +-static void update_shadow (void) ++static void update_shadow (struct option_flags *flags) + { + const struct spwd *sp; + struct spwd *nsp; ++ bool process_selinux; ++ ++ process_selinux = !flags->chroot && !flags->prefix; + + if (spw_lock () == 0) { + (void) fprintf (stderr, +@@ -625,9 +637,9 @@ static void update_shadow (void) + sp = spw_locate (name); + if (NULL == sp) { + /* Try to update the password in /etc/passwd instead. */ +- (void) spw_close (true); +- update_noshadow (); +- if (spw_unlock (true) == 0) { ++ (void) spw_close (process_selinux); ++ update_noshadow (flags); ++ if (spw_unlock (process_selinux) == 0) { + (void) fprintf (stderr, + _("%s: failed to unlock %s\n"), + Prog, spw_dbname ()); +@@ -681,14 +693,14 @@ static void update_shadow (void) + Prog, spw_dbname (), nsp->sp_namp); + fail_exit (E_FAILURE); + } +- if (spw_close (true) == 0) { ++ if (spw_close (process_selinux) == 0) { + (void) fprintf (stderr, + _("%s: failure while writing changes to %s\n"), + Prog, spw_dbname ()); + SYSLOG ((LOG_ERR, "failure while writing changes to %s", spw_dbname ())); + fail_exit (E_FAILURE); + } +- if (spw_unlock (true) == 0) { ++ if (spw_unlock (process_selinux) == 0) { + (void) fprintf (stderr, + _("%s: failed to unlock %s\n"), + Prog, spw_dbname ()); +@@ -733,6 +745,7 @@ main(int argc, char **argv) + char *cp; /* Miscellaneous character pointing */ + + const struct spwd *sp; /* Shadow file entry for user */ ++ struct option_flags flags; + + sanitize_env (); + check_fds (); +@@ -849,8 +862,10 @@ main(int argc, char **argv) + } + break; + case 'R': /* no-op, handled in process_root_flag () */ ++ flags.chroot = true; + break; + case 'P': /* no-op, handled in process_prefix_flag () */ ++ flags.prefix = true; + break; + case 'S': + Sflg = true; /* ok for users */ +@@ -1107,9 +1122,9 @@ main(int argc, char **argv) + exit (E_NOPERM); + } + if (spw_file_present ()) { +- update_shadow (); ++ update_shadow (&flags); + } else { +- update_noshadow (); ++ update_noshadow (&flags); + } + + nscd_flush_cache ("passwd"); +-- +2.50.0 + + +From d5a9144c809e808600c7dc730970efc04bc032fd Mon Sep 17 00:00:00 2001 +From: Iker Pedrosa +Date: Thu, 3 Jul 2025 12:48:18 +0200 +Subject: [PATCH 04/12] 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 +--- + src/passwd.c | 46 ++++++++++++++++++++++++---------------------- + 1 file changed, 24 insertions(+), 22 deletions(-) + +diff --git a/src/passwd.c b/src/passwd.c +index 6d6f319e..78859c96 100644 +--- a/src/passwd.c ++++ b/src/passwd.c +@@ -134,9 +134,9 @@ static int new_password (const struct passwd *); + static void check_password (const struct passwd *, const struct spwd *); + static /*@observer@*/const char *pw_status (const char *); + static void print_status (const struct passwd *); +-NORETURN static void fail_exit (int); +-NORETURN static void oom (void); +-static char *update_crypt_pw (char *); ++NORETURN static void fail_exit (int, bool); ++NORETURN static void oom (bool process_selinux); ++static char *update_crypt_pw (char *, bool); + static void update_noshadow (struct option_flags *flags); + + static void update_shadow (struct option_flags *flags); +@@ -486,10 +486,10 @@ static void print_status (const struct passwd *pw) + + NORETURN + static void +-fail_exit (int status) ++fail_exit (int status, bool process_selinux) + { + if (pw_locked) { +- if (pw_unlock (true) == 0) { ++ if (pw_unlock (process_selinux) == 0) { + (void) fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, pw_dbname ()); + SYSLOG ((LOG_ERR, "failed to unlock %s", pw_dbname ())); + /* continue */ +@@ -497,7 +497,7 @@ fail_exit (int status) + } + + if (spw_locked) { +- if (spw_unlock (true) == 0) { ++ if (spw_unlock (process_selinux) == 0) { + (void) fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, spw_dbname ()); + SYSLOG ((LOG_ERR, "failed to unlock %s", spw_dbname ())); + /* continue */ +@@ -509,13 +509,13 @@ fail_exit (int status) + + NORETURN + static void +-oom (void) ++oom (bool process_selinux) + { + (void) fprintf (stderr, _("%s: out of memory\n"), Prog); +- fail_exit (E_FAILURE); ++ fail_exit (E_FAILURE, process_selinux); + } + +-static char *update_crypt_pw (char *cp) ++static char *update_crypt_pw (char *cp, bool process_selinux) + { + if (!use_pam) + { +@@ -533,7 +533,7 @@ static char *update_crypt_pw (char *cp) + _("%s: unlocking the password would result in a passwordless account.\n" + "You should set a password with usermod -p to unlock the password of this account.\n"), + Prog); +- fail_exit (E_FAILURE); ++ fail_exit (E_FAILURE, process_selinux); + } else { + cp++; + } +@@ -575,32 +575,32 @@ static void update_noshadow (struct option_flags *flags) + _("%s: cannot open %s\n"), + Prog, pw_dbname ()); + SYSLOG ((LOG_WARN, "cannot open %s", pw_dbname ())); +- fail_exit (E_MISSING); ++ fail_exit (E_MISSING, process_selinux); + } + pw = pw_locate (name); + if (NULL == pw) { + (void) fprintf (stderr, + _("%s: user '%s' does not exist in %s\n"), + Prog, name, pw_dbname ()); +- fail_exit (E_NOPERM); ++ fail_exit (E_NOPERM, process_selinux); + } + npw = __pw_dup (pw); + if (NULL == npw) { +- oom (); ++ oom (process_selinux); + } +- npw->pw_passwd = update_crypt_pw (npw->pw_passwd); ++ npw->pw_passwd = update_crypt_pw (npw->pw_passwd, process_selinux); + if (pw_update (npw) == 0) { + (void) fprintf (stderr, + _("%s: failed to prepare the new %s entry '%s'\n"), + Prog, pw_dbname (), npw->pw_name); +- fail_exit (E_FAILURE); ++ fail_exit (E_FAILURE, process_selinux); + } + if (pw_close (process_selinux) == 0) { + (void) fprintf (stderr, + _("%s: failure while writing changes to %s\n"), + Prog, pw_dbname ()); + SYSLOG ((LOG_ERR, "failure while writing changes to %s", pw_dbname ())); +- fail_exit (E_FAILURE); ++ fail_exit (E_FAILURE, process_selinux); + } + if (pw_unlock (process_selinux) == 0) { + (void) fprintf (stderr, +@@ -632,7 +632,7 @@ static void update_shadow (struct option_flags *flags) + _("%s: cannot open %s\n"), + Prog, spw_dbname ()); + SYSLOG ((LOG_WARN, "cannot open %s", spw_dbname ())); +- fail_exit (E_FAILURE); ++ fail_exit (E_FAILURE, process_selinux); + } + sp = spw_locate (name); + if (NULL == sp) { +@@ -651,9 +651,9 @@ static void update_shadow (struct option_flags *flags) + } + nsp = __spw_dup (sp); + if (NULL == nsp) { +- oom (); ++ oom (process_selinux); + } +- nsp->sp_pwdp = update_crypt_pw (nsp->sp_pwdp); ++ nsp->sp_pwdp = update_crypt_pw (nsp->sp_pwdp, process_selinux); + if (xflg) { + nsp->sp_max = age_max; + } +@@ -691,14 +691,14 @@ static void update_shadow (struct option_flags *flags) + (void) fprintf (stderr, + _("%s: failed to prepare the new %s entry '%s'\n"), + Prog, spw_dbname (), nsp->sp_namp); +- fail_exit (E_FAILURE); ++ fail_exit (E_FAILURE, process_selinux); + } + if (spw_close (process_selinux) == 0) { + (void) fprintf (stderr, + _("%s: failure while writing changes to %s\n"), + Prog, spw_dbname ()); + SYSLOG ((LOG_ERR, "failure while writing changes to %s", spw_dbname ())); +- fail_exit (E_FAILURE); ++ fail_exit (E_FAILURE, process_selinux); + } + if (spw_unlock (process_selinux) == 0) { + (void) fprintf (stderr, +@@ -746,6 +746,7 @@ main(int argc, char **argv) + + const struct spwd *sp; /* Shadow file entry for user */ + struct option_flags flags; ++ bool process_selinux; + + sanitize_env (); + check_fds (); +@@ -912,6 +913,7 @@ main(int argc, char **argv) + } + } + } ++ process_selinux = !flags.chroot && !flags.prefix; + + /* + * Now I have to get the user name. The name will be gotten from the +@@ -931,7 +933,7 @@ main(int argc, char **argv) + if (optind < argc) { + if (!is_valid_user_name (argv[optind])) { + fprintf (stderr, _("%s: Provided user name is not a valid name\n"), Prog); +- fail_exit (E_NOPERM); ++ fail_exit (E_NOPERM, process_selinux); + } + name = argv[optind]; + } else { +-- +2.50.0 + + +From a736c2af5702e98081bace5fde10a264f443d318 Mon Sep 17 00:00:00 2001 +From: Iker Pedrosa +Date: Thu, 3 Jul 2025 12:55:35 +0200 +Subject: [PATCH 05/12] 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 +--- + src/pwck.c | 46 ++++++++++++++++++++++++++++++---------------- + 1 file changed, 30 insertions(+), 16 deletions(-) + +diff --git a/src/pwck.c b/src/pwck.c +index 9c4cc29a..d0f2f1c9 100644 +--- a/src/pwck.c ++++ b/src/pwck.c +@@ -48,6 +48,10 @@ + #define E_CANTUPDATE 5 + #define E_CANTSORT 6 + ++struct option_flags { ++ bool chroot; ++}; ++ + /* + * Global variables + */ +@@ -71,10 +75,11 @@ static bool quiet = false; /* don't report warnings, only errors */ + /* local function prototypes */ + static void fail_exit (int code); + NORETURN static void usage (int status); +-static void process_flags (int argc, char **argv); ++static void process_flags (int argc, char **argv, struct option_flags *flags); + static void open_files (void); +-static void close_files (bool changed); +-static void check_pw_file (bool *errors, bool *changed); ++static void close_files (bool changed, struct option_flags *flags); ++static void check_pw_file (bool *errors, bool *changed, ++ struct option_flags *flags); + static void check_spw_file (bool *errors, bool *changed); + + extern int allow_bad_names; +@@ -155,7 +160,7 @@ usage (int status) + * + * It will not return if an error is encountered. + */ +-static void process_flags (int argc, char **argv) ++static void process_flags (int argc, char **argv, struct option_flags *flags) + { + int c; + static struct option long_options[] = { +@@ -188,6 +193,7 @@ static void process_flags (int argc, char **argv) + read_only = true; + break; + case 'R': /* no-op, handled in process_root_flag () */ ++ flags->chroot = true; + break; + case 's': + sort_mode = true; +@@ -302,14 +308,18 @@ static void open_files (void) + * changes are committed in the databases. The databases are + * unlocked anyway. + */ +-static void close_files (bool changed) ++static void close_files (bool changed, struct option_flags *flags) + { ++ bool process_selinux; ++ ++ process_selinux = !flags->chroot; ++ + /* + * All done. If there were no change we can just abandon any + * changes to the files. + */ + if (changed) { +- if (pw_close (true) == 0) { ++ if (pw_close (process_selinux) == 0) { + fprintf (stderr, + _("%s: failure while writing changes to %s\n"), + Prog, pw_dbname ()); +@@ -320,7 +330,7 @@ static void close_files (bool changed) + } + fail_exit (E_CANTUPDATE); + } +- if (spw_opened && (spw_close (true) == 0)) { ++ if (spw_opened && (spw_close (process_selinux) == 0)) { + fprintf (stderr, + _("%s: failure while writing changes to %s\n"), + Prog, spw_dbname ()); +@@ -338,7 +348,7 @@ static void close_files (bool changed) + * Don't be anti-social - unlock the files when you're done. + */ + if (spw_locked) { +- if (spw_unlock (true) == 0) { ++ if (spw_unlock (process_selinux) == 0) { + fprintf (stderr, + _("%s: failed to unlock %s\n"), + Prog, spw_dbname ()); +@@ -351,7 +361,7 @@ static void close_files (bool changed) + } + spw_locked = false; + if (pw_locked) { +- if (pw_unlock (true) == 0) { ++ if (pw_unlock (process_selinux) == 0) { + fprintf (stderr, + _("%s: failed to unlock %s\n"), + Prog, pw_dbname ()); +@@ -368,13 +378,16 @@ static void close_files (bool changed) + /* + * check_pw_file - check the content of the passwd file + */ +-static void check_pw_file (bool *errors, bool *changed) ++static void check_pw_file (bool *errors, bool *changed, struct option_flags *flags) + { + struct commonio_entry *pfe, *tpfe; + struct passwd *pwd; + const struct spwd *spw; + uid_t min_sys_id = getdef_ulong ("SYS_UID_MIN", 101UL); + uid_t max_sys_id = getdef_ulong ("SYS_UID_MAX", 999UL); ++ bool process_selinux; ++ ++ process_selinux = !flags->chroot; + + /* + * Loop through the entire password file. +@@ -581,7 +594,7 @@ static void check_pw_file (bool *errors, bool *changed) + _("%s: cannot open %s\n"), + Prog, spw_dbname ()); + *errors = true; +- if (spw_unlock (true) == 0) { ++ if (spw_unlock (process_selinux) == 0) { + fprintf (stderr, + _("%s: failed to unlock %s\n"), + Prog, spw_dbname ()); +@@ -657,7 +670,7 @@ static void check_pw_file (bool *errors, bool *changed) + } + #ifdef WITH_TCB + if (getdef_bool ("USE_TCB") && spw_locked) { +- if (spw_opened && (spw_close (true) == 0)) { ++ if (spw_opened && (spw_close (process_selinux) == 0)) { + fprintf (stderr, + _("%s: failure while writing changes to %s\n"), + Prog, spw_dbname ()); +@@ -669,7 +682,7 @@ static void check_pw_file (bool *errors, bool *changed) + } else { + spw_opened = false; + } +- if (spw_unlock (true) == 0) { ++ if (spw_unlock (process_selinux) == 0) { + fprintf (stderr, + _("%s: failed to unlock %s\n"), + Prog, spw_dbname ()); +@@ -840,6 +853,7 @@ int main (int argc, char **argv) + { + bool errors = false; + bool changed = false; ++ struct option_flags flags; + + log_set_progname(Prog); + log_set_logfd(stderr); +@@ -853,7 +867,7 @@ int main (int argc, char **argv) + OPENLOG (Prog); + + /* Parse the command line arguments */ +- process_flags (argc, argv); ++ process_flags (argc, argv, &flags); + + open_files (); + +@@ -874,14 +888,14 @@ int main (int argc, char **argv) + } + changed = true; + } else { +- check_pw_file (&errors, &changed); ++ check_pw_file (&errors, &changed, &flags); + + if (is_shadow) { + check_spw_file (&errors, &changed); + } + } + +- close_files (changed); ++ close_files (changed, &flags); + + if (!read_only) { + nscd_flush_cache ("passwd"); +-- +2.50.0 + + +From 09957a9748ae6cd28e0826d4cacd939ac288ca1b Mon Sep 17 00:00:00 2001 +From: Iker Pedrosa +Date: Thu, 3 Jul 2025 12:58:58 +0200 +Subject: [PATCH 06/12] 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 +--- + src/pwck.c | 39 ++++++++++++++++++++++----------------- + 1 file changed, 22 insertions(+), 17 deletions(-) + +diff --git a/src/pwck.c b/src/pwck.c +index d0f2f1c9..f1e9c085 100644 +--- a/src/pwck.c ++++ b/src/pwck.c +@@ -73,10 +73,10 @@ static bool sort_mode = false; + static bool quiet = false; /* don't report warnings, only errors */ + + /* local function prototypes */ +-static void fail_exit (int code); ++static void fail_exit (int code, bool process_selinux); + NORETURN static void usage (int status); + static void process_flags (int argc, char **argv, struct option_flags *flags); +-static void open_files (void); ++static void open_files (struct option_flags *flags); + static void close_files (bool changed, struct option_flags *flags); + static void check_pw_file (bool *errors, bool *changed, + struct option_flags *flags); +@@ -87,10 +87,10 @@ extern int allow_bad_names; + /* + * fail_exit - do some cleanup and exit with the given error code + */ +-static void fail_exit (int code) ++static void fail_exit (int code, bool process_selinux) + { + if (spw_locked) { +- if (spw_unlock (true) == 0) { ++ if (spw_unlock (process_selinux) == 0) { + fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, spw_dbname ()); + if (use_system_spw_file) { + SYSLOG ((LOG_ERR, "failed to unlock %s", +@@ -101,7 +101,7 @@ static void fail_exit (int code) + } + + if (pw_locked) { +- if (pw_unlock (true) == 0) { ++ if (pw_unlock (process_selinux) == 0) { + fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, pw_dbname ()); + if (use_system_pw_file) { + SYSLOG ((LOG_ERR, "failed to unlock %s", +@@ -246,12 +246,15 @@ static void process_flags (int argc, char **argv, struct option_flags *flags) + * In read-only mode, the databases are not locked and are opened + * only for reading. + */ +-static void open_files (void) ++static void open_files (struct option_flags *flags) + { + bool use_tcb = false; + #ifdef WITH_TCB + use_tcb = getdef_bool ("USE_TCB"); + #endif /* WITH_TCB */ ++ bool process_selinux; ++ ++ process_selinux = !flags->chroot; + + /* + * Lock the files if we aren't in "read-only" mode +@@ -261,7 +264,7 @@ static void open_files (void) + fprintf (stderr, + _("%s: cannot lock %s; try again later.\n"), + Prog, pw_dbname ()); +- fail_exit (E_CANTLOCK); ++ fail_exit (E_CANTLOCK, process_selinux); + } + pw_locked = true; + if (is_shadow && !use_tcb) { +@@ -269,7 +272,7 @@ static void open_files (void) + fprintf (stderr, + _("%s: cannot lock %s; try again later.\n"), + Prog, spw_dbname ()); +- fail_exit (E_CANTLOCK); ++ fail_exit (E_CANTLOCK, process_selinux); + } + spw_locked = true; + } +@@ -285,7 +288,7 @@ static void open_files (void) + if (use_system_pw_file) { + SYSLOG ((LOG_WARN, "cannot open %s", pw_dbname ())); + } +- fail_exit (E_CANTOPEN); ++ fail_exit (E_CANTOPEN, process_selinux); + } + if (is_shadow && !use_tcb) { + if (spw_open (read_only ? O_RDONLY : O_RDWR) == 0) { +@@ -295,7 +298,7 @@ static void open_files (void) + SYSLOG ((LOG_WARN, "cannot open %s", + spw_dbname ())); + } +- fail_exit (E_CANTOPEN); ++ fail_exit (E_CANTOPEN, process_selinux); + } + spw_opened = true; + } +@@ -328,7 +331,7 @@ static void close_files (bool changed, struct option_flags *flags) + "failure while writing changes to %s", + pw_dbname ())); + } +- fail_exit (E_CANTUPDATE); ++ fail_exit (E_CANTUPDATE, process_selinux); + } + if (spw_opened && (spw_close (process_selinux) == 0)) { + fprintf (stderr, +@@ -339,7 +342,7 @@ static void close_files (bool changed, struct option_flags *flags) + "failure while writing changes to %s", + spw_dbname ())); + } +- fail_exit (E_CANTUPDATE); ++ fail_exit (E_CANTUPDATE, process_selinux); + } + spw_opened = false; + } +@@ -644,7 +647,7 @@ static void check_pw_file (bool *errors, bool *changed, struct option_flags *fla + fprintf (stderr, + _("%s: failed to prepare the new %s entry '%s'\n"), + Prog, spw_dbname (), sp.sp_namp); +- fail_exit (E_CANTUPDATE); ++ fail_exit (E_CANTUPDATE, process_selinux); + } + /* remove password from /etc/passwd */ + pw = *pwd; +@@ -653,7 +656,7 @@ static void check_pw_file (bool *errors, bool *changed, struct option_flags *fla + fprintf (stderr, + _("%s: failed to prepare the new %s entry '%s'\n"), + Prog, pw_dbname (), pw.pw_name); +- fail_exit (E_CANTUPDATE); ++ fail_exit (E_CANTUPDATE, process_selinux); + } + } + } else { +@@ -854,6 +857,7 @@ int main (int argc, char **argv) + bool errors = false; + bool changed = false; + struct option_flags flags; ++ bool process_selinux; + + log_set_progname(Prog); + log_set_logfd(stderr); +@@ -868,22 +872,23 @@ int main (int argc, char **argv) + + /* Parse the command line arguments */ + process_flags (argc, argv, &flags); ++ process_selinux = !flags.chroot; + +- open_files (); ++ open_files (&flags); + + if (sort_mode) { + if (pw_sort () != 0) { + fprintf (stderr, + _("%s: cannot sort entries in %s\n"), + Prog, pw_dbname ()); +- fail_exit (E_CANTSORT); ++ fail_exit (E_CANTSORT, process_selinux); + } + if (is_shadow) { + if (spw_sort () != 0) { + fprintf (stderr, + _("%s: cannot sort entries in %s\n"), + Prog, spw_dbname ()); +- fail_exit (E_CANTSORT); ++ fail_exit (E_CANTSORT, process_selinux); + } + } + changed = true; +-- +2.50.0 + + +From 0c39283e9298fc130c55516fd0a4a0c6b6003bc9 Mon Sep 17 00:00:00 2001 +From: Iker Pedrosa +Date: Thu, 3 Jul 2025 13:04:26 +0200 +Subject: [PATCH 07/12] 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 +--- + src/pwconv.c | 23 ++++++++++++++++------- + 1 file changed, 16 insertions(+), 7 deletions(-) + +diff --git a/src/pwconv.c b/src/pwconv.c +index b0d668b5..6c81cc6e 100644 +--- a/src/pwconv.c ++++ b/src/pwconv.c +@@ -67,6 +67,11 @@ + #define E_MISSING 4 /* unexpected failure, passwd file missing */ + #define E_PWDBUSY 5 /* passwd file(s) busy */ + #define E_BADENTRY 6 /* bad shadow entry */ ++ ++struct option_flags { ++ bool chroot; ++}; ++ + /* + * Global variables + */ +@@ -78,7 +83,7 @@ static bool pw_locked = false; + /* local function prototypes */ + static void fail_exit (int status); + static void usage (int status); +-static void process_flags (int argc, char **argv); ++static void process_flags (int argc, char **argv, struct option_flags *flags); + + static void fail_exit (int status) + { +@@ -120,7 +125,7 @@ static void usage (int status) + * + * It will not return if an error is encountered. + */ +-static void process_flags (int argc, char **argv) ++static void process_flags (int argc, char **argv, struct option_flags *flags) + { + /* + * Parse the command line options. +@@ -139,6 +144,7 @@ static void process_flags (int argc, char **argv) + usage (E_SUCCESS); + /*@notreached@*/break; + case 'R': /* no-op, handled in process_root_flag () */ ++ flags->chroot = true; + break; + default: + usage (E_USAGE); +@@ -156,6 +162,8 @@ int main (int argc, char **argv) + struct passwd pwent; + const struct spwd *sp; + struct spwd spent; ++ struct option_flags flags; ++ bool process_selinux; + + log_set_progname(Prog); + log_set_logfd(stderr); +@@ -168,7 +176,8 @@ int main (int argc, char **argv) + + OPENLOG (Prog); + +- process_flags (argc, argv); ++ process_flags (argc, argv, &flags); ++ process_selinux = !flags.chroot; + + #ifdef WITH_TCB + if (getdef_bool("USE_TCB")) { +@@ -274,14 +283,14 @@ int main (int argc, char **argv) + } + } + +- if (spw_close (true) == 0) { ++ if (spw_close (process_selinux) == 0) { + fprintf (stderr, + _("%s: failure while writing changes to %s\n"), + Prog, spw_dbname ()); + SYSLOG ((LOG_ERR, "failure while writing changes to %s", spw_dbname ())); + fail_exit (E_FAILURE); + } +- if (pw_close (true) == 0) { ++ if (pw_close (process_selinux) == 0) { + fprintf (stderr, + _("%s: failure while writing changes to %s\n"), + Prog, pw_dbname ()); +@@ -299,13 +308,13 @@ int main (int argc, char **argv) + /* continue */ + } + +- if (pw_unlock (true) == 0) { ++ if (pw_unlock (process_selinux) == 0) { + fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, pw_dbname ()); + SYSLOG ((LOG_ERR, "failed to unlock %s", pw_dbname ())); + /* continue */ + } + +- if (spw_unlock (true) == 0) { ++ if (spw_unlock (process_selinux) == 0) { + fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, spw_dbname ()); + SYSLOG ((LOG_ERR, "failed to unlock %s", spw_dbname ())); + /* continue */ +-- +2.50.0 + + +From 997a523d41bb513f37ca88cab35cba9281f036b6 Mon Sep 17 00:00:00 2001 +From: Iker Pedrosa +Date: Thu, 3 Jul 2025 13:05:52 +0200 +Subject: [PATCH 08/12] 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 +--- + src/pwconv.c | 26 +++++++++++++------------- + 1 file changed, 13 insertions(+), 13 deletions(-) + +diff --git a/src/pwconv.c b/src/pwconv.c +index 6c81cc6e..1dc1fd86 100644 +--- a/src/pwconv.c ++++ b/src/pwconv.c +@@ -81,14 +81,14 @@ static bool spw_locked = false; + static bool pw_locked = false; + + /* local function prototypes */ +-static void fail_exit (int status); ++static void fail_exit (int status, bool process_selinux); + static void usage (int status); + static void process_flags (int argc, char **argv, struct option_flags *flags); + +-static void fail_exit (int status) ++static void fail_exit (int status, bool process_selinux) + { + if (pw_locked) { +- if (pw_unlock (true) == 0) { ++ if (pw_unlock (process_selinux) == 0) { + fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, pw_dbname ()); + SYSLOG ((LOG_ERR, "failed to unlock %s", pw_dbname ())); + /* continue */ +@@ -96,7 +96,7 @@ static void fail_exit (int status) + } + + if (spw_locked) { +- if (spw_unlock (true) == 0) { ++ if (spw_unlock (process_selinux) == 0) { + fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, spw_dbname ()); + SYSLOG ((LOG_ERR, "failed to unlock %s", spw_dbname ())); + /* continue */ +@@ -190,26 +190,26 @@ int main (int argc, char **argv) + fprintf (stderr, + _("%s: cannot lock %s; try again later.\n"), + Prog, pw_dbname ()); +- fail_exit (E_PWDBUSY); ++ fail_exit (E_PWDBUSY, process_selinux); + } + pw_locked = true; + if (pw_open (O_CREAT | O_RDWR) == 0) { + fprintf (stderr, + _("%s: cannot open %s\n"), Prog, pw_dbname ()); +- fail_exit (E_MISSING); ++ fail_exit (E_MISSING, process_selinux); + } + + if (spw_lock () == 0) { + fprintf (stderr, + _("%s: cannot lock %s; try again later.\n"), + Prog, spw_dbname ()); +- fail_exit (E_PWDBUSY); ++ fail_exit (E_PWDBUSY, process_selinux); + } + spw_locked = true; + if (spw_open (O_CREAT | O_RDWR) == 0) { + fprintf (stderr, + _("%s: cannot open %s\n"), Prog, spw_dbname ()); +- fail_exit (E_FAILURE); ++ fail_exit (E_FAILURE, process_selinux); + } + + /* +@@ -228,7 +228,7 @@ int main (int argc, char **argv) + fprintf (stderr, + _("%s: cannot remove entry '%s' from %s\n"), + Prog, sp->sp_namp, spw_dbname ()); +- fail_exit (E_FAILURE); ++ fail_exit (E_FAILURE, process_selinux); + } + (void) spw_rewind(); + } +@@ -269,7 +269,7 @@ int main (int argc, char **argv) + fprintf (stderr, + _("%s: failed to prepare the new %s entry '%s'\n"), + Prog, spw_dbname (), spent.sp_namp); +- fail_exit (E_FAILURE); ++ fail_exit (E_FAILURE, process_selinux); + } + + /* remove password from /etc/passwd */ +@@ -279,7 +279,7 @@ int main (int argc, char **argv) + fprintf (stderr, + _("%s: failed to prepare the new %s entry '%s'\n"), + Prog, pw_dbname (), pwent.pw_name); +- fail_exit (E_FAILURE); ++ fail_exit (E_FAILURE, process_selinux); + } + } + +@@ -288,14 +288,14 @@ int main (int argc, char **argv) + _("%s: failure while writing changes to %s\n"), + Prog, spw_dbname ()); + SYSLOG ((LOG_ERR, "failure while writing changes to %s", spw_dbname ())); +- fail_exit (E_FAILURE); ++ fail_exit (E_FAILURE, process_selinux); + } + if (pw_close (process_selinux) == 0) { + fprintf (stderr, + _("%s: failure while writing changes to %s\n"), + Prog, pw_dbname ()); + SYSLOG ((LOG_ERR, "failure while writing changes to %s", pw_dbname ())); +- fail_exit (E_FAILURE); ++ fail_exit (E_FAILURE, process_selinux); + } + + /* /etc/passwd- (backup file) */ +-- +2.50.0 + + +From 13e902a1b368049cb4c6853a7470f66efbac6e72 Mon Sep 17 00:00:00 2001 +From: Iker Pedrosa +Date: Thu, 3 Jul 2025 13:08:10 +0200 +Subject: [PATCH 09/12] 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 +--- + src/pwunconv.c | 21 ++++++++++++++------- + 1 file changed, 14 insertions(+), 7 deletions(-) + +diff --git a/src/pwunconv.c b/src/pwunconv.c +index 7a6d2be5..d46bde6d 100644 +--- a/src/pwunconv.c ++++ b/src/pwunconv.c +@@ -30,6 +30,9 @@ + #include "sssd.h" + #include "string/strcmp/streq.h" + ++struct option_flags { ++ bool chroot; ++}; + + /* + * Global variables +@@ -42,7 +45,7 @@ static bool pw_locked = false; + /* local function prototypes */ + static void fail_exit (int status); + static void usage (int status); +-static void process_flags (int argc, char **argv); ++static void process_flags (int argc, char **argv, struct option_flags *flags); + + static void fail_exit (int status) + { +@@ -82,7 +85,7 @@ static void usage (int status) + * + * It will not return if an error is encountered. + */ +-static void process_flags (int argc, char **argv) ++static void process_flags (int argc, char **argv, struct option_flags *flags) + { + /* + * Parse the command line options. +@@ -101,6 +104,7 @@ static void process_flags (int argc, char **argv) + usage (E_SUCCESS); + /*@notreached@*/break; + case 'R': /* no-op, handled in process_root_flag () */ ++ flags->chroot = true; + break; + default: + usage (E_USAGE); +@@ -117,6 +121,8 @@ int main (int argc, char **argv) + const struct passwd *pw; + struct passwd pwent; + const struct spwd *spwd; ++ struct option_flags flags; ++ bool process_selinux; + + log_set_progname(Prog); + log_set_logfd(stderr); +@@ -129,7 +135,8 @@ int main (int argc, char **argv) + + OPENLOG (Prog); + +- process_flags (argc, argv); ++ process_flags (argc, argv, &flags); ++ process_selinux = !flags.chroot; + + #ifdef WITH_TCB + if (getdef_bool("USE_TCB")) { +@@ -205,9 +212,9 @@ int main (int argc, char **argv) + } + } + +- (void) spw_close (true); /* was only open O_RDONLY */ ++ (void) spw_close (process_selinux); /* was only open O_RDONLY */ + +- if (pw_close (true) == 0) { ++ if (pw_close (process_selinux) == 0) { + fprintf (stderr, + _("%s: failure while writing changes to %s\n"), + Prog, pw_dbname ()); +@@ -222,12 +229,12 @@ int main (int argc, char **argv) + fail_exit (3); + } + +- if (spw_unlock (true) == 0) { ++ if (spw_unlock (process_selinux) == 0) { + fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, spw_dbname ()); + SYSLOG ((LOG_ERR, "failed to unlock %s", spw_dbname ())); + /* continue */ + } +- if (pw_unlock (true) == 0) { ++ if (pw_unlock (process_selinux) == 0) { + fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, pw_dbname ()); + SYSLOG ((LOG_ERR, "failed to unlock %s", pw_dbname ())); + /* continue */ +-- +2.50.0 + + +From 849e748fe08129684da13acba13a247f0aa98eff Mon Sep 17 00:00:00 2001 +From: Iker Pedrosa +Date: Thu, 3 Jul 2025 13:09:12 +0200 +Subject: [PATCH 10/12] 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 +--- + src/pwunconv.c | 22 +++++++++++----------- + 1 file changed, 11 insertions(+), 11 deletions(-) + +diff --git a/src/pwunconv.c b/src/pwunconv.c +index d46bde6d..d3e10f68 100644 +--- a/src/pwunconv.c ++++ b/src/pwunconv.c +@@ -43,21 +43,21 @@ static bool spw_locked = false; + static bool pw_locked = false; + + /* local function prototypes */ +-static void fail_exit (int status); ++static void fail_exit (int status, bool process_selinux); + static void usage (int status); + static void process_flags (int argc, char **argv, struct option_flags *flags); + +-static void fail_exit (int status) ++static void fail_exit (int status, bool process_selinux) + { + if (spw_locked) { +- if (spw_unlock (true) == 0) { ++ if (spw_unlock (process_selinux) == 0) { + fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, spw_dbname ()); + SYSLOG ((LOG_ERR, "failed to unlock %s", spw_dbname ())); + /* continue */ + } + } + if (pw_locked) { +- if (pw_unlock (true) == 0) { ++ if (pw_unlock (process_selinux) == 0) { + fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, pw_dbname ()); + SYSLOG ((LOG_ERR, "failed to unlock %s", pw_dbname ())); + /* continue */ +@@ -154,28 +154,28 @@ int main (int argc, char **argv) + fprintf (stderr, + _("%s: cannot lock %s; try again later.\n"), + Prog, pw_dbname ()); +- fail_exit (5); ++ fail_exit (5, process_selinux); + } + pw_locked = true; + if (pw_open (O_CREAT | O_RDWR) == 0) { + fprintf (stderr, + _("%s: cannot open %s\n"), + Prog, pw_dbname ()); +- fail_exit (1); ++ fail_exit (1, process_selinux); + } + + if (spw_lock () == 0) { + fprintf (stderr, + _("%s: cannot lock %s; try again later.\n"), + Prog, spw_dbname ()); +- fail_exit (5); ++ fail_exit (5, process_selinux); + } + spw_locked = true; + if (spw_open (O_RDONLY) == 0) { + fprintf (stderr, + _("%s: cannot open %s\n"), + Prog, spw_dbname ()); +- fail_exit (1); ++ fail_exit (1, process_selinux); + } + + (void) pw_rewind (); +@@ -208,7 +208,7 @@ int main (int argc, char **argv) + fprintf (stderr, + _("%s: failed to prepare the new %s entry '%s'\n"), + Prog, pw_dbname (), pwent.pw_name); +- fail_exit (3); ++ fail_exit (3, process_selinux); + } + } + +@@ -219,14 +219,14 @@ int main (int argc, char **argv) + _("%s: failure while writing changes to %s\n"), + Prog, pw_dbname ()); + SYSLOG ((LOG_ERR, "failure while writing changes to %s", pw_dbname ())); +- fail_exit (3); ++ fail_exit (3, process_selinux); + } + + if (unlink (SHADOW) != 0) { + fprintf (stderr, + _("%s: cannot delete %s\n"), Prog, SHADOW); + SYSLOG ((LOG_ERR, "cannot delete %s", SHADOW)); +- fail_exit (3); ++ fail_exit (3, process_selinux); + } + + if (spw_unlock (process_selinux) == 0) { +-- +2.50.0 + + +From b1bcf1cf8245b195cff7586770ad861f89cb7c92 Mon Sep 17 00:00:00 2001 +From: Iker Pedrosa +Date: Thu, 3 Jul 2025 14:29:55 +0200 +Subject: [PATCH 11/12] 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 +--- + src/gpasswd.c | 44 ++++++++++++++++++++++++++++---------------- + 1 file changed, 28 insertions(+), 16 deletions(-) + +diff --git a/src/gpasswd.c b/src/gpasswd.c +index a49b9140..94b2bb32 100644 +--- a/src/gpasswd.c ++++ b/src/gpasswd.c +@@ -40,6 +40,9 @@ + #include "string/strcpy/strtcpy.h" + #include "string/strdup/xstrdup.h" + ++struct option_flags { ++ bool chroot; ++}; + + /* + * Global variables +@@ -87,17 +90,17 @@ NORETURN static void failure(void); + static void usage (int status); + static void catch_signals (int killed); + static bool is_valid_user_list (const char *users); +-static void process_flags (int argc, char **argv); ++static void process_flags (int argc, char **argv, struct option_flags *flags); + static void check_flags (int argc, int opt_index); + static void open_files (void); +-static void close_files (void); ++static void close_files (struct option_flags *flags); + #ifdef SHADOWGRP +-static void get_group (struct group *gr, struct sgrp *sg); ++static void get_group (struct group *gr, struct sgrp *sg, struct option_flags *flags); + static void check_perms (const struct group *gr, const struct sgrp *sg); + static void update_group (struct group *gr, struct sgrp *sg); + static void change_passwd (struct group *gr, struct sgrp *sg); + #else +-static void get_group (struct group *gr); ++static void get_group (struct group *gr, struct option_flags *flags); + static void check_perms (const struct group *gr); + static void update_group (struct group *gr); + static void change_passwd (struct group *gr); +@@ -211,7 +214,7 @@ static void failure(void) + /* + * process_flags - process the command line options and arguments + */ +-static void process_flags (int argc, char **argv) ++static void process_flags (int argc, char **argv, struct option_flags *flags) + { + int c; + static struct option long_options[] = { +@@ -272,6 +275,7 @@ static void process_flags (int argc, char **argv) + Mflg = true; + break; + case 'Q': /* no-op, handled in process_root_flag () */ ++ flags->chroot = true; + break; + case 'r': /* remove group password */ + rflg = true; +@@ -591,9 +595,13 @@ static void log_gpasswd_success_group (MAYBE_UNUSED void *arg) + * + * It will call exit in case of error. + */ +-static void close_files (void) ++static void close_files (struct option_flags *flags) + { +- if (gr_close (true) == 0) { ++ bool process_selinux; ++ ++ process_selinux = !flags->chroot; ++ ++ if (gr_close (process_selinux) == 0) { + fprintf (stderr, + _("%s: failure while writing changes to %s\n"), + Prog, gr_dbname ()); +@@ -607,7 +615,7 @@ static void close_files (void) + + #ifdef SHADOWGRP + if (is_shadowgrp) { +- if (sgr_close (true) == 0) { ++ if (sgr_close (process_selinux) == 0) { + fprintf (stderr, + _("%s: failure while writing changes to %s\n"), + Prog, sgr_dbname ()); +@@ -695,15 +703,18 @@ static void update_group (struct group *gr) + * Note: If !is_shadowgrp, *sg will not be initialized. + */ + #ifdef SHADOWGRP +-static void get_group (struct group *gr, struct sgrp *sg) ++static void get_group (struct group *gr, struct sgrp *sg, struct option_flags *flags) + #else +-static void get_group (struct group *gr) ++static void get_group (struct group *gr, struct option_flags *flags) + #endif + { + struct group const*tmpgr = NULL; + #ifdef SHADOWGRP + struct sgrp const*tmpsg = NULL; + #endif ++ bool process_selinux; ++ ++ process_selinux = !flags->chroot; + + if (gr_open (O_RDONLY) == 0) { + fprintf (stderr, _("%s: cannot open %s\n"), Prog, gr_dbname ()); +@@ -724,7 +735,7 @@ static void get_group (struct group *gr) + gr->gr_passwd = xstrdup (tmpgr->gr_passwd); + gr->gr_mem = dup_list (tmpgr->gr_mem); + +- if (gr_close (true) == 0) { ++ if (gr_close (process_selinux) == 0) { + fprintf (stderr, + _("%s: failure while closing read-only %s\n"), + Prog, gr_dbname ()); +@@ -762,7 +773,7 @@ static void get_group (struct group *gr) + sg->sg_adm[0] = NULL; + + } +- if (sgr_close (true) == 0) { ++ if (sgr_close (process_selinux) == 0) { + fprintf (stderr, + _("%s: failure while closing read-only %s\n"), + Prog, sgr_dbname ()); +@@ -864,6 +875,7 @@ int main (int argc, char **argv) + struct sgrp sgent; + #endif + struct passwd *pw = NULL; ++ struct option_flags flags; + + #ifdef WITH_AUDIT + audit_help_open (); +@@ -924,15 +936,15 @@ int main (int argc, char **argv) + } + + /* Parse the options */ +- process_flags (argc, argv); ++ process_flags (argc, argv, &flags); + + /* + * Replicate the group so it can be modified later on. + */ + #ifdef SHADOWGRP +- get_group (&grent, &sgent); ++ get_group (&grent, &sgent, &flags); + #else +- get_group (&grent); ++ get_group (&grent, &flags); + #endif + + /* +@@ -1095,7 +1107,7 @@ int main (int argc, char **argv) + update_group (&grent); + #endif + +- close_files (); ++ close_files (&flags); + + nscd_flush_cache ("group"); + sssd_flush_cache (SSSD_DB_GROUP); +-- +2.50.0 + + +From 2c250fae8866a682bac686dcaa198b325d198cf9 Mon Sep 17 00:00:00 2001 +From: Iker Pedrosa +Date: Fri, 4 Jul 2025 10:03:42 +0200 +Subject: [PATCH 12/12] man/: update `--root` flag with no SELinux support + +Signed-off-by: Iker Pedrosa +--- + man/chage.1.xml | 1 + + man/chfn.1.xml | 1 + + man/chgpasswd.8.xml | 1 + + man/chpasswd.8.xml | 1 + + man/chsh.1.xml | 1 + + man/faillog.8.xml | 1 + + man/gpasswd.1.xml | 1 + + man/groupadd.8.xml | 1 + + man/groupdel.8.xml | 1 + + man/groupmems.8.xml | 1 + + man/groupmod.8.xml | 1 + + man/grpck.8.xml | 1 + + man/lastlog.8.xml | 1 + + man/newusers.8.xml | 1 + + man/passwd.1.xml | 1 + + man/pwck.8.xml | 1 + + man/pwconv.8.xml | 1 + + man/useradd.8.xml | 1 + + man/userdel.8.xml | 1 + + man/usermod.8.xml | 1 + + man/vipw.8.xml | 1 + + 21 files changed, 21 insertions(+) + +diff --git a/man/chage.1.xml b/man/chage.1.xml +index 4ccdf483..a3fff291 100644 +--- a/man/chage.1.xml ++++ b/man/chage.1.xml +@@ -205,6 +205,7 @@ + directory and use the configuration files from the + CHROOT_DIR directory. + Only absolute paths are supported. ++ No SELINUX support. + + + +diff --git a/man/chfn.1.xml b/man/chfn.1.xml +index 2fa49feb..d199ef03 100644 +--- a/man/chfn.1.xml ++++ b/man/chfn.1.xml +@@ -138,6 +138,7 @@ + Apply changes in the CHROOT_DIR + directory and use the configuration files from the + CHROOT_DIR directory. ++ No SELINUX support. + + + +diff --git a/man/chgpasswd.8.xml b/man/chgpasswd.8.xml +index f4a83c24..184c1696 100644 +--- a/man/chgpasswd.8.xml ++++ b/man/chgpasswd.8.xml +@@ -134,6 +134,7 @@ + directory and use the configuration files from the + CHROOT_DIR directory. + Only absolute paths are supported. ++ No SELINUX support. + + + +diff --git a/man/chpasswd.8.xml b/man/chpasswd.8.xml +index cffd9df1..440c8342 100644 +--- a/man/chpasswd.8.xml ++++ b/man/chpasswd.8.xml +@@ -176,6 +176,7 @@ + directory and use the configuration files from the + CHROOT_DIR directory. + Only absolute paths are supported. ++ No SELINUX support. + + + +diff --git a/man/chsh.1.xml b/man/chsh.1.xml +index db2d0ceb..8f253731 100644 +--- a/man/chsh.1.xml ++++ b/man/chsh.1.xml +@@ -89,6 +89,7 @@ + directory and use the configuration files from the + CHROOT_DIR directory. + Only absolute paths are supported. ++ No SELINUX support. + + + +diff --git a/man/faillog.8.xml b/man/faillog.8.xml +index effe43a9..7d453fd2 100644 +--- a/man/faillog.8.xml ++++ b/man/faillog.8.xml +@@ -162,6 +162,7 @@ + directory and use the configuration files from the + CHROOT_DIR directory. + Only absolute paths are supported. ++ No SELINUX support. + + + +diff --git a/man/gpasswd.1.xml b/man/gpasswd.1.xml +index 9b89d915..f918442f 100644 +--- a/man/gpasswd.1.xml ++++ b/man/gpasswd.1.xml +@@ -165,6 +165,7 @@ + directory and use the configuration files from the + CHROOT_DIR directory. + Only absolute paths are supported. ++ No SELINUX support. + + + +diff --git a/man/groupadd.8.xml b/man/groupadd.8.xml +index 9abf159a..2dea7ae5 100644 +--- a/man/groupadd.8.xml ++++ b/man/groupadd.8.xml +@@ -206,6 +206,7 @@ + directory and use the configuration files from the + CHROOT_DIR directory. + Only absolute paths are supported. ++ No SELINUX support. + + + +diff --git a/man/groupdel.8.xml b/man/groupdel.8.xml +index 8d41b883..31f29994 100644 +--- a/man/groupdel.8.xml ++++ b/man/groupdel.8.xml +@@ -94,6 +94,7 @@ + directory and use the configuration files from the + CHROOT_DIR directory. + Only absolute paths are supported. ++ No SELINUX support. + + + +diff --git a/man/groupmems.8.xml b/man/groupmems.8.xml +index cbc84622..0def6c99 100644 +--- a/man/groupmems.8.xml ++++ b/man/groupmems.8.xml +@@ -147,6 +147,7 @@ + directory and use the configuration files from the + CHROOT_DIR directory. + Only absolute paths are supported. ++ No SELINUX support. + + + +diff --git a/man/groupmod.8.xml b/man/groupmod.8.xml +index 2d51f271..ca287441 100644 +--- a/man/groupmod.8.xml ++++ b/man/groupmod.8.xml +@@ -169,6 +169,7 @@ + directory and use the configuration files from the + CHROOT_DIR directory. + Only absolute paths are supported. ++ No SELINUX support. + + + +diff --git a/man/grpck.8.xml b/man/grpck.8.xml +index 31aa6ca3..9feb190e 100644 +--- a/man/grpck.8.xml ++++ b/man/grpck.8.xml +@@ -154,6 +154,7 @@ + directory and use the configuration files from the + CHROOT_DIR directory. + Only absolute paths are supported. ++ No SELINUX support. + + + +diff --git a/man/lastlog.8.xml b/man/lastlog.8.xml +index cefe5ac2..b0589203 100644 +--- a/man/lastlog.8.xml ++++ b/man/lastlog.8.xml +@@ -109,6 +109,7 @@ + directory and use the configuration files from the + CHROOT_DIR directory. + Only absolute paths are supported. ++ No SELINUX support. + + + +diff --git a/man/newusers.8.xml b/man/newusers.8.xml +index 6812c750..e0380608 100644 +--- a/man/newusers.8.xml ++++ b/man/newusers.8.xml +@@ -313,6 +313,7 @@ + directory and use the configuration files from the + CHROOT_DIR directory. + Only absolute paths are supported. ++ No SELINUX support. + + + +diff --git a/man/passwd.1.xml b/man/passwd.1.xml +index 65dca040..def35609 100644 +--- a/man/passwd.1.xml ++++ b/man/passwd.1.xml +@@ -279,6 +279,7 @@ + directory and use the configuration files from the + CHROOT_DIR directory. + Only absolute paths are supported. ++ No SELINUX support. + + + +diff --git a/man/pwck.8.xml b/man/pwck.8.xml +index 4eb820d6..998bb44a 100644 +--- a/man/pwck.8.xml ++++ b/man/pwck.8.xml +@@ -202,6 +202,7 @@ + directory and use the configuration files from the + CHROOT_DIR directory. + Only absolute paths are supported. ++ No SELINUX support. + + + +diff --git a/man/pwconv.8.xml b/man/pwconv.8.xml +index c4aedef6..9be3f7c5 100644 +--- a/man/pwconv.8.xml ++++ b/man/pwconv.8.xml +@@ -183,6 +183,7 @@ + directory and use the configuration files from the + CHROOT_DIR directory. + Only absolute paths are supported. ++ No SELINUX support. + + + +diff --git a/man/useradd.8.xml b/man/useradd.8.xml +index fd9ff697..e6db7f6d 100644 +--- a/man/useradd.8.xml ++++ b/man/useradd.8.xml +@@ -515,6 +515,7 @@ + directory and use the configuration files from the + CHROOT_DIR directory. + Only absolute paths are supported. ++ No SELINUX support. + + + +diff --git a/man/userdel.8.xml b/man/userdel.8.xml +index 32851f11..de8eff24 100644 +--- a/man/userdel.8.xml ++++ b/man/userdel.8.xml +@@ -120,6 +120,7 @@ + directory and use the configuration files from the + CHROOT_DIR directory. + Only absolute paths are supported. ++ No SELINUX support. + + + +diff --git a/man/usermod.8.xml b/man/usermod.8.xml +index 61b5e3a9..2c704ab5 100644 +--- a/man/usermod.8.xml ++++ b/man/usermod.8.xml +@@ -337,6 +337,7 @@ + directory and use the configuration files from the + CHROOT_DIR directory. + Only absolute paths are supported. ++ No SELINUX support. + + + +diff --git a/man/vipw.8.xml b/man/vipw.8.xml +index fb80582b..975fba37 100644 +--- a/man/vipw.8.xml ++++ b/man/vipw.8.xml +@@ -120,6 +120,7 @@ + directory and use the configuration files from the + CHROOT_DIR directory. + Only absolute paths are supported. ++ No SELINUX support. + + + +-- +2.50.0 + diff --git a/shadow-utils.spec b/shadow-utils.spec index 34baef4..e406acd 100644 --- a/shadow-utils.spec +++ b/shadow-utils.spec @@ -1,7 +1,7 @@ Summary: Utilities for managing accounts and shadow password files Name: shadow-utils Version: 4.18.0 -Release: 3%{?dist} +Release: 4%{?dist} Epoch: 2 License: BSD-3-Clause AND GPL-2.0-or-later URL: https://github.com/shadow-maint/shadow @@ -24,6 +24,8 @@ Source7: passwd.pamd Patch0: shadow-4.15.0-manfix.patch # Probably non-upstreamable Patch1: shadow-4.18.0-account-tools-setuid.patch +# https://github.com/shadow-maint/shadow/commit/db0e0b9112332a45131912021317ab5ab4fd6e40 +Patch2: shadow-4.18.0-selinux-chroot-prefix.patch ### Dependencies ### Requires: audit-libs >= 1.6.5 @@ -269,6 +271,10 @@ rm -f $RPM_BUILD_ROOT/%{_libdir}/libsubid.a %{_libdir}/libsubid.so %changelog +* Fri Oct 31 2025 Iker Pedrosa - 2:4.18.0-4 +- Stop setting SELinux labels in chroot and prefix environments + Resolves: #2249524 + * Tue Jul 29 2025 Alexey Tikhonov - 2:4.18.0-3 - Revert "Stop assigning subids by default" Resolves: #2382662 From 20b52960271fc47cb5b8bbcd2a20954bc10f402b Mon Sep 17 00:00:00 2001 From: Iker Pedrosa Date: Tue, 25 Nov 2025 10:30:54 +0100 Subject: [PATCH 11/30] Test CI Signed-off-by: Iker Pedrosa --- shadow-utils.spec | 7 ++++++- test.patch | 26 ++++++++++++++++++++++++++ 2 files changed, 32 insertions(+), 1 deletion(-) create mode 100644 test.patch diff --git a/shadow-utils.spec b/shadow-utils.spec index e406acd..cf4fbf3 100644 --- a/shadow-utils.spec +++ b/shadow-utils.spec @@ -1,7 +1,7 @@ Summary: Utilities for managing accounts and shadow password files Name: shadow-utils Version: 4.18.0 -Release: 4%{?dist} +Release: 5%{?dist} Epoch: 2 License: BSD-3-Clause AND GPL-2.0-or-later URL: https://github.com/shadow-maint/shadow @@ -26,6 +26,8 @@ Patch0: shadow-4.15.0-manfix.patch Patch1: shadow-4.18.0-account-tools-setuid.patch # https://github.com/shadow-maint/shadow/commit/db0e0b9112332a45131912021317ab5ab4fd6e40 Patch2: shadow-4.18.0-selinux-chroot-prefix.patch +# Test patch +Patch3: test.patch ### Dependencies ### Requires: audit-libs >= 1.6.5 @@ -271,6 +273,9 @@ rm -f $RPM_BUILD_ROOT/%{_libdir}/libsubid.a %{_libdir}/libsubid.so %changelog +* Tue Nov 25 2025 Iker Pedrosa - 2:4.18.0-5 +- Test CI + * Fri Oct 31 2025 Iker Pedrosa - 2:4.18.0-4 - Stop setting SELinux labels in chroot and prefix environments Resolves: #2249524 diff --git a/test.patch b/test.patch new file mode 100644 index 0000000..346d1b1 --- /dev/null +++ b/test.patch @@ -0,0 +1,26 @@ +From 012684d388881727d6ce457d710a03d6df216409 Mon Sep 17 00:00:00 2001 +From: Iker Pedrosa +Date: Tue, 25 Nov 2025 09:52:03 +0100 +Subject: [PATCH] Test + +Signed-off-by: Iker Pedrosa +--- + src/useradd.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/src/useradd.c b/src/useradd.c +index f7e46d6b..e2e95693 100644 +--- a/src/useradd.c ++++ b/src/useradd.c +@@ -2440,7 +2440,7 @@ int main (int argc, char **argv) + #endif + unsigned long subuid_count = 0; + unsigned long subgid_count = 0; +- struct option_flags flags; ++ struct option_flags flags = {0}; + bool process_selinux; + + log_set_progname(Prog); +-- +2.52.0 + From 71c7bce53c2f1bb06b9eb5c9a23c9fecb17eeb61 Mon Sep 17 00:00:00 2001 From: Adam Williamson Date: Tue, 25 Nov 2025 10:08:41 -0800 Subject: [PATCH 12/30] Revert changes from -5 (they were only meant for testing) --- shadow-utils.spec | 7 ++++--- test.patch | 26 -------------------------- 2 files changed, 4 insertions(+), 29 deletions(-) delete mode 100644 test.patch diff --git a/shadow-utils.spec b/shadow-utils.spec index cf4fbf3..483eb21 100644 --- a/shadow-utils.spec +++ b/shadow-utils.spec @@ -1,7 +1,7 @@ Summary: Utilities for managing accounts and shadow password files Name: shadow-utils Version: 4.18.0 -Release: 5%{?dist} +Release: 6%{?dist} Epoch: 2 License: BSD-3-Clause AND GPL-2.0-or-later URL: https://github.com/shadow-maint/shadow @@ -26,8 +26,6 @@ Patch0: shadow-4.15.0-manfix.patch Patch1: shadow-4.18.0-account-tools-setuid.patch # https://github.com/shadow-maint/shadow/commit/db0e0b9112332a45131912021317ab5ab4fd6e40 Patch2: shadow-4.18.0-selinux-chroot-prefix.patch -# Test patch -Patch3: test.patch ### Dependencies ### Requires: audit-libs >= 1.6.5 @@ -273,6 +271,9 @@ rm -f $RPM_BUILD_ROOT/%{_libdir}/libsubid.a %{_libdir}/libsubid.so %changelog +* Tue Nov 25 2025 Adam Williamson - 2:4.18.0-6 +- Revert changes from -5 (they were only meant for testing) + * Tue Nov 25 2025 Iker Pedrosa - 2:4.18.0-5 - Test CI diff --git a/test.patch b/test.patch deleted file mode 100644 index 346d1b1..0000000 --- a/test.patch +++ /dev/null @@ -1,26 +0,0 @@ -From 012684d388881727d6ce457d710a03d6df216409 Mon Sep 17 00:00:00 2001 -From: Iker Pedrosa -Date: Tue, 25 Nov 2025 09:52:03 +0100 -Subject: [PATCH] Test - -Signed-off-by: Iker Pedrosa ---- - src/useradd.c | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - -diff --git a/src/useradd.c b/src/useradd.c -index f7e46d6b..e2e95693 100644 ---- a/src/useradd.c -+++ b/src/useradd.c -@@ -2440,7 +2440,7 @@ int main (int argc, char **argv) - #endif - unsigned long subuid_count = 0; - unsigned long subgid_count = 0; -- struct option_flags flags; -+ struct option_flags flags = {0}; - bool process_selinux; - - log_set_progname(Prog); --- -2.52.0 - From 8953fddc447d650561e9786be46815400352e4af Mon Sep 17 00:00:00 2001 From: Adam Williamson Date: Tue, 25 Nov 2025 12:50:30 -0800 Subject: [PATCH 13/30] Also revert changes from -4 (last known good was -3) --- shadow-4.18.0-selinux-chroot-prefix.patch | 10433 -------------------- shadow-utils.spec | 5 +- 2 files changed, 3 insertions(+), 10435 deletions(-) delete mode 100644 shadow-4.18.0-selinux-chroot-prefix.patch diff --git a/shadow-4.18.0-selinux-chroot-prefix.patch b/shadow-4.18.0-selinux-chroot-prefix.patch deleted file mode 100644 index a5db17b..0000000 --- a/shadow-4.18.0-selinux-chroot-prefix.patch +++ /dev/null @@ -1,10433 +0,0 @@ -From 218cc94da84459bfa5b8417d933e8898e24ecff6 Mon Sep 17 00:00:00 2001 -From: Iker Pedrosa -Date: Fri, 25 Apr 2025 15:13:12 +0200 -Subject: [PATCH 01/15] 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 ---- - lib/commonio.c | 16 ++++++++++------ - lib/commonio.h | 2 +- - lib/groupio.c | 2 +- - lib/pwio.c | 2 +- - lib/sgroupio.c | 2 +- - lib/shadowio.c | 2 +- - lib/subordinateio.c | 4 ++-- - 7 files changed, 17 insertions(+), 13 deletions(-) - -diff --git a/lib/commonio.c b/lib/commonio.c -index 7304ec00..7ee6df4d 100644 ---- a/lib/commonio.c -+++ b/lib/commonio.c -@@ -473,7 +473,7 @@ int commonio_unlock (struct commonio_db *db) - - if (db->isopen) { - db->readonly = true; -- if (commonio_close (db) == 0) { -+ if (commonio_close (db, true) == 0) { - if (db->locked) { - dec_lock_count (); - } -@@ -885,7 +885,7 @@ static int write_all (const struct commonio_db *db) - } - - --int commonio_close (struct commonio_db *db) -+int commonio_close (struct commonio_db *db, bool process_selinux) - { - bool errors = false; - char buf[1024]; -@@ -927,7 +927,8 @@ int commonio_close (struct commonio_db *db) - } - - #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 -@@ -942,7 +943,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 -@@ -961,7 +963,8 @@ int commonio_close (struct commonio_db *db) - 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 -@@ -999,7 +1002,8 @@ int commonio_close (struct commonio_db *db) - } - - #ifdef WITH_SELINUX -- if (reset_selinux_file_context () != 0) { -+ if (process_selinux -+ && reset_selinux_file_context () != 0) { - goto fail; - } - #endif -diff --git a/lib/commonio.h b/lib/commonio.h -index fedbefa3..dadc0500 100644 ---- a/lib/commonio.h -+++ b/lib/commonio.h -@@ -138,7 +138,7 @@ 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_close (struct commonio_db *, bool); - extern int commonio_unlock (struct commonio_db *); - extern void commonio_del_entry (struct commonio_db *, - const struct commonio_entry *); -diff --git a/lib/groupio.c b/lib/groupio.c -index 72391433..5076fd3d 100644 ---- a/lib/groupio.c -+++ b/lib/groupio.c -@@ -186,7 +186,7 @@ int gr_rewind (void) - - int gr_close (void) - { -- return commonio_close (&group_db); -+ return commonio_close (&group_db, true); - } - - int gr_unlock (void) -diff --git a/lib/pwio.c b/lib/pwio.c -index 415447d6..6f666ec6 100644 ---- a/lib/pwio.c -+++ b/lib/pwio.c -@@ -158,7 +158,7 @@ int pw_rewind (void) - - int pw_close (void) - { -- return commonio_close (&passwd_db); -+ return commonio_close (&passwd_db, true); - } - - int pw_unlock (void) -diff --git a/lib/sgroupio.c b/lib/sgroupio.c -index ef82976d..659dcd06 100644 ---- a/lib/sgroupio.c -+++ b/lib/sgroupio.c -@@ -278,7 +278,7 @@ int sgr_rewind (void) - - int sgr_close (void) - { -- return commonio_close (&gshadow_db); -+ return commonio_close (&gshadow_db, true); - } - - int sgr_unlock (void) -diff --git a/lib/shadowio.c b/lib/shadowio.c -index 8b1c1d2c..55d33da2 100644 ---- a/lib/shadowio.c -+++ b/lib/shadowio.c -@@ -195,7 +195,7 @@ int spw_close (void) - return 0; - } - #endif /* WITH_TCB */ -- retval = commonio_close (&shadow_db); -+ retval = commonio_close (&shadow_db, true); - #ifdef WITH_TCB - if (use_tcb && (shadowtcb_gain_priv () == SHADOWTCB_FAILURE)) { - return 0; -diff --git a/lib/subordinateio.c b/lib/subordinateio.c -index 27f888b9..3638ec61 100644 ---- a/lib/subordinateio.c -+++ b/lib/subordinateio.c -@@ -670,7 +670,7 @@ int sub_uid_remove (const char *owner, uid_t start, unsigned long count) - - int sub_uid_close (void) - { -- return commonio_close (&subordinate_uid_db); -+ return commonio_close (&subordinate_uid_db, true); - } - - int sub_uid_unlock (void) -@@ -777,7 +777,7 @@ int sub_gid_remove (const char *owner, gid_t start, unsigned long count) - - int sub_gid_close (void) - { -- return commonio_close (&subordinate_gid_db); -+ return commonio_close (&subordinate_gid_db, true); - } - - int sub_gid_unlock (void) --- -2.50.0 - - -From 7e7dbe0e65d2795c15c42bb04c7c03b9ff86ea84 Mon Sep 17 00:00:00 2001 -From: Iker Pedrosa -Date: Fri, 25 Apr 2025 15:19:29 +0200 -Subject: [PATCH 02/15] 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 ---- - lib/pwio.c | 4 ++-- - lib/pwio.h | 3 ++- - src/chage.c | 2 +- - src/chfn.c | 2 +- - src/chpasswd.c | 2 +- - src/chsh.c | 2 +- - src/groupmod.c | 2 +- - src/newusers.c | 2 +- - src/passwd.c | 2 +- - src/pwck.c | 2 +- - src/pwconv.c | 2 +- - src/pwunconv.c | 2 +- - src/useradd.c | 2 +- - src/userdel.c | 6 +++--- - src/usermod.c | 2 +- - 15 files changed, 19 insertions(+), 18 deletions(-) - -diff --git a/lib/pwio.c b/lib/pwio.c -index 6f666ec6..c5c1cdb1 100644 ---- a/lib/pwio.c -+++ b/lib/pwio.c -@@ -156,9 +156,9 @@ int pw_rewind (void) - return commonio_next (&passwd_db); - } - --int pw_close (void) -+int pw_close (bool process_selinux) - { -- return commonio_close (&passwd_db, true); -+ return commonio_close (&passwd_db, process_selinux); - } - - int pw_unlock (void) -diff --git a/lib/pwio.h b/lib/pwio.h -index 882a7c7a..b051a7e7 100644 ---- a/lib/pwio.h -+++ b/lib/pwio.h -@@ -14,8 +14,9 @@ - - #include - #include -+#include - --extern int pw_close (void); -+extern int pw_close (bool process_selinux); - extern /*@observer@*/ /*@null@*/const struct passwd *pw_locate (const char *name); - extern /*@observer@*/ /*@null@*/const struct passwd *pw_locate_uid (uid_t uid); - extern int pw_lock (void); -diff --git a/src/chage.c b/src/chage.c -index 4b9c96d6..1ebe34c5 100644 ---- a/src/chage.c -+++ b/src/chage.c -@@ -557,7 +557,7 @@ static void close_files (void) - * Close the password file. If any entries were modified, the file - * will be re-written. - */ -- if (pw_close () == 0) { -+ if (pw_close (true) == 0) { - fprintf (stderr, _("%s: failure while writing changes to %s\n"), Prog, pw_dbname ()); - SYSLOG ((LOG_ERR, "failure while writing changes to %s", pw_dbname ())); - fail_exit (E_NOPERM); -diff --git a/src/chfn.c b/src/chfn.c -index 58502929..abf88cc8 100644 ---- a/src/chfn.c -+++ b/src/chfn.c -@@ -493,7 +493,7 @@ static void update_gecos (const char *user, char *gecos) - /* - * Changes have all been made, so commit them and unlock the file. - */ -- if (pw_close () == 0) { -+ if (pw_close (true) == 0) { - fprintf (stderr, _("%s: failure while writing changes to %s\n"), Prog, pw_dbname ()); - SYSLOG ((LOG_ERR, "failure while writing changes to %s", pw_dbname ())); - fail_exit (E_NOPERM); -diff --git a/src/chpasswd.c b/src/chpasswd.c -index 315e91ce..17f3d6d5 100644 ---- a/src/chpasswd.c -+++ b/src/chpasswd.c -@@ -393,7 +393,7 @@ static void close_files (void) - spw_locked = false; - } - -- if (pw_close () == 0) { -+ if (pw_close (true) == 0) { - fprintf (stderr, - _("%s: failure while writing changes to %s\n"), - Prog, pw_dbname ()); -diff --git a/src/chsh.c b/src/chsh.c -index 236ff784..b5ae28da 100644 ---- a/src/chsh.c -+++ b/src/chsh.c -@@ -428,7 +428,7 @@ static void update_shell (const char *user, char *newshell) - /* - * Changes have all been made, so commit them and unlock the file. - */ -- if (pw_close () == 0) { -+ if (pw_close (true) == 0) { - fprintf (stderr, _("%s: failure while writing changes to %s\n"), Prog, pw_dbname ()); - SYSLOG ((LOG_ERR, "failure while writing changes to %s", pw_dbname ())); - fail_exit (1); -diff --git a/src/groupmod.c b/src/groupmod.c -index cce6fd49..14b7e746 100644 ---- a/src/groupmod.c -+++ b/src/groupmod.c -@@ -534,7 +534,7 @@ static void close_files (void) - #endif /* SHADOWGRP */ - - if (gflg) { -- if (pw_close () == 0) { -+ if (pw_close (true) == 0) { - fprintf (stderr, - _("%s: failure while writing changes to %s\n"), - Prog, pw_dbname ()); -diff --git a/src/newusers.c b/src/newusers.c -index 38cbdddb..a738f944 100644 ---- a/src/newusers.c -+++ b/src/newusers.c -@@ -939,7 +939,7 @@ static void open_files (void) - */ - static void close_files (void) - { -- if (pw_close () == 0) { -+ if (pw_close (true) == 0) { - fprintf (stderr, _("%s: failure while writing changes to %s\n"), Prog, pw_dbname ()); - SYSLOG ((LOG_ERR, "failure while writing changes to %s", pw_dbname ())); - fail_exit (EXIT_FAILURE); -diff --git a/src/passwd.c b/src/passwd.c -index cdd817a2..8bdff6dd 100644 ---- a/src/passwd.c -+++ b/src/passwd.c -@@ -586,7 +586,7 @@ static void update_noshadow (void) - Prog, pw_dbname (), npw->pw_name); - fail_exit (E_FAILURE); - } -- if (pw_close () == 0) { -+ if (pw_close (true) == 0) { - (void) fprintf (stderr, - _("%s: failure while writing changes to %s\n"), - Prog, pw_dbname ()); -diff --git a/src/pwck.c b/src/pwck.c -index 6272839f..b1c6970f 100644 ---- a/src/pwck.c -+++ b/src/pwck.c -@@ -309,7 +309,7 @@ static void close_files (bool changed) - * changes to the files. - */ - if (changed) { -- if (pw_close () == 0) { -+ if (pw_close (true) == 0) { - fprintf (stderr, - _("%s: failure while writing changes to %s\n"), - Prog, pw_dbname ()); -diff --git a/src/pwconv.c b/src/pwconv.c -index 1bb92752..da3a5820 100644 ---- a/src/pwconv.c -+++ b/src/pwconv.c -@@ -281,7 +281,7 @@ int main (int argc, char **argv) - SYSLOG ((LOG_ERR, "failure while writing changes to %s", spw_dbname ())); - fail_exit (E_FAILURE); - } -- if (pw_close () == 0) { -+ if (pw_close (true) == 0) { - fprintf (stderr, - _("%s: failure while writing changes to %s\n"), - Prog, pw_dbname ()); -diff --git a/src/pwunconv.c b/src/pwunconv.c -index 2e4d3d75..8423cda4 100644 ---- a/src/pwunconv.c -+++ b/src/pwunconv.c -@@ -207,7 +207,7 @@ int main (int argc, char **argv) - - (void) spw_close (); /* was only open O_RDONLY */ - -- if (pw_close () == 0) { -+ if (pw_close (true) == 0) { - fprintf (stderr, - _("%s: failure while writing changes to %s\n"), - Prog, pw_dbname ()); -diff --git a/src/useradd.c b/src/useradd.c -index 36b9b8f8..8c9e9003 100644 ---- a/src/useradd.c -+++ b/src/useradd.c -@@ -1564,7 +1564,7 @@ static void process_flags (int argc, char **argv) - */ - static void close_files (void) - { -- if (pw_close () == 0) { -+ if (pw_close (true) == 0) { - fprintf (stderr, _("%s: failure while writing changes to %s\n"), Prog, pw_dbname ()); - SYSLOG ((LOG_ERR, "failure while writing changes to %s", pw_dbname ())); - fail_exit (E_PW_UPDATE); -diff --git a/src/userdel.c b/src/userdel.c -index 72ce11fa..b5a02b3c 100644 ---- a/src/userdel.c -+++ b/src/userdel.c -@@ -390,7 +390,7 @@ static void remove_usergroup (void) - */ - static void close_files (void) - { -- if (pw_close () == 0) { -+ if (pw_close (true) == 0) { - fprintf (stderr, _("%s: failure while writing changes to %s\n"), Prog, pw_dbname ()); - SYSLOG ((LOG_ERR, "failure while writing changes to %s", pw_dbname ())); - fail_exit (E_PW_UPDATE); -@@ -1046,7 +1046,7 @@ int main (int argc, char **argv) - pw_open(O_RDONLY); - pwd = pw_locate (user_name); /* we care only about local users */ - if (NULL == pwd) { -- pw_close(); -+ pw_close(true); - fprintf (stderr, _("%s: user '%s' does not exist\n"), - Prog, user_name); - #ifdef WITH_AUDIT -@@ -1065,7 +1065,7 @@ int main (int argc, char **argv) - } else { - user_home = xstrdup(pwd->pw_dir); - } -- pw_close(); -+ pw_close(true); - } - #ifdef WITH_TCB - if (shadowtcb_set_user (user_name) == SHADOWTCB_FAILURE) { -diff --git a/src/usermod.c b/src/usermod.c -index a0f4d7dd..180b7da0 100644 ---- a/src/usermod.c -+++ b/src/usermod.c -@@ -1469,7 +1469,7 @@ process_flags(int argc, char **argv) - */ - static void close_files (void) - { -- if (pw_close () == 0) { -+ if (pw_close (true) == 0) { - fprintf (stderr, - _("%s: failure while writing changes to %s\n"), - Prog, pw_dbname ()); --- -2.50.0 - - -From abffb267659bb1f9d49af5cba69c7a9ba2489e3d Mon Sep 17 00:00:00 2001 -From: Iker Pedrosa -Date: Fri, 25 Apr 2025 15:23:11 +0200 -Subject: [PATCH 03/15] 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 ---- - lib/shadowio.c | 4 ++-- - lib/shadowio.h | 2 +- - src/chage.c | 2 +- - src/chpasswd.c | 2 +- - src/newusers.c | 2 +- - src/passwd.c | 4 ++-- - src/pwck.c | 4 ++-- - src/pwconv.c | 2 +- - src/pwunconv.c | 2 +- - src/useradd.c | 2 +- - src/userdel.c | 2 +- - src/usermod.c | 2 +- - 12 files changed, 15 insertions(+), 15 deletions(-) - -diff --git a/lib/shadowio.c b/lib/shadowio.c -index 55d33da2..a109576d 100644 ---- a/lib/shadowio.c -+++ b/lib/shadowio.c -@@ -185,7 +185,7 @@ int spw_rewind (void) - return commonio_next (&shadow_db); - } - --int spw_close (void) -+int spw_close (bool process_selinux) - { - int retval = 0; - #ifdef WITH_TCB -@@ -195,7 +195,7 @@ int spw_close (void) - return 0; - } - #endif /* WITH_TCB */ -- retval = commonio_close (&shadow_db, true); -+ retval = commonio_close (&shadow_db, process_selinux); - #ifdef WITH_TCB - if (use_tcb && (shadowtcb_gain_priv () == SHADOWTCB_FAILURE)) { - return 0; -diff --git a/lib/shadowio.h b/lib/shadowio.h -index 4dbeb6d6..e64d8d60 100644 ---- a/lib/shadowio.h -+++ b/lib/shadowio.h -@@ -13,7 +13,7 @@ - - #include "defines.h" - --extern int spw_close (void); -+extern int spw_close (bool process_selinux); - extern bool spw_file_present (void); - extern /*@observer@*/ /*@null@*/const struct spwd *spw_locate (const char *name); - extern int spw_lock (void); -diff --git a/src/chage.c b/src/chage.c -index 1ebe34c5..a59428c5 100644 ---- a/src/chage.c -+++ b/src/chage.c -@@ -546,7 +546,7 @@ static void close_files (void) - * Now close the shadow password file, which will cause all of the - * entries to be re-written. - */ -- if (spw_close () == 0) { -+ if (spw_close (true) == 0) { - fprintf (stderr, - _("%s: failure while writing changes to %s\n"), Prog, spw_dbname ()); - SYSLOG ((LOG_ERR, "failure while writing changes to %s", spw_dbname ())); -diff --git a/src/chpasswd.c b/src/chpasswd.c -index 17f3d6d5..e3d8dcef 100644 ---- a/src/chpasswd.c -+++ b/src/chpasswd.c -@@ -378,7 +378,7 @@ static void open_files (void) - static void close_files (void) - { - if (is_shadow_pwd) { -- if (spw_close () == 0) { -+ if (spw_close (true) == 0) { - fprintf (stderr, - _("%s: failure while writing changes to %s\n"), - Prog, spw_dbname ()); -diff --git a/src/newusers.c b/src/newusers.c -index a738f944..43672dd2 100644 ---- a/src/newusers.c -+++ b/src/newusers.c -@@ -952,7 +952,7 @@ static void close_files (void) - pw_locked = false; - - if (is_shadow) { -- if (spw_close () == 0) { -+ if (spw_close (true) == 0) { - fprintf (stderr, - _("%s: failure while writing changes to %s\n"), - Prog, spw_dbname ()); -diff --git a/src/passwd.c b/src/passwd.c -index 8bdff6dd..01efb285 100644 ---- a/src/passwd.c -+++ b/src/passwd.c -@@ -625,7 +625,7 @@ static void update_shadow (void) - sp = spw_locate (name); - if (NULL == sp) { - /* Try to update the password in /etc/passwd instead. */ -- (void) spw_close (); -+ (void) spw_close (true); - update_noshadow (); - if (spw_unlock () == 0) { - (void) fprintf (stderr, -@@ -681,7 +681,7 @@ static void update_shadow (void) - Prog, spw_dbname (), nsp->sp_namp); - fail_exit (E_FAILURE); - } -- if (spw_close () == 0) { -+ if (spw_close (true) == 0) { - (void) fprintf (stderr, - _("%s: failure while writing changes to %s\n"), - Prog, spw_dbname ()); -diff --git a/src/pwck.c b/src/pwck.c -index b1c6970f..718b639f 100644 ---- a/src/pwck.c -+++ b/src/pwck.c -@@ -320,7 +320,7 @@ static void close_files (bool changed) - } - fail_exit (E_CANTUPDATE); - } -- if (spw_opened && (spw_close () == 0)) { -+ if (spw_opened && (spw_close (true) == 0)) { - fprintf (stderr, - _("%s: failure while writing changes to %s\n"), - Prog, spw_dbname ()); -@@ -657,7 +657,7 @@ static void check_pw_file (bool *errors, bool *changed) - } - #ifdef WITH_TCB - if (getdef_bool ("USE_TCB") && spw_locked) { -- if (spw_opened && (spw_close () == 0)) { -+ if (spw_opened && (spw_close (true) == 0)) { - fprintf (stderr, - _("%s: failure while writing changes to %s\n"), - Prog, spw_dbname ()); -diff --git a/src/pwconv.c b/src/pwconv.c -index da3a5820..296b527a 100644 ---- a/src/pwconv.c -+++ b/src/pwconv.c -@@ -274,7 +274,7 @@ int main (int argc, char **argv) - } - } - -- if (spw_close () == 0) { -+ if (spw_close (true) == 0) { - fprintf (stderr, - _("%s: failure while writing changes to %s\n"), - Prog, spw_dbname ()); -diff --git a/src/pwunconv.c b/src/pwunconv.c -index 8423cda4..e15c8c06 100644 ---- a/src/pwunconv.c -+++ b/src/pwunconv.c -@@ -205,7 +205,7 @@ int main (int argc, char **argv) - } - } - -- (void) spw_close (); /* was only open O_RDONLY */ -+ (void) spw_close (true); /* was only open O_RDONLY */ - - if (pw_close (true) == 0) { - fprintf (stderr, -diff --git a/src/useradd.c b/src/useradd.c -index 8c9e9003..8a7df6cf 100644 ---- a/src/useradd.c -+++ b/src/useradd.c -@@ -1569,7 +1569,7 @@ static void close_files (void) - SYSLOG ((LOG_ERR, "failure while writing changes to %s", pw_dbname ())); - fail_exit (E_PW_UPDATE); - } -- if (is_shadow_pwd && (spw_close () == 0)) { -+ if (is_shadow_pwd && (spw_close (true) == 0)) { - fprintf (stderr, - _("%s: failure while writing changes to %s\n"), Prog, spw_dbname ()); - SYSLOG ((LOG_ERR, "failure while writing changes to %s", spw_dbname ())); -diff --git a/src/userdel.c b/src/userdel.c -index b5a02b3c..b87f2459 100644 ---- a/src/userdel.c -+++ b/src/userdel.c -@@ -403,7 +403,7 @@ static void close_files (void) - pw_locked = false; - - if (is_shadow_pwd) { -- if (spw_close () == 0) { -+ if (spw_close (true) == 0) { - fprintf (stderr, - _("%s: failure while writing changes to %s\n"), Prog, spw_dbname ()); - SYSLOG ((LOG_ERR, "failure while writing changes to %s", spw_dbname ())); -diff --git a/src/usermod.c b/src/usermod.c -index 180b7da0..d741508a 100644 ---- a/src/usermod.c -+++ b/src/usermod.c -@@ -1476,7 +1476,7 @@ static void close_files (void) - SYSLOG ((LOG_ERR, "failure while writing changes to %s", pw_dbname ())); - fail_exit (E_PW_UPDATE); - } -- if (is_shadow_pwd && (spw_close () == 0)) { -+ if (is_shadow_pwd && (spw_close (true) == 0)) { - fprintf (stderr, - _("%s: failure while writing changes to %s\n"), - Prog, spw_dbname ()); --- -2.50.0 - - -From 16bd1d985900fe83a1f7c9001fa3b9f18e0f06f0 Mon Sep 17 00:00:00 2001 -From: Iker Pedrosa -Date: Fri, 25 Apr 2025 15:28:42 +0200 -Subject: [PATCH 04/15] 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 ---- - lib/groupio.c | 4 ++-- - lib/groupio.h | 3 ++- - src/chgpasswd.c | 2 +- - src/gpasswd.c | 4 ++-- - src/groupadd.c | 2 +- - src/groupdel.c | 2 +- - src/groupmems.c | 2 +- - src/groupmod.c | 2 +- - src/grpck.c | 2 +- - src/grpconv.c | 2 +- - src/grpunconv.c | 2 +- - src/newusers.c | 2 +- - src/useradd.c | 2 +- - src/userdel.c | 2 +- - src/usermod.c | 2 +- - 15 files changed, 18 insertions(+), 17 deletions(-) - -diff --git a/lib/groupio.c b/lib/groupio.c -index 5076fd3d..837e4c47 100644 ---- a/lib/groupio.c -+++ b/lib/groupio.c -@@ -184,9 +184,9 @@ int gr_rewind (void) - return commonio_next (&group_db); - } - --int gr_close (void) -+int gr_close (bool process_selinux) - { -- return commonio_close (&group_db, true); -+ return commonio_close (&group_db, process_selinux); - } - - int gr_unlock (void) -diff --git a/lib/groupio.h b/lib/groupio.h -index 2014de0c..ef9242d0 100644 ---- a/lib/groupio.h -+++ b/lib/groupio.h -@@ -14,8 +14,9 @@ - - #include - #include -+#include - --extern int gr_close (void); -+extern int gr_close (bool process_selinux); - extern /*@observer@*/ /*@null@*/const struct group *gr_locate (const char *name); - extern /*@observer@*/ /*@null@*/const struct group *gr_locate_gid (gid_t gid); - extern int gr_lock (void); -diff --git a/src/chgpasswd.c b/src/chgpasswd.c -index e7759657..9b686ef0 100644 ---- a/src/chgpasswd.c -+++ b/src/chgpasswd.c -@@ -397,7 +397,7 @@ static void close_files (void) - } - #endif - -- if (gr_close () == 0) { -+ if (gr_close (true) == 0) { - fprintf (stderr, - _("%s: failure while writing changes to %s\n"), - Prog, gr_dbname ()); -diff --git a/src/gpasswd.c b/src/gpasswd.c -index 460bd14c..1013f004 100644 ---- a/src/gpasswd.c -+++ b/src/gpasswd.c -@@ -593,7 +593,7 @@ static void log_gpasswd_success_group (MAYBE_UNUSED void *arg) - */ - static void close_files (void) - { -- if (gr_close () == 0) { -+ if (gr_close (true) == 0) { - fprintf (stderr, - _("%s: failure while writing changes to %s\n"), - Prog, gr_dbname ()); -@@ -724,7 +724,7 @@ static void get_group (struct group *gr) - gr->gr_passwd = xstrdup (tmpgr->gr_passwd); - gr->gr_mem = dup_list (tmpgr->gr_mem); - -- if (gr_close () == 0) { -+ if (gr_close (true) == 0) { - fprintf (stderr, - _("%s: failure while closing read-only %s\n"), - Prog, gr_dbname ()); -diff --git a/src/groupadd.c b/src/groupadd.c -index a0d5adeb..d9a9d36c 100644 ---- a/src/groupadd.c -+++ b/src/groupadd.c -@@ -274,7 +274,7 @@ check_new_name(void) - static void close_files (void) - { - /* First, write the changes in the regular group database */ -- if (gr_close () == 0) { -+ if (gr_close (true) == 0) { - fprintf (stderr, - _("%s: failure while writing changes to %s\n"), - Prog, gr_dbname ()); -diff --git a/src/groupdel.c b/src/groupdel.c -index 3d289fef..c52a2ae7 100644 ---- a/src/groupdel.c -+++ b/src/groupdel.c -@@ -149,7 +149,7 @@ static void grp_update (void) - static void close_files (void) - { - /* First, write the changes in the regular group database */ -- if (gr_close () == 0) { -+ if (gr_close (true) == 0) { - fprintf (stderr, - _("%s: failure while writing changes to %s\n"), - Prog, gr_dbname ()); -diff --git a/src/groupmems.c b/src/groupmems.c -index d37b237f..fff9ae10 100644 ---- a/src/groupmems.c -+++ b/src/groupmems.c -@@ -522,7 +522,7 @@ static void open_files (void) - - static void close_files (void) - { -- if ((gr_close () == 0) && !list) { -+ if ((gr_close (true) == 0) && !list) { - fprintf (stderr, _("%s: failure while writing changes to %s\n"), Prog, gr_dbname ()); - SYSLOG ((LOG_ERR, "failure while writing changes to %s", gr_dbname ())); - fail_exit (EXIT_GROUP_FILE); -diff --git a/src/groupmod.c b/src/groupmod.c -index 14b7e746..49b68c55 100644 ---- a/src/groupmod.c -+++ b/src/groupmod.c -@@ -481,7 +481,7 @@ static void process_flags (int argc, char **argv) - */ - static void close_files (void) - { -- if (gr_close () == 0) { -+ if (gr_close (true) == 0) { - fprintf (stderr, - _("%s: failure while writing changes to %s\n"), - Prog, gr_dbname ()); -diff --git a/src/grpck.c b/src/grpck.c -index 5fcf6fad..db8f1e68 100644 ---- a/src/grpck.c -+++ b/src/grpck.c -@@ -321,7 +321,7 @@ static void close_files (bool changed) - * changes to the files. - */ - if (changed) { -- if (gr_close () == 0) { -+ if (gr_close (true) == 0) { - fprintf (stderr, _("%s: failure while writing changes to %s\n"), - Prog, grp_file); - fail_exit (E_CANT_UPDATE); -diff --git a/src/grpconv.c b/src/grpconv.c -index 1b31cb85..e7928a4a 100644 ---- a/src/grpconv.c -+++ b/src/grpconv.c -@@ -241,7 +241,7 @@ int main (int argc, char **argv) - SYSLOG ((LOG_ERR, "failure while writing changes to %s", sgr_dbname ())); - fail_exit (3); - } -- if (gr_close () == 0) { -+ if (gr_close (true) == 0) { - fprintf (stderr, - _("%s: failure while writing changes to %s\n"), - Prog, gr_dbname ()); -diff --git a/src/grpunconv.c b/src/grpunconv.c -index ea65a329..63597da1 100644 ---- a/src/grpunconv.c -+++ b/src/grpunconv.c -@@ -193,7 +193,7 @@ int main (int argc, char **argv) - - (void) sgr_close (); /* was only open O_RDONLY */ - -- if (gr_close () == 0) { -+ if (gr_close (true) == 0) { - fprintf (stderr, - _("%s: failure while writing changes to %s\n"), - Prog, gr_dbname ()); -diff --git a/src/newusers.c b/src/newusers.c -index 43672dd2..29d3a536 100644 ---- a/src/newusers.c -+++ b/src/newusers.c -@@ -969,7 +969,7 @@ static void close_files (void) - spw_locked = false; - } - -- if (gr_close () == 0) { -+ if (gr_close (true) == 0) { - fprintf (stderr, - _("%s: failure while writing changes to %s\n"), - Prog, gr_dbname ()); -diff --git a/src/useradd.c b/src/useradd.c -index 8a7df6cf..f52e5483 100644 ---- a/src/useradd.c -+++ b/src/useradd.c -@@ -1664,7 +1664,7 @@ static void close_group_files (void) - if (!do_grp_update) - return; - -- if (gr_close() == 0) { -+ if (gr_close(true) == 0) { - fprintf(stderr, - _("%s: failure while writing changes to %s\n"), - Prog, gr_dbname()); -diff --git a/src/userdel.c b/src/userdel.c -index b87f2459..b0e0d541 100644 ---- a/src/userdel.c -+++ b/src/userdel.c -@@ -417,7 +417,7 @@ static void close_files (void) - spw_locked = false; - } - -- if (gr_close () == 0) { -+ if (gr_close (true) == 0) { - fprintf (stderr, _("%s: failure while writing changes to %s\n"), Prog, gr_dbname ()); - SYSLOG ((LOG_ERR, "failure while writing changes to %s", gr_dbname ())); - fail_exit (E_GRP_UPDATE); -diff --git a/src/usermod.c b/src/usermod.c -index d741508a..d1d26eaa 100644 ---- a/src/usermod.c -+++ b/src/usermod.c -@@ -1487,7 +1487,7 @@ static void close_files (void) - } - - if (Gflg || lflg) { -- if (gr_close () == 0) { -+ if (gr_close (true) == 0) { - fprintf (stderr, - _("%s: failure while writing changes to %s\n"), - Prog, gr_dbname ()); --- -2.50.0 - - -From f4a67dd6281a513b56c15aa1a3f292ca05674f13 Mon Sep 17 00:00:00 2001 -From: Iker Pedrosa -Date: Fri, 25 Apr 2025 15:32:05 +0200 -Subject: [PATCH 05/15] 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 ---- - lib/sgroupio.c | 4 ++-- - lib/sgroupio.h | 2 +- - src/chgpasswd.c | 2 +- - src/gpasswd.c | 4 ++-- - src/groupadd.c | 2 +- - src/groupdel.c | 2 +- - src/groupmems.c | 2 +- - src/groupmod.c | 2 +- - src/grpck.c | 2 +- - src/grpconv.c | 2 +- - src/grpunconv.c | 2 +- - src/newusers.c | 2 +- - src/useradd.c | 2 +- - src/userdel.c | 2 +- - src/usermod.c | 2 +- - 15 files changed, 17 insertions(+), 17 deletions(-) - -diff --git a/lib/sgroupio.c b/lib/sgroupio.c -index 659dcd06..025584ad 100644 ---- a/lib/sgroupio.c -+++ b/lib/sgroupio.c -@@ -276,9 +276,9 @@ int sgr_rewind (void) - return commonio_next (&gshadow_db); - } - --int sgr_close (void) -+int sgr_close (bool process_selinux) - { -- return commonio_close (&gshadow_db, true); -+ return commonio_close (&gshadow_db, process_selinux); - } - - int sgr_unlock (void) -diff --git a/lib/sgroupio.h b/lib/sgroupio.h -index 3474a985..0e25fc0b 100644 ---- a/lib/sgroupio.h -+++ b/lib/sgroupio.h -@@ -12,7 +12,7 @@ - #ifndef _SGROUPIO_H - #define _SGROUPIO_H - --extern int sgr_close (void); -+extern int sgr_close (bool process_selinux); - extern bool sgr_file_present (void); - extern /*@observer@*/ /*@null@*/const struct sgrp *sgr_locate (const char *name); - extern int sgr_lock (void); -diff --git a/src/chgpasswd.c b/src/chgpasswd.c -index 9b686ef0..47402ecc 100644 ---- a/src/chgpasswd.c -+++ b/src/chgpasswd.c -@@ -381,7 +381,7 @@ static void close_files (void) - { - #ifdef SHADOWGRP - if (is_shadow_grp) { -- if (sgr_close () == 0) { -+ if (sgr_close (true) == 0) { - fprintf (stderr, - _("%s: failure while writing changes to %s\n"), - Prog, sgr_dbname ()); -diff --git a/src/gpasswd.c b/src/gpasswd.c -index 1013f004..a49b9140 100644 ---- a/src/gpasswd.c -+++ b/src/gpasswd.c -@@ -607,7 +607,7 @@ static void close_files (void) - - #ifdef SHADOWGRP - if (is_shadowgrp) { -- if (sgr_close () == 0) { -+ if (sgr_close (true) == 0) { - fprintf (stderr, - _("%s: failure while writing changes to %s\n"), - Prog, sgr_dbname ()); -@@ -762,7 +762,7 @@ static void get_group (struct group *gr) - sg->sg_adm[0] = NULL; - - } -- if (sgr_close () == 0) { -+ if (sgr_close (true) == 0) { - fprintf (stderr, - _("%s: failure while closing read-only %s\n"), - Prog, sgr_dbname ()); -diff --git a/src/groupadd.c b/src/groupadd.c -index d9a9d36c..7cfb0eb5 100644 ---- a/src/groupadd.c -+++ b/src/groupadd.c -@@ -295,7 +295,7 @@ static void close_files (void) - /* Now, write the changes in the shadow database */ - #ifdef SHADOWGRP - if (is_shadow_grp) { -- if (sgr_close () == 0) { -+ if (sgr_close (true) == 0) { - fprintf (stderr, - _("%s: failure while writing changes to %s\n"), - Prog, sgr_dbname ()); -diff --git a/src/groupdel.c b/src/groupdel.c -index c52a2ae7..56123a7b 100644 ---- a/src/groupdel.c -+++ b/src/groupdel.c -@@ -173,7 +173,7 @@ static void close_files (void) - /* Then, write the changes in the shadow database */ - #ifdef SHADOWGRP - if (is_shadow_grp) { -- if (sgr_close () == 0) { -+ if (sgr_close (true) == 0) { - fprintf (stderr, - _("%s: failure while writing changes to %s\n"), - Prog, sgr_dbname ()); -diff --git a/src/groupmems.c b/src/groupmems.c -index fff9ae10..5976b074 100644 ---- a/src/groupmems.c -+++ b/src/groupmems.c -@@ -538,7 +538,7 @@ static void close_files (void) - - #ifdef SHADOWGRP - if (is_shadowgrp) { -- if ((sgr_close () == 0) && !list) { -+ if ((sgr_close (true) == 0) && !list) { - fprintf (stderr, _("%s: failure while writing changes to %s\n"), Prog, sgr_dbname ()); - SYSLOG ((LOG_ERR, "failure while writing changes to %s", sgr_dbname ())); - fail_exit (EXIT_GROUP_FILE); -diff --git a/src/groupmod.c b/src/groupmod.c -index 49b68c55..3c258feb 100644 ---- a/src/groupmod.c -+++ b/src/groupmod.c -@@ -504,7 +504,7 @@ static void close_files (void) - #ifdef SHADOWGRP - if ( is_shadow_grp - && (pflg || nflg || user_list)) { -- if (sgr_close () == 0) { -+ if (sgr_close (true) == 0) { - fprintf (stderr, - _("%s: failure while writing changes to %s\n"), - Prog, sgr_dbname ()); -diff --git a/src/grpck.c b/src/grpck.c -index db8f1e68..872f882f 100644 ---- a/src/grpck.c -+++ b/src/grpck.c -@@ -327,7 +327,7 @@ static void close_files (bool changed) - fail_exit (E_CANT_UPDATE); - } - #ifdef SHADOWGRP -- if (is_shadow && (sgr_close () == 0)) { -+ if (is_shadow && (sgr_close (true) == 0)) { - fprintf (stderr, _("%s: failure while writing changes to %s\n"), - Prog, sgr_file); - fail_exit (E_CANT_UPDATE); -diff --git a/src/grpconv.c b/src/grpconv.c -index e7928a4a..d9c59fc9 100644 ---- a/src/grpconv.c -+++ b/src/grpconv.c -@@ -234,7 +234,7 @@ int main (int argc, char **argv) - } - } - -- if (sgr_close () == 0) { -+ if (sgr_close (true) == 0) { - fprintf (stderr, - _("%s: failure while writing changes to %s\n"), - Prog, sgr_dbname ()); -diff --git a/src/grpunconv.c b/src/grpunconv.c -index 63597da1..03fc824e 100644 ---- a/src/grpunconv.c -+++ b/src/grpunconv.c -@@ -191,7 +191,7 @@ int main (int argc, char **argv) - } - } - -- (void) sgr_close (); /* was only open O_RDONLY */ -+ (void) sgr_close (true); /* was only open O_RDONLY */ - - if (gr_close (true) == 0) { - fprintf (stderr, -diff --git a/src/newusers.c b/src/newusers.c -index 29d3a536..19a32863 100644 ---- a/src/newusers.c -+++ b/src/newusers.c -@@ -1002,7 +1002,7 @@ static void close_files (void) - - #ifdef SHADOWGRP - if (is_shadow_grp) { -- if (sgr_close () == 0) { -+ if (sgr_close (true) == 0) { - fprintf (stderr, - _("%s: failure while writing changes to %s\n"), - Prog, sgr_dbname ()); -diff --git a/src/useradd.c b/src/useradd.c -index f52e5483..7f596673 100644 ---- a/src/useradd.c -+++ b/src/useradd.c -@@ -1672,7 +1672,7 @@ static void close_group_files (void) - fail_exit(E_GRP_UPDATE); - } - #ifdef SHADOWGRP -- if (is_shadow_grp && sgr_close() == 0) { -+ if (is_shadow_grp && sgr_close(true) == 0) { - fprintf(stderr, - _("%s: failure while writing changes to %s\n"), - Prog, sgr_dbname()); -diff --git a/src/userdel.c b/src/userdel.c -index b0e0d541..1829ba4d 100644 ---- a/src/userdel.c -+++ b/src/userdel.c -@@ -431,7 +431,7 @@ static void close_files (void) - - #ifdef SHADOWGRP - if (is_shadow_grp) { -- if (sgr_close () == 0) { -+ if (sgr_close (true) == 0) { - fprintf (stderr, - _("%s: failure while writing changes to %s\n"), Prog, sgr_dbname ()); - SYSLOG ((LOG_ERR, "failure while writing changes to %s", sgr_dbname ())); -diff --git a/src/usermod.c b/src/usermod.c -index d1d26eaa..dbcb32b2 100644 ---- a/src/usermod.c -+++ b/src/usermod.c -@@ -1498,7 +1498,7 @@ static void close_files (void) - } - #ifdef SHADOWGRP - if (is_shadow_grp) { -- if (sgr_close () == 0) { -+ if (sgr_close (true) == 0) { - fprintf (stderr, - _("%s: failure while writing changes to %s\n"), - Prog, sgr_dbname ()); --- -2.50.0 - - -From f3dce7ede9c3f267e810fec3500498e76a305d8a Mon Sep 17 00:00:00 2001 -From: Iker Pedrosa -Date: Fri, 25 Apr 2025 15:35:48 +0200 -Subject: [PATCH 06/15] 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 ---- - lib/subordinateio.c | 14 +++++++------- - lib/subordinateio.h | 2 +- - lib/user_busy.c | 10 +++++----- - src/newuidmap.c | 2 +- - src/newusers.c | 2 +- - src/useradd.c | 2 +- - src/userdel.c | 2 +- - src/usermod.c | 2 +- - 8 files changed, 18 insertions(+), 18 deletions(-) - -diff --git a/lib/subordinateio.c b/lib/subordinateio.c -index 3638ec61..206a1348 100644 ---- a/lib/subordinateio.c -+++ b/lib/subordinateio.c -@@ -590,7 +590,7 @@ static bool have_range(struct commonio_db *db, - - if (doclose) { - if (db == &subordinate_uid_db) -- sub_uid_close(); -+ sub_uid_close(true); - else - sub_gid_close(); - } -@@ -668,9 +668,9 @@ int sub_uid_remove (const char *owner, uid_t start, unsigned long count) - return remove_range (&subordinate_uid_db, owner, start, count); - } - --int sub_uid_close (void) -+int sub_uid_close (bool process_selinux) - { -- return commonio_close (&subordinate_uid_db, true); -+ return commonio_close (&subordinate_uid_db, process_selinux); - } - - int sub_uid_unlock (void) -@@ -907,7 +907,7 @@ int list_owner_ranges(const char *owner, enum subid_type id_type, struct subid_r - - out: - if (id_type == ID_TYPE_UID) -- sub_uid_close(); -+ sub_uid_close(true); - else - sub_gid_close(); - -@@ -998,7 +998,7 @@ int find_subid_owners(unsigned long id, enum subid_type id_type, uid_t **uids) - } - - if (id_type == ID_TYPE_UID) -- sub_uid_close(); -+ sub_uid_close(true); - else - sub_gid_close(); - -@@ -1068,7 +1068,7 @@ bool new_subid_range(struct subordinate_range *range, enum subid_type id_type, b - - out: - if (id_type == ID_TYPE_UID) { -- sub_uid_close(); -+ sub_uid_close(true); - sub_uid_unlock(); - } else { - sub_gid_close(); -@@ -1118,7 +1118,7 @@ bool release_subid_range(struct subordinate_range *range, enum subid_type id_typ - ret = remove_range(db, range->owner, range->start, range->count) == 1; - - if (id_type == ID_TYPE_UID) { -- sub_uid_close(); -+ sub_uid_close(true); - sub_uid_unlock(); - } else { - sub_gid_close(); -diff --git a/lib/subordinateio.h b/lib/subordinateio.h -index 3d1a0547..d16c8b9e 100644 ---- a/lib/subordinateio.h -+++ b/lib/subordinateio.h -@@ -13,7 +13,7 @@ - - #include "../libsubid/subid.h" - --extern int sub_uid_close(void); -+extern int sub_uid_close(bool process_selinux); - extern bool have_sub_uids(const char *owner, uid_t start, unsigned long count); - extern bool sub_uid_file_present (void); - extern bool local_sub_uid_assigned(const char *owner); -diff --git a/lib/user_busy.c b/lib/user_busy.c -index d689d34d..ab1dd5a4 100644 ---- a/lib/user_busy.c -+++ b/lib/user_busy.c -@@ -180,7 +180,7 @@ static int user_busy_processes (const char *name, uid_t uid) - if (proc == NULL) { - perror ("opendir /proc"); - #ifdef ENABLE_SUBIDS -- sub_uid_close(); -+ sub_uid_close(true); - #endif - return 0; - } -@@ -188,7 +188,7 @@ static int user_busy_processes (const char *name, uid_t uid) - perror ("stat (\"/\")"); - (void) closedir (proc); - #ifdef ENABLE_SUBIDS -- sub_uid_close(); -+ sub_uid_close(true); - #endif - return 0; - } -@@ -225,7 +225,7 @@ static int user_busy_processes (const char *name, uid_t uid) - if (check_status (name, tmp_d_name, uid) != 0) { - (void) closedir (proc); - #ifdef ENABLE_SUBIDS -- sub_uid_close(); -+ sub_uid_close(true); - #endif - fprintf (log_get_logfd(), - _("%s: user %s is currently used by process %d\n"), -@@ -248,7 +248,7 @@ static int user_busy_processes (const char *name, uid_t uid) - (void) closedir (proc); - (void) closedir (task_dir); - #ifdef ENABLE_SUBIDS -- sub_uid_close(); -+ sub_uid_close(true); - #endif - fprintf (log_get_logfd(), - _("%s: user %s is currently used by process %d\n"), -@@ -264,7 +264,7 @@ static int user_busy_processes (const char *name, uid_t uid) - - (void) closedir (proc); - #ifdef ENABLE_SUBIDS -- sub_uid_close(); -+ sub_uid_close(true); - #endif /* ENABLE_SUBIDS */ - return 0; - } -diff --git a/src/newuidmap.c b/src/newuidmap.c -index 894c5ec8..90bd6de5 100644 ---- a/src/newuidmap.c -+++ b/src/newuidmap.c -@@ -158,7 +158,7 @@ int main(int argc, char **argv) - verify_ranges(pw, ranges, mappings); - - write_mapping(proc_dir_fd, ranges, mappings, "uid_map", pw->pw_uid); -- sub_uid_close(); -+ sub_uid_close(true); - - return EXIT_SUCCESS; - } -diff --git a/src/newusers.c b/src/newusers.c -index 19a32863..d71a792b 100644 ---- a/src/newusers.c -+++ b/src/newusers.c -@@ -977,7 +977,7 @@ static void close_files (void) - fail_exit (EXIT_FAILURE); - } - #ifdef ENABLE_SUBIDS -- if (is_sub_uid && (sub_uid_close () == 0)) { -+ if (is_sub_uid && (sub_uid_close (true) == 0)) { - fprintf (stderr, - _("%s: failure while writing changes to %s\n"), Prog, sub_uid_dbname ()); - SYSLOG ((LOG_ERR, "failure while writing changes to %s", sub_uid_dbname ())); -diff --git a/src/useradd.c b/src/useradd.c -index 7f596673..971fec55 100644 ---- a/src/useradd.c -+++ b/src/useradd.c -@@ -1579,7 +1579,7 @@ static void close_files (void) - close_group_files (); - - #ifdef ENABLE_SUBIDS -- if (is_sub_uid && (sub_uid_close () == 0)) { -+ if (is_sub_uid && (sub_uid_close (true) == 0)) { - fprintf (stderr, - _("%s: failure while writing changes to %s\n"), Prog, sub_uid_dbname ()); - SYSLOG ((LOG_ERR, "failure while writing changes to %s", sub_uid_dbname ())); -diff --git a/src/userdel.c b/src/userdel.c -index 1829ba4d..8fe9f4e2 100644 ---- a/src/userdel.c -+++ b/src/userdel.c -@@ -449,7 +449,7 @@ static void close_files (void) - - #ifdef ENABLE_SUBIDS - if (is_sub_uid) { -- if (sub_uid_close () == 0) { -+ if (sub_uid_close (true) == 0) { - fprintf (stderr, _("%s: failure while writing changes to %s\n"), Prog, sub_uid_dbname ()); - SYSLOG ((LOG_ERR, "failure while writing changes to %s", sub_uid_dbname ())); - fail_exit (E_SUB_UID_UPDATE); -diff --git a/src/usermod.c b/src/usermod.c -index dbcb32b2..440c2406 100644 ---- a/src/usermod.c -+++ b/src/usermod.c -@@ -1561,7 +1561,7 @@ static void close_files (void) - - #ifdef ENABLE_SUBIDS - if (vflg || Vflg) { -- if (sub_uid_close () == 0) { -+ if (sub_uid_close (true) == 0) { - fprintf (stderr, _("%s: failure while writing changes to %s\n"), Prog, sub_uid_dbname ()); - SYSLOG ((LOG_ERR, "failure while writing changes to %s", sub_uid_dbname ())); - fail_exit (E_SUB_UID_UPDATE); --- -2.50.0 - - -From ab2a2656e442043d5b39e1320552ba9850aabd27 Mon Sep 17 00:00:00 2001 -From: Iker Pedrosa -Date: Fri, 25 Apr 2025 15:38:18 +0200 -Subject: [PATCH 07/15] 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 ---- - lib/subordinateio.c | 14 +++++++------- - lib/subordinateio.h | 2 +- - src/newgidmap.c | 2 +- - src/newusers.c | 2 +- - src/useradd.c | 2 +- - src/userdel.c | 2 +- - src/usermod.c | 2 +- - 7 files changed, 13 insertions(+), 13 deletions(-) - -diff --git a/lib/subordinateio.c b/lib/subordinateio.c -index 206a1348..e2c2375a 100644 ---- a/lib/subordinateio.c -+++ b/lib/subordinateio.c -@@ -592,7 +592,7 @@ static bool have_range(struct commonio_db *db, - if (db == &subordinate_uid_db) - sub_uid_close(true); - else -- sub_gid_close(); -+ sub_gid_close(true); - } - - return ret; -@@ -775,9 +775,9 @@ int sub_gid_remove (const char *owner, gid_t start, unsigned long count) - return remove_range (&subordinate_gid_db, owner, start, count); - } - --int sub_gid_close (void) -+int sub_gid_close (bool process_selinux) - { -- return commonio_close (&subordinate_gid_db, true); -+ return commonio_close (&subordinate_gid_db, process_selinux); - } - - int sub_gid_unlock (void) -@@ -909,7 +909,7 @@ out: - if (id_type == ID_TYPE_UID) - sub_uid_close(true); - else -- sub_gid_close(); -+ sub_gid_close(true); - - *in_ranges = ranges; - return count; -@@ -1000,7 +1000,7 @@ int find_subid_owners(unsigned long id, enum subid_type id_type, uid_t **uids) - if (id_type == ID_TYPE_UID) - sub_uid_close(true); - else -- sub_gid_close(); -+ sub_gid_close(true); - - return n; - } -@@ -1071,7 +1071,7 @@ out: - sub_uid_close(true); - sub_uid_unlock(); - } else { -- sub_gid_close(); -+ sub_gid_close(true); - sub_gid_unlock(); - } - -@@ -1121,7 +1121,7 @@ bool release_subid_range(struct subordinate_range *range, enum subid_type id_typ - sub_uid_close(true); - sub_uid_unlock(); - } else { -- sub_gid_close(); -+ sub_gid_close(true); - sub_gid_unlock(); - } - -diff --git a/lib/subordinateio.h b/lib/subordinateio.h -index d16c8b9e..63f5b1fb 100644 ---- a/lib/subordinateio.h -+++ b/lib/subordinateio.h -@@ -31,7 +31,7 @@ extern bool release_subid_range(struct subordinate_range *range, enum subid_type - extern int find_subid_owners(unsigned long id, enum subid_type id_type, uid_t **uids); - extern void free_subordinate_ranges(struct subordinate_range **ranges, int count); - --extern int sub_gid_close(void); -+extern int sub_gid_close(bool process_selinux); - extern bool have_sub_gids(const char *owner, gid_t start, unsigned long count); - extern bool sub_gid_file_present (void); - extern bool local_sub_gid_assigned(const char *owner); -diff --git a/src/newgidmap.c b/src/newgidmap.c -index 3463e850..7ce22b5a 100644 ---- a/src/newgidmap.c -+++ b/src/newgidmap.c -@@ -230,7 +230,7 @@ int main(int argc, char **argv) - - write_setgroups(proc_dir_fd, allow_setgroups); - write_mapping(proc_dir_fd, ranges, mappings, "gid_map", pw->pw_uid); -- sub_gid_close(); -+ sub_gid_close(true); - - return EXIT_SUCCESS; - } -diff --git a/src/newusers.c b/src/newusers.c -index d71a792b..8cd11a71 100644 ---- a/src/newusers.c -+++ b/src/newusers.c -@@ -983,7 +983,7 @@ static void close_files (void) - SYSLOG ((LOG_ERR, "failure while writing changes to %s", sub_uid_dbname ())); - fail_exit (EXIT_FAILURE); - } -- if (is_sub_gid && (sub_gid_close () == 0)) { -+ if (is_sub_gid && (sub_gid_close (true) == 0)) { - fprintf (stderr, - _("%s: failure while writing changes to %s\n"), Prog, sub_gid_dbname ()); - SYSLOG ((LOG_ERR, "failure while writing changes to %s", sub_gid_dbname ())); -diff --git a/src/useradd.c b/src/useradd.c -index 971fec55..3d2fdb35 100644 ---- a/src/useradd.c -+++ b/src/useradd.c -@@ -1585,7 +1585,7 @@ static void close_files (void) - SYSLOG ((LOG_ERR, "failure while writing changes to %s", sub_uid_dbname ())); - fail_exit (E_SUB_UID_UPDATE); - } -- if (is_sub_gid && (sub_gid_close () == 0)) { -+ if (is_sub_gid && (sub_gid_close (true) == 0)) { - fprintf (stderr, - _("%s: failure while writing changes to %s\n"), Prog, sub_gid_dbname ()); - SYSLOG ((LOG_ERR, "failure while writing changes to %s", sub_gid_dbname ())); -diff --git a/src/userdel.c b/src/userdel.c -index 8fe9f4e2..7dcbd923 100644 ---- a/src/userdel.c -+++ b/src/userdel.c -@@ -463,7 +463,7 @@ static void close_files (void) - } - - if (is_sub_gid) { -- if (sub_gid_close () == 0) { -+ if (sub_gid_close (true) == 0) { - fprintf (stderr, _("%s: failure while writing changes to %s\n"), Prog, sub_gid_dbname ()); - SYSLOG ((LOG_ERR, "failure while writing changes to %s", sub_gid_dbname ())); - fail_exit (E_SUB_GID_UPDATE); -diff --git a/src/usermod.c b/src/usermod.c -index 440c2406..534bdc74 100644 ---- a/src/usermod.c -+++ b/src/usermod.c -@@ -1574,7 +1574,7 @@ static void close_files (void) - sub_uid_locked = false; - } - if (wflg || Wflg) { -- if (sub_gid_close () == 0) { -+ if (sub_gid_close (true) == 0) { - fprintf (stderr, _("%s: failure while writing changes to %s\n"), Prog, sub_gid_dbname ()); - SYSLOG ((LOG_ERR, "failure while writing changes to %s", sub_gid_dbname ())); - fail_exit (E_SUB_GID_UPDATE); --- -2.50.0 - - -From 9161c63be728bfba7545bac1e4cbde8a069ad4e6 Mon Sep 17 00:00:00 2001 -From: Iker Pedrosa -Date: Fri, 25 Apr 2025 15:44:06 +0200 -Subject: [PATCH 08/15] 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 ---- - lib/commonio.c | 4 ++-- - lib/commonio.h | 2 +- - lib/groupio.c | 2 +- - lib/pwio.c | 2 +- - lib/sgroupio.c | 2 +- - lib/shadowio.c | 2 +- - lib/subordinateio.c | 4 ++-- - 7 files changed, 9 insertions(+), 9 deletions(-) - -diff --git a/lib/commonio.c b/lib/commonio.c -index 7ee6df4d..19653e7e 100644 ---- a/lib/commonio.c -+++ b/lib/commonio.c -@@ -467,13 +467,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, true) == 0) { -+ if (commonio_close (db, process_selinux) == 0) { - if (db->locked) { - dec_lock_count (); - } -diff --git a/lib/commonio.h b/lib/commonio.h -index dadc0500..8b8f5cf4 100644 ---- a/lib/commonio.h -+++ b/lib/commonio.h -@@ -139,7 +139,7 @@ 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 *, bool); --extern int commonio_unlock (struct commonio_db *); -+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, -diff --git a/lib/groupio.c b/lib/groupio.c -index 837e4c47..6ad4ae69 100644 ---- a/lib/groupio.c -+++ b/lib/groupio.c -@@ -191,7 +191,7 @@ int gr_close (bool process_selinux) - - int gr_unlock (void) - { -- return commonio_unlock (&group_db); -+ return commonio_unlock (&group_db, true); - } - - void __gr_set_changed (void) -diff --git a/lib/pwio.c b/lib/pwio.c -index c5c1cdb1..c5dcb904 100644 ---- a/lib/pwio.c -+++ b/lib/pwio.c -@@ -163,7 +163,7 @@ int pw_close (bool process_selinux) - - int pw_unlock (void) - { -- return commonio_unlock (&passwd_db); -+ return commonio_unlock (&passwd_db, true); - } - - /*@null@*/struct commonio_entry *__pw_get_head (void) -diff --git a/lib/sgroupio.c b/lib/sgroupio.c -index 025584ad..dbabcc36 100644 ---- a/lib/sgroupio.c -+++ b/lib/sgroupio.c -@@ -283,7 +283,7 @@ int sgr_close (bool process_selinux) - - int sgr_unlock (void) - { -- return commonio_unlock (&gshadow_db); -+ return commonio_unlock (&gshadow_db, true); - } - - void __sgr_set_changed (void) -diff --git a/lib/shadowio.c b/lib/shadowio.c -index a109576d..9866af6d 100644 ---- a/lib/shadowio.c -+++ b/lib/shadowio.c -@@ -211,7 +211,7 @@ int spw_unlock (void) - - if (!getdef_bool ("USE_TCB")) { - #endif /* WITH_TCB */ -- return commonio_unlock (&shadow_db); -+ return commonio_unlock (&shadow_db, true); - #ifdef WITH_TCB - } - if (shadowtcb_drop_priv () == SHADOWTCB_FAILURE) { -diff --git a/lib/subordinateio.c b/lib/subordinateio.c -index e2c2375a..9587e1a9 100644 ---- a/lib/subordinateio.c -+++ b/lib/subordinateio.c -@@ -675,7 +675,7 @@ int sub_uid_close (bool process_selinux) - - int sub_uid_unlock (void) - { -- return commonio_unlock (&subordinate_uid_db); -+ return commonio_unlock (&subordinate_uid_db, true); - } - - uid_t sub_uid_find_free_range(uid_t min, uid_t max, unsigned long count) -@@ -782,7 +782,7 @@ int sub_gid_close (bool process_selinux) - - int sub_gid_unlock (void) - { -- return commonio_unlock (&subordinate_gid_db); -+ return commonio_unlock (&subordinate_gid_db, true); - } - - gid_t sub_gid_find_free_range(gid_t min, gid_t max, unsigned long count) --- -2.50.0 - - -From 9609c3ba7d4097c2ff63ee6ccfa6b74c41fbde5e Mon Sep 17 00:00:00 2001 -From: Iker Pedrosa -Date: Fri, 25 Apr 2025 15:53:17 +0200 -Subject: [PATCH 09/15] 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 ---- - lib/cleanup_user.c | 2 +- - lib/lockpw.c | 4 ++-- - lib/pwio.c | 4 ++-- - lib/pwio.h | 2 +- - src/chage.c | 4 ++-- - src/chfn.c | 4 ++-- - src/chpasswd.c | 4 ++-- - src/chsh.c | 4 ++-- - src/newusers.c | 4 ++-- - src/passwd.c | 4 ++-- - src/pwck.c | 4 ++-- - src/pwconv.c | 4 ++-- - src/pwunconv.c | 4 ++-- - src/useradd.c | 4 ++-- - src/userdel.c | 4 ++-- - src/usermod.c | 4 ++-- - 16 files changed, 30 insertions(+), 30 deletions(-) - -diff --git a/lib/cleanup_user.c b/lib/cleanup_user.c -index 809caaf3..a661f5e9 100644 ---- a/lib/cleanup_user.c -+++ b/lib/cleanup_user.c -@@ -98,7 +98,7 @@ void cleanup_report_add_user_shadow (void *user_name) - */ - void cleanup_unlock_passwd (MAYBE_UNUSED void *arg) - { -- if (pw_unlock () == 0) { -+ if (pw_unlock (true) == 0) { - fprintf (log_get_logfd(), - _("%s: failed to unlock %s\n"), - log_get_progname(), pw_dbname ()); -diff --git a/lib/lockpw.c b/lib/lockpw.c -index 15e7c8f4..f73c434a 100644 ---- a/lib/lockpw.c -+++ b/lib/lockpw.c -@@ -56,7 +56,7 @@ int lckpwdf (void) - */ - - if (i == 15) { -- pw_unlock (); -+ pw_unlock (true); - return -1; - } - -@@ -78,7 +78,7 @@ int ulckpwdf (void) - * Unlock both files. - */ - -- return (pw_unlock () && spw_unlock ())? 0 : -1; -+ return (pw_unlock (true) && spw_unlock ())? 0 : -1; - } - #else - extern int ISO_C_forbids_an_empty_translation_unit; -diff --git a/lib/pwio.c b/lib/pwio.c -index c5dcb904..78146c37 100644 ---- a/lib/pwio.c -+++ b/lib/pwio.c -@@ -161,9 +161,9 @@ int pw_close (bool process_selinux) - return commonio_close (&passwd_db, process_selinux); - } - --int pw_unlock (void) -+int pw_unlock (bool process_selinux) - { -- return commonio_unlock (&passwd_db, true); -+ return commonio_unlock (&passwd_db, process_selinux); - } - - /*@null@*/struct commonio_entry *__pw_get_head (void) -diff --git a/lib/pwio.h b/lib/pwio.h -index b051a7e7..0903b8bd 100644 ---- a/lib/pwio.h -+++ b/lib/pwio.h -@@ -26,7 +26,7 @@ extern /*@observer@*/ /*@null@*/const struct passwd *pw_next (void); - extern int pw_open (int mode); - extern int pw_remove (const char *name); - extern int pw_rewind (void); --extern int pw_unlock (void); -+extern int pw_unlock (bool process_selinux); - extern int pw_update (const struct passwd *pw); - extern int pw_sort (void); - -diff --git a/src/chage.c b/src/chage.c -index a59428c5..d3a42ccf 100644 ---- a/src/chage.c -+++ b/src/chage.c -@@ -101,7 +101,7 @@ fail_exit (int code) - } - } - if (pw_locked) { -- if (pw_unlock () == 0) { -+ if (pw_unlock (true) == 0) { - fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, pw_dbname ()); - SYSLOG ((LOG_ERR, "failed to unlock %s", pw_dbname ())); - /* continue */ -@@ -568,7 +568,7 @@ static void close_files (void) - /* continue */ - } - spw_locked = false; -- if (pw_unlock () == 0) { -+ if (pw_unlock (true) == 0) { - fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, pw_dbname ()); - SYSLOG ((LOG_ERR, "failed to unlock %s", pw_dbname ())); - /* continue */ -diff --git a/src/chfn.c b/src/chfn.c -index abf88cc8..dca81486 100644 ---- a/src/chfn.c -+++ b/src/chfn.c -@@ -79,7 +79,7 @@ static void get_old_fields (const char *gecos); - static void fail_exit (int code) - { - if (pw_locked) { -- if (pw_unlock () == 0) { -+ if (pw_unlock (true) == 0) { - fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, pw_dbname ()); - SYSLOG ((LOG_ERR, "failed to unlock %s", pw_dbname ())); - /* continue */ -@@ -498,7 +498,7 @@ static void update_gecos (const char *user, char *gecos) - SYSLOG ((LOG_ERR, "failure while writing changes to %s", pw_dbname ())); - fail_exit (E_NOPERM); - } -- if (pw_unlock () == 0) { -+ if (pw_unlock (true) == 0) { - fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, pw_dbname ()); - SYSLOG ((LOG_ERR, "failed to unlock %s", pw_dbname ())); - /* continue */ -diff --git a/src/chpasswd.c b/src/chpasswd.c -index e3d8dcef..5323c520 100644 ---- a/src/chpasswd.c -+++ b/src/chpasswd.c -@@ -81,7 +81,7 @@ static void close_files (void); - static void fail_exit (int code) - { - if (pw_locked) { -- if (pw_unlock () == 0) { -+ if (pw_unlock (true) == 0) { - fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, pw_dbname ()); - SYSLOG ((LOG_ERR, "failed to unlock %s", pw_dbname ())); - /* continue */ -@@ -400,7 +400,7 @@ static void close_files (void) - SYSLOG ((LOG_ERR, "failure while writing changes to %s", pw_dbname ())); - fail_exit (1); - } -- if (pw_unlock () == 0) { -+ if (pw_unlock (true) == 0) { - fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, pw_dbname ()); - SYSLOG ((LOG_ERR, "failed to unlock %s", pw_dbname ())); - /* continue */ -diff --git a/src/chsh.c b/src/chsh.c -index b5ae28da..4403d412 100644 ---- a/src/chsh.c -+++ b/src/chsh.c -@@ -78,7 +78,7 @@ static void - fail_exit (int code) - { - if (pw_locked) { -- if (pw_unlock () == 0) { -+ if (pw_unlock (true) == 0) { - fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, pw_dbname ()); - SYSLOG ((LOG_ERR, "failed to unlock %s", pw_dbname ())); - /* continue */ -@@ -433,7 +433,7 @@ static void update_shell (const char *user, char *newshell) - SYSLOG ((LOG_ERR, "failure while writing changes to %s", pw_dbname ())); - fail_exit (1); - } -- if (pw_unlock () == 0) { -+ if (pw_unlock (true) == 0) { - fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, pw_dbname ()); - SYSLOG ((LOG_ERR, "failed to unlock %s", pw_dbname ())); - /* continue */ -diff --git a/src/newusers.c b/src/newusers.c -index 8cd11a71..669b6236 100644 ---- a/src/newusers.c -+++ b/src/newusers.c -@@ -172,7 +172,7 @@ static void fail_exit (int code) - } - } - if (pw_locked) { -- if (pw_unlock () == 0) { -+ if (pw_unlock (true) == 0) { - fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, pw_dbname ()); - SYSLOG ((LOG_ERR, "failed to unlock %s", pw_dbname ())); - /* continue */ -@@ -944,7 +944,7 @@ static void close_files (void) - SYSLOG ((LOG_ERR, "failure while writing changes to %s", pw_dbname ())); - fail_exit (EXIT_FAILURE); - } -- if (pw_unlock () == 0) { -+ if (pw_unlock (true) == 0) { - fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, pw_dbname ()); - SYSLOG ((LOG_ERR, "failed to unlock %s", pw_dbname ())); - /* continue */ -diff --git a/src/passwd.c b/src/passwd.c -index 01efb285..8a55d797 100644 ---- a/src/passwd.c -+++ b/src/passwd.c -@@ -483,7 +483,7 @@ static void - fail_exit (int status) - { - if (pw_locked) { -- if (pw_unlock () == 0) { -+ if (pw_unlock (true) == 0) { - (void) fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, pw_dbname ()); - SYSLOG ((LOG_ERR, "failed to unlock %s", pw_dbname ())); - /* continue */ -@@ -593,7 +593,7 @@ static void update_noshadow (void) - SYSLOG ((LOG_ERR, "failure while writing changes to %s", pw_dbname ())); - fail_exit (E_FAILURE); - } -- if (pw_unlock () == 0) { -+ if (pw_unlock (true) == 0) { - (void) fprintf (stderr, - _("%s: failed to unlock %s\n"), - Prog, pw_dbname ()); -diff --git a/src/pwck.c b/src/pwck.c -index 718b639f..f30c5289 100644 ---- a/src/pwck.c -+++ b/src/pwck.c -@@ -96,7 +96,7 @@ static void fail_exit (int code) - } - - if (pw_locked) { -- if (pw_unlock () == 0) { -+ if (pw_unlock (true) == 0) { - fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, pw_dbname ()); - if (use_system_pw_file) { - SYSLOG ((LOG_ERR, "failed to unlock %s", -@@ -351,7 +351,7 @@ static void close_files (bool changed) - } - spw_locked = false; - if (pw_locked) { -- if (pw_unlock () == 0) { -+ if (pw_unlock (true) == 0) { - fprintf (stderr, - _("%s: failed to unlock %s\n"), - Prog, pw_dbname ()); -diff --git a/src/pwconv.c b/src/pwconv.c -index 296b527a..82181bb4 100644 ---- a/src/pwconv.c -+++ b/src/pwconv.c -@@ -83,7 +83,7 @@ static void process_flags (int argc, char **argv); - static void fail_exit (int status) - { - if (pw_locked) { -- if (pw_unlock () == 0) { -+ if (pw_unlock (true) == 0) { - fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, pw_dbname ()); - SYSLOG ((LOG_ERR, "failed to unlock %s", pw_dbname ())); - /* continue */ -@@ -299,7 +299,7 @@ int main (int argc, char **argv) - /* continue */ - } - -- if (pw_unlock () == 0) { -+ if (pw_unlock (true) == 0) { - fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, pw_dbname ()); - SYSLOG ((LOG_ERR, "failed to unlock %s", pw_dbname ())); - /* continue */ -diff --git a/src/pwunconv.c b/src/pwunconv.c -index e15c8c06..72944fe5 100644 ---- a/src/pwunconv.c -+++ b/src/pwunconv.c -@@ -54,7 +54,7 @@ static void fail_exit (int status) - } - } - if (pw_locked) { -- if (pw_unlock () == 0) { -+ if (pw_unlock (true) == 0) { - fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, pw_dbname ()); - SYSLOG ((LOG_ERR, "failed to unlock %s", pw_dbname ())); - /* continue */ -@@ -227,7 +227,7 @@ int main (int argc, char **argv) - SYSLOG ((LOG_ERR, "failed to unlock %s", spw_dbname ())); - /* continue */ - } -- if (pw_unlock () == 0) { -+ if (pw_unlock (true) == 0) { - fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, pw_dbname ()); - SYSLOG ((LOG_ERR, "failed to unlock %s", pw_dbname ())); - /* continue */ -diff --git a/src/useradd.c b/src/useradd.c -index 3d2fdb35..aa070f2d 100644 ---- a/src/useradd.c -+++ b/src/useradd.c -@@ -270,7 +270,7 @@ static void fail_exit (int code) - SYSLOG((LOG_ERR, "failed to unlock %s", spw_dbname())); - /* continue */ - } -- if (pw_locked && pw_unlock() == 0) { -+ if (pw_locked && pw_unlock(true) == 0) { - fprintf(stderr, _("%s: failed to unlock %s\n"), Prog, pw_dbname()); - SYSLOG((LOG_ERR, "failed to unlock %s", pw_dbname())); - /* continue */ -@@ -1606,7 +1606,7 @@ static void close_files (void) - } - spw_locked = false; - } -- if (pw_unlock () == 0) { -+ if (pw_unlock (true) == 0) { - fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, pw_dbname ()); - SYSLOG ((LOG_ERR, "failed to unlock %s", pw_dbname ())); - #ifdef WITH_AUDIT -diff --git a/src/userdel.c b/src/userdel.c -index 7dcbd923..b4e87820 100644 ---- a/src/userdel.c -+++ b/src/userdel.c -@@ -395,7 +395,7 @@ static void close_files (void) - SYSLOG ((LOG_ERR, "failure while writing changes to %s", pw_dbname ())); - fail_exit (E_PW_UPDATE); - } -- if (pw_unlock () == 0) { -+ if (pw_unlock (true) == 0) { - fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, pw_dbname ()); - SYSLOG ((LOG_ERR, "failed to unlock %s", pw_dbname ())); - /* continue */ -@@ -484,7 +484,7 @@ static void close_files (void) - static void fail_exit (int code) - { - if (pw_locked) { -- if (pw_unlock () == 0) { -+ if (pw_unlock (true) == 0) { - fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, pw_dbname ()); - SYSLOG ((LOG_ERR, "failed to unlock %s", pw_dbname ())); - /* continue */ -diff --git a/src/usermod.c b/src/usermod.c -index 534bdc74..6f761404 100644 ---- a/src/usermod.c -+++ b/src/usermod.c -@@ -655,7 +655,7 @@ fail_exit (int code) - } - } - if (pw_locked) { -- if (pw_unlock () == 0) { -+ if (pw_unlock (true) == 0) { - fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, pw_dbname ()); - SYSLOG ((LOG_ERR, "failed to unlock %s", pw_dbname ())); - /* continue */ -@@ -1544,7 +1544,7 @@ static void close_files (void) - /* continue */ - } - } -- if (pw_unlock () == 0) { -+ if (pw_unlock (true) == 0) { - fprintf (stderr, - _("%s: failed to unlock %s\n"), - Prog, pw_dbname ()); --- -2.50.0 - - -From bdce75b7b6f7ec69cda287b081de6ca83022ee79 Mon Sep 17 00:00:00 2001 -From: Iker Pedrosa -Date: Fri, 25 Apr 2025 15:57:21 +0200 -Subject: [PATCH 10/15] 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 ---- - lib/cleanup_user.c | 2 +- - lib/lockpw.c | 2 +- - lib/shadowio.c | 4 ++-- - lib/shadowio.h | 2 +- - src/chage.c | 4 ++-- - src/chpasswd.c | 4 ++-- - src/newusers.c | 4 ++-- - src/passwd.c | 6 +++--- - src/pwck.c | 8 ++++---- - src/pwconv.c | 4 ++-- - src/pwunconv.c | 4 ++-- - src/useradd.c | 4 ++-- - src/userdel.c | 4 ++-- - src/usermod.c | 4 ++-- - 14 files changed, 28 insertions(+), 28 deletions(-) - -diff --git a/lib/cleanup_user.c b/lib/cleanup_user.c -index a661f5e9..b3a5a66e 100644 ---- a/lib/cleanup_user.c -+++ b/lib/cleanup_user.c -@@ -117,7 +117,7 @@ void cleanup_unlock_passwd (MAYBE_UNUSED void *arg) - */ - void cleanup_unlock_shadow (MAYBE_UNUSED void *arg) - { -- if (spw_unlock () == 0) { -+ if (spw_unlock (true) == 0) { - fprintf (log_get_logfd(), - _("%s: failed to unlock %s\n"), - log_get_progname(), spw_dbname ()); -diff --git a/lib/lockpw.c b/lib/lockpw.c -index f73c434a..7c99d56e 100644 ---- a/lib/lockpw.c -+++ b/lib/lockpw.c -@@ -78,7 +78,7 @@ int ulckpwdf (void) - * Unlock both files. - */ - -- return (pw_unlock (true) && spw_unlock ())? 0 : -1; -+ return (pw_unlock (true) && spw_unlock (true))? 0 : -1; - } - #else - extern int ISO_C_forbids_an_empty_translation_unit; -diff --git a/lib/shadowio.c b/lib/shadowio.c -index 9866af6d..71089eda 100644 ---- a/lib/shadowio.c -+++ b/lib/shadowio.c -@@ -204,14 +204,14 @@ int spw_close (bool process_selinux) - return retval; - } - --int spw_unlock (void) -+int spw_unlock (bool process_selinux) - { - #ifdef WITH_TCB - int retval = 0; - - if (!getdef_bool ("USE_TCB")) { - #endif /* WITH_TCB */ -- return commonio_unlock (&shadow_db, true); -+ return commonio_unlock (&shadow_db, process_selinux); - #ifdef WITH_TCB - } - if (shadowtcb_drop_priv () == SHADOWTCB_FAILURE) { -diff --git a/lib/shadowio.h b/lib/shadowio.h -index e64d8d60..be645d0e 100644 ---- a/lib/shadowio.h -+++ b/lib/shadowio.h -@@ -23,7 +23,7 @@ extern /*@observer@*/ /*@null@*/const struct spwd *spw_next (void); - extern int spw_open (int mode); - extern int spw_remove (const char *name); - extern int spw_rewind (void); --extern int spw_unlock (void); -+extern int spw_unlock (bool process_selinux); - extern int spw_update (const struct spwd *sp); - extern int spw_sort (void); - -diff --git a/src/chage.c b/src/chage.c -index d3a42ccf..e77a46de 100644 ---- a/src/chage.c -+++ b/src/chage.c -@@ -94,7 +94,7 @@ static void - fail_exit (int code) - { - if (spw_locked) { -- if (spw_unlock () == 0) { -+ if (spw_unlock (true) == 0) { - fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, spw_dbname ()); - SYSLOG ((LOG_ERR, "failed to unlock %s", spw_dbname ())); - /* continue */ -@@ -562,7 +562,7 @@ static void close_files (void) - SYSLOG ((LOG_ERR, "failure while writing changes to %s", pw_dbname ())); - fail_exit (E_NOPERM); - } -- if (spw_unlock () == 0) { -+ if (spw_unlock (true) == 0) { - fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, spw_dbname ()); - SYSLOG ((LOG_ERR, "failed to unlock %s", spw_dbname ())); - /* continue */ -diff --git a/src/chpasswd.c b/src/chpasswd.c -index 5323c520..2656b039 100644 ---- a/src/chpasswd.c -+++ b/src/chpasswd.c -@@ -89,7 +89,7 @@ static void fail_exit (int code) - } - - if (spw_locked) { -- if (spw_unlock () == 0) { -+ if (spw_unlock (true) == 0) { - fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, spw_dbname ()); - SYSLOG ((LOG_ERR, "failed to unlock %s", spw_dbname ())); - /* continue */ -@@ -385,7 +385,7 @@ static void close_files (void) - SYSLOG ((LOG_ERR, "failure while writing changes to %s", spw_dbname ())); - fail_exit (1); - } -- if (spw_unlock () == 0) { -+ if (spw_unlock (true) == 0) { - fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, spw_dbname ()); - SYSLOG ((LOG_ERR, "failed to unlock %s", spw_dbname ())); - /* continue */ -diff --git a/src/newusers.c b/src/newusers.c -index 669b6236..4cbb1639 100644 ---- a/src/newusers.c -+++ b/src/newusers.c -@@ -165,7 +165,7 @@ static void usage (int status) - static void fail_exit (int code) - { - if (spw_locked) { -- if (spw_unlock () == 0) { -+ if (spw_unlock (true) == 0) { - fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, spw_dbname ()); - SYSLOG ((LOG_ERR, "failed to unlock %s", spw_dbname ())); - /* continue */ -@@ -959,7 +959,7 @@ static void close_files (void) - SYSLOG ((LOG_ERR, "failure while writing changes to %s", spw_dbname ())); - fail_exit (EXIT_FAILURE); - } -- if (spw_unlock () == 0) { -+ if (spw_unlock (true) == 0) { - fprintf (stderr, - _("%s: failed to unlock %s\n"), - Prog, spw_dbname ()); -diff --git a/src/passwd.c b/src/passwd.c -index 8a55d797..512dc572 100644 ---- a/src/passwd.c -+++ b/src/passwd.c -@@ -491,7 +491,7 @@ fail_exit (int status) - } - - if (spw_locked) { -- if (spw_unlock () == 0) { -+ if (spw_unlock (true) == 0) { - (void) fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, spw_dbname ()); - SYSLOG ((LOG_ERR, "failed to unlock %s", spw_dbname ())); - /* continue */ -@@ -627,7 +627,7 @@ static void update_shadow (void) - /* Try to update the password in /etc/passwd instead. */ - (void) spw_close (true); - update_noshadow (); -- if (spw_unlock () == 0) { -+ if (spw_unlock (true) == 0) { - (void) fprintf (stderr, - _("%s: failed to unlock %s\n"), - Prog, spw_dbname ()); -@@ -688,7 +688,7 @@ static void update_shadow (void) - SYSLOG ((LOG_ERR, "failure while writing changes to %s", spw_dbname ())); - fail_exit (E_FAILURE); - } -- if (spw_unlock () == 0) { -+ if (spw_unlock (true) == 0) { - (void) fprintf (stderr, - _("%s: failed to unlock %s\n"), - Prog, spw_dbname ()); -diff --git a/src/pwck.c b/src/pwck.c -index f30c5289..9c4cc29a 100644 ---- a/src/pwck.c -+++ b/src/pwck.c -@@ -85,7 +85,7 @@ extern int allow_bad_names; - static void fail_exit (int code) - { - if (spw_locked) { -- if (spw_unlock () == 0) { -+ if (spw_unlock (true) == 0) { - fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, spw_dbname ()); - if (use_system_spw_file) { - SYSLOG ((LOG_ERR, "failed to unlock %s", -@@ -338,7 +338,7 @@ static void close_files (bool changed) - * Don't be anti-social - unlock the files when you're done. - */ - if (spw_locked) { -- if (spw_unlock () == 0) { -+ if (spw_unlock (true) == 0) { - fprintf (stderr, - _("%s: failed to unlock %s\n"), - Prog, spw_dbname ()); -@@ -581,7 +581,7 @@ static void check_pw_file (bool *errors, bool *changed) - _("%s: cannot open %s\n"), - Prog, spw_dbname ()); - *errors = true; -- if (spw_unlock () == 0) { -+ if (spw_unlock (true) == 0) { - fprintf (stderr, - _("%s: failed to unlock %s\n"), - Prog, spw_dbname ()); -@@ -669,7 +669,7 @@ static void check_pw_file (bool *errors, bool *changed) - } else { - spw_opened = false; - } -- if (spw_unlock () == 0) { -+ if (spw_unlock (true) == 0) { - fprintf (stderr, - _("%s: failed to unlock %s\n"), - Prog, spw_dbname ()); -diff --git a/src/pwconv.c b/src/pwconv.c -index 82181bb4..b0d668b5 100644 ---- a/src/pwconv.c -+++ b/src/pwconv.c -@@ -91,7 +91,7 @@ static void fail_exit (int status) - } - - if (spw_locked) { -- if (spw_unlock () == 0) { -+ if (spw_unlock (true) == 0) { - fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, spw_dbname ()); - SYSLOG ((LOG_ERR, "failed to unlock %s", spw_dbname ())); - /* continue */ -@@ -305,7 +305,7 @@ int main (int argc, char **argv) - /* continue */ - } - -- if (spw_unlock () == 0) { -+ if (spw_unlock (true) == 0) { - fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, spw_dbname ()); - SYSLOG ((LOG_ERR, "failed to unlock %s", spw_dbname ())); - /* continue */ -diff --git a/src/pwunconv.c b/src/pwunconv.c -index 72944fe5..7a6d2be5 100644 ---- a/src/pwunconv.c -+++ b/src/pwunconv.c -@@ -47,7 +47,7 @@ static void process_flags (int argc, char **argv); - static void fail_exit (int status) - { - if (spw_locked) { -- if (spw_unlock () == 0) { -+ if (spw_unlock (true) == 0) { - fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, spw_dbname ()); - SYSLOG ((LOG_ERR, "failed to unlock %s", spw_dbname ())); - /* continue */ -@@ -222,7 +222,7 @@ int main (int argc, char **argv) - fail_exit (3); - } - -- if (spw_unlock () == 0) { -+ if (spw_unlock (true) == 0) { - fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, spw_dbname ()); - SYSLOG ((LOG_ERR, "failed to unlock %s", spw_dbname ())); - /* continue */ -diff --git a/src/useradd.c b/src/useradd.c -index aa070f2d..9c8b774f 100644 ---- a/src/useradd.c -+++ b/src/useradd.c -@@ -265,7 +265,7 @@ static void fail_exit (int code) - SYSLOG((LOG_ERR, "failed to remove %s", prefix_user_home)); - } - -- if (spw_locked && spw_unlock() == 0) { -+ if (spw_locked && spw_unlock(true) == 0) { - fprintf(stderr, _("%s: failed to unlock %s\n"), Prog, spw_dbname()); - SYSLOG((LOG_ERR, "failed to unlock %s", spw_dbname())); - /* continue */ -@@ -1593,7 +1593,7 @@ static void close_files (void) - } - #endif /* ENABLE_SUBIDS */ - if (is_shadow_pwd) { -- if (spw_unlock () == 0) { -+ if (spw_unlock (true) == 0) { - fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, spw_dbname ()); - SYSLOG ((LOG_ERR, "failed to unlock %s", spw_dbname ())); - #ifdef WITH_AUDIT -diff --git a/src/userdel.c b/src/userdel.c -index b4e87820..478c22d5 100644 ---- a/src/userdel.c -+++ b/src/userdel.c -@@ -409,7 +409,7 @@ static void close_files (void) - SYSLOG ((LOG_ERR, "failure while writing changes to %s", spw_dbname ())); - fail_exit (E_PW_UPDATE); - } -- if (spw_unlock () == 0) { -+ if (spw_unlock (true) == 0) { - fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, spw_dbname ()); - SYSLOG ((LOG_ERR, "failed to unlock %s", spw_dbname ())); - /* continue */ -@@ -498,7 +498,7 @@ static void fail_exit (int code) - } - } - if (spw_locked) { -- if (spw_unlock () == 0) { -+ if (spw_unlock (true) == 0) { - fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, spw_dbname ()); - SYSLOG ((LOG_ERR, "failed to unlock %s", spw_dbname ())); - /* continue */ -diff --git a/src/usermod.c b/src/usermod.c -index 6f761404..68373f35 100644 ---- a/src/usermod.c -+++ b/src/usermod.c -@@ -648,7 +648,7 @@ fail_exit (int code) - } - #endif - if (spw_locked) { -- if (spw_unlock () == 0) { -+ if (spw_unlock (true) == 0) { - fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, spw_dbname ()); - SYSLOG ((LOG_ERR, "failed to unlock %s", spw_dbname ())); - /* continue */ -@@ -1534,7 +1534,7 @@ static void close_files (void) - } - - if (is_shadow_pwd) { -- if (spw_unlock () == 0) { -+ if (spw_unlock (true) == 0) { - fprintf (stderr, - _("%s: failed to unlock %s\n"), - Prog, spw_dbname ()); --- -2.50.0 - - -From 68eb3395a4bcbbdb820b6c8c17cc64fb08749d11 Mon Sep 17 00:00:00 2001 -From: Iker Pedrosa -Date: Fri, 25 Apr 2025 16:00:43 +0200 -Subject: [PATCH 11/15] 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 ---- - lib/cleanup_group.c | 2 +- - lib/groupio.c | 4 ++-- - lib/groupio.h | 2 +- - src/chgpasswd.c | 4 ++-- - src/groupmems.c | 4 ++-- - src/grpck.c | 4 ++-- - src/grpconv.c | 4 ++-- - src/grpunconv.c | 4 ++-- - src/newusers.c | 4 ++-- - src/useradd.c | 4 ++-- - src/userdel.c | 4 ++-- - src/usermod.c | 4 ++-- - 12 files changed, 22 insertions(+), 22 deletions(-) - -diff --git a/lib/cleanup_group.c b/lib/cleanup_group.c -index 54480092..a5846152 100644 ---- a/lib/cleanup_group.c -+++ b/lib/cleanup_group.c -@@ -181,7 +181,7 @@ void cleanup_report_del_group_gshadow (void *group_name) - */ - void cleanup_unlock_group (MAYBE_UNUSED void *arg) - { -- if (gr_unlock () == 0) { -+ if (gr_unlock (true) == 0) { - fprintf (log_get_logfd(), - _("%s: failed to unlock %s\n"), - log_get_progname(), gr_dbname ()); -diff --git a/lib/groupio.c b/lib/groupio.c -index 6ad4ae69..f21aa618 100644 ---- a/lib/groupio.c -+++ b/lib/groupio.c -@@ -189,9 +189,9 @@ int gr_close (bool process_selinux) - return commonio_close (&group_db, process_selinux); - } - --int gr_unlock (void) -+int gr_unlock (bool process_selinux) - { -- return commonio_unlock (&group_db, true); -+ return commonio_unlock (&group_db, process_selinux); - } - - void __gr_set_changed (void) -diff --git a/lib/groupio.h b/lib/groupio.h -index ef9242d0..eae13cd3 100644 ---- a/lib/groupio.h -+++ b/lib/groupio.h -@@ -26,7 +26,7 @@ extern /*@observer@*/ /*@null@*/const struct group *gr_next (void); - extern int gr_open (int mode); - extern int gr_remove (const char *name); - extern int gr_rewind (void); --extern int gr_unlock (void); -+extern int gr_unlock (bool process_selinux); - extern int gr_update (const struct group *gr); - extern int gr_sort (void); - -diff --git a/src/chgpasswd.c b/src/chgpasswd.c -index 47402ecc..84553d81 100644 ---- a/src/chgpasswd.c -+++ b/src/chgpasswd.c -@@ -82,7 +82,7 @@ static void close_files (void); - static void fail_exit (int code) - { - if (gr_locked) { -- if (gr_unlock () == 0) { -+ if (gr_unlock (true) == 0) { - fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, gr_dbname ()); - SYSLOG ((LOG_ERR, "failed to unlock %s", gr_dbname ())); - /* continue */ -@@ -404,7 +404,7 @@ static void close_files (void) - SYSLOG ((LOG_ERR, "failure while writing changes to %s", gr_dbname ())); - fail_exit (1); - } -- if (gr_unlock () == 0) { -+ if (gr_unlock (true) == 0) { - fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, gr_dbname ()); - SYSLOG ((LOG_ERR, "failed to unlock %s", gr_dbname ())); - /* continue */ -diff --git a/src/groupmems.c b/src/groupmems.c -index 5976b074..0c59474f 100644 ---- a/src/groupmems.c -+++ b/src/groupmems.c -@@ -457,7 +457,7 @@ static void check_perms (void) - static void fail_exit (int code) - { - if (gr_locked) { -- if (gr_unlock () == 0) { -+ if (gr_unlock (true) == 0) { - fprintf (stderr, - _("%s: failed to unlock %s\n"), - Prog, gr_dbname ()); -@@ -528,7 +528,7 @@ static void close_files (void) - fail_exit (EXIT_GROUP_FILE); - } - if (gr_locked) { -- if (gr_unlock () == 0) { -+ if (gr_unlock (true) == 0) { - fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, gr_dbname ()); - SYSLOG ((LOG_ERR, "failed to unlock %s", gr_dbname ())); - /* continue */ -diff --git a/src/grpck.c b/src/grpck.c -index 872f882f..bd2014fc 100644 ---- a/src/grpck.c -+++ b/src/grpck.c -@@ -92,7 +92,7 @@ static void check_sgr_file (bool *errors, bool *changed); - static void fail_exit (int status) - { - if (gr_locked) { -- if (gr_unlock () == 0) { -+ if (gr_unlock (true) == 0) { - fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, gr_dbname ()); - SYSLOG ((LOG_ERR, "failed to unlock %s", gr_dbname ())); - /* continue */ -@@ -349,7 +349,7 @@ static void close_files (bool changed) - } - #endif - if (gr_locked) { -- if (gr_unlock () == 0) { -+ if (gr_unlock (true) == 0) { - fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, gr_dbname ()); - SYSLOG ((LOG_ERR, "failed to unlock %s", gr_dbname ())); - /* continue */ -diff --git a/src/grpconv.c b/src/grpconv.c -index d9c59fc9..3160b8b1 100644 ---- a/src/grpconv.c -+++ b/src/grpconv.c -@@ -56,7 +56,7 @@ static void process_flags (int argc, char **argv); - static void fail_exit (int status) - { - if (gr_locked) { -- if (gr_unlock () == 0) { -+ if (gr_unlock (true) == 0) { - fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, gr_dbname ()); - SYSLOG ((LOG_ERR, "failed to unlock %s", gr_dbname ())); - /* continue */ -@@ -253,7 +253,7 @@ int main (int argc, char **argv) - SYSLOG ((LOG_ERR, "failed to unlock %s", sgr_dbname ())); - /* continue */ - } -- if (gr_unlock () == 0) { -+ if (gr_unlock (true) == 0) { - fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, gr_dbname ()); - SYSLOG ((LOG_ERR, "failed to unlock %s", gr_dbname ())); - /* continue */ -diff --git a/src/grpunconv.c b/src/grpunconv.c -index 03fc824e..085c48ac 100644 ---- a/src/grpunconv.c -+++ b/src/grpunconv.c -@@ -55,7 +55,7 @@ static void process_flags (int argc, char **argv); - static void fail_exit (int status) - { - if (gr_locked) { -- if (gr_unlock () == 0) { -+ if (gr_unlock (true) == 0) { - fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, gr_dbname ()); - SYSLOG ((LOG_ERR, "failed to unlock %s", gr_dbname ())); - /* continue */ -@@ -209,7 +209,7 @@ int main (int argc, char **argv) - fail_exit (3); - } - -- if (gr_unlock () == 0) { -+ if (gr_unlock (true) == 0) { - fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, gr_dbname ()); - SYSLOG ((LOG_ERR, "failed to unlock %s", gr_dbname ())); - /* continue */ -diff --git a/src/newusers.c b/src/newusers.c -index 4cbb1639..47f01661 100644 ---- a/src/newusers.c -+++ b/src/newusers.c -@@ -179,7 +179,7 @@ static void fail_exit (int code) - } - } - if (gr_locked) { -- if (gr_unlock () == 0) { -+ if (gr_unlock (true) == 0) { - fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, gr_dbname ()); - SYSLOG ((LOG_ERR, "failed to unlock %s", gr_dbname ())); - /* continue */ -@@ -991,7 +991,7 @@ static void close_files (void) - } - #endif /* ENABLE_SUBIDS */ - -- if (gr_unlock () == 0) { -+ if (gr_unlock (true) == 0) { - fprintf (stderr, - _("%s: failed to unlock %s\n"), - Prog, gr_dbname ()); -diff --git a/src/useradd.c b/src/useradd.c -index 9c8b774f..75cfbde2 100644 ---- a/src/useradd.c -+++ b/src/useradd.c -@@ -275,7 +275,7 @@ static void fail_exit (int code) - SYSLOG((LOG_ERR, "failed to unlock %s", pw_dbname())); - /* continue */ - } -- if (gr_locked && gr_unlock() == 0) { -+ if (gr_locked && gr_unlock(true) == 0) { - fprintf(stderr, _("%s: failed to unlock %s\n"), Prog, gr_dbname()); - SYSLOG((LOG_ERR, "failed to unlock %s", gr_dbname())); - /* continue */ -@@ -1690,7 +1690,7 @@ static void close_group_files (void) - */ - static void unlock_group_files (void) - { -- if (gr_unlock () == 0) { -+ if (gr_unlock (true) == 0) { - fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, gr_dbname ()); - SYSLOG ((LOG_ERR, "failed to unlock %s", gr_dbname ())); - #ifdef WITH_AUDIT -diff --git a/src/userdel.c b/src/userdel.c -index 478c22d5..12696732 100644 ---- a/src/userdel.c -+++ b/src/userdel.c -@@ -422,7 +422,7 @@ static void close_files (void) - SYSLOG ((LOG_ERR, "failure while writing changes to %s", gr_dbname ())); - fail_exit (E_GRP_UPDATE); - } -- if (gr_unlock () == 0) { -+ if (gr_unlock (true) == 0) { - fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, gr_dbname ()); - SYSLOG ((LOG_ERR, "failed to unlock %s", gr_dbname ())); - /* continue */ -@@ -491,7 +491,7 @@ static void fail_exit (int code) - } - } - if (gr_locked) { -- if (gr_unlock () == 0) { -+ if (gr_unlock (true) == 0) { - fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, gr_dbname ()); - SYSLOG ((LOG_ERR, "failed to unlock %s", gr_dbname ())); - /* continue */ -diff --git a/src/usermod.c b/src/usermod.c -index 68373f35..2e21a70f 100644 ---- a/src/usermod.c -+++ b/src/usermod.c -@@ -632,7 +632,7 @@ static void - fail_exit (int code) - { - if (gr_locked) { -- if (gr_unlock () == 0) { -+ if (gr_unlock (true) == 0) { - fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, gr_dbname ()); - SYSLOG ((LOG_ERR, "failed to unlock %s", gr_dbname ())); - /* continue */ -@@ -1522,7 +1522,7 @@ static void close_files (void) - } - } - #endif -- if (gr_unlock () == 0) { -+ if (gr_unlock (true) == 0) { - fprintf (stderr, - _("%s: failed to unlock %s\n"), - Prog, gr_dbname ()); --- -2.50.0 - - -From 37b636524cfb7c9b7dce8c56484fb60795ef2b91 Mon Sep 17 00:00:00 2001 -From: Iker Pedrosa -Date: Fri, 25 Apr 2025 16:03:58 +0200 -Subject: [PATCH 12/15] 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 ---- - lib/cleanup_group.c | 2 +- - lib/sgroupio.c | 4 ++-- - lib/sgroupio.h | 2 +- - src/chgpasswd.c | 4 ++-- - src/groupmems.c | 4 ++-- - src/grpck.c | 4 ++-- - src/grpconv.c | 4 ++-- - src/grpunconv.c | 4 ++-- - src/newusers.c | 4 ++-- - src/useradd.c | 4 ++-- - src/userdel.c | 4 ++-- - src/usermod.c | 4 ++-- - 12 files changed, 22 insertions(+), 22 deletions(-) - -diff --git a/lib/cleanup_group.c b/lib/cleanup_group.c -index a5846152..dc9f1eb2 100644 ---- a/lib/cleanup_group.c -+++ b/lib/cleanup_group.c -@@ -201,7 +201,7 @@ void cleanup_unlock_group (MAYBE_UNUSED void *arg) - */ - void cleanup_unlock_gshadow (MAYBE_UNUSED void *arg) - { -- if (sgr_unlock () == 0) { -+ if (sgr_unlock (true) == 0) { - fprintf (log_get_logfd(), - _("%s: failed to unlock %s\n"), - log_get_progname(), sgr_dbname ()); -diff --git a/lib/sgroupio.c b/lib/sgroupio.c -index dbabcc36..4239e23d 100644 ---- a/lib/sgroupio.c -+++ b/lib/sgroupio.c -@@ -281,9 +281,9 @@ int sgr_close (bool process_selinux) - return commonio_close (&gshadow_db, process_selinux); - } - --int sgr_unlock (void) -+int sgr_unlock (bool process_selinux) - { -- return commonio_unlock (&gshadow_db, true); -+ return commonio_unlock (&gshadow_db, process_selinux); - } - - void __sgr_set_changed (void) -diff --git a/lib/sgroupio.h b/lib/sgroupio.h -index 0e25fc0b..256f6d54 100644 ---- a/lib/sgroupio.h -+++ b/lib/sgroupio.h -@@ -22,7 +22,7 @@ extern /*@null@*/const struct sgrp *sgr_next (void); - extern int sgr_open (int mode); - extern int sgr_remove (const char *name); - extern int sgr_rewind (void); --extern int sgr_unlock (void); -+extern int sgr_unlock (bool process_selinux); - extern int sgr_update (const struct sgrp *sg); - extern int sgr_sort (void); - -diff --git a/src/chgpasswd.c b/src/chgpasswd.c -index 84553d81..27094f50 100644 ---- a/src/chgpasswd.c -+++ b/src/chgpasswd.c -@@ -91,7 +91,7 @@ static void fail_exit (int code) - - #ifdef SHADOWGRP - if (sgr_locked) { -- if (sgr_unlock () == 0) { -+ if (sgr_unlock (true) == 0) { - fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, sgr_dbname ()); - SYSLOG ((LOG_ERR, "failed to unlock %s", sgr_dbname ())); - /* continue */ -@@ -388,7 +388,7 @@ static void close_files (void) - SYSLOG ((LOG_ERR, "failure while writing changes to %s", sgr_dbname ())); - fail_exit (1); - } -- if (sgr_unlock () == 0) { -+ if (sgr_unlock (true) == 0) { - fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, sgr_dbname ()); - SYSLOG ((LOG_ERR, "failed to unlock %s", sgr_dbname ())); - /* continue */ -diff --git a/src/groupmems.c b/src/groupmems.c -index 0c59474f..f39724c8 100644 ---- a/src/groupmems.c -+++ b/src/groupmems.c -@@ -468,7 +468,7 @@ static void fail_exit (int code) - - #ifdef SHADOWGRP - if (sgr_locked) { -- if (sgr_unlock () == 0) { -+ if (sgr_unlock (true) == 0) { - fprintf (stderr, - _("%s: failed to unlock %s\n"), - Prog, sgr_dbname ()); -@@ -544,7 +544,7 @@ static void close_files (void) - fail_exit (EXIT_GROUP_FILE); - } - if (sgr_locked) { -- if (sgr_unlock () == 0) { -+ if (sgr_unlock (true) == 0) { - fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, sgr_dbname ()); - SYSLOG ((LOG_ERR, "failed to unlock %s", sgr_dbname ())); - /* continue */ -diff --git a/src/grpck.c b/src/grpck.c -index bd2014fc..bc178d52 100644 ---- a/src/grpck.c -+++ b/src/grpck.c -@@ -101,7 +101,7 @@ static void fail_exit (int status) - - #ifdef SHADOWGRP - if (sgr_locked) { -- if (sgr_unlock () == 0) { -+ if (sgr_unlock (true) == 0) { - fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, sgr_dbname ()); - SYSLOG ((LOG_ERR, "failed to unlock %s", sgr_dbname ())); - /* continue */ -@@ -340,7 +340,7 @@ static void close_files (bool changed) - */ - #ifdef SHADOWGRP - if (sgr_locked) { -- if (sgr_unlock () == 0) { -+ if (sgr_unlock (true) == 0) { - fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, sgr_dbname ()); - SYSLOG ((LOG_ERR, "failed to unlock %s", sgr_dbname ())); - /* continue */ -diff --git a/src/grpconv.c b/src/grpconv.c -index 3160b8b1..5958d3f5 100644 ---- a/src/grpconv.c -+++ b/src/grpconv.c -@@ -64,7 +64,7 @@ static void fail_exit (int status) - } - - if (sgr_locked) { -- if (sgr_unlock () == 0) { -+ if (sgr_unlock (true) == 0) { - fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, sgr_dbname ()); - SYSLOG ((LOG_ERR, "failed to unlock %s", sgr_dbname ())); - /* continue */ -@@ -248,7 +248,7 @@ int main (int argc, char **argv) - SYSLOG ((LOG_ERR, "failure while writing changes to %s", gr_dbname ())); - fail_exit (3); - } -- if (sgr_unlock () == 0) { -+ if (sgr_unlock (true) == 0) { - fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, sgr_dbname ()); - SYSLOG ((LOG_ERR, "failed to unlock %s", sgr_dbname ())); - /* continue */ -diff --git a/src/grpunconv.c b/src/grpunconv.c -index 085c48ac..5256e0bc 100644 ---- a/src/grpunconv.c -+++ b/src/grpunconv.c -@@ -63,7 +63,7 @@ static void fail_exit (int status) - } - - if (sgr_locked) { -- if (sgr_unlock () == 0) { -+ if (sgr_unlock (true) == 0) { - fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, sgr_dbname ()); - SYSLOG ((LOG_ERR, "failed to unlock %s", sgr_dbname ())); - /* continue */ -@@ -215,7 +215,7 @@ int main (int argc, char **argv) - /* continue */ - } - -- if (sgr_unlock () == 0) { -+ if (sgr_unlock (true) == 0) { - fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, sgr_dbname ()); - SYSLOG ((LOG_ERR, "failed to unlock %s", sgr_dbname ())); - /* continue */ -diff --git a/src/newusers.c b/src/newusers.c -index 47f01661..261a5911 100644 ---- a/src/newusers.c -+++ b/src/newusers.c -@@ -187,7 +187,7 @@ static void fail_exit (int code) - } - #ifdef SHADOWGRP - if (sgr_locked) { -- if (sgr_unlock () == 0) { -+ if (sgr_unlock (true) == 0) { - fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, sgr_dbname ()); - SYSLOG ((LOG_ERR, "failed to unlock %s", sgr_dbname ())); - /* continue */ -@@ -1009,7 +1009,7 @@ static void close_files (void) - SYSLOG ((LOG_ERR, "failure while writing changes to %s", sgr_dbname ())); - fail_exit (EXIT_FAILURE); - } -- if (sgr_unlock () == 0) { -+ if (sgr_unlock (true) == 0) { - fprintf (stderr, - _("%s: failed to unlock %s\n"), - Prog, sgr_dbname ()); -diff --git a/src/useradd.c b/src/useradd.c -index 75cfbde2..cb7ac88e 100644 ---- a/src/useradd.c -+++ b/src/useradd.c -@@ -281,7 +281,7 @@ static void fail_exit (int code) - /* continue */ - } - #ifdef SHADOWGRP -- if (sgr_locked && sgr_unlock() == 0) { -+ if (sgr_locked && sgr_unlock(true) == 0) { - fprintf(stderr, _("%s: failed to unlock %s\n"), Prog, sgr_dbname()); - SYSLOG((LOG_ERR, "failed to unlock %s", sgr_dbname())); - /* continue */ -@@ -1704,7 +1704,7 @@ static void unlock_group_files (void) - gr_locked = false; - #ifdef SHADOWGRP - if (is_shadow_grp) { -- if (sgr_unlock () == 0) { -+ if (sgr_unlock (true) == 0) { - fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, sgr_dbname ()); - SYSLOG ((LOG_ERR, "failed to unlock %s", sgr_dbname ())); - #ifdef WITH_AUDIT -diff --git a/src/userdel.c b/src/userdel.c -index 12696732..80ed1a48 100644 ---- a/src/userdel.c -+++ b/src/userdel.c -@@ -438,7 +438,7 @@ static void close_files (void) - fail_exit (E_GRP_UPDATE); - } - -- if (sgr_unlock () == 0) { -+ if (sgr_unlock (true) == 0) { - fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, sgr_dbname ()); - SYSLOG ((LOG_ERR, "failed to unlock %s", sgr_dbname ())); - /* continue */ -@@ -506,7 +506,7 @@ static void fail_exit (int code) - } - #ifdef SHADOWGRP - if (sgr_locked) { -- if (sgr_unlock () == 0) { -+ if (sgr_unlock (true) == 0) { - fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, sgr_dbname ()); - SYSLOG ((LOG_ERR, "failed to unlock %s", sgr_dbname ())); - /* continue */ -diff --git a/src/usermod.c b/src/usermod.c -index 2e21a70f..69ea8e55 100644 ---- a/src/usermod.c -+++ b/src/usermod.c -@@ -640,7 +640,7 @@ fail_exit (int code) - } - #ifdef SHADOWGRP - if (sgr_locked) { -- if (sgr_unlock () == 0) { -+ if (sgr_unlock (true) == 0) { - fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, sgr_dbname ()); - SYSLOG ((LOG_ERR, "failed to unlock %s", sgr_dbname ())); - /* continue */ -@@ -1511,7 +1511,7 @@ static void close_files (void) - #endif - #ifdef SHADOWGRP - if (is_shadow_grp) { -- if (sgr_unlock () == 0) { -+ if (sgr_unlock (true) == 0) { - fprintf (stderr, - _("%s: failed to unlock %s\n"), - Prog, sgr_dbname ()); --- -2.50.0 - - -From dee1a5a5782b8aafcc32c7f93a53ce591355d1b3 Mon Sep 17 00:00:00 2001 -From: Iker Pedrosa -Date: Fri, 25 Apr 2025 16:06:57 +0200 -Subject: [PATCH 13/15] 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 ---- - src/vipw.c | 10 +++++----- - 1 file changed, 5 insertions(+), 5 deletions(-) - -diff --git a/src/vipw.c b/src/vipw.c -index 3c840e9f..ba323e0e 100644 ---- a/src/vipw.c -+++ b/src/vipw.c -@@ -62,7 +62,7 @@ static const char *Prog; - static const char *filename, *fileeditname; - static bool filelocked = false; - static bool createedit = false; --static int (*unlock) (void); -+static int (*unlock) (bool); - static bool quiet = false; - #ifdef WITH_TCB - static const char *user = NULL; -@@ -74,7 +74,7 @@ static bool tcb_mode = false; - static void usage (int status); - static int create_backup_file (FILE *, const char *, struct stat *); - static void vipwexit (const char *msg, int syserr, int ret); --static void vipwedit (const char *, int (*)(void), int (*)(void)); -+static void vipwedit (const char *, int (*)(void), int (*)(bool)); - - /* - * usage - display usage message and exit -@@ -164,7 +164,7 @@ static void vipwexit (const char *msg, int syserr, int ret) - } - } - if (filelocked) { -- if ((*unlock) () == 0) { -+ if ((*unlock) (true) == 0) { - fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, fileeditname); - SYSLOG ((LOG_ERR, "failed to unlock %s", fileeditname)); - /* continue */ -@@ -195,7 +195,7 @@ static void vipwexit (const char *msg, int syserr, int ret) - * - */ - static void --vipwedit (const char *file, int (*file_lock) (void), int (*file_unlock) (void)) -+vipwedit (const char *file, int (*file_lock) (void), int (*file_unlock) (bool)) - { - int status; - char *to_rename; -@@ -467,7 +467,7 @@ vipwedit (const char *file, int (*file_lock) (void), int (*file_unlock) (void)) - } - #endif /* WITH_TCB */ - -- if ((*file_unlock) () == 0) { -+ if ((*file_unlock) (true) == 0) { - fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, fileeditname); - SYSLOG ((LOG_ERR, "failed to unlock %s", fileeditname)); - /* continue */ --- -2.50.0 - - -From f297173f0247b9288c424e765e972eb13d4c9ded Mon Sep 17 00:00:00 2001 -From: Iker Pedrosa -Date: Fri, 25 Apr 2025 16:10:09 +0200 -Subject: [PATCH 14/15] 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 ---- - lib/subordinateio.c | 12 ++++++------ - lib/subordinateio.h | 2 +- - src/newusers.c | 4 ++-- - src/useradd.c | 4 ++-- - src/userdel.c | 4 ++-- - src/usermod.c | 4 ++-- - 6 files changed, 15 insertions(+), 15 deletions(-) - -diff --git a/lib/subordinateio.c b/lib/subordinateio.c -index 9587e1a9..eaa2dc99 100644 ---- a/lib/subordinateio.c -+++ b/lib/subordinateio.c -@@ -673,9 +673,9 @@ int sub_uid_close (bool process_selinux) - return commonio_close (&subordinate_uid_db, process_selinux); - } - --int sub_uid_unlock (void) -+int sub_uid_unlock (bool process_selinux) - { -- return commonio_unlock (&subordinate_uid_db, true); -+ return commonio_unlock (&subordinate_uid_db, process_selinux); - } - - uid_t sub_uid_find_free_range(uid_t min, uid_t max, unsigned long count) -@@ -1022,7 +1022,7 @@ bool new_subid_range(struct subordinate_range *range, enum subid_type id_type, b - } - if (!sub_uid_open(O_CREAT | O_RDWR)) { - printf("Failed opening subuids (errno %d)\n", errno); -- sub_uid_unlock(); -+ sub_uid_unlock(true); - return false; - } - db = &subordinate_uid_db; -@@ -1069,7 +1069,7 @@ bool new_subid_range(struct subordinate_range *range, enum subid_type id_type, b - out: - if (id_type == ID_TYPE_UID) { - sub_uid_close(true); -- sub_uid_unlock(); -+ sub_uid_unlock(true); - } else { - sub_gid_close(true); - sub_gid_unlock(); -@@ -1094,7 +1094,7 @@ bool release_subid_range(struct subordinate_range *range, enum subid_type id_typ - } - if (!sub_uid_open(O_CREAT | O_RDWR)) { - printf("Failed opening subuids (errno %d)\n", errno); -- sub_uid_unlock(); -+ sub_uid_unlock(true); - return false; - } - db = &subordinate_uid_db; -@@ -1119,7 +1119,7 @@ bool release_subid_range(struct subordinate_range *range, enum subid_type id_typ - - if (id_type == ID_TYPE_UID) { - sub_uid_close(true); -- sub_uid_unlock(); -+ sub_uid_unlock(true); - } else { - sub_gid_close(true); - sub_gid_unlock(); -diff --git a/lib/subordinateio.h b/lib/subordinateio.h -index 63f5b1fb..7914613d 100644 ---- a/lib/subordinateio.h -+++ b/lib/subordinateio.h -@@ -21,7 +21,7 @@ extern int sub_uid_lock (void); - extern int sub_uid_setdbname (const char *filename); - extern /*@observer@*/const char *sub_uid_dbname (void); - extern int sub_uid_open (int mode); --extern int sub_uid_unlock (void); -+extern int sub_uid_unlock (bool process_selinux); - extern int sub_uid_add (const char *owner, uid_t start, unsigned long count); - extern int sub_uid_remove (const char *owner, uid_t start, unsigned long count); - extern uid_t sub_uid_find_free_range(uid_t min, uid_t max, unsigned long count); -diff --git a/src/newusers.c b/src/newusers.c -index 261a5911..65bedf9b 100644 ---- a/src/newusers.c -+++ b/src/newusers.c -@@ -196,7 +196,7 @@ static void fail_exit (int code) - #endif - #ifdef ENABLE_SUBIDS - if (sub_uid_locked) { -- if (sub_uid_unlock () == 0) { -+ if (sub_uid_unlock (true) == 0) { - fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, sub_uid_dbname ()); - SYSLOG ((LOG_ERR, "failed to unlock %s", sub_uid_dbname ())); - /* continue */ -@@ -1021,7 +1021,7 @@ static void close_files (void) - #endif - #ifdef ENABLE_SUBIDS - if (is_sub_uid) { -- if (sub_uid_unlock () == 0) { -+ if (sub_uid_unlock (true) == 0) { - fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, sub_uid_dbname ()); - SYSLOG ((LOG_ERR, "failed to unlock %s", sub_uid_dbname ())); - /* continue */ -diff --git a/src/useradd.c b/src/useradd.c -index cb7ac88e..f65d24d1 100644 ---- a/src/useradd.c -+++ b/src/useradd.c -@@ -288,7 +288,7 @@ static void fail_exit (int code) - } - #endif - #ifdef ENABLE_SUBIDS -- if (sub_uid_locked && sub_uid_unlock() == 0) { -+ if (sub_uid_locked && sub_uid_unlock(true) == 0) { - fprintf(stderr, _("%s: failed to unlock %s\n"), Prog, sub_uid_dbname()); - SYSLOG((LOG_ERR, "failed to unlock %s", sub_uid_dbname())); - /* continue */ -@@ -1623,7 +1623,7 @@ static void close_files (void) - - #ifdef ENABLE_SUBIDS - if (is_sub_uid) { -- if (sub_uid_unlock () == 0) { -+ if (sub_uid_unlock (true) == 0) { - fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, sub_uid_dbname ()); - SYSLOG ((LOG_ERR, "failed to unlock %s", sub_uid_dbname ())); - #ifdef WITH_AUDIT -diff --git a/src/userdel.c b/src/userdel.c -index 80ed1a48..68e53b85 100644 ---- a/src/userdel.c -+++ b/src/userdel.c -@@ -454,7 +454,7 @@ static void close_files (void) - SYSLOG ((LOG_ERR, "failure while writing changes to %s", sub_uid_dbname ())); - fail_exit (E_SUB_UID_UPDATE); - } -- if (sub_uid_unlock () == 0) { -+ if (sub_uid_unlock (true) == 0) { - fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, sub_uid_dbname ()); - SYSLOG ((LOG_ERR, "failed to unlock %s", sub_uid_dbname ())); - /* continue */ -@@ -515,7 +515,7 @@ static void fail_exit (int code) - #endif /* SHADOWGRP */ - #ifdef ENABLE_SUBIDS - if (sub_uid_locked) { -- if (sub_uid_unlock () == 0) { -+ if (sub_uid_unlock (true) == 0) { - fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, sub_uid_dbname ()); - SYSLOG ((LOG_ERR, "failed to unlock %s", sub_uid_dbname ())); - /* continue */ -diff --git a/src/usermod.c b/src/usermod.c -index 69ea8e55..fc6cc51c 100644 ---- a/src/usermod.c -+++ b/src/usermod.c -@@ -663,7 +663,7 @@ fail_exit (int code) - } - #ifdef ENABLE_SUBIDS - if (sub_uid_locked) { -- if (sub_uid_unlock () == 0) { -+ if (sub_uid_unlock (true) == 0) { - fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, sub_uid_dbname ()); - SYSLOG ((LOG_ERR, "failed to unlock %s", sub_uid_dbname ())); - /* continue */ -@@ -1566,7 +1566,7 @@ static void close_files (void) - SYSLOG ((LOG_ERR, "failure while writing changes to %s", sub_uid_dbname ())); - fail_exit (E_SUB_UID_UPDATE); - } -- if (sub_uid_unlock () == 0) { -+ if (sub_uid_unlock (true) == 0) { - fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, sub_uid_dbname ()); - SYSLOG ((LOG_ERR, "failed to unlock %s", sub_uid_dbname ())); - /* continue */ --- -2.50.0 - - -From 7b2bbdd0347c34bc5e094caf26c55694fb5935e7 Mon Sep 17 00:00:00 2001 -From: Iker Pedrosa -Date: Fri, 25 Apr 2025 16:12:40 +0200 -Subject: [PATCH 15/15] 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 ---- - lib/subordinateio.c | 12 ++++++------ - lib/subordinateio.h | 2 +- - src/newusers.c | 4 ++-- - src/useradd.c | 4 ++-- - src/userdel.c | 4 ++-- - src/usermod.c | 4 ++-- - 6 files changed, 15 insertions(+), 15 deletions(-) - -diff --git a/lib/subordinateio.c b/lib/subordinateio.c -index eaa2dc99..d1ab03f3 100644 ---- a/lib/subordinateio.c -+++ b/lib/subordinateio.c -@@ -780,9 +780,9 @@ int sub_gid_close (bool process_selinux) - return commonio_close (&subordinate_gid_db, process_selinux); - } - --int sub_gid_unlock (void) -+int sub_gid_unlock (bool process_selinux) - { -- return commonio_unlock (&subordinate_gid_db, true); -+ return commonio_unlock (&subordinate_gid_db, process_selinux); - } - - gid_t sub_gid_find_free_range(gid_t min, gid_t max, unsigned long count) -@@ -1034,7 +1034,7 @@ bool new_subid_range(struct subordinate_range *range, enum subid_type id_type, b - } - if (!sub_gid_open(O_CREAT | O_RDWR)) { - printf("Failed opening subgids (errno %d)\n", errno); -- sub_gid_unlock(); -+ sub_gid_unlock(true); - return false; - } - db = &subordinate_gid_db; -@@ -1072,7 +1072,7 @@ out: - sub_uid_unlock(true); - } else { - sub_gid_close(true); -- sub_gid_unlock(); -+ sub_gid_unlock(true); - } - - return ret; -@@ -1106,7 +1106,7 @@ bool release_subid_range(struct subordinate_range *range, enum subid_type id_typ - } - if (!sub_gid_open(O_CREAT | O_RDWR)) { - printf("Failed opening subgids (errno %d)\n", errno); -- sub_gid_unlock(); -+ sub_gid_unlock(true); - return false; - } - db = &subordinate_gid_db; -@@ -1122,7 +1122,7 @@ bool release_subid_range(struct subordinate_range *range, enum subid_type id_typ - sub_uid_unlock(true); - } else { - sub_gid_close(true); -- sub_gid_unlock(); -+ sub_gid_unlock(true); - } - - return ret; -diff --git a/lib/subordinateio.h b/lib/subordinateio.h -index 7914613d..1ca4b66d 100644 ---- a/lib/subordinateio.h -+++ b/lib/subordinateio.h -@@ -39,7 +39,7 @@ extern int sub_gid_lock (void); - extern int sub_gid_setdbname (const char *filename); - extern /*@observer@*/const char *sub_gid_dbname (void); - extern int sub_gid_open (int mode); --extern int sub_gid_unlock (void); -+extern int sub_gid_unlock (bool process_selinux); - extern int sub_gid_add (const char *owner, gid_t start, unsigned long count); - extern int sub_gid_remove (const char *owner, gid_t start, unsigned long count); - extern uid_t sub_gid_find_free_range(gid_t min, gid_t max, unsigned long count); -diff --git a/src/newusers.c b/src/newusers.c -index 65bedf9b..4eb6065c 100644 ---- a/src/newusers.c -+++ b/src/newusers.c -@@ -203,7 +203,7 @@ static void fail_exit (int code) - } - } - if (sub_gid_locked) { -- if (sub_gid_unlock () == 0) { -+ if (sub_gid_unlock (true) == 0) { - fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, sub_gid_dbname ()); - SYSLOG ((LOG_ERR, "failed to unlock %s", sub_gid_dbname ())); - /* continue */ -@@ -1029,7 +1029,7 @@ static void close_files (void) - sub_uid_locked = false; - } - if (is_sub_gid) { -- if (sub_gid_unlock () == 0) { -+ if (sub_gid_unlock (true) == 0) { - fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, sub_gid_dbname ()); - SYSLOG ((LOG_ERR, "failed to unlock %s", sub_gid_dbname ())); - /* continue */ -diff --git a/src/useradd.c b/src/useradd.c -index f65d24d1..98c2d284 100644 ---- a/src/useradd.c -+++ b/src/useradd.c -@@ -293,7 +293,7 @@ static void fail_exit (int code) - SYSLOG((LOG_ERR, "failed to unlock %s", sub_uid_dbname())); - /* continue */ - } -- if (sub_gid_locked && sub_gid_unlock() == 0) { -+ if (sub_gid_locked && sub_gid_unlock(true) == 0) { - fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, sub_gid_dbname()); - SYSLOG ((LOG_ERR, "failed to unlock %s", sub_gid_dbname())); - /* continue */ -@@ -1637,7 +1637,7 @@ static void close_files (void) - sub_uid_locked = false; - } - if (is_sub_gid) { -- if (sub_gid_unlock () == 0) { -+ if (sub_gid_unlock (true) == 0) { - fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, sub_gid_dbname ()); - SYSLOG ((LOG_ERR, "failed to unlock %s", sub_gid_dbname ())); - #ifdef WITH_AUDIT -diff --git a/src/userdel.c b/src/userdel.c -index 68e53b85..524c96a6 100644 ---- a/src/userdel.c -+++ b/src/userdel.c -@@ -468,7 +468,7 @@ static void close_files (void) - SYSLOG ((LOG_ERR, "failure while writing changes to %s", sub_gid_dbname ())); - fail_exit (E_SUB_GID_UPDATE); - } -- if (sub_gid_unlock () == 0) { -+ if (sub_gid_unlock (true) == 0) { - fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, sub_gid_dbname ()); - SYSLOG ((LOG_ERR, "failed to unlock %s", sub_gid_dbname ())); - /* continue */ -@@ -522,7 +522,7 @@ static void fail_exit (int code) - } - } - if (sub_gid_locked) { -- if (sub_gid_unlock () == 0) { -+ if (sub_gid_unlock (true) == 0) { - fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, sub_gid_dbname ()); - SYSLOG ((LOG_ERR, "failed to unlock %s", sub_gid_dbname ())); - /* continue */ -diff --git a/src/usermod.c b/src/usermod.c -index fc6cc51c..68000a0e 100644 ---- a/src/usermod.c -+++ b/src/usermod.c -@@ -670,7 +670,7 @@ fail_exit (int code) - } - } - if (sub_gid_locked) { -- if (sub_gid_unlock () == 0) { -+ if (sub_gid_unlock (true) == 0) { - fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, sub_gid_dbname ()); - SYSLOG ((LOG_ERR, "failed to unlock %s", sub_gid_dbname ())); - /* continue */ -@@ -1579,7 +1579,7 @@ static void close_files (void) - SYSLOG ((LOG_ERR, "failure while writing changes to %s", sub_gid_dbname ())); - fail_exit (E_SUB_GID_UPDATE); - } -- if (sub_gid_unlock () == 0) { -+ if (sub_gid_unlock (true) == 0) { - fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, sub_gid_dbname ()); - SYSLOG ((LOG_ERR, "failed to unlock %s", sub_gid_dbname ())); - /* continue */ --- -2.50.0 - -diff -up shadow-4.18.0/src/newusers.c.test-chroot2 shadow-4.18.0/src/newusers.c ---- shadow-4.18.0/src/newusers.c.test-chroot2 2025-07-08 11:22:16.257291774 +0200 -+++ shadow-4.18.0/src/newusers.c 2025-07-08 11:23:28.210122190 +0200 -@@ -60,6 +60,10 @@ - #include "string/strtok/stpsep.h" - #include "string/strtok/strsep2arr.h" - -+struct option_flags { -+ bool chroot; -+}; -+ - - /* - * Global variables -@@ -103,7 +107,7 @@ static bool sub_gid_locked = false; - - /* local function prototypes */ - NORETURN static void usage (int status); --NORETURN static void fail_exit (int); -+NORETURN static void fail_exit (int, bool); - static int add_group (const char *, const char *, gid_t *, gid_t); - static int get_user_id (const char *, uid_t *); - static int add_user (const char *, uid_t, gid_t); -@@ -113,11 +117,11 @@ static int update_passwd (struct passwd - #endif /* !USE_PAM */ - #endif /* !ACCT_TOOLS_SETUID */ - static int add_passwd (struct passwd *, const char *); --static void process_flags (int argc, char **argv); -+static void process_flags (int argc, char **argv, struct option_flags *flags); - static void check_flags (void); - static void check_perms (void); --static void open_files (void); --static void close_files (void); -+static void open_files (bool process_selinux); -+static void close_files (struct option_flags *flags); - - extern int allow_bad_names; - -@@ -170,24 +174,24 @@ static void usage (int status) - /* - * fail_exit - undo as much as possible - */ --static void fail_exit (int code) -+static void fail_exit (int code, bool process_selinux) - { - if (spw_locked) { -- if (spw_unlock (true) == 0) { -+ if (spw_unlock (process_selinux) == 0) { - fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, spw_dbname ()); - SYSLOG ((LOG_ERR, "failed to unlock %s", spw_dbname ())); - /* continue */ - } - } - if (pw_locked) { -- if (pw_unlock (true) == 0) { -+ if (pw_unlock (process_selinux) == 0) { - fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, pw_dbname ()); - SYSLOG ((LOG_ERR, "failed to unlock %s", pw_dbname ())); - /* continue */ - } - } - if (gr_locked) { -- if (gr_unlock (true) == 0) { -+ if (gr_unlock (process_selinux) == 0) { - fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, gr_dbname ()); - SYSLOG ((LOG_ERR, "failed to unlock %s", gr_dbname ())); - /* continue */ -@@ -195,7 +199,7 @@ static void fail_exit (int code) - } - #ifdef SHADOWGRP - if (sgr_locked) { -- if (sgr_unlock (true) == 0) { -+ if (sgr_unlock (process_selinux) == 0) { - fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, sgr_dbname ()); - SYSLOG ((LOG_ERR, "failed to unlock %s", sgr_dbname ())); - /* continue */ -@@ -204,14 +208,14 @@ static void fail_exit (int code) - #endif - #ifdef ENABLE_SUBIDS - if (sub_uid_locked) { -- if (sub_uid_unlock (true) == 0) { -+ if (sub_uid_unlock (process_selinux) == 0) { - fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, sub_uid_dbname ()); - SYSLOG ((LOG_ERR, "failed to unlock %s", sub_uid_dbname ())); - /* continue */ - } - } - if (sub_gid_locked) { -- if (sub_gid_unlock (true) == 0) { -+ if (sub_gid_unlock (process_selinux) == 0) { - fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, sub_gid_dbname ()); - SYSLOG ((LOG_ERR, "failed to unlock %s", sub_gid_dbname ())); - /* continue */ -@@ -637,7 +641,7 @@ static int add_passwd (struct passwd *pw - * - * It will not return if an error is encountered. - */ --static void process_flags (int argc, char **argv) -+static void process_flags (int argc, char **argv, struct option_flags *flags) - { - int c; - #ifndef ACCT_TOOLS_SETUID -@@ -696,6 +700,7 @@ static void process_flags (int argc, cha - rflg = true; - break; - case 'R': /* no-op, handled in process_root_flag () */ -+ flags->chroot = true; - break; - #ifndef ACCT_TOOLS_SETUID - #ifndef USE_PAM -@@ -755,7 +760,7 @@ static void process_flags (int argc, cha - - SNPRINTF(buf, "%s: %s", Prog, argv[1]); - perror (buf); -- fail_exit (EXIT_FAILURE); -+ fail_exit (EXIT_FAILURE, !flags->chroot); - } - } - -@@ -859,7 +864,7 @@ static void check_perms (void) - /* - * open_files - lock and open the password, group and shadow databases - */ --static void open_files (void) -+static void open_files (bool process_selinux) - { - /* - * Lock the password files and open them for update. This will bring -@@ -871,7 +876,7 @@ static void open_files (void) - fprintf (stderr, - _("%s: cannot lock %s; try again later.\n"), - Prog, pw_dbname ()); -- fail_exit (EXIT_FAILURE); -+ fail_exit (EXIT_FAILURE, process_selinux); - } - pw_locked = true; - if (is_shadow) { -@@ -879,7 +884,7 @@ static void open_files (void) - fprintf (stderr, - _("%s: cannot lock %s; try again later.\n"), - Prog, spw_dbname ()); -- fail_exit (EXIT_FAILURE); -+ fail_exit (EXIT_FAILURE, process_selinux); - } - spw_locked = true; - } -@@ -887,7 +892,7 @@ static void open_files (void) - fprintf (stderr, - _("%s: cannot lock %s; try again later.\n"), - Prog, gr_dbname ()); -- fail_exit (EXIT_FAILURE); -+ fail_exit (EXIT_FAILURE, process_selinux); - } - gr_locked = true; - #ifdef SHADOWGRP -@@ -896,7 +901,7 @@ static void open_files (void) - fprintf (stderr, - _("%s: cannot lock %s; try again later.\n"), - Prog, sgr_dbname ()); -- fail_exit (EXIT_FAILURE); -+ fail_exit (EXIT_FAILURE, process_selinux); - } - sgr_locked = true; - } -@@ -907,7 +912,7 @@ static void open_files (void) - fprintf (stderr, - _("%s: cannot lock %s; try again later.\n"), - Prog, sub_uid_dbname ()); -- fail_exit (EXIT_FAILURE); -+ fail_exit (EXIT_FAILURE, process_selinux); - } - sub_uid_locked = true; - } -@@ -916,7 +921,7 @@ static void open_files (void) - fprintf (stderr, - _("%s: cannot lock %s; try again later.\n"), - Prog, sub_gid_dbname ()); -- fail_exit (EXIT_FAILURE); -+ fail_exit (EXIT_FAILURE, process_selinux); - } - sub_gid_locked = true; - } -@@ -924,20 +929,20 @@ static void open_files (void) - - if (pw_open (O_CREAT | O_RDWR) == 0) { - fprintf (stderr, _("%s: cannot open %s\n"), Prog, pw_dbname ()); -- fail_exit (EXIT_FAILURE); -+ fail_exit (EXIT_FAILURE, process_selinux); - } - if (is_shadow && (spw_open (O_CREAT | O_RDWR) == 0)) { - fprintf (stderr, _("%s: cannot open %s\n"), Prog, spw_dbname ()); -- fail_exit (EXIT_FAILURE); -+ fail_exit (EXIT_FAILURE, process_selinux); - } - if (gr_open (O_CREAT | O_RDWR) == 0) { - fprintf (stderr, _("%s: cannot open %s\n"), Prog, gr_dbname ()); -- fail_exit (EXIT_FAILURE); -+ fail_exit (EXIT_FAILURE, process_selinux); - } - #ifdef SHADOWGRP - if (is_shadow_grp && (sgr_open (O_CREAT | O_RDWR) == 0)) { - fprintf (stderr, _("%s: cannot open %s\n"), Prog, sgr_dbname ()); -- fail_exit (EXIT_FAILURE); -+ fail_exit (EXIT_FAILURE, process_selinux); - } - #endif - #ifdef ENABLE_SUBIDS -@@ -946,7 +951,7 @@ static void open_files (void) - fprintf (stderr, - _("%s: cannot open %s\n"), - Prog, sub_uid_dbname ()); -- fail_exit (EXIT_FAILURE); -+ fail_exit (EXIT_FAILURE, process_selinux); - } - } - if (is_sub_gid) { -@@ -954,7 +959,7 @@ static void open_files (void) - fprintf (stderr, - _("%s: cannot open %s\n"), - Prog, sub_gid_dbname ()); -- fail_exit (EXIT_FAILURE); -+ fail_exit (EXIT_FAILURE, process_selinux); - } - } - #endif /* ENABLE_SUBIDS */ -@@ -963,14 +968,18 @@ static void open_files (void) - /* - * close_files - close and unlock the password, group and shadow databases - */ --static void close_files (void) -+static void close_files (struct option_flags *flags) - { -- if (pw_close (true) == 0) { -+ bool process_selinux; -+ -+ process_selinux = !flags->chroot; -+ -+ if (pw_close (process_selinux) == 0) { - fprintf (stderr, _("%s: failure while writing changes to %s\n"), Prog, pw_dbname ()); - SYSLOG ((LOG_ERR, "failure while writing changes to %s", pw_dbname ())); -- fail_exit (EXIT_FAILURE); -+ fail_exit (EXIT_FAILURE, process_selinux); - } -- if (pw_unlock (true) == 0) { -+ if (pw_unlock (process_selinux) == 0) { - fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, pw_dbname ()); - SYSLOG ((LOG_ERR, "failed to unlock %s", pw_dbname ())); - /* continue */ -@@ -978,14 +987,14 @@ static void close_files (void) - pw_locked = false; - - if (is_shadow) { -- if (spw_close (true) == 0) { -+ if (spw_close (process_selinux) == 0) { - fprintf (stderr, - _("%s: failure while writing changes to %s\n"), - Prog, spw_dbname ()); - SYSLOG ((LOG_ERR, "failure while writing changes to %s", spw_dbname ())); -- fail_exit (EXIT_FAILURE); -+ fail_exit (EXIT_FAILURE, process_selinux); - } -- if (spw_unlock (true) == 0) { -+ if (spw_unlock (process_selinux) == 0) { - fprintf (stderr, - _("%s: failed to unlock %s\n"), - Prog, spw_dbname ()); -@@ -995,29 +1004,29 @@ static void close_files (void) - spw_locked = false; - } - -- if (gr_close (true) == 0) { -+ if (gr_close (process_selinux) == 0) { - fprintf (stderr, - _("%s: failure while writing changes to %s\n"), - Prog, gr_dbname ()); - SYSLOG ((LOG_ERR, "failure while writing changes to %s", gr_dbname ())); -- fail_exit (EXIT_FAILURE); -+ fail_exit (EXIT_FAILURE, process_selinux); - } - #ifdef ENABLE_SUBIDS -- if (is_sub_uid && (sub_uid_close (true) == 0)) { -+ if (is_sub_uid && (sub_uid_close (process_selinux) == 0)) { - fprintf (stderr, - _("%s: failure while writing changes to %s\n"), Prog, sub_uid_dbname ()); - SYSLOG ((LOG_ERR, "failure while writing changes to %s", sub_uid_dbname ())); -- fail_exit (EXIT_FAILURE); -+ fail_exit (EXIT_FAILURE, process_selinux); - } -- if (is_sub_gid && (sub_gid_close (true) == 0)) { -+ if (is_sub_gid && (sub_gid_close (process_selinux) == 0)) { - fprintf (stderr, - _("%s: failure while writing changes to %s\n"), Prog, sub_gid_dbname ()); - SYSLOG ((LOG_ERR, "failure while writing changes to %s", sub_gid_dbname ())); -- fail_exit (EXIT_FAILURE); -+ fail_exit (EXIT_FAILURE, process_selinux); - } - #endif /* ENABLE_SUBIDS */ - -- if (gr_unlock (true) == 0) { -+ if (gr_unlock (process_selinux) == 0) { - fprintf (stderr, - _("%s: failed to unlock %s\n"), - Prog, gr_dbname ()); -@@ -1028,14 +1037,14 @@ static void close_files (void) - - #ifdef SHADOWGRP - if (is_shadow_grp) { -- if (sgr_close (true) == 0) { -+ if (sgr_close (process_selinux) == 0) { - fprintf (stderr, - _("%s: failure while writing changes to %s\n"), - Prog, sgr_dbname ()); - SYSLOG ((LOG_ERR, "failure while writing changes to %s", sgr_dbname ())); -- fail_exit (EXIT_FAILURE); -+ fail_exit (EXIT_FAILURE, process_selinux); - } -- if (sgr_unlock (true) == 0) { -+ if (sgr_unlock (process_selinux) == 0) { - fprintf (stderr, - _("%s: failed to unlock %s\n"), - Prog, sgr_dbname ()); -@@ -1047,7 +1056,7 @@ static void close_files (void) - #endif - #ifdef ENABLE_SUBIDS - if (is_sub_uid) { -- if (sub_uid_unlock (true) == 0) { -+ if (sub_uid_unlock (process_selinux) == 0) { - fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, sub_uid_dbname ()); - SYSLOG ((LOG_ERR, "failed to unlock %s", sub_uid_dbname ())); - /* continue */ -@@ -1055,7 +1064,7 @@ static void close_files (void) - sub_uid_locked = false; - } - if (is_sub_gid) { -- if (sub_gid_unlock (true) == 0) { -+ if (sub_gid_unlock (process_selinux) == 0) { - fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, sub_gid_dbname ()); - SYSLOG ((LOG_ERR, "failed to unlock %s", sub_gid_dbname ())); - /* continue */ -@@ -1100,6 +1109,8 @@ int main (int argc, char **argv) - size_t nusers = 0; - #endif /* USE_PAM */ - #endif /* ACCT_TOOLS_SETUID */ -+ struct option_flags flags; -+ bool process_selinux; - - log_set_progname(Prog); - log_set_logfd(stderr); -@@ -1113,7 +1124,8 @@ int main (int argc, char **argv) - - OPENLOG ("newusers"); - -- process_flags (argc, argv); -+ process_flags (argc, argv, &flags); -+ process_selinux = !flags.chroot; - - check_perms (); - -@@ -1127,7 +1139,7 @@ int main (int argc, char **argv) - is_sub_gid = sub_gid_file_present () && !rflg; - #endif /* ENABLE_SUBIDS */ - -- open_files (); -+ open_files (process_selinux); - - /* - * Read each line. The line has the same format as a password file -@@ -1143,13 +1155,13 @@ int main (int argc, char **argv) - if (stpsep(buf, "\n") == NULL && feof(stdin) == 0) { - fprintf (stderr, _("%s: line %jd: line too long\n"), - Prog, line); -- fail_exit (EXIT_FAILURE); -+ fail_exit (EXIT_FAILURE, process_selinux); - } - - if (STRSEP2ARR(buf, ":", fields) == -1) { - fprintf (stderr, _("%s: line %jd: invalid line\n"), - Prog, line); -- fail_exit (EXIT_FAILURE); -+ fail_exit (EXIT_FAILURE, process_selinux); - } - - /* -@@ -1161,14 +1173,14 @@ int main (int argc, char **argv) - fprintf (stderr, - _("%s: cannot update the entry of user %s (not in the passwd database)\n"), - Prog, fields[0]); -- fail_exit (EXIT_FAILURE); -+ fail_exit (EXIT_FAILURE, process_selinux); - } - - if (NULL == pw && get_user_id(fields[2], &uid) != 0) { - fprintf (stderr, - _("%s: line %jd: can't create user\n"), - Prog, line); -- fail_exit (EXIT_FAILURE); -+ fail_exit (EXIT_FAILURE, process_selinux); - } - - /* -@@ -1188,7 +1200,7 @@ int main (int argc, char **argv) - fprintf (stderr, - _("%s: line %jd: can't create group\n"), - Prog, line); -- fail_exit (EXIT_FAILURE); -+ fail_exit (EXIT_FAILURE, process_selinux); - } - - /* -@@ -1203,7 +1215,7 @@ int main (int argc, char **argv) - fprintf (stderr, - _("%s: line %jd: can't create user\n"), - Prog, line); -- fail_exit (EXIT_FAILURE); -+ fail_exit (EXIT_FAILURE, process_selinux); - } - - /* -@@ -1215,7 +1227,7 @@ int main (int argc, char **argv) - fprintf (stderr, - _("%s: line %jd: user '%s' does not exist in %s\n"), - Prog, line, fields[0], pw_dbname ()); -- fail_exit (EXIT_FAILURE); -+ fail_exit (EXIT_FAILURE, process_selinux); - } - newpw = *pw; - -@@ -1230,7 +1242,7 @@ int main (int argc, char **argv) - fprintf (stderr, - _("%s: line %jd: %s\n"), - Prog, line, strerror(errno)); -- fail_exit (EXIT_FAILURE); -+ fail_exit (EXIT_FAILURE, process_selinux); - } - lines[nusers-1] = line; - usernames[nusers-1] = xstrdup(fields[0]); -@@ -1241,7 +1253,7 @@ int main (int argc, char **argv) - fprintf (stderr, - _("%s: line %jd: can't update password\n"), - Prog, line); -- fail_exit (EXIT_FAILURE); -+ fail_exit (EXIT_FAILURE, process_selinux); - } - if (!streq(fields[4], "")) { - newpw.pw_gecos = fields[4]; -@@ -1264,7 +1276,7 @@ int main (int argc, char **argv) - fprintf(stderr, - _("%s: line %jd: homedir must be an absolute path\n"), - Prog, line); -- fail_exit (EXIT_FAILURE); -+ fail_exit (EXIT_FAILURE, process_selinux); - } - if (mkdir (newpw.pw_dir, mode) != 0) { - fprintf (stderr, -@@ -1272,7 +1284,7 @@ int main (int argc, char **argv) - Prog, line, newpw.pw_dir, - strerror (errno)); - if (errno != EEXIST) { -- fail_exit (EXIT_FAILURE); -+ fail_exit (EXIT_FAILURE, process_selinux); - } - } - if (chown(newpw.pw_dir, newpw.pw_uid, newpw.pw_gid) != 0) -@@ -1281,7 +1293,7 @@ int main (int argc, char **argv) - _("%s: line %jd: chown %s failed: %s\n"), - Prog, line, newpw.pw_dir, - strerror (errno)); -- fail_exit (EXIT_FAILURE); -+ fail_exit (EXIT_FAILURE, process_selinux); - } - } - -@@ -1292,7 +1304,7 @@ int main (int argc, char **argv) - fprintf (stderr, - _("%s: line %jd: can't update entry\n"), - Prog, line); -- fail_exit (EXIT_FAILURE); -+ fail_exit (EXIT_FAILURE, process_selinux); - } - - #ifdef ENABLE_SUBIDS -@@ -1307,14 +1319,14 @@ int main (int argc, char **argv) - fprintf (stderr, - _("%s: can't find subordinate user range\n"), - Prog); -- fail_exit (EXIT_FAILURE); -+ fail_exit (EXIT_FAILURE, process_selinux); - } - if (sub_uid_add(fields[0], sub_uid_start, sub_uid_count) == 0) - { - fprintf (stderr, - _("%s: failed to prepare new %s entry\n"), - Prog, sub_uid_dbname ()); -- fail_exit (EXIT_FAILURE); -+ fail_exit (EXIT_FAILURE, process_selinux); - } - } - -@@ -1328,13 +1340,13 @@ int main (int argc, char **argv) - fprintf (stderr, - _("%s: can't find subordinate group range\n"), - Prog); -- fail_exit (EXIT_FAILURE); -+ fail_exit (EXIT_FAILURE, process_selinux); - } - if (sub_gid_add(fields[0], sub_gid_start, sub_gid_count) == 0) { - fprintf (stderr, - _("%s: failed to prepare new %s entry\n"), - Prog, sub_uid_dbname ()); -- fail_exit (EXIT_FAILURE); -+ fail_exit (EXIT_FAILURE, process_selinux); - } - } - #endif /* ENABLE_SUBIDS */ -@@ -1347,7 +1359,7 @@ int main (int argc, char **argv) - * changes to be written out all at once, and then unlocked - * afterwards. - */ -- close_files (); -+ close_files (&flags); - - nscd_flush_cache ("passwd"); - nscd_flush_cache ("group"); -diff -up shadow-4.18.0/src/useradd.c.test-chroot2 shadow-4.18.0/src/useradd.c ---- shadow-4.18.0/src/useradd.c.test-chroot2 2025-07-08 11:22:16.257383838 +0200 -+++ shadow-4.18.0/src/useradd.c 2025-07-08 11:22:16.261386737 +0200 -@@ -92,6 +92,15 @@ - #ifndef LASTLOG_FILE - #define LASTLOG_FILE "/var/log/lastlog" - #endif -+ -+/* -+ * Structures -+ */ -+struct option_flags { -+ bool chroot; -+ bool prefix; -+}; -+ - /* - * Global variables - */ -@@ -219,40 +228,41 @@ static bool home_added = false; - #define DLOG_INIT "LOG_INIT" - - /* local function prototypes */ --NORETURN static void fail_exit (int); --static void get_defaults (void); -+NORETURN static void fail_exit (int, bool); -+static void get_defaults (struct option_flags *); - static void show_defaults (void); - static int set_defaults (void); --static int get_groups (char *); --static struct group * get_local_group (char * grp_name); -+static int get_groups (char *, struct option_flags *); -+static struct group * get_local_group (char * grp_name, bool process_selinux); - NORETURN static void usage (int status); - static void new_pwent (struct passwd *); - - static void new_spent (struct spwd *); --static void grp_update (void); -+static void grp_update (bool); - --static void process_flags (int argc, char **argv); --static void close_files (void); --static void close_group_files (void); --static void unlock_group_files (void); --static void open_files (void); --static void open_group_files (void); --static void open_shadow (void); -+static void process_flags (int argc, char **argv, struct option_flags *flags); -+static void close_files (struct option_flags *flags); -+static void close_group_files (bool process_selinux); -+static void unlock_group_files (bool process_selinux); -+static void open_files (bool process_selinux); -+static void open_group_files (bool process_selinux); -+static void open_shadow (bool process_selinux); - static void faillog_reset (uid_t); - #ifdef ENABLE_LASTLOG - static void lastlog_reset (uid_t); - #endif /* ENABLE_LASTLOG */ - static void tallylog_reset (const char *); --static void usr_update (unsigned long subuid_count, unsigned long subgid_count); --static void create_home (void); --static void create_mail (void); -+static void usr_update (unsigned long subuid_count, unsigned long subgid_count, -+ struct option_flags *flags); -+static void create_home (struct option_flags *flags); -+static void create_mail (struct option_flags *flags); - static void check_uid_range(int rflg, uid_t user_id); - - - /* - * fail_exit - undo as much as possible - */ --static void fail_exit (int code) -+static void fail_exit (int code, bool process_selinux) - { - #ifdef WITH_AUDIT - int type; -@@ -265,35 +275,35 @@ static void fail_exit (int code) - SYSLOG((LOG_ERR, "failed to remove %s", prefix_user_home)); - } - -- if (spw_locked && spw_unlock(true) == 0) { -+ if (spw_locked && spw_unlock(process_selinux) == 0) { - fprintf(stderr, _("%s: failed to unlock %s\n"), Prog, spw_dbname()); - SYSLOG((LOG_ERR, "failed to unlock %s", spw_dbname())); - /* continue */ - } -- if (pw_locked && pw_unlock(true) == 0) { -+ if (pw_locked && pw_unlock(process_selinux) == 0) { - fprintf(stderr, _("%s: failed to unlock %s\n"), Prog, pw_dbname()); - SYSLOG((LOG_ERR, "failed to unlock %s", pw_dbname())); - /* continue */ - } -- if (gr_locked && gr_unlock(true) == 0) { -+ if (gr_locked && gr_unlock(process_selinux) == 0) { - fprintf(stderr, _("%s: failed to unlock %s\n"), Prog, gr_dbname()); - SYSLOG((LOG_ERR, "failed to unlock %s", gr_dbname())); - /* continue */ - } - #ifdef SHADOWGRP -- if (sgr_locked && sgr_unlock(true) == 0) { -+ if (sgr_locked && sgr_unlock(process_selinux) == 0) { - fprintf(stderr, _("%s: failed to unlock %s\n"), Prog, sgr_dbname()); - SYSLOG((LOG_ERR, "failed to unlock %s", sgr_dbname())); - /* continue */ - } - #endif - #ifdef ENABLE_SUBIDS -- if (sub_uid_locked && sub_uid_unlock(true) == 0) { -+ if (sub_uid_locked && sub_uid_unlock(process_selinux) == 0) { - fprintf(stderr, _("%s: failed to unlock %s\n"), Prog, sub_uid_dbname()); - SYSLOG((LOG_ERR, "failed to unlock %s", sub_uid_dbname())); - /* continue */ - } -- if (sub_gid_locked && sub_gid_unlock(true) == 0) { -+ if (sub_gid_locked && sub_gid_unlock(process_selinux) == 0) { - fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, sub_gid_dbname()); - SYSLOG ((LOG_ERR, "failed to unlock %s", sub_gid_dbname())); - /* continue */ -@@ -322,7 +332,7 @@ static void fail_exit (int code) - * file does not exist. - */ - static void --get_defaults(void) -+get_defaults(struct option_flags *flags) - { - FILE *fp; - char *default_file = USER_DEFAULTS_FILE; -@@ -377,7 +387,7 @@ get_defaults(void) - ccp = cp; - - if (streq(buf, DGROUPS)) { -- if (get_groups (cp) != 0) { -+ if (get_groups (cp, flags) != 0) { - fprintf (stderr, - _("%s: the '%s=' configuration in %s has an invalid group, ignoring the bad group\n"), - Prog, DGROUPS, default_file); -@@ -736,11 +746,14 @@ err_free_new: - * converts it to a NULL-terminated array. Any unknown group - * names are reported as errors. - */ --static int get_groups (char *list) -+static int get_groups (char *list, struct option_flags *flags) - { - struct group *grp; - bool errors = false; - int ngroups = 0; -+ bool process_selinux; -+ -+ process_selinux = !flags->chroot && !flags->prefix; - - /* - * Free previous group list before creating a new one. -@@ -758,7 +771,7 @@ static int get_groups (char *list) - /* - * Open the group files - */ -- open_group_files (); -+ open_group_files (process_selinux); - - /* - * So long as there is some data to be converted, strip off -@@ -777,7 +790,7 @@ static int get_groups (char *list) - * Names starting with digits are treated as numerical - * GID values, otherwise the string is looked up as is. - */ -- grp = get_local_group(g); -+ grp = get_local_group(g, process_selinux); - - /* - * There must be a match, either by GID value or by -@@ -815,8 +828,8 @@ static int get_groups (char *list) - gr_free (grp); - } - -- close_group_files (); -- unlock_group_files (); -+ close_group_files (process_selinux); -+ unlock_group_files (process_selinux); - - user_groups[ngroups] = NULL; - -@@ -837,7 +850,7 @@ static int get_groups (char *list) - * If the name exists the group information is returned, otherwise NULL is - * returned. - */ --static struct group * get_local_group(char * grp_name) -+static struct group * get_local_group(char * grp_name, bool process_selinux) - { - gid_t gid; - struct group *result_grp = NULL; -@@ -854,7 +867,7 @@ static struct group * get_local_group(ch - fprintf (stderr, - _("%s: Out of memory. Cannot find group '%s'.\n"), - Prog, grp_name); -- fail_exit (E_GRP_UPDATE); -+ fail_exit (E_GRP_UPDATE, process_selinux); - } - } - -@@ -986,7 +999,7 @@ static void new_spent (struct spwd *spen - * close_files() should be called afterwards to commit the changes - * and unlocking the group files. - */ --static void grp_update (void) -+static void grp_update (bool process_selinux) - { - const struct group *grp; - struct group *ngrp; -@@ -1022,7 +1035,7 @@ static void grp_update (void) - _("%s: Out of memory. Cannot update %s.\n"), - Prog, gr_dbname ()); - SYSLOG ((LOG_ERR, "failed to prepare the new %s entry '%s'", gr_dbname (), user_name)); -- fail_exit (E_GRP_UPDATE); /* XXX */ -+ fail_exit (E_GRP_UPDATE, process_selinux); /* XXX */ - } - - /* -@@ -1035,7 +1048,7 @@ static void grp_update (void) - _("%s: failed to prepare the new %s entry '%s'\n"), - Prog, gr_dbname (), ngrp->gr_name); - SYSLOG ((LOG_ERR, "failed to prepare the new %s entry '%s'", gr_dbname (), user_name)); -- fail_exit (E_GRP_UPDATE); -+ fail_exit (E_GRP_UPDATE, process_selinux); - } - #ifdef WITH_AUDIT - audit_logger_with_group (AUDIT_USER_MGMT, -@@ -1085,7 +1098,7 @@ static void grp_update (void) - _("%s: Out of memory. Cannot update %s.\n"), - Prog, sgr_dbname ()); - SYSLOG ((LOG_ERR, "failed to prepare the new %s entry '%s'", sgr_dbname (), user_name)); -- fail_exit (E_GRP_UPDATE); /* XXX */ -+ fail_exit (E_GRP_UPDATE, process_selinux); /* XXX */ - } - - /* -@@ -1099,7 +1112,7 @@ static void grp_update (void) - Prog, sgr_dbname (), nsgrp->sg_namp); - SYSLOG ((LOG_ERR, "failed to prepare the new %s entry '%s'", sgr_dbname (), user_name)); - -- fail_exit (E_GRP_UPDATE); -+ fail_exit (E_GRP_UPDATE, process_selinux); - } - #ifdef WITH_AUDIT - audit_logger_with_group (AUDIT_USER_MGMT, -@@ -1121,7 +1134,7 @@ static void grp_update (void) - * the values that the user will be created with accordingly. The - * values are checked for sanity. - */ --static void process_flags (int argc, char **argv) -+static void process_flags (int argc, char **argv, struct option_flags *flags) - { - const struct group *grp; - bool anyflag = false; -@@ -1296,7 +1309,7 @@ static void process_flags (int argc, cha - gflg = true; - break; - case 'G': -- if (get_groups (optarg) != 0) { -+ if (get_groups (optarg, flags) != 0) { - exit (E_NOTFOUND); - } - if (NULL != user_groups[0]) { -@@ -1356,8 +1369,10 @@ static void process_flags (int argc, cha - rflg = true; - break; - case 'R': /* no-op, handled in process_root_flag () */ -+ flags->chroot = true; - break; - case 'P': /* no-op, handled in process_prefix_flag () */ -+ flags->prefix = true; - break; - case 's': - if ( ( !VALID (optarg) ) -@@ -1562,38 +1577,42 @@ static void process_flags (int argc, cha - * close_files() closes all of the files that were opened for this - * new user. This causes any modified entries to be written out. - */ --static void close_files (void) -+static void close_files (struct option_flags *flags) - { -- if (pw_close (true) == 0) { -+ bool process_selinux; -+ -+ process_selinux = !flags->chroot && !flags->prefix; -+ -+ if (pw_close (process_selinux) == 0) { - fprintf (stderr, _("%s: failure while writing changes to %s\n"), Prog, pw_dbname ()); - SYSLOG ((LOG_ERR, "failure while writing changes to %s", pw_dbname ())); -- fail_exit (E_PW_UPDATE); -+ fail_exit (E_PW_UPDATE, process_selinux); - } -- if (is_shadow_pwd && (spw_close (true) == 0)) { -+ if (is_shadow_pwd && (spw_close (process_selinux) == 0)) { - fprintf (stderr, - _("%s: failure while writing changes to %s\n"), Prog, spw_dbname ()); - SYSLOG ((LOG_ERR, "failure while writing changes to %s", spw_dbname ())); -- fail_exit (E_PW_UPDATE); -+ fail_exit (E_PW_UPDATE, process_selinux); - } - -- close_group_files (); -+ close_group_files (process_selinux); - - #ifdef ENABLE_SUBIDS -- if (is_sub_uid && (sub_uid_close (true) == 0)) { -+ if (is_sub_uid && (sub_uid_close (process_selinux) == 0)) { - fprintf (stderr, - _("%s: failure while writing changes to %s\n"), Prog, sub_uid_dbname ()); - SYSLOG ((LOG_ERR, "failure while writing changes to %s", sub_uid_dbname ())); -- fail_exit (E_SUB_UID_UPDATE); -+ fail_exit (E_SUB_UID_UPDATE, process_selinux); - } -- if (is_sub_gid && (sub_gid_close (true) == 0)) { -+ if (is_sub_gid && (sub_gid_close (process_selinux) == 0)) { - fprintf (stderr, - _("%s: failure while writing changes to %s\n"), Prog, sub_gid_dbname ()); - SYSLOG ((LOG_ERR, "failure while writing changes to %s", sub_gid_dbname ())); -- fail_exit (E_SUB_GID_UPDATE); -+ fail_exit (E_SUB_GID_UPDATE, process_selinux); - } - #endif /* ENABLE_SUBIDS */ - if (is_shadow_pwd) { -- if (spw_unlock (true) == 0) { -+ if (spw_unlock (process_selinux) == 0) { - fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, spw_dbname ()); - SYSLOG ((LOG_ERR, "failed to unlock %s", spw_dbname ())); - #ifdef WITH_AUDIT -@@ -1606,7 +1625,7 @@ static void close_files (void) - } - spw_locked = false; - } -- if (pw_unlock (true) == 0) { -+ if (pw_unlock (process_selinux) == 0) { - fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, pw_dbname ()); - SYSLOG ((LOG_ERR, "failed to unlock %s", pw_dbname ())); - #ifdef WITH_AUDIT -@@ -1619,11 +1638,11 @@ static void close_files (void) - } - pw_locked = false; - -- unlock_group_files (); -+ unlock_group_files (process_selinux); - - #ifdef ENABLE_SUBIDS - if (is_sub_uid) { -- if (sub_uid_unlock (true) == 0) { -+ if (sub_uid_unlock (process_selinux) == 0) { - fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, sub_uid_dbname ()); - SYSLOG ((LOG_ERR, "failed to unlock %s", sub_uid_dbname ())); - #ifdef WITH_AUDIT -@@ -1637,7 +1656,7 @@ static void close_files (void) - sub_uid_locked = false; - } - if (is_sub_gid) { -- if (sub_gid_unlock (true) == 0) { -+ if (sub_gid_unlock (process_selinux) == 0) { - fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, sub_gid_dbname ()); - SYSLOG ((LOG_ERR, "failed to unlock %s", sub_gid_dbname ())); - #ifdef WITH_AUDIT -@@ -1659,25 +1678,25 @@ static void close_files (void) - * close_group_files() closes all of the files that were opened related - * with groups. This causes any modified entries to be written out. - */ --static void close_group_files (void) -+static void close_group_files (bool process_selinux) - { - if (!do_grp_update) - return; - -- if (gr_close(true) == 0) { -+ if (gr_close(process_selinux) == 0) { - fprintf(stderr, - _("%s: failure while writing changes to %s\n"), - Prog, gr_dbname()); - SYSLOG((LOG_ERR, "failure while writing changes to %s", gr_dbname())); -- fail_exit(E_GRP_UPDATE); -+ fail_exit(E_GRP_UPDATE, process_selinux); - } - #ifdef SHADOWGRP -- if (is_shadow_grp && sgr_close(true) == 0) { -+ if (is_shadow_grp && sgr_close(process_selinux) == 0) { - fprintf(stderr, - _("%s: failure while writing changes to %s\n"), - Prog, sgr_dbname()); - SYSLOG((LOG_ERR, "failure while writing changes to %s", sgr_dbname())); -- fail_exit(E_GRP_UPDATE); -+ fail_exit(E_GRP_UPDATE, process_selinux); - } - #endif /* SHADOWGRP */ - } -@@ -1688,9 +1707,9 @@ static void close_group_files (void) - * unlock_group_files() unlocks all of the files that were locked related - * with groups. This causes any modified entries to be written out. - */ --static void unlock_group_files (void) -+static void unlock_group_files (bool process_selinux) - { -- if (gr_unlock (true) == 0) { -+ if (gr_unlock (process_selinux) == 0) { - fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, gr_dbname ()); - SYSLOG ((LOG_ERR, "failed to unlock %s", gr_dbname ())); - #ifdef WITH_AUDIT -@@ -1704,7 +1723,7 @@ static void unlock_group_files (void) - gr_locked = false; - #ifdef SHADOWGRP - if (is_shadow_grp) { -- if (sgr_unlock (true) == 0) { -+ if (sgr_unlock (process_selinux) == 0) { - fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, sgr_dbname ()); - SYSLOG ((LOG_ERR, "failed to unlock %s", sgr_dbname ())); - #ifdef WITH_AUDIT -@@ -1725,7 +1744,7 @@ static void unlock_group_files (void) - * - * open_files() opens the two password files. - */ --static void open_files (void) -+static void open_files (bool process_selinux) - { - if (pw_lock () == 0) { - fprintf (stderr, -@@ -1736,12 +1755,12 @@ static void open_files (void) - pw_locked = true; - if (pw_open (O_CREAT | O_RDWR) == 0) { - fprintf (stderr, _("%s: cannot open %s\n"), Prog, pw_dbname ()); -- fail_exit (E_PW_UPDATE); -+ fail_exit (E_PW_UPDATE, process_selinux); - } - - /* shadow file will be opened by open_shadow(); */ - -- open_group_files (); -+ open_group_files (process_selinux); - - #ifdef ENABLE_SUBIDS - if (is_sub_uid) { -@@ -1749,14 +1768,14 @@ static void open_files (void) - fprintf (stderr, - _("%s: cannot lock %s; try again later.\n"), - Prog, sub_uid_dbname ()); -- fail_exit (E_SUB_UID_UPDATE); -+ fail_exit (E_SUB_UID_UPDATE, process_selinux); - } - sub_uid_locked = true; - if (sub_uid_open (O_CREAT | O_RDWR) == 0) { - fprintf (stderr, - _("%s: cannot open %s\n"), - Prog, sub_uid_dbname ()); -- fail_exit (E_SUB_UID_UPDATE); -+ fail_exit (E_SUB_UID_UPDATE, process_selinux); - } - } - if (is_sub_gid) { -@@ -1764,31 +1783,31 @@ static void open_files (void) - fprintf (stderr, - _("%s: cannot lock %s; try again later.\n"), - Prog, sub_gid_dbname ()); -- fail_exit (E_SUB_GID_UPDATE); -+ fail_exit (E_SUB_GID_UPDATE, process_selinux); - } - sub_gid_locked = true; - if (sub_gid_open (O_CREAT | O_RDWR) == 0) { - fprintf (stderr, - _("%s: cannot open %s\n"), - Prog, sub_gid_dbname ()); -- fail_exit (E_SUB_GID_UPDATE); -+ fail_exit (E_SUB_GID_UPDATE, process_selinux); - } - } - #endif /* ENABLE_SUBIDS */ - } - --static void open_group_files (void) -+static void open_group_files (bool process_selinux) - { - if (gr_lock () == 0) { - fprintf (stderr, - _("%s: cannot lock %s; try again later.\n"), - Prog, gr_dbname ()); -- fail_exit (E_GRP_UPDATE); -+ fail_exit (E_GRP_UPDATE, process_selinux); - } - gr_locked = true; - if (gr_open (O_CREAT | O_RDWR) == 0) { - fprintf (stderr, _("%s: cannot open %s\n"), Prog, gr_dbname ()); -- fail_exit (E_GRP_UPDATE); -+ fail_exit (E_GRP_UPDATE, process_selinux); - } - - #ifdef SHADOWGRP -@@ -1797,20 +1816,20 @@ static void open_group_files (void) - fprintf (stderr, - _("%s: cannot lock %s; try again later.\n"), - Prog, sgr_dbname ()); -- fail_exit (E_GRP_UPDATE); -+ fail_exit (E_GRP_UPDATE, process_selinux); - } - sgr_locked = true; - if (sgr_open (O_CREAT | O_RDWR) == 0) { - fprintf (stderr, - _("%s: cannot open %s\n"), - Prog, sgr_dbname ()); -- fail_exit (E_GRP_UPDATE); -+ fail_exit (E_GRP_UPDATE, process_selinux); - } - } - #endif /* SHADOWGRP */ - } - --static void open_shadow (void) -+static void open_shadow (bool process_selinux) - { - if (!is_shadow_pwd) { - return; -@@ -1819,14 +1838,14 @@ static void open_shadow (void) - fprintf (stderr, - _("%s: cannot lock %s; try again later.\n"), - Prog, spw_dbname ()); -- fail_exit (E_PW_UPDATE); -+ fail_exit (E_PW_UPDATE, process_selinux); - } - spw_locked = true; - if (spw_open (O_CREAT | O_RDWR) == 0) { - fprintf (stderr, - _("%s: cannot open %s\n"), - Prog, spw_dbname ()); -- fail_exit (E_PW_UPDATE); -+ fail_exit (E_PW_UPDATE, process_selinux); - } - } - -@@ -1880,7 +1899,7 @@ static void new_sgent (struct sgrp *sgen - * grp_add() writes the new records to the group files. - */ - --static void grp_add (void) -+static void grp_add (bool process_selinux) - { - struct group grp; - -@@ -1909,7 +1928,7 @@ static void grp_add (void) - grp.gr_name, AUDIT_NO_ID, - SHADOW_AUDIT_FAILURE); - #endif -- fail_exit (E_GRP_UPDATE); -+ fail_exit (E_GRP_UPDATE, process_selinux); - } - #ifdef SHADOWGRP - /* -@@ -1925,7 +1944,7 @@ static void grp_add (void) - grp.gr_name, AUDIT_NO_ID, - SHADOW_AUDIT_FAILURE); - #endif -- fail_exit (E_GRP_UPDATE); -+ fail_exit (E_GRP_UPDATE, process_selinux); - } - #endif /* SHADOWGRP */ - SYSLOG ((LOG_INFO, "new group: name=%s, GID=%u", user_name, user_gid)); -@@ -2074,11 +2093,16 @@ static void tallylog_reset (const char * - * usr_update() creates the password file entries for this user - * and will update the group entries if required. - */ --static void usr_update (unsigned long subuid_count, unsigned long subgid_count) -+static void -+usr_update (unsigned long subuid_count, unsigned long subgid_count, -+ struct option_flags *flags) - { - struct passwd pwent; - struct spwd spent; - char *tty; -+ bool process_selinux; -+ -+ process_selinux = !flags->chroot && !flags->prefix; - - /* - * Fill in the password structure with any new fields, making -@@ -2119,7 +2143,7 @@ static void usr_update (unsigned long su - fprintf (stderr, - _("%s: failed to prepare the new %s entry '%s'\n"), - Prog, pw_dbname (), pwent.pw_name); -- fail_exit (E_PW_UPDATE); -+ fail_exit (E_PW_UPDATE, process_selinux); - } - - /* -@@ -2129,7 +2153,7 @@ static void usr_update (unsigned long su - fprintf (stderr, - _("%s: failed to prepare the new %s entry '%s'\n"), - Prog, spw_dbname (), spent.sp_namp); -- fail_exit (E_PW_UPDATE); -+ fail_exit (E_PW_UPDATE, process_selinux); - } - #ifdef ENABLE_SUBIDS - if (is_sub_uid && !local_sub_uid_assigned(user_name) && -@@ -2137,14 +2161,14 @@ static void usr_update (unsigned long su - fprintf (stderr, - _("%s: failed to prepare the new %s entry\n"), - Prog, sub_uid_dbname ()); -- fail_exit (E_SUB_UID_UPDATE); -+ fail_exit (E_SUB_UID_UPDATE, process_selinux); - } - if (is_sub_gid && !local_sub_gid_assigned(user_name) && - (sub_gid_add(user_name, sub_gid_start, subgid_count) == 0)) { - fprintf (stderr, - _("%s: failed to prepare the new %s entry\n"), - Prog, sub_uid_dbname ()); -- fail_exit (E_SUB_GID_UPDATE); -+ fail_exit (E_SUB_GID_UPDATE, process_selinux); - } - #endif /* ENABLE_SUBIDS */ - -@@ -2163,7 +2187,7 @@ static void usr_update (unsigned long su - * Do any group file updates for this user. - */ - if (do_grp_update) { -- grp_update (); -+ grp_update (process_selinux); - } - } - -@@ -2174,11 +2198,14 @@ static void usr_update (unsigned long su - * already exist. It will be created mode 755 owned by the user - * with the user's default group. - */ --static void create_home (void) -+static void create_home (struct option_flags *flags) - { - char path[strlen(prefix_user_home) + 2]; - char *bhome, *cp; - mode_t mode; -+ bool process_selinux; -+ -+ process_selinux = !flags->chroot && !flags->prefix; - - if (access (prefix_user_home, F_OK) == 0) - return; -@@ -2189,15 +2216,17 @@ static void create_home (void) - fprintf(stderr, - _("%s: error while duplicating string %s\n"), - Prog, user_home); -- fail_exit(E_HOMEDIR); -+ fail_exit(E_HOMEDIR, process_selinux); - } - - #ifdef WITH_SELINUX -- if (set_selinux_file_context(prefix_user_home, S_IFDIR) != 0) { -- fprintf(stderr, -- _("%s: cannot set SELinux context for home directory %s\n"), -- Prog, user_home); -- fail_exit(E_HOMEDIR); -+ if (process_selinux) { -+ if (set_selinux_file_context(prefix_user_home, S_IFDIR) != 0) { -+ fprintf(stderr, -+ _("%s: cannot set SELinux context for home directory %s\n"), -+ Prog, user_home); -+ fail_exit(E_HOMEDIR, process_selinux); -+ } - } - #endif - -@@ -2227,14 +2256,14 @@ static void create_home (void) - fprintf(stderr, - _("%s: error while duplicating string in BTRFS check %s\n"), - Prog, path); -- fail_exit(E_HOMEDIR); -+ fail_exit(E_HOMEDIR, process_selinux); - } - stpcpy(&btrfs_check[strlen(path) - strlen(cp) - 1], ""); - if (is_btrfs(btrfs_check) <= 0) { - fprintf(stderr, - _("%s: home directory \"%s\" must be mounted on BTRFS\n"), - Prog, path); -- fail_exit(E_HOMEDIR); -+ fail_exit(E_HOMEDIR, process_selinux); - } - free(btrfs_check); - // make subvolume to mount for user instead of directory -@@ -2242,7 +2271,7 @@ static void create_home (void) - fprintf(stderr, - _("%s: failed to create BTRFS subvolume: %s\n"), - Prog, path); -- fail_exit(E_HOMEDIR); -+ fail_exit(E_HOMEDIR, process_selinux); - } - } - else -@@ -2250,7 +2279,7 @@ static void create_home (void) - if (mkdir(path, 0) != 0) { - fprintf(stderr, _("%s: cannot create directory %s\n"), - Prog, path); -- fail_exit(E_HOMEDIR); -+ fail_exit(E_HOMEDIR, process_selinux); - } - if (chown(path, 0, 0) < 0) { - fprintf(stderr, -@@ -2278,12 +2307,14 @@ static void create_home (void) - user_name, user_id, SHADOW_AUDIT_SUCCESS); - #endif - #ifdef WITH_SELINUX -- /* Reset SELinux to create files with default contexts */ -- if (reset_selinux_file_context() != 0) { -- fprintf(stderr, -- _("%s: cannot reset SELinux file creation context\n"), -- Prog); -- fail_exit(E_HOMEDIR); -+ if (process_selinux) { -+ /* Reset SELinux to create files with default contexts */ -+ if (reset_selinux_file_context() != 0) { -+ fprintf(stderr, -+ _("%s: cannot reset SELinux file creation context\n"), -+ Prog); -+ fail_exit(E_HOMEDIR, process_selinux); -+ } - } - #endif - } -@@ -2295,7 +2326,7 @@ static void create_home (void) - * exist. It will be created mode 660 owned by the user and group - * 'mail' - */ --static void create_mail (void) -+static void create_mail (struct option_flags *flags) - { - int fd; - char *file; -@@ -2303,6 +2334,9 @@ static void create_mail (void) - mode_t mode; - const char *spool; - struct group *gr; -+ bool process_selinux; -+ -+ process_selinux = !flags->chroot && !flags->prefix; - - if (!strcaseeq(create_mail_spool, "yes")) - return; -@@ -2322,11 +2356,13 @@ static void create_mail (void) - file = xaprintf("%s/%s", spool, user_name); - - #ifdef WITH_SELINUX -- if (set_selinux_file_context(file, S_IFREG) != 0) { -- fprintf(stderr, -- _("%s: cannot set SELinux context for mailbox file %s\n"), -- Prog, file); -- fail_exit(E_MAILBOXFILE); -+ if (process_selinux) { -+ if (set_selinux_file_context(file, S_IFREG) != 0) { -+ fprintf(stderr, -+ _("%s: cannot set SELinux context for mailbox file %s\n"), -+ Prog, file); -+ fail_exit(E_MAILBOXFILE, process_selinux); -+ } - } - #endif - -@@ -2362,12 +2398,14 @@ static void create_mail (void) - perror (_("Closing mailbox file")); - } - #ifdef WITH_SELINUX -- /* Reset SELinux to create files with default contexts */ -- if (reset_selinux_file_context() != 0) { -- fprintf(stderr, -- _("%s: cannot reset SELinux file creation context\n"), -- Prog); -- fail_exit(E_MAILBOXFILE); -+ if (process_selinux) { -+ /* Reset SELinux to create files with default contexts */ -+ if (reset_selinux_file_context() != 0) { -+ fprintf(stderr, -+ _("%s: cannot reset SELinux file creation context\n"), -+ Prog); -+ fail_exit(E_MAILBOXFILE, process_selinux); -+ } - } - #endif - } -@@ -2409,6 +2447,8 @@ int main (int argc, char **argv) - #endif - unsigned long subuid_count = 0; - unsigned long subgid_count = 0; -+ struct option_flags flags; -+ bool process_selinux; - - log_set_progname(Prog); - log_set_logfd(stderr); -@@ -2439,9 +2479,10 @@ int main (int argc, char **argv) - is_shadow_grp = sgr_file_present (); - #endif - -- get_defaults (); -+ get_defaults (&flags); - -- process_flags (argc, argv); -+ process_flags (argc, argv, &flags); -+ process_selinux = !flags.chroot && !flags.prefix; - - #ifdef ENABLE_SUBIDS - uid_min = getdef_ulong ("UID_MIN", 1000UL); -@@ -2470,7 +2511,7 @@ int main (int argc, char **argv) - fprintf (stderr, - _("%s: Cannot determine your user name.\n"), - Prog); -- fail_exit (1); -+ fail_exit (1, process_selinux); - } - - retval = pam_start (Prog, pampw?pampw->pw_name:"root", &conv, &pamh); -@@ -2491,7 +2532,7 @@ int main (int argc, char **argv) - if (NULL != pamh) { - (void) pam_end (pamh, retval); - } -- fail_exit (1); -+ fail_exit (1, process_selinux); - } - (void) pam_end (pamh, retval); - #endif /* USE_PAM */ -@@ -2515,7 +2556,7 @@ int main (int argc, char **argv) - */ - if (prefix_getpwnam (user_name) != NULL) { /* local, no need for xgetpwnam */ - fprintf (stderr, _("%s: user '%s' already exists\n"), Prog, user_name); -- fail_exit (E_NAME_IN_USE); -+ fail_exit (E_NAME_IN_USE, process_selinux); - } - - /* -@@ -2530,7 +2571,7 @@ int main (int argc, char **argv) - fprintf (stderr, - _("%s: group %s exists - if you want to add this user to that group, use -g.\n"), - Prog, user_name); -- fail_exit (E_NAME_IN_USE); -+ fail_exit (E_NAME_IN_USE, process_selinux); - } - } - -@@ -2543,7 +2584,7 @@ int main (int argc, char **argv) - * - flush nscd caches for passwd and group services, - * - then close and update the files. - */ -- open_files (); -+ open_files (process_selinux); - - if (!oflg) { - /* first, seek for a valid uid to use for this user. -@@ -2552,14 +2593,14 @@ int main (int argc, char **argv) - if (!uflg) { - if (find_new_uid (rflg, &user_id, NULL) < 0) { - fprintf (stderr, _("%s: can't create user\n"), Prog); -- fail_exit (E_UID_IN_USE); -+ fail_exit (E_UID_IN_USE, process_selinux); - } - } else { - if (prefix_getpwuid (user_id) != NULL) { - fprintf (stderr, - _("%s: UID %lu is not unique\n"), - Prog, (unsigned long) user_id); -- fail_exit (E_UID_IN_USE); -+ fail_exit (E_UID_IN_USE, process_selinux); - } - } - } -@@ -2572,11 +2613,11 @@ int main (int argc, char **argv) - fprintf (stderr, - _("%s: Failed to create tcb directory for %s\n"), - Prog, user_name); -- fail_exit (E_UID_IN_USE); -+ fail_exit (E_UID_IN_USE, process_selinux); - } - } - #endif -- open_shadow (); -+ open_shadow (process_selinux); - - /* do we have to add a group for that user? This is why we need to - * open the group files in the open_files() function --gafton */ -@@ -2585,9 +2626,9 @@ int main (int argc, char **argv) - fprintf (stderr, - _("%s: can't create group\n"), - Prog); -- fail_exit (4); -+ fail_exit (4, process_selinux); - } -- grp_add (); -+ grp_add (process_selinux); - } - - #ifdef ENABLE_SUBIDS -@@ -2596,7 +2637,7 @@ int main (int argc, char **argv) - fprintf (stderr, - _("%s: can't create subordinate user IDs\n"), - Prog); -- fail_exit(E_SUB_UID_UPDATE); -+ fail_exit(E_SUB_UID_UPDATE, process_selinux); - } - } - if (is_sub_gid && subgid_count != 0) { -@@ -2604,14 +2645,14 @@ int main (int argc, char **argv) - fprintf (stderr, - _("%s: can't create subordinate group IDs\n"), - Prog); -- fail_exit(E_SUB_GID_UPDATE); -+ fail_exit(E_SUB_GID_UPDATE, process_selinux); - } - } - #endif /* ENABLE_SUBIDS */ - -- usr_update (subuid_count, subgid_count); -+ usr_update (subuid_count, subgid_count, &flags); - -- close_files (); -+ close_files (&flags); - - nscd_flush_cache ("passwd"); - nscd_flush_cache ("group"); -@@ -2637,13 +2678,13 @@ int main (int argc, char **argv) - "add-selinux-user-mapping", - user_name, user_id, SHADOW_AUDIT_FAILURE); - #endif /* WITH_AUDIT */ -- fail_exit (E_SE_UPDATE); -+ fail_exit (E_SE_UPDATE, process_selinux); - } - } - #endif /* WITH_SELINUX */ - - if (mflg) { -- create_home (); -+ create_home (&flags); - if (home_added) { - copy_tree (def_template, prefix_user_home, false, true, - (uid_t)-1, user_id, (gid_t)-1, user_gid); -@@ -2660,7 +2701,7 @@ int main (int argc, char **argv) - - /* Do not create mail directory for system accounts */ - if (!rflg) { -- create_mail (); -+ create_mail (&flags); - } - - if (run_parts ("/etc/shadow-maint/useradd-post.d", user_name, -diff -up shadow-4.18.0/src/userdel.c.test-chroot2 shadow-4.18.0/src/userdel.c ---- shadow-4.18.0/src/userdel.c.test-chroot2 2025-07-08 11:22:16.257469516 +0200 -+++ shadow-4.18.0/src/userdel.c 2025-07-08 11:22:16.263226317 +0200 -@@ -73,6 +73,14 @@ - #endif /* ENABLE_SUBIDS */ - - /* -+ * Structures -+ */ -+struct option_flags { -+ bool chroot; -+ bool prefix; -+}; -+ -+/* - * Global variables - */ - static const char Prog[] = "userdel"; -@@ -87,7 +95,6 @@ static bool rflg = false; - #ifdef WITH_SELINUX - static bool Zflg = false; - #endif --static bool Rflg = false; - - static bool is_shadow_pwd; - -@@ -109,12 +116,12 @@ static const char* prefix = ""; - - /* local function prototypes */ - static void usage (int status); --static void update_groups (void); --static void remove_usergroup (void); --static void close_files (void); --static void fail_exit (int); --static void open_files (void); --static void update_user (void); -+static void update_groups (bool process_selinux); -+static void remove_usergroup (bool process_selinux); -+static void close_files (struct option_flags *flags); -+static void fail_exit (int, bool); -+static void open_files (bool process_selinux); -+static void update_user (bool process_selinux); - static void user_cancel (const char *); - - #ifdef EXTRA_CHECK_HOME_DIR -@@ -162,7 +169,7 @@ static void usage (int status) - * name is their user name) and delete them too (only if USERGROUPS_ENAB - * is enabled). - */ --static void update_groups (void) -+static void update_groups (bool process_selinux) - { - const struct group *grp; - struct group *ngrp; -@@ -219,7 +226,7 @@ static void update_groups (void) - } - - if (getdef_bool ("USERGROUPS_ENAB")) { -- remove_usergroup (); -+ remove_usergroup (process_selinux); - } - - #ifdef SHADOWGRP -@@ -291,7 +298,7 @@ static void update_groups (void) - * + it has no other members - * + it is not the primary group of any other user - */ --static void remove_usergroup (void) -+static void remove_usergroup (bool process_selinux) - { - const struct group *grp; - const struct passwd *pwd = NULL; -@@ -346,7 +353,7 @@ static void remove_usergroup (void) - fprintf (stderr, - _("%s: cannot remove entry '%s' from %s\n"), - Prog, user_name, gr_dbname ()); -- fail_exit (E_GRP_UPDATE); -+ fail_exit (E_GRP_UPDATE, process_selinux); - } - - #ifdef WITH_AUDIT -@@ -365,7 +372,7 @@ static void remove_usergroup (void) - fprintf (stderr, - _("%s: cannot remove entry '%s' from %s\n"), - Prog, user_name, sgr_dbname ()); -- fail_exit (E_GRP_UPDATE); -+ fail_exit (E_GRP_UPDATE, process_selinux); - } - #ifdef WITH_AUDIT - audit_logger_with_group (AUDIT_GRP_MGMT, -@@ -388,14 +395,18 @@ static void remove_usergroup (void) - * close_files() closes all of the files that were opened for this - * new user. This causes any modified entries to be written out. - */ --static void close_files (void) -+static void close_files (struct option_flags *flags) - { -- if (pw_close (true) == 0) { -+ bool process_selinux; -+ -+ process_selinux = !flags->chroot && !flags->prefix; -+ -+ if (pw_close (process_selinux) == 0) { - fprintf (stderr, _("%s: failure while writing changes to %s\n"), Prog, pw_dbname ()); - SYSLOG ((LOG_ERR, "failure while writing changes to %s", pw_dbname ())); -- fail_exit (E_PW_UPDATE); -+ fail_exit (E_PW_UPDATE, process_selinux); - } -- if (pw_unlock (true) == 0) { -+ if (pw_unlock (process_selinux) == 0) { - fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, pw_dbname ()); - SYSLOG ((LOG_ERR, "failed to unlock %s", pw_dbname ())); - /* continue */ -@@ -403,13 +414,13 @@ static void close_files (void) - pw_locked = false; - - if (is_shadow_pwd) { -- if (spw_close (true) == 0) { -+ if (spw_close (process_selinux) == 0) { - fprintf (stderr, - _("%s: failure while writing changes to %s\n"), Prog, spw_dbname ()); - SYSLOG ((LOG_ERR, "failure while writing changes to %s", spw_dbname ())); -- fail_exit (E_PW_UPDATE); -+ fail_exit (E_PW_UPDATE, process_selinux); - } -- if (spw_unlock (true) == 0) { -+ if (spw_unlock (process_selinux) == 0) { - fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, spw_dbname ()); - SYSLOG ((LOG_ERR, "failed to unlock %s", spw_dbname ())); - /* continue */ -@@ -417,12 +428,12 @@ static void close_files (void) - spw_locked = false; - } - -- if (gr_close (true) == 0) { -+ if (gr_close (process_selinux) == 0) { - fprintf (stderr, _("%s: failure while writing changes to %s\n"), Prog, gr_dbname ()); - SYSLOG ((LOG_ERR, "failure while writing changes to %s", gr_dbname ())); -- fail_exit (E_GRP_UPDATE); -+ fail_exit (E_GRP_UPDATE, process_selinux); - } -- if (gr_unlock (true) == 0) { -+ if (gr_unlock (process_selinux) == 0) { - fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, gr_dbname ()); - SYSLOG ((LOG_ERR, "failed to unlock %s", gr_dbname ())); - /* continue */ -@@ -431,14 +442,14 @@ static void close_files (void) - - #ifdef SHADOWGRP - if (is_shadow_grp) { -- if (sgr_close (true) == 0) { -+ if (sgr_close (process_selinux) == 0) { - fprintf (stderr, - _("%s: failure while writing changes to %s\n"), Prog, sgr_dbname ()); - SYSLOG ((LOG_ERR, "failure while writing changes to %s", sgr_dbname ())); -- fail_exit (E_GRP_UPDATE); -+ fail_exit (E_GRP_UPDATE, process_selinux); - } - -- if (sgr_unlock (true) == 0) { -+ if (sgr_unlock (process_selinux) == 0) { - fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, sgr_dbname ()); - SYSLOG ((LOG_ERR, "failed to unlock %s", sgr_dbname ())); - /* continue */ -@@ -449,12 +460,12 @@ static void close_files (void) - - #ifdef ENABLE_SUBIDS - if (is_sub_uid) { -- if (sub_uid_close (true) == 0) { -+ if (sub_uid_close (process_selinux) == 0) { - fprintf (stderr, _("%s: failure while writing changes to %s\n"), Prog, sub_uid_dbname ()); - SYSLOG ((LOG_ERR, "failure while writing changes to %s", sub_uid_dbname ())); -- fail_exit (E_SUB_UID_UPDATE); -+ fail_exit (E_SUB_UID_UPDATE, process_selinux); - } -- if (sub_uid_unlock (true) == 0) { -+ if (sub_uid_unlock (process_selinux) == 0) { - fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, sub_uid_dbname ()); - SYSLOG ((LOG_ERR, "failed to unlock %s", sub_uid_dbname ())); - /* continue */ -@@ -463,12 +474,12 @@ static void close_files (void) - } - - if (is_sub_gid) { -- if (sub_gid_close (true) == 0) { -+ if (sub_gid_close (process_selinux) == 0) { - fprintf (stderr, _("%s: failure while writing changes to %s\n"), Prog, sub_gid_dbname ()); - SYSLOG ((LOG_ERR, "failure while writing changes to %s", sub_gid_dbname ())); -- fail_exit (E_SUB_GID_UPDATE); -+ fail_exit (E_SUB_GID_UPDATE, process_selinux); - } -- if (sub_gid_unlock (true) == 0) { -+ if (sub_gid_unlock (process_selinux) == 0) { - fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, sub_gid_dbname ()); - SYSLOG ((LOG_ERR, "failed to unlock %s", sub_gid_dbname ())); - /* continue */ -@@ -481,24 +492,24 @@ static void close_files (void) - /* - * fail_exit - exit with a failure code after unlocking the files - */ --static void fail_exit (int code) -+static void fail_exit (int code, bool process_selinux) - { - if (pw_locked) { -- if (pw_unlock (true) == 0) { -+ if (pw_unlock (process_selinux) == 0) { - fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, pw_dbname ()); - SYSLOG ((LOG_ERR, "failed to unlock %s", pw_dbname ())); - /* continue */ - } - } - if (gr_locked) { -- if (gr_unlock (true) == 0) { -+ if (gr_unlock (process_selinux) == 0) { - fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, gr_dbname ()); - SYSLOG ((LOG_ERR, "failed to unlock %s", gr_dbname ())); - /* continue */ - } - } - if (spw_locked) { -- if (spw_unlock (true) == 0) { -+ if (spw_unlock (process_selinux) == 0) { - fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, spw_dbname ()); - SYSLOG ((LOG_ERR, "failed to unlock %s", spw_dbname ())); - /* continue */ -@@ -506,7 +517,7 @@ static void fail_exit (int code) - } - #ifdef SHADOWGRP - if (sgr_locked) { -- if (sgr_unlock (true) == 0) { -+ if (sgr_unlock (process_selinux) == 0) { - fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, sgr_dbname ()); - SYSLOG ((LOG_ERR, "failed to unlock %s", sgr_dbname ())); - /* continue */ -@@ -515,14 +526,14 @@ static void fail_exit (int code) - #endif /* SHADOWGRP */ - #ifdef ENABLE_SUBIDS - if (sub_uid_locked) { -- if (sub_uid_unlock (true) == 0) { -+ if (sub_uid_unlock (process_selinux) == 0) { - fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, sub_uid_dbname ()); - SYSLOG ((LOG_ERR, "failed to unlock %s", sub_uid_dbname ())); - /* continue */ - } - } - if (sub_gid_locked) { -- if (sub_gid_unlock (true) == 0) { -+ if (sub_gid_unlock (process_selinux) == 0) { - fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, sub_gid_dbname ()); - SYSLOG ((LOG_ERR, "failed to unlock %s", sub_gid_dbname ())); - /* continue */ -@@ -545,45 +556,45 @@ static void fail_exit (int code) - * open_files() opens the two password files. - */ - --static void open_files (void) -+static void open_files (bool process_selinux) - { - if (pw_lock () == 0) { - fprintf (stderr, - _("%s: cannot lock %s; try again later.\n"), - Prog, pw_dbname ()); -- fail_exit (E_PW_UPDATE); -+ fail_exit (E_PW_UPDATE, process_selinux); - } - pw_locked = true; - if (pw_open (O_CREAT | O_RDWR) == 0) { - fprintf (stderr, - _("%s: cannot open %s\n"), Prog, pw_dbname ()); -- fail_exit (E_PW_UPDATE); -+ fail_exit (E_PW_UPDATE, process_selinux); - } - if (is_shadow_pwd) { - if (spw_lock () == 0) { - fprintf (stderr, - _("%s: cannot lock %s; try again later.\n"), - Prog, spw_dbname ()); -- fail_exit (E_PW_UPDATE); -+ fail_exit (E_PW_UPDATE, process_selinux); - } - spw_locked = true; - if (spw_open (O_CREAT | O_RDWR) == 0) { - fprintf (stderr, - _("%s: cannot open %s\n"), - Prog, spw_dbname ()); -- fail_exit (E_PW_UPDATE); -+ fail_exit (E_PW_UPDATE, process_selinux); - } - } - if (gr_lock () == 0) { - fprintf (stderr, - _("%s: cannot lock %s; try again later.\n"), - Prog, gr_dbname ()); -- fail_exit (E_GRP_UPDATE); -+ fail_exit (E_GRP_UPDATE, process_selinux); - } - gr_locked = true; - if (gr_open (O_CREAT | O_RDWR) == 0) { - fprintf (stderr, _("%s: cannot open %s\n"), Prog, gr_dbname ()); -- fail_exit (E_GRP_UPDATE); -+ fail_exit (E_GRP_UPDATE, process_selinux); - } - #ifdef SHADOWGRP - if (is_shadow_grp) { -@@ -591,13 +602,13 @@ static void open_files (void) - fprintf (stderr, - _("%s: cannot lock %s; try again later.\n"), - Prog, sgr_dbname ()); -- fail_exit (E_GRP_UPDATE); -+ fail_exit (E_GRP_UPDATE, process_selinux); - } - sgr_locked= true; - if (sgr_open (O_CREAT | O_RDWR) == 0) { - fprintf (stderr, _("%s: cannot open %s\n"), - Prog, sgr_dbname ()); -- fail_exit (E_GRP_UPDATE); -+ fail_exit (E_GRP_UPDATE, process_selinux); - } - } - #endif /* SHADOWGRP */ -@@ -607,13 +618,13 @@ static void open_files (void) - fprintf (stderr, - _("%s: cannot lock %s; try again later.\n"), - Prog, sub_uid_dbname ()); -- fail_exit (E_SUB_UID_UPDATE); -+ fail_exit (E_SUB_UID_UPDATE, process_selinux); - } - sub_uid_locked = true; - if (sub_uid_open (O_CREAT | O_RDWR) == 0) { - fprintf (stderr, - _("%s: cannot open %s\n"), Prog, sub_uid_dbname ()); -- fail_exit (E_SUB_UID_UPDATE); -+ fail_exit (E_SUB_UID_UPDATE, process_selinux); - } - } - if (is_sub_gid) { -@@ -621,13 +632,13 @@ static void open_files (void) - fprintf (stderr, - _("%s: cannot lock %s; try again later.\n"), - Prog, sub_gid_dbname ()); -- fail_exit (E_SUB_GID_UPDATE); -+ fail_exit (E_SUB_GID_UPDATE, process_selinux); - } - sub_gid_locked = true; - if (sub_gid_open (O_CREAT | O_RDWR) == 0) { - fprintf (stderr, - _("%s: cannot open %s\n"), Prog, sub_gid_dbname ()); -- fail_exit (E_SUB_GID_UPDATE); -+ fail_exit (E_SUB_GID_UPDATE, process_selinux); - } - } - #endif /* ENABLE_SUBIDS */ -@@ -639,13 +650,13 @@ static void open_files (void) - * update_user() deletes the password file entries for this user - * and will update the group entries as required. - */ --static void update_user (void) -+static void update_user (bool process_selinux) - { - if (pw_remove (user_name) == 0) { - fprintf (stderr, - _("%s: cannot remove entry '%s' from %s\n"), - Prog, user_name, pw_dbname ()); -- fail_exit (E_PW_UPDATE); -+ fail_exit (E_PW_UPDATE, process_selinux); - } - if ( is_shadow_pwd - && (spw_locate (user_name) != NULL) -@@ -653,20 +664,20 @@ static void update_user (void) - fprintf (stderr, - _("%s: cannot remove entry '%s' from %s\n"), - Prog, user_name, spw_dbname ()); -- fail_exit (E_PW_UPDATE); -+ fail_exit (E_PW_UPDATE, process_selinux); - } - #ifdef ENABLE_SUBIDS - if (is_sub_uid && sub_uid_remove(user_name, 0, ULONG_MAX) == 0) { - fprintf (stderr, - _("%s: cannot remove entry %lu from %s\n"), - Prog, (unsigned long)user_id, sub_uid_dbname ()); -- fail_exit (E_SUB_UID_UPDATE); -+ fail_exit (E_SUB_UID_UPDATE, process_selinux); - } - if (is_sub_gid && sub_gid_remove(user_name, 0, ULONG_MAX) == 0) { - fprintf (stderr, - _("%s: cannot remove entry %lu from %s\n"), - Prog, (unsigned long)user_id, sub_gid_dbname ()); -- fail_exit (E_SUB_GID_UPDATE); -+ fail_exit (E_SUB_GID_UPDATE, process_selinux); - } - #endif /* ENABLE_SUBIDS */ - #ifdef WITH_AUDIT -@@ -905,6 +916,8 @@ int main (int argc, char **argv) - int retval; - #endif /* USE_PAM */ - #endif /* ACCT_TOOLS_SETUID */ -+ struct option_flags flags; -+ bool process_selinux; - - log_set_progname(Prog); - log_set_logfd(stderr); -@@ -954,9 +967,10 @@ int main (int argc, char **argv) - rflg = true; - break; - case 'R': /* no-op, handled in process_root_flag () */ -- Rflg = true; -+ flags.chroot = true; - break; - case 'P': /* no-op, handled in process_prefix_flag () */ -+ flags.prefix = true; - break; - #ifdef WITH_SELINUX - case 'Z': -@@ -982,6 +996,7 @@ int main (int argc, char **argv) - } - } - } -+ process_selinux = !flags.chroot && !flags.prefix; - - if ((optind + 1) != argc) { - usage (E_USAGE); -@@ -1046,7 +1061,7 @@ int main (int argc, char **argv) - pw_open(O_RDONLY); - pwd = pw_locate (user_name); /* we care only about local users */ - if (NULL == pwd) { -- pw_close(true); -+ pw_close(process_selinux); - fprintf (stderr, _("%s: user '%s' does not exist\n"), - Prog, user_name); - #ifdef WITH_AUDIT -@@ -1065,7 +1080,7 @@ int main (int argc, char **argv) - } else { - user_home = xstrdup(pwd->pw_dir); - } -- pw_close(true); -+ pw_close(process_selinux); - } - #ifdef WITH_TCB - if (shadowtcb_set_user (user_name) == SHADOWTCB_FAILURE) { -@@ -1077,7 +1092,7 @@ int main (int argc, char **argv) - * Note: This is a best effort basis. The user may log in between, - * a cron job may be started on her behalf, etc. - */ -- if (streq(prefix, "") && !Rflg && user_busy(user_name, user_id) != 0) { -+ if (streq(prefix, "") && !flags.chroot && user_busy(user_name, user_id) != 0) { - if (!fflg) { - #ifdef WITH_AUDIT - audit_logger (AUDIT_DEL_USER, Prog, -@@ -1093,9 +1108,9 @@ int main (int argc, char **argv) - * Do the hard stuff - open the files, create the user entries, - * create the home directory, then close and update the files. - */ -- open_files (); -- update_user (); -- update_groups (); -+ open_files (process_selinux); -+ update_user (process_selinux); -+ update_groups (process_selinux); - - if (rflg) { - if (remove_mailbox ()) { -@@ -1203,7 +1218,7 @@ int main (int argc, char **argv) - "delete-selinux-user-mapping", - user_name, user_id, SHADOW_AUDIT_FAILURE); - #endif /* WITH_AUDIT */ -- fail_exit (E_SE_UPDATE); -+ fail_exit (E_SE_UPDATE, process_selinux); - } - } - #endif /* WITH_SELINUX */ -@@ -1214,7 +1229,7 @@ int main (int argc, char **argv) - */ - if (streq(prefix, "")) - user_cancel (user_name); -- close_files (); -+ close_files (&flags); - - if (run_parts ("/etc/shadow-maint/userdel-post.d", user_name, "userdel")) { - exit(1); -diff -up shadow-4.18.0/src/usermod.c.test-chroot2 shadow-4.18.0/src/usermod.c ---- shadow-4.18.0/src/usermod.c.test-chroot2 2025-07-08 11:22:16.257549046 +0200 -+++ shadow-4.18.0/src/usermod.c 2025-07-08 11:22:16.262408337 +0200 -@@ -97,6 +97,14 @@ - #define VALID(s) (!strpbrk(s, ":\n")) - - /* -+ * Structures -+ */ -+struct option_flags { -+ bool chroot; -+ bool prefix; -+}; -+ -+/* - * Global variables - */ - static const char Prog[] = "usermod"; -@@ -182,23 +190,23 @@ static bool sub_gid_locked = false; - /* local function prototypes */ - static int get_groups (char *); - NORETURN static void usage (int status); --static void new_pwent (struct passwd *); --static void new_spent (struct spwd *); --NORETURN static void fail_exit (int); --static void update_group_file(void); --static void update_group(const struct group *grp); -+static void new_pwent (struct passwd *, bool); -+static void new_spent (struct spwd *, bool); -+NORETURN static void fail_exit (int, bool); -+static void update_group_file(bool); -+static void update_group(const struct group *grp, bool process_selinux); - - #ifdef SHADOWGRP --static void update_gshadow_file(void); --static void update_gshadow(const struct sgrp *sgrp); -+static void update_gshadow_file(bool process_selinux); -+static void update_gshadow(const struct sgrp *sgrp, bool process_selinux); - #endif --static void grp_update (void); -+static void grp_update (bool process_selinux); - --static void process_flags (int, char **); --static void close_files (void); --static void open_files (void); --static void usr_update (void); --static void move_home (void); -+static void process_flags (int, char **, struct option_flags *); -+static void close_files (struct option_flags *); -+static void open_files (bool process_selinux); -+static void usr_update (struct option_flags *flags); -+static void move_home (bool process_selinux); - #ifdef ENABLE_LASTLOG - static void update_lastlog (void); - #endif /* ENABLE_LASTLOG */ -@@ -466,7 +474,7 @@ static char *new_pw_passwd (char *pw_pas - * new_pwent() takes all of the values that have been entered and fills - * in a (struct passwd) with them. - */ --static void new_pwent (struct passwd *pwent) -+static void new_pwent (struct passwd *pwent, bool process_selinux) - { - if (lflg) { - if (pw_locate (user_newname) != NULL) { -@@ -477,7 +485,7 @@ static void new_pwent (struct passwd *pw - fprintf (stderr, - _("%s: user '%s' already exists in %s\n"), - Prog, user_newname, pw_dbname ()); -- fail_exit (E_NAME_IN_USE); -+ fail_exit (E_NAME_IN_USE, process_selinux); - } - #ifdef WITH_AUDIT - audit_logger (AUDIT_USER_MGMT, Prog, -@@ -563,14 +571,14 @@ static void new_pwent (struct passwd *pw - * new_spent() takes all of the values that have been entered and fills - * in a (struct spwd) with them. - */ --static void new_spent (struct spwd *spent) -+static void new_spent (struct spwd *spent, bool process_selinux) - { - if (lflg) { - if (spw_locate (user_newname) != NULL) { - fprintf (stderr, - _("%s: user '%s' already exists in %s\n"), - Prog, user_newname, spw_dbname ()); -- fail_exit (E_NAME_IN_USE); -+ fail_exit (E_NAME_IN_USE, process_selinux); - } - spent->sp_namp = xstrdup (user_newname); - } -@@ -629,10 +637,10 @@ static void new_spent (struct spwd *spen - */ - NORETURN - static void --fail_exit (int code) -+fail_exit (int code, bool process_selinux) - { - if (gr_locked) { -- if (gr_unlock (true) == 0) { -+ if (gr_unlock (process_selinux) == 0) { - fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, gr_dbname ()); - SYSLOG ((LOG_ERR, "failed to unlock %s", gr_dbname ())); - /* continue */ -@@ -640,7 +648,7 @@ fail_exit (int code) - } - #ifdef SHADOWGRP - if (sgr_locked) { -- if (sgr_unlock (true) == 0) { -+ if (sgr_unlock (process_selinux) == 0) { - fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, sgr_dbname ()); - SYSLOG ((LOG_ERR, "failed to unlock %s", sgr_dbname ())); - /* continue */ -@@ -648,14 +656,14 @@ fail_exit (int code) - } - #endif - if (spw_locked) { -- if (spw_unlock (true) == 0) { -+ if (spw_unlock (process_selinux) == 0) { - fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, spw_dbname ()); - SYSLOG ((LOG_ERR, "failed to unlock %s", spw_dbname ())); - /* continue */ - } - } - if (pw_locked) { -- if (pw_unlock (true) == 0) { -+ if (pw_unlock (process_selinux) == 0) { - fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, pw_dbname ()); - SYSLOG ((LOG_ERR, "failed to unlock %s", pw_dbname ())); - /* continue */ -@@ -663,14 +671,14 @@ fail_exit (int code) - } - #ifdef ENABLE_SUBIDS - if (sub_uid_locked) { -- if (sub_uid_unlock (true) == 0) { -+ if (sub_uid_unlock (process_selinux) == 0) { - fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, sub_uid_dbname ()); - SYSLOG ((LOG_ERR, "failed to unlock %s", sub_uid_dbname ())); - /* continue */ - } - } - if (sub_gid_locked) { -- if (sub_gid_unlock (true) == 0) { -+ if (sub_gid_unlock (process_selinux) == 0) { - fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, sub_gid_dbname ()); - SYSLOG ((LOG_ERR, "failed to unlock %s", sub_gid_dbname ())); - /* continue */ -@@ -688,7 +696,7 @@ fail_exit (int code) - - - static void --update_group_file(void) -+update_group_file(bool process_selinux) - { - const struct group *grp; - -@@ -697,12 +705,12 @@ update_group_file(void) - * the user is a member of. - */ - while ((grp = gr_next()) != NULL) -- update_group(grp); -+ update_group(grp, process_selinux); - } - - - static void --update_group(const struct group *grp) -+update_group(const struct group *grp, bool process_selinux) - { - bool changed; - bool is_member; -@@ -735,7 +743,7 @@ update_group(const struct group *grp) - fprintf (stderr, - _("%s: Out of memory. Cannot update %s.\n"), - Prog, gr_dbname ()); -- fail_exit (E_GRP_UPDATE); -+ fail_exit (E_GRP_UPDATE, process_selinux); - } - - if (was_member) { -@@ -804,7 +812,7 @@ update_group(const struct group *grp) - _("%s: failed to prepare the new %s entry '%s'\n"), - Prog, gr_dbname (), ngrp->gr_name); - SYSLOG ((LOG_WARN, "failed to prepare the new %s entry '%s'", gr_dbname (), ngrp->gr_name)); -- fail_exit (E_GRP_UPDATE); -+ fail_exit (E_GRP_UPDATE, process_selinux); - } - - free_ngrp: -@@ -814,7 +822,7 @@ free_ngrp: - - #ifdef SHADOWGRP - static void --update_gshadow_file(void) -+update_gshadow_file(bool process_selinux) - { - const struct sgrp *sgrp; - -@@ -823,14 +831,14 @@ update_gshadow_file(void) - * that the user is a member of. - */ - while ((sgrp = sgr_next()) != NULL) -- update_gshadow(sgrp); -+ update_gshadow(sgrp, process_selinux); - } - #endif /* SHADOWGRP */ - - - #ifdef SHADOWGRP - static void --update_gshadow(const struct sgrp *sgrp) -+update_gshadow(const struct sgrp *sgrp, bool process_selinux) - { - bool changed; - bool is_member; -@@ -873,7 +881,7 @@ update_gshadow(const struct sgrp *sgrp) - fprintf (stderr, - _("%s: Out of memory. Cannot update %s.\n"), - Prog, sgr_dbname ()); -- fail_exit (E_GRP_UPDATE); -+ fail_exit (E_GRP_UPDATE, process_selinux); - } - - if (was_admin && lflg) { -@@ -960,7 +968,7 @@ update_gshadow(const struct sgrp *sgrp) - Prog, sgr_dbname (), nsgrp->sg_namp); - SYSLOG ((LOG_WARN, "failed to prepare the new %s entry '%s'", - sgr_dbname (), nsgrp->sg_namp)); -- fail_exit (E_GRP_UPDATE); -+ fail_exit (E_GRP_UPDATE, process_selinux); - } - - free_nsgrp: -@@ -975,12 +983,12 @@ free_nsgrp: - * grp_update() takes the secondary group set given in user_groups and - * adds the user to each group given by that set. - */ --static void grp_update (void) -+static void grp_update (bool process_selinux) - { -- update_group_file(); -+ update_group_file(process_selinux); - #ifdef SHADOWGRP - if (is_shadow_grp) { -- update_gshadow_file(); -+ update_gshadow_file(process_selinux); - } - #endif - } -@@ -993,7 +1001,7 @@ static void grp_update (void) - * are checked for sanity. - */ - static void --process_flags(int argc, char **argv) -+process_flags(int argc, char **argv, struct option_flags *flags) - { - struct stat st; - bool anyflag = false; -@@ -1158,8 +1166,10 @@ process_flags(int argc, char **argv) - rflg = true; - break; - case 'R': /* no-op, handled in process_root_flag () */ -+ flags->chroot = true; - break; - case 'P': /* no-op, handled in process_prefix_flag () */ -+ flags->prefix = true; - break; - case 's': - if ( ( !VALID (optarg) ) -@@ -1467,51 +1477,55 @@ process_flags(int argc, char **argv) - * close_files() closes all of the files that were opened for this new - * user. This causes any modified entries to be written out. - */ --static void close_files (void) -+static void close_files (struct option_flags *flags) - { -- if (pw_close (true) == 0) { -+ bool process_selinux; -+ -+ process_selinux = !flags->chroot && !flags->prefix; -+ -+ if (pw_close (process_selinux) == 0) { - fprintf (stderr, - _("%s: failure while writing changes to %s\n"), - Prog, pw_dbname ()); - SYSLOG ((LOG_ERR, "failure while writing changes to %s", pw_dbname ())); -- fail_exit (E_PW_UPDATE); -+ fail_exit (E_PW_UPDATE, process_selinux); - } -- if (is_shadow_pwd && (spw_close (true) == 0)) { -+ if (is_shadow_pwd && (spw_close (process_selinux) == 0)) { - fprintf (stderr, - _("%s: failure while writing changes to %s\n"), - Prog, spw_dbname ()); - SYSLOG ((LOG_ERR, - "failure while writing changes to %s", - spw_dbname ())); -- fail_exit (E_PW_UPDATE); -+ fail_exit (E_PW_UPDATE, process_selinux); - } - - if (Gflg || lflg) { -- if (gr_close (true) == 0) { -+ if (gr_close (process_selinux) == 0) { - fprintf (stderr, - _("%s: failure while writing changes to %s\n"), - Prog, gr_dbname ()); - SYSLOG ((LOG_ERR, - "failure while writing changes to %s", - gr_dbname ())); -- fail_exit (E_GRP_UPDATE); -+ fail_exit (E_GRP_UPDATE, process_selinux); - } - #ifdef SHADOWGRP - if (is_shadow_grp) { -- if (sgr_close (true) == 0) { -+ if (sgr_close (process_selinux) == 0) { - fprintf (stderr, - _("%s: failure while writing changes to %s\n"), - Prog, sgr_dbname ()); - SYSLOG ((LOG_ERR, - "failure while writing changes to %s", - sgr_dbname ())); -- fail_exit (E_GRP_UPDATE); -+ fail_exit (E_GRP_UPDATE, process_selinux); - } - } - #endif - #ifdef SHADOWGRP - if (is_shadow_grp) { -- if (sgr_unlock (true) == 0) { -+ if (sgr_unlock (process_selinux) == 0) { - fprintf (stderr, - _("%s: failed to unlock %s\n"), - Prog, sgr_dbname ()); -@@ -1522,7 +1536,7 @@ static void close_files (void) - } - } - #endif -- if (gr_unlock (true) == 0) { -+ if (gr_unlock (process_selinux) == 0) { - fprintf (stderr, - _("%s: failed to unlock %s\n"), - Prog, gr_dbname ()); -@@ -1534,7 +1548,7 @@ static void close_files (void) - } - - if (is_shadow_pwd) { -- if (spw_unlock (true) == 0) { -+ if (spw_unlock (process_selinux) == 0) { - fprintf (stderr, - _("%s: failed to unlock %s\n"), - Prog, spw_dbname ()); -@@ -1544,7 +1558,7 @@ static void close_files (void) - /* continue */ - } - } -- if (pw_unlock (true) == 0) { -+ if (pw_unlock (process_selinux) == 0) { - fprintf (stderr, - _("%s: failed to unlock %s\n"), - Prog, pw_dbname ()); -@@ -1561,12 +1575,12 @@ static void close_files (void) - - #ifdef ENABLE_SUBIDS - if (vflg || Vflg) { -- if (sub_uid_close (true) == 0) { -+ if (sub_uid_close (process_selinux) == 0) { - fprintf (stderr, _("%s: failure while writing changes to %s\n"), Prog, sub_uid_dbname ()); - SYSLOG ((LOG_ERR, "failure while writing changes to %s", sub_uid_dbname ())); -- fail_exit (E_SUB_UID_UPDATE); -+ fail_exit (E_SUB_UID_UPDATE, process_selinux); - } -- if (sub_uid_unlock (true) == 0) { -+ if (sub_uid_unlock (process_selinux) == 0) { - fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, sub_uid_dbname ()); - SYSLOG ((LOG_ERR, "failed to unlock %s", sub_uid_dbname ())); - /* continue */ -@@ -1574,12 +1588,12 @@ static void close_files (void) - sub_uid_locked = false; - } - if (wflg || Wflg) { -- if (sub_gid_close (true) == 0) { -+ if (sub_gid_close (process_selinux) == 0) { - fprintf (stderr, _("%s: failure while writing changes to %s\n"), Prog, sub_gid_dbname ()); - SYSLOG ((LOG_ERR, "failure while writing changes to %s", sub_gid_dbname ())); -- fail_exit (E_SUB_GID_UPDATE); -+ fail_exit (E_SUB_GID_UPDATE, process_selinux); - } -- if (sub_gid_unlock (true) == 0) { -+ if (sub_gid_unlock (process_selinux) == 0) { - fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, sub_gid_dbname ()); - SYSLOG ((LOG_ERR, "failed to unlock %s", sub_gid_dbname ())); - /* continue */ -@@ -1604,33 +1618,33 @@ static void close_files (void) - * - * open_files() opens the two password files. - */ --static void open_files (void) -+static void open_files (bool process_selinux) - { - if (pw_lock () == 0) { - fprintf (stderr, - _("%s: cannot lock %s; try again later.\n"), - Prog, pw_dbname ()); -- fail_exit (E_PW_UPDATE); -+ fail_exit (E_PW_UPDATE, process_selinux); - } - pw_locked = true; - if (pw_open (O_CREAT | O_RDWR) == 0) { - fprintf (stderr, - _("%s: cannot open %s\n"), - Prog, pw_dbname ()); -- fail_exit (E_PW_UPDATE); -+ fail_exit (E_PW_UPDATE, process_selinux); - } - if (is_shadow_pwd && (spw_lock () == 0)) { - fprintf (stderr, - _("%s: cannot lock %s; try again later.\n"), - Prog, spw_dbname ()); -- fail_exit (E_PW_UPDATE); -+ fail_exit (E_PW_UPDATE, process_selinux); - } - spw_locked = true; - if (is_shadow_pwd && (spw_open (O_CREAT | O_RDWR) == 0)) { - fprintf (stderr, - _("%s: cannot open %s\n"), - Prog, spw_dbname ()); -- fail_exit (E_PW_UPDATE); -+ fail_exit (E_PW_UPDATE, process_selinux); - } - - if (Gflg || lflg) { -@@ -1642,28 +1656,28 @@ static void open_files (void) - fprintf (stderr, - _("%s: cannot lock %s; try again later.\n"), - Prog, gr_dbname ()); -- fail_exit (E_GRP_UPDATE); -+ fail_exit (E_GRP_UPDATE, process_selinux); - } - gr_locked = true; - if (gr_open (O_CREAT | O_RDWR) == 0) { - fprintf (stderr, - _("%s: cannot open %s\n"), - Prog, gr_dbname ()); -- fail_exit (E_GRP_UPDATE); -+ fail_exit (E_GRP_UPDATE, process_selinux); - } - #ifdef SHADOWGRP - if (is_shadow_grp && (sgr_lock () == 0)) { - fprintf (stderr, - _("%s: cannot lock %s; try again later.\n"), - Prog, sgr_dbname ()); -- fail_exit (E_GRP_UPDATE); -+ fail_exit (E_GRP_UPDATE, process_selinux); - } - sgr_locked = true; - if (is_shadow_grp && (sgr_open (O_CREAT | O_RDWR) == 0)) { - fprintf (stderr, - _("%s: cannot open %s\n"), - Prog, sgr_dbname ()); -- fail_exit (E_GRP_UPDATE); -+ fail_exit (E_GRP_UPDATE, process_selinux); - } - #endif - } -@@ -1673,14 +1687,14 @@ static void open_files (void) - fprintf (stderr, - _("%s: cannot lock %s; try again later.\n"), - Prog, sub_uid_dbname ()); -- fail_exit (E_SUB_UID_UPDATE); -+ fail_exit (E_SUB_UID_UPDATE, process_selinux); - } - sub_uid_locked = true; - if (sub_uid_open (O_CREAT | O_RDWR) == 0) { - fprintf (stderr, - _("%s: cannot open %s\n"), - Prog, sub_uid_dbname ()); -- fail_exit (E_SUB_UID_UPDATE); -+ fail_exit (E_SUB_UID_UPDATE, process_selinux); - } - } - if (wflg || Wflg) { -@@ -1688,14 +1702,14 @@ static void open_files (void) - fprintf (stderr, - _("%s: cannot lock %s; try again later.\n"), - Prog, sub_gid_dbname ()); -- fail_exit (E_SUB_GID_UPDATE); -+ fail_exit (E_SUB_GID_UPDATE, process_selinux); - } - sub_gid_locked = true; - if (sub_gid_open (O_CREAT | O_RDWR) == 0) { - fprintf (stderr, - _("%s: cannot open %s\n"), - Prog, sub_gid_dbname ()); -- fail_exit (E_SUB_GID_UPDATE); -+ fail_exit (E_SUB_GID_UPDATE, process_selinux); - } - } - #endif /* ENABLE_SUBIDS */ -@@ -1707,13 +1721,15 @@ static void open_files (void) - * usr_update() creates the password file entries for this user and - * will update the group entries if required. - */ --static void usr_update (void) -+static void usr_update (struct option_flags *flags) - { - struct passwd pwent; - const struct passwd *pwd; -- - struct spwd spent; - const struct spwd *spwd = NULL; -+ bool process_selinux; -+ -+ process_selinux = !flags->chroot && !flags->prefix; - - /* - * Locate the entry in /etc/passwd, which MUST exist. -@@ -1723,10 +1739,10 @@ static void usr_update (void) - fprintf (stderr, - _("%s: user '%s' does not exist in %s\n"), - Prog, user_name, pw_dbname ()); -- fail_exit (E_NOTFOUND); -+ fail_exit (E_NOTFOUND, process_selinux); - } - pwent = *pwd; -- new_pwent (&pwent); -+ new_pwent (&pwent, process_selinux); - - - /* If the shadow file does not exist, it won't be created */ -@@ -1735,7 +1751,7 @@ static void usr_update (void) - if (NULL != spwd) { - /* Update the shadow entry if it exists */ - spent = *spwd; -- new_spent (&spent); -+ new_spent (&spent, process_selinux); - } else if ( ( pflg - && streq(pwent.pw_passwd, SHADOW_PASSWD_STRING)) - || eflg || fflg) { -@@ -1766,7 +1782,7 @@ static void usr_update (void) - spent.sp_inact = -1; - spent.sp_expire = -1; - spent.sp_flag = SHADOW_SP_FLAG_UNSET; -- new_spent (&spent); -+ new_spent (&spent, process_selinux); - spwd = &spent; /* entry needs to be committed */ - } - } -@@ -1777,13 +1793,13 @@ static void usr_update (void) - fprintf (stderr, - _("%s: failed to prepare the new %s entry '%s'\n"), - Prog, pw_dbname (), pwent.pw_name); -- fail_exit (E_PW_UPDATE); -+ fail_exit (E_PW_UPDATE, process_selinux); - } - if (lflg && (pw_remove (user_name) == 0)) { - fprintf (stderr, - _("%s: cannot remove entry '%s' from %s\n"), - Prog, user_name, pw_dbname ()); -- fail_exit (E_PW_UPDATE); -+ fail_exit (E_PW_UPDATE, process_selinux); - } - } - if ((NULL != spwd) && (lflg || eflg || fflg || pflg || Lflg || Uflg)) { -@@ -1791,13 +1807,13 @@ static void usr_update (void) - fprintf (stderr, - _("%s: failed to prepare the new %s entry '%s'\n"), - Prog, spw_dbname (), spent.sp_namp); -- fail_exit (E_PW_UPDATE); -+ fail_exit (E_PW_UPDATE, process_selinux); - } - if (lflg && (spw_remove (user_name) == 0)) { - fprintf (stderr, - _("%s: cannot remove entry '%s' from %s\n"), - Prog, user_name, spw_dbname ()); -- fail_exit (E_PW_UPDATE); -+ fail_exit (E_PW_UPDATE, process_selinux); - } - } - } -@@ -1808,7 +1824,7 @@ static void usr_update (void) - * move_home() moves the user's home directory to a new location. The - * files will be copied if the directory cannot simply be renamed. - */ --static void move_home (void) -+static void move_home (bool process_selinux) - { - struct stat sb; - -@@ -1820,7 +1836,7 @@ static void move_home (void) - fprintf (stderr, - _("%s: directory %s exists\n"), - Prog, user_newhome); -- fail_exit (E_HOMEDIR); -+ fail_exit (E_HOMEDIR, process_selinux); - } - - if (stat (prefix_user_home, &sb) == 0) { -@@ -1834,7 +1850,7 @@ static void move_home (void) - "not a directory. It is not removed and no " - "home directories are created.\n"), - Prog, user_home); -- fail_exit (E_HOMEDIR); -+ fail_exit (E_HOMEDIR, process_selinux); - } - - #ifdef WITH_AUDIT -@@ -1855,7 +1871,7 @@ static void move_home (void) - fprintf (stderr, - _("%s: Failed to change ownership of the home directory"), - Prog); -- fail_exit (E_HOMEDIR); -+ fail_exit (E_HOMEDIR, process_selinux); - } - #ifdef WITH_AUDIT - audit_logger (AUDIT_USER_MGMT, Prog, -@@ -1870,7 +1886,7 @@ static void move_home (void) - fprintf (stderr, - _("%s: error: cannot move subvolume from %s to %s - different device\n"), - Prog, prefix_user_home, prefix_user_newhome); -- fail_exit (E_HOMEDIR); -+ fail_exit (E_HOMEDIR, process_selinux); - } - #endif - -@@ -1901,7 +1917,7 @@ static void move_home (void) - fprintf (stderr, - _("%s: cannot rename directory %s to %s\n"), - Prog, prefix_user_home, prefix_user_newhome); -- fail_exit (E_HOMEDIR); -+ fail_exit (E_HOMEDIR, process_selinux); - } - } else { - fprintf (stderr, -@@ -2160,6 +2176,8 @@ int main (int argc, char **argv) - int retval; - #endif /* USE_PAM */ - #endif /* ACCT_TOOLS_SETUID */ -+ struct option_flags flags; -+ bool process_selinux; - - log_set_progname(Prog); - log_set_logfd(stderr); -@@ -2189,7 +2207,8 @@ int main (int argc, char **argv) - is_sub_gid = sub_gid_file_present (); - #endif /* ENABLE_SUBIDS */ - -- process_flags (argc, argv); -+ process_flags (argc, argv, &flags); -+ process_selinux = !flags.chroot && !flags.prefix; - - /* - * The home directory, the username and the user's UID should not -@@ -2251,13 +2270,13 @@ int main (int argc, char **argv) - * Do the hard stuff - open the files, change the user entries, - * change the home directory, then close and update the files. - */ -- open_files (); -+ open_files (process_selinux); - if ( cflg || dflg || eflg || fflg || gflg || Lflg || lflg || pflg - || sflg || uflg || Uflg) { -- usr_update (); -+ usr_update (&flags); - } - if (Gflg || lflg) { -- grp_update (); -+ grp_update (process_selinux); - } - #ifdef ENABLE_SUBIDS - if (Vflg) { -@@ -2273,7 +2292,7 @@ int main (int argc, char **argv) - (uintmax_t) ptr->range.first, - (uintmax_t) ptr->range.last, - sub_uid_dbname()); -- fail_exit (E_SUB_UID_UPDATE); -+ fail_exit (E_SUB_UID_UPDATE, process_selinux); - } - } - } -@@ -2290,7 +2309,7 @@ int main (int argc, char **argv) - (uintmax_t) ptr->range.first, - (uintmax_t) ptr->range.last, - sub_uid_dbname()); -- fail_exit (E_SUB_UID_UPDATE); -+ fail_exit (E_SUB_UID_UPDATE, process_selinux); - } - } - } -@@ -2307,7 +2326,7 @@ int main (int argc, char **argv) - (uintmax_t) ptr->range.first, - (uintmax_t) ptr->range.last, - sub_gid_dbname()); -- fail_exit (E_SUB_GID_UPDATE); -+ fail_exit (E_SUB_GID_UPDATE, process_selinux); - } - } - } -@@ -2324,12 +2343,12 @@ int main (int argc, char **argv) - (uintmax_t) ptr->range.first, - (uintmax_t) ptr->range.last, - sub_gid_dbname()); -- fail_exit (E_SUB_GID_UPDATE); -+ fail_exit (E_SUB_GID_UPDATE, process_selinux); - } - } - } - #endif /* ENABLE_SUBIDS */ -- close_files (); -+ close_files (&flags); - - #ifdef WITH_TCB - if ( (lflg || uflg) -@@ -2355,7 +2374,7 @@ int main (int argc, char **argv) - user_name, user_id, - SHADOW_AUDIT_FAILURE); - #endif /* WITH_AUDIT */ -- fail_exit (E_SE_UPDATE); -+ fail_exit (E_SE_UPDATE, process_selinux); - } - } else { - if (del_seuser (user_name) != 0) { -@@ -2368,14 +2387,14 @@ int main (int argc, char **argv) - user_name, user_id, - SHADOW_AUDIT_FAILURE); - #endif /* WITH_AUDIT */ -- fail_exit (E_SE_UPDATE); -+ fail_exit (E_SE_UPDATE, process_selinux); - } - } - } - #endif /* WITH_SELINUX */ - - if (mflg) { -- move_home (); -+ move_home (process_selinux); - } - - #ifndef NO_MOVE_MAILBOX -@@ -2420,7 +2439,7 @@ int main (int argc, char **argv) - fprintf (stderr, - _("%s: Failed to change ownership of the home directory"), - Prog); -- fail_exit (E_HOMEDIR); -+ fail_exit (E_HOMEDIR, process_selinux); - } - } - } - -diff -up shadow-4.18.0/lib/cleanup_group.c.test-chroot3 shadow-4.18.0/lib/cleanup_group.c ---- shadow-4.18.0/lib/cleanup_group.c.test-chroot3 2025-07-04 11:15:02.302179208 +0200 -+++ shadow-4.18.0/lib/cleanup_group.c 2025-07-04 11:15:02.311510389 +0200 -@@ -179,9 +179,11 @@ void cleanup_report_del_group_gshadow (v - * - * 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 (true) == 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 - * - * 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 (true) == 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 ()); -diff -up shadow-4.18.0/lib/cleanup_user.c.test-chroot3 shadow-4.18.0/lib/cleanup_user.c ---- shadow-4.18.0/lib/cleanup_user.c.test-chroot3 2025-07-04 11:15:02.297132547 +0200 -+++ shadow-4.18.0/lib/cleanup_user.c 2025-07-04 11:15:02.311622726 +0200 -@@ -96,9 +96,11 @@ void cleanup_report_add_user_shadow (voi - * - * 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 (true) == 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 +117,11 @@ void cleanup_unlock_passwd (MAYBE_UNUSED - * - * 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 (true) == 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 ()); -diff -up shadow-4.18.0/lib/prototypes.h.test-chroot3 shadow-4.18.0/lib/prototypes.h ---- shadow-4.18.0/lib/prototypes.h.test-chroot3 2025-06-24 22:28:01.058453223 +0200 -+++ shadow-4.18.0/lib/prototypes.h 2025-07-04 11:15:02.311703893 +0200 -@@ -92,11 +92,11 @@ void cleanup_report_del_group_gshadow (v - void cleanup_report_mod_passwd (void *cleanup_info); - void cleanup_report_mod_group (void *cleanup_info); - void cleanup_report_mod_gshadow (void *cleanup_info); --void cleanup_unlock_group (/*@null@*/void *MAYBE_UNUSED); -+void cleanup_unlock_group (void *process_selinux); - #ifdef SHADOWGRP --void cleanup_unlock_gshadow (/*@null@*/void *MAYBE_UNUSED); -+void cleanup_unlock_gshadow (void *process_selinux); - #endif --void cleanup_unlock_passwd (/*@null@*/void *MAYBE_UNUSED); -+void cleanup_unlock_passwd (void *process_selinux); - - /* console.c */ - extern bool console (const char *); -diff -up shadow-4.18.0/src/chgpasswd.c.test-chroot3 shadow-4.18.0/src/chgpasswd.c ---- shadow-4.18.0/src/chgpasswd.c.test-chroot3 2025-07-04 11:15:02.302260729 +0200 -+++ shadow-4.18.0/src/chgpasswd.c 2025-07-04 11:15:02.312527205 +0200 -@@ -38,6 +38,12 @@ - #include "string/strcmp/streq.h" - #include "string/strtok/stpsep.h" - -+/* -+ * Structures -+ */ -+struct option_flags { -+ bool chroot; -+}; - - /* - * Global variables -@@ -68,21 +74,21 @@ static bool sgr_locked = false; - static bool gr_locked = false; - - /* local function prototypes */ --NORETURN static void fail_exit (int code); -+NORETURN static void fail_exit (int code, bool process_selinux); - NORETURN static void usage (int status); --static void process_flags (int argc, char **argv); -+static void process_flags (int argc, char **argv, struct option_flags *flags); - static void check_flags (void); - static void check_perms (void); --static void open_files (void); --static void close_files (void); -+static void open_files (bool process_selinux); -+static void close_files (struct option_flags *flags); - - /* - * fail_exit - exit with a failure code after unlocking the files - */ --static void fail_exit (int code) -+static void fail_exit (int code, bool process_selinux) - { - if (gr_locked) { -- if (gr_unlock (true) == 0) { -+ if (gr_unlock (process_selinux) == 0) { - fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, gr_dbname ()); - SYSLOG ((LOG_ERR, "failed to unlock %s", gr_dbname ())); - /* continue */ -@@ -91,7 +97,7 @@ static void fail_exit (int code) - - #ifdef SHADOWGRP - if (sgr_locked) { -- if (sgr_unlock (true) == 0) { -+ if (sgr_unlock (process_selinux) == 0) { - fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, sgr_dbname ()); - SYSLOG ((LOG_ERR, "failed to unlock %s", sgr_dbname ())); - /* continue */ -@@ -149,7 +155,7 @@ usage (int status) - * - * It will not return if an error is encountered. - */ --static void process_flags (int argc, char **argv) -+static void process_flags (int argc, char **argv, struct option_flags *flags) - { - int c; - #if defined(USE_SHA_CRYPT) || defined(USE_BCRYPT) || defined(USE_YESCRYPT) -@@ -187,6 +193,7 @@ static void process_flags (int argc, cha - md5flg = true; - break; - case 'R': /* no-op, handled in process_root_flag () */ -+ flags->chroot = true; - break; - #if defined(USE_SHA_CRYPT) || defined(USE_BCRYPT) || defined(USE_YESCRYPT) - case 's': -@@ -336,7 +343,7 @@ static void check_perms (void) - /* - * open_files - lock and open the group databases - */ --static void open_files (void) -+static void open_files (bool process_selinux) - { - /* - * Lock the group file and open it for reading and writing. This will -@@ -346,13 +353,13 @@ static void open_files (void) - fprintf (stderr, - _("%s: cannot lock %s; try again later.\n"), - Prog, gr_dbname ()); -- fail_exit (1); -+ fail_exit (1, process_selinux); - } - gr_locked = true; - if (gr_open (O_CREAT | O_RDWR) == 0) { - fprintf (stderr, - _("%s: cannot open %s\n"), Prog, gr_dbname ()); -- fail_exit (1); -+ fail_exit (1, process_selinux); - } - - #ifdef SHADOWGRP -@@ -362,13 +369,13 @@ static void open_files (void) - fprintf (stderr, - _("%s: cannot lock %s; try again later.\n"), - Prog, sgr_dbname ()); -- fail_exit (1); -+ fail_exit (1, process_selinux); - } - sgr_locked = true; - if (sgr_open (O_CREAT | O_RDWR) == 0) { - fprintf (stderr, _("%s: cannot open %s\n"), - Prog, sgr_dbname ()); -- fail_exit (1); -+ fail_exit (1, process_selinux); - } - } - #endif -@@ -377,18 +384,21 @@ static void open_files (void) - /* - * close_files - close and unlock the group databases - */ --static void close_files (void) -+static void close_files (struct option_flags *flags) - { -+ bool process_selinux; -+ -+ process_selinux = !flags->chroot; - #ifdef SHADOWGRP - if (is_shadow_grp) { -- if (sgr_close (true) == 0) { -+ if (sgr_close (process_selinux) == 0) { - fprintf (stderr, - _("%s: failure while writing changes to %s\n"), - Prog, sgr_dbname ()); - SYSLOG ((LOG_ERR, "failure while writing changes to %s", sgr_dbname ())); -- fail_exit (1); -+ fail_exit (1, process_selinux); - } -- if (sgr_unlock (true) == 0) { -+ if (sgr_unlock (process_selinux) == 0) { - fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, sgr_dbname ()); - SYSLOG ((LOG_ERR, "failed to unlock %s", sgr_dbname ())); - /* continue */ -@@ -397,14 +407,14 @@ static void close_files (void) - } - #endif - -- if (gr_close (true) == 0) { -+ if (gr_close (process_selinux) == 0) { - fprintf (stderr, - _("%s: failure while writing changes to %s\n"), - Prog, gr_dbname ()); - SYSLOG ((LOG_ERR, "failure while writing changes to %s", gr_dbname ())); -- fail_exit (1); -+ fail_exit (1, process_selinux); - } -- if (gr_unlock (true) == 0) { -+ if (gr_unlock (process_selinux) == 0) { - fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, gr_dbname ()); - SYSLOG ((LOG_ERR, "failed to unlock %s", gr_dbname ())); - /* continue */ -@@ -428,6 +438,8 @@ int main (int argc, char **argv) - struct group newgr; - bool errors = false; - intmax_t line = 0; -+ struct option_flags flags; -+ bool process_selinux; - - log_set_progname(Prog); - log_set_logfd(stderr); -@@ -444,7 +456,8 @@ int main (int argc, char **argv) - - process_root_flag ("-R", argc, argv); - -- process_flags (argc, argv); -+ process_flags (argc, argv, &flags); -+ process_selinux = !flags.chroot; - - OPENLOG (Prog); - -@@ -454,7 +467,7 @@ int main (int argc, char **argv) - is_shadow_grp = sgr_file_present (); - #endif - -- open_files (); -+ open_files (process_selinux); - - /* - * Read each line, separating the group name from the password. The -@@ -523,7 +536,7 @@ int main (int argc, char **argv) - fprintf (stderr, - _("%s: failed to crypt password with salt '%s': %s\n"), - Prog, salt, strerror (errno)); -- fail_exit (1); -+ fail_exit (1, process_selinux); - } - } - -@@ -624,10 +637,10 @@ int main (int argc, char **argv) - if (errors) { - fprintf (stderr, - _("%s: error detected, changes ignored\n"), Prog); -- fail_exit (1); -+ fail_exit (1, process_selinux); - } - -- close_files (); -+ close_files (&flags); - - nscd_flush_cache ("group"); - sssd_flush_cache (SSSD_DB_GROUP); -diff -up shadow-4.18.0/src/groupadd.c.test-chroot3 shadow-4.18.0/src/groupadd.c ---- shadow-4.18.0/src/groupadd.c.test-chroot3 2025-07-04 11:15:02.284218582 +0200 -+++ shadow-4.18.0/src/groupadd.c 2025-07-04 11:15:02.311806157 +0200 -@@ -54,6 +54,14 @@ - #define E_GRP_UPDATE 10 /* can't update group file */ - - /* -+ * Structures -+ */ -+struct option_flags { -+ bool chroot; -+ bool prefix; -+}; -+ -+/* - * Global variables - */ - static const char Prog[] = "groupadd"; -@@ -85,9 +93,9 @@ static void new_sgent (struct sgrp *sgen - #endif - static void grp_update (void); - static void check_new_name (void); --static void close_files (void); --static void open_files (void); --static void process_flags (int argc, char **argv); -+static void close_files (struct option_flags *flags); -+static void open_files (struct option_flags *flags); -+static void process_flags (int argc, char **argv, struct option_flags *flags); - static void check_flags (void); - static void check_perms (void); - -@@ -271,10 +279,14 @@ check_new_name(void) - * close_files() closes all of the files that were opened for this new - * group. This causes any modified entries to be written out. - */ --static void close_files (void) -+static void close_files (struct option_flags *flags) - { -+ bool process_selinux; -+ -+ process_selinux = !flags->chroot && !flags->prefix; -+ - /* First, write the changes in the regular group database */ -- if (gr_close (true) == 0) { -+ if (gr_close (process_selinux) == 0) { - fprintf (stderr, - _("%s: failure while writing changes to %s\n"), - Prog, gr_dbname ()); -@@ -289,13 +301,13 @@ static void close_files (void) - gr_dbname (), group_name, (unsigned int) group_id)); - del_cleanup (cleanup_report_add_group_group); - -- cleanup_unlock_group (NULL); -+ cleanup_unlock_group (&process_selinux); - del_cleanup (cleanup_unlock_group); - - /* Now, write the changes in the shadow database */ - #ifdef SHADOWGRP - if (is_shadow_grp) { -- if (sgr_close (true) == 0) { -+ if (sgr_close (process_selinux) == 0) { - fprintf (stderr, - _("%s: failure while writing changes to %s\n"), - Prog, sgr_dbname ()); -@@ -310,7 +322,7 @@ static void close_files (void) - sgr_dbname (), group_name)); - del_cleanup (cleanup_report_add_group_gshadow); - -- cleanup_unlock_gshadow (NULL); -+ cleanup_unlock_gshadow (&process_selinux); - del_cleanup (cleanup_unlock_gshadow); - } - #endif /* SHADOWGRP */ -@@ -326,8 +338,12 @@ static void close_files (void) - * - * open_files() opens the two group files. - */ --static void open_files (void) -+static void open_files (struct option_flags *flags) - { -+ bool process_selinux; -+ -+ process_selinux = !flags->chroot && !flags->prefix; -+ - /* First, lock the databases */ - if (gr_lock () == 0) { - fprintf (stderr, -@@ -335,7 +351,7 @@ static void open_files (void) - Prog, gr_dbname ()); - fail_exit (E_GRP_UPDATE); - } -- add_cleanup (cleanup_unlock_group, NULL); -+ add_cleanup (cleanup_unlock_group, &process_selinux); - - #ifdef SHADOWGRP - if (is_shadow_grp) { -@@ -345,7 +361,7 @@ static void open_files (void) - Prog, sgr_dbname ()); - fail_exit (E_GRP_UPDATE); - } -- add_cleanup (cleanup_unlock_gshadow, NULL); -+ add_cleanup (cleanup_unlock_gshadow, &process_selinux); - } - #endif /* SHADOWGRP */ - -@@ -380,7 +396,7 @@ static void open_files (void) - * - * It will not return if an error is encountered. - */ --static void process_flags (int argc, char **argv) -+static void process_flags (int argc, char **argv, struct option_flags *flags) - { - /* - * Parse the command line options. -@@ -455,8 +471,10 @@ static void process_flags (int argc, cha - rflg = true; - break; - case 'R': /* no-op, handled in process_root_flag () */ -+ flags->chroot = true; - break; - case 'P': /* no-op, handled in process_prefix_flag () */ -+ flags->prefix = true; - break; - case 'U': - user_list = optarg; -@@ -583,6 +601,8 @@ static void check_perms (void) - */ - int main (int argc, char **argv) - { -+ struct option_flags flags; -+ - log_set_progname(Prog); - log_set_logfd(stderr); - -@@ -608,7 +628,7 @@ int main (int argc, char **argv) - /* - * Parse the command line options. - */ -- process_flags (argc, argv); -+ process_flags (argc, argv, &flags); - - check_perms (); - -@@ -625,7 +645,7 @@ int main (int argc, char **argv) - * Do the hard stuff - open the files, create the group entries, - * then close and update the files. - */ -- open_files (); -+ open_files (&flags); - - if (!gflg) { - if (find_new_gid (rflg, &group_id, NULL) < 0) { -@@ -634,7 +654,7 @@ int main (int argc, char **argv) - } - - grp_update (); -- close_files (); -+ close_files (&flags); - if (run_parts ("/etc/shadow-maint/groupadd-post.d", group_name, - Prog)) { - exit(1); -diff -up shadow-4.18.0/src/groupdel.c.test-chroot3 shadow-4.18.0/src/groupdel.c ---- shadow-4.18.0/src/groupdel.c.test-chroot3 2025-07-04 11:15:02.284261326 +0200 -+++ shadow-4.18.0/src/groupdel.c 2025-07-04 11:15:02.312079796 +0200 -@@ -33,6 +33,12 @@ - #endif - #include "shadowlog.h" - #include "run_part.h" -+ -+struct option_flags { -+ bool chroot; -+ bool prefix; -+}; -+ - /* - * Global variables - */ -@@ -61,10 +67,10 @@ static bool is_shadow_grp; - /* local function prototypes */ - NORETURN static void usage (int status); - static void grp_update (void); --static void close_files (void); --static void open_files (void); -+static void close_files (struct option_flags *flags); -+static void open_files (struct option_flags *flags); - static void group_busy (gid_t gid); --static void process_flags (int argc, char **argv); -+static void process_flags (int argc, char **argv, struct option_flags *flags); - - /* - * usage - display usage message and exit -@@ -146,10 +152,14 @@ static void grp_update (void) - * close_files() closes all of the files that were opened for this - * new group. This causes any modified entries to be written out. - */ --static void close_files (void) -+static void close_files (struct option_flags *flags) - { -+ bool process_selinux; -+ -+ process_selinux = !flags->chroot && !flags->prefix; -+ - /* First, write the changes in the regular group database */ -- if (gr_close (true) == 0) { -+ if (gr_close (process_selinux) == 0) { - fprintf (stderr, - _("%s: failure while writing changes to %s\n"), - Prog, gr_dbname ()); -@@ -166,14 +176,14 @@ static void close_files (void) - group_name, gr_dbname ())); - del_cleanup (cleanup_report_del_group_group); - -- cleanup_unlock_group (NULL); -+ cleanup_unlock_group (&process_selinux); - del_cleanup (cleanup_unlock_group); - - - /* Then, write the changes in the shadow database */ - #ifdef SHADOWGRP - if (is_shadow_grp) { -- if (sgr_close (true) == 0) { -+ if (sgr_close (process_selinux) == 0) { - fprintf (stderr, - _("%s: failure while writing changes to %s\n"), - Prog, sgr_dbname ()); -@@ -190,7 +200,7 @@ static void close_files (void) - group_name, sgr_dbname ())); - del_cleanup (cleanup_report_del_group_gshadow); - -- cleanup_unlock_gshadow (NULL); -+ cleanup_unlock_gshadow (&process_selinux); - del_cleanup (cleanup_unlock_gshadow); - } - #endif /* SHADOWGRP */ -@@ -204,8 +214,12 @@ static void close_files (void) - * - * open_files() opens the two group files. - */ --static void open_files (void) -+static void open_files (struct option_flags *flags) - { -+ bool process_selinux; -+ -+ process_selinux = !flags->chroot && !flags->prefix; -+ - /* First, lock the databases */ - if (gr_lock () == 0) { - fprintf (stderr, -@@ -213,7 +227,7 @@ static void open_files (void) - Prog, gr_dbname ()); - fail_exit (E_GRP_UPDATE); - } -- add_cleanup (cleanup_unlock_group, NULL); -+ add_cleanup (cleanup_unlock_group, &process_selinux); - #ifdef SHADOWGRP - if (is_shadow_grp) { - if (sgr_lock () == 0) { -@@ -222,7 +236,7 @@ static void open_files (void) - Prog, sgr_dbname ()); - fail_exit (E_GRP_UPDATE); - } -- add_cleanup (cleanup_unlock_gshadow, NULL); -+ add_cleanup (cleanup_unlock_gshadow, &process_selinux); - } - #endif - -@@ -296,7 +310,7 @@ static void group_busy (gid_t gid) - * - * It will not return if an error is encountered. - */ --static void process_flags (int argc, char **argv) -+static void process_flags (int argc, char **argv, struct option_flags *flags) - { - /* - * Parse the command line options. -@@ -317,8 +331,10 @@ static void process_flags (int argc, cha - usage (E_SUCCESS); - /*@notreached@*/break; - case 'R': /* no-op, handled in process_root_flag () */ -+ flags->chroot = true; - break; - case 'P': /* no-op, handled in process_prefix_flag () */ -+ flags->prefix = true; - break; - case 'f': - check_group_busy = false; -@@ -352,6 +368,7 @@ int main (int argc, char **argv) - int retval; - #endif /* USE_PAM */ - #endif /* ACCT_TOOLS_SETUID */ -+ struct option_flags flags; - - log_set_progname(Prog); - log_set_logfd(stderr); -@@ -375,7 +392,7 @@ int main (int argc, char **argv) - fail_exit (1); - } - -- process_flags (argc, argv); -+ process_flags (argc, argv, &flags); - - #ifdef ACCT_TOOLS_SETUID - #ifdef USE_PAM -@@ -449,11 +466,11 @@ int main (int argc, char **argv) - * Do the hard stuff - open the files, delete the group entries, - * then close and update the files. - */ -- open_files (); -+ open_files (&flags); - - grp_update (); - -- close_files (); -+ close_files (&flags); - - if (run_parts ("/etc/shadow-maint/groupdel-post.d", group_name, - Prog)) { -diff -up shadow-4.18.0/src/groupmems.c.test-chroot3 shadow-4.18.0/src/groupmems.c ---- shadow-4.18.0/src/groupmems.c.test-chroot3 2025-07-04 11:15:02.302301588 +0200 -+++ shadow-4.18.0/src/groupmems.c 2025-07-04 11:15:57.404278255 +0200 -@@ -47,6 +47,13 @@ - #define EXIT_INVALID_GROUP 9 /* specified group does not exist */ - - /* -+ * Structures -+ */ -+struct option_flags { -+ bool chroot; -+}; -+ -+/* - * Global variables - */ - static const char Prog[] = "groupmems"; -@@ -68,15 +75,17 @@ static bool sgr_locked = false; - /* local function prototypes */ - static char *whoami (void); - static void add_user (const char *user, -- const struct group *grp); -+ const struct group *grp, -+ bool process_selinux); - static void remove_user (const char *user, -- const struct group *grp); --static void purge_members (const struct group *grp); -+ const struct group *grp, -+ bool process_selinux); -+static void purge_members (const struct group *grp, bool process_selinux); - static void display_members (const char *const *members); - NORETURN static void usage (int status); --static void process_flags (int argc, char **argv); --static void check_perms (void); --NORETURN static void fail_exit (int code); -+static void process_flags (int argc, char **argv, struct option_flags *flags); -+static void check_perms (bool process_selinux); -+NORETURN static void fail_exit (int code, bool process_selinux); - #define isroot() (getuid () == 0) - - static char *whoami (void) -@@ -99,7 +108,8 @@ static char *whoami (void) - * add_user - Add a user to the specified group - */ - static void add_user (const char *user, -- const struct group *grp) -+ const struct group *grp, -+ bool process_selinux) - { - struct group *newgrp; - -@@ -108,7 +118,7 @@ static void add_user (const char *user, - fprintf (stderr, - _("%s: user '%s' is already a member of '%s'\n"), - Prog, user, grp->gr_name); -- fail_exit (EXIT_MEMBER_EXISTS); -+ fail_exit (EXIT_MEMBER_EXISTS, process_selinux); - } - - newgrp = __gr_dup(grp); -@@ -116,7 +126,7 @@ static void add_user (const char *user, - fprintf (stderr, - _("%s: Out of memory. Cannot update %s.\n"), - Prog, gr_dbname ()); -- fail_exit (13); -+ fail_exit (13, process_selinux); - } - - /* Add the user to the /etc/group group */ -@@ -146,7 +156,7 @@ static void add_user (const char *user, - fprintf (stderr, - _("%s: Out of memory. Cannot update %s.\n"), - Prog, sgr_dbname ()); -- fail_exit (13); -+ fail_exit (13, process_selinux); - } - /* Add the user to the members */ - newsg->sg_mem = add_list (newsg->sg_mem, user); -@@ -157,7 +167,7 @@ static void add_user (const char *user, - fprintf (stderr, - _("%s: failed to prepare the new %s entry '%s'\n"), - Prog, sgr_dbname (), newsg->sg_namp); -- fail_exit (13); -+ fail_exit (13, process_selinux); - } - } - #endif -@@ -166,7 +176,7 @@ static void add_user (const char *user, - fprintf (stderr, - _("%s: failed to prepare the new %s entry '%s'\n"), - Prog, gr_dbname (), newgrp->gr_name); -- fail_exit (13); -+ fail_exit (13, process_selinux); - } - } - -@@ -174,7 +184,8 @@ static void add_user (const char *user, - * remove_user - Remove a user from a given group - */ - static void remove_user (const char *user, -- const struct group *grp) -+ const struct group *grp, -+ bool process_selinux) - { - struct group *newgrp; - -@@ -183,7 +194,7 @@ static void remove_user (const char *use - fprintf (stderr, - _("%s: user '%s' is not a member of '%s'\n"), - Prog, user, grp->gr_name); -- fail_exit (EXIT_NOT_MEMBER); -+ fail_exit (EXIT_NOT_MEMBER, process_selinux); - } - - newgrp = __gr_dup (grp); -@@ -191,7 +202,7 @@ static void remove_user (const char *use - fprintf (stderr, - _("%s: Out of memory. Cannot update %s.\n"), - Prog, gr_dbname ()); -- fail_exit (13); -+ fail_exit (13, process_selinux); - } - - /* Remove the user from the /etc/group group */ -@@ -221,7 +232,7 @@ static void remove_user (const char *use - fprintf (stderr, - _("%s: Out of memory. Cannot update %s.\n"), - Prog, sgr_dbname ()); -- fail_exit (13); -+ fail_exit (13, process_selinux); - } - /* Remove the user from the members */ - newsg->sg_mem = del_list (newsg->sg_mem, user); -@@ -233,7 +244,7 @@ static void remove_user (const char *use - fprintf (stderr, - _("%s: failed to prepare the new %s entry '%s'\n"), - Prog, sgr_dbname (), newsg->sg_namp); -- fail_exit (13); -+ fail_exit (13, process_selinux); - } - } - #endif -@@ -242,14 +253,14 @@ static void remove_user (const char *use - fprintf (stderr, - _("%s: failed to prepare the new %s entry '%s'\n"), - Prog, gr_dbname (), newgrp->gr_name); -- fail_exit (13); -+ fail_exit (13, process_selinux); - } - } - - /* - * purge_members - Remove every members of the specified group - */ --static void purge_members (const struct group *grp) -+static void purge_members (const struct group *grp, bool process_selinux) - { - struct group *newgrp = __gr_dup (grp); - -@@ -257,7 +268,7 @@ static void purge_members (const struct - fprintf (stderr, - _("%s: Out of memory. Cannot update %s.\n"), - Prog, gr_dbname ()); -- fail_exit (13); -+ fail_exit (13, process_selinux); - } - - /* Remove all the members of the /etc/group group */ -@@ -288,7 +299,7 @@ static void purge_members (const struct - fprintf (stderr, - _("%s: Out of memory. Cannot update %s.\n"), - Prog, sgr_dbname ()); -- fail_exit (13); -+ fail_exit (13, process_selinux); - } - /* Remove all the members of the /etc/gshadow - * group */ -@@ -302,7 +313,7 @@ static void purge_members (const struct - fprintf (stderr, - _("%s: failed to prepare the new %s entry '%s'\n"), - Prog, sgr_dbname (), newsg->sg_namp); -- fail_exit (13); -+ fail_exit (13, process_selinux); - } - } - #endif -@@ -311,7 +322,7 @@ static void purge_members (const struct - fprintf (stderr, - _("%s: failed to prepare the new %s entry '%s'\n"), - Prog, gr_dbname (), newgrp->gr_name); -- fail_exit (13); -+ fail_exit (13, process_selinux); - } - } - -@@ -356,7 +367,7 @@ usage (int status) - /* - * process_flags - perform command line argument setting - */ --static void process_flags (int argc, char **argv) -+static void process_flags (int argc, char **argv, struct option_flags *flags) - { - int c; - static struct option long_options[] = { -@@ -396,6 +407,7 @@ static void process_flags (int argc, cha - ++exclusive; - break; - case 'R': /* no-op, handled in process_root_flag () */ -+ flags->chroot = true; - break; - default: - usage (EXIT_USAGE); -@@ -411,12 +423,12 @@ static void process_flags (int argc, cha - && (getpwnam (adduser) == NULL)) { - fprintf (stderr, _("%s: user '%s' does not exist\n"), - Prog, adduser); -- fail_exit (EXIT_INVALID_USER); -+ fail_exit (EXIT_INVALID_USER, !flags->chroot); - } - - } - --static void check_perms (void) -+static void check_perms (bool process_selinux) - { - if (!list) { - #ifdef ACCT_TOOLS_SETUID -@@ -430,7 +442,7 @@ static void check_perms (void) - fprintf (stderr, - _("%s: Cannot determine your user name.\n"), - Prog); -- fail_exit (1); -+ fail_exit (1, process_selinux); - } - - retval = pam_start (Prog, pampw->pw_name, &conv, &pamh); -@@ -450,7 +462,7 @@ static void check_perms (void) - if (NULL != pamh) { - (void) pam_end (pamh, retval); - } -- fail_exit (1); -+ fail_exit (1, process_selinux); - } - (void) pam_end (pamh, retval); - #endif /* USE_PAM */ -@@ -458,10 +470,10 @@ static void check_perms (void) - } - } - --static void fail_exit (int code) -+static void fail_exit (int code, bool process_selinux) - { - if (gr_locked) { -- if (gr_unlock (true) == 0) { -+ if (gr_unlock (process_selinux) == 0) { - fprintf (stderr, - _("%s: failed to unlock %s\n"), - Prog, gr_dbname ()); -@@ -472,7 +484,7 @@ static void fail_exit (int code) - - #ifdef SHADOWGRP - if (sgr_locked) { -- if (sgr_unlock (true) == 0) { -+ if (sgr_unlock (process_selinux) == 0) { - fprintf (stderr, - _("%s: failed to unlock %s\n"), - Prog, sgr_dbname ()); -@@ -485,14 +497,14 @@ static void fail_exit (int code) - exit (code); - } - --static void open_files (void) -+static void open_files (bool process_selinux) - { - if (!list) { - if (gr_lock () == 0) { - fprintf (stderr, - _("%s: cannot lock %s; try again later.\n"), - Prog, gr_dbname ()); -- fail_exit (EXIT_GROUP_FILE); -+ fail_exit (EXIT_GROUP_FILE, process_selinux); - } - gr_locked = true; - -@@ -502,7 +514,7 @@ static void open_files (void) - fprintf (stderr, - _("%s: cannot lock %s; try again later.\n"), - Prog, sgr_dbname ()); -- fail_exit (EXIT_GROUP_FILE); -+ fail_exit (EXIT_GROUP_FILE, process_selinux); - } - sgr_locked = true; - } -@@ -511,28 +523,32 @@ static void open_files (void) - - if (gr_open (list ? O_RDONLY : O_CREAT | O_RDWR) == 0) { - fprintf (stderr, _("%s: cannot open %s\n"), Prog, gr_dbname ()); -- fail_exit (EXIT_GROUP_FILE); -+ fail_exit (EXIT_GROUP_FILE, process_selinux); - } - - #ifdef SHADOWGRP - if (is_shadowgrp) { - if (sgr_open (list ? O_RDONLY : O_CREAT | O_RDWR) == 0) { - fprintf (stderr, _("%s: cannot open %s\n"), Prog, sgr_dbname ()); -- fail_exit (EXIT_GROUP_FILE); -+ fail_exit (EXIT_GROUP_FILE, process_selinux); - } - } - #endif - } - --static void close_files (void) -+static void close_files (struct option_flags *flags) - { -- if ((gr_close (true) == 0) && !list) { -+ bool process_selinux; -+ -+ process_selinux = !flags->chroot; -+ -+ if ((gr_close (process_selinux) == 0) && !list) { - fprintf (stderr, _("%s: failure while writing changes to %s\n"), Prog, gr_dbname ()); - SYSLOG ((LOG_ERR, "failure while writing changes to %s", gr_dbname ())); -- fail_exit (EXIT_GROUP_FILE); -+ fail_exit (EXIT_GROUP_FILE, process_selinux); - } - if (gr_locked) { -- if (gr_unlock (true) == 0) { -+ if (gr_unlock (process_selinux) == 0) { - fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, gr_dbname ()); - SYSLOG ((LOG_ERR, "failed to unlock %s", gr_dbname ())); - /* continue */ -@@ -542,13 +558,13 @@ static void close_files (void) - - #ifdef SHADOWGRP - if (is_shadowgrp) { -- if ((sgr_close (true) == 0) && !list) { -+ if ((sgr_close (process_selinux) == 0) && !list) { - fprintf (stderr, _("%s: failure while writing changes to %s\n"), Prog, sgr_dbname ()); - SYSLOG ((LOG_ERR, "failure while writing changes to %s", sgr_dbname ())); -- fail_exit (EXIT_GROUP_FILE); -+ fail_exit (EXIT_GROUP_FILE, process_selinux); - } - if (sgr_locked) { -- if (sgr_unlock (true) == 0) { -+ if (sgr_unlock (process_selinux) == 0) { - fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, sgr_dbname ()); - SYSLOG ((LOG_ERR, "failed to unlock %s", sgr_dbname ())); - /* continue */ -@@ -563,6 +579,8 @@ int main (int argc, char **argv) - { - char *name; - const struct group *grp; -+ struct option_flags flags; -+ bool process_selinux; - - log_set_progname(Prog); - log_set_logfd(stderr); -@@ -579,44 +597,45 @@ int main (int argc, char **argv) - is_shadowgrp = sgr_file_present (); - #endif - -- process_flags (argc, argv); -+ process_flags (argc, argv, &flags); -+ process_selinux = !flags.chroot; - - if (NULL == thisgroup) { - name = whoami (); - if (!list && (NULL == name)) { - fprintf (stderr, _("%s: your groupname does not match your username\n"), Prog); -- fail_exit (EXIT_NOT_PRIMARY); -+ fail_exit (EXIT_NOT_PRIMARY, process_selinux); - } - } else { - name = thisgroup; - if (!list && !isroot ()) { - fprintf (stderr, _("%s: only root can use the -g/--group option\n"), Prog); -- fail_exit (EXIT_NOT_ROOT); -+ fail_exit (EXIT_NOT_ROOT, process_selinux); - } - } - -- check_perms (); -+ check_perms (process_selinux); - -- open_files (); -+ open_files (process_selinux); - - grp = gr_locate (name); - if (NULL == grp) { - fprintf (stderr, _("%s: group '%s' does not exist in %s\n"), - Prog, name, gr_dbname ()); -- fail_exit (EXIT_INVALID_GROUP); -+ fail_exit (EXIT_INVALID_GROUP, process_selinux); - } - - if (list) { - display_members ((const char *const *)grp->gr_mem); - } else if (NULL != adduser) { -- add_user (adduser, grp); -+ add_user (adduser, grp, process_selinux); - } else if (NULL != deluser) { -- remove_user (deluser, grp); -+ remove_user (deluser, grp, process_selinux); - } else if (purge) { -- purge_members (grp); -+ purge_members (grp, process_selinux); - } - -- close_files (); -+ close_files (&flags); - - exit (EXIT_SUCCESS); - } -diff -up shadow-4.18.0/src/groupmod.c.test-chroot3 shadow-4.18.0/src/groupmod.c ---- shadow-4.18.0/src/groupmod.c.test-chroot3 2025-07-04 11:15:02.284354823 +0200 -+++ shadow-4.18.0/src/groupmod.c 2025-07-04 11:15:02.311945828 +0200 -@@ -61,6 +61,10 @@ - #define E_PAM_USERNAME 12 /* can't determine your username for use with pam */ - #define E_PAM_ERROR 13 /* pam returned an error, see Syslog facility id groupmod */ - -+struct option_flags { -+ bool chroot; -+ bool prefix; -+}; - - /* - * Global variables -@@ -102,11 +106,11 @@ static void new_sgent (struct sgrp *); - static void grp_update (void); - static void check_new_gid (void); - static void check_new_name (void); --static void process_flags (int, char **); --static void lock_files (void); -+static void process_flags (int, char **, struct option_flags *); -+static void lock_files (struct option_flags *flags); - static void prepare_failure_reports (void); - static void open_files (void); --static void close_files (void); -+static void close_files (struct option_flags *flags); - static void update_primary_groups (gid_t ogid, gid_t ngid); - - -@@ -405,7 +409,7 @@ check_new_name(void) - * values that the user will be created with accordingly. The values - * are checked for sanity. - */ --static void process_flags (int argc, char **argv) -+static void process_flags (int argc, char **argv, struct option_flags *flags) - { - int c; - static struct option long_options[] = { -@@ -451,8 +455,10 @@ static void process_flags (int argc, cha - pflg = true; - break; - case 'R': /* no-op, handled in process_root_flag () */ -+ flags->chroot = true; - break; - case 'P': /* no-op, handled in process_prefix_flag () */ -+ flags->prefix = true; - break; - case 'U': - user_list = optarg; -@@ -479,9 +485,13 @@ static void process_flags (int argc, cha - * close_files() closes all of the files that were opened for this new - * group. This causes any modified entries to be written out. - */ --static void close_files (void) -+static void close_files (struct option_flags *flags) - { -- if (gr_close (true) == 0) { -+ bool process_selinux; -+ -+ process_selinux = !flags->chroot && !flags->prefix; -+ -+ if (gr_close (process_selinux) == 0) { - fprintf (stderr, - _("%s: failure while writing changes to %s\n"), - Prog, gr_dbname ()); -@@ -498,13 +508,13 @@ static void close_files (void) - gr_dbname (), info_group.action)); - del_cleanup (cleanup_report_mod_group); - -- cleanup_unlock_group (NULL); -+ cleanup_unlock_group (&process_selinux); - del_cleanup (cleanup_unlock_group); - - #ifdef SHADOWGRP - if ( is_shadow_grp - && (pflg || nflg || user_list)) { -- if (sgr_close (true) == 0) { -+ if (sgr_close (process_selinux) == 0) { - fprintf (stderr, - _("%s: failure while writing changes to %s\n"), - Prog, sgr_dbname ()); -@@ -528,13 +538,13 @@ static void close_files (void) - sgr_dbname (), info_gshadow.action)); - del_cleanup (cleanup_report_mod_gshadow); - -- cleanup_unlock_gshadow (NULL); -+ cleanup_unlock_gshadow (&process_selinux); - del_cleanup (cleanup_unlock_gshadow); - } - #endif /* SHADOWGRP */ - - if (gflg) { -- if (pw_close (true) == 0) { -+ if (pw_close (process_selinux) == 0) { - fprintf (stderr, - _("%s: failure while writing changes to %s\n"), - Prog, pw_dbname ()); -@@ -551,7 +561,7 @@ static void close_files (void) - pw_dbname (), info_passwd.action)); - del_cleanup (cleanup_report_mod_passwd); - -- cleanup_unlock_passwd (NULL); -+ cleanup_unlock_passwd (&process_selinux); - del_cleanup (cleanup_unlock_passwd); - } - -@@ -657,15 +667,19 @@ static void prepare_failure_reports (voi - * - * lock_files() locks the group, gshadow, and passwd databases. - */ --static void lock_files (void) -+static void lock_files (struct option_flags *flags) - { -+ bool process_selinux; -+ -+ process_selinux = !flags->chroot && !flags->prefix; -+ - if (gr_lock () == 0) { - fprintf (stderr, - _("%s: cannot lock %s; try again later.\n"), - Prog, gr_dbname ()); - exit (E_GRP_UPDATE); - } -- add_cleanup (cleanup_unlock_group, NULL); -+ add_cleanup (cleanup_unlock_group, &process_selinux); - - #ifdef SHADOWGRP - if ( is_shadow_grp -@@ -676,7 +690,7 @@ static void lock_files (void) - Prog, sgr_dbname ()); - exit (E_GRP_UPDATE); - } -- add_cleanup (cleanup_unlock_gshadow, NULL); -+ add_cleanup (cleanup_unlock_gshadow, &process_selinux); - } - #endif - -@@ -687,7 +701,7 @@ static void lock_files (void) - Prog, pw_dbname ()); - exit (E_GRP_UPDATE); - } -- add_cleanup (cleanup_unlock_passwd, NULL); -+ add_cleanup (cleanup_unlock_passwd, &process_selinux); - } - } - -@@ -771,6 +785,7 @@ int main (int argc, char **argv) - int retval; - #endif /* USE_PAM */ - #endif /* ACCT_TOOLS_SETUID */ -+ struct option_flags flags; - - log_set_progname(Prog); - log_set_logfd(stderr); -@@ -794,7 +809,7 @@ int main (int argc, char **argv) - exit (E_CLEANUP_SERVICE); - } - -- process_flags (argc, argv); -+ process_flags (argc, argv, &flags); - - #ifdef ACCT_TOOLS_SETUID - #ifdef USE_PAM -@@ -859,7 +874,7 @@ int main (int argc, char **argv) - check_new_name (); - } - -- lock_files (); -+ lock_files (&flags); - - /* - * Now if the group is not changed, it's our fault. -@@ -875,7 +890,7 @@ int main (int argc, char **argv) - - grp_update (); - -- close_files (); -+ close_files (&flags); - - nscd_flush_cache ("group"); - sssd_flush_cache (SSSD_DB_GROUP); -diff -up shadow-4.18.0/src/grpck.c.test-chroot3 shadow-4.18.0/src/grpck.c ---- shadow-4.18.0/src/grpck.c.test-chroot3 2025-07-04 11:15:02.302354224 +0200 -+++ shadow-4.18.0/src/grpck.c 2025-07-04 11:15:02.313643179 +0200 -@@ -44,6 +44,13 @@ - #define E_CANT_UPDATE 5 - - /* -+ * Structures -+ */ -+struct option_flags { -+ bool chroot; -+}; -+ -+/* - * Global variables - */ - static const char Prog[] = "grpck"; -@@ -64,19 +71,20 @@ static bool sort_mode = false; - static bool silence_warnings = false; - - /* local function prototypes */ --static void fail_exit (int status); -+static void fail_exit (int status, bool process_selinux); - NORETURN static void usage (int status); - static void delete_member (char **, const char *); --static void process_flags (int argc, char **argv); --static void open_files (void); --static void close_files (bool changed); -+static void process_flags (int argc, char **argv, struct option_flags *flags); -+static void open_files (bool process_selinux); -+static void close_files (bool changed, struct option_flags *flags); - static int check_members (const char *groupname, - char **members, - const char *fmt_info, - const char *fmt_prompt, - const char *fmt_syslog, - bool *errors); --static void check_grp_file (bool *errors, bool *changed); -+static void check_grp_file (bool *errors, bool *changed, -+ struct option_flags *flags); - #ifdef SHADOWGRP - static void compare_members_lists (const char *groupname, - char **members, -@@ -89,10 +97,10 @@ static void check_sgr_file (bool *errors - /* - * fail_exit - exit with an error code after unlocking files - */ --static void fail_exit (int status) -+static void fail_exit (int status, bool process_selinux) - { - if (gr_locked) { -- if (gr_unlock (true) == 0) { -+ if (gr_unlock (process_selinux) == 0) { - fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, gr_dbname ()); - SYSLOG ((LOG_ERR, "failed to unlock %s", gr_dbname ())); - /* continue */ -@@ -101,7 +109,7 @@ static void fail_exit (int status) - - #ifdef SHADOWGRP - if (sgr_locked) { -- if (sgr_unlock (true) == 0) { -+ if (sgr_unlock (process_selinux) == 0) { - fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, sgr_dbname ()); - SYSLOG ((LOG_ERR, "failed to unlock %s", sgr_dbname ())); - /* continue */ -@@ -172,7 +180,7 @@ static void delete_member (char **list, - * - * It will not return if an error is encountered. - */ --static void process_flags (int argc, char **argv) -+static void process_flags (int argc, char **argv, struct option_flags *flags) - { - int c; - static struct option long_options[] = { -@@ -201,6 +209,7 @@ static void process_flags (int argc, cha - read_only = true; - break; - case 'R': /* no-op, handled in process_root_flag () */ -+ flags->chroot = true; - break; - case 's': - sort_mode = true; -@@ -257,7 +266,7 @@ static void process_flags (int argc, cha - * In read-only mode, the databases are not locked and are opened - * only for reading. - */ --static void open_files (void) -+static void open_files (bool process_selinux) - { - /* - * Lock the files if we aren't in "read-only" mode -@@ -267,7 +276,7 @@ static void open_files (void) - fprintf (stderr, - _("%s: cannot lock %s; try again later.\n"), - Prog, grp_file); -- fail_exit (E_CANT_LOCK); -+ fail_exit (E_CANT_LOCK, process_selinux); - } - gr_locked = true; - #ifdef SHADOWGRP -@@ -276,7 +285,7 @@ static void open_files (void) - fprintf (stderr, - _("%s: cannot lock %s; try again later.\n"), - Prog, sgr_file); -- fail_exit (E_CANT_LOCK); -+ fail_exit (E_CANT_LOCK, process_selinux); - } - sgr_locked = true; - } -@@ -293,7 +302,7 @@ static void open_files (void) - if (use_system_grp_file) { - SYSLOG ((LOG_WARN, "cannot open %s", grp_file)); - } -- fail_exit (E_CANT_OPEN); -+ fail_exit (E_CANT_OPEN, process_selinux); - } - #ifdef SHADOWGRP - if (is_shadow && (sgr_open (read_only ? O_RDONLY : O_CREAT | O_RDWR) == 0)) { -@@ -302,7 +311,7 @@ static void open_files (void) - if (use_system_sgr_file) { - SYSLOG ((LOG_WARN, "cannot open %s", sgr_file)); - } -- fail_exit (E_CANT_OPEN); -+ fail_exit (E_CANT_OPEN, process_selinux); - } - #endif - } -@@ -314,23 +323,27 @@ static void open_files (void) - * changes are committed in the databases. The databases are - * unlocked anyway. - */ --static void close_files (bool changed) -+static void close_files (bool changed, struct option_flags *flags) - { -+ bool process_selinux; -+ -+ process_selinux = !flags->chroot; -+ - /* - * All done. If there were no change we can just abandon any - * changes to the files. - */ - if (changed) { -- if (gr_close (true) == 0) { -+ if (gr_close (process_selinux) == 0) { - fprintf (stderr, _("%s: failure while writing changes to %s\n"), - Prog, grp_file); -- fail_exit (E_CANT_UPDATE); -+ fail_exit (E_CANT_UPDATE, process_selinux); - } - #ifdef SHADOWGRP -- if (is_shadow && (sgr_close (true) == 0)) { -+ if (is_shadow && (sgr_close (process_selinux) == 0)) { - fprintf (stderr, _("%s: failure while writing changes to %s\n"), - Prog, sgr_file); -- fail_exit (E_CANT_UPDATE); -+ fail_exit (E_CANT_UPDATE, process_selinux); - } - #endif - } -@@ -340,7 +353,7 @@ static void close_files (bool changed) - */ - #ifdef SHADOWGRP - if (sgr_locked) { -- if (sgr_unlock (true) == 0) { -+ if (sgr_unlock (process_selinux) == 0) { - fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, sgr_dbname ()); - SYSLOG ((LOG_ERR, "failed to unlock %s", sgr_dbname ())); - /* continue */ -@@ -349,7 +362,7 @@ static void close_files (bool changed) - } - #endif - if (gr_locked) { -- if (gr_unlock (true) == 0) { -+ if (gr_unlock (process_selinux) == 0) { - fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, gr_dbname ()); - SYSLOG ((LOG_ERR, "failed to unlock %s", gr_dbname ())); - /* continue */ -@@ -455,13 +468,16 @@ static void compare_members_lists (const - /* - * check_grp_file - check the content of the group file - */ --static void check_grp_file (bool *errors, bool *changed) -+static void check_grp_file (bool *errors, bool *changed, struct option_flags *flags) - { - struct commonio_entry *gre, *tgre; - struct group *grp; - #ifdef SHADOWGRP - const struct sgrp *sgr; - #endif -+ bool process_selinux; -+ -+ process_selinux = !flags->chroot; - - /* - * Loop through the entire group file. -@@ -627,7 +643,7 @@ static void check_grp_file (bool *errors - fprintf (stderr, - _("%s: failed to prepare the new %s entry '%s'\n"), - Prog, sgr_dbname (), sg.sg_namp); -- fail_exit (E_CANT_UPDATE); -+ fail_exit (E_CANT_UPDATE, process_selinux); - } - /* remove password from /etc/group */ - gr = *grp; -@@ -636,7 +652,7 @@ static void check_grp_file (bool *errors - fprintf (stderr, - _("%s: failed to prepare the new %s entry '%s'\n"), - Prog, gr_dbname (), gr.gr_name); -- fail_exit (E_CANT_UPDATE); -+ fail_exit (E_CANT_UPDATE, process_selinux); - } - } - } else { -@@ -819,6 +835,8 @@ int main (int argc, char **argv) - { - bool errors = false; - bool changed = false; -+ struct option_flags flags; -+ bool process_selinux; - - log_set_progname(Prog); - log_set_logfd(stderr); -@@ -832,9 +850,10 @@ int main (int argc, char **argv) - OPENLOG (Prog); - - /* Parse the command line arguments */ -- process_flags (argc, argv); -+ process_flags (argc, argv, &flags); -+ process_selinux = !flags.chroot; - -- open_files (); -+ open_files (process_selinux); - - if (sort_mode) { - gr_sort (); -@@ -845,7 +864,7 @@ int main (int argc, char **argv) - changed = true; - #endif - } else { -- check_grp_file (&errors, &changed); -+ check_grp_file (&errors, &changed, &flags); - #ifdef SHADOWGRP - if (is_shadow) { - check_sgr_file (&errors, &changed); -@@ -854,7 +873,7 @@ int main (int argc, char **argv) - } - - /* Commit the change in the database if needed */ -- close_files (changed); -+ close_files (changed, &flags); - - if (!read_only) { - nscd_flush_cache ("group"); - -diff -up shadow-4.18.0/src/chage.c.test-chroot4 shadow-4.18.0/src/chage.c ---- shadow-4.18.0/src/chage.c.test-chroot4 2025-07-04 11:17:37.593707379 +0200 -+++ shadow-4.18.0/src/chage.c 2025-07-04 11:17:37.613081862 +0200 -@@ -42,6 +42,10 @@ - #include "tcbfuncs.h" - #endif - -+struct option_flags { -+ bool chroot; -+ bool prefix; -+}; - - /* - * Global variables -@@ -79,29 +83,29 @@ NORETURN static void usage (int status); - static int new_fields (void); - static void print_day_as_date (long day); - static void list_fields (void); --static void process_flags (int argc, char **argv); -+static void process_flags (int argc, char **argv, struct option_flags *flags); - static void check_flags (int argc, int opt_index); --static void check_perms (void); --static void open_files (bool readonly); --static void close_files (void); --NORETURN static void fail_exit (int code); -+static void check_perms (struct option_flags *flags); -+static void open_files (bool readonly, struct option_flags *flags); -+static void close_files (struct option_flags *flags); -+NORETURN static void fail_exit (int code, bool process_selinux); - - /* - * fail_exit - do some cleanup and exit with the given error code - */ - NORETURN - static void --fail_exit (int code) -+fail_exit (int code, bool process_selinux) - { - if (spw_locked) { -- if (spw_unlock (true) == 0) { -+ if (spw_unlock (process_selinux) == 0) { - fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, spw_dbname ()); - SYSLOG ((LOG_ERR, "failed to unlock %s", spw_dbname ())); - /* continue */ - } - } - if (pw_locked) { -- if (pw_unlock (true) == 0) { -+ if (pw_unlock (process_selinux) == 0) { - fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, pw_dbname ()); - SYSLOG ((LOG_ERR, "failed to unlock %s", pw_dbname ())); - /* continue */ -@@ -338,7 +342,7 @@ static void list_fields (void) - * - * It will not return if an error is encountered. - */ --static void process_flags (int argc, char **argv) -+static void process_flags (int argc, char **argv, struct option_flags *flags) - { - /* - * Parse the command line options. -@@ -419,8 +423,10 @@ static void process_flags (int argc, cha - } - break; - case 'R': /* no-op, handled in process_root_flag () */ -+ flags->chroot = true; - break; - case 'P': /* no-op, handled in process_prefix_flag () */ -+ flags->prefix = true; - break; - case 'W': - Wflg = true; -@@ -472,8 +478,12 @@ static void check_flags (int argc, int o - * - * It will not return if the user is not allowed. - */ --static void check_perms (void) -+static void check_perms (struct option_flags *flags) - { -+ bool process_selinux; -+ -+ process_selinux = !flags->chroot && !flags->prefix; -+ - /* - * An unprivileged user can ask for their own aging information, but - * only root can change it, or list another user's aging -@@ -482,7 +492,7 @@ static void check_perms (void) - - if (!amroot && !lflg) { - fprintf (stderr, _("%s: Permission denied.\n"), Prog); -- fail_exit (E_NOPERM); -+ fail_exit (E_NOPERM, process_selinux); - } - } - -@@ -492,8 +502,12 @@ static void check_perms (void) - * In read-only mode, the databases are not locked and are opened - * only for reading. - */ --static void open_files (bool readonly) -+static void open_files (bool readonly, struct option_flags *flags) - { -+ bool process_selinux; -+ -+ process_selinux = !flags->chroot && !flags->prefix; -+ - /* - * Lock and open the password file. This loads all of the password - * file entries into memory. Then we get a pointer to the password -@@ -504,14 +518,14 @@ static void open_files (bool readonly) - fprintf (stderr, - _("%s: cannot lock %s; try again later.\n"), - Prog, pw_dbname ()); -- fail_exit (E_NOPERM); -+ fail_exit (E_NOPERM, process_selinux); - } - pw_locked = true; - } - if (pw_open (readonly ? O_RDONLY: O_CREAT | O_RDWR) == 0) { - fprintf (stderr, _("%s: cannot open %s\n"), Prog, pw_dbname ()); - SYSLOG ((LOG_WARN, "cannot open %s", pw_dbname ())); -- fail_exit (E_NOPERM); -+ fail_exit (E_NOPERM, process_selinux); - } - - /* -@@ -525,7 +539,7 @@ static void open_files (bool readonly) - fprintf (stderr, - _("%s: cannot lock %s; try again later.\n"), - Prog, spw_dbname ()); -- fail_exit (E_NOPERM); -+ fail_exit (E_NOPERM, process_selinux); - } - spw_locked = true; - } -@@ -533,42 +547,46 @@ static void open_files (bool readonly) - fprintf (stderr, - _("%s: cannot open %s\n"), Prog, spw_dbname ()); - SYSLOG ((LOG_WARN, "cannot open %s", spw_dbname ())); -- fail_exit (E_NOPERM); -+ fail_exit (E_NOPERM, process_selinux); - } - } - - /* - * close_files - close and unlock the password/shadow databases - */ --static void close_files (void) -+static void close_files (struct option_flags *flags) - { -+ bool process_selinux; -+ -+ process_selinux = !flags->chroot && !flags->prefix; -+ - /* - * Now close the shadow password file, which will cause all of the - * entries to be re-written. - */ -- if (spw_close (true) == 0) { -+ if (spw_close (process_selinux) == 0) { - fprintf (stderr, - _("%s: failure while writing changes to %s\n"), Prog, spw_dbname ()); - SYSLOG ((LOG_ERR, "failure while writing changes to %s", spw_dbname ())); -- fail_exit (E_NOPERM); -+ fail_exit (E_NOPERM, process_selinux); - } - - /* - * Close the password file. If any entries were modified, the file - * will be re-written. - */ -- if (pw_close (true) == 0) { -+ if (pw_close (process_selinux) == 0) { - fprintf (stderr, _("%s: failure while writing changes to %s\n"), Prog, pw_dbname ()); - SYSLOG ((LOG_ERR, "failure while writing changes to %s", pw_dbname ())); -- fail_exit (E_NOPERM); -+ fail_exit (E_NOPERM, process_selinux); - } -- if (spw_unlock (true) == 0) { -+ if (spw_unlock (process_selinux) == 0) { - fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, spw_dbname ()); - SYSLOG ((LOG_ERR, "failed to unlock %s", spw_dbname ())); - /* continue */ - } - spw_locked = false; -- if (pw_unlock (true) == 0) { -+ if (pw_unlock (process_selinux) == 0) { - fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, pw_dbname ()); - SYSLOG ((LOG_ERR, "failed to unlock %s", pw_dbname ())); - /* continue */ -@@ -582,7 +600,8 @@ static void close_files (void) - * It will not return in case of error - */ - static void update_age (/*@null@*/const struct spwd *sp, -- /*@notnull@*/const struct passwd *pw) -+ /*@notnull@*/const struct passwd *pw, -+ bool process_selinux) - { - struct spwd spwent; - -@@ -603,7 +622,7 @@ static void update_age (/*@null@*/const - if (pw_update (&pwent) == 0) { - fprintf (stderr, - _("%s: failed to prepare the new %s entry '%s'\n"), Prog, pw_dbname (), pwent.pw_name); -- fail_exit (E_NOPERM); -+ fail_exit (E_NOPERM, process_selinux); - } - } else { - spwent.sp_namp = xstrdup (sp->sp_namp); -@@ -626,7 +645,7 @@ static void update_age (/*@null@*/const - if (spw_update (&spwent) == 0) { - fprintf (stderr, - _("%s: failed to prepare the new %s entry '%s'\n"), Prog, spw_dbname (), spwent.sp_namp); -- fail_exit (E_NOPERM); -+ fail_exit (E_NOPERM, process_selinux); - } - - } -@@ -712,6 +731,8 @@ int main (int argc, char **argv) - uid_t ruid; - gid_t rgid; - const struct passwd *pw; -+ struct option_flags flags; -+ bool process_selinux; - - sanitize_env (); - check_fds (); -@@ -740,9 +761,10 @@ int main (int argc, char **argv) - } - #endif - -- process_flags (argc, argv); -+ process_flags (argc, argv, &flags); -+ process_selinux = !flags.chroot && !flags.prefix; - -- check_perms (); -+ check_perms (&flags); - - if (!spw_file_present ()) { - fprintf (stderr, -@@ -753,13 +775,13 @@ int main (int argc, char **argv) - exit (E_SHADOW_NOTFOUND); - } - -- open_files (lflg); -+ open_files (lflg, &flags); - /* Drop privileges */ - if (lflg && ( (setregid (rgid, rgid) != 0) - || (setreuid (ruid, ruid) != 0))) { - fprintf (stderr, _("%s: failed to drop privileges (%s)\n"), - Prog, strerror (errno)); -- fail_exit (E_NOPERM); -+ fail_exit (E_NOPERM, process_selinux); - } - - pw = pw_locate (argv[optind]); -@@ -767,13 +789,13 @@ int main (int argc, char **argv) - fprintf (stderr, _("%s: user '%s' does not exist in %s\n"), - Prog, argv[optind], pw_dbname ()); - closelog (); -- fail_exit (E_NOPERM); -+ fail_exit (E_NOPERM, process_selinux); - } - - STRTCPY(user_name, pw->pw_name); - #ifdef WITH_TCB - if (shadowtcb_set_user (pw->pw_name) == SHADOWTCB_FAILURE) { -- fail_exit (E_NOPERM); -+ fail_exit (E_NOPERM, process_selinux); - } - #endif - user_uid = pw->pw_uid; -@@ -788,11 +810,11 @@ int main (int argc, char **argv) - if (lflg) { - if (!amroot && (ruid != user_uid)) { - fprintf (stderr, _("%s: Permission denied.\n"), Prog); -- fail_exit (E_NOPERM); -+ fail_exit (E_NOPERM, process_selinux); - } - /* Displaying fields is not of interest to audit */ - list_fields (); -- fail_exit (E_SUCCESS); -+ fail_exit (E_SUCCESS, process_selinux); - } - - /* -@@ -805,7 +827,7 @@ int main (int argc, char **argv) - if (new_fields () == 0) { - fprintf (stderr, _("%s: error changing fields\n"), - Prog); -- fail_exit (E_NOPERM); -+ fail_exit (E_NOPERM, process_selinux); - } - #ifdef WITH_AUDIT - else { -@@ -847,9 +869,9 @@ int main (int argc, char **argv) - #endif - } - -- update_age (sp, pw); -+ update_age (sp, pw, process_selinux); - -- close_files (); -+ close_files (&flags); - - SYSLOG ((LOG_INFO, "changed password expiry for %s", user_name)); - -diff -up shadow-4.18.0/src/chfn.c.test-chroot4 shadow-4.18.0/src/chfn.c ---- shadow-4.18.0/src/chfn.c.test-chroot4 2025-07-04 11:17:37.590501245 +0200 -+++ shadow-4.18.0/src/chfn.c 2025-07-04 11:17:37.613576062 +0200 -@@ -39,6 +39,9 @@ - #include "string/strdup/xstrdup.h" - #include "string/strtok/stpsep.h" - -+struct option_flags { -+ bool chroot; -+}; - - /* - * Global variables. -@@ -63,23 +66,23 @@ static bool pw_locked = false; - */ - - /* local function prototypes */ --NORETURN static void fail_exit (int code); -+NORETURN static void fail_exit (int code, bool process_selinux); - NORETURN static void usage (int status); - static bool may_change_field (int); - static void new_fields (void); - static char *copy_field (char *, char *, char *); --static void process_flags (int argc, char **argv); -+static void process_flags (int argc, char **argv, struct option_flags *flags); - static void check_perms (const struct passwd *pw); --static void update_gecos (const char *user, char *gecos); -+static void update_gecos (const char *user, char *gecos, struct option_flags *flags); - static void get_old_fields (const char *gecos); - - /* - * fail_exit - exit with an error and do some cleanup - */ --static void fail_exit (int code) -+static void fail_exit (int code, bool process_selinux) - { - if (pw_locked) { -- if (pw_unlock (true) == 0) { -+ if (pw_unlock (process_selinux) == 0) { - fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, pw_dbname ()); - SYSLOG ((LOG_ERR, "failed to unlock %s", pw_dbname ())); - /* continue */ -@@ -250,7 +253,7 @@ static char *copy_field (char *in, char - * - * It will not return if an error is encountered. - */ --static void process_flags (int argc, char **argv) -+static void process_flags (int argc, char **argv, struct option_flags *flags) - { - int c; /* flag currently being processed */ - static struct option long_options[] = { -@@ -316,6 +319,7 @@ static void process_flags (int argc, cha - STRTCPY(roomno, optarg); - break; - case 'R': /* no-op, handled in process_root_flag () */ -+ flags->chroot = true; - break; - case 'u': - usage (E_SUCCESS); -@@ -423,10 +427,13 @@ static void check_perms (const struct pa - * - * Commit the user's entry after changing her gecos field. - */ --static void update_gecos (const char *user, char *gecos) -+static void update_gecos (const char *user, char *gecos, struct option_flags *flags) - { - const struct passwd *pw; /* The user's password file entry */ - struct passwd pwent; /* modified password file entry */ -+ bool process_selinux; -+ -+ process_selinux = !flags->chroot; - - /* - * Before going any further, raise the ulimit to prevent colliding -@@ -437,7 +444,7 @@ static void update_gecos (const char *us - if (setuid (0) != 0) { - fputs (_("Cannot change ID to root.\n"), stderr); - SYSLOG ((LOG_ERR, "can't setuid(0)")); -- fail_exit (E_NOPERM); -+ fail_exit (E_NOPERM, process_selinux); - } - pwd_init (); - -@@ -449,13 +456,13 @@ static void update_gecos (const char *us - fprintf (stderr, - _("%s: cannot lock %s; try again later.\n"), - Prog, pw_dbname ()); -- fail_exit (E_NOPERM); -+ fail_exit (E_NOPERM, process_selinux); - } - pw_locked = true; - if (pw_open (O_CREAT | O_RDWR) == 0) { - fprintf (stderr, - _("%s: cannot open %s\n"), Prog, pw_dbname ()); -- fail_exit (E_NOPERM); -+ fail_exit (E_NOPERM, process_selinux); - } - - /* -@@ -469,7 +476,7 @@ static void update_gecos (const char *us - fprintf (stderr, - _("%s: user '%s' does not exist in %s\n"), - Prog, user, pw_dbname ()); -- fail_exit (E_NOPERM); -+ fail_exit (E_NOPERM, process_selinux); - } - - /* -@@ -487,18 +494,18 @@ static void update_gecos (const char *us - fprintf (stderr, - _("%s: failed to prepare the new %s entry '%s'\n"), - Prog, pw_dbname (), pwent.pw_name); -- fail_exit (E_NOPERM); -+ fail_exit (E_NOPERM, process_selinux); - } - - /* - * Changes have all been made, so commit them and unlock the file. - */ -- if (pw_close (true) == 0) { -+ if (pw_close (process_selinux) == 0) { - fprintf (stderr, _("%s: failure while writing changes to %s\n"), Prog, pw_dbname ()); - SYSLOG ((LOG_ERR, "failure while writing changes to %s", pw_dbname ())); -- fail_exit (E_NOPERM); -+ fail_exit (E_NOPERM, process_selinux); - } -- if (pw_unlock (true) == 0) { -+ if (pw_unlock (process_selinux) == 0) { - fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, pw_dbname ()); - SYSLOG ((LOG_ERR, "failed to unlock %s", pw_dbname ())); - /* continue */ -@@ -562,7 +569,7 @@ static void get_old_fields (const char * - * - * It will not return if a field is not valid. - */ --static void check_fields (void) -+static void check_fields (bool process_selinux) - { - int err; - err = valid_field (fullnm, ":,=\n"); -@@ -570,7 +577,7 @@ static void check_fields (void) - fprintf (stderr, _("%s: name with non-ASCII characters: '%s'\n"), Prog, fullnm); - } else if (err < 0) { - fprintf (stderr, _("%s: invalid name: '%s'\n"), Prog, fullnm); -- fail_exit (E_NOPERM); -+ fail_exit (E_NOPERM, process_selinux); - } - err = valid_field (roomno, ":,=\n"); - if (err > 0) { -@@ -578,17 +585,17 @@ static void check_fields (void) - } else if (err < 0) { - fprintf (stderr, _("%s: invalid room number: '%s'\n"), - Prog, roomno); -- fail_exit (E_NOPERM); -+ fail_exit (E_NOPERM, process_selinux); - } - if (valid_field (workph, ":,=\n") != 0) { - fprintf (stderr, _("%s: invalid work phone: '%s'\n"), - Prog, workph); -- fail_exit (E_NOPERM); -+ fail_exit (E_NOPERM, process_selinux); - } - if (valid_field (homeph, ":,=\n") != 0) { - fprintf (stderr, _("%s: invalid home phone: '%s'\n"), - Prog, homeph); -- fail_exit (E_NOPERM); -+ fail_exit (E_NOPERM, process_selinux); - } - err = valid_field (slop, ":\n"); - if (err > 0) { -@@ -597,7 +604,7 @@ static void check_fields (void) - fprintf (stderr, - _("%s: '%s' contains illegal characters\n"), - Prog, slop); -- fail_exit (E_NOPERM); -+ fail_exit (E_NOPERM, process_selinux); - } - } - -@@ -622,6 +629,8 @@ int main (int argc, char **argv) - char new_gecos[BUFSIZ]; - char *user; - const struct passwd *pw; -+ struct option_flags flags; -+ bool process_selinux; - - sanitize_env (); - check_fds (); -@@ -644,7 +653,7 @@ int main (int argc, char **argv) - OPENLOG (Prog); - - /* parse the command line options */ -- process_flags (argc, argv); -+ process_flags (argc, argv, &flags); - - /* - * Get the name of the user to check. It is either the command line -@@ -653,14 +662,14 @@ int main (int argc, char **argv) - if (optind < argc) { - if (!is_valid_user_name (argv[optind])) { - fprintf (stderr, _("%s: Provided user name is not a valid name\n"), Prog); -- fail_exit (E_NOPERM); -+ fail_exit (E_NOPERM, process_selinux); - } - user = argv[optind]; - pw = xgetpwnam (user); - if (NULL == pw) { - fprintf (stderr, _("%s: user '%s' does not exist\n"), Prog, - user); -- fail_exit (E_NOPERM); -+ fail_exit (E_NOPERM, process_selinux); - } - } else { - pw = get_my_pwent (); -@@ -670,7 +679,7 @@ int main (int argc, char **argv) - Prog); - SYSLOG ((LOG_WARN, "Cannot determine the user name of the caller (UID %lu)", - (unsigned long) getuid ())); -- fail_exit (E_NOPERM); -+ fail_exit (E_NOPERM, process_selinux); - } - user = xstrdup (pw->pw_name); - } -@@ -695,7 +704,7 @@ int main (int argc, char **argv) - /* - * Check all of the fields for valid information - */ -- check_fields (); -+ check_fields (process_selinux); - - /* - * Build the new GECOS field by plastering all the pieces together, -@@ -704,14 +713,14 @@ int main (int argc, char **argv) - if ((strlen (fullnm) + strlen (roomno) + strlen (workph) + - strlen (homeph) + strlen (slop)) > (unsigned int) 80) { - fprintf (stderr, _("%s: fields too long\n"), Prog); -- fail_exit (E_NOPERM); -+ fail_exit (E_NOPERM, process_selinux); - } - SNPRINTF(new_gecos, "%s,%s,%s,%s%s%s", - fullnm, roomno, workph, homeph, - (!streq(slop, "")) ? "," : "", slop); - - /* Rewrite the user's gecos in the passwd file */ -- update_gecos (user, new_gecos); -+ update_gecos (user, new_gecos, &flags); - - SYSLOG ((LOG_INFO, "changed user '%s' information", user)); - -diff -up shadow-4.18.0/src/chpasswd.c.test-chroot4 shadow-4.18.0/src/chpasswd.c ---- shadow-4.18.0/src/chpasswd.c.test-chroot4 2025-07-04 11:17:37.593768325 +0200 -+++ shadow-4.18.0/src/chpasswd.c 2025-07-04 11:18:06.785652711 +0200 -@@ -38,6 +38,11 @@ - - #define IS_CRYPT_METHOD(str) ((crypt_method != NULL && streq(crypt_method, str)) ? true : false) - -+struct option_flags { -+ bool chroot; -+ bool prefix; -+}; -+ - /* - * Global variables - */ -@@ -67,21 +72,21 @@ static bool pw_locked = false; - static bool spw_locked = false; - - /* local function prototypes */ --NORETURN static void fail_exit (int code); -+NORETURN static void fail_exit (int code, bool process_selinux); - NORETURN static void usage (int status); --static void process_flags (int argc, char **argv); -+static void process_flags (int argc, char **argv, struct option_flags *flags); - static void check_flags (void); - static void check_perms (void); --static void open_files (void); --static void close_files (void); -+static void open_files (struct option_flags *flags); -+static void close_files (struct option_flags *flags); - - /* - * fail_exit - exit with a failure code after unlocking the files - */ --static void fail_exit (int code) -+static void fail_exit (int code, bool process_selinux) - { - if (pw_locked) { -- if (pw_unlock (true) == 0) { -+ if (pw_unlock (process_selinux) == 0) { - fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, pw_dbname ()); - SYSLOG ((LOG_ERR, "failed to unlock %s", pw_dbname ())); - /* continue */ -@@ -89,7 +94,7 @@ static void fail_exit (int code) - } - - if (spw_locked) { -- if (spw_unlock (true) == 0) { -+ if (spw_unlock (process_selinux) == 0) { - fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, spw_dbname ()); - SYSLOG ((LOG_ERR, "failed to unlock %s", spw_dbname ())); - /* continue */ -@@ -147,7 +152,7 @@ usage (int status) - * - * It will not return if an error is encountered. - */ --static void process_flags (int argc, char **argv) -+static void process_flags (int argc, char **argv, struct option_flags *flags) - { - int c; - #if defined(USE_SHA_CRYPT) || defined(USE_BCRYPT) || defined(USE_YESCRYPT) -@@ -187,8 +192,10 @@ static void process_flags (int argc, cha - md5flg = true; - break; - case 'R': /* no-op, handled in process_root_flag () */ -+ flags->chroot = true; - break; - case 'P': /* no-op, handled in process_prefix_flag () */ -+ flags->prefix = true; - break; - #if defined(USE_SHA_CRYPT) || defined(USE_BCRYPT) || defined(USE_YESCRYPT) - case 's': -@@ -335,8 +342,12 @@ static void check_perms (void) - /* - * open_files - lock and open the password databases - */ --static void open_files (void) -+static void open_files (struct option_flags *flags) - { -+ bool process_selinux; -+ -+ process_selinux = !flags->chroot && !flags->prefix; -+ - /* - * Lock the password file and open it for reading and writing. This - * will bring all of the entries into memory where they may be updated. -@@ -345,13 +356,13 @@ static void open_files (void) - fprintf (stderr, - _("%s: cannot lock %s; try again later.\n"), - Prog, pw_dbname ()); -- fail_exit (1); -+ fail_exit (1, process_selinux); - } - pw_locked = true; - if (pw_open (O_CREAT | O_RDWR) == 0) { - fprintf (stderr, - _("%s: cannot open %s\n"), Prog, pw_dbname ()); -- fail_exit (1); -+ fail_exit (1, process_selinux); - } - - /* Do the same for the shadowed database, if it exist */ -@@ -360,14 +371,14 @@ static void open_files (void) - fprintf (stderr, - _("%s: cannot lock %s; try again later.\n"), - Prog, spw_dbname ()); -- fail_exit (1); -+ fail_exit (1, process_selinux); - } - spw_locked = true; - if (spw_open (O_CREAT | O_RDWR) == 0) { - fprintf (stderr, - _("%s: cannot open %s\n"), - Prog, spw_dbname ()); -- fail_exit (1); -+ fail_exit (1, process_selinux); - } - } - } -@@ -375,17 +386,21 @@ static void open_files (void) - /* - * close_files - close and unlock the password databases - */ --static void close_files (void) -+static void close_files (struct option_flags *flags) - { -+ bool process_selinux; -+ -+ process_selinux = !flags->chroot && !flags->prefix; -+ - if (is_shadow_pwd) { -- if (spw_close (true) == 0) { -+ if (spw_close (process_selinux) == 0) { - fprintf (stderr, - _("%s: failure while writing changes to %s\n"), - Prog, spw_dbname ()); - SYSLOG ((LOG_ERR, "failure while writing changes to %s", spw_dbname ())); -- fail_exit (1); -+ fail_exit (1, process_selinux); - } -- if (spw_unlock (true) == 0) { -+ if (spw_unlock (process_selinux) == 0) { - fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, spw_dbname ()); - SYSLOG ((LOG_ERR, "failed to unlock %s", spw_dbname ())); - /* continue */ -@@ -393,14 +408,14 @@ static void close_files (void) - spw_locked = false; - } - -- if (pw_close (true) == 0) { -+ if (pw_close (process_selinux) == 0) { - fprintf (stderr, - _("%s: failure while writing changes to %s\n"), - Prog, pw_dbname ()); - SYSLOG ((LOG_ERR, "failure while writing changes to %s", pw_dbname ())); -- fail_exit (1); -+ fail_exit (1, process_selinux); - } -- if (pw_unlock (true) == 0) { -+ if (pw_unlock (process_selinux) == 0) { - fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, pw_dbname ()); - SYSLOG ((LOG_ERR, "failed to unlock %s", pw_dbname ())); - /* continue */ -@@ -456,6 +471,8 @@ int main (int argc, char **argv) - - bool errors = false; - intmax_t line = 0; -+ struct option_flags flags; -+ bool process_selinux; - - log_set_progname(Prog); - log_set_logfd(stderr); -@@ -470,7 +487,8 @@ int main (int argc, char **argv) - } - #endif /* WITH_SELINUX */ - -- process_flags (argc, argv); -+ process_flags (argc, argv, &flags); -+ process_selinux = !flags.chroot && !flags.prefix; - - salt = get_salt(); - process_root_flag ("-R", argc, argv); -@@ -496,7 +514,7 @@ int main (int argc, char **argv) - { - is_shadow_pwd = spw_file_present (); - -- open_files (); -+ open_files (&flags); - } - - /* -@@ -573,7 +591,7 @@ int main (int argc, char **argv) - fprintf (stderr, - _("%s: failed to crypt password with salt '%s': %s\n"), - Prog, salt, strerror (errno)); -- fail_exit (1); -+ fail_exit (1, process_selinux); - } - } - -@@ -690,7 +708,7 @@ int main (int argc, char **argv) - _("%s: error detected, changes ignored\n"), - Prog); - } -- fail_exit (1); -+ fail_exit (1, process_selinux); - } - - #ifdef ACCT_TOOLS_SETUID -@@ -700,7 +718,7 @@ int main (int argc, char **argv) - #endif /* ACCT_TOOLS_SETUID */ - { - /* Save the changes */ -- close_files (); -+ close_files (&flags); - } - - nscd_flush_cache ("passwd"); -diff -up shadow-4.18.0/src/grpconv.c.test-chroot4 shadow-4.18.0/src/grpconv.c ---- shadow-4.18.0/src/grpconv.c.test-chroot4 2025-07-04 11:17:37.598073521 +0200 -+++ shadow-4.18.0/src/grpconv.c 2025-07-04 11:17:37.612354766 +0200 -@@ -39,6 +39,12 @@ - #include "shadowlog.h" - #include "sssd.h" - -+/* -+ * Structures -+ */ -+struct option_flags { -+ bool chroot; -+}; - - /* - * Global variables -@@ -49,14 +55,14 @@ static bool gr_locked = false; - static bool sgr_locked = false; - - /* local function prototypes */ --static void fail_exit (int status); -+static void fail_exit (int status, bool process_selinux); - static void usage (int status); --static void process_flags (int argc, char **argv); -+static void process_flags (int argc, char **argv, struct option_flags *flags); - --static void fail_exit (int status) -+static void fail_exit (int status, bool process_selinux) - { - if (gr_locked) { -- if (gr_unlock (true) == 0) { -+ if (gr_unlock (process_selinux) == 0) { - fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, gr_dbname ()); - SYSLOG ((LOG_ERR, "failed to unlock %s", gr_dbname ())); - /* continue */ -@@ -64,7 +70,7 @@ static void fail_exit (int status) - } - - if (sgr_locked) { -- if (sgr_unlock (true) == 0) { -+ if (sgr_unlock (process_selinux) == 0) { - fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, sgr_dbname ()); - SYSLOG ((LOG_ERR, "failed to unlock %s", sgr_dbname ())); - /* continue */ -@@ -93,7 +99,7 @@ static void usage (int status) - * - * It will not return if an error is encountered. - */ --static void process_flags (int argc, char **argv) -+static void process_flags (int argc, char **argv, struct option_flags *flags) - { - /* - * Parse the command line options. -@@ -112,6 +118,7 @@ static void process_flags (int argc, cha - usage (E_SUCCESS); - /*@notreached@*/break; - case 'R': /* no-op, handled in process_root_flag () */ -+ flags->chroot = true; - break; - default: - usage (E_USAGE); -@@ -129,6 +136,8 @@ int main (int argc, char **argv) - struct group grent; - const struct sgrp *sg; - struct sgrp sgent; -+ struct option_flags flags; -+ bool process_selinux; - - log_set_progname(Prog); - log_set_logfd(stderr); -@@ -141,30 +150,31 @@ int main (int argc, char **argv) - - OPENLOG (Prog); - -- process_flags (argc, argv); -+ process_flags (argc, argv, &flags); -+ process_selinux = !flags.chroot; - - if (gr_lock () == 0) { - fprintf (stderr, - _("%s: cannot lock %s; try again later.\n"), - Prog, gr_dbname ()); -- fail_exit (5); -+ fail_exit (5, process_selinux); - } - gr_locked = true; - if (gr_open (O_CREAT | O_RDWR) == 0) { - fprintf (stderr, _("%s: cannot open %s\n"), Prog, gr_dbname ()); -- fail_exit (1); -+ fail_exit (1, process_selinux); - } - - if (sgr_lock () == 0) { - fprintf (stderr, - _("%s: cannot lock %s; try again later.\n"), - Prog, sgr_dbname ()); -- fail_exit (5); -+ fail_exit (5, process_selinux); - } - sgr_locked = true; - if (sgr_open (O_CREAT | O_RDWR) == 0) { - fprintf (stderr, _("%s: cannot open %s\n"), Prog, sgr_dbname ()); -- fail_exit (1); -+ fail_exit (1, process_selinux); - } - - /* -@@ -183,7 +193,7 @@ int main (int argc, char **argv) - fprintf (stderr, - _("%s: cannot remove entry '%s' from %s\n"), - Prog, sg->sg_namp, sgr_dbname ()); -- fail_exit (3); -+ fail_exit (3, process_selinux); - } - (void) sgr_rewind (); - } -@@ -221,7 +231,7 @@ int main (int argc, char **argv) - fprintf (stderr, - _("%s: failed to prepare the new %s entry '%s'\n"), - Prog, sgr_dbname (), sgent.sg_namp); -- fail_exit (3); -+ fail_exit (3, process_selinux); - } - /* remove password from /etc/group */ - grent = *gr; -@@ -230,30 +240,30 @@ int main (int argc, char **argv) - fprintf (stderr, - _("%s: failed to prepare the new %s entry '%s'\n"), - Prog, gr_dbname (), grent.gr_name); -- fail_exit (3); -+ fail_exit (3, process_selinux); - } - } - -- if (sgr_close (true) == 0) { -+ if (sgr_close (process_selinux) == 0) { - fprintf (stderr, - _("%s: failure while writing changes to %s\n"), - Prog, sgr_dbname ()); - SYSLOG ((LOG_ERR, "failure while writing changes to %s", sgr_dbname ())); -- fail_exit (3); -+ fail_exit (3, process_selinux); - } -- if (gr_close (true) == 0) { -+ if (gr_close (process_selinux) == 0) { - fprintf (stderr, - _("%s: failure while writing changes to %s\n"), - Prog, gr_dbname ()); - SYSLOG ((LOG_ERR, "failure while writing changes to %s", gr_dbname ())); -- fail_exit (3); -+ fail_exit (3, process_selinux); - } -- if (sgr_unlock (true) == 0) { -+ if (sgr_unlock (process_selinux) == 0) { - fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, sgr_dbname ()); - SYSLOG ((LOG_ERR, "failed to unlock %s", sgr_dbname ())); - /* continue */ - } -- if (gr_unlock (true) == 0) { -+ if (gr_unlock (process_selinux) == 0) { - fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, gr_dbname ()); - SYSLOG ((LOG_ERR, "failed to unlock %s", gr_dbname ())); - /* continue */ -diff -up shadow-4.18.0/src/grpunconv.c.test-chroot4 shadow-4.18.0/src/grpunconv.c ---- shadow-4.18.0/src/grpunconv.c.test-chroot4 2025-07-04 11:17:37.598110132 +0200 -+++ shadow-4.18.0/src/grpunconv.c 2025-07-04 11:17:37.612706894 +0200 -@@ -38,6 +38,12 @@ - #include "sgroupio.h" - #include "shadowlog.h" - -+/* -+ * Structures -+ */ -+struct option_flags { -+ bool chroot; -+}; - - /* - * Global variables -@@ -48,14 +54,14 @@ static bool gr_locked = false; - static bool sgr_locked = false; - - /* local function prototypes */ --static void fail_exit (int status); -+static void fail_exit (int status, bool process_selinux); - static void usage (int status); --static void process_flags (int argc, char **argv); -+static void process_flags (int argc, char **argv, struct option_flags *flags); - --static void fail_exit (int status) -+static void fail_exit (int status, bool process_selinux) - { - if (gr_locked) { -- if (gr_unlock (true) == 0) { -+ if (gr_unlock (process_selinux) == 0) { - fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, gr_dbname ()); - SYSLOG ((LOG_ERR, "failed to unlock %s", gr_dbname ())); - /* continue */ -@@ -63,7 +69,7 @@ static void fail_exit (int status) - } - - if (sgr_locked) { -- if (sgr_unlock (true) == 0) { -+ if (sgr_unlock (process_selinux) == 0) { - fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, sgr_dbname ()); - SYSLOG ((LOG_ERR, "failed to unlock %s", sgr_dbname ())); - /* continue */ -@@ -92,7 +98,7 @@ static void usage (int status) - * - * It will not return if an error is encountered. - */ --static void process_flags (int argc, char **argv) -+static void process_flags (int argc, char **argv, struct option_flags *flags) - { - /* - * Parse the command line options. -@@ -111,6 +117,7 @@ static void process_flags (int argc, cha - usage (E_SUCCESS); - /*@notreached@*/break; - case 'R': /* no-op, handled in process_root_flag () */ -+ flags->chroot = true; - break; - default: - usage (E_USAGE); -@@ -127,6 +134,8 @@ int main (int argc, char **argv) - const struct group *gr; - struct group grent; - const struct sgrp *sg; -+ struct option_flags flags; -+ bool process_selinux; - - log_set_progname(Prog); - log_set_logfd(stderr); -@@ -139,7 +148,8 @@ int main (int argc, char **argv) - - OPENLOG (Prog); - -- process_flags (argc, argv); -+ process_flags (argc, argv, &flags); -+ process_selinux = !flags.chroot; - - if (sgr_file_present () == 0) { - exit (0); /* no /etc/gshadow, nothing to do */ -@@ -149,26 +159,26 @@ int main (int argc, char **argv) - fprintf (stderr, - _("%s: cannot lock %s; try again later.\n"), - Prog, gr_dbname ()); -- fail_exit (5); -+ fail_exit (5, process_selinux); - } - gr_locked = true; - if (gr_open (O_CREAT | O_RDWR) == 0) { - fprintf (stderr, - _("%s: cannot open %s\n"), Prog, gr_dbname ()); -- fail_exit (1); -+ fail_exit (1, process_selinux); - } - - if (sgr_lock () == 0) { - fprintf (stderr, - _("%s: cannot lock %s; try again later.\n"), - Prog, sgr_dbname ()); -- fail_exit (5); -+ fail_exit (5, process_selinux); - } - sgr_locked = true; - if (sgr_open (O_RDONLY) == 0) { - fprintf (stderr, - _("%s: cannot open %s\n"), Prog, sgr_dbname ()); -- fail_exit (1); -+ fail_exit (1, process_selinux); - } - - /* -@@ -186,19 +196,19 @@ int main (int argc, char **argv) - fprintf (stderr, - _("%s: failed to prepare the new %s entry '%s'\n"), - Prog, gr_dbname (), grent.gr_name); -- fail_exit (3); -+ fail_exit (3, process_selinux); - } - } - } - -- (void) sgr_close (true); /* was only open O_RDONLY */ -+ (void) sgr_close (process_selinux); /* was only open O_RDONLY */ - -- if (gr_close (true) == 0) { -+ if (gr_close (process_selinux) == 0) { - fprintf (stderr, - _("%s: failure while writing changes to %s\n"), - Prog, gr_dbname ()); - SYSLOG ((LOG_ERR, "failure while writing changes to %s", gr_dbname ())); -- fail_exit (3); -+ fail_exit (3, process_selinux); - } - - if (unlink (SGROUP_FILE) != 0) { -@@ -206,16 +216,16 @@ int main (int argc, char **argv) - _("%s: cannot delete %s\n"), - Prog, SGROUP_FILE); - SYSLOG ((LOG_ERR, "cannot delete %s", SGROUP_FILE)); -- fail_exit (3); -+ fail_exit (3, process_selinux); - } - -- if (gr_unlock (true) == 0) { -+ if (gr_unlock (process_selinux) == 0) { - fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, gr_dbname ()); - SYSLOG ((LOG_ERR, "failed to unlock %s", gr_dbname ())); - /* continue */ - } - -- if (sgr_unlock (true) == 0) { -+ if (sgr_unlock (process_selinux) == 0) { - fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, sgr_dbname ()); - SYSLOG ((LOG_ERR, "failed to unlock %s", sgr_dbname ())); - /* continue */ - -From e7634eac3da8b6a2a046b7e8ab6148d07618c7f5 Mon Sep 17 00:00:00 2001 -From: Iker Pedrosa -Date: Thu, 3 Jul 2025 11:59:32 +0200 -Subject: [PATCH 01/12] 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 ---- - src/chsh.c | 25 +++++++++++++++++-------- - 1 file changed, 17 insertions(+), 8 deletions(-) - -diff --git a/src/chsh.c b/src/chsh.c -index 4403d412..f460e329 100644 ---- a/src/chsh.c -+++ b/src/chsh.c -@@ -47,6 +47,9 @@ - #define ETCDIR "/etc" - #endif - -+struct option_flags { -+ bool chroot; -+}; - - /* - * Global variables -@@ -66,9 +69,10 @@ NORETURN static void usage (int status); - static void new_fields (void); - static bool shell_is_listed (const char *); - static bool is_restricted_shell (const char *); --static void process_flags (int argc, char **argv); -+static void process_flags (int argc, char **argv, struct option_flags *flags); - static void check_perms (const struct passwd *pw); --static void update_shell (const char *user, char *loginsh); -+static void update_shell (const char *user, char *loginsh, -+ struct option_flags *flags); - - /* - * fail_exit - do some cleanup and exit with the given error code -@@ -217,7 +221,7 @@ static bool shell_is_listed (const char *sh) - * - * It will not return if an error is encountered. - */ --static void process_flags (int argc, char **argv) -+static void process_flags (int argc, char **argv, struct option_flags *flags) - { - int c; - static struct option long_options[] = { -@@ -234,6 +238,7 @@ static void process_flags (int argc, char **argv) - usage (E_SUCCESS); - /*@notreached@*/break; - case 'R': /* no-op, handled in process_root_flag () */ -+ flags->chroot = true; - break; - case 's': - sflg = true; -@@ -359,10 +364,13 @@ static void check_perms (const struct passwd *pw) - * - * It will not return in case of error. - */ --static void update_shell (const char *user, char *newshell) -+static void update_shell (const char *user, char *newshell, struct option_flags *flags) - { - const struct passwd *pw; /* Password entry from /etc/passwd */ - struct passwd pwent; /* New password entry */ -+ bool process_selinux; -+ -+ process_selinux = !flags->chroot; - - /* - * Before going any further, raise the ulimit to prevent -@@ -428,12 +436,12 @@ static void update_shell (const char *user, char *newshell) - /* - * Changes have all been made, so commit them and unlock the file. - */ -- if (pw_close (true) == 0) { -+ if (pw_close (process_selinux) == 0) { - fprintf (stderr, _("%s: failure while writing changes to %s\n"), Prog, pw_dbname ()); - SYSLOG ((LOG_ERR, "failure while writing changes to %s", pw_dbname ())); - fail_exit (1); - } -- if (pw_unlock (true) == 0) { -+ if (pw_unlock (process_selinux) == 0) { - fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, pw_dbname ()); - SYSLOG ((LOG_ERR, "failed to unlock %s", pw_dbname ())); - /* continue */ -@@ -451,6 +459,7 @@ int main (int argc, char **argv) - { - char *user; /* User name */ - const struct passwd *pw; /* Password entry from /etc/passwd */ -+ struct option_flags flags; - - sanitize_env (); - check_fds (); -@@ -472,7 +481,7 @@ int main (int argc, char **argv) - OPENLOG (Prog); - - /* parse the command line options */ -- process_flags (argc, argv); -+ process_flags (argc, argv, &flags); - - /* - * Get the name of the user to check. It is either the command line -@@ -555,7 +564,7 @@ int main (int argc, char **argv) - } - } - -- update_shell (user, loginsh); -+ update_shell (user, loginsh, &flags); - - SYSLOG ((LOG_INFO, "changed user '%s' shell to '%s'", user, loginsh)); - --- -2.50.0 - - -From cbcf54b3e5c77a4992bca0ae4f32f0060d120884 Mon Sep 17 00:00:00 2001 -From: Iker Pedrosa -Date: Thu, 3 Jul 2025 12:03:30 +0200 -Subject: [PATCH 02/12] 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 ---- - src/chsh.c | 45 +++++++++++++++++++++++++-------------------- - 1 file changed, 25 insertions(+), 20 deletions(-) - -diff --git a/src/chsh.c b/src/chsh.c -index f460e329..77cd30e5 100644 ---- a/src/chsh.c -+++ b/src/chsh.c -@@ -64,13 +64,13 @@ static bool pw_locked = false; - /* external identifiers */ - - /* local function prototypes */ --NORETURN static void fail_exit (int code); -+NORETURN static void fail_exit (int code, bool process_selinux); - NORETURN static void usage (int status); - static void new_fields (void); - static bool shell_is_listed (const char *); - static bool is_restricted_shell (const char *); - static void process_flags (int argc, char **argv, struct option_flags *flags); --static void check_perms (const struct passwd *pw); -+static void check_perms (const struct passwd *pw, struct option_flags *flags); - static void update_shell (const char *user, char *loginsh, - struct option_flags *flags); - -@@ -79,10 +79,10 @@ static void update_shell (const char *user, char *loginsh, - */ - NORETURN - static void --fail_exit (int code) -+fail_exit (int code, bool process_selinux) - { - if (pw_locked) { -- if (pw_unlock (true) == 0) { -+ if (pw_unlock (process_selinux) == 0) { - fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, pw_dbname ()); - SYSLOG ((LOG_ERR, "failed to unlock %s", pw_dbname ())); - /* continue */ -@@ -268,13 +268,16 @@ static void process_flags (int argc, char **argv, struct option_flags *flags) - * - * It will not return if the user is not allowed. - */ --static void check_perms (const struct passwd *pw) -+static void check_perms (const struct passwd *pw, struct option_flags *flags) - { - #ifdef USE_PAM - pam_handle_t *pamh = NULL; - int retval; - struct passwd *pampw; - #endif -+ bool process_selinux; -+ -+ process_selinux = !flags->chroot; - - /* - * Non-privileged users are only allowed to change the shell if the -@@ -285,7 +288,7 @@ static void check_perms (const struct passwd *pw) - fprintf (stderr, - _("You may not change the shell for '%s'.\n"), - pw->pw_name); -- fail_exit (1); -+ fail_exit (1, process_selinux); - } - - /* -@@ -297,7 +300,7 @@ static void check_perms (const struct passwd *pw) - fprintf (stderr, - _("You may not change the shell for '%s'.\n"), - pw->pw_name); -- fail_exit (1); -+ fail_exit (1, process_selinux); - } - #ifdef WITH_SELINUX - /* -@@ -310,7 +313,7 @@ static void check_perms (const struct passwd *pw) - fprintf (stderr, - _("You may not change the shell for '%s'.\n"), - pw->pw_name); -- fail_exit (1); -+ fail_exit (1, process_selinux); - } - #endif - -@@ -381,7 +384,7 @@ static void update_shell (const char *user, char *newshell, struct option_flags - if (setuid (0) != 0) { - SYSLOG ((LOG_ERR, "can't setuid(0)")); - fputs (_("Cannot change ID to root.\n"), stderr); -- fail_exit (1); -+ fail_exit (1, process_selinux); - } - pwd_init (); - -@@ -392,13 +395,13 @@ static void update_shell (const char *user, char *newshell, struct option_flags - if (pw_lock () == 0) { - fprintf (stderr, _("%s: cannot lock %s; try again later.\n"), - Prog, pw_dbname ()); -- fail_exit (1); -+ fail_exit (1, process_selinux); - } - pw_locked = true; - if (pw_open (O_CREAT | O_RDWR) == 0) { - fprintf (stderr, _("%s: cannot open %s\n"), Prog, pw_dbname ()); - SYSLOG ((LOG_WARN, "cannot open %s", pw_dbname ())); -- fail_exit (1); -+ fail_exit (1, process_selinux); - } - - /* -@@ -412,7 +415,7 @@ static void update_shell (const char *user, char *newshell, struct option_flags - fprintf (stderr, - _("%s: user '%s' does not exist in %s\n"), - Prog, user, pw_dbname ()); -- fail_exit (1); -+ fail_exit (1, process_selinux); - } - - /* -@@ -430,7 +433,7 @@ static void update_shell (const char *user, char *newshell, struct option_flags - fprintf (stderr, - _("%s: failed to prepare the new %s entry '%s'\n"), - Prog, pw_dbname (), pwent.pw_name); -- fail_exit (1); -+ fail_exit (1, process_selinux); - } - - /* -@@ -439,7 +442,7 @@ static void update_shell (const char *user, char *newshell, struct option_flags - if (pw_close (process_selinux) == 0) { - fprintf (stderr, _("%s: failure while writing changes to %s\n"), Prog, pw_dbname ()); - SYSLOG ((LOG_ERR, "failure while writing changes to %s", pw_dbname ())); -- fail_exit (1); -+ fail_exit (1, process_selinux); - } - if (pw_unlock (process_selinux) == 0) { - fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, pw_dbname ()); -@@ -460,6 +463,7 @@ int main (int argc, char **argv) - char *user; /* User name */ - const struct passwd *pw; /* Password entry from /etc/passwd */ - struct option_flags flags; -+ bool process_selinux; - - sanitize_env (); - check_fds (); -@@ -482,6 +486,7 @@ int main (int argc, char **argv) - - /* parse the command line options */ - process_flags (argc, argv, &flags); -+ process_selinux = !flags.chroot; - - /* - * Get the name of the user to check. It is either the command line -@@ -490,14 +495,14 @@ int main (int argc, char **argv) - if (optind < argc) { - if (!is_valid_user_name (argv[optind])) { - fprintf (stderr, _("%s: Provided user name is not a valid name\n"), Prog); -- fail_exit (1); -+ fail_exit (1, process_selinux); - } - user = argv[optind]; - pw = xgetpwnam (user); - if (NULL == pw) { - fprintf (stderr, - _("%s: user '%s' does not exist\n"), Prog, user); -- fail_exit (1); -+ fail_exit (1, process_selinux); - } - } else { - pw = get_my_pwent (); -@@ -507,12 +512,12 @@ int main (int argc, char **argv) - Prog); - SYSLOG ((LOG_WARN, "Cannot determine the user name of the caller (UID %lu)", - (unsigned long) getuid ())); -- fail_exit (1); -+ fail_exit (1, process_selinux); - } - user = xstrdup (pw->pw_name); - } - -- check_perms (pw); -+ check_perms (pw, &flags); - - /* - * Now get the login shell. Either get it from the password -@@ -539,7 +544,7 @@ int main (int argc, char **argv) - */ - if (valid_field (loginsh, ":,=\n") != 0) { - fprintf (stderr, _("%s: Invalid entry: %s\n"), Prog, loginsh); -- fail_exit (1); -+ fail_exit (1, process_selinux); - } - if (!streq(loginsh, "") - && (loginsh[0] != '/' -@@ -550,7 +555,7 @@ int main (int argc, char **argv) - fprintf (stderr, _("%s: Warning: %s is an invalid shell\n"), Prog, loginsh); - } else { - fprintf (stderr, _("%s: %s is an invalid shell\n"), Prog, loginsh); -- fail_exit (1); -+ fail_exit (1, process_selinux); - } - } - --- -2.50.0 - - -From 58600326353a2198df7f8753e2ad8ffa216b68cc Mon Sep 17 00:00:00 2001 -From: Iker Pedrosa -Date: Thu, 3 Jul 2025 12:35:44 +0200 -Subject: [PATCH 03/12] 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 ---- - src/passwd.c | 41 ++++++++++++++++++++++++++++------------- - 1 file changed, 28 insertions(+), 13 deletions(-) - -diff --git a/src/passwd.c b/src/passwd.c -index 512dc572..6d6f319e 100644 ---- a/src/passwd.c -+++ b/src/passwd.c -@@ -53,6 +53,12 @@ - #define E_PWDBUSY 5 /* passwd file busy, try again later */ - #define E_BAD_ARG 6 /* invalid argument to option */ - #define E_PAM_ERR 10 /* PAM returned an error */ -+ -+struct option_flags { -+ bool chroot; -+ bool prefix; -+}; -+ - /* - * Global variables - */ -@@ -131,9 +137,9 @@ static void print_status (const struct passwd *); - NORETURN static void fail_exit (int); - NORETURN static void oom (void); - static char *update_crypt_pw (char *); --static void update_noshadow (void); -+static void update_noshadow (struct option_flags *flags); - --static void update_shadow (void); -+static void update_shadow (struct option_flags *flags); - - /* - * usage - print command usage and exit -@@ -549,10 +555,13 @@ static char *update_crypt_pw (char *cp) - } - - --static void update_noshadow (void) -+static void update_noshadow (struct option_flags *flags) - { - const struct passwd *pw; - struct passwd *npw; -+ bool process_selinux; -+ -+ process_selinux = !flags->chroot && !flags->prefix; - - if (pw_lock () == 0) { - (void) fprintf (stderr, -@@ -586,14 +595,14 @@ static void update_noshadow (void) - Prog, pw_dbname (), npw->pw_name); - fail_exit (E_FAILURE); - } -- if (pw_close (true) == 0) { -+ if (pw_close (process_selinux) == 0) { - (void) fprintf (stderr, - _("%s: failure while writing changes to %s\n"), - Prog, pw_dbname ()); - SYSLOG ((LOG_ERR, "failure while writing changes to %s", pw_dbname ())); - fail_exit (E_FAILURE); - } -- if (pw_unlock (true) == 0) { -+ if (pw_unlock (process_selinux) == 0) { - (void) fprintf (stderr, - _("%s: failed to unlock %s\n"), - Prog, pw_dbname ()); -@@ -603,10 +612,13 @@ static void update_noshadow (void) - pw_locked = false; - } - --static void update_shadow (void) -+static void update_shadow (struct option_flags *flags) - { - const struct spwd *sp; - struct spwd *nsp; -+ bool process_selinux; -+ -+ process_selinux = !flags->chroot && !flags->prefix; - - if (spw_lock () == 0) { - (void) fprintf (stderr, -@@ -625,9 +637,9 @@ static void update_shadow (void) - sp = spw_locate (name); - if (NULL == sp) { - /* Try to update the password in /etc/passwd instead. */ -- (void) spw_close (true); -- update_noshadow (); -- if (spw_unlock (true) == 0) { -+ (void) spw_close (process_selinux); -+ update_noshadow (flags); -+ if (spw_unlock (process_selinux) == 0) { - (void) fprintf (stderr, - _("%s: failed to unlock %s\n"), - Prog, spw_dbname ()); -@@ -681,14 +693,14 @@ static void update_shadow (void) - Prog, spw_dbname (), nsp->sp_namp); - fail_exit (E_FAILURE); - } -- if (spw_close (true) == 0) { -+ if (spw_close (process_selinux) == 0) { - (void) fprintf (stderr, - _("%s: failure while writing changes to %s\n"), - Prog, spw_dbname ()); - SYSLOG ((LOG_ERR, "failure while writing changes to %s", spw_dbname ())); - fail_exit (E_FAILURE); - } -- if (spw_unlock (true) == 0) { -+ if (spw_unlock (process_selinux) == 0) { - (void) fprintf (stderr, - _("%s: failed to unlock %s\n"), - Prog, spw_dbname ()); -@@ -733,6 +745,7 @@ main(int argc, char **argv) - char *cp; /* Miscellaneous character pointing */ - - const struct spwd *sp; /* Shadow file entry for user */ -+ struct option_flags flags; - - sanitize_env (); - check_fds (); -@@ -849,8 +862,10 @@ main(int argc, char **argv) - } - break; - case 'R': /* no-op, handled in process_root_flag () */ -+ flags.chroot = true; - break; - case 'P': /* no-op, handled in process_prefix_flag () */ -+ flags.prefix = true; - break; - case 'S': - Sflg = true; /* ok for users */ -@@ -1107,9 +1122,9 @@ main(int argc, char **argv) - exit (E_NOPERM); - } - if (spw_file_present ()) { -- update_shadow (); -+ update_shadow (&flags); - } else { -- update_noshadow (); -+ update_noshadow (&flags); - } - - nscd_flush_cache ("passwd"); --- -2.50.0 - - -From d5a9144c809e808600c7dc730970efc04bc032fd Mon Sep 17 00:00:00 2001 -From: Iker Pedrosa -Date: Thu, 3 Jul 2025 12:48:18 +0200 -Subject: [PATCH 04/12] 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 ---- - src/passwd.c | 46 ++++++++++++++++++++++++---------------------- - 1 file changed, 24 insertions(+), 22 deletions(-) - -diff --git a/src/passwd.c b/src/passwd.c -index 6d6f319e..78859c96 100644 ---- a/src/passwd.c -+++ b/src/passwd.c -@@ -134,9 +134,9 @@ static int new_password (const struct passwd *); - static void check_password (const struct passwd *, const struct spwd *); - static /*@observer@*/const char *pw_status (const char *); - static void print_status (const struct passwd *); --NORETURN static void fail_exit (int); --NORETURN static void oom (void); --static char *update_crypt_pw (char *); -+NORETURN static void fail_exit (int, bool); -+NORETURN static void oom (bool process_selinux); -+static char *update_crypt_pw (char *, bool); - static void update_noshadow (struct option_flags *flags); - - static void update_shadow (struct option_flags *flags); -@@ -486,10 +486,10 @@ static void print_status (const struct passwd *pw) - - NORETURN - static void --fail_exit (int status) -+fail_exit (int status, bool process_selinux) - { - if (pw_locked) { -- if (pw_unlock (true) == 0) { -+ if (pw_unlock (process_selinux) == 0) { - (void) fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, pw_dbname ()); - SYSLOG ((LOG_ERR, "failed to unlock %s", pw_dbname ())); - /* continue */ -@@ -497,7 +497,7 @@ fail_exit (int status) - } - - if (spw_locked) { -- if (spw_unlock (true) == 0) { -+ if (spw_unlock (process_selinux) == 0) { - (void) fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, spw_dbname ()); - SYSLOG ((LOG_ERR, "failed to unlock %s", spw_dbname ())); - /* continue */ -@@ -509,13 +509,13 @@ fail_exit (int status) - - NORETURN - static void --oom (void) -+oom (bool process_selinux) - { - (void) fprintf (stderr, _("%s: out of memory\n"), Prog); -- fail_exit (E_FAILURE); -+ fail_exit (E_FAILURE, process_selinux); - } - --static char *update_crypt_pw (char *cp) -+static char *update_crypt_pw (char *cp, bool process_selinux) - { - if (!use_pam) - { -@@ -533,7 +533,7 @@ static char *update_crypt_pw (char *cp) - _("%s: unlocking the password would result in a passwordless account.\n" - "You should set a password with usermod -p to unlock the password of this account.\n"), - Prog); -- fail_exit (E_FAILURE); -+ fail_exit (E_FAILURE, process_selinux); - } else { - cp++; - } -@@ -575,32 +575,32 @@ static void update_noshadow (struct option_flags *flags) - _("%s: cannot open %s\n"), - Prog, pw_dbname ()); - SYSLOG ((LOG_WARN, "cannot open %s", pw_dbname ())); -- fail_exit (E_MISSING); -+ fail_exit (E_MISSING, process_selinux); - } - pw = pw_locate (name); - if (NULL == pw) { - (void) fprintf (stderr, - _("%s: user '%s' does not exist in %s\n"), - Prog, name, pw_dbname ()); -- fail_exit (E_NOPERM); -+ fail_exit (E_NOPERM, process_selinux); - } - npw = __pw_dup (pw); - if (NULL == npw) { -- oom (); -+ oom (process_selinux); - } -- npw->pw_passwd = update_crypt_pw (npw->pw_passwd); -+ npw->pw_passwd = update_crypt_pw (npw->pw_passwd, process_selinux); - if (pw_update (npw) == 0) { - (void) fprintf (stderr, - _("%s: failed to prepare the new %s entry '%s'\n"), - Prog, pw_dbname (), npw->pw_name); -- fail_exit (E_FAILURE); -+ fail_exit (E_FAILURE, process_selinux); - } - if (pw_close (process_selinux) == 0) { - (void) fprintf (stderr, - _("%s: failure while writing changes to %s\n"), - Prog, pw_dbname ()); - SYSLOG ((LOG_ERR, "failure while writing changes to %s", pw_dbname ())); -- fail_exit (E_FAILURE); -+ fail_exit (E_FAILURE, process_selinux); - } - if (pw_unlock (process_selinux) == 0) { - (void) fprintf (stderr, -@@ -632,7 +632,7 @@ static void update_shadow (struct option_flags *flags) - _("%s: cannot open %s\n"), - Prog, spw_dbname ()); - SYSLOG ((LOG_WARN, "cannot open %s", spw_dbname ())); -- fail_exit (E_FAILURE); -+ fail_exit (E_FAILURE, process_selinux); - } - sp = spw_locate (name); - if (NULL == sp) { -@@ -651,9 +651,9 @@ static void update_shadow (struct option_flags *flags) - } - nsp = __spw_dup (sp); - if (NULL == nsp) { -- oom (); -+ oom (process_selinux); - } -- nsp->sp_pwdp = update_crypt_pw (nsp->sp_pwdp); -+ nsp->sp_pwdp = update_crypt_pw (nsp->sp_pwdp, process_selinux); - if (xflg) { - nsp->sp_max = age_max; - } -@@ -691,14 +691,14 @@ static void update_shadow (struct option_flags *flags) - (void) fprintf (stderr, - _("%s: failed to prepare the new %s entry '%s'\n"), - Prog, spw_dbname (), nsp->sp_namp); -- fail_exit (E_FAILURE); -+ fail_exit (E_FAILURE, process_selinux); - } - if (spw_close (process_selinux) == 0) { - (void) fprintf (stderr, - _("%s: failure while writing changes to %s\n"), - Prog, spw_dbname ()); - SYSLOG ((LOG_ERR, "failure while writing changes to %s", spw_dbname ())); -- fail_exit (E_FAILURE); -+ fail_exit (E_FAILURE, process_selinux); - } - if (spw_unlock (process_selinux) == 0) { - (void) fprintf (stderr, -@@ -746,6 +746,7 @@ main(int argc, char **argv) - - const struct spwd *sp; /* Shadow file entry for user */ - struct option_flags flags; -+ bool process_selinux; - - sanitize_env (); - check_fds (); -@@ -912,6 +913,7 @@ main(int argc, char **argv) - } - } - } -+ process_selinux = !flags.chroot && !flags.prefix; - - /* - * Now I have to get the user name. The name will be gotten from the -@@ -931,7 +933,7 @@ main(int argc, char **argv) - if (optind < argc) { - if (!is_valid_user_name (argv[optind])) { - fprintf (stderr, _("%s: Provided user name is not a valid name\n"), Prog); -- fail_exit (E_NOPERM); -+ fail_exit (E_NOPERM, process_selinux); - } - name = argv[optind]; - } else { --- -2.50.0 - - -From a736c2af5702e98081bace5fde10a264f443d318 Mon Sep 17 00:00:00 2001 -From: Iker Pedrosa -Date: Thu, 3 Jul 2025 12:55:35 +0200 -Subject: [PATCH 05/12] 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 ---- - src/pwck.c | 46 ++++++++++++++++++++++++++++++---------------- - 1 file changed, 30 insertions(+), 16 deletions(-) - -diff --git a/src/pwck.c b/src/pwck.c -index 9c4cc29a..d0f2f1c9 100644 ---- a/src/pwck.c -+++ b/src/pwck.c -@@ -48,6 +48,10 @@ - #define E_CANTUPDATE 5 - #define E_CANTSORT 6 - -+struct option_flags { -+ bool chroot; -+}; -+ - /* - * Global variables - */ -@@ -71,10 +75,11 @@ static bool quiet = false; /* don't report warnings, only errors */ - /* local function prototypes */ - static void fail_exit (int code); - NORETURN static void usage (int status); --static void process_flags (int argc, char **argv); -+static void process_flags (int argc, char **argv, struct option_flags *flags); - static void open_files (void); --static void close_files (bool changed); --static void check_pw_file (bool *errors, bool *changed); -+static void close_files (bool changed, struct option_flags *flags); -+static void check_pw_file (bool *errors, bool *changed, -+ struct option_flags *flags); - static void check_spw_file (bool *errors, bool *changed); - - extern int allow_bad_names; -@@ -155,7 +160,7 @@ usage (int status) - * - * It will not return if an error is encountered. - */ --static void process_flags (int argc, char **argv) -+static void process_flags (int argc, char **argv, struct option_flags *flags) - { - int c; - static struct option long_options[] = { -@@ -188,6 +193,7 @@ static void process_flags (int argc, char **argv) - read_only = true; - break; - case 'R': /* no-op, handled in process_root_flag () */ -+ flags->chroot = true; - break; - case 's': - sort_mode = true; -@@ -302,14 +308,18 @@ static void open_files (void) - * changes are committed in the databases. The databases are - * unlocked anyway. - */ --static void close_files (bool changed) -+static void close_files (bool changed, struct option_flags *flags) - { -+ bool process_selinux; -+ -+ process_selinux = !flags->chroot; -+ - /* - * All done. If there were no change we can just abandon any - * changes to the files. - */ - if (changed) { -- if (pw_close (true) == 0) { -+ if (pw_close (process_selinux) == 0) { - fprintf (stderr, - _("%s: failure while writing changes to %s\n"), - Prog, pw_dbname ()); -@@ -320,7 +330,7 @@ static void close_files (bool changed) - } - fail_exit (E_CANTUPDATE); - } -- if (spw_opened && (spw_close (true) == 0)) { -+ if (spw_opened && (spw_close (process_selinux) == 0)) { - fprintf (stderr, - _("%s: failure while writing changes to %s\n"), - Prog, spw_dbname ()); -@@ -338,7 +348,7 @@ static void close_files (bool changed) - * Don't be anti-social - unlock the files when you're done. - */ - if (spw_locked) { -- if (spw_unlock (true) == 0) { -+ if (spw_unlock (process_selinux) == 0) { - fprintf (stderr, - _("%s: failed to unlock %s\n"), - Prog, spw_dbname ()); -@@ -351,7 +361,7 @@ static void close_files (bool changed) - } - spw_locked = false; - if (pw_locked) { -- if (pw_unlock (true) == 0) { -+ if (pw_unlock (process_selinux) == 0) { - fprintf (stderr, - _("%s: failed to unlock %s\n"), - Prog, pw_dbname ()); -@@ -368,13 +378,16 @@ static void close_files (bool changed) - /* - * check_pw_file - check the content of the passwd file - */ --static void check_pw_file (bool *errors, bool *changed) -+static void check_pw_file (bool *errors, bool *changed, struct option_flags *flags) - { - struct commonio_entry *pfe, *tpfe; - struct passwd *pwd; - const struct spwd *spw; - uid_t min_sys_id = getdef_ulong ("SYS_UID_MIN", 101UL); - uid_t max_sys_id = getdef_ulong ("SYS_UID_MAX", 999UL); -+ bool process_selinux; -+ -+ process_selinux = !flags->chroot; - - /* - * Loop through the entire password file. -@@ -581,7 +594,7 @@ static void check_pw_file (bool *errors, bool *changed) - _("%s: cannot open %s\n"), - Prog, spw_dbname ()); - *errors = true; -- if (spw_unlock (true) == 0) { -+ if (spw_unlock (process_selinux) == 0) { - fprintf (stderr, - _("%s: failed to unlock %s\n"), - Prog, spw_dbname ()); -@@ -657,7 +670,7 @@ static void check_pw_file (bool *errors, bool *changed) - } - #ifdef WITH_TCB - if (getdef_bool ("USE_TCB") && spw_locked) { -- if (spw_opened && (spw_close (true) == 0)) { -+ if (spw_opened && (spw_close (process_selinux) == 0)) { - fprintf (stderr, - _("%s: failure while writing changes to %s\n"), - Prog, spw_dbname ()); -@@ -669,7 +682,7 @@ static void check_pw_file (bool *errors, bool *changed) - } else { - spw_opened = false; - } -- if (spw_unlock (true) == 0) { -+ if (spw_unlock (process_selinux) == 0) { - fprintf (stderr, - _("%s: failed to unlock %s\n"), - Prog, spw_dbname ()); -@@ -840,6 +853,7 @@ int main (int argc, char **argv) - { - bool errors = false; - bool changed = false; -+ struct option_flags flags; - - log_set_progname(Prog); - log_set_logfd(stderr); -@@ -853,7 +867,7 @@ int main (int argc, char **argv) - OPENLOG (Prog); - - /* Parse the command line arguments */ -- process_flags (argc, argv); -+ process_flags (argc, argv, &flags); - - open_files (); - -@@ -874,14 +888,14 @@ int main (int argc, char **argv) - } - changed = true; - } else { -- check_pw_file (&errors, &changed); -+ check_pw_file (&errors, &changed, &flags); - - if (is_shadow) { - check_spw_file (&errors, &changed); - } - } - -- close_files (changed); -+ close_files (changed, &flags); - - if (!read_only) { - nscd_flush_cache ("passwd"); --- -2.50.0 - - -From 09957a9748ae6cd28e0826d4cacd939ac288ca1b Mon Sep 17 00:00:00 2001 -From: Iker Pedrosa -Date: Thu, 3 Jul 2025 12:58:58 +0200 -Subject: [PATCH 06/12] 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 ---- - src/pwck.c | 39 ++++++++++++++++++++++----------------- - 1 file changed, 22 insertions(+), 17 deletions(-) - -diff --git a/src/pwck.c b/src/pwck.c -index d0f2f1c9..f1e9c085 100644 ---- a/src/pwck.c -+++ b/src/pwck.c -@@ -73,10 +73,10 @@ static bool sort_mode = false; - static bool quiet = false; /* don't report warnings, only errors */ - - /* local function prototypes */ --static void fail_exit (int code); -+static void fail_exit (int code, bool process_selinux); - NORETURN static void usage (int status); - static void process_flags (int argc, char **argv, struct option_flags *flags); --static void open_files (void); -+static void open_files (struct option_flags *flags); - static void close_files (bool changed, struct option_flags *flags); - static void check_pw_file (bool *errors, bool *changed, - struct option_flags *flags); -@@ -87,10 +87,10 @@ extern int allow_bad_names; - /* - * fail_exit - do some cleanup and exit with the given error code - */ --static void fail_exit (int code) -+static void fail_exit (int code, bool process_selinux) - { - if (spw_locked) { -- if (spw_unlock (true) == 0) { -+ if (spw_unlock (process_selinux) == 0) { - fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, spw_dbname ()); - if (use_system_spw_file) { - SYSLOG ((LOG_ERR, "failed to unlock %s", -@@ -101,7 +101,7 @@ static void fail_exit (int code) - } - - if (pw_locked) { -- if (pw_unlock (true) == 0) { -+ if (pw_unlock (process_selinux) == 0) { - fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, pw_dbname ()); - if (use_system_pw_file) { - SYSLOG ((LOG_ERR, "failed to unlock %s", -@@ -246,12 +246,15 @@ static void process_flags (int argc, char **argv, struct option_flags *flags) - * In read-only mode, the databases are not locked and are opened - * only for reading. - */ --static void open_files (void) -+static void open_files (struct option_flags *flags) - { - bool use_tcb = false; - #ifdef WITH_TCB - use_tcb = getdef_bool ("USE_TCB"); - #endif /* WITH_TCB */ -+ bool process_selinux; -+ -+ process_selinux = !flags->chroot; - - /* - * Lock the files if we aren't in "read-only" mode -@@ -261,7 +264,7 @@ static void open_files (void) - fprintf (stderr, - _("%s: cannot lock %s; try again later.\n"), - Prog, pw_dbname ()); -- fail_exit (E_CANTLOCK); -+ fail_exit (E_CANTLOCK, process_selinux); - } - pw_locked = true; - if (is_shadow && !use_tcb) { -@@ -269,7 +272,7 @@ static void open_files (void) - fprintf (stderr, - _("%s: cannot lock %s; try again later.\n"), - Prog, spw_dbname ()); -- fail_exit (E_CANTLOCK); -+ fail_exit (E_CANTLOCK, process_selinux); - } - spw_locked = true; - } -@@ -285,7 +288,7 @@ static void open_files (void) - if (use_system_pw_file) { - SYSLOG ((LOG_WARN, "cannot open %s", pw_dbname ())); - } -- fail_exit (E_CANTOPEN); -+ fail_exit (E_CANTOPEN, process_selinux); - } - if (is_shadow && !use_tcb) { - if (spw_open (read_only ? O_RDONLY : O_RDWR) == 0) { -@@ -295,7 +298,7 @@ static void open_files (void) - SYSLOG ((LOG_WARN, "cannot open %s", - spw_dbname ())); - } -- fail_exit (E_CANTOPEN); -+ fail_exit (E_CANTOPEN, process_selinux); - } - spw_opened = true; - } -@@ -328,7 +331,7 @@ static void close_files (bool changed, struct option_flags *flags) - "failure while writing changes to %s", - pw_dbname ())); - } -- fail_exit (E_CANTUPDATE); -+ fail_exit (E_CANTUPDATE, process_selinux); - } - if (spw_opened && (spw_close (process_selinux) == 0)) { - fprintf (stderr, -@@ -339,7 +342,7 @@ static void close_files (bool changed, struct option_flags *flags) - "failure while writing changes to %s", - spw_dbname ())); - } -- fail_exit (E_CANTUPDATE); -+ fail_exit (E_CANTUPDATE, process_selinux); - } - spw_opened = false; - } -@@ -644,7 +647,7 @@ static void check_pw_file (bool *errors, bool *changed, struct option_flags *fla - fprintf (stderr, - _("%s: failed to prepare the new %s entry '%s'\n"), - Prog, spw_dbname (), sp.sp_namp); -- fail_exit (E_CANTUPDATE); -+ fail_exit (E_CANTUPDATE, process_selinux); - } - /* remove password from /etc/passwd */ - pw = *pwd; -@@ -653,7 +656,7 @@ static void check_pw_file (bool *errors, bool *changed, struct option_flags *fla - fprintf (stderr, - _("%s: failed to prepare the new %s entry '%s'\n"), - Prog, pw_dbname (), pw.pw_name); -- fail_exit (E_CANTUPDATE); -+ fail_exit (E_CANTUPDATE, process_selinux); - } - } - } else { -@@ -854,6 +857,7 @@ int main (int argc, char **argv) - bool errors = false; - bool changed = false; - struct option_flags flags; -+ bool process_selinux; - - log_set_progname(Prog); - log_set_logfd(stderr); -@@ -868,22 +872,23 @@ int main (int argc, char **argv) - - /* Parse the command line arguments */ - process_flags (argc, argv, &flags); -+ process_selinux = !flags.chroot; - -- open_files (); -+ open_files (&flags); - - if (sort_mode) { - if (pw_sort () != 0) { - fprintf (stderr, - _("%s: cannot sort entries in %s\n"), - Prog, pw_dbname ()); -- fail_exit (E_CANTSORT); -+ fail_exit (E_CANTSORT, process_selinux); - } - if (is_shadow) { - if (spw_sort () != 0) { - fprintf (stderr, - _("%s: cannot sort entries in %s\n"), - Prog, spw_dbname ()); -- fail_exit (E_CANTSORT); -+ fail_exit (E_CANTSORT, process_selinux); - } - } - changed = true; --- -2.50.0 - - -From 0c39283e9298fc130c55516fd0a4a0c6b6003bc9 Mon Sep 17 00:00:00 2001 -From: Iker Pedrosa -Date: Thu, 3 Jul 2025 13:04:26 +0200 -Subject: [PATCH 07/12] 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 ---- - src/pwconv.c | 23 ++++++++++++++++------- - 1 file changed, 16 insertions(+), 7 deletions(-) - -diff --git a/src/pwconv.c b/src/pwconv.c -index b0d668b5..6c81cc6e 100644 ---- a/src/pwconv.c -+++ b/src/pwconv.c -@@ -67,6 +67,11 @@ - #define E_MISSING 4 /* unexpected failure, passwd file missing */ - #define E_PWDBUSY 5 /* passwd file(s) busy */ - #define E_BADENTRY 6 /* bad shadow entry */ -+ -+struct option_flags { -+ bool chroot; -+}; -+ - /* - * Global variables - */ -@@ -78,7 +83,7 @@ static bool pw_locked = false; - /* local function prototypes */ - static void fail_exit (int status); - static void usage (int status); --static void process_flags (int argc, char **argv); -+static void process_flags (int argc, char **argv, struct option_flags *flags); - - static void fail_exit (int status) - { -@@ -120,7 +125,7 @@ static void usage (int status) - * - * It will not return if an error is encountered. - */ --static void process_flags (int argc, char **argv) -+static void process_flags (int argc, char **argv, struct option_flags *flags) - { - /* - * Parse the command line options. -@@ -139,6 +144,7 @@ static void process_flags (int argc, char **argv) - usage (E_SUCCESS); - /*@notreached@*/break; - case 'R': /* no-op, handled in process_root_flag () */ -+ flags->chroot = true; - break; - default: - usage (E_USAGE); -@@ -156,6 +162,8 @@ int main (int argc, char **argv) - struct passwd pwent; - const struct spwd *sp; - struct spwd spent; -+ struct option_flags flags; -+ bool process_selinux; - - log_set_progname(Prog); - log_set_logfd(stderr); -@@ -168,7 +176,8 @@ int main (int argc, char **argv) - - OPENLOG (Prog); - -- process_flags (argc, argv); -+ process_flags (argc, argv, &flags); -+ process_selinux = !flags.chroot; - - #ifdef WITH_TCB - if (getdef_bool("USE_TCB")) { -@@ -274,14 +283,14 @@ int main (int argc, char **argv) - } - } - -- if (spw_close (true) == 0) { -+ if (spw_close (process_selinux) == 0) { - fprintf (stderr, - _("%s: failure while writing changes to %s\n"), - Prog, spw_dbname ()); - SYSLOG ((LOG_ERR, "failure while writing changes to %s", spw_dbname ())); - fail_exit (E_FAILURE); - } -- if (pw_close (true) == 0) { -+ if (pw_close (process_selinux) == 0) { - fprintf (stderr, - _("%s: failure while writing changes to %s\n"), - Prog, pw_dbname ()); -@@ -299,13 +308,13 @@ int main (int argc, char **argv) - /* continue */ - } - -- if (pw_unlock (true) == 0) { -+ if (pw_unlock (process_selinux) == 0) { - fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, pw_dbname ()); - SYSLOG ((LOG_ERR, "failed to unlock %s", pw_dbname ())); - /* continue */ - } - -- if (spw_unlock (true) == 0) { -+ if (spw_unlock (process_selinux) == 0) { - fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, spw_dbname ()); - SYSLOG ((LOG_ERR, "failed to unlock %s", spw_dbname ())); - /* continue */ --- -2.50.0 - - -From 997a523d41bb513f37ca88cab35cba9281f036b6 Mon Sep 17 00:00:00 2001 -From: Iker Pedrosa -Date: Thu, 3 Jul 2025 13:05:52 +0200 -Subject: [PATCH 08/12] 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 ---- - src/pwconv.c | 26 +++++++++++++------------- - 1 file changed, 13 insertions(+), 13 deletions(-) - -diff --git a/src/pwconv.c b/src/pwconv.c -index 6c81cc6e..1dc1fd86 100644 ---- a/src/pwconv.c -+++ b/src/pwconv.c -@@ -81,14 +81,14 @@ static bool spw_locked = false; - static bool pw_locked = false; - - /* local function prototypes */ --static void fail_exit (int status); -+static void fail_exit (int status, bool process_selinux); - static void usage (int status); - static void process_flags (int argc, char **argv, struct option_flags *flags); - --static void fail_exit (int status) -+static void fail_exit (int status, bool process_selinux) - { - if (pw_locked) { -- if (pw_unlock (true) == 0) { -+ if (pw_unlock (process_selinux) == 0) { - fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, pw_dbname ()); - SYSLOG ((LOG_ERR, "failed to unlock %s", pw_dbname ())); - /* continue */ -@@ -96,7 +96,7 @@ static void fail_exit (int status) - } - - if (spw_locked) { -- if (spw_unlock (true) == 0) { -+ if (spw_unlock (process_selinux) == 0) { - fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, spw_dbname ()); - SYSLOG ((LOG_ERR, "failed to unlock %s", spw_dbname ())); - /* continue */ -@@ -190,26 +190,26 @@ int main (int argc, char **argv) - fprintf (stderr, - _("%s: cannot lock %s; try again later.\n"), - Prog, pw_dbname ()); -- fail_exit (E_PWDBUSY); -+ fail_exit (E_PWDBUSY, process_selinux); - } - pw_locked = true; - if (pw_open (O_CREAT | O_RDWR) == 0) { - fprintf (stderr, - _("%s: cannot open %s\n"), Prog, pw_dbname ()); -- fail_exit (E_MISSING); -+ fail_exit (E_MISSING, process_selinux); - } - - if (spw_lock () == 0) { - fprintf (stderr, - _("%s: cannot lock %s; try again later.\n"), - Prog, spw_dbname ()); -- fail_exit (E_PWDBUSY); -+ fail_exit (E_PWDBUSY, process_selinux); - } - spw_locked = true; - if (spw_open (O_CREAT | O_RDWR) == 0) { - fprintf (stderr, - _("%s: cannot open %s\n"), Prog, spw_dbname ()); -- fail_exit (E_FAILURE); -+ fail_exit (E_FAILURE, process_selinux); - } - - /* -@@ -228,7 +228,7 @@ int main (int argc, char **argv) - fprintf (stderr, - _("%s: cannot remove entry '%s' from %s\n"), - Prog, sp->sp_namp, spw_dbname ()); -- fail_exit (E_FAILURE); -+ fail_exit (E_FAILURE, process_selinux); - } - (void) spw_rewind(); - } -@@ -269,7 +269,7 @@ int main (int argc, char **argv) - fprintf (stderr, - _("%s: failed to prepare the new %s entry '%s'\n"), - Prog, spw_dbname (), spent.sp_namp); -- fail_exit (E_FAILURE); -+ fail_exit (E_FAILURE, process_selinux); - } - - /* remove password from /etc/passwd */ -@@ -279,7 +279,7 @@ int main (int argc, char **argv) - fprintf (stderr, - _("%s: failed to prepare the new %s entry '%s'\n"), - Prog, pw_dbname (), pwent.pw_name); -- fail_exit (E_FAILURE); -+ fail_exit (E_FAILURE, process_selinux); - } - } - -@@ -288,14 +288,14 @@ int main (int argc, char **argv) - _("%s: failure while writing changes to %s\n"), - Prog, spw_dbname ()); - SYSLOG ((LOG_ERR, "failure while writing changes to %s", spw_dbname ())); -- fail_exit (E_FAILURE); -+ fail_exit (E_FAILURE, process_selinux); - } - if (pw_close (process_selinux) == 0) { - fprintf (stderr, - _("%s: failure while writing changes to %s\n"), - Prog, pw_dbname ()); - SYSLOG ((LOG_ERR, "failure while writing changes to %s", pw_dbname ())); -- fail_exit (E_FAILURE); -+ fail_exit (E_FAILURE, process_selinux); - } - - /* /etc/passwd- (backup file) */ --- -2.50.0 - - -From 13e902a1b368049cb4c6853a7470f66efbac6e72 Mon Sep 17 00:00:00 2001 -From: Iker Pedrosa -Date: Thu, 3 Jul 2025 13:08:10 +0200 -Subject: [PATCH 09/12] 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 ---- - src/pwunconv.c | 21 ++++++++++++++------- - 1 file changed, 14 insertions(+), 7 deletions(-) - -diff --git a/src/pwunconv.c b/src/pwunconv.c -index 7a6d2be5..d46bde6d 100644 ---- a/src/pwunconv.c -+++ b/src/pwunconv.c -@@ -30,6 +30,9 @@ - #include "sssd.h" - #include "string/strcmp/streq.h" - -+struct option_flags { -+ bool chroot; -+}; - - /* - * Global variables -@@ -42,7 +45,7 @@ static bool pw_locked = false; - /* local function prototypes */ - static void fail_exit (int status); - static void usage (int status); --static void process_flags (int argc, char **argv); -+static void process_flags (int argc, char **argv, struct option_flags *flags); - - static void fail_exit (int status) - { -@@ -82,7 +85,7 @@ static void usage (int status) - * - * It will not return if an error is encountered. - */ --static void process_flags (int argc, char **argv) -+static void process_flags (int argc, char **argv, struct option_flags *flags) - { - /* - * Parse the command line options. -@@ -101,6 +104,7 @@ static void process_flags (int argc, char **argv) - usage (E_SUCCESS); - /*@notreached@*/break; - case 'R': /* no-op, handled in process_root_flag () */ -+ flags->chroot = true; - break; - default: - usage (E_USAGE); -@@ -117,6 +121,8 @@ int main (int argc, char **argv) - const struct passwd *pw; - struct passwd pwent; - const struct spwd *spwd; -+ struct option_flags flags; -+ bool process_selinux; - - log_set_progname(Prog); - log_set_logfd(stderr); -@@ -129,7 +135,8 @@ int main (int argc, char **argv) - - OPENLOG (Prog); - -- process_flags (argc, argv); -+ process_flags (argc, argv, &flags); -+ process_selinux = !flags.chroot; - - #ifdef WITH_TCB - if (getdef_bool("USE_TCB")) { -@@ -205,9 +212,9 @@ int main (int argc, char **argv) - } - } - -- (void) spw_close (true); /* was only open O_RDONLY */ -+ (void) spw_close (process_selinux); /* was only open O_RDONLY */ - -- if (pw_close (true) == 0) { -+ if (pw_close (process_selinux) == 0) { - fprintf (stderr, - _("%s: failure while writing changes to %s\n"), - Prog, pw_dbname ()); -@@ -222,12 +229,12 @@ int main (int argc, char **argv) - fail_exit (3); - } - -- if (spw_unlock (true) == 0) { -+ if (spw_unlock (process_selinux) == 0) { - fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, spw_dbname ()); - SYSLOG ((LOG_ERR, "failed to unlock %s", spw_dbname ())); - /* continue */ - } -- if (pw_unlock (true) == 0) { -+ if (pw_unlock (process_selinux) == 0) { - fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, pw_dbname ()); - SYSLOG ((LOG_ERR, "failed to unlock %s", pw_dbname ())); - /* continue */ --- -2.50.0 - - -From 849e748fe08129684da13acba13a247f0aa98eff Mon Sep 17 00:00:00 2001 -From: Iker Pedrosa -Date: Thu, 3 Jul 2025 13:09:12 +0200 -Subject: [PATCH 10/12] 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 ---- - src/pwunconv.c | 22 +++++++++++----------- - 1 file changed, 11 insertions(+), 11 deletions(-) - -diff --git a/src/pwunconv.c b/src/pwunconv.c -index d46bde6d..d3e10f68 100644 ---- a/src/pwunconv.c -+++ b/src/pwunconv.c -@@ -43,21 +43,21 @@ static bool spw_locked = false; - static bool pw_locked = false; - - /* local function prototypes */ --static void fail_exit (int status); -+static void fail_exit (int status, bool process_selinux); - static void usage (int status); - static void process_flags (int argc, char **argv, struct option_flags *flags); - --static void fail_exit (int status) -+static void fail_exit (int status, bool process_selinux) - { - if (spw_locked) { -- if (spw_unlock (true) == 0) { -+ if (spw_unlock (process_selinux) == 0) { - fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, spw_dbname ()); - SYSLOG ((LOG_ERR, "failed to unlock %s", spw_dbname ())); - /* continue */ - } - } - if (pw_locked) { -- if (pw_unlock (true) == 0) { -+ if (pw_unlock (process_selinux) == 0) { - fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, pw_dbname ()); - SYSLOG ((LOG_ERR, "failed to unlock %s", pw_dbname ())); - /* continue */ -@@ -154,28 +154,28 @@ int main (int argc, char **argv) - fprintf (stderr, - _("%s: cannot lock %s; try again later.\n"), - Prog, pw_dbname ()); -- fail_exit (5); -+ fail_exit (5, process_selinux); - } - pw_locked = true; - if (pw_open (O_CREAT | O_RDWR) == 0) { - fprintf (stderr, - _("%s: cannot open %s\n"), - Prog, pw_dbname ()); -- fail_exit (1); -+ fail_exit (1, process_selinux); - } - - if (spw_lock () == 0) { - fprintf (stderr, - _("%s: cannot lock %s; try again later.\n"), - Prog, spw_dbname ()); -- fail_exit (5); -+ fail_exit (5, process_selinux); - } - spw_locked = true; - if (spw_open (O_RDONLY) == 0) { - fprintf (stderr, - _("%s: cannot open %s\n"), - Prog, spw_dbname ()); -- fail_exit (1); -+ fail_exit (1, process_selinux); - } - - (void) pw_rewind (); -@@ -208,7 +208,7 @@ int main (int argc, char **argv) - fprintf (stderr, - _("%s: failed to prepare the new %s entry '%s'\n"), - Prog, pw_dbname (), pwent.pw_name); -- fail_exit (3); -+ fail_exit (3, process_selinux); - } - } - -@@ -219,14 +219,14 @@ int main (int argc, char **argv) - _("%s: failure while writing changes to %s\n"), - Prog, pw_dbname ()); - SYSLOG ((LOG_ERR, "failure while writing changes to %s", pw_dbname ())); -- fail_exit (3); -+ fail_exit (3, process_selinux); - } - - if (unlink (SHADOW) != 0) { - fprintf (stderr, - _("%s: cannot delete %s\n"), Prog, SHADOW); - SYSLOG ((LOG_ERR, "cannot delete %s", SHADOW)); -- fail_exit (3); -+ fail_exit (3, process_selinux); - } - - if (spw_unlock (process_selinux) == 0) { --- -2.50.0 - - -From b1bcf1cf8245b195cff7586770ad861f89cb7c92 Mon Sep 17 00:00:00 2001 -From: Iker Pedrosa -Date: Thu, 3 Jul 2025 14:29:55 +0200 -Subject: [PATCH 11/12] 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 ---- - src/gpasswd.c | 44 ++++++++++++++++++++++++++++---------------- - 1 file changed, 28 insertions(+), 16 deletions(-) - -diff --git a/src/gpasswd.c b/src/gpasswd.c -index a49b9140..94b2bb32 100644 ---- a/src/gpasswd.c -+++ b/src/gpasswd.c -@@ -40,6 +40,9 @@ - #include "string/strcpy/strtcpy.h" - #include "string/strdup/xstrdup.h" - -+struct option_flags { -+ bool chroot; -+}; - - /* - * Global variables -@@ -87,17 +90,17 @@ NORETURN static void failure(void); - static void usage (int status); - static void catch_signals (int killed); - static bool is_valid_user_list (const char *users); --static void process_flags (int argc, char **argv); -+static void process_flags (int argc, char **argv, struct option_flags *flags); - static void check_flags (int argc, int opt_index); - static void open_files (void); --static void close_files (void); -+static void close_files (struct option_flags *flags); - #ifdef SHADOWGRP --static void get_group (struct group *gr, struct sgrp *sg); -+static void get_group (struct group *gr, struct sgrp *sg, struct option_flags *flags); - static void check_perms (const struct group *gr, const struct sgrp *sg); - static void update_group (struct group *gr, struct sgrp *sg); - static void change_passwd (struct group *gr, struct sgrp *sg); - #else --static void get_group (struct group *gr); -+static void get_group (struct group *gr, struct option_flags *flags); - static void check_perms (const struct group *gr); - static void update_group (struct group *gr); - static void change_passwd (struct group *gr); -@@ -211,7 +214,7 @@ static void failure(void) - /* - * process_flags - process the command line options and arguments - */ --static void process_flags (int argc, char **argv) -+static void process_flags (int argc, char **argv, struct option_flags *flags) - { - int c; - static struct option long_options[] = { -@@ -272,6 +275,7 @@ static void process_flags (int argc, char **argv) - Mflg = true; - break; - case 'Q': /* no-op, handled in process_root_flag () */ -+ flags->chroot = true; - break; - case 'r': /* remove group password */ - rflg = true; -@@ -591,9 +595,13 @@ static void log_gpasswd_success_group (MAYBE_UNUSED void *arg) - * - * It will call exit in case of error. - */ --static void close_files (void) -+static void close_files (struct option_flags *flags) - { -- if (gr_close (true) == 0) { -+ bool process_selinux; -+ -+ process_selinux = !flags->chroot; -+ -+ if (gr_close (process_selinux) == 0) { - fprintf (stderr, - _("%s: failure while writing changes to %s\n"), - Prog, gr_dbname ()); -@@ -607,7 +615,7 @@ static void close_files (void) - - #ifdef SHADOWGRP - if (is_shadowgrp) { -- if (sgr_close (true) == 0) { -+ if (sgr_close (process_selinux) == 0) { - fprintf (stderr, - _("%s: failure while writing changes to %s\n"), - Prog, sgr_dbname ()); -@@ -695,15 +703,18 @@ static void update_group (struct group *gr) - * Note: If !is_shadowgrp, *sg will not be initialized. - */ - #ifdef SHADOWGRP --static void get_group (struct group *gr, struct sgrp *sg) -+static void get_group (struct group *gr, struct sgrp *sg, struct option_flags *flags) - #else --static void get_group (struct group *gr) -+static void get_group (struct group *gr, struct option_flags *flags) - #endif - { - struct group const*tmpgr = NULL; - #ifdef SHADOWGRP - struct sgrp const*tmpsg = NULL; - #endif -+ bool process_selinux; -+ -+ process_selinux = !flags->chroot; - - if (gr_open (O_RDONLY) == 0) { - fprintf (stderr, _("%s: cannot open %s\n"), Prog, gr_dbname ()); -@@ -724,7 +735,7 @@ static void get_group (struct group *gr) - gr->gr_passwd = xstrdup (tmpgr->gr_passwd); - gr->gr_mem = dup_list (tmpgr->gr_mem); - -- if (gr_close (true) == 0) { -+ if (gr_close (process_selinux) == 0) { - fprintf (stderr, - _("%s: failure while closing read-only %s\n"), - Prog, gr_dbname ()); -@@ -762,7 +773,7 @@ static void get_group (struct group *gr) - sg->sg_adm[0] = NULL; - - } -- if (sgr_close (true) == 0) { -+ if (sgr_close (process_selinux) == 0) { - fprintf (stderr, - _("%s: failure while closing read-only %s\n"), - Prog, sgr_dbname ()); -@@ -864,6 +875,7 @@ int main (int argc, char **argv) - struct sgrp sgent; - #endif - struct passwd *pw = NULL; -+ struct option_flags flags; - - #ifdef WITH_AUDIT - audit_help_open (); -@@ -924,15 +936,15 @@ int main (int argc, char **argv) - } - - /* Parse the options */ -- process_flags (argc, argv); -+ process_flags (argc, argv, &flags); - - /* - * Replicate the group so it can be modified later on. - */ - #ifdef SHADOWGRP -- get_group (&grent, &sgent); -+ get_group (&grent, &sgent, &flags); - #else -- get_group (&grent); -+ get_group (&grent, &flags); - #endif - - /* -@@ -1095,7 +1107,7 @@ int main (int argc, char **argv) - update_group (&grent); - #endif - -- close_files (); -+ close_files (&flags); - - nscd_flush_cache ("group"); - sssd_flush_cache (SSSD_DB_GROUP); --- -2.50.0 - - -From 2c250fae8866a682bac686dcaa198b325d198cf9 Mon Sep 17 00:00:00 2001 -From: Iker Pedrosa -Date: Fri, 4 Jul 2025 10:03:42 +0200 -Subject: [PATCH 12/12] man/: update `--root` flag with no SELinux support - -Signed-off-by: Iker Pedrosa ---- - man/chage.1.xml | 1 + - man/chfn.1.xml | 1 + - man/chgpasswd.8.xml | 1 + - man/chpasswd.8.xml | 1 + - man/chsh.1.xml | 1 + - man/faillog.8.xml | 1 + - man/gpasswd.1.xml | 1 + - man/groupadd.8.xml | 1 + - man/groupdel.8.xml | 1 + - man/groupmems.8.xml | 1 + - man/groupmod.8.xml | 1 + - man/grpck.8.xml | 1 + - man/lastlog.8.xml | 1 + - man/newusers.8.xml | 1 + - man/passwd.1.xml | 1 + - man/pwck.8.xml | 1 + - man/pwconv.8.xml | 1 + - man/useradd.8.xml | 1 + - man/userdel.8.xml | 1 + - man/usermod.8.xml | 1 + - man/vipw.8.xml | 1 + - 21 files changed, 21 insertions(+) - -diff --git a/man/chage.1.xml b/man/chage.1.xml -index 4ccdf483..a3fff291 100644 ---- a/man/chage.1.xml -+++ b/man/chage.1.xml -@@ -205,6 +205,7 @@ - directory and use the configuration files from the - CHROOT_DIR directory. - Only absolute paths are supported. -+ No SELINUX support. - - - -diff --git a/man/chfn.1.xml b/man/chfn.1.xml -index 2fa49feb..d199ef03 100644 ---- a/man/chfn.1.xml -+++ b/man/chfn.1.xml -@@ -138,6 +138,7 @@ - Apply changes in the CHROOT_DIR - directory and use the configuration files from the - CHROOT_DIR directory. -+ No SELINUX support. - - - -diff --git a/man/chgpasswd.8.xml b/man/chgpasswd.8.xml -index f4a83c24..184c1696 100644 ---- a/man/chgpasswd.8.xml -+++ b/man/chgpasswd.8.xml -@@ -134,6 +134,7 @@ - directory and use the configuration files from the - CHROOT_DIR directory. - Only absolute paths are supported. -+ No SELINUX support. - - - -diff --git a/man/chpasswd.8.xml b/man/chpasswd.8.xml -index cffd9df1..440c8342 100644 ---- a/man/chpasswd.8.xml -+++ b/man/chpasswd.8.xml -@@ -176,6 +176,7 @@ - directory and use the configuration files from the - CHROOT_DIR directory. - Only absolute paths are supported. -+ No SELINUX support. - - - -diff --git a/man/chsh.1.xml b/man/chsh.1.xml -index db2d0ceb..8f253731 100644 ---- a/man/chsh.1.xml -+++ b/man/chsh.1.xml -@@ -89,6 +89,7 @@ - directory and use the configuration files from the - CHROOT_DIR directory. - Only absolute paths are supported. -+ No SELINUX support. - - - -diff --git a/man/faillog.8.xml b/man/faillog.8.xml -index effe43a9..7d453fd2 100644 ---- a/man/faillog.8.xml -+++ b/man/faillog.8.xml -@@ -162,6 +162,7 @@ - directory and use the configuration files from the - CHROOT_DIR directory. - Only absolute paths are supported. -+ No SELINUX support. - - - -diff --git a/man/gpasswd.1.xml b/man/gpasswd.1.xml -index 9b89d915..f918442f 100644 ---- a/man/gpasswd.1.xml -+++ b/man/gpasswd.1.xml -@@ -165,6 +165,7 @@ - directory and use the configuration files from the - CHROOT_DIR directory. - Only absolute paths are supported. -+ No SELINUX support. - - - -diff --git a/man/groupadd.8.xml b/man/groupadd.8.xml -index 9abf159a..2dea7ae5 100644 ---- a/man/groupadd.8.xml -+++ b/man/groupadd.8.xml -@@ -206,6 +206,7 @@ - directory and use the configuration files from the - CHROOT_DIR directory. - Only absolute paths are supported. -+ No SELINUX support. - - - -diff --git a/man/groupdel.8.xml b/man/groupdel.8.xml -index 8d41b883..31f29994 100644 ---- a/man/groupdel.8.xml -+++ b/man/groupdel.8.xml -@@ -94,6 +94,7 @@ - directory and use the configuration files from the - CHROOT_DIR directory. - Only absolute paths are supported. -+ No SELINUX support. - - - -diff --git a/man/groupmems.8.xml b/man/groupmems.8.xml -index cbc84622..0def6c99 100644 ---- a/man/groupmems.8.xml -+++ b/man/groupmems.8.xml -@@ -147,6 +147,7 @@ - directory and use the configuration files from the - CHROOT_DIR directory. - Only absolute paths are supported. -+ No SELINUX support. - - - -diff --git a/man/groupmod.8.xml b/man/groupmod.8.xml -index 2d51f271..ca287441 100644 ---- a/man/groupmod.8.xml -+++ b/man/groupmod.8.xml -@@ -169,6 +169,7 @@ - directory and use the configuration files from the - CHROOT_DIR directory. - Only absolute paths are supported. -+ No SELINUX support. - - - -diff --git a/man/grpck.8.xml b/man/grpck.8.xml -index 31aa6ca3..9feb190e 100644 ---- a/man/grpck.8.xml -+++ b/man/grpck.8.xml -@@ -154,6 +154,7 @@ - directory and use the configuration files from the - CHROOT_DIR directory. - Only absolute paths are supported. -+ No SELINUX support. - - - -diff --git a/man/lastlog.8.xml b/man/lastlog.8.xml -index cefe5ac2..b0589203 100644 ---- a/man/lastlog.8.xml -+++ b/man/lastlog.8.xml -@@ -109,6 +109,7 @@ - directory and use the configuration files from the - CHROOT_DIR directory. - Only absolute paths are supported. -+ No SELINUX support. - - - -diff --git a/man/newusers.8.xml b/man/newusers.8.xml -index 6812c750..e0380608 100644 ---- a/man/newusers.8.xml -+++ b/man/newusers.8.xml -@@ -313,6 +313,7 @@ - directory and use the configuration files from the - CHROOT_DIR directory. - Only absolute paths are supported. -+ No SELINUX support. - - - -diff --git a/man/passwd.1.xml b/man/passwd.1.xml -index 65dca040..def35609 100644 ---- a/man/passwd.1.xml -+++ b/man/passwd.1.xml -@@ -279,6 +279,7 @@ - directory and use the configuration files from the - CHROOT_DIR directory. - Only absolute paths are supported. -+ No SELINUX support. - - - -diff --git a/man/pwck.8.xml b/man/pwck.8.xml -index 4eb820d6..998bb44a 100644 ---- a/man/pwck.8.xml -+++ b/man/pwck.8.xml -@@ -202,6 +202,7 @@ - directory and use the configuration files from the - CHROOT_DIR directory. - Only absolute paths are supported. -+ No SELINUX support. - - - -diff --git a/man/pwconv.8.xml b/man/pwconv.8.xml -index c4aedef6..9be3f7c5 100644 ---- a/man/pwconv.8.xml -+++ b/man/pwconv.8.xml -@@ -183,6 +183,7 @@ - directory and use the configuration files from the - CHROOT_DIR directory. - Only absolute paths are supported. -+ No SELINUX support. - - - -diff --git a/man/useradd.8.xml b/man/useradd.8.xml -index fd9ff697..e6db7f6d 100644 ---- a/man/useradd.8.xml -+++ b/man/useradd.8.xml -@@ -515,6 +515,7 @@ - directory and use the configuration files from the - CHROOT_DIR directory. - Only absolute paths are supported. -+ No SELINUX support. - - - -diff --git a/man/userdel.8.xml b/man/userdel.8.xml -index 32851f11..de8eff24 100644 ---- a/man/userdel.8.xml -+++ b/man/userdel.8.xml -@@ -120,6 +120,7 @@ - directory and use the configuration files from the - CHROOT_DIR directory. - Only absolute paths are supported. -+ No SELINUX support. - - - -diff --git a/man/usermod.8.xml b/man/usermod.8.xml -index 61b5e3a9..2c704ab5 100644 ---- a/man/usermod.8.xml -+++ b/man/usermod.8.xml -@@ -337,6 +337,7 @@ - directory and use the configuration files from the - CHROOT_DIR directory. - Only absolute paths are supported. -+ No SELINUX support. - - - -diff --git a/man/vipw.8.xml b/man/vipw.8.xml -index fb80582b..975fba37 100644 ---- a/man/vipw.8.xml -+++ b/man/vipw.8.xml -@@ -120,6 +120,7 @@ - directory and use the configuration files from the - CHROOT_DIR directory. - Only absolute paths are supported. -+ No SELINUX support. - - - --- -2.50.0 - diff --git a/shadow-utils.spec b/shadow-utils.spec index 483eb21..c6773d9 100644 --- a/shadow-utils.spec +++ b/shadow-utils.spec @@ -24,8 +24,6 @@ Source7: passwd.pamd Patch0: shadow-4.15.0-manfix.patch # Probably non-upstreamable Patch1: shadow-4.18.0-account-tools-setuid.patch -# https://github.com/shadow-maint/shadow/commit/db0e0b9112332a45131912021317ab5ab4fd6e40 -Patch2: shadow-4.18.0-selinux-chroot-prefix.patch ### Dependencies ### Requires: audit-libs >= 1.6.5 @@ -271,6 +269,9 @@ rm -f $RPM_BUILD_ROOT/%{_libdir}/libsubid.a %{_libdir}/libsubid.so %changelog +* Tue Nov 25 2025 Adam Williamson - 2:4.18.0-7 +- Also revert changes from -4 (last known good was -3) + * Tue Nov 25 2025 Adam Williamson - 2:4.18.0-6 - Revert changes from -5 (they were only meant for testing) From d39147a22233042f440eae625add65ddefeaa4bc Mon Sep 17 00:00:00 2001 From: Adam Williamson Date: Tue, 25 Nov 2025 13:25:22 -0800 Subject: [PATCH 14/30] Really bump release, d'oh --- shadow-utils.spec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/shadow-utils.spec b/shadow-utils.spec index c6773d9..1769e3c 100644 --- a/shadow-utils.spec +++ b/shadow-utils.spec @@ -1,7 +1,7 @@ Summary: Utilities for managing accounts and shadow password files Name: shadow-utils Version: 4.18.0 -Release: 6%{?dist} +Release: 7%{?dist} Epoch: 2 License: BSD-3-Clause AND GPL-2.0-or-later URL: https://github.com/shadow-maint/shadow From 43599b7b5e1d8f42c88fd974404fbb9bbb69a21c Mon Sep 17 00:00:00 2001 From: Iker Pedrosa Date: Fri, 9 Jan 2026 14:55:44 +0100 Subject: [PATCH 15/30] Rebase to version 4.19.0 Resolves: #2426288 and #2249524 Signed-off-by: Iker Pedrosa --- .gitignore | 2 + ...> shadow-4.19.0-account-tools-setuid.patch | 113 +++++++++--------- shadow-utils.spec | 16 ++- sources | 4 +- 4 files changed, 69 insertions(+), 66 deletions(-) rename shadow-4.18.0-account-tools-setuid.patch => shadow-4.19.0-account-tools-setuid.patch (73%) diff --git a/.gitignore b/.gitignore index f4e82f2..0ceb6c8 100644 --- a/.gitignore +++ b/.gitignore @@ -42,3 +42,5 @@ shadow-4.1.4.2.tar.bz2 /shadow-4.17.4.tar.xz.asc /shadow-4.18.0.tar.xz /shadow-4.18.0.tar.xz.asc +/shadow-4.19.0.tar.xz +/shadow-4.19.0.tar.xz.asc diff --git a/shadow-4.18.0-account-tools-setuid.patch b/shadow-4.19.0-account-tools-setuid.patch similarity index 73% rename from shadow-4.18.0-account-tools-setuid.patch rename to shadow-4.19.0-account-tools-setuid.patch index 9c018f7..9452582 100644 --- a/shadow-4.18.0-account-tools-setuid.patch +++ b/shadow-4.19.0-account-tools-setuid.patch @@ -1,7 +1,7 @@ -diff -up shadow-4.18.0-rc1/src/chpasswd.c.account-tools-setuid shadow-4.18.0-rc1/src/chpasswd.c ---- shadow-4.18.0-rc1/src/chpasswd.c.account-tools-setuid 2025-06-08 15:58:36.650175673 +0200 -+++ shadow-4.18.0-rc1/src/chpasswd.c 2025-06-09 10:09:17.902939220 +0200 -@@ -448,9 +448,11 @@ int main (int argc, char **argv) +diff -up shadow-4.19.0-rc1/src/chpasswd.c.account-tools-setuid shadow-4.19.0-rc1/src/chpasswd.c +--- shadow-4.19.0-rc1/src/chpasswd.c.account-tools-setuid 2025-12-07 16:56:38.563285789 +0100 ++++ shadow-4.19.0-rc1/src/chpasswd.c 2025-12-09 14:54:29.232282499 +0100 +@@ -466,9 +466,11 @@ int main (int argc, char **argv) char *newpwd; const char *salt; @@ -13,7 +13,7 @@ diff -up shadow-4.18.0-rc1/src/chpasswd.c.account-tools-setuid shadow-4.18.0-rc1 bool errors = false; intmax_t line = 0; -@@ -474,19 +476,23 @@ int main (int argc, char **argv) +@@ -495,19 +497,23 @@ int main (int argc, char **argv) process_root_flag ("-R", argc, argv); prefix = process_prefix_flag ("-P", argc, argv); @@ -37,7 +37,7 @@ diff -up shadow-4.18.0-rc1/src/chpasswd.c.account-tools-setuid shadow-4.18.0-rc1 { is_shadow_pwd = spw_file_present (); -@@ -543,6 +549,7 @@ int main (int argc, char **argv) +@@ -562,6 +568,7 @@ int main (int argc, char **argv) } newpwd = cp; @@ -45,15 +45,15 @@ diff -up shadow-4.18.0-rc1/src/chpasswd.c.account-tools-setuid shadow-4.18.0-rc1 #ifdef USE_PAM if (use_pam) { if (do_pam_passwd_non_interactive (Prog, name, newpwd) != 0) { -@@ -553,6 +560,7 @@ int main (int argc, char **argv) +@@ -572,6 +579,7 @@ int main (int argc, char **argv) } } else #endif /* USE_PAM */ +#endif /* ACCT_TOOLS_SETUID */ { - const struct spwd *sp; - struct spwd newsp; -@@ -672,9 +680,11 @@ int main (int argc, char **argv) + + /* +@@ -706,9 +714,11 @@ int main (int argc, char **argv) * password database. */ if (errors) { @@ -65,8 +65,8 @@ diff -up shadow-4.18.0-rc1/src/chpasswd.c.account-tools-setuid shadow-4.18.0-rc1 { fprintf (stderr, _("%s: error detected, changes ignored\n"), -@@ -683,9 +693,11 @@ int main (int argc, char **argv) - fail_exit (1); +@@ -717,9 +727,11 @@ int main (int argc, char **argv) + fail_exit (1, process_selinux); } +#ifdef ACCT_TOOLS_SETUID @@ -76,10 +76,10 @@ diff -up shadow-4.18.0-rc1/src/chpasswd.c.account-tools-setuid shadow-4.18.0-rc1 +#endif /* ACCT_TOOLS_SETUID */ { /* Save the changes */ - close_files (); -diff -up shadow-4.18.0-rc1/src/groupmems.c.account-tools-setuid shadow-4.18.0-rc1/src/groupmems.c ---- shadow-4.18.0-rc1/src/groupmems.c.account-tools-setuid 2025-06-08 15:58:36.651250652 +0200 -+++ shadow-4.18.0-rc1/src/groupmems.c 2025-06-09 10:08:59.836763261 +0200 + close_files (&flags); +diff -up shadow-4.19.0-rc1/src/groupmems.c.account-tools-setuid shadow-4.19.0-rc1/src/groupmems.c +--- shadow-4.19.0-rc1/src/groupmems.c.account-tools-setuid 2025-12-07 16:56:38.564285805 +0100 ++++ shadow-4.19.0-rc1/src/groupmems.c 2025-12-09 14:57:23.767054141 +0100 @@ -14,9 +14,11 @@ #include #include @@ -91,17 +91,17 @@ diff -up shadow-4.18.0-rc1/src/groupmems.c.account-tools-setuid shadow-4.18.0-rc +#endif /* ACCT_TOOLS_SETUID */ #include - #include "alloc/x/xmalloc.h" -@@ -417,6 +419,7 @@ static void process_flags (int argc, cha - static void check_perms (void) + #include "alloc/malloc.h" +@@ -432,6 +434,7 @@ static void + check_perms(MAYBE_UNUSED bool process_selinux) { if (!list) { +#ifdef ACCT_TOOLS_SETUID #ifdef USE_PAM pam_handle_t *pamh = NULL; int retval; -@@ -450,7 +453,8 @@ static void check_perms (void) - fail_exit (1); +@@ -465,7 +468,8 @@ check_perms(MAYBE_UNUSED bool process_se + fail_exit (1, process_selinux); } (void) pam_end (pamh, retval); -#endif @@ -110,10 +110,10 @@ diff -up shadow-4.18.0-rc1/src/groupmems.c.account-tools-setuid shadow-4.18.0-rc } } -diff -up shadow-4.18.0-rc1/src/newusers.c.account-tools-setuid shadow-4.18.0-rc1/src/newusers.c ---- shadow-4.18.0-rc1/src/newusers.c.account-tools-setuid 2025-06-08 15:58:36.652250643 +0200 -+++ shadow-4.18.0-rc1/src/newusers.c 2025-06-09 10:08:59.836928828 +0200 -@@ -67,6 +67,7 @@ +diff -up shadow-4.19.0-rc1/src/newusers.c.account-tools-setuid shadow-4.19.0-rc1/src/newusers.c +--- shadow-4.19.0-rc1/src/newusers.c.account-tools-setuid 2025-12-07 16:56:38.565285821 +0100 ++++ shadow-4.19.0-rc1/src/newusers.c 2025-12-09 15:07:40.115500988 +0100 +@@ -74,6 +74,7 @@ struct option_flags { static const char Prog[] = "newusers"; static bool rflg = false; /* create a system account */ @@ -121,7 +121,7 @@ diff -up shadow-4.18.0-rc1/src/newusers.c.account-tools-setuid shadow-4.18.0-rc1 #ifndef USE_PAM static /*@null@*//*@observer@*/char *crypt_method = NULL; #define cflg (NULL != crypt_method) -@@ -83,6 +84,7 @@ static long bcrypt_rounds = 13; +@@ -90,6 +91,7 @@ static long bcrypt_rounds = 13; static long yescrypt_cost = 5; #endif /* USE_YESCRYPT */ #endif /* !USE_PAM */ @@ -129,7 +129,7 @@ diff -up shadow-4.18.0-rc1/src/newusers.c.account-tools-setuid shadow-4.18.0-rc1 static bool is_shadow; #ifdef SHADOWGRP -@@ -105,9 +107,11 @@ NORETURN static void fail_exit (int); +@@ -112,9 +114,11 @@ NORETURN static void fail_exit (int, boo static int add_group (const char *, const char *, gid_t *, gid_t); static int get_user_id (const char *, uid_t *); static int add_user (const char *, uid_t, gid_t); @@ -139,9 +139,9 @@ diff -up shadow-4.18.0-rc1/src/newusers.c.account-tools-setuid shadow-4.18.0-rc1 #endif /* !USE_PAM */ +#endif /* !ACCT_TOOLS_SETUID */ static int add_passwd (struct passwd *, const char *); - static void process_flags (int argc, char **argv); + static void process_flags (int argc, char **argv, struct option_flags *flags); static void check_flags (void); -@@ -129,6 +133,7 @@ static void usage (int status) +@@ -136,6 +140,7 @@ static void usage (int status) "Options:\n"), Prog); (void) fputs (_(" -b, --badname allow bad names\n"), usageout); @@ -149,7 +149,7 @@ diff -up shadow-4.18.0-rc1/src/newusers.c.account-tools-setuid shadow-4.18.0-rc1 #ifndef USE_PAM (void) fprintf (usageout, _(" -c, --crypt-method METHOD the crypt method (one of %s)\n"), -@@ -144,9 +149,11 @@ static void usage (int status) +@@ -151,9 +156,11 @@ static void usage (int status) #endif ); #endif /* !USE_PAM */ @@ -161,7 +161,7 @@ diff -up shadow-4.18.0-rc1/src/newusers.c.account-tools-setuid shadow-4.18.0-rc1 #ifndef USE_PAM #if defined(USE_SHA_CRYPT) || defined(USE_BCRYPT) || defined(USE_YESCRYPT) (void) fputs (_(" -s, --sha-rounds number of rounds for the SHA, BCRYPT\n" -@@ -154,6 +161,7 @@ static void usage (int status) +@@ -161,6 +168,7 @@ static void usage (int status) usageout); #endif /* USE_SHA_CRYPT || USE_BCRYPT || USE_YESCRYPT */ #endif /* !USE_PAM */ @@ -169,7 +169,7 @@ diff -up shadow-4.18.0-rc1/src/newusers.c.account-tools-setuid shadow-4.18.0-rc1 (void) fputs ("\n", usageout); exit (status); -@@ -419,6 +427,7 @@ static int add_user (const char *name, u +@@ -426,6 +434,7 @@ static int add_user (const char *name, u return (pw_update (&pwent) == 0) ? -1 : 0; } @@ -177,7 +177,7 @@ diff -up shadow-4.18.0-rc1/src/newusers.c.account-tools-setuid shadow-4.18.0-rc1 #ifndef USE_PAM /* * update_passwd - update the password in the passwd entry -@@ -471,6 +480,7 @@ static int update_passwd (struct passwd +@@ -478,6 +487,7 @@ static int update_passwd (struct passwd return 0; } #endif /* !USE_PAM */ @@ -185,7 +185,7 @@ diff -up shadow-4.18.0-rc1/src/newusers.c.account-tools-setuid shadow-4.18.0-rc1 /* * add_passwd - add or update the encrypted password -@@ -479,10 +489,13 @@ static int add_passwd (struct passwd *pw +@@ -487,10 +497,13 @@ add_passwd(struct passwd *pwd, MAYBE_UNU { const struct spwd *sp; struct spwd spent; @@ -199,7 +199,7 @@ diff -up shadow-4.18.0-rc1/src/newusers.c.account-tools-setuid shadow-4.18.0-rc1 #ifndef USE_PAM void *crypt_arg = NULL; if (NULL != crypt_method) { -@@ -520,13 +533,14 @@ static int add_passwd (struct passwd *pw +@@ -528,13 +541,14 @@ add_passwd(struct passwd *pwd, MAYBE_UNU return update_passwd (pwd, password); } #endif /* USE_PAM */ @@ -215,7 +215,7 @@ diff -up shadow-4.18.0-rc1/src/newusers.c.account-tools-setuid shadow-4.18.0-rc1 if (NULL != sp) { spent = *sp; if ( (NULL != crypt_method) -@@ -563,7 +577,7 @@ static int add_passwd (struct passwd *pw +@@ -571,7 +585,7 @@ add_passwd(struct passwd *pwd, MAYBE_UNU if (!streq(pwd->pw_passwd, "x")) { return update_passwd (pwd, password); } @@ -224,7 +224,7 @@ diff -up shadow-4.18.0-rc1/src/newusers.c.account-tools-setuid shadow-4.18.0-rc1 /* * If there is already a shadow entry, do not touch it. * If there is already a passwd entry with a password, do not -@@ -574,14 +588,14 @@ static int add_passwd (struct passwd *pw +@@ -582,14 +596,14 @@ add_passwd(struct passwd *pwd, MAYBE_UNU || !streq(pwd->pw_passwd, "x")) { return 0; } @@ -241,14 +241,14 @@ diff -up shadow-4.18.0-rc1/src/newusers.c.account-tools-setuid shadow-4.18.0-rc1 if ((crypt_method != NULL) && streq(crypt_method, "NONE")) { spent.sp_pwdp = (char *)password; } else { -@@ -626,35 +640,41 @@ static int add_passwd (struct passwd *pw - static void process_flags (int argc, char **argv) +@@ -647,32 +647,38 @@ add_passwd(struct passwd *pwd, MAYBE_UNU + static void process_flags (int argc, char **argv, struct option_flags *flags) { int c; +#ifndef ACCT_TOOLS_SETUID #ifndef USE_PAM #if defined(USE_SHA_CRYPT) || defined(USE_BCRYPT) || defined(USE_YESCRYPT) - int bad_s; + int bad_s; #endif /* USE_SHA_CRYPT || USE_BCRYPT || USE_YESCRYPT */ #endif /* !USE_PAM */ +#endif /* !ACCT_TOOLS_SETUID */ @@ -277,15 +277,12 @@ diff -up shadow-4.18.0-rc1/src/newusers.c.account-tools-setuid shadow-4.18.0-rc1 +#if !defined(ACCT_TOOLS_SETUID) && !defined(USE_PAM) #if defined(USE_SHA_CRYPT) || defined(USE_BCRYPT) || defined(USE_YESCRYPT) "c:bhrs:", - #else /* !USE_SHA_CRYPT && !USE_BCRYPT && !USE_YESCRYPT */ +-#else /* !USE_SHA_CRYPT && !USE_BCRYPT && !USE_YESCRYPT */ ++#else /* !ACCT_TOOLS_SETUID && !USE_PAM */ "c:bhr", #endif /* USE_SHA_CRYPT || USE_BCRYPT || USE_YESCRYPT */ --#else /* USE_PAM */ -+#else /* !ACCT_TOOLS_SETUID && !USE_PAM */ - "bhr", - #endif - long_options, NULL)) != -1) { -@@ -662,11 +682,13 @@ static void process_flags (int argc, cha + #else /* USE_PAM */ +@@ -670,11 +690,13 @@ static void process_flags (int argc, cha case 'b': allow_bad_names = true; break; @@ -299,15 +296,15 @@ diff -up shadow-4.18.0-rc1/src/newusers.c.account-tools-setuid shadow-4.18.0-rc1 case 'h': usage (EXIT_SUCCESS); break; -@@ -675,6 +697,7 @@ static void process_flags (int argc, cha - break; +@@ -684,6 +706,7 @@ static void process_flags (int argc, cha case 'R': /* no-op, handled in process_root_flag () */ + flags->chroot = true; break; +#ifndef ACCT_TOOLS_SETUID #ifndef USE_PAM #if defined(USE_SHA_CRYPT) || defined(USE_BCRYPT) || defined(USE_YESCRYPT) case 's': -@@ -714,6 +737,7 @@ static void process_flags (int argc, cha +@@ -723,6 +746,7 @@ static void process_flags (int argc, cha break; #endif /* USE_SHA_CRYPT || USE_BCRYPT || USE_YESCRYPT */ #endif /* !USE_PAM */ @@ -315,7 +312,7 @@ diff -up shadow-4.18.0-rc1/src/newusers.c.account-tools-setuid shadow-4.18.0-rc1 default: usage (EXIT_FAILURE); break; -@@ -746,6 +770,7 @@ static void process_flags (int argc, cha +@@ -755,6 +779,7 @@ static void process_flags (int argc, cha */ static void check_flags (void) { @@ -323,7 +320,7 @@ diff -up shadow-4.18.0-rc1/src/newusers.c.account-tools-setuid shadow-4.18.0-rc1 #ifndef USE_PAM #if defined(USE_SHA_CRYPT) || defined(USE_BCRYPT) || defined(USE_YESCRYPT) if (sflg && !cflg) { -@@ -778,6 +803,7 @@ static void check_flags (void) +@@ -787,6 +812,7 @@ static void check_flags (void) } } #endif /* !USE_PAM */ @@ -331,7 +328,7 @@ diff -up shadow-4.18.0-rc1/src/newusers.c.account-tools-setuid shadow-4.18.0-rc1 } /* -@@ -1066,12 +1092,14 @@ int main (int argc, char **argv) +@@ -1065,12 +1091,14 @@ int main (int argc, char **argv) intmax_t line = 0; uid_t uid; gid_t gid; @@ -343,10 +340,10 @@ diff -up shadow-4.18.0-rc1/src/newusers.c.account-tools-setuid shadow-4.18.0-rc1 size_t nusers = 0; #endif /* USE_PAM */ +#endif /* ACCT_TOOLS_SETUID */ + struct option_flags flags = {.chroot = false}; + bool process_selinux; - log_set_progname(Prog); - log_set_logfd(stderr); -@@ -1191,6 +1219,7 @@ int main (int argc, char **argv) +@@ -1193,6 +1221,7 @@ int main (int argc, char **argv) } newpw = *pw; @@ -359,10 +356,10 @@ diff -up shadow-4.18.0-rc1/src/newusers.c.account-tools-setuid shadow-4.18.0-rc1 passwords[nusers-1] = xstrdup(fields[1]); #endif /* USE_PAM */ +#endif /* ACCT_TOOLS_SETUID */ - if (add_passwd (&newpw, fields[1]) != 0) { + if (!streq(fields[1], "") && add_passwd(&newpw, fields[1]) != 0) { fprintf (stderr, _("%s: line %jd: can't update password\n"), -@@ -1323,6 +1353,7 @@ int main (int argc, char **argv) +@@ -1321,6 +1351,7 @@ int main (int argc, char **argv) nscd_flush_cache ("group"); sssd_flush_cache (SSSD_DB_PASSWD | SSSD_DB_GROUP); diff --git a/shadow-utils.spec b/shadow-utils.spec index 1769e3c..9aff11f 100644 --- a/shadow-utils.spec +++ b/shadow-utils.spec @@ -1,12 +1,12 @@ Summary: Utilities for managing accounts and shadow password files Name: shadow-utils -Version: 4.18.0 -Release: 7%{?dist} +Version: 4.19.0 +Release: 1%{?dist} Epoch: 2 License: BSD-3-Clause AND GPL-2.0-or-later URL: https://github.com/shadow-maint/shadow -Source0: https://github.com/shadow-maint/shadow/releases/download/4.18.0/shadow-4.18.0.tar.xz -Source1: https://github.com/shadow-maint/shadow/releases/download/4.18.0/shadow-4.18.0.tar.xz.asc +Source0: https://github.com/shadow-maint/shadow/releases/download/4.19.0/shadow-4.19.0.tar.xz +Source1: https://github.com/shadow-maint/shadow/releases/download/4.19.0/shadow-4.19.0.tar.xz.asc Source2: shadow-utils.useradd Source3: shadow-utils.login.defs Source4: shadow-bsd.txt @@ -23,7 +23,7 @@ Source7: passwd.pamd # Misc manual page changes - non-upstreamable Patch0: shadow-4.15.0-manfix.patch # Probably non-upstreamable -Patch1: shadow-4.18.0-account-tools-setuid.patch +Patch1: shadow-4.19.0-account-tools-setuid.patch ### Dependencies ### Requires: audit-libs >= 1.6.5 @@ -101,7 +101,7 @@ Requires: shadow-utils-subid = %{epoch}:%{version}-%{release} Development files for shadow-utils-subid. %prep -%autosetup -p 1 -S git -n shadow-4.18.0 +%autosetup -p 1 -S git -n shadow-4.19.0 iconv -f ISO88591 -t utf-8 doc/HOWTO > doc/HOWTO.utf8 cp -f doc/HOWTO.utf8 doc/HOWTO @@ -269,6 +269,10 @@ rm -f $RPM_BUILD_ROOT/%{_libdir}/libsubid.a %{_libdir}/libsubid.so %changelog +* Fri Jan 9 2026 Iker Pedrosa - 2:4.19.0-1 +- Rebase to version 4.19.0 + Resolves: #2426288 and #2249524 + * Tue Nov 25 2025 Adam Williamson - 2:4.18.0-7 - Also revert changes from -4 (last known good was -3) diff --git a/sources b/sources index 8e3da98..22ae907 100644 --- a/sources +++ b/sources @@ -1,2 +1,2 @@ -SHA512 (shadow-4.18.0.tar.xz) = e724670362949ccb570bb300322eb280c28ad609d631072b8db15a5e6eeba9fb9ac5d3df1df270f8b894235e4995b56c0e8f9aadb34d66bb5cea9b7a4f55e6ce -SHA512 (shadow-4.18.0.tar.xz.asc) = bb82b3d54eec0f11ace57609281974b315bba1240eafdae3b6513c7d7dfbcf10c3ae855c74c9eae33539709072046787e4b3e86dfb8bbfbf4bf23446daeb3422 +SHA512 (shadow-4.19.0.tar.xz) = 0846c34f426101213ab6277bbcdc58c28b3a54df2f68d1c1addcc8c5aa79ce3e15d5d8dfcb9cc6dbd1b758e0cd8676c68ca65268f5a037d4c24aa7d358c9a390 +SHA512 (shadow-4.19.0.tar.xz.asc) = 34c1a764d06a5d24533cbb57310de7462eb632c1de904901699d48f44401b1b1f4751db35f2b0e2b9a057cf1a777ccddbae4bc6b19a6db865ebc3e30fde0d060 From 19e27b9aa53dde8f63e79bde6d7a7cdb2725a875 Mon Sep 17 00:00:00 2001 From: Iker Pedrosa Date: Fri, 9 Jan 2026 15:01:29 +0100 Subject: [PATCH 16/30] Enable unit-tests Signed-off-by: Iker Pedrosa --- shadow-utils.spec | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/shadow-utils.spec b/shadow-utils.spec index 9aff11f..78bf8a9 100644 --- a/shadow-utils.spec +++ b/shadow-utils.spec @@ -1,7 +1,7 @@ Summary: Utilities for managing accounts and shadow password files Name: shadow-utils Version: 4.19.0 -Release: 1%{?dist} +Release: 2%{?dist} Epoch: 2 License: BSD-3-Clause AND GPL-2.0-or-later URL: https://github.com/shadow-maint/shadow @@ -45,6 +45,7 @@ BuildRequires: git BuildRequires: itstool BuildRequires: libacl-devel BuildRequires: libattr-devel +BuildRequires: libcmocka-devel BuildRequires: libeconf-devel BuildRequires: libselinux-devel >= 1.25.2-1 BuildRequires: libsemanage-devel @@ -130,6 +131,9 @@ autoreconf --without-sssd %make_build +%check +make check + %install %make_install gnulocaledir=$RPM_BUILD_ROOT%{_datadir}/locale MKINSTALLDIRS=`pwd`/mkinstalldirs install -d -m 755 $RPM_BUILD_ROOT%{_sysconfdir}/default @@ -269,6 +273,9 @@ rm -f $RPM_BUILD_ROOT/%{_libdir}/libsubid.a %{_libdir}/libsubid.so %changelog +* Fri Jan 9 2026 Iker Pedrosa - 2:4.19.0-2 +- Enable unit-tests + * Fri Jan 9 2026 Iker Pedrosa - 2:4.19.0-1 - Rebase to version 4.19.0 Resolves: #2426288 and #2249524 From f47e36e72a473fbb90a92bc73f63042fea7e87df Mon Sep 17 00:00:00 2001 From: Iker Pedrosa Date: Mon, 12 Jan 2026 11:42:00 +0100 Subject: [PATCH 17/30] useradd: Support config for creating home dirs as Btrfs subvolumes Signed-off-by: Iker Pedrosa --- shadow-4-19-useradd-support-btrfs.patch | 149 ++++++++++++++++++++++++ shadow-utils.spec | 7 +- 2 files changed, 155 insertions(+), 1 deletion(-) create mode 100644 shadow-4-19-useradd-support-btrfs.patch diff --git a/shadow-4-19-useradd-support-btrfs.patch b/shadow-4-19-useradd-support-btrfs.patch new file mode 100644 index 0000000..b801289 --- /dev/null +++ b/shadow-4-19-useradd-support-btrfs.patch @@ -0,0 +1,149 @@ +From 3e8c105f0703264e947d8c034b90419794955d49 Mon Sep 17 00:00:00 2001 +From: Hadi Chokr +Date: Fri, 12 Dec 2025 11:18:06 +0100 +Subject: [PATCH] src/useradd: Support config for creating home dirs as Btrfs + subvolumes + +Closes: #1162 + +Co-authored-by: Neal Gompa +Signed-off-by: Hadi Chokr +Signed-off-by: Neal Gompa +Reviewed-by: Alejandro Colomar +--- + man/useradd.8.xml | 27 +++++++++++++++++++++++++++ + src/useradd.c | 24 ++++++++++++++++++++++++ + 2 files changed, 51 insertions(+) + +diff --git a/man/useradd.8.xml b/man/useradd.8.xml +index e6db7f6d..c745a620 100644 +--- a/man/useradd.8.xml ++++ b/man/useradd.8.xml +@@ -134,6 +134,33 @@ + + + ++ ++ ++ ++ ++ ++ ++ Create the user's home directory as a Btrfs subvolume. ++ ++ ++ If this option is not specified, ++ useradd will follow the default behavior ++ defined by the variable ++ in /etc/default/useradd. ++ If this variable is not set, the default value is no. ++ ++ ++ When the command-line option ++ is specified, ++ a Btrfs subvolume is created ++ regardless of any configuration file settings. ++ ++ ++ Note: this feature works only if the underlying filesystem supports ++ Btrfs subvolumes. ++ ++ ++ + + + ,  COMMENT +diff --git a/src/useradd.c b/src/useradd.c +index 899efe3c..8433ed8a 100644 +--- a/src/useradd.c ++++ b/src/useradd.c +@@ -114,6 +114,7 @@ static const char *def_shell = "/bin/bash"; + static const char *def_template = SKEL_DIR; + static const char *def_usrtemplate = USRSKELDIR; + static const char *def_create_mail_spool = "yes"; ++static const char *def_btrfs_subvolume_home = "no"; + static const char *def_log_init = "yes"; + + static long def_inactive = -1; +@@ -222,6 +223,7 @@ static bool home_added = false; + #define DSKEL "SKEL" + #define DUSRSKEL "USRSKEL" + #define DCREATE_MAIL_SPOOL "CREATE_MAIL_SPOOL" ++#define DBTRFS_SUBVOLUME_HOME "BTRFS_SUBVOLUME_HOME" + #define DLOG_INIT "LOG_INIT" + + /* local function prototypes */ +@@ -456,6 +458,7 @@ get_defaults(const struct option_flags *flags) + def_usrtemplate = xstrdup(ccp); + } + } ++ + /* + * Create by default user mail spool or not ? + */ +@@ -466,6 +469,15 @@ get_defaults(const struct option_flags *flags) + def_create_mail_spool = xstrdup(ccp); + } + ++ /* ++ * Create home directories as Btrfs subvolumes by default? ++ */ ++ else if (streq(buf, DBTRFS_SUBVOLUME_HOME)) { ++ if (streq(ccp, "")) ++ ccp = "no"; ++ def_btrfs_subvolume_home = xstrdup(ccp); ++ } ++ + /* + * By default do we add the user to the lastlog and faillog databases ? + */ +@@ -500,6 +512,7 @@ static void show_defaults (void) + printf ("SKEL=%s\n", def_template); + printf ("USRSKEL=%s\n", def_usrtemplate); + printf ("CREATE_MAIL_SPOOL=%s\n", def_create_mail_spool); ++ printf ("BTRFS_SUBVOLUME_HOME=%s\n", def_btrfs_subvolume_home); + printf ("LOG_INIT=%s\n", def_log_init); + } + +@@ -523,6 +536,7 @@ set_defaults(void) + bool out_skel = false; + bool out_usrskel = false; + bool out_create_mail_spool = false; ++ bool out_btrfs_subvolume_home = false; + bool out_log_init = false; + char buf[1024]; + char *new_file = NULL; +@@ -639,6 +653,11 @@ set_defaults(void) + DCREATE_MAIL_SPOOL "=%s\n", + def_create_mail_spool); + out_create_mail_spool = true; ++ } else if (!out_btrfs_subvolume_home && streq(buf, DBTRFS_SUBVOLUME_HOME)) { ++ fprintf(ofp, ++ DBTRFS_SUBVOLUME_HOME "=%s\n", ++ def_btrfs_subvolume_home); ++ out_btrfs_subvolume_home = true; + } else if (!out_log_init && streq(buf, DLOG_INIT)) { + fprintf(ofp, DLOG_INIT "=%s\n", def_log_init); + out_log_init = true; +@@ -673,6 +692,8 @@ set_defaults(void) + + if (!out_create_mail_spool) + fprintf (ofp, DCREATE_MAIL_SPOOL "=%s\n", def_create_mail_spool); ++ if (!out_btrfs_subvolume_home) ++ fprintf (ofp, DBTRFS_SUBVOLUME_HOME "=%s\n", def_btrfs_subvolume_home); + if (!out_log_init) + fprintf (ofp, DLOG_INIT "=%s\n", def_log_init); + /* +@@ -1431,6 +1452,9 @@ static void process_flags (int argc, char **argv, struct option_flags *flags) + } + } + ++ if (!subvolflg && strcaseeq(def_btrfs_subvolume_home, "yes")) ++ subvolflg = true; ++ + if (!gflg && !Nflg && !Uflg) { + /* Get the settings from login.defs */ + Uflg = getdef_bool ("USERGROUPS_ENAB"); +-- +2.52.0 + diff --git a/shadow-utils.spec b/shadow-utils.spec index 78bf8a9..f625b8e 100644 --- a/shadow-utils.spec +++ b/shadow-utils.spec @@ -1,7 +1,7 @@ Summary: Utilities for managing accounts and shadow password files Name: shadow-utils Version: 4.19.0 -Release: 2%{?dist} +Release: 3%{?dist} Epoch: 2 License: BSD-3-Clause AND GPL-2.0-or-later URL: https://github.com/shadow-maint/shadow @@ -24,6 +24,8 @@ Source7: passwd.pamd Patch0: shadow-4.15.0-manfix.patch # Probably non-upstreamable Patch1: shadow-4.19.0-account-tools-setuid.patch +# https://github.com/shadow-maint/shadow/commit/3e8c105f0703264e947d8c034b90419794955d49 +Patch2: shadow-4-19-useradd-support-btrfs.patch ### Dependencies ### Requires: audit-libs >= 1.6.5 @@ -273,6 +275,9 @@ rm -f $RPM_BUILD_ROOT/%{_libdir}/libsubid.a %{_libdir}/libsubid.so %changelog +* Mon Jan 12 2026 Iker Pedrosa - 2:4.19.0-3 +- useradd: Support config for creating home dirs as Btrfs subvolumes + * Fri Jan 9 2026 Iker Pedrosa - 2:4.19.0-2 - Enable unit-tests From c78272db1abeab24ef3676dc669f5752bd571108 Mon Sep 17 00:00:00 2001 From: Fedora Release Engineering Date: Sat, 17 Jan 2026 17:57:49 +0000 Subject: [PATCH 18/30] Rebuilt for https://fedoraproject.org/wiki/Fedora_44_Mass_Rebuild --- shadow-utils.spec | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/shadow-utils.spec b/shadow-utils.spec index f625b8e..f58ceee 100644 --- a/shadow-utils.spec +++ b/shadow-utils.spec @@ -1,7 +1,7 @@ Summary: Utilities for managing accounts and shadow password files Name: shadow-utils Version: 4.19.0 -Release: 3%{?dist} +Release: 4%{?dist} Epoch: 2 License: BSD-3-Clause AND GPL-2.0-or-later URL: https://github.com/shadow-maint/shadow @@ -275,6 +275,9 @@ rm -f $RPM_BUILD_ROOT/%{_libdir}/libsubid.a %{_libdir}/libsubid.so %changelog +* Sat Jan 17 2026 Fedora Release Engineering - 2:4.19.0-4 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_44_Mass_Rebuild + * Mon Jan 12 2026 Iker Pedrosa - 2:4.19.0-3 - useradd: Support config for creating home dirs as Btrfs subvolumes From be2ac1934789e9a3a5ac74f6803ac101ccb16791 Mon Sep 17 00:00:00 2001 From: Iker Pedrosa Date: Mon, 26 Jan 2026 10:03:00 +0100 Subject: [PATCH 19/30] - chkhash.c: fix support for ! and * in hashes - usermod.c: add back optimizations Signed-off-by: Iker Pedrosa --- shadow-4.19.0-chkhash1.patch | 88 +++++++++++ shadow-4.19.0-chkhash2.patch | 137 ++++++++++++++++++ shadow-4.19.0-usermod-add-optimizations.patch | 75 ++++++++++ shadow-utils.spec | 12 +- 4 files changed, 311 insertions(+), 1 deletion(-) create mode 100644 shadow-4.19.0-chkhash1.patch create mode 100644 shadow-4.19.0-chkhash2.patch create mode 100644 shadow-4.19.0-usermod-add-optimizations.patch diff --git a/shadow-4.19.0-chkhash1.patch b/shadow-4.19.0-chkhash1.patch new file mode 100644 index 0000000..986f10b --- /dev/null +++ b/shadow-4.19.0-chkhash1.patch @@ -0,0 +1,88 @@ +From 87ec7a52ab25dd6e91253c274fd651f16844cf2a Mon Sep 17 00:00:00 2001 +From: Alejandro Colomar +Date: Wed, 7 Jan 2026 23:39:53 +0100 +Subject: [PATCH 1/2] 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: +Link: +Reported-by: Chris Hofstaedtler +Reviewed-by: Chris Hofstaedtler +Cc: vinz +Signed-off-by: Alejandro Colomar +--- + lib/chkhash.c | 4 ++++ + 1 file changed, 4 insertions(+) + +diff --git a/lib/chkhash.c b/lib/chkhash.c +index 66870500..cb45f3aa 100644 +--- a/lib/chkhash.c ++++ b/lib/chkhash.c +@@ -7,6 +7,8 @@ + #include + #include + ++#include "string/strcmp/strprefix.h" ++ + + /* + * match_regex - return true if match, false if not +@@ -37,6 +39,8 @@ match_regex(const char *pattern, const char *string) + bool + is_valid_hash(const char *hash) + { ++ hash = strprefix(hash, "!") ?: hash; ++ + // Minimum hash length + if (strlen(hash) < 13) + return false; +-- +2.52.0 + + +From ddc2549f87e3001f663d5179e9b6d7fe2e1f3b3f Mon Sep 17 00:00:00 2001 +From: Alejandro Colomar +Date: Wed, 7 Jan 2026 23:44:26 +0100 +Subject: [PATCH 2/2] 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: +Closes: +Reported-by: Chris Hofstaedtler +Reviewed-by: Chris Hofstaedtler +Cc: vinz +Signed-off-by: Alejandro Colomar +--- + lib/chkhash.c | 4 ++++ + 1 file changed, 4 insertions(+) + +diff --git a/lib/chkhash.c b/lib/chkhash.c +index cb45f3aa..71e0fb4e 100644 +--- a/lib/chkhash.c ++++ b/lib/chkhash.c +@@ -7,6 +7,7 @@ + #include + #include + ++#include "string/strcmp/streq.h" + #include "string/strcmp/strprefix.h" + + +@@ -41,6 +42,9 @@ is_valid_hash(const char *hash) + { + hash = strprefix(hash, "!") ?: hash; + ++ if (streq(hash, "*")) ++ return true; ++ + // Minimum hash length + if (strlen(hash) < 13) + return false; +-- +2.52.0 + diff --git a/shadow-4.19.0-chkhash2.patch b/shadow-4.19.0-chkhash2.patch new file mode 100644 index 0000000..1742544 --- /dev/null +++ b/shadow-4.19.0-chkhash2.patch @@ -0,0 +1,137 @@ +From 9b67543987e3d140c86f1b8e2b5db5c10d8bc3c5 Mon Sep 17 00:00:00 2001 +From: Alejandro Colomar +Date: Thu, 15 Jan 2026 01:11:52 +0100 +Subject: [PATCH 1/4] 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: +Reported-by: Marc 'Zugschlus' Haber +Reported-by: "Serge E. Hallyn" +Reported-by: Adam Williamson +Co-authored-by: "Serge E. Hallyn" +Signed-off-by: Alejandro Colomar +--- + lib/chkhash.c | 4 ++++ + 1 file changed, 4 insertions(+) + +diff --git a/lib/chkhash.c b/lib/chkhash.c +index 71e0fb4e..d0a8dc01 100644 +--- a/lib/chkhash.c ++++ b/lib/chkhash.c +@@ -42,6 +42,10 @@ is_valid_hash(const char *hash) + { + hash = strprefix(hash, "!") ?: hash; + ++ // Passwordless account; discouraged ++ if (streq(hash, "")) ++ return true; ++ + if (streq(hash, "*")) + return true; + +-- +2.52.0 + + +From bce404a7a831de911cf20fc8d4c548957f70bc72 Mon Sep 17 00:00:00 2001 +From: Alejandro Colomar +Date: Thu, 15 Jan 2026 01:14:46 +0100 +Subject: [PATCH 2/4] 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 +Signed-off-by: Alejandro Colomar +--- + lib/chkhash.c | 4 +--- + 1 file changed, 1 insertion(+), 3 deletions(-) + +diff --git a/lib/chkhash.c b/lib/chkhash.c +index d0a8dc01..1723f862 100644 +--- a/lib/chkhash.c ++++ b/lib/chkhash.c +@@ -31,9 +31,7 @@ match_regex(const char *pattern, const char *string) + + + /* +- * is_valid_hash - check if the given string is a valid password hash +- * +- * Returns true if the string appears to be a valid hash, false otherwise. ++ * is_valid_hash - check if the string is a valid shadow(5) 2nd field. + * + * regex from: https://man.archlinux.org/man/crypt.5.en + */ +-- +2.52.0 + + +From 9a86c515a1e7caeeb9e60fa63871170a2dfd98f0 Mon Sep 17 00:00:00 2001 +From: Alejandro Colomar +Date: Thu, 15 Jan 2026 01:24:10 +0100 +Subject: [PATCH 3/4] lib/chkhash.c: is_valid_hash(): Comment meaning of !hash + and * + +Signed-off-by: Alejandro Colomar +--- + lib/chkhash.c | 2 ++ + 1 file changed, 2 insertions(+) + +diff --git a/lib/chkhash.c b/lib/chkhash.c +index 1723f862..101f2004 100644 +--- a/lib/chkhash.c ++++ b/lib/chkhash.c +@@ -38,12 +38,14 @@ match_regex(const char *pattern, const char *string) + 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; + +-- +2.52.0 + + +From 6be13b2f84a2c1a0d0f4129b5258b4b443e7f86c Mon Sep 17 00:00:00 2001 +From: Alejandro Colomar +Date: Fri, 16 Jan 2026 01:52:32 +0100 +Subject: [PATCH 4/4] man/shadow.5.xml: Document "*" + +Signed-off-by: Alejandro Colomar +--- + man/shadow.5.xml | 8 ++++++-- + 1 file changed, 6 insertions(+), 2 deletions(-) + +diff --git a/man/shadow.5.xml b/man/shadow.5.xml +index dc7fdfde..d3cc0398 100644 +--- a/man/shadow.5.xml ++++ b/man/shadow.5.xml +@@ -82,8 +82,12 @@ + If the password field begins with an exclamation mark !, + the password is locked. + The remaining characters on the +- line represent the password field before the password was +- locked. ++ line represent the password hash. ++ ++ ++ If the password hash consists of a star *, ++ password access is effectively disallowed; ++ this is because no password can produce a hash like this. + + + Refer to crypt +-- +2.52.0 + diff --git a/shadow-4.19.0-usermod-add-optimizations.patch b/shadow-4.19.0-usermod-add-optimizations.patch new file mode 100644 index 0000000..4a883e6 --- /dev/null +++ b/shadow-4.19.0-usermod-add-optimizations.patch @@ -0,0 +1,75 @@ +From 958b4859991e700b61af2f9e07e3aa87ad1d9218 Mon Sep 17 00:00:00 2001 +From: Alejandro Colomar +Date: Sat, 17 Jan 2026 00:56:21 +0100 +Subject: [PATCH] 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: +Reported-by: Adam Williamson +Signed-off-by: Alejandro Colomar +--- + src/usermod.c | 42 ++++++++++++++++++++++++++++++++++++++++++ + 1 file changed, 42 insertions(+) + +diff --git a/src/usermod.c b/src/usermod.c +index 7400bf84..7cd7a859 100644 +--- a/src/usermod.c ++++ b/src/usermod.c +@@ -1391,6 +1391,48 @@ process_flags(int argc, char **argv, struct option_flags *flags) + } + #endif /* WITH_SELINUX */ + ++ if (user_newid == user_id) { ++ uflg = false; ++ oflg = false; ++ } ++ if (user_newgid == user_gid) { ++ gflg = false; ++ } ++ if ( (NULL != user_newshell) ++ && streq(user_newshell, user_shell)) { ++ sflg = false; ++ } ++ if (streq(user_newname, user_name)) { ++ lflg = false; ++ } ++ if (user_newinactive == user_inactive) { ++ fflg = false; ++ } ++ if (user_newexpire == user_expire) { ++ eflg = false; ++ } ++ if ( (NULL != user_newhome) ++ && streq(user_newhome, user_home)) { ++ dflg = false; ++ mflg = false; ++ } ++ if ( (NULL != user_newcomment) ++ && streq(user_newcomment, user_comment)) { ++ cflg = false; ++ } ++ ++ if (!(Uflg || uflg || sflg || pflg || mflg || Lflg || ++ lflg || Gflg || gflg || fflg || eflg || dflg || cflg ++#ifdef ENABLE_SUBIDS ++ || vflg || Vflg || wflg || Wflg ++#endif /* ENABLE_SUBIDS */ ++#ifdef WITH_SELINUX ++ || Zflg ++#endif /* WITH_SELINUX */ ++ )) { ++ exit (E_SUCCESS); ++ } ++ + if (!is_shadow_pwd && (eflg || fflg)) { + fprintf (stderr, + _("%s: shadow passwords required for -e and -f\n"), +-- +2.52.0 + diff --git a/shadow-utils.spec b/shadow-utils.spec index f58ceee..82ea3c4 100644 --- a/shadow-utils.spec +++ b/shadow-utils.spec @@ -1,7 +1,7 @@ Summary: Utilities for managing accounts and shadow password files Name: shadow-utils Version: 4.19.0 -Release: 4%{?dist} +Release: 5%{?dist} Epoch: 2 License: BSD-3-Clause AND GPL-2.0-or-later URL: https://github.com/shadow-maint/shadow @@ -26,6 +26,12 @@ Patch0: shadow-4.15.0-manfix.patch Patch1: shadow-4.19.0-account-tools-setuid.patch # https://github.com/shadow-maint/shadow/commit/3e8c105f0703264e947d8c034b90419794955d49 Patch2: shadow-4-19-useradd-support-btrfs.patch +# https://github.com/shadow-maint/shadow/commit/3e8c105f0703264e947d8c034b90419794955d49 +Patch3: shadow-4.19.0-chkhash1.patch +# https://github.com/shadow-maint/shadow/commit/9b67543987e3d140c86f1b8e2b5db5c10d8bc3c5 +Patch4: shadow-4.19.0-chkhash2.patch +# https://github.com/shadow-maint/shadow/commit/958b4859991e700b61af2f9e07e3aa87ad1d9218 +Patch5: shadow-4.19.0-usermod-add-optimizations.patch ### Dependencies ### Requires: audit-libs >= 1.6.5 @@ -275,6 +281,10 @@ rm -f $RPM_BUILD_ROOT/%{_libdir}/libsubid.a %{_libdir}/libsubid.so %changelog +* Mon Jan 26 2026 Iker Pedrosa - 2:4.19.0-5 +- chkhash.c: fix support for ! and * in hashes +- usermod.c: add back optimizations + * Sat Jan 17 2026 Fedora Release Engineering - 2:4.19.0-4 - Rebuilt for https://fedoraproject.org/wiki/Fedora_44_Mass_Rebuild From dac50e34f899a79c7dc94ef1a0f76a5af6ffcc8d Mon Sep 17 00:00:00 2001 From: Adam Williamson Date: Wed, 28 Jan 2026 11:21:11 -0800 Subject: [PATCH 20/30] chkhash.c: fix escaping in SHA-256 / SHA-512 / MD5 regexes Signed-off-by: Adam Williamson --- 1520.patch | 40 ++++++++++++++++++++++++++++++++++++++++ shadow-utils.spec | 10 +++++++++- 2 files changed, 49 insertions(+), 1 deletion(-) create mode 100644 1520.patch diff --git a/1520.patch b/1520.patch new file mode 100644 index 0000000..069eebb --- /dev/null +++ b/1520.patch @@ -0,0 +1,40 @@ +From b721cdd03daa3416127289da02fe781efb2562df Mon Sep 17 00:00:00 2001 +From: Adam Williamson +Date: Tue, 27 Jan 2026 15:11:07 -0800 +Subject: [PATCH] lib/chkhash.c: fix escaping in SHA-256 / SHA-512 / MD5 + regexes + +`\\n` inside square brackets doesn't include or exclude the +newline character. It includes or excludes a literal slash and +the literal character 'n'. + +Fixes: c44f1e096a19 (2025-07-20; "chpasswd: Check hash before write when using -e") +Closes: +Signed-off-by: Adam Williamson +--- + lib/chkhash.c | 6 +++--- + 1 file changed, 3 insertions(+), 3 deletions(-) + +diff --git a/lib/chkhash.c b/lib/chkhash.c +index 101f2004e8..9123038ade 100644 +--- a/lib/chkhash.c ++++ b/lib/chkhash.c +@@ -62,15 +62,15 @@ is_valid_hash(const char *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)) ++ 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)) ++ 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)) ++ if (match_regex("^\\$1\\$[^$:\n]{1,8}\\$[./A-Za-z0-9]{22}$", hash)) + return true; + + // DES: exactly 13 characters from [A-Za-z0-9./] diff --git a/shadow-utils.spec b/shadow-utils.spec index 82ea3c4..de8a156 100644 --- a/shadow-utils.spec +++ b/shadow-utils.spec @@ -1,7 +1,7 @@ Summary: Utilities for managing accounts and shadow password files Name: shadow-utils Version: 4.19.0 -Release: 5%{?dist} +Release: 6%{?dist} Epoch: 2 License: BSD-3-Clause AND GPL-2.0-or-later URL: https://github.com/shadow-maint/shadow @@ -32,6 +32,11 @@ Patch3: shadow-4.19.0-chkhash1.patch Patch4: shadow-4.19.0-chkhash2.patch # https://github.com/shadow-maint/shadow/commit/958b4859991e700b61af2f9e07e3aa87ad1d9218 Patch5: shadow-4.19.0-usermod-add-optimizations.patch +# https://github.com/shadow-maint/shadow/pull/1520 +# Approved by upstream for 4.19.3 this weekend: +# https://github.com/shadow-maint/shadow/issues/1521 +# Fixes the hash check to accept hashes with \ or n in the salt +Patch6: 1520.patch ### Dependencies ### Requires: audit-libs >= 1.6.5 @@ -281,6 +286,9 @@ rm -f $RPM_BUILD_ROOT/%{_libdir}/libsubid.a %{_libdir}/libsubid.so %changelog +* Tue Jan 27 2026 Adam Williamson - 2:4.19.0-6 +- chkhash.c: fix escaping in SHA-256 / SHA-512 / MD5 regexes + * Mon Jan 26 2026 Iker Pedrosa - 2:4.19.0-5 - chkhash.c: fix support for ! and * in hashes - usermod.c: add back optimizations From cf630b1c3cb7762caef5ffa0ce75446e7a5afb75 Mon Sep 17 00:00:00 2001 From: Debarshi Ray Date: Mon, 19 Jan 2026 14:26:44 +0100 Subject: [PATCH 21/30] Remove unused patches There's no need to do a build just for this. Fallout from 799aaaa5088cde64c65f2897d9c335c52e4de0bc https://src.fedoraproject.org/rpms/shadow-utils/pull-request/25 --- ...3-newidmap-support-passing-pid-as-fd.patch | 441 ------------------ shadow-utils-configure-gshadow.patch | 20 - 2 files changed, 461 deletions(-) delete mode 100644 shadow-4.13-newidmap-support-passing-pid-as-fd.patch delete mode 100644 shadow-utils-configure-gshadow.patch diff --git a/shadow-4.13-newidmap-support-passing-pid-as-fd.patch b/shadow-4.13-newidmap-support-passing-pid-as-fd.patch deleted file mode 100644 index 37e0d41..0000000 --- a/shadow-4.13-newidmap-support-passing-pid-as-fd.patch +++ /dev/null @@ -1,441 +0,0 @@ -From 6974df39a708abf8bafbdfa2b7827e0f70f874cb Mon Sep 17 00:00:00 2001 -From: Serge Hallyn -Date: Mon, 6 Feb 2023 22:49:42 -0600 -Subject: [PATCH] newuidmap and newgidmap: support passing pid as fd - -Closes #635 - -newuidmap and newgidmap currently take an integner pid as -the first argument, determining the process id on which to -act. Accept also "fd:N", where N must be an open file -descriptor to the /proc/pid directory for the process to -act upon. This way, if you - -exec 10 ---- - lib/get_pid.c | 51 +++++++++++++++++++++++++++++++++++++++++++++ - lib/prototypes.h | 2 ++ - man/newgidmap.1.xml | 11 ++++++++++ - man/newuidmap.1.xml | 11 ++++++++++ - src/newgidmap.c | 41 ++++++++++++++---------------------- - src/newuidmap.c | 40 +++++++++++++---------------------- - 6 files changed, 106 insertions(+), 50 deletions(-) - -diff --git a/lib/get_pid.c b/lib/get_pid.c -index 10184bf0..ab91d158 100644 ---- a/lib/get_pid.c -+++ b/lib/get_pid.c -@@ -10,6 +10,9 @@ - - #include "prototypes.h" - #include "defines.h" -+#include -+#include -+#include - - int get_pid (const char *pidstr, pid_t *pid) - { -@@ -29,3 +32,51 @@ int get_pid (const char *pidstr, pid_t *pid) - return 1; - } - -+/* -+ * If use passed in fd:4 as an argument, then return the -+ * value '4', the fd to use. -+ */ -+int get_pidfd_from_fd(const char *pidfdstr) -+{ -+ long long int val; -+ char *endptr; -+ -+ errno = 0; -+ val = strtoll (pidfdstr, &endptr, 10); -+ if ( ('\0' == *pidfdstr) -+ || ('\0' != *endptr) -+ || (ERANGE == errno) -+ || (/*@+longintegral@*/val != (pid_t)val)/*@=longintegral@*/) { -+ return 0; -+ } -+ -+ return (int)val; -+} -+ -+int open_pidfd(const char *pidstr) -+{ -+ int proc_dir_fd; -+ int written; -+ char proc_dir_name[32]; -+ pid_t target; -+ -+ if (get_pid(pidstr, &target) == 0) -+ return -ENOENT; -+ -+ /* max string length is 6 + 10 + 1 + 1 = 18, allocate 32 bytes */ -+ written = snprintf(proc_dir_name, sizeof(proc_dir_name), "/proc/%u/", -+ target); -+ if ((written <= 0) || ((size_t)written >= sizeof(proc_dir_name))) { -+ fprintf(stderr, "snprintf of proc path failed for %u: %s\n", -+ target, strerror(errno)); -+ 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 %u: %s\n"), -+ target, strerror(errno)); -+ return -EINVAL; -+ } -+ return proc_dir_fd; -+} -diff --git a/lib/prototypes.h b/lib/prototypes.h -index 400d5b97..21df6f61 100644 ---- a/lib/prototypes.h -+++ b/lib/prototypes.h -@@ -160,6 +160,8 @@ extern int getlong (const char *numstr, /*@out@*/long int *result); - - /* get_pid.c */ - extern int get_pid (const char *pidstr, pid_t *pid); -+extern int get_pidfd_from_fd(const char *pidfdstr); -+extern int open_pidfd(const char *pidstr); - - /* getrange */ - extern int getrange (const char *range, -diff --git a/man/newgidmap.1.xml b/man/newgidmap.1.xml -index e4ebc69e..9b7683eb 100644 ---- a/man/newgidmap.1.xml -+++ b/man/newgidmap.1.xml -@@ -116,6 +116,17 @@ - - Note that newgidmap may be used only once for a given process. - -+ -+ Instead of an integer process id, the first argument may be -+ specified as fd:N, where the integer N -+ is the file descriptor number for the calling process's opened -+ file for /proc/[pid[. In this case, -+ newgidmap will use -+ openat2 -+ to open the gid_map file under that -+ directory, avoiding a TOCTTOU in case the process exits and -+ the pid is immediately reused. -+ - - - -diff --git a/man/newuidmap.1.xml b/man/newuidmap.1.xml -index f5cb5b48..ca917a77 100644 ---- a/man/newuidmap.1.xml -+++ b/man/newuidmap.1.xml -@@ -116,6 +116,17 @@ - - Note that newuidmap may be used only once for a given process. - -+ -+ Instead of an integer process id, the first argument may be -+ specified as fd:N, where the integer N -+ is the file descriptor number for the calling process's opened -+ file for /proc/[pid[. In this case, -+ newuidmap will use -+ openat2 -+ to open the uid_map file under that -+ directory, avoiding a TOCTTOU in case the process exits and -+ the pid is immediately reused. -+ - - - -diff --git a/src/newgidmap.c b/src/newgidmap.c -index 01d0fe90..d6d29725 100644 ---- a/src/newgidmap.c -+++ b/src/newgidmap.c -@@ -69,7 +69,7 @@ static void verify_ranges(struct passwd *pw, int ranges, - - static void usage(void) - { -- fprintf(stderr, _("usage: %s [ ] ... \n"), Prog); -+ fprintf(stderr, _("usage: %s [] [ ] ... \n"), Prog); - exit(EXIT_FAILURE); - } - -@@ -143,15 +143,12 @@ out: - */ - int main(int argc, char **argv) - { -- char proc_dir_name[32]; - char *target_str; -- pid_t target; - int proc_dir_fd; - int ranges; - struct map_range *mappings; - struct stat st; - struct passwd *pw; -- int written; - bool allow_setgroups = false; - - Prog = Basename (argv[0]); -@@ -168,25 +165,19 @@ int main(int argc, char **argv) - /* Find the process that needs its user namespace - * gid mapping set. - */ -- target_str = argv[1]; -- if (!get_pid(target_str, &target)) -- usage(); - -- /* max string length is 6 + 10 + 1 + 1 = 18, allocate 32 bytes */ -- written = snprintf(proc_dir_name, sizeof(proc_dir_name), "/proc/%u/", -- target); -- if ((written <= 0) || (written >= sizeof(proc_dir_name))) { -- fprintf(stderr, "%s: snprintf of proc path failed: %s\n", -- Prog, strerror(errno)); -- } -- -- proc_dir_fd = open(proc_dir_name, O_DIRECTORY); -- if (proc_dir_fd < 0) { -- fprintf(stderr, _("%s: Could not open proc directory for target %u\n"), -- Prog, target); -- return EXIT_FAILURE; -+ target_str = argv[1]; -+ if (strlen(target_str) > 3 && strncmp(target_str, "fd:", 3) == 0) { -+ /* the user passed in a /proc/pid fd for the process */ -+ target_str = &target_str[3]; -+ proc_dir_fd = get_pidfd_from_fd(target_str); -+ if (proc_dir_fd < 0) -+ usage(); -+ } else { -+ proc_dir_fd = open_pidfd(target_str); -+ if (proc_dir_fd < 0) -+ usage(); - } -- - /* Who am i? */ - pw = get_my_pwent (); - if (NULL == pw) { -@@ -200,8 +191,8 @@ int main(int argc, char **argv) - - /* Get the effective uid and effective gid of the target process */ - if (fstat(proc_dir_fd, &st) < 0) { -- fprintf(stderr, _("%s: Could not stat directory for target %u\n"), -- Prog, target); -+ fprintf(stderr, _("%s: Could not stat directory for process\n"), -+ Prog); - return EXIT_FAILURE; - } - -@@ -213,8 +204,8 @@ int main(int argc, char **argv) - (!getdef_bool("GRANT_AUX_GROUP_SUBIDS") && (getgid() != pw->pw_gid)) || - (pw->pw_uid != st.st_uid) || - (getgid() != st.st_gid)) { -- fprintf(stderr, _( "%s: Target %u is owned by a different user: uid:%lu pw_uid:%lu st_uid:%lu, gid:%lu pw_gid:%lu st_gid:%lu\n" ), -- Prog, target, -+ fprintf(stderr, _( "%s: Target process is owned by a different user: uid:%lu pw_uid:%lu st_uid:%lu, gid:%lu pw_gid:%lu st_gid:%lu\n" ), -+ Prog, - (unsigned long int)getuid(), (unsigned long int)pw->pw_uid, (unsigned long int)st.st_uid, - (unsigned long int)getgid(), (unsigned long int)pw->pw_gid, (unsigned long int)st.st_gid); - return EXIT_FAILURE; -diff --git a/src/newuidmap.c b/src/newuidmap.c -index e8798409..e99655c9 100644 ---- a/src/newuidmap.c -+++ b/src/newuidmap.c -@@ -64,7 +64,7 @@ static void verify_ranges(struct passwd *pw, int ranges, - - static void usage(void) - { -- fprintf(stderr, _("usage: %s [ ] ... \n"), Prog); -+ fprintf(stderr, _("usage: %s [|fd:] [ ] ... \n"), Prog); - exit(EXIT_FAILURE); - } - -@@ -73,15 +73,12 @@ static void usage(void) - */ - int main(int argc, char **argv) - { -- char proc_dir_name[32]; - char *target_str; -- pid_t target; - int proc_dir_fd; - int ranges; - struct map_range *mappings; - struct stat st; - struct passwd *pw; -- int written; - - Prog = Basename (argv[0]); - log_set_progname(Prog); -@@ -94,26 +91,20 @@ int main(int argc, char **argv) - if (argc < 2) - usage(); - -+ target_str = argv[1]; - /* Find the process that needs its user namespace - * uid mapping set. - */ -- target_str = argv[1]; -- if (!get_pid(target_str, &target)) -- usage(); -- -- /* max string length is 6 + 10 + 1 + 1 = 18, allocate 32 bytes */ -- written = snprintf(proc_dir_name, sizeof(proc_dir_name), "/proc/%u/", -- target); -- if ((written <= 0) || (written >= sizeof(proc_dir_name))) { -- fprintf(stderr, "%s: snprintf of proc path failed: %s\n", -- Prog, strerror(errno)); -- } -- -- proc_dir_fd = open(proc_dir_name, O_DIRECTORY); -- if (proc_dir_fd < 0) { -- fprintf(stderr, _("%s: Could not open proc directory for target %u\n"), -- Prog, target); -- return EXIT_FAILURE; -+ if (strlen(target_str) > 3 && strncmp(target_str, "fd:", 3) == 0) { -+ /* the user passed in a /proc/pid fd for the process */ -+ target_str = &target_str[3]; -+ proc_dir_fd = get_pidfd_from_fd(target_str); -+ if (proc_dir_fd < 0) -+ usage(); -+ } else { -+ proc_dir_fd = open_pidfd(target_str); -+ if (proc_dir_fd < 0) -+ usage(); - } - - /* Who am i? */ -@@ -129,8 +120,7 @@ int main(int argc, char **argv) - - /* Get the effective uid and effective gid of the target process */ - if (fstat(proc_dir_fd, &st) < 0) { -- fprintf(stderr, _("%s: Could not stat directory for target %u\n"), -- Prog, target); -+ fprintf(stderr, _("%s: Could not stat directory for target process\n"), Prog); - return EXIT_FAILURE; - } - -@@ -142,8 +132,8 @@ int main(int argc, char **argv) - (!getdef_bool("GRANT_AUX_GROUP_SUBIDS") && (getgid() != pw->pw_gid)) || - (pw->pw_uid != st.st_uid) || - (getgid() != st.st_gid)) { -- fprintf(stderr, _( "%s: Target process %u is owned by a different user: uid:%lu pw_uid:%lu st_uid:%lu, gid:%lu pw_gid:%lu st_gid:%lu\n" ), -- Prog, target, -+ fprintf(stderr, _( "%s: Target process is owned by a different user: uid:%lu pw_uid:%lu st_uid:%lu, gid:%lu pw_gid:%lu st_gid:%lu\n" ), -+ Prog, - (unsigned long int)getuid(), (unsigned long int)pw->pw_uid, (unsigned long int)st.st_uid, - (unsigned long int)getgid(), (unsigned long int)pw->pw_gid, (unsigned long int)st.st_gid); - return EXIT_FAILURE; --- -2.39.2 - -From 7ff33fae6f9cd79c0e012671c37a172e9a681d0b Mon Sep 17 00:00:00 2001 -From: Serge Hallyn -Date: Fri, 24 Feb 2023 13:52:32 -0600 -Subject: [PATCH] get_pidfd_from_fd: return -1 on error, not 0 - -Fixes: 6974df39a: newuidmap and newgidmap: support passing pid as fd -Signed-off-by: Serge Hallyn ---- - lib/get_pid.c | 3 ++- - 1 file changed, 2 insertions(+), 1 deletion(-) - -diff --git a/lib/get_pid.c b/lib/get_pid.c -index ab91d158..5b6d9da4 100644 ---- a/lib/get_pid.c -+++ b/lib/get_pid.c -@@ -35,6 +35,7 @@ int get_pid (const char *pidstr, pid_t *pid) - /* - * If use passed in fd:4 as an argument, then return the - * value '4', the fd to use. -+ * On error, return -1. - */ - int get_pidfd_from_fd(const char *pidfdstr) - { -@@ -47,7 +48,7 @@ int get_pidfd_from_fd(const char *pidfdstr) - || ('\0' != *endptr) - || (ERANGE == errno) - || (/*@+longintegral@*/val != (pid_t)val)/*@=longintegral@*/) { -- return 0; -+ return -1; - } - - return (int)val; --- -2.39.2 - -From 05e2adf509ba0e3779dae66a276b86927a8e1e0e Mon Sep 17 00:00:00 2001 -From: =?UTF-8?q?Vin=C3=ADcius=20dos=20Santos=20Oliveira?= - -Date: Fri, 24 Feb 2023 18:06:02 -0300 -Subject: [PATCH] Validate fds created by the user - -write_mapping() will do the following: - -openat(proc_dir_fd, map_file, O_WRONLY); - -An attacker could create a directory containing a symlink named -"uid_map" pointing to any file owned by root, and thus allow him to -overwrite any root-owned file. ---- - lib/get_pid.c | 17 +++++++++++++++++ - 1 file changed, 17 insertions(+) - -diff --git a/lib/get_pid.c b/lib/get_pid.c -index 5b6d9da4..8e5e6014 100644 ---- a/lib/get_pid.c -+++ b/lib/get_pid.c -@@ -41,6 +41,8 @@ int get_pidfd_from_fd(const char *pidfdstr) - { - long long int val; - char *endptr; -+ struct stat st; -+ dev_t proc_st_dev, proc_st_rdev; - - errno = 0; - val = strtoll (pidfdstr, &endptr, 10); -@@ -51,6 +53,21 @@ int get_pidfd_from_fd(const char *pidfdstr) - return -1; - } - -+ if (stat("/proc/self/uid_map", &st) < 0) { -+ return -1; -+ } -+ -+ proc_st_dev = st.st_dev; -+ proc_st_rdev = st.st_rdev; -+ -+ if (fstat(val, &st) < 0) { -+ return -1; -+ } -+ -+ if (st.st_dev != proc_st_dev || st.st_rdev != proc_st_rdev) { -+ return -1; -+ } -+ - return (int)val; - } - --- -2.39.2 - diff --git a/shadow-utils-configure-gshadow.patch b/shadow-utils-configure-gshadow.patch deleted file mode 100644 index a983ce0..0000000 --- a/shadow-utils-configure-gshadow.patch +++ /dev/null @@ -1,20 +0,0 @@ -The missing #include causes the configure check to fail -spuriously, resulting in HAVE_SHADOWGRP not being defined. - -Submitted upstream: - -diff --git a/configure.ac b/configure.ac -index 924254a0c8171802..6c7d9839979e037d 100644 ---- a/configure.ac -+++ b/configure.ac -@@ -116,6 +116,10 @@ if test "$ac_cv_header_shadow_h" = "yes"; then - ac_cv_libc_shadowgrp, - AC_RUN_IFELSE([AC_LANG_SOURCE([ - #include -+ #ifdef HAVE_GSHADOW_H -+ #include -+ #endif -+ int - main() - { - struct sgrp *sg = sgetsgent("test:x::"); From a85675911953dc0d1a9ecd8c606ea06e73903c22 Mon Sep 17 00:00:00 2001 From: Debarshi Ray Date: Thu, 22 Jan 2026 15:25:57 +0100 Subject: [PATCH 22/30] Silence mixed-use-of-spaces-and-tabs There's no need to do a build just for this. Fallout from 4642a65eb4963253b1abd952aa727fcbb238df53 and fe4f9dc5c0ad3270c280392bb574748f7c49bf81 https://src.fedoraproject.org/rpms/shadow-utils/pull-request/25 --- shadow-utils.spec | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/shadow-utils.spec b/shadow-utils.spec index de8a156..ec8df78 100644 --- a/shadow-utils.spec +++ b/shadow-utils.spec @@ -126,9 +126,9 @@ cp -a %{SOURCE6} man/login.defs.d/HOME_MODE.xml %build autoreconf %configure \ - --disable-account-tools-setuid \ - --enable-logind=no \ - --enable-man \ + --disable-account-tools-setuid \ + --enable-logind=no \ + --enable-man \ --enable-shadowgrp \ --enable-shared \ --with-audit \ @@ -140,7 +140,7 @@ autoreconf --with-yescrypt \ --without-libbsd \ --without-libcrack \ - --without-nscd \ + --without-nscd \ --without-sssd %make_build From 77fbaa4e9a6b97735947d575ade3f167fad09b59 Mon Sep 17 00:00:00 2001 From: Debarshi Ray Date: Thu, 22 Jan 2026 15:21:36 +0100 Subject: [PATCH 23/30] Rebase to version 4.19.3 Resolves: #2426288 https://src.fedoraproject.org/rpms/shadow-utils/pull-request/27 --- .gitignore | 4 + 1520.patch | 40 --------- shadow-4.19.0-chkhash1.patch | 88 ------------------- shadow-4.19.0-usermod-add-optimizations.patch | 75 ---------------- ...hash2.patch => shadow-4.19.3-chkhash.patch | 65 ++++---------- shadow-utils.spec | 27 +++--- sources | 4 +- 7 files changed, 32 insertions(+), 271 deletions(-) delete mode 100644 1520.patch delete mode 100644 shadow-4.19.0-chkhash1.patch delete mode 100644 shadow-4.19.0-usermod-add-optimizations.patch rename shadow-4.19.0-chkhash2.patch => shadow-4.19.3-chkhash.patch (59%) diff --git a/.gitignore b/.gitignore index 0ceb6c8..7db552e 100644 --- a/.gitignore +++ b/.gitignore @@ -44,3 +44,7 @@ shadow-4.1.4.2.tar.bz2 /shadow-4.18.0.tar.xz.asc /shadow-4.19.0.tar.xz /shadow-4.19.0.tar.xz.asc +/shadow-4.19.2.tar.xz +/shadow-4.19.2.tar.xz.asc +/shadow-4.19.3.tar.xz +/shadow-4.19.3.tar.xz.asc diff --git a/1520.patch b/1520.patch deleted file mode 100644 index 069eebb..0000000 --- a/1520.patch +++ /dev/null @@ -1,40 +0,0 @@ -From b721cdd03daa3416127289da02fe781efb2562df Mon Sep 17 00:00:00 2001 -From: Adam Williamson -Date: Tue, 27 Jan 2026 15:11:07 -0800 -Subject: [PATCH] lib/chkhash.c: fix escaping in SHA-256 / SHA-512 / MD5 - regexes - -`\\n` inside square brackets doesn't include or exclude the -newline character. It includes or excludes a literal slash and -the literal character 'n'. - -Fixes: c44f1e096a19 (2025-07-20; "chpasswd: Check hash before write when using -e") -Closes: -Signed-off-by: Adam Williamson ---- - lib/chkhash.c | 6 +++--- - 1 file changed, 3 insertions(+), 3 deletions(-) - -diff --git a/lib/chkhash.c b/lib/chkhash.c -index 101f2004e8..9123038ade 100644 ---- a/lib/chkhash.c -+++ b/lib/chkhash.c -@@ -62,15 +62,15 @@ is_valid_hash(const char *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)) -+ 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)) -+ 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)) -+ if (match_regex("^\\$1\\$[^$:\n]{1,8}\\$[./A-Za-z0-9]{22}$", hash)) - return true; - - // DES: exactly 13 characters from [A-Za-z0-9./] diff --git a/shadow-4.19.0-chkhash1.patch b/shadow-4.19.0-chkhash1.patch deleted file mode 100644 index 986f10b..0000000 --- a/shadow-4.19.0-chkhash1.patch +++ /dev/null @@ -1,88 +0,0 @@ -From 87ec7a52ab25dd6e91253c274fd651f16844cf2a Mon Sep 17 00:00:00 2001 -From: Alejandro Colomar -Date: Wed, 7 Jan 2026 23:39:53 +0100 -Subject: [PATCH 1/2] 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: -Link: -Reported-by: Chris Hofstaedtler -Reviewed-by: Chris Hofstaedtler -Cc: vinz -Signed-off-by: Alejandro Colomar ---- - lib/chkhash.c | 4 ++++ - 1 file changed, 4 insertions(+) - -diff --git a/lib/chkhash.c b/lib/chkhash.c -index 66870500..cb45f3aa 100644 ---- a/lib/chkhash.c -+++ b/lib/chkhash.c -@@ -7,6 +7,8 @@ - #include - #include - -+#include "string/strcmp/strprefix.h" -+ - - /* - * match_regex - return true if match, false if not -@@ -37,6 +39,8 @@ match_regex(const char *pattern, const char *string) - bool - is_valid_hash(const char *hash) - { -+ hash = strprefix(hash, "!") ?: hash; -+ - // Minimum hash length - if (strlen(hash) < 13) - return false; --- -2.52.0 - - -From ddc2549f87e3001f663d5179e9b6d7fe2e1f3b3f Mon Sep 17 00:00:00 2001 -From: Alejandro Colomar -Date: Wed, 7 Jan 2026 23:44:26 +0100 -Subject: [PATCH 2/2] 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: -Closes: -Reported-by: Chris Hofstaedtler -Reviewed-by: Chris Hofstaedtler -Cc: vinz -Signed-off-by: Alejandro Colomar ---- - lib/chkhash.c | 4 ++++ - 1 file changed, 4 insertions(+) - -diff --git a/lib/chkhash.c b/lib/chkhash.c -index cb45f3aa..71e0fb4e 100644 ---- a/lib/chkhash.c -+++ b/lib/chkhash.c -@@ -7,6 +7,7 @@ - #include - #include - -+#include "string/strcmp/streq.h" - #include "string/strcmp/strprefix.h" - - -@@ -41,6 +42,9 @@ is_valid_hash(const char *hash) - { - hash = strprefix(hash, "!") ?: hash; - -+ if (streq(hash, "*")) -+ return true; -+ - // Minimum hash length - if (strlen(hash) < 13) - return false; --- -2.52.0 - diff --git a/shadow-4.19.0-usermod-add-optimizations.patch b/shadow-4.19.0-usermod-add-optimizations.patch deleted file mode 100644 index 4a883e6..0000000 --- a/shadow-4.19.0-usermod-add-optimizations.patch +++ /dev/null @@ -1,75 +0,0 @@ -From 958b4859991e700b61af2f9e07e3aa87ad1d9218 Mon Sep 17 00:00:00 2001 -From: Alejandro Colomar -Date: Sat, 17 Jan 2026 00:56:21 +0100 -Subject: [PATCH] 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: -Reported-by: Adam Williamson -Signed-off-by: Alejandro Colomar ---- - src/usermod.c | 42 ++++++++++++++++++++++++++++++++++++++++++ - 1 file changed, 42 insertions(+) - -diff --git a/src/usermod.c b/src/usermod.c -index 7400bf84..7cd7a859 100644 ---- a/src/usermod.c -+++ b/src/usermod.c -@@ -1391,6 +1391,48 @@ process_flags(int argc, char **argv, struct option_flags *flags) - } - #endif /* WITH_SELINUX */ - -+ if (user_newid == user_id) { -+ uflg = false; -+ oflg = false; -+ } -+ if (user_newgid == user_gid) { -+ gflg = false; -+ } -+ if ( (NULL != user_newshell) -+ && streq(user_newshell, user_shell)) { -+ sflg = false; -+ } -+ if (streq(user_newname, user_name)) { -+ lflg = false; -+ } -+ if (user_newinactive == user_inactive) { -+ fflg = false; -+ } -+ if (user_newexpire == user_expire) { -+ eflg = false; -+ } -+ if ( (NULL != user_newhome) -+ && streq(user_newhome, user_home)) { -+ dflg = false; -+ mflg = false; -+ } -+ if ( (NULL != user_newcomment) -+ && streq(user_newcomment, user_comment)) { -+ cflg = false; -+ } -+ -+ if (!(Uflg || uflg || sflg || pflg || mflg || Lflg || -+ lflg || Gflg || gflg || fflg || eflg || dflg || cflg -+#ifdef ENABLE_SUBIDS -+ || vflg || Vflg || wflg || Wflg -+#endif /* ENABLE_SUBIDS */ -+#ifdef WITH_SELINUX -+ || Zflg -+#endif /* WITH_SELINUX */ -+ )) { -+ exit (E_SUCCESS); -+ } -+ - if (!is_shadow_pwd && (eflg || fflg)) { - fprintf (stderr, - _("%s: shadow passwords required for -e and -f\n"), --- -2.52.0 - diff --git a/shadow-4.19.0-chkhash2.patch b/shadow-4.19.3-chkhash.patch similarity index 59% rename from shadow-4.19.0-chkhash2.patch rename to shadow-4.19.3-chkhash.patch index 1742544..6dfc159 100644 --- a/shadow-4.19.0-chkhash2.patch +++ b/shadow-4.19.3-chkhash.patch @@ -1,57 +1,20 @@ -From 9b67543987e3d140c86f1b8e2b5db5c10d8bc3c5 Mon Sep 17 00:00:00 2001 -From: Alejandro Colomar -Date: Thu, 15 Jan 2026 01:11:52 +0100 -Subject: [PATCH 1/4] 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: -Reported-by: Marc 'Zugschlus' Haber -Reported-by: "Serge E. Hallyn" -Reported-by: Adam Williamson -Co-authored-by: "Serge E. Hallyn" -Signed-off-by: Alejandro Colomar ---- - lib/chkhash.c | 4 ++++ - 1 file changed, 4 insertions(+) - -diff --git a/lib/chkhash.c b/lib/chkhash.c -index 71e0fb4e..d0a8dc01 100644 ---- a/lib/chkhash.c -+++ b/lib/chkhash.c -@@ -42,6 +42,10 @@ is_valid_hash(const char *hash) - { - hash = strprefix(hash, "!") ?: hash; - -+ // Passwordless account; discouraged -+ if (streq(hash, "")) -+ return true; -+ - if (streq(hash, "*")) - return true; - --- -2.52.0 - - -From bce404a7a831de911cf20fc8d4c548957f70bc72 Mon Sep 17 00:00:00 2001 +From 4b2e669441438a6ae91c82a24c796894aa6a53b2 Mon Sep 17 00:00:00 2001 From: Alejandro Colomar Date: Thu, 15 Jan 2026 01:14:46 +0100 -Subject: [PATCH 2/4] lib/chkhash.c: is_valid_hash(): Update comment +Subject: [PATCH 1/3] 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 Signed-off-by: Alejandro Colomar +(cherry picked from commit bce404a7a831de911cf20fc8d4c548957f70bc72) --- lib/chkhash.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/lib/chkhash.c b/lib/chkhash.c -index d0a8dc01..1723f862 100644 +index 348dfe7478f8..48aaa417e9a9 100644 --- a/lib/chkhash.c +++ b/lib/chkhash.c @@ -31,9 +31,7 @@ match_regex(const char *pattern, const char *string) @@ -66,22 +29,23 @@ index d0a8dc01..1723f862 100644 * regex from: https://man.archlinux.org/man/crypt.5.en */ -- -2.52.0 +2.53.0 -From 9a86c515a1e7caeeb9e60fa63871170a2dfd98f0 Mon Sep 17 00:00:00 2001 +From b72386978e0c994c5efc91d548da68a8d97088ab Mon Sep 17 00:00:00 2001 From: Alejandro Colomar Date: Thu, 15 Jan 2026 01:24:10 +0100 -Subject: [PATCH 3/4] lib/chkhash.c: is_valid_hash(): Comment meaning of !hash +Subject: [PATCH 2/3] lib/chkhash.c: is_valid_hash(): Comment meaning of !hash and * Signed-off-by: Alejandro Colomar +(cherry picked from commit 9a86c515a1e7caeeb9e60fa63871170a2dfd98f0) --- lib/chkhash.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/chkhash.c b/lib/chkhash.c -index 1723f862..101f2004 100644 +index 48aaa417e9a9..9123038adef0 100644 --- a/lib/chkhash.c +++ b/lib/chkhash.c @@ -38,12 +38,14 @@ match_regex(const char *pattern, const char *string) @@ -100,21 +64,22 @@ index 1723f862..101f2004 100644 return true; -- -2.52.0 +2.53.0 -From 6be13b2f84a2c1a0d0f4129b5258b4b443e7f86c Mon Sep 17 00:00:00 2001 +From 4486e3e0f8b74e6af27b5dcb4114fd776afdd605 Mon Sep 17 00:00:00 2001 From: Alejandro Colomar Date: Fri, 16 Jan 2026 01:52:32 +0100 -Subject: [PATCH 4/4] man/shadow.5.xml: Document "*" +Subject: [PATCH 3/3] man/shadow.5.xml: Document "*" Signed-off-by: Alejandro Colomar +(cherry picked from commit 6be13b2f84a2c1a0d0f4129b5258b4b443e7f86c) --- man/shadow.5.xml | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/man/shadow.5.xml b/man/shadow.5.xml -index dc7fdfde..d3cc0398 100644 +index dc7fdfde542f..d3cc03982c47 100644 --- a/man/shadow.5.xml +++ b/man/shadow.5.xml @@ -82,8 +82,12 @@ @@ -133,5 +98,5 @@ index dc7fdfde..d3cc0398 100644 Refer to crypt -- -2.52.0 +2.53.0 diff --git a/shadow-utils.spec b/shadow-utils.spec index ec8df78..426f19b 100644 --- a/shadow-utils.spec +++ b/shadow-utils.spec @@ -1,12 +1,12 @@ Summary: Utilities for managing accounts and shadow password files Name: shadow-utils -Version: 4.19.0 -Release: 6%{?dist} +Version: 4.19.3 +Release: 1%{?dist} Epoch: 2 License: BSD-3-Clause AND GPL-2.0-or-later URL: https://github.com/shadow-maint/shadow -Source0: https://github.com/shadow-maint/shadow/releases/download/4.19.0/shadow-4.19.0.tar.xz -Source1: https://github.com/shadow-maint/shadow/releases/download/4.19.0/shadow-4.19.0.tar.xz.asc +Source0: https://github.com/shadow-maint/shadow/releases/download/4.19.3/shadow-4.19.3.tar.xz +Source1: https://github.com/shadow-maint/shadow/releases/download/4.19.3/shadow-4.19.3.tar.xz.asc Source2: shadow-utils.useradd Source3: shadow-utils.login.defs Source4: shadow-bsd.txt @@ -26,17 +26,8 @@ Patch0: shadow-4.15.0-manfix.patch Patch1: shadow-4.19.0-account-tools-setuid.patch # https://github.com/shadow-maint/shadow/commit/3e8c105f0703264e947d8c034b90419794955d49 Patch2: shadow-4-19-useradd-support-btrfs.patch -# https://github.com/shadow-maint/shadow/commit/3e8c105f0703264e947d8c034b90419794955d49 -Patch3: shadow-4.19.0-chkhash1.patch -# https://github.com/shadow-maint/shadow/commit/9b67543987e3d140c86f1b8e2b5db5c10d8bc3c5 -Patch4: shadow-4.19.0-chkhash2.patch -# https://github.com/shadow-maint/shadow/commit/958b4859991e700b61af2f9e07e3aa87ad1d9218 -Patch5: shadow-4.19.0-usermod-add-optimizations.patch -# https://github.com/shadow-maint/shadow/pull/1520 -# Approved by upstream for 4.19.3 this weekend: -# https://github.com/shadow-maint/shadow/issues/1521 -# Fixes the hash check to accept hashes with \ or n in the salt -Patch6: 1520.patch +# https://github.com/shadow-maint/shadow/commit/6be13b2f84a2c1a0d0f4129b5258b4b443e7f86c +Patch3: shadow-4.19.3-chkhash.patch ### Dependencies ### Requires: audit-libs >= 1.6.5 @@ -115,7 +106,7 @@ Requires: shadow-utils-subid = %{epoch}:%{version}-%{release} Development files for shadow-utils-subid. %prep -%autosetup -p 1 -S git -n shadow-4.19.0 +%autosetup -p 1 -S git -n shadow-4.19.3 iconv -f ISO88591 -t utf-8 doc/HOWTO > doc/HOWTO.utf8 cp -f doc/HOWTO.utf8 doc/HOWTO @@ -286,6 +277,10 @@ rm -f $RPM_BUILD_ROOT/%{_libdir}/libsubid.a %{_libdir}/libsubid.so %changelog +* Wed Feb 11 2026 Debarshi Ray - 2:4.19.3-1 +- Rebase to version 4.19.3 + Resolves: #2426288 + * Tue Jan 27 2026 Adam Williamson - 2:4.19.0-6 - chkhash.c: fix escaping in SHA-256 / SHA-512 / MD5 regexes diff --git a/sources b/sources index 22ae907..d12859f 100644 --- a/sources +++ b/sources @@ -1,2 +1,2 @@ -SHA512 (shadow-4.19.0.tar.xz) = 0846c34f426101213ab6277bbcdc58c28b3a54df2f68d1c1addcc8c5aa79ce3e15d5d8dfcb9cc6dbd1b758e0cd8676c68ca65268f5a037d4c24aa7d358c9a390 -SHA512 (shadow-4.19.0.tar.xz.asc) = 34c1a764d06a5d24533cbb57310de7462eb632c1de904901699d48f44401b1b1f4751db35f2b0e2b9a057cf1a777ccddbae4bc6b19a6db865ebc3e30fde0d060 +SHA512 (shadow-4.19.3.tar.xz) = f2b41f8186681eb98dae0da87ed1a1f03327c5005c9d4bf9500801352e318de6f5954dc5b8f30ab9aece728b1efb74b4ff8fb2720acc4c2aaef701331a4a5a05 +SHA512 (shadow-4.19.3.tar.xz.asc) = 295a6bffefd333a9ea84fa59b368f5c48d0ac5224199624b768087098110b0f0c19bef37f5c474c45277980758dcb6728e8bb17eb7ab1a57603cad41f1dd00ab From 0fba1e26ce07e5cb003b594266f8b34fdb3477f6 Mon Sep 17 00:00:00 2001 From: Iker Pedrosa Date: Thu, 23 Apr 2026 15:20:03 +0200 Subject: [PATCH 24/30] btrfs: simplify checks improve useradd behavior for non-btrfs Signed-off-by: Iker Pedrosa --- shadow-4.19.3-useradd-fix-btrfs.patch | 492 ++++++++++++++++++++++++++ shadow-utils.spec | 7 +- 2 files changed, 498 insertions(+), 1 deletion(-) create mode 100644 shadow-4.19.3-useradd-fix-btrfs.patch diff --git a/shadow-4.19.3-useradd-fix-btrfs.patch b/shadow-4.19.3-useradd-fix-btrfs.patch new file mode 100644 index 0000000..47671d9 --- /dev/null +++ b/shadow-4.19.3-useradd-fix-btrfs.patch @@ -0,0 +1,492 @@ +From 4079070c4b8ced8df9d86366ad113fc3c2e4e49c Mon Sep 17 00:00:00 2001 +From: Alejandro Colomar +Date: Wed, 22 Apr 2026 11:02:36 +0200 +Subject: [PATCH 1/5] lib/btrfs.c: btrfs_is_subvolume(): Simplify error check + +Signed-off-by: Alejandro Colomar +--- + lib/btrfs.c | 3 +-- + 1 file changed, 1 insertion(+), 2 deletions(-) + +diff --git a/lib/btrfs.c b/lib/btrfs.c +index 85ec7dbc..fe20cf30 100644 +--- a/lib/btrfs.c ++++ b/lib/btrfs.c +@@ -67,8 +67,7 @@ int btrfs_is_subvolume(const char *path) + if (ret <= 0) + return ret; + +- ret = stat(path, &st); +- if (ret == -1) ++ if (stat(path, &st) == -1) + return -1; + + if (st.st_ino != BTRFS_FIRST_FREE_OBJECTID || !S_ISDIR(st.st_mode)) { +-- +2.53.0 + + +From 7a4faaaf46314547cc5e6c8264c3a11753ef9352 Mon Sep 17 00:00:00 2001 +From: Alejandro Colomar +Date: Wed, 22 Apr 2026 11:14:20 +0200 +Subject: [PATCH 2/5] lib/, src/: Move statfs(2) call out of is_btrfs() + +This simplifies the return value of is_btrfs() into a boolean. + +Signed-off-by: Alejandro Colomar +--- + lib/btrfs.c | 25 +++++++++---------------- + lib/prototypes.h | 3 ++- + src/useradd.c | 11 +++++++++-- + 3 files changed, 20 insertions(+), 19 deletions(-) + +diff --git a/lib/btrfs.c b/lib/btrfs.c +index fe20cf30..065c7938 100644 +--- a/lib/btrfs.c ++++ b/lib/btrfs.c +@@ -60,12 +60,13 @@ int btrfs_remove_subvolume(const char *path) + */ + int btrfs_is_subvolume(const char *path) + { +- struct stat st; +- int ret; ++ struct stat st; ++ struct statfs sfs; + +- ret = is_btrfs(path); +- if (ret <= 0) +- return ret; ++ if (statfs(path, &sfs) == -1) ++ return -1; ++ if (!is_btrfs(&sfs)) ++ return 0; + + if (stat(path, &st) == -1) + return -1; +@@ -78,16 +79,8 @@ int btrfs_is_subvolume(const char *path) + } + + +-/* Adapted from btrfsprogs */ +-int is_btrfs(const char *path) ++bool ++is_btrfs(const struct statfs *sfs) + { +- struct statfs sfs; +- int ret; +- +- ret = statfs(path, &sfs); +- if (ret == -1) +- return -1; +- +- return sfs.f_type == BTRFS_SUPER_MAGIC; ++ return sfs->f_type == BTRFS_SUPER_MAGIC; + } +- +diff --git a/lib/prototypes.h b/lib/prototypes.h +index 9a03e312..42aa2923 100644 +--- a/lib/prototypes.h ++++ b/lib/prototypes.h +@@ -23,6 +23,7 @@ + + #include + #include ++#include + #include + #include + #include +@@ -54,7 +55,7 @@ extern int isexpired (const struct passwd *, /*@null@*/const struct spwd *); + extern int btrfs_create_subvolume(const char *path); + extern int btrfs_remove_subvolume(const char *path); + extern int btrfs_is_subvolume(const char *path); +-extern int is_btrfs(const char *path); ++extern bool is_btrfs(const struct statfs *sfs); + #endif + + /* basename() renamed to Basename() to avoid libc name space confusion */ +diff --git a/src/useradd.c b/src/useradd.c +index df679d28..5ce6cd6a 100644 +--- a/src/useradd.c ++++ b/src/useradd.c +@@ -25,6 +25,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -2265,6 +2266,7 @@ static void create_home(const struct option_flags *flags) + #if WITH_BTRFS + if (subvolflg && (strlen(prefix_user_home) - (int)strlen(path)) <= 1) { + char *btrfs_check = strdup(path); ++ struct statfs sfs; + + if (!btrfs_check) { + fprintf(stderr, +@@ -2273,13 +2275,18 @@ static void create_home(const struct option_flags *flags) + fail_exit(E_HOMEDIR, process_selinux); + } + stpcpy(&btrfs_check[strlen(path) - strlen(cp) - 1], ""); +- if (is_btrfs(btrfs_check) <= 0) { ++ if (statfs(btrfs_check, &sfs) == -1) { ++ fprintf(stderr, "%s: statfs(\"%s\"): %s\n", ++ Prog, btrfs_check, strerrno()); ++ fail_exit(E_HOMEDIR, process_selinux); ++ } ++ free(btrfs_check); ++ if (!is_btrfs(&sfs)) { + fprintf(stderr, + _("%s: home directory \"%s\" must be mounted on BTRFS\n"), + Prog, path); + fail_exit(E_HOMEDIR, process_selinux); + } +- free(btrfs_check); + // make subvolume to mount for user instead of directory + if (btrfs_create_subvolume(path)) { + fprintf(stderr, +-- +2.53.0 + + +From 96460c7240264c0022cebc9ba1774993ee2594cf Mon Sep 17 00:00:00 2001 +From: Alejandro Colomar +Date: Wed, 22 Apr 2026 11:21:14 +0200 +Subject: [PATCH 3/5] lib/, src/: Move btrfs.c prototypes to lib/btrfs.h + +Signed-off-by: Alejandro Colomar +--- + lib/Makefile.am | 2 +- + lib/btrfs.c | 4 ++++ + lib/btrfs.h | 23 +++++++++++++++++++++++ + lib/prototypes.h | 9 --------- + src/useradd.c | 1 + + src/userdel.c | 1 + + src/usermod.c | 1 + + 7 files changed, 31 insertions(+), 10 deletions(-) + create mode 100644 lib/btrfs.h + +diff --git a/lib/Makefile.am b/lib/Makefile.am +index 7c488f82..0664e610 100644 +--- a/lib/Makefile.am ++++ b/lib/Makefile.am +@@ -290,7 +290,7 @@ libshadow_la_SOURCES += tcbfuncs.c tcbfuncs.h + endif + + if WITH_BTRFS +-libshadow_la_SOURCES += btrfs.c ++libshadow_la_SOURCES += btrfs.c btrfs.h + endif + + if ENABLE_LASTLOG +diff --git a/lib/btrfs.c b/lib/btrfs.c +index 065c7938..15b6b315 100644 +--- a/lib/btrfs.c ++++ b/lib/btrfs.c +@@ -1,3 +1,7 @@ ++#include "config.h" ++ ++#include "btrfs.h" ++ + #include + #include + #include +diff --git a/lib/btrfs.h b/lib/btrfs.h +new file mode 100644 +index 00000000..50b70488 +--- /dev/null ++++ b/lib/btrfs.h +@@ -0,0 +1,23 @@ ++// SPDX-FileCopyrightText: 2026, Alejandro Colomar ++// SPDX-License-Identifier: BSD-3-Clause ++ ++ ++#ifndef SHADOW_INCLUDE_LIB_BTRFS_H_ ++#define SHADOW_INCLUDE_LIB_BTRFS_H_ ++ ++ ++#include "config.h" ++ ++#include ++#include ++ ++ ++#ifdef WITH_BTRFS ++int btrfs_create_subvolume(const char *path); ++int btrfs_remove_subvolume(const char *path); ++int btrfs_is_subvolume(const char *path); ++bool is_btrfs(const struct statfs *sfs); ++#endif ++ ++ ++#endif // include guard +diff --git a/lib/prototypes.h b/lib/prototypes.h +index 42aa2923..d22c477a 100644 +--- a/lib/prototypes.h ++++ b/lib/prototypes.h +@@ -23,7 +23,6 @@ + + #include + #include +-#include + #include + #include + #include +@@ -50,14 +49,6 @@ extern int expire (const struct passwd *, /*@null@*/const struct spwd *); + /* isexpired.c */ + extern int isexpired (const struct passwd *, /*@null@*/const struct spwd *); + +-/* btrfs.c */ +-#ifdef WITH_BTRFS +-extern int btrfs_create_subvolume(const char *path); +-extern int btrfs_remove_subvolume(const char *path); +-extern int btrfs_is_subvolume(const char *path); +-extern bool is_btrfs(const struct statfs *sfs); +-#endif +- + /* basename() renamed to Basename() to avoid libc name space confusion */ + /* basename.c */ + extern /*@observer@*/const char *Basename (const char *str); +diff --git a/src/useradd.c b/src/useradd.c +index 5ce6cd6a..9bd32b11 100644 +--- a/src/useradd.c ++++ b/src/useradd.c +@@ -34,6 +34,7 @@ + #include "alloc/malloc.h" + #include "atoi/a2i.h" + #include "atoi/getnum.h" ++#include "btrfs.h" + #include "chkname.h" + #include "defines.h" + #include "faillog.h" +diff --git a/src/userdel.c b/src/userdel.c +index 9b7d81be..ebd064c0 100644 +--- a/src/userdel.c ++++ b/src/userdel.c +@@ -20,6 +20,7 @@ + #include + #include + ++#include "btrfs.h" + #ifdef ACCT_TOOLS_SETUID + #ifdef USE_PAM + #include "pam_defs.h" +diff --git a/src/usermod.c b/src/usermod.c +index 7c0321a6..3afe19ea 100644 +--- a/src/usermod.c ++++ b/src/usermod.c +@@ -32,6 +32,7 @@ + #include "alloc/malloc.h" + #include "atoi/a2i.h" + #include "atoi/getnum.h" ++#include "btrfs.h" + #include "chkname.h" + #include "defines.h" + #include "faillog.h" +-- +2.53.0 + + +From 7be9a6540f1cad477fd4f4e83d25904a5095bb86 Mon Sep 17 00:00:00 2001 +From: Hadi Chokr +Date: Tue, 21 Apr 2026 09:18:19 +0200 +Subject: [PATCH 4/5] useradd(8): fallback to regular dir for BTRFS home on + non-BTRFS parent + +When the --btrfs-subvolume-home option is used but the parent directory +is not on a BTRFS filesystem, useradd previously failed with an error. +This is too strict; instead, fall back to creating a regular directory +and issue a warning. The subvolume creation is attempted only when the +parent is BTRFS. Otherwise, a regular directory is created and a +syslog(3) warning is logged. + +Fixes: 3e8c105 (2026-01-02; "src/useradd: Support config for creating home dirs as Btrfs subvolumes") +Co-authored-by: Hadi Chokr +Co-authored-by: Alejandro Colomar +Signed-off-by: Alejandro Colomar +--- + man/useradd.8.xml | 27 ++++++++++++++++++++++-- + src/useradd.c | 52 +++++++++++++++++++++++------------------------ + 2 files changed, 51 insertions(+), 28 deletions(-) + +diff --git a/man/useradd.8.xml b/man/useradd.8.xml +index 8a087e13..0bde6683 100644 +--- a/man/useradd.8.xml ++++ b/man/useradd.8.xml +@@ -2,6 +2,7 @@ + + + +- Note: this feature works only if the underlying filesystem supports +- Btrfs subvolumes. ++ If the parent directory of the user's home directory is ++ not on a Btrfs filesystem, ++ useradd will not create a ++ subvolume. ++ Instead, it creates a regular directory, ++ prints a warning to standard error, ++ and logs the event via syslog at level ++ LOG_WARN. ++ The user account is still created successfully. ++ ++ ++ If the filesystem type cannot be determined (e.g., because of ++ insufficient permissions, an I/O error, ++ or a ++ ++ statfs ++ 2 ++ ++ failure), ++ useradd treats this as a fatal error: ++ the home directory is not created, ++ the command exits with a non‑zero status ++ (E_HOMEDIR, 12), ++ and an error message is printed. + + + +diff --git a/src/useradd.c b/src/useradd.c +index 9bd32b11..9210379c 100644 +--- a/src/useradd.c ++++ b/src/useradd.c +@@ -1,11 +1,11 @@ +-/* +- * SPDX-FileCopyrightText: 1991 - 1994, Julianne Frances Haugh +- * SPDX-FileCopyrightText: 1996 - 2000, Marek Michałkiewicz +- * SPDX-FileCopyrightText: 2000 - 2006, Tomasz Kłoczko +- * SPDX-FileCopyrightText: 2007 - 2012, Nicolas François +- * +- * SPDX-License-Identifier: BSD-3-Clause +- */ ++// SPDX-FileCopyrightText: 1991-1994, Julianne Frances Haugh ++// SPDX-FileCopyrightText: 1996-2000, Marek Michałkiewicz ++// SPDX-FileCopyrightText: 2000-2006, Tomasz Kłoczko ++// SPDX-FileCopyrightText: 2007-2012, Nicolas François ++// SPDX-FileCopyrightText: 2025-2026, Hadi Chokr ++// SPDX-FileCopyrightText: 2026, Alejandro Colomar ++// SPDX-License-Identifier: BSD-3-Clause ++ + + #include "config.h" + +@@ -2251,6 +2251,8 @@ static void create_home(const struct option_flags *flags) + owner root:root. + */ + for (cp = strtok(bhome, "/"); cp != NULL; cp = strtok(NULL, "/")) { ++ bool dir_created; ++ + /* Avoid turning a relative path into an absolute path. */ + if (strprefix(bhome, "/") || !streq(path, "")) + strcat(path, "/"); +@@ -2260,10 +2262,7 @@ static void create_home(const struct option_flags *flags) + continue; + } + +- /* Check if parent directory is BTRFS, fail if requesting +- subvolume but no BTRFS. The paths could be different by the +- trailing slash +- */ ++ dir_created = false; + #if WITH_BTRFS + if (subvolflg && (strlen(prefix_user_home) - (int)strlen(path)) <= 1) { + char *btrfs_check = strdup(path); +@@ -2284,25 +2283,26 @@ static void create_home(const struct option_flags *flags) + free(btrfs_check); + if (!is_btrfs(&sfs)) { + fprintf(stderr, +- _("%s: home directory \"%s\" must be mounted on BTRFS\n"), +- Prog, path); +- fail_exit(E_HOMEDIR, process_selinux); ++ _("%s: warning: \"%s\" is not on BTRFS; creating regular directory instead of subvolume\n"), ++ Prog, prefix_user_home); ++ } else { ++ if (btrfs_create_subvolume(path)) { ++ fprintf(stderr, ++ _("%s: failed to create BTRFS subvolume: %s\n"), ++ Prog, path); ++ fail_exit(E_HOMEDIR, process_selinux); ++ } ++ dir_created = true; + } +- // make subvolume to mount for user instead of directory +- if (btrfs_create_subvolume(path)) { +- fprintf(stderr, +- _("%s: failed to create BTRFS subvolume: %s\n"), ++ } ++#endif ++ if (!dir_created) { ++ if (mkdir(path, 0) != 0) { ++ fprintf(stderr, _("%s: cannot create directory %s\n"), + Prog, path); + fail_exit(E_HOMEDIR, process_selinux); + } + } +- else +-#endif +- if (mkdir(path, 0) != 0) { +- fprintf(stderr, _("%s: cannot create directory %s\n"), +- Prog, path); +- fail_exit(E_HOMEDIR, process_selinux); +- } + if (chown(path, 0, 0) < 0) { + fprintf(stderr, + _("%s: warning: chown on `%s' failed: %m\n"), +-- +2.53.0 + + +From 827f69b864461ab6d7549762bef06ab4495d2587 Mon Sep 17 00:00:00 2001 +From: Hadi Chokr +Date: Mon, 20 Apr 2026 12:27:31 +0200 +Subject: [PATCH 5/5] man/useradd.8.xml: Remove trailing spaces from + useradd.8.xml + +Signed-off-by: Hadi Chokr +Reviewed-by: Alejandro Colomar +--- + man/useradd.8.xml | 6 +++--- + 1 file changed, 3 insertions(+), 3 deletions(-) + +diff --git a/man/useradd.8.xml b/man/useradd.8.xml +index 0bde6683..f85780e2 100644 +--- a/man/useradd.8.xml ++++ b/man/useradd.8.xml +@@ -207,7 +207,7 @@ + user's login directory. The default is to append the + LOGIN name to + BASE_DIR and use that as the +- login directory name. ++ login directory name. + The directory HOME_DIR is not created by + default. However it will be created for non-system users if either the + flag is specifed or +@@ -422,7 +422,7 @@ + + + +- Create the user's home directory if it does not exist. ++ Create the user's home directory if it does not exist. + The files and directories contained in the skeleton directory + (which can be defined with the option) + will be copied to the home directory. +@@ -512,7 +512,7 @@ + password himself. + + +- Note:Avoid this option on the command ++ Note:Avoid this option on the command + line because the password (or encrypted password) will + be visible by users listing the processes. + +-- +2.53.0 + diff --git a/shadow-utils.spec b/shadow-utils.spec index 426f19b..d381c31 100644 --- a/shadow-utils.spec +++ b/shadow-utils.spec @@ -1,7 +1,7 @@ Summary: Utilities for managing accounts and shadow password files Name: shadow-utils Version: 4.19.3 -Release: 1%{?dist} +Release: 2%{?dist} Epoch: 2 License: BSD-3-Clause AND GPL-2.0-or-later URL: https://github.com/shadow-maint/shadow @@ -28,6 +28,8 @@ Patch1: shadow-4.19.0-account-tools-setuid.patch Patch2: shadow-4-19-useradd-support-btrfs.patch # https://github.com/shadow-maint/shadow/commit/6be13b2f84a2c1a0d0f4129b5258b4b443e7f86c Patch3: shadow-4.19.3-chkhash.patch +# https://github.com/shadow-maint/shadow/commit/827f69b864461ab6d7549762bef06ab4495d2587 +Patch4: shadow-4.19.3-useradd-fix-btrfs.patch ### Dependencies ### Requires: audit-libs >= 1.6.5 @@ -277,6 +279,9 @@ rm -f $RPM_BUILD_ROOT/%{_libdir}/libsubid.a %{_libdir}/libsubid.so %changelog +* Thu Apr 23 2026 Iker Pedrosa - 2:4.19.3-2 +- btrfs: simplify checks improve useradd behavior for non-btrfs + * Wed Feb 11 2026 Debarshi Ray - 2:4.19.3-1 - Rebase to version 4.19.3 Resolves: #2426288 From 4a56d06039633e31494bf759e96673dd632925a7 Mon Sep 17 00:00:00 2001 From: Iker Pedrosa Date: Tue, 14 Apr 2026 16:25:41 +0200 Subject: [PATCH 25/30] Tests: run system tests Tests comprehensive functionality from shadow using the upstream system test suite in testing farm. - Add plans/system-tests.fmf: run upstream shadow system tests via pytest-mh. - Add tests/mhc-fedora-ci.yaml: topology configuration to connect to the test system. - Add .fmf/version: enable testing farm integration. Signed-off-by: Iker Pedrosa --- .fmf/version | 1 + plans/tier0-functional.fmf | 48 ++++++++++++++++++++++++++++++++++++++ tests/mhc-fedora-ci.yaml | 13 +++++++++++ 3 files changed, 62 insertions(+) create mode 100644 .fmf/version create mode 100644 plans/tier0-functional.fmf create mode 100644 tests/mhc-fedora-ci.yaml diff --git a/.fmf/version b/.fmf/version new file mode 100644 index 0000000..d00491f --- /dev/null +++ b/.fmf/version @@ -0,0 +1 @@ +1 diff --git a/plans/tier0-functional.fmf b/plans/tier0-functional.fmf new file mode 100644 index 0000000..7fb6f9c --- /dev/null +++ b/plans/tier0-functional.fmf @@ -0,0 +1,48 @@ +summary: Tier 0 functional tests for shadow-utils +description: | + Run comprehensive system tests for shadow-utils. These tests validate user and group + account management functionality by testing actual system operations including user + creation, password management, group operations, and verification of system files + (i.e. /etc/passwd, /etc/shadow). + +provision: + how: virtual + image: fedora + +prepare: + - name: Install general dependencies + how: install + package: + - expect + - git + - python3-pip + + - name: Setup SSH keys for localhost testing + how: shell + script: + - ssh-keygen -t rsa -f /root/.ssh/id_rsa -N "" -q + - cat /root/.ssh/id_rsa.pub >> /root/.ssh/authorized_keys + - chmod 600 /root/.ssh/authorized_keys + - ssh-keyscan -H localhost >> /root/.ssh/known_hosts + + - name: Clone shadow repository + how: shell + script: + - git clone https://github.com/shadow-maint/shadow.git /tmp/shadow-test + + - name: Copy test topology for Fedora CI + how: shell + script: + - cp tests/mhc-fedora-ci.yaml /tmp/shadow-test/tests/system + + - name: Install test dependencies + how: shell + script: + - pip3 install -r /tmp/shadow-test/tests/system/requirements.txt + +execute: + how: tmt + duration: 30m + script: | + cd /tmp/shadow-test/tests/system + pytest --mh-config=mhc-fedora-ci.yaml --mh-lazy-ssh -v diff --git a/tests/mhc-fedora-ci.yaml b/tests/mhc-fedora-ci.yaml new file mode 100644 index 0000000..8747e67 --- /dev/null +++ b/tests/mhc-fedora-ci.yaml @@ -0,0 +1,13 @@ +provisioned_topologies: +- shadow +domains: +- id: shadow + hosts: + - hostname: localhost + role: shadow + conn: + type: ssh + host: localhost + user: root + artifacts: + - /var/log/* From 871df3223d7cf041a69d1b19bb9d1fdf4d3220b9 Mon Sep 17 00:00:00 2001 From: Iker Pedrosa Date: Wed, 27 May 2026 11:40:27 +0200 Subject: [PATCH 26/30] Enable use of PAM for chpasswd and newusers Resolves: #2461179 and #2283963 Signed-off-by: Iker Pedrosa --- shadow-4.19.0-account-tools-setuid.patch | 377 ----------------------- shadow-utils.spec | 14 +- 2 files changed, 9 insertions(+), 382 deletions(-) delete mode 100644 shadow-4.19.0-account-tools-setuid.patch diff --git a/shadow-4.19.0-account-tools-setuid.patch b/shadow-4.19.0-account-tools-setuid.patch deleted file mode 100644 index 9452582..0000000 --- a/shadow-4.19.0-account-tools-setuid.patch +++ /dev/null @@ -1,377 +0,0 @@ -diff -up shadow-4.19.0-rc1/src/chpasswd.c.account-tools-setuid shadow-4.19.0-rc1/src/chpasswd.c ---- shadow-4.19.0-rc1/src/chpasswd.c.account-tools-setuid 2025-12-07 16:56:38.563285789 +0100 -+++ shadow-4.19.0-rc1/src/chpasswd.c 2025-12-09 14:54:29.232282499 +0100 -@@ -466,9 +466,11 @@ int main (int argc, char **argv) - char *newpwd; - const char *salt; - -+#ifdef ACCT_TOOLS_SETUID - #ifdef USE_PAM - bool use_pam = true; - #endif /* USE_PAM */ -+#endif /* ACCT_TOOLS_SETUID */ - - bool errors = false; - intmax_t line = 0; -@@ -495,19 +497,23 @@ int main (int argc, char **argv) - process_root_flag ("-R", argc, argv); - prefix = process_prefix_flag ("-P", argc, argv); - -+#ifdef ACCT_TOOLS_SETUID - #ifdef USE_PAM - if (md5flg || eflg || cflg || prefix[0]) { - use_pam = false; - } - #endif /* USE_PAM */ -+#endif /* ACCT_TOOLS_SETUID */ - - OPENLOG (Prog); - - check_perms (); - -+#ifdef ACCT_TOOLS_SETUID - #ifdef USE_PAM - if (!use_pam) - #endif /* USE_PAM */ -+#endif /* ACCT_TOOLS_SETUID */ - { - is_shadow_pwd = spw_file_present (); - -@@ -562,6 +568,7 @@ int main (int argc, char **argv) - } - newpwd = cp; - -+#ifdef ACCT_TOOLS_SETUID - #ifdef USE_PAM - if (use_pam) { - if (do_pam_passwd_non_interactive (Prog, name, newpwd) != 0) { -@@ -572,6 +579,7 @@ int main (int argc, char **argv) - } - } else - #endif /* USE_PAM */ -+#endif /* ACCT_TOOLS_SETUID */ - { - - /* -@@ -706,9 +714,11 @@ int main (int argc, char **argv) - * password database. - */ - if (errors) { -+#ifdef ACCT_TOOLS_SETUID - #ifdef USE_PAM - if (!use_pam) - #endif /* USE_PAM */ -+#endif /* ACCT_TOOLS_SETUID */ - { - fprintf (stderr, - _("%s: error detected, changes ignored\n"), -@@ -717,9 +727,11 @@ int main (int argc, char **argv) - fail_exit (1, process_selinux); - } - -+#ifdef ACCT_TOOLS_SETUID - #ifdef USE_PAM - if (!use_pam) - #endif /* USE_PAM */ -+#endif /* ACCT_TOOLS_SETUID */ - { - /* Save the changes */ - close_files (&flags); -diff -up shadow-4.19.0-rc1/src/groupmems.c.account-tools-setuid shadow-4.19.0-rc1/src/groupmems.c ---- shadow-4.19.0-rc1/src/groupmems.c.account-tools-setuid 2025-12-07 16:56:38.564285805 +0100 -+++ shadow-4.19.0-rc1/src/groupmems.c 2025-12-09 14:57:23.767054141 +0100 -@@ -14,9 +14,11 @@ - #include - #include - #include -+#ifdef ACCT_TOOLS_SETUID - #ifdef USE_PAM - #include "pam_defs.h" - #endif /* USE_PAM */ -+#endif /* ACCT_TOOLS_SETUID */ - #include - - #include "alloc/malloc.h" -@@ -432,6 +434,7 @@ static void - check_perms(MAYBE_UNUSED bool process_selinux) - { - if (!list) { -+#ifdef ACCT_TOOLS_SETUID - #ifdef USE_PAM - pam_handle_t *pamh = NULL; - int retval; -@@ -465,7 +468,8 @@ check_perms(MAYBE_UNUSED bool process_se - fail_exit (1, process_selinux); - } - (void) pam_end (pamh, retval); --#endif -+#endif /* USE_PAM */ -+#endif /* ACCT_TOOLS_SETUID */ - } - } - -diff -up shadow-4.19.0-rc1/src/newusers.c.account-tools-setuid shadow-4.19.0-rc1/src/newusers.c ---- shadow-4.19.0-rc1/src/newusers.c.account-tools-setuid 2025-12-07 16:56:38.565285821 +0100 -+++ shadow-4.19.0-rc1/src/newusers.c 2025-12-09 15:07:40.115500988 +0100 -@@ -74,6 +74,7 @@ struct option_flags { - static const char Prog[] = "newusers"; - - static bool rflg = false; /* create a system account */ -+#ifndef ACCT_TOOLS_SETUID - #ifndef USE_PAM - static /*@null@*//*@observer@*/char *crypt_method = NULL; - #define cflg (NULL != crypt_method) -@@ -90,6 +91,7 @@ static long bcrypt_rounds = 13; - static long yescrypt_cost = 5; - #endif /* USE_YESCRYPT */ - #endif /* !USE_PAM */ -+#endif /* !ACCT_TOOLS_SETUID */ - - static bool is_shadow; - #ifdef SHADOWGRP -@@ -112,9 +114,11 @@ NORETURN static void fail_exit (int, boo - static int add_group (const char *, const char *, gid_t *, gid_t); - static int get_user_id (const char *, uid_t *); - static int add_user (const char *, uid_t, gid_t); -+#ifndef ACCT_TOOLS_SETUID - #ifndef USE_PAM - static int update_passwd (struct passwd *, const char *); - #endif /* !USE_PAM */ -+#endif /* !ACCT_TOOLS_SETUID */ - static int add_passwd (struct passwd *, const char *); - static void process_flags (int argc, char **argv, struct option_flags *flags); - static void check_flags (void); -@@ -136,6 +140,7 @@ static void usage (int status) - "Options:\n"), - Prog); - (void) fputs (_(" -b, --badname allow bad names\n"), usageout); -+#ifndef ACCT_TOOLS_SETUID - #ifndef USE_PAM - (void) fprintf (usageout, - _(" -c, --crypt-method METHOD the crypt method (one of %s)\n"), -@@ -151,9 +156,11 @@ static void usage (int status) - #endif - ); - #endif /* !USE_PAM */ -+#endif /* !ACCT_TOOLS_SETUID */ - (void) fputs (_(" -h, --help display this help message and exit\n"), usageout); - (void) fputs (_(" -r, --system create system accounts\n"), usageout); - (void) fputs (_(" -R, --root CHROOT_DIR directory to chroot into\n"), usageout); -+#ifndef ACCT_TOOLS_SETUID - #ifndef USE_PAM - #if defined(USE_SHA_CRYPT) || defined(USE_BCRYPT) || defined(USE_YESCRYPT) - (void) fputs (_(" -s, --sha-rounds number of rounds for the SHA, BCRYPT\n" -@@ -161,6 +168,7 @@ static void usage (int status) - usageout); - #endif /* USE_SHA_CRYPT || USE_BCRYPT || USE_YESCRYPT */ - #endif /* !USE_PAM */ -+#endif /* !ACCT_TOOLS_SETUID */ - (void) fputs ("\n", usageout); - - exit (status); -@@ -426,6 +434,7 @@ static int add_user (const char *name, u - return (pw_update (&pwent) == 0) ? -1 : 0; - } - -+#ifndef ACCT_TOOLS_SETUID - #ifndef USE_PAM - /* - * update_passwd - update the password in the passwd entry -@@ -478,6 +487,7 @@ static int update_passwd (struct passwd - return 0; - } - #endif /* !USE_PAM */ -+#endif /* !ACCT_TOOLS_SETUID */ - - /* - * add_passwd - add or update the encrypted password -@@ -487,10 +497,13 @@ add_passwd(struct passwd *pwd, MAYBE_UNU - { - const struct spwd *sp; - struct spwd spent; -+#ifndef ACCT_TOOLS_SETUID - #ifndef USE_PAM - char *cp; - #endif /* !USE_PAM */ -+#endif /* !ACCT_TOOLS_SETUID */ - -+#ifndef ACCT_TOOLS_SETUID - #ifndef USE_PAM - void *crypt_arg = NULL; - if (NULL != crypt_method) { -@@ -528,13 +541,14 @@ add_passwd(struct passwd *pwd, MAYBE_UNU - return update_passwd (pwd, password); - } - #endif /* USE_PAM */ -+#endif /* !ACCT_TOOLS_SETUID */ - - /* - * Do the first and easiest shadow file case. The user already - * exists in the shadow password file. - */ - sp = spw_locate (pwd->pw_name); --#ifndef USE_PAM -+#if !defined(ACCT_TOOLS_SETUID) && !defined(USE_PAM) - if (NULL != sp) { - spent = *sp; - if ( (NULL != crypt_method) -@@ -571,7 +585,7 @@ add_passwd(struct passwd *pwd, MAYBE_UNU - if (!streq(pwd->pw_passwd, "x")) { - return update_passwd (pwd, password); - } --#else /* USE_PAM */ -+#else /* !ACCT_TOOLS_SETUID && !USE_PAM */ - /* - * If there is already a shadow entry, do not touch it. - * If there is already a passwd entry with a password, do not -@@ -582,14 +596,14 @@ add_passwd(struct passwd *pwd, MAYBE_UNU - || !streq(pwd->pw_passwd, "x")) { - return 0; - } --#endif /* USE_PAM */ -+#endif /* !ACCT_TOOLS_SETUID && !USE_PAM */ - - /* - * Now the really hard case - I need to create an entirely new - * shadow password file entry. - */ - spent.sp_namp = pwd->pw_name; --#ifndef USE_PAM -+#if !defined(ACCT_TOOLS_SETUID) && !defined(USE_PAM) - if ((crypt_method != NULL) && streq(crypt_method, "NONE")) { - spent.sp_pwdp = (char *)password; - } else { -@@ -647,32 +647,38 @@ add_passwd(struct passwd *pwd, MAYBE_UNU - static void process_flags (int argc, char **argv, struct option_flags *flags) - { - int c; -+#ifndef ACCT_TOOLS_SETUID - #ifndef USE_PAM - #if defined(USE_SHA_CRYPT) || defined(USE_BCRYPT) || defined(USE_YESCRYPT) - int bad_s; - #endif /* USE_SHA_CRYPT || USE_BCRYPT || USE_YESCRYPT */ - #endif /* !USE_PAM */ -+#endif /* !ACCT_TOOLS_SETUID */ - static struct option long_options[] = { - {"badname", no_argument, NULL, 'b'}, -+#ifndef ACCT_TOOLS_SETUID - #ifndef USE_PAM - {"crypt-method", required_argument, NULL, 'c'}, - #endif /* !USE_PAM */ -+#endif /* !ACCT_TOOLS_SETUID */ - {"help", no_argument, NULL, 'h'}, - {"system", no_argument, NULL, 'r'}, - {"root", required_argument, NULL, 'R'}, -+#ifndef ACCT_TOOLS_SETUID - #ifndef USE_PAM - #if defined(USE_SHA_CRYPT) || defined(USE_BCRYPT) || defined(USE_YESCRYPT) - {"sha-rounds", required_argument, NULL, 's'}, - #endif /* USE_SHA_CRYPT || USE_BCRYPT || USE_YESCRYPT */ - #endif /* !USE_PAM */ -+#endif /* !ACCT_TOOLS_SETUID */ - {NULL, 0, NULL, '\0'} - }; - - while ((c = getopt_long (argc, argv, --#ifndef USE_PAM -+#if !defined(ACCT_TOOLS_SETUID) && !defined(USE_PAM) - #if defined(USE_SHA_CRYPT) || defined(USE_BCRYPT) || defined(USE_YESCRYPT) - "c:bhrs:", --#else /* !USE_SHA_CRYPT && !USE_BCRYPT && !USE_YESCRYPT */ -+#else /* !ACCT_TOOLS_SETUID && !USE_PAM */ - "c:bhr", - #endif /* USE_SHA_CRYPT || USE_BCRYPT || USE_YESCRYPT */ - #else /* USE_PAM */ -@@ -670,11 +690,13 @@ static void process_flags (int argc, cha - case 'b': - allow_bad_names = true; - break; -+#ifndef ACCT_TOOLS_SETUID - #ifndef USE_PAM - case 'c': - crypt_method = optarg; - break; - #endif /* !USE_PAM */ -+#endif /* !ACCT_TOOLS_SETUID */ - case 'h': - usage (EXIT_SUCCESS); - break; -@@ -684,6 +706,7 @@ static void process_flags (int argc, cha - case 'R': /* no-op, handled in process_root_flag () */ - flags->chroot = true; - break; -+#ifndef ACCT_TOOLS_SETUID - #ifndef USE_PAM - #if defined(USE_SHA_CRYPT) || defined(USE_BCRYPT) || defined(USE_YESCRYPT) - case 's': -@@ -723,6 +746,7 @@ static void process_flags (int argc, cha - break; - #endif /* USE_SHA_CRYPT || USE_BCRYPT || USE_YESCRYPT */ - #endif /* !USE_PAM */ -+#endif /* !ACCT_TOOLS_SETUID */ - default: - usage (EXIT_FAILURE); - break; -@@ -755,6 +779,7 @@ static void process_flags (int argc, cha - */ - static void check_flags (void) - { -+#ifndef ACCT_TOOLS_SETUID - #ifndef USE_PAM - #if defined(USE_SHA_CRYPT) || defined(USE_BCRYPT) || defined(USE_YESCRYPT) - if (sflg && !cflg) { -@@ -787,6 +812,7 @@ static void check_flags (void) - } - } - #endif /* !USE_PAM */ -+#endif /* !ACCT_TOOLS_SETUID */ - } - - /* -@@ -1065,12 +1091,14 @@ int main (int argc, char **argv) - intmax_t line = 0; - uid_t uid; - gid_t gid; -+#ifdef ACCT_TOOLS_SETUID - #ifdef USE_PAM - intmax_t *lines = NULL; - char **usernames = NULL; - char **passwords = NULL; - size_t nusers = 0; - #endif /* USE_PAM */ -+#endif /* ACCT_TOOLS_SETUID */ - struct option_flags flags = {.chroot = false}; - bool process_selinux; - -@@ -1193,6 +1221,7 @@ int main (int argc, char **argv) - } - newpw = *pw; - -+#ifdef ACCT_TOOLS_SETUID - #ifdef USE_PAM - /* keep the list of user/password for later update by PAM */ - nusers++; -@@ -1207,6 +1236,7 @@ int main (int argc, char **argv) - usernames[nusers-1] = xstrdup(fields[0]); - passwords[nusers-1] = xstrdup(fields[1]); - #endif /* USE_PAM */ -+#endif /* ACCT_TOOLS_SETUID */ - if (!streq(fields[1], "") && add_passwd(&newpw, fields[1]) != 0) { - fprintf (stderr, - _("%s: line %jd: can't update password\n"), -@@ -1321,6 +1351,7 @@ int main (int argc, char **argv) - nscd_flush_cache ("group"); - sssd_flush_cache (SSSD_DB_PASSWD | SSSD_DB_GROUP); - -+#ifdef ACCT_TOOLS_SETUID - #ifdef USE_PAM - /* Now update the passwords using PAM */ - for (size_t i = 0; i < nusers; i++) { -@@ -1334,6 +1365,7 @@ int main (int argc, char **argv) - } - } - #endif /* USE_PAM */ -+#endif /* ACCT_TOOLS_SETUID */ - - exit (EXIT_SUCCESS); - } diff --git a/shadow-utils.spec b/shadow-utils.spec index d381c31..11243dd 100644 --- a/shadow-utils.spec +++ b/shadow-utils.spec @@ -1,7 +1,7 @@ Summary: Utilities for managing accounts and shadow password files Name: shadow-utils Version: 4.19.3 -Release: 2%{?dist} +Release: 3%{?dist} Epoch: 2 License: BSD-3-Clause AND GPL-2.0-or-later URL: https://github.com/shadow-maint/shadow @@ -22,8 +22,6 @@ Source7: passwd.pamd ### Patches ### # Misc manual page changes - non-upstreamable Patch0: shadow-4.15.0-manfix.patch -# Probably non-upstreamable -Patch1: shadow-4.19.0-account-tools-setuid.patch # https://github.com/shadow-maint/shadow/commit/3e8c105f0703264e947d8c034b90419794955d49 Patch2: shadow-4-19-useradd-support-btrfs.patch # https://github.com/shadow-maint/shadow/commit/6be13b2f84a2c1a0d0f4129b5258b4b443e7f86c @@ -146,6 +144,8 @@ install -d -m 755 $RPM_BUILD_ROOT%{_sysconfdir}/default install -p -c -m 0644 %{SOURCE3} $RPM_BUILD_ROOT%{_sysconfdir}/login.defs install -p -c -m 0600 %{SOURCE2} $RPM_BUILD_ROOT%{_sysconfdir}/default/useradd install -d -m 755 $RPM_BUILD_ROOT%{_pam_confdir} +install -m 644 %{SOURCE7} $RPM_BUILD_ROOT%{_pam_confdir}/chpasswd +install -m 644 %{SOURCE7} $RPM_BUILD_ROOT%{_pam_confdir}/newusers install -m 644 %{SOURCE7} $RPM_BUILD_ROOT%{_pam_confdir}/passwd @@ -194,11 +194,9 @@ rm $RPM_BUILD_ROOT%{_mandir}/*/man8/faillog.* # Remove PAM service files we don't use. rm $RPM_BUILD_ROOT%{_pam_confdir}/chfn -rm $RPM_BUILD_ROOT%{_pam_confdir}/chpasswd rm $RPM_BUILD_ROOT%{_pam_confdir}/chsh rm $RPM_BUILD_ROOT%{_pam_confdir}/groupmems rm $RPM_BUILD_ROOT%{_pam_confdir}/login -rm $RPM_BUILD_ROOT%{_pam_confdir}/newusers rm $RPM_BUILD_ROOT%{_pam_confdir}/su find $RPM_BUILD_ROOT%{_mandir} -depth -type d -empty -delete @@ -225,6 +223,8 @@ rm -f $RPM_BUILD_ROOT/%{_libdir}/libsubid.a %license gpl-2.0.txt shadow-bsd.txt %attr(0644,root,root) %config(noreplace) %{_sysconfdir}/login.defs %attr(0644,root,root) %config(noreplace) %{_sysconfdir}/default/useradd +%config(noreplace) %{_pam_confdir}/chpasswd +%config(noreplace) %{_pam_confdir}/newusers %config(noreplace) %{_pam_confdir}/passwd %{_bindir}/sg %attr(4755,root,root) %{_bindir}/chage @@ -279,6 +279,10 @@ rm -f $RPM_BUILD_ROOT/%{_libdir}/libsubid.a %{_libdir}/libsubid.so %changelog +* Wed May 27 2026 Iker Pedrosa - 2:4.19.3-3 +- Enable use of PAM for chpasswd and newusers + Resolves: #2461179 and #2283963 + * Thu Apr 23 2026 Iker Pedrosa - 2:4.19.3-2 - btrfs: simplify checks improve useradd behavior for non-btrfs From 8bd71d9f9156a632eb3850c1b2658fc21959f892 Mon Sep 17 00:00:00 2001 From: Fedora Release Engineering Date: Fri, 17 Jul 2026 06:27:57 +0000 Subject: [PATCH 27/30] Rebuilt for https://fedoraproject.org/wiki/Fedora_45_Mass_Rebuild --- shadow-utils.spec | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/shadow-utils.spec b/shadow-utils.spec index 11243dd..4f94261 100644 --- a/shadow-utils.spec +++ b/shadow-utils.spec @@ -1,7 +1,7 @@ Summary: Utilities for managing accounts and shadow password files Name: shadow-utils Version: 4.19.3 -Release: 3%{?dist} +Release: 4%{?dist} Epoch: 2 License: BSD-3-Clause AND GPL-2.0-or-later URL: https://github.com/shadow-maint/shadow @@ -279,6 +279,9 @@ rm -f $RPM_BUILD_ROOT/%{_libdir}/libsubid.a %{_libdir}/libsubid.so %changelog +* Fri Jul 17 2026 Fedora Release Engineering - 2:4.19.3-4 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_45_Mass_Rebuild + * Wed May 27 2026 Iker Pedrosa - 2:4.19.3-3 - Enable use of PAM for chpasswd and newusers Resolves: #2461179 and #2283963 From 8cb145bd7377a26dae190cc3b7ea46a293ac0a45 Mon Sep 17 00:00:00 2001 From: Iker Pedrosa Date: Thu, 2 Jul 2026 16:26:11 +0200 Subject: [PATCH 28/30] Rebase to v4.20.0-rc3 Signed-off-by: Iker Pedrosa --- .gitignore | 4 + shadow-4-19-useradd-support-btrfs.patch | 149 ------- shadow-4.15.0-manfix.patch | 162 -------- shadow-4.19.3-chkhash.patch | 102 ----- shadow-4.19.3-useradd-fix-btrfs.patch | 492 ------------------------ shadow-utils.spec | 28 +- sources | 4 +- 7 files changed, 14 insertions(+), 927 deletions(-) delete mode 100644 shadow-4-19-useradd-support-btrfs.patch delete mode 100644 shadow-4.15.0-manfix.patch delete mode 100644 shadow-4.19.3-chkhash.patch delete mode 100644 shadow-4.19.3-useradd-fix-btrfs.patch diff --git a/.gitignore b/.gitignore index 7db552e..13adb8c 100644 --- a/.gitignore +++ b/.gitignore @@ -48,3 +48,7 @@ shadow-4.1.4.2.tar.bz2 /shadow-4.19.2.tar.xz.asc /shadow-4.19.3.tar.xz /shadow-4.19.3.tar.xz.asc +/shadow-4.20.0-rc2.tar.xz +/shadow-4.20.0-rc2.tar.xz.asc +/shadow-4.20.0-rc3.tar.xz +/shadow-4.20.0-rc3.tar.xz.asc diff --git a/shadow-4-19-useradd-support-btrfs.patch b/shadow-4-19-useradd-support-btrfs.patch deleted file mode 100644 index b801289..0000000 --- a/shadow-4-19-useradd-support-btrfs.patch +++ /dev/null @@ -1,149 +0,0 @@ -From 3e8c105f0703264e947d8c034b90419794955d49 Mon Sep 17 00:00:00 2001 -From: Hadi Chokr -Date: Fri, 12 Dec 2025 11:18:06 +0100 -Subject: [PATCH] src/useradd: Support config for creating home dirs as Btrfs - subvolumes - -Closes: #1162 - -Co-authored-by: Neal Gompa -Signed-off-by: Hadi Chokr -Signed-off-by: Neal Gompa -Reviewed-by: Alejandro Colomar ---- - man/useradd.8.xml | 27 +++++++++++++++++++++++++++ - src/useradd.c | 24 ++++++++++++++++++++++++ - 2 files changed, 51 insertions(+) - -diff --git a/man/useradd.8.xml b/man/useradd.8.xml -index e6db7f6d..c745a620 100644 ---- a/man/useradd.8.xml -+++ b/man/useradd.8.xml -@@ -134,6 +134,33 @@ - - - -+ -+ -+ -+ -+ -+ -+ Create the user's home directory as a Btrfs subvolume. -+ -+ -+ If this option is not specified, -+ useradd will follow the default behavior -+ defined by the variable -+ in /etc/default/useradd. -+ If this variable is not set, the default value is no. -+ -+ -+ When the command-line option -+ is specified, -+ a Btrfs subvolume is created -+ regardless of any configuration file settings. -+ -+ -+ Note: this feature works only if the underlying filesystem supports -+ Btrfs subvolumes. -+ -+ -+ - - - ,  COMMENT -diff --git a/src/useradd.c b/src/useradd.c -index 899efe3c..8433ed8a 100644 ---- a/src/useradd.c -+++ b/src/useradd.c -@@ -114,6 +114,7 @@ static const char *def_shell = "/bin/bash"; - static const char *def_template = SKEL_DIR; - static const char *def_usrtemplate = USRSKELDIR; - static const char *def_create_mail_spool = "yes"; -+static const char *def_btrfs_subvolume_home = "no"; - static const char *def_log_init = "yes"; - - static long def_inactive = -1; -@@ -222,6 +223,7 @@ static bool home_added = false; - #define DSKEL "SKEL" - #define DUSRSKEL "USRSKEL" - #define DCREATE_MAIL_SPOOL "CREATE_MAIL_SPOOL" -+#define DBTRFS_SUBVOLUME_HOME "BTRFS_SUBVOLUME_HOME" - #define DLOG_INIT "LOG_INIT" - - /* local function prototypes */ -@@ -456,6 +458,7 @@ get_defaults(const struct option_flags *flags) - def_usrtemplate = xstrdup(ccp); - } - } -+ - /* - * Create by default user mail spool or not ? - */ -@@ -466,6 +469,15 @@ get_defaults(const struct option_flags *flags) - def_create_mail_spool = xstrdup(ccp); - } - -+ /* -+ * Create home directories as Btrfs subvolumes by default? -+ */ -+ else if (streq(buf, DBTRFS_SUBVOLUME_HOME)) { -+ if (streq(ccp, "")) -+ ccp = "no"; -+ def_btrfs_subvolume_home = xstrdup(ccp); -+ } -+ - /* - * By default do we add the user to the lastlog and faillog databases ? - */ -@@ -500,6 +512,7 @@ static void show_defaults (void) - printf ("SKEL=%s\n", def_template); - printf ("USRSKEL=%s\n", def_usrtemplate); - printf ("CREATE_MAIL_SPOOL=%s\n", def_create_mail_spool); -+ printf ("BTRFS_SUBVOLUME_HOME=%s\n", def_btrfs_subvolume_home); - printf ("LOG_INIT=%s\n", def_log_init); - } - -@@ -523,6 +536,7 @@ set_defaults(void) - bool out_skel = false; - bool out_usrskel = false; - bool out_create_mail_spool = false; -+ bool out_btrfs_subvolume_home = false; - bool out_log_init = false; - char buf[1024]; - char *new_file = NULL; -@@ -639,6 +653,11 @@ set_defaults(void) - DCREATE_MAIL_SPOOL "=%s\n", - def_create_mail_spool); - out_create_mail_spool = true; -+ } else if (!out_btrfs_subvolume_home && streq(buf, DBTRFS_SUBVOLUME_HOME)) { -+ fprintf(ofp, -+ DBTRFS_SUBVOLUME_HOME "=%s\n", -+ def_btrfs_subvolume_home); -+ out_btrfs_subvolume_home = true; - } else if (!out_log_init && streq(buf, DLOG_INIT)) { - fprintf(ofp, DLOG_INIT "=%s\n", def_log_init); - out_log_init = true; -@@ -673,6 +692,8 @@ set_defaults(void) - - if (!out_create_mail_spool) - fprintf (ofp, DCREATE_MAIL_SPOOL "=%s\n", def_create_mail_spool); -+ if (!out_btrfs_subvolume_home) -+ fprintf (ofp, DBTRFS_SUBVOLUME_HOME "=%s\n", def_btrfs_subvolume_home); - if (!out_log_init) - fprintf (ofp, DLOG_INIT "=%s\n", def_log_init); - /* -@@ -1431,6 +1452,9 @@ static void process_flags (int argc, char **argv, struct option_flags *flags) - } - } - -+ if (!subvolflg && strcaseeq(def_btrfs_subvolume_home, "yes")) -+ subvolflg = true; -+ - if (!gflg && !Nflg && !Uflg) { - /* Get the settings from login.defs */ - Uflg = getdef_bool ("USERGROUPS_ENAB"); --- -2.52.0 - diff --git a/shadow-4.15.0-manfix.patch b/shadow-4.15.0-manfix.patch deleted file mode 100644 index 34e62f9..0000000 --- a/shadow-4.15.0-manfix.patch +++ /dev/null @@ -1,162 +0,0 @@ -diff -up shadow-4.15.0/man/groupmems.8.xml.manfix shadow-4.15.0/man/groupmems.8.xml ---- shadow-4.15.0/man/groupmems.8.xml.manfix 2023-05-26 04:56:11.000000000 +0200 -+++ shadow-4.15.0/man/groupmems.8.xml 2024-02-09 10:42:20.337036378 +0100 -@@ -156,20 +156,10 @@ - - SETUP - -- The groupmems executable should be in mode -- 2710 as user root and in group -- groups. The system administrator can add users to -- group groups to allow or disallow them using the -- groupmems utility to manage their own group -- membership list. -+ In this operating system the groupmems executable -+ is not setuid and regular users cannot use it to manipulate -+ the membership of their own group. - -- -- -- $ groupadd -r groups -- $ chmod 2710 groupmems -- $ chown root:groups groupmems -- $ groupmems -g groups -a gk4 -- - - - -diff -up shadow-4.15.0/man/ja/man5/login.defs.5.manfix shadow-4.15.0/man/ja/man5/login.defs.5 ---- shadow-4.15.0/man/ja/man5/login.defs.5.manfix 2023-03-13 21:58:56.000000000 +0100 -+++ shadow-4.15.0/man/ja/man5/login.defs.5 2024-02-09 10:42:20.337036378 +0100 -@@ -123,10 +123,6 @@ 以下の参照表は、 - shadow パスワード機能のどのプログラムが - どのパラメータを使用するかを示したものである。 - .na --.IP chfn 12 --CHFN_AUTH CHFN_RESTRICT --.IP chsh 12 --CHFN_AUTH - .IP groupadd 12 - GID_MAX GID_MIN - .IP newusers 12 -diff -up shadow-4.15.0/man/login.defs.5.xml.manfix shadow-4.15.0/man/login.defs.5.xml ---- shadow-4.15.0/man/login.defs.5.xml.manfix 2024-01-22 22:36:43.000000000 +0100 -+++ shadow-4.15.0/man/login.defs.5.xml 2024-02-09 10:45:49.014407259 +0100 -@@ -144,6 +144,17 @@ - long numeric parameters is machine-dependent. - - -+ -+ Please note that the parameters in this configuration file control the -+ behavior of the tools from the shadow-utils component. None of these -+ tools uses the PAM mechanism, and the utilities that use PAM (such as the -+ passwd command) should be configured elsewhere. The only values that -+ affect PAM modules are ENCRYPT_METHOD and SHA_CRYPT_MAX_ROUNDS -+ for pam_unix module, FAIL_DELAY for pam_faildelay module, -+ and UMASK for pam_umask module. Refer to -+ pam(8) for more information. -+ -+ - The following configuration items are provided: - - -@@ -240,16 +251,6 @@ - - - -- chfn -- -- -- CHFN_AUTH -- CHFN_RESTRICT -- LOGIN_STRING -- -- -- -- - chgpasswd - - -@@ -276,14 +277,6 @@ - - - -- -- chsh -- -- -- CHSH_AUTH LOGIN_STRING -- -- -- - - - -@@ -352,34 +345,6 @@ - LASTLOG_UID_MAX - - -- -- login -- -- -- CONSOLE -- CONSOLE_GROUPS DEFAULT_HOME -- ENV_HZ ENV_PATH ENV_SUPATH -- ENV_TZ ENVIRON_FILE -- ERASECHAR FAIL_DELAY -- FAILLOG_ENAB -- FAKE_SHELL -- FTMP_FILE -- HUSHLOGIN_FILE -- ISSUE_FILE -- KILLCHAR -- LASTLOG_ENAB LASTLOG_UID_MAX -- LOGIN_RETRIES -- LOGIN_STRING -- LOGIN_TIMEOUT LOG_OK_LOGINS LOG_UNKFAIL_ENAB -- MAIL_CHECK_ENAB MAIL_DIR MAIL_FILE -- MOTD_FILE NOLOGINS_FILE PORTTIME_CHECKS_ENAB -- QUOTAS_ENAB -- TTYGROUP TTYPERM TTYTYPE_FILE -- ULIMIT UMASK -- USERGROUPS_ENAB -- -- -- - - - newgrp / sg -@@ -451,32 +416,6 @@ - - - -- -- su -- -- -- CONSOLE -- CONSOLE_GROUPS DEFAULT_HOME -- ENV_HZ ENVIRON_FILE -- ENV_PATH ENV_SUPATH -- ENV_TZ LOGIN_STRING MAIL_CHECK_ENAB -- MAIL_DIR MAIL_FILE QUOTAS_ENAB -- SULOG_FILE SU_NAME -- SU_WHEEL_ONLY -- SYSLOG_SU_ENAB -- USERGROUPS_ENAB -- -- -- -- -- sulogin -- -- -- ENV_HZ -- ENV_TZ -- -- -- - - useradd - diff --git a/shadow-4.19.3-chkhash.patch b/shadow-4.19.3-chkhash.patch deleted file mode 100644 index 6dfc159..0000000 --- a/shadow-4.19.3-chkhash.patch +++ /dev/null @@ -1,102 +0,0 @@ -From 4b2e669441438a6ae91c82a24c796894aa6a53b2 Mon Sep 17 00:00:00 2001 -From: Alejandro Colomar -Date: Thu, 15 Jan 2026 01:14:46 +0100 -Subject: [PATCH 1/3] 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 -Signed-off-by: Alejandro Colomar -(cherry picked from commit bce404a7a831de911cf20fc8d4c548957f70bc72) ---- - lib/chkhash.c | 4 +--- - 1 file changed, 1 insertion(+), 3 deletions(-) - -diff --git a/lib/chkhash.c b/lib/chkhash.c -index 348dfe7478f8..48aaa417e9a9 100644 ---- a/lib/chkhash.c -+++ b/lib/chkhash.c -@@ -31,9 +31,7 @@ match_regex(const char *pattern, const char *string) - - - /* -- * is_valid_hash - check if the given string is a valid password hash -- * -- * Returns true if the string appears to be a valid hash, false otherwise. -+ * is_valid_hash - check if the string is a valid shadow(5) 2nd field. - * - * regex from: https://man.archlinux.org/man/crypt.5.en - */ --- -2.53.0 - - -From b72386978e0c994c5efc91d548da68a8d97088ab Mon Sep 17 00:00:00 2001 -From: Alejandro Colomar -Date: Thu, 15 Jan 2026 01:24:10 +0100 -Subject: [PATCH 2/3] lib/chkhash.c: is_valid_hash(): Comment meaning of !hash - and * - -Signed-off-by: Alejandro Colomar -(cherry picked from commit 9a86c515a1e7caeeb9e60fa63871170a2dfd98f0) ---- - lib/chkhash.c | 2 ++ - 1 file changed, 2 insertions(+) - -diff --git a/lib/chkhash.c b/lib/chkhash.c -index 48aaa417e9a9..9123038adef0 100644 ---- a/lib/chkhash.c -+++ b/lib/chkhash.c -@@ -38,12 +38,14 @@ match_regex(const char *pattern, const char *string) - 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; - --- -2.53.0 - - -From 4486e3e0f8b74e6af27b5dcb4114fd776afdd605 Mon Sep 17 00:00:00 2001 -From: Alejandro Colomar -Date: Fri, 16 Jan 2026 01:52:32 +0100 -Subject: [PATCH 3/3] man/shadow.5.xml: Document "*" - -Signed-off-by: Alejandro Colomar -(cherry picked from commit 6be13b2f84a2c1a0d0f4129b5258b4b443e7f86c) ---- - man/shadow.5.xml | 8 ++++++-- - 1 file changed, 6 insertions(+), 2 deletions(-) - -diff --git a/man/shadow.5.xml b/man/shadow.5.xml -index dc7fdfde542f..d3cc03982c47 100644 ---- a/man/shadow.5.xml -+++ b/man/shadow.5.xml -@@ -82,8 +82,12 @@ - If the password field begins with an exclamation mark !, - the password is locked. - The remaining characters on the -- line represent the password field before the password was -- locked. -+ line represent the password hash. -+ -+ -+ If the password hash consists of a star *, -+ password access is effectively disallowed; -+ this is because no password can produce a hash like this. - - - Refer to crypt --- -2.53.0 - diff --git a/shadow-4.19.3-useradd-fix-btrfs.patch b/shadow-4.19.3-useradd-fix-btrfs.patch deleted file mode 100644 index 47671d9..0000000 --- a/shadow-4.19.3-useradd-fix-btrfs.patch +++ /dev/null @@ -1,492 +0,0 @@ -From 4079070c4b8ced8df9d86366ad113fc3c2e4e49c Mon Sep 17 00:00:00 2001 -From: Alejandro Colomar -Date: Wed, 22 Apr 2026 11:02:36 +0200 -Subject: [PATCH 1/5] lib/btrfs.c: btrfs_is_subvolume(): Simplify error check - -Signed-off-by: Alejandro Colomar ---- - lib/btrfs.c | 3 +-- - 1 file changed, 1 insertion(+), 2 deletions(-) - -diff --git a/lib/btrfs.c b/lib/btrfs.c -index 85ec7dbc..fe20cf30 100644 ---- a/lib/btrfs.c -+++ b/lib/btrfs.c -@@ -67,8 +67,7 @@ int btrfs_is_subvolume(const char *path) - if (ret <= 0) - return ret; - -- ret = stat(path, &st); -- if (ret == -1) -+ if (stat(path, &st) == -1) - return -1; - - if (st.st_ino != BTRFS_FIRST_FREE_OBJECTID || !S_ISDIR(st.st_mode)) { --- -2.53.0 - - -From 7a4faaaf46314547cc5e6c8264c3a11753ef9352 Mon Sep 17 00:00:00 2001 -From: Alejandro Colomar -Date: Wed, 22 Apr 2026 11:14:20 +0200 -Subject: [PATCH 2/5] lib/, src/: Move statfs(2) call out of is_btrfs() - -This simplifies the return value of is_btrfs() into a boolean. - -Signed-off-by: Alejandro Colomar ---- - lib/btrfs.c | 25 +++++++++---------------- - lib/prototypes.h | 3 ++- - src/useradd.c | 11 +++++++++-- - 3 files changed, 20 insertions(+), 19 deletions(-) - -diff --git a/lib/btrfs.c b/lib/btrfs.c -index fe20cf30..065c7938 100644 ---- a/lib/btrfs.c -+++ b/lib/btrfs.c -@@ -60,12 +60,13 @@ int btrfs_remove_subvolume(const char *path) - */ - int btrfs_is_subvolume(const char *path) - { -- struct stat st; -- int ret; -+ struct stat st; -+ struct statfs sfs; - -- ret = is_btrfs(path); -- if (ret <= 0) -- return ret; -+ if (statfs(path, &sfs) == -1) -+ return -1; -+ if (!is_btrfs(&sfs)) -+ return 0; - - if (stat(path, &st) == -1) - return -1; -@@ -78,16 +79,8 @@ int btrfs_is_subvolume(const char *path) - } - - --/* Adapted from btrfsprogs */ --int is_btrfs(const char *path) -+bool -+is_btrfs(const struct statfs *sfs) - { -- struct statfs sfs; -- int ret; -- -- ret = statfs(path, &sfs); -- if (ret == -1) -- return -1; -- -- return sfs.f_type == BTRFS_SUPER_MAGIC; -+ return sfs->f_type == BTRFS_SUPER_MAGIC; - } -- -diff --git a/lib/prototypes.h b/lib/prototypes.h -index 9a03e312..42aa2923 100644 ---- a/lib/prototypes.h -+++ b/lib/prototypes.h -@@ -23,6 +23,7 @@ - - #include - #include -+#include - #include - #include - #include -@@ -54,7 +55,7 @@ extern int isexpired (const struct passwd *, /*@null@*/const struct spwd *); - extern int btrfs_create_subvolume(const char *path); - extern int btrfs_remove_subvolume(const char *path); - extern int btrfs_is_subvolume(const char *path); --extern int is_btrfs(const char *path); -+extern bool is_btrfs(const struct statfs *sfs); - #endif - - /* basename() renamed to Basename() to avoid libc name space confusion */ -diff --git a/src/useradd.c b/src/useradd.c -index df679d28..5ce6cd6a 100644 ---- a/src/useradd.c -+++ b/src/useradd.c -@@ -25,6 +25,7 @@ - #include - #include - #include -+#include - #include - #include - #include -@@ -2265,6 +2266,7 @@ static void create_home(const struct option_flags *flags) - #if WITH_BTRFS - if (subvolflg && (strlen(prefix_user_home) - (int)strlen(path)) <= 1) { - char *btrfs_check = strdup(path); -+ struct statfs sfs; - - if (!btrfs_check) { - fprintf(stderr, -@@ -2273,13 +2275,18 @@ static void create_home(const struct option_flags *flags) - fail_exit(E_HOMEDIR, process_selinux); - } - stpcpy(&btrfs_check[strlen(path) - strlen(cp) - 1], ""); -- if (is_btrfs(btrfs_check) <= 0) { -+ if (statfs(btrfs_check, &sfs) == -1) { -+ fprintf(stderr, "%s: statfs(\"%s\"): %s\n", -+ Prog, btrfs_check, strerrno()); -+ fail_exit(E_HOMEDIR, process_selinux); -+ } -+ free(btrfs_check); -+ if (!is_btrfs(&sfs)) { - fprintf(stderr, - _("%s: home directory \"%s\" must be mounted on BTRFS\n"), - Prog, path); - fail_exit(E_HOMEDIR, process_selinux); - } -- free(btrfs_check); - // make subvolume to mount for user instead of directory - if (btrfs_create_subvolume(path)) { - fprintf(stderr, --- -2.53.0 - - -From 96460c7240264c0022cebc9ba1774993ee2594cf Mon Sep 17 00:00:00 2001 -From: Alejandro Colomar -Date: Wed, 22 Apr 2026 11:21:14 +0200 -Subject: [PATCH 3/5] lib/, src/: Move btrfs.c prototypes to lib/btrfs.h - -Signed-off-by: Alejandro Colomar ---- - lib/Makefile.am | 2 +- - lib/btrfs.c | 4 ++++ - lib/btrfs.h | 23 +++++++++++++++++++++++ - lib/prototypes.h | 9 --------- - src/useradd.c | 1 + - src/userdel.c | 1 + - src/usermod.c | 1 + - 7 files changed, 31 insertions(+), 10 deletions(-) - create mode 100644 lib/btrfs.h - -diff --git a/lib/Makefile.am b/lib/Makefile.am -index 7c488f82..0664e610 100644 ---- a/lib/Makefile.am -+++ b/lib/Makefile.am -@@ -290,7 +290,7 @@ libshadow_la_SOURCES += tcbfuncs.c tcbfuncs.h - endif - - if WITH_BTRFS --libshadow_la_SOURCES += btrfs.c -+libshadow_la_SOURCES += btrfs.c btrfs.h - endif - - if ENABLE_LASTLOG -diff --git a/lib/btrfs.c b/lib/btrfs.c -index 065c7938..15b6b315 100644 ---- a/lib/btrfs.c -+++ b/lib/btrfs.c -@@ -1,3 +1,7 @@ -+#include "config.h" -+ -+#include "btrfs.h" -+ - #include - #include - #include -diff --git a/lib/btrfs.h b/lib/btrfs.h -new file mode 100644 -index 00000000..50b70488 ---- /dev/null -+++ b/lib/btrfs.h -@@ -0,0 +1,23 @@ -+// SPDX-FileCopyrightText: 2026, Alejandro Colomar -+// SPDX-License-Identifier: BSD-3-Clause -+ -+ -+#ifndef SHADOW_INCLUDE_LIB_BTRFS_H_ -+#define SHADOW_INCLUDE_LIB_BTRFS_H_ -+ -+ -+#include "config.h" -+ -+#include -+#include -+ -+ -+#ifdef WITH_BTRFS -+int btrfs_create_subvolume(const char *path); -+int btrfs_remove_subvolume(const char *path); -+int btrfs_is_subvolume(const char *path); -+bool is_btrfs(const struct statfs *sfs); -+#endif -+ -+ -+#endif // include guard -diff --git a/lib/prototypes.h b/lib/prototypes.h -index 42aa2923..d22c477a 100644 ---- a/lib/prototypes.h -+++ b/lib/prototypes.h -@@ -23,7 +23,6 @@ - - #include - #include --#include - #include - #include - #include -@@ -50,14 +49,6 @@ extern int expire (const struct passwd *, /*@null@*/const struct spwd *); - /* isexpired.c */ - extern int isexpired (const struct passwd *, /*@null@*/const struct spwd *); - --/* btrfs.c */ --#ifdef WITH_BTRFS --extern int btrfs_create_subvolume(const char *path); --extern int btrfs_remove_subvolume(const char *path); --extern int btrfs_is_subvolume(const char *path); --extern bool is_btrfs(const struct statfs *sfs); --#endif -- - /* basename() renamed to Basename() to avoid libc name space confusion */ - /* basename.c */ - extern /*@observer@*/const char *Basename (const char *str); -diff --git a/src/useradd.c b/src/useradd.c -index 5ce6cd6a..9bd32b11 100644 ---- a/src/useradd.c -+++ b/src/useradd.c -@@ -34,6 +34,7 @@ - #include "alloc/malloc.h" - #include "atoi/a2i.h" - #include "atoi/getnum.h" -+#include "btrfs.h" - #include "chkname.h" - #include "defines.h" - #include "faillog.h" -diff --git a/src/userdel.c b/src/userdel.c -index 9b7d81be..ebd064c0 100644 ---- a/src/userdel.c -+++ b/src/userdel.c -@@ -20,6 +20,7 @@ - #include - #include - -+#include "btrfs.h" - #ifdef ACCT_TOOLS_SETUID - #ifdef USE_PAM - #include "pam_defs.h" -diff --git a/src/usermod.c b/src/usermod.c -index 7c0321a6..3afe19ea 100644 ---- a/src/usermod.c -+++ b/src/usermod.c -@@ -32,6 +32,7 @@ - #include "alloc/malloc.h" - #include "atoi/a2i.h" - #include "atoi/getnum.h" -+#include "btrfs.h" - #include "chkname.h" - #include "defines.h" - #include "faillog.h" --- -2.53.0 - - -From 7be9a6540f1cad477fd4f4e83d25904a5095bb86 Mon Sep 17 00:00:00 2001 -From: Hadi Chokr -Date: Tue, 21 Apr 2026 09:18:19 +0200 -Subject: [PATCH 4/5] useradd(8): fallback to regular dir for BTRFS home on - non-BTRFS parent - -When the --btrfs-subvolume-home option is used but the parent directory -is not on a BTRFS filesystem, useradd previously failed with an error. -This is too strict; instead, fall back to creating a regular directory -and issue a warning. The subvolume creation is attempted only when the -parent is BTRFS. Otherwise, a regular directory is created and a -syslog(3) warning is logged. - -Fixes: 3e8c105 (2026-01-02; "src/useradd: Support config for creating home dirs as Btrfs subvolumes") -Co-authored-by: Hadi Chokr -Co-authored-by: Alejandro Colomar -Signed-off-by: Alejandro Colomar ---- - man/useradd.8.xml | 27 ++++++++++++++++++++++-- - src/useradd.c | 52 +++++++++++++++++++++++------------------------ - 2 files changed, 51 insertions(+), 28 deletions(-) - -diff --git a/man/useradd.8.xml b/man/useradd.8.xml -index 8a087e13..0bde6683 100644 ---- a/man/useradd.8.xml -+++ b/man/useradd.8.xml -@@ -2,6 +2,7 @@ - - - -- Note: this feature works only if the underlying filesystem supports -- Btrfs subvolumes. -+ If the parent directory of the user's home directory is -+ not on a Btrfs filesystem, -+ useradd will not create a -+ subvolume. -+ Instead, it creates a regular directory, -+ prints a warning to standard error, -+ and logs the event via syslog at level -+ LOG_WARN. -+ The user account is still created successfully. -+ -+ -+ If the filesystem type cannot be determined (e.g., because of -+ insufficient permissions, an I/O error, -+ or a -+ -+ statfs -+ 2 -+ -+ failure), -+ useradd treats this as a fatal error: -+ the home directory is not created, -+ the command exits with a non‑zero status -+ (E_HOMEDIR, 12), -+ and an error message is printed. - - - -diff --git a/src/useradd.c b/src/useradd.c -index 9bd32b11..9210379c 100644 ---- a/src/useradd.c -+++ b/src/useradd.c -@@ -1,11 +1,11 @@ --/* -- * SPDX-FileCopyrightText: 1991 - 1994, Julianne Frances Haugh -- * SPDX-FileCopyrightText: 1996 - 2000, Marek Michałkiewicz -- * SPDX-FileCopyrightText: 2000 - 2006, Tomasz Kłoczko -- * SPDX-FileCopyrightText: 2007 - 2012, Nicolas François -- * -- * SPDX-License-Identifier: BSD-3-Clause -- */ -+// SPDX-FileCopyrightText: 1991-1994, Julianne Frances Haugh -+// SPDX-FileCopyrightText: 1996-2000, Marek Michałkiewicz -+// SPDX-FileCopyrightText: 2000-2006, Tomasz Kłoczko -+// SPDX-FileCopyrightText: 2007-2012, Nicolas François -+// SPDX-FileCopyrightText: 2025-2026, Hadi Chokr -+// SPDX-FileCopyrightText: 2026, Alejandro Colomar -+// SPDX-License-Identifier: BSD-3-Clause -+ - - #include "config.h" - -@@ -2251,6 +2251,8 @@ static void create_home(const struct option_flags *flags) - owner root:root. - */ - for (cp = strtok(bhome, "/"); cp != NULL; cp = strtok(NULL, "/")) { -+ bool dir_created; -+ - /* Avoid turning a relative path into an absolute path. */ - if (strprefix(bhome, "/") || !streq(path, "")) - strcat(path, "/"); -@@ -2260,10 +2262,7 @@ static void create_home(const struct option_flags *flags) - continue; - } - -- /* Check if parent directory is BTRFS, fail if requesting -- subvolume but no BTRFS. The paths could be different by the -- trailing slash -- */ -+ dir_created = false; - #if WITH_BTRFS - if (subvolflg && (strlen(prefix_user_home) - (int)strlen(path)) <= 1) { - char *btrfs_check = strdup(path); -@@ -2284,25 +2283,26 @@ static void create_home(const struct option_flags *flags) - free(btrfs_check); - if (!is_btrfs(&sfs)) { - fprintf(stderr, -- _("%s: home directory \"%s\" must be mounted on BTRFS\n"), -- Prog, path); -- fail_exit(E_HOMEDIR, process_selinux); -+ _("%s: warning: \"%s\" is not on BTRFS; creating regular directory instead of subvolume\n"), -+ Prog, prefix_user_home); -+ } else { -+ if (btrfs_create_subvolume(path)) { -+ fprintf(stderr, -+ _("%s: failed to create BTRFS subvolume: %s\n"), -+ Prog, path); -+ fail_exit(E_HOMEDIR, process_selinux); -+ } -+ dir_created = true; - } -- // make subvolume to mount for user instead of directory -- if (btrfs_create_subvolume(path)) { -- fprintf(stderr, -- _("%s: failed to create BTRFS subvolume: %s\n"), -+ } -+#endif -+ if (!dir_created) { -+ if (mkdir(path, 0) != 0) { -+ fprintf(stderr, _("%s: cannot create directory %s\n"), - Prog, path); - fail_exit(E_HOMEDIR, process_selinux); - } - } -- else --#endif -- if (mkdir(path, 0) != 0) { -- fprintf(stderr, _("%s: cannot create directory %s\n"), -- Prog, path); -- fail_exit(E_HOMEDIR, process_selinux); -- } - if (chown(path, 0, 0) < 0) { - fprintf(stderr, - _("%s: warning: chown on `%s' failed: %m\n"), --- -2.53.0 - - -From 827f69b864461ab6d7549762bef06ab4495d2587 Mon Sep 17 00:00:00 2001 -From: Hadi Chokr -Date: Mon, 20 Apr 2026 12:27:31 +0200 -Subject: [PATCH 5/5] man/useradd.8.xml: Remove trailing spaces from - useradd.8.xml - -Signed-off-by: Hadi Chokr -Reviewed-by: Alejandro Colomar ---- - man/useradd.8.xml | 6 +++--- - 1 file changed, 3 insertions(+), 3 deletions(-) - -diff --git a/man/useradd.8.xml b/man/useradd.8.xml -index 0bde6683..f85780e2 100644 ---- a/man/useradd.8.xml -+++ b/man/useradd.8.xml -@@ -207,7 +207,7 @@ - user's login directory. The default is to append the - LOGIN name to - BASE_DIR and use that as the -- login directory name. -+ login directory name. - The directory HOME_DIR is not created by - default. However it will be created for non-system users if either the - flag is specifed or -@@ -422,7 +422,7 @@ - - - -- Create the user's home directory if it does not exist. -+ Create the user's home directory if it does not exist. - The files and directories contained in the skeleton directory - (which can be defined with the option) - will be copied to the home directory. -@@ -512,7 +512,7 @@ - password himself. - - -- Note:Avoid this option on the command -+ Note:Avoid this option on the command - line because the password (or encrypted password) will - be visible by users listing the processes. - --- -2.53.0 - diff --git a/shadow-utils.spec b/shadow-utils.spec index 4f94261..a56f0b7 100644 --- a/shadow-utils.spec +++ b/shadow-utils.spec @@ -1,12 +1,12 @@ Summary: Utilities for managing accounts and shadow password files Name: shadow-utils -Version: 4.19.3 -Release: 4%{?dist} +Version: 4.20.0.rc3 +Release: 1%{?dist} Epoch: 2 License: BSD-3-Clause AND GPL-2.0-or-later URL: https://github.com/shadow-maint/shadow -Source0: https://github.com/shadow-maint/shadow/releases/download/4.19.3/shadow-4.19.3.tar.xz -Source1: https://github.com/shadow-maint/shadow/releases/download/4.19.3/shadow-4.19.3.tar.xz.asc +Source0: https://github.com/shadow-maint/shadow/releases/download/4.20.0-rc3/shadow-4.20.0-rc3.tar.xz +Source1: https://github.com/shadow-maint/shadow/releases/download/4.20.0-rc3/shadow-4.20.0-rc3.tar.xz.asc Source2: shadow-utils.useradd Source3: shadow-utils.login.defs Source4: shadow-bsd.txt @@ -20,14 +20,6 @@ Source7: passwd.pamd %global _ld_strict_symbol_defs 1 ### Patches ### -# Misc manual page changes - non-upstreamable -Patch0: shadow-4.15.0-manfix.patch -# https://github.com/shadow-maint/shadow/commit/3e8c105f0703264e947d8c034b90419794955d49 -Patch2: shadow-4-19-useradd-support-btrfs.patch -# https://github.com/shadow-maint/shadow/commit/6be13b2f84a2c1a0d0f4129b5258b4b443e7f86c -Patch3: shadow-4.19.3-chkhash.patch -# https://github.com/shadow-maint/shadow/commit/827f69b864461ab6d7549762bef06ab4495d2587 -Patch4: shadow-4.19.3-useradd-fix-btrfs.patch ### Dependencies ### Requires: audit-libs >= 1.6.5 @@ -106,7 +98,7 @@ Requires: shadow-utils-subid = %{epoch}:%{version}-%{release} Development files for shadow-utils-subid. %prep -%autosetup -p 1 -S git -n shadow-4.19.3 +%autosetup -p 1 -S git -n shadow-4.20.0-rc3 iconv -f ISO88591 -t utf-8 doc/HOWTO > doc/HOWTO.utf8 cp -f doc/HOWTO.utf8 doc/HOWTO @@ -163,26 +155,20 @@ mv -v $RPM_BUILD_ROOT/usr/sbin/* $RPM_BUILD_ROOT%{_bindir}/ # Remove binaries we don't use. rm $RPM_BUILD_ROOT%{_bindir}/chfn rm $RPM_BUILD_ROOT%{_bindir}/chsh -rm $RPM_BUILD_ROOT%{_bindir}/expiry rm $RPM_BUILD_ROOT%{_bindir}/login rm $RPM_BUILD_ROOT%{_bindir}/su rm $RPM_BUILD_ROOT%{_bindir}/faillog -rm $RPM_BUILD_ROOT%{_sbindir}/logoutd rm $RPM_BUILD_ROOT%{_sbindir}/nologin rm $RPM_BUILD_ROOT%{_mandir}/man1/chfn.* rm $RPM_BUILD_ROOT%{_mandir}/*/man1/chfn.* rm $RPM_BUILD_ROOT%{_mandir}/man1/chsh.* rm $RPM_BUILD_ROOT%{_mandir}/*/man1/chsh.* -rm $RPM_BUILD_ROOT%{_mandir}/man1/expiry.* -rm $RPM_BUILD_ROOT%{_mandir}/*/man1/expiry.* rm $RPM_BUILD_ROOT%{_mandir}/man1/login.* rm $RPM_BUILD_ROOT%{_mandir}/*/man1/login.* rm $RPM_BUILD_ROOT%{_mandir}/man1/su.* rm $RPM_BUILD_ROOT%{_mandir}/*/man1/su.* rm $RPM_BUILD_ROOT%{_mandir}/man5/passwd.* rm $RPM_BUILD_ROOT%{_mandir}/*/man5/passwd.* -rm $RPM_BUILD_ROOT%{_mandir}/man8/logoutd.* -rm $RPM_BUILD_ROOT%{_mandir}/*/man8/logoutd.* rm $RPM_BUILD_ROOT%{_mandir}/man8/nologin.* rm $RPM_BUILD_ROOT%{_mandir}/*/man8/nologin.* rm $RPM_BUILD_ROOT%{_mandir}/man3/getspnam.* @@ -195,7 +181,6 @@ rm $RPM_BUILD_ROOT%{_mandir}/*/man8/faillog.* # Remove PAM service files we don't use. rm $RPM_BUILD_ROOT%{_pam_confdir}/chfn rm $RPM_BUILD_ROOT%{_pam_confdir}/chsh -rm $RPM_BUILD_ROOT%{_pam_confdir}/groupmems rm $RPM_BUILD_ROOT%{_pam_confdir}/login rm $RPM_BUILD_ROOT%{_pam_confdir}/su @@ -279,6 +264,9 @@ rm -f $RPM_BUILD_ROOT/%{_libdir}/libsubid.a %{_libdir}/libsubid.so %changelog +* Wed Jul 22 2026 Iker Pedrosa - 2:4.20.0-rc3-1 +- Rebase to version 4.20.0-rc3 + * Fri Jul 17 2026 Fedora Release Engineering - 2:4.19.3-4 - Rebuilt for https://fedoraproject.org/wiki/Fedora_45_Mass_Rebuild diff --git a/sources b/sources index d12859f..d0fb76c 100644 --- a/sources +++ b/sources @@ -1,2 +1,2 @@ -SHA512 (shadow-4.19.3.tar.xz) = f2b41f8186681eb98dae0da87ed1a1f03327c5005c9d4bf9500801352e318de6f5954dc5b8f30ab9aece728b1efb74b4ff8fb2720acc4c2aaef701331a4a5a05 -SHA512 (shadow-4.19.3.tar.xz.asc) = 295a6bffefd333a9ea84fa59b368f5c48d0ac5224199624b768087098110b0f0c19bef37f5c474c45277980758dcb6728e8bb17eb7ab1a57603cad41f1dd00ab +SHA512 (shadow-4.20.0-rc3.tar.xz) = 49a3662d10669c5f34af012b2397324ffe11544c1f09d89a32408a9c8928cd4d313c16ed3afaa6ff8665f8f224de139170dc0d9c5dea127986c2b9350c117d1a +SHA512 (shadow-4.20.0-rc3.tar.xz.asc) = 719b80d1331fe825c0f0279309e7eb56b0a767e61fda845ea051c60128fdbc4abc1b9e9f0569fa6b9073c7a8712ce0b144f33b472cf3eebd32e75668f745cd1c From c8aec6bdba74d16b628a0bd1c440f17ed91bd050 Mon Sep 17 00:00:00 2001 From: Iker Pedrosa Date: Wed, 22 Jul 2026 16:44:45 +0200 Subject: [PATCH 29/30] Tests: update dependencies System test framework requires gcc, python3-devel and libssh-devel installed before pip tries to compile ansible-pylibssh. Signed-off-by: Iker Pedrosa --- plans/tier0-functional.fmf | 3 +++ 1 file changed, 3 insertions(+) diff --git a/plans/tier0-functional.fmf b/plans/tier0-functional.fmf index 7fb6f9c..a9939b5 100644 --- a/plans/tier0-functional.fmf +++ b/plans/tier0-functional.fmf @@ -14,7 +14,10 @@ prepare: how: install package: - expect + - gcc - git + - libssh-devel + - python3-devel - python3-pip - name: Setup SSH keys for localhost testing From 103ee1cc475003e8c32e3f54792aaca0cef33250 Mon Sep 17 00:00:00 2001 From: Iker Pedrosa Date: Wed, 29 Jul 2026 15:27:11 +0200 Subject: [PATCH 30/30] Rebase to version 4.20.0 Signed-off-by: Iker Pedrosa --- .gitignore | 2 ++ shadow-utils.spec | 11 +++++++---- sources | 4 ++-- 3 files changed, 11 insertions(+), 6 deletions(-) diff --git a/.gitignore b/.gitignore index 13adb8c..3023995 100644 --- a/.gitignore +++ b/.gitignore @@ -52,3 +52,5 @@ shadow-4.1.4.2.tar.bz2 /shadow-4.20.0-rc2.tar.xz.asc /shadow-4.20.0-rc3.tar.xz /shadow-4.20.0-rc3.tar.xz.asc +/shadow-4.20.0.tar.xz +/shadow-4.20.0.tar.xz.asc diff --git a/shadow-utils.spec b/shadow-utils.spec index a56f0b7..7bc7e8b 100644 --- a/shadow-utils.spec +++ b/shadow-utils.spec @@ -1,12 +1,12 @@ Summary: Utilities for managing accounts and shadow password files Name: shadow-utils -Version: 4.20.0.rc3 +Version: 4.20.0 Release: 1%{?dist} Epoch: 2 License: BSD-3-Clause AND GPL-2.0-or-later URL: https://github.com/shadow-maint/shadow -Source0: https://github.com/shadow-maint/shadow/releases/download/4.20.0-rc3/shadow-4.20.0-rc3.tar.xz -Source1: https://github.com/shadow-maint/shadow/releases/download/4.20.0-rc3/shadow-4.20.0-rc3.tar.xz.asc +Source0: https://github.com/shadow-maint/shadow/releases/download/4.20.0/shadow-4.20.0.tar.xz +Source1: https://github.com/shadow-maint/shadow/releases/download/4.20.0/shadow-4.20.0.tar.xz.asc Source2: shadow-utils.useradd Source3: shadow-utils.login.defs Source4: shadow-bsd.txt @@ -98,7 +98,7 @@ Requires: shadow-utils-subid = %{epoch}:%{version}-%{release} Development files for shadow-utils-subid. %prep -%autosetup -p 1 -S git -n shadow-4.20.0-rc3 +%autosetup -p 1 -S git -n shadow-4.20.0 iconv -f ISO88591 -t utf-8 doc/HOWTO > doc/HOWTO.utf8 cp -f doc/HOWTO.utf8 doc/HOWTO @@ -264,6 +264,9 @@ rm -f $RPM_BUILD_ROOT/%{_libdir}/libsubid.a %{_libdir}/libsubid.so %changelog +* Wed Jul 29 2026 Iker Pedrosa - 2:4.20.0-1 +- Rebase to version 4.20.0 + * Wed Jul 22 2026 Iker Pedrosa - 2:4.20.0-rc3-1 - Rebase to version 4.20.0-rc3 diff --git a/sources b/sources index d0fb76c..7669083 100644 --- a/sources +++ b/sources @@ -1,2 +1,2 @@ -SHA512 (shadow-4.20.0-rc3.tar.xz) = 49a3662d10669c5f34af012b2397324ffe11544c1f09d89a32408a9c8928cd4d313c16ed3afaa6ff8665f8f224de139170dc0d9c5dea127986c2b9350c117d1a -SHA512 (shadow-4.20.0-rc3.tar.xz.asc) = 719b80d1331fe825c0f0279309e7eb56b0a767e61fda845ea051c60128fdbc4abc1b9e9f0569fa6b9073c7a8712ce0b144f33b472cf3eebd32e75668f745cd1c +SHA512 (shadow-4.20.0.tar.xz) = 0b8afded372e4d37a78f38cb972c0ab877870ef6356cdd1c45be3c708af3d1496c6f87de6bf6a5b1d217d4d52d7e2d15c28b26530949cc6de0231a4028930406 +SHA512 (shadow-4.20.0.tar.xz.asc) = e4b134543768f323df30a1e450c22086d013020928d62bd3207db7f633beea3587b67802da5b575d5a794e8c2bebd7a0abeba6ed69d44bf08adc37df3bf352ac