| /* |
| * Copyright (c) 2003, 2016, Oracle and/or its affiliates. All rights reserved. |
| * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. |
| * |
| * This code is free software; you can redistribute it and/or modify it |
| * under the terms of the GNU General Public License version 2 only, as |
| * published by the Free Software Foundation. Oracle designates this |
| * particular file as subject to the "Classpath" exception as provided |
| * by Oracle in the LICENSE file that accompanied this code. |
| * |
| * This code is distributed in the hope that it will be useful, but WITHOUT |
| * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
| * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
| * version 2 for more details (a copy is included in the LICENSE file that |
| * accompanied this code). |
| * |
| * You should have received a copy of the GNU General Public License version |
| * 2 along with this work; if not, write to the Free Software Foundation, |
| * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. |
| * |
| * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA |
| * or visit www.oracle.com if you need additional information or have any |
| * questions. |
| */ |
| |
| package sun.security.jca; |
| |
| import java.io.File; |
| import java.lang.reflect.*; |
| |
| import java.security.*; |
| |
| import sun.security.util.PropertyExpander; |
| |
| /** |
| * Class representing a configured provider. Encapsulates configuration |
| * (className plus optional argument), the provider loading logic, and |
| * the loaded Provider object itself. |
| * |
| * @author Andreas Sterbenz |
| * @since 1.5 |
| */ |
| final class ProviderConfig { |
| |
| private final static sun.security.util.Debug debug = |
| sun.security.util.Debug.getInstance("jca", "ProviderConfig"); |
| |
| // classname of the SunPKCS11-Solaris provider |
| private static final String P11_SOL_NAME = |
| "sun.security.pkcs11.SunPKCS11"; |
| |
| // config file argument of the SunPKCS11-Solaris provider |
| private static final String P11_SOL_ARG = |
| "${java.home}/lib/security/sunpkcs11-solaris.cfg"; |
| |
| // maximum number of times to try loading a provider before giving up |
| private final static int MAX_LOAD_TRIES = 30; |
| |
| // parameters for the Provider(String) constructor, |
| // use by doLoadProvider() |
| private final static Class[] CL_STRING = { String.class }; |
| |
| // name of the provider class |
| private final String className; |
| |
| // argument to the provider constructor, |
| // empty string indicates no-arg constructor |
| private final String argument; |
| |
| // number of times we have already tried to load this provider |
| private int tries; |
| |
| // Provider object, if loaded |
| private volatile Provider provider; |
| |
| // flag indicating if we are currently trying to load the provider |
| // used to detect recursion |
| private boolean isLoading; |
| |
| ProviderConfig(String className, String argument) { |
| if (className.equals(P11_SOL_NAME) && argument.equals(P11_SOL_ARG)) { |
| checkSunPKCS11Solaris(); |
| } |
| this.className = className; |
| this.argument = expand(argument); |
| } |
| |
| ProviderConfig(String className) { |
| this(className, ""); |
| } |
| |
| ProviderConfig(Provider provider) { |
| this.className = provider.getClass().getName(); |
| this.argument = ""; |
| this.provider = provider; |
| } |
| |
| // check if we should try to load the SunPKCS11-Solaris provider |
| // avoid if not available (pre Solaris 10) to reduce startup time |
| // or if disabled via system property |
| private void checkSunPKCS11Solaris() { |
| Boolean o = AccessController.doPrivileged( |
| new PrivilegedAction<Boolean>() { |
| public Boolean run() { |
| File file = new File("/usr/lib/libpkcs11.so"); |
| if (file.exists() == false) { |
| return Boolean.FALSE; |
| } |
| if ("false".equalsIgnoreCase(System.getProperty |
| ("sun.security.pkcs11.enable-solaris"))) { |
| return Boolean.FALSE; |
| } |
| return Boolean.TRUE; |
| } |
| }); |
| if (o == Boolean.FALSE) { |
| tries = MAX_LOAD_TRIES; |
| } |
| } |
| |
| private boolean hasArgument() { |
| return argument.length() != 0; |
| } |
| |
| // should we try to load this provider? |
| private boolean shouldLoad() { |
| return (tries < MAX_LOAD_TRIES); |
| } |
| |
| // do not try to load this provider again |
| private void disableLoad() { |
| tries = MAX_LOAD_TRIES; |
| } |
| |
| boolean isLoaded() { |
| return (provider != null); |
| } |
| |
| public boolean equals(Object obj) { |
| if (this == obj) { |
| return true; |
| } |
| if (obj instanceof ProviderConfig == false) { |
| return false; |
| } |
| ProviderConfig other = (ProviderConfig)obj; |
| return this.className.equals(other.className) |
| && this.argument.equals(other.argument); |
| } |
| |
| public int hashCode() { |
| return className.hashCode() + argument.hashCode(); |
| } |
| |
| public String toString() { |
| if (hasArgument()) { |
| return className + "('" + argument + "')"; |
| } else { |
| return className; |
| } |
| } |
| |
| /** |
| * Get the provider object. Loads the provider if it is not already loaded. |
| */ |
| synchronized Provider getProvider() { |
| // volatile variable load |
| Provider p = provider; |
| if (p != null) { |
| return p; |
| } |
| if (shouldLoad() == false) { |
| return null; |
| } |
| if (isLoading) { |
| // because this method is synchronized, this can only |
| // happen if there is recursion. |
| if (debug != null) { |
| debug.println("Recursion loading provider: " + this); |
| new Exception("Call trace").printStackTrace(); |
| } |
| return null; |
| } |
| try { |
| isLoading = true; |
| tries++; |
| p = doLoadProvider(); |
| } finally { |
| isLoading = false; |
| } |
| provider = p; |
| return p; |
| } |
| |
| /** |
| * Load and instantiate the Provider described by this class. |
| * |
| * NOTE use of doPrivileged(). |
| * |
| * @return null if the Provider could not be loaded |
| * |
| * @throws ProviderException if executing the Provider's constructor |
| * throws a ProviderException. All other Exceptions are ignored. |
| */ |
| private Provider doLoadProvider() { |
| return AccessController.doPrivileged(new PrivilegedAction<Provider>() { |
| public Provider run() { |
| if (debug != null) { |
| debug.println("Loading provider: " + ProviderConfig.this); |
| } |
| |
| // BEGIN Android-changed: Prefer the boot classloader to the system classloader. |
| try { |
| // First try with the boot classloader. |
| return initProvider(className, Object.class.getClassLoader()); |
| } catch (Exception e1) { |
| // If that fails, try with the system classloader. |
| try { |
| return initProvider(className, ClassLoader.getSystemClassLoader()); |
| } catch (Exception e) { |
| Throwable t; |
| if (e instanceof InvocationTargetException) { |
| t = ((InvocationTargetException)e).getCause(); |
| } else { |
| t = e; |
| } |
| if (debug != null) { |
| debug.println("Error loading provider " + ProviderConfig.this); |
| t.printStackTrace(); |
| } |
| // provider indicates fatal error, pass through exception |
| if (t instanceof ProviderException) { |
| throw (ProviderException)t; |
| } |
| // provider indicates that loading should not be retried |
| if (t instanceof UnsupportedOperationException) { |
| disableLoad(); |
| } |
| return null; |
| } |
| } |
| } |
| }); |
| } |
| |
| private Provider initProvider(String className, ClassLoader cl) throws Exception { |
| Class<?> provClass; |
| if (cl != null) { |
| provClass = cl.loadClass(className); |
| } else { |
| provClass = Class.forName(className); |
| } |
| Object obj; |
| if (hasArgument() == false) { |
| obj = provClass.newInstance(); |
| } else { |
| Constructor<?> cons = provClass.getConstructor(CL_STRING); |
| obj = cons.newInstance(argument); |
| } |
| if (obj instanceof Provider) { |
| if (debug != null) { |
| debug.println("Loaded provider " + obj); |
| } |
| return (Provider)obj; |
| } else { |
| if (debug != null) { |
| debug.println(className + " is not a provider"); |
| } |
| disableLoad(); |
| return null; |
| } |
| } |
| // END Android-changed: Prefer the boot classloader to the system classloader. |
| |
| /** |
| * Perform property expansion of the provider value. |
| * |
| * NOTE use of doPrivileged(). |
| */ |
| private static String expand(final String value) { |
| // shortcut if value does not contain any properties |
| if (value.contains("${") == false) { |
| return value; |
| } |
| return AccessController.doPrivileged(new PrivilegedAction<String>() { |
| public String run() { |
| try { |
| return PropertyExpander.expand(value); |
| } catch (GeneralSecurityException e) { |
| throw new ProviderException(e); |
| } |
| } |
| }); |
| } |
| |
| } |