diff --git a/SPECS/pam/CVE-2026-54411.patch b/SPECS/pam/CVE-2026-54411.patch new file mode 100644 index 00000000000..34bd2282390 --- /dev/null +++ b/SPECS/pam/CVE-2026-54411.patch @@ -0,0 +1,154 @@ +From ecd62ad57bab9f1da252b8248dd8425690c54f4a Mon Sep 17 00:00:00 2001 +From: AllSpark +Date: Tue, 28 Jul 2026 12:09:15 +0000 +Subject: [PATCH] pam_userdb: fix password comparison timing leak + +Signed-off-by: Azure Linux Security Servicing Account +Upstream-reference: AI Backport of https://github.com/linux-pam/linux-pam/pull/991/changes/30708d973b63891bf700299ce3ae0f1086398284.patch +--- + libpam/include/pam_inline.h | 21 +++++++++ + modules/pam_userdb/pam_userdb.c | 77 +++++++++++++++++++-------------- + 2 files changed, 66 insertions(+), 32 deletions(-) + +diff --git a/libpam/include/pam_inline.h b/libpam/include/pam_inline.h +index 7721c0b..e9b427f 100644 +--- a/libpam/include/pam_inline.h ++++ b/libpam/include/pam_inline.h +@@ -9,6 +9,7 @@ + #define PAM_INLINE_H + + #include "pam_cc_compat.h" ++#include + #include + #include + #include +@@ -175,4 +176,24 @@ pam_read_passwords(int fd, int npass, char **passwords) + return i; + } + ++ ++/* ++ * Constant-time, case-insensitive string equality check. ++ * Same contract as pam_consttime_streq but uses tolower() on each byte. ++ * Runs for exactly strlen(userinput)+1 iterations regardless of secret. ++ */ ++static inline int ++pam_consttime_strcaseeq(const char *userinput, const char *secret) { ++ volatile const char *u = userinput, *s = secret; ++ volatile int ret = 0; ++ ++ do { ++ ret |= tolower((unsigned char)*u) ^ tolower((unsigned char)*s); ++ ++ s += !!*s; ++ } while (*u++ != '\0'); ++ ++ return ret == 0; ++} ++ + #endif /* PAM_INLINE_H */ +diff --git a/modules/pam_userdb/pam_userdb.c b/modules/pam_userdb/pam_userdb.c +index 297403b..2492242 100644 +--- a/modules/pam_userdb/pam_userdb.c ++++ b/modules/pam_userdb/pam_userdb.c +@@ -255,15 +255,24 @@ user_lookup (pam_handle_t *pamh, const char *database, const char *cryptmode, + } else { + + /* Unknown password encryption method - +- * default to plaintext password storage ++ * default to plaintext password storage. ++ * Use constant-time comparison: strncmp/strncasecmp leak prefix bytes ++ * and the length pre-check leaks the password length (CWE-208). + */ + +- if (strlen(pass) != (size_t)data.dsize) { +- compare = 1; /* wrong password len -> wrong password */ +- } else if (ctrl & PAM_ICASE_ARG) { +- compare = strncasecmp(data.dptr, pass, data.dsize); ++ /* libdb is not guaranteed to produce null-terminated strings */ ++ char *stored = strndup(data.dptr, data.dsize); ++ if (stored == NULL) { ++ pam_syslog(pamh, LOG_CRIT, "strndup failed: data.dptr"); ++ compare = -2; + } else { +- compare = strncmp(data.dptr, pass, data.dsize); ++ if (ctrl & PAM_ICASE_ARG) { ++ compare = pam_consttime_strcaseeq(pass, stored) ? 0 : 1; ++ } else { ++ compare = pam_consttime_streq(pass, stored) ? 0 : 1; ++ } ++ pam_overwrite_string(stored); ++ free(stored); + } + + if (cryptmode && pam_str_skip_icase_prefix(cryptmode, "none") == NULL +@@ -295,36 +304,40 @@ user_lookup (pam_handle_t *pamh, const char *database, const char *cryptmode, + } + + /* now handle the key_only case */ ++ size_t ulen = strlen(user); + for (key = dbm_firstkey(dbm); + key.dptr != NULL; +- key = dbm_nextkey(dbm)) { +- int compare; +- /* first compare the user portion (case sensitive) */ +- compare = strncmp(key.dptr, user, strlen(user)); +- if (compare == 0) { +- /* assume failure */ +- compare = -1; +- /* if we have the divider where we expect it to be... */ +- if (key.dptr[strlen(user)] == '-') { +- saw_user = 1; +- if ((size_t)key.dsize == strlen(user) + 1 + strlen(pass)) { +- if (ctrl & PAM_ICASE_ARG) { +- /* compare the password portion (case insensitive)*/ +- compare = strncasecmp(key.dptr + strlen(user) + 1, +- pass, +- strlen(pass)); +- } else { +- /* compare the password portion (case sensitive) */ +- compare = strncmp(key.dptr + strlen(user) + 1, +- pass, +- strlen(pass)); +- } +- } +- } +- if (compare == 0) { ++ key = dbm_nextkey(dbm)) { ++ /* assume failure */ ++ int compare = -1; ++ ++ /* ++ * First compare the user portion (case sensitive); ++ * user is caller-supplied, so this memcmp leaks nothing secret. ++ */ ++ if ((size_t)key.dsize > ulen && ++ key.dptr[ulen] == '-' && ++ memcmp(key.dptr, user, ulen) == 0) { ++ saw_user = 1; ++ char *stored_pass = strndup(key.dptr + ulen + 1, ++ key.dsize - ulen - 1); ++ if (stored_pass == NULL) { + dbm_close(dbm); +- return 0; /* match */ ++ return -2; + } ++ /* compare the password portion (case (in)sensitive) */ ++ if (ctrl & PAM_ICASE_ARG) { ++ compare = pam_consttime_strcaseeq(pass, stored_pass) ? 0 : 1; ++ } else { ++ compare = pam_consttime_streq(pass, stored_pass) ? 0 : 1; ++ } ++ pam_overwrite_string(stored_pass); ++ free(stored_pass); ++ } ++ ++ if (compare == 0) { ++ dbm_close(dbm); ++ return 0; /* match */ + } + } + dbm_close(dbm); +-- +2.45.4 + diff --git a/SPECS/pam/pam.spec b/SPECS/pam/pam.spec index b323ab5a87a..eb60f8c756d 100644 --- a/SPECS/pam/pam.spec +++ b/SPECS/pam/pam.spec @@ -1,7 +1,7 @@ Summary: Linux Pluggable Authentication Modules Name: pam Version: 1.5.3 -Release: 5%{?dist} +Release: 6%{?dist} License: BSD and GPLv2+ URL: https://github.com/linux-pam/linux-pam Source0: https://github.com/linux-pam/linux-pam/releases/download/v%{version}/Linux-PAM-%{version}.tar.xz @@ -20,6 +20,7 @@ Patch1: CVE-2024-10963.patch Patch2: CVE-2024-10041.patch Patch3: sync_pam_namespace_module_to_version_1.7.0.patch Patch4: CVE-2025-6020.patch +Patch5: CVE-2026-54411.patch %description The Linux PAM package contains Pluggable Authentication Modules used to @@ -108,6 +109,9 @@ EOF %{_libdir}/pkgconfig/pamc.pc %changelog +* Tue Jul 28 2026 Azure Linux Security Servicing Account - 1.5.3-6 +- Patch for CVE-2026-54411 + * Tue Jun 24 2025 Jyoti Kanase - 1.5.3-5 - Add patch for sync_pam_namespace_module_to_version_1.7.0.patch and CVE-2025-6020 diff --git a/toolkit/resources/manifests/package/toolchain_aarch64.txt b/toolkit/resources/manifests/package/toolchain_aarch64.txt index fedcd7bd115..dd32faee55b 100644 --- a/toolkit/resources/manifests/package/toolchain_aarch64.txt +++ b/toolkit/resources/manifests/package/toolchain_aarch64.txt @@ -298,10 +298,10 @@ p11-kit-debuginfo-0.25.0-1.azl3.aarch64.rpm p11-kit-devel-0.25.0-1.azl3.aarch64.rpm p11-kit-server-0.25.0-1.azl3.aarch64.rpm p11-kit-trust-0.25.0-1.azl3.aarch64.rpm -pam-1.5.3-5.azl3.aarch64.rpm -pam-debuginfo-1.5.3-5.azl3.aarch64.rpm -pam-devel-1.5.3-5.azl3.aarch64.rpm -pam-lang-1.5.3-5.azl3.aarch64.rpm +pam-1.5.3-6.azl3.aarch64.rpm +pam-debuginfo-1.5.3-6.azl3.aarch64.rpm +pam-devel-1.5.3-6.azl3.aarch64.rpm +pam-lang-1.5.3-6.azl3.aarch64.rpm patch-2.7.6-10.azl3.aarch64.rpm patch-debuginfo-2.7.6-10.azl3.aarch64.rpm pcre2-10.42-3.azl3.aarch64.rpm diff --git a/toolkit/resources/manifests/package/toolchain_x86_64.txt b/toolkit/resources/manifests/package/toolchain_x86_64.txt index ada6e3a3897..d7c3db27159 100644 --- a/toolkit/resources/manifests/package/toolchain_x86_64.txt +++ b/toolkit/resources/manifests/package/toolchain_x86_64.txt @@ -306,10 +306,10 @@ p11-kit-debuginfo-0.25.0-1.azl3.x86_64.rpm p11-kit-devel-0.25.0-1.azl3.x86_64.rpm p11-kit-server-0.25.0-1.azl3.x86_64.rpm p11-kit-trust-0.25.0-1.azl3.x86_64.rpm -pam-1.5.3-5.azl3.x86_64.rpm -pam-debuginfo-1.5.3-5.azl3.x86_64.rpm -pam-devel-1.5.3-5.azl3.x86_64.rpm -pam-lang-1.5.3-5.azl3.x86_64.rpm +pam-1.5.3-6.azl3.x86_64.rpm +pam-debuginfo-1.5.3-6.azl3.x86_64.rpm +pam-devel-1.5.3-6.azl3.x86_64.rpm +pam-lang-1.5.3-6.azl3.x86_64.rpm patch-2.7.6-10.azl3.x86_64.rpm patch-debuginfo-2.7.6-10.azl3.x86_64.rpm pcre2-10.42-3.azl3.x86_64.rpm