Description of problem: LDAP referrals are not being followed for authentication Version-Release number of selected component (if applicable): EAP 6.2 CP01 How reproducible: always Steps to Reproduce: 1. Setup two LDAP servers. I used 2x Sun DSEE 7 Server 1: uid=tom,ou=people,dc=example,dc=com which is a member of the group: cn=JBossAdmin,ou=Groups,dc=example,dc=com A referral to the second LDAP server: version: 1 dn: ou=RemoteLdap,dc=example,dc=com objectClass: organizationalUnit objectClass: referral objectClass: top ou: RemoteLdap ref: ldap://zen.usersys.redhat.com:391/dc=example,dc=com Server 2: uid=tomds3,ou=people,dc=example,dc=com which is a member of the group: cn=JBossAdmin,ou=Groups,dc=example,dc=com 2. configure a security domain <security-domain name="LdapRealm" cache-type="default"> <authentication> <login-module code="LdapExtended" flag="required"> <module-option name="java.naming.provider.url" value="ldap://zen.usersys.redhat.com:389/"/> <module-option name="java.naming.factory.initial" value="com.sun.jndi.ldap.LdapCtxFactory"/> <module-option name="java.naming.security.authentication" value="simple"/> <module-option name="bindDN" value="cn=Directory Manager"/> <module-option name="bindCredential" value="12345678"/> <module-option name="baseCtxDN" value="dc=example,dc=com"/> <module-option name="baseFilter" value="(uid={0})"/> <module-option name="rolesCtxDN" value="ou=Groups,dc=example,dc=com"/> <module-option name="roleFilter" value="(uniqueMember={1})"/> <module-option name="roleAttributeID" value="cn"/> <module-option name="roleNameAttributeID" value="cn"/> <module-option name="roleRecursion" value="0"/> <module-option name="throwValidateError" value="true"/> <module-option name="java.naming.referral" value="follow"/> </login-module> </authentication> </security-domain> 3. Deploy a form based authentication test app for testing the login. -> attachement "formlogin-LdapRealm.war" It's pre-configured to use the above domain. Note that the initial page contains a session invalidation call to make it easy to test several times 4. login with: tom/tom => success 5. login with: tomds3/tomds3 = fails: 13:23:30,769 TRACE [org.jboss.security] (http-orac.usersys.redhat.com/10.33.1.110:8080-1) PBOX000240: Begin login method 13:23:30,779 TRACE [org.jboss.security] (http-orac.usersys.redhat.com/10.33.1.110:8080-1) PBOX000220: Logging into LDAP server with env {java.naming.factory.initial=com.sun.jndi.ldap.LdapCtxFactory, java.naming.referral=follow, roleNameAttributeID=cn, java.naming.security.principal=cn=Directory Manager, roleRecursion=0, baseCtxDN=dc=example,dc=com, roleAttributeID=cn, roleFilter=(uniqueMember={1}), rolesCtxDN=ou=Groups,dc=example,dc=com, baseFilter=(uid={0}), jboss.security.security_domain=LdapRealm, throwValidateError=true, java.naming.provider.url=ldap://zen.usersys.redhat.com:389/, bindDN=cn=Directory Manager, java.naming.security.authentication=simple, bindCredential=******, java.naming.security.credentials=******} 13:23:31,134 DEBUG [org.jboss.security] (http-orac.usersys.redhat.com/10.33.1.110:8080-1) PBOX000283: Bad password for username tomds3 13:23:31,134 TRACE [org.jboss.security] (http-orac.usersys.redhat.com/10.33.1.110:8080-1) PBOX000244: Begin abort method 13:23:31,134 DEBUG [org.jboss.security] (http-orac.usersys.redhat.com/10.33.1.110:8080-1) PBOX000206: Login failure: javax.security.auth.login.FailedLoginException: PBOX000070: Password invalid/Password required at org.jboss.security.auth.spi.UsernamePasswordLoginModule.login(UsernamePasswordLoginModule.java:284) [picketbox-4.0.19.SP3-redhat-1.jar:4.0.19.SP3-redhat-1] at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) [rt.jar:1.6.0_45] at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39) [rt.jar:1.6.0_45] at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25) [rt.jar:1.6.0_45] at java.lang.reflect.Method.invoke(Method.java:597) [rt.jar:1.6.0_45] at javax.security.auth.login.LoginContext.invoke(LoginContext.java:769) [rt.jar:1.6.0_45] at javax.security.auth.login.LoginContext.access$000(LoginContext.java:186) [rt.jar:1.6.0_45] at javax.security.auth.login.LoginContext$4.run(LoginContext.java:683) [rt.jar:1.6.0_45] at java.security.AccessController.doPrivileged(Native Method) [rt.jar:1.6.0_45] at javax.security.auth.login.LoginContext.invokePriv(LoginContext.java:680) [rt.jar:1.6.0_45] at javax.security.auth.login.LoginContext.login(LoginContext.java:579) [rt.jar:1.6.0_45] at org.jboss.security.authentication.JBossCachedAuthenticationManager.defaultLogin(JBossCachedAuthenticationManager.java:408) [picketbox-infinispan-4.0.19.SP3-redhat-1.jar:4.0.19.SP3-redhat-1] at org.jboss.security.authentication.JBossCachedAuthenticationManager.proceedWithJaasLogin(JBossCachedAuthenticationManager.java:345) [picketbox-infinispan-4.0.19.SP3-redhat-1.jar:4.0.19.SP3-redhat-1] at org.jboss.security.authentication.JBossCachedAuthenticationManager.authenticate(JBossCachedAuthenticationManager.java:333) [picketbox-infinispan-4.0.19.SP3-redhat-1.jar:4.0.19.SP3-redhat-1] at org.jboss.security.authentication.JBossCachedAuthenticationManager.isValid(JBossCachedAuthenticationManager.java:146) [picketbox-infinispan-4.0.19.SP3-redhat-1.jar:4.0.19.SP3-redhat-1] at org.jboss.as.web.security.JBossWebRealm.authenticate(JBossWebRealm.java:216) [jboss-as-web-7.3.1.Final-redhat-3.jar:7.3.1.Final-redhat-3] at org.apache.catalina.authenticator.FormAuthenticator.authenticate(FormAuthenticator.java:280) [jbossweb-7.3.0.Final-redhat-1.jar:7.3.0.Final-redhat-1] at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:391) [jbossweb-7.3.0.Final-redhat-1.jar:7.3.0.Final-redhat-1] at org.jboss.as.web.security.SecurityContextAssociationValve.invoke(SecurityContextAssociationValve.java:169) [jboss-as-web-7.3.1.Final-redhat-3.jar:7.3.1.Final-redhat-3] at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:145) [jbossweb-7.3.0.Final-redhat-1.jar:7.3.0.Final-redhat-1] at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:97) [jbossweb-7.3.0.Final-redhat-1.jar:7.3.0.Final-redhat-1] at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:102) [jbossweb-7.3.0.Final-redhat-1.jar:7.3.0.Final-redhat-1] at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:336) [jbossweb-7.3.0.Final-redhat-1.jar:7.3.0.Final-redhat-1] at org.apache.coyote.http11.Http11Processor.process(Http11Processor.java:856) [jbossweb-7.3.0.Final-redhat-1.jar:7.3.0.Final-redhat-1] at org.apache.coyote.http11.Http11Protocol$Http11ConnectionHandler.process(Http11Protocol.java:653) [jbossweb-7.3.0.Final-redhat-1.jar:7.3.0.Final-redhat-1] at org.apache.tomcat.util.net.JIoEndpoint$Worker.run(JIoEndpoint.java:920) [jbossweb-7.3.0.Final-redhat-1.jar:7.3.0.Final-redhat-1] at java.lang.Thread.run(Thread.java:662) [rt.jar:1.6.0_45] Caused by: javax.naming.NamingException: PBOX000038: Unable to follow referral for authentication: ldap://zen.usersys.redhat.com:391/uid=tomds3,%20ou=People,%20dc=example,dc=com at org.jboss.security.auth.spi.LdapExtLoginModule.bindDNAuthentication(LdapExtLoginModule.java:553) [picketbox-4.0.19.SP3-redhat-1.jar:4.0.19.SP3-redhat-1] at org.jboss.security.auth.spi.LdapExtLoginModule.createLdapInitContext(LdapExtLoginModule.java:465) [picketbox-4.0.19.SP3-redhat-1.jar:4.0.19.SP3-redhat-1] at org.jboss.security.auth.spi.LdapExtLoginModule.validatePassword(LdapExtLoginModule.java:340) [picketbox-4.0.19.SP3-redhat-1.jar:4.0.19.SP3-redhat-1] at org.jboss.security.auth.spi.UsernamePasswordLoginModule.login(UsernamePasswordLoginModule.java:281) [picketbox-4.0.19.SP3-redhat-1.jar:4.0.19.SP3-redhat-1] ... 26 more Additional info: It is fairly easy to see why this is not working: org/jboss/security/auth/spi/LdapExtLoginModule.java 496 protected String bindDNAuthentication(InitialLdapContext ctx, String user, Object credential, String baseDN, 497 String filter) throws NamingException 498 { 499 SearchControls constraints = new SearchControls(); 500 constraints.setSearchScope(SearchControls.SUBTREE_SCOPE); 501 constraints.setTimeLimit(searchTimeLimit); 502 String attrList[] = {distinguishedNameAttribute}; 503 constraints.setReturningAttributes(attrList); 504 505 NamingEnumeration results = null; 506 507 Object[] filterArgs = {user}; 508 509 LdapContext ldapCtx = ctx; 510 511 boolean referralsLeft = true; 512 SearchResult sr = null; 513 while (referralsLeft) { 514 try { 515 results = ldapCtx.search(baseDN, filter, filterArgs, constraints); 516 while (results.hasMore()) { 517 sr = (SearchResult) results.next(); 518 break; 519 } 520 referralsLeft = false; 521 } 522 catch (ReferralException e) { 523 ldapCtx = (LdapContext) e.getReferralContext(); 524 if (results != null) { 525 results.close(); 526 } 527 } 528 } 529 530 if (sr == null) 531 { 532 results.close(); 533 throw PicketBoxMessages.MESSAGES.failedToFindBaseContextDN(baseDN); 534 } 535 536 String name = sr.getName(); 537 String userDN = null; 538 Attributes attrs = sr.getAttributes(); 539 if (attrs != null) 540 { 541 Attribute dn = attrs.get(distinguishedNameAttribute); 542 if (dn != null) 543 { 544 userDN = (String) dn.get(); 545 } 546 } 547 if (userDN == null) 548 { 549 if (sr.isRelative() == true) { 550 userDN = name + ("".equals(baseDN) ? "" : "," + baseDN); 551 } 552 else { 553 throw PicketBoxMessages.MESSAGES.unableToFollowReferralForAuth(name); 554 } 555 } In our case there is never a ReferralException thrown as we get an actual result. During debugging it was found that by line 549, we had: dn: was absolute: uid=tomds3, ou=People, dc=example,dc=com name : ldap://zen.usersys.redhat.com:391/uid=tomds3,%20ou=People,%20dc=example,dc=com NameInNamespace : uid=tomds3, ou=People, dc=example,dc=com hence the throw PicketBoxMessages.MESSAGES.unableToFollowReferralForAuth(name); Test code was written that would replace line 553 above private boolean authUser(SearchResult result) throws IOException, NamingException { LdapContext ctx; if (result.getName().startsWith("ldap")) { //TODO: the big question.... is this the right way of doing this ? String ref_url = result.getName().substring(0, result.getName().indexOf("/", 8)); String ref_binddn = result.getNameInNamespace(); System.out.println("Following referral to: " + ref_url); ctx = this.getLdapContext(ref_url, ref_binddn, userPassword); } else { ctx = this.getLdapContext(url, binddn, bindCredentials); } return (ctx != null); } While this worked fine, it is not clear if this is the proper way of doing this.
Created attachment 864564 [details] test war file formlogin-LdapRealm.war used "/fl" as context
Created attachment 864565 [details] standalone test program standalone test app usage: java -jar ldaptest.jar -> prints usage java -jar ldaptest.jar -u ldap://zen.usersys.redhat.com -b dc=example,dc=com -f uid=tomds3 -rf -p tomds3 -rf: follow referrals -p : the password for the user as specified in the -f filter Expected output: Connected to: ldap://zen.usersys.redhat.com --------------------------------------------- dn was: absolute dn : uid=tomds3, ou=People, dc=example,dc=com name : ldap://zen.usersys.redhat.com:391/uid=tomds3,%20ou=People,%20dc=example,dc=com NameInNamespace: uid=tomds3, ou=People, dc=example,dc=com ------------attributes----------------------- l=FAB mail=tomf uid=tomds3 ou=People givenName=Tom objectClass=top objectClass=person objectClass=organizationalPerson objectClass=inetOrgPerson sn=on ds3 cn=tomds3 --------------------------------------------- Following referral to: ldap://zen.usersys.redhat.com:391 Binding with principal: uid=tomds3, ou=People, dc=example,dc=com Connected to: ldap://zen.usersys.redhat.com:391 authentication successful
Created attachment 864566 [details] standalone test program main code
Created attachment 864567 [details] standalone test program util class for certificates
see also bz-1066488
I have the fix on my workspace. Currently working on backport and release of related components PicketBox and JBoss Negotiation.
PR for PicketBox part: https://github.com/jbossas/jboss-eap/pull/1366
as per above, re-opening
The problem in C#17 is setup issue. rolesCtxDN is set to ou=Groups,dc=example,dc=com which causes that referral object exiting in context dc=example,dc=com is not examined by the query and therefore not followed nor throwing exception for further handling. The setup is also missing option: <module-option name="referralUserAttributeIDToCheck" value="uniqueMember"/> My working setup is: <security-domain name="LdapRealm" cache-type="default"> <authentication> <login-module code="LdapExtended" flag="required"> <module-option name="java.naming.provider.url" value="ldap://zen.usersys.redhat.com:389/"/> <module-option name="java.naming.factory.initial" value="com.sun.jndi.ldap.LdapCtxFactory"/> <module-option name="java.naming.security.authentication" value="simple"/> <module-option name="bindDN" value="cn=Directory Manager"/> <module-option name="bindCredential" value="**"/> <module-option name="baseCtxDN" value="dc=example,dc=com"/> <module-option name="baseFilter" value="(uid={0})"/> <module-option name="rolesCtxDN" value="dc=example,dc=com"/> <!-- module-option name="rolesCtxDN" value="ou=Groups,dc=example,dc=com"/ --> <module-option name="roleFilter" value="(uniqueMember={1})"/> <module-option name="roleAttributeID" value="cn"/> <module-option name="roleNameAttributeID" value="cn"/> <module-option name="roleRecursion" value="0"/> <module-option name="throwValidateError" value="true"/> <module-option name="java.naming.referral" value="follow"/> <module-option name="referralUserAttributeIDToCheck" value="uniqueMember"/> </login-module> </authentication> </security-domain>
referralUserAttributeIDToCheck is not documented. I'll create a doc-bug
Verified in 6.3.0.ER7