Chapitre 7. Fonctionnement des permissions dans OpenLDAP

Table des matières

Description de la syntaxe
Description générale
Description de la cible
Description du sujet
Description de l'action
Description des contrôles
Cas pratiques
Conseils généraux
Permission pour la création d'enfants
Restriction pour la création d'enfant

Résumé

Dans ce chapitre nous nous attarderons sur le fonctionnement des permissions qui permettent de contrôler l'accès aux données des serveurs de la suite logicielle Openldap.

Description de la syntaxe

Description générale

Les permissions d'accès sont fournies au serveur slapd sous la forme d'une liste de clauses. La syntaxe de ces clauses est la suivante:

<clause d'accès> ::= access to <objet> [ by <sujet> <action> <contrôle> ]+

La définition de chacun de ces quatre composants est la suivante:

objet
L'objet, ou bien la cible, désigne une entrée, un ensemble d'entrée ou un attribut de l'annuaire.
sujet
Décrit la ou les personnes à qui la clauses d'accès donne les droits. Il s'agit d'un ensemble de DNs.
action
Désigne le type d'accès est concédé: lecture, écriture, etc.
contrôle
Indique le comportement du serveur après l'accès à la clause.

Les informations les plus pertinentes sur la gestion des contrôle d'accès d'Openldap se trouvent dans la page manuel de slapd.access, et dans la FAQ d'Openldap.

Description de la cible

Syntaxe

La syntaxe de la cible est la suivante:

<objet> ::= * | [dn[.<dnstyle>]=<dnspec>] [filter=<ldapfilter>] [attrs=<attrlist>]

La cible peut donc être *, ce qui désigne toutes les entrées de l'annuaire. Elle peut sinon être désignée par son DN, par un filtre ou être une liste d'attributs. Il est possible de combiner toutes ces trois dernières cibles ensemble. Cela permet par exemple de cibler tous les attributs des entrées répondant à un filtre, dans une branche, ciblée par le DN.

Le DN

La désignation du DN peut être donnée de cinq façons différentes:

  • De façon exacte. Il s'agit du comportement par défaut à partir de la version 2.2 de slapd. Il n'y a pas de dnstyle à fournir. Avec les versions précédentes, il est obligatoire de mettre un dnstyle ayant la valeur base ou bien exact qui est synonyme.

  • Désignation d'une sous branche. Si l'on veut cibler toutes les entrées sous une entrée précise, dnstyle doit contenir la valeur subtree ou sub synonyme du précédent.

  • Désignation d'un seul niveau. Si l'on ne veut cibler que les entrées immédiatement sous une entrée précise, dnstyle doit contenir la valeur one ou onelevel synonyme du précédent à partir de la version 2.2.

  • Désignation des enfants. Si l'on ne veut cibler que les enfants d'une entrée précise, en excluant l'entrée elle même, dnstyle doit contenir alors la valeur children.

  • Désignation par expression régulière. Il est possible de cibler des entrées par une expression régulière sur leur DN. dnstyle doit contenir alors la valeur regex. Il s'agit de la valeur par défaut sous Openldap 2.1.

Les attributs

attrlist doit contenir une liste d'attributs, séparés par des virgules. Il existe deux attributs particuliers qui peuvent être inclus: entry et children. Le premier permet de cibler l'entrée elle même, le second ses enfants uniquement. Il est possible aussi de mettre des objectclass, ce qui est équivalent à mettre tous les attributs de l'objectclass en question.

L'accès à des entrées en tant que telle est nécessaire pour des opérations de création, d'effacement ou de renommage de l'entrée, mais aussi pour l'accès en écriture. Nous détaillerons ces cas dans des études de cas pratiques ci-après.

Description du sujet

La syntaxe

La définition complète de la clause sujet est la suivante:

            *
            anonymous
            users
            self

            dn[.<dnstyle>[,<modifier>]]=<pattern>
            dnattr=<attrname>
            group[/<objectclass>[/<attrname>]]
                 [.<style>]=<pattern>
            peername[.<style>]=<pattern>
            sockname[.<style>]=<pattern>
            domain[.<domainstyle>[,<modifier>]]=<pattern>
            sockurl[.<style>]=<pattern>
            set[.<style>]=<pattern>

            ssf=<n>
            transport_ssf=<n>
            tls_ssf=<n>
            sasl_ssf=<n>

            aci=<attrname>

Nous allons étudier qu'une partie de cette syntaxe, la plus significative et la plus utile.

Désignation par catégorie du sujet

Le serveur slapd distingue quatre catégories différentes (mais non disjointes) d'utilisateurs, à qui il est possible de donner accès dans une clause, via les syntaxes suivantes dédiées au sujet:

