diff options
| -rw-r--r-- | ldap/Makefile | 88 | ||||
| -rw-r--r-- | ldap/README | 30 | ||||
| -rw-r--r-- | ldap/acl.ldif | 293 | ||||
| -rw-r--r-- | ldap/database.ldif | 60 | ||||
| -rw-r--r-- | ldap/fripost.ldif | 140 | ||||
| -rw-r--r-- | ldap/index.ldif | 44 | ||||
| -rw-r--r-- | ldap/modules.ldif | 16 | ||||
| -rw-r--r-- | ldap/populate.ldif | 199 | ||||
| -rw-r--r-- | ldap/syncprov.ldif | 25 | ||||
| -rw-r--r-- | ldap/syncrepl.ldif | 33 | ||||
| -rwxr-xr-x | ldap/test-user-acl.sh | 906 | 
11 files changed, 1834 insertions, 0 deletions
| 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}" | 
