diff options
author | Fraser Tweedale <ftweedal@redhat.com> | 2015-05-25 08:39:07 -0400 |
---|---|---|
committer | Jan Cholasta <jcholast@redhat.com> | 2015-06-11 10:50:31 +0000 |
commit | bc0c60688505968daf6851e3e179aab20e23af7d (patch) | |
tree | ea8cb740dfcd50ab46d73a350686502d80a902ec /ipalib | |
parent | ae56ca422d1897569717fa44a5d483b10e490f6a (diff) | |
download | freeipa-bc0c60688505968daf6851e3e179aab20e23af7d.tar.gz freeipa-bc0c60688505968daf6851e3e179aab20e23af7d.tar.xz freeipa-bc0c60688505968daf6851e3e179aab20e23af7d.zip |
Add CA ACL plugin
Implement the caacl commands, which are used to indicate which
principals may be issued certificates from which (sub-)CAs, using
which profiles.
At this commit, and until sub-CAs are implemented, all rules refer
to the top-level CA (represented as ".") and no ca-ref argument is
exposed.
Also, during install and upgrade add a default CA ACL that permits
certificate issuance for all hosts and services using the profile
'caIPAserviceCert' on the top-level CA.
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/constants.py | 1 | ||||
-rw-r--r-- | ipalib/plugins/caacl.py | 477 |
2 files changed, 478 insertions, 0 deletions
diff --git a/ipalib/constants.py b/ipalib/constants.py index 0ffdcbfc7..330f9df74 100644 --- a/ipalib/constants.py +++ b/ipalib/constants.py @@ -120,6 +120,7 @@ DEFAULT_CONFIG = ( ('container_masters', DN(('cn', 'masters'), ('cn', 'ipa'), ('cn', 'etc'))), ('container_certprofile', DN(('cn', 'certprofiles'), ('cn', 'ca'))), ('container_topology', DN(('cn', 'topology'), ('cn', 'ipa'), ('cn', 'etc'))), + ('container_caacl', DN(('cn', 'caacls'), ('cn', 'ca'))), # Ports, hosts, and URIs: ('xmlrpc_uri', 'http://localhost:8888/ipa/xml'), diff --git a/ipalib/plugins/caacl.py b/ipalib/plugins/caacl.py new file mode 100644 index 000000000..f0dc9ae35 --- /dev/null +++ b/ipalib/plugins/caacl.py @@ -0,0 +1,477 @@ +# +# Copyright (C) 2015 FreeIPA Contributors see COPYING for license +# + +from ipalib import api, errors, output +from ipalib import Bool, Str, StrEnum +from ipalib.plugable import Registry +from ipalib.plugins.baseldap import ( + LDAPObject, LDAPSearch, LDAPCreate, LDAPDelete, LDAPQuery, + LDAPUpdate, LDAPRetrieve, LDAPAddMember, LDAPRemoveMember, + global_output_params, pkey_to_value) +from ipalib.plugins.hbacrule import is_all +from ipalib import _, ngettext +from ipapython.dn import DN + + +__doc__ = _(""" +Manage CA ACL rules. + +This plugin is used to define rules governing which principals are +permitted to have certificates issued using a given certificate +profile. + +PROFILE ID SYNTAX: + +A Profile ID is a string without spaces or punctuation starting with a letter +and followed by a sequence of letters, digits or underscore ("_"). + +EXAMPLES: + + Create a CA ACL "test" that grants all users access to the + "UserCert" profile: + ipa caacl-add test --usercat=all + ipa caacl-add-profile test --certprofiles UserCert + + Display the properties of a named CA ACL: + ipa caacl-show test + + Create a CA ACL to let user "alice" use the "DNP3" profile: + ipa caacl-add-profile alice_dnp3 --certprofiles DNP3 + ipa caacl-add-user alice_dnp3 --user=alice + + Disable a CA ACL: + ipa caacl-disable test + + Remove a CA ACL: + ipa caacl-del test +""") + +register = Registry() + + +@register() +class caacl(LDAPObject): + """ + CA ACL object. + """ + container_dn = api.env.container_caacl + object_name = _('CA ACL') + object_name_plural = _('CA ACLs') + object_class = ['ipaassociation', 'ipacaacl'] + permission_filter_objectclasses = ['ipacaacl'] + default_attributes = [ + 'cn', 'description', 'ipaenabledflag', + 'ipacacategory', 'ipamemberca', + 'ipacertprofilecategory', 'ipamembercertprofile', + 'usercategory', 'memberuser', + 'hostcategory', 'memberhost', + 'servicecategory', 'memberservice', + ] + uuid_attribute = 'ipauniqueid' + rdn_attribute = 'ipauniqueid' + attribute_members = { + 'memberuser': ['user', 'group'], + 'memberhost': ['host', 'hostgroup'], + 'memberservice': ['service'], + 'ipamembercertprofile': ['certprofile'], + } + managed_permissions = { + 'System: Read CA ACLs': { + 'replaces_global_anonymous_aci': True, + 'ipapermbindruletype': 'all', + 'ipapermright': {'read', 'search', 'compare'}, + 'ipapermdefaultattr': { + 'cn', 'description', 'ipaenabledflag', + 'ipacacategory', 'ipamemberca', + 'ipacertprofilecategory', 'ipamembercertprofile', + 'usercategory', 'memberuser', + 'hostcategory', 'memberhost', + 'servicecategory', 'memberservice', + 'ipauniqueid', + 'objectclass', 'member', + }, + }, + 'System: Add CA ACL': { + 'ipapermright': {'add'}, + 'replaces': [ + '(target = "ldap:///ipauniqueid=*,cn=caacls,cn=ca,$SUFFIX")(version 3.0;acl "permission:Add CA ACL";allow (add) groupdn = "ldap:///cn=Add CA ACL,cn=permissions,cn=pbac,$SUFFIX";)', + ], + 'default_privileges': {'CA Administrator'}, + }, + 'System: Delete CA ACL': { + 'ipapermright': {'delete'}, + 'replaces': [ + '(target = "ldap:///ipauniqueid=*,cn=caacls,cn=ca,$SUFFIX")(version 3.0;acl "permission:Delete CA ACL";allow (delete) groupdn = "ldap:///cn=Delete CA ACL,cn=permissions,cn=pbac,$SUFFIX";)', + ], + 'default_privileges': {'CA Administrator'}, + }, + 'System: Manage CA ACL Membership': { + 'ipapermright': {'write'}, + 'ipapermdefaultattr': { + 'ipacacategory', 'ipamemberca', + 'ipacertprofilecategory', 'ipamembercertprofile', + 'usercategory', 'memberuser', + 'hostcategory', 'memberhost', + 'servicecategory', 'memberservice' + }, + 'replaces': [ + '(targetattr = "ipamemberca || ipamembercertprofile || memberuser || memberservice || memberhost || ipacacategory || ipacertprofilecategory || usercategory || hostcategory || servicecategory")(target = "ldap:///ipauniqueid=*,cn=caacls,cn=ca,$SUFFIX")(version 3.0;acl "permission:Manage CA ACL membership";allow (write) groupdn = "ldap:///cn=Manage CA ACL membership,cn=permissions,cn=pbac,$SUFFIX";)', + ], + 'default_privileges': {'CA Administrator'}, + }, + 'System: Modify CA ACL': { + 'ipapermright': {'write'}, + 'ipapermdefaultattr': { + 'cn', 'description', 'ipaenabledflag', + }, + 'replaces': [ + '(targetattr = "cn || description || ipaenabledflag")(target = "ldap:///ipauniqueid=*,cn=caacls,cn=ca,$SUFFIX")(version 3.0;acl "permission:Modify CA ACL";allow (write) groupdn = "ldap:///cn=Modify CA ACL,cn=permissions,cn=pbac,$SUFFIX";)', + ], + 'default_privileges': {'CA Administrator'}, + }, + } + + label = _('CA ACLs') + label_singular = _('CA ACL') + + takes_params = ( + Str('cn', + cli_name='name', + label=_('ACL name'), + primary_key=True, + ), + Str('description?', + cli_name='desc', + label=_('Description'), + ), + Bool('ipaenabledflag?', + label=_('Enabled'), + flags=['no_option'], + ), + # Commented until subca plugin arrives + #StrEnum('ipacacategory?', + # cli_name='cacat', + # label=_('CA category'), + # doc=_('CA category the ACL applies to'), + # values=(u'all', ), + #), + StrEnum('ipacertprofilecategory?', + cli_name='profilecat', + label=_('Profile category'), + doc=_('Profile category the ACL applies to'), + values=(u'all', ), + ), + StrEnum('usercategory?', + cli_name='usercat', + label=_('User category'), + doc=_('User category the ACL applies to'), + values=(u'all', ), + ), + StrEnum('hostcategory?', + cli_name='hostcat', + label=_('Host category'), + doc=_('Host category the ACL applies to'), + values=(u'all', ), + ), + StrEnum('servicecategory?', + cli_name='servicecat', + label=_('Service category'), + doc=_('Service category the ACL applies to'), + values=(u'all', ), + ), + # Commented until subca plugin arrives + #Str('ipamemberca_subca?', + # label=_('CAs'), + # flags=['no_create', 'no_update', 'no_search'], + #), + Str('ipamembercertprofile_certprofile?', + label=_('Profiles'), + flags=['no_create', 'no_update', 'no_search'], + ), + Str('memberuser_user?', + label=_('Users'), + flags=['no_create', 'no_update', 'no_search'], + ), + Str('memberuser_group?', + label=_('User Groups'), + flags=['no_create', 'no_update', 'no_search'], + ), + Str('memberhost_host?', + label=_('Hosts'), + flags=['no_create', 'no_update', 'no_search'], + ), + Str('memberhost_hostgroup?', + label=_('Host Groups'), + flags=['no_create', 'no_update', 'no_search'], + ), + Str('memberservice_service?', + label=_('Services'), + flags=['no_create', 'no_update', 'no_search'], + ), + ) + + +@register() +class caacl_add(LDAPCreate): + __doc__ = _('Create a new CA ACL.') + + msg_summary = _('Added CA ACL "%(value)s"') + + def pre_callback(self, ldap, dn, entry_attrs, attrs_list, *keys, **options): + # CA ACLs are enabled by default + entry_attrs['ipaenabledflag'] = ['TRUE'] + return dn + + +@register() +class caacl_del(LDAPDelete): + __doc__ = _('Delete a CA ACL.') + + msg_summary = _('Deleted CA ACL "%(value)s"') + + +@register() +class caacl_mod(LDAPUpdate): + __doc__ = _('Modify a CA ACL.') + + msg_summary = _('Modified CA ACL "%(value)s"') + + def pre_callback(self, ldap, dn, entry_attrs, attrs_list, *keys, **options): + assert isinstance(dn, DN) + try: + entry_attrs = ldap.get_entry(dn, attrs_list) + dn = entry_attrs.dn + except errors.NotFound: + self.obj.handle_not_found(*keys) + + # Commented until subca plugin arrives + #if is_all(options, 'ipacacategory') and 'ipamemberca' in entry_attrs: + # raise errors.MutuallyExclusiveError(reason=_( + # "CA category cannot be set to 'all' " + # "while there are allowed CAs")) + if (is_all(options, 'ipacertprofilecategory') + and 'ipamembercertprofile' in entry_attrs): + raise errors.MutuallyExclusiveError(reason=_( + "profile category cannot be set to 'all' " + "while there are allowed profiles")) + if is_all(options, 'usercategory') and 'memberuser' in entry_attrs: + raise errors.MutuallyExclusiveError(reason=_( + "user category cannot be set to 'all' " + "while there are allowed users")) + if is_all(options, 'hostcategory') and 'memberhost' in entry_attrs: + raise errors.MutuallyExclusiveError(reason=_( + "host category cannot be set to 'all' " + "while there are allowed hosts")) + if is_all(options, 'servicecategory') and 'memberservice' in entry_attrs: + raise errors.MutuallyExclusiveError(reason=_( + "service category cannot be set to 'all' " + "while there are allowed services")) + return dn + + +@register() +class caacl_find(LDAPSearch): + __doc__ = _('Search for CA ACLs.') + + msg_summary = ngettext( + '%(count)d CA ACL matched', '%(count)d CA ACLs matched', 0 + ) + + +@register() +class caacl_show(LDAPRetrieve): + __doc__ = _('Display the properties of a CA ACL.') + + +@register() +class caacl_enable(LDAPQuery): + __doc__ = _('Enable a CA ACL.') + + msg_summary = _('Enabled CA ACL "%(value)s"') + has_output = output.standard_value + + def execute(self, cn, **options): + ldap = self.obj.backend + + dn = self.obj.get_dn(cn) + try: + entry_attrs = ldap.get_entry(dn, ['ipaenabledflag']) + except errors.NotFound: + self.obj.handle_not_found(cn) + + entry_attrs['ipaenabledflag'] = ['TRUE'] + + try: + ldap.update_entry(entry_attrs) + except errors.EmptyModlist: + pass + + return dict( + result=True, + value=pkey_to_value(cn, options), + ) + + +@register() +class caacl_disable(LDAPQuery): + __doc__ = _('Disable a CA ACL.') + + msg_summary = _('Disabled CA ACL "%(value)s"') + has_output = output.standard_value + + def execute(self, cn, **options): + ldap = self.obj.backend + + dn = self.obj.get_dn(cn) + try: + entry_attrs = ldap.get_entry(dn, ['ipaenabledflag']) + except errors.NotFound: + self.obj.handle_not_found(cn) + + entry_attrs['ipaenabledflag'] = ['FALSE'] + + try: + ldap.update_entry(entry_attrs) + except errors.EmptyModlist: + pass + + return dict( + result=True, + value=pkey_to_value(cn, options), + ) + + +@register() +class caacl_add_user(LDAPAddMember): + __doc__ = _('Add users and groups to a CA ACL.') + + member_attributes = ['memberuser'] + member_count_out = ( + _('%i user or group added.'), + _('%i users or groups added.')) + + def pre_callback(self, ldap, dn, found, not_found, *keys, **options): + assert isinstance(dn, DN) + try: + entry_attrs = ldap.get_entry(dn, self.obj.default_attributes) + dn = entry_attrs.dn + except errors.NotFound: + self.obj.handle_not_found(*keys) + if is_all(entry_attrs, 'usercategory'): + raise errors.MutuallyExclusiveError( + reason=_("users cannot be added when user category='all'")) + return dn + + +@register() +class caacl_remove_user(LDAPRemoveMember): + __doc__ = _('Remove users and groups from a CA ACL.') + + member_attributes = ['memberuser'] + member_count_out = ( + _('%i user or group removed.'), + _('%i users or groups removed.')) + + +@register() +class caacl_add_host(LDAPAddMember): + __doc__ = _('Add target hosts and hostgroups to a CA ACL.') + + member_attributes = ['memberhost'] + member_count_out = ( + _('%i host or hostgroup added.'), + _('%i hosts or hostgroups added.')) + + def pre_callback(self, ldap, dn, found, not_found, *keys, **options): + assert isinstance(dn, DN) + try: + entry_attrs = ldap.get_entry(dn, self.obj.default_attributes) + dn = entry_attrs.dn + except errors.NotFound: + self.obj.handle_not_found(*keys) + if is_all(entry_attrs, 'hostcategory'): + raise errors.MutuallyExclusiveError( + reason=_("hosts cannot be added when host category='all'")) + return dn + + +@register() +class caacl_remove_host(LDAPRemoveMember): + __doc__ = _('Remove target hosts and hostgroups from a CA ACL.') + + member_attributes = ['memberhost'] + member_count_out = ( + _('%i host or hostgroup removed.'), + _('%i hosts or hostgroups removed.')) + + +@register() +class caacl_add_service(LDAPAddMember): + __doc__ = _('Add services to a CA ACL.') + + member_attributes = ['memberservice'] + member_count_out = (_('%i service added.'), _('%i services added.')) + + def pre_callback(self, ldap, dn, found, not_found, *keys, **options): + assert isinstance(dn, DN) + try: + entry_attrs = ldap.get_entry(dn, self.obj.default_attributes) + dn = entry_attrs.dn + except errors.NotFound: + self.obj.handle_not_found(*keys) + if is_all(entry_attrs, 'servicecategory'): + raise errors.MutuallyExclusiveError(reason=_( + "services cannot be added when service category='all'")) + return dn + + +@register() +class caacl_remove_service(LDAPRemoveMember): + __doc__ = _('Remove services from a CA ACL.') + + member_attributes = ['memberservice'] + member_count_out = (_('%i service removed.'), _('%i services removed.')) + + +caacl_output_params = global_output_params + ( + Str('ipamembercertprofile', + label=_('Failed profiles'), + ), + # Commented until caacl plugin arrives + #Str('ipamemberca', + # label=_('Failed CAs'), + #), +) + + +@register() +class caacl_add_profile(LDAPAddMember): + __doc__ = _('Add profiles to a CA ACL.') + + has_output_params = caacl_output_params + + member_attributes = ['ipamembercertprofile'] + member_count_out = (_('%i profile added.'), _('%i profiles added.')) + + def pre_callback(self, ldap, dn, found, not_found, *keys, **options): + assert isinstance(dn, DN) + try: + entry_attrs = ldap.get_entry(dn, self.obj.default_attributes) + dn = entry_attrs.dn + except errors.NotFound: + self.obj.handle_not_found(*keys) + if is_all(entry_attrs, 'ipacertprofilecategory'): + raise errors.MutuallyExclusiveError(reason=_( + "profiles cannot be added when profile category='all'")) + return dn + + +@register() +class caacl_remove_profile(LDAPRemoveMember): + __doc__ = _('Remove profiles from a CA ACL.') + + has_output_params = caacl_output_params + + member_attributes = ['ipamembercertprofile'] + member_count_out = (_('%i profile removed.'), _('%i profiles removed.')) |