You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@logging.apache.org by ma...@apache.org on 2021/12/27 23:04:32 UTC

[logging-log4j2] branch log4j-2.3.x updated: [LOG4J2-2819] Add SSL/TLS hostname verify option

This is an automated email from the ASF dual-hosted git repository.

mattsicker pushed a commit to branch log4j-2.3.x
in repository https://gitbox.apache.org/repos/asf/logging-log4j2.git


The following commit(s) were added to refs/heads/log4j-2.3.x by this push:
     new 3c62f0b  [LOG4J2-2819] Add SSL/TLS hostname verify option
3c62f0b is described below

commit 3c62f0bea692456b1b5039d3bcc1c3e0ba65146a
Author: Matt Sicker <bo...@gmail.com>
AuthorDate: Mon Dec 27 17:03:13 2021 -0600

    [LOG4J2-2819] Add SSL/TLS hostname verify option
    
    This backports a fix for CVE-2020-9488.
---
 .../logging/log4j/core/appender/SmtpAppender.java  | 295 ++++++++++++++++-----
 .../apache/logging/log4j/core/net/SmtpManager.java |  68 ++++-
 .../log4j/core/net/ssl/SslConfiguration.java       |  49 ++--
 .../log4j/core/appender/SmtpAppenderTest.java      |  21 +-
 src/changes/changes.xml                            |   6 +
 src/site/xdoc/manual/appenders.xml                 |   4 +-
 6 files changed, 343 insertions(+), 100 deletions(-)

diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/SmtpAppender.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/SmtpAppender.java
index 10668af..0f79afe 100644
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/SmtpAppender.java
+++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/SmtpAppender.java
@@ -17,20 +17,23 @@
 
 package org.apache.logging.log4j.core.appender;
 
-import java.io.Serializable;
-
+import org.apache.logging.log4j.Level;
 import org.apache.logging.log4j.core.Filter;
 import org.apache.logging.log4j.core.Layout;
 import org.apache.logging.log4j.core.LogEvent;
 import org.apache.logging.log4j.core.config.plugins.Plugin;
-import org.apache.logging.log4j.core.config.plugins.PluginAttribute;
+import org.apache.logging.log4j.core.config.plugins.PluginBuilderAttribute;
+import org.apache.logging.log4j.core.config.plugins.PluginBuilderFactory;
 import org.apache.logging.log4j.core.config.plugins.PluginElement;
-import org.apache.logging.log4j.core.config.plugins.PluginFactory;
+import org.apache.logging.log4j.core.config.plugins.validation.constraints.Required;
 import org.apache.logging.log4j.core.filter.ThresholdFilter;
 import org.apache.logging.log4j.core.layout.HtmlLayout;
 import org.apache.logging.log4j.core.net.SmtpManager;
+import org.apache.logging.log4j.core.net.ssl.SslConfiguration;
 import org.apache.logging.log4j.core.util.Booleans;
 
