/* * Copyright (c) 2000, 2010, Oracle and/or its affiliates. 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. Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.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"); private static final String DEFAULT_HANDLER = "auth.login.defaultCallbackHandler"; 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 oidStr 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 privCredentials = null; HashSet pubCredentials = new HashSet(); // empty Set Set gssCredentials = null; Set krb5Principals = new HashSet(); 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(gssCredentials.size()); populateCredentials(privCredentials, gssCredentials); } else { privCredentials = new HashSet(); // 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 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(GSSCaller caller, Oid mech) throws LoginException { CallbackHandler cb = null; if (caller instanceof HttpCaller) { cb = new sun.net.www.protocol.http.spnego.NegotiateCallbackHandler( ((HttpCaller)caller).info()); } 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(GSSCaller 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 instanceof HttpCaller) { 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? "<>" : name.toString()) + ", " + credCls.getName() + ")"); final AccessControlContext acc = AccessController.getContext(); try { Vector creds = AccessController.doPrivileged (new PrivilegedExceptionAction() { public Vector run() throws Exception { Subject accSubj = Subject.getSubject(acc); Vector result = null; if (accSubj != null) { result = new Vector(); Iterator 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; } } }