Spring 3.1 LDAP Authentication Process: "Bad Credentials" msg When Credentials Are Good
Asked Answered
F

2

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.

Freeze answered 28/3, 2012 at 21:37 Comment(1)
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.Sangsanger
S
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.

Sangsanger answered 4/4, 2012 at 12:18 Comment(7)
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 <password> 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?Freeze
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?Sangsanger
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.Freeze
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.Sangsanger
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.Freeze
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.Freeze
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.Sangsanger
J
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()" />  
Jodoin answered 30/3, 2012 at 21:55 Comment(12)
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.Freeze
I have edited the answer. If you don't want roles, don't associate one to the access.Jodoin
I tried the new access value you suggested. No change. Where would I put that logger xml you quoted above?Freeze
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.Jodoin
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.Jodoin
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".Freeze
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?Jodoin
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.Freeze
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.Jodoin
POST. jddsantaella, I added my login.jsp and my Login.java to my original post.Freeze
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.Freeze
You are welcome. It is nice you finally solved the problem. Just mark my answer as useful if it helped you.Jodoin

© 2022 - 2024 — McMap. All rights reserved.