Skip to content
Merged
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,8 @@
import java.nio.file.LinkOption;
import java.nio.file.OpenOption;
import java.nio.file.Path;
import java.nio.file.WatchEvent;
import java.nio.file.WatchService;
import java.nio.file.attribute.FileAttribute;
import java.nio.file.attribute.UserPrincipal;
import java.nio.file.spi.FileSystemProvider;
Expand Down Expand Up @@ -652,4 +654,17 @@ void checkNewByteChannel(
void checkName(Class<?> callerClass, FileStore that);

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

// path
void checkPathToRealPath(Class<?> callerClass, Path that, LinkOption... options);

void checkPathRegister(Class<?> callerClass, Path that, WatchService watcher, WatchEvent.Kind<?>... events);

void checkPathRegister(
Class<?> callerClass,
Path that,
WatchService watcher,
WatchEvent.Kind<?>[] events,
WatchEvent.Modifier... modifiers
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the "Elastic License
* 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side
* Public License v 1"; you may not use this file except in compliance with, at
* your election, the "Elastic License 2.0", the "GNU Affero General Public
* License v3.0 only", or the "Server Side Public License, v 1".
*/

package org.elasticsearch.entitlement.qa.test;

import java.io.IOException;
import java.nio.file.FileSystems;
import java.nio.file.LinkOption;
import java.nio.file.WatchEvent;

import static org.elasticsearch.entitlement.qa.test.EntitlementTest.ExpectedAccess.PLUGINS;

class PathActions {

@EntitlementTest(expectedAccess = PLUGINS)
static void checkToRealPath() throws IOException {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: we haven't been consistent, but in other files we've named this as classMethodName, no "check"

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We're fairly inconsistent, see NioFileSystemActions

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've auto-generated most of the names in my tests, so I don't mess them up, like this. Not pretty, especially when suffixes are needed for overloads. 🤷

FileCheckActions.readFile().toRealPath();
}

@EntitlementTest(expectedAccess = PLUGINS)
static void checkToRealPathNoFollow() throws IOException {
FileCheckActions.readFile().toRealPath(LinkOption.NOFOLLOW_LINKS);
}

@SuppressWarnings("rawtypes")
@EntitlementTest(expectedAccess = PLUGINS)
static void checkRegister() throws IOException {
try (var watchService = FileSystems.getDefault().newWatchService()) {
FileCheckActions.readFile().register(watchService, new WatchEvent.Kind[0]);
} catch (IllegalArgumentException e) {
// intentionally no events registered
}
}

@SuppressWarnings("rawtypes")
@EntitlementTest(expectedAccess = PLUGINS)
static void checkRegisterWithModifiers() throws IOException {
try (var watchService = FileSystems.getDefault().newWatchService()) {
FileCheckActions.readFile().register(watchService, new WatchEvent.Kind[0], new WatchEvent.Modifier[0]);
} catch (IllegalArgumentException e) {
// intentionally no events registered
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -187,7 +187,7 @@ static CheckAction alwaysDenied(CheckedRunnable<Exception> action) {
getTestEntries(SystemActions.class),
getTestEntries(NativeActions.class),
getTestEntries(NioFileSystemActions.class),
getTestEntries(FileStoreActions.class)
getTestEntries(PathActions.class)
)
.flatMap(Function.identity())
.filter(entry -> entry.getValue().fromJavaVersion() == null || Runtime.version().feature() >= entry.getValue().fromJavaVersion())
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,8 @@
import java.nio.file.LinkOption;
import java.nio.file.OpenOption;
import java.nio.file.Path;
import java.nio.file.WatchEvent;
import java.nio.file.WatchService;
import java.nio.file.attribute.FileAttribute;
import java.nio.file.spi.FileSystemProvider;
import java.util.ArrayList;
Expand All @@ -58,6 +60,7 @@
import java.util.stream.Stream;
import java.util.stream.StreamSupport;

import static org.elasticsearch.entitlement.runtime.policy.entitlements.FilesEntitlement.Mode.READ;
import static org.elasticsearch.entitlement.runtime.policy.entitlements.FilesEntitlement.Mode.READ_WRITE;

/**
Expand Down Expand Up @@ -94,6 +97,7 @@ public static void initialize(Instrumentation inst) throws Exception {
Stream.of(
fileSystemProviderChecks(),
fileStoreChecks(),
pathChecks(),
Stream.of(
INSTRUMENTATION_SERVICE.lookupImplementationMethod(
SelectorProvider.class,
Expand Down Expand Up @@ -144,13 +148,30 @@ private static PolicyManager createPolicyManager() {
new OutboundNetworkEntitlement(),
new LoadNativeLibrariesEntitlement(),
new FilesEntitlement(
List.of(new FilesEntitlement.FileData(EntitlementBootstrap.bootstrapArgs().tempDir().toString(), READ_WRITE))
Stream.concat(
Stream.of(
new FileData(EntitlementBootstrap.bootstrapArgs().tempDir().toString(), READ_WRITE),
new FileData(EntitlementBootstrap.bootstrapArgs().configDir().toString(), READ)
),
Arrays.stream(dataDirs).map(d -> new FileData(d.toString(), READ))
).toList()
)
)
),
new Scope("org.apache.httpcomponents.httpclient", List.of(new OutboundNetworkEntitlement())),
new Scope("io.netty.transport", List.of(new InboundNetworkEntitlement(), new OutboundNetworkEntitlement())),
new Scope("org.apache.lucene.core", List.of(new LoadNativeLibrariesEntitlement())),
new Scope(
"org.apache.lucene.core",
List.of(
new LoadNativeLibrariesEntitlement(),
new FilesEntitlement(
Stream.concat(
Stream.of(new FileData(EntitlementBootstrap.bootstrapArgs().configDir().toString(), READ)),
Arrays.stream(dataDirs).map(d -> new FileData(d.toString(), READ_WRITE))
).toList()
)
)
),
new Scope(
"org.elasticsearch.nativeaccess",
List.of(
Expand Down Expand Up @@ -255,6 +276,33 @@ public InstrumentationService.InstrumentationInfo of(String methodName, Class<?>
});
}

private static Stream<InstrumentationService.InstrumentationInfo> pathChecks() {
var pathClasses = StreamSupport.stream(FileSystems.getDefault().getRootDirectories().spliterator(), false)
.map(Path::getClass)
.distinct();
return pathClasses.flatMap(pathClass -> {
InstrumentationInfoFactory instrumentation = (String methodName, Class<?>... parameterTypes) -> INSTRUMENTATION_SERVICE
.lookupImplementationMethod(
Path.class,
methodName,
pathClass,
EntitlementChecker.class,
"checkPath" + Character.toUpperCase(methodName.charAt(0)) + methodName.substring(1),
parameterTypes
);

try {
return Stream.of(
instrumentation.of("toRealPath", LinkOption[].class),
instrumentation.of("register", WatchService.class, WatchEvent.Kind[].class),
instrumentation.of("register", WatchService.class, WatchEvent.Kind[].class, WatchEvent.Modifier[].class)
);
} catch (NoSuchMethodException | ClassNotFoundException e) {
throw new RuntimeException(e);
}
});
}

/**
* Returns the "most recent" checker class compatible with the current runtime Java version.
* For checkers, we have (optionally) version specific classes, each with a prefix (e.g. Java23).
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
import org.elasticsearch.entitlement.runtime.policy.PolicyManager;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintStream;
import java.io.PrintWriter;
Expand Down Expand Up @@ -64,6 +65,8 @@
import java.nio.file.OpenOption;
import java.nio.file.Path;
import java.nio.file.StandardOpenOption;
import java.nio.file.WatchEvent;
import java.nio.file.WatchService;
import java.nio.file.attribute.FileAttribute;
import java.nio.file.attribute.UserPrincipal;
import java.nio.file.spi.FileSystemProvider;
Expand Down Expand Up @@ -1322,4 +1325,40 @@ public void checkName(Class<?> callerClass, FileStore that) {
public void checkType(Class<?> callerClass, FileStore that) {
policyManager.checkReadStoreAttributes(callerClass);
}

@Override
public void checkPathToRealPath(Class<?> callerClass, Path that, LinkOption... options) {
if (EntitlementChecker.class.isAssignableFrom(callerClass)) {
return;
}

boolean followLinks = true;
for (LinkOption option : options) {
if (option == LinkOption.NOFOLLOW_LINKS) {
followLinks = false;
}
}
if (followLinks) {
try {
policyManager.checkFileRead(callerClass, that.toRealPath());
} catch (IOException e) {}
}
policyManager.checkFileRead(callerClass, that);
}

@Override
public void checkPathRegister(Class<?> callerClass, Path that, WatchService watcher, WatchEvent.Kind<?>... events) {
policyManager.checkFileRead(callerClass, that);
}

@Override
public void checkPathRegister(
Class<?> callerClass,
Path that,
WatchService watcher,
WatchEvent.Kind<?>[] events,
WatchEvent.Modifier... modifiers
) {
policyManager.checkFileRead(callerClass, that);
}
}