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
9 changes: 7 additions & 2 deletions src/main/java/org/lsc/Launcher.java
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@
import org.apache.commons.cli.Options;
import org.apache.commons.cli.ParseException;
import org.lsc.configuration.LscConfiguration;
import org.lsc.jndi.JndiServices;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

Expand Down Expand Up @@ -205,7 +206,8 @@ public int run() {
if (timeLimit > 0) {
sync.setTimeLimit(timeLimit);
}
sync.launch(asyncType, syncType, cleanType);
boolean launchResult = sync.launch(asyncType, syncType, cleanType);
return launchResult ? 0 : 1;
} catch (Exception e) {
if (!Configuration.isLoggingSetup()) {
System.err.println("Error: " + e.toString());
Expand All @@ -215,8 +217,11 @@ public int run() {
LOGGER.debug(e.toString(), e);
}
return 1;
} finally {
if (asyncType == null || asyncType.isEmpty() || validateConfiguration) {
JndiServices.cleanSecurityProperties();
}
}
return 0;
}

/**
Expand Down
4 changes: 3 additions & 1 deletion src/main/java/org/lsc/SimpleSynchronize.java
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@
import org.lsc.configuration.TaskType;
import org.lsc.exception.LscConfigurationException;
import org.lsc.jmx.LscServerImpl;
import org.lsc.jndi.JndiServices;
import org.lsc.runnable.SynchronizeEntryRunner;
import org.lsc.service.IAsynchronousService;
import org.lsc.service.SyncReplSourceService;
Expand Down Expand Up @@ -137,7 +138,7 @@ public void initAllTasks() throws LscConfigurationException {
}
}

private void close() {
public void close() {
for (Task task: cache.values()) {
if (task.getSourceService() instanceof Closeable) {
try {
Expand All @@ -154,6 +155,7 @@ private void close() {
}
}
}
JndiServices.cleanSecurityProperties();
}


Expand Down
2 changes: 2 additions & 0 deletions src/main/java/org/lsc/configuration/LscConfiguration.java
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@
import org.lsc.beans.syncoptions.PropertiesBasedSyncOptions;
import org.lsc.exception.LscConfigurationException;
import org.lsc.exception.LscException;
import org.lsc.jndi.JndiServices;
import org.lsc.jndi.PullableJndiSrcService;
import org.lsc.jndi.SimpleJndiDstService;
import org.lsc.service.MultipleDstService;
Expand Down Expand Up @@ -511,6 +512,7 @@ private static PolicyType parseSyncType(String value) throws LscConfigurationExc
public static void reset() {
instance = null;
original = null;
JndiServices.reset();
}

public static void setSyncOptions(TaskType task, SyncOptionsType syncOptions) throws LscConfigurationException {
Expand Down
56 changes: 41 additions & 15 deletions src/main/java/org/lsc/jndi/JndiServices.java
Original file line number Diff line number Diff line change
Expand Up @@ -329,21 +329,26 @@ public static Properties getLdapProperties(LdapConnectionType connection) throws
props.setProperty(DirContext.SECURITY_AUTHENTICATION, connection.getAuthentication().value());
props.setProperty(DirContext.SECURITY_PRINCIPAL, connection.getUsername());
if (connection.getAuthentication().equals(LdapAuthenticationType.GSSAPI)) {
if (System.getProperty("java.security.krb5.conf") != null) {
throw new RuntimeException("Multiple Kerberos connections not supported (existing value: "
+ System.getProperty("java.security.krb5.conf")
+ "). Need to set another LSC instance or unset system property !");
} else {
System.setProperty("java.security.krb5.conf",
new File(Configuration.getConfigurationDirectory(), "krb5.ini").getAbsolutePath());
}
if (System.getProperty("java.security.auth.login.config") != null) {
throw new RuntimeException("Multiple JAAS not supported (existing value: "
+ System.getProperty("java.security.auth.login.config")
+ "). Need to set another LSC instance or unset system property !");
} else {
System.setProperty("java.security.auth.login.config",
new File(Configuration.getConfigurationDirectory(), "gsseg_jaas.conf").getAbsolutePath());
synchronized (JndiServices.class) {
String targetKrb5Conf = new File(Configuration.getConfigurationDirectory(), "krb5.ini").getAbsolutePath();
String existingKrb5Conf = System.getProperty("java.security.krb5.conf");
if (existingKrb5Conf == null) {
System.setProperty("java.security.krb5.conf", targetKrb5Conf);
} else if (!existingKrb5Conf.equals(targetKrb5Conf)) {
throw new RuntimeException("Multiple Kerberos connections not supported (existing value: "
+ existingKrb5Conf + ", requested value: " + targetKrb5Conf
+ "). Need to set another LSC instance or unset system property !");
}

String targetJaasConf = new File(Configuration.getConfigurationDirectory(), "gsseg_jaas.conf").getAbsolutePath();
String existingJaasConf = System.getProperty("java.security.auth.login.config");
if (existingJaasConf == null) {
System.setProperty("java.security.auth.login.config", targetJaasConf);
} else if (!existingJaasConf.equals(targetJaasConf)) {
throw new RuntimeException("Multiple JAAS not supported (existing value: "
+ existingJaasConf + ", requested value: " + targetJaasConf
+ "). Need to set another LSC instance or unset system property !");
}
}
props.setProperty("javax.security.sasl.server.authentication",
"" + connection.isSaslMutualAuthentication());
Expand Down Expand Up @@ -1397,6 +1402,27 @@ public String completeDn(String dn) {
public static CallbackHandler getCallbackHandler(String user, String pass) {
return new KerberosCallbackHandler(user, pass);
}

/**
* Unset Kerberos and JAAS security system properties and reset cached JAAS configuration.
*/
public static synchronized void cleanSecurityProperties() {
System.clearProperty("java.security.krb5.conf");
System.clearProperty("java.security.auth.login.config");
try {
javax.security.auth.login.Configuration.setConfiguration(null);
} catch (Exception e) {
LOGGER.warn("Failed to reset JAAS configuration: {}", e.getMessage());
}
}

