J. Duke | 319a3b9 | 2007-12-01 00:00:00 +0000 | [diff] [blame^] | 1 | /* |
| 2 | * Copyright 2003-2006 Sun Microsystems, Inc. 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. Sun designates this |
| 8 | * particular file as subject to the "Classpath" exception as provided |
| 9 | * by Sun 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, |
| 22 | * CA 95054 USA or visit www.sun.com if you need additional information or |
| 23 | * have any 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 | // lock to use while loading a provider. it ensures that each provider |
| 64 | // is loaded only once and that we can detect recursion. |
| 65 | // NOTE that because of 4944382 we use the system classloader as lock. |
| 66 | // By using the same lock to load classes as to load providers we avoid |
| 67 | // deadlock due to lock ordering. However, this class may be initialized |
| 68 | // early in the startup when the system classloader has not yet been set |
| 69 | // up. Use a temporary lock object if that is the case. |
| 70 | // Any of this may break if the class loading implementation is changed. |
| 71 | private static volatile Object LOCK = new Object(); |
| 72 | |
| 73 | private static Object getLock() { |
| 74 | Object o = LOCK; |
| 75 | // check if lock is already set to the class loader |
| 76 | if (o instanceof ClassLoader) { |
| 77 | return o; |
| 78 | } |
| 79 | Object cl = AccessController.doPrivileged( |
| 80 | new PrivilegedAction<Object>() { |
| 81 | public Object run() { |
| 82 | return ClassLoader.getSystemClassLoader(); |
| 83 | } |
| 84 | }); |
| 85 | // check if class loader initialized now (non-null) |
| 86 | if (cl != null) { |
| 87 | LOCK = cl; |
| 88 | o = cl; |
| 89 | } |
| 90 | return o; |
| 91 | } |
| 92 | |
| 93 | |
| 94 | // name of the provider class |
| 95 | private final String className; |
| 96 | |
| 97 | // argument to the provider constructor, |
| 98 | // empty string indicates no-arg constructor |
| 99 | private final String argument; |
| 100 | |
| 101 | // number of times we have already tried to load this provider |
| 102 | private int tries; |
| 103 | |
| 104 | // Provider object, if loaded |
| 105 | private volatile Provider provider; |
| 106 | |
| 107 | // flag indicating if we are currently trying to load the provider |
| 108 | // used to detect recursion |
| 109 | private boolean isLoading; |
| 110 | |
| 111 | ProviderConfig(String className, String argument) { |
| 112 | if (className.equals(P11_SOL_NAME) && argument.equals(P11_SOL_ARG)) { |
| 113 | checkSunPKCS11Solaris(); |
| 114 | } |
| 115 | this.className = className; |
| 116 | this.argument = expand(argument); |
| 117 | } |
| 118 | |
| 119 | ProviderConfig(String className) { |
| 120 | this(className, ""); |
| 121 | } |
| 122 | |
| 123 | ProviderConfig(Provider provider) { |
| 124 | this.className = provider.getClass().getName(); |
| 125 | this.argument = ""; |
| 126 | this.provider = provider; |
| 127 | } |
| 128 | |
| 129 | // check if we should try to load the SunPKCS11-Solaris provider |
| 130 | // avoid if not available (pre Solaris 10) to reduce startup time |
| 131 | // or if disabled via system property |
| 132 | private void checkSunPKCS11Solaris() { |
| 133 | Boolean o = AccessController.doPrivileged( |
| 134 | new PrivilegedAction<Boolean>() { |
| 135 | public Boolean run() { |
| 136 | File file = new File("/usr/lib/libpkcs11.so"); |
| 137 | if (file.exists() == false) { |
| 138 | return Boolean.FALSE; |
| 139 | } |
| 140 | if ("false".equalsIgnoreCase(System.getProperty |
| 141 | ("sun.security.pkcs11.enable-solaris"))) { |
| 142 | return Boolean.FALSE; |
| 143 | } |
| 144 | return Boolean.TRUE; |
| 145 | } |
| 146 | }); |
| 147 | if (o == Boolean.FALSE) { |
| 148 | tries = MAX_LOAD_TRIES; |
| 149 | } |
| 150 | } |
| 151 | |
| 152 | private boolean hasArgument() { |
| 153 | return argument.length() != 0; |
| 154 | } |
| 155 | |
| 156 | // should we try to load this provider? |
| 157 | private boolean shouldLoad() { |
| 158 | return (tries < MAX_LOAD_TRIES); |
| 159 | } |
| 160 | |
| 161 | // do not try to load this provider again |
| 162 | private void disableLoad() { |
| 163 | tries = MAX_LOAD_TRIES; |
| 164 | } |
| 165 | |
| 166 | boolean isLoaded() { |
| 167 | return (provider != null); |
| 168 | } |
| 169 | |
| 170 | public boolean equals(Object obj) { |
| 171 | if (this == obj) { |
| 172 | return true; |
| 173 | } |
| 174 | if (obj instanceof ProviderConfig == false) { |
| 175 | return false; |
| 176 | } |
| 177 | ProviderConfig other = (ProviderConfig)obj; |
| 178 | return this.className.equals(other.className) |
| 179 | && this.argument.equals(other.argument); |
| 180 | } |
| 181 | |
| 182 | public int hashCode() { |
| 183 | return className.hashCode() + argument.hashCode(); |
| 184 | } |
| 185 | |
| 186 | public String toString() { |
| 187 | if (hasArgument()) { |
| 188 | return className + "('" + argument + "')"; |
| 189 | } else { |
| 190 | return className; |
| 191 | } |
| 192 | } |
| 193 | |
| 194 | /** |
| 195 | * Get the provider object. Loads the provider if it is not already loaded. |
| 196 | */ |
| 197 | Provider getProvider() { |
| 198 | // volatile variable load |
| 199 | Provider p = provider; |
| 200 | if (p != null) { |
| 201 | return p; |
| 202 | } |
| 203 | if (shouldLoad() == false) { |
| 204 | return null; |
| 205 | } |
| 206 | synchronized (getLock()) { |
| 207 | p = provider; |
| 208 | if (p != null) { |
| 209 | // loaded by another thread while we were blocked on lock |
| 210 | return p; |
| 211 | } |
| 212 | if (isLoading) { |
| 213 | // because this method is synchronized, this can only |
| 214 | // happen if there is recursion. |
| 215 | if (debug != null) { |
| 216 | debug.println("Recursion loading provider: " + this); |
| 217 | new Exception("Call trace").printStackTrace(); |
| 218 | } |
| 219 | return null; |
| 220 | } |
| 221 | try { |
| 222 | isLoading = true; |
| 223 | tries++; |
| 224 | p = doLoadProvider(); |
| 225 | } finally { |
| 226 | isLoading = false; |
| 227 | } |
| 228 | provider = p; |
| 229 | } |
| 230 | return p; |
| 231 | } |
| 232 | |
| 233 | /** |
| 234 | * Load and instantiate the Provider described by this class. |
| 235 | * |
| 236 | * NOTE use of doPrivileged(). |
| 237 | * |
| 238 | * @return null if the Provider could not be loaded |
| 239 | * |
| 240 | * @throws ProviderException if executing the Provider's constructor |
| 241 | * throws a ProviderException. All other Exceptions are ignored. |
| 242 | */ |
| 243 | private Provider doLoadProvider() { |
| 244 | return AccessController.doPrivileged(new PrivilegedAction<Provider>() { |
| 245 | public Provider run() { |
| 246 | if (debug != null) { |
| 247 | debug.println("Loading provider: " + ProviderConfig.this); |
| 248 | } |
| 249 | try { |
| 250 | ClassLoader cl = ClassLoader.getSystemClassLoader(); |
| 251 | Class<?> provClass; |
| 252 | if (cl != null) { |
| 253 | provClass = cl.loadClass(className); |
| 254 | } else { |
| 255 | provClass = Class.forName(className); |
| 256 | } |
| 257 | Object obj; |
| 258 | if (hasArgument() == false) { |
| 259 | obj = provClass.newInstance(); |
| 260 | } else { |
| 261 | Constructor<?> cons = provClass.getConstructor(CL_STRING); |
| 262 | obj = cons.newInstance(argument); |
| 263 | } |
| 264 | if (obj instanceof Provider) { |
| 265 | if (debug != null) { |
| 266 | debug.println("Loaded provider " + obj); |
| 267 | } |
| 268 | return (Provider)obj; |
| 269 | } else { |
| 270 | if (debug != null) { |
| 271 | debug.println(className + " is not a provider"); |
| 272 | } |
| 273 | disableLoad(); |
| 274 | return null; |
| 275 | } |
| 276 | } catch (Exception e) { |
| 277 | Throwable t; |
| 278 | if (e instanceof InvocationTargetException) { |
| 279 | t = ((InvocationTargetException)e).getCause(); |
| 280 | } else { |
| 281 | t = e; |
| 282 | } |
| 283 | if (debug != null) { |
| 284 | debug.println("Error loading provider " + ProviderConfig.this); |
| 285 | t.printStackTrace(); |
| 286 | } |
| 287 | // provider indicates fatal error, pass through exception |
| 288 | if (t instanceof ProviderException) { |
| 289 | throw (ProviderException)t; |
| 290 | } |
| 291 | // provider indicates that loading should not be retried |
| 292 | if (t instanceof UnsupportedOperationException) { |
| 293 | disableLoad(); |
| 294 | } |
| 295 | return null; |
| 296 | } |
| 297 | } |
| 298 | }); |
| 299 | } |
| 300 | |
| 301 | /** |
| 302 | * Perform property expansion of the provider value. |
| 303 | * |
| 304 | * NOTE use of doPrivileged(). |
| 305 | */ |
| 306 | private static String expand(final String value) { |
| 307 | // shortcut if value does not contain any properties |
| 308 | if (value.contains("${") == false) { |
| 309 | return value; |
| 310 | } |
| 311 | return AccessController.doPrivileged(new PrivilegedAction<String>() { |
| 312 | public String run() { |
| 313 | try { |
| 314 | return PropertyExpander.expand(value); |
| 315 | } catch (GeneralSecurityException e) { |
| 316 | throw new ProviderException(e); |
| 317 | } |
| 318 | } |
| 319 | }); |
| 320 | } |
| 321 | |
| 322 | } |