postgresql-jdbc/SOURCES/CVE-2026-42198.patch
2026-06-10 06:26:41 -04:00

158 lines
7.2 KiB
Diff

From 2f69e69103319fd336bf63194a2ebc0e602dc481 Mon Sep 17 00:00:00 2001
From: Marian Koncek <mkoncek@redhat.com>
Date: Mon, 8 Jun 2026 14:50:02 +0200
Subject: [PATCH 1/2] 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
Co-authored-by: Cursor <cursoragent@cursor.com>
---
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/src/main/java/org/postgresql/PGProperty.java b/src/main/java/org/postgresql/PGProperty.java
index f94b387..712ece6 100644
--- a/src/main/java/org/postgresql/PGProperty.java
+++ b/src/main/java/org/postgresql/PGProperty.java
@@ -470,6 +470,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/src/main/java/org/postgresql/core/v3/ConnectionFactoryImpl.java b/src/main/java/org/postgresql/core/v3/ConnectionFactoryImpl.java
index 9737a82..a21c0b1 100644
--- a/src/main/java/org/postgresql/core/v3/ConnectionFactoryImpl.java
+++ b/src/main/java/org/postgresql/core/v3/ConnectionFactoryImpl.java
@@ -666,9 +666,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, password, pgStream);
+ scramAuthenticator = new org.postgresql.jre7.sasl.ScramAuthenticator(user, password, pgStream, scramMaxIterations);
scramAuthenticator.processServerMechanismsAndInit();
scramAuthenticator.sendScramClientFirstMessage();
// This works as follows:
diff --git a/src/main/java/org/postgresql/ds/common/BaseDataSource.java b/src/main/java/org/postgresql/ds/common/BaseDataSource.java
index e4e2bb3..522d81c 100644
--- a/src/main/java/org/postgresql/ds/common/BaseDataSource.java
+++ b/src/main/java/org/postgresql/ds/common/BaseDataSource.java
@@ -1185,6 +1185,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/src/main/java/org/postgresql/jre7/sasl/ScramAuthenticator.java b/src/main/java/org/postgresql/jre7/sasl/ScramAuthenticator.java
index 2d97387..ea78189 100644
--- a/src/main/java/org/postgresql/jre7/sasl/ScramAuthenticator.java
+++ b/src/main/java/org/postgresql/jre7/sasl/ScramAuthenticator.java
@@ -5,6 +5,7 @@
package org.postgresql.jre7.sasl;
+import org.postgresql.PGProperty;
import org.postgresql.core.PGStream;
import org.postgresql.util.GT;
import org.postgresql.util.PSQLException;
@@ -31,6 +32,7 @@ public class ScramAuthenticator {
private final String user;
private final String password;
private final PGStream pgStream;
+ private final int maxIterations;
private ScramClient scramClient;
private ScramSession scramSession;
private ScramSession.ServerFirstProcessor serverFirstProcessor;
@@ -48,10 +50,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 {
@@ -129,6 +132,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.54.0