You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@directory.apache.org by se...@apache.org on 2021/06/20 06:36:50 UTC
[directory-server] 01/02: DIRSERVER-1632: Use improved SASL filter
from ldap-api (DIRAPI-373)
This is an automated email from the ASF dual-hosted git repository.
seelmann pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/directory-server.git
commit e71b7260dcdddf8611701384cfe0559c53ec03a5
Author: Stefan Seelmann <ma...@stefan-seelmann.de>
AuthorDate: Sun Jun 20 08:31:28 2021 +0200
DIRSERVER-1632: Use improved SASL filter from ldap-api (DIRAPI-373)
---
pom.xml | 2 +-
.../handlers/sasl/AbstractMechanismHandler.java | 1 +
.../server/ldap/handlers/sasl/SaslFilter.java | 176 ----------
.../server/operations/bind/SaslBindIT.java | 355 ++++++++++++++++++---
4 files changed, 309 insertions(+), 225 deletions(-)
diff --git a/pom.xml b/pom.xml
index 81c6905..71393fe 100644
--- a/pom.xml
+++ b/pom.xml
@@ -46,7 +46,7 @@
<doclint>none</doclint>
<!-- Set versions for depending projects -->
- <org.apache.directory.api.version>2.0.2</org.apache.directory.api.version>
+ <org.apache.directory.api.version>2.0.3-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.1</org.apache.directory.checkstyle-configuration.version>
<org.apache.directory.jdbm.version>2.0.0-M3</org.apache.directory.jdbm.version>
diff --git a/protocol-ldap/src/main/java/org/apache/directory/server/ldap/handlers/sasl/AbstractMechanismHandler.java b/protocol-ldap/src/main/java/org/apache/directory/server/ldap/handlers/sasl/AbstractMechanismHandler.java
index 57f50bd..249f858 100644
--- a/protocol-ldap/src/main/java/org/apache/directory/server/ldap/handlers/sasl/AbstractMechanismHandler.java
+++ b/protocol-ldap/src/main/java/org/apache/directory/server/ldap/handlers/sasl/AbstractMechanismHandler.java
@@ -22,6 +22,7 @@ package org.apache.directory.server.ldap.handlers.sasl;
import javax.security.sasl.SaslServer;
+import org.apache.directory.api.ldap.codec.api.SaslFilter;
import org.apache.directory.server.ldap.LdapSession;
import org.apache.mina.core.filterchain.IoFilterChain;
import org.apache.mina.core.session.IoSession;
diff --git a/protocol-ldap/src/main/java/org/apache/directory/server/ldap/handlers/sasl/SaslFilter.java b/protocol-ldap/src/main/java/org/apache/directory/server/ldap/handlers/sasl/SaslFilter.java
deleted file mode 100644
index ae2f6ba..0000000
--- a/protocol-ldap/src/main/java/org/apache/directory/server/ldap/handlers/sasl/SaslFilter.java
+++ /dev/null
@@ -1,176 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- *
- */
-package org.apache.directory.server.ldap.handlers.sasl;
-
-
-import javax.security.sasl.Sasl;
-import javax.security.sasl.SaslException;
-import javax.security.sasl.SaslServer;
-
-import org.apache.directory.api.ldap.model.constants.SaslQoP;
-import org.apache.mina.core.buffer.IoBuffer;
-import org.apache.mina.core.filterchain.IoFilterAdapter;
-import org.apache.mina.core.session.IoSession;
-import org.apache.mina.core.write.DefaultWriteRequest;
-import org.apache.mina.core.write.WriteRequest;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-
-/**
- * An {@link IoFilterAdapter} that handles integrity and confidentiality protection
- * for a SASL bound session. The SaslFilter must be constructed with a SASL
- * context that has completed SASL negotiation. Some SASL mechanisms, such as
- * CRAM-MD5, only support authentication and thus do not need this filter. DIGEST-MD5
- * and GSSAPI do support message integrity and confidentiality and, therefore,
- * do need this filter.
- *
- * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
- */
-public class SaslFilter extends IoFilterAdapter
-{
- private static final Logger LOG = LoggerFactory.getLogger( SaslFilter.class );
-
- /**
- * A session attribute key that makes next one write request bypass
- * this filter (not adding a security layer). This is a marker attribute,
- * which means that you can put whatever as its value. ({@link Boolean#TRUE}
- * is preferred.) The attribute is automatically removed from the session
- * attribute map as soon as {@link IoSession#write(Object)} is invoked,
- * and therefore should be put again if you want to make more messages
- * bypass this filter.
- */
- public static final String DISABLE_SECURITY_LAYER_ONCE = SaslFilter.class.getName() + ".DisableSecurityLayerOnce";
-
- private SaslServer saslServer;
-
-
- /**
- * Creates a new instance of SaslFilter. The SaslFilter must be constructed
- * with a SASL context that has completed SASL negotiation. The SASL context
- * will be used to provide message integrity and, optionally, message
- * confidentiality.
- *
- * @param saslServer The initialized SASL context.
- */
- public SaslFilter( SaslServer saslServer )
- {
- if ( saslServer == null )
- {
- throw new IllegalStateException();
- }
-
- this.saslServer = saslServer;
- }
-
-
- @Override
- public void messageReceived( NextFilter nextFilter, IoSession session, Object message ) throws SaslException
- {
- LOG.debug( "Message received: {}", message );
-
- /*
- * Unwrap the data for mechanisms that support QoP (DIGEST-MD5, GSSAPI).
- */
- String qop = ( String ) saslServer.getNegotiatedProperty( Sasl.QOP );
- boolean hasSecurityLayer = ( qop != null && ( qop.equals( SaslQoP.AUTH_INT.getValue() ) || qop
- .equals( SaslQoP.AUTH_CONF.getValue() ) ) );
-
- if ( hasSecurityLayer )
- {
- /*
- * Get the buffer as bytes. First 4 bytes are length as int.
- */
- IoBuffer buf = ( IoBuffer ) message;
- int bufferLength = buf.getInt();
- byte[] bufferBytes = new byte[bufferLength];
- buf.get( bufferBytes );
-
- LOG.debug( "Will use SASL to unwrap received message of length: {}", bufferLength );
- byte[] token = saslServer.unwrap( bufferBytes, 0, bufferBytes.length );
- nextFilter.messageReceived( session, IoBuffer.wrap( token ) );
- }
- else
- {
- LOG.debug( "Will not use SASL on received message." );
- nextFilter.messageReceived( session, message );
- }
- }
-
-
- @Override
- public void filterWrite( NextFilter nextFilter, IoSession session, WriteRequest writeRequest ) throws SaslException
- {
- LOG.debug( "Filtering write request: {}", writeRequest );
-
- /*
- * Check if security layer processing should be disabled once.
- */
- if ( session.containsAttribute( DISABLE_SECURITY_LAYER_ONCE ) )
- {
- // Remove the marker attribute because it is temporary.
- LOG.debug( "Disabling SaslFilter once; will not use SASL on write request." );
- session.removeAttribute( DISABLE_SECURITY_LAYER_ONCE );
- nextFilter.filterWrite( session, writeRequest );
- return;
- }
-
- /*
- * Wrap the data for mechanisms that support QoP (DIGEST-MD5, GSSAPI).
- */
- String qop = ( String ) saslServer.getNegotiatedProperty( Sasl.QOP );
- boolean hasSecurityLayer = ( qop != null && ( qop.equals( SaslQoP.AUTH_INT.getValue() ) || qop
- .equals( SaslQoP.AUTH_CONF.getValue() ) ) );
-
- IoBuffer saslLayerBuffer = null;
-
- if ( hasSecurityLayer )
- {
- /*
- * Get the buffer as bytes.
- */
- IoBuffer buf = ( IoBuffer ) writeRequest.getMessage();
- int bufferLength = buf.remaining();
- byte[] bufferBytes = new byte[bufferLength];
- buf.get( bufferBytes );
-
- LOG.debug( "Will use SASL to wrap message of length: {}", bufferLength );
-
- byte[] saslLayer = saslServer.wrap( bufferBytes, 0, bufferBytes.length );
-
- /*
- * Prepend 4 byte length.
- */
- saslLayerBuffer = IoBuffer.allocate( 4 + saslLayer.length );
- saslLayerBuffer.putInt( saslLayer.length );
- saslLayerBuffer.put( saslLayer );
- saslLayerBuffer.position( 0 );
- saslLayerBuffer.limit( 4 + saslLayer.length );
-
- LOG.debug( "Sending encrypted token of length {}.", saslLayerBuffer.limit() );
- nextFilter.filterWrite( session, new DefaultWriteRequest( saslLayerBuffer, writeRequest.getFuture() ) );
- }
- else
- {
- LOG.debug( "Will not use SASL on write request." );
- nextFilter.filterWrite( session, writeRequest );
- }
- }
-}
diff --git a/server-integ/src/test/java/org/apache/directory/server/operations/bind/SaslBindIT.java b/server-integ/src/test/java/org/apache/directory/server/operations/bind/SaslBindIT.java
index 6e27ec9..043e390 100644
--- a/server-integ/src/test/java/org/apache/directory/server/operations/bind/SaslBindIT.java
+++ b/server-integ/src/test/java/org/apache/directory/server/operations/bind/SaslBindIT.java
@@ -27,6 +27,8 @@ import static org.junit.Assert.fail;
import java.io.File;
import java.lang.reflect.Field;
import java.nio.ByteBuffer;
+import java.util.ArrayList;
+import java.util.List;
import java.util.Locale;
import java.util.Objects;
@@ -36,6 +38,7 @@ import javax.naming.directory.Attributes;
import javax.naming.directory.DirContext;
import javax.naming.directory.InitialDirContext;
+import org.apache.commons.lang3.RandomStringUtils;
import org.apache.commons.net.SocketClient;
import org.apache.directory.api.asn1.util.Asn1Buffer;
import org.apache.directory.api.ldap.codec.api.LdapDecoder;
@@ -45,18 +48,23 @@ import org.apache.directory.api.ldap.model.constants.SaslQoP;
import org.apache.directory.api.ldap.model.constants.SchemaConstants;
import org.apache.directory.api.ldap.model.constants.SupportedSaslMechanisms;
import org.apache.directory.api.ldap.model.entry.DefaultEntry;
+import org.apache.directory.api.ldap.model.entry.DefaultModification;
import org.apache.directory.api.ldap.model.entry.Entry;
+import org.apache.directory.api.ldap.model.entry.ModificationOperation;
import org.apache.directory.api.ldap.model.exception.LdapException;
+import org.apache.directory.api.ldap.model.exception.LdapInvalidAttributeValueException;
import org.apache.directory.api.ldap.model.message.BindRequest;
import org.apache.directory.api.ldap.model.message.BindRequestImpl;
import org.apache.directory.api.ldap.model.message.BindResponse;
import org.apache.directory.api.ldap.model.message.Message;
import org.apache.directory.api.ldap.model.message.ResultCodeEnum;
+import org.apache.directory.api.ldap.model.message.SearchScope;
import org.apache.directory.api.ldap.model.name.Dn;
import org.apache.directory.api.util.Network;
import org.apache.directory.api.util.Strings;
import org.apache.directory.ldap.client.api.LdapConnection;
import org.apache.directory.ldap.client.api.LdapNetworkConnection;
+import org.apache.directory.ldap.client.api.NoVerificationTrustManager;
import org.apache.directory.ldap.client.api.SaslCramMd5Request;
import org.apache.directory.ldap.client.api.SaslDigestMd5Request;
import org.apache.directory.ldap.client.api.SaslGssApiRequest;
@@ -68,9 +76,11 @@ import org.apache.directory.server.core.annotations.ContextEntry;
import org.apache.directory.server.core.annotations.CreateDS;
import org.apache.directory.server.core.annotations.CreateIndex;
import org.apache.directory.server.core.annotations.CreatePartition;
+import org.apache.directory.server.core.annotations.LoadSchema;
import org.apache.directory.server.core.integ.AbstractLdapTestUnit;
import org.apache.directory.server.core.integ.ApacheDSTestExtension;
import org.apache.directory.server.core.kerberos.KeyDerivationInterceptor;
+import org.apache.directory.server.ldap.handlers.extended.StartTlsHandler;
import org.apache.directory.server.ldap.handlers.extended.StoredProcedureExtendedOperationHandler;
import org.apache.directory.server.ldap.handlers.sasl.cramMD5.CramMd5MechanismHandler;
import org.apache.directory.server.ldap.handlers.sasl.digestMD5.DigestMd5MechanismHandler;
@@ -80,7 +90,6 @@ import org.apache.directory.server.ldap.handlers.sasl.plain.PlainMechanismHandle
import org.apache.directory.shared.kerberos.KerberosAttribute;
import org.apache.kerby.kerberos.kdc.impl.NettyKdcServerImpl;
import org.apache.kerby.kerberos.kerb.server.SimpleKdcServer;
-import org.junit.Ignore;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
@@ -88,6 +97,8 @@ import org.junit.jupiter.api.extension.ExtendWith;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import com.nimbusds.jose.util.StandardCharset;
+
/**
* An {@link AbstractServerTest} testing SASL authentication.
@@ -150,6 +161,18 @@ import org.slf4j.LoggerFactory;
@CreateDS(
allowAnonAccess = false,
name = "SaslBindIT-class",
+ loadedSchemas =
+ {
+ @LoadSchema(name = "apachedns", enabled = true),
+ @LoadSchema(name = "autofs", enabled = true),
+ @LoadSchema(name = "corba", enabled = true),
+ @LoadSchema(name = "dhcp", enabled = true),
+ @LoadSchema(name = "mozilla", enabled = true),
+ @LoadSchema(name = "nis", enabled = true),
+ @LoadSchema(name = "posix", enabled = true),
+ @LoadSchema(name = "rfc2307bis", enabled = true),
+ @LoadSchema(name = "samba", enabled = true)
+ },
partitions =
{
@CreatePartition(
@@ -173,8 +196,9 @@ import org.slf4j.LoggerFactory;
{ KeyDerivationInterceptor.class })
@CreateLdapServer(transports =
{
- @CreateTransport(protocol = "LDAP")
-},
+ @CreateTransport(protocol = "LDAP"),
+ @CreateTransport(protocol = "LDAPS")
+ },
saslHost = "localhost",
saslPrincipal = "ldap/localhost@EXAMPLE.COM",
saslMechanisms =
@@ -186,18 +210,25 @@ import org.slf4j.LoggerFactory;
@SaslMechanism(name = SupportedSaslMechanisms.NTLM, implClass = NtlmMechanismHandler.class),
@SaslMechanism(name = SupportedSaslMechanisms.GSS_SPNEGO, implClass = NtlmMechanismHandler.class)
},
+ maxSizeLimit = 100,
extendedOpHandlers =
{
+ StartTlsHandler.class,
StoredProcedureExtendedOperationHandler.class
},
ntlmProvider = BogusNtlmProvider.class)
public class SaslBindIT extends AbstractLdapTestUnit
{
+
+ private Dn userDn;
+
@BeforeEach
public void init() throws Exception
{
+ ldapServer.setConfidentialityRequired( false );
KerberosTestUtils.fixServicePrincipalName( "ldap/" + Network.LOOPBACK_HOSTNAME + "@EXAMPLE.COM",
new Dn( "uid=ldap,ou=users,dc=example,dc=com" ), getLdapServer() );
+ userDn = new Dn( "uid=hnelson,ou=users,dc=example,dc=com" );
}
@@ -241,7 +272,6 @@ public class SaslBindIT extends AbstractLdapTestUnit
BindResponse resp = connection.bindSaslPlain( "hnelson", "secret" );
assertEquals( ResultCodeEnum.SUCCESS, resp.getLdapResult().getResultCode() );
- Dn userDn = new Dn( "uid=hnelson,ou=users,dc=example,dc=com" );
Entry entry = connection.lookup( userDn );
assertEquals( "hnelson", entry.get( "uid" ).getString() );
@@ -264,7 +294,6 @@ public class SaslBindIT extends AbstractLdapTestUnit
@Disabled("Activate and fix when DIRAPI-36 (Provide a SaslBindRequest extending BindRequest that can be used in LdapConnection.bind(...) method) is solved")
public void testSaslBindNoMech() throws Exception
{
- Dn userDn = new Dn( "uid=hnelson,ou=users,dc=example,dc=com" );
LdapConnection connection = new LdapNetworkConnection( Network.LOOPBACK_HOSTNAME, getLdapServer().getPort() );
BindRequest bindReq = new BindRequestImpl();
bindReq.setCredentials( "secret" );
@@ -292,7 +321,6 @@ public class SaslBindIT extends AbstractLdapTestUnit
@Test
public void testSaslCramMd5Bind() throws Exception
{
- Dn userDn = new Dn( "uid=hnelson,ou=users,dc=example,dc=com" );
LdapNetworkConnection connection = new LdapNetworkConnection( Network.LOOPBACK_HOSTNAME, getLdapServer().getPort() );
SaslCramMd5Request request = new SaslCramMd5Request();
@@ -315,7 +343,6 @@ public class SaslBindIT extends AbstractLdapTestUnit
@Test
public void testSaslCramMd5BindBadPassword() throws Exception
{
- Dn userDn = new Dn( "uid=hnelson,ou=users,dc=example,dc=com" );
LdapNetworkConnection connection = new LdapNetworkConnection( Network.LOOPBACK_HOSTNAME, getLdapServer().getPort() );
SaslCramMd5Request request = new SaslCramMd5Request();
@@ -329,18 +356,20 @@ public class SaslBindIT extends AbstractLdapTestUnit
/**
- * Tests to make sure DIGEST-MD5 binds below the RootDSE work.
+ * Tests to make sure DIGEST-MD5 binds below the RootDSE work with
+ * SASL Quality of Protection set to 'auth'.
*/
@Test
- public void testSaslDigestMd5Bind() throws Exception
+ public void testSaslDigestMd5BindSaslQoPAuth() throws Exception
{
- Dn userDn = new Dn( "uid=hnelson,ou=users,dc=example,dc=com" );
- LdapNetworkConnection connection = new LdapNetworkConnection( Network.LOOPBACK_HOSTNAME, getLdapServer().getPort() );
+ LdapNetworkConnection connection = new LdapNetworkConnection( Network.LOOPBACK_HOSTNAME,
+ getLdapServer().getPort() );
SaslDigestMd5Request request = new SaslDigestMd5Request();
request.setUsername( userDn.getRdn().getValue() );
request.setCredentials( "secret" );
request.setRealmName( ldapServer.getSaslRealms().get( 0 ) );
+ request.setQualityOfProtection( SaslQoP.AUTH );
BindResponse resp = connection.bind( request );
assertEquals( ResultCodeEnum.SUCCESS, resp.getLdapResult().getResultCode() );
@@ -353,19 +382,19 @@ public class SaslBindIT extends AbstractLdapTestUnit
/**
* Tests to make sure DIGEST-MD5 binds below the RootDSE work with
- * SASL Quality of Protection set to 'auth'.
+ * SASL Quality of Protection set to 'auth' over ldaps://.
*/
@Test
- public void testSaslDigestMd5BindSaslQoPAuth() throws Exception
+ public void testSaslDigestMd5BindSaslQoPAuthOverLdaps() throws Exception
{
- Dn userDn = new Dn( "uid=hnelson,ou=users,dc=example,dc=com" );
- LdapNetworkConnection connection = new LdapNetworkConnection( Network.LOOPBACK_HOSTNAME, getLdapServer().getPort() );
+ LdapNetworkConnection connection = new LdapNetworkConnection( Network.LOOPBACK_HOSTNAME,
+ getLdapServer().getPortSSL(), true );
+ connection.getConfig().setTrustManagers( new NoVerificationTrustManager() );
SaslDigestMd5Request request = new SaslDigestMd5Request();
request.setUsername( userDn.getRdn().getValue() );
request.setCredentials( "secret" );
request.setRealmName( ldapServer.getSaslRealms().get( 0 ) );
- request.setQualityOfProtection( SaslQoP.AUTH );
BindResponse resp = connection.bind( request );
assertEquals( ResultCodeEnum.SUCCESS, resp.getLdapResult().getResultCode() );
@@ -381,24 +410,96 @@ public class SaslBindIT extends AbstractLdapTestUnit
* SASL Quality of Protection set to 'auth-int'.
*/
@Test
- @Disabled
public void testSaslDigestMd5BindSaslQoPAuthInt() throws Exception
{
- Dn userDn = new Dn( "uid=hnelson,ou=users,dc=example,dc=com" );
- LdapNetworkConnection connection = new LdapNetworkConnection( Network.LOOPBACK_HOSTNAME, getLdapServer().getPort() );
+ LdapNetworkConnection connection = new LdapNetworkConnection( Network.LOOPBACK_HOSTNAME,
+ getLdapServer().getPort() );
- SaslDigestMd5Request request = new SaslDigestMd5Request();
- request.setUsername( userDn.getRdn().getValue() );
- request.setCredentials( "secret" );
- request.setRealmName( ldapServer.getSaslRealms().get( 0 ) );
- request.setQualityOfProtection( SaslQoP.AUTH_INT );
- BindResponse resp = connection.bind( request );
- assertEquals( ResultCodeEnum.SUCCESS, resp.getLdapResult().getResultCode() );
+ // Repeat SASL bind to test proper replacement of the SASL filter
+ for ( int i = 0; i < 3; i++ )
+ {
+ SaslDigestMd5Request request = new SaslDigestMd5Request();
+ request.setUsername( userDn.getRdn().getValue() );
+ request.setCredentials( "secret" );
+ request.setRealmName( ldapServer.getSaslRealms().get( 0 ) );
+ request.setQualityOfProtection( SaslQoP.AUTH_INT );
+ BindResponse resp = connection.bind( request );
+ assertEquals( ResultCodeEnum.SUCCESS, resp.getLdapResult().getResultCode() );
- Entry entry = connection.lookup( userDn );
- assertEquals( "hnelson", entry.get( "uid" ).getString() );
+ Entry entry = connection.lookup( userDn );
+ assertEquals( "hnelson", entry.get( "uid" ).getString() );
- connection.close();
+ testSaslFilter( connection );
+
+ connection.close();
+ }
+ }
+
+
+ /**
+ * Tests to make sure DIGEST-MD5 binds below the RootDSE work with
+ * SASL Quality of Protection set to 'auth-int' over ldaps://.
+ */
+ @Test
+ public void testSaslDigestMd5BindSaslQoPAuthIntOverLdaps() throws Exception
+ {
+ LdapNetworkConnection connection = new LdapNetworkConnection( Network.LOOPBACK_HOSTNAME,
+ getLdapServer().getPortSSL(), true );
+ connection.getConfig().setTrustManagers( new NoVerificationTrustManager() );
+
+ // Repeat SASL bind to test proper replacement of the SASL filter
+ for ( int i = 0; i < 3; i++ )
+ {
+ SaslDigestMd5Request request = new SaslDigestMd5Request();
+ request.setUsername( userDn.getRdn().getValue() );
+ request.setCredentials( "secret" );
+ request.setRealmName( ldapServer.getSaslRealms().get( 0 ) );
+ request.setQualityOfProtection( SaslQoP.AUTH_INT );
+ BindResponse resp = connection.bind( request );
+ assertEquals( ResultCodeEnum.SUCCESS, resp.getLdapResult().getResultCode() );
+
+ Entry entry = connection.lookup( userDn );
+ assertEquals( "hnelson", entry.get( "uid" ).getString() );
+
+ testSaslFilter( connection );
+
+ connection.close();
+ }
+ }
+
+
+ /**
+ * Tests to make sure DIGEST-MD5 binds below the RootDSE work with
+ * SASL Quality of Protection set to 'auth-int' over StartTLS
+ */
+ @Test
+ public void testSaslDigestMd5BindSaslQoPAuthIntOverStartTLS() throws Exception
+ {
+ ldapServer.setConfidentialityRequired( true );
+ LdapNetworkConnection connection = new LdapNetworkConnection( Network.LOOPBACK_HOSTNAME,
+ getLdapServer().getPort() );
+ connection.getConfig().setTrustManagers( new NoVerificationTrustManager() );
+
+ // Repeat SASL bind to test proper replacement of the SASL filter
+ for ( int i = 0; i < 3; i++ )
+ {
+ connection.startTls();
+
+ SaslDigestMd5Request request = new SaslDigestMd5Request();
+ request.setUsername( userDn.getRdn().getValue() );
+ request.setCredentials( "secret" );
+ request.setRealmName( ldapServer.getSaslRealms().get( 0 ) );
+ request.setQualityOfProtection( SaslQoP.AUTH_INT );
+ BindResponse resp = connection.bind( request );
+ assertEquals( ResultCodeEnum.SUCCESS, resp.getLdapResult().getResultCode() );
+
+ Entry entry = connection.lookup( userDn );
+ assertEquals( "hnelson", entry.get( "uid" ).getString() );
+
+ testSaslFilter( connection );
+
+ connection.close();
+ }
}
@@ -407,24 +508,128 @@ public class SaslBindIT extends AbstractLdapTestUnit
* SASL Quality of Protection set to 'auth-conf'.
*/
@Test
- @Disabled
public void testSaslDigestMd5BindSaslQoPAuthConf() throws Exception
{
- Dn userDn = new Dn( "uid=hnelson,ou=users,dc=example,dc=com" );
- LdapNetworkConnection connection = new LdapNetworkConnection( Network.LOOPBACK_HOSTNAME, getLdapServer().getPort() );
+ LdapNetworkConnection connection = new LdapNetworkConnection( Network.LOOPBACK_HOSTNAME,
+ getLdapServer().getPort() );
- SaslDigestMd5Request request = new SaslDigestMd5Request();
- request.setUsername( userDn.getRdn().getValue() );
- request.setCredentials( "secret" );
- request.setRealmName( ldapServer.getSaslRealms().get( 0 ) );
- request.setQualityOfProtection( SaslQoP.AUTH_CONF );
- BindResponse resp = connection.bind( request );
- assertEquals( ResultCodeEnum.SUCCESS, resp.getLdapResult().getResultCode() );
+ // Repeat SASL bind to test proper replacement of the SASL filter
+ for ( int i = 0; i < 3; i++ )
+ {
+ SaslDigestMd5Request request = new SaslDigestMd5Request();
+ request.setUsername( userDn.getRdn().getValue() );
+ request.setCredentials( "secret" );
+ request.setRealmName( ldapServer.getSaslRealms().get( 0 ) );
+ request.setQualityOfProtection( SaslQoP.AUTH_CONF );
+ BindResponse resp = connection.bind( request );
+ assertEquals( ResultCodeEnum.SUCCESS, resp.getLdapResult().getResultCode() );
- Entry entry = connection.lookup( userDn );
- assertEquals( "hnelson", entry.get( "uid" ).getString() );
+ testSaslFilter( connection );
- connection.close();
+ connection.close();
+ }
+ }
+
+
+ /**
+ * Tests to make sure DIGEST-MD5 binds below the RootDSE work with
+ * SASL Quality of Protection set to 'auth-conf' over ldaps://.
+ */
+ @Test
+ public void testSaslDigestMd5BindSaslQoPAuthConfOverLdaps() throws Exception
+ {
+ ldapServer.setConfidentialityRequired( true );
+ LdapNetworkConnection connection = new LdapNetworkConnection( Network.LOOPBACK_HOSTNAME,
+ getLdapServer().getPortSSL(), true );
+ connection.getConfig().setTrustManagers( new NoVerificationTrustManager() );
+
+ // Repeat SASL bind to test proper replacement of the SASL filter
+ for ( int i = 0; i < 3; i++ )
+ {
+ SaslDigestMd5Request request = new SaslDigestMd5Request();
+ request.setUsername( userDn.getRdn().getValue() );
+ request.setCredentials( "secret" );
+ request.setRealmName( ldapServer.getSaslRealms().get( 0 ) );
+ request.setQualityOfProtection( SaslQoP.AUTH_CONF );
+ BindResponse resp = connection.bind( request );
+ assertEquals( ResultCodeEnum.SUCCESS, resp.getLdapResult().getResultCode() );
+
+ testSaslFilter( connection );
+
+ connection.close();
+ }
+ }
+
+
+ /**
+ * Tests to make sure DIGEST-MD5 binds below the RootDSE work with
+ * SASL Quality of Protection set to 'auth-conf' over StartTLS.
+ */
+ @Test
+ public void testSaslDigestMd5BindSaslQoPAuthConfOverStartTLS() throws Exception
+ {
+ ldapServer.setConfidentialityRequired( true );
+ LdapNetworkConnection connection = new LdapNetworkConnection( Network.LOOPBACK_HOSTNAME,
+ getLdapServer().getPort() );
+ connection.getConfig().setTrustManagers( new NoVerificationTrustManager() );
+
+ // Repeat SASL bind to test proper replacement of the SASL filter
+ for ( int i = 0; i < 3; i++ )
+ {
+ connection.startTls();
+
+ SaslDigestMd5Request request = new SaslDigestMd5Request();
+ request.setUsername( userDn.getRdn().getValue() );
+ request.setCredentials( "secret" );
+ request.setRealmName( ldapServer.getSaslRealms().get( 0 ) );
+ request.setQualityOfProtection( SaslQoP.AUTH_CONF );
+ BindResponse resp = connection.bind( request );
+ assertEquals( ResultCodeEnum.SUCCESS, resp.getLdapResult().getResultCode() );
+
+ testSaslFilter( connection );
+
+ connection.close();
+ }
+ }
+
+
+ /**
+ * Run various search and modify operations with small and large payloads
+ * to test proper SASL message splitting and re-assembling.
+ */
+ private void testSaslFilter( LdapNetworkConnection connection )
+ throws LdapException, LdapInvalidAttributeValueException
+ {
+ // lookup Root DSE
+ assertTrue( connection.lookup( "", "+", "*" ).containsAttribute( "namingContexts" ) );
+
+ // lookup cn=schema with all schemas enabled which is larger than the SASL max buffer size
+ Entry lookup = connection.lookup( "cn=schema", "+", "*" );
+ assertTrue( lookup.containsAttribute( "objectClasses", "attributeTypes" ) );
+
+ // subtree search which returns 100 entries
+ List<Entry> entries = new ArrayList<>();
+ for ( Entry e : connection.search( "ou=schema", "(objectClass=*)", SearchScope.SUBTREE, "+", "*" ) )
+ {
+ entries.add( e );
+ }
+ assertEquals( 100, entries.size() );
+
+ // do a small modification
+ connection.modify( userDn,
+ new DefaultModification( ModificationOperation.REPLACE_ATTRIBUTE, "description", "test" ) );
+
+ // do a medium size modification
+ String largeString = RandomStringUtils.randomAscii( 10000 );
+ connection.modify( userDn,
+ new DefaultModification( ModificationOperation.REPLACE_ATTRIBUTE, "description", largeString ) );
+ assertEquals( largeString, connection.lookup( userDn ).get( "description" ).getString() );
+
+ // do a large modification
+ byte[] largeBytes = RandomStringUtils.random( 500_000 ).getBytes( StandardCharset.UTF_8 );
+ connection.modify( userDn,
+ new DefaultModification( ModificationOperation.REPLACE_ATTRIBUTE, "userCertificate", largeBytes ) );
+ assertEquals( largeBytes.length, connection.lookup( userDn ).get( "userCertificate" ).getBytes().length );
}
@@ -434,7 +639,6 @@ public class SaslBindIT extends AbstractLdapTestUnit
@Test
public void testSaslDigestMd5BindBadRealm() throws Exception
{
- Dn userDn = new Dn( "uid=hnelson,ou=users,dc=example,dc=com" );
LdapNetworkConnection connection = new LdapNetworkConnection( Network.LOOPBACK_HOSTNAME, getLdapServer().getPort() );
SaslDigestMd5Request request = new SaslDigestMd5Request();
@@ -454,7 +658,6 @@ public class SaslBindIT extends AbstractLdapTestUnit
@Test
public void testSaslDigestMd5BindBadPassword() throws Exception
{
- Dn userDn = new Dn( "uid=hnelson,ou=users,dc=example,dc=com" );
LdapNetworkConnection connection = new LdapNetworkConnection( Network.LOOPBACK_HOSTNAME, getLdapServer().getPort() );
SaslDigestMd5Request request = new SaslDigestMd5Request();
@@ -495,7 +698,6 @@ public class SaslBindIT extends AbstractLdapTestUnit
kerbyServer.createPrincipal( ldap, "randall" );
kerbyServer.start();
- Dn userDn = new Dn( "uid=hnelson,ou=users,dc=example,dc=com" );
LdapNetworkConnection connection = new LdapNetworkConnection( Network.LOOPBACK_HOSTNAME, getLdapServer().getPort() );
//kdcServer.getConfig().setPaEncTimestampRequired( false );
@@ -517,13 +719,72 @@ public class SaslBindIT extends AbstractLdapTestUnit
kerbyServer.stop();
}
+
+ @Test
+ public void testSaslGssApiBindSaslQoPAuthConfOverStartTLS() throws Exception
+ {
+ SimpleKdcServer kerbyServer = new SimpleKdcServer();
+
+ String basedir = System.getProperty( "basedir" );
+ if ( basedir == null )
+ {
+ basedir = new File( "." ).getCanonicalPath();
+ }
+
+ kerbyServer.setKdcRealm( "EXAMPLE.COM" );
+ kerbyServer.setAllowUdp( true );
+ kerbyServer.setWorkDir( new File( basedir + "/target" ) );
+
+ kerbyServer.setInnerKdcImpl( new NettyKdcServerImpl( kerbyServer.getKdcSetting() ) );
+ kerbyServer.init();
+
+ // Create principals
+ String hnelson = "hnelson@EXAMPLE.COM";
+ String ldap = "ldap/" + Network.LOOPBACK_HOSTNAME + "@EXAMPLE.COM";
+ kerbyServer.createPrincipal( hnelson, "secret" );
+ kerbyServer.createPrincipal( ldap, "randall" );
+ kerbyServer.start();
+
+ //kdcServer.getConfig().setPaEncTimestampRequired( false );
+
+ ldapServer.setConfidentialityRequired( true );
+ LdapNetworkConnection connection = new LdapNetworkConnection( Network.LOOPBACK_HOSTNAME,
+ getLdapServer().getPort() );
+ connection.getConfig().setTrustManagers( new NoVerificationTrustManager() );
+
+ // Repeat SASL bind to test proper replacement of the SASL filter
+ for ( int i = 0; i < 3; i++ )
+ {
+ connection.startTls();
+
+ SaslGssApiRequest request = new SaslGssApiRequest();
+ request.setUsername( userDn.getRdn().getValue() );
+ request.setCredentials( "secret" );
+ request.setRealmName( ldapServer.getSaslRealms().get( 0 ).toUpperCase( Locale.ROOT ) );
+ request.setKdcHost( Network.LOOPBACK_HOSTNAME );
+ request.setKdcPort( kerbyServer.getKdcPort() );
+ request.setQualityOfProtection( SaslQoP.AUTH_CONF );
+ BindResponse resp = connection.bind( request );
+ assertEquals( ResultCodeEnum.SUCCESS, resp.getLdapResult().getResultCode() );
+
+ Entry entry = connection.lookup( userDn );
+ assertEquals( "hnelson", entry.get( "uid" ).getString() );
+
+ testSaslFilter( connection );
+
+ connection.close();
+ }
+
+ kerbyServer.stop();
+ }
+
+
/**
* Tests to make sure GSS-API binds below the RootDSE fail if the realm is bad.
*/
@Test
public void testSaslGssApiBindBadRealm() throws Exception
{
- Dn userDn = new Dn( "uid=hnelson,ou=users,dc=example,dc=com" );
LdapNetworkConnection connection = new LdapNetworkConnection( Network.LOOPBACK_HOSTNAME, ldapServer.getPort() );
SaslGssApiRequest request = new SaslGssApiRequest();
@@ -553,7 +814,6 @@ public class SaslBindIT extends AbstractLdapTestUnit
@Test
public void testSaslGssApiBindBadPassword() throws Exception
{
- Dn userDn = new Dn( "uid=hnelson,ou=users,dc=example,dc=com" );
LdapNetworkConnection connection = new LdapNetworkConnection( Network.LOOPBACK_HOSTNAME, ldapServer.getPort() );
SaslGssApiRequest request = new SaslGssApiRequest();
@@ -642,7 +902,6 @@ public class SaslBindIT extends AbstractLdapTestUnit
LdapNetworkConnection connection;
BindResponse resp;
Entry entry;
- Dn userDn = new Dn( "uid=hnelson,ou=users,dc=example,dc=com" );
for ( int i = 0; i < 1000; i++ )
{