Skip to content

improve security of TLS handling in MimeMailers #219

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Jun 9, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions WHATSNEW
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,14 @@ Changes that could break older environments:
the behavior of 1.10.15.
Bugzilla Report 65756

* the <mail> task as well as MailLogger will now check the server
identity as specified by RFC 2595 when JavaMail is used in
combination with TLS (plain TLS or StartTLS).
The check can be disabled with a new MailLogger property
MailLogger.tls.checkserveridentity or a new <mail> task attribute
checkServerIdentity.
Bugzilla Report 69416

Fixed bugs:
-----------

Expand All @@ -40,6 +48,10 @@ Other changes:
"disallow".
Github Pull Request #216

* <mail> and MailLogger can now enforce the use of STARTLS rather
than silently fall back to unencrypted authentication via a new
MailLogger property and a new <mail> task attribute.
Bugzilla Report 69416

Changes from Ant 1.10.14 TO Ant 1.10.15
=======================================
Expand Down
17 changes: 17 additions & 0 deletions manual/Tasks/mail.html
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,23 @@ <h3>Parameters</h3>
1.8.0</em></td>
<td>No</td>
</tr>
<tr>
<td>requireStartTLS</td>
<td>(boolean) Whether the <code>STARTTLS</code> command used to switch to an encrypted
connection for authentication should be required.
<br/>Implicitly sets <q>enableStartTLS</q> to <q>true</q> if enabled. Requires JavaMail.
<br/><em>Since Ant 1.10.16</em></td>
<td>No; default is <q>false</q></td>
</tr>
<tr>
<td>checkServerIdentity</td>
<td>(boolean) Whether the server's identity shall be verified
during TLS handshake.
<br/> Ignored unless <q>ssl</q> or <q>enableStartTLS</q> is <q>true</q>.
Requires JavaMail.
<br/><em>Since Ant 1.10.16</em></td>
<td>No; default is <q>true</q></td>
</tr>
</table>

<h3>Note regarding the attributes containing email addresses</h3>
Expand Down
16 changes: 16 additions & 0 deletions manual/listeners.html
Original file line number Diff line number Diff line change
Expand Up @@ -259,6 +259,22 @@ <h3 id="MailLogger">MailLogger</h3>
1.8.0</em></td>
<td>No; default is <q>false</q></td>
</tr>
<tr>
<td><code>MailLogger.starttls.require</code></td>
<td>on or true if <code>STARTTLS</code> should be required
(requires JavaMail).
If <q>true</q> implicitly
sets <code>MailLogger.starttls.enable</code> to <q>true</q> as well.
<br/><em>Since Ant 1.10.16</em></td>
<td>No; default is <q>false</q></td>
</tr>
<tr>
<td><code>MailLogger.tls.checkserveridentity</code></td>
<td>on or true if the identity of the server shall be chcked
during the TLS handshake (requires JavaMail).
<br/><em>Since Ant 1.10.16</em></td>
<td>No; default is <q>true</q></td>
</tr>
<tr>
<td><code>MailLogger.properties.file</code></td>
<td>Filename of properties file that will override other values.</td>
Expand Down
27 changes: 27 additions & 0 deletions src/main/org/apache/tools/ant/listener/MailLogger.java
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,11 @@
* <li> MailLogger.charset [no default] - character set of email</li>
* <li> MailLogger.starttls.enable [default: false] - on or true if
* STARTTLS should be supported (requires Java or Jakarta Mail)</li>
* <li> MailLogger.starttls.require [default: false] - on or true if
* STARTTLS should be required (requires Java or Jakarta Mail)</li>
* <li> MailLogger.tls.checkserveridentity [default: true] - on or true if
* the identity of the server shall be chcked during the TLS handshake
* (requires Java or Jakarta Mail)</li>
* <li> MailLogger.properties.file [no default] - Filename of
* properties file that will override other values.</li>
* </ul>
Expand Down Expand Up @@ -153,6 +158,10 @@ public void buildFinished(BuildEvent event) {
"ssl", "off")))
.starttls(Project.toBoolean(getValue(properties,
"starttls.enable", "off")))
.starttlsRequired(Project.toBoolean(getValue(properties,
"starttls.require", "off")))
.checkTlsServerIdentity(Project.toBoolean(getValue(properties,
"tls.checkserveridentity", "on")))
.from(getValue(properties, "from", null))
.replytoList(getValue(properties, "replyto", ""))
.toList(getValue(properties, prefix + ".to", null))
Expand Down Expand Up @@ -299,6 +308,22 @@ public Values starttls(boolean starttls) {
this.starttls = starttls;
return this;
}
private boolean starttlsRequired;
public boolean starttlsRequired() {
return starttlsRequired;
}
public Values starttlsRequired(boolean required) {
this.starttlsRequired = required;
return required ? starttls(required) : this;
}
private boolean checkTlsServerIdentity = true;
public boolean checkTlsServerIdentity() {
return checkTlsServerIdentity;
}
public Values checkTlsServerIdentity(boolean check) {
this.checkTlsServerIdentity = check;
return this;
}
}

