• No results found

Access Control Common Examples

8. Access Control

8.4. Access Control Common Examples

add: olcAccess

olcAccess: to dn.children="dc=example,dc=com" by * write

-But this example will not guarantee that the existing values remain in their original order, so it will most likely yield a broken security configuration. Instead, the numeric index should be used:

changetype: modify delete: olcAccess olcAccess: {1}

add: olcAccess

olcAccess: {1}to dn.children="dc=example,dc=com" by * write

-This example deletes whatever rule is in value #1 of the olcAccess attribute (regardless of its value) and adds a new value that is explicitly inserted as value #1. The result will be

olcAccess: {0}to attrs=member,entry by dnattr=member selfwrite

olcAccess: {1}to dn.children="dc=example,dc=com"

by * write

olcAccess: {2}to dn.children="dc=com"

by * read

which is exactly what was intended.

8.4. Access Control Common Examples

8.4.1. Basic ACLs

Generally one should start with some basic ACLs such as:

access to attr=userPassword by self =xw

by anonymous auth by * none

access to * by self write by users read by * none

The first ACL allows users to update (but not read) their passwords, anonymous users to authenticate against this attribute, and (implicitly) denying all access to others.

The second ACL allows users full access to their entry, authenticated users read access to anything, and (implicitly) denying all access to others (in this case, anonymous users).

8.4.2. Matching Anonymous and Authenticated users

An anonymous user has a empty DN. While the dn.exact="" or dn.regex="^$" could be used, slapd(8)) offers an anonymous shorthand which should be used instead.

access to *

by anonymous none by * read

denies all access to anonymous users while granting others read.

Authenticated users have a subject DN. While dn.regex=".+" will match any authenticated user, OpenLDAP provides the users short hand which should be used instead.

access to * by users read by * none

This ACL grants read permissions to authenticated users while denying others (i.e.: anonymous users).

8.4.3. Controlling rootdn access

You could specify the rootdn in slapd.conf(5) or slapd.d without specifying a rootpw. Then you have to add an actual directory entry with the same dn, e.g.:

dn: cn=Manager,o=MyOrganization cn: Manager

sn: Manager

objectClass: person objectClass: top

userPassword: {SSHA}someSSHAdata

Then binding as the rootdn will require a regular bind to that DN, which in turn requires auth access to that entry's DN and userPassword, and this can be restricted via ACLs. E.g.:

access to dn.base="cn=Manager,o=MyOrganization"

by peername.regex=127\.0\.0\.1 auth by peername.regex=192\.168\.0\..* auth by users none

by * none

The ACLs above will only allow binding using rootdn from localhost and 192.168.0.0/24.

8.4.4. Managing access with Groups

There are a few ways to do this. One approach is illustrated here. Consider the following DIT layout:

+-dc=example,dc=com

+---cn=administrators,dc=example,dc=com +---cn=fred blogs,dc=example,dc=com

and the following group object (in LDIF format):

dn: cn=administrators,dc=example,dc=com cn: administrators of this region

objectclass: groupOfNames (important for the group acl feature) member: cn=fred blogs,dc=example,dc=com

member: cn=somebody else,dc=example,dc=com

One can then grant access to the members of this this group by adding appropriate by group clause to an access directive in slapd.conf(5). For instance,

access to dn.children="dc=example,dc=com"

by self write

by group.exact="cn=Administrators,dc=example,dc=com" write by * auth

Like by dn clauses, one can also use expand to expand the group name based upon the regular expression matching of the target, that is, the to dn.regex). For instance,

access to dn.regex="(.+,)?ou=People,(dc=[^,]+,dc=[^,]+)$"

attrs=children,entry,uid

by group.expand="cn=Managers,$2" write by users read

by * auth

The above illustration assumed that the group members are to be found in the member attribute type of the groupOfNames object class. If you need to use a different group object and/or a different attribute type then use the following slapd.conf(5) (abbreviated) syntax:

access to <what>

by group/<objectclass>/<attributename>=<DN> <access>

For example:

access to *

by group/organizationalRole/roleOccupant="cn=Administrator,dc=example,dc=com" write

In this case, we have an ObjectClass organizationalRole which contains the administrator DN's in the roleOccupant attribute. For instance:

dn: cn=Administrator,dc=example,dc=com cn: Administrator

objectclass: organizationalRole

roleOccupant: cn=Jane Doe,dc=example,dc=com

Note: the specified member attribute type MUST be of DN or NameAndOptionalUID syntax, and the specified object class SHOULD allow the attribute type.

Dynamic Groups are also supported in Access Control. Please see slapo-dynlist(5) and the Dynamic Lists overlay section.

8.4.5. Granting access to a subset of attributes

You can grant access to a set of attributes by specifying a list of attribute names in the ACL to clause. To be useful, you also need to grant access to the entry itself. Also note how children controls the ability to add, delete, and rename entries.

# mail: self may write, authenticated users may read access to attrs=mail

by self write by users read by * none

# cn, sn: self my write, all may read access to attrs=cn,sn

by self write by * read

# immediate children: only self can add/delete entries under this entry access to attrs=children

by self write

# entry itself: self may write, all may read access to attrs=entry

by self write by * read

# other attributes: self may write, others have no access access to *

by self write by * none

ObjectClass names may also be specified in this list, which will affect all the attributes that are required and/or allowed by that objectClass. Actually, names in attrlist that are prefixed by @ are directly treated as objectClass names. A name prefixed by ! is also treated as an objectClass, but in this case the access rule affects the attributes that are not required nor allowed by that objectClass.

8.4.6. Allowing a user write to all entries below theirs

For a setup where a user can write to its own record and to all of its children:

access to dn.regex="(.+,)?(uid=[^,]+,o=Company)$"

