8075706: Policy implementation does not allow policy.provider to be on the class path
authormullan
Tue, 12 May 2015 13:13:06 -0400
changeset 30444 054604aed79c
parent 30443 8d2f2ce637bd
child 30445 e1e28b9abbe5
8075706: Policy implementation does not allow policy.provider to be on the class path Reviewed-by: alanb, mchung
jdk/src/java.base/share/classes/java/security/Policy.java
jdk/src/java.base/share/conf/security/java.security
jdk/test/java/security/Policy/PolicyProvider/CustomPolicy.java
jdk/test/java/security/Policy/PolicyProvider/UseSystemClassLoader.java
jdk/test/java/security/Policy/PolicyProvider/test.policy
--- 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";
+};