From 2f9f867e65cb1e6aab842cde5133dfa557654641 Mon Sep 17 00:00:00 2001
From: coulbeck <coulbeck@localhost>
Date: Wed, 05 Sep 2007 19:44:39 +0000
Subject: [PATCH] Enable replication session authentication. - Adds a thread to synchronize the server instance certificates from the admin data branch to the local trust store backend. - Replaces the blind trust managers in the replication session with trust managers that use the local trust store.

---
 opendj-sdk/opends/src/server/org/opends/server/backends/TrustStoreBackend.java               |  142 ++++++++-
 opendj-sdk/opends/src/server/org/opends/server/core/DirectoryServer.java                     |    3 
 opendj-sdk/opends/src/messages/messages/backend.properties                                   |   17 
 opendj-sdk/opends/src/messages/messages/core.properties                                      |    8 
 opendj-sdk/opends/src/server/org/opends/server/core/TrustStoreSyncThread.java                |  708 +++++++++++++++++++++++++++++++++++++++++++++++
 opendj-sdk/opends/src/server/org/opends/server/replication/protocol/ReplSessionSecurity.java |    5 
 6 files changed, 857 insertions(+), 26 deletions(-)

diff --git a/opendj-sdk/opends/src/messages/messages/backend.properties b/opendj-sdk/opends/src/messages/messages/backend.properties
index 00aee65..1073a7a 100644
--- a/opendj-sdk/opends/src/messages/messages/backend.properties
+++ b/opendj-sdk/opends/src/messages/messages/backend.properties
@@ -877,9 +877,8 @@
 SEVERE_ERR_TRUSTSTORE_DN_DOES_NOT_SPECIFY_CERTIFICATE_302=Unable to process \
  entry %s in the trust store backend because the requested DN is one level \
  below the base DN but does not specify a certificate name
-SEVERE_ERR_TRUSTSTORE_INVALID_CERTIFICATE_303=Unable to retrieve entry %s \
- from the trust store backend because the requested certificate is invalid: \
- %s
+SEVERE_ERR_TRUSTSTORE_CANNOT_RETRIEVE_CERT_303=Error while trying to retrieve \
+ certificate %s from the trust store file %s: %s
 SEVERE_ERR_TRUSTSTORE_MODIFY_NOT_SUPPORTED_304=Modify operations are not \
  supported in the trust store backend
 SEVERE_ERR_TRUSTSTORE_MODIFY_DN_NOT_SUPPORTED_305=Modify DN operations are not \
@@ -897,10 +896,9 @@
 SEVERE_ERR_TRUSTSTORE_INVALID_TYPE_310=The trust store type %s \
  specified in attribute ds-cfg-trust-store-type of configuration entry %s is \
  not valid:  %s
-SEVERE_ERR_TRUSTSTORE_PIN_NO_SUCH_FILE_311=File %s specified in \
- attribute ds-cfg-trust-store-pin-file of configuration entry %s should \
- contain the PIN needed to access the trust store, but this file \
- does not exist
+SEVERE_ERR_TRUSTSTORE_PIN_FILE_CANNOT_CREATE_311=An error occurred while \
+ trying to create the PIN file %s specified in attribute \
+ ds-cfg-trust-store-pin-file of configuration entry %s
 SEVERE_ERR_TRUSTSTORE_PIN_FILE_CANNOT_READ_312=An error occurred while \
  trying to read the trust store PIN from file %s specified in configuration \
  attribute ds-cfg-trust-store-pin-file of configuration entry %s:  %s
@@ -962,4 +960,7 @@
  replace the value of the ds-task-state attribute with "cancel"
 INFO_TASKBE_RUNNING_TASK_CANCELLED_336=Task processing was interrupted by a \
  modify request to cancel the task
-
+SEVERE_ERR_TRUSTSTORE_CANNOT_DELETE_CERT_337=Error while trying to delete \
+ certificate %s from the trust store file %s: %s
+SEVERE_ERR_TRUSTSTORE_CERTIFICATE_NOT_FOUND_338=Unable to retrieve entry %s \
+ from the trust store backend because the certificate %s does not exist
diff --git a/opendj-sdk/opends/src/messages/messages/core.properties b/opendj-sdk/opends/src/messages/messages/core.properties
index c65a87e..49a1ac4 100644
--- a/opendj-sdk/opends/src/messages/messages/core.properties
+++ b/opendj-sdk/opends/src/messages/messages/core.properties
@@ -1655,3 +1655,11 @@
 INFO_DSCORE_DESCRIPTION_LASTKNOWNGOODCFG_652=Attempt to start using the \
  configuration that was in place at the last successful startup (if it is \
  available) rather than using the current active configuration
