Skip to content

Commit d91427e

Browse files
authored
[Entitlements] Add set_https_connection_properties entitlement and checks (#118577) (#119464)
1 parent 261cf2c commit d91427e

File tree

21 files changed

+265
-8
lines changed

21 files changed

+265
-8
lines changed

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

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,11 @@
1313
import java.net.URLStreamHandlerFactory;
1414
import java.util.List;
1515

16+
import javax.net.ssl.HostnameVerifier;
17+
import javax.net.ssl.HttpsURLConnection;
18+
import javax.net.ssl.SSLContext;
19+
import javax.net.ssl.SSLSocketFactory;
20+
1621
@SuppressWarnings("unused") // Called from instrumentation code inserted by the Entitlements agent
1722
public interface EntitlementChecker {
1823

@@ -21,7 +26,7 @@ public interface EntitlementChecker {
2126

2227
void check$java_lang_Runtime$halt(Class<?> callerClass, Runtime runtime, int status);
2328

24-
// URLClassLoader ctor
29+
// URLClassLoader constructors
2530
void check$java_net_URLClassLoader$(Class<?> callerClass, URL[] urls);
2631

2732
void check$java_net_URLClassLoader$(Class<?> callerClass, URL[] urls, ClassLoader parent);
@@ -32,6 +37,15 @@ public interface EntitlementChecker {
3237

3338
void check$java_net_URLClassLoader$(Class<?> callerClass, String name, URL[] urls, ClassLoader parent, URLStreamHandlerFactory factory);
3439

40+
// "setFactory" methods
41+
void check$javax_net_ssl_HttpsURLConnection$setSSLSocketFactory(Class<?> callerClass, HttpsURLConnection conn, SSLSocketFactory sf);
42+
43+
void check$javax_net_ssl_HttpsURLConnection$$setDefaultSSLSocketFactory(Class<?> callerClass, SSLSocketFactory sf);
44+
45+
void check$javax_net_ssl_HttpsURLConnection$$setDefaultHostnameVerifier(Class<?> callerClass, HostnameVerifier hv);
46+
47+
void check$javax_net_ssl_SSLContext$$setDefault(Class<?> callerClass, SSLContext context);
48+
3549
// Process creation
3650
void check$java_lang_ProcessBuilder$start(Class<?> callerClass, ProcessBuilder that);
3751

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

Lines changed: 40 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,12 +23,17 @@
2323
import java.io.UncheckedIOException;
2424
import java.net.URL;
2525
import java.net.URLClassLoader;
26+
import java.security.NoSuchAlgorithmException;
2627
import java.util.List;
2728
import java.util.Map;
2829
import java.util.Set;
2930
import java.util.stream.Collectors;
3031

32+
import javax.net.ssl.HttpsURLConnection;
33+
import javax.net.ssl.SSLContext;
34+
3135
import static java.util.Map.entry;
36+
import static org.elasticsearch.entitlement.qa.common.RestEntitlementsCheckAction.CheckAction.alwaysDenied;
3237
import static org.elasticsearch.entitlement.qa.common.RestEntitlementsCheckAction.CheckAction.deniedToPlugins;
3338
import static org.elasticsearch.entitlement.qa.common.RestEntitlementsCheckAction.CheckAction.forPlugins;
3439
import static org.elasticsearch.rest.RestRequest.Method.GET;
@@ -49,16 +54,43 @@ static CheckAction deniedToPlugins(Runnable action) {
4954
static CheckAction forPlugins(Runnable action) {
5055
return new CheckAction(action, false);
5156
}
57+
58+
static CheckAction alwaysDenied(Runnable action) {
59+
return new CheckAction(action, true);
60+
}
5261
}
5362

5463
private static final Map<String, CheckAction> checkActions = Map.ofEntries(
5564
entry("runtime_exit", deniedToPlugins(RestEntitlementsCheckAction::runtimeExit)),
5665
entry("runtime_halt", deniedToPlugins(RestEntitlementsCheckAction::runtimeHalt)),
5766
entry("create_classloader", forPlugins(RestEntitlementsCheckAction::createClassLoader)),
5867
entry("processBuilder_start", deniedToPlugins(RestEntitlementsCheckAction::processBuilder_start)),
59-
entry("processBuilder_startPipeline", deniedToPlugins(RestEntitlementsCheckAction::processBuilder_startPipeline))
68+
entry("processBuilder_startPipeline", deniedToPlugins(RestEntitlementsCheckAction::processBuilder_startPipeline)),
69+
entry("set_https_connection_properties", forPlugins(RestEntitlementsCheckAction::setHttpsConnectionProperties)),
70+
entry("set_default_ssl_socket_factory", alwaysDenied(RestEntitlementsCheckAction::setDefaultSSLSocketFactory)),
71+
entry("set_default_hostname_verifier", alwaysDenied(RestEntitlementsCheckAction::setDefaultHostnameVerifier)),
72+
entry("set_default_ssl_context", alwaysDenied(RestEntitlementsCheckAction::setDefaultSSLContext))
6073
);
6174

75+
private static void setDefaultSSLContext() {
76+
logger.info("Calling SSLContext.setDefault");
77+
try {
78+
SSLContext.setDefault(SSLContext.getDefault());
79+
} catch (NoSuchAlgorithmException e) {
80+
throw new RuntimeException(e);
81+
}
82+
}
83+
84+
private static void setDefaultHostnameVerifier() {
85+
logger.info("Calling HttpsURLConnection.setDefaultHostnameVerifier");
86+
HttpsURLConnection.setDefaultHostnameVerifier((hostname, session) -> false);
87+
}
88+
89+
private static void setDefaultSSLSocketFactory() {
90+
logger.info("Calling HttpsURLConnection.setDefaultSSLSocketFactory");
91+
HttpsURLConnection.setDefaultSSLSocketFactory(new TestSSLSocketFactory());
92+
}
93+
6294
@SuppressForbidden(reason = "Specifically testing Runtime.exit")
6395
private static void runtimeExit() {
6496
Runtime.getRuntime().exit(123);
@@ -93,11 +125,17 @@ private static void processBuilder_startPipeline() {
93125
}
94126
}
95127

128+
private static void setHttpsConnectionProperties() {
129+
logger.info("Calling setSSLSocketFactory");
130+
var connection = new TestHttpsURLConnection();
131+
connection.setSSLSocketFactory(new TestSSLSocketFactory());
132+
}
133+
96134
public RestEntitlementsCheckAction(String prefix) {
97135
this.prefix = prefix;
98136
}
99137

100-
public static Set<String> getServerAndPluginsCheckActions() {
138+
public static Set<String> getCheckActionsAllowedInPlugins() {
101139
return checkActions.entrySet()
102140
.stream()
103141
.filter(kv -> kv.getValue().isAlwaysDeniedToPlugins() == false)
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
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.io.IOException;
13+
import java.security.cert.Certificate;
14+
15+
import javax.net.ssl.HttpsURLConnection;
16+
import javax.net.ssl.SSLPeerUnverifiedException;
17+
18+
class TestHttpsURLConnection extends HttpsURLConnection {
19+
TestHttpsURLConnection() {
20+
super(null);
21+
}
22+
23+
@Override
24+
public void connect() throws IOException {}
25+
26+
@Override
27+
public void disconnect() {}
28+
29+
@Override
30+
public boolean usingProxy() {
31+
return false;
32+
}
33+
34+
@Override
35+
public String getCipherSuite() {
36+
return "";
37+
}
38+
39+
@Override
40+
public Certificate[] getLocalCertificates() {
41+
return new Certificate[0];
42+
}
43+
44+
@Override
45+
public Certificate[] getServerCertificates() throws SSLPeerUnverifiedException {
46+
return new Certificate[0];
47+
}
48+
}
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
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.io.IOException;
13+
import java.net.InetAddress;
14+
import java.net.Socket;
15+
import java.net.UnknownHostException;
16+
17+
import javax.net.ssl.SSLSocketFactory;
18+
19+
class TestSSLSocketFactory extends SSLSocketFactory {
20+
@Override
21+
public Socket createSocket(String host, int port) throws IOException, UnknownHostException {
22+
return null;
23+
}
24+
25+
@Override
26+
public Socket createSocket(String host, int port, InetAddress localHost, int localPort) {
27+
return null;
28+
}
29+
30+
@Override
31+
public Socket createSocket(InetAddress host, int port) throws IOException {
32+
return null;
33+
}
34+
35+
@Override
36+
public Socket createSocket(InetAddress address, int port, InetAddress localAddress, int localPort) throws IOException {
37+
return null;
38+
}
39+
40+
@Override
41+
public String[] getDefaultCipherSuites() {
42+
return new String[0];
43+
}
44+
45+
@Override
46+
public String[] getSupportedCipherSuites() {
47+
return new String[0];
48+
}
49+
50+
@Override
51+
public Socket createSocket(Socket s, String host, int port, boolean autoClose) throws IOException {
52+
return null;
53+
}
54+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
11
ALL-UNNAMED:
22
- create_class_loader
3+
- set_https_connection_properties
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
11
org.elasticsearch.entitlement.qa.common:
22
- create_class_loader
3+
- set_https_connection_properties

libs/entitlement/qa/src/javaRestTest/java/org/elasticsearch/entitlement/qa/EntitlementsAllowedIT.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ public EntitlementsAllowedIT(@Name("pathPrefix") String pathPrefix, @Name("actio
4646
public static Iterable<Object[]> data() {
4747
return Stream.of("allowed", "allowed_nonmodular")
4848
.flatMap(
49-
path -> RestEntitlementsCheckAction.getServerAndPluginsCheckActions().stream().map(action -> new Object[] { path, action })
49+
path -> RestEntitlementsCheckAction.getCheckActionsAllowedInPlugins().stream().map(action -> new Object[] { path, action })
5050
)
5151
.toList();
5252
}

libs/entitlement/src/main/java/org/elasticsearch/entitlement/initialization/EntitlementInitialization.java

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99

1010
package org.elasticsearch.entitlement.initialization;
1111

12+
import org.elasticsearch.core.Strings;
1213
import org.elasticsearch.core.internal.provider.ProviderLocator;
1314
import org.elasticsearch.entitlement.bootstrap.EntitlementBootstrap;
1415
import org.elasticsearch.entitlement.bridge.EntitlementChecker;
@@ -120,7 +121,15 @@ private static Policy loadPluginPolicy(Path pluginRoot, boolean isModular, Strin
120121
// TODO: should this check actually be part of the parser?
121122
for (Scope scope : policy.scopes) {
122123
if (moduleNames.contains(scope.name) == false) {
123-
throw new IllegalStateException("policy [" + policyFile + "] contains invalid module [" + scope.name + "]");
124+
throw new IllegalStateException(
125+
Strings.format(
126+
"Invalid module name in policy: plugin [%s] does not have module [%s]; available modules [%s]; policy file [%s]",
127+
pluginName,
128+
scope.name,
129+
String.join(", ", moduleNames),
130+
policyFile
131+
)
132+
);
124133
}
125134
}
126135
return policy;

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

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,11 @@
1616
import java.net.URLStreamHandlerFactory;
1717
import java.util.List;
1818

19+
import javax.net.ssl.HostnameVerifier;
20+
import javax.net.ssl.HttpsURLConnection;
21+
import javax.net.ssl.SSLContext;
22+
import javax.net.ssl.SSLSocketFactory;
23+
1924
/**
2025
* Implementation of the {@link EntitlementChecker} interface, providing additional
2126
* API methods for managing the checks.
@@ -78,4 +83,28 @@ public ElasticsearchEntitlementChecker(PolicyManager policyManager) {
7883
public void check$java_lang_ProcessBuilder$$startPipeline(Class<?> callerClass, List<ProcessBuilder> builders) {
7984
policyManager.checkStartProcess(callerClass);
8085
}
86+
87+
@Override
88+
public void check$javax_net_ssl_HttpsURLConnection$setSSLSocketFactory(
89+
Class<?> callerClass,
90+
HttpsURLConnection connection,
91+
SSLSocketFactory sf
92+
) {
93+
policyManager.checkSetHttpsConnectionProperties(callerClass);
94+
}
95+
96+
@Override
97+
public void check$javax_net_ssl_HttpsURLConnection$$setDefaultSSLSocketFactory(Class<?> callerClass, SSLSocketFactory sf) {
98+
policyManager.checkSetGlobalHttpsConnectionProperties(callerClass);
99+
}
100+
101+
@Override
102+
public void check$javax_net_ssl_HttpsURLConnection$$setDefaultHostnameVerifier(Class<?> callerClass, HostnameVerifier hv) {
103+
policyManager.checkSetGlobalHttpsConnectionProperties(callerClass);
104+
}
105+
106+
@Override
107+
public void check$javax_net_ssl_SSLContext$$setDefault(Class<?> callerClass, SSLContext context) {
108+
policyManager.checkSetGlobalHttpsConnectionProperties(callerClass);
109+
}
81110
}

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

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,14 @@ public void checkCreateClassLoader(Class<?> callerClass) {
130130
checkEntitlementPresent(callerClass, CreateClassLoaderEntitlement.class);
131131
}
132132

133+
public void checkSetHttpsConnectionProperties(Class<?> callerClass) {
134+
checkEntitlementPresent(callerClass, SetHttpsConnectionPropertiesEntitlement.class);
135+
}
136+
137+
public void checkSetGlobalHttpsConnectionProperties(Class<?> callerClass) {
138+
neverEntitled(callerClass, "set global https connection properties");
139+
}
140+
133141
private void checkEntitlementPresent(Class<?> callerClass, Class<? extends Entitlement> entitlementClass) {
134142
var requestingModule = requestingModule(callerClass);
135143
if (isTriviallyAllowed(requestingModule)) {

0 commit comments

Comments
 (0)