Home | History | Annotate | Download | only in diff
      1 # Copyright 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 namedtuple
     20 
     21 from .descriptors import DiffResultDescriptor
     22 from .difference import Difference, SymbolWrapper, Wrapper
     23 
     24 modified_cat_record = namedtuple("modified_category", ["added_aliases",
     25                                                        "removed_aliases",
     26                                                        "matched_aliases"])
     27 
     28 modified_sens_record = namedtuple("modified_sensitivity", ["added_aliases",
     29                                                            "removed_aliases",
     30                                                            "matched_aliases"])
     31 
     32 modified_level_record = namedtuple("modified_level", ["level",
     33                                                       "added_categories",
     34                                                       "removed_categories",
     35                                                       "matched_categories"])
     36 
     37 
     38 class CategoriesDifference(Difference):
     39 
     40     """Determine the difference in categories between two policies."""
     41 
     42     added_categories = DiffResultDescriptor("diff_categories")
     43     removed_categories = DiffResultDescriptor("diff_categories")
     44     modified_categories = DiffResultDescriptor("diff_categories")
     45 
     46     def diff_categories(self):
     47         """Generate the difference in categories between the policies."""
     48 
     49         self.log.info(
     50             "Generating category differences from {0.left_policy} to {0.right_policy}".format(self))
     51 
     52         self.added_categories, self.removed_categories, matched_categories = self._set_diff(
     53             (SymbolWrapper(c) for c in self.left_policy.categories()),
     54             (SymbolWrapper(c) for c in self.right_policy.categories()))
     55 
     56         self.modified_categories = dict()
     57 
     58         for left_category, right_category in matched_categories:
     59             # Criteria for modified categories
     60             # 1. change to aliases
     61             added_aliases, removed_aliases, matched_aliases = self._set_diff(
     62                 left_category.aliases(), right_category.aliases())
     63 
     64             if added_aliases or removed_aliases:
     65                 self.modified_categories[left_category] = modified_cat_record(added_aliases,
     66                                                                               removed_aliases,
     67                                                                               matched_aliases)
     68 
     69     #
     70     # Internal functions
     71     #
     72     def _reset_diff(self):
     73         """Reset diff results on policy changes."""
     74         self.log.debug("Resetting category differences")
     75         self.added_categories = None
     76         self.removed_categories = None
     77         self.modified_categories = None
     78 
     79 
     80 class SensitivitiesDifference(Difference):
     81 
     82     """Determine the difference in sensitivities between two policies."""
     83 
     84     added_sensitivities = DiffResultDescriptor("diff_sensitivities")
     85     removed_sensitivities = DiffResultDescriptor("diff_sensitivities")
     86     modified_sensitivities = DiffResultDescriptor("diff_sensitivities")
     87 
     88     def diff_sensitivities(self):
     89         """Generate the difference in sensitivities between the policies."""
     90 
     91         self.log.info(
     92             "Generating sensitivity differences from {0.left_policy} to {0.right_policy}".
     93             format(self))
     94 
     95         self.added_sensitivities, self.removed_sensitivities, matched_sensitivities = \
     96             self._set_diff(
     97                 (SymbolWrapper(s) for s in self.left_policy.sensitivities()),
     98                 (SymbolWrapper(s) for s in self.right_policy.sensitivities()))
     99 
    100         self.modified_sensitivities = dict()
    101 
    102         for left_sens, right_sens in matched_sensitivities:
    103             # Criteria for modified sensitivities
    104             # 1. change to aliases
    105             added_aliases, removed_aliases, matched_aliases = self._set_diff(
    106                 left_sens.aliases(), right_sens.aliases())
    107 
    108             if added_aliases or removed_aliases:
    109                 self.modified_sensitivities[left_sens] = modified_sens_record(added_aliases,
    110                                                                               removed_aliases,
    111                                                                               matched_aliases)
    112 
    113     #
    114     # Internal functions
    115     #
    116     def _reset_diff(self):
    117         """Reset diff results on policy changes."""
    118         self.log.debug("Resetting sensitivity differences")
    119         self.added_sensitivities = None
    120         self.removed_sensitivities = None
    121         self.modified_sensitivities = None
    122 
    123 
    124 class LevelDeclsDifference(Difference):
    125 
    126     """Determine the difference in levels between two policies."""
    127 
    128     added_levels = DiffResultDescriptor("diff_levels")
    129     removed_levels = DiffResultDescriptor("diff_levels")
    130     modified_levels = DiffResultDescriptor("diff_levels")
    131 
    132     def diff_levels(self):
    133         """Generate the difference in levels between the policies."""
    134 
    135         self.log.info(
    136             "Generating level decl differences from {0.left_policy} to {0.right_policy}".
    137             format(self))
    138 
    139         self.added_levels, self.removed_levels, matched_levels = \
    140             self._set_diff(
    141                 (LevelDeclWrapper(s) for s in self.left_policy.levels()),
    142                 (LevelDeclWrapper(s) for s in self.right_policy.levels()))
    143 
    144         self.modified_levels = []
    145 
    146         for left_level, right_level in matched_levels:
    147             # Criteria for modified levels
    148             # 1. change to allowed categories
    149             added_categories, removed_categories, matched_categories = self._set_diff(
    150                 (SymbolWrapper(c) for c in left_level.categories()),
    151                 (SymbolWrapper(c) for c in right_level.categories()))
    152 
    153             if added_categories or removed_categories:
    154                 self.modified_levels.append(modified_level_record(
    155                     left_level, added_categories, removed_categories, matched_categories))
    156 
    157     #
    158     # Internal functions
    159     #
    160     def _reset_diff(self):
    161         """Reset diff results on policy changes."""
    162         self.log.debug("Resetting sensitivity differences")
    163         self.added_levels = None
    164         self.removed_levels = None
    165         self.modified_levels = None
    166 
    167 
    168 class LevelDeclWrapper(Wrapper):
    169 
    170     """Wrap level declarations to allow comparisons."""
    171 
    172     def __init__(self, level):
    173         self.origin = level
    174         self.sensitivity = SymbolWrapper(level.sensitivity)
    175         self.key = hash(level)
    176 
    177     def __hash__(self):
    178         return self.key
    179 
    180     def __eq__(self, other):
    181         # non-MLS policies have no level declarations so there
    182         # should be no AttributeError possiblity here
    183         return self.sensitivity == other.sensitivity
    184 
    185     def __lt__(self, other):
    186         return self.sensitivity < other.sensitivity
    187 
    188 
    189 class LevelWrapper(Wrapper):
    190 
    191     """Wrap levels to allow comparisons."""
    192 
    193     def __init__(self, level):
    194         self.origin = level
    195         self.sensitivity = SymbolWrapper(level.sensitivity)
    196         self.categories = set(SymbolWrapper(c) for c in level.categories())
    197 
    198     def __eq__(self, other):
    199         try:
    200             return self.sensitivity == other.sensitivity and \
    201                    self.categories == other.categories
    202         except AttributeError:
    203             # comparing an MLS policy to non-MLS policy will result in
    204             # other being None
    205             return False
    206 
    207 
    208 class RangeWrapper(Wrapper):
    209 
    210     """
    211     Wrap ranges to allow comparisons.
    212 
    213     This only compares the low and high levels of the range.
    214     It does not detect additions/removals/modifications
    215     to levels between the low and high levels of the range.
    216     """
    217 
    218     def __init__(self, range_):
    219         self.origin = range_
    220         self.low = LevelWrapper(range_.low)
    221         self.high = LevelWrapper(range_.high)
    222 
    223     def __eq__(self, other):
    224         try:
    225             return self.low == other.low and \
    226                    self.high == other.high
    227         except AttributeError:
    228             # comparing an MLS policy to non-MLS policy will result in
    229             # other being None
    230             return False
    231