|OpenLDAP Faq-O-Matic : OpenLDAP Software FAQ : Configuration : SLAPD Configuration : Access Control : Access control customization : DynACL: pluggable access control configuration : ACI : What are the steps for ACIs?|
|The steps for using ACIs are:|
1. Compile OpenLDAP with --enable-aci. If slapd complains about aci during startup, you do not have this option enabled. If you are using binaries (RPM, etc.), make sure this option was included. If not, you will need to rebuild. 2. Configure your access.conf to use ACI for objects matching a pattern. For instance:This means that any object in the rights container specified above can be modified by members of the enterprise admins group or admins sharing the same locality (l=*) as the right. If a user in not in either group, consult the object itself to resolve access. Ordering is very important here. As soon as a subject match occurs, the mask will be determined and resolution will stop. A group matched above would mean that the ACI will not be evaluated. If aci is evaluated, it will ALWAYS match, and as such no directives after aci will ever get processed. You can think of the aci directive as having an implicit "by * none" directly behind it. The explicit definition above is then redundant, but simplifies readibility.
As mentioned above, ACI works with a subset of the mask granted in the "by aci" clause. If the directive were written as:
by aci read... then nothing specified in the object would allow the object to be modified or written. To enable full control of the object via ACI, I always use:
by aci writeTo enable full control of the entire directory with ACI, simply use:
access to * by aci writeHowever, this is not recommended. Each object would need to be maintained independently. A good mix of well thought out ACL directives with a few buckets of ACI objects typically works best.
3. Define ACI info in each object.This part can be tricky. There is an implied "deny" at the end of each ACI definition. The other confusing part is that there is no implied access to the object itself. Granting read permissions to the cn attribute will not allow a subject to read that attribute unless they are also given read access to [entry]. I feel this is a bug, but I am sure others would disagree. In addition to entry, there are also keywords for [all] and [children]. [all] is handy for simplifying access, and write access to [children] is needed to create objects underneath the matched DN. Now that the ground rules are set, here is the syntax:
< aci > ::= < acl syntax > < acl syntax > ::= <familyOID> + '#' + <scope > + '#' + < rights > + '#' + < dnType > + '#' + < subjectDn > < subjectDn > ::= < printable string > < familyOid > ::= < oid > < scope > ::= "entry" | "subtree" | <level> < level > ::= numericstring < dnType > ::= "access-id" | "role" | "group" | "self" < rights > ::= [ ] | [ < right > + [ '$' + <right> ] * ] < right > ::= <action > + ';' + <permissions> + ';' + <attrs> < action > ::= "grant" | "deny" < permissions > ::= [ ] | [ < permission > + [ ',' + <permission> ] * ] < attrs > ::= [ < attributeString> + [ ',' + < attributeString > ] * ] < attributeString > ::= "[all]" | "[entry]" | <printableString > < permission > ::= "r" | "s" | "w" | "c"Here is the explanation in human terms. The toplevel format is 5 fields separated by '#' characters:
OID # SCOPE # RIGHTS # TYPE # SUBJECTCurrently, it is ignored. Note that this may change over time. You can put whatever you want there. For convenience, I use a sequence of integers in the OID space so that I can tell the loading order of directives, and I can programmatically maintain the ordering. This is not the intended purpose, so YMMV.
SCOPEIt would be a nice thing to have, but for now it is always "entry". "subtree" would be used if inheritance worked, or "level" could set an arbitrary depth. Neither work, so just put in "entry". Note: "entry", "children" and "subtree" are currently supported.
RIGHTSIt is a series of statements separated by semi-colons (";"). The simplest form is a triplet of the following:
ACTION ; PERMISSION ; TARGETIt can be either "grant" or "deny", but the interpretation is such that "grant" is the only meaningful action. "deny" is implied when no grants exist. "deny" does not force a stop in evaluation, so I ignore it altogether.
PERMISSIONIt can be any combination of OpenLDAP permissions:
"w" write "r" read "c" compare "s" search "x" authI usually specify the entire list of "sub" permissions, so I usually designate "r,c,s,x" for read-only tagets and "w,r,c,s,x" for writable ones. You may use other combinations if necessary.
TARGETIt is a comma separated list of any of the following:
attribute eg, givenName, cn, userPassword, etc., but not the object itself [all] all attributes in the object [entry] the object itself but no attributes [children] sub-objects (only write is meaningful here)The simplest is to use [all]. If you'd like restricted access to attributes, grant them explicitly and in addition grant read access to [entry]. A valid rights triplet could be:
grant;r,s,w,c;[all]PERMISSION and TARGET can be supplied multiple times in a singe definition as such:
... grant;r,s,w,c;userPassword;r,s,c;cn,telephoneNumber,[entry]; ...It is one of:
"access-id" the DN of a subject "public" irrelevant "self" irrelevant "dnattr" the AttributeDescription of the value in the object "group" the DN of a group "role" the DN of a role "set" a set "set-ref" a set"access-id" is straightforward. "public" matches any DN, including ananymous. "self" is equivalent to setting "access-id" with the object's DN listed as the SUBJECT. "dnattr" matches the value of the AttributeDescription indicated as dnattr in the object itself. "group" requires that the specified DN must contain the objectclass "groupOfNames", and that each member's DN is listed as a "member" attribute of the group. "role" is the same as group; only, the "roleOccupant" attribute of the "organizationalRole" is used. "set" matches any DN listed in the set, which is dynamically gathered. "set-ref" matches any DN listed in the set, with dereferentiation.
SUBJECTIt is the value related to the TYPE field; e.g. the DN of the operator if using TYPE "access-id", or of a group if using TYPE "group". This is a single entry.
Once the directives have been determined, they must be stored in the object under an attributed called "OpenLDAPaci". Using schema rules, adding this attribute will most likely fail unless you create an objectclass that allows those attributes. Here is my schema definition for OpenLDAPacl:
objectclass ( 184.108.40.206.4.1.107220.127.116.11.6 NAME 'OpenLDAPacl' DESC 'OpenLDAP access control information' SUP top STRUCTURAL MUST ( objectclass ) MAY ( OpenLDAPaci ) )You must add this (or similar) objectclass to each object before specifying OpenLDAPaci attributes. This is the LDIF of an object configured to use OpenLDAP aci:
dn: uid=mccarthy,ou=people,l=dallas,o=acme uid: mccarthy givenName: Kevin sn: McCarthy cn: Kevin McCarthy mail: firstname.lastname@example.org userPassword: foobar objectClass: top objectClass: person objectClass: openLDAPacl OpenLDAPaci: 1#entry#grant;r,w,s,c;[all]#group#cn=enterprise admins,ou=groups,o=acme OpenLDAPaci: 2#entry#grant;r,w,s,c;[all]#group#cn=dallas admins,ou=groups,l=dallas,o=acme OpenLDAPaci: 3#entry#grant;r,w,s,c;userPassword,mail;r,s,c;[all]#access-id#uid=user1,ou=people,l=dallas,o=acme OpenLDAPaci: 4#entry#grant;r,s,c;[all]#group#cn=all acme,ou=groups,o=acmeYou can see that the directives become ugly very quickly, but this is OK since they are designed to be maintained by code and not by hand. Actually, openldapaci is declared in the system schema (core.schema) to be an operational attribute, and as such, it will not be given on searches unless specifically asked for by name. You can change this behavior by removing the line from the schema saying:
USAGE directoryOperationinside the code block for OpenLDAPaci. Slurpd will be smart enough to replicate these attributes even if they do appear "invisible".
Another schema gotcha is the fact that matching is not properly implemented. The schema calls for a mathing rule of OpenLDAPaciMatch (or something alike). Since this does not actually exist, you will not be able to modify the attribute. You can remove it completely and recreate it from scratch, but mods need to use a matching rule to determine which values need to be altered, and a broken matching rules breaks mod. A workaround is to set matching to:
EQUALITY caseIgnoreIA5MatchThis is close enough to get the job done, but not perfect. Still, it is useful enough to mention.
|Some notes about the above:|
* As of (at least) OpenLDAP version 2.1.22, the loading of the objectclass schema described is NOT necessary. It's hardcoded into the code.
* As of 2.1.22, it doesn't seem to be possible to change the USAGE and/or the EQUALITY in the code. It will compile, but slapd won't start for various reasons.
|[Append to This Answer]|
|Next:||What are the pros and cons of ACIs?|