From 2be645039124c47883165c83bff284824af5b9a9 Mon Sep 17 00:00:00 2001
From: Patrick Diligent <patrick.diligent@forgerock.com>
Date: Wed, 22 Jul 2015 12:37:35 +0000
Subject: [PATCH] OPENDJ-1056 CR-7615

---
 opendj-sdk/opendj-server-legacy/src/main/java/org/opends/server/extensions/FileBasedKeyManagerProvider.java |   88 ++++++++++++++++-----
 opendj-sdk/opendj-server-legacy/src/main/java/org/opends/server/protocols/ldap/LDAPConnectionHandler.java   |   41 +++++++++
 opendj-sdk/opendj-server-legacy/src/main/java/org/opends/server/protocols/http/HTTPConnectionHandler.java   |   37 ++++++++
 opendj-sdk/opendj-server-legacy/src/messages/org/opends/messages/protocol.properties                        |    9 +
 opendj-sdk/opendj-server-legacy/src/main/java/org/opends/server/util/SelectableCertificateKeyManager.java   |    8 +-
 opendj-sdk/opendj-server-legacy/src/main/java/org/opends/server/api/KeyManagerProvider.java                 |   25 ++++++
 opendj-sdk/opendj-server-legacy/src/messages/org/opends/messages/extension.properties                       |    4 
 7 files changed, 174 insertions(+), 38 deletions(-)

diff --git a/opendj-sdk/opendj-server-legacy/src/main/java/org/opends/server/api/KeyManagerProvider.java b/opendj-sdk/opendj-server-legacy/src/main/java/org/opends/server/api/KeyManagerProvider.java
index d7b086c..e6bfe18 100644
--- a/opendj-sdk/opendj-server-legacy/src/main/java/org/opends/server/api/KeyManagerProvider.java
+++ b/opendj-sdk/opendj-server-legacy/src/main/java/org/opends/server/api/KeyManagerProvider.java
@@ -22,7 +22,7 @@
  *
  *
  *      Copyright 2006-2008 Sun Microsystems, Inc.
- *      Portions Copyright 2014 ForgeRock AS
+ *      Portions Copyright 2015 ForgeRock AS
  */
 package org.opends.server.api;
 import org.forgerock.i18n.LocalizableMessage;
@@ -76,6 +76,29 @@
       throws ConfigException, InitializationException;
 
 
+  /**
+   *
+   * Verifies that an alias is defined in the scope of this Key Manager.
+   *
+   * @param alias
+   *          The alias to check.
+   * @return true if the alias exists, false otherwise
+   */
+  public boolean containsKeyWithAlias(String alias)
+  {
+    return true;
+  }
+
+  /**
+   *
+   * Verifies that the keystore has at least one usable key.
+   *
+   * @return true if the keystore has at least one usable key, false otherwise
+   */
+  public boolean containsAtLeastOneKey()
+  {
+    return true;
+  }
 
   /**
    * Indicates whether the provided configuration is acceptable for
diff --git a/opendj-sdk/opendj-server-legacy/src/main/java/org/opends/server/extensions/FileBasedKeyManagerProvider.java b/opendj-sdk/opendj-server-legacy/src/main/java/org/opends/server/extensions/FileBasedKeyManagerProvider.java
index 3636e02..d3ccd49 100644
--- a/opendj-sdk/opendj-server-legacy/src/main/java/org/opends/server/extensions/FileBasedKeyManagerProvider.java
+++ b/opendj-sdk/opendj-server-legacy/src/main/java/org/opends/server/extensions/FileBasedKeyManagerProvider.java
@@ -229,25 +229,41 @@
 
 
   /**
-   * Retrieves a set of <CODE>KeyManager</CODE> objects that may be used for
-   * interactions requiring access to a key manager.
-   *
-   * @return  A set of <CODE>KeyManager</CODE> objects that may be used for
-   *          interactions requiring access to a key manager.
-   *
-   * @throws  DirectoryException  If a problem occurs while attempting to obtain
-   *                              the set of key managers.
+   * {@inheritDoc}
    */
   @Override
