view j2se/src/share/classes/sun/security/jgss/GSSUtil.java @ 7:807dfe9c366c trunk

[svn] Load openjdk/jdk7/b19 into jdk/trunk.
author xiomara
date Fri, 31 Aug 2007 00:44:13 +0000
parents a4ed3fb96592
children
line wrap: on
line source

/*
 * Copyright 2000-2006 Sun Microsystems, Inc.  All Rights Reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.  Sun designates this
 * particular file as subject to the "Classpath" exception as provided
 * by Sun in the LICENSE file that accompanied this code.
 *
 * This code is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
 * CA 95054 USA or visit www.sun.com if you need additional information or
 * have any questions.
 */
 
package sun.security.jgss;

import com.sun.security.auth.callback.TextCallbackHandler;
import javax.security.auth.Subject;
import javax.security.auth.kerberos.KerberosPrincipal;
import javax.security.auth.kerberos.KerberosTicket;
import javax.security.auth.kerberos.KerberosKey;
import org.ietf.jgss.*;
import sun.security.jgss.spi.GSSNameSpi;
import sun.security.jgss.spi.GSSCredentialSpi;
import sun.security.action.GetPropertyAction;
import sun.security.jgss.krb5.Krb5NameElement;
import sun.security.jgss.spnego.SpNegoCredElement;
import java.util.Set;
import java.util.HashSet;
import java.util.Vector;
import java.util.Iterator;
import java.security.AccessController;
import java.security.AccessControlContext;
import java.security.PrivilegedExceptionAction;
import java.security.PrivilegedActionException;
import javax.security.auth.callback.CallbackHandler;
import javax.security.auth.login.LoginContext;
import javax.security.auth.login.LoginException;
import sun.security.action.GetBooleanAction;

/**
 * The GSSUtilImplementation that knows how to work with the internals of
 * the GSS-API. 
 */
public class GSSUtil {

    public static final Oid GSS_KRB5_MECH_OID =
		GSSUtil.createOid("1.2.840.113554.1.2.2");
    public static final Oid GSS_KRB5_MECH_OID2 = 
                GSSUtil.createOid("1.3.5.1.5.2");

    public static final Oid GSS_SPNEGO_MECH_OID =
		GSSUtil.createOid("1.3.6.1.5.5.2");
    
    public static final Oid NT_GSS_KRB5_PRINCIPAL =
		GSSUtil.createOid("1.2.840.113554.1.2.2.1");

    public static final Oid NT_HOSTBASED_SERVICE2 =
		GSSUtil.createOid("1.2.840.113554.1.2.1.4");

    private static final String DEFAULT_HANDLER =
            "auth.login.defaultCallbackHandler";

    public static final int CALLER_UNKNOWN         = -1;
    public static final int CALLER_INITIATE        = 1;
    public static final int CALLER_ACCEPT          = 2;
    public static final int CALLER_SSL_CLIENT      = 3;
    public static final int CALLER_SSL_SERVER      = 4;
    public static final int CALLER_HTTP_NEGOTIATE  = 5; 

    static final boolean DEBUG;
    static {
	DEBUG = (AccessController.doPrivileged
			(new GetBooleanAction("sun.security.jgss.debug"))).
				booleanValue();
    }

    static void debug(String message) {
        if (DEBUG) {
	    assert(message != null);
            System.out.println(message);
        }
    }

    // NOTE: this method is only for creating Oid objects with
    // known to be valid <code>oidStr</code> given it ignores
    // the GSSException
    public static Oid createOid(String oidStr) {
	try {
            return new Oid(oidStr);
	} catch (GSSException e) {
	    debug("Ignored invalid OID: " + oidStr);
	    return null;
	}
    }

    public static boolean isSpNegoMech(Oid oid) {
	return (GSS_SPNEGO_MECH_OID.equals(oid));
    }

    public static boolean isKerberosMech(Oid oid) {
        return (GSS_KRB5_MECH_OID.equals(oid) || 
		GSS_KRB5_MECH_OID2.equals(oid));

    }

    public static String getMechStr(Oid oid) {
	if (isSpNegoMech(oid)) {
	    return "SPNEGO";
	} else if (isKerberosMech(oid)) {
	    return "Kerberos V5";
	} else {
	    return oid.toString();
	}
    }

