Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
154 changes: 154 additions & 0 deletions SPECS/pam/CVE-2026-54411.patch
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
From ecd62ad57bab9f1da252b8248dd8425690c54f4a Mon Sep 17 00:00:00 2001
From: AllSpark <allspark@microsoft.com>
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 <azurelinux-security@microsoft.com>
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 <ctype.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
@@ -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, key)) {
+ /* 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

6 changes: 5 additions & 1 deletion SPECS/pam/pam.spec
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -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
Expand Down Expand Up @@ -108,6 +109,9 @@ EOF
%{_libdir}/pkgconfig/pamc.pc

%changelog
* Tue Jul 28 2026 Azure Linux Security Servicing Account <azurelinux-security@microsoft.com> - 1.5.3-6
- Patch for CVE-2026-54411

* Tue Jun 24 2025 Jyoti Kanase <v-jykanase@microsoft.com> - 1.5.3-5
- Add patch for sync_pam_namespace_module_to_version_1.7.0.patch and CVE-2025-6020

Expand Down
8 changes: 4 additions & 4 deletions toolkit/resources/manifests/package/toolchain_aarch64.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
8 changes: 4 additions & 4 deletions toolkit/resources/manifests/package/toolchain_x86_64.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Loading