summaryrefslogtreecommitdiffstats
path: root/ipalib
diff options
context:
space:
mode:
authorFraser Tweedale <ftweedal@redhat.com>2015-05-26 04:44:20 -0400
committerJan Cholasta <jcholast@redhat.com>2015-06-11 10:50:31 +0000
commit947af1a037609fa42cbfd794301d5a5c4061c81b (patch)
tree756b550ca4456c3b2090dabcc1f0e56f63a01cdf /ipalib
parentbc0c60688505968daf6851e3e179aab20e23af7d (diff)
downloadfreeipa-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>
Diffstat (limited to 'ipalib')
-rw-r--r--ipalib/plugins/caacl.py76
-rw-r--r--ipalib/plugins/cert.py17
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