LDAP Authentication & Active Directory

Abstract

Your application may need to authenticate users managed by an Active Directory (AD). AD is Microsoft's directory service server, and as such it provides some level of access via LDAP. This article shows how to authenticate using LDAP and how to use DNS SRV to automatically lookup an AD to authenticate against. (October 2013).

LDAP authentication

LDAP (Lightweight Directory Access Protocol) is an application protocol to access and maintain directory services. In LDAP, a BIND operation, when creating a session, etablishes an authenticated session [1]. In Java, LDAP oparations are done through the JNDI (Java Naming and Directory Interface) API [2]. More specifically, the BIND operation is done by creating an InitialDirContext with the required authentication information.

// the protocol is either ldap or ldaps
String ldapUrl = "ldap://directory.service.provider:389";

Hashtable env = new Hashtable();
env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");
env.put(Context.PROVIDER_URL, ldapUrl);

// username/password authentication
env.put(Context.SECURITY_AUTHENTICATION, "simple");
env.put(Context.SECURITY_PRINCIPAL, username);
env.put(Context.SECURITY_CREDENTIALS, password);

// bind with the above authentication info
DirContext context = new InitialDirContext(env);

Context.SECURITY_AUTHENTICATION specifies the mechanism of authentication.

AnonymousThe default mechanism is "none"
SimpleThis mechanism consists of authenticating using the user name (usually fully qualified DN) and the password in clear-text. This is not a secure mechanism because the password is transmitted in clear-text. Set the value to "simple"
SASLOnly in LDAPv3, this pluggable mechanism uses SASL (Simple Authentication and Security Layer). Currently defined SASL mechanisms are: Anonymous, CRAM-MD5, Digest-MD5, External, Kerberos V4, Kerberos V5, SecurID, S/Key [3].

To use a particular SASL mechanism, specify its (Internet Assigned Numbers Authority) IANA-registered name as value. An ordered list of space-separated mechanism names, such as "DIGEST-MD5 SECURID", may also be used; the LDAP server will use the first mechanism it supports.

DNS SRV record

The authentication above requires the knowledge of the host and the port of the LDAP server. But how can a LDAP client locate such provider in the first place? This is where DNS (Domain Name System) comes into play, more specifically the DNS RR (Resource Record) called Service record. A SRV record specifies the location of the server(s) for a specfic protocol and domain [4]. If a LDAP client wants to discover a LDAP server that supports TCP protocol and provides directory service for the domain example.com, for instance, it does a lookup of _ldap._tcp.example.com.

Microsoft defines SRV record for its domain controller that can be used to locate the AD(s) for a given domain. The SRV record to look up for is _ldap._tcp.dc._msdcs.example.com. Below is an example of lookup of AD servers for the domain lab.nwsummit.com using the (Linux) nslookup command:

% nslookup
> set type=srv
> _ldap._tcp.dc._msdcs.lab.nwsummit.com
Server:		127.0.1.1
Address:	127.0.1.1#53

Non-authoritative answer:
_ldap._tcp.dc._msdcs.lab.nwsummit.com	service = 0 100 389 labdc01.lab.nwsummit.com.
_ldap._tcp.dc._msdcs.lab.nwsummit.com	service = 0 100 389 labdc03.lab.nwsummit.com.
_ldap._tcp.dc._msdcs.lab.nwsummit.com	service = 0 100 389 labdc02.lab.nwsummit.com.

Authoritative answers can be found from:
labdc01.lab.nwsummit.com	internet address = 10.0.0.11
labdc03.lab.nwsummit.com	internet address = 192.168.0.2
labdc02.lab.nwsummit.com	internet address = 10.0.0.12

The information returned for each SRV record contains - in order - the priority, the weight, the port, and the hostname.

DNS SRV lookup in Java

Now, how do we make a DNS SRV lookup in Java? The answer is JNDI. DNS is a naming service and JNDI is an API that abstracts the specific implementation of naming and directory services. The below sample code retrieves the SRV atributes of the "_ldap._tcp.dc._msdcs.lab.nwsummit.com" object from DNS. (Note that I've changed vocalulary to use Java centric terms instead of DNS centric, but they all refer to the same thing).

// lookup DNS SRV records
DirContext context = new InitialDirContext();
Attributes attributes = context.getAttributes(
  "dns:/_ldap._tcp.dc._msdcs.lab.nwsummit.com",
  new String[] { "SRV" });

// display the records
Enumeration attrs = attributes.getAll();
while (attrs.hasMoreElements())
{
  Attribute attr = attrs.nextElement();
  System.out.println( "--- Attribute ID: " + attr.getID() );
  Enumeration values = attr.getAll();
  while (values.hasMoreElements())
  {
    Object value = values.nextElement();
    System.out.println(value.getClass().getName() + " == " + value);
  }
}

The above code produces the below output:

--- Attribute ID: SRV
java.lang.String == 0 100 389 labdc03.lab.nwsummit.com.
java.lang.String == 0 100 389 labdc02.lab.nwsummit.com.
java.lang.String == 0 100 389 labdc01.lab.nwsummit.com.

As expected, there's only one attribute returned, which is the specified SRV attribute. Each value of the attribute is a String representation of the service record in the same format as specified in the DNS RR, namely priority, weight, port and hostname. Now it's just a matter of parsing the string to get the port and host of the AD server to connect to for authentication.

References

[1] LDAP
Basic information on the Lightweight Directory Access Protocol from Wikipedia
[2] JNDI
Java Naming and Directory Interface
[3] Advanced Topics for LDAP Users
Part of the Java Tutorials series with emphasis on LDAP via JNDI
[4] DNS SRV
RFC-2782 A DNS RR for specifying the location of services (DNS SRV)
[5] Domain controller DNS SRV
Microsoft's KB for locating domain controllers for Active Directory
Last updated: 2013-10-23 22:39:26 -0700