-  public KeyManager[] getKeyManagers() throws DirectoryException
-  {
+  public boolean containsKeyWithAlias(String alias) {
+    KeyStore keyStore;
+
+    try {
+      keyStore = getKeystore();
+    } catch (DirectoryException e) {
+      return false;
+    }
+
+    try {
+      Enumeration<String> aliases = keyStore.aliases();
+      while (aliases.hasMoreElements()) {
+        String theAlias = aliases.nextElement();
+        if (alias.equals(theAlias) && keyStore.entryInstanceOf(alias, KeyStore.PrivateKeyEntry.class)) {
+          return true;
+        }
+      }
+    } catch (KeyStoreException e) {
+    }
+
+    return false;
+  }
+
+  private KeyStore getKeystore()
+          throws DirectoryException {
     KeyStore keyStore;
     try
     {
       keyStore = KeyStore.getInstance(keyStoreType);
 
       FileInputStream inputStream =
-           new FileInputStream(getFileForPath(keyStoreFile));
+              new FileInputStream(getFileForPath(keyStoreFile));
       try
       {
         keyStore.load(inputStream, keyStorePIN);
@@ -262,24 +278,36 @@
       logger.traceException(e);
 
       LocalizableMessage message = ERR_FILE_KEYMANAGER_CANNOT_LOAD.get(
-          keyStoreFile, getExceptionMessage(e));
+              keyStoreFile, getExceptionMessage(e));
       throw new DirectoryException(DirectoryServer.getServerErrorResultCode(),
-                                   message, e);
+              message, e);
     }
+    return keyStore;
+  }
 
-    try {
-      // Troubleshooting aid; Analyse the keystore for the presence of at least one private entry.
-      if (!findOneKeyEntry(keyStore))
-      {
-        logger.warn(INFO_NO_KEY_ENTRY_IN_KEYSTORE, keyStoreFile);
-      }
-    }
-    catch (Exception e) {
-      logger.traceException(e);
-    }
+    /**
+     * Retrieves a set of <CODE>KeyManager</CODE> objects that may be used for
+     * interactions requiring access to a key manager.
+     *
+     * @return  A set of <CODE>KeyManager</CODE> objects that may be used for
+     *          interactions requiring access to a key manager.
+     *
+     * @throws  DirectoryException  If a problem occurs while attempting to obtain
+     *                              the set of key managers.
+     */
+  @Override
+  public KeyManager[] getKeyManagers() throws DirectoryException
+  {
+    KeyStore keyStore = getKeystore();
 
     try
     {
+      if (! findOneKeyEntry(keyStore))
+      {
+        // Troubleshooting message to let now of possible config error
+        logger.error(ERR_NO_KEY_ENTRY_IN_KEYSTORE, keyStoreFile);
+      }
+
       String keyManagerAlgorithm = KeyManagerFactory.getDefaultAlgorithm();
       KeyManagerFactory keyManagerFactory =
            KeyManagerFactory.getInstance(keyManagerAlgorithm);
@@ -297,6 +325,20 @@
     }
   }
 