    /**
     * Note: The current impl only works with Sun's impl of
     * GSSName and GSSCredential since it depends on package
     * private APIs. 
     */
    public static Subject getSubject(GSSName name,
				     GSSCredential creds) {

	HashSet<Object> privCredentials = null;
	HashSet<Object> pubCredentials = new HashSet<Object>(); // empty Set

	Set<GSSCredentialSpi> gssCredentials = null;

	Set<KerberosPrincipal> krb5Principals =
				new HashSet<KerberosPrincipal>();

	if (name instanceof GSSNameImpl) {
	    try {
		GSSNameSpi ne = ((GSSNameImpl) name).getElement
		    (GSS_KRB5_MECH_OID);
		String krbName = ne.toString();
		if (ne instanceof Krb5NameElement) {
		    krbName = 
			((Krb5NameElement) ne).getKrb5PrincipalName().getName();
		} 
		KerberosPrincipal krbPrinc = new KerberosPrincipal(krbName);
		krb5Principals.add(krbPrinc);
	    } catch (GSSException ge) {
		debug("Skipped name " + name + " due to " + ge);
	    }
	}

	if (creds instanceof GSSCredentialImpl) {
	    gssCredentials = ((GSSCredentialImpl) creds).getElements();
	    privCredentials = new HashSet<Object>(gssCredentials.size());
	    populateCredentials(privCredentials, gssCredentials);
	} else {
	    privCredentials = new HashSet<Object>(); // empty Set
	}
	debug("Created Subject with the following");
	debug("principals=" + krb5Principals);
	debug("public creds=" + pubCredentials);
	debug("private creds=" + privCredentials);

	return new Subject(false, krb5Principals, pubCredentials, 
			   privCredentials);

    }

    /**
     * Populates the set credentials with elements from gssCredentials. At
     * the same time, it converts any subclasses of KerberosTicket
     * into KerberosTicket instances and any subclasses of KerberosKey into
     * KerberosKey instances. (It is not desirable to expose the customer
     * to sun.security.jgss.krb5.Krb5InitCredential which extends
     * KerberosTicket and sun.security.jgss.krb5.Kbr5AcceptCredential which 
     * extends KerberosKey.)
     */
    private static void populateCredentials(Set<Object> credentials, 
					    Set<?> gssCredentials) {

	Object cred;

	Iterator<?> elements = gssCredentials.iterator();
	while (elements.hasNext()) {

	    cred = elements.next();

	    // Retrieve the internal cred out of SpNegoCredElement
	    if (cred instanceof SpNegoCredElement) {
		cred = ((SpNegoCredElement) cred).getInternalCred();
	    }
	    
	    if (cred instanceof KerberosTicket) {
		if (!cred.getClass().getName().equals
		    ("javax.security.auth.kerberos.KerberosTicket")) {
		    KerberosTicket tempTkt = (KerberosTicket) cred;
		    cred = new KerberosTicket(tempTkt.getEncoded(),
					      tempTkt.getClient(),
					      tempTkt.getServer(),
					      tempTkt.getSessionKey().getEncoded(),
					      tempTkt.getSessionKeyType(),
					      tempTkt.getFlags(),
					      tempTkt.getAuthTime(),
					      tempTkt.getStartTime(),
					      tempTkt.getEndTime(),
					      tempTkt.getRenewTill(),
					      tempTkt.getClientAddresses());
		}
		credentials.add(cred);
	    } else if (cred instanceof KerberosKey) {
		if (!cred.getClass().getName().equals
		    ("javax.security.auth.kerberos.KerberosKey")) {
		    KerberosKey tempKey = (KerberosKey) cred;
		    cred = new KerberosKey(tempKey.getPrincipal(),
					   tempKey.getEncoded(),
					   tempKey.getKeyType(),
					   tempKey.getVersionNumber());
		}
		credentials.add(cred);
	    } else {
		// Ignore non-KerberosTicket and non-KerberosKey elements
		debug("Skipped cred element: " + cred);
	    }
	}
    }
    
    /**
     * Authenticate using the login module from the specified
     * configuration entry.
     *
     * @param caller the caller of JAAS Login
     * @param mech the mech to be used
     * @return the authenticated subject
     */
    public static Subject login(int caller, Oid mech) throws LoginException {
        
        CallbackHandler cb = null;
        if (caller == GSSUtil.CALLER_HTTP_NEGOTIATE) {
            cb = new sun.net.www.protocol.http.NegotiateCallbackHandler();
        } else {
            String defaultHandler =
                    java.security.Security.getProperty(DEFAULT_HANDLER);
            // get the default callback handler
            if ((defaultHandler != null) && (defaultHandler.length() != 0)) {
                cb = null;
            } else {
                cb = new TextCallbackHandler();
            }
        }

        // New instance of LoginConfigImpl must be created for each login,
        // since the entry name is not passed as the first argument, but 
        // generated with caller and mech inside LoginConfigImpl
        LoginContext lc = new LoginContext("", null, cb,
                new LoginConfigImpl(caller, mech));
        lc.login();
        return lc.getSubject();
    }

    /**
     * Determines if the application doesn't mind if the mechanism obtains
     * the required credentials from outside of the current Subject. Our
     * Kerberos v5 mechanism would do a JAAS login on behalf of the
     * application if this were the case.
     *
     * The application indicates this by explicitly setting the system
     * property javax.security.auth.useSubjectCredsOnly to false.
     */
    public static boolean useSubjectCredsOnly(int caller) {
        
        // HTTP/SPNEGO doesn't use the standard JAAS framework. Instead, it 
        // uses the java.net.Authenticator style, therefore always return 
        // false here.
        if (caller == CALLER_HTTP_NEGOTIATE) {
            return false;
        }
        /*
         * Don't use GetBooleanAction because the default value in the JRE
         * (when this is unset) has to treated as true.
         */
        String propValue = AccessController.doPrivileged(
                new GetPropertyAction("javax.security.auth.useSubjectCredsOnly",
                "true"));
        /*
         * This property has to be explicitly set to "false". Invalid
         * values should be ignored and the default "true" assumed.
         */
        return (!propValue.equalsIgnoreCase("false"));
    }
    
    /**
     * Determines the SPNEGO interoperability mode with Microsoft;
     * by default it is set to true.
     *
     * To disable it, the application indicates this by explicitly setting
     * the system property sun.security.spnego.interop to false.
     */
    public static boolean useMSInterop() {
        /*
         * Don't use GetBooleanAction because the default value in the JRE
         * (when this is unset) has to treated as true.
         */
        String propValue = AccessController.doPrivileged(
                new GetPropertyAction("sun.security.spnego.msinterop",
                "true"));
        /*
         * This property has to be explicitly set to "false". Invalid
         * values should be ignored and the default "true" assumed.
         */
        return (!propValue.equalsIgnoreCase("false"));
    }

    /**
     * Searches the private credentials of current Subject with the 
     * specified criteria and returns the matching GSSCredentialSpi
     * object out of Sun's impl of GSSCredential. Returns null if 
     * no Subject present or a Vector which contains 0 or more 
     * matching GSSCredentialSpi objects.
     */
    public static Vector searchSubject(final GSSNameSpi name,
				       final Oid mech, 
				       final boolean initiate,
				       final Class credCls) {
        debug("Search Subject for " + getMechStr(mech) +
	      (initiate? " INIT" : " ACCEPT") + " cred (" + 
	      (name == null? "<<DEF>>" : name.toString()) + ", " +
	      credCls.getName() + ")");
	final AccessControlContext acc = AccessController.getContext();
	try {
	    Vector creds = 
		AccessController.doPrivileged
		(new PrivilegedExceptionAction<Vector>() {
		    public Vector run() throws Exception {
			Subject accSubj = Subject.getSubject(acc);
			Vector<GSSCredentialSpi> result = null;
			if (accSubj != null) {
			    result = new Vector<GSSCredentialSpi>();
			    Iterator<GSSCredentialImpl> iterator =
				accSubj.getPrivateCredentials
				(GSSCredentialImpl.class).iterator();
			    while (iterator.hasNext()) {
				GSSCredentialImpl cred = iterator.next();
				debug("...Found cred" + cred);
				try {
				    GSSCredentialSpi ce =
					cred.getElement(mech, initiate);
				    debug("......Found element: " + ce);
				    if (ce.getClass().equals(credCls) &&
					(name == null || 
					 name.equals((Object) ce.getName()))) {
					result.add(ce);
				    } else {
					debug("......Discard element");
				    }
				} catch (GSSException ge) {
				    debug("...Discard cred (" + ge + ")");
				}
			    }
			} else debug("No Subject");
			return result;
		    }
		});
	    return creds;
	} catch (PrivilegedActionException pae) {
	    debug("Unexpected exception when searching Subject:");
	    if (DEBUG) pae.printStackTrace();
	    return null;
	}
    }
}