| /* | |
| * Copyright (c) 1997, 2024, Oracle and/or its affiliates. All rights reserved. | |
| * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. | |
| * | |
| * This code is free software; you can redistribute it and/or modify it | |
| * under the terms of the GNU General Public License version 2 only, as | |
| * published by the Free Software Foundation. Oracle designates this | |
| * particular file as subject to the "Classpath" exception as provided | |
| * by Oracle in the LICENSE file that accompanied this code. | |
| * | |
| * This code is distributed in the hope that it will be useful, but WITHOUT | |
| * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
| * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | |
| * version 2 for more details (a copy is included in the LICENSE file that | |
| * accompanied this code). | |
| * | |
| * You should have received a copy of the GNU General Public License version | |
| * 2 along with this work; if not, write to the Free Software Foundation, | |
| * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. | |
| * | |
| * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA | |
| * or visit www.oracle.com if you need additional information or have any | |
| * questions. | |
| */ | |
| /* | |
| * README README README README README README README README README | |
| * | |
| * This file is the template for generating the JceSecurity.java source | |
| * file. | |
| * | |
| * In the current jdk builds, this file is first preprocessed to replace | |
| * @@JCE_DEFAULT_POLICY@ [sic] with "limited" or "unlimited" which is | |
| * determined by the $(UNLIMTED_CRYPTO) make variable. This variable is | |
| * set by top-level configure script, using either | |
| * --disable-unlimited-crypto or --enable-unlimited-crypto [default]. | |
| * | |
| * Since this file is a generated source, incremental changes to | |
| * this file require regenerating the source. Compilation options: | |
| * | |
| * (fewer dependencies/"faster" ones first) | |
| * | |
| * 1. make JDK_FILTER=javax/crypto java.base-gensrc-only java.base-java-only | |
| * 2. make java.base-gensrc-only java.base-java-only | |
| * 3. make java.base-gensrc-only java.base-only | |
| * 4. make java.base-only | |
| * 5. make | |
| */ | |
| package javax.crypto; | |
| import java.util.*; | |
| import java.util.concurrent.ConcurrentHashMap; | |
| import java.util.function.Function; | |
| import java.io.*; | |
| import java.lang.ref.ReferenceQueue; | |
| import java.lang.ref.WeakReference; | |
| import java.net.URL; | |
| import java.nio.file.*; | |
| import java.security.*; | |
| import java.security.Provider.Service; | |
| import jdk.internal.util.StaticProperty; | |
| import sun.security.jca.*; | |
| import sun.security.jca.GetInstance.Instance; | |
| import sun.security.util.Debug; | |
| /** | |
| * This class instantiates implementations of JCE engine classes from | |
| * providers registered with the java.security.Security object. | |
| * | |
| * @author Jan Luehe | |
| * @author Sharon Liu | |
| * @since 1.4 | |
| */ | |
| final class JceSecurity { | |
| private static final Debug debug = Debug.getInstance("jca"); | |
| // The defaultPolicy and exemptPolicy will be set up | |
| // in the static initializer. | |
| private static CryptoPermissions defaultPolicy = null; | |
| private static CryptoPermissions exemptPolicy = null; | |
| // Map of the providers we already have verified. | |
| // If verified ok, value == PROVIDER_VERIFIED, otherwise | |
| // the cause of verification failure is stored as value. | |
| private static final Map<WeakIdentityWrapper, Object> | |
| verificationResults = new ConcurrentHashMap<>(); | |
| // Map<Provider,?> of the providers currently being verified | |
| private static final Map<Provider, Object> verifyingProviders = | |
| new IdentityHashMap<>(); | |
| // weak references queued by GC | |
| private static final ReferenceQueue<Object> queue = new ReferenceQueue<>(); | |
| private static final boolean isRestricted; | |
| /* | |
| * Don't let anyone instantiate this. | |
| */ | |
| private JceSecurity() { | |
| } | |
| static { | |
| try { | |
| setupJurisdictionPolicies(); | |
| isRestricted = defaultPolicy.implies( | |
| CryptoAllPermission.INSTANCE) ? false : true; | |
| } catch (Exception e) { | |
| throw new SecurityException( | |
| "Can not initialize cryptographic mechanism", e); | |
| } | |
| } | |
| static Instance getInstance(String type, Class<?> clazz, String algorithm, | |
| String provider) throws NoSuchAlgorithmException, | |
| NoSuchProviderException { | |
| Service s = GetInstance.getService(type, algorithm, provider); | |
| Exception ve = getVerificationResult(s.getProvider()); | |
| if (ve != null) { | |
| String msg = "JCE cannot authenticate the provider " + provider; | |
| throw (NoSuchProviderException) | |
| new NoSuchProviderException(msg).initCause(ve); | |
| } | |
| return GetInstance.getInstance(s, clazz); | |
| } | |
| static Instance getInstance(String type, Class<?> clazz, String algorithm, | |
| Provider provider) throws NoSuchAlgorithmException { | |
| Service s = GetInstance.getService(type, algorithm, provider); | |
| Exception ve = JceSecurity.getVerificationResult(provider); | |
| if (ve != null) { | |
| String msg = "JCE cannot authenticate the provider " | |
| + provider.getName(); | |
| throw new SecurityException(msg, ve); | |
| } | |
| return GetInstance.getInstance(s, clazz); | |
| } | |
| static Instance getInstance(String type, Class<?> clazz, String algorithm) | |
| throws NoSuchAlgorithmException { | |
| Iterator<Service> t = GetInstance.getServices(type, algorithm); | |
| NoSuchAlgorithmException failure = null; | |
| while (t.hasNext()) { | |
| Service s = t.next(); | |
| if (canUseProvider(s.getProvider()) == false) { | |
| // allow only signed providers | |
| continue; | |
| } | |
| try { | |
| Instance instance = GetInstance.getInstance(s, clazz); | |
| return instance; | |
| } catch (NoSuchAlgorithmException e) { | |
| failure = e; | |
| } | |
| } | |
| throw new NoSuchAlgorithmException("Algorithm " + algorithm | |
| + " not available", failure); | |
| } | |
| /** | |
| * Verify if the JAR at URL codeBase is a signed exempt application | |
| * JAR file and returns the permissions bundled with the JAR. | |
| * | |
| * @throws Exception on error | |
| */ | |
| static CryptoPermissions verifyExemptJar(URL codeBase) throws Exception { | |
| ProviderVerifier pv = new ProviderVerifier(codeBase, true); | |
| pv.verify(); | |
| return pv.getPermissions(); | |
| } | |
| /** | |
| * Verify if the JAR at URL codeBase is a signed provider JAR file. | |
| * | |
| * @throws Exception on error | |
| */ | |
| static void verifyProvider(URL codeBase, Provider p) throws Exception { | |
| // Verify the provider JAR file and all | |
| // supporting JAR files if there are any. | |
| ProviderVerifier pv = new ProviderVerifier(codeBase, p, false); | |
| pv.verify(); | |
| } | |
| private static final Object PROVIDER_VERIFIED = Boolean.TRUE; | |
| /* | |
| * Verify that the provider JAR files are signed properly, which | |
| * means the signer's certificate can be traced back to a | |
| * JCE trusted CA. | |
| * Return null if ok, failure Exception if verification failed. | |
| */ | |
| static Exception getVerificationResult(Provider p) { | |
| expungeStaleWrappers(); | |
| WeakIdentityWrapper pKey = new WeakIdentityWrapper(p, queue); | |
| try { | |
| Object o = verificationResults.computeIfAbsent(pKey, new Function<>() { | |
| public Object apply(WeakIdentityWrapper key) { | |
| // no mapping found | |
| if (verifyingProviders.get(p) != null) { | |
| // recursion; return failure now | |
| throw new IllegalStateException(); | |
| } | |
| Object result; | |
| try { | |
| verifyingProviders.put(p, Boolean.FALSE); | |
| URL providerURL = getCodeBase(p.getClass()); | |
| verifyProvider(providerURL, p); | |
| result = PROVIDER_VERIFIED; | |
| } catch (Exception e) { | |
| result = e; | |
| } finally { | |
| verifyingProviders.remove(p); | |
| } | |
| if (debug != null) { | |
| debug.println("Provider " + p.getName() + | |
| " verification result: " + result); | |
| } | |
| return result; | |
| } | |
| }); | |
| return (o == PROVIDER_VERIFIED? null : (Exception) o); | |
| } catch (IllegalStateException ise) { | |
| // recursive update detected | |
| return new NoSuchProviderException | |
| ("Recursion during verification"); | |
| } | |
| } | |
| /** | |
| * Removes weakly reachable keys from history. | |
| */ | |
| static void expungeStaleWrappers() { | |
| WeakIdentityWrapper key; | |
| while ((key = (WeakIdentityWrapper) queue.poll()) != null) { | |
| verificationResults.remove(key); | |
| } | |
| } | |
| // return whether this provider is properly signed and can be used by JCE | |
| static boolean canUseProvider(Provider p) { | |
| Exception e = getVerificationResult(p); | |
| if (debug != null && e != null) { | |
| debug.println("Provider verification result: " + e); | |
| } | |
| return e == null; | |
| } | |
| // dummy object to represent null | |
| private static final URL NULL_URL; | |
| static { | |
| try { | |
| @SuppressWarnings("deprecation") | |
| var _unused = NULL_URL = new URL("http://null.oracle.com/"); | |
| } catch (Exception e) { | |
| throw new RuntimeException(e); | |
| } | |
| } | |
| // reference to a Map we use as a cache for codebases | |
| private static final Map<Class<?>, URL> codeBaseCacheRef = | |
| new WeakHashMap<>(); | |
| /* | |
| * Returns the CodeBase for the given class. | |
| */ | |
| static URL getCodeBase(final Class<?> clazz) { | |
| synchronized (codeBaseCacheRef) { | |
| URL url = codeBaseCacheRef.get(clazz); | |
| if (url == null) { | |
| url = NULL_URL; | |
| ProtectionDomain pd = clazz.getProtectionDomain(); | |
| if (pd != null) { | |
| CodeSource cs = pd.getCodeSource(); | |
| if (cs != null) { | |
| url = cs.getLocation(); | |
| } | |
| } | |
| codeBaseCacheRef.put(clazz, url); | |
| } | |
| return (url == NULL_URL) ? null : url; | |
| } | |
| } | |
| private static void setupJurisdictionPolicies() throws Exception { | |
| // Sanity check the crypto.policy Security property. Single | |
| // directory entry, no pseudo-directories (".", "..", leading/trailing | |
| // path separators). normalize()/getParent() will help later. | |
| String cryptoPolicyProperty = Security.getProperty("crypto.policy"); | |
| /* | |
| * In case no property is present, rather than fail catastrophically, | |
| * we at least try for a "sane" value, which is what we were | |
| * built with. We first preprocess this file to plug in that | |
| * value, then compile the result gensrc. | |
| * | |
| * Log the warning first. | |
| */ | |
| if (cryptoPolicyProperty == null) { | |
| cryptoPolicyProperty = "@@JCE_DEFAULT_POLICY@@"; | |
| if (debug != null) { | |
| debug.println( | |
| "Security Property 'crypto.policy' not found: " | |
| + "using '" + cryptoPolicyProperty + "' as fallback"); | |
| } | |
| } | |
| Path cpPath = Paths.get(cryptoPolicyProperty); | |
| if ((cpPath.getNameCount() != 1) || | |
| (cpPath.compareTo(cpPath.getFileName()) != 0)) { | |
| throw new SecurityException( | |
| "Invalid policy directory name format: " + | |
| cryptoPolicyProperty); | |
| } | |
| // Prepend java.home to get the full path. normalize() in | |
| // case an extra "." or ".." snuck in somehow. | |
| String javaHomeProperty = StaticProperty.javaHome(); | |
| Path javaHomePolicyPath = Paths.get(javaHomeProperty, "conf", | |
| "security", "policy").normalize(); | |
| Path cryptoPolicyPath = Paths.get(javaHomeProperty, "conf", "security", | |
| "policy", cryptoPolicyProperty).normalize(); | |
| if (cryptoPolicyPath.getParent().compareTo(javaHomePolicyPath) != 0) { | |
| throw new SecurityException( | |
| "Invalid cryptographic jurisdiction policy directory path: " + | |
| cryptoPolicyProperty); | |
| } | |
| if (!Files.isDirectory(cryptoPolicyPath) | |
| || !Files.isReadable(cryptoPolicyPath)) { | |
| throw new SecurityException( | |
| "Can't read cryptographic policy directory: " + | |
| cryptoPolicyProperty); | |
| } | |
| try (DirectoryStream<Path> stream = Files.newDirectoryStream( | |
| cryptoPolicyPath, "{default,exempt}_*.policy")) { | |
| for (Path entry : stream) { | |
| try (InputStream is = new BufferedInputStream( | |
| Files.newInputStream(entry))) { | |
| String filename = entry.getFileName().toString(); | |
| CryptoPermissions tmpPerms = new CryptoPermissions(); | |
| tmpPerms.load(is); | |
| if (filename.startsWith("default_")) { | |
| // Did we find a default perms? | |
| defaultPolicy = ((defaultPolicy == null) ? tmpPerms : | |
| defaultPolicy.getMinimum(tmpPerms)); | |
| } else if (filename.startsWith("exempt_")) { | |
| // Did we find a exempt perms? | |
| exemptPolicy = ((exemptPolicy == null) ? tmpPerms : | |
| exemptPolicy.getMinimum(tmpPerms)); | |
| } else { | |
| // This should never happen. newDirectoryStream | |
| // should only throw return "{default,exempt}_*.policy" | |
| throw new SecurityException( | |
| "Unexpected jurisdiction policy files in : " + | |
| cryptoPolicyProperty); | |
| } | |
| } catch (Exception e) { | |
| throw new SecurityException( | |
| "Couldn't parse jurisdiction policy files in: " + | |
| cryptoPolicyProperty); | |
| } | |
| } | |
| } catch (DirectoryIteratorException ex) { | |
| // I/O error encountered during the iteration, | |
| // the cause is an IOException | |
| throw new SecurityException( | |
| "Couldn't iterate through the jurisdiction policy files: " + | |
| cryptoPolicyProperty); | |
| } | |
| // Must have a default policy | |
| if ((defaultPolicy == null) || defaultPolicy.isEmpty()) { | |
| throw new SecurityException( | |
| "Missing mandatory jurisdiction policy files: " + | |
| cryptoPolicyProperty); | |
| } | |
| // If there was an empty exempt policy file, ignore it. | |
| if ((exemptPolicy != null) && exemptPolicy.isEmpty()) { | |
| exemptPolicy = null; | |
| } | |
| } | |
| static CryptoPermissions getDefaultPolicy() { | |
| return defaultPolicy; | |
| } | |
| static CryptoPermissions getExemptPolicy() { | |
| return exemptPolicy; | |
| } | |
| static boolean isRestricted() { | |
| return isRestricted; | |
| } | |
| private static final class WeakIdentityWrapper extends WeakReference<Object> { | |
| final int hash; | |
| WeakIdentityWrapper(Provider obj, ReferenceQueue<Object> queue) { | |
| super(obj, queue); | |
| hash = System.identityHashCode(obj); | |
| } | |
| @Override | |
| public boolean equals(Object o) { | |
| if (this == o) { | |
| return true; | |
| } | |
| return o instanceof WeakIdentityWrapper w && get() == w.get(); | |
| } | |
| @Override | |
| public int hashCode() { | |
| return hash; | |
| } | |
| } | |
| } |