1 /*
   2  * Copyright (c) 2003, 2015, 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 import java.util.*;
  31 
  32 import java.security.*;
  33 
  34 import sun.security.util.PropertyExpander;
  35 
  36 /**
  37  * Class representing a configured provider which encapsulates configuration
  38  * (provider name + optional argument), the provider loading logic, and
  39  * the loaded Provider object itself.
  40  *
  41  * @author  Andreas Sterbenz
  42  * @since   1.5
  43  */
  44 final class ProviderConfig {
  45 
  46     private final static sun.security.util.Debug debug =
  47         sun.security.util.Debug.getInstance("jca", "ProviderConfig");
  48 
  49     // suffix for identifying the SunPKCS11-Solaris provider
  50     private static final String P11_SOL_NAME = "SunPKCS11";
  51 
  52     // config file argument of the SunPKCS11-Solaris provider
  53     private static final String P11_SOL_ARG  =
  54         "${java.home}/conf/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     // could be provider name (module) or provider class name (legacy)
  60     private final String provName;
  61 
  62     // argument to the Provider.configure() call, never null
  63     private final String argument;
  64 
  65     // number of times we have already tried to load this provider
  66     private int tries;
  67 
  68     // Provider object, if loaded
  69     private volatile Provider provider;
  70 
  71     // flag indicating if we are currently trying to load the provider
  72     // used to detect recursion
  73     private boolean isLoading;
  74 
  75     ProviderConfig(String provName, String argument) {
  76         if (provName.endsWith(P11_SOL_NAME) && argument.equals(P11_SOL_ARG)) {
  77             checkSunPKCS11Solaris();
  78         }
  79         this.provName = provName;
  80         this.argument = expand(argument);
  81     }
  82 
  83     ProviderConfig(String provName) {
  84         this(provName, "");
  85     }
  86 
  87     ProviderConfig(Provider provider) {
  88         this.provName = provider.getName();
  89         this.argument = "";
  90         this.provider = provider;
  91     }
  92 
  93     // check if we should try to load the SunPKCS11-Solaris provider
  94     // avoid if not available (pre Solaris 10) to reduce startup time
  95     // or if disabled via system property
  96     private void checkSunPKCS11Solaris() {
  97         Boolean o = AccessController.doPrivileged(
  98                                 new PrivilegedAction<Boolean>() {
  99             public Boolean run() {
 100                 File file = new File("/usr/lib/libpkcs11.so");
 101                 if (file.exists() == false) {
 102                     return Boolean.FALSE;
 103                 }
 104                 if ("false".equalsIgnoreCase(System.getProperty
 105                         ("sun.security.pkcs11.enable-solaris"))) {
 106                     return Boolean.FALSE;
 107                 }
 108                 return Boolean.TRUE;
 109             }
 110         });
 111         if (o == Boolean.FALSE) {
 112             tries = MAX_LOAD_TRIES;
 113         }
 114     }
 115 
 116     private boolean hasArgument() {
 117         return argument.length() != 0;
 118     }
 119 
 120     // should we try to load this provider?
 121     private boolean shouldLoad() {
 122         return (tries < MAX_LOAD_TRIES);
 123     }
 124 
 125     // do not try to load this provider again
 126     private void disableLoad() {
 127         tries = MAX_LOAD_TRIES;
 128     }
 129 
 130     boolean isLoaded() {
 131         return (provider != null);
 132     }
 133 
 134     public boolean equals(Object obj) {
 135         if (this == obj) {
 136             return true;
 137         }
 138         if (obj instanceof ProviderConfig == false) {
 139             return false;
 140         }
 141         ProviderConfig other = (ProviderConfig)obj;
 142         return this.provName.equals(other.provName)
 143             && this.argument.equals(other.argument);
 144 
 145     }
 146 
 147     public int hashCode() {
 148         return provName.hashCode() + argument.hashCode();
 149     }
 150 
 151     public String toString() {
 152         if (hasArgument()) {
 153             return provName + "('" + argument + "')";
 154         } else {
 155             return provName;
 156         }
 157     }
 158 
 159     /**
 160      * Get the provider object. Loads the provider if it is not already loaded.
 161      */
 162     synchronized Provider getProvider() {
 163         // volatile variable load
 164         Provider p = provider;
 165         if (p != null) {
 166             return p;
 167         }
 168         if (shouldLoad() == false) {
 169             return null;
 170         }
 171 
 172         // Create providers which are in java.base directly
 173         if (provName.equals("SUN")) {
 174             p = new sun.security.provider.Sun();
 175         } else if (provName.equals("SunRsaSign")) {
 176             p = new sun.security.rsa.SunRsaSign();
 177         } else if (provName.equals("SunJCE")) {
 178             p = new com.sun.crypto.provider.SunJCE();
 179         } else if (provName.equals("SunJSSE")) {
 180             p = new com.sun.net.ssl.internal.ssl.Provider();
 181         } else {
 182             if (isLoading) {
 183                 // because this method is synchronized, this can only
 184                 // happen if there is recursion.
 185                 if (debug != null) {
 186                     debug.println("Recursion loading provider: " + this);
 187                     new Exception("Call trace").printStackTrace();
 188                 }
 189                 return null;
 190             }
 191             try {
 192                 isLoading = true;
 193                 tries++;
 194                 p = doLoadProvider();
 195             } finally {
 196                 isLoading = false;
 197             }
 198         }
 199         provider = p;
 200         return p;
 201     }
 202 
 203     /**
 204      * Load and instantiate the Provider described by this class.
 205      *
 206      * NOTE use of doPrivileged().
 207      *
 208      * @return null if the Provider could not be loaded
 209      *
 210      * @throws ProviderException if executing the Provider's constructor
 211      * throws a ProviderException. All other Exceptions are ignored.
 212      */
 213     private Provider doLoadProvider() {
 214         return AccessController.doPrivileged(new PrivilegedAction<Provider>() {
 215             public Provider run() {
 216                 if (debug != null) {
 217                     debug.println("Loading provider " + ProviderConfig.this);
 218                 }
 219                 ProviderLoader pl = new ProviderLoader();
 220                 try {
 221                     Provider p = pl.load(provName);
 222                     if (p != null) {
 223                         if (hasArgument()) {
 224                             p = p.configure(argument);
 225                         }
 226                         if (debug != null) {
 227                             debug.println("Loaded provider " + p.getName());
 228                         }
 229                     } else {
 230                         if (debug != null) {
 231                             debug.println("Error loading provider " +
 232                                 ProviderConfig.this);
 233                         }
 234                         disableLoad();
 235                     }
 236                     return p;
 237                 } catch (Exception e) {
 238                     if (e instanceof ProviderException) {
 239                         // pass up
 240                         throw e;
 241                     } else {
 242                         if (debug != null) {
 243                             debug.println("Error loading provider " +
 244                                 ProviderConfig.this);
 245                             e.printStackTrace();
 246                         }
 247                         disableLoad();
 248                         return null;
 249                     }
 250                 } catch (ExceptionInInitializerError err) {
 251                     // no sufficient permission to initialize provider class
 252                     if (debug != null) {
 253                         debug.println("Error loading provider " + ProviderConfig.this);
 254                         err.printStackTrace();
 255                     }
 256                     disableLoad();
 257                     return null;
 258                 }
 259             }
 260         });
 261     }
 262 
 263     /**
 264      * Perform property expansion of the provider value.
 265      *
 266      * NOTE use of doPrivileged().
 267      */
 268     private static String expand(final String value) {
 269         // shortcut if value does not contain any properties
 270         if (value.contains("${") == false) {
 271             return value;
 272         }
 273         return AccessController.doPrivileged(new PrivilegedAction<String>() {
 274             public String run() {
 275                 try {
 276                     return PropertyExpander.expand(value);
 277                 } catch (GeneralSecurityException e) {
 278                     throw new ProviderException(e);
 279                 }
 280             }
 281         });
 282     }
 283 
 284     // Inner class for loading security providers listed in java.security file
 285     private static final class ProviderLoader {
 286         private final ServiceLoader<Provider> services;
 287 
 288         ProviderLoader() {
 289             // VM should already been booted at this point, if not
 290             // - Only providers in java.base should be loaded, don't use
 291             //   ServiceLoader
 292             // - ClassLoader.getSystemClassLoader() will throw InternalError
 293             services = ServiceLoader.load(java.security.Provider.class,
 294                                           ClassLoader.getSystemClassLoader());
 295         }
 296 
 297         /**
 298          * Loads the provider with the specified class name.
 299          *
 300          * @param name the name of the provider
 301          * @return the Provider, or null if it cannot be found or loaded
 302          * @throws ProviderException all other exceptions are ignored
 303          */
 304         public Provider load(String pn) {
 305             if (debug != null) {
 306                 debug.println("Attempt to load " + pn + " using SL");
 307             }
 308             Iterator<Provider> iter = services.iterator();
 309             while (iter.hasNext()) {
 310                 try {
 311                     Provider p = iter.next();
 312                     String pName = p.getName();
 313                     if (debug != null) {
 314                         debug.println("Found SL Provider named " + pName);
 315                     }
 316                     if (pName.equals(pn)) {
 317                         return p;
 318                     }
 319                 } catch (SecurityException | ServiceConfigurationError |
 320                          InvalidParameterException ex) {
 321                     // if provider loading fail due to security permission,
 322                     // log it and move on to next provider
 323                     if (debug != null) {
 324                         debug.println("Encountered " + ex +
 325                             " while iterating through SL, ignore and move on");
 326                             ex.printStackTrace();
 327                     }
 328                 }
 329             }
 330             // No success with ServiceLoader. Try loading provider the legacy,
 331             // i.e. pre-module, way via reflection
 332             try {
 333                 return legacyLoad(pn);
 334             } catch (ProviderException pe) {
 335                 // pass through
 336                 throw pe;
 337             } catch (Exception ex) {
 338                 // logged and ignored
 339                 if (debug != null) {
 340                     debug.println("Encountered " + ex +
 341                         " during legacy load of " + pn);
 342                         ex.printStackTrace();
 343                 }
 344                 return null;
 345             }
 346         }
 347 
 348         private Provider legacyLoad(String classname) {
 349 
 350             if (debug != null) {
 351                 debug.println("Loading legacy provider: " + classname);
 352             }
 353 
 354             try {
 355                 Class<?> provClass =
 356                     ClassLoader.getSystemClassLoader().loadClass(classname);
 357 
 358                 // only continue if the specified class extends Provider
 359                 if (!Provider.class.isAssignableFrom(provClass)) {
 360                     if (debug != null) {
 361                         debug.println(classname + " is not a provider");
 362                     }
 363                     return null;
 364                 }
 365 
 366                 Provider p = AccessController.doPrivileged
 367                     (new PrivilegedExceptionAction<Provider>() {
 368                     public Provider run() throws Exception {
 369                         return (Provider) provClass.newInstance();
 370                     }
 371                 });
 372                 return p;
 373             } catch (Exception e) {
 374                 Throwable t;
 375                 if (e instanceof InvocationTargetException) {
 376                     t = ((InvocationTargetException)e).getCause();
 377                 } else {
 378                     t = e;
 379                 }
 380                 if (debug != null) {
 381                     debug.println("Error loading legacy provider " + classname);
 382                     t.printStackTrace();
 383                 }
 384                 // provider indicates fatal error, pass through exception
 385                 if (t instanceof ProviderException) {
 386                     throw (ProviderException) t;
 387                 }
 388                 return null;
 389             } catch (ExceptionInInitializerError | NoClassDefFoundError err) {
 390                 // no sufficient permission to access/initialize provider class
 391                 if (debug != null) {
 392                     debug.println("Error loading legacy provider " + classname);
 393                     err.printStackTrace();
 394                 }
 395                 return null;
 396             }
 397         }
 398     }
 399 }