diff --git a/postgresql-jdbc-CVE-2024-1597.patch b/postgresql-jdbc-CVE-2024-1597.patch new file mode 100644 index 0000000..2c1ec2b --- /dev/null +++ b/postgresql-jdbc-CVE-2024-1597.patch @@ -0,0 +1,217 @@ +Sources of this patch: +https://github.com/pgjdbc/pgjdbc/commit/b9b3777671c8a5cc580e1985f61337d39d47c730 +https://github.com/pgjdbc/pgjdbc/commit/990d63f6be401ab40de5eb303a75924c9e71903c + + +diff --git a/pgjdbc/src/main/java/org/postgresql/core/v3/SimpleParameterList.java b/pgjdbc/src/main/java/org/postgresql/core/v3/SimpleParameterList.java +index 1ce49996..b1bbb41a 100644 +--- a/pgjdbc/src/main/java/org/postgresql/core/v3/SimpleParameterList.java ++++ b/pgjdbc/src/main/java/org/postgresql/core/v3/SimpleParameterList.java +@@ -168,99 +170,163 @@ class SimpleParameterList implements V3ParameterList { + bind(index, NULL_OBJECT, oid, binaryTransfer); + } + ++ /** ++ *
Escapes a given text value as a literal, wraps it in single quotes, casts it to the ++ * to the given data type, and finally wraps the whole thing in parentheses.
++ * ++ *For example, "123" and "int4" becomes "('123'::int)"
++ * ++ *The additional parentheses is added to ensure that the surrounding text of where the ++ * parameter value is entered does modify the interpretation of the value.
++ * ++ *For example if our input SQL is: SELECT ?b
Using a parameter value of '{}' and type of json we'd get:
++ * ++ *
++ * test=# SELECT ('{}'::json)b;
++ * b
++ * ----
++ * {}
++ *
++ *
++ * But without the parentheses the result changes:
++ * ++ *
++ * test=# SELECT '{}'::jsonb;
++ * jsonb
++ * -------
++ * {}
++ *
++ **/
++ private static String quoteAndCast(String text, String type, boolean standardConformingStrings) {
++ StringBuilder sb = new StringBuilder((text.length() + 10) / 10 * 11); // Add 10% for escaping.
++ sb.append("('");
++ try {
++ Utils.escapeLiteral(sb, text, standardConformingStrings);
++ } catch (SQLException e) {
++ // This should only happen if we have an embedded null
++ // and there's not much we can do if we do hit one.
++ //
++ // To force a server side failure, we deliberately include
++ // a zero byte character in the literal to force the server
++ // to reject the command.
++ sb.append('\u0000');
++ }
++ sb.append("'");
++ if (type != null) {
++ sb.append("::");
++ sb.append(type);
++ }
++ sb.append(")");
++ return sb.toString();
++ }
++
+ @Override
+ public String toString(int index, boolean standardConformingStrings) {
+ --index;
+ if (paramValues[index] == null) {
+ return "?";
+ } else if (paramValues[index] == NULL_OBJECT) {
+- return "NULL";
+- } else if ((flags[index] & BINARY) == BINARY) {
++ return "(NULL)";
++ }
++ String textValue;
++ String type;
++ if ((flags[index] & BINARY) == BINARY) {
+ // handle some of the numeric types
+-
+ switch (paramTypes[index]) {
+ case Oid.INT2:
+ short s = ByteConverter.int2((byte[]) paramValues[index], 0);
+- return Short.toString(s);
++ textValue = Short.toString(s);
++ type = "int2";
++ break;
+
+ case Oid.INT4:
+ int i = ByteConverter.int4((byte[]) paramValues[index], 0);
+- return Integer.toString(i);
++ textValue = Integer.toString(i);
++ type = "int4";
++ break;
+
+ case Oid.INT8:
+ long l = ByteConverter.int8((byte[]) paramValues[index], 0);
+- return Long.toString(l);
++ textValue = Long.toString(l);
++ type = "int8";
++ break;
+
+ case Oid.FLOAT4:
+ float f = ByteConverter.float4((byte[]) paramValues[index], 0);
+ if (Float.isNaN(f)) {
+- return "'NaN'::real";
++ return "('NaN'::real)";
+ }
+- return Float.toString(f);
++ textValue = Float.toString(f);
++ type = "real";
++ break;
+
+ case Oid.FLOAT8:
+ double d = ByteConverter.float8((byte[]) paramValues[index], 0);
+ if (Double.isNaN(d)) {
+- return "'NaN'::double precision";
++ return "('NaN'::double precision)";
++ }
++ textValue = Double.toString(d);
++ type = "double precision";
++ break;
++
++ case Oid.NUMERIC:
++ Number n = ByteConverter.numeric((byte[]) paramValues[index]);
++ if (n instanceof Double) {
++ assert ((Double) n).isNaN();
++ return "('NaN'::numeric)";
+ }
+- return Double.toString(d);
++ textValue = n.toString();
++ type = "numeric";
++ break;
+
+ case Oid.UUID:
+- String uuid =
++ textValue =
+ new UUIDArrayAssistant().buildElement((byte[]) paramValues[index], 0, 16).toString();
+- return "'" + uuid + "'::uuid";
++ type = "uuid";
++ break;
+
+ case Oid.POINT:
+ PGpoint pgPoint = new PGpoint();
+ pgPoint.setByteValue((byte[]) paramValues[index], 0);
+- return "'" + pgPoint.toString() + "'::point";
++ textValue = pgPoint.toString();
++ type = "point";
++ break;
+
+ case Oid.BOX:
+ PGbox pgBox = new PGbox();
+ pgBox.setByteValue((byte[]) paramValues[index], 0);
+- return "'" + pgBox.toString() + "'::box";
++ textValue = pgBox.toString();
++ type = "box";
++ break;
++
++ default:
++ return "?";
+ }
+- return "?";
+ } else {
+- String param = paramValues[index].toString();
+-
+- // add room for quotes + potential escaping.
+- StringBuilder p = new StringBuilder(3 + (param.length() + 10) / 10 * 11);
+-
+- // No E'..' here since escapeLiteral escapes all things and it does not use \123 kind of
+- // escape codes
+- p.append('\'');
+- try {
+- p = Utils.escapeLiteral(p, param, standardConformingStrings);
+- } catch (SQLException sqle) {
+- // This should only happen if we have an embedded null
+- // and there's not much we can do if we do hit one.
+- //
+- // The goal of toString isn't to be sent to the server,
+- // so we aren't 100% accurate (see StreamWrapper), put
+- // the unescaped version of the data.
+- //
+- p.append(param);
+- }
+- p.append('\'');
++ textValue = paramValues[index].toString();
++
+ int paramType = paramTypes[index];
+ if (paramType == Oid.TIMESTAMP) {
+- p.append("::timestamp");
++ type = "timestamp";
+ } else if (paramType == Oid.TIMESTAMPTZ) {
+- p.append("::timestamp with time zone");
++ type = "timestamp with time zone";
+ } else if (paramType == Oid.TIME) {
+- p.append("::time");
++ type = "time";
+ } else if (paramType == Oid.TIMETZ) {
+- p.append("::time with time zone");
++ type = "time with time zone";
+ } else if (paramType == Oid.DATE) {
+- p.append("::date");
++ type = "date";
+ } else if (paramType == Oid.INTERVAL) {
+- p.append("::interval");
++ type = "interval";
+ } else if (paramType == Oid.NUMERIC) {
+- p.append("::numeric");
++ type = "numeric";
++ } else {
++ type = null;
+ }
+- return p.toString();
+ }
++ return quoteAndCast(textValue, type, standardConformingStrings);
+ }
+
+ @Override
diff --git a/postgresql-jdbc.spec b/postgresql-jdbc.spec
index b582f33..bdec0ef 100644
--- a/postgresql-jdbc.spec
+++ b/postgresql-jdbc.spec
@@ -31,12 +31,13 @@
Summary: JDBC driver for PostgreSQL
Name: postgresql-jdbc
Version: 42.2.14
-Release: 2%{?dist}
+Release: 3%{?dist}
License: BSD
URL: http://jdbc.postgresql.org/
Source0: https://repo1.maven.org/maven2/org/postgresql/postgresql/%{version}/postgresql-%{version}-src.tar.gz
Patch0: postgresql-jdbc-CVE-2022-41946.patch
+Patch1: postgresql-jdbc-CVE-2024-1597.patch
Provides: pgjdbc = %version-%release
BuildArch: noarch
@@ -67,6 +68,7 @@ This package contains the API Documentation for %{name}.
%prep
%setup -c -q
%patch -P 0 -p1
+%patch -P 1 -p2
# remove any binary libs
find -type f \( -name "*.jar" -or -name "*.class" \) | xargs rm -f
@@ -105,6 +107,9 @@ find -type f \( -name "*.jar" -or -name "*.class" \) | xargs rm -f
%changelog
+* Wed Feb 28 2024 Zuzana Miklankova