diff --git a/httpclient/src/main/java/org/apache/http/client/params/AuthPolicy.java b/httpclient/src/main/java/org/apache/http/client/params/AuthPolicy.java index 3f7731034..a27857846 100644 --- a/httpclient/src/main/java/org/apache/http/client/params/AuthPolicy.java +++ b/httpclient/src/main/java/org/apache/http/client/params/AuthPolicy.java @@ -60,10 +60,17 @@ public final class AuthPolicy { public static final String BASIC = "Basic"; /** - * SPNEGO/Kerberos Authentication scheme. + * SPNEGO Authentication scheme. * * @since 4.1 */ - public static final String SPNEGO = "negotiate"; + public static final String SPNEGO = "Negotiate"; + + /** + * Kerberos Authentication scheme. + * + * @since 4.2 + */ + public static final String KERBEROS = "Kerberos"; } diff --git a/httpclient/src/main/java/org/apache/http/impl/auth/GGSSchemeBase.java b/httpclient/src/main/java/org/apache/http/impl/auth/GGSSchemeBase.java new file mode 100644 index 000000000..330655bb0 --- /dev/null +++ b/httpclient/src/main/java/org/apache/http/impl/auth/GGSSchemeBase.java @@ -0,0 +1,192 @@ +/* + * ==================================================================== + * + * 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. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + */ + +package org.apache.http.impl.auth; + +import org.apache.commons.codec.binary.Base64; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.apache.http.Header; +import org.apache.http.HttpHost; +import org.apache.http.HttpRequest; +import org.apache.http.auth.AuthenticationException; +import org.apache.http.auth.Credentials; +import org.apache.http.auth.InvalidCredentialsException; +import org.apache.http.auth.MalformedChallengeException; +import org.apache.http.message.BasicHeader; +import org.apache.http.protocol.ExecutionContext; +import org.apache.http.protocol.HttpContext; +import org.apache.http.util.CharArrayBuffer; +import org.ietf.jgss.GSSContext; +import org.ietf.jgss.GSSException; +import org.ietf.jgss.GSSManager; +import org.ietf.jgss.GSSName; +import org.ietf.jgss.Oid; + +public abstract class GGSSchemeBase extends AuthSchemeBase { + + enum State { + UNINITIATED, + CHALLENGE_RECEIVED, + TOKEN_GENERATED, + FAILED, + } + + private final Log log = LogFactory.getLog(getClass()); + + private final boolean stripPort; + private final Base64 base64codec; + + /** Authentication process state */ + private State state; + + /** base64 decoded challenge **/ + private byte[] token; + + GGSSchemeBase(boolean stripPort) { + super(); + this.base64codec = new Base64(); + this.state = State.UNINITIATED; + this.stripPort = stripPort; + } + + GGSSchemeBase() { + this(false); + } + + protected GSSManager getManager() { + return GSSManager.getInstance(); + } + + protected byte[] generateGSSToken( + final byte[] input, final Oid oid, final String authServer) throws GSSException { + byte[] token = input; + if (token == null) { + token = new byte[0]; + } + GSSManager manager = getManager(); + GSSName serverName = manager.createName("HTTP@" + authServer, GSSName.NT_HOSTBASED_SERVICE); + GSSContext gssContext = manager.createContext( + serverName.canonicalize(oid), oid, null, GSSContext.DEFAULT_LIFETIME); + gssContext.requestMutualAuth(true); + gssContext.requestCredDeleg(true); + return gssContext.initSecContext(token, 0, token.length); + } + + protected abstract byte[] generateToken( + byte[] input, final String authServer) throws GSSException; + + public boolean isComplete() { + return this.state == State.TOKEN_GENERATED || this.state == State.FAILED; + } + + @Deprecated + public Header authenticate( + final Credentials credentials, + final HttpRequest request) throws AuthenticationException { + return authenticate(credentials, request, null); + } + + @Override + public Header authenticate( + final Credentials credentials, + final HttpRequest request, + final HttpContext context) throws AuthenticationException { + if (request == null) { + throw new IllegalArgumentException("HTTP request may not be null"); + } + switch (state) { + case UNINITIATED: + throw new AuthenticationException(getSchemeName() + " authentication has not been initiated"); + case FAILED: + throw new AuthenticationException(getSchemeName() + " authentication has failed"); + case CHALLENGE_RECEIVED: + try { + String key = null; + if (isProxy()) { + key = ExecutionContext.HTTP_PROXY_HOST; + } else { + key = ExecutionContext.HTTP_TARGET_HOST; + } + HttpHost host = (HttpHost) context.getAttribute(key); + if (host == null) { + throw new AuthenticationException("Authentication host is not set " + + "in the execution context"); + } + String authServer; + if (!this.stripPort && host.getPort() > 0) { + authServer = host.toHostString(); + } else { + authServer = host.getHostName(); + } + + if (log.isDebugEnabled()) { + log.debug("init " + authServer); + } + token = generateToken(token, authServer); + state = State.TOKEN_GENERATED; + } catch (GSSException gsse) { + state = State.FAILED; + if (gsse.getMajor() == GSSException.DEFECTIVE_CREDENTIAL + || gsse.getMajor() == GSSException.CREDENTIALS_EXPIRED) + throw new InvalidCredentialsException(gsse.getMessage(), gsse); + if (gsse.getMajor() == GSSException.NO_CRED ) + throw new InvalidCredentialsException(gsse.getMessage(), gsse); + if (gsse.getMajor() == GSSException.DEFECTIVE_TOKEN + || gsse.getMajor() == GSSException.DUPLICATE_TOKEN + || gsse.getMajor() == GSSException.OLD_TOKEN) + throw new AuthenticationException(gsse.getMessage(), gsse); + // other error + throw new AuthenticationException(gsse.getMessage()); + } + case TOKEN_GENERATED: + String tokenstr = new String(base64codec.encode(token)); + if (log.isDebugEnabled()) { + log.debug("Sending response '" + tokenstr + "' back to the auth server"); + } + return new BasicHeader("Authorization", "Negotiate " + tokenstr); + default: + throw new IllegalStateException("Illegal state: " + state); + } + } + + @Override + protected void parseChallenge( + final CharArrayBuffer buffer, + int beginIndex, int endIndex) throws MalformedChallengeException { + String challenge = buffer.substringTrimmed(beginIndex, endIndex); + if (log.isDebugEnabled()) { + log.debug("Received challenge '" + challenge + "' from the auth server"); + } + if (state == State.UNINITIATED) { + token = base64codec.decode(challenge.getBytes()); + state = State.CHALLENGE_RECEIVED; + } else { + log.debug("Authentication already attempted"); + state = State.FAILED; + } + } + +} diff --git a/httpclient/src/main/java/org/apache/http/impl/auth/KerberosScheme.java b/httpclient/src/main/java/org/apache/http/impl/auth/KerberosScheme.java new file mode 100644 index 000000000..d9971c1ce --- /dev/null +++ b/httpclient/src/main/java/org/apache/http/impl/auth/KerberosScheme.java @@ -0,0 +1,114 @@ +/* + * ==================================================================== + * + * 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. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + */ + +package org.apache.http.impl.auth; + +import org.apache.http.Header; +import org.apache.http.HttpRequest; +import org.apache.http.auth.AuthenticationException; +import org.apache.http.auth.Credentials; +import org.apache.http.protocol.HttpContext; +import org.ietf.jgss.GSSException; +import org.ietf.jgss.Oid; + +/** + * KERBEROS authentication scheme. + * + * @since 4.2 + */ +public class KerberosScheme extends GGSSchemeBase { + + private static final String KERBEROS_OID = "1.2.840.113554.1.2.2"; + + public KerberosScheme(boolean stripPort) { + super(stripPort); + } + + public KerberosScheme() { + super(false); + } + + public String getSchemeName() { + return "Kerberos"; + } + + /** + * Produces KERBEROS authorization Header based on token created by + * processChallenge. + * + * @param credentials not used by the KERBEROS scheme. + * @param request The request being authenticated + * + * @throws AuthenticationException if authentication string cannot + * be generated due to an authentication failure + * + * @return KERBEROS authentication Header + */ + @Override + public Header authenticate( + final Credentials credentials, + final HttpRequest request, + final HttpContext context) throws AuthenticationException { + return super.authenticate(credentials, request, context); + } + + @Override + protected byte[] generateToken(final byte[] input, final String authServer) throws GSSException { + return generateGSSToken(input, new Oid(KERBEROS_OID), authServer); + } + + /** + * There are no valid parameters for KERBEROS authentication so this + * method always returns null. + * + * @return null + */ + public String getParameter(String name) { + if (name == null) { + throw new IllegalArgumentException("Parameter name may not be null"); + } + return null; + } + + /** + * The concept of an authentication realm is not supported by the Negotiate + * authentication scheme. Always returns null. + * + * @return null + */ + public String getRealm() { + return null; + } + + /** + * Returns true. KERBEROS authentication scheme is connection based. + * + * @return true. + */ + public boolean isConnectionBased() { + return true; + } + +} diff --git a/httpclient/src/main/java/org/apache/http/impl/auth/KerberosSchemeFactory.java b/httpclient/src/main/java/org/apache/http/impl/auth/KerberosSchemeFactory.java new file mode 100644 index 000000000..6bb8a64a4 --- /dev/null +++ b/httpclient/src/main/java/org/apache/http/impl/auth/KerberosSchemeFactory.java @@ -0,0 +1,61 @@ +/* + * ==================================================================== + * + * 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. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package org.apache.http.impl.auth; + +import org.apache.http.annotation.Immutable; +import org.apache.http.auth.AuthScheme; +import org.apache.http.auth.AuthSchemeFactory; +import org.apache.http.params.HttpParams; + +/** + * Kerberos authentication scheme factory. + * + * @since 4.2 + */ +@Immutable +public class KerberosSchemeFactory implements AuthSchemeFactory { + + private final boolean stripPort; + + public KerberosSchemeFactory(boolean stripPort) { + super(); + this.stripPort = stripPort; + } + + public KerberosSchemeFactory() { + this(false); + } + + public AuthScheme newInstance(final HttpParams params) { + return new KerberosScheme(this.stripPort); + } + + public boolean isStripPort() { + return stripPort; + } + +} diff --git a/httpclient/src/main/java/org/apache/http/impl/auth/NegotiateScheme.java b/httpclient/src/main/java/org/apache/http/impl/auth/NegotiateScheme.java index 990c9e09f..5d3e84605 100644 --- a/httpclient/src/main/java/org/apache/http/impl/auth/NegotiateScheme.java +++ b/httpclient/src/main/java/org/apache/http/impl/auth/NegotiateScheme.java @@ -27,24 +27,14 @@ package org.apache.http.impl.auth; import java.io.IOException; -import org.apache.commons.codec.binary.Base64; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.apache.http.Header; -import org.apache.http.HttpHost; import org.apache.http.HttpRequest; import org.apache.http.auth.AuthenticationException; import org.apache.http.auth.Credentials; -import org.apache.http.auth.InvalidCredentialsException; -import org.apache.http.auth.MalformedChallengeException; -import org.apache.http.message.BasicHeader; -import org.apache.http.protocol.ExecutionContext; import org.apache.http.protocol.HttpContext; -import org.apache.http.util.CharArrayBuffer; -import org.ietf.jgss.GSSContext; import org.ietf.jgss.GSSException; -import org.ietf.jgss.GSSManager; -import org.ietf.jgss.GSSName; import org.ietf.jgss.Oid; /** @@ -52,44 +42,26 @@ import org.ietf.jgss.Oid; * scheme. * * @since 4.1 + * + * @deprecated use {@link SPNegoScheme} or {@link KerberosScheme}. */ -public class NegotiateScheme extends AuthSchemeBase { +@Deprecated +public class NegotiateScheme extends GGSSchemeBase { - enum State { - UNINITIATED, - CHALLENGE_RECEIVED, - TOKEN_GENERATED, - FAILED, - } + private final Log log = LogFactory.getLog(getClass()); private static final String SPNEGO_OID = "1.3.6.1.5.5.2"; private static final String KERBEROS_OID = "1.2.840.113554.1.2.2"; - private final Log log = LogFactory.getLog(getClass()); - private final SpnegoTokenGenerator spengoGenerator; - private final boolean stripPort; - - private GSSContext gssContext = null; - - /** Authentication process state */ - private State state; - - /** base64 decoded challenge **/ - private byte[] token; - - private Oid negotiationOid = null; - /** * Default constructor for the Negotiate authentication scheme. * */ public NegotiateScheme(final SpnegoTokenGenerator spengoGenerator, boolean stripPort) { - super(); - this.state = State.UNINITIATED; + super(stripPort); this.spengoGenerator = spengoGenerator; - this.stripPort = stripPort; } public NegotiateScheme(final SpnegoTokenGenerator spengoGenerator) { @@ -100,17 +72,6 @@ public class NegotiateScheme extends AuthSchemeBase { this(null, false); } - /** - * Tests if the Negotiate authentication process has been completed. - * - * @return true if authorization has been processed, - * false otherwise. - * - */ - public boolean isComplete() { - return this.state == State.TOKEN_GENERATED || this.state == State.FAILED; - } - /** * Returns textual designation of the Negotiate authentication scheme. * @@ -120,17 +81,6 @@ public class NegotiateScheme extends AuthSchemeBase { return "Negotiate"; } - @Deprecated - public Header authenticate( - final Credentials credentials, - final HttpRequest request) throws AuthenticationException { - return authenticate(credentials, request, null); - } - - protected GSSManager getManager() { - return GSSManager.getInstance(); - } - /** * Produces Negotiate authorization Header based on token created by * processChallenge. @@ -149,130 +99,62 @@ public class NegotiateScheme extends AuthSchemeBase { final Credentials credentials, final HttpRequest request, final HttpContext context) throws AuthenticationException { - if (request == null) { - throw new IllegalArgumentException("HTTP request may not be null"); + return super.authenticate(credentials, request, context); + } + + @Override + protected byte[] generateToken(final byte[] input, final String authServer) throws GSSException { + /* Using the SPNEGO OID is the correct method. + * Kerberos v5 works for IIS but not JBoss. Unwrapping + * the initial token when using SPNEGO OID looks like what is + * described here... + * + * http://msdn.microsoft.com/en-us/library/ms995330.aspx + * + * Another helpful URL... + * + * http://publib.boulder.ibm.com/infocenter/wasinfo/v7r0/index.jsp?topic=/com.ibm.websphere.express.doc/info/exp/ae/tsec_SPNEGO_token.html + * + * Unfortunately SPNEGO is JRE >=1.6. + */ + + /** Try SPNEGO by default, fall back to Kerberos later if error */ + Oid negotiationOid = new Oid(SPNEGO_OID); + + byte[] token = input; + boolean tryKerberos = false; + try { + token = generateGSSToken(token, negotiationOid, authServer); + } catch (GSSException ex){ + // BAD MECH means we are likely to be using 1.5, fall back to Kerberos MECH. + // Rethrow any other exception. + if (ex.getMajor() == GSSException.BAD_MECH ){ + log.debug("GSSException BAD_MECH, retry with Kerberos MECH"); + tryKerberos = true; + } else { + throw ex; + } + } - switch (state) { - case UNINITIATED: - throw new AuthenticationException("SPNEGO authentication has not been initiated"); - case FAILED: - throw new AuthenticationException("SPNEGO authentication has failed"); - case CHALLENGE_RECEIVED: - try { - String key = null; - if (isProxy()) { - key = ExecutionContext.HTTP_PROXY_HOST; - } else { - key = ExecutionContext.HTTP_TARGET_HOST; - } - HttpHost host = (HttpHost) context.getAttribute(key); - if (host == null) { - throw new AuthenticationException("Authentication host is not set " + - "in the execution context"); - } - String authServer; - if (!this.stripPort && host.getPort() > 0) { - authServer = host.toHostString(); - } else { - authServer = host.getHostName(); - } + if (tryKerberos){ + /* Kerberos v5 GSS-API mechanism defined in RFC 1964.*/ + log.debug("Using Kerberos MECH " + KERBEROS_OID); + negotiationOid = new Oid(KERBEROS_OID); + token = generateGSSToken(token, negotiationOid, authServer); - if (log.isDebugEnabled()) { - log.debug("init " + authServer); - } - /* Using the SPNEGO OID is the correct method. - * Kerberos v5 works for IIS but not JBoss. Unwrapping - * the initial token when using SPNEGO OID looks like what is - * described here... - * - * http://msdn.microsoft.com/en-us/library/ms995330.aspx - * - * Another helpful URL... - * - * http://publib.boulder.ibm.com/infocenter/wasinfo/v7r0/index.jsp?topic=/com.ibm.websphere.express.doc/info/exp/ae/tsec_SPNEGO_token.html - * - * Unfortunately SPNEGO is JRE >=1.6. - */ - - /** Try SPNEGO by default, fall back to Kerberos later if error */ - negotiationOid = new Oid(SPNEGO_OID); - - boolean tryKerberos = false; + /* + * IIS accepts Kerberos and SPNEGO tokens. Some other servers Jboss, Glassfish? + * seem to only accept SPNEGO. Below wraps Kerberos into SPNEGO token. + */ + if (token != null && spengoGenerator != null) { try { - GSSManager manager = getManager(); - GSSName serverName = manager.createName("HTTP@" + authServer, GSSName.NT_HOSTBASED_SERVICE); - gssContext = manager.createContext( - serverName.canonicalize(negotiationOid), negotiationOid, null, - GSSContext.DEFAULT_LIFETIME); - gssContext.requestMutualAuth(true); - gssContext.requestCredDeleg(true); - } catch (GSSException ex){ - // BAD MECH means we are likely to be using 1.5, fall back to Kerberos MECH. - // Rethrow any other exception. - if (ex.getMajor() == GSSException.BAD_MECH ){ - log.debug("GSSException BAD_MECH, retry with Kerberos MECH"); - tryKerberos = true; - } else { - throw ex; - } - - } - if (tryKerberos){ - /* Kerberos v5 GSS-API mechanism defined in RFC 1964.*/ - log.debug("Using Kerberos MECH " + KERBEROS_OID); - negotiationOid = new Oid(KERBEROS_OID); - GSSManager manager = getManager(); - GSSName serverName = manager.createName("HTTP@" + authServer, GSSName.NT_HOSTBASED_SERVICE); - gssContext = manager.createContext( - serverName.canonicalize(negotiationOid), negotiationOid, null, - GSSContext.DEFAULT_LIFETIME); - gssContext.requestMutualAuth(true); - gssContext.requestCredDeleg(true); - } - if (token == null) { - token = new byte[0]; - } - token = gssContext.initSecContext(token, 0, token.length); - if (token == null) { - state = State.FAILED; - throw new AuthenticationException("GSS security context initialization failed"); - } - - /* - * IIS accepts Kerberos and SPNEGO tokens. Some other servers Jboss, Glassfish? - * seem to only accept SPNEGO. Below wraps Kerberos into SPNEGO token. - */ - if (spengoGenerator != null && negotiationOid.toString().equals(KERBEROS_OID)) { token = spengoGenerator.generateSpnegoDERObject(token); + } catch (IOException ex) { + log.error(ex.getMessage(), ex); } - - state = State.TOKEN_GENERATED; - } catch (GSSException gsse) { - state = State.FAILED; - if (gsse.getMajor() == GSSException.DEFECTIVE_CREDENTIAL - || gsse.getMajor() == GSSException.CREDENTIALS_EXPIRED) - throw new InvalidCredentialsException(gsse.getMessage(), gsse); - if (gsse.getMajor() == GSSException.NO_CRED ) - throw new InvalidCredentialsException(gsse.getMessage(), gsse); - if (gsse.getMajor() == GSSException.DEFECTIVE_TOKEN - || gsse.getMajor() == GSSException.DUPLICATE_TOKEN - || gsse.getMajor() == GSSException.OLD_TOKEN) - throw new AuthenticationException(gsse.getMessage(), gsse); - // other error - throw new AuthenticationException(gsse.getMessage()); - } catch (IOException ex){ - state = State.FAILED; - throw new AuthenticationException(ex.getMessage()); } - case TOKEN_GENERATED: - String tokenstr = new String(Base64.encodeBase64(token, false)); - if (log.isDebugEnabled()) { - log.debug("Sending response '" + tokenstr + "' back to the auth server"); - } - return new BasicHeader("Authorization", "Negotiate " + tokenstr); - default: - throw new IllegalStateException("Illegal state: " + state); } + return token; } /** @@ -312,21 +194,4 @@ public class NegotiateScheme extends AuthSchemeBase { return true; } - @Override - protected void parseChallenge( - final CharArrayBuffer buffer, - int beginIndex, int endIndex) throws MalformedChallengeException { - String challenge = buffer.substringTrimmed(beginIndex, endIndex); - if (log.isDebugEnabled()) { - log.debug("Received challenge '" + challenge + "' from the auth server"); - } - if (state == State.UNINITIATED) { - token = new Base64().decode(challenge.getBytes()); - state = State.CHALLENGE_RECEIVED; - } else { - log.debug("Authentication already attempted"); - state = State.FAILED; - } - } - } diff --git a/httpclient/src/main/java/org/apache/http/impl/auth/NegotiateSchemeFactory.java b/httpclient/src/main/java/org/apache/http/impl/auth/NegotiateSchemeFactory.java index 4ad002d46..fb38d83e5 100644 --- a/httpclient/src/main/java/org/apache/http/impl/auth/NegotiateSchemeFactory.java +++ b/httpclient/src/main/java/org/apache/http/impl/auth/NegotiateSchemeFactory.java @@ -26,7 +26,6 @@ package org.apache.http.impl.auth; -import org.apache.http.annotation.Immutable; import org.apache.http.auth.AuthScheme; import org.apache.http.auth.AuthSchemeFactory; import org.apache.http.params.HttpParams; @@ -36,8 +35,10 @@ import org.apache.http.params.HttpParams; * scheme factory. * * @since 4.1 + * + * @deprecated */ -@Immutable +@Deprecated public class NegotiateSchemeFactory implements AuthSchemeFactory { private final SpnegoTokenGenerator spengoGenerator; diff --git a/httpclient/src/main/java/org/apache/http/impl/auth/SPNegoScheme.java b/httpclient/src/main/java/org/apache/http/impl/auth/SPNegoScheme.java new file mode 100644 index 000000000..66ee8db87 --- /dev/null +++ b/httpclient/src/main/java/org/apache/http/impl/auth/SPNegoScheme.java @@ -0,0 +1,115 @@ +/* + * ==================================================================== + * + * 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. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + */ + +package org.apache.http.impl.auth; + +import org.apache.http.Header; +import org.apache.http.HttpRequest; +import org.apache.http.auth.AuthenticationException; +import org.apache.http.auth.Credentials; +import org.apache.http.protocol.HttpContext; +import org.ietf.jgss.GSSException; +import org.ietf.jgss.Oid; + +/** + * SPNEGO (Simple and Protected GSSAPI Negotiation Mechanism) authentication + * scheme. + * + * @since 4.2 + */ +public class SPNegoScheme extends GGSSchemeBase { + + private static final String SPNEGO_OID = "1.3.6.1.5.5.2"; + + public SPNegoScheme(boolean stripPort) { + super(stripPort); + } + + public SPNegoScheme() { + super(false); + } + + public String getSchemeName() { + return "Negotiate"; + } + + /** + * Produces SPNEGO authorization Header based on token created by + * processChallenge. + * + * @param credentials not used by the SPNEGO scheme. + * @param request The request being authenticated + * + * @throws AuthenticationException if authentication string cannot + * be generated due to an authentication failure + * + * @return SPNEGO authentication Header + */ + @Override + public Header authenticate( + final Credentials credentials, + final HttpRequest request, + final HttpContext context) throws AuthenticationException { + return super.authenticate(credentials, request, context); + } + + @Override + protected byte[] generateToken(final byte[] input, final String authServer) throws GSSException { + return generateGSSToken(input, new Oid(SPNEGO_OID), authServer); + } + + /** + * There are no valid parameters for SPNEGO authentication so this + * method always returns null. + * + * @return null + */ + public String getParameter(String name) { + if (name == null) { + throw new IllegalArgumentException("Parameter name may not be null"); + } + return null; + } + + /** + * The concept of an authentication realm is not supported by the Negotiate + * authentication scheme. Always returns null. + * + * @return null + */ + public String getRealm() { + return null; + } + + /** + * Returns true. SPNEGO authentication scheme is connection based. + * + * @return true. + */ + public boolean isConnectionBased() { + return true; + } + +} diff --git a/httpclient/src/main/java/org/apache/http/impl/auth/SPNegoSchemeFactory.java b/httpclient/src/main/java/org/apache/http/impl/auth/SPNegoSchemeFactory.java new file mode 100644 index 000000000..6ee3ac62c --- /dev/null +++ b/httpclient/src/main/java/org/apache/http/impl/auth/SPNegoSchemeFactory.java @@ -0,0 +1,62 @@ +/* + * ==================================================================== + * + * 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. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package org.apache.http.impl.auth; + +import org.apache.http.annotation.Immutable; +import org.apache.http.auth.AuthScheme; +import org.apache.http.auth.AuthSchemeFactory; +import org.apache.http.params.HttpParams; + +/** + * SPNEGO (Simple and Protected GSSAPI Negotiation Mechanism) authentication + * scheme factory. + * + * @since 4.2 + */ +@Immutable +public class SPNegoSchemeFactory implements AuthSchemeFactory { + + private final boolean stripPort; + + public SPNegoSchemeFactory(boolean stripPort) { + super(); + this.stripPort = stripPort; + } + + public SPNegoSchemeFactory() { + this(false); + } + + public AuthScheme newInstance(final HttpParams params) { + return new SPNegoScheme(this.stripPort); + } + + public boolean isStripPort() { + return stripPort; + } + +} diff --git a/httpclient/src/main/java/org/apache/http/impl/auth/SpnegoTokenGenerator.java b/httpclient/src/main/java/org/apache/http/impl/auth/SpnegoTokenGenerator.java index 3e474bb49..0def2f420 100644 --- a/httpclient/src/main/java/org/apache/http/impl/auth/SpnegoTokenGenerator.java +++ b/httpclient/src/main/java/org/apache/http/impl/auth/SpnegoTokenGenerator.java @@ -35,7 +35,11 @@ import java.io.IOException; * Implementations of this interface are expected to be thread-safe. * * @since 4.1 + * + * @deprecated subclass {@link KerberosScheme} and override + * {@link KerberosScheme#generateGSSToken(byte[], org.ietf.jgss.Oid, String)} */ +@Deprecated public interface SpnegoTokenGenerator { byte [] generateSpnegoDERObject(byte [] kerberosTicket) throws IOException; diff --git a/httpclient/src/main/java/org/apache/http/impl/client/AbstractHttpClient.java b/httpclient/src/main/java/org/apache/http/impl/client/AbstractHttpClient.java index 4ef1dd006..956b097f6 100644 --- a/httpclient/src/main/java/org/apache/http/impl/client/AbstractHttpClient.java +++ b/httpclient/src/main/java/org/apache/http/impl/client/AbstractHttpClient.java @@ -74,8 +74,9 @@ import org.apache.http.cookie.CookieSpecRegistry; import org.apache.http.impl.DefaultConnectionReuseStrategy; import org.apache.http.impl.auth.BasicSchemeFactory; import org.apache.http.impl.auth.DigestSchemeFactory; +import org.apache.http.impl.auth.KerberosSchemeFactory; import org.apache.http.impl.auth.NTLMSchemeFactory; -import org.apache.http.impl.auth.NegotiateSchemeFactory; +import org.apache.http.impl.auth.SPNegoSchemeFactory; import org.apache.http.impl.conn.DefaultHttpRoutePlanner; import org.apache.http.impl.conn.PoolingClientConnectionManager; import org.apache.http.impl.conn.SchemeRegistryFactory; @@ -346,7 +347,10 @@ public abstract class AbstractHttpClient implements HttpClient { new NTLMSchemeFactory()); registry.register( AuthPolicy.SPNEGO, - new NegotiateSchemeFactory()); + new SPNegoSchemeFactory()); + registry.register( + AuthPolicy.KERBEROS, + new KerberosSchemeFactory()); return registry; } diff --git a/httpclient/src/main/java/org/apache/http/impl/client/AuthenticationStrategyImpl.java b/httpclient/src/main/java/org/apache/http/impl/client/AuthenticationStrategyImpl.java index 79de9792c..e6263615f 100644 --- a/httpclient/src/main/java/org/apache/http/impl/client/AuthenticationStrategyImpl.java +++ b/httpclient/src/main/java/org/apache/http/impl/client/AuthenticationStrategyImpl.java @@ -66,6 +66,7 @@ class AuthenticationStrategyImpl implements AuthenticationStrategy { private static final List DEFAULT_SCHEME_PRIORITY = Collections.unmodifiableList(Arrays.asList(new String[] { AuthPolicy.SPNEGO, + AuthPolicy.KERBEROS, AuthPolicy.NTLM, AuthPolicy.DIGEST, AuthPolicy.BASIC diff --git a/httpclient/src/main/java/org/apache/http/impl/client/ProxyClient.java b/httpclient/src/main/java/org/apache/http/impl/client/ProxyClient.java index 41ff8892f..19771a433 100644 --- a/httpclient/src/main/java/org/apache/http/impl/client/ProxyClient.java +++ b/httpclient/src/main/java/org/apache/http/impl/client/ProxyClient.java @@ -56,8 +56,9 @@ import org.apache.http.impl.DefaultConnectionReuseStrategy; import org.apache.http.impl.DefaultHttpClientConnection; import org.apache.http.impl.auth.BasicSchemeFactory; import org.apache.http.impl.auth.DigestSchemeFactory; +import org.apache.http.impl.auth.KerberosSchemeFactory; import org.apache.http.impl.auth.NTLMSchemeFactory; -import org.apache.http.impl.auth.NegotiateSchemeFactory; +import org.apache.http.impl.auth.SPNegoSchemeFactory; import org.apache.http.message.BasicHttpRequest; import org.apache.http.params.BasicHttpParams; import org.apache.http.params.HttpParams; @@ -104,7 +105,8 @@ public class ProxyClient { this.authSchemeRegistry.register(AuthPolicy.BASIC, new BasicSchemeFactory()); this.authSchemeRegistry.register(AuthPolicy.DIGEST, new DigestSchemeFactory()); this.authSchemeRegistry.register(AuthPolicy.NTLM, new NTLMSchemeFactory()); - this.authSchemeRegistry.register(AuthPolicy.SPNEGO, new NegotiateSchemeFactory()); + this.authSchemeRegistry.register(AuthPolicy.SPNEGO, new SPNegoSchemeFactory()); + this.authSchemeRegistry.register(AuthPolicy.KERBEROS, new KerberosSchemeFactory()); this.reuseStrategy = new DefaultConnectionReuseStrategy(); this.params = params; } diff --git a/httpclient/src/test/java/org/apache/http/impl/auth/TestNegotiateScheme.java b/httpclient/src/test/java/org/apache/http/impl/auth/TestSPNegoScheme.java similarity index 96% rename from httpclient/src/test/java/org/apache/http/impl/auth/TestNegotiateScheme.java rename to httpclient/src/test/java/org/apache/http/impl/auth/TestSPNegoScheme.java index 80295f0ea..b3a80f56b 100644 --- a/httpclient/src/test/java/org/apache/http/impl/auth/TestNegotiateScheme.java +++ b/httpclient/src/test/java/org/apache/http/impl/auth/TestSPNegoScheme.java @@ -64,7 +64,7 @@ import org.mockito.Mockito; /** * Tests for {@link NegotiateScheme}. */ -public class TestNegotiateScheme extends BasicServerTestBase { +public class TestSPNegoScheme extends BasicServerTestBase { @Before public void setUp() throws Exception { @@ -96,14 +96,14 @@ public class TestNegotiateScheme extends BasicServerTestBase { * Kerberos configuration. * */ - private static class NegotiateSchemeWithMockGssManager extends NegotiateScheme { + private static class NegotiateSchemeWithMockGssManager extends SPNegoScheme { GSSManager manager = Mockito.mock(GSSManager.class); GSSName name = Mockito.mock(GSSName.class); GSSContext context = Mockito.mock(GSSContext.class); NegotiateSchemeWithMockGssManager() throws Exception { - super(null, true); + super(true); Mockito.when(context.initSecContext( Matchers.any(byte[].class), Matchers.anyInt(), Matchers.anyInt())) .thenReturn("12345678".getBytes()); @@ -135,7 +135,7 @@ public class TestNegotiateScheme extends BasicServerTestBase { } - private static class NegotiateSchemeFactoryWithMockGssManager extends NegotiateSchemeFactory { + private static class NegotiateSchemeFactoryWithMockGssManager extends SPNegoSchemeFactory { NegotiateSchemeWithMockGssManager scheme; @@ -161,7 +161,7 @@ public class TestNegotiateScheme extends BasicServerTestBase { HttpHost target = new HttpHost("localhost", port); - NegotiateSchemeFactory nsf = new NegotiateSchemeFactoryWithMockGssManager(); + SPNegoSchemeFactory nsf = new NegotiateSchemeFactoryWithMockGssManager(); this.httpclient.getAuthSchemes().register(AuthPolicy.SPNEGO, nsf);