+INFO_TRUSTSTORESYNC_ADMIN_SUFFIX_SEARCH_FAILED_653=Error while searching base \
+ %s to synchronize the trust store: %s
+SEVERE_ERR_TRUSTSTORESYNC_EXCEPTION_654=An error occurred in the trust store \
+ synchronization thread: %s
+INFO_TRUSTSTORESYNC_ADD_FAILED_655=Error while trying to add entry %s \
+ to the trust store: %s
+INFO_TRUSTSTORESYNC_DELETE_FAILED_656=Error while trying to delete entry %s \
+ from the trust store: %s
diff --git a/opendj-sdk/opends/src/server/org/opends/server/backends/TrustStoreBackend.java b/opendj-sdk/opends/src/server/org/opends/server/backends/TrustStoreBackend.java
index 14ec3cb..32ef62d 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/backends/TrustStoreBackend.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/backends/TrustStoreBackend.java
@@ -41,7 +41,6 @@
 import java.security.KeyStoreException;
 
 import org.opends.server.api.Backend;
-import org.opends.server.api.TrustManagerProvider;
 import org.opends.server.config.ConfigException;
 import org.opends.server.core.AddOperation;
 import org.opends.server.core.DeleteOperation;
@@ -60,16 +59,18 @@
 import static org.opends.server.util.StaticUtils.*;
 import org.opends.server.util.Validator;
 import org.opends.server.util.CertificateManager;
+import org.opends.server.util.ExpirationCheckTrustManager;
 import org.opends.server.admin.server.ConfigurationChangeListener;
 import org.opends.server.admin.std.server.TrustStoreBackendCfg;
 import org.opends.server.admin.Configuration;
-import org.opends.server.extensions.BlindTrustManagerProvider;
 import org.opends.messages.Message;
 import static org.opends.messages.BackendMessages.*;
 
 import javax.net.ssl.KeyManager;
 import javax.net.ssl.KeyManagerFactory;
 import javax.net.ssl.TrustManager;
+import javax.net.ssl.TrustManagerFactory;
+import javax.net.ssl.X509TrustManager;
 import javax.naming.ldap.Rdn;
 import java.security.cert.Certificate;
 import java.net.UnknownHostException;
@@ -247,7 +248,7 @@
             }
             catch (Exception e)
             {
-              Message message = ERR_TRUSTSTORE_PIN_NO_SUCH_FILE.get(
+              Message message = ERR_TRUSTSTORE_PIN_FILE_CANNOT_CREATE.get(
                    String.valueOf(pinFilePath), String.valueOf(configEntryDN));
               throw new InitializationException(message);
             }
@@ -537,11 +538,17 @@
                                    baseDN, null);
     }
 
-
+    String certAlias = v.getStringValue();
     ByteString certValue;
     try
     {
-      Certificate cert = certificateManager.getCertificate(v.getStringValue());
+      Certificate cert = certificateManager.getCertificate(certAlias);
+      if (cert == null)
+      {
+        Message message = ERR_TRUSTSTORE_CERTIFICATE_NOT_FOUND.get(
+            String.valueOf(entryDN), certAlias);
+        throw new DirectoryException(ResultCode.NO_SUCH_OBJECT, message);
+      }
       certValue = new ASN1OctetString(cert.getEncoded());
     }
     catch (Exception e)
@@ -551,12 +558,11 @@
         TRACER.debugCaught(DebugLogLevel.ERROR, e);
       }
 
-      Message message = ERR_TRUSTSTORE_INVALID_CERTIFICATE.get(
-          String.valueOf(entryDN), e.getMessage());
+      Message message = ERR_TRUSTSTORE_CANNOT_RETRIEVE_CERT.get(
+          certAlias, trustStoreFile, e.getMessage());
       throw new DirectoryException(ResultCode.NO_SUCH_OBJECT, message);
     }
 
-
     // Construct the certificate entry to return.
     LinkedHashMap<ObjectClass,String> ocMap =
         new LinkedHashMap<ObjectClass,String>(2);
@@ -642,8 +648,22 @@
   public void deleteEntry(DN entryDN, DeleteOperation deleteOperation)
          throws DirectoryException
   {
-    Message message = ERR_TRUSTSTORE_DELETE_NOT_SUPPORTED.get();
-    throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM, message);
+    if (entryDN.equals(baseDN))
+    {
+      Message message = ERR_TRUSTSTORE_INVALID_BASE.get(
+           String.valueOf(entryDN));
+      throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM, message);
+    }
+
+    DN parentDN = entryDN.getParentDNInSuffix();
+    if (parentDN == null || !parentDN.equals(baseDN))
+    {
+      Message message = ERR_TRUSTSTORE_INVALID_BASE.get(
+           String.valueOf(entryDN));
+      throw new DirectoryException(ResultCode.NO_SUCH_OBJECT, message);
+    }
+
+    deleteCertificate(entryDN);
   }
 
 
