Skip to content
Open
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
2 changes: 2 additions & 0 deletions copyright-exclude
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ mailhandler/src/main/resources/META-INF/hk2-locator/default
docker/JTReportParser/
providers/angus-mail/src/test/resources/org/eclipse/angus/mail/util/paramdatanostrict
providers/angus-mail/src/test/resources/org/eclipse/angus/mail/util/paramdata
providers/angus-mail/src/test/resources/keystore.jks
providers/angus-mail/src/test/resources/test-hosts
.txt
.TXT
www/
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 1997, 2023 Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 1997, 2025 Oracle and/or its affiliates. All rights reserved.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0, which is available at
Expand All @@ -18,10 +18,12 @@

import javax.net.ssl.KeyManager;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLEngine;
import javax.net.ssl.SSLSocket;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManager;
import javax.net.ssl.TrustManagerFactory;
import javax.net.ssl.X509ExtendedTrustManager;
import javax.net.ssl.X509TrustManager;
import java.io.IOException;
import java.net.InetAddress;
Expand Down Expand Up @@ -334,51 +336,120 @@ public synchronized Socket createSocket(String s, int i)
*
* @author Stephan Sann
*/
private class MailTrustManager implements X509TrustManager {
private class MailTrustManager extends X509ExtendedTrustManager {

/**
* A TrustManager to pass method calls to
* A TrustManager to delegate to
*/
private X509TrustManager adapteeTrustManager = null;
private final X509ExtendedTrustManager adapteeTrustManager;

/**
* Initializes a new TrustManager instance.
*/
private MailTrustManager() throws GeneralSecurityException {
TrustManagerFactory tmf = TrustManagerFactory.getInstance("X509");
tmf.init((KeyStore) null);
adapteeTrustManager = (X509TrustManager) tmf.getTrustManagers()[0];

TrustManager tm = tmf.getTrustManagers()[0];

if (tm instanceof X509ExtendedTrustManager) {
adapteeTrustManager = (X509ExtendedTrustManager) tm;
} else {
// Should not happen, but wrap X509TrustManager if needed
adapteeTrustManager = new X509ExtendedTrustManager() {
private final X509TrustManager base = (X509TrustManager) tm;

@Override
public void checkClientTrusted(X509Certificate[] chain, String authType, Socket socket)
throws CertificateException {
base.checkClientTrusted(chain, authType);
}

@Override
public void checkClientTrusted(X509Certificate[] chain, String authType, SSLEngine engine)
throws CertificateException {
base.checkClientTrusted(chain, authType);
}

@Override
public void checkServerTrusted(X509Certificate[] chain, String authType, Socket socket)
throws CertificateException {
base.checkServerTrusted(chain, authType);
}

@Override
public void checkServerTrusted(X509Certificate[] chain, String authType, SSLEngine engine)
throws CertificateException {
base.checkServerTrusted(chain, authType);
}

@Override
public void checkClientTrusted(X509Certificate[] chain, String authType)
throws CertificateException {
base.checkClientTrusted(chain, authType);
}

@Override
public void checkServerTrusted(X509Certificate[] chain, String authType)
throws CertificateException {
base.checkServerTrusted(chain, authType);
}

@Override
public X509Certificate[] getAcceptedIssuers() {
return base.getAcceptedIssuers();
}
};
}
}

private boolean shouldDelegate() {
return !(isTrustAllHosts() || getTrustedHosts() != null);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Double check me on this, but i think for X509ExtendedTrustManager methods this is not correct when EIA is enabled and we are trusting a list of hosts. In that case we should short circuit if matching, otherwise delegate.

Essentially we should call MailSSLSocketFactory::isServerTrusted to determine if we delegate. Or rather update this private method to take a host name.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We might have to remove MailSSLSocketFactory::isServerTrusted call from SocketFetcher and just have the call happen in the trustmanager. The reason I think we have to do that is because we don't reflective invoke EIA from SocketFetcher.

}

/* (non-Javadoc)
* @see javax.net.ssl.X509TrustManager#checkClientTrusted(
* java.security.cert.X509Certificate[], java.lang.String)
*/
@Override
public void checkClientTrusted(X509Certificate[] certs, String authType)
throws CertificateException {
if (!(isTrustAllHosts() || getTrustedHosts() != null))
if (shouldDelegate())
adapteeTrustManager.checkClientTrusted(certs, authType);
}

/* (non-Javadoc)
* @see javax.net.ssl.X509TrustManager#checkServerTrusted(
* java.security.cert.X509Certificate[], java.lang.String)
*/
@Override
public void checkServerTrusted(X509Certificate[] certs, String authType)
throws CertificateException {

if (!(isTrustAllHosts() || getTrustedHosts() != null))
if (shouldDelegate())
adapteeTrustManager.checkServerTrusted(certs, authType);
}

/* (non-Javadoc)
* @see javax.net.ssl.X509TrustManager#getAcceptedIssuers()
*/
@Override
public X509Certificate[] getAcceptedIssuers() {
return adapteeTrustManager.getAcceptedIssuers();
}

@Override
public void checkClientTrusted(X509Certificate[] chain, String authType, Socket socket)
throws CertificateException {
if (shouldDelegate())
adapteeTrustManager.checkClientTrusted(chain, authType, socket);
}

@Override
public void checkClientTrusted(X509Certificate[] chain, String authType, SSLEngine engine)
throws CertificateException {
if (shouldDelegate())
adapteeTrustManager.checkClientTrusted(chain, authType, engine);
}

@Override
public void checkServerTrusted(X509Certificate[] chain, String authType, Socket socket)
throws CertificateException {
if (shouldDelegate())
adapteeTrustManager.checkServerTrusted(chain, authType, socket);
}

@Override
public void checkServerTrusted(X509Certificate[] chain, String authType, SSLEngine engine)
throws CertificateException {
if (shouldDelegate())
adapteeTrustManager.checkServerTrusted(chain, authType, engine);
}
}

}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 1997, 2024 Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 1997, 2025 Oracle and/or its affiliates. All rights reserved.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0, which is available at
Expand Down Expand Up @@ -628,15 +628,15 @@ private static void configureSSLSocket(Socket socket, String host,
Arrays.asList(sslsocket.getEnabledCipherSuites()));
}

