Browse Source

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
Jason Tedor 8 years ago
parent
commit
e6bd34e22c

+ 30 - 19
securesm/src/main/java/org/elasticsearch/SecureSM.java

@@ -63,7 +63,7 @@ import java.util.Objects;
  */
 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.
@@ -73,22 +73,24 @@ public class SecureSM extends SecurityManager {
   }
 
   /**
-   * Creates a new security manager with the specified list of packages being the only packages
-   * that can exit or halt the virtual machine.
+   * 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 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) {
-    this.packagesThatCanExit = packagesThatCanExit;
+  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
+   * 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
      */
@@ -96,15 +98,15 @@ public class SecureSM extends SecurityManager {
     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
-    "org.apache.maven.surefire.booter.",
+    "org\\.apache\\.maven\\.surefire\\.booter\\..*",
     // junit4 test runner
-    "com.carrotsearch.ant.tasks.junit4.",
+    "com\\.carrotsearch\\.ant\\.tasks\\.junit4\\.slave\\..*",
     // eclipse test runner
-    "org.eclipse.jdt.internal.junit.runner.",
+    "org\\.eclipse.jdt\\.internal\\.junit\\.runner\\..*",
     // intellij test runner
-    "com.intellij.rt.execution.junit."
+    "com\\.intellij\\.rt\\.execution\\.junit\\..*"
   };
 
   // java.security.debug support
@@ -203,6 +205,8 @@ public class SecureSM extends SecurityManager {
   
   /**
    * The "Uwe Schindler" algorithm.
+   *
+   * @param status the exit status
    */
   protected void innerCheckExit(final int status) {
     AccessController.doPrivileged(new PrivilegedAction<Void>() {
@@ -222,14 +226,12 @@ public class SecureSM extends SecurityManager {
           }
           
           if (exitMethodHit != null) {
-            if (packagesThatCanExit == null) {
+            if (classesThatCanExit == null) {
               break;
             }
-            for (String packageThatCanExit : packagesThatCanExit) {
-              if (className.startsWith(packageThatCanExit)) {
-                // this exit point is allowed, we return normally from closure:
-                return null;
-              }
+            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;
@@ -248,4 +250,13 @@ public class SecureSM extends SecurityManager {
     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;
+  }
+
 }

+ 10 - 0
securesm/src/test/java/org/elasticsearch/TestSecureSM.java

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