+  /** {@inheritDoc} */
+  @Override
+  public boolean containsAtLeastOneKey()
+  {
+    try
+    {
+      return findOneKeyEntry(getKeystore());
+   }
+    catch (Exception e) {
+      logger.traceException(e);
+    }
+    return false;
+  }
+
   private boolean findOneKeyEntry(KeyStore keyStore) throws KeyStoreException
   {
     Enumeration<String> aliases = keyStore.aliases();
diff --git a/opendj-sdk/opendj-server-legacy/src/main/java/org/opends/server/protocols/http/HTTPConnectionHandler.java b/opendj-sdk/opendj-server-legacy/src/main/java/org/opends/server/protocols/http/HTTPConnectionHandler.java
index 7019709..65f7e49 100644
--- a/opendj-sdk/opendj-server-legacy/src/main/java/org/opends/server/protocols/http/HTTPConnectionHandler.java
+++ b/opendj-sdk/opendj-server-legacy/src/main/java/org/opends/server/protocols/http/HTTPConnectionHandler.java
@@ -446,6 +446,8 @@
   public void initializeConnectionHandler(HTTPConnectionHandlerCfg config)
       throws ConfigException, InitializationException
   {
+    this.enabled = config.isEnabled();
+
     if (friendlyName == null)
     {
       friendlyName = config.dn().rdn().getAttributeValue(0).toString();
@@ -462,6 +464,7 @@
     // Configure SSL if needed.
     try
     {
+      // This call may disable the connector if wrong SSL settings
       configureSSL(config);
     }
     catch (DirectoryException e)
@@ -482,7 +485,6 @@
 
     this.initConfig = config;
     this.currentConfig = config;
-    this.enabled = this.currentConfig.isEnabled();
   }
 
   private String getHandlerName(HTTPConnectionHandlerCfg config)
@@ -649,6 +651,8 @@
     setName(handlerName);
 
     boolean lastIterationFailed = false;
+    boolean starting = true;
+
     while (!shutdownRequested)
     {
       // If this connection handler is not enabled, then just sleep
@@ -660,6 +664,20 @@
           stopHttpServer();
         }
 
+        if (starting)
+        {
+          // This may happen if there was an initialisation error
+          // which led to disable the connector.
+          // The main thread is waiting for the connector to listen
+          // on its port, which will not occur yet,
+          // so notify here to allow the server startup to complete.
+          synchronized (waitListen)
+          {
+            starting = false;
+            waitListen.notify();
+          }
+        }
+
         StaticUtils.sleep(1000);
         continue;
       }
@@ -992,9 +1010,17 @@
     DN keyMgrDN = config.getKeyManagerProviderDN();
     KeyManagerProvider<?> keyManagerProvider =
         DirectoryServer.getKeyManagerProvider(keyMgrDN);
-    if (keyManagerProvider == null)
-    {
+    if (keyManagerProvider == null) {
+      logger.error(ERR_NULL_KEY_PROVIDER_MANAGER, keyMgrDN, friendlyName);
+      logger.warn(INFO_DISABLE_CONNECTION, friendlyName);
       keyManagerProvider = new NullKeyManagerProvider();
+      enabled = false;
+    }
+    else if (! keyManagerProvider.containsAtLeastOneKey())
+    {
+      logger.error(ERR_INVALID_KEYSTORE, friendlyName);
+      logger.warn(INFO_DISABLE_CONNECTION, friendlyName);
+      enabled = false;
     }
 
     String alias = config.getSSLCertNickname();
@@ -1005,6 +1031,11 @@
     }
     else
     {
+      if (! keyManagerProvider.containsKeyWithAlias(alias)) {
+        logger.error(ERR_KEYSTORE_DOES_NOT_CONTAIN_ALIAS, alias, friendlyName);
+        logger.warn(INFO_DISABLE_CONNECTION, friendlyName);
+        enabled = false;
+      }
       keyManagers =
           SelectableCertificateKeyManager.wrap(keyManagerProvider
               .getKeyManagers(), alias);
diff --git a/opendj-sdk/opendj-server-legacy/src/main/java/org/opends/server/protocols/ldap/LDAPConnectionHandler.java b/opendj-sdk/opendj-server-legacy/src/main/java/org/opends/server/protocols/ldap/LDAPConnectionHandler.java
index 205dd87..bca67f2 100644
--- a/opendj-sdk/opendj-server-legacy/src/main/java/org/opends/server/protocols/ldap/LDAPConnectionHandler.java
+++ b/opendj-sdk/opendj-server-legacy/src/main/java/org/opends/server/protocols/ldap/LDAPConnectionHandler.java
@@ -674,6 +674,7 @@
     // Configure SSL if needed.
     try
     {
+      // This call may disable the connector if wrong SSL settings
       configureSSL(config);
     }
     catch (DirectoryException e)
@@ -929,6 +930,7 @@
   {
     setName(handlerName);
     boolean listening = false;
+    boolean starting = true;
 
     while (!shutdownRequested)
     {
@@ -944,6 +946,20 @@
           logger.info(NOTE_CONNHANDLER_STOPPED_LISTENING, handlerName);
         }
 
+        if (starting)
+        {
+          // This may happen if there was an initialisation error
+          // which led to disable the connector.
+          // The main thread is waiting for the connector to listen
+          // on its port, which will not occur yet,
+          // so notify here to allow the server startup to complete.
+          synchronized (waitListen)
+          {
+            starting = false;
+            waitListen.notify();
+          }
+        }
+
         StaticUtils.sleep(1000);
         continue;
       }
@@ -1376,6 +1392,15 @@
 
 
 
+  private void disableAndWarnIfUseSSL(LDAPConnectionHandlerCfg config)
+  {
+    if (config.isUseSSL())
+    {
+      logger.warn(INFO_DISABLE_CONNECTION, friendlyName);
+      enabled = false;
+    }
+  }
+
   private SSLContext createSSLContext(LDAPConnectionHandlerCfg config)
       throws DirectoryException
   {
@@ -1386,10 +1411,15 @@
           .getKeyManagerProvider(keyMgrDN);
       if (keyManagerProvider == null)
       {
-        if (config.isUseSSL()) {
-          logger.warn(INFO_NULL_KEY_PROVIDER_MANAGER, keyMgrDN, friendlyName);
-        }
+        logger.error(ERR_NULL_KEY_PROVIDER_MANAGER, keyMgrDN, friendlyName);
+        disableAndWarnIfUseSSL(config);
         keyManagerProvider = new NullKeyManagerProvider();
+        // The SSL connection is unusable without a key manager provider
+      }
+      else if (! keyManagerProvider.containsAtLeastOneKey())
+      {
+        logger.error(ERR_INVALID_KEYSTORE, friendlyName);
+        disableAndWarnIfUseSSL(config);
       }
 
       String alias = config.getSSLCertNickname();
@@ -1400,6 +1430,11 @@
       }
       else
       {
+        if (!keyManagerProvider.containsKeyWithAlias(alias))
+        {
+          logger.error(ERR_KEYSTORE_DOES_NOT_CONTAIN_ALIAS, alias, friendlyName);
+          disableAndWarnIfUseSSL(config);
+        }
         keyManagers = SelectableCertificateKeyManager.wrap(
             keyManagerProvider.getKeyManagers(), alias, friendlyName);
       }
diff --git a/opendj-sdk/opendj-server-legacy/src/main/java/org/opends/server/util/SelectableCertificateKeyManager.java b/opendj-sdk/opendj-server-legacy/src/main/java/org/opends/server/util/SelectableCertificateKeyManager.java
index 7ea0319..10f0b64 100644
--- a/opendj-sdk/opendj-server-legacy/src/main/java/org/opends/server/util/SelectableCertificateKeyManager.java
+++ b/opendj-sdk/opendj-server-legacy/src/main/java/org/opends/server/util/SelectableCertificateKeyManager.java
@@ -135,7 +135,7 @@
         }
       }
     }
