Skip to content

Commit 46387fb

Browse files
authored
[Entitlements] Instrument nio path (elastic#122507) (elastic#122763)
(cherry picked from commit 7fd1add) # Conflicts: # libs/entitlement/src/main/java/org/elasticsearch/entitlement/runtime/api/ElasticsearchEntitlementChecker.java
1 parent d4c2c37 commit 46387fb

File tree

6 files changed

+220
-62
lines changed

6 files changed

+220
-62
lines changed

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

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,8 @@
5151
import java.nio.file.LinkOption;
5252
import java.nio.file.OpenOption;
5353
import java.nio.file.Path;
54+
import java.nio.file.WatchEvent;
55+
import java.nio.file.WatchService;
5456
import java.nio.file.attribute.FileAttribute;
5557
import java.nio.file.attribute.UserPrincipal;
5658
import java.nio.file.spi.FileSystemProvider;
@@ -588,6 +590,19 @@ void checkNewByteChannel(
588590

589591
void checkType(Class<?> callerClass, FileStore that);
590592

593+
// path
594+
void checkPathToRealPath(Class<?> callerClass, Path that, LinkOption... options);
595+
596+
void checkPathRegister(Class<?> callerClass, Path that, WatchService watcher, WatchEvent.Kind<?>... events);
597+
598+
void checkPathRegister(
599+
Class<?> callerClass,
600+
Path that,
601+
WatchService watcher,
602+
WatchEvent.Kind<?>[] events,
603+
WatchEvent.Modifier... modifiers
604+
);
605+
591606
////////////////////
592607
//
593608
// Thread management
@@ -608,5 +623,4 @@ void checkNewByteChannel(
608623
void check$java_lang_Thread$setUncaughtExceptionHandler(Class<?> callerClass, Thread thread, Thread.UncaughtExceptionHandler ueh);
609624

610625
void check$java_lang_ThreadGroup$setMaxPriority(Class<?> callerClass, ThreadGroup threadGroup, int pri);
611-
612626
}
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
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.qa.test;
11+
12+
import java.io.IOException;
13+
import java.nio.file.FileSystems;
14+
import java.nio.file.LinkOption;
15+
import java.nio.file.WatchEvent;
16+
17+
import static org.elasticsearch.entitlement.qa.test.EntitlementTest.ExpectedAccess.PLUGINS;
18+
19+
class PathActions {
20+
21+
@EntitlementTest(expectedAccess = PLUGINS)
22+
static void checkToRealPath() throws IOException {
23+
FileCheckActions.readFile().toRealPath();
24+
}
25+
26+
@EntitlementTest(expectedAccess = PLUGINS)
27+
static void checkToRealPathNoFollow() throws IOException {
28+
FileCheckActions.readFile().toRealPath(LinkOption.NOFOLLOW_LINKS);
29+
}
30+
31+
@SuppressWarnings("rawtypes")
32+
@EntitlementTest(expectedAccess = PLUGINS)
33+
static void checkRegister() throws IOException {
34+
try (var watchService = FileSystems.getDefault().newWatchService()) {
35+
FileCheckActions.readFile().register(watchService, new WatchEvent.Kind[0]);
36+
} catch (IllegalArgumentException e) {
37+
// intentionally no events registered
38+
}
39+
}
40+
41+
@SuppressWarnings("rawtypes")
42+
@EntitlementTest(expectedAccess = PLUGINS)
43+
static void checkRegisterWithModifiers() throws IOException {
44+
try (var watchService = FileSystems.getDefault().newWatchService()) {
45+
FileCheckActions.readFile().register(watchService, new WatchEvent.Kind[0], new WatchEvent.Modifier[0]);
46+
} catch (IllegalArgumentException e) {
47+
// intentionally no events registered
48+
}
49+
}
50+
}

libs/entitlement/qa/entitlement-test-plugin/src/main/java/org/elasticsearch/entitlement/qa/test/RestEntitlementsCheckAction.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -190,6 +190,7 @@ static CheckAction alwaysDenied(CheckedRunnable<Exception> action) {
190190
getTestEntries(ManageThreadsActions.class),
191191
getTestEntries(NativeActions.class),
192192
getTestEntries(NioFileSystemActions.class),
193+
getTestEntries(PathActions.class),
193194
getTestEntries(SpiActions.class),
194195
getTestEntries(SystemActions.class),
195196
getTestEntries(VersionSpecificManageThreadsActions.class),

libs/entitlement/src/main/java/org/elasticsearch/entitlement/initialization/EntitlementInitialization.java

Lines changed: 69 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -47,9 +47,12 @@
4747
import java.nio.file.LinkOption;
4848
import java.nio.file.OpenOption;
4949
import java.nio.file.Path;
50+
import java.nio.file.WatchEvent;
51+
import java.nio.file.WatchService;
5052
import java.nio.file.attribute.FileAttribute;
5153
import java.nio.file.spi.FileSystemProvider;
5254
import java.util.ArrayList;
55+
import java.util.Arrays;
5356
import java.util.HashMap;
5457
import java.util.List;
5558
import java.util.Map;
@@ -97,6 +100,7 @@ public static void initialize(Instrumentation inst) throws Exception {
97100
Stream.of(
98101
fileSystemProviderChecks(),
99102
fileStoreChecks(),
103+
pathChecks(),
100104
Stream.of(
101105
INSTRUMENTATION_SERVICE.lookupImplementationMethod(
102106
SelectorProvider.class,
@@ -150,33 +154,49 @@ private static PolicyManager createPolicyManager() {
150154
new LoadNativeLibrariesEntitlement(),
151155
new ManageThreadsEntitlement(),
152156
new FilesEntitlement(
153-
List.of(
154-
FileData.ofPath(bootstrapArgs.tempDir(), READ_WRITE),
155-
FileData.ofPath(bootstrapArgs.logsDir(), READ_WRITE),
156-
// OS release on Linux
157-
FileData.ofPath(Path.of("/etc/os-release"), READ),
158-
FileData.ofPath(Path.of("/etc/system-release"), READ),
159-
FileData.ofPath(Path.of("/usr/lib/os-release"), READ),
160-
// read max virtual memory areas
161-
FileData.ofPath(Path.of("/proc/sys/vm/max_map_count"), READ),
162-
FileData.ofPath(Path.of("/proc/meminfo"), READ),
163-
// load averages on Linux
164-
FileData.ofPath(Path.of("/proc/loadavg"), READ),
165-
// control group stats on Linux. cgroup v2 stats are in an unpredicable
166-
// location under `/sys/fs/cgroup`, so unfortunately we have to allow
167-
// read access to the entire directory hierarchy.
168-
FileData.ofPath(Path.of("/proc/self/cgroup"), READ),
169-
FileData.ofPath(Path.of("/sys/fs/cgroup/"), READ),
170-
// // io stats on Linux
171-
FileData.ofPath(Path.of("/proc/self/mountinfo"), READ),
172-
FileData.ofPath(Path.of("/proc/diskstats"), READ)
173-
)
157+
Stream.concat(
158+
Stream.of(
159+
FileData.ofPath(bootstrapArgs.tempDir(), READ_WRITE),
160+
FileData.ofPath(bootstrapArgs.configDir(), READ),
161+
FileData.ofPath(bootstrapArgs.logsDir(), READ_WRITE),
162+
// OS release on Linux
163+
FileData.ofPath(Path.of("/etc/os-release"), READ),
164+
FileData.ofPath(Path.of("/etc/system-release"), READ),
165+
FileData.ofPath(Path.of("/usr/lib/os-release"), READ),
166+
// read max virtual memory areas
167+
FileData.ofPath(Path.of("/proc/sys/vm/max_map_count"), READ),
168+
FileData.ofPath(Path.of("/proc/meminfo"), READ),
169+
// load averages on Linux
170+
FileData.ofPath(Path.of("/proc/loadavg"), READ),
171+
// control group stats on Linux. cgroup v2 stats are in an unpredicable
172+
// location under `/sys/fs/cgroup`, so unfortunately we have to allow
173+
// read access to the entire directory hierarchy.
174+
FileData.ofPath(Path.of("/proc/self/cgroup"), READ),
175+
FileData.ofPath(Path.of("/sys/fs/cgroup/"), READ),
176+
// // io stats on Linux
177+
FileData.ofPath(Path.of("/proc/self/mountinfo"), READ),
178+
FileData.ofPath(Path.of("/proc/diskstats"), READ)
179+
),
180+
Arrays.stream(bootstrapArgs.dataDirs()).map(d -> FileData.ofPath(d, READ))
181+
).toList()
174182
)
175183
)
176184
),
177185
new Scope("org.apache.httpcomponents.httpclient", List.of(new OutboundNetworkEntitlement())),
178186
new Scope("io.netty.transport", List.of(new InboundNetworkEntitlement(), new OutboundNetworkEntitlement())),
179-
new Scope("org.apache.lucene.core", List.of(new LoadNativeLibrariesEntitlement(), new ManageThreadsEntitlement())),
187+
new Scope(
188+
"org.apache.lucene.core",
189+
List.of(
190+
new LoadNativeLibrariesEntitlement(),
191+
new ManageThreadsEntitlement(),
192+
new FilesEntitlement(
193+
Stream.concat(
194+
Stream.of(FileData.ofPath(bootstrapArgs.configDir(), READ)),
195+
Arrays.stream(bootstrapArgs.dataDirs()).map(d -> FileData.ofPath(d, READ_WRITE))
196+
).toList()
197+
)
198+
)
199+
),
180200
new Scope("org.apache.logging.log4j.core", List.of(new ManageThreadsEntitlement())),
181201
new Scope(
182202
"org.elasticsearch.nativeaccess",
@@ -323,6 +343,33 @@ public InstrumentationService.InstrumentationInfo of(String methodName, Class<?>
323343
});
324344
}
325345

346+
private static Stream<InstrumentationService.InstrumentationInfo> pathChecks() {
347+
var pathClasses = StreamSupport.stream(FileSystems.getDefault().getRootDirectories().spliterator(), false)
348+
.map(Path::getClass)
349+
.distinct();
350+
return pathClasses.flatMap(pathClass -> {
351+
InstrumentationInfoFactory instrumentation = (String methodName, Class<?>... parameterTypes) -> INSTRUMENTATION_SERVICE
352+
.lookupImplementationMethod(
353+
Path.class,
354+
methodName,
355+
pathClass,
356+
EntitlementChecker.class,
357+
"checkPath" + Character.toUpperCase(methodName.charAt(0)) + methodName.substring(1),
358+
parameterTypes
359+
);
360+
361+
try {
362+
return Stream.of(
363+
instrumentation.of("toRealPath", LinkOption[].class),
364+
instrumentation.of("register", WatchService.class, WatchEvent.Kind[].class),
365+
instrumentation.of("register", WatchService.class, WatchEvent.Kind[].class, WatchEvent.Modifier[].class)
366+
);
367+
} catch (NoSuchMethodException | ClassNotFoundException e) {
368+
throw new RuntimeException(e);
369+
}
370+
});
371+
}
372+
326373
/**
327374
* Returns the "most recent" checker class compatible with the current runtime Java version.
328375
* For checkers, we have (optionally) version specific classes, each with a prefix (e.g. Java23).

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

Lines changed: 73 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
import org.elasticsearch.entitlement.runtime.policy.PolicyManager;
1515

1616
import java.io.File;
17+
import java.io.IOException;
1718
import java.io.InputStream;
1819
import java.io.PrintStream;
1920
import java.io.PrintWriter;
@@ -53,10 +54,13 @@
5354
import java.nio.file.CopyOption;
5455
import java.nio.file.DirectoryStream;
5556
import java.nio.file.FileStore;
57+
import java.nio.file.Files;
5658
import java.nio.file.LinkOption;
5759
import java.nio.file.OpenOption;
5860
import java.nio.file.Path;
5961
import java.nio.file.StandardOpenOption;
62+
import java.nio.file.WatchEvent;
63+
import java.nio.file.WatchService;
6064
import java.nio.file.attribute.FileAttribute;
6165
import java.nio.file.attribute.UserPrincipal;
6266
import java.nio.file.spi.FileSystemProvider;
@@ -1176,6 +1180,52 @@ public void checkSetAttribute(
11761180

11771181
}
11781182

1183+
// Thread management
1184+
1185+
@Override
1186+
public void check$java_lang_Thread$start(Class<?> callerClass, Thread thread) {
1187+
policyManager.checkManageThreadsEntitlement(callerClass);
1188+
}
1189+
1190+
@Override
1191+
public void check$java_lang_Thread$setDaemon(Class<?> callerClass, Thread thread, boolean on) {
1192+
policyManager.checkManageThreadsEntitlement(callerClass);
1193+
}
1194+
1195+
@Override
1196+
public void check$java_lang_ThreadGroup$setDaemon(Class<?> callerClass, ThreadGroup threadGroup, boolean daemon) {
1197+
policyManager.checkManageThreadsEntitlement(callerClass);
1198+
}
1199+
1200+
@Override
1201+
public void check$java_util_concurrent_ForkJoinPool$setParallelism(Class<?> callerClass, ForkJoinPool forkJoinPool, int size) {
1202+
policyManager.checkManageThreadsEntitlement(callerClass);
1203+
}
1204+
1205+
@Override
1206+
public void check$java_lang_Thread$setName(Class<?> callerClass, Thread thread, String name) {
1207+
policyManager.checkManageThreadsEntitlement(callerClass);
1208+
}
1209+
1210+
@Override
1211+
public void check$java_lang_Thread$setPriority(Class<?> callerClass, Thread thread, int newPriority) {
1212+
policyManager.checkManageThreadsEntitlement(callerClass);
1213+
}
1214+
1215+
@Override
1216+
public void check$java_lang_Thread$setUncaughtExceptionHandler(
1217+
Class<?> callerClass,
1218+
Thread thread,
1219+
Thread.UncaughtExceptionHandler ueh
1220+
) {
1221+
policyManager.checkManageThreadsEntitlement(callerClass);
1222+
}
1223+
1224+
@Override
1225+
public void check$java_lang_ThreadGroup$setMaxPriority(Class<?> callerClass, ThreadGroup threadGroup, int pri) {
1226+
policyManager.checkManageThreadsEntitlement(callerClass);
1227+
}
1228+
11791229
@Override
11801230
public void checkGetFileStoreAttributeView(Class<?> callerClass, FileStore that, Class<?> type) {
11811231
policyManager.checkWriteStoreAttributes(callerClass);
@@ -1221,50 +1271,37 @@ public void checkType(Class<?> callerClass, FileStore that) {
12211271
policyManager.checkReadStoreAttributes(callerClass);
12221272
}
12231273

1224-
// Thread management
1225-
1226-
@Override
1227-
public void check$java_lang_Thread$start(Class<?> callerClass, Thread thread) {
1228-
policyManager.checkManageThreadsEntitlement(callerClass);
1229-
}
1230-
1231-
@Override
1232-
public void check$java_lang_Thread$setDaemon(Class<?> callerClass, Thread thread, boolean on) {
1233-
policyManager.checkManageThreadsEntitlement(callerClass);
1234-
}
1235-
1236-
@Override
1237-
public void check$java_lang_ThreadGroup$setDaemon(Class<?> callerClass, ThreadGroup threadGroup, boolean daemon) {
1238-
policyManager.checkManageThreadsEntitlement(callerClass);
1239-
}
1240-
12411274
@Override
1242-
public void check$java_util_concurrent_ForkJoinPool$setParallelism(Class<?> callerClass, ForkJoinPool forkJoinPool, int size) {
1243-
policyManager.checkManageThreadsEntitlement(callerClass);
1244-
}
1245-
1246-
@Override
1247-
public void check$java_lang_Thread$setName(Class<?> callerClass, Thread thread, String name) {
1248-
policyManager.checkManageThreadsEntitlement(callerClass);
1275+
public void checkPathToRealPath(Class<?> callerClass, Path that, LinkOption... options) {
1276+
boolean followLinks = true;
1277+
for (LinkOption option : options) {
1278+
if (option == LinkOption.NOFOLLOW_LINKS) {
1279+
followLinks = false;
1280+
}
1281+
}
1282+
if (followLinks) {
1283+
try {
1284+
policyManager.checkFileRead(callerClass, Files.readSymbolicLink(that));
1285+
} catch (IOException | UnsupportedOperationException e) {
1286+
// that is not a link, or unrelated IOException or unsupported
1287+
}
1288+
}
1289+
policyManager.checkFileRead(callerClass, that);
12491290
}
12501291

12511292
@Override
1252-
public void check$java_lang_Thread$setPriority(Class<?> callerClass, Thread thread, int newPriority) {
1253-
policyManager.checkManageThreadsEntitlement(callerClass);
1293+
public void checkPathRegister(Class<?> callerClass, Path that, WatchService watcher, WatchEvent.Kind<?>... events) {
1294+
policyManager.checkFileRead(callerClass, that);
12541295
}
12551296

12561297
@Override
1257-
public void check$java_lang_Thread$setUncaughtExceptionHandler(
1298+
public void checkPathRegister(
12581299
Class<?> callerClass,
1259-
Thread thread,
1260-
Thread.UncaughtExceptionHandler ueh
1300+
Path that,
1301+
WatchService watcher,
1302+
WatchEvent.Kind<?>[] events,
1303+
WatchEvent.Modifier... modifiers
12611304
) {
1262-
policyManager.checkManageThreadsEntitlement(callerClass);
1263-
}
1264-
1265-
@Override
1266-
public void check$java_lang_ThreadGroup$setMaxPriority(Class<?> callerClass, ThreadGroup threadGroup, int pri) {
1267-
policyManager.checkManageThreadsEntitlement(callerClass);
1305+
policyManager.checkFileRead(callerClass, that);
12681306
}
1269-
12701307
}

0 commit comments

Comments
 (0)