diff --git a/libs/entitlement/bridge/src/main/java/org/elasticsearch/entitlement/bridge/EntitlementChecker.java b/libs/entitlement/bridge/src/main/java/org/elasticsearch/entitlement/bridge/EntitlementChecker.java index c40091f42586..4bd1c4c5d49c 100644 --- a/libs/entitlement/bridge/src/main/java/org/elasticsearch/entitlement/bridge/EntitlementChecker.java +++ b/libs/entitlement/bridge/src/main/java/org/elasticsearch/entitlement/bridge/EntitlementChecker.java @@ -1214,6 +1214,26 @@ public interface EntitlementChecker { WatchEvent.Modifier... modifiers ); + // URLConnection + + void check$sun_net_www_protocol_file_FileURLConnection$connect(Class callerClass, java.net.URLConnection that); + + void check$sun_net_www_protocol_file_FileURLConnection$getHeaderFields(Class callerClass, java.net.URLConnection that); + + void check$sun_net_www_protocol_file_FileURLConnection$getHeaderField(Class callerClass, java.net.URLConnection that, String name); + + void check$sun_net_www_protocol_file_FileURLConnection$getHeaderField(Class callerClass, java.net.URLConnection that, int n); + + void check$sun_net_www_protocol_file_FileURLConnection$getContentLength(Class callerClass, java.net.URLConnection that); + + void check$sun_net_www_protocol_file_FileURLConnection$getContentLengthLong(Class callerClass, java.net.URLConnection that); + + void check$sun_net_www_protocol_file_FileURLConnection$getHeaderFieldKey(Class callerClass, java.net.URLConnection that, int n); + + void check$sun_net_www_protocol_file_FileURLConnection$getLastModified(Class callerClass, java.net.URLConnection that); + + void check$sun_net_www_protocol_file_FileURLConnection$getInputStream(Class callerClass, java.net.URLConnection that); + //////////////////// // // Thread management diff --git a/libs/entitlement/qa/entitled-plugin/src/main/java/org/elasticsearch/entitlement/qa/entitled/EntitledActions.java b/libs/entitlement/qa/entitled-plugin/src/main/java/org/elasticsearch/entitlement/qa/entitled/EntitledActions.java index 531ac97c6504..a1f59ce2f600 100644 --- a/libs/entitlement/qa/entitled-plugin/src/main/java/org/elasticsearch/entitlement/qa/entitled/EntitledActions.java +++ b/libs/entitlement/qa/entitled-plugin/src/main/java/org/elasticsearch/entitlement/qa/entitled/EntitledActions.java @@ -71,4 +71,9 @@ public final class EntitledActions { public static URLConnection createFtpURLConnection() throws IOException { return URI.create("ftp://127.0.0.1:12345/").toURL().openConnection(); } + + public static URLConnection createFileURLConnection() throws IOException { + var fileUrl = createTempFileForWrite().toUri().toURL(); + return fileUrl.openConnection(); + } } diff --git a/libs/entitlement/qa/entitlement-test-plugin/src/main/java/org/elasticsearch/entitlement/qa/test/RestEntitlementsCheckAction.java b/libs/entitlement/qa/entitlement-test-plugin/src/main/java/org/elasticsearch/entitlement/qa/test/RestEntitlementsCheckAction.java index ecaa76d0b9c2..cbb5fb514ae5 100644 --- a/libs/entitlement/qa/entitlement-test-plugin/src/main/java/org/elasticsearch/entitlement/qa/test/RestEntitlementsCheckAction.java +++ b/libs/entitlement/qa/entitlement-test-plugin/src/main/java/org/elasticsearch/entitlement/qa/test/RestEntitlementsCheckAction.java @@ -194,6 +194,7 @@ public class RestEntitlementsCheckAction extends BaseRestHandler { getTestEntries(PathActions.class), getTestEntries(SpiActions.class), getTestEntries(SystemActions.class), + getTestEntries(URLConnectionFileActions.class), getTestEntries(URLConnectionNetworkActions.class) ) .flatMap(Function.identity()) diff --git a/libs/entitlement/qa/entitlement-test-plugin/src/main/java/org/elasticsearch/entitlement/qa/test/URLConnectionFileActions.java b/libs/entitlement/qa/entitlement-test-plugin/src/main/java/org/elasticsearch/entitlement/qa/test/URLConnectionFileActions.java new file mode 100644 index 000000000000..582d8296488c --- /dev/null +++ b/libs/entitlement/qa/entitlement-test-plugin/src/main/java/org/elasticsearch/entitlement/qa/test/URLConnectionFileActions.java @@ -0,0 +1,117 @@ +/* + * 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.test; + +import org.elasticsearch.core.CheckedConsumer; +import org.elasticsearch.entitlement.qa.entitled.EntitledActions; + +import java.io.IOException; +import java.net.URLConnection; + +import static org.elasticsearch.entitlement.qa.test.EntitlementTest.ExpectedAccess.PLUGINS; + +class URLConnectionFileActions { + + private static void withJdkFileConnection(CheckedConsumer connectionConsumer) throws Exception { + var conn = EntitledActions.createFileURLConnection(); + // Be sure we got the connection implementation we want + assert conn.getClass().getSimpleName().equals("FileURLConnection"); + try { + connectionConsumer.accept(conn); + } catch (IOException e) { + // It's OK, it means we passed entitlement checks, and we tried to perform some operation + } + } + + @EntitlementTest(expectedAccess = PLUGINS) + static void sunFileURLConnectionConnect() throws Exception { + withJdkFileConnection(URLConnection::connect); + } + + @EntitlementTest(expectedAccess = PLUGINS) + static void sunFileURLConnectionGetHeaderFields() throws Exception { + withJdkFileConnection(URLConnection::getHeaderFields); + } + + @EntitlementTest(expectedAccess = PLUGINS) + static void sunFileURLConnectionGetHeaderFieldWithName() throws Exception { + withJdkFileConnection(urlConnection -> urlConnection.getHeaderField("date")); + } + + @EntitlementTest(expectedAccess = PLUGINS) + static void sunFileURLConnectionGetHeaderFieldWithIndex() throws Exception { + withJdkFileConnection(urlConnection -> urlConnection.getHeaderField(0)); + } + + @EntitlementTest(expectedAccess = PLUGINS) + static void sunFileURLConnectionGetContentLength() throws Exception { + withJdkFileConnection(URLConnection::getContentLength); + } + + @EntitlementTest(expectedAccess = PLUGINS) + static void sunFileURLConnectionGetContentLengthLong() throws Exception { + withJdkFileConnection(URLConnection::getContentLengthLong); + } + + @EntitlementTest(expectedAccess = PLUGINS) + static void sunFileURLConnectionGetHeaderFieldKey() throws Exception { + withJdkFileConnection(urlConnection -> urlConnection.getHeaderFieldKey(0)); + } + + @EntitlementTest(expectedAccess = PLUGINS) + static void sunFileURLConnectionGetLastModified() throws Exception { + withJdkFileConnection(URLConnection::getLastModified); + } + + @EntitlementTest(expectedAccess = PLUGINS) + static void sunFileURLConnectionGetInputStream() throws Exception { + withJdkFileConnection(URLConnection::getInputStream); + } + + @EntitlementTest(expectedAccess = PLUGINS) + static void sunFileURLConnectionGetContentType() throws Exception { + withJdkFileConnection(URLConnection::getContentType); + } + + @EntitlementTest(expectedAccess = PLUGINS) + static void sunFileURLConnectionGetContentEncoding() throws Exception { + withJdkFileConnection(URLConnection::getContentEncoding); + } + + @EntitlementTest(expectedAccess = PLUGINS) + static void sunFileURLConnectionGetExpiration() throws Exception { + withJdkFileConnection(URLConnection::getExpiration); + } + + @EntitlementTest(expectedAccess = PLUGINS) + static void sunFileURLConnectionGetDate() throws Exception { + withJdkFileConnection(URLConnection::getDate); + } + + @EntitlementTest(expectedAccess = PLUGINS) + static void sunFileURLConnectionGetHeaderFieldInt() throws Exception { + withJdkFileConnection(conn -> conn.getHeaderFieldInt("field", 0)); + } + + @EntitlementTest(expectedAccess = PLUGINS) + static void sunFileURLConnectionGetHeaderFieldLong() throws Exception { + withJdkFileConnection(conn -> conn.getHeaderFieldLong("field", 0)); + } + + @EntitlementTest(expectedAccess = PLUGINS) + static void sunFileURLConnectionGetContent() throws Exception { + withJdkFileConnection(URLConnection::getContent); + } + + @EntitlementTest(expectedAccess = PLUGINS) + static void sunFileURLConnectionGetContentWithClasses() throws Exception { + withJdkFileConnection(conn -> conn.getContent(new Class[] { String.class })); + } +} diff --git a/libs/entitlement/src/main/java/org/elasticsearch/entitlement/runtime/api/ElasticsearchEntitlementChecker.java b/libs/entitlement/src/main/java/org/elasticsearch/entitlement/runtime/api/ElasticsearchEntitlementChecker.java index 9f80e0d4bc42..0afb3ff18985 100644 --- a/libs/entitlement/src/main/java/org/elasticsearch/entitlement/runtime/api/ElasticsearchEntitlementChecker.java +++ b/libs/entitlement/src/main/java/org/elasticsearch/entitlement/runtime/api/ElasticsearchEntitlementChecker.java @@ -49,6 +49,7 @@ import java.net.Socket; import java.net.SocketAddress; import java.net.SocketImplFactory; import java.net.URI; +import java.net.URISyntaxException; import java.net.URL; import java.net.URLStreamHandler; import java.net.URLStreamHandlerFactory; @@ -74,6 +75,7 @@ import java.nio.file.Files; import java.nio.file.LinkOption; import java.nio.file.OpenOption; import java.nio.file.Path; +import java.nio.file.Paths; import java.nio.file.StandardOpenOption; import java.nio.file.WatchEvent; import java.nio.file.WatchService; @@ -644,6 +646,8 @@ public class ElasticsearchEntitlementChecker implements EntitlementChecker { public void check$java_net_URL$openConnection(Class callerClass, java.net.URL that) { if (isNetworkUrl(that)) { policyManager.checkOutboundNetworkAccess(callerClass); + } else if (isFileUrl(that)) { + checkURLFileRead(callerClass, that); } } @@ -651,6 +655,8 @@ public class ElasticsearchEntitlementChecker implements EntitlementChecker { public void check$java_net_URL$openConnection(Class callerClass, URL that, Proxy proxy) { if (proxy.type() != Proxy.Type.DIRECT || isNetworkUrl(that)) { policyManager.checkOutboundNetworkAccess(callerClass); + } else if (isFileUrl(that)) { + checkURLFileRead(callerClass, that); } } @@ -658,6 +664,8 @@ public class ElasticsearchEntitlementChecker implements EntitlementChecker { public void check$java_net_URL$openStream(Class callerClass, java.net.URL that) { if (isNetworkUrl(that)) { policyManager.checkOutboundNetworkAccess(callerClass); + } else if (isFileUrl(that)) { + checkURLFileRead(callerClass, that); } } @@ -665,6 +673,8 @@ public class ElasticsearchEntitlementChecker implements EntitlementChecker { public void check$java_net_URL$getContent(Class callerClass, java.net.URL that) { if (isNetworkUrl(that)) { policyManager.checkOutboundNetworkAccess(callerClass); + } else if (isFileUrl(that)) { + checkURLFileRead(callerClass, that); } } @@ -672,6 +682,8 @@ public class ElasticsearchEntitlementChecker implements EntitlementChecker { public void check$java_net_URL$getContent(Class callerClass, java.net.URL that, Class[] classes) { if (isNetworkUrl(that)) { policyManager.checkOutboundNetworkAccess(callerClass); + } else if (isFileUrl(that)) { + checkURLFileRead(callerClass, that); } } @@ -681,22 +693,37 @@ public class ElasticsearchEntitlementChecker implements EntitlementChecker { "sun.net.www.protocol.mailto.MailToURLConnection" ); + private static final List FILE_URL_CONNECT_CLASS_NAMES = List.of("sun.net.www.protocol.file.FileURLConnection"); + private static final Set NETWORK_PROTOCOLS = Set.of("http", "https", "ftp", "mailto"); + private static final Set FILE_PROTOCOLS = Set.of("file"); + private static boolean isNetworkUrl(java.net.URL url) { return NETWORK_PROTOCOLS.contains(url.getProtocol()); } + private static boolean isFileUrl(java.net.URL url) { + return FILE_PROTOCOLS.contains(url.getProtocol()); + } + private static boolean isNetworkUrlConnection(java.net.URLConnection urlConnection) { var connectionClass = urlConnection.getClass(); return HttpURLConnection.class.isAssignableFrom(connectionClass) || ADDITIONAL_NETWORK_URL_CONNECT_CLASS_NAMES.contains(connectionClass.getName()); } + private static boolean isFileUrlConnection(java.net.URLConnection urlConnection) { + var connectionClass = urlConnection.getClass(); + return FILE_URL_CONNECT_CLASS_NAMES.contains(connectionClass.getName()); + } + @Override public void check$java_net_URLConnection$getContentLength(Class callerClass, java.net.URLConnection that) { if (isNetworkUrlConnection(that)) { policyManager.checkOutboundNetworkAccess(callerClass); + } else if (isFileUrlConnection(that)) { + checkURLFileRead(callerClass, that.getURL()); } } @@ -704,6 +731,8 @@ public class ElasticsearchEntitlementChecker implements EntitlementChecker { public void check$java_net_URLConnection$getContentLengthLong(Class callerClass, java.net.URLConnection that) { if (isNetworkUrlConnection(that)) { policyManager.checkOutboundNetworkAccess(callerClass); + } else if (isFileUrlConnection(that)) { + checkURLFileRead(callerClass, that.getURL()); } } @@ -711,6 +740,8 @@ public class ElasticsearchEntitlementChecker implements EntitlementChecker { public void check$java_net_URLConnection$getContentType(Class callerClass, java.net.URLConnection that) { if (isNetworkUrlConnection(that)) { policyManager.checkOutboundNetworkAccess(callerClass); + } else if (isFileUrlConnection(that)) { + checkURLFileRead(callerClass, that.getURL()); } } @@ -718,6 +749,8 @@ public class ElasticsearchEntitlementChecker implements EntitlementChecker { public void check$java_net_URLConnection$getContentEncoding(Class callerClass, java.net.URLConnection that) { if (isNetworkUrlConnection(that)) { policyManager.checkOutboundNetworkAccess(callerClass); + } else if (isFileUrlConnection(that)) { + checkURLFileRead(callerClass, that.getURL()); } } @@ -725,6 +758,8 @@ public class ElasticsearchEntitlementChecker implements EntitlementChecker { public void check$java_net_URLConnection$getExpiration(Class callerClass, java.net.URLConnection that) { if (isNetworkUrlConnection(that)) { policyManager.checkOutboundNetworkAccess(callerClass); + } else if (isFileUrlConnection(that)) { + checkURLFileRead(callerClass, that.getURL()); } } @@ -732,6 +767,8 @@ public class ElasticsearchEntitlementChecker implements EntitlementChecker { public void check$java_net_URLConnection$getDate(Class callerClass, java.net.URLConnection that) { if (isNetworkUrlConnection(that)) { policyManager.checkOutboundNetworkAccess(callerClass); + } else if (isFileUrlConnection(that)) { + checkURLFileRead(callerClass, that.getURL()); } } @@ -739,6 +776,8 @@ public class ElasticsearchEntitlementChecker implements EntitlementChecker { public void check$java_net_URLConnection$getLastModified(Class callerClass, java.net.URLConnection that) { if (isNetworkUrlConnection(that)) { policyManager.checkOutboundNetworkAccess(callerClass); + } else if (isFileUrlConnection(that)) { + checkURLFileRead(callerClass, that.getURL()); } } @@ -751,6 +790,8 @@ public class ElasticsearchEntitlementChecker implements EntitlementChecker { ) { if (isNetworkUrlConnection(that)) { policyManager.checkOutboundNetworkAccess(callerClass); + } else if (isFileUrlConnection(that)) { + checkURLFileRead(callerClass, that.getURL()); } } @@ -763,6 +804,8 @@ public class ElasticsearchEntitlementChecker implements EntitlementChecker { ) { if (isNetworkUrlConnection(that)) { policyManager.checkOutboundNetworkAccess(callerClass); + } else if (isFileUrlConnection(that)) { + checkURLFileRead(callerClass, that.getURL()); } } @@ -775,6 +818,8 @@ public class ElasticsearchEntitlementChecker implements EntitlementChecker { ) { if (isNetworkUrlConnection(that)) { policyManager.checkOutboundNetworkAccess(callerClass); + } else if (isFileUrlConnection(that)) { + checkURLFileRead(callerClass, that.getURL()); } } @@ -782,6 +827,8 @@ public class ElasticsearchEntitlementChecker implements EntitlementChecker { public void check$java_net_URLConnection$getContent(Class callerClass, java.net.URLConnection that) { if (isNetworkUrlConnection(that)) { policyManager.checkOutboundNetworkAccess(callerClass); + } else if (isFileUrlConnection(that)) { + checkURLFileRead(callerClass, that.getURL()); } } @@ -789,6 +836,8 @@ public class ElasticsearchEntitlementChecker implements EntitlementChecker { public void check$java_net_URLConnection$getContent(Class callerClass, java.net.URLConnection that, Class[] classes) { if (isNetworkUrlConnection(that)) { policyManager.checkOutboundNetworkAccess(callerClass); + } else if (isFileUrlConnection(that)) { + checkURLFileRead(callerClass, that.getURL()); } } @@ -817,6 +866,8 @@ public class ElasticsearchEntitlementChecker implements EntitlementChecker { public void check$sun_net_www_URLConnection$getHeaderField(Class callerClass, java.net.URLConnection that, String name) { if (isNetworkUrlConnection(that)) { policyManager.checkOutboundNetworkAccess(callerClass); + } else if (isFileUrlConnection(that)) { + checkURLFileRead(callerClass, that.getURL()); } } @@ -824,6 +875,8 @@ public class ElasticsearchEntitlementChecker implements EntitlementChecker { public void check$sun_net_www_URLConnection$getHeaderFields(Class callerClass, java.net.URLConnection that) { if (isNetworkUrlConnection(that)) { policyManager.checkOutboundNetworkAccess(callerClass); + } else if (isFileUrlConnection(that)) { + checkURLFileRead(callerClass, that.getURL()); } } @@ -831,6 +884,8 @@ public class ElasticsearchEntitlementChecker implements EntitlementChecker { public void check$sun_net_www_URLConnection$getHeaderFieldKey(Class callerClass, java.net.URLConnection that, int n) { if (isNetworkUrlConnection(that)) { policyManager.checkOutboundNetworkAccess(callerClass); + } else if (isFileUrlConnection(that)) { + checkURLFileRead(callerClass, that.getURL()); } } @@ -838,6 +893,8 @@ public class ElasticsearchEntitlementChecker implements EntitlementChecker { public void check$sun_net_www_URLConnection$getHeaderField(Class callerClass, java.net.URLConnection that, int n) { if (isNetworkUrlConnection(that)) { policyManager.checkOutboundNetworkAccess(callerClass); + } else if (isFileUrlConnection(that)) { + checkURLFileRead(callerClass, that.getURL()); } } @@ -845,6 +902,8 @@ public class ElasticsearchEntitlementChecker implements EntitlementChecker { public void check$sun_net_www_URLConnection$getContentType(Class callerClass, java.net.URLConnection that) { if (isNetworkUrlConnection(that)) { policyManager.checkOutboundNetworkAccess(callerClass); + } else if (isFileUrlConnection(that)) { + checkURLFileRead(callerClass, that.getURL()); } } @@ -852,6 +911,8 @@ public class ElasticsearchEntitlementChecker implements EntitlementChecker { public void check$sun_net_www_URLConnection$getContentLength(Class callerClass, java.net.URLConnection that) { if (isNetworkUrlConnection(that)) { policyManager.checkOutboundNetworkAccess(callerClass); + } else if (isFileUrlConnection(that)) { + checkURLFileRead(callerClass, that.getURL()); } } @@ -2724,4 +2785,66 @@ public class ElasticsearchEntitlementChecker implements EntitlementChecker { ) { policyManager.checkFileRead(callerClass, that); } + + private void checkURLFileRead(Class callerClass, URL url) { + try { + policyManager.checkFileRead(callerClass, Paths.get(url.toURI())); + } catch (URISyntaxException e) { + // We expect this method to be called only on File URLs; otherwise the underlying method would fail anyway + throw new RuntimeException(e); + } + } + + @Override + public void check$sun_net_www_protocol_file_FileURLConnection$connect(Class callerClass, java.net.URLConnection that) { + checkURLFileRead(callerClass, that.getURL()); + } + + @Override + public void check$sun_net_www_protocol_file_FileURLConnection$getHeaderFields(Class callerClass, java.net.URLConnection that) { + checkURLFileRead(callerClass, that.getURL()); + } + + @Override + public void check$sun_net_www_protocol_file_FileURLConnection$getHeaderField( + Class callerClass, + java.net.URLConnection that, + String name + ) { + checkURLFileRead(callerClass, that.getURL()); + } + + @Override + public void check$sun_net_www_protocol_file_FileURLConnection$getHeaderField(Class callerClass, java.net.URLConnection that, int n) { + checkURLFileRead(callerClass, that.getURL()); + } + + @Override + public void check$sun_net_www_protocol_file_FileURLConnection$getContentLength(Class callerClass, java.net.URLConnection that) { + checkURLFileRead(callerClass, that.getURL()); + } + + @Override + public void check$sun_net_www_protocol_file_FileURLConnection$getContentLengthLong(Class callerClass, java.net.URLConnection that) { + checkURLFileRead(callerClass, that.getURL()); + } + + @Override + public void check$sun_net_www_protocol_file_FileURLConnection$getHeaderFieldKey( + Class callerClass, + java.net.URLConnection that, + int n + ) { + checkURLFileRead(callerClass, that.getURL()); + } + + @Override + public void check$sun_net_www_protocol_file_FileURLConnection$getLastModified(Class callerClass, java.net.URLConnection that) { + checkURLFileRead(callerClass, that.getURL()); + } + + @Override + public void check$sun_net_www_protocol_file_FileURLConnection$getInputStream(Class callerClass, java.net.URLConnection that) { + checkURLFileRead(callerClass, that.getURL()); + } }