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