Skip to content

Commit 5f00b64

Browse files
authored
Instrument methods on File that require write permission (#122109)
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 05a2003 commit 5f00b64

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
@@ -501,6 +501,36 @@ public interface EntitlementChecker {
501501
//
502502

503503
// old io (ie File)
504+
void check$java_io_File$createNewFile(Class<?> callerClass, File file);
505+
506+
void check$java_io_File$$createTempFile(Class<?> callerClass, String prefix, String suffix, File directory);
507+
508+
void check$java_io_File$delete(Class<?> callerClass, File file);
509+
510+
void check$java_io_File$deleteOnExit(Class<?> callerClass, File file);
511+
512+
void check$java_io_File$mkdir(Class<?> callerClass, File file);
513+
514+
void check$java_io_File$mkdirs(Class<?> callerClass, File file);
515+
516+
void check$java_io_File$renameTo(Class<?> callerClass, File file, File dest);
517+
518+
void check$java_io_File$setExecutable(Class<?> callerClass, File file, boolean executable);
519+
520+
void check$java_io_File$setExecutable(Class<?> callerClass, File file, boolean executable, boolean ownerOnly);
521+
522+
void check$java_io_File$setLastModified(Class<?> callerClass, File file, long time);
523+
524+
void check$java_io_File$setReadable(Class<?> callerClass, File file, boolean readable);
525+
526+
void check$java_io_File$setReadable(Class<?> callerClass, File file, boolean readable, boolean ownerOnly);
527+
528+
void check$java_io_File$setReadOnly(Class<?> callerClass, File file);
529+
530+
void check$java_io_File$setWritable(Class<?> callerClass, File file, boolean writable);
531+
532+
void check$java_io_File$setWritable(Class<?> callerClass, File file, boolean writable, boolean ownerOnly);
533+
504534
void check$java_io_FileOutputStream$(Class<?> callerClass, File file);
505535

506536
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 @@ 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;
@@ -39,6 +41,7 @@
3941
import java.nio.file.Path;
4042
import java.nio.file.spi.FileSystemProvider;
4143
import java.util.ArrayList;
44+
import java.util.Arrays;
4245
import java.util.HashMap;
4346
import java.util.List;
4447
import java.util.Map;
@@ -47,6 +50,8 @@
4750
import java.util.stream.Stream;
4851
import java.util.stream.StreamSupport;
4952

53+
import static org.elasticsearch.entitlement.runtime.policy.entitlements.FilesEntitlement.Mode.READ_WRITE;
54+
5055
/**
5156
* Called by the agent during {@code agentmain} to configure the entitlement system,
5257
* instantiate and configure an {@link EntitlementChecker},
@@ -121,6 +126,7 @@ private static Class<?>[] findClassesToRetransform(Class<?>[] loadedClasses, Set
121126

122127
private static PolicyManager createPolicyManager() {
123128
Map<String, Policy> pluginPolicies = EntitlementBootstrap.bootstrapArgs().pluginPolicies();
129+
Path[] dataDirs = EntitlementBootstrap.bootstrapArgs().dataDirs();
124130

125131
// TODO(ES-10031): Decide what goes in the elasticsearch default policy and extend it
126132
var serverPolicy = new Policy(
@@ -142,7 +148,13 @@ private static PolicyManager createPolicyManager() {
142148
new Scope("org.apache.httpcomponents.httpclient", List.of(new OutboundNetworkEntitlement())),
143149
new Scope("io.netty.transport", List.of(new InboundNetworkEntitlement(), new OutboundNetworkEntitlement())),
144150
new Scope("org.apache.lucene.core", List.of(new LoadNativeLibrariesEntitlement())),
145-
new Scope("org.elasticsearch.nativeaccess", List.of(new LoadNativeLibrariesEntitlement()))
151+
new Scope(
152+
"org.elasticsearch.nativeaccess",
153+
List.of(
154+
new LoadNativeLibrariesEntitlement(),
155+
new FilesEntitlement(Arrays.stream(dataDirs).map(d -> new FileData(d.toString(), READ_WRITE)).toList())
156+
)
157+
)
146158
)
147159
);
148160
// 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
@@ -941,6 +941,82 @@ public void checkSelectorProviderInheritedChannel(Class<?> callerClass, Selector
941941

942942
// old io (ie File)
943943

944+
@Override
945+
public void check$java_io_File$createNewFile(Class<?> callerClass, File file) {
946+
policyManager.checkFileWrite(callerClass, file);
947+
}
948+
949+
@Override
950+
public void check$java_io_File$$createTempFile(Class<?> callerClass, String prefix, String suffix, File directory) {
951+
policyManager.checkFileWrite(callerClass, directory);
952+
}
953+
954+
@Override
955+
public void check$java_io_File$delete(Class<?> callerClass, File file) {
956+
policyManager.checkFileWrite(callerClass, file);
957+
}
958+
959+
@Override
960+
public void check$java_io_File$deleteOnExit(Class<?> callerClass, File file) {
961+
policyManager.checkFileWrite(callerClass, file);
962+
}
963+
964+
@Override
965+
public void check$java_io_File$mkdir(Class<?> callerClass, File file) {
966+
policyManager.checkFileWrite(callerClass, file);
967+
}
968+
969+
@Override
970+
public void check$java_io_File$mkdirs(Class<?> callerClass, File file) {
971+
policyManager.checkFileWrite(callerClass, file);
972+
}
973+
974+
@Override
975+
public void check$java_io_File$renameTo(Class<?> callerClass, File file, File dest) {
976+
policyManager.checkFileRead(callerClass, file);
977+
policyManager.checkFileWrite(callerClass, dest);
978+
}
979+
980+
@Override
981+
public void check$java_io_File$setExecutable(Class<?> callerClass, File file, boolean executable) {
982+
policyManager.checkFileWrite(callerClass, file);
983+
}
984+
985+
@Override
986+
public void check$java_io_File$setExecutable(Class<?> callerClass, File file, boolean executable, boolean ownerOnly) {
987+
policyManager.checkFileWrite(callerClass, file);
988+
}
989+
990+
@Override
991+
public void check$java_io_File$setLastModified(Class<?> callerClass, File file, long time) {
992+
policyManager.checkFileWrite(callerClass, file);
993+
}
994+
995+
@Override
996+
public void check$java_io_File$setReadable(Class<?> callerClass, File file, boolean readable) {
997+
policyManager.checkFileWrite(callerClass, file);
998+
}
999+
1000+
@Override
1001+
public void check$java_io_File$setReadable(Class<?> callerClass, File file, boolean readable, boolean ownerOnly) {
1002+
policyManager.checkFileWrite(callerClass, file);
1003+
}
1004+
1005+
@Override
1006+
public void check$java_io_File$setReadOnly(Class<?> callerClass, File file) {
1007+
policyManager.checkFileWrite(callerClass, file);
1008+
}
1009+
1010+
@Override
1011+
public void check$java_io_File$setWritable(Class<?> callerClass, File file, boolean writable) {
1012+
policyManager.checkFileWrite(callerClass, file);
1013+
}
1014+
1015+
@Override
1016+
public void check$java_io_File$setWritable(Class<?> callerClass, File file, boolean writable, boolean ownerOnly) {
1017+
policyManager.checkFileWrite(callerClass, file);
1018+
}
1019+
9441020
@Override
9451021
public void check$java_io_FileOutputStream$(Class<?> callerClass, String name) {
9461022
policyManager.checkFileWrite(callerClass, new File(name));

0 commit comments

Comments
 (0)