diff --git a/sediff b/sediff index 5fd0c10..45090e9 100755 --- a/sediff +++ b/sediff @@ -1,5 +1,5 @@ #!/usr/bin/python -# Copyright 2015, Tresys Technology, LLC +# Copyright 2015-2016, Tresys Technology, LLC # # This file is part of SETools. # @@ -487,6 +487,52 @@ try: print() + if all_differences or args.range_trans: + if diff.added_range_transitions or diff.removed_range_transitions or \ + diff.modified_range_transitions or args.range_trans: + print("Range_transition Rules ({0} Added, {1} Removed, {2} Modified)".format( + len(diff.added_range_transitions), len(diff.removed_range_transitions), + len(diff.modified_range_transitions))) + + if diff.added_range_transitions: + print(" Added Range_transition Rules: {0}".format( + len(diff.added_range_transitions))) + for r in sorted(diff.added_range_transitions): + print(" + {0}".format(r)) + + if diff.removed_range_transitions: + print(" Removed Range_transition Rules: {0}".format( + len(diff.removed_range_transitions))) + for r in sorted(diff.removed_range_transitions): + print(" - {0}".format(r)) + + if diff.modified_range_transitions: + print(" Modified Range_transition Rules: {0}".format( + len(diff.modified_range_transitions))) + + for rule, added_default, removed_default in sorted(diff.modified_range_transitions): + # added brackets around range change for clarity since ranges + # can have '-' and spaces. + rule_string = \ + "{0.ruletype} {0.source} {0.target}:{0.tclass} +[{1}] -[{2}]".format( + rule, added_default, removed_default) + + try: + rule_string += " {0}".format(self.filename) + except: + pass + + rule_string += ";" + + try: + rule_string += " [ {0} ]".format(rule.conditional) + except: + pass + + print(" * {0}".format(rule_string)) + + print() + except Exception as err: if args.debug: import traceback diff --git a/setools/diff/__init__.py b/setools/diff/__init__.py index 8bc045f..2eba1fb 100644 --- a/setools/diff/__init__.py +++ b/setools/diff/__init__.py @@ -1,4 +1,4 @@ -# Copyright 2015, Tresys Technology, LLC +# Copyright 2015-2016, Tresys Technology, LLC # # This file is part of SETools. # @@ -17,6 +17,7 @@ # . # from .commons import CommonDifference +from .mlsrules import MLSRulesDifference from .objclass import ObjClassDifference from .roles import RolesDifference from .terules import TERulesDifference @@ -26,6 +27,7 @@ __all__ = ['PolicyDifference'] class PolicyDifference(CommonDifference, + MLSRulesDifference, ObjClassDifference, RolesDifference, TERulesDifference, diff --git a/setools/diff/mlsrules.py b/setools/diff/mlsrules.py new file mode 100644 index 0000000..6ccbd92 --- /dev/null +++ b/setools/diff/mlsrules.py @@ -0,0 +1,135 @@ +# Copyright 2016, Tresys Technology, LLC +# +# This file is part of SETools. +# +# SETools is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as +# published by the Free Software Foundation, either version 2.1 of +# the License, or (at your option) any later version. +# +# SETools 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 Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with SETools. If not, see +# . +# +from collections import namedtuple + +from ..policyrep.exception import RuleNotConditional, RuleUseError, TERuleNoFilename + +from .descriptors import DiffResultDescriptor +from .difference import Difference, SymbolWrapper, Wrapper +from .mls import RangeWrapper + + +modified_mlsrule_record = namedtuple("modified_mlsrule", ["rule", + "added_default", + "removed_default"]) + + +class MLSRulesDifference(Difference): + + """Determine the difference in MLS rules between two policies.""" + + added_range_transitions = DiffResultDescriptor("diff_range_transitions") + removed_range_transitions = DiffResultDescriptor("diff_range_transitions") + modified_range_transitions = DiffResultDescriptor("diff_range_transitions") + + # Lists of rules for each policy + _left_range_transitions = None + _right_range_transitions = None + + def diff_range_transitions(self): + """Generate the difference in range_transition rules between the policies.""" + + self.log.info( + "Generating range_transition differences from {0.left_policy} to {0.right_policy}". + format(self)) + + if self._left_range_transitions is None or self._right_range_transitions is None: + self._create_mls_rule_lists() + + self.added_range_transitions, \ + self.removed_range_transitions, \ + self.modified_range_transitions = self._diff_mls_rules( + self._expand_generator(self._left_range_transitions, MLSRuleWrapper), + self._expand_generator(self._right_range_transitions, MLSRuleWrapper)) + + # + # Internal functions + # + def _create_mls_rule_lists(self): + """Create rule lists for both policies.""" + self._left_range_transitions = [] + for rule in self.left_policy.mlsrules(): + # do not expand yet, to keep memory + # use down as long as possible + if rule.ruletype == "range_transition": + self._left_range_transitions.append(rule) + else: + raise TypeError("Unknown rule type: {0}".format(rule.ruletype)) + + self._right_range_transitions = [] + for rule in self.right_policy.mlsrules(): + # do not expand yet, to keep memory + # use down as long as possible + if rule.ruletype == "range_transition": + self._right_range_transitions.append(rule) + else: + raise TypeError("Unknown rule type: {0}".format(rule.ruletype)) + + def _diff_mls_rules(self, left_list, right_list): + """Common method for comparing type_* rules.""" + added, removed, matched = self._set_diff(left_list, right_list) + + modified = [] + + for left_rule, right_rule in matched: + # Criteria for modified rules + # 1. change to default range + if RangeWrapper(left_rule.default) != RangeWrapper(right_rule.default): + modified.append(modified_mlsrule_record(left_rule, + right_rule.default, + left_rule.default)) + + return added, removed, modified + + def _reset_diff(self): + """Reset diff results on policy changes.""" + self.log.debug("Resetting MLS rule differences") + self.added_range_transitions = None + self.removed_range_transitions = None + self.modified_range_transitions = None + + # Sets of rules for each policy + self._left_range_transitions = None + self._right_range_transitions = None + + +class MLSRuleWrapper(Wrapper): + + """Wrap MLS rules to allow set operations.""" + + def __init__(self, rule): + self.origin = rule + self.ruletype = rule.ruletype + self.source = SymbolWrapper(rule.source) + self.target = SymbolWrapper(rule.target) + self.tclass = SymbolWrapper(rule.tclass) + self.key = hash(rule) + + def __hash__(self): + return self.key + + def __lt__(self, other): + return self.key < other.key + + def __eq__(self, other): + # because MLSRuleDifference groups rules by ruletype, + # the ruletype always matches. + return self.source == other.source and \ + self.target == other.target and \ + self.tclass == other.tclass diff --git a/tests/diff.py b/tests/diff.py index 571f8d7..b231ff0 100644 --- a/tests/diff.py +++ b/tests/diff.py @@ -1,4 +1,4 @@ -# Copyright 2015, Tresys Technology, LLC +# Copyright 2015-2016, Tresys Technology, LLC # # This file is part of SETools. # @@ -736,6 +736,48 @@ class PolicyDifferenceTest(ValidateRule, unittest.TestCase): self.assertEqual("tm_new_type", added_default) self.assertEqual("tm_old_type", removed_default) + # + # Range_transition rules + # + def test_added_range_transition_rules(self): + """Diff: added range_transition rules.""" + rules = sorted(self.diff.added_range_transitions) + self.assertEqual(2, len(rules)) + + # added rule with new type + self.validate_rule(rules[0], "range_transition", "added_type", "system", "infoflow4", + "s3") + + # added rule with existing types + self.validate_rule(rules[1], "range_transition", "rt_added_rule_source", + "rt_added_rule_target", "infoflow", "s3") + + def test_removed_range_transition_rules(self): + """Diff: removed range_transition rules.""" + rules = sorted(self.diff.removed_range_transitions) + self.assertEqual(2, len(rules)) + + # removed rule with new type + self.validate_rule(rules[0], "range_transition", "removed_type", "system", "infoflow4", + "s1") + + # removed rule with existing types + self.validate_rule(rules[1], "range_transition", "rt_removed_rule_source", + "rt_removed_rule_target", "infoflow", "s1") + + def test_modified_range_transition_rules(self): + """Diff: modified range_transition rules.""" + l = sorted(self.diff.modified_range_transitions) + self.assertEqual(1, len(l)) + + rule, added_default, removed_default = l[0] + self.assertEqual("range_transition", rule.ruletype) + self.assertEqual("rt_matched_source", rule.source) + self.assertEqual("system", rule.target) + self.assertEqual("infoflow", rule.tclass) + self.assertEqual("s0:c0,c4 - s1:c0.c2,c4", added_default) + self.assertEqual("s2:c0 - s3:c0.c2", removed_default) + class PolicyDifferenceTestNoDiff(unittest.TestCase): @@ -876,3 +918,15 @@ class PolicyDifferenceTestNoDiff(unittest.TestCase): def test_modified_type_members(self): """NoDiff: no modified type_member rules.""" self.assertFalse(self.diff.modified_type_members) + + def test_added_range_transitions(self): + """NoDiff: no added range_transition rules.""" + self.assertFalse(self.diff.added_range_transitions) + + def test_removed_range_transitions(self): + """NoDiff: no removed range_transition rules.""" + self.assertFalse(self.diff.removed_range_transitions) + + def test_modified_range_transitions(self): + """NoDiff: no modified range_transition rules.""" + self.assertFalse(self.diff.modified_range_transitions) diff --git a/tests/diff_left.conf b/tests/diff_left.conf index 2db5cbc..5fe13bf 100644 --- a/tests/diff_left.conf +++ b/tests/diff_left.conf @@ -92,20 +92,38 @@ class modified_remove_perm class modified_change_common inherits removed_common -sensitivity low_s; -sensitivity medium_s alias med; -sensitivity high_s; +sensitivity s0; +sensitivity s1; +sensitivity s2; +sensitivity s3; +sensitivity s40; +sensitivity s41; +sensitivity s42; +sensitivity s43; +sensitivity s44; +sensitivity s45; +sensitivity s46; -dominance { low_s med high_s } +dominance { s0 s1 s2 s3 s40 s41 s42 s43 s44 s45 s46 } -category here; -category there; -category elsewhere alias lost; +category c0; +category c1; +category c2; +category c3; +category c4; #level decl -level low_s:here.there; -level med:here, elsewhere; -level high_s:here.lost; +level s0:c0.c4; +level s1:c0.c4; +level s2:c0.c4; +level s3:c0.c4; +level s40:c0.c4; +level s41:c0.c4; +level s42:c0.c4; +level s43:c0.c4; +level s44:c0.c4; +level s45:c0.c4; +level s46:c0.c4; #some constraints mlsconstrain infoflow hi_r ((l1 dom l2) or (t1 == mls_exempt)); @@ -486,32 +504,78 @@ type_member tm_unioned_perm_via_attr system:infoflow2 system; type_member tm_unioned_perm_via_attr_A_t system:infoflow2 system; type_member tm_unioned_perm_via_attr_B_t system:infoflow2 system; +# range_transition rule differences +type rt_matched_source; +type rt_matched_target; +range_transition rt_matched_source rt_matched_target:infoflow s0; + +type rt_removed_rule_source; +type rt_removed_rule_target; +range_transition rt_removed_rule_source rt_removed_rule_target:infoflow s1; + +type rt_added_rule_source; +type rt_added_rule_target; + +range_transition rt_matched_source system:infoflow s2:c0 - s3:c0.c2; + +range_transition removed_type system:infoflow4 s1; + +# range transitions cannot be conditional. +#type rt_move_to_bool; +#bool rt_move_to_bool_b false; +#range_transition rt_move_to_bool system:infoflow3 s0; + +#type rt_move_from_bool; +#bool rt_move_from_bool_b false; +#if (rt_move_from_bool_b) { +#range_transition rt_move_from_bool system:infoflow4 s0; +#} + +#type rt_switch_block; +#bool rt_switch_block_b false; +#if (rt_switch_block_b) { +#range_transition system rt_switch_block:infoflow5 s0; +#range_transition system rt_switch_block:infoflow6 s0; +#} else { +#range_transition system rt_switch_block:infoflow7 s0; +#} + +attribute rt_match_rule_by_attr; +type rt_match_rule_by_attr_A_t, rt_match_rule_by_attr; +type rt_match_rule_by_attr_B_t, rt_match_rule_by_attr; +range_transition rt_match_rule_by_attr system:infoflow2 s0; + +attribute rt_unioned_perm_via_attr; +type rt_unioned_perm_via_attr_A_t, rt_unioned_perm_via_attr; +type rt_unioned_perm_via_attr_B_t, rt_unioned_perm_via_attr; +range_transition rt_unioned_perm_via_attr system:infoflow2 s0; +range_transition rt_unioned_perm_via_attr_A_t system:infoflow2 s0; + ################################################################################ #users -user system roles system level med range low_s - high_s:here.lost; +user system roles system level s0 range s0 - s46:c0.c4; #normal constraints constrain infoflow hi_w (u1 == u2); #isids -sid kernel system:system:system:medium_s:here -sid security system:system:system:high_s:lost +sid kernel system:system:system:s0 +sid security system:system:system:s0 #fs_use -fs_use_trans devpts system:object_r:system:low_s; -fs_use_xattr ext3 system:object_r:system:low_s; -fs_use_task pipefs system:object_r:system:low_s; +fs_use_trans devpts system:object_r:system:s0; +fs_use_xattr ext3 system:object_r:system:s0; +fs_use_task pipefs system:object_r:system:s0; #genfscon -genfscon proc / system:object_r:system:med -genfscon proc /sys system:object_r:system:low_s -genfscon selinuxfs / system:object_r:system:high_s:here.there +genfscon proc / system:object_r:system:s0 +genfscon proc /sys system:object_r:system:s0 +genfscon selinuxfs / system:object_r:system:s0 -portcon tcp 80 system:object_r:system:low_s +portcon tcp 80 system:object_r:system:s0 -netifcon eth0 system:object_r:system:low_s system:object_r:system:low_s - -nodecon 127.0.0.1 255.255.255.255 system:object_r:system:low_s:here -nodecon ::1 ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff system:object_r:system:low_s:here +netifcon eth0 system:object_r:system:s0 system:object_r:system:s0 +nodecon 127.0.0.1 255.255.255.255 system:object_r:system:s0 +nodecon ::1 ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff system:object_r:system:s0 diff --git a/tests/diff_right.conf b/tests/diff_right.conf index c174541..55802d5 100644 --- a/tests/diff_right.conf +++ b/tests/diff_right.conf @@ -92,20 +92,39 @@ class modified_remove_perm class modified_change_common inherits added_common -sensitivity low_s; -sensitivity medium_s alias med; -sensitivity high_s; +sensitivity s0; +sensitivity s1; +sensitivity s2; +sensitivity s3; +sensitivity s40; +sensitivity s41; +sensitivity s42; +sensitivity s43; +sensitivity s44; +sensitivity s45; +sensitivity s46; -dominance { low_s med high_s } +dominance { s0 s1 s2 s3 s40 s41 s42 s43 s44 s45 s46 } -category here; -category there; -category elsewhere alias lost; +category c0; +category c0a; +category c1; +category c2; +category c3; +category c4; #level decl -level low_s:here.there; -level med:here, elsewhere; -level high_s:here.lost; +level s0:c0.c4; +level s1:c0.c4; +level s2:c0.c4; +level s3:c0.c4; +level s40:c0.c4; +level s41:c0.c4; +level s42:c0.c4; +level s43:c0.c4; +level s44:c0.c4; +level s45:c0.c4; +level s46:c0.c4; #some constraints mlsconstrain infoflow hi_r ((l1 dom l2) or (t1 == mls_exempt)); @@ -486,32 +505,78 @@ type_member tm_unioned_perm_via_attr system:infoflow2 system; type_member tm_unioned_perm_via_attr_A_t system:infoflow2 system; type_member tm_unioned_perm_via_attr_B_t system:infoflow2 system; +# range_transition rule differences +type rt_matched_source; +type rt_matched_target; +range_transition rt_matched_source rt_matched_target:infoflow s0; + +type rt_removed_rule_source; +type rt_removed_rule_target; + +type rt_added_rule_source; +type rt_added_rule_target; +range_transition rt_added_rule_source rt_added_rule_target:infoflow s3; + +range_transition rt_matched_source system:infoflow s0:c0,c4 - s1:c0.c2,c4; + +range_transition added_type system:infoflow4 s3; + +# range transitions cannot be conditional. +#type rt_move_to_bool; +#bool rt_move_to_bool_b false; +#if (rt_move_to_bool_b) { +#range_transition rt_move_to_bool system:infoflow3 s0; +#} + +#type rt_move_from_bool; +#bool rt_move_from_bool_b false; +#range_transition rt_move_from_bool system:infoflow4 s0; + +#type rt_switch_block; +#bool rt_switch_block_b false; +#if (rt_switch_block_b) { +#range_transition system rt_switch_block:infoflow5 s0; +#} else { +#range_transition system rt_switch_block:infoflow6 s0; +#range_transition system rt_switch_block:infoflow7 s0; +#} + +attribute rt_match_rule_by_attr; +type rt_match_rule_by_attr_A_t, rt_match_rule_by_attr; +type rt_match_rule_by_attr_B_t, rt_match_rule_by_attr; +range_transition rt_match_rule_by_attr system:infoflow2 s0; + +attribute rt_unioned_perm_via_attr; +type rt_unioned_perm_via_attr_A_t, rt_unioned_perm_via_attr; +type rt_unioned_perm_via_attr_B_t, rt_unioned_perm_via_attr; +range_transition rt_unioned_perm_via_attr system:infoflow2 s0; +range_transition rt_unioned_perm_via_attr_B_t system:infoflow2 s0; + ################################################################################ #users -user system roles system level med range low_s - high_s:here.lost; +user system roles system level s0 range s0 - s46:c0.c4; #normal constraints constrain infoflow hi_w (u1 == u2); #isids -sid kernel system:system:system:medium_s:here -sid security system:system:system:high_s:lost +sid kernel system:system:system:s0 +sid security system:system:system:s0 #fs_use -fs_use_trans devpts system:object_r:system:low_s; -fs_use_xattr ext3 system:object_r:system:low_s; -fs_use_task pipefs system:object_r:system:low_s; +fs_use_trans devpts system:object_r:system:s0; +fs_use_xattr ext3 system:object_r:system:s0; +fs_use_task pipefs system:object_r:system:s0; #genfscon -genfscon proc / system:object_r:system:med -genfscon proc /sys system:object_r:system:low_s -genfscon selinuxfs / system:object_r:system:high_s:here.there +genfscon proc / system:object_r:system:s0 +genfscon proc /sys system:object_r:system:s0 +genfscon selinuxfs / system:object_r:system:s0 -portcon tcp 80 system:object_r:system:low_s +portcon tcp 80 system:object_r:system:s0 -netifcon eth0 system:object_r:system:low_s system:object_r:system:low_s - -nodecon 127.0.0.1 255.255.255.255 system:object_r:system:low_s:here -nodecon ::1 ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff system:object_r:system:low_s:here +netifcon eth0 system:object_r:system:s0 system:object_r:system:s0 +nodecon 127.0.0.1 255.255.255.255 system:object_r:system:s0 +nodecon ::1 ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff system:object_r:system:s0