Skip to content

Commit d93011e

Browse files
authored
[8.x] Add "always denied" network access checks (elastic#119867) (elastic#120027)
1 parent 19c9dfe commit d93011e

File tree

7 files changed

+207
-16
lines changed

7 files changed

+207
-16
lines changed

libs/entitlement/bridge/src/main/java/org/elasticsearch/entitlement/bridge/EntitlementChecker.java

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,14 +15,18 @@
1515
import java.net.ContentHandlerFactory;
1616
import java.net.DatagramSocketImplFactory;
1717
import java.net.FileNameMap;
18+
import java.net.ProxySelector;
19+
import java.net.ResponseCache;
1820
import java.net.SocketImplFactory;
1921
import java.net.URL;
22+
import java.net.URLStreamHandler;
2023
import java.net.URLStreamHandlerFactory;
2124
import java.util.List;
2225

2326
import javax.net.ssl.HostnameVerifier;
2427
import javax.net.ssl.HttpsURLConnection;
2528
import javax.net.ssl.SSLContext;
29+
import javax.net.ssl.SSLSession;
2630
import javax.net.ssl.SSLSocketFactory;
2731

2832
@SuppressWarnings("unused") // Called from instrumentation code inserted by the Entitlements agent
@@ -167,4 +171,22 @@ public interface EntitlementChecker {
167171

168172
void check$java_net_URLConnection$$setContentHandlerFactory(Class<?> callerClass, ContentHandlerFactory fac);
169173

174+
////////////////////
175+
//
176+
// Network access
177+
//
178+
void check$java_net_ProxySelector$$setDefault(Class<?> callerClass, ProxySelector ps);
179+
180+
void check$java_net_ResponseCache$$setDefault(Class<?> callerClass, ResponseCache rc);
181+
182+
void check$java_net_spi_InetAddressResolverProvider$(Class<?> callerClass);
183+
184+
void check$java_net_spi_URLStreamHandlerProvider$(Class<?> callerClass);
185+
186+
void check$java_net_URL$(Class<?> callerClass, String protocol, String host, int port, String file, URLStreamHandler handler);
187+
188+
void check$java_net_URL$(Class<?> callerClass, URL context, String spec, URLStreamHandler handler);
189+
190+
// The only implementation of SSLSession#getSessionContext(); unfortunately it's an interface, so we need to check the implementation
191+
void check$sun_security_ssl_SSLSessionImpl$getSessionContext(Class<?> callerClass, SSLSession sslSession);
170192
}

libs/entitlement/qa/common/build.gradle

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
*/
99

1010
apply plugin: 'elasticsearch.build'
11+
apply plugin: 'elasticsearch.mrjar'
1112

1213
dependencies {
1314
implementation project(':server')

libs/entitlement/qa/common/src/main/java/org/elasticsearch/entitlement/qa/common/RestEntitlementsCheckAction.java

Lines changed: 88 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -34,53 +34,61 @@
3434
import java.io.IOException;
3535
import java.io.UncheckedIOException;
3636
import java.net.DatagramSocket;
37-
import java.net.DatagramSocketImpl;
38-
import java.net.DatagramSocketImplFactory;
3937
import java.net.HttpURLConnection;
38+
import java.net.MalformedURLException;
39+
import java.net.ProxySelector;
40+
import java.net.ResponseCache;
4041
import java.net.ServerSocket;
4142
import java.net.Socket;
4243
import java.net.URL;
4344
import java.net.URLClassLoader;
4445
import java.net.URLConnection;
46+
import java.net.URLStreamHandler;
47+
import java.net.spi.URLStreamHandlerProvider;
4548
import java.security.NoSuchAlgorithmException;
4649
import java.util.List;
4750
import java.util.Map;
4851
import java.util.Set;
4952
import java.util.stream.Collectors;
53+
import java.util.stream.Stream;
5054

5155
import javax.net.ssl.HttpsURLConnection;
5256
import javax.net.ssl.SSLContext;
57+
import javax.net.ssl.SSLSession;
58+
import javax.net.ssl.SSLSocket;
59+
import javax.net.ssl.SSLSocketFactory;
5360

5461
import static java.util.Map.entry;
5562
import static org.elasticsearch.entitlement.qa.common.RestEntitlementsCheckAction.CheckAction.alwaysDenied;
5663
import static org.elasticsearch.entitlement.qa.common.RestEntitlementsCheckAction.CheckAction.deniedToPlugins;
5764
import static org.elasticsearch.entitlement.qa.common.RestEntitlementsCheckAction.CheckAction.forPlugins;
5865
import static org.elasticsearch.rest.RestRequest.Method.GET;
5966

67+
@SuppressWarnings("unused")
6068
public class RestEntitlementsCheckAction extends BaseRestHandler {
6169
private static final Logger logger = LogManager.getLogger(RestEntitlementsCheckAction.class);
6270
public static final Thread NO_OP_SHUTDOWN_HOOK = new Thread(() -> {}, "Shutdown hook for testing");
6371
private final String prefix;
6472

65-
record CheckAction(Runnable action, boolean isAlwaysDeniedToPlugins) {
73+
record CheckAction(Runnable action, boolean isAlwaysDeniedToPlugins, Integer fromJavaVersion) {
6674
/**
6775
* These cannot be granted to plugins, so our test plugins cannot test the "allowed" case.
68-
* Used both for always-denied entitlements as well as those granted only to the server itself.
76+
* Used both for always-denied entitlements and those granted only to the server itself.
6977
*/
7078
static CheckAction deniedToPlugins(Runnable action) {
71-
return new CheckAction(action, true);
79+
return new CheckAction(action, true, null);
7280
}
7381

7482
static CheckAction forPlugins(Runnable action) {
75-
return new CheckAction(action, false);
83+
return new CheckAction(action, false, null);
7684
}
7785

7886
static CheckAction alwaysDenied(Runnable action) {
79-
return new CheckAction(action, true);
87+
return new CheckAction(action, true, null);
8088
}
8189
}
8290

83-
private static final Map<String, CheckAction> checkActions = Map.ofEntries(
91+
private static final Map<String, CheckAction> checkActions = Stream.of(
8492
entry("runtime_exit", deniedToPlugins(RestEntitlementsCheckAction::runtimeExit)),
8593
entry("runtime_halt", deniedToPlugins(RestEntitlementsCheckAction::runtimeHalt)),
8694
entry("system_exit", deniedToPlugins(RestEntitlementsCheckAction::systemExit)),
@@ -125,8 +133,77 @@ static CheckAction alwaysDenied(Runnable action) {
125133
entry("socket_setSocketImplFactory", alwaysDenied(RestEntitlementsCheckAction::socket$$setSocketImplFactory)),
126134
entry("url_setURLStreamHandlerFactory", alwaysDenied(RestEntitlementsCheckAction::url$$setURLStreamHandlerFactory)),
127135
entry("urlConnection_setFileNameMap", alwaysDenied(RestEntitlementsCheckAction::urlConnection$$setFileNameMap)),
128-
entry("urlConnection_setContentHandlerFactory", alwaysDenied(RestEntitlementsCheckAction::urlConnection$$setContentHandlerFactory))
129-
);
136+
entry("urlConnection_setContentHandlerFactory", alwaysDenied(RestEntitlementsCheckAction::urlConnection$$setContentHandlerFactory)),
137+
138+
entry("proxySelector_setDefault", alwaysDenied(RestEntitlementsCheckAction::setDefaultProxySelector)),
139+
entry("responseCache_setDefault", alwaysDenied(RestEntitlementsCheckAction::setDefaultResponseCache)),
140+
entry(
141+
"createInetAddressResolverProvider",
142+
new CheckAction(VersionSpecificNetworkChecks::createInetAddressResolverProvider, true, 18)
143+
),
144+
entry("createURLStreamHandlerProvider", alwaysDenied(RestEntitlementsCheckAction::createURLStreamHandlerProvider)),
145+
entry("createURLWithURLStreamHandler", alwaysDenied(RestEntitlementsCheckAction::createURLWithURLStreamHandler)),
146+
entry("createURLWithURLStreamHandler2", alwaysDenied(RestEntitlementsCheckAction::createURLWithURLStreamHandler2)),
147+
entry("sslSessionImpl_getSessionContext", alwaysDenied(RestEntitlementsCheckAction::sslSessionImplGetSessionContext))
148+
)
149+
.filter(entry -> entry.getValue().fromJavaVersion() == null || Runtime.version().feature() >= entry.getValue().fromJavaVersion())
150+
.collect(Collectors.toUnmodifiableMap(Map.Entry::getKey, Map.Entry::getValue));
151+
152+
private static void createURLStreamHandlerProvider() {
153+
var x = new URLStreamHandlerProvider() {
154+
@Override
155+
public URLStreamHandler createURLStreamHandler(String protocol) {
156+
return null;
157+
}
158+
};
159+
}
160+
161+
private static void sslSessionImplGetSessionContext() {
162+
SSLSocketFactory factory = HttpsURLConnection.getDefaultSSLSocketFactory();
163+
try (SSLSocket socket = (SSLSocket) factory.createSocket()) {
164+
SSLSession session = socket.getSession();
165+
166+
session.getSessionContext();
167+
} catch (IOException e) {
168+
throw new RuntimeException(e);
169+
}
170+
}
171+
172+
@SuppressWarnings("deprecation")
173+
private static void createURLWithURLStreamHandler() {
174+
try {
175+
var x = new URL("http", "host", 1234, "file", new URLStreamHandler() {
176+
@Override
177+
protected URLConnection openConnection(URL u) {
178+
return null;
179+
}
180+
});
181+
} catch (MalformedURLException e) {
182+
throw new RuntimeException(e);
183+
}
184+
}
185+
186+
@SuppressWarnings("deprecation")
187+
private static void createURLWithURLStreamHandler2() {
188+
try {
189+
var x = new URL(null, "spec", new URLStreamHandler() {
190+
@Override
191+
protected URLConnection openConnection(URL u) {
192+
return null;
193+
}
194+
});
195+
} catch (MalformedURLException e) {
196+
throw new RuntimeException(e);
197+
}
198+
}
199+
200+
private static void setDefaultResponseCache() {
201+
ResponseCache.setDefault(null);
202+
}
203+
204+
private static void setDefaultProxySelector() {
205+
ProxySelector.setDefault(null);
206+
}
130207

131208
private static void setDefaultSSLContext() {
132209
try {
@@ -270,12 +347,7 @@ private static void setHttpsConnectionProperties() {
270347
@SuppressForbidden(reason = "We're required to prevent calls to this forbidden API")
271348
private static void datagramSocket$$setDatagramSocketImplFactory() {
272349
try {
273-
DatagramSocket.setDatagramSocketImplFactory(new DatagramSocketImplFactory() {
274-
@Override
275-
public DatagramSocketImpl createDatagramSocketImpl() {
276-
throw new IllegalStateException();
277-
}
278-
});
350+
DatagramSocket.setDatagramSocketImplFactory(() -> { throw new IllegalStateException(); });
279351
} catch (IOException e) {
280352
throw new IllegalStateException(e);
281353
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
/*
2+
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
3+
* or more contributor license agreements. Licensed under the "Elastic License
4+
* 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side
5+
* Public License v 1"; you may not use this file except in compliance with, at
6+
* your election, the "Elastic License 2.0", the "GNU Affero General Public
7+
* License v3.0 only", or the "Server Side Public License, v 1".
8+
*/
9+
10+
package org.elasticsearch.entitlement.qa.common;
11+
12+
class VersionSpecificNetworkChecks {
13+
static void createInetAddressResolverProvider() {}
14+
}
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
/*
2+
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
3+
* or more contributor license agreements. Licensed under the "Elastic License
4+
* 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side
5+
* Public License v 1"; you may not use this file except in compliance with, at
6+
* your election, the "Elastic License 2.0", the "GNU Affero General Public
7+
* License v3.0 only", or the "Server Side Public License, v 1".
8+
*/
9+
10+
package org.elasticsearch.entitlement.qa.common;
11+
12+
import java.net.spi.InetAddressResolver;
13+
import java.net.spi.InetAddressResolverProvider;
14+
15+
class VersionSpecificNetworkChecks {
16+
static void createInetAddressResolverProvider() {
17+
var x = new InetAddressResolverProvider() {
18+
@Override
19+
public InetAddressResolver get(Configuration configuration) {
20+
return null;
21+
}
22+
23+
@Override
24+
public String name() {
25+
return "TEST";
26+
}
27+
};
28+
}
29+
}

libs/entitlement/src/main/java/org/elasticsearch/entitlement/runtime/api/ElasticsearchEntitlementChecker.java

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,14 +18,18 @@
1818
import java.net.ContentHandlerFactory;
1919
import java.net.DatagramSocketImplFactory;
2020
import java.net.FileNameMap;
21+
import java.net.ProxySelector;
22+
import java.net.ResponseCache;
2123
import java.net.SocketImplFactory;
2224
import java.net.URL;
25+
import java.net.URLStreamHandler;
2326
import java.net.URLStreamHandlerFactory;
2427
import java.util.List;
2528

2629
import javax.net.ssl.HostnameVerifier;
2730
import javax.net.ssl.HttpsURLConnection;
2831
import javax.net.ssl.SSLContext;
32+
import javax.net.ssl.SSLSession;
2933
import javax.net.ssl.SSLSocketFactory;
3034

3135
/**
@@ -310,4 +314,39 @@ public ElasticsearchEntitlementChecker(PolicyManager policyManager) {
310314
public void check$javax_net_ssl_SSLContext$$setDefault(Class<?> callerClass, SSLContext context) {
311315
policyManager.checkChangeJVMGlobalState(callerClass);
312316
}
317+
318+
@Override
319+
public void check$java_net_ProxySelector$$setDefault(Class<?> callerClass, ProxySelector ps) {
320+
policyManager.checkChangeNetworkHandling(callerClass);
321+
}
322+
323+
@Override
324+
public void check$java_net_ResponseCache$$setDefault(Class<?> callerClass, ResponseCache rc) {
325+
policyManager.checkChangeNetworkHandling(callerClass);
326+
}
327+
328+
@Override
329+
public void check$java_net_spi_InetAddressResolverProvider$(Class<?> callerClass) {
330+
policyManager.checkChangeNetworkHandling(callerClass);
331+
}
332+
333+
@Override
334+
public void check$java_net_spi_URLStreamHandlerProvider$(Class<?> callerClass) {
335+
policyManager.checkChangeNetworkHandling(callerClass);
336+
}
337+
338+
@Override
339+
public void check$java_net_URL$(Class<?> callerClass, String protocol, String host, int port, String file, URLStreamHandler handler) {
340+
policyManager.checkChangeNetworkHandling(callerClass);
341+
}
342+
343+
@Override
344+
public void check$java_net_URL$(Class<?> callerClass, URL context, String spec, URLStreamHandler handler) {
345+
policyManager.checkChangeNetworkHandling(callerClass);
346+
}
347+
348+
@Override
349+
public void check$sun_security_ssl_SSLSessionImpl$getSessionContext(Class<?> callerClass, SSLSession sslSession) {
350+
policyManager.checkReadSensitiveNetworkInformation(callerClass);
351+
}
313352
}

libs/entitlement/src/main/java/org/elasticsearch/entitlement/runtime/policy/PolicyManager.java

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -171,6 +171,20 @@ public void checkChangeJVMGlobalState(Class<?> callerClass) {
171171
});
172172
}
173173

174+
/**
175+
* Check for operations that can modify the way network operations are handled
176+
*/
177+
public void checkChangeNetworkHandling(Class<?> callerClass) {
178+
checkChangeJVMGlobalState(callerClass);
179+
}
180+
181+
/**
182+
* Check for operations that can access sensitive network information, e.g. secrets, tokens or SSL sessions
183+
*/
184+
public void checkReadSensitiveNetworkInformation(Class<?> callerClass) {
185+
neverEntitled(callerClass, "access sensitive network information");
186+
}
187+
174188
private String operationDescription(String methodName) {
175189
// TODO: Use a more human-readable description. Perhaps share code with InstrumentationServiceImpl.parseCheckerMethodName
176190
return methodName.substring(methodName.indexOf('$'));

0 commit comments

Comments
 (0)