You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@directory.apache.org by fe...@apache.org on 2011/12/08 22:35:33 UTC

svn commit: r1212137 - in /directory/documentation/apacheds-manuals/trunk/src/basic-user-guide-confluence: ./ basic-security/ images/

Author: felixk
Date: Thu Dec  8 21:35:32 2011
New Revision: 1212137

URL: http://svn.apache.org/viewvc?rev=1212137&view=rev
Log:
Moving basic user guide from confluence, 3. Chapter (DIRSERVER-1678)

Added:
    directory/documentation/apacheds-manuals/trunk/src/basic-user-guide-confluence/basic-security.confluence
    directory/documentation/apacheds-manuals/trunk/src/basic-user-guide-confluence/basic-security/authentication_options.confluence
    directory/documentation/apacheds-manuals/trunk/src/basic-user-guide-confluence/basic-security/basic_authorization.confluence
    directory/documentation/apacheds-manuals/trunk/src/basic-user-guide-confluence/basic-security/howto_enable_ssl.confluence
    directory/documentation/apacheds-manuals/trunk/src/basic-user-guide-confluence/images/studio_password_editor.png   (with props)
    directory/documentation/apacheds-manuals/trunk/src/basic-user-guide-confluence/images/studio_props_auth_anonymous.png   (with props)
    directory/documentation/apacheds-manuals/trunk/src/basic-user-guide-confluence/images/studio_props_auth_digestmd5.png   (with props)
    directory/documentation/apacheds-manuals/trunk/src/basic-user-guide-confluence/images/studio_props_auth_simplebind.png   (with props)
    directory/documentation/apacheds-manuals/trunk/src/basic-user-guide-confluence/images/studio_simplebind_failed.png   (with props)
Modified:
    directory/documentation/apacheds-manuals/trunk/src/basic-user-guide-confluence/book.txt
    directory/documentation/apacheds-manuals/trunk/src/basic-user-guide-confluence/images/portecle_with_certificate.png
    directory/documentation/apacheds-manuals/trunk/src/basic-user-guide-confluence/images/portecle_with_keystore.png
    directory/documentation/apacheds-manuals/trunk/src/basic-user-guide-confluence/images/studio_ssl.png

Added: directory/documentation/apacheds-manuals/trunk/src/basic-user-guide-confluence/basic-security.confluence
URL: http://svn.apache.org/viewvc/directory/documentation/apacheds-manuals/trunk/src/basic-user-guide-confluence/basic-security.confluence?rev=1212137&view=auto
==============================================================================
--- directory/documentation/apacheds-manuals/trunk/src/basic-user-guide-confluence/basic-security.confluence (added)
+++ directory/documentation/apacheds-manuals/trunk/src/basic-user-guide-confluence/basic-security.confluence Thu Dec  8 21:35:32 2011
@@ -0,0 +1,3 @@
+h1. Basic Security
+
+This chapter gives an overview about some of ApacheDS' security features. It provides both configuration and programming examples. The story begins with options available for authentication. This topic is not only interesting if you want to protect your server itself. Furthermore LDAP servers like ApacheDS are heavily used to authenticate users for other services (learn more about this in the example in section [4.2. User database for Apache Tomcat]). The next section covers authorization, i.e. how to ensure that authenticated have the privileges needed to perform certain tasks. The chapter ends with SSL configuration, which means protecting transmitted data (clear text passwords and other confidential stuff).

