public inbox for git-commits@fedoraproject.org
help / color / mirror / Atom feed
From: Iker Pedrosa <ipedrosa@redhat.com>
To: git-commits@fedoraproject.org
Subject: [rpms/pam] rawhide: pam_userdb: fix password comparison timing leak
Date: Thu, 02 Jul 2026 10:58:46 GMT [thread overview]
Message-ID: <178298992674.1.1403165462693301381.rpms-pam-ee0d2834f984@fedoraproject.org> (raw)
A new commit has been pushed.
Repo : rpms/pam
Branch : rawhide
Commit : ee0d2834f9842deba7f0c043bf07aa98e35a4ffc
Author : Iker Pedrosa <ipedrosa@redhat.com>
Date : 2026-07-02T12:58:15+02:00
Stats : +169/-1 in 2 file(s)
URL : https://src.fedoraproject.org/rpms/pam/c/ee0d2834f9842deba7f0c043bf07aa98e35a4ffc?branch=rawhide
Log:
pam_userdb: fix password comparison timing leak
Resolves: #2496416
Resolves: CVE-2026-54411
Signed-off-by: Iker Pedrosa <ipedrosa@redhat.com>
---
diff --git a/pam-1.7.2-pam-userdb-fix-password-leak.patch b/pam-1.7.2-pam-userdb-fix-password-leak.patch
new file mode 100644
index 0000000..587920c
--- /dev/null
+++ b/pam-1.7.2-pam-userdb-fix-password-leak.patch
@@ -0,0 +1,160 @@
+From 30708d973b63891bf700299ce3ae0f1086398284 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 | 20 +++++++++
+ modules/pam_userdb/pam_userdb.c | 75 +++++++++++++++++++--------------
+ 2 files changed, 64 insertions(+), 31 deletions(-)
+
+diff --git a/libpam/include/pam_inline.h b/libpam/include/pam_inline.h
+index d79d6fdf..86d131cf 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>
+@@ -244,4 +245,23 @@ pam_consttime_streq(const char *userinput, const char *secret) {
+ 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 bdb8553c..b37f096c 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);
+--
+2.54.0
+
diff --git a/pam.spec b/pam.spec
index d382f10..428cb37 100644
--- a/pam.spec
+++ b/pam.spec
@@ -14,7 +14,7 @@
Summary: An extensible library which provides authentication for applications
Name: pam
Version: 1.7.2
-Release: 2%{?dist}
+Release: 3%{?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 and pam_loginuid modules are GPLv2+.
@@ -34,6 +34,8 @@ Source17: postlogin.5
Source18: https://www.gnu.org/licenses/old-licenses/gpl-2.0.txt
Patch1: pam-1.7.0-redhat-modules.patch
Patch2: pam-1.5.3-unix-nomsg.patch
+# https://github.com/linux-pam/linux-pam/commit/30708d973b63891bf700299ce3ae0f1086398284
+Patch3: pam-1.7.2-pam-userdb-fix-password-leak.patch
%{load:%{SOURCE3}}
@@ -130,6 +132,7 @@ cp %{SOURCE18} .
%patch -P 1 -p1 -b .redhat-modules
%patch -P 2 -p1 -b .nomsg
+%patch -P 3 -p1 -b .pam-userdb-fix-password-leak
%build
%meson \
@@ -363,6 +366,11 @@ done
%{_pam_libdir}/libpam_misc.so.%{so_ver}*
%changelog
+* Thu Jul 2 2026 Iker Pedrosa <ipedrosa@redhat.com> - 1.7.2-3
+- pam_userdb: fix password comparison timing leak
+ Resolves: #2496416
+ Resolves: CVE-2026-54411
+
* Fri Jun 12 2026 Yaakov Selkowitz <yselkowi@redhat.com>
- Rebuilt for openssl 4.0
reply other threads:[~2026-07-02 10:58 UTC|newest]
Thread overview: [no followups] expand[flat|nested] mbox.gz Atom feed
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=178298992674.1.1403165462693301381.rpms-pam-ee0d2834f984@fedoraproject.org \
--to=ipedrosa@redhat.com \
--cc=git-commits@fedoraproject.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox