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