You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@qpid.apache.org by or...@apache.org on 2020/09/02 19:32:48 UTC

[qpid-broker-j] 01/10: NO-JIRA: Fix Kerberos tests

This is an automated email from the ASF dual-hosted git repository.

orudyy pushed a commit to branch 7.1.x
in repository https://gitbox.apache.org/repos/asf/qpid-broker-j.git

commit 55bab0d6d226faf9077a6590cecb439f6c2071d8
Author: Alex Rudyy <or...@apache.org>
AuthorDate: Fri Nov 29 13:08:51 2019 +0000

    NO-JIRA: Fix Kerberos tests
---
 .../manager/KerberosAuthenticationManagerTest.java |  79 +++++----
 .../SimpleLDAPAuthenticationManagerTest.java       |  17 +-
 .../auth/manager/SpnegoAuthenticatorTest.java      |  61 +++----
 .../qpid/server/test/EmbeddedKdcResource.java      |  29 +--
 .../apache/qpid/server/test/KerberosUtilities.java | 196 +++++++++++++++++++--
 broker-core/src/test/resources/login.ibm.config    |  51 ++++++
 .../java/org/apache/qpid/test/utils/JvmVendor.java |  25 ++-
 .../org/apache/qpid/test/utils/UnitTestBase.java   |  18 +-
 8 files changed, 338 insertions(+), 138 deletions(-)

diff --git a/broker-core/src/test/java/org/apache/qpid/server/security/auth/manager/KerberosAuthenticationManagerTest.java b/broker-core/src/test/java/org/apache/qpid/server/security/auth/manager/KerberosAuthenticationManagerTest.java
index e24d56c..9211710 100644
--- a/broker-core/src/test/java/org/apache/qpid/server/security/auth/manager/KerberosAuthenticationManagerTest.java
+++ b/broker-core/src/test/java/org/apache/qpid/server/security/auth/manager/KerberosAuthenticationManagerTest.java
@@ -19,19 +19,22 @@
 
 package org.apache.qpid.server.security.auth.manager;
 
-import static org.apache.commons.codec.CharEncoding.UTF_8;
 import static org.apache.qpid.server.security.auth.manager.KerberosAuthenticationManager.GSSAPI_MECHANISM;
-import static org.hamcrest.Matchers.not;
+import static org.apache.qpid.server.test.KerberosUtilities.ACCEPT_SCOPE;
+import static org.apache.qpid.server.test.KerberosUtilities.CLIENT_PRINCIPAL_FULL_NAME;
+import static org.apache.qpid.server.test.KerberosUtilities.CLIENT_PRINCIPAL_NAME;
+import static org.apache.qpid.server.test.KerberosUtilities.HOST_NAME;
+import static org.apache.qpid.server.test.KerberosUtilities.LOGIN_CONFIG;
+import static org.apache.qpid.server.test.KerberosUtilities.REALM;
+import static org.apache.qpid.server.test.KerberosUtilities.SERVER_PROTOCOL;
+import static org.apache.qpid.server.test.KerberosUtilities.SERVICE_PRINCIPAL_NAME;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.fail;
-import static org.junit.Assume.assumeThat;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.when;
 
 import java.io.File;
-import java.net.URL;
-import java.net.URLDecoder;
 import java.security.PrivilegedActionException;
 import java.security.PrivilegedExceptionAction;
 import java.util.Base64;
@@ -45,13 +48,10 @@ import javax.security.auth.login.LoginContext;
 import javax.security.sasl.Sasl;
 import javax.security.sasl.SaslClient;
 
-import org.ietf.jgss.GSSException;
 import org.junit.Before;
 import org.junit.BeforeClass;
 import org.junit.ClassRule;
 import org.junit.Test;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
 
 import org.apache.qpid.server.configuration.IllegalConfigurationException;
 import org.apache.qpid.server.model.AuthenticationProvider;
@@ -62,26 +62,20 @@ import org.apache.qpid.server.security.auth.sasl.SaslNegotiator;
 import org.apache.qpid.server.security.auth.sasl.SaslSettings;
 import org.apache.qpid.server.test.EmbeddedKdcResource;
 import org.apache.qpid.server.test.KerberosUtilities;
