-
Bug
-
Resolution: Not an Issue
-
P3
-
None
-
8u51, 9
-
b01
Reported on OpenJDK:
http://mail.openjdk.java.net/pipermail/security-dev/2015-September/012845.html
David Black (Corrected with the right JDK bug number):
---------------------------------------------------------------------
As I do not have an account on https://bugs.openjdk.java.net, yes I have submitted a standard oracle java bug report, I thought it might be of interest to those on this mailing list to forward information on how some java ldap users since the JDK-8067695 fix may encounter invalid ldaps hostname issues.
When an "ldaps" provider url is set in the environment and a custom SocketFactory, which sets the SSLSocket endpoint identification algorithm to "LDAPS", is set in the ldap environment hostname because of the way the fix for JDK-8067695 was implemented some clients encounter a CertificateException. This occurs because the code inside the com.sun.jndi.ldap.Connection class createSocket method[0] "prefers" to invoke the socketFactory.createSocket() method instead of socketFactory.createSocket(String host, int port) when a connection timeout is set, which results in the host not being set in the created ssl socket instance.
Steps to reproduce:
1. git clone https://bitbucket.org/atlassian/cwd-4444-java-bug-reproducer.git
2. build the code - `cd src/main/java && javac Broken.java Main.java Working.java`
3. run the Main class and provide an ldaps url - `java Main ldaps://example.java:10636`
4. Observe that when the "Broken" SocketFactory is in use a hostname verification error occurs.
Workaround:
Naturally, one can workaround the Connection class's "preference" for the no argument createSocket method by not having such a method in the SocketFactory set in the ldap environment.
Untested potential patch:
--- Connection.java.orig 2015-09-25 11:39:26.323117929 +1000
+++ Connection.java 2015-09-25 12:41:04.175068697 +1000
@@ -33,6 +33,7 @@
import java.io.InputStream;
import java.net.Socket;
import javax.net.ssl.SSLSocket;
+import javax.net.ssl.SSLSocketFactory;
import javax.naming.CommunicationException;
import javax.naming.ServiceUnavailableException;
@@ -287,7 +288,40 @@
Method createSocket = null;
- if (connectTimeout > 0) {
+ if (connectTimeout > 0 && socketFactoryClass instanceof SSLSocketFactory) {
+
+ try {
+ Socket sock = null;
+ createSocket = socketFactoryClass.getMethod("createSocket",
+ new Class<?>[]{Socket.class, String.class,
+ int.class, boolean.class});
+ Constructor<Socket> socketCons =
+ Socket.class.getConstructor(new Class<?>[]{});
+
+ Method connect = Socket.class.getMethod("connect",
+ new Class<?>[]{Class.forName("java.net.SocketAddress"),
+ int.class});
+ Object endpoint = createInetSocketAddress(host, port);
+
+ // unconnected underlying socket
+ sock = socketCons.newInstance(new Object[]{});
+
+ if (debug) {
+ System.err.println("Connection: creating socket with " +
+ "a timeout");
+ }
+ // connect the underlying socket
+ connect.invoke(sock, new Object[]{
+ endpoint, new Integer(connectTimeout)});
+ // connect the ssl socket
+ socket = createSocket(sock, host, port, true);
+
+ } catch (NoSuchMethodException e) {
+ // continue
+ }
+ }
+
+ if (connectTimeout > 0 && socket == null) {
try {
createSocket = socketFactoryClass.getMethod("createSocket",
[0] http://hg.openjdk.java.net/jdk8u/jdk8u/jdk/file/7d1d5f4d019a/src/share/classes/com/sun/jndi/ldap/Connection.java#l272
--
David Black / Security Engineer.
http://mail.openjdk.java.net/pipermail/security-dev/2015-September/012845.html
David Black (Corrected with the right JDK bug number):
---------------------------------------------------------------------
As I do not have an account on https://bugs.openjdk.java.net, yes I have submitted a standard oracle java bug report, I thought it might be of interest to those on this mailing list to forward information on how some java ldap users since the JDK-8067695 fix may encounter invalid ldaps hostname issues.
When an "ldaps" provider url is set in the environment and a custom SocketFactory, which sets the SSLSocket endpoint identification algorithm to "LDAPS", is set in the ldap environment hostname because of the way the fix for JDK-8067695 was implemented some clients encounter a CertificateException. This occurs because the code inside the com.sun.jndi.ldap.Connection class createSocket method[0] "prefers" to invoke the socketFactory.createSocket() method instead of socketFactory.createSocket(String host, int port) when a connection timeout is set, which results in the host not being set in the created ssl socket instance.
Steps to reproduce:
1. git clone https://bitbucket.org/atlassian/cwd-4444-java-bug-reproducer.git
2. build the code - `cd src/main/java && javac Broken.java Main.java Working.java`
3. run the Main class and provide an ldaps url - `java Main ldaps://example.java:10636`
4. Observe that when the "Broken" SocketFactory is in use a hostname verification error occurs.
Workaround:
Naturally, one can workaround the Connection class's "preference" for the no argument createSocket method by not having such a method in the SocketFactory set in the ldap environment.
Untested potential patch:
--- Connection.java.orig 2015-09-25 11:39:26.323117929 +1000
+++ Connection.java 2015-09-25 12:41:04.175068697 +1000
@@ -33,6 +33,7 @@
import java.io.InputStream;
import java.net.Socket;
import javax.net.ssl.SSLSocket;
+import javax.net.ssl.SSLSocketFactory;
import javax.naming.CommunicationException;
import javax.naming.ServiceUnavailableException;
@@ -287,7 +288,40 @@
Method createSocket = null;
- if (connectTimeout > 0) {
+ if (connectTimeout > 0 && socketFactoryClass instanceof SSLSocketFactory) {
+
+ try {
+ Socket sock = null;
+ createSocket = socketFactoryClass.getMethod("createSocket",
+ new Class<?>[]{Socket.class, String.class,
+ int.class, boolean.class});
+ Constructor<Socket> socketCons =
+ Socket.class.getConstructor(new Class<?>[]{});
+
+ Method connect = Socket.class.getMethod("connect",
+ new Class<?>[]{Class.forName("java.net.SocketAddress"),
+ int.class});
+ Object endpoint = createInetSocketAddress(host, port);
+
+ // unconnected underlying socket
+ sock = socketCons.newInstance(new Object[]{});
+
+ if (debug) {
+ System.err.println("Connection: creating socket with " +
+ "a timeout");
+ }
+ // connect the underlying socket
+ connect.invoke(sock, new Object[]{
+ endpoint, new Integer(connectTimeout)});
+ // connect the ssl socket
+ socket = createSocket(sock, host, port, true);
+
+ } catch (NoSuchMethodException e) {
+ // continue
+ }
+ }
+
+ if (connectTimeout > 0 && socket == null) {
try {
createSocket = socketFactoryClass.getMethod("createSocket",
[0] http://hg.openjdk.java.net/jdk8u/jdk8u/jdk/file/7d1d5f4d019a/src/share/classes/com/sun/jndi/ldap/Connection.java#l272
--
David Black / Security Engineer.