Skip to content

Commit bd60b8e

Browse files
committed
Include denial reason info in checkFileRead
1 parent 97ee7fc commit bd60b8e

File tree

2 files changed

+85
-8
lines changed

2 files changed

+85
-8
lines changed

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

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -227,28 +227,29 @@ public void checkFileRead(Class<?> callerClass, Path path, boolean followLinks)
227227
ModuleEntitlements entitlements = policyManager.getEntitlements(requestingClass);
228228

229229
Path realPath = null;
230-
boolean canRead = entitlements.fileAccess().canRead(path);
231-
if (canRead && followLinks) {
230+
String denialReason = entitlements.fileAccess().canRead(path)? null : "Forbidden by policies";
231+
if (denialReason == null && followLinks) {
232232
try {
233233
realPath = path.toRealPath();
234234
if (realPath.equals(path) == false) {
235-
canRead = entitlements.fileAccess().canRead(realPath);
235+
denialReason = entitlements.fileAccess().canRead(realPath)? null : "Symlink target forbidden by policies";
236236
}
237237
} catch (NoSuchFileException e) {
238238
throw e; // rethrow
239239
} catch (IOException e) {
240-
canRead = false;
240+
denialReason = e.toString();
241241
}
242242
}
243243

244-
if (canRead == false) {
244+
if (denialReason != null) {
245245
notEntitled(
246246
Strings.format(
247-
"component [%s], module [%s], class [%s], entitlement [file], operation [read], path [%s]",
247+
"component [%s], module [%s], class [%s], entitlement [file], operation [read], path [%s], reason [%s]",
248248
entitlements.componentName(),
249249
entitlements.moduleName(),
250250
requestingClass,
251-
realPath == null ? path : Strings.format("%s -> %s", path, realPath)
251+
realPath == null ? path : Strings.format("%s -> %s", path, realPath),
252+
denialReason
252253
),
253254
requestingClass,
254255
entitlements

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

Lines changed: 77 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,15 +9,31 @@
99

1010
package org.elasticsearch.entitlement.runtime.policy;
1111

12+
import org.elasticsearch.entitlement.runtime.api.NotEntitledException;
13+
import org.elasticsearch.entitlement.runtime.policy.entitlements.FilesEntitlement;
14+
import org.elasticsearch.entitlement.runtime.policy.entitlements.FilesEntitlement.FileData;
1215
import org.elasticsearch.test.ESTestCase;
16+
import org.hamcrest.text.MatchesPattern;
1317

1418
import java.io.IOException;
19+
import java.nio.file.Files;
20+
import java.nio.file.LinkOption;
21+
import java.nio.file.NoSuchFileException;
22+
import java.nio.file.Path;
23+
import java.util.List;
24+
import java.util.Map;
1525
import java.util.Set;
26+
import java.util.regex.Pattern;
1627
import java.util.stream.Stream;
1728

29+
import static java.nio.file.LinkOption.NOFOLLOW_LINKS;
1830
import static org.elasticsearch.entitlement.runtime.policy.PolicyManagerTests.NO_ENTITLEMENTS_MODULE;
1931
import static org.elasticsearch.entitlement.runtime.policy.PolicyManagerTests.TEST_PATH_LOOKUP;
32+
import static org.elasticsearch.entitlement.runtime.policy.PolicyManagerTests.createEmptyTestServerPolicy;
2033
import static org.elasticsearch.entitlement.runtime.policy.PolicyManagerTests.makeClassInItsOwnModule;
34+
import static org.elasticsearch.entitlement.runtime.policy.entitlements.FilesEntitlement.Mode.READ;
35+
import static org.hamcrest.Matchers.containsString;
36+
import static org.hamcrest.text.MatchesPattern.matchesPattern;
2137

2238
public class PolicyCheckerImplTests extends ESTestCase {
2339
public void testRequestingClassFastPath() throws IOException, ClassNotFoundException {
@@ -54,8 +70,68 @@ public void testRequestingModuleWithStackWalk() throws IOException, ClassNotFoun
5470
);
5571
}
5672

73+
/**
74+
* Set up a situation where the file read entitlement check encounters
75+
* a strange {@link IOException} that is not {@link NoSuchFileException}.
76+
* Ensure that the resulting {@link NotEntitledException} makes
77+
* some effort to indicate the nature of the problem.
78+
*/
79+
public void testIOExceptionFollowingSymlink() throws IOException {
80+
Path dir = createTempDir();
81+
Path symlink = dir.resolve("symlink");
82+
Path allegedDir = dir.resolve("not_a_dir");
83+
Path target = allegedDir.resolve("target");
84+
Files.createFile(allegedDir); // Not a dir!
85+
Files.createSymbolicLink(symlink, target);
86+
87+
PathLookup testPathLookup = new PathLookup() {
88+
@Override
89+
public Path pidFile() {
90+
throw new UnsupportedOperationException();
91+
}
92+
93+
@Override
94+
public Stream<Path> getBaseDirPaths(BaseDir baseDir) {
95+
return Stream.empty();
96+
}
97+
98+
@Override
99+
public Stream<Path> resolveSettingPaths(BaseDir baseDir, String settingName) {
100+
throw new UnsupportedOperationException();
101+
}
102+
103+
@Override
104+
public boolean isPathOnDefaultFilesystem(Path path) {
105+
// We need this to be on the default filesystem or it will be trivially allowed
106+
return true;
107+
}
108+
};
109+
110+
var policyManager = new TestPolicyManager(
111+
createEmptyTestServerPolicy(),
112+
List.of(),
113+
Map.of("testComponent", new Policy("testPolicy", List.of(new Scope("testModule", List.of(new FilesEntitlement(List.of(FileData.ofPath(symlink, READ)))))))),
114+
c-> PolicyManager.PolicyScope.plugin("testComponent", "testModule"),
115+
testPathLookup,
116+
List.of(),
117+
List.of()
118+
);
119+
policyManager.setActive(true);
120+
policyManager.setTriviallyAllowingTestCode(false);
121+
122+
var checker = checker(NO_ENTITLEMENTS_MODULE, policyManager, testPathLookup);
123+
var exception = assertThrows(NotEntitledException.class, ()->checker.checkFileRead(getClass(), symlink, true));
124+
assertThat("The reason should be an exception of some sort",
125+
exception.getMessage(), matchesPattern(".*reason.*Exception.*"));
126+
}
127+
57128
private static PolicyCheckerImpl checker(Module entitlementsModule) {
58-
return new PolicyCheckerImpl(Set.of(), entitlementsModule, null, TEST_PATH_LOOKUP);
129+
// TODO: TEST_PATH_LOOKUP is always null at this point!
130+
return checker(entitlementsModule, null, TEST_PATH_LOOKUP);
131+
}
132+
133+
private static PolicyCheckerImpl checker(Module entitlementsModule, PolicyManager policyManager, PathLookup testPathLookup) {
134+
return new PolicyCheckerImpl(Set.of(), entitlementsModule, policyManager, testPathLookup);
59135
}
60136

61137
}

0 commit comments

Comments
 (0)