-import org.apache.qpid.test.utils.JvmVendor;
+import org.apache.qpid.server.util.StringUtil;
 import org.apache.qpid.test.utils.SystemPropertySetter;
 import org.apache.qpid.test.utils.UnitTestBase;
 
 public class KerberosAuthenticationManagerTest extends UnitTestBase
 {
-    private static final Logger LOGGER = LoggerFactory.getLogger(KerberosAuthenticationManagerTest.class);
-    private static final String LOGIN_CONFIG = "login.config";
-    private static final String REALM = "QPID.ORG";
-    private static final String SERVER_NAME = "localhost";
-    private static final String SERVER_PROTOCOL = "AMQP";
-    private static final String SERVICE_PRINCIPAL_NAME = SERVER_PROTOCOL + "/" + SERVER_NAME;
-    private static final String SERVER_PRINCIPAL_FULL_NAME = SERVICE_PRINCIPAL_NAME + "@" + REALM;
-    private static final String CLIENT_PRINCIPAL_NAME = "client";
-    private static final String CLIENT_PRINCIPAL_FULL_NAME = CLIENT_PRINCIPAL_NAME + "@" + REALM;
 
     private static final KerberosUtilities UTILS = new KerberosUtilities();
 
     @ClassRule
-    public static final EmbeddedKdcResource KDC = new EmbeddedKdcResource(REALM);
+    public static final EmbeddedKdcResource KDC = new EmbeddedKdcResource(HOST_NAME,
+                                                                          0,
+                                                                          "QpidTestKerberosServer",
+                                                                          REALM);
 
     @ClassRule
     public static final SystemPropertySetter SYSTEM_PROPERTY_SETTER = new SystemPropertySetter();
@@ -94,21 +88,15 @@ public class KerberosAuthenticationManagerTest extends UnitTestBase
     @BeforeClass
     public static void createKeyTabs() throws Exception
     {
-        assumeThat(getJvmVendor(), not(JvmVendor.IBM));
-        KDC.createPrincipal("broker.keytab", SERVER_PRINCIPAL_FULL_NAME);
-        _clientKeyTabFile = KDC.createPrincipal("client.keytab", CLIENT_PRINCIPAL_FULL_NAME);
-        final URL resource = KerberosAuthenticationManagerTest.class.getClassLoader().getResource(LOGIN_CONFIG);
-        LOGGER.debug("JAAS config:" + resource);
-        assertNotNull(resource);
-        SYSTEM_PROPERTY_SETTER.setSystemProperty("java.security.auth.login.config", URLDecoder.decode(resource.getPath(), UTF_8));
-        SYSTEM_PROPERTY_SETTER.setSystemProperty("javax.security.auth.useSubjectCredsOnly", "false");
+        UTILS.prepareConfiguration(HOST_NAME, SYSTEM_PROPERTY_SETTER);
+        _clientKeyTabFile = UTILS.prepareKeyTabs(KDC);
     }
 
     @Before
     public void setUp() throws Exception
     {
         Map<String, String> context = Collections.singletonMap(KerberosAuthenticationManager.GSSAPI_SPNEGO_CONFIG,
-                                                               "com.sun.security.jgss.accept");
+                                                               ACCEPT_SCOPE);
         final Map<String, Object> attributes = new HashMap<>();
         attributes.put(AuthenticationProvider.NAME, getTestName());
         attributes.put(AuthenticationProvider.CONTEXT, context);
@@ -123,7 +111,7 @@ public class KerberosAuthenticationManagerTest extends UnitTestBase
     public void testCreateSaslNegotiator() throws Exception
     {
         final SaslSettings saslSettings = mock(SaslSettings.class);
-        when(saslSettings.getLocalFQDN()).thenReturn(SERVER_NAME);
+        when(saslSettings.getLocalFQDN()).thenReturn(HOST_NAME);
         final SaslNegotiator negotiator = _kerberosAuthenticationProvider.createSaslNegotiator(GSSAPI_MECHANISM,
                                                                                                saslSettings,
                                                                                                null);
@@ -163,7 +151,7 @@ public class KerberosAuthenticationManagerTest extends UnitTestBase
     public void testCreateKerberosAuthenticationProvidersWithNonExistingJaasLoginModule()
     {
         when(_broker.getChildren(AuthenticationProvider.class)).thenReturn(Collections.emptySet());
-        SYSTEM_PROPERTY_SETTER.setSystemProperty("java.security.auth.login.config",
+        SYSTEM_PROPERTY_SETTER.setSystemProperty(LOGIN_CONFIG,
                                                  "config.module." + System.nanoTime());
         final Map<String, Object> attributes = Collections.singletonMap(AuthenticationProvider.NAME, getTestName());
         final KerberosAuthenticationManager kerberosAuthenticationProvider =
@@ -180,10 +168,11 @@ public class KerberosAuthenticationManagerTest extends UnitTestBase
     }
 
     @Test
-    public void testAuthenticateUsingNegotiationToken() throws GSSException
+    public void testAuthenticateUsingNegotiationToken() throws Exception
     {
-        final String token =
-                Base64.getEncoder().encodeToString(UTILS.buildToken(CLIENT_PRINCIPAL_NAME, SERVICE_PRINCIPAL_NAME));
+        byte[] negotiationTokenBytes =
+                UTILS.buildToken(CLIENT_PRINCIPAL_NAME, _clientKeyTabFile, SERVICE_PRINCIPAL_NAME);
+        final String token = Base64.getEncoder().encodeToString(negotiationTokenBytes);
         final String authenticationHeader = SpnegoAuthenticator.NEGOTIATE_PREFIX + token;
 
         final AuthenticationResult result = _kerberosAuthenticationProvider.authenticate(authenticationHeader);
@@ -197,19 +186,30 @@ public class KerberosAuthenticationManagerTest extends UnitTestBase
         final LoginContext lc = UTILS.createKerberosKeyTabLoginContext(getTestName(),
                                                                        CLIENT_PRINCIPAL_FULL_NAME,
                                                                        _clientKeyTabFile);
+
+        Subject clientSubject = null;
         try
         {
             lc.login();
-            final Subject clientSubject = lc.getSubject();
+            clientSubject = lc.getSubject();
+            debug("LoginContext subject {}", clientSubject);
             final SaslClient saslClient = createSaslClient(clientSubject);
             return performNegotiation(clientSubject, saslClient, negotiator);
         }
         finally
         {
-            lc.logout();
+            if (clientSubject != null)
+            {
+                lc.logout();
+            }
         }
     }
 
+    private void debug(String message, Object... args)
+    {
+        UTILS.debug(message, args);
+    }
+
     private AuthenticationResult performNegotiation(final Subject clientSubject,
                                                     final SaslClient saslClient,
                                                     final SaslNegotiator negotiator)
@@ -223,6 +223,7 @@ public class KerberosAuthenticationManagerTest extends UnitTestBase
             if (!initiated)
             {
                 initiated = true;
+                debug("Sending initial challenge");
                 response = Subject.doAs(clientSubject, (PrivilegedExceptionAction<byte[]>) () -> {
                     if (saslClient.hasInitialResponse())
                     {
@@ -230,19 +231,25 @@ public class KerberosAuthenticationManagerTest extends UnitTestBase
                     }
                     return null;
                 });
+                debug("Initial challenge sent");
             }
 
+            debug("Handling response: {}", StringUtil.toHex(response));
             result = negotiator.handleResponse(response);
 
             byte[] challenge = result.getChallenge();
+
             if (challenge != null)
             {
+                debug("Challenge: {}", StringUtil.toHex(challenge));
                 response = Subject.doAs(clientSubject,
                                         (PrivilegedExceptionAction<byte[]>) () -> saslClient.evaluateChallenge(
                                                 challenge));
             }
         }
         while (result.getStatus() == AuthenticationResult.AuthenticationStatus.CONTINUE);
+
+        debug("Result {}", result.getStatus());
         return result;
     }
 
@@ -256,7 +263,7 @@ public class KerberosAuthenticationManagerTest extends UnitTestBase
             return Sasl.createSaslClient(new String[]{GSSAPI_MECHANISM},
                                          null,
                                          SERVER_PROTOCOL,
-                                         SERVER_NAME,
+                                         HOST_NAME,
                                          props,
                                          null);
         });
