001: /*
002: * Copyright 2000-2006 Sun Microsystems, Inc. All Rights Reserved.
003: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
004: *
005: * This code is free software; you can redistribute it and/or modify it
006: * under the terms of the GNU General Public License version 2 only, as
007: * published by the Free Software Foundation. Sun designates this
008: * particular file as subject to the "Classpath" exception as provided
009: * by Sun in the LICENSE file that accompanied this code.
010: *
011: * This code is distributed in the hope that it will be useful, but WITHOUT
012: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
013: * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
014: * version 2 for more details (a copy is included in the LICENSE file that
015: * accompanied this code).
016: *
017: * You should have received a copy of the GNU General Public License version
018: * 2 along with this work; if not, write to the Free Software Foundation,
019: * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
020: *
021: * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
022: * CA 95054 USA or visit www.sun.com if you need additional information or
023: * have any questions.
024: */
025:
026: package sun.security.jgss;
027:
028: import com.sun.security.auth.callback.TextCallbackHandler;
029: import javax.security.auth.Subject;
030: import javax.security.auth.kerberos.KerberosPrincipal;
031: import javax.security.auth.kerberos.KerberosTicket;
032: import javax.security.auth.kerberos.KerberosKey;
033: import org.ietf.jgss.*;
034: import sun.security.jgss.spi.GSSNameSpi;
035: import sun.security.jgss.spi.GSSCredentialSpi;
036: import sun.security.action.GetPropertyAction;
037: import sun.security.jgss.krb5.Krb5NameElement;
038: import sun.security.jgss.spnego.SpNegoCredElement;
039: import java.util.Set;
040: import java.util.HashSet;
041: import java.util.Vector;
042: import java.util.Iterator;
043: import java.security.AccessController;
044: import java.security.AccessControlContext;
045: import java.security.PrivilegedExceptionAction;
046: import java.security.PrivilegedActionException;
047: import javax.security.auth.callback.CallbackHandler;
048: import javax.security.auth.login.LoginContext;
049: import javax.security.auth.login.LoginException;
050: import sun.security.action.GetBooleanAction;
051:
052: /**
053: * The GSSUtilImplementation that knows how to work with the internals of
054: * the GSS-API.
055: */
056: public class GSSUtil {
057:
058: public static final Oid GSS_KRB5_MECH_OID = GSSUtil
059: .createOid("1.2.840.113554.1.2.2");
060: public static final Oid GSS_KRB5_MECH_OID2 = GSSUtil
061: .createOid("1.3.5.1.5.2");
062:
063: public static final Oid GSS_SPNEGO_MECH_OID = GSSUtil
064: .createOid("1.3.6.1.5.5.2");
065:
066: public static final Oid NT_GSS_KRB5_PRINCIPAL = GSSUtil
067: .createOid("1.2.840.113554.1.2.2.1");
068:
069: public static final Oid NT_HOSTBASED_SERVICE2 = GSSUtil
070: .createOid("1.2.840.113554.1.2.1.4");
071:
072: private static final String DEFAULT_HANDLER = "auth.login.defaultCallbackHandler";
073:
074: public static final int CALLER_UNKNOWN = -1;
075: public static final int CALLER_INITIATE = 1;
076: public static final int CALLER_ACCEPT = 2;
077: public static final int CALLER_SSL_CLIENT = 3;
078: public static final int CALLER_SSL_SERVER = 4;
079: public static final int CALLER_HTTP_NEGOTIATE = 5;
080:
081: static final boolean DEBUG;
082: static {
083: DEBUG = (AccessController.doPrivileged(new GetBooleanAction(
084: "sun.security.jgss.debug"))).booleanValue();
085: }
086:
087: static void debug(String message) {
088: if (DEBUG) {
089: assert (message != null);
090: System.out.println(message);
091: }
092: }
093:
094: // NOTE: this method is only for creating Oid objects with
095: // known to be valid <code>oidStr</code> given it ignores
096: // the GSSException
097: public static Oid createOid(String oidStr) {
098: try {
099: return new Oid(oidStr);
100: } catch (GSSException e) {
101: debug("Ignored invalid OID: " + oidStr);
102: return null;
103: }
104: }
105:
106: public static boolean isSpNegoMech(Oid oid) {
107: return (GSS_SPNEGO_MECH_OID.equals(oid));
108: }
109:
110: public static boolean isKerberosMech(Oid oid) {
111: return (GSS_KRB5_MECH_OID.equals(oid) || GSS_KRB5_MECH_OID2
112: .equals(oid));
113:
114: }
115:
116: public static String getMechStr(Oid oid) {
117: if (isSpNegoMech(oid)) {
118: return "SPNEGO";
119: } else if (isKerberosMech(oid)) {
120: return "Kerberos V5";
121: } else {
122: return oid.toString();
123: }
124: }
125:
126: /**
127: * Note: The current impl only works with Sun's impl of
128: * GSSName and GSSCredential since it depends on package
129: * private APIs.
130: */
131: public static Subject getSubject(GSSName name, GSSCredential creds) {
132:
133: HashSet<Object> privCredentials = null;
134: HashSet<Object> pubCredentials = new HashSet<Object>(); // empty Set
135:
136: Set<GSSCredentialSpi> gssCredentials = null;
137:
138: Set<KerberosPrincipal> krb5Principals = new HashSet<KerberosPrincipal>();
139:
140: if (name instanceof GSSNameImpl) {
141: try {
142: GSSNameSpi ne = ((GSSNameImpl) name)
143: .getElement(GSS_KRB5_MECH_OID);
144: String krbName = ne.toString();
145: if (ne instanceof Krb5NameElement) {
146: krbName = ((Krb5NameElement) ne)
147: .getKrb5PrincipalName().getName();
148: }
149: KerberosPrincipal krbPrinc = new KerberosPrincipal(
150: krbName);
151: krb5Principals.add(krbPrinc);
152: } catch (GSSException ge) {
153: debug("Skipped name " + name + " due to " + ge);
154: }
155: }
156:
157: if (creds instanceof GSSCredentialImpl) {
158: gssCredentials = ((GSSCredentialImpl) creds).getElements();
159: privCredentials = new HashSet<Object>(gssCredentials.size());
160: populateCredentials(privCredentials, gssCredentials);
161: } else {
162: privCredentials = new HashSet<Object>(); // empty Set
163: }
164: debug("Created Subject with the following");
165: debug("principals=" + krb5Principals);
166: debug("public creds=" + pubCredentials);
167: debug("private creds=" + privCredentials);
168:
169: return new Subject(false, krb5Principals, pubCredentials,
170: privCredentials);
171:
172: }
173:
174: /**
175: * Populates the set credentials with elements from gssCredentials. At
176: * the same time, it converts any subclasses of KerberosTicket
177: * into KerberosTicket instances and any subclasses of KerberosKey into
178: * KerberosKey instances. (It is not desirable to expose the customer
179: * to sun.security.jgss.krb5.Krb5InitCredential which extends
180: * KerberosTicket and sun.security.jgss.krb5.Kbr5AcceptCredential which
181: * extends KerberosKey.)
182: */
183: private static void populateCredentials(Set<Object> credentials,
184: Set<?> gssCredentials) {
185:
186: Object cred;
187:
188: Iterator<?> elements = gssCredentials.iterator();
189: while (elements.hasNext()) {
190:
191: cred = elements.next();
192:
193: // Retrieve the internal cred out of SpNegoCredElement
194: if (cred instanceof SpNegoCredElement) {
195: cred = ((SpNegoCredElement) cred).getInternalCred();
196: }
197:
198: if (cred instanceof KerberosTicket) {
199: if (!cred.getClass().getName().equals(
200: "javax.security.auth.kerberos.KerberosTicket")) {
201: KerberosTicket tempTkt = (KerberosTicket) cred;
202: cred = new KerberosTicket(tempTkt.getEncoded(),
203: tempTkt.getClient(), tempTkt.getServer(),
204: tempTkt.getSessionKey().getEncoded(),
205: tempTkt.getSessionKeyType(), tempTkt
206: .getFlags(), tempTkt.getAuthTime(),
207: tempTkt.getStartTime(), tempTkt
208: .getEndTime(), tempTkt
209: .getRenewTill(), tempTkt
210: .getClientAddresses());
211: }
212: credentials.add(cred);
213: } else if (cred instanceof KerberosKey) {
214: if (!cred.getClass().getName().equals(
215: "javax.security.auth.kerberos.KerberosKey")) {
216: KerberosKey tempKey = (KerberosKey) cred;
217: cred = new KerberosKey(tempKey.getPrincipal(),
218: tempKey.getEncoded(), tempKey.getKeyType(),
219: tempKey.getVersionNumber());
220: }
221: credentials.add(cred);
222: } else {
223: // Ignore non-KerberosTicket and non-KerberosKey elements
224: debug("Skipped cred element: " + cred);
225: }
226: }
227: }
228:
229: /**
230: * Authenticate using the login module from the specified
231: * configuration entry.
232: *
233: * @param caller the caller of JAAS Login
234: * @param mech the mech to be used
235: * @return the authenticated subject
236: */
237: public static Subject login(int caller, Oid mech)
238: throws LoginException {
239:
240: CallbackHandler cb = null;
241: if (caller == GSSUtil.CALLER_HTTP_NEGOTIATE) {
242: cb = new sun.net.www.protocol.http.NegotiateCallbackHandler();
243: } else {
244: String defaultHandler = java.security.Security
245: .getProperty(DEFAULT_HANDLER);
246: // get the default callback handler
247: if ((defaultHandler != null)
248: && (defaultHandler.length() != 0)) {
249: cb = null;
250: } else {
251: cb = new TextCallbackHandler();
252: }
253: }
254:
255: // New instance of LoginConfigImpl must be created for each login,
256: // since the entry name is not passed as the first argument, but
257: // generated with caller and mech inside LoginConfigImpl
258: LoginContext lc = new LoginContext("", null, cb,
259: new LoginConfigImpl(caller, mech));
260: lc.login();
261: return lc.getSubject();
262: }
263:
264: /**
265: * Determines if the application doesn't mind if the mechanism obtains
266: * the required credentials from outside of the current Subject. Our
267: * Kerberos v5 mechanism would do a JAAS login on behalf of the
268: * application if this were the case.
269: *
270: * The application indicates this by explicitly setting the system
271: * property javax.security.auth.useSubjectCredsOnly to false.
272: */
273: public static boolean useSubjectCredsOnly() {
274: /*
275: * Don't use GetBooleanAction because the default value in the JRE
276: * (when this is unset) has to treated as true.
277: */
278: String propValue = AccessController
279: .doPrivileged(new GetPropertyAction(
280: "javax.security.auth.useSubjectCredsOnly",
281: "true"));
282: /*
283: * This property has to be explicitly set to "false". Invalid
284: * values should be ignored and the default "true" assumed.
285: */
286: return (!propValue.equalsIgnoreCase("false"));
287: }
288:
289: /**
290: * Determines the SPNEGO interoperability mode with Microsoft;
291: * by default it is set to true.
292: *
293: * To disable it, the application indicates this by explicitly setting
294: * the system property sun.security.spnego.interop to false.
295: */
296: public static boolean useMSInterop() {
297: /*
298: * Don't use GetBooleanAction because the default value in the JRE
299: * (when this is unset) has to treated as true.
300: */
301: String propValue = AccessController
302: .doPrivileged(new GetPropertyAction(
303: "sun.security.spnego.msinterop", "true"));
304: /*
305: * This property has to be explicitly set to "false". Invalid
306: * values should be ignored and the default "true" assumed.
307: */
308: return (!propValue.equalsIgnoreCase("false"));
309: }
310:
311: /**
312: * Searches the private credentials of current Subject with the
313: * specified criteria and returns the matching GSSCredentialSpi
314: * object out of Sun's impl of GSSCredential. Returns null if
315: * no Subject present or a Vector which contains 0 or more
316: * matching GSSCredentialSpi objects.
317: */
318: public static Vector searchSubject(final GSSNameSpi name,
319: final Oid mech, final boolean initiate, final Class credCls) {
320: debug("Search Subject for " + getMechStr(mech)
321: + (initiate ? " INIT" : " ACCEPT") + " cred ("
322: + (name == null ? "<<DEF>>" : name.toString()) + ", "
323: + credCls.getName() + ")");
324: final AccessControlContext acc = AccessController.getContext();
325: try {
326: Vector creds = AccessController
327: .doPrivileged(new PrivilegedExceptionAction<Vector>() {
328: public Vector run() throws Exception {
329: Subject accSubj = Subject.getSubject(acc);
330: Vector<GSSCredentialSpi> result = null;
331: if (accSubj != null) {
332: result = new Vector<GSSCredentialSpi>();
333: Iterator<GSSCredentialImpl> iterator = accSubj
334: .getPrivateCredentials(
335: GSSCredentialImpl.class)
336: .iterator();
337: while (iterator.hasNext()) {
338: GSSCredentialImpl cred = iterator
339: .next();
340: debug("...Found cred" + cred);
341: try {
342: GSSCredentialSpi ce = cred
343: .getElement(mech,
344: initiate);
345: debug("......Found element: "
346: + ce);
347: if (ce.getClass().equals(
348: credCls)
349: && (name == null || name
350: .equals((Object) ce
351: .getName()))) {
352: result.add(ce);
353: } else {
354: debug("......Discard element");
355: }
356: } catch (GSSException ge) {
357: debug("...Discard cred (" + ge
358: + ")");
359: }
360: }
361: } else
362: debug("No Subject");
363: return result;
364: }
365: });
366: return creds;
367: } catch (PrivilegedActionException pae) {
368: debug("Unexpected exception when searching Subject:");
369: if (DEBUG)
370: pae.printStackTrace();
371: return null;
372: }
373: }
374: }
|