# HG changeset patch # User mullan # Date 1539203140 14400 # Node ID ec4f2762b23413b1c7f46002b516ad57b308517c # Parent eb1ecdd3611e8147a464431265476188d6e8de3b 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 diff -r eb1ecdd3611e -r ec4f2762b234 src/java.base/share/classes/java/lang/SecurityManager.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 SecurityException if the operation is not * permitted. *

- * The current security manager is set by the - * setSecurityManager method in class - * System. The current security manager is obtained - * by the getSecurityManager 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. + *

+ * 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: + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
property value, + * the SecurityManager set at startup, + * can dynamically set a SecurityManager + *
Property ValueThe SecurityManager set at startupSystem.setSecurityManager run-time behavior
nullNoneSuccess or throws {@code SecurityException} if not permitted by + * the currently installed security manager
empty String (""){@code java.lang.SecurityManager}Success or throws {@code SecurityException} if not permitted by + * the currently installed security manager
"default"{@code java.lang.SecurityManager}Success or throws {@code SecurityException} if not permitted by + * the currently installed security manager
"disallow"NoneAlways throws {@code UnsupportedOperationException}
"allow"NoneSuccess or throws {@code SecurityException} if not permitted by + * the currently installed security manager
a class namethe named classSuccess or throws {@code SecurityException} if not permitted by + * the currently installed security manager
+ *

A future release of the JDK may change the default value of the + * {@code java.security.manager} system property to "{@code disallow}". + *

+ * The current security manager is returned by the + * {@link System#getSecurityManager() getSecurityManager} method. *

* 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 diff -r eb1ecdd3611e -r ec4f2762b234 src/java.base/share/classes/java/lang/System.java --- 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 diff -r eb1ecdd3611e -r ec4f2762b234 test/jdk/java/lang/System/AllowSecurityManager.java --- /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"); + } + } + } +}