diff -up Linux-PAM-1.5.1/modules/pam_unix/passverify.c.fail1 Linux-PAM-1.5.1/modules/pam_unix/passverify.c --- Linux-PAM-1.5.1/modules/pam_unix/passverify.c.fail1 2024-11-04 11:42:51.962791265 +0100 +++ Linux-PAM-1.5.1/modules/pam_unix/passverify.c 2024-11-04 11:45:18.246218579 +0100 @@ -239,17 +239,21 @@ PAMH_ARG_DECL(int get_account_info, return PAM_UNIX_RUN_HELPER; #endif } else if (is_pwd_shadowed(*pwd)) { +#ifdef HELPER_COMPILE /* - * ...and shadow password file entry for this user, + * shadow password file entry for this user, * if shadowing is enabled */ -#ifndef HELPER_COMPILE - if (geteuid() || SELINUX_ENABLED) - return PAM_UNIX_RUN_HELPER; -#endif - *spwdent = pam_modutil_getspnam(pamh, name); + *spwdent = getspnam(name); if (*spwdent == NULL || (*spwdent)->sp_pwdp == NULL) return PAM_AUTHINFO_UNAVAIL; +#else + /* + * The helper has to be invoked to deal with + * the shadow password file entry. + */ + return PAM_UNIX_RUN_HELPER; +#endif } } else { return PAM_USER_UNKNOWN; From 8d0c575336ad301cd14e16ad2fdec6fe621764b8 Mon Sep 17 00:00:00 2001 From: Sergei Trofimovich Date: Thu, 28 Mar 2024 21:58:35 +0000 Subject: [PATCH] pam_unix: allow empty passwords with non-empty hashes Before the change pam_unix has different behaviours for a user with empty password for these two `/etc/shadow` entries: nulloktest:$6$Yy4ty2jJ$bsVQWo8qlXC6UHq1/qTC3UR60ZJKmKApJ3Wj7DreAy8FxlVKtlDnplFQ7jMLVlDqordE7e4t49GvTb.aI59TP0:1:::::: nulloktest::1:::::: The entry with a hash was rejected and the entry without was accepted. The rejection happened because 9e74e90147c "pam_unix: avoid determining if user exists" introduced the following rejection check (slightly simplified): ... } else if (p[0] == '\0' && nullok) { if (hash[0] != '\0') { retval = PAM_AUTH_ERR; } We should not reject the user with a hash assuming it's non-empty. The change does that by pushing empty password check into `verify_pwd_hash()`. `NixOS` generates such hashed entries for empty passwords as if they were non-empty using the following perl code: sub hashPassword { my ($password) = @_; my $salt = ""; my @chars = ('.', '/', 0..9, 'A'..'Z', 'a'..'z'); $salt .= $chars[rand 64] for (1..8); return crypt($password, '$6$' . $salt . '$'); } Resolves: https://github.com/linux-pam/linux-pam/issues/758 Fixes: 9e74e90147c "pam_unix: avoid determining if user exists" Signed-off-by: Sergei Trofimovich --- modules/pam_unix/passverify.c | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/modules/pam_unix/passverify.c b/modules/pam_unix/passverify.c index 30045333..1c83f1aa 100644 --- a/modules/pam_unix/passverify.c +++ b/modules/pam_unix/passverify.c @@ -76,9 +76,13 @@ PAMH_ARG_DECL(int verify_pwd_hash, strip_hpux_aging(hash); hash_len = strlen(hash); - if (!hash_len) { + + if (p && p[0] == '\0' && !nullok) { + /* The passed password is empty */ + retval = PAM_AUTH_ERR; + } else if (!hash_len) { /* the stored password is NULL */ - if (nullok) { /* this means we've succeeded */ + if (p && p[0] == '\0' && nullok) { /* this means we've succeeded */ D(("user has empty password - access granted")); retval = PAM_SUCCESS; } else { @@ -1109,12 +1113,6 @@ helper_verify_password(const char *name, const char *p, int nullok) if (pwd == NULL || hash == NULL) { helper_log_err(LOG_NOTICE, "check pass; user unknown"); retval = PAM_USER_UNKNOWN; - } else if (p[0] == '\0' && nullok) { - if (hash[0] == '\0') { - retval = PAM_SUCCESS; - } else { - retval = PAM_AUTH_ERR; - } } else { retval = verify_pwd_hash(p, hash, nullok); } -- 2.47.0