directory-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From conflue...@apache.org
Subject [CONF] Apache Directory Server v1.5 > 3.1. Authentication options
Date Wed, 28 Oct 2009 19:45:01 GMT
<html>
<head>
    <base href="http://cwiki.apache.org/confluence">
            <link rel="stylesheet" href="/confluence/s/1519/1/1/_/styles/combined.css?spaceKey=DIRxSRVx11&amp;forWysiwyg=true"
type="text/css">
    </head>
<body style="background-color: white" bgcolor="white">
<div id="pageContent">
<div id="notificationFormat">
<div class="wiki-content">
<div class="email">
     <h2><a href="http://cwiki.apache.org/confluence/display/DIRxSRVx11/3.1.+Authentication+options">3.1.
Authentication options</a></h2>
     <h4>Page <b>edited</b> by             <a href="http://cwiki.apache.org/confluence/display/~seelmann">Stefan
Seelmann</a>
    </h4>
     
          <br/>
     <div class="notificationGreySide">
         <div class='panelMacro'><table class='noteMacro'><colgroup><col
width='24'><col></colgroup><tr><td valign='top'><img src="/confluence/images/icons/emoticons/warning.gif"
width="16" height="16" align="absmiddle" alt="" border="0"></td><td><b>Work
in progress</b><br /><p>This site is in the process of being reviewed and
updated.</p></td></tr></table></div>
<style type='text/css'>/*<![CDATA[*/
table.ScrollbarTable  {border: none;padding: 3px;width: 100%;padding: 3px;margin: 0px;background-color:
#f0f0f0}
table.ScrollbarTable td.ScrollbarPrevIcon {text-align: center;width: 16px;border: none;}
table.ScrollbarTable td.ScrollbarPrevName {text-align: left;border: none;}
table.ScrollbarTable td.ScrollbarParent {text-align: center;border: none;}
table.ScrollbarTable td.ScrollbarNextName {text-align: right;border: none;}
table.ScrollbarTable td.ScrollbarNextIcon {text-align: center;width: 16px;border: none;}

/*]]>*/</style><div class="Scrollbar"><table class='ScrollbarTable'><tr><td
width='33%' class='ScrollbarPrevName'>&nbsp;</td><td width='33%' class='ScrollbarParent'><sup><a
href="/confluence/display/DIRxSRVx11/3.+Basic+Security"><img border='0' align='middle'
src='/confluence/images/icons/up_16.gif' width='8' height='8'></a></sup><a
href="/confluence/display/DIRxSRVx11/3.+Basic+Security">3. Basic Security</a></td><td
width='33%' class='ScrollbarNextName'>&nbsp;<a href="/confluence/display/DIRxSRVx11/3.2.+Basic+authorization">3.2.
Basic authorization</a></td><td class='ScrollbarNextIcon'><a href="/confluence/display/DIRxSRVx11/3.2.+Basic+authorization"><img
border='0' align='middle' src='/confluence/images/icons/forwd_16.gif' width='16' height='16'></a></td></tr></table></div>

<h1><a name="3.1.Authenticationoptions-Authenticationoptions"></a>Authentication
options</h1>

<p>This section describes the authentication options of ApacheDS 1.5. Anonymous and
simple binds are supported, as well as SASL mechanisms. Configuring and using the first two
of them is described below with the help of examples.</p>

<div>
<ul>
    <li><a href='#3.1.Authenticationoptions-Whatisauthentication%3F'>What is authentication?</a></li>
    <li><a href='#3.1.Authenticationoptions-Simplebinds'>Simple binds</a></li>
    <li><a href='#3.1.Authenticationoptions-Passwordsstoredonewayencrypted'>Passwords
stored one-way encrypted</a></li>
    <li><a href='#3.1.Authenticationoptions-Anonymousbinds'>Anonymous binds</a></li>
    <li><a href='#3.1.Authenticationoptions-Howtoauthenticateauserbyuidandpassword%3F'>How
to authenticate a user by uid and password?</a></li>
    <li><a href='#3.1.Authenticationoptions-Resources'>Resources</a></li>
</ul></div>

<h2><a name="3.1.Authenticationoptions-Whatisauthentication%3F"></a>What
is authentication?</h2>

<p><b>Authentication</b> is the process of determining whether someone (or
something) in fact is what he/she/it asserts to be. </p>

<p>Within ApacheDS you will likely want to authenticate clients in order to check whether
they are allowed to read, add or manipulate certain data stored within the directory. The
latter, i.e. whether an authenticated client is permitted to do something, is deduced during
<b>authorization</b>.</p>

<p>Quite often, the process of authentication is delegated to a directory service by
other software components. Because in doing so, authentication data (e.g. username, password)
and authorization data (e.g. group relationships) are stored and managed centrally in the
directory, and all connected software solutions benefit from it. The integration sections
of this guide provide examples for Apache Tomcat, Apache HTTP servers, and others.</p>

<p>ApacheDS 1.5 supports simple authentication and anonymous binds while storing passwords
within <em>userPassword</em> attributes in user entries. Passwords can be stored
in clear text or one-way encrypted with a hash algorithm like MD5 or SHA1. Since version 1.5.1,
SASL mechanism are supported as well. We start with anonymous binds.  </p>

<h2><a name="3.1.Authenticationoptions-Simplebinds"></a>Simple binds</h2>

<p>Authentication via simple bind is widely used. The method is supported by ApacheDS
1.5 for all person entries stored within any partition, if they contain a password attribute.
How does it work? An LDAP client provides the DN of a user entry and a password to the server,
the parameters of the bind operation. ApacheDS checks whether the given password is the same
as the one stored in the <em>userpassword</em> attribute of the given entry. If
not, the bind operation fails (LDAP error code 49, LDAP_INVALID_CREDENTIALS), and the user
is not authenticated.</p>

<h3><a name="3.1.Authenticationoptions-Usingcommandlinetools"></a>Using
command line tools</h3>

<p>Assume this entry from the Seven Seas partition is stored within the directory (only
a fragment with the relevant attributes is shown).</p>

<div class="preformatted panel" style="border-width: 1px;"><div class="preformattedContent
panelContent">
<pre>dn: cn=Horatio Hornblower,ou=people,o=sevenSeas
objectclass: person
objectclass: organizationalPerson
cn: Horatio Hornblower
sn: Hornblower
userpassword: pass
...
</pre>
</div></div>

<p>In the following search command, a user tries to bind with the given DN (option -D)
but a wrong password (option -w). The bind fails and the command terminates without performing
the search.</p>

<div class="preformatted panel" style="border-width: 1px;"><div class="preformattedContent
panelContent">
<pre>$ ldapsearch -h zanzibar -p 10389 -D "cn=Horatio Hornblower,ou=people,o=sevenSeas"
\\
    -w wrong -b "ou=people,o=sevenSeas" -s base "(objectclass=*)"
ldap_simple_bind: Invalid credentials
ldap_simple_bind: additional info: Bind failed: null
</pre>
</div></div>

<p>If the user provides the correct password during the call of the ldapsearch command,
the bind operation succeeds and the seach operation is performed afterwards.</p>

<div class="preformatted panel" style="border-width: 1px;"><div class="preformattedContent
panelContent">
<pre>$ ldapsearch -h zanzibar -p 10389 -D "cn=Horatio Hornblower,ou=people,o=sevenSeas"
\\
    -w pass -b "ou=people,o=sevenSeas" -s base "(objectclass=*)"
version: 1
dn: ou=people,o=sevenSeas
ou: people
description: Contains entries which describe persons (seamen)
objectclass: organizationalUnit
objectclass: top
</pre>
</div></div>

<h3><a name="3.1.Authenticationoptions-BindsfromJavacomponentsusingJNDI"></a>Binds
from Java components using JNDI</h3>

<p>Using JNDI, authentication via simple binds is accomplished by appropriate configuration.
One option is to provide the parameters in a Hashtable object like this</p>

<div class="code panel" style="border-width: 1px;"><div class="codeContent panelContent">
<pre class="code-java">
<span class="code-keyword">import</span> java.util.Hashtable;

<span class="code-keyword">import</span> javax.naming.Context;
<span class="code-keyword">import</span> javax.naming.InitialContext;
<span class="code-keyword">import</span> javax.naming.NamingEnumeration;
<span class="code-keyword">import</span> javax.naming.NamingException;

<span class="code-keyword">public</span> class SimpleBindDemo {

    <span class="code-keyword">public</span> <span class="code-keyword">static</span>
void main(<span class="code-object">String</span>[] args) <span class="code-keyword">throws</span>
NamingException {

        <span class="code-keyword">if</span> (args.length &lt; 2) {
            <span class="code-object">System</span>.err.println(<span class="code-quote">"Usage:
java SimpleBindDemo &lt;userDN&gt; &lt;password&gt;"</span>);
            <span class="code-object">System</span>.exit(1);
        }

        Hashtable env = <span class="code-keyword">new</span> Hashtable();
        env.put(Context.INITIAL_CONTEXT_FACTORY, <span class="code-quote">"com.sun.jndi.ldap.LdapCtxFactory"</span>);
        env.put(Context.PROVIDER_URL, <span class="code-quote">"ldap:<span class="code-comment">//zanzibar:10389/o=sevenSeas"</span>);
</span>
        env.put(Context.SECURITY_AUTHENTICATION, <span class="code-quote">"simple"</span>);
        env.put(Context.SECURITY_PRINCIPAL, args[0]);
        env.put(Context.SECURITY_CREDENTIALS, args[1]);

        <span class="code-keyword">try</span> {
            Context ctx = <span class="code-keyword">new</span> InitialContext(env);
            NamingEnumeration enm = ctx.list("");
            <span class="code-keyword">while</span> (enm.hasMore()) {
                <span class="code-object">System</span>.out.println(enm.next());
            }
            ctx.close();
        } <span class="code-keyword">catch</span> (NamingException e) {
            <span class="code-object">System</span>.out.println(e.getMessage());
        }
    }
}
</pre>
</div></div>  


<p>If the DN of a user entry and the fitting password are provided as command line arguments,
the program binds successfully and performs a search:</p>

<div class="preformatted panel" style="border-width: 1px;"><div class="preformattedContent
panelContent">
<pre>$ java SimpleBindDemo "cn=Horatio Hornblower,ou=people,o=sevenSeas" pass
ou=people: javax.naming.directory.DirContext
ou=groups: javax.naming.directory.DirContext
</pre>
</div></div>

<p>On the other hand, providing an incorrect password results in a failed bind operation.
JNDI maps it to a <em>NamingException</em>:</p>

<div class="preformatted panel" style="border-width: 1px;"><div class="preformattedContent
panelContent">
<pre>$ java SimpleBindDemo "cn=Horatio Hornblower,ou=people,o=sevenSeas" quatsch
[LDAP: error code 49 - Bind failed: null]
</pre>
</div></div>

<p>In real life, you obviously want to separate most of the configuration data from
the source code, for instance with the help of the <em>jndi.properties</em> file.</p>

<h2><a name="3.1.Authenticationoptions-Passwordsstoredonewayencrypted"></a>Passwords
stored one-way encrypted</h2>

<p>If passwords are stored in the directory in clear like above, the administrator (<em>uid=admin,ou=system</em>)
is able to read them. This holds true even if authorization is enabled. The passwords would
also be visible in exported LDIF files. This is often unacceptable.</p>

<div class='panelMacro'><table class='warningMacro'><colgroup><col width='24'><col></colgroup><tr><td
valign='top'><img src="/confluence/images/icons/emoticons/forbidden.gif" width="16"
height="16" align="absmiddle" alt="" border="0"></td><td><p>Not only
the administrator will be able to read your password, or be visible in LDIF files, but if
one does not use SSL, the the password is transmitted in clear text above the wire...</p></td></tr></table></div>

<h3><a name="3.1.Authenticationoptions-Passwordsnotstoredincleartext"></a>Passwords
not stored in clear text</h3>

<p>ApacheDS does also support simple binds, if user passwords are stored one-way encrypted.
An LDAP client, which creates user entries, applies a hash-function (SHA for instance) to
the user passwords beforehand, and stores the users with these fingerprints as <em>userpassword</em>
values (instead of the clear text values), for instance:</p>

<div class="preformatted panel" style="border-width: 1px;"><div class="preformattedContent
panelContent">
<pre>dn: cn=Horatio Hornblower,ou=people,o=sevenSeas
objectclass: person
objectclass: organizationalPerson
cn: Horatio Hornblower
sn: Hornblower
userpassword: {SHA}nU4eI71bcnBGqeO0t9tXvY1u5oQ=
...
</pre>
</div></div>

<p>The value "{SHA}nU4eI71bcnBGqeO0t9tXvY1u5oQ=" means that <em>SHA</em>
(Secure Hash Algorithm) was applied to the password, and "nU4eI71bcnBGqeO0t9tXvY1u5oQ=" was
the result (Base-64 encoded). Please note that it is not possible to calculate the source
("pass" in our case) back from the result. This is why it is called one-way encrypted &#8211;
it is rather difficult to decrypt it. One may guess many times, calculate the hash values
(the algorithms are public) and compare the result. But this would take a long time, especially
if you choose a more complex password than we did ("pass").  </p>

<h3><a name="3.1.Authenticationoptions-Buthowtoobtainthehashvalueforapassword%3F"></a>But
how to obtain the hash value for a password?</h3>

<p>With some lines of code, it is quite easy to accomplish this task programatically
in Java:</p>

<div class="code panel" style="border-width: 1px;"><div class="codeContent panelContent">
<pre class="code-java">
<span class="code-keyword">import</span> java.security.MessageDigest;
<span class="code-keyword">import</span> java.security.NoSuchAlgorithmException;
<span class="code-keyword">import</span> sun.misc.BASE64Encoder;

<span class="code-keyword">public</span> class DigestDemo {
    <span class="code-keyword">public</span> <span class="code-keyword">static</span>
void main(<span class="code-object">String</span>[] args) <span class="code-keyword">throws</span>
NoSuchAlgorithmException {
        <span class="code-object">String</span> password = <span class="code-quote">"pass"</span>;
        <span class="code-object">String</span> algorithm = <span class="code-quote">"SHA"</span>;
        
        <span class="code-comment">// Calculate hash value
</span>        MessageDigest md = MessageDigest.getInstance(algorithm);
        md.update(password.getBytes());
        <span class="code-object">byte</span>[] bytes = md.digest();
        
        <span class="code-comment">// Print out value in Base64 encoding
</span>        BASE64Encoder base64encoder = <span class="code-keyword">new</span>
BASE64Encoder();
        <span class="code-object">String</span> hash = base64encoder.encode(bytes);
       
        <span class="code-object">System</span>.out.println('{'+algorithm+'}'+hash);
    }
}
</pre>
</div></div>

<p>The output is "{SHA}nU4eI71bcnBGqeO0t9tXvY1u5oQ=".</p>

<p>Another option is to use command line tools to calculate the hash value; the <a
href="http://www.openssl.org" rel="nofollow">OpenSSL</a> project provides such stuff.
Furthermore many UI LDAP tools allow you to store passwords automatically encrypted with the
hash algorithm of your choice. See below <a href="http://directory.apache.org/studio/"
rel="nofollow">Apache Directory Studio</a> as an example. The dialog automatically
shows up if a <em>userPassword</em> attribute is to be manipulated (added, changed).</p>

<p><img src="/confluence/download/attachments/55240/password_edit_ls.png" align="absmiddle"
border="0" /></p>

<h3><a name="3.1.Authenticationoptions-FromanLDAPclientpointofview"></a>From
an LDAP client point of view</h3>

<p>From an LDAP client point of view, the behavior during authentication is the same
as with passwords stored in clear. During a simple bind, a client sends DN and password (unencrypted,
i.e. no hash algorithm applied) to the server. If ApacheDS detects, that the user password
for the given DN is stored in the directory with a hash function applied, it calculates the
hash value of the given password with the appropriate algorithm (this is why the algorithm
is stored together with the hashed password). Afterwards it compares the result with the stored
attribute value. In case of a match, the bind operation ends successfully:</p>

<div class="preformatted panel" style="border-width: 1px;"><div class="preformattedContent
panelContent">
<pre>$ ldapsearch -h zanzibar -p 10389 -D "cn=Horatio Hornblower,ou=people,o=sevenSeas"
\\ 
    -w pass -b "ou=people,o=sevenSeas" -s base "(objectclass=*)"
version: 1
dn: ou=people,o=sevenSeas
ou: people
description: Contains entries which describe persons (seamen)
objectclass: organizationalUnit
objectclass: top
</pre>
</div></div>

<p>Providing the hashed value of the <em>userPassword</em> attribute instead
of the original value will be rejected by ApacheDS:</p>

<div class="preformatted panel" style="border-width: 1px;"><div class="preformattedContent
panelContent">
<pre>$ ldapsearch -h zanzibar -p 10389 -D "cn=Horatio Hornblower,ou=people,o=sevenSeas"
\\
    -w "{SHA}nU4eI71bcnBGqeO0t9tXvY1u5oQ=" -b "ou=people,o=sevenSeas" -s base "(objectclass=*)"
ldap_simple_bind: Invalid credentials
ldap_simple_bind: additional info: Bind failed: null
</pre>
</div></div>

<p>This is intended. If someone was able to catch this value (from an LDIF export for
instance), s/he must still provide the password itself in order to get authenticated.</p>

<div class='panelMacro'><table class='noteMacro'><colgroup><col width='24'><col></colgroup><tr><td
valign='top'><img src="/confluence/images/icons/emoticons/warning.gif" width="16" height="16"
align="absmiddle" alt="" border="0"></td><td><b>Be Warned: Limited security
added</b><br /><p>Please note that storing user passwords one-way encrypted
only adds limited security. During the bind operation, the credentials are still transmitted
unencrypted, if no SSL/TLS communication is used (thus you should definitely consider to do
so). </p>

<p>Furthermore, if someone gets an LDIF file with userpassword values digested with
SHA etc., s/he may be able to determine some of the passwords with brute force. Calculation
of hash functions can be done very fast, and the attacker can attempt millions of values with
ease, without you getting notice of it. Therefore protect your data, even if one-way encryption
is applied to the passwords!</p></td></tr></table></div>


<h2><a name="3.1.Authenticationoptions-Anonymousbinds"></a>Anonymous binds</h2>

<p>In some occasions it is appropriate to allow LDAP clients to permit operations without
authentication. If data managed by the directory service is well known by all clients, it
is not uncommon to allow search operations (not manipulation) within this data to all clients
&#8211; without providing credentials. An example for this are enterprise wide telephone
books, if clients access the directory service from the intranet.</p>

<h3><a name="3.1.Authenticationoptions-Enable%2Fdisableanonymousbinds"></a>Enable/disable
anonymous binds</h3>

<p>Anonymous access is enabled by default. Changing this is one of the basic configuration
tasks. If you use the server standalone configured with a <em>server.xml</em>
file, you can enable/disable it by changing the value for property <em>allowAnonymousAccess</em>
in the Spring bean definition for bean <em>defaultDirectoryService</em>, as depicted
in the following fragment:</p>

<div class="code panel" style="border-width: 1px;"><div class="codeContent panelContent">
<pre class="code-xml">
  &lt;defaultDirectoryService id=<span class="code-quote">"directoryService"</span>
instanceId=<span class="code-quote">"default"</span>
                           ...
                           allowAnonymousAccess=<span class="code-quote">"false"</span>
                           ...&gt;

</pre>
</div></div>

<p>A restart of the server is necessary for this change to take effect. </p>

<h3><a name="3.1.Authenticationoptions-Example%3AServerbehaviorwithanonymousbindsdisabled"></a>Example:
Server behavior with anonymous binds disabled</h3>

<p>Assume anonymous binds are disabled and our sample partition <em>Seven Seaes</em>
present in the server. Here is an example with a search operation performed by a command line
tool as a client. It tries to connect anonymously (no DN and password given, i.e. options
-D and -w missing) to the server. Afterwards the entry <em>ou=people,o=sevenSeas</em>
should be displayed.</p>

<p>See the command and the resulting error message provided by the server below </p>

<div class="preformatted panel" style="border-width: 1px;"><div class="preformattedContent
panelContent">
<pre>$ ldapsearch -h zanzibar -p 10389 -b "ou=people,o=sevenSeas" -s one "(objectclass=*)"
ldap_search: Insufficient access
ldap_search: additional info: failed on search operation: Anonymous binds have been disabled!
</pre>
</div></div>

<h3><a name="3.1.Authenticationoptions-Example%3AServerbehaviorwithanonymousbindsenabled"></a>Example:
Server behavior with anonymous binds enabled</h3>

<p>Now the same command performed against ApacheDS 1.5 with anonymous access enabled
as described above. The behavior is different &#8211; the entry is visible. </p>

<div class="preformatted panel" style="border-width: 1px;"><div class="preformattedContent
panelContent">
<pre>$ ldapsearch -h zanzibar -p 10389 -b "ou=people,o=sevenSeas" -s base "(objectclass=*)"
version: 1
dn: ou=people,o=sevenSeas
ou: people
description: Contains entries which describe persons (seamen)
objectclass: organizationalUnit
objectclass: top
</pre>
</div></div>

<h3><a name="3.1.Authenticationoptions-Otherclients"></a>Other clients</h3>

<p>The examples above have used a command line tool. Of course graphical tools and programmatical
access (JNDI etc.) allow anonymous binds as well. Below is a screen shot from the configuration
dialog of <a href="http://directory.apache.org/studio/" rel="nofollow">Apache Directory
Studio</a> as an example. During configuration of the connection data ("New LDAP Connection",
for instance), the option <em>Anonymous Authentication</em> leads to anonymous
binds. Other UI tools offer this feature as well.</p>

<p><img src="/confluence/download/attachments/55240/authentication_options_ls.png"
align="absmiddle" border="0" /></p>

<div class='panelMacro'><table class='noteMacro'><colgroup><col width='24'><col></colgroup><tr><td
valign='top'><img src="/confluence/images/icons/emoticons/warning.gif" width="16" height="16"
align="absmiddle" alt="" border="0"></td><td><b>Use this feature wisely</b><br
/><p>With anonymous access enabled it is not only possible to search the directory
without providing username and password. With autorization disabled, anonymous users may also
be able to modify data. It is therefore highly recommended to enable and configure the authorization
subsystem as well. Learn more about authorization in the <a href="/confluence/display/DIRxSRVx11/3.2.+Basic+authorization"
title="3.2. Basic authorization">3.2. Basic authorization</a> section.</p></td></tr></table></div>


<h2><a name="3.1.Authenticationoptions-Howtoauthenticateauserbyuidandpassword%3F"></a>How
to authenticate a user by uid and password?</h2>

<p>If you want to use simple binds with user DN and password within a Java component,
in order to authenticate users programatically, in practice one problem arises: Most users
do not know their DN. Therefore they will not be able to enter it. And even if they know it,
it would be frequently very laborious due to the length of the DN. It would be easier for
a user if s/he only has to probvide a short, unique <em>ID</em> and the password,
like in this web form</p>

<p><img src="/confluence/download/attachments/55240/confluence_logon.png" align="absmiddle"
border="0" /></p>

<p>Usually the ID is an attribute within the user's entry. In our sample data (Seven
Seas), each user entry contains the <em>uid</em> attribute, for instance uid=hhornblo
for Captain Hornblower:</p>

<div class="code panel" style="border-width: 1px;"><div class="codeContent panelContent">
<pre class="code-java">
dn: cn=Horatio Hornblower,ou=people,o=sevenSeas
objectclass: person
objectclass: organizationalPerson
objectclass: inetOrgPerson
objectclass: top
cn: Horatio Hornblower
description: Capt. Horatio Hornblower, R.N
givenname: Horatio
sn: Hornblower
uid: hhornblo
mail: hhornblo@royalnavy.mod.uk
userpassword: {SHA}nU4eI71bcnBGqeO0t9tXvY1u5oQ=
</pre>
</div></div>

<p>But how to authenticate a user who provides "hhornblo"/"pass" instead of "cn=Horatio
Hornblower,ou=people,o=sevenSeas"/"pass" with the help of ApacheDS?</p>

<h3><a name="3.1.Authenticationoptions-Analgorithm"></a>An algorithm</h3>

<p>In order to accomplish this task programmatically, one option is to perform the following
steps</p>

<h4><a name="3.1.Authenticationoptions-Arguments"></a>Arguments</h4>
<ul>
	<li><em>uid</em> of a user (e.g. "hhornblow")</li>
	<li><em>password</em> proclaimed to be correct for the user</li>
</ul>


<h4><a name="3.1.Authenticationoptions-Steps"></a>Steps</h4>
<ol>
	<li>Bind to ApacheDS anonymously, or with the DN of a technical user. In both cases
it must be possible to search the directory afterwards (authorization has to be configured
that way)</li>
	<li>Perform a search operation with an appropriate filter to find the user entry for
the given ID, in our case "(&amp;(objectClass=inetorgperson)(uid=hhornblo))"
	<ul>
		<li>If the search result is empty, the user does not exist &#8211; terminate</li>
		<li>If the search result contains more than one entry, the given ID is not unique,
this is likely a data error within your directory</li>
	</ul>
	</li>
	<li>Bind to ApacheDS with the DN of the entry found in the previous search, and the
<em>password</em> provided as argument
	<ul>
		<li>If the bind operation fails, the password is wrong, and the result is <em>false</em>
(not authenticated)</li>
		<li>If the bind is successful, authenticate the user</li>
	</ul>
	</li>
</ol>


<h3><a name="3.1.Authenticationoptions-SamplecodewithJNDI"></a>Sample code
with JNDI</h3>

<p>The algorithm described above is implemented by many software solutions which are
able to integrate LDAP directories. You will learn more about some of them and their configuration
options within a later section of this guide.</p>

<p>For illustration purposes, here is a simple Java program which performs the steps
with the help of JNDI. It uses anonymous bind for the first step, hence it must be enabled
(replace with a technical user, if it better meets your requirements). </p>

<div class="code panel" style="border-width: 1px;"><div class="codeContent panelContent">
<pre class="code-java">
<span class="code-keyword">import</span> java.util.Hashtable;
<span class="code-keyword">import</span> javax.naming.Context;
<span class="code-keyword">import</span> javax.naming.NamingEnumeration;
<span class="code-keyword">import</span> javax.naming.NamingException;
<span class="code-keyword">import</span> javax.naming.directory.DirContext;
<span class="code-keyword">import</span> javax.naming.directory.InitialDirContext;
<span class="code-keyword">import</span> javax.naming.directory.SearchControls;
<span class="code-keyword">import</span> javax.naming.directory.SearchResult;

<span class="code-keyword">public</span> class AdvancedBindDemo {

    <span class="code-keyword">public</span> <span class="code-keyword">static</span>
void main(<span class="code-object">String</span>[] args) <span class="code-keyword">throws</span>
NamingException {

        <span class="code-keyword">if</span> (args.length &lt; 2) {
            <span class="code-object">System</span>.err.println(<span class="code-quote">"Usage:
java AdvancedBindDemo &lt;uid&gt; &lt;password&gt;"</span>);
            <span class="code-object">System</span>.exit(1);
        }

        Hashtable env = <span class="code-keyword">new</span> Hashtable();
        env.put(Context.INITIAL_CONTEXT_FACTORY, <span class="code-quote">"com.sun.jndi.ldap.LdapCtxFactory"</span>);
        env.put(Context.PROVIDER_URL, <span class="code-quote">"ldap:<span class="code-comment">//zanzibar:10389/"</span>);
</span>        env.put(Context.SECURITY_AUTHENTICATION, <span class="code-quote">"simple"</span>);
        
        <span class="code-object">String</span> uid = args[0];
        <span class="code-object">String</span> password = args[1];

        DirContext ctx = <span class="code-keyword">null</span>;
        <span class="code-keyword">try</span> {            
            <span class="code-comment">// Step 1: Bind anonymously            
</span>            ctx = <span class="code-keyword">new</span> InitialDirContext(env);
            
            <span class="code-comment">// Step 2: Search the directory
</span>            <span class="code-object">String</span> base = <span
class="code-quote">"o=sevenSeas"</span>;
            <span class="code-object">String</span> filter = <span class="code-quote">"(&amp;(objectClass=inetOrgPerson)(uid={0}))"</span>;
          
            SearchControls ctls = <span class="code-keyword">new</span> SearchControls();
            ctls.setSearchScope(SearchControls.SUBTREE_SCOPE);
            ctls.setReturningAttributes(<span class="code-keyword">new</span>
<span class="code-object">String</span>[0]);
            ctls.setReturningObjFlag(<span class="code-keyword">true</span>);
            NamingEnumeration enm = ctx.search(base, filter, <span class="code-keyword">new</span>
<span class="code-object">String</span>[] { uid }, ctls);
            
            <span class="code-object">String</span> dn = <span class="code-keyword">null</span>;
            <span class="code-keyword">if</span> (enm.hasMore()) {
                SearchResult result = (SearchResult) enm.next();
                dn = result.getNameInNamespace();
                
                <span class="code-object">System</span>.out.println(<span class="code-quote">"dn:
"</span>+dn);
            }
            
            <span class="code-keyword">if</span> (dn == <span class="code-keyword">null</span>
|| enm.hasMore()) {
                <span class="code-comment">// uid not found or not unique
</span>                <span class="code-keyword">throw</span> <span
class="code-keyword">new</span> NamingException(<span class="code-quote">"Authentication
failed"</span>);
            }
            
            <span class="code-comment">// Step 3: Bind with found DN and given password
</span>            ctx.addToEnvironment(Context.SECURITY_PRINCIPAL, dn);
            ctx.addToEnvironment(Context.SECURITY_CREDENTIALS, password);
            <span class="code-comment">// Perform a lookup in order to force a bind
operation with JNDI
</span>            ctx.lookup(dn);
            <span class="code-object">System</span>.out.println(<span class="code-quote">"Authentication
successful"</span>);
            
        } <span class="code-keyword">catch</span> (NamingException e) {
            <span class="code-object">System</span>.out.println(e.getMessage());
        } <span class="code-keyword">finally</span> {
            ctx.close();
        }
    }
}
</pre>
</div></div>

<p>Some example calls:</p>

<div class="code panel" style="border-width: 1px;"><div class="codeContent panelContent">
<pre class="code-none">
$ java AdvancedBindDemo unknown sailor
Authentication failed

$ java AdvancedBindDemo hornblo pass
dn: cn=Horatio Hornblower,ou=people,o=sevenSeas
Authentication successful

$ java AdvancedBindDemo hornblo quatsch
dn: cn=Horatio Hornblower,ou=people,o=sevenSeas
[LDAP: error code 49 - Bind failed: null]
</pre>
</div></div>

<p>The examples consist of an unknown user (an <em>inetOrgPerson</em> entry
with uid=unknown does not exist), a successful authenttication, and an attempt with an existing
uid but a wrong password.  </p>

<h2><a name="3.1.Authenticationoptions-Resources"></a>Resources </h2>

<ul>
	<li><a href="http://www.faqs.org/rfcs/rfc2829.html" rel="nofollow">RFC 2829</a>
Authentication Methods for LDAP</li>
	<li><a href="http://www.secure-hash-algorithm-md5-sha-1.co.uk/" rel="nofollow">The
Secure Hash Algorithm Directory</a> MD5, SHA-1 and HMAC Resources</li>
</ul>

     </div>
     <div id="commentsSection" class="wiki-content pageSection">
       <div style="float: right;">
            <a href="http://cwiki.apache.org/confluence/users/viewnotifications.action"
class="grey">Change Notification Preferences</a>
       </div>

       <a href="http://cwiki.apache.org/confluence/display/DIRxSRVx11/3.1.+Authentication+options">View
Online</a>
       |
       <a href="http://cwiki.apache.org/confluence/pages/diffpagesbyversion.action?pageId=55240&revisedVersion=13&originalVersion=12">View
Change</a>
            </div>
</div>
</div>
</div>
</div>
</body>
</html>

Mime
View raw message