Skip to content

Commit b9afd63

Browse files
committed
Grant manage_threads to java.desktop for Tika (elastic#134454)
* Grant manage_threads to java.desktop for Tika * Produce a PolicyScope for excluded system modules. Fortunately, the JDK is still modular even if the tests are run sans modules, so we can use the module API to identify the packages in every system module. * TODO with Jira link
1 parent 6496364 commit b9afd63

File tree

5 files changed

+99
-13
lines changed

5 files changed

+99
-13
lines changed

libs/entitlement/src/main/java/org/elasticsearch/entitlement/bootstrap/HardcodedEntitlements.java

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -114,7 +114,13 @@ private static List<Scope> createServerEntitlements(Path pidFile) {
114114
new FilesEntitlement(serverModuleFileDatas)
115115
)
116116
),
117-
new Scope("java.desktop", List.of(new LoadNativeLibrariesEntitlement())),
117+
new Scope(
118+
"java.desktop",
119+
List.of(
120+
new LoadNativeLibrariesEntitlement(),
121+
new ManageThreadsEntitlement() // For sun.java2d.Disposer. TODO: https://elasticco.atlassian.net/browse/ES-12888
122+
)
123+
),
118124
new Scope(
119125
"java.xml",
120126
List.of(

libs/entitlement/src/main/java/org/elasticsearch/entitlement/runtime/policy/PolicyManager.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ public class PolicyManager {
5454
*/
5555
static final Logger generalLogger = LogManager.getLogger(PolicyManager.class);
5656

57-
static final Set<String> MODULES_EXCLUDED_FROM_SYSTEM_MODULES = Set.of("java.desktop", "java.xml");
57+
public static final Set<String> MODULES_EXCLUDED_FROM_SYSTEM_MODULES = Set.of("java.desktop", "java.xml");
5858

5959
/**
6060
* Identifies a particular entitlement {@link Scope} within a {@link Policy}.
@@ -94,7 +94,7 @@ public enum ComponentKind {
9494
* If this kind corresponds to a single component, this is that component's name;
9595
* otherwise null.
9696
*/
97-
final String componentName;
97+
public final String componentName;
9898

9999
ComponentKind(String componentName) {
100100
this.componentName = componentName;
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
/*
2+
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
3+
* or more contributor license agreements. Licensed under the "Elastic License
4+
* 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side
5+
* Public License v 1"; you may not use this file except in compliance with, at
6+
* your election, the "Elastic License 2.0", the "GNU Affero General Public
7+
* License v3.0 only", or the "Server Side Public License, v 1".
8+
*/
9+
10+
package org.elasticsearch.entitlement.bootstrap;
11+
12+
import org.elasticsearch.test.ESTestCase;
13+
import org.elasticsearch.test.ESTestCase.WithEntitlementsOnTestCode;
14+
15+
import java.io.ByteArrayInputStream;
16+
17+
import javax.imageio.stream.MemoryCacheImageInputStream;
18+
19+
import static java.nio.charset.StandardCharsets.UTF_8;
20+
21+
@WithEntitlementsOnTestCode
22+
public class HardcodedEntitlementsTests extends ESTestCase {
23+
24+
/**
25+
* The Tika library can do some things we don't ordinarily want to allow.
26+
* <p>
27+
* Note that {@link MemoryCacheImageInputStream} doesn't even use {@code Disposer} in JDK 26,
28+
* so it's an open question how much effort this deserves.
29+
*/
30+
public void testTikaPDF() {
31+
new MemoryCacheImageInputStream(new ByteArrayInputStream("test test".getBytes(UTF_8)));
32+
}
33+
}

test/framework/src/main/java/org/elasticsearch/bootstrap/TestScopeResolver.java

Lines changed: 52 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,14 @@
99

1010
package org.elasticsearch.bootstrap;
1111

12+
import org.elasticsearch.core.Nullable;
1213
import org.elasticsearch.core.SuppressForbidden;
13-
import org.elasticsearch.entitlement.runtime.policy.PolicyManager;
14+
import org.elasticsearch.entitlement.runtime.policy.PolicyManager.PolicyScope;
1415
import org.elasticsearch.logging.LogManager;
1516
import org.elasticsearch.logging.Logger;
1617

18+
import java.lang.module.ModuleDescriptor;
19+
import java.lang.module.ModuleFinder;
1720
import java.net.MalformedURLException;
1821
import java.net.URL;
1922
import java.util.List;
@@ -22,39 +25,78 @@
2225
import java.util.TreeMap;
2326
import java.util.function.Function;
2427

28+
import static java.util.Objects.requireNonNull;
29+
import static java.util.stream.Collectors.toSet;
2530
import static org.elasticsearch.entitlement.runtime.policy.PolicyManager.ALL_UNNAMED;
2631
import static org.elasticsearch.entitlement.runtime.policy.PolicyManager.ComponentKind.PLUGIN;
32+
import static org.elasticsearch.entitlement.runtime.policy.PolicyManager.ComponentKind.SERVER;
33+
import static org.elasticsearch.entitlement.runtime.policy.PolicyManager.MODULES_EXCLUDED_FROM_SYSTEM_MODULES;
2734

28-
public record TestScopeResolver(Map<String, PolicyManager.PolicyScope> scopeMap) {
35+
public final class TestScopeResolver {
2936

3037
private static final Logger logger = LogManager.getLogger(TestScopeResolver.class);
38+
private final Map<String, PolicyScope> scopeMap;
39+
private static final Map<String, PolicyScope> excludedSystemPackageScopes = computeExcludedSystemPackageScopes();
3140

32-
PolicyManager.PolicyScope getScope(Class<?> callerClass) {
41+
public TestScopeResolver(Map<String, PolicyScope> scopeMap) {
42+
this.scopeMap = scopeMap;
43+
}
44+
45+
private static Map<String, PolicyScope> computeExcludedSystemPackageScopes() {
46+
// Within any one module layer, module names are unique, so we just need the names
47+
Set<String> systemModuleNames = ModuleFinder.ofSystem()
48+
.findAll()
49+
.stream()
50+
.map(ref -> ref.descriptor().name())
51+
.filter(MODULES_EXCLUDED_FROM_SYSTEM_MODULES::contains)
52+
.collect(toSet());
53+
54+
Map<String, PolicyScope> result = new TreeMap<>();
55+
ModuleLayer.boot().modules().stream().filter(m -> systemModuleNames.contains(m.getName())).forEach(m -> {
56+
ModuleDescriptor desc = m.getDescriptor();
57+
if (desc != null) {
58+
desc.packages().forEach(pkg ->
59+
// Our component identification logic returns SERVER for these
60+
result.put(pkg, new PolicyScope(SERVER, SERVER.componentName, m.getName())));
61+
}
62+
});
63+
return result;
64+
}
65+
66+
public static @Nullable PolicyScope getExcludedSystemPackageScope(Class<?> callerClass) {
67+
return excludedSystemPackageScopes.get(callerClass.getPackageName());
68+
}
69+
70+
PolicyScope getScope(Class<?> callerClass) {
3371
var callerCodeSource = callerClass.getProtectionDomain().getCodeSource();
34-
assert callerCodeSource != null;
72+
if (callerCodeSource == null) {
73+
// This only happens for JDK classes. Furthermore, for trivially allowed modules, we shouldn't even get here.
74+
// Hence, this must be an excluded system module, so check for that.
75+
return requireNonNull(getExcludedSystemPackageScope(callerClass));
76+
}
3577

3678
var location = callerCodeSource.getLocation().toString();
3779
var scope = scopeMap.get(location);
3880
if (scope == null) {
3981
// Special cases for libraries not handled by our automatically-generated scopeMap
4082
if (callerClass.getPackageName().startsWith("org.bouncycastle")) {
41-
scope = new PolicyManager.PolicyScope(PLUGIN, "security", ALL_UNNAMED);
83+
scope = new PolicyScope(PLUGIN, "security", ALL_UNNAMED);
4284
logger.debug("Assuming bouncycastle is part of the security plugin");
4385
}
4486
}
4587
if (scope == null) {
4688
logger.warn("Cannot identify a scope for class [{}], location [{}]", callerClass.getName(), location);
47-
return PolicyManager.PolicyScope.unknown(location);
89+
return PolicyScope.unknown(location);
4890
}
4991
return scope;
5092
}
5193

52-
public static Function<Class<?>, PolicyManager.PolicyScope> createScopeResolver(
94+
public static Function<Class<?>, PolicyScope> createScopeResolver(
5395
TestBuildInfo serverBuildInfo,
5496
List<TestBuildInfo> pluginsBuildInfo,
5597
Set<String> modularPlugins
5698
) {
57-
Map<String, PolicyManager.PolicyScope> scopeMap = new TreeMap<>(); // Sorted to make it easier to read during debugging
99+
Map<String, PolicyScope> scopeMap = new TreeMap<>(); // Sorted to make it easier to read during debugging
58100
for (var pluginBuildInfo : pluginsBuildInfo) {
59101
boolean isModular = modularPlugins.contains(pluginBuildInfo.component());
60102
for (var location : pluginBuildInfo.locations()) {
@@ -66,7 +108,7 @@ public static Function<Class<?>, PolicyManager.PolicyScope> createScopeResolver(
66108
String module = isModular ? location.module() : ALL_UNNAMED;
67109
scopeMap.put(
68110
getCodeSource(codeSource, location.representativeClass()),
69-
PolicyManager.PolicyScope.plugin(pluginBuildInfo.component(), module)
111+
PolicyScope.plugin(pluginBuildInfo.component(), module)
70112
);
71113
} catch (MalformedURLException e) {
72114
throw new IllegalArgumentException("Cannot locate class [" + location.representativeClass() + "]", e);
@@ -81,7 +123,7 @@ public static Function<Class<?>, PolicyManager.PolicyScope> createScopeResolver(
81123
continue;
82124
}
83125
try {
84-
scopeMap.put(getCodeSource(classUrl, location.representativeClass()), PolicyManager.PolicyScope.server(location.module()));
126+
scopeMap.put(getCodeSource(classUrl, location.representativeClass()), PolicyScope.server(location.module()));
85127
} catch (MalformedURLException e) {
86128
throw new IllegalArgumentException("Cannot locate class [" + location.representativeClass() + "]", e);
87129
}

test/framework/src/main/java/org/elasticsearch/entitlement/runtime/policy/TestPolicyManager.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99

1010
package org.elasticsearch.entitlement.runtime.policy;
1111

12+
import org.elasticsearch.bootstrap.TestScopeResolver;
1213
import org.elasticsearch.common.util.ArrayUtils;
1314
import org.elasticsearch.entitlement.runtime.policy.entitlements.Entitlement;
1415
import org.elasticsearch.test.ESTestCase;
@@ -97,6 +98,10 @@ public final void clearModuleEntitlementsCache() {
9798

9899
@Override
99100
protected boolean isTrustedSystemClass(Class<?> requestingClass) {
101+
if (TestScopeResolver.getExcludedSystemPackageScope(requestingClass) != null) {
102+
// We don't trust the excluded packages even though they are in system modules
103+
return false;
104+
}
100105
ClassLoader loader = requestingClass.getClassLoader();
101106
return loader == null || loader == ClassLoader.getPlatformClassLoader();
102107
}

0 commit comments

Comments
 (0)