mirror of https://github.com/OpenIdentityPlatform/OpenDJ.git

Jean-Noel Rouvignac
22.46.2014 2e56b3f76709f8f82fabad8a51362c18cbba5fe4
opendj3-server-dev/src/server/org/opends/server/crypto/CryptoManagerSync.java
@@ -26,14 +26,23 @@
 */
package org.opends.server.crypto;
import java.util.ArrayList;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import org.forgerock.i18n.LocalizableMessage;
import org.forgerock.i18n.slf4j.LocalizedLogger;
import org.forgerock.opendj.ldap.DereferenceAliasesPolicy;
import org.forgerock.opendj.ldap.ResultCode;
import org.forgerock.opendj.ldap.SearchScope;
import org.opends.admin.ads.ADSContext;
import org.opends.server.api.Backend;
import org.opends.server.api.BackendInitializationListener;
import org.opends.server.api.ChangeNotificationListener;
import org.opends.server.api.plugin.InternalDirectoryServerPlugin;
import org.opends.server.api.plugin.PluginResult.PostResponse;
import org.opends.server.config.ConfigConstants;
import org.opends.server.controls.EntryChangeNotificationControl;
import org.opends.server.controls.PersistentSearchChangeType;
@@ -43,71 +52,75 @@
import org.opends.server.protocols.internal.InternalClientConnection;
import org.opends.server.protocols.internal.InternalSearchOperation;
import org.opends.server.protocols.ldap.LDAPControl;
import org.opends.server.types.*;
import org.forgerock.opendj.ldap.ResultCode;
import org.opends.server.types.Attribute;
import org.opends.server.types.AttributeType;
import org.opends.server.types.Control;
import org.opends.server.types.CryptoManagerException;
import org.opends.server.types.DN;
import org.opends.server.types.DirectoryException;
import org.opends.server.types.Entry;
import org.opends.server.types.InitializationException;
import org.opends.server.types.ObjectClass;
import org.opends.server.types.RDN;
import org.opends.server.types.SearchFilter;
import org.opends.server.types.SearchResultEntry;
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.messages.CoreMessages.*;
import static org.opends.server.api.plugin.PluginType.*;
import static org.opends.server.config.ConfigConstants.*;
import static org.opends.server.protocols.internal.InternalClientConnection.*;
import static org.opends.server.util.ServerConstants.*;
import static org.opends.server.util.StaticUtils.*;
import java.util.LinkedHashSet;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.HashMap;
/**
 * This class defines an object that synchronizes certificates from the admin
 * data branch into the trust store backend, and synchronizes secret-key entries
 * from the admin data branch to the crypto manager secret-key cache.
 */
public class CryptoManagerSync
     implements BackendInitializationListener, ChangeNotificationListener
