/* * 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.util.*; import java.security.*; import sun.security.util.PropertyExpander; /** * Class representing a configured provider which encapsulates configuration * (provider name + optional argument), the provider loading logic, and * the loaded Provider object itself. * * @author Andreas Sterbenz * @since 1.5 */ final class ProviderConfig { private static final sun.security.util.Debug debug = sun.security.util.Debug.getInstance("jca", "ProviderConfig"); // suffix for identifying the SunPKCS11-Solaris provider private static final String P11_SOL_NAME = "SunPKCS11"; // config file argument of the SunPKCS11-Solaris provider private static final String P11_SOL_ARG = "${java.home}/conf/security/sunpkcs11-solaris.cfg"; // maximum number of times to try loading a provider before giving up private static final int MAX_LOAD_TRIES = 30; // could be provider name (module) or provider class name (legacy) private final String provName; // argument to the Provider.configure() call, never null 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 provName, String argument) { if (provName.endsWith(P11_SOL_NAME) && argument.equals(P11_SOL_ARG)) { checkSunPKCS11Solaris(); } this.provName = provName; this.argument = expand(argument); } ProviderConfig(String provName) { this(provName, ""); } ProviderConfig(Provider provider) { this.provName = provider.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() { 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.isEmpty(); } // 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.provName.equals(other.provName) && this.argument.equals(other.argument); } public int hashCode() { return provName.hashCode() + argument.hashCode(); } public String toString() { if (hasArgument()) { return provName + "('" + argument + "')"; } else { return provName; } } /** * Get the provider object. Loads the provider if it is not already loaded. */ // com.sun.net.ssl.internal.ssl.Provider has been deprecated since JDK 9 @SuppressWarnings("deprecation") synchronized Provider getProvider() { // volatile variable load Provider p = provider; if (p != null) { return p; } if (shouldLoad() == false) { return null; } // Create providers which are in java.base directly if (provName.equals("SUN") || provName.equals("sun.security.provider.Sun")) { p = new sun.security.provider.Sun(); } else if (provName.equals("SunRsaSign") || provName.equals("sun.security.rsa.SunRsaSign")) { p = new sun.security.rsa.SunRsaSign(); } else if (provName.equals("SunJCE") || provName.equals("com.sun.crypto.provider.SunJCE")) { p = new com.sun.crypto.provider.SunJCE(); } else if (provName.equals("SunJSSE") || provName.equals("com.sun.net.ssl.internal.ssl.Provider")) { p = new com.sun.net.ssl.internal.ssl.Provider(); } else if (provName.equals("Apple") || provName.equals("apple.security.AppleProvider")) { // need to use reflection since this class only exists on MacOsx p = AccessController.doPrivileged(new PrivilegedAction() { public Provider run() { try { Class c = Class.forName("apple.security.AppleProvider"); if (Provider.class.isAssignableFrom(c)) { @SuppressWarnings("deprecation") Object tmp = c.newInstance(); return (Provider) tmp; } else { return null; } } catch (Exception ex) { if (debug != null) { debug.println("Error loading provider Apple"); ex.printStackTrace(); } return null; } } }); } else { 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() { public Provider run() { if (debug != null) { debug.println("Loading provider " + ProviderConfig.this); } try { Provider p = ProviderLoader.INSTANCE.load(provName); if (p != null) { if (hasArgument()) { p = p.configure(argument); } if (debug != null) { debug.println("Loaded provider " + p.getName()); } } else { if (debug != null) { debug.println("Error loading provider " + ProviderConfig.this); } disableLoad(); } return p; } catch (Exception e) { if (e instanceof ProviderException) { // pass up throw e; } else { if (debug != null) { debug.println("Error loading provider " + ProviderConfig.this); e.printStackTrace(); } disableLoad(); return null; } } catch (ExceptionInInitializerError err) { // no sufficient permission to initialize provider class if (debug != null) { debug.println("Error loading provider " + ProviderConfig.this); err.printStackTrace(); } disableLoad(); return null; } } }); } /** * 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() { public String run() { try { return PropertyExpander.expand(value); } catch (GeneralSecurityException e) { throw new ProviderException(e); } } }); } // Inner class for loading security providers listed in java.security file private static final class ProviderLoader { static final ProviderLoader INSTANCE = new ProviderLoader(); private final ServiceLoader services; private ProviderLoader() { // VM should already been booted at this point, if not // - Only providers in java.base should be loaded, don't use // ServiceLoader // - ClassLoader.getSystemClassLoader() will throw InternalError services = ServiceLoader.load(java.security.Provider.class, ClassLoader.getSystemClassLoader()); } /** * Loads the provider with the specified class name. * * @param name the name of the provider * @return the Provider, or null if it cannot be found or loaded * @throws ProviderException all other exceptions are ignored */ public Provider load(String pn) { if (debug != null) { debug.println("Attempt to load " + pn + " using SL"); } Iterator iter = services.iterator(); while (iter.hasNext()) { try { Provider p = iter.next(); String pName = p.getName(); if (debug != null) { debug.println("Found SL Provider named " + pName); } if (pName.equals(pn)) { return p; } } catch (SecurityException | ServiceConfigurationError | InvalidParameterException ex) { // if provider loading fail due to security permission, // log it and move on to next provider if (debug != null) { debug.println("Encountered " + ex + " while iterating through SL, ignore and move on"); ex.printStackTrace(); } } } // No success with ServiceLoader. Try loading provider the legacy, // i.e. pre-module, way via reflection try { return legacyLoad(pn); } catch (ProviderException pe) { // pass through throw pe; } catch (Exception ex) { // logged and ignored if (debug != null) { debug.println("Encountered " + ex + " during legacy load of " + pn); ex.printStackTrace(); } return null; } } private Provider legacyLoad(String classname) { if (debug != null) { debug.println("Loading legacy provider: " + classname); } try { Class provClass = ClassLoader.getSystemClassLoader().loadClass(classname); // only continue if the specified class extends Provider if (!Provider.class.isAssignableFrom(provClass)) { if (debug != null) { debug.println(classname + " is not a provider"); } return null; } Provider p = AccessController.doPrivileged (new PrivilegedExceptionAction() { @SuppressWarnings("deprecation") // Class.newInstance public Provider run() throws Exception { return (Provider) provClass.newInstance(); } }); return p; } catch (Exception e) { Throwable t; if (e instanceof InvocationTargetException) { t = ((InvocationTargetException)e).getCause(); } else { t = e; } if (debug != null) { debug.println("Error loading legacy provider " + classname); t.printStackTrace(); } // provider indicates fatal error, pass through exception if (t instanceof ProviderException) { throw (ProviderException) t; } return null; } catch (ExceptionInInitializerError | NoClassDefFoundError err) { // no sufficient permission to access/initialize provider class if (debug != null) { debug.println("Error loading legacy provider " + classname); err.printStackTrace(); } return null; } } } }