*
Désigne n'importe quel utilisateur, qu'il soit identifié ou pas.
anonymous
Désigne un utilisateur non identifié.
users
Désigne un utilisateur identifié.
self
Désigne l'objet, c'est à dire l'utilisateur dont le DN est la cible.

Désignation directe ou indirecte du sujet

La désignation directe du sujet se fait avec la syntaxe suivante:

dn[.<dnstyle>[,<modifier>]]=<pattern>

pattern est le DN de l'utilisateur qui sera sujet. Il est possible d'y associer un dnstyle, exactement comme dans la désignation de la cible. Dans le cas où ce dnstyle a pour valeur regex, il est possible de faire apparaître dans le pattern des sous chaînes de substitution, de la forme $i (où i est un chiffre entre 1 et 9). Chaque chaîne $i sera remplacée par la ième sous chaîne substituée de l'expression régulière de l'objet. Depuis la version 2.2 d'Openldap il existe un nouveau dnstyle qui est expand, qui permet d'utiliser ces chaînes de substitution de la cible, sans que le reste du pattern soit considéré comme une expression régulière.

Il existe une autre façon plus dynamique encore de désigner le sujet. Il est possible en effet de désigner le DN du sujet comme étant la valeur d'un attribut de la cible. La syntaxe suivante permet cela:

dnattr=<attrname>

.

<attrname> contient alors l'attribut dont la valeur désigne le sujet. Un exemple classique d'utilisation de cette syntaxe est la clause qui donne accès en écriture à un objet de type groupe au propriétaire du groupe. Le propriétaire étant un attribut de l'objet groupe.

Désignation par un groupe du sujet

Il est possible de définir des objets de type groupe dans un annuaire. Ces objets possèdent un attribut, multivalué, contenant les DN des personnes appartenant au groupe. Dans les schémas fournis avec Openldap il existe deux classes de groupe: groupOfNames et groupOfUniqueNames, dont les attributs désignant les membres sont respectivement member et uniqueMember. Il est fort utile de savoir écrire des permissions concernant les membres d'un groupe. C'est ce que nous allons étudier maintenant.

La syntaxe pour que le sujet d'une clause soit les membres d'un groupe est la suivante:

group[/<objectclass>[/<attrname>]] [.<style>]=<pattern>

<pattern> est le nom du groupe. Par défaut les groupes doivent être de classe groupofnames, mais il est possible de changer cette classe en précisant l'objectclass. De même que l'attribut par défaut contenant les DNs sera member mais il est possible de le modifier en précisant l'attrname.

Il est possible de préciser un style, qui peut être base, exact (synonyme de base) ou regex. Dans ce dernier cas, il n'est pas possible d'utiliser des expressions régulières dans le pattern mais seulement des chaînes de substitutions.

Désignation par les connexions réseau du sujet

Jusqu'à présent la désignation du sujet s'effectue à partir du contenu de l'annuaire. Dans cette partie nous allons désigner le sujet à partir d'informations concernant son accès physique au réseau.

Les syntaxes pour désigner ainsi le sujet sont les suivantes:

            peername[.<style>]=<pattern>
            sockname[.<style>]=<pattern>
            domain[.<domainstyle>[,<modifier>]]=<pattern>
            sockurl[.<style>]=<pattern>

Les définitions de ces clauses sont les suivantes:

peername
Désigne l'extrémité distante de la connexion entre le serveur et le client. pattern est de la forme ip-address:port.
sockname
Désigne l'extrémité locale de la connexion entre le serveur et le client. pattern est de la même forme que celle décrite ci-dessus.
domain
Désigne le nom de la machine distante d'où est lancée la requête. Ce nom est récupéré par recherche DNS inversée, à condition que cette recherche, désactivée par défaut, soit activée dans le fichier de configuration de slapd. Les clauses désignant un sujet à l'aide du domain sont déconseillées, parce qu'il est très facile de falsifier son domaine. Il est possible d'utiliser un domainstyle avec la valeur subtree pour faire accepter des surdomaines.
sockurl
Désigne l'url contactée.

style peut être base (ou son synonyme exact) ou bien regex pour pouvoir faire des substitutions, tout comme avec les groupes.

Désignation par les ensembles du sujet

Les ensembles est l'un des moyens les plus pratique mais des moins connus pour préciser le sujet. Ils permettent de déterminer le sujet par rapport à la valeur de ses attributs, et éventuellement par rapport aussi à la valeur des attributs de l'objet.

La syntaxe est la suivante:

   <set> :=        <base>
                 | "(" <set> ")"
                 | <set> <conj> <set>
                 | <set> "/" <attribute> "*"
                 | <set> "/" <attribute>
   <base> :=       "this"
                 | "user"
                 | "[" <any text> "]"
   <conj> :=       "&" | "|"
   <attribute> :=  any attribute name

Cette syntaxe définit des intersections (via le symbole &) et des réunions (via le symbole |) d'ensembles. Chaque ensemble est une ou plusieurs chaînes de caractères, qui peuvent être interprétées comme des DN lorsqu'elles sont suivies d'un /, dans une syntaxe de la forme <ensemble>/ attribut .

Dans ce cas un nouvel ensemble est construit, par la réunion des valeurs des attributs, pour chaque objet désigné par son DN dans l'ensemble initial. Il est possible de récupérer récursivement les valeurs de l'attribut par l'opérateur *.

Les valeurs des ensembles sont définies soit de façon absolues, par une chaîne fournies entre crochet ([ et ]), soit relativement à l'objet, désigné par this, soit à l'utilisateur, désigné par user.

L'accès est obtenu lorsque l'ensemble n'est pas vide.

Exemple 7.1. Tous les utilisateur dont une valeur de l'attribut description est python

user/description & [python]

Exemple 7.2. Tous les utilisateur dont une valeur de l'attribut description est identique à une valeur de l'attribution description de la cible

user/description & this/description

Exemple 7.3. Tous les utilisateur membre d'un groupe

[cn=dev,dc=ee,dc=fr]/uniquemember  & user

Exemple 7.4. Tous les utilisateur membre d'un groupe, ce groupe pouvant lui même contenir d'autres groupes

[cn=dev,dc=ee,dc=fr]/uniquemember*  & user

Exemple 7.5. Tous les utilisateurs qui sont membres du groupes dont la valeur est contenu dans la cible

this/allowedgroups/uniquemember & user

Exemple 7.6. Tous les utilisateurs qui sont membres du groupes dont la valeur est contenu dans la cible, ou bien, récursivement dans un groupe de ce groupe

this/allowedgroups/uniquemember* & user

Ces exemples montrent qu'il est possible d'utiliser la syntaxe des ensemble pour désigner des groupes. Ils montrent aussi que cette syntaxe est beaucoup plus forte que celle des groupes, puisqu'elle permet de faire des groupes récursifs.

Description de l'action

Après avoir spécifier sur quel objet portait la clauses de sécurité, puis à qui elle donnait accès, il faut ensuite préciser sur quel est type d'accès, d'action, la permission est à accorder. Il existe deux méthodes pour définir l'action. La première consiste à fournir des types d'accès par groupe, les groupes ayant une relation d'inclusion entre eux. La deuxième méthode consiste à faire des accès élémentaires.

La syntaxe pour fournir une action est la suivante:

<access> ::= [self]{<level>|<priv>}
<level> ::= none | auth | compare | search | read | write
<priv> ::= {=|+|-}{w|r|s|c|x}+

La syntaxe level implémente la première méthode citée. Il existe six niveaux de permissions, de l'accès nul (none), le plus bas, à l'accès en écriture (write), le plus élevé. Chaque niveau incluant les niveaux inférieur. Le deuxième niveau, auth permettant l'authentification.

Autoriser l'authentification

À moins d'être dans le cas d'un annuaire en lecture seule pour tout le monde, la moindre des choses dans la mise en place d'un annuaire consiste à autoriser les utilisateurs à s'identifier. Pour cela, il est obligatoire d'avoir parmi les clauses d'accès une clause similaire à la suivante.

access to attribute=userPassword
        by anonymous auth
        by self write
        by * none break

La deuxième méthode pour désigner l'action consiste donc à donner, ou à retirer des accès élémentaires. La syntaxe priv permet cela. Les accès élémentaires sont définis par une des lettres x, c, s r ou w, ces lettres désignant respectivement l'accès en authentification, comparaison, recherche, lecture et écriture. Pour ajouter une accès élémentaire, il faut préfixer l'accès d'un +. Pour retirer il faut préfixer l'accès de -. Pour imposer un accès fixe, et ne pas tenir compte de ce qui a déjà été accordé, il faut préfixer d'un =.

En préfixant chacune de ces deux syntaxes d'un self, l'accès ne concernera que si l'utilisateur est désigné dans la cible. Un exemple d'utilisation de cette syntaxe est l'accès selfwrite accordé sur un objet de type groupe et qui permet à un utilisateur de ne modifier que la valeur attribut contenant son DN, pour se retirer du groupe par exemple.

Description des contrôles

Les contrôles précisent le comportement que doit avoir le serveur slapd après avoir atteint une clause d'accès. Le comportement par défaut est de s'arrêter d'accorder les permissions obtenues lorsque la clause a été atteinte. Ceci est équivalent à écrire stop à la fin de la clause.

Les autres possibilités pour un contrôle sont break et continue. continue indique au serveur qu'il doit continuer à chercher d'autre sujet (et action), pour le même objet. break indique qu'il doit rechercher une autre cible.

Par défaut, chaque groupe de clauses pour un objet donné se termine implicitement par * none stop. Le serveur s'arrête donc et ne donne aucun droit.