0N/A/*
2362N/A * Copyright (c) 2000, 2010, Oracle and/or its affiliates. All rights reserved.
0N/A * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
0N/A *
0N/A * This code is free software; you can redistribute it and/or modify it
0N/A * under the terms of the GNU General Public License version 2 only, as
2362N/A * published by the Free Software Foundation. Oracle designates this
0N/A * particular file as subject to the "Classpath" exception as provided
2362N/A * by Oracle in the LICENSE file that accompanied this code.
0N/A *
0N/A * This code is distributed in the hope that it will be useful, but WITHOUT
0N/A * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
0N/A * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
0N/A * version 2 for more details (a copy is included in the LICENSE file that
0N/A * accompanied this code).
0N/A *
0N/A * You should have received a copy of the GNU General Public License version
0N/A * 2 along with this work; if not, write to the Free Software Foundation,
0N/A * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
0N/A *
2362N/A * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
2362N/A * or visit www.oracle.com if you need additional information or have any
2362N/A * questions.
0N/A */
0N/A
0N/Apackage sun.security.jgss;
0N/A
0N/Aimport com.sun.security.auth.callback.TextCallbackHandler;
0N/Aimport javax.security.auth.Subject;
0N/Aimport javax.security.auth.kerberos.KerberosPrincipal;
0N/Aimport javax.security.auth.kerberos.KerberosTicket;
0N/Aimport javax.security.auth.kerberos.KerberosKey;
0N/Aimport org.ietf.jgss.*;
0N/Aimport sun.security.jgss.spi.GSSNameSpi;
0N/Aimport sun.security.jgss.spi.GSSCredentialSpi;
0N/Aimport sun.security.action.GetPropertyAction;
0N/Aimport sun.security.jgss.krb5.Krb5NameElement;
0N/Aimport sun.security.jgss.spnego.SpNegoCredElement;
0N/Aimport java.util.Set;
0N/Aimport java.util.HashSet;
0N/Aimport java.util.Vector;
0N/Aimport java.util.Iterator;
0N/Aimport java.security.AccessController;
0N/Aimport java.security.AccessControlContext;
0N/Aimport java.security.PrivilegedExceptionAction;
0N/Aimport java.security.PrivilegedActionException;
0N/Aimport javax.security.auth.callback.CallbackHandler;
0N/Aimport javax.security.auth.login.LoginContext;
0N/Aimport javax.security.auth.login.LoginException;
0N/Aimport sun.security.action.GetBooleanAction;
0N/A
0N/A/**
0N/A * The GSSUtilImplementation that knows how to work with the internals of
0N/A * the GSS-API.
0N/A */
0N/Apublic class GSSUtil {
0N/A
0N/A public static final Oid GSS_KRB5_MECH_OID =
0N/A GSSUtil.createOid("1.2.840.113554.1.2.2");
0N/A public static final Oid GSS_KRB5_MECH_OID2 =
0N/A GSSUtil.createOid("1.3.5.1.5.2");
0N/A
0N/A public static final Oid GSS_SPNEGO_MECH_OID =
0N/A GSSUtil.createOid("1.3.6.1.5.5.2");
0N/A
0N/A public static final Oid NT_GSS_KRB5_PRINCIPAL =
0N/A GSSUtil.createOid("1.2.840.113554.1.2.2.1");
0N/A
0N/A private static final String DEFAULT_HANDLER =
0N/A "auth.login.defaultCallbackHandler";
0N/A
0N/A static final boolean DEBUG;
0N/A static {
0N/A DEBUG = (AccessController.doPrivileged
0N/A (new GetBooleanAction("sun.security.jgss.debug"))).
0N/A booleanValue();
0N/A }
0N/A
0N/A static void debug(String message) {
0N/A if (DEBUG) {
0N/A assert(message != null);
0N/A System.out.println(message);
0N/A }
0N/A }
0N/A
0N/A // NOTE: this method is only for creating Oid objects with
0N/A // known to be valid <code>oidStr</code> given it ignores
0N/A // the GSSException
0N/A public static Oid createOid(String oidStr) {
0N/A try {
0N/A return new Oid(oidStr);
0N/A } catch (GSSException e) {
0N/A debug("Ignored invalid OID: " + oidStr);
0N/A return null;
0N/A }
0N/A }
0N/A
0N/A public static boolean isSpNegoMech(Oid oid) {
0N/A return (GSS_SPNEGO_MECH_OID.equals(oid));
0N/A }
0N/A
0N/A public static boolean isKerberosMech(Oid oid) {
0N/A return (GSS_KRB5_MECH_OID.equals(oid) ||
0N/A GSS_KRB5_MECH_OID2.equals(oid));
0N/A
0N/A }
0N/A
0N/A public static String getMechStr(Oid oid) {
0N/A if (isSpNegoMech(oid)) {
0N/A return "SPNEGO";
0N/A } else if (isKerberosMech(oid)) {
0N/A return "Kerberos V5";
0N/A } else {
0N/A return oid.toString();
0N/A }
0N/A }
0N/A
0N/A /**
0N/A * Note: The current impl only works with Sun's impl of
0N/A * GSSName and GSSCredential since it depends on package
0N/A * private APIs.
0N/A */
0N/A public static Subject getSubject(GSSName name,
0N/A GSSCredential creds) {
0N/A
0N/A HashSet<Object> privCredentials = null;
0N/A HashSet<Object> pubCredentials = new HashSet<Object>(); // empty Set
0N/A
0N/A Set<GSSCredentialSpi> gssCredentials = null;
0N/A
0N/A Set<KerberosPrincipal> krb5Principals =
0N/A new HashSet<KerberosPrincipal>();
0N/A
0N/A if (name instanceof GSSNameImpl) {
0N/A try {
0N/A GSSNameSpi ne = ((GSSNameImpl) name).getElement
0N/A (GSS_KRB5_MECH_OID);
0N/A String krbName = ne.toString();
0N/A if (ne instanceof Krb5NameElement) {
0N/A krbName =
0N/A ((Krb5NameElement) ne).getKrb5PrincipalName().getName();
0N/A }
0N/A KerberosPrincipal krbPrinc = new KerberosPrincipal(krbName);
0N/A krb5Principals.add(krbPrinc);
0N/A } catch (GSSException ge) {
0N/A debug("Skipped name " + name + " due to " + ge);
0N/A }
0N/A }
0N/A
0N/A if (creds instanceof GSSCredentialImpl) {
0N/A gssCredentials = ((GSSCredentialImpl) creds).getElements();
0N/A privCredentials = new HashSet<Object>(gssCredentials.size());
0N/A populateCredentials(privCredentials, gssCredentials);
0N/A } else {
0N/A privCredentials = new HashSet<Object>(); // empty Set
0N/A }
0N/A debug("Created Subject with the following");
0N/A debug("principals=" + krb5Principals);
0N/A debug("public creds=" + pubCredentials);
0N/A debug("private creds=" + privCredentials);
0N/A
0N/A return new Subject(false, krb5Principals, pubCredentials,
0N/A privCredentials);
0N/A
0N/A }
0N/A
0N/A /**
0N/A * Populates the set credentials with elements from gssCredentials. At
0N/A * the same time, it converts any subclasses of KerberosTicket
0N/A * into KerberosTicket instances and any subclasses of KerberosKey into
0N/A * KerberosKey instances. (It is not desirable to expose the customer
0N/A * to sun.security.jgss.krb5.Krb5InitCredential which extends
0N/A * KerberosTicket and sun.security.jgss.krb5.Kbr5AcceptCredential which
0N/A * extends KerberosKey.)
0N/A */
0N/A private static void populateCredentials(Set<Object> credentials,
0N/A Set<?> gssCredentials) {
0N/A
0N/A Object cred;
0N/A
0N/A Iterator<?> elements = gssCredentials.iterator();
0N/A while (elements.hasNext()) {
0N/A
0N/A cred = elements.next();
0N/A
0N/A // Retrieve the internal cred out of SpNegoCredElement
0N/A if (cred instanceof SpNegoCredElement) {
0N/A cred = ((SpNegoCredElement) cred).getInternalCred();
0N/A }
0N/A
0N/A if (cred instanceof KerberosTicket) {
0N/A if (!cred.getClass().getName().equals
0N/A ("javax.security.auth.kerberos.KerberosTicket")) {
0N/A KerberosTicket tempTkt = (KerberosTicket) cred;
0N/A cred = new KerberosTicket(tempTkt.getEncoded(),
0N/A tempTkt.getClient(),
0N/A tempTkt.getServer(),
0N/A tempTkt.getSessionKey().getEncoded(),
0N/A tempTkt.getSessionKeyType(),
0N/A tempTkt.getFlags(),
0N/A tempTkt.getAuthTime(),
0N/A tempTkt.getStartTime(),
0N/A tempTkt.getEndTime(),
0N/A tempTkt.getRenewTill(),
0N/A tempTkt.getClientAddresses());
0N/A }
0N/A credentials.add(cred);
0N/A } else if (cred instanceof KerberosKey) {
0N/A if (!cred.getClass().getName().equals
0N/A ("javax.security.auth.kerberos.KerberosKey")) {
0N/A KerberosKey tempKey = (KerberosKey) cred;
0N/A cred = new KerberosKey(tempKey.getPrincipal(),
0N/A tempKey.getEncoded(),
0N/A tempKey.getKeyType(),
0N/A tempKey.getVersionNumber());
0N/A }
0N/A credentials.add(cred);
0N/A } else {
0N/A // Ignore non-KerberosTicket and non-KerberosKey elements
0N/A debug("Skipped cred element: " + cred);
0N/A }
0N/A }
0N/A }
0N/A
0N/A /**
0N/A * Authenticate using the login module from the specified
0N/A * configuration entry.
0N/A *
0N/A * @param caller the caller of JAAS Login
0N/A * @param mech the mech to be used
0N/A * @return the authenticated subject
0N/A */
1266N/A public static Subject login(GSSCaller caller, Oid mech) throws LoginException {
0N/A
0N/A CallbackHandler cb = null;
1266N/A if (caller instanceof HttpCaller) {
1791N/A cb = new sun.net.www.protocol.http.spnego.NegotiateCallbackHandler(
1266N/A ((HttpCaller)caller).info());
0N/A } else {
0N/A String defaultHandler =
0N/A java.security.Security.getProperty(DEFAULT_HANDLER);
0N/A // get the default callback handler
0N/A if ((defaultHandler != null) && (defaultHandler.length() != 0)) {
0N/A cb = null;
0N/A } else {
0N/A cb = new TextCallbackHandler();
0N/A }
0N/A }
0N/A
0N/A // New instance of LoginConfigImpl must be created for each login,
0N/A // since the entry name is not passed as the first argument, but
0N/A // generated with caller and mech inside LoginConfigImpl
0N/A LoginContext lc = new LoginContext("", null, cb,
0N/A new LoginConfigImpl(caller, mech));
0N/A lc.login();
0N/A return lc.getSubject();
0N/A }
0N/A
0N/A /**
0N/A * Determines if the application doesn't mind if the mechanism obtains
0N/A * the required credentials from outside of the current Subject. Our
0N/A * Kerberos v5 mechanism would do a JAAS login on behalf of the
0N/A * application if this were the case.
0N/A *
0N/A * The application indicates this by explicitly setting the system
0N/A * property javax.security.auth.useSubjectCredsOnly to false.
0N/A */
1266N/A public static boolean useSubjectCredsOnly(GSSCaller caller) {
0N/A
0N/A // HTTP/SPNEGO doesn't use the standard JAAS framework. Instead, it
0N/A // uses the java.net.Authenticator style, therefore always return
0N/A // false here.
1266N/A if (caller instanceof HttpCaller) {
0N/A return false;
0N/A }
0N/A /*
0N/A * Don't use GetBooleanAction because the default value in the JRE
0N/A * (when this is unset) has to treated as true.
0N/A */
0N/A String propValue = AccessController.doPrivileged(
0N/A new GetPropertyAction("javax.security.auth.useSubjectCredsOnly",
0N/A "true"));
0N/A /*
0N/A * This property has to be explicitly set to "false". Invalid
0N/A * values should be ignored and the default "true" assumed.
0N/A */
0N/A return (!propValue.equalsIgnoreCase("false"));
0N/A }
0N/A
0N/A /**
0N/A * Determines the SPNEGO interoperability mode with Microsoft;
0N/A * by default it is set to true.
0N/A *
0N/A * To disable it, the application indicates this by explicitly setting
0N/A * the system property sun.security.spnego.interop to false.
0N/A */
0N/A public static boolean useMSInterop() {
0N/A /*
0N/A * Don't use GetBooleanAction because the default value in the JRE
0N/A * (when this is unset) has to treated as true.
0N/A */
0N/A String propValue = AccessController.doPrivileged(
0N/A new GetPropertyAction("sun.security.spnego.msinterop",
0N/A "true"));
0N/A /*
0N/A * This property has to be explicitly set to "false". Invalid
0N/A * values should be ignored and the default "true" assumed.
0N/A */
0N/A return (!propValue.equalsIgnoreCase("false"));
0N/A }
0N/A
0N/A /**
0N/A * Searches the private credentials of current Subject with the
0N/A * specified criteria and returns the matching GSSCredentialSpi
0N/A * object out of Sun's impl of GSSCredential. Returns null if
0N/A * no Subject present or a Vector which contains 0 or more
0N/A * matching GSSCredentialSpi objects.
0N/A */
0N/A public static Vector searchSubject(final GSSNameSpi name,
0N/A final Oid mech,
0N/A final boolean initiate,
0N/A final Class credCls) {
0N/A debug("Search Subject for " + getMechStr(mech) +
0N/A (initiate? " INIT" : " ACCEPT") + " cred (" +
0N/A (name == null? "<<DEF>>" : name.toString()) + ", " +
0N/A credCls.getName() + ")");
0N/A final AccessControlContext acc = AccessController.getContext();
0N/A try {
0N/A Vector creds =
0N/A AccessController.doPrivileged
0N/A (new PrivilegedExceptionAction<Vector>() {
0N/A public Vector run() throws Exception {
0N/A Subject accSubj = Subject.getSubject(acc);
0N/A Vector<GSSCredentialSpi> result = null;
0N/A if (accSubj != null) {
0N/A result = new Vector<GSSCredentialSpi>();
0N/A Iterator<GSSCredentialImpl> iterator =
0N/A accSubj.getPrivateCredentials
0N/A (GSSCredentialImpl.class).iterator();
0N/A while (iterator.hasNext()) {
0N/A GSSCredentialImpl cred = iterator.next();
0N/A debug("...Found cred" + cred);
0N/A try {
0N/A GSSCredentialSpi ce =
0N/A cred.getElement(mech, initiate);
0N/A debug("......Found element: " + ce);
0N/A if (ce.getClass().equals(credCls) &&
0N/A (name == null ||
0N/A name.equals((Object) ce.getName()))) {
0N/A result.add(ce);
0N/A } else {
0N/A debug("......Discard element");
0N/A }
0N/A } catch (GSSException ge) {
0N/A debug("...Discard cred (" + ge + ")");
0N/A }
0N/A }
0N/A } else debug("No Subject");
0N/A return result;
0N/A }
0N/A });
0N/A return creds;
0N/A } catch (PrivilegedActionException pae) {
0N/A debug("Unexpected exception when searching Subject:");
0N/A if (DEBUG) pae.printStackTrace();
0N/A return null;
0N/A }
0N/A }
0N/A}