From f7c289a4b4206256bde71c4c7eff9f5eb5e7ebc6 Mon Sep 17 00:00:00 2001 From: James Nord Date: Tue, 12 Mar 2024 11:03:09 +0000 Subject: [PATCH] Warn when using insecure auth (#275) Additionally if running in FIPS use an error in the form validation and do not support auth without SSL or TLS. This is handled when we attempt to send as the config is part of the global configuration and could leave the config in an non determinate state --- pom.xml | 18 ++---------- src/main/java/hudson/tasks/Mailer.java | 28 ++++++++++++++++-- .../mailer/tasks/i18n/Messages.properties | 2 ++ src/test/java/hudson/tasks/MailerTest.java | 29 ++++++++++++++++--- 4 files changed, 56 insertions(+), 21 deletions(-) diff --git a/pom.xml b/pom.xml index 786e0e9c..52da6eab 100644 --- a/pom.xml +++ b/pom.xml @@ -23,7 +23,7 @@ 999999-SNAPSHOT - 2.387.3 + 2.440.1 jenkinsci/${project.artifactId}-plugin true @@ -32,23 +32,11 @@ io.jenkins.tools.bom - bom-2.387.x - 2543.vfb_1a_5fb_9496d + bom-2.440.x + 2884.vc36b_64ce114a_ import pom - - - io.jenkins.plugins - jakarta-activation-api - 2.1.3-1 - - - - io.jenkins.plugins - jakarta-mail-api - 2.1.3-1 - diff --git a/src/main/java/hudson/tasks/Mailer.java b/src/main/java/hudson/tasks/Mailer.java index f10da269..cc7d6e25 100644 --- a/src/main/java/hudson/tasks/Mailer.java +++ b/src/main/java/hudson/tasks/Mailer.java @@ -38,6 +38,7 @@ import hudson.Util; import hudson.model.*; import jenkins.plugins.mailer.tasks.i18n.Messages; +import jenkins.security.FIPS140; import hudson.security.Permission; import hudson.util.FormValidation; import hudson.util.Secret; @@ -408,8 +409,15 @@ private static Session createSession(String smtpHost, String smtpPort, boolean u props.put("mail.smtp.starttls.enable", "true"); props.put("mail.smtp.starttls.required", "true"); } - if(smtpAuthUserName!=null) + if(smtpAuthUserName!=null) { props.put("mail.smtp.auth","true"); + if (FIPS140.useCompliantAlgorithms()) { + // the authentication mechanisms can only be performed when protected by TLS/SSL + if (!(useSsl || useTls)) { + throw new IllegalStateException("SMTP Authentication can only be used when either TLS or SSL is used"); + } + } + } // avoid hang by setting some timeout. props.put("mail.smtp.timeout","60000"); @@ -703,7 +711,10 @@ public FormValidation doSendTestMail( username = null; password = null; } - + if (FIPS140.useCompliantAlgorithms() && authentication && !(useSsl || useTls)) { + return FormValidation.error(Messages.Mailer_InsecureAuthError()); + } + MimeMessage msg = new MimeMessage(createSession(smtpHost, smtpPort, useSsl, useTls, username, password)); msg.setSubject(Messages.Mailer_TestMail_Subject(testEmailCount.incrementAndGet()), charset); msg.setText(Messages.Mailer_TestMail_Content(testEmailCount.get(), Jenkins.get().getDisplayName()), charset); @@ -721,6 +732,19 @@ public FormValidation doSendTestMail( } } + @RequirePOST + public FormValidation doCheckAuthentication(@QueryParameter boolean authentication, @QueryParameter boolean useSsl, @QueryParameter boolean useTls) { + Jenkins.get().checkPermission(Jenkins.MANAGE); + if (!authentication || useSsl || useTls) { + return FormValidation.ok(); + } + // authentication is enabled without either TLS or SSL + if (FIPS140.useCompliantAlgorithms()) { + return FormValidation.error(Messages.Mailer_InsecureAuthError()); + } + return FormValidation.warning(Messages.Mailer_InsecureAuthWarning()); + } + public boolean isApplicable(Class jobType) { return true; } diff --git a/src/main/resources/jenkins/plugins/mailer/tasks/i18n/Messages.properties b/src/main/resources/jenkins/plugins/mailer/tasks/i18n/Messages.properties index 941e2675..ad728e9a 100644 --- a/src/main/resources/jenkins/plugins/mailer/tasks/i18n/Messages.properties +++ b/src/main/resources/jenkins/plugins/mailer/tasks/i18n/Messages.properties @@ -47,6 +47,8 @@ Mailer.EmailSentSuccessfully=Email was successfully sent Mailer.FailedToSendEmail=Failed to send out e-mail Mailer.TestMail.Subject=Test email #{0} Mailer.TestMail.Content=This is test email #{0} sent from {1} +Mailer.InsecureAuthWarning=For security when using authentication it is recommended to enable either TLS or SSL +Mailer.InsecureAuthError=Authentication requires either TLS or SSL to be enabled MailCommand.ShortDescription=\ Reads stdin and sends that out as an e-mail. diff --git a/src/test/java/hudson/tasks/MailerTest.java b/src/test/java/hudson/tasks/MailerTest.java index e8eecb31..b626d7cf 100644 --- a/src/test/java/hudson/tasks/MailerTest.java +++ b/src/test/java/hudson/tasks/MailerTest.java @@ -32,6 +32,7 @@ import hudson.security.ACLContext; import hudson.slaves.DumbSlave; import hudson.tasks.Mailer.DescriptorImpl; +import hudson.util.FormValidation; import hudson.util.Secret; import org.hamcrest.MatcherAssert; import org.jenkinsci.plugins.workflow.cps.CpsFlowDefinition; @@ -40,7 +41,6 @@ import org.junit.Assume; import org.junit.Rule; import org.junit.Test; -import org.jvnet.hudson.test.Bug; import org.jvnet.hudson.test.FailureBuilder; import org.jvnet.hudson.test.Email; import org.jvnet.hudson.test.FakeChangeLogSCM; @@ -65,6 +65,7 @@ import java.util.Optional; import java.util.concurrent.atomic.AtomicLong; +import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.empty; import static org.hamcrest.Matchers.hasSize; @@ -74,7 +75,6 @@ import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNotSame; import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertThat; import static org.junit.Assert.assertThrows; import static org.junit.Assert.assertTrue; @@ -193,7 +193,7 @@ public void testFixEmptyAndTrimFour() throws Exception { assertEquals("UTF-8",d.getCharset()); } - @Bug(1566) + @Issue("JENKINS-1566") @Test public void testSenderAddress() throws Exception { // intentionally give the whole thin in a double quote @@ -287,7 +287,28 @@ public void globalConfig() throws Exception { assertNull(d.getAuthentication()); } - + + @Test + public void authenticationFormValidation() { + DescriptorImpl d = Mailer.descriptor(); + + // no authentication without TLS/SSL + assertThat(d.doCheckAuthentication(false, false, false), is(FormValidation.ok())); + + // no authentication with TLS / SSL combos + assertThat(d.doCheckAuthentication(false, true, false), is(FormValidation.ok())); + assertThat(d.doCheckAuthentication(false, false, true), is(FormValidation.ok())); + assertThat(d.doCheckAuthentication(false, true, true), is(FormValidation.ok())); + + // authentication without TLS/SSL + assertThat(d.doCheckAuthentication(true, false, false).kind, is(FormValidation.Kind.WARNING)); + + // authentication with TSL / SSL combos + assertThat(d.doCheckAuthentication(true, true, false), is(FormValidation.ok())); + assertThat(d.doCheckAuthentication(true, false, true), is(FormValidation.ok())); + assertThat(d.doCheckAuthentication(true, true, true), is(FormValidation.ok())); + } + /** * Simulates {@link JenkinsLocationConfiguration} is not configured. */