You are viewing a plain text version of this content. The canonical link for it is here.
Posted to notifications@james.apache.org by ka...@apache.org on 2022/10/11 12:32:51 UTC
[james-project] branch master updated: JAMES-3832 RemoteDelivery will do TLS host name verification when contacting remote mail servers.
This is an automated email from the ASF dual-hosted git repository.
kao pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/james-project.git
The following commit(s) were added to refs/heads/master by this push:
new a1710453f0 JAMES-3832 RemoteDelivery will do TLS host name verification when contacting remote mail servers.
a1710453f0 is described below
commit a1710453f0fe2066b73b78879262229e4aa31eea
Author: Karsten Otto <ka...@akquinet.de>
AuthorDate: Wed Oct 5 14:08:06 2022 +0200
JAMES-3832 RemoteDelivery will do TLS host name verification when contacting remote mail servers.
The RemoteDelivery mailet configuration gets a new property verifyServerIdentity for this, enabled by default.
---
.../docs/modules/ROOT/partials/RemoteDelivery.adoc | 17 ++++++---
.../james/transport/mailets/RemoteDelivery.java | 21 +++++++----
.../remote/delivery/MailDelivrerToHost.java | 4 +-
.../delivery/RemoteDeliveryConfiguration.java | 12 ++++++
.../delivery/RemoteDeliveryConfigurationTest.java | 43 ++++++++++++++++++++++
upgrade-instructions.md | 15 ++++++++
6 files changed, 98 insertions(+), 14 deletions(-)
diff --git a/server/apps/distributed-app/docs/modules/ROOT/partials/RemoteDelivery.adoc b/server/apps/distributed-app/docs/modules/ROOT/partials/RemoteDelivery.adoc
index 18c3a5796f..f450898607 100644
--- a/server/apps/distributed-app/docs/modules/ROOT/partials/RemoteDelivery.adoc
+++ b/server/apps/distributed-app/docs/modules/ROOT/partials/RemoteDelivery.adoc
@@ -46,6 +46,7 @@ to switch the connection to a TLS-protected connection before issuing any login
* *sslEnable* (optional) - a Boolean (true/false) indicating whether to use SSL to connect and use the SSL port unless
explicitly overridden. Default is false. Setting up to true will result in delivery attempts in SMTPS on port 465 with a fallback
to SMTP on port 25. The trust-store if needed can be customized by *-Djavax.net.ssl.trustStore=/root/conf/keystore*.
+* *verifyServerIdentity* (optional) - a Boolean (true/false) indicating whether to match the remote server name against its certificate on TLS connections. Default is true. Disabling this runs the risk of someone spoofing a legitimate server and intercepting mails, but may be necessary to contact servers that have strange certificates, no DNS entries, are reachable by IP address only, and similar edge cases.
* *gateway* (optional) - a String containing a comma separated list of patterns defining the gateway servers to be used to
deliver mail regardless of the recipient address. If multiple gateway servers are defined, each will be tried in definition order
until delivery is successful. If none are successful, the mail is bounced. The pattern is *host[:port]* where:
@@ -75,16 +76,20 @@ the ability to perform their own problem resolutions.
==== Security
-You can use the *mail.smtp.ssl.enable* javax property described above to force SMTP outgoing delivery to default to SSL
-encrypted traffic.
+You can use the *sslEnable* parameter described above to force SMTP outgoing delivery to default to SSL encrypted traffic (SMTPS).
+This is a shortcut for the *mail.smtps.ssl.enable* javax property.
-When enabling SSL, you might need to specify *mail.smtp.ssl.checkserveridentity* and *mail.smtp.ssl.trust*
-properties. You can also control ciphersuites and protocols via *mail.smtp.ssl.ciphersuites* and
-*mail.smtp.ssl.protocols* properties.
+When enabling SSL, you might need to specify the *mail.smtps.ssl.trust* property as well.
+You can also control ciphersuites and protocols via *mail.smtps.ssl.ciphersuites* and
+*mail.smtps.ssl.protocols* properties.
-*startTls* can alternatively be enabled upon sending a mail. For this, use the *startTls* configuration property, serving as a shortcut for
+StartTLS can alternatively be enabled upon sending a mail. For this, use the *startTls* parameter, serving as a shortcut for the
javax *mail.smtp.starttls.enable* property. Depending on how strict your security policy is, you might consider
*mail.smtp.starttls.required* as well. Be aware that configuring trust will then be required.
+You can also use other javax properties for StartTLS, but their property prefix must be *mail.smtp.ssl.* in this case.
+
+James enables server identity verification by default. In certain rare edge cases you might disable it via the *verifyServerIdentity* parameter,
+or use the *mail.smtps.ssl.checkserveridentity* and *mail.smtp.ssl.checkserveridentity* javax properties for fine control.
Read https://javaee.github.io/javamail/docs/api/com/sun/mail/smtp/package-summary.html[*com.sun.mail.smtp*]
for full information.
diff --git a/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/RemoteDelivery.java b/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/RemoteDelivery.java
index 83984c892f..93f140dcf3 100644
--- a/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/RemoteDelivery.java
+++ b/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/RemoteDelivery.java
@@ -95,6 +95,10 @@ import com.google.common.collect.ImmutableList;
* <li><b>sslEnable</b> (optional) - a Boolean (true/false) indicating whether to use SSL to connect and use the SSL port unless
* explicitly overridden. Default is false. The trust-store if needed can be customized by
* <strong>-Djavax.net.ssl.trustStore=/root/conf/keystore</strong>.</li>
+ * <li><b>verifyServerIdentity</b> (optional) - a Boolean (true/false) indicating whether to match the remote server name against its
+ * certificate on TLS connections. Default is true. Disabling this runs the risk of someone spoofing a legitimate server and intercepting
+ * mails, but may be necessary to contact servers that have strange certificates, no DNS entries, are reachable by IP address only,
+ * and similar edge cases.</li>
* <li><b>gateway</b> (optional) - a String containing a comma separated list of patterns defining the gateway servers to be used to
* deliver mail regardless of the recipient address. If multiple gateway servers are defined, each will be tried in definition order
* until delivery is successful. If none are successful, the mail is bounced. The pattern is <code>host[:port]</code> where:
@@ -125,14 +129,17 @@ import com.google.common.collect.ImmutableList;
* </ul>
* <br/>
* <b>Security:</b><br/>
- * You can use the <i>mail.smtp.ssl.enable</i> javax property described above to force SMTP outgoing delivery to default to SSL
- * encrypted traffic. <br/>
- * When enabling SSL, you might need to specify <i>mail.smtp.ssl.checkserveridentity</i> and <i>mail.smtp.ssl.trust</i>
- * properties. You can also control ciphersuites and protocols via <i>mail.smtp.ssl.ciphersuites</i> and
- * <i>mail.smtp.ssl.protocols</i> properties.<br/>
- * <b>startTls</b> can alternatively be enabled upon sending a mail. For this, use the <i>startTls</i> configuration property, serving as a shortcut for
+ * You can use the <b>sslEnable</b> parameter described above to force SMTP outgoing delivery to default to SSL encrypted traffic (SMTPS).
+ * This is a shortcut for the <i>mail.smtps.ssl.enable</i> javax property.<br/>
+ * When enabling SSL, you might need to specify the <i>mail.smtps.ssl.trust</i> property as well.
+ * You can also control ciphersuites and protocols via *mail.smtps.ssl.ciphersuites* and
+ * <b>mail.smtps.ssl.protocols</b> properties.<br/>
+ * StartTLS can alternatively be enabled upon sending a mail. For this, use the <b>startTls</b> parameter, serving as a shortcut for the
* javax <i>mail.smtp.starttls.enable</i> property. Depending on how strict your security policy is, you might consider
- * <i>mail.smtp.starttls.required</i> as well. Be aware that configuring trust will then be required.<br/>
+ * <i>mail.smtp.starttls.required</i> as well. Be aware that configuring trust will then be required.
+ * You can also use other javax properties for StartTLS, but their property prefix must be <i>mail.smtp.ssl.</i> in this case.<br/>
+ * James enables server identity verification by default. In certain rare edge cases you might disable it via the <b>verifyServerIdentity</b> parameter,
+ * or use the <i>mail.smtps.ssl.checkserveridentity</i> and <i>mail.smtp.ssl.checkserveridentity</i> javax properties for fine control.<br/>
* Read <a href="https://javaee.github.io/javamail/docs/api/com/sun/mail/smtp/package-summary.html"><code>com.sun.mail.smtp</code></a>
* for full information.
*/
diff --git a/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remote/delivery/MailDelivrerToHost.java b/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remote/delivery/MailDelivrerToHost.java
index cf75232d89..e816d782c7 100644
--- a/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remote/delivery/MailDelivrerToHost.java
+++ b/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remote/delivery/MailDelivrerToHost.java
@@ -253,8 +253,10 @@ public class MailDelivrerToHost {
private void connect(HostAddress outgoingMailServer, SMTPTransport transport) throws MessagingException {
if (configuration.getAuthUser() != null) {
transport.connect(outgoingMailServer.getHostName(), configuration.getAuthUser(), configuration.getAuthPass());
+ } else if (configuration.isConnectByHostname()) {
+ transport.connect(outgoingMailServer.getHostName(), null, null);
} else {
- transport.connect();
+ transport.connect(); // connect via IP address instead of host name
}
}
diff --git a/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remote/delivery/RemoteDeliveryConfiguration.java b/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remote/delivery/RemoteDeliveryConfiguration.java
index a0cffb83cf..262eec7dec 100644
--- a/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remote/delivery/RemoteDeliveryConfiguration.java
+++ b/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remote/delivery/RemoteDeliveryConfiguration.java
@@ -55,6 +55,7 @@ public class RemoteDeliveryConfiguration {
public static final String GATEWAY = "gateway";
public static final String SSL_ENABLE = "sslEnable";
public static final String START_TLS = "startTLS";
+ public static final String VERIFY_SERVER_IDENTITY = "verifyServerIdentity";
public static final String BOUNCE_PROCESSOR = "bounceProcessor";
public static final String SENDPARTIAL = "sendpartial";
public static final String TIMEOUT = "timeout";
@@ -75,6 +76,7 @@ public class RemoteDeliveryConfiguration {
private final boolean usePriority;
private final boolean startTLS;
private final boolean isSSLEnable;
+ private final boolean verifyServerIdentity;
private final boolean isBindUsed;
private final boolean sendPartial;
private final int maxRetries;
@@ -96,6 +98,7 @@ public class RemoteDeliveryConfiguration {
isDebug = MailetUtil.getInitParameter(mailetConfig, DEBUG).orElse(false);
startTLS = MailetUtil.getInitParameter(mailetConfig, START_TLS).orElse(false);
isSSLEnable = MailetUtil.getInitParameter(mailetConfig, SSL_ENABLE).orElse(false);
+ verifyServerIdentity = MailetUtil.getInitParameter(mailetConfig, VERIFY_SERVER_IDENTITY).orElse(true);
usePriority = MailetUtil.getInitParameter(mailetConfig, USE_PRIORITY).orElse(false);
sendPartial = MailetUtil.getInitParameter(mailetConfig, SENDPARTIAL).orElse(false);
outGoingQueueName = Optional.ofNullable(mailetConfig.getInitParameter(OUTGOING))
@@ -239,6 +242,7 @@ public class RemoteDeliveryConfiguration {
props.put("mail." + protocol + ".sendpartial", String.valueOf(sendPartial));
props.put("mail." + protocol + ".localhost", heloNameProvider.getHeloName());
props.put("mail." + protocol + ".starttls.enable", String.valueOf(startTLS));
+ props.put("mail." + protocol + ".ssl.checkserveridentity", String.valueOf(verifyServerIdentity));
if (isBindUsed()) {
props.put("mail." + protocol + ".localaddress", bindAddress);
}
@@ -304,6 +308,14 @@ public class RemoteDeliveryConfiguration {
public boolean isSSLEnable() {
return isSSLEnable;
}
+
+ public boolean isVerifyServerIdentity() {
+ return verifyServerIdentity;
+ }
+
+ public boolean isConnectByHostname() {
+ return (isSSLEnable() || isStartTLS()) && isVerifyServerIdentity();
+ }
public HeloNameProvider getHeloNameProvider() {
return heloNameProvider;
diff --git a/server/mailet/mailets/src/test/java/org/apache/james/transport/mailets/remote/delivery/RemoteDeliveryConfigurationTest.java b/server/mailet/mailets/src/test/java/org/apache/james/transport/mailets/remote/delivery/RemoteDeliveryConfigurationTest.java
index 32a03d30eb..4facb265ba 100644
--- a/server/mailet/mailets/src/test/java/org/apache/james/transport/mailets/remote/delivery/RemoteDeliveryConfigurationTest.java
+++ b/server/mailet/mailets/src/test/java/org/apache/james/transport/mailets/remote/delivery/RemoteDeliveryConfigurationTest.java
@@ -316,6 +316,41 @@ class RemoteDeliveryConfigurationTest {
assertThat(new RemoteDeliveryConfiguration(mailetConfig, mock(DomainList.class)).isSSLEnable()).isFalse();
}
+ @Test
+ void isVerifyServerIdentityShouldBeTrueByDefault() {
+ FakeMailetConfig mailetConfig = FakeMailetConfig.builder()
+ .build();
+
+ assertThat(new RemoteDeliveryConfiguration(mailetConfig, mock(DomainList.class)).isVerifyServerIdentity()).isTrue();
+ }
+
+ @Test
+ void isVerifyServerIdentityShouldBeTrueIfSpecified() {
+ FakeMailetConfig mailetConfig = FakeMailetConfig.builder()
+ .setProperty(RemoteDeliveryConfiguration.VERIFY_SERVER_IDENTITY, "true")
+ .build();
+
+ assertThat(new RemoteDeliveryConfiguration(mailetConfig, mock(DomainList.class)).isVerifyServerIdentity()).isTrue();
+ }
+
+ @Test
+ void isVerifyServerIdentityShouldBeFalseIfSpecified() {
+ FakeMailetConfig mailetConfig = FakeMailetConfig.builder()
+ .setProperty(RemoteDeliveryConfiguration.VERIFY_SERVER_IDENTITY, "false")
+ .build();
+
+ assertThat(new RemoteDeliveryConfiguration(mailetConfig, mock(DomainList.class)).isVerifyServerIdentity()).isFalse();
+ }
+
+ @Test
+ void isVerifyServerIdentityShouldBeTrueIfParsingException() {
+ FakeMailetConfig mailetConfig = FakeMailetConfig.builder()
+ .setProperty(RemoteDeliveryConfiguration.VERIFY_SERVER_IDENTITY, "invalid")
+ .build();
+
+ assertThat(new RemoteDeliveryConfiguration(mailetConfig, mock(DomainList.class)).isVerifyServerIdentity()).isTrue();
+ }
+
@Test
void isBindUsedShouldBeFalseByDefault() {
FakeMailetConfig mailetConfig = FakeMailetConfig.builder()
@@ -759,6 +794,7 @@ class RemoteDeliveryConfigurationTest {
assertThat(properties)
.containsOnly(MapEntry.entry("mail.smtp.ssl.enable", "false"),
+ MapEntry.entry("mail.smtp.ssl.checkserveridentity", "true"),
MapEntry.entry("mail.smtp.sendpartial", "false"),
MapEntry.entry("mail.smtp.ehlo", "true"),
MapEntry.entry("mail.smtp.connectiontimeout", "60000"),
@@ -777,6 +813,7 @@ class RemoteDeliveryConfigurationTest {
.setProperty(RemoteDeliveryConfiguration.SENDPARTIAL, "true")
.setProperty(RemoteDeliveryConfiguration.CONNECTIONTIMEOUT, String.valueOf(connectionTimeout))
.setProperty(RemoteDeliveryConfiguration.START_TLS, "true")
+ .setProperty(RemoteDeliveryConfiguration.VERIFY_SERVER_IDENTITY, "false")
.setProperty(RemoteDeliveryConfiguration.HELO_NAME, helo)
.build();
@@ -785,6 +822,7 @@ class RemoteDeliveryConfigurationTest {
assertThat(properties)
.containsOnly(MapEntry.entry("mail.smtp.ssl.enable", "false"),
+ MapEntry.entry("mail.smtp.ssl.checkserveridentity", "false"),
MapEntry.entry("mail.smtp.sendpartial", "true"),
MapEntry.entry("mail.smtp.ehlo", "true"),
MapEntry.entry("mail.smtp.connectiontimeout", String.valueOf(connectionTimeout)),
@@ -803,6 +841,7 @@ class RemoteDeliveryConfigurationTest {
.setProperty(RemoteDeliveryConfiguration.SENDPARTIAL, "true")
.setProperty(RemoteDeliveryConfiguration.CONNECTIONTIMEOUT, String.valueOf(connectionTimeout))
.setProperty(RemoteDeliveryConfiguration.START_TLS, "true")
+ .setProperty(RemoteDeliveryConfiguration.VERIFY_SERVER_IDENTITY, "false")
.setProperty(RemoteDeliveryConfiguration.HELO_NAME, helo)
.setProperty(RemoteDeliveryConfiguration.GATEWAY, "gateway.domain.com")
.setProperty(RemoteDeliveryConfiguration.GATEWAY_USERNAME, "user")
@@ -814,6 +853,7 @@ class RemoteDeliveryConfigurationTest {
assertThat(properties)
.containsOnly(MapEntry.entry("mail.smtp.ssl.enable", "false"),
+ MapEntry.entry("mail.smtp.ssl.checkserveridentity", "false"),
MapEntry.entry("mail.smtp.sendpartial", "true"),
MapEntry.entry("mail.smtp.ehlo", "true"),
MapEntry.entry("mail.smtp.connectiontimeout", String.valueOf(connectionTimeout)),
@@ -837,6 +877,7 @@ class RemoteDeliveryConfigurationTest {
assertThat(properties)
.containsOnly(MapEntry.entry("mail.smtps.ssl.enable", "true"),
+ MapEntry.entry("mail.smtps.ssl.checkserveridentity", "true"),
MapEntry.entry("mail.smtps.sendpartial", "false"),
MapEntry.entry("mail.smtps.ehlo", "true"),
MapEntry.entry("mail.smtps.connectiontimeout", "60000"),
@@ -852,6 +893,7 @@ class RemoteDeliveryConfigurationTest {
int connectionTimeout = 1856;
FakeMailetConfig mailetConfig = FakeMailetConfig.builder()
.setProperty(RemoteDeliveryConfiguration.SSL_ENABLE, "true")
+ .setProperty(RemoteDeliveryConfiguration.VERIFY_SERVER_IDENTITY, "false")
.setProperty(RemoteDeliveryConfiguration.SENDPARTIAL, "true")
.setProperty(RemoteDeliveryConfiguration.CONNECTIONTIMEOUT, String.valueOf(connectionTimeout))
.setProperty(RemoteDeliveryConfiguration.START_TLS, "false")
@@ -863,6 +905,7 @@ class RemoteDeliveryConfigurationTest {
assertThat(properties)
.containsOnly(MapEntry.entry("mail.smtps.ssl.enable", "true"),
+ MapEntry.entry("mail.smtps.ssl.checkserveridentity", "false"),
MapEntry.entry("mail.smtps.sendpartial", "true"),
MapEntry.entry("mail.smtps.ehlo", "true"),
MapEntry.entry("mail.smtps.connectiontimeout", String.valueOf(connectionTimeout)),
diff --git a/upgrade-instructions.md b/upgrade-instructions.md
index d27d2c70c4..0beed75702 100644
--- a/upgrade-instructions.md
+++ b/upgrade-instructions.md
@@ -21,6 +21,21 @@ Change list:
- [Migration to OpenSearch](#migration-to-opensearch)
- [Deleted message vault is now deactivated by default](#deleted-message-vault-is-now-deactivated-by-default)
- [SortOrder addition in Identity](#sortorder-addition-in-identity)
+- [TLS host name verification is now enabled by default](#tls-host-name-verification-is-now-enabled-by-default)
+
+### TLS host name verification is now enabled by default
+
+Date: 06/10/2022
+
+When establishing an SMTPS or StartTLS connection during remote mail delivery, James will now check the remote server's host/domain name against its certificate (RFC 2595). If they do not match, the connection fails as a temporary delivery error.
+
+This prevents attackers from spoofing legitimate servers and intercepting mails. However, it may prevent James from connecting to servers that have strange certificates, no DNS entries, are reachable by IP address only, and similar edge cases.
+
+Users requiring such connectivity may disable this check within `mailetcontainer.xml` at the RemoteDelivery mailet configuration:
+
+```
+<verifyServerIdentity>false</verifyServerIdentity>
+```
### SortOrder addition in Identity
---------------------------------------------------------------------
To unsubscribe, e-mail: notifications-unsubscribe@james.apache.org
For additional commands, e-mail: notifications-help@james.apache.org