You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@commons.apache.org by se...@apache.org on 2015/08/24 02:24:36 UTC

svn commit: r1697287 - in /commons/proper/net/trunk/src: changes/ main/java/org/apache/commons/net/ main/java/org/apache/commons/net/ftp/ main/java/org/apache/commons/net/imap/ main/java/org/apache/commons/net/pop3/ main/java/org/apache/commons/net/smt...

Author: sebb
Date: Mon Aug 24 00:24:36 2015
New Revision: 1697287

URL: http://svn.apache.org/r1697287
Log:
NET-579 SSL/TLS SocketClients do not verify the hostname against the certificate

Added:
    commons/proper/net/trunk/src/main/java/org/apache/commons/net/util/SSLSocketUtils.java   (with props)
Modified:
    commons/proper/net/trunk/src/changes/changes.xml
    commons/proper/net/trunk/src/main/java/org/apache/commons/net/SocketClient.java
    commons/proper/net/trunk/src/main/java/org/apache/commons/net/ftp/FTPSClient.java
    commons/proper/net/trunk/src/main/java/org/apache/commons/net/imap/IMAPSClient.java
    commons/proper/net/trunk/src/main/java/org/apache/commons/net/pop3/POP3SClient.java
    commons/proper/net/trunk/src/main/java/org/apache/commons/net/smtp/SMTPSClient.java

Modified: commons/proper/net/trunk/src/changes/changes.xml
URL: http://svn.apache.org/viewvc/commons/proper/net/trunk/src/changes/changes.xml?rev=1697287&r1=1697286&r2=1697287&view=diff
==============================================================================
--- commons/proper/net/trunk/src/changes/changes.xml [utf-8] (original)
+++ commons/proper/net/trunk/src/changes/changes.xml [utf-8] Mon Aug 24 00:24:36 2015
@@ -72,6 +72,9 @@ This is mainly a bug-fix release. See fu
   IMAPExportMbox (example app) allows IMAP folders to be exported into an mbox file.
   This is the inverse of the IMAPImportMbox example added previously
         ">
+            <action issue="NET-579" type="fix" dev="sebb" due-to="Simon Arlott">
+            SSL/TLS SocketClients do not verify the hostname against the certificate
+            </action>
             <action issue="NET-576" type="update" dev="sebb">
             Allow FTPClient to use SYST response if system type is not specified in configuration
             </action>

Modified: commons/proper/net/trunk/src/main/java/org/apache/commons/net/SocketClient.java
URL: http://svn.apache.org/viewvc/commons/proper/net/trunk/src/main/java/org/apache/commons/net/SocketClient.java?rev=1697287&r1=1697286&r2=1697287&view=diff
==============================================================================
--- commons/proper/net/trunk/src/main/java/org/apache/commons/net/SocketClient.java (original)
+++ commons/proper/net/trunk/src/main/java/org/apache/commons/net/SocketClient.java Mon Aug 24 00:24:36 2015
@@ -80,6 +80,9 @@ public abstract class SocketClient
     /** The socket used for the connection. */
     protected Socket _socket_;
 
+    /** The hostname used for the connection (null = no hostname supplied). */
+    protected String _hostname_;
+
     /** The default port the client should connect to. */
     protected int _defaultPort_;
 
