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 31 import java.security.*; 32 33 import sun.security.util.PropertyExpander; 34 35 /** 36 * Class representing a configured provider. Encapsulates configuration 37 * (className plus optional argument), the provider loading logic, and 38 * the loaded Provider object itself. 39 * 40 * @author Andreas Sterbenz 41 * @since 1.5 42 */ 43 final class ProviderConfig { 44 45 private final static sun.security.util.Debug debug = 46 sun.security.util.Debug.getInstance("jca", "ProviderConfig"); 47 48 // classname of the SunPKCS11-Solaris provider 49 private static final String P11_SOL_NAME = 50 "sun.security.pkcs11.SunPKCS11"; 51 52 // config file argument of the SunPKCS11-Solaris provider 53 private static final String P11_SOL_ARG = 54 "${java.home}/lib/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 // parameters for the Provider(String) constructor, 60 // use by doLoadProvider() 61 private final static Class[] CL_STRING = { String.class }; 62 63 // name of the provider class 64 private final String className; 65 66 // argument to the provider constructor, 67 // empty string indicates no-arg constructor 68 private final String argument; 69 70 // number of times we have already tried to load this provider 71 private int tries; 72 73 // Provider object, if loaded 74 private volatile Provider provider; 75 76 // flag indicating if we are currently trying to load the provider 77 // used to detect recursion 78 private boolean isLoading; 79 ProviderConfig(String className, String argument)80 ProviderConfig(String className, String argument) { 81 if (className.equals(P11_SOL_NAME) && argument.equals(P11_SOL_ARG)) { 82 checkSunPKCS11Solaris(); 83 } 84 this.className = className; 85 this.argument = expand(argument); 86 } 87 ProviderConfig(String className)88 ProviderConfig(String className) { 89 this(className, ""); 90 } 91 ProviderConfig(Provider provider)92 ProviderConfig(Provider provider) { 93 this.className = provider.getClass().getName(); 94 this.argument = ""; 95 this.provider = provider; 96 } 97 98 // check if we should try to load the SunPKCS11-Solaris provider 99 // avoid if not available (pre Solaris 10) to reduce startup time 100 // or if disabled via system property checkSunPKCS11Solaris()101 private void checkSunPKCS11Solaris() { 102 Boolean o = AccessController.doPrivileged( 103 new PrivilegedAction<Boolean>() { 104 public Boolean run() { 105 File file = new File("/usr/lib/libpkcs11.so"); 106 if (file.exists() == false) { 107 return Boolean.FALSE; 108 } 109 if ("false".equalsIgnoreCase(System.getProperty 110 ("sun.security.pkcs11.enable-solaris"))) { 111 return Boolean.FALSE; 112 } 113 return Boolean.TRUE; 114 } 115 }); 116 if (o == Boolean.FALSE) { 117 tries = MAX_LOAD_TRIES; 118 } 119 } 120 hasArgument()121 private boolean hasArgument() { 122 return argument.length() != 0; 123 } 124 125 // should we try to load this provider? shouldLoad()126 private boolean shouldLoad() { 127 return (tries < MAX_LOAD_TRIES); 128 } 129 130 // do not try to load this provider again disableLoad()131 private void disableLoad() { 132 tries = MAX_LOAD_TRIES; 133 } 134 isLoaded()135 boolean isLoaded() { 136 return (provider != null); 137 } 138 equals(Object obj)139 public boolean equals(Object obj) { 140 if (this == obj) { 141 return true; 142 } 143 if (obj instanceof ProviderConfig == false) { 144 return false; 145 } 146 ProviderConfig other = (ProviderConfig)obj; 147 return this.className.equals(other.className) 148 && this.argument.equals(other.argument); 149 } 150 hashCode()151 public int hashCode() { 152 return className.hashCode() + argument.hashCode(); 153 } 154 toString()155 public String toString() { 156 if (hasArgument()) { 157 return className + "('" + argument + "')"; 158 } else { 159 return className; 160 } 161 } 162 163 /** 164 * Get the provider object. Loads the provider if it is not already loaded. 165 */ getProvider()166 synchronized Provider getProvider() { 167 // volatile variable load 168 Provider p = provider; 169 if (p != null) { 170 return p; 171 } 172 if (shouldLoad() == false) { 173 return null; 174 } 175 if (isLoading) { 176 // because this method is synchronized, this can only 177 // happen if there is recursion. 178 if (debug != null) { 179 debug.println("Recursion loading provider: " + this); 180 new Exception("Call trace").printStackTrace(); 181 } 182 return null; 183 } 184 try { 185 isLoading = true; 186 tries++; 187 p = doLoadProvider(); 188 } finally { 189 isLoading = false; 190 } 191 provider = p; 192 return p; 193 } 194 195 /** 196 * Load and instantiate the Provider described by this class. 197 * 198 * NOTE use of doPrivileged(). 199 * 200 * @return null if the Provider could not be loaded 201 * 202 * @throws ProviderException if executing the Provider's constructor 203 * throws a ProviderException. All other Exceptions are ignored. 204 */ doLoadProvider()205 private Provider doLoadProvider() { 206 return AccessController.doPrivileged(new PrivilegedAction<Provider>() { 207 public Provider run() { 208 if (debug != null) { 209 debug.println("Loading provider: " + ProviderConfig.this); 210 } 211 212 // BEGIN Android-changed: Prefer the boot classloader to the system classloader. 213 try { 214 // First try with the boot classloader. 215 return initProvider(className, Object.class.getClassLoader()); 216 } catch (Exception e1) { 217 // If that fails, try with the system classloader. 218 try { 219 return initProvider(className, ClassLoader.getSystemClassLoader()); 220 } catch (Exception e) { 221 Throwable t; 222 if (e instanceof InvocationTargetException) { 223 t = ((InvocationTargetException)e).getCause(); 224 } else { 225 t = e; 226 } 227 if (debug != null) { 228 debug.println("Error loading provider " + ProviderConfig.this); 229 t.printStackTrace(); 230 } 231 // provider indicates fatal error, pass through exception 232 if (t instanceof ProviderException) { 233 throw (ProviderException)t; 234 } 235 // provider indicates that loading should not be retried 236 if (t instanceof UnsupportedOperationException) { 237 disableLoad(); 238 } 239 return null; 240 } 241 } 242 } 243 }); 244 } 245 246 private Provider initProvider(String className, ClassLoader cl) throws Exception { 247 Class<?> provClass; 248 if (cl != null) { 249 provClass = cl.loadClass(className); 250 } else { 251 provClass = Class.forName(className); 252 } 253 Object obj; 254 if (hasArgument() == false) { 255 obj = provClass.newInstance(); 256 } else { 257 Constructor<?> cons = provClass.getConstructor(CL_STRING); 258 obj = cons.newInstance(argument); 259 } 260 if (obj instanceof Provider) { 261 if (debug != null) { 262 debug.println("Loaded provider " + obj); 263 } 264 return (Provider)obj; 265 } else { 266 if (debug != null) { 267 debug.println(className + " is not a provider"); 268 } 269 disableLoad(); 270 return null; 271 } 272 } 273 // END Android-changed: Prefer the boot classloader to the system classloader. 274 275 /** 276 * Perform property expansion of the provider value. 277 * 278 * NOTE use of doPrivileged(). 279 */ 280 private static String expand(final String value) { 281 // shortcut if value does not contain any properties 282 if (value.contains("${") == false) { 283 return value; 284 } 285 return AccessController.doPrivileged(new PrivilegedAction<String>() { 286 public String run() { 287 try { 288 return PropertyExpander.expand(value); 289 } catch (GeneralSecurityException e) { 290 throw new ProviderException(e); 291 } 292 } 293 }); 294 } 295 296 } 297