/**
Expand Down Expand Up @@ -400,6 +425,8 @@ private void sendMimeMail(Project project, Values values, String message) {
mailer.setPassword(values.password());
mailer.setSSL(values.ssl());
mailer.setEnableStartTLS(values.starttls());
mailer.setRequireStartTLS(values.starttlsRequired());
mailer.setCheckServerIdentity(values.checkTlsServerIdentity());
Message mymessage =
new Message(!values.body().isEmpty() ? values.body() : message);
mymessage.setProject(project);
Expand Down
29 changes: 29 additions & 0 deletions src/main/org/apache/tools/ant/taskdefs/email/EmailTask.java
Original file line number Diff line number Diff line change
Expand Up @@ -105,10 +105,14 @@ public String[] getValues() {
private boolean ssl = false;
/** indicate if the user wishes support for STARTTLS */
private boolean starttls = false;
private boolean requireStarttls = false;

/** ignore invalid recipients? */
private boolean ignoreInvalidRecipients = false;

/** more strict TLS server certificate check */
private boolean checkServerIdentity = true;

/**
* Set the user for SMTP auth; this requires JavaMail.
*
Expand Down Expand Up @@ -150,6 +154,29 @@ public void setEnableStartTLS(boolean b) {
this.starttls = b;
}

/**
* Set whether to require authentication to switch to a TLS
* connection via STARTTLS.
*
* @param b boolean; if true STARTTLS will be supported and required.
* @since Ant 1.10.16
*/
public void setRequireStartTLS(boolean b) {
this.requireStarttls = b;
if (b) {
setEnableStartTLS(b);
}
}

/**
* Whether the server's identity shall be verified during TLS handshake.
* @param b boolean; if true server identity will be checked.
* @since Ant 1.10.16
*/
public void setCheckServerIdentity(boolean b) {
this.checkServerIdentity = b;
}

/**
* Set the preferred encoding method.
*
Expand Down Expand Up @@ -557,6 +584,8 @@ public void execute() {
mailer.setPassword(password);
mailer.setSSL(ssl);
mailer.setEnableStartTLS(starttls);
mailer.setRequireStartTLS(requireStarttls);
mailer.setCheckServerIdentity(checkServerIdentity);
mailer.setMessage(message);
mailer.setFrom(from);
mailer.setReplyToList(replyToList);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -155,13 +155,22 @@ public void send() {
props.put("mail.smtp.socketFactory.port",
String.valueOf(port));
}
if (shouldCheckServerIdentity()) {
props.put("mail.smtp.ssl.checkserveridentity", "true");
}
}
if (user != null || password != null) {
props.put("mail.smtp.auth", "true");
auth = new SimpleAuthenticator(user, password);
}
if (isStartTLSEnabled()) {
props.put("mail.smtp.starttls.enable", "true");
if (isStartTLSRequired()) {
props.put("mail.smtp.starttls.required", "true");
}
if (shouldCheckServerIdentity()) {
props.put("mail.smtp.ssl.checkserveridentity", "true");
}
}
sesh = Session.getInstance(props, auth);

Expand Down
35 changes: 35 additions & 0 deletions src/main/org/apache/tools/ant/taskdefs/email/Mailer.java
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,9 @@ public abstract class Mailer {
// CheckStyle:VisibilityModifier ON
private boolean ignoreInvalidRecipients = false;
private boolean starttls = false;
private boolean requireStarttls = false;
private boolean portExplicitlySpecified = false;
private boolean checkServerIdentity = true;

/**
* Set the mail server.
Expand Down Expand Up @@ -136,6 +138,25 @@ protected boolean isStartTLSEnabled() {
return starttls;
}

/**
* Set whether to enforce authentication to switch to a TLS
* connection via STARTTLS.
* @param b boolean; if true STARTTLS will be required.
* @since Ant 1.10.16
*/
public void setRequireStartTLS(boolean b) {
this.requireStarttls = b;
}

/**
* Whether the server's identity shall be verified during TLS handshake.
* @param b boolean; if true server identity will be checked.
* @since Ant 1.10.16
*/
public void setCheckServerIdentity(boolean b) {
this.checkServerIdentity = b;
}

/**
* Set the message.
*
Expand Down Expand Up @@ -268,6 +289,20 @@ protected boolean shouldIgnoreInvalidRecipients() {
return ignoreInvalidRecipients;
}

/**
* @since Ant 1.10.16
*/
protected boolean shouldCheckServerIdentity() {
return starttls;
}

/**
* @since Ant 1.10.16
*/
protected boolean isStartTLSRequired() {
return requireStarttls;
}

/**
* Return the current Date in a format suitable for a SMTP date
* header.
Expand Down
9 changes: 9 additions & 0 deletions src/main/org/apache/tools/ant/taskdefs/email/MimeMailer.java
Original file line number Diff line number Diff line change
Expand Up @@ -157,13 +157,22 @@ public void send() {
props.put("mail.smtp.socketFactory.port",
String.valueOf(port));
}
if (shouldCheckServerIdentity()) {
props.put("mail.smtp.ssl.checkserveridentity", "true");
}
}
if (user != null || password != null) {
props.put("mail.smtp.auth", "true");
auth = new SimpleAuthenticator(user, password);
}
if (isStartTLSEnabled()) {
props.put("mail.smtp.starttls.enable", "true");
if (isStartTLSRequired()) {
props.put("mail.smtp.starttls.required", "true");
}
if (shouldCheckServerIdentity()) {
props.put("mail.smtp.ssl.checkserveridentity", "true");
}
}
sesh = Session.getInstance(props, auth);

Expand Down