You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@directory.apache.org by co...@apache.org on 2020/09/08 08:49:25 UTC
[directory-server] branch master updated: DIRSERVER-2327 Make the
ReplayCache implementation configurable (#38)
This is an automated email from the ASF dual-hosted git repository.
coheigea pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/directory-server.git
The following commit(s) were added to refs/heads/master by this push:
new 11a901f DIRSERVER-2327 Make the ReplayCache implementation configurable (#38)
11a901f is described below
commit 11a901f880195b3faaf5f9a7c3352b8545c6bd38
Author: Josef Cacek <jo...@gmail.com>
AuthorDate: Tue Sep 8 10:49:16 2020 +0200
DIRSERVER-2327 Make the ReplayCache implementation configurable (#38)
---
.../directory/server/kerberos/KerberosConfig.java | 20 +++
.../directory/server/kerberos/kdc/KdcServer.java | 39 ++++-
.../kerberos/protocol/TGSReplayCacheTest.java | 193 +++++++++++++++++++++
.../server/annotations/CreateKdcServer.java | 5 +
.../server/factory/ServerAnnotationProcessor.java | 1 +
.../factory/CreateKdcServerAnnotationTest.java | 35 +++-
6 files changed, 287 insertions(+), 6 deletions(-)
diff --git a/protocol-kerberos/src/main/java/org/apache/directory/server/kerberos/KerberosConfig.java b/protocol-kerberos/src/main/java/org/apache/directory/server/kerberos/KerberosConfig.java
index 5237d9c..b3e2e9a 100644
--- a/protocol-kerberos/src/main/java/org/apache/directory/server/kerberos/KerberosConfig.java
+++ b/protocol-kerberos/src/main/java/org/apache/directory/server/kerberos/KerberosConfig.java
@@ -26,6 +26,8 @@ import java.util.Set;
import javax.security.auth.kerberos.KerberosPrincipal;
import org.apache.directory.server.constants.ServerDNConstants;
+import org.apache.directory.server.kerberos.shared.replay.ReplayCache;
+import org.apache.directory.server.kerberos.shared.replay.ReplayCacheImpl;
import org.apache.directory.shared.kerberos.KerberosUtils;
import org.apache.directory.shared.kerberos.codec.types.EncryptionType;
import org.apache.directory.shared.kerberos.codec.types.PrincipalNameType;
@@ -80,6 +82,9 @@ public class KerberosConfig
public static final String[] DEFAULT_ENCRYPTION_TYPES = new String[]
{ "aes128-cts-hmac-sha1-96", "des-cbc-md5", "des3-cbc-sha1-kd" };
+ /** The default ReplayCache type */
+ public static final Class<? extends ReplayCache> DEFAULT_REPLAY_CACHE_TYPE = ReplayCacheImpl.class;
+
/** The primary realm */
private String primaryRealm = KerberosConfig.DEFAULT_REALM;
@@ -119,6 +124,9 @@ public class KerberosConfig
/** Whether to verify the body checksum. */
private boolean isBodyChecksumVerified = KerberosConfig.DEFAULT_VERIFY_BODY_CHECKSUM;
+ /** Replay cache implementing class. */
+ private Class<? extends ReplayCache> replayCacheType = KerberosConfig.DEFAULT_REPLAY_CACHE_TYPE;
+
/** The encryption types. */
private Set<EncryptionType> encryptionTypes;
@@ -419,6 +427,18 @@ public class KerberosConfig
}
+ public Class<? extends ReplayCache> getReplayCacheType()
+ {
+ return replayCacheType;
+ }
+
+
+ public void setReplayCacheType( Class<? extends ReplayCache> replayCacheType )
+ {
+ this.replayCacheType = replayCacheType;
+ }
+
+
public long getMinimumTicketLifetime()
{
return minimumTicketLifetime;
diff --git a/protocol-kerberos/src/main/java/org/apache/directory/server/kerberos/kdc/KdcServer.java b/protocol-kerberos/src/main/java/org/apache/directory/server/kerberos/kdc/KdcServer.java
index cd919da..c2a3696 100644
--- a/protocol-kerberos/src/main/java/org/apache/directory/server/kerberos/kdc/KdcServer.java
+++ b/protocol-kerberos/src/main/java/org/apache/directory/server/kerberos/kdc/KdcServer.java
@@ -29,7 +29,6 @@ import org.apache.directory.server.kerberos.changepwd.ChangePasswordServer;
import org.apache.directory.server.kerberos.protocol.KerberosProtocolHandler;
import org.apache.directory.server.kerberos.protocol.codec.KerberosProtocolCodecFactory;
import org.apache.directory.server.kerberos.shared.replay.ReplayCache;
-import org.apache.directory.server.kerberos.shared.replay.ReplayCacheImpl;
import org.apache.directory.server.kerberos.shared.store.PrincipalStore;
import org.apache.directory.server.protocol.shared.DirectoryBackedService;
import org.apache.directory.server.protocol.shared.transport.TcpTransport;
@@ -89,6 +88,8 @@ public class KdcServer extends DirectoryBackedService
this.config = config;
super.setServiceName( SERVICE_NAME );
super.setSearchBaseDn( config.getSearchBaseDn() );
+
+ initReplayCache();
}
@@ -110,10 +111,6 @@ public class KdcServer extends DirectoryBackedService
store = new DirectoryPrincipalStore( getDirectoryService(), new Dn( this.getSearchBaseDn() ) );
- LOG.debug( "initializing the kerberos replay cache" );
-
- replayCache = new ReplayCacheImpl( config.getAllowableClockSkew() );
-
// Kerberos can use UDP or TCP
for ( Transport transport : transports )
{
@@ -255,4 +252,36 @@ public class KdcServer extends DirectoryBackedService
return sb.toString();
}
+
+ private void initReplayCache()
+ {
+ LOG.debug( "initializing the kerberos replay cache" );
+
+ Class<? extends ReplayCache> clazz = config.getReplayCacheType();
+ if ( clazz == null )
+ {
+ LOG.trace( "Kerberos replay cache is disabled" );
+ return;
+ }
+
+ LOG.debug( "Creating ReplayCache of type {}", clazz.getName() );
+ ReplayCache instance = null;
+ try
+ {
+ try
+ {
+ instance = clazz.getConstructor( Long.TYPE ).newInstance( config.getAllowableClockSkew() );
+ }
+ catch ( NoSuchMethodException e )
+ {
+ instance = clazz.newInstance();
+ }
+ }
+ catch ( Exception e )
+ {
+ LOG.error( "Failed to create the ReplayCache instance. No replay cache will be used!", e );
+ }
+ replayCache = instance;
+ }
+
}
diff --git a/protocol-kerberos/src/test/java/org/apache/directory/server/kerberos/protocol/TGSReplayCacheTest.java b/protocol-kerberos/src/test/java/org/apache/directory/server/kerberos/protocol/TGSReplayCacheTest.java
new file mode 100644
index 0000000..2273068
--- /dev/null
+++ b/protocol-kerberos/src/test/java/org/apache/directory/server/kerberos/protocol/TGSReplayCacheTest.java
@@ -0,0 +1,193 @@
+/*
+ * 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.kerberos.protocol;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import java.text.ParseException;
+
+import javax.security.auth.kerberos.KerberosPrincipal;
+
+import org.apache.directory.server.kerberos.KerberosConfig;
+import org.apache.directory.server.kerberos.kdc.KdcServer;
+import org.apache.directory.server.kerberos.protocol.AbstractAuthenticationServiceTest.KrbDummySession;
+import org.apache.directory.server.kerberos.shared.crypto.encryption.CipherTextHandler;
+import org.apache.directory.server.kerberos.shared.replay.ReplayCache;
+import org.apache.directory.shared.kerberos.KerberosTime;
+import org.apache.directory.shared.kerberos.codec.options.KdcOptions;
+import org.apache.directory.shared.kerberos.components.EncTicketPart;
+import org.apache.directory.shared.kerberos.components.EncryptionKey;
+import org.apache.directory.shared.kerberos.components.KdcReq;
+import org.apache.directory.shared.kerberos.components.KdcReqBody;
+import org.apache.directory.shared.kerberos.exceptions.ErrorType;
+import org.apache.directory.shared.kerberos.exceptions.KerberosException;
+import org.apache.directory.shared.kerberos.messages.KrbError;
+import org.apache.directory.shared.kerberos.messages.TgsRep;
+import org.apache.directory.shared.kerberos.messages.Ticket;
+import org.junit.After;
+import org.junit.Test;
+
+/**
+ * Tests for configurable {@link ReplayCache}.
+ */
+public class TGSReplayCacheTest extends AbstractTicketGrantingServiceTest
+{
+ private KdcServer kdcServer;
+ private KerberosProtocolHandler handler;
+
+ /**
+ * Shutdown the Kerberos server
+ */
+ @After
+ public void shutDown()
+ {
+ kdcServer.stop();
+ }
+
+ /**
+ * Tests the replay cache is used by default.
+ */
+ @Test
+ public void testDefaultReplayCache() throws Exception
+ {
+ initKdcServer( new KerberosConfig() );
+
+ KdcReq message = createTgsRequest();
+
+ KrbDummySession session = new KrbDummySession();
+ handler.messageReceived( session, message );
+ assertEquals( "session.getMessage() instanceOf", TgsRep.class, session.getMessage().getClass() );
+
+ handler.messageReceived( session, message );
+ Object msg = session.getMessage();
+ assertEquals( "session.getMessage() instanceOf", KrbError.class, msg.getClass() );
+ KrbError error = (KrbError) msg;
+ assertEquals( "Replay not detected", ErrorType.KRB_AP_ERR_REPEAT, error.getErrorCode() );
+ }
+
+ /**
+ * Tests the replay cache is not used if the type is explicitly set to null.
+ */
+ @Test
+ public void testNullReplayCacheType() throws Exception
+ {
+ KerberosConfig config = new KerberosConfig();
+ config.setReplayCacheType( null );
+ initKdcServer( config );
+
+ KdcReq message = createTgsRequest();
+
+ KrbDummySession session = new KrbDummySession();
+ handler.messageReceived( session, message );
+ assertEquals( "session.getMessage() instanceOf", TgsRep.class, session.getMessage().getClass() );
+
+ handler.messageReceived( session, message );
+ assertEquals( "session.getMessage() instanceOf", TgsRep.class, session.getMessage().getClass() );
+ }
+
+ /**
+ * Tests that custom replay cache can be set.
+ */
+ @Test
+ public void testDummyReplayCacheType() throws Exception
+ {
+ KerberosConfig config = new KerberosConfig();
+ config.setReplayCacheType( DisabledReplayCache.class );
+ initKdcServer( config );
+
+ KdcReq message = createTgsRequest();
+
+ KrbDummySession session = new KrbDummySession();
+ handler.messageReceived( session, message );
+ assertEquals( "session.getMessage() instanceOf", TgsRep.class, session.getMessage().getClass() );
+
+ handler.messageReceived( session, message );
+ assertEquals( "session.getMessage() instanceOf", TgsRep.class, session.getMessage().getClass() );
+
+ assertTrue( "Incorrect cache implementation", DisabledReplayCache.replayTested );
+ }
+
+ private KdcReq createTgsRequest() throws KerberosException, ParseException, Exception
+ {
+ // Get the mutable ticket part.
+ KerberosPrincipal clientPrincipal = new KerberosPrincipal( "hnelson@EXAMPLE.COM" );
+ EncTicketPart encTicketPart = getTicketArchetype( clientPrincipal );
+
+ // Seal the ticket for the server.
+ KerberosPrincipal serverPrincipal = new KerberosPrincipal( "krbtgt/EXAMPLE.COM@EXAMPLE.COM" );
+ String passPhrase = "randomKey";
+ EncryptionKey serverKey = getEncryptionKey( serverPrincipal, passPhrase );
+ Ticket tgt = getTicket( encTicketPart, serverPrincipal, serverKey );
+
+ KdcReqBody kdcReqBody = new KdcReqBody();
+ kdcReqBody.setSName( getPrincipalName( "ldap/ldap.example.com@EXAMPLE.COM" ) );
+ kdcReqBody.setRealm( "EXAMPLE.COM" );
+ kdcReqBody.setEType( kdcServer.getConfig().getEncryptionTypes() );
+ kdcReqBody.setNonce( random.nextInt() );
+ kdcReqBody.setKdcOptions( new KdcOptions() );
+
+ long now = System.currentTimeMillis();
+
+ KerberosTime requestedEndTime = new KerberosTime( now + 1 * KerberosTime.DAY );
+ kdcReqBody.setTill( requestedEndTime );
+
+ KdcReq message = getKdcRequest( tgt, kdcReqBody );
+ return message;
+ }
+
+ /**
+ * Creates a new instance of {@link KdcServer}.
+ */
+ private void initKdcServer( KerberosConfig config )
+ {
+ config.setBodyChecksumVerified( false );
+ kdcServer = new KdcServer( config );
+ handler = new KerberosProtocolHandler( kdcServer, new MapPrincipalStoreImpl() );
+ lockBox = new CipherTextHandler();
+ }
+
+ public static class DisabledReplayCache implements ReplayCache
+ {
+
+ public static volatile boolean replayTested = false;
+
+ @Override
+ public boolean isReplay(KerberosPrincipal serverPrincipal, KerberosPrincipal clientPrincipal, KerberosTime clientTime,
+ int clientMicroSeconds)
+ {
+ replayTested = true;
+ return false;
+ }
+
+ @Override
+ public void save(KerberosPrincipal serverPrincipal, KerberosPrincipal clientPrincipal, KerberosTime clientTime,
+ int clientMicroSeconds)
+ {
+ }
+
+ @Override
+ public void clear()
+ {
+ }
+
+ }
+
+}
diff --git a/server-annotations/src/main/java/org/apache/directory/server/annotations/CreateKdcServer.java b/server-annotations/src/main/java/org/apache/directory/server/annotations/CreateKdcServer.java
index 86c6fb7..eb40d63 100644
--- a/server-annotations/src/main/java/org/apache/directory/server/annotations/CreateKdcServer.java
+++ b/server-annotations/src/main/java/org/apache/directory/server/annotations/CreateKdcServer.java
@@ -28,6 +28,8 @@ import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.apache.directory.server.constants.ServerDNConstants;
+import org.apache.directory.server.kerberos.shared.replay.ReplayCache;
+import org.apache.directory.server.kerberos.shared.replay.ReplayCacheImpl;
/**
@@ -83,4 +85,7 @@ public @interface CreateKdcServer
/** @return the DN of the search base for finding users and services */
String searchBaseDn() default ServerDNConstants.USER_EXAMPLE_COM_DN;
+
+ /** @return the replay cache implementing class */
+ Class<? extends ReplayCache> replayCacheType() default ReplayCacheImpl.class;
}
\ No newline at end of file
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 be226c6..70f736e 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
@@ -408,6 +408,7 @@ public final class ServerAnnotationProcessor
kdcConfig.setPrimaryRealm( createKdcServer.primaryRealm() );
kdcConfig.setMaximumTicketLifetime( createKdcServer.maxTicketLifetime() );
kdcConfig.setMaximumRenewableLifetime( createKdcServer.maxRenewableLifetime() );
+ kdcConfig.setReplayCacheType( createKdcServer.replayCacheType() );
KdcServer kdcServer = new KdcServer( kdcConfig );
diff --git a/server-annotations/src/test/java/org/apache/directory/server/factory/CreateKdcServerAnnotationTest.java b/server-annotations/src/test/java/org/apache/directory/server/factory/CreateKdcServerAnnotationTest.java
index fbc4610..dbb6b80 100644
--- a/server-annotations/src/test/java/org/apache/directory/server/factory/CreateKdcServerAnnotationTest.java
+++ b/server-annotations/src/test/java/org/apache/directory/server/factory/CreateKdcServerAnnotationTest.java
@@ -23,6 +23,8 @@ package org.apache.directory.server.factory;
import static org.junit.Assert.assertEquals;
+import javax.security.auth.kerberos.KerberosPrincipal;
+
import org.apache.directory.api.util.FileUtils;
import org.apache.directory.server.annotations.CreateKdcServer;
import org.apache.directory.server.annotations.CreateTransport;
@@ -31,6 +33,8 @@ import org.apache.directory.server.core.api.DirectoryService;
import org.apache.directory.server.core.factory.DSAnnotationProcessor;
import org.apache.directory.server.kerberos.KerberosConfig;
import org.apache.directory.server.kerberos.kdc.KdcServer;
+import org.apache.directory.server.kerberos.shared.replay.ReplayCache;
+import org.apache.directory.shared.kerberos.KerberosTime;
import org.apache.mina.util.AvailablePortFinder;
import org.junit.Test;
@@ -47,6 +51,7 @@ public class CreateKdcServerAnnotationTest
kdcPrincipal = "krbtgt/apache.org@apache.org",
maxTicketLifetime = 1000,
maxRenewableLifetime = 2000,
+ replayCacheType = DisabledReplayCache.class,
transports =
{
@CreateTransport(protocol = "TCP"),
@@ -69,6 +74,7 @@ public class CreateKdcServerAnnotationTest
assertEquals( "krbtgt/apache.org@apache.org", config.getServicePrincipal().getName() );
assertEquals( 1000, config.getMaximumTicketLifetime() );
assertEquals( 2000, config.getMaximumRenewableLifetime() );
+ assertEquals( DisabledReplayCache.class, config.getReplayCacheType() );
server.stop();
directoryService.shutdown();
@@ -101,11 +107,38 @@ public class CreateKdcServerAnnotationTest
assertEquals( "krbtgt/apache.org@apache.org", config.getServicePrincipal().getName() );
assertEquals( 1000, config.getMaximumTicketLifetime() );
assertEquals( 2000, config.getMaximumRenewableLifetime() );
+ assertEquals( KerberosConfig.DEFAULT_REPLAY_CACHE_TYPE, config.getReplayCacheType() );
server.stop();
directoryService.shutdown();
FileUtils.deleteDirectory( directoryService.getInstanceLayout().getInstanceDirectory() );
}
-
+
+ /**
+ * Empty {@link ReplayCache} implementation which doesn't cache.
+ */
+ public static class DisabledReplayCache implements ReplayCache
+ {
+
+ @Override
+ public boolean isReplay(KerberosPrincipal serverPrincipal, KerberosPrincipal clientPrincipal, KerberosTime clientTime,
+ int clientMicroSeconds)
+ {
+ return false;
+ }
+
+ @Override
+ public void save(KerberosPrincipal serverPrincipal, KerberosPrincipal clientPrincipal, KerberosTime clientTime,
+ int clientMicroSeconds)
+ {
+ }
+
+ @Override
+ public void clear()
+ {
+ }
+
+ }
+
}