From 4c4888190b78b0a4e58471235550d1709ef7e329 Mon Sep 17 00:00:00 2001 From: Rob Crittenden Date: Jan 18 2012 09:03:05 +0000 Subject: In sudo when the category is all do not allow members, and vice versa. This is what we already do in the HBAC plugin, this ports it to Sudo. If a category (user, host, etc) is u'all' then we don't allow individual members be added. Conversely if there are members we don't allow the category be set to u'all'. https://fedorahosted.org/freeipa/ticket/1440 --- diff --git a/ipalib/plugins/hbacrule.py b/ipalib/plugins/hbacrule.py index 92b656d..0fa44a5 100644 --- a/ipalib/plugins/hbacrule.py +++ b/ipalib/plugins/hbacrule.py @@ -96,10 +96,13 @@ def is_all(options, attribute): """ See if options[attribute] is lower-case 'all' in a safe way. """ - if attribute in options and \ - options[attribute] is not None and \ - options[attribute].lower() == 'all': - return True + if attribute in options and options[attribute] is not None: + if type(options[attribute]) in (list, tuple): + value = options[attribute][0].lower() + else: + value = options[attribute].lower() + if value == 'all': + return True else: return False diff --git a/ipalib/plugins/sudorule.py b/ipalib/plugins/sudorule.py index 65a1d85..df395ea 100644 --- a/ipalib/plugins/sudorule.py +++ b/ipalib/plugins/sudorule.py @@ -20,6 +20,7 @@ from ipalib import api, errors from ipalib import Str, StrEnum from ipalib.plugins.baseldap import * +from ipalib.plugins.hbacrule import is_all from ipalib import _, ngettext __doc__ = _(""" @@ -77,6 +78,8 @@ class sudorule(LDAPObject): 'description', 'usercategory', 'hostcategory', 'cmdcategory', 'memberuser', 'memberhost', 'memberallowcmd', 'memberdenycmd', 'ipasudoopt', + 'ipasudorunas', 'ipasudorunasgroup', + 'ipasudorunasusercategory', 'ipasudorunasgroupcategory', ] uuid_attribute = 'ipauniqueid' rdn_attribute = 'ipauniqueid' @@ -232,6 +235,25 @@ class sudorule_mod(LDAPUpdate): __doc__ = _('Modify Sudo Rule.') msg_summary = _('Modified Sudo Rule "%(value)s"') + def pre_callback(self, ldap, dn, entry_attrs, attrs_list, *keys, **options): + try: + (_dn, _entry_attrs) = ldap.get_entry(dn, self.obj.default_attributes) + except errors.NotFound: + self.obj.handle_not_found(*keys) + + if is_all(options, 'usercategory') and 'memberuser' in _entry_attrs: + raise errors.MutuallyExclusiveError(reason=_("user category cannot be set to 'all' while there are 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 hosts")) + if is_all(options, 'cmdcategory') and ('memberallowcmd' or + 'memberdenywcmd') in _entry_attrs: + raise errors.MutuallyExclusiveError(reason=_("command category cannot be set to 'all' while there are allow or deny commands")) + if is_all(options, 'ipasudorunasusercategory') and 'ipasudorunas' in _entry_attrs: + raise errors.MutuallyExclusiveError(reason=_("user runAs category cannot be set to 'all' while there are users")) + if is_all(options, 'ipasudorunasgroupcategory') and 'ipasudorunasgroup' in _entry_attrs: + raise errors.MutuallyExclusiveError(reason=_("group runAs category cannot be set to 'all' while there are groups")) + + return dn api.register(sudorule_mod) @@ -306,6 +328,16 @@ class sudorule_add_allow_command(LDAPAddMember): member_attributes = ['memberallowcmd'] member_count_out = ('%i object added.', '%i objects added.') + def pre_callback(self, ldap, dn, found, not_found, *keys, **options): + try: + (_dn, _entry_attrs) = ldap.get_entry(dn, self.obj.default_attributes) + except errors.NotFound: + self.obj.handle_not_found(*keys) + if is_all(_entry_attrs, 'cmdcategory'): + raise errors.MutuallyExclusiveError(reason=_("commands cannot be added when command category='all'")) + + return dn + api.register(sudorule_add_allow_command) @@ -324,6 +356,15 @@ class sudorule_add_deny_command(LDAPAddMember): member_attributes = ['memberdenycmd'] member_count_out = ('%i object added.', '%i objects added.') + def pre_callback(self, ldap, dn, found, not_found, *keys, **options): + try: + (_dn, _entry_attrs) = ldap.get_entry(dn, self.obj.default_attributes) + except errors.NotFound: + self.obj.handle_not_found(*keys) + if is_all(_entry_attrs, 'cmdcategory'): + raise errors.MutuallyExclusiveError(reason=_("commands cannot be added when command category='all'")) + return dn + api.register(sudorule_add_deny_command) @@ -342,6 +383,15 @@ class sudorule_add_user(LDAPAddMember): member_attributes = ['memberuser'] member_count_out = ('%i object added.', '%i objects added.') + def pre_callback(self, ldap, dn, found, not_found, *keys, **options): + try: + (_dn, _entry_attrs) = ldap.get_entry(dn, self.obj.default_attributes) + 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 + def post_callback(self, ldap, completed, failed, dn, entry_attrs, *keys, **options): completed_external = 0 # Sift through the user failures. We assume that these are all @@ -410,6 +460,15 @@ class sudorule_add_host(LDAPAddMember): member_attributes = ['memberhost'] member_count_out = ('%i object added.', '%i objects added.') + def pre_callback(self, ldap, dn, found, not_found, *keys, **options): + try: + (_dn, _entry_attrs) = ldap.get_entry(dn, self.obj.default_attributes) + 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 + def post_callback(self, ldap, completed, failed, dn, entry_attrs, *keys, **options): completed_external = 0 # Sift through the host failures. We assume that these are all @@ -485,6 +544,14 @@ class sudorule_add_runasuser(LDAPAddMember): return False return True + try: + (_dn, _entry_attrs) = ldap.get_entry(dn, self.obj.default_attributes) + except errors.NotFound: + self.obj.handle_not_found(*keys) + if is_all(_entry_attrs, 'ipasudorunasusercategory') or \ + is_all(_entry_attrs, 'ipasudorunasgroupcategory'): + raise errors.MutuallyExclusiveError(reason=_("users cannot be added when runAs user or runAs group category='all'")) + if 'user' in options: for name in options['user']: if not check_validity(name): @@ -575,6 +642,14 @@ class sudorule_add_runasgroup(LDAPAddMember): return False return True + try: + (_dn, _entry_attrs) = ldap.get_entry(dn, self.obj.default_attributes) + except errors.NotFound: + self.obj.handle_not_found(*keys) + if is_all(_entry_attrs, 'ipasudorunasusercategory') or \ + is_all(_entry_attrs, 'ipasudorunasgroupcategory'): + raise errors.MutuallyExclusiveError(reason=_("users cannot be added when runAs user or runAs group category='all'")) + if 'group' in options: for name in options['group']: if not check_validity(name): diff --git a/tests/test_xmlrpc/test_sudorule_plugin.py b/tests/test_xmlrpc/test_sudorule_plugin.py index 88e31c7..07d23c3 100644 --- a/tests/test_xmlrpc/test_sudorule_plugin.py +++ b/tests/test_xmlrpc/test_sudorule_plugin.py @@ -47,7 +47,7 @@ class test_sudorule(XMLRPC_test): test_denycommand = u'/usr/bin/testdenysudocmd1' test_runasuser = u'manager' test_runasgroup = u'manager' - test_catagory = u'all' + test_category = u'all' test_option = u'authenticate' def test_0_sudorule_add(self): @@ -520,7 +520,99 @@ class test_sudorule(XMLRPC_test): assert 'memberdenycmd_sudocmd' not in entry assert 'memberdenycmd_sudocmdgroup' not in entry - def test_c_sudorule_clear_testing_data(self): + def test_c_sudorule_exclusiveuser(self): + """ + Test adding a user to an Sudo rule when usercat='all' + """ + api.Command['sudorule_mod'](self.rule_name, usercategory=u'all') + try: + api.Command['sudorule_add_user'](self.rule_name, users='admin') + except errors.MutuallyExclusiveError: + pass + api.Command['sudorule_mod'](self.rule_name, usercategory=u'') + + def test_d_sudorule_exclusiveuser(self): + """ + Test setting usercat='all' in an Sudo rule when there are users + """ + api.Command['sudorule_add_user'](self.rule_name, users='admin') + try: + api.Command['sudorule_mod'](self.rule_name, usercategory=u'all') + except errors.MutuallyExclusiveError: + pass + finally: + api.Command['sudorule_remove_user'](self.rule_name, users='admin') + + def test_e_sudorule_exclusivehost(self): + """ + Test adding a host to an Sudo rule when hostcat='all' + """ + api.Command['sudorule_mod'](self.rule_name, hostcategory=u'all') + try: + api.Command['sudorule_add_host'](self.rule_name, host=self.test_host) + except errors.MutuallyExclusiveError: + pass + api.Command['sudorule_mod'](self.rule_name, hostcategory=u'') + + def test_f_sudorule_exclusivehost(self): + """ + Test setting hostcat='all' in an Sudo rule when there are hosts + """ + api.Command['sudorule_add_host'](self.rule_name, host=self.test_host) + try: + api.Command['sudorule_mod'](self.rule_name, hostcategory=u'all') + except errors.MutuallyExclusiveError: + pass + finally: + api.Command['sudorule_remove_host'](self.rule_name, host=self.test_host) + + def test_g_sudorule_exclusivecommand(self): + """ + Test adding a command to an Sudo rule when cmdcategory='all' + """ + api.Command['sudorule_mod'](self.rule_name, cmdcategory=u'all') + try: + api.Command['sudorule_add_allow_command'](self.rule_name, sudocmd=self.test_command) + except errors.MutuallyExclusiveError: + pass + api.Command['sudorule_mod'](self.rule_name, cmdcategory=u'') + + def test_h_sudorule_exclusivecommand(self): + """ + Test setting cmdcategory='all' in an Sudo rule when there are commands + """ + api.Command['sudorule_add_allow_command'](self.rule_name, sudocmd=self.test_command) + try: + api.Command['sudorule_mod'](self.rule_name, cmdcategory=u'all') + except errors.MutuallyExclusiveError: + pass + finally: + api.Command['sudorule_remove_allow_command'](self.rule_name, sudocmd=self.test_command) + + def test_i_sudorule_exclusiverunas(self): + """ + Test adding a runasuser to an Sudo rule when ipasudorunasusercategory='all' + """ + api.Command['sudorule_mod'](self.rule_name, ipasudorunasusercategory=u'all') + try: + api.Command['sudorule_add_runasuser'](self.rule_name, sudocmd=self.test_user) + except errors.MutuallyExclusiveError: + pass + api.Command['sudorule_mod'](self.rule_name, ipasudorunasusercategory=u'') + + def test_j_sudorule_exclusiverunas(self): + """ + Test setting ipasudorunasusercategory='all' in an Sudo rule when there are runas users + """ + api.Command['sudorule_add_runasuser'](self.rule_name, user=self.test_user) + try: + api.Command['sudorule_mod'](self.rule_name, ipasudorunasusercategory=u'all') + except errors.MutuallyExclusiveError: + pass + finally: + api.Command['sudorule_remove_runasuser'](self.rule_name, user=self.test_command) + + def test_k_sudorule_clear_testing_data(self): """ Clear data for Sudo rule plugin testing. """ @@ -534,7 +626,7 @@ class test_sudorule(XMLRPC_test): api.Command['sudocmdgroup_del'](self.test_sudodenycmdgroup) - def test_f_sudorule_del(self): + def test_l_sudorule_del(self): """ Test deleting a Sudo rule using `xmlrpc.sudorule_del`. """