<h2>But de ce document</h2>
<p class="spip">Ce document me sert de mémo sur <strong class="spip">l’installation et l’utilisation d’OpenLDAP sous Debian</strong>. Je le diffuse en espérant qu’il puisse servir à d’autres personnes.</p>

<h2>Pré-requis</h2>
<p class="spip">Avoir installé une Debian de Base :
<br /></p>
<li> cf mon autre document sur ce sujet : <a href="http://www.coagul.org/article.php3?id_article=158" class="spip_out">http://www.coagul.org/article.php3 ?id_article=158</a>
<h2>Présentation rapide</h2>
<p class="spip">Une base <strong class="spip">LDAP</strong> est une base de données où les informations sont enregistrées de manière hiérarchique sous forme d’arbre et non sous forme tabulaire.</p>

<p class="spip">Une base LDAP est optimisée pour la lecture d’un nombre important de petits enregistrements et convient donc parfaitement pour stocker des annuaires ou des profils utilisateurs.</p>

<p class="spip">Le système LDAP utilise des <strong class="spip">schémas</strong> (/etc/ldap/schema) pour décrire des objets.</p>

<p class="spip">Chaque <strong class="spip">objet</strong> contient plusieurs <strong class="spip">attributs</strong> (obligatoire ou facultatifs). Et chaque objet peut <strong class="spip">hériter</strong> des attributs d’un autre objet. Exemple :
<br /></p>
<li> L’objet « person » a comme attributs : commonName, surname,..
<br /><li> L’objet fils « organizationalPerson » dérivé de l’objet « person » ajoute les attributs : title, PostalAddress,...
<p class="spip">Les objets et les attributs sont <strong class="spip">normalisés</strong> pour assurer les échanges entre les logiciels.</p>

<p class="spip">Il est possible de modifier un schéma en rajoutant des attributs à un objet (déconseillé) ou en créant un nouvel objet (mieux), mais le mieux est de faire valider cette modification par l’IANA.</p>

<p class="spip">Chaque donnée enregistrée dans la base est identifiée par son <strong class="spip">DN</strong> (Distinguished Name). Ce DN est comparable au chemin complet d’un fichier. Exemple : dc=mondomaine,dc=fr</p>

<p class="spip">Pour ajouter ou modifier des données dans la base, il est possible d’utiliser le format <strong class="spip">LDIF</strong>.</p>

<h2>Installation</h2>
<p class="spip">Paquets à installer :</p>

<p class="spip"><code class='spip_code' dir='ltr'># apt-get install ldap-server ldap-client</code></p>

<p class="spip">Ce qui installera en fait :</p>

<p class="spip"><code class='spip_code' dir='ltr'># apt-get install slapd ldap-utils</code></p>

<table class="spip">
<thead><tr class='row_first'><th scope='col'><strong class="spip">Écran (Titre en rouge) </strong></th><th scope='col'><strong class="spip">Message </strong></th><th scope='col'><strong class="spip">Réponse</strong></th></tr></thead>
<tbody>
<tr class="row_even"><td>Configuration de slapd</td><td>Enter your DNS domain </td><td><strong class="spip">mondomaine.com</strong></td></tr>
<tr class="row_odd"><td> </td><td>Enter the name of organisation</td><td><strong class="spip">mondomaine.com</strong></td></tr>
<tr class="row_even"><td> </td><td>Admin password</td><td><strong class="spip">xxxx</strong></td></tr>
<tr class="row_odd"><td> </td><td>Allow LDAPv2 protocol </td><td><strong class="spip">Oui *</strong></td></tr>
</tbody>
</table>
<p class="spip"><strong class="spip">* J’ai été obligé d’autoriser la norme v2 pour les clients Win 2000</strong>. Avec les clients Win NT, il n’y avait pas de problème.</p>

<h2>Configuration</h2>
<p class="spip">Le fichier de configuration est :</p>

<p class="spip"><code class='spip_code' dir='ltr'>/etc/ldap/slapd.conf</code></p>

