diff options
author | Fraser Tweedale <ftweedal@redhat.com> | 2015-05-26 04:44:20 -0400 |
---|---|---|
committer | Jan Cholasta <jcholast@redhat.com> | 2015-06-11 10:50:31 +0000 |
commit | 947af1a037609fa42cbfd794301d5a5c4061c81b (patch) | |
tree | 756b550ca4456c3b2090dabcc1f0e56f63a01cdf | |
parent | bc0c60688505968daf6851e3e179aab20e23af7d (diff) | |
download | freeipa-947af1a037609fa42cbfd794301d5a5c4061c81b.tar.gz freeipa-947af1a037609fa42cbfd794301d5a5c4061c81b.tar.xz freeipa-947af1a037609fa42cbfd794301d5a5c4061c81b.zip |
Enforce CA ACLs in cert-request command
This commit adds CA ACL enforcement to the cert-request command and
uses the pyhbac machinery.
It is planned to implement ACL enforcement in Dogtag in a future
release, and remove certificate issuance privileges and CA ACL
enforcement responsibility from the framework. See
https://fedorahosted.org/freeipa/ticket/5011 for more information.
Part of: https://fedorahosted.org/freeipa/ticket/57
Part of: https://fedorahosted.org/freeipa/ticket/4559
Reviewed-By: Martin Basti <mbasti@redhat.com>
-rw-r--r-- | ipalib/plugins/caacl.py | 76 | ||||
-rw-r--r-- | ipalib/plugins/cert.py | 17 |
2 files changed, 93 insertions, 0 deletions
diff --git a/ipalib/plugins/caacl.py b/ipalib/plugins/caacl.py index f0dc9ae35..6bf39d233 100644 --- a/ipalib/plugins/caacl.py +++ b/ipalib/plugins/caacl.py @@ -2,6 +2,8 @@ # Copyright (C) 2015 FreeIPA Contributors see COPYING for license # +import pyhbac + from ipalib import api, errors, output from ipalib import Bool, Str, StrEnum from ipalib.plugable import Registry @@ -10,6 +12,7 @@ from ipalib.plugins.baseldap import ( LDAPUpdate, LDAPRetrieve, LDAPAddMember, LDAPRemoveMember, global_output_params, pkey_to_value) from ipalib.plugins.hbacrule import is_all +from ipalib.plugins.service import normalize_principal, split_any_principal from ipalib import _, ngettext from ipapython.dn import DN @@ -50,6 +53,79 @@ EXAMPLES: register = Registry() +def _acl_make_request(principal_type, principal, ca_ref, profile_id): + """Construct HBAC request for the given principal, CA and profile""" + req = pyhbac.HbacRequest() + req.targethost.name = ca_ref + req.service.name = profile_id + if principal_type == 'user': + req.user.name = principal + elif principal_type == 'host': + req.user.name = principal[:5] # strip 'host/' + elif principal_type == 'service': + req.user.name = normalize_principal(principal) + groups = [] + if principal_type == 'user': + user_obj = api.Command.user_show(principal)['result'] + groups = user_obj.get('memberof_group', []) + groups += user_obj.get('memberofindirect_group', []) + elif principal_type == 'host': + service, hostname, realm = split_any_principal(principal) + host_obj = api.Command.host_show(hostname)['result'] + groups = host_obj.get('memberof_hostgroup', []) + groups += host_obj.get('memberofindirect_hostgroup', []) + req.user.groups = sorted(set(groups)) + return req + + +def _acl_make_rule(principal_type, obj): + """Turn CA ACL object into HBAC rule. + + ``principal_type`` + String in {'user', 'host', 'service'} + """ + rule = pyhbac.HbacRule(obj['cn'][0]) + rule.enabled = obj['ipaenabledflag'][0] + rule.srchosts.category = {pyhbac.HBAC_CATEGORY_ALL} + + # add CA(s) + # Hardcoded until caacl plugin arrives + rule.targethosts.category = {pyhbac.HBAC_CATEGORY_ALL} + #if 'ipacacategory' in obj and obj['ipacacategory'][0].lower() == 'all': + # rule.targethosts.category = {pyhbac.HBAC_CATEGORY_ALL} + #else: + # rule.targethosts.names = obj.get('ipacaaclcaref', []) + + # add profiles + if ('ipacertprofilecategory' in obj + and obj['ipacertprofilecategory'][0].lower() == 'all'): + rule.services.category = {pyhbac.HBAC_CATEGORY_ALL} + else: + attr = 'ipamembercertprofile_certprofile' + rule.services.names = obj.get(attr, []) + + # add principals and principal's groups + m = {'user': 'group', 'host': 'hostgroup', 'service': None} + category_attr = '{}category'.format(principal_type) + if category_attr in obj and obj[category_attr][0].lower() == 'all': + rule.users.category = {pyhbac.HBAC_CATEGORY_ALL} + else: + principal_attr = 'member{}_{}'.format(principal_type, principal_type) + rule.users.names = obj.get(principal_attr, []) + if m[principal_type] is not None: + group_attr = 'member{}_{}'.format(principal_type, m[principal_type]) + rule.users.groups = obj.get(group_attr, []) + + return rule + + +def acl_evaluate(principal_type, principal, ca_ref, profile_id): + req = _acl_make_request(principal_type, principal, ca_ref, profile_id) + acls = api.Command.caacl_find()['result'] + rules = [_acl_make_rule(principal_type, obj) for obj in acls] + return req.evaluate(rules) == pyhbac.HBAC_EVAL_ALLOW + + @register() class caacl(LDAPObject): """ diff --git a/ipalib/plugins/cert.py b/ipalib/plugins/cert.py index d12290017..1878e5ad5 100644 --- a/ipalib/plugins/cert.py +++ b/ipalib/plugins/cert.py @@ -33,6 +33,7 @@ from ipalib.plugins.virtual import * from ipalib.plugins.baseldap import pkey_to_value from ipalib.plugins.service import split_any_principal from ipalib.plugins.certprofile import validate_profile_id +import ipalib.plugins.caacl import base64 import traceback from ipalib.text import _ @@ -326,6 +327,22 @@ class cert_request(VirtualCommand): else: principal_type = SERVICE + principal_type_map = {USER: 'user', HOST: 'host', SERVICE: 'service'} + ca = '.' # top-level CA hardcoded until subca plugin implemented + if not ipalib.plugins.caacl.acl_evaluate( + principal_type_map[principal_type], + principal_string, ca, profile_id): + raise errors.ACIError(info=_( + "Principal '%(principal)s' " + "is not permitted to use CA '%(ca)s' " + "with profile '%(profile_id)s' for certificate issuance." + ) % dict( + principal=principal_string, + ca=ca or '.', + profile_id=profile_id + ) + ) + bind_principal = split_any_principal(getattr(context, 'principal')) bind_service, bind_name, bind_realm = bind_principal |