9

Spring 3.1 Tomcat 6.*

I'm working on making a Spring 3.1 webapp, authenticating with LDAP.

I tested the LDAP credentials (username, password, ldap URL, search pattern ) with a JNDI styled Java program I wrote (quoted below ). That program worked, dumped all of the users attributes, including the password, which seems to be encrypted on the LDAP server.

When I try to login with the same credentials in Spring 3.1 I get the error message "Bad Credentials".

I got this message in the logs:

DEBUG [org.springframework.security.authentication.ProviderManager:authenticate] (ProviderManager.java:152) - Authentication attempt using org.springframework.security.ldap.authentication.LdapAuthenticationProvider
DEBUG [org.springframework.security.ldap.authentication.AbstractLdapAuthenticationProvider:authenticate] (AbstractLdapAuthenticationProvider.java:51) - Processing authentication request for user: John.A.Smith
DEBUG [org.springframework.security.ldap.authentication.BindAuthenticator:bindWithDn] (BindAuthenticator.java:108) - Attempting to bind as uid=John.A.Smith,ou=People,o=acme.com,o=acme.com
DEBUG [org.springframework.security.ldap.DefaultSpringSecurityContextSource$1:setupEnvironment] (DefaultSpringSecurityContextSource.java:76) - Removing pooling flag for user uid=John.A.Smith,ou=People,o=acme.com,o=acme.com
DEBUG [org.springframework.security.ldap.authentication.BindAuthenticator:handleBindException] (BindAuthenticator.java:152) - Failed to bind as uid=John.A.Smith,ou=People,o=acme.gov: org.springframework.ldap.AuthenticationException: [LDAP: error code 32 - No Such Object]; nested exception is javax.naming.AuthenticationException: [LDAP: error code 32 - No Such Object]
DEBUG [org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter:unsuccessfulAuthentication] (AbstractAuthenticationProcessingFilter.java:340) - Authentication request failed: org.springframework.security.authentication.BadCredentialsException: Bad credentials

In my *-security.xml I tried using tags to make a password comparison and encoding happen, but it didn't help. I tried using md4,md5,plaintext,sha,sha-256,{ssha},{sha} to no avail.

   <s:authentication-manager>
        <s:ldap-authentication-provider user-dn-pattern="uid={0},ou=People,o=noaa.gov" >
          <s:password-compare hash="md5">
            <s:password-encoder hash="md5"/>
          </s:password-compare>
        </s:ldap-authentication-provider>
      </s:authentication-manager>

My networking group is a big, slow, bureaucratic org. Is there a way I can tell what encoding they use, if any, without contacting them?

Any ideas of things I could check out?

This is my *-security.xml as of my last attempt and the java LDAP demo I WAS able to connect with

Thanks.

My *-security.xml file:

<beans xmlns="http://www.springframework.org/schema/beans"  
  xmlns:s="http://www.springframework.org/schema/security"  
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"  
  xsi:schemaLocation="http://www.springframework.org/schema/beans  
    http://www.springframework.org/schema/beans/spring-beans-3.0.xsd  
    http://www.springframework.org/schema/security  
    http://www.springframework.org/schema/security/spring-security-3.1.xsd">  



  <s:http auto-config="true" use-expressions="true">  
    **<s:intercept-url pattern="/welcome*" access="isAuthenticated()" />** 
    <s:form-login login-page="/login" default-target-url="/welcome"  
      authentication-failure-url="/loginfailed" />  
    <s:logout logout-success-url="/logout" />  
  </s:http>  



  <s:ldap-server url = "ldap://ldap-itc.sam.acme.com:636/o=acme.com"/>  

  <s:authentication-manager>
    <s:ldap-authentication-provider user-dn-pattern="uid={0},ou=People,o=noaa.gov" />
  </s:authentication-manager>

</beans>  

Here is the JNDI style LDAP Java program that WORKS with the same credentials:

import javax.naming.*;  
import javax.naming.directory.*;  
import java.util.*;  
import java.sql.*;  

public class LDAPDEMO {  

    public static void main(String args[]) {  

        String lcf                = "com.sun.jndi.ldap.LdapCtxFactory";  
        String ldapurl            = "ldap://ldap-itc.sam.acme.com:636/o=acme.com";  
        String loginid            = "John.A.Smith";  
        String password           = "passowordforjohn";  
        DirContext ctx            = null;  
        Hashtable env             = new Hashtable();  
        Attributes attr           = null;  
        Attributes resultsAttrs   = null;  
        SearchResult result       = null;  
        NamingEnumeration results = null;  
        int iResults              = 0;  


        env.put(Context.INITIAL_CONTEXT_FACTORY, lcf);  
        env.put(Context.PROVIDER_URL, ldapurl);  
        env.put(Context.SECURITY_PROTOCOL, "ssl");  
        env.put(Context.SECURITY_AUTHENTICATION, "simple");  
        env.put(Context.SECURITY_PRINCIPAL, "uid=" + loginid + ",ou=People,o=acme.com");  
        env.put(Context.SECURITY_CREDENTIALS, password);  
        try {  

            ctx     = new InitialDirContext(env);  
            attr    = new BasicAttributes(true);  
            attr.put(new BasicAttribute("uid",loginid));  
            results = ctx.search("ou=People",attr);  

            while (results.hasMore()) {  
                result       = (SearchResult)results.next();  
                resultsAttrs = result.getAttributes();  

                for (NamingEnumeration enumAttributes  = resultsAttrs.getAll(); enumAttributes.hasMore();) {  
                    Attribute a = (Attribute)enumAttributes.next();  
                    System.out.println("attribute: " + a.getID() + " : " + a.get().toString());  


                }// end for loop  

                iResults++;  
            }// end while loop  

            System.out.println("iResults == " + iResults);  

        }// end try  
        catch (Exception e) {  
            e.printStackTrace();  
        }  



    }// end function main()  
}// end class LDAPDEMO  

