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>
This commit is contained in:
Fraser Tweedale
2015-05-26 04:44:20 -04:00
committed by Jan Cholasta
parent bc0c606885
commit 947af1a037
2 changed files with 93 additions and 0 deletions

View File

@@ -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):
"""

View File

@@ -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