<p class="spip">La ligne suivante permet d’autoriser l’utilisation de la norme V2 de LDAP.</p>

<p class="spip"><code class='spip_code' dir='ltr'>allow bind_v2</code></p>

<p class="spip">La ligne suivante donne la racine de la base LDAP :</p>

<p class="spip"><code class='spip_code' dir='ltr'>suffix "dc=mondomaine,dc=com"</code></p>

<p class="spip">La ligne suivante <strong class="spip">qu’il faut ajouter manuellement </strong>donne le login de l’administrateur (admin avec le rappel de la racine). Cette ligne et la suivante sont obligatoires pour avoir un accès root sur la base depuis un programme externe (ex : PHP)</p>

<p class="spip"><code class='spip_code' dir='ltr'>rootdn "cn=admin,dc=mondomaine ,dc=com"</code></p>

<p class="spip">La ligne suivante <strong class="spip">qu’il faut ajouter manuellement </strong>donne le mot de passe en clair :</p>

<p class="spip"><code class='spip_code' dir='ltr'>rootpw admin</code></p>

<p class="spip">Pour plus de sécurité, il est préférable de générer un mot de passe crypté en utilisant la commande :</p>

<div style='text-align: left;' class='spip_code' dir='ltr'><code># slappasswd<br />
New password:<br />
Re-enter new password:<br />
{SSHA}5y67xJ/t7esuGKUD7TQPcgykd8xiYMO2</code></div>
<p class="spip">Ensuite, il faut copier la chaîne cryptée à la place du mot de passe en clair</p>

<p class="spip">Paramétrage de l’accès en écriture de la base. Il faut indiquer le bon login et la racine de la base :</p>

<div style='text-align: left;' class='spip_code' dir='ltr'><code>access to attribute=userPassword<br />
        by dn="cn=admin,dc=test,dc=com" write<br />
        by anonymous auth<br />
        by self write<br />
        by * none</code></div>
<p class="spip">Paramétrage de l’accès en lecture seule de la base. Il faut indiquer le bon login et la racine de la base :</p>

<div style='text-align: left;' class='spip_code' dir='ltr'><code>access to *<br />
        by dn="cn=admin,dc=test,dc=com" write<br />
        by * read</code></div>
<h2>Démarrage du serveur (slapd)</h2>
<p class="spip">Le serveur slapd se démarre d’une manière classique avec la commande :</p>

<p class="spip"><code class='spip_code' dir='ltr'># /etc/init.d/slapd restart</code></p>

<h2>Le format de fichier LDIF</h2>
<p class="spip">Ce format de fichier est utilisé pour faire des imports / exports entre plusieurs bases ou pour modifier ou ajouter des données dans une base.</p>

<p class="spip">ATTENTION : Il est obligatoire de coder les données en UTF-8. Si lors de l’importation une erreur est rencontrée, celle-ci est abandonnée à l’endroit où elle en était.</p>

<h3>Fichier LDIF pour ajouter des enregistrements</h3>
<p class="spip">Voici la structure d’un fichier LDIF</p>

<div style='text-align: left;' class='spip_code' dir='ltr'><code>dn: <distinguished name <br />
    objectClass: <object class <br />
    objectClass: <object class <br />
    ... <br />
    <attribute type:<attribute value <br />
    <attribute type:<attribute value <br />
    ...</code></div>
<p class="spip">Voici un exemple de fichier LDIF (AjoutRacine.ldif) pour créer la racine de l’arbre LDAP :</p>

<div style='text-align: left;' class='spip_code' dir='ltr'><code>dn: dc=mondomaine,dc=com<br />
objectClass: dcObject<br />
objectClass: organization<br />
o: Votre Societe<br />
dc: mondomaine</code></div>
<p class="spip">Commande pour ajouter la racine :</p>

<p class="spip"><code class='spip_code' dir='ltr'>ldapadd -x -D "cn=admin,dc=mondomaine,dc=com" -w admin -f AjoutRacine.ldif</code></p>

