certmap rules: altSecurityIdentities should only be used for trusted domains

IPA LDAP has no altSecurityIdentities in use, it only should apply to
identities in trusted Active Directory domains.

Add checks to enforce proper certmap rule attribution for specific
Active Directory domains.

Related: https://pagure.io/freeipa/issue/7932
Reviewed-By: Florence Blanc-Renaud <flo@redhat.com>
This commit is contained in:
Alexander Bokovoy 2019-05-03 13:00:18 +03:00
parent 725899595f
commit 41ca4d484e

View File

@ -91,6 +91,67 @@ EXAMPLES:
register = Registry()
def check_maprule_is_for_trusted_domain(api_inst, options, entry_attrs):
"""
Check that the certmap rule references altSecurityIdentities and
associateddomain in options is a trusted domain.
:param api_inst: API instance
:param options: options passed to the command
at least ipacertmapmaprule and
associateddomain options are expected for the check
:raises: ValidationError if altSecurityIdentities is present in the
rule but no Active Directory trusted domain listed in
associated domains
"""
is_trusted_domain_required = False
# If no explicit option passed, fallback to the content of the entry.
# This helps to catch cases when an associated domain value is removed
# while the rule requires its presence.
#
# In certmap-mod pre-callback we pass LDAPEntry instance instead of a dict
# LDAEntry.get() returns lists, even for single valued attrs. Using
# LDAPEntry.single_value would give us a single-valued result instead.
maprule_entry = entry_attrs
if not isinstance(maprule_entry, dict):
maprule_entry = entry_attrs.single_value
maprule = options.get('ipacertmapmaprule',
maprule_entry.get('ipacertmapmaprule'))
if maprule:
if 'altsecurityidentities' in maprule.lower():
is_trusted_domain_required = True
if is_trusted_domain_required:
domains = options.get('associateddomain',
entry_attrs.get('associateddomain'))
if domains:
trusted_domains = api_inst.Object.config.gather_trusted_domains()
trust_suffix_namespace = {dom_name.lower() for dom_name in
trusted_domains}
candidates = {str(dom).lower() for dom in domains}
invalid = candidates - trust_suffix_namespace
if invalid == candidates or len(trust_suffix_namespace) == 0:
raise errors.ValidationError(
name=_('domain'),
error=_('The domain(s) "%s" cannot be used to apply '
'altSecurityIdentities check.') %
", ".join(list(invalid))
)
else:
raise errors.ValidationError(
name=_('domain'),
error=_('The mapping rule with altSecurityIdentities '
'should be applied to a trusted Active Directory '
'domain but no domain was associated with the rule.')
)
def check_associateddomain_is_trusted(api_inst, options):
"""
Check that the associateddomain in options are either IPA domain or
@ -299,6 +360,7 @@ class certmaprule_add(LDAPCreate):
def pre_callback(self, ldap, dn, entry_attrs, attrs_list, *keys,
**options):
check_associateddomain_is_trusted(self.api, options)
check_maprule_is_for_trusted_domain(self.api, options, entry_attrs)
return dn
@ -311,6 +373,17 @@ class certmaprule_mod(LDAPUpdate):
def pre_callback(self, ldap, dn, entry_attrs, attrs_list, *keys,
**options):
check_associateddomain_is_trusted(self.api, options)
# For update of the existing certmaprule we need to retrieve
# content of the LDAP entry because modification may affect two cases:
# - altSecurityIdentities might be removed by the modification
# - trusted domains may be removed by the modification while they
# should be in place
#
# For both these cases we need to know actual content of the entry but
# LDAPUpdate.execute() provides us with entry_attrs built from the
# options, not the original content.
entry = ldap.get_entry(dn)
check_maprule_is_for_trusted_domain(self.api, options, entry)
return dn