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 * (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 */ 44 final class ProviderConfig { 45 46 private final static 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 final static 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")) { 174 p = new sun.security.provider.Sun(); 175 } else if (provName.equals("SunRsaSign")) { 176 p = new sun.security.rsa.SunRsaSign(); 177 } else if (provName.equals("SunJCE")) { 178 p = new com.sun.crypto.provider.SunJCE(); 179 } else if (provName.equals("SunJSSE")) { 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(provName); 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 provider " + 232 ProviderConfig.this); 233 } 234 disableLoad(); 235 } 236 return p; 237 } catch (Exception e) { 238 if (e instanceof ProviderException) { 239 // pass up 240 throw e; 241 } else { 242 if (debug != null) { 243 debug.println("Error loading provider " + 244 ProviderConfig.this); 245 e.printStackTrace(); 246 } 247 disableLoad(); 248 return null; 249 } 250 } catch (ExceptionInInitializerError err) { 251 // no sufficient permission to initialize provider class 252 if (debug != null) { 253 debug.println("Error loading provider " + ProviderConfig.this); 254 err.printStackTrace(); 255 } 256 disableLoad(); 257 return null; 258 } 259 } 260 }); 261 } 262 263 /** 264 * Perform property expansion of the provider value. 265 * 266 * NOTE use of doPrivileged(). 267 */ 268 private static String expand(final String value) { 269 // shortcut if value does not contain any properties 270 if (value.contains("${") == false) { 271 return value; 272 } 273 return AccessController.doPrivileged(new PrivilegedAction<String>() { 274 public String run() { 275 try { 276 return PropertyExpander.expand(value); 277 } catch (GeneralSecurityException e) { 278 throw new ProviderException(e); 279 } 280 } 281 }); 282 } 283 284 // Inner class for loading security providers listed in java.security file 285 private static final class ProviderLoader { 286 private final ServiceLoader<Provider> services; 287 288 ProviderLoader() { 289 // VM should already been booted at this point, if not 290 // - Only providers in java.base should be loaded, don't use 291 // ServiceLoader 292 // - ClassLoader.getSystemClassLoader() will throw InternalError 293 services = ServiceLoader.load(java.security.Provider.class, 294 ClassLoader.getSystemClassLoader()); 295 } 296 297 /** 298 * Loads the provider with the specified class name. 299 * 300 * @param name the name of the provider 301 * @return the Provider, or null if it cannot be found or loaded 302 * @throws ProviderException all other exceptions are ignored 303 */ 304 public Provider load(String pn) { 305 if (debug != null) { 306 debug.println("Attempt to load " + pn + " using SL"); 307 } 308 Iterator<Provider> iter = services.iterator(); 309 while (iter.hasNext()) { 310 try { 311 Provider p = iter.next(); 312 String pName = p.getName(); 313 if (debug != null) { 314 debug.println("Found SL Provider named " + pName); 315 } 316 if (pName.equals(pn)) { 317 return p; 318 } 319 } catch (SecurityException | ServiceConfigurationError | 320 InvalidParameterException ex) { 321 // if provider loading fail due to security permission, 322 // log it and move on to next provider 323 if (debug != null) { 324 debug.println("Encountered " + ex + 325 " while iterating through SL, ignore and move on"); 326 ex.printStackTrace(); 327 } 328 } 329 } 330 // No success with ServiceLoader. Try loading provider the legacy, 331 // i.e. pre-module, way via reflection 332 try { 333 return legacyLoad(pn); 334 } catch (ProviderException pe) { 335 // pass through 336 throw pe; 337 } catch (Exception ex) { 338 // logged and ignored 339 if (debug != null) { 340 debug.println("Encountered " + ex + 341 " during legacy load of " + pn); 342 ex.printStackTrace(); 343 } 344 return null; 345 } 346 } 347 348 private Provider legacyLoad(String classname) { 349 350 if (debug != null) { 351 debug.println("Loading legacy provider: " + classname); 352 } 353 354 try { 355 Class<?> provClass = 356 ClassLoader.getSystemClassLoader().loadClass(classname); 357 358 // only continue if the specified class extends Provider 359 if (!Provider.class.isAssignableFrom(provClass)) { 360 if (debug != null) { 361 debug.println(classname + " is not a provider"); 362 } 363 return null; 364 } 365 366 Provider p = AccessController.doPrivileged 367 (new PrivilegedExceptionAction<Provider>() { 368 public Provider run() throws Exception { 369 return (Provider) provClass.newInstance(); 370 } 371 }); 372 return p; 373 } catch (Exception e) { 374 Throwable t; 375 if (e instanceof InvocationTargetException) { 376 t = ((InvocationTargetException)e).getCause(); 377 } else { 378 t = e; 379 } 380 if (debug != null) { 381 debug.println("Error loading legacy provider " + classname); 382 t.printStackTrace(); 383 } 384 // provider indicates fatal error, pass through exception 385 if (t instanceof ProviderException) { 386 throw (ProviderException) t; 387 } 388 return null; 389 } catch (ExceptionInInitializerError | NoClassDefFoundError err) { 390 // no sufficient permission to access/initialize provider class 391 if (debug != null) { 392 debug.println("Error loading legacy provider " + classname); 393 err.printStackTrace(); 394 } 395 return null; 396 } 397 } 398 } 399 }