PolicyDifference: implement typebounds diff.

Closes #67
This commit is contained in:
Chris PeBenito 2016-02-10 11:44:41 -05:00
parent 699ce33cd7
commit a4d4920d3c
9 changed files with 259 additions and 4 deletions

25
sediff
View File

@ -91,6 +91,7 @@ other.add_argument("--default", action="store_true", help="Print default_* diffe
other.add_argument("--property", action="store_true", other.add_argument("--property", action="store_true",
help="Print policy property differences (handle_unknown, version, MLS)") help="Print policy property differences (handle_unknown, version, MLS)")
other.add_argument("--polcap", action="store_true", help="Print policy capability differences") other.add_argument("--polcap", action="store_true", help="Print policy capability differences")
other.add_argument("--typebounds", action="store_true", help="Print typebounds differences")
args = parser.parse_args() args = parser.parse_args()
@ -101,7 +102,7 @@ all_differences = not any((args.class_, args.common, args.type_, args.attribute,
args.role_trans, args.range_trans, args.initialsid, args.genfscon, args.role_trans, args.range_trans, args.initialsid, args.genfscon,
args.netifcon, args.nodecon, args.portcon, args.fs_use, args.polcap, args.netifcon, args.nodecon, args.portcon, args.fs_use, args.polcap,
args.property, args.default, args.constrain, args.mlsconstrain, args.property, args.default, args.constrain, args.mlsconstrain,
args.validatetrans, args.mlsvalidatetrans)) args.validatetrans, args.mlsvalidatetrans, args.typebounds))
if args.debug: if args.debug:
logging.basicConfig(level=logging.DEBUG, logging.basicConfig(level=logging.DEBUG,
@ -1087,6 +1088,28 @@ try:
print() print()
if all_differences or args.typebounds:
if diff.added_typebounds or diff.removed_typebounds or args.typebounds:
print("Typebounds ({0} Added, {1} Removed, {2} Modified)".format(
len(diff.added_typebounds), len(diff.removed_typebounds),
len(diff.modified_typebounds)))
if diff.added_typebounds and not args.stats:
print(" Added Typebounds: {0}".format(len(diff.added_typebounds)))
for d in sorted(diff.added_typebounds):
print(" + {0}".format(d))
if diff.removed_typebounds and not args.stats:
print(" Removed Typebounds: {0}".format(len(diff.removed_typebounds)))
for d in sorted(diff.removed_typebounds):
print(" - {0}".format(d))
if diff.modified_typebounds and not args.stats:
print(" Modified Typebounds: {0}".format(len(diff.modified_typebounds)))
for bound, added_bound, removed_bound in sorted(
diff.modified_typebounds, key=lambda x: x.rule):
print(" * {0.ruletype} +{1} -{2} {0.child};".format(
bound, added_bound, removed_bound))
print()
except Exception as err: except Exception as err:
if args.debug: if args.debug:
import traceback import traceback

View File

@ -17,6 +17,7 @@
# <http://www.gnu.org/licenses/>. # <http://www.gnu.org/licenses/>.
# #
from .bool import BooleansDifference from .bool import BooleansDifference
from .bounds import BoundsDifference
from .commons import CommonDifference from .commons import CommonDifference
from .constraints import ConstraintsDifference from .constraints import ConstraintsDifference
from .default import DefaultsDifference from .default import DefaultsDifference
@ -42,6 +43,7 @@ __all__ = ['PolicyDifference']
class PolicyDifference(BooleansDifference, class PolicyDifference(BooleansDifference,
BoundsDifference,
CategoriesDifference, CategoriesDifference,
CommonDifference, CommonDifference,
ConstraintsDifference, ConstraintsDifference,

112
setools/diff/bounds.py Normal file
View File

@ -0,0 +1,112 @@
# 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
# <http://www.gnu.org/licenses/>.
#
from collections import namedtuple
from .descriptors import DiffResultDescriptor
from .difference import Difference, SymbolWrapper, Wrapper
modified_bounds_record = namedtuple("modified_bound", ["rule", "added_bound", "removed_bound"])
class BoundsDifference(Difference):
"""Determine the difference in *bounds between two policies."""
added_typebounds = DiffResultDescriptor("diff_typebounds")
removed_typebounds = DiffResultDescriptor("diff_typebounds")
modified_typebounds = DiffResultDescriptor("diff_typebounds")
# Lists of rules for each policy
_left_typebounds = None
_right_typebounds = None
def diff_typebounds(self):
"""Generate the difference in typebound rules between the policies."""
self.log.info("Generating typebounds differences from {0.left_policy} to {0.right_policy}".
format(self))
if self._left_typebounds is None or self._right_typebounds is None:
self._create_typebound_lists()
self.added_typebounds, self.removed_typebounds, matched_typebounds = self._set_diff(
(BoundsWrapper(c) for c in self._left_typebounds),
(BoundsWrapper(c) for c in self._right_typebounds),
key=lambda b: str(b.child))
self.modified_typebounds = []
for left_bound, right_bound in matched_typebounds:
if SymbolWrapper(left_bound.parent) != SymbolWrapper(right_bound.parent):
self.modified_typebounds.append(modified_bounds_record(
left_bound, right_bound.parent, left_bound.parent))
#
# Internal functions
#
def _create_typebound_lists(self):
"""Create rule lists for both policies."""
self._left_typebounds = []
for rule in self.left_policy.bounds():
if rule.ruletype == "typebounds":
self._left_typebounds.append(rule)
else:
self.log.error("Unknown rule type: {0} (This is an SETools bug)".
format(rule.ruletype))
self._right_typebounds = []
for rule in self.right_policy.bounds():
if rule.ruletype == "typebounds":
self._right_typebounds.append(rule)
else:
self.log.error("Unknown rule type: {0} (This is an SETools bug)".
format(rule.ruletype))
def _reset_diff(self):
"""Reset diff results on policy changes."""
self.log.debug("Resetting all *bounds differences")
self.added_typebounds = None
self.removed_typebounds = None
# Sets of rules for each policy
self._left_typebounds = None
self._right_typebounds = None
class BoundsWrapper(Wrapper):
"""Wrap *bounds for diff purposes."""
def __init__(self, rule):
self.origin = rule
self.ruletype = rule.ruletype
self.parent = SymbolWrapper(rule.parent)
self.child = SymbolWrapper(rule.child)
self.key = hash(rule)
def __hash__(self):
return self.key
def __lt__(self, other):
return self.key < other.key
def __eq__(self, other):
return self.ruletype == other.ruletype and \
self.child == other.child

View File

@ -72,7 +72,7 @@ class Difference(object):
yield Wrapper(expanded_rule) yield Wrapper(expanded_rule)
@staticmethod @staticmethod
def _set_diff(left, right): def _set_diff(left, right, key=None):
""" """
Standard diff of two sets. Standard diff of two sets.
@ -108,8 +108,8 @@ class Difference(object):
# instead of giving wrong results. If there is a better way to, # instead of giving wrong results. If there is a better way to,
# ensure the items match up, please let me know how or submit a patch. # ensure the items match up, please let me know how or submit a patch.
matched_items = set() matched_items = set()
left_matched_items = sorted((left_items - removed_items)) left_matched_items = sorted((left_items - removed_items), key=key)
right_matched_items = sorted((right_items - added_items)) right_matched_items = sorted((right_items - added_items), key=key)
assert len(left_matched_items) == len(right_matched_items), \ assert len(left_matched_items) == len(right_matched_items), \
"Matched items assertion failure (this is an SETools bug), {0} != {1}". \ "Matched items assertion failure (this is an SETools bug), {0} != {1}". \
format(len(left_matched_items), len(right_matched_items)) format(len(left_matched_items), len(right_matched_items))

View File

@ -37,6 +37,9 @@ class Bounds(PolicySymbol):
def __str__(self): def __str__(self):
return "{0.ruletype} {0.parent} {0.child};".format(self) return "{0.ruletype} {0.parent} {0.child};".format(self)
def __hash__(self):
return hash("{0.ruletype}|{0.child};".format(self))
ruletype = "typebounds" ruletype = "typebounds"
@property @property

View File

@ -1521,6 +1521,40 @@ class PolicyDifferenceTest(ValidateRule, unittest.TestCase):
['l1', 'l2', 'dom', 'h1', 'h2', 'dom', 'and', 't3', set(["mls_exempt"]), '==', 'or'], ['l1', 'l2', 'dom', 'h1', 'h2', 'dom', 'and', 't3', set(["mls_exempt"]), '==', 'or'],
mlsvalidatetrans.postfix_expression()) mlsvalidatetrans.postfix_expression())
#
# typebounds
#
def test_added_typebounds(self):
"""Diff: added typebounds."""
l = sorted(self.diff.added_typebounds)
self.assertEqual(1, len(l))
bounds = l[0]
self.assertEqual("typebounds", bounds.ruletype)
self.assertEqual("added_parent", bounds.parent)
self.assertEqual("added_child", bounds.child)
def test_removed_typebounds(self):
"""Diff: removed typebounds."""
l = sorted(self.diff.removed_typebounds)
self.assertEqual(1, len(l))
bounds = l[0]
self.assertEqual("typebounds", bounds.ruletype)
self.assertEqual("removed_parent", bounds.parent)
self.assertEqual("removed_child", bounds.child)
def test_modified_typebounds(self):
"""Diff: modified typebounds."""
l = sorted(self.diff.modified_typebounds, key=lambda x: x.rule)
self.assertEqual(1, len(l))
bounds, added_bound, removed_bound = l[0]
self.assertEqual("typebounds", bounds.ruletype)
self.assertEqual("mod_child", bounds.child)
self.assertEqual("mod_parent_added", added_bound)
self.assertEqual("mod_parent_removed", removed_bound)
class PolicyDifferenceTestNoDiff(unittest.TestCase): class PolicyDifferenceTestNoDiff(unittest.TestCase):
@ -1899,6 +1933,18 @@ class PolicyDifferenceTestNoDiff(unittest.TestCase):
"""NoDiff: no removed mlsvalidatetrans.""" """NoDiff: no removed mlsvalidatetrans."""
self.assertFalse(self.diff.removed_mlsvalidatetrans) self.assertFalse(self.diff.removed_mlsvalidatetrans)
def test_added_typebounds(self):
"""NoDiff: no added typebounds."""
self.assertFalse(self.diff.added_typebounds)
def test_removed_typebounds(self):
"""NoDiff: no removed typebounds."""
self.assertFalse(self.diff.removed_typebounds)
def test_modified_typebounds(self):
"""NoDiff: no modified typebounds."""
self.assertFalse(self.diff.modified_typebounds)
class PolicyDifferenceTestMLStoStandard(unittest.TestCase): class PolicyDifferenceTestMLStoStandard(unittest.TestCase):
@ -2294,3 +2340,15 @@ class PolicyDifferenceTestMLStoStandard(unittest.TestCase):
self.assertEqual( self.assertEqual(
sum(1 for m in self.diff.left_policy.constraints() if m.ruletype == "mlsvalidatetrans"), sum(1 for m in self.diff.left_policy.constraints() if m.ruletype == "mlsvalidatetrans"),
len(self.diff.removed_mlsvalidatetrans)) len(self.diff.removed_mlsvalidatetrans))
def test_added_typebounds(self):
"""MLSvsStandardDiff: no added typebounds."""
self.assertFalse(self.diff.added_typebounds)
def test_removed_typebounds(self):
"""MLSvsStandardDiff: no removed typebounds."""
self.assertFalse(self.diff.removed_typebounds)
def test_modified_typebounds(self):
"""MLSvsStandardDiff: no modified typebounds."""
self.assertFalse(self.diff.modified_typebounds)

View File

@ -635,6 +635,25 @@ role role_tr_new_role;
role_transition role_tr_matched_source role_tr_matched_target:infoflow3 role_tr_old_role; role_transition role_tr_matched_source role_tr_matched_target:infoflow3 role_tr_old_role;
################################################################################ ################################################################################
# matching typebounds
type match_parent;
type match_child;
typebounds match_parent match_child;
# removed typebounds
type removed_parent;
type removed_child;
typebounds removed_parent removed_child;
# added typebounds
type added_parent;
type added_child;
# modified typebounds
type mod_parent_removed;
type mod_parent_added;
type mod_child;
typebounds mod_parent_removed mod_child;
# policycaps # policycaps
policycap open_perms; policycap open_perms;

View File

@ -557,6 +557,25 @@ role role_tr_new_role;
role_transition role_tr_matched_source role_tr_matched_target:infoflow3 role_tr_old_role; role_transition role_tr_matched_source role_tr_matched_target:infoflow3 role_tr_old_role;
################################################################################ ################################################################################
# matching typebounds
type match_parent;
type match_child;
typebounds match_parent match_child;
# removed typebounds
type removed_parent;
type removed_child;
typebounds removed_parent removed_child;
# added typebounds
type added_parent;
type added_child;
# modified typebounds
type mod_parent_removed;
type mod_parent_added;
type mod_child;
typebounds mod_parent_removed mod_child;
# policycaps # policycaps
policycap open_perms; policycap open_perms;

View File

@ -635,6 +635,25 @@ role role_tr_new_role;
role_transition role_tr_matched_source role_tr_matched_target:infoflow3 role_tr_new_role; role_transition role_tr_matched_source role_tr_matched_target:infoflow3 role_tr_new_role;
################################################################################ ################################################################################
# matching typebounds
type match_parent;
type match_child;
typebounds match_parent match_child;
# removed typebounds
type removed_parent;
type removed_child;
# added typebounds
type added_parent;
type added_child;
typebounds added_parent added_child;
# modified typebounds
type mod_parent_removed;
type mod_parent_added;
type mod_child;
typebounds mod_parent_added mod_child;
# policycaps # policycaps
policycap open_perms; policycap open_perms;