Uploaded image for project: 'JDK'
  1. JDK
  2. JDK-8139942

The fix for JDK-8067694 breaks some ldap usage

XMLWordPrintable

      FULL PRODUCT VERSION :


      A DESCRIPTION OF THE PROBLEM :
      When an "ldaps" provider url is set in the environment, a connection timeout > 0 is specified 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-806769 was implemented some clients encounter a CertificateException. This occurs because the code inside the com.sun.jndi.ldap.Connection class's createSocket method[0] "prefers" to invoke socketFactory.createSocket() instead of socketFactory.createSocket(String host, int port) when a connection timeout > 0 is specified.


      [0] http://hg.openjdk.java.net/jdk8u/jdk8u/jdk/file/7d1d5f4d019a/src/share/classes/com/sun/jndi/ldap/Connection.java#l272

      REGRESSION. Last worked in version 8u60

      ADDITIONAL REGRESSION INFORMATION:
      java version "1.8.0_60"
      Java(TM) SE Runtime Environment (build 1.8.0_60-b27)
      Java HotSpot(TM) 64-Bit Server VM (build 25.60-b23, mixed mode)


      STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
      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.

      EXPECTED VERSUS ACTUAL BEHAVIOR :
      EXPECTED -
      No hostname verification error occurs when the remote ldaps resource has the correct hostname and has a certificate that is considered to be trusted.
      ACTUAL -
      A hostname verification error occurs when the remote ldaps resource has the correct hostname and has a certificate that is considered to be trusted.

      ERROR MESSAGES/STACK TRACES THAT OCCUR :
      ... 39 common frames omitted
      Caused by: javax.naming.CommunicationException: <DNS.NAME>:636
      at com.sun.jndi.ldap.Connection.<init>(Unknown Source) ~[na:1.8.0_51]
      at com.sun.jndi.ldap.LdapClient.<init>(Unknown Source) ~[na:1.8.0_51]
      ...
      ... 43 common frames omitted
      Caused by: javax.net.ssl.SSLHandshakeException: java.security.cert.CertificateException: No subject alternative names matching IP address XXXXX found
      at sun.security.ssl.Alerts.getSSLException(Unknown Source) ~[na:1.8.0_51]
      at sun.security.ssl.SSLSocketImpl.fatal(Unknown Source) ~[na:1.8.0_51]
      ...
      ... 58 common frames omitted
      Caused by: java.security.cert.CertificateException: No subject alternative names matching IP address XXXXX found
      at sun.security.util.HostnameChecker.matchIP(Unknown Source) ~[na:1.8.0_51]
      ...
      ... 67 common frames omitted

      REPRODUCIBILITY :
      This bug can be reproduced always.

      ---------- BEGIN SOURCE ----------
      Main.java:

      import java.util.Hashtable;

      import javax.naming.Context;
      import javax.naming.NamingException;
      import javax.naming.directory.DirContext;
      import javax.naming.directory.InitialDirContext;

      public class Main
      {
          // args[0] should look like "ldaps://example.com:10636/o=example"
          public static void main(String[] args) throws NamingException
          {
              ldapsConnect("Working", args[0]);
              System.out.println("Working worked :)");

              ldapsConnect("Broken", args[0]);
              System.out.println("Broken worked :(");
          }

          static void ldapsConnect(String classname, String ldapsUrl) throws NamingException
          {
              Hashtable<String, Object> environment = new Hashtable<String, Object>();
              environment.put(Context.SECURITY_PROTOCOL, "ssl");
              environment.put("java.naming.ldap.factory.socket", classname);
              environment.put(Context.INITIAL_CONTEXT_FACTORY,
                      "com.sun.jndi.ldap.LdapCtxFactory");
              environment.put(Context.PROVIDER_URL, ldapsUrl);
              environment.put("com.sun.jndi.ldap.connect.timeout", "5000");

              DirContext ctx = new InitialDirContext(environment);

              ctx.lookup("");
          }
      }

      ----
      Broken.java:

      import java.io.IOException;
      import java.net.InetAddress;
      import java.net.Socket;
      import java.net.UnknownHostException;
      import java.security.NoSuchAlgorithmException;

      import javax.net.ssl.SSLContext;
      import javax.net.ssl.SSLParameters;
      import javax.net.ssl.SSLSocket;
      import javax.net.ssl.SSLSocketFactory;

      public class Broken extends SSLSocketFactory
      {
          SSLSocketFactory sf;

          public Broken() throws NoSuchAlgorithmException
          {
              sf = SSLContext.getDefault().getSocketFactory();
          }

          public static SSLSocketFactory getDefault()
          {
              try
              {
                  return new Broken();
              } catch (NoSuchAlgorithmException e)
              {
                  throw new RuntimeException(e);
              }
          }

          @Override
          public String[] getDefaultCipherSuites()
          {
              throw new UnsupportedOperationException("Not implemented");
          }

          @Override
          public String[] getSupportedCipherSuites()
          {
              throw new UnsupportedOperationException("Not implemented");
          }

          @Override
          public Socket createSocket(Socket socket, String s, int i, boolean b) throws IOException
          {
              throw new UnsupportedOperationException("Not implemented");
          }

          @Override
          public Socket createSocket(String s, int i) throws IOException, UnknownHostException
          {
              throw new UnsupportedOperationException("Not implemented");
          }

          @Override
          public Socket createSocket(String s, int i, InetAddress inetAddress, int i1) throws IOException, UnknownHostException
          {
              throw new UnsupportedOperationException("Not implemented");
          }

          @Override
          public Socket createSocket(InetAddress inetAddress, int i) throws IOException
          {
              throw new UnsupportedOperationException("Not implemented");
          }

          @Override
          public Socket createSocket(InetAddress inetAddress, int i, InetAddress inetAddress1, int i1) throws IOException
          {
              throw new UnsupportedOperationException("Not implemented");
          }

          @Override
          public Socket createSocket() throws IOException {
              SSLSocket s = (SSLSocket) sf.createSocket();
              SSLParameters param = s.getSSLParameters();
              param.setEndpointIdentificationAlgorithm("LDAPS");
              s.setSSLParameters(param);
              return s;
          }
      }

      ----
      Working.java:

      import java.io.IOException;
      import java.net.Socket;
      import java.net.UnknownHostException;
      import java.security.NoSuchAlgorithmException;

      import javax.net.ssl.SSLContext;
      import javax.net.ssl.SSLParameters;
      import javax.net.ssl.SSLSocket;
      import javax.net.ssl.SSLSocketFactory;

      public class Working
      {
          SSLSocketFactory sf;

          public Working() throws NoSuchAlgorithmException
          {
              sf = SSLContext.getDefault().getSocketFactory();
          }

          public static Object getDefault()
          {
              try
              {
                  return new Working();
              } catch (NoSuchAlgorithmException e)
              {
                  throw new RuntimeException(e);
              }
          }

          public Socket createSocket(String host, int port) throws IOException, UnknownHostException
          {
              SSLSocket s = (SSLSocket) sf.createSocket(host, port);
              SSLParameters param = s.getSSLParameters();
              param.setEndpointIdentificationAlgorithm("LDAPS");
              s.setSSLParameters(param);
              return s;
          }
      }


      ---------- END SOURCE ----------

      CUSTOMER SUBMITTED WORKAROUND :
      Create and use a "SocketFactory" class that only implements the `getDefault()` and `createSocket(String host, int port)` methods.

            psonal Pallavi Sonal (Inactive)
            webbuggrp Webbug Group
            Votes:
            0 Vote for this issue
            Watchers:
            4 Start watching this issue

              Created:
              Updated:
              Resolved: