hc-dev mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From Ortwin Glück <...@odi.ch>
Subject Re: SPNEGO support in httpclient
Date Tue, 01 Nov 2005 14:56:41 GMT
Mikael,

First of all, thanks for your contribution!

We may consider your code for the contrib package for 3.0 because
   i) it is too late in the release process for 3.0
   ii) JGSS API is only available as of JDK 1.4 while HttpClient 3.0 is 
compatible with JDK 1.2 (sigh)

We may decide to include it in the 4.0 code base though. For this step 
some JUnit tests would be nice.

Personally I have no objections. What do the others think?

Ortwin Glück

Mikael Wikström wrote:
> this is a followup on my previus mail with the same title sent 08/03/2005
> 
> Hi,
> 
> finally... Here's my spnego/Negotiate AuthScheme for httpclient 3.0.
> 
> It's more or less complete and rely on JAAS for credentials and jgss for
> mutual auth and credential forwarding.
> 
> My next goal will be to se if I can use this scheme with axis (SOAP)
> over ssl to create a secure WS that can use forwarded credentials.
> 
> I really hope this patch will become part of the main dist.
> 
> references:
> http://www.ietf.org/internet-drafts/draft-jaganathan-kerberos-http-00.txt
> http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnsecure/html/http-sso-2.asp
> 
> http://www11.informatik.tu-muenchen.de/Java/j2sdkse/guide/security/jgss/tutorials/ClientServer.html
> 
> 
> Regards
> / Mikael Wikstrom
> mikael.wikstrom@it.su.se
> 
> 
> 
> 
> ------------------------------------------------------------------------
> 
> /*
>  * $Header: /home/jerenkrantz/tmp/commons/commons-convert/cvs/home/cvs/jakarta-commons//httpclient/src/examples/CustomAuthenticationExample.java,v
1.1 2004/09/06 20:10:02 mbecke Exp $
>  * $Revision: 155418 $
>  * $Date: 2005-02-26 14:01:52 +0100 (Sat, 26 Feb 2005) $
>  *
>  * ====================================================================
>  *
>  *  Copyright 2002-2004 The Apache Software Foundation
>  *
>  *  Licensed 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
>  * <http://www.apache.org/>.
>  *
>  */
> import java.util.ArrayList;
> 
> import org.apache.commons.httpclient.Credentials;
> import org.apache.commons.httpclient.HttpClient;
> import org.apache.commons.httpclient.auth.AuthPolicy;
> import org.apache.commons.httpclient.auth.AuthScope;
> import org.apache.commons.httpclient.auth.NegotiateScheme;
> import org.apache.commons.httpclient.methods.GetMethod;
> import org.apache.commons.httpclient.params.DefaultHttpParams;
> import org.apache.commons.httpclient.params.HttpParams;
> 
> 
> /**
>  * A simple custom AuthScheme example.  The included auth scheme is meant 
>  * for demonstration purposes only.  It does not actually implement a usable
>  * authentication method.
>  */
> public class CustomAuthenticationNegotiateExample {
> 
>     public static void main(String[] args) {
>         
>         // register the auth scheme
>         AuthPolicy.registerAuthScheme("Negotiate", NegotiateScheme.class);
> 
>         // include the scheme in the AuthPolicy.AUTH_SCHEME_PRIORITY preference
>         ArrayList schemes = new ArrayList();
>         schemes.add("Negotiate");
> 
>         HttpParams params = DefaultHttpParams.getDefaultParams();        
>         params.setParameter(AuthPolicy.AUTH_SCHEME_PRIORITY, schemes);
>         
>         // now that our scheme has been registered we can execute methods against
>         // servers that require "Negotiate" authentication... 
>         HttpClient client = new HttpClient();
>         
>         // The Negotiate scheme uses JAAS as credential provider but the
>         // httpclient api require us to supply cred anyway.
>         // a work around is to provide an empty set of creds.
>         Credentials use_jaas_creds = new Credentials() {};
>         client.getState().setCredentials(
>             new AuthScope(null, -1, null),
>             use_jaas_creds);
>         GetMethod httpget = new GetMethod(args[0]);
> 
>         try {
>             client.executeMethod(httpget);
>             //System.out.println(httpget.getStatusLine());
>             //System.out.println(httpget.getResponseBodyAsString());
>         } catch (Exception e) {
>             e.printStackTrace();            
>         } finally {
>             // release any connection resources used by the method
>             httpget.releaseConnection();
>         }            
>         
>     }
> } 
> 
> 
> ------------------------------------------------------------------------
> 
> /*
>  * $Header:$
>  * $Revision:$
>  * $Date:$
>  *
>  * ====================================================================
>  *
>  *  Copyright 2002-2004 The Apache Software Foundation
>  *
>  *  Licensed 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
>  * <http://www.apache.org/>.
>  *
>  */
> 
> package org.apache.commons.httpclient.auth;
> 
> import org.apache.commons.codec.binary.Base64;
> import org.apache.commons.httpclient.Credentials;
> import org.apache.commons.httpclient.HttpMethod;
> import org.apache.commons.logging.Log;
> import org.apache.commons.logging.LogFactory;
> 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;
> 
> /** 
>  * 
>  * @author <a href="mailto:mikael.wikstrom@it.su.se">Mikael Wilstrom</a>
>  * @author Mikael Wikstrom
>  */
> public class NegotiateScheme implements AuthScheme {
> 
>     /** Log object for this class. */
>     private static final Log LOG = LogFactory.getLog(NegotiateScheme.class);
> 
>     /** challenge string. */
>     private String challenge = null;
> 
>     private static final int UNINITIATED         = 0;
>     private static final int INITIATED           = 1;
>     private static final int NEGOTIATING         = 3;
>     private static final int ESTABLISHED         = 4;
>     private static final int FAILED              = Integer.MAX_VALUE;
> 
>     private GSSContext context = null;
> 
>     /** Authentication process state */
>     private int state;
> 
>     /** base64 decoded challenge **/
>     byte[] token = new byte[0];
> 
>     /**
>      * Init GSSContext for negotiation.
>      * 
>      * @param server servername only (e.g: radar.it.su.se)
>      */
>     protected void init(String server) throws GSSException {
>          LOG.debug("init " + server);
>          /* Kerberos v5 GSS-API mechanism defined in RFC 1964. */
>          Oid krb5Oid = new Oid("1.2.840.113554.1.2.2");
>          GSSManager manager = GSSManager.getInstance();
>          GSSName serverName = manager.createName("HTTP/"+server, null); 
>          context = manager.createContext(serverName, krb5Oid, null,
>                                     GSSContext.DEFAULT_LIFETIME);
>          context.requestMutualAuth(true); 
>          context.requestCredDeleg(true);
>          state = INITIATED;
>     }
>     
>     /**
>      * Default constructor for the Negotiate authentication scheme.
>      * 
>      * @since 3.0
>      */
>     public NegotiateScheme() {
>         super();
>         state = UNINITIATED;
>     }
> 
>     /**
>      * Constructor for the Negotiate authentication scheme.
>      * 
>      * @param challenge The authentication challenge
>      */
>     public NegotiateScheme(final String challenge) {
>         super();
>         LOG.debug("enter NegotiateScheme("+challenge+")");
>         processChallenge(challenge);
>     }
> 
>     /**
>      * Processes the Negotiate challenge.
>      *  
>      * @param challenge the challenge string
>      * 
>      * @since 3.0
>      */
>     public void processChallenge(final String challenge){
>         LOG.debug("enter processChallenge(challenge=\""+challenge+"\")");
>         if (challenge.startsWith("Negotiate")) {
>             if(isComplete() == false)
>                 state = NEGOTIATING;
>             
>             if (challenge.startsWith("Negotiate "))
>                 token = new Base64().decode(challenge.substring(10).getBytes());
>             else
>                 token = new byte[0];
>         }
>     }
> 
>     /**
>      * Tests if the Negotiate authentication process has been completed.
>      * 
>      * @return <tt>true</tt> if authorization has been processed,
>      *   <tt>false</tt> otherwise.
>      * 
>      * @since 3.0
>      */
>     public boolean isComplete() {
>         LOG.debug("enter isComplete()");
>         return this.state == ESTABLISHED || this.state == FAILED;
>     }
> 
>     /**
>      * Returns textual designation of the Negotiate authentication scheme.
>      * 
>      * @return <code>Negotiate</code>
>      */
>     public String getSchemeName() {
>         return "Negotiate";
>     }
> 
>     /**
>      * The concept of an authentication realm is not supported by the Negotiate 
>      * authentication scheme. Always returns <code>null</code>.
>      * 
>      * @return <code>null</code>
>      */
>     public String getRealm() {
>         return null;
>     }
>     
>     /**
>      * Returns a String identifying the authentication challenge.  This is
>      * used, in combination with the host and port to determine if
>      * authorization has already been attempted or not.  Schemes which
>      * require multiple requests to complete the authentication should
>      * return a different value for each stage in the request.
>      * 
>      * <p>Additionally, the ID should take into account any changes to the
>      * authentication challenge and return a different value when appropriate.
>      * For example when the realm changes in basic authentication it should be
>      * considered a different authentication attempt and a different value should
>      * be returned.</p>
>      * 
>      * @return String a String identifying the authentication challenge.  The
>      * returned value may be null.
>      * 
>      * @deprecated no longer used
>      */
>     public String getID() {
>         LOG.debug("enter getID(): " + challenge);
>         return challenge;
>     }
>     
>     /**
>      * Returns the authentication parameter with the given name, if available.
>      * 
>      * <p>There are no valid parameters for Negotiate authentication so this 
>      * method always returns <tt>null</tt>.</p>
>      * 
>      * @param name The name of the parameter to be returned
>      * 
>      * @return the parameter with the given name
>      */
>     public String getParameter(String name) {
>         LOG.debug("enter getParameter("+name+")");
>         if (name == null) {
>             throw new IllegalArgumentException("Parameter name may not be null"); 
>         }
>         return null;
>     }
> 
>     /**
>      * Returns <tt>true</tt>. 
>      * Negotiate authentication scheme is connection based.
>      * 
>      * @return <tt>true</tt>.
>      * 
>      * @since 3.0
>      */
>     public boolean isConnectionBased() {
>         LOG.info("enter isConnectionBased()");
>         return true;
>     }
> 
>     /**
>      * Method not supported by Negotiate scheme. 
>      * 
>      * @throws AuthenticationException if called.
>      * 
>      * @deprecated Use {@link #authenticate(Credentials, HttpMethod)}
>      */
>     public String authenticate(Credentials credentials, String method, String uri) 
>       throws AuthenticationException {
>         throw new AuthenticationException("method not supported by Negotiate scheme");
>     }
>     
>     /**
>      * Produces Negotiate authorization string based on token created by 
>      * processChallenge.
>      * 
>      * @param credentials Never used be the Negotiate scheme but must be provided to

>      * satisfy common-httpclient API. Credentials from JAAS will be used insted.
>      * @param method The method being authenticated
>      * 
>      * @throws AuthenticationException if authorization string cannot 
>      *   be generated due to an authentication failure
>      * 
>      * @return an Negotiate authorization string
>      * 
>      * @since 3.0
>      */
>     public String authenticate(
>         Credentials credentials, 
>         HttpMethod method
>     ) throws AuthenticationException {
>         LOG.debug("enter NegotiateScheme.authenticate(Credentials, HttpMethod)");
> 
>         if (state == UNINITIATED) {
>             throw new IllegalStateException(
>                "Negotiation authentication process has not been initiated");
>         }
> 
>         try {
>             try {                
>                 if(context==null) {
>                     LOG.info("host: " + method.getURI().getHost());
>                     init( method.getURI().getHost() );
>                 }
>             } catch (org.apache.commons.httpclient.URIException urie) {
>                 LOG.error(urie.getMessage());
>                 state = FAILED;
>                 throw new AuthenticationException(urie.getMessage());
>             }
>         
>             // HTTP 1.1 issue:
>             // Mutual auth will never complete do to 200 insted of 401 in 
>             // return from server. "state" will never reach ESTABLISHED
>             // but it works anyway
>             token = context.initSecContext(token, 0, token.length);
>             LOG.info("got token, sending " + token.length + " to server");
>         } catch (GSSException gsse) {
>             LOG.fatal(gsse.getMessage());
>             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 CredentialsNotAvailableException(gsse.getMessage(),gsse);
>             if( gsse.getMajor() == GSSException.DEFECTIVE_TOKEN
>                     || gsse.getMajor() == GSSException.DUPLICATE_TOKEN
>                     || gsse.getMajor() == GSSException.OLD_TOKEN )
>                 throw new AuthChallengeException(gsse.getMessage(),gsse);
>             // other error
>             throw new AuthenticationException(gsse.getMessage());
>         }
>         return "Negotiate " + new String(new Base64().encode(token));
>     }
> }
> 
> 
> ------------------------------------------------------------------------
> 
> /** 
>  * Login Configuration for JAAS.
>  */
> 
> com.sun.security.jgss.initiate {
>   com.sun.security.auth.module.Krb5LoginModule required client=TRUE useTicketCache="true"
ticketCache="${user.krb5cc}" debug=true;
> };
> 
> com.sun.security.jgss.accept {
>   com.sun.security.auth.module.Krb5LoginModule required client=TRUE useTicketCache="true"
ticketCache="${user.krb5cc}" debug=true;
> };
> 
> 
> ------------------------------------------------------------------------
> 
> ---------------------------------------------------------------------
> To unsubscribe, e-mail: httpclient-dev-unsubscribe@jakarta.apache.org
> For additional commands, e-mail: httpclient-dev-help@jakarta.apache.org


---------------------------------------------------------------------
To unsubscribe, e-mail: httpclient-dev-unsubscribe@jakarta.apache.org
For additional commands, e-mail: httpclient-dev-help@jakarta.apache.org


Mime
View raw message