From 45690fbc42773415ef034419ed3f27d2974b78e1 Mon Sep 17 00:00:00 2001
From: Matthew Swift <matthew.swift@forgerock.com>
Date: Wed, 21 Nov 2012 23:24:40 +0000
Subject: [PATCH] Fix OPENDJ-649: Add supportedTLSCiphers and supportedTLSProtocols to RootDSE and system monitor

---
 opends/src/server/org/opends/server/protocols/ldap/LDAPConnectionHandler.java |  278 ++++++++++++++++++++++++++++++++++++-------------------
 1 files changed, 181 insertions(+), 97 deletions(-)

diff --git a/opends/src/server/org/opends/server/protocols/ldap/LDAPConnectionHandler.java b/opends/src/server/org/opends/server/protocols/ldap/LDAPConnectionHandler.java
index 78405e5..22066c7 100644
--- a/opends/src/server/org/opends/server/protocols/ldap/LDAPConnectionHandler.java
+++ b/opends/src/server/org/opends/server/protocols/ldap/LDAPConnectionHandler.java
@@ -40,14 +40,14 @@
 import java.net.InetSocketAddress;
 import java.net.SocketException;
 import java.nio.channels.*;
-import java.security.KeyManagementException;
-import java.security.NoSuchAlgorithmException;
 import java.util.*;
 import java.util.concurrent.Executors;
 import java.util.concurrent.ScheduledExecutorService;
 import java.util.concurrent.TimeUnit;
 
+import javax.net.ssl.KeyManager;
 import javax.net.ssl.SSLContext;
+import javax.net.ssl.SSLEngine;
 
 import org.opends.messages.Message;
 import org.opends.server.admin.server.ConfigurationChangeListener;
@@ -175,12 +175,6 @@
   // server.
   private AddressMask[] deniedClients;
 
-  // The set of SSL cipher suites that should be allowed.
-  private String[] enabledSSLCipherSuites;
-
-  // The set of SSL protocols that should be allowed.
-  private String[] enabledSSLProtocols;
-
   // The index to the request handler that will be used for the next
   // connection accepted by the server.
   private int requestHandlerIndex;
@@ -223,9 +217,10 @@
   // SSL instance name used in context creation.
   private static final String SSL_CONTEXT_INSTANCE_NAME = "TLS";
 
-  // SSL context.
+  // SSL context and engine - the engine is used for obtaining default SSL
+  // parameters.
   private SSLContext sslContext;
-  private boolean sslConfig = false;
+  private SSLEngine sslEngine;
 
   /**
    * Connection finalizer thread.
@@ -340,10 +335,25 @@
     allowedClients = config.getAllowedClient().toArray(new AddressMask[0]);
     deniedClients = config.getDeniedClient().toArray(new AddressMask[0]);
 
-    // Reconfigure SSL context if needed.
+    // Reconfigure SSL if needed.
+    protocol = config.isUseSSL() ? "LDAPS" : "LDAP";
     if (config.isUseSSL() || config.isAllowStartTLS())
     {
-      sslConfig = true;
+      try
+      {
+        sslContext = createSSLContext(config);
+        sslEngine = createSSLEngine(config, sslContext);
+      }
+      catch (DirectoryException e)
+      {
+        if (debugEnabled())
+        {
+          TRACER.debugCaught(DebugLogLevel.ERROR, e);
+        }
+        messages.add(e.getMessageObject());
+        return new ConfigChangeResult(e.getResultCode(), adminActionRequired,
+            messages);
+      }
     }
 
     if (config.isAllowLDAPV2())
@@ -501,29 +511,37 @@
 
 
   /**
-   * Retrieves the set of enabled SSL cipher suites configured for this
-   * connection handler.
-   *
-   * @return The set of enabled SSL cipher suites configured for this connection
-   *         handler.
+   * {@inheritDoc}
    */
-  public String[] getEnabledSSLCipherSuites()
+  public Collection<String> getEnabledSSLCipherSuites()
   {
-    return enabledSSLCipherSuites;
+    if (currentConfig.isUseSSL() || currentConfig.isAllowStartTLS())
+    {
+      final SSLEngine engine = sslEngine;
+      if (engine != null)
+      {
+        return Arrays.asList(engine.getEnabledCipherSuites());
+      }
+    }
+    return super.getEnabledSSLCipherSuites();
   }
 
 
 
   /**
-   * Retrieves the set of enabled SSL protocols configured for this connection
-   * handler.
-   *
-   * @return The set of enabled SSL protocols configured for this connection
-   *         handler.
+   * {@inheritDoc}
    */
-  public String[] getEnabledSSLProtocols()
+  public Collection<String> getEnabledSSLProtocols()
   {
-    return enabledSSLProtocols;
+    if (currentConfig.isUseSSL() || currentConfig.isAllowStartTLS())
+    {
+      final SSLEngine engine = sslEngine;
+      if (engine != null)
+      {
+        return Arrays.asList(engine.getEnabledProtocols());
+      }
+    }
+    return super.getEnabledSSLProtocols();
   }
 
 
@@ -669,8 +687,6 @@
       throw new InitializationException(message, e);
     }
 
