Script 'mail_helper' called by obssrc
Hello community,

here is the log from the commit of package pam for openSUSE:Factory checked in 
at 2026-07-07 20:59:28
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/pam (Old)
 and      /work/SRC/openSUSE:Factory/.pam.new.1982 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "pam"

Tue Jul  7 20:59:28 2026 rev:154 rq:1364026 version:1.7.2+git12

Changes:
--------
--- /work/SRC/openSUSE:Factory/pam/pam.changes  2026-05-27 16:14:05.449846099 
+0200
+++ /work/SRC/openSUSE:Factory/.pam.new.1982/pam.changes        2026-07-07 
20:59:28.554634200 +0200
@@ -1,0 +2,7 @@
+Tue Jun 16 15:15:09 UTC 2026 - Valentin Lefebvre <[email protected]>
+
+- Fix password comparison timing leak from pam_userdb
+  [CVE-2026-54411], [bsc#1268290]
+  * pam_userdb-fix-password-comparison-timing-leak.patch
+
+-------------------------------------------------------------------

New:
----
  pam_userdb-fix-password-comparison-timing-leak.patch

----------(New B)----------
  New:  [CVE-2026-54411], [bsc#1268290]
  * pam_userdb-fix-password-comparison-timing-leak.patch
----------(New E)----------

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Other differences:
------------------
++++++ pam.spec ++++++
--- /var/tmp/diff_new_pack.rldkYp/_old  2026-07-07 20:59:30.610703694 +0200
+++ /var/tmp/diff_new_pack.rldkYp/_new  2026-07-07 20:59:30.614703830 +0200
@@ -92,6 +92,8 @@
 Source23:       postlogin-password.pamd
 Source24:       postlogin-session.pamd
 Patch1:         pam-limit-nproc.patch
+# PATCH-FIX-UPSTREAM
+Patch2:         pam_userdb-fix-password-comparison-timing-leak.patch
 BuildRequires:  audit-devel
 BuildRequires:  bison
 BuildRequires:  flex

++++++ pam_userdb-fix-password-comparison-timing-leak.patch ++++++
>From 30708d973b63891bf700299ce3ae0f1086398284 Mon Sep 17 00:00:00 2001
From: vlefebvre <[email protected]>
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 <[email protected]>
---
 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.51.0

Reply via email to