8191053: Provide a mechanism to make system's security manager immutable
authormullan
Wed, 10 Oct 2018 16:25:40 -0400
changeset 52084 ec4f2762b234
parent 52083 eb1ecdd3611e
child 52085 26c3104c936d
child 56952 2e2fa5dc9f38
8191053: Provide a mechanism to make system's security manager immutable Summary: Make System.setSecurityManager optional to support and add new disallow and allow options to the java.security.manager system property Reviewed-by: alanb, mchung, rriggs, smarks
src/java.base/share/classes/java/lang/SecurityManager.java
src/java.base/share/classes/java/lang/System.java
test/jdk/java/lang/System/AllowSecurityManager.java
--- a/src/java.base/share/classes/java/lang/SecurityManager.java	Wed Oct 10 15:23:38 2018 -0400
+++ b/src/java.base/share/classes/java/lang/SecurityManager.java	Wed Oct 10 16:25:40 2018 -0400
@@ -28,7 +28,6 @@
 import java.lang.module.ModuleDescriptor;
 import java.lang.module.ModuleDescriptor.Exports;
 import java.lang.module.ModuleDescriptor.Opens;
-import java.lang.module.ModuleReference;
 import java.lang.reflect.Member;
 import java.io.FileDescriptor;
 import java.io.File;
@@ -47,9 +46,7 @@
 import java.util.PropertyPermission;
 import java.util.Set;
 import java.util.concurrent.ConcurrentHashMap;
-import java.util.stream.Collectors;
 
-import jdk.internal.module.ModuleBootstrap;
 import jdk.internal.module.ModuleLoaderMap;
 import jdk.internal.reflect.CallerSensitive;
 import sun.security.util.SecurityConstants;
@@ -81,10 +78,100 @@
  * throws a <code>SecurityException</code> if the operation is not
  * permitted.
  * <p>
- * The current security manager is set by the
- * <code>setSecurityManager</code> method in class
- * <code>System</code>. The current security manager is obtained
- * by the <code>getSecurityManager</code> method.
+ * Environments using a security manager will typically set the security
+ * manager at startup. In the JDK implementation, this is done by setting
+ * the system property {@code java.security.manager} on the command line to
+ * the class name of the security manager. It can also be set to the empty
+ * String ("") or the special token "{@code default}" to use the
+ * default {@code java.lang.SecurityManager}. If a class name is specified,
+ * it must be {@code java.lang.SecurityManager} or a public subclass and have
+ * a public no-arg constructor. The class is loaded by the
+ * {@linkplain ClassLoader#getSystemClassLoader() built-in system class loader}
+ * if it is not {@code java.lang.SecurityManager}. If the
+ * {@code java.security.manager} system property is not set, the default value
+ * is {@code null}, which means a security manager will not be set at startup.
+ * <p>
+ * The Java run-time may also allow, but is not required to allow, the security
+ * manager to be set dynamically by invoking the
+ * {@link System#setSecurityManager(SecurityManager) setSecurityManager} method.
+ * In the JDK implementation, if the Java virtual machine is started with
+ * the {@code java.security.manager} system property set to the special token
+ * "{@code disallow}" then a security manager will not be set at startup and
+ * cannot be set dynamically (the
+ * {@link System#setSecurityManager(SecurityManager) setSecurityManager}
+ * method will throw an {@code UnsupportedOperationException}). If the
+ * {@code java.security.manager} system property is not set or is set to the
+ * special token "{@code allow}", then a security manager will not be set at
+ * startup but can be set dynamically. Finally, if the
+ * {@code java.security.manager} system property is set to the class name of
+ * the security manager, or to the empty String ("") or the special token
+ * "{@code default}", then a security manager is set at startup (as described
+ * previously) and can also be subsequently replaced (or disabled) dynamically
+ * (subject to the policy of the currently installed security manager). The
+ * following table illustrates the behavior of the JDK implementation for the
+ * different settings of the {@code java.security.manager} system property:
+ * <table class="striped">
+ * <caption style="display:none">property value,
+ *  the SecurityManager set at startup,
+ *  can dynamically set a SecurityManager
+ * </caption>
+ * <thead>
+ * <tr>
+ * <th scope="col">Property Value</th>
+ * <th scope="col">The SecurityManager set at startup</th>
+ * <th scope="col">System.setSecurityManager run-time behavior</th>
+ * </tr>
+ * </thead>
+ * <tbody>
+ *
+ * <tr>
+ *   <th scope="row">null</th>
+ *   <td>None</td>
+ *   <td>Success or throws {@code SecurityException} if not permitted by
+ * the currently installed security manager</td>
+ * </tr>
+ *
+ * <tr>
+ *   <th scope="row">empty String ("")</th>
+ *   <td>{@code java.lang.SecurityManager}</td>
+ *   <td>Success or throws {@code SecurityException} if not permitted by
+ * the currently installed security manager</td>
+ * </tr>
+ *
+ * <tr>
+ *   <th scope="row">"default"</th>
+ *   <td>{@code java.lang.SecurityManager}</td>
+ *   <td>Success or throws {@code SecurityException} if not permitted by
+ * the currently installed security manager</td>
+ * </tr>
+ *
+ * <tr>
+ *   <th scope="row">"disallow"</th>
+ *   <td>None</td>
+ *   <td>Always throws {@code UnsupportedOperationException}</td>
+ * </tr>
+ *
+ * <tr>
+ *   <th scope="row">"allow"</th>
+ *   <td>None</td>
+ *   <td>Success or throws {@code SecurityException} if not permitted by
+ * the currently installed security manager</td>
+ * </tr>
+ *
+ * <tr>
+ *   <th scope="row">a class name</th>
+ *   <td>the named class</td>
+ *   <td>Success or throws {@code SecurityException} if not permitted by
+ * the currently installed security manager</td>
+ * </tr>
+ *
+ * </tbody>
+ * </table>
+ * <p> A future release of the JDK may change the default value of the
+ * {@code java.security.manager} system property to "{@code disallow}".
+ * <p>
+ * The current security manager is returned by the
+ * {@link System#getSecurityManager() getSecurityManager} method.
  * <p>
  * The special method
  * {@link SecurityManager#checkPermission(java.security.Permission)}
@@ -221,7 +308,6 @@
  * @see     java.net.SocketPermission
  * @see     java.util.PropertyPermission
  * @see     java.lang.RuntimePermission
- * @see     java.awt.AWTPermission
  * @see     java.security.Policy Policy
  * @see     java.security.SecurityPermission SecurityPermission
  * @see     java.security.ProtectionDomain
--- a/src/java.base/share/classes/java/lang/System.java	Wed Oct 10 15:23:38 2018 -0400
+++ b/src/java.base/share/classes/java/lang/System.java	Wed Oct 10 16:25:40 2018 -0400
@@ -72,6 +72,7 @@
 import jdk.internal.logger.LoggerFinderLoader;
 import jdk.internal.logger.LazyLoggers;
 import jdk.internal.logger.LocalizedLoggerWrapper;
+import jdk.internal.vm.annotation.Stable;
 import sun.reflect.annotation.AnnotationType;
 import sun.nio.ch.Interruptible;
 import sun.security.util.SecurityConstants;
@@ -154,9 +155,18 @@
      */
     public static final PrintStream err = null;
 
-    /* The security manager for the system.
-     */
-    private static volatile SecurityManager security;
+    // indicates if a security manager is possible
+    private static final int NEVER = 1;
+    private static final int MAYBE = 2;
+    private static @Stable int allowSecurityManager;
+
+    // current security manager
+    private static volatile SecurityManager security;   // read by VM
+
+    // return true if a security manager is allowed
+    private static boolean allowSecurityManager() {
+        return (allowSecurityManager != NEVER);
+    }
 
     /**
      * Reassigns the "standard" input stream.
@@ -231,6 +241,7 @@
     }
 
     private static volatile Console cons;
+
     /**
      * Returns the unique {@link java.io.Console Console} object associated
      * with the current Java virtual machine, if any.
@@ -292,7 +303,7 @@
     private static native void setErr0(PrintStream err);
 
     /**
-     * Sets the System security.
+     * Sets the system-wide security manager.
      *
      * If there is a security manager already installed, this method first
      * calls the security manager's {@code checkPermission} method
@@ -306,27 +317,46 @@
      * security manager has been established, then no action is taken and
      * the method simply returns.
      *
-     * @param      s   the security manager.
-     * @throws     SecurityException  if the security manager has already
-     *             been set and its {@code checkPermission} method
-     *             doesn't allow it to be replaced.
+     * @implNote In the JDK implementation, if the Java virtual machine is
+     * started with the system property {@code java.security.manager} set to
+     * the special token "{@code disallow}" then the {@code setSecurityManager}
+     * method cannot be used to set a security manager.
+     *
+     * @param  sm the security manager or {@code null}
+     * @throws SecurityException
+     *         if the security manager has already been set and its {@code
+     *         checkPermission} method doesn't allow it to be replaced
+     * @throws UnsupportedOperationException
+     *         if {@code sm} is non-null and a security manager is not allowed
+     *         to be set dynamically
      * @see #getSecurityManager
      * @see SecurityManager#checkPermission
      * @see java.lang.RuntimePermission
      */
-    public static void setSecurityManager(final SecurityManager s) {
-        if (security == null) {
-            // ensure image reader is initialized
-            Object.class.getResource("java/lang/ANY");
-        }
-        if (s != null) {
-            try {
-                s.checkPackageAccess("java.lang");
-            } catch (Exception e) {
-                // no-op
+    public static void setSecurityManager(SecurityManager sm) {
+        if (allowSecurityManager()) {
+            if (security == null) {
+                // ensure image reader is initialized
+                Object.class.getResource("java/lang/ANY");
+            }
+            if (sm != null) {
+                try {
+                    // pre-populates the SecurityManager.packageAccess cache
+                    // to avoid recursive permission checking issues with custom
+                    // SecurityManager implementations
+                    sm.checkPackageAccess("java.lang");
+                } catch (Exception e) {
+                    // no-op
+                }
+            }
+            setSecurityManager0(sm);
+        } else {
+            // security manager not allowed
+            if (sm != null) {
+                throw new UnsupportedOperationException(
+                    "Runtime configured to disallow security manager");
             }
         }
-        setSecurityManager0(s);
     }
 
     private static synchronized
@@ -335,13 +365,12 @@
         if (sm != null) {
             // ask the currently installed security manager if we
             // can replace it.
-            sm.checkPermission(new RuntimePermission
-                                     ("setSecurityManager"));
+            sm.checkPermission(new RuntimePermission("setSecurityManager"));
         }
 
         if ((s != null) && (s.getClass().getClassLoader() != null)) {
             // New security manager class is not on bootstrap classpath.
-            // Cause policy to get initialized before we install the new
+            // Force policy to get initialized before we install the new
             // security manager, in order to prevent infinite loops when
             // trying to initialize the policy (which usually involves
             // accessing some security and/or system properties, which in turn
@@ -361,7 +390,7 @@
     }
 
     /**
-     * Gets the system security interface.
+     * Gets the system-wide security manager.
      *
      * @return  if a security manager has already been established for the
      *          current application, then that security manager is returned;
@@ -369,7 +398,11 @@
      * @see     #setSecurityManager
      */
     public static SecurityManager getSecurityManager() {
-        return security;
+        if (allowSecurityManager()) {
+            return security;
+        } else {
+            return null;
+        }
     }
 
     /**
@@ -2028,35 +2061,48 @@
      * 3. set TCCL
      *
      * This method must be called after the module system initialization.
-     * The security manager and system class loader may be custom class from
+     * The security manager and system class loader may be a custom class from
      * the application classpath or modulepath.
      */
     private static void initPhase3() {
-        // set security manager
-        String cn = System.getProperty("java.security.manager");
-        if (cn != null) {
-            if (cn.isEmpty() || "default".equals(cn)) {
-                System.setSecurityManager(new SecurityManager());
-            } else {
-                try {
-                    Class<?> c = Class.forName(cn, false, ClassLoader.getBuiltinAppClassLoader());
-                    Constructor<?> ctor = c.getConstructor();
-                    // Must be a public subclass of SecurityManager with
-                    // a public no-arg constructor
-                    if (!SecurityManager.class.isAssignableFrom(c) ||
+        String smProp = System.getProperty("java.security.manager");
+        if (smProp != null) {
+            switch (smProp) {
+                case "disallow":
+                    allowSecurityManager = NEVER;
+                    break;
+                case "allow":
+                    allowSecurityManager = MAYBE;
+                    break;
+                case "":
+                case "default":
+                    setSecurityManager(new SecurityManager());
+                    allowSecurityManager = MAYBE;
+                    break;
+                default:
+                    try {
+                        ClassLoader cl = ClassLoader.getBuiltinAppClassLoader();
+                        Class<?> c = Class.forName(smProp, false, cl);
+                        Constructor<?> ctor = c.getConstructor();
+                        // Must be a public subclass of SecurityManager with
+                        // a public no-arg constructor
+                        if (!SecurityManager.class.isAssignableFrom(c) ||
                             !Modifier.isPublic(c.getModifiers()) ||
                             !Modifier.isPublic(ctor.getModifiers())) {
-                        throw new Error("Could not create SecurityManager: " + ctor.toString());
+                            throw new Error("Could not create SecurityManager: "
+                                             + ctor.toString());
+                        }
+                        // custom security manager may be in non-exported package
+                        ctor.setAccessible(true);
+                        SecurityManager sm = (SecurityManager) ctor.newInstance();
+                        setSecurityManager(sm);
+                    } catch (Exception e) {
+                        throw new InternalError("Could not create SecurityManager", e);
                     }
-                    // custom security manager implementation may be in unnamed module
-                    // or a named module but non-exported package
-                    ctor.setAccessible(true);
-                    SecurityManager sm = (SecurityManager) ctor.newInstance();
-                    System.setSecurityManager(sm);
-                } catch (Exception e) {
-                    throw new Error("Could not create SecurityManager", e);
-                }
+                    allowSecurityManager = MAYBE;
             }
+        } else {
+            allowSecurityManager = MAYBE;
         }
 
         // initializing the system class loader
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/java/lang/System/AllowSecurityManager.java	Wed Oct 10 16:25:40 2018 -0400
@@ -0,0 +1,53 @@
+/*
+ * Copyright (c) 2018, 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.
+ */
+
+/*
+ * @test
+ * @bug 8191053
+ * @summary Test that the allow/disallow options of the java.security.manager
+ *          system property work correctly
+ * @run main/othervm AllowSecurityManager
+ * @run main/othervm -Djava.security.manager=disallow AllowSecurityManager
+ * @run main/othervm -Djava.security.manager=allow AllowSecurityManager
+ */
+
+public class AllowSecurityManager {
+
+    public static void main(String args[]) throws Exception {
+        String prop = System.getProperty("java.security.manager");
+        boolean disallow = "disallow".equals(prop);
+        try {
+            System.setSecurityManager(new SecurityManager());
+            if (disallow) {
+                throw new Exception("System.setSecurityManager did not " +
+                                    "throw UnsupportedOperationException");
+            }
+        } catch (UnsupportedOperationException uoe) {
+            if (!disallow) {
+                throw new Exception("UnsupportedOperationException " +
+                                    "unexpectedly thrown by " +
+                                    "System.setSecurityManager");
+            }
+        }
+    }
+}