1 /*
   2  * Copyright (c) 1997, 2011, 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 javax.crypto;
  27 
  28 import java.util.*;
  29 import java.util.jar.*;
  30 import java.io.*;
  31 import java.net.URL;
  32 import java.security.*;
  33 
  34 import java.security.Provider.Service;
  35 
  36 import sun.security.jca.*;
  37 import sun.security.jca.GetInstance.Instance;
  38 
  39 /**
  40  * This class instantiates implementations of JCE engine classes from
  41  * providers registered with the java.security.Security object.
  42  *
  43  * @author Jan Luehe
  44  * @author Sharon Liu
  45  * @since 1.4
  46  */
  47 
  48 final class JceSecurity {
  49 
  50     static final SecureRandom RANDOM = new SecureRandom();
  51 
  52     // The defaultPolicy and exemptPolicy will be set up
  53     // in the static initializer.
  54     private static CryptoPermissions defaultPolicy = null;
  55     private static CryptoPermissions exemptPolicy = null;
  56 
  57     // Map<Provider,?> of the providers we already have verified
  58     // value == PROVIDER_VERIFIED is successfully verified
  59     // value is failure cause Exception in error case
  60     private final static Map<Provider, Object> verificationResults =
  61             new IdentityHashMap<>();
  62 
  63     // Map<Provider,?> of the providers currently being verified
  64     private final static Map<Provider, Object> verifyingProviders =
  65             new IdentityHashMap<>();
  66 
  67     // Set the default value. May be changed in the static initializer.
  68     private static boolean isRestricted = true;
  69 
  70     /*
  71      * Don't let anyone instantiate this.
  72      */
  73     private JceSecurity() {
  74     }
  75 
  76     static {
  77         try {
  78             AccessController.doPrivileged(
  79                 new PrivilegedExceptionAction<Object>() {
  80                     public Object run() throws Exception {
  81                         setupJurisdictionPolicies();
  82                         return null;
  83                     }
  84                 });
  85 
  86             isRestricted = defaultPolicy.implies(
  87                 CryptoAllPermission.INSTANCE) ? false : true;
  88         } catch (Exception e) {
  89             throw new SecurityException(
  90                     "Can not initialize cryptographic mechanism", e);
  91         }
  92     }
  93 
  94     static Instance getInstance(String type, Class<?> clazz, String algorithm,
  95             String provider) throws NoSuchAlgorithmException,
  96             NoSuchProviderException {
  97         Service s = GetInstance.getService(type, algorithm, provider);
  98         Exception ve = getVerificationResult(s.getProvider());
  99         if (ve != null) {
 100             String msg = "JCE cannot authenticate the provider " + provider;
 101             throw (NoSuchProviderException)
 102                                 new NoSuchProviderException(msg).initCause(ve);
 103         }
 104         return GetInstance.getInstance(s, clazz);
 105     }
 106 
 107     static Instance getInstance(String type, Class<?> clazz, String algorithm,
 108             Provider provider) throws NoSuchAlgorithmException {
 109         Service s = GetInstance.getService(type, algorithm, provider);
 110         Exception ve = JceSecurity.getVerificationResult(provider);
 111         if (ve != null) {
 112             String msg = "JCE cannot authenticate the provider "
 113                 + provider.getName();
 114             throw new SecurityException(msg, ve);
 115         }
 116         return GetInstance.getInstance(s, clazz);
 117     }
 118 
 119     static Instance getInstance(String type, Class<?> clazz, String algorithm)
 120             throws NoSuchAlgorithmException {
 121         List<Service> services = GetInstance.getServices(type, algorithm);
 122         NoSuchAlgorithmException failure = null;
 123         for (Service s : services) {
 124             if (canUseProvider(s.getProvider()) == false) {
 125                 // allow only signed providers
 126                 continue;
 127             }
 128             try {
 129                 Instance instance = GetInstance.getInstance(s, clazz);
 130                 return instance;
 131             } catch (NoSuchAlgorithmException e) {
 132                 failure = e;
 133             }
 134         }
 135         throw new NoSuchAlgorithmException("Algorithm " + algorithm
 136                 + " not available", failure);
 137     }
 138 
 139     /**
 140      * Verify if the JAR at URL codeBase is a signed exempt application
 141      * JAR file and returns the permissions bundled with the JAR.
 142      *
 143      * @throws Exception on error
 144      */
 145     static CryptoPermissions verifyExemptJar(URL codeBase) throws Exception {
 146         JarVerifier jv = new JarVerifier(codeBase, true);
 147         jv.verify();
 148         return jv.getPermissions();
 149     }
 150 
 151     /**
 152      * Verify if the JAR at URL codeBase is a signed provider JAR file.
 153      *
 154      * @throws Exception on error
 155      */
 156     static void verifyProviderJar(URL codeBase) throws Exception {
 157         // Verify the provider JAR file and all
 158         // supporting JAR files if there are any.
 159         JarVerifier jv = new JarVerifier(codeBase, false);
 160         jv.verify();
 161     }
 162 
 163     private final static Object PROVIDER_VERIFIED = Boolean.TRUE;
 164 
 165     /*
 166      * Verify that the provider JAR files are signed properly, which
 167      * means the signer's certificate can be traced back to a
 168      * JCE trusted CA.
 169      * Return null if ok, failure Exception if verification failed.
 170      */
 171     static synchronized Exception getVerificationResult(Provider p) {
 172         Object o = verificationResults.get(p);
 173         if (o == PROVIDER_VERIFIED) {
 174             return null;
 175         } else if (o != null) {
 176             return (Exception)o;
 177         }
 178         if (verifyingProviders.get(p) != null) {
 179             // this method is static synchronized, must be recursion
 180             // return failure now but do not save the result
 181             return new NoSuchProviderException("Recursion during verification");
 182         }
 183         try {
 184             verifyingProviders.put(p, Boolean.FALSE);
 185             URL providerURL = getCodeBase(p.getClass());
 186             verifyProviderJar(providerURL);
 187             // Verified ok, cache result
 188             verificationResults.put(p, PROVIDER_VERIFIED);
 189             return null;
 190         } catch (Exception e) {
 191             verificationResults.put(p, e);
 192             return e;
 193         } finally {
 194             verifyingProviders.remove(p);
 195         }
 196     }
 197 
 198     // return whether this provider is properly signed and can be used by JCE
 199     static boolean canUseProvider(Provider p) {
 200         return getVerificationResult(p) == null;
 201     }
 202 
 203     // dummy object to represent null
 204     private static final URL NULL_URL;
 205 
 206     static {
 207         try {
 208             NULL_URL = new URL("http://null.sun.com/");
 209         } catch (Exception e) {
 210             throw new RuntimeException(e);
 211         }
 212     }
 213 
 214     // reference to a Map we use as a cache for codebases
 215     private static final Map<Class<?>, URL> codeBaseCacheRef =
 216             new WeakHashMap<>();
 217 
 218     /*
 219      * Retuns the CodeBase for the given class.
 220      */
 221     static URL getCodeBase(final Class<?> clazz) {
 222         URL url = codeBaseCacheRef.get(clazz);
 223         if (url == null) {
 224             url = AccessController.doPrivileged(new PrivilegedAction<URL>() {
 225                 public URL run() {
 226                     ProtectionDomain pd = clazz.getProtectionDomain();
 227                     if (pd != null) {
 228                         CodeSource cs = pd.getCodeSource();
 229                         if (cs != null) {
 230                             return cs.getLocation();
 231                         }
 232                     }
 233                     return NULL_URL;
 234                 }
 235             });
 236             codeBaseCacheRef.put(clazz, url);
 237         }
 238         return (url == NULL_URL) ? null : url;
 239     }
 240 
 241     private static void setupJurisdictionPolicies() throws Exception {
 242         String javaHomeDir = System.getProperty("java.home");
 243         String sep = File.separator;
 244         String pathToPolicyJar = javaHomeDir + sep + "lib" + sep +
 245             "security" + sep;
 246 
 247         File exportJar = new File(pathToPolicyJar, "US_export_policy.jar");
 248         File importJar = new File(pathToPolicyJar, "local_policy.jar");
 249         URL jceCipherURL = ClassLoader.getSystemResource
 250                 ("javax/crypto/Cipher.class");
 251 
 252         if ((jceCipherURL == null) ||
 253                 !exportJar.exists() || !importJar.exists()) {
 254             throw new SecurityException
 255                                 ("Cannot locate policy or framework files!");
 256         }
 257 
 258         // Read jurisdiction policies.
 259         CryptoPermissions defaultExport = new CryptoPermissions();
 260         CryptoPermissions exemptExport = new CryptoPermissions();
 261         loadPolicies(exportJar, defaultExport, exemptExport);
 262 
 263         CryptoPermissions defaultImport = new CryptoPermissions();
 264         CryptoPermissions exemptImport = new CryptoPermissions();
 265         loadPolicies(importJar, defaultImport, exemptImport);
 266 
 267         // Merge the export and import policies for default applications.
 268         if (defaultExport.isEmpty() || defaultImport.isEmpty()) {
 269             throw new SecurityException("Missing mandatory jurisdiction " +
 270                                         "policy files");
 271         }
 272         defaultPolicy = defaultExport.getMinimum(defaultImport);
 273 
 274         // Merge the export and import policies for exempt applications.
 275         if (exemptExport.isEmpty())  {
 276             exemptPolicy = exemptImport.isEmpty() ? null : exemptImport;
 277         } else {
 278             exemptPolicy = exemptExport.getMinimum(exemptImport);
 279         }
 280     }
 281 
 282     /**
 283      * Load the policies from the specified file. Also checks that the
 284      * policies are correctly signed.
 285      */
 286     private static void loadPolicies(File jarPathName,
 287                                      CryptoPermissions defaultPolicy,
 288                                      CryptoPermissions exemptPolicy)
 289         throws Exception {
 290 
 291         JarFile jf = new JarFile(jarPathName);
 292 
 293         Enumeration<JarEntry> entries = jf.entries();
 294         while (entries.hasMoreElements()) {
 295             JarEntry je = entries.nextElement();
 296             InputStream is = null;
 297             try {
 298                 if (je.getName().startsWith("default_")) {
 299                     is = jf.getInputStream(je);
 300                     defaultPolicy.load(is);
 301                 } else if (je.getName().startsWith("exempt_")) {
 302                     is = jf.getInputStream(je);
 303                     exemptPolicy.load(is);
 304                 } else {
 305                     continue;
 306                 }
 307             } finally {
 308                 if (is != null) {
 309                     is.close();
 310                 }
 311             }
 312 
 313             // Enforce the signer restraint, i.e. signer of JCE framework
 314             // jar should also be the signer of the two jurisdiction policy
 315             // jar files.
 316             JarVerifier.verifyPolicySigned(je.getCertificates());
 317         }
 318         // Close and nullify the JarFile reference to help GC.
 319         jf.close();
 320         jf = null;
 321     }
 322 
 323     static CryptoPermissions getDefaultPolicy() {
 324         return defaultPolicy;
 325     }
 326 
 327     static CryptoPermissions getExemptPolicy() {
 328         return exemptPolicy;
 329     }
 330 
 331     static boolean isRestricted() {
 332         return isRestricted;
 333     }
 334 }