You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@qpid.apache.org by ro...@apache.org on 2017/07/31 17:23:22 UTC

[1/2] qpid-jms git commit: QPIDJMS-303: Initial work on SASL GSSAPI/Kerberos authentication, additional changes to come. This closes #10.

Repository: qpid-jms
Updated Branches:
  refs/heads/master a08ebec01 -> ce833c7b6


QPIDJMS-303: Initial work on SASL GSSAPI/Kerberos authentication, additional changes to come. This closes #10.


Project: http://git-wip-us.apache.org/repos/asf/qpid-jms/repo
Commit: http://git-wip-us.apache.org/repos/asf/qpid-jms/commit/6dff7ac9
Tree: http://git-wip-us.apache.org/repos/asf/qpid-jms/tree/6dff7ac9
Diff: http://git-wip-us.apache.org/repos/asf/qpid-jms/diff/6dff7ac9

Branch: refs/heads/master
Commit: 6dff7ac9a5c2da24cbf1b3179210b41331f2c77f
Parents: a08ebec
Author: gtully <ga...@gmail.com>
Authored: Thu Jul 20 17:55:07 2017 +0100
Committer: Robert Gemmell <ro...@apache.org>
Committed: Mon Jul 31 18:21:33 2017 +0100

----------------------------------------------------------------------
 pom.xml                                         |  14 ++
 qpid-jms-client/pom.xml                         |  11 +
 .../qpid/jms/provider/amqp/AmqpProvider.java    |  18 +-
 .../provider/amqp/AmqpSaslAuthenticator.java    |   4 +-
 .../apache/qpid/jms/sasl/AbstractMechanism.java |   4 +-
 .../apache/qpid/jms/sasl/GssapiMechanism.java   | 176 +++++++++++++++
 .../qpid/jms/sasl/GssapiMechanismFactory.java   |  25 +++
 .../services/org/apache/qpid/jms/sasl/GSSAPI    |  17 ++
 .../integration/SaslGssApiIntegrationTest.java  | 217 +++++++++++++++++++
 .../qpid/jms/test/testpeer/TestAmqpPeer.java    | 169 +++++++++++++++
 .../SaslGssApiIntegrationTest-login.config      |  23 ++
 .../src/test/resources/log4j.properties         |   4 +
 .../src/test/resources/minikdc-krb5.conf        |  26 +++
 13 files changed, 705 insertions(+), 3 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/qpid-jms/blob/6dff7ac9/pom.xml
----------------------------------------------------------------------
diff --git a/pom.xml b/pom.xml
index 7cc1c04..bace1ac 100644
--- a/pom.xml
+++ b/pom.xml
@@ -54,6 +54,8 @@
     <jetty-version>9.2.13.v20150730</jetty-version>
     <mockito-version>1.10.19</mockito-version>
     <hamcrest-version>1.3</hamcrest-version>
+    <hadoop-minikdc-version>2.8.1</hadoop-minikdc-version>
+    <directory-jdbm2-version>2.0.0-M3</directory-jdbm2-version>
 
     <!-- Maven Plugin Versions for this Project -->
     <maven-javacc-plugin-version>2.6</maven-javacc-plugin-version>
@@ -180,6 +182,18 @@
         <version>${hamcrest-version}</version>
         <scope>test</scope>
       </dependency>
+      <dependency>
+        <groupId>org.apache.hadoop</groupId>
+        <artifactId>hadoop-minikdc</artifactId>
+        <version>${hadoop-minikdc-version}</version>
+        <scope>test</scope>
+      </dependency>
+      <dependency>
+        <groupId>org.apache.directory.jdbm</groupId>
+        <artifactId>apacheds-jdbm2</artifactId>
+        <version>${directory-jdbm2-version}</version>
+        <scope>test</scope>
+      </dependency>
     </dependencies>
   </dependencyManagement>
 

http://git-wip-us.apache.org/repos/asf/qpid-jms/blob/6dff7ac9/qpid-jms-client/pom.xml
----------------------------------------------------------------------
diff --git a/qpid-jms-client/pom.xml b/qpid-jms-client/pom.xml
index abbd0ba..7e6e971 100644
--- a/qpid-jms-client/pom.xml
+++ b/qpid-jms-client/pom.xml
@@ -93,6 +93,17 @@
       <artifactId>hamcrest-all</artifactId>
       <scope>test</scope>
     </dependency>
+    <dependency>
+      <groupId>org.apache.hadoop</groupId>
+      <artifactId>hadoop-minikdc</artifactId>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.directory.jdbm</groupId>
+      <artifactId>apacheds-jdbm2</artifactId>
+      <scope>test</scope>
+    </dependency>
+
   </dependencies>
 
   <build>

http://git-wip-us.apache.org/repos/asf/qpid-jms/blob/6dff7ac9/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/amqp/AmqpProvider.java
----------------------------------------------------------------------
diff --git a/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/amqp/AmqpProvider.java b/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/amqp/AmqpProvider.java
index 3bd7537..abedabc 100644
--- a/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/amqp/AmqpProvider.java
+++ b/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/amqp/AmqpProvider.java
@@ -21,6 +21,7 @@ import java.net.URI;
 import java.nio.ByteBuffer;
 import java.util.HashSet;
 import java.util.List;
+import java.util.Map;
 import java.util.Set;
 import java.util.concurrent.Future;
 import java.util.concurrent.ScheduledExecutorService;
@@ -57,11 +58,13 @@ import org.apache.qpid.jms.provider.ProviderFuture;
 import org.apache.qpid.jms.provider.ProviderListener;
 import org.apache.qpid.jms.provider.amqp.builders.AmqpClosedConnectionBuilder;
 import org.apache.qpid.jms.provider.amqp.builders.AmqpConnectionBuilder;
+import org.apache.qpid.jms.sasl.GssapiMechanism;
 import org.apache.qpid.jms.sasl.Mechanism;
 import org.apache.qpid.jms.sasl.SaslMechanismFinder;
 import org.apache.qpid.jms.transports.Transport;
 import org.apache.qpid.jms.transports.TransportListener;
 import org.apache.qpid.jms.util.IOExceptionSupport;
+import org.apache.qpid.jms.util.PropertyUtil;
 import org.apache.qpid.jms.util.QpidJMSThreadFactory;
 import org.apache.qpid.jms.util.ThreadPoolUtils;
 import org.apache.qpid.proton.engine.Collector;
@@ -1379,7 +1382,20 @@ public class AmqpProvider implements Provider, TransportListener , AmqpResourceP
         if (mechanism != null) {
             mechanism.setUsername(connectionInfo.getUsername());
             mechanism.setPassword(connectionInfo.getPassword());
-            // TODO - set additional options from URI.
+
+            if (GssapiMechanism.NAME.equals(mechanism.getName())) {
+                try {
+                    Map<String, String> props =
+                            PropertyUtil.filterProperties(PropertyUtil.parseQuery(getRemoteURI()), "sasl.");
+                    if (!props.containsKey("serverName")) {
+                        props.put("serverName", remoteURI.getHost());
+                    }
+                    PropertyUtil.setProperties(mechanism, props);
+                    PropertyUtil.setProperty(mechanism, "options", props);
+                } catch (Exception badConfig) {
+                    throw new RuntimeException("Failed to apply sasl url params to mechanism: " + mechanism.getName() + ", reason: " + badConfig.toString(), badConfig);
+                }
+            }
         }
 
         return mechanism;

