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