diff --git a/broker-core/src/test/java/org/apache/qpid/server/security/auth/manager/SimpleLDAPAuthenticationManagerTest.java b/broker-core/src/test/java/org/apache/qpid/server/security/auth/manager/SimpleLDAPAuthenticationManagerTest.java
index 3f6efa5..ed33947 100644
--- a/broker-core/src/test/java/org/apache/qpid/server/security/auth/manager/SimpleLDAPAuthenticationManagerTest.java
+++ b/broker-core/src/test/java/org/apache/qpid/server/security/auth/manager/SimpleLDAPAuthenticationManagerTest.java
@@ -20,7 +20,7 @@ package org.apache.qpid.server.security.auth.manager;
 
 import static java.nio.charset.StandardCharsets.UTF_8;
 import static org.apache.qpid.server.security.auth.manager.CachingAuthenticationProvider.AUTHENTICATION_CACHE_MAX_SIZE;
-import static org.hamcrest.Matchers.not;
+import static org.hamcrest.CoreMatchers.not;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.fail;
@@ -31,8 +31,6 @@ import static org.mockito.Mockito.when;
 import java.io.File;
 import java.io.IOException;
 import java.net.InetSocketAddress;
-import java.net.URL;
-import java.net.URLDecoder;
 import java.nio.file.FileSystems;
 import java.nio.file.Path;
 import java.security.Principal;
@@ -49,7 +47,6 @@ import java.util.concurrent.atomic.AtomicBoolean;
 import javax.security.auth.Subject;
 import javax.security.auth.kerberos.KerberosPrincipal;
 
-import org.apache.commons.codec.CharEncoding;
 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.Entry;
@@ -91,6 +88,7 @@ import org.apache.qpid.server.security.auth.SocketConnectionPrincipal;
 import org.apache.qpid.server.security.auth.sasl.SaslNegotiator;
 import org.apache.qpid.server.security.auth.sasl.SaslSettings;
 import org.apache.qpid.test.utils.JvmVendor;
+import org.apache.qpid.server.test.KerberosUtilities;
 import org.apache.qpid.test.utils.SystemPropertySetter;
 import org.apache.qpid.test.utils.TestFileUtils;
 import org.apache.qpid.test.utils.UnitTestBase;
@@ -147,9 +145,9 @@ public class SimpleLDAPAuthenticationManagerTest extends UnitTestBase
     private static final String HOSTNAME = "localhost";
     private static final String BROKER_PRINCIPAL = "service/" + HOSTNAME;
     private static final String LINE_SEPARATOR = System.lineSeparator();
-    private static final String LOGIN_CONFIG = "login.config";
     private static final String LOGIN_SCOPE = "ldap-gssapi-bind";
     private static final AtomicBoolean KERBEROS_SETUP = new AtomicBoolean();
+    private static final KerberosUtilities UTILS = new KerberosUtilities();
 
     @ClassRule
     public static CreateLdapServerRule LDAP = new CreateLdapServerRule();
@@ -400,14 +398,11 @@ public class SimpleLDAPAuthenticationManagerTest extends UnitTestBase
         createPrincipal("Service", "LDAP Service", "ldap", UUID.randomUUID().toString(), servicePrincipalName);
     }
 
-    private void setUpJaas() throws LdapException, IOException
+    private void setUpJaas() throws Exception
     {
         createKeyTab(BROKER_PRINCIPAL);
-        final URL resource = SimpleLDAPAuthenticationManagerTest.class.getClassLoader().getResource(LOGIN_CONFIG);
-        LOGGER.debug("JAAS config:" + resource);
-        assertNotNull(resource);
-        SYSTEM_PROPERTY_SETTER.setSystemProperty("java.security.auth.login.config", URLDecoder.decode(resource.getPath(), CharEncoding.UTF_8));
-        SYSTEM_PROPERTY_SETTER.setSystemProperty("sun.security.krb5.debug", "true");
+
+        UTILS.prepareConfiguration(KerberosUtilities.HOST_NAME, SYSTEM_PROPERTY_SETTER);
     }
 
     private String createKrb5Conf(final int port) throws IOException