+import java.io.Serializable;
+
 /**
  * Send an e-mail when a specific logging event occurs, typically on errors or
  * fatal errors.
@@ -64,64 +67,232 @@ public final class SmtpAppender extends AbstractAppender {
         this.manager = manager;
     }
 
-    /**
-     * Create a SmtpAppender.
-     *
-     * @param name
-     *            The name of the Appender.
-     * @param to
-     *            The comma-separated list of recipient email addresses.
-     * @param cc
-     *            The comma-separated list of CC email addresses.
-     * @param bcc
-     *            The comma-separated list of BCC email addresses.
-     * @param from
-     *            The email address of the sender.
-     * @param replyTo
-     *            The comma-separated list of reply-to email addresses.
-     * @param subject The subject of the email message.
-     * @param smtpProtocol The SMTP transport protocol (such as "smtps", defaults to "smtp").
-     * @param smtpHost
-     *            The SMTP hostname to send to.
-     * @param smtpPortStr
-     *            The SMTP port to send to.
-     * @param smtpUsername
-     *            The username required to authenticate against the SMTP server.
-     * @param smtpPassword
-     *            The password required to authenticate against the SMTP server.
-     * @param smtpDebug
-     *            Enable mail session debuging on STDOUT.
-     * @param bufferSizeStr
-     *            How many log events should be buffered for inclusion in the
-     *            message?
-     * @param layout
-     *            The layout to use (defaults to HtmlLayout).
-     * @param filter
-     *            The Filter or null (defaults to ThresholdFilter, level of
-     *            ERROR).
-     * @param ignore If {@code "true"} (default) exceptions encountered when appending events are logged; otherwise
-     *               they are propagated to the caller.
-     * @return The SmtpAppender.
-     */
-    @PluginFactory
-    public static SmtpAppender createAppender(
-            @PluginAttribute("name") final String name,
-            @PluginAttribute("to") final String to,
-            @PluginAttribute("cc") final String cc,
-            @PluginAttribute("bcc") final String bcc,
-            @PluginAttribute("from") final String from,
-            @PluginAttribute("replyTo") final String replyTo,
-            @PluginAttribute("subject") final String subject,
-            @PluginAttribute("smtpProtocol") final String smtpProtocol,
-            @PluginAttribute("smtpHost") final String smtpHost,
-            @PluginAttribute("smtpPort") final String smtpPortStr,
-            @PluginAttribute("smtpUsername") final String smtpUsername,
-            @PluginAttribute("smtpPassword") final String smtpPassword,
-            @PluginAttribute("smtpDebug") final String smtpDebug,
-            @PluginAttribute("bufferSize") final String bufferSizeStr,
-            @PluginElement("Layout") Layout<? extends Serializable> layout,
-            @PluginElement("Filter") Filter filter,
-            @PluginAttribute("ignoreExceptions") final String ignore) {
+    public static class Builder implements org.apache.logging.log4j.core.util.Builder<SmtpAppender> {
+        @PluginBuilderAttribute
+        @Required(message = "No name provided for SmtpAppender")
+        private String name;
+
+        @PluginElement("Filter")
+        private Filter filter = ThresholdFilter.createFilter(null, null, null);
+
+        @PluginElement("Layout")
+        private Layout<? extends Serializable> layout = HtmlLayout.createDefaultLayout();
+
+        @PluginBuilderAttribute
+        private boolean ignoreExceptions = true;
+
+        @PluginBuilderAttribute
+        private String to;
+
+        @PluginBuilderAttribute
+        private String cc;
+
+        @PluginBuilderAttribute
+        private String bcc;
+
+        @PluginBuilderAttribute
+        private String from;
+
+        @PluginBuilderAttribute
+        private String replyTo;
+
+        @PluginBuilderAttribute
+        private String subject;
+
+        @PluginBuilderAttribute
+        private String smtpProtocol = "smtp";
+
+        @PluginBuilderAttribute
+        private String smtpHost;
+
+        @PluginBuilderAttribute
+        private int smtpPort;
+
+        @PluginBuilderAttribute
+        private String smtpUsername;
+
+        @PluginBuilderAttribute(sensitive = true)
+        private String smtpPassword;
+
+        @PluginBuilderAttribute
+        private boolean smtpDebug;
+
+        @PluginBuilderAttribute
+        private int bufferSize = DEFAULT_BUFFER_SIZE;
+
+        @PluginElement("SSL")
+        private SslConfiguration sslConfiguration;
+
+        /**
+         * Name of the SmtpAppender.
+         */
+        public Builder setName(final String name) {
+            this.name = name;
+            return this;
+        }
+
+        /**
+         * Overrides the default filter. Default filter is a {@link ThresholdFilter} of level {@link Level#ERROR}.
+         */
+        public Builder setFilter(final Filter filter) {
+            this.filter = filter;
+            return this;
+        }
+
+        /**
+         * Overrides the default layout. Default layout is the {@linkplain HtmlLayout#createDefaultLayout() default HTML layout}.
+         */
+        public Builder setLayout(final Layout<? extends Serializable> layout) {
+            this.layout = layout;
+            return this;
+        }
+
+        /**
+         * Whether to ignore exceptions thrown by the SmtpAppender. Defaults to {@code true}.
+         */
+        public Builder setIgnoreExceptions(final boolean ignoreExceptions) {
+            this.ignoreExceptions = ignoreExceptions;
+            return this;
+        }
+
+        /**
+         * Comma-separated list of recipient email addresses.
+         */
+        public Builder setTo(final String to) {
+            this.to = to;
+            return this;
+        }
+
+        /**
+         * Comma-separated list of CC email addresses.
+         */
+        public Builder setCc(final String cc) {
+            this.cc = cc;
+            return this;
+        }
+
+        /**
+         * Comma-separated list of BCC email addresses.
+         */
+        public Builder setBcc(final String bcc) {
+            this.bcc = bcc;
+            return this;
+        }
+
+        /**
+         * Email address of the sender.
+         */
+        public Builder setFrom(final String from) {
+            this.from = from;
+            return this;
+        }
+
+        /**
+         * Comma-separated list of Reply-To email addresses.
+         */
+        public Builder setReplyTo(final String replyTo) {
+            this.replyTo = replyTo;
+            return this;
+        }
+
+        /**
+         * Subject template for the email messages.
+         * @see org.apache.logging.log4j.core.layout.PatternLayout
+         */
+        public Builder setSubject(final String subject) {
+            this.subject = subject;
+            return this;
+        }
+
+        /**
+         * Transport protocol to use for SMTP such as "smtp" or "smtps". Defaults to "smtp".
+         */
+        public Builder setSmtpProtocol(final String smtpProtocol) {
+            this.smtpProtocol = smtpProtocol;
+            return this;
+        }
+
+        /**
+         * Host name of SMTP server to send messages through.
+         */
+        public Builder setSmtpHost(final String smtpHost) {
+            this.smtpHost = smtpHost;
+            return this;
+        }
+
+        /**
+         * Port number of SMTP server to send messages through.
+         */
+        public Builder setSmtpPort(final int smtpPort) {
+            this.smtpPort = smtpPort;
+            return this;
+        }
+
+        /**
+         * Username to authenticate with SMTP server.
+         */
+        public Builder setSmtpUsername(final String smtpUsername) {
+            this.smtpUsername = smtpUsername;
+            return this;
+        }
+
+        /**
+         * Password to authenticate with SMTP server.
+         */
+        public Builder setSmtpPassword(final String smtpPassword) {
+            this.smtpPassword = smtpPassword;
+            return this;
+        }
+
+        /**
+         * Enables or disables mail session debugging on STDOUT. Disabled by default.
+         */
+        public Builder setSmtpDebug(final boolean smtpDebug) {
+            this.smtpDebug = smtpDebug;
+            return this;
+        }
+
+        /**
+         * Number of log events to buffer before sending an email. Defaults to {@value #DEFAULT_BUFFER_SIZE}.
+         */
+        public Builder setBufferSize(final int bufferSize) {
+            this.bufferSize = bufferSize;
+            return this;
+        }
+
+        /**
+         * Specifies an SSL configuration for smtps connections.
+         */
+        public Builder setSslConfiguration(final SslConfiguration sslConfiguration) {
+            this.sslConfiguration = sslConfiguration;
+            return this;
+        }
+
+        @Override
+        public SmtpAppender build() {
+            if (layout == null) {
+                layout = HtmlLayout.createDefaultLayout();
+            }
+            if (filter == null) {
+                filter = ThresholdFilter.createFilter(null, null, null);
+            }
+            final SmtpManager smtpManager =
+                    SmtpManager.getSMTPManager(to, cc, bcc, from, replyTo, subject, smtpProtocol, smtpHost, smtpPort,
+                            smtpUsername, smtpPassword, smtpDebug, filter.toString(), bufferSize, sslConfiguration);
+            return new SmtpAppender(name, filter, layout, smtpManager, ignoreExceptions);
+        }
+    }
+
+    @PluginBuilderFactory
+    public static Builder newBuilder() {
+        return new Builder();
+    }
+
+    @Deprecated
+    public static SmtpAppender createAppender(final String name, final String to, final String cc, final String bcc,
+            final String from, final String replyTo, final String subject, final String smtpProtocol, final String smtpHost,
+            final String smtpPortStr, final String smtpUsername, final String smtpPassword, final String smtpDebug,
+            final String bufferSizeStr, Layout<? extends Serializable> layout, Filter filter, final String ignore) {
         if (name == null) {
             LOGGER.error("No name provided for SmtpAppender");
             return null;
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/net/SmtpManager.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/net/SmtpManager.java
index b939f01..04c1b13 100644
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/net/SmtpManager.java
+++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/net/SmtpManager.java
@@ -35,12 +35,14 @@ import javax.mail.internet.MimeMessage;
 import javax.mail.internet.MimeMultipart;
 import javax.mail.internet.MimeUtility;
 import javax.mail.util.ByteArrayDataSource;
+import javax.net.ssl.SSLSocketFactory;
 
 import org.apache.logging.log4j.LoggingException;
 import org.apache.logging.log4j.core.Layout;
 import org.apache.logging.log4j.core.LogEvent;
 import org.apache.logging.log4j.core.appender.AbstractManager;
 import org.apache.logging.log4j.core.appender.ManagerFactory;
+import org.apache.logging.log4j.core.net.ssl.SslConfiguration;
 import org.apache.logging.log4j.core.util.CyclicBuffer;
 import org.apache.logging.log4j.core.util.NameUtil;
 import org.apache.logging.log4j.core.util.NetUtils;
@@ -74,11 +76,27 @@ public class SmtpManager extends AbstractManager {
         buffer.add(event);
     }
 
-    public static SmtpManager getSMTPManager(final String to, final String cc, final String bcc,
-                                             final String from, final String replyTo,
-                                             final String subject, String protocol, final String host,
-                                             final int port, final String username, final String password,
-                                             final boolean isDebug, final String filterName, final int numElements) {
+    /**
+     * @deprecated use {@link #getSMTPManager(String, String, String, String, String, String, String, String, int, String, String, boolean, String, int, SslConfiguration)}
+     */
+    @Deprecated
+    public static SmtpManager getSMTPManager(
+            final String to, final String cc, final String bcc,
+            final String from, final String replyTo,
+            final String subject, String protocol, final String host,
+            final int port, final String username, final String password,
+            final boolean isDebug, final String filterName, final int numElements) {
+        return getSMTPManager(to, cc, bcc, from, replyTo, subject, protocol, host, port, username, password, isDebug,
+                filterName, numElements, null);
+    }
+
+    public static SmtpManager getSMTPManager(
+            final String to, final String cc, final String bcc,
+            final String from, final String replyTo,
+            final String subject, String protocol, final String host,
+            final int port, final String username, final String password,
+            final boolean isDebug, final String filterName, final int numElements,
+            final SslConfiguration sslConfiguration) {
         if (Strings.isEmpty(protocol)) {
             protocol = "smtp";
         }
@@ -122,7 +140,7 @@ public class SmtpManager extends AbstractManager {
         final String name = "SMTP:" + NameUtil.md5(sb.toString());
 
         return getManager(name, FACTORY, new FactoryData(to, cc, bcc, from, replyTo, subject,
-            protocol, host, port, username, password, isDebug, numElements));
+            protocol, host, port, username, password, isDebug, numElements, sslConfiguration));
     }
 
     /**
@@ -259,10 +277,24 @@ public class SmtpManager extends AbstractManager {
         private final String password;
         private final boolean isDebug;
         private final int numElements;
+        private final SslConfiguration sslConfiguration;
+
+        /**
+         * @deprecated use {@link #FactoryData(String, String, String, String, String, String, String, String, int, String, String, boolean, int, SslConfiguration)}
+         */
+        @Deprecated
+        public FactoryData(
+                final String to, final String cc, final String bcc, final String from, final String replyTo,
+                final String subject, final String protocol, final String host, final int port,
+                final String username, final String password, final boolean isDebug, final int numElements) {
+            this(to, cc, bcc, from, replyTo, subject, protocol, host, port, username, password, isDebug, numElements, null);
+        }
 
-        public FactoryData(final String to, final String cc, final String bcc, final String from, final String replyTo,
-                           final String subject, final String protocol, final String host, final int port,
-                           final String username, final String password, final boolean isDebug, final int numElements) {
+        public FactoryData(
+                final String to, final String cc, final String bcc, final String from, final String replyTo,
+                final String subject, final String protocol, final String host, final int port,
+                final String username, final String password, final boolean isDebug, final int numElements,
+                final SslConfiguration sslConfiguration) {
             this.to = to;
             this.cc = cc;
             this.bcc = bcc;
@@ -276,6 +308,7 @@ public class SmtpManager extends AbstractManager {
             this.password = password;
             this.isDebug = isDebug;
             this.numElements = numElements;
+            this.sslConfiguration = sslConfiguration;
         }
     }
 
@@ -303,22 +336,31 @@ public class SmtpManager extends AbstractManager {
             final String prefix = "mail." + data.protocol;
 
             final Properties properties = PropertiesUtil.getSystemProperties();
-            properties.put("mail.transport.protocol", data.protocol);
+            properties.setProperty("mail.transport.protocol", data.protocol);
             if (properties.getProperty("mail.host") == null) {
                 // Prevent an UnknownHostException in Java 7
                 properties.put("mail.host", NetUtils.getLocalHostname());
             }
 
             if (null != data.host) {
-                properties.put(prefix + ".host", data.host);
+                properties.setProperty(prefix + ".host", data.host);
             }
             if (data.port > 0) {
-                properties.put(prefix + ".port", String.valueOf(data.port));
+                properties.setProperty(prefix + ".port", Integer.toString(data.port));
             }
 
             final Authenticator authenticator = buildAuthenticator(data.username, data.password);
             if (null != authenticator) {
-                properties.put(prefix + ".auth", "true");
+                properties.setProperty(prefix + ".auth", "true");
+            }
+
+            if (data.protocol.equals("smtps")) {
+                final SslConfiguration sslConfiguration = data.sslConfiguration;
+                if (sslConfiguration != null) {
+                    final SSLSocketFactory sslSocketFactory = sslConfiguration.getSslSocketFactory();
+                    properties.put(prefix + ".ssl.socketFactory", sslSocketFactory);
+                    properties.setProperty(prefix + ".ssl.checkserveridentity", Boolean.toString(sslConfiguration.isVerifyHostName()));
+                }
             }
 
             final Session session = Session.getInstance(properties, authenticator);
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/net/ssl/SslConfiguration.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/net/ssl/SslConfiguration.java
index 43464e5..b453f78 100644
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/net/ssl/SslConfiguration.java
+++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/net/ssl/SslConfiguration.java
@@ -16,10 +16,11 @@
  */
 package org.apache.logging.log4j.core.net.ssl;
 
-import java.security.KeyManagementException;
-import java.security.KeyStoreException;
-import java.security.NoSuchAlgorithmException;
-import java.security.UnrecoverableKeyException;
+import org.apache.logging.log4j.core.config.plugins.Plugin;
+import org.apache.logging.log4j.core.config.plugins.PluginAttribute;
+import org.apache.logging.log4j.core.config.plugins.PluginElement;
+import org.apache.logging.log4j.core.config.plugins.PluginFactory;
+import org.apache.logging.log4j.status.StatusLogger;
 
 import javax.net.ssl.KeyManager;
 import javax.net.ssl.KeyManagerFactory;
@@ -28,12 +29,10 @@ import javax.net.ssl.SSLServerSocketFactory;
 import javax.net.ssl.SSLSocketFactory;
 import javax.net.ssl.TrustManager;
 import javax.net.ssl.TrustManagerFactory;
-
-import org.apache.logging.log4j.core.config.plugins.Plugin;
-import org.apache.logging.log4j.core.config.plugins.PluginAttribute;
-import org.apache.logging.log4j.core.config.plugins.PluginElement;
-import org.apache.logging.log4j.core.config.plugins.PluginFactory;
-import org.apache.logging.log4j.status.StatusLogger;
+import java.security.KeyManagementException;
+import java.security.KeyStoreException;
+import java.security.NoSuchAlgorithmException;
+import java.security.UnrecoverableKeyException;
 
 /**
  *  SSL Configuration
@@ -45,12 +44,15 @@ public class SslConfiguration {
     private final TrustStoreConfiguration trustStoreConfig;
     private final SSLContext sslContext;
     private final String protocol;
+    private final boolean verifyHostName;
 
-    private SslConfiguration(final String protocol, final KeyStoreConfiguration keyStoreConfig,
-            final TrustStoreConfiguration trustStoreConfig) {
+    private SslConfiguration(
+            final String protocol, final KeyStoreConfiguration keyStoreConfig,
+            final TrustStoreConfiguration trustStoreConfig, final boolean verifyHostName) {
         this.keyStoreConfig = keyStoreConfig;
         this.trustStoreConfig = trustStoreConfig;
         this.protocol = protocol == null ? SslConfigurationDefaults.PROTOCOL : protocol;
+        this.verifyHostName = verifyHostName;
         this.sslContext = this.createSslContext();
     }
 
@@ -62,6 +64,10 @@ public class SslConfiguration {
         return sslContext.getServerSocketFactory();
     }
 
+    public boolean isVerifyHostName() {
+        return verifyHostName;
+    }
+
     private SSLContext createSslContext() {
         SSLContext context = null;
 
@@ -224,7 +230,16 @@ public class SslConfiguration {
             trustStoreEquals = trustStoreConfig == config.trustStoreConfig;
         }
 
-        return keyStoreEquals && trustStoreEquals;
+        return keyStoreEquals && trustStoreEquals && verifyHostName == config.verifyHostName;
+    }
+
+    /**
+     * @deprecated use {@link #createSSLConfiguration(String, KeyStoreConfiguration, TrustStoreConfiguration, boolean)}
+     */
+    @Deprecated
+    public static SslConfiguration createSSLConfiguration(
+            final String protocol, final KeyStoreConfiguration keyStoreConfig, final TrustStoreConfiguration trustStoreConfig) {
+        return createSSLConfiguration(protocol, keyStoreConfig, trustStoreConfig, false);
     }
 
     /**
@@ -232,15 +247,17 @@ public class SslConfiguration {
      * @param protocol The protocol, see http://docs.oracle.com/javase/7/docs/technotes/guides/security/StandardNames.html#SSLContext
      * @param keyStoreConfig The KeyStoreConfiguration.
      * @param trustStoreConfig The TrustStoreConfiguration.
+     * @param verifyHostName Whether to enable TLS hostname verification
      * @return a new SslConfiguration
      */
     @PluginFactory
     public static SslConfiguration createSSLConfiguration(
             // @formatter:off
             @PluginAttribute("protocol") final String protocol,
-            @PluginElement("KeyStore") final KeyStoreConfiguration keyStoreConfig, 
-            @PluginElement("TrustStore") final TrustStoreConfiguration trustStoreConfig) {
+            @PluginElement("KeyStore") final KeyStoreConfiguration keyStoreConfig,
+            @PluginElement("TrustStore") final TrustStoreConfiguration trustStoreConfig,
+            @PluginAttribute("verifyHostName") final boolean verifyHostName) {
             // @formatter:on
-        return new SslConfiguration(protocol, keyStoreConfig, trustStoreConfig);
+        return new SslConfiguration(protocol, keyStoreConfig, trustStoreConfig, verifyHostName);
     }
 }
diff --git a/log4j-core/src/test/java/org/apache/logging/log4j/core/appender/SmtpAppenderTest.java b/log4j-core/src/test/java/org/apache/logging/log4j/core/appender/SmtpAppenderTest.java
index cead991..5d4383f 100644
--- a/log4j-core/src/test/java/org/apache/logging/log4j/core/appender/SmtpAppenderTest.java
+++ b/log4j-core/src/test/java/org/apache/logging/log4j/core/appender/SmtpAppenderTest.java
@@ -39,8 +39,6 @@ import static org.junit.Assert.*;
 public class SmtpAppenderTest {
 
     private static final String HOST = "localhost";
-    private static final int PORTNUM = AvailablePortFinder.getNextAvailable();
-    private static final String PORT = String.valueOf(PORTNUM);
 
     @Test
     public void testMessageFactorySetFrom() throws MessagingException {
@@ -131,10 +129,19 @@ public class SmtpAppenderTest {
 
     @Test
     public void testDelivery() {
-        final SmtpAppender appender = SmtpAppender.createAppender("Test",
-                "to@example.com", "cc@example.com", "bcc@example.com",
-                "from@example.com", "replyTo@example.com", "Subject", null,
-                HOST, PORT, null, null, "false", "3", null, null, "true");
+        int smtpPort = AvailablePortFinder.getNextAvailable();
+        final SmtpAppender appender = SmtpAppender.newBuilder()
+                .setName("Test")
+                .setTo("to@example.com")
+                .setCc("cc@example.com")
+                .setBcc("bcc@example.com")
+                .setFrom("from@example.com")
+                .setReplyTo("replyTo@example.com")
+                .setSubject("Subject")
+                .setSmtpHost(HOST)
+                .setSmtpPort(smtpPort)
+                .setBufferSize(3)
+                .build();
         appender.start();
 
         final LoggerContext context = (LoggerContext) LogManager.getContext();
@@ -143,7 +150,7 @@ public class SmtpAppenderTest {
         root.setAdditive(false);
         root.setLevel(Level.DEBUG);
 
-        final SimpleSmtpServer server = SimpleSmtpServer.start(PORTNUM);
+        final SimpleSmtpServer server = SimpleSmtpServer.start(smtpPort);
 
         root.debug("Debug message #1");
         root.debug("Debug message #2");
diff --git a/src/changes/changes.xml b/src/changes/changes.xml
index c5d89a9..4a8a8c7 100644
--- a/src/changes/changes.xml
+++ b/src/changes/changes.xml
@@ -23,6 +23,12 @@
     <title>Changes</title>
   </properties>
   <body>
+    <release version="2.3.2" date="2021-12-xx" description="GA Release 2.3.2">
+      <action issue="LOG4J2-2819" dev="mattsicker" type="fix">
+        Add support for specifying an SSL configuration for SmtpAppender.
+        Backport fix for CVE-2020-9488 to allow SSL/TLS hostname verification.
+      </action>
+    </release>
     <release version="2.3.1" date="2021-12-20" description="GA Release 2.3.1">
       <action issue="LOG4J2-3242" dev="rgoers, ggregory" type="fix">
         Limit JNDI to the java protocol only. JNDI will remain disabled by default. Rename JNDI enablement property from
diff --git a/src/site/xdoc/manual/appenders.xml b/src/site/xdoc/manual/appenders.xml
index b451501..76ce2b7 100644
--- a/src/site/xdoc/manual/appenders.xml
+++ b/src/site/xdoc/manual/appenders.xml
@@ -2919,7 +2919,7 @@ public class JpaLogEntity extends AbstractLogEventWrapperEntity {
   <Appenders>
     <Socket name="socket" host="localhost" port="9500">
       <SerializedLayout />
-      <SSL>
+      <SSL verifyHostName="true">
         <KeyStore location="log4j2-keystore.jks" password="changeme"/>
         <TrustStore location="truststore.jks" password="changeme"/>
       </SSL>
@@ -3143,7 +3143,7 @@ public class JpaLogEntity extends AbstractLogEventWrapperEntity {
 <Configuration status="warn" name="MyApp" packages="">
   <Appenders>
     <TLSSyslog name="bsd" host="localhost" port="6514">
-      <SSL>
+      <SSL verifyHostName="true">
         <KeyStore location="log4j2-keystore.jks" password="changeme"/>
         <TrustStore location="truststore.jks" password="changeme"/>
       </SSL>