http://git-wip-us.apache.org/repos/asf/qpid-jms/blob/6dff7ac9/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/amqp/AmqpSaslAuthenticator.java
----------------------------------------------------------------------
diff --git a/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/amqp/AmqpSaslAuthenticator.java b/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/amqp/AmqpSaslAuthenticator.java
index 456f9ef..bac8e3e 100644
--- a/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/amqp/AmqpSaslAuthenticator.java
+++ b/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/amqp/AmqpSaslAuthenticator.java
@@ -119,7 +119,9 @@ public class AmqpSaslAuthenticator {
                 byte[] challenge = new byte[sasl.pending()];
                 sasl.recv(challenge, 0, challenge.length);
                 byte[] response = mechanism.getChallengeResponse(challenge);
-                sasl.send(response, 0, response.length);
+                if (response != null) {
+                    sasl.send(response, 0, response.length);
+                }
             }
         } catch (Throwable error) {
             recordFailure("Exception while processing SASL step: " + error.getMessage(), error);

http://git-wip-us.apache.org/repos/asf/qpid-jms/blob/6dff7ac9/qpid-jms-client/src/main/java/org/apache/qpid/jms/sasl/AbstractMechanism.java
----------------------------------------------------------------------
diff --git a/qpid-jms-client/src/main/java/org/apache/qpid/jms/sasl/AbstractMechanism.java b/qpid-jms-client/src/main/java/org/apache/qpid/jms/sasl/AbstractMechanism.java
index 40efa6a..783029c 100644
--- a/qpid-jms-client/src/main/java/org/apache/qpid/jms/sasl/AbstractMechanism.java
+++ b/qpid-jms-client/src/main/java/org/apache/qpid/jms/sasl/AbstractMechanism.java
@@ -16,6 +16,8 @@
  */
 package org.apache.qpid.jms.sasl;
 
+import javax.security.sasl.SaslException;
+
 /**
  * Base class for SASL Authentication Mechanism that implements the basic
  * methods of a Mechanism class.
@@ -28,7 +30,7 @@ public abstract class AbstractMechanism implements Mechanism {
     private String password;
 
     @Override
-    public void verifyCompletion() {
+    public void verifyCompletion() throws SaslException {
     }
 
     @Override

http://git-wip-us.apache.org/repos/asf/qpid-jms/blob/6dff7ac9/qpid-jms-client/src/main/java/org/apache/qpid/jms/sasl/GssapiMechanism.java
----------------------------------------------------------------------
diff --git a/qpid-jms-client/src/main/java/org/apache/qpid/jms/sasl/GssapiMechanism.java b/qpid-jms-client/src/main/java/org/apache/qpid/jms/sasl/GssapiMechanism.java
new file mode 100644
index 0000000..e2644f8
--- /dev/null
+++ b/qpid-jms-client/src/main/java/org/apache/qpid/jms/sasl/GssapiMechanism.java
@@ -0,0 +1,176 @@
+/*
+ * 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.qpid.jms.sasl;
+
+import org.apache.qpid.jms.util.PropertyUtil;
+
+import javax.security.auth.Subject;
+import javax.security.auth.login.AppConfigurationEntry;
+import javax.security.auth.login.Configuration;
+import javax.security.auth.login.LoginContext;
+import javax.security.sasl.Sasl;
+import javax.security.sasl.SaslClient;
+import javax.security.sasl.SaslException;
+import java.security.Principal;
+import java.security.PrivilegedActionException;
+import java.security.PrivilegedExceptionAction;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Implements the GSSAPI sasl authentication Mechanism.
+ */
+public class GssapiMechanism extends AbstractMechanism {
+
+    public static final String NAME = "GSSAPI";
+    private Subject subject;
+    private SaslClient saslClient;
+    private String protocol = "amqp";
+    private String serverName = null;
+    private String configScope = null;
+    private Map<String, String> options = new HashMap<String, String>();
+
+    // a gss/sasl service name, x@y, morphs to a krbPrincipal a/y@REALM
+
+    @Override
+    public int getPriority() {
+        return PRIORITY.LOW.getValue();
+    }
+
+    @Override
+    public String getName() {
+        return NAME;
+    }
+
+    @Override
+    public byte[] getInitialResponse() throws SaslException {
+        try {
+            LoginContext loginContext = null;
+            if (configScope != null) {
+                loginContext = new LoginContext(configScope);
+            } else {
+                // inline keytab config using user as principal
+                loginContext = new LoginContext("", null, null,
+                        kerb5InlineConfig(getUsername(), options));
+            }
+            loginContext.login();
+            subject = loginContext.getSubject();
+
+            return Subject.doAs(subject, new PrivilegedExceptionAction<byte[]>() {
+
+                @Override
+                public byte[] run() throws Exception {
+                    saslClient = Sasl.createSaslClient(new String[]{NAME}, null, protocol, serverName, null, null);
+                    if (saslClient.hasInitialResponse()) {
+                        return saslClient.evaluateChallenge(new byte[0]);
+                    }
+                    return null;
+                }
+            });
+        } catch (Exception e) {
+            throw new SaslException(e.toString(), e);
+        }
+    }
+
+    @Override
+    public byte[] getChallengeResponse(final byte[] challenge) throws SaslException {
+        try {
+            return Subject.doAs(subject, new PrivilegedExceptionAction<byte[]>() {
+                @Override
+                public byte[] run() throws Exception {
+                    return saslClient.evaluateChallenge(challenge);
+                }
+            });
+        } catch (PrivilegedActionException e) {
+            throw new SaslException(e.toString(), e);
+        }
+    }
+
+    @Override
+    public void verifyCompletion() throws SaslException {
+        boolean result = saslClient.isComplete();
+        saslClient.dispose();
+        if (!result) {
+            throw new SaslException("not complete");
+        }
+    }
+
+
+    @Override
+    public boolean isApplicable(String username, String password, Principal localPrincipal) {
+        return true;
+    }
+
+    public static Configuration kerb5InlineConfig(String principal, final Map<String, String> userOptions) {
+        final Map<String, String> options = new HashMap<>();
+        options.put("principal", principal);
+        options.put("useKeyTab", "true");
+        options.put("storeKey", "true");
+        String ticketCache = System.getenv("KRB5CCNAME");
+        if (ticketCache != null) {
+            options.put("ticketCache", ticketCache);
+        }
+        options.putAll(PropertyUtil.filterProperties(userOptions, "krb5."));
+        return new Configuration() {
+            @Override
+            public AppConfigurationEntry[] getAppConfigurationEntry(String name) {
+                return new AppConfigurationEntry[]{
+                        new AppConfigurationEntry(getKrb5LoginModuleName(),
+                                AppConfigurationEntry.LoginModuleControlFlag.REQUIRED,
+                                options)};
+            }
+        };
+    }
+
+    private static final boolean IBM_JAVA =  System.getProperty("java.vendor").contains("IBM");
+    private static String getKrb5LoginModuleName() {
+        return IBM_JAVA ? "com.ibm.security.auth.module.Krb5LoginModule"
+                : "com.sun.security.auth.module.Krb5LoginModule";
+    }
+
+    public String getProtocol() {
+        return protocol;
+    }
+
+    public void setProtocol(String protocol) {
+        this.protocol = protocol;
+    }
+
+    public String getServerName() {
+        return serverName;
+    }
+
+    public void setServerName(String serverName) {
+        this.serverName = serverName;
+    }
+
+    public String getConfigScope() {
+        return configScope;
+    }
+
+    public void setConfigScope(String configScope) {
+        this.configScope = configScope;
+    }
+
+    public Map<String, String> getOptions() {
+        return options;
+    }
+
+    public void setOptions(Map<String, String> options) {
+        this.options = options;
+    }
+}

http://git-wip-us.apache.org/repos/asf/qpid-jms/blob/6dff7ac9/qpid-jms-client/src/main/java/org/apache/qpid/jms/sasl/GssapiMechanismFactory.java
----------------------------------------------------------------------
diff --git a/qpid-jms-client/src/main/java/org/apache/qpid/jms/sasl/GssapiMechanismFactory.java b/qpid-jms-client/src/main/java/org/apache/qpid/jms/sasl/GssapiMechanismFactory.java
new file mode 100644
index 0000000..b05f4fd
--- /dev/null
+++ b/qpid-jms-client/src/main/java/org/apache/qpid/jms/sasl/GssapiMechanismFactory.java
@@ -0,0 +1,25 @@
+/*
+ * 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.qpid.jms.sasl;
+
+public class GssapiMechanismFactory implements MechanismFactory {
+
+    @Override
+    public Mechanism createMechanism() {
+        return new GssapiMechanism();
+    }
+}

http://git-wip-us.apache.org/repos/asf/qpid-jms/blob/6dff7ac9/qpid-jms-client/src/main/resources/META-INF/services/org/apache/qpid/jms/sasl/GSSAPI
----------------------------------------------------------------------
diff --git a/qpid-jms-client/src/main/resources/META-INF/services/org/apache/qpid/jms/sasl/GSSAPI b/qpid-jms-client/src/main/resources/META-INF/services/org/apache/qpid/jms/sasl/GSSAPI
new file mode 100644
index 0000000..505385c
--- /dev/null
+++ b/qpid-jms-client/src/main/resources/META-INF/services/org/apache/qpid/jms/sasl/GSSAPI
@@ -0,0 +1,17 @@
+## ---------------------------------------------------------------------------
+## 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.
+## ---------------------------------------------------------------------------
+class=org.apache.qpid.jms.sasl.GssapiMechanismFactory

http://git-wip-us.apache.org/repos/asf/qpid-jms/blob/6dff7ac9/qpid-jms-client/src/test/java/org/apache/qpid/jms/integration/SaslGssApiIntegrationTest.java
----------------------------------------------------------------------
diff --git a/qpid-jms-client/src/test/java/org/apache/qpid/jms/integration/SaslGssApiIntegrationTest.java b/qpid-jms-client/src/test/java/org/apache/qpid/jms/integration/SaslGssApiIntegrationTest.java
new file mode 100644
index 0000000..60f8c73
--- /dev/null
+++ b/qpid-jms-client/src/test/java/org/apache/qpid/jms/integration/SaslGssApiIntegrationTest.java
@@ -0,0 +1,217 @@
+/*
+ *
+ * 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.qpid.jms.integration;
+
+import org.apache.directory.server.kerberos.shared.keytab.Keytab;
+import org.apache.directory.server.kerberos.shared.keytab.KeytabEntry;
+import org.apache.hadoop.minikdc.MiniKdc;
+import org.apache.qpid.jms.JmsConnectionFactory;
+import org.apache.qpid.jms.test.QpidJmsTestCase;
+import org.apache.qpid.jms.test.testpeer.TestAmqpPeer;
+import org.apache.qpid.proton.amqp.Symbol;
+import org.junit.After;
+import org.junit.Assume;
+import org.junit.Before;
+import org.junit.Test;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.jms.Connection;
+import javax.jms.ConnectionFactory;
+import javax.jms.JMSSecurityException;
+import java.io.File;
+import java.nio.file.Files;
+import java.nio.file.Path;
+
+import static junit.framework.TestCase.assertTrue;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.fail;
+
+public class SaslGssApiIntegrationTest extends QpidJmsTestCase {
+
+    private static final Logger LOG = LoggerFactory.getLogger(SaslGssApiIntegrationTest.class);
+
+    private static final Symbol GSSAPI = Symbol.valueOf("GSSAPI");
+    private static final String serviceName = "amqp/localhost";
+
+    private MiniKdc kdc;
+    private final boolean debug = false;
+
+    @Before
+    public void setUpKerberos() throws Exception {
+
+        // login.config or url overrides necessary for the IBM krb5 login module
+        Assume.assumeFalse(System.getProperty("java.vendor").contains("IBM"));
+
+        Path tempDirectory = Files.createTempDirectory("junit.SaslGssApiIntegrationTest.");
+        File root = tempDirectory.toFile();
+        root.deleteOnExit();
+        kdc = new MiniKdc(MiniKdc.createConf(), new File(root, "kdc"));
+        kdc.start();
+
+        // hard coded match, default_keytab_name in minikdc-krb5.conf template
+        File userKeyTab = new File("target/SaslGssApiIntegrationTest.krb5.keytab");
+        kdc.createPrincipal(userKeyTab, "client", serviceName);
+
+        Keytab kt = Keytab.read(userKeyTab);
+        for (KeytabEntry entry : kt.getEntries()) {
+            LOG.info("KeyTab Kerb PrincipalNames:" + entry.getPrincipalName());
+        }
+
+        if (debug) {
+            java.util.logging.Logger logger = java.util.logging.Logger.getLogger("javax.security.sasl");
+            logger.setLevel(java.util.logging.Level.FINEST);
+            logger.addHandler(new java.util.logging.ConsoleHandler());
+            for (java.util.logging.Handler handler : logger.getHandlers()) {
+                handler.setLevel(java.util.logging.Level.FINEST);
+            }
+        }
+    }
+
+    @After
+    public void stopKDC() throws Exception {
+        if (kdc != null) {
+            kdc.stop();
+        }
+    }
+
+    @Test(timeout = 20000)
+    public void testSaslGssApiKrbConnection() throws Exception {
+        try (TestAmqpPeer testPeer = new TestAmqpPeer();) {
+
+            testPeer.expectSaslGSSAPI(serviceName);
+            testPeer.expectOpen();
+
+            // Each connection creates a session for managing temporary destinations etc
+            testPeer.expectBegin();
+
+            String uriOptions = "?amqp.saslMechanisms=" + GSSAPI.toString();
+            ConnectionFactory factory = new JmsConnectionFactory("amqp://localhost:" + testPeer.getServerPort() + uriOptions);
+            Connection connection = factory.createConnection("client", null);
+            // Set a clientID to provoke the actual AMQP connection process to occur.
+            connection.setClientID("clientName");
+
+            testPeer.waitForAllHandlersToComplete(1000);
+            assertNull(testPeer.getThrowable());
+
+            testPeer.expectClose();
+            connection.close();
+        }
+    }
+
+    @Test(timeout = 20000)
+    public void testSaslGssApiKrbConnectionJmsUser() throws Exception {
+        try (TestAmqpPeer testPeer = new TestAmqpPeer();) {
+
+            testPeer.expectSaslGSSAPI(serviceName);
+            testPeer.expectOpen();
+
+            // Each connection creates a session for managing temporary destinations etc
+            testPeer.expectBegin();
+
+            String uriOptions = "?jms.username=client&amqp.saslMechanisms=" + GSSAPI.toString();
+            ConnectionFactory factory = new JmsConnectionFactory("amqp://localhost:" + testPeer.getServerPort() + uriOptions);
+            Connection connection = factory.createConnection();
+            // Set a clientID to provoke the actual AMQP connection process to occur.
+            connection.setClientID("clientName");
+
+            testPeer.waitForAllHandlersToComplete(1000);
+            assertNull(testPeer.getThrowable());
+
+            testPeer.expectClose();
+            connection.close();
+        }
+    }
+
+    @Test(timeout = 20000)
+    public void testSaslGssApiKrb5ConfigOptionOverridePrincipal() throws Exception {
+        try (TestAmqpPeer testPeer = new TestAmqpPeer();) {
+
+            testPeer.expectSaslGSSAPI(serviceName);
+            testPeer.expectOpen();
+
+            // Each connection creates a session for managing temporary destinations etc
+            testPeer.expectBegin();
+
+            String uriOptions = "?jms.username=getsOverridden&sasl.krb5.principal=client&amqp.saslMechanisms=" + GSSAPI.toString();
+            ConnectionFactory factory = new JmsConnectionFactory("amqp://localhost:" + testPeer.getServerPort() + uriOptions);
+            Connection connection = factory.createConnection();
+            // Set a clientID to provoke the actual AMQP connection process to occur.
+            connection.setClientID("clientName");
+
+            testPeer.waitForAllHandlersToComplete(1000);
+            assertNull(testPeer.getThrowable());
+
+            testPeer.expectClose();
+            connection.close();
+        }
+    }
+
+
+
+    @Test(timeout = 20000)
+    public void testSaslGssApiKrbConfigConnection() throws Exception {
+        setTestSystemProperty("java.security.auth.login.config",
+                SaslGssApiIntegrationTest.class.getClassLoader().getResource("SaslGssApiIntegrationTest-login.config").getPath());
+        try (TestAmqpPeer testPeer = new TestAmqpPeer();) {
+
+            testPeer.expectSaslGSSAPI(serviceName);
+            testPeer.expectOpen();
+
+            // Each connection creates a session for managing temporary destinations etc
+            testPeer.expectBegin();
+
+            String uriOptions = "?sasl.configScope=KRB5-CLIENT&sasl.protocol=amqp&sasl.server=localhost&amqp.saslMechanisms=" + GSSAPI.toString();
+            ConnectionFactory factory = new JmsConnectionFactory("amqp://localhost:" + testPeer.getServerPort() + uriOptions);
+            Connection connection = factory.createConnection();
+            // Set a clientID to provoke the actual AMQP connection process to occur.
+            connection.setClientID("clientName");
+
+            testPeer.waitForAllHandlersToComplete(1000);
+            assertNull(testPeer.getThrowable());
+
+            testPeer.expectClose();
+            connection.close();
+        }
+    }
+
+    @Test(timeout = 20000)
+    public void testSaslGssApiKrbConfigError() throws Exception {
+        final String loginConfigScope = "KRB5-CLIENT-DOES-NOT-EXIST";
+        try (TestAmqpPeer testPeer = new TestAmqpPeer();) {
+
+            testPeer.expectSaslGSSAPIFail();
+
+            String uriOptions = "?sasl.configScope=" + loginConfigScope + "&sasl.protocol=amqp&sasl.server=localhost&amqp.saslMechanisms=" + GSSAPI.toString();
+            ConnectionFactory factory = new JmsConnectionFactory("amqp://localhost:" + testPeer.getServerPort() + uriOptions);
+            Connection connection = factory.createConnection();
+            // Set a clientID to provoke the actual AMQP connection process to occur.
+            connection.setClientID("clientName");
+
+            testPeer.expectClose();
+            connection.close();
+            fail("Expect exception on no login config");
+        } catch (JMSSecurityException expected) {
+            assertTrue(expected.getMessage().contains(loginConfigScope));
+        }
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/qpid-jms/blob/6dff7ac9/qpid-jms-client/src/test/java/org/apache/qpid/jms/test/testpeer/TestAmqpPeer.java
----------------------------------------------------------------------
diff --git a/qpid-jms-client/src/test/java/org/apache/qpid/jms/test/testpeer/TestAmqpPeer.java b/qpid-jms-client/src/test/java/org/apache/qpid/jms/test/testpeer/TestAmqpPeer.java
index 3d99b75..da1e69a 100644
--- a/qpid-jms-client/src/test/java/org/apache/qpid/jms/test/testpeer/TestAmqpPeer.java
+++ b/qpid-jms-client/src/test/java/org/apache/qpid/jms/test/testpeer/TestAmqpPeer.java
@@ -21,6 +21,7 @@ package org.apache.qpid.jms.test.testpeer;
 import static org.apache.qpid.jms.provider.amqp.AmqpSupport.DYNAMIC_NODE_LIFETIME_POLICY;
 import static org.apache.qpid.jms.provider.amqp.AmqpSupport.GLOBAL;
 import static org.apache.qpid.jms.provider.amqp.AmqpSupport.SHARED;
+import static org.apache.qpid.jms.sasl.GssapiMechanism.kerb5InlineConfig;
 import static org.hamcrest.MatcherAssert.assertThat;
 import static org.hamcrest.Matchers.arrayContaining;
 import static org.hamcrest.Matchers.equalTo;
@@ -32,14 +33,26 @@ import static org.hamcrest.Matchers.nullValue;
 
 import java.io.IOException;
 import java.net.Socket;
+import java.security.PrivilegedActionException;
+import java.security.PrivilegedExceptionAction;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicBoolean;
 
 import javax.net.ssl.SSLContext;
+import javax.security.auth.Subject;
+import javax.security.auth.callback.Callback;
+import javax.security.auth.callback.CallbackHandler;
+import javax.security.auth.callback.UnsupportedCallbackException;
+import javax.security.auth.login.LoginContext;
+import javax.security.sasl.AuthorizeCallback;
+import javax.security.sasl.Sasl;
+import javax.security.sasl.SaslServer;
 
 import org.apache.qpid.jms.provider.amqp.AmqpSupport;
 import org.apache.qpid.jms.provider.amqp.message.AmqpDestinationHelper;
@@ -65,6 +78,7 @@ import org.apache.qpid.jms.test.testpeer.describedtypes.FlowFrame;
 import org.apache.qpid.jms.test.testpeer.describedtypes.FrameDescriptorMapping;
 import org.apache.qpid.jms.test.testpeer.describedtypes.OpenFrame;
 import org.apache.qpid.jms.test.testpeer.describedtypes.Released;
+import org.apache.qpid.jms.test.testpeer.describedtypes.SaslChallengeFrame;
 import org.apache.qpid.jms.test.testpeer.describedtypes.SaslMechanismsFrame;
 import org.apache.qpid.jms.test.testpeer.describedtypes.SaslOutcomeFrame;
 import org.apache.qpid.jms.test.testpeer.describedtypes.Source;
@@ -86,6 +100,7 @@ import org.apache.qpid.jms.test.testpeer.matchers.EndMatcher;
 import org.apache.qpid.jms.test.testpeer.matchers.FlowMatcher;
 import org.apache.qpid.jms.test.testpeer.matchers.OpenMatcher;
 import org.apache.qpid.jms.test.testpeer.matchers.SaslInitMatcher;
+import org.apache.qpid.jms.test.testpeer.matchers.SaslResponseMatcher;
 import org.apache.qpid.jms.test.testpeer.matchers.SourceMatcher;
 import org.apache.qpid.jms.test.testpeer.matchers.TargetMatcher;
 import org.apache.qpid.jms.test.testpeer.matchers.TransferMatcher;
@@ -99,6 +114,8 @@ import org.apache.qpid.proton.amqp.UnsignedInteger;
 import org.apache.qpid.proton.amqp.UnsignedShort;
 import org.apache.qpid.proton.codec.Data;
 import org.apache.qpid.proton.engine.impl.AmqpHeader;
+import org.hamcrest.BaseMatcher;
+import org.hamcrest.Description;
 import org.hamcrest.Matcher;
 import org.hamcrest.Matchers;
 import org.junit.Assert;
@@ -114,6 +131,7 @@ public class TestAmqpPeer implements AutoCloseable
     private static final Symbol ANONYMOUS = Symbol.valueOf("ANONYMOUS");
     private static final Symbol EXTERNAL = Symbol.valueOf("EXTERNAL");
     private static final Symbol PLAIN = Symbol.valueOf("PLAIN");
+    private static final Symbol GSSAPI = Symbol.valueOf("GSSAPI");
     private static final UnsignedByte SASL_OK = UnsignedByte.valueOf((byte)0);
     private static final UnsignedByte SASL_FAIL_AUTH = UnsignedByte.valueOf((byte)1);
     private static final int CONNECTION_CHANNEL = 0;
@@ -495,6 +513,157 @@ public class TestAmqpPeer implements AutoCloseable
         }
     }
 
+    public void expectSaslGSSAPIFail() throws Exception {
+        SaslMechanismsFrame saslMechanismsFrame = new SaslMechanismsFrame().setSaslServerMechanisms(GSSAPI);
+
+        addHandler(new HeaderHandlerImpl(AmqpHeader.SASL_HEADER, AmqpHeader.SASL_HEADER,
+                new FrameSender(
+                        this, FrameType.SASL, 0,
+                        saslMechanismsFrame, null)));
+
+        addHandler(new SaslInitMatcher().withMechanism(equalTo(GSSAPI)));
+
+    }
+
+    public void expectSaslGSSAPI(String serviceName) throws Exception {
+
+        SaslMechanismsFrame saslMechanismsFrame = new SaslMechanismsFrame().setSaslServerMechanisms(GSSAPI);
+
+        addHandler(new HeaderHandlerImpl(AmqpHeader.SASL_HEADER, AmqpHeader.SASL_HEADER,
+                new FrameSender(
+                        this, FrameType.SASL, 0,
+                        saslMechanismsFrame, null)));
+
+        final Map<String, String> options = new HashMap<>();
+        options.put("isInitiator", "false");
+
+        // setup server gss context
+        LoginContext loginContext = new LoginContext("", null, null,
+                kerb5InlineConfig(serviceName, options));
+        loginContext.login();
+        final Subject serverSubject =loginContext.getSubject();
+
+        LOGGER.info("saslServer subject:" + serverSubject.getPrivateCredentials());
+
+        Map<String, ?> config = new HashMap();
+        final CallbackHandler handler = new CallbackHandler() {
+            @Override
+            public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException {
+                LOGGER.info("Here with: " + Arrays.asList(callbacks));
+                for (Callback callback :callbacks) {
+                    if (callback instanceof AuthorizeCallback) {
+                        AuthorizeCallback authorizeCallback = (AuthorizeCallback) callback;
+                        authorizeCallback.setAuthorized(authorizeCallback.getAuthenticationID().equals(authorizeCallback.getAuthorizationID()));
+                    }
+                }
+            }
+        };
+        final SaslServer saslServer = Subject.doAs(serverSubject, new PrivilegedExceptionAction<SaslServer>() {
+            @Override
+            public SaslServer run() throws Exception {
+                return Sasl.createSaslServer(GSSAPI.toString(), null, null, config, handler);
+            }
+        });
+
+        final SaslChallengeFrame challengeFrame = new SaslChallengeFrame();
+
+        SaslInitMatcher saslInitMatcher = new SaslInitMatcher()
+                .withMechanism(equalTo(GSSAPI))
+                .withInitialResponse(new BaseMatcher<Binary>() {
+
+                    @Override
+                    public void describeTo(Description description) {}
+
+                    @Override
+                    public boolean matches(Object o) {
+                        if (o == null) {
+                            LOGGER.error("Got null initial response!");
+                            return false;
+                        }
+                        final Binary binary = (Binary) o;
+                        // validate via sasl
+                        try {
+                            byte[] token = Subject.doAs(serverSubject, new PrivilegedExceptionAction<byte[]>() {
+                                @Override
+                                public byte[] run() throws Exception {
+                                    LOGGER.info("Evaluate Response.. size:" + binary.getLength());
+                                    return saslServer.evaluateResponse(binary.getArray());
+                                }
+                            });
+
+                            challengeFrame.setChallenge(new Binary(token));
+
+                        } catch (PrivilegedActionException e) {
+                            e.printStackTrace();
+                            throw new RuntimeException("failed to eval response", e);
+                        }
+                        LOGGER.info("Complete:" + saslServer.isComplete());
+
+                        return true;
+                    }
+                }).onCompletion(new AmqpPeerRunnable() {
+                    @Override
+                    public void run() {
+                        LOGGER.info("Send challenge..");
+                        TestAmqpPeer.this.sendFrame(
+                                FrameType.SASL, 0,
+                                challengeFrame,
+                                null,
+                                false, 0);
+                    }
+                });
+
+        AtomicBoolean response = new AtomicBoolean(false);
+        SaslResponseMatcher challengeMatcher = new SaslResponseMatcher().withResponse(new BaseMatcher<Binary>() {
+
+            @Override
+            public void describeTo(Description description) {}
+
+            @Override
+            public boolean matches(Object o) {
+                final Binary binary = (Binary) o;
+                // validate via sasl
+                try {
+                    Subject.doAs(serverSubject, new PrivilegedExceptionAction<byte[]>() {
+                        @Override
+                        public byte[] run() throws Exception {
+                            LOGGER.info("Evaluate response.. size:" + binary.getLength());
+                            return saslServer.evaluateResponse(binary.getArray());
+                        }
+                    });
+                } catch (PrivilegedActionException e) {
+                    e.printStackTrace();
+                    throw new RuntimeException("failed to evaluate challenge response", e);
+                }
+                LOGGER.info("Complete:" + saslServer.isComplete());
+                return saslServer.isComplete();
+            }
+        }).onCompletion(new AmqpPeerRunnable() {
+            @Override
+            public void run() {
+
+                if (saslServer.isComplete()) {
+                    LOGGER.info("Authorized ID: " + saslServer.getAuthorizationID());
+                    LOGGER.info("Send Outcome");
+                    TestAmqpPeer.this.sendFrame(
+                            FrameType.SASL, 0,
+                            new SaslOutcomeFrame().setCode(SASL_OK),
+                            null,
+                            false, 0);
+
+                    // Now that we processed the SASL layer AMQP header, reset the
+                    // peer to expect the non-SASL AMQP header.
+                    _driverRunnable.expectHeader();
+                }
+            }
+        });
+
+        addHandler(saslInitMatcher);
+        addHandler(challengeMatcher);
+
+        addHandler(new HeaderHandlerImpl(AmqpHeader.HEADER, AmqpHeader.HEADER));
+    }
+
     public void expectSaslPlain(String username, String password)
     {
         byte[] usernameBytes = username.getBytes();

http://git-wip-us.apache.org/repos/asf/qpid-jms/blob/6dff7ac9/qpid-jms-client/src/test/resources/SaslGssApiIntegrationTest-login.config
----------------------------------------------------------------------
diff --git a/qpid-jms-client/src/test/resources/SaslGssApiIntegrationTest-login.config b/qpid-jms-client/src/test/resources/SaslGssApiIntegrationTest-login.config
new file mode 100644
index 0000000..6aa4123
--- /dev/null
+++ b/qpid-jms-client/src/test/resources/SaslGssApiIntegrationTest-login.config
@@ -0,0 +1,23 @@
+/*
+ * 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.
+ */
+
+KRB5-CLIENT {
+    com.sun.security.auth.module.Krb5LoginModule required
+    useKeyTab=true
+    principal="client"
+    keytab="target/SaslGssApiIntegrationTest.krb5.keytab";
+};

http://git-wip-us.apache.org/repos/asf/qpid-jms/blob/6dff7ac9/qpid-jms-client/src/test/resources/log4j.properties
----------------------------------------------------------------------
diff --git a/qpid-jms-client/src/test/resources/log4j.properties b/qpid-jms-client/src/test/resources/log4j.properties
index c5c11d1..eb2531a 100644
--- a/qpid-jms-client/src/test/resources/log4j.properties
+++ b/qpid-jms-client/src/test/resources/log4j.properties
@@ -23,6 +23,10 @@ log4j.rootLogger=TRACE, out, stdout
 log4j.logger.org.apache.qpid.jms=DEBUG
 log4j.logger.org.apache.qpid.jms.provider=TRACE
 
+# calm down MinkKdc OpenDirectory
+log4j.logger.org.apache.directory=WARN
+log4j.logger.jdbm=WARN
+
 # Tune the TestPeer as needed for debugging.
 log4j.logger.org.apache.qpid.jms.test.testpeer=TRACE
 

http://git-wip-us.apache.org/repos/asf/qpid-jms/blob/6dff7ac9/qpid-jms-client/src/test/resources/minikdc-krb5.conf
----------------------------------------------------------------------
diff --git a/qpid-jms-client/src/test/resources/minikdc-krb5.conf b/qpid-jms-client/src/test/resources/minikdc-krb5.conf
new file mode 100644
index 0000000..9645dec
--- /dev/null
+++ b/qpid-jms-client/src/test/resources/minikdc-krb5.conf
@@ -0,0 +1,26 @@
+#
+# 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.
+#
+[libdefaults]
+    default_realm = {0}
+    udp_preference_limit = 1
+    default_keytab_name = FILE:target/SaslGssApiIntegrationTest.krb5.keytab
+
+[realms]
+    {0} = '{'
+        kdc = {1}:{2}
+    '}'
\ No newline at end of file


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


[2/2] qpid-jms git commit: QPIDJMS-303: updates to round out GSSAPI/Kerberos authentication handling

Posted by ro...@apache.org.
QPIDJMS-303: updates to round out GSSAPI/Kerberos authentication handling

-Consolidate around login module config file, simplify options handling.
-Updates to support for passing credentials via URI/ConnectionFactory.
-Only allow the mechanism to be selected if configured to enable it.
-Improve handling of init-failure due to e.g config issues.
-Tests rework, e.g. reuse mini-kdc etc for efficiency, move temp state to ensure cleanup, drop unused dep, additional handling in testpeer.


Project: http://git-wip-us.apache.org/repos/asf/qpid-jms/repo
Commit: http://git-wip-us.apache.org/repos/asf/qpid-jms/commit/ce833c7b
Tree: http://git-wip-us.apache.org/repos/asf/qpid-jms/tree/ce833c7b
Diff: http://git-wip-us.apache.org/repos/asf/qpid-jms/diff/ce833c7b

Branch: refs/heads/master
Commit: ce833c7b6d72fcff3a5517ede0a22094c86d726a
Parents: 6dff7ac
Author: Robert Gemmell <ro...@apache.org>
Authored: Mon Jul 31 18:22:17 2017 +0100
Committer: Robert Gemmell <ro...@apache.org>
Committed: Mon Jul 31 18:22:17 2017 +0100

----------------------------------------------------------------------
 pom.xml                                         |   7 -
 qpid-jms-client/pom.xml                         |   6 -
 .../qpid/jms/provider/amqp/AmqpProvider.java    |  20 ++-
 .../provider/amqp/AmqpSaslAuthenticator.java    |   2 +-
 .../apache/qpid/jms/sasl/AbstractMechanism.java |  11 ++
 .../apache/qpid/jms/sasl/GssapiMechanism.java   |  86 +++++-----
 .../org/apache/qpid/jms/sasl/Mechanism.java     |  16 ++
 .../qpid/jms/sasl/SaslMechanismFinder.java      |  10 +-
 .../integration/SaslGssApiIntegrationTest.java  | 161 ++++++++++++-------
 .../qpid/jms/sasl/AbstractMechanismTest.java    |   5 +
 .../qpid/jms/sasl/AnonymousMechanismTest.java   |   7 +
 .../qpid/jms/sasl/CramMD5MechanismTest.java     |   7 +
 .../qpid/jms/sasl/ExternalMechanismTest.java    |   7 +
 .../qpid/jms/sasl/GssapiMechanismTest.java      |  39 +++++
 .../qpid/jms/sasl/PlainMechanismTest.java       |   7 +
 .../qpid/jms/sasl/ScramSHA1MechanismTest.java   |  11 ++
 .../qpid/jms/sasl/ScramSHA256MechanismTest.java |  11 ++
 .../qpid/jms/test/testpeer/TestAmqpPeer.java    |  85 ++++++----
 .../jms/test/testpeer/TestAmqpPeerRunner.java   |   2 +-
 .../SaslGssApiIntegrationTest-login.config      |  21 ++-
 20 files changed, 356 insertions(+), 165 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/qpid-jms/blob/ce833c7b/pom.xml
----------------------------------------------------------------------
diff --git a/pom.xml b/pom.xml
index bace1ac..c912587 100644
--- a/pom.xml
+++ b/pom.xml
@@ -55,7 +55,6 @@
     <mockito-version>1.10.19</mockito-version>
     <hamcrest-version>1.3</hamcrest-version>
     <hadoop-minikdc-version>2.8.1</hadoop-minikdc-version>
-    <directory-jdbm2-version>2.0.0-M3</directory-jdbm2-version>
 
     <!-- Maven Plugin Versions for this Project -->
     <maven-javacc-plugin-version>2.6</maven-javacc-plugin-version>
@@ -188,12 +187,6 @@
         <version>${hadoop-minikdc-version}</version>
         <scope>test</scope>
       </dependency>
-      <dependency>
-        <groupId>org.apache.directory.jdbm</groupId>
-        <artifactId>apacheds-jdbm2</artifactId>
-        <version>${directory-jdbm2-version}</version>
-        <scope>test</scope>
-      </dependency>
     </dependencies>
   </dependencyManagement>
 

http://git-wip-us.apache.org/repos/asf/qpid-jms/blob/ce833c7b/qpid-jms-client/pom.xml
----------------------------------------------------------------------
diff --git a/qpid-jms-client/pom.xml b/qpid-jms-client/pom.xml
index 7e6e971..b825bb9 100644
--- a/qpid-jms-client/pom.xml
+++ b/qpid-jms-client/pom.xml
@@ -98,12 +98,6 @@
       <artifactId>hadoop-minikdc</artifactId>
       <scope>test</scope>
     </dependency>
-    <dependency>
-      <groupId>org.apache.directory.jdbm</groupId>
-      <artifactId>apacheds-jdbm2</artifactId>
-      <scope>test</scope>
-    </dependency>
-
   </dependencies>
 
   <build>

http://git-wip-us.apache.org/repos/asf/qpid-jms/blob/ce833c7b/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/amqp/AmqpProvider.java
----------------------------------------------------------------------
diff --git a/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/amqp/AmqpProvider.java b/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/amqp/AmqpProvider.java
index abedabc..16a82a0 100644
--- a/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/amqp/AmqpProvider.java
+++ b/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/amqp/AmqpProvider.java
@@ -19,6 +19,7 @@ package org.apache.qpid.jms.provider.amqp;
 import java.io.IOException;
 import java.net.URI;
 import java.nio.ByteBuffer;
+import java.util.Collections;
 import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
@@ -1383,18 +1384,15 @@ public class AmqpProvider implements Provider, TransportListener , AmqpResourceP
             mechanism.setUsername(connectionInfo.getUsername());
             mechanism.setPassword(connectionInfo.getPassword());
 
-            if (GssapiMechanism.NAME.equals(mechanism.getName())) {
-                try {
-                    Map<String, String> props =
-                            PropertyUtil.filterProperties(PropertyUtil.parseQuery(getRemoteURI()), "sasl.");
-                    if (!props.containsKey("serverName")) {
-                        props.put("serverName", remoteURI.getHost());
-                    }
-                    PropertyUtil.setProperties(mechanism, props);
-                    PropertyUtil.setProperty(mechanism, "options", props);
-                } catch (Exception badConfig) {
-                    throw new RuntimeException("Failed to apply sasl url params to mechanism: " + mechanism.getName() + ", reason: " + badConfig.toString(), badConfig);
+            try {
+                Map<String, String> saslOptions = PropertyUtil.filterProperties(PropertyUtil.parseQuery(getRemoteURI()), "sasl.options.");
+                if (!saslOptions.containsKey("serverName")) {
+                    saslOptions.put("serverName", remoteURI.getHost());
                 }
+
+                mechanism.init(Collections.unmodifiableMap(saslOptions));
+            } catch (Exception ex) {
+                throw new RuntimeException("Failed to apply sasl options to mechanism: " + mechanism.getName() + ", reason: " + ex.toString(), ex);
             }
         }
 

http://git-wip-us.apache.org/repos/asf/qpid-jms/blob/ce833c7b/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/amqp/AmqpSaslAuthenticator.java
----------------------------------------------------------------------
diff --git a/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/amqp/AmqpSaslAuthenticator.java b/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/amqp/AmqpSaslAuthenticator.java
index bac8e3e..584dc6d 100644
--- a/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/amqp/AmqpSaslAuthenticator.java
+++ b/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/amqp/AmqpSaslAuthenticator.java
@@ -99,11 +99,11 @@ public class AmqpSaslAuthenticator {
             if (remoteMechanisms != null && remoteMechanisms.length != 0) {
                 mechanism = mechanismFinder.apply(remoteMechanisms);
                 if (mechanism != null) {
-                    sasl.setMechanisms(mechanism.getName());
                     byte[] response = mechanism.getInitialResponse();
                     if (response != null) {
                         sasl.send(response, 0, response.length);
                     }
+                    sasl.setMechanisms(mechanism.getName());
                 } else {
                     recordFailure("Could not find a suitable SASL mechanism for the remote peer using the available credentials.", null);
                 }

http://git-wip-us.apache.org/repos/asf/qpid-jms/blob/ce833c7b/qpid-jms-client/src/main/java/org/apache/qpid/jms/sasl/AbstractMechanism.java
----------------------------------------------------------------------
diff --git a/qpid-jms-client/src/main/java/org/apache/qpid/jms/sasl/AbstractMechanism.java b/qpid-jms-client/src/main/java/org/apache/qpid/jms/sasl/AbstractMechanism.java
index 783029c..098f3e5 100644
--- a/qpid-jms-client/src/main/java/org/apache/qpid/jms/sasl/AbstractMechanism.java
+++ b/qpid-jms-client/src/main/java/org/apache/qpid/jms/sasl/AbstractMechanism.java
@@ -16,6 +16,8 @@
  */
 package org.apache.qpid.jms.sasl;
 
+import java.util.Map;
+
 import javax.security.sasl.SaslException;
 
 /**
@@ -30,6 +32,10 @@ public abstract class AbstractMechanism implements Mechanism {
     private String password;
 
     @Override
+    public void init(Map<String, String> options) {
+    }
+
+    @Override
     public void verifyCompletion() throws SaslException {
     }
 
@@ -68,4 +74,9 @@ public abstract class AbstractMechanism implements Mechanism {
     public String toString() {
         return "SASL-" + getName();
     }
+
+    @Override
+    public boolean isEnabledByDefault() {
+        return true;
+    }
 }

http://git-wip-us.apache.org/repos/asf/qpid-jms/blob/ce833c7b/qpid-jms-client/src/main/java/org/apache/qpid/jms/sasl/GssapiMechanism.java
----------------------------------------------------------------------
diff --git a/qpid-jms-client/src/main/java/org/apache/qpid/jms/sasl/GssapiMechanism.java b/qpid-jms-client/src/main/java/org/apache/qpid/jms/sasl/GssapiMechanism.java
index e2644f8..86627fe 100644
--- a/qpid-jms-client/src/main/java/org/apache/qpid/jms/sasl/GssapiMechanism.java
+++ b/qpid-jms-client/src/main/java/org/apache/qpid/jms/sasl/GssapiMechanism.java
@@ -19,16 +19,20 @@ package org.apache.qpid.jms.sasl;
 import org.apache.qpid.jms.util.PropertyUtil;
 
 import javax.security.auth.Subject;
-import javax.security.auth.login.AppConfigurationEntry;
-import javax.security.auth.login.Configuration;
+import javax.security.auth.callback.Callback;
+import javax.security.auth.callback.CallbackHandler;
+import javax.security.auth.callback.NameCallback;
+import javax.security.auth.callback.PasswordCallback;
+import javax.security.auth.callback.UnsupportedCallbackException;
 import javax.security.auth.login.LoginContext;
 import javax.security.sasl.Sasl;
 import javax.security.sasl.SaslClient;
 import javax.security.sasl.SaslException;
+
+import java.io.IOException;
 import java.security.Principal;
 import java.security.PrivilegedActionException;
 import java.security.PrivilegedExceptionAction;
-import java.util.HashMap;
 import java.util.Map;
 
 /**
@@ -37,12 +41,12 @@ import java.util.Map;
 public class GssapiMechanism extends AbstractMechanism {
 
     public static final String NAME = "GSSAPI";
+
     private Subject subject;
     private SaslClient saslClient;
     private String protocol = "amqp";
     private String serverName = null;
-    private String configScope = null;
-    private Map<String, String> options = new HashMap<String, String>();
+    private String configScope = "amqp-jms-client";
 
     // a gss/sasl service name, x@y, morphs to a krbPrincipal a/y@REALM
 
@@ -57,16 +61,22 @@ public class GssapiMechanism extends AbstractMechanism {
     }
 
     @Override
+    public boolean isEnabledByDefault() {
+        // Only enable if given explicit configuration to do so, as we can't discern here
+        // whether the external configuration is appropriately set to actually allow its use.
+        return false;
+    }
+
+    @Override
+    public void init(Map<String, String> saslOptions) {
+        PropertyUtil.setProperties(this, saslOptions);
+    }
+
+    @Override
     public byte[] getInitialResponse() throws SaslException {
         try {
-            LoginContext loginContext = null;
-            if (configScope != null) {
-                loginContext = new LoginContext(configScope);
-            } else {
-                // inline keytab config using user as principal
-                loginContext = new LoginContext("", null, null,
-                        kerb5InlineConfig(getUsername(), options));
-            }
+            LoginContext loginContext = new LoginContext(configScope, new CredentialCallbackHandler());;
+
             loginContext.login();
             subject = loginContext.getSubject();
 
@@ -109,39 +119,11 @@ public class GssapiMechanism extends AbstractMechanism {
         }
     }
 
-
     @Override
     public boolean isApplicable(String username, String password, Principal localPrincipal) {
         return true;
     }
 
-    public static Configuration kerb5InlineConfig(String principal, final Map<String, String> userOptions) {
-        final Map<String, String> options = new HashMap<>();
-        options.put("principal", principal);
-        options.put("useKeyTab", "true");
-        options.put("storeKey", "true");
-        String ticketCache = System.getenv("KRB5CCNAME");
-        if (ticketCache != null) {
-            options.put("ticketCache", ticketCache);
-        }
-        options.putAll(PropertyUtil.filterProperties(userOptions, "krb5."));
-        return new Configuration() {
-            @Override
-            public AppConfigurationEntry[] getAppConfigurationEntry(String name) {
-                return new AppConfigurationEntry[]{
-                        new AppConfigurationEntry(getKrb5LoginModuleName(),
-                                AppConfigurationEntry.LoginModuleControlFlag.REQUIRED,
-                                options)};
-            }
-        };
-    }
-
-    private static final boolean IBM_JAVA =  System.getProperty("java.vendor").contains("IBM");
-    private static String getKrb5LoginModuleName() {
-        return IBM_JAVA ? "com.ibm.security.auth.module.Krb5LoginModule"
-                : "com.sun.security.auth.module.Krb5LoginModule";
-    }
-
     public String getProtocol() {
         return protocol;
     }
@@ -166,11 +148,23 @@ public class GssapiMechanism extends AbstractMechanism {
         this.configScope = configScope;
     }
 
-    public Map<String, String> getOptions() {
-        return options;
-    }
+    private class CredentialCallbackHandler implements CallbackHandler {
 
-    public void setOptions(Map<String, String> options) {
-        this.options = options;
+        @Override
+        public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException {
+            for (int i = 0; i < callbacks.length; i++) {
+                Callback cb = callbacks[i];
+                if (cb instanceof NameCallback) {
+                    ((NameCallback) cb).setName(getUsername());
+                } else if (cb instanceof PasswordCallback) {
+                    String pass = getPassword();
+                    if (pass != null) {
+                        ((PasswordCallback) cb).setPassword(pass.toCharArray());
+                    }
+                } else {
+                    throw new UnsupportedCallbackException(cb);
+                }
+            }
+        }
     }
 }

http://git-wip-us.apache.org/repos/asf/qpid-jms/blob/ce833c7b/qpid-jms-client/src/main/java/org/apache/qpid/jms/sasl/Mechanism.java
----------------------------------------------------------------------
diff --git a/qpid-jms-client/src/main/java/org/apache/qpid/jms/sasl/Mechanism.java b/qpid-jms-client/src/main/java/org/apache/qpid/jms/sasl/Mechanism.java
index ca8225d..53e71f2 100644
--- a/qpid-jms-client/src/main/java/org/apache/qpid/jms/sasl/Mechanism.java
+++ b/qpid-jms-client/src/main/java/org/apache/qpid/jms/sasl/Mechanism.java
@@ -17,6 +17,7 @@
 package org.apache.qpid.jms.sasl;
 
 import java.security.Principal;
+import java.util.Map;
 
 import javax.security.sasl.SaslException;
 
@@ -62,6 +63,14 @@ public interface Mechanism extends Comparable<Mechanism> {
     String getName();
 
     /**
+     * Perform any configuration initiation required by the mechanism.
+     *
+     * @param options
+     *        An immutable map of sasl options. Will always be non-null.
+     */
+    void init(Map<String, String> options);
+
+    /**
      * Create an initial response based on selected mechanism.
      *
      * May be null if there is no initial response.
@@ -139,4 +148,11 @@ public interface Mechanism extends Comparable<Mechanism> {
      */
     boolean isApplicable(String username, String password, Principal localPrincipal);
 
+    /**
+     * Allows the mechanism to indicate if it is enabled by default, or only when explicitly enabled
+     * through configuring the permitted sasl mechanisms.
+     *
+     * @return true if this Mechanism is enabled by default.
+     */
+    boolean isEnabledByDefault();
 }

http://git-wip-us.apache.org/repos/asf/qpid-jms/blob/ce833c7b/qpid-jms-client/src/main/java/org/apache/qpid/jms/sasl/SaslMechanismFinder.java
----------------------------------------------------------------------
diff --git a/qpid-jms-client/src/main/java/org/apache/qpid/jms/sasl/SaslMechanismFinder.java b/qpid-jms-client/src/main/java/org/apache/qpid/jms/sasl/SaslMechanismFinder.java
index 6ca6b9b..ce09489 100644
--- a/qpid-jms-client/src/main/java/org/apache/qpid/jms/sasl/SaslMechanismFinder.java
+++ b/qpid-jms-client/src/main/java/org/apache/qpid/jms/sasl/SaslMechanismFinder.java
@@ -73,10 +73,16 @@ public class SaslMechanismFinder {
             MechanismFactory factory = findMechanismFactory(remoteMechanism);
             if (factory != null) {
                 Mechanism mech = factory.createMechanism();
-                if(mechRestrictions != null && !mechRestrictions.contains(remoteMechanism)) {
+
+                boolean mechConfigured = mechRestrictions != null && mechRestrictions.contains(remoteMechanism);
+                if(mechRestrictions != null && !mechConfigured) {
                     LOG.debug("Skipping {} mechanism because it is not in the configured mechanisms restriction set", remoteMechanism);
                 } else if(mech.isApplicable(username, password, localPrincipal)) {
-                    found.add(mech);
+                    if(mech.isEnabledByDefault() || mechConfigured) {
+                        found.add(mech);
+                    } else {
+                        LOG.debug("Skipping {} mechanism as it must be explicitly enabled in the configured sasl mechanisms", mech);
+                    }
                 } else {
                     LOG.debug("Skipping {} mechanism because the available credentials are not sufficient", mech);
                 }

http://git-wip-us.apache.org/repos/asf/qpid-jms/blob/ce833c7b/qpid-jms-client/src/test/java/org/apache/qpid/jms/integration/SaslGssApiIntegrationTest.java
----------------------------------------------------------------------
diff --git a/qpid-jms-client/src/test/java/org/apache/qpid/jms/integration/SaslGssApiIntegrationTest.java b/qpid-jms-client/src/test/java/org/apache/qpid/jms/integration/SaslGssApiIntegrationTest.java
index 60f8c73..bcbb060 100644
--- a/qpid-jms-client/src/test/java/org/apache/qpid/jms/integration/SaslGssApiIntegrationTest.java
+++ b/qpid-jms-client/src/test/java/org/apache/qpid/jms/integration/SaslGssApiIntegrationTest.java
@@ -27,9 +27,10 @@ import org.apache.qpid.jms.JmsConnectionFactory;
 import org.apache.qpid.jms.test.QpidJmsTestCase;
 import org.apache.qpid.jms.test.testpeer.TestAmqpPeer;
 import org.apache.qpid.proton.amqp.Symbol;
-import org.junit.After;
+import org.junit.AfterClass;
 import org.junit.Assume;
 import org.junit.Before;
+import org.junit.BeforeClass;
 import org.junit.Test;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -38,6 +39,7 @@ import javax.jms.Connection;
 import javax.jms.ConnectionFactory;
 import javax.jms.JMSSecurityException;
 import java.io.File;
+import java.nio.file.FileSystems;
 import java.nio.file.Files;
 import java.nio.file.Path;
 
@@ -49,34 +51,40 @@ public class SaslGssApiIntegrationTest extends QpidJmsTestCase {
 
     private static final Logger LOG = LoggerFactory.getLogger(SaslGssApiIntegrationTest.class);
 
-    private static final Symbol GSSAPI = Symbol.valueOf("GSSAPI");
-    private static final String serviceName = "amqp/localhost";
-
-    private MiniKdc kdc;
-    private final boolean debug = false;
-
-    @Before
-    public void setUpKerberos() throws Exception {
-
-        // login.config or url overrides necessary for the IBM krb5 login module
-        Assume.assumeFalse(System.getProperty("java.vendor").contains("IBM"));
-
-        Path tempDirectory = Files.createTempDirectory("junit.SaslGssApiIntegrationTest.");
+    private static final String LOGIN_CONFIG = "SaslGssApiIntegrationTest-login.config";
+    private static final String GSSAPI = "GSSAPI";
+    private static final Symbol ANONYMOUS = Symbol.valueOf("ANONYMOUS");
+    private static final Symbol PLAIN = Symbol.valueOf("PLAIN");
+    private static final String KRB5_KEYTAB = "target/SaslGssApiIntegrationTest.krb5.keytab";
+    private static final String SERVICE_PRINCIPAL = "amqp/localhost";
+    private static final String CLIENT_PRINCIPAL_LOGIN_CONFIG = "clientprincipal";
+    private static final String CLIENT_PRINCIPAL_FACTORY_USERNAME = "factoryusername";
+    private static final String CLIENT_PRINCIPAL_URI_USERNAME = "uriusername";
+    private static final String CLIENT_PRINCIPAL_DEFAULT_CONFIG_SCOPE = "defaultscopeprincipal";
+
+    private static MiniKdc kdc;
+    private static final boolean DEBUG = false;
+
+    @BeforeClass
+    public static void setUpKerberos() throws Exception {
+        Path targetDir = FileSystems.getDefault().getPath("target");
+        Path tempDirectory = Files.createTempDirectory(targetDir, "junit.SaslGssApiIntegrationTest.");
         File root = tempDirectory.toFile();
-        root.deleteOnExit();
+
         kdc = new MiniKdc(MiniKdc.createConf(), new File(root, "kdc"));
         kdc.start();
 
         // hard coded match, default_keytab_name in minikdc-krb5.conf template
-        File userKeyTab = new File("target/SaslGssApiIntegrationTest.krb5.keytab");
-        kdc.createPrincipal(userKeyTab, "client", serviceName);
+        File userKeyTab = new File(KRB5_KEYTAB);
+        kdc.createPrincipal(userKeyTab, CLIENT_PRINCIPAL_LOGIN_CONFIG, CLIENT_PRINCIPAL_FACTORY_USERNAME,
+                CLIENT_PRINCIPAL_URI_USERNAME, CLIENT_PRINCIPAL_DEFAULT_CONFIG_SCOPE, SERVICE_PRINCIPAL);
 
         Keytab kt = Keytab.read(userKeyTab);
         for (KeytabEntry entry : kt.getEntries()) {
             LOG.info("KeyTab Kerb PrincipalNames:" + entry.getPrincipalName());
         }
 
-        if (debug) {
+        if (DEBUG) {
             java.util.logging.Logger logger = java.util.logging.Logger.getLogger("javax.security.sasl");
             logger.setLevel(java.util.logging.Level.FINEST);
             logger.addHandler(new java.util.logging.ConsoleHandler());
@@ -86,50 +94,51 @@ public class SaslGssApiIntegrationTest extends QpidJmsTestCase {
         }
     }
 
-    @After
-    public void stopKDC() throws Exception {
+    @AfterClass
+    public static void cleanUpKerberos() {
         if (kdc != null) {
             kdc.stop();
         }
     }
 
-    @Test(timeout = 20000)
-    public void testSaslGssApiKrbConnection() throws Exception {
-        try (TestAmqpPeer testPeer = new TestAmqpPeer();) {
-
-            testPeer.expectSaslGSSAPI(serviceName);
-            testPeer.expectOpen();
-
-            // Each connection creates a session for managing temporary destinations etc
-            testPeer.expectBegin();
+    @Before
+    @Override
+    public void setUp() throws Exception {
+        super.setUp();
 
-            String uriOptions = "?amqp.saslMechanisms=" + GSSAPI.toString();
-            ConnectionFactory factory = new JmsConnectionFactory("amqp://localhost:" + testPeer.getServerPort() + uriOptions);
-            Connection connection = factory.createConnection("client", null);
-            // Set a clientID to provoke the actual AMQP connection process to occur.
-            connection.setClientID("clientName");
+        Assume.assumeFalse(System.getProperty("java.vendor").contains("IBM"));
 
-            testPeer.waitForAllHandlersToComplete(1000);
-            assertNull(testPeer.getThrowable());
+        // NOTE: we may need to isolate this test later if we use login.config in others
+        setTestSystemProperty("java.security.auth.login.config",
+                SaslGssApiIntegrationTest.class.getClassLoader().getResource(LOGIN_CONFIG).getPath());
+    }
 
-            testPeer.expectClose();
-            connection.close();
-        }
+    @Test(timeout = 20000)
+    public void testSaslGssApiKrbConnection() throws Exception {
+        doSaslGssApiKrbConnectionTestImpl("KRB5-CLIENT", CLIENT_PRINCIPAL_LOGIN_CONFIG + "@EXAMPLE.COM");
     }
 
     @Test(timeout = 20000)
-    public void testSaslGssApiKrbConnectionJmsUser() throws Exception {
+    public void testSaslGssApiKrbConnectionWithDefaultScope() throws Exception {
+        doSaslGssApiKrbConnectionTestImpl(null, CLIENT_PRINCIPAL_DEFAULT_CONFIG_SCOPE + "@EXAMPLE.COM");
+    }
+
+    private void doSaslGssApiKrbConnectionTestImpl(String configScope, String clientAuthIdAtServer) throws Exception {
         try (TestAmqpPeer testPeer = new TestAmqpPeer();) {
 
-            testPeer.expectSaslGSSAPI(serviceName);
+            testPeer.expectSaslGSSAPI(SERVICE_PRINCIPAL, KRB5_KEYTAB, clientAuthIdAtServer);
             testPeer.expectOpen();
 
             // Each connection creates a session for managing temporary destinations etc
             testPeer.expectBegin();
 
-            String uriOptions = "?jms.username=client&amqp.saslMechanisms=" + GSSAPI.toString();
+            String uriOptions = "?amqp.saslMechanisms=" + GSSAPI;
+            if(configScope != null) {
+                uriOptions += "&sasl.options.configScope=" + configScope;
+            }
+
             ConnectionFactory factory = new JmsConnectionFactory("amqp://localhost:" + testPeer.getServerPort() + uriOptions);
-            Connection connection = factory.createConnection();
+            Connection connection = factory.createConnection("ignoredusername", null);
             // Set a clientID to provoke the actual AMQP connection process to occur.
             connection.setClientID("clientName");
 
@@ -142,18 +151,22 @@ public class SaslGssApiIntegrationTest extends QpidJmsTestCase {
     }
 
     @Test(timeout = 20000)
-    public void testSaslGssApiKrb5ConfigOptionOverridePrincipal() throws Exception {
+    public void testSaslGssApiKrbConnectionWithPrincipalViaJmsUsernameUri() throws Exception {
         try (TestAmqpPeer testPeer = new TestAmqpPeer();) {
 
-            testPeer.expectSaslGSSAPI(serviceName);
+            testPeer.expectSaslGSSAPI(SERVICE_PRINCIPAL, KRB5_KEYTAB, CLIENT_PRINCIPAL_URI_USERNAME + "@EXAMPLE.COM");
             testPeer.expectOpen();
 
             // Each connection creates a session for managing temporary destinations etc
             testPeer.expectBegin();
 
-            String uriOptions = "?jms.username=getsOverridden&sasl.krb5.principal=client&amqp.saslMechanisms=" + GSSAPI.toString();
+            // No password, not needed as using keyTab.
+            String uriOptions = "?sasl.options.configScope=KRB5-CLIENT-URI-USERNAME-CALLBACK&jms.username="
+                                + CLIENT_PRINCIPAL_URI_USERNAME +"&amqp.saslMechanisms=" + GSSAPI;
             ConnectionFactory factory = new JmsConnectionFactory("amqp://localhost:" + testPeer.getServerPort() + uriOptions);
+
             Connection connection = factory.createConnection();
+
             // Set a clientID to provoke the actual AMQP connection process to occur.
             connection.setClientID("clientName");
 
@@ -165,23 +178,22 @@ public class SaslGssApiIntegrationTest extends QpidJmsTestCase {
         }
     }
 
-
-
     @Test(timeout = 20000)
-    public void testSaslGssApiKrbConfigConnection() throws Exception {
-        setTestSystemProperty("java.security.auth.login.config",
-                SaslGssApiIntegrationTest.class.getClassLoader().getResource("SaslGssApiIntegrationTest-login.config").getPath());
+    public void testSaslGssApiKrbConnectionWithPrincipalViaJmsUsernameConnFactory() throws Exception {
         try (TestAmqpPeer testPeer = new TestAmqpPeer();) {
 
-            testPeer.expectSaslGSSAPI(serviceName);
+            testPeer.expectSaslGSSAPI(SERVICE_PRINCIPAL, KRB5_KEYTAB, CLIENT_PRINCIPAL_FACTORY_USERNAME + "@EXAMPLE.COM");
             testPeer.expectOpen();
 
             // Each connection creates a session for managing temporary destinations etc
             testPeer.expectBegin();
 
-            String uriOptions = "?sasl.configScope=KRB5-CLIENT&sasl.protocol=amqp&sasl.server=localhost&amqp.saslMechanisms=" + GSSAPI.toString();
+            String uriOptions = "?sasl.options.configScope=KRB5-CLIENT-FACTORY-USERNAME-CALLBACK" + "&amqp.saslMechanisms=" + GSSAPI;
             ConnectionFactory factory = new JmsConnectionFactory("amqp://localhost:" + testPeer.getServerPort() + uriOptions);
-            Connection connection = factory.createConnection();
+
+            // No password, not needed as using keyTab.
+            Connection connection = factory.createConnection(CLIENT_PRINCIPAL_FACTORY_USERNAME, null);
+
             // Set a clientID to provoke the actual AMQP connection process to occur.
             connection.setClientID("clientName");
 
@@ -196,22 +208,49 @@ public class SaslGssApiIntegrationTest extends QpidJmsTestCase {
     @Test(timeout = 20000)
     public void testSaslGssApiKrbConfigError() throws Exception {
         final String loginConfigScope = "KRB5-CLIENT-DOES-NOT-EXIST";
-        try (TestAmqpPeer testPeer = new TestAmqpPeer();) {
 
+        try (TestAmqpPeer testPeer = new TestAmqpPeer();) {
             testPeer.expectSaslGSSAPIFail();
 
-            String uriOptions = "?sasl.configScope=" + loginConfigScope + "&sasl.protocol=amqp&sasl.server=localhost&amqp.saslMechanisms=" + GSSAPI.toString();
+            String uriOptions = "?sasl.options.configScope=" + loginConfigScope + "&amqp.saslMechanisms=" + GSSAPI;
             ConnectionFactory factory = new JmsConnectionFactory("amqp://localhost:" + testPeer.getServerPort() + uriOptions);
-            Connection connection = factory.createConnection();
-            // Set a clientID to provoke the actual AMQP connection process to occur.
-            connection.setClientID("clientName");
+            factory.createConnection();
 
-            testPeer.expectClose();
-            connection.close();
             fail("Expect exception on no login config");
         } catch (JMSSecurityException expected) {
             assertTrue(expected.getMessage().contains(loginConfigScope));
         }
     }
 
+    @Test(timeout = 20000)
+    public void testGssapiOnlySelectedWhenPresentIfExplicitlyEnabled() throws Exception {
+        doMechanismSelectedTestImpl("username", "password", PLAIN, new Symbol[] {Symbol.valueOf(GSSAPI), PLAIN, ANONYMOUS}, false);
+        doMechanismSelectedTestImpl("username", "password", Symbol.valueOf(GSSAPI), new Symbol[] {Symbol.valueOf(GSSAPI), PLAIN, ANONYMOUS}, true);
+    }
+
+    private void doMechanismSelectedTestImpl(String username, String password, Symbol clientSelectedMech, Symbol[] serverMechs, boolean enableGssapiExplicitly) throws Exception {
+        try (TestAmqpPeer testPeer = new TestAmqpPeer();) {
+
+            testPeer.expectFailingSaslAuthentication(serverMechs, clientSelectedMech);
+
+            String uriOptions = "?jms.clientID=myclientid";
+            if(enableGssapiExplicitly) {
+                uriOptions += "&amqp.saslMechanisms=PLAIN," + GSSAPI;
+            }
+            ConnectionFactory factory = new JmsConnectionFactory("amqp://localhost:" + testPeer.getServerPort() + uriOptions);
+
+            try {
+                factory.createConnection(username, password);
+                fail("Excepted exception to be thrown");
+            }catch (JMSSecurityException jmsse) {
+                // Expected, we deliberately failed the SASL process,
+                // we only wanted to verify the correct mechanism
+                // was selected, other tests verify the remainder.
+
+                LOG.info("Caught expected security exception: {}", jmsse.getMessage());
+            }
+
+            testPeer.waitForAllHandlersToComplete(1000);
+        }
+    }
 }

http://git-wip-us.apache.org/repos/asf/qpid-jms/blob/ce833c7b/qpid-jms-client/src/test/java/org/apache/qpid/jms/sasl/AbstractMechanismTest.java
----------------------------------------------------------------------
diff --git a/qpid-jms-client/src/test/java/org/apache/qpid/jms/sasl/AbstractMechanismTest.java b/qpid-jms-client/src/test/java/org/apache/qpid/jms/sasl/AbstractMechanismTest.java
index 768cc74..bbacf02 100644
--- a/qpid-jms-client/src/test/java/org/apache/qpid/jms/sasl/AbstractMechanismTest.java
+++ b/qpid-jms-client/src/test/java/org/apache/qpid/jms/sasl/AbstractMechanismTest.java
@@ -68,5 +68,10 @@ public class AbstractMechanismTest {
         public boolean isApplicable(String username, String password, Principal localPrincipal) {
             return false;
         }
+
+        @Override
+        public boolean isEnabledByDefault() {
+            return false;
+        }
     }
 }

http://git-wip-us.apache.org/repos/asf/qpid-jms/blob/ce833c7b/qpid-jms-client/src/test/java/org/apache/qpid/jms/sasl/AnonymousMechanismTest.java
----------------------------------------------------------------------
diff --git a/qpid-jms-client/src/test/java/org/apache/qpid/jms/sasl/AnonymousMechanismTest.java b/qpid-jms-client/src/test/java/org/apache/qpid/jms/sasl/AnonymousMechanismTest.java
index 82b686f..9c9da7c 100644
--- a/qpid-jms-client/src/test/java/org/apache/qpid/jms/sasl/AnonymousMechanismTest.java
+++ b/qpid-jms-client/src/test/java/org/apache/qpid/jms/sasl/AnonymousMechanismTest.java
@@ -103,4 +103,11 @@ public class AnonymousMechanismTest {
             }
         }));
     }
+
+    @Test
+    public void testIsEnabledByDefault() {
+        AnonymousMechanism mech = new AnonymousMechanism();
+
+        assertTrue("Should be enabled by default", mech.isEnabledByDefault());
+    }
 }

http://git-wip-us.apache.org/repos/asf/qpid-jms/blob/ce833c7b/qpid-jms-client/src/test/java/org/apache/qpid/jms/sasl/CramMD5MechanismTest.java
----------------------------------------------------------------------
diff --git a/qpid-jms-client/src/test/java/org/apache/qpid/jms/sasl/CramMD5MechanismTest.java b/qpid-jms-client/src/test/java/org/apache/qpid/jms/sasl/CramMD5MechanismTest.java
index 1a6149f..3110147 100644
--- a/qpid-jms-client/src/test/java/org/apache/qpid/jms/sasl/CramMD5MechanismTest.java
+++ b/qpid-jms-client/src/test/java/org/apache/qpid/jms/sasl/CramMD5MechanismTest.java
@@ -85,4 +85,11 @@ public class CramMD5MechanismTest {
             }
         }));
     }
+
+    @Test
+    public void testIsEnabledByDefault() {
+        CramMD5Mechanism mech = new CramMD5Mechanism();
+
+        assertTrue("Should be enabled by default", mech.isEnabledByDefault());
+    }
 }

http://git-wip-us.apache.org/repos/asf/qpid-jms/blob/ce833c7b/qpid-jms-client/src/test/java/org/apache/qpid/jms/sasl/ExternalMechanismTest.java
----------------------------------------------------------------------
diff --git a/qpid-jms-client/src/test/java/org/apache/qpid/jms/sasl/ExternalMechanismTest.java b/qpid-jms-client/src/test/java/org/apache/qpid/jms/sasl/ExternalMechanismTest.java
index ab613b9..bb9142f 100644
--- a/qpid-jms-client/src/test/java/org/apache/qpid/jms/sasl/ExternalMechanismTest.java
+++ b/qpid-jms-client/src/test/java/org/apache/qpid/jms/sasl/ExternalMechanismTest.java
@@ -74,4 +74,11 @@ public class ExternalMechanismTest {
             }
         }));
     }
+
+    @Test
+    public void testIsEnabledByDefault() {
+        ExternalMechanism mech = new ExternalMechanism();
+
+        assertTrue("Should be enabled by default", mech.isEnabledByDefault());
+    }
 }

http://git-wip-us.apache.org/repos/asf/qpid-jms/blob/ce833c7b/qpid-jms-client/src/test/java/org/apache/qpid/jms/sasl/GssapiMechanismTest.java
----------------------------------------------------------------------
diff --git a/qpid-jms-client/src/test/java/org/apache/qpid/jms/sasl/GssapiMechanismTest.java b/qpid-jms-client/src/test/java/org/apache/qpid/jms/sasl/GssapiMechanismTest.java
new file mode 100644
index 0000000..713ebd4
--- /dev/null
+++ b/qpid-jms-client/src/test/java/org/apache/qpid/jms/sasl/GssapiMechanismTest.java
@@ -0,0 +1,39 @@
+/*
+ * 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.qpid.jms.sasl;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import org.junit.Test;
+
+public class GssapiMechanismTest {
+
+    @Test
+    public void testIsApplicableWithoutCredentials() {
+        GssapiMechanism mech = new GssapiMechanism();
+
+        assertTrue("Should be applicable without credentials", mech.isApplicable(null, null, null));
+    }
+
+    @Test
+    public void testIsNotEnabledByDefault() {
+        GssapiMechanism mech = new GssapiMechanism();
+
+        assertFalse("Should not be enabled by default", mech.isEnabledByDefault());
+    }
+}

http://git-wip-us.apache.org/repos/asf/qpid-jms/blob/ce833c7b/qpid-jms-client/src/test/java/org/apache/qpid/jms/sasl/PlainMechanismTest.java
----------------------------------------------------------------------
diff --git a/qpid-jms-client/src/test/java/org/apache/qpid/jms/sasl/PlainMechanismTest.java b/qpid-jms-client/src/test/java/org/apache/qpid/jms/sasl/PlainMechanismTest.java
index ac03286..808c7df 100644
--- a/qpid-jms-client/src/test/java/org/apache/qpid/jms/sasl/PlainMechanismTest.java
+++ b/qpid-jms-client/src/test/java/org/apache/qpid/jms/sasl/PlainMechanismTest.java
@@ -104,4 +104,11 @@ public class PlainMechanismTest {
             }
         }));
     }
+
+    @Test
+    public void testIsEnabledByDefault() {
+        PlainMechanism mech = new PlainMechanism();
+
+        assertTrue("Should be enabled by default", mech.isEnabledByDefault());
+    }
 }

http://git-wip-us.apache.org/repos/asf/qpid-jms/blob/ce833c7b/qpid-jms-client/src/test/java/org/apache/qpid/jms/sasl/ScramSHA1MechanismTest.java
----------------------------------------------------------------------
diff --git a/qpid-jms-client/src/test/java/org/apache/qpid/jms/sasl/ScramSHA1MechanismTest.java b/qpid-jms-client/src/test/java/org/apache/qpid/jms/sasl/ScramSHA1MechanismTest.java
index 094e78b..0011fb8 100644
--- a/qpid-jms-client/src/test/java/org/apache/qpid/jms/sasl/ScramSHA1MechanismTest.java
+++ b/qpid-jms-client/src/test/java/org/apache/qpid/jms/sasl/ScramSHA1MechanismTest.java
@@ -16,6 +16,10 @@
  */
 package org.apache.qpid.jms.sasl;
 
+import static org.junit.Assert.assertTrue;
+
+import org.junit.Test;
+
 /**
  * The known good used by these tests is taken from the example in RFC 5802 section 5.
  */
@@ -46,4 +50,11 @@ public class ScramSHA1MechanismTest extends AbstractScramSHAMechanismTestBase {
         mech.setPassword(PASSWORD);
         return mech;
     }
+
+    @Test
+    public void testIsEnabledByDefault() {
+        ScramSHA1Mechanism mech = new ScramSHA1Mechanism();
+
+        assertTrue("Should be enabled by default", mech.isEnabledByDefault());
+    }
 }
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/qpid-jms/blob/ce833c7b/qpid-jms-client/src/test/java/org/apache/qpid/jms/sasl/ScramSHA256MechanismTest.java
----------------------------------------------------------------------
diff --git a/qpid-jms-client/src/test/java/org/apache/qpid/jms/sasl/ScramSHA256MechanismTest.java b/qpid-jms-client/src/test/java/org/apache/qpid/jms/sasl/ScramSHA256MechanismTest.java
index e917e58..945fcd7 100644
--- a/qpid-jms-client/src/test/java/org/apache/qpid/jms/sasl/ScramSHA256MechanismTest.java
+++ b/qpid-jms-client/src/test/java/org/apache/qpid/jms/sasl/ScramSHA256MechanismTest.java
@@ -16,6 +16,10 @@
  */
 package org.apache.qpid.jms.sasl;
 
+import static org.junit.Assert.assertTrue;
+
+import org.junit.Test;
+
 /**
  * The known good used by these tests is taken from the example in RFC 7677 section 3.
  */
@@ -46,4 +50,11 @@ public class ScramSHA256MechanismTest extends AbstractScramSHAMechanismTestBase
         mech.setPassword(PASSWORD);
         return mech;
     }
+
+    @Test
+    public void testIsEnabledByDefault() {
+        ScramSHA256Mechanism mech = new ScramSHA256Mechanism();
+
+        assertTrue("Should be enabled by default", mech.isEnabledByDefault());
+    }
 }
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/qpid-jms/blob/ce833c7b/qpid-jms-client/src/test/java/org/apache/qpid/jms/test/testpeer/TestAmqpPeer.java
----------------------------------------------------------------------
diff --git a/qpid-jms-client/src/test/java/org/apache/qpid/jms/test/testpeer/TestAmqpPeer.java b/qpid-jms-client/src/test/java/org/apache/qpid/jms/test/testpeer/TestAmqpPeer.java
index da1e69a..ffbafa7 100644
--- a/qpid-jms-client/src/test/java/org/apache/qpid/jms/test/testpeer/TestAmqpPeer.java
+++ b/qpid-jms-client/src/test/java/org/apache/qpid/jms/test/testpeer/TestAmqpPeer.java
@@ -21,7 +21,6 @@ package org.apache.qpid.jms.test.testpeer;
 import static org.apache.qpid.jms.provider.amqp.AmqpSupport.DYNAMIC_NODE_LIFETIME_POLICY;
 import static org.apache.qpid.jms.provider.amqp.AmqpSupport.GLOBAL;
 import static org.apache.qpid.jms.provider.amqp.AmqpSupport.SHARED;
-import static org.apache.qpid.jms.sasl.GssapiMechanism.kerb5InlineConfig;
 import static org.hamcrest.MatcherAssert.assertThat;
 import static org.hamcrest.Matchers.arrayContaining;
 import static org.hamcrest.Matchers.equalTo;
@@ -49,6 +48,8 @@ import javax.security.auth.Subject;
 import javax.security.auth.callback.Callback;
 import javax.security.auth.callback.CallbackHandler;
 import javax.security.auth.callback.UnsupportedCallbackException;
+import javax.security.auth.login.AppConfigurationEntry;
+import javax.security.auth.login.Configuration;
 import javax.security.auth.login.LoginContext;
 import javax.security.sasl.AuthorizeCallback;
 import javax.security.sasl.Sasl;
@@ -520,12 +521,9 @@ public class TestAmqpPeer implements AutoCloseable
                 new FrameSender(
                         this, FrameType.SASL, 0,
                         saslMechanismsFrame, null)));
