jdk/src/java.base/share/classes/javax/crypto/JceSecurity.java
changeset 42365 5e640c2994d6
parent 42364 c5a725b3d358
child 42366 b1483ec50db1
equal deleted inserted replaced
42364:c5a725b3d358 42365:5e640c2994d6
     1 /*
       
     2  * Copyright (c) 1997, 2016, 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.nio.file.*;
       
    33 import java.security.*;
       
    34 
       
    35 import java.security.Provider.Service;
       
    36 
       
    37 import sun.security.jca.*;
       
    38 import sun.security.jca.GetInstance.Instance;
       
    39 
       
    40 /**
       
    41  * This class instantiates implementations of JCE engine classes from
       
    42  * providers registered with the java.security.Security object.
       
    43  *
       
    44  * @author Jan Luehe
       
    45  * @author Sharon Liu
       
    46  * @since 1.4
       
    47  */
       
    48 
       
    49 final class JceSecurity {
       
    50 
       
    51     static final SecureRandom RANDOM = new SecureRandom();
       
    52 
       
    53     // The defaultPolicy and exemptPolicy will be set up
       
    54     // in the static initializer.
       
    55     private static CryptoPermissions defaultPolicy = null;
       
    56     private static CryptoPermissions exemptPolicy = null;
       
    57 
       
    58     // Map<Provider,?> of the providers we already have verified
       
    59     // value == PROVIDER_VERIFIED is successfully verified
       
    60     // value is failure cause Exception in error case
       
    61     private static final Map<Provider, Object> verificationResults =
       
    62             new IdentityHashMap<>();
       
    63 
       
    64     // Map<Provider,?> of the providers currently being verified
       
    65     private static final Map<Provider, Object> verifyingProviders =
       
    66             new IdentityHashMap<>();
       
    67 
       
    68     private static final boolean isRestricted;
       
    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<> () {
       
    80                     @Override
       
    81                     public Void run() throws Exception {
       
    82                         setupJurisdictionPolicies();
       
    83                         return null;
       
    84                     }
       
    85                 }
       
    86             );
       
    87 
       
    88             isRestricted = defaultPolicy.implies(
       
    89                 CryptoAllPermission.INSTANCE) ? false : true;
       
    90         } catch (Exception e) {
       
    91             throw new SecurityException(
       
    92                     "Can not initialize cryptographic mechanism", e);
       
    93         }
       
    94     }
       
    95 
       
    96     static Instance getInstance(String type, Class<?> clazz, String algorithm,
       
    97             String provider) throws NoSuchAlgorithmException,
       
    98             NoSuchProviderException {
       
    99         Service s = GetInstance.getService(type, algorithm, provider);
       
   100         Exception ve = getVerificationResult(s.getProvider());
       
   101         if (ve != null) {
       
   102             String msg = "JCE cannot authenticate the provider " + provider;
       
   103             throw (NoSuchProviderException)
       
   104                                 new NoSuchProviderException(msg).initCause(ve);
       
   105         }
       
   106         return GetInstance.getInstance(s, clazz);
       
   107     }
       
   108 
       
   109     static Instance getInstance(String type, Class<?> clazz, String algorithm,
       
   110             Provider provider) throws NoSuchAlgorithmException {
       
   111         Service s = GetInstance.getService(type, algorithm, provider);
       
   112         Exception ve = JceSecurity.getVerificationResult(provider);
       
   113         if (ve != null) {
       
   114             String msg = "JCE cannot authenticate the provider "
       
   115                 + provider.getName();
       
   116             throw new SecurityException(msg, ve);
       
   117         }
       
   118         return GetInstance.getInstance(s, clazz);
       
   119     }
       
   120 
       
   121     static Instance getInstance(String type, Class<?> clazz, String algorithm)
       
   122             throws NoSuchAlgorithmException {
       
   123         List<Service> services = GetInstance.getServices(type, algorithm);
       
   124         NoSuchAlgorithmException failure = null;
       
   125         for (Service s : services) {
       
   126             if (canUseProvider(s.getProvider()) == false) {
       
   127                 // allow only signed providers
       
   128                 continue;
       
   129             }
       
   130             try {
       
   131                 Instance instance = GetInstance.getInstance(s, clazz);
       
   132                 return instance;
       
   133             } catch (NoSuchAlgorithmException e) {
       
   134                 failure = e;
       
   135             }
       
   136         }
       
   137         throw new NoSuchAlgorithmException("Algorithm " + algorithm
       
   138                 + " not available", failure);
       
   139     }
       
   140 
       
   141     /**
       
   142      * Verify if the JAR at URL codeBase is a signed exempt application
       
   143      * JAR file and returns the permissions bundled with the JAR.
       
   144      *
       
   145      * @throws Exception on error
       
   146      */
       
   147     static CryptoPermissions verifyExemptJar(URL codeBase) throws Exception {
       
   148         ProviderVerifier pv = new ProviderVerifier(codeBase, true);
       
   149         pv.verify();
       
   150         return pv.getPermissions();
       
   151     }
       
   152 
       
   153     /**
       
   154      * Verify if the JAR at URL codeBase is a signed provider JAR file.
       
   155      *
       
   156      * @throws Exception on error
       
   157      */
       
   158     static void verifyProvider(URL codeBase, Provider p) throws Exception {
       
   159         // Verify the provider JAR file and all
       
   160         // supporting JAR files if there are any.
       
   161         ProviderVerifier pv = new ProviderVerifier(codeBase, p, false);
       
   162         pv.verify();
       
   163     }
       
   164 
       
   165     private static final Object PROVIDER_VERIFIED = Boolean.TRUE;
       
   166 
       
   167     /*
       
   168      * Verify that the provider JAR files are signed properly, which
       
   169      * means the signer's certificate can be traced back to a
       
   170      * JCE trusted CA.
       
   171      * Return null if ok, failure Exception if verification failed.
       
   172      */
       
   173     static synchronized Exception getVerificationResult(Provider p) {
       
   174         Object o = verificationResults.get(p);
       
   175         if (o == PROVIDER_VERIFIED) {
       
   176             return null;
       
   177         } else if (o != null) {
       
   178             return (Exception)o;
       
   179         }
       
   180         if (verifyingProviders.get(p) != null) {
       
   181             // this method is static synchronized, must be recursion
       
   182             // return failure now but do not save the result
       
   183             return new NoSuchProviderException("Recursion during verification");
       
   184         }
       
   185         try {
       
   186             verifyingProviders.put(p, Boolean.FALSE);
       
   187             URL providerURL = getCodeBase(p.getClass());
       
   188             verifyProvider(providerURL, p);
       
   189             // Verified ok, cache result
       
   190             verificationResults.put(p, PROVIDER_VERIFIED);
       
   191             return null;
       
   192         } catch (Exception e) {
       
   193             verificationResults.put(p, e);
       
   194             return e;
       
   195         } finally {
       
   196             verifyingProviders.remove(p);
       
   197         }
       
   198     }
       
   199 
       
   200     // return whether this provider is properly signed and can be used by JCE
       
   201     static boolean canUseProvider(Provider p) {
       
   202         return getVerificationResult(p) == null;
       
   203     }
       
   204 
       
   205     // dummy object to represent null
       
   206     private static final URL NULL_URL;
       
   207 
       
   208     static {
       
   209         try {
       
   210             NULL_URL = new URL("http://null.oracle.com/");
       
   211         } catch (Exception e) {
       
   212             throw new RuntimeException(e);
       
   213         }
       
   214     }
       
   215 
       
   216     // reference to a Map we use as a cache for codebases
       
   217     private static final Map<Class<?>, URL> codeBaseCacheRef =
       
   218             new WeakHashMap<>();
       
   219 
       
   220     /*
       
   221      * Returns the CodeBase for the given class.
       
   222      */
       
   223     static URL getCodeBase(final Class<?> clazz) {
       
   224         synchronized (codeBaseCacheRef) {
       
   225             URL url = codeBaseCacheRef.get(clazz);
       
   226             if (url == null) {
       
   227                 url = AccessController.doPrivileged(
       
   228                     new PrivilegedAction<>() {
       
   229                         @Override
       
   230                         public URL run() {
       
   231                             ProtectionDomain pd = clazz.getProtectionDomain();
       
   232                             if (pd != null) {
       
   233                                 CodeSource cs = pd.getCodeSource();
       
   234                                 if (cs != null) {
       
   235                                     return cs.getLocation();
       
   236                                 }
       
   237                             }
       
   238                             return NULL_URL;
       
   239                         }
       
   240                     });
       
   241                 codeBaseCacheRef.put(clazz, url);
       
   242             }
       
   243             return (url == NULL_URL) ? null : url;
       
   244         }
       
   245     }
       
   246 
       
   247     // This is called from within an doPrivileged block.
       
   248     private static void setupJurisdictionPolicies() throws Exception {
       
   249 
       
   250         // Sanity check the crypto.policy Security property.  Single
       
   251         // directory entry, no pseudo-directories (".", "..", leading/trailing
       
   252         // path separators). normalize()/getParent() will help later.
       
   253         String cryptoPolicyProperty = Security.getProperty("crypto.policy");
       
   254         Path cpPath = Paths.get(cryptoPolicyProperty);
       
   255 
       
   256         if ((cryptoPolicyProperty == null) ||
       
   257                 (cpPath.getNameCount() != 1) ||
       
   258                 (cpPath.compareTo(cpPath.getFileName()) != 0)) {
       
   259             throw new SecurityException(
       
   260                 "Invalid policy directory name format: " +
       
   261                 cryptoPolicyProperty);
       
   262         }
       
   263 
       
   264         // Prepend java.home to get the full path.  normalize() in
       
   265         // case an extra "." or ".." snuck in somehow.
       
   266         String javaHomeProperty = System.getProperty("java.home");
       
   267         Path javaHomePolicyPath = Paths.get(javaHomeProperty, "conf",
       
   268                 "security", "policy").normalize();
       
   269         Path cryptoPolicyPath = Paths.get(javaHomeProperty, "conf", "security",
       
   270                 "policy", cryptoPolicyProperty).normalize();
       
   271 
       
   272         if (cryptoPolicyPath.getParent().compareTo(javaHomePolicyPath) != 0) {
       
   273             throw new SecurityException(
       
   274                 "Invalid cryptographic jurisdiction policy directory path: " +
       
   275                 cryptoPolicyProperty);
       
   276         }
       
   277 
       
   278         if (!Files.isDirectory(cryptoPolicyPath)
       
   279                 || !Files.isReadable(cryptoPolicyPath)) {
       
   280             throw new SecurityException(
       
   281                 "Can't read cryptographic policy directory: " +
       
   282                 cryptoPolicyProperty);
       
   283         }
       
   284 
       
   285         try (DirectoryStream<Path> stream = Files.newDirectoryStream(
       
   286                 cryptoPolicyPath, "{default,exempt}_*.policy")) {
       
   287             for (Path entry : stream) {
       
   288                 try (InputStream is = new BufferedInputStream(
       
   289                         Files.newInputStream(entry))) {
       
   290                     String filename = entry.getFileName().toString();
       
   291 
       
   292                     CryptoPermissions tmpPerms = new CryptoPermissions();
       
   293                     tmpPerms.load(is);
       
   294 
       
   295                     if (filename.startsWith("default_")) {
       
   296                         // Did we find a default perms?
       
   297                         defaultPolicy = ((defaultPolicy == null) ? tmpPerms :
       
   298                                 defaultPolicy.getMinimum(tmpPerms));
       
   299                     } else if (filename.startsWith("exempt_")) {
       
   300                         // Did we find a exempt perms?
       
   301                         exemptPolicy = ((exemptPolicy == null) ? tmpPerms :
       
   302                                 exemptPolicy.getMinimum(tmpPerms));
       
   303                     } else {
       
   304                         // This should never happen.  newDirectoryStream
       
   305                         // should only throw return "{default,exempt}_*.policy"
       
   306                         throw new SecurityException(
       
   307                             "Unexpected jurisdiction policy files in : " +
       
   308                             cryptoPolicyProperty);
       
   309                     }
       
   310                 } catch (Exception e) {
       
   311                     throw new SecurityException(
       
   312                         "Couldn't parse jurisdiction policy files in: " +
       
   313                         cryptoPolicyProperty);
       
   314                 }
       
   315             }
       
   316         } catch (DirectoryIteratorException ex) {
       
   317             // I/O error encountered during the iteration,
       
   318             // the cause is an IOException
       
   319             throw new SecurityException(
       
   320                 "Couldn't iterate through the jurisdiction policy files: " +
       
   321                 cryptoPolicyProperty);
       
   322         }
       
   323 
       
   324         // Must have a default policy
       
   325         if ((defaultPolicy == null) || defaultPolicy.isEmpty()) {
       
   326             throw new SecurityException(
       
   327                 "Missing mandatory jurisdiction policy files: " +
       
   328                 cryptoPolicyProperty);
       
   329         }
       
   330 
       
   331         // If there was an empty exempt policy file, ignore it.
       
   332         if ((exemptPolicy != null) && exemptPolicy.isEmpty()) {
       
   333             exemptPolicy = null;
       
   334         }
       
   335     }
       
   336 
       
   337     static CryptoPermissions getDefaultPolicy() {
       
   338         return defaultPolicy;
       
   339     }
       
   340 
       
   341     static CryptoPermissions getExemptPolicy() {
       
   342         return exemptPolicy;
       
   343     }
       
   344 
       
   345     static boolean isRestricted() {
       
   346         return isRestricted;
       
   347     }
       
   348 }