Skip to content

Commit d907cbc

Browse files
authored
Instrument methods on File that require write permission (elastic#122109) (elastic#122387)
This commit adds instrumentation for File methods that require write permission. No server or plugins use these methods, so no policy changes were necessary. Note that since we are not planning to restrict temp file creation, the bootstrap self test on file writing was removed, which failed with these changes.
1 parent 1e11764 commit d907cbc

File tree

6 files changed

+209
-36
lines changed

6 files changed

+209
-36
lines changed

libs/entitlement/bridge/src/main/java/org/elasticsearch/entitlement/bridge/EntitlementChecker.java

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -438,6 +438,36 @@ public interface EntitlementChecker {
438438
//
439439

440440
// old io (ie File)
441+
void check$java_io_File$createNewFile(Class<?> callerClass, File file);
442+
443+
void check$java_io_File$$createTempFile(Class<?> callerClass, String prefix, String suffix, File directory);
444+
445+
void check$java_io_File$delete(Class<?> callerClass, File file);
446+
447+
void check$java_io_File$deleteOnExit(Class<?> callerClass, File file);
448+
449+
void check$java_io_File$mkdir(Class<?> callerClass, File file);
450+
451+
void check$java_io_File$mkdirs(Class<?> callerClass, File file);
452+
453+
void check$java_io_File$renameTo(Class<?> callerClass, File file, File dest);
454+
455+
void check$java_io_File$setExecutable(Class<?> callerClass, File file, boolean executable);
456+
457+
void check$java_io_File$setExecutable(Class<?> callerClass, File file, boolean executable, boolean ownerOnly);
458+
459+
void check$java_io_File$setLastModified(Class<?> callerClass, File file, long time);
460+
461+
void check$java_io_File$setReadable(Class<?> callerClass, File file, boolean readable);
462+
463+
void check$java_io_File$setReadable(Class<?> callerClass, File file, boolean readable, boolean ownerOnly);
464+
465+
void check$java_io_File$setReadOnly(Class<?> callerClass, File file);
466+
467+
void check$java_io_File$setWritable(Class<?> callerClass, File file, boolean writable);
468+
469+
void check$java_io_File$setWritable(Class<?> callerClass, File file, boolean writable, boolean ownerOnly);
470+
441471
void check$java_io_FileOutputStream$(Class<?> callerClass, File file);
442472

443473
void check$java_io_FileOutputStream$(Class<?> callerClass, File file, boolean append);

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

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,4 +27,8 @@ static void System_clearProperty(String key) {
2727
public static UserPrincipal getFileOwner(Path path) throws IOException {
2828
return Files.getOwner(path);
2929
}
30+
31+
public static void createFile(Path path) throws IOException {
32+
Files.createFile(path);
33+
}
3034
}

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

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
import org.elasticsearch.core.SuppressForbidden;
1313
import org.elasticsearch.entitlement.qa.entitled.EntitledActions;
1414

15+
import java.io.File;
1516
import java.io.FileNotFoundException;
1617
import java.io.FileOutputStream;
1718
import java.io.IOException;
@@ -45,6 +46,91 @@ private static Path readWriteFile() {
4546
return testRootDir.resolve("read_write_file");
4647
}
4748

49+
@EntitlementTest(expectedAccess = PLUGINS)
50+
static void fileCreateNewFile() throws IOException {
51+
readWriteDir().resolve("new_file").toFile().createNewFile();
52+
}
53+
54+
@EntitlementTest(expectedAccess = PLUGINS)
55+
static void fileCreateTempFile() throws IOException {
56+
File.createTempFile("prefix", "suffix", readWriteDir().toFile());
57+
}
58+
59+
@EntitlementTest(expectedAccess = PLUGINS)
60+
static void fileDelete() throws IOException {
61+
Path toDelete = readWriteDir().resolve("to_delete");
62+
EntitledActions.createFile(toDelete);
63+
toDelete.toFile().delete();
64+
}
65+
66+
@EntitlementTest(expectedAccess = PLUGINS)
67+
static void fileDeleteOnExit() throws IOException {
68+
Path toDelete = readWriteDir().resolve("to_delete_on_exit");
69+
EntitledActions.createFile(toDelete);
70+
toDelete.toFile().deleteOnExit();
71+
}
72+
73+
@EntitlementTest(expectedAccess = PLUGINS)
74+
static void fileMkdir() throws IOException {
75+
Path mkdir = readWriteDir().resolve("mkdir");
76+
mkdir.toFile().mkdir();
77+
}
78+
79+
@EntitlementTest(expectedAccess = PLUGINS)
80+
static void fileMkdirs() throws IOException {
81+
Path mkdir = readWriteDir().resolve("mkdirs");
82+
mkdir.toFile().mkdirs();
83+
}
84+
85+
@EntitlementTest(expectedAccess = PLUGINS)
86+
static void fileRenameTo() throws IOException {
87+
Path toRename = readWriteDir().resolve("to_rename");
88+
EntitledActions.createFile(toRename);
89+
toRename.toFile().renameTo(readWriteDir().resolve("renamed").toFile());
90+
}
91+
92+
@EntitlementTest(expectedAccess = PLUGINS)
93+
static void fileSetExecutable() throws IOException {
94+
readWriteFile().toFile().setExecutable(false);
95+
}
96+
97+
@EntitlementTest(expectedAccess = PLUGINS)
98+
static void fileSetExecutableOwner() throws IOException {
99+
readWriteFile().toFile().setExecutable(false, false);
100+
}
101+
102+
@EntitlementTest(expectedAccess = PLUGINS)
103+
static void fileSetLastModified() throws IOException {
104+
readWriteFile().toFile().setLastModified(System.currentTimeMillis());
105+
}
106+
107+
@EntitlementTest(expectedAccess = PLUGINS)
108+
static void fileSetReadable() throws IOException {
109+
readWriteFile().toFile().setReadable(true);
110+
}
111+
112+
@EntitlementTest(expectedAccess = PLUGINS)
113+
static void fileSetReadableOwner() throws IOException {
114+
readWriteFile().toFile().setReadable(true, false);
115+
}
116+
117+
@EntitlementTest(expectedAccess = PLUGINS)
118+
static void fileSetReadOnly() throws IOException {
119+
Path readOnly = readWriteDir().resolve("read_only");
120+
EntitledActions.createFile(readOnly);
121+
readOnly.toFile().setReadOnly();
122+
}
123+
124+
@EntitlementTest(expectedAccess = PLUGINS)
125+
static void fileSetWritable() throws IOException {
126+
readWriteFile().toFile().setWritable(true);
127+
}
128+
129+
@EntitlementTest(expectedAccess = PLUGINS)
130+
static void fileSetWritableOwner() throws IOException {
131+
readWriteFile().toFile().setWritable(true, false);
132+
}
133+
48134
@EntitlementTest(expectedAccess = PLUGINS)
49135
static void createScannerFile() throws FileNotFoundException {
50136
new Scanner(readFile().toFile());

libs/entitlement/src/main/java/org/elasticsearch/entitlement/bootstrap/EntitlementBootstrap.java

Lines changed: 0 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@
1515
import com.sun.tools.attach.VirtualMachine;
1616

1717
import org.elasticsearch.core.CheckedConsumer;
18-
import org.elasticsearch.core.CheckedSupplier;
1918
import org.elasticsearch.core.SuppressForbidden;
2019
import org.elasticsearch.entitlement.initialization.EntitlementInitialization;
2120
import org.elasticsearch.entitlement.runtime.api.NotEntitledException;
@@ -27,7 +26,6 @@
2726
import java.lang.reflect.InvocationTargetException;
2827
import java.nio.file.Files;
2928
import java.nio.file.Path;
30-
import java.nio.file.attribute.FileAttribute;
3129
import java.util.Map;
3230
import java.util.function.Function;
3331

@@ -149,11 +147,8 @@ private static String findAgentJar() {
149147
*/
150148
private static void selfTest() {
151149
ensureCannotStartProcess(ProcessBuilder::start);
152-
ensureCanCreateTempFile(EntitlementBootstrap::createTempFile);
153-
154150
// Try again with reflection
155151
ensureCannotStartProcess(EntitlementBootstrap::reflectiveStartProcess);
156-
ensureCanCreateTempFile(EntitlementBootstrap::reflectiveCreateTempFile);
157152
}
158153

159154
private static void ensureCannotStartProcess(CheckedConsumer<ProcessBuilder, ?> startProcess) {
@@ -169,31 +164,6 @@ private static void ensureCannotStartProcess(CheckedConsumer<ProcessBuilder, ?>
169164
throw new IllegalStateException("Entitlement protection self-test was incorrectly permitted");
170165
}
171166

172-
@SuppressForbidden(reason = "accesses jvm default tempdir as a self-test")
173-
private static void ensureCanCreateTempFile(CheckedSupplier<Path, ?> createTempFile) {
174-
try {
175-
Path p = createTempFile.get();
176-
p.toFile().deleteOnExit();
177-
178-
// Make an effort to clean up the file immediately; also, deleteOnExit leaves the file if the JVM exits abnormally.
179-
try {
180-
Files.delete(p);
181-
} catch (IOException ignored) {
182-
// Can be caused by virus scanner
183-
}
184-
} catch (NotEntitledException e) {
185-
throw new IllegalStateException("Entitlement protection self-test was incorrectly forbidden", e);
186-
} catch (Exception e) {
187-
throw new IllegalStateException("Unable to perform entitlement protection self-test", e);
188-
}
189-
logger.debug("Success: Entitlement protection correctly permitted temp file creation");
190-
}
191-
192-
@SuppressForbidden(reason = "accesses jvm default tempdir as a self-test")
193-
private static Path createTempFile() throws Exception {
194-
return Files.createTempFile(null, null);
195-
}
196-
197167
private static void reflectiveStartProcess(ProcessBuilder pb) throws Exception {
198168
try {
199169
var start = ProcessBuilder.class.getMethod("start");
@@ -203,10 +173,5 @@ private static void reflectiveStartProcess(ProcessBuilder pb) throws Exception {
203173
}
204174
}
205175

206-
private static Path reflectiveCreateTempFile() throws Exception {
207-
return (Path) Files.class.getMethod("createTempFile", String.class, String.class, FileAttribute[].class)
208-
.invoke(null, null, null, new FileAttribute<?>[0]);
209-
}
210-
211176
private static final Logger logger = LogManager.getLogger(EntitlementBootstrap.class);
212177
}

libs/entitlement/src/main/java/org/elasticsearch/entitlement/initialization/EntitlementInitialization.java

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@
2424
import org.elasticsearch.entitlement.runtime.policy.entitlements.CreateClassLoaderEntitlement;
2525
import org.elasticsearch.entitlement.runtime.policy.entitlements.Entitlement;
2626
import org.elasticsearch.entitlement.runtime.policy.entitlements.ExitVMEntitlement;
27+
import org.elasticsearch.entitlement.runtime.policy.entitlements.FilesEntitlement;
28+
import org.elasticsearch.entitlement.runtime.policy.entitlements.FilesEntitlement.FileData;
2729
import org.elasticsearch.entitlement.runtime.policy.entitlements.InboundNetworkEntitlement;
2830
import org.elasticsearch.entitlement.runtime.policy.entitlements.LoadNativeLibrariesEntitlement;
2931
import org.elasticsearch.entitlement.runtime.policy.entitlements.OutboundNetworkEntitlement;
@@ -37,13 +39,16 @@
3739
import java.nio.file.Path;
3840
import java.nio.file.spi.FileSystemProvider;
3941
import java.util.ArrayList;
42+
import java.util.Arrays;
4043
import java.util.HashMap;
4144
import java.util.List;
4245
import java.util.Map;
4346
import java.util.Set;
4447
import java.util.stream.Collectors;
4548
import java.util.stream.Stream;
4649

50+
import static org.elasticsearch.entitlement.runtime.policy.entitlements.FilesEntitlement.Mode.READ_WRITE;
51+
4752
/**
4853
* Called by the agent during {@code agentmain} to configure the entitlement system,
4954
* instantiate and configure an {@link EntitlementChecker},
@@ -109,6 +114,7 @@ private static Class<?>[] findClassesToRetransform(Class<?>[] loadedClasses, Set
109114

110115
private static PolicyManager createPolicyManager() {
111116
Map<String, Policy> pluginPolicies = EntitlementBootstrap.bootstrapArgs().pluginPolicies();
117+
Path[] dataDirs = EntitlementBootstrap.bootstrapArgs().dataDirs();
112118

113119
// TODO(ES-10031): Decide what goes in the elasticsearch default policy and extend it
114120
var serverPolicy = new Policy(
@@ -129,7 +135,13 @@ private static PolicyManager createPolicyManager() {
129135
new Scope("org.apache.httpcomponents.httpclient", List.of(new OutboundNetworkEntitlement())),
130136
new Scope("io.netty.transport", List.of(new InboundNetworkEntitlement(), new OutboundNetworkEntitlement())),
131137
new Scope("org.apache.lucene.core", List.of(new LoadNativeLibrariesEntitlement())),
132-
new Scope("org.elasticsearch.nativeaccess", List.of(new LoadNativeLibrariesEntitlement()))
138+
new Scope(
139+
"org.elasticsearch.nativeaccess",
140+
List.of(
141+
new LoadNativeLibrariesEntitlement(),
142+
new FilesEntitlement(Arrays.stream(dataDirs).map(d -> new FileData(d.toString(), READ_WRITE)).toList())
143+
)
144+
)
133145
)
134146
);
135147
// agents run without a module, so this is a special hack for the apm agent

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

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -853,6 +853,82 @@ public void checkSelectorProviderInheritedChannel(Class<?> callerClass, Selector
853853

854854
// old io (ie File)
855855

856+
@Override
857+
public void check$java_io_File$createNewFile(Class<?> callerClass, File file) {
858+
policyManager.checkFileWrite(callerClass, file);
859+
}
860+
861+
@Override
862+
public void check$java_io_File$$createTempFile(Class<?> callerClass, String prefix, String suffix, File directory) {
863+
policyManager.checkFileWrite(callerClass, directory);
864+
}
865+
866+
@Override
867+
public void check$java_io_File$delete(Class<?> callerClass, File file) {
868+
policyManager.checkFileWrite(callerClass, file);
869+
}
870+
871+
@Override
872+
public void check$java_io_File$deleteOnExit(Class<?> callerClass, File file) {
873+
policyManager.checkFileWrite(callerClass, file);
874+
}
875+
876+
@Override
877+
public void check$java_io_File$mkdir(Class<?> callerClass, File file) {
878+
policyManager.checkFileWrite(callerClass, file);
879+
}
880+
881+
@Override
882+
public void check$java_io_File$mkdirs(Class<?> callerClass, File file) {
883+
policyManager.checkFileWrite(callerClass, file);
884+
}
885+
886+
@Override
887+
public void check$java_io_File$renameTo(Class<?> callerClass, File file, File dest) {
888+
policyManager.checkFileRead(callerClass, file);
889+
policyManager.checkFileWrite(callerClass, dest);
890+
}
891+
892+
@Override
893+
public void check$java_io_File$setExecutable(Class<?> callerClass, File file, boolean executable) {
894+
policyManager.checkFileWrite(callerClass, file);
895+
}
896+
897+
@Override
898+
public void check$java_io_File$setExecutable(Class<?> callerClass, File file, boolean executable, boolean ownerOnly) {
899+
policyManager.checkFileWrite(callerClass, file);
900+
}
901+
902+
@Override
903+
public void check$java_io_File$setLastModified(Class<?> callerClass, File file, long time) {
904+
policyManager.checkFileWrite(callerClass, file);
905+
}
906+
907+
@Override
908+
public void check$java_io_File$setReadable(Class<?> callerClass, File file, boolean readable) {
909+
policyManager.checkFileWrite(callerClass, file);
910+
}
911+
912+
@Override
913+
public void check$java_io_File$setReadable(Class<?> callerClass, File file, boolean readable, boolean ownerOnly) {
914+
policyManager.checkFileWrite(callerClass, file);
915+
}
916+
917+
@Override
918+
public void check$java_io_File$setReadOnly(Class<?> callerClass, File file) {
919+
policyManager.checkFileWrite(callerClass, file);
920+
}
921+
922+
@Override
923+
public void check$java_io_File$setWritable(Class<?> callerClass, File file, boolean writable) {
924+
policyManager.checkFileWrite(callerClass, file);
925+
}
926+
927+
@Override
928+
public void check$java_io_File$setWritable(Class<?> callerClass, File file, boolean writable, boolean ownerOnly) {
929+
policyManager.checkFileWrite(callerClass, file);
930+
}
931+
856932
@Override
857933
public void check$java_io_FileOutputStream$(Class<?> callerClass, String name) {
858934
policyManager.checkFileWrite(callerClass, new File(name));

0 commit comments

Comments
 (0)