-    logger.warn(INFO_KEYSTORE_DOES_NOT_CONTAIN_ALIAS, keyType, alias, componentName);
+    logger.warn(INFO_KEYSTORE_DOES_NOT_CONTAIN_ALIAS, componentName, keyType, alias);
     return null;
   }
 
@@ -175,7 +175,7 @@
       }
     }
 
-    logger.warn(INFO_KEYSTORE_DOES_NOT_CONTAIN_ALIAS, keyType, alias, componentName);
+    logger.warn(INFO_KEYSTORE_DOES_NOT_CONTAIN_ALIAS, componentName, keyType, alias);
     return null;
   }
 
@@ -210,7 +210,7 @@
       }
     }
 
-    logger.warn(INFO_KEYSTORE_DOES_NOT_CONTAIN_ALIAS, keyType, alias, componentName);
+    logger.warn(INFO_KEYSTORE_DOES_NOT_CONTAIN_ALIAS, componentName, keyType, alias);
     return null;
   }
 
@@ -249,7 +249,7 @@
       }
     }
 
-    logger.warn(INFO_KEYSTORE_DOES_NOT_CONTAIN_ALIAS, keyType, alias, componentName);
+    logger.warn(INFO_KEYSTORE_DOES_NOT_CONTAIN_ALIAS, componentName, keyType, alias);
     return null;
   }
 