diff --git a/broker-core/src/test/java/org/apache/qpid/server/security/auth/manager/SpnegoAuthenticatorTest.java b/broker-core/src/test/java/org/apache/qpid/server/security/auth/manager/SpnegoAuthenticatorTest.java
index d3f8342..bbd65a4 100644
--- a/broker-core/src/test/java/org/apache/qpid/server/security/auth/manager/SpnegoAuthenticatorTest.java
+++ b/broker-core/src/test/java/org/apache/qpid/server/security/auth/manager/SpnegoAuthenticatorTest.java
@@ -19,52 +19,45 @@
 
 package org.apache.qpid.server.security.auth.manager;
 
-import static org.apache.commons.codec.CharEncoding.UTF_8;
-import static org.hamcrest.Matchers.not;
+import static org.apache.qpid.server.test.KerberosUtilities.ACCEPT_SCOPE;
+import static org.apache.qpid.server.test.KerberosUtilities.HOST_NAME;
+import static org.apache.qpid.server.test.KerberosUtilities.REALM;
+import static org.apache.qpid.server.test.KerberosUtilities.SERVICE_PRINCIPAL_NAME;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertTrue;
-import static org.junit.Assume.assumeThat;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.when;
 
-import java.net.URL;
-import java.net.URLDecoder;
+import java.io.File;
 import java.security.Principal;
 import java.util.Base64;
 import java.util.Map;
 
-import org.ietf.jgss.GSSException;
 import org.junit.Before;
 import org.junit.BeforeClass;
 import org.junit.ClassRule;
 import org.junit.Test;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
 
 import org.apache.qpid.server.security.TokenCarryingPrincipal;
 import org.apache.qpid.server.security.auth.AuthenticationResult;
 import org.apache.qpid.server.test.EmbeddedKdcResource;
 import org.apache.qpid.server.test.KerberosUtilities;
-import org.apache.qpid.test.utils.JvmVendor;
 import org.apache.qpid.test.utils.SystemPropertySetter;
 import org.apache.qpid.test.utils.UnitTestBase;
 
 public class SpnegoAuthenticatorTest extends UnitTestBase
 {
-    private static final Logger LOGGER = LoggerFactory.getLogger(SpnegoAuthenticatorTest.class);
-    private static final String CLIENT_NAME = "client";
-    private static final String SERVER_NAME = "AMQP/localhost";
-    private static final String ANOTHER_SERVICE = "foo/localhost";
-    private static final String REALM = "QPID.ORG";
-    private static final String LOGIN_CONFIG = "login.config";
-    private static final KerberosUtilities UTILS = new KerberosUtilities();;
+    private static final String ANOTHER_SERVICE = "foo/" + HOST_NAME;
+    private static final String ANOTHER_SERVICE_FULL_NAME = ANOTHER_SERVICE + "@" + REALM;
+    private static final KerberosUtilities UTILS = new KerberosUtilities();
 
     @ClassRule
-    public static final EmbeddedKdcResource KDC = new EmbeddedKdcResource(REALM);
+    public static final EmbeddedKdcResource KDC = new EmbeddedKdcResource(HOST_NAME, 0, "QpidTestKerberosServer", REALM);
 
     @ClassRule
     public static final SystemPropertySetter SYSTEM_PROPERTY_SETTER = new SystemPropertySetter();
+    private static File _clientKeyTab;
 
     private SpnegoAuthenticator _spnegoAuthenticator;
     private KerberosAuthenticationManager _kerberosAuthenticationManager;
@@ -72,31 +65,25 @@ public class SpnegoAuthenticatorTest extends UnitTestBase
     @BeforeClass
     public static void createKeyTabs() throws Exception
     {
-        assumeThat(getJvmVendor(), not(JvmVendor.IBM));
-        KDC.createPrincipal("broker.keytab", SERVER_NAME + "@" + REALM);
-        KDC.createPrincipal("client.keytab", CLIENT_NAME + "@" + REALM);
-        KDC.createPrincipal("another.keytab", ANOTHER_SERVICE + "@" + REALM);
-        final URL resource = KerberosAuthenticationManagerTest.class.getClassLoader().getResource(LOGIN_CONFIG);
-        LOGGER.debug("JAAS config:" + resource);
-        assertNotNull(resource);
-        SYSTEM_PROPERTY_SETTER.setSystemProperty("java.security.auth.login.config", URLDecoder.decode(resource.getPath(), UTF_8));
-        SYSTEM_PROPERTY_SETTER.setSystemProperty("javax.security.auth.useSubjectCredsOnly", "false");
+        KDC.createPrincipal("another.keytab", ANOTHER_SERVICE_FULL_NAME);
+        UTILS.prepareConfiguration(HOST_NAME, SYSTEM_PROPERTY_SETTER);
+        _clientKeyTab = UTILS.prepareKeyTabs(KDC);
     }
 
     @Before
     public void setUp()
     {
         _kerberosAuthenticationManager = mock(KerberosAuthenticationManager.class);
-        when(_kerberosAuthenticationManager.getSpnegoLoginConfigScope()).thenReturn("com.sun.security.jgss.accept");
+        when(_kerberosAuthenticationManager.getSpnegoLoginConfigScope()).thenReturn(ACCEPT_SCOPE);
         when(_kerberosAuthenticationManager.isStripRealmFromPrincipalName()).thenReturn(true);
 
         _spnegoAuthenticator = new SpnegoAuthenticator(_kerberosAuthenticationManager);
     }
 
     @Test
-    public void testAuthenticate() throws GSSException
+    public void testAuthenticate() throws Exception
     {
-        final String token = Base64.getEncoder().encodeToString(buildToken(SERVER_NAME));
+        final String token = Base64.getEncoder().encodeToString(buildToken(SERVICE_PRINCIPAL_NAME));
         final String authenticationHeader = SpnegoAuthenticator.NEGOTIATE_PREFIX + token;
 
         final AuthenticationResult result = _spnegoAuthenticator.authenticate(authenticationHeader);
@@ -105,7 +92,7 @@ public class SpnegoAuthenticatorTest extends UnitTestBase
         assertEquals(AuthenticationResult.AuthenticationStatus.SUCCESS, result.getStatus());
         final Principal principal = result.getMainPrincipal();
         assertTrue(principal instanceof TokenCarryingPrincipal);
-        assertEquals(CLIENT_NAME, principal.getName());
+        assertEquals(KerberosUtilities.CLIENT_PRINCIPAL_NAME, principal.getName());
 
         final Map<String, String> tokens = ((TokenCarryingPrincipal)principal).getTokens();
         assertNotNull(tokens);
@@ -121,9 +108,9 @@ public class SpnegoAuthenticatorTest extends UnitTestBase
     }
 
     @Test
-    public void testAuthenticateNoNegotiatePrefix() throws GSSException
+    public void testAuthenticateNoNegotiatePrefix() throws Exception
     {
-        final String token = Base64.getEncoder().encodeToString(buildToken(SERVER_NAME));
+        final String token = Base64.getEncoder().encodeToString(buildToken(SERVICE_PRINCIPAL_NAME));
         final AuthenticationResult result = _spnegoAuthenticator.authenticate(token);
         assertNotNull(result);
         assertEquals(AuthenticationResult.AuthenticationStatus.ERROR, result.getStatus());
@@ -148,10 +135,10 @@ public class SpnegoAuthenticatorTest extends UnitTestBase
     }
 
     @Test
-    public void testAuthenticateWrongConfigName() throws GSSException
+    public void testAuthenticateWrongConfigName() throws Exception
     {
         when(_kerberosAuthenticationManager.getSpnegoLoginConfigScope()).thenReturn("foo");
-        final String token = Base64.getEncoder().encodeToString(buildToken(SERVER_NAME));
+        final String token = Base64.getEncoder().encodeToString(buildToken(SERVICE_PRINCIPAL_NAME));
         final String authenticationHeader = SpnegoAuthenticator.NEGOTIATE_PREFIX + token;
 
         final AuthenticationResult result = _spnegoAuthenticator.authenticate(authenticationHeader);
@@ -160,7 +147,7 @@ public class SpnegoAuthenticatorTest extends UnitTestBase
     }
 
     @Test
-    public void testAuthenticateWrongServer() throws GSSException
+    public void testAuthenticateWrongServer() throws Exception
     {
         final String token = Base64.getEncoder().encodeToString(buildToken(ANOTHER_SERVICE));
         final String authenticationHeader = SpnegoAuthenticator.NEGOTIATE_PREFIX + token;
@@ -170,8 +157,8 @@ public class SpnegoAuthenticatorTest extends UnitTestBase
         assertEquals(AuthenticationResult.AuthenticationStatus.ERROR, result.getStatus());
     }
 
-    private byte[] buildToken(final String anotherService) throws GSSException
+    private byte[] buildToken(final String anotherService) throws Exception
     {
-        return UTILS.buildToken(CLIENT_NAME, anotherService);
+        return UTILS.buildToken(KerberosUtilities.CLIENT_PRINCIPAL_NAME, _clientKeyTab, anotherService);
     }
 }
