Skip to content

Commit b5f8da1

Browse files
prdoyleelasticsearchmachine
andauthored
Grant all entitlements to system modules (#119168) (#119364)
* Grant all entitlements to system modules * [CI] Auto commit changes from spotless * Make NO_ENTITLEMENTS_MODULE non-null * Initialize NO_ENTITLEMENTS_MODULE with @BeforeClass. Looks like @WithoutSecurityManager doesn't work with static initializers. * Move check to public method * Logging adjustments --------- Co-authored-by: elasticsearchmachine <[email protected]>
1 parent e7aab5b commit b5f8da1

File tree

5 files changed

+48
-49
lines changed

5 files changed

+48
-49
lines changed

libs/entitlement/bridge/src/main/java/org/elasticsearch/entitlement/bridge/EntitlementChecker.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ public interface EntitlementChecker {
3232
void check$java_net_URLClassLoader$(Class<?> callerClass, String name, URL[] urls, ClassLoader parent, URLStreamHandlerFactory factory);
3333

3434
// Process creation
35-
void check$$start(Class<?> callerClass, ProcessBuilder that, ProcessBuilder.Redirect[] redirects);
35+
void check$$start(Class<?> callerClass, ProcessBuilder that);
3636

3737
void check$java_lang_ProcessBuilder$startPipeline(Class<?> callerClass, List<ProcessBuilder> builders);
3838

libs/entitlement/qa/src/javaRestTest/java/org/elasticsearch/entitlement/qa/EntitlementsDeniedIT.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,8 @@ public class EntitlementsDeniedIT extends ESRestTestCase {
3131
.plugin("entitlement-denied-nonmodular")
3232
.systemProperty("es.entitlements.enabled", "true")
3333
.setting("xpack.security.enabled", "false")
34+
// Logs in libs/entitlement/qa/build/test-results/javaRestTest/TEST-org.elasticsearch.entitlement.qa.EntitlementsDeniedIT.xml
35+
// .setting("logger.org.elasticsearch.entitlement", "TRACE")
3436
.build();
3537

3638
@Override

libs/entitlement/src/main/java/org/elasticsearch/entitlement/runtime/api/ElasticsearchEntitlementChecker.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ public ElasticsearchEntitlementChecker(PolicyManager policyManager) {
7070
}
7171

7272
@Override
73-
public void check$$start(Class<?> callerClass, ProcessBuilder processBuilder, ProcessBuilder.Redirect[] redirects) {
73+
public void check$$start(Class<?> callerClass, ProcessBuilder processBuilder) {
7474
policyManager.checkStartProcess(callerClass);
7575
}
7676

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

Lines changed: 19 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@
1010
package org.elasticsearch.entitlement.runtime.policy;
1111

1212
import org.elasticsearch.core.Strings;
13-
import org.elasticsearch.entitlement.runtime.api.ElasticsearchEntitlementChecker;
1413
import org.elasticsearch.entitlement.runtime.api.NotEntitledException;
1514
import org.elasticsearch.logging.LogManager;
1615
import org.elasticsearch.logging.Logger;
@@ -32,10 +31,9 @@
3231

3332
import static java.lang.StackWalker.Option.RETAIN_CLASS_REFERENCE;
3433
import static java.util.Objects.requireNonNull;
35-
import static java.util.function.Predicate.not;
3634

3735
public class PolicyManager {
38-
private static final Logger logger = LogManager.getLogger(ElasticsearchEntitlementChecker.class);
36+
private static final Logger logger = LogManager.getLogger(PolicyManager.class);
3937

4038
static class ModuleEntitlements {
4139
public static final ModuleEntitlements NONE = new ModuleEntitlements(List.of());
@@ -68,25 +66,24 @@ public <E extends Entitlement> Stream<E> getEntitlements(Class<E> entitlementCla
6866

6967
private static final Set<Module> systemModules = findSystemModules();
7068

71-
/**
72-
* Frames originating from this module are ignored in the permission logic.
73-
*/
74-
private final Module entitlementsModule;
75-
7669
private static Set<Module> findSystemModules() {
7770
var systemModulesDescriptors = ModuleFinder.ofSystem()
7871
.findAll()
7972
.stream()
8073
.map(ModuleReference::descriptor)
8174
.collect(Collectors.toUnmodifiableSet());
82-
8375
return ModuleLayer.boot()
8476
.modules()
8577
.stream()
8678
.filter(m -> systemModulesDescriptors.contains(m.getDescriptor()))
8779
.collect(Collectors.toUnmodifiableSet());
8880
}
8981

82+
/**
83+
* Frames originating from this module are ignored in the permission logic.
84+
*/
85+
private final Module entitlementsModule;
86+
9087
public PolicyManager(
9188
Policy defaultPolicy,
9289
Map<String, Policy> pluginPolicies,
@@ -227,12 +224,12 @@ private static boolean isServerModule(Module requestingModule) {
227224
* this is a fast-path check that can avoid the stack walk
228225
* in cases where the caller class is available.
229226
* @return the requesting module, or {@code null} if the entire call stack
230-
* comes from modules that are trusted.
227+
* comes from the entitlement library itself.
231228
*/
232229
Module requestingModule(Class<?> callerClass) {
233230
if (callerClass != null) {
234-
Module callerModule = callerClass.getModule();
235-
if (systemModules.contains(callerModule) == false) {
231+
var callerModule = callerClass.getModule();
232+
if (callerModule != null && entitlementsModule.equals(callerModule) == false) {
236233
// fast path
237234
return callerModule;
238235
}
@@ -251,8 +248,8 @@ Module requestingModule(Class<?> callerClass) {
251248
Optional<Module> findRequestingModule(Stream<Class<?>> classes) {
252249
return classes.map(Objects::requireNonNull)
253250
.map(PolicyManager::moduleOf)
254-
.filter(m -> m != entitlementsModule) // Ignore the entitlements library itself
255-
.filter(not(systemModules::contains)) // Skip trusted JDK modules
251+
.filter(m -> m != entitlementsModule) // Ignore the entitlements library itself entirely
252+
.skip(1) // Skip the sensitive method itself
256253
.findFirst();
257254
}
258255

@@ -266,8 +263,15 @@ private static Module moduleOf(Class<?> c) {
266263
}
267264

268265
private static boolean isTriviallyAllowed(Module requestingModule) {
266+
if (logger.isTraceEnabled()) {
267+
logger.trace("Stack trace for upcoming trivially-allowed check", new Exception());
268+
}
269269
if (requestingModule == null) {
270-
logger.debug("Entitlement trivially allowed: entire call stack is in composed of classes in system modules");
270+
logger.debug("Entitlement trivially allowed: no caller frames outside the entitlement library");
271+
return true;
272+
}
273+
if (systemModules.contains(requestingModule)) {
274+
logger.debug("Entitlement trivially allowed from system module [{}]", requestingModule.getName());
271275
return true;
272276
}
273277
logger.trace("Entitlement not trivially allowed");

libs/entitlement/src/test/java/org/elasticsearch/entitlement/runtime/policy/PolicyManagerTests.java

Lines changed: 25 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
import org.elasticsearch.test.ESTestCase;
1414
import org.elasticsearch.test.compiler.InMemoryJavaCompiler;
1515
import org.elasticsearch.test.jar.JarUtils;
16+
import org.junit.BeforeClass;
1617

1718
import java.io.IOException;
1819
import java.lang.module.Configuration;
@@ -37,8 +38,22 @@
3738

3839
@ESTestCase.WithoutSecurityManager
3940
public class PolicyManagerTests extends ESTestCase {
41+
/**
42+
* A module you can use for test cases that don't actually care about the
43+
* entitlements module.
44+
*/
45+
private static Module NO_ENTITLEMENTS_MODULE;
46+
47+
@BeforeClass
48+
public static void beforeClass() {
49+
try {
50+
// Any old module will do for tests using NO_ENTITLEMENTS_MODULE
51+
NO_ENTITLEMENTS_MODULE = makeClassInItsOwnModule().getModule();
52+
} catch (Exception e) {
53+
throw new IllegalStateException(e);
54+
}
4055

41-
private static final Module NO_ENTITLEMENTS_MODULE = null;
56+
}
4257

4358
public void testGetEntitlementsThrowsOnMissingPluginUnnamedModule() {
4459
var policyManager = new PolicyManager(
@@ -210,53 +225,31 @@ public void testRequestingModuleFastPath() throws IOException, ClassNotFoundExce
210225
}
211226

212227
public void testRequestingModuleWithStackWalk() throws IOException, ClassNotFoundException {
213-
var requestingClass = makeClassInItsOwnModule();
214-
var runtimeClass = makeClassInItsOwnModule(); // A class in the entitlements library itself
228+
var entitlementsClass = makeClassInItsOwnModule(); // A class in the entitlements library itself
229+
var requestingClass = makeClassInItsOwnModule(); // This guy is always the right answer
230+
var instrumentedClass = makeClassInItsOwnModule(); // The class that called the check method
215231
var ignorableClass = makeClassInItsOwnModule();
216-
var systemClass = Object.class;
217232

218-
var policyManager = policyManagerWithEntitlementsModule(runtimeClass.getModule());
233+
var policyManager = policyManagerWithEntitlementsModule(entitlementsClass.getModule());
219234

220235
var requestingModule = requestingClass.getModule();
221236

222237
assertEquals(
223-
"Skip one system frame",
224-
requestingModule,
225-
policyManager.findRequestingModule(Stream.of(systemClass, requestingClass, ignorableClass)).orElse(null)
226-
);
227-
assertEquals(
228-
"Skip multiple system frames",
229-
requestingModule,
230-
policyManager.findRequestingModule(Stream.of(systemClass, systemClass, systemClass, requestingClass, ignorableClass))
231-
.orElse(null)
232-
);
233-
assertEquals(
234-
"Skip system frame between runtime frames",
238+
"Skip entitlement library and the instrumented method",
235239
requestingModule,
236-
policyManager.findRequestingModule(Stream.of(runtimeClass, systemClass, runtimeClass, requestingClass, ignorableClass))
240+
policyManager.findRequestingModule(Stream.of(entitlementsClass, instrumentedClass, requestingClass, ignorableClass))
237241
.orElse(null)
238242
);
239243
assertEquals(
240-
"Skip runtime frame between system frames",
241-
requestingModule,
242-
policyManager.findRequestingModule(Stream.of(systemClass, runtimeClass, systemClass, requestingClass, ignorableClass))
243-
.orElse(null)
244-
);
245-
assertEquals(
246-
"No system frames",
247-
requestingModule,
248-
policyManager.findRequestingModule(Stream.of(requestingClass, ignorableClass)).orElse(null)
249-
);
250-
assertEquals(
251-
"Skip runtime frames up to the first system frame",
244+
"Skip multiple library frames",
252245
requestingModule,
253-
policyManager.findRequestingModule(Stream.of(runtimeClass, runtimeClass, systemClass, requestingClass, ignorableClass))
246+
policyManager.findRequestingModule(Stream.of(entitlementsClass, entitlementsClass, instrumentedClass, requestingClass))
254247
.orElse(null)
255248
);
256249
assertThrows(
257250
"Non-modular caller frames are not supported",
258251
NullPointerException.class,
259-
() -> policyManager.findRequestingModule(Stream.of(systemClass, null))
252+
() -> policyManager.findRequestingModule(Stream.of(entitlementsClass, null))
260253
);
261254
}
262255

0 commit comments

Comments
 (0)