From 37782718a0fbde0434cb9de3875459421fdb4231 Mon Sep 17 00:00:00 2001 From: Boris Yao Date: Tue, 17 Sep 2024 12:20:16 +0200 Subject: [PATCH 01/17] fix HPI --- pom.xml | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/pom.xml b/pom.xml index 66346de3..013c986b 100644 --- a/pom.xml +++ b/pom.xml @@ -320,6 +320,16 @@ + + + + + org.jenkins-ci.tools + maven-hpi-plugin + 3.58-rc1621.0cb_cd49b_b_c68 + + + org.jenkins-ci.tools From e198f7f0b6df9f624e4369ba0c7f1431af4a4093 Mon Sep 17 00:00:00 2001 From: Boris Yao Date: Tue, 17 Sep 2024 12:56:25 +0200 Subject: [PATCH 02/17] revert pom change --- pom.xml | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/pom.xml b/pom.xml index 013c986b..66346de3 100644 --- a/pom.xml +++ b/pom.xml @@ -320,16 +320,6 @@ - - - - - org.jenkins-ci.tools - maven-hpi-plugin - 3.58-rc1621.0cb_cd49b_b_c68 - - - org.jenkins-ci.tools From 3380f467217020f1f68052f206ac98611c1a22b5 Mon Sep 17 00:00:00 2001 From: Boris Yao Date: Tue, 17 Sep 2024 16:44:10 +0200 Subject: [PATCH 03/17] check password length --- .../jenkins/security/plugins/ldap/LDAPConfiguration.java | 5 +++++ .../jenkins/security/plugins/ldap/Messages.properties | 1 + 2 files changed, 6 insertions(+) diff --git a/src/main/java/jenkins/security/plugins/ldap/LDAPConfiguration.java b/src/main/java/jenkins/security/plugins/ldap/LDAPConfiguration.java index 7aa22444..aa80483e 100644 --- a/src/main/java/jenkins/security/plugins/ldap/LDAPConfiguration.java +++ b/src/main/java/jenkins/security/plugins/ldap/LDAPConfiguration.java @@ -36,6 +36,8 @@ import hudson.util.Secret; import jenkins.model.Jenkins; import java.nio.charset.StandardCharsets; + +import jenkins.security.FIPS140; import org.apache.commons.lang.StringUtils; import org.kohsuke.accmod.Restricted; import org.kohsuke.accmod.restrictions.NoExternalUse; @@ -411,6 +413,9 @@ public FormValidation doCheckServer(@QueryParameter String value, @QueryParamete if(!Jenkins.get().hasPermission(Jenkins.ADMINISTER)) return FormValidation.ok(); + if(FIPS140.useCompliantAlgorithms() && managerPassword.length() < 14) + return FormValidation.error(Messages.LDAPSecurityRealm_AuthenticationFailedNotFipsCompliantPass()); + Context ctx = null; try { Hashtable props = new Hashtable<>(); diff --git a/src/main/resources/jenkins/security/plugins/ldap/Messages.properties b/src/main/resources/jenkins/security/plugins/ldap/Messages.properties index 081a03e3..e16cb22f 100644 --- a/src/main/resources/jenkins/security/plugins/ldap/Messages.properties +++ b/src/main/resources/jenkins/security/plugins/ldap/Messages.properties @@ -6,6 +6,7 @@ LDAPSecurityRealm.LoginHeader=Login LDAPSecurityRealm.AuthenticationSuccessful=Authentication: successful LDAPSecurityRealm.AuthenticationFailed=Authentication: failed for user "{0}" LDAPSecurityRealm.AuthenticationFailedEmptyPass=Authentication: failed for user "{0}" with empty password +LDAPSecurityRealm.AuthenticationFailedNotFipsCompliantPass=Authentication failed: your password must be at least 14 characters long LDAPSecurityRealm.UserId=User ID: {0} LDAPSecurityRealm.UserDn=User DN: {0} LDAPSecurityRealm.UserConfiguration=User Server: {0} From bd62b159932a647f4572a0097d93b033eeb22dc9 Mon Sep 17 00:00:00 2001 From: Boris Yao Date: Tue, 17 Sep 2024 17:14:14 +0200 Subject: [PATCH 04/17] Add test --- .../plugins/ldap/LDAPConfigurationTest.java | 44 +++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/src/test/java/jenkins/security/plugins/ldap/LDAPConfigurationTest.java b/src/test/java/jenkins/security/plugins/ldap/LDAPConfigurationTest.java index 484cafe8..49090994 100644 --- a/src/test/java/jenkins/security/plugins/ldap/LDAPConfigurationTest.java +++ b/src/test/java/jenkins/security/plugins/ldap/LDAPConfigurationTest.java @@ -23,9 +23,20 @@ */ package jenkins.security.plugins.ldap; +import hudson.security.LDAPSecurityRealm; +import hudson.util.Secret; +import jenkins.model.IdStrategy; +import jenkins.security.FIPS140; +import org.junit.ClassRule; import org.junit.Rule; import org.junit.Test; +import org.jvnet.hudson.test.FlagRule; import org.jvnet.hudson.test.JenkinsRule; +import org.xml.sax.SAXException; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.collection.IsArrayWithSize.arrayWithSize; @@ -40,6 +51,9 @@ public class LDAPConfigurationTest { @Rule public JenkinsRule r = new JenkinsRule(); + @ClassRule + public static FlagRule fipsFlag = FlagRule.systemProperty(FIPS140.class.getName() + ".COMPLIANCE", "true"); + @Test public void getId() { LDAPConfiguration c = new LDAPConfiguration("ldap.example.com", "dc=example,dc=com", true, null, null); @@ -142,4 +156,34 @@ public void normalizeServerSameButDifferentOrder() { assertThat(n1.split("\\s+"), arrayWithSize(s1.split("\\s+").length)); } + @Test + public void managerPasswordSizeInFipsMode() throws Exception { + final String server = "localhost"; + final String rootDN = "ou=umich,dc=ou.edu"; + final String userSearchBase = "cn=users,ou=umich,ou.edu"; + final String managerDN = "cn=admin,ou=umich,ou.edu"; + final String managerSecret = "secret"; + + LDAPConfiguration c = new LDAPConfiguration(server, rootDN, false, managerDN, Secret.fromString(managerSecret)); + + List configurations = new ArrayList<>(); + configurations.add(c); + LDAPSecurityRealm realm = new LDAPSecurityRealm( + configurations, + false, + null, + IdStrategy.CASE_INSENSITIVE, + IdStrategy.CASE_INSENSITIVE + ); + + r.jenkins.setSecurityRealm(realm); + final JenkinsRule.WebClient client = r.createWebClient(); + r.submit(client.goTo("configureSecurity").getFormByName("config")); + + } + + @Test + public void managerPasswordSizeWithoutFipsMode() { + + } } \ No newline at end of file From 2b0c36abacbc21d8e4628978867606213ace3918 Mon Sep 17 00:00:00 2001 From: Boris Yao Date: Fri, 20 Sep 2024 14:28:31 +0200 Subject: [PATCH 05/17] update message --- .../resources/jenkins/security/plugins/ldap/Messages.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/resources/jenkins/security/plugins/ldap/Messages.properties b/src/main/resources/jenkins/security/plugins/ldap/Messages.properties index e16cb22f..b98cb095 100644 --- a/src/main/resources/jenkins/security/plugins/ldap/Messages.properties +++ b/src/main/resources/jenkins/security/plugins/ldap/Messages.properties @@ -6,7 +6,7 @@ LDAPSecurityRealm.LoginHeader=Login LDAPSecurityRealm.AuthenticationSuccessful=Authentication: successful LDAPSecurityRealm.AuthenticationFailed=Authentication: failed for user "{0}" LDAPSecurityRealm.AuthenticationFailedEmptyPass=Authentication: failed for user "{0}" with empty password -LDAPSecurityRealm.AuthenticationFailedNotFipsCompliantPass=Authentication failed: your password must be at least 14 characters long +LDAPSecurityRealm.AuthenticationFailedNotFipsCompliantPass=When running in FIPS compliance mode, the password must be at least 14 characters long LDAPSecurityRealm.UserId=User ID: {0} LDAPSecurityRealm.UserDn=User DN: {0} LDAPSecurityRealm.UserConfiguration=User Server: {0} From 5f438fb7794e28010cd4a884515a4e5a8eeb1f25 Mon Sep 17 00:00:00 2001 From: Boris Yao Date: Mon, 23 Sep 2024 10:15:16 +0200 Subject: [PATCH 06/17] Add Unit test --- .../plugins/ldap/LDAPConfigurationTest.java | 30 +++++-------------- 1 file changed, 7 insertions(+), 23 deletions(-) diff --git a/src/test/java/jenkins/security/plugins/ldap/LDAPConfigurationTest.java b/src/test/java/jenkins/security/plugins/ldap/LDAPConfigurationTest.java index 49090994..8ad270eb 100644 --- a/src/test/java/jenkins/security/plugins/ldap/LDAPConfigurationTest.java +++ b/src/test/java/jenkins/security/plugins/ldap/LDAPConfigurationTest.java @@ -24,9 +24,11 @@ package jenkins.security.plugins.ldap; import hudson.security.LDAPSecurityRealm; +import hudson.util.FormValidation; import hudson.util.Secret; import jenkins.model.IdStrategy; import jenkins.security.FIPS140; +import org.junit.Assert; import org.junit.ClassRule; import org.junit.Rule; import org.junit.Test; @@ -157,33 +159,15 @@ public void normalizeServerSameButDifferentOrder() { } @Test - public void managerPasswordSizeInFipsMode() throws Exception { + public void managerPasswordSizeInFipsModeTest() throws Exception { final String server = "localhost"; final String rootDN = "ou=umich,dc=ou.edu"; - final String userSearchBase = "cn=users,ou=umich,ou.edu"; final String managerDN = "cn=admin,ou=umich,ou.edu"; final String managerSecret = "secret"; - LDAPConfiguration c = new LDAPConfiguration(server, rootDN, false, managerDN, Secret.fromString(managerSecret)); - - List configurations = new ArrayList<>(); - configurations.add(c); - LDAPSecurityRealm realm = new LDAPSecurityRealm( - configurations, - false, - null, - IdStrategy.CASE_INSENSITIVE, - IdStrategy.CASE_INSENSITIVE - ); - - r.jenkins.setSecurityRealm(realm); - final JenkinsRule.WebClient client = r.createWebClient(); - r.submit(client.goTo("configureSecurity").getFormByName("config")); - - } - - @Test - public void managerPasswordSizeWithoutFipsMode() { - + LDAPConfiguration.LDAPConfigurationDescriptor descriptor = new LDAPConfiguration.LDAPConfigurationDescriptor(); + FormValidation result = descriptor.doCheckServer(server, managerDN, Secret.fromString(managerSecret), rootDN); + assertEquals("ERROR", result.kind.name()); + assertEquals(result.getMessage(), Messages.LDAPSecurityRealm_AuthenticationFailedNotFipsCompliantPass()); } } \ No newline at end of file From d11d97c14e527c8c2be34d78105351ae981b7f9a Mon Sep 17 00:00:00 2001 From: Boris Yao Date: Mon, 23 Sep 2024 10:57:52 +0200 Subject: [PATCH 07/17] Add Unit tests --- .../plugins/ldap/LDAPConfigurationTest.java | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/src/test/java/jenkins/security/plugins/ldap/LDAPConfigurationTest.java b/src/test/java/jenkins/security/plugins/ldap/LDAPConfigurationTest.java index 8ad270eb..fef9a111 100644 --- a/src/test/java/jenkins/security/plugins/ldap/LDAPConfigurationTest.java +++ b/src/test/java/jenkins/security/plugins/ldap/LDAPConfigurationTest.java @@ -159,7 +159,7 @@ public void normalizeServerSameButDifferentOrder() { } @Test - public void managerPasswordSizeInFipsModeTest() throws Exception { + public void managerPasswordUnderSizeInFipsModeTest() throws Exception { final String server = "localhost"; final String rootDN = "ou=umich,dc=ou.edu"; final String managerDN = "cn=admin,ou=umich,ou.edu"; @@ -167,7 +167,20 @@ public void managerPasswordSizeInFipsModeTest() throws Exception { LDAPConfiguration.LDAPConfigurationDescriptor descriptor = new LDAPConfiguration.LDAPConfigurationDescriptor(); FormValidation result = descriptor.doCheckServer(server, managerDN, Secret.fromString(managerSecret), rootDN); - assertEquals("ERROR", result.kind.name()); - assertEquals(result.getMessage(), Messages.LDAPSecurityRealm_AuthenticationFailedNotFipsCompliantPass()); + Assert.assertTrue(result.kind.name().equals("ERROR")); + Assert.assertTrue(result.getMessage().equals(Messages.LDAPSecurityRealm_AuthenticationFailedNotFipsCompliantPass())); + } + + @Test + public void managerPasswordOverSizeInFipsModeTest() throws Exception { + final String server = "localhost"; + final String rootDN = "ou=umich,dc=ou.edu"; + final String managerDN = "cn=admin,ou=umich,ou.edu"; + final String managerSecret = "passwordwithatleastfourteencaracters"; + + LDAPConfiguration.LDAPConfigurationDescriptor descriptor = new LDAPConfiguration.LDAPConfigurationDescriptor(); + FormValidation result = descriptor.doCheckServer(server, managerDN, Secret.fromString(managerSecret), rootDN); + Assert.assertTrue(result.kind.name().equals("ERROR")); + Assert.assertFalse(result.getMessage().equals(Messages.LDAPSecurityRealm_AuthenticationFailedNotFipsCompliantPass())); } } \ No newline at end of file From df06d8a571668fcd7f4d6d74eeb84d5f30ea69dd Mon Sep 17 00:00:00 2001 From: Boris Yao Date: Mon, 23 Sep 2024 11:20:35 +0200 Subject: [PATCH 08/17] Add class for FIPS test --- .../plugins/ldap/LDAPConfigurationTest.java | 41 ---------------- .../ldap/LDAPConfigurationWithFIPSTest.java | 49 +++++++++++++++++++ 2 files changed, 49 insertions(+), 41 deletions(-) create mode 100644 src/test/java/jenkins/security/plugins/ldap/LDAPConfigurationWithFIPSTest.java diff --git a/src/test/java/jenkins/security/plugins/ldap/LDAPConfigurationTest.java b/src/test/java/jenkins/security/plugins/ldap/LDAPConfigurationTest.java index fef9a111..484cafe8 100644 --- a/src/test/java/jenkins/security/plugins/ldap/LDAPConfigurationTest.java +++ b/src/test/java/jenkins/security/plugins/ldap/LDAPConfigurationTest.java @@ -23,22 +23,9 @@ */ package jenkins.security.plugins.ldap; -import hudson.security.LDAPSecurityRealm; -import hudson.util.FormValidation; -import hudson.util.Secret; -import jenkins.model.IdStrategy; -import jenkins.security.FIPS140; -import org.junit.Assert; -import org.junit.ClassRule; import org.junit.Rule; import org.junit.Test; -import org.jvnet.hudson.test.FlagRule; import org.jvnet.hudson.test.JenkinsRule; -import org.xml.sax.SAXException; - -import java.io.IOException; -import java.util.ArrayList; -import java.util.List; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.collection.IsArrayWithSize.arrayWithSize; @@ -53,9 +40,6 @@ public class LDAPConfigurationTest { @Rule public JenkinsRule r = new JenkinsRule(); - @ClassRule - public static FlagRule fipsFlag = FlagRule.systemProperty(FIPS140.class.getName() + ".COMPLIANCE", "true"); - @Test public void getId() { LDAPConfiguration c = new LDAPConfiguration("ldap.example.com", "dc=example,dc=com", true, null, null); @@ -158,29 +142,4 @@ public void normalizeServerSameButDifferentOrder() { assertThat(n1.split("\\s+"), arrayWithSize(s1.split("\\s+").length)); } - @Test - public void managerPasswordUnderSizeInFipsModeTest() throws Exception { - final String server = "localhost"; - final String rootDN = "ou=umich,dc=ou.edu"; - final String managerDN = "cn=admin,ou=umich,ou.edu"; - final String managerSecret = "secret"; - - LDAPConfiguration.LDAPConfigurationDescriptor descriptor = new LDAPConfiguration.LDAPConfigurationDescriptor(); - FormValidation result = descriptor.doCheckServer(server, managerDN, Secret.fromString(managerSecret), rootDN); - Assert.assertTrue(result.kind.name().equals("ERROR")); - Assert.assertTrue(result.getMessage().equals(Messages.LDAPSecurityRealm_AuthenticationFailedNotFipsCompliantPass())); - } - - @Test - public void managerPasswordOverSizeInFipsModeTest() throws Exception { - final String server = "localhost"; - final String rootDN = "ou=umich,dc=ou.edu"; - final String managerDN = "cn=admin,ou=umich,ou.edu"; - final String managerSecret = "passwordwithatleastfourteencaracters"; - - LDAPConfiguration.LDAPConfigurationDescriptor descriptor = new LDAPConfiguration.LDAPConfigurationDescriptor(); - FormValidation result = descriptor.doCheckServer(server, managerDN, Secret.fromString(managerSecret), rootDN); - Assert.assertTrue(result.kind.name().equals("ERROR")); - Assert.assertFalse(result.getMessage().equals(Messages.LDAPSecurityRealm_AuthenticationFailedNotFipsCompliantPass())); - } } \ No newline at end of file diff --git a/src/test/java/jenkins/security/plugins/ldap/LDAPConfigurationWithFIPSTest.java b/src/test/java/jenkins/security/plugins/ldap/LDAPConfigurationWithFIPSTest.java new file mode 100644 index 00000000..2e274277 --- /dev/null +++ b/src/test/java/jenkins/security/plugins/ldap/LDAPConfigurationWithFIPSTest.java @@ -0,0 +1,49 @@ +package jenkins.security.plugins.ldap; + +import hudson.util.FormValidation; +import hudson.util.Secret; +import jenkins.security.FIPS140; +import org.junit.Assert; +import org.junit.ClassRule; +import org.junit.Rule; +import org.junit.Test; +import org.jvnet.hudson.test.FlagRule; +import org.jvnet.hudson.test.JenkinsRule; + +/** + * Tests {@link LDAPConfiguration} in FIPS mode. + */ +public class LDAPConfigurationWithFIPSTest { + + @Rule + public JenkinsRule r = new JenkinsRule(); + + @ClassRule + public static FlagRule fipsFlag = FlagRule.systemProperty(FIPS140.class.getName() + ".COMPLIANCE", "true"); + + @Test + public void managerPasswordUnderSizeInFipsModeTest() throws Exception { + final String server = "localhost"; + final String rootDN = "ou=umich,dc=ou.edu"; + final String managerDN = "cn=admin,ou=umich,ou.edu"; + final String managerSecret = "secret"; + + LDAPConfiguration.LDAPConfigurationDescriptor descriptor = new LDAPConfiguration.LDAPConfigurationDescriptor(); + FormValidation result = descriptor.doCheckServer(server, managerDN, Secret.fromString(managerSecret), rootDN); + Assert.assertTrue(result.kind.name().equals("ERROR")); + Assert.assertTrue(result.getMessage().equals(Messages.LDAPSecurityRealm_AuthenticationFailedNotFipsCompliantPass())); + } + + @Test + public void managerPasswordOverSizeInFipsModeTest() throws Exception { + final String server = "localhost"; + final String rootDN = "ou=umich,dc=ou.edu"; + final String managerDN = "cn=admin,ou=umich,ou.edu"; + final String managerSecret = "passwordwithatleastfourteencaracters"; + + LDAPConfiguration.LDAPConfigurationDescriptor descriptor = new LDAPConfiguration.LDAPConfigurationDescriptor(); + FormValidation result = descriptor.doCheckServer(server, managerDN, Secret.fromString(managerSecret), rootDN); + Assert.assertTrue(result.kind.name().equals("ERROR")); + Assert.assertFalse(result.getMessage().equals(Messages.LDAPSecurityRealm_AuthenticationFailedNotFipsCompliantPass())); + } +} From 654de60f5ca1eb84b9d0e3225895dde41dd58493 Mon Sep 17 00:00:00 2001 From: Boris Yao Date: Tue, 24 Sep 2024 11:15:33 +0200 Subject: [PATCH 09/17] update condition --- .../java/jenkins/security/plugins/ldap/LDAPConfiguration.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/jenkins/security/plugins/ldap/LDAPConfiguration.java b/src/main/java/jenkins/security/plugins/ldap/LDAPConfiguration.java index aa80483e..71191574 100644 --- a/src/main/java/jenkins/security/plugins/ldap/LDAPConfiguration.java +++ b/src/main/java/jenkins/security/plugins/ldap/LDAPConfiguration.java @@ -413,7 +413,7 @@ public FormValidation doCheckServer(@QueryParameter String value, @QueryParamete if(!Jenkins.get().hasPermission(Jenkins.ADMINISTER)) return FormValidation.ok(); - if(FIPS140.useCompliantAlgorithms() && managerPassword.length() < 14) + if(FIPS140.useCompliantAlgorithms() && StringUtils.isNotBlank(managerPassword) && managerPassword.length() < 14) return FormValidation.error(Messages.LDAPSecurityRealm_AuthenticationFailedNotFipsCompliantPass()); Context ctx = null; From 182861097e8e4514249e5e72712184c90ae6b335 Mon Sep 17 00:00:00 2001 From: Boris Yao Date: Tue, 24 Sep 2024 11:28:12 +0200 Subject: [PATCH 10/17] update condition --- .../jenkins/security/plugins/ldap/LDAPConfiguration.java | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/main/java/jenkins/security/plugins/ldap/LDAPConfiguration.java b/src/main/java/jenkins/security/plugins/ldap/LDAPConfiguration.java index 71191574..e3e8437f 100644 --- a/src/main/java/jenkins/security/plugins/ldap/LDAPConfiguration.java +++ b/src/main/java/jenkins/security/plugins/ldap/LDAPConfiguration.java @@ -413,9 +413,6 @@ public FormValidation doCheckServer(@QueryParameter String value, @QueryParamete if(!Jenkins.get().hasPermission(Jenkins.ADMINISTER)) return FormValidation.ok(); - if(FIPS140.useCompliantAlgorithms() && StringUtils.isNotBlank(managerPassword) && managerPassword.length() < 14) - return FormValidation.error(Messages.LDAPSecurityRealm_AuthenticationFailedNotFipsCompliantPass()); - Context ctx = null; try { Hashtable props = new Hashtable<>(); @@ -435,6 +432,10 @@ public FormValidation doCheckServer(@QueryParameter String value, @QueryParamete props.put("com.sun.jndi.ldap.read.timeout", Integer.toString(READ_TIMEOUT)); ctx = new InitialDirContext(props); + + if(FIPS140.useCompliantAlgorithms() && StringUtils.isNotBlank(managerPassword) && managerPassword.length() < 14) + return FormValidation.error(Messages.LDAPSecurityRealm_AuthenticationFailedNotFipsCompliantPass()); + return FormValidation.ok(); // connected } catch (NamingException e) { // trouble-shoot From f6cf9a63d0b1246e0ecaa6c097ad4a7504b3f948 Mon Sep 17 00:00:00 2001 From: Boris Yao Date: Wed, 25 Sep 2024 10:39:57 +0200 Subject: [PATCH 11/17] Rollback changes --- .../plugins/ldap/LDAPConfiguration.java | 6 --- .../security/plugins/ldap/Messages.properties | 1 - .../ldap/LDAPConfigurationWithFIPSTest.java | 49 ------------------- 3 files changed, 56 deletions(-) delete mode 100644 src/test/java/jenkins/security/plugins/ldap/LDAPConfigurationWithFIPSTest.java diff --git a/src/main/java/jenkins/security/plugins/ldap/LDAPConfiguration.java b/src/main/java/jenkins/security/plugins/ldap/LDAPConfiguration.java index e3e8437f..7aa22444 100644 --- a/src/main/java/jenkins/security/plugins/ldap/LDAPConfiguration.java +++ b/src/main/java/jenkins/security/plugins/ldap/LDAPConfiguration.java @@ -36,8 +36,6 @@ import hudson.util.Secret; import jenkins.model.Jenkins; import java.nio.charset.StandardCharsets; - -import jenkins.security.FIPS140; import org.apache.commons.lang.StringUtils; import org.kohsuke.accmod.Restricted; import org.kohsuke.accmod.restrictions.NoExternalUse; @@ -432,10 +430,6 @@ public FormValidation doCheckServer(@QueryParameter String value, @QueryParamete props.put("com.sun.jndi.ldap.read.timeout", Integer.toString(READ_TIMEOUT)); ctx = new InitialDirContext(props); - - if(FIPS140.useCompliantAlgorithms() && StringUtils.isNotBlank(managerPassword) && managerPassword.length() < 14) - return FormValidation.error(Messages.LDAPSecurityRealm_AuthenticationFailedNotFipsCompliantPass()); - return FormValidation.ok(); // connected } catch (NamingException e) { // trouble-shoot diff --git a/src/main/resources/jenkins/security/plugins/ldap/Messages.properties b/src/main/resources/jenkins/security/plugins/ldap/Messages.properties index b98cb095..081a03e3 100644 --- a/src/main/resources/jenkins/security/plugins/ldap/Messages.properties +++ b/src/main/resources/jenkins/security/plugins/ldap/Messages.properties @@ -6,7 +6,6 @@ LDAPSecurityRealm.LoginHeader=Login LDAPSecurityRealm.AuthenticationSuccessful=Authentication: successful LDAPSecurityRealm.AuthenticationFailed=Authentication: failed for user "{0}" LDAPSecurityRealm.AuthenticationFailedEmptyPass=Authentication: failed for user "{0}" with empty password -LDAPSecurityRealm.AuthenticationFailedNotFipsCompliantPass=When running in FIPS compliance mode, the password must be at least 14 characters long LDAPSecurityRealm.UserId=User ID: {0} LDAPSecurityRealm.UserDn=User DN: {0} LDAPSecurityRealm.UserConfiguration=User Server: {0} diff --git a/src/test/java/jenkins/security/plugins/ldap/LDAPConfigurationWithFIPSTest.java b/src/test/java/jenkins/security/plugins/ldap/LDAPConfigurationWithFIPSTest.java deleted file mode 100644 index 2e274277..00000000 --- a/src/test/java/jenkins/security/plugins/ldap/LDAPConfigurationWithFIPSTest.java +++ /dev/null @@ -1,49 +0,0 @@ -package jenkins.security.plugins.ldap; - -import hudson.util.FormValidation; -import hudson.util.Secret; -import jenkins.security.FIPS140; -import org.junit.Assert; -import org.junit.ClassRule; -import org.junit.Rule; -import org.junit.Test; -import org.jvnet.hudson.test.FlagRule; -import org.jvnet.hudson.test.JenkinsRule; - -/** - * Tests {@link LDAPConfiguration} in FIPS mode. - */ -public class LDAPConfigurationWithFIPSTest { - - @Rule - public JenkinsRule r = new JenkinsRule(); - - @ClassRule - public static FlagRule fipsFlag = FlagRule.systemProperty(FIPS140.class.getName() + ".COMPLIANCE", "true"); - - @Test - public void managerPasswordUnderSizeInFipsModeTest() throws Exception { - final String server = "localhost"; - final String rootDN = "ou=umich,dc=ou.edu"; - final String managerDN = "cn=admin,ou=umich,ou.edu"; - final String managerSecret = "secret"; - - LDAPConfiguration.LDAPConfigurationDescriptor descriptor = new LDAPConfiguration.LDAPConfigurationDescriptor(); - FormValidation result = descriptor.doCheckServer(server, managerDN, Secret.fromString(managerSecret), rootDN); - Assert.assertTrue(result.kind.name().equals("ERROR")); - Assert.assertTrue(result.getMessage().equals(Messages.LDAPSecurityRealm_AuthenticationFailedNotFipsCompliantPass())); - } - - @Test - public void managerPasswordOverSizeInFipsModeTest() throws Exception { - final String server = "localhost"; - final String rootDN = "ou=umich,dc=ou.edu"; - final String managerDN = "cn=admin,ou=umich,ou.edu"; - final String managerSecret = "passwordwithatleastfourteencaracters"; - - LDAPConfiguration.LDAPConfigurationDescriptor descriptor = new LDAPConfiguration.LDAPConfigurationDescriptor(); - FormValidation result = descriptor.doCheckServer(server, managerDN, Secret.fromString(managerSecret), rootDN); - Assert.assertTrue(result.kind.name().equals("ERROR")); - Assert.assertFalse(result.getMessage().equals(Messages.LDAPSecurityRealm_AuthenticationFailedNotFipsCompliantPass())); - } -} From 9cf3712276d84fb588c48c4176c0ee52717c0670 Mon Sep 17 00:00:00 2001 From: Boris Yao Date: Thu, 26 Sep 2024 16:18:01 +0200 Subject: [PATCH 12/17] Fix Authentication for FIPS --- .../hudson/security/LDAPSecurityRealm.java | 4 ++ .../plugins/ldap/LDAPConfiguration.java | 1 + .../security/plugins/ldap/Messages.properties | 1 + .../LDAPSecurityRealmWithFIPSTest.java | 51 +++++++++++++++++++ 4 files changed, 57 insertions(+) create mode 100644 src/test/java/hudson/security/LDAPSecurityRealmWithFIPSTest.java diff --git a/src/main/java/hudson/security/LDAPSecurityRealm.java b/src/main/java/hudson/security/LDAPSecurityRealm.java index 59aefafd..fec23964 100644 --- a/src/main/java/hudson/security/LDAPSecurityRealm.java +++ b/src/main/java/hudson/security/LDAPSecurityRealm.java @@ -39,6 +39,7 @@ import hudson.util.Secret; import jenkins.model.IdStrategy; import jenkins.model.Jenkins; +import jenkins.security.FIPS140; import jenkins.security.SecurityListener; import jenkins.security.plugins.ldap.FromGroupSearchLDAPGroupMembershipStrategy; import jenkins.security.plugins.ldap.LDAPConfiguration; @@ -753,6 +754,9 @@ public SecurityComponents createSecurityComponents() { */ @Override protected UserDetails authenticate2(String username, String password) throws AuthenticationException { + if(FIPS140.useCompliantAlgorithms() && StringUtils.isNotBlank(password) && password.length() < 14) { + throw new org.springframework.ldap.AuthenticationException(new javax.naming.AuthenticationException("When running in FIPS compliance mode, the password must be at least 14 characters long")); + } return updateUserDetails((UserDetails) getSecurityComponents().manager2.authenticate( new UsernamePasswordAuthenticationToken(fixUsername(username), password)).getPrincipal(), null); } diff --git a/src/main/java/jenkins/security/plugins/ldap/LDAPConfiguration.java b/src/main/java/jenkins/security/plugins/ldap/LDAPConfiguration.java index 7aa22444..3a9ea306 100644 --- a/src/main/java/jenkins/security/plugins/ldap/LDAPConfiguration.java +++ b/src/main/java/jenkins/security/plugins/ldap/LDAPConfiguration.java @@ -36,6 +36,7 @@ import hudson.util.Secret; import jenkins.model.Jenkins; import java.nio.charset.StandardCharsets; + import org.apache.commons.lang.StringUtils; import org.kohsuke.accmod.Restricted; import org.kohsuke.accmod.restrictions.NoExternalUse; diff --git a/src/main/resources/jenkins/security/plugins/ldap/Messages.properties b/src/main/resources/jenkins/security/plugins/ldap/Messages.properties index 081a03e3..b98cb095 100644 --- a/src/main/resources/jenkins/security/plugins/ldap/Messages.properties +++ b/src/main/resources/jenkins/security/plugins/ldap/Messages.properties @@ -6,6 +6,7 @@ LDAPSecurityRealm.LoginHeader=Login LDAPSecurityRealm.AuthenticationSuccessful=Authentication: successful LDAPSecurityRealm.AuthenticationFailed=Authentication: failed for user "{0}" LDAPSecurityRealm.AuthenticationFailedEmptyPass=Authentication: failed for user "{0}" with empty password +LDAPSecurityRealm.AuthenticationFailedNotFipsCompliantPass=When running in FIPS compliance mode, the password must be at least 14 characters long LDAPSecurityRealm.UserId=User ID: {0} LDAPSecurityRealm.UserDn=User DN: {0} LDAPSecurityRealm.UserConfiguration=User Server: {0} diff --git a/src/test/java/hudson/security/LDAPSecurityRealmWithFIPSTest.java b/src/test/java/hudson/security/LDAPSecurityRealmWithFIPSTest.java new file mode 100644 index 00000000..530522b3 --- /dev/null +++ b/src/test/java/hudson/security/LDAPSecurityRealmWithFIPSTest.java @@ -0,0 +1,51 @@ +package hudson.security; + +import hudson.util.Secret; +import jenkins.model.IdStrategy; +import jenkins.security.FIPS140; +import jenkins.security.plugins.ldap.FromUserRecordLDAPGroupMembershipStrategy; +import jenkins.security.plugins.ldap.LDAPConfiguration; +import org.junit.ClassRule; +import org.junit.Rule; +import org.junit.Test; +import org.jvnet.hudson.test.FlagRule; +import org.jvnet.hudson.test.JenkinsRule; + +public class LDAPSecurityRealmWithFIPSTest { + + @Rule + public JenkinsRule r = new JenkinsRule(); + + + @ClassRule + public static FlagRule fipsFlag = FlagRule.systemProperty(FIPS140.class.getName() + ".COMPLIANCE", "true"); + + @Test + public void ldapAuthenticationWithFIPSTest() throws Exception { + final String server = "localhost"; + final String rootDN = "ou=umich,dc=ou.edu"; + final String userSearchBase = "cn=users,ou=umich,ou.edu"; + final String managerDN = "cn=admin,ou=umich,ou.edu"; + final String managerSecret = "secret"; + final LDAPSecurityRealm realm = new LDAPSecurityRealm( + server, + rootDN, + userSearchBase, + null, + null, + null, + new FromUserRecordLDAPGroupMembershipStrategy("previousValue"), + managerDN, + Secret.fromString(managerSecret), + false, + false, + null, + null, + null, + null, + IdStrategy.CASE_INSENSITIVE, + IdStrategy.CASE_INSENSITIVE); + r.jenkins.setSecurityRealm(realm); + //realm.authenticate2("user","secret"); + } +} From f21ae9a004b7cdbd4ee5ed717ea4a0cef386ecb7 Mon Sep 17 00:00:00 2001 From: Boris Yao <106237859+BorisYaoA@users.noreply.github.com> Date: Fri, 27 Sep 2024 12:03:02 +0200 Subject: [PATCH 13/17] Update src/main/java/hudson/security/LDAPSecurityRealm.java Co-authored-by: Robert Sandell --- src/main/java/hudson/security/LDAPSecurityRealm.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/hudson/security/LDAPSecurityRealm.java b/src/main/java/hudson/security/LDAPSecurityRealm.java index fec23964..62f6bba5 100644 --- a/src/main/java/hudson/security/LDAPSecurityRealm.java +++ b/src/main/java/hudson/security/LDAPSecurityRealm.java @@ -754,7 +754,7 @@ public SecurityComponents createSecurityComponents() { */ @Override protected UserDetails authenticate2(String username, String password) throws AuthenticationException { - if(FIPS140.useCompliantAlgorithms() && StringUtils.isNotBlank(password) && password.length() < 14) { + if(FIPS140.useCompliantAlgorithms() && (StringUtils.isBlank(password) || password.length() < 14)) { throw new org.springframework.ldap.AuthenticationException(new javax.naming.AuthenticationException("When running in FIPS compliance mode, the password must be at least 14 characters long")); } return updateUserDetails((UserDetails) getSecurityComponents().manager2.authenticate( From 6cd0818fc85ad333db1605036f94260c587f4a5d Mon Sep 17 00:00:00 2001 From: Boris Yao Date: Fri, 27 Sep 2024 16:07:21 +0200 Subject: [PATCH 14/17] Fix Authentication for FIPS with exception --- src/main/java/hudson/security/LDAPSecurityRealm.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/java/hudson/security/LDAPSecurityRealm.java b/src/main/java/hudson/security/LDAPSecurityRealm.java index 62f6bba5..12755297 100644 --- a/src/main/java/hudson/security/LDAPSecurityRealm.java +++ b/src/main/java/hudson/security/LDAPSecurityRealm.java @@ -754,9 +754,6 @@ public SecurityComponents createSecurityComponents() { */ @Override protected UserDetails authenticate2(String username, String password) throws AuthenticationException { - if(FIPS140.useCompliantAlgorithms() && (StringUtils.isBlank(password) || password.length() < 14)) { - throw new org.springframework.ldap.AuthenticationException(new javax.naming.AuthenticationException("When running in FIPS compliance mode, the password must be at least 14 characters long")); - } return updateUserDetails((UserDetails) getSecurityComponents().manager2.authenticate( new UsernamePasswordAuthenticationToken(fixUsername(username), password)).getPrincipal(), null); } @@ -990,6 +987,9 @@ private void addDelegate(AuthenticationManager delegate, String configurationId, @Override public Authentication authenticate(Authentication authentication) throws AuthenticationException { try (SetContextClassLoader sccl = new SetContextClassLoader()) { + if(FIPS140.useCompliantAlgorithms() && (StringUtils.isBlank(authentication.getCredentials().toString()) || authentication.getCredentials().toString().length() < 14)) { + throw new org.springframework.ldap.AuthenticationException(new javax.naming.AuthenticationException("When running in FIPS compliance mode, the password must be at least 14 characters long")); + } AuthenticationException lastException = null; for (ManagerEntry delegate : delegates) { try { From cbb238f61b10d2998082fe82404c97f87bd371ff Mon Sep 17 00:00:00 2001 From: Francisco Javier Fernandez Gonzalez Date: Fri, 27 Sep 2024 16:32:42 +0200 Subject: [PATCH 15/17] Move FIPS check to AuthenticationManager --- .../hudson/security/LDAPSecurityRealm.java | 10 +++++-- .../LDAPSecurityRealmWithFIPSTest.java | 30 +++++++++++++++++-- 2 files changed, 34 insertions(+), 6 deletions(-) diff --git a/src/main/java/hudson/security/LDAPSecurityRealm.java b/src/main/java/hudson/security/LDAPSecurityRealm.java index 62f6bba5..a3f332b1 100644 --- a/src/main/java/hudson/security/LDAPSecurityRealm.java +++ b/src/main/java/hudson/security/LDAPSecurityRealm.java @@ -754,9 +754,6 @@ public SecurityComponents createSecurityComponents() { */ @Override protected UserDetails authenticate2(String username, String password) throws AuthenticationException { - if(FIPS140.useCompliantAlgorithms() && (StringUtils.isBlank(password) || password.length() < 14)) { - throw new org.springframework.ldap.AuthenticationException(new javax.naming.AuthenticationException("When running in FIPS compliance mode, the password must be at least 14 characters long")); - } return updateUserDetails((UserDetails) getSecurityComponents().manager2.authenticate( new UsernamePasswordAuthenticationToken(fixUsername(username), password)).getPrincipal(), null); } @@ -990,6 +987,13 @@ private void addDelegate(AuthenticationManager delegate, String configurationId, @Override public Authentication authenticate(Authentication authentication) throws AuthenticationException { try (SetContextClassLoader sccl = new SetContextClassLoader()) { + String password = authentication.getCredentials() instanceof String ? (String) authentication.getCredentials() : null; + if(FIPS140.useCompliantAlgorithms() && (StringUtils.isBlank(password) || password.length() < 14)) { + final String message = "When running in FIPS compliance mode, the password must be at least 14 characters long"; + LOGGER.warning(message); + throw new org.springframework.ldap.AuthenticationException(new javax.naming.AuthenticationException(message)); + } + AuthenticationException lastException = null; for (ManagerEntry delegate : delegates) { try { diff --git a/src/test/java/hudson/security/LDAPSecurityRealmWithFIPSTest.java b/src/test/java/hudson/security/LDAPSecurityRealmWithFIPSTest.java index 530522b3..871cb667 100644 --- a/src/test/java/hudson/security/LDAPSecurityRealmWithFIPSTest.java +++ b/src/test/java/hudson/security/LDAPSecurityRealmWithFIPSTest.java @@ -1,21 +1,31 @@ package hudson.security; +import java.util.logging.Level; + import hudson.util.Secret; import jenkins.model.IdStrategy; import jenkins.security.FIPS140; import jenkins.security.plugins.ldap.FromUserRecordLDAPGroupMembershipStrategy; -import jenkins.security.plugins.ldap.LDAPConfiguration; + +import org.htmlunit.FailingHttpStatusCodeException; import org.junit.ClassRule; import org.junit.Rule; import org.junit.Test; import org.jvnet.hudson.test.FlagRule; import org.jvnet.hudson.test.JenkinsRule; +import org.jvnet.hudson.test.LoggerRule; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.containsString; +import static org.hamcrest.Matchers.is; +import static org.junit.Assert.assertThrows; public class LDAPSecurityRealmWithFIPSTest { @Rule public JenkinsRule r = new JenkinsRule(); - + @Rule + public LoggerRule log = new LoggerRule(); @ClassRule public static FlagRule fipsFlag = FlagRule.systemProperty(FIPS140.class.getName() + ".COMPLIANCE", "true"); @@ -46,6 +56,20 @@ public void ldapAuthenticationWithFIPSTest() throws Exception { IdStrategy.CASE_INSENSITIVE, IdStrategy.CASE_INSENSITIVE); r.jenkins.setSecurityRealm(realm); - //realm.authenticate2("user","secret"); + + JenkinsRule.WebClient wc = r.createWebClient(); + + log.record(LDAPSecurityRealm.class, Level.WARNING).capture(10); // reset + FailingHttpStatusCodeException cannotLogin = assertThrows("Valid password, but expected to fail as there is no such user. Just 401", + FailingHttpStatusCodeException.class, + () -> wc.login("alice", "passwordLongEnoughToBeFIPScompliant")); + assertThat("Invalid user", cannotLogin.getStatusCode(), is(401)); + + log.record(LDAPSecurityRealm.class, Level.WARNING).capture(10); // reset + cannotLogin = assertThrows("Short password, so the error is different now", + FailingHttpStatusCodeException.class, + () -> wc.login("bob", "shortPassword")); + assertThat("Password invalid in FIPS, not even authenticated", cannotLogin.getStatusCode(), is(500)); + assertThat("FIPS message is logged", log, LoggerRule.recorded(Level.WARNING, containsString("the password must be at least 14 characters long"))); } } From 5533e3037e2e48628fb02bf9611de01cc367d123 Mon Sep 17 00:00:00 2001 From: Boris Yao <106237859+BorisYaoA@users.noreply.github.com> Date: Fri, 27 Sep 2024 17:04:38 +0200 Subject: [PATCH 16/17] Update LDAPConfiguration.java --- .../java/jenkins/security/plugins/ldap/LDAPConfiguration.java | 1 - 1 file changed, 1 deletion(-) diff --git a/src/main/java/jenkins/security/plugins/ldap/LDAPConfiguration.java b/src/main/java/jenkins/security/plugins/ldap/LDAPConfiguration.java index 3a9ea306..7aa22444 100644 --- a/src/main/java/jenkins/security/plugins/ldap/LDAPConfiguration.java +++ b/src/main/java/jenkins/security/plugins/ldap/LDAPConfiguration.java @@ -36,7 +36,6 @@ import hudson.util.Secret; import jenkins.model.Jenkins; import java.nio.charset.StandardCharsets; - import org.apache.commons.lang.StringUtils; import org.kohsuke.accmod.Restricted; import org.kohsuke.accmod.restrictions.NoExternalUse; From 2c101347e2a89dca8cddc701ae60dedc355a9bd6 Mon Sep 17 00:00:00 2001 From: Boris Yao <106237859+BorisYaoA@users.noreply.github.com> Date: Fri, 27 Sep 2024 17:05:06 +0200 Subject: [PATCH 17/17] Update Messages.properties --- .../resources/jenkins/security/plugins/ldap/Messages.properties | 1 - 1 file changed, 1 deletion(-) diff --git a/src/main/resources/jenkins/security/plugins/ldap/Messages.properties b/src/main/resources/jenkins/security/plugins/ldap/Messages.properties index b98cb095..081a03e3 100644 --- a/src/main/resources/jenkins/security/plugins/ldap/Messages.properties +++ b/src/main/resources/jenkins/security/plugins/ldap/Messages.properties @@ -6,7 +6,6 @@ LDAPSecurityRealm.LoginHeader=Login LDAPSecurityRealm.AuthenticationSuccessful=Authentication: successful LDAPSecurityRealm.AuthenticationFailed=Authentication: failed for user "{0}" LDAPSecurityRealm.AuthenticationFailedEmptyPass=Authentication: failed for user "{0}" with empty password -LDAPSecurityRealm.AuthenticationFailedNotFipsCompliantPass=When running in FIPS compliance mode, the password must be at least 14 characters long LDAPSecurityRealm.UserId=User ID: {0} LDAPSecurityRealm.UserDn=User DN: {0} LDAPSecurityRealm.UserConfiguration=User Server: {0}