/**
* Reset connections cache and clean security properties.
*/
public static synchronized void reset() {
cache.clear();
cleanSecurityProperties();
}
}

class KerberosCallbackHandler implements CallbackHandler {
Expand Down
164 changes: 164 additions & 0 deletions src/test/java/org/lsc/jndi/JndiServicesTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;

import java.net.URL;
Expand Down Expand Up @@ -344,6 +345,169 @@ public final void testCloseIdempotent() throws Exception {
assertNull(freshInstance.getContext());
}

@Test
public final void testGssapiMultipleCallsSuccess() throws Exception {
org.lsc.configuration.LdapConnectionType conn = new org.lsc.configuration.LdapConnectionType();
conn.setName("gssapi-ldap");
conn.setUrl("ldap://localhost:33389/dc=lsc-project,dc=org");
conn.setUsername("user@REALM");
conn.setPassword("password");
conn.setAuthentication(org.lsc.configuration.LdapAuthenticationType.GSSAPI);
conn.setSaslQop(org.lsc.configuration.SaslQopType.AUTH);
conn.setTlsActivated(false);
conn.setSaslMutualAuthentication(false);

try {
JndiServices.cleanSecurityProperties();

// First call sets java.security.krb5.conf and java.security.auth.login.config
JndiServices.getLdapProperties(conn);
assertNotNull(System.getProperty("java.security.krb5.conf"));
assertNotNull(System.getProperty("java.security.auth.login.config"));

// Second call with same configuration succeeds without throwing
JndiServices.getLdapProperties(conn);
assertNotNull(System.getProperty("java.security.krb5.conf"));
assertNotNull(System.getProperty("java.security.auth.login.config"));
} finally {
JndiServices.cleanSecurityProperties();
}
}

@Test
public final void testGssapiConflictingKrb5ConfThrowsException() throws Exception {
org.lsc.configuration.LdapConnectionType conn = new org.lsc.configuration.LdapConnectionType();
conn.setName("gssapi-ldap");
conn.setUrl("ldap://localhost:33389/dc=lsc-project,dc=org");
conn.setUsername("user@REALM");
conn.setPassword("password");
conn.setAuthentication(org.lsc.configuration.LdapAuthenticationType.GSSAPI);
conn.setSaslQop(org.lsc.configuration.SaslQopType.AUTH);
conn.setTlsActivated(false);
conn.setSaslMutualAuthentication(false);

try {
JndiServices.cleanSecurityProperties();
System.setProperty("java.security.krb5.conf", "/different/path/krb5.conf");

RuntimeException thrown = assertThrows(RuntimeException.class, () -> {
JndiServices.getLdapProperties(conn);
});
assertTrue(thrown.getMessage().contains("Multiple Kerberos connections not supported"));
} finally {
JndiServices.cleanSecurityProperties();
}
}

@Test
public final void testGssapiConflictingJaasConfThrowsException() throws Exception {
org.lsc.configuration.LdapConnectionType conn = new org.lsc.configuration.LdapConnectionType();
conn.setName("gssapi-ldap");
conn.setUrl("ldap://localhost:33389/dc=lsc-project,dc=org");
conn.setUsername("user@REALM");
conn.setPassword("password");
conn.setAuthentication(org.lsc.configuration.LdapAuthenticationType.GSSAPI);
conn.setSaslQop(org.lsc.configuration.SaslQopType.AUTH);
conn.setTlsActivated(false);
conn.setSaslMutualAuthentication(false);

try {
JndiServices.cleanSecurityProperties();
System.setProperty("java.security.auth.login.config", "/different/path/jaas.conf");

RuntimeException thrown = assertThrows(RuntimeException.class, () -> {
JndiServices.getLdapProperties(conn);
});
assertTrue(thrown.getMessage().contains("Multiple JAAS not supported"));
} finally {
JndiServices.cleanSecurityProperties();
}
}

@Test
public final void testCleanSecurityProperties() throws Exception {
org.lsc.configuration.LdapConnectionType conn = new org.lsc.configuration.LdapConnectionType();
conn.setName("gssapi-ldap");
conn.setUrl("ldap://localhost:33389/dc=lsc-project,dc=org");
conn.setUsername("user@REALM");
conn.setPassword("password");
conn.setAuthentication(org.lsc.configuration.LdapAuthenticationType.GSSAPI);
conn.setSaslQop(org.lsc.configuration.SaslQopType.AUTH);
conn.setTlsActivated(false);
conn.setSaslMutualAuthentication(false);

JndiServices.cleanSecurityProperties();
JndiServices.getLdapProperties(conn);
assertNotNull(System.getProperty("java.security.krb5.conf"));
assertNotNull(System.getProperty("java.security.auth.login.config"));

JndiServices.cleanSecurityProperties();
assertNull(System.getProperty("java.security.krb5.conf"));
assertNull(System.getProperty("java.security.auth.login.config"));
}

@Test
public final void testLscConfigurationResetCleansSecurityProperties() throws Exception {
org.lsc.configuration.LdapConnectionType conn = new org.lsc.configuration.LdapConnectionType();
conn.setName("gssapi-ldap");
conn.setUrl("ldap://localhost:33389/dc=lsc-project,dc=org");
conn.setUsername("user@REALM");
conn.setPassword("password");
conn.setAuthentication(org.lsc.configuration.LdapAuthenticationType.GSSAPI);
conn.setSaslQop(org.lsc.configuration.SaslQopType.AUTH);
conn.setTlsActivated(false);
conn.setSaslMutualAuthentication(false);

JndiServices.cleanSecurityProperties();
JndiServices.getLdapProperties(conn);
assertNotNull(System.getProperty("java.security.krb5.conf"));
assertNotNull(System.getProperty("java.security.auth.login.config"));

LscConfiguration.reset();
assertNull(System.getProperty("java.security.krb5.conf"));
assertNull(System.getProperty("java.security.auth.login.config"));
}

@Test
public final void testGssapiConcurrentCalls() throws Exception {
org.lsc.configuration.LdapConnectionType conn = new org.lsc.configuration.LdapConnectionType();
conn.setName("gssapi-ldap");
conn.setUrl("ldap://localhost:33389/dc=lsc-project,dc=org");
conn.setUsername("user@REALM");
conn.setPassword("password");
conn.setAuthentication(org.lsc.configuration.LdapAuthenticationType.GSSAPI);
conn.setSaslQop(org.lsc.configuration.SaslQopType.AUTH);
conn.setTlsActivated(false);
conn.setSaslMutualAuthentication(false);

try {
JndiServices.cleanSecurityProperties();
int threadCount = 8;
java.util.concurrent.ExecutorService executor = java.util.concurrent.Executors.newFixedThreadPool(threadCount);
java.util.concurrent.CountDownLatch startLatch = new java.util.concurrent.CountDownLatch(1);
java.util.List<java.util.concurrent.Future<?>> futures = new java.util.ArrayList<>();

for (int i = 0; i < threadCount; i++) {
futures.add(executor.submit(() -> {
try {
startLatch.await();
JndiServices.getLdapProperties(conn);
} catch (Exception e) {
throw new RuntimeException(e);
}
}));
}

startLatch.countDown();
for (java.util.concurrent.Future<?> f : futures) {
f.get();
}
executor.shutdown();
} finally {
JndiServices.cleanSecurityProperties();
}
}

public void testAuthenticationThroughJAAS() {
LoginContext lc = null;
String user = "";
Expand Down
9 changes: 9 additions & 0 deletions src/test/resources/etc/gsseg_jaas.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
/**
* Login Configuration for JAAS.
*
* Specify that Kerberos v5 is a required login module for the
* JndiServices connection helper.
*/
org.lsc.jndi.JndiServices {
com.sun.security.auth.module.Krb5LoginModule required client=TRUE useTicketCache=TRUE;
};
6 changes: 6 additions & 0 deletions src/test/resources/etc/krb5.ini
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
[libdefaults]
default_realm = LSC-PROJECT.ORG
[realms]
LSC-PROJECT.ORG = {
kdc = localhost:33389
}
Loading