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