<p class="spip">Voici un exemple de fichier LDIF (AjoutFiche.ldif) pour créer une nouvelle fiche :</p>

<div style='text-align: left;' class='spip_code' dir='ltr'><code>dn: cn=Tony GALMICHE,dc=mondomaine,dc=com<br />
objectClass: inetOrgPerson<br />
cn: Tony GALMICHE<br />
givenName: Tony<br />
sn: GALMICHE</code></div>
<p class="spip">Commande pour ajouter la fiche :</p>

<p class="spip"><code class='spip_code' dir='ltr'>ldapadd -x -D "cn=admin,dc=mondomaine,dc=com" -w admin -f AjoutFiche.ldif</code></p>

<h3>Fichier LDIF pour modifier des enregistrements</h3>
<p class="spip">Les commandes de modification ont la syntaxe suivante :</p>

<div style='text-align: left;' class='spip_code' dir='ltr'><code>   dn: distinguished name<br />
    changetype  {{TYPE}} <br />
    change operation identifier<br />
    list of attributes...<br />
    ...<br />
   -<br />
    change operation identifier<br />
    list of attributes<br />
    ...</code></div>
<p class="spip">TYPE peu avoir l’une des valeurs suivantes :
<br /></p>
<li> add (ajout d’une entrée), 
<br /><li> delete (suppression), 
<br /><li> modrdn (modification du RDN), 
<br /><li> modify (modification : add, replace, delete d’un attribut)
<p class="spip">Le fichier « ModifFiche.ldif » ci-dessous permet d’ajouter le numéro de téléphone :</p>

<div style='text-align: left;' class='spip_code' dir='ltr'><code>dn: cn=Tony GALMICHE,dc=mondomaine,dc=com<br />
changetype: modify<br />
add: telephonenumber<br />
telephonenumber: 03 80 97 98 99</code></div>
<p class="spip">Commande pour modifier la fiche :</p>

<p class="spip"><code class='spip_code' dir='ltr'>ldapadd -x -D "cn=admin,dc=mondomaine,dc=com" -w admin -f ModifFiche.ldif</code></p>

<h3>Fichier LDIF pour supprimer des enregistrements</h3>
<p class="spip">Le fichier « SuppFiche.ldif » ci-dessous permet de supprimer une fiche :</p>

<div style='text-align: left;' class='spip_code' dir='ltr'><code>dn: cn=Tony GALMICHE,dc=mondomaine,dc=com<br />
changetype: delete</code></div>
<p class="spip">Commande pour supprimer la fiche :</p>

<p class="spip"><code class='spip_code' dir='ltr'>ldapadd -x -D "cn=admin,dc=mondomaine,dc=com" -w admin -f SuppFiche.ldif</code></p>

<h2>Commande pour rechercher des enregistrements</h2>
<p class="spip">Rechercher les enregistrements contenant un attribut objectclass (donc tous) depuis la racine :</p>

<p class="spip"><code class='spip_code' dir='ltr'>ldapsearch -x -b "dc=mondomaine,dc=com" "objectclass=*"</code></p>

<p class="spip">Rechercher les enregistrements contenant un attribut cn dans la branche Eloyes :</p>

<p class="spip"><code class='spip_code' dir='ltr'>ldapsearch -x -b "o=Eloyes,dc=mondomaine,dc=com" "cn=*"</code></p>

<p class="spip">Rechercher les enregistrements donc l’attribut dc se termine par gray depuis la racine :</p>

<p class="spip"><code class='spip_code' dir='ltr'>ldapsearch -x -b "dc=mondomaine,dc=com" "dc=*gray"</code></p>

<h2>Sauvegarde et Restauration d’une base de données LDAP</h2>
<p class="spip">La commande suivante permet de générer un fichier .LDIF contenant la base complète :</p>

