How LDAP Authentication works with Active Directory and the UWWI
This document provides background on what LDAP authentication is,
what specific LDAP authentication methods and mechanisms Active
Directory and UWWI supports, and finally gives some guidance on
which method and mechanism you should use. LDAP AuthenticationLDAP
authentication is a loaded term which simply means to issue the
LDAP BIND operation. Since the LDAP BIND operation is very
flexible, this means there are a variety of forms this
authentication might take. These forms include extremes such as:
cleartext passwords over the wire no identifying name specified
choosing not to authenticate authentication with a certificate
authentication using some custom methodas well as more traditional
forms where a name (username) and password are used, and the
password is encrypted on the wire or a private/public key algorithm
used to protect the password. Active Directory supports all the
LDAP standard authentication mechanisms, as well as a few more, but
many of these more extreme forms are not supported by UWWI, usually
because they violate computing standards.The LDAP standard
introduces the various forms of authentication by first
categorizing them as authentication methods, with various
authentication mechanisms underlying each method. This document
follows that organizational precedent.Authentication MethodsThere
are two primary authentication methods that must be supported by
RFC standard: Simple Authentication Method SASL Authentication
MethodOther authentication methods *may* be supported, but are not
part of the LDAP standard and so are likely to be less widely used
by LDAP applications.Each method has many authentication
mechanisms, and the RFC standards specify that a number of these
mechanisms must be supported.Active Directory may accept a number
of additional methods, but because of lack of good Microsoft
documentation, these additional methods can't be covered here at
all. However, this lack of documentation also means that
applications won't know how to use them either, so Active Directory
might as well not provide these undocumented methods at all.Simple
Authentication MethodThe simple authentication method provides
three authentication mechanisms: Anonymous Authentication Mechanism
of Simple BindA simple LDAP bind operation with a name and password
value of zero length. UWWI allows this, but you can't do anything
meaningful. Unauthenticated Authentication Mechanism of Simple
BindA simple LDAP bind operation with a non-zero length name that
corresponds to a valid user entry and a password value of zero
length. UWWI allows this, but you can't do anything meaningful.
Name/Password Authentication Mechanism of Simple BindA simple LDAP
bind operation with a name that corresponds to a valid user entry
and the password value corresponding to that user entry. The
password is sent in cleartext over the wire.
This is possible with UWWI, however, using this mechanism
without first initiating session level encryption is prohibited by
UWWI because it fails to meet UW computing standards. Currently
there is no way to turn off this functionality, but should a way to
turn this mechanism (w/o session-level encryption) become
available, it will be turned off.
You can initiate session level encryption via a StartTLS
operation or by using IPsec. We *strongly* recommend that a network
sniffer first be used with any application that must use this
authentication mechanism to verify that it is using session-level
encryption and not exposing UW NetID passwords.Fast Bind for
Authentication Only
In addition to authentication methods and mechanisms, there is
one noteworthy LDAP session option that may be of interest. The
Concurrent bind or Fast Bind session option can be used by
applications to authenticate users, assuming that only
authentication is needed, and no subsequent LDAP operations
requiring authorization are needed. A Fast Bind does not build a
user token; it only verifies the username and password--so
subsequent LDAP operations that would require the authorization
information in that token are not allowed. The LDAP Fast Bind
requires that the Simple Authentication Method be used, and so TLS
or IPsec must be used in association with making use of this
option. Sample code and more information are available on MSDN.
SASL Authentication MethodThe SASL authentication method is a
framework for providing authentication and data security services
in connection-oriented protocols via replaceable mechanisms. The
SASL standard (RFC4422) only defines one such mechanism, the
EXTERNAL authentication mechanism, however the SASL standard
expects many authentication mechanisms to be defined to work within
its framework.Active Directory supports four SASL authentication
mechanisms: GSSAPI Authentication MechanismThe GSSAPI
authentication mechanism (RFC2743) allows the client to pass GSSAPI
tokens to the server to establish their credentials. In this case,
valid GSSAPI tokens are a Kerberos TGT or NTLM token (although some
Microsoft documentation erroneously claims that GSSAPI is limited
to Kerberos). A confidentiality feature can be employed as part of
GSSAPI to encrypt the token and authentication exchange.
A SASL GSSAPI LDAP bind operation with a name that corresponds
to a valid user entry and the password value corresponding to that
user entry. The password is not sent over the wire at all, instead
the token representing the user is sent.
UWWI recommends this authentication mechanism. When it is known
that the client can only negotiate NTLMv2, we suggest that a
network sniffer be used with any application to verify that the
confidentiality feature is used to protect NTLM tokens. GSS-SPNEGO
Authentication MechanismThe GSS-SPNEGO authentication mechanism
(RFC4178) is actually the GSSAPI authentication mechanism but with
a client-server negotiation mechanism that provides for selection
of the preferred security mechanism that both client and server
support. In this case, the server will prefer Kerberos then NTLMv2
then NTLM (UWWI does not support LM). For that reason, refer to the
GSSAPI authentication mechanism for further details. EXTERNAL
Authentication MechanismThe EXTERNAL authentication mechanism
allows the client to request that the server use credentials
established by a means external to the mechanism to authenticate
the client. The external means might be via information in IPsec or
TLS or some other other means. What means are used depends on the
server, and there is no negotiation of what those means are, so the
client must know beforehand what the means are, and provide
them.
A SASL EXTERNAL LDAP bind operation is issued with either:- a
non-zero length name that corresponds to the valid user entry
desired or - a zero length name if the client wants the server to
map the results of the external mechanism to the appropriate user
entry.
Active Directory supports the SASL EXTERNAL authentication
mechanism via TLS and user certificates that are mapped onto user
accounts. This is considered a very strong authentication
mechanism.
UWWI does not support user certificates at this time, so the
SASL EXTERNAL authentication mechanism is not an option at this
time. DIGEST-MD5 Authentication MechanismThe DIGEST-MD5
authentication mechanism (RFC2831) provides a mechanism for the
HTTP Digest authentication (RFC2617) challenge/response paradigm to
be used within the SASL framework. Because this mechanism relies on
HTTP 1.1, it has a smaller set of uses.
This mechanism sends a MD5 checksum of the password over the
wire without encryption. This is superior to sending a cleartext
password with a simple bind, but is not considered strong
authentication.
UWWI recommends using GSSAPI with Kerberos instead of DIGEST-MD5
authentication. Which Method and Mechanism Should I Use?UWWI
supports AND recommends only two authentication methods/mechanisms:
Simple Authentication Method, using the Name/Password
Authentication Mechanism of Simple Bind, having first initiated
session-level encryption SASL Authentication Method, using the
GSSAPI Authentication Mechanism, employing Kerberos, NTLMv2, or
NTLMWhich method/mechanism you choose, will likely be dependent on
which method and mechanism is supported by your operating system
and application. Since passwords are sensitive data, you should
choose the strongest method and mechanism available. The strongest
possible method and mechanism is SASL using Kerberos and employing
session-level encryption. From strongest to least, the possible
combinations are: SASL, using GSSAPI, employing Kerberos and
session-level encryption SASL, using GSSAPI, employing NTLMv2 and
session-level encryption SASL, using GSSAPI, employing Kerberos
SASL, using GSSAPI, employing NTLMv2 SASL, using GSSAPI, employing
NTLM and session-level encryption Simple Authentication, using
name/password and employing session-level encryption. SASL, using
GSSAPI, employing NTLMAs noted above, Simple Authentication, using
name/password without session-level encryption is prohibited by
UWWI.For configuration details, see 'How to Configure LDAP so your
Application Works with UWWI'Reference DocumentsLDAP Directories
ExplainedLDAP: Authentication Methods and Security
MechanismsUnderstanding LDAP whitepaperMSDN: Binding to an LDAP
ServerMSDN: Using LDAP for Password AuthenticationJNDI, Active
Directory, and AuthenticationOracle Directory Server LDAP
Authentication MechanismsOracle SASL AuthenicationConfiguring LDAP
Authentication Using Active DirectoryTable of Contents1. 1.
Overview2. 2. Steps to Complete1. 2.1. Enabling LDAP for the
Instance2. 2.2. Explanation of Settings3. 2.3. Enabling LDAP for
Users4. 2.4. LDAP With a Firewall3. 3. Related1. 1. Overview2. 2.
Steps to Complete1. 2.1. Enabling LDAP for the Instance2. 2.2.
Explanation of Settings3. 2.3. Enabling LDAP for Users4. 2.4. LDAP
With a Firewall3. 3. RelatedOverviewSugar can be configured to
accept Lightweight Directory Access Protocol (LDAP) authentication
if your organization has implemented LDAP or Active Directory
authentication. When users in your system attempt to log into
Sugar, the application will authenticate them against your LDAP
directory or Active Directory. If authentication is successful, the
user is allowed to log into Sugar. If the authentication is
unsuccessful, Sugar will then attempt to verify the provided
credentials against its own database of valid usernames and
passwords.You will need to add a user to your active directory for
the purpose of authenticating from your SugarCRM to your active
directory to read the LDAP. The user should be a service account
that needs read-only access to Active Directory. Configuring Active
Directory to support LDAP is beyond the scope of this
document.Steps to CompleteEnabling LDAP for the Instance1. Log into
Sugar as an administrator and navigate to Admin > Password
Management2. Scroll down to the 'LDAP Support' section at the
bottom of the page.3. Select the 'Enable LDAP Authentication'
option. Sugar will then display additional fields for entering the
necessary information.
4. Enter in the appropriate information and then click the
'Save' button.
Explanation of SettingsHere is a better explanation of the above
settings: Enable LDAP - Yes Server enter the FQDN of your Active
Directory Server (example: MYSERVER.MYDOMAIN.com. This should be
your Domain Controller) Port enter 389 (This is the default port
enter it unless you have some custom configuration for LDAP) Base
DN enter OU=sugarcrm,DC=MYSERVER,DC=MYDOMAIN,DC=com (The OU
OU=sugarcrm is the actual OU in Active Directory that you chose to
put your users in. Note this OU does not have to called sugarcrm.
It can be called anything you want or any OU that has the users you
want to be in your SugarCRM. Please confirm the group is an OU and
not a CN, if CN you can use the designator CN=Users for example)
Bind Attribute enter userPrincipalName (This is what is used for
Active Directory and is case sensitive.) Login Attribute enter
sAMAccountName (This is what is used for Active Directory and is
case sensitive.) Authenticated User enter
[email protected] or domain\userfirstname.userlastname
(You need to add a user to your active directory for the purpose of
authenticating from your SugarCRM to your active directory to read
the LDAP. This user should be a service account that needs only
read access to Active Directory. If you chose to enter int his
format domain\userfirstname.userlastname enter it with two
backslashes like domain\\userfirstname.userlastname because when
you click save Sugar will remove one of the backslashes.)
Authenticated Password enter the password of the authenticated user
you created. Auto Create Users (Leave this option unchecked. When
you create an AD user you dont have to create their account in
SugarCRM you just have them log in and SugarCRM will create the
account automatically.)Enabling LDAP for Users1. Navigate to Admin
> User Management > {user name} > Advanced Tab2. Select
LDAP Authentication for this user.
Once this is selected, Sugar will synchronize their Active
Directory user name and present the password on the LDAP port. When
the User Logs in they will use their active directory username and
password.
LDAP With a FirewallIf the Active Directory Authentication
server is behind a corporate firewall and your instance of Sugar is
hosted in our On-Demand environment, then please visit the
following article to ensure the appropriate IP range is open on
your firewall to allow communication with the Active Directory
server. A rule will need to be created allowing the LDAP
bi-directional communication for the necessary IP range. This can
be the standard LDAP port 389 or you can use LDAP over SSL.Using
LDAP for Password AuthenticationConcurrent bind, also known as fast
bind, enables an application to determine if multiple users have
valid IDs and passwords and if their accounts are enabled. By using
concurrent binding, the application can request multiple bind
operations by way of a single LDAP connection.Unlike a normal LDAP
bind, a concurrent bind does not determine a user group association
or build a security token; it only determines if the user has a
valid ID and password. This enables the concurrent bind to complete
in a fraction of the time of a normal bind.To enable concurrent
bind on an LDAP connection, the application sets the
LDAP_OPT_FAST_CONCURRENT_BIND session option after the LDAP
connection has been initialized, but before any binds are
performed. When concurrent binding is enabled for a particular LDAP
session, it cannot be disabled without closing the session
connection.Concurrent bind cannot be enabled on an LDAP session if
signing or data encryption are enabled. Attempting to enable
concurrent bind on sessions with signing or data encryption will
fail the ldap_set_option call and return an
LDAP_UNWILLING_TO_PERFORM error code.When concurrent bind is
enabled on an LDAP session, only simple bind operations may be
performed in that session and all simple binds are fast binds. As a
result all subsequent bind requests will not be serialized
internally by the LDAP client and the binds will not generate a
security token. Any binds performed in this session are performed
as anonymous, and because data encryption is not allowed any data
sent through this session will appear on the network in an
unencrypted form. If the application attempts to use a non-simple
bind on a session with concurrent bind enabled, the call will fail
and return an LDAP_UNWILLING_TO_PERFORM error code.The following
example code shows how to create an LDAP session with concurrent
bind enabled.C++Copy
ULONG ldap_open_fast_bind_session(LPTSTR pHostName, PLDAP
pSession){ ULONG lRtn = LDAP_SUCCESS; ULONG version =
LDAP_VERSION3; // Initialize session. LDAP_PORT is the default
port, 389. pSession = ldap_init(pHostName, LDAP_PORT); if (pLS ==
NULL) return LdapGetLastError();
// Set the version to 3.0 (default version is 2.0). lRtn =
ldap_set_option(pSession, LDAP_OPT_PROTOCOL_VERSION,
(void*)&version);
// Enable concurrent bind. if (lRtn == LDAP_SUCCESS) lRtn =
ldap_set_option(pSession, LDAP_OPT_FAST_CONCURRENT_BIND,
LDAP_OPT_ON);
// Cleanup on error. if (lRtn != LDAP_SUCCESS)
ldap_unbind(pSession); return lRtn;}
JNDI, Active Directory and Authentication (part 1)
800477 Dec 15, 2004 12:22 PM Currently Being Moderated
Disclaimer, the information presented here is about 3 years old,
however it will adequately describe how to use Kerberos &
GSS-API to authenticate a Java/JNDI application against the Active
Directory.
The sample code presented here is adapted from the original Sun
Java/JAAS/GSS_API code samples.
There is a ton of information for integrating non-Windows
Kerberos implementaions with Active Directory.
http://www.microsoft.com/windowsserver2003/technologies/security/kerberos/default.mspx
In particular for configuring the MIT Kerberos implementation to
work with Active Directory, I would
recomend:http://www.microsoft.com/windows2000/techinfo/planning/security/kerbsteps.asp
And for general Unix/Windows integration I would highly recomend
the "Solution Guide for Windows Security and Directory Services for
UNIX"
I would also recomend the Vintela suite of products that
simplify the integration of Linux/Unix clients with Active
Directory.
http://www.vintela.com/products/vas/index.php
Anyway, here's a working step-by step guide. If I get some free
time I may upgrade this information to include more recent releases
of MIT Kerberos V, using PAM and any applicable updates to
JAAS.
Note that this assume the Linux host will be joined to an Active
Directory domain. If the host is joined to a Kerberos realm, then
all you need to do is to establisht a Kerberos trust between the
Active Directory & the Kerberos realm.
Step 1. Create a user object in the Active Directory to
represent the Unix/Linux host from which the Java application will
be run. In this case the user object is called "linux". If you are
using the Active Directory Users & Computers snap-in, it
doesn't matter what the givenName, Surname, Display names are, but
the User Logon (pre-Windows 2000) name in thus example is set to
"linux" (without the quotes of course !)
Also remember to have created a user object to represent the
user that you will log in as. (Hopefully not root !)
Step 2.Create the keytab from Active Directory. In this example,
my Linux host is called "linux", it's fully qualified dns name is
linux.antipodes.com, my Active Directory domain is called
antipodes.com, the password I have used for the host account is
"password", and the resultant keytab is named linux.keytab. (Note
that the Active Directory domain, or kerberos realm, must be
uppercased, this will make sense later on in the krb5.conf
description)
c:> ktpass -princ host/[email protected]
-mapuser linux -pass password -out linux.keytab
Note that if you look at the Users & Computer snap-in, the
"linux" account will now have a Login name of the
form:"host/[email protected]"
Step 3.Securely transfer the linux.keytab file to the Linux
host, and install it using ktutil.# ktutil ktutil: read_kt
linux.keytabktutil: write_kt /etc/krb5.keytabktutil: quit
To check that the keytab has been merged, use the ktutil list
command.# ktutilktutil: listslot KVNO Principal----- ----
----------------------------------------1 1
host/[email protected]
Step 4.Configure Kerberos on the linux host. My /etc/krb5.conf
file looks like this.[libdefaults]default_realm =
ANTIPODES.COMdefault_tgs_enctypes = des-cbc-crcdefault_tkt_enctypes
= des-cbc-crc
[realms]ANTIPODES.COM = {kdc = mydc.antipodes.com:88admin_server
= mydc.antipodes.com:88kpasswd_server =
mydc.antipodes.com:464default_domain = ANTIPODES.COM}
Note that this is using a really old version of the MIT Kerberos
V that used DES as the common encryption algorithm supported by
both Active Directory & the MIT release. Newer MIT releases
support stronger encryption algorithms that may or may not be
supported by Active Directory such as 3DES or RC4-HMAC. (RC4-HMAC
is the default encryption algorithm used by Active Directory &
Windows clients)
Step 5.Test that Kerberos actually works.# kinitPassword for
[email protected]# klistTicket cache: /tmp/krb5cc_0Default
principal: [email protected] Starting Expires Service
Principal12/15/2004 09:26:02 12/15/2004 19:24:04
krbtgt/ANTIPODES.COM
You could also configure PAM to use Kerberos as the
authentication mechanism.
One common gotcha, the time needs to be synchronized between the
Linux client and Active Directory domain controller so that it does
not exceed the Kerberos Clock Skew (default 5 minutes).
Step 6.The application. As I said, a simple modification of the
Sun sample code:/*** SearchWithAuth.java* 10 July 2001* Sample JNDI
application to use Kerberos & GSS-APi for authentication**/
import javax.naming.*;import javax.naming.directory.*;import
javax.security.auth.login.*;import
javax.security.auth.Subject;import java.util.Hashtable;
class SearchWithAuth {
public static void main(String[] args) {
LoginContext lc = null; try {
lc = new LoginContext(SearchWithAuth.class.getName(), new
SampleCallbackHandler());
lc.login();
} catch (LoginException le) { System.err.println("Authentication
attempt failed" + le); System.exit(-1); }
System.out.println("Authenticated via GSS-API");
Subject.doAs(lc.getSubject(), new LDAPSearch()); }}
//The privileged action that is executed under doAsclass
LDAPSearch implements java.security.PrivilegedAction {
public LDAPSearch() { }
public Object run() { performLDAPSearch(); return null; }
private static void performLDAPSearch() {
// Set up environment for creating initial context Hashtable env
= new Hashtable();
env.put(Context.INITIAL_CONTEXT_FACTORY,
"com.sun.jndi.ldap.LdapCtxFactory");
// Connect to my domain controller env.put(Context.PROVIDER_URL,
"ldap://mydc.antipodes.com:389"); // Specify GSSAPI as the SASL
provider env.put(Context.SECURITY_AUTHENTICATION, "GSSAPI");
try { // Create the initial directory context DirContext ctx =
new InitialDirContext(env); // Create the search controls
SearchControls searchCtls = new SearchControls(); //Specify the
attributes to return String
returnedAtts[]={"sn","givenName","mail"};
searchCtls.setReturningAttributes(returnedAtts); //Specify the
search scope
searchCtls.setSearchScope(SearchControls.SUBTREE_SCOPE);
//specify the LDAP search filter String searchFilter =
"(&(objectClass=user)(mail=*))";
//Specify the Base for the search String searchBase =
"DC=antipodes,DC=com"; //initialize counter to total the results
int totalResults = 0;
// Search for objects using the filter NamingEnumeration answer
= ctx.search(searchBase, searchFilter, searchCtls);
//Loop through the search results while
(answer.hasMoreElements()) { SearchResult sr =
(SearchResult)answer.next();
totalResults++;
System.out.println(">>>" + sr.getName());
}
System.out.println("Total results: " + totalResults);
ctx.close();
ctx.close();
} catch (NamingException e) { // e.printStackTrace(); }
} }And the corresponding Callback handler. (Question: how does
one create a password entry reader, which echos * instead of the
password charcters ?)/*** SampleCallbackHandler.java* 10 July 2001*
Sample JNDI application to use Kerberos & GSS-APi for
authentication**/import javax.security.auth.callback.*;import
javax.security.auth.callback.PasswordCallback;import
javax.security.auth.callback.NameCallback;import
java.io.IOException;import java.io.BufferedReader;import
java.io.InputStreamReader;
public class SampleCallbackHandler implements CallbackHandler {
public void handle(Callback[] callbacks) throws
java.io.IOException, UnsupportedCallbackException { for (int i = 0;
i < callbacks.length; i++) { if (callbacks[i] instanceof
NameCallback) { NameCallback cb = (NameCallback)callbacks;
cb.setName(getClearInput(cb.getPrompt()));
} else if (callbacks[i] instanceof PasswordCallback) {
PasswordCallback cb = (PasswordCallback)callbacks[i];
if (cb.isEchoOn()) {
System.out.println("Echo On");
}
String pw = getProtectedInput(cb.getPrompt());
char[] passwd = new char[pw.length()];
pw.getChars(0, passwd.length, passwd, 0);
cb.setPassword(passwd);
} else {
throw new UnsupportedCallbackException(callbacks[i]);
}
}
}
private String getClearInput(String prompt) throws IOException
{
System.out.print(prompt);
BufferedReader in = new BufferedReader(new
InputStreamReader(System.in));
return in.readLine();
}
private String getProtectedInput(String prompt) throws
IOException {
System.out.print(prompt);
BufferedReader in = new BufferedReader(new
InputStreamReader(System.in));
return in.readLine();
}
public static void main(String[] args) throws IOException,
UnsupportedCallbackException {
// Test handler
CallbackHandler ch = new SampleCallbackHandler();
Callback[] callbacks = new Callback[]{
new NameCallback("user id:"),
new PasswordCallback("password:", true)};
ch.handle(callbacks);
NameCallback ncb = (NameCallback)callbacks[0];
System.out.println("Debug: " + ncb.getName());
}
}
And finally the Java application configuration file, named
SearchWithAuh.conf/** * Login Configuration for JAAS. * * Specify
that Kerberos v5 is a required login module for the * sample
application SearchWithAuth. */SearchWithAuth {
com.sun.security.auth.module.Krb5LoginModule required client=TRUE
useTicketCache=TRUE;};Note the useTicketCache = TRUE value means
that if the user already has a Kerberos ticket they will not be
prompted for credentials. If the user does not have a ticket (for
example use kdestroy to logout of Kerberos), then the user will be
prompted for credentials.
Step 8.
You can avoid steps 1-4 with the Vintela software. It simplifies
the integration of Unix & Linux clients with the Active
Directory.
It extends the schema with Unix GID, UID attributes, enhances
the Active Directory Users & Computers snap-in with additional
property pages to enable editing of Unix UID, GID, shell script
atributes and allows Active Directory Group Policy to be applied to
Unix & Linux clients (including password policy)
There is also a Linux/Unix command line utility that "joins" the
machine to the domain (essentially automates the creation of
computer account, generation, transfer and import of the keytab,
configuration of the kr5.conf ) and extends the crypto libraries to
support RC4-HMAC. 854 Views
JNDI, Active Directory & Authentication (part 2)
800477 Dec 21, 2004 5:40 PM Currently Being Moderated A previous
topic "JNDI, Active Directiory & Authentication (part 1)"
described using JAAS and GSSAPI to utilize Kerberos as the
authentication protocol to access Active Directory.
Most of the JNDI sample code illustrates the use of simple
(clear text) authentication which is inherently insecure as
credentials are sent in clear over the network.
While Kerberos is a much more secure authentication option, it
does not protect the LDAP data that is transmitted over the
network. For example the results of a LDAP Search operation are
tranmitted in clear and may potentially reveal sensitive
information.
One solution is to use SSL to protect the LDAP
communications.
In addition, for Active Directory, the only way that passwords
can be changed or reset using LDAP is for the communications to
take place over SSL.
The code changes required to enable a JNDI application to use
SSL are minimal.
Step 1. Modify the Java app.The modifications are:Change the
LDAP port from 398 to 636String ldapURL =
"ldap://mydc.antipodes.com:636";Set the trustStore propertyString
keystore =
"/usr/java/j2sdk1.4.0/jre/lib/security/cacerts";System.setProperty("javax.net.ssl.trustStore",keystore);and
establish ssl as the commications protocol for the
context.env.put(Context.SECURITY_PROTOCOL,"ssl");/** *
searchssl.java * 5 July 2001 * December 2004 - noted that ldaps url
now supported on JSDK 1.5.0 * Sample JNDI application to perform a
search for against the Active Directory * over SSL (port 636)
*/
import java.util.Hashtable;import javax.naming.ldap.*;import
javax.naming.directory.*;import javax.naming.*;
public class searchssl{ public static void main (String[] args)
{ Hashtable env = new Hashtable(); String adminName =
"CN=Administrator,CN=Users,DC=ANTIPODES,DC=COM"; String
adminPassword = "XXXXXXX"; String ldapURL =
"ldap://mydc.antipodes.com:636";
//Access the keystore, this is where the Root CA public key cert
was installed //Could also do this via the command line option java
-Djavax.net.ssl.trustStore.... //No need to specifiy the keystore
password for read operations String keystore =
"/usr/java/j2sdk1.4.0/jre/lib/security/cacerts";
System.setProperty("javax.net.ssl.trustStore",keystore);
env.put(Context.INITIAL_CONTEXT_FACTORY,"com.sun.jndi.ldap.LdapCtxFactory");
//set security credentials
env.put(Context.SECURITY_AUTHENTICATION,"simple");
env.put(Context.SECURITY_PRINCIPAL,adminName);
env.put(Context.SECURITY_CREDENTIALS,adminPassword);
//specify use of ssl env.put(Context.SECURITY_PROTOCOL,"ssl");
//connect to my domain controller
env.put(Context.PROVIDER_URL,ldapURL); try {
// Create the initial directory context DirContext ctx = new
InitialLdapContext(env,null); //Create the search controls
SearchControls searchCtls = new SearchControls(); //Specify the
attributes to return String
returnedAtts[]={"sn","givenName","mail"};
searchCtls.setReturningAttributes(returnedAtts); //Specify the
search scope
searchCtls.setSearchScope(SearchControls.SUBTREE_SCOPE);
//specify the LDAP search filter String searchFilter =
"(&(objectClass=user)(mail=*))";
//Specify the Base for the search String searchBase =
"DC=ANTIPODES,DC=COM";
//initialize counter to total the results int totalResults =
0;
// Search for objects using the filter NamingEnumeration answer
= ctx.search(searchBase, searchFilter, searchCtls);
//Loop through the search results while
(answer.hasMoreElements()) { SearchResult sr =
(SearchResult)answer.next();
totalResults++;
System.out.println(">>>" + sr.getName());
// Print out some of the attributes, catch the exception if the
attributes have no values Attributes attrs = sr.getAttributes(); if
(attrs != null) { try { System.out.println(" surname: " +
attrs.get("sn").get()); System.out.println(" firstname: " +
attrs.get("givenName").get()); System.out.println(" mail: " +
attrs.get("mail").get());
} catch (NullPointerException e) { System.out.println("Errors
listing attributes: " + e); } }
}
System.out.println("Total results: " + totalResults);
ctx.close();
} catch (NamingException e) { System.err.println("Problem
searching directory: " + e); } }}Step 2. Establish trust with the
Active Directory domain PKI certificates.
I have installed an Enterprise CA, with the distinguished name
of cn=Antipodes Root,dc=antipodes,dc=com"
By default, domain controllers automatically enroll for domain
controller certificates once an Enterprise CA has been installed.
The root certificate can be found in the root of the system drive
(Eg. if the operating system has been installed in c:\windows, the
cert will be found in c:\)
By default the certificate file is named _.crt
#keytool -import -alias antipodes -file
"mydc.antipodes.com_Antipodes Root.crt" -keystore
/usr/java/j2sdk1.4.0/jre/lib/securitycacerts
There is no need to publish the root certificates in the Active
Directory or to add them via Group Policy as an Enterprise CA will
automagically publish the root certs and Domain Controllers will
automagically enroll for Domain Controller certificates.
An alternative mechanism to obtain the root cert is via the
Certicate Authority Web Enrollment tool.
Assuming the Certificate Authority is installed on
myca.antipodes.com (and that the CA Web Interface is also running
on the same server), via a browser connect to
http://myca.antipodes.com/certsrv. From the page, go to "Download a
CA Certificate" and then select "Download CA Certificate". Leave
the encoding method as Distinguished Encoding Rules (DER). .
By default the certificate file name is "certnew.cer"
In this case, import the cert using:#keytool -import -alias
antipodes -file certnew.cer -keystore
/usr/java/j2sdk1.4.0/jre/lib/securitycacerts
Note the default password for the java keystore is
"changeit"
For information on configuring a certificate authority one good
document
is:http://www.microsoft.com/technet/prodtechnol/windowsserver2003/technologies/security/webenroll.mspx
If you are not using a Microsoft Certificate Authority,
documention describing how to use a third party certificate
authority with Active Directory can be found
at:http://support.microsoft.com/default.aspx?scid=kb;EN-US;Q295663
(In this case you will need to establish trust with the third
party Certificate Authority by importing it's root cert).
Some Common errors1. javax.net.ssl,SSLHandshakeException:
untrusted server cert chainYou haven't installed the cert, or have
installed the wrong cert.
2. javaxNamingException: Cannot parse url ldaps://dc dns
name:636
You are running an older JDK that doesn't support ldaps as a
url. It is ok to use ldap://dc dns name:636, or upgrade to JDK
1.5
3. A little misleading
howeverjavax.naming.AuthenticationException: [LDAP: error code 49 -
80090308: LdapErr: DSID-0C09030F, comment AcceptSecurityContext
error, data 525, vece]
the Win32 error code (actually it is an error code from
ntstatus.h), 525 (0x20D) STATUS_CONNECTION_RESET - The transport
connection has been reset, kind of implies a SSL problem, but
really is invalid credentials.
4. I haven't looked at expired or revoked certs, but I assume
that javax.net.ssl would raise the appropriate exception and
display a helpful message.
JNDI, Active Directory & Authentication (part 3)
800477 Dec 23, 2004 11:48 AM Currently Being Moderated Another
mechanism that can be used to authenticate users against the Active
Directory is DIGEST-MD5.
One benefit of this mechanism, is that the credentials are not
sent in clear over the network. Another advantage is that the ldap
traffic can be protected from tampering or eavesdropping by
providing integrity checking and confidentiality.
However one disadvantage is that user's passwords must be stored
in the Active DIrectory using reversible encryption, potentially
making it easier for an attacker to compromise users' passwords if
they can get access to a domain controller.
The following code demonstrates the use of DIGEST-MD5
authentication:/** * searchdigest.java * December 2004 * Sample
JNDI application to perform a search for against the Active
Directory * Uses HTTP-DIGEST (DIGEST-MD5) as the Authentication
Protocol * */
import java.util.Hashtable;import javax.naming.ldap.*;import
javax.naming.directory.*;import javax.naming.*;
public class searchdigest{ public static void main (String[]
args) { Hashtable env = new Hashtable();
//Must use either the userPrincipalName or samAccountName,
//Cannot use the distinguished name
//String adminName = "[email protected]"; String
adminName = "ANTIPODES\\Administrator"; String adminPassword =
"XXXXXXX"; String ldapURL = "ldap://mydc.antipodes.com:389";
env.put(Context.INITIAL_CONTEXT_FACTORY,"com.sun.jndi.ldap.LdapCtxFactory");
//set security credentials, note using DIGEST-MD5 //Requires
user account to be stored with reversible encryption
env.put(Context.SECURITY_AUTHENTICATION,"DIGEST-MD5");
env.put(Context.SECURITY_PRINCIPAL,adminName);
env.put(Context.SECURITY_CREDENTIALS,adminPassword);
//Could also use DIGEST-MD5 to protect the communications //Eg.
auth-int;integrity, auth-conf;confidentiality
//env.put("javax.security.sasl.qop","auth-conf"); //And could also
request the level of crypto //Eg. low, medium, high
//env.put("javax.security.sasl.strength","high"); //connect to my
domain controller env.put(Context.PROVIDER_URL,ldapURL);
try {
// Create the initial directory context DirContext ctx = new
InitialLdapContext(env,null); // Create the search controls
SearchControls searchCtls = new SearchControls(); //Specify the
attributes to return String
returnedAtts[]={"sn","givenName","mail"};
searchCtls.setReturningAttributes(returnedAtts); //Specify the
search scope
searchCtls.setSearchScope(SearchControls.SUBTREE_SCOPE);
//specify the LDAP search filter String searchFilter =
"(&(objectClass=user)(mail=*))";
//Specify the Base for the search String searchBase =
"DC=antipodes,DC=com";
//initialize counter to total the results int totalResults =
0;
// Search for objects using the filter NamingEnumeration answer
= ctx.search(searchBase, searchFilter, searchCtls);
//Loop through the search results while
(answer.hasMoreElements()) { SearchResult sr =
(SearchResult)answer.next(); totalResults++;
System.out.println(">>>" + sr.getName());
// Print out some of the attributes, catch the exception if the
attributes have no values Attributes attrs = sr.getAttributes(); if
(attrs != null) { try { System.out.println(" mail: " +
attrs.get("mail").get()); } catch (NullPointerException e) {
System.out.println(); } }
}
System.out.println("Total results: " + totalResults);
ctx.close();
}
catch (NamingException e) { System.err.println("Problem
searching directory: " + e); } }}Note that there are two major
gotchas.
1. In the application, only NT Domain names (DOMAIN\username) or
userPrincipal names ([email protected]) can be used.
Distinguished names are not supported.
2. The user's password must be configured to be stored using
reversible encryption (Performed either from the Active Directory
Users & Computers tool, or by modifying the user's
userAccountControl attribute with a JNDI, LDAP, ADSI or LDAP
application). Also after the reversible encryption option has been
set, the user must change their password for the password to be
stored using this format