boolean checkServerIdentity =
PropUtil.getBooleanProperty(props, prefix + ".ssl.checkserveridentity", true);
try {
/*
* Check server identity and trust.
* See: JDK-8062515 and JDK-7192189
* LDAPS requires the same regex handling as we need
*/
String eia = PropUtil.getBooleanProperty(props,
prefix + ".ssl.checkserveridentity", true)
? "LDAPS" : (String) null;
String eia = checkServerIdentity ? "LDAPS" : (String) null;
SSLParameters params = sslsocket.getSSLParameters();
params.setEndpointIdentificationAlgorithm(eia);
sslsocket.setSSLParameters(params);
Expand Down Expand Up @@ -679,7 +679,7 @@ private static void configureSSLSocket(Socket socket, String host,

if (sf instanceof MailSSLSocketFactory) {
MailSSLSocketFactory msf = (MailSSLSocketFactory) sf;
if (!msf.isServerTrusted(host, sslsocket)) {
if (checkServerIdentity && !msf.isServerTrusted(host, sslsocket)) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe all of this code for MailSSLSocketFactory should be removed from SocketFetcher. The isServerTrusted should be invoked in all TrustManager methods and the result should be the decision to forward the call.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, I get your point and I am working on it.

throw cleanupAndThrow(sslsocket,
new IOException("Server is not trusted: " + host));
}
Expand Down
6 changes: 6 additions & 0 deletions doc/src/main/resources/docs/CHANGES.txt
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,12 @@ for the Eclipse EE4J Angus Mail project:

https://github.com/eclipse-ee4j/angus-mail/issues/<bug-number>

CHANGES IN THE 2.1.0 RELEASE
----------------------------
The following bugs have been fixed in the 2.1.0 release.

187: "mail.smtp.ssl.trust" seems to be broken

CHANGES IN THE 2.0.5 RELEASE
----------------------------
The following bugs have been fixed in the 2.0.5 release.
Expand Down
13 changes: 13 additions & 0 deletions doc/src/main/resources/docs/COMPAT.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,19 @@

Angus Mail ${angus-mail.version} release
------------------------------

-- Angus Mail 2.1.0 --

- MailTrustManager updated to extend X509ExtendedTrustManager

The internal MailTrustManager implementation has been updated to extend
X509ExtendedTrustManager instead of X509TrustManager. This allows Angus Mail
to pass the connected SSLSocket or SSLEngine to the TrustManager during
certificate validation.

As a result of this, setting mail.<protocol>.ssl.checkserveridentity = true
will check trusted hosts.

-- Angus Mail 2.0.5 --

- Resource File Duplication due to Jakartification
Expand Down
Loading