<p class="spip"><code class='spip_code' dir='ltr'>slapcat -l DumpLDAP.ldif -b "dc=mondomaine,dc=com"</code></p>

<h2>LDAP Browser</h2>
<p class="spip">LDAP Browser est un programme en Java, permettant de consulter et de modifier une base LDAP :
<br /></p>
<li> <a href="http://www-unix.mcs.anl.gov/~gawor/ldap/" class="spip_out">http://www-unix.mcs.anl.gov/ gawor/ldap/</a>
<h2>Exemples de scripts PHP</h2>
<h3>Exemple de script PHP pour se connecter au serveur LDAP</h3>
<div style='text-align: left;' class='spip_code' dir='ltr'><code>$server="localhost";<br />
$port="389";<br />
$dn="dc=mondomaine,dc=com";<br />
$rootdn="cn=admin,$dn";<br />
$rootpw="admin";<br />
<br />
$ds=ldap_connect($server,$port); <br />
ldap_set_option($ds, LDAP_OPT_PROTOCOL_VERSION, 3);<br />
$r=ldap_bind($ds,$rootdn,$rootpw) <br />
        or die ("Impossible de se connecter au serveur ! \n");<br />
echo "Authentification sur le serveur OpenLDAP -> OK \n\n";</code></div>
<h3>Exemple de script PHP pour rechercher des enregistrements</h3>
<div style='text-align: left;' class='spip_code' dir='ltr'><code>$sr=ldap_search($ds,$dn,"(objectclass=*)");<br />
if ($sr) {<br />
        $info=ldap_get_entries($ds,$sr);<br />
        echo $info["count"]." enregistrements dans OpenLDAP \n";<br />
        for ($i=0;$i<=$info["count"];$i++) {<br />
                echo "$i - ".$info[$i]["dn"]." \n";<br />
        }<br />
}</code></div>
<h2>Liste partielle des attributs de la classe « organization »</h2>
<table class="spip">
<thead><tr class='row_first'><th scope='col'><strong class="spip">Attribut</strong></th><th scope='col'><strong class="spip">Description</strong></th></tr></thead>
<tbody>
<tr class="row_even"><td>businessCategory</td><td>Activité professionnelle d’une entreprise ou d’une personne</td></tr>
<tr class="row_odd"><td>c</td><td>Code du pays en deux lettres (respectant le standard ISO 3166)</td></tr>
<tr class="row_even"><td>cn</td><td>Nom de l’objet (common name)</td></tr>
<tr class="row_odd"><td>description</td><td>Description de l’objet</td></tr>
<tr class="row_even"><td>distinguishedName</td><td>Nom distingué (utilisé par d’autres attributs par héritage)</td></tr>
<tr class="row_odd"><td>facsimileTelephoneNumber</td><td>Numéro de fax</td></tr>
<tr class="row_even"><td>givenName</td><td>Prénom de la personne</td></tr>
<tr class="row_odd"><td>houseIdentifier</td><td>Identifiant d’un batiment</td></tr>
<tr class="row_even"><td>initials</td><td>Initiales d’une personne</td></tr>
<tr class="row_odd"><td>internationalSDNNumber</td><td>Numéro ISDN</td></tr>
<tr class="row_even"><td>l</td><td>localité de l’objet (géographique)</td></tr>
<tr class="row_odd"><td>member</td><td>Distinguished Name des membres</td></tr>
<tr class="row_even"><td>name</td><td>Nom (utilisé par d’autres attributs par héritage)</td></tr>
<tr class="row_odd"><td>o</td><td>Nom de l’organisation</td></tr>
<tr class="row_even"><td>objectClass</td><td>Classe d’objets</td></tr>
<tr class="row_odd"><td>ou</td><td>Unité organisationnelle (branche de l’organisation)</td></tr>
<tr class="row_even"><td>owner</td><td>Nom du propriétaire de l’objet</td></tr>
<tr class="row_odd"><td>postalAddress</td><td>Adresse postale (sans le code postal)</td></tr>
<tr class="row_even"><td>postalCode</td><td>Code postal</td></tr>
<tr class="row_odd"><td>postalOfficeBox</td><td>Boîte aux lettres (postale)</td></tr>
<tr class="row_even"><td>presentationAddress</td><td>Adresse réseau de la présentation de l’objet (généralement une URL vers la présentation en ligne)</td></tr>
<tr class="row_odd"><td>protocolInformation</td><td>Attribut complémentaire à presentationAddress pour définir le protocole à utiliser</td></tr>
<tr class="row_even"><td>registeredAddress</td><td>Adresse postale pour des envois de courriers recommandés et de colis</td></tr>
<tr class="row_odd"><td>seeAlso</td><td>DN d’objets complémentaires</td></tr>
<tr class="row_even"><td>serialNumber</td><td>Numéro de série de l’objet</td></tr>
<tr class="row_odd"><td>sn</td><td>Nom de famille de la personne (surname)</td></tr>
<tr class="row_even"><td>st</td><td>Etat ou région (state)</td></tr>
<tr class="row_odd"><td>streetAddress</td><td>Nom de la rue et assimiilé (boulevard, ...)</td></tr>
<tr class="row_even"><td>telephoneNumber</td><td>Numéro de téléphone</td></tr>
<tr class="row_odd"><td>title</td><td>Titre de la personne (différent de fonction)</td></tr>
<tr class="row_even"><td>uid</td><td>Identifiant unique de l’objet</td></tr>
<tr class="row_odd"><td>userPassword</td><td>Mot de passe de l’utilisateur</td></tr>
</tbody>
</table>
<h2>Liste partielle des attributs de la classe « inetOrgPerson »</h2>
<table class="spip">
<thead><tr class='row_first'><th scope='col'><strong class="spip">Nom</strong></th><th scope='col'><strong class="spip">Sémantique</strong></th><th scope='col'><strong class="spip">Mono</strong></th><th scope='col'><strong class="spip">Obl</strong></th><th scope='col'><strong class="spip">Lecture</strong></th><th scope='col'><strong class="spip">Utilisation</strong></th></tr></thead>
<tbody>
<tr class="row_even"><td>cn</td><td>nom(s) complet(s) (d’usage) sans accent</td><td> </td><td>O</td><td>RI</td><td>Ordre : Nom, Prénom. Attention : pas d’accent pour simplifier les recherches. Voir aussi displayName. Exemple : "Bugale Jerome"</td></tr>
<tr class="row_odd"><td>displayName</td><td>nom complet avec accents</td><td> </td><td> </td><td> </td><td>Version accentuée de la valeur principale de cn. Exemple : "Bugalé Jérôme"</td></tr>
<tr class="row_even"><td>employeeType</td><td>type de personnel</td><td> </td><td>D ?</td><td>RI ?</td><td>Définir les grandes familles ?</td></tr>
<tr class="row_odd"><td>facsimileTelephoneNumber</td><td>Numéro de fax</td><td> </td><td> </td><td>RI</td><td>Format E 123 (cf Références)</td></tr>
<tr class="row_even"><td>givenName</td><td>Prénom</td><td>M</td><td>D</td><td>RI</td><td>idem sn. Exemple : "Jérôme"</td></tr>
<tr class="row_odd"><td>l</td><td> </td><td> </td><td> </td><td> </td><td>localité de l’objet (géographique)</td></tr>
<tr class="row_even"><td>labeledURI</td><td>Page personnelle</td><td> </td><td> </td><td>RI</td><td> </td></tr>
<tr class="row_odd"><td>mail</td><td>adresse mel canonique</td><td>M</td><td> </td><td>RI ?</td><td> </td></tr>
<tr class="row_even"><td>mobile</td><td>numéro de téléphone mobile</td><td> </td><td> </td><td>RI</td><td>Format E 123 (cf Références)</td></tr>
<tr class="row_odd"><td>o</td><td> </td><td> </td><td> </td><td> </td><td>Nom de l’organisation</td></tr>
<tr class="row_even"><td>ou</td><td> </td><td> </td><td> </td><td> </td><td>Unité organisationnelle (branche de l’organisation)</td></tr>
<tr class="row_odd"><td>postalAddress</td><td>Adresse postale</td><td> </td><td> </td><td>RI</td><td>Adresse complète. Attention au format ("$" séparateur, voir RFC2256)</td></tr>
<tr class="row_even"><td>postalCode</td><td>Code postal</td><td> </td><td> </td><td> </td><td> </td></tr>
<tr class="row_odd"><td>preferredLanguage</td><td>langue préférée</td><td>M</td><td> </td><td>RI</td><td>cf RFC2068</td></tr>
<tr class="row_even"><td>sn</td><td>Nom </td><td> </td><td>O</td><td>RI</td><td>Contient le nom d’usage. Il est possible d’ajouter le nom de famille (nom patronymique) en seconde valeur. Tout caractère diacritique. Première lettre en majuscule. Voir aussi cn. Exemple : "Bugalé". </td></tr>
<tr class="row_odd"><td>st</td><td> </td><td> </td><td> </td><td> </td><td>Etat ou région (state)</td></tr>
<tr class="row_even"><td>telephoneNumber</td><td>numéro de téléphone fixe</td><td> </td><td> </td><td>RI</td><td>Format E 123 (cf Références)</td></tr>
<tr class="row_odd"><td>title</td><td>titre</td><td> </td><td> </td><td>RI</td><td>Responsabilité ; président, directeur, ... (cf Harpège ?). Code ou intitulé complet ?</td></tr>
<tr class="row_even"><td>uid</td><td>identifiant unique</td><td>M</td><td>D</td><td>R</td><td>utilisé comme rdn, contenu indifférent mais aussi court que possible</td></tr>
<tr class="row_odd"><td>userCertificate</td><td>certificat X509</td><td> </td><td> </td><td>A ?</td><td> </td></tr>
</tbody>
</table>
<h2>Problème rencontré</h2>
<p class="spip">Suite à une coupure brutale du serveur, le serveur ldap n’est pas reparti. Voici le message que j’avais au démarrage en mode débugage :</p>