Solution


This comment from Luke Taylor helped me get my configuration working:

Your configuration is wrong in that you have "o=acme.com" in the LDAP server URL and are also using "o=acme.com" in the user DN pattern.

I took the "o=acme.com" out of the DN pattern and the LDAP worked. I had originally put the "o=acme.com" in both the LDAP URL and the DN pattern because I am new to Spring 3.1 and LDAP, and that is similar to how it is/was done in the Java JNDI version of the LDAP demo I wrote based on the legacy code I am replacing.

Here is the final, working version of my *-security.xml

<beans xmlns="http://www.springframework.org/schema/beans"  
  xmlns:s="http://www.springframework.org/schema/security"  
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"  
  xsi:schemaLocation="http://www.springframework.org/schema/beans  
    http://www.springframework.org/schema/beans/spring-beans-3.0.xsd  
    http://www.springframework.org/schema/security  
    http://www.springframework.org/schema/security/spring-security-3.1.xsd">  



  <s:http auto-config="true" use-expressions="true">  
    **<s:intercept-url pattern="/welcome*" access="isAuthenticated()" />** 
    <s:form-login login-page="/login" default-target-url="/welcome"  
      authentication-failure-url="/loginfailed" />  
    <s:logout logout-success-url="/logout" />  
  </s:http>  



  <s:ldap-server url = "ldap://ldap-itc.sam.acme.com:636/o=acme.com"/>  

  <s:authentication-manager>
    <s:ldap-authentication-provider user-dn-pattern="uid={0},ou=People" />
  </s:authentication-manager>

</beans>  

I'm going to explore his other comment and see if I can put the password encoding back in or if I need to.

Steve
  • 3,127
  • 14
  • 56
  • 96
  • Another note. You can't put password encoding back in, since you don't know what algorithm the server is using and in practice it could be using more than one. Hashing passwords is about protecting the password database by making it harder to read. It isn't about making client authentication more secure (you will find many discussions on this on SO). If you want to prevent eavesdropping on the network from your app to the LDAP server, then using an SSL connection is probably the simplest option. – Shaun the Sheep Apr 04 '12 at 23:02

2 Answers2

3

Your Java example is using standard bind authentication, but you have set the Spring Security configuration to do an LDAP compare operation on the user's password. This will fail because the LDAP server is not using the same password encoding format as Spring Security's MD5 encoder. For a compare operation to succeed, the stored value must match the string that is sent to the directory. In most cases you want to use standard LDAP (bind) authentication. You'll probably need to use a bean configuration for the authentication provider. Try using:

<s:ldap-server id="contextSource" url="ldap://ldap-itc.sam.acme.com:636/o=acme.com"/>

<bean id="ldapAuthProvider" class="org.springframework.security.ldap.authentication.LdapAuthenticationProvider">
 <constructor-arg>
   <bean class="org.springframework.security.ldap.authentication.BindAuthenticator">
     <constructor-arg ref="contextSource"/>
     <property name="userDnPatterns">
       <list><value>uid={0},ou=People</value></list>
     </property>
   </bean>
 </constructor-arg>
 <constructor-arg>
   <bean class="org.springframework.security.ldap.authentication.NullLdapAuthoritiesPopulator"/>
 </constructor-arg>
  <property name="authoritiesMapper">
    <bean class="class="org.springframework.security.core.authority.mapping">
       <property name="defaultAuthority" value="ROLE_USER" />
    </bean>
  </property>   
</bean>

<s:authentication-manager>
  <s:authentication-manager ref="ldapAuthProvider" />
</s:authentication-manager>

I'd recommend you also read the LDAP chapter of the reference manual.

Also, if you want to know why an authentication is failing, the best place to find out is the log for the LDAP server itself. If you don't have full access, then find out how it is set up and use a local server (such as OpenLDAP) where you have full control.