-
-        addHandler(new SaslInitMatcher().withMechanism(equalTo(GSSAPI)));
-
     }
 
-    public void expectSaslGSSAPI(String serviceName) throws Exception {
+    public void expectSaslGSSAPI(String serviceName, String keyTab, String clientAuthId) throws Exception {
 
         SaslMechanismsFrame saslMechanismsFrame = new SaslMechanismsFrame().setSaslServerMechanisms(GSSAPI);
 
@@ -534,18 +532,30 @@ public class TestAmqpPeer implements AutoCloseable
                         this, FrameType.SASL, 0,
                         saslMechanismsFrame, null)));
 
+        // setup server gss context
         final Map<String, String> options = new HashMap<>();
+        options.put("principal", serviceName);
+        options.put("useKeyTab", "true");
+        options.put("keyTab", keyTab);
+        options.put("storeKey", "true");
         options.put("isInitiator", "false");
+        Configuration loginCconfiguration = new Configuration() {
+            @Override
+            public AppConfigurationEntry[] getAppConfigurationEntry(String name) {
+                return new AppConfigurationEntry[]{
+                        new AppConfigurationEntry("com.sun.security.auth.module.Krb5LoginModule",
+                                AppConfigurationEntry.LoginModuleControlFlag.REQUIRED,
+                                options)};
+            }
+        };
 