<div style='text-align: left;' class='spip_code' dir='ltr'><code># slapd -d 1<br />
bdb_db_open: dbenv_open(/var/lib/ldap)<br />
bdb(dc=mondomaine,dc=com): operation not permitted during recovery<br />
bdb_db_open: db_open(/var/lib/ldap) failed: Invalid argument (22)<br />
backend_startup: bi_db_open failed! (22)<br />
bdb_db_destroy: close failed: Invalid argument (22)<br />
slapd stopped.</code></div>
<p class="spip">En fait, c’était la base ldap qui était corrompue et pour régler ce problème, je l’ai simplement ré-indexée avec ces commandes :</p>

<div style='text-align: left;' class='spip_code' dir='ltr'><code># /etc/init.d/slapd stop<br />
# slapindex<br />
# /etc/init.d/slapd start </code></div>
<h2>Historique des modifications</h2>
<table class="spip">
<thead><tr class='row_first'><th scope='col'><strong class="spip">Version</strong></th><th scope='col'><strong class="spip">Date</strong></th><th scope='col'><strong class="spip">Commentaire</strong></th></tr></thead>
<tbody>
<tr class="row_even"><td>0.1</td><td>18/11/04</td><td>Création par Tony GALMICHE</td></tr>
<tr class="row_odd"><td>0.2</td><td>03/12/04</td><td>MAJ suite à installation serveur Pentium Pro 2000</td></tr>
<tr class="row_even"><td>0.3</td><td>17/12/04</td><td>MAJ suite à présentation atelier Coagul</td></tr>
<tr class="row_odd"><td>0.4</td><td>15/03/07</td><td>Ajout « Problème rencontré »</td></tr>
</tbody>
</table>