diff --git a/broker-core/src/test/java/org/apache/qpid/server/test/EmbeddedKdcResource.java b/broker-core/src/test/java/org/apache/qpid/server/test/EmbeddedKdcResource.java
index cda31a8..8224189 100644
--- a/broker-core/src/test/java/org/apache/qpid/server/test/EmbeddedKdcResource.java
+++ b/broker-core/src/test/java/org/apache/qpid/server/test/EmbeddedKdcResource.java
@@ -21,11 +21,11 @@ package org.apache.qpid.server.test;
 
 import java.io.File;
 import java.io.IOException;
-import java.net.InetAddress;
 import java.nio.file.FileSystems;
 import java.nio.file.FileVisitResult;
 import java.nio.file.Files;
 import java.nio.file.Path;
+import java.nio.file.Paths;
 import java.nio.file.SimpleFileVisitor;
 import java.nio.file.attribute.BasicFileAttributes;
 import java.util.ArrayList;
@@ -46,11 +46,6 @@ public class EmbeddedKdcResource extends ExternalResource
     private final List<File> _createdFiles = new ArrayList<>();
     private volatile File _kdcDirectory;
 
-    public EmbeddedKdcResource(final String realm)
-    {
-        this(InetAddress.getLoopbackAddress().getCanonicalHostName(), 0, "QpidTestKerberosServer", realm);
-    }
-
     public EmbeddedKdcResource(final String host, final int port, final String serviceName, final String realm)
     {
         _realm = realm;
@@ -80,6 +75,7 @@ public class EmbeddedKdcResource extends ExternalResource
         _simpleKdcServer.setWorkDir(_kdcDirectory);
         _simpleKdcServer.init();
         _simpleKdcServer.start();
+        LOGGER.info("SimpleKdcServer started on port {}, realm '{}'", getPort(), getRealm());
     }
 
     @Override
@@ -149,7 +145,8 @@ public class EmbeddedKdcResource extends ExternalResource
     public File createPrincipal(String keyTabFileName, String... principals)
             throws Exception
     {
-        final File ketTabFile = createFile(keyTabFileName);
+        final Path ketTabPath = Paths.get("target", keyTabFileName);
+        final File ketTabFile = ketTabPath.toFile();
         _createdFiles.add(ketTabFile);
         createPrincipal(ketTabFile, principals);
         return ketTabFile;
@@ -175,22 +172,4 @@ public class EmbeddedKdcResource extends ExternalResource
         }
     }
 
