Home | History | Annotate | Download | only in diff
      1 # Copyright 2015-2016, Tresys Technology, LLC
      2 #
      3 # This file is part of SETools.
      4 #
      5 # SETools is free software: you can redistribute it and/or modify
      6 # it under the terms of the GNU Lesser General Public License as
      7 # published by the Free Software Foundation, either version 2.1 of
      8 # the License, or (at your option) any later version.
      9 #
     10 # SETools is distributed in the hope that it will be useful,
     11 # but WITHOUT ANY WARRANTY; without even the implied warranty of
     12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     13 # GNU Lesser General Public License for more details.
     14 #
     15 # You should have received a copy of the GNU Lesser General Public
     16 # License along with SETools.  If not, see
     17 # <http://www.gnu.org/licenses/>.
     18 #
     19 from collections import defaultdict, namedtuple
     20 
     21 from ..policyrep import ioctlSet
     22 from ..policyrep.exception import RuleNotConditional, RuleUseError, TERuleNoFilename
     23 
     24 from .conditional import ConditionalExprWrapper
     25 from .descriptors import DiffResultDescriptor
     26 from .difference import Difference, SymbolWrapper, Wrapper
     27 
     28 
     29 modified_avrule_record = namedtuple("modified_avrule", ["rule",
     30                                                         "added_perms",
     31                                                         "removed_perms",
     32                                                         "matched_perms"])
     33 
     34 modified_terule_record = namedtuple("modified_terule", ["rule", "added_default", "removed_default"])
     35 
     36 
     37 def av_diff_template(ruletype):
     38 
     39     """
     40     This is a template for the access vector diff functions.
     41 
     42     Parameters:
     43     ruletype    The rule type, e.g. "allow".
     44     """
     45 
     46     def diff(self):
     47         """Generate the difference in rules between the policies."""
     48 
     49         self.log.info(
     50             "Generating {0} differences from {1.left_policy} to {1.right_policy}".
     51             format(ruletype, self))
     52 
     53         if not self._left_te_rules or not self._right_te_rules:
     54             self._create_te_rule_lists()
     55 
     56         added, removed, matched = self._set_diff(
     57                 self._expand_generator(self._left_te_rules[ruletype], AVRuleWrapper),
     58                 self._expand_generator(self._right_te_rules[ruletype], AVRuleWrapper))
     59 
     60         modified = []
     61         for left_rule, right_rule in matched:
     62             # Criteria for modified rules
     63             # 1. change to permissions
     64             added_perms, removed_perms, matched_perms = self._set_diff(left_rule.perms,
     65                                                                        right_rule.perms)
     66 
     67             # the final set comprehension is to avoid having lists
     68             # like [("perm1", "perm1"), ("perm2", "perm2")], as the
     69             # matched_perms return from _set_diff is a set of tuples
     70             if added_perms or removed_perms:
     71                 modified.append(modified_avrule_record(left_rule,
     72                                                        added_perms,
     73                                                        removed_perms,
     74                                                        set(p[0] for p in matched_perms)))
     75 
     76         setattr(self, "added_{0}s".format(ruletype), added)
     77         setattr(self, "removed_{0}s".format(ruletype), removed)
     78         setattr(self, "modified_{0}s".format(ruletype), modified)
     79 
     80     return diff
     81 
     82 
     83 def avx_diff_template(ruletype):
     84 
     85     """
     86     This is a template for the extended permission access vector diff functions.
     87 
     88     Parameters:
     89     ruletype    The rule type, e.g. "allowxperm".
     90     """
     91 
     92     def diff(self):
     93         """Generate the difference in rules between the policies."""
     94 
     95         self.log.info(
     96             "Generating {0} differences from {1.left_policy} to {1.right_policy}".
     97             format(ruletype, self))
     98 
     99         if not self._left_te_rules or not self._right_te_rules:
    100             self._create_te_rule_lists()
    101 
    102         added, removed, matched = self._set_diff(
    103                 self._expand_generator(self._left_te_rules[ruletype], AVRuleXpermWrapper),
    104                 self._expand_generator(self._right_te_rules[ruletype], AVRuleXpermWrapper))
    105 
    106         modified = []
    107         for left_rule, right_rule in matched:
    108             # Criteria for modified rules
    109             # 1. change to permissions
    110             added_perms, removed_perms, matched_perms = self._set_diff(left_rule.perms,
    111                                                                        right_rule.perms)
    112 
    113             # the final set comprehension is to avoid having lists
    114             # like [("perm1", "perm1"), ("perm2", "perm2")], as the
    115             # matched_perms return from _set_diff is a set of tuples
    116             if added_perms or removed_perms:
    117                 modified.append(modified_avrule_record(left_rule,
    118                                                        ioctlSet(added_perms),
    119                                                        ioctlSet(removed_perms),
    120                                                        ioctlSet(p[0] for p in matched_perms)))
    121 
    122         setattr(self, "added_{0}s".format(ruletype), added)
    123         setattr(self, "removed_{0}s".format(ruletype), removed)
    124         setattr(self, "modified_{0}s".format(ruletype), modified)
    125 
    126     return diff
    127 
    128 
    129 def te_diff_template(ruletype):
    130 
    131     """
    132     This is a template for the type_* diff functions.
    133 
    134     Parameters:
    135     ruletype    The rule type, e.g. "type_transition".
    136     """
    137 
    138     def diff(self):
    139         """Generate the difference in rules between the policies."""
    140 
    141         self.log.info(
    142             "Generating {0} differences from {1.left_policy} to {1.right_policy}".
    143             format(ruletype, self))
    144 
    145         if not self._left_te_rules or not self._right_te_rules:
    146             self._create_te_rule_lists()
    147 
    148         added, removed, matched = self._set_diff(
    149                 self._expand_generator(self._left_te_rules[ruletype], TERuleWrapper),
    150                 self._expand_generator(self._right_te_rules[ruletype], TERuleWrapper))
    151 
    152         modified = []
    153         for left_rule, right_rule in matched:
    154             # Criteria for modified rules
    155             # 1. change to default type
    156             if SymbolWrapper(left_rule.default) != SymbolWrapper(right_rule.default):
    157                 modified.append(modified_terule_record(left_rule,
    158                                                        right_rule.default,
    159                                                        left_rule.default))
    160 
    161         setattr(self, "added_{0}s".format(ruletype), added)
    162         setattr(self, "removed_{0}s".format(ruletype), removed)
    163         setattr(self, "modified_{0}s".format(ruletype), modified)
    164 
    165     return diff
    166 
    167 
    168 class TERulesDifference(Difference):
    169 
    170     """
    171     Determine the difference in type enforcement rules
    172     between two policies.
    173     """
    174 
    175     diff_allows = av_diff_template("allow")
    176     added_allows = DiffResultDescriptor("diff_allows")
    177     removed_allows = DiffResultDescriptor("diff_allows")
    178     modified_allows = DiffResultDescriptor("diff_allows")
    179 
    180     diff_auditallows = av_diff_template("auditallow")
    181     added_auditallows = DiffResultDescriptor("diff_auditallows")
    182     removed_auditallows = DiffResultDescriptor("diff_auditallows")
    183     modified_auditallows = DiffResultDescriptor("diff_auditallows")
    184 
    185     diff_neverallows = av_diff_template("neverallow")
    186     added_neverallows = DiffResultDescriptor("diff_neverallows")
    187     removed_neverallows = DiffResultDescriptor("diff_neverallows")
    188     modified_neverallows = DiffResultDescriptor("diff_neverallows")
    189 
    190     diff_dontaudits = av_diff_template("dontaudit")
    191     added_dontaudits = DiffResultDescriptor("diff_dontaudits")
    192     removed_dontaudits = DiffResultDescriptor("diff_dontaudits")
    193     modified_dontaudits = DiffResultDescriptor("diff_dontaudits")
    194 
    195     diff_allowxperms = avx_diff_template("allowxperm")
    196     added_allowxperms = DiffResultDescriptor("diff_allowxperms")
    197     removed_allowxperms = DiffResultDescriptor("diff_allowxperms")
    198     modified_allowxperms = DiffResultDescriptor("diff_allowxperms")
    199 
    200     diff_auditallowxperms = avx_diff_template("auditallowxperm")
    201     added_auditallowxperms = DiffResultDescriptor("diff_auditallowxperms")
    202     removed_auditallowxperms = DiffResultDescriptor("diff_auditallowxperms")
    203     modified_auditallowxperms = DiffResultDescriptor("diff_auditallowxperms")
    204 
    205     diff_neverallowxperms = avx_diff_template("neverallowxperm")
    206     added_neverallowxperms = DiffResultDescriptor("diff_neverallowxperms")
    207     removed_neverallowxperms = DiffResultDescriptor("diff_neverallowxperms")
    208     modified_neverallowxperms = DiffResultDescriptor("diff_neverallowxperms")
    209 
    210     diff_dontauditxperms = avx_diff_template("dontauditxperm")
    211     added_dontauditxperms = DiffResultDescriptor("diff_dontauditxperms")
    212     removed_dontauditxperms = DiffResultDescriptor("diff_dontauditxperms")
    213     modified_dontauditxperms = DiffResultDescriptor("diff_dontauditxperms")
    214 
    215     diff_type_transitions = te_diff_template("type_transition")
    216     added_type_transitions = DiffResultDescriptor("diff_type_transitions")
    217     removed_type_transitions = DiffResultDescriptor("diff_type_transitions")
    218     modified_type_transitions = DiffResultDescriptor("diff_type_transitions")
    219 
    220     diff_type_changes = te_diff_template("type_change")
    221     added_type_changes = DiffResultDescriptor("diff_type_changes")
    222     removed_type_changes = DiffResultDescriptor("diff_type_changes")
    223     modified_type_changes = DiffResultDescriptor("diff_type_changes")
    224 
    225     diff_type_members = te_diff_template("type_member")
    226     added_type_members = DiffResultDescriptor("diff_type_members")
    227     removed_type_members = DiffResultDescriptor("diff_type_members")
    228     modified_type_members = DiffResultDescriptor("diff_type_members")
    229 
    230     # Lists of rules for each policy
    231     _left_te_rules = defaultdict(list)
    232     _right_te_rules = defaultdict(list)
    233 
    234     #
    235     # Internal functions
    236     #
    237     def _create_te_rule_lists(self):
    238         """Create rule lists for both policies."""
    239         # do not expand yet, to keep memory
    240         # use down as long as possible
    241         self.log.debug("Building TE rule lists from {0.left_policy}".format(self))
    242         for rule in self.left_policy.terules():
    243             self._left_te_rules[rule.ruletype].append(rule)
    244 
    245         self.log.debug("Building TE rule lists from {0.right_policy}".format(self))
    246         for rule in self.right_policy.terules():
    247             self._right_te_rules[rule.ruletype].append(rule)
    248 
    249         self.log.debug("Completed building TE rule lists.")
    250 
    251     def _reset_diff(self):
    252         """Reset diff results on policy changes."""
    253         self.log.debug("Resetting TE rule differences")
    254         self.added_allows = None
    255         self.removed_allows = None
    256         self.modified_allows = None
    257         self.added_auditallows = None
    258         self.removed_auditallows = None
    259         self.modified_auditallows = None
    260         self.added_neverallows = None
    261         self.removed_neverallows = None
    262         self.modified_neverallows = None
    263         self.added_dontaudits = None
    264         self.removed_dontaudits = None
    265         self.modified_dontaudits = None
    266         self.added_allowxperms = None
    267         self.removed_allowxperms = None
    268         self.modified_allowxperms = None
    269         self.added_auditallowxperms = None
    270         self.removed_auditallowxperms = None
    271         self.modified_auditallowxperms = None
    272         self.added_neverallowxperms = None
    273         self.removed_neverallowxperms = None
    274         self.modified_neverallowxperms = None
    275         self.added_dontauditxperms = None
    276         self.removed_dontauditxperms = None
    277         self.modified_dontauditxperms = None
    278         self.added_type_transitions = None
    279         self.removed_type_transitions = None
    280         self.modified_type_transitions = None
    281         self.added_type_changes = None
    282         self.removed_type_changes = None
    283         self.modified_type_changes = None
    284         self.added_type_members = None
    285         self.removed_type_members = None
    286         self.modified_type_members = None
    287 
    288         # Sets of rules for each policy
    289         self._left_te_rules.clear()
    290         self._right_te_rules.clear()
    291 
    292 
    293 class AVRuleWrapper(Wrapper):
    294 
    295     """Wrap access vector rules to allow set operations."""
    296 
    297     def __init__(self, rule):
    298         self.origin = rule
    299         self.ruletype = rule.ruletype
    300         self.source = SymbolWrapper(rule.source)
    301         self.target = SymbolWrapper(rule.target)
    302         self.tclass = SymbolWrapper(rule.tclass)
    303         self.key = hash(rule)
    304 
    305         try:
    306             self.conditional = ConditionalExprWrapper(rule.conditional)
    307             self.conditional_block = rule.conditional_block
    308         except RuleNotConditional:
    309             self.conditional = None
    310             self.conditional_block = None
    311 
    312     def __hash__(self):
    313         return self.key
    314 
    315     def __lt__(self, other):
    316         return self.key < other.key
    317 
    318     def __eq__(self, other):
    319         # because TERuleDifference groups rules by ruletype,
    320         # the ruletype always matches.
    321         return self.source == other.source and \
    322                self.target == other.target and \
    323                self.tclass == other.tclass and \
    324                self.conditional == other.conditional and \
    325                self.conditional_block == other.conditional_block
    326 
    327 
    328 class AVRuleXpermWrapper(Wrapper):
    329 
    330     """Wrap extended permission access vector rules to allow set operations."""
    331 
    332     def __init__(self, rule):
    333         self.origin = rule
    334         self.ruletype = rule.ruletype
    335         self.source = SymbolWrapper(rule.source)
    336         self.target = SymbolWrapper(rule.target)
    337         self.tclass = SymbolWrapper(rule.tclass)
    338         self.xperm_type = rule.xperm_type
    339         self.key = hash(rule)
    340 
    341     def __hash__(self):
    342         return self.key
    343 
    344     def __lt__(self, other):
    345         return self.key < other.key
    346 
    347     def __eq__(self, other):
    348         # because TERuleDifference groups rules by ruletype,
    349         # the ruletype always matches.
    350         return self.source == other.source and \
    351                self.target == other.target and \
    352                self.tclass == other.tclass and \
    353                self.xperm_type == other.xperm_type
    354 
    355 
    356 class TERuleWrapper(Wrapper):
    357 
    358     """Wrap type_* rules to allow set operations."""
    359 
    360     def __init__(self, rule):
    361         self.origin = rule
    362         self.ruletype = rule.ruletype
    363         self.source = SymbolWrapper(rule.source)
    364         self.target = SymbolWrapper(rule.target)
    365         self.tclass = SymbolWrapper(rule.tclass)
    366         self.key = hash(rule)
    367 
    368         try:
    369             self.conditional = ConditionalExprWrapper(rule.conditional)
    370             self.conditional_block = rule.conditional_block
    371         except RuleNotConditional:
    372             self.conditional = None
    373             self.conditional_block = None
    374 
    375         try:
    376             self.filename = rule.filename
    377         except (RuleUseError, TERuleNoFilename):
    378             self.filename = None
    379 
    380     def __hash__(self):
    381         return self.key
    382 
    383     def __lt__(self, other):
    384         return self.key < other.key
    385 
    386     def __eq__(self, other):
    387         # because TERuleDifference groups rules by ruletype,
    388         # the ruletype always matches.
    389         return self.source == other.source and \
    390                self.target == other.target and \
    391                self.tclass == other.tclass and \
    392                self.conditional == other.conditional and \
    393                self.conditional_block == other.conditional_block and \
    394                self.filename == self.filename
    395