Spring Security
  1. Spring Security
  2. SEC-1661

LDAP search base should not require JNDI escaping in addition to LDAP escaping for special characters

    Details

    • Type: Bug Bug
    • Status: Closed
    • Priority: Minor Minor
    • Resolution: Fixed
    • Affects Version/s: 3.0.1
    • Fix Version/s: 3.1.0.RC1, 3.0.6
    • Component/s: LDAP
    • Labels:
      None

      Description

      Having double quotes in the base dn (search base) does not work.

      The following base dn value is valid, but does not work:

      • OU="Admin Users",OU=TEST-NC,DC=TESTCustomers,DC=Com

        Activity

        Hide
        Luke Taylor added a comment -

        If you are reporting an issue, please check with the latest release (3.0.5 in this case).

        The DN you've given here is not valid - it would be rejected if you entered it in an LDIF file and attempted to import it into a directory. Likewise if you used it as an argument to any LDAP operation. Quotes in DNs need to be escaped. If the name in the directory contains quotes then you need to use ou= \"Admin Users\".

        Escaping is further complicated when used from Java, because of JNDI, see:

        http://download.oracle.com/javase/jndi/tutorial/beyond/names/syntax.html

        so it wouldn't surprise me if there were other bugs in this area, but "does not work" isn't much to go on. Please explain what the directory entries contain, what configuration parameters you are using (make sure DN strings are LDAP escaped), what exceptions occur (if any) and what actually goes wrong.

        Show
        Luke Taylor added a comment - If you are reporting an issue, please check with the latest release (3.0.5 in this case). The DN you've given here is not valid - it would be rejected if you entered it in an LDIF file and attempted to import it into a directory. Likewise if you used it as an argument to any LDAP operation. Quotes in DNs need to be escaped. If the name in the directory contains quotes then you need to use ou= \"Admin Users\". Escaping is further complicated when used from Java, because of JNDI, see: http://download.oracle.com/javase/jndi/tutorial/beyond/names/syntax.html so it wouldn't surprise me if there were other bugs in this area, but "does not work" isn't much to go on. Please explain what the directory entries contain, what configuration parameters you are using (make sure DN strings are LDAP escaped), what exceptions occur (if any) and what actually goes wrong.
        Hide
        Jason Konicki added a comment -

        Sorry about the lack of information:

        I also tested with latest code from git and modified the BindAuthenticatorTests to test the quoted string, but it failed.

        Here is the stacktrace when the quoted string is used instead of the non-quoted string:
        org.springframework.security.authentication.BadCredentialsException: Bad credentials
        at org.springframework.security.ldap.authentication.LdapAuthenticationProvider.authenticate(LdapAuthenticationProvider.java:266)
        at com.hyperic.hq.security.LdapHQAuthenticationProvider.authenticateFromSearchBase(LdapHQAuthenticationProvider.java:198)
        at com.hyperic.hq.security.LdapHQAuthenticationProvider.authenticate(LdapHQAuthenticationProvider.java:165)
        at org.hyperic.hq.security.InternalAuthenticationProvider.authenticate(InternalAuthenticationProvider.java:88)
        at org.springframework.security.authentication.ProviderManager.doAuthentication(ProviderManager.java:121)
        at org.springframework.security.authentication.AbstractAuthenticationManager.authenticate(AbstractAuthenticationManager.java:49)
        at org.hyperic.hq.auth.server.session.AuthManagerImpl.authenticate(AuthManagerImpl.java:67)
        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
        at java.lang.reflect.Method.invoke(Method.java:597)
        at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:307)
        at org.springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint(ReflectiveMethodInvocation.java:183)
        at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:150)
        at org.springframework.aop.aspectj.MethodInvocationProceedingJoinPoint.proceed(MethodInvocationProceedingJoinPoint.java:80)
        at org.hyperic.hq.monitor.aop.aspects.PerformanceMonitor.monitorServiceMethod(PerformanceMonitor.java:84)
        at sun.reflect.GeneratedMethodAccessor324.invoke(Unknown Source)
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
        at java.lang.reflect.Method.invoke(Method.java:597)
        at org.springframework.aop.aspectj.AbstractAspectJAdvice.invokeAdviceMethodWithGivenArgs(AbstractAspectJAdvice.java:622)
        at org.springframework.aop.aspectj.AbstractAspectJAdvice.invokeAdviceMethod(AbstractAspectJAdvice.java:611)
        at org.springframework.aop.aspectj.AspectJAroundAdvice.invoke(AspectJAroundAdvice.java:65)
        at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172)
        at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:108)
        at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172)
        at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:89)
        at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172)
        at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:202)
        at $Proxy46.authenticate(Unknown Source)

        Show
        Jason Konicki added a comment - Sorry about the lack of information: I also tested with latest code from git and modified the BindAuthenticatorTests to test the quoted string, but it failed. Here is the stacktrace when the quoted string is used instead of the non-quoted string: org.springframework.security.authentication.BadCredentialsException: Bad credentials at org.springframework.security.ldap.authentication.LdapAuthenticationProvider.authenticate(LdapAuthenticationProvider.java:266) at com.hyperic.hq.security.LdapHQAuthenticationProvider.authenticateFromSearchBase(LdapHQAuthenticationProvider.java:198) at com.hyperic.hq.security.LdapHQAuthenticationProvider.authenticate(LdapHQAuthenticationProvider.java:165) at org.hyperic.hq.security.InternalAuthenticationProvider.authenticate(InternalAuthenticationProvider.java:88) at org.springframework.security.authentication.ProviderManager.doAuthentication(ProviderManager.java:121) at org.springframework.security.authentication.AbstractAuthenticationManager.authenticate(AbstractAuthenticationManager.java:49) at org.hyperic.hq.auth.server.session.AuthManagerImpl.authenticate(AuthManagerImpl.java:67) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25) at java.lang.reflect.Method.invoke(Method.java:597) at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:307) at org.springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint(ReflectiveMethodInvocation.java:183) at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:150) at org.springframework.aop.aspectj.MethodInvocationProceedingJoinPoint.proceed(MethodInvocationProceedingJoinPoint.java:80) at org.hyperic.hq.monitor.aop.aspects.PerformanceMonitor.monitorServiceMethod(PerformanceMonitor.java:84) at sun.reflect.GeneratedMethodAccessor324.invoke(Unknown Source) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25) at java.lang.reflect.Method.invoke(Method.java:597) at org.springframework.aop.aspectj.AbstractAspectJAdvice.invokeAdviceMethodWithGivenArgs(AbstractAspectJAdvice.java:622) at org.springframework.aop.aspectj.AbstractAspectJAdvice.invokeAdviceMethod(AbstractAspectJAdvice.java:611) at org.springframework.aop.aspectj.AspectJAroundAdvice.invoke(AspectJAroundAdvice.java:65) at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172) at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:108) at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172) at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:89) at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172) at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:202) at $Proxy46.authenticate(Unknown Source)
        Hide
        Luke Taylor added a comment -

        Ok, this still doesn't clear up what the directory contains or what the configuration valuues are. If you write a basic Java LDAP test:

        Hashtable<String,String> env = new Hashtable<String,String>();
        env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");
        env.put(Context.PROVIDER_URL, "ldap://127.0.0.1:22389/dc=springsource,dc=com"); // use your URL
        env.put(Context.SECURITY_AUTHENTICATION, "simple");
        env.put(Context.SECURITY_PRINCIPAL, "cn=admin,dc=springsource,dc=com"); // use whatever username and credentials you need to perform the search
        env.put(Context.SECURITY_CREDENTIALS, "password");

        InitialDirContext idc = new InitialDirContext(env);
        SearchControls searchControls = new SearchControls();
        searchControls.setSearchScope(SearchControls.SUBTREE_SCOPE);

        String baseDn = ?
        String username = ?

        NamingEnumeration<SearchResult> matches = idc.search(baseDn, "(cn=

        {0}

        )", new Object[]

        {username}

        , searchControls);

        while(matches.hasMore())

        { SearchResult match = matches.next(); System.out.println("**** Match: " +match.getName()); }

        Then what value of the string "baseDn" gives you a match on a valid username?

        Show
        Luke Taylor added a comment - Ok, this still doesn't clear up what the directory contains or what the configuration valuues are. If you write a basic Java LDAP test: Hashtable<String,String> env = new Hashtable<String,String>(); env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory"); env.put(Context.PROVIDER_URL, "ldap://127.0.0.1:22389/dc=springsource,dc=com"); // use your URL env.put(Context.SECURITY_AUTHENTICATION, "simple"); env.put(Context.SECURITY_PRINCIPAL, "cn=admin,dc=springsource,dc=com"); // use whatever username and credentials you need to perform the search env.put(Context.SECURITY_CREDENTIALS, "password"); InitialDirContext idc = new InitialDirContext(env); SearchControls searchControls = new SearchControls(); searchControls.setSearchScope(SearchControls.SUBTREE_SCOPE); String baseDn = ? String username = ? NamingEnumeration<SearchResult> matches = idc.search(baseDn, "(cn= {0} )", new Object[] {username} , searchControls); while(matches.hasMore()) { SearchResult match = matches.next(); System.out.println("**** Match: " +match.getName()); } Then what value of the string "baseDn" gives you a match on a valid username?
        Hide
        Luke Taylor added a comment -

        I've added the use of an LDAP DistinguishedName to the baseDn to avoid the need for JNDI escaping (see the link to the Java Tutorial above). However, the string still needs to be LDAP escaped in the configuration if it contains any special characters.

        Show
        Luke Taylor added a comment - I've added the use of an LDAP DistinguishedName to the baseDn to avoid the need for JNDI escaping (see the link to the Java Tutorial above). However, the string still needs to be LDAP escaped in the configuration if it contains any special characters.
        Hide
        Luke Taylor added a comment -

        Does the DN in the directory actually contain quotes?

        If not, then they shouldn't be there in the configuration (either escaped or otherwise).

        It looks like you are saying that the system should treat one with quotes as if they weren't there - i.e. that the original system treated DNs with and without the quotes in the same way. I'd regard that as a bug in the original system. Since quotes are valid content in a DN, if the actual value contained in the directory doesn't have any nor should the value you use to represent it elsewhere.

        Show
        Luke Taylor added a comment - Does the DN in the directory actually contain quotes? If not, then they shouldn't be there in the configuration (either escaped or otherwise). It looks like you are saying that the system should treat one with quotes as if they weren't there - i.e. that the original system treated DNs with and without the quotes in the same way. I'd regard that as a bug in the original system. Since quotes are valid content in a DN, if the actual value contained in the directory doesn't have any nor should the value you use to represent it elsewhere.
        Hide
        Jason Konicki added a comment -

        Luke,

        I received feedback from the customer, their DN was "OU=Admin-Users,OU=TEST-NC,DC=TESTCustomers,DC=Com" (without the quotes around Admin-Users).
        So it appears that the other 3rdparty Ldap must have been stripping off the quotes which would not be correct as you said.

        Thanks for the clarification on this issue.

        Show
        Jason Konicki added a comment - Luke, I received feedback from the customer, their DN was "OU=Admin-Users,OU=TEST-NC,DC=TESTCustomers,DC=Com" (without the quotes around Admin-Users). So it appears that the other 3rdparty Ldap must have been stripping off the quotes which would not be correct as you said. Thanks for the clarification on this issue.
        Hide
        Luke Taylor added a comment -

        The initial report is essentially invalid, so changing the title to reflect the minor change to the code that has actually been made - i.e. the search base should now only require LDAP escaping.

        Show
        Luke Taylor added a comment - The initial report is essentially invalid, so changing the title to reflect the minor change to the code that has actually been made - i.e. the search base should now only require LDAP escaping.

          People

          • Assignee:
            Luke Taylor
            Reporter:
            Jason Konicki
          • Votes:
            0 Vote for this issue
            Watchers:
            0 Start watching this issue

            Dates

            • Created:
              Updated:
              Resolved: