Merge main into multi-project

This commit is contained in:
Tim Vernum 2025-01-08 13:02:38 +11:00
commit 60010f991e
44 changed files with 1264 additions and 332 deletions

View file

@ -66,7 +66,7 @@ public class InstrumentationServiceImpl implements InstrumentationService {
private static final Type CLASS_TYPE = Type.getType(Class.class);
static MethodKey parseCheckerMethodSignature(String checkerMethodName, Type[] checkerMethodArgumentTypes) {
static ParsedCheckerMethod parseCheckerMethodName(String checkerMethodName) {
boolean targetMethodIsStatic;
int classNameEndIndex = checkerMethodName.lastIndexOf("$$");
int methodNameStartIndex;
@ -100,9 +100,14 @@ public class InstrumentationServiceImpl implements InstrumentationService {
if (targetClassName.isBlank()) {
throw new IllegalArgumentException(String.format(Locale.ROOT, "Checker method %s has no class name", checkerMethodName));
}
return new ParsedCheckerMethod(targetClassName, targetMethodName, targetMethodIsStatic, targetMethodIsCtor);
}
static MethodKey parseCheckerMethodSignature(String checkerMethodName, Type[] checkerMethodArgumentTypes) {
ParsedCheckerMethod checkerMethod = parseCheckerMethodName(checkerMethodName);
final List<String> targetParameterTypes;
if (targetMethodIsStatic || targetMethodIsCtor) {
if (checkerMethod.targetMethodIsStatic() || checkerMethod.targetMethodIsCtor()) {
if (checkerMethodArgumentTypes.length < 1 || CLASS_TYPE.equals(checkerMethodArgumentTypes[0]) == false) {
throw new IllegalArgumentException(
String.format(
@ -130,7 +135,13 @@ public class InstrumentationServiceImpl implements InstrumentationService {
}
targetParameterTypes = Arrays.stream(checkerMethodArgumentTypes).skip(2).map(Type::getInternalName).toList();
}
boolean hasReceiver = (targetMethodIsStatic || targetMethodIsCtor) == false;
return new MethodKey(targetClassName, targetMethodName, targetParameterTypes);
return new MethodKey(checkerMethod.targetClassName(), checkerMethod.targetMethodName(), targetParameterTypes);
}
private record ParsedCheckerMethod(
String targetClassName,
String targetMethodName,
boolean targetMethodIsStatic,
boolean targetMethodIsCtor
) {}
}

View file

@ -9,6 +9,13 @@
package org.elasticsearch.entitlement.bridge;
import java.io.InputStream;
import java.io.PrintStream;
import java.io.PrintWriter;
import java.net.ContentHandlerFactory;
import java.net.DatagramSocketImplFactory;
import java.net.FileNameMap;
import java.net.SocketImplFactory;
import java.net.URL;
import java.net.URLStreamHandlerFactory;
import java.util.List;
@ -21,26 +28,42 @@ import javax.net.ssl.SSLSocketFactory;
@SuppressWarnings("unused") // Called from instrumentation code inserted by the Entitlements agent
public interface EntitlementChecker {
////////////////////
//
// Exit the JVM process
//
void check$java_lang_Runtime$exit(Class<?> callerClass, Runtime runtime, int status);
void check$java_lang_Runtime$halt(Class<?> callerClass, Runtime runtime, int status);
////////////////////
//
// ClassLoader ctor
//
void check$java_lang_ClassLoader$(Class<?> callerClass);
void check$java_lang_ClassLoader$(Class<?> callerClass, ClassLoader parent);
void check$java_lang_ClassLoader$(Class<?> callerClass, String name, ClassLoader parent);
////////////////////
//
// SecureClassLoader ctor
//
void check$java_security_SecureClassLoader$(Class<?> callerClass);
void check$java_security_SecureClassLoader$(Class<?> callerClass, ClassLoader parent);
void check$java_security_SecureClassLoader$(Class<?> callerClass, String name, ClassLoader parent);
////////////////////
//
// URLClassLoader constructors
//
void check$java_net_URLClassLoader$(Class<?> callerClass, URL[] urls);
void check$java_net_URLClassLoader$(Class<?> callerClass, URL[] urls, ClassLoader parent);
@ -51,7 +74,11 @@ public interface EntitlementChecker {
void check$java_net_URLClassLoader$(Class<?> callerClass, String name, URL[] urls, ClassLoader parent, URLStreamHandlerFactory factory);
////////////////////
//
// "setFactory" methods
//
void check$javax_net_ssl_HttpsURLConnection$setSSLSocketFactory(Class<?> callerClass, HttpsURLConnection conn, SSLSocketFactory sf);
void check$javax_net_ssl_HttpsURLConnection$$setDefaultSSLSocketFactory(Class<?> callerClass, SSLSocketFactory sf);
@ -60,9 +87,82 @@ public interface EntitlementChecker {
void check$javax_net_ssl_SSLContext$$setDefault(Class<?> callerClass, SSLContext context);
////////////////////
//
// Process creation
//
void check$java_lang_ProcessBuilder$start(Class<?> callerClass, ProcessBuilder that);
void check$java_lang_ProcessBuilder$$startPipeline(Class<?> callerClass, List<ProcessBuilder> builders);
////////////////////
//
// JVM-wide state changes
//
void check$java_lang_System$$setIn(Class<?> callerClass, InputStream in);
void check$java_lang_System$$setOut(Class<?> callerClass, PrintStream out);
void check$java_lang_System$$setErr(Class<?> callerClass, PrintStream err);
void check$java_lang_Runtime$addShutdownHook(Class<?> callerClass, Runtime runtime, Thread hook);
void check$java_lang_Runtime$removeShutdownHook(Class<?> callerClass, Runtime runtime, Thread hook);
void check$jdk_tools_jlink_internal_Jlink$(Class<?> callerClass);
void check$jdk_tools_jlink_internal_Main$$run(Class<?> callerClass, PrintWriter out, PrintWriter err, String... args);
void check$jdk_vm_ci_services_JVMCIServiceLocator$$getProviders(Class<?> callerClass, Class<?> service);
void check$jdk_vm_ci_services_Services$$load(Class<?> callerClass, Class<?> service);
void check$jdk_vm_ci_services_Services$$loadSingle(Class<?> callerClass, Class<?> service, boolean required);
void check$com_sun_tools_jdi_VirtualMachineManagerImpl$$virtualMachineManager(Class<?> callerClass);
void check$java_lang_Thread$$setDefaultUncaughtExceptionHandler(Class<?> callerClass, Thread.UncaughtExceptionHandler ueh);
void check$java_util_spi_LocaleServiceProvider$(Class<?> callerClass);
void check$java_text_spi_BreakIteratorProvider$(Class<?> callerClass);
void check$java_text_spi_CollatorProvider$(Class<?> callerClass);
void check$java_text_spi_DateFormatProvider$(Class<?> callerClass);
void check$java_text_spi_DateFormatSymbolsProvider$(Class<?> callerClass);
void check$java_text_spi_DecimalFormatSymbolsProvider$(Class<?> callerClass);
void check$java_text_spi_NumberFormatProvider$(Class<?> callerClass);
void check$java_util_spi_CalendarDataProvider$(Class<?> callerClass);
void check$java_util_spi_CalendarNameProvider$(Class<?> callerClass);
void check$java_util_spi_CurrencyNameProvider$(Class<?> callerClass);
void check$java_util_spi_LocaleNameProvider$(Class<?> callerClass);
void check$java_util_spi_TimeZoneNameProvider$(Class<?> callerClass);
void check$java_util_logging_LogManager$(Class<?> callerClass);
void check$java_net_DatagramSocket$$setDatagramSocketImplFactory(Class<?> callerClass, DatagramSocketImplFactory fac);
void check$java_net_HttpURLConnection$$setFollowRedirects(Class<?> callerClass, boolean set);
void check$java_net_ServerSocket$$setSocketFactory(Class<?> callerClass, SocketImplFactory fac);
void check$java_net_Socket$$setSocketImplFactory(Class<?> callerClass, SocketImplFactory fac);
void check$java_net_URL$$setURLStreamHandlerFactory(Class<?> callerClass, URLStreamHandlerFactory fac);
void check$java_net_URLConnection$$setFileNameMap(Class<?> callerClass, FileNameMap map);
void check$java_net_URLConnection$$setContentHandlerFactory(Class<?> callerClass, ContentHandlerFactory fac);
}

View file

@ -12,5 +12,8 @@ module org.elasticsearch.entitlement.qa.common {
requires org.elasticsearch.base;
requires org.elasticsearch.logging;
// Modules we'll attempt to use in order to exercise entitlements
requires java.logging;
exports org.elasticsearch.entitlement.qa.common;
}

View file

@ -0,0 +1,334 @@
/*
* 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.entitlement.qa.common;
import java.net.InetAddress;
import java.net.Socket;
import java.security.cert.Certificate;
import java.text.BreakIterator;
import java.text.Collator;
import java.text.DateFormat;
import java.text.DateFormatSymbols;
import java.text.DecimalFormatSymbols;
import java.text.NumberFormat;
import java.text.spi.BreakIteratorProvider;
import java.text.spi.CollatorProvider;
import java.text.spi.DateFormatProvider;
import java.text.spi.DateFormatSymbolsProvider;
import java.text.spi.DecimalFormatSymbolsProvider;
import java.text.spi.NumberFormatProvider;
import java.util.Locale;
import java.util.Map;
import java.util.spi.CalendarDataProvider;
import java.util.spi.CalendarNameProvider;
import java.util.spi.CurrencyNameProvider;
import java.util.spi.LocaleNameProvider;
import java.util.spi.LocaleServiceProvider;
import java.util.spi.TimeZoneNameProvider;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLSocketFactory;
/**
* A collection of concrete subclasses that we can instantiate but that don't actually work.
* <p>
* A bit like Mockito but way more painful.
*/
class DummyImplementations {
static class DummyLocaleServiceProvider extends LocaleServiceProvider {
@Override
public Locale[] getAvailableLocales() {
throw unexpected();
}
}
static class DummyBreakIteratorProvider extends BreakIteratorProvider {
@Override
public BreakIterator getWordInstance(Locale locale) {
throw unexpected();
}
@Override
public BreakIterator getLineInstance(Locale locale) {
throw unexpected();
}
@Override
public BreakIterator getCharacterInstance(Locale locale) {
throw unexpected();
}
@Override
public BreakIterator getSentenceInstance(Locale locale) {
throw unexpected();
}
@Override
public Locale[] getAvailableLocales() {
throw unexpected();
}
}
static class DummyCollatorProvider extends CollatorProvider {
@Override
public Collator getInstance(Locale locale) {
throw unexpected();
}
@Override
public Locale[] getAvailableLocales() {
throw unexpected();
}
}
static class DummyDateFormatProvider extends DateFormatProvider {
@Override
public DateFormat getTimeInstance(int style, Locale locale) {
throw unexpected();
}
@Override
public DateFormat getDateInstance(int style, Locale locale) {
throw unexpected();
}
@Override
public DateFormat getDateTimeInstance(int dateStyle, int timeStyle, Locale locale) {
throw unexpected();
}
@Override
public Locale[] getAvailableLocales() {
throw unexpected();
}
}
static class DummyDateFormatSymbolsProvider extends DateFormatSymbolsProvider {
@Override
public DateFormatSymbols getInstance(Locale locale) {
throw unexpected();
}
@Override
public Locale[] getAvailableLocales() {
throw unexpected();
}
}
static class DummyDecimalFormatSymbolsProvider extends DecimalFormatSymbolsProvider {
@Override
public DecimalFormatSymbols getInstance(Locale locale) {
throw unexpected();
}
@Override
public Locale[] getAvailableLocales() {
throw unexpected();
}
}
static class DummyNumberFormatProvider extends NumberFormatProvider {
@Override
public NumberFormat getCurrencyInstance(Locale locale) {
throw unexpected();
}
@Override
public NumberFormat getIntegerInstance(Locale locale) {
throw unexpected();
}
@Override
public NumberFormat getNumberInstance(Locale locale) {
throw unexpected();
}
@Override
public NumberFormat getPercentInstance(Locale locale) {
throw unexpected();
}
@Override
public Locale[] getAvailableLocales() {
throw unexpected();
}
}
static class DummyCalendarDataProvider extends CalendarDataProvider {
@Override
public int getFirstDayOfWeek(Locale locale) {
throw unexpected();
}
@Override
public int getMinimalDaysInFirstWeek(Locale locale) {
throw unexpected();
}
@Override
public Locale[] getAvailableLocales() {
throw unexpected();
}
}
static class DummyCalendarNameProvider extends CalendarNameProvider {
@Override
public String getDisplayName(String calendarType, int field, int value, int style, Locale locale) {
throw unexpected();
}
@Override
public Map<String, Integer> getDisplayNames(String calendarType, int field, int style, Locale locale) {
throw unexpected();
}
@Override
public Locale[] getAvailableLocales() {
throw unexpected();
}
}
static class DummyCurrencyNameProvider extends CurrencyNameProvider {
@Override
public String getSymbol(String currencyCode, Locale locale) {
throw unexpected();
}
@Override
public Locale[] getAvailableLocales() {
throw unexpected();
}
}
static class DummyLocaleNameProvider extends LocaleNameProvider {
@Override
public String getDisplayLanguage(String languageCode, Locale locale) {
throw unexpected();
}
@Override
public String getDisplayCountry(String countryCode, Locale locale) {
throw unexpected();
}
@Override
public String getDisplayVariant(String variant, Locale locale) {
throw unexpected();
}
@Override
public Locale[] getAvailableLocales() {
throw unexpected();
}
}
static class DummyTimeZoneNameProvider extends TimeZoneNameProvider {
@Override
public String getDisplayName(String ID, boolean daylight, int style, Locale locale) {
throw unexpected();
}
@Override
public Locale[] getAvailableLocales() {
throw unexpected();
}
}
static class DummyHttpsURLConnection extends HttpsURLConnection {
DummyHttpsURLConnection() {
super(null);
}
@Override
public void connect() {
throw unexpected();
}
@Override
public void disconnect() {
throw unexpected();
}
@Override
public boolean usingProxy() {
throw unexpected();
}
@Override
public String getCipherSuite() {
throw unexpected();
}
@Override
public Certificate[] getLocalCertificates() {
throw unexpected();
}
@Override
public Certificate[] getServerCertificates() {
throw unexpected();
}
}
static class DummySSLSocketFactory extends SSLSocketFactory {
@Override
public Socket createSocket(String host, int port) {
throw unexpected();
}
@Override
public Socket createSocket(String host, int port, InetAddress localHost, int localPort) {
throw unexpected();
}
@Override
public Socket createSocket(InetAddress host, int port) {
throw unexpected();
}
@Override
public Socket createSocket(InetAddress address, int port, InetAddress localAddress, int localPort) {
throw unexpected();
}
@Override
public String[] getDefaultCipherSuites() {
throw unexpected();
}
@Override
public String[] getSupportedCipherSuites() {
throw unexpected();
}
@Override
public Socket createSocket(Socket s, String host, int port, boolean autoClose) {
throw unexpected();
}
}
private static RuntimeException unexpected() {
return new IllegalStateException("This method isn't supposed to be called");
}
}

View file

@ -12,6 +12,18 @@ package org.elasticsearch.entitlement.qa.common;
import org.elasticsearch.client.internal.node.NodeClient;
import org.elasticsearch.common.Strings;
import org.elasticsearch.core.SuppressForbidden;
import org.elasticsearch.entitlement.qa.common.DummyImplementations.DummyBreakIteratorProvider;
import org.elasticsearch.entitlement.qa.common.DummyImplementations.DummyCalendarDataProvider;
import org.elasticsearch.entitlement.qa.common.DummyImplementations.DummyCalendarNameProvider;
import org.elasticsearch.entitlement.qa.common.DummyImplementations.DummyCollatorProvider;
import org.elasticsearch.entitlement.qa.common.DummyImplementations.DummyCurrencyNameProvider;
import org.elasticsearch.entitlement.qa.common.DummyImplementations.DummyDateFormatProvider;
import org.elasticsearch.entitlement.qa.common.DummyImplementations.DummyDateFormatSymbolsProvider;
import org.elasticsearch.entitlement.qa.common.DummyImplementations.DummyDecimalFormatSymbolsProvider;
import org.elasticsearch.entitlement.qa.common.DummyImplementations.DummyLocaleNameProvider;
import org.elasticsearch.entitlement.qa.common.DummyImplementations.DummyLocaleServiceProvider;
import org.elasticsearch.entitlement.qa.common.DummyImplementations.DummyNumberFormatProvider;
import org.elasticsearch.entitlement.qa.common.DummyImplementations.DummyTimeZoneNameProvider;
import org.elasticsearch.logging.LogManager;
import org.elasticsearch.logging.Logger;
import org.elasticsearch.rest.BaseRestHandler;
@ -21,8 +33,15 @@ import org.elasticsearch.rest.RestStatus;
import java.io.IOException;
import java.io.UncheckedIOException;
import java.net.DatagramSocket;
import java.net.DatagramSocketImpl;
import java.net.DatagramSocketImplFactory;
import java.net.HttpURLConnection;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.URL;
import java.net.URLClassLoader;
import java.net.URLConnection;
import java.security.NoSuchAlgorithmException;
import java.util.List;
import java.util.Map;
@ -40,6 +59,7 @@ import static org.elasticsearch.rest.RestRequest.Method.GET;
public class RestEntitlementsCheckAction extends BaseRestHandler {
private static final Logger logger = LogManager.getLogger(RestEntitlementsCheckAction.class);
public static final Thread NO_OP_SHUTDOWN_HOOK = new Thread(() -> {}, "Shutdown hook for testing");
private final String prefix;
record CheckAction(Runnable action, boolean isAlwaysDeniedToPlugins) {
@ -69,11 +89,45 @@ public class RestEntitlementsCheckAction extends BaseRestHandler {
entry("set_https_connection_properties", forPlugins(RestEntitlementsCheckAction::setHttpsConnectionProperties)),
entry("set_default_ssl_socket_factory", alwaysDenied(RestEntitlementsCheckAction::setDefaultSSLSocketFactory)),
entry("set_default_hostname_verifier", alwaysDenied(RestEntitlementsCheckAction::setDefaultHostnameVerifier)),
entry("set_default_ssl_context", alwaysDenied(RestEntitlementsCheckAction::setDefaultSSLContext))
entry("set_default_ssl_context", alwaysDenied(RestEntitlementsCheckAction::setDefaultSSLContext)),
entry("system_setIn", alwaysDenied(RestEntitlementsCheckAction::system$$setIn)),
entry("system_setOut", alwaysDenied(RestEntitlementsCheckAction::system$$setOut)),
entry("system_setErr", alwaysDenied(RestEntitlementsCheckAction::system$$setErr)),
entry("runtime_addShutdownHook", alwaysDenied(RestEntitlementsCheckAction::runtime$addShutdownHook)),
entry("runtime_removeShutdownHook", alwaysDenied(RestEntitlementsCheckAction::runtime$$removeShutdownHook)),
entry(
"thread_setDefaultUncaughtExceptionHandler",
alwaysDenied(RestEntitlementsCheckAction::thread$$setDefaultUncaughtExceptionHandler)
),
entry("localeServiceProvider", alwaysDenied(RestEntitlementsCheckAction::localeServiceProvider$)),
entry("breakIteratorProvider", alwaysDenied(RestEntitlementsCheckAction::breakIteratorProvider$)),
entry("collatorProvider", alwaysDenied(RestEntitlementsCheckAction::collatorProvider$)),
entry("dateFormatProvider", alwaysDenied(RestEntitlementsCheckAction::dateFormatProvider$)),
entry("dateFormatSymbolsProvider", alwaysDenied(RestEntitlementsCheckAction::dateFormatSymbolsProvider$)),
entry("decimalFormatSymbolsProvider", alwaysDenied(RestEntitlementsCheckAction::decimalFormatSymbolsProvider$)),
entry("numberFormatProvider", alwaysDenied(RestEntitlementsCheckAction::numberFormatProvider$)),
entry("calendarDataProvider", alwaysDenied(RestEntitlementsCheckAction::calendarDataProvider$)),
entry("calendarNameProvider", alwaysDenied(RestEntitlementsCheckAction::calendarNameProvider$)),
entry("currencyNameProvider", alwaysDenied(RestEntitlementsCheckAction::currencyNameProvider$)),
entry("localeNameProvider", alwaysDenied(RestEntitlementsCheckAction::localeNameProvider$)),
entry("timeZoneNameProvider", alwaysDenied(RestEntitlementsCheckAction::timeZoneNameProvider$)),
entry("logManager", alwaysDenied(RestEntitlementsCheckAction::logManager$)),
// This group is a bit nasty: if entitlements don't prevent these, then networking is
// irreparably borked for the remainder of the test run.
entry(
"datagramSocket_setDatagramSocketImplFactory",
alwaysDenied(RestEntitlementsCheckAction::datagramSocket$$setDatagramSocketImplFactory)
),
entry("httpURLConnection_setFollowRedirects", alwaysDenied(RestEntitlementsCheckAction::httpURLConnection$$setFollowRedirects)),
entry("serverSocket_setSocketFactory", alwaysDenied(RestEntitlementsCheckAction::serverSocket$$setSocketFactory)),
entry("socket_setSocketImplFactory", alwaysDenied(RestEntitlementsCheckAction::socket$$setSocketImplFactory)),
entry("url_setURLStreamHandlerFactory", alwaysDenied(RestEntitlementsCheckAction::url$$setURLStreamHandlerFactory)),
entry("urlConnection_setFileNameMap", alwaysDenied(RestEntitlementsCheckAction::urlConnection$$setFileNameMap)),
entry("urlConnection_setContentHandlerFactory", alwaysDenied(RestEntitlementsCheckAction::urlConnection$$setContentHandlerFactory))
);
private static void setDefaultSSLContext() {
logger.info("Calling SSLContext.setDefault");
try {
SSLContext.setDefault(SSLContext.getDefault());
} catch (NoSuchAlgorithmException e) {
@ -82,13 +136,11 @@ public class RestEntitlementsCheckAction extends BaseRestHandler {
}
private static void setDefaultHostnameVerifier() {
logger.info("Calling HttpsURLConnection.setDefaultHostnameVerifier");
HttpsURLConnection.setDefaultHostnameVerifier((hostname, session) -> false);
}
private static void setDefaultSSLSocketFactory() {
logger.info("Calling HttpsURLConnection.setDefaultSSLSocketFactory");
HttpsURLConnection.setDefaultSSLSocketFactory(new TestSSLSocketFactory());
HttpsURLConnection.setDefaultSSLSocketFactory(new DummyImplementations.DummySSLSocketFactory());
}
@SuppressForbidden(reason = "Specifically testing Runtime.exit")
@ -126,9 +178,137 @@ public class RestEntitlementsCheckAction extends BaseRestHandler {
}
private static void setHttpsConnectionProperties() {
logger.info("Calling setSSLSocketFactory");
var connection = new TestHttpsURLConnection();
connection.setSSLSocketFactory(new TestSSLSocketFactory());
new DummyImplementations.DummyHttpsURLConnection().setSSLSocketFactory(new DummyImplementations.DummySSLSocketFactory());
}
private static void system$$setIn() {
System.setIn(System.in);
}
@SuppressForbidden(reason = "This should be a no-op so we don't interfere with system streams")
private static void system$$setOut() {
System.setOut(System.out);
}
@SuppressForbidden(reason = "This should be a no-op so we don't interfere with system streams")
private static void system$$setErr() {
System.setErr(System.err);
}
private static void runtime$addShutdownHook() {
Runtime.getRuntime().addShutdownHook(NO_OP_SHUTDOWN_HOOK);
}
private static void runtime$$removeShutdownHook() {
Runtime.getRuntime().removeShutdownHook(NO_OP_SHUTDOWN_HOOK);
}
private static void thread$$setDefaultUncaughtExceptionHandler() {
Thread.setDefaultUncaughtExceptionHandler(Thread.getDefaultUncaughtExceptionHandler());
}
private static void localeServiceProvider$() {
new DummyLocaleServiceProvider();
}
private static void breakIteratorProvider$() {
new DummyBreakIteratorProvider();
}
private static void collatorProvider$() {
new DummyCollatorProvider();
}
private static void dateFormatProvider$() {
new DummyDateFormatProvider();
}
private static void dateFormatSymbolsProvider$() {
new DummyDateFormatSymbolsProvider();
}
private static void decimalFormatSymbolsProvider$() {
new DummyDecimalFormatSymbolsProvider();
}
private static void numberFormatProvider$() {
new DummyNumberFormatProvider();
}
private static void calendarDataProvider$() {
new DummyCalendarDataProvider();
}
private static void calendarNameProvider$() {
new DummyCalendarNameProvider();
}
private static void currencyNameProvider$() {
new DummyCurrencyNameProvider();
}
private static void localeNameProvider$() {
new DummyLocaleNameProvider();
}
private static void timeZoneNameProvider$() {
new DummyTimeZoneNameProvider();
}
private static void logManager$() {
new java.util.logging.LogManager() {
};
}
@SuppressWarnings("deprecation")
@SuppressForbidden(reason = "We're required to prevent calls to this forbidden API")
private static void datagramSocket$$setDatagramSocketImplFactory() {
try {
DatagramSocket.setDatagramSocketImplFactory(new DatagramSocketImplFactory() {
@Override
public DatagramSocketImpl createDatagramSocketImpl() {
throw new IllegalStateException();
}
});
} catch (IOException e) {
throw new IllegalStateException(e);
}
}
private static void httpURLConnection$$setFollowRedirects() {
HttpURLConnection.setFollowRedirects(HttpURLConnection.getFollowRedirects());
}
@SuppressWarnings("deprecation")
@SuppressForbidden(reason = "We're required to prevent calls to this forbidden API")
private static void serverSocket$$setSocketFactory() {
try {
ServerSocket.setSocketFactory(() -> { throw new IllegalStateException(); });
} catch (IOException e) {
throw new IllegalStateException(e);
}
}
@SuppressWarnings("deprecation")
@SuppressForbidden(reason = "We're required to prevent calls to this forbidden API")
private static void socket$$setSocketImplFactory() {
try {
Socket.setSocketImplFactory(() -> { throw new IllegalStateException(); });
} catch (IOException e) {
throw new IllegalStateException(e);
}
}
private static void url$$setURLStreamHandlerFactory() {
URL.setURLStreamHandlerFactory(__ -> { throw new IllegalStateException(); });
}
private static void urlConnection$$setFileNameMap() {
URLConnection.setFileNameMap(__ -> { throw new IllegalStateException(); });
}
private static void urlConnection$$setContentHandlerFactory() {
URLConnection.setContentHandlerFactory(__ -> { throw new IllegalStateException(); });
}
public RestEntitlementsCheckAction(String prefix) {

View file

@ -1,48 +0,0 @@
/*
* 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.entitlement.qa.common;
import java.io.IOException;
import java.security.cert.Certificate;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLPeerUnverifiedException;
class TestHttpsURLConnection extends HttpsURLConnection {
TestHttpsURLConnection() {
super(null);
}
@Override
public void connect() throws IOException {}
@Override
public void disconnect() {}
@Override
public boolean usingProxy() {
return false;
}
@Override
public String getCipherSuite() {
return "";
}
@Override
public Certificate[] getLocalCertificates() {
return new Certificate[0];
}
@Override
public Certificate[] getServerCertificates() throws SSLPeerUnverifiedException {
return new Certificate[0];
}
}

View file

@ -1,54 +0,0 @@
/*
* 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.entitlement.qa.common;
import java.io.IOException;
import java.net.InetAddress;
import java.net.Socket;
import java.net.UnknownHostException;
import javax.net.ssl.SSLSocketFactory;
class TestSSLSocketFactory extends SSLSocketFactory {
@Override
public Socket createSocket(String host, int port) throws IOException, UnknownHostException {
return null;
}
@Override
public Socket createSocket(String host, int port, InetAddress localHost, int localPort) {
return null;
}
@Override
public Socket createSocket(InetAddress host, int port) throws IOException {
return null;
}
@Override
public Socket createSocket(InetAddress address, int port, InetAddress localAddress, int localPort) throws IOException {
return null;
}
@Override
public String[] getDefaultCipherSuites() {
return new String[0];
}
@Override
public String[] getSupportedCipherSuites() {
return new String[0];
}
@Override
public Socket createSocket(Socket s, String host, int port, boolean autoClose) throws IOException {
return null;
}
}

View file

@ -32,7 +32,7 @@ public class EntitlementsDeniedIT extends ESRestTestCase {
.systemProperty("es.entitlements.enabled", "true")
.setting("xpack.security.enabled", "false")
// Logs in libs/entitlement/qa/build/test-results/javaRestTest/TEST-org.elasticsearch.entitlement.qa.EntitlementsDeniedIT.xml
.setting("logger.org.elasticsearch.entitlement", "TRACE")
// .setting("logger.org.elasticsearch.entitlement", "DEBUG")
.build();
@Override

View file

@ -12,6 +12,13 @@ package org.elasticsearch.entitlement.runtime.api;
import org.elasticsearch.entitlement.bridge.EntitlementChecker;
import org.elasticsearch.entitlement.runtime.policy.PolicyManager;
import java.io.InputStream;
import java.io.PrintStream;
import java.io.PrintWriter;
import java.net.ContentHandlerFactory;
import java.net.DatagramSocketImplFactory;
import java.net.FileNameMap;
import java.net.SocketImplFactory;
import java.net.URL;
import java.net.URLStreamHandlerFactory;
import java.util.List;
@ -115,6 +122,166 @@ public class ElasticsearchEntitlementChecker implements EntitlementChecker {
policyManager.checkStartProcess(callerClass);
}
@Override
public void check$java_lang_System$$setIn(Class<?> callerClass, InputStream in) {
policyManager.checkChangeJVMGlobalState(callerClass);
}
@Override
public void check$java_lang_System$$setOut(Class<?> callerClass, PrintStream out) {
policyManager.checkChangeJVMGlobalState(callerClass);
}
@Override
public void check$java_lang_System$$setErr(Class<?> callerClass, PrintStream err) {
policyManager.checkChangeJVMGlobalState(callerClass);
}
@Override
public void check$java_lang_Runtime$addShutdownHook(Class<?> callerClass, Runtime runtime, Thread hook) {
policyManager.checkChangeJVMGlobalState(callerClass);
}
@Override
public void check$java_lang_Runtime$removeShutdownHook(Class<?> callerClass, Runtime runtime, Thread hook) {
policyManager.checkChangeJVMGlobalState(callerClass);
}
@Override
public void check$jdk_tools_jlink_internal_Jlink$(Class<?> callerClass) {
policyManager.checkChangeJVMGlobalState(callerClass);
}
@Override
public void check$jdk_tools_jlink_internal_Main$$run(Class<?> callerClass, PrintWriter out, PrintWriter err, String... args) {
policyManager.checkChangeJVMGlobalState(callerClass);
}
@Override
public void check$jdk_vm_ci_services_JVMCIServiceLocator$$getProviders(Class<?> callerClass, Class<?> service) {
policyManager.checkChangeJVMGlobalState(callerClass);
}
@Override
public void check$jdk_vm_ci_services_Services$$load(Class<?> callerClass, Class<?> service) {
policyManager.checkChangeJVMGlobalState(callerClass);
}
@Override
public void check$jdk_vm_ci_services_Services$$loadSingle(Class<?> callerClass, Class<?> service, boolean required) {
policyManager.checkChangeJVMGlobalState(callerClass);
}
@Override
public void check$com_sun_tools_jdi_VirtualMachineManagerImpl$$virtualMachineManager(Class<?> callerClass) {
policyManager.checkChangeJVMGlobalState(callerClass);
}
@Override
public void check$java_lang_Thread$$setDefaultUncaughtExceptionHandler(Class<?> callerClass, Thread.UncaughtExceptionHandler ueh) {
policyManager.checkChangeJVMGlobalState(callerClass);
}
@Override
public void check$java_util_spi_LocaleServiceProvider$(Class<?> callerClass) {
policyManager.checkChangeJVMGlobalState(callerClass);
}
@Override
public void check$java_text_spi_BreakIteratorProvider$(Class<?> callerClass) {
policyManager.checkChangeJVMGlobalState(callerClass);
}
@Override
public void check$java_text_spi_CollatorProvider$(Class<?> callerClass) {
policyManager.checkChangeJVMGlobalState(callerClass);
}
@Override
public void check$java_text_spi_DateFormatProvider$(Class<?> callerClass) {
policyManager.checkChangeJVMGlobalState(callerClass);
}
@Override
public void check$java_text_spi_DateFormatSymbolsProvider$(Class<?> callerClass) {
policyManager.checkChangeJVMGlobalState(callerClass);
}
@Override
public void check$java_text_spi_DecimalFormatSymbolsProvider$(Class<?> callerClass) {
policyManager.checkChangeJVMGlobalState(callerClass);
}
@Override
public void check$java_text_spi_NumberFormatProvider$(Class<?> callerClass) {
policyManager.checkChangeJVMGlobalState(callerClass);
}
@Override
public void check$java_util_spi_CalendarDataProvider$(Class<?> callerClass) {
policyManager.checkChangeJVMGlobalState(callerClass);
}
@Override
public void check$java_util_spi_CalendarNameProvider$(Class<?> callerClass) {
policyManager.checkChangeJVMGlobalState(callerClass);
}
@Override
public void check$java_util_spi_CurrencyNameProvider$(Class<?> callerClass) {
policyManager.checkChangeJVMGlobalState(callerClass);
}
@Override
public void check$java_util_spi_LocaleNameProvider$(Class<?> callerClass) {
policyManager.checkChangeJVMGlobalState(callerClass);
}
@Override
public void check$java_util_spi_TimeZoneNameProvider$(Class<?> callerClass) {
policyManager.checkChangeJVMGlobalState(callerClass);
}
@Override
public void check$java_util_logging_LogManager$(Class<?> callerClass) {
policyManager.checkChangeJVMGlobalState(callerClass);
}
@Override
public void check$java_net_DatagramSocket$$setDatagramSocketImplFactory(Class<?> callerClass, DatagramSocketImplFactory fac) {
policyManager.checkChangeJVMGlobalState(callerClass);
}
@Override
public void check$java_net_HttpURLConnection$$setFollowRedirects(Class<?> callerClass, boolean set) {
policyManager.checkChangeJVMGlobalState(callerClass);
}
@Override
public void check$java_net_ServerSocket$$setSocketFactory(Class<?> callerClass, SocketImplFactory fac) {
policyManager.checkChangeJVMGlobalState(callerClass);
}
@Override
public void check$java_net_Socket$$setSocketImplFactory(Class<?> callerClass, SocketImplFactory fac) {
policyManager.checkChangeJVMGlobalState(callerClass);
}
@Override
public void check$java_net_URL$$setURLStreamHandlerFactory(Class<?> callerClass, URLStreamHandlerFactory fac) {
policyManager.checkChangeJVMGlobalState(callerClass);
}
@Override
public void check$java_net_URLConnection$$setFileNameMap(Class<?> callerClass, FileNameMap map) {
policyManager.checkChangeJVMGlobalState(callerClass);
}
@Override
public void check$java_net_URLConnection$$setContentHandlerFactory(Class<?> callerClass, ContentHandlerFactory fac) {
policyManager.checkChangeJVMGlobalState(callerClass);
}
@Override
public void check$javax_net_ssl_HttpsURLConnection$setSSLSocketFactory(
Class<?> callerClass,
@ -126,16 +293,16 @@ public class ElasticsearchEntitlementChecker implements EntitlementChecker {
@Override
public void check$javax_net_ssl_HttpsURLConnection$$setDefaultSSLSocketFactory(Class<?> callerClass, SSLSocketFactory sf) {
policyManager.checkSetGlobalHttpsConnectionProperties(callerClass);
policyManager.checkChangeJVMGlobalState(callerClass);
}
@Override
public void check$javax_net_ssl_HttpsURLConnection$$setDefaultHostnameVerifier(Class<?> callerClass, HostnameVerifier hv) {
policyManager.checkSetGlobalHttpsConnectionProperties(callerClass);
policyManager.checkChangeJVMGlobalState(callerClass);
}
@Override
public void check$javax_net_ssl_SSLContext$$setDefault(Class<?> callerClass, SSLContext context) {
policyManager.checkSetGlobalHttpsConnectionProperties(callerClass);
policyManager.checkChangeJVMGlobalState(callerClass);
}
}

View file

@ -23,12 +23,15 @@ import java.util.Optional;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import static java.lang.StackWalker.Option.RETAIN_CLASS_REFERENCE;
import static java.util.Objects.requireNonNull;
import static java.util.function.Predicate.not;
import static java.util.stream.Collectors.groupingBy;
import static java.util.stream.Collectors.toUnmodifiableMap;
public class PolicyManager {
private static final Logger logger = LogManager.getLogger(PolicyManager.class);
@ -93,13 +96,13 @@ public class PolicyManager {
this.agentEntitlements = agentEntitlements;
this.pluginsEntitlements = requireNonNull(pluginPolicies).entrySet()
.stream()
.collect(Collectors.toUnmodifiableMap(Map.Entry::getKey, e -> buildScopeEntitlementsMap(e.getValue())));
.collect(toUnmodifiableMap(Map.Entry::getKey, e -> buildScopeEntitlementsMap(e.getValue())));
this.pluginResolver = pluginResolver;
this.entitlementsModule = entitlementsModule;
}
private static Map<String, List<Entitlement>> buildScopeEntitlementsMap(Policy policy) {
return policy.scopes().stream().collect(Collectors.toUnmodifiableMap(scope -> scope.moduleName(), scope -> scope.entitlements()));
return policy.scopes().stream().collect(toUnmodifiableMap(Scope::moduleName, Scope::entitlements));
}
public void checkStartProcess(Class<?> callerClass) {
@ -122,6 +125,26 @@ public class PolicyManager {
);
}
/**
* @param operationDescription is only called when the operation is not trivially allowed, meaning the check is about to fail;
* therefore, its performance is not a major concern.
*/
private void neverEntitled(Class<?> callerClass, Supplier<String> operationDescription) {
var requestingModule = requestingClass(callerClass);
if (isTriviallyAllowed(requestingModule)) {
return;
}
throw new NotEntitledException(
Strings.format(
"Not entitled: caller [%s], module [%s], operation [%s]",
callerClass,
requestingModule.getName(),
operationDescription.get()
)
);
}
public void checkExitVM(Class<?> callerClass) {
checkEntitlementPresent(callerClass, ExitVMEntitlement.class);
}
@ -134,8 +157,23 @@ public class PolicyManager {
checkEntitlementPresent(callerClass, SetHttpsConnectionPropertiesEntitlement.class);
}
public void checkSetGlobalHttpsConnectionProperties(Class<?> callerClass) {
neverEntitled(callerClass, "set global https connection properties");
public void checkChangeJVMGlobalState(Class<?> callerClass) {
neverEntitled(callerClass, () -> {
// Look up the check$ method to compose an informative error message.
// This way, we don't need to painstakingly describe every individual global-state change.
Optional<String> checkMethodName = StackWalker.getInstance()
.walk(
frames -> frames.map(StackFrame::getMethodName)
.dropWhile(not(methodName -> methodName.startsWith("check$")))
.findFirst()
);
return checkMethodName.map(this::operationDescription).orElse("change JVM global state");
});
}
private String operationDescription(String methodName) {
// TODO: Use a more human-readable description. Perhaps share code with InstrumentationServiceImpl.parseCheckerMethodName
return methodName.substring(methodName.indexOf('$'));
}
private void checkEntitlementPresent(Class<?> callerClass, Class<? extends Entitlement> entitlementClass) {