-    private static File createFile(final String keyTabFile) throws IOException
-    {
-        final File target = FileSystems.getDefault().getPath("target").toFile();
-        final File file = new File(target, keyTabFile);
-        if (file.exists())
-        {
-            if (!file.delete())
-            {
-                throw new IOException(String.format("Cannot delete existing file '%s'", keyTabFile));
-            }
-        }
-        if (!file.createNewFile())
-        {
-            throw new IOException(String.format("Cannot create file '%s'", keyTabFile));
-        }
-        return file;
-    }
-
 }
diff --git a/broker-core/src/test/java/org/apache/qpid/server/test/KerberosUtilities.java b/broker-core/src/test/java/org/apache/qpid/server/test/KerberosUtilities.java
index 182df06..88fe2db 100644
--- a/broker-core/src/test/java/org/apache/qpid/server/test/KerberosUtilities.java
+++ b/broker-core/src/test/java/org/apache/qpid/server/test/KerberosUtilities.java
@@ -20,8 +20,20 @@
 package org.apache.qpid.server.test;
 
 import static java.lang.Boolean.TRUE;
+import static java.nio.charset.StandardCharsets.UTF_8;
 
 import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.InetAddress;
+import java.net.URL;
+import java.net.URLDecoder;
+import java.nio.file.Files;
+import java.nio.file.LinkOption;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.nio.file.StandardOpenOption;
+import java.security.PrivilegedExceptionAction;
 import java.util.Collections;
 import java.util.HashMap;
 import java.util.Map;
@@ -38,6 +50,7 @@ import javax.security.auth.login.Configuration;
 import javax.security.auth.login.LoginContext;
 import javax.security.auth.login.LoginException;
 
+import com.google.common.io.ByteStreams;
 import org.ietf.jgss.GSSContext;
 import org.ietf.jgss.GSSCredential;
 import org.ietf.jgss.GSSException;
