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 7af921c46bd51..4f97d9a7d22e5 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 @@ -1148,6 +1148,26 @@ void checkPathRegister( 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 531ac97c65046..a1f59ce2f6006 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 static URLConnection createHttpsURLConnection() throws IOException { 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 d033fa475f282..3aefd864e0a5f 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 @@ static CheckAction alwaysDenied(CheckedRunnable action) { getTestEntries(PathActions.class), getTestEntries(SpiActions.class), getTestEntries(SystemActions.class), + getTestEntries(URLConnectionFileActions.class), getTestEntries(URLConnectionNetworkActions.class), getTestEntries(VersionSpecificManageThreadsActions.class), getTestEntries(VersionSpecificNioFileSystemActions.class) 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 0000000000000..582d8296488c1 --- /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 6bb91f1ed6359..61fc45a4f5552 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 @@ -42,6 +42,7 @@ 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; @@ -67,6 +68,7 @@ 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; @@ -636,6 +638,8 @@ public ElasticsearchEntitlementChecker(PolicyManager policyManager) { 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); } } @@ -643,6 +647,8 @@ public ElasticsearchEntitlementChecker(PolicyManager policyManager) { 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); } } @@ -650,6 +656,8 @@ public ElasticsearchEntitlementChecker(PolicyManager policyManager) { 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); } } @@ -657,6 +665,8 @@ public ElasticsearchEntitlementChecker(PolicyManager policyManager) { 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); } } @@ -664,6 +674,8 @@ public ElasticsearchEntitlementChecker(PolicyManager policyManager) { 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); } } @@ -673,22 +685,37 @@ public ElasticsearchEntitlementChecker(PolicyManager policyManager) { "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()); } } @@ -696,6 +723,8 @@ private static boolean isNetworkUrlConnection(java.net.URLConnection urlConnecti 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()); } } @@ -703,6 +732,8 @@ private static boolean isNetworkUrlConnection(java.net.URLConnection urlConnecti 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()); } } @@ -710,6 +741,8 @@ private static boolean isNetworkUrlConnection(java.net.URLConnection urlConnecti 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()); } } @@ -717,6 +750,8 @@ private static boolean isNetworkUrlConnection(java.net.URLConnection urlConnecti 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()); } } @@ -724,6 +759,8 @@ private static boolean isNetworkUrlConnection(java.net.URLConnection urlConnecti 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()); } } @@ -731,6 +768,8 @@ private static boolean isNetworkUrlConnection(java.net.URLConnection urlConnecti 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()); } } @@ -743,6 +782,8 @@ private static boolean isNetworkUrlConnection(java.net.URLConnection urlConnecti ) { if (isNetworkUrlConnection(that)) { policyManager.checkOutboundNetworkAccess(callerClass); + } else if (isFileUrlConnection(that)) { + checkURLFileRead(callerClass, that.getURL()); } } @@ -755,6 +796,8 @@ private static boolean isNetworkUrlConnection(java.net.URLConnection urlConnecti ) { if (isNetworkUrlConnection(that)) { policyManager.checkOutboundNetworkAccess(callerClass); + } else if (isFileUrlConnection(that)) { + checkURLFileRead(callerClass, that.getURL()); } } @@ -767,6 +810,8 @@ private static boolean isNetworkUrlConnection(java.net.URLConnection urlConnecti ) { if (isNetworkUrlConnection(that)) { policyManager.checkOutboundNetworkAccess(callerClass); + } else if (isFileUrlConnection(that)) { + checkURLFileRead(callerClass, that.getURL()); } } @@ -774,6 +819,8 @@ private static boolean isNetworkUrlConnection(java.net.URLConnection urlConnecti 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()); } } @@ -781,6 +828,8 @@ private static boolean isNetworkUrlConnection(java.net.URLConnection urlConnecti 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()); } } @@ -809,6 +858,8 @@ private static boolean isNetworkUrlConnection(java.net.URLConnection urlConnecti 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()); } } @@ -816,6 +867,8 @@ private static boolean isNetworkUrlConnection(java.net.URLConnection urlConnecti 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()); } } @@ -823,6 +876,8 @@ private static boolean isNetworkUrlConnection(java.net.URLConnection urlConnecti 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()); } } @@ -830,6 +885,8 @@ private static boolean isNetworkUrlConnection(java.net.URLConnection urlConnecti 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()); } } @@ -837,6 +894,8 @@ private static boolean isNetworkUrlConnection(java.net.URLConnection urlConnecti 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()); } } @@ -844,6 +903,8 @@ private static boolean isNetworkUrlConnection(java.net.URLConnection urlConnecti 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()); } } @@ -2621,4 +2682,66 @@ public void checkPathRegister( ) { 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()); + } }