Skip to content

Commit 155ba49

Browse files
authored
Fix entitlement checks for relative links (elastic#124133) (elastic#124478)
1 parent 85c1138 commit 155ba49

File tree

6 files changed

+73
-16
lines changed

6 files changed

+73
-16
lines changed

libs/entitlement/qa/entitled-plugin/src/main/java/org/elasticsearch/entitlement/qa/entitled/EntitledActions.java

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,19 @@ public static Path createTempSymbolicLink() throws IOException {
6666
return Files.createSymbolicLink(readDir().resolve("entitlements-link-" + random.nextLong()), readWriteDir());
6767
}
6868

69+
public static Path createK8sLikeMount() throws IOException {
70+
Path baseDir = readDir().resolve("k8s");
71+
var versionedDir = Files.createDirectories(baseDir.resolve("..version"));
72+
var actualFileMount = Files.createFile(versionedDir.resolve("mount-" + random.nextLong() + ".tmp"));
73+
74+
var dataDir = Files.createSymbolicLink(baseDir.resolve("..data"), versionedDir.getFileName());
75+
// mount-0.tmp -> ..data/mount-0.tmp -> ..version/mount-0.tmp
76+
return Files.createSymbolicLink(
77+
baseDir.resolve(actualFileMount.getFileName()),
78+
dataDir.getFileName().resolve(actualFileMount.getFileName())
79+
);
80+
}
81+
6982
public static URLConnection createHttpURLConnection() throws IOException {
7083
return URI.create("http://127.0.0.1:12345/").toURL().openConnection();
7184
}

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

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,16 @@ static void checkFilesCreateSymbolicLink() throws IOException {
140140
}
141141
}
142142

143+
@EntitlementTest(expectedAccess = PLUGINS)
144+
static void checkFilesCreateRelativeSymbolicLink() throws IOException {
145+
var directory = EntitledActions.createTempDirectoryForWrite();
146+
try {
147+
Files.createSymbolicLink(directory.resolve("link"), Path.of("target"));
148+
} catch (UnsupportedOperationException | FileSystemException e) {
149+
// OK not to implement symbolic link in the filesystem
150+
}
151+
}
152+
143153
@EntitlementTest(expectedAccess = PLUGINS)
144154
static void checkFilesCreateLink() throws IOException {
145155
var directory = EntitledActions.createTempDirectoryForWrite();
@@ -150,6 +160,17 @@ static void checkFilesCreateLink() throws IOException {
150160
}
151161
}
152162