-    protocol = "LDAP";
-
     // Save this configuration for future reference.
     currentConfig = config;
     enabled = config.isEnabled();
@@ -678,8 +694,24 @@
     allowedClients = config.getAllowedClient().toArray(new AddressMask[0]);
     deniedClients = config.getDeniedClient().toArray(new AddressMask[0]);
 
-    // Setup SSL context if needed.
-    if (config.isUseSSL() || config.isAllowStartTLS()) sslConfig = true;
+    // Configure SSL if needed.
+    protocol = config.isUseSSL() ? "LDAPS" : "LDAP";
+    if (config.isUseSSL() || config.isAllowStartTLS())
+    {
+      try
+      {
+        sslContext = createSSLContext(config);
+        sslEngine = createSSLEngine(config, sslContext);
+      }
+      catch (DirectoryException e)
+      {
+        if (debugEnabled())
+        {
+          TRACER.debugCaught(DebugLogLevel.ERROR, e);
+        }
+        throw new InitializationException(e.getMessageObject());
+      }
+    }
 
     // Save properties that cannot be dynamically modified.
     allowReuseAddress = config.isAllowTCPReuseAddress();
@@ -786,11 +818,11 @@
   {
     LDAPConnectionHandlerCfg config = (LDAPConnectionHandlerCfg) configuration;
 
-    // Attempt to bind to the listen port on all configured addresses to
-    // verify whether the connection handler will be able to start.
     if ((currentConfig == null)
         || (!currentConfig.isEnabled() && config.isEnabled()))
     {
+      // Attempt to bind to the listen port on all configured addresses to
+      // verify whether the connection handler will be able to start.
       for (InetAddress a : config.getListenAddress())
       {
         try
@@ -817,7 +849,31 @@
         }
       }
     }
-    return isConfigurationChangeAcceptable(config, unacceptableReasons);
+
+    if (config.isEnabled())
+    {
+      // Check that the SSL configuration is valid.
+      if (config.isUseSSL() || config.isAllowStartTLS())
+      {
+        try
+        {
+          SSLContext sslContext = createSSLContext(config);
+          createSSLEngine(config, sslContext);
+        }
+        catch (DirectoryException e)
+        {
+          if (debugEnabled())
+          {
+            TRACER.debugCaught(DebugLogLevel.ERROR, e);
+          }
+
+          unacceptableReasons.add(e.getMessageObject());
+          return false;
+        }
+      }
+    }
+
+    return true;
   }
 
 
@@ -828,8 +884,7 @@
   public boolean isConfigurationChangeAcceptable(
       LDAPConnectionHandlerCfg config, List<Message> unacceptableReasons)
   {
-    // All validation is performed by the admin framework.
-    return true;
+    return isConfigurationAcceptable(config, unacceptableReasons);
   }
 
 
@@ -1153,8 +1208,8 @@
     // Check to see if the core server rejected the
     // connection (e.g., already too many connections
     // established).