Shaun the Sheep
  • 22,353
  • 1
  • 72
  • 100
  • Hi Luke. I read the LDAP chapter in the Spring 3.1 several times. It is brief and I am new to LDAP as well as Spring. I want to make sure I understand your comment. If remove the tags I will drop down to using standard binding, as the quoted Java program is and I will NOT be doing a password comparison at all? Does Spring do a password comparison by default? Would I need to set something for it to NOT do that? – Steve Apr 04 '12 at 16:25
  • Short of contacting them, you can't, and comparing passwords isn't a great idea if they are encode (the case might be different, for example). You should use bind authentication since you've already demonstrated that works and forget about password compare. It is normally only used when bind is disabled. Did you try the configuration I provided? If so, what happened? – Shaun the Sheep Apr 04 '12 at 17:07
  • Luke, in light of your comments I changed my *server.xml and my entire original post. I need to study your configuration for a bit. If possible I would like to find a way to write it Spring 3.1 style. I will give it a shot and let you know how it works. Thanks a ton. – Steve Apr 04 '12 at 17:56
  • Your configuration is wrong in that you have "o=acme.com" in the LDAP server URL and are also using "o=acme.com" in the user DN pattern. This was probably because I had written it that way :). But the pattern should be relative to the root DN. I've corrected my answer. – Shaun the Sheep Apr 04 '12 at 18:13
  • I pasted the combination of your configuration and the rest of my *-security.xml up above towards the bottom of the original post. It threw errors when the webapp started up. I don't know enough about the namespace yet to fix it ( I tried a few guesses ). Thanks in advance. – Steve Apr 04 '12 at 18:49
  • Hi Luke. I couldn't get your configuration to work, but I got an idea from one of your comments and used it to get my config working. I posted a description at the bottom of my original post. I'm giving you the points. I was getting ready to go work on everything else and revisit this at a later time. Thanks a ton for the help. – Steve Apr 04 '12 at 20:53
  • Glad you got it working. Your configuration is similar to the explicit configuration I have used except that it will be performing an unnecessary search for groups assigned to the user. The use of the `NullAuthoritiesPopulator` in the configuration will prevent this. – Shaun the Sheep Apr 04 '12 at 22:41
0

I will try luck. Few weeks ago I had a similar problem. No errors, correct user/pass and Bad Credentials error.

First, I recommend you to activate debug level for spring security. You will get more information. In my case, this helped me to see that the problem was that my user did not have any role associated and Spring was traducing it as "bad credentials" error. It could be your case. Check it.

Anyway, bad credentials doesn't mean always user/pass incorrect.

EDIT: For activating debug level using log4j:

<logger name="org.springframework.security">
    <level value="DEBUG" />
</logger>

In you configuration, it can be read that access to welcome page requires admin role: ROLE_ADMIN. If you don't want roles, you should try something like this:

<s:intercept-url pattern="/welcome*" access="isAuthenticated()" />  
jddsantaella
  • 3,657
  • 1
  • 23
  • 39
  • I'm new to Spring and LDAP. How can I activate the debug level in Spring Security? Is it possible to NOT associate a role with a user and if so how? Can I just associate a neutral role with the user? We really don't have roles for the users. – Steve Apr 02 '12 at 13:29
  • I have edited the answer. If you don't want roles, don't associate one to the access. – jddsantaella Apr 02 '12 at 14:27
  • I tried the new access value you suggested. No change. Where would I put that logger xml you quoted above? – Steve Apr 02 '12 at 15:24
  • In log4j.xml file. If you are using log4j.property instead log4j.xml the format will be different but the same idea. to be honest, I was not sure "isAuthenticated" was going to work. I think Spring Security is always expecting to find a role associated to the user, but again, I am not sure. – jddsantaella Apr 02 '12 at 15:33
  • In the jdbc implementation (taking a look to spring source code), I can see that if the user has not associated any role, UsernameNotFoundException is thrown (even is username is found). It could be happening the same to ldap implementation. You have to check this in log and if I am right you should overwrite some code. – jddsantaella Apr 02 '12 at 15:45
  • I made the change to my *-security.xml you advised and plugged in debug level logging. I put those changes and my logging output in my original post. The debug messages didn't tell me much more than "bad credentials". – Steve Apr 02 '12 at 18:32
  • In the log file it looks like authentication fails. You are trying to login with Robert.A.Smith but in the java code you provided you are testing with John.A.Smith. Is this right? – jddsantaella Apr 02 '12 at 18:46
  • Those are aliases. I redacted out the real names to keep the security gods happy. The username I found in the log is good and spelled correctly. The username and password has been tested. – Steve Apr 02 '12 at 18:52
  • Are you using POST or GET in your login form? At this point, I recommend you to debug the source code. If you are using Maven and Eclipse this is easy. – jddsantaella Apr 02 '12 at 19:16
  • POST. jddsantaella, I added my login.jsp and my Login.java to my original post. – Steve Apr 02 '12 at 19:24
  • jddsantaella, though I didn't use your answer directly, I learned a lot from your suggestions so I wanted to thank you. If there is a way I can give you some points too let me know. – Steve Apr 04 '12 at 20:55
  • You are welcome. It is nice you finally solved the problem. Just mark my answer as useful if it helped you. – jddsantaella Apr 05 '12 at 21:33