bbeaudreault commented on code in PR #4724:
URL: https://github.com/apache/hbase/pull/4724#discussion_r974474811


##########
hbase-common/src/main/java/org/apache/hadoop/hbase/io/crypto/tls/HBaseTrustManager.java:
##########
@@ -0,0 +1,160 @@
+/*
+ * 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.hadoop.hbase.io.crypto.tls;
+
+import java.net.InetAddress;
+import java.net.Socket;
+import java.net.UnknownHostException;
+import java.security.cert.CertificateException;
+import java.security.cert.X509Certificate;
+import javax.net.ssl.SSLEngine;
+import javax.net.ssl.SSLException;
+import javax.net.ssl.X509ExtendedTrustManager;
+import org.apache.yetus.audience.InterfaceAudience;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * A custom TrustManager that supports hostname verification We attempt to 
perform verification
+ * using just the IP address first and if that fails will attempt to perform a 
reverse DNS lookup
+ * and verify using the hostname. This file has been copied from the Apache 
ZooKeeper project.
+ * @see <a href=
+ *      
"https://github.com/apache/zookeeper/blob/c74658d398cdc1d207aa296cb6e20de00faec03e/zookeeper-server/src/main/java/org/apache/zookeeper/common/ZKTrustManager.java";>Base
+ *      revision</a>
+ */
[email protected]
+public class HBaseTrustManager extends X509ExtendedTrustManager {
+
+  private static final Logger LOG = 
LoggerFactory.getLogger(HBaseTrustManager.class);
+
+  private final X509ExtendedTrustManager x509ExtendedTrustManager;
+  private final boolean serverHostnameVerificationEnabled;
+  private final boolean clientHostnameVerificationEnabled;
+
+  private final HBaseHostnameVerifier hostnameVerifier;
+
+  /**
+   * Instantiate a new HBaseTrustManager.
+   * @param x509ExtendedTrustManager          The trustmanager to use for
+   *                                          
checkClientTrusted/checkServerTrusted logic
+   * @param serverHostnameVerificationEnabled If true, this TrustManager 
should verify hostnames of
+   *                                          servers that this instance 
connects to.
+   * @param clientHostnameVerificationEnabled If true, the hostname of a 
client connecting to this
+   *                                          machine will be verified.
+   */
+  HBaseTrustManager(X509ExtendedTrustManager x509ExtendedTrustManager,
+    boolean serverHostnameVerificationEnabled, boolean 
clientHostnameVerificationEnabled) {
+    this.x509ExtendedTrustManager = x509ExtendedTrustManager;
+    this.serverHostnameVerificationEnabled = serverHostnameVerificationEnabled;
+    this.clientHostnameVerificationEnabled = clientHostnameVerificationEnabled;
+    this.hostnameVerifier = new HBaseHostnameVerifier();
+  }
+
+  @Override
+  public X509Certificate[] getAcceptedIssuers() {
+    return x509ExtendedTrustManager.getAcceptedIssuers();
+  }
+
+  @Override
+  public void checkClientTrusted(X509Certificate[] chain, String authType, 
Socket socket)
+    throws CertificateException {
+    x509ExtendedTrustManager.checkClientTrusted(chain, authType, socket);
+    if (clientHostnameVerificationEnabled) {
+      performHostVerification(socket.getInetAddress(), chain[0]);
+    }
+  }
+
+  @Override
+  public void checkServerTrusted(X509Certificate[] chain, String authType, 
Socket socket)
+    throws CertificateException {
+    x509ExtendedTrustManager.checkServerTrusted(chain, authType, socket);
+    if (serverHostnameVerificationEnabled) {
+      performHostVerification(socket.getInetAddress(), chain[0]);
+    }
+  }
+
+  @Override
+  public void checkClientTrusted(X509Certificate[] chain, String authType, 
SSLEngine engine)
+    throws CertificateException {
+    x509ExtendedTrustManager.checkClientTrusted(chain, authType, engine);
+    if (clientHostnameVerificationEnabled) {
+      try {
+        performHostVerification(InetAddress.getByName(engine.getPeerHost()), 
chain[0]);
+      } catch (UnknownHostException e) {
+        throw new CertificateException("Failed to verify host", e);
+      }
+    }
+  }
+
+  @Override
+  public void checkServerTrusted(X509Certificate[] chain, String authType, 
SSLEngine engine)
+    throws CertificateException {
+    x509ExtendedTrustManager.checkServerTrusted(chain, authType, engine);
+    if (serverHostnameVerificationEnabled) {
+      try {
+        performHostVerification(InetAddress.getByName(engine.getPeerHost()), 
chain[0]);
+      } catch (UnknownHostException e) {
+        throw new CertificateException("Failed to verify host", e);
+      }
+    }
+  }
+
+  @Override
+  public void checkClientTrusted(X509Certificate[] chain, String authType)
+    throws CertificateException {
+    x509ExtendedTrustManager.checkClientTrusted(chain, authType);
+  }
+
+  @Override
+  public void checkServerTrusted(X509Certificate[] chain, String authType)
+    throws CertificateException {
+    x509ExtendedTrustManager.checkServerTrusted(chain, authType);
+  }
+
+  /**
+   * Compares peer's hostname with the one stored in the provided client 
certificate. Performs
+   * verification with the help of provided HostnameVerifier.
+   * @param inetAddress Peer's inet address.
+   * @param certificate Peer's certificate
+   * @throws CertificateException Thrown if the provided certificate doesn't 
match the peer
+   *                              hostname.
+   */
+  private void performHostVerification(InetAddress inetAddress, 
X509Certificate certificate)
+    throws CertificateException {
+    String hostAddress = "";
+    String hostName = "";
+    try {
+      hostAddress = inetAddress.getHostAddress();
+      hostnameVerifier.verify(hostAddress, certificate);
+    } catch (SSLException addressVerificationException) {
+      try {

Review Comment:
   Sorry @Apache9, I'm not entirely following your question/suggestion.
   
   Are you suggesting that I change to use inetAddress.getHostName() here which 
uses reverse lookup? Or are you suggesting that I swap the order of the 
try/catch here so we try the reverse dns below first?
   
   Since we are in the TrustManager layer, we only have what's available in 
this layer. So it's possible this method will be called in one of two ways:
   
   - Two of the callers above (one for client, one for server), they pass the 
`socket.getInetAddress()`. This will probably be an ip address I guess.
   - The other two callers above pass the parsed `engine.getPeerHost()` which 
according to the javadoc is the un-verified hostname.
   
   The internals of SSL are hard to follow; I don't know in what circumstances 
each caller will be used. But both should work, because we have this 2 layer 
check (one direct check, one reverse lookup).
   
   I'm happy to optimize this by changing the order if that's what you're 
asking. Please clarify if possible.



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to