[apparmor] [1/4] Add ChangeProfileRule and ChangeProfileRuleset classes
Christian Boltz
apparmor at cboltz.de
Sat May 9 20:35:54 UTC 2015
Hello,
this patch adds utils/apparmor/rule/change_profile.py with the
ChangeProfileRule and ChangeProfileRuleset classes. These classes are
meant to handle change_profile rules.
In comparison to the current code in aa.py, ChangeProfileRule has some
added features:
- support for audit and allow/deny keywords (for which John promised a
parser patch really soon)
- support for change_profile rules with an exec condition
Also add the improved regex RE_PROFILE_CHANGE_PROFILE_2 to regex.py.
[ 01-add-ChangeProfileRule.diff ]
=== modified file utils/apparmor/regex.py
--- utils/apparmor/regex.py 2015-05-08 21:27:52.890348306 +0200
+++ utils/apparmor/regex.py 2015-05-09 22:00:07.611093982 +0200
@@ -72,6 +72,16 @@
'\s+((flags=)?\((?P<flags>.+)\)\s+)?\{' +
RE_EOL)
+
+RE_PROFILE_CHANGE_PROFILE_2 = re.compile(
+ RE_AUDIT_DENY +
+ 'change_profile' +
+ '(\s+' + RE_PROFILE_PATH % 'execcond' + ')?' + # optionally exec condition
+ '(\s+->\s*' + RE_PROFILE_NAME % 'targetprofile' + ')?' + # optionally '->' target profile
+ RE_COMMA_EOL)
+
+
+
def parse_profile_start_line(line, filename):
matches = RE_PROFILE_START.search(line)
=== modified file utils/apparmor/rule/change_profile.py
--- utils/apparmor/rule/change_profile.py 2015-05-09 22:05:27.191505750 +0200
+++ utils/apparmor/rule/change_profile.py 2015-05-09 20:25:33.333309986 +0200
@@ -0,0 +1,173 @@
+#!/usr/bin/env python
+# ----------------------------------------------------------------------
+# Copyright (C) 2013 Kshitij Gupta <kgupta8592 at gmail.com>
+# Copyright (C) 2015 Christian Boltz <apparmor at cboltz.de>
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of version 2 of the GNU General Public
+# License as published by the Free Software Foundation.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# ----------------------------------------------------------------------
+
+from apparmor.regex import RE_PROFILE_CHANGE_PROFILE_2, strip_quotes
+from apparmor.common import AppArmorBug, AppArmorException
+from apparmor.rule import BaseRule, BaseRuleset, parse_modifiers, quote_if_needed
+
+# setup module translations
+from apparmor.translations import init_translation
+_ = init_translation()
+
+
+class ChangeProfileRule(BaseRule):
+ '''Class to handle and store a single network rule'''
+
+ # Nothing external should reference this class, all external users
+ # should reference the class field ChangeProfileRule.ALL
+ class __ChangeProfileAll(object):
+ pass
+
+ ALL = __ChangeProfileAll
+
+ def __init__(self, execcond, targetprofile, audit=False, deny=False, allow_keyword=False,
+ comment='', log_event=None):
+
+ '''
+ CHANGE_PROFILE RULE = 'change_profile' [ EXEC COND ] [ -> PROGRAMCHILD ]
+ '''
+
+ super(ChangeProfileRule, self).__init__(audit=audit, deny=deny,
+ allow_keyword=allow_keyword,
+ comment=comment,
+ log_event=log_event)
+
+ self.execcond = None
+ self.all_execconds = False
+ if execcond == ChangeProfileRule.ALL:
+ self.all_execconds = True
+ elif type(execcond) == str:
+ if execcond.strip():
+ if execcond.startswith('/'):
+ self.execcond = execcond
+ else:
+ raise AppArmorException('Exec condition in change_profile rule does not start with /: %s' % str(execcond))
+ else:
+ raise AppArmorBug('Empty exec condition in change_profile rule')
+ else:
+ raise AppArmorBug('Passed unknown object to ChangeProfileRule: %s' % str(execcond))
+
+ self.targetprofile = None
+ self.all_targetprofiles = False
+ if targetprofile == ChangeProfileRule.ALL:
+ self.all_targetprofiles = True
+ elif type(targetprofile) == str:
+ if targetprofile.strip():
+ self.targetprofile = targetprofile
+ else:
+ raise AppArmorBug('Empty target profile in change_profile rule')
+ else:
+ raise AppArmorBug('Passed unknown object to ChangeProfileRule: %s' % str(targetprofile))
+
+ @classmethod
+ def _match(cls, raw_rule):
+ return RE_PROFILE_CHANGE_PROFILE_2.search(raw_rule)
+
+ @classmethod
+ def _parse(cls, raw_rule):
+ '''parse raw_rule and return ChangeProfileRule'''
+
+ matches = cls._match(raw_rule)
+ if not matches:
+ raise AppArmorException(_("Invalid change_profile rule '%s'") % raw_rule)
+
+ audit, deny, allow_keyword, comment = parse_modifiers(matches)
+
+ if matches.group('execcond'):
+ execcond = strip_quotes(matches.group('execcond'))
+ else:
+ execcond = ChangeProfileRule.ALL
+
+ if matches.group('targetprofile'):
+ targetprofile = strip_quotes(matches.group('targetprofile'))
+ else:
+ targetprofile = ChangeProfileRule.ALL
+
+ return ChangeProfileRule(execcond, targetprofile,
+ audit=audit, deny=deny, allow_keyword=allow_keyword, comment=comment)
+
+ def get_clean(self, depth=0):
+ '''return rule (in clean/default formatting)'''
+
+ space = ' ' * depth
+
+ if self.all_execconds:
+ execcond = ''
+ elif self.execcond:
+ execcond = ' %s' % quote_if_needed(self.execcond)
+ else:
+ raise AppArmorBug('Empty execcond in change_profile rule')
+
+ if self.all_targetprofiles:
+ targetprofile = ''
+ elif self.targetprofile:
+ targetprofile = ' -> %s' % quote_if_needed(self.targetprofile)
+ else:
+ raise AppArmorBug('Empty target profile in change_profile rule')
+
+ return('%s%schange_profile%s%s,%s' % (space, self.modifiers_str(), execcond, targetprofile, self.comment))
+
+ def is_covered_localvars(self, other_rule):
+ '''check if other_rule is covered by this rule object'''
+
+ if not other_rule.execcond and not other_rule.all_execconds:
+ raise AppArmorBug('No execcond specified in other change_profile rule')
+
+ if not other_rule.targetprofile and not other_rule.all_targetprofiles:
+ raise AppArmorBug('No target profile specified in other change_profile rule')
+
+ if not self.all_execconds:
+ if other_rule.all_execconds:
+ return False
+ if other_rule.execcond != self.execcond:
+ # TODO: honor globbing and variables
+ return False
+
+ if not self.all_targetprofiles:
+ if other_rule.all_targetprofiles:
+ return False
+ if other_rule.targetprofile != self.targetprofile:
+ return False
+
+ # still here? -> then it is covered
+ return True
+
+ def is_equal_localvars(self, rule_obj):
+ '''compare if rule-specific variables are equal'''
+
+ if not type(rule_obj) == ChangeProfileRule:
+ raise AppArmorBug('Passed non-change_profile rule: %s' % str(rule_obj))
+
+ if (self.execcond != rule_obj.execcond
+ or self.all_execconds != rule_obj.all_execconds):
+ return False
+
+ if (self.targetprofile != rule_obj.targetprofile
+ or self.all_targetprofiles != rule_obj.all_targetprofiles):
+ return False
+
+ return True
+
+
+class ChangeProfileRuleset(BaseRuleset):
+ '''Class to handle and store a collection of change_profile rules'''
+
+ def get_glob(self, path_or_rule):
+ '''Return the next possible glob. For change_profile rules, that can be "change_profile EXECCOND,",
+ "change_profile -> TARGET_PROFILE," or "change_profile," (all change_profile).
+ Also, EXECCOND filename can be globbed'''
+ # XXX implement all options mentioned above ;-)
+ return 'change_profile,'
Regards,
Christian Boltz
--
Graphisch??? Wie meinen? Hast du zuviel Fleisch von zu "gluecklichen"
Rindern gefuttert? *scnr* Wozu zum Henker sollte man sowas brauchen?
Logo ginge auch per ASCII :) (Logo? welches Logo? Wozu ueberhaupt?)
[David Haller in suse-linux]
More information about the AppArmor
mailing list