@@ -47,22 +60,112 @@ import org.ietf.jgss.Oid;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import org.apache.qpid.test.utils.JvmVendor;
+import org.apache.qpid.test.utils.SystemPropertySetter;
+
 public class KerberosUtilities
 {
+    public static final String REALM = "QPID.ORG";
+    public static final String HOST_NAME = InetAddress.getLoopbackAddress().getCanonicalHostName();
+    public static final String CLIENT_PRINCIPAL_NAME = "client";
+    public static final String CLIENT_PRINCIPAL_FULL_NAME = CLIENT_PRINCIPAL_NAME + "@" + REALM;
+    public static final String SERVER_PROTOCOL = "AMQP";
+    public static final String SERVICE_PRINCIPAL_NAME = SERVER_PROTOCOL + "/" + HOST_NAME;
+    public static final String ACCEPT_SCOPE = isIBM() ? "com.ibm.security.jgss.krb5.accept" : "com.sun.security.jgss.accept";
+    private static final String USE_SUBJECT_CREDS_ONLY = "javax.security.auth.useSubjectCredsOnly";
+    public static final String LOGIN_CONFIG = "java.security.auth.login.config";
+
+    private static final String INITIATE_SCOPE = isIBM() ? "com.ibm.security.jgss.krb5.initiate" : "com.sun.security.jgss.initiate";
     private static final Logger LOGGER = LoggerFactory.getLogger(KerberosUtilities.class);
     private static final String IBM_LOGIN_MODULE_CLASS = "com.ibm.security.auth.module.Krb5LoginModule";
     private static final String SUN_LOGIN_MODULE_CLASS = "com.sun.security.auth.module.Krb5LoginModule";
-    public static final String KERBEROS_LOGIN_MODULE_CLASS =
-            System.getProperty("java.vendor").contains("IBM") ? IBM_LOGIN_MODULE_CLASS : SUN_LOGIN_MODULE_CLASS;
+    private static final String KERBEROS_LOGIN_MODULE_CLASS = isIBM() ? IBM_LOGIN_MODULE_CLASS : SUN_LOGIN_MODULE_CLASS;
+    private static final String LOGIN_CONFIG_RESOURCE = "login.config";
+    private static final String LOGIN_IBM_CONFIG_RESOURCE = "login.ibm.config";
+    private static final String SERVICE_PRINCIPAL_FULL_NAME = SERVICE_PRINCIPAL_NAME + "@" + REALM;
+    private static final String BROKER_KEYTAB = "broker.keytab";
+    private static final String CLIENT_KEYTAB = "client.keytab";
+
+
+    public File prepareKeyTabs(final EmbeddedKdcResource kdc) throws Exception
+    {
+        final File clientKeyTabFile;
+        kdc.createPrincipal(BROKER_KEYTAB, SERVICE_PRINCIPAL_FULL_NAME);
+        clientKeyTabFile = kdc.createPrincipal(CLIENT_KEYTAB, CLIENT_PRINCIPAL_FULL_NAME);
+        return clientKeyTabFile;
+    }
+
+    public void prepareConfiguration(final String hostName, final SystemPropertySetter systemPropertySetter)
+            throws IOException
+    {
+        final Path loginConfig = transformLoginConfig(hostName);
+        systemPropertySetter.setSystemProperty(LOGIN_CONFIG,
+                                               URLDecoder.decode(loginConfig.toFile().getAbsolutePath(), UTF_8.name()));
+        systemPropertySetter.setSystemProperty(USE_SUBJECT_CREDS_ONLY, "false");
+    }
+
+    public byte[] buildToken(String clientPrincipalName, File clientKeyTabFile, String targetServerPrincipalName)
+            throws Exception
+    {
+        final LoginContext lc = createKerberosKeyTabLoginContext("test",
+                                                                 clientPrincipalName,
+                                                                 clientKeyTabFile);
+
+        Subject clientSubject = null;
+        String useSubjectCredsOnly = System.getProperty(USE_SUBJECT_CREDS_ONLY);
+        try
+        {
+            debug("Before login");
+            lc.login();
+            clientSubject = lc.getSubject();
+            debug("LoginContext subject {}", clientSubject);
+            System.setProperty(USE_SUBJECT_CREDS_ONLY, "true");
+            return Subject.doAs(clientSubject,
+                                (PrivilegedExceptionAction<byte[]>) () -> buildTokenWithinSubjectWithKerberosTicket(
+                                        clientPrincipalName,
+                                        targetServerPrincipalName));
+        }
+        finally
+        {
+            if (useSubjectCredsOnly == null)
+            {
+                System.clearProperty(USE_SUBJECT_CREDS_ONLY);
+            }
+            else
+            {
+                System.setProperty(USE_SUBJECT_CREDS_ONLY, useSubjectCredsOnly);
+            }
+            if (clientSubject != null)
+            {
+                lc.logout();
+            }
+        }
+    }
 
-    public byte[] buildToken(String clientPrincipalName, String targetServerPrincipalName) throws GSSException
+    private byte[] buildTokenWithinSubjectWithKerberosTicket(String clientPrincipalName,
+                                                             String targetServerPrincipalName) throws GSSException
     {
+        debug("Building token for client principal '{}' and server principal '{}'",
+              clientPrincipalName,
+              targetServerPrincipalName);
+
         final GSSManager manager = GSSManager.getInstance();
         final GSSName clientName = manager.createName(clientPrincipalName, GSSName.NT_USER_NAME);
-        final GSSCredential credential = manager.createCredential(clientName,
-                                                                  GSSCredential.DEFAULT_LIFETIME,
-                                                                  new Oid("1.2.840.113554.1.2.2"),
-                                                                  GSSCredential.INITIATE_ONLY);
+        final GSSCredential credential;
+        try
+        {
+            credential = manager.createCredential(clientName,
+                                                  GSSCredential.DEFAULT_LIFETIME,
+                                                  new Oid("1.2.840.113554.1.2.2"),
+                                                  GSSCredential.INITIATE_ONLY);
+        }
+        catch (GSSException e)
+        {
+            debug("Failure to create credential for {}", clientName, e);
+            throw e;
+        }
+
+        debug("Client credential '{}'", credential);
 
         final GSSName serverName = manager.createName(targetServerPrincipalName, GSSName.NT_USER_NAME);
         final Oid spnegoMechOid = new Oid("1.3.6.1.5.5.2");
@@ -70,11 +173,20 @@ public class KerberosUtilities
                                                                spnegoMechOid,
                                                                credential,
                                                                GSSContext.DEFAULT_LIFETIME);
+
+        debug("Requesting ticket using initiator's credentials");
+
         try
         {
             clientContext.requestCredDeleg(true);
+            debug("Requesting ticket");
             return clientContext.initSecContext(new byte[]{}, 0, 0);
         }
+        catch (GSSException e)
+        {
+            debug("Failure to request token", e);
+            throw e;
+        }
         finally
         {
             clientContext.dispose();
@@ -166,10 +278,19 @@ public class KerberosUtilities
         {
             final Map<String, String> options = new HashMap<>();
             options.put("principal", principalName);
-            options.put("useKeyTab", TRUE.toString());
-            options.put("keyTab", keyTabFile.getAbsolutePath());
-            options.put("refreshKrb5Config", TRUE.toString());
-            options.put("doNotPrompt", TRUE.toString());
+
+            if (isIBM())
+            {
+                options.put("useKeytab", keyTabFile.getAbsolutePath());
+                options.put("credsType", "both");
+            }
+            else
+            {
+                options.put("keyTab", keyTabFile.getAbsolutePath());
+                options.put("useKeyTab", TRUE.toString());
+                options.put("doNotPrompt", TRUE.toString());
+                options.put("refreshKrb5Config", TRUE.toString());
+            }
             _entry = new AppConfigurationEntry(KERBEROS_LOGIN_MODULE_CLASS,
                                                AppConfigurationEntry.LoginModuleControlFlag.REQUIRED,
                                                options);
@@ -186,4 +307,57 @@ public class KerberosUtilities
             return new AppConfigurationEntry[0];
         }
     }
+
+    public void debug(String message, Object... args)
+    {
+        LOGGER.debug(message, args);
+        if (Boolean.TRUE.toString().equalsIgnoreCase(System.getProperty("sun.security.krb5.debug")))
+        {
+            System.out.println(String.format(message.replace("{}", "%s"), args));
+        }
+    }
+
+    private Path transformLoginConfig(String hostName) throws IOException
+    {
+        final String resourceName = isIBM() ? LOGIN_IBM_CONFIG_RESOURCE : LOGIN_CONFIG_RESOURCE;
+        final URL resource = KerberosUtilities.class.getClassLoader().getResource(resourceName);
+        if (resource == null)
+        {
+            throw new IllegalArgumentException(String.format("Unknown resource '%s'", resourceName));
+        }
+        final String config;
+        try (InputStream is = resource.openStream())
+        {
+            config = new String(ByteStreams.toByteArray(is), UTF_8);
+        }
+        catch (IOException e)
+        {
+            throw new IOException(String.format("Failed to load resource '%s'", resource.toExternalForm()), e);
+        }
+        final String newConfig = config.replace("AMQP/localhost", "AMQP/" + hostName)
+                                       .replace("target/" + BROKER_KEYTAB, toAbsolutePath(BROKER_KEYTAB))
+                                       .replace("target/" + CLIENT_KEYTAB, toAbsolutePath(CLIENT_KEYTAB));
+
+        final Path file = Paths.get("target", LOGIN_CONFIG_RESOURCE);
+        Files.write(file,
+                    newConfig.getBytes(UTF_8),
+                    StandardOpenOption.WRITE,
+                    StandardOpenOption.CREATE,
+                    StandardOpenOption.TRUNCATE_EXISTING);
+        return file.toRealPath(LinkOption.NOFOLLOW_LINKS);
+    }
+
+    private String toAbsolutePath(String fileName)
+    {
+        final Path path = Paths.get("target", fileName)
+                               .toAbsolutePath()
+                               .normalize();
+        return path.toUri().getPath();
+    }
+
+    private static boolean isIBM()
+    {
+        return JvmVendor.getJvmVendor() == JvmVendor.IBM;
+    }
+
 }
