blob: 4f11c79c791f29cae76dcbd96e1cffa2934a6b06 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more contributor license
* agreements. See the NOTICE file distributed with this work for additional information regarding
* copyright ownership. The ASF licenses this file to You under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the License. You may obtain a
* copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
* or implied. See the License for the specific language governing permissions and limitations under
* the License.
*/
package org.apache.geode.internal.net;
import java.io.Console;
import java.io.FileInputStream;
import java.io.IOException;
import java.net.BindException;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketException;
import java.net.SocketTimeoutException;
import java.net.UnknownHostException;
import java.nio.ByteBuffer;
import java.nio.channels.SocketChannel;
import java.security.GeneralSecurityException;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.Principal;
import java.security.PrivateKey;
import java.security.UnrecoverableKeyException;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.concurrent.ConcurrentHashMap;
import javax.net.ssl.KeyManager;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SNIHostName;
import javax.net.ssl.SNIServerName;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLEngine;
import javax.net.ssl.SSLException;
import javax.net.ssl.SSLHandshakeException;
import javax.net.ssl.SSLParameters;
import javax.net.ssl.SSLPeerUnverifiedException;
import javax.net.ssl.SSLProtocolException;
import javax.net.ssl.SSLSocket;
import javax.net.ssl.StandardConstants;
import javax.net.ssl.TrustManager;
import javax.net.ssl.TrustManagerFactory;
import javax.net.ssl.X509ExtendedKeyManager;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.validator.routines.InetAddressValidator;
import org.apache.logging.log4j.Logger;
import org.apache.geode.GemFireConfigException;
import org.apache.geode.SystemFailure;
import org.apache.geode.annotations.VisibleForTesting;
import org.apache.geode.annotations.internal.DeprecatedButRequiredForBackwardsCompatibilityTesting;
import org.apache.geode.annotations.internal.MakeNotStatic;
import org.apache.geode.cache.wan.GatewaySender;
import org.apache.geode.cache.wan.GatewayTransportFilter;
import org.apache.geode.distributed.ClientSocketFactory;
import org.apache.geode.distributed.internal.DistributionConfig;
import org.apache.geode.distributed.internal.DistributionConfigImpl;
import org.apache.geode.distributed.internal.tcpserver.AdvancedSocketCreatorImpl;
import org.apache.geode.distributed.internal.tcpserver.HostAndPort;
import org.apache.geode.distributed.internal.tcpserver.TcpSocketCreatorImpl;
import org.apache.geode.internal.ClassPathLoader;
import org.apache.geode.internal.cache.wan.TransportFilterServerSocket;
import org.apache.geode.internal.cache.wan.TransportFilterSocketFactory;
import org.apache.geode.internal.inet.LocalHostUtil;
import org.apache.geode.internal.util.ArgumentRedactor;
import org.apache.geode.internal.util.PasswordUtil;
import org.apache.geode.logging.internal.log4j.api.LogService;
import org.apache.geode.net.SSLParameterExtension;
import org.apache.geode.util.internal.GeodeGlossary;
/**
* SocketCreators are built using a SocketCreatorFactory using Geode distributed-system properties.
* They know how to properly configure sockets for TLS (SSL) communications and perform
* handshakes. Connection-initiation uses a HostAndPort instance that is similar to an
* InetSocketAddress.
* <p>
* SocketCreator also supports a client-socket-factory that is designated with the property
* gemfire.clientSocketFactory for use in creating client->server connections.
*/
public class SocketCreator extends TcpSocketCreatorImpl {
private static final Logger logger = LogService.getLogger();
/**
* flag to force always using DNS (regardless of the fact that these lookups can hang)
*/
public static final boolean FORCE_DNS_USE =
Boolean.getBoolean(GeodeGlossary.GEMFIRE_PREFIX + "forceDnsUse");
/**
* set this to false to inhibit host name lookup
*/
@MakeNotStatic
public static volatile boolean resolve_dns = true;
/**
* set this to false to use an inet_addr in a client's ID
*/
@MakeNotStatic
public static volatile boolean use_client_host_name = true;
@MakeNotStatic
private static final ConcurrentHashMap<InetAddress, String> hostNames = new ConcurrentHashMap<>();
/**
* Only print this SocketCreator's config once
*/
private boolean configShown = false;
/**
* Only print hostname validation disabled log once
*/
private boolean hostnameValidationDisabledLogShown = false;
private SSLContext sslContext;
private final SSLConfig sslConfig;
private ClientSocketFactory clientSocketFactory;
/**
* Whether to enable TCP keep alive for sockets. This boolean is controlled by the
* gemfire.setTcpKeepAlive java system property. If not set then GemFire will enable keep-alive on
* server->client and p2p connections.
*/
public static final boolean ENABLE_TCP_KEEP_ALIVE =
AdvancedSocketCreatorImpl.ENABLE_TCP_KEEP_ALIVE;
// -------------------------------------------------------------------------
// Static instance accessors
// -------------------------------------------------------------------------
/**
* This method has migrated to LocalHostUtil but is kept in place here for
* backward-compatibility testing.
*
* @deprecated use {@link LocalHostUtil#getLocalHost()}
*/
@DeprecatedButRequiredForBackwardsCompatibilityTesting
@Deprecated
public static InetAddress getLocalHost() throws UnknownHostException {
return LocalHostUtil.getLocalHost();
}
/**
* returns the host name for the given inet address, using a local cache of names to avoid dns
* hits and duplicate strings
*/
public static String getHostName(InetAddress addr) {
String result = hostNames.get(addr);
if (result == null) {
result = addr.getHostName();
hostNames.put(addr, result);
}
return result;
}
/**
* Reset the hostNames caches
*/
public static void resetHostNameCache() {
hostNames.clear();
}
// -------------------------------------------------------------------------
// Constructor
// -------------------------------------------------------------------------
/**
* Constructs new SocketCreator instance.
*/
public SocketCreator(final SSLConfig sslConfig) {
this.sslConfig = sslConfig;
initialize();
}
@VisibleForTesting
SocketCreator(final SSLConfig sslConfig, SSLContext sslContext) {
this.sslConfig = sslConfig;
this.sslContext = sslContext;
}
/** returns the hostname or address for this client */
public static String getClientHostName() throws UnknownHostException {
InetAddress hostAddr = LocalHostUtil.getLocalHost();
return SocketCreator.use_client_host_name ? hostAddr.getCanonicalHostName()
: hostAddr.getHostAddress();
}
// -------------------------------------------------------------------------
// Initializers (change SocketCreator state)
// -------------------------------------------------------------------------
protected void initializeCreators() {
clusterSocketCreator = new SCClusterSocketCreator(this);
clientSocketCreator = new SCClientSocketCreator(this);
advancedSocketCreator = new SCAdvancedSocketCreator(this);
}
/**
* Initialize this SocketCreator.
* <p>
* Caller must synchronize on the SocketCreator instance.
*/
private void initialize() {
try {
try {
if (this.sslConfig.isEnabled() && getSslContext() == null) {
sslContext = createAndConfigureSSLContext();
}
} catch (Exception e) {
throw new GemFireConfigException("Error configuring GemFire ssl ", e);
}
// make sure TCPConduit picks up p2p properties...
org.apache.geode.internal.tcp.TCPConduit.init();
initializeClientSocketFactory();
} catch (VirtualMachineError err) {
SystemFailure.initiateFailure(err);
// If this ever returns, rethrow the error. We're poisoned
// now, so don't let this thread continue.
throw err;
} catch (Error t) {
// Whenever you catch Error or Throwable, you must also
// catch VirtualMachineError (see above). However, there is
// _still_ a possibility that you are dealing with a cascading
// error condition, so you also need to check to see if the JVM
// is still usable:
SystemFailure.checkFailure();
t.printStackTrace();
throw t;
} catch (RuntimeException re) {
re.printStackTrace();
throw re;
}
}
/**
* Creates & configures the SSLContext when SSL is enabled.
*
* @return new SSLContext configured using the given protocols & properties
*
* @throws GeneralSecurityException if security information can not be found
* @throws IOException if information can not be loaded
*/
private SSLContext createAndConfigureSSLContext() throws GeneralSecurityException, IOException {
if (sslConfig.useDefaultSSLContext()) {
return SSLContext.getDefault();
}
SSLContext newSSLContext = SSLUtil.getSSLContextInstance(sslConfig);
KeyManager[] keyManagers = getKeyManagers();
TrustManager[] trustManagers = getTrustManagers();
newSSLContext.init(keyManagers, trustManagers, null /* use the default secure random */);
return newSSLContext;
}
/**
* Used by SystemAdmin to read the properties from console
*
* @param env Map in which the properties are to be read from console.
*/
public static void readSSLProperties(Map<String, String> env) {
readSSLProperties(env, false);
}
/**
* Used to read the properties from console. AgentLauncher calls this method directly & ignores
* gemfire.properties. SystemAdmin calls this through {@link #readSSLProperties(Map)} and does
* NOT ignore gemfire.properties.
*
* @param env Map in which the properties are to be read from console.
* @param ignoreGemFirePropsFile if <code>false</code> existing gemfire.properties file is read,
* if <code>true</code>, properties from gemfire.properties file are ignored.
*/
public static void readSSLProperties(Map<String, String> env, boolean ignoreGemFirePropsFile) {
Properties props = new Properties();
DistributionConfigImpl.loadGemFireProperties(props, ignoreGemFirePropsFile);
for (Map.Entry<Object, Object> ent : props.entrySet()) {
String key = (String) ent.getKey();
// if the value of ssl props is empty, read them from console
if (key.startsWith(DistributionConfig.SSL_SYSTEM_PROPS_NAME)
|| key.startsWith(DistributionConfig.SYS_PROP_NAME)) {
if (key.startsWith(DistributionConfig.SYS_PROP_NAME)) {
key = key.substring(DistributionConfig.SYS_PROP_NAME.length());
}
final String value = (String) ent.getValue();
if (value == null || value.trim().equals("")) {
Console console = System.console();
if (console == null) {
throw new GemFireConfigException(
"SSL properties are empty, but a console is not available");
}
String val = console.readLine("Please enter " + key + ": ");
env.put(key, val);
}
}
}
}
private TrustManager[] getTrustManagers()
throws KeyStoreException, NoSuchAlgorithmException, CertificateException, IOException {
TrustManager[] trustManagers;
String trustStoreType = sslConfig.getTruststoreType();
if (StringUtils.isEmpty(trustStoreType)) {
trustStoreType = KeyStore.getDefaultType();
}
KeyStore ts = KeyStore.getInstance(trustStoreType);
String trustStorePath = sslConfig.getTruststore();
char[] password = null;
try (FileInputStream fis = new FileInputStream(trustStorePath)) {
String passwordString = sslConfig.getTruststorePassword();
if (passwordString != null) {
if (passwordString.trim().equals("")) {
if (!StringUtils.isEmpty(passwordString)) {
String toDecrypt = "encrypted(" + passwordString + ")";
passwordString = PasswordUtil.decrypt(toDecrypt);
password = passwordString.toCharArray();
}
} else {
password = passwordString.toCharArray();
}
}
ts.load(fis, password);
}
// default algorithm can be changed by setting property "ssl.TrustManagerFactory.algorithm" in
// security properties
TrustManagerFactory tmf =
TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
tmf.init(ts);
trustManagers = tmf.getTrustManagers();
// follow the security tip in java doc
if (password != null) {
java.util.Arrays.fill(password, ' ');
}
return trustManagers;
}
private KeyManager[] getKeyManagers() throws KeyStoreException, IOException,
NoSuchAlgorithmException, CertificateException, UnrecoverableKeyException {
if (sslConfig.getKeystore() == null) {
return null;
}
KeyManager[] keyManagers;
String keyStoreType = sslConfig.getKeystoreType();
if (StringUtils.isEmpty(keyStoreType)) {
keyStoreType = KeyStore.getDefaultType();
}
KeyStore keyStore = KeyStore.getInstance(keyStoreType);
String keyStoreFilePath = sslConfig.getKeystore();
if (StringUtils.isEmpty(keyStoreFilePath)) {
keyStoreFilePath =
System.getProperty("user.home") + System.getProperty("file.separator") + ".keystore";
}
char[] password = null;
try (FileInputStream fileInputStream = new FileInputStream(keyStoreFilePath)) {
String passwordString = sslConfig.getKeystorePassword();
if (passwordString != null) {
if (passwordString.trim().equals("")) {
String encryptedPass = System.getenv("javax.net.ssl.keyStorePassword");
if (!StringUtils.isEmpty(encryptedPass)) {
String toDecrypt = "encrypted(" + encryptedPass + ")";
passwordString = PasswordUtil.decrypt(toDecrypt);
password = passwordString.toCharArray();
}
} else {
password = passwordString.toCharArray();
}
}
keyStore.load(fileInputStream, password);
}
// default algorithm can be changed by setting property "ssl.KeyManagerFactory.algorithm" in
// security properties
KeyManagerFactory keyManagerFactory =
KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
keyManagerFactory.init(keyStore, password);
keyManagers = keyManagerFactory.getKeyManagers();
// follow the security tip in java doc
if (password != null) {
java.util.Arrays.fill(password, ' ');
}
KeyManager[] extendedKeyManagers = new KeyManager[keyManagers.length];
for (int i = 0; i < keyManagers.length; i++)
{
extendedKeyManagers[i] = new ExtendedAliasKeyManager(keyManagers[i], sslConfig.getAlias());
}
return extendedKeyManagers;
}
/**
* context for SSL socket factories
*/
@VisibleForTesting
public SSLContext getSslContext() {
return sslContext;
}
/**
* A factory used to create client <code>Sockets</code>.
*/
public ClientSocketFactory getClientSocketFactory() {
return clientSocketFactory;
}
public SSLConfig getSslConfig() {
return sslConfig;
}
/**
* ExtendedAliasKeyManager supports use of certificate aliases in distributed system
* properties.
*/
private static class ExtendedAliasKeyManager extends X509ExtendedKeyManager {
private final X509ExtendedKeyManager delegate;
private final String keyAlias;
/**
* Constructor.
*
* @param mgr The X509KeyManager used as a delegate
* @param keyAlias The alias name of the server's keypair and supporting certificate chain
*/
ExtendedAliasKeyManager(KeyManager mgr, String keyAlias) {
this.delegate = (X509ExtendedKeyManager) mgr;
this.keyAlias = keyAlias;
}
@Override
public String[] getClientAliases(final String s, final Principal[] principals) {
return delegate.getClientAliases(s, principals);
}
@Override
public String chooseClientAlias(final String[] strings, final Principal[] principals,
final Socket socket) {
if (!StringUtils.isEmpty(this.keyAlias)) {
return keyAlias;
}
return delegate.chooseClientAlias(strings, principals, socket);
}
@Override
public String[] getServerAliases(final String s, final Principal[] principals) {
return delegate.getServerAliases(s, principals);
}
@Override
public String chooseServerAlias(String keyType, Principal[] issuers, Socket socket) {
if (!StringUtils.isEmpty(this.keyAlias)) {
PrivateKey key = this.delegate.getPrivateKey(this.keyAlias);
return getKeyAlias(keyType, key);
}
return this.delegate.chooseServerAlias(keyType, issuers, socket);
}
@Override
public X509Certificate[] getCertificateChain(final String s) {
if (!StringUtils.isEmpty(this.keyAlias)) {
return delegate.getCertificateChain(keyAlias);
}
return delegate.getCertificateChain(s);
}
@Override
public PrivateKey getPrivateKey(final String alias) {
return delegate.getPrivateKey(alias);
}
@Override
public String chooseEngineClientAlias(String[] keyTypes, Principal[] principals,
SSLEngine sslEngine) {
return delegate.chooseEngineClientAlias(keyTypes, principals, sslEngine);
}
@Override
public String chooseEngineServerAlias(final String keyType, final Principal[] principals,
final SSLEngine sslEngine) {
if (!StringUtils.isEmpty(this.keyAlias)) {
PrivateKey key = this.delegate.getPrivateKey(this.keyAlias);
return getKeyAlias(keyType, key);
}
return this.delegate.chooseEngineServerAlias(keyType, principals, sslEngine);
}
private String getKeyAlias(final String keyType, final PrivateKey key) {
if (key != null) {
if (key.getAlgorithm().equals(keyType)) {
return this.keyAlias;
} else {
return null;
}
} else {
return null;
}
}
}
/**
* Returns true if this SocketCreator is configured to use SSL.
*/
@Override
protected boolean useSSL() {
return this.sslConfig.isEnabled();
}
// -------------------------------------------------------------------------
// Public methods
// -------------------------------------------------------------------------
/**
* Returns an SSLEngine that can be used to perform TLS handshakes and communication
*/
public SSLEngine createSSLEngine(String hostName, int port, boolean clientSocket) {
SSLEngine engine = getSslContext().createSSLEngine(hostName, port);
configureSSLEngine(engine, hostName, port, clientSocket);
return engine;
}
@VisibleForTesting
void configureSSLEngine(SSLEngine engine, String hostName, int port, boolean clientSocket) {
SSLParameters parameters = engine.getSSLParameters();
boolean updateEngineWithParameters = false;
if (sslConfig.doEndpointIdentification()) {
// set server-names so that endpoint identification algorithms can find what's expected
if (setServerNames(parameters, new HostAndPort(hostName, port))) {
updateEngineWithParameters = true;
}
}
engine.setUseClientMode(clientSocket);
if (!clientSocket) {
engine.setNeedClientAuth(sslConfig.isRequireAuth());
}
if (clientSocket) {
if (checkAndEnableHostnameValidation(parameters)) {
updateEngineWithParameters = true;
}
}
String[] protocols = this.sslConfig.getProtocolsAsStringArray();
if (protocols != null && !"any".equalsIgnoreCase(protocols[0])) {
engine.setEnabledProtocols(protocols);
}
String[] ciphers = this.sslConfig.getCiphersAsStringArray();
if (ciphers != null && !"any".equalsIgnoreCase(ciphers[0])) {
engine.setEnabledCipherSuites(ciphers);
}
if (updateEngineWithParameters) {
engine.setSSLParameters(parameters);
}
}
/**
* @see <a
* href=https://docs.oracle.com/javase/8/docs/technotes/guides/security/jsse/JSSERefGuide.html#SSLENG">JSSE
* Reference Guide</a>
*
* @param socketChannel the socket's NIO channel
* @param engine the sslEngine (see createSSLEngine)
* @param timeout handshake timeout in milliseconds. No timeout if <= 0
* @param clientSocket set to true if you initiated the connect(), false if you accepted it
* @param peerNetBuffer the buffer to use in reading data fron socketChannel. This should also be
* used in subsequent I/O operations
* @return The SSLEngine to be used in processing data for sending/receiving from the channel
*/
public NioSslEngine handshakeSSLSocketChannel(SocketChannel socketChannel, SSLEngine engine,
int timeout,
boolean clientSocket,
ByteBuffer peerNetBuffer,
BufferPool bufferPool)
throws IOException {
while (!socketChannel.finishConnect()) {
try {
Thread.sleep(50);
} catch (InterruptedException e) {
if (!socketChannel.socket().isClosed()) {
socketChannel.close();
}
throw new IOException("Interrupted while performing handshake", e);
}
}
NioSslEngine nioSslEngine = new NioSslEngine(engine, bufferPool);
boolean blocking = socketChannel.isBlocking();
if (blocking) {
socketChannel.configureBlocking(false);
}
try {
nioSslEngine.handshake(socketChannel, timeout, peerNetBuffer);
} catch (SSLException e) {
if (!socketChannel.socket().isClosed()) {
socketChannel.close();
}
logger.warn("SSL handshake exception", e);
throw e;
} catch (InterruptedException e) {
if (!socketChannel.socket().isClosed()) {
socketChannel.close();
}
throw new IOException("SSL handshake interrupted");
} finally {
if (blocking) {
try {
socketChannel.configureBlocking(true);
} catch (IOException ignored) {
// problem setting the socket back to blocking mode but the socket's going to be closed
}
}
}
return nioSslEngine;
}
/**
* @return true if the parameters have been modified by this method
*/
private boolean checkAndEnableHostnameValidation(SSLParameters sslParameters) {
if (sslConfig.doEndpointIdentification()) {
sslParameters.setEndpointIdentificationAlgorithm("HTTPS");
return true;
}
if (!hostnameValidationDisabledLogShown) {
logger.info("Your SSL configuration disables hostname validation. "
+ "ssl-endpoint-identification-enabled should be set to true when SSL is enabled. "
+ "Please refer to the Apache GEODE SSL Documentation for SSL Property: ssl‑endpoint‑identification‑enabled");
hostnameValidationDisabledLogShown = true;
}
return false;
}
/**
* Use this method to perform the SSL handshake on a newly accepted socket. Non-SSL
* sockets are ignored by this method.
*
* @param timeout the number of milliseconds allowed for the handshake to complete
*/
void handshakeIfSocketIsSSL(Socket socket, int timeout) throws IOException {
if (!(socket instanceof SSLSocket)) {
return;
}
int oldTimeout = socket.getSoTimeout();
socket.setSoTimeout(timeout);
SSLSocket sslSocket = (SSLSocket) socket;
try {
sslSocket.startHandshake();
} catch (SSLPeerUnverifiedException ex) {
if (this.sslConfig.isRequireAuth()) {
logger.fatal(String.format("SSL Error in authenticating peer %s[%s].",
socket.getInetAddress(), socket.getPort()), ex);
throw ex;
}
}
// Pre jkd11, startHandshake is throwing SocketTimeoutException.
// in jdk 11 it is throwing SSLProtocolException with a cause of SocketTimeoutException.
// this is to keep the exception consistent across jdk
catch (SSLProtocolException ex) {
if (ex.getCause() instanceof SocketTimeoutException) {
throw (SocketTimeoutException) ex.getCause();
} else {
throw ex;
}
} finally {
try {
socket.setSoTimeout(oldTimeout);
} catch (SocketException ignored) {
}
}
}
/**
* Create a server socket with the given transport filters.<br>
* Note: This method is outside of the
* client/server/advanced interfaces because it references WAN classes that aren't
* available to them.
*/
public ServerSocket createServerSocket(int nport, int backlog, InetAddress bindAddr,
List<GatewayTransportFilter> transportFilters, int socketBufferSize) throws IOException {
if (transportFilters.isEmpty()) {
return ((SCClusterSocketCreator) forCluster())
.createServerSocket(nport, backlog, bindAddr, socketBufferSize, useSSL());
} else {
printConfig();
ServerSocket result = new TransportFilterServerSocket(transportFilters);
result.setReuseAddress(true);
// Set the receive buffer size before binding the socket so
// that large buffers will be allocated on accepted sockets (see
// java.net.ServerSocket.setReceiverBufferSize javadocs)
result.setReceiveBufferSize(socketBufferSize);
try {
result.bind(new InetSocketAddress(bindAddr, nport), backlog);
} catch (BindException e) {
BindException throwMe = new BindException(
String.format("Failed to create server socket on %s[%s]", bindAddr, nport));
throwMe.initCause(e);
throw throwMe;
}
return result;
}
}
// -------------------------------------------------------------------------
// Private implementation methods
// -------------------------------------------------------------------------
/**
* When a socket is connected to a server socket, it should be passed to this method for SSL
* configuration.
*/
void configureClientSSLSocket(Socket socket, HostAndPort addr, int timeout) throws IOException {
if (socket instanceof SSLSocket) {
SSLSocket sslSocket = (SSLSocket) socket;
sslSocket.setUseClientMode(true);
sslSocket.setEnableSessionCreation(true);
SSLParameters parameters = sslSocket.getSSLParameters();
boolean updateSSLParameters =
checkAndEnableHostnameValidation(parameters);
if (setServerNames(parameters, addr)) {
updateSSLParameters = true;
} ;
SSLParameterExtension sslParameterExtension = this.sslConfig.getSSLParameterExtension();
if (sslParameterExtension != null) {
parameters =
sslParameterExtension.modifySSLClientSocketParameters(parameters);
updateSSLParameters = true;
}
if (updateSSLParameters) {
sslSocket.setSSLParameters(parameters);
}
String[] protocols = this.sslConfig.getProtocolsAsStringArray();
// restrict cyphers
if (protocols != null && !"any".equalsIgnoreCase(protocols[0])) {
sslSocket.setEnabledProtocols(protocols);
}
String[] ciphers = this.sslConfig.getCiphersAsStringArray();
if (ciphers != null && !"any".equalsIgnoreCase(ciphers[0])) {
sslSocket.setEnabledCipherSuites(ciphers);
}
try {
if (timeout > 0) {
sslSocket.setSoTimeout(timeout);
}
sslSocket.startHandshake();
}
// Pre jkd11, startHandshake is throwing SocketTimeoutException.
// in jdk 11 it is throwing SSLProtocolException with a cause of SocketTimeoutException.
// this is to keep the exception consistent across jdk
catch (SSLProtocolException ex) {
if (ex.getCause() instanceof SocketTimeoutException) {
throw (SocketTimeoutException) ex.getCause();
} else {
throw ex;
}
} catch (SSLHandshakeException ex) {
logger.fatal(String.format("Problem forming SSL connection to %s[%s].",
socket.getInetAddress(), socket.getPort()), ex);
throw ex;
} catch (SSLPeerUnverifiedException ex) {
if (this.sslConfig.isRequireAuth()) {
logger.fatal("SSL authentication exception.", ex);
throw ex;
}
}
}
}
/**
* returns true if the SSLParameters are altered, false if not
*/
private boolean setServerNames(SSLParameters modifiedParams, HostAndPort addr) {
List<SNIServerName> oldNames = modifiedParams.getServerNames();
oldNames = oldNames == null ? Collections.emptyList() : oldNames;
final List<SNIServerName> serverNames = new ArrayList<>(oldNames);
if (serverNames.stream()
.mapToInt(SNIServerName::getType)
.anyMatch(type -> type == StandardConstants.SNI_HOST_NAME)) {
// we already have a SNI hostname set. Do nothing.
return false;
}
String hostName = addr.getHostName();
if (this.sslConfig.doEndpointIdentification()
&& InetAddressValidator.getInstance().isValid(hostName)) {
// endpoint validation typically uses a hostname in the sniServer parameter that the handshake
// will compare against the subject alternative addresses in the server's certificate. Here
// we attempt to get a hostname instead of the proffered numeric address
try {
hostName = InetAddress.getByName(hostName).getCanonicalHostName();
} catch (UnknownHostException e) {
// ignore - we'll see what happens with endpoint validation using a numeric address...
}
}
serverNames.add(new SNIHostName(hostName));
modifiedParams.setServerNames(serverNames);
return true;
}
/**
* Print current configured state to log.
*/
void printConfig() {
if (!configShown && logger.isDebugEnabled()) {
configShown = true;
StringBuilder sb = new StringBuilder();
sb.append("SSL Configuration: \n");
sb.append(" ssl-enabled = ").append(this.sslConfig.isEnabled()).append("\n");
// add other options here....
for (String key : System.getProperties().stringPropertyNames()) { // fix for 46822
if (key.startsWith("javax.net.ssl")) {
String possiblyRedactedValue =
ArgumentRedactor.redactArgumentIfNecessary(key, System.getProperty(key));
sb.append(" ").append(key).append(" = ").append(possiblyRedactedValue).append("\n");
}
}
logger.debug(sb.toString());
}
}
protected void initializeClientSocketFactory() {
this.clientSocketFactory = null;
String className =
System.getProperty(GeodeGlossary.GEMFIRE_PREFIX + "clientSocketFactory");
if (className != null) {
Object o;
try {
Class c = ClassPathLoader.getLatest().forName(className);
o = c.newInstance();
} catch (Exception e) {
// No cache exists yet, so this can't be logged.
String s = "An unexpected exception occurred while instantiating a " + className + ": " + e;
throw new IllegalArgumentException(s);
}
if (o instanceof ClientSocketFactory) {
this.clientSocketFactory = (ClientSocketFactory) o;
} else {
String s = "Class \"" + className + "\" is not a ClientSocketFactory";
throw new IllegalArgumentException(s);
}
}
}
public void initializeTransportFilterClientSocketFactory(GatewaySender sender) {
this.clientSocketFactory = new TransportFilterSocketFactory()
.setGatewayTransportFilters(sender.getGatewayTransportFilters());
}
}