@@ -664,7 +684,7 @@
    * {@inheritDoc}
    */
   public void renameEntry(DN currentDN, Entry entry,
-                                   ModifyDNOperation modifyDNOperation)
+                          ModifyDNOperation modifyDNOperation)
          throws DirectoryException
   {
     Message message = ERR_TRUSTSTORE_MODIFY_DN_NOT_SUPPORTED.get();
@@ -866,7 +886,7 @@
    * {@inheritDoc}
    */
   public void createBackup(BackupConfig backupConfig)
-  throws DirectoryException
+       throws DirectoryException
   {
     // This backend does not provide a backup/restore mechanism.
     Message message = ERR_TRUSTSTORE_BACKUP_AND_RESTORE_NOT_SUPPORTED.get();
@@ -1170,7 +1190,7 @@
             {
               resultCode = DirectoryServer.getServerErrorResultCode();
 
-              messages.add(ERR_TRUSTSTORE_PIN_NO_SUCH_FILE.get(
+              messages.add(ERR_TRUSTSTORE_PIN_FILE_CANNOT_CREATE.get(
                       String.valueOf(newPINFile),
                       String.valueOf(configEntryDN)));
             }
@@ -1360,10 +1380,57 @@
   public TrustManager[] getTrustManagers()
          throws DirectoryException
   {
-    // TODO Temporary until the trust store is populated with the certificates
-    // TODO   of all the servers in the ADS topology.
-    TrustManagerProvider trustManagerProvider = new BlindTrustManagerProvider();
-    return trustManagerProvider.getTrustManagers();
+    KeyStore trustStore;
+    try
+    {
+      trustStore = KeyStore.getInstance(trustStoreType);
+
+      FileInputStream inputStream =
+           new FileInputStream(getFileForPath(trustStoreFile));
+      trustStore.load(inputStream, trustStorePIN);
+      inputStream.close();
+    }
+    catch (Exception e)
+    {
+      if (debugEnabled())
+      {
+        TRACER.debugCaught(DebugLogLevel.ERROR, e);
+      }
+
+      Message message = ERR_TRUSTSTORE_CANNOT_LOAD.get(
+          trustStoreFile, getExceptionMessage(e));
+      throw new DirectoryException(DirectoryServer.getServerErrorResultCode(),
+                                   message, e);
+    }
+
+
+    try
+    {
+      String trustManagerAlgorithm = TrustManagerFactory.getDefaultAlgorithm();
+      TrustManagerFactory trustManagerFactory =
+           TrustManagerFactory.getInstance(trustManagerAlgorithm);
+      trustManagerFactory.init(trustStore);
+      TrustManager[] trustManagers = trustManagerFactory.getTrustManagers();
+      TrustManager[] newTrustManagers = new TrustManager[trustManagers.length];
+      for (int i=0; i < trustManagers.length; i++)
+      {
+        newTrustManagers[i] = new ExpirationCheckTrustManager(
+                                       (X509TrustManager) trustManagers[i]);
+      }
+      return newTrustManagers;
+    }
+    catch (Exception e)
+    {
+      if (debugEnabled())
+      {
+        TRACER.debugCaught(DebugLogLevel.ERROR, e);
+      }
+
+      Message message = ERR_TRUSTSTORE_CANNOT_CREATE_FACTORY.get(
+          trustStoreFile, getExceptionMessage(e));
+      throw new DirectoryException(DirectoryServer.getServerErrorResultCode(),
+                                   message, e);
+    }
   }
 
 
@@ -1387,7 +1454,7 @@
 
     try
     {
-      if (certificateManager.aliasInUse(v.getStringValue()))
+      if (certificateManager.aliasInUse(certAlias))
       {
         Message message = ERR_TRUSTSTORE_ALIAS_IN_USE.get(
              String.valueOf(entryDN));
@@ -1498,6 +1565,45 @@
   }
 
 
+  private void deleteCertificate(DN entryDN)
+       throws DirectoryException
+  {
+    // Make sure that the DN specifies a certificate alias.
+    AttributeType t =
+         DirectoryServer.getAttributeType(ATTR_CERT_ALIAS, true);
+    AttributeValue v = entryDN.getRDN().getAttributeValue(t);
+    if (v == null)
+    {
+      Message message = ERR_TRUSTSTORE_DN_DOES_NOT_SPECIFY_CERTIFICATE.get(
+           String.valueOf(entryDN));
+      throw new DirectoryException(ResultCode.CONSTRAINT_VIOLATION, message,
+                                   baseDN, null);
+    }
+    String certAlias = v.getStringValue();
+
+    try
+    {
+      if (!certificateManager.aliasInUse(certAlias))
+      {
+        Message message = ERR_TRUSTSTORE_INVALID_BASE.get(
+             String.valueOf(entryDN));
+        throw new DirectoryException(ResultCode.NO_SUCH_OBJECT,
+                                     message);
+      }
+
+      certificateManager.removeCertificate(certAlias);
+    }
+    catch (Exception e)
+    {
+      Message message = ERR_TRUSTSTORE_CANNOT_DELETE_CERT.get(
+           certAlias, trustStoreFile, getExceptionMessage(e));
+      throw new DirectoryException(
+           DirectoryServer.getServerErrorResultCode(), message, e);
+    }
+
+  }
+
+
   /**
    * Returns the validity period to be used to generate the ADS certificate.
    * @return The validity period to be used to generate the ADS certificate.
diff --git a/opendj-sdk/opends/src/server/org/opends/server/core/DirectoryServer.java b/opendj-sdk/opends/src/server/org/opends/server/core/DirectoryServer.java
index a6528b2..0b93dd2 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/core/DirectoryServer.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/core/DirectoryServer.java
@@ -1435,6 +1435,9 @@
       }
 
 
+      // Start a thread to synchronize the trust store.
+      new TrustStoreSyncThread().start();
+
       // If we should write a copy of the config on successful startup, then do
       // so now.
       if (saveConfigOnSuccessfulStartup)
diff --git a/opendj-sdk/opends/src/server/org/opends/server/core/TrustStoreSyncThread.java b/opendj-sdk/opends/src/server/org/opends/server/core/TrustStoreSyncThread.java
new file mode 100644
index 0000000..2b72403
--- /dev/null
+++ b/opendj-sdk/opends/src/server/org/opends/server/core/TrustStoreSyncThread.java
@@ -0,0 +1,708 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License").  You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at
+ * trunk/opends/resource/legal-notices/OpenDS.LICENSE
+ * or https://OpenDS.dev.java.net/OpenDS.LICENSE.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at
+ * trunk/opends/resource/legal-notices/OpenDS.LICENSE.  If applicable,
+ * add the following below this CDDL HEADER, with the fields enclosed
+ * by brackets "[]" replaced with your own identifying information:
+ *      Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ *
+ *
+ *      Portions Copyright 2007 Sun Microsystems, Inc.
+ */
+
+package org.opends.server.core;
+
+import org.opends.server.api.DirectoryThread;
+import org.opends.server.api.Backend;
+import org.opends.server.api.BackendInitializationListener;
+import org.opends.server.api.ServerShutdownListener;
+import org.opends.server.api.ChangeNotificationListener;
+import org.opends.server.loggers.debug.DebugTracer;
+import static org.opends.server.loggers.debug.DebugLogger.getTracer;
+import static org.opends.server.loggers.debug.DebugLogger.debugEnabled;
+import org.opends.server.loggers.ErrorLogger;
+import org.opends.server.types.*;
+import org.opends.server.types.operation.PostResponseAddOperation;
+import org.opends.server.types.operation.PostResponseDeleteOperation;
+import org.opends.server.types.operation.PostResponseModifyOperation;
+import org.opends.server.types.operation.PostResponseModifyDNOperation;
+import static org.opends.server.util.StaticUtils.stackTraceToSingleLineString;
+import static org.opends.server.util.ServerConstants.OC_TOP;
+import static org.opends.server.util.ServerConstants.
+     OID_ENTRY_CHANGE_NOTIFICATION;
+import org.opends.server.config.ConfigConstants;
+import static org.opends.server.config.ConfigConstants.OC_INSTANCE_KEY;
+import org.opends.server.protocols.internal.InternalClientConnection;
+import org.opends.server.protocols.internal.InternalSearchOperation;
+import org.opends.server.protocols.internal.InternalSearchListener;
+import org.opends.server.controls.PersistentSearchChangeType;
+import org.opends.server.controls.EntryChangeNotificationControl;
+import static org.opends.messages.CoreMessages.*;
+import org.opends.messages.Message;
+import org.opends.admin.ads.ADSContext;
+
+import java.util.concurrent.locks.Lock;
+import java.util.concurrent.locks.Condition;
+import java.util.concurrent.locks.ReentrantLock;
+import java.util.LinkedHashSet;
+import java.util.ArrayList;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.HashMap;
+
+/**
+ * This class defines a thread that synchronizes certificates from the admin
+ * data branch into the trust store backend.
+ */
+public class TrustStoreSyncThread extends DirectoryThread
+     implements ServerShutdownListener, BackendInitializationListener,
+     InternalSearchListener, ChangeNotificationListener
+{
+  /**
+   * The debug log tracer for this object.
+   */
+  private static final DebugTracer TRACER = getTracer();
+
+
+
+  // A lock and condition for notifying this thread.
+  private Lock lock;
+  private Condition condition;
+
+  // The DN of the administration suffix.
+  private DN adminSuffixDN;
+
+  // The DN of the instance keys container within the admin suffix.
+  private DN instanceKeysDN;
+
+  // The DN of the trust store root.
+  private DN trustStoreRootDN;
+
+  // The attribute type that is used to specify a server instance certificate.
+  AttributeType attrCert;
+
+  // The attribute type that holds a server certificate identifier.
+  AttributeType attrAlias;
+
+  // The attribute type that holds the time a key was compromised.
+  AttributeType attrCompromisedTime;
+
+  // A filter on the instance key object class.
+  private SearchFilter instanceKeyFilter;
+
+  // Indicates whether the ADS suffix backend is initialized.
+  private boolean adminBackendInitialized;
+
+  // Indicates whether the trust store backend is initialized.
+  private boolean trustStoreBackendInitialized;
+
+  // Indicates whether a shutdown request has been received.
+  private boolean shutdownRequested;
+
+  // Indicates whether the initial search has been done.
+  private boolean searchDone;
+
+  /**
+   * Creates a new instance of this trust store synchronization thread.
+   */
+  public TrustStoreSyncThread()
+  {
+    super("Trust Store Synchronization Thread");
+    setDaemon(true);
+
+    shutdownRequested = false;
+    adminBackendInitialized = false;
+    trustStoreBackendInitialized = false;
+    searchDone = false;
+
+    DirectoryServer.registerShutdownListener(this);
+    DirectoryServer.registerBackendInitializationListener(this);
+
+    lock = new ReentrantLock();
+    condition = lock.newCondition();
+
+    try
+    {
+      adminSuffixDN = DN.decode(ADSContext.getAdministrationSuffixDN());
+      instanceKeysDN = adminSuffixDN.concat(DN.decode("cn=instance keys"));
+      trustStoreRootDN = DN.decode(ConfigConstants.DN_TRUST_STORE_ROOT);
+      instanceKeyFilter =
+           SearchFilter.createFilterFromString(
+                "(objectclass=" + ConfigConstants.OC_INSTANCE_KEY + ")");
+    }
+    catch (DirectoryException e)
+    {
+      //
+    }
+
+    attrCert = DirectoryServer.getAttributeType(
+         ConfigConstants.ATTR_ADS_CERTIFICATE, true);
+    attrAlias = DirectoryServer.getAttributeType(
+         ConfigConstants.ATTR_CERT_ALIAS, true);
+    attrCompromisedTime = DirectoryServer.getAttributeType(
+         "ds-cfg-key-compromised-time", true);
+
+    if (DirectoryServer.getBackendWithBaseDN(adminSuffixDN) != null)
+    {
+      adminBackendInitialized = true;
+    }
+
+    if (DirectoryServer.getBackendWithBaseDN(trustStoreRootDN) != null)
+    {
+      trustStoreBackendInitialized = true;
+    }
+
+  }
+
+
+//  We would need this to detect changes if the admin branch was remote.
+//  private SearchOperation runPersistentSearch()
+//  {
+//    InternalClientConnection conn =
+//         InternalClientConnection.getRootConnection();
+//    LinkedHashSet<String> attributes = new LinkedHashSet<String>(0);
+//
+//    // Specify the persistent search control.
+//    Set<PersistentSearchChangeType> changeTypes =
+//         new HashSet<PersistentSearchChangeType>();
+//    changeTypes.add(PersistentSearchChangeType.ADD);
+//    changeTypes.add(PersistentSearchChangeType.DELETE);
+//    changeTypes.add(PersistentSearchChangeType.MODIFY);
+//
+//    boolean changesOnly = false;
+//    boolean returnECs = true;
+//
+//    PersistentSearchControl psc =
+//         new PersistentSearchControl(changeTypes, changesOnly, returnECs);
+//    ArrayList<Control> controls = new ArrayList<Control>(1);
+//    controls.add(psc);
+//
+//    InternalSearchOperation searchOperation =
+//         new InternalSearchOperation(
+//              conn,
+//              InternalClientConnection.nextOperationID(),
+//              InternalClientConnection.nextMessageID(),
+//              controls,
+//              adminSuffixDN, SearchScope.WHOLE_SUBTREE,
+//              DereferencePolicy.NEVER_DEREF_ALIASES,
+//              0, 0,
+//              false, instanceKeyFilter, attributes,
+//              this);
+//
+//    searchOperation.run();
+//
+//    return searchOperation;
+//  }
+
+
+  private SearchOperation runSearch()
+  {
+    InternalClientConnection conn =
+         InternalClientConnection.getRootConnection();
+    LinkedHashSet<String> attributes = new LinkedHashSet<String>(0);
+
+    ArrayList<Control> controls = new ArrayList<Control>(0);
+
+    InternalSearchOperation searchOperation =
+         new InternalSearchOperation(conn,
+                                     InternalClientConnection.nextOperationID(),
+                                     InternalClientConnection.nextMessageID(),
+                                     controls,
+                                     adminSuffixDN, SearchScope.WHOLE_SUBTREE,
+                                     DereferencePolicy.NEVER_DEREF_ALIASES,
+                                     0, 0,
+                                     false, instanceKeyFilter, attributes,
+                                     this);
+
+    searchOperation.run();
+
+    return searchOperation;
+  }
+
+
+  /**
+   * Performs an initial search on the ADS branch when enabled, then listens
+   * for changes within the branch, writing certificates from instance key
+   * entries to the trust store backend.
+   */
+  public void run()
+  {
+    while (!shutdownRequested)
+    {
+      try
+      {
+        if (!searchDone && adminBackendInitialized &&
+             trustStoreBackendInitialized)
+        {
+          SearchOperation searchOperation = runSearch();
+
+          ResultCode resultCode = searchOperation.getResultCode();
+          if (resultCode != ResultCode.SUCCESS)
+          {
+            Message message =
+                 INFO_TRUSTSTORESYNC_ADMIN_SUFFIX_SEARCH_FAILED.get(
+                      String.valueOf(adminSuffixDN),
+                      searchOperation.getErrorMessage().toString());
+            ErrorLogger.logError(message);
+          }
+          searchDone = true;
+          DirectoryServer.registerChangeNotificationListener(this);
+        }
+
+        // Wait until a backend changes state or a shutdown is requested.
+        awaitCondition();
+      }
+      catch (Exception e)
+      {
+        if (debugEnabled())
+        {
+          TRACER.debugCaught(DebugLogLevel.ERROR, e);
+        }
+
+        Message message = ERR_TRUSTSTORESYNC_EXCEPTION.get(
+             stackTraceToSingleLineString(e));
+        ErrorLogger.logError(message);
+      }
+    }
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  public String getShutdownListenerName()
+  {
+    return "Trust Store Synchronization Thread";
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  public void processServerShutdown(Message reason)
+  {
+    shutdownRequested = true;
+    notifyCondition();
+  }
+
+  private void notifyCondition()
+  {
+    lock.lock();
+    try
+    {
+      condition.signalAll();
+    }
+    finally
+    {
+      lock.unlock();
+    }
+  }
+
+
+  private void awaitCondition()
+  {
+    lock.lock();
+    try
+    {
+      condition.await();
+    }
+    catch (InterruptedException e)
+    {
+      // ignore
+    }
+    finally
+    {
+      lock.unlock();
+    }
+  }
+
+
+  /**
+   * {@inheritDoc}
+   */
+  public void performBackendInitializationProcessing(Backend backend)
+  {
+    boolean notify = false;
+
+    DN[] baseDNs = backend.getBaseDNs();
+    if (baseDNs != null)
+    {
+      for (DN baseDN : baseDNs)
+      {
+        if (baseDN.equals(adminSuffixDN))
+        {
+          adminBackendInitialized = true;
+          notify = true;
+        }
+        else if (baseDN.equals(trustStoreRootDN))
+        {
+          trustStoreBackendInitialized = true;
+          notify = true;
+        }
+      }
+    }
+
+    if (notify)
+    {
+      notifyCondition();
+    }
+  }
+
+  /**
+   * {@inheritDoc}
+   */
+  public void performBackendFinalizationProcessing(Backend backend)
+  {
+    boolean notify = false;
+
+    DN[] baseDNs = backend.getBaseDNs();
+    if (baseDNs != null)
+    {
+      for (DN baseDN : baseDNs)
+      {
+        if (baseDN.equals(adminSuffixDN))
+        {
+          adminBackendInitialized = false;
+          notify = true;
+        }
+        else if (baseDN.equals(trustStoreRootDN))
+        {
+          adminBackendInitialized = false;
+          notify = true;
+        }
+      }
+    }
+
+    if (notify)
+    {
+      notifyCondition();
+    }
+  }
+
+  /**
+   * {@inheritDoc}
+   */
+  public void handleInternalSearchEntry(InternalSearchOperation searchOperation,
+                                        SearchResultEntry searchEntry)
+       throws DirectoryException
+  {
+    RDN srcRDN = searchEntry.getDN().getRDN();
+
+    // Only process the entry if it has the expected form of RDN.
+    if (!srcRDN.isMultiValued() &&
+         srcRDN.getAttributeType(0).equals(attrAlias))
+    {
+      DN dstDN = trustStoreRootDN.concat(srcRDN);
+
+      // Extract any change notification control.
+      EntryChangeNotificationControl ecn = null;
+      List<Control> controls = searchEntry.getControls();
+      try
+      {
+        for (Control c : controls)
+        {
+          if (c.getOID().equals(OID_ENTRY_CHANGE_NOTIFICATION))
+          {
+            ecn = EntryChangeNotificationControl.decodeControl(c);
+          }
+        }
+      }
+      catch (LDAPException e)
+      {
+        // ignore
+      }
+
+      // Get any existing local trust store entry.
+      Entry dstEntry = DirectoryServer.getEntry(dstDN);
+
+      if (ecn != null &&
+           ecn.getChangeType() == PersistentSearchChangeType.DELETE)
+      {
+        // The entry was deleted so we should remove it from the local trust
+        // store.
+        if (dstEntry != null)
+        {
+          deleteEntry(dstDN);
+        }
+      }
+      else if (searchEntry.hasAttribute(attrCompromisedTime))
+      {
+        // The key was compromised so we should remove it from the local
+        // trust store.
+        if (dstEntry != null)
+        {
+          deleteEntry(dstDN);
+        }
+      }
+      else
+      {
+        // The entry was added or modified.
+        if (dstEntry == null)
+        {
+          addEntry(searchEntry, dstDN);
+        }
+        else
+        {
+          modifyEntry(searchEntry, dstEntry);
+        }
+      }
+    }
+  }
+
+
+  /**
+   * Modify an entry in the local trust store if it differs from an entry in
+   * the ADS branch.
+   * @param srcEntry The instance key entry in the ADS branch.
+   * @param dstEntry The local trust store entry.
+   */
+  private void modifyEntry(Entry srcEntry, Entry dstEntry)
+  {
+    List<Attribute> srcList;
+    srcList = srcEntry.getAttribute(attrCert);
+
+    List<Attribute> dstList;
+    dstList = dstEntry.getAttribute(attrCert);
+
+    // Check for changes to the certificate value.
+    boolean differ = false;
+    if (srcList == null)
+    {
+      if (dstList != null)
+      {
+        differ = true;
+      }
+    }
+    else if (dstList == null)
+    {
+      differ = true;
+    }
+    else if (srcList.size() != dstList.size())
+    {
+      differ = true;
+    }
+    else
+    {
+      if (!srcList.equals(dstList))
+      {
+        differ = true;
+      }
+    }
+
+    if (differ)
+    {
+      // The trust store backend does not implement modify so we need to
+      // delete then add.
+      DN dstDN = dstEntry.getDN();
+      deleteEntry(dstDN);
+      addEntry(srcEntry, dstDN);
+    }
+  }
+
+
+  /**
+   * Delete an entry from the local trust store.
+   * @param dstDN The DN of the entry to be deleted in the local trust store.
+   */
+  private void deleteEntry(DN dstDN)
+  {
+    InternalClientConnection conn =
+         InternalClientConnection.getRootConnection();
+
+    DeleteOperation delOperation = conn.processDelete(dstDN);
+
+    if (delOperation.getResultCode() != ResultCode.SUCCESS)
+    {
+      Message message = INFO_TRUSTSTORESYNC_DELETE_FAILED.get(
+           String.valueOf(dstDN),
+           String.valueOf(delOperation.getErrorMessage()));
+      ErrorLogger.logError(message);
+    }
+  }
+
+
+  /**
+   * Add an entry to the local trust store.
+   * @param srcEntry The instance key entry in the ADS branch.
+   * @param dstDN The DN of the entry to be added in the local trust store.
+   */
+  private void addEntry(Entry srcEntry, DN dstDN)
+  {
+    ObjectClass instanceKeyOC =
+         DirectoryServer.getObjectClass(OC_INSTANCE_KEY, true);
+
+    LinkedHashMap<ObjectClass,String> ocMap =
+         new LinkedHashMap<ObjectClass,String>(2);
+    ocMap.put(DirectoryServer.getTopObjectClass(), OC_TOP);
+    ocMap.put(instanceKeyOC, OC_INSTANCE_KEY);
+
+    HashMap<AttributeType, List<Attribute>> userAttrs =
+         new HashMap<AttributeType, List<Attribute>>();
+
+    List<Attribute> attrList;
+    attrList = srcEntry.getAttribute(attrAlias);
+    if (attrList != null)
+    {
+      userAttrs.put(attrAlias, attrList);
+    }
+    attrList = srcEntry.getAttribute(attrCert);
+    if (attrList != null)
+    {
+      userAttrs.put(attrCert, attrList);
+    }
+
+    Entry addEntry = new Entry(dstDN, ocMap, userAttrs, null);
+
+    InternalClientConnection conn =
+         InternalClientConnection.getRootConnection();
+
+    AddOperation addOperation = conn.processAdd(addEntry);
+    if (addOperation.getResultCode() != ResultCode.SUCCESS)
+    {
+      Message message = INFO_TRUSTSTORESYNC_ADD_FAILED.get(
+           String.valueOf(dstDN),
+           String.valueOf(addOperation.getErrorMessage()));
+      ErrorLogger.logError(message);
+    }
+  }
+
+
+  /**
+   * {@inheritDoc}
+   */
+  public void handleInternalSearchReference(
+       InternalSearchOperation searchOperation,
+       SearchResultReference searchReference) throws DirectoryException
+  {
+    // No implementation required.
+  }
+
+  /**
+   * {@inheritDoc}
+   */
+  public void handleAddOperation(PostResponseAddOperation addOperation,
+                                 Entry entry)
+  {
+    if (!addOperation.getEntryDN().isDescendantOf(instanceKeysDN))
+    {
+      return;
+    }
+
+    RDN srcRDN = entry.getDN().getRDN();
+
+    // Only process the entry if it has the expected form of RDN.
+    if (!srcRDN.isMultiValued() &&
+         srcRDN.getAttributeType(0).equals(attrAlias))
+    {
+      DN dstDN = trustStoreRootDN.concat(srcRDN);
+
+      if (!entry.hasAttribute(attrCompromisedTime))
+      {
+        addEntry(entry, dstDN);
+      }
+    }
+  }
+
+  /**
+   * {@inheritDoc}
+   */
+  public void handleDeleteOperation(PostResponseDeleteOperation deleteOperation,
+                                    Entry entry)
+  {
+    if (!deleteOperation.getEntryDN().isDescendantOf(instanceKeysDN))
+    {
+      return;
+    }
+
+    RDN srcRDN = entry.getDN().getRDN();
+
+    // Only process the entry if it has the expected form of RDN.
+    if (!srcRDN.isMultiValued() &&
+         srcRDN.getAttributeType(0).equals(attrAlias))
+    {
+      DN dstDN = trustStoreRootDN.concat(srcRDN);
+
+      deleteEntry(dstDN);
+    }
+  }
+
+  /**
+   * {@inheritDoc}
+   */
+  public void handleModifyOperation(PostResponseModifyOperation modifyOperation,
+                                    Entry oldEntry, Entry newEntry)
+  {
+    if (!modifyOperation.getEntryDN().isDescendantOf(instanceKeysDN))
+    {
+      return;
+    }
+
+    RDN srcRDN = newEntry.getDN().getRDN();
+
+    // Only process the entry if it has the expected form of RDN.
+    if (!srcRDN.isMultiValued() &&
+         srcRDN.getAttributeType(0).equals(attrAlias))
+    {
+      DN dstDN = trustStoreRootDN.concat(srcRDN);
+
+      // Get any existing local trust store entry.
+      Entry dstEntry = null;
+      try
+      {
+        dstEntry = DirectoryServer.getEntry(dstDN);
+      }
+      catch (DirectoryException e)
+      {
+        // ignore
+      }
+
+      if (newEntry.hasAttribute(attrCompromisedTime))
+      {
+        // The key was compromised so we should remove it from the local
+        // trust store.
+        if (dstEntry != null)
+        {
+          deleteEntry(dstDN);
+        }
+      }
+      else
+      {
+        if (dstEntry == null)
+        {
+          addEntry(newEntry, dstDN);
+        }
+        else
+        {
+          modifyEntry(newEntry, dstEntry);
+        }
+      }
+    }
+  }
+
+  /**
+   * {@inheritDoc}
+   */
+  public void handleModifyDNOperation(
+       PostResponseModifyDNOperation modifyDNOperation, Entry oldEntry,
+       Entry newEntry)
+  {
+    // No implementation required.
+  }
+}
diff --git a/opendj-sdk/opends/src/server/org/opends/server/replication/protocol/ReplSessionSecurity.java b/opendj-sdk/opends/src/server/org/opends/server/replication/protocol/ReplSessionSecurity.java
index f77f3a7..7cf2e29 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/replication/protocol/ReplSessionSecurity.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/replication/protocol/ReplSessionSecurity.java
@@ -272,6 +272,11 @@
       // Force TLS negotiation now.
       secureSocket.startHandshake();
 
+//      SSLSession sslSession = secureSocket.getSession();
+//      System.out.println("Peer      = " + sslSession.getPeerHost() + ":" +
+//           sslSession.getPeerPort());
+//      System.out.println("Principal = " + sslSession.getPeerPrincipal());
+
       return new TLSSocketSession(socket, secureSocket);
     }
     else

--
Gitblit v1.10.0