mirror of
https://github.com/elastic/elasticsearch.git
synced 2025-06-28 17:34:17 -04:00
Remove custom SecurityManager (#127778)
Since SecurityManager is no longer used, the custom subclass of SecurityManager, SecureSM, is no longer needed.
This commit is contained in:
parent
197d9b32cc
commit
60ad8ba744
10 changed files with 0 additions and 592 deletions
|
@ -60,7 +60,6 @@ public class InternalDistributionModuleCheckTaskProvider {
|
||||||
"org.elasticsearch.nativeaccess",
|
"org.elasticsearch.nativeaccess",
|
||||||
"org.elasticsearch.plugin",
|
"org.elasticsearch.plugin",
|
||||||
"org.elasticsearch.plugin.analysis",
|
"org.elasticsearch.plugin.analysis",
|
||||||
"org.elasticsearch.securesm",
|
|
||||||
"org.elasticsearch.server",
|
"org.elasticsearch.server",
|
||||||
"org.elasticsearch.simdvec",
|
"org.elasticsearch.simdvec",
|
||||||
"org.elasticsearch.tdigest",
|
"org.elasticsearch.tdigest",
|
||||||
|
|
|
@ -1,32 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
|
||||||
* or more contributor license agreements. Licensed under the "Elastic License
|
|
||||||
* 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side
|
|
||||||
* Public License v 1"; you may not use this file except in compliance with, at
|
|
||||||
* your election, the "Elastic License 2.0", the "GNU Affero General Public
|
|
||||||
* License v3.0 only", or the "Server Side Public License, v 1".
|
|
||||||
*/
|
|
||||||
apply plugin: 'elasticsearch.publish'
|
|
||||||
|
|
||||||
dependencies {
|
|
||||||
// do not add non-test compile dependencies to secure-sm without a good reason to do so
|
|
||||||
|
|
||||||
testImplementation "com.carrotsearch.randomizedtesting:randomizedtesting-runner:${versions.randomizedrunner}"
|
|
||||||
testImplementation "junit:junit:${versions.junit}"
|
|
||||||
testImplementation "org.hamcrest:hamcrest:${versions.hamcrest}"
|
|
||||||
|
|
||||||
testImplementation(project(":test:framework")) {
|
|
||||||
exclude group: 'org.elasticsearch', module: 'secure-sm'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
tasks.named('forbiddenApisMain').configure {
|
|
||||||
replaceSignatureFiles 'jdk-signatures'
|
|
||||||
}
|
|
||||||
|
|
||||||
// JAR hell is part of core which we do not want to add as a dependency
|
|
||||||
tasks.named("jarHell").configure { enabled = false }
|
|
||||||
tasks.named("testTestingConventions").configure {
|
|
||||||
baseClass 'junit.framework.TestCase'
|
|
||||||
baseClass 'org.junit.Assert'
|
|
||||||
}
|
|
|
@ -1,12 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
|
||||||
* or more contributor license agreements. Licensed under the "Elastic License
|
|
||||||
* 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side
|
|
||||||
* Public License v 1"; you may not use this file except in compliance with, at
|
|
||||||
* your election, the "Elastic License 2.0", the "GNU Affero General Public
|
|
||||||
* License v3.0 only", or the "Server Side Public License, v 1".
|
|
||||||
*/
|
|
||||||
|
|
||||||
module org.elasticsearch.securesm {
|
|
||||||
exports org.elasticsearch.secure_sm;
|
|
||||||
}
|
|
|
@ -1,275 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
|
||||||
* or more contributor license agreements. Licensed under the "Elastic License
|
|
||||||
* 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side
|
|
||||||
* Public License v 1"; you may not use this file except in compliance with, at
|
|
||||||
* your election, the "Elastic License 2.0", the "GNU Affero General Public
|
|
||||||
* License v3.0 only", or the "Server Side Public License, v 1".
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.elasticsearch.secure_sm;
|
|
||||||
|
|
||||||
import java.security.AccessController;
|
|
||||||
import java.security.Permission;
|
|
||||||
import java.security.PrivilegedAction;
|
|
||||||
import java.util.Objects;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Extension of SecurityManager that works around a few design flaws in Java Security.
|
|
||||||
* <p>
|
|
||||||
* There are a few major problems that require custom {@code SecurityManager} logic to fix:
|
|
||||||
* <ul>
|
|
||||||
* <li>{@code exitVM} permission is implicitly granted to all code by the default
|
|
||||||
* Policy implementation. For a server app, this is not wanted. </li>
|
|
||||||
* <li>ThreadGroups are not enforced by default, instead only system threads are
|
|
||||||
* protected out of box by {@code modifyThread/modifyThreadGroup}. Applications
|
|
||||||
* are encouraged to override the logic here to implement a stricter policy.
|
|
||||||
* <li>System threads are not even really protected, because if the system uses
|
|
||||||
* ThreadPools, {@code modifyThread} is abused by its {@code shutdown} checks. This means
|
|
||||||
* a thread must have {@code modifyThread} to even terminate its own pool, leaving
|
|
||||||
* system threads unprotected.
|
|
||||||
* </ul>
|
|
||||||
* This class throws exception on {@code exitVM} calls, and provides a whitelist where calls
|
|
||||||
* from exit are allowed.
|
|
||||||
* <p>
|
|
||||||
* Additionally it enforces threadgroup security with the following rules:
|
|
||||||
* <ul>
|
|
||||||
* <li>{@code modifyThread} and {@code modifyThreadGroup} are required for any thread access
|
|
||||||
* checks: with these permissions, access is granted as long as the thread group is
|
|
||||||
* the same or an ancestor ({@code sourceGroup.parentOf(targetGroup) == true}).
|
|
||||||
* <li>code without these permissions can do very little, except to interrupt itself. It may
|
|
||||||
* not even create new threads.
|
|
||||||
* <li>very special cases (like test runners) that have {@link ThreadPermission} can violate
|
|
||||||
* threadgroup security rules.
|
|
||||||
* </ul>
|
|
||||||
* <p>
|
|
||||||
* If java security debugging ({@code java.security.debug}) is enabled, and this SecurityManager
|
|
||||||
* is installed, it will emit additional debugging information when threadgroup access checks fail.
|
|
||||||
*
|
|
||||||
* @see SecurityManager#checkAccess(Thread)
|
|
||||||
* @see SecurityManager#checkAccess(ThreadGroup)
|
|
||||||
* @see <a href="http://cs.oswego.edu/pipermail/concurrency-interest/2009-August/006508.html">
|
|
||||||
* http://cs.oswego.edu/pipermail/concurrency-interest/2009-August/006508.html</a>
|
|
||||||
*/
|
|
||||||
public class SecureSM extends SecurityManager {
|
|
||||||
|
|
||||||
private final String[] classesThatCanExit;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a new security manager where no packages can exit nor halt the virtual machine.
|
|
||||||
*/
|
|
||||||
public SecureSM() {
|
|
||||||
this(new String[0]);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a new security manager with the specified list of regular expressions as the those that class names will be tested against to
|
|
||||||
* check whether or not a class can exit or halt the virtual machine.
|
|
||||||
*
|
|
||||||
* @param classesThatCanExit the list of classes that can exit or halt the virtual machine
|
|
||||||
*/
|
|
||||||
public SecureSM(final String[] classesThatCanExit) {
|
|
||||||
this.classesThatCanExit = classesThatCanExit;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a new security manager with a standard set of test packages being the only packages that can exit or halt the virtual
|
|
||||||
* machine. The packages that can exit are:
|
|
||||||
* <ul>
|
|
||||||
* <li><code>org.apache.maven.surefire.booter.</code></li>
|
|
||||||
* <li><code>com.carrotsearch.ant.tasks.junit4.</code></li>
|
|
||||||
* <li><code>org.eclipse.internal.junit.runner.</code></li>
|
|
||||||
* <li><code>com.intellij.rt.execution.junit.</code></li>
|
|
||||||
* </ul>
|
|
||||||
*
|
|
||||||
* @return an instance of SecureSM where test packages can halt or exit the virtual machine
|
|
||||||
*/
|
|
||||||
public static SecureSM createTestSecureSM() {
|
|
||||||
return new SecureSM(TEST_RUNNER_PACKAGES);
|
|
||||||
}
|
|
||||||
|
|
||||||
static final String[] TEST_RUNNER_PACKAGES = new String[] {
|
|
||||||
// surefire test runner
|
|
||||||
"org\\.apache\\.maven\\.surefire\\.booter\\..*",
|
|
||||||
// junit4 test runner
|
|
||||||
"com\\.carrotsearch\\.ant\\.tasks\\.junit4\\.slave\\..*",
|
|
||||||
// eclipse test runner
|
|
||||||
"org\\.eclipse.jdt\\.internal\\.junit\\.runner\\..*",
|
|
||||||
// intellij test runner (before IDEA version 2019.3)
|
|
||||||
"com\\.intellij\\.rt\\.execution\\.junit\\..*",
|
|
||||||
// intellij test runner (since IDEA version 2019.3)
|
|
||||||
"com\\.intellij\\.rt\\.junit\\..*" };
|
|
||||||
|
|
||||||
// java.security.debug support
|
|
||||||
private static final boolean DEBUG = AccessController.doPrivileged(new PrivilegedAction<Boolean>() {
|
|
||||||
@Override
|
|
||||||
public Boolean run() {
|
|
||||||
try {
|
|
||||||
String v = System.getProperty("java.security.debug");
|
|
||||||
// simple check that they are trying to debug
|
|
||||||
return v != null && v.length() > 0;
|
|
||||||
} catch (SecurityException e) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
@Override
|
|
||||||
@SuppressForbidden(reason = "java.security.debug messages go to standard error")
|
|
||||||
public void checkAccess(Thread t) {
|
|
||||||
try {
|
|
||||||
checkThreadAccess(t);
|
|
||||||
} catch (SecurityException e) {
|
|
||||||
if (DEBUG) {
|
|
||||||
System.err.println("access: caller thread=" + Thread.currentThread());
|
|
||||||
System.err.println("access: target thread=" + t);
|
|
||||||
debugThreadGroups(Thread.currentThread().getThreadGroup(), t.getThreadGroup());
|
|
||||||
}
|
|
||||||
throw e;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
@SuppressForbidden(reason = "java.security.debug messages go to standard error")
|
|
||||||
public void checkAccess(ThreadGroup g) {
|
|
||||||
try {
|
|
||||||
checkThreadGroupAccess(g);
|
|
||||||
} catch (SecurityException e) {
|
|
||||||
if (DEBUG) {
|
|
||||||
System.err.println("access: caller thread=" + Thread.currentThread());
|
|
||||||
debugThreadGroups(Thread.currentThread().getThreadGroup(), g);
|
|
||||||
}
|
|
||||||
throw e;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressForbidden(reason = "java.security.debug messages go to standard error")
|
|
||||||
private static void debugThreadGroups(final ThreadGroup caller, final ThreadGroup target) {
|
|
||||||
System.err.println("access: caller group=" + caller);
|
|
||||||
System.err.println("access: target group=" + target);
|
|
||||||
}
|
|
||||||
|
|
||||||
// thread permission logic
|
|
||||||
|
|
||||||
private static final Permission MODIFY_THREAD_PERMISSION = new RuntimePermission("modifyThread");
|
|
||||||
private static final Permission MODIFY_ARBITRARY_THREAD_PERMISSION = new ThreadPermission("modifyArbitraryThread");
|
|
||||||
|
|
||||||
// Returns true if the given thread is an instance of the JDK's InnocuousThread.
|
|
||||||
private static boolean isInnocuousThread(Thread t) {
|
|
||||||
final Class<?> c = t.getClass();
|
|
||||||
return c.getModule() == Object.class.getModule()
|
|
||||||
&& (c.getName().equals("jdk.internal.misc.InnocuousThread")
|
|
||||||
|| c.getName().equals("java.util.concurrent.ForkJoinWorkerThread$InnocuousForkJoinWorkerThread"));
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void checkThreadAccess(Thread t) {
|
|
||||||
Objects.requireNonNull(t);
|
|
||||||
|
|
||||||
boolean targetThreadIsInnocuous = isInnocuousThread(t);
|
|
||||||
// we don't need to check if innocuous thread is modifying itself (like changes its name)
|
|
||||||
if (Thread.currentThread() != t || targetThreadIsInnocuous == false) {
|
|
||||||
// first, check if we can modify threads at all.
|
|
||||||
checkPermission(MODIFY_THREAD_PERMISSION);
|
|
||||||
}
|
|
||||||
|
|
||||||
// check the threadgroup, if its our thread group or an ancestor, its fine.
|
|
||||||
final ThreadGroup source = Thread.currentThread().getThreadGroup();
|
|
||||||
final ThreadGroup target = t.getThreadGroup();
|
|
||||||
|
|
||||||
if (target == null) {
|
|
||||||
return; // its a dead thread, do nothing.
|
|
||||||
} else if (source.parentOf(target) == false && targetThreadIsInnocuous == false) {
|
|
||||||
checkPermission(MODIFY_ARBITRARY_THREAD_PERMISSION);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static final Permission MODIFY_THREADGROUP_PERMISSION = new RuntimePermission("modifyThreadGroup");
|
|
||||||
private static final Permission MODIFY_ARBITRARY_THREADGROUP_PERMISSION = new ThreadPermission("modifyArbitraryThreadGroup");
|
|
||||||
|
|
||||||
// Returns true if the given thread is an instance of the JDK's InnocuousThread.
|
|
||||||
private static boolean isInnocuousThreadGroup(ThreadGroup t) {
|
|
||||||
final Class<?> c = t.getClass();
|
|
||||||
return c.getModule() == Object.class.getModule() && t.getName().equals("InnocuousForkJoinWorkerThreadGroup");
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void checkThreadGroupAccess(ThreadGroup g) {
|
|
||||||
Objects.requireNonNull(g);
|
|
||||||
|
|
||||||
boolean targetThreadGroupIsInnocuous = isInnocuousThreadGroup(g);
|
|
||||||
|
|
||||||
// first, check if we can modify thread groups at all.
|
|
||||||
if (targetThreadGroupIsInnocuous == false) {
|
|
||||||
checkPermission(MODIFY_THREADGROUP_PERMISSION);
|
|
||||||
}
|
|
||||||
|
|
||||||
// check the threadgroup, if its our thread group or an ancestor, its fine.
|
|
||||||
final ThreadGroup source = Thread.currentThread().getThreadGroup();
|
|
||||||
final ThreadGroup target = g;
|
|
||||||
|
|
||||||
if (source == null) {
|
|
||||||
return; // we are a dead thread, do nothing
|
|
||||||
} else if (source.parentOf(target) == false && targetThreadGroupIsInnocuous == false) {
|
|
||||||
checkPermission(MODIFY_ARBITRARY_THREADGROUP_PERMISSION);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// exit permission logic
|
|
||||||
@Override
|
|
||||||
public void checkExit(int status) {
|
|
||||||
innerCheckExit(status);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The "Uwe Schindler" algorithm.
|
|
||||||
*
|
|
||||||
* @param status the exit status
|
|
||||||
*/
|
|
||||||
protected void innerCheckExit(final int status) {
|
|
||||||
AccessController.doPrivileged(new PrivilegedAction<Void>() {
|
|
||||||
@Override
|
|
||||||
public Void run() {
|
|
||||||
final String systemClassName = System.class.getName(), runtimeClassName = Runtime.class.getName();
|
|
||||||
String exitMethodHit = null;
|
|
||||||
for (final StackTraceElement se : Thread.currentThread().getStackTrace()) {
|
|
||||||
final String className = se.getClassName(), methodName = se.getMethodName();
|
|
||||||
if (("exit".equals(methodName) || "halt".equals(methodName))
|
|
||||||
&& (systemClassName.equals(className) || runtimeClassName.equals(className))) {
|
|
||||||
exitMethodHit = className + '#' + methodName + '(' + status + ')';
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (exitMethodHit != null) {
|
|
||||||
if (classesThatCanExit == null) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (classCanExit(className, classesThatCanExit)) {
|
|
||||||
// this exit point is allowed, we return normally from closure:
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
// anything else in stack trace is not allowed, break and throw SecurityException below:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (exitMethodHit == null) {
|
|
||||||
// should never happen, only if JVM hides stack trace - replace by generic:
|
|
||||||
exitMethodHit = "JVM exit method";
|
|
||||||
}
|
|
||||||
throw new SecurityException(exitMethodHit + " calls are not allowed");
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// we passed the stack check, delegate to super, so default policy can still deny permission:
|
|
||||||
super.checkExit(status);
|
|
||||||
}
|
|
||||||
|
|
||||||
static boolean classCanExit(final String className, final String[] classesThatCanExit) {
|
|
||||||
for (final String classThatCanExit : classesThatCanExit) {
|
|
||||||
if (className.matches(classThatCanExit)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,24 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
|
||||||
* or more contributor license agreements. Licensed under the "Elastic License
|
|
||||||
* 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side
|
|
||||||
* Public License v 1"; you may not use this file except in compliance with, at
|
|
||||||
* your election, the "Elastic License 2.0", the "GNU Affero General Public
|
|
||||||
* License v3.0 only", or the "Server Side Public License, v 1".
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.elasticsearch.secure_sm;
|
|
||||||
|
|
||||||
import java.lang.annotation.ElementType;
|
|
||||||
import java.lang.annotation.Retention;
|
|
||||||
import java.lang.annotation.RetentionPolicy;
|
|
||||||
import java.lang.annotation.Target;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Annotation to suppress forbidden-apis errors inside a whole class, a method, or a field.
|
|
||||||
*/
|
|
||||||
@Retention(RetentionPolicy.CLASS)
|
|
||||||
@Target({ ElementType.CONSTRUCTOR, ElementType.FIELD, ElementType.METHOD, ElementType.TYPE })
|
|
||||||
@interface SuppressForbidden {
|
|
||||||
String reason();
|
|
||||||
}
|
|
|
@ -1,50 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
|
||||||
* or more contributor license agreements. Licensed under the "Elastic License
|
|
||||||
* 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side
|
|
||||||
* Public License v 1"; you may not use this file except in compliance with, at
|
|
||||||
* your election, the "Elastic License 2.0", the "GNU Affero General Public
|
|
||||||
* License v3.0 only", or the "Server Side Public License, v 1".
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.elasticsearch.secure_sm;
|
|
||||||
|
|
||||||
import java.security.BasicPermission;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Permission to modify threads or thread groups normally not accessible
|
|
||||||
* to the current thread.
|
|
||||||
* <p>
|
|
||||||
* {@link SecureSM} enforces ThreadGroup security: threads with
|
|
||||||
* {@code RuntimePermission("modifyThread")} or {@code RuntimePermission("modifyThreadGroup")}
|
|
||||||
* are only allowed to modify their current thread group or an ancestor of that group.
|
|
||||||
* <p>
|
|
||||||
* In some cases (e.g. test runners), code needs to manipulate arbitrary threads,
|
|
||||||
* so this Permission provides for that: the targets {@code modifyArbitraryThread} and
|
|
||||||
* {@code modifyArbitraryThreadGroup} allow a thread blanket access to any group.
|
|
||||||
*
|
|
||||||
* @see ThreadGroup
|
|
||||||
* @see SecureSM
|
|
||||||
*/
|
|
||||||
public final class ThreadPermission extends BasicPermission {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a new ThreadPermission object.
|
|
||||||
*
|
|
||||||
* @param name target name
|
|
||||||
*/
|
|
||||||
public ThreadPermission(String name) {
|
|
||||||
super(name);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a new ThreadPermission object.
|
|
||||||
* This constructor exists for use by the {@code Policy} object to instantiate new Permission objects.
|
|
||||||
*
|
|
||||||
* @param name target name
|
|
||||||
* @param actions ignored
|
|
||||||
*/
|
|
||||||
public ThreadPermission(String name, String actions) {
|
|
||||||
super(name, actions);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,158 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
|
||||||
* or more contributor license agreements. Licensed under the "Elastic License
|
|
||||||
* 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side
|
|
||||||
* Public License v 1"; you may not use this file except in compliance with, at
|
|
||||||
* your election, the "Elastic License 2.0", the "GNU Affero General Public
|
|
||||||
* License v3.0 only", or the "Server Side Public License, v 1".
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.elasticsearch.secure_sm;
|
|
||||||
|
|
||||||
import com.carrotsearch.randomizedtesting.JUnit3MethodProvider;
|
|
||||||
import com.carrotsearch.randomizedtesting.RandomizedRunner;
|
|
||||||
import com.carrotsearch.randomizedtesting.RandomizedTest;
|
|
||||||
import com.carrotsearch.randomizedtesting.annotations.TestMethodProviders;
|
|
||||||
|
|
||||||
import org.elasticsearch.jdk.RuntimeVersionFeature;
|
|
||||||
import org.junit.BeforeClass;
|
|
||||||
import org.junit.runner.RunWith;
|
|
||||||
|
|
||||||
import java.security.Permission;
|
|
||||||
import java.security.Policy;
|
|
||||||
import java.security.ProtectionDomain;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Set;
|
|
||||||
import java.util.concurrent.atomic.AtomicBoolean;
|
|
||||||
import java.util.stream.Collectors;
|
|
||||||
|
|
||||||
/** Simple tests for SecureSM */
|
|
||||||
@TestMethodProviders({ JUnit3MethodProvider.class })
|
|
||||||
@RunWith(RandomizedRunner.class)
|
|
||||||
public class SecureSMTests extends org.junit.Assert {
|
|
||||||
|
|
||||||
@BeforeClass
|
|
||||||
public static void initialize() {
|
|
||||||
RandomizedTest.assumeFalse(
|
|
||||||
"SecurityManager has been permanently removed in JDK 24",
|
|
||||||
RuntimeVersionFeature.isSecurityManagerAvailable() == false
|
|
||||||
);
|
|
||||||
// install a mock security policy:
|
|
||||||
// AllPermission to source code
|
|
||||||
// ThreadPermission not granted anywhere else
|
|
||||||
final var sourceCode = Set.of(SecureSM.class.getProtectionDomain(), RandomizedRunner.class.getProtectionDomain());
|
|
||||||
Policy.setPolicy(new Policy() {
|
|
||||||
@Override
|
|
||||||
public boolean implies(ProtectionDomain domain, Permission permission) {
|
|
||||||
if (sourceCode.contains(domain)) {
|
|
||||||
return true;
|
|
||||||
} else if (permission instanceof ThreadPermission) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
System.setSecurityManager(SecureSM.createTestSecureSM());
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressForbidden(reason = "testing that System#exit is blocked")
|
|
||||||
public void testTryToExit() {
|
|
||||||
try {
|
|
||||||
System.exit(1);
|
|
||||||
fail("did not hit expected exception");
|
|
||||||
} catch (SecurityException expected) {}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void testClassCanExit() {
|
|
||||||
assertTrue(SecureSM.classCanExit("org.apache.maven.surefire.booter.CommandReader", SecureSM.TEST_RUNNER_PACKAGES));
|
|
||||||
assertTrue(SecureSM.classCanExit("com.carrotsearch.ant.tasks.junit4.slave.JvmExit", SecureSM.TEST_RUNNER_PACKAGES));
|
|
||||||
assertTrue(SecureSM.classCanExit("org.eclipse.jdt.internal.junit.runner.RemoteTestRunner", SecureSM.TEST_RUNNER_PACKAGES));
|
|
||||||
assertTrue(SecureSM.classCanExit("com.intellij.rt.execution.junit.JUnitStarter", SecureSM.TEST_RUNNER_PACKAGES));
|
|
||||||
assertTrue(SecureSM.classCanExit("org.elasticsearch.Foo", new String[] { "org.elasticsearch.Foo" }));
|
|
||||||
assertFalse(SecureSM.classCanExit("org.elasticsearch.Foo", new String[] { "org.elasticsearch.Bar" }));
|
|
||||||
}
|
|
||||||
|
|
||||||
public void testCreateThread() throws Exception {
|
|
||||||
Thread t = new Thread();
|
|
||||||
t.start();
|
|
||||||
t.join();
|
|
||||||
// no exception
|
|
||||||
}
|
|
||||||
|
|
||||||
public void testCreateThreadGroup() throws Exception {
|
|
||||||
Thread t = new Thread(new ThreadGroup("childgroup"), "child");
|
|
||||||
t.start();
|
|
||||||
t.join();
|
|
||||||
// no exception
|
|
||||||
}
|
|
||||||
|
|
||||||
public void testModifyChild() throws Exception {
|
|
||||||
final AtomicBoolean interrupted = new AtomicBoolean(false);
|
|
||||||
Thread t = new Thread(new ThreadGroup("childgroup"), "child") {
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
try {
|
|
||||||
Thread.sleep(Long.MAX_VALUE);
|
|
||||||
} catch (InterruptedException expected) {
|
|
||||||
interrupted.set(true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
t.start();
|
|
||||||
t.interrupt();
|
|
||||||
t.join();
|
|
||||||
// no exception
|
|
||||||
assertTrue(interrupted.get());
|
|
||||||
}
|
|
||||||
|
|
||||||
public void testNoModifySibling() throws Exception {
|
|
||||||
final AtomicBoolean interrupted1 = new AtomicBoolean(false);
|
|
||||||
final AtomicBoolean interrupted2 = new AtomicBoolean(false);
|
|
||||||
|
|
||||||
final Thread t1 = new Thread(new ThreadGroup("childgroup"), "child") {
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
try {
|
|
||||||
Thread.sleep(Long.MAX_VALUE);
|
|
||||||
} catch (InterruptedException expected) {
|
|
||||||
interrupted1.set(true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
t1.start();
|
|
||||||
|
|
||||||
Thread t2 = new Thread(new ThreadGroup("anothergroup"), "another child") {
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
try {
|
|
||||||
Thread.sleep(Long.MAX_VALUE);
|
|
||||||
} catch (InterruptedException expected) {
|
|
||||||
interrupted2.set(true);
|
|
||||||
try {
|
|
||||||
t1.interrupt(); // try to bogusly interrupt our sibling
|
|
||||||
fail("did not hit expected exception");
|
|
||||||
} catch (SecurityException expected2) {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
t2.start();
|
|
||||||
t2.interrupt();
|
|
||||||
t2.join();
|
|
||||||
// sibling attempted to but was not able to muck with its other sibling
|
|
||||||
assertTrue(interrupted2.get());
|
|
||||||
assertFalse(interrupted1.get());
|
|
||||||
// but we are the parent and can terminate
|
|
||||||
t1.interrupt();
|
|
||||||
t1.join();
|
|
||||||
assertTrue(interrupted1.get());
|
|
||||||
}
|
|
||||||
|
|
||||||
public void testParallelStreamThreadGroup() throws Exception {
|
|
||||||
List<Integer> list = new ArrayList<>();
|
|
||||||
for (int i = 0; i < 100; ++i) {
|
|
||||||
list.add(i);
|
|
||||||
}
|
|
||||||
list.parallelStream().collect(Collectors.toSet());
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,38 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
|
||||||
* or more contributor license agreements. Licensed under the "Elastic License
|
|
||||||
* 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side
|
|
||||||
* Public License v 1"; you may not use this file except in compliance with, at
|
|
||||||
* your election, the "Elastic License 2.0", the "GNU Affero General Public
|
|
||||||
* License v3.0 only", or the "Server Side Public License, v 1".
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.elasticsearch.secure_sm;
|
|
||||||
|
|
||||||
import junit.framework.TestCase;
|
|
||||||
|
|
||||||
import java.security.AllPermission;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Simple tests for ThreadPermission
|
|
||||||
*/
|
|
||||||
public class ThreadPermissionTests extends TestCase {
|
|
||||||
|
|
||||||
public void testEquals() {
|
|
||||||
assertEquals(new ThreadPermission("modifyArbitraryThread"), new ThreadPermission("modifyArbitraryThread"));
|
|
||||||
assertFalse(new ThreadPermission("modifyArbitraryThread").equals(new AllPermission()));
|
|
||||||
assertFalse(new ThreadPermission("modifyArbitraryThread").equals(new ThreadPermission("modifyArbitraryThreadGroup")));
|
|
||||||
}
|
|
||||||
|
|
||||||
public void testImplies() {
|
|
||||||
assertTrue(new ThreadPermission("modifyArbitraryThread").implies(new ThreadPermission("modifyArbitraryThread")));
|
|
||||||
assertTrue(new ThreadPermission("modifyArbitraryThreadGroup").implies(new ThreadPermission("modifyArbitraryThreadGroup")));
|
|
||||||
assertFalse(new ThreadPermission("modifyArbitraryThread").implies(new ThreadPermission("modifyArbitraryThreadGroup")));
|
|
||||||
assertFalse(new ThreadPermission("modifyArbitraryThreadGroup").implies(new ThreadPermission("modifyArbitraryThread")));
|
|
||||||
assertFalse(new ThreadPermission("modifyArbitraryThread").implies(new AllPermission()));
|
|
||||||
assertFalse(new ThreadPermission("modifyArbitraryThreadGroup").implies(new AllPermission()));
|
|
||||||
assertTrue(new ThreadPermission("*").implies(new ThreadPermission("modifyArbitraryThread")));
|
|
||||||
assertTrue(new ThreadPermission("*").implies(new ThreadPermission("modifyArbitraryThreadGroup")));
|
|
||||||
assertFalse(new ThreadPermission("*").implies(new AllPermission()));
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -28,7 +28,6 @@ dependencies {
|
||||||
|
|
||||||
api project(':libs:core')
|
api project(':libs:core')
|
||||||
api project(':libs:logging')
|
api project(':libs:logging')
|
||||||
api project(':libs:secure-sm')
|
|
||||||
api project(':libs:x-content')
|
api project(':libs:x-content')
|
||||||
api project(":libs:geo")
|
api project(":libs:geo")
|
||||||
api project(":libs:lz4")
|
api project(":libs:lz4")
|
||||||
|
|
|
@ -23,7 +23,6 @@ module org.elasticsearch.server {
|
||||||
requires org.elasticsearch.nativeaccess;
|
requires org.elasticsearch.nativeaccess;
|
||||||
requires org.elasticsearch.geo;
|
requires org.elasticsearch.geo;
|
||||||
requires org.elasticsearch.lz4;
|
requires org.elasticsearch.lz4;
|
||||||
requires org.elasticsearch.securesm;
|
|
||||||
requires org.elasticsearch.xcontent;
|
requires org.elasticsearch.xcontent;
|
||||||
requires org.elasticsearch.logging;
|
requires org.elasticsearch.logging;
|
||||||
requires org.elasticsearch.plugin;
|
requires org.elasticsearch.plugin;
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue