\documentclass{article}
\usepackage{ulem}
\pagestyle{headings}
\begin{document}
\tableofcontents

\part{ LDAP HowTo}
Written by Ben Chavet (ben [at] horde [dot] org)


\noindent\rule{\textwidth}{1pt}
This document is intended to help administrators configure various parts of horde to use an \textbf{existing} LDAP directory.  Please feel free to fill in any gaps or to clarify any existing information presented here.


\noindent\rule{\textwidth}{1pt}
\section{ Document Standards}
In this document, we assume the following, please adjust accordingly for your LDAP directory

\begin{itemize}
\item{The directory is already populated with posix user account information.}
\item{User information is stored in \texttt{ou=Users,dc=example,dc=com}.}
\item{\texttt{cn=horde,ou=DSA,dc=example,dc=com} is used by horde to bind to the LDAP directory.}
\item{The LDAP directory is secured, with no anonymous binding.}
\end{itemize}

\noindent\rule{\textwidth}{1pt}
\section{ Authenticating with LDAP}
\subsection{ Horde Setup}
The Horde authentication setup should look something like the following:

Image: NI

\begin{itemize}
\item{\textbf{The hostname of the LDAP server} - This is the address of your LDAP server.  If it is running on the same machine as horde, then \textsl{localhost} is fine here.  Otherwise enter the hostname or IP address of the server.  If you have a master and one or more slave LDAP servers, you can provide failover here by entering all of your LDAP servers separated by a space.  For ldaps, use ldaps://<hostname> in this field.}
\begin{itemize}
\item{\textbf{The base DN for the LDAP server} - This is the subtree that horde will search through to find user information.}
\item{\textbf{The DN used to bind to the LDAP server} - Because our LDAP directory does not allow anonymous binding, we must provide the binding account here.  If your LDAP directory allows anonymous binding, this can be left blank.}
\item{\textbf{The password used to bind to the LDAP server} - The password associated with the binding account.  Leave this blank if binding anonymously.}
\item{\textbf{LDAP Protocol Version} - This should almost always be \textsl{LDAPv3}.}
\item{\textbf{The username search key} - This is the field that stores the username.}
\item{\textbf{How to specify a filter for the user lists} - Unless you have to use some fancy filters to find users, \textsl{One or more objectclass filters} should work fine here.}
\item{\textbf{The objectclass filter used to search for users. Can be a single objectclass or a list} - This is simply a list of objectClass values that represent valid users.  We are piggybacking on an existing LDAP directory used to authenticate users on a POSIX system, so we know that a \textsl{posixAccount} object is a valid user account. Note: If the objectclass for your users is "shadowAccount", this should NOT be "objectclass=shadowAccount" just "shadowAccount"}
\end{itemize}
\end{itemize}
\subsection{ Directory Permissions}
These are the \textbf{minimum} LDAP directory permissions needed by horde to authenticate against LDAP

\begin{verbatim}

access to dn.children="ou=Users,dc=example,dc=com"
        attrs=entry,objectClass,uid
        by dn="cn=horde,ou=DSA,dc=example,dc=com" read
        by self read
        by * none

access to dn.children="ou=Users,dc=example,dc=com"
        attrs=userPassword
        by self write
        by anonymous auth
        by * none
\end{verbatim}


\noindent\rule{\textwidth}{1pt}
\section{ Managing Posix Accounts}
With a little tweaking, Horde can be used to do basic user management for a Posix system, such as adding and removing users.

\subsection{ NextFreeUnixId Object}
We have to have a place to keep track of the next user id number.  If you are already using LDAP for user management, chances are, you already have an object doing this, and you can skip this section.  If you do not, we need to create one with the following \textsl{ldif} file:

\begin{verbatim}

dn: cn=NextFreeUnixId,dc=example,dc=com
homeDirectory: /dev/null
objectClass: inetOrgPerson
objectClass: posixAccount
objectClass: top
uid: NextFreeUnixId
gidNumber: 1000
uidNumber: 1000
sn: NextFreeUnixId
cn: NextFreeUnixId
\end{verbatim}

And add this object to the directory:

\begin{verbatim}

ldapadd -x -h localhost -D "cn=root,dc=example,dc=com" -f filename.ldif -W
\end{verbatim}

\subsection{ Configure AuthLDAP Hook}
\begin{verbatim}

if (!function_exists('_horde_hook_authldap')) {
    function _horde_hook_authldap($userID, $credentials = null)
    {
        $entry['dn'] = 'uid=' . $userID . ',ou=horde,dc=example,dc=com';
        if (isset($credentials) && isset($credentials['user_fullname'])) {
            $entry['cn'] = $credentials['user_fullname'];
        } else {
            $entry['cn'] = $userID;
        }
        $entry['sn'] = $userID;
        $entry['objectclass'][0] = 'top';
        $entry['objectclass'][1] = 'posixAccount';
        $entry['objectclass'][2] = 'shadowAccount';
        $entry['objectclass'][3] = 'inetOrgPerson';
        $entry['uid'] = $userID;
        $entry['homeDirectory'] = '/home/' . $userID;
        $entry['gidNumber'] = 100;

        // get the next available uid and increment it if we're adding a user
        if (isset($credentials)) {
            $ds = @ldap_connect($GLOBALS['conf']['auth']['params']['hostspec']);
            @ldap_set_option($ds, LDAP_OPT_PROTOCOL_VERSION, $GLOBALS['conf']['auth']['params']['version']);
            @ldap_bind($ds, $GLOBALS['conf']['auth']['params']['binddn'], $GLOBALS['conf']['auth']['params']['password']);
            $searchResults = @ldap_search($ds, 'dc=example,dc=com', 'cn=NextFreeUnixId');
            $information = @ldap_get_entries($ds, $searchResults);
            ldap_modify($ds, 'cn=NextFreeUnixId,dc=example,dc=com', array('uidnumber' => $information[0]['uidnumber'][0] + 1));
            @ldap_close($ds);
            $entry['uidNumber'] = $information[0]['uidnumber'][0];
        }

        // need to check for new users (password) and edited users (user_pass_2)
        if (isset($credentials) && isset($credentials['password'])) {
            $entry['userPassword'] =  '{MD5}' . base64_encode(mHash(MHASH_MD5, $credentials['password']));
        } else if (isset($credentials) && isset($credentials['user_pass_2'])) {
            $entry['userPassword'] =  '{MD5}' . base64_encode(mHash(MHASH_MD5, $credentials['user_pass_2']));
        }
        return $entry;
    }
}
\end{verbatim}

Another example for this code using a ou to store the next available uid information can be found at:  http://heinous.org/wiki/Horde\_Notes\#Authentication\footnote{http://heinous.org/wiki/Horde\_Notes\#Authentication}

\subsection{ Directory Permissions}
If you are using Horde to manage your user accounts, the horde account needs more priveleges in order to make the proper changes.

\begin{verbatim}

access to dn.base="cn=NextFreeUnixId,dc=example,dc=com"
        by dn="cn=horde,ou=DSA,dc=example,dc=com" write
        by * none

access to dn.children="ou=Users,dc=example,dc=com"
        attrs=entry,objectClass,uid
        by dn="cn=horde,ou=DSA,dc=example,dc=com" write
        by self read
        by * none

access to dn.children="ou=Users,dc=example,dc=com"
        attrs=userPassword
        by dn="cn=horde,ou=DSA,dc=example,dc=com" write
        by self write
        by anonymous auth
        by * none
\end{verbatim}


\noindent\rule{\textwidth}{1pt}
\section{ Storing Preferences with LDAP}
\subsection{ Horde Setup}
Storing Horde preferences in the LDAP directory adds a large number of attribute entries to every user DN.  If this is something you do not want, you should look into using some other preference backend.

To use LDAP to store Horde preferences, set the preference system to use LDAP as its backend.  The field values here are very simular to the Horde configuration.

Image: NI

\begin{itemize}
\item{\textbf{The hostname of the LDAP server} - This is the address of your LDAP server.  If you have a master and one or more slave LDAP servers, you can provide failover here by entering all of your LDAP servers separated by a space.  For ldaps, use ldaps://<hostname> in this field.}
\begin{itemize}
\item{\textbf{The port of the LDAP server} - This is the port that your LDAP server is listening on.  Most commonly, this will be 389.}
\item{\textbf{LDAP Protocol Version} - This should almost always be \textsl{LDAPv3}.}
\item{\textbf{The base DN for the LDAP server} - This is the subtree that horde will search through to find user preference information.}
\item{\textbf{The DN of the root (administrative) account to bind for write operations} - This is not actually asking for the LDAP root account, this is just the DN that horde uses to bind to the LDAP directory.  This account should have write priveleges to the preference fields, as shown below.}
\item{\textbf{The password of the root DN for bind authentication} - The password associated with the binding account.}
\item{\textbf{The username search key} - This is the field that stores the username.}
\end{itemize}
\end{itemize}
\subsection{ Directory Permissions}
These are the suggested LDAP directory permissions needed by Horde to store user preferences.  The explicit write permission for the Horde DN can be omitted if the LDAP driver is configured to bind to LDAP as the logged-in user.  However it remains necessary if you use a "root" DN.  The "attrs=@hordePerson" syntax means this rule applies to all attributes included in the "hordePerson" objectClass.

\begin{verbatim}

# Access to Horde attributes
access to attrs="@hordePerson"
        by dn="cn=horde,ou=DSA,dc=example,dc=com" write
        by self write
        by users none
        by * none
\end{verbatim}


\noindent\rule{\textwidth}{1pt}
\section{ LDAP Based Address Book}
\subsection{ Turba Setup}
Patch \texttt{/etc/openldap/schema/core.schema} with \texttt{/horde/turba/scripts/ldap/core.schema.patch}

Turba ships with an example LDAP address book, so we will use that here as our base.  This example assumes that we are providing an address book containing all of the users who have access to this horde installation.

\begin{verbatim}

$cfgSources['localldap'] = array(
    'title' => _("Shared Directory"),
    'type' => 'ldap',
    'params' => array(
        'server' => 'localhost',
        'port' => 389,
        'root' => 'ou=Users,dc=example,dc=com',
        'bind_dn' => 'cn=horde,ou=Users,dc=example,dc=com',
        'bind_password' => '********',
        'sizelimit' => 200,
        'dn' => array('cn'),
        'objectclass' => array('top',
                               'person',
                               'organizationalPerson',
                               'inetOrgPerson',
                               'turbaContact',
                         ),
        'charset' => 'iso-8859-1',
        'checkrequired' => false,
        'version' => 3
    ),
    'map' => array(
        '__key' => 'dn',
        'name' => 'cn',
        'email' => 'mail',
        'workPhone' => 'telephonenumber',
        'cellPhone' => 'mobile',
        'office' => 'roomNumber',
        'employeeType' => 'employeeType',
        'pgpPublicKey' => 'userCertificate',
        'freebusyUrl' => 'calFBURL',
    ),
    'search' => array(
        'name',
        'email',
        'homePhone',
        'workPhone',
        'cellPhone',
        'homeAddress'
    ),
    'public' => true,
    'readonly' => true,
    'admin' => array(),
    'export' => true
);
\end{verbatim}

The amount of information you can store is not by any means limited by what we have configured here.  Any number of LDAP fields can be added to the \texttt{'map'} array.

\subsection{ Directory Configuration}
In order to use the \textsl{calFBURL} field, we have to include the rfc2739 schema in our LDAP configuration file.

\begin{itemize}
\item{Copy \texttt{horde/turba/scripts/ldap/rfc2739.schema} to your server's schema directory.  This is commonly \texttt{/etc/openldap/schema/}.}
\begin{itemize}
\item{Add the following to your LDAP configuration file (\texttt{/etc/openldap/slapd.conf})}
\end{itemize}
\end{itemize}
\begin{verbatim}

include /etc/openldap/schema/rfc2739.schema
\end{verbatim}

\subsection{ Directory Permissions}
These are the \textbf{minimum} LDAP permissions required for the address book we defined above.  If you included extra fields, be sure to add them here.

\begin{verbatim}

access to dn.children="ou=Users,dc=example,dc=com"
        attrs=entry,cn,objectClass,mail,telephoneNumber,mobile,roomNumber,employeeType,userCertificate,calFBURL,displayName
        by dn="cn=horde,ou=Users,dc=example,dc=com" read
        by self read
        by * none
\end{verbatim}


\noindent\rule{\textwidth}{1pt}
\section{ Pass LDAP credentials to IMP}
To pass credentials used to log in Horde to IMP, modify the file \texttt{./horde/imp/config/servers.php} (from your webroot). Here is the explanation given in the file :

\begin{quote}
hordeauth : \newline
If this parameter is present and true, then IMP will attempt to use the user's existing credentials (the username/password they used to log in to Horde) to log in to this source. If this  parameter is 'full', the username will be used unmodified; otherwise everything after and including the first @ in the username will be stripped off before attempting authentication.\newline
\end{quote}

\newline
Added by Jonathan Gibert (jokot3 [47] gmail [d°7] com). March 26, 2007.
\noindent\rule{\textwidth}{1pt}
\section{ See Also}
\begin{itemize}
\item{NewLDAPHowTo\footnote{http://example.com/index.php?page=NewLDAPHowTo}}
\item{LdapPref\footnote{http://example.com/index.php?page=LdapPref}}
\end{itemize}
\end{document}
