diff --git a/pom.xml b/pom.xml
index 6e1f5c59a7..cc63a43e29 100644
--- a/pom.xml
+++ b/pom.xml
@@ -46,7 +46,7 @@
none
- 2.1.9
+ 2.1.10-SNAPSHOT
1.0.0-M8
2.0.2
2.0.0-M3
diff --git a/protocol-ldap/src/main/java/org/apache/directory/server/ldap/replication/ReplicationTrustManager.java b/protocol-ldap/src/main/java/org/apache/directory/server/ldap/replication/ReplicationTrustManager.java
index 108e07ca7b..2400e53e72 100644
--- a/protocol-ldap/src/main/java/org/apache/directory/server/ldap/replication/ReplicationTrustManager.java
+++ b/protocol-ldap/src/main/java/org/apache/directory/server/ldap/replication/ReplicationTrustManager.java
@@ -22,6 +22,9 @@
import java.io.ByteArrayInputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.InputStream;
import java.security.KeyStore;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
@@ -38,9 +41,11 @@
/**
* A X509TrustManager implementation used by the replication subsystem.
- * This implementation doesn't require the certificates to be stored in a file, instead
- * it parses the given certificates of replica peers using Bouncycastle's X509CertParser
- * and stores them in the in-memory KeyStore.
+ * It is seeded with the JVM default trust store (the 'javax.net.ssl.trustStore'
+ * file, or the 'cacerts' file of the Java installation) so that certificates
+ * signed by a trusted CA are accepted out of the box. Certificates of replica
+ * peers can be added on top of that by parsing them with Bouncycastle's
+ * X509CertParser and storing them in the in-memory KeyStore.
*
* The SunX509 TrustManagerFactory is then initialized using this KeyStore and the
* resulting X509TrustManager present in this factory's TrustManagers will be used
@@ -85,7 +90,7 @@ private ReplicationTrustManager()
{
try
{
- ks.load( null, null ); // initiate with null stream and password, this keystore resides in-memory only
+ loadDefaultTrustStore();
TrustManagerFactory tmFactory = TrustManagerFactory.getInstance( TrustManagerFactory.getDefaultAlgorithm() );
tmFactory.init( ks );
@@ -110,6 +115,70 @@ private ReplicationTrustManager()
}
+ /**
+ * Initializes the in-memory keystore with the JVM default trust store:
+ * either the file configured through the 'javax.net.ssl.trustStore' system
+ * property, or the 'cacerts' file of the Java installation. If none of
+ * them can be loaded, the keystore is left empty and certificates can
+ * still be added via {@link #addCertificate(String, byte[])}.
+ */
+ private void loadDefaultTrustStore()
+ {
+ String trustStoreFile = System.getProperty( "javax.net.ssl.trustStore" );
+ char[] trustStorePassword = null;
+
+ String trustStorePasswordValue = System.getProperty( "javax.net.ssl.trustStorePassword" );
+
+ if ( trustStorePasswordValue != null )
+ {
+ trustStorePassword = trustStorePasswordValue.toCharArray();
+ }
+
+ try
+ {
+ if ( trustStoreFile != null )
+ {
+ try ( InputStream in = new FileInputStream( trustStoreFile ) )
+ {
+ ks.load( in, trustStorePassword );
+ }
+ }
+ else
+ {
+ File cacertsFile = new File( System.getProperty( "java.home" ),
+ "lib" + File.separator + "security" + File.separator + "cacerts" );
+
+ if ( cacertsFile.exists() )
+ {
+ try ( InputStream in = new FileInputStream( cacertsFile ) )
+ {
+ ks.load( in, trustStorePassword );
+ }
+ }
+ else
+ {
+ ks.load( null, null );
+ }
+ }
+ }
+ catch ( Exception e )
+ {
+ LOG.warn( "failed to load the default trust store, continuing with an empty keystore", e );
+
+ try
+ {
+ ks = KeyStore.getInstance( KeyStore.getDefaultType() );
+ ks.load( null, null );
+ }
+ catch ( Exception ke )
+ {
+ // cannot happen with a null stream and null password
+ LOG.error( "failed to initialize an empty keystore", ke );
+ }
+ }
+ }
+
+
/**
* loads the given map of [alias-name, certificate-data] entries into the keystore
* to be used by the trust manager
diff --git a/protocol-ldap/src/main/java/org/apache/directory/server/ldap/replication/SyncReplConfiguration.java b/protocol-ldap/src/main/java/org/apache/directory/server/ldap/replication/SyncReplConfiguration.java
index e5731e3af2..7671096d89 100644
--- a/protocol-ldap/src/main/java/org/apache/directory/server/ldap/replication/SyncReplConfiguration.java
+++ b/protocol-ldap/src/main/java/org/apache/directory/server/ldap/replication/SyncReplConfiguration.java
@@ -55,7 +55,8 @@
*
configEntryDn : the configuration entry's DN
* chaseReferrals : tells if we chase referrals, defaults to false
* cookie : the replication cookie
- * useTls : the connection uses TLS, defaults to true
+ * useTls : the connection uses TLS (= StartTLS over LDAP), defaults to true
+ * useSsl : the connection uses SSL (= LDAPS), defaults to false
* trustManager : the trustManager to use, defaults to {@link ReplicationTrustManager}
*
*
@@ -119,9 +120,12 @@ public class SyncReplConfiguration implements ReplicationConsumerConfig
/** flag to indicate whether to chase referrals or not, default is false hence passes ManageDsaITControl with syncsearch request*/
private boolean chaseReferrals = false;
- /** flag to indicate the use of TLS, default is true */
+ /** flag to indicate the use of TLS (= StartTLS over LDAP), default is true */
private boolean useTls = true;
+ /** flag to indicate the use of SSL (= LDAPS), default is false */
+ private boolean useSsl = false;
+
/** the X509 certificate trust manager used, default value set to {@link ReplicationTrustManager} */
private X509TrustManager trustManager = ReplicationTrustManager.getInstance();
@@ -489,7 +493,7 @@ public Dn getConfigEntryDn()
/**
- * @return true if we use TLS
+ * @return true if we use TLS (= StartTLS over LDAP)
*/
public boolean isUseTls()
{
@@ -498,9 +502,9 @@ public boolean isUseTls()
/**
- * set the option to turn on/off use of TLS
+ * set the option to turn on/off use of TLS (= StartTLS over LDAP)
*
- * @param useTls If we have to use TLS
+ * @param useTls If we have to use TLS (= StartTLS over LDAP)
*/
public void setUseTls( boolean useTls )
{
@@ -508,6 +512,25 @@ public void setUseTls( boolean useTls )
}
+ /**
+ * @return true if we use SSL (= LDAPS)
+ */
+ public boolean isUseSsl()
+ {
+ return useSsl;
+ }
+
+
+ /**
+ * set the option to turn on/off use of SSL (= LDAPS)
+ *
+ * @param useSsl If we have to use SSL (= LDAPS)
+ */
+ public void setUseSsl( boolean useSsl )
+ {
+ this.useSsl = useSsl;
+ }
+
/**
* @return The Trustmanager instance
*/
@@ -643,7 +666,7 @@ public String toString()
sb.append( "provider:" ).append( producer ).append( ", " );
sb.append( "user:'" ).append( replUserDn ).append( "', " );
- sb.append( "TLS:" ).append( useTls ).append( "]" );
+ sb.append( "TLS:" ).append( useTls ).append( ", SSL:" ).append( useSsl ).append( "]" );
return sb.toString();
}
diff --git a/protocol-ldap/src/main/java/org/apache/directory/server/ldap/replication/consumer/ReplicationConsumerImpl.java b/protocol-ldap/src/main/java/org/apache/directory/server/ldap/replication/consumer/ReplicationConsumerImpl.java
index e2c292f783..8d7902623e 100644
--- a/protocol-ldap/src/main/java/org/apache/directory/server/ldap/replication/consumer/ReplicationConsumerImpl.java
+++ b/protocol-ldap/src/main/java/org/apache/directory/server/ldap/replication/consumer/ReplicationConsumerImpl.java
@@ -222,7 +222,12 @@ public boolean connect()
connection = new LdapNetworkConnection( providerHost, port );
connection.setSchemaManager( schemaManager );
- if ( config.isUseTls() )
+ if ( config.isUseSsl() )
+ {
+ connection.getConfig().setTrustManagers( config.getTrustManager() );
+ connection.getConfig().setUseSsl( true );
+ }
+ else if ( config.isUseTls() )
{
connection.getConfig().setTrustManagers( config.getTrustManager() );
connection.getConfig().setUseTls( true );
diff --git a/server-annotations/src/main/java/org/apache/directory/server/annotations/CreateConsumer.java b/server-annotations/src/main/java/org/apache/directory/server/annotations/CreateConsumer.java
index a2a3764a09..52333a39e3 100644
--- a/server-annotations/src/main/java/org/apache/directory/server/annotations/CreateConsumer.java
+++ b/server-annotations/src/main/java/org/apache/directory/server/annotations/CreateConsumer.java
@@ -53,6 +53,7 @@
* configEntryDn : the configuration entry's DN
* chaseReferrals : tells if we chase referrals, defaults to false
* useTls : the connection uses TLS, defaults to true
+ * useSsl : the connection uses SSL (= LDAPS), defaults to false
* trustManagerFQCN : X509TrustManager FQCN to override strictCertVerification
*
* @author Apache Directory Project
@@ -199,6 +200,14 @@
boolean useTls() default true;
+ /**
+ * flag to indicate the use of SSL (= LDAPS), default is false
+ *
+ * @return true if SSL is in use
+ */
+ boolean useSsl() default false;
+
+
/**
* Fully-qualified class name of an X509TrustManager to use for replication TLS.
* If set, this takes precedence over strictCertVerification().
diff --git a/server-annotations/src/main/java/org/apache/directory/server/factory/ServerAnnotationProcessor.java b/server-annotations/src/main/java/org/apache/directory/server/factory/ServerAnnotationProcessor.java
index fbf1ab2d1f..ec66535b5d 100644
--- a/server-annotations/src/main/java/org/apache/directory/server/factory/ServerAnnotationProcessor.java
+++ b/server-annotations/src/main/java/org/apache/directory/server/factory/ServerAnnotationProcessor.java
@@ -294,6 +294,7 @@ private static ReplicationConsumer createConsumer( CreateConsumer createConsumer
config.setReplUserDn( createConsumer.replUserDn() );
config.setReplUserPassword( Strings.getBytesUtf8( createConsumer.replUserPassword() ) );
config.setUseTls( createConsumer.useTls() );
+ config.setUseSsl( createConsumer.useSsl() );
config.setBaseDn( createConsumer.baseDn() );
config.setRefreshInterval( createConsumer.refreshInterval() );
diff --git a/server-config/src/main/java/org/apache/directory/server/config/beans/ReplConsumerBean.java b/server-config/src/main/java/org/apache/directory/server/config/beans/ReplConsumerBean.java
index 20f66c5ef9..6dbe7ea6da 100644
--- a/server-config/src/main/java/org/apache/directory/server/config/beans/ReplConsumerBean.java
+++ b/server-config/src/main/java/org/apache/directory/server/config/beans/ReplConsumerBean.java
@@ -98,6 +98,10 @@ public class ReplConsumerBean extends AdsBaseBean
@ConfigurationElement(attributeType = SchemaConstants.ADS_REPL_USE_TLS, isOptional = true)
private boolean replUseTls;
+ /** Tells if SSL should be used during replication */
+ @ConfigurationElement(attributeType = SchemaConstants.ADS_REPL_USE_SSL, isOptional = true, defaultValue = "false")
+ private boolean replUseSsl;
+
/** The X509 TrustManager FQCN to use */
@ConfigurationElement(attributeType = "ads-replTrustManagerFQCN", isOptional = true)
private String replTrustManagerFQCN;
@@ -419,6 +423,24 @@ public void setReplUseTls( boolean replUseTls )
}
+ /**
+ * @return the replUseSsl
+ */
+ public boolean isReplUseSsl()
+ {
+ return replUseSsl;
+ }
+
+
+ /**
+ * @param replUseSsl the replUseSsl to set
+ */
+ public void setReplUseSsl( boolean replUseSsl )
+ {
+ this.replUseSsl = replUseSsl;
+ }
+
+
/**
* @return the replTrustManagerFQCN
*/
@@ -485,6 +507,7 @@ public String toString( String tabs )
sb.append( toString( tabs, " trust manager's FQCN", replTrustManagerFQCN ) );
sb.append( toString( tabs, " use TLS", replUseTls ) );
+ sb.append( toString( tabs, " use SSL", replUseSsl ) );
return sb.toString();
}
diff --git a/server-config/src/test/java/org/apache/directory/server/config/ConfigWriterTest.java b/server-config/src/test/java/org/apache/directory/server/config/ConfigWriterTest.java
index a3962cad1d..48c454b51c 100644
--- a/server-config/src/test/java/org/apache/directory/server/config/ConfigWriterTest.java
+++ b/server-config/src/test/java/org/apache/directory/server/config/ConfigWriterTest.java
@@ -22,10 +22,12 @@
import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertTrue;
import java.io.File;
+import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
@@ -44,6 +46,10 @@
import org.apache.directory.api.util.FileUtils;
import org.apache.directory.api.util.exception.Exceptions;
import org.apache.directory.server.config.beans.ConfigBean;
+import org.apache.directory.server.config.beans.AdsBaseBean;
+import org.apache.directory.server.config.beans.DirectoryServiceBean;
+import org.apache.directory.server.config.beans.LdapServerBean;
+import org.apache.directory.server.config.beans.ReplConsumerBean;
import org.apache.directory.server.core.api.DnFactory;
import org.apache.directory.server.core.partition.ldif.SingleFileLdifPartition;
import org.apache.directory.server.core.shared.DefaultDnFactory;
@@ -162,4 +168,69 @@ public int compare( LdifEntry o1, LdifEntry o2 )
// Destroying the config partition
configPartition.destroy( configPartition.beginReadTransaction() );
}
+
+
+ /**
+ * Tests that the 'ads-replUseSsl' attribute is not written into the
+ * configuration entries as long as it keeps its default value (false),
+ * and that it is written once explicitly enabled. The attribute type
+ * itself is defined in the LDAP API schema.
+ */
+ @Test
+ public void testReplConsumerUseSsl() throws Exception
+ {
+ ConfigBean configBean = new ConfigBean();
+
+ // The list is normally set by the ConfigPartitionReader, initialize it here
+ Field directoryServiceBeansField = ConfigBean.class.getDeclaredField( "directoryServiceBeans" );
+ directoryServiceBeansField.setAccessible( true );
+ directoryServiceBeansField.set( configBean, new ArrayList() );
+
+ DirectoryServiceBean directoryServiceBean = new DirectoryServiceBean();
+ directoryServiceBean.setDirectoryServiceId( "default" );
+
+ LdapServerBean ldapServerBean = new LdapServerBean();
+ ldapServerBean.setServerId( "ldapServer" );
+
+ ReplConsumerBean replConsumerBean = new ReplConsumerBean();
+ replConsumerBean.setReplConsumerId( "test" );
+ replConsumerBean.setReplProvHostName( "localhost" );
+ replConsumerBean.setReplProvPort( 10389 );
+ replConsumerBean.setSearchBaseDn( "ou=system" );
+
+ ldapServerBean.addReplConsumers( replConsumerBean );
+ directoryServiceBean.addServers( ldapServerBean );
+ configBean.addDirectoryService( directoryServiceBean );
+
+ // With the default value (false), the attribute must not be written
+ ConfigWriter configWriter = new ConfigWriter( schemaManager, configBean );
+ LdifEntry replConsumerEntry = findReplConsumerEntry( configWriter.getConvertedLdifEntries() );
+ assertNotNull( replConsumerEntry );
+ assertFalse( replConsumerEntry.getEntry().containsAttribute( "ads-replUseSsl" ) );
+
+ // With the flag explicitly enabled, the attribute must be written
+ replConsumerBean.setReplUseSsl( true );
+ configWriter = new ConfigWriter( schemaManager, configBean );
+ replConsumerEntry = findReplConsumerEntry( configWriter.getConvertedLdifEntries() );
+ assertNotNull( replConsumerEntry );
+ assertTrue( replConsumerEntry.getEntry().containsAttribute( "ads-replUseSsl" ) );
+ assertTrue( Boolean.parseBoolean( replConsumerEntry.getEntry().get( "ads-replUseSsl" ).getString() ) );
+ }
+
+
+ /**
+ * Finds the replication consumer entry in the given list of entries.
+ */
+ private LdifEntry findReplConsumerEntry( List entries )
+ {
+ for ( LdifEntry entry : entries )
+ {
+ if ( "ads-replConsumerId=test".equalsIgnoreCase( entry.getDn().getRdn().getName() ) )
+ {
+ return entry;
+ }
+ }
+
+ return null;
+ }
}
diff --git a/server-config/src/test/resources/ldapServer.ldif b/server-config/src/test/resources/ldapServer.ldif
index 4d3c625747..2a24f0b616 100644
--- a/server-config/src/test/resources/ldapServer.ldif
+++ b/server-config/src/test/resources/ldapServer.ldif
@@ -180,6 +180,7 @@ ads-replSearchFilter: (&(objectClass=top)(ou=test))
ads-replProvPort: 12345
ads-replRefreshNPersist: TRUE
ads-replUseTls: TRUE
+ads-replUseSsl: TRUE
ads-replStrictCertValidation: TRUE
ads-replAliasDerefMode: NEVER
ads-replRefreshInterval: 100
diff --git a/service-builder/src/main/java/org/apache/directory/server/config/builder/ServiceBuilder.java b/service-builder/src/main/java/org/apache/directory/server/config/builder/ServiceBuilder.java
index a105231067..1c8e503847 100644
--- a/service-builder/src/main/java/org/apache/directory/server/config/builder/ServiceBuilder.java
+++ b/service-builder/src/main/java/org/apache/directory/server/config/builder/ServiceBuilder.java
@@ -1074,6 +1074,7 @@ public static List createReplConsumers( List