Added: directory/documentation/apacheds-manuals/trunk/src/basic-user-guide-confluence/basic-security/authentication_options.confluence
URL: http://svn.apache.org/viewvc/directory/documentation/apacheds-manuals/trunk/src/basic-user-guide-confluence/basic-security/authentication_options.confluence?rev=1212137&view=auto
==============================================================================
--- directory/documentation/apacheds-manuals/trunk/src/basic-user-guide-confluence/basic-security/authentication_options.confluence (added)
+++ directory/documentation/apacheds-manuals/trunk/src/basic-user-guide-confluence/basic-security/authentication_options.confluence Thu Dec  8 21:35:32 2011
@@ -0,0 +1,558 @@
+h2. Authentication options
+
+This section describes the authentication options of ApacheDS 2.0. Anonymous and simple binds are supported, as well as SASL mechanisms. Configuring and using them is described below with the help of examples.
+
+{toc:type=list|minLevel=2|maxLevel=2}
+
+h3. What is authentication?
+
+*Authentication* is the process of determining whether someone (or something) in fact is what he/she/it asserts to be. 
+
+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 *authorization*.
+
+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.
+
+ApacheDS 1.5 supports simple authentication and anonymous binds while storing passwords within _userPassword_ 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 simple binds.  
+
+h3. Simple binds
+
+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 _userpassword_ attribute of the given entry. If not, the bind operation fails (LDAP error code 49, LDAP_INVALID_CREDENTIALS), and the user is not authenticated.
+
+Assume this entry from the Seven Seas partition is stored within the directory (only a fragment with the relevant attributes is shown).
+
+{noformat}
+dn: cn=Horatio Hornblower,ou=people,o=sevenSeas
+objectclass: person
+objectclass: organizationalPerson
+cn: Horatio Hornblower
+sn: Hornblower
+userpassword: pass
+...
+{noformat}
+
+h4. Using Apache Directory Studio
+
+As described in [2.1.1. Apache Directory Studio], you can authenticate to an ApacheDS server if you provide a DN and a password. It is also possible to modify these parameters afterwards by editing the _Properties_ of the connection:
+
+!images/studio_props_auth_simplebind.png!
+
+Other UI tools have similar options.
+
+If you provide invalid information (a wrong password, for instance), Studio will reject to establish a connection:
+
+!images/studio_simplebind_failed.png!
+
+h4. Using command line tools
+
+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.
+
+{noformat}
+$ 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
+{noformat}
+
+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.
+
+{noformat}
+$ 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
+{noformat}
+
+h4. Binds from Java components using JNDI
+
+Using JNDI, authentication via simple binds is accomplished by appropriate configuration. One option is to provide the parameters in a Hashtable object like this
+
+{code:java}
+import java.util.Hashtable;
+
+import javax.naming.Context;
+import javax.naming.InitialContext;
+import javax.naming.NamingEnumeration;
+import javax.naming.NamingException;
+
+public class SimpleBindDemo {
+
+    public static void main(String[] args) throws NamingException {
+
+        if (args.length < 2) {
+            System.err.println("Usage: java SimpleBindDemo <userDN> <password>");
+            System.exit(1);
+        }
+
+        Hashtable env = new Hashtable();
+        env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");
+        env.put(Context.PROVIDER_URL, "ldap://zanzibar:10389/o=sevenSeas");
+
+        env.put(Context.SECURITY_AUTHENTICATION, "simple");
+        env.put(Context.SECURITY_PRINCIPAL, args[0]);
+        env.put(Context.SECURITY_CREDENTIALS, args[1]);
+
+        try {
+            Context ctx = new InitialContext(env);
+            NamingEnumeration enm = ctx.list("");
+            while (enm.hasMore()) {
+                System.out.println(enm.next());
+            }
+            ctx.close();
+        } catch (NamingException e) {
+            System.out.println(e.getMessage());
+        }
+    }
+}
+{code}  
+
+
+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:
+
+{noformat}
+$ java SimpleBindDemo "cn=Horatio Hornblower,ou=people,o=sevenSeas" pass
+ou=people: javax.naming.directory.DirContext
+ou=groups: javax.naming.directory.DirContext
+{noformat}
+
+On the other hand, providing an incorrect password results in a failed bind operation. JNDI maps it to a _NamingException_:
+
+{noformat}
+$ java SimpleBindDemo "cn=Horatio Hornblower,ou=people,o=sevenSeas" quatsch
+[LDAP: error code 49 - Bind failed: null]
+{noformat}
+
+In real life, you obviously want to separate most of the configuration data from the source code, for instance with the help of the _jndi.properties_ file.
+
+h3. Passwords stored one-way encrypted
+
+If passwords are stored in the directory in clear like above, the administrator (_uid=admin,ou=system_) 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.
+
+{warning}
+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...
+{warning}
+
+h4. Passwords not stored in clear text
+
+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 _userpassword_ values (instead of the clear text values), for instance:
+
+{noformat}
+dn: cn=Horatio Hornblower,ou=people,o=sevenSeas
+objectclass: person
+objectclass: organizationalPerson
+cn: Horatio Hornblower
+sn: Hornblower
+userpassword: {SHA}nU4eI71bcnBGqeO0t9tXvY1u5oQ=
+...
+{noformat}
+
+The value "\{SHA\}nU4eI71bcnBGqeO0t9tXvY1u5oQ=" means that _SHA_ (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 -- 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").  
+
+h4. But how to obtain the hash value for a password?
+
+Many UI LDAP tools allow you to store passwords automatically encrypted with the hash algorithm of your choice. [Apache Directory Studio|http://directory.apache.org/studio/] for instance. The dialog automatically shows up if a _userPassword_ attribute is to be manipulated (added, changed).
+
+!images/studio_password_editor.png!
+
+With some lines of code, it is quite easy to accomplish this task programatically in Java:
+
+{code:java}
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import sun.misc.BASE64Encoder;
+
+public class DigestDemo {
+    public static void main(String[] args) throws NoSuchAlgorithmException {
+        String password = "pass";
+        String algorithm = "SHA";
+        
+        // Calculate hash value
+        MessageDigest md = MessageDigest.getInstance(algorithm);
+        md.update(password.getBytes());
+        byte[] bytes = md.digest();
+        
+        // Print out value in Base64 encoding
+        BASE64Encoder base64encoder = new BASE64Encoder();
+        String hash = base64encoder.encode(bytes);        
+        System.out.println('{'+algorithm+'}'+hash);
+    }
+}
+{code}
+
+Another option is to use command line tools to calculate the hash value; the [OpenSSL|http://www.openssl.org] project provides such stuff. 
+
+h4. From an LDAP client point of view
+
+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:
+
+{noformat}
+$ 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
+{noformat}
+
+Providing the hashed value of the _userPassword_ attribute instead of the original value will be rejected by ApacheDS:
+
+{noformat}
+$ 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
+{noformat}
+
+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.
+
+{note:title=Be Warned: Limited security added}
+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). 
+
+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!
+{note}
+
+h3. Anonymous binds
+
+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 -- without providing credentials. An example for this are enterprise wide telephone books, if clients access the directory service from the intranet.
+
+h4. Enable/disable anonymous binds
+
+Anonymous access is disabled by default. Changing this is an easy configuration task. If you use the server standalone configured with a _server.xml_ file, you can enable it by changing the value for attribute _allowAnonymousAccess_ in the configuration for element _apacheDS_, as depicted in the following fragment:
+
+{noformat}
+...
+<apacheDS id="apacheDS"
+            synchPeriodMillis="15000"
+            allowAnonymousAccess="false">
+
+    <directoryService>#directoryService</directoryService>
+    <ldapServer>#ldapServer</ldapServer>
+    <ldapsServer>#ldapsServer</ldapsServer>
+</apacheDS>
+...
+{noformat}
+
+A restart of the server is necessary for this change to take effect. 
+
+h4. Example: Server behavior with anonymous binds disabled
+
+Assume anonymous binds are disabled, as configured by default in ApacheDS 2.0, and our sample partition _Seven Seaes_ 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 _ou=people,o=sevenSeas_ should be displayed.
+
+See the command and the resulting error message provided by the server below 
+
+{noformat}
+$ ldapsearch -h zanzibar -p 10389 -b "ou=people,o=sevenSeas" -s base "(objectclass=*)"
+ldap_search: Insufficient access
+ldap_search: additional info: failed on search operation: Anonymous binds have been disabled!
+{noformat}
+
+h4. Example: Server behavior with anonymous binds enabled
+
+Now the same command performed against ApacheDS 2.0 with anonymous access enabled as described above. The behavior is different -- the entry is visible. 
+
+{noformat}
+$ 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
+{noformat}
+
+h4. Other clients
+
+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 [Apache Directory Studio|http://directory.apache.org/studio/] as an example. During configuration of the connection data ("New LDAP Connection", for instance), the option _Anonymous Authentication_ leads to anonymous binds. Other UI tools offer this feature as well. It is also possible to modify the properties of a connection afterwards.
+
+!images/studio_props_auth_anonymous.png!
+
+{note:title=Use this feature wisely}
+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 [3.2. Basic authorization] section.
+{note}
+
+h3. How to authenticate a user by uid and password with simple binds?
+
+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 provide a short, unique _ID_ and the password, like in this web form
+
+!images/confluence_logon.png!
+
+Usually the ID is an attribute within the user's entry. In our sample data (Seven Seas), each user entry contains the _uid_ attribute, for instance uid=hhornblo for Captain Hornblower:
+
+{code}
+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=
+{code}
+
+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?
+
+h4. An algorithm
+
+In order to accomplish this task programmatically, one option is to perform the following steps
+
+h5. Arguments
+* _uid_ of a user (e.g. "hhornblow")
+* _password_ proclaimed to be correct for the user
+
+h5. Steps
+# 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)
+# Perform a search operation with an appropriate filter to find the user entry for the given ID, in our case "(&(objectClass=inetorgperson)(uid=hhornblo))"
+** If the search result is empty, the user does not exist -- terminate
+** If the search result contains more than one entry, the given ID is not unique, this is likely a data error within your directory
+# Bind to ApacheDS with the DN of the entry found in the previous search, and the _password_ provided as argument
+** If the bind operation fails, the password is wrong, and the result is _false_ (not authenticated) 
+** If the bind is successful, authenticate the user
+
+h4. Sample code with JNDI
+
+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.
+
+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). 
+
+{code:java}
+import java.util.Hashtable;
+import javax.naming.Context;
+import javax.naming.NamingEnumeration;
+import javax.naming.NamingException;
+import javax.naming.directory.DirContext;
+import javax.naming.directory.InitialDirContext;
+import javax.naming.directory.SearchControls;
+import javax.naming.directory.SearchResult;
+
+public class AdvancedBindDemo {
+
+    public static void main(String[] args) throws NamingException {
+
+        if (args.length < 2) {
+            System.err.println("Usage: java AdvancedBindDemo <uid> <password>");
+            System.exit(1);
+        }
+
+        Hashtable env = new Hashtable();
+        env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");
+        env.put(Context.PROVIDER_URL, "ldap://zanzibar:10389/");
+        env.put(Context.SECURITY_AUTHENTICATION, "simple");
+        
+        String uid = args[0];
+        String password = args[1];
+
+        DirContext ctx = null;
+        try {            
+            // Step 1: Bind anonymously            
+            ctx = new InitialDirContext(env);
+            
+            // Step 2: Search the directory
+            String base = "o=sevenSeas";
+            String filter = "(&(objectClass=inetOrgPerson)(uid={0}))";           
+            SearchControls ctls = new SearchControls();
+            ctls.setSearchScope(SearchControls.SUBTREE_SCOPE);
+            ctls.setReturningAttributes(new String[0]);
+            ctls.setReturningObjFlag(true);
+            NamingEnumeration enm = ctx.search(base, filter, new String[] { uid }, ctls);
+            
+            String dn = null;
+            if (enm.hasMore()) {
+                SearchResult result = (SearchResult) enm.next();
+                dn = result.getNameInNamespace();
+                
+                System.out.println("dn: "+dn);
+            }
+            
+            if (dn == null || enm.hasMore()) {
+                // uid not found or not unique
+                throw new NamingException("Authentication failed");
+            }
+            
+            // Step 3: Bind with found DN and given password
+            ctx.addToEnvironment(Context.SECURITY_PRINCIPAL, dn);
+            ctx.addToEnvironment(Context.SECURITY_CREDENTIALS, password);
+            // Perform a lookup in order to force a bind operation with JNDI
+            ctx.lookup(dn);
+            System.out.println("Authentication successful");
+            
+        } catch (NamingException e) {
+            System.out.println(e.getMessage());
+        } finally {
+            ctx.close();
+        }
+    }
+}
+{code}
+
+Some example calls:
+
+{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]
+{code}
+
+The examples consist of an unknown user (an _inetOrgPerson_ entry with uid=unknown does not exist), a successful authentication, and an attempt with an existing uid but a wrong password.
+
+h3. An example for a strong authentication mechanism: DIGEST-MD5
+
+The option described until now are widely adopted and easy to configure. Unfortunately, they are not very secure, at least if used without SSL. The reason for this is that with a simple bind, both DN and password are transmitted in clear.
+
+One option to increase security is to use simple binds in conjunction with LDAPS. Learn kore about enabling SSL in section [3.3. How to enable SSL].
+
+Another option is to use a stronger authentication mechanism. ApacheDS supports SASL mechanism, and as a last example of this chapter we describe how to configure our sample directory to provide DIGEST-MD5.
+
+h4. About DIGEST-MD5
+
+DIGEST-MD5 is a mechanism which allows authentication with user ID and password without transmission of the password itself. Basically, the server sends a challenge to the client, which s/he is only able to answer if s/he knows the password. A computation is necessary, in which the password is an input  parameter. 
+
+The client responds with the result, and the server compares it with the expected value. The latter is computed by the server the same way the client has built the answer (the server knows the user's password as well). If the two values match, the client has proved that he knows the password, without transmitting it.
+
+Note that the server needs to know the passwords of the users in clear text. One-way-encryption causes the mechanism to fail! 
+
+h4. Requisites on the server side
+
+Within the ApacheDS configuration file _server.xml_, some parameters have to be adjusted in order to use DIGEST-MD5 for the users in the example partition _o=sevenSeas_. The following fragment of _server.xml_ is from the modified configuration file.
+
+h5. Configuration in _server.xml_
+
+{noformat}
+...
+  <ldapServer id="ldapServer"
+              ipPort="10389"
+              saslHost="zanzibar"
+              searchBaseDn="ou=people,o=sevenSeas"
+              ...>
+
+    ...
+
+    <!-- The list of supported authentication mechanisms.                   -->
+    <supportedMechanisms>
+      <value xmlns="http://www.springframework.org/schema/beans">SIMPLE</value>
+      <value xmlns="http://www.springframework.org/schema/beans">CRAM-MD5</value>
+      <value xmlns="http://www.springframework.org/schema/beans">DIGEST-MD5</value>
+      <!--<value xmlns="http://www.springframework.org/schema/beans">GSSAPI</value>-->
+    </supportedMechanisms>
+
+    <!-- The desired quality-of-protection, used by DIGEST-MD5 and GSSAPI.  -->
+    <saslQop>
+      <value xmlns="http://www.springframework.org/schema/beans">auth</value>
+      <value xmlns="http://www.springframework.org/schema/beans">auth-int</value>
+      <value xmlns="http://www.springframework.org/schema/beans">auth-conf</value>
+    </saslQop>
+
+    <!-- The realms serviced by this SASL host, used by DIGEST-MD5 and GSSAPI. -->
+    <saslRealms>
+      <value xmlns="http://www.springframework.org/schema/beans">sevenSeas@zanzibar</value>
+    </saslRealms>
+
+    ...
+  </ldapServer>
+{noformat}
+
+Attribute _saslHost_ carries the fully qualified domain name of the server, as used by the client during SASL authentication. In our case, we use the sample hostname "zanzibar". 
+Attribute _searchBaseDn_ is the Base DN for searches, which ApacheDS will perform in order to find a user entry for a given _uid_. Note that for DIGEST-MD5, a client has only to present his ID. No DN is given. This leads to search task for the server, because it needs the user entry for the password value.
+As a _saslRealms_ value, we have added "sevenSeas@zanzibar". This value must be known to the clients which would like to authenticate.
+
+h5. User entries
+
+As described above, in the DIGEST-MD5 mechanism needs to know the user password in clear. The corresponding entries should therefore contain unencrypted _userPassword_ attribute values. Furthermore, the _uid_ attribute must be present, as it is used by the searches to find the entry.
+
+{noformat}
+dn: cn=Horatio Hornblower,ou=people,o=sevenSeas
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Horatio Hornblower
+sn: Hornblower
+uid: hhornblo
+userpassword: pass
+...
+{noformat}
+
+The user entries must reside below the search base defined in the _ldapServer_ element of the _server.xml_ ("ou=people,o=sevenSeas") in our case.
+
+h4. Authentication 
+
+Let's finally check, whether the configuration works, with the help of two clients.
+
+h5. Using Apache Directory Studio
+
+Apache Directory Studio supports authentication with DIGEST-MD5 for LDAP connection. The following screen shows the parameters, which exactly fit to the configuration above. User "hhornblow" tries to authenticate with password "pass".
+
+!images/studio_props_auth_digestmd5.png!
+
+Try _Check Authentication_ to see whether it works. In case of a failure, the detailed error message should help to resolve the issue. Otherwise check the server logs.
+
+h5. Programmatically with JNDI
+
+The following JNDI program performs the same connect against the configured ApacheDS and lists the entries below "o=sevenSeas".
+
+{code:java}
+import java.util.Hashtable;
+
+import javax.naming.Context;
+import javax.naming.InitialContext;
+import javax.naming.NamingEnumeration;
+import javax.naming.NamingException;
+import javax.security.sasl.Sasl;
+
+public class DigestMd5Demo {
+
+    public static void main(String[] args) throws NamingException {
+
+        Hashtable<String, String> env = new Hashtable<String, String>();
+        env.put(Context.INITIAL_CONTEXT_FACTORY,
+                "com.sun.jndi.ldap.LdapCtxFactory");
+        env.put(Context.PROVIDER_URL, "ldap://zanzibar:10389");
+
+        env.put(Context.SECURITY_AUTHENTICATION, "DIGEST-MD5");
+        
+        // authentication plus integrity and confidentiality protection
+        env.put(Sasl.QOP, "auth-conf"); 
+        
+        env.put(Context.SECURITY_PRINCIPAL, "hhornblo");
+        env.put(Context.SECURITY_CREDENTIALS, "pass");
+
+        env.put("java.naming.security.sasl.realm", "sevenSeas@zanzibar");
+
+        try {
+            Context ctx = new InitialContext(env);
+            NamingEnumeration enm = ctx.list("o=sevenSeas");
+            while (enm.hasMore()) {
+                System.out.println(enm.next());
+            }
+            ctx.close();
+        } catch (NamingException e) {
+            System.out.println(e.getMessage());
+        }
+    }
+}
+{code}
+
+You can download the source code [here|^DigestMd5Demo.java|DigestMd5Demo.java].
+
+
+h3. Resources 
+
+* [RFC 4513|http://tools.ietf.org/html/rfc4513|tools.ietf.org] LDAP: Authentication Methods and Security Mechanisms
+* [The Secure Hash Algorithm Directory|http://www.secure-hash-algorithm-md5-sha-1.co.uk/|www.secure-hash-algorithm-md5-sha-1.co.uk] MD5, SHA-1 and HMAC Resources
+* [RFC 4422|http://tools.ietf.org/html/rfc4422|tools.ietf.org] Simple Authentication and Security Layer (SASL)
+* [RFC 2831|http://tools.ietf.org/html/rfc2831|tools.ietf.org] Using Digest Authentication as a SASL Mechanism

Added: directory/documentation/apacheds-manuals/trunk/src/basic-user-guide-confluence/basic-security/basic_authorization.confluence
URL: http://svn.apache.org/viewvc/directory/documentation/apacheds-manuals/trunk/src/basic-user-guide-confluence/basic-security/basic_authorization.confluence?rev=1212137&view=auto
==============================================================================
--- directory/documentation/apacheds-manuals/trunk/src/basic-user-guide-confluence/basic-security/basic_authorization.confluence (added)
+++ directory/documentation/apacheds-manuals/trunk/src/basic-user-guide-confluence/basic-security/basic_authorization.confluence Thu Dec  8 21:35:32 2011
@@ -0,0 +1,584 @@
+h2. Basic authorization
+
+This section describes the default authorization functionality of ApacheDS 2.0, which is very simple. On the other hand, it is inadequate for most serious deployments. Therefore a basic example to the "real" authorization subsystem is provided as well.
+
+{toc:type=list|minLevel=2|maxLevel=2}
+
+h3. What is authorization?
+
+After authentication of a user or an application (or more generally an LDAP client) against the directory server (or attaining anonymous access respectively), certain LDAP operations will be granted or rejected, according to configuration and certain rules. This process of granting access is called authorization.
+
+Authorization for directory operations is not strictly standardized in the LDAP world, [RFC 4513|http://www.faqs.org/rfcs/rfc4513.html|www.faqs.org] describes various scenarios and concepts, but does not enforce a concrete implementation. Thus each product comes with its own authorization feature. So does ApacheDS. A powerful authorization subsystem is provided since version 0.9.3, but disabled as a default.
+
+h4. Authorization for directory operations vs. group membership
+
+In order to accomplish their authorization functionality, software components often take advantage of LDAP groups stored within the directory. _groupOfNames_ and _groupOfUniqueNames_ are common object classes for groups entries; they contain the DNs of their members (users, other groups) as attribute values. 
+
+In order to illustrate this, the "Seven Seas" example partition contains such group entries below "ou=groups,o=sevenSeas". Here the entry of a group describing the HMS Bounty crew (before the mutiny) in LDIF format.
+
+{code:none}
+dn: cn=HMS Bounty,ou=crews,ou=groups,o=sevenSeas
+objectclass: groupOfUniqueNames
+objectclass: top
+cn: HMS Bounty
+uniquemember: cn=William Bligh,ou=people,o=sevenSeas
+uniquemember: cn=Fletcher Christian,ou=people,o=sevenSeas
+uniquemember: cn=John Fryer,ou=people,o=sevenSeas
+...
+{code}
+
+In such a scenario, a user, who is directly or indirectly member of a certain group is permitted to do something. The software component acts as a normal LDAP client and determines group belonging with the help of ordinary search operations. This is widely used but has nothing to do with the authorization for directory operations as described in this section (except that the client needs the permission to search the data). Learn more about best practices in this area in the article [Practices in Directory Groups|http://middleware.internet2.edu/dir/groups/docs/internet2-mace-dir-groups-best-practices-200210.htm]. Further examples in this guide are the Tomcat and Apache HTTPD integration sections.
+
+h3. Default authorization behavior for directory operations
+
+Without access controls enabled all entries are accessible and alterable by all: even anonymous users. There are however some minimal built-in rules for protecting users and groups within the server without having to turn on the ACI subsystem.
+
+h4. Sample data within "ou=users,ou=system"
+
+In addition to our brave sailors below _ou=people,o=sevenSeas_, assume the following to entries present within _ou=users,ou=system_:
+
+{noformat}
+dn: cn=Tori Amos,ou=users,ou=system
+objectclass: person
+objectclass: top
+sn: Amos
+cn: Tori Amos
+userpassword: amos
+
+dn: cn=Kate Bush,ou=users,ou=system
+objectclass: person
+objectclass: top
+sn: Bush
+cn: Kate Bush
+userpassword: bush
+{noformat}
+
+Here is an LDAP Browser view of the entries from Apache Directory Studio
+
+!images/authorization_sample_entries.png!
+
+The two entries are used in the following examples, in conjunction with _o=sevenSeas_, to describe the default authorization rules.
+
+h4. Rules and sample operations
+
+Without ACIs the server automatically protects, hides, the admin user from everyone but the admin user. Here a sample search operation in order to demonstrate this protection. We use a [command line tool|2.2.2. Command line tools] for this task for the ease of documentation; you can perform the same operations with a UI tool like Apache Directory Studio.
+
+The same command is submitted three times with different users.
+
+{noformat}
+$ ldapsearch -h zanzibar -p 10389 -D "uid=admin,ou=system" -w secret \\
+    -b "ou=system" -s one "(uid=admin)" dn
+version: 1
+dn: uid=admin,ou=system
+
+$ ldapsearch -h zanzibar -p 10389 -D "cn=William Bush,ou=people,o=sevenSeas" -w pass \\
+    -b "ou=system" -s one "(uid=admin)" dn
+
+$ ldapsearch -h zanzibar -p 10389 -D "cn=Tori Amos,ou=users,ou=system" -w amos \\
+    -b "ou=system" -s one "(uid=admin)" dn
+
+$
+{noformat}
+
+Users cannot see other user entries under the 'ou=users,ou=system' entry. So placing new users there automatically protects them. Placing new users anywhere else exposes them.
+
+{noformat}
+$ ldapsearch -h zanzibar -p 10389 -D "uid=admin,ou=system" -w secret \\
+    -b "ou=users,ou=system" -s one "(objectclass=*)" dn
+version: 1
+dn: cn=Tori Amos,ou=users,ou=system
+
+dn: cn=Kate Bush,ou=users,ou=system
+
+$ ldapsearch -h zanzibar -p 10389 -D "cn=Kate Bush,ou=users,ou=system" -w bush \\
+    -b "ou=users,ou=system" -s one "(objectclass=*)" dn
+version: 1
+dn: cn=Kate Bush,ou=users,ou=system
+
+$ ldapsearch -h zanzibar -p 10389 -D "cn=William Bush,ou=people,o=sevenSeas" -w pass \\
+    -b "ou=users,ou=system" -s one "(objectclass=*)" dn
+
+$ ldapsearch -h zanzibar -p 10389 -D "cn=William Bush,ou=people,o=sevenSeas" -w pass \\
+    -b "ou=people,o=sevenSeas" -s one "(objectclass=*)" dn
+version: 1
+dn: cn=Horatio Hornblower,ou=people,o=sevenSeas
+
+dn: cn=William Bush,ou=people,o=sevenSeas
+
+dn: cn=Thomas Masterman Hardy,ou=people,o=sevenSeas
+
+dn: cn=Cornelius Buckley,ou=people,o=sevenSeas
+
+dn: cn=William Bligh,ou=people,o=sevenSeas
+...
+$
+{noformat}
+
+Groups defined using _groupOfNames_ or _groupOfUniqueNames_ under the 'ou=groups,ou=system' are also protected from access or alteration by anyone other than the admin user. Again this protection is not allowed anywhere else but under these entries.
+
+h4. Is this sufficient?
+
+For simple configurations the described rules should provide adequate protection but it lacks flexibility. For advanced configurations users should enable the ACI subsystem. This however shuts down access to everything by everyone except the admin user which bypasses the ACI subsystem. Directory administrators should look at the documentation on how to specify access control information in the Advanced User's Guide.
+
+h3. Simple example for the ACI subsystem
+
+As an appetizer for the stunning ACI subsystem (ACI = access control item) within ApacheDS, we provide a simple yet realistic example. It manifests the following requirements
+
+h4. Requirements met
+
+# Suffix "o=sevenSeas" used as Access Control Specific Area
+# User "cn=Horatio Nelson,ou=people,o=sevenSeas" should be able to perform all operations (delete, add, ...) below the base "o=sevenSeas"
+# Other users and anonymous users should only be able to search and compare (no add, modify etc.)
+# Other users and anonymous users should not be able to read the userPassword attribute
+
+h4. Enable the ACI Subsystem
+
+The authorization (ACI) subsystem is disabled by default. If you use the server standalone configured with a _server.xml_ file, you can enable it by changing the value for attribute_accessControlEnabled_ in the defaultDirectoryService element, as depicted in the following fragment:
+
+{noformat}
+...
+<defaultDirectoryService 
+   id="directoryService" 
+   instanceId="default"
+   ...
+   accessControlEnabled="true"
+   ...>
+    ...
+</defaultDirectoryService>
+...{noformat}
+
+A restart of the server is necessary for this change to take effect. 
+
+h4. Further configuration tasks to perform afterwards
+
+1. Create an operational attribute _administrativeRole_ with value "accessControlSpecificArea" in the entry "o=sevenSeas".
+2. Create a subentry subordinate to "o=sevenSeas" to grant all operations' permissions to "cn=Horatio Nelson,ou=people,o=sevenSeas", who acts as directory manager 
+
+The subentry should contain the following attributes and values:
+
+{noformat}
+cn="sevenSeasAuthorizationRequirementsACISubentry"
+subtreeSpecification="{}"
+prescriptiveACI="{
+                   identificationTag "directoryManagerFullAccessACI",
+                   precedence 11,
+                   authenticationLevel simple,
+                   itemOrUserFirst userFirst:
+                   {
+                     userClasses
+                     {
+                       name { "cn=Horatio Nelson,ou=people,o=sevenSeas" }
+                     },
+                     userPermissions
+                     { 
+                       {
+                         protectedItems
+                         {
+                           entry, allUserAttributeTypesAndValues
+                         },
+                         grantsAndDenials
+                         {
+                           grantAdd, grantDiscloseOnError, grantRead,
+                           grantRemove, grantBrowse, grantExport, grantImport,
+                           grantModify, grantRename, grantReturnDN,
+                           grantCompare, grantFilterMatch, grantInvoke
+                         } 
+                       }
+                     }
+                   } 
+                 }"
+{noformat}
+
+3. A new attribute value should added to the previously created Subentry's prescriptiveACI attribute to grant search and compare permissions to all users.
+
+The new value:
+
+{noformat}
+prescriptiveACI="{
+                   identificationTag "allUsersSearchAndCompareACI",
+                   precedence 10,
+                   authenticationLevel simple,
+                   itemOrUserFirst userFirst:
+                   {
+                     userClasses
+                     {
+                       allUsers
+                     },
+                     userPermissions
+                     { 
+                       {
+                         protectedItems
+                         {
+                           entry, allUserAttributeTypesAndValues
+                         },
+                         grantsAndDenials
+                         {
+                           grantRead, grantBrowse, grantReturnDN,
+                           grantCompare, grantFilterMatch, grantDiscloseOnError 
+                         } 
+                       }
+                     }
+                   } 
+                 }"
+{noformat}
+
+4. A new attribute value should added to the previously created Subentry's prescriptiveACI attribute to deny search and compare permissions for _userPassword_ attribute to all users.
+
+The new value:
+
+{noformat}
+prescriptiveACI="{
+                   identificationTag "preventAllUsersFromReadingUserPasswordAttributeACI",
+                   precedence 10,
+                   authenticationLevel simple,
+                   itemOrUserFirst userFirst:
+                   {
+                     userClasses
+                     {
+                       allUsers
+                     },
+                     userPermissions
+                     { 
+                       {
+                         protectedItems
+                         {
+                           attributeType { userPassword }
+                         },
+                         grantsAndDenials
+                         {
+                           denyRead, denyCompare, denyFilterMatch
+                         } 
+                       }
+                     }
+                   } 
+                 }"
+{noformat}
+
+The two values given in 3 and 4 can be combined in a single value as:
+
+{noformat}
+prescriptiveACI="{
+                   identificationTag "allUsersACI",
+                   precedence 10,
+                   authenticationLevel none,
+                   itemOrUserFirst userFirst:
+                   {
+                     userClasses
+                     {
+                       allUsers
+                     },
+                     userPermissions
+                     { 
+                       {
+                         protectedItems { entry, allUserAttributeTypesAndValues },
+                         grantsAndDenials { grantRead, grantBrowse, grantReturnDN,
+                                            grantCompare, grantFilterMatch, grantDiscloseOnError } 
+                       },
+                       {
+                         protectedItems { attributeType { userPassword } },
+                         grantsAndDenials { denyRead, denyCompare, denyFilterMatch }
+                       }
+                     }
+                   } 
+                 }"
+{noformat}
+ 
+h4. LDIF for this configuration
+
+The following LDIF file ([^authz_sevenSeas.ldif]) provides a set of changes made to directory entries in the "Seven Seas" data. In total it performs the steps described above.  
+
+{code:none}
+# File authz_sevenSeas.ldif
+#
+# Create an operational attribute "administrativeRole"
+# with value "accessControlSpecificArea" in the entry "o=sevenSeas".
+#
+dn: o=sevenSeas
+changetype: modify
+add: administrativeRole
+administrativeRole: accessControlSpecificArea
+
+# Create a subentry subordinate to "o=sevenSeas" to grant all operations' permissions 
+# to "cn=Horatio Nelson,ou=people,o=sevenSeas", to grant search and compare permissions
+# to all users and to deny search and compare permissions for userPassword attribute to all users. 
+#
+dn: cn=sevenSeasAuthorizationRequirementsACISubentry,o=sevenSeas
+changetype: add
+objectclass: top
+objectclass: subentry
+objectclass: accessControlSubentry
+cn: sevenSeasAuthorizationRequirementsACISubentry
+subtreeSpecification: {}
+prescriptiveACI: {
+    identificationTag "directoryManagerFullAccessACI",
+    precedence 11,
+    authenticationLevel simple,
+    itemOrUserFirst userFirst:
+    {
+      userClasses
+      {
+        name { "cn=Horatio Nelson,ou=people,o=sevenSeas" }
+      },
+      userPermissions
+      { 
+        {
+          protectedItems
+          {
+            entry, allUserAttributeTypesAndValues
+          },
+          grantsAndDenials
+          {
+            grantAdd, grantDiscloseOnError, grantRead,
+            grantRemove, grantBrowse, grantExport, grantImport,
+            grantModify, grantRename, grantReturnDN,
+            grantCompare, grantFilterMatch, grantInvoke
+          } 
+        }
+      }
+    } 
+  }
+prescriptiveACI: {
+    identificationTag "allUsersACI",
+    precedence 10,
+    authenticationLevel none,
+    itemOrUserFirst userFirst:
+    {
+      userClasses
+      {
+        allUsers
+      },
+      userPermissions
+      { 
+        {
+          protectedItems { entry, allUserAttributeTypesAndValues },
+          grantsAndDenials { grantRead, grantBrowse, grantReturnDN,
+                             grantCompare, grantFilterMatch, grantDiscloseOnError } 
+        },
+        {
+          protectedItems { attributeType { userPassword } },
+          grantsAndDenials { denyRead, denyCompare, denyFilterMatch }
+        }
+      }
+    }
+  }
+{code}
+
+To apply this configuration to the sample data partition, you can perform an _ldapmodify_ with the LDIF as argument:
+
+{noformat}
+$ ldapmodify -h zanzibar -p 10389 -D "uid=admin,ou=system" -w secret -f authz_sevenSeas.ldif
+modifying entry o=sevenSeas
+
+adding new entry cn=sevenSeasAuthorizationRequirementsACISubentry,o=sevenSeas
+$
+{noformat}
+
+It is also possible to use graphical tools; some of them offer the feature to perform operations given in LDIF.
+
+h3. Verification, that it works
+
+After successfully applying the changes to the sample partition, one may ask how to check whether it works. We therefore perform some operations with the help of command line tools. Some will be permitted, some will not (and cause an appropriate error message). It would also be able to check this with the help of graphical tools (you might like to do this instead). But it is easier to document the parameters used with the help command line arguments.  
+
+h4. Performing some search operations in order to read data
+
+Bind as user "William Bush" and search for entries which match "(uid=hhornblo)". Expected behavior: We are able to read the attributes of entry "cn=Horatio Hornblower,ou=people,o=sevenSeas" (the only entry which matches the filter). The password attribute should not be visible. It works as desired: 
+
+{code:none}
+$ ldapsearch -h zanzibar -p 10389 -D "cn=William Bush,ou=people,o=sevenSeas" -w pass \\
+    -b "o=sevenSeas" -s sub "(uid=hhornblo)"
+version: 1
+dn: cn=Horatio Hornblower,ou=people,o=sevenSeas
+mail: hhornblo@royalnavy.mod.uk
+objectclass: person
+objectclass: organizationalPerson
+objectclass: inetOrgPerson
+objectclass: top
+cn: Horatio Hornblower
+uid: hhornblo
+givenname: Horatio
+description: Capt. Horatio Hornblower, R.N
+sn: Hornblower
+{code}
+
+In the described configuration, the user "Horatio Nelson" acts as a directory manager below "o=sevenSeas". Hence he should basically be allowed to do everything. He should even be able to see other users' _userPassword_ values. In our case, the hash function _SHA_ was applied to them:
+
+{code:none}
+$ ldapsearch -h zanzibar -p 10389 -D "cn=Horatio Nelson,ou=people,o=sevenSeas" -w pass \\
+    -b "o=sevenSeas" -s sub "(objectclass=person)" uid userPassword
+version: 1
+dn: cn=Horatio Hornblower,ou=people,o=sevenSeas
+userpassword: {SHA}nU4eI71bcnBGqeO0t9tXvY1u5oQ=
+uid: hhornblo
+
+dn: cn=William Bush,ou=people,o=sevenSeas
+userpassword: {SHA}nU4eI71bcnBGqeO0t9tXvY1u5oQ=
+uid: wbush
+
+dn: cn=Thomas Quist,ou=people,o=sevenSeas
+userpassword: {SHA}nU4eI71bcnBGqeO0t9tXvY1u5oQ=
+uid: tquist
+...
+{code}
+
+But "Horation Nelson" is not able to perform searches in other areas than "o=sevenSeas" to see the entries. Of course our global ApacheDS administrator "uid=admin,ou=system" is still able to see them:
+
+{code:none}
+$ ldapsearch -h zanzibar -p 10389 -D "cn=Horatio Nelson,ou=people,o=sevenSeas" -w pass \\
+    -b "ou=system" -s sub "(objectclass=person)"
+
+$ ldapsearch -h zanzibar -p 10389 -D "uid=admin,ou=system" -w secret \\
+    -b "ou=system" -s sub "(objectclass=person)"
+version: 1
+dn: uid=admin,ou=system
+sn: administrator
+cn: system administrator
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+userpassword: secret
+uid: admin
+displayName: Directory Superuser
+
+dn: cn=Tori Amos,ou=users,ou=system
+cn: Tori Amos
+userpassword: amos
+objectclass: person
+objectclass: top
+sn: Amos
+...
+{code}
+
+h4. Trying to manipulate data
+
+Until now the authorization only hided data (entries, attributes) from users with insufficient access rights. Let's perform some operations which try to manipulate the directory data! 
+
+h5. Adding an entry
+
+First we try to add a new user to the "Seven Seas" partition. The data for the entry is inspired by "Peter Pan" and provided by this LDIF file ([^captain_hook.ldif]): 
+
+{code:none}
+# File captain_hook.ldif
+dn: cn=James Hook,ou=people,o=sevenSeas
+objectclass: inetOrgPerson
+objectclass: organizationalPerson
+objectclass: person
+objectclass: top
+cn: James Hook
+description: A pirate captain and Peter Pan's nemesis
+sn: Hook
+mail: jhook@neverland
+userpassword: peterPan
+{code}
+
+An anonymous user is not allowed to create new entries, as the following error message shows:
+
+{code:none}
+$ ldapmodify -h zanzibar -p 10389 -a -f captain_hook.ldif
+adding new entry cn=James Hook,ou=people,o=sevenSeas
+ldap_add: Insufficient access
+ldap_add: additional info: failed to add entry cn=James Hook,ou=people,o=sevenSeas: null
+$
+{code}
+
+Of course, the "Insufficient access" (LDAP result code 50) error would be presented by other client as wee, Apache Directory Studio, if you try to manipulate entries with an anonymous access, for instance.
+
+The same holds true for all "Seven Seas"-user other than "Horatio Nelson". The latter is permitted to do so:
+{code:none}
+$ ldapmodify -h zanzibar -p 10389 -D "cn=William Bush,ou=people,o=sevenSeas" -w pass \\
+    -a -f captain_hook.ldif
+adding new entry cn=James Hook,ou=people,o=sevenSeas
+ldap_add: Insufficient access
+ldap_add: additional info: failed to add entry cn=James Hook,ou=people,o=sevenSeas: null
+
+$ ldapmodify -h zanzibar -p 10389 -D "cn=Horatio Nelson,ou=people,o=sevenSeas" -w pass \\
+    -a -f captain_hook.ldif
+adding new entry cn=James Hook,ou=people,o=sevenSeas
+$
+{code}
+
+Afterwards a new entry is successfully created within the "Seven Seas" partition by user "Horatio Nelson". The '+' sign in the attributes list of the _ldapsearch_ command causes ApacheDS to return the operational attributes, which demonstrate this.
+
+{code:none}
+$ ldapsearch -h zanzibar -p 10389 -b "o=sevenSeas" -s sub "(cn=James Hook)" +
+version: 1
+dn: cn=James Hook,ou=people,o=sevenSeas
+accessControlSubentries: cn=sevenSeasAuthorizationRequirementsACISubentry,o=sevenSeas
+creatorsName: cn=Horatio Nelson,ou=people,o=sevenSeas
+createTimestamp: 20071228172613Z
+{code}
+
+h5. Modifying an entry
+
+As a further example which tries to write to the directory, we add a new value to the description attribute of the freshly created entry for Captain Hook. With a change entry in an LDIF file, it looks like this (file [^captain_hook_modify.ldif]):
+
+{code:none}
+# File captain_hook_modify.ldif
+dn: cn=James Hook,ou=people,o=sevenSeas
+changetype: modify
+add: description
+description: Wears an iron hook in place of his right hand
+-
+{code}
+
+Performing the modification with the _ldapmodify_ command line tool again fails for users other than "Horation Nelson" (who is allowed to due to the authorization configuration) and "uid=admin,ou=system".
+
+{code:none}
+$ ldapmodify -h zanzibar -p 10389 -f captain_hook_modify.ldif
+modifying entry cn=James Hook,ou=people,o=sevenSeas
+ldap_modify: Insufficient access
+ldap_modify: additional info: failed to modify entry cn=James Hook,ou=people,o=sevenSeas: null
+
+$ ldapmodify -h zanzibar -p 10389 -D "cn=William Bush,ou=people,o=sevenSeas" -w pass \\ 
+    -f captain_hook_modify.ldif
+modifying entry cn=James Hook,ou=people,o=sevenSeas
+ldap_modify: Insufficient access
+ldap_modify: additional info: failed to modify entry cn=James Hook,ou=people,o=s
+evenSeas: null
+
+$ ldapmodify -h zanzibar -p 10389 -D "cn=Horatio Nelson,ou=people,o=sevenSeas" -w pass \\
+    -f captain_hook_modify.ldif
+modifying entry cn=James Hook,ou=people,o=sevenSeas
+
+{code}
+
+h5. Deleting an entry
+
+Now it is finale time. A demonstration on how to delete the villain's entry from the directory. With an LDIF file ([^captain_hook_delete.ldif]) with an appropriate change entry, this can easily be accomplished, if the bind user is allowed to do so. 
+
+{code:none}
+# File captain_hook_delete.ldif
+dn: cn=James Hook,ou=people,o=sevenSeas
+changetype: delete
+{code}
+
+Applying this file with the help of _ldapmodify_ results in a behavior comparable to the modification. Anonymous or "normal" users (like "William Bush") are not permitted to delete Captain Hook's entry. The user "Horatio Nelson", our directory manager for "Seven Seas", is:
+
+{code:none}
+$ ldapmodify -h zanzibar -p 10389 -f captain_hook_delete.ldif
+deleting entry cn=James Hook,ou=people,o=sevenSeas
+ldap_delete: Insufficient access
+ldap_delete: additional info: failed to delete entry cn=James Hook,ou=people,o=sevenSeas: null
+
+$ ldapmodify -h zanzibar -p 10389 -D "cn=William Bush,ou=people,o=sevenSeas" -w pass \\
+    -f captain_hook_delete.ldif
+deleting entry cn=James Hook,ou=people,o=sevenSeas
+ldap_delete: Insufficient access
+ldap_delete: additional info: failed to delete entry cn=James Hook,ou=people,o=sevenSeas: null
+
+$ ldapmodify -h zanzibar -p 10389 -D "cn=Horatio Nelson,ou=people,o=sevenSeas" -w pass \\
+    -f captain_hook_delete.ldif
+deleting entry cn=James Hook,ou=people,o=sevenSeas
+$
+{code}
+
+The entry "cn=James Hook,ou=people,o=sevenSeas" has been successfully deleted from the partition. Our little demonstration on how the ACI subsystem with a realistic configuration behaves ends here. Learn more about it in the Advanced User's Guide.
+
+{anchor:Resources_1}
+
+h3. Resources
+
+* [Practices in Directory Groups|http://middleware.internet2.edu/dir/groups/docs/internet2-mace-dir-groups-best-practices-200210.htm] describes how to use groups within LDAP directories. Highly recommended.
+* [RFC 2849|http://www.faqs.org/rfcs/rfc2849.html] The LDAP Data Interchange Format (LDIF) is used extensively in this section
+* The ApacheDS v2.0 Advanced User's Guide will provide a detailed authorization chapter
+

Added: directory/documentation/apacheds-manuals/trunk/src/basic-user-guide-confluence/basic-security/howto_enable_ssl.confluence
URL: http://svn.apache.org/viewvc/directory/documentation/apacheds-manuals/trunk/src/basic-user-guide-confluence/basic-security/howto_enable_ssl.confluence?rev=1212137&view=auto
==============================================================================
--- directory/documentation/apacheds-manuals/trunk/src/basic-user-guide-confluence/basic-security/howto_enable_ssl.confluence (added)
+++ directory/documentation/apacheds-manuals/trunk/src/basic-user-guide-confluence/basic-security/howto_enable_ssl.confluence Thu Dec  8 21:35:32 2011
@@ -0,0 +1,262 @@
+h2. How to enable SSL
+
+This section describes the transport layer security options for LDAP, and especially how to enable LDAPS on ApacheDS.
+
+{toc:type=list|minLevel=2|maxLevel=2}
+
+h3. Transport layer security and LDAP
+
+Several requirements related to security can be easily accomplished with the help of *SSL* technology (Secure Socket Layer) or its standardized successor *TLS* (Transport Layer Security, RFC 2246). Among these are the protection of data against eavesdropping and modification, when on transit between client and server (data integrity), and the authentication of a server toward a client with the help of a certificate.
+
+There are two approaches to utilize these technologies in the LDAP world. 
+ 
+# ldaps (LDAP over SSL/TLS, port 636)
+# StartTLS (extended operation) 
+
+The first option is comparable to HTTPS and inserts an SSL/TLS layer between the TCP/IP protocol and LDAP. Establishing a connection like this is normally provided via a different server port (port 636 is common, it is a well-known port, like port 389 is for LDAP). In URIs the schema "ldaps" is specified  (for instance _ldaps://zanzibar:636/_) instead of "ldap". It is possible to write programs which switch between ldap and ldaps without changes in the source, if the connection data is configured external.
+
+In the second option a client establishes at first a "normal" LDAP connection. With a special request (extended operation StartTLS) it tries to switch to secure communication afterwards. It is not necessary to change the port for this, the communication continues on the established connection. The client may go back to the original connection state ("TLS Closure Alert"), in doing so protecting only selected parts of the communication.
+
+Both ways to utilize SSL/TLS within LDAP require the configuration of the server with an appropriate certificate.
+
+h3. Server configuration
+
+ApacheDS 2.0 currently only supports the first option (ldaps) and only if it runs with JDK 1.5 or above. The feature is disabled by default, you need to configure it. There are some steps to follow in order to obtain a SSL enabled server.
+
+Of capital importance: You need a *certificate* for your server. A certificate is signed public key (signed normally by a third party, a certificate authority, CA).
+
+There are different options
+- either you buy a certificate from a Certificate Authority (like Verisign, etc.), or you obtain one from your enterprise CA, if available
+- or you ask for a free certificate from [CACERT organisation|http://www.cacert.org/|www.cacert.org]
+- or you create your own certificate, self-signed or signed by your private CA, which will not be trusted.
+
+We will do it the last way (self-signed), primarily because it's easy and fast (you won't have to pay nor to wait to obtain your certificate)
+
+h4. Key creation
+
+First it is necessary to create a key pair (public/private key) for your server, _zanzibar_ in our case.  One option is to use the JDK tool _keytool_ for this task. In the following example, we use these options
+
+|| Option || value || Description ||
+| -genkey | | command to generate a key pair |
+| -keyalg | "RSA" | algorithm to be used to generate the key pair,  in our case, default is "DSA" |
+| -dname | "cn=zanzibar, ou=ApacheDS, o=ASF, c=US" | the X.500 Distinguished Name to be associated with alias, used as the issuer and subject fields in the self-signed certificate |
+| -alias | zanzibar | name to refer the entry within the keystore  |
+| -keystore | zanzibar.ks |  keystore file location |
+| -storepass | secret |  password used to protect the integrity of the keystore |
+| -validity | 730 | number of days for which the certificate should be considered valid, default is 90 |
+
+Learn more about keytool at the [manpage|http://java.sun.com/j2se/1.5.0/docs/tooldocs/solaris/keytool.html|java.sun.com].
+
+{noformat}
+$ keytool -genkey -keyalg "RSA" -dname "cn=zanzibar, ou=ApacheDS, o=ASF, c=US" \\
+    -alias zanzibar -keystore zanzibar.ks -storepass secret -validity 730
+Enter key password for <zanzibar>
+        (RETURN if same as keystore password):
+$ -bash-3.00$ ls -l
+total 4
+-rw-r--r--   1 szoerner staff       1275 Dec 22 12:23 zanzibar.ks
+$ keytool -list -keystore zanzibar.ks
+Enter keystore password:  secret
+
+Keystore type: jks
+Keystore provider: SUN
+
+Your keystore contains 1 entry
+
+zanzibar, Dec 22, 2007, keyEntry,
+Certificate fingerprint (MD5): 30:15:82:C2:C0:CE:FD:8C:3C:41:48:FF:93:EB:93:BC
+$
+{noformat}
+
+Another option is to use graphical tools for key creation like [Portecle|http://portecle.sourceforge.net/|portecle.sourceforge.net], which is basically a user-friendly front-end for keytool with comparable functionality. For a first impression see a screen shot below.
+
+!images/portecle_with_keystore.png!
+
+h4. Configuring ApacheDS
+
+Enabling SSL in Apache Directory Server and using the key pair created as above is quite easy. Simply put the keystore file in the _conf_ directory of your ApacheDS instance, and enable ldaps. Here is the fragment from _server.xml_ on how to do so. Note that there are two ldapServer elements within the default _server.xml_ file. Pick the one with id="ldapsServer".
+
+{code:xml}
+...
+  <ldapServer id="ldapsServer"
+     enabled="true"
+     ipPort="636"
+     
+     enableLdaps="true"
+     ldapsCertificateFile="C:/Programme/Apache Directory Server/instances/default/conf/zanzibar.ks"
+     ldapsCertificatePassword="secret"
+  >
+...
+{code}
+
+The following attributes were used
+
+|| Property|| default value || Description ||
+| enabled | false | sets if this configuration is enabled or not |
+| ipPort | 636 | LDAPS TCP/IP port number to listen to |
+| enableLdaps | false | sets if ldaps is enabled or not |
+| ldapsCertificateFile | server-work/certificates/server.cert | path of the X509 (or JKS) certificate file for LDAPS |
+| ldapsCertificatePassword | changeit| password which is used to load the LDAPS certificate file |
+
+After modification of the _server.xml_, the server has to be restarted in order to take effect.
+
+h3. Verification, Clients
+
+After restarting the server, you should have a server offering both ldap and ldaps. How to verify whether it works?
+
+h4. Using Apache Directory Studio to connect
+
+Apache Directory Studio happily supports ldaps connections. Enter the connection data (hostname and port) and select "Use SSL encryption" from the dropdown, if you create or modify a connection:
+
+!images/studio_ssl.png!
+
+Afterwards the connection behaves like LDAP does. No difference in functionality, but the transmission is secured by SSL. 
+
+Because our self-signed certificate is not trustworthy, many tools will present a warning (Studio currently does not). You will likely be able to view the certificate, and decide to continue (accepting the certificate always or this session only), like with web browsers.
+
+h4. Other clients, Java programs using JNDI
+
+If you use other graphical clients, the behavior will be comparable. Sometimes clients don't allow to connect to a server, if the certificate is not trustworthy. This is for instance by default the case for Java clients using JNDI.  
+
+The following simple Java program tries to connect via JNDI/JSSE (Java Secure Socket Extension) and LDAPS to _ldaps://zanzibar:10636_
+
+{code:java}
+import java.util.Hashtable;
+import javax.naming.*;
+import javax.naming.directory.*;
+
+public class ConnectWithLdaps {
+
+    public static void main(String[] args) throws NamingException {
+
+        Hashtable env = new Hashtable();
+
+        // Simple bind
+        env.put(Context.SECURITY_AUTHENTICATION, "simple");
+        env.put(Context.SECURITY_PRINCIPAL,
+                "cn=Horatio Hornblower,ou=people,o=sevenSeas");
+        env.put(Context.SECURITY_CREDENTIALS, "pass");
+
+        env.put(Context.INITIAL_CONTEXT_FACTORY,
+                "com.sun.jndi.ldap.LdapCtxFactory");
+        env.put(Context.PROVIDER_URL, "ldaps://zanzibar:636/o=sevenSeas");
+
+        DirContext ctx = new InitialDirContext(env);
+        NamingEnumeration enm = ctx.list("");
+        while (enm.hasMore()) {
+            System.out.println(enm.next());
+        }
+        ctx.close();
+    }
+}
+{code}
+
+It causes a _CommunicationException_, if the certificate is not trusted:
+
+{noformat}
+$ java ConnectWithLdaps
+Exception in thread "main" javax.naming.CommunicationException: 
+  simple bind failed: zanzibar:636 
+      [Root exception is javax.net.ssl.SSLHandshakeException: 
+       sun.security.validator.ValidatorException: PKIX path building failed:    
+       sun.security.provider.certpath.SunCertPathBuilderException: 
+       unable to find valid certification path to requested target]
+at com.sun.jndi.ldap.LdapClient.authenticate(Unknown Source)
+...
+{noformat}
+
+In order to make the client trust our server, one option is to share a self signed certificate.
+So we export the certificate (DER format) using keytool like this:
+
+{noformat}
+$ keytool -export -keystore zanzibar.ks -alias zanzibar -file zanzibar.cer
+Enter keystore password:  secret
+Certificate stored in file <zanzibar.cer>
+$ ls -l
+total 6
+-rw-r--r--   1 szoerner staff        504 Dec 22 13:34 zanzibar.cer
+-rw-r--r--   1 szoerner staff       1275 Dec 22 12:23 zanzibar.ks
+$ 
+{noformat}
+
+Please note that you don't want to share the server keystore file itself with arbitrary clients, because it holds the private key. Instead we create a separate keystore _trusted.ks_ with the help of _keytool_. We import the certificate _zanzibar.cer_ like this:
+
+{noformat}
+$ keytool -import -file zanzibar.cer -alias zanzibar -keystore trusted.ks -storepass secret
+Owner: CN=zanzibar, OU=ApacheDS, O=ASF, C=US
+Issuer: CN=zanzibar, OU=ApacheDS, O=ASF, C=US
+Serial number: 476d4825
+Valid from: Sat Dec 22 12:23:49 EST 2007 until: Mon Dec 21 12:23:49 EST 2009
+Certificate fingerprints:
+         MD5:  30:15:82:C2:C0:CE:FD:8C:3C:41:48:FF:93:EB:93:BC
+         SHA1: D2:7F:C7:48:3D:B6:F7:04:97:27:72:1F:71:75:27:AF:B2:D4:6A:3C
+Trust this certificate? [no]:  yes
+Certificate was added to keystore
+-bash-3.00$ keytool -list -keystore trusted.ks -storepass secret
+
+Keystore type: jks
+Keystore provider: SUN
+
+Your keystore contains 1 entry
+
+zanzibar, Dec 22, 2007, trustedCertEntry,
+Certificate fingerprint (MD5): 30:15:82:C2:C0:CE:FD:8C:3C:41:48:FF:93:EB:93:BC
+$
+{noformat}
+
+Instead of using the command line version of keytool, it is also possible to perform the certificate export and import operations with Portecle or any other graphical frontend. This is for instance how the _trusted.ks_ files with the imported certificate looks like in Portecle.  
+
+!images/portecle_with_certificate.png!
+
+Clients may use this keystore in order to connect to the server. Therefore they can configure _trusted.ks_ as the trusted store via the environment like this:
+
+{noformat}
+$ java -Djavax.net.ssl.trustStore=trusted.ks ConnectWithLdaps
+ou=people: javax.naming.directory.DirContext
+ou=groups: javax.naming.directory.DirContext 
+{noformat}
+
+Another option would be to import the certificate in the default keystore of the JRE installation (within $JAVA_HOME/jre/lib/security). For a test certificate this proceeding is not appropriate.
+
+h5. Troubleshooting
+
+In practice connection establishment with LDAP over SSL may lead to various problems. In order to eliminate the errors it is helpful to see communication-specific debug information. The system property _javax.net.debug_ is available for this task. The value "ssl" provides information about the certificates in the used key store, the server certificate, and the steps during establishing of the SSL connection (handshake):
+
+{noformat}
+$ java -Djavax.net.ssl.trustStore=trusted.ks -Djavax.net.debug=ssl ConnectWithLdaps
+setting up default SSLSocketFactory
+use default SunJSSE impl class: com.sun.net.ssl.internal.ssl.SSLSocketFactoryImpl
+class com.sun.net.ssl.internal.ssl.SSLSocketFactoryImpl is loaded
+keyStore is :
+keyStore type is : jks
+keyStore provider is :
+init keystore
+init keymanager of type SunX509
+trustStore is: trusted.ks
+trustStore type is : jks
+trustStore provider is :
+init truststore
+adding as trusted cert:
+  Subject: CN=zanzibar, OU=ApacheDS, O=ASF, C=US
+  Issuer:  CN=zanzibar, OU=ApacheDS, O=ASF, C=US
+  Algorithm: RSA; Serial number: 0x476d4825
+  Valid from Sat Dec 22 18:23:49 CET 2007 until Mon Dec 21 18:23:49 CET 2009
+
+init context
+trigger seeding of SecureRandom
+done seeding SecureRandom
+instantiated an instance of class com.sun.net.ssl.internal.ssl.SSLSocketFactoryImpl
+%% No cached client session
+*** ClientHello, TLSv1
+...
+{noformat}
+
+You should be able to determine any SSL-related configuration problem with the help of this log.
+
+{anchor:Resources_2}
+
+h3. Resources
+
+* [Java Secure Socket Extension (JSSE)|http://java.sun.com/products/jsse/|java.sun.com]
+* [Portecle|http://portecle.sourceforge.net|portecle.sourceforge.net] a free UI application for creating, managing and examining keystores
+* [SSL 3.0 Specification (Netscape)|http://wp.netscape.com/eng/ssl3/|wp.netscape.com]

Modified: directory/documentation/apacheds-manuals/trunk/src/basic-user-guide-confluence/book.txt
URL: http://svn.apache.org/viewvc/directory/documentation/apacheds-manuals/trunk/src/basic-user-guide-confluence/book.txt?rev=1212137&r1=1212136&r2=1212137&view=diff
==============================================================================
--- directory/documentation/apacheds-manuals/trunk/src/basic-user-guide-confluence/book.txt (original)
+++ directory/documentation/apacheds-manuals/trunk/src/basic-user-guide-confluence/book.txt Thu Dec  8 21:35:32 2011
@@ -21,7 +21,10 @@ managing-data-within-your-directory/cust
 managing-data-within-your-directory/ldap_operations.confluence
 managing-data-within-your-directory/ldap_operations/ldap_operations_overview.confluence
 managing-data-within-your-directory/replication.confluence
-
+basic-security.confluence
+basic-security/authentication_options.confluence
+basic-security/basic_authorization.confluence
+basic-security/howto_enable_ssl.confluence
 
 #managing-data-within-your-directory
 #basic-security

Modified: directory/documentation/apacheds-manuals/trunk/src/basic-user-guide-confluence/images/portecle_with_certificate.png
URL: http://svn.apache.org/viewvc/directory/documentation/apacheds-manuals/trunk/src/basic-user-guide-confluence/images/portecle_with_certificate.png?rev=1212137&r1=1212136&r2=1212137&view=diff
==============================================================================
Binary files - no diff available.

Modified: directory/documentation/apacheds-manuals/trunk/src/basic-user-guide-confluence/images/portecle_with_keystore.png
URL: http://svn.apache.org/viewvc/directory/documentation/apacheds-manuals/trunk/src/basic-user-guide-confluence/images/portecle_with_keystore.png?rev=1212137&r1=1212136&r2=1212137&view=diff
==============================================================================
Binary files - no diff available.

Added: directory/documentation/apacheds-manuals/trunk/src/basic-user-guide-confluence/images/studio_password_editor.png
URL: http://svn.apache.org/viewvc/directory/documentation/apacheds-manuals/trunk/src/basic-user-guide-confluence/images/studio_password_editor.png?rev=1212137&view=auto
==============================================================================
Binary file - no diff available.

Propchange: directory/documentation/apacheds-manuals/trunk/src/basic-user-guide-confluence/images/studio_password_editor.png
------------------------------------------------------------------------------
    svn:mime-type = image/png

Added: directory/documentation/apacheds-manuals/trunk/src/basic-user-guide-confluence/images/studio_props_auth_anonymous.png
URL: http://svn.apache.org/viewvc/directory/documentation/apacheds-manuals/trunk/src/basic-user-guide-confluence/images/studio_props_auth_anonymous.png?rev=1212137&view=auto
==============================================================================
Binary file - no diff available.

Propchange: directory/documentation/apacheds-manuals/trunk/src/basic-user-guide-confluence/images/studio_props_auth_anonymous.png
------------------------------------------------------------------------------
    svn:mime-type = image/png

Added: directory/documentation/apacheds-manuals/trunk/src/basic-user-guide-confluence/images/studio_props_auth_digestmd5.png
URL: http://svn.apache.org/viewvc/directory/documentation/apacheds-manuals/trunk/src/basic-user-guide-confluence/images/studio_props_auth_digestmd5.png?rev=1212137&view=auto
==============================================================================
Binary file - no diff available.

Propchange: directory/documentation/apacheds-manuals/trunk/src/basic-user-guide-confluence/images/studio_props_auth_digestmd5.png
------------------------------------------------------------------------------
    svn:mime-type = image/png

Added: directory/documentation/apacheds-manuals/trunk/src/basic-user-guide-confluence/images/studio_props_auth_simplebind.png
URL: http://svn.apache.org/viewvc/directory/documentation/apacheds-manuals/trunk/src/basic-user-guide-confluence/images/studio_props_auth_simplebind.png?rev=1212137&view=auto
==============================================================================
Binary file - no diff available.

Propchange: directory/documentation/apacheds-manuals/trunk/src/basic-user-guide-confluence/images/studio_props_auth_simplebind.png
------------------------------------------------------------------------------
    svn:mime-type = image/png

Added: directory/documentation/apacheds-manuals/trunk/src/basic-user-guide-confluence/images/studio_simplebind_failed.png
URL: http://svn.apache.org/viewvc/directory/documentation/apacheds-manuals/trunk/src/basic-user-guide-confluence/images/studio_simplebind_failed.png?rev=1212137&view=auto
==============================================================================
Binary file - no diff available.

Propchange: directory/documentation/apacheds-manuals/trunk/src/basic-user-guide-confluence/images/studio_simplebind_failed.png
------------------------------------------------------------------------------
    svn:mime-type = image/png

Modified: directory/documentation/apacheds-manuals/trunk/src/basic-user-guide-confluence/images/studio_ssl.png
URL: http://svn.apache.org/viewvc/directory/documentation/apacheds-manuals/trunk/src/basic-user-guide-confluence/images/studio_ssl.png?rev=1212137&r1=1212136&r2=1212137&view=diff
==============================================================================
Binary files - no diff available.