Skip to content

Commit f2d8f7d

Browse files
authored
[Entitlements] Follows links during FileAccessTree creation (#123357) (#123363)
1 parent 3ddaea1 commit f2d8f7d

File tree

2 files changed

+84
-8
lines changed

2 files changed

+84
-8
lines changed

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

Lines changed: 40 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -10,18 +10,27 @@
1010
package org.elasticsearch.entitlement.runtime.policy;
1111

1212
import org.elasticsearch.entitlement.runtime.policy.entitlements.FilesEntitlement;
13+
import org.elasticsearch.entitlement.runtime.policy.entitlements.FilesEntitlement.Mode;
14+
import org.elasticsearch.logging.LogManager;
15+
import org.elasticsearch.logging.Logger;
1316

17+
import java.io.IOException;
18+
import java.io.UncheckedIOException;
19+
import java.nio.file.Files;
1420
import java.nio.file.Path;
21+
import java.nio.file.Paths;
1522
import java.util.ArrayList;
1623
import java.util.Arrays;
1724
import java.util.Comparator;
1825
import java.util.List;
1926
import java.util.Objects;
27+
import java.util.function.BiConsumer;
2028

2129
import static org.elasticsearch.core.PathUtils.getDefaultFileSystem;
2230

2331
public final class FileAccessTree {
2432

33+
private static final Logger logger = LogManager.getLogger(FileAccessTree.class);
2534
private static final String FILE_SEPARATOR = getDefaultFileSystem().getSeparator();
2635

2736
private final String[] readPaths;
@@ -30,6 +39,27 @@ public final class FileAccessTree {
3039
private FileAccessTree(FilesEntitlement filesEntitlement, PathLookup pathLookup) {
3140
List<String> readPaths = new ArrayList<>();
3241
List<String> writePaths = new ArrayList<>();
42+
BiConsumer<Path, Mode> addPath = (path, mode) -> {
43+
var normalized = normalizePath(path);
44+
if (mode == Mode.READ_WRITE) {
45+
writePaths.add(normalized);
46+
}
47+
readPaths.add(normalized);
48+
};
49+
BiConsumer<Path, Mode> addPathAndMaybeLink = (path, mode) -> {
50+
addPath.accept(path, mode);
51+
// also try to follow symlinks. Lucene does this and writes to the target path.
52+
if (Files.exists(path)) {
53+
try {
54+
Path realPath = path.toRealPath();
55+
if (realPath.equals(path) == false) {
56+
addPath.accept(realPath, mode);
57+
}
58+
} catch (IOException e) {
59+
throw new UncheckedIOException(e);
60+
}
61+
}
62+
};
3363
for (FilesEntitlement.FileData fileData : filesEntitlement.filesData()) {
3464
var platform = fileData.platform();
3565
if (platform != null && platform.isCurrent() == false) {
@@ -38,18 +68,20 @@ private FileAccessTree(FilesEntitlement filesEntitlement, PathLookup pathLookup)
3868
var mode = fileData.mode();
3969
var paths = fileData.resolvePaths(pathLookup);
4070
paths.forEach(path -> {
41-
var normalized = normalizePath(path);
42-
if (mode == FilesEntitlement.Mode.READ_WRITE) {
43-
writePaths.add(normalized);
71+
if (path == null) {
72+
// TODO: null paths shouldn't be allowed, but they can occur due to repo paths
73+
return;
4474
}
45-
readPaths.add(normalized);
75+
addPathAndMaybeLink.accept(path, mode);
4676
});
4777
}
4878

49-
// everything has access to the temp dir
50-
String tempDir = normalizePath(pathLookup.tempDir());
51-
readPaths.add(tempDir);
52-
writePaths.add(tempDir);
79+
// everything has access to the temp dir and the jdk
80+
addPathAndMaybeLink.accept(pathLookup.tempDir(), Mode.READ_WRITE);
81+
82+
// TODO: watcher uses javax.activation which looks for known mime types configuration, should this be global or explicit in watcher?
83+
Path jdk = Paths.get(System.getProperty("java.home"));
84+
addPathAndMaybeLink.accept(jdk.resolve("conf"), Mode.READ);
5385

5486
readPaths.sort(PATH_ORDER);
5587
writePaths.sort(PATH_ORDER);

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

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

1212
import org.elasticsearch.common.settings.Settings;
13+
import org.elasticsearch.core.SuppressForbidden;
1314
import org.elasticsearch.entitlement.runtime.policy.entitlements.FilesEntitlement;
1415
import org.elasticsearch.test.ESTestCase;
1516
import org.junit.BeforeClass;
1617

18+
import java.io.IOException;
19+
import java.nio.file.Files;
1720
import java.nio.file.Path;
21+
import java.nio.file.Paths;
1822
import java.util.ArrayList;
1923
import java.util.HashMap;
2024
import java.util.List;
@@ -23,6 +27,7 @@
2327
import static org.elasticsearch.core.PathUtils.getDefaultFileSystem;
2428
import static org.hamcrest.Matchers.is;
2529

30+
@ESTestCase.WithoutSecurityManager
2631
public class FileAccessTreeTests extends ESTestCase {
2732

2833
static Path root;
@@ -211,6 +216,45 @@ public void testForwardSlashes() {
211216
assertThat(tree.canRead(path("m/n")), is(true));
212217
}
213218

219+
public void testJdkAccess() {
220+
Path jdkDir = Paths.get(System.getProperty("java.home"));
221+
var confDir = jdkDir.resolve("conf");
222+
var tree = accessTree(FilesEntitlement.EMPTY);
223+
224+
assertThat(tree.canRead(confDir), is(true));
225+
assertThat(tree.canWrite(confDir), is(false));
226+
assertThat(tree.canRead(jdkDir), is(false));
227+
}
228+
229+
@SuppressForbidden(reason = "don't care about the directory location in tests")
230+
public void testFollowLinks() throws IOException {
231+
Path baseSourceDir = Files.createTempDirectory("fileaccess_source");
232+
Path source1Dir = baseSourceDir.resolve("source1");
233+
Files.createDirectory(source1Dir);
234+
Path source2Dir = baseSourceDir.resolve("source2");
235+
Files.createDirectory(source2Dir);
236+
237+
Path baseTargetDir = Files.createTempDirectory("fileaccess_target");
238+
Path readTarget = baseTargetDir.resolve("read_link");
239+
Path writeTarget = baseTargetDir.resolve("write_link");
240+
Files.createSymbolicLink(readTarget, source1Dir);
241+
Files.createSymbolicLink(writeTarget, source2Dir);
242+
var tree = accessTree(entitlement(readTarget.toString(), "read", writeTarget.toString(), "read_write"));
243+
244+
assertThat(tree.canRead(baseSourceDir), is(false));
245+
assertThat(tree.canRead(baseTargetDir), is(false));
246+
247+
assertThat(tree.canRead(readTarget), is(true));
248+
assertThat(tree.canWrite(readTarget), is(false));
249+
assertThat(tree.canRead(source1Dir), is(true));
250+
assertThat(tree.canWrite(source1Dir), is(false));
251+
252+
assertThat(tree.canRead(writeTarget), is(true));
253+
assertThat(tree.canWrite(writeTarget), is(true));
254+
assertThat(tree.canRead(source2Dir), is(true));
255+
assertThat(tree.canWrite(source2Dir), is(true));
256+
}
257+
214258
public void testTempDirAccess() {
215259
var tree = FileAccessTree.of(FilesEntitlement.EMPTY, TEST_PATH_LOOKUP);
216260
assertThat(tree.canRead(TEST_PATH_LOOKUP.tempDir()), is(true));

0 commit comments

Comments
 (0)