8075706: Policy implementation does not allow policy.provider to be on the class path
Reviewed-by: alanb, mchung
--- a/jdk/src/java.base/share/classes/java/security/Policy.java Tue May 12 13:59:31 2015 +0100
+++ b/jdk/src/java.base/share/classes/java/security/Policy.java Tue May 12 13:13:06 2015 -0400
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1997, 2015, 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
@@ -49,7 +49,8 @@
* implementation (a default subclass implementation of this abstract class).
* The default Policy implementation can be changed by setting the value
* of the {@code policy.provider} security property to the fully qualified
- * name of the desired Policy subclass implementation.
+ * name of the desired Policy subclass implementation. The system class loader
+ * is used to load this class.
*
* <p> Application code can directly subclass Policy to provide a custom
* implementation. In addition, an instance of a Policy object can be
@@ -111,6 +112,10 @@
private static final Debug debug = Debug.getInstance("policy");
+ // Default policy provider
+ private static final String DEFAULT_POLICY =
+ "sun.security.provider.PolicyFile";
+
// Cache mapping ProtectionDomain.Key to PermissionCollection
private WeakHashMap<ProtectionDomain.Key, PermissionCollection> pdMapping;
@@ -169,74 +174,7 @@
synchronized (Policy.class) {
PolicyInfo pinfo = policy.get();
if (pinfo.policy == null) {
- String policy_class = AccessController.doPrivileged(
- new PrivilegedAction<>() {
- public String run() {
- return Security.getProperty("policy.provider");
- }
- });
- if (policy_class == null) {
- policy_class = "sun.security.provider.PolicyFile";
- }
-
- try {
- pinfo = new PolicyInfo(
- (Policy) Class.forName(policy_class).newInstance(),
- true);
- } catch (Exception e) {
- /*
- * The policy_class seems to be an extension
- * so we have to bootstrap loading it via a policy
- * provider that is on the bootclasspath.
- * If it loads then shift gears to using the configured
- * provider.
- */
-
- // install the bootstrap provider to avoid recursion
- Policy polFile = new sun.security.provider.PolicyFile();
- pinfo = new PolicyInfo(polFile, false);
- policy.set(pinfo);
-
- final String pc = policy_class;
- Policy pol = AccessController.doPrivileged(
- new PrivilegedAction<>() {
- public Policy run() {
- try {
- ClassLoader cl =
- ClassLoader.getSystemClassLoader();
- // we want the extension loader
- ClassLoader extcl = null;
- while (cl != null) {
- extcl = cl;
- cl = cl.getParent();
- }
- return (extcl != null ? (Policy)Class.forName(
- pc, true, extcl).newInstance() : null);
- } catch (Exception e) {
- if (debug != null) {
- debug.println("policy provider " +
- pc +
- " not available");
- e.printStackTrace();
- }
- return null;
- }
- }
- });
- /*
- * if it loaded install it as the policy provider. Otherwise
- * continue to use the system default implementation
- */
- if (pol != null) {
- pinfo = new PolicyInfo(pol, true);
- } else {
- if (debug != null) {
- debug.println("using sun.security.provider.PolicyFile");
- }
- pinfo = new PolicyInfo(polFile, true);
- }
- }
- policy.set(pinfo);
+ return loadPolicyProvider();
}
return pinfo.policy;
}
@@ -245,6 +183,70 @@
}
/**
+ * Loads and instantiates a Policy implementation specified by the
+ * policy.provider security property. Note that this method should only
+ * be called by getPolicyNoCheck and from within a synchronized block with
+ * an intrinsic lock on the Policy.class.
+ */
+ private static Policy loadPolicyProvider() {
+ String policyProvider =
+ AccessController.doPrivileged(new PrivilegedAction<>() {
+ @Override
+ public String run() {
+ return Security.getProperty("policy.provider");
+ }
+ });
+
+ /*
+ * If policy.provider is not set or is set to the default provider,
+ * simply instantiate it and return.
+ */
+ if (policyProvider == null || policyProvider.isEmpty() ||
+ policyProvider.equals(DEFAULT_POLICY))
+ {
+ Policy polFile = new sun.security.provider.PolicyFile();
+ policy.set(new PolicyInfo(polFile, true));
+ return polFile;
+ }
+
+ /*
+ * Locate, load, and instantiate the policy.provider impl using
+ * the system class loader. While doing so, install the bootstrap
+ * provider to avoid potential recursion.
+ */
+ Policy polFile = new sun.security.provider.PolicyFile();
+ policy.set(new PolicyInfo(polFile, false));
+
+ Policy pol = AccessController.doPrivileged(new PrivilegedAction<>() {
+ @Override
+ public Policy run() {
+ try {
+ ClassLoader scl = ClassLoader.getSystemClassLoader();
+ Class<?> c = Class.forName(policyProvider, true, scl);
+ return (Policy)c.newInstance();
+ } catch (Exception e) {
+ if (debug != null) {
+ debug.println("policy provider " + policyProvider +
+ " not available");
+ e.printStackTrace();
+ }
+ return null;
+ }
+ }
+ });
+
+ if (pol == null) {
+ // Fallback and use the system default implementation
+ if (debug != null) {
+ debug.println("using " + DEFAULT_POLICY);
+ }
+ pol = polFile;
+ }
+ policy.set(new PolicyInfo(pol, true));
+ return pol;
+ }
+
+ /**
* Sets the system-wide Policy object. This method first calls
* {@code SecurityManager.checkPermission} with a
* {@code SecurityPermission("setPolicy")}
--- a/jdk/src/java.base/share/conf/security/java.security Tue May 12 13:59:31 2015 +0100
+++ b/jdk/src/java.base/share/conf/security/java.security Tue May 12 13:13:06 2015 -0400
@@ -156,7 +156,8 @@
#
# Class to instantiate as the system Policy. This is the name of the class
-# that will be used as the Policy object.
+# that will be used as the Policy object. The system class loader is used to
+# locate this class.
#
policy.provider=sun.security.provider.PolicyFile
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/security/Policy/PolicyProvider/CustomPolicy.java Tue May 12 13:13:06 2015 -0400
@@ -0,0 +1,61 @@
+/*
+ * Copyright (c) 2015, 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.
+ *
+ * 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.
+ */
+
+import java.security.AccessController;
+import java.security.Permission;
+import java.security.Policy;
+import java.security.PrivilegedAction;
+import java.security.ProtectionDomain;
+
+public class CustomPolicy extends Policy {
+
+ // the ProtectionDomain of CustomPolicy
+ private final ProtectionDomain policyPd;
+
+ public CustomPolicy() {
+ policyPd = AccessController.doPrivileged(
+ (PrivilegedAction<ProtectionDomain>)
+ () -> this.getClass().getProtectionDomain());
+ }
+
+ @Override
+ public boolean implies(ProtectionDomain pd, Permission perm) {
+ System.out.println("CustomPolicy.implies");
+
+ // If the protection domain is the same as CustomPolicy, then
+ // we return true. This is to prevent recursive permission checks
+ // that lead to StackOverflow errors when the policy implementation
+ // performs a sensitive operation that triggers a permission check,
+ // for example, as below.
+ if (pd == policyPd) {
+ return true;
+ }
+
+ // Do something that triggers a permission check to make sure that
+ // we don't cause a StackOverflow error.
+ String home = AccessController.doPrivileged(
+ (PrivilegedAction<String>) () -> System.getProperty("user.home"));
+
+ return true;
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/security/Policy/PolicyProvider/UseSystemClassLoader.java Tue May 12 13:13:06 2015 -0400
@@ -0,0 +1,94 @@
+/*
+ * Copyright (c) 2015, 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.
+ *
+ * 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.
+ */
+
+import java.io.File;
+import java.net.URL;
+import java.security.Policy;
+import java.security.Security;
+
+/*
+ * @test
+ * @bug 8075706
+ * @summary Check that a custom policy provider can be loaded from the classpath
+ * @run main/othervm UseSystemClassLoader CUSTOM
+ * @run main/othervm UseSystemClassLoader DEFAULT
+ * @run main/othervm UseSystemClassLoader NOT_AVAIL
+ * @run main/othervm UseSystemClassLoader NOT_SET
+ */
+
+public class UseSystemClassLoader {
+
+ enum Type {
+ CUSTOM, DEFAULT, NOT_AVAIL, NOT_SET
+ };
+
+ public static void main(String[] args) throws Exception {
+
+ Type t = Type.valueOf(args[0]);
+
+ // We can't use the jtreg java.security.policy option to specify
+ // the policy file because that causes the default JDK policy provider
+ // to be set and once set, we cannot change it. So, instead we use the
+ // policy.url security property.
+ File file = new File(System.getProperty("test.src"), "test.policy");
+ URL policyURL = file.toURI().toURL();
+ Security.setProperty("policy.url.1", policyURL.toString());
+
+ switch (t) {
+ case CUSTOM:
+ // Set policy.provider to our custom policy provider
+ Security.setProperty("policy.provider", "CustomPolicy");
+ break;
+ case NOT_AVAIL:
+ // Set policy.provider to a non-existent policy provider
+ Security.setProperty("policy.provider", "NonExistentPolicy");
+ break;
+ case DEFAULT:
+ // Don't set policy.provider (leave default)
+ break;
+ case NOT_SET:
+ // Set policy.provider to empty string
+ Security.setProperty("policy.provider", "");
+ break;
+ }
+
+ System.setSecurityManager(new SecurityManager());
+ Policy p = Policy.getPolicy();
+ switch (t) {
+ case CUSTOM:
+ // check that the custom policy provider has been set
+ if (!(p instanceof CustomPolicy)) {
+ throw new Exception("CustomPolicy was not set");
+ }
+ break;
+ case NOT_AVAIL:
+ case DEFAULT:
+ case NOT_SET:
+ // check that the default policy provider has been set
+ if (!(p instanceof sun.security.provider.PolicyFile)) {
+ throw new Exception("default provider was not set");
+ }
+ break;
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/security/Policy/PolicyProvider/test.policy Tue May 12 13:13:06 2015 -0400
@@ -0,0 +1,9 @@
+grant {
+ permission java.lang.RuntimePermission "createSecurityManager";
+ permission java.lang.RuntimePermission "setSecurityManager";
+ permission java.security.SecurityPermission "setProperty.policy.provider";
+ permission java.security.SecurityPermission "getPolicy";
+ permission java.util.PropertyPermission "user.home", "read";
+ permission java.lang.RuntimePermission "getProtectionDomain";
+ permission java.lang.RuntimePermission "accessClassInPackage.sun.security.provider";
+};