From 5f84ddee7c2c77742c0980599a01f22078c423a4 Mon Sep 17 00:00:00 2001 From: Tim Brooks Date: Thu, 26 Jan 2017 13:18:36 -0600 Subject: [PATCH] Add doPrivileged blocks for socket connect ops (elastic/elasticsearch#4701) This is related to elastic/elasticsearch#22116. X-pack opens socket connections for a number of pieces of functionality (Active Directory support, ssl, email notification, etc). As SocketPermissions are transitioned out of core, x-pack will require connect permission. This pull request wraps operations requiring these permissions in doPrivileged blocks. Original commit: elastic/x-pack-elasticsearch@1bfee97550aa3b411f595c6f7d62a766dc2c41ef --- .../xpack/common/http/HttpClient.java | 7 +-- .../xpack/common/socket/SocketAccess.java | 49 +++++++++++++++++++ .../xpack/notification/email/Account.java | 39 +++++++++------ .../esnative/ESNativeRealmMigrateTool.java | 20 ++++---- .../ldap/ActiveDirectorySessionFactory.java | 3 +- .../authc/ldap/LdapSessionFactory.java | 3 +- .../ldap/LdapUserSearchSessionFactory.java | 12 +++-- .../authc/ldap/support/LdapUtils.java | 16 +++++- .../elasticsearch/xpack/ssl/SSLService.java | 18 ++++--- .../plugin-metadata/plugin-security.policy | 2 +- .../SessionFactoryLoadBalancingTests.java | 8 +-- .../authc/pki/PkiAuthenticationTests.java | 6 +-- .../filter/IpFilteringIntegrationTests.java | 4 +- .../transport/ssl/SslIntegrationTests.java | 8 +-- 14 files changed, 141 insertions(+), 54 deletions(-) create mode 100644 elasticsearch/src/main/java/org/elasticsearch/xpack/common/socket/SocketAccess.java diff --git a/elasticsearch/src/main/java/org/elasticsearch/xpack/common/http/HttpClient.java b/elasticsearch/src/main/java/org/elasticsearch/xpack/common/http/HttpClient.java index 020ff7a6493..e3351e50ccf 100644 --- a/elasticsearch/src/main/java/org/elasticsearch/xpack/common/http/HttpClient.java +++ b/elasticsearch/src/main/java/org/elasticsearch/xpack/common/http/HttpClient.java @@ -37,6 +37,7 @@ import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.unit.TimeValue; import org.elasticsearch.xpack.common.http.auth.ApplicableHttpAuth; import org.elasticsearch.xpack.common.http.auth.HttpAuthRegistry; +import org.elasticsearch.xpack.common.socket.SocketAccess; import org.elasticsearch.xpack.ssl.SSLService; import javax.net.ssl.HostnameVerifier; @@ -156,7 +157,7 @@ public class HttpClient extends AbstractComponent { internalRequest.setConfig(config.build()); - try (CloseableHttpResponse response = client.execute(internalRequest, localContext)) { + try (CloseableHttpResponse response = SocketAccess.doPrivileged(() -> client.execute(internalRequest, localContext))) { // headers Header[] headers = response.getAllHeaders(); Map responseHeaders = new HashMap<>(headers.length); @@ -166,7 +167,7 @@ public class HttpClient extends AbstractComponent { String[] values = new String[old.length + 1]; System.arraycopy(old, 0, values, 0, old.length); - values[values.length-1] = header.getValue(); + values[values.length - 1] = header.getValue(); responseHeaders.put(header.getName(), values); } else { @@ -195,7 +196,7 @@ public class HttpClient extends AbstractComponent { // url path escaping, and we have done this already, so this would result in double escaping try { List qparams = new ArrayList<>(request.params.size()); - request.params.forEach((k, v)-> qparams.add(new BasicNameValuePair(k, v))); + request.params.forEach((k, v) -> qparams.add(new BasicNameValuePair(k, v))); URI uri = URIUtils.createURI(request.scheme.scheme(), request.host, request.port, request.path, URLEncodedUtils.format(qparams, "UTF-8"), null); diff --git a/elasticsearch/src/main/java/org/elasticsearch/xpack/common/socket/SocketAccess.java b/elasticsearch/src/main/java/org/elasticsearch/xpack/common/socket/SocketAccess.java new file mode 100644 index 00000000000..b637b862cca --- /dev/null +++ b/elasticsearch/src/main/java/org/elasticsearch/xpack/common/socket/SocketAccess.java @@ -0,0 +1,49 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +package org.elasticsearch.xpack.common.socket; + +import org.elasticsearch.SpecialPermission; +import org.elasticsearch.common.CheckedRunnable; +import org.elasticsearch.common.CheckedSupplier; + +import java.io.IOException; +import java.net.SocketPermission; +import java.security.AccessController; +import java.security.PrivilegedAction; +import java.security.PrivilegedActionException; +import java.security.PrivilegedExceptionAction; + +/** + * X-pack uses various libraries that establish socket connections. For these remote calls the plugin requires + * {@link SocketPermission} 'connect' to establish connections. This class wraps the operations requiring access in + * {@link AccessController#doPrivileged(PrivilegedAction)} blocks. + */ +public final class SocketAccess { + + private SocketAccess() { + } + + public static R doPrivileged(CheckedSupplier supplier) throws IOException { + SpecialPermission.check(); + try { + return AccessController.doPrivileged((PrivilegedExceptionAction) supplier::get); + } catch (PrivilegedActionException e) { + throw (IOException) e.getCause(); + } + } + + public static void doPrivileged(CheckedRunnable action) throws IOException { + SpecialPermission.check(); + try { + AccessController.doPrivileged((PrivilegedExceptionAction) () -> { + action.run(); + return null; + }); + } catch (PrivilegedActionException e) { + throw (IOException) e.getCause(); + } + } +} diff --git a/elasticsearch/src/main/java/org/elasticsearch/xpack/notification/email/Account.java b/elasticsearch/src/main/java/org/elasticsearch/xpack/notification/email/Account.java index dffd261a29b..3111f74af22 100644 --- a/elasticsearch/src/main/java/org/elasticsearch/xpack/notification/email/Account.java +++ b/elasticsearch/src/main/java/org/elasticsearch/xpack/notification/email/Account.java @@ -21,6 +21,8 @@ import javax.mail.internet.InternetAddress; import javax.mail.internet.MimeMessage; import java.security.AccessController; import java.security.PrivilegedAction; +import java.security.PrivilegedActionException; +import java.security.PrivilegedExceptionAction; import java.util.Map; import java.util.Properties; @@ -33,19 +35,16 @@ public class Account { if (sm != null) { sm.checkPermission(new SpecialPermission()); } - AccessController.doPrivileged(new PrivilegedAction() { - @Override - public Void run() { - // required as java doesn't always find the correct mailcap to properly handle mime types - final MailcapCommandMap mailcap = (MailcapCommandMap) CommandMap.getDefaultCommandMap(); - mailcap.addMailcap("text/html;; x-java-content-handler=com.sun.mail.handlers.text_html"); - mailcap.addMailcap("text/xml;; x-java-content-handler=com.sun.mail.handlers.text_xml"); - mailcap.addMailcap("text/plain;; x-java-content-handler=com.sun.mail.handlers.text_plain"); - mailcap.addMailcap("multipart/*;; x-java-content-handler=com.sun.mail.handlers.multipart_mixed"); - mailcap.addMailcap("message/rfc822;; x-java-content-handler=com.sun.mail.handlers.message_rfc822"); - CommandMap.setDefaultCommandMap(mailcap); - return null; - } + AccessController.doPrivileged((PrivilegedAction) () -> { + // required as java doesn't always find the correct mailcap to properly handle mime types + final MailcapCommandMap mailcap = (MailcapCommandMap) CommandMap.getDefaultCommandMap(); + mailcap.addMailcap("text/html;; x-java-content-handler=com.sun.mail.handlers.text_html"); + mailcap.addMailcap("text/xml;; x-java-content-handler=com.sun.mail.handlers.text_xml"); + mailcap.addMailcap("text/plain;; x-java-content-handler=com.sun.mail.handlers.text_plain"); + mailcap.addMailcap("multipart/*;; x-java-content-handler=com.sun.mail.handlers.multipart_mixed"); + mailcap.addMailcap("message/rfc822;; x-java-content-handler=com.sun.mail.handlers.message_rfc822"); + CommandMap.setDefaultCommandMap(mailcap); + return null; }); } @@ -110,7 +109,7 @@ public class Account { profile = config.profile; } - transport.connect(config.smtp.host, config.smtp.port, user, password); + executeConnect(transport, user, password); ClassLoader contextClassLoader = null; try { MimeMessage message = profile.toMimeMessage(email, session); @@ -148,6 +147,18 @@ public class Account { return email; } + private void executeConnect(Transport transport, String user, String password) throws MessagingException { + SpecialPermission.check(); + try { + AccessController.doPrivileged((PrivilegedExceptionAction) () -> { + transport.connect(config.smtp.host, config.smtp.port, user, password); + return null; + }); + } catch (PrivilegedActionException e) { + throw (MessagingException) e.getCause(); + } + } + private void setContextClassLoader(final ClassLoader classLoader) { SecurityManager sm = System.getSecurityManager(); if (sm != null) { diff --git a/elasticsearch/src/main/java/org/elasticsearch/xpack/security/authc/esnative/ESNativeRealmMigrateTool.java b/elasticsearch/src/main/java/org/elasticsearch/xpack/security/authc/esnative/ESNativeRealmMigrateTool.java index 76425396af0..570320f2cfa 100644 --- a/elasticsearch/src/main/java/org/elasticsearch/xpack/security/authc/esnative/ESNativeRealmMigrateTool.java +++ b/elasticsearch/src/main/java/org/elasticsearch/xpack/security/authc/esnative/ESNativeRealmMigrateTool.java @@ -34,6 +34,7 @@ import org.elasticsearch.common.xcontent.XContentBuilder; import org.elasticsearch.common.xcontent.XContentParser; import org.elasticsearch.common.xcontent.json.JsonXContent; import org.elasticsearch.env.Environment; +import org.elasticsearch.xpack.common.socket.SocketAccess; import org.elasticsearch.xpack.security.authc.file.FileUserPasswdStore; import org.elasticsearch.xpack.security.authc.file.FileUserRolesStore; import org.elasticsearch.xpack.security.authc.support.SecuredString; @@ -81,7 +82,9 @@ public class ESNativeRealmMigrateTool extends MultiCommand { subcommands.put("native", new MigrateUserOrRoles()); } - /** Command to migrate users and roles to the native realm */ + /** + * Command to migrate users and roles to the native realm + */ public static class MigrateUserOrRoles extends EnvironmentAwareCommand { private final OptionSpec username; @@ -140,13 +143,10 @@ public class ESNativeRealmMigrateTool extends MultiCommand { Settings sslSettings = settings.getByPrefix(setting("http.ssl.")); final SSLService sslService = new SSLService(settings, env); final HttpsURLConnection httpsConn = (HttpsURLConnection) url.openConnection(); - AccessController.doPrivileged(new PrivilegedAction() { - @Override - public Void run() { - // Requires permission java.lang.RuntimePermission "setFactory"; - httpsConn.setSSLSocketFactory(sslService.sslSocketFactory(sslSettings)); - return null; - } + AccessController.doPrivileged((PrivilegedAction) () -> { + // Requires permission java.lang.RuntimePermission "setFactory"; + httpsConn.setSSLSocketFactory(sslService.sslSocketFactory(sslSettings)); + return null; }); conn = httpsConn; } else { @@ -159,7 +159,7 @@ public class ESNativeRealmMigrateTool extends MultiCommand { UsernamePasswordToken.basicAuthHeaderValue(username.value(options), new SecuredString(password.value(options).toCharArray()))); conn.setDoOutput(true); // we'll be sending a body - conn.connect(); + SocketAccess.doPrivileged(conn::connect); if (bodyString != null) { try (OutputStream out = conn.getOutputStream()) { out.write(bodyString.getBytes(StandardCharsets.UTF_8)); @@ -333,7 +333,7 @@ public class ESNativeRealmMigrateTool extends MultiCommand { terminal.println("migrating role [" + roleName + "]"); String reqBody = "n/a"; try { - reqBody = createRoleJson(roles.get(roleName));; + reqBody = createRoleJson(roles.get(roleName)); String resp = postURL(env.settings(), env, "POST", this.url.value(options) + "/_xpack/security/role/" + roleName, options, reqBody); terminal.println(resp); diff --git a/elasticsearch/src/main/java/org/elasticsearch/xpack/security/authc/ldap/ActiveDirectorySessionFactory.java b/elasticsearch/src/main/java/org/elasticsearch/xpack/security/authc/ldap/ActiveDirectorySessionFactory.java index 8e49363bf56..cbdf4dff603 100644 --- a/elasticsearch/src/main/java/org/elasticsearch/xpack/security/authc/ldap/ActiveDirectorySessionFactory.java +++ b/elasticsearch/src/main/java/org/elasticsearch/xpack/security/authc/ldap/ActiveDirectorySessionFactory.java @@ -23,6 +23,7 @@ import org.elasticsearch.xpack.security.authc.RealmConfig; import org.elasticsearch.xpack.security.authc.ldap.support.LdapSearchScope; import org.elasticsearch.xpack.security.authc.ldap.support.LdapSession; import org.elasticsearch.xpack.security.authc.ldap.support.LdapSession.GroupsResolver; +import org.elasticsearch.xpack.security.authc.ldap.support.LdapUtils; import org.elasticsearch.xpack.security.authc.ldap.support.SessionFactory; import org.elasticsearch.xpack.security.authc.support.SecuredString; import org.elasticsearch.xpack.ssl.SSLService; @@ -89,7 +90,7 @@ class ActiveDirectorySessionFactory extends SessionFactory { // authenticate. If there was a failure pass it back using the listener Runnable runnable; try { - final LDAPConnection connection = serverSet.getConnection(); + final LDAPConnection connection = LdapUtils.privilegedConnect(serverSet::getConnection); runnable = () -> getADAuthenticator(username).authenticate(connection, username, password, ActionListener.wrap(listener::onResponse, (e) -> { diff --git a/elasticsearch/src/main/java/org/elasticsearch/xpack/security/authc/ldap/LdapSessionFactory.java b/elasticsearch/src/main/java/org/elasticsearch/xpack/security/authc/ldap/LdapSessionFactory.java index 32cf32c4e77..8868590afdd 100644 --- a/elasticsearch/src/main/java/org/elasticsearch/xpack/security/authc/ldap/LdapSessionFactory.java +++ b/elasticsearch/src/main/java/org/elasticsearch/xpack/security/authc/ldap/LdapSessionFactory.java @@ -19,6 +19,7 @@ import org.elasticsearch.xpack.security.authc.RealmConfig; import org.elasticsearch.xpack.security.authc.RealmSettings; import org.elasticsearch.xpack.security.authc.ldap.support.LdapSession; import org.elasticsearch.xpack.security.authc.ldap.support.LdapSession.GroupsResolver; +import org.elasticsearch.xpack.security.authc.ldap.support.LdapUtils; import org.elasticsearch.xpack.security.authc.ldap.support.SessionFactory; import org.elasticsearch.xpack.security.authc.support.CharArrays; import org.elasticsearch.xpack.security.authc.support.SecuredString; @@ -74,7 +75,7 @@ public class LdapSessionFactory extends SessionFactory { final byte[] passwordBytes = CharArrays.toUtf8Bytes(password.internalChars()); boolean success = false; try { - connection = serverSet.getConnection(); + connection = LdapUtils.privilegedConnect(serverSet::getConnection); for (String template : userDnTemplates) { String dn = buildDnFromTemplate(username, template); try { diff --git a/elasticsearch/src/main/java/org/elasticsearch/xpack/security/authc/ldap/LdapUserSearchSessionFactory.java b/elasticsearch/src/main/java/org/elasticsearch/xpack/security/authc/ldap/LdapUserSearchSessionFactory.java index e8ccb404843..2d43600f318 100644 --- a/elasticsearch/src/main/java/org/elasticsearch/xpack/security/authc/ldap/LdapUserSearchSessionFactory.java +++ b/elasticsearch/src/main/java/org/elasticsearch/xpack/security/authc/ldap/LdapUserSearchSessionFactory.java @@ -16,6 +16,7 @@ import com.unboundid.ldap.sdk.ServerSet; import com.unboundid.ldap.sdk.SimpleBindRequest; import org.apache.logging.log4j.Logger; import org.apache.lucene.util.IOUtils; +import org.elasticsearch.SpecialPermission; import org.elasticsearch.action.ActionListener; import org.elasticsearch.common.settings.Setting; import org.elasticsearch.common.settings.Settings; @@ -25,6 +26,7 @@ import org.elasticsearch.xpack.security.authc.RealmSettings; import org.elasticsearch.xpack.security.authc.ldap.support.LdapSearchScope; import org.elasticsearch.xpack.security.authc.ldap.support.LdapSession; import org.elasticsearch.xpack.security.authc.ldap.support.LdapSession.GroupsResolver; +import org.elasticsearch.xpack.security.authc.ldap.support.LdapUtils; import org.elasticsearch.xpack.security.authc.ldap.support.SessionFactory; import org.elasticsearch.xpack.security.authc.support.CharArrays; import org.elasticsearch.xpack.security.authc.support.SecuredString; @@ -109,7 +111,7 @@ class LdapUserSearchSessionFactory extends SessionFactory { LDAPConnectionPool pool = null; boolean success = false; try { - pool = new LDAPConnectionPool(serverSet, bindRequest, initialSize, size); + pool = LdapUtils.privilegedConnect(() -> new LDAPConnectionPool(serverSet, bindRequest, initialSize, size)); pool.setRetryFailedOperationsDueToInvalidConnections(true); if (HEALTH_CHECK_ENABLED.get(settings)) { String entryDn = HEALTH_CHECK_DN.get(settings).orElseGet(() -> bindRequest == null ? null : bindRequest.getBindDN()); @@ -170,7 +172,7 @@ class LdapUserSearchSessionFactory extends SessionFactory { } else { final String dn = entry.getDN(); try { - connectionPool.bindAndRevertAuthentication(dn, new String(password.internalChars())); + LdapUtils.privilegedConnect(() -> connectionPool.bindAndRevertAuthentication(dn, new String(password.internalChars()))); listener.onResponse(new LdapSession(logger, connectionPool, dn, groupResolver, timeout, entry.getAttributes())); } catch (LDAPException e) { listener.onFailure(e); @@ -195,7 +197,7 @@ class LdapUserSearchSessionFactory extends SessionFactory { boolean success = false; LDAPConnection connection = null; try { - connection = serverSet.getConnection(); + connection = LdapUtils.privilegedConnect(serverSet::getConnection); connection.bind(bindRequest(config.settings())); final LDAPConnection finalConnection = connection; findUser(user, connection, ActionListener.wrap((entry) -> { @@ -210,7 +212,7 @@ class LdapUserSearchSessionFactory extends SessionFactory { LDAPConnection userConnection = null; final byte[] passwordBytes = CharArrays.toUtf8Bytes(password.internalChars()); try { - userConnection = serverSet.getConnection(); + userConnection = LdapUtils.privilegedConnect(serverSet::getConnection); userConnection.bind(new SimpleBindRequest(dn, passwordBytes)); LdapSession session = new LdapSession(logger, userConnection, dn, groupResolver, timeout, entry.getAttributes()); @@ -255,7 +257,7 @@ class LdapUserSearchSessionFactory extends SessionFactory { if (useConnectionPool) { ldapInterface = connectionPool; } else { - connection = serverSet.getConnection(); + connection = LdapUtils.privilegedConnect(serverSet::getConnection); connection.bind(bindRequest(config.settings())); ldapInterface = connection; } diff --git a/elasticsearch/src/main/java/org/elasticsearch/xpack/security/authc/ldap/support/LdapUtils.java b/elasticsearch/src/main/java/org/elasticsearch/xpack/security/authc/ldap/support/LdapUtils.java index ae8d9562356..555d476eaaf 100644 --- a/elasticsearch/src/main/java/org/elasticsearch/xpack/security/authc/ldap/support/LdapUtils.java +++ b/elasticsearch/src/main/java/org/elasticsearch/xpack/security/authc/ldap/support/LdapUtils.java @@ -26,13 +26,18 @@ import org.apache.logging.log4j.message.ParameterizedMessage; import org.apache.logging.log4j.util.Supplier; import org.apache.lucene.util.IOUtils; import org.apache.lucene.util.SetOnce; +import org.elasticsearch.SpecialPermission; import org.elasticsearch.action.ActionListener; +import org.elasticsearch.common.CheckedSupplier; import org.elasticsearch.common.Strings; import org.elasticsearch.common.logging.ESLoggerFactory; import org.elasticsearch.common.util.concurrent.CountDown; import org.elasticsearch.xpack.security.support.Exceptions; import javax.naming.ldap.Rdn; +import java.security.AccessController; +import java.security.PrivilegedActionException; +import java.security.PrivilegedExceptionAction; import java.text.MessageFormat; import java.util.ArrayList; import java.util.Arrays; @@ -57,6 +62,15 @@ public final class LdapUtils { } } + public static T privilegedConnect(CheckedSupplier supplier) throws LDAPException { + SpecialPermission.check(); + try { + return AccessController.doPrivileged((PrivilegedExceptionAction) supplier::get); + } catch (PrivilegedActionException e) { + throw (LDAPException) e.getCause(); + } + } + public static String relativeName(DN dn) { return dn.getRDNString().split("=")[1].trim(); } @@ -106,7 +120,7 @@ public final class LdapUtils { boolean searching = false; LDAPConnection ldapConnection = null; try { - ldapConnection = ldap.getConnection(); + ldapConnection = privilegedConnect(ldap::getConnection); final LDAPConnection finalConnection = ldapConnection; searchForEntry(finalConnection, baseDN, scope, filter, timeLimitSeconds, ActionListener.wrap( (entry) -> { diff --git a/elasticsearch/src/main/java/org/elasticsearch/xpack/ssl/SSLService.java b/elasticsearch/src/main/java/org/elasticsearch/xpack/ssl/SSLService.java index 76f60e04f4e..3c041bf7898 100644 --- a/elasticsearch/src/main/java/org/elasticsearch/xpack/ssl/SSLService.java +++ b/elasticsearch/src/main/java/org/elasticsearch/xpack/ssl/SSLService.java @@ -8,12 +8,14 @@ package org.elasticsearch.xpack.ssl; import org.apache.http.conn.ssl.NoopHostnameVerifier; import org.apache.http.nio.conn.ssl.SSLIOSessionStrategy; import org.elasticsearch.ElasticsearchException; +import org.elasticsearch.common.CheckedSupplier; import org.elasticsearch.common.Strings; import org.elasticsearch.common.component.AbstractComponent; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.env.Environment; import org.elasticsearch.transport.TransportSettings; import org.elasticsearch.xpack.XPackSettings; +import org.elasticsearch.xpack.common.socket.SocketAccess; import org.elasticsearch.xpack.security.Security; import javax.net.ssl.HostnameVerifier; @@ -443,42 +445,42 @@ public class SSLService extends AbstractComponent { @Override public Socket createSocket() throws IOException { - SSLSocket sslSocket = (SSLSocket) delegate.createSocket(); + SSLSocket sslSocket = createWithPermissions(delegate::createSocket); configureSSLSocket(sslSocket); return sslSocket; } @Override public Socket createSocket(Socket socket, String host, int port, boolean autoClose) throws IOException { - SSLSocket sslSocket = (SSLSocket) delegate.createSocket(socket, host, port, autoClose); + SSLSocket sslSocket = createWithPermissions(() -> delegate.createSocket(socket, host, port, autoClose)); configureSSLSocket(sslSocket); return sslSocket; } @Override public Socket createSocket(String host, int port) throws IOException { - SSLSocket sslSocket = (SSLSocket) delegate.createSocket(host, port); + SSLSocket sslSocket = createWithPermissions(() -> delegate.createSocket(host, port)); configureSSLSocket(sslSocket); return sslSocket; } @Override public Socket createSocket(String host, int port, InetAddress localHost, int localPort) throws IOException { - SSLSocket sslSocket = (SSLSocket) delegate.createSocket(host, port, localHost, localPort); + SSLSocket sslSocket = createWithPermissions(() -> delegate.createSocket(host, port, localHost, localPort)); configureSSLSocket(sslSocket); return sslSocket; } @Override public Socket createSocket(InetAddress host, int port) throws IOException { - SSLSocket sslSocket = (SSLSocket) delegate.createSocket(host, port); + SSLSocket sslSocket = createWithPermissions(() -> delegate.createSocket(host, port)); configureSSLSocket(sslSocket); return sslSocket; } @Override public Socket createSocket(InetAddress address, int port, InetAddress localAddress, int localPort) throws IOException { - SSLSocket sslSocket = (SSLSocket) delegate.createSocket(address, port, localAddress, localPort); + SSLSocket sslSocket = createWithPermissions(() -> delegate.createSocket(address, port, localAddress, localPort)); configureSSLSocket(sslSocket); return sslSocket; } @@ -489,6 +491,10 @@ public class SSLService extends AbstractComponent { parameters.setUseCipherSuitesOrder(true); socket.setSSLParameters(parameters); } + + private static SSLSocket createWithPermissions(CheckedSupplier supplier) throws IOException { + return (SSLSocket) SocketAccess.doPrivileged(supplier); + } } /** diff --git a/elasticsearch/src/main/plugin-metadata/plugin-security.policy b/elasticsearch/src/main/plugin-metadata/plugin-security.policy index 9f06db90a7a..5e0da18f30f 100644 --- a/elasticsearch/src/main/plugin-metadata/plugin-security.policy +++ b/elasticsearch/src/main/plugin-metadata/plugin-security.policy @@ -26,5 +26,5 @@ grant { permission java.util.PropertyPermission "sun.nio.ch.bugLevel", "write"; // needed for multiple server implementations used in tests - permission java.net.SocketPermission "*", "accept"; + permission java.net.SocketPermission "*", "accept,connect"; }; diff --git a/elasticsearch/src/test/java/org/elasticsearch/xpack/security/authc/ldap/support/SessionFactoryLoadBalancingTests.java b/elasticsearch/src/test/java/org/elasticsearch/xpack/security/authc/ldap/support/SessionFactoryLoadBalancingTests.java index c223045320e..9a31ef34b92 100644 --- a/elasticsearch/src/test/java/org/elasticsearch/xpack/security/authc/ldap/support/SessionFactoryLoadBalancingTests.java +++ b/elasticsearch/src/test/java/org/elasticsearch/xpack/security/authc/ldap/support/SessionFactoryLoadBalancingTests.java @@ -35,7 +35,7 @@ public class SessionFactoryLoadBalancingTests extends LdapTestCase { for (int i = 0; i < numberOfLdapServers; i++) { LDAPConnection connection = null; try { - connection = testSessionFactory.getServerSet().getConnection(); + connection = LdapUtils.privilegedConnect(testSessionFactory.getServerSet()::getConnection); assertThat(connection.getConnectedPort(), is(ldapServers[i].getListenPort())); } finally { if (connection != null) { @@ -81,7 +81,7 @@ public class SessionFactoryLoadBalancingTests extends LdapTestCase { LDAPConnection connection = null; try { logger.debug("attempting connection with expected port [{}]", port); - connection = testSessionFactory.getServerSet().getConnection(); + connection = LdapUtils.privilegedConnect(testSessionFactory.getServerSet()::getConnection); assertThat(connection.getConnectedPort(), is(port)); } finally { if (connection != null) { @@ -102,7 +102,7 @@ public class SessionFactoryLoadBalancingTests extends LdapTestCase { for (int i = 0; i < numberOfLdapServers; i++) { LDAPConnection connection = null; try { - connection = testSessionFactory.getServerSet().getConnection(); + connection = LdapUtils.privilegedConnect(testSessionFactory.getServerSet()::getConnection); assertThat(connection.getConnectedPort(), is(firstPort)); } finally { if (connection != null) { @@ -151,7 +151,7 @@ public class SessionFactoryLoadBalancingTests extends LdapTestCase { LDAPConnection connection = null; try { logger.debug("attempting connection with expected port [{}] iteration [{}]", firstNonStoppedPort, iteration); - connection = testSessionFactory.getServerSet().getConnection(); + connection = LdapUtils.privilegedConnect(testSessionFactory.getServerSet()::getConnection); assertThat(connection.getConnectedPort(), is(firstNonStoppedPort)); } finally { if (connection != null) { diff --git a/elasticsearch/src/test/java/org/elasticsearch/xpack/security/authc/pki/PkiAuthenticationTests.java b/elasticsearch/src/test/java/org/elasticsearch/xpack/security/authc/pki/PkiAuthenticationTests.java index efa52b6dd02..896e0a48295 100644 --- a/elasticsearch/src/test/java/org/elasticsearch/xpack/security/authc/pki/PkiAuthenticationTests.java +++ b/elasticsearch/src/test/java/org/elasticsearch/xpack/security/authc/pki/PkiAuthenticationTests.java @@ -18,6 +18,7 @@ import org.elasticsearch.common.network.NetworkModule; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.transport.TransportAddress; import org.elasticsearch.http.HttpServerTransport; +import org.elasticsearch.xpack.common.socket.SocketAccess; import org.elasticsearch.xpack.security.Security; import org.elasticsearch.xpack.security.authc.file.FileRealm; import org.elasticsearch.xpack.ssl.SSLClientAuth; @@ -39,7 +40,6 @@ import java.util.Map.Entry; import static org.elasticsearch.test.SecuritySettingsSource.getSSLSettingsForStore; import static org.hamcrest.Matchers.containsString; -import static org.hamcrest.Matchers.instanceOf; import static org.hamcrest.Matchers.is; /** @@ -100,7 +100,7 @@ public class PkiAuthenticationTests extends SecurityIntegTestCase { SSLContext context = getRestSSLContext("/org/elasticsearch/xpack/security/transport/ssl/certs/simple/testnode.jks", "testnode"); try (CloseableHttpClient client = HttpClients.custom().setSSLContext(context).build()) { HttpPut put = new HttpPut(getNodeUrl() + "foo"); - try (CloseableHttpResponse response = client.execute(put)) { + try (CloseableHttpResponse response = SocketAccess.doPrivileged(() -> client.execute(put))) { String body = EntityUtils.toString(response.getEntity()); assertThat(body, containsString("\"acknowledged\":true")); } @@ -111,7 +111,7 @@ public class PkiAuthenticationTests extends SecurityIntegTestCase { SSLContext context = getRestSSLContext("/org/elasticsearch/xpack/security/transport/ssl/certs/simple/testclient.jks", "testclient"); try (CloseableHttpClient client = HttpClients.custom().setSSLContext(context).build()) { HttpPut put = new HttpPut(getNodeUrl() + "foo"); - try (CloseableHttpResponse response = client.execute(put)) { + try (CloseableHttpResponse response = SocketAccess.doPrivileged(() -> client.execute(put))) { assertThat(response.getStatusLine().getStatusCode(), is(401)); String body = EntityUtils.toString(response.getEntity()); assertThat(body, containsString("unable to authenticate user [Elasticsearch Test Client]")); diff --git a/elasticsearch/src/test/java/org/elasticsearch/xpack/security/transport/filter/IpFilteringIntegrationTests.java b/elasticsearch/src/test/java/org/elasticsearch/xpack/security/transport/filter/IpFilteringIntegrationTests.java index 95f3a980d91..89ce5887600 100644 --- a/elasticsearch/src/test/java/org/elasticsearch/xpack/security/transport/filter/IpFilteringIntegrationTests.java +++ b/elasticsearch/src/test/java/org/elasticsearch/xpack/security/transport/filter/IpFilteringIntegrationTests.java @@ -14,6 +14,7 @@ import org.elasticsearch.test.ESIntegTestCase.ClusterScope; import org.elasticsearch.test.ESIntegTestCase.Scope; import org.elasticsearch.test.SecurityIntegTestCase; import org.elasticsearch.transport.Transport; +import org.elasticsearch.xpack.common.socket.SocketAccess; import org.junit.BeforeClass; import java.io.IOException; @@ -23,7 +24,6 @@ import java.net.InetSocketAddress; import java.net.Socket; import java.nio.charset.StandardCharsets; -import static org.hamcrest.Matchers.instanceOf; import static org.hamcrest.Matchers.is; // no client nodes, no transport clients, as they all get rejected on network connections @@ -72,7 +72,7 @@ public class IpFilteringIntegrationTests extends SecurityIntegTestCase { private void trySocketConnection(Socket socket, InetSocketAddress address) throws IOException { logger.info("connecting to {}", address); - socket.connect(address, 500); + SocketAccess.doPrivileged(() -> socket.connect(address, 500)); assertThat(socket.isConnected(), is(true)); try (OutputStream os = socket.getOutputStream()) { diff --git a/elasticsearch/src/test/java/org/elasticsearch/xpack/security/transport/ssl/SslIntegrationTests.java b/elasticsearch/src/test/java/org/elasticsearch/xpack/security/transport/ssl/SslIntegrationTests.java index 1cd2912d1b5..5a611bb6380 100644 --- a/elasticsearch/src/test/java/org/elasticsearch/xpack/security/transport/ssl/SslIntegrationTests.java +++ b/elasticsearch/src/test/java/org/elasticsearch/xpack/security/transport/ssl/SslIntegrationTests.java @@ -10,6 +10,7 @@ import org.apache.http.auth.UsernamePasswordCredentials; import org.apache.http.client.CredentialsProvider; import org.apache.http.client.methods.CloseableHttpResponse; import org.apache.http.client.methods.HttpGet; +import org.apache.http.client.methods.HttpUriRequest; import org.apache.http.conn.ssl.NoopHostnameVerifier; import org.apache.http.conn.ssl.SSLConnectionSocketFactory; import org.apache.http.impl.client.BasicCredentialsProvider; @@ -22,6 +23,7 @@ import org.elasticsearch.common.network.NetworkModule; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.transport.TransportAddress; import org.elasticsearch.http.HttpServerTransport; +import org.elasticsearch.xpack.common.socket.SocketAccess; import org.elasticsearch.xpack.ssl.SSLService; import org.elasticsearch.test.SecurityIntegTestCase; import org.elasticsearch.transport.Transport; @@ -75,7 +77,7 @@ public class SslIntegrationTests extends SecurityIntegTestCase { // no SSL exception as this is the exception is returned when connecting public void testThatTransportClientUsingSSLv3ProtocolIsRejected() { - try(TransportClient transportClient = new TestXPackTransportClient(Settings.builder() + try (TransportClient transportClient = new TestXPackTransportClient(Settings.builder() .put(transportClientSettings()) .put("node.name", "programmatic_transport_client") .put("cluster.name", internalCluster().getClusterName()) @@ -105,7 +107,7 @@ public class SslIntegrationTests extends SecurityIntegTestCase { .setSSLSocketFactory(new SSLConnectionSocketFactory(service.sslSocketFactory(Settings.EMPTY), SSLConnectionSocketFactory.getDefaultHostnameVerifier())) .setDefaultCredentialsProvider(provider).build(); - CloseableHttpResponse response = client.execute(new HttpGet(getNodeUrl()))) { + CloseableHttpResponse response = SocketAccess.doPrivileged(() -> client.execute(new HttpGet(getNodeUrl())))) { assertThat(response.getStatusLine().getStatusCode(), is(200)); String data = Streams.copyToString(new InputStreamReader(response.getEntity().getContent(), StandardCharsets.UTF_8)); assertThat(data, containsString("You Know, for Search")); @@ -121,7 +123,7 @@ public class SslIntegrationTests extends SecurityIntegTestCase { SSLConnectionSocketFactory sf = new SSLConnectionSocketFactory(sslContext, new String[]{ "SSLv3" }, null, NoopHostnameVerifier.INSTANCE); try (CloseableHttpClient client = HttpClients.custom().setSSLSocketFactory(sf).build()) { - client.execute(new HttpGet(getNodeUrl())); + CloseableHttpResponse result = SocketAccess.doPrivileged(() -> client.execute(new HttpGet(getNodeUrl()))); fail("Expected a connection error due to SSLv3 not being supported by default"); } catch (Exception e) { assertThat(e, is(instanceOf(SSLHandshakeException.class)));