ProviderConfig.java revision 12745:f068a4ffddd2
1/* 2 * Copyright (c) 2003, 2015, 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.jca; 27 28import java.io.File; 29import java.lang.reflect.*; 30import java.util.*; 31 32import java.security.*; 33 34import sun.security.util.PropertyExpander; 35 36/** 37 * Class representing a configured provider which encapsulates configuration 38 * (provider name + optional argument), the provider loading logic, and 39 * the loaded Provider object itself. 40 * 41 * @author Andreas Sterbenz 42 * @since 1.5 43 */ 44final class ProviderConfig { 45 46 private static final sun.security.util.Debug debug = 47 sun.security.util.Debug.getInstance("jca", "ProviderConfig"); 48 49 // suffix for identifying the SunPKCS11-Solaris provider 50 private static final String P11_SOL_NAME = "SunPKCS11"; 51 52 // config file argument of the SunPKCS11-Solaris provider 53 private static final String P11_SOL_ARG = 54 "${java.home}/conf/security/sunpkcs11-solaris.cfg"; 55 56 // maximum number of times to try loading a provider before giving up 57 private static final int MAX_LOAD_TRIES = 30; 58 59 // could be provider name (module) or provider class name (legacy) 60 private final String provName; 61 62 // argument to the Provider.configure() call, never null 63 private final String argument; 64 65 // number of times we have already tried to load this provider 66 private int tries; 67 68 // Provider object, if loaded 69 private volatile Provider provider; 70 71 // flag indicating if we are currently trying to load the provider 72 // used to detect recursion 73 private boolean isLoading; 74 75 ProviderConfig(String provName, String argument) { 76 if (provName.endsWith(P11_SOL_NAME) && argument.equals(P11_SOL_ARG)) { 77 checkSunPKCS11Solaris(); 78 } 79 this.provName = provName; 80 this.argument = expand(argument); 81 } 82 83 ProviderConfig(String provName) { 84 this(provName, ""); 85 } 86 87 ProviderConfig(Provider provider) { 88 this.provName = provider.getName(); 89 this.argument = ""; 90 this.provider = provider; 91 } 92 93 // check if we should try to load the SunPKCS11-Solaris provider 94 // avoid if not available (pre Solaris 10) to reduce startup time 95 // or if disabled via system property 96 private void checkSunPKCS11Solaris() { 97 Boolean o = AccessController.doPrivileged( 98 new PrivilegedAction<Boolean>() { 99 public Boolean run() { 100 File file = new File("/usr/lib/libpkcs11.so"); 101 if (file.exists() == false) { 102 return Boolean.FALSE; 103 } 104 if ("false".equalsIgnoreCase(System.getProperty 105 ("sun.security.pkcs11.enable-solaris"))) { 106 return Boolean.FALSE; 107 } 108 return Boolean.TRUE; 109 } 110 }); 111 if (o == Boolean.FALSE) { 112 tries = MAX_LOAD_TRIES; 113 } 114 } 115 116 private boolean hasArgument() { 117 return argument.length() != 0; 118 } 119 120 // should we try to load this provider? 121 private boolean shouldLoad() { 122 return (tries < MAX_LOAD_TRIES); 123 } 124 125 // do not try to load this provider again 126 private void disableLoad() { 127 tries = MAX_LOAD_TRIES; 128 } 129 130 boolean isLoaded() { 131 return (provider != null); 132 } 133 134 public boolean equals(Object obj) { 135 if (this == obj) { 136 return true; 137 } 138 if (obj instanceof ProviderConfig == false) { 139 return false; 140 } 141 ProviderConfig other = (ProviderConfig)obj; 142 return this.provName.equals(other.provName) 143 && this.argument.equals(other.argument); 144 145 } 146 147 public int hashCode() { 148 return provName.hashCode() + argument.hashCode(); 149 } 150 151 public String toString() { 152 if (hasArgument()) { 153 return provName + "('" + argument + "')"; 154 } else { 155 return provName; 156 } 157 } 158 159 /** 160 * Get the provider object. Loads the provider if it is not already loaded. 161 */ 162 synchronized Provider getProvider() { 163 // volatile variable load 164 Provider p = provider; 165 if (p != null) { 166 return p; 167 } 168 if (shouldLoad() == false) { 169 return null; 170 } 171 172 // Create providers which are in java.base directly 173 if (provName.equals("SUN") || provName.equals("sun.security.provider.Sun")) { 174 p = new sun.security.provider.Sun(); 175 } else if (provName.equals("SunRsaSign") || provName.equals("sun.security.rsa.SunRsaSign")) { 176 p = new sun.security.rsa.SunRsaSign(); 177 } else if (provName.equals("SunJCE") || provName.equals("com.sun.crypto.provider.SunJCE")) { 178 p = new com.sun.crypto.provider.SunJCE(); 179 } else if (provName.equals("SunJSSE") || provName.equals("com.sun.net.ssl.internal.ssl.Provider")) { 180 p = new com.sun.net.ssl.internal.ssl.Provider(); 181 } else if (provName.equals("Apple") || provName.equals("apple.security.AppleProvider")) { 182 // need to use reflection since this class only exists on MacOsx 183 p = AccessController.doPrivileged(new PrivilegedAction<Provider>() { 184 public Provider run() { 185 try { 186 Class<?> c = Class.forName("apple.security.AppleProvider"); 187 if (Provider.class.isAssignableFrom(c)) { 188 return (Provider) c.newInstance(); 189 } else { 190 return null; 191 } 192 } catch (Exception ex) { 193 if (debug != null) { 194 debug.println("Error loading provider Apple"); 195 ex.printStackTrace(); 196 } 197 return null; 198 } 199 } 200 }); 201 } else { 202 if (isLoading) { 203 // because this method is synchronized, this can only 204 // happen if there is recursion. 205 if (debug != null) { 206 debug.println("Recursion loading provider: " + this); 207 new Exception("Call trace").printStackTrace(); 208 } 209 return null; 210 } 211 try { 212 isLoading = true; 213 tries++; 214 p = doLoadProvider(); 215 } finally { 216 isLoading = false; 217 } 218 } 219 provider = p; 220 return p; 221 } 222 223 /** 224 * Load and instantiate the Provider described by this class. 225 * 226 * NOTE use of doPrivileged(). 227 * 228 * @return null if the Provider could not be loaded 229 * 230 * @throws ProviderException if executing the Provider's constructor 231 * throws a ProviderException. All other Exceptions are ignored. 232 */ 233 private Provider doLoadProvider() { 234 return AccessController.doPrivileged(new PrivilegedAction<Provider>() { 235 public Provider run() { 236 if (debug != null) { 237 debug.println("Loading provider " + ProviderConfig.this); 238 } 239 ProviderLoader pl = new ProviderLoader(); 240 try { 241 Provider p = pl.load(provName); 242 if (p != null) { 243 if (hasArgument()) { 244 p = p.configure(argument); 245 } 246 if (debug != null) { 247 debug.println("Loaded provider " + p.getName()); 248 } 249 } else { 250 if (debug != null) { 251 debug.println("Error loading provider " + 252 ProviderConfig.this); 253 } 254 disableLoad(); 255 } 256 return p; 257 } catch (Exception e) { 258 if (e instanceof ProviderException) { 259 // pass up 260 throw e; 261 } else { 262 if (debug != null) { 263 debug.println("Error loading provider " + 264 ProviderConfig.this); 265 e.printStackTrace(); 266 } 267 disableLoad(); 268 return null; 269 } 270 } catch (ExceptionInInitializerError err) { 271 // no sufficient permission to initialize provider class 272 if (debug != null) { 273 debug.println("Error loading provider " + ProviderConfig.this); 274 err.printStackTrace(); 275 } 276 disableLoad(); 277 return null; 278 } 279 } 280 }); 281 } 282 283 /** 284 * Perform property expansion of the provider value. 285 * 286 * NOTE use of doPrivileged(). 287 */ 288 private static String expand(final String value) { 289 // shortcut if value does not contain any properties 290 if (value.contains("${") == false) { 291 return value; 292 } 293 return AccessController.doPrivileged(new PrivilegedAction<String>() { 294 public String run() { 295 try { 296 return PropertyExpander.expand(value); 297 } catch (GeneralSecurityException e) { 298 throw new ProviderException(e); 299 } 300 } 301 }); 302 } 303 304 // Inner class for loading security providers listed in java.security file 305 private static final class ProviderLoader { 306 private final ServiceLoader<Provider> services; 307 308 ProviderLoader() { 309 // VM should already been booted at this point, if not 310 // - Only providers in java.base should be loaded, don't use 311 // ServiceLoader 312 // - ClassLoader.getSystemClassLoader() will throw InternalError 313 services = ServiceLoader.load(java.security.Provider.class, 314 ClassLoader.getSystemClassLoader()); 315 } 316 317 /** 318 * Loads the provider with the specified class name. 319 * 320 * @param name the name of the provider 321 * @return the Provider, or null if it cannot be found or loaded 322 * @throws ProviderException all other exceptions are ignored 323 */ 324 public Provider load(String pn) { 325 if (debug != null) { 326 debug.println("Attempt to load " + pn + " using SL"); 327 } 328 Iterator<Provider> iter = services.iterator(); 329 while (iter.hasNext()) { 330 try { 331 Provider p = iter.next(); 332 String pName = p.getName(); 333 if (debug != null) { 334 debug.println("Found SL Provider named " + pName); 335 } 336 if (pName.equals(pn)) { 337 return p; 338 } 339 } catch (SecurityException | ServiceConfigurationError | 340 InvalidParameterException ex) { 341 // if provider loading fail due to security permission, 342 // log it and move on to next provider 343 if (debug != null) { 344 debug.println("Encountered " + ex + 345 " while iterating through SL, ignore and move on"); 346 ex.printStackTrace(); 347 } 348 } 349 } 350 // No success with ServiceLoader. Try loading provider the legacy, 351 // i.e. pre-module, way via reflection 352 try { 353 return legacyLoad(pn); 354 } catch (ProviderException pe) { 355 // pass through 356 throw pe; 357 } catch (Exception ex) { 358 // logged and ignored 359 if (debug != null) { 360 debug.println("Encountered " + ex + 361 " during legacy load of " + pn); 362 ex.printStackTrace(); 363 } 364 return null; 365 } 366 } 367 368 private Provider legacyLoad(String classname) { 369 370 if (debug != null) { 371 debug.println("Loading legacy provider: " + classname); 372 } 373 374 try { 375 Class<?> provClass = 376 ClassLoader.getSystemClassLoader().loadClass(classname); 377 378 // only continue if the specified class extends Provider 379 if (!Provider.class.isAssignableFrom(provClass)) { 380 if (debug != null) { 381 debug.println(classname + " is not a provider"); 382 } 383 return null; 384 } 385 386 Provider p = AccessController.doPrivileged 387 (new PrivilegedExceptionAction<Provider>() { 388 public Provider run() throws Exception { 389 return (Provider) provClass.newInstance(); 390 } 391 }); 392 return p; 393 } catch (Exception e) { 394 Throwable t; 395 if (e instanceof InvocationTargetException) { 396 t = ((InvocationTargetException)e).getCause(); 397 } else { 398 t = e; 399 } 400 if (debug != null) { 401 debug.println("Error loading legacy provider " + classname); 402 t.printStackTrace(); 403 } 404 // provider indicates fatal error, pass through exception 405 if (t instanceof ProviderException) { 406 throw (ProviderException) t; 407 } 408 return null; 409 } catch (ExceptionInInitializerError | NoClassDefFoundError err) { 410 // no sufficient permission to access/initialize provider class 411 if (debug != null) { 412 debug.println("Error loading legacy provider " + classname); 413 err.printStackTrace(); 414 } 415 return null; 416 } 417 } 418 } 419} 420