diff --git a/libs/entitlement/bridge/src/main/java/module-info.java b/libs/entitlement/bridge/src/main/java/module-info.java index 518a0a1ef29ec..6a85013c1f1f5 100644 --- a/libs/entitlement/bridge/src/main/java/module-info.java +++ b/libs/entitlement/bridge/src/main/java/module-info.java @@ -12,6 +12,7 @@ module org.elasticsearch.entitlement.bridge { requires java.net.http; requires jdk.net; + requires java.logging; exports org.elasticsearch.entitlement.bridge; } diff --git a/libs/entitlement/bridge/src/main/java/org/elasticsearch/entitlement/bridge/EntitlementChecker.java b/libs/entitlement/bridge/src/main/java/org/elasticsearch/entitlement/bridge/EntitlementChecker.java index f0bbcd9b7d09e..ce162abed60c6 100644 --- a/libs/entitlement/bridge/src/main/java/org/elasticsearch/entitlement/bridge/EntitlementChecker.java +++ b/libs/entitlement/bridge/src/main/java/org/elasticsearch/entitlement/bridge/EntitlementChecker.java @@ -88,6 +88,7 @@ import java.util.concurrent.ForkJoinPool; import java.util.function.BiPredicate; import java.util.function.Consumer; +import java.util.logging.FileHandler; import javax.net.ssl.HostnameVerifier; import javax.net.ssl.HttpsURLConnection; @@ -882,9 +883,34 @@ public interface EntitlementChecker { void check$java_nio_file_Files$$lines(Class callerClass, Path path); - // file system providers void check$java_nio_file_spi_FileSystemProvider$(Class callerClass); + void check$java_util_logging_FileHandler$(Class callerClass); + + void check$java_util_logging_FileHandler$(Class callerClass, String pattern); + + void check$java_util_logging_FileHandler$(Class callerClass, String pattern, boolean append); + + void check$java_util_logging_FileHandler$(Class callerClass, String pattern, int limit, int count); + + void check$java_util_logging_FileHandler$(Class callerClass, String pattern, int limit, int count, boolean append); + + void check$java_util_logging_FileHandler$(Class callerClass, String pattern, long limit, int count, boolean append); + + void check$java_util_logging_FileHandler$close(Class callerClass, FileHandler that); + + void check$java_net_http_HttpRequest$BodyPublishers$$ofFile(Class callerClass, Path path); + + void check$java_net_http_HttpResponse$BodyHandlers$$ofFile(Class callerClass, Path path); + + void check$java_net_http_HttpResponse$BodyHandlers$$ofFile(Class callerClass, Path path, OpenOption... options); + + void check$java_net_http_HttpResponse$BodyHandlers$$ofFileDownload(Class callerClass, Path directory, OpenOption... openOptions); + + void check$java_net_http_HttpResponse$BodySubscribers$$ofFile(Class callerClass, Path directory); + + void check$java_net_http_HttpResponse$BodySubscribers$$ofFile(Class callerClass, Path directory, OpenOption... openOptions); + void checkNewFileSystem(Class callerClass, FileSystemProvider that, URI uri, Map env); void checkNewFileSystem(Class callerClass, FileSystemProvider that, Path path, Map env); diff --git a/libs/entitlement/qa/entitled-plugin/src/main/java/module-info.java b/libs/entitlement/qa/entitled-plugin/src/main/java/module-info.java index eafac9006daec..74559a12a4da4 100644 --- a/libs/entitlement/qa/entitled-plugin/src/main/java/module-info.java +++ b/libs/entitlement/qa/entitled-plugin/src/main/java/module-info.java @@ -12,6 +12,7 @@ requires org.elasticsearch.entitlement; requires org.elasticsearch.base; // SuppressForbidden requires org.elasticsearch.logging; + requires java.logging; exports org.elasticsearch.entitlement.qa.entitled; // Must be unqualified so non-modular IT tests can call us } diff --git a/libs/entitlement/qa/entitlement-test-plugin/src/main/java/org/elasticsearch/entitlement/qa/test/FileCheckActions.java b/libs/entitlement/qa/entitlement-test-plugin/src/main/java/org/elasticsearch/entitlement/qa/test/FileCheckActions.java index d62fbf458be0f..b22643c90064e 100644 --- a/libs/entitlement/qa/entitlement-test-plugin/src/main/java/org/elasticsearch/entitlement/qa/test/FileCheckActions.java +++ b/libs/entitlement/qa/entitlement-test-plugin/src/main/java/org/elasticsearch/entitlement/qa/test/FileCheckActions.java @@ -22,6 +22,8 @@ import java.io.FileWriter; import java.io.IOException; import java.io.RandomAccessFile; +import java.net.http.HttpRequest; +import java.net.http.HttpResponse; import java.nio.charset.StandardCharsets; import java.nio.file.Path; import java.nio.file.Paths; @@ -29,10 +31,13 @@ import java.security.KeyStore; import java.util.Scanner; import java.util.jar.JarFile; +import java.util.logging.FileHandler; import java.util.zip.ZipException; import java.util.zip.ZipFile; import static java.nio.charset.Charset.defaultCharset; +import static java.nio.file.StandardOpenOption.CREATE; +import static java.nio.file.StandardOpenOption.WRITE; import static java.util.zip.ZipFile.OPEN_DELETE; import static java.util.zip.ZipFile.OPEN_READ; import static org.elasticsearch.entitlement.qa.entitled.EntitledActions.createTempFileForWrite; @@ -477,5 +482,86 @@ static void createScannerFileWithCharsetName() throws FileNotFoundException { new Scanner(readFile().toFile(), "UTF-8"); } + @EntitlementTest(expectedAccess = ALWAYS_DENIED) + static void fileHandler() throws IOException { + new FileHandler(); + } + + @EntitlementTest(expectedAccess = ALWAYS_DENIED) + static void fileHandler_String() throws IOException { + new FileHandler(readFile().toString()); + } + + @EntitlementTest(expectedAccess = ALWAYS_DENIED) + static void fileHandler_StringBoolean() throws IOException { + new FileHandler(readFile().toString(), false); + } + + @EntitlementTest(expectedAccess = ALWAYS_DENIED) + static void fileHandler_StringIntInt() throws IOException { + new FileHandler(readFile().toString(), 1, 2); + } + + @EntitlementTest(expectedAccess = ALWAYS_DENIED) + static void fileHandler_StringIntIntBoolean() throws IOException { + new FileHandler(readFile().toString(), 1, 2, false); + } + + @EntitlementTest(expectedAccess = ALWAYS_DENIED) + static void fileHandler_StringLongIntBoolean() throws IOException { + new FileHandler(readFile().toString(), 1L, 2, false); + } + + @EntitlementTest(expectedAccess = PLUGINS) + static void httpRequestBodyPublishersOfFile() throws IOException { + HttpRequest.BodyPublishers.ofFile(readFile()); + } + + @EntitlementTest(expectedAccess = PLUGINS) + static void httpResponseBodyHandlersOfFile() { + HttpResponse.BodyHandlers.ofFile(readWriteFile()); + } + + @EntitlementTest(expectedAccess = ALWAYS_DENIED) + static void httpResponseBodyHandlersOfFile_readOnly() { + HttpResponse.BodyHandlers.ofFile(readFile()); + } + + @EntitlementTest(expectedAccess = PLUGINS) + static void httpResponseBodyHandlersOfFileDownload() { + HttpResponse.BodyHandlers.ofFileDownload(readWriteDir()); + } + + @EntitlementTest(expectedAccess = ALWAYS_DENIED) + static void httpResponseBodyHandlersOfFileDownload_readOnly() { + HttpResponse.BodyHandlers.ofFileDownload(readDir()); + } + + @EntitlementTest(expectedAccess = PLUGINS) + static void httpResponseBodySubscribersOfFile_File() { + HttpResponse.BodySubscribers.ofFile(readWriteFile()); + } + + @EntitlementTest(expectedAccess = ALWAYS_DENIED) + static void httpResponseBodySubscribersOfFile_File_readOnly() { + HttpResponse.BodySubscribers.ofFile(readFile()); + } + + @EntitlementTest(expectedAccess = PLUGINS) + static void httpResponseBodySubscribersOfFile_FileOpenOptions() { + // Note that, unlike other methods like BodyHandlers.ofFile, this is indeed + // an overload distinct from ofFile with no OpenOptions, and so it needs its + // own instrumentation and its own test. + HttpResponse.BodySubscribers.ofFile(readWriteFile(), CREATE, WRITE); + } + + @EntitlementTest(expectedAccess = ALWAYS_DENIED) + static void httpResponseBodySubscribersOfFile_FileOpenOptions_readOnly() { + // Note that, unlike other methods like BodyHandlers.ofFile, this is indeed + // an overload distinct from ofFile with no OpenOptions, and so it needs its + // own instrumentation and its own test. + HttpResponse.BodySubscribers.ofFile(readFile(), CREATE, WRITE); + } + private FileCheckActions() {} } diff --git a/libs/entitlement/src/main/java/module-info.java b/libs/entitlement/src/main/java/module-info.java index 697d26747b806..d6737a14a0b88 100644 --- a/libs/entitlement/src/main/java/module-info.java +++ b/libs/entitlement/src/main/java/module-info.java @@ -8,12 +8,13 @@ */ module org.elasticsearch.entitlement { + requires org.elasticsearch.base; requires org.elasticsearch.xcontent; requires org.elasticsearch.logging; requires java.instrument; - requires org.elasticsearch.base; - requires jdk.attach; + requires java.logging; requires java.net.http; + requires jdk.attach; requires jdk.net; requires static org.elasticsearch.entitlement.bridge; // At runtime, this will be in java.base diff --git a/libs/entitlement/src/main/java/org/elasticsearch/entitlement/runtime/api/ElasticsearchEntitlementChecker.java b/libs/entitlement/src/main/java/org/elasticsearch/entitlement/runtime/api/ElasticsearchEntitlementChecker.java index f6a45cce3c56c..028d03056724d 100644 --- a/libs/entitlement/src/main/java/org/elasticsearch/entitlement/runtime/api/ElasticsearchEntitlementChecker.java +++ b/libs/entitlement/src/main/java/org/elasticsearch/entitlement/runtime/api/ElasticsearchEntitlementChecker.java @@ -97,6 +97,7 @@ import java.util.concurrent.ForkJoinPool; import java.util.function.BiPredicate; import java.util.function.Consumer; +import java.util.logging.FileHandler; import javax.net.ssl.HostnameVerifier; import javax.net.ssl.HttpsURLConnection; @@ -1845,6 +1846,78 @@ public void checkSelectorProviderInheritedChannel(Class callerClass, Selector policyManager.checkChangeJVMGlobalState(callerClass); } + @Override + public void check$java_util_logging_FileHandler$(Class callerClass) { + policyManager.checkLoggingFileHandler(callerClass); + } + + @Override + public void check$java_util_logging_FileHandler$(Class callerClass, String pattern) { + policyManager.checkLoggingFileHandler(callerClass); + } + + @Override + public void check$java_util_logging_FileHandler$(Class callerClass, String pattern, boolean append) { + policyManager.checkLoggingFileHandler(callerClass); + } + + @Override + public void check$java_util_logging_FileHandler$(Class callerClass, String pattern, int limit, int count) { + policyManager.checkLoggingFileHandler(callerClass); + } + + @Override + public void check$java_util_logging_FileHandler$(Class callerClass, String pattern, int limit, int count, boolean append) { + policyManager.checkLoggingFileHandler(callerClass); + } + + @Override + public void check$java_util_logging_FileHandler$(Class callerClass, String pattern, long limit, int count, boolean append) { + policyManager.checkLoggingFileHandler(callerClass); + } + + @Override + public void check$java_util_logging_FileHandler$close(Class callerClass, FileHandler that) { + // Note that there's no IT test for this one, because there's no way to create + // a FileHandler. However, we have this check just in case someone does manage + // to get their hands on a FileHandler and uses close() to cause its lock file to be deleted. + policyManager.checkLoggingFileHandler(callerClass); + } + + @Override + public void check$java_net_http_HttpRequest$BodyPublishers$$ofFile(Class callerClass, Path path) { + policyManager.checkFileRead(callerClass, path); + } + + @Override + public void check$java_net_http_HttpResponse$BodyHandlers$$ofFile(Class callerClass, Path path) { + policyManager.checkFileWrite(callerClass, path); + } + + @Override + public void check$java_net_http_HttpResponse$BodyHandlers$$ofFile(Class callerClass, Path path, OpenOption... options) { + policyManager.checkFileWrite(callerClass, path); + } + + @Override + public void check$java_net_http_HttpResponse$BodyHandlers$$ofFileDownload( + Class callerClass, + Path directory, + OpenOption... openOptions + ) { + policyManager.checkFileWrite(callerClass, directory); + } + + @Override + public void check$java_net_http_HttpResponse$BodySubscribers$$ofFile(Class callerClass, Path directory) { + policyManager.checkFileWrite(callerClass, directory); + } + + @Override + public void check$java_net_http_HttpResponse$BodySubscribers$$ofFile(Class callerClass, Path directory, OpenOption... openOptions) { + policyManager.checkFileWrite(callerClass, directory); + } + @Override public void checkNewFileSystem(Class callerClass, FileSystemProvider that, URI uri, Map env) { policyManager.checkChangeJVMGlobalState(callerClass); diff --git a/libs/entitlement/src/main/java/org/elasticsearch/entitlement/runtime/policy/PolicyManager.java b/libs/entitlement/src/main/java/org/elasticsearch/entitlement/runtime/policy/PolicyManager.java index 66e44576b7452..2aafcfc594abd 100644 --- a/libs/entitlement/src/main/java/org/elasticsearch/entitlement/runtime/policy/PolicyManager.java +++ b/libs/entitlement/src/main/java/org/elasticsearch/entitlement/runtime/policy/PolicyManager.java @@ -240,6 +240,10 @@ public void checkChangeJVMGlobalState(Class callerClass) { neverEntitled(callerClass, () -> walkStackForCheckMethodName().orElse("change JVM global state")); } + public void checkLoggingFileHandler(Class callerClass) { + neverEntitled(callerClass, () -> walkStackForCheckMethodName().orElse("create logging file handler")); + } + private Optional walkStackForCheckMethodName() { // Look up the check$ method to compose an informative error message. // This way, we don't need to painstakingly describe every individual global-state change.