public class CryptoManagerSync extends InternalDirectoryServerPlugin
     implements BackendInitializationListener
{
  /**
   * The debug log tracer for this object.
   */
  /** The debug log tracer for this object. */
  private static final LocalizedLogger logger = LocalizedLogger.getLoggerForThisClass();
  // The DN of the administration suffix.
  /** The DN of the administration suffix. */
  private DN adminSuffixDN;
  // The DN of the instance keys container within the admin suffix.
  /** The DN of the instance keys container within the admin suffix. */
  private DN instanceKeysDN;
  // The DN of the secret keys container within the admin suffix.
  /** The DN of the secret keys container within the admin suffix. */
  private DN secretKeysDN;
  // The DN of the trust store root.
  /** 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 is used to specify a server instance certificate. */
  private final AttributeType attrCert;
  // The attribute type that holds a server certificate identifier.
  AttributeType attrAlias;
  /** The attribute type that holds a server certificate identifier. */
  private final AttributeType attrAlias;
  // The attribute type that holds the time a key was compromised.
  AttributeType attrCompromisedTime;
  /** The attribute type that holds the time a key was compromised. */
  private final AttributeType attrCompromisedTime;
  // A filter on object class to select key entries.
  /** A filter on object class to select key entries. */
  private SearchFilter keySearchFilter;
  // The instance key objectclass.
  private ObjectClass ocInstanceKey;
  /** The instance key objectclass. */
  private final ObjectClass ocInstanceKey;
  // The cipher key objectclass.
  private ObjectClass ocCipherKey;
  /** The cipher key objectclass. */
  private final ObjectClass ocCipherKey;
  // The mac key objectclass.
  private ObjectClass ocMacKey;
  /** The mac key objectclass. */
  private final ObjectClass ocMacKey;
  /** Dummy configuration DN. */
  private static final String CONFIG_DN = "cn=Crypto Manager Sync,cn=config";
  /**
   * Creates a new instance of this trust store synchronization thread.
@@ -116,29 +129,17 @@
   * initialization, such as a failure to publish the instance-key-pair
   * public-key-certificate in ADS.
   */
  public CryptoManagerSync()
          throws InitializationException
  public CryptoManagerSync() throws InitializationException
  {
    this(true);
  }
  /**
   * Creates a new instance of this trust store synchronization thread.
   *
   * @param publishInstanceKey whether the instance key must be published in
   * the ADS or not.
   * @throws InitializationException in case an exception occurs during
   * initialization, such as a failure to publish the instance-key-pair
   * public-key-certificate in ADS.
   */
  public CryptoManagerSync(boolean publishInstanceKey)
  throws InitializationException
  {
    super(toDN(CONFIG_DN), EnumSet.of(
        // No implementation required for modify_dn operations
        // FIXME: Technically it is possible to perform a subtree modDN
        // in this case however such subtree modDN would essentially be
        // moving configuration branches which should not happen.
        POST_RESPONSE_ADD, POST_RESPONSE_MODIFY, POST_RESPONSE_DELETE),
        true);
    try {
      if (publishInstanceKey)
      {
        CryptoManagerImpl.publishInstanceKeyEntryInADS();
      }
      CryptoManagerImpl.publishInstanceKeyEntryInADS();
    }
    catch (CryptoManagerException ex) {
      throw new InitializationException(ex.getMessageObject());
@@ -160,15 +161,11 @@
    }
    catch (DirectoryException e)
    {
      //
    }
    ocInstanceKey = DirectoryServer.getObjectClass(
         OC_CRYPTO_INSTANCE_KEY, true);
    ocCipherKey = DirectoryServer.getObjectClass(
         OC_CRYPTO_CIPHER_KEY, true);
    ocMacKey = DirectoryServer.getObjectClass(
         OC_CRYPTO_MAC_KEY, true);
    ocInstanceKey = DirectoryServer.getObjectClass(OC_CRYPTO_INSTANCE_KEY, true);
    ocCipherKey = DirectoryServer.getObjectClass(OC_CRYPTO_CIPHER_KEY, true);
    ocMacKey = DirectoryServer.getObjectClass(OC_CRYPTO_MAC_KEY, true);
    attrCert = DirectoryServer.getAttributeType(
         ConfigConstants.ATTR_CRYPTO_PUBLIC_KEY_CERTIFICATE, true);
@@ -182,22 +179,32 @@
      searchAdminSuffix();
    }
    DirectoryServer.registerChangeNotificationListener(this);
    DirectoryServer.registerInternalPlugin(this);
  }
  private static DN toDN(final String dn) throws InitializationException
  {
    try
    {
      return DN.valueOf(dn);
    }
    catch (DirectoryException e)
    {
      throw new RuntimeException(e);
    }
  }
  private void searchAdminSuffix()
  {
    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(),
         new InternalSearchOperation(getRootConnection(),
                                     nextOperationID(),
                                     nextMessageID(),
                                     controls,
                                     adminSuffixDN, SearchScope.WHOLE_SUBTREE,
                                     DereferenceAliasesPolicy.NEVER,
@@ -231,9 +238,7 @@
  }
  /**
   * {@inheritDoc}
   */
  /** {@inheritDoc} */
  @Override
  public void performBackendInitializationProcessing(Backend backend)
  {
@@ -250,9 +255,7 @@
    }
  }
  /**
   * {@inheritDoc}
   */
  /** {@inheritDoc} */
  @Override
  public void performBackendFinalizationProcessing(Backend backend)
  {
@@ -306,7 +309,7 @@
      {
        for (Control c : controls)
        {
          if (c.getOID().equals(OID_ENTRY_CHANGE_NOTIFICATION))
          if (OID_ENTRY_CHANGE_NOTIFICATION.equals(c.getOID()))
          {
            if (c instanceof LDAPControl)
            {
@@ -331,8 +334,7 @@
      if (ecn != null &&
           ecn.getChangeType() == PersistentSearchChangeType.DELETE)
      {
        // The entry was deleted so we should remove it from the local trust
        // store.
        // entry was deleted so remove it from the local trust store
        if (dstEntry != null)
        {
          deleteEntry(dstDN);
@@ -340,24 +342,21 @@
      }
      else if (searchEntry.hasAttribute(attrCompromisedTime))
      {
        // The key was compromised so we should remove it from the local
        // trust store.
        // key was compromised so remove it from the local trust store
        if (dstEntry != null)
        {
          deleteEntry(dstDN);
        }
      }
      else if (dstEntry == null)
      {
        // The entry was added
        addEntry(searchEntry, dstDN);
      }
      else
      {
        // The entry was added or modified.
        if (dstEntry == null)
        {
          addEntry(searchEntry, dstDN);
        }
        else
        {
          modifyEntry(searchEntry, dstEntry);
        }
        // The entry was modified
        modifyEntry(searchEntry, dstEntry);
      }
    }
  }
@@ -371,11 +370,8 @@
   */
  private void modifyEntry(Entry srcEntry, Entry dstEntry)
  {
    List<Attribute> srcList;
    srcList = srcEntry.getAttribute(attrCert);
    List<Attribute> dstList;
    dstList = dstEntry.getAttribute(attrCert);
    List<Attribute> srcList = srcEntry.getAttribute(attrCert);
    List<Attribute> dstList = dstEntry.getAttribute(attrCert);
    // Check for changes to the certificate value.
    boolean differ = false;
@@ -386,21 +382,12 @@
        differ = true;
      }
    }
    else if (dstList == null)
    else if (dstList == null
        || srcList.size() != dstList.size()
        || !srcList.equals(dstList))
    {
      differ = true;
    }
    else if (srcList.size() != dstList.size())
    {
      differ = true;
    }
    else
    {
      if (!srcList.equals(dstList))
      {
        differ = true;
      }
    }
    if (differ)
    {
@@ -470,19 +457,22 @@
    }
  }
  /**
   * {@inheritDoc}
   */
  /** {@inheritDoc} */
  @Override
  public void handleAddOperation(PostResponseAddOperation addOperation,
                                 Entry entry)
  public PostResponse doPostResponse(PostResponseAddOperation op)
  {
    if (addOperation.getEntryDN().isDescendantOf(instanceKeysDN))
    if (op.getResultCode() != ResultCode.SUCCESS)
    {
      return PostResponse.continueOperationProcessing();
    }
    final Entry entry = op.getEntryToAdd();
    final DN entryDN = op.getEntryDN();
    if (entryDN.isDescendantOf(instanceKeysDN))
    {
      handleInstanceKeyAddOperation(entry);
    }
    else if (addOperation.getEntryDN().isDescendantOf(secretKeysDN))
    else if (entryDN.isDescendantOf(secretKeysDN))
    {
      try
      {
@@ -501,6 +491,7 @@
            "Failed to import key entry: %s", e.getMessage()));
      }
    }
    return PostResponse.continueOperationProcessing();
  }
