* Fixes #6184 - JEP-411 will deprecate/remove the SecurityManager from the JVM. Removed usages of `SecurityManager` and `AccessControlller.doPrivileged()`. In places where they are still necessary, now using reflection via newly introduced `SecurityUtils` class. Signed-off-by: Simone Bordet <simone.bordet@gmail.com>
This commit is contained in:
parent
795315f6ff
commit
2c61011de1
|
@ -23,6 +23,7 @@ import javax.security.auth.message.config.AuthConfigFactory;
|
|||
import javax.security.auth.message.config.AuthConfigProvider;
|
||||
import javax.security.auth.message.config.RegistrationListener;
|
||||
|
||||
import org.eclipse.jetty.util.security.SecurityUtils;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
|
@ -60,9 +61,7 @@ public class DefaultAuthConfigFactory extends AuthConfigFactory
|
|||
@Override
|
||||
public String registerConfigProvider(String className, Map properties, String layer, String appContext, String description)
|
||||
{
|
||||
SecurityManager sm = System.getSecurityManager();
|
||||
if (sm != null)
|
||||
sm.checkPermission(new AuthPermission("registerAuthConfigProvider"));
|
||||
checkPermission("registerAuthConfigProvider");
|
||||
|
||||
String key = getKey(layer, appContext);
|
||||
AuthConfigProvider configProvider = createConfigProvider(className, properties);
|
||||
|
@ -76,9 +75,7 @@ public class DefaultAuthConfigFactory extends AuthConfigFactory
|
|||
@Override
|
||||
public String registerConfigProvider(AuthConfigProvider provider, String layer, String appContext, String description)
|
||||
{
|
||||
SecurityManager sm = System.getSecurityManager();
|
||||
if (sm != null)
|
||||
sm.checkPermission(new AuthPermission("registerAuthConfigProvider"));
|
||||
checkPermission("registerAuthConfigProvider");
|
||||
|
||||
String key = getKey(layer, appContext);
|
||||
DefaultRegistrationContext context = new DefaultRegistrationContext(provider, layer, appContext, description, false);
|
||||
|
@ -91,9 +88,7 @@ public class DefaultAuthConfigFactory extends AuthConfigFactory
|
|||
@Override
|
||||
public boolean removeRegistration(String registrationID)
|
||||
{
|
||||
SecurityManager sm = System.getSecurityManager();
|
||||
if (sm != null)
|
||||
sm.checkPermission(new AuthPermission("removeAuthRegistration"));
|
||||
checkPermission("removeAuthRegistration");
|
||||
|
||||
DefaultRegistrationContext registrationContext = _registrations.remove(registrationID);
|
||||
if (registrationContext == null)
|
||||
|
@ -106,9 +101,7 @@ public class DefaultAuthConfigFactory extends AuthConfigFactory
|
|||
@Override
|
||||
public String[] detachListener(RegistrationListener listener, String layer, String appContext)
|
||||
{
|
||||
SecurityManager sm = System.getSecurityManager();
|
||||
if (sm != null)
|
||||
sm.checkPermission(new AuthPermission("detachAuthListener"));
|
||||
checkPermission("detachAuthListener");
|
||||
|
||||
List<String> registrationIds = new ArrayList<>();
|
||||
for (DefaultRegistrationContext registration : _registrations.values())
|
||||
|
@ -145,13 +138,16 @@ public class DefaultAuthConfigFactory extends AuthConfigFactory
|
|||
@Override
|
||||
public void refresh()
|
||||
{
|
||||
SecurityManager sm = System.getSecurityManager();
|
||||
if (sm != null)
|
||||
sm.checkPermission(new AuthPermission("refreshAuth"));
|
||||
checkPermission("refreshAuth");
|
||||
|
||||
// TODO: maybe we should re-construct providers created from classname.
|
||||
}
|
||||
|
||||
private static void checkPermission(String permission)
|
||||
{
|
||||
SecurityUtils.checkPermission(new AuthPermission(permission));
|
||||
}
|
||||
|
||||
private static String getKey(String layer, String appContext)
|
||||
{
|
||||
return layer + "/" + appContext;
|
||||
|
|
|
@ -20,8 +20,6 @@ import java.net.MalformedURLException;
|
|||
import java.net.URI;
|
||||
import java.net.URL;
|
||||
import java.net.URLClassLoader;
|
||||
import java.security.AccessController;
|
||||
import java.security.PrivilegedAction;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
|
@ -87,6 +85,7 @@ import org.eclipse.jetty.util.component.DumpableCollection;
|
|||
import org.eclipse.jetty.util.component.Graceful;
|
||||
import org.eclipse.jetty.util.component.LifeCycle;
|
||||
import org.eclipse.jetty.util.resource.Resource;
|
||||
import org.eclipse.jetty.util.security.SecurityUtils;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
|
@ -219,7 +218,7 @@ public class ContextHandler extends ScopedHandler implements Attributes, Gracefu
|
|||
private int _maxFormKeys = Integer.getInteger(MAX_FORM_KEYS_KEY, DEFAULT_MAX_FORM_KEYS);
|
||||
private int _maxFormContentSize = Integer.getInteger(MAX_FORM_CONTENT_SIZE_KEY, DEFAULT_MAX_FORM_CONTENT_SIZE);
|
||||
private boolean _compactPath = false;
|
||||
private boolean _usingSecurityManager = System.getSecurityManager() != null;
|
||||
private boolean _usingSecurityManager = getSecurityManager() != null;
|
||||
|
||||
private final List<EventListener> _programmaticListeners = new CopyOnWriteArrayList<>();
|
||||
private final List<ServletContextListener> _servletContextListeners = new CopyOnWriteArrayList<>();
|
||||
|
@ -326,7 +325,7 @@ public class ContextHandler extends ScopedHandler implements Attributes, Gracefu
|
|||
|
||||
public void setUsingSecurityManager(boolean usingSecurityManager)
|
||||
{
|
||||
if (usingSecurityManager && System.getSecurityManager() == null)
|
||||
if (usingSecurityManager && getSecurityManager() == null)
|
||||
throw new IllegalStateException("No security manager");
|
||||
_usingSecurityManager = usingSecurityManager;
|
||||
}
|
||||
|
@ -2114,6 +2113,11 @@ public class ContextHandler extends ScopedHandler implements Attributes, Gracefu
|
|||
_aliasChecks.clear();
|
||||
}
|
||||
|
||||
private static Object getSecurityManager()
|
||||
{
|
||||
return SecurityUtils.getSecurityManager();
|
||||
}
|
||||
|
||||
/**
|
||||
* Context.
|
||||
* <p>
|
||||
|
@ -2561,11 +2565,9 @@ public class ContextHandler extends ScopedHandler implements Attributes, Gracefu
|
|||
{
|
||||
// check to see if the classloader of the caller is the same as the context
|
||||
// classloader, or a parent of it, as required by the javadoc specification.
|
||||
|
||||
// Wrap in a PrivilegedAction so that only Jetty code will require the
|
||||
// "createSecurityManager" permission, not also application code that calls this method.
|
||||
Caller caller = AccessController.doPrivileged((PrivilegedAction<Caller>)Caller::new);
|
||||
ClassLoader callerLoader = caller.getCallerClassLoader(2);
|
||||
ClassLoader callerLoader = StackWalker.getInstance(StackWalker.Option.RETAIN_CLASS_REFERENCE)
|
||||
.getCallerClass()
|
||||
.getClassLoader();
|
||||
while (callerLoader != null)
|
||||
{
|
||||
if (callerLoader == _classLoader)
|
||||
|
@ -2573,7 +2575,7 @@ public class ContextHandler extends ScopedHandler implements Attributes, Gracefu
|
|||
else
|
||||
callerLoader = callerLoader.getParent();
|
||||
}
|
||||
System.getSecurityManager().checkPermission(new RuntimePermission("getClassLoader"));
|
||||
SecurityUtils.checkPermission(new RuntimePermission("getClassLoader"));
|
||||
return _classLoader;
|
||||
}
|
||||
}
|
||||
|
@ -3103,17 +3105,4 @@ public class ContextHandler extends ScopedHandler implements Attributes, Gracefu
|
|||
*/
|
||||
void exitScope(Context context, Request request);
|
||||
}
|
||||
|
||||
private static class Caller extends SecurityManager
|
||||
{
|
||||
public ClassLoader getCallerClassLoader(int depth)
|
||||
{
|
||||
if (depth < 0)
|
||||
return null;
|
||||
Class<?>[] classContext = getClassContext();
|
||||
if (classContext.length <= depth)
|
||||
return null;
|
||||
return classContext[depth].getClassLoader();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,8 +16,6 @@ package org.eclipse.jetty.logging;
|
|||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.net.URL;
|
||||
import java.security.AccessController;
|
||||
import java.security.PrivilegedAction;
|
||||
import java.util.Arrays;
|
||||
import java.util.Locale;
|
||||
import java.util.Properties;
|
||||
|
@ -160,8 +158,6 @@ public class JettyLoggerConfiguration
|
|||
* @return the configuration
|
||||
*/
|
||||
public JettyLoggerConfiguration load(ClassLoader loader)
|
||||
{
|
||||
return AccessController.doPrivileged((PrivilegedAction<JettyLoggerConfiguration>)() ->
|
||||
{
|
||||
// First see if the jetty-logging.properties object exists in the classpath.
|
||||
// * This is an optional feature used by embedded mode use, and test cases to allow for early
|
||||
|
@ -183,7 +179,6 @@ public class JettyLoggerConfiguration
|
|||
// these values will override any key conflicts in properties.
|
||||
load(System.getProperties());
|
||||
return this;
|
||||
});
|
||||
}
|
||||
|
||||
public String getString(String key, String defValue)
|
||||
|
|
|
@ -13,9 +13,6 @@
|
|||
|
||||
package org.eclipse.jetty.util;
|
||||
|
||||
import java.security.AccessController;
|
||||
import java.security.PrivilegedAction;
|
||||
|
||||
/**
|
||||
* MemoryUtils provides an abstraction over memory properties and operations.
|
||||
*/
|
||||
|
@ -25,18 +22,11 @@ public class MemoryUtils
|
|||
|
||||
static
|
||||
{
|
||||
final int defaultValue = 64;
|
||||
int defaultValue = 64;
|
||||
int value = defaultValue;
|
||||
try
|
||||
{
|
||||
value = Integer.parseInt(AccessController.doPrivileged(new PrivilegedAction<String>()
|
||||
{
|
||||
@Override
|
||||
public String run()
|
||||
{
|
||||
return System.getProperty("org.eclipse.jetty.util.cacheLineBytes", String.valueOf(defaultValue));
|
||||
}
|
||||
}));
|
||||
value = Integer.parseInt(System.getProperty("org.eclipse.jetty.util.cacheLineBytes", String.valueOf(defaultValue)));
|
||||
}
|
||||
catch (Exception ignored)
|
||||
{
|
||||
|
|
|
@ -26,9 +26,7 @@ import java.lang.reflect.Method;
|
|||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
import java.net.URL;
|
||||
import java.security.AccessController;
|
||||
import java.security.CodeSource;
|
||||
import java.security.PrivilegedAction;
|
||||
import java.security.ProtectionDomain;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
|
@ -671,7 +669,7 @@ public class TypeUtil
|
|||
{
|
||||
try
|
||||
{
|
||||
ProtectionDomain domain = AccessController.doPrivileged((PrivilegedAction<ProtectionDomain>)() -> clazz.getProtectionDomain());
|
||||
ProtectionDomain domain = clazz.getProtectionDomain();
|
||||
if (domain != null)
|
||||
{
|
||||
CodeSource source = domain.getCodeSource();
|
||||
|
|
|
@ -0,0 +1,124 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others.
|
||||
//
|
||||
// This program and the accompanying materials are made available under the
|
||||
// terms of the Eclipse Public License v. 2.0 which is available at
|
||||
// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
|
||||
// which is available at https://www.apache.org/licenses/LICENSE-2.0.
|
||||
//
|
||||
// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
|
||||
// ========================================================================
|
||||
//
|
||||
|
||||
package org.eclipse.jetty.util.security;
|
||||
|
||||
import java.lang.invoke.MethodHandle;
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.lang.invoke.MethodType;
|
||||
import java.security.Permission;
|
||||
import java.security.PrivilegedAction;
|
||||
|
||||
/**
|
||||
* <p>Collections of utility methods to deal with the scheduled removal
|
||||
* of the security classes defined by <a href="https://openjdk.org/jeps/411">JEP 411</a>.</p>
|
||||
*/
|
||||
public class SecurityUtils
|
||||
{
|
||||
private static final MethodHandle doPrivileged = lookup();
|
||||
|
||||
private static MethodHandle lookup()
|
||||
{
|
||||
try
|
||||
{
|
||||
// Use reflection to work with Java versions that have and don't have AccessController.
|
||||
Class<?> klass = ClassLoader.getPlatformClassLoader().loadClass("java.security.AccessController");
|
||||
MethodHandles.Lookup lookup = MethodHandles.lookup();
|
||||
return lookup.findStatic(klass, "doPrivileged", MethodType.methodType(Object.class, PrivilegedAction.class));
|
||||
}
|
||||
catch (Throwable x)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the current security manager, if available
|
||||
*/
|
||||
public static Object getSecurityManager()
|
||||
{
|
||||
try
|
||||
{
|
||||
// Use reflection to work with Java versions that have and don't have SecurityManager.
|
||||
return System.class.getMethod("getSecurityManager").invoke(null);
|
||||
}
|
||||
catch (Throwable ignored)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Checks the given permission, if the {@link #getSecurityManager() security manager}
|
||||
* is set.</p>
|
||||
*
|
||||
* @param permission the permission to check
|
||||
* @throws SecurityException if the permission check fails
|
||||
*/
|
||||
public static void checkPermission(Permission permission) throws SecurityException
|
||||
{
|
||||
Object securityManager = SecurityUtils.getSecurityManager();
|
||||
if (securityManager == null)
|
||||
return;
|
||||
try
|
||||
{
|
||||
securityManager.getClass().getMethod("checkPermission")
|
||||
.invoke(securityManager, permission);
|
||||
}
|
||||
catch (SecurityException x)
|
||||
{
|
||||
throw x;
|
||||
}
|
||||
catch (Throwable ignored)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Runs the given action with the calling context restricted
|
||||
* to just the calling frame, not all the frames in the stack.</p>
|
||||
*
|
||||
* @param action the action to run
|
||||
* @return the result of running the action
|
||||
* @param <T> the type of the result
|
||||
*/
|
||||
public static <T> T doPrivileged(PrivilegedAction<T> action)
|
||||
{
|
||||
// Keep this method short and inlineable.
|
||||
MethodHandle methodHandle = doPrivileged;
|
||||
if (methodHandle == null)
|
||||
return action.run();
|
||||
return doPrivileged(methodHandle, action);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private static <T> T doPrivileged(MethodHandle doPrivileged, PrivilegedAction<T> action)
|
||||
{
|
||||
try
|
||||
{
|
||||
return (T)doPrivileged.invoke(action);
|
||||
}
|
||||
catch (RuntimeException | Error x)
|
||||
{
|
||||
throw x;
|
||||
}
|
||||
catch (Throwable x)
|
||||
{
|
||||
throw new RuntimeException(x);
|
||||
}
|
||||
}
|
||||
|
||||
private SecurityUtils()
|
||||
{
|
||||
}
|
||||
}
|
|
@ -13,39 +13,46 @@
|
|||
|
||||
package org.eclipse.jetty.util.thread;
|
||||
|
||||
import java.security.AccessController;
|
||||
import java.security.PrivilegedAction;
|
||||
import java.security.ProtectionDomain;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import org.eclipse.jetty.util.security.SecurityUtils;
|
||||
|
||||
/**
|
||||
* Convenience class to ensure that a new Thread is created
|
||||
* inside a privileged block.
|
||||
*
|
||||
* This prevents the Thread constructor
|
||||
* from pinning the caller's context classloader. This happens
|
||||
* when the Thread constructor takes a snapshot of the current
|
||||
* calling context - which contains ProtectionDomains that may
|
||||
* reference the context classloader - and remembers it for the
|
||||
* lifetime of the Thread.
|
||||
* <p>Convenience {@link Thread} factory that ensure threads are
|
||||
* created without referencing any web application {@link ClassLoader}.</p>
|
||||
* <p>Up to Java 17, the {@code Thread} constructor was taking a
|
||||
* snapshot of the calling context, which may contain a {@link ProtectionDomain}
|
||||
* that references a web application {@code ClassLoader}
|
||||
* (for example if the creation of the {@code Thread} was triggered
|
||||
* by some operation performed by the web application).
|
||||
* The {@code Thread} might then be pooled and prevent the
|
||||
* web application {@code ClassLoader} to be garbage collected
|
||||
* when the web application is undeployed.
|
||||
* For this reason, {@code Thread}s must be created in a privileged
|
||||
* action, which restricts the calling context to just the caller
|
||||
* frame, not all the frames in the stack.</p>
|
||||
* <p>Since Java 18 and the removal of the Java security manager
|
||||
* and related classes by JEP 411, {@code Thread}s do not retain
|
||||
* the calling context, so there is no need to create them in a
|
||||
* privileged action.</p>
|
||||
*/
|
||||
class PrivilegedThreadFactory
|
||||
{
|
||||
/**
|
||||
* Use a Supplier to make a new thread, calling it within
|
||||
* a privileged block to prevent classloader pinning.
|
||||
* <p>Creates a new {@link Thread} from the given {@link Supplier},
|
||||
* without retaining the calling context.</p>
|
||||
*
|
||||
* @param newThreadSupplier a Supplier to create a fresh thread
|
||||
* @return a new thread, protected from classloader pinning.
|
||||
* @param creator the action that creates the {@link Thread}
|
||||
* @return a new {@link Thread} without retaining the calling context
|
||||
*/
|
||||
static <T extends Thread> T newThread(Supplier<T> newThreadSupplier)
|
||||
static <T extends Thread> T newThread(PrivilegedAction<T> creator)
|
||||
{
|
||||
return AccessController.doPrivileged(new PrivilegedAction<T>()
|
||||
{
|
||||
@Override
|
||||
public T run()
|
||||
{
|
||||
return newThreadSupplier.get();
|
||||
return SecurityUtils.doPrivileged(creator);
|
||||
}
|
||||
});
|
||||
|
||||
private PrivilegedThreadFactory()
|
||||
{
|
||||
}
|
||||
}
|
||||
|
|
|
@ -31,14 +31,11 @@ import org.slf4j.LoggerFactory;
|
|||
public class ShutdownThread extends Thread
|
||||
{
|
||||
private static final Logger LOG = LoggerFactory.getLogger(ShutdownThread.class);
|
||||
private static final ShutdownThread _thread = PrivilegedThreadFactory.newThread(() ->
|
||||
{
|
||||
return new ShutdownThread();
|
||||
});
|
||||
private static final ShutdownThread _thread = PrivilegedThreadFactory.newThread(ShutdownThread::new);
|
||||
|
||||
private final AutoLock _lock = new AutoLock();
|
||||
private boolean _hooked;
|
||||
private final List<LifeCycle> _lifeCycles = new CopyOnWriteArrayList<LifeCycle>();
|
||||
private final List<LifeCycle> _lifeCycles = new CopyOnWriteArrayList<>();
|
||||
|
||||
/**
|
||||
* Default constructor for the singleton
|
||||
|
|
|
@ -30,8 +30,6 @@ import java.net.URL;
|
|||
import java.net.UnknownHostException;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.security.AccessController;
|
||||
import java.security.PrivilegedExceptionAction;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
|
@ -493,7 +491,7 @@ public class XmlConfiguration
|
|||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
LOG.warn("Config error {} at {} in {}", e.toString(), node, _configuration);
|
||||
LOG.warn("Config error {} at {} in {}", e, node, _configuration);
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
@ -1814,8 +1812,6 @@ public class XmlConfiguration
|
|||
public static void main(final String... args) throws Exception
|
||||
{
|
||||
try
|
||||
{
|
||||
AccessController.doPrivileged((PrivilegedExceptionAction<Void>)() ->
|
||||
{
|
||||
Properties properties = new Properties();
|
||||
properties.putAll(System.getProperties());
|
||||
|
@ -1852,9 +1848,8 @@ public class XmlConfiguration
|
|||
if (properties.size() > 0)
|
||||
{
|
||||
Map<String, String> props = new HashMap<>();
|
||||
properties.entrySet().stream()
|
||||
.forEach(objectObjectEntry -> props.put(objectObjectEntry.getKey().toString(),
|
||||
String.valueOf(objectObjectEntry.getValue())));
|
||||
properties.forEach((key, value) -> props.put(key.toString(),
|
||||
String.valueOf(value)));
|
||||
configuration.getProperties().putAll(props);
|
||||
}
|
||||
|
||||
|
@ -1893,8 +1888,6 @@ public class XmlConfiguration
|
|||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
});
|
||||
}
|
||||
catch (Error | Exception e)
|
||||
{
|
||||
|
|
Loading…
Reference in New Issue