-
Notifications
You must be signed in to change notification settings - Fork 6.1k
Description
We have an issue with Active Directory/LDAP user accounts becoming locked after doing a single call to LdapAuthenticationProvider::authenticate (spring-security 5.4.2) with a wrong password when the LDAP URL contains more LDAP servers than the max allowed Bad-Pwd-Count in the AD.
The problem does not occur when using jdk1.8.0_252 but it does occur using jdk1.8.0_301 and above. Using tcpdump shows that all LDAP servers contained in the provided LDAP server URL list are being asked to validate the wrong password under jdk1.8.0_301 which locks the account. The following code demonstrates the problem using rroemhild/test-openldap (see https://github.com/rroemhild/docker-test-openldap). When the code is run with jdk1.8.0_252 the result is a javax.naming.AuthenticationException: [LDAP: error code 49 - Invalid Credentials] which is the result we expect. When it is executed under jdk1.8.0_301 the result is a sun.security.provider.certpath.SunCertPathBuilderException. This happens because the second LDAP server in the test setup is using an unknown cert - displaying the fact that it has been contacted, which it shouldn’t.
Which behavior is the expected one and how can we avoid user accounts being locked when using jdk1.8.0_301 with multiple LDAP servers?
To verify the issue do:
docker pull rroemhild/test-openldap
docker run --rm -p 10389:10389 -p 10636:10636 rroemhild/test-openldap
tcpdump -i docker0 port 10389 or port 10636
And then run following code:
@RunWith(SpringJUnit4ClassRunner.class)
@EnableWebSecurity
@WebAppConfiguration
@EnableWebMvc
@ContextConfiguration(loader = AnnotationConfigWebContextLoader.class)
public class LdapAuthOpenLdapTest {
@Configuration
@EnableWebSecurity
@WebAppConfiguration
@EnableWebMvc
static class LdapSecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) {
auth.authenticationProvider(ldapAuthProvider());
}
@Bean
DefaultSpringSecurityContextSource ldapServer() {
DefaultSpringSecurityContextSource result = new DefaultSpringSecurityContextSource(
"ldap://localhost:10389 ldaps://localhost:10636");
result.setUserDn("cn=admin,dc=planetexpress,dc=com");
result.setPassword("GoodNewsEveryone");
return result;
}
@Bean
FilterBasedLdapUserSearch userSearch() {
return new FilterBasedLdapUserSearch("dc=planetexpress,dc=com",
"(&(uid={0})(objectClass=inetOrgPerson))", ldapServer());
}
@Bean
LdapAuthenticationProvider ldapAuthProvider() {
BindAuthenticator bindAuthenticator = new BindAuthenticator(
ldapServer());
bindAuthenticator.setUserSearch(userSearch());
DefaultLdapAuthoritiesPopulator authoritiesPopulator = new DefaultLdapAuthoritiesPopulator(
ldapServer(), "ou=people,dc=planetexpress,dc=com");
authoritiesPopulator.setGroupRoleAttribute("cn");
authoritiesPopulator.setRolePrefix("ROLE_");
authoritiesPopulator.setConvertToUpperCase(true);
return new LdapAuthenticationProvider(bindAuthenticator,
authoritiesPopulator);
}
}
@Autowired
LdapAuthenticationProvider ldapAuthProvider;
@Test
public void test() {
Exception ex = null;
try {
UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(
"professor", "wrongPassword");
Authentication auth = ldapAuthProvider.authenticate(token);
System.out.println(auth);
} catch (Exception e) {
e.printStackTrace();
}
assertThat(ex, is(nullValue()));
}
}