diff --git a/.fmf/version b/.fmf/version new file mode 100644 index 0000000..d00491f --- /dev/null +++ b/.fmf/version @@ -0,0 +1 @@ +1 diff --git a/.gitignore b/.gitignore index 0ceb6c8..3023995 100644 --- a/.gitignore +++ b/.gitignore @@ -44,3 +44,13 @@ shadow-4.1.4.2.tar.bz2 /shadow-4.18.0.tar.xz.asc /shadow-4.19.0.tar.xz /shadow-4.19.0.tar.xz.asc +/shadow-4.19.2.tar.xz +/shadow-4.19.2.tar.xz.asc +/shadow-4.19.3.tar.xz +/shadow-4.19.3.tar.xz.asc +/shadow-4.20.0-rc2.tar.xz +/shadow-4.20.0-rc2.tar.xz.asc +/shadow-4.20.0-rc3.tar.xz +/shadow-4.20.0-rc3.tar.xz.asc +/shadow-4.20.0.tar.xz +/shadow-4.20.0.tar.xz.asc diff --git a/1520.patch b/1520.patch deleted file mode 100644 index 069eebb..0000000 --- a/1520.patch +++ /dev/null @@ -1,40 +0,0 @@ -From b721cdd03daa3416127289da02fe781efb2562df Mon Sep 17 00:00:00 2001 -From: Adam Williamson -Date: Tue, 27 Jan 2026 15:11:07 -0800 -Subject: [PATCH] lib/chkhash.c: fix escaping in SHA-256 / SHA-512 / MD5 - regexes - -`\\n` inside square brackets doesn't include or exclude the -newline character. It includes or excludes a literal slash and -the literal character 'n'. - -Fixes: c44f1e096a19 (2025-07-20; "chpasswd: Check hash before write when using -e") -Closes: -Signed-off-by: Adam Williamson ---- - lib/chkhash.c | 6 +++--- - 1 file changed, 3 insertions(+), 3 deletions(-) - -diff --git a/lib/chkhash.c b/lib/chkhash.c -index 101f2004e8..9123038ade 100644 ---- a/lib/chkhash.c -+++ b/lib/chkhash.c -@@ -62,15 +62,15 @@ is_valid_hash(const char *hash) - return true; - - // SHA-512: $6$ + salt + $ + 86-char hash -- if (match_regex("^\\$6\\$(rounds=[1-9][0-9]{3,8}\\$)?[^$:\\n]{1,16}\\$[./A-Za-z0-9]{86}$", hash)) -+ if (match_regex("^\\$6\\$(rounds=[1-9][0-9]{3,8}\\$)?[^$:\n]{1,16}\\$[./A-Za-z0-9]{86}$", hash)) - return true; - - // SHA-256: $5$ + salt + $ + 43-char hash -- if (match_regex("^\\$5\\$(rounds=[1-9][0-9]{3,8}\\$)?[^$:\\n]{1,16}\\$[./A-Za-z0-9]{43}$", hash)) -+ if (match_regex("^\\$5\\$(rounds=[1-9][0-9]{3,8}\\$)?[^$:\n]{1,16}\\$[./A-Za-z0-9]{43}$", hash)) - return true; - - // MD5: $1$ + salt + $ + 22-char hash -- if (match_regex("^\\$1\\$[^$:\\n]{1,8}\\$[./A-Za-z0-9]{22}$", hash)) -+ if (match_regex("^\\$1\\$[^$:\n]{1,8}\\$[./A-Za-z0-9]{22}$", hash)) - return true; - - // DES: exactly 13 characters from [A-Za-z0-9./] diff --git a/plans/tier0-functional.fmf b/plans/tier0-functional.fmf new file mode 100644 index 0000000..a9939b5 --- /dev/null +++ b/plans/tier0-functional.fmf @@ -0,0 +1,51 @@ +summary: Tier 0 functional tests for shadow-utils +description: | + Run comprehensive system tests for shadow-utils. These tests validate user and group + account management functionality by testing actual system operations including user + creation, password management, group operations, and verification of system files + (i.e. /etc/passwd, /etc/shadow). + +provision: + how: virtual + image: fedora + +prepare: + - name: Install general dependencies + how: install + package: + - expect + - gcc + - git + - libssh-devel + - python3-devel + - python3-pip + + - name: Setup SSH keys for localhost testing + how: shell + script: + - ssh-keygen -t rsa -f /root/.ssh/id_rsa -N "" -q + - cat /root/.ssh/id_rsa.pub >> /root/.ssh/authorized_keys + - chmod 600 /root/.ssh/authorized_keys + - ssh-keyscan -H localhost >> /root/.ssh/known_hosts + + - name: Clone shadow repository + how: shell + script: + - git clone https://github.com/shadow-maint/shadow.git /tmp/shadow-test + + - name: Copy test topology for Fedora CI + how: shell + script: + - cp tests/mhc-fedora-ci.yaml /tmp/shadow-test/tests/system + + - name: Install test dependencies + how: shell + script: + - pip3 install -r /tmp/shadow-test/tests/system/requirements.txt + +execute: + how: tmt + duration: 30m + script: | + cd /tmp/shadow-test/tests/system + pytest --mh-config=mhc-fedora-ci.yaml --mh-lazy-ssh -v diff --git a/shadow-4-19-useradd-support-btrfs.patch b/shadow-4-19-useradd-support-btrfs.patch deleted file mode 100644 index b801289..0000000 --- a/shadow-4-19-useradd-support-btrfs.patch +++ /dev/null @@ -1,149 +0,0 @@ -From 3e8c105f0703264e947d8c034b90419794955d49 Mon Sep 17 00:00:00 2001 -From: Hadi Chokr -Date: Fri, 12 Dec 2025 11:18:06 +0100 -Subject: [PATCH] src/useradd: Support config for creating home dirs as Btrfs - subvolumes - -Closes: #1162 - -Co-authored-by: Neal Gompa -Signed-off-by: Hadi Chokr -Signed-off-by: Neal Gompa -Reviewed-by: Alejandro Colomar ---- - man/useradd.8.xml | 27 +++++++++++++++++++++++++++ - src/useradd.c | 24 ++++++++++++++++++++++++ - 2 files changed, 51 insertions(+) - -diff --git a/man/useradd.8.xml b/man/useradd.8.xml -index e6db7f6d..c745a620 100644 ---- a/man/useradd.8.xml -+++ b/man/useradd.8.xml -@@ -134,6 +134,33 @@ - - - -+ -+ -+ -+ -+ -+ -+ Create the user's home directory as a Btrfs subvolume. -+ -+ -+ If this option is not specified, -+ useradd will follow the default behavior -+ defined by the variable -+ in /etc/default/useradd. -+ If this variable is not set, the default value is no. -+ -+ -+ When the command-line option -+ is specified, -+ a Btrfs subvolume is created -+ regardless of any configuration file settings. -+ -+ -+ Note: this feature works only if the underlying filesystem supports -+ Btrfs subvolumes. -+ -+ -+ - - - ,  COMMENT -diff --git a/src/useradd.c b/src/useradd.c -index 899efe3c..8433ed8a 100644 ---- a/src/useradd.c -+++ b/src/useradd.c -@@ -114,6 +114,7 @@ static const char *def_shell = "/bin/bash"; - static const char *def_template = SKEL_DIR; - static const char *def_usrtemplate = USRSKELDIR; - static const char *def_create_mail_spool = "yes"; -+static const char *def_btrfs_subvolume_home = "no"; - static const char *def_log_init = "yes"; - - static long def_inactive = -1; -@@ -222,6 +223,7 @@ static bool home_added = false; - #define DSKEL "SKEL" - #define DUSRSKEL "USRSKEL" - #define DCREATE_MAIL_SPOOL "CREATE_MAIL_SPOOL" -+#define DBTRFS_SUBVOLUME_HOME "BTRFS_SUBVOLUME_HOME" - #define DLOG_INIT "LOG_INIT" - - /* local function prototypes */ -@@ -456,6 +458,7 @@ get_defaults(const struct option_flags *flags) - def_usrtemplate = xstrdup(ccp); - } - } -+ - /* - * Create by default user mail spool or not ? - */ -@@ -466,6 +469,15 @@ get_defaults(const struct option_flags *flags) - def_create_mail_spool = xstrdup(ccp); - } - -+ /* -+ * Create home directories as Btrfs subvolumes by default? -+ */ -+ else if (streq(buf, DBTRFS_SUBVOLUME_HOME)) { -+ if (streq(ccp, "")) -+ ccp = "no"; -+ def_btrfs_subvolume_home = xstrdup(ccp); -+ } -+ - /* - * By default do we add the user to the lastlog and faillog databases ? - */ -@@ -500,6 +512,7 @@ static void show_defaults (void) - printf ("SKEL=%s\n", def_template); - printf ("USRSKEL=%s\n", def_usrtemplate); - printf ("CREATE_MAIL_SPOOL=%s\n", def_create_mail_spool); -+ printf ("BTRFS_SUBVOLUME_HOME=%s\n", def_btrfs_subvolume_home); - printf ("LOG_INIT=%s\n", def_log_init); - } - -@@ -523,6 +536,7 @@ set_defaults(void) - bool out_skel = false; - bool out_usrskel = false; - bool out_create_mail_spool = false; -+ bool out_btrfs_subvolume_home = false; - bool out_log_init = false; - char buf[1024]; - char *new_file = NULL; -@@ -639,6 +653,11 @@ set_defaults(void) - DCREATE_MAIL_SPOOL "=%s\n", - def_create_mail_spool); - out_create_mail_spool = true; -+ } else if (!out_btrfs_subvolume_home && streq(buf, DBTRFS_SUBVOLUME_HOME)) { -+ fprintf(ofp, -+ DBTRFS_SUBVOLUME_HOME "=%s\n", -+ def_btrfs_subvolume_home); -+ out_btrfs_subvolume_home = true; - } else if (!out_log_init && streq(buf, DLOG_INIT)) { - fprintf(ofp, DLOG_INIT "=%s\n", def_log_init); - out_log_init = true; -@@ -673,6 +692,8 @@ set_defaults(void) - - if (!out_create_mail_spool) - fprintf (ofp, DCREATE_MAIL_SPOOL "=%s\n", def_create_mail_spool); -+ if (!out_btrfs_subvolume_home) -+ fprintf (ofp, DBTRFS_SUBVOLUME_HOME "=%s\n", def_btrfs_subvolume_home); - if (!out_log_init) - fprintf (ofp, DLOG_INIT "=%s\n", def_log_init); - /* -@@ -1431,6 +1452,9 @@ static void process_flags (int argc, char **argv, struct option_flags *flags) - } - } - -+ if (!subvolflg && strcaseeq(def_btrfs_subvolume_home, "yes")) -+ subvolflg = true; -+ - if (!gflg && !Nflg && !Uflg) { - /* Get the settings from login.defs */ - Uflg = getdef_bool ("USERGROUPS_ENAB"); --- -2.52.0 - diff --git a/shadow-4.15.0-manfix.patch b/shadow-4.15.0-manfix.patch deleted file mode 100644 index 34e62f9..0000000 --- a/shadow-4.15.0-manfix.patch +++ /dev/null @@ -1,162 +0,0 @@ -diff -up shadow-4.15.0/man/groupmems.8.xml.manfix shadow-4.15.0/man/groupmems.8.xml ---- shadow-4.15.0/man/groupmems.8.xml.manfix 2023-05-26 04:56:11.000000000 +0200 -+++ shadow-4.15.0/man/groupmems.8.xml 2024-02-09 10:42:20.337036378 +0100 -@@ -156,20 +156,10 @@ - - SETUP - -- The groupmems executable should be in mode -- 2710 as user root and in group -- groups. The system administrator can add users to -- group groups to allow or disallow them using the -- groupmems utility to manage their own group -- membership list. -+ In this operating system the groupmems executable -+ is not setuid and regular users cannot use it to manipulate -+ the membership of their own group. - -- -- -- $ groupadd -r groups -- $ chmod 2710 groupmems -- $ chown root:groups groupmems -- $ groupmems -g groups -a gk4 -- - - - -diff -up shadow-4.15.0/man/ja/man5/login.defs.5.manfix shadow-4.15.0/man/ja/man5/login.defs.5 ---- shadow-4.15.0/man/ja/man5/login.defs.5.manfix 2023-03-13 21:58:56.000000000 +0100 -+++ shadow-4.15.0/man/ja/man5/login.defs.5 2024-02-09 10:42:20.337036378 +0100 -@@ -123,10 +123,6 @@ 以下の参照表は、 - shadow パスワード機能のどのプログラムが - どのパラメータを使用するかを示したものである。 - .na --.IP chfn 12 --CHFN_AUTH CHFN_RESTRICT --.IP chsh 12 --CHFN_AUTH - .IP groupadd 12 - GID_MAX GID_MIN - .IP newusers 12 -diff -up shadow-4.15.0/man/login.defs.5.xml.manfix shadow-4.15.0/man/login.defs.5.xml ---- shadow-4.15.0/man/login.defs.5.xml.manfix 2024-01-22 22:36:43.000000000 +0100 -+++ shadow-4.15.0/man/login.defs.5.xml 2024-02-09 10:45:49.014407259 +0100 -@@ -144,6 +144,17 @@ - long numeric parameters is machine-dependent. - - -+ -+ Please note that the parameters in this configuration file control the -+ behavior of the tools from the shadow-utils component. None of these -+ tools uses the PAM mechanism, and the utilities that use PAM (such as the -+ passwd command) should be configured elsewhere. The only values that -+ affect PAM modules are ENCRYPT_METHOD and SHA_CRYPT_MAX_ROUNDS -+ for pam_unix module, FAIL_DELAY for pam_faildelay module, -+ and UMASK for pam_umask module. Refer to -+ pam(8) for more information. -+ -+ - The following configuration items are provided: - - -@@ -240,16 +251,6 @@ - - - -- chfn -- -- -- CHFN_AUTH -- CHFN_RESTRICT -- LOGIN_STRING -- -- -- -- - chgpasswd - - -@@ -276,14 +277,6 @@ - - - -- -- chsh -- -- -- CHSH_AUTH LOGIN_STRING -- -- -- - - - -@@ -352,34 +345,6 @@ - LASTLOG_UID_MAX - - -- -- login -- -- -- CONSOLE -- CONSOLE_GROUPS DEFAULT_HOME -- ENV_HZ ENV_PATH ENV_SUPATH -- ENV_TZ ENVIRON_FILE -- ERASECHAR FAIL_DELAY -- FAILLOG_ENAB -- FAKE_SHELL -- FTMP_FILE -- HUSHLOGIN_FILE -- ISSUE_FILE -- KILLCHAR -- LASTLOG_ENAB LASTLOG_UID_MAX -- LOGIN_RETRIES -- LOGIN_STRING -- LOGIN_TIMEOUT LOG_OK_LOGINS LOG_UNKFAIL_ENAB -- MAIL_CHECK_ENAB MAIL_DIR MAIL_FILE -- MOTD_FILE NOLOGINS_FILE PORTTIME_CHECKS_ENAB -- QUOTAS_ENAB -- TTYGROUP TTYPERM TTYTYPE_FILE -- ULIMIT UMASK -- USERGROUPS_ENAB -- -- -- - - - newgrp / sg -@@ -451,32 +416,6 @@ - - - -- -- su -- -- -- CONSOLE -- CONSOLE_GROUPS DEFAULT_HOME -- ENV_HZ ENVIRON_FILE -- ENV_PATH ENV_SUPATH -- ENV_TZ LOGIN_STRING MAIL_CHECK_ENAB -- MAIL_DIR MAIL_FILE QUOTAS_ENAB -- SULOG_FILE SU_NAME -- SU_WHEEL_ONLY -- SYSLOG_SU_ENAB -- USERGROUPS_ENAB -- -- -- -- -- sulogin -- -- -- ENV_HZ -- ENV_TZ -- -- -- - - useradd - diff --git a/shadow-4.19.0-account-tools-setuid.patch b/shadow-4.19.0-account-tools-setuid.patch deleted file mode 100644 index 9452582..0000000 --- a/shadow-4.19.0-account-tools-setuid.patch +++ /dev/null @@ -1,377 +0,0 @@ -diff -up shadow-4.19.0-rc1/src/chpasswd.c.account-tools-setuid shadow-4.19.0-rc1/src/chpasswd.c ---- shadow-4.19.0-rc1/src/chpasswd.c.account-tools-setuid 2025-12-07 16:56:38.563285789 +0100 -+++ shadow-4.19.0-rc1/src/chpasswd.c 2025-12-09 14:54:29.232282499 +0100 -@@ -466,9 +466,11 @@ int main (int argc, char **argv) - char *newpwd; - const char *salt; - -+#ifdef ACCT_TOOLS_SETUID - #ifdef USE_PAM - bool use_pam = true; - #endif /* USE_PAM */ -+#endif /* ACCT_TOOLS_SETUID */ - - bool errors = false; - intmax_t line = 0; -@@ -495,19 +497,23 @@ int main (int argc, char **argv) - process_root_flag ("-R", argc, argv); - prefix = process_prefix_flag ("-P", argc, argv); - -+#ifdef ACCT_TOOLS_SETUID - #ifdef USE_PAM - if (md5flg || eflg || cflg || prefix[0]) { - use_pam = false; - } - #endif /* USE_PAM */ -+#endif /* ACCT_TOOLS_SETUID */ - - OPENLOG (Prog); - - check_perms (); - -+#ifdef ACCT_TOOLS_SETUID - #ifdef USE_PAM - if (!use_pam) - #endif /* USE_PAM */ -+#endif /* ACCT_TOOLS_SETUID */ - { - is_shadow_pwd = spw_file_present (); - -@@ -562,6 +568,7 @@ int main (int argc, char **argv) - } - newpwd = cp; - -+#ifdef ACCT_TOOLS_SETUID - #ifdef USE_PAM - if (use_pam) { - if (do_pam_passwd_non_interactive (Prog, name, newpwd) != 0) { -@@ -572,6 +579,7 @@ int main (int argc, char **argv) - } - } else - #endif /* USE_PAM */ -+#endif /* ACCT_TOOLS_SETUID */ - { - - /* -@@ -706,9 +714,11 @@ int main (int argc, char **argv) - * password database. - */ - if (errors) { -+#ifdef ACCT_TOOLS_SETUID - #ifdef USE_PAM - if (!use_pam) - #endif /* USE_PAM */ -+#endif /* ACCT_TOOLS_SETUID */ - { - fprintf (stderr, - _("%s: error detected, changes ignored\n"), -@@ -717,9 +727,11 @@ int main (int argc, char **argv) - fail_exit (1, process_selinux); - } - -+#ifdef ACCT_TOOLS_SETUID - #ifdef USE_PAM - if (!use_pam) - #endif /* USE_PAM */ -+#endif /* ACCT_TOOLS_SETUID */ - { - /* Save the changes */ - close_files (&flags); -diff -up shadow-4.19.0-rc1/src/groupmems.c.account-tools-setuid shadow-4.19.0-rc1/src/groupmems.c ---- shadow-4.19.0-rc1/src/groupmems.c.account-tools-setuid 2025-12-07 16:56:38.564285805 +0100 -+++ shadow-4.19.0-rc1/src/groupmems.c 2025-12-09 14:57:23.767054141 +0100 -@@ -14,9 +14,11 @@ - #include - #include - #include -+#ifdef ACCT_TOOLS_SETUID - #ifdef USE_PAM - #include "pam_defs.h" - #endif /* USE_PAM */ -+#endif /* ACCT_TOOLS_SETUID */ - #include - - #include "alloc/malloc.h" -@@ -432,6 +434,7 @@ static void - check_perms(MAYBE_UNUSED bool process_selinux) - { - if (!list) { -+#ifdef ACCT_TOOLS_SETUID - #ifdef USE_PAM - pam_handle_t *pamh = NULL; - int retval; -@@ -465,7 +468,8 @@ check_perms(MAYBE_UNUSED bool process_se - fail_exit (1, process_selinux); - } - (void) pam_end (pamh, retval); --#endif -+#endif /* USE_PAM */ -+#endif /* ACCT_TOOLS_SETUID */ - } - } - -diff -up shadow-4.19.0-rc1/src/newusers.c.account-tools-setuid shadow-4.19.0-rc1/src/newusers.c ---- shadow-4.19.0-rc1/src/newusers.c.account-tools-setuid 2025-12-07 16:56:38.565285821 +0100 -+++ shadow-4.19.0-rc1/src/newusers.c 2025-12-09 15:07:40.115500988 +0100 -@@ -74,6 +74,7 @@ struct option_flags { - static const char Prog[] = "newusers"; - - static bool rflg = false; /* create a system account */ -+#ifndef ACCT_TOOLS_SETUID - #ifndef USE_PAM - static /*@null@*//*@observer@*/char *crypt_method = NULL; - #define cflg (NULL != crypt_method) -@@ -90,6 +91,7 @@ static long bcrypt_rounds = 13; - static long yescrypt_cost = 5; - #endif /* USE_YESCRYPT */ - #endif /* !USE_PAM */ -+#endif /* !ACCT_TOOLS_SETUID */ - - static bool is_shadow; - #ifdef SHADOWGRP -@@ -112,9 +114,11 @@ NORETURN static void fail_exit (int, boo - static int add_group (const char *, const char *, gid_t *, gid_t); - static int get_user_id (const char *, uid_t *); - static int add_user (const char *, uid_t, gid_t); -+#ifndef ACCT_TOOLS_SETUID - #ifndef USE_PAM - static int update_passwd (struct passwd *, const char *); - #endif /* !USE_PAM */ -+#endif /* !ACCT_TOOLS_SETUID */ - static int add_passwd (struct passwd *, const char *); - static void process_flags (int argc, char **argv, struct option_flags *flags); - static void check_flags (void); -@@ -136,6 +140,7 @@ static void usage (int status) - "Options:\n"), - Prog); - (void) fputs (_(" -b, --badname allow bad names\n"), usageout); -+#ifndef ACCT_TOOLS_SETUID - #ifndef USE_PAM - (void) fprintf (usageout, - _(" -c, --crypt-method METHOD the crypt method (one of %s)\n"), -@@ -151,9 +156,11 @@ static void usage (int status) - #endif - ); - #endif /* !USE_PAM */ -+#endif /* !ACCT_TOOLS_SETUID */ - (void) fputs (_(" -h, --help display this help message and exit\n"), usageout); - (void) fputs (_(" -r, --system create system accounts\n"), usageout); - (void) fputs (_(" -R, --root CHROOT_DIR directory to chroot into\n"), usageout); -+#ifndef ACCT_TOOLS_SETUID - #ifndef USE_PAM - #if defined(USE_SHA_CRYPT) || defined(USE_BCRYPT) || defined(USE_YESCRYPT) - (void) fputs (_(" -s, --sha-rounds number of rounds for the SHA, BCRYPT\n" -@@ -161,6 +168,7 @@ static void usage (int status) - usageout); - #endif /* USE_SHA_CRYPT || USE_BCRYPT || USE_YESCRYPT */ - #endif /* !USE_PAM */ -+#endif /* !ACCT_TOOLS_SETUID */ - (void) fputs ("\n", usageout); - - exit (status); -@@ -426,6 +434,7 @@ static int add_user (const char *name, u - return (pw_update (&pwent) == 0) ? -1 : 0; - } - -+#ifndef ACCT_TOOLS_SETUID - #ifndef USE_PAM - /* - * update_passwd - update the password in the passwd entry -@@ -478,6 +487,7 @@ static int update_passwd (struct passwd - return 0; - } - #endif /* !USE_PAM */ -+#endif /* !ACCT_TOOLS_SETUID */ - - /* - * add_passwd - add or update the encrypted password -@@ -487,10 +497,13 @@ add_passwd(struct passwd *pwd, MAYBE_UNU - { - const struct spwd *sp; - struct spwd spent; -+#ifndef ACCT_TOOLS_SETUID - #ifndef USE_PAM - char *cp; - #endif /* !USE_PAM */ -+#endif /* !ACCT_TOOLS_SETUID */ - -+#ifndef ACCT_TOOLS_SETUID - #ifndef USE_PAM - void *crypt_arg = NULL; - if (NULL != crypt_method) { -@@ -528,13 +541,14 @@ add_passwd(struct passwd *pwd, MAYBE_UNU - return update_passwd (pwd, password); - } - #endif /* USE_PAM */ -+#endif /* !ACCT_TOOLS_SETUID */ - - /* - * Do the first and easiest shadow file case. The user already - * exists in the shadow password file. - */ - sp = spw_locate (pwd->pw_name); --#ifndef USE_PAM -+#if !defined(ACCT_TOOLS_SETUID) && !defined(USE_PAM) - if (NULL != sp) { - spent = *sp; - if ( (NULL != crypt_method) -@@ -571,7 +585,7 @@ add_passwd(struct passwd *pwd, MAYBE_UNU - if (!streq(pwd->pw_passwd, "x")) { - return update_passwd (pwd, password); - } --#else /* USE_PAM */ -+#else /* !ACCT_TOOLS_SETUID && !USE_PAM */ - /* - * If there is already a shadow entry, do not touch it. - * If there is already a passwd entry with a password, do not -@@ -582,14 +596,14 @@ add_passwd(struct passwd *pwd, MAYBE_UNU - || !streq(pwd->pw_passwd, "x")) { - return 0; - } --#endif /* USE_PAM */ -+#endif /* !ACCT_TOOLS_SETUID && !USE_PAM */ - - /* - * Now the really hard case - I need to create an entirely new - * shadow password file entry. - */ - spent.sp_namp = pwd->pw_name; --#ifndef USE_PAM -+#if !defined(ACCT_TOOLS_SETUID) && !defined(USE_PAM) - if ((crypt_method != NULL) && streq(crypt_method, "NONE")) { - spent.sp_pwdp = (char *)password; - } else { -@@ -647,32 +647,38 @@ add_passwd(struct passwd *pwd, MAYBE_UNU - static void process_flags (int argc, char **argv, struct option_flags *flags) - { - int c; -+#ifndef ACCT_TOOLS_SETUID - #ifndef USE_PAM - #if defined(USE_SHA_CRYPT) || defined(USE_BCRYPT) || defined(USE_YESCRYPT) - int bad_s; - #endif /* USE_SHA_CRYPT || USE_BCRYPT || USE_YESCRYPT */ - #endif /* !USE_PAM */ -+#endif /* !ACCT_TOOLS_SETUID */ - static struct option long_options[] = { - {"badname", no_argument, NULL, 'b'}, -+#ifndef ACCT_TOOLS_SETUID - #ifndef USE_PAM - {"crypt-method", required_argument, NULL, 'c'}, - #endif /* !USE_PAM */ -+#endif /* !ACCT_TOOLS_SETUID */ - {"help", no_argument, NULL, 'h'}, - {"system", no_argument, NULL, 'r'}, - {"root", required_argument, NULL, 'R'}, -+#ifndef ACCT_TOOLS_SETUID - #ifndef USE_PAM - #if defined(USE_SHA_CRYPT) || defined(USE_BCRYPT) || defined(USE_YESCRYPT) - {"sha-rounds", required_argument, NULL, 's'}, - #endif /* USE_SHA_CRYPT || USE_BCRYPT || USE_YESCRYPT */ - #endif /* !USE_PAM */ -+#endif /* !ACCT_TOOLS_SETUID */ - {NULL, 0, NULL, '\0'} - }; - - while ((c = getopt_long (argc, argv, --#ifndef USE_PAM -+#if !defined(ACCT_TOOLS_SETUID) && !defined(USE_PAM) - #if defined(USE_SHA_CRYPT) || defined(USE_BCRYPT) || defined(USE_YESCRYPT) - "c:bhrs:", --#else /* !USE_SHA_CRYPT && !USE_BCRYPT && !USE_YESCRYPT */ -+#else /* !ACCT_TOOLS_SETUID && !USE_PAM */ - "c:bhr", - #endif /* USE_SHA_CRYPT || USE_BCRYPT || USE_YESCRYPT */ - #else /* USE_PAM */ -@@ -670,11 +690,13 @@ static void process_flags (int argc, cha - case 'b': - allow_bad_names = true; - break; -+#ifndef ACCT_TOOLS_SETUID - #ifndef USE_PAM - case 'c': - crypt_method = optarg; - break; - #endif /* !USE_PAM */ -+#endif /* !ACCT_TOOLS_SETUID */ - case 'h': - usage (EXIT_SUCCESS); - break; -@@ -684,6 +706,7 @@ static void process_flags (int argc, cha - case 'R': /* no-op, handled in process_root_flag () */ - flags->chroot = true; - break; -+#ifndef ACCT_TOOLS_SETUID - #ifndef USE_PAM - #if defined(USE_SHA_CRYPT) || defined(USE_BCRYPT) || defined(USE_YESCRYPT) - case 's': -@@ -723,6 +746,7 @@ static void process_flags (int argc, cha - break; - #endif /* USE_SHA_CRYPT || USE_BCRYPT || USE_YESCRYPT */ - #endif /* !USE_PAM */ -+#endif /* !ACCT_TOOLS_SETUID */ - default: - usage (EXIT_FAILURE); - break; -@@ -755,6 +779,7 @@ static void process_flags (int argc, cha - */ - static void check_flags (void) - { -+#ifndef ACCT_TOOLS_SETUID - #ifndef USE_PAM - #if defined(USE_SHA_CRYPT) || defined(USE_BCRYPT) || defined(USE_YESCRYPT) - if (sflg && !cflg) { -@@ -787,6 +812,7 @@ static void check_flags (void) - } - } - #endif /* !USE_PAM */ -+#endif /* !ACCT_TOOLS_SETUID */ - } - - /* -@@ -1065,12 +1091,14 @@ int main (int argc, char **argv) - intmax_t line = 0; - uid_t uid; - gid_t gid; -+#ifdef ACCT_TOOLS_SETUID - #ifdef USE_PAM - intmax_t *lines = NULL; - char **usernames = NULL; - char **passwords = NULL; - size_t nusers = 0; - #endif /* USE_PAM */ -+#endif /* ACCT_TOOLS_SETUID */ - struct option_flags flags = {.chroot = false}; - bool process_selinux; - -@@ -1193,6 +1221,7 @@ int main (int argc, char **argv) - } - newpw = *pw; - -+#ifdef ACCT_TOOLS_SETUID - #ifdef USE_PAM - /* keep the list of user/password for later update by PAM */ - nusers++; -@@ -1207,6 +1236,7 @@ int main (int argc, char **argv) - usernames[nusers-1] = xstrdup(fields[0]); - passwords[nusers-1] = xstrdup(fields[1]); - #endif /* USE_PAM */ -+#endif /* ACCT_TOOLS_SETUID */ - if (!streq(fields[1], "") && add_passwd(&newpw, fields[1]) != 0) { - fprintf (stderr, - _("%s: line %jd: can't update password\n"), -@@ -1321,6 +1351,7 @@ int main (int argc, char **argv) - nscd_flush_cache ("group"); - sssd_flush_cache (SSSD_DB_PASSWD | SSSD_DB_GROUP); - -+#ifdef ACCT_TOOLS_SETUID - #ifdef USE_PAM - /* Now update the passwords using PAM */ - for (size_t i = 0; i < nusers; i++) { -@@ -1334,6 +1365,7 @@ int main (int argc, char **argv) - } - } - #endif /* USE_PAM */ -+#endif /* ACCT_TOOLS_SETUID */ - - exit (EXIT_SUCCESS); - } diff --git a/shadow-4.19.0-chkhash1.patch b/shadow-4.19.0-chkhash1.patch deleted file mode 100644 index 986f10b..0000000 --- a/shadow-4.19.0-chkhash1.patch +++ /dev/null @@ -1,88 +0,0 @@ -From 87ec7a52ab25dd6e91253c274fd651f16844cf2a Mon Sep 17 00:00:00 2001 -From: Alejandro Colomar -Date: Wed, 7 Jan 2026 23:39:53 +0100 -Subject: [PATCH 1/2] lib/chkhash.c: is_valid_hash(): Accept a leading '!' - -A leading '!' means that the account is locked. - -Fixes: c44f1e096a19 (2025-07-20; "chpasswd: Check hash before write when using -e") -Link: -Link: -Reported-by: Chris Hofstaedtler -Reviewed-by: Chris Hofstaedtler -Cc: vinz -Signed-off-by: Alejandro Colomar ---- - lib/chkhash.c | 4 ++++ - 1 file changed, 4 insertions(+) - -diff --git a/lib/chkhash.c b/lib/chkhash.c -index 66870500..cb45f3aa 100644 ---- a/lib/chkhash.c -+++ b/lib/chkhash.c -@@ -7,6 +7,8 @@ - #include - #include - -+#include "string/strcmp/strprefix.h" -+ - - /* - * match_regex - return true if match, false if not -@@ -37,6 +39,8 @@ match_regex(const char *pattern, const char *string) - bool - is_valid_hash(const char *hash) - { -+ hash = strprefix(hash, "!") ?: hash; -+ - // Minimum hash length - if (strlen(hash) < 13) - return false; --- -2.52.0 - - -From ddc2549f87e3001f663d5179e9b6d7fe2e1f3b3f Mon Sep 17 00:00:00 2001 -From: Alejandro Colomar -Date: Wed, 7 Jan 2026 23:44:26 +0100 -Subject: [PATCH 2/2] lib/chkhash.c: is_valid_hash(): Accept '*' as the hash - -This is widely accepted as an invalid hash, to remove password access -for an account (that is, no passwords will match the "hash"). - -Fixes: c44f1e096a19 (2025-07-20; "chpasswd: Check hash before write when using -e") -Closes: -Closes: -Reported-by: Chris Hofstaedtler -Reviewed-by: Chris Hofstaedtler -Cc: vinz -Signed-off-by: Alejandro Colomar ---- - lib/chkhash.c | 4 ++++ - 1 file changed, 4 insertions(+) - -diff --git a/lib/chkhash.c b/lib/chkhash.c -index cb45f3aa..71e0fb4e 100644 ---- a/lib/chkhash.c -+++ b/lib/chkhash.c -@@ -7,6 +7,7 @@ - #include - #include - -+#include "string/strcmp/streq.h" - #include "string/strcmp/strprefix.h" - - -@@ -41,6 +42,9 @@ is_valid_hash(const char *hash) - { - hash = strprefix(hash, "!") ?: hash; - -+ if (streq(hash, "*")) -+ return true; -+ - // Minimum hash length - if (strlen(hash) < 13) - return false; --- -2.52.0 - diff --git a/shadow-4.19.0-chkhash2.patch b/shadow-4.19.0-chkhash2.patch deleted file mode 100644 index 1742544..0000000 --- a/shadow-4.19.0-chkhash2.patch +++ /dev/null @@ -1,137 +0,0 @@ -From 9b67543987e3d140c86f1b8e2b5db5c10d8bc3c5 Mon Sep 17 00:00:00 2001 -From: Alejandro Colomar -Date: Thu, 15 Jan 2026 01:11:52 +0100 -Subject: [PATCH 1/4] lib/chkhash.c: is_valid_hash(): Accept an empty hash - -It represents a passwordless account. -That is discouraged, but accepted. - -Fixes: c44f1e096a19 (2025-07-20; "chpasswd: Check hash before write when using -e") -Link: -Reported-by: Marc 'Zugschlus' Haber -Reported-by: "Serge E. Hallyn" -Reported-by: Adam Williamson -Co-authored-by: "Serge E. Hallyn" -Signed-off-by: Alejandro Colomar ---- - lib/chkhash.c | 4 ++++ - 1 file changed, 4 insertions(+) - -diff --git a/lib/chkhash.c b/lib/chkhash.c -index 71e0fb4e..d0a8dc01 100644 ---- a/lib/chkhash.c -+++ b/lib/chkhash.c -@@ -42,6 +42,10 @@ is_valid_hash(const char *hash) - { - hash = strprefix(hash, "!") ?: hash; - -+ // Passwordless account; discouraged -+ if (streq(hash, "")) -+ return true; -+ - if (streq(hash, "*")) - return true; - --- -2.52.0 - - -From bce404a7a831de911cf20fc8d4c548957f70bc72 Mon Sep 17 00:00:00 2001 -From: Alejandro Colomar -Date: Thu, 15 Jan 2026 01:14:46 +0100 -Subject: [PATCH 2/4] lib/chkhash.c: is_valid_hash(): Update comment - -This checks the entire shadow(5) 2nd field, which is more than just -a hash. - -Reported-by: Tobias Stoeckmann -Signed-off-by: Alejandro Colomar ---- - lib/chkhash.c | 4 +--- - 1 file changed, 1 insertion(+), 3 deletions(-) - -diff --git a/lib/chkhash.c b/lib/chkhash.c -index d0a8dc01..1723f862 100644 ---- a/lib/chkhash.c -+++ b/lib/chkhash.c -@@ -31,9 +31,7 @@ match_regex(const char *pattern, const char *string) - - - /* -- * is_valid_hash - check if the given string is a valid password hash -- * -- * Returns true if the string appears to be a valid hash, false otherwise. -+ * is_valid_hash - check if the string is a valid shadow(5) 2nd field. - * - * regex from: https://man.archlinux.org/man/crypt.5.en - */ --- -2.52.0 - - -From 9a86c515a1e7caeeb9e60fa63871170a2dfd98f0 Mon Sep 17 00:00:00 2001 -From: Alejandro Colomar -Date: Thu, 15 Jan 2026 01:24:10 +0100 -Subject: [PATCH 3/4] lib/chkhash.c: is_valid_hash(): Comment meaning of !hash - and * - -Signed-off-by: Alejandro Colomar ---- - lib/chkhash.c | 2 ++ - 1 file changed, 2 insertions(+) - -diff --git a/lib/chkhash.c b/lib/chkhash.c -index 1723f862..101f2004 100644 ---- a/lib/chkhash.c -+++ b/lib/chkhash.c -@@ -38,12 +38,14 @@ match_regex(const char *pattern, const char *string) - bool - is_valid_hash(const char *hash) - { -+ // Password temporarily locked - hash = strprefix(hash, "!") ?: hash; - - // Passwordless account; discouraged - if (streq(hash, "")) - return true; - -+ // Password permanently locked (and forgotten) - if (streq(hash, "*")) - return true; - --- -2.52.0 - - -From 6be13b2f84a2c1a0d0f4129b5258b4b443e7f86c Mon Sep 17 00:00:00 2001 -From: Alejandro Colomar -Date: Fri, 16 Jan 2026 01:52:32 +0100 -Subject: [PATCH 4/4] man/shadow.5.xml: Document "*" - -Signed-off-by: Alejandro Colomar ---- - man/shadow.5.xml | 8 ++++++-- - 1 file changed, 6 insertions(+), 2 deletions(-) - -diff --git a/man/shadow.5.xml b/man/shadow.5.xml -index dc7fdfde..d3cc0398 100644 ---- a/man/shadow.5.xml -+++ b/man/shadow.5.xml -@@ -82,8 +82,12 @@ - If the password field begins with an exclamation mark !, - the password is locked. - The remaining characters on the -- line represent the password field before the password was -- locked. -+ line represent the password hash. -+ -+ -+ If the password hash consists of a star *, -+ password access is effectively disallowed; -+ this is because no password can produce a hash like this. - - - Refer to crypt --- -2.52.0 - diff --git a/shadow-4.19.0-useradd-fix-btrfs.patch b/shadow-4.19.0-useradd-fix-btrfs.patch deleted file mode 100644 index 47671d9..0000000 --- a/shadow-4.19.0-useradd-fix-btrfs.patch +++ /dev/null @@ -1,492 +0,0 @@ -From 4079070c4b8ced8df9d86366ad113fc3c2e4e49c Mon Sep 17 00:00:00 2001 -From: Alejandro Colomar -Date: Wed, 22 Apr 2026 11:02:36 +0200 -Subject: [PATCH 1/5] lib/btrfs.c: btrfs_is_subvolume(): Simplify error check - -Signed-off-by: Alejandro Colomar ---- - lib/btrfs.c | 3 +-- - 1 file changed, 1 insertion(+), 2 deletions(-) - -diff --git a/lib/btrfs.c b/lib/btrfs.c -index 85ec7dbc..fe20cf30 100644 ---- a/lib/btrfs.c -+++ b/lib/btrfs.c -@@ -67,8 +67,7 @@ int btrfs_is_subvolume(const char *path) - if (ret <= 0) - return ret; - -- ret = stat(path, &st); -- if (ret == -1) -+ if (stat(path, &st) == -1) - return -1; - - if (st.st_ino != BTRFS_FIRST_FREE_OBJECTID || !S_ISDIR(st.st_mode)) { --- -2.53.0 - - -From 7a4faaaf46314547cc5e6c8264c3a11753ef9352 Mon Sep 17 00:00:00 2001 -From: Alejandro Colomar -Date: Wed, 22 Apr 2026 11:14:20 +0200 -Subject: [PATCH 2/5] lib/, src/: Move statfs(2) call out of is_btrfs() - -This simplifies the return value of is_btrfs() into a boolean. - -Signed-off-by: Alejandro Colomar ---- - lib/btrfs.c | 25 +++++++++---------------- - lib/prototypes.h | 3 ++- - src/useradd.c | 11 +++++++++-- - 3 files changed, 20 insertions(+), 19 deletions(-) - -diff --git a/lib/btrfs.c b/lib/btrfs.c -index fe20cf30..065c7938 100644 ---- a/lib/btrfs.c -+++ b/lib/btrfs.c -@@ -60,12 +60,13 @@ int btrfs_remove_subvolume(const char *path) - */ - int btrfs_is_subvolume(const char *path) - { -- struct stat st; -- int ret; -+ struct stat st; -+ struct statfs sfs; - -- ret = is_btrfs(path); -- if (ret <= 0) -- return ret; -+ if (statfs(path, &sfs) == -1) -+ return -1; -+ if (!is_btrfs(&sfs)) -+ return 0; - - if (stat(path, &st) == -1) - return -1; -@@ -78,16 +79,8 @@ int btrfs_is_subvolume(const char *path) - } - - --/* Adapted from btrfsprogs */ --int is_btrfs(const char *path) -+bool -+is_btrfs(const struct statfs *sfs) - { -- struct statfs sfs; -- int ret; -- -- ret = statfs(path, &sfs); -- if (ret == -1) -- return -1; -- -- return sfs.f_type == BTRFS_SUPER_MAGIC; -+ return sfs->f_type == BTRFS_SUPER_MAGIC; - } -- -diff --git a/lib/prototypes.h b/lib/prototypes.h -index 9a03e312..42aa2923 100644 ---- a/lib/prototypes.h -+++ b/lib/prototypes.h -@@ -23,6 +23,7 @@ - - #include - #include -+#include - #include - #include - #include -@@ -54,7 +55,7 @@ extern int isexpired (const struct passwd *, /*@null@*/const struct spwd *); - extern int btrfs_create_subvolume(const char *path); - extern int btrfs_remove_subvolume(const char *path); - extern int btrfs_is_subvolume(const char *path); --extern int is_btrfs(const char *path); -+extern bool is_btrfs(const struct statfs *sfs); - #endif - - /* basename() renamed to Basename() to avoid libc name space confusion */ -diff --git a/src/useradd.c b/src/useradd.c -index df679d28..5ce6cd6a 100644 ---- a/src/useradd.c -+++ b/src/useradd.c -@@ -25,6 +25,7 @@ - #include - #include - #include -+#include - #include - #include - #include -@@ -2265,6 +2266,7 @@ static void create_home(const struct option_flags *flags) - #if WITH_BTRFS - if (subvolflg && (strlen(prefix_user_home) - (int)strlen(path)) <= 1) { - char *btrfs_check = strdup(path); -+ struct statfs sfs; - - if (!btrfs_check) { - fprintf(stderr, -@@ -2273,13 +2275,18 @@ static void create_home(const struct option_flags *flags) - fail_exit(E_HOMEDIR, process_selinux); - } - stpcpy(&btrfs_check[strlen(path) - strlen(cp) - 1], ""); -- if (is_btrfs(btrfs_check) <= 0) { -+ if (statfs(btrfs_check, &sfs) == -1) { -+ fprintf(stderr, "%s: statfs(\"%s\"): %s\n", -+ Prog, btrfs_check, strerrno()); -+ fail_exit(E_HOMEDIR, process_selinux); -+ } -+ free(btrfs_check); -+ if (!is_btrfs(&sfs)) { - fprintf(stderr, - _("%s: home directory \"%s\" must be mounted on BTRFS\n"), - Prog, path); - fail_exit(E_HOMEDIR, process_selinux); - } -- free(btrfs_check); - // make subvolume to mount for user instead of directory - if (btrfs_create_subvolume(path)) { - fprintf(stderr, --- -2.53.0 - - -From 96460c7240264c0022cebc9ba1774993ee2594cf Mon Sep 17 00:00:00 2001 -From: Alejandro Colomar -Date: Wed, 22 Apr 2026 11:21:14 +0200 -Subject: [PATCH 3/5] lib/, src/: Move btrfs.c prototypes to lib/btrfs.h - -Signed-off-by: Alejandro Colomar ---- - lib/Makefile.am | 2 +- - lib/btrfs.c | 4 ++++ - lib/btrfs.h | 23 +++++++++++++++++++++++ - lib/prototypes.h | 9 --------- - src/useradd.c | 1 + - src/userdel.c | 1 + - src/usermod.c | 1 + - 7 files changed, 31 insertions(+), 10 deletions(-) - create mode 100644 lib/btrfs.h - -diff --git a/lib/Makefile.am b/lib/Makefile.am -index 7c488f82..0664e610 100644 ---- a/lib/Makefile.am -+++ b/lib/Makefile.am -@@ -290,7 +290,7 @@ libshadow_la_SOURCES += tcbfuncs.c tcbfuncs.h - endif - - if WITH_BTRFS --libshadow_la_SOURCES += btrfs.c -+libshadow_la_SOURCES += btrfs.c btrfs.h - endif - - if ENABLE_LASTLOG -diff --git a/lib/btrfs.c b/lib/btrfs.c -index 065c7938..15b6b315 100644 ---- a/lib/btrfs.c -+++ b/lib/btrfs.c -@@ -1,3 +1,7 @@ -+#include "config.h" -+ -+#include "btrfs.h" -+ - #include - #include - #include -diff --git a/lib/btrfs.h b/lib/btrfs.h -new file mode 100644 -index 00000000..50b70488 ---- /dev/null -+++ b/lib/btrfs.h -@@ -0,0 +1,23 @@ -+// SPDX-FileCopyrightText: 2026, Alejandro Colomar -+// SPDX-License-Identifier: BSD-3-Clause -+ -+ -+#ifndef SHADOW_INCLUDE_LIB_BTRFS_H_ -+#define SHADOW_INCLUDE_LIB_BTRFS_H_ -+ -+ -+#include "config.h" -+ -+#include -+#include -+ -+ -+#ifdef WITH_BTRFS -+int btrfs_create_subvolume(const char *path); -+int btrfs_remove_subvolume(const char *path); -+int btrfs_is_subvolume(const char *path); -+bool is_btrfs(const struct statfs *sfs); -+#endif -+ -+ -+#endif // include guard -diff --git a/lib/prototypes.h b/lib/prototypes.h -index 42aa2923..d22c477a 100644 ---- a/lib/prototypes.h -+++ b/lib/prototypes.h -@@ -23,7 +23,6 @@ - - #include - #include --#include - #include - #include - #include -@@ -50,14 +49,6 @@ extern int expire (const struct passwd *, /*@null@*/const struct spwd *); - /* isexpired.c */ - extern int isexpired (const struct passwd *, /*@null@*/const struct spwd *); - --/* btrfs.c */ --#ifdef WITH_BTRFS --extern int btrfs_create_subvolume(const char *path); --extern int btrfs_remove_subvolume(const char *path); --extern int btrfs_is_subvolume(const char *path); --extern bool is_btrfs(const struct statfs *sfs); --#endif -- - /* basename() renamed to Basename() to avoid libc name space confusion */ - /* basename.c */ - extern /*@observer@*/const char *Basename (const char *str); -diff --git a/src/useradd.c b/src/useradd.c -index 5ce6cd6a..9bd32b11 100644 ---- a/src/useradd.c -+++ b/src/useradd.c -@@ -34,6 +34,7 @@ - #include "alloc/malloc.h" - #include "atoi/a2i.h" - #include "atoi/getnum.h" -+#include "btrfs.h" - #include "chkname.h" - #include "defines.h" - #include "faillog.h" -diff --git a/src/userdel.c b/src/userdel.c -index 9b7d81be..ebd064c0 100644 ---- a/src/userdel.c -+++ b/src/userdel.c -@@ -20,6 +20,7 @@ - #include - #include - -+#include "btrfs.h" - #ifdef ACCT_TOOLS_SETUID - #ifdef USE_PAM - #include "pam_defs.h" -diff --git a/src/usermod.c b/src/usermod.c -index 7c0321a6..3afe19ea 100644 ---- a/src/usermod.c -+++ b/src/usermod.c -@@ -32,6 +32,7 @@ - #include "alloc/malloc.h" - #include "atoi/a2i.h" - #include "atoi/getnum.h" -+#include "btrfs.h" - #include "chkname.h" - #include "defines.h" - #include "faillog.h" --- -2.53.0 - - -From 7be9a6540f1cad477fd4f4e83d25904a5095bb86 Mon Sep 17 00:00:00 2001 -From: Hadi Chokr -Date: Tue, 21 Apr 2026 09:18:19 +0200 -Subject: [PATCH 4/5] useradd(8): fallback to regular dir for BTRFS home on - non-BTRFS parent - -When the --btrfs-subvolume-home option is used but the parent directory -is not on a BTRFS filesystem, useradd previously failed with an error. -This is too strict; instead, fall back to creating a regular directory -and issue a warning. The subvolume creation is attempted only when the -parent is BTRFS. Otherwise, a regular directory is created and a -syslog(3) warning is logged. - -Fixes: 3e8c105 (2026-01-02; "src/useradd: Support config for creating home dirs as Btrfs subvolumes") -Co-authored-by: Hadi Chokr -Co-authored-by: Alejandro Colomar -Signed-off-by: Alejandro Colomar ---- - man/useradd.8.xml | 27 ++++++++++++++++++++++-- - src/useradd.c | 52 +++++++++++++++++++++++------------------------ - 2 files changed, 51 insertions(+), 28 deletions(-) - -diff --git a/man/useradd.8.xml b/man/useradd.8.xml -index 8a087e13..0bde6683 100644 ---- a/man/useradd.8.xml -+++ b/man/useradd.8.xml -@@ -2,6 +2,7 @@ - - - -- Note: this feature works only if the underlying filesystem supports -- Btrfs subvolumes. -+ If the parent directory of the user's home directory is -+ not on a Btrfs filesystem, -+ useradd will not create a -+ subvolume. -+ Instead, it creates a regular directory, -+ prints a warning to standard error, -+ and logs the event via syslog at level -+ LOG_WARN. -+ The user account is still created successfully. -+ -+ -+ If the filesystem type cannot be determined (e.g., because of -+ insufficient permissions, an I/O error, -+ or a -+ -+ statfs -+ 2 -+ -+ failure), -+ useradd treats this as a fatal error: -+ the home directory is not created, -+ the command exits with a non‑zero status -+ (E_HOMEDIR, 12), -+ and an error message is printed. - - - -diff --git a/src/useradd.c b/src/useradd.c -index 9bd32b11..9210379c 100644 ---- a/src/useradd.c -+++ b/src/useradd.c -@@ -1,11 +1,11 @@ --/* -- * SPDX-FileCopyrightText: 1991 - 1994, Julianne Frances Haugh -- * SPDX-FileCopyrightText: 1996 - 2000, Marek Michałkiewicz -- * SPDX-FileCopyrightText: 2000 - 2006, Tomasz Kłoczko -- * SPDX-FileCopyrightText: 2007 - 2012, Nicolas François -- * -- * SPDX-License-Identifier: BSD-3-Clause -- */ -+// SPDX-FileCopyrightText: 1991-1994, Julianne Frances Haugh -+// SPDX-FileCopyrightText: 1996-2000, Marek Michałkiewicz -+// SPDX-FileCopyrightText: 2000-2006, Tomasz Kłoczko -+// SPDX-FileCopyrightText: 2007-2012, Nicolas François -+// SPDX-FileCopyrightText: 2025-2026, Hadi Chokr -+// SPDX-FileCopyrightText: 2026, Alejandro Colomar -+// SPDX-License-Identifier: BSD-3-Clause -+ - - #include "config.h" - -@@ -2251,6 +2251,8 @@ static void create_home(const struct option_flags *flags) - owner root:root. - */ - for (cp = strtok(bhome, "/"); cp != NULL; cp = strtok(NULL, "/")) { -+ bool dir_created; -+ - /* Avoid turning a relative path into an absolute path. */ - if (strprefix(bhome, "/") || !streq(path, "")) - strcat(path, "/"); -@@ -2260,10 +2262,7 @@ static void create_home(const struct option_flags *flags) - continue; - } - -- /* Check if parent directory is BTRFS, fail if requesting -- subvolume but no BTRFS. The paths could be different by the -- trailing slash -- */ -+ dir_created = false; - #if WITH_BTRFS - if (subvolflg && (strlen(prefix_user_home) - (int)strlen(path)) <= 1) { - char *btrfs_check = strdup(path); -@@ -2284,25 +2283,26 @@ static void create_home(const struct option_flags *flags) - free(btrfs_check); - if (!is_btrfs(&sfs)) { - fprintf(stderr, -- _("%s: home directory \"%s\" must be mounted on BTRFS\n"), -- Prog, path); -- fail_exit(E_HOMEDIR, process_selinux); -+ _("%s: warning: \"%s\" is not on BTRFS; creating regular directory instead of subvolume\n"), -+ Prog, prefix_user_home); -+ } else { -+ if (btrfs_create_subvolume(path)) { -+ fprintf(stderr, -+ _("%s: failed to create BTRFS subvolume: %s\n"), -+ Prog, path); -+ fail_exit(E_HOMEDIR, process_selinux); -+ } -+ dir_created = true; - } -- // make subvolume to mount for user instead of directory -- if (btrfs_create_subvolume(path)) { -- fprintf(stderr, -- _("%s: failed to create BTRFS subvolume: %s\n"), -+ } -+#endif -+ if (!dir_created) { -+ if (mkdir(path, 0) != 0) { -+ fprintf(stderr, _("%s: cannot create directory %s\n"), - Prog, path); - fail_exit(E_HOMEDIR, process_selinux); - } - } -- else --#endif -- if (mkdir(path, 0) != 0) { -- fprintf(stderr, _("%s: cannot create directory %s\n"), -- Prog, path); -- fail_exit(E_HOMEDIR, process_selinux); -- } - if (chown(path, 0, 0) < 0) { - fprintf(stderr, - _("%s: warning: chown on `%s' failed: %m\n"), --- -2.53.0 - - -From 827f69b864461ab6d7549762bef06ab4495d2587 Mon Sep 17 00:00:00 2001 -From: Hadi Chokr -Date: Mon, 20 Apr 2026 12:27:31 +0200 -Subject: [PATCH 5/5] man/useradd.8.xml: Remove trailing spaces from - useradd.8.xml - -Signed-off-by: Hadi Chokr -Reviewed-by: Alejandro Colomar ---- - man/useradd.8.xml | 6 +++--- - 1 file changed, 3 insertions(+), 3 deletions(-) - -diff --git a/man/useradd.8.xml b/man/useradd.8.xml -index 0bde6683..f85780e2 100644 ---- a/man/useradd.8.xml -+++ b/man/useradd.8.xml -@@ -207,7 +207,7 @@ - user's login directory. The default is to append the - LOGIN name to - BASE_DIR and use that as the -- login directory name. -+ login directory name. - The directory HOME_DIR is not created by - default. However it will be created for non-system users if either the - flag is specifed or -@@ -422,7 +422,7 @@ - - - -- Create the user's home directory if it does not exist. -+ Create the user's home directory if it does not exist. - The files and directories contained in the skeleton directory - (which can be defined with the option) - will be copied to the home directory. -@@ -512,7 +512,7 @@ - password himself. - - -- Note:Avoid this option on the command -+ Note:Avoid this option on the command - line because the password (or encrypted password) will - be visible by users listing the processes. - --- -2.53.0 - diff --git a/shadow-4.19.0-usermod-add-optimizations.patch b/shadow-4.19.0-usermod-add-optimizations.patch deleted file mode 100644 index 4a883e6..0000000 --- a/shadow-4.19.0-usermod-add-optimizations.patch +++ /dev/null @@ -1,75 +0,0 @@ -From 958b4859991e700b61af2f9e07e3aa87ad1d9218 Mon Sep 17 00:00:00 2001 -From: Alejandro Colomar -Date: Sat, 17 Jan 2026 00:56:21 +0100 -Subject: [PATCH] Revert "src/usermod.c: Remove optimizations" - -This wasn't only an optimization; it also skipped some checks that were -now spuriously triggering errors. We may be able to get rid of the -optimizations, but that will need more analysis. For now, let's revert -to a known-good state. - -Fixes: 6a8a25dc7de6 (2025-10-15; "src/usermod.c: Remove optimizations") -Reverts: 6a8a25dc7de6 (2025-10-15; "src/usermod.c: Remove optimizations") -Closes: -Reported-by: Adam Williamson -Signed-off-by: Alejandro Colomar ---- - src/usermod.c | 42 ++++++++++++++++++++++++++++++++++++++++++ - 1 file changed, 42 insertions(+) - -diff --git a/src/usermod.c b/src/usermod.c -index 7400bf84..7cd7a859 100644 ---- a/src/usermod.c -+++ b/src/usermod.c -@@ -1391,6 +1391,48 @@ process_flags(int argc, char **argv, struct option_flags *flags) - } - #endif /* WITH_SELINUX */ - -+ if (user_newid == user_id) { -+ uflg = false; -+ oflg = false; -+ } -+ if (user_newgid == user_gid) { -+ gflg = false; -+ } -+ if ( (NULL != user_newshell) -+ && streq(user_newshell, user_shell)) { -+ sflg = false; -+ } -+ if (streq(user_newname, user_name)) { -+ lflg = false; -+ } -+ if (user_newinactive == user_inactive) { -+ fflg = false; -+ } -+ if (user_newexpire == user_expire) { -+ eflg = false; -+ } -+ if ( (NULL != user_newhome) -+ && streq(user_newhome, user_home)) { -+ dflg = false; -+ mflg = false; -+ } -+ if ( (NULL != user_newcomment) -+ && streq(user_newcomment, user_comment)) { -+ cflg = false; -+ } -+ -+ if (!(Uflg || uflg || sflg || pflg || mflg || Lflg || -+ lflg || Gflg || gflg || fflg || eflg || dflg || cflg -+#ifdef ENABLE_SUBIDS -+ || vflg || Vflg || wflg || Wflg -+#endif /* ENABLE_SUBIDS */ -+#ifdef WITH_SELINUX -+ || Zflg -+#endif /* WITH_SELINUX */ -+ )) { -+ exit (E_SUCCESS); -+ } -+ - if (!is_shadow_pwd && (eflg || fflg)) { - fprintf (stderr, - _("%s: shadow passwords required for -e and -f\n"), --- -2.52.0 - diff --git a/shadow-utils.spec b/shadow-utils.spec index e62258a..7bc7e8b 100644 --- a/shadow-utils.spec +++ b/shadow-utils.spec @@ -1,12 +1,12 @@ Summary: Utilities for managing accounts and shadow password files Name: shadow-utils -Version: 4.19.0 -Release: 7%{?dist} +Version: 4.20.0 +Release: 1%{?dist} Epoch: 2 License: BSD-3-Clause AND GPL-2.0-or-later URL: https://github.com/shadow-maint/shadow -Source0: https://github.com/shadow-maint/shadow/releases/download/4.19.0/shadow-4.19.0.tar.xz -Source1: https://github.com/shadow-maint/shadow/releases/download/4.19.0/shadow-4.19.0.tar.xz.asc +Source0: https://github.com/shadow-maint/shadow/releases/download/4.20.0/shadow-4.20.0.tar.xz +Source1: https://github.com/shadow-maint/shadow/releases/download/4.20.0/shadow-4.20.0.tar.xz.asc Source2: shadow-utils.useradd Source3: shadow-utils.login.defs Source4: shadow-bsd.txt @@ -20,25 +20,6 @@ Source7: passwd.pamd %global _ld_strict_symbol_defs 1 ### Patches ### -# Misc manual page changes - non-upstreamable -Patch0: shadow-4.15.0-manfix.patch -# Probably non-upstreamable -Patch1: shadow-4.19.0-account-tools-setuid.patch -# https://github.com/shadow-maint/shadow/commit/3e8c105f0703264e947d8c034b90419794955d49 -Patch2: shadow-4-19-useradd-support-btrfs.patch -# https://github.com/shadow-maint/shadow/commit/3e8c105f0703264e947d8c034b90419794955d49 -Patch3: shadow-4.19.0-chkhash1.patch -# https://github.com/shadow-maint/shadow/commit/9b67543987e3d140c86f1b8e2b5db5c10d8bc3c5 -Patch4: shadow-4.19.0-chkhash2.patch -# https://github.com/shadow-maint/shadow/commit/958b4859991e700b61af2f9e07e3aa87ad1d9218 -Patch5: shadow-4.19.0-usermod-add-optimizations.patch -# https://github.com/shadow-maint/shadow/pull/1520 -# Approved by upstream for 4.19.3 this weekend: -# https://github.com/shadow-maint/shadow/issues/1521 -# Fixes the hash check to accept hashes with \ or n in the salt -Patch6: 1520.patch -# https://github.com/shadow-maint/shadow/commit/827f69b864461ab6d7549762bef06ab4495d2587 -Patch7: shadow-4.19.0-useradd-fix-btrfs.patch ### Dependencies ### Requires: audit-libs >= 1.6.5 @@ -117,7 +98,7 @@ Requires: shadow-utils-subid = %{epoch}:%{version}-%{release} Development files for shadow-utils-subid. %prep -%autosetup -p 1 -S git -n shadow-4.19.0 +%autosetup -p 1 -S git -n shadow-4.20.0 iconv -f ISO88591 -t utf-8 doc/HOWTO > doc/HOWTO.utf8 cp -f doc/HOWTO.utf8 doc/HOWTO @@ -155,6 +136,8 @@ install -d -m 755 $RPM_BUILD_ROOT%{_sysconfdir}/default install -p -c -m 0644 %{SOURCE3} $RPM_BUILD_ROOT%{_sysconfdir}/login.defs install -p -c -m 0600 %{SOURCE2} $RPM_BUILD_ROOT%{_sysconfdir}/default/useradd install -d -m 755 $RPM_BUILD_ROOT%{_pam_confdir} +install -m 644 %{SOURCE7} $RPM_BUILD_ROOT%{_pam_confdir}/chpasswd +install -m 644 %{SOURCE7} $RPM_BUILD_ROOT%{_pam_confdir}/newusers install -m 644 %{SOURCE7} $RPM_BUILD_ROOT%{_pam_confdir}/passwd @@ -172,26 +155,20 @@ mv -v $RPM_BUILD_ROOT/usr/sbin/* $RPM_BUILD_ROOT%{_bindir}/ # Remove binaries we don't use. rm $RPM_BUILD_ROOT%{_bindir}/chfn rm $RPM_BUILD_ROOT%{_bindir}/chsh -rm $RPM_BUILD_ROOT%{_bindir}/expiry rm $RPM_BUILD_ROOT%{_bindir}/login rm $RPM_BUILD_ROOT%{_bindir}/su rm $RPM_BUILD_ROOT%{_bindir}/faillog -rm $RPM_BUILD_ROOT%{_sbindir}/logoutd rm $RPM_BUILD_ROOT%{_sbindir}/nologin rm $RPM_BUILD_ROOT%{_mandir}/man1/chfn.* rm $RPM_BUILD_ROOT%{_mandir}/*/man1/chfn.* rm $RPM_BUILD_ROOT%{_mandir}/man1/chsh.* rm $RPM_BUILD_ROOT%{_mandir}/*/man1/chsh.* -rm $RPM_BUILD_ROOT%{_mandir}/man1/expiry.* -rm $RPM_BUILD_ROOT%{_mandir}/*/man1/expiry.* rm $RPM_BUILD_ROOT%{_mandir}/man1/login.* rm $RPM_BUILD_ROOT%{_mandir}/*/man1/login.* rm $RPM_BUILD_ROOT%{_mandir}/man1/su.* rm $RPM_BUILD_ROOT%{_mandir}/*/man1/su.* rm $RPM_BUILD_ROOT%{_mandir}/man5/passwd.* rm $RPM_BUILD_ROOT%{_mandir}/*/man5/passwd.* -rm $RPM_BUILD_ROOT%{_mandir}/man8/logoutd.* -rm $RPM_BUILD_ROOT%{_mandir}/*/man8/logoutd.* rm $RPM_BUILD_ROOT%{_mandir}/man8/nologin.* rm $RPM_BUILD_ROOT%{_mandir}/*/man8/nologin.* rm $RPM_BUILD_ROOT%{_mandir}/man3/getspnam.* @@ -203,11 +180,8 @@ rm $RPM_BUILD_ROOT%{_mandir}/*/man8/faillog.* # Remove PAM service files we don't use. rm $RPM_BUILD_ROOT%{_pam_confdir}/chfn -rm $RPM_BUILD_ROOT%{_pam_confdir}/chpasswd rm $RPM_BUILD_ROOT%{_pam_confdir}/chsh -rm $RPM_BUILD_ROOT%{_pam_confdir}/groupmems rm $RPM_BUILD_ROOT%{_pam_confdir}/login -rm $RPM_BUILD_ROOT%{_pam_confdir}/newusers rm $RPM_BUILD_ROOT%{_pam_confdir}/su find $RPM_BUILD_ROOT%{_mandir} -depth -type d -empty -delete @@ -234,6 +208,8 @@ rm -f $RPM_BUILD_ROOT/%{_libdir}/libsubid.a %license gpl-2.0.txt shadow-bsd.txt %attr(0644,root,root) %config(noreplace) %{_sysconfdir}/login.defs %attr(0644,root,root) %config(noreplace) %{_sysconfdir}/default/useradd +%config(noreplace) %{_pam_confdir}/chpasswd +%config(noreplace) %{_pam_confdir}/newusers %config(noreplace) %{_pam_confdir}/passwd %{_bindir}/sg %attr(4755,root,root) %{_bindir}/chage @@ -288,9 +264,26 @@ rm -f $RPM_BUILD_ROOT/%{_libdir}/libsubid.a %{_libdir}/libsubid.so %changelog -* Wed Apr 29 2026 Iker Pedrosa - 2:4.19.0-7 +* Wed Jul 29 2026 Iker Pedrosa - 2:4.20.0-1 +- Rebase to version 4.20.0 + +* Wed Jul 22 2026 Iker Pedrosa - 2:4.20.0-rc3-1 +- Rebase to version 4.20.0-rc3 + +* Fri Jul 17 2026 Fedora Release Engineering - 2:4.19.3-4 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_45_Mass_Rebuild + +* Wed May 27 2026 Iker Pedrosa - 2:4.19.3-3 +- Enable use of PAM for chpasswd and newusers + Resolves: #2461179 and #2283963 + +* Thu Apr 23 2026 Iker Pedrosa - 2:4.19.3-2 - btrfs: simplify checks improve useradd behavior for non-btrfs +* Wed Feb 11 2026 Debarshi Ray - 2:4.19.3-1 +- Rebase to version 4.19.3 + Resolves: #2426288 + * Tue Jan 27 2026 Adam Williamson - 2:4.19.0-6 - chkhash.c: fix escaping in SHA-256 / SHA-512 / MD5 regexes diff --git a/sources b/sources index 22ae907..7669083 100644 --- a/sources +++ b/sources @@ -1,2 +1,2 @@ -SHA512 (shadow-4.19.0.tar.xz) = 0846c34f426101213ab6277bbcdc58c28b3a54df2f68d1c1addcc8c5aa79ce3e15d5d8dfcb9cc6dbd1b758e0cd8676c68ca65268f5a037d4c24aa7d358c9a390 -SHA512 (shadow-4.19.0.tar.xz.asc) = 34c1a764d06a5d24533cbb57310de7462eb632c1de904901699d48f44401b1b1f4751db35f2b0e2b9a057cf1a777ccddbae4bc6b19a6db865ebc3e30fde0d060 +SHA512 (shadow-4.20.0.tar.xz) = 0b8afded372e4d37a78f38cb972c0ab877870ef6356cdd1c45be3c708af3d1496c6f87de6bf6a5b1d217d4d52d7e2d15c28b26530949cc6de0231a4028930406 +SHA512 (shadow-4.20.0.tar.xz.asc) = e4b134543768f323df30a1e450c22086d013020928d62bd3207db7f633beea3587b67802da5b575d5a794e8c2bebd7a0abeba6ed69d44bf08adc37df3bf352ac diff --git a/tests/mhc-fedora-ci.yaml b/tests/mhc-fedora-ci.yaml new file mode 100644 index 0000000..8747e67 --- /dev/null +++ b/tests/mhc-fedora-ci.yaml @@ -0,0 +1,13 @@ +provisioned_topologies: +- shadow +domains: +- id: shadow + hosts: + - hostname: localhost + role: shadow + conn: + type: ssh + host: localhost + user: root + artifacts: + - /var/log/*