Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Tomcat 8 - LDAP: NameNotFoundException error code 32, remaining name empty string

Trying to migrate an application from WebLogic 12.2.1 to Tomcat 8.5.4, what under Weblogic was an entry as Foreign JNDI Providers for an LDAP connection has been migrated to a new Resource under Tomcat.

Following this advice on Stack Overflow, a custom LdapContextFactory has been packaged as a new jar file under Tomcat lib folder.

In the Tomcat server.xml file the following GlobalNamingResources/Resource has been configured:

    <Resource name="ldapConnection" 
        auth="Container"
        type="javax.naming.ldap.LdapContext"
        factory="com.sample.custom.LdapContextFactory"
        singleton="false"
        java.naming.referral="follow"
        java.naming.factory.initial="com.sun.jndi.ldap.LdapCtxFactory"
        java.naming.provider.url="ldap://some.host:389"
        java.naming.security.authentication="simple"
        java.naming.security.principal="CN=some,OU=some,OU=some,DC=some,DC=a,DC=b"
        java.naming.security.credentials="password"
        com.sun.jndi.ldap.connect.pool="true"
        com.sun.jndi.ldap.connect.pool.maxsize="10"
        com.sun.jndi.ldap.connect.pool.prefsize="4"
        com.sun.jndi.ldap.connect.pool.timeout="30000" />

The connection above works fine when browsing the LDAP directory via an LDAP browser like Apache Directory Studio / LDAP Browser embedded in Eclipse.

The custom com.sample.custom.LdapContextFactory is quite simple:

public class LdapContextFactory implements ObjectFactory {

    public Object getObjectInstance(Object obj, Name name, Context nameCtx, Hashtable<?, ?> environment)
            throws Exception {

        Hashtable<Object, Object> env = new Hashtable<>();
        Reference reference = (Reference) obj;
        Enumeration<RefAddr> references = reference.getAll();

        while (references.hasMoreElements()) {
            RefAddr address = references.nextElement();
            String type = address.getType();
            String content = (String) address.getContent();
            env.put(type, content);
        }
        return new InitialLdapContext(env, null);
    }
}

However, at start-up Tomcat is throwing the following exception:

07-Sep-2016 15:04:01.064 SEVERE [main] org.apache.catalina.mbeans.GlobalResourcesLifecycleListener.createMBeans Exception processing Global JNDI Resources
 javax.naming.NameNotFoundException: [LDAP: error code 32 - 0000208D: NameErr: DSID-031001E5, problem 2001 (NO_OBJECT), data 0, best match of:
    ''
 ]; remaining name ''
    at com.sun.jndi.ldap.LdapCtx.mapErrorCode(LdapCtx.java:3160)
    at com.sun.jndi.ldap.LdapCtx.processReturnCode(LdapCtx.java:3081)
    at com.sun.jndi.ldap.LdapCtx.processReturnCode(LdapCtx.java:2888)
    at com.sun.jndi.ldap.LdapCtx.c_listBindings(LdapCtx.java:1189)
    at com.sun.jndi.toolkit.ctx.ComponentContext.p_listBindings(ComponentContext.java:592)
    at com.sun.jndi.toolkit.ctx.PartialCompositeContext.listBindings(PartialCompositeContext.java:330)
    at com.sun.jndi.toolkit.ctx.PartialCompositeContext.listBindings(PartialCompositeContext.java:317)
    at javax.naming.InitialContext.listBindings(InitialContext.java:472)
    at org.apache.catalina.mbeans.GlobalResourcesLifecycleListener.createMBeans(GlobalResourcesLifecycleListener.java:136)
    at org.apache.catalina.mbeans.GlobalResourcesLifecycleListener.createMBeans(GlobalResourcesLifecycleListener.java:145)
    at org.apache.catalina.mbeans.GlobalResourcesLifecycleListener.createMBeans(GlobalResourcesLifecycleListener.java:110)
    at org.apache.catalina.mbeans.GlobalResourcesLifecycleListener.lifecycleEvent(GlobalResourcesLifecycleListener.java:82)
    at org.apache.catalina.util.LifecycleBase.fireLifecycleEvent(LifecycleBase.java:94)
    at org.apache.catalina.util.LifecycleBase.setStateInternal(LifecycleBase.java:401)
    at org.apache.catalina.util.LifecycleBase.setState(LifecycleBase.java:345)
    at org.apache.catalina.core.StandardServer.startInternal(StandardServer.java:784)
    at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:152)
    at org.apache.catalina.startup.Catalina.start(Catalina.java:655)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:497)
    at org.apache.catalina.startup.Bootstrap.start(Bootstrap.java:355)
    at org.apache.catalina.startup.Bootstrap.main(Bootstrap.java:495)

