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
--- 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");
+ }
+ }
+ }
+}