-    LDAPClientConnection clientConnection =
-        createClientConnection(clientChannel);
+    LDAPClientConnection clientConnection = new LDAPClientConnection(this,
+        clientChannel, getProtocol());
     if (clientConnection.getConnectionID() < 0)
     {
       // The connection will have already been closed.
@@ -1326,97 +1381,126 @@
 
 
 
-  private LDAPClientConnection createClientConnection(
-      SocketChannel socketChannel) throws DirectoryException
-  {
-    if (sslConfig)
-    {
-      configSSL(currentConfig);
-      sslConfig = false;
-    }
-    LDAPClientConnection c = new LDAPClientConnection(this, socketChannel,
-        getProtocol());
-    return c;
-  }
-
-
-
   /**
-   * Creates a TLS Byte Channel instance using the specified LDAP client
-   * connection and socket channel.
+   * Creates a TLS Byte Channel instance using the specified socket channel.
    *
-   * @param c
-   *          The client connection to use in the creation.
-   * @param socketChannel
+   * @param channel
    *          The socket channel to use in the creation.
    * @return A TLS Byte Channel instance.
    * @throws DirectoryException
    *           If the channel cannot be created.
    */
-  public TLSByteChannel getTLSByteChannel(LDAPClientConnection c,
-      ByteChannel socketChannel) throws DirectoryException
+  public TLSByteChannel getTLSByteChannel(ByteChannel channel)
+      throws DirectoryException
   {
-    return (TLSByteChannel.getTLSByteChannel(currentConfig, c, sslContext,
-        socketChannel));
+    SSLEngine sslEngine = createSSLEngine(currentConfig, sslContext);
+    return new TLSByteChannel(channel, sslEngine);
   }
 
 
 
-  private void configSSL(LDAPConnectionHandlerCfg config)
-      throws DirectoryException
+  private SSLEngine createSSLEngine(LDAPConnectionHandlerCfg config,
+      SSLContext sslContext) throws DirectoryException
   {
-    ResultCode resCode = DirectoryServer.getServerErrorResultCode();
     try
     {
-      String alias = config.getSSLCertNickname();
-      if (config.isUseSSL())
+      SSLEngine sslEngine = sslContext.createSSLEngine();
+      sslEngine.setUseClientMode(false);
+
+      final Set<String> protocols = config.getSSLProtocol();
+      if (!protocols.isEmpty())
       {
-        protocol = "LDAPS";
+        sslEngine.setEnabledProtocols(protocols.toArray(new String[0]));
       }
+
+      final Set<String> ciphers = config.getSSLCipherSuite();
+      if (!ciphers.isEmpty())
+      {
+        sslEngine.setEnabledCipherSuites(ciphers.toArray(new String[0]));
+      }
+
+      switch (config.getSSLClientAuthPolicy())
+      {
+      case DISABLED:
+        sslEngine.setNeedClientAuth(false);
+        sslEngine.setWantClientAuth(false);
+        break;
+      case REQUIRED:
+        sslEngine.setWantClientAuth(true);
+        sslEngine.setNeedClientAuth(true);
+        break;
+      case OPTIONAL:
+      default:
+        sslEngine.setNeedClientAuth(false);
+        sslEngine.setWantClientAuth(true);
+        break;
+      }
+
+      return sslEngine;
+    }
+    catch (Exception e)
+    {
+      if (debugEnabled())
+      {
+        TRACER.debugCaught(DebugLogLevel.ERROR, e);
+      }
+      ResultCode resCode = DirectoryServer.getServerErrorResultCode();
+      Message message = ERR_CONNHANDLER_SSL_CANNOT_INITIALIZE
+          .get(getExceptionMessage(e));
+      throw new DirectoryException(resCode, message, e);
+    }
+  }
+
+
+
+  private SSLContext createSSLContext(LDAPConnectionHandlerCfg config)
+      throws DirectoryException
+  {
+    try
+    {
       DN keyMgrDN = config.getKeyManagerProviderDN();
-      DN trustMgrDN = config.getTrustManagerProviderDN();
       KeyManagerProvider<?> keyManagerProvider = DirectoryServer
           .getKeyManagerProvider(keyMgrDN);
       if (keyManagerProvider == null)
+      {
         keyManagerProvider = new NullKeyManagerProvider();
-      TrustManagerProvider<?> trustManagerProvider = DirectoryServer
-          .getTrustManagerProvider(trustMgrDN);
-      if (trustManagerProvider == null)
-        trustManagerProvider = new NullTrustManagerProvider();
-      sslContext = SSLContext.getInstance(SSL_CONTEXT_INSTANCE_NAME);
+      }
+
+      String alias = config.getSSLCertNickname();
+      KeyManager[] keyManagers;
       if (alias == null)
       {
-        sslContext.init(keyManagerProvider.getKeyManagers(),
-            trustManagerProvider.getTrustManagers(), null);
+        keyManagers = keyManagerProvider.getKeyManagers();
       }
       else
       {
-        sslContext.init(
-            SelectableCertificateKeyManager.wrap(
-                keyManagerProvider.getKeyManagers(), alias),
-            trustManagerProvider.getTrustManagers(), null);
+        keyManagers = SelectableCertificateKeyManager.wrap(
+            keyManagerProvider.getKeyManagers(), alias);
       }
+
+      DN trustMgrDN = config.getTrustManagerProviderDN();
+      TrustManagerProvider<?> trustManagerProvider = DirectoryServer
+          .getTrustManagerProvider(trustMgrDN);
+      if (trustManagerProvider == null)
+      {
+        trustManagerProvider = new NullTrustManagerProvider();
+      }
+
+      SSLContext sslContext = SSLContext.getInstance(SSL_CONTEXT_INSTANCE_NAME);
+      sslContext.init(keyManagers, trustManagerProvider.getTrustManagers(),
+          null);
+      return sslContext;
     }
-    catch (NoSuchAlgorithmException nsae)
+    catch (Exception e)
     {
-      if (debugEnabled()) TRACER.debugCaught(DebugLogLevel.ERROR, nsae);
+      if (debugEnabled())
+      {
+        TRACER.debugCaught(DebugLogLevel.ERROR, e);
+      }
+      ResultCode resCode = DirectoryServer.getServerErrorResultCode();
       Message message = ERR_CONNHANDLER_SSL_CANNOT_INITIALIZE
-          .get(getExceptionMessage(nsae));
-      throw new DirectoryException(resCode, message, nsae);
-    }
-    catch (KeyManagementException kme)
-    {
-      if (debugEnabled()) TRACER.debugCaught(DebugLogLevel.ERROR, kme);
-      Message message = ERR_CONNHANDLER_SSL_CANNOT_INITIALIZE
-          .get(getExceptionMessage(kme));
-      throw new DirectoryException(resCode, message, kme);
-    }
-    catch (DirectoryException de)
-    {
-      if (debugEnabled()) TRACER.debugCaught(DebugLogLevel.ERROR, de);
-      Message message = ERR_CONNHANDLER_SSL_CANNOT_INITIALIZE
-          .get(getExceptionMessage(de));
-      throw new DirectoryException(resCode, message, de);
+          .get(getExceptionMessage(e));
+      throw new DirectoryException(resCode, message, e);
     }
   }
 

--
Gitblit v1.10.0