2

I configured OpenLDAP server with this configuration:

version: 1

# Entry 1: dc=unixmen,dc=com
dn: dc=unixmen,dc=com
dc: unixmen
o: unixmen
objectclass: top
objectclass: dcObject
objectclass: organization

# Entry 2: cn=ServerAdmins,dc=unixmen,dc=com
dn: cn=ServerAdmins,dc=unixmen,dc=com
cn: ServerAdmins
gidnumber: 501
objectclass: posixGroup
objectclass: top

# Entry 3: cn=rcbandit,cn=ServerAdmins,dc=unixmen,dc=com
dn: cn=rcbandit,cn=ServerAdmins,dc=unixmen,dc=com
cn: rcbandit
gidnumber: 501
givenname: rcbandit
homedirectory: /home/users/rcbandit
objectclass: inetOrgPerson
objectclass: posixAccount
objectclass: top
sn: rcbandit
uid: rcbandit
uidnumber: 1000
userpassword: {MD5}2FeO34RYzgb7xbt2pYxcpA==

And I created this Java code which searches for credentials:

public class SAuth
{

    public static void main(String[] args)
    {

        Hashtable env = new Hashtable(11);
        env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");
        env.put(Context.PROVIDER_URL, "ldap://192.168.1.177:389");
        //env.put(Context.SECURITY_AUTHENTICATION, "simple");
        //env.put(Context.SECURITY_PRINCIPAL, "cn=rcbandit,cn=ServerAdmins,dc=unixmen,dc=com");
        //env.put(Context.SECURITY_CREDENTIALS, "qwerty");

        // Enable connection pooling
        env.put("com.sun.jndi.ldap.connect.pool", "true");

        try
        {
            LdapContext ctx = new InitialLdapContext(env, null);
            ctx.setRequestControls(null);
            NamingEnumeration<?> namingEnum = ctx.search("cn=rcbandit,cn=ServerAdmins,dc=unixmen,dc=com", "(objectclass=*)", getSimpleSearchControls());
            while (namingEnum.hasMore())
            {
                SearchResult result = (SearchResult) namingEnum.next();
                Attributes attrs = result.getAttributes();
                System.out.println(attrs.get("cn"));
                System.out.println(attrs.get("gidnumber"));
                System.out.println(attrs.get("givenname"));
                System.out.println(attrs.get("homedirectory"));
                System.out.println(attrs.get("objectclass"));
                System.out.println(attrs.get("objectclass"));
                System.out.println(attrs.get("objectclass"));
                System.out.println(attrs.get("sn"));
                System.out.println(attrs.get("uid"));
                System.out.println(attrs.get("uidnumber"));
                System.out.println(attrs.get("userpassword"));

            }
            namingEnum.close();
            ctx.close();
        }
        catch (NamingException e)
        {
            e.printStackTrace();
        }

    }

    private static SearchControls getSimpleSearchControls()
    {
        SearchControls searchControls = new SearchControls();
        searchControls.setSearchScope(SearchControls.SUBTREE_SCOPE);
        searchControls.setTimeLimit(30000);
        //String[] attrIDs = {"objectGUID"};
        //searchControls.setReturningAttributes(attrIDs);
        return searchControls;
    }
}

When I run the code I get this result:

cn: rcbandit
gidNumber: 501
givenName: rcbandit
homeDirectory: /home/users/rcbandit
objectClass: inetOrgPerson, posixAccount, top
objectClass: inetOrgPerson, posixAccount, top
objectClass: inetOrgPerson, posixAccount, top
sn: rcbandit
uid: rcbandit
uidNumber: 1000
null

What is the proper way to authenticate a user name and password?

2 Answers 2

1

Looks like your are able to get the information with an unauthenticated connection. You however need to perform bind() operation in LDAP to perform authentication.

The function of the Bind operation is to allow authentication information to be exchanged between the client and server. The Bind operation should be thought of as the "authenticate" operation.

More information here.

Bind operation in your code will be performed when you create InitialLdapContext. However, you need to have credentials, security principal which you want to authenticate (that you have commented out). At present you are reading the allowed information available through unauthenticated channel.

The correct way would be to bind using instantiating InitialLdapContext with principal and credentials you want to authenticate and catching javax.naming.AuthenticationException for the failed ones.

