Home | History | Annotate | Download | only in jca
      1 /*
      2  * Copyright (c) 2003, 2009, 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 
     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 
     88     ProviderConfig(String className) {
     89         this(className, "");
     90     }
     91 
     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
    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 
    121     private boolean hasArgument() {
    122         return argument.length() != 0;
    123     }
    124 
    125     // should we try to load this provider?
    126     private boolean shouldLoad() {
    127         return (tries < MAX_LOAD_TRIES);
    128     }
    129 
    130     // do not try to load this provider again
    131     private void disableLoad() {
    132         tries = MAX_LOAD_TRIES;
    133     }
    134 
    135     boolean isLoaded() {
    136         return (provider != null);
    137     }
    138 
    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 
    151     public int hashCode() {
    152         return className.hashCode() + argument.hashCode();
    153     }
    154 
    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      */
    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      */
    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                 try {
    213                     // First try with the boot classloader.
    214                     return initProvider(className, Object.class.getClassLoader());
    215                 } catch (Exception e1) {
    216                     // If that fails, try with the system classloader.
    217                     try {
    218                         return initProvider(className, ClassLoader.getSystemClassLoader());
    219                     } catch (Exception e) {
    220                         Throwable t;
    221                         if (e instanceof InvocationTargetException) {
    222                             t = ((InvocationTargetException)e).getCause();
    223                         } else {
    224                             t = e;
    225                         }
    226                         if (debug != null) {
    227                             debug.println("Error loading provider " + ProviderConfig.this);
    228                             t.printStackTrace();
    229                         }
    230                         // provider indicates fatal error, pass through exception
    231                         if (t instanceof ProviderException) {
    232                             throw (ProviderException)t;
    233                         }
    234                         // provider indicates that loading should not be retried
    235                         if (t instanceof UnsupportedOperationException) {
    236                             disableLoad();
    237                         }
    238                         return null;
    239                     }
    240                 }
    241             }
    242         });
    243     }
    244 
    245     private Provider initProvider(String className, ClassLoader cl) throws Exception {
    246         Class<?> provClass;
    247         if (cl != null) {
    248             provClass = cl.loadClass(className);
    249         } else {
    250             provClass = Class.forName(className);
    251         }
    252         Object obj;
    253         if (hasArgument() == false) {
    254             obj = provClass.newInstance();
    255         } else {
    256             Constructor<?> cons = provClass.getConstructor(CL_STRING);
    257             obj = cons.newInstance(argument);
    258         }
    259         if (obj instanceof Provider) {
    260             if (debug != null) {
    261                 debug.println("Loaded provider " + obj);
    262             }
    263             return (Provider)obj;
    264         } else {
    265             if (debug != null) {
    266                 debug.println(className + " is not a provider");
    267             }
    268             disableLoad();
    269             return null;
    270         }
    271     }
    272 
    273     /**
    274      * Perform property expansion of the provider value.
    275      *
    276      * NOTE use of doPrivileged().
    277      */
    278     private static String expand(final String value) {
    279         // shortcut if value does not contain any properties
    280         if (value.contains("${") == false) {
    281             return value;
    282         }
    283         return AccessController.doPrivileged(new PrivilegedAction<String>() {
    284             public String run() {
    285                 try {
    286                     return PropertyExpander.expand(value);
    287                 } catch (GeneralSecurityException e) {
    288                     throw new ProviderException(e);
    289                 }
    290             }
    291         });
    292     }
    293 
    294 }
    295