Similar questions and investigations suggest an invalid LDAP DN, but:

  • The same LDAP configuration works fine via an LDAP Client
  • No search is actually performed, at start-up time Tomcat throws this exception without any query
  • The error suggests an empty string '' as remaining name, hence not really something not found, apparently

Question(s): Is this the correct way to migrate an Foreign JNDI Providers entry from WebLogic to Tomcat? How to fix an invalid LDAP DN entry with an empty remaining name? Could it be a missing baseDN to configure somewhere?


Update
The same exact error happens when changing the LdapContextFactory to the following, as suggested via comments:

public Object getObjectInstance(Object obj, Name name, Context nameCtx, Hashtable<?, ?> environment)
        throws Exception {

    Hashtable<Object, Object> env = new Hashtable<>();
    Reference reference = (Reference) obj;
    Enumeration<RefAddr> references = reference.getAll();

    String providerUrl = "no valid URL";

    while (references.hasMoreElements()) {
        RefAddr address = references.nextElement();
        String type = address.getType();
        String content = (String) address.getContent();

        switch (type) {
        case Context.PROVIDER_URL:
            env.put(Context.PROVIDER_URL, content);
            providerUrl = content;
            break;

        default:
            env.put(type, content);
            break;
        }
    }

    InitialLdapContext context = null;
    Object result = null;
    try {
        context = new InitialLdapContext(env, null);

        LOGGER.info("looking up for " + providerUrl);
        result = context.lookup(providerUrl);
    } finally {
        if (context != null) {
            context.close();
        }
    }
    LOGGER.info("Created new LDAP Context");
    return result;
}

Change is confirmed via logging, to make sure it was deployed properly.

The involved listener is defined by default at the top of the server.xml file as

<Listener className="org.apache.catalina.mbeans.GlobalResourcesLifecycleListener" />

And cannot be disabled as per official documentation:

The Global Resources Lifecycle Listener initializes the Global JNDI resources defined in server.xml as part of the Global Resources element. Without this listener, none of the Global Resources will be available.


The same also happens on Tomcat version 8.5.5 and 7.0.69: simply adding the new global resource as above and the additional jar providing the factory above, the exception pointing at an empty remaining name will be thrown.

like image 749
A_Di-Matteo Avatar asked Sep 07 '16 14:09

A_Di-Matteo


1 Answers

The stacktrace went away by appending to the java.naming.provider.url property the LDAP schema DN, using the first factory implementation provided in the question.

Below a screenshot of the LDAP client used in this context, the Apache Directory Studio / LDAP Browser embedded in Eclipse, from which it was possible to browse the concerned LDAP simply using the initial values of the question.

enter image description here

By appending the schema DN of the Root element to the connection URL, the exception went away and the LDAP resource is now shared via JNDI in Tomcat 8.


Further details as outcome of the troubleshooting:

In Tomcat 8 global resources are handled via a global resource listener, the GlobalResourcesLifecycleListener, defined by default in the server.xml file. Such a listener invokes a context.listBindings("") on bean creation, hence effectively browsing the LDAP directory.

This initial browsing may most probably be the difference between Tomcat and WebLogic, where LDAP is looked up via JNDI only when required, hence via direct query, rather than at start-up with a generic query. As such, in Tomcat the LDAP url would need further details, that is, a slightly different configuration as part of its url to directly point to a valid base DN.

From official WebLogic documentation:

On start up, WebLogic Server attempts to connect to the JNDI source. If the connection is successful, WebLogic Server sets up the requested objects and links in the local JNDI tree, making them available to WebLogic Server clients.

Hence, a connection is rather simpler than a listBindings:

Enumerates the names bound in the named context, along with the objects bound to them. The contents of any subcontexts are not included.

like image 70
A_Di-Matteo Avatar answered Nov 06 '22 19:11

A_Di-Matteo