Uploaded image for project: 'Confluence Data Center'
  1. Confluence Data Center
  2. CONFSERVER-26523

Can't connect to LDAP over SSL when using Java 7

    XMLWordPrintable

Details

    Description

      Symptoms

      When connecting a directory to LDAP via SSL you will see an error like this in the web browser:

      Connection test failed. Response from the server:
      localhost:636; nested exception is javax.naming.CommunicationException: localhost:636 [Root exception is java.lang.RuntimeException: Unable to set hostname verification on SSLSocket]

      In the log file there will be an entry like this:

      012-09-05 16:59:49,184 ERROR [scheduler_Worker-2] [atlassian.crowd.directory.DbCachingDirectoryPoller] pollChanges Error occurred while refreshing the cache for directory [ 819201 ].
      com.atlassian.crowd.exception.OperationFailedException: org.springframework.ldap.CommunicationException: localhost:636; nested exception is javax.naming.CommunicationException: localhost:636 [Root exception is java.lang.RuntimeException: Unable to set hostname verification on SSLSocket]
          at com.atlassian.crowd.directory.SpringLDAPConnector.searchEntitiesWithRequestControls(SpringLDAPConnector.java:416)
          at com.atlassian.crowd.directory.SpringLDAPConnector.searchEntities(SpringLDAPConnector.java:384)
          at com.atlassian.crowd.directory.SpringLDAPConnector.searchUserObjects(SpringLDAPConnector.java:574)
          at com.atlassian.crowd.directory.SpringLDAPConnector.searchUsers(SpringLDAPConnector.java:943)
          at com.atlassian.crowd.directory.ldap.cache.RemoteDirectoryCacheRefresher.findAllRemoteUsers(RemoteDirectoryCacheRefresher.java:41)
          at com.atlassian.crowd.directory.ldap.cache.RemoteDirectoryCacheRefresher.synchroniseAllUsers(RemoteDirectoryCacheRefresher.java:60)
          at com.atlassian.crowd.directory.ldap.cache.AbstractCacheRefresher.synchroniseAll(AbstractCacheRefresher.java:40)
          at com.atlassian.crowd.directory.DbCachingRemoteDirectory.synchroniseCache(DbCachingRemoteDirectory.java:621)
          at com.atlassian.crowd.manager.directory.DirectorySynchroniserImpl.synchronise(DirectorySynchroniserImpl.java:63)
          at com.atlassian.crowd.directory.DbCachingDirectoryPoller.pollChanges(DbCachingDirectoryPoller.java:50)
          at com.atlassian.crowd.manager.directory.monitor.poller.DirectoryPollerJobBean.executeInternal(DirectoryPollerJobBean.java:29)
          at org.springframework.scheduling.quartz.QuartzJobBean.execute(QuartzJobBean.java:86)
          at org.quartz.core.JobRunShell.run(JobRunShell.java:199)
          at com.atlassian.confluence.schedule.quartz.ConfluenceQuartzThreadPool$1.run(ConfluenceQuartzThreadPool.java:20)
          at org.quartz.simpl.SimpleThreadPool$WorkerThread.run(SimpleThreadPool.java:549)
      Caused by: org.springframework.ldap.CommunicationException: localhost:636; nested exception is javax.naming.CommunicationException: localhost:636 [Root exception is java.lang.RuntimeException: Unable to set hostname verification on SSLSocket]
          at org.springframework.ldap.support.LdapUtils.convertLdapException(LdapUtils.java:98)
          at org.springframework.ldap.core.support.AbstractContextSource.createContext(AbstractContextSource.java:266)
          at org.springframework.ldap.core.support.AbstractContextSource.getContext(AbstractContextSource.java:106)
          at org.springframework.ldap.core.support.AbstractContextSource.getReadWriteContext(AbstractContextSource.java:138)
          at org.springframework.ldap.transaction.compensating.manager.TransactionAwareContextSourceProxy.getReadWriteContext(TransactionAwareContextSourceProxy.java:94)
          at org.springframework.ldap.transaction.compensating.manager.TransactionAwareContextSourceProxy.getReadOnlyContext(TransactionAwareContextSourceProxy.java:65)
          at org.springframework.ldap.core.LdapTemplate.search(LdapTemplate.java:287)
          at org.springframework.ldap.core.LdapTemplate.search(LdapTemplate.java:237)
          at org.springframework.ldap.core.LdapTemplate.search(LdapTemplate.java:624)
          at org.springframework.ldap.core.LdapTemplate.search(LdapTemplate.java:535)
          at com.atlassian.crowd.directory.ldap.LdapTemplateWithClassLoaderWrapper$1.call(LdapTemplateWithClassLoaderWrapper.java:56)
          at com.atlassian.crowd.directory.ldap.LdapTemplateWithClassLoaderWrapper$1.call(LdapTemplateWithClassLoaderWrapper.java:53)
          at com.atlassian.crowd.directory.ldap.LdapTemplateWithClassLoaderWrapper.invokeWithContextClassLoader(LdapTemplateWithClassLoaderWrapper.java:43)
          at com.atlassian.crowd.directory.ldap.LdapTemplateWithClassLoaderWrapper.search(LdapTemplateWithClassLoaderWrapper.java:53)
          at com.atlassian.crowd.directory.SpringLDAPConnector.searchEntitiesWithRequestControls(SpringLDAPConnector.java:412)
          ... 14 more
      Caused by: javax.naming.CommunicationException: localhost:636 [Root exception is java.lang.RuntimeException: Unable to set hostname verification on SSLSocket]
          at com.sun.jndi.ldap.Connection.<init>(Connection.java:214)
          at com.sun.jndi.ldap.LdapClient.<init>(LdapClient.java:136)
          at com.sun.jndi.ldap.LdapClient.getInstance(LdapClient.java:1600)
          at com.sun.jndi.ldap.LdapCtx.connect(LdapCtx.java:2698)
          at com.sun.jndi.ldap.LdapCtx.<init>(LdapCtx.java:316)
          at com.sun.jndi.ldap.LdapCtxFactory.getUsingURL(LdapCtxFactory.java:193)
          at com.sun.jndi.ldap.LdapCtxFactory.getUsingURLs(LdapCtxFactory.java:211)
          at com.sun.jndi.ldap.LdapCtxFactory.getLdapCtxInstance(LdapCtxFactory.java:154)
          at com.sun.jndi.ldap.LdapCtxFactory.getInitialContext(LdapCtxFactory.java:84)
          at javax.naming.spi.NamingManager.getInitialContext(NamingManager.java:684)
          at javax.naming.InitialContext.getDefaultInitCtx(InitialContext.java:307)
          at javax.naming.InitialContext.init(InitialContext.java:242)
          at javax.naming.ldap.InitialLdapContext.<init>(InitialLdapContext.java:153)
          at org.springframework.ldap.core.support.LdapContextSource.getDirContextInstance(LdapContextSource.java:43)
          at org.springframework.ldap.core.support.AbstractContextSource.createContext(AbstractContextSource.java:254)
          ... 27 more
      Caused by: java.lang.RuntimeException: Unable to set hostname verification on SSLSocket
          at com.atlassian.crowd.directory.ssl.LdapHostnameVerificationSSLSocketFactory.makeUseLdapVerification(LdapHostnameVerificationSSLSocketFactory.java:85)
          at com.atlassian.crowd.directory.ssl.LdapHostnameVerificationSSLSocketFactory.createSocket(LdapHostnameVerificationSSLSocketFactory.java:144)
          at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
          at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
          at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
          at java.lang.reflect.Method.invoke(Method.java:601)
          at com.sun.jndi.ldap.Connection.createSocket(Connection.java:305)
          at com.sun.jndi.ldap.Connection.<init>(Connection.java:201)
          ... 41 more
      Caused by: java.lang.NoSuchMethodException: sun.security.ssl.SSLSocketImpl.trySetHostnameVerification(java.lang.String)
          at java.lang.Class.getMethod(Class.java:1622)
          at com.atlassian.crowd.directory.ssl.LdapHostnameVerificationSSLSocketFactory.makeUseLdapVerification(LdapHostnameVerificationSSLSocketFactory.java:80)
          ... 48 more

      Steps to Reproduce

      1. Run Confluence using Java 7, not Java 6 - Java 7 is listed as a supported platform
      2. Import SSL certificate of the LDAP server into Confluence's JVM keystore as per normal procedure
      3. Add an LDAP directory, attempt to configure using SSL
      4. Observe the error message and log entry described above

      Workaround

      Use Java 6

      Cause

      I've pasted the original support case below, as it has a very detailed analysis of the cause:

      I am testing our upgrade to Confluence 4.x and I am finding that Confluence 4.x works fine with our configuration under Java 6, but fails under Java 7. If I shutdown, clear confluence-data/plugins-osgi-cache, switch to Java 6, and startup, it works. If I shutdown, clear confluence-data/plugins-osgi-cache, switch to Java 7, and startup it fails.

      In our environment, we use ldaps:// configuration to tie Confluence user management to our OpenLDAP servers. I don't want to provide our configuration here as it includes our internal hostname and authentication which should not need to be shared to reproduce or understand this issue.

      When running under Java 7, the error I get during startup (Embedded Crowd synchronization) and when I attempt to login (which fails) is:

      Caused by: java.lang.RuntimeException: Unable to set hostname verification on SSLSocket
              at com.atlassian.crowd.directory.ssl.LdapHostnameVerificationSSLSocketFactory.makeUseLdapVerification(LdapHostnameVerificationSSLSocketFactory.java:85)
              at com.atlassian.crowd.directory.ssl.LdapHostnameVerificationSSLSocketFactory.createSocket(LdapHostnameVerificationSSLSocketFactory.java:144)
              at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
              at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
              at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
              at java.lang.reflect.Method.invoke(Method.java:601)
              at com.sun.jndi.ldap.Connection.createSocket(Connection.java:305)
              at com.sun.jndi.ldap.Connection.<init>(Connection.java:201)
              ... 41 more
      Caused by: java.lang.NoSuchMethodException: sun.security.ssl.SSLSocketImpl.trySetHostnameVerification(java.lang.String)
              at java.lang.Class.getMethod(Class.java:1622)
              at com.atlassian.crowd.directory.ssl.LdapHostnameVerificationSSLSocketFactory.makeUseLdapVerification(LdapHostnameVerificationSSLSocketFactory.java:80)
              ... 48 more
      

      As I expect reproduction of our issue may be difficult, I performed my own investigation and found the following...

      In this file:

      crowd-ldap/src/main/java/com/atlassian/crowd/directory/ssl/LdapHostnameVerificationSSLSocketFactory.java

      It has this method:

          ...
          /**
           * Accept a <code>sun.security.ssl.SSLSocketImpl</code> or a <code>com.sun.net.ssl.internal.ssl.SSLSocketImpl</code>
           * and invoke <code>trySetHostnameVerification("ldap")</code> through reflection.
           */
          static void makeUseLdapVerification(Socket s)
          {
              ...
              try
              {
                  Method m = c.getMethod("trySetHostnameVerification", METHOD_ARG_TYPES);
                  m.invoke(s, "ldap");
              }
              catch (NoSuchMethodException e)
              {
                  throw new RuntimeException(UNABLE_TO_SET_MESSAGE, e);
              }
              ...
          }
          ...
      

      Tracing the issue further by analyzing the Java source, I find that:

      • Java 6 provides a method sun.security.ssl.SSLEngineImpl.trySetHostnameVerification() which appears to be what Embedded Crowd is trying to find and use.
      • Java 7 does NOT provide this method.

      Therefore, the code works in Java 6, but fails in Java 7 with "throw new RuntimeException(UNABLE_TO_SET_MESSAGE, e);" which results in the stack trace we are receiving.

      My conclusion is that the combination of Confluence 4.x + Java 7 + secure LDAP is not currently working. However, Confluence 4.x + Java 6 + secure LDAP does work. The Embedded Crowd implementation needs to be modified to support Java 7 + secure LDAP and tested.

      Please analyze the above, confirm with the Embedded Crowd design team what the expectation is around why this method needs to be called and what it should do in Java 7, and document the problem on jira.atlassian.com using terminology which will be familiar to your team to expedite correction.

      For now, I plan on proceeding with Java 6 at this time, with a hope to change to Java 7 in the future when this issue is addressed.

      Thanks!

      Attachments

        Issue Links

          Activity

            People

              Unassigned Unassigned
              dmason David Mason (Inactive)
              Votes:
              2 Vote for this issue
              Watchers:
              5 Start watching this issue

              Dates

                Created:
                Updated:
                Resolved: