LDAP présentation et implémentation

Jehan Procaccia MCI INT-EVRY- jehan.procaccia@int-evry.fr

12 mars 2003

Table des matières

Résumé : Ceci est un document de travail en cours d'évolution (le qualificatif ``new'' indique les rubriques dernierement mises à jour) , cela explique la présence, notament dans les cadres d'exemple de la partie exploitation, de noms de machines, répertoires ... qui peuvent être différents d'un exemple à l'autre.
Il s'agit dans un premier temps d'une présentation des concepts, suivi d'un decription détaillée de la mise en oeuvre d'un serveur OpenLdap pour la gestion système des services Unix.
Il existe beaucoup d'autres documentations sur le sujet, celle-ci recenses l'ensembles des étapes de la mise en oeuvre de l'annuaire LDAP à l'INT-Evry.

1  Introduction

Le système d'information implique un nombre important d'annuaires. Par annuaire nous entendons un ensemble d'information utile à la réalisation de services. Ce peut être la liste du personnel pour les applications RH (payes, congés ...), une liste de compte pour l'accès aux ressources informatiques, se déclinant souvent en une liste par type de service (comptes Unix, comptes NT/2000, comptes RAS ...) chacun faisant référence à une base propre ( respectivement , Nis, SAM/Active Directory, radius ...), ou encore des informations pour l'autocommutateur, répertoriages de ressources immeubles; salles, prises ..., meubles: mobilier, matériels informatique ... .
On le voit les besoins sont immenses et souvent redondants. Des solutions existent, elle sont souvent propriétaires et dépendantes des services. Face à ces faits, des standards ont été érigées, c'est notament le cas de la norme X.500, normalisation ISO dédiées aux annuaires électroniques. Elle reste cependant très lourde à mettre en oeuvre, est n'est pas adaptée aux réseaux réellement mis en productions (TCP/IP et non OSI !). C'est ici qu'intervient LDAP (Lightweight Directory Acces Protocol), qui comme son nom l'indique apporte de la souplesse aux normes X.500. L'objectif est le même: fédérer les informations dans une base unique dont l'accès est normalisé et indépendant de tout constructeur. Il apporte essentiellement une solution exploitable sur des réseaux que nous utilisons ( technologies Internet) et une mise en oeuvre abordable. Bien qu'il puisse répondre à l'ensemble des besoins d'un système d'information complexe, il n'est pas de notre volonté d'aboutir directement à ce point. Il conviendra avant tout de concrétiser son utilisation sur des services limités et déjà bien adaptés. Ce sera le cas, par exemple, de l'authentification des utilisateurs sur les différentes ressources informatiques de l'établissement.

2  l'existant

2.1  X.500

Le standard X.500 définis en 1988 puis dans sa deuxième version en 1993 comprend plusieurs normes:

2.2  les annuaires en exploitation

2.2.1  systèmes d'exploitation Windows

Windows NT: l'information est disséminée dans plusieurs outils et bases de données (gestionnaires de serveurs, gestionnaire wins, gestionnaire des utilisateurs ...). Windows 2000: l'ensemble est regroupé dans un annuaire propriétaire ; Active Directory, mais compatible avec le protocole LDAP, il sera donc possible d'y accéder en lecture/écriture via LDAP grâce notament à l'interface de programmation ADSI.

2.2.2  Novell NDS

Novell à rendu son système d'annuaire indépendant du système d'exploitation (Netware). Novell Directory Service est un annuaire object disponible sur Netware, Solaris et Windows. Il supporte le standard LDAP v3 et les données au format LDIF ( Ldap Data Interchange Format, format texte de données d'import/export dans un annuaire LDAP ).

2.2.3  NIS

Dès 1985 Sun à introduit le Network Information Service, déjà destiné à centraliser l'administration des informations système. Les informations sont stockées sous forme de map (table de correspondance entre une clé et une valeur: clé titan , valeur 192.163.12.43) dans de simples bases indexées (db, dbm ...), accessibles par des appels RPC (Remote Procedure Call). Dans un premier temps les map s'ajoutaient aux fichiers locaux (ajout d'un caractère ``+'' dans le fichier concerné pour rediriger la recherche vers les nis, remarque: depuis solaris 2 ce signe n'a plus cet effet. Avec solaris 2 et toutes ses sources d'information système (dns, nis+ ...) il devenait difficile de programmer dans chaque service ou utilitaire système, le mode de recherche. Sun a créé une API qui se place entre ces programmes et les différentes sources d'information sur lesquelles ils s'appuient, cette API est le Name Service Switch. Avec cette API les programmes n'ont plus a connaître les détails d'implémentation des services d'information qu'ils contactent. Les appels à cette API sont du type getXbyY (gethostbyname), lisent le fichier /etc/nsswitch.conf dans lequel est associé à chaque type de service de nom une liste des sources disponibles;
exemple: passwd: files nis nisplus, host: files nis nisplus dns. La recherche se fait d'une source à l'autre dans l'ordre définis tant qu'une correspondance n'est pas trouvée, ce qui abouti à une erreur NOTFOUND en cas d'échec sur toutes les sources listées. On peut d'ailleurs utiliser l'option [NOTFOUND=return] afin d'indiquer aux programmes de consulter uniquement les sources listées à gauche de cette entrée, seulement en cas de disfonctionnement de ces sources celles de droite seront consultées. Exemple: networks: nis [NOTFOUND=return] files. Les NIS fonctionnent sur un système maître-esclave (les modifications se font sur le maître, elles sont répercutées sur le(s) esclave(s) du domaine nis), seulement ils ne sont pas adaptés à des volumes important de données, à chaque modification c'est l'ensemble de la base qui est transférée entre maitre-esclave. Il y à également un manque d'organisation des données sous forme hiérarchique. Enfin la sécurité d'accès à ce service est très faible

2.2.4  NIS+

Sun à réagit aux défauts de NIS par l'introduction dans solaris 2 des NIS+. Ils répondent aux trois remarques ci-dessus en introduisant un propagation des données entre maître-esclave de manière incrémentale, en ajoutant la notion de root domain master en haut de l'arbre hiérarchique sous lequel de trouvent des subdmain master pouvant eux-mêmes être au-dessus d'autres subdomain-master. Enfin la notion de ``certificat, identité'' (credential) via une paire de clé privée/publique vient combler le problème de sécurité. L'imposition d'une architecture hiérarchique de haut en bas des nis+ qui implique une coopération entre les administrateurs systèmes des différents départements d'une organisation, ainsi que la gestion des paires clé privée/public fut finalement un frein au passage des nis aux nis+. Pourtant ces derniers préfigurent beaucoups de concepts utilisés par la suite dans LDAP.

2.2.5  DNS

Le Domain Name Service à été crée pour palier à l'élargissement des échanges de fichiers hosts et à la répartition de leur administration déjà conséquente dans le réseau ARPANET, précurseur de l'Internet. C'est un système de nommage hiérarchique à l'échelle de l'Internet. Il dispose d'une redondance et depuis peu d'un minimum de sécurité/contrôle d'accès. Bien que très performant et adapté dans son rôle de service de noms IP, il est reste aussi très spécialisé.

2.2.6  logiciels du marché

Microsoft MS-exchange, Lotus cc:Mail, Novell Groupewise ou Netscape Messaging server utilisent des annuaires propriétaires , souvent liés à une base de donnée propre à leurs besoins pour la gestion de leurs ressources. Des interfaces d'accès (API) à ces bases sont disponibles (ADSI pour Microsoft, VIM pour Lotus).

2.2.7  logiciels spécifiques

Il existe souvent dans l'entreprise un nombre important de produits ``maison'' basés la plupart du temps sur des SGBD (Oracles, DB2, MySQL, Access ...). Encore une fois, bien souvent une base de comptes est définis pour chacune de ces applications et les interfaces d'accès ne sont pas toujours simples (C, cobol ...) et uniques.

3  LDAP

Après le succès des NIS et beaucoup plus modérément des NIS+ par SUN, le CCITT et l'ISO se sont attelés à définir un standard d'annuaire, dont l'accès serait indépendant des RPC (SUN), il créèrent le protocole X.500. Bien qu'ayant de très bonnes spécifications, les détails d'implémentations n'étaient en revanche pas très heureux que se soit au niveau de la rigidité des règles d'implémentation ou de la pile de protocole envisagée (ISO plutôt que TCP/IP !). C'est dans ces conditions qu'est née une version allégée de X.500 basée sur TCP/IP; Lightweight Directory Acces Protocol qui peut être définis comme étant un protocole de réseaux standard spécialisé dans la manipulation d'annuaires adapté aux réseaux et systèmes en exploitation sur l'Internet.

3.1  Modèles

Ce standard définis 4 modèles.
Le modèle d'information
qui définis la nature des données: classe, attribut, types..., l'arbre de données (DIT: Directory Information Tree).
Le modèle de nommage
qui organise et définis des règles de nommages des éléments de l'annuaire. Exemple; l'annuaire dispose d'un schéma, une classe d'objet doit être unique dans l'annuaire, elle est identifiée par un OID.
Le modèle fonctionnel
qui définis les services offerts, comment accéder aux informations et comment les mettre à jours.
Le modèle de sécurité
qui définis les droits d'accès et l'identification.

3.2  Réplication LDAP

Mécanisme qui permet de copier automatiquement les données d'un annuaire vers un autre serveur d'annuaire. Cela permet d'améliorer les performance en répartissant plusieurs annuaires identiques sur un domaine, un client sera au plus près de son serveur, cela permet aussi une répartition de la charge.

3.3  Subschema

Dans la norme V3 de LDAP le schéma doit être définis dans l'annuaire lui même. Par l'utilisation de classes (subschema) et attributs spécifiques on permet de lire et modifier le schéma de l'annuaire au moyen du protocole LDAP lui même.
On peut lire le schéma via l'URL:
ldap://localhost:port/cn=schema??base?obectclass=*

3.4  RootDSE

On parle de RootDSE (Directory Server Agent (Dsa), Specific Entry). Il comprendra des attributs de type: NamingContext, SupportedExtensions, SupportedControl,SupportedSALMechanism,SupportedLDAPVersion. C'est le seul objet qui n'a pas de nom.

3.5  Définitions

Règles
règles de comparaison d'attributs identifiés par un oid.
Règles:(caseignorematch,CaseExactMatch,TelephoneNumberMatch, IntegerMatch,BouleanMatch,DNMatch,OctetStringMatch ...) cf RFC 2252.
Classes
Elles décrivent les entrées, entrées qui sont composées d'attributs. Elles peuvent être de différents types:
abstrait
n'ont pas d'instance, elles servent de réservoir de définition pour les autres classes (grâce à l'héritage): exemple classe top.
structurel
définis des objets instanciés, se sont les plus classiques: exemple Person.
auxiliaire
elles sont utilisées pour compléter les classes structurelles (afin de ne pas modifier directement ces dernières), pour ajouter des attributs à une classe déjà définie par exemple. Elles hérites de top.
L'ordre d'utilisation des classes n'est pas obligatoire avec LDAP. Ce n'est pas le cas dans X500: exemple country->organization->organizationalUnit, avec LDAP on peut avoir organization->country .
Extension
ce sont les opérations conformes LDAP V3 en plus des 9 opérations de base. Exemple: association de signature électronique à toute modification de l'annuaire, ou bien le support de Transaction à la façon SGBDR.
Scope
sélectionne la profondeur de recherche dans l'arbre (DIT).
BASE
indique une recherche uniquement sur la base sélectionnée (recherche sur une seule entrée)
ONELEVEL
toutes entrées se trouvants au niveau juste inférieur à la base sélectionnée
SUBTREE
recherche à partir de la base sélectionnée et parcoure toutes les branches en dessous.
URL
On peut interroger un annuaire Ldap avec un navigateur Internet. L'URL utilise la syntaxe suivante:
protocol://server:port/base?attributs à extraire?type de scope?critères. Exemple:

ldap://corbeau.int-evry.fr:389/dc=int-evry,dc=fr?host?sub?uid=test test host corne.int-evry.fr

3.6  habilitations

Les controles sont de la forme: accès à quoi, par qui -> type d'accès pour cette paire. exemple: accès à l'attribut telephoneNumber par l'utilisateur lui même -> écriture.

3.6.1  Quoi

Dans la partie quoi, on peut spécifier ;
une expression régulière
correspondant à un dn: dn=<regular expression>
une liste d'attributs
attrs=<attribute list>
un filtre
filter=<ldap filter>
L'accès à l'entrée elle même (attribut "entry") est nécessaire pour donner des accès à un attribut individuel.

3.6.2  Qui

*
tous les utilisateurs, autant les anonymes que les authetnifiés
anonymous
utilisateur non authentifiés
users
utilisateurs authentifiés
dn=<regex>
utilisateurs correspondants à une expression régulière, le dn ne doit pas contenir de blancs (il est "normalized")
remarque: toute directive de type d'accès (access to) finie par un by * none implicite ainsi que toute ACL (access list) finie par un access to * by * none implicite.

3.6.3  Type d'accès

none
pas d'accès
auth
= x permet de faire un bind authentifié,
compare
= cx pour la comparaison,
search
=scx pour l'application de filtre de recherche,
read
=rscx pour lire les resulats de recherche,
write
=wrscx pour modifier ou renommer.

3.6.4  Evaluation

L'évaluation des controles d'accès se fait dans l'odre ou les regles sont définies dans le fichier de configuration avec un arret d'évaluation à la première correspondance ("first match"). slapd compare d'abord le quoi, puis le qui et décide enfin si la requête du client est confome (inférieure ou égale) au type d'accès définis dans ces circonstances (quoi/qui).

3.7  Connexion à l'annuaire (bind)

3.7.1  Authentification Simple

Il suffit de fournir à l'annuaire un Distinguish Name (dn) et le mot de passe associé (ou bien se connecter en annonymous), on parle d'attachement au serveur ou Bind. Par défaut cette authentification simple laisse circuler en clair les échanges LDAP avec le serveur sur le réseau. Il faut ajouter des mécanismes de sécurité comme SSL ou SASL pour crypter ces échanges.

4  Compilation, Installation d'Openldap 2.1.X (NEW)

4.1  Contraintes de migration 2.0.X à 2.1.X (NEW)

Un certains nombre de contraintes ont été ajouté dans les versions 2.1.x pour un meilleurs respet des RFC . Voici la liste de celles que j'ai rencontré:
objectclass strutural
un objet (une entrée) ne peut dépendre que d'une seule objectclass structurelle, sauf si ces objectclass sont dans la même descendance ,
attribut systeme structuralObjectClass
cet attribut apparait sur chaque nouvelle entrée ldap, cela explique pourquoi on ne peux avoir un master en 2.0.X et un slave en 2.1.X -> error: "No structuralObjectClass operational attribute",
bind v2
n'est plus disponible par défaut, il faut le forcer dans le slapd.conf si nécessaire: allow bind_v2,
bind non dn et defaultaccess
par défaut il y a maintenant un disallow bind_anon_dn et defaultaccess none est positionné par défaut,
entryUUID/entryCSN
ces deux nouveaux attributs opérationnels sont automatiquement mis à jour -> utiles pour un futur multi-master ...
RDN
la valeur de l'attribut RDN doit être reprise a l'identique dans l'entrée, exp: dn: cn=JP_Bar,ou=people,dc=int-evry,dc=fr puis dans les attrribut de cet objet -> cn: Jean-Pierre Bar, ne sera pas valide ! il doit être identique au RDN -> cn: JP_Bar .

4.2  Berkeley DB

La base LDAP est ici au format berkeley DB (BDB),
cf http://www.sleepycat.com/, qui peut être utilisée dans un backend-bdb ou backend-ldbm with-ldapm-api=berkeley

4.2.1  L'essentiel de BDB

Sur ce lien je reprend les principales caracteristiques de BDB, et les notions fondamentales, c'est un copier-collé de la doc sleepycat qui résume ce qui m'a semblé important, pour ma propre information mais aussi pour le lecteur ... http://www.int-evry.fr/mci/user/procacci/ldap/BDB.txt

4.2.2  Compilation et installation BDB

Afin de rendre les binaires openldap indépendants des modifications de versions des librairies BDB système (BDB étant utilisé par ailleurs par le système) nous prenons une version à jour de BDB et l'installons à part. La compilation d'openldap fera par la suite référence à cette installation: cf CPPFLAGS et LDFLAGS de la configuration de la compilation d'openldap ci-après.
[jehan@corbeau /usr/local/src/db-4.1.24.NC/dist]
$./configure
$make
[jehan@corbeau /usr/local/src/db-4.1.24.NC/dist]
$make install prefix=/usr/local/jehan/bdb-4.1.24

4.3  Openldap

Exemple de compilation et installation dans une arborescence de test, dans l'objectif par la suite de construire un package RPM pour une installation système propre.
[jehan@corbeau /usr/local/src/openldap-2.1.8]
$ CPPFLAGS=-I/usr/local/jehan/bdb-4.1.24/include \
LDFLAGS=-L/usr/local/jehan/bdb-4.1.24/lib \
./configure  --enable-debug --enable-crypt --enable-bdb --enable-ldbm \
--with-ldbm-api=berkeley --enable-monitor --enable-local --enable-cldap \
 --disable-rlookups --with-tls --with-cyrus-sasl --enable-passwd \
--enable-shell --enable-cleartext --enable-spasswd --enable-meta --enable-ldap --enable-rewrite
$ make depend
$ make
$ make test
$ make install prefix=/usr/local/src/jehan/openldap-2.1.8

4.3.1  Configuration

Adaptation du schema
Penser (pour une config redhat avec autofs et kerberos) à recréer l'arborenscence de schema redhat pour les schemas autofs et kerberos aisni que de modifier le schema de base afin de tenir compte du fait qu'une entrée ldap ne peu dépendre que d'une classe structurelle (sauf si cette classe hérite d'une autre classe structurelle de la meme lignée -> Person -> InetOrgPerson )
 [jehan@corbeau /usr/local/src/jehan/openldap-2.1.8/etc/openldap/schema]
$ mkdir redhat
$ cp /tmp/autofs.schema ./redhat/
$ cp /tmp/kerberosobject.schema ./redhat/
$ cp /tmp/int-evry.schema .

Ajout de l'attribut krbName a kerberosobject.schema,
attribut qui était avant définit dans core.schema et reclassement
en AUXILIARY de l'objectclass kerberosSecurityObject qui etait STRUCTURAL !.

attributetype ( 1.3.6.1.4.1.250.1.32
        NAME ( 'krbName' 'kerberosName' )
        DESC 'Kerberos Name'
        EQUALITY caseIgnoreIA5Match
        SYNTAX 1.3.6.1.4.1.1466.115.121.1.26
        SINGLE-VALUE )

objectclass ( 1.3.6.1.4.1.2312.4.2.4 NAME 'kerberosSecurityObject'
        SUP top AUXILIARY
        DESC 'A uid with an associated Kerberos principal'
        MUST ( krbName ) )


Redéfinition de l'objectclass account de STRUCTURAL vers AUXILIARY

$ vi cosine.schema
objectclass ( 0.9.2342.19200300.100.4.5 NAME 'account'
        SUP top AUXILIARY


Adaptation du fichier de configuration
Modification du slapd.conf pour s'adapter à l'arborescence de test
ucdata-path     /usr/local/src/jehan/openldap-2.1.8/share/openldap/ucdata
include         /usr/local/src/jehan/openldap-2.1.8/etc/openldap/schema/core.schema
include ....
allow bind_v2
database        bdb
directory       /usr/local/src/jehan/openldap-2.1.8/var/openldap-data/int

Test
Restauration d'une base
[jehan@corbeau /usr/local/src/jehan/openldap-2.1.8]
$ ./sbin/slapadd -l /tmp/dump_int.ldif21Nov200219:59:03
 -f ./etc/openldap/slapd.conf
$ ls var/openldap-data/int/
cn.bdb    __db.005       IntEPersInetServ.bdb  log.0000000005   uid.bdb
__db.001  dn2id.bdb      log.0000000001        log.0000000006   uidNumber.bdb
__db.002  gidNumber.bdb  log.0000000002        mail.bdb
__db.003  givenName.bdb  log.0000000003        objectClass.bdb
__db.004  id2entry.bdb   log.0000000004        sn.bdb

Lancement du serveur
[jehan@corbeau /usr/local/src/jehan/openldap-2.1.8]
$ ./libexec/slapd -4 -d 256 -f etc/openldap/slapd.conf \
-h ldap://localhost:9009/
bdb_open: Sleepycat Software: Berkeley DB 4.1.24: (September 13, 2002)
...

Requête
[jehan@corbeau /usr/local/src/jehan/openldap-2.1.8]
$ ./bin/ldapsearch -x uid=procacci -b "dc=int-evry,dc=fr" -p 9009 -h localhost

Utilitaires BDB
[jehan@corbeau /usr/local/src/jehan/openldap-2.1.8/var/openldap-data/int]
$ /usr/local/src/jehan/bdb/bin/db_stat  -m
....
=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
Pool File: id2entry.bdb
16384   Page size.
0       Requested pages mapped into the process' address space.
12      Requested pages found in the cache (1%).
1405    Requested pages not found in the cache.
0       Pages created in the cache.
1405    Pages read into the cache.
3       Pages written from the cache to the backing file.

$ /usr/local/src/jehan/bdb/bin/db_verify id2entry.bdb -o

$ /usr/local/src/jehan/bdb/bin/db_dump -f mail.bdb.dump mail.bdb

$ rm mail.bdb
$ /usr/local/src/jehan/bdb/bin/db_load -f mail.bdb.dump mail.bdb
db_load: Lock table is out of available locks
db_load: Cannot allocate memory
???

$ /usr/local/src/jehan/bdb/bin/db_recover -v
db_recover: Finding last valid log LSN: file: 6 offset 6567774
db_recover: Recovery starting from [6][6567605]
db_recover: Recovery complete at Sun Nov 17 19:49:04 2002
db_recover: Maximum transaction ID 80001453 Recovery checkpoint [6][6567774]
db_recover: Recovery complete at Sun Nov 17 19:49:04 2002
db_recover: Maximum transaction id 80000000 Recovery checkpoint [6][6567774]


4.4  Package Openldap RPM (NEW)

Installation d'openldap à partir de source RPM. Partir des packages source permet entre autre de modifier le mode de compilation d'openldap. On voudra par exemple forcer la compilation avec les supports des threads (pas de threads par defaut dans les versions 2.0.1x des packages RedHat d'openldap !) ou bien avec le support des berkeley DB ou des fonctionnalité méta etc ... On retrouve donc toutes les options de compilation des sources au format tar.gz, puisque qu'un package source n'est autre qu'une automatisation de la configuration-compilation-installation d'une application. cf RPM-HOWTO: http://www.linux.org/docs/ldp/howto/RPM-HOWTO/
Ici nous sommes partis d'un package source 2.1.3 RedHat, en y incluant régulièrement les différentes version openldap, dernièrement le tar.gz 2.1.15 d'openldap, avec les adaptations qui vont bien, cf commentaires ci-dessous.
$ rpm -i /home/jehan/openldap-2.1.3-4-RH.src.rpm

Modification des options de compilation et adaptation du package source 2.1.3 vers 2.1.15. Essentiellement, il s'agit de prendre en compte les berkeley DB -> backend berkeley puis quelques modifications (patch de la 2.1.3 devenus inutiles, ou idéfinis !? en 2.1.15) et options de compilation pour la prise en charge de fonctionnalitées supplémentaires: --enable-monitor --enable-meta etc ...
$ vi /usr/src/redhat/SPECS/openldap-2.1.15.spec
%define migtools_ver 44
%define db_version 4.1.25.NC
#%define backend gdbm
%define backend berkeley
Summary: The configuration files, libraries, and documentation for OpenLDAP.
Name: openldap
#Version: 2.1.3
Version: 2.1.15
#Release: 4
Release: 1
...
#Modification des patch de la 2.1.3 vers la 2.1.8; on en retire 2 !.
Patch0: openldap-2.1.3-config.patch
#Patch1: openldap-2.1.2-redhat.patch
Patch2: openldap-1.2.11-cldap.patch
#Patch3: openldap-2.1.2-syslog.patch
Patch3: openldap-2.1.13-syslog.patch
Patch4: openldap-2.1.2-sendbuf.patch
Patch5: openldap-2.0.11-ldaprc.patch
Patch7: openldap-2.1.12-subdir.patch

...
%setup -q -a 1 -a 3
%patch0 -p1 -b .config
#%patch1 -p1 -b .redhat
%patch2 -p1 -b .cldap
%patch3 -p1 -b .syslog
%patch4 -p1 -b .sendbuf
%patch5 -p1 -b .ldaprc
#%patch6 -p1 -b .debug
%patch7 -p1 -b .subdir
...


%configure \
        --with-slapd --with-slurpd --without-ldapd \
        --with-threads=posix --enable-static \
        \
        --enable-local --enable-cldap --disable-rlookups \
        \
        --with-tls \
        --with-cyrus-sasl \
        \
        --enable-wrappers \
        \
        --enable-passwd \
        --enable-shell \
        --enable-cleartext \
        --enable-crypt \
        --enable-spasswd \
        --enable-modules \
        --enable-lmpasswd \
        --enable-monitor \
        --enable-rewrite \
        --enable-ldap \
        --enable-meta \
        --enable-shell \
       --enable-password \
        --enable-debug \
        \
        --libexecdir=%{_sbindir} \
        --localstatedir=/%{_var}/run \
        $@ \$@


Construction des packages openldap, attention depuis la version 8.0 de RedHat la commande est maintenant rpmbuild -ba et non plus rpm -ba !.
[root@corbeau /usr/src/redhat/SPECS]
$ rpmbuild -ba openldap-2.1.15.spec
...
Wrote: /usr/src/redhat/SRPMS/openldap-2.1.15-1.src.rpm
Wrote: /usr/src/redhat/RPMS/i386/openldap-2.1.15-1.i386.rpm
Wrote: /usr/src/redhat/RPMS/i386/openldap-devel-2.1.15-1.i386.rpm
Wrote: /usr/src/redhat/RPMS/i386/openldap-servers-2.1.15-1.i386.rpm
Wrote: /usr/src/redhat/RPMS/i386/openldap-clients-2.1.15-1.i386.rpm

liste des packages ldap sur la machine
$ rpm -qa | grep ldap
openldap-devel-2.1.15-1
nss_ldap-198-3
php-ldap-4.2.2-8.0.5
openldap-2.1.15-1
openldap-clients-2.1.15-1
openldap-servers-2.1.15-1


Installation des packages binaires
$ cd /usr/src/redhat/RPMS/i386/
[root@corbeau /usr/src/redhat/RPMS/i386]
$ rpm -Uvh openldap-*2.1.15-1.i386.rpm
Preparing...                ########################################### [100%]
   1:openldap               ########################################### [ 25%]
   2:openldap-servers       ########################################### [ 50%]
   3:openldap-clients       ########################################### [ 75%]
   4:openldap-devel         ########################################### [100%]

4.5  Disponibilite des RPM

Je met régulièrement en ligne mes compilations de RPM sur: http://www.int-evry.fr/mci/user/procacci/SRPMS/

5  Exploitation

5.1  Modifier le schéma

Il est possible de personnaliser le schéma par ajout d'attribut/objectclass.

5.1.1  Définition d'objets

Une syntaxe particulière est utilisée pour définir des attributs/objectclass. Ici nous employons la syntaxe LdapV3. il existe aussi une syntaxe ASN1, ou X500 .
Cela commence par le mot clé attributetype ou objectclass suivi de son oid du nom NAME et d'une éventuelle DESCription.
On trouve ensuite la règle de comparaison entre attributs, EQUALITY caseIgnoreMatch par exemple éventuellement suivit d'une règle de correspondance sur les opérations de recherche, SUBSTR caseIgnoreSubstringsMatch par exemple.
Enfin une SYNTAXE identifiée par un oid définit le type d'un attribut.
Types:(binary,boolean,dn,diretory string (chaine UTF8), integer, telephoneNumber...) cf RFC 2252 et 2256.

5.1.2  Exemple de schema

Ici nous utilisons une objectclass auxiliary dont l'objectif est d'apporter des modifications à une classe structurelle standard.
[root@mci21056 /etc/openldap/schema]
$ more int-evry.schema
#definitions propres à l'int

attributetype (1.3.6.1.4.1.7391.2.2.1.1.1.5
        NAME ('IntEPersInetServDemande')
        DESC 'Services Internet autorises'
        EQUALITY caseIgnoreIA5Match
        SYNTAX 1.3.6.1.4.1.1466.115.121.1.15
        )


objectclass ( 1.3.6.1.4.1.7391.2.1.1.1.1.1
        NAME 'IntE-user'
        SUP TOP
        AUXILIARY
        MAY ( IntEPersInetServDemande )
 )


OID
L'INT-Evry s'est vu attribué par l'IANA (http://www.iana.org/) l'oid 7391, donc la branche d'oid 1.3.6.1.4.1.7391. Ici suivant l'exemple de la doc openldap nous commençons l'arbre privé par 2 (pour ldap, 1 pour snmp) puis 2 pour les attributs ou 1 pour les objectclass. Ensuite on peut imaginer une branche système_informatique 1, mci 1, comptes 1; d'où le 7391.2.1.1.1.1.1 pour l'attribut IntEPersInetServDemande.

Autre exemple; utilisation du SubstringsMatch pour matcher une sous-chaine dans une liste ou aussi des types boolean; attention la case est importante: TRUE ou FALSE ou ON et OFF
$more /etc/openldap/schema/int-evry.schema
#definitions propres à l'int
attributetype (1.3.6.1.4.1.7391.2.2.1.1.1.4
 NAME ('IntEPersInetServ')
 DESC 'Services Internet autorises'
 EQUALITY caseIgnoreIA5Match
 SUBSTR caseIgnoreIA5SubstringsMatch
 SYNTAX 1.3.6.1.4.1.1466.115.121.1.15
 )
attributetype (1.3.6.1.4.1.7391.2.2.1.1.1.9
 NAME ('IntEPersPublic')
 DESC 'Services Internet demandes'
 EQUALITY booleanMatch
 SYNTAX 1.3.6.1.4.1.1466.115.121.1.7
 )

5.1.3  Exemple d'entrée

Exemple d'entrée ldif pour un compte utilisateur INT-evry. Dans cet exemple les attributs IntEPersxxxx (Int Evry Personne nom d'attribut) sont propres au schéma interne de l'INT.
dn: uid=procacci,ou=People,dc=int-evry,dc=fr
uid: procacci
cn: Jehan PROCACCIA
telephoneNumber: 01 60 76
givenName: Jehan
sn: PROCACCIA
objectClass: inetLocalMailRecipient
objectClass: person
objectClass: organizationalPerson
objectClass: inetOrgPerson
objectClass: account
objectClass: posixAccount
objectClass: IntE-user
objectClass: labeledURIObject
objectClass: strongAuthenticationUser
objectClass: certificationAuthority
objectClass: top
objectClass: kerberosSecurityObject
objectClass: shadowAccount
mail: Jehan.Procaccia@int-evry.fr
mailLocalAddress: procacci@mci.int-evry.fr
mailRoutingAddress: email@email
mailHost: smtp-mci
departmentNumber: MCI
employeeType: permanent
IntEPersUserQuota: 10000
IntEPersUserParrain: Eric.Collery
IntEPersUserExpire: 2999/12/31
IntEPersUserPTM: mci
IntEPersCreationDate: 2001/07/31-17:22:20
IntEPersLastModificationDate: 2001/07/31-17:22:20
IntEPersUserNature: permanent
IntEPersUserLogin: procacci
IntEPersUserUid: 145032
IntEPersUserGroup: mci
IntEPersUserShell: ksh
IntEPersUserPrenom: Jehan
IntEPersUserNom: PROCACCIA
IntEPersUserGecos: Jehan PROCACCIA
IntEPersUserEntite: MCI
IntEPersUserEmail: Jehan.Procaccia
IntEPersUserSmtp: smtp-mci
IntEPersUserMailLogin: procacci
IntEPersUserMbox: pop-mci
IntEPersUserMX: mci
o: INT Evry FRANCE
ou: MCI
shadowLastChange: 10000
krbName: procacci@INT-EVRY.FR
loginShell: /usr/local/bin/ksh
uidNumber: 145032
gidNumber: 145
homeDirectory: /mci/mci/procacci
gecos: Jehan PROCACCIA
host: .int-evry.fr
title: procacci
roomNumber: B001
jpegPhoto;binary:: /9j/4AAQSkZJRgABAQEAAQABAAD/2w......
labeledURI: http://www.int-evry.fr
facsimileTelephoneNumber: 01 60 76 xx xx
postalCode: 91011 EVRY CEDEX
postalAddress: 9 rue Charles Fourier
homePhone: 00 00 00 00 00
homePostalAddress: xx xxx
secretary: uid=secretary-name,ou=People,dc=int-evry,dc=fr
userCertificate;binary:: YWJjZGVmZ2hpamts
authorityRevocationList;binary:: YWJjZGVmZ2hpamts
certificateRevocationList;binary:: YWJjZGVmZ2hpamts
cACertificate;binary:: YWJjZGVmZ2hpamts
IntEPersInetServ: unix-int mail-int
IntEPersInetServDemande: unix-int mail-int ras-int
IntEPersACLDroit: telephoneNumber homePostalAddress
IntEPersPublic: FALSE
IntEPersUserPasswordFlag: FALSE
IntEPersUserLastPasswordChange: 1900/01/01-00:00:00


5.2  Modifier une entrée, outils shell ldap*

syntaxe

changetype: <[modify|add|delete|modrdn]>

Après la prise en compte de l'objectclass ci-dessus ( slapd.conf: include int-evry.schema) il est possible d'ajouter l'attribut IntEPersInetServ:
$ more add_IntEPersInetServ.ldif 
dn: uid=testlinu,ou=staff,ou=People,dc=int-evry,dc=fr
changetype: modify
add: objectclass
objectclass: int-evry-user
-
add: IntEPersInetServ
IntEPersInetServ: unix-int mail-int

$ ldapmodify -D "cn=admin,dc=int-evry,dc=fr" -W -p 9009 -h corbeau -x \
-f ./add_IntEPersInetServ.ldif  
Enter LDAP Password: 
modifying entry "uid=testlinu,ou=staff,ou=People,dc=int-evry,dc=fr"

Suppression:
$ more del_IntEPersInetServ.ldif 
dn: uid=testlinu,ou=staff,ou=People,dc=int-evry,dc=fr
changetype: modify
delete: IntEPersInetServ
IntEPersInetServ: unix-int mail-int
-
delete: objectclass
objectclass: int-evry-user

$ ldapmodify -D "cn=admin,dc=int-evry,dc=fr" -W -p 9009 \
-h corbeau -x -f ./del_IntEPersInetServ.ldif 
Enter LDAP Password: 
modifying entry "uid=testlinu,ou=staff,ou=People,dc=int-evry,dc=fr"

Pour supprimer une entrée complète:
dn: uid=testlinu,ou=staff,ou=People,dc=int-evry,dc=fr
changetype: delete

5.3  Dump et restore de la base

5.3.1  dump

Après avoir arreter le serveur, du moins pour les backend ldbm, normalement cela n'est plus nécessaire avec back-bdb (openldap 2.1.X), on peux faire un dump de la base au format ldif.
$ /etc/init.d/ldap stop
Stopping slapd:                                            [OK]

$/usr/sbin/slapcat -l /tmp/dump_int.ldif \
-f /etc/openldap/slapd.conf -b "dc=int-evry,dc=fr" 
$more /tmp/dump_int.ldif
dn: dc=int-evry,dc=fr
dc: int-evry
objectClass: top
objectClass: domain
objectClass: domainRelatedObject
associatedDomain: int-evry.fr
creatorsName: cn=admin, dc=int-evry, dc=fr
createTimestamp: 20010718150911Z
modifiersName: cn=admin, dc=int-evry, dc=fr
modifyTimestamp: 20010730082211Z
...

5.3.2  Restore

Si on remplace une base existante, on commence par la supprimer. Méthode rapide: supprimer les fichiers
$ /etc/init.d/ldap stop
Stopping slapd:                                            [  OK  ]

[root@openldap /var/lib/ldap/int]
$ rm -f *

Insertion sous forme de fichiers .gdbm, .dbb ou .bdb du dump.
$ /usr/sbin/slapadd -l /tmp/dump_int.ldif -f /etc/openldap/slapd.conf \
-b "dc=int-evry,dc=fr"

[root@corbeau /var/lib/ldap/int]
$ ls -al
total 11130
drwxr-xr-x    2 ldap     ldap         1024 Aug  2 17:46 .
drwx------    6 ldap     ldap         1024 Aug  2 17:15 ..
-rw-------    1 root     root      1716224 Aug  2 17:46 cn.dbb
-rw-------    1 root     root       700416 Aug  2 17:46 dn2id.dbb
-rw-------    1 root     root        31304 Aug  2 17:46 gidNumber.dbb
-rw-------    1 root     root       495920 Aug  2 17:46 givenName.dbb
-rw-------    1 root     root      7448938 Aug  2 17:46 id2entry.dbb
-rw-------    1 root     root      1728600 Aug  2 17:46 mail.dbb
-rw-------    1 root     root        12296 Aug  2 17:46 nextid.dbb
-rw-------    1 root     root       421909 Aug  2 17:46 objectClass.dbb
-rw-------    1 root     root       860200 Aug  2 17:46 sn.dbb
-rw-------    1 root     root       188416 Aug  2 17:46 uid.dbb
-rw-------    1 root     root       196608 Aug  2 17:46 uidNumber.dbb

Remarque: l'insertion c'est faite sous le compte root, il faut rendre ldap propiétaire des fichiers sans quoi slapd ne pourra rien lire !.
$ chown ldap:ldap *
[root@corbeau /var/lib/ldap/int]
$ ls -l
total 11128
-rw-------    1 ldap     ldap      1716224 Aug  2 17:46 cn.dbb
-rw-------    1 ldap     ldap       700416 Aug  2 17:46 dn2id.dbb
-rw-------    1 ldap     ldap        31304 Aug  2 17:46 gidNumber.dbb
-rw-------    1 ldap     ldap       495920 Aug  2 17:46 givenName.dbb
-rw-------    1 ldap     ldap      7448938 Aug  2 17:46 id2entry.dbb
-rw-------    1 ldap     ldap      1728600 Aug  2 17:46 mail.dbb
-rw-------    1 ldap     ldap        12296 Aug  2 17:46 nextid.dbb
-rw-------    1 ldap     ldap       421909 Aug  2 17:46 objectClass.dbb
-rw-------    1 ldap     ldap       860200 Aug  2 17:46 sn.dbb
-rw-------    1 ldap     ldap       188416 Aug  2 17:46 uid.dbb
-rw-------    1 ldap     ldap       196608 Aug  2 17:46 uidNumber.dbb

$ /etc/init.d/ldap start
Starting slapd:                                            [  OK  ]


et c'est reparti .

6  Développement

API Ldap en C cf draft-ietf-ldap-c-api-01.txt

6.1  concepts

Toute interaction avec un serveur se fait sur la base d'une recherche. Il n'y a pas de fonction de navigation ou d'interrogation. Les fonctions peuvent être synchrones, dans ce cas le programme client est bloqué en l'attente d'une réponse du serveur LDAP. Elle peuvent être asynchrones, le programme client peut alors faire autre chose en attendant que le serveur réponde, il faudra cependant "poller" régulièrement pour vérifier la presence d'une réponse. Les fonctions synchrones finissent toujours par un s: ldap_simple_bind_s

6.1.1  code de connexion

   0 #include <stdio.h>
   1 #include <ldap.h>
   2 
   3 main (int argc, int **argv) {
   4 //  Structure de donnée LDAP qui enregistre la connexion
   5 LDAP *ld;
   6 int rc = 0;
   7 printf("Test de connexion Ldap en C\n");
   8 /* initialisation de la connexion, 
   9 retour d'un handle de connexion dans la structure LDAP.
  10  l'init est tj synchrone. */
  11 if ((ld = ldap_init("corbeau", 9009) )== NULL)
  12         {
  13         perror ("ldap_init");
  14         }
  15 printf("Connecté à ldap\n");
  16 // Bind au serveur, bind synchrone 
  17 rc = ldap_simple_bind_s(ld,"cn=admin,dc=int-evry,dc=fr","password");
  18 
  19 if (rc != LDAP_SUCCESS)
  20         {
  21         fprintf(stderr,"Ldap error: %s\n", ldap_err2string(rc));
  22         }
  23         else
  24         {
  25         printf("Bind OK!\n");
  26         }
  27 // défaire le bind
  28 if ( ldap_unbind(ld) != LDAP_SUCCESS )
  29         {
  30         perror("ldap_unbind");
  31         }
  32 }

6.1.2  code de recherche

  33 
  34 #include <stdio.h>
  35 #include <ldap.h>
  36 
  37 
  38 #define HOST "corbeau"
  39 #define PORT 9009
  40 #define BASE "dc=int-evry,dc=fr"
  41 #define SCOPE LDAP_SCOPE_SUBTREE //autres: LDAP_SCOPE_ONELEVEL LDAP_SCOPE_BASE
  42 //filtre: l'uid = procacci ou testlinu et pour l'un ou l'autre le gid est 145
  43 #define FILTER "(&(|(uid=procacci)(uid=testlinu))(gidNumber=145))"
  44 
  45 main (int argc, int **argv) {
  46 
  47 // handle de connexion
  48 LDAP *ld;
  49 // structure contenant les données d'une entry + un pointeur vers l'entrée
  50 // suivante ou NULL s'il n'y en a pas
  51 LDAPMessage *result,*e;
  52 // structure de données Basic Encoding Rules (BER), règles définies pour
  53 // transmettre des données binaires sur l'Internet
  54 BerElement *ber;
  55  char *attribute,**vals;
  56  int i,rc,parse_rc =0;
  57  char *attribs[3];
  58  attribs[0]="cn";
  59  attribs[1]="mail";
  60  attribs[2]=NULL;
  61 
  62  // initialisation de la connexion
  63 printf("Initialisation de la connexion\n");
  64 if ((ld = ldap_init(HOST, PORT) )== NULL)
  65   {
  66     perror ("ldap_init");
  67     exit(1);
  68   }
  69 printf("Connecté au serveur ldap %s sur le port %d\n",HOST,PORT);
  70 
  71 /* fonction de recherche, parametres non NULL: handle de connexion, base, scope
  72 , filtre de recherche.
  73 parametres optionnels: attributs à selectionner (LDAP_NO_ATTRS pour aucuns),
  74 entier 0 pour retourner le nom des attributs en plus de leur valeur (1 sinon),
  75 extended operations, controles clients, time-out limite dans une structure 
  76 timeval ici pas de limite, combien d'entrées nous voulons sélectionner, et
  77 enfin  un pointeu sur une structure LDAPMessage pour le resultat du search */
  78   
  79  rc = ldap_search_ext_s(ld,BASE,SCOPE,FILTER,attribs,0,
  80 NULL,NULL,LDAP_NO_LIMIT,0,&result);
  81 
  82  if (rc != LDAP_SUCCESS )
  83    {
  84      fprintf(stderr, "ldap_search_ext: %s\n", ldap_err2string(rc));
  85      ldap_unbind(ld);
  86      exit(1);
  87    }
  88 
  89  printf("Nombre d'entrées sélectionnées: %d\n", ldap_count_entries(ld,result));
  90 
  91  //parcours des entrées
  92  for (e=ldap_first_entry(ld,result); e !=NULL; e=ldap_next_entry(ld,e))
  93    {
  94      //fonction spécifique pour le dn
  95      printf("DN: %s\n",ldap_get_dn(ld,e));
  96      //parcours des attributs<->valeurs d'une entrée
  97      for (attribute = ldap_first_attribute(ld,e,&ber);
  98 attribute != NULL; attribute = ldap_next_attribute(ld,e,ber))
  99          {
 100            //valeur(s) d'attribut, handle connexion, entrée, attribut
 101            if ((vals = ldap_get_values(ld,e,attribute)) != NULL)
 102            {
 103            //parcours eventuel des attributs multivalués,sinon une seule valeur
 104            for (i=0; vals[i] != NULL; i++)
 105             {
 106              printf ("\t%s: %s\n",attribute,vals[i]);
 107             }
 108            //libération de la mémoire allouée aux valeurs d'attribut
 109            ldap_value_free(vals);
 110            }
 111            //liberation de la mémoire allouée à l'attribut
 112            ldap_memfree(attribute);
 113          }
 114 
 115             //libération de la mémoire utilisée pour stocker 
 116          la structure de valeur (ber) de l'attribut
 117       if (ber != NULL)
 118          {
 119            ber_free (ber, 0);
 120          }
 121      
 122   }
 123 
 124 // test du rc de  for (e=ldap_first_entry(ld,result)... ci dessus
 125 if (rc != LDAP_SUCCESS)
 126   {
 127      fprintf(stderr, "ldap_search_ext: %s\n", ldap_err2string(rc));
 128   }
 129 
 130      ldap_msgfree ( result);
 131      printf ("Search réussi !\n");
 132 }
 133 
compilation
$  gcc ldap_search.c -lldap -llber -o ldap_search.bin

exécution
$ ./ldap_search.bin 
Initialisation de la connexion
Connecté au serveur ldap corbeau sur le port 9009
Nombre d'entrées sélectionnées: 2
DN: uid=testlinu,ou=Staff,ou=People,dc=int-evry,dc=fr
 cn: Compte_test LINUX
 mail: Compte_test.LINUX@int-evry.fr
DN: uid=procacci,ou=Staff,ou=People,dc=int-evry,dc=fr
 cn: Jehan PROCACCIA
 mail: Jehan.PROCACCIA@int-evry.fr
Search réussi !

7  Authentification système Unix, connexion à un système

7.1  Objectifs

l'authentification système par un annuaire LDAP permet d'unifier les bases de données utilisateur. De plus le système de répartition et de réplication que procure ce type d'annuaire permet une meilleure tolérance aux pannes. Enfin d'autres services peuvent tirer partie de cette base commune d'authentification (email, accès web, ftp, carnet d'adresse ...). C'est aussi la disponibilité d'outils d'administration et de surveillance de la journalisation.

7.2  Authentification système traditionnelle Unix

Le système unix utilise depuis longtemps un système d'authentification basé sur une algorithme de hachage nommé crypt(3) disponible aux programmeurs C par la librairie libcrypt.a, a ne pas confondre avec l'application d'encryptage/décryptage de fichiers /usr/bin/crypt. Avec crypt(3) la chaine texte saisie par l'utilisateur est hachée (one-way-encrypted) par l'algorithme crypt qui introduit en plus de cette chaîne un grain de sel (il s'agit des 2 premiers caractères de la version cryptée des 13 caractères ASCII du password, ils sont choisis aléatoirement afin de différencier différente occurrence d'une même chaine: salt).
Lors de l'authentification l'utilisateur saisi son login, ce qui permet au système de rechercher la chaine cryptée (password) qui est associée à ce login. Ensuite l'utilisateur est invité à saisir son password, celui ci sera crypté par l'algorithme crypt(3) avec les 2 premiers caractères du password crypté récupéré lors du login comme salt. Enfin une correspondance positive entre ce cryptage de la saisie et la chaîne cryptée récupérée lors du login attestera de l'identité de l'utilisateur.

7.3  Authentification système NIS+

La méthode ci-dessus ne permet pas de contrôler d'où se connecte l'utilisateur puisque cette information n'est stockée nulle part, de plus elle est basée sur un algorithme qui utilise une clé sur 56 bit relativement ancien. Dans NIS+ des ``certificats'' (user credentials) sont crées sous une forme unix.userid@domainname et stockés dans la table cred. Quand un utilisateur se connecte, les credentials sont retirés depuis la table cred. Cette table contient le mot de passe réseau de l'utilisateur sous forme de paire clé privée/publique.

7.4  PAM

Tout comme les Name Service Switch NSS viennent s'intercaler entre les programmes faisant accès à des sources d'information et ces sources, Pluggable Authentification Module fournis un cadre permettant à de nouvelles technologies d'authentification de s'interfacer avec les programmes qui les utilisent. On va par exemple avec un module PAM_LDAP, permettre à des programmes tels que login, telnet, ftp..., d'aller chercher et vérifier l'information d'authentification auprès d'un annuaire LDAP. Ces programmes doivent être ``pam-enabled'' c'est à dire lié à la librairie pam libpam.so pour utiliser ce cadre d'authentification. Reste à l'administrateur du système de définir les modules à utiliser, ce qui peut être définis de manière individuelle pour chaque application, ou communément pour un ensemble d'application. L'administrateur pourra empiler et définir un ordre d'importance à chacun des modules d'une manière propre pour chacun des programmes. C'est une structure d'authentification extrêmement modulaire et donc très évolutive.

7.4.1  Type de modules PAM

On distingue 4 fonctions qui peuvent être implémentés par un module.
authentification
ce type de module permet l'authentification et positionne l'identité d'un utilisateur.
account managment
une fois authentifier, ce type de module permet de valider l'accès de l'utilisateur aux ressources sur des critères tels que le vieillisement du mot de passe, l'expiration du compte, heures de restriction....
session management
gère certaines fonctions pendant la session de l'utilisateur, notament lors de l'ouverture et la fermeture de la session.
password management
gère les modifications de mot de passe.

7.4.2  Empilage et drapeaux de contrôle

Pour un service donné (ou plusieurs) différent modules peuvent être empilés, un mécanisme de drapeaux de contrôle (control flags) permet de donner plus ou moins d'importance au succès ou échec d'authentification sur chacun des modules empilés.
required
dans le cas d'un module déclaré required, en cas d'échec sur celui-ci le reste de la pile est consulté,mais le retour final sera un échec quelquesoit le résultat des modules suivants traversés.
requisite
en cas d'échec sur un module déclaré requisite, une erreur est immédiatement retournée sans poursuivre l'empilement des modules.
sufficient
si ce module retourne un succès, alors un succès général est immédiatement retourné sans poursuivre l'empilement.
optional
si ce module échoue, un autre peut retourner un succès, cet échec ne sera pas pris en compte dans le résultat final.

7.4.3  pam.conf ou pam.d/service

Exemple de fichier /etc/pam.d/login commenté
#%PAM-1.0
###############################################################################
#auth: actual authentification, ask and check password and set credentials as
#group membership or kerberos tickets

#securetty check that root logs from the console
auth       required /lib/security/pam_securetty.so 

#warn log info to syslog
auth       required /lib/security/pam_warn.so

#check presence of /etc/nologin file 
auth       required     /lib/security/pam_nologin.so

#pam ldap
auth       sufficient /lib/security/pam_ldap.so debug 

#pam_unix: This is the standard Unix authentication module.
# It uses standard calls from the system's libraries to retrieve 
#and set account information as well as authentication
#arguments: debug; audit; use_first_pass; try_first_pass; nullok; nodelay
auth       required /lib/security/pam_unix_auth.so try_first_pass audit

#############################################################################
#Account: non-authentification account management
#restrict access on time of day, available system ressources ...

account    required /lib/security/pam_warn.so
account    sufficient /lib/security/pam_ldap.so 

#pam_unix: Recognized arguments: debug; audit
account    required /lib/security/pam_unix_acct.so audit

##############################################################################
#password: for updating the authentification, if an auth module has determined
#that the password needs to be changed e.g expired ...

#if pam_unix present in the module type password 
#arguments: debug; audit; nullok; not_set_pass; use_authtok; try_first_pass; 
use_first_pass; md5; bigcrypt; shadow; nis; remember 

password   required /lib/security/pam_warn.so

#cracklib, checks tha newly entered password does not appear in a dictionnary
password   required /lib/security/pam_cracklib.so

password   sufficient /lib/security/pam_ldap.so debug

#pwdb modules: is a pluggable replacement for the pam_unix_.. modules.
#Based on the following pwdb_elements: expire; last_change; max_change; 
#defer_change; warn_change,
# this module performs the task of establishing the status of the user's 
#account and password
#The default action of this module is to not permit the user access to a 
#service if their official password is blank.
#The nullok argument overrides this default. 
#the argument try_first_pass, before prompting the user for their password, 
#the module first tries the  previous stacked auth-module's password in case
#that satisfies this module as well.
#The argument use_first_pass forces the module to use such a recalled password
#and will never prompt the user - if no password is available or the password 
#is not appropriate, the user will be denied access.
#arguments: debug; nullok; not_set_pass; use_authtok; try_first_pass; 
#use_first_pass; md5; bigcrypt; shadow; radius; unix 
password   required     /lib/security/pam_pwdb.so use_first_pass debug

###########################################################################
#session: for doing things before/after users can be given a service
#e.g: logging of information, mounting directory ...
session    required /lib/security/pam_warn.so
session    required /lib/security/pam_unix_session.so
session    optional     /lib/security/pam_console.so


7.4.4  configuration PAM sous RedHat (NEW)

Exemple de la version RH 8.0, pam-0.75-40, l'empilement des modules pam pour la plupart des services est centralisé dans le fichier system-auth.
$ cat /etc/pam.d/login
#%PAM-1.0
auth       required /lib/security/pam_securetty.so
auth       required /lib/security/pam_stack.so service=system-auth
auth       required /lib/security/pam_nologin.so
account    required /lib/security/pam_stack.so service=system-auth
password   required /lib/security/pam_stack.so service=system-auth
session    required /lib/security/pam_stack.so service=system-auth
session    optional /lib/security/pam_console.so

$ cat /etc/pam.d/system-auth
#%PAM-1.0
# This file is auto-generated.
# User changes will be destroyed the next time authconfig is run.
auth        required      /lib/security/pam_env.so
auth        sufficient    /lib/security/pam_unix.so likeauth nullok
auth        sufficient    /lib/security/pam_ldap.so use_first_pass
auth        required      /lib/security/pam_deny.so

account     required      /lib/security/pam_unix.so
#ajouter pour la connexion de compte locaux quand il n'y a pas de serveur LDAP
account     sufficient   /lib/security/pam_localuser.so
account     [default=bad success=ok user_unknown=ignore \
service_err=ignore system_err=ignore] /lib/security/pam_ldap.so

password    required      /lib/security/pam_cracklib.so retry=3 type=
password    sufficient    /lib/security/pam_unix.so nullok use_authtok md5 \ 
shadow
password    sufficient    /lib/security/pam_ldap.so use_authtok
password    required      /lib/security/pam_deny.so

session     required      /lib/security/pam_limits.so
session     required      /lib/security/pam_unix.so
session     optional      /lib/security/pam_ldap.so


7.5  NSS et Pam ldap, outils d'authentification système par LDAP

NSS et PAM ldap
Il existe deux outils sous unix pour authentifier via un annuaire ldap, il s'agit de nss_ldap et pam_ldap.
Le premier est utilisé par les services/programmes qui ne sont pas compatibles PAM; exemple /usr/bin/id qui fait un appel getpwent() (accès à /etc/passwd et /etc/shadow).
Les Name Service Switch (/etc/nsswitch.conf) redirigent cet appel vers différentes sources: files, nis, dns et grâce à nss_ldap vers ldap . Pour les applications compatibles PAM (pam-enabled) il est possible d'utiliser pam_ldap, cela permet d'individualiser le fichier de configuration d'accès à l'annuaire par service, donc d'utiliser aussi une méthode d'authentification différente suivant le service, il apporte pour cela d'autres méthodes d'authentification que crypt.

8  Mise en oeuvre de l'authentification système par LDAP

8.1  Tcpd

Certaines versions de packages sont ou peuvent être compilées avec la libwrap , donc utilise les tcp-wrappers. Il faut penser à configurer les fichiers de contrôle d'accès en conséquence.
$vi /etc/hosts.allow
slapd: .int-evry.fr
Sans cette ligne slapd, nous aurions un "access denied ".

8.2  lancement


$more /etc/rc.d/init.d/ldap

daemon ${slapd} -u ldap -f /etc/openldap/slapd.conf \
-h "ldap://ldap1.int-evry.fr:389/" -l LOCAL3 $OPTIONS $SLAPD_OPTIONS

Les log seront dans local3 redirigés vers /var/log/ldap.log par /etc/syslog.conf, le port standard en 389 sur le host ldap1.

8.3  configuration

Personnalisation du fichier de configuration slapd.conf

Ajouts aux directives par défaut:
#et les attributs/objectclass propre à l'INT
include  /etc/openldap/schema/int-evry.schema

#check du schema
schemacheck on

# The next line allows LDAPv2 bind requests, which are disabled by default.
allow bind_v2

#on augmente le nombre max d'entré retourné lors d'une requète 'large'
sizelimit 15000

#le directory et les index seront dans 
directory       /var/lib/ldap/int

# modif performance ed
cachesize 10000
dbcachesize 1500000


8.4  Mot de passe administrateur

Définition du password "root":
[root@openldap ~]
$perl -e 'print crypt("plaintext", "somerandomsalt"), qq(\n)'

$perl -e 'print crypt("padle", "gd"), qq(\n)'
gdRK6cNklXSV6

donc dans slapd.conf
rootdn          "cn=admin,dc=int-evry, dc=fr"
rootpw          {crypt}abcd123XQ1234

Il faut définir les droits unix sur ce directory personnalisé:
$chown ldap:ldap /var/lib/ldap/int

8.5  Première initialisation des données

Format utf8
Il faut avant tout convertir le fichier ldif en utf8 (accents sur ``télécom'' par exemple...)
[root@openldap /usr/local/Migratemci/Ldif]
$iconv arbre.ldif -f LATIN1 -t UTF8 -o arbre_utf8.ldif --verbose

Démarrage du serveur
On démarre le serveur afin d'y insérer les données.
$/etc/rc.d/init.d/ldap start
Starting slapd:                                            [  OK  ]

Ossature
On ajoute l'ossature du DIT (arbre).
[root@servfax /usr/local/Migratemci/Ldif]
$ldapadd -f arbre_utf8.ldif -x -D "cn=admin,dc=int-evry,dc=fr" -W  -p
389 -h ldap1
Enter LDAP Password: 
adding new entry "dc=int-evry,dc=fr"

adding new entry "ou=People,dc=int-evry,dc=fr"

adding new entry "ou=Group,dc=int-evry,dc=fr"

adding new entry "ou=Staff,ou=People,dc=int-evry,dc=fr"

adding new entry "ou=Students,ou=People,dc=int-evry,dc=fr"

adding new entry "ou=Stagiaires,ou=People,dc=int-evry,dc=fr"

adding new entry "ou=Thesards,ou=People,dc=int-evry,dc=fr"

Contrôle avec un browser LDAP
Visualisation avec le ldap browser (java): http://www.iit.edu/ gawojar/ldap
$cd /usr/local/ldapbrowser/
[root@ldap1 /usr/local/ldapbrowser]
$./lbe.sh 

8.6  Migration des Données système (nis) vers ldap

8.6.1  Users

Exemple restreint
Migration de /etc/passwd et /etc/shadow (depuis /var/yp/src/passwd et shadow.
[root@ldap1 /usr/local/MigrationToolsJP]
$./migrate_passwd_staff.pl ./Files/passwd.mci ./Ldif/passwd_mci.ldif

Le nom des fichiers est important (cf source perl des scripts). Le script déduit de la chaine passwd_staff le namingcontext du fichier migrate_common_mci.ph (gestion de l'ou=staff dans notre exemple de schéma personnalisé). La presence d'un fichier shadow (ici dans ./Files/shadow.mci) est importante aussi pour qu'il intègre les attributs d'authentification shadowaccount.

Exploitation
Finalement en exploitation nous utiliserons un schema standard (ou=people, puis tout les login à plat dessous): Migration depuis ypcat passwd.
[root@ldap1 /usr/local/Migratemci]
$./migrate_passwd.pl ./Files/yppasswd ./Ldif/people.ldif

Ajout des entrées utilisateurs
[root@ldap1 /usr/local/MigrationToolsJP]
$ldapadd -f ./Ldif/passwd_mci.ldif  -x -D "cn=admin,dc=int-evry,dc=fr"
-W  -p 389 -h ldap1
Enter LDAP Password: 
adding new entry "uid=gilles,ou=Staff,ou=People,dc=int-evry,dc=fr"

adding new entry "uid=petit,ou=Staff,ou=People,dc=int-evry,dc=fr"
...

8.6.2  Groups

Migration des groups

[root@ldap1 /usr/local/MigrationToolsJP]
$./migrate_group.pl ./Files/group ./Ldif/groups.ldif

Ajout à l'annuaire

[root@ldap1 /usr/local/MigrationToolsJP]
$ldapadd -f ./Ldif/groups.ldif  -x -D "cn=admin,dc=int-evry,dc=fr" -W -p
9009 -h ldap1
Enter LDAP Password: 
adding new entry "cn=admins,ou=Group,dc=int-evry,dc=fr"

adding new entry "cn=guests,ou=Group,dc=int-evry,dc=fr"

....

8.6.3  L'ensemble dans un fichier ldif global

Plutot que de migrer les fichiers sources un a un , l'esemble de la migration peut se faire via migrate_all_offline.sh.
[mciadmin@openldap /usr/local/Migratemci]
$./migrate_all_offline.sh 
Creating naming context entries...
Migrating aliases...
Migrating groups...
Migrating hosts...
Migrating networks...
Migrating users...
Migrating protocols...
Migrating rpcs...
Migrating services...
Migrating netgroups...
Importing into LDAP...
Migrating netgroups (by user)...
Migrating netgroups (by host)...
Preparing LDAP database...
/etc/openldap/slapd.conf: Permission denied
No databases found in config file

A partir de "Importing into LDAP" le script plante volontairement, dans la phase de debuggage nous voulions disposer d'un ficher ldif, puis de l'inserer dans l'annuaire mannuellement.
$ls -ltra /tmp | tail -1
-rw-------    1 mciadmin mciadmin  3578259 aoû  1 13:42 nis.ldif.DRnZGR
$more /tmp/nis.ldif.DRnZGR 
dn: dc=int-evry,dc=fr
dc: int-evry
objectClass: top
objectClass: domain
objectClass: domainRelatedObject
associatedDomain: int-evry.fr

dn: ou=People,dc=int-evry,dc=fr
ou: People
objectClass: top
objectClass: organizationalUnit
objectClass: domainRelatedObject
associatedDomain: int-evry.fr

dn: ou=Rpc,ou=System,dc=int-evry,dc=fr
ou: Rpc,ou
objectClass: top
objectClass: organizationalUnit
objectClass: domainRelatedObject
associatedDomain: int-evry.fr
.....

Il ne reste plus qu'a faire l'insertion manuelle:
$ldapadd -f/tmp/nis.ldif.DRnZGR -x -W -D"cn=admin,dc=int-evry,dc=fr" -h ldap1
ldap_bind:
....

8.7  Authentification système

8.7.1  Installation

Installation de nss_ldap et éventuellement pam_ldap. Avec la distribution RedHat, le module pam_ldap est intégré dans le package nss_ldap.
$ rpm -qli nss_ldap-198-3 | grep pam_ldap
This package includes two LDAP access clients: nss_ldap and pam_ldap.
/lib/security/pam_ldap.so


des exemples de configuration pam pour les services sont disponibles:

/usr/share/doc/nss_ldap-198/pam.d/login
/usr/share/doc/nss_ldap-198/pam.d/passwd
/usr/share/doc/nss_ldap-198/pam.d/pop
....


Pour connaitre la version de pam_ldap incluse dans le package nss_ldap RedHat:

$ rpm -q nss_ldap --changelog | grep pam_ldap
- update to nss_ldap 197, pam_ldap 150
...

[root@servfax /etc]
$vi ldap.conf

host ldap1.int-evry.fr ldap2.int-evry.fr
base dc=int-evry,dc=fr
ldap_version 3
port 389
binddn cn=binduser,ou=system,dc=int-evry,dc=fr
bindpw secret
pam_password crypt         #pour la commande/usr/bin/passwd.
pam_filter IntEPersInetServ=*unix-int* #filtre propre à l'INT

[root@servfax /etc]
$vi nsswitch.conf
passwd:     files nisplus ldap
shadow:     files nisplus ldap
group:      files nisplus ldap
automount:  files nisplus ldap

8.7.2  Exemple d'authentification

$telnet servfax
Connected to servfax.int-evry.fr.
Escape character is '^]'.

Red Hat Linux release 7.2 (Enigma)
Kernel 2.4.14 on an i686
login: procacci
Password: 
Last login: Sun Jan 27 18:06:53 on :0
No directory /mci/mci/procacci!
Logging in with home = "/".
ksh-2.04$

Sans automount pas de home directory ! cf ci-dessous pour l'automount.

8.8  Automount (NEW)

8.8.1  Principes

Nous prenons ici l'option de ne monter que le homedir complet d'un utilisateur et non son directory parent. Cette implémentation non documentée a été réalisé en étroite collaboration avec Nalin Dahyabhai <nalin@redhat.com>. Cela suppose de créer un automounter "master", exemple /citi ici, qui lancera un automounter par sous-répertoire; /citi/citi1 ou /citi/citi2, enfin un wildcard sur cn=/ montera le directory prorpre à l'utilisateur; /citi/citi1/testciti par exemple.
Après modification de migrate_automount.pl nous arrivons à ce qui est décrit ci-dessus.

8.8.2  Mise en oeuvre


[mciadmin@openldap /usr/local/Migratemci]
$./migrate_automount.pl /usr/local/Migratemci/Files/auto.citi \
./Ldif/auto-citi-tree.ldif
$more ./Ldif/auto-citi-tree.ldif 

#The auto.master
dn: ou=auto.master,dc=int-evry,dc=fr
objectClass: top
objectClass: automountMap
ou: auto.master

#auto.master  points to cn /citi, that latest managing subdirectories
dn: cn=/citi,ou=auto.master,dc=int-evry,dc=fr
objectClass: top
objectClass: automount
automountInformation: ldap:ou=auto.citi,ou=automount,dc=int-evry,dc=fr
cn: /citi

# This entry is more or less a place-holder for automount entries for directories \
#which get mounted under /citi.
dn: ou=auto.citi,ou=automount,dc=int-evry,dc=fr
objectClass: top
objectClass: organizationalUnit
ou: auto.citi

# This entry causes autofs to start up another automounter on /citi/citi1.
Dn: cn=citi1,ou=auto.citi,ou=automount,dc=int-evry,dc=fr
objectClass: top
objectClass: automount
cn: citi1
automountInformation: -fstype=autofs \
ldap:ou=auto.citi.citi1,ou=auto.citi,ou=automount,dc=int-evry,dc=fr

# This entry is more or less a place-holder for automount entries for directories \
#which get mounted under /citi/citi1.
dn: ou=auto.citi.citi1,ou=auto.citi,ou=automount,dc=int-evry,dc=fr
objectClass: top
objectClass: organizationalUnit
ou: auto.citi.citi1

# This is a wildcard entry for any user whose home directory is under
# /citi/citi1
dn: cn=/,ou=auto.citi.citi1,ou=auto.citi,ou=automount,dc=int-evry,dc=fr
objectClass: top
objectClass: automount
cn: /
automountInformation: -rw,intr,soft,quota nfsserver:/home/&


8.8.3  Configuration système

Ici nous prenons en compte la presence de replicas. On trouvera dans /etc/ldap.conf la liste des serveurs LDAP à interroger pour l'authentification pam, et dans /etc/openldap/ldap.conf la liste des serveurs LDAP (les même) pour automount , ainsi que pour les outils shell openldap (ldapsearch, ldapmodify ...) .
$grep host /etc/ldap.conf
host ldap1.int-evry.fr ldap2.int-evry.fr

$grep HOST /etc/openldap/ldap.conf
HOST ldap1.int-evry.fr ldap2.int-evry.fr

8.8.4  Exemple d'utilisation


#L'utilisateur testciti (nfs-automount homedir) se connecte:
$ pwd
/citi/citi1/testciti

#Autofs status
$ /etc/init.d/autofs status
Configured Mount Points:
------------------------
/usr/sbin/automount /citi ldap ou=auto.citi,ou=automount,dc=int-evry,dc=fr

Active Mount Points:
--------------------
/usr/sbin/automount /citi ldap ou=auto.citi,ou=automount,dc=int-evry,dc=fr
/usr/sbin/automount --submount /citi/citi1 ldap \
ou=auto.citi.citi1,ou=auto.citi,ou=automount,dc=int-evry,dc=fr

ldap log

Mar 14 11:19:52  corne automount[2670]: starting automounter version 3.1.7, \
path = /mci/mci, maptype = ldap, mapname = \
ou=auto.citi.citi1,ou=automount,dc=int-evry,dc=fr
Mar 14 11:19:52 corne automount[2670]: Map argc = 1
Mar 14 11:19:52 corne automount[2670]: Map argv[0] = \
ou=auto.citi.citi1,ou=automount,dc=int-evry,dc=fr
Mar 14 11:19:52 corne automount[2670]: lookup(ldap): server = "(default)", \
base dn = "ou=auto.citi.citi1,ou=automount,dc=int-evry,dc=fr"


8.8.5  Contrôle d'accès aux stations et serveurs NFS

Sur le principe décrit dans la section suivante ``contrôle d'acces aux services'', on ajoute un filtre pam dans le fichier /etc/ldap.conf pour contrôler les accès login et NFS sur le stations. Ce filtre diffèrera suivant que l'on se trouve sur une station salles TP ou une station dans un département par exemple:
stations département citi
$ grep pam_filter /etc/ldap.conf
pam_filter IntEPersInetServ=*unix-citi*

stations salles TP
$ grep pam_filter /etc/ldap.conf
pam_filter IntEPersInetServ=*unix-int*

8.8.6  Gestion de differents homedir

L'objectif est de mettre à disposition des utlisateurs des répertoires d'accueil (homedir) sur different serveurs NFS. Le serveur NFS (homedir) sera choisi en fonction de la localité de la station de travail où l'utilisteur se connecte, dans un département ou en salle de TP par exemple.
#A user ldap entry can contain 2 (or more ) homeDirectory attributes
homeDirectory: /mci/citi/testciti
homeDirectoryDept: /citi/citi1/testciti

#On departement citi client machines, the homeDirectory attr is remapped, 
#so that users get their homes from the citi nfsserver.
$ grep homeDirectoryDept /etc/ldap.conf
nss_map_attribute       homeDirectory           homeDirectoryDept
$ ssh testciti@citi_client_machine
$ pwd
/citi/citi1/testciti

#On departement mci client machines, traditional homeDirectory attr is seacrh,\
#they get their homes from mci nfsserfer (cf ldap automount maps)
$ ssh testciti@mci_client_machine
$ pwd
/mci/citi/testciti


8.9  Contrôle d'accès host

On desire contrôler sur quels hosts un utilisateur peut se connecter. On peut utiliser l'attribut host (objectclass account de cosine.schema) pour cela, il suffit de lister les serveurs (un par occurence de l'attribut) sur lesquels un utilisateurs peut se connecter. Restera sur les serveurs en question à definir un filtre sur leur propre nom dans /etc/ldap.conf. Depuis pam_ldap 130 voir l'option pam_check_host_attr. Entre les versions pam_ldap 125 et 129, la simple présence de l'attribut host dans une entrée ldap impossait le contrôle du nom de host sur lequel l'utilisateur se connecte.

8.9.1  exemple d'entrée ldif


$ ldapsearch -h ldap1 -p 9009 -x -b"dc=int-evry,dc=fr" "(host=*)"
....
objectClass: person
objectClass: organizationalPerson
objectClass: inetOrgPerson
objectClass: account
objectClass: posixAccount
objectClass: top
objectClass: shadowAccount

....
host: mci21056.int-evry.fr
host: servfax.int-evry.fr
#host: *.int-evry.fr #* dans l'entree ne fonctionne pas 
#en revanche cela fonctionne dans le filtre /etc/ldap.conf

8.9.2  Configuration du serveur

Version ancienne de pam_ldap(< 125 ?)
[root@mci21056 /usr/local/Migratemci/Ldif]
$ more /etc/ldap.conf
...
# Filter to AND with uid=%s
#pam_filter objectclass=account
pam_filter host=servfax.int-evry.fr
...

Puis, contrôle par défaut pour les versions pam_ldap 124 à 129, depuis la version pam_ldap 130 utilisation de la directive pam_check_host_attr dans /etc/ldap.conf.

8.9.3  ACL

Du fait de l'empilement des modules pam, le module pam_unix basé sur les nss, contourne le filtre pam_ldap definit dans /etc/ldap.conf. En effet , nss_ldap va directement lire l'annuaire te ne tiens pas compte des filtres de pam_ldap, ainsi l'utilisateur sera authentifié par pam_unix, même si pam_ldap échoue grâce au contrôle d'accès host !. Il faut alors interdir à nss_ldap d'authentifier en tant qu'anonymous (car le bind ce fait en anonymous au login). Pour cela on spécifie des ACL dans le slapd.conf afin de ne pas permettre au compte anonymous l'accès read sur l'attribut userpassword mais seulement le droit auth (nécessaire pour faire un bind).
access to attr=userPassword
 by self write
 by anonymous auth
 by dn="cn=root,dc=int-evry,dc=fr" write
 by * none

access to * 
 by self write
 by dn="cn=root,dc=int-evry,dc=fr" write
 by * read

8.9.4  démonstration

Avant et après changement des ACL.
[procacci@mci21056 ~]
$su - testlinu
Password: 
su: incorrect password
#changement ACL
[procacci@mci21056 ~]
$su - testlinu
Password: 
ksh-2.04$

Remarque: le message d'erreur su: incorrect password peut être trompeur !
Les log confirment le filtre demandé dans /etc/ldap.conf:
Jun 25 14:22:08 mci21056 slapd[8205]: conn=1 op=1 SRCH base="dc=int-evry,dc=fr" 
scope=2 filter="(&(host=servfax.int-evry.fr)(uid=testlinu))"

9  Contrôle d'accés aux services (NEW)

Ici nous gérons l'accés aux différents services basés sur ldap (connexion unix, mountage nfs, accés web, accés messagerie etc ...) par le biais d'un attribut (perso) multivalué (IntEPersInetServ)qui liste l'ensemble des services ouverts à un individu:
$ ldapsearch -x uid=testciti -D "cn=admin,dc=int-evry,dc=fr" -W  IntEPersInetServ -LLL
Enter LDAP Password:
dn: uid=testciti,ou=People,dc=int-evry,dc=fr
IntEPersInetServ: unix-int mail-int unix-citi unix-citi cours

Dans cet exemple, l'individu a accés aux services de connexion et de disque (NFS) sur le machines unix central (unix-int) , au mail central (mail-int) , aux services de connexion et disque du département citi (unix-citi) et enfin à la publication de cours en ligne (cours-int).

C'est un filtre (SubStringMatch !) sur une des valeures de cet attribut qui donnera ou non accés au service. Pour pam le filtre est realisé dans /etc/ldap.conf , pour le web dans la configuration du module apache en question, et pour toute application interne -> à définir dans le code source !. La plupart des applications ``ldap enable'' proposants l'ajout d'un filtre aux requètes de base, nous pouvons largement étendre les fonctionnalités de cet attribut.

Avertissement
La combinaison de pam et nss ldap peut provoquer des résultats inattendus. Par exemple pam_unix peut valider une connexion via ldap (/etc/nsswitch.conf -> passwd: ldap) alors que l'on voulais forcer un filtre via pam_ldap (/etc/ldap.conf ->pam_filter IntEPersInetServ=*unix-int*). Cela impose donc une grande rigueur sur l'empilement des modules dans pam et sur la qualité des controles qu'on leur applique (required, sufficient ....).

10  Replication

10.1  Compte de replication

Nous utiliserons un compte pour la replication different du rootdn.
$ cat Replicator.ldif 
dn: cn=replicator,ou=System,dc=int-evry,dc=fr
cn: replicator
sn: REPLICATOR SN
objectClass: person
userPassword: {crypt}fgtyp5LztXllw

$ ldapadd -f ./Replicator.ldif -x -D"cn=admin,dc=int-evry,dc=fr" -W -h corne
Enter LDAP Password: 
adding new entry "cn=replicator,ou=System,dc=int-evry,dc=fr"

10.2  Configuration du maître

Les sections ACL et database du fichier de configuration slapd sont modifiés. attention dans la section replica avec une bindmethod=simple (ce serai different avec kerberos) le mot de passe qui suit dans credentials doit être en clair !.
$ cat /etc/openldap/slapd.conf

access to attr=userPassword
 by self write
 by anonymous auth
 by dn="cn=admin,dc=int-evry,dc=fr" write
 by dn="cn=replicator,ou=System,dc=int-evry,dc=fr" write
 by * none
access to * 
 by self read
 by dn="cn=admin,dc=int-evry,dc=fr" write
 by dn="cn=replicator,ou=System,dc=int-evry,dc=fr" write
 by * read


database ldbm
suffix  "dc=int-evry, dc=fr"
rootdn  "cn=admin, dc=int-evry, dc=fr"
rootpw  {crypt}REYTjkmLCELfk
directory /var/lib/ldap/int
replica  host=esclave.int-evry.fr
  binddn="cn=replicator,ou=System,dc=int-evry,dc=fr"
  bindmethod=simple   credentials=pass
replogfile /var/lib/ldap/replica/replogfile
index objectClass,uid,uidNumber,gidNumber,IntEPersInetServ eq
index cn,mail,surname,givenname  eq,subinitial

10.3  Configuration de l'esclave

Sur l'esclave on modifie également les ACL pour donner tous les droits au compte de réplication. la section database est modifiée afin de prendre en compte le dn du compte de replica definit dans la section replica du maitre (binddn=) .
$ cat /etc/openldap/slapd.conf
#ACL
access to ...
 ...
 by dn="cn=replicator,ou=System,dc=int-evry,dc=fr" write
 ...

database ldbm
suffix  "dc=int-evry, dc=fr"
rootdn  "cn=admin, dc=int-evry, dc=fr"
rootpw  {crypt}REYTjkmLCELfk
directory /var/lib/ldap/int
updatedn "cn=replicator,ou=System,dc=int-evry,dc=fr"
updateref "ldap://maitre.int-evry.fr:389"
index objectClass,uid,uidNumber,gidNumber,IntEPersInetServ eq
index cn,mail,surname,givenname  eq,subinitial

10.4  Mise en place

Apres avoir prédéfinis les fichiers de configuration ci-dessus, et crée le compte de réplication, il faut maintenant arrêter le serveur maître afin de récuperer la base ldap.
$ /etc/init.d/ldap stop
Arrêt de slapd :                                           [  OK  ]
On récupère les fichiers de la base ldap du maître sur l'esclave
$ cd /var/lib/ldap/int
$ ftp maitre
>cd /var/lib/ldap/int
>mget *.dbb # dans le cas d'un ldap utilisant db3 au lieu de gdbm !
>bye
$ chown ldap:ldap *.dbb

Enfin on relance ldap sur le maître et sur l'esclave.
#Maître
$ /etc/init.d/ldap start
Starting slapd:                                            [  OK  ]
Starting slurpd:                                           [  OK  ]
#Esclave
$ /etc/init.d/ldap start
Starting slapd:                                            [  OK  ]

10.5  Démonstration

10.5.1  Lancement d'une modification sur le maître.


#Maître
$ ldapmodify -f ./replace-host.ldif -x -W -D "cn=admin,dc=int-evry,dc=fr" 
Enter LDAP Password: 
modifying entry "uid=test,ou=People,dc=int-evry,dc=fr"
#log Maître
Jan 25 18:39:39 openldap slapd[5988]: daemon: conn=52 fd=24 connection from 
IP=157.159.10.16:33986 (IP=157.159.10.16:34049) accepted. 
Jan 25 18:39:39 openldap slapd[5995]: conn=52 op=0 BIND 
dn="CN=ADMIN,DC=INT-EVRY,DC=FR" method=128 
Jan 25 18:39:39 openldap slapd[5995]: conn=52 op=0 RESULT tag=97 err=0 text= 
Jan 25 18:39:39 openldap slapd[6003]: conn=52 op=1 
MOD dn="uid=test,ou=People,dc=int-evry,dc=fr" 
Jan 25 18:39:40 openldap slapd[6003]: conn=52 op=1 RESULT tag=103 err=0 text= 
Jan 25 18:39:40 openldap slapd[5995]: conn=52 op=2 UNBIND 
Jan 25 18:39:40 openldap slapd[5995]: conn=-1 fd=24 closed 

#log esclave
Jan 25 18:39:40 ur slapd[6369]: conn=1 op=5 
MOD dn="uid=test,ou=People,dc=int-evry,dc=fr" 
Jan 25 18:39:41 ur slapd[6369]: conn=1 op=5 RESULT tag=103 err=0 text=

10.5.2  Fichiers de replication, explication

Explication: Merci à Vincent MATHIEU <Vincent.Mathieu@univ-nancy2.fr>
1
slurpd utilise comme répertoire de travail le sous-répertoire replica du répertoire précisé par l'option -t du lancement du démon.
2
slapd met les informations de modification dans le fichier indiqué par la directive replogfile du fichier de conf.
3
slurpd puise ses infos et vide ce fichier, qui sert ainsi de communicationentre les 2 processus. Ca permet un fonctionnement asynchrone, et l'arrêt-relance non synchronisé des 2 processus.
4
slurpd inscrit les infos de mises à jour dans le fichier slurp.replog de sonrépertoire de travail, avec un marqueur de temps (time). C'est un fichier qui s'agrandit indéfiniment.
5
slurpd tente de mettre à jour les réplicas. En cas d'erreur, il va créer un fichier de nom NomDuReplica.rej (reject). Il est possible ultérieurement de rejouer ce fichier des rejets.
6
Il met à jour à chaque traitement le fichier slurpd.status. Ce fichier contient une ligne par esclave. la ligne comprend un indicateur de temps identique au marqueur de temps du fichier slurpd.replog. C'est grâce à ce fichier qu'il sait pour chaque esclave où il en est des mises à jour.
A noter que le fichier des reject n'est mis à jour que lors de tentative de maj défectueuse d'un esclave LDAP (par exemple, incohérence dans les schémas, ...); si l'esclave n'est pas dispo, ce n'est pas considéré comme une erreur, slurpd tentera une mise à jour plus tard pour cet esclave.

A noter également qu'il faut prévoir une moulinette qui nettoie de temps en temps le fichier slurpd.replog.
[root@openldap /var/lib/ldap/replica]
$ ls -al
total 16
drwxr-xr-x    2 root     root         4096 Jan 25 17:29 .
drwx------    7 ldap     ldap         4096 Jan 25 17:08 ..
-rw-r--r--    1 ldap     root            0 Jan 25 18:39 replogfile
-rw-r--r--    1 ldap     root            0 Jan 25 18:39 replogfile.lock
-rw-r--r--    1 root     root         3678 Jan 25 18:39 slurpd.replog
-rw-r--r--    1 root     root            0 Jan 25 18:39 slurpd.replog.lock
-rw-r--r--    1 root     root           30 Jan 25 18:39 slurpd.status
-rw-r--r--    1 root     root            0 Jan 25 18:34 slurpd.status.lock
[root@openldap /var/lib/ldap/replica]
$ tail -14 slurpd.replog
replica: ur.int-evry.fr
time: 1011980380
dn: uid=test,ou=People,dc=int-evry,dc=fr
changetype: modify
replace: host
host: hostname0.int-evry.fr
-
replace: modifiersName
modifiersName: cn=admin, dc=int-evry, dc=fr
-
replace: modifyTimestamp
modifyTimestamp: 20020125173939Z
-
[root@openldap /var/lib/ldap/replica]
$ cat slurpd.status
ur.int-evry.fr:0:1011980380:0

10.5.3  Rejouer une réplication rejetée

Il se peut que la réplication ne se fasse pas, c'est le cas par exemple quand on modifie le schema sur le master pour ajouter de nouvelles objectclass/attribut et qu'on ne le fait pas sur le slave !.
[root@corbeau /var/lib/ldap/replica]
-rw-r-----    1 root     root         2990 Mar 13 19:42 corne.int-evry.fr:0.rej
[root@corbeau /var/lib/ldap/replica]
$ more corne.int-evry.fr:0.rej
ERROR: Undefined attribute type
replica: corne.int-evry.fr:0
time: 1015551248.0
dn: uid=doutrele,ou=People,dc=int-evry,dc=fr
changetype: modify
add: maildeliveryoption
maildeliveryoption: mailbox
-
replace: modifiersName
modifiersName: cn=admin, dc=int-evry, dc=fr
-
replace: modifyTimestamp
modifyTimestamp: 20020308013406Z
-
$ slurpd -r ./corne.int-evry.fr\:0.rej -o         
Processing in one-shot mode:
9 total replication records in file,
9 replication records to process.

ici un problème de timestamp lié à une difference de date entre le master et le slave ainsi qu'une application peut-etre trop tardive du fichier, refuse de repliquer ... !? .
[root@corbeau /var/lib/ldap/replica]
$ slurpd -r ./corne.int-evry.fr\:0.rej -o -d 65535
Config: opening config file "/etc/openldap/slapd.conf"
Config:....
...
begin replication thread for corne.int-evry.fr:0
Replica corne.int-evry.fr:0, skip repl record for uid=doutrele,ou=People,dc=int-evry,dc=fr (old)
.... 9 fois ...
end replication thread for corne.int-evry.fr:0
slurpd: terminated.

10.6  Configuration PAM

L'une des utilisations d'un réplica, est de permettre aux clients de s'authentifier sur l'un ou l'autre des serveurs. Cela repond au problême de panne, et permet aussi en exploitation normale de répartir la charge. En ce qui concerne PAM, il suffit de modifier le fichier de configuaration /etc/ldap.conf en conséquence:
$ cat /etc/ldap.conf | grep host
host openldap.int-evry.fr ur.int-evry.fr
il suffit donc lister les serveurs ldap (séparés par un espace) dans la directive host de /etc/ldap.conf.

Pour d'autres programmes comme les commandes shell openldap (ldapsearch, ldapmodify ..) ou automount par exemple, c'est /etc/openldap/ldap.conf qui sera lu !
$ grep HOST /etc/openldap/ldap.conf
HOST openldap.int-evry.fr ur.int-evry.fr

11  Réplication Partielle (NEW)

11.1  Principes

Il peut être interessent d'avoir un replica contenant seulement une partie du DIT et/ou une partie des atrributs/objectclass du master. Dans cet exemple nous allons créer un replica dédié pages blanches, contenant uniquement la branche ou=people,dc=int-evry,dc=fr dépourvue des fonctionnalitées authentification système unix, donc dépourvue des objectclass PosixAccount et shadowAccount ainsi que les attributs associés (sauf ceux necessaires ``MUST'' des autres objectclass; exp: uid !).

11.2  Initialisation du replica

11.3  Compte de replication partiel

Insertion du compte de replication partiel dans l'oberescence réduite à ou=people.
$ cat /usr/local/Migratemci/Ldif/replicator-partial.ldif
dn: ou=System,ou=people,dc=int-evry,dc=fr
ou: System
objectClass: top
objectClass: organizationalUnit

dn: cn=replicator,ou=System,ou=people,dc=int-evry,dc=fr
objectClass: person
sn: REPLICATOR SN
cn: replicator
userPassword:: e0NSWVBUfVg4aHUseU9UUjRlb1E=
$slapadd -f /etc/openldap/slapd.conf -l Ldif/replicator-partial.ldif

11.4  Extraction du subtree ou=people

Le replica partiel doit être initialialisé avec une base partielle !. On va donc extraire du master un ldif contenant uniquement l'arborescence desirée. (l'option -s de slapcat est un patch de la commande originale, je pense qu'elle devrait etre intégré dans les futures versions openldap, cf liste openldap-software ).
$ /usr/sbin/slapcat -b dc=int-evry,dc=fr -s ou=people,dc=int-evry,dc=fr \
-f /etc/openldap/slapd.conf > dump-ou=people.ldif
Puis on retire les attributs non desirables sur le replica pages blanches:
$ cat remove-posix-account.pl
#!/usr/bin/perl

open (F1,"<dump-ou=people.ldif") || die " pb $!";
open (FOUT,">dump-no-posix.ldif");
while ($ligne1 = <F1>) {
   if ($ligne1 =~ /Account|uidNumber|gidNumber|loginShell|homeDirectory|\
gecos|shadowLastChange/  )
        {next;}
        else {
        print FOUT ( $ligne1 );}
}
$ ./remove-posix-account.pl

11.5  Initialisation du replica

Sur le replica on récupère le fichier ldif travaillés ci-dessus et on crée la base ldap:
sftp> get dump-no-posix.ldif
[root@ldaptux /var/lib/ldap/int-evry]
$ slapadd -f /etc/openldap/slapd_int-evry.conf -l ./dump-no-posix.ldif
$ chown ldap:ldap *

11.6  Configuration du master

database        bdb
suffix          "dc=int-evry, dc=fr"
rootdn          "cn=root, dc=int-evry, dc=fr"
rootpw          {crypt}rfG0trfN67nDn6
directory       /var/lib/ldap/int
#cachesize      6000
#checkpoint      100000 360
#dbnosync
#readonly       on
replica         host=ldaptux.int-evry.fr:3006
#suffix limité a ou=people
                suffix="ou=people,dc=int-evry,dc=fr"
#on retire les objectclass/attributs indésirables
                attr!="posixAccount,shadowAccount,loginShell,homeDirectory,\
uidNumber,gidNumber,gecos"
                binddn="cn=replicator,ou=System,ou=people,dc=int-evry,dc=fr"
                bindmethod=simple   credentials=secret
replogfile /var/lib/ldap/replica/replogfile

11.7  Configuration du replica partiel

access to attr=userPassword
        by self write
        by anonymous auth
        by dn="cn=replicator,ou=System,ou=people,dc=int-evry,dc=fr" write
        by * none

access to *
        by self read
        by dn="uid=procacci,ou=people,dc=int-evry,dc=fr" write
        by dn="cn=replicator,ou=system,ou=people,dc=int-evry,dc=fr" write
        by * none

schemacheck on

database        bdb
suffix          "ou=people,dc=int-evry,dc=fr"
rootdn          "uid=procacci,ou=people,dc=int-evry,dc=fr
rootpw          {crypt}ttG0lY6U7nDn6
directory       /var/lib/ldap/int-evry
updatedn "cn=replicator,ou=system,ou=people,dc=int-evry,dc=fr"
updateref "ldap://corbeau.int-evry.fr:389"
#cachesize       100
#checkpoint      10 5
index   objectClass,uid,uidNumber,gidNumber,IntEPersInetServ    eq
index   cn,mail,surname,givenname               eq,subinitial

11.8  Demarrage et test

[root@ldaptux /var/lib/ldap/int-evry]
$ /etc/init.d/ldap_int-evry start

Modification des attributs telephoneNumber et loginShell sur le master slapd logs:
Master
Mar  7 18:12:36 corbeau slapd[4796]: conn=8 op=5 MOD dn="uid=test, ou=People, \
dc=int-evry,dc=fr"
Mar  7 18:12:36 corbeau slapd[4796]: conn=8 op=5 MOD attr=telephoneNumber \
loginShell userPassword
Mar  7 18:12:36 corbeau slapd[4796]: conn=8 op=5 RESULT tag=103 err=0 text=
Mar  7 18:12:36 corbeau slapd[4806]: conn=8 op=6 SRCH base="uid=test,ou=People,\
dc=int-evry,dc=fr" scope=0 filter="(objectClass=*)"
Mar  7 18:12:36 corbeau slapd[4806]: conn=8 op=6 SEARCH RESULT tag=101 err=0 \
nentries=1 text=

replica
Mar  7 18:47:44 ldaptux slapd[22618]: conn=0 op=3 MOD dn="uid=test,ou=People,\
dc=int-evry,dc=fr"
Mar  7 18:47:44 ldaptux slapd[22618]: conn=0 op=3 RESULT tag=103 err=0 text=

[root@corbeau /var/lib/ldap/replica]
tail slurpd.replog

replica: ldaptux.int-evry.fr:3006
time: 1047057156
dn: uid=test,ou=People,dc=int-evry,dc=fr
changetype: modify
replace: telephoneNumber
telephoneNumber: 4408
-
replace: entryCSN
entryCSN: 2003030717:12:36Z#0x0001#0#0000
-
replace: modifiersName
modifiersName: cn=root,dc=int-evry,dc=fr
-
replace: modifyTimestamp
modifyTimestamp: 20030307171236Z
-

Verification
$ ldapsearch -x uid=test -h corbeau -D "uid=test,ou=people,dc=int-evry,dc=fr" \
-W telephoneNumber loginShell -LLL
Enter LDAP Password:
dn: uid=test,ou=People,dc=int-evry,dc=fr
telephoneNumber: 4408
loginShell: /usr/local/bin/bash
$ ldapsearch -x uid=test -h ldaptux -p 3006 -b ou=people,dc=int-evry,dc=fr \
-D "uid=test,ou=people,dc=int-evry,dc=fr" -W telephoneNumber loginShell -LLL
Enter LDAP Password:
dn: uid=test,ou=People,dc=int-evry,dc=fr
telephoneNumber: 4408
Ok :-)

12  Groups

Mise en place de groupes afin de déléguer l'administration des données de l'annuaire.

12.1  Création des groupes

Définition ldif de 2 groupes afin de gérer d'une part les attributs concernants les services géneraux, d'autre part les attributs relatifs aux Ressources Humaines.
$ cat group-servG.ldif
dn: cn=servG,ou=Groups,dc=int-evry,dc=fr
cn: administrateurs services generaux
objectclass: groupOfNames
objectclass: top
member: uid=test,ou=people,dc=int-evry,dc=fr
member: uid=riquet,ou=people,dc=int-evry,dc=fr

$ cat group-RH2.ldif
dn: cn=RH2,ou=Groups,dc=int-evry,dc=fr
cn: administrateurs RH2
objectclass: groupOfNames
objectclass: top
Member: uid=savoye,ou=People,dc=int-evry,dc=fr
Member: uid=herve,ou=People,dc=int-evry,dc=fr

$ ldapadd -x -D "cn=admin,dc=int-evry,dc=fr" -W -f ./group-servG.ldif
$ ldapadd -x -D "cn=admin,dc=int-evry,dc=fr" -W -f ./group-RH2.ldif

12.2  ACL

Définition dans le fichier slapd.conf d'ACL utilisants ces groupes. Ici on autorise le groupe servG à modifier le numero de téléphone, la pièce, le code postal et l'adresse postale, de même le groupe RH pourra modifier le type d'employé, le département ... .
access to dn="ou=people,dc=int-evry,dc=fr"
        attrs=telephoneNumber,roomNumber,postalCode,postalAddress
        by group="cn=servG,ou=Groups,dc=int-evry,dc=fr" write
        by dn="cn=admin,dc=int-evry,dc=fr" write
        by dn="cn=replicator,ou=System,dc=int-evry,dc=fr" write
        by * read

access to dn="ou=people,dc=int-evry,dc=fr"
        attrs=employeeType,departmentNumber,title,cn,givenName,sn
        by group="cn=RH2,ou=Groups,dc=int-evry,dc=fr" write
        by dn="cn=admin,dc=int-evry,dc=fr" write
        by dn="cn=replicator,ou=System,dc=int-evry,dc=fr" write
        by * read


12.3  Exemple d'utilisation

Ici un employé des ressources humaines va s'authentifier sur le serveur ldap (bind individuel) pour modifier le titre d'une personne et ajouter l'attribut de département (détruit au préalable, cf # du fichier ldif) de cette personne.
$ cat modify-rh.ldif 
dn: uid=procacci,ou=People,dc=int-evry,dc=fr
changetype: modify
replace: title
title: Ingenieur
#-
#delete: departmentNumber
-
add: departmentNumber
departmentNumber: MCI
#-
#changetype: modify
#replace: mailHost
#mailHost: bidon


$ ldapmodify -x -D "uid=herve,ou=people,dc=int-evry,dc=fr" -W -f ./modify-rh.ldif -h corbeau
Enter LDAP Password: 
modifying entry "uid=procacci,ou=People,dc=int-evry,dc=fr"

$ldapsearch -x uid=procacci departmentNumber title
dn: uid=procacci,ou=People,dc=int-evry,dc=fr
title: Ingenieur
departmentNumber: MCI


Si on revient sur le fichier modify-rh.ldif ci dessus, en décommentant le remplacement du mailHost, attribut pour lequel le groupe RH n'a pas le droit d'écriture (cf ACL), le serveur répond ldap_modify: Insufficient access et annule entierement l'opération (pas de modification du title).

13  Trucs et astuces

13.1  Index

Remarque sur l'indexation à posteriori d'un annuaire.

ajouter une indexation après avoir "populé" la base: il faut detruire les fichier d'index existant, puis lancer slapindex. Il est recommendé d'arreter le serveur ldap pendant cette opération
slapd.conf
#ajout des index sur sn,mail en pres et sub
index   cn,sn,mail              pres,eq,sub
#ou dans l'exemple ci-dessus, suite a des pb d'index, reconstruction totale:
index   objectClass,uid,uidNumber,gidNumber,IntEPersInetServ    eq
index   cn,mail,surname,givenname               eq,subinitial

$ /etc/init.d/ldap stop
Arrêt de slapd :                                           
[root@ur /var/lib/ldap/int]
$ ls -al
total 25128
drwxr-xr-x    2 ldap     ldap         4096 aoû  1 17:07 .
drwx------    3 ldap     ldap         4096 jan 25  2002 ..
-rw-r--r--    1 ldap     ldap      1617920 aoû  1 17:56 cn.dbb
-rw-r--r--    1 ldap     ldap       827392 aoû  1 15:39 dn2id.dbb
-rw-r--r--    1 ldap     ldap        49152 aoû  1 17:56 gidNumber.dbb
-rw-r--r--    1 ldap     ldap       536576 aoû  1 17:56 givenName.dbb
-rw-r--r--    1 ldap     ldap     19894272 aoû  1 17:56 id2entry.dbb
-rw-r--r--    1 ldap     ldap        20480 aoû  1 17:56 IntEPersInetServ.dbb
-rw-r--r--    1 ldap     ldap      1306624 aoû  1 17:56 mail.dbb
-rw-r--r--    1 ldap     ldap         8192 aoû  1 15:39 nextid.dbb
-rw-------    1 ldap     ldap       253952 aoû  1 17:56 objectClass.dbb
-rw-r--r--    1 ldap     ldap       876544 aoû  1 17:56 sn.dbb
-rw-r--r--    1 ldap     ldap       139264 aoû  1 17:56 uid.dbb
-rw-r--r--    1 ldap     ldap       135168 aoû  1 17:56 uidNumber.dbb
[root@ur /var/lib/ldap/int]
$ rm cn.dbb gidNumber.dbb givenName.dbb IntEPersInetServ.dbb mail.dbb  \
objectClass.dbbsn.dbb uid.dbb uidNumber.dbb
[root@ur /var/lib/ldap/int]
$ ls -ltra
total 20280
drwx------    3 ldap     ldap         4096 jan 25  2002 ..
-rw-r--r--    1 ldap     ldap         8192 aoû  1 15:39 nextid.dbb
-rw-r--r--    1 ldap     ldap       827392 aoû  1 15:39 dn2id.dbb
-rw-r--r--    1 ldap     ldap     19894272 aoû  1 17:56 id2entry.dbb
drwxr-xr-x    2 ldap     ldap         4096 aoû  1 18:02 .

#On garde id2entry qui est la base elle meme, ainsi que nextid et dn2id.

$ slapindex -f /etc/openldap/slapd.conf

# redonner les droits au compte ldap sur ces fichiers

[root@ur /var/lib/ldap/int]
$ chown ldap:ldap *

#et c'est reparti
$ /etc/init.d/ldap start
Démarrage de slapd:                                        [  OK  ]


13.2  Récuperer le champs userPassword

Via les commandes shell ; ldapsearch, avec un bind administrateur ou l'utilisateur du compte concerné. Le champs userPassword est retourné encodé en base 64, il faut donc le decoder .
$ldapsearch -x -D "uid=test,ou=people,dc=int-evry,dc=fr" \
-W "uid=test" -LLL userPassword                                                                       
Enter LDAP Password: 
dn: uid=test,ou=People,dc=int-evry,dc=fr
userPassword:: e2NyeXB0fTgybmhJTTcuUGtFODY=

ajouter de le décodage

$ldapsearch -x -D "uid=test,ou=people,dc=int-evry,dc=fr" \
-W "uid=test" -LLL userPassword | grep userPassword | \
cut --delimiter=" " -f2 | openssl base64 -d ; echo 
Enter LDAP Password: 
{crypt}82nhIM7.PkE86

verification de la concordance de la chaine cryptée et du password en clair

$python -c "import crypt; print crypt.crypt('motdepas','82nhIM7.PkE86')"
82nhIM7.PkE86

13.3  Acces au schema par ldap

Attention, suivant les ACL, il faut faire ces recherches avec un bind administrateur -D ``cn=admin,dc=int-evry,dc=fr'' -W .
get RootDSE without SASL

$ ldapsearch -x -H ldap://corbeau:389 -b '' -s base -LLL + -D "cn=admin,dc=int-evry,dc=fr" -W
Enter LDAP Password:
dn:
structuralObjectClass: OpenLDAProotDSE
namingContexts: dc=int-evry,dc=fr
monitorContext: cn=Monitor
supportedControl: 2.16.840.1.113730.3.4.2
supportedControl: 1.3.6.1.4.1.4203.1.10.2
supportedControl: 1.2.826.0.1.334810.2.3
supportedExtension: 1.3.6.1.4.1.4203.1.11.3
supportedExtension: 1.3.6.1.4.1.4203.1.11.1
supportedExtension: 1.3.6.1.4.1.1466.20037
supportedFeatures: 1.3.6.1.4.1.4203.1.5.1
supportedFeatures: 1.3.6.1.4.1.4203.1.5.2
supportedFeatures: 1.3.6.1.4.1.4203.1.5.3
supportedFeatures: 1.3.6.1.4.1.4203.1.5.4
supportedFeatures: 1.3.6.1.4.1.4203.1.5.5
supportedLDAPVersion: 2
supportedLDAPVersion: 3
supportedSASLMechanisms: DIGEST-MD5
supportedSASLMechanisms: CRAM-MD5
subschemaSubentry: cn=Subschema



get supported attributes from subschema entry
$ldapsearch -x -H ldap://corne:389 -b 'cn=Subschema' -s base -LLL \
                objectclass=subschema attributeTypes
dn: cn=Subschema
attributeTypes: ( 2.5.18.1 NAME 'createTimestamp' EQUALITY generalizedTimeMatc
 h ORDERING generalizedTimeOrderingMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.24 
 SINGLE-VALUE NO-USER-MODIFICATION USAGE directoryOperation )
attributeTypes: ( 2.5.18.2 NAME 'modifyTimestamp' EQUALITY generalizedTimeMatc
 h ORDERING generalizedTimeOrderingMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.24 
 SINGLE-VALUE NO-USER-MODIFICATION USAGE directoryOperation )
attributeTypes: ( 2.5.18.3 NAME 'creatorsName' EQUALITY distinguishedNameMatch
  SYNTAX 1.3.6.1.4.1.1466.115.121.1.12 SINGLE-VALUE NO-USER-MODIFICATION USAGE
  directoryOperation )
...
attributeTypes: ( 1.3.6.1.4.1.7391.2.2.1.1.2.8 NAME 'vacationaddress' DESC 'Ad
 resse de vacation' EQUALITY caseIgnoreMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1
 .15 )



get supported objectclasses from subschema entry
$ldapsearch -x -H ldap://corne:389 -b 'cn=Subschema' -s base -LLL \
                objectclass=subschema objectClasses
objectClasses: ( 2.5.20.1 NAME 'subschema' DESC 'RFC2252: controlling subschem
 a' AUXILIARY MAY ( dITStructureRules $ nameForms $ ditContentRules $ objectCl
 asses $ attributeTypes $ matchingRules $ matchingRuleUse ) )
objectClasses: ( 2.5.6.0 NAME 'top' ABSTRACT MUST objectClass )
...
objectClasses: ( 1.3.6.1.4.1.7391.2.2.2.1 NAME 'Vacation' DESC 'Users vacation
  status information' STRUCTURAL MUST ( vacationActive $ maildeliveryoption $ 
 uid ) MAY ( vacationInfo $ vacationStart $ vacationEnd $ vacationaddress $ ma
 ilforwardingaddress ) )

13.4  Erreurs stupides !

Après avoir perdu du temps sur cette erreur, j'en note la raison.
fichier ldif contenant:
objectclass: groupOfNames

ajout par ldapadd retourne:
ldap_add: Invalid syntax
 additional info: objectclass: value #0 invalid per syntax

Ici j'avais laissé trainer un espace derrière groupOfNames dans le fichier ldif, ce qui donne ce genre d'erreur !.

Références

[1]
Administrator's Guide du site openldap, http://www.openldap.org/doc/admin/
[2]
Archives de la liste de discussion openldap, http://www.openldap.org/lists/openldap-software/
[3]
Pam ldap, http://www.padl.com/pam_ldap.html
[4]
Open-IT project, http://kodama.open-it.org/metadot/index.pl
[5]
Ldap au CRU, http://www.cru.fr/ldap/
[6]
Ldap schema repository, http://www.hklc.com/ldapschema/

Ce document a été traduit de LATEX par HEVEA.