8210274: Source Launcher should work with a security manager
authorjjg
Wed, 26 Sep 2018 11:41:08 -0700
changeset 51885 789cc1561621
parent 51884 2ee7e1b7ba66
child 51886 bdf62f266de4
8210274: Source Launcher should work with a security manager Reviewed-by: mchung, alanb
src/jdk.compiler/share/classes/com/sun/tools/javac/launcher/Main.java
test/langtools/tools/javac/launcher/SourceLauncherTest.java
--- a/src/jdk.compiler/share/classes/com/sun/tools/javac/launcher/Main.java	Sat Aug 25 20:16:43 2018 +0530
+++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/launcher/Main.java	Wed Sep 26 11:41:08 2018 -0700
@@ -50,6 +50,9 @@
 import java.nio.file.InvalidPathException;
 import java.nio.file.Path;
 import java.nio.file.Paths;
+import java.security.CodeSigner;
+import java.security.CodeSource;
+import java.security.ProtectionDomain;
 import java.text.MessageFormat;
 import java.util.ArrayList;
 import java.util.Arrays;
@@ -182,7 +185,7 @@
     public void run(String[] runtimeArgs, String[] args) throws Fault, InvocationTargetException {
         Path file = getFile(args);
 
-        Context context = new Context();
+        Context context = new Context(file.toAbsolutePath());
         String mainClassName = compile(file, getJavacOpts(runtimeArgs), context);
 
         String[] appArgs = Arrays.copyOfRange(args, 1, args.length);
@@ -193,7 +196,7 @@
      * Returns the path for the filename found in the first of an array of arguments.
      *
      * @param args the array
-     * @return the path
+     * @return the path, as given in the array of args
      * @throws Fault if there is a problem determining the path, or if the file does not exist
      */
     private Path getFile(String[] args) throws Fault {
@@ -478,14 +481,19 @@
      * a class loader.
      */
     private static class Context {
-        private Map<String, byte[]> inMemoryClasses = new HashMap<>();
+        private final Path file;
+        private final Map<String, byte[]> inMemoryClasses = new HashMap<>();
+
+        Context(Path file) {
+            this.file = file;
+        }
 
         JavaFileManager getFileManager(StandardJavaFileManager delegate) {
             return new MemoryFileManager(inMemoryClasses, delegate);
         }
 
         ClassLoader getClassLoader(ClassLoader parent) {
-            return new MemoryClassLoader(inMemoryClasses, parent);
+            return new MemoryClassLoader(inMemoryClasses, parent, file);
         }
     }
 
@@ -546,9 +554,22 @@
          */
         private final Map<String, byte[]> sourceFileClasses;
 
-        MemoryClassLoader(Map<String, byte[]> sourceFileClasses, ClassLoader parent) {
+        /**
+         * A minimal protection domain, specifying a code source of the source file itself,
+         * used for classes found in the source file and defined by this loader.
+         */
+        private final ProtectionDomain domain;
+
+        MemoryClassLoader(Map<String, byte[]> sourceFileClasses, ClassLoader parent, Path file) {
             super(parent);
             this.sourceFileClasses = sourceFileClasses;
+            CodeSource codeSource;
+            try {
+                codeSource = new CodeSource(file.toUri().toURL(), (CodeSigner[]) null);
+            } catch (MalformedURLException e) {
+                codeSource = null;
+            }
+            domain = new ProtectionDomain(codeSource, null, this, null);
         }
 
         /**
@@ -632,7 +653,7 @@
             if (bytes == null) {
                 throw new ClassNotFoundException(name);
             }
-            return defineClass(name, bytes, 0, bytes.length);
+            return defineClass(name, bytes, 0, bytes.length, domain);
         }
 
         @Override
--- a/test/langtools/tools/javac/launcher/SourceLauncherTest.java	Sat Aug 25 20:16:43 2018 +0530
+++ b/test/langtools/tools/javac/launcher/SourceLauncherTest.java	Wed Sep 26 11:41:08 2018 -0700
@@ -192,6 +192,64 @@
         checkEqual("stdout", log.trim(), "Hello World! [1, 2, 3]");
     }
 
+    @Test
+    public void testCodeSource(Path base) throws IOException {
+        tb.writeJavaFiles(base,
+            "import java.net.URL;\n" +
+            "class ShowCodeSource {\n" +
+            "    public static void main(String... args) {\n" +
+            "        URL u = ShowCodeSource.class.getProtectionDomain().getCodeSource().getLocation();\n" +
+            "        System.out.println(u);\n" +
+            "    }\n" +
+            "}");
+
+        Path file = base.resolve("ShowCodeSource.java");
+        String log = new JavaTask(tb)
+                .className(file.toString())
+                .run(Task.Expect.SUCCESS)
+                .getOutput(Task.OutputKind.STDOUT);
+        checkEqual("stdout", log.trim(), file.toAbsolutePath().toUri().toURL().toString());
+    }
+
+    @Test
+    public void testPermissions(Path base) throws IOException {
+        Path policyFile = base.resolve("test.policy");
+        Path sourceFile = base.resolve("TestPermissions.java");
+
+        tb.writeFile(policyFile,
+            "grant codeBase \"jrt:/jdk.compiler\" {\n" +
+            "    permission java.security.AllPermission;\n" +
+            "};\n" +
+            "grant codeBase \"" + sourceFile.toUri().toURL() + "\" {\n" +
+            "    permission java.util.PropertyPermission \"user.dir\", \"read\";\n" +
+            "};\n");
+
+        tb.writeJavaFiles(base,
+            "import java.net.URL;\n" +
+            "class TestPermissions {\n" +
+            "    public static void main(String... args) {\n" +
+            "        System.out.println(\"user.dir=\" + System.getProperty(\"user.dir\"));\n" +
+            "        try {\n" +
+            "            System.setProperty(\"user.dir\", \"\");\n" +
+            "            System.out.println(\"no exception\");\n" +
+            "            System.exit(1);\n" +
+            "        } catch (SecurityException e) {\n" +
+            "            System.out.println(\"exception: \" + e);\n" +
+            "        }\n" +
+            "    }\n" +
+            "}");
+
+        String log = new JavaTask(tb)
+                .vmOptions("-Djava.security.manager", "-Djava.security.policy=" + policyFile)
+                .className(sourceFile.toString())
+                .run(Task.Expect.SUCCESS)
+                .getOutput(Task.OutputKind.STDOUT);
+        checkEqual("stdout", log.trim().replace(tb.lineSeparator, "\n"),
+                "user.dir=" + System.getProperty("user.dir") + "\n" +
+                "exception: java.security.AccessControlException: " +
+                    "access denied (\"java.util.PropertyPermission\" \"user.dir\" \"write\")");
+    }
+
     void testSuccess(Path file, String expect) throws IOException {
         Result r = run(file, Collections.emptyList(), List.of("1", "2", "3"));
         checkEqual("stdout", r.stdOut, expect);