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 // com.sun.net.ssl.internal.ssl.Provider has been deprecated since JDK 9 163 @SuppressWarnings("deprecation") 164 synchronized Provider getProvider() { 165 // volatile variable load 166 Provider p = provider; 167 if (p != null) { 168 return p; 169 } 170 if (shouldLoad() == false) { 171 return null; 172 } 173 174 // Create providers which are in java.base directly 175 if (provName.equals("SUN") || provName.equals("sun.security.provider.Sun")) { 176 p = new sun.security.provider.Sun(); 177 } else if (provName.equals("SunRsaSign") || provName.equals("sun.security.rsa.SunRsaSign")) { 178 p = new sun.security.rsa.SunRsaSign(); 179 } else if (provName.equals("SunJCE") || provName.equals("com.sun.crypto.provider.SunJCE")) { 180 p = new com.sun.crypto.provider.SunJCE(); 181 } else if (provName.equals("SunJSSE") || provName.equals("com.sun.net.ssl.internal.ssl.Provider")) { 182 p = new com.sun.net.ssl.internal.ssl.Provider(); 183 } else if (provName.equals("Apple") || provName.equals("apple.security.AppleProvider")) { 184 // need to use reflection since this class only exists on MacOsx 185 p = AccessController.doPrivileged(new PrivilegedAction<Provider>() { 186 public Provider run() { 187 try { 188 Class<?> c = Class.forName("apple.security.AppleProvider"); 189 if (Provider.class.isAssignableFrom(c)) { 190 @SuppressWarnings("deprecation") 191 Object tmp = c.newInstance(); 192 return (Provider) tmp; 193 } else { 194 return null; 195 } 196 } catch (Exception ex) { 197 if (debug != null) { 198 debug.println("Error loading provider Apple"); 199 ex.printStackTrace(); 200 } 201 return null; 202 } 203 } 204 }); 205 } else { 206 if (isLoading) { 207 // because this method is synchronized, this can only 208 // happen if there is recursion. 209 if (debug != null) { 210 debug.println("Recursion loading provider: " + this); 211 new Exception("Call trace").printStackTrace(); 212 } 213 return null; 214 } 215 try { 216 isLoading = true; 217 tries++; 218 p = doLoadProvider(); 219 } finally { 220 isLoading = false; 221 } 222 } 223 provider = p; 224 return p; 225 } 226 227 /** 228 * Load and instantiate the Provider described by this class. 229 * 230 * NOTE use of doPrivileged(). 231 * 232 * @return null if the Provider could not be loaded 233 * 234 * @throws ProviderException if executing the Provider's constructor 235 * throws a ProviderException. All other Exceptions are ignored. 236 */ 237 private Provider doLoadProvider() { 238 return AccessController.doPrivileged(new PrivilegedAction<Provider>() { 239 public Provider run() { 240 if (debug != null) { 241 debug.println("Loading provider " + ProviderConfig.this); 242 } 243 try { 244 Provider p = ProviderLoader.INSTANCE.load(provName); 245 if (p != null) { 246 if (hasArgument()) { 247 p = p.configure(argument); 248 } 249 if (debug != null) { 250 debug.println("Loaded provider " + p.getName()); 251 } 252 } else { 253 if (debug != null) { 254 debug.println("Error loading provider " + 255 ProviderConfig.this); 256 } 257 disableLoad(); 258 } 259 return p; 260 } catch (Exception e) { 261 if (e instanceof ProviderException) { 262 // pass up 263 throw e; 264 } else { 265 if (debug != null) { 266 debug.println("Error loading provider " + 267 ProviderConfig.this); 268 e.printStackTrace(); 269 } 270 disableLoad(); 271 return null; 272 } 273 } catch (ExceptionInInitializerError err) { 274 // no sufficient permission to initialize provider class 275 if (debug != null) { 276 debug.println("Error loading provider " + ProviderConfig.this); 277 err.printStackTrace(); 278 } 279 disableLoad(); 280 return null; 281 } 282 } 283 }); 284 } 285 286 /** 287 * Perform property expansion of the provider value. 288 * 289 * NOTE use of doPrivileged(). 290 */ 291 private static String expand(final String value) { 292 // shortcut if value does not contain any properties 293 if (value.contains("${") == false) { 294 return value; 295 } 296 return AccessController.doPrivileged(new PrivilegedAction<String>() { 297 public String run() { 298 try { 299 return PropertyExpander.expand(value); 300 } catch (GeneralSecurityException e) { 301 throw new ProviderException(e); 302 } 303 } 304 }); 305 } 306 307 // Inner class for loading security providers listed in java.security file 308 private static final class ProviderLoader { 309 static final ProviderLoader INSTANCE = new ProviderLoader(); 310 311 private final ServiceLoader<Provider> services; 312 313 private ProviderLoader() { 314 // VM should already been booted at this point, if not 315 // - Only providers in java.base should be loaded, don't use 316 // ServiceLoader 317 // - ClassLoader.getSystemClassLoader() will throw InternalError 318 services = ServiceLoader.load(java.security.Provider.class, 319 ClassLoader.getSystemClassLoader()); 320 } 321 322 /** 323 * Loads the provider with the specified class name. 324 * 325 * @param name the name of the provider 326 * @return the Provider, or null if it cannot be found or loaded 327 * @throws ProviderException all other exceptions are ignored 328 */ 329 public Provider load(String pn) { 330 if (debug != null) { 331 debug.println("Attempt to load " + pn + " using SL"); 332 } 333 Iterator<Provider> iter = services.iterator(); 334 while (iter.hasNext()) { 335 try { 336 Provider p = iter.next(); 337 String pName = p.getName(); 338 if (debug != null) { 339 debug.println("Found SL Provider named " + pName); 340 } 341 if (pName.equals(pn)) { 342 return p; 343 } 344 } catch (SecurityException | ServiceConfigurationError | 345 InvalidParameterException ex) { 346 // if provider loading fail due to security permission, 347 // log it and move on to next provider 348 if (debug != null) { 349 debug.println("Encountered " + ex + 350 " while iterating through SL, ignore and move on"); 351 ex.printStackTrace(); 352 } 353 } 354 } 355 // No success with ServiceLoader. Try loading provider the legacy, 356 // i.e. pre-module, way via reflection 357 try { 358 return legacyLoad(pn); 359 } catch (ProviderException pe) { 360 // pass through 361 throw pe; 362 } catch (Exception ex) { 363 // logged and ignored 364 if (debug != null) { 365 debug.println("Encountered " + ex + 366 " during legacy load of " + pn); 367 ex.printStackTrace(); 368 } 369 return null; 370 } 371 } 372 373 private Provider legacyLoad(String classname) { 374 375 if (debug != null) { 376 debug.println("Loading legacy provider: " + classname); 377 } 378 379 try { 380 Class<?> provClass = 381 ClassLoader.getSystemClassLoader().loadClass(classname); 382 383 // only continue if the specified class extends Provider 384 if (!Provider.class.isAssignableFrom(provClass)) { 385 if (debug != null) { 386 debug.println(classname + " is not a provider"); 387 } 388 return null; 389 } 390 391 Provider p = AccessController.doPrivileged 392 (new PrivilegedExceptionAction<Provider>() { 393 @SuppressWarnings("deprecation") // Class.newInstance 394 public Provider run() throws Exception { 395 return (Provider) provClass.newInstance(); 396 } 397 }); 398 return p; 399 } catch (Exception e) { 400 Throwable t; 401 if (e instanceof InvocationTargetException) { 402 t = ((InvocationTargetException)e).getCause(); 403 } else { 404 t = e; 405 } 406 if (debug != null) { 407 debug.println("Error loading legacy provider " + classname); 408 t.printStackTrace(); 409 } 410 // provider indicates fatal error, pass through exception 411 if (t instanceof ProviderException) { 412 throw (ProviderException) t; 413 } 414 return null; 415 } catch (ExceptionInInitializerError | NoClassDefFoundError err) { 416 // no sufficient permission to access/initialize provider class 417 if (debug != null) { 418 debug.println("Error loading legacy provider " + classname); 419 err.printStackTrace(); 420 } 421 return null; 422 } 423 } 424 } 425 }