d547a707bf
Reviewed-by: alanb, jpai, mchung, lancea
276 lines
11 KiB
Java
276 lines
11 KiB
Java
/*
|
|
* Copyright (c) 2020, 2022, Oracle and/or its affiliates. All rights reserved.
|
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
|
*
|
|
* This code is free software; you can redistribute it and/or modify it
|
|
* under the terms of the GNU General Public License version 2 only, as
|
|
* published by the Free Software Foundation.
|
|
*
|
|
* This code is distributed in the hope that it will be useful, but WITHOUT
|
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
|
* version 2 for more details (a copy is included in the LICENSE file that
|
|
* accompanied this code).
|
|
*
|
|
* You should have received a copy of the GNU General Public License version
|
|
* 2 along with this work; if not, write to the Free Software Foundation,
|
|
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
*
|
|
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
|
* or visit www.oracle.com if you need additional information or have any
|
|
* questions.
|
|
*/
|
|
|
|
/*
|
|
* @test
|
|
* @bug 8246778
|
|
* @summary Test that security checks occur for getPermittedSubclasses
|
|
* @library /test/lib
|
|
* @modules java.compiler
|
|
* @build jdk.test.lib.compiler.CompilerUtils jdk.test.lib.compiler.ModuleInfoMaker TestSecurityManagerChecks
|
|
* @run main/othervm -Djava.security.manager=allow TestSecurityManagerChecks named
|
|
* @run main/othervm -Djava.security.manager=allow TestSecurityManagerChecks unnamed
|
|
*/
|
|
|
|
import java.io.IOException;
|
|
import java.lang.module.Configuration;
|
|
import java.lang.module.ModuleFinder;
|
|
import java.net.MalformedURLException;
|
|
import java.net.URL;
|
|
import java.net.URLClassLoader;
|
|
import java.nio.file.Files;
|
|
import java.nio.file.Path;
|
|
import java.nio.file.Paths;
|
|
import java.util.Arrays;
|
|
import java.util.List;
|
|
import java.util.Objects;
|
|
import java.util.stream.Collectors;
|
|
|
|
import jdk.test.lib.compiler.*;
|
|
|
|
public class TestSecurityManagerChecks {
|
|
|
|
private static final ClassLoader OBJECT_CL = null;
|
|
|
|
public static void main(String[] args) throws Throwable {
|
|
if ("named".equals(args[0])) {
|
|
runNamedModuleTest();
|
|
} else {
|
|
runUnnamedModuleTest();
|
|
}
|
|
}
|
|
|
|
private static void runNamedModuleTest() throws Throwable {
|
|
Path classes = compileNamedModuleTest();
|
|
URL[] testClassPath = getTestClassPath();
|
|
|
|
//need to use a different ClassLoader to run the test, so that the checks are performed:
|
|
ClassLoader testCL = new URLClassLoader(testClassPath, OBJECT_CL);
|
|
testCL.loadClass("TestSecurityManagerChecks")
|
|
.getDeclaredMethod("doRunNamedModuleTest", Path.class)
|
|
.invoke(null, classes);
|
|
}
|
|
|
|
public static void doRunNamedModuleTest(Path classes) throws Throwable {
|
|
Configuration testConfig = ModuleLayer.boot()
|
|
.configuration()
|
|
.resolve(ModuleFinder.of(),
|
|
ModuleFinder.of(classes),
|
|
List.of("test"));
|
|
ModuleLayer testLayer = ModuleLayer.boot()
|
|
.defineModulesWithOneLoader(testConfig,
|
|
OBJECT_CL);
|
|
|
|
// First get hold of the target classes before we enable security
|
|
Class<?> sealed = Class.forName(testLayer.findModule("test").get(), "test.Base");
|
|
|
|
//try without a SecurityManager:
|
|
checkPermittedSubclasses(sealed, "test.a.ImplA1",
|
|
"test.a.ImplA2",
|
|
"test.b.ImplB");
|
|
|
|
String[] denyPackageAccess = new String[1];
|
|
int[] checkPackageAccessCallCount = new int[1];
|
|
|
|
//try with a SecurityManager:
|
|
SecurityManager sm = new SecurityManager() {
|
|
@Override
|
|
public void checkPackageAccess(String pkg) {
|
|
if (pkg.startsWith("test.")) {
|
|
checkPackageAccessCallCount[0]++;
|
|
}
|
|
if (Objects.equals(denyPackageAccess[0], pkg)) {
|
|
throw new SecurityException();
|
|
}
|
|
}
|
|
};
|
|
|
|
System.setSecurityManager(sm);
|
|
|
|
denyPackageAccess[0] = "test";
|
|
|
|
//passes - does not return a class from package "test":
|
|
checkPermittedSubclasses(sealed, "test.a.ImplA1",
|
|
"test.a.ImplA2",
|
|
"test.b.ImplB");
|
|
|
|
if (checkPackageAccessCallCount[0] != 2) {
|
|
throw new AssertionError("Unexpected call count: " +
|
|
checkPackageAccessCallCount[0]);
|
|
}
|
|
|
|
denyPackageAccess[0] = "test.a";
|
|
|
|
try {
|
|
sealed.getPermittedSubclasses();
|
|
throw new Error("getPermittedSubclasses incorrectly succeeded for " +
|
|
sealed.getName());
|
|
} catch (SecurityException e) {
|
|
System.out.println("OK - getPermittedSubclasses for " + sealed.getName() +
|
|
" got expected exception: " + e);
|
|
}
|
|
}
|
|
|
|
private static Path compileNamedModuleTest() throws IOException {
|
|
Path base = Paths.get(".", "named");
|
|
Path src = base.resolve("src");
|
|
Path classes = base.resolve("classes");
|
|
|
|
ModuleInfoMaker maker = new ModuleInfoMaker(src);
|
|
maker.writeJavaFiles("test",
|
|
"module test {}",
|
|
"package test; public sealed interface Base permits test.a.ImplA1, test.a.ImplA2, test.b.ImplB, test.c.ImplC {}",
|
|
"package test.a; public final class ImplA1 implements test.Base {}",
|
|
"package test.a; public final class ImplA2 implements test.Base {}",
|
|
"package test.b; public final class ImplB implements test.Base {}",
|
|
"package test.c; public final class ImplC implements test.Base {}"
|
|
);
|
|
|
|
if (!CompilerUtils.compile(src, classes.resolve("test"), "--enable-preview", "-source", System.getProperty("java.specification.version"))) {
|
|
throw new AssertionError("Compilation didn't succeed!");
|
|
}
|
|
|
|
Files.delete(classes.resolve("test").resolve("test").resolve("c").resolve("ImplC.class"));
|
|
|
|
return classes;
|
|
}
|
|
|
|
private static void runUnnamedModuleTest() throws Throwable {
|
|
Path classes = compileUnnamedModuleTest();
|
|
URL[] testClassPath = getTestClassPath();
|
|
|
|
//need to use a different ClassLoader to run the test, so that the checks are performed:
|
|
ClassLoader testCL = new URLClassLoader(testClassPath, OBJECT_CL);
|
|
testCL.loadClass("TestSecurityManagerChecks")
|
|
.getDeclaredMethod("doRunUnnamedModuleTest", Path.class)
|
|
.invoke(null, classes);
|
|
}
|
|
|
|
public static void doRunUnnamedModuleTest(Path classes) throws Throwable {
|
|
ClassLoader unnamedModuleCL =
|
|
new URLClassLoader(new URL[] {classes.toUri().toURL()}, OBJECT_CL);
|
|
|
|
// First get hold of the target classes before we enable security
|
|
Class<?> sealed = unnamedModuleCL.loadClass("test.Base");
|
|
|
|
//try without a SecurityManager:
|
|
checkPermittedSubclasses(sealed, "test.ImplA1",
|
|
"test.ImplA2",
|
|
"test.ImplB");
|
|
|
|
String[] denyPackageAccess = new String[1];
|
|
int[] checkPackageAccessCallCount = new int[1];
|
|
|
|
//try with a SecurityManager:
|
|
SecurityManager sm = new SecurityManager() {
|
|
@Override
|
|
public void checkPackageAccess(String pkg) {
|
|
if (pkg.equals("test")) {
|
|
checkPackageAccessCallCount[0]++;
|
|
}
|
|
if (Objects.equals(denyPackageAccess[0], pkg)) {
|
|
throw new SecurityException();
|
|
}
|
|
}
|
|
};
|
|
|
|
System.setSecurityManager(sm);
|
|
|
|
denyPackageAccess[0] = "test.unknown";
|
|
|
|
//passes - does not return a class from package "test.unknown":
|
|
checkPermittedSubclasses(sealed, "test.ImplA1",
|
|
"test.ImplA2",
|
|
"test.ImplB");
|
|
|
|
if (checkPackageAccessCallCount[0] != 1) {
|
|
throw new AssertionError("Unexpected call count: " +
|
|
checkPackageAccessCallCount[0]);
|
|
}
|
|
|
|
denyPackageAccess[0] = "test";
|
|
|
|
try {
|
|
sealed.getPermittedSubclasses();
|
|
throw new Error("getPermittedSubclasses incorrectly succeeded for " +
|
|
sealed.getName());
|
|
} catch (SecurityException e) {
|
|
System.out.println("OK - getPermittedSubclasses for " + sealed.getName() +
|
|
" got expected exception: " + e);
|
|
}
|
|
}
|
|
|
|
private static Path compileUnnamedModuleTest() throws IOException {
|
|
Path base = Paths.get(".", "unnamed");
|
|
Path src = base.resolve("src");
|
|
Path classes = base.resolve("classes");
|
|
|
|
ModuleInfoMaker maker = new ModuleInfoMaker(src);
|
|
maker.writeJavaFiles("test",
|
|
"module test {}",
|
|
"package test; public sealed interface Base permits ImplA1, ImplA2, ImplB, ImplC {}",
|
|
"package test; public final class ImplA1 implements test.Base {}",
|
|
"package test; public final class ImplA2 implements test.Base {}",
|
|
"package test; public final class ImplB implements test.Base {}",
|
|
"package test; public final class ImplC implements test.Base {}"
|
|
);
|
|
|
|
Files.delete(src.resolve("test").resolve("module-info.java"));
|
|
|
|
if (!CompilerUtils.compile(src.resolve("test"), classes, "--enable-preview", "-source", System.getProperty("java.specification.version"))) {
|
|
throw new AssertionError("Compilation didn't succeed!");
|
|
}
|
|
|
|
Files.delete(classes.resolve("test").resolve("ImplC.class"));
|
|
|
|
return classes;
|
|
}
|
|
|
|
private static void checkPermittedSubclasses(Class<?> c, String... expected) {
|
|
Class<?>[] subclasses = c.getPermittedSubclasses();
|
|
List<String> subclassesNames = Arrays.stream(subclasses)
|
|
.map(Class::getName)
|
|
.collect(Collectors.toList());
|
|
if (!subclassesNames.equals(Arrays.asList(expected))) {
|
|
throw new AssertionError("Incorrect permitted subclasses: " +
|
|
subclassesNames);
|
|
}
|
|
}
|
|
|
|
private static URL[] getTestClassPath() {
|
|
return Arrays.stream(System.getProperty("test.class.path")
|
|
.split(System.getProperty("path.separator")))
|
|
.map(TestSecurityManagerChecks::path2URL)
|
|
.toArray(s -> new URL[s]);
|
|
}
|
|
|
|
private static URL path2URL(String p) {
|
|
try {
|
|
return Path.of(p).toUri().toURL();
|
|
} catch (MalformedURLException ex) {
|
|
throw new AssertionError(ex);
|
|
}
|
|
}
|
|
|
|
}
|