diff --git a/SOURCES/pam-1.5.1-pam-faillock-skip.patch b/SOURCES/pam-1.5.1-pam-faillock-skip.patch new file mode 100644 index 0000000..c54364f --- /dev/null +++ b/SOURCES/pam-1.5.1-pam-faillock-skip.patch @@ -0,0 +1,62 @@ +diff -up Linux-PAM-1.5.1/modules/pam_faillock/pam_faillock.8.xml.pam-faillock-skip Linux-PAM-1.5.1/modules/pam_faillock/pam_faillock.8.xml +--- Linux-PAM-1.5.1/modules/pam_faillock/pam_faillock.8.xml.pam-faillock-skip 2025-12-10 16:12:22.912937203 +0100 ++++ Linux-PAM-1.5.1/modules/pam_faillock/pam_faillock.8.xml 2025-12-10 16:12:48.429569598 +0100 +@@ -240,6 +240,14 @@ + user accounts allowing the adversary to infer that a particular account + is not existing on a system. + ++ ++ If the stack has not been run prior to the ++ stack, the logic to reset the failed login counter is intentionally skipped. This prevents ++ automated services, such as crond or systemd-user, ++ which might only perform account management tasks, from inadvertently clearing a user's ++ failed attempt records. This ensures the faillock counter is only reset by a service that ++ performs a full, successful authentication. ++ + + + +diff -up Linux-PAM-1.5.1/modules/pam_faillock/pam_faillock.c.pam-faillock-skip Linux-PAM-1.5.1/modules/pam_faillock/pam_faillock.c +--- Linux-PAM-1.5.1/modules/pam_faillock/pam_faillock.c.pam-faillock-skip 2025-12-10 16:12:22.924887130 +0100 ++++ Linux-PAM-1.5.1/modules/pam_faillock/pam_faillock.c 2025-12-10 16:12:22.969905176 +0100 +@@ -61,6 +61,8 @@ + #define FAILLOCK_ACTION_AUTHSUCC 1 + #define FAILLOCK_ACTION_AUTHFAIL 2 + ++#define FAILLOCK_AUTH_EXECUTED "pam_faillock:auth_executed" ++ + static int + args_parse(pam_handle_t *pamh, int argc, const char **argv, + int flags, struct options *opts) +@@ -452,6 +454,10 @@ pam_sm_authenticate(pam_handle_t *pamh, + goto err; + } + ++ rv = pam_set_data(pamh, FAILLOCK_AUTH_EXECUTED, (void *)1, NULL); ++ if (rv != PAM_SUCCESS) ++ goto err; ++ + if (!(opts.flags & FAILLOCK_FLAG_LOCAL_ONLY) || + check_local_user (pamh, opts.user) != 0) { + switch (opts.action) { +@@ -505,6 +511,7 @@ pam_sm_acct_mgmt(pam_handle_t *pamh, int + struct options opts; + int rv, fd = -1; + struct tally_data tallies; ++ const void *auth_flag; + + memset(&tallies, 0, sizeof(tallies)); + +@@ -515,6 +522,12 @@ pam_sm_acct_mgmt(pam_handle_t *pamh, int + + opts.action = FAILLOCK_ACTION_AUTHSUCC; + ++ rv = pam_get_data(pamh, FAILLOCK_AUTH_EXECUTED, &auth_flag); ++ if (rv == PAM_NO_MODULE_DATA) { ++ rv = PAM_SUCCESS; ++ goto err; ++ } ++ + if ((rv=get_pam_user(pamh, &opts)) != PAM_SUCCESS) { + goto err; + } diff --git a/SOURCES/pam-1.5.1-pam-unix-blank-expiration.patch b/SOURCES/pam-1.5.1-pam-unix-blank-expiration.patch new file mode 100644 index 0000000..50c3d8e --- /dev/null +++ b/SOURCES/pam-1.5.1-pam-unix-blank-expiration.patch @@ -0,0 +1,104 @@ +diff -up Linux-PAM-1.5.1/modules/pam_unix/passverify.c.pam-unix-blank-expiration Linux-PAM-1.5.1/modules/pam_unix/passverify.c +--- Linux-PAM-1.5.1/modules/pam_unix/passverify.c.pam-unix-blank-expiration 2025-04-14 14:40:34.181165272 +0200 ++++ Linux-PAM-1.5.1/modules/pam_unix/passverify.c 2025-04-14 14:42:11.740385381 +0200 +@@ -5,6 +5,7 @@ + #include + #include + #include "support.h" ++#include + #include + #include + #include +@@ -286,14 +286,29 @@ PAMH_ARG_DECL(int get_pwd_hash, + return PAM_SUCCESS; + } + ++/* ++ * invariant: 0 <= num1 ++ * invariant: 0 <= num2 ++ */ ++static int ++subtract(long num1, long num2) ++{ ++ long value = num1 - num2; ++ if (value < INT_MIN) ++ return INT_MIN; ++ if (value > INT_MAX) ++ return INT_MAX; ++ return (int)value; ++} ++ + PAMH_ARG_DECL(int check_shadow_expiry, + struct spwd *spent, int *daysleft) + { +- long int curdays; ++ long int curdays, passed; + *daysleft = -1; + curdays = (long int)(time(NULL) / (60 * 60 * 24)); +- D(("today is %d, last change %d", curdays, spent->sp_lstchg)); +- if ((curdays >= spent->sp_expire) && (spent->sp_expire != -1)) { ++ D(("today is %ld, last change %ld", curdays, spent->sp_lstchg)); ++ if (spent->sp_expire >= 0 && curdays >= spent->sp_expire) { + D(("account expired")); + return PAM_ACCT_EXPIRED; + } +@@ -308,31 +323,41 @@ PAMH_ARG_DECL(int check_shadow_expiry, + *daysleft = 0; + return PAM_NEW_AUTHTOK_REQD; + } ++ if (spent->sp_lstchg < 0) { ++ D(("password aging disabled")); ++ return PAM_SUCCESS; ++ } + if (curdays < spent->sp_lstchg) { + pam_syslog(pamh, LOG_DEBUG, + "account %s has password changed in future", + spent->sp_namp); + return PAM_SUCCESS; + } +- if ((curdays - spent->sp_lstchg > spent->sp_max) +- && (curdays - spent->sp_lstchg > spent->sp_inact) +- && (curdays - spent->sp_lstchg > spent->sp_max + spent->sp_inact) +- && (spent->sp_max != -1) && (spent->sp_inact != -1)) { +- *daysleft = (int)((spent->sp_lstchg + spent->sp_max) - curdays); +- D(("authtok expired")); +- return PAM_AUTHTOK_EXPIRED; +- } +- if ((curdays - spent->sp_lstchg > spent->sp_max) && (spent->sp_max != -1)) { +- D(("need a new password 2")); +- return PAM_NEW_AUTHTOK_REQD; +- } +- if ((curdays - spent->sp_lstchg > spent->sp_max - spent->sp_warn) +- && (spent->sp_max != -1) && (spent->sp_warn != -1)) { +- *daysleft = (int)((spent->sp_lstchg + spent->sp_max) - curdays); +- D(("warn before expiry")); ++ passed = curdays - spent->sp_lstchg; ++ if (spent->sp_max >= 0) { ++ if (spent->sp_inact >= 0) { ++ long inact = spent->sp_max < LONG_MAX - spent->sp_inact ? ++ spent->sp_max + spent->sp_inact : LONG_MAX; ++ if (passed >= inact) { ++ *daysleft = subtract(inact, passed); ++ D(("authtok expired")); ++ return PAM_AUTHTOK_EXPIRED; ++ } ++ } ++ if (passed >= spent->sp_max) { ++ D(("need a new password 2")); ++ return PAM_NEW_AUTHTOK_REQD; ++ } ++ if (spent->sp_warn > 0) { ++ long warn = spent->sp_warn > spent->sp_max ? -1 : ++ spent->sp_max - spent->sp_warn; ++ if (passed >= warn) { ++ *daysleft = subtract(spent->sp_max, passed); ++ D(("warn before expiry")); ++ } ++ } + } +- if ((curdays - spent->sp_lstchg < spent->sp_min) +- && (spent->sp_min != -1)) { ++ if (spent->sp_min > 0 && passed < spent->sp_min) { + /* + * The last password change was too recent. This error will be ignored + * if no password change is attempted. diff --git a/SOURCES/pamtmp.conf b/SOURCES/pamtmp.conf index ab2237f..e4cfe2a 100644 --- a/SOURCES/pamtmp.conf +++ b/SOURCES/pamtmp.conf @@ -2,4 +2,3 @@ d /run/console 0755 root root - d /run/faillock 0755 root root - d /run/sepermit 0755 root root - d /run/motd.d 0755 root root - -f /var/log/tallylog 0600 root root - diff --git a/SPECS/pam.spec b/SPECS/pam.spec index 36920d2..1d07632 100644 --- a/SPECS/pam.spec +++ b/SPECS/pam.spec @@ -3,7 +3,7 @@ Summary: An extensible library which provides authentication for applications Name: pam Version: 1.5.1 -Release: 26%{?dist} +Release: 28%{?dist} # The library is BSD licensed with option to relicense as GPLv2+ # - this option is redundant as the BSD license allows that anyway. # pam_timestamp, pam_loginuid, and pam_console modules are GPLv2+. @@ -75,10 +75,16 @@ Patch22: pam-1.5.1-pam-unix-shadow-password.patch Patch23: pam-1.5.1-pam-access-local.patch # https://github.com/linux-pam/linux-pam/commit/940747f88c16e029b69a74e80a2e94f65cb3e628 Patch24: pam-1.5.1-pam-access-resolve-ip.patch +# https://github.com/linux-pam/linux-pam/commit/2c711ce57ced9f97c2cf4c8d59c1730447a7bd7f +# https://github.com/linux-pam/linux-pam/commit/51a06bc8cc2278c6e81c9c08a9381c9eb0d2de96 +# https://github.com/linux-pam/linux-pam/commit/470b5bdd8fd29d6b35e3a80f9a57bdd4b2438200 +Patch25: pam-1.5.1-pam-unix-blank-expiration.patch # https://github.com/linux-pam/linux-pam/commit/10b80543807e3fc5af5f8bcfd8bb6e219bb3cecc -Patch25: pam-1.5.1-pam-inline-pam-asprintf.patch +Patch26: pam-1.5.1-pam-inline-pam-asprintf.patch # https://github.com/linux-pam/linux-pam/commit/475bd60c552b98c7eddb3270b0b4196847c0072e -Patch26: pam-1.5.1-pam-namespace-rebase.patch +Patch27: pam-1.5.1-pam-namespace-rebase.patch +# https://github.com/linux-pam/linux-pam/commit/7d96d452e65ba5dec73f2c77104113977dd3aeb1 +Patch28: pam-1.5.1-pam-faillock-skip.patch %global _pamlibdir %{_libdir} %global _moduledir %{_libdir}/security @@ -185,8 +191,10 @@ cp %{SOURCE18} . %patch22 -p1 -b .pam-unix-shadow-password %patch23 -p1 -b .pam-access-local %patch24 -p1 -b .pam-access-resolve-ip -%patch25 -p1 -b .pam-inline-pam-asprintf -%patch26 -p1 -b .pam-namespace-rebase +%patch25 -p1 -b .pam-unix-blank-expiration +%patch26 -p1 -b .pam-inline-pam-asprintf +%patch27 -p1 -b .pam-namespace-rebase +%patch28 -p1 -b .pam-faillock-skip autoreconf -i @@ -442,9 +450,16 @@ done %doc doc/sag/*.txt doc/sag/html %changelog -* Wed Aug 6 2025 Iker Pedrosa - 1.5.1-26 +* Wed Dec 10 2025 Iker Pedrosa - 1.5.1-28 +- pam_faillock: skip clearing user's failed attempt. + Resolves: RHEL-130875 + +* Tue Aug 5 2025 Iker Pedrosa - 1.5.1-27 +- pam_unix: sync expiry checks with shadow. + Resolves: RHEL-70519, RHEL-95475, RHEL-96955 and RHEL-96973 +- pam.conf: remove tallylog. Resolves: RHEL-15324 - pam_namespace: fix potential privilege escalation. - Resolves: CVE-2025-6020 and RHEL-96729 + Resolves: CVE-2025-6020 and RHEL-96727 * Thu Nov 21 2024 Iker Pedrosa - 1.5.1-23 - pam_access: rework resolving of tokens as hostname.