import UBI pam-1.6.1-9.el10_2.1

This commit is contained in:
AlmaLinux RelEng Bot 2026-08-25 12:24:52 -04:00
parent 16b7e24418
commit ae4cec8288
2 changed files with 178 additions and 1 deletions

View File

@ -0,0 +1,170 @@
From 2f48a7075d04108fbfb4fb8979cbeeed07f45982 Mon Sep 17 00:00:00 2001
From: vlefebvre <valentin.lefebvre@suse.com>
Date: Tue, 16 Jun 2026 16:31:29 +0200
Subject: [PATCH] pam_userdb: fix password comparison timing leak
* libpam/include/pam_inline.h: Include <ctype.h>.
(pam_consttime_strcaseeq): New function that implements a constant-time,
case-insensitive string equality check.
* modules/pam_userdb/pam_userdb.c (user_lookup): Use it along with
pam_consttime_streq instead of strncmp and strncasecmp to fix
timing side-channel that leaks password prefix bytes and length
(CWE-208).
Resolves: https://github.com/linux-pam/linux-pam/issues/992
Co-authored-by: Dmitry V. Levin <ldv@strace.io>
---
libpam/include/pam_inline.h | 33 +++++++++++++++
modules/pam_userdb/pam_userdb.c | 75 +++++++++++++++++++--------------
2 files changed, 77 insertions(+), 31 deletions(-)
diff --git a/libpam/include/pam_inline.h b/libpam/include/pam_inline.h
index 74232c51..9fde8dfa 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 <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
@@ -210,4 +211,36 @@ pam_read_passwords(int fd, int npass, char **passwords)
return i;
}
+static inline int
+pam_consttime_streq(const char *userinput, const char *secret) {
+ volatile const char *u = userinput, *s = secret;
+ volatile int ret = 0;
+
+ do {
+ ret |= *u ^ *s;
+
+ s += !!*s;
+ } while (*u++ != '\0');
+
+ return ret == 0;
+}
+
+/*
+ * 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 3bcb4c87..95cc590c 100644
--- a/modules/pam_userdb/pam_userdb.c
+++ b/modules/pam_userdb/pam_userdb.c
@@ -321,15 +321,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
@@ -361,36 +370,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 = db_firstkey(dbm);
key.dptr != NULL;
key = db_nextkey(dbm, key)) {
- 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) {
+ /* 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) {
db_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) {
+ db_close(dbm);
+ return 0; /* match */
}
}
db_close(dbm);

View File

@ -4,7 +4,7 @@
Summary: An extensible library which provides authentication for applications
Name: pam
Version: 1.6.1
Release: 9%{?dist}
Release: 9%{?dist}.1
# The library is BSD licensed with option to relicense as GPLv2+
# - this option is redundant as the BSD license allows that anyway.
# pam_timestamp and pam_loginuid modules are GPLv2+.
@ -39,6 +39,8 @@ Patch8: pam-1.6.1-pam-inline-pam-asprintf.patch
Patch9: pam-1.6.1-pam-namespace-rebase.patch
# https://github.com/linux-pam/linux-pam/commit/7d96d452e65ba5dec73f2c77104113977dd3aeb1
Patch10: pam-1.6.1-pam-faillock-skip.patch
# https://github.com/linux-pam/linux-pam/commit/30708d973b63891bf700299ce3ae0f1086398284
Patch11: pam-1.6.1-CVE-2026-54411.patch
%{load:%{SOURCE3}}
@ -139,6 +141,7 @@ cp %{SOURCE18} .
%patch -P 8 -p1 -b .pam-inline-pam-asprintf
%patch -P 9 -p1 -b .pam-namespace-rebase
%patch -P 10 -p1 -b .pam-faillock-skip
%patch -P 11 -p1 -b .CVE-2026-54411
autoreconf -i
@ -377,6 +380,10 @@ done
%{_pam_libdir}/libpam_misc.so.%{so_ver}*
%changelog
* Sun Jul 12 2026 RHEL Packaging Agent <redhat-ymir-agent@redhat.com> - 1.6.1-9.1
- pam_userdb: fix password comparison timing leak.
Resolves: CVE-2026-54411 and RHEL-191704
* Mon Dec 1 2025 Iker Pedrosa <ipedrosa@redhat.com> - 1.6.1-9
- pam_faillock: skip clearing user's failed attempt.
Resolves: RHEL-130871