Skip to content

Commit 04fdec3

Browse files
authored
Entitlement bootstrap self-test (#119376) (#119537)
* Entitlement bootstrap self-test * Add a permitted action to self-test * Refactor: Move integrity checks to record constructors * Self-test javadocs
1 parent dc4a03c commit 04fdec3

File tree

1 file changed

+74
-4
lines changed

1 file changed

+74
-4
lines changed

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

Lines changed: 74 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,21 +16,32 @@
1616

1717
import org.elasticsearch.core.SuppressForbidden;
1818
import org.elasticsearch.entitlement.initialization.EntitlementInitialization;
19+
import org.elasticsearch.entitlement.runtime.api.NotEntitledException;
1920
import org.elasticsearch.logging.LogManager;
2021
import org.elasticsearch.logging.Logger;
2122

2223
import java.io.IOException;
2324
import java.nio.file.Files;
2425
import java.nio.file.Path;
2526
import java.util.Collection;
26-
import java.util.Objects;
2727
import java.util.function.Function;
2828

29+
import static java.util.Objects.requireNonNull;
30+
2931
public class EntitlementBootstrap {
3032

31-
public record PluginData(Path pluginPath, boolean isModular, boolean isExternalPlugin) {}
33+
public record BootstrapArgs(Collection<PluginData> pluginData, Function<Class<?>, String> pluginResolver) {
34+
public BootstrapArgs {
35+
requireNonNull(pluginData);
36+
requireNonNull(pluginResolver);
37+
}
38+
}
3239

33-
public record BootstrapArgs(Collection<PluginData> pluginData, Function<Class<?>, String> pluginResolver) {}
40+
public record PluginData(Path pluginPath, boolean isModular, boolean isExternalPlugin) {
41+
public PluginData {
42+
requireNonNull(pluginPath);
43+
}
44+
}
3445

3546
private static BootstrapArgs bootstrapArgs;
3647

@@ -50,9 +61,10 @@ public static void bootstrap(Collection<PluginData> pluginData, Function<Class<?
5061
if (EntitlementBootstrap.bootstrapArgs != null) {
5162
throw new IllegalStateException("plugin data is already set");
5263
}
53-
EntitlementBootstrap.bootstrapArgs = new BootstrapArgs(Objects.requireNonNull(pluginData), Objects.requireNonNull(pluginResolver));
64+
EntitlementBootstrap.bootstrapArgs = new BootstrapArgs(pluginData, pluginResolver);
5465
exportInitializationToAgent();
5566
loadAgent(findAgentJar());
67+
selfTest();
5668
}
5769

5870
@SuppressForbidden(reason = "The VirtualMachine API is the only way to attach a java agent dynamically")
@@ -98,5 +110,63 @@ private static String findAgentJar() {
98110
}
99111
}
100112

113+
/**
114+
* Attempt a few sensitive operations to ensure that some are permitted and some are forbidden.
115+
* <p>
116+
*
117+
* This serves two purposes:
118+
*
119+
* <ol>
120+
* <li>
121+
* a smoke test to make sure the entitlements system is not completely broken, and
122+
* </li>
123+
* <li>
124+
* an early test of certain important operations so they don't fail later on at an awkward time.
125+
* </li>
126+
* </ol>
127+
*
128+
* @throws IllegalStateException if the entitlements system can't prevent an unauthorized action of our choosing
129+
*/
130+
private static void selfTest() {
131+
ensureCannotStartProcess();
132+
ensureCanCreateTempFile();
133+
}
134+
135+
private static void ensureCannotStartProcess() {
136+
try {
137+
// The command doesn't matter; it doesn't even need to exist
138+
new ProcessBuilder("").start();
139+
} catch (NotEntitledException e) {
140+
logger.debug("Success: Entitlement protection correctly prevented process creation");
141+
return;
142+
} catch (IOException e) {
143+
throw new IllegalStateException("Failed entitlement protection self-test", e);
144+
}
145+
throw new IllegalStateException("Entitlement protection self-test was incorrectly permitted");
146+
}
147+
148+
/**
149+
* Originally {@code Security.selfTest}.
150+
*/
151+
@SuppressForbidden(reason = "accesses jvm default tempdir as a self-test")
152+
private static void ensureCanCreateTempFile() {
153+
try {
154+
Path p = Files.createTempFile(null, null);
155+
p.toFile().deleteOnExit();
156+
157+
// Make an effort to clean up the file immediately; also, deleteOnExit leaves the file if the JVM exits abnormally.
158+
try {
159+
Files.delete(p);
160+
} catch (IOException ignored) {
161+
// Can be caused by virus scanner
162+
}
163+
} catch (NotEntitledException e) {
164+
throw new IllegalStateException("Entitlement protection self-test was incorrectly forbidden", e);
165+
} catch (Exception e) {
166+
throw new IllegalStateException("Unable to perform entitlement protection self-test", e);
167+
}
168+
logger.debug("Success: Entitlement protection correctly permitted temp file creation");
169+
}
170+
101171
private static final Logger logger = LogManager.getLogger(EntitlementBootstrap.class);
102172
}

0 commit comments

Comments
 (0)