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