1/* 2 * Copyright (c) 2000, 2009, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26package sun.security.jgss; 27 28import java.lang.reflect.InvocationTargetException; 29import org.ietf.jgss.*; 30import java.security.AccessController; 31import java.security.Provider; 32import java.security.Security; 33import java.util.ArrayList; 34import java.util.HashSet; 35import java.util.HashMap; 36import java.util.Enumeration; 37import java.util.Iterator; 38import sun.security.jgss.spi.*; 39import sun.security.jgss.wrapper.NativeGSSFactory; 40import sun.security.jgss.wrapper.SunNativeProvider; 41import sun.security.action.GetPropertyAction; 42 43/** 44 * This class stores the list of providers that this 45 * GSS-Implementation is configured to use. The GSSManagerImpl class 46 * queries this class whenever it needs a mechanism's factory.<p> 47 * 48 * This class stores an ordered list of pairs of the form 49 * {@code <provider, oid>}. When it attempts to instantiate a mechanism 50 * defined by oid o, it steps through the list looking for an entry 51 * with oid=o, or with oid=null. (An entry with oid=null matches all 52 * mechanisms.) When it finds such an entry, the corresponding 53 * provider is approached for the mechanism's factory class. 54 * At instantiation time this list in initialized to contain those 55 * system wide providers that contain a property of the form 56 * "GssApiMechanism.x.y.z..." where "x.y.z..." is a numeric object 57 * identifier with numbers x, y, z, etc. Such a property is defined 58 * to map to that provider's implementation of the MechanismFactory 59 * interface for the mechanism x.y.z... 60 * As and when a MechanismFactory is instantiated, it is 61 * cached for future use. <p> 62 * 63 * An application can cause more providers to be added by means of 64 * the addProviderAtFront and addProviderAtEnd methods on 65 * GSSManager which get delegated to this class. The 66 * addProviderAtFront method can also cause a change in the ordering 67 * of the providers without adding any new providers, by causing a 68 * provider to move up in a list. The method addProviderAtEnd can 69 * only add providers at the end of the list if they are not already 70 * in the list. The rationale is that an application will call 71 * addProviderAtFront when it wants a provider to be used in 72 * preference over the default ones. And it will call 73 * addProviderAtEnd when it wants a provider to be used in case 74 * the system ones don't suffice.<p> 75 * 76 * If a mechanism's factory is being obtained from a provider as a 77 * result of encountering a entryof the form {@code <provider, oid>} where 78 * oid is non-null, then the assumption is that the application added 79 * this entry and it wants this mechanism to be obtained from this 80 * provider. Thus is the provider does not actually contain the 81 * requested mechanism, an exception will be thrown. However, if the 82 * entry were of the form {@code <provider, null>}, then it is viewed more 83 * liberally and is simply skipped over if the provider does not claim to 84 * support the requested mechanism. 85 */ 86 87public final class ProviderList { 88 89 private static final String PROV_PROP_PREFIX = "GssApiMechanism."; 90 private static final int PROV_PROP_PREFIX_LEN = 91 PROV_PROP_PREFIX.length(); 92 93 private static final String SPI_MECH_FACTORY_TYPE 94 = "sun.security.jgss.spi.MechanismFactory"; 95 96 // Undocumented property? 97 private static final String DEFAULT_MECH_PROP = 98 "sun.security.jgss.mechanism"; 99 100 public static final Oid DEFAULT_MECH_OID; 101 102 static { 103 /* 104 * Set the default mechanism. Kerberos v5 is the default 105 * mechanism unless it is overridden by a system property. 106 * with a valid OID value 107 */ 108 Oid defOid = null; 109 String defaultOidStr = AccessController.doPrivileged 110 (new GetPropertyAction(DEFAULT_MECH_PROP)); 111 if (defaultOidStr != null) { 112 defOid = GSSUtil.createOid(defaultOidStr); 113 } 114 DEFAULT_MECH_OID = 115 (defOid == null ? GSSUtil.GSS_KRB5_MECH_OID : defOid); 116 } 117 118 private ArrayList<PreferencesEntry> preferences = 119 new ArrayList<PreferencesEntry>(5); 120 private HashMap<PreferencesEntry, MechanismFactory> factories = 121 new HashMap<PreferencesEntry, MechanismFactory>(5); 122 private HashSet<Oid> mechs = new HashSet<Oid>(5); 123 124 final private GSSCaller caller; 125 126 public ProviderList(GSSCaller caller, boolean useNative) { 127 this.caller = caller; 128 Provider[] provList; 129 if (useNative) { 130 provList = new Provider[1]; 131 provList[0] = new SunNativeProvider(); 132 } else { 133 provList = Security.getProviders(); 134 } 135 136 for (int i = 0; i < provList.length; i++) { 137 Provider prov = provList[i]; 138 try { 139 addProviderAtEnd(prov, null); 140 } catch (GSSException ge) { 141 // Move on to the next provider 142 GSSUtil.debug("Error in adding provider " + 143 prov.getName() + ": " + ge); 144 } 145 } // End of for loop 146 } 147 148 /** 149 * Determines if the given provider property represents a GSS-API 150 * Oid to MechanismFactory mapping. 151 * @return true if this is a GSS-API property, false otherwise. 152 */ 153 private boolean isMechFactoryProperty(String prop) { 154 return (prop.startsWith(PROV_PROP_PREFIX) || 155 prop.regionMatches(true, 0, // Try ignoring case 156 PROV_PROP_PREFIX, 0, 157 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) mechOid = ProviderList.DEFAULT_MECH_OID; 171 return getMechFactory(mechOid, null); 172 } 173 174 /** 175 * Obtains a MechanismFactory for a given mechanism. If the 176 * specified provider is not null, then the impl from the 177 * provider is used. Otherwise, the most preferred impl based 178 * on the configured preferences is used. 179 * @param mechOid the oid of the desired mechanism 180 * @return a MechanismFactory for the desired mechanism. 181 * @throws GSSException when the specified provider does not 182 * support the desired mechanism, or when no provider supports 183 * the desired mechanism. 184 */ 185 synchronized public MechanismFactory getMechFactory(Oid mechOid, 186 Provider p) 187 throws GSSException { 188 189 if (mechOid == null) mechOid = ProviderList.DEFAULT_MECH_OID; 190 191 if (p == null) { 192 // Iterate thru all preferences to find right provider 193 String className; 194 PreferencesEntry entry; 195 196 Iterator<PreferencesEntry> list = preferences.iterator(); 197 while (list.hasNext()) { 198 entry = list.next(); 199 if (entry.impliesMechanism(mechOid)) { 200 MechanismFactory retVal = getMechFactory(entry, mechOid); 201 if (retVal != null) return retVal; 202 } 203 } // end of while loop 204 throw new GSSExceptionImpl(GSSException.BAD_MECH, mechOid); 205 } else { 206 // Use the impl from the specified provider; return null if the 207 // the mech is unsupported by the specified provider. 208 PreferencesEntry entry = new PreferencesEntry(p, mechOid); 209 return getMechFactory(entry, mechOid); 210 } 211 } 212 213 /** 214 * Helper routine that uses a preferences entry to obtain an 215 * implementation of a MechanismFactory from it. 216 * @param e the preferences entry that contains the provider and 217 * either a null of an explicit oid that matched the oid of the 218 * desired mechanism. 219 * @param mechOid the oid of the desired mechanism 220 * @throws GSSException If the application explicitly requested 221 * this entry's provider to be used for the desired mechanism but 222 * some problem is encountered 223 */ 224 private MechanismFactory getMechFactory(PreferencesEntry e, Oid mechOid) 225 throws GSSException { 226 Provider p = e.getProvider(); 227 228 /* 229 * See if a MechanismFactory was previously instantiated for 230 * this provider and mechanism combination. 231 */ 232 PreferencesEntry searchEntry = new PreferencesEntry(p, mechOid); 233 MechanismFactory retVal = factories.get(searchEntry); 234 if (retVal == null) { 235 /* 236 * Apparently not. Now try to instantiate this class from 237 * the provider. 238 */ 239 String prop = PROV_PROP_PREFIX + mechOid.toString(); 240 String className = p.getProperty(prop); 241 if (className != null) { 242 retVal = getMechFactoryImpl(p, className, mechOid, caller); 243 factories.put(searchEntry, retVal); 244 } else { 245 /* 246 * This provider does not support this mechanism. 247 * If the application explicitly requested that 248 * this provider be used for this mechanism, then 249 * throw an exception 250 */ 251 if (e.getOid() != null) { 252 throw new GSSExceptionImpl(GSSException.BAD_MECH, 253 "Provider " + p.getName() + 254 " does not support mechanism " + mechOid); 255 } 256 } 257 } 258 return retVal; 259 } 260 261 /** 262 * Helper routine to obtain a MechanismFactory implementation 263 * from the same class loader as the provider of this 264 * implementation. 265 * @param p the provider whose classloader must be used for 266 * instantiating the desired MechanismFactory 267 * @ param className the name of the MechanismFactory class 268 * @throws GSSException If some error occurs when trying to 269 * instantiate this MechanismFactory. 270 */ 271 private static MechanismFactory getMechFactoryImpl(Provider p, 272 String className, 273 Oid mechOid, 274 GSSCaller caller) 275 throws GSSException { 276 277 try { 278 Class<?> baseClass = Class.forName(SPI_MECH_FACTORY_TYPE); 279 280 /* 281 * Load the implementation class with the same class loader 282 * that was used to load the provider. 283 * In order to get the class loader of a class, the 284 * caller's class loader must be the same as or an ancestor of 285 * the class loader being returned. Otherwise, the caller must 286 * have "getClassLoader" permission, or a SecurityException 287 * will be thrown. 288 */ 289 290 ClassLoader cl = p.getClass().getClassLoader(); 291 Class<?> implClass; 292 if (cl != null) { 293 implClass = cl.loadClass(className); 294 } else { 295 implClass = Class.forName(className); 296 } 297 298 if (baseClass.isAssignableFrom(implClass)) { 299 300 java.lang.reflect.Constructor<?> c = 301 implClass.getConstructor(GSSCaller.class); 302 MechanismFactory mf = (MechanismFactory) (c.newInstance(caller)); 303 304 if (mf instanceof NativeGSSFactory) { 305 ((NativeGSSFactory) mf).setMech(mechOid); 306 } 307 return mf; 308 } else { 309 throw createGSSException(p, className, "is not a " + 310 SPI_MECH_FACTORY_TYPE, null); 311 } 312 } catch (ClassNotFoundException e) { 313 throw createGSSException(p, className, "cannot be created", e); 314 } catch (NoSuchMethodException e) { 315 throw createGSSException(p, className, "cannot be created", e); 316 } catch (InvocationTargetException e) { 317 throw createGSSException(p, className, "cannot be created", e); 318 } catch (InstantiationException e) { 319 throw createGSSException(p, className, "cannot be created", e); 320 } catch (IllegalAccessException e) { 321 throw createGSSException(p, className, "cannot be created", e); 322 } catch (SecurityException e) { 323 throw createGSSException(p, className, "cannot be created", e); 324 } 325 } 326 327 // Only used by getMechFactoryImpl 328 private static GSSException createGSSException(Provider p, 329 String className, 330 String trailingMsg, 331 Exception cause) { 332 String errClassInfo = className + " configured by " + 333 p.getName() + " for GSS-API Mechanism Factory "; 334 return new GSSExceptionImpl(GSSException.BAD_MECH, 335 errClassInfo + trailingMsg, 336 cause); 337 } 338 339 public Oid[] getMechs() { 340 return mechs.toArray(new Oid[] {}); 341 } 342 343 synchronized public void addProviderAtFront(Provider p, Oid mechOid) 344 throws GSSException { 345 346 PreferencesEntry newEntry = new PreferencesEntry(p, mechOid); 347 PreferencesEntry oldEntry; 348 boolean foundSomeMech; 349 350 Iterator<PreferencesEntry> list = preferences.iterator(); 351 while (list.hasNext()) { 352 oldEntry = list.next(); 353 if (newEntry.implies(oldEntry)) 354 list.remove(); 355 } 356 357 if (mechOid == null) { 358 foundSomeMech = addAllMechsFromProvider(p); 359 } else { 360 String oidStr = mechOid.toString(); 361 if (p.getProperty(PROV_PROP_PREFIX + oidStr) == null) 362 throw new GSSExceptionImpl(GSSException.BAD_MECH, 363 "Provider " + p.getName() 364 + " does not support " 365 + oidStr); 366 mechs.add(mechOid); 367 foundSomeMech = true; 368 } 369 370 if (foundSomeMech) { 371 preferences.add(0, newEntry); 372 } 373 } 374 375 synchronized public void addProviderAtEnd(Provider p, Oid mechOid) 376 throws GSSException { 377 378 PreferencesEntry newEntry = new PreferencesEntry(p, mechOid); 379 PreferencesEntry oldEntry; 380 boolean foundSomeMech; 381 382 Iterator<PreferencesEntry> list = preferences.iterator(); 383 while (list.hasNext()) { 384 oldEntry = list.next(); 385 if (oldEntry.implies(newEntry)) 386 return; 387 } 388 389 // System.out.println("addProviderAtEnd: No it is not redundant"); 390 391 if (mechOid == null) 392 foundSomeMech = addAllMechsFromProvider(p); 393 else { 394 String oidStr = mechOid.toString(); 395 if (p.getProperty(PROV_PROP_PREFIX + oidStr) == null) 396 throw new GSSExceptionImpl(GSSException.BAD_MECH, 397 "Provider " + p.getName() 398 + " does not support " 399 + oidStr); 400 mechs.add(mechOid); 401 foundSomeMech = true; 402 } 403 404 if (foundSomeMech) { 405 preferences.add(newEntry); 406 } 407 } 408 409 /** 410 * Helper routine to go through all properties contined in a 411 * provider and add its mechanisms to the list of supported 412 * mechanisms. If no default mechanism has been assinged so far, 413 * it sets the default MechanismFactory and Oid as well. 414 * @param p the provider to query 415 * @return true if there is at least one mechanism that this 416 * provider contributed, false otherwise 417 */ 418 private boolean addAllMechsFromProvider(Provider p) { 419 420 String prop; 421 boolean retVal = false; 422 423 // Get all props for this provider 424 Enumeration<Object> props = p.keys(); 425 426 // See if there are any GSS prop's 427 while (props.hasMoreElements()) { 428 prop = (String) props.nextElement(); 429 if (isMechFactoryProperty(prop)) { 430 // Ok! This is a GSS provider! 431 try { 432 Oid mechOid = getOidFromMechFactoryProperty(prop); 433 mechs.add(mechOid); 434 retVal = true; 435 } catch (GSSException e) { 436 // Skip to next property 437 GSSUtil.debug("Ignore the invalid property " + 438 prop + " from provider " + p.getName()); 439 } 440 } // Processed GSS property 441 } // while loop 442 443 return retVal; 444 445 } 446 447 /** 448 * Stores a provider and a mechanism oid indicating that the 449 * provider should be used for the mechanism. If the mechanism 450 * Oid is null, then it indicates that this preference holds for 451 * any mechanism.<p> 452 * 453 * The ProviderList maintains an ordered list of 454 * PreferencesEntry's and iterates thru them as it tries to 455 * instantiate MechanismFactory's. 456 */ 457 private static final class PreferencesEntry { 458 private Provider p; 459 private Oid oid; 460 PreferencesEntry(Provider p, Oid oid) { 461 this.p = p; 462 this.oid = oid; 463 } 464 465 public boolean equals(Object other) { 466 if (this == other) { 467 return true; 468 } 469 470 if (!(other instanceof PreferencesEntry)) { 471 return false; 472 } 473 474 PreferencesEntry that = (PreferencesEntry)other; 475 if (this.p.getName().equals(that.p.getName())) { 476 if (this.oid != null && that.oid != null) { 477 return this.oid.equals(that.oid); 478 } else { 479 return (this.oid == null && that.oid == null); 480 } 481 } 482 483 return false; 484 } 485 486 public int hashCode() { 487 int result = 17; 488 489 result = 37 * result + p.getName().hashCode(); 490 if (oid != null) { 491 result = 37 * result + oid.hashCode(); 492 } 493 494 return result; 495 } 496 497 /** 498 * Determines if a preference implies another. A preference 499 * implies another if the latter is subsumed by the 500 * former. e.g., <Provider1, null> implies <Provider1, OidX> 501 * because the null in the former indicates that it should 502 * be used for all mechanisms. 503 */ 504 boolean implies(Object other) { 505 506 if (other instanceof PreferencesEntry) { 507 PreferencesEntry temp = (PreferencesEntry) other; 508 return (equals(temp) || 509 p.getName().equals(temp.p.getName()) && 510 oid == null); 511 } else { 512 return false; 513 } 514 } 515 516 Provider getProvider() { 517 return p; 518 } 519 520 Oid getOid() { 521 return oid; 522 } 523 524 /** 525 * Determines if this entry is applicable to the desired 526 * mechanism. The entry is applicable to the desired mech if 527 * it contains the same oid or if it contains a null oid 528 * indicating that it is applicable to all mechs. 529 * @param mechOid the desired mechanism 530 * @return true if the provider in this entry should be 531 * queried for this mechanism. 532 */ 533 boolean impliesMechanism(Oid oid) { 534 return (this.oid == null || this.oid.equals(oid)); 535 } 536 537 // For debugging 538 public String toString() { 539 StringBuilder sb = new StringBuilder("<"); 540 sb.append(p.getName()); 541 sb.append(", "); 542 sb.append(oid); 543 sb.append(">"); 544 return sb.toString(); 545 } 546 } 547} 548