1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196
|
From: Markus Koschany <apo@debian.org>
Date: Sun, 3 Jul 2022 16:16:36 +0200
Subject: CVE-2022-21724
Origin: https://github.com/pgjdbc/pgjdbc/commit/f4d0ed69c0b3aae8531d83d6af4c57f22312c813
---
.../org/postgresql/core/SocketFactoryFactory.java | 4 +-
.../main/java/org/postgresql/ssl/LibPQFactory.java | 2 +-
.../src/main/java/org/postgresql/ssl/MakeSSL.java | 2 +-
.../java/org/postgresql/util/ObjectFactory.java | 7 +-
.../postgresql/test/util/ObjectFactoryTest.java | 106 +++++++++++++++++++++
5 files changed, 114 insertions(+), 7 deletions(-)
create mode 100644 pgjdbc/src/test/java/org/postgresql/test/util/ObjectFactoryTest.java
diff --git a/pgjdbc/src/main/java/org/postgresql/core/SocketFactoryFactory.java b/pgjdbc/src/main/java/org/postgresql/core/SocketFactoryFactory.java
index 09efa75..fe56354 100644
--- a/pgjdbc/src/main/java/org/postgresql/core/SocketFactoryFactory.java
+++ b/pgjdbc/src/main/java/org/postgresql/core/SocketFactoryFactory.java
@@ -36,7 +36,7 @@ public class SocketFactoryFactory {
return SocketFactory.getDefault();
}
try {
- return (SocketFactory) ObjectFactory.instantiate(socketFactoryClassName, info, true,
+ return ObjectFactory.instantiate(SocketFactory.class, socketFactoryClassName, info, true,
PGProperty.SOCKET_FACTORY_ARG.get(info));
} catch (Exception e) {
throw new PSQLException(
@@ -61,7 +61,7 @@ public class SocketFactoryFactory {
return new LibPQFactory(info);
}
try {
- return (SSLSocketFactory) ObjectFactory.instantiate(classname, info, true,
+ return ObjectFactory.instantiate(SSLSocketFactory.class, classname, info, true,
PGProperty.SSL_FACTORY_ARG.get(info));
} catch (Exception e) {
throw new PSQLException(
diff --git a/pgjdbc/src/main/java/org/postgresql/ssl/LibPQFactory.java b/pgjdbc/src/main/java/org/postgresql/ssl/LibPQFactory.java
index c0c34bd..4d4c1aa 100644
--- a/pgjdbc/src/main/java/org/postgresql/ssl/LibPQFactory.java
+++ b/pgjdbc/src/main/java/org/postgresql/ssl/LibPQFactory.java
@@ -77,7 +77,7 @@ public class LibPQFactory extends WrappedFactory {
String sslpasswordcallback = PGProperty.SSL_PASSWORD_CALLBACK.get(info);
if (sslpasswordcallback != null) {
try {
- cbh = (CallbackHandler) ObjectFactory.instantiate(sslpasswordcallback, info, false, null);
+ cbh = (CallbackHandler) MakeSSL.instantiate(CallbackHandler.class, sslpasswordcallback, info, false, null);
} catch (Exception e) {
throw new PSQLException(
GT.tr("The password callback class provided {0} could not be instantiated.",
diff --git a/pgjdbc/src/main/java/org/postgresql/ssl/MakeSSL.java b/pgjdbc/src/main/java/org/postgresql/ssl/MakeSSL.java
index e09d88e..4aa20d9 100644
--- a/pgjdbc/src/main/java/org/postgresql/ssl/MakeSSL.java
+++ b/pgjdbc/src/main/java/org/postgresql/ssl/MakeSSL.java
@@ -63,7 +63,7 @@ public class MakeSSL extends ObjectFactory {
sslhostnameverifier = "PgjdbcHostnameVerifier";
} else {
try {
- hvn = (HostnameVerifier) instantiate(sslhostnameverifier, info, false, null);
+ hvn = instantiate(HostnameVerifier.class, sslhostnameverifier, info, false, null);
} catch (Exception e) {
throw new PSQLException(
GT.tr("The HostnameVerifier class provided {0} could not be instantiated.",
diff --git a/pgjdbc/src/main/java/org/postgresql/util/ObjectFactory.java b/pgjdbc/src/main/java/org/postgresql/util/ObjectFactory.java
index 273ac6d..dd37812 100644
--- a/pgjdbc/src/main/java/org/postgresql/util/ObjectFactory.java
+++ b/pgjdbc/src/main/java/org/postgresql/util/ObjectFactory.java
@@ -34,13 +34,14 @@ public class ObjectFactory {
* @throws IllegalAccessException if something goes wrong
* @throws InvocationTargetException if something goes wrong
*/
- public static Object instantiate(String classname, Properties info, boolean tryString,
+ public static <T> T instantiate(Class<T> expectedClass, String classname, Properties info,
+ boolean tryString,
String stringarg) throws ClassNotFoundException, SecurityException, NoSuchMethodException,
IllegalArgumentException, InstantiationException, IllegalAccessException,
InvocationTargetException {
Object[] args = {info};
- Constructor<?> ctor = null;
- Class<?> cls = Class.forName(classname);
+ Constructor<? extends T> ctor = null;
+ Class<? extends T> cls = Class.forName(classname).asSubclass(expectedClass);
try {
ctor = cls.getConstructor(Properties.class);
} catch (NoSuchMethodException nsme) {
diff --git a/pgjdbc/src/test/java/org/postgresql/test/util/ObjectFactoryTest.java b/pgjdbc/src/test/java/org/postgresql/test/util/ObjectFactoryTest.java
new file mode 100644
index 0000000..e0a9d1f
--- /dev/null
+++ b/pgjdbc/src/test/java/org/postgresql/test/util/ObjectFactoryTest.java
@@ -0,0 +1,106 @@
+/*
+ * Copyright (c) 2022, PostgreSQL Global Development Group
+ * See the LICENSE file in the project root for more information.
+ */
+
+package org.postgresql.test.util;
+
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+
+import org.postgresql.PGProperty;
+import org.postgresql.jdbc.SslMode;
+import org.postgresql.test.TestUtil;
+import org.postgresql.util.ObjectFactory;
+import org.postgresql.util.PSQLState;
+
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.Assertions;
+import org.opentest4j.MultipleFailuresError;
+
+import java.sql.SQLException;
+import java.util.Properties;
+
+import javax.net.SocketFactory;
+
+public class ObjectFactoryTest {
+ Properties props = new Properties();
+
+ static class BadObject {
+ static boolean wasInstantiated = false;
+
+ BadObject() {
+ wasInstantiated = true;
+ throw new RuntimeException("I should not be instantiated");
+ }
+ }
+
+ private void testInvalidInstantiation(PGProperty prop, PSQLState expectedSqlState) {
+ prop.set(props, BadObject.class.getName());
+
+ BadObject.wasInstantiated = false;
+ SQLException ex = assertThrows(SQLException.class, () -> {
+ TestUtil.openDB(props);
+ });
+
+ try {
+ Assertions.assertAll(
+ () -> assertFalse(BadObject.wasInstantiated, "ObjectFactory should not have "
+ + "instantiated bad object for " + prop),
+ () -> assertEquals(expectedSqlState.getState(), ex.getSQLState(), () -> "#getSQLState()"),
+ () -> {
+ assertThrows(
+ ClassCastException.class,
+ () -> {
+ throw ex.getCause();
+ },
+ () -> "Wrong class specified for " + prop.name()
+ + " => ClassCastException is expected in SQLException#getCause()"
+ );
+ }
+ );
+ } catch (MultipleFailuresError e) {
+ // Add the original exception so it is easier to understand the reason for the test to fail
+ e.addSuppressed(ex);
+ throw e;
+ }
+ }
+
+ @Test
+ public void testInvalidSocketFactory() {
+ testInvalidInstantiation(PGProperty.SOCKET_FACTORY, PSQLState.CONNECTION_FAILURE);
+ }
+
+ @Test
+ public void testInvalidSSLFactory() {
+ TestUtil.assumeSslTestsEnabled();
+ // We need at least "require" to trigger SslSockerFactory instantiation
+ PGProperty.SSL_MODE.set(props, SslMode.REQUIRE.value);
+ testInvalidInstantiation(PGProperty.SSL_FACTORY, PSQLState.CONNECTION_FAILURE);
+ }
+
+ @Test
+ public void testInvalidAuthenticationPlugin() {
+ testInvalidInstantiation(PGProperty.AUTHENTICATION_PLUGIN_CLASS_NAME,
+ PSQLState.INVALID_PARAMETER_VALUE);
+ }
+
+ @Test
+ public void testInvalidSslHostnameVerifier() {
+ TestUtil.assumeSslTestsEnabled();
+ // Hostname verification is done at verify-full level only
+ PGProperty.SSL_MODE.set(props, SslMode.VERIFY_FULL.value);
+ PGProperty.SSL_ROOT_CERT.set(props, TestUtil.getSslTestCertPath("goodroot.crt"));
+ testInvalidInstantiation(PGProperty.SSL_HOSTNAME_VERIFIER, PSQLState.CONNECTION_FAILURE);
+ }
+
+ @Test
+ public void testInstantiateInvalidSocketFactory() {
+ Properties props = new Properties();
+ assertThrows(ClassCastException.class, () -> {
+ ObjectFactory.instantiate(SocketFactory.class, BadObject.class.getName(), props,
+ false, null);
+ });
+ }
+}
|