diff --git a/services/src/main/java/org/keycloak/email/DefaultEmailSenderProvider.java b/services/src/main/java/org/keycloak/email/DefaultEmailSenderProvider.java index 74da6f0cbb..c4680f3f7c 100644 --- a/services/src/main/java/org/keycloak/email/DefaultEmailSenderProvider.java +++ b/services/src/main/java/org/keycloak/email/DefaultEmailSenderProvider.java @@ -35,6 +35,7 @@ import javax.mail.internet.AddressException; import javax.mail.internet.InternetAddress; import javax.mail.internet.MimeBodyPart; import javax.mail.internet.MimeMultipart; +import javax.net.ssl.SSLContext; import javax.net.ssl.SSLSocketFactory; import java.io.UnsupportedEncodingException; import java.security.KeyManagementException; @@ -48,6 +49,8 @@ import java.util.Properties; */ public class DefaultEmailSenderProvider implements EmailSenderProvider { + private static final String SUPPORTED_SSL_PROTOCOLS = getSupportedSslProtocols(); + private static final Logger logger = Logger.getLogger(DefaultEmailSenderProvider.class); private final KeycloakSession session; @@ -89,6 +92,8 @@ public class DefaultEmailSenderProvider implements EmailSenderProvider { } if (ssl || starttls) { + props.put("mail.smtp.ssl.protocols", SUPPORTED_SSL_PROTOCOLS); + setupTruststore(props); } @@ -171,7 +176,8 @@ public class DefaultEmailSenderProvider implements EmailSenderProvider { return user.getEmail(); } - private void setupTruststore(Properties props) throws NoSuchAlgorithmException, KeyManagementException { + private void setupTruststore(Properties props) { + boolean checkServerIdentity = true; JSSETruststoreConfigurator configurator = new JSSETruststoreConfigurator(session); @@ -180,12 +186,30 @@ public class DefaultEmailSenderProvider implements EmailSenderProvider { props.put("mail.smtp.ssl.socketFactory", factory); if (configurator.getProvider().getPolicy() == HostnameVerificationPolicy.ANY) { props.setProperty("mail.smtp.ssl.trust", "*"); + checkServerIdentity = false; } } + + if (checkServerIdentity) { + props.put("mail.smtp.ssl.checkserveridentity", "true"); + } } @Override public void close() { } + + private static String getSupportedSslProtocols() { + try { + String[] protocols = SSLContext.getDefault().getSupportedSSLParameters().getProtocols(); + if (protocols != null) { + return String.join(" ", protocols); + } + } catch (Exception e) { + logger.warn("Failed to get list of supported SSL protocols", e); + } + return null; + } + } diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/ssl/TrustStoreEmailTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/ssl/TrustStoreEmailTest.java index 0ff3679d70..f5c2b0085e 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/ssl/TrustStoreEmailTest.java +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/ssl/TrustStoreEmailTest.java @@ -40,6 +40,9 @@ import org.keycloak.testsuite.util.SslMailServer; import static org.junit.Assert.assertEquals; import org.keycloak.testsuite.arquillian.annotation.AuthServerContainerExclude.AuthServer; + +import java.util.Map; + import static org.keycloak.testsuite.util.MailAssert.assertEmailAndGetUrl; import static org.keycloak.testsuite.util.URLAssert.assertCurrentUrlStartsWith; @@ -164,4 +167,38 @@ public class TrustStoreEmailTest extends AbstractTestRealmKeycloakTest { assertEquals("You need to verify your email address to activate your account.", testRealmVerifyEmailPage.feedbackMessage().getText()); } + + @Test + public void verifyEmailWithSslWrongHostname() throws Exception { + UserRepresentation user = ApiUtil.findUserByUsername(testRealm(), "test-user@localhost"); + + RealmRepresentation realmRep = testRealm().toRepresentation(); + realmRep.getSmtpServer().put("host", "localhost.localdomain"); + testRealm().update(realmRep); + + try { + SslMailServer.startWithSsl(this.getClass().getClassLoader().getResource(SslMailServer.PRIVATE_KEY).getFile()); + accountManagement.navigateTo(); + loginPage.form().login(user.getUsername(), "password"); + + events.expectRequiredAction(EventType.SEND_VERIFY_EMAIL_ERROR) + .error(Errors.EMAIL_SEND_FAILED) + .user(user.getId()) + .client("account") + .detail(Details.USERNAME, "test-user@localhost") + .detail(Details.EMAIL, "test-user@localhost") + .removeDetail(Details.REDIRECT_URI) + .assertEvent(); + + // Email wasn't send + Assert.assertNull(SslMailServer.getLastReceivedMessage()); + + // Email wasn't send, but we won't notify end user about that. Admin is aware due to the error in the logs and the SEND_VERIFY_EMAIL_ERROR event. + assertEquals("You need to verify your email address to activate your account.", + testRealmVerifyEmailPage.feedbackMessage().getText()); + } finally { + realmRep.getSmtpServer().put("host", "localhost"); + testRealm().update(realmRep); + } + } }