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