@@ -521,19 +512,17 @@
    }
  }
  /**
   * {@inheritDoc}
   */
  /** {@inheritDoc} */
  @Override
  public void handleDeleteOperation(PostResponseDeleteOperation deleteOperation,
                                    Entry entry)
  public PostResponse doPostResponse(PostResponseDeleteOperation op)
  {
    if (!deleteOperation.getEntryDN().isDescendantOf(instanceKeysDN))
    if (op.getResultCode() != ResultCode.SUCCESS
        || !op.getEntryDN().isDescendantOf(instanceKeysDN))
    {
      return;
      return PostResponse.continueOperationProcessing();
    }
    RDN srcRDN = entry.getName().rdn();
    RDN srcRDN = op.getEntryToDelete().getName().rdn();
    // Only process the entry if it has the expected form of RDN.
    // FIXME: Technically it is possible to perform a subtree in
@@ -542,24 +531,28 @@
    if (!srcRDN.isMultiValued() &&
         srcRDN.getAttributeType(0).equals(attrAlias))
    {
      DN dstDN = trustStoreRootDN.child(srcRDN);
      deleteEntry(dstDN);
      DN destDN = trustStoreRootDN.child(srcRDN);
      deleteEntry(destDN);
    }
    return PostResponse.continueOperationProcessing();
  }
  /**
   * {@inheritDoc}
   */
  /** {@inheritDoc} */
  @Override
  public void handleModifyOperation(PostResponseModifyOperation modifyOperation,
                                    Entry oldEntry, Entry newEntry)
  public PostResponse doPostResponse(PostResponseModifyOperation op)
  {
    if (modifyOperation.getEntryDN().isDescendantOf(instanceKeysDN))
    if (op.getResultCode() != ResultCode.SUCCESS)
    {
      return PostResponse.continueOperationProcessing();
    }
    final Entry newEntry = op.getModifiedEntry();
    final DN entryDN = op.getEntryDN();
    if (entryDN.isDescendantOf(instanceKeysDN))
    {
      handleInstanceKeyModifyOperation(newEntry);
    }
    else if (modifyOperation.getEntryDN().isDescendantOf(secretKeysDN))
    else if (entryDN.isDescendantOf(secretKeysDN))
    {
      try
      {
@@ -578,6 +571,7 @@
            "Failed to import modified key entry: %s", e.getMessage()));
      }
    }
    return PostResponse.continueOperationProcessing();
  }
  private void handleInstanceKeyModifyOperation(Entry newEntry)
@@ -610,31 +604,14 @@
          deleteEntry(dstDN);
        }
      }
      else if (dstEntry == null)
      {
        addEntry(newEntry, dstDN);
      }
      else
      {
        if (dstEntry == null)
        {
          addEntry(newEntry, dstDN);
        }
        else
        {
          modifyEntry(newEntry, dstEntry);
        }
        modifyEntry(newEntry, dstEntry);
      }
    }
  }
  /**
   * {@inheritDoc}
   */
  @Override
  public void handleModifyDNOperation(
       PostResponseModifyDNOperation modifyDNOperation, Entry oldEntry,
       Entry newEntry)
  {
    // No implementation required.
    // FIXME: Technically it is possible to perform a subtree modDN
    // in this case however such subtree modDN would essentially be
    // moving configuration branches which should not happen.
  }
}