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 java.lang.reflect.InvocationTargetException;
029: import org.ietf.jgss.*;
030: import java.security.AccessController;
031: import java.security.AccessControlContext;
032: import java.security.PrivilegedAction;
033: import java.security.Provider;
034: import java.security.Security;
035: import java.util.ArrayList;
036: import java.util.HashSet;
037: import java.util.HashMap;
038: import java.util.Enumeration;
039: import java.util.Iterator;
040: import javax.security.auth.Subject;
041: import sun.security.jgss.spi.*;
042: import sun.security.jgss.wrapper.NativeGSSFactory;
043: import sun.security.jgss.wrapper.SunNativeProvider;
044: import sun.security.action.GetPropertyAction;
045:
046: /**
047: * This class stores the list of providers that this
048: * GSS-Implementation is configured to use. The GSSManagerImpl class
049: * queries this class whenever it needs a mechanism's factory.<p>
050: *
051: * This class stores an ordered list of pairs of the form
052: * <provider, oid>. When it attempts to instantiate a mechanism
053: * defined by oid o, it steps through the list looking for an entry
054: * with oid=o, or with oid=null. (An entry with oid=null matches all
055: * mechanisms.) When it finds such an entry, the corresponding
056: * provider is approached for the mechanism's factory class.
057: * At instantiation time this list in initialized to contain those
058: * system wide providers that contain a property of the form
059: * "GssApiMechanism.x.y.z..." where "x.y.z..." is a numeric object
060: * identifier with numbers x, y, z, etc. Such a property is defined
061: * to map to that provider's implementation of the MechanismFactory
062: * interface for the mechanism x.y.z...
063: * As and when a MechanismFactory is instantiated, it is
064: * cached for future use. <p>
065: *
066: * An application can cause more providers to be added by means of
067: * the addProviderAtFront and addProviderAtEnd methods on
068: * GSSManager which get delegated to this class. The
069: * addProviderAtFront method can also cause a change in the ordering
070: * of the providers without adding any new providers, by causing a
071: * provider to move up in a list. The method addProviderAtEnd can
072: * only add providers at the end of the list if they are not already
073: * in the list. The rationale is that an application will call
074: * addProviderAtFront when it wants a provider to be used in
075: * preference over the default ones. And it will call
076: * addProviderAtEnd when it wants a provider to be used in case
077: * the system ones don't suffice.<p>
078: *
079: * If a mechanism's factory is being obtained from a provider as a
080: * result of encountering a entryof the form <provider, oid> where
081: * oid is non-null, then the assumption is that the application added
082: * this entry and it wants this mechanism to be obtained from this
083: * provider. Thus is the provider does not actually contain the
084: * requested mechanism, an exception will be thrown. However, if the
085: * entry were of the form <provider, null>, then it is viewed more
086: * liberally and is simply skipped over if the provider does not claim to
087: * support the requested mechanism.
088: */
089:
090: public final class ProviderList {
091:
092: private static final String PROV_PROP_PREFIX = "GssApiMechanism.";
093: private static final int PROV_PROP_PREFIX_LEN = PROV_PROP_PREFIX
094: .length();
095:
096: private static final String SPI_MECH_FACTORY_TYPE = "sun.security.jgss.spi.MechanismFactory";
097:
098: // Undocumented property?
099: private static final String DEFAULT_MECH_PROP = "sun.security.jgss.mechanism";
100:
101: public static final Oid DEFAULT_MECH_OID;
102:
103: static {
104: /*
105: * Set the default mechanism. Kerberos v5 is the default
106: * mechanism unless it is overridden by a system property.
107: * with a valid OID value
108: */
109: Oid defOid = null;
110: String defaultOidStr = AccessController
111: .doPrivileged(new GetPropertyAction(DEFAULT_MECH_PROP));
112: if (defaultOidStr != null) {
113: defOid = GSSUtil.createOid(defaultOidStr);
114: }
115: DEFAULT_MECH_OID = (defOid == null ? GSSUtil.GSS_KRB5_MECH_OID
116: : defOid);
117: }
118:
119: private ArrayList<PreferencesEntry> preferences = new ArrayList<PreferencesEntry>(
120: 5);
121: private HashMap<PreferencesEntry, MechanismFactory> factories = new HashMap<PreferencesEntry, MechanismFactory>(
122: 5);
123: private HashSet<Oid> mechs = new HashSet<Oid>(5);
124:
125: final private int caller;
126:
127: public ProviderList(int caller, boolean useNative) {
128: this .caller = caller;
129: Provider[] provList;
130: if (useNative) {
131: provList = new Provider[1];
132: provList[0] = new SunNativeProvider();
133: } else {
134: provList = Security.getProviders();
135: }
136:
137: for (int i = 0; i < provList.length; i++) {
138: Provider prov = provList[i];
139: try {
140: addProviderAtEnd(prov, null);
141: } catch (GSSException ge) {
142: // Move on to the next provider
143: GSSUtil.debug("Error in adding provider "
144: + prov.getName() + ": " + ge);
145: }
146: } // End of for loop
147: }
148:
149: /**
150: * Determines if the given provider property represents a GSS-API
151: * Oid to MechanismFactory mapping.
152: * @return true if this is a GSS-API property, false otherwise.
153: */
154: private boolean isMechFactoryProperty(String prop) {
155: return (prop.startsWith(PROV_PROP_PREFIX) || prop
156: .regionMatches(true, 0, // Try ignoring case
157: PROV_PROP_PREFIX, 0, PROV_PROP_PREFIX_LEN));
158: }
159:
160: private Oid getOidFromMechFactoryProperty(String prop)
161: throws GSSException {
162:
163: String oidPart = prop.substring(PROV_PROP_PREFIX_LEN);
164: return new Oid(oidPart);
165: }
166:
167: // So the existing code do not have to be changed
168: synchronized public MechanismFactory getMechFactory(Oid mechOid)
169: throws GSSException {
170: if (mechOid == null)
171: mechOid = ProviderList.DEFAULT_MECH_OID;
172: return getMechFactory(mechOid, null);
173: }
174:
175: /**
176: * Obtains a MechanismFactory for a given mechanism. If the
177: * specified provider is not null, then the impl from the
178: * provider is used. Otherwise, the most preferred impl based
179: * on the configured preferences is used.
180: * @param mechOid the oid of the desired mechanism
181: * @return a MechanismFactory for the desired mechanism.
182: * @throws GSSException when the specified provider does not
183: * support the desired mechanism, or when no provider supports
184: * the desired mechanism.
185: */
186: synchronized public MechanismFactory getMechFactory(Oid mechOid,
187: Provider p) throws GSSException {
188:
189: if (mechOid == null)
190: mechOid = ProviderList.DEFAULT_MECH_OID;
191:
192: if (p == null) {
193: // Iterate thru all preferences to find right provider
194: String className;
195: PreferencesEntry entry;
196:
197: Iterator<PreferencesEntry> list = preferences.iterator();
198: while (list.hasNext()) {
199: entry = list.next();
200: if (entry.impliesMechanism(mechOid)) {
201: MechanismFactory retVal = getMechFactory(entry,
202: mechOid);
203: if (retVal != null)
204: return retVal;
205: }
206: } // end of while loop
207: throw new GSSExceptionImpl(GSSException.BAD_MECH, mechOid);
208: } else {
209: // Use the impl from the specified provider; return null if the
210: // the mech is unsupported by the specified provider.
211: PreferencesEntry entry = new PreferencesEntry(p, mechOid);
212: return getMechFactory(entry, mechOid);
213: }
214: }
215:
216: /**
217: * Helper routine that uses a preferences entry to obtain an
218: * implementation of a MechanismFactory from it.
219: * @param e the preferences entry that contains the provider and
220: * either a null of an explicit oid that matched the oid of the
221: * desired mechanism.
222: * @param mechOid the oid of the desired mechanism
223: * @throws GSSException If the application explicitly requested
224: * this entry's provider to be used for the desired mechanism but
225: * some problem is encountered
226: */
227: private MechanismFactory getMechFactory(PreferencesEntry e,
228: Oid mechOid) throws GSSException {
229: Provider p = e.getProvider();
230:
231: /*
232: * See if a MechanismFactory was previously instantiated for
233: * this provider and mechanism combination.
234: */
235: PreferencesEntry searchEntry = new PreferencesEntry(p, mechOid);
236: MechanismFactory retVal = factories.get(searchEntry);
237: if (retVal == null) {
238: /*
239: * Apparently not. Now try to instantiate this class from
240: * the provider.
241: */
242: String prop = PROV_PROP_PREFIX + mechOid.toString();
243: String className = p.getProperty(prop);
244: if (className != null) {
245: retVal = getMechFactoryImpl(p, className, mechOid,
246: caller);
247: factories.put(searchEntry, retVal);
248: } else {
249: /*
250: * This provider does not support this mechanism.
251: * If the application explicitly requested that
252: * this provider be used for this mechanism, then
253: * throw an exception
254: */
255: if (e.getOid() != null) {
256: throw new GSSExceptionImpl(GSSException.BAD_MECH,
257: "Provider " + p.getName()
258: + " does not support mechanism "
259: + mechOid);
260: }
261: }
262: }
263: return retVal;
264: }
265:
266: /**
267: * Helper routine to obtain a MechanismFactory implementation
268: * from the same class loader as the provider of this
269: * implementation.
270: * @param p the provider whose classloader must be used for
271: * instantiating the desired MechanismFactory
272: * @ param className the name of the MechanismFactory class
273: * @throws GSSException If some error occurs when trying to
274: * instantiate this MechanismFactory.
275: */
276: private static MechanismFactory getMechFactoryImpl(Provider p,
277: String className, Oid mechOid, int caller)
278: throws GSSException {
279:
280: try {
281: Class<?> baseClass = Class.forName(SPI_MECH_FACTORY_TYPE);
282:
283: /*
284: * Load the implementation class with the same class loader
285: * that was used to load the provider.
286: * In order to get the class loader of a class, the
287: * caller's class loader must be the same as or an ancestor of
288: * the class loader being returned. Otherwise, the caller must
289: * have "getClassLoader" permission, or a SecurityException
290: * will be thrown.
291: */
292:
293: ClassLoader cl = p.getClass().getClassLoader();
294: Class<?> implClass;
295: if (cl != null) {
296: implClass = cl.loadClass(className);
297: } else {
298: implClass = Class.forName(className);
299: }
300:
301: if (baseClass.isAssignableFrom(implClass)) {
302:
303: java.lang.reflect.Constructor<?> c = implClass
304: .getConstructor(Integer.TYPE);
305: MechanismFactory mf = (MechanismFactory) (c
306: .newInstance(caller));
307:
308: if (mf instanceof NativeGSSFactory) {
309: ((NativeGSSFactory) mf).setMech(mechOid);
310: }
311: return mf;
312: } else {
313: throw createGSSException(p, className, "is not a "
314: + SPI_MECH_FACTORY_TYPE, null);
315: }
316: } catch (ClassNotFoundException e) {
317: throw createGSSException(p, className, "cannot be created",
318: e);
319: } catch (NoSuchMethodException e) {
320: throw createGSSException(p, className, "cannot be created",
321: e);
322: } catch (InvocationTargetException e) {
323: throw createGSSException(p, className, "cannot be created",
324: e);
325: } catch (InstantiationException e) {
326: throw createGSSException(p, className, "cannot be created",
327: e);
328: } catch (IllegalAccessException e) {
329: throw createGSSException(p, className, "cannot be created",
330: e);
331: } catch (SecurityException e) {
332: throw createGSSException(p, className, "cannot be created",
333: e);
334: }
335: }
336:
337: // Only used by getMechFactoryImpl
338: private static GSSException createGSSException(Provider p,
339: String className, String trailingMsg, Exception cause) {
340: String errClassInfo = className + " configured by "
341: + p.getName() + " for GSS-API Mechanism Factory ";
342: return new GSSExceptionImpl(GSSException.BAD_MECH, errClassInfo
343: + trailingMsg, cause);
344: }
345:
346: public Oid[] getMechs() {
347: return mechs.toArray(new Oid[] {});
348: }
349:
350: synchronized public void addProviderAtFront(Provider p, Oid mechOid)
351: throws GSSException {
352:
353: PreferencesEntry newEntry = new PreferencesEntry(p, mechOid);
354: PreferencesEntry oldEntry;
355: boolean foundSomeMech;
356:
357: Iterator<PreferencesEntry> list = preferences.iterator();
358: while (list.hasNext()) {
359: oldEntry = list.next();
360: if (newEntry.implies(oldEntry))
361: list.remove();
362: }
363:
364: if (mechOid == null) {
365: foundSomeMech = addAllMechsFromProvider(p);
366: } else {
367: String oidStr = mechOid.toString();
368: if (p.getProperty(PROV_PROP_PREFIX + oidStr) == null)
369: throw new GSSExceptionImpl(GSSException.BAD_MECH,
370: "Provider " + p.getName()
371: + " does not support " + oidStr);
372: mechs.add(mechOid);
373: foundSomeMech = true;
374: }
375:
376: if (foundSomeMech) {
377: preferences.add(0, newEntry);
378: }
379: }
380:
381: synchronized public void addProviderAtEnd(Provider p, Oid mechOid)
382: throws GSSException {
383:
384: PreferencesEntry newEntry = new PreferencesEntry(p, mechOid);
385: PreferencesEntry oldEntry;
386: boolean foundSomeMech;
387:
388: Iterator<PreferencesEntry> list = preferences.iterator();
389: while (list.hasNext()) {
390: oldEntry = list.next();
391: if (oldEntry.implies(newEntry))
392: return;
393: }
394:
395: // System.out.println("addProviderAtEnd: No it is not redundant");
396:
397: if (mechOid == null)
398: foundSomeMech = addAllMechsFromProvider(p);
399: else {
400: String oidStr = mechOid.toString();
401: if (p.getProperty(PROV_PROP_PREFIX + oidStr) == null)
402: throw new GSSExceptionImpl(GSSException.BAD_MECH,
403: "Provider " + p.getName()
404: + " does not support " + oidStr);
405: mechs.add(mechOid);
406: foundSomeMech = true;
407: }
408:
409: if (foundSomeMech) {
410: preferences.add(newEntry);
411: }
412: }
413:
414: /**
415: * Helper routine to go through all properties contined in a
416: * provider and add its mechanisms to the list of supported
417: * mechanisms. If no default mechanism has been assinged so far,
418: * it sets the default MechanismFactory and Oid as well.
419: * @param p the provider to query
420: * @return true if there is at least one mechanism that this
421: * provider contributed, false otherwise
422: */
423: private boolean addAllMechsFromProvider(Provider p) {
424:
425: String prop;
426: boolean retVal = false;
427:
428: // Get all props for this provider
429: Enumeration<Object> props = p.keys();
430:
431: // See if there are any GSS prop's
432: while (props.hasMoreElements()) {
433: prop = (String) props.nextElement();
434: if (isMechFactoryProperty(prop)) {
435: // Ok! This is a GSS provider!
436: try {
437: Oid mechOid = getOidFromMechFactoryProperty(prop);
438: mechs.add(mechOid);
439: retVal = true;
440: } catch (GSSException e) {
441: // Skip to next property
442: GSSUtil.debug("Ignore the invalid property " + prop
443: + " from provider " + p.getName());
444: }
445: } // Processed GSS property
446: } // while loop
447:
448: return retVal;
449:
450: }
451:
452: /**
453: * Stores a provider and a mechanism oid indicating that the
454: * provider should be used for the mechanism. If the mechanism
455: * Oid is null, then it indicates that this preference holds for
456: * any mechanism.<p>
457: *
458: * The ProviderList maintains an ordered list of
459: * PreferencesEntry's and iterates thru them as it tries to
460: * instantiate MechanismFactory's.
461: */
462: private static final class PreferencesEntry {
463: private Provider p;
464: private Oid oid;
465:
466: PreferencesEntry(Provider p, Oid oid) {
467: this .p = p;
468: this .oid = oid;
469: }
470:
471: public boolean equals(Object other) {
472: if (this == other) {
473: return true;
474: }
475:
476: if (!(other instanceof PreferencesEntry)) {
477: return false;
478: }
479:
480: PreferencesEntry that = (PreferencesEntry) other;
481: if (this .p.getName().equals(that.p.getName())) {
482: if (this .oid != null && that.oid != null) {
483: return this .oid.equals(that.oid);
484: } else {
485: return (this .oid == null && that.oid == null);
486: }
487: }
488:
489: return false;
490: }
491:
492: public int hashCode() {
493: int result = 17;
494:
495: result = 37 * result + p.getName().hashCode();
496: if (oid != null) {
497: result = 37 * result + oid.hashCode();
498: }
499:
500: return result;
501: }
502:
503: /**
504: * Determines if a preference implies another. A preference
505: * implies another if the latter is subsumed by the
506: * former. e.g., <Provider1, null> implies <Provider1, OidX>
507: * because the null in the former indicates that it should
508: * be used for all mechanisms.
509: */
510: boolean implies(Object other) {
511:
512: if (other instanceof PreferencesEntry) {
513: PreferencesEntry temp = (PreferencesEntry) other;
514: return (equals(temp) || p.getName().equals(
515: temp.p.getName())
516: && oid == null);
517: } else {
518: return false;
519: }
520: }
521:
522: Provider getProvider() {
523: return p;
524: }
525:
526: Oid getOid() {
527: return oid;
528: }
529:
530: /**
531: * Determines if this entry is applicable to the desired
532: * mechanism. The entry is applicable to the desired mech if
533: * it contains the same oid or if it contains a null oid
534: * indicating that it is applicable to all mechs.
535: * @param mechOid the desired mechanism
536: * @return true if the provider in this entry should be
537: * queried for this mechanism.
538: */
539: boolean impliesMechanism(Oid oid) {
540: return (this .oid == null || this .oid.equals(oid));
541: }
542:
543: // For debugging
544: public String toString() {
545: StringBuffer buf = new StringBuffer("<");
546: buf.append(p.getName());
547: buf.append(", ");
548: buf.append(oid);
549: buf.append(">");
550: return buf.toString();
551: }
552: }
553: }
|