Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@
<doclint>none</doclint>

<!-- Set versions for depending projects -->
<org.apache.directory.api.version>2.1.9</org.apache.directory.api.version>
<org.apache.directory.api.version>2.1.10-SNAPSHOT</org.apache.directory.api.version>
<org.apache.directory.mavibot.version>1.0.0-M8</org.apache.directory.mavibot.version>
<org.apache.directory.checkstyle-configuration.version>2.0.2</org.apache.directory.checkstyle-configuration.version>
<org.apache.directory.jdbm.version>2.0.0-M3</org.apache.directory.jdbm.version>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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
Expand Down Expand Up @@ -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 );
Expand All @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,8 @@
* <li>configEntryDn : the configuration entry's DN</li>
* <li>chaseReferrals : tells if we chase referrals, defaults to false</li>
* <li>cookie : the replication cookie</li>
* <li>useTls : the connection uses TLS, defaults to true</li>
* <li>useTls : the connection uses TLS (= StartTLS over LDAP), defaults to true</li>
* <li>useSsl : the connection uses SSL (= LDAPS), defaults to false</li>
* <li>trustManager : the trustManager to use, defaults to {@link ReplicationTrustManager}</li>
* </ul>
*
Expand Down Expand Up @@ -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();

Expand Down Expand Up @@ -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()
{
Expand All @@ -498,16 +502,35 @@ 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 )
{
this.useTls = 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
*/
Expand Down Expand Up @@ -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();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 );
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@
* <li>configEntryDn : the configuration entry's DN</li>
* <li>chaseReferrals : tells if we chase referrals, defaults to false</li>
* <li>useTls : the connection uses TLS, defaults to true</li>
* <li>useSsl : the connection uses SSL (= LDAPS), defaults to false</li>
* <li>trustManagerFQCN : X509TrustManager FQCN to override strictCertVerification</li>
* </ul>
* @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
Expand Down Expand Up @@ -199,6 +200,14 @@
boolean useTls() default true;


/**
* flag to indicate the use of SSL (= LDAPS), default is false
*
* @return <tt>true</tt> 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().
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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() );

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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
*/
Expand Down Expand Up @@ -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();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
Expand Down Expand Up @@ -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<AdsBaseBean>() );

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<LdifEntry> entries )
{
for ( LdifEntry entry : entries )
{
if ( "ads-replConsumerId=test".equalsIgnoreCase( entry.getDn().getRdn().getName() ) )
{
return entry;
}
}

return null;
}
}
1 change: 1 addition & 0 deletions server-config/src/test/resources/ldapServer.ldif
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1074,6 +1074,7 @@ public static List<ReplicationConsumer> createReplConsumers( List<ReplConsumerBe
config.setSearchSizeLimit( replBean.getReplSearchSizeLimit() );

config.setUseTls( replBean.isReplUseTls() );
config.setUseSsl( replBean.isReplUseSsl() );

if ( !Strings.isEmpty( replBean.getReplTrustManagerFQCN() ) )
{
Expand Down