env.put(Context.PROVIDER_URL, "ldap://XXX.XXX.XXX.XXX:XXX");
env.put(Context.SECURITY_AUTHENTICATION, "simple");
env.put(Context.SECURITY_PRINCIPAL, "cn=rcbandit,cn=ServerAdmins,dc=unixmen,dc=com");
env.put(Context.SECURITY_CREDENTIALS, "xxxx");
..
LdapContext ctx = new InitialLdapContext(env, null);

}
catch(AuthenticationException ex) {
...
}
3
  • 1
    There is one problem, I want to use one connection which will be used to authenticate all users. Could you please edit my code how it could be fixed? Commented Jan 11, 2014 at 19:33
  • @PeterPenzov It is an interesting requirement and I cannot think of a way the environment might allow this by default unless you provide some specifics around them. Commented Jan 11, 2014 at 20:04
  • You mean that I need to configure something additional into the LDAP server? Commented Jan 11, 2014 at 20:07
1

This would be easy to do if you were not using JNDI. All the Java LDAP SDKs allow a method to bind over the same LDAP Connection. I would STRONGLY recommend you utilize a modern, current LDAP SDK and NOT use JNDI. Currently we like https://www.unboundid.com/products/ldap-sdk/.

Why one connection? - No configuration on LDAP server required.

Here is a sample that uses an admin to search and then bind as a returned entry:

package com.willeke.samples.ldap.jndi;

import java.util.*;

import javax.naming.*;
import javax.naming.directory.*;

/**
 * 
 * <p>
 * Title: BasicJNDISearch
 * </p>
 * 
 * <p>
 * Description: Provides a sample for performing JNDI Searches
 * </p>
 * 
 * @author Jim Willeke
 * @version 1.0
 */
public class BasicAdminSearchBind
{
    public BasicAdminSearchBind(String[] args)
    {
    super();

    try
    {
        BasicAdminSearchBind.doBasicSearch(args);
    }
    catch (Exception ex)
    {
        ex.printStackTrace();
    }
    }

    /**
     * 
     * @param stid
     *            String - Standard ID (uid)
     * @throws Exception
     *             -
     */
    public static void doBasicSearch(String[] args) throws Exception
    {
    System.out.println("Performing LDAP Search with:");
    System.out.println("    ldapHostName = " + args[0]);
    System.out.println("        ldapPort = " + args[1]);
    System.out.println("          bindDn = " + args[2]);
    System.out.println("       bindDnPwd = " + args[3]);
    System.out.println("      searchBase = " + args[4]);
    System.out.println("          filter = (" + args[5] + "=" + args[6] + ")");
    System.out.println("          Scope: = SUBTREE_SCOPE");
    // Get the context for the admin account
    DirContext adminCtx = getDirContext(args[0], args[1], args[2], args[3]);
    SearchControls constraints = new SearchControls();
    // Set the Scope of the search
    constraints.setSearchScope(SearchControls.SUBTREE_SCOPE);
    // Create the filter from args
    String filter = "(" + args[5] + "=" + args[6] + ")";
    // Search for objects with those matching attributes
    NamingEnumeration<?> answer = adminCtx.search(args[4], filter, constraints);
    //formatResults(answer);
    SearchResult sr = (SearchResult) answer.next();
    String userDN = sr.getNameInNamespace();
    //bind as returned entry
    try
    {
        DirContext userCtx = bindAsEntry(args[0], args[1],  userDN, "Secret Password");
        System.out.println("We are now bound as the User: "+ userDN);
        // we could do something with the userCtx here.
        userCtx.close();
    }
    catch (Exception e)
    {
       System.err.println("We failed to make a bind as " + userDN + "\n" + e.getMessage());
    }
    adminCtx.close();
    }

    /**
     * 
     * @param ldapHostName
     * @param ldapPost
     * @param bindDn
     * @param bindDnPwd
     * @return
     * @throws NamingException 
     * @throws Exception
     */
    private static DirContext bindAsEntry(String ldapHostName, String ldapPost, String bindDn, String bindDnPwd) throws NamingException 
    {
    Hashtable<String, String> env = new Hashtable<String, String>(11);
    env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");
    env.put(Context.PROVIDER_URL, "ldap://" + ldapHostName + ":" + ldapPost);
    env.put(Context.SECURITY_PRINCIPAL, bindDn);
    env.put(Context.SECURITY_CREDENTIALS, bindDnPwd);
    // Create the initial context
    DirContext ctx = new InitialDirContext(env);
    return ctx;
    }

