From f5b84f76a29b91abc7bd187af449520a0c63093f Mon Sep 17 00:00:00 2001 From: AlmaLinux RelEng Bot Date: Thu, 25 Jun 2026 09:45:05 -0400 Subject: [PATCH] import Oracle_OSS postgresql-jdbc-42.2.28-2.el9_8.2 --- SOURCES/CVE-2026-42198-tests.patch | 182 +++++++++++++++++++++++++++++ SOURCES/RHEL-173489.patch | 154 ++++++++++++++++++++++++ SPECS/postgresql-jdbc.spec | 26 ++++- 3 files changed, 358 insertions(+), 4 deletions(-) create mode 100644 SOURCES/CVE-2026-42198-tests.patch create mode 100644 SOURCES/RHEL-173489.patch diff --git a/SOURCES/CVE-2026-42198-tests.patch b/SOURCES/CVE-2026-42198-tests.patch new file mode 100644 index 0000000..cb0e69d --- /dev/null +++ b/SOURCES/CVE-2026-42198-tests.patch @@ -0,0 +1,182 @@ +From 6e318b53fa3b1b84139fa13b2f121670b884c7da Mon Sep 17 00:00:00 2001 +From: Marian Koncek +Date: Wed, 20 May 2026 12:58:33 +0200 +Subject: [PATCH] Add tests for CVE-2026-42198 + +--- + .../org/postgresql/core/ServerVersion.java | 4 +- + .../java/org/postgresql/jdbc/ScramTest.java | 91 +++++++++++++++++++ + .../java/org/postgresql/test/TestUtil.java | 15 +++ + 3 files changed, 109 insertions(+), 1 deletion(-) + +diff --git a/src/main/java/org/postgresql/core/ServerVersion.java b/src/main/java/org/postgresql/core/ServerVersion.java +index 5d18da1..4036b01 100644 +--- a/src/main/java/org/postgresql/core/ServerVersion.java ++++ b/src/main/java/org/postgresql/core/ServerVersion.java +@@ -30,7 +30,9 @@ public enum ServerVersion implements Version { + v11("11"), + v12("12"), + v13("13"), +- v14("14") ++ v14("14"), ++ v15("15"), ++ v16("16") + ; + + private final int version; +diff --git a/src/test/java/org/postgresql/jdbc/ScramTest.java b/src/test/java/org/postgresql/jdbc/ScramTest.java +index 9c6e3e0..60bc75a 100644 +--- a/src/test/java/org/postgresql/jdbc/ScramTest.java ++++ b/src/test/java/org/postgresql/jdbc/ScramTest.java +@@ -11,12 +11,15 @@ import static org.junit.jupiter.api.Assertions.assertThrows; + import static org.junit.jupiter.api.Assertions.assertTrue; + import static org.junit.jupiter.api.Assumptions.assumeTrue; + ++import org.postgresql.PGProperty; + import org.postgresql.core.ServerVersion; + import org.postgresql.test.TestUtil; ++import org.postgresql.util.PSQLException; + import org.postgresql.util.PSQLState; + + import org.junit.jupiter.api.AfterAll; + import org.junit.jupiter.api.BeforeAll; ++import org.junit.jupiter.api.Test; + import org.junit.jupiter.params.ParameterizedTest; + import org.junit.jupiter.params.provider.ValueSource; + +@@ -24,6 +27,7 @@ import java.sql.Connection; + import java.sql.ResultSet; + import java.sql.SQLException; + import java.sql.Statement; ++import java.text.NumberFormat; + import java.util.Properties; + + class ScramTest { +@@ -94,6 +98,79 @@ class ScramTest { + assertEquals(PSQLState.INVALID_PASSWORD.getState(), ex.getSQLState()); + } + ++ private PSQLException scramAuthExpectingFailure(String scramMaxIterations, int serverScramIterations, String password) throws SQLException { ++ createRoleWithCustomScramIters(serverScramIterations); ++ Properties props = new Properties(); ++ props.setProperty("username", ROLE_NAME); ++ props.setProperty("password", password); ++ if (scramMaxIterations != null) { ++ PGProperty.SCRAM_MAX_ITERATIONS.set(props, scramMaxIterations); ++ } ++ return assertThrows(PSQLException.class, () -> TestUtil.openDB(props)); ++ } ++ ++ @Test ++ void rejectIterationCountAboveDefaultCap() throws SQLException { ++ int serverScramIterations = 789_123_456; ++ PSQLException ex = scramAuthExpectingFailure(null, serverScramIterations, "does-not-matter"); ++ assertTrue(ex.getMessage().contains("exceeds"), ++ "expected iteration-cap error, got: " + ex.getMessage()); ++ assertTrue(ex.getMessage().contains("scramMaxIterations"), ++ "error should reference the connection property name, got: " + ex.getMessage()); ++ // The message is formatted through MessageFormat, which applies locale-aware grouping ++ // to integer arguments; format the expected numbers the same way. ++ NumberFormat nf = NumberFormat.getNumberInstance(); ++ assertTrue(ex.getMessage().contains(nf.format(serverScramIterations)), ++ "error should include the configured cap, got: " + ex.getMessage()); ++ } ++ ++ @Test ++ void rejectIterationCountAboveCustomCap() throws SQLException { ++ int scramMaxIterations = 123_456; ++ int serverScramIterations = 789_123_456; ++ PSQLException ex = scramAuthExpectingFailure(Integer.toString(scramMaxIterations), serverScramIterations, "does-not-matter"); ++ // The message is formatted through MessageFormat, which applies locale-aware grouping ++ // to integer arguments; format the expected numbers the same way. ++ NumberFormat nf = NumberFormat.getNumberInstance(); ++ assertTrue(ex.getMessage().contains(nf.format(scramMaxIterations)), ++ "error should include the configured cap, got: " + ex.getMessage()); ++ assertTrue(ex.getMessage().contains(nf.format(serverScramIterations)), ++ "error should include the server-supplied iteration count, got: " + ex.getMessage()); ++ } ++ ++ @Test ++ void rejectValidCredentialsAboveCustomCap() throws SQLException { ++ String password = "t0pSecret"; ++ createRole(password); ++ Properties props = new Properties(); ++ props.setProperty("username", ROLE_NAME); ++ props.setProperty("password", password); ++ PGProperty.SCRAM_MAX_ITERATIONS.set(props, "1234"); ++ PSQLException ex = assertThrows(PSQLException.class, () -> TestUtil.openDB(props)); ++ // The message is formatted through MessageFormat, which applies locale-aware grouping ++ // to integer arguments; format the expected numbers the same way. ++ NumberFormat nf = NumberFormat.getNumberInstance(); ++ assertTrue(ex.getMessage().contains(nf.format(1234)), ++ "error should include the configured cap, got: " + ex.getMessage()); ++ } ++ ++ @Test ++ void acceptsValidCredentialsBelowCustomCap() throws SQLException { ++ assumeTrue(TestUtil.haveMinimumServerVersion(con, ServerVersion.v16), ++ "scram_iterations configuration requires PostgreSQL 16+"); ++ int serverScramIterations = Integer.parseInt(TestUtil.queryForString(con, "SHOW scram_iterations")); ++ String password = "t0pSecret"; ++ createRole(password); ++ Properties props = new Properties(); ++ props.setProperty("username", ROLE_NAME); ++ props.setProperty("password", password); ++ PGProperty.SCRAM_MAX_ITERATIONS.set(props, Integer.toString(serverScramIterations)); ++ try (Connection conn = TestUtil.openDB(props)) { ++ String username = TestUtil.queryForString(conn, "SELECT USER"); ++ assertEquals(ROLE_NAME, username); ++ } ++ } ++ + private void createRole(String passwd) throws SQLException { + try (Statement stmt = con.createStatement()) { + stmt.execute("SET password_encryption='scram-sha-256'"); +@@ -102,4 +179,18 @@ class ScramTest { + } + } + ++ private static void createRoleWithCustomScramIters(int iters) throws SQLException { ++ TestUtil.execute("DROP ROLE IF EXISTS " + ROLE_NAME, con); ++ TestUtil.execute("CREATE ROLE " + ROLE_NAME + " WITH LOGIN", con); ++ // SCRAM-SHA-256$:$: ++ // salt: 16 zero bytes, StoredKey and ServerKey: 32 zero bytes each. ++ String encodedPassword = "SCRAM-SHA-256$" + iters ++ + ":AAAAAAAAAAAAAAAAAAAAAA==" ++ + "$AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=" ++ + ":AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA="; ++ // NOTE: We must directly update the system catalog to prevent the server from trying to ++ // verify the password at creation time. Otherwise it will try to hash empty string with ++ // our huge number of iterations to ensure the password is not an empty string. ++ TestUtil.execute("UPDATE pg_authid SET rolpassword = '" + encodedPassword + "' WHERE rolname = '" + ROLE_NAME + "'", con); ++ } + } +diff --git a/src/test/java/org/postgresql/test/TestUtil.java b/src/test/java/org/postgresql/test/TestUtil.java +index 7064f86..e89dab0 100644 +--- a/src/test/java/org/postgresql/test/TestUtil.java ++++ b/src/test/java/org/postgresql/test/TestUtil.java +@@ -975,6 +975,21 @@ public class TestUtil { + return hasNext; + } + ++ /** ++ * Execute a SQL query with a given connection, fetch the first row, and return its ++ * string value. ++ */ ++ public static /* @Nullable */ String queryForString(Connection conn, String sql) throws SQLException { ++ Statement stmt = conn.createStatement(); ++ ResultSet rs = stmt.executeQuery(sql); ++ Assert.assertTrue("Query should have returned exactly one row but none was found: " + sql, rs.next()); ++ String value = rs.getString(1); ++ Assert.assertFalse("Query should have returned exactly one row but more than one found: " + sql, rs.next()); ++ rs.close(); ++ stmt.close(); ++ return value; ++ } ++ + /** + * Retrieve the backend process id for a given connection. + */ +-- +2.54.0 + diff --git a/SOURCES/RHEL-173489.patch b/SOURCES/RHEL-173489.patch new file mode 100644 index 0000000..3c9d8b1 --- /dev/null +++ b/SOURCES/RHEL-173489.patch @@ -0,0 +1,154 @@ +From be412561fe988dc9396ec3e492af2e8a4e43c620 Mon Sep 17 00:00:00 2001 +From: Sehrope Sarkuni +Date: Sat, 18 Apr 2026 08:20:17 -0400 +Subject: [PATCH] fix: Limit SCRAM PBKDF2 iterations accepted from the server + +A malicious or compromised PostgreSQL server can advertise an +arbitrarily large PBKDF2 iteration count in its SCRAM +server-first-message, forcing the client to burn CPU inside +clientFinalMessage() before authentication can possibly fail. Combined +with an abandoned connect-thread on loginTimeout expiry, that CPU +continues spinning after the caller has given up. + +We add a new `scramMaxIterations` connection property (default 100000) +and validate the iteration count from ServerFirstMessage against it +after parsing but before the PBKDF2-heavy clientFinalMessage() step. +Exceeding the cap throws a PSQLException with CONNECTION_REJECTED and +an error message naming the property so operators can raise it for +trusted servers that legitimately use a higher count. + +Fixes CVE-2026-42198 +--- + .../src/main/java/org/postgresql/PGProperty.java | 14 ++++++++++++++ + .../core/v3/ConnectionFactoryImpl.java | 9 ++++++++- + .../org/postgresql/ds/common/BaseDataSource.java | 16 ++++++++++++++++ + .../postgresql/jre7/sasl/ScramAuthenticator.java | 15 ++++++++++++++- + 4 files changed, 52 insertions(+), 2 deletions(-) + +diff --git a/pgjdbc/src/main/java/org/postgresql/PGProperty.java b/pgjdbc/src/main/java/org/postgresql/PGProperty.java +index 3f4cad67..2d6ec487 100644 +--- a/pgjdbc/src/main/java/org/postgresql/PGProperty.java ++++ b/pgjdbc/src/main/java/org/postgresql/PGProperty.java +@@ -480,6 +480,20 @@ public enum PGProperty { + "false", + "Enable optimization to rewrite and collapse compatible INSERT statements that are batched."), + ++ /** ++ * Maximum number of PBKDF2 iterations the client will accept from the server during SCRAM ++ * authentication. If the server advertises more iterations than this value, authentication ++ * is rejected before the expensive PBKDF2 computation runs. This mitigates a denial-of-service ++ * vector where a malicious or compromised server forces the client to burn CPU on an ++ * attacker-controlled iteration count. Must be a non-negative integer. Defaults to 100000. Raise ++ * only if you know you are connecting to a trusted server that legitimately uses a higher ++ * iteration count. A value of zero disables this check. ++ */ ++ SCRAM_MAX_ITERATIONS( ++ "scramMaxIterations", ++ "100000", ++ "Maximum PBKDF2 iteration count accepted from the server during SCRAM authentication. A value of zero disables this check."), ++ + /** + * Socket write buffer size (SO_SNDBUF). A value of {@code -1}, which is the default, means system + * default. +diff --git a/pgjdbc/src/main/java/org/postgresql/core/v3/ConnectionFactoryImpl.java b/pgjdbc/src/main/java/org/postgresql/core/v3/ConnectionFactoryImpl.java +index 0ffc9e56..7732bdcb 100644 +--- a/pgjdbc/src/main/java/org/postgresql/core/v3/ConnectionFactoryImpl.java ++++ b/pgjdbc/src/main/java/org/postgresql/core/v3/ConnectionFactoryImpl.java +@@ -754,9 +754,16 @@ public class ConnectionFactoryImpl extends ConnectionFactory { + + case AUTH_REQ_SASL: + LOGGER.log(Level.FINEST, " <=BE AuthenticationSASL"); ++ int scramMaxIterations = PGProperty.SCRAM_MAX_ITERATIONS.getInt(info); ++ if (scramMaxIterations < 0) { ++ throw new PSQLException( ++ GT.tr("{0} must be a non-negative integer, but was: {1}", ++ PGProperty.SCRAM_MAX_ITERATIONS.getName(), scramMaxIterations), ++ PSQLState.INVALID_PARAMETER_VALUE); ++ } + + //#if mvn.project.property.postgresql.jdbc.spec >= "JDBC4.1" +- scramAuthenticator = new org.postgresql.jre7.sasl.ScramAuthenticator(user, castNonNull(password), pgStream); ++ scramAuthenticator = new org.postgresql.jre7.sasl.ScramAuthenticator(user, castNonNull(password), pgStream, scramMaxIterations); + scramAuthenticator.processServerMechanismsAndInit(); + scramAuthenticator.sendScramClientFirstMessage(); + // This works as follows: +diff --git a/pgjdbc/src/main/java/org/postgresql/ds/common/BaseDataSource.java b/pgjdbc/src/main/java/org/postgresql/ds/common/BaseDataSource.java +index 1dfa42c3..f1c5ec78 100644 +--- a/pgjdbc/src/main/java/org/postgresql/ds/common/BaseDataSource.java ++++ b/pgjdbc/src/main/java/org/postgresql/ds/common/BaseDataSource.java +@@ -1208,6 +1208,22 @@ public abstract class BaseDataSource implements CommonDataSource, Referenceable + PGProperty.LOGGER_FILE.set(properties, loggerFile); + } + ++ /** ++ * @return maximum PBKDF2 iteration count accepted during SCRAM authentication ++ * @see PGProperty#SCRAM_MAX_ITERATIONS ++ */ ++ public int getScramMaxIterations() { ++ return PGProperty.SCRAM_MAX_ITERATIONS.getIntNoCheck(properties); ++ } ++ ++ /** ++ * @param scramMaxIterations maximum PBKDF2 iteration count accepted during SCRAM authentication ++ * @see PGProperty#SCRAM_MAX_ITERATIONS ++ */ ++ public void setScramMaxIterations(int scramMaxIterations) { ++ PGProperty.SCRAM_MAX_ITERATIONS.set(properties, scramMaxIterations); ++ } ++ + /** + * Generates a {@link DriverManager} URL from the other properties supplied. + * +diff --git a/pgjdbc/src/main/java/org/postgresql/jre7/sasl/ScramAuthenticator.java b/pgjdbc/src/main/java/org/postgresql/jre7/sasl/ScramAuthenticator.java +index 59f272a0..f14b3c07 100644 +--- a/pgjdbc/src/main/java/org/postgresql/jre7/sasl/ScramAuthenticator.java ++++ b/pgjdbc/src/main/java/org/postgresql/jre7/sasl/ScramAuthenticator.java +@@ -7,6 +7,7 @@ package org.postgresql.jre7.sasl; + + import static org.postgresql.util.internal.Nullness.castNonNull; + ++import org.postgresql.PGProperty; + import org.postgresql.core.PGStream; + import org.postgresql.util.GT; + import org.postgresql.util.PSQLException; +@@ -34,6 +35,7 @@ public class ScramAuthenticator { + private final String user; + private final String password; + private final PGStream pgStream; ++ private final int maxIterations; + private /* @Nullable */ ScramClient scramClient; + private /* @Nullable */ ScramSession scramSession; + private /* @Nullable */ ScramSession.ClientFinalProcessor clientFinalProcessor; +@@ -50,10 +52,11 @@ public class ScramAuthenticator { + pgStream.flush(); + } + +- public ScramAuthenticator(String user, String password, PGStream pgStream) { ++ public ScramAuthenticator(String user, String password, PGStream pgStream, int maxIterations) { + this.user = user; + this.password = password; + this.pgStream = pgStream; ++ this.maxIterations = maxIterations; + } + + public void processServerMechanismsAndInit() throws IOException, PSQLException { +@@ -144,6 +147,16 @@ public class ScramAuthenticator { + ); + } + ++ int iterations = serverFirstProcessor.getIteration(); ++ if (maxIterations > 0 && iterations > maxIterations) { ++ throw new PSQLException( ++ GT.tr("Server requested {0} SCRAM PBKDF2 iterations, which exceeds the " ++ + "client-side limit of {1}. If you trust this server, raise the " ++ + "{2} connection property.", ++ iterations, maxIterations, PGProperty.SCRAM_MAX_ITERATIONS.getName()), ++ PSQLState.CONNECTION_REJECTED); ++ } ++ + clientFinalProcessor = serverFirstProcessor.clientFinalProcessor(password); + + String clientFinalMessage = clientFinalProcessor.clientFinalMessage(); +-- +2.52.0 + diff --git a/SPECS/postgresql-jdbc.spec b/SPECS/postgresql-jdbc.spec index 70bac6f..19f1ae7 100644 --- a/SPECS/postgresql-jdbc.spec +++ b/SPECS/postgresql-jdbc.spec @@ -49,12 +49,16 @@ Summary: JDBC driver for PostgreSQL Name: postgresql-jdbc Version: 42.2.28 -Release: 1%{?dist} +Release: 2%{?dist}.2 License: BSD URL: http://jdbc.postgresql.org/ Source0: https://repo1.maven.org/maven2/org/postgresql/postgresql/%{version}/postgresql-%{version}-jdbc-src.tar.gz +# https://github.com/pgjdbc/pgjdbc/commit/c9d41d1332a7426fcef19ff89f2e6b1116429143 +Patch0: RHEL-173489.patch +Patch1: CVE-2026-42198-tests.patch + Provides: pgjdbc = %version-%release BuildArch: noarch @@ -65,6 +69,7 @@ BuildRequires: maven-plugin-bundle BuildRequires: classloader-leak-test-framework BuildRequires: mvn(com.ongres.scram:client) +BuildRequires: mvn(org.apache.maven.plugins:maven-shade-plugin) BuildRequires: mvn(org.apache.maven.surefire:surefire-junit-platform) BuildRequires: mvn(org.junit.jupiter:junit-jupiter-api) BuildRequires: mvn(org.junit.jupiter:junit-jupiter-engine) @@ -81,6 +86,9 @@ BuildRequires: postgresql-test-rpm-macros Obsoletes: %{name}-parent-poms < 42.2.2-2 +Provides: bundled(mvn(com.ongres.scram:client)) = 1.9.beta1 +Provides: bundled(mvn(com.ongres.scram:common)) = 1.9.beta1 + %description PostgreSQL is an advanced Object-Relational database management system. The postgresql-jdbc package includes the .jar files needed for @@ -99,12 +107,12 @@ This package contains the API Documentation for %{name}. mv postgresql-%{version}-jdbc-src/* . +%patch -P0 -p2 +%patch -P1 -p1 + # remove any binary libs find -type f \( -name "*.jar" -or -name "*.class" \) | xargs rm -f -# Build parent POMs in the same Maven call. -%pom_xpath_remove "pom:plugin[pom:artifactId = 'maven-shade-plugin']" - %pom_remove_plugin -r :maven-javadoc-plugin # compat symlink: requested by dtardon (libreoffice), reverts part of @@ -165,6 +173,16 @@ opts="-f" %changelog +* Tue May 19 2026 Marian Koncek - 42.2.28-2.2 +- Add tests for CVE-2026-42198 + +* Mon May 11 2026 RHEL Packaging Agent - 42.2.28-2.1 +- Fix CVE-2026-42198: limit SCRAM PBKDF2 iterations to prevent DoS +- Resolves: RHEL-173489 + +* Fri Jan 09 2026 Marian Koncek - 42.2.28-2 +- Bundle shaded ongres-* dependencies + * Wed Feb 28 2024 Zuzana Miklankova - 42.2.28-1 - rebase to 42.2.28 - fix for CVE-2024-1597