diff --git a/bin/jmeter.properties b/bin/jmeter.properties
index 975fb7038..610f942cd 100644
--- a/bin/jmeter.properties
+++ b/bin/jmeter.properties
@@ -392,6 +392,10 @@ remote_hosts=127.0.0.1
# for SPNEGO authentication
#kerberos.spnego.strip_port=true
+# Should credentials be delegated to webservers when using
+# SPNEGO authentication
+#kerberos.spnego.delegate_cred=false
+
#---------------------------------------------------------------------------
# Apache HttpComponents HTTPClient configuration (HTTPClient4)
#---------------------------------------------------------------------------
@@ -1294,4 +1298,4 @@ jmeter.reportgenerator.apdex_tolerated_threshold=1500
# Switch that allows using Local documentation opened in JMeter GUI
# By default we use Online documentation opened in Browser
-#help.local=false
\ No newline at end of file
+#help.local=false
diff --git a/src/protocol/http/org/apache/jmeter/protocol/http/control/DelegatingKerberosScheme.java b/src/protocol/http/org/apache/jmeter/protocol/http/control/DelegatingKerberosScheme.java
new file mode 100644
index 000000000..82708b5f9
--- /dev/null
+++ b/src/protocol/http/org/apache/jmeter/protocol/http/control/DelegatingKerberosScheme.java
@@ -0,0 +1,66 @@
+/*
+ * 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.jmeter.protocol.http.control;
+
+import org.apache.http.auth.Credentials;
+import org.apache.http.auth.KerberosCredentials;
+import org.apache.http.impl.auth.KerberosScheme;
+import org.ietf.jgss.GSSContext;
+import org.ietf.jgss.GSSCredential;
+import org.ietf.jgss.GSSException;
+import org.ietf.jgss.GSSManager;
+import org.ietf.jgss.GSSName;
+import org.ietf.jgss.Oid;
+
+public class DelegatingKerberosScheme extends KerberosScheme {
+ public DelegatingKerberosScheme(final boolean stripPort, final boolean useCanonicalHostName) {
+ super(stripPort, useCanonicalHostName);
+ }
+
+ @Override
+ protected byte[] generateGSSToken(
+ final byte[] input, final Oid oid, final String authServer,
+ final Credentials credentials) throws GSSException {
+ final GSSManager manager = getManager();
+ final GSSName serverName = manager.createName("HTTP@" + authServer, GSSName.NT_HOSTBASED_SERVICE);
+
+ final GSSCredential gssCredential;
+ if (credentials instanceof KerberosCredentials) {
+ gssCredential = ((KerberosCredentials) credentials).getGSSCredential();
+ } else {
+ gssCredential = null;
+ }
+
+ final GSSContext gssContext = createDelegatingGSSContext(manager, oid, serverName, gssCredential);
+ if (input != null) {
+ return gssContext.initSecContext(input, 0, input.length);
+ } else {
+ return gssContext.initSecContext(new byte[] {}, 0, 0);
+ }
+ }
+
+ GSSContext createDelegatingGSSContext(final GSSManager manager, final Oid oid, final GSSName serverName,
+ final GSSCredential gssCredential) throws GSSException {
+ final GSSContext gssContext = manager.createContext(serverName.canonicalize(oid), oid, gssCredential,
+ GSSContext.DEFAULT_LIFETIME);
+ gssContext.requestMutualAuth(true);
+ gssContext.requestCredDeleg(true);
+ return gssContext;
+ }
+}
diff --git a/src/protocol/http/org/apache/jmeter/protocol/http/control/DelegatingSPNegoScheme.java b/src/protocol/http/org/apache/jmeter/protocol/http/control/DelegatingSPNegoScheme.java
new file mode 100644
index 000000000..14b77b25a
--- /dev/null
+++ b/src/protocol/http/org/apache/jmeter/protocol/http/control/DelegatingSPNegoScheme.java
@@ -0,0 +1,66 @@
+/*
+ * 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.jmeter.protocol.http.control;
+
+import org.apache.http.auth.Credentials;
+import org.apache.http.auth.KerberosCredentials;
+import org.apache.http.impl.auth.SPNegoScheme;
+import org.ietf.jgss.GSSContext;
+import org.ietf.jgss.GSSCredential;
+import org.ietf.jgss.GSSException;
+import org.ietf.jgss.GSSManager;
+import org.ietf.jgss.GSSName;
+import org.ietf.jgss.Oid;
+
+public class DelegatingSPNegoScheme extends SPNegoScheme {
+ public DelegatingSPNegoScheme(final boolean stripPort, final boolean useCanonicalHostName) {
+ super(stripPort, useCanonicalHostName);
+ }
+
+ @Override
+ protected byte[] generateGSSToken(
+ final byte[] input, final Oid oid, final String authServer,
+ final Credentials credentials) throws GSSException {
+ final GSSManager manager = getManager();
+ final GSSName serverName = manager.createName("HTTP@" + authServer, GSSName.NT_HOSTBASED_SERVICE);
+
+ final GSSCredential gssCredential;
+ if (credentials instanceof KerberosCredentials) {
+ gssCredential = ((KerberosCredentials) credentials).getGSSCredential();
+ } else {
+ gssCredential = null;
+ }
+
+ final GSSContext gssContext = createDelegatingGSSContext(manager, oid, serverName, gssCredential);
+ if (input != null) {
+ return gssContext.initSecContext(input, 0, input.length);
+ } else {
+ return gssContext.initSecContext(new byte[] {}, 0, 0);
+ }
+ }
+
+ GSSContext createDelegatingGSSContext(final GSSManager manager, final Oid oid, final GSSName serverName,
+ final GSSCredential gssCredential) throws GSSException {
+ final GSSContext gssContext = manager.createContext(serverName.canonicalize(oid), oid, gssCredential,
+ GSSContext.DEFAULT_LIFETIME);
+ gssContext.requestMutualAuth(true);
+ gssContext.requestCredDeleg(true);
+ return gssContext;
+ }
+}
diff --git a/src/protocol/http/org/apache/jmeter/protocol/http/control/DynamicKerberosSchemeFactory.java b/src/protocol/http/org/apache/jmeter/protocol/http/control/DynamicKerberosSchemeFactory.java
index 34afc7373..3cf65e541 100644
--- a/src/protocol/http/org/apache/jmeter/protocol/http/control/DynamicKerberosSchemeFactory.java
+++ b/src/protocol/http/org/apache/jmeter/protocol/http/control/DynamicKerberosSchemeFactory.java
@@ -22,6 +22,10 @@ import org.apache.http.auth.AuthScheme;
import org.apache.http.impl.auth.KerberosScheme;
import org.apache.http.impl.auth.KerberosSchemeFactory;
import org.apache.http.protocol.HttpContext;
+import org.apache.jmeter.util.JMeterUtils;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
/**
* Extends {@link KerberosSchemeFactory} to provide ability to customize stripPort
@@ -30,6 +34,9 @@ import org.apache.http.protocol.HttpContext;
*/
public class DynamicKerberosSchemeFactory extends KerberosSchemeFactory {
static final String CONTEXT_ATTRIBUTE_STRIP_PORT = "__jmeter.K_SP__";
+ static final String CONTEXT_ATTRIBUTE_DELEGATE_CRED = "__jmeter.K_DT__";
+ static final boolean DELEGATE_CRED = JMeterUtils.getPropDefault("kerberos.spnego.delegate_cred", false);
+ private static final Logger log = LoggerFactory.getLogger(DynamicKerberosSchemeFactory.class);
/**
* Constructor for DynamicKerberosSchemeFactory
@@ -43,8 +50,19 @@ public class DynamicKerberosSchemeFactory extends KerberosSchemeFactory {
@Override
public AuthScheme create(final HttpContext context) {
- Boolean localStripPort = (Boolean) context.getAttribute(CONTEXT_ATTRIBUTE_STRIP_PORT);
- Boolean stripPort = localStripPort != null ? localStripPort : isStripPort();
+ boolean stripPort = isEnabled(context.getAttribute(CONTEXT_ATTRIBUTE_STRIP_PORT), isStripPort());
+ if (isEnabled(context.getAttribute(CONTEXT_ATTRIBUTE_DELEGATE_CRED), DELEGATE_CRED)) {
+ log.debug("Use DelegatingKerberosScheme");
+ return new DelegatingKerberosScheme(stripPort, isStripPort());
+ }
+ log.debug("Use KerberosScheme");
return new KerberosScheme(stripPort, isUseCanonicalHostname());
}
+
+ private boolean isEnabled(Object contextAttribute, boolean defaultValue) {
+ if (contextAttribute instanceof Boolean) {
+ return ((Boolean) contextAttribute).booleanValue();
+ }
+ return defaultValue;
+ }
}
diff --git a/src/protocol/http/org/apache/jmeter/protocol/http/control/DynamicSPNegoSchemeFactory.java b/src/protocol/http/org/apache/jmeter/protocol/http/control/DynamicSPNegoSchemeFactory.java
new file mode 100644
index 000000000..bb856c693
--- /dev/null
+++ b/src/protocol/http/org/apache/jmeter/protocol/http/control/DynamicSPNegoSchemeFactory.java
@@ -0,0 +1,68 @@
+/*
+ * 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.jmeter.protocol.http.control;
+
+import org.apache.http.auth.AuthScheme;
+import org.apache.http.impl.auth.SPNegoScheme;
+import org.apache.http.impl.auth.SPNegoSchemeFactory;
+import org.apache.http.protocol.HttpContext;
+import org.apache.jmeter.util.JMeterUtils;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Extends {@link SPNegoSchemeFactory} to provide ability to customize stripPort
+ * setting in {@link SPNegoScheme} based on {@link HttpContext}
+ * @since 4.1
+ */
+public class DynamicSPNegoSchemeFactory extends SPNegoSchemeFactory {
+ static final String CONTEXT_ATTRIBUTE_STRIP_PORT = "__jmeter.K_SP__";
+ static final String CONTEXT_ATTRIBUTE_DELEGATE_CRED = "__jmeter.K_DT__";
+ static final boolean DELEGATE_CRED = JMeterUtils.getPropDefault("kerberos.spnego.delegate_cred", false);
+ private static final Logger log = LoggerFactory.getLogger(DynamicSPNegoSchemeFactory.class);
+
+ /**
+ * Constructor for DynamicSPNegoSchemeFactory
+ * @param stripPort flag, whether port should be stripped from SPN
+ * @param useCanonicalHostname flag, whether SPN should use the canonical hostname
+ * @since 4.0
+ */
+ public DynamicSPNegoSchemeFactory(final boolean stripPort, final boolean useCanonicalHostname) {
+ super(stripPort, useCanonicalHostname);
+ }
+
+ @Override
+ public AuthScheme create(final HttpContext context) {
+ boolean stripPort = isEnabled(context.getAttribute(CONTEXT_ATTRIBUTE_STRIP_PORT), isStripPort());
+ if (isEnabled(context.getAttribute(CONTEXT_ATTRIBUTE_DELEGATE_CRED), DELEGATE_CRED)) {
+ log.debug("Use DelegatingSPNegoScheme");
+ return new DelegatingSPNegoScheme(stripPort, isStripPort());
+ }
+ log.debug("Use SPNegoScheme");
+ return new SPNegoScheme(stripPort, isUseCanonicalHostname());
+ }
+
+ private boolean isEnabled(Object contextAttribute, boolean defaultValue) {
+ if (contextAttribute instanceof Boolean) {
+ return ((Boolean) contextAttribute).booleanValue();
+ }
+ return defaultValue;
+ }
+}
diff --git a/src/protocol/http/org/apache/jmeter/protocol/http/sampler/HTTPHC4Impl.java b/src/protocol/http/org/apache/jmeter/protocol/http/sampler/HTTPHC4Impl.java
index e92d60787..4566966e6 100644
--- a/src/protocol/http/org/apache/jmeter/protocol/http/sampler/HTTPHC4Impl.java
+++ b/src/protocol/http/org/apache/jmeter/protocol/http/sampler/HTTPHC4Impl.java
@@ -110,7 +110,6 @@ import org.apache.http.impl.auth.DigestScheme;
import org.apache.http.impl.auth.DigestSchemeFactory;
import org.apache.http.impl.auth.KerberosScheme;
import org.apache.http.impl.auth.NTLMSchemeFactory;
-import org.apache.http.impl.auth.SPNegoSchemeFactory;
import org.apache.http.impl.client.BasicAuthCache;
import org.apache.http.impl.client.BasicCredentialsProvider;
import org.apache.http.impl.client.CloseableHttpClient;
@@ -142,6 +141,7 @@ import org.apache.jmeter.protocol.http.control.Authorization;
import org.apache.jmeter.protocol.http.control.CacheManager;
import org.apache.jmeter.protocol.http.control.CookieManager;
import org.apache.jmeter.protocol.http.control.DynamicKerberosSchemeFactory;
+import org.apache.jmeter.protocol.http.control.DynamicSPNegoSchemeFactory;
import org.apache.jmeter.protocol.http.control.HeaderManager;
import org.apache.jmeter.protocol.http.sampler.hc.LaxDeflateInputStream;
import org.apache.jmeter.protocol.http.sampler.hc.LazyLayeredConnectionSocketFactory;
@@ -1030,7 +1030,8 @@ public class HTTPHC4Impl extends HTTPHCAbstractImpl {
.register(AuthSchemes.BASIC, new BasicSchemeFactory())
.register(AuthSchemes.DIGEST, new DigestSchemeFactory())
.register(AuthSchemes.NTLM, new NTLMSchemeFactory())
- .register(AuthSchemes.SPNEGO, new SPNegoSchemeFactory())
+ .register(AuthSchemes.SPNEGO, new DynamicSPNegoSchemeFactory(
+ AuthManager.STRIP_PORT, AuthManager.USE_CANONICAL_HOST_NAME))
.register(AuthSchemes.KERBEROS, new DynamicKerberosSchemeFactory(
AuthManager.STRIP_PORT, AuthManager.USE_CANONICAL_HOST_NAME))
.build();
diff --git a/xdocs/usermanual/component_reference.xml b/xdocs/usermanual/component_reference.xml
index 2aca4b1b2..37fb3c62f 100644
--- a/xdocs/usermanual/component_reference.xml
+++ b/xdocs/usermanual/component_reference.xml
@@ -3644,6 +3644,9 @@ Look at the two sample configuration files (krb5.conf
and jaa
for references to more documentation, and tweak them to match your Kerberos configuration.
+Delegation of credentials is disabled by default for SPNEGO. If you want to enable it, you can do so by setting the property kerberos.spnego.delegate_cred
to true
.
+
When generating a SPN for Kerberos SPNEGO authentication IE and Firefox will omit the port number
from the URL. Chrome has an option (--enable-auth-negotiate-port
) to include the port
number if it differs from the standard ones (80
and 443
). That behavior
diff --git a/xdocs/usermanual/properties_reference.xml b/xdocs/usermanual/properties_reference.xml
index f13885f65..db55d5f5a 100644
--- a/xdocs/usermanual/properties_reference.xml
+++ b/xdocs/usermanual/properties_reference.xml
@@ -463,6 +463,10 @@ JMETER-SERVER
Should port be stripped from urls before constructing SPNs for SPNEGO authentication.
Defaults to: true
+false
+