From 73b1d8214bd973d9ea1eb2def1ea1121b53ea5da Mon Sep 17 00:00:00 2001
From: coulbeck <coulbeck@localhost>
Date: Tue, 28 Aug 2007 16:04:25 +0000
Subject: [PATCH] Files missing from previous commit
---
opends/src/server/org/opends/server/backends/TrustStoreBackend.java | 1619 ++++++++++++++++++++++++++++++++++++++++++++++++++
opends/src/admin/defn/org/opends/server/admin/std/TrustStoreBackendConfiguration.xml | 115 +++
opends/src/admin/defn/org/opends/server/admin/std/CryptoManagerConfiguration.xml | 145 ++++
3 files changed, 1,879 insertions(+), 0 deletions(-)
diff --git a/opends/src/admin/defn/org/opends/server/admin/std/CryptoManagerConfiguration.xml b/opends/src/admin/defn/org/opends/server/admin/std/CryptoManagerConfiguration.xml
new file mode 100644
index 0000000..41a6af2
--- /dev/null
+++ b/opends/src/admin/defn/org/opends/server/admin/std/CryptoManagerConfiguration.xml
@@ -0,0 +1,145 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ! 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.
+ ! -->
+
+<adm:managed-object name="crypto-manager"
+ plural-name="crypto-managers"
+ package="org.opends.server.admin.std"
+ xmlns:adm="http://www.opends.org/admin"
+ xmlns:ldap="http://www.opends.org/admin-ldap">
+ <adm:synopsis>
+ The
+ <adm:user-friendly-name />
+ provides hashing, encryption and other kinds of cryptographic operations.
+ It also contains methods for compressing and uncompressing data.
+ </adm:synopsis>
+ <adm:tag name="security"/>
+ <adm:profile name="ldap">
+ <ldap:object-class>
+ <ldap:oid>ds-cfg-crypto-manager-oid</ldap:oid>
+ <ldap:name>ds-cfg-crypto-manager</ldap:name>
+ <ldap:superior>top</ldap:superior>
+ </ldap:object-class>
+ </adm:profile>
+ <adm:property name="ssl-protocols" multi-valued="true">
+ <adm:synopsis>
+ Specifies the names of the SSL protocols that will be allowed for
+ use in SSL or TLS communication.
+ </adm:synopsis>
+ <adm:requires-admin-action>
+ <adm:none>
+ <adm:synopsis>
+ Changes to this property will take effect immediately but will
+ only impact new SSL/TLS-based sessions created after the
+ change.
+ </adm:synopsis>
+ </adm:none>
+ </adm:requires-admin-action>
+ <adm:default-behavior>
+ <adm:alias>
+ <adm:synopsis>
+ Uses the default set of SSL protocols provided by the server's
+ JVM.
+ </adm:synopsis>
+ </adm:alias>
+ </adm:default-behavior>
+ <adm:syntax>
+ <adm:string />
+ </adm:syntax>
+ <adm:profile name="ldap">
+ <ldap:attribute>
+ <ldap:oid>1.3.6.1.4.1.26027.1.1.461</ldap:oid>
+ <ldap:name>ds-cfg-ssl-protocol</ldap:name>
+ </ldap:attribute>
+ </adm:profile>
+ </adm:property>
+ <adm:property name="ssl-cipher-suites" multi-valued="true">
+ <adm:synopsis>
+ Specifies the names of the SSL cipher suites that will be allowed
+ for use in SSL or TLS communication.
+ </adm:synopsis>
+ <adm:requires-admin-action>
+ <adm:none>
+ <adm:synopsis>
+ Changes to this property will take effect immediately but will
+ only impact new SSL/TLS-based sessions created after the
+ change.
+ </adm:synopsis>
+ </adm:none>
+ </adm:requires-admin-action>
+ <adm:default-behavior>
+ <adm:alias>
+ <adm:synopsis>
+ Uses the default set of SSL cipher suites provided by the
+ server's JVM.
+ </adm:synopsis>
+ </adm:alias>
+ </adm:default-behavior>
+ <adm:syntax>
+ <adm:string />
+ </adm:syntax>
+ <adm:profile name="ldap">
+ <ldap:attribute>
+ <ldap:oid>1.3.6.1.4.1.26027.1.1.462</ldap:oid>
+ <ldap:name>ds-cfg-ssl-cipher-suite</ldap:name>
+ </ldap:attribute>
+ </adm:profile>
+ </adm:property>
+ <adm:property name="ssl-encryption" multi-valued="false">
+ <adm:synopsis>
+ Specifies whether SSL/TLS is used to provide encrypted communication
+ between two OpenDS server components.
+ </adm:synopsis>
+ <adm:requires-admin-action>
+ <adm:none>
+ <adm:synopsis>
+ Changes to this property will take effect immediately but will
+ only impact new SSL/TLS-based sessions created after the
+ change.
+ </adm:synopsis>
+ </adm:none>
+ </adm:requires-admin-action>
+ <adm:default-behavior>
+ <adm:defined>
+ <adm:value>
+ false
+ </adm:value>
+ </adm:defined>
+ </adm:default-behavior>
+ <adm:syntax>
+ <adm:boolean />
+ </adm:syntax>
+ <adm:profile name="ldap">
+ <ldap:attribute>
+ <ldap:oid>ds-cfg-ssl-encryption-oid</ldap:oid>
+ <ldap:name>ds-cfg-ssl-encryption</ldap:name>
+ </ldap:attribute>
+ </adm:profile>
+ </adm:property>
+ <adm:property-reference name="ssl-cert-nickname" />
+</adm:managed-object>
+
diff --git a/opends/src/admin/defn/org/opends/server/admin/std/TrustStoreBackendConfiguration.xml b/opends/src/admin/defn/org/opends/server/admin/std/TrustStoreBackendConfiguration.xml
new file mode 100644
index 0000000..e436def
--- /dev/null
+++ b/opends/src/admin/defn/org/opends/server/admin/std/TrustStoreBackendConfiguration.xml
@@ -0,0 +1,115 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ! 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.
+ ! -->
+
+<adm:managed-object name="trust-store-backend"
+ plural-name="trust-store-backends"
+ extends="backend"
+ package="org.opends.server.admin.std"
+ xmlns:adm="http://www.opends.org/admin"
+ xmlns:ldap="http://www.opends.org/admin-ldap">
+ <adm:synopsis>
+ The
+ <adm:user-friendly-name />
+ provides an LDAP view of a file-based trust store. It is used by the
+ administrative cryptographic framework.
+ </adm:synopsis>
+ <adm:profile name="ldap">
+ <ldap:object-class>
+ <ldap:oid>ds-cfg-trust-store-backend-oid</ldap:oid>
+ <ldap:name>ds-cfg-trust-store-backend</ldap:name>
+ <ldap:superior>ds-cfg-backend</ldap:superior>
+ </ldap:object-class>
+ </adm:profile>
+ <adm:property-override name="backend-class">
+ <adm:default-behavior>
+ <adm:defined>
+ <adm:value>
+ org.opends.server.backends.TrustStoreBackend
+ </adm:value>
+ </adm:defined>
+ </adm:default-behavior>
+ </adm:property-override>
+ <adm:property name="trust-store-file" mandatory="true">
+ <adm:TODO>Should use a file-based property definition?</adm:TODO>
+ <adm:synopsis>
+ Specifies the path to the file that stores the trust
+ information. It may be an absolute path, or a path that is
+ relative to the
+ <adm:product-name />
+ instance root.
+ </adm:synopsis>
+ <adm:default-behavior>
+ <adm:defined>
+ <adm:value>
+ config/ads-truststore
+ </adm:value>
+ </adm:defined>
+ </adm:default-behavior>
+ <adm:syntax>
+ <adm:string />
+ </adm:syntax>
+ <adm:profile name="ldap">
+ <ldap:attribute>
+ <ldap:oid>1.3.6.1.4.1.26027.1.1.129</ldap:oid>
+ <ldap:name>ds-cfg-trust-store-file</ldap:name>
+ </ldap:attribute>
+ </adm:profile>
+ </adm:property>
+ <adm:property name="trust-store-type">
+ <adm:TODO>
+ Can we restrict this to an enumeration? How can the client guess
+ which values are possible? What is the default value?
+ </adm:TODO>
+ <adm:synopsis>
+ Specifies the format for the data in the key store file.
+ </adm:synopsis>
+ <adm:description>
+ Valid values should always include 'JKS' and 'PKCS12', but
+ different implementations may allow other values as well. If no
+ value is provided, then the JVM-default value will be used.
+ Changes to this configuration attribute will take effect the next
+ time that the key manager is accessed.
+ </adm:description>
+ <adm:default-behavior>
+ <adm:undefined />
+ </adm:default-behavior>
+ <adm:syntax>
+ <adm:string />
+ </adm:syntax>
+ <adm:profile name="ldap">
+ <ldap:attribute>
+ <ldap:oid>1.3.6.1.4.1.26027.1.1.134</ldap:oid>
+ <ldap:name>ds-cfg-trust-store-type</ldap:name>
+ </ldap:attribute>
+ </adm:profile>
+ </adm:property>
+ <adm:property-reference name="trust-store-pin" />
+ <adm:property-reference name="trust-store-pin-property" />
+ <adm:property-reference name="trust-store-pin-environment-variable" />
+ <adm:property-reference name="trust-store-pin-file" />
+</adm:managed-object>
diff --git a/opends/src/server/org/opends/server/backends/TrustStoreBackend.java b/opends/src/server/org/opends/server/backends/TrustStoreBackend.java
new file mode 100644
index 0000000..14ec3cb
--- /dev/null
+++ b/opends/src/server/org/opends/server/backends/TrustStoreBackend.java
@@ -0,0 +1,1619 @@
+/*
+ * 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 2006-2007 Sun Microsystems, Inc.
+ */
+package org.opends.server.backends;
+
+
+
+import java.io.File;
+import java.io.BufferedReader;
+import java.io.FileReader;
+import java.io.IOException;
+import java.io.FileInputStream;
+import java.io.FileWriter;
+import java.io.PrintWriter;
+import java.io.FileOutputStream;
+import java.util.*;
+import java.security.KeyStore;
+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;
+import org.opends.server.core.DirectoryServer;
+import org.opends.server.core.ModifyOperation;
+import org.opends.server.core.ModifyDNOperation;
+import org.opends.server.core.SearchOperation;
+import org.opends.server.protocols.asn1.ASN1OctetString;
+
+import static org.opends.server.config.ConfigConstants.*;
+import static org.opends.server.loggers.debug.DebugLogger.*;
+import org.opends.server.loggers.debug.DebugTracer;
+import org.opends.server.loggers.ErrorLogger;
+import org.opends.server.types.*;
+import static org.opends.server.util.ServerConstants.*;
+import static org.opends.server.util.StaticUtils.*;
+import org.opends.server.util.Validator;
+import org.opends.server.util.CertificateManager;
+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.naming.ldap.Rdn;
+import java.security.cert.Certificate;
+import java.net.UnknownHostException;
+
+
+/**
+ * This class defines a backend used to provide an LDAP view of public keys
+ * stored in a key store.
+ */
+public class TrustStoreBackend
+ extends Backend
+ implements ConfigurationChangeListener<TrustStoreBackendCfg>
+{
+ /**
+ * The tracer object for the debug logger.
+ */
+ private static final DebugTracer TRACER = getTracer();
+
+ // The current configuration state.
+ private TrustStoreBackendCfg configuration;
+
+ // The DN for the base entry.
+ private DN baseDN;
+
+ // The set of base DNs for this backend.
+ private DN[] baseDNs;
+
+ // The base entry.
+ private Entry baseEntry;
+
+ // The set of supported controls for this backend.
+ private HashSet<String> supportedControls;
+
+ // The set of supported features for this backend.
+ private HashSet<String> supportedFeatures;
+
+ // The PIN needed to access the trust store backing file.
+ private char[] trustStorePIN;
+
+ // The path to the trust store backing file.
+ private String trustStoreFile;
+
+ // The type of trust store backing file to use.
+ private String trustStoreType;
+
+ // The certificate manager for the trust store.
+ private CertificateManager certificateManager;
+
+
+ /**
+ * Creates a new backend. All backend
+ * implementations must implement a default constructor that use
+ * <CODE>super()</CODE> to invoke this constructor.
+ */
+ public TrustStoreBackend()
+ {
+ super();
+
+ // Perform all initialization in initializeBackend.
+ }
+
+
+ /**
+ * {@inheritDoc}
+ */
+ public void configureBackend(Configuration config) throws ConfigException
+ {
+ Validator.ensureNotNull(config);
+ Validator.ensureTrue(config instanceof TrustStoreBackendCfg);
+
+ configuration = (TrustStoreBackendCfg)config;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void initializeBackend()
+ throws ConfigException, InitializationException
+ {
+ // Create the set of base DNs that we will handle. In this case, it's just
+ // the DN of the base trust store entry.
+ try
+ {
+ // FIXME -- Deal with this more correctly.
+ baseDN = DN.decode(DN_TRUST_STORE_ROOT);
+ this.baseDNs = new DN[] {baseDN};
+ }
+ catch (Exception e)
+ {
+ if (debugEnabled())
+ {
+ TRACER.debugCaught(DebugLogLevel.ERROR, e);
+ }
+
+ Message message =
+ ERR_TRUSTSTORE_CANNOT_DECODE_TRUSTSTORE_ROOT_DN.get(
+ getExceptionMessage(e));
+ throw new InitializationException(message, e);
+ }
+
+
+ DN configEntryDN = configuration.dn();
+
+ // Get the path to the trust store file.
+ trustStoreFile = configuration.getTrustStoreFile();
+
+
+ // Get the trust store type. If none is specified, then use the default
+ // type.
+ trustStoreType = configuration.getTrustStoreType();
+ if (trustStoreType == null)
+ {
+ trustStoreType = KeyStore.getDefaultType();
+ }
+
+ try
+ {
+ KeyStore.getInstance(trustStoreType);
+ }
+ catch (KeyStoreException kse)
+ {
+ if (debugEnabled())
+ {
+ TRACER.debugCaught(DebugLogLevel.ERROR, kse);
+ }
+
+ Message message = ERR_TRUSTSTORE_INVALID_TYPE.
+ get(String.valueOf(trustStoreType), String.valueOf(configEntryDN),
+ getExceptionMessage(kse));
+ throw new InitializationException(message);
+ }
+
+
+ // Get the PIN needed to access the contents of the trust store file. We
+ // will offer several places to look for the PIN, and we will do so in the
+ // following order:
+ // - In a specified Java property
+ // - In a specified environment variable
+ // - In a specified file on the server filesystem.
+ // - As the value of a configuration attribute.
+ // In any case, the PIN must be in the clear. If no PIN is provided, then
+ // it will be assumed that none is required to access the information in the
+ // trust store.
+ String pinProperty = configuration.getTrustStorePinProperty();
+ if (pinProperty == null)
+ {
+ String pinEnVar = configuration.getTrustStorePinEnvironmentVariable();
+ if (pinEnVar == null)
+ {
+ String pinFilePath = configuration.getTrustStorePinFile();
+ if (pinFilePath == null)
+ {
+ String pinStr = configuration.getTrustStorePin();
+ if (pinStr == null)
+ {
+ trustStorePIN = null;
+ }
+ else
+ {
+ trustStorePIN = pinStr.toCharArray();
+ }
+ }
+ else
+ {
+ File pinFile = getFileForPath(pinFilePath);
+ if (! pinFile.exists())
+ {
+ try
+ {
+ // Generate a PIN.
+ trustStorePIN = createKeystorePassword();
+
+ // Store the PIN in the pin file.
+ createPINFile(pinFile.getPath(), new String(trustStorePIN));
+ }
+ catch (Exception e)
+ {
+ Message message = ERR_TRUSTSTORE_PIN_NO_SUCH_FILE.get(
+ String.valueOf(pinFilePath), String.valueOf(configEntryDN));
+ throw new InitializationException(message);
+ }
+ }
+ else
+ {
+ String pinStr;
+
+ BufferedReader br = null;
+ try
+ {
+ br = new BufferedReader(new FileReader(pinFile));
+ pinStr = br.readLine();
+ }
+ catch (IOException ioe)
+ {
+ Message message = ERR_TRUSTSTORE_PIN_FILE_CANNOT_READ.
+ get(String.valueOf(pinFilePath),
+ String.valueOf(configEntryDN), getExceptionMessage(ioe));
+ throw new InitializationException(message, ioe);
+ }
+ finally
+ {
+ try
+ {
+ br.close();
+ } catch (Exception e) {
+ // ignore
+ }
+ }
+
+ if (pinStr == null)
+ {
+ Message message = ERR_TRUSTSTORE_PIN_FILE_EMPTY.get(
+ String.valueOf(pinFilePath), String.valueOf(configEntryDN));
+ throw new InitializationException(message);
+ }
+ else
+ {
+ trustStorePIN = pinStr.toCharArray();
+ }
+ }
+ }
+ }
+ else
+ {
+ String pinStr = System.getenv(pinEnVar);
+ if (pinStr == null)
+ {
+ Message message = ERR_TRUSTSTORE_PIN_ENVAR_NOT_SET.get(
+ String.valueOf(pinProperty), String.valueOf(configEntryDN));
+ throw new InitializationException(message);
+ }
+ else
+ {
+ trustStorePIN = pinStr.toCharArray();
+ }
+ }
+ }
+ else
+ {
+ String pinStr = System.getProperty(pinProperty);
+ if (pinStr == null)
+ {
+ Message message = ERR_TRUSTSTORE_PIN_PROPERTY_NOT_SET.get(
+ String.valueOf(pinProperty), String.valueOf(configEntryDN));
+ throw new InitializationException(message);
+ }
+ else
+ {
+ trustStorePIN = pinStr.toCharArray();
+ }
+ }
+
+ // Create a certificate manager.
+ certificateManager =
+ new CertificateManager(getFileForPath(trustStoreFile).getPath(),
+ trustStoreType,
+ new String(trustStorePIN));
+
+ // Construct the trust store base entry.
+ LinkedHashMap<ObjectClass,String> objectClasses =
+ new LinkedHashMap<ObjectClass,String>(2);
+ objectClasses.put(DirectoryServer.getTopObjectClass(), OC_TOP);
+
+ ObjectClass branchOC =
+ DirectoryServer.getObjectClass("ds-cfg-branch", true);
+ objectClasses.put(branchOC, "ds-cfg-branch");
+
+ LinkedHashMap<AttributeType,List<Attribute>> opAttrs =
+ new LinkedHashMap<AttributeType,List<Attribute>>(0);
+ LinkedHashMap<AttributeType,List<Attribute>> userAttrs =
+ new LinkedHashMap<AttributeType,List<Attribute>>(1);
+
+ RDN rdn = baseDN.getRDN();
+ int numAVAs = rdn.getNumValues();
+ for (int i=0; i < numAVAs; i++)
+ {
+ LinkedHashSet<AttributeValue> valueSet =
+ new LinkedHashSet<AttributeValue>(1);
+ valueSet.add(rdn.getAttributeValue(i));
+
+ AttributeType attrType = rdn.getAttributeType(i);
+ ArrayList<Attribute> attrList = new ArrayList<Attribute>(1);
+ attrList.add(new Attribute(attrType, attrType.getNameOrOID(),
+ valueSet));
+
+ userAttrs.put(attrType, attrList);
+ }
+
+ baseEntry = new Entry(baseDN, objectClasses, userAttrs,
+ opAttrs);
+
+
+ // Define an empty sets for the supported controls and features.
+ supportedControls = new HashSet<String>(0);
+ supportedFeatures = new HashSet<String>(0);
+
+
+ // Register this as a change listener.
+ configuration.addTrustStoreChangeListener(this);
+
+
+ // Register the trust store base as a private suffix.
+ try
+ {
+ DirectoryServer.registerBaseDN(baseDN, this, true, false);
+ }
+ catch (Exception e)
+ {
+ if (debugEnabled())
+ {
+ TRACER.debugCaught(DebugLogLevel.ERROR, e);
+ }
+
+ Message message = ERR_BACKEND_CANNOT_REGISTER_BASEDN.get(
+ String.valueOf(baseDN), String.valueOf(e));
+ throw new InitializationException(message, e);
+ }
+ }
+
+
+
+ /**
+ * {@inheritDoc}
+ */
+ public void finalizeBackend()
+ {
+ configuration.addTrustStoreChangeListener(this);
+
+ try
+ {
+ DirectoryServer.deregisterBaseDN(baseDN, false);
+ }
+ catch (Exception e)
+ {
+ if (debugEnabled())
+ {
+ TRACER.debugCaught(DebugLogLevel.ERROR, e);
+ }
+ }
+ }
+
+
+
+ /**
+ * {@inheritDoc}
+ */
+ public DN[] getBaseDNs()
+ {
+ return baseDNs;
+ }
+
+
+
+ /**
+ * {@inheritDoc}
+ */
+ public long getEntryCount()
+ {
+ int numEntries = 1;
+
+ try
+ {
+ String[] aliases = certificateManager.getCertificateAliases();
+ if (aliases != null)
+ {
+ numEntries += aliases.length;
+ }
+ }
+ catch (KeyStoreException e)
+ {
+ if (debugEnabled())
+ {
+ TRACER.debugCaught(DebugLogLevel.ERROR, e);
+ }
+ }
+
+ return numEntries;
+ }
+
+
+
+ /**
+ * {@inheritDoc}
+ */
+ public boolean isLocal()
+ {
+ // For the purposes of this method, this is a local backend.
+ return true;
+ }
+
+
+
+ /**
+ * {@inheritDoc}
+ */
+ public Entry getEntry(DN entryDN)
+ throws DirectoryException
+ {
+ // If the requested entry was null, then throw an exception.
+ if (entryDN == null)
+ {
+ Message message = ERR_TRUSTSTORE_GET_ENTRY_NULL.get();
+ throw new DirectoryException(DirectoryServer.getServerErrorResultCode(),
+ message);
+ }
+
+
+ // If the requested entry was the backend base entry, then retrieve it.
+ if (entryDN.equals(baseDN))
+ {
+ return baseEntry.duplicate(true);
+ }
+
+
+ // See if the requested entry was one level below the backend base entry.
+ // If so, then it must point to a trust store entry.
+ DN parentDN = entryDN.getParentDNInSuffix();
+ if (parentDN == null)
+ {
+ return null;
+ }
+ else if (parentDN.equals(baseDN))
+ {
+ try
+ {
+ return getCertEntry(entryDN);
+ }
+ catch (DirectoryException e)
+ {
+ return null;
+ }
+ }
+ else
+ {
+ return null;
+ }
+ }
+
+
+
+ /**
+ * Generates an entry for a certificate based on the provided DN. The
+ * DN must contain an RDN component that specifies the alias of the
+ * certificate, and that certificate alias must exist in the key store.
+ *
+ * @param entryDN The DN of the certificate to retrieve.
+ *
+ * @return The requested certificate entry.
+ *
+ * @throws DirectoryException If the specified alias does not exist, or if
+ * the DN does not specify any alias.
+ */
+ private Entry getCertEntry(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);
+ }
+
+
+ ByteString certValue;
+ try
+ {
+ Certificate cert = certificateManager.getCertificate(v.getStringValue());
+ certValue = new ASN1OctetString(cert.getEncoded());
+ }
+ catch (Exception e)
+ {
+ if (debugEnabled())
+ {
+ TRACER.debugCaught(DebugLogLevel.ERROR, e);
+ }
+
+ Message message = ERR_TRUSTSTORE_INVALID_CERTIFICATE.get(
+ String.valueOf(entryDN), 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);
+ ocMap.put(DirectoryServer.getTopObjectClass(), OC_TOP);
+
+ ObjectClass objectClass =
+ DirectoryServer.getObjectClass(OC_INSTANCE_KEY, true);
+ ocMap.put(objectClass, OC_INSTANCE_KEY);
+
+ LinkedHashMap<AttributeType,List<Attribute>> opAttrs =
+ new LinkedHashMap<AttributeType,List<Attribute>>(0);
+ LinkedHashMap<AttributeType,List<Attribute>> userAttrs =
+ new LinkedHashMap<AttributeType,List<Attribute>>(3);
+
+ LinkedHashSet<AttributeValue> valueSet =
+ new LinkedHashSet<AttributeValue>(1);
+ valueSet.add(v);
+
+ ArrayList<Attribute> attrList = new ArrayList<Attribute>(1);
+ attrList.add(new Attribute(t, t.getNameOrOID(), valueSet));
+ userAttrs.put(t, attrList);
+
+
+ t = DirectoryServer.getAttributeType(ATTR_ADS_CERTIFICATE, true);
+ valueSet = new LinkedHashSet<AttributeValue>(1);
+ valueSet.add(new AttributeValue(t,
+ certValue));
+ attrList = new ArrayList<Attribute>(1);
+ LinkedHashSet<String> options = new LinkedHashSet<String>(1);
+ options.add("binary");
+ attrList.add(new Attribute(t, t.getNameOrOID(), options, valueSet));
+ userAttrs.put(t, attrList);
+
+
+ Entry e = new Entry(entryDN, ocMap, userAttrs, opAttrs);
+ e.processVirtualAttributes();
+ return e;
+ }
+
+
+
+ /**
+ * {@inheritDoc}
+ */
+ public void addEntry(Entry entry, AddOperation addOperation)
+ throws DirectoryException
+ {
+ DN entryDN = entry.getDN();
+
+ if (entryDN.equals(baseDN))
+ {
+ Message message = ERR_TRUSTSTORE_INVALID_BASE.get(
+ String.valueOf(entryDN));
+ throw new DirectoryException(ResultCode.ENTRY_ALREADY_EXISTS, message);
+ }
+
+ DN parentDN = entryDN.getParentDNInSuffix();
+ if (parentDN == null)
+ {
+ Message message = ERR_TRUSTSTORE_INVALID_BASE.get(
+ String.valueOf(entryDN));
+ throw new DirectoryException(ResultCode.NO_SUCH_OBJECT, message);
+ }
+
+ if (parentDN.equals(baseDN))
+ {
+ addCertificate(entry);
+ }
+ else
+ {
+ Message message = ERR_TRUSTSTORE_INVALID_BASE.get(
+ String.valueOf(entryDN));
+ throw new DirectoryException(ResultCode.NO_SUCH_OBJECT, message);
+ }
+
+ }
+
+
+
+ /**
+ * {@inheritDoc}
+ */
+ 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);
+ }
+
+
+
+ /**
+ * {@inheritDoc}
+ */
+ public void replaceEntry(Entry entry, ModifyOperation modifyOperation)
+ throws DirectoryException
+ {
+ Message message = ERR_TRUSTSTORE_MODIFY_NOT_SUPPORTED.get();
+ throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM, message);
+ }
+
+
+
+ /**
+ * {@inheritDoc}
+ */
+ public void renameEntry(DN currentDN, Entry entry,
+ ModifyDNOperation modifyDNOperation)
+ throws DirectoryException
+ {
+ Message message = ERR_TRUSTSTORE_MODIFY_DN_NOT_SUPPORTED.get();
+ throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM, message);
+ }
+
+
+
+ /**
+ * {@inheritDoc}
+ */
+ public void search(SearchOperation searchOperation)
+ throws DirectoryException
+ {
+ // Get the base entry for the search, if possible. If it doesn't exist,
+ // then this will throw an exception.
+ DN baseDN = searchOperation.getBaseDN();
+ Entry baseEntry = getEntry(baseDN);
+
+
+ // Look at the base DN and see if it's the trust store base DN, or a
+ // trust store entry DN.
+ SearchScope scope = searchOperation.getScope();
+ SearchFilter filter = searchOperation.getFilter();
+ if (this.baseDN.equals(baseDN))
+ {
+ if ((scope == SearchScope.BASE_OBJECT) ||
+ (scope == SearchScope.WHOLE_SUBTREE))
+ {
+ if (filter.matchesEntry(baseEntry))
+ {
+ searchOperation.returnEntry(baseEntry, null);
+ }
+ }
+
+ String[] aliases = null;
+ try
+ {
+ aliases = certificateManager.getCertificateAliases();
+ }
+ catch (KeyStoreException e)
+ {
+ if (debugEnabled())
+ {
+ TRACER.debugCaught(DebugLogLevel.ERROR, e);
+ }
+ }
+
+ if (aliases == null)
+ {
+ aliases = new String[0];
+ }
+
+ if ((scope != SearchScope.BASE_OBJECT) && (! (aliases.length == 0) ))
+ {
+ AttributeType certAliasType =
+ DirectoryServer.getAttributeType(ATTR_CERT_ALIAS, true);
+ for (String alias : aliases)
+ {
+ DN certDN = makeChildDN(this.baseDN, certAliasType,
+ alias);
+
+ Entry certEntry;
+ try
+ {
+ certEntry = getCertEntry(certDN);
+ }
+ catch (Exception e)
+ {
+ if (debugEnabled())
+ {
+ TRACER.debugCaught(DebugLogLevel.ERROR, e);
+ }
+
+ continue;
+ }
+
+ if (filter.matchesEntry(certEntry))
+ {
+ searchOperation.returnEntry(certEntry, null);
+ }
+
+ }
+ }
+ }
+ else if (this.baseDN.equals(baseDN.getParentDNInSuffix()))
+ {
+ Entry certEntry = getCertEntry(baseDN);
+
+ if ((scope == SearchScope.BASE_OBJECT) ||
+ (scope == SearchScope.WHOLE_SUBTREE))
+ {
+ if (filter.matchesEntry(certEntry))
+ {
+ searchOperation.returnEntry(certEntry, null);
+ }
+ }
+ }
+ else
+ {
+ Message message = ERR_TRUSTSTORE_INVALID_BASE.get(String.valueOf(baseDN));
+ throw new DirectoryException(ResultCode.NO_SUCH_OBJECT, message);
+ }
+ }
+
+
+
+ /**
+ * {@inheritDoc}
+ */
+ public HashSet<String> getSupportedControls()
+ {
+ return supportedControls;
+ }
+
+
+
+ /**
+ * {@inheritDoc}
+ */
+ public HashSet<String> getSupportedFeatures()
+ {
+ return supportedFeatures;
+ }
+
+
+
+ /**
+ * {@inheritDoc}
+ */
+ public boolean supportsLDIFExport()
+ {
+ // We do not support LDIF exports.
+ return false;
+ }
+
+
+
+ /**
+ * {@inheritDoc}
+ */
+ public void exportLDIF(LDIFExportConfig exportConfig)
+ throws DirectoryException
+ {
+ Message message = ERR_TRUSTSTORE_IMPORT_AND_EXPORT_NOT_SUPPORTED.get();
+ throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM, message);
+ }
+
+
+
+ /**
+ * {@inheritDoc}
+ */
+ public boolean supportsLDIFImport()
+ {
+ // This backend does not support LDIF imports.
+ return false;
+ }
+
+
+
+ /**
+ * {@inheritDoc}
+ */
+ public LDIFImportResult importLDIF(LDIFImportConfig importConfig)
+ throws DirectoryException
+ {
+ // This backend does not support LDIF imports.
+ Message message = ERR_TRUSTSTORE_IMPORT_AND_EXPORT_NOT_SUPPORTED.get();
+ throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM, message);
+ }
+
+
+
+ /**
+ * {@inheritDoc}
+ */
+ public boolean supportsBackup()
+ {
+ // This backend does not provide a backup/restore mechanism.
+ return false;
+ }
+
+
+
+ /**
+ * {@inheritDoc}
+ */
+ public boolean supportsBackup(BackupConfig backupConfig,
+ StringBuilder unsupportedReason)
+ {
+ // This backend does not provide a backup/restore mechanism.
+ return false;
+ }
+
+
+
+ /**
+ * {@inheritDoc}
+ */
+ public void createBackup(BackupConfig backupConfig)
+ throws DirectoryException
+ {
+ // This backend does not provide a backup/restore mechanism.
+ Message message = ERR_TRUSTSTORE_BACKUP_AND_RESTORE_NOT_SUPPORTED.get();
+ throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM, message);
+ }
+
+
+
+ /**
+ * {@inheritDoc}
+ */
+ public void removeBackup(BackupDirectory backupDirectory,
+ String backupID)
+ throws DirectoryException
+ {
+ // This backend does not provide a backup/restore mechanism.
+ Message message = ERR_TRUSTSTORE_BACKUP_AND_RESTORE_NOT_SUPPORTED.get();
+ throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM, message);
+ }
+
+
+
+ /**
+ * {@inheritDoc}
+ */
+ public boolean supportsRestore()
+ {
+ // This backend does not provide a backup/restore mechanism.
+ return false;
+ }
+
+
+
+ /**
+ * {@inheritDoc}
+ */
+ public void restoreBackup(RestoreConfig restoreConfig)
+ throws DirectoryException
+ {
+ // This backend does not provide a backup/restore mechanism.
+ Message message = ERR_TRUSTSTORE_BACKUP_AND_RESTORE_NOT_SUPPORTED.get();
+ throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM, message);
+ }
+
+
+ /**
+ * {@inheritDoc}
+ */
+ public ConditionResult hasSubordinates(DN entryDN) throws DirectoryException
+ {
+ return ConditionResult.UNDEFINED;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public long numSubordinates(DN entryDN) throws DirectoryException
+ {
+ return -1;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public boolean isConfigurationChangeAcceptable(
+ TrustStoreBackendCfg configuration, List<Message> unacceptableReasons)
+ {
+ boolean configAcceptable = true;
+ DN cfgEntryDN = configuration.dn();
+
+
+ // Get the path to the trust store file.
+ String newTrustStoreFile = configuration.getTrustStoreFile();
+ try
+ {
+ File f = getFileForPath(newTrustStoreFile);
+ if (!(f.exists() && f.isFile()))
+ {
+ unacceptableReasons.add(ERR_TRUSTSTORE_NO_SUCH_FILE.get(
+ String.valueOf(newTrustStoreFile),
+ String.valueOf(cfgEntryDN)));
+ configAcceptable = false;
+ }
+ }
+ catch (Exception e)
+ {
+ if (debugEnabled())
+ {
+ TRACER.debugCaught(DebugLogLevel.ERROR, e);
+ }
+
+ unacceptableReasons.add(ERR_TRUSTSTORE_CANNOT_DETERMINE_FILE.get(
+ String.valueOf(cfgEntryDN),
+ getExceptionMessage(e)));
+ configAcceptable = false;
+ }
+
+
+ // Check to see if the trust store type is acceptable.
+ String storeType = configuration.getTrustStoreType();
+ if (storeType != null)
+ {
+ try
+ {
+ KeyStore.getInstance(storeType);
+ }
+ catch (KeyStoreException kse)
+ {
+ if (debugEnabled())
+ {
+ TRACER.debugCaught(DebugLogLevel.ERROR, kse);
+ }
+
+ Message message = ERR_TRUSTSTORE_INVALID_TYPE.get(
+ String.valueOf(storeType),
+ String.valueOf(cfgEntryDN),
+ getExceptionMessage(kse));
+ unacceptableReasons.add(message);
+ configAcceptable = false;
+ }
+ }
+
+
+ // If there is a PIN property, then make sure the corresponding
+ // property is set.
+ String pinProp = configuration.getTrustStorePinProperty();
+ if (pinProp != null)
+ {
+ if (System.getProperty(pinProp) == null)
+ {
+ Message message = ERR_TRUSTSTORE_PIN_PROPERTY_NOT_SET.get(
+ String.valueOf(pinProp),
+ String.valueOf(cfgEntryDN));
+ unacceptableReasons.add(message);
+ configAcceptable = false;
+ }
+ }
+
+
+ // If there is a PIN environment variable, then make sure the corresponding
+ // environment variable is set.
+ String pinEnVar = configuration.getTrustStorePinEnvironmentVariable();
+ if (pinEnVar != null)
+ {
+ if (System.getenv(pinEnVar) == null)
+ {
+ Message message = ERR_TRUSTSTORE_PIN_ENVAR_NOT_SET.get(
+ String.valueOf(pinEnVar),
+ String.valueOf(cfgEntryDN));
+ unacceptableReasons.add(message);
+ configAcceptable = false;
+ }
+ }
+
+
+ // If there is a PIN file, then make sure the file is readable if it exists.
+ String pinFile = configuration.getTrustStorePinFile();
+ if (pinFile != null)
+ {
+ File f = new File(pinFile);
+ if (f.exists())
+ {
+ String pinStr = null;
+
+ BufferedReader br = null;
+ try
+ {
+ br = new BufferedReader(new FileReader(pinFile));
+ pinStr = br.readLine();
+ }
+ catch (IOException ioe)
+ {
+ Message message = ERR_TRUSTSTORE_PIN_FILE_CANNOT_READ.get(
+ String.valueOf(pinFile),
+ String.valueOf(cfgEntryDN),
+ getExceptionMessage(ioe));
+ unacceptableReasons.add(message);
+ configAcceptable = false;
+ }
+ finally
+ {
+ try
+ {
+ br.close();
+ } catch (Exception e) {
+ // ignore
+ }
+ }
+
+ if (pinStr == null)
+ {
+ Message message = ERR_TRUSTSTORE_PIN_FILE_EMPTY.get(
+ String.valueOf(pinFile),
+ String.valueOf(cfgEntryDN));
+ unacceptableReasons.add(message);
+ configAcceptable = false;
+ }
+ }
+ }
+
+
+ return configAcceptable;
+ }
+
+
+ /**
+ * {@inheritDoc}
+ */
+ public ConfigChangeResult applyConfigurationChange(TrustStoreBackendCfg cfg)
+ {
+ ResultCode resultCode = ResultCode.SUCCESS;
+ boolean adminActionRequired = false;
+ ArrayList<Message> messages = new ArrayList<Message>();
+ DN configEntryDN = cfg.dn();
+
+ // Get the path to the trust store file.
+ String newTrustStoreFile = cfg.getTrustStoreFile();
+ File f = getFileForPath(newTrustStoreFile);
+ if (! (f.exists() && f.isFile()))
+ {
+ resultCode = DirectoryServer.getServerErrorResultCode();
+
+ messages.add(ERR_TRUSTSTORE_NO_SUCH_FILE.get(
+ String.valueOf(newTrustStoreFile),
+ String.valueOf(configEntryDN)));
+ }
+
+
+ // Get the trust store type. If none is specified, then use the default
+ // type.
+ String newTrustStoreType = cfg.getTrustStoreType();
+ if (newTrustStoreType == null)
+ {
+ newTrustStoreType = KeyStore.getDefaultType();
+ }
+
+ try
+ {
+ KeyStore.getInstance(newTrustStoreType);
+ }
+ catch (KeyStoreException kse)
+ {
+ if (debugEnabled())
+ {
+ TRACER.debugCaught(DebugLogLevel.ERROR, kse);
+ }
+
+ messages.add(ERR_TRUSTSTORE_INVALID_TYPE.get(
+ String.valueOf(newTrustStoreType),
+ String.valueOf(configEntryDN),
+ getExceptionMessage(kse)));
+
+ resultCode = DirectoryServer.getServerErrorResultCode();
+ }
+
+
+ // Get the PIN needed to access the contents of the trust store file. We
+ // will offer several places to look for the PIN, and we will do so in the
+ // following order:
+ // - In a specified Java property
+ // - In a specified environment variable
+ // - In a specified file on the server filesystem.
+ // - As the value of a configuration attribute.
+ // In any case, the PIN must be in the clear. If no PIN is provided, then
+ // it will be assumed that none is required to access the information in the
+ // trust store.
+ char[] newPIN = null;
+ String newPINProperty = cfg.getTrustStorePinProperty();
+ if (newPINProperty == null)
+ {
+ String newPINEnVar = cfg.getTrustStorePinEnvironmentVariable();
+ if (newPINEnVar == null)
+ {
+ String newPINFile = cfg.getTrustStorePinFile();
+ if (newPINFile == null)
+ {
+ String pinStr = cfg.getTrustStorePin();
+ if (pinStr == null)
+ {
+ newPIN = null;
+ }
+ else
+ {
+ newPIN = pinStr.toCharArray();
+ }
+ }
+ else
+ {
+ File pinFile = getFileForPath(newPINFile);
+ if (! pinFile.exists())
+ {
+ try
+ {
+ // Generate a PIN.
+ newPIN = createKeystorePassword();
+
+ // Store the PIN in the pin file.
+ createPINFile(pinFile.getPath(), new String(newPIN));
+ }
+ catch (Exception e)
+ {
+ resultCode = DirectoryServer.getServerErrorResultCode();
+
+ messages.add(ERR_TRUSTSTORE_PIN_NO_SUCH_FILE.get(
+ String.valueOf(newPINFile),
+ String.valueOf(configEntryDN)));
+ }
+ }
+ else
+ {
+ String pinStr = null;
+
+ BufferedReader br = null;
+ try
+ {
+ br = new BufferedReader(new FileReader(pinFile));
+ pinStr = br.readLine();
+ }
+ catch (IOException ioe)
+ {
+ resultCode = DirectoryServer.getServerErrorResultCode();
+
+ messages.add(ERR_TRUSTSTORE_PIN_FILE_CANNOT_READ.get(
+ String.valueOf(newPINFile),
+ String.valueOf(configEntryDN),
+ getExceptionMessage(ioe)));
+ }
+ finally
+ {
+ try
+ {
+ br.close();
+ } catch (Exception e) {
+ // ignore
+ }
+ }
+
+ if (pinStr == null)
+ {
+ resultCode = DirectoryServer.getServerErrorResultCode();
+
+ messages.add(ERR_TRUSTSTORE_PIN_FILE_EMPTY.get(
+ String.valueOf(newPINFile),
+ String.valueOf(configEntryDN)));
+ }
+ else
+ {
+ newPIN = pinStr.toCharArray();
+ }
+ }
+ }
+ }
+ else
+ {
+ String pinStr = System.getenv(newPINEnVar);
+ if (pinStr == null)
+ {
+ resultCode = DirectoryServer.getServerErrorResultCode();
+
+ messages.add(ERR_TRUSTSTORE_PIN_ENVAR_NOT_SET.get(
+ String.valueOf(newPINEnVar),
+ String.valueOf(configEntryDN)));
+ }
+ else
+ {
+ newPIN = pinStr.toCharArray();
+ }
+ }
+ }
+ else
+ {
+ String pinStr = System.getProperty(newPINProperty);
+ if (pinStr == null)
+ {
+ resultCode = DirectoryServer.getServerErrorResultCode();
+
+ messages.add(ERR_TRUSTSTORE_PIN_PROPERTY_NOT_SET.get(
+ String.valueOf(newPINProperty),
+ String.valueOf(configEntryDN)));
+ }
+ else
+ {
+ newPIN = pinStr.toCharArray();
+ }
+ }
+
+
+ if (resultCode == ResultCode.SUCCESS)
+ {
+ trustStoreFile = newTrustStoreFile;
+ trustStoreType = newTrustStoreType;
+ trustStorePIN = newPIN;
+ configuration = cfg;
+ certificateManager =
+ new CertificateManager(getFileForPath(trustStoreFile).getPath(),
+ trustStoreType,
+ new String(trustStorePIN));
+ }
+
+
+ return new ConfigChangeResult(resultCode, adminActionRequired, messages);
+ }
+
+ /**
+ * Create a new child DN from a given parent DN. The child RDN is formed
+ * from a given attribute type and string value.
+ * @param parentDN The DN of the parent.
+ * @param rdnAttrType The attribute type of the RDN.
+ * @param rdnStringValue The string value of the RDN.
+ * @return A new child DN.
+ */
+ public static DN makeChildDN(DN parentDN, AttributeType rdnAttrType,
+ String rdnStringValue)
+ {
+ AttributeValue attrValue =
+ new AttributeValue(rdnAttrType, rdnStringValue);
+ return parentDN.concat(RDN.create(rdnAttrType, attrValue));
+ }
+
+
+ /**
+ * 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.
+ */
+ public KeyManager[] getKeyManagers()
+ throws DirectoryException
+ {
+ KeyStore keyStore;
+ try
+ {
+ keyStore = KeyStore.getInstance(trustStoreType);
+
+ FileInputStream inputStream =
+ new FileInputStream(getFileForPath(trustStoreFile));
+ keyStore.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 keyManagerAlgorithm = KeyManagerFactory.getDefaultAlgorithm();
+ KeyManagerFactory keyManagerFactory =
+ KeyManagerFactory.getInstance(keyManagerAlgorithm);
+ keyManagerFactory.init(keyStore, trustStorePIN);
+ return keyManagerFactory.getKeyManagers();
+ }
+ 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);
+ }
+ }
+
+
+ /**
+ * Retrieves a set of {@code TrustManager} objects that may be used
+ * for interactions requiring access to a trust manager.
+ *
+ * @return A set of {@code TrustManager} objects that may be used
+ * for interactions requiring access to a trust manager.
+ *
+ * @throws DirectoryException If a problem occurs while attempting
+ * to obtain the set of trust managers.
+ */
+ 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();
+ }
+
+
+ private void addCertificate(Entry entry)
+ throws DirectoryException
+ {
+ DN entryDN = entry.getDN();
+
+ // 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(v.getStringValue()))
+ {
+ Message message = ERR_TRUSTSTORE_ALIAS_IN_USE.get(
+ String.valueOf(entryDN));
+ throw new DirectoryException(ResultCode.ENTRY_ALREADY_EXISTS,
+ message);
+ }
+
+ ObjectClass ocSelfSignedCertRequest =
+ DirectoryServer.getObjectClass(OC_SELF_SIGNED_CERT_REQUEST, true);
+ if (entry.hasObjectClass(ocSelfSignedCertRequest))
+ {
+ try
+ {
+ certificateManager.generateSelfSignedCertificate(
+ certAlias,
+ getADSCertificateSubjectDN(),
+ getADSCertificateValidity());
+ }
+ catch (Exception e)
+ {
+ Message message = ERR_TRUSTSTORE_CANNOT_GENERATE_CERT.get(
+ certAlias, trustStoreFile, getExceptionMessage(e));
+ throw new DirectoryException(
+ DirectoryServer.getServerErrorResultCode(), message, e);
+ }
+ }
+ else
+ {
+ List<Attribute> certAttrs = entry.getAttribute(ATTR_ADS_CERTIFICATE);
+ if (certAttrs == null)
+ {
+ Message message =
+ ERR_TRUSTSTORE_ENTRY_MISSING_CERT_ATTR.get(
+ String.valueOf(entryDN), ATTR_ADS_CERTIFICATE);
+ throw new DirectoryException(
+ DirectoryServer.getServerErrorResultCode(), message);
+ }
+ if (certAttrs.size() != 1)
+ {
+ Message message =
+ ERR_TRUSTSTORE_ENTRY_HAS_MULTIPLE_CERT_ATTRS.get(
+ String.valueOf(entryDN), ATTR_ADS_CERTIFICATE);
+ throw new DirectoryException(
+ DirectoryServer.getServerErrorResultCode(), message);
+ }
+
+ LinkedHashSet<AttributeValue> certValues = certAttrs.get(0).getValues();
+ if (certValues == null)
+ {
+ Message message =
+ ERR_TRUSTSTORE_ENTRY_MISSING_CERT_VALUE.get(
+ String.valueOf(entryDN), ATTR_ADS_CERTIFICATE);
+ throw new DirectoryException(
+ DirectoryServer.getServerErrorResultCode(), message);
+ }
+ if (certValues.size() != 1)
+ {
+ Message message =
+ ERR_TRUSTSTORE_ENTRY_HAS_MULTIPLE_CERT_VALUES.get(
+ String.valueOf(entryDN), ATTR_ADS_CERTIFICATE);
+ throw new DirectoryException(
+ DirectoryServer.getServerErrorResultCode(), message);
+ }
+
+ byte[] certBytes = certValues.iterator().next().getValueBytes();
+ try
+ {
+ File tempDir = getFileForPath("config");
+ File tempFile = File.createTempFile(configuration.getBackendId(),
+ certAlias, tempDir);
+ try
+ {
+ FileOutputStream outputStream =
+ new FileOutputStream(tempFile.getPath(), false);
+ try
+ {
+ outputStream.write(certBytes);
+ }
+ finally
+ {
+ outputStream.close();
+ }
+
+ certificateManager.addCertificate(certAlias, tempFile);
+ }
+ finally
+ {
+ tempFile.delete();
+ }
+ }
+ catch (IOException e)
+ {
+ Message message = ERR_TRUSTSTORE_CANNOT_WRITE_CERT.get(
+ certAlias, getExceptionMessage(e));
+ throw new DirectoryException(
+ DirectoryServer.getServerErrorResultCode(), message, e);
+ }
+ }
+ }
+ catch (Exception e)
+ {
+ Message message = ERR_TRUSTSTORE_CANNOT_ADD_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.
+ */
+ private static int getADSCertificateValidity()
+ {
+ return 20 * 365;
+ }
+
+ /**
+ * Returns the Subject DN to be used to generate the ADS certificate.
+ * @return The Subject DN to be used to generate the ADS certificate.
+ * @throws java.net.UnknownHostException If the server host name could not be
+ * determined.
+ */
+ private static String getADSCertificateSubjectDN()
+ throws UnknownHostException
+ {
+ String hostname =
+ java.net.InetAddress.getLocalHost().getCanonicalHostName();
+ return "cn=" + Rdn.escapeValue(hostname) + ",O=OpenDS Certificate";
+ }
+
+ /**
+ * Create a randomly generated password for a certificate keystore.
+ * @return A randomly generated password for a certificate keystore.
+ */
+ private static char[] createKeystorePassword() {
+ int pwdLength = 50;
+ char[] pwd = new char[pwdLength];
+ Random random = new Random();
+ for (int pos=0; pos < pwdLength; pos++) {
+ int type = getRandomInt(random,3);
+ char nextChar = getRandomChar(random,type);
+ pwd[pos] = nextChar;
+ }
+ return pwd;
+ }
+
+ private static char getRandomChar(Random random, int type)
+ {
+ char generatedChar;
+ int next = random.nextInt();
+ int d;
+
+ switch (type)
+ {
+ case 0:
+ // Will return a digit
+ d = next % 10;
+ if (d < 0)
+ {
+ d = d * (-1);
+ }
+ generatedChar = (char) (d+48);
+ break;
+ case 1:
+ // Will return a lower case letter
+ d = next % 26;
+ if (d < 0)
+ {
+ d = d * (-1);
+ }
+ generatedChar = (char) (d + 97);
+ break;
+ default:
+ // Will return a capital letter
+ d = (next % 26);
+ if (d < 0)
+ {
+ d = d * (-1);
+ }
+ generatedChar = (char) (d + 65) ;
+ }
+
+ return generatedChar;
+ }
+
+ private static int getRandomInt(Random random,int modulo)
+ {
+ return (random.nextInt() & modulo);
+ }
+
+ /**
+ * Creates a PIN file on the specified path.
+ * @param path the path where the PIN file will be created.
+ * @param pin The PIN to store in the file.
+ * @throws IOException if something goes wrong.
+ */
+ public static void createPINFile(String path, String pin)
+ throws IOException
+ {
+ FileWriter file = new FileWriter(path);
+ PrintWriter out = new PrintWriter(file);
+
+ out.println(pin);
+
+ out.flush();
+ out.close();
+
+ if(FilePermission.canSetPermissions()) {
+ try {
+ if (!FilePermission.setPermissions(new File(path),
+ new FilePermission(0600)))
+ {
+ // Log a warning that the permissions were not set.
+ Message message = WARN_TRUSTSTORE_SET_PERMISSIONS_FAILED.get(path);
+ ErrorLogger.logError(message);
+ }
+ } catch(DirectoryException e) {
+ // Log a warning that the permissions were not set.
+ Message message = WARN_TRUSTSTORE_SET_PERMISSIONS_FAILED.get(path);
+ ErrorLogger.logError(message);
+ }
+ }
+ }
+
+}
+
--
Gitblit v1.10.0