Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the "Elastic License
* 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side
* Public License v 1"; you may not use this file except in compliance with, at
* your election, the "Elastic License 2.0", the "GNU Affero General Public
* License v3.0 only", or the "Server Side Public License, v 1".
*/

package org.elasticsearch.core;

/**
* A {@link java.util.function.Supplier}-like interface which allows throwing checked exceptions.
*/
@FunctionalInterface
public interface CheckedSupplier<T, E extends Exception> {
T get() throws E;
}
Original file line number Diff line number Diff line change
Expand Up @@ -52,10 +52,9 @@
* <p>
* A bit like Mockito but way more painful.
*/
class DummyImplementations {

static class DummyLocaleServiceProvider extends LocaleServiceProvider {
public class DummyImplementations {

public static class DummyLocaleServiceProvider extends LocaleServiceProvider {
@Override
public Locale[] getAvailableLocales() {
throw unexpected();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,9 @@ static CheckAction alwaysDenied(CheckedRunnable<Exception> action) {

private static final Map<String, CheckAction> checkActions = Stream.concat(
Stream.<Entry<String, CheckAction>>of(
entry("static_reflection", deniedToPlugins(RestEntitlementsCheckAction::staticMethodNeverEntitledViaReflection)),
entry("nonstatic_reflection", deniedToPlugins(RestEntitlementsCheckAction::nonstaticMethodNeverEntitledViaReflection)),
entry("constructor_reflection", deniedToPlugins(RestEntitlementsCheckAction::constructorNeverEntitledViaReflection)),
entry("runtime_exit", deniedToPlugins(RestEntitlementsCheckAction::runtimeExit)),
entry("runtime_halt", deniedToPlugins(RestEntitlementsCheckAction::runtimeHalt)),
entry("system_exit", deniedToPlugins(RestEntitlementsCheckAction::systemExit)),
Expand Down Expand Up @@ -338,6 +341,11 @@ private static void systemExit() {
System.exit(123);
}

private static void staticMethodNeverEntitledViaReflection() throws Exception {
Method systemExit = System.class.getMethod("exit", int.class);
systemExit.invoke(null, 123);
}

private static void createClassLoader() throws IOException {
try (var classLoader = new URLClassLoader("test", new URL[0], RestEntitlementsCheckAction.class.getClassLoader())) {
logger.info("Created URLClassLoader [{}]", classLoader.getName());
Expand All @@ -348,6 +356,11 @@ private static void processBuilder_start() throws IOException {
new ProcessBuilder("").start();
}

private static void nonstaticMethodNeverEntitledViaReflection() throws Exception {
Method processBuilderStart = ProcessBuilder.class.getMethod("start");
processBuilderStart.invoke(new ProcessBuilder(""));
}

private static void processBuilder_startPipeline() throws IOException {
ProcessBuilder.startPipeline(List.of());
}
Expand Down Expand Up @@ -386,6 +399,10 @@ private static void setHttpsConnectionProperties() {
new DummyLocaleServiceProvider();
}

private static void constructorNeverEntitledViaReflection() throws Exception {
DummyLocaleServiceProvider.class.getConstructor().newInstance();
}

private static void breakIteratorProvider$() {
new DummyBreakIteratorProvider();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@
import com.sun.tools.attach.AttachNotSupportedException;
import com.sun.tools.attach.VirtualMachine;

import org.elasticsearch.core.CheckedConsumer;
import org.elasticsearch.core.CheckedSupplier;
import org.elasticsearch.core.SuppressForbidden;
import org.elasticsearch.entitlement.initialization.EntitlementInitialization;
import org.elasticsearch.entitlement.runtime.api.NotEntitledException;
Expand All @@ -22,8 +24,10 @@
import org.elasticsearch.logging.Logger;

import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.attribute.FileAttribute;
import java.util.Map;
import java.util.function.Function;

Expand Down Expand Up @@ -144,30 +148,31 @@ private static String findAgentJar() {
* @throws IllegalStateException if the entitlements system can't prevent an unauthorized action of our choosing
*/
private static void selfTest() {
ensureCannotStartProcess();
ensureCanCreateTempFile();
ensureCannotStartProcess(ProcessBuilder::start);
ensureCanCreateTempFile(EntitlementBootstrap::createTempFile);

// Try again with reflection
ensureCannotStartProcess(EntitlementBootstrap::reflectiveStartProcess);
ensureCanCreateTempFile(EntitlementBootstrap::reflectiveCreateTempFile);
}

private static void ensureCannotStartProcess() {
private static void ensureCannotStartProcess(CheckedConsumer<ProcessBuilder, ?> startProcess) {
try {
// The command doesn't matter; it doesn't even need to exist
new ProcessBuilder("").start();
startProcess.accept(new ProcessBuilder(""));
} catch (NotEntitledException e) {
logger.debug("Success: Entitlement protection correctly prevented process creation");
return;
} catch (IOException e) {
} catch (Exception e) {
throw new IllegalStateException("Failed entitlement protection self-test", e);
}
throw new IllegalStateException("Entitlement protection self-test was incorrectly permitted");
}

/**
* Originally {@code Security.selfTest}.
*/
@SuppressForbidden(reason = "accesses jvm default tempdir as a self-test")
private static void ensureCanCreateTempFile() {
private static void ensureCanCreateTempFile(CheckedSupplier<Path, ?> createTempFile) {
try {
Path p = Files.createTempFile(null, null);
Path p = createTempFile.get();
p.toFile().deleteOnExit();

// Make an effort to clean up the file immediately; also, deleteOnExit leaves the file if the JVM exits abnormally.
Expand All @@ -184,5 +189,24 @@ private static void ensureCanCreateTempFile() {
logger.debug("Success: Entitlement protection correctly permitted temp file creation");
}

@SuppressForbidden(reason = "accesses jvm default tempdir as a self-test")
private static Path createTempFile() throws Exception {
return Files.createTempFile(null, null);
}

private static void reflectiveStartProcess(ProcessBuilder pb) throws Exception {
try {
var start = ProcessBuilder.class.getMethod("start");
start.invoke(pb);
} catch (InvocationTargetException e) {
throw (Exception) e.getCause();
}
}

private static Path reflectiveCreateTempFile() throws Exception {
return (Path) Files.class.getMethod("createTempFile", String.class, String.class, FileAttribute[].class)
.invoke(null, null, null, new FileAttribute<?>[0]);
}

private static final Logger logger = LogManager.getLogger(EntitlementBootstrap.class);
}