    /**
     * Generic method to obtain a reference to a DirContext
     * 
     * @param ldapHostName
     * @param ldapPost
     * @param bindDn
     * @param bindDnPwd
     */
    public static DirContext getDirContext(String ldapHostName, String ldapPost, String bindDn, String bindDnPwd) throws Exception
    {
    Hashtable<String, String> env = new Hashtable<String, String>(11);
    env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");
    env.put(Context.PROVIDER_URL, "ldap://" + ldapHostName + ":" + ldapPost);
    env.put(Context.SECURITY_PRINCIPAL, bindDn);
    env.put(Context.SECURITY_CREDENTIALS, bindDnPwd);
    // Create the initial context
    DirContext ctx = new InitialDirContext(env);
    return ctx;
    }

    /*
     * Generic method to format the NamingEnumeration returned from a search.
     */
    public static void formatResults(NamingEnumeration<?> enumer) throws Exception
    {
    int count = 0;
    try
    {
        while (enumer.hasMore())
        {
        SearchResult sr = (SearchResult) enumer.next();
        System.out.println("SEARCH RESULT:" + sr.getName());
        formatAttributes(sr.getAttributes());
        System.out.println("====================================================");
        count++;
        }
        System.out.println("Search returned " + count + " results");
    }
    catch (NamingException e)
    {
        e.printStackTrace();
    }
    }

    /*
     * Generic method to format the Attributes .Displays all the multiple values of each Attribute in the Attributes
     */
    public static void formatAttributes(Attributes attrs) throws Exception
    {
    if (attrs == null)
    {
        System.out.println("This result has no attributes");
    }
    else
    {
        try
        {
        for (NamingEnumeration<?> enumer = attrs.getAll(); enumer.hasMore();)
        {
            Attribute attrib = (Attribute) enumer.next();

            System.out.println("ATTRIBUTE :" + attrib.getID());
            for (NamingEnumeration<?> e = attrib.getAll(); e.hasMore();)
            {
            Object value = e.next();
            boolean canPrint = isAsciiPrintable(value);
            if (canPrint)
            {
                System.out.println("\t\t        = " + value);
            }
            else
            {
                System.out.println("\t\t        = <-value is not printable->");
            }
            }
        }
        }
        catch (NamingException e)
        {
        e.printStackTrace();
        }
    }
    }

    /**
     * Check to see if this Object can be printed.
     * 
     * @param obj
     * @return
     */
    public static boolean isAsciiPrintable(Object obj)
    {
    String str = null;
    try
    {
        str = (String) obj;
    }
    catch (Exception e)
    {
        return false;
        // TODO Auto-generated catch block e.printStackTrace();
    }
    if (str == null)
    {
        return false;
    }
    int sz = str.length();
    for (int i = 0; i < sz; i++)
    {
        if (isAsciiPrintable(str.charAt(i)) == false)
        {
        return false;
        }
    }
    return true;
    }

    /**
     * Used by isAsciiPrintable(Object obj)
     * 
     * @param ch
     * @return
     */
    public static boolean isAsciiPrintable(char ch)
    {
    return ch >= 32 && ch < 127;
    }

    /**
     * Does a simple search on the LDAP Directory
     * 
     * String ldapHostName = args[0]; String ldapPort = args[1]; String bindDn = args[2]; String bindDnPwd = args[3]; String searchBase = args[4]; // String searchScope=args[4]; String searchAttribute = args[5];
     * String searchAttributeValue = args[6];
     * 
     * @param args
     * 
     */
    public static void main(String[] args)
    {
    if (args.length == 7)
    {
        BasicAdminSearchBind basicjndisearch = new BasicAdminSearchBind(args);
    }
    else
    {
        System.out.println("\nYou must provide ldapHostName, ldapPort, bindDn, bindDnPwd, searchBase, searchAttribute and searchAttributeValue on the command line!\n");
    }
    }
}

-jim

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.