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 - 42.2.14-3 +- Fix CVE-2024-1597 + * Mon Jan 09 2023 Zuzana Miklankova - 42.2.14-2 - Fix CVE-2022-41946