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 f113960be9275..b50880494a4c6 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 @@ -66,6 +66,8 @@ import java.nio.file.attribute.FileAttribute; import java.nio.file.attribute.UserPrincipal; import java.nio.file.spi.FileSystemProvider; +import java.security.KeyStore; +import java.security.Provider; import java.security.cert.CertStoreParameters; import java.util.List; import java.util.Locale; @@ -621,12 +623,50 @@ public interface EntitlementChecker { void check$java_io_RandomAccessFile$(Class callerClass, File file, String mode); + void check$java_security_KeyStore$$getInstance(Class callerClass, File file, char[] password); + + void check$java_security_KeyStore$$getInstance(Class callerClass, File file, KeyStore.LoadStoreParameter param); + + void check$java_security_KeyStore$Builder$$newInstance(Class callerClass, File file, KeyStore.ProtectionParameter protection); + + void check$java_security_KeyStore$Builder$$newInstance( + Class callerClass, + String type, + Provider provider, + File file, + KeyStore.ProtectionParameter protection + ); + void check$java_util_Scanner$(Class callerClass, File source); void check$java_util_Scanner$(Class callerClass, File source, String charsetName); void check$java_util_Scanner$(Class callerClass, File source, Charset charset); + void check$java_util_jar_JarFile$(Class callerClass, String name); + + void check$java_util_jar_JarFile$(Class callerClass, String name, boolean verify); + + void check$java_util_jar_JarFile$(Class callerClass, File file); + + void check$java_util_jar_JarFile$(Class callerClass, File file, boolean verify); + + void check$java_util_jar_JarFile$(Class callerClass, File file, boolean verify, int mode); + + void check$java_util_jar_JarFile$(Class callerClass, File file, boolean verify, int mode, Runtime.Version version); + + void check$java_util_zip_ZipFile$(Class callerClass, String name); + + void check$java_util_zip_ZipFile$(Class callerClass, String name, Charset charset); + + void check$java_util_zip_ZipFile$(Class callerClass, File file); + + void check$java_util_zip_ZipFile$(Class callerClass, File file, int mode); + + void check$java_util_zip_ZipFile$(Class callerClass, File file, Charset charset); + + void check$java_util_zip_ZipFile$(Class callerClass, File file, int mode, Charset charset); + // nio void check$java_nio_file_Files$$getOwner(Class callerClass, Path path, LinkOption... options); 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 bedb8790c1ad8..05fbc716e5723 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 @@ -9,6 +9,7 @@ package org.elasticsearch.entitlement.qa.test; +import org.elasticsearch.core.CheckedRunnable; import org.elasticsearch.core.SuppressForbidden; import org.elasticsearch.entitlement.qa.entitled.EntitledActions; @@ -26,12 +27,22 @@ import java.nio.file.Path; import java.nio.file.Paths; import java.nio.file.attribute.UserPrincipal; +import java.security.GeneralSecurityException; +import java.security.KeyStore; import java.util.Scanner; - +import java.util.jar.JarFile; +import java.util.zip.ZipException; +import java.util.zip.ZipFile; + +import static java.nio.charset.Charset.defaultCharset; +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; import static org.elasticsearch.entitlement.qa.test.EntitlementTest.ExpectedAccess.ALWAYS_DENIED; import static org.elasticsearch.entitlement.qa.test.EntitlementTest.ExpectedAccess.PLUGINS; @SuppressForbidden(reason = "Explicitly checking APIs that are forbidden") +@SuppressWarnings("unused") // Called via reflection class FileCheckActions { static Path testRootDir = Paths.get(System.getProperty("es.entitlements.testdir")); @@ -207,21 +218,6 @@ static void fileSetWritableOwner() throws IOException { readWriteFile().toFile().setWritable(true, false); } - @EntitlementTest(expectedAccess = PLUGINS) - static void createScannerFile() throws FileNotFoundException { - new Scanner(readFile().toFile()); - } - - @EntitlementTest(expectedAccess = PLUGINS) - static void createScannerFileWithCharset() throws IOException { - new Scanner(readFile().toFile(), StandardCharsets.UTF_8); - } - - @EntitlementTest(expectedAccess = PLUGINS) - static void createScannerFileWithCharsetName() throws FileNotFoundException { - new Scanner(readFile().toFile(), "UTF-8"); - } - @EntitlementTest(expectedAccess = PLUGINS) static void createFileInputStreamFile() throws IOException { new FileInputStream(readFile().toFile()).close(); @@ -363,5 +359,140 @@ static void filesSetOwner() throws IOException { Files.setOwner(readWriteFile(), owner); // set to existing owner, just trying to execute the method } + @EntitlementTest(expectedAccess = PLUGINS) + static void keystoreGetInstance_FileCharArray() throws IOException { + try { + KeyStore.getInstance(readFile().toFile(), new char[0]); + } catch (GeneralSecurityException expected) { + return; + } + throw new AssertionError("Expected an exception"); + } + + @EntitlementTest(expectedAccess = PLUGINS) + static void keystoreGetInstance_FileLoadStoreParameter() throws IOException { + try { + KeyStore.LoadStoreParameter loadStoreParameter = () -> null; + KeyStore.getInstance(readFile().toFile(), loadStoreParameter); + } catch (GeneralSecurityException expected) { + return; + } + throw new AssertionError("Expected an exception"); + } + + @EntitlementTest(expectedAccess = PLUGINS) + static void keystoreBuilderNewInstance() { + try { + KeyStore.Builder.newInstance("", null, readFile().toFile(), null); + } catch (NullPointerException expected) { + return; + } + throw new AssertionError("Expected an exception"); + } + + @EntitlementTest(expectedAccess = PLUGINS) + static void zipFile_String() throws IOException { + expectZipException(() -> new ZipFile(readFile().toString()).close()); + } + + @EntitlementTest(expectedAccess = PLUGINS) + static void zipFile_StringCharset() throws IOException { + expectZipException(() -> new ZipFile(readFile().toString(), defaultCharset()).close()); + } + + @EntitlementTest(expectedAccess = PLUGINS) + static void zipFile_File() throws IOException { + expectZipException(() -> new ZipFile(readFile().toFile()).close()); + } + + @EntitlementTest(expectedAccess = PLUGINS) + static void zipFile_FileCharset() throws IOException { + expectZipException(() -> new ZipFile(readFile().toFile(), defaultCharset()).close()); + } + + @EntitlementTest(expectedAccess = PLUGINS) + static void zipFile_FileReadOnly() throws IOException { + expectZipException(() -> new ZipFile(readFile().toFile(), OPEN_READ).close()); + } + + @EntitlementTest(expectedAccess = PLUGINS) + static void zipFile_FileReadAndDelete() throws IOException { + expectZipException(() -> new ZipFile(createTempFileForWrite().toFile(), OPEN_READ | OPEN_DELETE).close()); + } + + @EntitlementTest(expectedAccess = PLUGINS) + static void zipFile_ReadOnlyCharset() throws IOException { + expectZipException(() -> new ZipFile(readFile().toFile(), OPEN_READ, defaultCharset()).close()); + } + + @EntitlementTest(expectedAccess = PLUGINS) + static void zipFile_ReadAndDeleteCharset() throws IOException { + expectZipException(() -> new ZipFile(createTempFileForWrite().toFile(), OPEN_READ | OPEN_DELETE, defaultCharset()).close()); + } + + @EntitlementTest(expectedAccess = PLUGINS) + static void jarFile_String() throws IOException { + expectZipException(() -> new JarFile(readFile().toString()).close()); + } + + @EntitlementTest(expectedAccess = PLUGINS) + static void jarFile_StringBoolean() throws IOException { + expectZipException(() -> new JarFile(readFile().toString(), false).close()); + } + + @EntitlementTest(expectedAccess = PLUGINS) + static void jarFile_FileReadOnly() throws IOException { + expectZipException(() -> new JarFile(readFile().toFile(), false, OPEN_READ).close()); + } + + @EntitlementTest(expectedAccess = PLUGINS) + static void jarFile_FileReadAndDelete() throws IOException { + expectZipException(() -> new JarFile(createTempFileForWrite().toFile(), false, OPEN_READ | OPEN_DELETE).close()); + } + + @EntitlementTest(expectedAccess = PLUGINS) + static void jarFile_FileBooleanReadOnlyVersion() throws IOException { + expectZipException(() -> new JarFile(readFile().toFile(), false, OPEN_READ, Runtime.version()).close()); + } + + @EntitlementTest(expectedAccess = PLUGINS) + static void jarFile_FileBooleanReadAndDeleteOnlyVersion() throws IOException { + expectZipException(() -> new JarFile(createTempFileForWrite().toFile(), false, OPEN_READ | OPEN_DELETE, Runtime.version()).close()); + } + + @EntitlementTest(expectedAccess = PLUGINS) + static void jarFile_File() throws IOException { + expectZipException(() -> new JarFile(readFile().toFile()).close()); + } + + @EntitlementTest(expectedAccess = PLUGINS) + static void jarFileFileBoolean() throws IOException { + expectZipException(() -> new JarFile(readFile().toFile(), false).close()); + } + + private static void expectZipException(CheckedRunnable action) throws IOException { + try { + action.run(); + } catch (ZipException expected) { + return; + } + throw new AssertionError("Expected an exception"); + } + + @EntitlementTest(expectedAccess = PLUGINS) + static void createScannerFile() throws FileNotFoundException { + new Scanner(readFile().toFile()); + } + + @EntitlementTest(expectedAccess = PLUGINS) + static void createScannerFileWithCharset() throws IOException { + new Scanner(readFile().toFile(), StandardCharsets.UTF_8); + } + + @EntitlementTest(expectedAccess = PLUGINS) + static void createScannerFileWithCharsetName() throws FileNotFoundException { + new Scanner(readFile().toFile(), "UTF-8"); + } + private FileCheckActions() {} } 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 1e2a88f82f7dd..4109151ddc496 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 @@ -74,6 +74,8 @@ import java.nio.file.attribute.FileAttribute; import java.nio.file.attribute.UserPrincipal; import java.nio.file.spi.FileSystemProvider; +import java.security.KeyStore; +import java.security.Provider; import java.security.cert.CertStoreParameters; import java.util.List; import java.util.Locale; @@ -1232,6 +1234,36 @@ public void checkSelectorProviderInheritedChannel(Class callerClass, Selector } } + @Override + public void check$java_security_KeyStore$$getInstance(Class callerClass, File file, char[] password) { + policyManager.checkFileRead(callerClass, file); + } + + @Override + public void check$java_security_KeyStore$$getInstance(Class callerClass, File file, KeyStore.LoadStoreParameter param) { + policyManager.checkFileRead(callerClass, file); + } + + @Override + public void check$java_security_KeyStore$Builder$$newInstance( + Class callerClass, + File file, + KeyStore.ProtectionParameter protection + ) { + policyManager.checkFileRead(callerClass, file); + } + + @Override + public void check$java_security_KeyStore$Builder$$newInstance( + Class callerClass, + String type, + Provider provider, + File file, + KeyStore.ProtectionParameter protection + ) { + policyManager.checkFileRead(callerClass, file); + } + @Override public void check$java_util_Scanner$(Class callerClass, File source) { policyManager.checkFileRead(callerClass, source); @@ -1247,6 +1279,66 @@ public void checkSelectorProviderInheritedChannel(Class callerClass, Selector policyManager.checkFileRead(callerClass, source); } + @Override + public void check$java_util_jar_JarFile$(Class callerClass, String name) { + policyManager.checkFileRead(callerClass, new File(name)); + } + + @Override + public void check$java_util_jar_JarFile$(Class callerClass, String name, boolean verify) { + policyManager.checkFileRead(callerClass, new File(name)); + } + + @Override + public void check$java_util_jar_JarFile$(Class callerClass, File file) { + policyManager.checkFileRead(callerClass, file); + } + + @Override + public void check$java_util_jar_JarFile$(Class callerClass, File file, boolean verify) { + policyManager.checkFileRead(callerClass, file); + } + + @Override + public void check$java_util_jar_JarFile$(Class callerClass, File file, boolean verify, int mode) { + policyManager.checkFileWithZipMode(callerClass, file, mode); + } + + @Override + public void check$java_util_jar_JarFile$(Class callerClass, File file, boolean verify, int mode, Runtime.Version version) { + policyManager.checkFileWithZipMode(callerClass, file, mode); + } + + @Override + public void check$java_util_zip_ZipFile$(Class callerClass, String name) { + policyManager.checkFileRead(callerClass, new File(name)); + } + + @Override + public void check$java_util_zip_ZipFile$(Class callerClass, String name, Charset charset) { + policyManager.checkFileRead(callerClass, new File(name)); + } + + @Override + public void check$java_util_zip_ZipFile$(Class callerClass, File file) { + policyManager.checkFileRead(callerClass, file); + } + + @Override + public void check$java_util_zip_ZipFile$(Class callerClass, File file, int mode) { + policyManager.checkFileWithZipMode(callerClass, file, mode); + } + + @Override + public void check$java_util_zip_ZipFile$(Class callerClass, File file, Charset charset) { + policyManager.checkFileRead(callerClass, file); + } + + @Override + public void check$java_util_zip_ZipFile$(Class callerClass, File file, int mode, Charset charset) { + policyManager.checkFileWithZipMode(callerClass, file, mode); + } + // nio @Override 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 d607a5e25b410..bbff4eed539aa 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 @@ -48,6 +48,8 @@ import static java.util.function.Predicate.not; import static java.util.stream.Collectors.groupingBy; import static java.util.stream.Collectors.toUnmodifiableMap; +import static java.util.zip.ZipFile.OPEN_DELETE; +import static java.util.zip.ZipFile.OPEN_READ; public class PolicyManager { private static final Logger logger = LogManager.getLogger(PolicyManager.class); @@ -304,6 +306,18 @@ public void checkFileWrite(Class callerClass, Path path) { } } + @SuppressForbidden(reason = "Explicitly checking File apis") + public void checkFileWithZipMode(Class callerClass, File file, int zipMode) { + assert zipMode == OPEN_READ || zipMode == (OPEN_READ | OPEN_DELETE); + if ((zipMode & OPEN_DELETE) == OPEN_DELETE) { + // This needs both read and write, but we happen to know that checkFileWrite + // actually checks both. + checkFileWrite(callerClass, file); + } else { + checkFileRead(callerClass, file); + } + } + public void checkFileDescriptorRead(Class callerClass) { neverEntitled(callerClass, () -> "read file descriptor"); }