From 12db285f178fcaa8d9d6e1a208030eb11a35d751 Mon Sep 17 00:00:00 2001 From: Greg Wilkins Date: Mon, 15 Jul 2024 13:32:34 +1000 Subject: [PATCH] Remove usage of a SecurityManager from EE11 (#12032) Use callAs rather than doAs Disable after java 21 --- .../jetty/client/SPNEGOAuthentication.java | 2 +- .../jetty/security/SPNEGOLoginService.java | 7 +-- .../jetty/util/security/SecurityUtils.java | 50 ++++++++++++++----- .../ee11/servlet/ServletContextHandler.java | 36 +++---------- 4 files changed, 49 insertions(+), 46 deletions(-) diff --git a/jetty-core/jetty-client/src/main/java/org/eclipse/jetty/client/SPNEGOAuthentication.java b/jetty-core/jetty-client/src/main/java/org/eclipse/jetty/client/SPNEGOAuthentication.java index 8d2ca3669dd..34b303581ea 100644 --- a/jetty-core/jetty-client/src/main/java/org/eclipse/jetty/client/SPNEGOAuthentication.java +++ b/jetty-core/jetty-client/src/main/java/org/eclipse/jetty/client/SPNEGOAuthentication.java @@ -214,7 +214,7 @@ public class SPNEGOAuthentication extends AbstractAuthentication String b64Input = headerInfo.getBase64(); byte[] input = b64Input == null ? new byte[0] : Base64.getDecoder().decode(b64Input); - byte[] output = SecurityUtils.doAs(spnegoContext.subject, initGSSContext(spnegoContext, request.getHost(), input)); + byte[] output = SecurityUtils.callAs(spnegoContext.subject, initGSSContext(spnegoContext, request.getHost(), input)); String b64Output = output == null ? null : new String(Base64.getEncoder().encode(output)); // The result cannot be used for subsequent requests, diff --git a/jetty-core/jetty-security/src/main/java/org/eclipse/jetty/security/SPNEGOLoginService.java b/jetty-core/jetty-security/src/main/java/org/eclipse/jetty/security/SPNEGOLoginService.java index ec95a5fdd9a..3b4a0026dec 100644 --- a/jetty-core/jetty-security/src/main/java/org/eclipse/jetty/security/SPNEGOLoginService.java +++ b/jetty-core/jetty-security/src/main/java/org/eclipse/jetty/security/SPNEGOLoginService.java @@ -143,7 +143,7 @@ public class SPNEGOLoginService extends ContainerLifeCycle implements LoginServi LoginContext loginContext = new LoginContext("", null, null, new SPNEGOConfiguration()); loginContext.login(); Subject subject = loginContext.getSubject(); - _context = SecurityUtils.doAs(subject, newSpnegoContext(subject)); + _context = SecurityUtils.callAs(subject, newSpnegoContext(subject)); super.doStart(); } @@ -182,10 +182,11 @@ public class SPNEGOLoginService extends ContainerLifeCycle implements LoginServi gssContext = holder == null ? null : holder.gssContext; } if (gssContext == null) - gssContext = SecurityUtils.doAs(subject, newGSSContext()); + gssContext = SecurityUtils.callAs(subject, newGSSContext()); + byte[] input = Base64.getDecoder().decode((String)credentials); - byte[] output = SecurityUtils.doAs(_context._subject, acceptGSSContext(gssContext, input)); + byte[] output = SecurityUtils.callAs(_context._subject, acceptGSSContext(gssContext, input)); String token = Base64.getEncoder().encodeToString(output); String userName = toUserName(gssContext); diff --git a/jetty-core/jetty-util/src/main/java/org/eclipse/jetty/util/security/SecurityUtils.java b/jetty-core/jetty-util/src/main/java/org/eclipse/jetty/util/security/SecurityUtils.java index 301965e997a..91dbc026149 100644 --- a/jetty-core/jetty-util/src/main/java/org/eclipse/jetty/util/security/SecurityUtils.java +++ b/jetty-core/jetty-util/src/main/java/org/eclipse/jetty/util/security/SecurityUtils.java @@ -22,16 +22,22 @@ import java.util.concurrent.Callable; import java.util.concurrent.CompletionException; import javax.security.auth.Subject; +import org.eclipse.jetty.util.JavaVersion; + /** *

Collections of utility methods to deal with the scheduled removal * of the security classes defined by JEP 411.

+ *

To enable usage of a {@link SecurityManager}, the system property {@link #USE_SECURITY_MANAGER} must be set to {@code true} + * for JVMs after version 21.

*/ public class SecurityUtils { - private static final MethodHandle doAs = lookupDoAs(); + public static final boolean USE_SECURITY_MANAGER = Boolean.parseBoolean( + System.getProperty("org.eclipse.jetty.util.security.useSecurityManager", JavaVersion.VERSION.getMajor() <= 21 ? "true" : "false")); + private static final MethodHandle callAs = lookupCallAs(); private static final MethodHandle doPrivileged = lookupDoPrivileged(); - private static MethodHandle lookupDoAs() + private static MethodHandle lookupCallAs() { MethodHandles.Lookup lookup = MethodHandles.lookup(); try @@ -39,14 +45,15 @@ public class SecurityUtils // Subject.doAs() is deprecated for removal and replaced by Subject.callAs(). // Lookup first the new API, since for Java versions where both exists, the // new API delegates to the old API (for example Java 18, 19 and 20). - // Otherwise (Java 17), lookup the old API. return lookup.findStatic(Subject.class, "callAs", MethodType.methodType(Object.class, Subject.class, Callable.class)); } catch (Throwable x) { try { - // Lookup the old API. + if (!USE_SECURITY_MANAGER) + return null; + // Otherwise (Java 17), lookup the old API. MethodType oldSignature = MethodType.methodType(Object.class, Subject.class, PrivilegedAction.class); MethodHandle doAs = lookup.findStatic(Subject.class, "doAs", oldSignature); // Convert the Callable used in the new API to the PrivilegedAction used in the old API. @@ -63,6 +70,8 @@ public class SecurityUtils private static MethodHandle lookupDoPrivileged() { + if (!USE_SECURITY_MANAGER) + return null; try { // Use reflection to work with Java versions that have and don't have AccessController. @@ -84,6 +93,8 @@ public class SecurityUtils { try { + if (!USE_SECURITY_MANAGER) + return null; // Use reflection to work with Java versions that have and don't have SecurityManager. return System.class.getMethod("getSecurityManager").invoke(null); } @@ -102,6 +113,8 @@ public class SecurityUtils */ public static void checkPermission(Permission permission) throws SecurityException { + if (!USE_SECURITY_MANAGER) + return; Object securityManager = SecurityUtils.getSecurityManager(); if (securityManager == null) return; @@ -129,11 +142,9 @@ public class SecurityUtils */ public static T doPrivileged(PrivilegedAction action) { - // Keep this method short and inlineable. - MethodHandle methodHandle = doPrivileged; - if (methodHandle == null) + if (!USE_SECURITY_MANAGER || doPrivileged == null) return action.run(); - return doPrivileged(methodHandle, action); + return doPrivileged(doPrivileged, action); } @SuppressWarnings("unchecked") @@ -153,6 +164,21 @@ public class SecurityUtils } } + /** + *

Runs the given action as the given subject.

+ * + * @param subject the subject this action runs as + * @param action the action to run + * @return the result of the action + * @param the type of the result + * @deprecated use {@link #callAs(Subject, Callable)} + */ + @Deprecated(forRemoval = true, since = "12.1.0") + public static T doAs(Subject subject, Callable action) + { + return callAs(subject, action); + } + /** *

Runs the given action as the given subject.

* @@ -162,14 +188,14 @@ public class SecurityUtils * @param the type of the result */ @SuppressWarnings("unchecked") - public static T doAs(Subject subject, Callable action) + public static T callAs(Subject subject, Callable action) { try { - MethodHandle methodHandle = doAs; - if (methodHandle == null) + if (callAs == null) return action.call(); - return (T)methodHandle.invoke(subject, action); + + return (T)callAs.invoke(subject, action); } catch (RuntimeException | Error x) { diff --git a/jetty-ee11/jetty-ee11-servlet/src/main/java/org/eclipse/jetty/ee11/servlet/ServletContextHandler.java b/jetty-ee11/jetty-ee11-servlet/src/main/java/org/eclipse/jetty/ee11/servlet/ServletContextHandler.java index fb24c0ec10e..2598c650112 100644 --- a/jetty-ee11/jetty-ee11-servlet/src/main/java/org/eclipse/jetty/ee11/servlet/ServletContextHandler.java +++ b/jetty-ee11/jetty-ee11-servlet/src/main/java/org/eclipse/jetty/ee11/servlet/ServletContextHandler.java @@ -108,7 +108,6 @@ import org.eclipse.jetty.util.component.LifeCycle; import org.eclipse.jetty.util.resource.Resource; import org.eclipse.jetty.util.resource.ResourceFactory; import org.eclipse.jetty.util.resource.Resources; -import org.eclipse.jetty.util.security.SecurityUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -219,7 +218,6 @@ public class ServletContextHandler extends ContextHandler private Logger _logger; 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 _usingSecurityManager = getSecurityManager() != null; private final List _programmaticListeners = new CopyOnWriteArrayList<>(); private final List _servletContextListeners = new CopyOnWriteArrayList<>(); @@ -324,16 +322,17 @@ public class ServletContextHandler extends ContextHandler new DumpableCollection("initparams " + this, getInitParams().entrySet())); } + @Deprecated(forRemoval = true, since = "12.1.0") public boolean isUsingSecurityManager() { - return _usingSecurityManager; + return false; } + @Deprecated(forRemoval = true, since = "12.1.0") public void setUsingSecurityManager(boolean usingSecurityManager) { - if (usingSecurityManager && getSecurityManager() == null) - throw new IllegalStateException("No security manager"); - _usingSecurityManager = usingSecurityManager; + if (usingSecurityManager) + throw new UnsupportedOperationException("SecurityManager not supported"); } /** @@ -1712,11 +1711,6 @@ public class ServletContextHandler extends ContextHandler getContext().destroy(listener); } - private static Object getSecurityManager() - { - return SecurityUtils.getSecurityManager(); - } - public static class JspPropertyGroup implements JspPropertyGroupDescriptor { private final List _urlPatterns = new ArrayList<>(); @@ -2980,25 +2974,7 @@ public class ServletContextHandler extends ContextHandler @Override public ClassLoader getClassLoader() { - // no security manager just return the classloader - ClassLoader classLoader = ServletContextHandler.this.getClassLoader(); - if (isUsingSecurityManager()) - { - // 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. - ClassLoader callerLoader = StackWalker.getInstance(StackWalker.Option.RETAIN_CLASS_REFERENCE) - .getCallerClass() - .getClassLoader(); - while (callerLoader != null) - { - if (callerLoader == classLoader) - return classLoader; - else - callerLoader = callerLoader.getParent(); - } - SecurityUtils.checkPermission(new RuntimePermission("getClassLoader")); - } - return classLoader; + return ServletContextHandler.this.getClassLoader(); } public void setEnabled(boolean enabled)