Skip to content

Commit f4d0ed6

Browse files
authored
Merge pull request from GHSA-v7wg-cpwc-24m4
This ensures arbitrary classes can't be passed instead of AuthenticationPlugin, SocketFactory, SSLSocketFactory, CallbackHandler, HostnameVerifier
1 parent d6e5312 commit f4d0ed6

File tree

6 files changed

+120
-11
lines changed

6 files changed

+120
-11
lines changed

Diff for: pgjdbc/src/main/java/org/postgresql/core/SocketFactoryFactory.java

+2-2
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ public static SocketFactory getSocketFactory(Properties info) throws PSQLExcepti
3636
return SocketFactory.getDefault();
3737
}
3838
try {
39-
return (SocketFactory) ObjectFactory.instantiate(socketFactoryClassName, info, true,
39+
return ObjectFactory.instantiate(SocketFactory.class, socketFactoryClassName, info, true,
4040
PGProperty.SOCKET_FACTORY_ARG.get(info));
4141
} catch (Exception e) {
4242
throw new PSQLException(
@@ -61,7 +61,7 @@ public static SSLSocketFactory getSslSocketFactory(Properties info) throws PSQLE
6161
return new LibPQFactory(info);
6262
}
6363
try {
64-
return (SSLSocketFactory) ObjectFactory.instantiate(classname, info, true,
64+
return ObjectFactory.instantiate(SSLSocketFactory.class, classname, info, true,
6565
PGProperty.SSL_FACTORY_ARG.get(info));
6666
} catch (Exception e) {
6767
throw new PSQLException(

Diff for: pgjdbc/src/main/java/org/postgresql/core/v3/AuthenticationPluginManager.java

+6-4
Original file line numberDiff line numberDiff line change
@@ -66,11 +66,12 @@ public static <T> T withPassword(AuthenticationRequestType type, Properties info
6666
} else {
6767
AuthenticationPlugin authPlugin;
6868
try {
69-
authPlugin = (AuthenticationPlugin) ObjectFactory.instantiate(authPluginClassName, info,
69+
authPlugin = ObjectFactory.instantiate(AuthenticationPlugin.class, authPluginClassName, info,
7070
false, null);
7171
} catch (Exception ex) {
72-
LOGGER.log(Level.FINE, "Unable to load Authentication Plugin " + ex.toString());
73-
throw new PSQLException(ex.getMessage(), PSQLState.UNEXPECTED_ERROR);
72+
String msg = GT.tr("Unable to load Authentication Plugin {0}", authPluginClassName);
73+
LOGGER.log(Level.FINE, msg, ex);
74+
throw new PSQLException(msg, PSQLState.INVALID_PARAMETER_VALUE, ex);
7475
}
7576

7677
password = authPlugin.getPassword(type);
@@ -106,7 +107,8 @@ public static <T> T withEncodedPassword(AuthenticationRequestType type, Properti
106107
byte[] encodedPassword = withPassword(type, info, password -> {
107108
if (password == null) {
108109
throw new PSQLException(
109-
GT.tr("The server requested password-based authentication, but no password was provided."),
110+
GT.tr("The server requested password-based authentication, but no password was provided by plugin {0}",
111+
PGProperty.AUTHENTICATION_PLUGIN_CLASS_NAME.get(info)),
110112
PSQLState.CONNECTION_REJECTED);
111113
}
112114
ByteBuffer buf = StandardCharsets.UTF_8.encode(CharBuffer.wrap(password));

Diff for: pgjdbc/src/main/java/org/postgresql/ssl/LibPQFactory.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ private CallbackHandler getCallbackHandler(
5656
String sslpasswordcallback = PGProperty.SSL_PASSWORD_CALLBACK.get(info);
5757
if (sslpasswordcallback != null) {
5858
try {
59-
cbh = (CallbackHandler) ObjectFactory.instantiate(sslpasswordcallback, info, false, null);
59+
cbh = ObjectFactory.instantiate(CallbackHandler.class, sslpasswordcallback, info, false, null);
6060
} catch (Exception e) {
6161
throw new PSQLException(
6262
GT.tr("The password callback class provided {0} could not be instantiated.",

Diff for: pgjdbc/src/main/java/org/postgresql/ssl/MakeSSL.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ private static void verifyPeerName(PGStream stream, Properties info, SSLSocket n
6464
sslhostnameverifier = "PgjdbcHostnameVerifier";
6565
} else {
6666
try {
67-
hvn = (HostnameVerifier) instantiate(sslhostnameverifier, info, false, null);
67+
hvn = instantiate(HostnameVerifier.class, sslhostnameverifier, info, false, null);
6868
} catch (Exception e) {
6969
throw new PSQLException(
7070
GT.tr("The HostnameVerifier class provided {0} could not be instantiated.",

Diff for: pgjdbc/src/main/java/org/postgresql/util/ObjectFactory.java

+4-3
Original file line numberDiff line numberDiff line change
@@ -36,14 +36,15 @@ public class ObjectFactory {
3636
* @throws IllegalAccessException if something goes wrong
3737
* @throws InvocationTargetException if something goes wrong
3838
*/
39-
public static Object instantiate(String classname, Properties info, boolean tryString,
39+
public static <T> T instantiate(Class<T> expectedClass, String classname, Properties info,
40+
boolean tryString,
4041
@Nullable String stringarg)
4142
throws ClassNotFoundException, SecurityException, NoSuchMethodException,
4243
IllegalArgumentException, InstantiationException, IllegalAccessException,
4344
InvocationTargetException {
4445
@Nullable Object[] args = {info};
45-
Constructor<?> ctor = null;
46-
Class<?> cls = Class.forName(classname);
46+
Constructor<? extends T> ctor = null;
47+
Class<? extends T> cls = Class.forName(classname).asSubclass(expectedClass);
4748
try {
4849
ctor = cls.getConstructor(Properties.class);
4950
} catch (NoSuchMethodException ignored) {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
/*
2+
* Copyright (c) 2022, PostgreSQL Global Development Group
3+
* See the LICENSE file in the project root for more information.
4+
*/
5+
6+
package org.postgresql.test.util;
7+
8+
import static org.junit.jupiter.api.Assertions.assertThrows;
9+
import static org.junit.jupiter.api.Assertions.assertEquals;
10+
import static org.junit.jupiter.api.Assertions.assertFalse;
11+
12+
import org.postgresql.PGProperty;
13+
import org.postgresql.jdbc.SslMode;
14+
import org.postgresql.test.TestUtil;
15+
import org.postgresql.util.ObjectFactory;
16+
import org.postgresql.util.PSQLState;
17+
18+
import org.junit.jupiter.api.Test;
19+
import org.junit.jupiter.api.Assertions;
20+
import org.opentest4j.MultipleFailuresError;
21+
22+
import java.sql.SQLException;
23+
import java.util.Properties;
24+
25+
import javax.net.SocketFactory;
26+
27+
public class ObjectFactoryTest {
28+
Properties props = new Properties();
29+
30+
static class BadObject {
31+
static boolean wasInstantiated = false;
32+
33+
BadObject() {
34+
wasInstantiated = true;
35+
throw new RuntimeException("I should not be instantiated");
36+
}
37+
}
38+
39+
private void testInvalidInstantiation(PGProperty prop, PSQLState expectedSqlState) {
40+
prop.set(props, BadObject.class.getName());
41+
42+
BadObject.wasInstantiated = false;
43+
SQLException ex = assertThrows(SQLException.class, () -> {
44+
TestUtil.openDB(props);
45+
});
46+
47+
try {
48+
Assertions.assertAll(
49+
() -> assertFalse(BadObject.wasInstantiated, "ObjectFactory should not have "
50+
+ "instantiated bad object for " + prop),
51+
() -> assertEquals(expectedSqlState.getState(), ex.getSQLState(), () -> "#getSQLState()"),
52+
() -> {
53+
assertThrows(
54+
ClassCastException.class,
55+
() -> {
56+
throw ex.getCause();
57+
},
58+
() -> "Wrong class specified for " + prop.name()
59+
+ " => ClassCastException is expected in SQLException#getCause()"
60+
);
61+
}
62+
);
63+
} catch (MultipleFailuresError e) {
64+
// Add the original exception so it is easier to understand the reason for the test to fail
65+
e.addSuppressed(ex);
66+
throw e;
67+
}
68+
}
69+
70+
@Test
71+
public void testInvalidSocketFactory() {
72+
testInvalidInstantiation(PGProperty.SOCKET_FACTORY, PSQLState.CONNECTION_FAILURE);
73+
}
74+
75+
@Test
76+
public void testInvalidSSLFactory() {
77+
TestUtil.assumeSslTestsEnabled();
78+
// We need at least "require" to trigger SslSockerFactory instantiation
79+
PGProperty.SSL_MODE.set(props, SslMode.REQUIRE.value);
80+
testInvalidInstantiation(PGProperty.SSL_FACTORY, PSQLState.CONNECTION_FAILURE);
81+
}
82+
83+
@Test
84+
public void testInvalidAuthenticationPlugin() {
85+
testInvalidInstantiation(PGProperty.AUTHENTICATION_PLUGIN_CLASS_NAME,
86+
PSQLState.INVALID_PARAMETER_VALUE);
87+
}
88+
89+
@Test
90+
public void testInvalidSslHostnameVerifier() {
91+
TestUtil.assumeSslTestsEnabled();
92+
// Hostname verification is done at verify-full level only
93+
PGProperty.SSL_MODE.set(props, SslMode.VERIFY_FULL.value);
94+
PGProperty.SSL_ROOT_CERT.set(props, TestUtil.getSslTestCertPath("goodroot.crt"));
95+
testInvalidInstantiation(PGProperty.SSL_HOSTNAME_VERIFIER, PSQLState.CONNECTION_FAILURE);
96+
}
97+
98+
@Test
99+
public void testInstantiateInvalidSocketFactory() {
100+
Properties props = new Properties();
101+
assertThrows(ClassCastException.class, () -> {
102+
ObjectFactory.instantiate(SocketFactory.class, BadObject.class.getName(), props,
103+
false, null);
104+
});
105+
}
106+
}

0 commit comments

Comments
 (0)