From e82fdff2e9ef0ba00dfc4e81d0877388b0d5a7b1 Mon Sep 17 00:00:00 2001 From: Oleg Kalnichevski Date: Mon, 8 Sep 2014 07:44:18 +0000 Subject: [PATCH] Adding support for using delegated GSSCredential for Kerberos authentication Contributed by Vipul Mehta Internally httpclient relies on GSS API which uses JAAS login configuration specified by user to get the GSSCredential. This patch will allow a user to avoid the config file and directly set a delegated or normal GSSCredential. A normal GSSCredential can be obtained programatically from spn-keytab or user-password using custom Login module. git-svn-id: https://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk@1623311 13f79535-47bb-0310-9956-ffa450edef68 --- .../http/impl/auth/NegotiateScheme.java | 9 ++- .../apache/http/auth/KerberosCredentials.java | 69 +++++++++++++++++++ .../apache/http/impl/auth/GGSSchemeBase.java | 42 +++++++++-- .../apache/http/impl/auth/KerberosScheme.java | 9 ++- .../apache/http/impl/auth/SPNegoScheme.java | 9 ++- 5 files changed, 128 insertions(+), 10 deletions(-) create mode 100644 httpclient/src/main/java/org/apache/http/auth/KerberosCredentials.java diff --git a/httpclient/src/main/java-deprecated/org/apache/http/impl/auth/NegotiateScheme.java b/httpclient/src/main/java-deprecated/org/apache/http/impl/auth/NegotiateScheme.java index ff6dd3632..d39a0193c 100644 --- a/httpclient/src/main/java-deprecated/org/apache/http/impl/auth/NegotiateScheme.java +++ b/httpclient/src/main/java-deprecated/org/apache/http/impl/auth/NegotiateScheme.java @@ -113,6 +113,11 @@ public class NegotiateScheme extends GGSSchemeBase { @Override protected byte[] generateToken(final byte[] input, final String authServer) throws GSSException { + return super.generateToken(input, authServer); + } + + @Override + protected byte[] generateToken(final byte[] input, final String authServer, final Credentials credentials) 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 @@ -133,7 +138,7 @@ public class NegotiateScheme extends GGSSchemeBase { byte[] token = input; boolean tryKerberos = false; try { - token = generateGSSToken(token, negotiationOid, authServer); + token = generateGSSToken(token, negotiationOid, authServer, credentials); } catch (final GSSException ex){ // BAD MECH means we are likely to be using 1.5, fall back to Kerberos MECH. // Rethrow any other exception. @@ -149,7 +154,7 @@ public class NegotiateScheme extends GGSSchemeBase { /* 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); + token = generateGSSToken(token, negotiationOid, authServer, credentials); /* * IIS accepts Kerberos and SPNEGO tokens. Some other servers Jboss, Glassfish? diff --git a/httpclient/src/main/java/org/apache/http/auth/KerberosCredentials.java b/httpclient/src/main/java/org/apache/http/auth/KerberosCredentials.java new file mode 100644 index 000000000..05cb43357 --- /dev/null +++ b/httpclient/src/main/java/org/apache/http/auth/KerberosCredentials.java @@ -0,0 +1,69 @@ +/* + * ==================================================================== + * 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.auth; + +import java.io.Serializable; +import java.security.Principal; + +import org.apache.http.annotation.Immutable; +import org.ietf.jgss.GSSCredential; + +/** + * {@link Credentials} implementation based on GSSCredential for Kerberos Authentication. + * + * @since 4.4 + */ +@Immutable +public class KerberosCredentials implements Credentials, Serializable { + + private static final long serialVersionUID = 487421613855550713L; + + /** GSSCredential */ + private final GSSCredential gssCredential; + + /** + * Constructor with GSSCredential argument + * + * @param gssCredential + */ + public KerberosCredentials(final GSSCredential gssCredential) { + this.gssCredential = gssCredential; + } + + public GSSCredential getGSSCredential() { + return gssCredential; + } + + public Principal getUserPrincipal() { + return null; + } + + public String getPassword() { + return null; + } + +} 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 index 88636dd2d..eba45c214 100644 --- a/httpclient/src/main/java/org/apache/http/impl/auth/GGSSchemeBase.java +++ b/httpclient/src/main/java/org/apache/http/impl/auth/GGSSchemeBase.java @@ -40,6 +40,7 @@ import org.apache.http.auth.AUTH; import org.apache.http.auth.AuthenticationException; import org.apache.http.auth.Credentials; import org.apache.http.auth.InvalidCredentialsException; +import org.apache.http.auth.KerberosCredentials; import org.apache.http.auth.MalformedChallengeException; import org.apache.http.client.protocol.HttpClientContext; import org.apache.http.conn.routing.HttpRoute; @@ -48,6 +49,7 @@ import org.apache.http.protocol.HttpContext; import org.apache.http.util.Args; import org.apache.http.util.CharArrayBuffer; 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; @@ -100,21 +102,53 @@ public abstract class GGSSchemeBase extends AuthSchemeBase { protected byte[] generateGSSToken( final byte[] input, final Oid oid, final String authServer) throws GSSException { + return generateGSSToken(input, oid, authServer, null); + } + + /** + * @since 4.4 + */ + protected byte[] generateGSSToken( + final byte[] input, final Oid oid, final String authServer, + final Credentials credentials) throws GSSException { byte[] inputBuff = input; if (inputBuff == null) { inputBuff = new byte[0]; } 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 = manager.createContext( - serverName.canonicalize(oid), oid, null, GSSContext.DEFAULT_LIFETIME); + serverName.canonicalize(oid), oid, gssCredential, GSSContext.DEFAULT_LIFETIME); gssContext.requestMutualAuth(true); gssContext.requestCredDeleg(true); return gssContext.initSecContext(inputBuff, 0, inputBuff.length); } - protected abstract byte[] generateToken( - byte[] input, final String authServer) throws GSSException; + /** + * @deprecated (4.4) Use {@link #generateToken(byte[], String, org.apache.http.auth.Credentials)}. + */ + @Deprecated + protected byte[] generateToken(final byte[] input, final String authServer) throws GSSException { + return null; + } + + /** + * @since 4.4 + */ + //TODO: make this method abstract + @SuppressWarnings("deprecation") + protected byte[] generateToken( + final byte[] input, final String authServer, final Credentials credentials) throws GSSException { + return generateToken(input, authServer); + } @Override public boolean isComplete() { @@ -181,7 +215,7 @@ public abstract class GGSSchemeBase extends AuthSchemeBase { if (log.isDebugEnabled()) { log.debug("init " + authServer); } - token = generateToken(token, authServer); + token = generateToken(token, authServer, credentials); state = State.TOKEN_GENERATED; } catch (final GSSException gsse) { 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 index 489ed8351..9b2ba6206 100644 --- a/httpclient/src/main/java/org/apache/http/impl/auth/KerberosScheme.java +++ b/httpclient/src/main/java/org/apache/http/impl/auth/KerberosScheme.java @@ -86,9 +86,14 @@ public class KerberosScheme extends GGSSchemeBase { return super.authenticate(credentials, request, context); } - @Override + @Override @SuppressWarnings("deprecation") protected byte[] generateToken(final byte[] input, final String authServer) throws GSSException { - return generateGSSToken(input, new Oid(KERBEROS_OID), authServer); + return super.generateToken(input, authServer); + } + + @Override + protected byte[] generateToken(final byte[] input, final String authServer, final Credentials credentials) throws GSSException { + return generateGSSToken(input, new Oid(KERBEROS_OID), authServer, credentials); } /** 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 index 07be6d6ee..808738b44 100644 --- a/httpclient/src/main/java/org/apache/http/impl/auth/SPNegoScheme.java +++ b/httpclient/src/main/java/org/apache/http/impl/auth/SPNegoScheme.java @@ -87,9 +87,14 @@ public class SPNegoScheme extends GGSSchemeBase { return super.authenticate(credentials, request, context); } - @Override + @Override @SuppressWarnings("deprecation") protected byte[] generateToken(final byte[] input, final String authServer) throws GSSException { - return generateGSSToken(input, new Oid(SPNEGO_OID), authServer); + return super.generateToken(input, authServer); + } + + @Override + protected byte[] generateToken(final byte[] input, final String authServer, final Credentials credentials) throws GSSException { + return generateGSSToken(input, new Oid(SPNEGO_OID), authServer, credentials); } /**