diff --git a/src/main/java/hudson/security/LDAPSecurityRealm.java b/src/main/java/hudson/security/LDAPSecurityRealm.java index 59aefafd..0aa6f8e9 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; @@ -986,6 +987,12 @@ 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 new file mode 100644 index 00000000..871cb667 --- /dev/null +++ b/src/test/java/hudson/security/LDAPSecurityRealmWithFIPSTest.java @@ -0,0 +1,75 @@ +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 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"); + + @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); + + 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"))); + } +}