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 import sun.security.util.Debug; 40 41 /** 42 * This class instantiates implementations of JCE engine classes from 43 * providers registered with the java.security.Security object. 44 * 45 * @author Jan Luehe 46 * @author Sharon Liu 47 * @since 1.4 48 */ 49 50 final class JceSecurity { 51 52 static final SecureRandom RANDOM = new SecureRandom(); 53 54 // The defaultPolicy and exemptPolicy will be set up 55 // in the static initializer. 56 private static CryptoPermissions defaultPolicy = null; 57 private static CryptoPermissions exemptPolicy = null; 58 59 // Map<Provider,?> of the providers we already have verified 60 // value == PROVIDER_VERIFIED is successfully verified 61 // value is failure cause Exception in error case 62 private final static Map<Provider, Object> verificationResults = 63 new IdentityHashMap<>(); 64 65 // Map<Provider,?> of the providers currently being verified 66 private final static Map<Provider, Object> verifyingProviders = 67 new IdentityHashMap<>(); 68 69 private static final boolean isRestricted; 70 71 private static final Debug debug = 72 Debug.getInstance("jca", "Cipher"); 73 74 /* 75 * Don't let anyone instantiate this. 76 */ 77 private JceSecurity() { 78 } 79 80 static { 81 try { 82 AccessController.doPrivileged( 83 new PrivilegedExceptionAction<Object>() { 84 public Object run() throws Exception { 85 setupJurisdictionPolicies(); 86 return null; 87 } 88 }); 89 90 isRestricted = defaultPolicy.implies( 91 CryptoAllPermission.INSTANCE) ? false : true; 92 } catch (Exception e) { 93 throw new SecurityException( 94 "Can not initialize cryptographic mechanism", e); 95 } 96 } 97 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 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 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 */ 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 */ 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 */ 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 203 static boolean canUseProvider(Provider p) { 204 return getVerificationResult(p) == null; 205 } 206 207 // dummy object to represent null 208 private static final URL NULL_URL; 209 210 static { 211 try { 212 NULL_URL = new URL("http://null.oracle.com/"); 213 } catch (Exception e) { 214 throw new RuntimeException(e); 215 } 216 } 217 218 // reference to a Map we use as a cache for codebases 219 private static final Map<Class<?>, URL> codeBaseCacheRef = 220 new WeakHashMap<>(); 221 222 /* 223 * Returns the CodeBase for the given class. 224 */ 225 static URL getCodeBase(final Class<?> clazz) { 226 synchronized (codeBaseCacheRef) { 227 URL url = codeBaseCacheRef.get(clazz); 228 if (url == null) { 229 url = AccessController.doPrivileged(new PrivilegedAction<URL>() { 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 /* 248 * This is called from within an doPrivileged block. 249 * 250 * Following logic is used to decide what policy files are selected. 251 * 252 * If the new Security property (crypto.policy) is set in the 253 * java.security file, or has been set dynamically using the 254 * Security.setProperty() call before the JCE framework has 255 * been initialized, that setting will be used. 256 * Remember - this property is not defined by default. A conscious 257 * user edit or an application call is required. 258 * 259 * Otherwise, if user has policy jar files installed in the legacy 260 * jre/lib/security/ directory, the JDK will honor whatever 261 * setting is set by those policy files. (legacy/current behavior) 262 * 263 * If none of the above 2 conditions are met, the JDK will default 264 * to using the limited crypto policy files found in the 265 * jre/lib/security/policy/limited/ directory 266 */ 267 private static void setupJurisdictionPolicies() throws Exception { 268 // Sanity check the crypto.policy Security property. Single 269 // directory entry, no pseudo-directories (".", "..", leading/trailing 270 // path separators). normalize()/getParent() will help later. 271 String javaHomeProperty = System.getProperty("java.home"); 272 String cryptoPolicyProperty = Security.getProperty("crypto.policy"); 273 Path cpPath = (cryptoPolicyProperty == null) ? null : 274 Paths.get(cryptoPolicyProperty); 275 276 if ((cpPath != null) && ((cpPath.getNameCount() != 1) || 277 (cpPath.compareTo(cpPath.getFileName())) != 0)) { 278 throw new SecurityException( 279 "Invalid policy directory name format: " + 280 cryptoPolicyProperty); 281 } 282 283 if (cpPath == null) { 284 // Security property is not set, use default path 285 cpPath = Paths.get(javaHomeProperty, "lib", "security"); 286 } else { 287 // populate with java.home 288 cpPath = Paths.get(javaHomeProperty, "lib", "security", 289 "policy", cryptoPolicyProperty); 290 } 291 292 if (debug != null) { 293 debug.println("crypto policy directory: " + cpPath); 294 } 295 296 File exportJar = new File(cpPath.toFile(),"US_export_policy.jar"); 297 File importJar = new File(cpPath.toFile(),"local_policy.jar"); 298 299 if (cryptoPolicyProperty == null && (!exportJar.exists() || 300 !importJar.exists())) { 301 // Compatibility set up. If crypto.policy is not defined. 302 // check to see if legacy jars exist in lib directory. If 303 // they don't exist, we default to limited policy mode. 304 cpPath = Paths.get( 305 javaHomeProperty, "lib", "security", "policy", "limited"); 306 // point to the new jar files in limited directory 307 exportJar = new File(cpPath.toFile(),"US_export_policy.jar"); 308 importJar = new File(cpPath.toFile(),"local_policy.jar"); 309 } 310 311 URL jceCipherURL = ClassLoader.getSystemResource 312 ("javax/crypto/Cipher.class"); 313 314 if ((jceCipherURL == null) || 315 !exportJar.exists() || !importJar.exists()) { 316 throw new SecurityException 317 ("Cannot locate policy or framework files!"); 318 } 319 320 // Read jurisdiction policies. 321 CryptoPermissions defaultExport = new CryptoPermissions(); 322 CryptoPermissions exemptExport = new CryptoPermissions(); 323 loadPolicies(exportJar, defaultExport, exemptExport); 324 325 CryptoPermissions defaultImport = new CryptoPermissions(); 326 CryptoPermissions exemptImport = new CryptoPermissions(); 327 loadPolicies(importJar, defaultImport, exemptImport); 328 329 // Merge the export and import policies for default applications. 330 if (defaultExport.isEmpty() || defaultImport.isEmpty()) { 331 throw new SecurityException("Missing mandatory jurisdiction " + 332 "policy files"); 333 } 334 defaultPolicy = defaultExport.getMinimum(defaultImport); 335 336 // Merge the export and import policies for exempt applications. 337 if (exemptExport.isEmpty()) { 338 exemptPolicy = exemptImport.isEmpty() ? null : exemptImport; 339 } else { 340 exemptPolicy = exemptExport.getMinimum(exemptImport); 341 } 342 } 343 344 /** 345 * Load the policies from the specified file. Also checks that the 346 * policies are correctly signed. 347 */ 348 private static void loadPolicies(File jarPathName, 349 CryptoPermissions defaultPolicy, 350 CryptoPermissions exemptPolicy) 351 throws Exception { 352 353 JarFile jf = new JarFile(jarPathName); 354 355 Enumeration<JarEntry> entries = jf.entries(); 356 while (entries.hasMoreElements()) { 357 JarEntry je = entries.nextElement(); 358 InputStream is = null; 359 try { 360 if (je.getName().startsWith("default_")) { 361 is = jf.getInputStream(je); 362 defaultPolicy.load(is); 363 } else if (je.getName().startsWith("exempt_")) { 364 is = jf.getInputStream(je); 365 exemptPolicy.load(is); 366 } else { 367 continue; 368 } 369 } finally { 370 if (is != null) { 371 is.close(); 372 } 373 } 374 375 // Enforce the signer restraint, i.e. signer of JCE framework 376 // jar should also be the signer of the two jurisdiction policy 377 // jar files. 378 JarVerifier.verifyPolicySigned(je.getCertificates()); 379 } 380 // Close and nullify the JarFile reference to help GC. 381 jf.close(); 382 jf = null; 383 } 384 385 static CryptoPermissions getDefaultPolicy() { 386 return defaultPolicy; 387 } 388 389 static CryptoPermissions getExemptPolicy() { 390 return exemptPolicy; 391 } 392 393 static boolean isRestricted() { 394 return isRestricted; 395 } 396 }