Page 1 of 2 12 LastLast
Results 1 to 10 of 17

Thread: Can't do ldap bind authentication with domain\firstname.lastname

  1. #1
    Join Date
    Jul 2008
    Location
    Washington DC
    Posts
    67

    Default Can't do ldap bind authentication with domain\firstname.lastname

    I get the following ldap.core.TokenMgrError when I try to do an ldap bind on a domain with "xyz\first.last" as the username. The bind itself works properly via ldp.exe and JXPlorer. Our Active Directory doesn't allow anonymous binds, so we have to use the user's login credentials on every ldap call (in this case authentication). I'm using Spring LDAP 1.3 with Spring Security 3.0.2. Below is my config (obfuscated to protect).

    Ideally I don't even want to have to parse the DN to bind. I want to bind with the domain\username. But I can't find any method on LdapTemplate or DirContextOperations to do that directly. If this needs to be posted in the spring security forum I'll do that, but the exception I'm getting comes from spring ldap so I figured here's the right place to post.

    Error: Your login attempt was not successful, try again.

    Reason: Failed to parse DN; nested exception is org.springframework.ldap.core.TokenMgrError: Lexical error at line 1, column 9. Encountered: "." (46), after : "".
    Reason: Failed to parse DN; nested exception is org.springframework.ldap.core.TokenMgrError: Lexical error at line 1, column 4. Encountered: "\\" (92), after : "".

    Code:
    2010-03-23 17:28:12,179 [http-8080-2] DEBUG authentication.UsernamePasswordAuthenticationFilter  - Authentication request failed: org.springframework.security.authentication.AuthenticationServiceException: Failed to parse DN; nested exception is org.springframework.ldap.core.TokenMgrError: Lexical error at line 1, column 9.  Encountered: "." (46), after : ""

    Here's the applicationContext-security.xml:

    Code:
    <?xml version="1.0" encoding="UTF-8"?>
    <beans:beans xmlns="http://www.springframework.org/schema/security"
        xmlns:beans="http://www.springframework.org/schema/beans"
        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.0.xsd">
    
        <global-method-security pre-post-annotations="enabled">
            <!-- AspectJ pointcut expression that locates our "post" method and applies security that way
            <protect-pointcut expression="execution(* bigbank.*Service.post*(..))" access="ROLE_TELLER"/>
            -->
        </global-method-security>
    
    	<http use-expressions="true">
    		<!-- intercept-url pattern="/secure/extreme/**" access="hasRole('ROLE_SUPERVISOR')"/ --> 
    		<intercept-url pattern="/phase/**" access="hasRole('ROLE_ADMIN')"/>
    		<intercept-url pattern="/summary/**" access="isAuthenticated()" />
    		<intercept-url pattern="/login**" access="permitAll"/>
    		<intercept-url pattern="/spring_security_login" access="permitAll"/>
    		<intercept-url pattern="/images/**" access="permitAll"/>
    		<intercept-url pattern="/js/**" access="permitAll"/>
    		<intercept-url pattern="/css/**" access="permitAll"/>
    		<intercept-url pattern="/yaml/**" access="permitAll"/>
    		<intercept-url pattern="/**" access="isAuthenticated()" />
    
          <form-login login-page="/login.jsp" 
          	login-processing-url="/j_spring_security_check"
          	authentication-failure-url="/login.jsp?login_error=1" 
          	always-use-default-target="true"
          	default-target-url="/"
          	/>
          <logout logout-success-url="/"/>
    	</http>
    
    
        <authentication-manager>
            <authentication-provider ref='ldapProvider'/>
        </authentication-manager>
    
        <beans:bean id="ldapProvider" class="org.springframework.security.ldap.authentication.LdapAuthenticationProvider">
            <beans:constructor-arg ref="bindAuthenticator"></beans:constructor-arg>
            <beans:constructor-arg ref="authoritiesPopulator"></beans:constructor-arg>
        </beans:bean>
    
    	<beans:bean id="bindAuthenticator" class="my.security.ldap.authentication.BindAuthenticator">
    <!-- consider this to be the same exact thing as spring security's 
    BindAuthenticator, haven't modified  the authenticate or bindWithDn methods -->
    		<beans:constructor-arg ref="contextSource" />
    		<beans:property name="userDnPatterns">
    			<beans:list>
    				<beans:value>cn={0},OU=DevTest Users,DC=xyz,DC=com</beans:value>
    <!-- Both of these next two fail before even going into the BindAuthenticator -->				<beans:value>{0}</beans:value>
    				<beans:value>xyz\{0}</beans:value>
    				<beans:value>cn={0},OU=group,OU=mail,DC=xyz,DC=com</beans:value>
    			</beans:list>
    		</beans:property>
    	</beans:bean>
    
        <!-- Modified based on Example 8.1 of section 8.1.3.2. Custom Principal and Credentials Management in the Spring LDAP Reference doc -->
    	<beans:bean id="contextSource" 
    			class="org.springframework.security.ldap.DefaultSpringSecurityContextSource">
         <beans:constructor-arg value="ldap://activedirectoryhostname:9389/DC=xyz,DC=com"/>
         <beans:property name="authenticationSource" ref="springSecurityAuthenticationSource" />
    	</beans:bean>
    
       <beans:bean id="springSecurityAuthenticationSource"
          class="org.springframework.security.ldap.authentication.SpringSecurityAuthenticationSource" />
        
    	<beans:bean id="authoritiesPopulator" class="my.security.ldap.userdetails.myAuthoritiesPopulator">
    	    <beans:constructor-arg index="0" ref="contextSource" />
    	    <beans:constructor-arg index="1" value="ou=groups" />
    	    <beans:constructor-arg index="2" ref="gmsTemplate"/>
    	    <beans:property name="groupSearchFilter" value="(member={0})"/>
    	    <beans:property name="rolePrefix" value="ROLE_"/>
    	    <beans:property name="searchSubtree" value="true"/>
    	    <beans:property name="convertToUpperCase" value="true"/>
    	</beans:bean>
    
    	<beans:bean id="gmsTemplate" class="my.security.gms.GmsTemplate">
    		<beans:property name="baseUrl" value="http://localhost/GMS/Gms.svc"/>
    		<beans:property name="messageConverters">
    			<beans:list>
    				<beans:bean class="org.springframework.http.converter.json.MappingJacksonHttpMessageConverter"/>
    			</beans:list>
    		</beans:property>
    	</beans:bean>
    
    </beans:beans>

  2. #2
    Join Date
    Jul 2005
    Location
    Helsingborg, Sweden
    Posts
    504

    Default

    I can trigger a part of your problem by sending an inadequately escaped DN to DistinguishedName:

    Fails:
    Code:
    DistinguishedName dn = new DistinguishedName("cn=Some\\Per-son.7,ou=company1,c=Sweden");
    
    org.springframework.ldap.core.TokenMgrError: Lexical error at line 1, column 8.  Encountered: "\\" (92), after : ""
    However, after escaping the backslashes properly, there are no problems with either dot or dash. This works:
    Code:
    DistinguishedName dn = new DistinguishedName("cn=Some\\\\Per-son.7,ou=company1,c=Sweden");
    The only conclusion I can come up with is (again) that Spring Security passes invalid DNs to Spring LDAP.

    Interestingly, using DistinguishedName as it was intended, namely to do the escaping for you, the problem with escaping goes away:

    Code:
    DistinguishedName dn = new DistinguishedName();
    dn.append("c", "Sweden");
    dn.append("ou", "company1");
    dn.append("cn", ",=+<>#;\\"); // all the special characters plus the escape character
    System.out.println(dn);
    
    cn=\,\=\+\<\>\#\;\\,ou=company1,c=Sweden
    Note that dn.getValue will give the unescaped value:

    Code:
    System.out.println(dn.getValue("cn"));
    ,=+<>#;\
    Ulrik Sandberg
    Jayway (www.jayway.com)
    Spring LDAP project member

  3. #3
    Join Date
    Jul 2008
    Location
    Washington DC
    Posts
    67

    Default

    Quote Originally Posted by ulsa View Post
    The only conclusion I can come up with is (again) that Spring Security passes invalid DNs to Spring LDAP.
    Can someone tell me where exactly this takes place in spring security ldap? I can then debug/step through it and hope to fix it.
    Last edited by djKianoosh; Mar 25th, 2010 at 10:13 AM.

  4. #4
    Join Date
    Jul 2008
    Location
    Washington DC
    Posts
    67

    Default

    possibly either DefaultLdapUsernameToDnMapper.buildDn() or LdapUtils.getFullDn()?

  5. #5
    Join Date
    Jul 2005
    Location
    Helsingborg, Sweden
    Posts
    504

    Default

    Jeff writes:
    In Spring Security 3.0, BindAuthenticator.java line 115 the call to ctx.getAttributes should be using fullDn instead of userDn; by using userDn the encoding in LdapEncoder never gets used.

    Side Note: I learned a lot about the internals of both Spring-LDAP and Spring-Security while hunting this one down. In a few places (BindAuthenticator.java is an example) I found the dividing line between Spring-LDAP and Spring-Security to be less than ideal.
    Perhaps that could point you in the right direction. It's likely the source of your problem as well.
    Ulrik Sandberg
    Jayway (www.jayway.com)
    Spring LDAP project member

  6. #6
    Join Date
    Jul 2008
    Location
    Washington DC
    Posts
    67

    Default

    back at this.. stepping through it with a debugger, it's throwing the TokenMgrError in DistinguishedName.parse() on line 184: dn.parser.dn();

    Code:
    	/**
    	 * Parse the supplied String and make this instance represent the
    	 * corresponding distinguished name.
    	 * 
    	 * @param path the LDAP path to parse.
    	 */
    	protected void parse(String path) {
    		DnParser parser = DefaultDnParserFactory.createDnParser(unmangleCompositeName(path));
    		DistinguishedName dn;
    		try {
    			dn = parser.dn();
    		}
    		catch (ParseException e) {
    			throw new BadLdapGrammarException("Failed to parse DN", e);
    		}
    		catch (TokenMgrError e) {
    			throw new BadLdapGrammarException("Failed to parse DN", e);
    		}
    		this.names = dn.names;
    	}
    For some reason I don't have the source for that DnParserImpl class, so I can't see what it's doing. But going into it, in that 'path' variable, is a string that looks like: cn=first.last,OU=DevTest Users,DC=xyz,DC=com

    should it have been escaped by this point already? The last few lines of the trace went like so:
    DistinguishedName.parse(String) line: 184
    DistinguishedName.<init>(String) line: 140
    BindAuthenticator.bindWithDn(String, String, String) line: 95
    BindAuthenticator.authenticate(Authentication) line: 63
    LdapAuthenticationProvider.authenticate(Authentica tion) line: 252


    So really, the only other place it should have been escaped is in BindAuthenticator itself (that's a spring security class that I extended). Now, I thought the parser itself was the one that should have done the escaping, but if you tell me that it should be escaped before this, then please tell me there's an existing method that can do this for me

  7. #7
    Join Date
    Jul 2008
    Location
    Washington DC
    Posts
    67

    Default

    From your example above, which uses the append() method, it eventually calls this:
    org.springframework.ldap.core.LdapEncoder.nameDeco de(String)

    So maybe I can use nameEncode() before I pass the path to new DistinguishedName() in BindAuthenticator?

  8. #8
    Join Date
    Jul 2005
    Location
    Helsingborg, Sweden
    Posts
    504

    Default

    Be careful here. You should probably stay away from manually trying to escape a string into a full DN. The DistinguishedName#append method does this for you, but you need to have the individual parts at hand.

    The DistinguishedName constructor that takes a string representing a full DN simply assumes it's a correctly escaped and valid DN. I think you should focus on getting Spring Security to send a valid DN. Jeff stated something about the wrong variable was used in BindAuthenticator: "...ctx.getAttributes should be using fullDn instead of userDn". Wouldn't that affect you too?

    You don't see the source of DnParserImpl because it's a generated class. The DN parser is written in JavaCC.
    Ulrik Sandberg
    Jayway (www.jayway.com)
    Spring LDAP project member

  9. #9
    Join Date
    Jul 2005
    Location
    Helsingborg, Sweden
    Posts
    504

    Default

    ... I don't have the source for that DnParserImpl class, so I can't see what it's doing. But going into it, in that 'path' variable, is a string that looks like: cn=first.last,OU=DevTest Users,DC=xyz,DC=com
    Interesting. The following test passes:

    Code:
    /**
     * Test for http://forum.springsource.org/showthread.php?t=86640.
     */
    public void testDistinguishedNameWithDotParsesProperly() {
       DistinguishedName name = new DistinguishedName("cn=first.last,OU=DevTest Users,DC=xyz,DC=com");
       assertEquals("cn=first.last,ou=DevTest Users,dc=xyz,dc=com", name.toCompactString());
    
       // also testing the parse method, you know, to be sure... :)
       DistinguishedName dn = new DistinguishedName();
       dn.parse("cn=first.last,OU=DevTest Users,DC=xyz,DC=com");
       assertEquals("first.last", dn.getValue("cn"));
       assertEquals("DevTest Users", dn.getValue("ou"));
       assertEquals("xyz", dn.getLdapRdn(1).getValue());
       assertEquals("com", dn.getLdapRdn(0).getValue());
    }
    Ulrik Sandberg
    Jayway (www.jayway.com)
    Spring LDAP project member

  10. #10
    Join Date
    Apr 2008
    Posts
    8

    Default

    If I understand it correctly you want to use "DOMAIN\username" instead of "distinguished name" to bind to LDAP.

    ...then your user can enter 'username' & 'password' rather than 'CN=Fred,OU,something,O=somethingelse' & 'password'

    The normal answer to this is that you use a static (ie in your config file) account to bind to LDAP and then use this bind to lookup the DN that matches the username and then try and bind with the DN + password.

    The problem I have is that our companies security policy doesnt allow us to store a static account, so I've been trying to figure out if its possible to bind using the username (aka sAMAccount) + password since as you point out jXplorer does it.

    ...I think you're barking up the wrong tree to be trying to figure out why Spring doesnt like a DN of DOMAIN\username as the answer is because its not a DN! - you (we) need to find an alternative place to set the 'username' rather than setting the DN.

    Regards

    David Bevan

Tags for this Thread

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •