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