by dn.exact,expand="$2" write by anonymous auth

(Add more examples for above)

8.4.7. Allowing entry creation

Let's say, you have it like this:

o=<basedn>

then, if you wanted user uid=<someuserid> to ONLY create an entry for its own thing, you could write an ACL like this:

# this rule lets users of "associatedDomain=<matcheddomain>"

# write under "ou=addressbook,associatedDomain=<matcheddomain>,ou=domains,o=<basedn>", # i.e. a user can write ANY entry below its domain's address book;

# this permission is necessary, but not sufficient, the next # will restrict this permission further

access to dn.regex="^ou=addressbook,associatedDomain=([^,]+),ou=domains,o=<basedn>$" attrs=children by dn.regex="^uid=([^,]+),ou=users,associatedDomain=$1,ou=domains,o=<basedn>$$" write by * none

# Note that above the "by" clause needs a "regex" style to make sure # it expands to a DN that starts with a "uid=<someuserid>" pattern

# while substituting the associatedDomain submatch from the "what" clause.

# This rule lets a user with "uid=<matcheduid>" of "<associatedDomain=matcheddomain>"

# write (i.e. add, modify, delete) the entry whose DN is exactly

# "uid=<matcheduid>,ou=addressbook,associatedDomain=<matcheddomain>,ou=domains,o=<basedn>"

# and ANY entry as subtree of it

access to dn.regex="^(.+,)?uid=([^,]+),ou=addressbook,associatedDomain=([^,]+),ou=domains,o=<basedn>$"

by dn.exact,expand="uid=$2,ou=users,associatedDomain=$3,ou=domains,o=<basedn>" write by * none

# Note that above the "by" clause uses the "exact" style with the "expand"

# modifier because now the whole pattern can be rebuilt by means of the # submatches from the "what" clause, so a "regex" compilation and evaluation # is no longer required.

8.4.8. Tips for using regular expressions in Access Control

Always use dn.regex=<pattern> when you intend to use regular expression matching. dn=<pattern> alone defaults to dn.exact<pattern>.

Use (.+) instead of (.*) when you want at least one char to be matched. (.*) matches the empty string as well.

Don't use regular expressions for matches that can be done otherwise in a safer and cheaper manner.

Examples:

dn.regex=".*dc=example,dc=com"

is unsafe and expensive:

unsafe because any string containing dc=example,dc=com will match, not only those that end with the desired pattern; use .*dc=example,dc=com$ instead.

unsafe also because it would allow any attributeType ending with dc as naming attribute for the first RDN in the string, e.g. a custom attributeType mydc would match as well. If you really need a regular expression that allows just dc=example,dc=com or any of its subtrees, use

^(.+,)?dc=example,dc=com$, which means: anything to the left of dc=..., if any (the question mark after the pattern within brackets), must end with a comma;

expensive because if you don't need submatches, you could use scoping styles, e.g.

dn.subtree="dc=example,dc=com"

to include dc=example,dc=com in the matching patterns,

dn.children="dc=example,dc=com"

to exclude dc=example,dc=com from the matching patterns, or

dn.onelevel="dc=example,dc=com"

to allow exactly one sublevel matches only.

Always use ^ and $ in regexes, whenever appropriate, because ou=(.+),ou=(.+),ou=addressbooks,o=basedn will match

something=bla,ou=xxx,ou=yyy,ou=addressbooks,o=basedn,ou=addressbooks,o=basedn,dc=some,dc=org Always use ([^,]+) to indicate exactly one RDN, because (.+) can include any number of RDNs; e.g.

ou=(.+),dc=example,dc=com will match ou=My,o=Org,dc=example,dc=com, which might not be what you want.

Never add the rootdn to the by clauses. ACLs are not even processed for operations performed with rootdn identity (otherwise there would be no reason to define a rootdn at all).

Use shorthands. The user directive matches authenticated users and the anonymous directive matches anonymous users.

Don't use the dn.regex form for <by> clauses if all you need is scoping and/or substring replacement; use scoping styles (e.g. exact, onelevel, children or subtree) and the style modifier expand to cause substring expansion.

For instance,

access to dn.regex=".+,dc=([^,]+),dc=([^,]+)$"

by dn.regex="^[^,],ou=Admin,dc=$1,dc=$2$$" write

although correct, can be safely and efficiently replaced by

access to dn.regex=".+,(dc=[^,]+,dc=[^,]+)$"

by dn.onelevel,expand="ou=Admin,$1" write

where the regex in the <what> clause is more compact, and the one in the <by> clause is replaced by a much more efficient scoping style of onelevel with substring expansion.

8.4.9. Granting and Denying access based on security strength factors (ssf)

You can restrict access based on the security strength factor (SSF)

access to dn="cn=example,cn=edu"

by * ssf=256 read

0 (zero) implies no protection, 1 implies integrity protection only, 56 DES or other weak ciphers, 112 triple DES and other strong ciphers, 128 RC4, Blowfish and other modern strong ciphers.

Other possibilities:

transport_ssf=<n>

tls_ssf=<n>

sasl_ssf=<n>

256 is recommended.

See slapd.conf(5) for information on ssf.

8.4.10. When things aren't working as expected

Consider this example:

access to *

by anonymous auth

access to * by self write access to * by users read

You may think this will allow any user to login, to read everything and change his own data if he is logged in.

But in this example only the login works and an ldapsearch returns no data. The Problem is that SLAPD goes through its access config line by line and stops as soon as it finds a match in the part of the access rule.(here:

to *)

To get what we wanted the file has to read:

access to *

by anonymous auth by self write by users read

The general rule is: "special access rules first, generic access rules last"

See also slapd.access(5), loglevel 128 and slapacl(8) for debugging information.