From 89958abf4bc85a4e376cc68d98a721604af1ea77 Mon Sep 17 00:00:00 2001 From: Guilhem Moulin Date: Fri, 29 Nov 2013 22:41:56 +0100 Subject: Allow flexible ACLs for SASL's EXTERNAL mechanism. "username=postfix,cn=peercred,cn=external,cn=auth" is replaced by "gidNumber=106+uidNumber=102,cn=peercred,cn=external,cn=auth" where 102 is postfix's UID and 106 its primary GID (looked up from /etc/passwd). --- lib/openldap | 31 +++++++++++++++++++++- .../templates/etc/ldap/database.ldif.j2 | 4 +-- 2 files changed, 32 insertions(+), 3 deletions(-) diff --git a/lib/openldap b/lib/openldap index 5a4cef4..983299a 100644 --- a/lib/openldap +++ b/lib/openldap @@ -22,7 +22,7 @@ from ldap.dn import dn2str,explode_dn,str2dn from ldap.modlist import addModlist from ldif import LDIFParser from functools import partial -import re +import re, pwd # Dirty hack to check equality between the targetted LDIF and that @@ -47,6 +47,28 @@ indexedDN = { 'olcHdbConfig': [('olcDbDirectory', '%s' )], } +# Allow for flexible ACLs for user using SASL's EXTERNAL mechanism. +# "username=postfix,cn=peercred,cn=external,cn=auth" is replaced by +# "gidNumber=106+uidNumber=102,cn=peercred,cn=external,cn=auth" where +# 102 is postfix's UID and 106 its primary GID. +# (Regular expressions are not allowed.) +sasl_ext_re = re.compile( r"""(?P\sby\s+dn(?:\.exact)?)= + (?P['\"]?)username=(?P[a-z][-a-z0-9_]*), + (?Pcn=peercred,cn=external,cn=auth) + (?P=quote)\s""" + , re.VERBOSE ) +pwd_dict = {} + +def acl_sasl_ext(m): + u = m.group('user') + if u not in pwd_dict.keys(): + pwd_dict[u] = pwd.getpwnam(u) + return '%s="gidNumber=%d+uidNumber=%d,%s" ' % ( m.group('start') + , pwd_dict[u].pw_gid + , pwd_dict[u].pw_uid + , m.group('end') + ) + # Run the given callback on each DN seen. If its return value is not # None, update the changed variable. @@ -149,6 +171,11 @@ def processEntry(module, l, dn, entry): # which is implicit diff.append(( ldap.MOD_DELETE, a, None )) elif a in indexedAttributes: + if a == 'olcAccess': + # replace "username=...,cn=peercred,cn=external,cn=auth" + # by a DN with proper gidNumber and uidNumber + entry[a] = map ( partial(re.sub, sasl_ext_re, acl_sasl_ext) + , entry[a] ) # add explicit indices in the entry from the LDIF entry[a] = map( (lambda x: '{%d}%s' % x) , zip(range(len(entry[a])),entry[a]) ) @@ -310,6 +337,8 @@ def main(): module.fail_json(rv=e.returncode, msg=e.output.rstrip()) except ldap.LDAPError, e: module.fail_json(msg=e.args[0]['info']) + except KeyError, e: + module.fail_json(msg=str(e)) module.exit_json(changed=changed) diff --git a/roles/common-LDAP/templates/etc/ldap/database.ldif.j2 b/roles/common-LDAP/templates/etc/ldap/database.ldif.j2 index 19fcdd0..1970a99 100644 --- a/roles/common-LDAP/templates/etc/ldap/database.ldif.j2 +++ b/roles/common-LDAP/templates/etc/ldap/database.ldif.j2 @@ -111,14 +111,14 @@ olcAccess: to dn.children="ou=virtual,o=mailHosting,dc=fripost,dc=org" olcAccess: to dn.exact="ou=virtual,o=mailHosting,dc=fripost,dc=org" attrs=entry by dn.exact="cn=Postfix,ou=services,o=mailHosting,dc=fripost,dc=org" =s - by dn.exact="gidNumber=8+uidNumber=8,cn=peercred,cn=external,cn=auth" =s + by dn.exact="username=postfix,cn=peercred,cn=external,cn=auth" =s by users =0 break # # Search domain owners / postmasters olcAccess: to dn.children="ou=virtual,o=mailHosting,dc=fripost,dc=org" attrs=entry,objectClass,fvd,fvl,fripostPostmaster,fripostOwner filter=(&(objectClass=FripostVirtualDomain)(!(objectClass=FripostPendingEntry))(!(fripostIsStatusActive=FALSE))) - by dn.exact="gidNumber=8+uidNumber=8,cn=peercred,cn=external,cn=auth" =rsd + by dn.exact="username=postfix,cn=peercred,cn=external,cn=auth" =rsd by users =0 break # # Anonymous can authenticate into the services. (But not read or write the password.) -- cgit v1.2.3