@@ -123,6 +126,7 @@ public abstract class SocketClient
     public SocketClient()
     {
         _socket_ = null;
+        _hostname_ = null;
         _input_ = null;
         _output_ = null;
         _timeout_ = 0;
@@ -173,6 +177,7 @@ public abstract class SocketClient
     public void connect(InetAddress host, int port)
     throws SocketException, IOException
     {
+        _hostname_ = null;
         _socket_ = _socketFactory_.createSocket();
         if (receiveBufferSize != -1) {
             _socket_.setReceiveBufferSize(receiveBufferSize);
@@ -202,6 +207,7 @@ public abstract class SocketClient
     throws SocketException, IOException
     {
         connect(InetAddress.getByName(hostname), port);
+        _hostname_ = hostname;
     }
 
 
@@ -224,6 +230,7 @@ public abstract class SocketClient
                         InetAddress localAddr, int localPort)
     throws SocketException, IOException
     {
+        _hostname_ = null;
         _socket_ = _socketFactory_.createSocket();
         if (receiveBufferSize != -1) {
             _socket_.setReceiveBufferSize(receiveBufferSize);
@@ -258,6 +265,7 @@ public abstract class SocketClient
     throws SocketException, IOException
     {
        connect(InetAddress.getByName(hostname), port, localAddr, localPort);
+       _hostname_ = hostname;
     }
 
 
@@ -275,6 +283,7 @@ public abstract class SocketClient
      */
     public void connect(InetAddress host) throws SocketException, IOException
     {
+        _hostname_ = null;
         connect(host, _defaultPort_);
     }
 
@@ -295,6 +304,7 @@ public abstract class SocketClient
     public void connect(String hostname) throws SocketException, IOException
     {
         connect(hostname, _defaultPort_);
+        _hostname_ = hostname;
     }
 
 
@@ -314,6 +324,7 @@ public abstract class SocketClient
         closeQuietly(_input_);
         closeQuietly(_output_);
         _socket_ = null;
+        _hostname_ = null;
         _input_ = null;
         _output_ = null;
     }

Modified: commons/proper/net/trunk/src/main/java/org/apache/commons/net/ftp/FTPSClient.java
URL: http://svn.apache.org/viewvc/commons/proper/net/trunk/src/main/java/org/apache/commons/net/ftp/FTPSClient.java?rev=1697287&r1=1697286&r2=1697287&view=diff
==============================================================================
--- commons/proper/net/trunk/src/main/java/org/apache/commons/net/ftp/FTPSClient.java (original)
+++ commons/proper/net/trunk/src/main/java/org/apache/commons/net/ftp/FTPSClient.java Mon Aug 24 00:24:36 2015
@@ -23,21 +23,28 @@ import java.io.IOException;
 import java.io.InputStreamReader;
 import java.io.OutputStreamWriter;
 import java.net.Socket;
+
+import javax.net.ssl.HostnameVerifier;
 import javax.net.ssl.KeyManager;
 import javax.net.ssl.SSLContext;
 import javax.net.ssl.SSLException;
+import javax.net.ssl.SSLHandshakeException;
 import javax.net.ssl.SSLSocket;
 import javax.net.ssl.SSLSocketFactory;
 import javax.net.ssl.TrustManager;
 
 import org.apache.commons.net.util.Base64;
 import org.apache.commons.net.util.SSLContextUtils;
+import org.apache.commons.net.util.SSLSocketUtils;
 import org.apache.commons.net.util.TrustManagerUtils;
 
 /**
  * FTP over SSL processing. If desired, the JVM property -Djavax.net.debug=all can be used to
  * see wire-level SSL details.
  *
+ * Warning: the hostname is not verified against the certificate by default, use
+ * {@link #setHostnameVerifier(HostnameVerifier)} or {@link #setEndpointCheckingEnabled(boolean)}
+ * (on Java 1.7+) to enable verification. Verification is only performed on client mode connections.
  * @version $Id$
  * @since 2.0
  */
@@ -106,6 +113,12 @@ public class FTPSClient extends FTPClien
     /** The {@link KeyManager}, default null (i.e. use system default). */
     private KeyManager keyManager = null;
 
+    /** The {@link HostnameVerifier} to use post-TLS, default null (i.e. no verification). */
+    private HostnameVerifier hostnameVerifier = null;
+
+    /** Use Java 1.7+ HTTPS Endpoint Identification Algorithim. */
+    private boolean tlsEndpointChecking;
+
     /**
      * Constructor for FTPSClient, calls {@link #FTPSClient(String, boolean)}.
      *
@@ -248,14 +261,19 @@ public class FTPSClient extends FTPClien
         initSslContext();
 
         SSLSocketFactory ssf = context.getSocketFactory();
-        String ip = _socket_.getInetAddress().getHostAddress();
+        String host = (_hostname_ != null) ? _hostname_ : getRemoteAddress().getHostAddress();
         int port = _socket_.getPort();
         SSLSocket socket =
-            (SSLSocket) ssf.createSocket(_socket_, ip, port, false);
+            (SSLSocket) ssf.createSocket(_socket_, host, port, false);
         socket.setEnableSessionCreation(isCreation);
         socket.setUseClientMode(isClientMode);
-        // server mode
-        if (!isClientMode) {
+
+        // client mode
+        if (isClientMode) {
+            if (tlsEndpointChecking) {
+                SSLSocketUtils.enableEndpointNameVerification(socket);
+            }
+        } else { // server mode
             socket.setNeedClientAuth(isNeedClientAuth);
             socket.setWantClientAuth(isWantClientAuth);
         }
@@ -274,6 +292,12 @@ public class FTPSClient extends FTPClien
                 socket .getInputStream(), getControlEncoding()));
         _controlOutput_ = new BufferedWriter(new OutputStreamWriter(
                 socket.getOutputStream(), getControlEncoding()));
+
+        if (isClientMode) {
+            if (hostnameVerifier != null && !hostnameVerifier.verify(host, socket.getSession())) {
+                throw new SSLHandshakeException("Hostname doesn't match certificate");
+            }
+        }
     }
 
     /**
@@ -658,6 +682,56 @@ public class FTPSClient extends FTPClien
     }
 
     /**
+     * Get the currently configured {@link HostnameVerifier}.
+     * The verifier is only used on client mode connections.
+     * @return A HostnameVerifier instance.
+     * @since 3.4
+     */
+    public HostnameVerifier getHostnameVerifier()
+    {
+        return hostnameVerifier;
+    }
+
+    /**
+     * Override the default {@link HostnameVerifier} to use.
+     * The verifier is only used on client mode connections.
+     * @param newHostnameVerifier The HostnameVerifier implementation to set or <code>null</code> to disable.
+     * @since 3.4
+     */
+    public void setHostnameVerifier(HostnameVerifier newHostnameVerifier)
+    {
+        hostnameVerifier = newHostnameVerifier;
+    }
+
+    /**
+     * Return whether or not endpoint identification using the HTTPS algorithm
+     * on Java 1.7+ is enabled. The default behaviour is for this to be disabled.
+     *
+     * This check is only performed on client mode connections.
+     *
+     * @return True if enabled, false if not.
+     * @since 3.4
+     */
+    public boolean isEndpointCheckingEnabled()
+    {
+        return tlsEndpointChecking;
+    }
+
+    /**
+     * Automatic endpoint identification checking using the HTTPS algorithm
+     * is supported on Java 1.7+. The default behaviour is for this to be disabled.
+     *
+     * This check is only performed on client mode connections.
+     *
+     * @param enable Enable automatic endpoint identification checking using the HTTPS algorithm on Java 1.7+.
+     * @since 3.4
+     */
+    public void setEndpointCheckingEnabled(boolean enable)
+    {
+        tlsEndpointChecking = enable;
+    }
+
+    /**
      * Closes the connection to the FTP server and restores
      * connection parameters to the default values.
      * <p>

Modified: commons/proper/net/trunk/src/main/java/org/apache/commons/net/imap/IMAPSClient.java
URL: http://svn.apache.org/viewvc/commons/proper/net/trunk/src/main/java/org/apache/commons/net/imap/IMAPSClient.java?rev=1697287&r1=1697286&r2=1697287&view=diff
==============================================================================
--- commons/proper/net/trunk/src/main/java/org/apache/commons/net/imap/IMAPSClient.java (original)
+++ commons/proper/net/trunk/src/main/java/org/apache/commons/net/imap/IMAPSClient.java Mon Aug 24 00:24:36 2015
@@ -22,15 +22,18 @@ import java.io.InputStreamReader;
 import java.io.IOException;
 import java.io.OutputStreamWriter;
 
+import javax.net.ssl.HostnameVerifier;
 import javax.net.ssl.KeyManager;
 import javax.net.ssl.SSLContext;
 import javax.net.ssl.SSLException;
+import javax.net.ssl.SSLHandshakeException;
 import javax.net.ssl.SSLSocket;
 import javax.net.ssl.SSLSocketFactory;
 import javax.net.ssl.TrustManager;
 
 import org.apache.commons.net.io.CRLFLineReader;
 import org.apache.commons.net.util.SSLContextUtils;
+import org.apache.commons.net.util.SSLSocketUtils;
 
 /**
  * The IMAPSClient class provides SSL/TLS connection encryption to IMAPClient.
@@ -45,6 +48,10 @@ import org.apache.commons.net.util.SSLCo
  *               IMAPSClient c = new IMAPSClient();
  *               c.connect("127.0.0.1", 143);
  *               if (c.execTLS()) { /rest of the commands here/ }
+ *
+ * Warning: the hostname is not verified against the certificate by default, use
+ * {@link #setHostnameVerifier(HostnameVerifier)} or {@link #setEndpointCheckingEnabled(boolean)}
+ * (on Java 1.7+) to enable verification.
  */
 public class IMAPSClient extends IMAPClient
 {
@@ -73,6 +80,12 @@ public class IMAPSClient extends IMAPCli
     /** The {@link KeyManager}, default null. */
     private KeyManager keyManager = null;
 
+    /** The {@link HostnameVerifier} to use post-TLS, default null (i.e. no verification). */
+    private HostnameVerifier hostnameVerifier = null;
+
+    /** Use Java 1.7+ HTTPS Endpoint Identification Algorithim. */
+    private boolean tlsEndpointChecking;
+
     /**
      * Constructor for IMAPSClient.
      * Sets security mode to explicit (isImplicit = false).
@@ -185,13 +198,17 @@ public class IMAPSClient extends IMAPCli
         initSSLContext();
 
         SSLSocketFactory ssf = context.getSocketFactory();
-        String ip = getRemoteAddress().getHostAddress();
+        String host = (_hostname_ != null) ? _hostname_ : getRemoteAddress().getHostAddress();
         int port = getRemotePort();
         SSLSocket socket =
-            (SSLSocket) ssf.createSocket(_socket_, ip, port, true);
+            (SSLSocket) ssf.createSocket(_socket_, host, port, true);
         socket.setEnableSessionCreation(true);
         socket.setUseClientMode(true);
 
+        if (tlsEndpointChecking) {
+            SSLSocketUtils.enableEndpointNameVerification(socket);
+        }
+
         if (protocols != null) {
             socket.setEnabledProtocols(protocols);
         }
@@ -210,6 +227,10 @@ public class IMAPSClient extends IMAPCli
         __writer =
           new BufferedWriter(new OutputStreamWriter(_output_,
                                                     __DEFAULT_ENCODING));
+
+        if (hostnameVerifier != null && !hostnameVerifier.verify(host, socket.getSession())) {
+            throw new SSLHandshakeException("Hostname doesn't match certificate");
+        }
     }
 
     /**
@@ -320,5 +341,48 @@ public class IMAPSClient extends IMAPCli
         trustManager = newTrustManager;
     }
 
+    /**
+     * Get the currently configured {@link HostnameVerifier}.
+     * @return A HostnameVerifier instance.
+     * @since 3.4
+     */
+    public HostnameVerifier getHostnameVerifier()
+    {
+        return hostnameVerifier;
+    }
+
+    /**
+     * Override the default {@link HostnameVerifier} to use.
+     * @param newHostnameVerifier The HostnameVerifier implementation to set or <code>null</code> to disable.
+     * @since 3.4
+     */
+    public void setHostnameVerifier(HostnameVerifier newHostnameVerifier)
+    {
+        hostnameVerifier = newHostnameVerifier;
+    }
+
+    /**
+     * Return whether or not endpoint identification using the HTTPS algorithm
+     * on Java 1.7+ is enabled. The default behaviour is for this to be disabled.
+     *
+     * @return True if enabled, false if not.
+     * @since 3.4
+     */
+    public boolean isEndpointCheckingEnabled()
+    {
+        return tlsEndpointChecking;
+    }
+
+    /**
+     * Automatic endpoint identification checking using the HTTPS algorithm
+     * is supported on Java 1.7+. The default behaviour is for this to be disabled.
+     *
+     * @param enable Enable automatic endpoint identification checking using the HTTPS algorithm on Java 1.7+.
+     * @since 3.4
+     */
+    public void setEndpointCheckingEnabled(boolean enable)
+    {
+        tlsEndpointChecking = enable;
+    }
 }
 /* kate: indent-width 4; replace-tabs on; */

Modified: commons/proper/net/trunk/src/main/java/org/apache/commons/net/pop3/POP3SClient.java
URL: http://svn.apache.org/viewvc/commons/proper/net/trunk/src/main/java/org/apache/commons/net/pop3/POP3SClient.java?rev=1697287&r1=1697286&r2=1697287&view=diff
==============================================================================
--- commons/proper/net/trunk/src/main/java/org/apache/commons/net/pop3/POP3SClient.java (original)
+++ commons/proper/net/trunk/src/main/java/org/apache/commons/net/pop3/POP3SClient.java Mon Aug 24 00:24:36 2015
@@ -22,15 +22,18 @@ import java.io.IOException;
 import java.io.InputStreamReader;
 import java.io.OutputStreamWriter;
 
+import javax.net.ssl.HostnameVerifier;
 import javax.net.ssl.KeyManager;
 import javax.net.ssl.SSLContext;
 import javax.net.ssl.SSLException;
+import javax.net.ssl.SSLHandshakeException;
 import javax.net.ssl.SSLSocket;
 import javax.net.ssl.SSLSocketFactory;
 import javax.net.ssl.TrustManager;
 
 import org.apache.commons.net.io.CRLFLineReader;
 import org.apache.commons.net.util.SSLContextUtils;
+import org.apache.commons.net.util.SSLSocketUtils;
 
 /**
  * POP3 over SSL processing. Copied from FTPSClient.java and modified to suit POP3.
@@ -44,6 +47,10 @@ import org.apache.commons.net.util.SSLCo
  *               POP3SClient c = new POP3SClient();
  *               c.connect("127.0.0.1", 110);
  *               if (c.execTLS()) { /rest of the commands here/ }
+ *
+ * Warning: the hostname is not verified against the certificate by default, use
+ * {@link #setHostnameVerifier(HostnameVerifier)} or {@link #setEndpointCheckingEnabled(boolean)}
+ * (on Java 1.7+) to enable verification.
  * @since 3.0
  */
 public class POP3SClient extends POP3Client
@@ -77,6 +84,12 @@ public class POP3SClient extends POP3Cli
     /** The {@link KeyManager}, default null. */
     private KeyManager keyManager = null;
 
+    /** The {@link HostnameVerifier} to use post-TLS, default null (i.e. no verification). */
+    private HostnameVerifier hostnameVerifier = null;
+
+    /** Use Java 1.7+ HTTPS Endpoint Identification Algorithim. */
+    private boolean tlsEndpointChecking;
+
     /**
      * Constructor for POP3SClient, using {@link #DEFAULT_PROTOCOL} i.e. TLS
      * Sets security mode to explicit.
@@ -194,13 +207,17 @@ public class POP3SClient extends POP3Cli
         initSSLContext();
 
         SSLSocketFactory ssf = context.getSocketFactory();
-        String ip = getRemoteAddress().getHostAddress();
+        String host = (_hostname_ != null) ? _hostname_ : getRemoteAddress().getHostAddress();
         int port = getRemotePort();
         SSLSocket socket =
-            (SSLSocket) ssf.createSocket(_socket_, ip, port, true);
+            (SSLSocket) ssf.createSocket(_socket_, host, port, true);
         socket.setEnableSessionCreation(true);
         socket.setUseClientMode(true);
 
+        if (tlsEndpointChecking) {
+            SSLSocketUtils.enableEndpointNameVerification(socket);
+        }
+
         if (protocols != null) {
             socket.setEnabledProtocols(protocols);
         }
@@ -215,6 +232,10 @@ public class POP3SClient extends POP3Cli
         _output_ = socket.getOutputStream();
         _reader = new CRLFLineReader(new InputStreamReader(_input_, _DEFAULT_ENCODING));
         _writer = new BufferedWriter(new OutputStreamWriter(_output_, _DEFAULT_ENCODING));
+
+        if (hostnameVerifier != null && !hostnameVerifier.verify(host, socket.getSession())) {
+            throw new SSLHandshakeException("Hostname doesn't match certificate");
+        }
     }
 
     /**
@@ -324,6 +345,50 @@ public class POP3SClient extends POP3Cli
     {
         trustManager = newTrustManager;
     }
+
+    /**
+     * Get the currently configured {@link HostnameVerifier}.
+     * @return A HostnameVerifier instance.
+     * @since 3.4
+     */
+    public HostnameVerifier getHostnameVerifier()
+    {
+        return hostnameVerifier;
+    }
+
+    /**
+     * Override the default {@link HostnameVerifier} to use.
+     * @param newHostnameVerifier The HostnameVerifier implementation to set or <code>null</code> to disable.
+     * @since 3.4
+     */
+    public void setHostnameVerifier(HostnameVerifier newHostnameVerifier)
+    {
+        hostnameVerifier = newHostnameVerifier;
+    }
+
+    /**
+     * Return whether or not endpoint identification using the HTTPS algorithm
+     * on Java 1.7+ is enabled. The default behaviour is for this to be disabled.
+     *
+     * @return True if enabled, false if not.
+     * @since 3.4
+     */
+    public boolean isEndpointCheckingEnabled()
+    {
+        return tlsEndpointChecking;
+    }
+
+    /**
+     * Automatic endpoint identification checking using the HTTPS algorithm
+     * is supported on Java 1.7+. The default behaviour is for this to be disabled.
+     *
+     * @param enable Enable automatic endpoint identification checking using the HTTPS algorithm on Java 1.7+.
+     * @since 3.4
+     */
+    public void setEndpointCheckingEnabled(boolean enable)
+    {
+        tlsEndpointChecking = enable;
+    }
 }
 
 /* kate: indent-width 4; replace-tabs on; */

Modified: commons/proper/net/trunk/src/main/java/org/apache/commons/net/smtp/SMTPSClient.java
URL: http://svn.apache.org/viewvc/commons/proper/net/trunk/src/main/java/org/apache/commons/net/smtp/SMTPSClient.java?rev=1697287&r1=1697286&r2=1697287&view=diff
==============================================================================
--- commons/proper/net/trunk/src/main/java/org/apache/commons/net/smtp/SMTPSClient.java (original)
+++ commons/proper/net/trunk/src/main/java/org/apache/commons/net/smtp/SMTPSClient.java Mon Aug 24 00:24:36 2015
@@ -22,14 +22,17 @@ import java.io.IOException;
 import java.io.InputStreamReader;
 import java.io.OutputStreamWriter;
 
+import javax.net.ssl.HostnameVerifier;
 import javax.net.ssl.KeyManager;
 import javax.net.ssl.SSLContext;
+import javax.net.ssl.SSLHandshakeException;
 import javax.net.ssl.SSLSocket;
 import javax.net.ssl.SSLSocketFactory;
 import javax.net.ssl.TrustManager;
 
 import org.apache.commons.net.io.CRLFLineReader;
 import org.apache.commons.net.util.SSLContextUtils;
+import org.apache.commons.net.util.SSLSocketUtils;
 
 /**
  * SMTP over SSL processing. Copied from FTPSClient.java and modified to suit SMTP.
@@ -43,6 +46,10 @@ import org.apache.commons.net.util.SSLCo
  *               SMTPSClient c = new SMTPSClient();
  *               c.connect("127.0.0.1", 25);
  *               if (c.execTLS()) { /rest of the commands here/ }
+ *
+ * Warning: the hostname is not verified against the certificate by default, use
+ * {@link #setHostnameVerifier(HostnameVerifier)} or {@link #setEndpointCheckingEnabled(boolean)}
+ * (on Java 1.7+) to enable verification.
  * @since 3.0
  */
 public class SMTPSClient extends SMTPClient
@@ -68,6 +75,12 @@ public class SMTPSClient extends SMTPCli
     /** The {@link KeyManager}, default null (i.e. use system managers). */
     private KeyManager keyManager = null; // seems not to be required
 
+    /** The {@link HostnameVerifier} to use post-TLS, default null (i.e. no verification). */
+    private HostnameVerifier hostnameVerifier = null;
+
+    /** Use Java 1.7+ HTTPS Endpoint Identification Algorithim. */
+    private boolean tlsEndpointChecking;
+
     /**
      * Constructor for SMTPSClient, using {@link #DEFAULT_PROTOCOL} i.e. TLS
      * Sets security mode to explicit (isImplicit = false).
@@ -183,13 +196,16 @@ public class SMTPSClient extends SMTPCli
         initSSLContext();
 
         SSLSocketFactory ssf = context.getSocketFactory();
-        String ip = getRemoteAddress().getHostAddress();
+        String host = (_hostname_ != null) ? _hostname_ : getRemoteAddress().getHostAddress();
         int port = getRemotePort();
         SSLSocket socket =
-            (SSLSocket) ssf.createSocket(_socket_, ip, port, true);
+            (SSLSocket) ssf.createSocket(_socket_, host, port, true);
         socket.setEnableSessionCreation(true);
         socket.setUseClientMode(true);
 
+        if (tlsEndpointChecking) {
+            SSLSocketUtils.enableEndpointNameVerification(socket);
+        }
         if (protocols != null) {
             socket.setEnabledProtocols(protocols);
         }
@@ -207,6 +223,9 @@ public class SMTPSClient extends SMTPCli
         _writer = new BufferedWriter(
                         new OutputStreamWriter(_output_, encoding));
 
+        if (hostnameVerifier != null && !hostnameVerifier.verify(host, socket.getSession())) {
+            throw new SSLHandshakeException("Hostname doesn't match certificate");
+        }
     }
 
     /**
@@ -315,6 +334,50 @@ public class SMTPSClient extends SMTPCli
     {
         trustManager = newTrustManager;
     }
+
+    /**
+     * Get the currently configured {@link HostnameVerifier}.
+     * @return A HostnameVerifier instance.
+     * @since 3.4
+     */
+    public HostnameVerifier getHostnameVerifier()
+    {
+        return hostnameVerifier;
+    }
+
+    /**
+     * Override the default {@link HostnameVerifier} to use.
+     * @param newHostnameVerifier The HostnameVerifier implementation to set or <code>null</code> to disable.
+     * @since 3.4
+     */
+    public void setHostnameVerifier(HostnameVerifier newHostnameVerifier)
+    {
+        hostnameVerifier = newHostnameVerifier;
+    }
+
+    /**
+     * Return whether or not endpoint identification using the HTTPS algorithm
+     * on Java 1.7+ is enabled. The default behaviour is for this to be disabled.
+     *
+     * @return True if enabled, false if not.
+     * @since 3.4
+     */
+    public boolean isEndpointCheckingEnabled()
+    {
+        return tlsEndpointChecking;
+    }
+
+    /**
+     * Automatic endpoint identification checking using the HTTPS algorithm
+     * is supported on Java 1.7+. The default behaviour is for this to be disabled.
+     *
+     * @param enable Enable automatic endpoint identification checking using the HTTPS algorithm on Java 1.7+.
+     * @since 3.4
+     */
+    public void setEndpointCheckingEnabled(boolean enable)
+    {
+        tlsEndpointChecking = enable;
+    }
 }
 
 /* kate: indent-width 4; replace-tabs on; */

Added: commons/proper/net/trunk/src/main/java/org/apache/commons/net/util/SSLSocketUtils.java
URL: http://svn.apache.org/viewvc/commons/proper/net/trunk/src/main/java/org/apache/commons/net/util/SSLSocketUtils.java?rev=1697287&view=auto
==============================================================================
--- commons/proper/net/trunk/src/main/java/org/apache/commons/net/util/SSLSocketUtils.java (added)
+++ commons/proper/net/trunk/src/main/java/org/apache/commons/net/util/SSLSocketUtils.java Mon Aug 24 00:24:36 2015
@@ -0,0 +1,63 @@
+/*
+ * 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.commons.net.util;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+
+import javax.net.ssl.SSLSocket;
+
+/**
+ * General utilities for SSLSocket.
+ * @since 3.4
+ */
+public class SSLSocketUtils {
+    private SSLSocketUtils() {
+        // Not instantiable
+    }
+
+    /**
+     * Enable the HTTPS endpoint identification algorithm on an SSLSocket.
+     * @param socket the SSL socket
+     * @return {@code true} on success (this is only supported on Java 1.7+)
+     */
+    public static boolean enableEndpointNameVerification(SSLSocket socket) {
+        try {
+            Class<?> cls = Class.forName("javax.net.ssl.SSLParameters");
+            Method setEndpointIdentificationAlgorithm = cls.getDeclaredMethod("setEndpointIdentificationAlgorithm", String.class);
+            Method getSSLParameters = SSLSocket.class.getDeclaredMethod("getSSLParameters");
+            Method setSSLParameters = SSLSocket.class.getDeclaredMethod("setSSLParameters", cls);
+            if (setEndpointIdentificationAlgorithm != null && getSSLParameters != null && setSSLParameters != null) {
+                Object sslParams = getSSLParameters.invoke(socket);
+                if (sslParams != null) {
+                    setEndpointIdentificationAlgorithm.invoke(sslParams, "HTTPS");
+                    setSSLParameters.invoke(socket, sslParams);
+                    return true;
+                }
+            }
+        } catch (SecurityException e) {
+        } catch (ClassNotFoundException e) {
+        } catch (NoSuchMethodException e) {
+        } catch (IllegalArgumentException e) {
+        } catch (IllegalAccessException e) {
+        } catch (InvocationTargetException e) {
+        }
+        return false;
+    }
+}

Propchange: commons/proper/net/trunk/src/main/java/org/apache/commons/net/util/SSLSocketUtils.java
------------------------------------------------------------------------------
    svn:eol-style = native