Tighten which classes can exit

Today a SecureSM security manager allows defining a list of packages
that can exit the VM. However, today there are no restrictions on
defining a package inside another JAR. This commit strengthens the
ability to prevent exit by allowing construction of SecureSM to be done
with a list of regular expressions (instead of a list of prefix names)
that classes will be tested against. With this, a security manager can
be installed that permits only exiting from an exact list of classes.

Relates #5
This commit is contained in:
Jason Tedor 2017-11-21 09:29:48 -05:00
parent 09057f24b5
commit e6bd34e22c
2 changed files with 40 additions and 19 deletions

View File

@ -63,7 +63,7 @@ import java.util.Objects;
*/ */
public class SecureSM extends SecurityManager { public class SecureSM extends SecurityManager {
private final String[] packagesThatCanExit; private final String[] classesThatCanExit;
/** /**
* Creates a new security manager where no packages can exit nor halt the virtual machine. * Creates a new security manager where no packages can exit nor halt the virtual machine.
@ -73,22 +73,24 @@ public class SecureSM extends SecurityManager {
} }
/** /**
* Creates a new security manager with the specified list of packages being the only packages * Creates a new security manager with the specified list of regular expressions as the those that class names will be tested against to
* that can exit or halt the virtual machine. * check whether or not a class can exit or halt the virtual machine.
* *
* @param packagesThatCanExit the list of packages that 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[] packagesThatCanExit) { public SecureSM(final String[] classesThatCanExit) {
this.packagesThatCanExit = packagesThatCanExit; this.classesThatCanExit = classesThatCanExit;
} }
/** /**
* Creates a new security manager with a standard set of test packages being the only packages * Creates a new security manager with a standard set of test packages being the only packages that can exit or halt the virtual machine.
* that can exit or halt the virtual machine. The packages that can exit are * The packages that can exit are:
* <ul>
* <li><code>org.apache.maven.surefire.booter.</code></li> * <li><code>org.apache.maven.surefire.booter.</code></li>
* <li><code>com.carrotsearch.ant.tasks.junit4.</code></li> * <li><code>com.carrotsearch.ant.tasks.junit4.</code></li>
* <li><code>org.eclipse.internal.junit.runner.</code></li> * <li><code>org.eclipse.internal.junit.runner.</code></li>
* <li><code>com.intellij.rt.execution.junit.</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 * @return an instance of SecureSM where test packages can halt or exit the virtual machine
*/ */
@ -96,15 +98,15 @@ public class SecureSM extends SecurityManager {
return new SecureSM(TEST_RUNNER_PACKAGES); return new SecureSM(TEST_RUNNER_PACKAGES);
} }
private static final String[] TEST_RUNNER_PACKAGES = new String[] { static final String[] TEST_RUNNER_PACKAGES = new String[] {
// surefire test runner // surefire test runner
"org.apache.maven.surefire.booter.", "org\\.apache\\.maven\\.surefire\\.booter\\..*",
// junit4 test runner // junit4 test runner
"com.carrotsearch.ant.tasks.junit4.", "com\\.carrotsearch\\.ant\\.tasks\\.junit4\\.slave\\..*",
// eclipse test runner // eclipse test runner
"org.eclipse.jdt.internal.junit.runner.", "org\\.eclipse.jdt\\.internal\\.junit\\.runner\\..*",
// intellij test runner // intellij test runner
"com.intellij.rt.execution.junit." "com\\.intellij\\.rt\\.execution\\.junit\\..*"
}; };
// java.security.debug support // java.security.debug support
@ -203,6 +205,8 @@ public class SecureSM extends SecurityManager {
/** /**
* The "Uwe Schindler" algorithm. * The "Uwe Schindler" algorithm.
*
* @param status the exit status
*/ */
protected void innerCheckExit(final int status) { protected void innerCheckExit(final int status) {
AccessController.doPrivileged(new PrivilegedAction<Void>() { AccessController.doPrivileged(new PrivilegedAction<Void>() {
@ -222,14 +226,12 @@ public class SecureSM extends SecurityManager {
} }
if (exitMethodHit != null) { if (exitMethodHit != null) {
if (packagesThatCanExit == null) { if (classesThatCanExit == null) {
break; break;
} }
for (String packageThatCanExit : packagesThatCanExit) { if (classCanExit(className, classesThatCanExit)) {
if (className.startsWith(packageThatCanExit)) { // this exit point is allowed, we return normally from closure:
// this exit point is allowed, we return normally from closure: return null;
return null;
}
} }
// anything else in stack trace is not allowed, break and throw SecurityException below: // anything else in stack trace is not allowed, break and throw SecurityException below:
break; break;
@ -248,4 +250,13 @@ public class SecureSM extends SecurityManager {
super.checkExit(status); 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;
}
} }

View File

@ -55,6 +55,16 @@ public class TestSecureSM extends TestCase {
fail("did not hit expected exception"); fail("did not hit expected exception");
} catch (SecurityException expected) {} } catch (SecurityException expected) {}
} }
@Test
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"}));
}
@Test @Test
public void testCreateThread() throws Exception { public void testCreateThread() throws Exception {