From ded29bf9eb3fa40c56eb9ace365d13e6348e215c Mon Sep 17 00:00:00 2001 From: Guilhem Moulin Date: Mon, 20 Aug 2012 01:53:16 +0200 Subject: A little test suite for LDAP ACLs. --- ldap/Makefile | 88 +++++ ldap/README | 30 ++ ldap/acl.ldif | 293 ++++++++++++++++ ldap/database.ldif | 60 ++++ ldap/fripost.ldif | 140 ++++++++ ldap/index.ldif | 44 +++ ldap/modules.ldif | 16 + ldap/populate.ldif | 199 +++++++++++ ldap/syncprov.ldif | 25 ++ ldap/syncrepl.ldif | 33 ++ ldap/test-user-acl.sh | 906 ++++++++++++++++++++++++++++++++++++++++++++++++++ 11 files changed, 1834 insertions(+) create mode 100644 ldap/Makefile create mode 100644 ldap/README create mode 100644 ldap/acl.ldif create mode 100644 ldap/database.ldif create mode 100644 ldap/fripost.ldif create mode 100644 ldap/index.ldif create mode 100644 ldap/modules.ldif create mode 100644 ldap/populate.ldif create mode 100644 ldap/syncprov.ldif create mode 100644 ldap/syncrepl.ldif create mode 100755 ldap/test-user-acl.sh diff --git a/ldap/Makefile b/ldap/Makefile new file mode 100644 index 0000000..8d6868f --- /dev/null +++ b/ldap/Makefile @@ -0,0 +1,88 @@ +DIR := $(shell grep -i '^olcDbDirectory: ' database.ldif | sed -e 's/^olcDbDirectory: //') +SUFFIX := $(shell grep -i '^olcSuffix: ' database.ldif | sed -e 's/^olcSuffix: //') +TMPSLAPD := /tmp/$(shell mktemp -u slapd.d-XXXXXX) +BACKUPDB := /tmp/$(shell mktemp -u db-XXXXXX.ldif) +BACKUPCONFIG := /tmp/$(shell mktemp -u config-XXXXXX.ldif) +NUM := $(shell ldapsearch -Q -LLL -Y EXTERNAL -H ldapi:/// -b "cn=config" "olcSuffix=$(SUFFIX)" dn | sed -e '/^$$/d' -e 's/^dn: //') +SCHEMA := $(shell grep -i '^dn: ' fripost.ldif | sed -re 's/^dn: cn=([^,]+),.*/\1/') + +all: + @echo "make install-schema: install the LDAP schema" + @echo "make install-db: install the database" + @echo "make install-mx: " + + +init: + @echo "Suffix: \`$(SUFFIX)\'" + @echo "Location: \`$(DIR)\'" + @test -e "$(DIR)" && (echo "Error: \`$(DIR)' exists." ; false); true + @mkdir -m 0700 "$(DIR)" + @chown openldap:openldap "$(DIR)" + @ldapadd -Q -Y EXTERNAL -H ldapi:/// -f database.ldif +# @if test -d "$(SUFFIX)" ; then echo nop ; fi + + +install-mx: + + rm -f -- "$(BACKUPDB)" "$(BACKUPCONFIG)" + @mkdir -m 0700 "$(BACKUPSLAPD)" + + @rmdir "$(BACKUPSLAPD)" + + +install-schema: + @ldapadd -Q -Y EXTERNAL -H ldapi:/// -f fripost.ldif + + +install-acl: + @sed "s/^dn: olcDatabase={.*}hdb,cn=config$$/dn: $(NUM)/" acl.ldif | ldapmodify -Q -Y EXTERNAL -H ldapi:/// + + +uninstall: + @echo "The database - suffix \"$(SUFFIX)\" - will be saved into \`$(BACKUPDB)' (if non-empty), and then *cleared* (but not deleted)." + @echo "Also, its ACLs and indexes will be cleared as well, if there are any." + @echo "Global configuration will be saved into \`$(BACKUPCONFIG)'." + @echo "slapd will be offline for a moment - at best." + @/bin/echo -n "Are you sure you want to continue [y/N] " + @read A && if [ "x$$A" = "xY" -o "x$$A" = "xy" ]; then true; else echo Aborted; false; fi +# + @if slapcat -b "$(SUFFIX)" 2>/dev/null | grep -q '.'; then \ + echo "Saving database" && slapcat -b "$(SUFFIX)" -l "$(BACKUPDB)" && echo "Clearing database" && ldapdelete -Q -Y EXTERNAL -H ldapi:/// -r "$(SUFFIX)" \ + ;else \ + echo "Empty database, not saving anything." \ + ;fi +# + @echo "Saving configuration"; slapcat -n0 -l "$(BACKUPCONFIG)" +# + @if (ldapsearch -Q -LLL -Y EXTERNAL -H ldapi:/// -s base -b "$(NUM)" "(olcAccess=*)" | grep -q '^dn: '); then \ + echo "Removing ACLs" && /bin/echo -e "dn: $(NUM)\nchangetype: modify\ndelete: olcAccess" | ldapmodify -Q -Y EXTERNAL -H ldapi:/// >/dev/null \ + ;else \ + echo "No ACLs to remove" \ + ;fi +# + @if (ldapsearch -Q -LLL -Y EXTERNAL -H ldapi:/// -s base -b "$(NUM)" "(olcDbIndex=*)" | grep -q '^dn: '); then \ + echo "Removing indexes" && /bin/echo -e "dn: $(NUM)\nchangetype: modify\ndelete: olcDbIndex" | ldapmodify -Q -Y EXTERNAL -H ldapi:/// >/dev/null \ + ;else \ + echo "No indexes to remove" \ + ;fi + + @echo "Making a new configuration directory at \`$(TMPSLAPD)'" + @mkdir -m0700 "$(TMPSLAPD)" && slapcat -n0 | slapadd -F "$(TMPSLAPD)" -n0 && chown -R 'openldap:openldap' "$(TMPSLAPD)" + + @echo "Deleting schema \"cn=$(SCHEMA),cn=config\"" && find "$(TMPSLAPD)" -name "cn={*}$(SCHEMA).ldif" -delete +# + @/etc/init.d/slapd stop +# + @echo "Replacing the old \`slapd.d'" + rm -rf /etc/ldap/slapd.d/ && mv "$(TMPSLAPD)" /etc/ldap/slapd.d/ +# + @/etc/init.d/slapd start +# + @echo "Don't forget to repopulate the database (if non-empty) from \`$(BACKUPDB)'" + + + + + + +.PHONY: all backup-db backup-config diff --git a/ldap/README b/ldap/README new file mode 100644 index 0000000..037ae65 --- /dev/null +++ b/ldap/README @@ -0,0 +1,30 @@ +/!\ This work is still in developpement, DO NOT run/install that on a +production server! + + +Since the user now have (partial) write access to the LDAP directory, it +is of crucial importance to configure the ACL properly. + + * "populate.ldif" is meant to provide at least an example of every +single situation we may encounter in our directory. + + * "test-user-acl.sh" checks the database against the ACLs. + + +/!\ Every modification to the schema or the ACLs should be made to +"populate.ldif" and "test-user-acl.sh" too! + + +Usage: + + * Load the ACLs: + + ldapmodify -Y EXTERNAL -H ldapi:/// -f acl.ldif + + * Repopulate the database (will clear it out first!): + + ldapdelete -Y EXTERNAL -H ldapi:/// -r "ou=virtual,o=mailHosting,dc=fripost,dc=dev" ; ldapadd -Y EXTERNAL -H ldapi:/// -f populate.ldif + + * Running the test suite: + + sudo ./test-user-acl.sh diff --git a/ldap/acl.ldif b/ldap/acl.ldif new file mode 100644 index 0000000..5af52aa --- /dev/null +++ b/ldap/acl.ldif @@ -0,0 +1,293 @@ +# Load this file with +# +# ldapmodify -Y EXTERNAL -H ldapi:/// -f acl.ldif +# +# It will remove existing ACLs, and add the following instead. Ensure +# that it's indeed the database #1 that you want to amend: +# +# ldapsearch -Q -LLL -Y EXTERNAL -H ldapi:/// -b "cn=config" "olcSuffix=o=mailHosting,dc=fripost,dc=dev" dn +# +# +# /!\ ATTENTION! Every modification made to this file should be +# /!\ implemented in the test suite as well! +# +# +# References: +# - http://www.openldap.org/doc/admin24/access-control.html +# - http://www.openldap.org/faq/data/cache/189.html +# - http://www.openldap.org/faq/data/cache/1140.html +# - http://www.openldap.org/faq/data/cache/1133.html +# - man 5 slapd.access + + +dn: olcDatabase={1}hdb,cn=config +changetype: modify +replace: olcAccess +## Managers have read/write access to the "virtual" subtree. +#olcAccess: to dn.subtree="ou=virtual,o=mailHosting,dc=fripost,dc=dev" +# by dn.onelevel="ou=managers,o=mailHosting,dc=fripost,dc=org" write +# by * break +#- +## 1. Users/Services/Managers can change their password (but not read it). +## 2. Anonymous users/services/managers can bind. +## 3. Else, we inspect the 2 following ACLs. +#add: olcAccess +olcAccess: to dn.subtree="o=mailHosting,dc=fripost,dc=dev" + attrs=userPassword + by self =w + by anonymous auth + by users none break +- +# The postmaster of a domain can change (replace) his/her users' password. +add: olcAccess +olcAccess: to dn.regex="^fvu=[^,]+,(fvd=[^,]+,ou=virtual,o=mailHosting,dc=fripost,dc=dev)$" + filter=(objectClass=fripostVirtualMailbox) + attrs=userPassword + by group/fripostVirtualDomain/fripostPostmaster.expand="$1" =w +- +# No permission on the userPassword attribute for other users. +# (That's a catch-all, just to be sure that services, etc. cannot read the passwords). +add: olcAccess +olcAccess: to dn.subtree="o=mailHosting,dc=fripost,dc=dev" + attrs=userPassword + by * none +#- +## Services can read the whole subtree (minus the userPassword attributes). +#add: olcAccess +#olcAccess: to dn.subtree="o=mailHosting,dc=fripost,dc=dev" +# attrs=entry,creatorsName,@fripostVirtualDomain,@fripostVirtualMailbox,@fripostVirtualAlias,@fripostVirtualML +# by dn.onelevel="ou=services,o=mailHosting,dc=fripost,dc=org" read +# by users * break +- +# Users can search (e.g., to list the entries they have created). +# Additional permissions may be added later on. +add: olcAccess +olcAccess: to dn.subtree="ou=virtual,o=mailHosting,dc=fripost,dc=dev" + attrs=entry,creatorsName,fripostOwner,fripostPostmaster,fripostCanCreateAlias,fripostCanCreateML + by users =s break +- +# Everyone can delete domains. (Provided he has +d access to the "entry" +# attribute of the domains he wants to delete.) +add: olcAccess +olcAccess: to dn.base="ou=virtual,o=mailHosting,dc=fripost,dc=dev" + attrs=children + by users =z +- +# 1. The postmaster of a domain can give (or take back) people the right to create +# aliases. +# 2,3. People that can create aliases can list the members of the group. +add: olcAccess +olcAccess: to dn.regex="^fvd=[^,]+,ou=virtual,o=mailHosting,dc=fripost,dc=dev$" + filter=(objectClass=fripostVirtualDomain) + attrs=fripostCanCreateAlias + by dnattr=fripostPostmaster write + by dnattr=fripostOwner read + by set.exact="this/fripostCanCreateAlias & (user | user/-1)" read +- +# 1. The postmaster of a domain can give (or take back) people the right to create +# mailing lists. +# 2,3. People that can create mailing lists can list the members of the group. +add: olcAccess +olcAccess: to dn.regex="^fvd=[^,]+,ou=virtual,o=mailHosting,dc=fripost,dc=dev$" + filter=(objectClass=fripostVirtualDomain) + attrs=fripostCanCreateML + by dnattr=fripostPostmaster write + by dnattr=fripostOwner read + by set.exact="this/fripostCanCreateML & (user | user/-1)" read +- +# 1-3. Noone (but the managers) can appoint domain Owners or Postmasters. +# But people that can create aliases and mailing lists can list the members of their group. +add: olcAccess +olcAccess: to dn.regex="^(fvd=[^,]+,ou=virtual,o=mailHosting,dc=fripost,dc=dev)$" + filter=(objectClass=fripostVirtualDomain) + attrs=fripostOwner,fripostPostmaster + by dnattr=fripostOwner read + by dnattr=fripostPostmaster read + by set.exact="(this/fripostCanCreateAlias | this/fripostCanCreateML)& (user | user/-1)" read + by dn.onelevel,expand="$1" +d + by users +0 +- +# Every one can add or delete children, but we will be carefull with the +# kid's "entry" attribute, which require +a and +z to add and delete +# respectively. +add: olcAccess +olcAccess: to dn.regex="^fvd=[^,]+,ou=virtual,o=mailHosting,dc=fripost,dc=dev$" + filter=(objectClass=fripostVirtualDomain) + attrs=children + by users +w +- +# 1. Domain owners can edit their entry's attributes. +# 2. So can domain postmasters. +# 3. Domain users can read the public domain attributes. +# 4. So can users with "canCreateAlias" or "canCreateML" access. +add: olcAccess +olcAccess: to dn.regex="^(fvd=[^,]+,ou=virtual,o=mailHosting,dc=fripost,dc=dev)$" + filter=(objectClass=fripostVirtualDomain) + attrs=fvd,fripostIsStatusActive,description + by dnattr=fripostOwner write + by dnattr=fripostPostmaster write + by dn.onelevel,expand="$1" read + by set.exact="(this/fripostCanCreateAlias | this/fripostCanCreateML) & (user | user/-1)" read +- +# 1. Domain owners can edit their entry's attributes. +# 2. So can domain postmasters. +add: olcAccess +olcAccess: to dn.regex="^fvd=[^,]+,ou=virtual,o=mailHosting,dc=fripost,dc=dev$" + filter=(objectClass=fripostVirtualDomain) + attrs=@fripostVirtualDomain + by dnattr=fripostOwner write + by dnattr=fripostPostmaster write + by users +0 +- +# 1. Domain owners can delete the domain (and read the entry). +# 2. So can domain postmasters. +# 3. Domain users can read the domain entry (but not delete it). +# 4. So can users with "canCreateAlias" or "canCreateML" rights. +add: olcAccess +olcAccess: to dn.regex="^(fvd=[^,]+,ou=virtual,o=mailHosting,dc=fripost,dc=dev)$" + filter=(objectClass=fripostVirtualDomain) + attrs=entry + by dnattr=fripostOwner +zrd + by dnattr=fripostPostmaster +zrd + by dn.onelevel,expand="$1" +rd + by set.exact="(this/fripostCanCreateAlias | this/fripostCanCreateML) & (user | user/-1)" +rd + by users +0 +- +# Noone (but the managers) can change quotas. +add: olcAccess +olcAccess: to dn.regex="^fvu=[^,]+,(fvd=[^,]+,ou=virtual,o=mailHosting,dc=fripost,dc=dev)$" + filter=(objectClass=fripostVirtualMailbox) + attrs=fripostMailboxQuota + by self read + by group/fripostVirtualDomain/fripostPostmaster.expand="$1" read +- +# 1. Users can modify their own entry. +# 2. So can their postmasters. +add: olcAccess +olcAccess: to dn.regex="^fvu=[^,]+,(fvd=[^,]+,ou=virtual,o=mailHosting,dc=fripost,dc=dev)$" + filter=(objectClass=fripostVirtualMailbox) + attrs=@FripostVirtualMailbox + by self write + by group/fripostVirtualDomain/fripostPostmaster.expand="$1" write +- +# 1. Postmasters can create mailboxes (but not delete them). +# (Provided that they have +a access to the parent's "children" attribute.) +# 2. Users can read their entry (but not delete it). +add: olcAccess +olcAccess: to dn.regex="^fvu=[^,]+,(fvd=[^,]+,ou=virtual,o=mailHosting,dc=fripost,dc=dev)$" + filter=(objectClass=fripostVirtualMailbox) + attrs=entry + by group/fripostVirtualDomain/fripostPostmaster.expand="$1" +ard + by self +rd +- +# Reserved aliases cannot be deactivated. (But the alias definition may be changed by the +# domain owner.) +add: olcAccess +olcAccess: to dn.regex="^fva=(abuse|postmaster),(fvd=[^,]+,ou=virtual,o=mailHosting,dc=fripost,dc=dev)$" + filter=(objectClass=fripostVirtualAlias) + attrs=fripostIsStatusActive,fripostOwner,fva + by group/fripostVirtualDomain/fripostOwner.expand="$2" read + by group/fripostVirtualDomain/fripostPostmaster.expand="$2" read + by users +0 +- +# Reserved aliases cannot be deleted. +add: olcAccess +olcAccess: to dn.regex="^fva=(abuse|postmaster),(fvd=[^,]+,ou=virtual,o=mailHosting,dc=fripost,dc=dev)$" + filter=(objectClass=fripostVirtualAlias) + attrs=entry + by group/fripostVirtualDomain/fripostOwner.expand="$2" +ard + by group/fripostVirtualDomain/fripostPostmaster.expand="$2" +ard + by set.exact="this/-1/fripostCanCreateAlias & (user | user/-1)" +a + by users +0 +- +# 1. The alias owner can list the ownership of the entry. +# 2. The domain owner can add/delete/change the ownership of the entry. +# 3. So can the domain postmasters. +add: olcAccess +olcAccess: to dn.regex="^fva=[^,]+,(fvd=[^,]+,ou=virtual,o=mailHosting,dc=fripost,dc=dev)$" + filter=(objectClass=fripostVirtualAlias) + attrs=fripostOwner + by dnattr=fripostOwner read continue + by group/fripostVirtualDomain/fripostOwner.expand="$1" write + by group/fripostVirtualDomain/fripostPostmaster.expand="$1" write + by users +0 +- +# 1. The alias owners can edit the rest of their entry's attributes. +# 2. So can the domain owners. +# 3. So can the domain postmasters. +add: olcAccess +olcAccess: to dn.regex="^fva=[^,]+,(fvd=[^,]+,ou=virtual,o=mailHosting,dc=fripost,dc=dev)$" + filter=(objectClass=fripostVirtualAlias) + attrs=@FripostVirtualAlias + by dnattr=fripostOwner write + by group/fripostVirtualDomain/fripostOwner.expand="$1" write + by group/fripostVirtualDomain/fripostPostmaster.expand="$1" write +- +# 1. The alias owners can read and delete the entry. +# 2. So can the domain owner. +# 3. So can the domain postmaster. +# 4. Users with "canCreateAlias" access (either explicitely, or as a wildcard) for the domain can create aliases for that domain. +# (But *not* delete them, unless also owner.) +add: olcAccess +olcAccess: to dn.regex="^fva=[^,]+,(fvd=[^,]+,ou=virtual,o=mailHosting,dc=fripost,dc=dev)$" + filter=(objectClass=fripostVirtualAlias) + attrs=entry + by dnattr=fripostOwner +zrd continue + by group/fripostVirtualDomain/fripostOwner.expand="$1" +wrd + by group/fripostVirtualDomain/fripostPostmaster.expand="$1" +wrd + by set.exact="this/-1/fripostCanCreateAlias & (user | user/-1)" +a + by users +0 +- +# 1. The mailing list owner can list the ownership of the entry. +# 2. The domain owner can add/delete/change the ownership of the entry. +# 3. So can the domain postmasters. +add: olcAccess +olcAccess: to dn.regex="^fvml=[^,]+,(fvd=[^,]+,ou=virtual,o=mailHosting,dc=fripost,dc=dev)$" + filter=(objectClass=fripostVirtualML) + attrs=fripostOwner + by dnattr=fripostOwner read continue + by group/fripostVirtualDomain/fripostOwner.expand="$1" write + by group/fripostVirtualDomain/fripostPostmaster.expand="$1" write + by users +0 +- +# 1. The mailing list owner read (but not edit) the transport-related attributes. +# 2. So can the domain ower. +# 3. So can the domain postmaster. +add: olcAccess +olcAccess: to dn.regex="^fvml=[^,]+,(fvd=[^,]+,ou=virtual,o=mailHosting,dc=fripost,dc=dev)$" + filter=(objectClass=fripostVirtualML) + attrs=fripostMLManager,fripostMLCommand + by dnattr=fripostOwner read + by group/fripostVirtualDomain/fripostOwner.expand="$1" read + by group/fripostVirtualDomain/fripostPostmaster.expand="$1" read +- +# 1. The mailing list owners can edit their entry's attributes. +# 2. So can the domain owners. +# 3. So can the domain postmasters. +add: olcAccess +olcAccess: to dn.regex="^fvml=[^,]+,(fvd=[^,]+,ou=virtual,o=mailHosting,dc=fripost,dc=dev)$" + filter=(objectClass=fripostVirtualML) + attrs=@FripostVirtualML + by dnattr=fripostOwner write + by group/fripostVirtualDomain/fripostOwner.expand="$1" write + by group/fripostVirtualDomain/fripostPostmaster.expand="$1" write +- +# 1. The mailing list owners can read and delete the entry. +# 2. So can the domain's Owner. +# 3. So can the domain's Postmaster. +# 4. Users with "canCreateML" capability (either explicitely, or as a wildcard) for the domain can create mailing lists for that domain. +# (But *not* delete them, unless also owner.) +add: olcAccess +olcAccess: to dn.regex="^fvml=[^,]+,(fvd=[^,]+,ou=virtual,o=mailHosting,dc=fripost,dc=dev)$" + filter=(objectClass=fripostVirtualML) + attrs=entry + by dnattr=fripostOwner +rzd continue + by group/fripostVirtualDomain/fripostOwner.expand="$1" +rwd + by group/fripostVirtualDomain/fripostPostmaster.expand="$1" +rwd + by set.exact="this/-1/fripostCanCreateML & (user | user/-1)" +a + by users +0 +- +# Catch the "break" control above. +add: olcAccess +olcAccess: to dn.subtree="ou=virtual,o=mailHosting,dc=fripost,dc=dev" + by users +0 diff --git a/ldap/database.ldif b/ldap/database.ldif new file mode 100644 index 0000000..300d933 --- /dev/null +++ b/ldap/database.ldif @@ -0,0 +1,60 @@ +# Load this file with +# +# ldapadd -Y EXTERNAL -H ldapi:/// -f database.ldif +# +# It will create a new database under `/var/lib/ldap/dev', which has to +# be an existing directory: +# +# mkdir -m 0700 /var/lib/ldap/dev && chown openldap:openldap /var/lib/ldap/dev + + +dn: olcDatabase=hdb,cn=config +objectClass: olcDatabaseConfig +objectClass: olcHdbConfig +olcDbDirectory: /var/lib/ldap/dev +olcSuffix: o=mailHosting,dc=fripost,dc=dev +olcLastMod: TRUE +olcDbCheckpoint: 512 30 +# Require LDAPv3 protocol and authentication prior to directory +# operations. +olcRequires: LDAPv3 authc +# We don't want to give "canCreate{Alias,ML}" write access to alias/ml +# attributes. +olcAddContentAcl: FALSE +# The root user has all rights on the whole database (when SASL-binding +# on a UNIX socket). +olcRootDN: gidNumber=0+uidNumber=0,cn=peercred,cn=external,cn=auth + + +# +# Performance considerations +# +# References: +# - https://wiki.zimbra.com/wiki/OpenLDAP_Performance_Tuning_5.0 +# - http://www.openldap.org/doc/admin24/tuning.html +# +# +# 1. On single- and dual-core systems, change the maximum number of +# threads to 8. (The default, 16, is fine for 4- and 8-core systems.) +# +# dn: cn=config +# changetype: modify +# add: olcThreads +# olcThreads: 8 +# +# +# 2. It may be a good idea to modify DB_CONFIG, depending on the output +# of +# +# db4.8_stat -m -h /var/lib/ldap/ | head -16 +# +# (For optimal performance, the Requested pages found in the cache +# should be above 95%, and the pages forced from the cache should be 0.) +# +# and +# +# db4.8_stat -m -h /var/lib/ldap/ | head -16 +# +# (For optimal performance, usage should be within 85% of the configured +# values.) +# diff --git a/ldap/fripost.ldif b/ldap/fripost.ldif new file mode 100644 index 0000000..e0052a3 --- /dev/null +++ b/ldap/fripost.ldif @@ -0,0 +1,140 @@ +# Load this file with +# +# ldapadd -Y EXTERNAL -H ldapi:/// -f fripost.ldif +# +# It will load the schema. To perform modifications, the easiest way is to +# +# * Save the database: slapcat -b 'o=mailHosting,dc=fripost,dc=dev' > /tmp/db.ldif +# * Save the configuration: slapcat -n0 > /tmp/config.ldif +# * Backup slap.d: cp -a /etc/ldap/slapd.d/ /tmp/slap.d_back +# * Edit the schema in /tmp/config.ldif +# * Load the new config: mkdir -m 0700 /tmp/slapd.d_new && slapadd -F /tmp/slapd.d_new -n0 -l /tmp/config.ldif +# * Stop slapd: /etc/init.d/slapd stop +# * Load the new config: rm -rf /etc/ldap/slapd.d/ && mv /tmp/slapd.d_new /etc/ldap/slapd.d && chown -R openldap:openldap /etc/ldap/slapd.d +# * Create indexes: su openldap -c "slapindex -b 'o=mailHosting,dc=fripost,dc=dev'" +# * Start slapd: /etc/init.d/slapd start +# If it fails, remove the existing database and see what's wrong +# rm -rf /var/lib/ldap/dev/* && su openldap -c "slapadd -b 'o=mailHosting,dc=fripost,dc=org' -l /tmp/db.ldif" +# +# +# /!\ ATTENTION! Every modification made to this file should be +# /!\ implemented in the test suite as well! +# +# +# References: +# - http://courier.svn.sourceforge.net/svnroot/courier/trunk/courier-authlib/authldap.schema +# - http://www.qmail-ldap.org/wiki/index.php/Qmail.schema +# - http://www.wanderingbarque.com/howtos/mailserver/mailserver.html + + +# OID prefix: 1.3.6.1.4.1.40011 + +# This schema depends on: +# - core.schema +# - cosine.schema +# - nis.schema + + +dn: cn=fripost-master,cn=schema,cn=config +objectClass: olcSchemaConfig +# +# Attributes: 1.3.6.1.4.1.40011.1.1 +# +olcAttributeTypes: ( 1.3.6.1.4.1.40011.1.2.1.1 NAME 'fvd' + DESC 'A virtual mail domain' + EQUALITY caseIgnoreIA5Match + SUBSTR caseIgnoreIA5SubstringsMatch + SYNTAX 1.3.6.1.4.1.1466.115.121.1.26{256} SINGLE-VALUE ) +# +olcAttributeTypes: ( 1.3.6.1.4.1.40011.1.2.1.2 NAME 'fvu' + DESC 'The local part of a virtual user' + EQUALITY caseIgnoreIA5Match + SUBSTR caseIgnoreIA5SubstringsMatch + SYNTAX 1.3.6.1.4.1.1466.115.121.1.26{64} SINGLE-VALUE ) +# +olcAttributeTypes: ( 1.3.6.1.4.1.40011.1.2.1.3 NAME 'fva' + DESC 'The local part of a virtual mail alias' + EQUALITY caseIgnoreIA5Match + SUBSTR caseIgnoreIA5SubstringsMatch + SYNTAX 1.3.6.1.4.1.1466.115.121.1.26{64} SINGLE-VALUE ) +# +olcAttributeTypes: ( 1.3.6.1.4.1.40011.1.2.1.4 NAME 'fvml' + DESC 'The local part of a virtual mailing list' + EQUALITY caseIgnoreIA5Match + SUBSTR caseIgnoreIA5SubstringsMatch + SYNTAX 1.3.6.1.4.1.1466.115.121.1.26{64} SINGLE-VALUE ) +# +olcAttributeTypes: ( 1.3.6.1.4.1.40011.1.2.1.5 NAME 'fripostMLCommand' + DESC 'The local part of a command associated with a mailing list' + EQUALITY caseIgnoreIA5Match + SUBSTR caseIgnoreIA5SubstringsMatch + SYNTAX 1.3.6.1.4.1.1466.115.121.1.26{64} ) +# +olcAttributeTypes: ( 1.3.6.1.4.1.40011.1.2.1.6 NAME 'fripostMaildrop' + DESC 'An email address the virtual alias should be mapped to' + EQUALITY caseIgnoreIA5Match + SUBSTR caseIgnoreIA5SubstringsMatch + SYNTAX 1.3.6.1.4.1.1466.115.121.1.26{256} ) +# +olcAttributeTypes: ( 1.3.6.1.4.1.40011.1.2.1.7 NAME 'fripostIsStatusActive' + DESC 'Is the entry active?' + EQUALITY booleanMatch + SYNTAX 1.3.6.1.4.1.1466.115.121.1.7 SINGLE-VALUE ) +# +olcAttributeTypes: ( 1.3.6.1.4.1.40011.1.2.1.8 NAME 'fripostMailboxQuota' + DESC 'The quota on a mailbox e.g., "50MB"' + EQUALITY caseExactMatch + SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{32} SINGLE-VALUE ) +# +olcAttributeTypes: ( 1.3.6.1.4.1.40011.1.2.1.9 NAME 'fripostCanCreateAlias' + DESC 'A user/domain that can create aliases for the parent domain' + SUP distinguishedName ) +# +olcAttributeTypes: ( 1.3.6.1.4.1.40011.1.2.1.10 NAME 'fripostCanCreateML' + DESC 'A user/domain that can create mailing lists for the parent domain' + SUP distinguishedName ) +# +olcAttributeTypes: ( 1.3.6.1.4.1.40011.1.2.1.11 NAME 'fripostOwner' + DESC 'A user that owns the parent domain' + SUP distinguishedName ) +# +olcAttributeTypes: ( 1.3.6.1.4.1.40011.1.2.1.12 NAME 'fripostPostmaster' + DESC 'A user that is a postmaster of the parent domain' + SUP distinguishedName ) +# +olcAttributeTypes: ( 1.3.6.1.4.1.40011.1.2.1.13 NAME 'fripostMLManager' + DESC 'A mailing list manager' + EQUALITY caseIgnoreMatch + SUBSTR caseIgnoreSubstringsMatch + SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{64} SINGLE-VALUE ) +# +# +# Objects: 1.3.6.1.4.1.40011.1.2 +# +olcObjectclasses: ( 1.3.6.1.4.1.40011.1.2.1 NAME 'FripostVirtualDomain' + SUP top STRUCTURAL + DESC 'Virtual domain' + MUST ( fvd $ fripostIsStatusActive ) + MAY ( fripostCanCreateAlias $ fripostCanCreateML $ + fripostOwner $ fripostPostmaster $ + fripostMaildrop $ description ) ) +# +# | TODO: add limits here +olcObjectclasses: ( 1.3.6.1.4.1.40011.1.2.2 NAME 'FripostVirtualMailbox' + SUP top STRUCTURAL + DESC 'Virtual mailbox' + MUST ( fvu $ userPassword $ fripostIsStatusActive ) + MAY ( fripostMailboxQuota $ fripostMaildrop $ cn $ description) ) +# +olcObjectclasses: ( 1.3.6.1.4.1.40011.1.2.3 NAME 'FripostVirtualAlias' + SUP top STRUCTURAL + DESC 'Virtual alias' + MUST ( fva $ fripostMaildrop $ fripostIsStatusActive ) + MAY ( fripostOwner $ description ) ) +# +olcObjectclasses: ( 1.3.6.1.4.1.40011.1.2.4 NAME 'FripostVirtualML' + SUP top STRUCTURAL + DESC 'Mailing List' + MUST ( fvml $ fripostMLManager $ fripostIsStatusActive ) + MAY ( fripostMLCommand $ fripostOwner $ description ) ) + diff --git a/ldap/index.ldif b/ldap/index.ldif new file mode 100644 index 0000000..d5f31a5 --- /dev/null +++ b/ldap/index.ldif @@ -0,0 +1,44 @@ +# Load this file with +# +# ldapmodify -Y EXTERNAL -H ldapi:/// -f index.ldif +# +# It will remove existing indexes, and add the following instead. Ensure +# that it's indeed the database #1 that you want to amend: +# +# ldapsearch -Q -LLL -Y EXTERNAL -H ldapi:/// -b "cn=config" "olcSuffix=o=mailHosting,dc=fripost,dc=dev" dn +# +# +# To reindex an existing database, you have to +# * Stop slapd /etc/init.d/slapd stop +# * Reindex su openldap -c "slapindex -b 'o=mailHosting,dc=fripost,dc=dev'" +# * Restart slapd /etc/init.d/slapd start +# +# +# References +# - http://www.openldap.org/doc/admin24/tuning.html +# - http://www.openldap.org/faq/data/cache/42.html +# - http://www.openldap.org/faq/data/cache/136.html +# - http://www.zytrax.com/books/ldap/apa/indeces.html + + +dn: olcDatabase={1}hdb,cn=config +changetype: modify +replace: olcDbIndex +olcDbIndex: objectClass eq +- +add: olcDbIndex +olcDbIndex: fripostIsStatusActive eq +- +add: olcDbIndex +olcDbIndex: fvd,fvu,fva,fvml,fripostMLCommand,fripostMLManager eq +- +add: olcDbIndex +olcDbIndex: fripostMaildrop pres +# ^ TODO: a presence index on fripostMaildrop is not optimal, as the +# attribute is not very rare... +# Having a different attribute for the virtualMailbox object class would +# be better. +- +# synprov specific indexing (provider side) +add: olcDbIndex +olcDbIndex: entryCSN,entryUUID eq diff --git a/ldap/modules.ldif b/ldap/modules.ldif new file mode 100644 index 0000000..0e63819 --- /dev/null +++ b/ldap/modules.ldif @@ -0,0 +1,16 @@ +# Load this file (on the provider) with +# +# ldapmodify -Y EXTERNAL -H ldapi:/// -f modules.ldif +# +# It will load the "syncprov" module. +# +# +# References: +# - http://www.openldap.org/doc/admin24/replication.html#Syncrepl +# - http://www.zytrax.com/books/ldap/ch7/#ol-syncrepl-rap + + +dn: cn=module{0}, cn=config +changetype: modify +add: olcModuleLoad +olcModuleLoad: syncprov.la diff --git a/ldap/populate.ldif b/ldap/populate.ldif new file mode 100644 index 0000000..04d5177 --- /dev/null +++ b/ldap/populate.ldif @@ -0,0 +1,199 @@ +# Load this file with +# +# ldapadd -Y EXTERNAL -H ldapi:/// -f populate.ldif +# +# It will populate the directory for testing purposes. +# If "o=mailHosting,dc=fripost,dc=dev" exists, you can delete it with +# +# ldapdelete -Y EXTERNAL -H ldapi:/// -r "ou=virtual,o=mailHosting,dc=fripost,dc=dev" + +# ou=quotas,o=mailHosting,dc=fripost,dc=dev +# |- fvd=fripost.org +# | maxAccounts: 20 fripost.org +# | maxAlias: 5 user@fripost.org +# |- + + +dn: o=mailHosting,dc=fripost,dc=dev +objectClass: organization +description: Mail hosting + + +dn: ou=virtual,o=mailHosting,dc=fripost,dc=dev +objectClass: organizationalUnit +description: Virtual mail hosting + + +# An independent domain, not self managed +dn: fvd=fripost.org,ou=virtual,o=mailHosting,dc=fripost,dc=dev +objectClass: fripostVirtualDomain +fripostCanCreateAlias: fvu=fake,fvd=fripost.org,ou=virtual,o=mailHosting,dc=fripost,dc=dev +fripostCanCreateML: fvu=fake,fvd=fripost.org,ou=virtual,o=mailHosting,dc=fripost,dc=dev +fripostIsStatusActive: TRUE + +dn: fvu=user1,fvd=fripost.org,ou=virtual,o=mailHosting,dc=fripost,dc=dev +objectClass: fripostVirtualMailbox +userPassword: user1 +fripostIsStatusActive: TRUE + +dn: fvu=user2,fvd=fripost.org,ou=virtual,o=mailHosting,dc=fripost,dc=dev +objectClass: fripostVirtualMailbox +userPassword: user2 +fripostIsStatusActive: TRUE + +# A owned alias +dn: fva=alias1,fvd=fripost.org,ou=virtual,o=mailHosting,dc=fripost,dc=dev +objectClass: fripostVirtualAlias +fripostIsStatusActive: TRUE +fripostOwner: fvu=user1,fvd=fripost.org,ou=virtual,o=mailHosting,dc=fripost,dc=dev +fripostMaildrop: user1@fripost.org +fripostMaildrop: user1@example.org +# Buggy owner +fripostOwner: fvd=fripost.org,ou=virtual,o=mailHosting,dc=fripost,dc=dev + +# An independent alias +dn: fva=alias2,fvd=fripost.org,ou=virtual,o=mailHosting,dc=fripost,dc=dev +objectClass: fripostVirtualAlias +fripostIsStatusActive: TRUE +fripostMaildrop: user2@fripost.org + +# A owned mailing list +dn: fvml=ml1,fvd=fripost.org,ou=virtual,o=mailHosting,dc=fripost,dc=dev +objectClass: fripostVirtualML +fripostMLManager: mailman +fripostIsStatusActive: TRUE +fripostMLCommand: ml1-request +fripostMLCommand: ml1-bounces +fripostOwner: fvu=user1,fvd=fripost.org,ou=virtual,o=mailHosting,dc=fripost,dc=dev + +# An independent mailing list (for user1) +dn: fvml=ml2,fvd=fripost.org,ou=virtual,o=mailHosting,dc=fripost,dc=dev +objectClass: fripostVirtualML +fripostMLManager: schleuder +fripostIsStatusActive: TRUE +fripostOwner: fvu=user2,fvd=fripost.org,ou=virtual,o=mailHosting,dc=fripost,dc=dev +# Buggy owner +fripostOwner: fvd=fripost.org,ou=virtual,o=mailHosting,dc=fripost,dc=dev + + +# An independent domain, with canCreateAlias options +dn: fvd=example.org,ou=virtual,o=mailHosting,dc=fripost,dc=dev +objectClass: fripostVirtualDomain +fripostCanCreateAlias: fvd=fripost.org,ou=virtual,o=mailHosting,dc=fripost,dc=dev +fripostIsStatusActive: TRUE + +# A owned alias +dn: fva=alias1,fvd=example.org,ou=virtual,o=mailHosting,dc=fripost,dc=dev +objectClass: fripostVirtualAlias +fripostIsStatusActive: TRUE +fripostOwner: fvu=user1,fvd=fripost.org,ou=virtual,o=mailHosting,dc=fripost,dc=dev +fripostMaildrop: user1@fripost.org + + +# An independent domain, with canCreateML options +dn: fvd=example2.org,ou=virtual,o=mailHosting,dc=fripost,dc=dev +objectClass: fripostVirtualDomain +fripostCanCreateML: fvd=fripost.org,ou=virtual,o=mailHosting,dc=fripost,dc=dev +fripostIsStatusActive: TRUE + +# A owned mailing list +dn: fvml=ml1,fvd=example2.org,ou=virtual,o=mailHosting,dc=fripost,dc=dev +objectClass: fripostVirtualML +fripostIsStatusActive: TRUE +fripostMLManager: schleuder +fripostOwner: fvu=user2,fvd=fripost.org,ou=virtual,o=mailHosting,dc=fripost,dc=dev + + +# An independent domain, with both can createAlias and canCreateML options +dn: fvd=example3.org,ou=virtual,o=mailHosting,dc=fripost,dc=dev +objectClass: fripostVirtualDomain +fripostCanCreateAlias: fvu=user2,fvd=fripost.org,ou=virtual,o=mailHosting,dc=fripost,dc=dev +fripostCanCreateML: fvu=user2,fvd=fripost.org,ou=virtual,o=mailHosting,dc=fripost,dc=dev +fripostIsStatusActive: TRUE + +# A owned mailing list +dn: fvml=ml,fvd=example3.org,ou=virtual,o=mailHosting,dc=fripost,dc=dev +objectClass: fripostVirtualML +fripostIsStatusActive: TRUE +fripostMLManager: mailman +fripostOwner: fvu=user1,fvd=fripost.org,ou=virtual,o=mailHosting,dc=fripost,dc=dev + + +# A owned domain +dn: fvd=owned.org,ou=virtual,o=mailHosting,dc=fripost,dc=dev +objectClass: fripostVirtualDomain +fripostIsStatusActive: TRUE +fripostOwner: fvu=user1,fvd=fripost.org,ou=virtual,o=mailHosting,dc=fripost,dc=dev +# Buggy owner +fripostOwner: fvd=fripost.org,ou=virtual,o=mailHosting,dc=fripost,dc=dev + +dn: fva=alias,fvd=owned.org,ou=virtual,o=mailHosting,dc=fripost,dc=dev +objectClass: fripostVirtualAlias +fripostIsStatusActive: TRUE +fripostMaildrop: user1@fripost.org + +dn: fva=abuse,fvd=owned.org,ou=virtual,o=mailHosting,dc=fripost,dc=dev +objectClass: fripostVirtualAlias +fripostIsStatusActive: TRUE +fripostMaildrop: abuse@fripost.org + +dn: fva=postmaster,fvd=owned.org,ou=virtual,o=mailHosting,dc=fripost,dc=dev +objectClass: fripostVirtualAlias +fripostIsStatusActive: TRUE +fripostMaildrop: postmaster@fripost.org + +dn: fvml=ml,fvd=owned.org,ou=virtual,o=mailHosting,dc=fripost,dc=dev +objectClass: fripostVirtualML +fripostMLManager: mailman +fripostIsStatusActive: TRUE +fripostMLCommand: ml-request +fripostMLCommand: ml-bounces + +dn: fvu=user,fvd=owned.org,ou=virtual,o=mailHosting,dc=fripost,dc=dev +objectClass: fripostVirtualMailbox +userPassword: user +fripostIsStatusActive: TRUE + + +# A postmastered domain +dn: fvd=postmastered.org,ou=virtual,o=mailHosting,dc=fripost,dc=dev +objectClass: fripostVirtualDomain +fripostIsStatusActive: TRUE +fripostPostmaster: fvu=user1,fvd=fripost.org,ou=virtual,o=mailHosting,dc=fripost,dc=dev +fripostPostmaster: fvu=postmaster,fvd=postmastered.org,ou=virtual,o=mailHosting,dc=fripost,dc=dev +# Buggy owner +fripostPostmaster: fvd=fripost.org,ou=virtual,o=mailHosting,dc=fripost,dc=dev +fripostCanCreateAlias: fvu=user,fvd=postmastered.org,ou=virtual,o=mailHosting,dc=fripost,dc=dev + +dn: fva=alias,fvd=postmastered.org,ou=virtual,o=mailHosting,dc=fripost,dc=dev +objectClass: fripostVirtualAlias +fripostIsStatusActive: TRUE +fripostMaildrop: user1@fripost.org + +dn: fva=abuse,fvd=postmastered.org,ou=virtual,o=mailHosting,dc=fripost,dc=dev +objectClass: fripostVirtualAlias +fripostIsStatusActive: TRUE +fripostMaildrop: abuse@fripost.org +fripostOwner: fvu=postmaster,fvd=postmastered.org,ou=virtual,o=mailHosting,dc=fripost,dc=dev + +dn: fva=postmaster,fvd=postmastered.org,ou=virtual,o=mailHosting,dc=fripost,dc=dev +objectClass: fripostVirtualAlias +fripostIsStatusActive: TRUE +fripostMaildrop: postmaster@fripost.org + +dn: fvml=ml,fvd=postmastered.org,ou=virtual,o=mailHosting,dc=fripost,dc=dev +objectClass: fripostVirtualML +fripostMLManager: mailman +fripostIsStatusActive: TRUE +fripostMLCommand: ml-request +fripostMLCommand: ml-bounces + +dn: fvu=user,fvd=postmastered.org,ou=virtual,o=mailHosting,dc=fripost,dc=dev +objectClass: fripostVirtualMailbox +userPassword: user +fripostIsStatusActive: TRUE + +dn: fvu=postmaster,fvd=postmastered.org,ou=virtual,o=mailHosting,dc=fripost,dc=dev +objectClass: fripostVirtualMailbox +userPassword: postmaster +fripostIsStatusActive: TRUE diff --git a/ldap/syncprov.ldif b/ldap/syncprov.ldif new file mode 100644 index 0000000..66ce154 --- /dev/null +++ b/ldap/syncprov.ldif @@ -0,0 +1,25 @@ +# Load this file (on the provider) with +# +# ldapadd -Y EXTERNAL -H ldapi:/// -f syncprov.ldif +# +# It will load the "syncprov" overlay configuration for the database #1. +# Ensure that it's indeed the database #1 that you want to configure: +# +# ldapsearch -Q -LLL -Y EXTERNAL -H ldapi:/// -b "cn=config" "olcSuffix=o=mailHosting,dc=fripost,dc=dev" dn +# +# +# References: +# - http://www.openldap.org/doc/admin24/replication.html#Syncrepl +# - http://www.zytrax.com/books/ldap/ch7/#ol-syncrepl-rap +# - man 5 slapo-syncprov + + +dn: olcOverlay=syncprov,olcDatabase={1}hdb,cn=config +objectClass: olcOverlayConfig +objectClass: olcSyncProvConfig +olcOverlay: syncprov +# contextCSN saved to database every 50 updates or 5 +# minutes +olcSpCheckpoint: 50 5 +syncprov-reloadhint: TRUE + diff --git a/ldap/syncrepl.ldif b/ldap/syncrepl.ldif new file mode 100644 index 0000000..6b9c378 --- /dev/null +++ b/ldap/syncrepl.ldif @@ -0,0 +1,33 @@ +# Load this file (on the consumer) with +# +# ldapmodify -Y EXTERNAL -H ldapi:/// -f syncrepl.ldif +# +# It will load the "syncRepl" directive for the database #1. Ensure +# that it's indeed the database #1 that you want to configure: +# +# ldapsearch -Q -LLL -Y EXTERNAL -H ldapi:/// -b "cn=config" "olcSuffix=o=mailHosting,dc=fripost,dc=dev" dn +# +# +# References: +# - http://www.openldap.org/doc/admin24/replication.html#Syncrepl +# - http://www.zytrax.com/books/ldap/ch7/#ol-syncrepl-rap + +# TODO: Delta-syncrepl configuration? http://www.openldap.org/doc/admin24/replication.html#Delta-syncrepl + +dn: olcDatabase={1}hdb,cn=config +changetype: modify +replace: olcSyncRepl +# Increase the rid for the different consumers +olcSyncRepl: rid=000 +provider=ldap://127.0.0.1:3890 +bindmethod=simple +binddn="cn=SMTP,ou=services,o=mailHosting,dc=fripost,dc=org" +credentials="xxxxxx" +type=refreshAndPersist +retry="5 5 300 +" +searchbase="ou=virtual,o=mailHosting,dc=fripost,dc=org" +filter="(&(|(objectClass=FripostVirtualDomain)(objectClass=FripostVirtualMailbox)(objectClass=FripostVirtualAlias)(objectClass=FripostVirtualML))(fripostIsStatusActive=TRUE))" +attrs="fripostIsStatusActive,fripostMaildrop,fvd,fvu,fva,fvml,fripostMLCommand,fripostMLManager" +scope=sub +schemachecking=off + diff --git a/ldap/test-user-acl.sh b/ldap/test-user-acl.sh new file mode 100755 index 0000000..4b233ef --- /dev/null +++ b/ldap/test-user-acl.sh @@ -0,0 +1,906 @@ +#!/bin/sh + +# This a test suite for the user ACLs. +# *Every* modification to the schema and/or to the ACLs should be made +# here too! It is crucial that the test suite doesn't break. +# +# Requires a populated database with every possible test case, like +# `populate.ldif'. +# The database remains unchanged, but slapacl needs to have read access, +# so the test suite may have to be run by root. + + +SLAPACL=/usr/sbin/slapacl +SUFFIX="ou=virtual,o=mailHosting,dc=fripost,dc=dev" + +RES=$(tempfile) || exit 1 + +checkACL () { + CMD=${SLAPACL} + BIND="${1},${SUFFIX}" + if [ -n "${1}" ]; then CMD="${CMD} -D ${BIND}"; fi + if [ -n "${2}" ]; then BASE="${2},${SUFFIX}"; else BASE="${SUFFIX}"; fi + shift; shift + + ${CMD} -b "${BASE}" "$@" 2>&1 | grep -vixF -e "authcDN: \"${BIND}\"" + N=1 +} + +msg () { + /bin/echo -n " " + /bin/echo -n "$@" + /bin/echo -n "... " +} + +isOK () { + # Ensures that every line in "$RES" satisfies the regexp "$1". + # The total count is the number of "$2" attributes, or the number of lines in "$RES". + [ -n "${1}" ] || exit 1 + cat > "${RES}" + if [ -n "${2}" ]; then + if [ -n "${3}" ]; then + TOT=$(grep -Pic "^${3} access to ${2}: " < "${RES}") + else + TOT=$(grep -Pic "^((write|add|delete|read|search|compare|auth|disclose) access to )?${2}: " < "${RES}") + fi + else + TOT=$(wc -l < "${RES}") + fi + FAILS=$(grep -vc "${1}" < "${RES}") + if [ ${FAILS} -eq 0 ]; then + /bin/echo -n "OK" + else + echo "Fail! (x${FAILS})"; exit 1 + fi + /bin/echo -n " (${TOT}/${TOT})" + echo + # We need a complete test suite + ([ -n "${TOT}" ] && [ ${TOT} -ne 0 ]) || (echo "Add more test data."; exit 1) +} + +search () { + ldapsearch -Q -LLL -Y EXTERNAL -H ldapi:// "$@" +} + + +######################################################################## + + +DOMAINS=$(search -u -b "${SUFFIX}" "objectClass=FripostVirtualDomain" dn | \ + grep -i '^ufn: ' | sed -re 's/^ufn: ([^,]+),.*/fvd=\1/') +USERS=$(search -u -b "${SUFFIX}" "objectClass=FripostVirtualMailbox" dn | \ + grep -i '^ufn: ' | sed -re 's/^ufn: ([^,]+), *([^,]+),.*/fvu=\1,fvd=\2/') +ALIASES=$(search -u -b "${SUFFIX}" "objectClass=FripostVirtualAlias" dn | \ + grep -i '^ufn: ' | sed -re 's/^ufn: ([^,]+), *([^,]+),.*/fva=\1,fvd=\2/') +MLS=$(search -u -b "${SUFFIX}" "objectClass=FripostVirtualML" dn | \ + grep -i '^ufn: ' | sed -re 's/^ufn: ([^,]+), *([^,]+),.*/fvml=\1,fvd=\2/') + + +######################################################################## + + +echo "Anonymous users:" + + +# Anonymous need to bind to any fvu +msg "Have =xd access to \"userPassword\" attributes" +for U in ${USERS}; do + checkACL "" "${U}" userPassword +done | isOK 'auth(=xd)$' +[ $? -eq 0 ] || exit $? + + +msg "Have =0 access to the rest of user entries" +for U in ${USERS}; do + checkACL "" "${U}" +done | grep -v '^userPassword=.*: auth(=xd)$' | isOK '=0$' entry +[ $? -eq 0 ] || exit $? + + +msg "Have =0 access to alias entries" +for A in ${ALIASES}; do + checkACL "" "${A}" +done | isOK '=0' entry +[ $? -eq 0 ] || exit $? + + +msg "Have =0 access to mailing lists entries" +for ML in ${MLS}; do + checkACL "" "${ML}" +done | isOK '=0' entry +[ $? -eq 0 ] || exit $? + + +msg "Have =0 access to domain entries" +for D in ${DOMAINS}; do + checkACL "" "${D}" +done | isOK '=0' entry +[ $? -eq 0 ] || exit $? + + +msg "Have =0 access to the base" +checkACL "" "" | isOK '=0' entry +[ $? -eq 0 ] || exit $? + + +########################################################################### + + +echo +echo "Authenticated users, access to the base" + + +usersB () { + for U in ${USERS}; do + checkACL "${U}" "" "$@" + done +} + + +msg "Have =s access on the base's \"entry\" attribute" +usersB entry | isOK '=s' entry +[ $? -eq 0 ] || exit $? + + +# Needed to delete domains. They cannot create domains though, as they +# would need =a on the "children" attribute. +msg "Have =z access on the base's \"children\" attribute" +usersB children | isOK '=z$' children +[ $? -eq 0 ] || exit $? + + +msg "Have =0 access to the operational attributes" +usersB structuralObjectClass entryUUID createTimestamp entryCSN modifiersName modifyTimestamp | isOK '=0$' entryUUID +[ $? -eq 0 ] || exit $? + + +########################################################################### + + +echo +echo "Authenticated users, access to domain entries" + +# * entry: +# =s-a for all +# +rd if children, canCreate{Alias,ML}, owner or postmaster +# +z if owner or postmaster +# * children: +# =w for all +# * fvd: +# =rscd if children, canCreate{Alias,ML}, owner or postmaster +# +w if owner or postmaster +# * fripostIsStatusActive +# =rscd if children, canCreate{Alias,ML}, owner or postmaster +# +w if owner or postmaster +# * fripostCanCreateAlias +# =rscd if canCreateAlias, owner or postmaster +# +w if postmaster +# * fripostCanCreateML +# =rscd if canCreateML, owner or postmaster +# +w if postmaster +# * fripostOwner +# =s for all +# +d if children +# +rc if canCreate{Alias,ML}, owner or postmaster +# * fripostPostmaster +# =s for all +# +d if children +# +rc if canCreate{Alias,ML}, owner or postmaster +# * fripostMaildrop +# =wrscd if owner or postmaster +# * description +# =rscd if children, canCreate{Alias,ML}, owner or postmaster +# +w if owner or postmaster + +usersD () { + for U in ${USERS}; do + for D in ${DOMAINS}; do + checkACL "${U}" "${D}" "$@" + done + done +} + +msg "Cannot appoint domain Owners or Postmasters; Cannot add a domain" +usersD fripostOwner/add fripostOwner/delete \ + fripostPostmaster/add fripostPostmaster/delete \ + entry/add \ + | isOK 'DENIED$' entry +[ $? -eq 0 ] || exit $? + +# We ensure not to give +a/+z access to the \"entry\" attribute of the +# children, unless justified (required to add/delete a child). +msg "Have =w access to \"children\"" +usersD children | isOK '=w$' children +[ $? -eq 0 ] || exit $? + +msg "Have >=s access on \"entry\", \"fripostOwner\" and \"fripostPostmaster\"" +usersD entry/search fripostOwner/search fripostPostmaster/search | isOK 'ALLOWED$' entry +[ $? -eq 0 ] || exit $? + +msg "Have =0 access to the operational attributes" +usersD structuralObjectClass entryUUID createTimestamp entryCSN modifiersName modifyTimestamp | isOK '=0$' entryUUID +[ $? -eq 0 ] || exit $? + + +# We check the following permissions: +# 0. Simple user +# 1. canCreateAlias (exact,wildcard) +# 2. canCreateML (exact,wildcard) +# 3. Owner +# 4. Postmaster + +# 0 +ATTRS0="entry/read entry/disclose + fvd/read fvd/search fvd/compare fvd/disclose + fripostIsStatusActive/read fripostIsStatusActive/search fripostIsStatusActive/compare fripostIsStatusActive/disclose + fripostOwner/disclose + fripostPostmaster/disclose + description/read description/search description/compare description/disclose" +msg "Have >=rscd access to the public attributes of their parent" +for U in ${USERS}; do + D="$(echo "${U}" | sed -re 's/.*,(fvd=[^,]+)$/\1/')" + checkACL "${U}" "${D}" ${ATTRS0} +done | isOK 'ALLOWED$' entry read +[ $? -eq 0 ] || exit $? + + +# 1 +ATTRSA="fripostOwner/read fripostOwner/compare + fripostPostmaster/read fripostPostmaster/compare + fripostCanCreateAlias/read fripostCanCreateAlias/search fripostCanCreateAlias/compare fripostCanCreateAlias/disclose" +msg "Have >=rscd access to the public attributes and >=a to \"children\" (if CanCreateAlias, exact)" +for U in ${USERS}; do + for D in ${DOMAINS}; do + search -s base -b "${D},${SUFFIX}" "fripostCanCreateAlias=${U},${SUFFIX}" | grep -q '^dn: ' && \ + checkACL "${U}" "${D}" children/add ${ATTRS0} ${ATTRSA} + done +done | isOK 'ALLOWED$' children +[ $? -eq 0 ] || exit $? + + +# 1 +msg "Have >=rscd to the public attributes and >=a to \"children\" (if CanCreateAlias, wildcard)" +for U in ${USERS}; do + DU="$(echo "${U}" | sed -re 's/.*,(fvd=[^,]+)$/\1/')" + for D in ${DOMAINS}; do + search -s base -b "${D},${SUFFIX}" "fripostCanCreateAlias=${DU},${SUFFIX}" | grep -q '^dn: ' && \ + checkACL "${U}" "${D}" children/add ${ATTRS0} ${ATTRSA} + done +done | isOK 'ALLOWED$' children +[ $? -eq 0 ] || exit $? + + +# 2 +ATTRSML="fripostOwner/read fripostOwner/compare + fripostPostmaster/read fripostPostmaster/compare + fripostCanCreateML/read fripostCanCreateML/search fripostCanCreateML/compare fripostCanCreateML/disclose" +msg "Have >=rscd access to the public attributes and >=a to \"children\" (if CanCreateML, exact)" +for U in ${USERS}; do + for D in ${DOMAINS}; do + search -s base -b "${D},${SUFFIX}" "fripostCanCreateML=${U},${SUFFIX}" | grep -q '^dn: ' && \ + checkACL "${U}" "${D}" children/add ${ATTRS0} ${ATTRSML} + done +done | isOK 'ALLOWED$' children +[ $? -eq 0 ] || exit $? + + +# 2 +msg "Have >=rscd access to the public attributes and >=a to \"children\" (if CanCreateML, wildcard)" +for U in ${USERS}; do + DU="$(echo "${U}" | sed -re 's/.*,(fvd=[^,]+)$/\1/')" + for D in ${DOMAINS}; do + search -s base -b "${D},${SUFFIX}" "fripostCanCreateML=${DU},${SUFFIX}" | grep -q '^dn: ' && \ + checkACL "${U}" "${D}" children/add ${ATTRS0} ${ATTRSML} + done +done | isOK 'ALLOWED$' children +[ $? -eq 0 ] || exit $? + + +# 3 +# >=w to "children", =zrscd to "entry", >=rscd to "fripostCanCreateAlias" and +# "fripostCanCreateML", and =wrscd to the rest (other than "Owner" and +# Postmaster") +msg "Have =wrscd to the domain attributes (other than \"canCreate\"), and >=w to \"children\" (if Owner)" +ATTRSO="entry/delete + fvd/write + fripostIsStatusActive/write + fripostMaildrop/delete fripostMaildrop/add fripostMaildrop/read fripostMaildrop/search fripostMaildrop/compare fripostMaildrop/disclose + description/add description/delete" +for U in ${USERS}; do + for D in ${DOMAINS}; do + search -s base -b "${D},${SUFFIX}" "fripostOwner=${U},${SUFFIX}" | grep -q '^dn: ' && \ + checkACL "${U}" "${D}" children/write ${ATTRS0} ${ATTRSA} ${ATTRSML} ${ATTRSO} + done +done | isOK 'ALLOWED$' children +[ $? -eq 0 ] || exit $? + + +# 4 +# >=w to "children", =zrscd to "entry", >=rscd to "fripostCanCreateAlias" and +# "fripostCanCreateML", and =wrscd to the rest (other than "Owner" and +# Postmaster") +msg "Have =wrscd to the domain attributes, and >=w to \"children\" (if Postmaster)" +ATTRSP="fripostCanCreateAlias/add fripostCanCreateAlias/delete + fripostCanCreateML/add fripostCanCreateML/delete" +for U in ${USERS}; do + for D in ${DOMAINS}; do + search -s base -b "${D},${SUFFIX}" "fripostPostmaster=${U},${SUFFIX}" | grep -q '^dn: ' && \ + checkACL "${U}" "${D}" children/write ${ATTRS0} ${ATTRSA} ${ATTRSML} ${ATTRSO} ${ATTRSP} + done +done | isOK 'ALLOWED$' children +[ $? -eq 0 ] || exit $? + + +# not (0 or 1 or 2 or 3 or 4) +msg "Have no access to the public attributes of irrelevant domains" +for U in ${USERS}; do + DU="$(echo "${U}" | sed -re 's/.*,(fvd=[^,]+)$/\1/')" + for D in ${DOMAINS}; do + [ "x${DU}" = "x${D}" ] || \ + search -s base -b "${D},${SUFFIX}" "(|(fripostCanCreateAlias=${U},${SUFFIX}) + (fripostCanCreateAlias=${DU},${SUFFIX}) + (fripostCanCreateML=${U},${SUFFIX}) + (fripostCanCreateML=${DU},${SUFFIX}) + (fripostOwner=${U},${SUFFIX}) + (fripostPostmaster=${U},${SUFFIX}))" | grep -q '^dn: ' || \ + checkACL "${U}" "${D}" ${ATTRS0} + done +done | isOK 'DENIED$' entry read +[ $? -eq 0 ] || exit $? + + +# not (1 or 2 or 3 or 4) +msg "Do not have >=rc access to \"canCreate{Alias,ML}\", \"Owner\", \"Postmaster\" (unless member)" +for U in ${USERS}; do + DU="$(echo "${U}" | sed -re 's/.*,(fvd=[^,]+)$/\1/')" + for D in ${DOMAINS}; do + search -s base -b "${D},${SUFFIX}" "(|(fripostCanCreateAlias=${U},${SUFFIX}) + (fripostCanCreateAlias=${DU},${SUFFIX}) + (fripostCanCreateML=${U},${SUFFIX}) + (fripostCanCreateML=${DU},${SUFFIX}) + (fripostOwner=${U},${SUFFIX}) + (fripostPostmaster=${U},${SUFFIX}))" | grep -q '^dn: ' || \ + checkACL "${U}" "${D}" ${ATTRSA} ${ATTRSML} entry/add + done +done | isOK 'DENIED$' entry # "entry" here is useless, but it's just to get the count +[ $? -eq 0 ] || exit $? + + +# not (1 or 3 or 4) +msg "Have =0 access to \"canCreateAlias\" (unless member, Owner, or Postmaster)" +for U in ${USERS}; do + DU="$(echo "${U}" | sed -re 's/.*,(fvd=[^,]+)$/\1/')" + for D in ${DOMAINS}; do + search -s base -b "${D},${SUFFIX}" "(|(fripostCanCreateAlias=${U},${SUFFIX}) + (fripostCanCreateAlias=${DU},${SUFFIX}) + (fripostOwner=${U},${SUFFIX}) + (fripostPostmaster=${U},${SUFFIX}))" | grep -q '^dn: ' || \ + checkACL "${U}" "${D}" fripostCanCreateAlias entry/add + done +done | isOK '\(=0\|DENIED\)$' entry # "entry" here is useless, but it's just to get the count +[ $? -eq 0 ] || exit $? + + +# not (2 or 3 or 4) +msg "Have =0 access to \"canCreateML\" (unless member, Owner, or Postmaster)" +for U in ${USERS}; do + DU="$(echo "${U}" | sed -re 's/.*,(fvd=[^,]+)$/\1/')" + for D in ${DOMAINS}; do + search -s base -b "${D},${SUFFIX}" "(|(fripostCanCreateML=${U},${SUFFIX}) + (fripostCanCreateML=${DU},${SUFFIX}) + (fripostOwner=${U},${SUFFIX}) + (fripostPostmaster=${U},${SUFFIX}))" | grep -q '^dn: ' || \ + checkACL "${U}" "${D}" fripostCanCreateML entry/add + done +done | isOK '\(=0\|DENIED\)$' entry # "entry" here is useless, but it's just to get the count +[ $? -eq 0 ] || exit $? + + +# not (3 or 4) +msg "Have =0 access to \"fripostMaildrop\" (unless Owner or Postmaster)" +for U in ${USERS}; do + for D in ${DOMAINS}; do + search -s base -b "${D},${SUFFIX}" "(|(fripostOwner=${U},${SUFFIX}) + (fripostPostmaster=${U},${SUFFIX}))" | grep -q '^dn: ' || \ + checkACL "${U}" "${D}" ${ATTRSO} + done +done | isOK 'DENIED$' entry +[ $? -eq 0 ] || exit $? + + +# not 4 +msg "Do not have >=w access to \"canCreate{Alias,ML}\" (unless Postmaster)" +for U in ${USERS}; do + for D in ${DOMAINS}; do + search -s base -b "${D},${SUFFIX}" "fripostPostmaster=${U},${SUFFIX}" | grep -q '^dn: ' || \ + checkACL "${U}" "${D}" ${ATTRSP} entry/add + done +done | isOK 'DENIED$' entry # "entry" here is useless, but it's just to get the count +[ $? -eq 0 ] || exit $? + + + +########################################################################### + + +echo +echo "Authenticated users, access to user entries" + +# * entry: +# =rsd if account owner +# +a if domain postmaster +# * children: +# =0 for all +# * fvu: +# =wrscd if account owner or domain postmaster +# * userPassword: +# =w if account owner or domain postmaster +# * fripostIsStatusActive: +# =wrscd if account owner or domain postmaster +# * fripostMailboxQuota: +# =rscd if account owner or domain postmaster +# * fripostMaildrop: +# =wrscd if account owner or domain postmaster +# * cn: +# =wrscd if account owner or domain postmaster +# * description: +# =wrscd if account owner or domain postmaster + +usersU () { + for U in ${USERS}; do + checkACL "${U}" "${U}" "$@" + done +} + +# They would need write access to their fripostMailboxQuota. +# In practice they can't write fvu either, since it's single valued. +msg "Have =rscxd access to their \"fripostMailboxQuota\"" +usersU fripostMailboxQuota | isOK 'read(=rscxd)$' +[ $? -eq 0 ] || exit $? + +msg "Have =wd access to their own \"userPassword\"" +usersU userPassword | isOK '=w$' +[ $? -eq 0 ] || exit $? + +msg "Have =wrscxd access to the other attributes of their own entry" +usersU fvu fripostIsStatusActive fripostMaildrop cn description | isOK 'write(=wrscxd)$' fvu +[ $? -eq 0 ] || exit $? + +msg "Have >=rsd access to the \"entry\" attribute of their own entry" +usersU entry/read entry/search entry/disclose fvu/read \ + | isOK 'ALLOWED$' fvu # fvu is useless here, but it's just to get the count +[ $? -eq 0 ] || exit $? + +msg "Have =0 access to their \"children\" and operational attributes" +usersU children structuralObjectClass entryUUID createTimestamp entryCSN modifiersName modifyTimestamp | isOK '=0$' children +[ $? -eq 0 ] || exit $? + +msg "Have =0 access to other user entries (unless Postmaster)" +for U1 in ${USERS}; do + for U2 in ${USERS}; do + D2="$(echo "${U2}" | sed -re 's/.*,(fvd=[^,]+)$/\1/')" + [ "x${U1}" = "x${U2}" ] || \ + search -s base -b "${D2},${SUFFIX}" "(fripostPostmaster=${U1},${SUFFIX})" | grep -q '^dn: ' || \ + checkACL "${U1}" "${U2}" entry children \ + fvu userPassword \ + fripostIsStatusActive \ + fripostMailboxQuota \ + fripostMaildrop \ + cn description + done +done | isOK '=0$' entry +[ $? -eq 0 ] || exit $? + + +usersP () { + for P in ${USERS}; do + for U in ${USERS}; do + D="$(echo "${U}" | sed -re 's/.*,(fvd=[^,]+)$/\1/')" + [ "x${U}" != "x${P}" ] && \ + search -s base -b "${D},${SUFFIX}" "(fripostPostmaster=${P},${SUFFIX})" | grep -q '^dn: ' && \ + checkACL "${P}" "${U}" "$@" + done + done +} + +msg "Have =rscxd access to their user's \"fripostMailboxQuota\" (if Postmaster)" +usersP fripostMailboxQuota | isOK 'read(=rscxd)$' +[ $? -eq 0 ] || exit $? + +msg "Have =wd access to their user's \"userPassword\" (if Postmaster)" +usersP userPassword | isOK '=w$' +[ $? -eq 0 ] || exit $? + +msg "Have =wrscxd access to the other attributes of their users' entry (if Postmaster)" +usersP fvu fripostIsStatusActive fripostMaildrop cn description | isOK 'write(=wrscxd)$' fvu +[ $? -eq 0 ] || exit $? + +# "+a" is needed to create new accounts. "+z" would be required to +# delete such accounts. +msg "Have =arsd access to the \"entry\" attribute of their users' entry (if Postmaster)" +usersP entry | isOK '=arsd$' entry +[ $? -eq 0 ] || exit $? + +msg "Have =0 access to their users' \"children\" and operational attributes (if Postmaster)" +usersP children structuralObjectClass entryUUID createTimestamp entryCSN modifiersName modifyTimestamp | isOK '=0$' children +[ $? -eq 0 ] || exit $? + + +########################################################################### + + +echo +echo "Authenticated users, access to alias entries" + +# * entry: +# =s for all +# +a if canCreateAlias +# +rd if alias owner, domain owner or domain postmaster +# +z (regular alias) if alias owner +# +w (regular alias) if domain owner or domain postmaster +# * children: +# =0 for all +# * fva: +# =rscd (reserved alias) if domain owner or domain postmaster +# =wrscd (regular alias) if alias owner, domain owner or domain postmaster +# * fripostMaildrop: +# =wrscd if alias owner, domain owner or domain postmaster +# * fripostIsStatusActive: +# =rscd (reserved alias) if domain owner or domain postmaster +# =wrscd (regular alias) if alias owner, domain owner or domain postmaster +# * fripostOwner: +# =d for all +# +rsc (reserved alias) if domain owner or domain postmaster +# +rsc (regular alias) if alias owner, domain owner or domain postmaster +# +w (regular alias) if domain owner or domain postmaster +# * description: +# =wrscd if alias owner, domain owner or domain postmaster + +usersA () { + for U in ${USERS}; do + for A in ${ALIASES}; do + checkACL "${U}" "${A}" "$@" + done + done +} + + +msg "Have >=s access to \"entry\" and \"fripostOwner\"" +usersA fripostOwner/search entry/search | isOK 'ALLOWED$' entry +[ $? -eq 0 ] || exit $? + + +msg "Have =0 access to the \"children\" and operational attributes" +usersA children structuralObjectClass entryUUID createTimestamp entryCSN modifiersName modifyTimestamp | isOK '=0$' children +[ $? -eq 0 ] || exit $? + +RESERVED_ATTRS="entry/delete + fva/write + fripostIsStatusActive/write" +RESERVED_ATTRS2="fripostOwner/add fripostOwner/delete" + +ATTRS="entry/read entry/disclose + fva/read fva/search fva/compare fva/disclose + fripostMaildrop/add fripostMaildrop/delete fripostMaildrop/read fripostMaildrop/search fripostMaildrop/compare fripostMaildrop/disclose + fripostIsStatusActive/read fripostIsStatusActive/search fripostIsStatusActive/compare fripostIsStatusActive/disclose + fripostOwner/read fripostOwner/compare fripostOwner/disclose + description/add description/delete description/read description/search description/compare description/disclose" + +msg "Cannot delete/deactivate/change ownership of reserved aliases" +for U in ${USERS}; do + for A in ${ALIASES}; do + DA="$(echo "${A}" | sed -re 's/.*,(fvd=[^,]+)$/\1/')" + LA="$(echo "${A}" | sed -re 's/^fva=(.*),fvd=[^,]+$/\1/')" + [ "x${LA}" = "xabuse" -o "x${LA}" = "xpostmaster" ] && \ + checkACL "${U}" "${A}" ${RESERVED_ATTRS} + done +done | isOK 'DENIED$' entry +[ $? -eq 0 ] || exit $? + + +msg "Can delete/deactivate/change ownership of regular aliases (if alias Owner)" +for U in ${USERS}; do + for A in ${ALIASES}; do + DA="$(echo "${A}" | sed -re 's/.*,(fvd=[^,]+)$/\1/')" + LA="$(echo "${A}" | sed -re 's/^fva=(.*),fvd=[^,]+$/\1/')" + [ "x${LA}" != "xabuse" -a "x${LA}" != "xpostmaster" ] && \ + search -s base -b "${A},${SUFFIX}" "fripostOwner=${U},${SUFFIX}" | grep -q '^dn: ' && \ + checkACL "${U}" "${A}" ${RESERVED_ATTRS} + done +done | isOK 'ALLOWED$' entry +[ $? -eq 0 ] || exit $? + + +msg "Can delete/deactivate/change ownership of regular aliases (if domain Owner)" +for U in ${USERS}; do + for A in ${ALIASES}; do + DA="$(echo "${A}" | sed -re 's/.*,(fvd=[^,]+)$/\1/')" + LA="$(echo "${A}" | sed -re 's/^fva=(.*),fvd=[^,]+$/\1/')" + [ "x${LA}" != "xabuse" -a "x${LA}" != "xpostmaster" ] && \ + search -s base -b "${DA},${SUFFIX}" "fripostOwner=${U},${SUFFIX}" | grep -q '^dn: ' && \ + checkACL "${U}" "${A}" ${RESERVED_ATTRS} ${RESERVED_ATTRS2} + done +done | isOK 'ALLOWED$' entry +[ $? -eq 0 ] || exit $? + + +msg "Can delete/deactivate/change ownership of regular aliases (if domain Postmaster)" +for U in ${USERS}; do + for A in ${ALIASES}; do + DA="$(echo "${A}" | sed -re 's/.*,(fvd=[^,]+)$/\1/')" + LA="$(echo "${A}" | sed -re 's/^fva=(.*),fvd=[^,]+$/\1/')" + [ "x${LA}" != "xabuse" -a "x${LA}" != "xpostmaster" ] && \ + search -s base -b "${DA},${SUFFIX}" "fripostPostmaster=${U},${SUFFIX}" | grep -q '^dn: ' && \ + checkACL "${U}" "${A}" ${RESERVED_ATTRS} ${RESERVED_ATTRS2} + done +done | isOK 'ALLOWED$' entry +[ $? -eq 0 ] || exit $? + + +msg "Can change destination (if alias Owner)" +for U in ${USERS}; do + for A in ${ALIASES}; do + search -s base -b "${A},${SUFFIX}" "fripostOwner=${U},${SUFFIX}" | grep -q '^dn: ' && \ + checkACL "${U}" "${A}" ${ATTRS} + done +done | isOK 'ALLOWED$' entry read +[ $? -eq 0 ] || exit $? + + +msg "Can change destination and create new aliases (if domain Owner)" +for U in ${USERS}; do + for A in ${ALIASES}; do + DA="$(echo "${A}" | sed -re 's/.*,(fvd=[^,]+)$/\1/')" + search -s base -b "${DA},${SUFFIX}" "fripostOwner=${U},${SUFFIX}" | grep -q '^dn: ' && \ + checkACL "${U}" "${A}" entry/add ${ATTRS} + done +done | isOK 'ALLOWED$' entry add +[ $? -eq 0 ] || exit $? + + +msg "Can change destination and create new aliases (if domain Postmaster)" +for U in ${USERS}; do + for A in ${ALIASES}; do + DA="$(echo "${A}" | sed -re 's/.*,(fvd=[^,]+)$/\1/')" + search -s base -b "${DA},${SUFFIX}" "fripostPostmaster=${U},${SUFFIX}" | grep -q '^dn: ' && \ + checkACL "${U}" "${A}" entry/add ${ATTRS} + done +done | isOK 'ALLOWED$' entry add +[ $? -eq 0 ] || exit $? + + +# Needed to create new entries. ("+z" is required to delete, btw.) +msg "Have >=a access to \"entry\" (if CanCreateAlias, exact)" +for U in ${USERS}; do + for A in ${ALIASES}; do + DA="$(echo "${A}" | sed -re 's/.*,(fvd=[^,]+)$/\1/')" + search -s base -b "${DA},${SUFFIX}" "fripostCanCreateAlias=${U},${SUFFIX}" | grep -q '^dn: ' && \ + checkACL "${U}" "${A}" entry/add + done +done | isOK 'ALLOWED$' entry add +[ $? -eq 0 ] || exit $? + + +# Needed to create new entries. ("+z" is required to delete, btw.) +msg "Have >=a access to \"entry\" (if CanCreateAlias, wildcard)" +for U in ${USERS}; do + DU="$(echo "${U}" | sed -re 's/.*,(fvd=[^,]+)$/\1/')" + for A in ${ALIASES}; do + DA="$(echo "${A}" | sed -re 's/.*,(fvd=[^,]+)$/\1/')" + search -s base -b "${DA},${SUFFIX}" "fripostCanCreateAlias=${DU},${SUFFIX}" | grep -q '^dn: ' && \ + checkACL "${U}" "${A}" entry/add + done +done | isOK 'ALLOWED$' entry add +[ $? -eq 0 ] || exit $? + + +msg "Do not have >=a access to \"entry\" (unless canCreateAlias)" +for U in ${USERS}; do + DU="$(echo "${U}" | sed -re 's/.*,(fvd=[^,]+)$/\1/')" + for A in ${ALIASES}; do + DA="$(echo "${A}" | sed -re 's/.*,(fvd=[^,]+)$/\1/')" + search -s base -b "${DA},${SUFFIX}" "(|(fripostCanCreateAlias=${U},${SUFFIX}) + (fripostCanCreateAlias=${DU},${SUFFIX}) + (fripostOwner=${U},${SUFFIX}) + (fripostPostmaster=${U},${SUFFIX}))" | grep -q '^dn: ' || \ + checkACL "${U}" "${A}" entry/add + done +done | isOK 'DENIED$' entry add +[ $? -eq 0 ] || exit $? + + +msg "Cannot manage ownership (unless domain owner/domain postmaster)" +for U in ${USERS}; do + for A in ${ALIASES}; do + DA="$(echo "${A}" | sed -re 's/.*,(fvd=[^,]+)$/\1/')" + search -s base -b "${DA},${SUFFIX}" "(|(fripostOwner=${U},${SUFFIX}) + (fripostPostmaster=${U},${SUFFIX}))" | grep -q '^dn: ' || \ + checkACL "${U}" "${A}" ${RESERVED_ATTRS2} + done +done | isOK 'DENIED$' fripostOwner add +[ $? -eq 0 ] || exit $? + + +msg "Have no access to aliases entries (unless alias owner/domain owner/domain postmaster)" +for U in ${USERS}; do + for A in ${ALIASES}; do + DA="$(echo "${A}" | sed -re 's/.*,(fvd=[^,]+)$/\1/')" + search -s base -b "${A},${SUFFIX}" "fripostOwner=${U},${SUFFIX}" | grep -q '^dn: ' || \ + search -s base -b "${DA},${SUFFIX}" "(|(fripostOwner=${U},${SUFFIX}) + (fripostPostmaster=${U},${SUFFIX}))" | grep -q '^dn: ' || \ + checkACL "${U}" "${A}" ${RESERVED_ATTRS} ${ATTRS} + done +done | isOK 'DENIED$' entry delete +[ $? -eq 0 ] || exit $? + + + +########################################################################### + + +echo +echo "Authenticated users, access to mailing list entries" + +# * entry: +# =s for all +# +a if canCreateML, domain owner or domain postmaster +# +zrd if mailing list owner, domain owner or domain postmaster +# * children: +# =0 for all +# * fvml: +# =wrscd if mailing list owner, domain owner or domain postmaster +# * fripostMLManager: +# =rscd if mailing list owner, domain owner or domain postmaster +# * fripostIsStatusActive: +# =wrscd if mailing list owner, domain owner or domain postmaster +# * fripostMLCommand: +# =rscd if mailing list owner, domain owner or domain postmaster +# * fripostOwner: +# =d for all +# +rsc if mailing list owner, domain owner or domain postmaster +# +w if domain owner or domain postmaster +# * description: +# =wrscd if mailing list owner, domain owner or domain postmaster + +usersML () { + for U in ${USERS}; do + for ML in ${MLS}; do + checkACL "${U}" "${ML}" "$@" + done + done +} + + +msg "Have >=s access on \"entry\" and \"fripostOwner\"" +usersML fripostOwner/search entry/search | isOK 'ALLOWED$' entry +[ $? -eq 0 ] || exit $? + + +msg "Have =0 access the \"children\" and operational attributes" +usersML children structuralObjectClass entryUUID createTimestamp entryCSN modifiersName modifyTimestamp | isOK '=0$' children +[ $? -eq 0 ] || exit $? + + +msg "Cannot change transport-related attributes" +for U in ${USERS}; do + for ML in ${MLS}; do + checkACL "${U}" "${ML}" fripostMLCommand/add fripostMLCommand/delete \ + fripostMLManager/write + done +done | isOK 'DENIED$' fripostMLManager +[ $? -eq 0 ] || exit $? + + +ATTRS="entry/read entry/disclose entry/delete + fvml/write fvml/read fvml/search fvml/compare fvml/disclose + fripostMLManager/read fripostMLManager/search fripostMLManager/compare fripostMLManager/disclose + fripostIsStatusActive/write fripostIsStatusActive/read fripostIsStatusActive/search fripostIsStatusActive/compare fripostIsStatusActive/disclose + fripostMLCommand/read fripostMLCommand/search fripostMLCommand/compare fripostMLCommand/disclose + fripostOwner/read fripostOwner/compare fripostOwner/disclose + description/add description/delete description/read description/compare description/disclose" +ATTRS2="fripostOwner/add fripostOwner/delete" + +msg "Can edit/delete mailing list (if mailing list Owner)" +for U in ${USERS}; do + for ML in ${MLS}; do + search -s base -b "${ML},${SUFFIX}" "fripostOwner=${U},${SUFFIX}" | grep -q '^dn: ' && \ + checkACL "${U}" "${ML}" ${ATTRS} + done +done | isOK 'ALLOWED$' entry delete +[ $? -eq 0 ] || exit $? + + +msg "Can edit/create/delete mailing list (if domain Owner)" +[ $? -eq 0 ] || exit $? +for U in ${USERS}; do + for ML in ${MLS}; do + DML="$(echo "${ML}" | sed -re 's/.*,(fvd=[^,]+)$/\1/')" + search -s base -b "${DML},${SUFFIX}" "fripostOwner=${U},${SUFFIX}" | grep -q '^dn: ' && \ + checkACL "${U}" "${ML}" ${ATTRS} ${ATTRS2} entry/add + done +done | isOK 'ALLOWED$' entry add +[ $? -eq 0 ] || exit $? + + +msg "Can edit/create/delete mailing list (if domain Postmaster)" +[ $? -eq 0 ] || exit $? +for U in ${USERS}; do + for ML in ${MLS}; do + DML="$(echo "${ML}" | sed -re 's/.*,(fvd=[^,]+)$/\1/')" + search -s base -b "${DML},${SUFFIX}" "fripostPostmaster=${U},${SUFFIX}" | grep -q '^dn: ' && \ + checkACL "${U}" "${ML}" ${ATTRS} ${ATTRS2} entry/add + done +done | isOK 'ALLOWED$' entry add +[ $? -eq 0 ] || exit $? + + +# Needed to create new entries. ("+z" is required to delete, btw.) +msg "Have >=a access to \"entry\" (if CanCreateML, exact)" +for U in ${USERS}; do + for ML in ${MLS}; do + DML="$(echo "${ML}" | sed -re 's/.*,(fvd=[^,]+)$/\1/')" + search -s base -b "${DML},${SUFFIX}" "fripostCanCreateML=${U},${SUFFIX}" | grep -q '^dn: ' && \ + checkACL "${U}" "${ML}" entry/add + done +done | isOK 'ALLOWED$' entry +[ $? -eq 0 ] || exit $? + + +# Needed to create new entries. ("+z" is required to delete, btw.) +msg "Have >=a access to \"entry\" (if CanCreateML, wildcard)" +for U in ${USERS}; do + DU="$(echo "${U}" | sed -re 's/.*,(fvd=[^,]+)$/\1/')" + for ML in ${MLS}; do + DML="$(echo "${ML}" | sed -re 's/.*,(fvd=[^,]+)$/\1/')" + search -s base -b "${DML},${SUFFIX}" "fripostCanCreateML=${DU},${SUFFIX}" | grep -q '^dn: ' && \ + checkACL "${U}" "${ML}" entry/add + done +done | isOK 'ALLOWED$' entry +[ $? -eq 0 ] || exit $? + + +msg "Do not have >=a access to \"entry\" (unless canCreateML)" +for U in ${USERS}; do + DU="$(echo "${U}" | sed -re 's/.*,(fvd=[^,]+)$/\1/')" + for ML in ${MLS}; do + DML="$(echo "${ML}" | sed -re 's/.*,(fvd=[^,]+)$/\1/')" + search -s base -b "${DML},${SUFFIX}" "(|(fripostCanCreateML=${U},${SUFFIX}) + (fripostCanCreateML=${DU},${SUFFIX}) + (fripostOwner=${U},${SUFFIX}) + (fripostPostmaster=${U},${SUFFIX}))" | grep -q '^dn: ' || \ + checkACL "${U}" "${ML}" entry/add + done +done | isOK 'DENIED$' entry +[ $? -eq 0 ] || exit $? + + +msg "Cannot manage ownership (unless domain owner/domain postmaster)" +for U in ${USERS}; do + for A in ${ALIASES}; do + DA="$(echo "${A}" | sed -re 's/.*,(fvd=[^,]+)$/\1/')" + search -s base -b "${DA},${SUFFIX}" "(|(fripostOwner=${U},${SUFFIX}) + (fripostPostmaster=${U},${SUFFIX}))" | grep -q '^dn: ' || \ + checkACL "${U}" "${A}" ${ATTRS2} + done +done | isOK 'DENIED$' fripostOwner add +[ $? -eq 0 ] || exit $? + + +msg "Have no access to mailing list entries (unless mailing list owner/domain owner/domain postmaster)" +for U in ${USERS}; do + for ML in ${MLS}; do + DML="$(echo "${ML}" | sed -re 's/.*,(fvd=[^,]+)$/\1/')" + search -s base -b "${ML},${SUFFIX}" "fripostOwner=${U},${SUFFIX}" | grep -q '^dn: ' || \ + search -s base -b "${DML},${SUFFIX}" "(|(fripostOwner=${U},${SUFFIX}) + (fripostPostmaster=${U},${SUFFIX}))" | grep -q '^dn: ' || \ + checkACL "${U}" "${ML}" ${ATTRS} entry/delete + done +done | isOK 'DENIED$' entry delete +[ $? -eq 0 ] || exit $? + + + +########################################################################### + + +rm "${RES}" -- cgit v1.2.3