You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@calcite.apache.org by el...@apache.org on 2017/07/27 16:26:05 UTC
calcite-avatica git commit: [CALCITE-1904] Allow SSL hostname
verification to be turned off
Repository: calcite-avatica
Updated Branches:
refs/heads/master 4d0f397a7 -> c3f4611d4
[CALCITE-1904] Allow SSL hostname verification to be turned off
Closes #12
Project: http://git-wip-us.apache.org/repos/asf/calcite-avatica/repo
Commit: http://git-wip-us.apache.org/repos/asf/calcite-avatica/commit/c3f4611d
Tree: http://git-wip-us.apache.org/repos/asf/calcite-avatica/tree/c3f4611d
Diff: http://git-wip-us.apache.org/repos/asf/calcite-avatica/diff/c3f4611d
Branch: refs/heads/master
Commit: c3f4611d4e94f50ed2130801c7054e3d109bde4b
Parents: 4d0f397
Author: Josh Elser <el...@apache.org>
Authored: Thu Jul 27 12:19:05 2017 -0400
Committer: Josh Elser <el...@apache.org>
Committed: Thu Jul 27 12:19:05 2017 -0400
----------------------------------------------------------------------
.../avatica/BuiltInConnectionProperty.java | 6 ++-
.../calcite/avatica/ConnectionConfig.java | 3 ++
.../calcite/avatica/ConnectionConfigImpl.java | 6 +++
.../remote/AvaticaCommonsHttpClientImpl.java | 39 +++++++++++++++-
.../remote/AvaticaHttpClientFactoryImpl.java | 9 ++++
.../HostnameVerificationConfigurable.java | 47 ++++++++++++++++++++
.../AvaticaCommonsHttpClientImplTest.java | 28 ++++++++++++
7 files changed, 135 insertions(+), 3 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/calcite-avatica/blob/c3f4611d/core/src/main/java/org/apache/calcite/avatica/BuiltInConnectionProperty.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/avatica/BuiltInConnectionProperty.java b/core/src/main/java/org/apache/calcite/avatica/BuiltInConnectionProperty.java
index d68bae8..1da7025 100644
--- a/core/src/main/java/org/apache/calcite/avatica/BuiltInConnectionProperty.java
+++ b/core/src/main/java/org/apache/calcite/avatica/BuiltInConnectionProperty.java
@@ -17,6 +17,7 @@
package org.apache.calcite.avatica;
import org.apache.calcite.avatica.remote.AvaticaHttpClientFactoryImpl;
+import org.apache.calcite.avatica.remote.HostnameVerificationConfigurable.HostnameVerification;
import java.util.HashMap;
import java.util.HashSet;
@@ -73,7 +74,10 @@ public enum BuiltInConnectionProperty implements ConnectionProperty {
TRUSTSTORE("truststore", Type.STRING, null, false),
/** Password for the truststore */
- TRUSTSTORE_PASSWORD("truststore_password", Type.STRING, null, false);
+ TRUSTSTORE_PASSWORD("truststore_password", Type.STRING, null, false),
+
+ HOSTNAME_VERIFICATION("hostname_verification", Type.ENUM, HostnameVerification.STRICT,
+ HostnameVerification.class, false);
private final String camelName;
private final Type type;
http://git-wip-us.apache.org/repos/asf/calcite-avatica/blob/c3f4611d/core/src/main/java/org/apache/calcite/avatica/ConnectionConfig.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/avatica/ConnectionConfig.java b/core/src/main/java/org/apache/calcite/avatica/ConnectionConfig.java
index e8ff0be..cd18ec5 100644
--- a/core/src/main/java/org/apache/calcite/avatica/ConnectionConfig.java
+++ b/core/src/main/java/org/apache/calcite/avatica/ConnectionConfig.java
@@ -17,6 +17,7 @@
package org.apache.calcite.avatica;
import org.apache.calcite.avatica.remote.AvaticaHttpClientFactory;
+import org.apache.calcite.avatica.remote.HostnameVerificationConfigurable.HostnameVerification;
import org.apache.calcite.avatica.remote.Service;
import java.io.File;
@@ -53,6 +54,8 @@ public interface ConnectionConfig {
File truststore();
/** @see BuiltInConnectionProperty#TRUSTSTORE_PASSWORD */
String truststorePassword();
+ /** @see BuiltInConnectionProperty#HOSTNAME_VERIFICATION */
+ HostnameVerification hostnameVerification();
}
// End ConnectionConfig.java
http://git-wip-us.apache.org/repos/asf/calcite-avatica/blob/c3f4611d/core/src/main/java/org/apache/calcite/avatica/ConnectionConfigImpl.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/avatica/ConnectionConfigImpl.java b/core/src/main/java/org/apache/calcite/avatica/ConnectionConfigImpl.java
index a003b82..63fbd8d 100644
--- a/core/src/main/java/org/apache/calcite/avatica/ConnectionConfigImpl.java
+++ b/core/src/main/java/org/apache/calcite/avatica/ConnectionConfigImpl.java
@@ -17,6 +17,7 @@
package org.apache.calcite.avatica;
import org.apache.calcite.avatica.remote.AvaticaHttpClientFactory;
+import org.apache.calcite.avatica.remote.HostnameVerificationConfigurable.HostnameVerification;
import org.apache.calcite.avatica.remote.Service;
import java.io.File;
@@ -105,6 +106,11 @@ public class ConnectionConfigImpl implements ConnectionConfig {
return BuiltInConnectionProperty.TRUSTSTORE_PASSWORD.wrap(properties).getString();
}
+ public HostnameVerification hostnameVerification() {
+ return BuiltInConnectionProperty.HOSTNAME_VERIFICATION.wrap(properties)
+ .getEnum(HostnameVerification.class);
+ }
+
/** Converts a {@link Properties} object containing (name, value)
* pairs into a map whose keys are
* {@link org.apache.calcite.avatica.InternalProperty} objects.
http://git-wip-us.apache.org/repos/asf/calcite-avatica/blob/c3f4611d/core/src/main/java/org/apache/calcite/avatica/remote/AvaticaCommonsHttpClientImpl.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/avatica/remote/AvaticaCommonsHttpClientImpl.java b/core/src/main/java/org/apache/calcite/avatica/remote/AvaticaCommonsHttpClientImpl.java
index 33872d0..5576847 100644
--- a/core/src/main/java/org/apache/calcite/avatica/remote/AvaticaCommonsHttpClientImpl.java
+++ b/core/src/main/java/org/apache/calcite/avatica/remote/AvaticaCommonsHttpClientImpl.java
@@ -31,6 +31,7 @@ import org.apache.http.config.Lookup;
import org.apache.http.config.RegistryBuilder;
import org.apache.http.conn.socket.ConnectionSocketFactory;
import org.apache.http.conn.socket.PlainConnectionSocketFactory;
+import org.apache.http.conn.ssl.NoopHostnameVerifier;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.entity.ByteArrayEntity;
import org.apache.http.entity.ContentType;
@@ -55,6 +56,7 @@ import java.net.URISyntaxException;
import java.net.URL;
import java.util.Objects;
+import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.SSLContext;
/**
@@ -62,7 +64,7 @@ import javax.net.ssl.SSLContext;
* sent and received across the wire.
*/
public class AvaticaCommonsHttpClientImpl implements AvaticaHttpClient,
- UsernamePasswordAuthenticateable, TrustStoreConfigurable {
+ UsernamePasswordAuthenticateable, TrustStoreConfigurable, HostnameVerificationConfigurable {
private static final Logger LOG = LoggerFactory.getLogger(AvaticaCommonsHttpClientImpl.class);
// Some basic exposed configurations
@@ -84,6 +86,7 @@ public class AvaticaCommonsHttpClientImpl implements AvaticaHttpClient,
protected File truststore = null;
protected String truststorePassword = null;
+ protected HostnameVerification hostnameVerification = null;
public AvaticaCommonsHttpClientImpl(URL url) {
this.host = new HttpHost(url.getHost(), url.getPort(), url.getProtocol());
@@ -97,7 +100,10 @@ public class AvaticaCommonsHttpClientImpl implements AvaticaHttpClient,
try {
SSLContext sslcontext = SSLContexts.custom().loadTrustMaterial(
truststore, truststorePassword.toCharArray()).build();
- sslFactory = new SSLConnectionSocketFactory(sslcontext);
+
+ final HostnameVerifier verifier = getHostnameVerifier(hostnameVerification);
+
+ sslFactory = new SSLConnectionSocketFactory(sslcontext, verifier);
} catch (Exception e) {
throw new RuntimeException(e);
}
@@ -128,6 +134,30 @@ public class AvaticaCommonsHttpClientImpl implements AvaticaHttpClient,
this.client = HttpClients.custom().setConnectionManager(pool).build();
}
+ /**
+ * Creates the {@code HostnameVerifier} given the provided {@code verification}.
+ *
+ * @param verification The intended hostname verification action.
+ * @return A verifier for the request verification.
+ * @throws IllegalArgumentException if the provided verification cannot be handled.
+ */
+ HostnameVerifier getHostnameVerifier(HostnameVerification verification) {
+ // Normally, the configuration logic would give us a default of STRICT if it was not
+ // provided by the user. It's easy for us to do a double-check.
+ if (verification == null) {
+ verification = HostnameVerification.STRICT;
+ }
+ switch (verification) {
+ case STRICT:
+ return SSLConnectionSocketFactory.getDefaultHostnameVerifier();
+ case NONE:
+ return NoopHostnameVerifier.INSTANCE;
+ default:
+ throw new IllegalArgumentException("Unhandled HostnameVerification: "
+ + hostnameVerification);
+ }
+ }
+
public byte[] send(byte[] request) {
while (true) {
HttpClientContext context = HttpClientContext.create();
@@ -216,6 +246,11 @@ public class AvaticaCommonsHttpClientImpl implements AvaticaHttpClient,
this.truststorePassword = Objects.requireNonNull(password);
initializeClient();
}
+
+ @Override public void setHostnameVerification(HostnameVerification verification) {
+ this.hostnameVerification = Objects.requireNonNull(verification);
+ initializeClient();
+ }
}
// End AvaticaCommonsHttpClientImpl.java
http://git-wip-us.apache.org/repos/asf/calcite-avatica/blob/c3f4611d/core/src/main/java/org/apache/calcite/avatica/remote/AvaticaHttpClientFactoryImpl.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/avatica/remote/AvaticaHttpClientFactoryImpl.java b/core/src/main/java/org/apache/calcite/avatica/remote/AvaticaHttpClientFactoryImpl.java
index 8778c3d..f352ac1 100644
--- a/core/src/main/java/org/apache/calcite/avatica/remote/AvaticaHttpClientFactoryImpl.java
+++ b/core/src/main/java/org/apache/calcite/avatica/remote/AvaticaHttpClientFactoryImpl.java
@@ -80,6 +80,15 @@ public class AvaticaHttpClientFactoryImpl implements AvaticaHttpClientFactory {
LOG.debug("{} is not capable of SSL/TLS communication", client.getClass().getName());
}
+ // Set the SSL hostname verification if the client supports it
+ if (client instanceof HostnameVerificationConfigurable) {
+ ((HostnameVerificationConfigurable) client)
+ .setHostnameVerification(config.hostnameVerification());
+ } else {
+ LOG.debug("{} is not capable of configurable SSL/TLS hostname verification",
+ client.getClass().getName());
+ }
+
if (client instanceof UsernamePasswordAuthenticateable) {
// Shortcircuit quickly if authentication wasn't provided (implies NONE)
final String authString = config.authentication();
http://git-wip-us.apache.org/repos/asf/calcite-avatica/blob/c3f4611d/core/src/main/java/org/apache/calcite/avatica/remote/HostnameVerificationConfigurable.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/avatica/remote/HostnameVerificationConfigurable.java b/core/src/main/java/org/apache/calcite/avatica/remote/HostnameVerificationConfigurable.java
new file mode 100644
index 0000000..4f6d111
--- /dev/null
+++ b/core/src/main/java/org/apache/calcite/avatica/remote/HostnameVerificationConfigurable.java
@@ -0,0 +1,47 @@
+/*
+ * 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.calcite.avatica.remote;
+
+/**
+ * An interface to decorate an {@link AvaticaHttpClient} that can support configuration on
+ * SSL hostname verification.
+ */
+public interface HostnameVerificationConfigurable {
+ /**
+ * Describes the support hostname verification methods of {@link AvaticaHttpClient}.
+ */
+ public enum HostnameVerification {
+ /**
+ * The common name (CN) on the certificate must match the server's hostname.
+ */
+ STRICT,
+ /**
+ * No verification is performed.
+ */
+ NONE,
+ }
+
+ /**
+ * Instructs the {@link AvaticaHttpClient} how to perform hostname verification for SSL
+ * connections.
+ *
+ * @param verification The mode of hostname verification
+ */
+ void setHostnameVerification(HostnameVerification verification);
+}
+
+// End HostnameVerificationConfigurable.java
http://git-wip-us.apache.org/repos/asf/calcite-avatica/blob/c3f4611d/core/src/test/java/org/apache/calcite/avatica/remote/AvaticaCommonsHttpClientImplTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/calcite/avatica/remote/AvaticaCommonsHttpClientImplTest.java b/core/src/test/java/org/apache/calcite/avatica/remote/AvaticaCommonsHttpClientImplTest.java
index 990ecd2..9918143 100644
--- a/core/src/test/java/org/apache/calcite/avatica/remote/AvaticaCommonsHttpClientImplTest.java
+++ b/core/src/test/java/org/apache/calcite/avatica/remote/AvaticaCommonsHttpClientImplTest.java
@@ -16,11 +16,15 @@
*/
package org.apache.calcite.avatica.remote;
+import org.apache.calcite.avatica.remote.HostnameVerificationConfigurable.HostnameVerification;
+
import org.apache.http.NoHttpResponseException;
import org.apache.http.StatusLine;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.protocol.HttpClientContext;
+import org.apache.http.conn.ssl.DefaultHostnameVerifier;
+import org.apache.http.conn.ssl.NoopHostnameVerifier;
import org.apache.http.entity.StringEntity;
import org.junit.Test;
@@ -28,10 +32,14 @@ import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;
import java.net.HttpURLConnection;
+import javax.net.ssl.HostnameVerifier;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.nullable;
import static org.mockito.Mockito.when;
import static java.nio.charset.StandardCharsets.UTF_8;
@@ -110,6 +118,26 @@ public class AvaticaCommonsHttpClientImplTest {
byte[] responseBytes = client.send(requestBytes);
assertEquals("success", new String(responseBytes, UTF_8));
}
+
+ @Test public void testHostnameVerification() throws Exception {
+ AvaticaCommonsHttpClientImpl client = mock(AvaticaCommonsHttpClientImpl.class);
+ // Call the real method
+ when(client.getHostnameVerifier(nullable(HostnameVerification.class)))
+ .thenCallRealMethod();
+
+ // No verification should give the default (strict) verifier
+ HostnameVerifier actualVerifier = client.getHostnameVerifier(null);
+ assertNotNull(actualVerifier);
+ assertTrue(actualVerifier instanceof DefaultHostnameVerifier);
+
+ actualVerifier = client.getHostnameVerifier(HostnameVerification.STRICT);
+ assertNotNull(actualVerifier);
+ assertTrue(actualVerifier instanceof DefaultHostnameVerifier);
+
+ actualVerifier = client.getHostnameVerifier(HostnameVerification.NONE);
+ assertNotNull(actualVerifier);
+ assertTrue(actualVerifier instanceof NoopHostnameVerifier);
+ }
}
// End AvaticaCommonsHttpClientImplTest.java