-        // setup server gss context
-        LoginContext loginContext = new LoginContext("", null, null,
-                kerb5InlineConfig(serviceName, options));
+        LoginContext loginContext = new LoginContext("", null, null, loginCconfiguration);
         loginContext.login();
         final Subject serverSubject =loginContext.getSubject();
 
         LOGGER.info("saslServer subject:" + serverSubject.getPrivateCredentials());
 
-        Map<String, ?> config = new HashMap();
+        Map<String, ?> config = new HashMap<>();
         final CallbackHandler handler = new CallbackHandler() {
             @Override
             public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException {
@@ -613,9 +623,8 @@ public class TestAmqpPeer implements AutoCloseable
                     }
                 });
 
-        AtomicBoolean response = new AtomicBoolean(false);
-        SaslResponseMatcher challengeMatcher = new SaslResponseMatcher().withResponse(new BaseMatcher<Binary>() {
-
+        AtomicBoolean succeeded = new AtomicBoolean(false);
+        SaslResponseMatcher responseMatcher = new SaslResponseMatcher().withResponse(new BaseMatcher<Binary>() {
             @Override
             public void describeTo(Description description) {}
 
@@ -623,8 +632,10 @@ public class TestAmqpPeer implements AutoCloseable
             public boolean matches(Object o) {
                 final Binary binary = (Binary) o;
                 // validate via sasl
+
+                byte[] additionalData = null;
                 try {
-                    Subject.doAs(serverSubject, new PrivilegedExceptionAction<byte[]>() {
+                    additionalData = Subject.doAs(serverSubject, new PrivilegedExceptionAction<byte[]>() {
                         @Override
                         public byte[] run() throws Exception {
                             LOGGER.info("Evaluate response.. size:" + binary.getLength());
@@ -635,32 +646,48 @@ public class TestAmqpPeer implements AutoCloseable
                     e.printStackTrace();
                     throw new RuntimeException("failed to evaluate challenge response", e);
                 }
-                LOGGER.info("Complete:" + saslServer.isComplete());
-                return saslServer.isComplete();
+
+                boolean complete = saslServer.isComplete();
+                boolean expectedAuthId = false;
+                if(complete) {
+                    expectedAuthId = clientAuthId.equals(saslServer.getAuthorizationID());
+                    LOGGER.info("Authorized ID: " + saslServer.getAuthorizationID());
+                }
+
+                LOGGER.info("Complete:" + complete + ", expectedAuthID:" + expectedAuthId +", additionalData:" + additionalData);
+
+                if(complete && expectedAuthId && additionalData == null) {
+                    succeeded.set(true);
+                    return true;
+                } else {
+                    return false;
+                }
             }
         }).onCompletion(new AmqpPeerRunnable() {
             @Override
             public void run() {
+                SaslOutcomeFrame saslOutcome = new SaslOutcomeFrame();
+                if (saslServer.isComplete() && succeeded.get()) {
+                    saslOutcome.setCode(SASL_OK);
+                } else {
+                    saslOutcome.setCode(SASL_FAIL_AUTH);
+                }
 
-                if (saslServer.isComplete()) {
-                    LOGGER.info("Authorized ID: " + saslServer.getAuthorizationID());
-                    LOGGER.info("Send Outcome");
-                    TestAmqpPeer.this.sendFrame(
-                            FrameType.SASL, 0,
-                            new SaslOutcomeFrame().setCode(SASL_OK),
-                            null,
-                            false, 0);
+                LOGGER.info("Send Outcome");
+                TestAmqpPeer.this.sendFrame(
+                        FrameType.SASL, 0,
+                        saslOutcome,
+                        null,
+                        false, 0);
 
-                    // Now that we processed the SASL layer AMQP header, reset the
-                    // peer to expect the non-SASL AMQP header.
-                    _driverRunnable.expectHeader();
-                }
+                // Now that we processed the SASL layer AMQP header, reset the
+                // peer to expect the non-SASL AMQP header.
+                _driverRunnable.expectHeader();
             }
         });
 
         addHandler(saslInitMatcher);
-        addHandler(challengeMatcher);
-
+        addHandler(responseMatcher);
         addHandler(new HeaderHandlerImpl(AmqpHeader.HEADER, AmqpHeader.HEADER));
     }
 

http://git-wip-us.apache.org/repos/asf/qpid-jms/blob/ce833c7b/qpid-jms-client/src/test/java/org/apache/qpid/jms/test/testpeer/TestAmqpPeerRunner.java
----------------------------------------------------------------------
diff --git a/qpid-jms-client/src/test/java/org/apache/qpid/jms/test/testpeer/TestAmqpPeerRunner.java b/qpid-jms-client/src/test/java/org/apache/qpid/jms/test/testpeer/TestAmqpPeerRunner.java
index 7b3f24f..393e70a 100644
--- a/qpid-jms-client/src/test/java/org/apache/qpid/jms/test/testpeer/TestAmqpPeerRunner.java
+++ b/qpid-jms-client/src/test/java/org/apache/qpid/jms/test/testpeer/TestAmqpPeerRunner.java
@@ -117,7 +117,7 @@ class TestAmqpPeerRunner implements Runnable
                 {
                     ByteBuffer networkInputByteBuffer = ByteBuffer.wrap(networkInputBytes, 0, bytesRead);
 
-                    LOGGER.debug("Read: {}", new Binary(networkInputBytes, 0, bytesRead));
+                    LOGGER.debug("Read: {} ({} bytes)", new Binary(networkInputBytes, 0, bytesRead), bytesRead);
 
                     try {
                         _testFrameParser.input(networkInputByteBuffer);

http://git-wip-us.apache.org/repos/asf/qpid-jms/blob/ce833c7b/qpid-jms-client/src/test/resources/SaslGssApiIntegrationTest-login.config
----------------------------------------------------------------------
diff --git a/qpid-jms-client/src/test/resources/SaslGssApiIntegrationTest-login.config b/qpid-jms-client/src/test/resources/SaslGssApiIntegrationTest-login.config
index 6aa4123..edd7fc1 100644
--- a/qpid-jms-client/src/test/resources/SaslGssApiIntegrationTest-login.config
+++ b/qpid-jms-client/src/test/resources/SaslGssApiIntegrationTest-login.config
@@ -17,7 +17,26 @@
 
 KRB5-CLIENT {
     com.sun.security.auth.module.Krb5LoginModule required
+    principal="clientprincipal"
+    useKeyTab=true
+    keytab="target/SaslGssApiIntegrationTest.krb5.keytab";
+};
+
+KRB5-CLIENT-URI-USERNAME-CALLBACK {
+    com.sun.security.auth.module.Krb5LoginModule required
+    useKeyTab=true
+    keytab="target/SaslGssApiIntegrationTest.krb5.keytab";
+};
+
+KRB5-CLIENT-FACTORY-USERNAME-CALLBACK {
+    com.sun.security.auth.module.Krb5LoginModule required
+    useKeyTab=true
+    keytab="target/SaslGssApiIntegrationTest.krb5.keytab";
+};
+
+amqp-jms-client {
+    com.sun.security.auth.module.Krb5LoginModule required
+    principal="defaultscopeprincipal"
     useKeyTab=true
-    principal="client"
     keytab="target/SaslGssApiIntegrationTest.krb5.keytab";
 };


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