diff --git a/broker-core/src/test/resources/login.ibm.config b/broker-core/src/test/resources/login.ibm.config
new file mode 100644
index 0000000..6e85f6e
--- /dev/null
+++ b/broker-core/src/test/resources/login.ibm.config
@@ -0,0 +1,51 @@
+/*
+ * 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.
+ */
+
+ldap-gssapi-bind {
+    com.ibm.security.auth.module.Krb5LoginModule required
+    credsType="both"
+    principal="service/localhost@QPID.ORG"
+    useKeytab="target/kerberos.keytab";
+};
+
+ldap-gssapi-bind-broken {
+    com.ibm.security.auth.module.Krb5LoginModule required
+    credsType="both"
+    principal="service/localhost@QPID.ORG"
+    useKeytab="target/kerberos-non-existing.keytab";
+};
+
+qpid-broker-j {
+    com.ibm.security.auth.module.Krb5LoginModule required
+    credsType="both"
+    principal="service/localhost@QPID.ORG"
+    useKeytab="target/kerberos.keytab";
+};
+
+com.ibm.security.jgss.krb5.accept {
+    com.ibm.security.auth.module.Krb5LoginModule required
+    credsType="acceptor"
+    principal="AMQP/localhost@QPID.ORG"
+    useKeytab="target/broker.keytab";
+};
+
+com.ibm.security.jgss.krb5.initiate {
+    com.ibm.security.auth.module.Krb5LoginModule required
+    credsType="both"
+    principal="client@QPID.ORG"
+    useKeytab="target/client.keytab";
+};
diff --git a/qpid-test-utils/src/main/java/org/apache/qpid/test/utils/JvmVendor.java b/qpid-test-utils/src/main/java/org/apache/qpid/test/utils/JvmVendor.java
index bd176d7..9ed7c28 100644
--- a/qpid-test-utils/src/main/java/org/apache/qpid/test/utils/JvmVendor.java
+++ b/qpid-test-utils/src/main/java/org/apache/qpid/test/utils/JvmVendor.java
@@ -20,10 +20,33 @@
 
 package org.apache.qpid.test.utils;
 
+import com.google.common.base.StandardSystemProperty;
+
 public enum JvmVendor
 {
     ORACLE,
     IBM,
     OPENJDK,
-    UNKNOWN
+    UNKNOWN;
+
+    public static JvmVendor getJvmVendor()
+    {
+        final String property = String.valueOf(System.getProperty(StandardSystemProperty.JAVA_VENDOR.key())).toUpperCase();
+        if (property.contains("IBM"))
+        {
+            return JvmVendor.IBM;
+        }
+        else if (property.contains("ORACLE"))
+        {
+            return JvmVendor.ORACLE;
+        }
+        else if (property.contains("OPENJDK"))
+        {
+            return JvmVendor.OPENJDK;
+        }
+        else
+        {
+            return JvmVendor.UNKNOWN;
+        }
+    }
 }
diff --git a/qpid-test-utils/src/main/java/org/apache/qpid/test/utils/UnitTestBase.java b/qpid-test-utils/src/main/java/org/apache/qpid/test/utils/UnitTestBase.java
index 4dcfab0..4d71700 100644
--- a/qpid-test-utils/src/main/java/org/apache/qpid/test/utils/UnitTestBase.java
+++ b/qpid-test-utils/src/main/java/org/apache/qpid/test/utils/UnitTestBase.java
@@ -77,23 +77,7 @@ public class UnitTestBase
 
     public static JvmVendor getJvmVendor()
     {
-        final String property = String.valueOf(System.getProperty(StandardSystemProperty.JAVA_VENDOR.key())).toUpperCase();
-        if (property.contains("IBM"))
-        {
-            return JvmVendor.IBM;
-        }
-        else if (property.contains("ORACLE"))
-        {
-            return JvmVendor.ORACLE;
-        }
-        else if (property.contains("OPENJDK"))
-        {
-            return JvmVendor.OPENJDK;
-        }
-        else
-        {
-            return JvmVendor.UNKNOWN;
-        }
+        return JvmVendor.getJvmVendor();
     }
 
     public VirtualHostNodeStoreType getVirtualHostNodeStoreType()


---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@qpid.apache.org
For additional commands, e-mail: commits-help@qpid.apache.org