diff --git a/opendj-sdk/opendj-server-legacy/src/messages/org/opends/messages/extension.properties b/opendj-sdk/opendj-server-legacy/src/messages/org/opends/messages/extension.properties
index bc04302..ac37b48 100644
--- a/opendj-sdk/opendj-server-legacy/src/messages/org/opends/messages/extension.properties
+++ b/opendj-sdk/opendj-server-legacy/src/messages/org/opends/messages/extension.properties
@@ -1023,5 +1023,5 @@
  definition '%s' is invalid because the range '%s' is missing the minus
 ERR_CHARSET_VALIDATOR_SHORT_RANGE_635=The provided character range \
  definition '%s' is invalid because the range '%s' is too short
-INFO_KEYSTORE_DOES_NOT_CONTAIN_ALIAS_636=The %s key with alias '%s' was not found for '%s'
-INFO_NO_KEY_ENTRY_IN_KEYSTORE_637=There is no key entry in keystore %s
\ No newline at end of file
+ERR_NO_KEY_ENTRY_IN_KEYSTORE_636=There is no private key entry in keystore %s
+INFO_KEYSTORE_DOES_NOT_CONTAIN_ALIAS_637=handshake for '%s': cipher is expecting key %s to be of type %s
\ No newline at end of file
diff --git a/opendj-sdk/opendj-server-legacy/src/messages/org/opends/messages/protocol.properties b/opendj-sdk/opendj-server-legacy/src/messages/org/opends/messages/protocol.properties
index ddca568..91d6c42 100644
--- a/opendj-sdk/opendj-server-legacy/src/messages/org/opends/messages/protocol.properties
+++ b/opendj-sdk/opendj-server-legacy/src/messages/org/opends/messages/protocol.properties
@@ -907,7 +907,12 @@
  whitespace character at the current position: %s
 ERR_GSER_NO_VALID_IDENTIFIEDCHOICE_1523=The GSER value does not \
  contain a valid IdentifiedChoiceValue at the current position: %s
-INFO_NULL_KEY_PROVIDER_MANAGER_1524=The keystore %s seems to be missing, \
-  this may render the secure port inoperative for '%s'
+ERR_NULL_KEY_PROVIDER_MANAGER_1524=The keystore %s seems to be missing, \
+  this may render the secure port inoperative for '%s'. \
+  Verify the keystore setting in the configuration.
 ERR_PROXYAUTH_AUTHZ_NOT_PERMITTED_1525=Authorization as '%s' specified in \
  the proxied authorization control is not permitted
+ERR_KEYSTORE_DOES_NOT_CONTAIN_ALIAS_1526=The key with alias '%s' was not found for '%s'. \
+  Verify that the keystore is properly configured
+ERR_INVALID_KEYSTORE_1527=No usable key was found for '%s'. Verify the keystore content
+INFO_DISABLE_CONNECTION_1528=Disabling %s

--
Gitblit v1.10.0