mirror of
https://github.com/SELinuxProject/setools
synced 2025-03-25 04:26:28 +00:00
setools: add PolicyDifference class
Begin diff functions by implementing type difference function. Closes #32
This commit is contained in:
parent
3f7b428b70
commit
eae9ff97d5
@ -65,3 +65,6 @@ from .permmap import PermissionMap
|
||||
|
||||
# Domain Transition Analysis
|
||||
from .dta import DomainTransitionAnalysis
|
||||
|
||||
# Policy difference
|
||||
from .diff import PolicyDifference
|
||||
|
183
setools/diff.py
Normal file
183
setools/diff.py
Normal file
@ -0,0 +1,183 @@
|
||||
# Copyright 2015, 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/>.
|
||||
#
|
||||
import logging
|
||||
from collections import namedtuple
|
||||
from weakref import WeakKeyDictionary
|
||||
|
||||
__all__ = ['PolicyDifference']
|
||||
|
||||
|
||||
modified_types_record = namedtuple("modified_type", ["added_attributes",
|
||||
"removed_attributes",
|
||||
"matched_attributes",
|
||||
"modified_permissive",
|
||||
"permissive",
|
||||
"added_aliases",
|
||||
"removed_aliases",
|
||||
"matched_aliases"])
|
||||
|
||||
|
||||
class DiffResultDescriptor(object):
|
||||
|
||||
"""Descriptor for managing diff results."""
|
||||
|
||||
# @properties could be used instead, but there are so
|
||||
# many result attributes, this will keep the code more compact.
|
||||
|
||||
def __init__(self, diff_function):
|
||||
self.diff_function = diff_function
|
||||
|
||||
# use weak references so instances can be
|
||||
# garbage collected, rather than unnecessarily
|
||||
# kept around due to this descriptor.
|
||||
self.instances = WeakKeyDictionary()
|
||||
|
||||
def __get__(self, obj, objtype=None):
|
||||
if obj is None:
|
||||
return self
|
||||
|
||||
if self.instances.setdefault(obj, None) is None:
|
||||
diff = getattr(obj, self.diff_function)
|
||||
diff()
|
||||
|
||||
return self.instances[obj]
|
||||
|
||||
def __set__(self, obj, value):
|
||||
self.instances[obj] = value
|
||||
|
||||
|
||||
class PolicyDifference(object):
|
||||
|
||||
"""
|
||||
Determine the differences between two policies.
|
||||
|
||||
All results are represented as str rather than the
|
||||
original Python objects. This was done because the
|
||||
source of the object becomes less clear (Python
|
||||
set logic doesn't have any guarantees for set
|
||||
intersection). Using str will prevent problems
|
||||
if you expect to be using a symbol but it is
|
||||
coming from the wrong policy.
|
||||
"""
|
||||
|
||||
def __init__(self, left_policy, right_policy):
|
||||
self.log = logging.getLogger(self.__class__.__name__)
|
||||
self.left_policy = left_policy
|
||||
self.right_policy = right_policy
|
||||
|
||||
#
|
||||
# Policies to compare
|
||||
#
|
||||
@property
|
||||
def left_policy(self):
|
||||
return self._left_policy
|
||||
|
||||
@left_policy.setter
|
||||
def left_policy(self, policy):
|
||||
self._left_policy = policy
|
||||
self._reset_diff()
|
||||
|
||||
@property
|
||||
def right_policy(self):
|
||||
return self._right_policy
|
||||
|
||||
@right_policy.setter
|
||||
def right_policy(self, policy):
|
||||
self._right_policy = policy
|
||||
self._reset_diff()
|
||||
|
||||
#
|
||||
# Type differences
|
||||
#
|
||||
added_types = DiffResultDescriptor("diff_types")
|
||||
removed_types = DiffResultDescriptor("diff_types")
|
||||
modified_types = DiffResultDescriptor("diff_types")
|
||||
|
||||
def diff_types(self):
|
||||
"""Generate the difference in types between the policies."""
|
||||
|
||||
self.log.info(
|
||||
"Generating type differences from {0.left_policy} to {0.right_policy}".format(self))
|
||||
|
||||
self.added_types, self.removed_types, matched_types = self._set_diff(
|
||||
self.left_policy.types(), self.right_policy.types())
|
||||
|
||||
self.modified_types = dict()
|
||||
|
||||
for name in matched_types:
|
||||
# Criteria for modified types
|
||||
# 1. change to attribute set, or
|
||||
# 2. change to alias set, or
|
||||
# 3. different permissive setting
|
||||
left_type = self.left_policy.lookup_type(name)
|
||||
right_type = self.right_policy.lookup_type(name)
|
||||
|
||||
added_attr, removed_attr, matched_attr = self._set_diff(left_type.attributes(),
|
||||
right_type.attributes())
|
||||
|
||||
added_aliases, removed_aliases, matched_aliases = self._set_diff(left_type.aliases(),
|
||||
right_type.aliases())
|
||||
|
||||
left_permissive = left_type.ispermissive
|
||||
right_permissive = right_type.ispermissive
|
||||
mod_permissive = left_permissive != right_permissive
|
||||
|
||||
if added_attr or removed_attr or added_aliases or removed_aliases or mod_permissive:
|
||||
self.modified_types[name] = modified_types_record(added_attr,
|
||||
removed_attr,
|
||||
matched_attr,
|
||||
mod_permissive,
|
||||
left_permissive,
|
||||
added_aliases,
|
||||
removed_aliases,
|
||||
matched_aliases)
|
||||
|
||||
#
|
||||
# Internal functions
|
||||
#
|
||||
def _reset_diff(self):
|
||||
"""Reset diff results on policy changes."""
|
||||
self.added_types = None
|
||||
self.removed_types = None
|
||||
self.modified_types = None
|
||||
|
||||
@staticmethod
|
||||
def _set_diff(left, right):
|
||||
"""
|
||||
Standard diff of two sets.
|
||||
|
||||
Parameters:
|
||||
left An iterable
|
||||
right An iterable
|
||||
|
||||
Return:
|
||||
tuple (added, removed, matched)
|
||||
|
||||
added Set of items in right but not left
|
||||
removed Set of items in left but not right
|
||||
matched Set of items in both left and right
|
||||
"""
|
||||
|
||||
left_items = set(str(l) for l in left)
|
||||
right_items = set(str(r) for r in right)
|
||||
added_items = right_items - left_items
|
||||
removed_items = left_items - right_items
|
||||
matched_items = left_items & right_items
|
||||
|
||||
return added_items, removed_items, matched_items
|
@ -19,6 +19,7 @@ from . import boolquery
|
||||
from . import categoryquery
|
||||
from . import constraintquery
|
||||
from . import commonquery
|
||||
from . import diff
|
||||
from . import dta
|
||||
from . import fsusequery
|
||||
from . import genfsconquery
|
||||
|
138
tests/diff.py
Normal file
138
tests/diff.py
Normal file
@ -0,0 +1,138 @@
|
||||
# Copyright 2015, 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 General Public License as published by
|
||||
# the Free Software Foundation, either version 2 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 General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with SETools. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
import unittest
|
||||
|
||||
from setools import SELinuxPolicy, PolicyDifference
|
||||
|
||||
|
||||
class PolicyDifferenceTest(unittest.TestCase):
|
||||
|
||||
"""Policy difference tests."""
|
||||
|
||||
def setUp(self):
|
||||
self.diff = PolicyDifference(SELinuxPolicy("tests/diff_left.conf"),
|
||||
SELinuxPolicy("tests/diff_right.conf"))
|
||||
|
||||
def test_001_added_types(self):
|
||||
"""Diff: added type"""
|
||||
self.assertSetEqual(set(["added_type"]), self.diff.added_types)
|
||||
|
||||
def test_002_removed_types(self):
|
||||
"""Diff: modified type"""
|
||||
self.assertSetEqual(set(["removed_type"]), self.diff.removed_types)
|
||||
|
||||
def test_003_modified_types_count(self):
|
||||
"""Diff: total modified types"""
|
||||
self.assertEqual(6, len(self.diff.modified_types))
|
||||
|
||||
def test_004_modified_types_remove_attr(self):
|
||||
"""Diff: modified type with removed attribute."""
|
||||
self.assertIn("modified_remove_attr", self.diff.modified_types)
|
||||
removed_attrs = self.diff.modified_types["modified_remove_attr"].removed_attributes
|
||||
self.assertSetEqual(set(["an_attr"]), removed_attrs)
|
||||
self.assertFalse(self.diff.modified_types["modified_remove_attr"].added_attributes)
|
||||
self.assertFalse(self.diff.modified_types["modified_remove_attr"].matched_attributes)
|
||||
self.assertFalse(self.diff.modified_types["modified_remove_attr"].modified_permissive)
|
||||
self.assertFalse(self.diff.modified_types["modified_remove_attr"].permissive)
|
||||
self.assertFalse(self.diff.modified_types["modified_remove_attr"].added_aliases)
|
||||
self.assertFalse(self.diff.modified_types["modified_remove_attr"].removed_aliases)
|
||||
self.assertFalse(self.diff.modified_types["modified_remove_attr"].matched_aliases)
|
||||
|
||||
def test_005_modified_types_remove_alias(self):
|
||||
"""Diff: modified type with removed alias."""
|
||||
self.assertIn("modified_remove_alias", self.diff.modified_types)
|
||||
removed_alias = self.diff.modified_types["modified_remove_alias"].removed_aliases
|
||||
self.assertSetEqual(set(["an_alias"]), removed_alias)
|
||||
self.assertFalse(self.diff.modified_types["modified_remove_alias"].added_attributes)
|
||||
self.assertFalse(self.diff.modified_types["modified_remove_alias"].removed_attributes)
|
||||
self.assertFalse(self.diff.modified_types["modified_remove_alias"].matched_attributes)
|
||||
self.assertFalse(self.diff.modified_types["modified_remove_alias"].modified_permissive)
|
||||
self.assertFalse(self.diff.modified_types["modified_remove_alias"].permissive)
|
||||
self.assertFalse(self.diff.modified_types["modified_remove_alias"].added_aliases)
|
||||
self.assertFalse(self.diff.modified_types["modified_remove_alias"].matched_aliases)
|
||||
|
||||
def test_006_modified_types_remove_permissive(self):
|
||||
"""Diff: modified type with removed permissve."""
|
||||
self.assertIn("modified_remove_permissive", self.diff.modified_types)
|
||||
self.assertFalse(self.diff.modified_types["modified_remove_permissive"].added_attributes)
|
||||
self.assertFalse(self.diff.modified_types["modified_remove_permissive"].removed_attributes)
|
||||
self.assertFalse(self.diff.modified_types["modified_remove_permissive"].matched_attributes)
|
||||
self.assertTrue(self.diff.modified_types["modified_remove_permissive"].modified_permissive)
|
||||
self.assertTrue(self.diff.modified_types["modified_remove_permissive"].permissive)
|
||||
self.assertFalse(self.diff.modified_types["modified_remove_permissive"].added_aliases)
|
||||
self.assertFalse(self.diff.modified_types["modified_remove_permissive"].removed_aliases)
|
||||
self.assertFalse(self.diff.modified_types["modified_remove_permissive"].matched_aliases)
|
||||
|
||||
def test_007_modified_types_add_attr(self):
|
||||
"""Diff: modified type with added attribute."""
|
||||
self.assertIn("modified_add_attr", self.diff.modified_types)
|
||||
added_attrs = self.diff.modified_types["modified_add_attr"].added_attributes
|
||||
self.assertSetEqual(set(["an_attr"]), added_attrs)
|
||||
self.assertFalse(self.diff.modified_types["modified_add_attr"].removed_attributes)
|
||||
self.assertFalse(self.diff.modified_types["modified_add_attr"].matched_attributes)
|
||||
self.assertFalse(self.diff.modified_types["modified_add_attr"].modified_permissive)
|
||||
self.assertFalse(self.diff.modified_types["modified_add_attr"].permissive)
|
||||
self.assertFalse(self.diff.modified_types["modified_add_attr"].added_aliases)
|
||||
self.assertFalse(self.diff.modified_types["modified_add_attr"].removed_aliases)
|
||||
self.assertFalse(self.diff.modified_types["modified_add_attr"].matched_aliases)
|
||||
|
||||
def test_008_modified_types_add_alias(self):
|
||||
"""Diff: modified type with added alias."""
|
||||
self.assertIn("modified_add_alias", self.diff.modified_types)
|
||||
added_alias = self.diff.modified_types["modified_add_alias"].added_aliases
|
||||
self.assertSetEqual(set(["an_alias"]), added_alias)
|
||||
self.assertFalse(self.diff.modified_types["modified_add_alias"].added_attributes)
|
||||
self.assertFalse(self.diff.modified_types["modified_add_alias"].removed_attributes)
|
||||
self.assertFalse(self.diff.modified_types["modified_add_alias"].matched_attributes)
|
||||
self.assertFalse(self.diff.modified_types["modified_add_alias"].modified_permissive)
|
||||
self.assertFalse(self.diff.modified_types["modified_add_alias"].permissive)
|
||||
self.assertFalse(self.diff.modified_types["modified_add_alias"].removed_aliases)
|
||||
self.assertFalse(self.diff.modified_types["modified_add_alias"].matched_aliases)
|
||||
|
||||
def test_009_modified_types_add_permissive(self):
|
||||
"""Diff: modified type with added permissive."""
|
||||
self.assertIn("modified_add_permissive", self.diff.modified_types)
|
||||
self.assertFalse(self.diff.modified_types["modified_add_permissive"].added_attributes)
|
||||
self.assertFalse(self.diff.modified_types["modified_add_permissive"].removed_attributes)
|
||||
self.assertFalse(self.diff.modified_types["modified_add_permissive"].matched_attributes)
|
||||
self.assertTrue(self.diff.modified_types["modified_add_permissive"].modified_permissive)
|
||||
self.assertFalse(self.diff.modified_types["modified_add_permissive"].permissive)
|
||||
self.assertFalse(self.diff.modified_types["modified_add_permissive"].added_aliases)
|
||||
self.assertFalse(self.diff.modified_types["modified_add_permissive"].removed_aliases)
|
||||
self.assertFalse(self.diff.modified_types["modified_add_permissive"].matched_aliases)
|
||||
|
||||
|
||||
class PolicyDifferenceTestNoDiff(unittest.TestCase):
|
||||
|
||||
"""Policy difference test with no policy differences."""
|
||||
|
||||
def setUp(self):
|
||||
self.diff = PolicyDifference(SELinuxPolicy("tests/diff_left.conf"),
|
||||
SELinuxPolicy("tests/diff_left.conf"))
|
||||
|
||||
def test_001_added_types(self):
|
||||
"""NoDiff: no added types"""
|
||||
self.assertFalse(self.diff.added_types)
|
||||
|
||||
def test_002_removed_types(self):
|
||||
"""NoDiff: no removed types"""
|
||||
self.assertFalse(self.diff.removed_types)
|
||||
|
||||
def test_003_modified_types(self):
|
||||
"""NoDiff: no modified types"""
|
||||
self.assertFalse(self.diff.modified_types)
|
127
tests/diff_left.conf
Normal file
127
tests/diff_left.conf
Normal file
@ -0,0 +1,127 @@
|
||||
class infoflow
|
||||
class infoflow2
|
||||
class infoflow3
|
||||
class infoflow4
|
||||
class infoflow5
|
||||
class infoflow6
|
||||
class infoflow7
|
||||
|
||||
sid kernel
|
||||
sid security
|
||||
|
||||
common infoflow
|
||||
{
|
||||
low_w
|
||||
med_w
|
||||
hi_w
|
||||
low_r
|
||||
med_r
|
||||
hi_r
|
||||
}
|
||||
|
||||
class infoflow
|
||||
inherits infoflow
|
||||
|
||||
class infoflow2
|
||||
inherits infoflow
|
||||
{
|
||||
super_w
|
||||
super_r
|
||||
}
|
||||
|
||||
class infoflow3
|
||||
{
|
||||
null
|
||||
}
|
||||
|
||||
class infoflow4
|
||||
inherits infoflow
|
||||
|
||||
class infoflow5
|
||||
inherits infoflow
|
||||
|
||||
class infoflow6
|
||||
inherits infoflow
|
||||
|
||||
class infoflow7
|
||||
inherits infoflow
|
||||
{
|
||||
super_w
|
||||
super_r
|
||||
super_none
|
||||
super_both
|
||||
super_unmapped
|
||||
}
|
||||
|
||||
sensitivity low_s;
|
||||
sensitivity medium_s alias med;
|
||||
sensitivity high_s;
|
||||
|
||||
dominance { low_s med high_s }
|
||||
|
||||
category here;
|
||||
category there;
|
||||
category elsewhere alias lost;
|
||||
|
||||
#level decl
|
||||
level low_s:here.there;
|
||||
level med:here, elsewhere;
|
||||
level high_s:here.lost;
|
||||
|
||||
#some constraints
|
||||
mlsconstrain infoflow hi_r ((l1 dom l2) or (t1 == mls_exempt));
|
||||
|
||||
attribute mls_exempt;
|
||||
attribute an_attr;
|
||||
|
||||
type system;
|
||||
role system;
|
||||
role system types system;
|
||||
|
||||
################################################################################
|
||||
# Type enforcement declarations and rules
|
||||
|
||||
type removed_type;
|
||||
|
||||
type modified_remove_attr, an_attr;
|
||||
|
||||
type modified_remove_alias alias an_alias;
|
||||
|
||||
type modified_remove_permissive;
|
||||
permissive modified_remove_permissive;
|
||||
|
||||
type modified_add_attr;
|
||||
|
||||
type modified_add_alias;
|
||||
|
||||
type modified_add_permissive;
|
||||
|
||||
################################################################################
|
||||
|
||||
#users
|
||||
user system roles system level med range low_s - high_s:here.lost;
|
||||
|
||||
#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
|
||||
|
||||
#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;
|
||||
|
||||
#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
|
||||
|
||||
portcon tcp 80 system:object_r:system:low_s
|
||||
|
||||
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
|
||||
|
127
tests/diff_right.conf
Normal file
127
tests/diff_right.conf
Normal file
@ -0,0 +1,127 @@
|
||||
class infoflow
|
||||
class infoflow2
|
||||
class infoflow3
|
||||
class infoflow4
|
||||
class infoflow5
|
||||
class infoflow6
|
||||
class infoflow7
|
||||
|
||||
sid kernel
|
||||
sid security
|
||||
|
||||
common infoflow
|
||||
{
|
||||
low_w
|
||||
med_w
|
||||
hi_w
|
||||
low_r
|
||||
med_r
|
||||
hi_r
|
||||
}
|
||||
|
||||
class infoflow
|
||||
inherits infoflow
|
||||
|
||||
class infoflow2
|
||||
inherits infoflow
|
||||
{
|
||||
super_w
|
||||
super_r
|
||||
}
|
||||
|
||||
class infoflow3
|
||||
{
|
||||
null
|
||||
}
|
||||
|
||||
class infoflow4
|
||||
inherits infoflow
|
||||
|
||||
class infoflow5
|
||||
inherits infoflow
|
||||
|
||||
class infoflow6
|
||||
inherits infoflow
|
||||
|
||||
class infoflow7
|
||||
inherits infoflow
|
||||
{
|
||||
super_w
|
||||
super_r
|
||||
super_none
|
||||
super_both
|
||||
super_unmapped
|
||||
}
|
||||
|
||||
sensitivity low_s;
|
||||
sensitivity medium_s alias med;
|
||||
sensitivity high_s;
|
||||
|
||||
dominance { low_s med high_s }
|
||||
|
||||
category here;
|
||||
category there;
|
||||
category elsewhere alias lost;
|
||||
|
||||
#level decl
|
||||
level low_s:here.there;
|
||||
level med:here, elsewhere;
|
||||
level high_s:here.lost;
|
||||
|
||||
#some constraints
|
||||
mlsconstrain infoflow hi_r ((l1 dom l2) or (t1 == mls_exempt));
|
||||
|
||||
attribute mls_exempt;
|
||||
attribute an_attr;
|
||||
|
||||
type system;
|
||||
role system;
|
||||
role system types system;
|
||||
|
||||
################################################################################
|
||||
# Type enforcement declarations and rules
|
||||
|
||||
type added_type;
|
||||
|
||||
type modified_remove_attr;
|
||||
|
||||
type modified_remove_alias;
|
||||
|
||||
type modified_remove_permissive;
|
||||
|
||||
type modified_add_attr, an_attr;
|
||||
|
||||
type modified_add_alias alias an_alias;
|
||||
|
||||
type modified_add_permissive;
|
||||
permissive modified_add_permissive;
|
||||
|
||||
################################################################################
|
||||
|
||||
#users
|
||||
user system roles system level med range low_s - high_s:here.lost;
|
||||
|
||||
#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
|
||||
|
||||
#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;
|
||||
|
||||
#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
|
||||
|
||||
portcon tcp 80 system:object_r:system:low_s
|
||||
|
||||
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
|
||||
|
Loading…
Reference in New Issue
Block a user