163+
@EntitlementTest(expectedAccess = PLUGINS)
164+
static void checkFilesCreateRelativeLink() throws IOException {
165+
var directory = EntitledActions.createTempDirectoryForWrite();
166+
var target = directory.resolve("target");
167+
try {
168+
Files.createLink(directory.resolve("link"), Path.of("target"));
169+
} catch (UnsupportedOperationException | FileSystemException e) {
170+
// OK not to implement symbolic link in the filesystem
171+
}
172+
}
173+
153174
@EntitlementTest(expectedAccess = PLUGINS)
154175
static void checkFilesDelete() throws IOException {
155176
var file = EntitledActions.createTempFileForWrite();

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

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

1010
package org.elasticsearch.entitlement.qa.test;
1111

12+
import org.elasticsearch.entitlement.qa.entitled.EntitledActions;
13+
1214
import java.io.IOException;
1315
import java.nio.file.FileSystems;
1416
import java.nio.file.LinkOption;
@@ -24,6 +26,11 @@ static void checkToRealPath() throws IOException {
2426
FileCheckActions.readFile().toRealPath();
2527
}
2628

29+
@EntitlementTest(expectedAccess = PLUGINS)
30+
static void checkToRealPathWithK8sLikeMount() throws IOException, Exception {
31+
EntitledActions.createK8sLikeMount().toRealPath();
32+
}
33+
2734
@EntitlementTest(expectedAccess = PLUGINS)
2835
static void checkToRealPathNoFollow() throws IOException {
2936
FileCheckActions.readFile().toRealPath(LinkOption.NOFOLLOW_LINKS);

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ class EntitlementsTestRule implements TestRule {
4040
"files",
4141
List.of(
4242
Map.of("path", tempDir.resolve("read_dir"), "mode", "read_write"),
43+
Map.of("path", tempDir.resolve("read_dir").resolve("k8s").resolve("..data"), "mode", "read", "exclusive", true),
4344
Map.of("path", tempDir.resolve("read_write_dir"), "mode", "read_write"),
4445
Map.of("path", tempDir.resolve("read_file"), "mode", "read"),
4546
Map.of("path", tempDir.resolve("read_write_file"), "mode", "read_write")

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

Lines changed: 10 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@
1919
import java.io.FileDescriptor;
2020
import java.io.FileFilter;
2121
import java.io.FilenameFilter;
22-
import java.io.IOException;
2322
import java.io.InputStream;
2423
import java.io.OutputStream;
2524
import java.io.PrintStream;
@@ -67,7 +66,6 @@
6766
import java.nio.file.FileStore;
6867
import java.nio.file.FileVisitOption;
6968
import java.nio.file.FileVisitor;
70-
import java.nio.file.Files;
7169
import java.nio.file.LinkOption;
7270
import java.nio.file.OpenOption;
7371
import java.nio.file.Path;
@@ -1963,16 +1961,21 @@ public void checkSelectorProviderInheritedChannel(Class<?> callerClass, Selector
19631961
policyManager.checkCreateTempFile(callerClass);
19641962
}
19651963

1964+
private static Path resolveLinkTarget(Path path, Path target) {
1965+
var parent = path.getParent();
1966+
return parent == null ? target : parent.resolve(target);
1967+
}
1968+
19661969
@Override
19671970
public void check$java_nio_file_Files$$createSymbolicLink(Class<?> callerClass, Path link, Path target, FileAttribute<?>... attrs) {
1968-
policyManager.checkFileRead(callerClass, target);
19691971
policyManager.checkFileWrite(callerClass, link);
1972+
policyManager.checkFileRead(callerClass, resolveLinkTarget(link, target));
19701973
}
19711974

19721975
@Override
19731976
public void check$java_nio_file_Files$$createLink(Class<?> callerClass, Path link, Path existing) {
1974-
policyManager.checkFileRead(callerClass, existing);
19751977
policyManager.checkFileWrite(callerClass, link);
1978+
policyManager.checkFileRead(callerClass, resolveLinkTarget(link, existing));
19761979
}
19771980

19781981
@Override
@@ -2461,13 +2464,13 @@ public void checkCreateDirectory(Class<?> callerClass, FileSystemProvider that,
24612464
@Override
24622465
public void checkCreateSymbolicLink(Class<?> callerClass, FileSystemProvider that, Path link, Path target, FileAttribute<?>... attrs) {
24632466
policyManager.checkFileWrite(callerClass, link);
2464-
policyManager.checkFileRead(callerClass, target);
2467+
policyManager.checkFileRead(callerClass, resolveLinkTarget(link, target));
24652468
}
24662469

24672470
@Override
24682471
public void checkCreateLink(Class<?> callerClass, FileSystemProvider that, Path link, Path existing) {
24692472
policyManager.checkFileWrite(callerClass, link);
2470-
policyManager.checkFileRead(callerClass, existing);
2473+
policyManager.checkFileRead(callerClass, resolveLinkTarget(link, existing));
24712474
}
24722475

24732476
@Override
@@ -2645,14 +2648,7 @@ public void checkPathToRealPath(Class<?> callerClass, Path that, LinkOption... o
26452648
followLinks = false;
26462649
}
26472650
}
2648-
if (followLinks) {
2649-
try {
2650-
policyManager.checkFileRead(callerClass, Files.readSymbolicLink(that));
2651-
} catch (IOException | UnsupportedOperationException e) {
2652-
// that is not a link, or unrelated IOException or unsupported
2653-
}
2654-
}
2655-
policyManager.checkFileRead(callerClass, that);
2651+
policyManager.checkFileRead(callerClass, that, followLinks);
26562652
}
26572653

26582654
@Override

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

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
import org.elasticsearch.logging.Logger;
3232

3333
import java.io.File;
34+
import java.io.IOException;
3435
import java.lang.StackWalker.StackFrame;
3536
import java.lang.module.ModuleFinder;
3637
import java.lang.module.ModuleReference;
@@ -325,6 +326,10 @@ private static boolean isPathOnDefaultFilesystem(Path path) {
325326
}
326327

327328
public void checkFileRead(Class<?> callerClass, Path path) {
329+
checkFileRead(callerClass, path, false);
330+
}
331+
332+
public void checkFileRead(Class<?> callerClass, Path path, boolean followLinks) {
328333
if (isPathOnDefaultFilesystem(path) == false) {
329334
return;
330335
}
@@ -334,14 +339,28 @@ public void checkFileRead(Class<?> callerClass, Path path) {
334339
}
335340

336341
ModuleEntitlements entitlements = getEntitlements(requestingClass);
337-
if (entitlements.fileAccess().canRead(path) == false) {
342+
343+
Path realPath = null;
344+
boolean canRead = entitlements.fileAccess().canRead(path);
345+
if (canRead && followLinks) {
346+
try {
347+
realPath = path.toRealPath();
348+
} catch (IOException e) {
349+
// target not found or other IO error
350+
}
351+
if (realPath != null && realPath.equals(path) == false) {
352+
canRead = entitlements.fileAccess().canRead(realPath);
353+
}
354+
}
355+
356+
if (canRead == false) {
338357
notEntitled(
339358
Strings.format(
340359
"Not entitled: component [%s], module [%s], class [%s], entitlement [file], operation [read], path [%s]",
341360
entitlements.componentName(),
342361
requestingClass.getModule().getName(),
343362
requestingClass,
344-
path
363+
realPath == null ? path : Strings.format("%s -> %s", path, realPath)
345364
),
346365
callerClass
347366
);

0 commit comments

Comments
 (0)