Skip to content

Commit c65223d

Browse files
committed
Check hidden frames in entitlements (elastic#127877)
Entitlements do a stack walk to find the calling class. When method refences are used in a lambda, the frame ends up hidden in the stack walk. In the case of using a method reference with AccessController.doPrivileged, the call looks like it is the jdk itself, so the call is trivially allowed. This commit adds hidden frames to the stack walk so that the lambda frame created for the method reference is included. Several internal packages are then necessary to filter out of the stack.
1 parent 671b8d6 commit c65223d

File tree

4 files changed

+20
-2
lines changed

4 files changed

+20
-2
lines changed

docs/changelog/127877.yaml

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
pr: 127877
2+
summary: Check hidden frames in entitlements
3+
area: Infra/Core
4+
type: bug
5+
issues: []

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

+6-1
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,10 @@
1010
package org.elasticsearch.entitlement.bridge;
1111

1212
import java.util.Optional;
13+
import java.util.Set;
1314

1415
import static java.lang.StackWalker.Option.RETAIN_CLASS_REFERENCE;
16+
import static java.lang.StackWalker.Option.SHOW_HIDDEN_FRAMES;
1517

1618
public class Util {
1719
/**
@@ -23,6 +25,8 @@ public class Util {
2325
public static final Class<?> NO_CLASS = new Object() {
2426
}.getClass();
2527

28+
private static final Set<String> skipInternalPackages = Set.of("java.lang.invoke", "java.lang.reflect", "jdk.internal.reflect");
29+
2630
/**
2731
* Why would we write this instead of using {@link StackWalker#getCallerClass()}?
2832
* Because that method throws {@link IllegalCallerException} if called from the "outermost frame",
@@ -32,9 +36,10 @@ public class Util {
3236
*/
3337
@SuppressWarnings("unused") // Called reflectively from InstrumenterImpl
3438
public static Class<?> getCallerClass() {
35-
Optional<Class<?>> callerClassIfAny = StackWalker.getInstance(RETAIN_CLASS_REFERENCE)
39+
Optional<Class<?>> callerClassIfAny = StackWalker.getInstance(Set.of(RETAIN_CLASS_REFERENCE, SHOW_HIDDEN_FRAMES))
3640
.walk(
3741
frames -> frames.skip(2) // Skip this method and its caller
42+
.filter(frame -> skipInternalPackages.contains(frame.getDeclaringClass().getPackageName()) == false)
3843
.findFirst()
3944
.map(StackWalker.StackFrame::getDeclaringClass)
4045
);

libs/entitlement/src/main/java/org/elasticsearch/entitlement/runtime/policy/entitlements/FilesEntitlement.java

+3-1
Original file line numberDiff line numberDiff line change
@@ -182,7 +182,9 @@ private static BaseDir parseBaseDir(String baseDir) {
182182
case "config" -> BaseDir.CONFIG;
183183
case "data" -> BaseDir.DATA;
184184
case "home" -> BaseDir.USER_HOME;
185-
// NOTE: shared_repo is _not_ accessible to policy files, only internally
185+
// it would be nice to limit this to just ES modules, but we don't have a way to plumb that through to here
186+
// however, we still don't document in the error case below that shared_repo is valid
187+
case "shared_repo" -> BaseDir.SHARED_REPO;
186188
default -> throw new PolicyValidationException(
187189
"invalid relative directory: " + baseDir + ", valid values: [config, data, home]"
188190
);
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,8 @@
1+
org.elasticsearch.repository.url:
2+
- outbound_network
3+
- files:
4+
- relative_path: .
5+
relative_to: shared_repo
6+
mode: read
17
org.apache.httpcomponents.httpclient:
28
- outbound_network # for URLHttpClient

0 commit comments

Comments
 (0)