Skip to content

Commit

Permalink
Allow whitelist of packages that can exit
Browse files Browse the repository at this point in the history
Today, SecureSM has a mechanism that enables a hardcoded list of test
packages to exit if the SecureSM instance is constructed with a boolean
flag indicating that these packages will be permitted to exit. This
commit replaces this mechanism by allowing the SecureSM instance to be
constructed with a whitelist of packages that can exit.

Relates elastic#4
  • Loading branch information
jasontedor authored Jul 5, 2016
1 parent 06817ea commit 5924bb9
Show file tree
Hide file tree
Showing 2 changed files with 54 additions and 38 deletions.
83 changes: 50 additions & 33 deletions main/java/org/elasticsearch/SecureSM.java
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,8 @@
* 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 testing mode
* where calls from test runners are allowed.
* 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>
Expand All @@ -62,23 +62,51 @@
* http://cs.oswego.edu/pipermail/concurrency-interest/2009-August/006508.html</a>
*/
public class SecureSM extends SecurityManager {
private final boolean allowTestExit;


private final String[] packagesThatCanExit;

/**
* Create a new SecurityManager.
* Creates a new security manager where no packages can exit nor halt the virtual machine.
*/
public SecureSM() {
this(false);
this(new String[0]);
}

/**
* Expert: for testing only.
* @param allowTestExit {@code true} if test-runners should be allowed to exit the VM.

/**
* Creates a new security manager with the specified list of packages being the only packages
* that can exit or halt the virtual machine.
*
* @param packagesThatCanExit the list of packages that can exit or halt the virtual machine
*/
public SecureSM(boolean allowTestExit) {
this.allowTestExit = allowTestExit;
public SecureSM(final String[] packagesThatCanExit) {
this.packagesThatCanExit = packagesThatCanExit;
}


/**
* 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
* <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>
*
* @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);
}

private static final String[] TEST_RUNNER_PACKAGES = new String[] {
// surefire test runner
"org.apache.maven.surefire.booter.",
// junit4 test runner
"com.carrotsearch.ant.tasks.junit4.",
// eclipse test runner
"org.eclipse.jdt.internal.junit.runner.",
// intellij test runner
"com.intellij.rt.execution.junit."
};

// java.security.debug support
private static final boolean DEBUG = AccessController.doPrivileged(new PrivilegedAction<Boolean>() {
@Override
Expand Down Expand Up @@ -170,28 +198,13 @@ protected void checkThreadGroupAccess(ThreadGroup g) {
// exit permission logic
@Override
public void checkExit(int status) {
if (allowTestExit) {
checkTestExit(status);
} else {
throw new SecurityException("exit(" + status + ") not allowed by system policy");
}
innerCheckExit(status);
}

static final String TEST_RUNNER_PACKAGES[] = {
// surefire test runner
"org.apache.maven.surefire.booter.",
// junit4 test runner
"com.carrotsearch.ant.tasks.junit4.",
// eclipse test runner
"org.eclipse.jdt.internal.junit.runner.",
// intellij test runner
"com.intellij.rt.execution.junit."
};

/**
* The "Uwe Schindler" algorithm.
*/
protected void checkTestExit(final int status) {
protected void innerCheckExit(final int status) {
AccessController.doPrivileged(new PrivilegedAction<Void>() {
@Override
public Void run() {
Expand All @@ -209,8 +222,11 @@ public Void run() {
}

if (exitMethodHit != null) {
for (String testPackage : TEST_RUNNER_PACKAGES) {
if (className.startsWith(testPackage)) {
if (packagesThatCanExit == null) {
break;
}
for (String packageThatCanExit : packagesThatCanExit) {
if (className.startsWith(packageThatCanExit)) {
// this exit point is allowed, we return normally from closure:
return null;
}
Expand All @@ -224,11 +240,12 @@ public Void run() {
// should never happen, only if JVM hides stack trace - replace by generic:
exitMethodHit = "JVM exit method";
}
throw new SecurityException(exitMethodHit + " calls are not allowed because they terminate the test runner's JVM.");
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);
}

}
9 changes: 4 additions & 5 deletions test/java/org/elasticsearch/TestSecureSM.java
Original file line number Diff line number Diff line change
Expand Up @@ -19,15 +19,14 @@
* under the License.
*/

import junit.framework.TestCase;
import org.junit.Test;

import java.security.Permission;
import java.security.Policy;
import java.security.ProtectionDomain;
import java.util.concurrent.atomic.AtomicBoolean;

import org.junit.Test;

import junit.framework.TestCase;

/** Simple tests for SecureSM */
public class TestSecureSM extends TestCase {
static {
Expand All @@ -46,7 +45,7 @@ public boolean implies(ProtectionDomain domain, Permission permission) {
return true;
}
});
System.setSecurityManager(new SecureSM(true));
System.setSecurityManager(SecureSM.createTestSecureSM());
}

@Test
Expand Down

0 comments on commit 5924bb9

Please sign in to comment.