summaryrefslogtreecommitdiffstats
path: root/lib/python2.7/site-packages/setools
diff options
context:
space:
mode:
Diffstat (limited to 'lib/python2.7/site-packages/setools')
-rw-r--r--lib/python2.7/site-packages/setools/__init__.py9
-rw-r--r--lib/python2.7/site-packages/setools/constraintquery.py4
-rw-r--r--lib/python2.7/site-packages/setools/defaultquery.py71
-rw-r--r--lib/python2.7/site-packages/setools/descriptors.py38
-rw-r--r--lib/python2.7/site-packages/setools/diff/__init__.py77
-rw-r--r--lib/python2.7/site-packages/setools/diff/bool.py64
-rw-r--r--lib/python2.7/site-packages/setools/diff/commons.py72
-rw-r--r--lib/python2.7/site-packages/setools/diff/conditional.py31
-rw-r--r--lib/python2.7/site-packages/setools/diff/context.py44
-rw-r--r--lib/python2.7/site-packages/setools/diff/default.py113
-rw-r--r--lib/python2.7/site-packages/setools/diff/descriptors.py48
-rw-r--r--lib/python2.7/site-packages/setools/diff/difference.py173
-rw-r--r--lib/python2.7/site-packages/setools/diff/fsuse.py89
-rw-r--r--lib/python2.7/site-packages/setools/diff/genfscon.py92
-rw-r--r--lib/python2.7/site-packages/setools/diff/initsid.py64
-rw-r--r--lib/python2.7/site-packages/setools/diff/mls.py230
-rw-r--r--lib/python2.7/site-packages/setools/diff/mlsrules.py135
-rw-r--r--lib/python2.7/site-packages/setools/diff/netifcon.py102
-rw-r--r--lib/python2.7/site-packages/setools/diff/nodecon.py90
-rw-r--r--lib/python2.7/site-packages/setools/diff/objclass.py86
-rw-r--r--lib/python2.7/site-packages/setools/diff/polcap.py47
-rw-r--r--lib/python2.7/site-packages/setools/diff/portcon.py89
-rw-r--r--lib/python2.7/site-packages/setools/diff/properties.py64
-rw-r--r--lib/python2.7/site-packages/setools/diff/rbacrules.py189
-rw-r--r--lib/python2.7/site-packages/setools/diff/roles.py71
-rw-r--r--lib/python2.7/site-packages/setools/diff/terules.py418
-rw-r--r--lib/python2.7/site-packages/setools/diff/typeattr.py71
-rw-r--r--lib/python2.7/site-packages/setools/diff/types.py89
-rw-r--r--lib/python2.7/site-packages/setools/diff/users.py121
-rw-r--r--lib/python2.7/site-packages/setools/fsusequery.py4
-rw-r--r--lib/python2.7/site-packages/setools/mlsrulequery.py4
-rw-r--r--lib/python2.7/site-packages/setools/policyrep/__init__.py30
-rw-r--r--lib/python2.7/site-packages/setools/policyrep/_qpol.py7
-rwxr-xr-xlib/python2.7/site-packages/setools/policyrep/_qpol.sobin2166242 -> 2159327 bytes
-rw-r--r--lib/python2.7/site-packages/setools/policyrep/boolcond.py76
-rw-r--r--lib/python2.7/site-packages/setools/policyrep/constraint.py9
-rw-r--r--lib/python2.7/site-packages/setools/policyrep/default.py138
-rw-r--r--lib/python2.7/site-packages/setools/policyrep/exception.py33
-rw-r--r--lib/python2.7/site-packages/setools/policyrep/fscontext.py43
-rw-r--r--lib/python2.7/site-packages/setools/policyrep/mls.py14
-rw-r--r--lib/python2.7/site-packages/setools/policyrep/mlsrule.py54
-rw-r--r--lib/python2.7/site-packages/setools/policyrep/netcontext.py37
-rw-r--r--lib/python2.7/site-packages/setools/policyrep/rbacrule.py76
-rw-r--r--lib/python2.7/site-packages/setools/policyrep/rule.py23
-rw-r--r--lib/python2.7/site-packages/setools/policyrep/symbol.py2
-rw-r--r--lib/python2.7/site-packages/setools/policyrep/terule.py141
-rw-r--r--lib/python2.7/site-packages/setools/rbacrulequery.py4
-rw-r--r--lib/python2.7/site-packages/setools/rolequery.py7
-rw-r--r--lib/python2.7/site-packages/setools/terulequery.py4
49 files changed, 3287 insertions, 210 deletions
diff --git a/lib/python2.7/site-packages/setools/__init__.py b/lib/python2.7/site-packages/setools/__init__.py
index d86ecdc..f6bfff8 100644
--- a/lib/python2.7/site-packages/setools/__init__.py
+++ b/lib/python2.7/site-packages/setools/__init__.py
@@ -17,7 +17,8 @@
# License along with SETools. If not, see
# <http://www.gnu.org/licenses/>.
#
-__version__ = "3.3.8"
+__version__ = "4.0.0-alpha3"
+
# Python classes for policy representation
from . import policyrep
from .policyrep import SELinuxPolicy
@@ -45,6 +46,9 @@ from .terulequery import TERuleQuery
# Constraint queries
from .constraintquery import ConstraintQuery
+# Other queries
+from .defaultquery import DefaultQuery
+
# In-policy Context Queries
from .fsusequery import FSUseQuery
from .genfsconquery import GenfsconQuery
@@ -59,3 +63,6 @@ from .permmap import PermissionMap
# Domain Transition Analysis
from .dta import DomainTransitionAnalysis
+
+# Policy difference
+from .diff import PolicyDifference
diff --git a/lib/python2.7/site-packages/setools/constraintquery.py b/lib/python2.7/site-packages/setools/constraintquery.py
index 82a6fc2..a7fee76 100644
--- a/lib/python2.7/site-packages/setools/constraintquery.py
+++ b/lib/python2.7/site-packages/setools/constraintquery.py
@@ -20,7 +20,7 @@ import logging
import re
from . import mixins, query
-from .descriptors import CriteriaDescriptor, CriteriaSetDescriptor, RuletypeDescriptor
+from .descriptors import CriteriaDescriptor, CriteriaSetDescriptor
from .policyrep.exception import ConstraintUseError
@@ -62,7 +62,7 @@ class ConstraintQuery(mixins.MatchObjClass, mixins.MatchPermission, query.Policy
be used on the user.
"""
- ruletype = RuletypeDescriptor("validate_constraint_ruletype")
+ ruletype = CriteriaSetDescriptor(lookup_function="validate_constraint_ruletype")
user = CriteriaDescriptor("user_regex", "lookup_user")
user_regex = False
role = CriteriaDescriptor("role_regex", "lookup_role")
diff --git a/lib/python2.7/site-packages/setools/defaultquery.py b/lib/python2.7/site-packages/setools/defaultquery.py
new file mode 100644
index 0000000..dac93bc
--- /dev/null
+++ b/lib/python2.7/site-packages/setools/defaultquery.py
@@ -0,0 +1,71 @@
+# Copyright 2014-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
+import re
+
+from .query import PolicyQuery
+from .descriptors import CriteriaDescriptor, CriteriaSetDescriptor
+from .mixins import MatchObjClass
+
+
+class DefaultQuery(MatchObjClass, PolicyQuery):
+
+ """
+ Query default_* statements.
+
+ Parameter:
+ policy The policy to query.
+
+ Keyword Parameters/Class attributes:
+ ruletype The rule type(s) to match.
+ tclass The object class(es) to match.
+ tclass_regex If true, use a regular expression for
+ matching the rule's object class.
+ default The default to base new contexts (e.g. "source" or "target")
+ default_range The range to use on new context, default_range only
+ ("low", "high", "low_high")
+ """
+
+ ruletype = CriteriaSetDescriptor(lookup_function="validate_default_ruletype")
+ default = CriteriaDescriptor(lookup_function="validate_default_value")
+ default_range = CriteriaDescriptor(lookup_function="validate_default_range")
+
+ def results(self):
+ """Generator which yields all matching default_* statements."""
+ self.log.info("Generating results from {0.policy}".format(self))
+ self.log.debug("Ruletypes: {0.ruletype}".format(self))
+
+ for d in self.policy.defaults():
+ if self.ruletype and d.ruletype not in self.ruletype:
+ continue
+
+ if not self._match_object_class(d):
+ continue
+
+ if self.default and d.default != self.default:
+ continue
+
+ if self.default_range:
+ try:
+ if d.default_range != self.default_range:
+ continue
+ except AttributeError:
+ continue
+
+ yield d
diff --git a/lib/python2.7/site-packages/setools/descriptors.py b/lib/python2.7/site-packages/setools/descriptors.py
index eab9210..c4bb73c 100644
--- a/lib/python2.7/site-packages/setools/descriptors.py
+++ b/lib/python2.7/site-packages/setools/descriptors.py
@@ -106,44 +106,6 @@ class CriteriaSetDescriptor(CriteriaDescriptor):
self.instances[obj] = set(value)
-class RuletypeDescriptor(object):
-
- """
- Descriptor for a list of rule types.
-
- Parameters:
- validator The name of the SELinuxPolicy ruletype
- validator function, e.g. validate_te_ruletype
- default_value The default value of the criteria. The default
- is None.
-
- Read-only instance attribute use (obj parameter):
- policy The instance of SELinuxPolicy
- """
-
- def __init__(self, validator):
- self.validator = validator
-
- # 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
-
- return self.instances.setdefault(obj, None)
-
- def __set__(self, obj, value):
- if value:
- validate = getattr(obj.policy, self.validator)
- validate(value)
- self.instances[obj] = value
- else:
- self.instances[obj] = None
-
-
#
# NetworkX Graph Descriptors
#
diff --git a/lib/python2.7/site-packages/setools/diff/__init__.py b/lib/python2.7/site-packages/setools/diff/__init__.py
new file mode 100644
index 0000000..8d5900a
--- /dev/null
+++ b/lib/python2.7/site-packages/setools/diff/__init__.py
@@ -0,0 +1,77 @@
+# Copyright 2015-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 .bool import BooleansDifference
+from .commons import CommonDifference
+from .default import DefaultsDifference
+from .fsuse import FSUsesDifference
+from .genfscon import GenfsconsDifference
+from .initsid import InitialSIDsDifference
+from .mls import CategoriesDifference, LevelDeclsDifference, SensitivitiesDifference
+from .mlsrules import MLSRulesDifference
+from .netifcon import NetifconsDifference
+from .nodecon import NodeconsDifference
+from .objclass import ObjClassDifference
+from .polcap import PolCapsDifference
+from .portcon import PortconsDifference
+from .properties import PropertiesDifference
+from .rbacrules import RBACRulesDifference
+from .roles import RolesDifference
+from .terules import TERulesDifference
+from .typeattr import TypeAttributesDifference
+from .types import TypesDifference
+from .users import UsersDifference
+
+__all__ = ['PolicyDifference']
+
+
+class PolicyDifference(BooleansDifference,
+ CategoriesDifference,
+ CommonDifference,
+ DefaultsDifference,
+ FSUsesDifference,
+ GenfsconsDifference,
+ InitialSIDsDifference,
+ LevelDeclsDifference,
+ MLSRulesDifference,
+ NetifconsDifference,
+ NodeconsDifference,
+ ObjClassDifference,
+ PolCapsDifference,
+ PortconsDifference,
+ PropertiesDifference,
+ RBACRulesDifference,
+ RolesDifference,
+ SensitivitiesDifference,
+ TERulesDifference,
+ TypeAttributesDifference,
+ TypesDifference,
+ UsersDifference):
+
+ """
+ Determine the differences from the left policy to the right policy.
+
+ Parameters:
+ left A policy
+ right A policy
+ """
+
+ def _reset_diff(self):
+ """Reset diff results on policy changes."""
+ for c in PolicyDifference.__bases__:
+ c._reset_diff(self)
diff --git a/lib/python2.7/site-packages/setools/diff/bool.py b/lib/python2.7/site-packages/setools/diff/bool.py
new file mode 100644
index 0000000..212a715
--- /dev/null
+++ b/lib/python2.7/site-packages/setools/diff/bool.py
@@ -0,0 +1,64 @@
+# 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
+
+
+modified_bool_record = namedtuple("modified_boolean", ["added_state", "removed_state"])
+
+
+class BooleansDifference(Difference):
+
+ """Determine the difference in type attributes between two policies."""
+
+ added_booleans = DiffResultDescriptor("diff_booleans")
+ removed_booleans = DiffResultDescriptor("diff_booleans")
+ modified_booleans = DiffResultDescriptor("diff_booleans")
+
+ def diff_booleans(self):
+ """Generate the difference in type attributes between the policies."""
+
+ self.log.info("Generating Boolean differences from {0.left_policy} to {0.right_policy}".
+ format(self))
+
+ self.added_booleans, self.removed_booleans, matched_booleans = \
+ self._set_diff(
+ (SymbolWrapper(b) for b in self.left_policy.bools()),
+ (SymbolWrapper(b) for b in self.right_policy.bools()))
+
+ self.modified_booleans = dict()
+
+ for left_boolean, right_boolean in matched_booleans:
+ # Criteria for modified booleans
+ # 1. change to default state
+ if left_boolean.state != right_boolean.state:
+ self.modified_booleans[left_boolean] = modified_bool_record(right_boolean.state,
+ left_boolean.state)
+
+ #
+ # Internal functions
+ #
+ def _reset_diff(self):
+ """Reset diff results on policy changes."""
+ self.log.debug("Resetting Boolean differences")
+ self.added_booleans = None
+ self.removed_booleans = None
+ self.modified_booleans = None
diff --git a/lib/python2.7/site-packages/setools/diff/commons.py b/lib/python2.7/site-packages/setools/diff/commons.py
new file mode 100644
index 0000000..41c13f8
--- /dev/null
+++ b/lib/python2.7/site-packages/setools/diff/commons.py
@@ -0,0 +1,72 @@
+# 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/>.
+#
+from collections import namedtuple
+
+from .descriptors import DiffResultDescriptor
+from .difference import Difference, SymbolWrapper
+
+
+modified_commons_record = namedtuple("modified_common", ["added_perms",
+ "removed_perms",
+ "matched_perms"])
+
+
+class CommonDifference(Difference):
+
+ """
+ Determine the difference in common permission sets
+ between two policies.
+ """
+
+ added_commons = DiffResultDescriptor("diff_commons")
+ removed_commons = DiffResultDescriptor("diff_commons")
+ modified_commons = DiffResultDescriptor("diff_commons")
+
+ def diff_commons(self):
+ """Generate the difference in commons between the policies."""
+
+ self.log.info(
+ "Generating common differences from {0.left_policy} to {0.right_policy}".format(self))
+
+ self.added_commons, self.removed_commons, matched_commons = self._set_diff(
+ (SymbolWrapper(c) for c in self.left_policy.commons()),
+ (SymbolWrapper(c) for c in self.right_policy.commons()))
+
+ self.modified_commons = dict()
+
+ for left_common, right_common in matched_commons:
+ # Criteria for modified commons
+ # 1. change to permissions
+ added_perms, removed_perms, matched_perms = self._set_diff(left_common.perms,
+ right_common.perms)
+
+ if added_perms or removed_perms:
+ self.modified_commons[left_common] = modified_commons_record(added_perms,
+ removed_perms,
+ matched_perms)
+
+ #
+ # Internal functions
+ #
+ def _reset_diff(self):
+ """Reset diff results on policy changes."""
+ self.log.debug("Resetting common differences")
+ self.added_commons = None
+ self.removed_commons = None
+ self.modified_commons = None
diff --git a/lib/python2.7/site-packages/setools/diff/conditional.py b/lib/python2.7/site-packages/setools/diff/conditional.py
new file mode 100644
index 0000000..95a620e
--- /dev/null
+++ b/lib/python2.7/site-packages/setools/diff/conditional.py
@@ -0,0 +1,31 @@
+# Copyright 2015-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 .difference import Wrapper
+
+
+class ConditionalExprWrapper(Wrapper):
+
+ """Wrap conditional policy expressions to allow comparisons by truth table."""
+
+ def __init__(self, cond):
+ self.origin = cond
+ self.truth_table = cond.truth_table()
+
+ def __eq__(self, other):
+ return self.truth_table == other.truth_table
diff --git a/lib/python2.7/site-packages/setools/diff/context.py b/lib/python2.7/site-packages/setools/diff/context.py
new file mode 100644
index 0000000..4124aff
--- /dev/null
+++ b/lib/python2.7/site-packages/setools/diff/context.py
@@ -0,0 +1,44 @@
+# 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 ..policyrep.exception import MLSDisabled
+
+from .difference import SymbolWrapper, Wrapper
+from .mls import RangeWrapper
+
+
+class ContextWrapper(Wrapper):
+
+ """Wrap contexts to allow comparisons."""
+
+ def __init__(self, ctx):
+ self.origin = ctx
+ self.user = SymbolWrapper(ctx.user)
+ self.role = SymbolWrapper(ctx.role)
+ self.type_ = SymbolWrapper(ctx.type_)
+
+ try:
+ self.range_ = RangeWrapper(ctx.range_)
+ except MLSDisabled:
+ self.range_ = None
+
+ def __eq__(self, other):
+ return self.user == other.user and \
+ self.role == other.role and \
+ self.type_ == other.type_ and \
+ self.range_ == other.range_
diff --git a/lib/python2.7/site-packages/setools/diff/default.py b/lib/python2.7/site-packages/setools/diff/default.py
new file mode 100644
index 0000000..ce7c569
--- /dev/null
+++ b/lib/python2.7/site-packages/setools/diff/default.py
@@ -0,0 +1,113 @@
+# 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_default_record = namedtuple("modified_default", ["rule",
+ "added_default",
+ "removed_default",
+ "added_default_range",
+ "removed_default_range"])
+
+
+class DefaultsDifference(Difference):
+
+ """Determine the difference in default_* between two policies."""
+
+ added_defaults = DiffResultDescriptor("diff_defaults")
+ removed_defaults = DiffResultDescriptor("diff_defaults")
+ modified_defaults = DiffResultDescriptor("diff_defaults")
+
+ def diff_defaults(self):
+ """Generate the difference in type defaults between the policies."""
+
+ self.log.info(
+ "Generating default_* differences from {0.left_policy} to {0.right_policy}".
+ format(self))
+
+ self.added_defaults, self.removed_defaults, matched_defaults = self._set_diff(
+ (DefaultWrapper(d) for d in self.left_policy.defaults()),
+ (DefaultWrapper(d) for d in self.right_policy.defaults()))
+
+ self.modified_defaults = []
+
+ for left_default, right_default in matched_defaults:
+ # Criteria for modified defaults
+ # 1. change to default setting
+ # 2. change to default range
+
+ if left_default.default != right_default.default:
+ removed_default = left_default.default
+ added_default = right_default.default
+ else:
+ removed_default = None
+ added_default = None
+
+ try:
+ if left_default.default_range != right_default.default_range:
+ removed_default_range = left_default.default_range
+ added_default_range = right_default.default_range
+ else:
+ removed_default_range = None
+ added_default_range = None
+ except AttributeError:
+ removed_default_range = None
+ added_default_range = None
+
+ if removed_default or removed_default_range:
+ self.modified_defaults.append(
+ modified_default_record(left_default,
+ added_default,
+ removed_default,
+ added_default_range,
+ removed_default_range))
+
+ #
+ # Internal functions
+ #
+ def _reset_diff(self):
+ """Reset diff results on policy changes."""
+ self.log.debug("Resetting default_* differences")
+ self.added_defaults = None
+ self.removed_defaults = None
+ self.modified_defaults = None
+
+
+class DefaultWrapper(Wrapper):
+
+ """Wrap default_* to allow comparisons."""
+
+ def __init__(self, default):
+ self.origin = default
+ self.ruletype = default.ruletype
+ self.tclass = SymbolWrapper(default.tclass)
+ self.key = hash(default)
+
+ 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.tclass == other.tclass
diff --git a/lib/python2.7/site-packages/setools/diff/descriptors.py b/lib/python2.7/site-packages/setools/diff/descriptors.py
new file mode 100644
index 0000000..2295d74
--- /dev/null
+++ b/lib/python2.7/site-packages/setools/diff/descriptors.py
@@ -0,0 +1,48 @@
+# 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/>.
+#
+from weakref import WeakKeyDictionary
+
+
+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 cleaner.
+
+ 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
diff --git a/lib/python2.7/site-packages/setools/diff/difference.py b/lib/python2.7/site-packages/setools/diff/difference.py
new file mode 100644
index 0000000..189c67d
--- /dev/null
+++ b/lib/python2.7/site-packages/setools/diff/difference.py
@@ -0,0 +1,173 @@
+# Copyright 2015-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/>.
+#
+import logging
+from collections import namedtuple
+
+modified_item_record = namedtuple("modified_item", ["left", "right"])
+
+
+class Difference(object):
+
+ """Base class for all policy differences."""
+
+ 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.log.info("Policy diff left policy set to {0}".format(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.log.info("Policy diff right policy set to {0}".format(policy))
+ self._right_policy = policy
+ self._reset_diff()
+
+ #
+ # Internal functions
+ #
+ def _reset_diff(self):
+ """Reset diff results on policy changes."""
+ raise NotImplementedError
+
+ @staticmethod
+ def _expand_generator(rule_list, Wrapper):
+ """Generator that yields a wrapped, expanded rule list."""
+ # this is to delay creating any containers
+ # as long as possible, since rule lists
+ # are typically massive.
+ for unexpanded_rule in rule_list:
+ for expanded_rule in unexpanded_rule.expand():
+ yield Wrapper(expanded_rule)
+
+ @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. This is
+ in the form of tuples with the matching item
+ from left and right
+ """
+
+ left_items = set(left)
+ right_items = set(right)
+ added_items = right_items - left_items
+ removed_items = left_items - right_items
+
+ # The problem here is the symbol from both policies are
+ # needed to build each tuple in the matched items set.
+ # Using the standard Python set intersection code will only result
+ # in one object.
+ #
+ # This tuple-generating code creates lists from the sets, to sort them.
+ # This should result in all of the symbols lining up. If they don't,
+ # this will break the caller. This should work since there is no remapping.
+ #
+ # This has extra checking to make sure this assertion holds, to fail
+ # 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.
+ matched_items = set()
+ left_matched_items = sorted((left_items - removed_items))
+ right_matched_items = sorted((right_items - added_items))
+ assert len(left_matched_items) == len(right_matched_items), \
+ "Matched items assertion failure (this is an SETools bug), {0} != {1}". \
+ format(len(left_matched_items), len(right_matched_items))
+
+ for l, r in zip(left_matched_items, right_matched_items):
+ assert l == r, \
+ "Matched items assertion failure (this is an SETools bug), {0} != {1}".format(l, r)
+
+ matched_items.add((l, r))
+
+ try:
+ # unwrap the objects
+ return set(i.origin for i in added_items), \
+ set(i.origin for i in removed_items), \
+ set((l.origin, r.origin) for (l, r) in matched_items)
+ except AttributeError:
+ return added_items, removed_items, matched_items
+
+
+class Wrapper(object):
+
+ """Base class for policy object wrappers."""
+
+ origin = None
+
+ def __repr__(self):
+ return "<{0.__class__.__name__}(Wrapping {1})>".format(self, repr(self.origin))
+
+ def __hash__(self):
+ raise NotImplementedError
+
+ def __eq__(self, other):
+ raise NotImplementedError
+
+ def __lt__(self, other):
+ raise NotImplementedError
+
+ def __ne__(self, other):
+ return not self == other
+
+
+class SymbolWrapper(Wrapper):
+
+ """
+ General wrapper for policy symbols, e.g. types, roles
+ to provide a diff-specific equality operation based
+ on its name.
+ """
+
+ def __init__(self, symbol):
+ self.origin = symbol
+ self.name = str(symbol)
+
+ def __hash__(self):
+ return hash(self.name)
+
+ def __lt__(self, other):
+ return self.name < other.name
+
+ def __eq__(self, other):
+ return self.name == other.name
diff --git a/lib/python2.7/site-packages/setools/diff/fsuse.py b/lib/python2.7/site-packages/setools/diff/fsuse.py
new file mode 100644
index 0000000..3a0c0e1
--- /dev/null
+++ b/lib/python2.7/site-packages/setools/diff/fsuse.py
@@ -0,0 +1,89 @@
+# 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 .context import ContextWrapper
+from .descriptors import DiffResultDescriptor
+from .difference import Difference, Wrapper
+
+
+modified_fsuse_record = namedtuple("modified_fsuse", ["rule",
+ "added_context",
+ "removed_context"])
+
+
+class FSUsesDifference(Difference):
+
+ """Determine the difference in fs_use_* rules between two policies."""
+
+ added_fs_uses = DiffResultDescriptor("diff_fs_uses")
+ removed_fs_uses = DiffResultDescriptor("diff_fs_uses")
+ modified_fs_uses = DiffResultDescriptor("diff_fs_uses")
+
+ def diff_fs_uses(self):
+ """Generate the difference in fs_use rules between the policies."""
+
+ self.log.info(
+ "Generating fs_use_* differences from {0.left_policy} to {0.right_policy}".
+ format(self))
+
+ self.added_fs_uses, self.removed_fs_uses, matched = self._set_diff(
+ (FSUseWrapper(fs) for fs in self.left_policy.fs_uses()),
+ (FSUseWrapper(fs) for fs in self.right_policy.fs_uses()))
+
+ self.modified_fs_uses = []
+
+ for left_rule, right_rule in matched:
+ # Criteria for modified rules
+ # 1. change to context
+ if ContextWrapper(left_rule.context) != ContextWrapper(right_rule.context):
+ self.modified_fs_uses.append(modified_fsuse_record(left_rule,
+ right_rule.context,
+ left_rule.context))
+
+ #
+ # Internal functions
+ #
+ def _reset_diff(self):
+ """Reset diff results on policy changes."""
+ self.log.debug("Resetting fs_use_* rule differences")
+ self.added_fs_uses = None
+ self.removed_fs_uses = None
+ self.modified_fs_uses = None
+
+
+class FSUseWrapper(Wrapper):
+
+ """Wrap fs_use_* rules to allow set operations."""
+
+ def __init__(self, rule):
+ self.origin = rule
+ self.ruletype = rule.ruletype
+ self.fs = rule.fs
+ self.context = ContextWrapper(rule.context)
+ 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.fs == other.fs
diff --git a/lib/python2.7/site-packages/setools/diff/genfscon.py b/lib/python2.7/site-packages/setools/diff/genfscon.py
new file mode 100644
index 0000000..24f0a7d
--- /dev/null
+++ b/lib/python2.7/site-packages/setools/diff/genfscon.py
@@ -0,0 +1,92 @@
+# 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 .context import ContextWrapper
+from .descriptors import DiffResultDescriptor
+from .difference import Difference, Wrapper
+
+
+modified_genfs_record = namedtuple("modified_genfs", ["rule",
+ "added_context",
+ "removed_context"])
+
+
+class GenfsconsDifference(Difference):
+
+ """Determine the difference in genfscon rules between two policies."""
+
+ added_genfscons = DiffResultDescriptor("diff_genfscons")
+ removed_genfscons = DiffResultDescriptor("diff_genfscons")
+ modified_genfscons = DiffResultDescriptor("diff_genfscons")
+
+ def diff_genfscons(self):
+ """Generate the difference in genfscon rules between the policies."""
+
+ self.log.info(
+ "Generating genfscon differences from {0.left_policy} to {0.right_policy}".
+ format(self))
+
+ self.added_genfscons, self.removed_genfscons, matched = self._set_diff(
+ (GenfsconWrapper(fs) for fs in self.left_policy.genfscons()),
+ (GenfsconWrapper(fs) for fs in self.right_policy.genfscons()))
+
+ self.modified_genfscons = []
+
+ for left_rule, right_rule in matched:
+ # Criteria for modified rules
+ # 1. change to context
+ if ContextWrapper(left_rule.context) != ContextWrapper(right_rule.context):
+ self.modified_genfscons.append(modified_genfs_record(left_rule,
+ right_rule.context,
+ left_rule.context))
+
+ #
+ # Internal functions
+ #
+ def _reset_diff(self):
+ """Reset diff results on policy changes."""
+ self.log.debug("Resetting genfscon rule differences")
+ self.added_genfscons = None
+ self.removed_genfscons = None
+ self.modified_genfscons = None
+
+
+class GenfsconWrapper(Wrapper):
+
+ """Wrap genfscon rules to allow set operations."""
+
+ def __init__(self, rule):
+ self.origin = rule
+ self.fs = rule.fs
+ self.path = rule.path
+ self.filetype = rule.filetype
+ self.context = ContextWrapper(rule.context)
+ 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.fs == other.fs and \
+ self.path == other.path and \
+ self.filetype == other.filetype
diff --git a/lib/python2.7/site-packages/setools/diff/initsid.py b/lib/python2.7/site-packages/setools/diff/initsid.py
new file mode 100644
index 0000000..33098ad
--- /dev/null
+++ b/lib/python2.7/site-packages/setools/diff/initsid.py
@@ -0,0 +1,64 @@
+# 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 .context import ContextWrapper
+from .descriptors import DiffResultDescriptor
+from .difference import Difference, SymbolWrapper
+
+
+modified_initsids_record = namedtuple("modified_initsid", ["added_context", "removed_context"])
+
+
+class InitialSIDsDifference(Difference):
+
+ """Determine the difference in initsids between two policies."""
+
+ added_initialsids = DiffResultDescriptor("diff_initialsids")
+ removed_initialsids = DiffResultDescriptor("diff_initialsids")
+ modified_initialsids = DiffResultDescriptor("diff_initialsids")
+
+ def diff_initialsids(self):
+ """Generate the difference in initial SIDs between the policies."""
+
+ self.log.info("Generating initial SID differences from {0.left_policy} to {0.right_policy}".
+ format(self))
+
+ self.added_initialsids, self.removed_initialsids, matched_initialsids = self._set_diff(
+ (SymbolWrapper(i) for i in self.left_policy.initialsids()),
+ (SymbolWrapper(i) for i in self.right_policy.initialsids()))
+
+ self.modified_initialsids = dict()
+
+ for left_initialsid, right_initialsid in matched_initialsids:
+ # Criteria for modified initialsids
+ # 1. change to context
+ if ContextWrapper(left_initialsid.context) != ContextWrapper(right_initialsid.context):
+ self.modified_initialsids[left_initialsid] = modified_initsids_record(
+ right_initialsid.context, left_initialsid.context)
+
+ #
+ # Internal functions
+ #
+ def _reset_diff(self):
+ """Reset diff results on policy changes."""
+ self.log.debug("Resetting initialsid differences")
+ self.added_initialsids = None
+ self.removed_initialsids = None
+ self.modified_initialsids = None
diff --git a/lib/python2.7/site-packages/setools/diff/mls.py b/lib/python2.7/site-packages/setools/diff/mls.py
new file mode 100644
index 0000000..efd758f
--- /dev/null
+++ b/lib/python2.7/site-packages/setools/diff/mls.py
@@ -0,0 +1,230 @@
+# 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_cat_record = namedtuple("modified_category", ["added_aliases",
+ "removed_aliases",
+ "matched_aliases"])
+
+modified_sens_record = namedtuple("modified_sensitivity", ["added_aliases",
+ "removed_aliases",
+ "matched_aliases"])
+
+modified_level_record = namedtuple("modified_level", ["level",
+ "added_categories",
+ "removed_categories",
+ "matched_categories"])
+
+
+class CategoriesDifference(Difference):
+
+ """Determine the difference in categories between two policies."""
+
+ added_categories = DiffResultDescriptor("diff_categories")
+ removed_categories = DiffResultDescriptor("diff_categories")
+ modified_categories = DiffResultDescriptor("diff_categories")
+
+ def diff_categories(self):
+ """Generate the difference in categories between the policies."""
+
+ self.log.info(
+ "Generating category differences from {0.left_policy} to {0.right_policy}".format(self))
+
+ self.added_categories, self.removed_categories, matched_categories = self._set_diff(
+ (SymbolWrapper(c) for c in self.left_policy.categories()),
+ (SymbolWrapper(c) for c in self.right_policy.categories()))
+
+ self.modified_categories = dict()
+
+ for left_category, right_category in matched_categories:
+ # Criteria for modified categories
+ # 1. change to aliases
+ added_aliases, removed_aliases, matched_aliases = self._set_diff(
+ left_category.aliases(), right_category.aliases())
+
+ if added_aliases or removed_aliases:
+ self.modified_categories[left_category] = modified_cat_record(added_aliases,
+ removed_aliases,
+ matched_aliases)
+
+ #
+ # Internal functions
+ #
+ def _reset_diff(self):
+ """Reset diff results on policy changes."""
+ self.log.debug("Resetting category differences")
+ self.added_categories = None
+ self.removed_categories = None
+ self.modified_categories = None
+
+
+class SensitivitiesDifference(Difference):
+
+ """Determine the difference in sensitivities between two policies."""
+
+ added_sensitivities = DiffResultDescriptor("diff_sensitivities")
+ removed_sensitivities = DiffResultDescriptor("diff_sensitivities")
+ modified_sensitivities = DiffResultDescriptor("diff_sensitivities")
+
+ def diff_sensitivities(self):
+ """Generate the difference in sensitivities between the policies."""
+
+ self.log.info(
+ "Generating sensitivity differences from {0.left_policy} to {0.right_policy}".
+ format(self))
+
+ self.added_sensitivities, self.removed_sensitivities, matched_sensitivities = \
+ self._set_diff(
+ (SymbolWrapper(s) for s in self.left_policy.sensitivities()),
+ (SymbolWrapper(s) for s in self.right_policy.sensitivities()))
+
+ self.modified_sensitivities = dict()
+
+ for left_sens, right_sens in matched_sensitivities:
+ # Criteria for modified sensitivities
+ # 1. change to aliases
+ added_aliases, removed_aliases, matched_aliases = self._set_diff(
+ left_sens.aliases(), right_sens.aliases())
+
+ if added_aliases or removed_aliases:
+ self.modified_sensitivities[left_sens] = modified_sens_record(added_aliases,
+ removed_aliases,
+ matched_aliases)
+
+ #
+ # Internal functions
+ #
+ def _reset_diff(self):
+ """Reset diff results on policy changes."""
+ self.log.debug("Resetting sensitivity differences")
+ self.added_sensitivities = None
+ self.removed_sensitivities = None
+ self.modified_sensitivities = None
+
+
+class LevelDeclsDifference(Difference):
+
+ """Determine the difference in levels between two policies."""
+
+ added_levels = DiffResultDescriptor("diff_levels")
+ removed_levels = DiffResultDescriptor("diff_levels")
+ modified_levels = DiffResultDescriptor("diff_levels")
+
+ def diff_levels(self):
+ """Generate the difference in levels between the policies."""
+
+ self.log.info(
+ "Generating level decl differences from {0.left_policy} to {0.right_policy}".
+ format(self))
+
+ self.added_levels, self.removed_levels, matched_levels = \
+ self._set_diff(
+ (LevelDeclWrapper(s) for s in self.left_policy.levels()),
+ (LevelDeclWrapper(s) for s in self.right_policy.levels()))
+
+ self.modified_levels = []
+
+ for left_level, right_level in matched_levels:
+ # Criteria for modified levels
+ # 1. change to allowed categories
+ added_categories, removed_categories, matched_categories = self._set_diff(
+ (SymbolWrapper(c) for c in left_level.categories()),
+ (SymbolWrapper(c) for c in right_level.categories()))
+
+ if added_categories or removed_categories:
+ self.modified_levels.append(modified_level_record(
+ left_level, added_categories, removed_categories, matched_categories))
+
+ #
+ # Internal functions
+ #
+ def _reset_diff(self):
+ """Reset diff results on policy changes."""
+ self.log.debug("Resetting sensitivity differences")
+ self.added_levels = None
+ self.removed_levels = None
+ self.modified_levels = None
+
+
+class LevelDeclWrapper(Wrapper):
+
+ """Wrap level declarations to allow comparisons."""
+
+ def __init__(self, level):
+ self.origin = level
+ self.sensitivity = SymbolWrapper(level.sensitivity)
+ self.key = hash(level)
+
+ def __hash__(self):
+ return self.key
+
+ def __eq__(self, other):
+ # non-MLS policies have no level declarations so there
+ # should be no AttributeError possiblity here
+ return self.sensitivity == other.sensitivity
+
+ def __lt__(self, other):
+ return self.sensitivity < other.sensitivity
+
+
+class LevelWrapper(Wrapper):
+
+ """Wrap levels to allow comparisons."""
+
+ def __init__(self, level):
+ self.origin = level
+ self.sensitivity = SymbolWrapper(level.sensitivity)
+ self.categories = set(SymbolWrapper(c) for c in level.categories())
+
+ def __eq__(self, other):
+ try:
+ return self.sensitivity == other.sensitivity and \
+ self.categories == other.categories
+ except AttributeError:
+ # comparing an MLS policy to non-MLS policy will result in
+ # other being None
+ return False
+
+
+class RangeWrapper(Wrapper):
+
+ """
+ Wrap ranges to allow comparisons.
+
+ This only compares the low and high levels of the range.
+ It does not detect additions/removals/modifications
+ to levels between the low and high levels of the range.
+ """
+
+ def __init__(self, range_):
+ self.origin = range_
+ self.low = LevelWrapper(range_.low)
+ self.high = LevelWrapper(range_.high)
+
+ def __eq__(self, other):
+ try:
+ return self.low == other.low and \
+ self.high == other.high
+ except AttributeError:
+ # comparing an MLS policy to non-MLS policy will result in
+ # other being None
+ return False
diff --git a/lib/python2.7/site-packages/setools/diff/mlsrules.py b/lib/python2.7/site-packages/setools/diff/mlsrules.py
new file mode 100644
index 0000000..75f443e
--- /dev/null
+++ b/lib/python2.7/site-packages/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
+# <http://www.gnu.org/licenses/>.
+#
+from collections import namedtuple
+
+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:
+ self.log.error("Unknown rule type: {0} (This is an SETools bug)".
+ 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:
+ self.log.error("Unknown rule type: {0} (This is an SETools bug)".
+ 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/lib/python2.7/site-packages/setools/diff/netifcon.py b/lib/python2.7/site-packages/setools/diff/netifcon.py
new file mode 100644
index 0000000..8a2b9e3
--- /dev/null
+++ b/lib/python2.7/site-packages/setools/diff/netifcon.py
@@ -0,0 +1,102 @@
+# 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 .context import ContextWrapper
+from .descriptors import DiffResultDescriptor
+from .difference import Difference, Wrapper
+
+
+modified_netifcon_record = namedtuple("modified_netifcon", ["rule",
+ "added_context",
+ "removed_context",
+ "added_packet",
+ "removed_packet"])
+
+
+class NetifconsDifference(Difference):
+
+ """Determine the difference in netifcons between two policies."""
+
+ added_netifcons = DiffResultDescriptor("diff_netifcons")
+ removed_netifcons = DiffResultDescriptor("diff_netifcons")
+ modified_netifcons = DiffResultDescriptor("diff_netifcons")
+
+ def diff_netifcons(self):
+ """Generate the difference in netifcons between the policies."""
+
+ self.log.info("Generating netifcon differences from {0.left_policy} to {0.right_policy}".
+ format(self))
+
+ self.added_netifcons, self.removed_netifcons, matched_netifcons = self._set_diff(
+ (NetifconWrapper(n) for n in self.left_policy.netifcons()),
+ (NetifconWrapper(n) for n in self.right_policy.netifcons()))
+
+ self.modified_netifcons = []
+
+ for left_netifcon, right_netifcon in matched_netifcons:
+ # Criteria for modified netifcons
+ # 1. change to context
+ # 2. change to packet context
+ if ContextWrapper(left_netifcon.context) != ContextWrapper(right_netifcon.context):
+ removed_context = left_netifcon.context
+ added_context = right_netifcon.context
+ else:
+ removed_context = None
+ added_context = None
+
+ if ContextWrapper(left_netifcon.packet) != ContextWrapper(right_netifcon.packet):
+ removed_packet = left_netifcon.packet
+ added_packet = right_netifcon.packet
+ else:
+ removed_packet = None
+ added_packet = None
+
+ if removed_context or removed_packet:
+ self.modified_netifcons.append(modified_netifcon_record(
+ left_netifcon, added_context, removed_context, added_packet, removed_packet))
+
+ #
+ # Internal functions
+ #
+ def _reset_diff(self):
+ """Reset diff results on policy changes."""
+ self.log.debug("Resetting netifcon differences")
+ self.added_netifcons = None
+ self.removed_netifcons = None
+ self.modified_netifcons = None
+
+
+class NetifconWrapper(Wrapper):
+
+ """Wrap netifcon statements for diff purposes."""
+
+ def __init__(self, ocon):
+ self.origin = ocon
+ self.netif = ocon.netif
+ self.key = hash(ocon)
+
+ def __hash__(self):
+ return self.key
+
+ def __lt__(self, other):
+ return self.netif < other.netif
+
+ def __eq__(self, other):
+ return self.netif == other.netif
diff --git a/lib/python2.7/site-packages/setools/diff/nodecon.py b/lib/python2.7/site-packages/setools/diff/nodecon.py
new file mode 100644
index 0000000..6e94c9e
--- /dev/null
+++ b/lib/python2.7/site-packages/setools/diff/nodecon.py
@@ -0,0 +1,90 @@
+# 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 .context import ContextWrapper
+from .descriptors import DiffResultDescriptor
+from .difference import Difference, Wrapper
+
+
+modified_nodecon_record = namedtuple("modified_nodecon", ["rule",
+ "added_context",
+ "removed_context"])
+
+
+class NodeconsDifference(Difference):
+
+ """Determine the difference in nodecons between two policies."""
+
+ added_nodecons = DiffResultDescriptor("diff_nodecons")
+ removed_nodecons = DiffResultDescriptor("diff_nodecons")
+ modified_nodecons = DiffResultDescriptor("diff_nodecons")
+
+ def diff_nodecons(self):
+ """Generate the difference in nodecons between the policies."""
+
+ self.log.info("Generating nodecon differences from {0.left_policy} to {0.right_policy}".
+ format(self))
+
+ self.added_nodecons, self.removed_nodecons, matched_nodecons = self._set_diff(
+ (NodeconWrapper(n) for n in self.left_policy.nodecons()),
+ (NodeconWrapper(n) for n in self.right_policy.nodecons()))
+
+ self.modified_nodecons = []
+
+ for left_nodecon, right_nodecon in matched_nodecons:
+ # Criteria for modified nodecons
+ # 1. change to context
+ if ContextWrapper(left_nodecon.context) != ContextWrapper(right_nodecon.context):
+ self.modified_nodecons.append(modified_nodecon_record(left_nodecon,
+ right_nodecon.context,
+ left_nodecon.context))
+
+ #
+ # Internal functions
+ #
+ def _reset_diff(self):
+ """Reset diff results on policy changes."""
+ self.log.debug("Resetting nodecon differences")
+ self.added_nodecons = None
+ self.removed_nodecons = None
+ self.modified_nodecons = None
+
+
+class NodeconWrapper(Wrapper):
+
+ """Wrap nodecon statements for diff purposes."""
+
+ def __init__(self, ocon):
+ self.origin = ocon
+ self.ip_version = ocon.ip_version
+ self.address = ocon.address
+ self.netmask = ocon.netmask
+ self.key = hash(ocon)
+
+ def __hash__(self):
+ return self.key
+
+ def __lt__(self, other):
+ return self.origin < other.origin
+
+ def __eq__(self, other):
+ return self.ip_version == other.ip_version and \
+ self.address == other.address and \
+ self.netmask == other.netmask
diff --git a/lib/python2.7/site-packages/setools/diff/objclass.py b/lib/python2.7/site-packages/setools/diff/objclass.py
new file mode 100644
index 0000000..6a12de4
--- /dev/null
+++ b/lib/python2.7/site-packages/setools/diff/objclass.py
@@ -0,0 +1,86 @@
+# 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/>.
+#
+from collections import namedtuple
+
+from ..policyrep.exception import NoCommon
+
+from .descriptors import DiffResultDescriptor
+from .difference import Difference, SymbolWrapper
+
+
+modified_classes_record = namedtuple("modified_class", ["added_perms",
+ "removed_perms",
+ "matched_perms"])
+
+
+class ObjClassDifference(Difference):
+
+ """
+ Determine the difference in object classes
+ between two policies.
+ """
+
+ added_classes = DiffResultDescriptor("diff_classes")
+ removed_classes = DiffResultDescriptor("diff_classes")
+ modified_classes = DiffResultDescriptor("diff_classes")
+
+ def diff_classes(self):
+ """Generate the difference in object classes between the policies."""
+
+ self.log.info(
+ "Generating class differences from {0.left_policy} to {0.right_policy}".format(self))
+
+ self.added_classes, self.removed_classes, matched_classes = self._set_diff(
+ (SymbolWrapper(c) for c in self.left_policy.classes()),
+ (SymbolWrapper(c) for c in self.right_policy.classes()))
+
+ self.modified_classes = dict()
+
+ for left_class, right_class in matched_classes:
+ # Criteria for modified classes
+ # 1. change to permissions (inherited common is expanded)
+
+ left_perms = left_class.perms
+ try:
+ left_perms |= left_class.common.perms
+ except NoCommon:
+ pass
+
+ right_perms = right_class.perms
+ try:
+ right_perms |= right_class.common.perms
+ except NoCommon:
+ pass
+
+ added_perms, removed_perms, matched_perms = self._set_diff(left_perms, right_perms)
+
+ if added_perms or removed_perms:
+ self.modified_classes[left_class] = modified_classes_record(added_perms,
+ removed_perms,
+ matched_perms)
+
+ #
+ # Internal functions
+ #
+ def _reset_diff(self):
+ """Reset diff results on policy changes."""
+ self.log.debug("Resetting object class differences")
+ self.added_classes = None
+ self.removed_classes = None
+ self.modified_classes = None
diff --git a/lib/python2.7/site-packages/setools/diff/polcap.py b/lib/python2.7/site-packages/setools/diff/polcap.py
new file mode 100644
index 0000000..9c0afe2
--- /dev/null
+++ b/lib/python2.7/site-packages/setools/diff/polcap.py
@@ -0,0 +1,47 @@
+# 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 .descriptors import DiffResultDescriptor
+from .difference import Difference, SymbolWrapper
+
+
+class PolCapsDifference(Difference):
+
+ """Determine the difference in polcaps between two policies."""
+
+ added_polcaps = DiffResultDescriptor("diff_polcaps")
+ removed_polcaps = DiffResultDescriptor("diff_polcaps")
+
+ def diff_polcaps(self):
+ """Generate the difference in polcaps between the policies."""
+
+ self.log.info("Generating policy cap differences from {0.left_policy} to {0.right_policy}".
+ format(self))
+
+ self.added_polcaps, self.removed_polcaps, _ = self._set_diff(
+ (SymbolWrapper(n) for n in self.left_policy.polcaps()),
+ (SymbolWrapper(n) for n in self.right_policy.polcaps()))
+
+ #
+ # Internal functions
+ #
+ def _reset_diff(self):
+ """Reset diff results on policy changes."""
+ self.log.debug("Resetting policy capability differences")
+ self.added_polcaps = None
+ self.removed_polcaps = None
diff --git a/lib/python2.7/site-packages/setools/diff/portcon.py b/lib/python2.7/site-packages/setools/diff/portcon.py
new file mode 100644
index 0000000..17df377
--- /dev/null
+++ b/lib/python2.7/site-packages/setools/diff/portcon.py
@@ -0,0 +1,89 @@
+# 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 .context import ContextWrapper
+from .descriptors import DiffResultDescriptor
+from .difference import Difference, Wrapper
+
+
+modified_portcon_record = namedtuple("modified_portcon", ["rule",
+ "added_context",
+ "removed_context"])
+
+
+class PortconsDifference(Difference):
+
+ """Determine the difference in portcons between two policies."""
+
+ added_portcons = DiffResultDescriptor("diff_portcons")
+ removed_portcons = DiffResultDescriptor("diff_portcons")
+ modified_portcons = DiffResultDescriptor("diff_portcons")
+
+ def diff_portcons(self):
+ """Generate the difference in portcons between the policies."""
+
+ self.log.info("Generating portcon differences from {0.left_policy} to {0.right_policy}".
+ format(self))
+
+ self.added_portcons, self.removed_portcons, matched_portcons = self._set_diff(
+ (PortconWrapper(n) for n in self.left_policy.portcons()),
+ (PortconWrapper(n) for n in self.right_policy.portcons()))
+
+ self.modified_portcons = []
+
+ for left_portcon, right_portcon in matched_portcons:
+ # Criteria for modified portcons
+ # 1. change to context
+ if ContextWrapper(left_portcon.context) != ContextWrapper(right_portcon.context):
+ self.modified_portcons.append(modified_portcon_record(left_portcon,
+ right_portcon.context,
+ left_portcon.context))
+
+ #
+ # Internal functions
+ #
+ def _reset_diff(self):
+ """Reset diff results on policy changes."""
+ self.log.debug("Resetting portcon differences")
+ self.added_portcons = None
+ self.removed_portcons = None
+ self.modified_portcons = None
+
+
+class PortconWrapper(Wrapper):
+
+ """Wrap portcon statements for diff purposes."""
+
+ def __init__(self, ocon):
+ self.origin = ocon
+ self.protocol = ocon.protocol
+ self.low, self.high = ocon.ports
+ self.key = hash(ocon)
+
+ def __hash__(self):
+ return self.key
+
+ def __lt__(self, other):
+ return self.origin < other.origin
+
+ def __eq__(self, other):
+ return self.protocol == other.protocol and \
+ self.low == other.low and \
+ self.high == other.high
diff --git a/lib/python2.7/site-packages/setools/diff/properties.py b/lib/python2.7/site-packages/setools/diff/properties.py
new file mode 100644
index 0000000..8cd4c13
--- /dev/null
+++ b/lib/python2.7/site-packages/setools/diff/properties.py
@@ -0,0 +1,64 @@
+# 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
+
+
+modified_properties_record = namedtuple("modified_property", ["property", "added", "removed"])
+
+
+class PropertiesDifference(Difference):
+
+ """
+ Determine the difference in policy properties
+ (unknown permissions, MLS, etc.) between two policies.
+ """
+
+ modified_properties = DiffResultDescriptor("diff_properties")
+
+ def diff_properties(self):
+ self.modified_properties = []
+
+ if self.left_policy.handle_unknown != self.right_policy.handle_unknown:
+ self.modified_properties.append(
+ modified_properties_record("handle_unknown",
+ self.right_policy.handle_unknown,
+ self.left_policy.handle_unknown))
+
+ if self.left_policy.mls != self.right_policy.mls:
+ self.modified_properties.append(
+ modified_properties_record("MLS",
+ self.right_policy.mls,
+ self.left_policy.mls))
+
+ if self.left_policy.version != self.right_policy.version:
+ self.modified_properties.append(
+ modified_properties_record("version",
+ self.right_policy.version,
+ self.left_policy.version))
+
+ #
+ # Internal functions
+ #
+ def _reset_diff(self):
+ """Reset diff results on policy changes."""
+ self.log.debug("Resetting property differences")
+ self.modified_properties = None
diff --git a/lib/python2.7/site-packages/setools/diff/rbacrules.py b/lib/python2.7/site-packages/setools/diff/rbacrules.py
new file mode 100644
index 0000000..8a51b88
--- /dev/null
+++ b/lib/python2.7/site-packages/setools/diff/rbacrules.py
@@ -0,0 +1,189 @@
+# 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_rbacrule_record = namedtuple("modified_rbacrule", ["rule",
+ "added_default",
+ "removed_default"])
+
+
+class RBACRulesDifference(Difference):
+
+ """Determine the difference in RBAC rules between two policies."""
+
+ added_role_allows = DiffResultDescriptor("diff_role_allows")
+ removed_role_allows = DiffResultDescriptor("diff_role_allows")
+ # role allows cannot be modified, only added/removed
+
+ added_role_transitions = DiffResultDescriptor("diff_role_transitions")
+ removed_role_transitions = DiffResultDescriptor("diff_role_transitions")
+ modified_role_transitions = DiffResultDescriptor("diff_role_transitions")
+
+ # Lists of rules for each policy
+ _left_role_allows = None
+ _right_role_allows = None
+
+ _left_role_transitions = None
+ _right_role_transitions = None
+
+ def diff_role_allows(self):
+ """Generate the difference in role allow rules between the policies."""
+
+ self.log.info(
+ "Generating role allow differences from {0.left_policy} to {0.right_policy}".
+ format(self))
+
+ if self._left_role_allows is None or self._right_role_allows is None:
+ self._create_rbac_rule_lists()
+
+ self.added_role_allows, self.removed_role_allows, _ = \
+ self._set_diff(self._expand_generator(self._left_role_allows, RoleAllowWrapper),
+ self._expand_generator(self._right_role_allows, RoleAllowWrapper))
+
+ def diff_role_transitions(self):
+ """Generate the difference in role_transition rules between the policies."""
+
+ self.log.info(
+ "Generating role_transition differences from {0.left_policy} to {0.right_policy}".
+ format(self))
+
+ if self._left_role_transitions is None or self._right_role_transitions is None:
+ self._create_rbac_rule_lists()
+
+ self.added_role_transitions, \
+ self.removed_role_transitions, \
+ self.modified_role_transitions = self._diff_rbac_rules(
+ self._expand_generator(self._left_role_transitions, RoleTransitionWrapper),
+ self._expand_generator(self._right_role_transitions, RoleTransitionWrapper))
+
+ #
+ # Internal functions
+ #
+ def _create_rbac_rule_lists(self):
+ """Create rule lists for both policies."""
+ self._left_role_allows = []
+ self._left_role_transitions = []
+ for rule in self.left_policy.rbacrules():
+ # do not expand yet, to keep memory
+ # use down as long as possible
+ if rule.ruletype == "allow":
+ self._left_role_allows.append(rule)
+ elif rule.ruletype == "role_transition":
+ self._left_role_transitions.append(rule)
+ else:
+ self.log.error("Unknown rule type: {0} (This is an SETools bug)".
+ format(rule.ruletype))
+
+ self._right_role_allows = []
+ self._right_role_transitions = []
+ for rule in self.right_policy.rbacrules():
+ # do not expand yet, to keep memory
+ # use down as long as possible
+ if rule.ruletype == "allow":
+ self._right_role_allows.append(rule)
+ elif rule.ruletype == "role_transition":
+ self._right_role_transitions.append(rule)
+ else:
+ self.log.error("Unknown rule type: {0} (This is an SETools bug)".
+ format(rule.ruletype))
+
+ def _diff_rbac_rules(self, left_list, right_list):
+ """Common method for comparing rbac 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 role
+ if SymbolWrapper(left_rule.default) != SymbolWrapper(right_rule.default):
+ modified.append(modified_rbacrule_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 RBAC rule differences")
+ self.added_role_allows = None
+ self.removed_role_allows = None
+ self.modified_role_allows = None
+ self.added_role_transitions = None
+ self.removed_role_transitions = None
+ self.modified_role_transitions = None
+
+ # Sets of rules for each policy
+ self._left_role_allows = None
+ self._right_role_allows = None
+ self._left_role_transitions = None
+ self._right_role_transitions = None
+
+
+class RoleAllowWrapper(Wrapper):
+
+ """Wrap role allow 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.key = hash(rule)
+
+ def __hash__(self):
+ return self.key
+
+ def __lt__(self, other):
+ return self.key < other.key
+
+ def __eq__(self, other):
+ # because RBACRuleDifference groups rules by ruletype,
+ # the ruletype always matches.
+ return self.source == other.source and self.target == other.target
+
+
+class RoleTransitionWrapper(Wrapper):
+
+ """Wrap role_transition 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 RBACRuleDifference 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/lib/python2.7/site-packages/setools/diff/roles.py b/lib/python2.7/site-packages/setools/diff/roles.py
new file mode 100644
index 0000000..38ca84e
--- /dev/null
+++ b/lib/python2.7/site-packages/setools/diff/roles.py
@@ -0,0 +1,71 @@
+# 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/>.
+#
+from collections import namedtuple
+
+from .descriptors import DiffResultDescriptor
+from .difference import Difference, SymbolWrapper
+
+
+modified_roles_record = namedtuple("modified_role", ["added_types",
+ "removed_types",
+ "matched_types"])
+
+
+class RolesDifference(Difference):
+
+ """Determine the difference in roles between two policies."""
+
+ added_roles = DiffResultDescriptor("diff_roles")
+ removed_roles = DiffResultDescriptor("diff_roles")
+ modified_roles = DiffResultDescriptor("diff_roles")
+
+ def diff_roles(self):
+ """Generate the difference in roles between the policies."""
+
+ self.log.info(
+ "Generating role differences from {0.left_policy} to {0.right_policy}".format(self))
+
+ self.added_roles, self.removed_roles, matched_roles = self._set_diff(
+ (SymbolWrapper(r) for r in self.left_policy.roles()),
+ (SymbolWrapper(r) for r in self.right_policy.roles()))
+
+ self.modified_roles = dict()
+
+ for left_role, right_role in matched_roles:
+ # Criteria for modified roles
+ # 1. change to type set, or
+ # 2. change to attribute set (not implemented)
+ added_types, removed_types, matched_types = self._set_diff(
+ (SymbolWrapper(t) for t in left_role.types()),
+ (SymbolWrapper(t) for t in right_role.types()))
+
+ if added_types or removed_types:
+ self.modified_roles[left_role] = modified_roles_record(added_types,
+ removed_types,
+ matched_types)
+
+ #
+ # Internal functions
+ #
+ def _reset_diff(self):
+ """Reset diff results on policy changes."""
+ self.log.debug("Resetting role differences")
+ self.added_roles = None
+ self.removed_roles = None
+ self.modified_roles = None
diff --git a/lib/python2.7/site-packages/setools/diff/terules.py b/lib/python2.7/site-packages/setools/diff/terules.py
new file mode 100644
index 0000000..179e5ec
--- /dev/null
+++ b/lib/python2.7/site-packages/setools/diff/terules.py
@@ -0,0 +1,418 @@
+# Copyright 2015-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 ..policyrep.exception import RuleNotConditional, RuleUseError, TERuleNoFilename
+
+from .conditional import ConditionalExprWrapper
+from .descriptors import DiffResultDescriptor
+from .difference import Difference, SymbolWrapper, Wrapper
+
+
+modified_avrule_record = namedtuple("modified_avrule", ["rule",
+ "added_perms",
+ "removed_perms",
+ "matched_perms"])
+
+modified_terule_record = namedtuple("modified_terule", ["rule", "added_default", "removed_default"])
+
+
+class TERulesDifference(Difference):
+
+ """
+ Determine the difference in type enforcement rules
+ between two policies.
+ """
+
+ added_allows = DiffResultDescriptor("diff_allows")
+ removed_allows = DiffResultDescriptor("diff_allows")
+ modified_allows = DiffResultDescriptor("diff_allows")
+
+ added_auditallows = DiffResultDescriptor("diff_auditallows")
+ removed_auditallows = DiffResultDescriptor("diff_auditallows")
+ modified_auditallows = DiffResultDescriptor("diff_auditallows")
+
+ added_neverallows = DiffResultDescriptor("diff_neverallows")
+ removed_neverallows = DiffResultDescriptor("diff_neverallows")
+ modified_neverallows = DiffResultDescriptor("diff_neverallows")
+
+ added_dontaudits = DiffResultDescriptor("diff_dontaudits")
+ removed_dontaudits = DiffResultDescriptor("diff_dontaudits")
+ modified_dontaudits = DiffResultDescriptor("diff_dontaudits")
+
+ added_type_transitions = DiffResultDescriptor("diff_type_transitions")
+ removed_type_transitions = DiffResultDescriptor("diff_type_transitions")
+ modified_type_transitions = DiffResultDescriptor("diff_type_transitions")
+
+ added_type_changes = DiffResultDescriptor("diff_type_changes")
+ removed_type_changes = DiffResultDescriptor("diff_type_changes")
+ modified_type_changes = DiffResultDescriptor("diff_type_changes")
+
+ added_type_members = DiffResultDescriptor("diff_type_members")
+ removed_type_members = DiffResultDescriptor("diff_type_members")
+ modified_type_members = DiffResultDescriptor("diff_type_members")
+
+ # Lists of rules for each policy
+ _left_allows = None
+ _right_allows = None
+
+ _left_auditallows = None
+ _right_auditallows = None
+
+ _left_neverallows = None
+ _right_neverallows = None
+
+ _left_dontaudits = None
+ _right_dontaudits = None
+
+ _left_type_transitions = None
+ _right_type_transitions = None
+
+ _left_type_changes = None
+ _right_type_changes = None
+
+ _left_type_members = None
+ _right_type_members = None
+
+ def diff_allows(self):
+ """Generate the difference in allow rules between the policies."""
+
+ self.log.info(
+ "Generating allow differences from {0.left_policy} to {0.right_policy}".format(self))
+
+ if self._left_allows is None or self._right_allows is None:
+ self._create_te_rule_lists()
+
+ self.added_allows, self.removed_allows, self.modified_allows = self._diff_av_rules(
+ self._expand_generator(self._left_allows, AVRuleWrapper),
+ self._expand_generator(self._right_allows, AVRuleWrapper))
+
+ def diff_auditallows(self):
+ """Generate the difference in auditallow rules between the policies."""
+
+ self.log.info(
+ "Generating auditallow differences from {0.left_policy} to {0.right_policy}".
+ format(self))
+
+ if self._left_auditallows is None or self._right_auditallows is None:
+ self._create_te_rule_lists()
+
+ self.added_auditallows, \
+ self.removed_auditallows, \
+ self.modified_auditallows = self._diff_av_rules(
+ self._expand_generator(self._left_auditallows, AVRuleWrapper),
+ self._expand_generator(self._right_auditallows, AVRuleWrapper))
+
+ def diff_neverallows(self):
+ """Generate the difference in neverallow rules between the policies."""
+
+ self.log.info(
+ "Generating neverallow differences from {0.left_policy} to {0.right_policy}".
+ format(self))
+
+ if self._left_neverallows is None or self._right_neverallows is None:
+ self._create_te_rule_lists()
+
+ self.added_neverallows, \
+ self.removed_neverallows, \
+ self.modified_neverallows = self._diff_av_rules(
+ self._expand_generator(self._left_neverallows, AVRuleWrapper),
+ self._expand_generator(self._right_neverallows, AVRuleWrapper))
+
+ def diff_dontaudits(self):
+ """Generate the difference in dontaudit rules between the policies."""
+
+ self.log.info(
+ "Generating dontaudit differences from {0.left_policy} to {0.right_policy}".
+ format(self))
+
+ if self._left_dontaudits is None or self._right_dontaudits is None:
+ self._create_te_rule_lists()
+
+ self.added_dontaudits, \
+ self.removed_dontaudits, \
+ self.modified_dontaudits = self._diff_av_rules(
+ self._expand_generator(self._left_dontaudits, AVRuleWrapper),
+ self._expand_generator(self._right_dontaudits, AVRuleWrapper))
+
+ def diff_type_transitions(self):
+ """Generate the difference in type_transition rules between the policies."""
+
+ self.log.info(
+ "Generating type_transition differences from {0.left_policy} to {0.right_policy}".
+ format(self))
+
+ if self._left_type_transitions is None or self._right_type_transitions is None:
+ self._create_te_rule_lists()
+
+ self.added_type_transitions, \
+ self.removed_type_transitions, \
+ self.modified_type_transitions = self._diff_te_rules(
+ self._expand_generator(self._left_type_transitions, TERuleWrapper),
+ self._expand_generator(self._right_type_transitions, TERuleWrapper))
+
+ def diff_type_changes(self):
+ """Generate the difference in type_change rules between the policies."""
+
+ self.log.info(
+ "Generating type_change differences from {0.left_policy} to {0.right_policy}".
+ format(self))
+
+ if self._left_type_changes is None or self._right_type_changes is None:
+ self._create_te_rule_lists()
+
+ self.added_type_changes, \
+ self.removed_type_changes, \
+ self.modified_type_changes = self._diff_te_rules(
+ self._expand_generator(self._left_type_changes, TERuleWrapper),
+ self._expand_generator(self._right_type_changes, TERuleWrapper))
+
+ def diff_type_members(self):
+ """Generate the difference in type_member rules between the policies."""
+
+ self.log.info(
+ "Generating type_member differences from {0.left_policy} to {0.right_policy}".
+ format(self))
+
+ if self._left_type_members is None or self._right_type_members is None:
+ self._create_te_rule_lists()
+
+ self.added_type_members, \
+ self.removed_type_members, \
+ self.modified_type_members = self._diff_te_rules(
+ self._expand_generator(self._left_type_members, TERuleWrapper),
+ self._expand_generator(self._right_type_members, TERuleWrapper))
+
+ #
+ # Internal functions
+ #
+ def _create_te_rule_lists(self):
+ """Create rule lists for both policies."""
+
+ self._left_allows = []
+ self._left_auditallows = []
+ self._left_neverallows = []
+ self._left_dontaudits = []
+ self._left_type_transitions = []
+ self._left_type_changes = []
+ self._left_type_members = []
+ for rule in self.left_policy.terules():
+ # do not expand yet, to keep memory
+ # use down as long as possible
+ if rule.ruletype == "allow":
+ self._left_allows.append(rule)
+ elif rule.ruletype == "auditallow":
+ self._left_auditallows.append(rule)
+ elif rule.ruletype == "neverallow":
+ self._left_neverallows.append(rule)
+ elif rule.ruletype == "dontaudit":
+ self._left_dontaudits.append(rule)
+ elif rule.ruletype == "type_transition":
+ self._left_type_transitions.append(rule)
+ elif rule.ruletype == "type_change":
+ self._left_type_changes.append(rule)
+ elif rule.ruletype == "type_member":
+ self._left_type_members.append(rule)
+ else:
+ self.log.error("Unknown rule type: {0} (This is an SETools bug)".
+ format(rule.ruletype))
+
+ self._right_allows = []
+ self._right_auditallows = []
+ self._right_neverallows = []
+ self._right_dontaudits = []
+ self._right_type_transitions = []
+ self._right_type_changes = []
+ self._right_type_members = []
+ for rule in self.right_policy.terules():
+ # do not expand yet, to keep memory
+ # use down as long as possible
+ if rule.ruletype == "allow":
+ self._right_allows.append(rule)
+ elif rule.ruletype == "auditallow":
+ self._right_auditallows.append(rule)
+ elif rule.ruletype == "neverallow":
+ self._right_neverallows.append(rule)
+ elif rule.ruletype == "dontaudit":
+ self._right_dontaudits.append(rule)
+ elif rule.ruletype == "type_transition":
+ self._right_type_transitions.append(rule)
+ elif rule.ruletype == "type_change":
+ self._right_type_changes.append(rule)
+ elif rule.ruletype == "type_member":
+ self._right_type_members.append(rule)
+ else:
+ self.log.error("Unknown rule type: {0} (This is an SETools bug)".
+ format(rule.ruletype))
+
+ def _diff_av_rules(self, left_list, right_list):
+ """Common method for comparing access vector 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 permissions
+ added_perms, removed_perms, matched_perms = self._set_diff(left_rule.perms,
+ right_rule.perms)
+
+ # the final set comprehension is to avoid having lists
+ # like [("perm1", "perm1"), ("perm2", "perm2")], as the
+ # matched_perms return from _set_diff is a set of tuples
+ if added_perms or removed_perms:
+ modified.append(modified_avrule_record(left_rule,
+ added_perms,
+ removed_perms,
+ set(p[0] for p in matched_perms)))
+
+ return added, removed, modified
+
+ def _diff_te_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 type
+ if SymbolWrapper(left_rule.default) != SymbolWrapper(right_rule.default):
+ modified.append(modified_terule_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 TE rule differences")
+ self.added_allows = None
+ self.removed_allows = None
+ self.modified_allows = None
+ self.added_auditallows = None
+ self.removed_auditallows = None
+ self.modified_auditallows = None
+ self.added_neverallows = None
+ self.removed_neverallows = None
+ self.modified_neverallows = None
+ self.added_dontaudits = None
+ self.removed_dontaudits = None
+ self.modified_dontaudits = None
+ self.added_type_transitions = None
+ self.removed_type_transitions = None
+ self.modified_type_transitions = None
+ self.added_type_changes = None
+ self.removed_type_changes = None
+ self.modified_type_changes = None
+ self.added_type_members = None
+ self.removed_type_members = None
+ self.modified_type_members = None
+
+ # Sets of rules for each policy
+ self._left_allows = None
+ self._right_allows = None
+ self._left_auditallows = None
+ self._right_auditallows = None
+ self._left_neverallows = None
+ self._right_neverallows = None
+ self._left_dontaudits = None
+ self._right_dontaudits = None
+ self._left_type_transitions = None
+ self._right_type_transitions = None
+ self._left_type_changes = None
+ self._right_type_changes = None
+ self._left_type_members = None
+ self._right_type_members = None
+
+
+class AVRuleWrapper(Wrapper):
+
+ """Wrap access vector 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)
+
+ try:
+ self.conditional = ConditionalExprWrapper(rule.conditional)
+ self.conditional_block = rule.conditional_block
+ except RuleNotConditional:
+ self.conditional = None
+ self.conditional_block = None
+
+ def __hash__(self):
+ return self.key
+
+ def __lt__(self, other):
+ return self.key < other.key
+
+ def __eq__(self, other):
+ # because TERuleDifference groups rules by ruletype,
+ # the ruletype always matches.
+ return self.source == other.source and \
+ self.target == other.target and \
+ self.tclass == other.tclass and \
+ self.conditional == other.conditional and \
+ self.conditional_block == other.conditional_block
+
+
+class TERuleWrapper(Wrapper):
+
+ """Wrap type_* 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)
+
+ try:
+ self.conditional = ConditionalExprWrapper(rule.conditional)
+ self.conditional_block = rule.conditional_block
+ except RuleNotConditional:
+ self.conditional = None
+ self.conditional_block = None
+
+ try:
+ self.filename = rule.filename
+ except (RuleUseError, TERuleNoFilename):
+ self.filename = None
+
+ def __hash__(self):
+ return self.key
+
+ def __lt__(self, other):
+ return self.key < other.key
+
+ def __eq__(self, other):
+ # because TERuleDifference groups rules by ruletype,
+ # the ruletype always matches.
+ return self.source == other.source and \
+ self.target == other.target and \
+ self.tclass == other.tclass and \
+ self.conditional == other.conditional and \
+ self.conditional_block == other.conditional_block and \
+ self.filename == self.filename
diff --git a/lib/python2.7/site-packages/setools/diff/typeattr.py b/lib/python2.7/site-packages/setools/diff/typeattr.py
new file mode 100644
index 0000000..8c51832
--- /dev/null
+++ b/lib/python2.7/site-packages/setools/diff/typeattr.py
@@ -0,0 +1,71 @@
+# 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
+
+
+modified_typeattr_record = namedtuple("modified_typeattr", ["added_types",
+ "removed_types",
+ "matched_types"])
+
+
+class TypeAttributesDifference(Difference):
+
+ """Determine the difference in type attributes between two policies."""
+
+ added_type_attributes = DiffResultDescriptor("diff_type_attributes")
+ removed_type_attributes = DiffResultDescriptor("diff_type_attributes")
+ modified_type_attributes = DiffResultDescriptor("diff_type_attributes")
+
+ def diff_type_attributes(self):
+ """Generate the difference in type attributes between the policies."""
+
+ self.log.info(
+ "Generating type attribute differences from {0.left_policy} to {0.right_policy}".
+ format(self))
+
+ self.added_type_attributes, self.removed_type_attributes, matched_attributes = \
+ self._set_diff(
+ (SymbolWrapper(r) for r in self.left_policy.typeattributes()),
+ (SymbolWrapper(r) for r in self.right_policy.typeattributes()))
+
+ self.modified_type_attributes = dict()
+
+ for left_attribute, right_attribute in matched_attributes:
+ # Criteria for modified attributes
+ # 1. change to type set
+ added_types, removed_types, matched_types = self._set_diff(
+ (SymbolWrapper(t) for t in left_attribute.expand()),
+ (SymbolWrapper(t) for t in right_attribute.expand()))
+
+ if added_types or removed_types:
+ self.modified_type_attributes[left_attribute] = modified_typeattr_record(
+ added_types, removed_types, matched_types)
+
+ #
+ # Internal functions
+ #
+ def _reset_diff(self):
+ """Reset diff results on policy changes."""
+ self.log.debug("Resetting type attribute differences")
+ self.added_type_attributes = None
+ self.removed_type_attributes = None
+ self.modified_type_attributes = None
diff --git a/lib/python2.7/site-packages/setools/diff/types.py b/lib/python2.7/site-packages/setools/diff/types.py
new file mode 100644
index 0000000..d0e99d9
--- /dev/null
+++ b/lib/python2.7/site-packages/setools/diff/types.py
@@ -0,0 +1,89 @@
+# 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/>.
+#
+from collections import namedtuple
+
+from .descriptors import DiffResultDescriptor
+from .difference import Difference, SymbolWrapper
+
+
+modified_types_record = namedtuple("modified_type", ["added_attributes",
+ "removed_attributes",
+ "matched_attributes",
+ "modified_permissive",
+ "permissive",
+ "added_aliases",
+ "removed_aliases",
+ "matched_aliases"])
+
+
+class TypesDifference(Difference):
+
+ """Determine the difference in types between two policies."""
+
+ 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(
+ (SymbolWrapper(t) for t in self.left_policy.types()),
+ (SymbolWrapper(t) for t in self.right_policy.types()))
+
+ self.modified_types = dict()
+
+ for left_type, right_type in matched_types:
+ # Criteria for modified types
+ # 1. change to attribute set, or
+ # 2. change to alias set, or
+ # 3. different permissive setting
+ added_attr, removed_attr, matched_attr = self._set_diff(
+ (SymbolWrapper(a) for a in left_type.attributes()),
+ (SymbolWrapper(a) for a in 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[left_type] = 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.log.debug("Resetting type differences")
+ self.added_types = None
+ self.removed_types = None
+ self.modified_types = None
diff --git a/lib/python2.7/site-packages/setools/diff/users.py b/lib/python2.7/site-packages/setools/diff/users.py
new file mode 100644
index 0000000..78f3d3e
--- /dev/null
+++ b/lib/python2.7/site-packages/setools/diff/users.py
@@ -0,0 +1,121 @@
+# 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 ..policyrep.exception import MLSDisabled
+
+from .descriptors import DiffResultDescriptor
+from .difference import Difference, SymbolWrapper
+from .mls import LevelWrapper, RangeWrapper
+
+
+modified_users_record = namedtuple("modified_user", ["added_roles",
+ "removed_roles",
+ "matched_roles",
+ "added_level",
+ "removed_level",
+ "added_range",
+ "removed_range"])
+
+
+class UsersDifference(Difference):
+
+ """Determine the difference in users between two policies."""
+
+ added_users = DiffResultDescriptor("diff_users")
+ removed_users = DiffResultDescriptor("diff_users")
+ modified_users = DiffResultDescriptor("diff_users")
+
+ def diff_users(self):
+ """Generate the difference in users between the policies."""
+
+ self.log.info(
+ "Generating user differences from {0.left_policy} to {0.right_policy}".format(self))
+
+ self.added_users, self.removed_users, matched_users = self._set_diff(
+ (SymbolWrapper(r) for r in self.left_policy.users()),
+ (SymbolWrapper(r) for r in self.right_policy.users()))
+
+ self.modified_users = dict()
+
+ for left_user, right_user in matched_users:
+ # Criteria for modified users
+ # 1. change to role set, or
+ # 2. change to default level, or
+ # 3. change to range
+ added_roles, removed_roles, matched_roles = self._set_diff(
+ (SymbolWrapper(r) for r in left_user.roles),
+ (SymbolWrapper(r) for r in right_user.roles))
+
+ # keep wrapped and unwrapped MLS objects here so there
+ # are not several nested try blocks
+ try:
+ left_level_wrap = LevelWrapper(left_user.mls_level)
+ left_range_wrap = RangeWrapper(left_user.mls_range)
+ left_level = left_user.mls_level
+ left_range = left_user.mls_range
+ except MLSDisabled:
+ left_level_wrap = None
+ left_range_wrap = None
+ left_level = "None (MLS Disabled)"
+ left_range = "None (MLS Disabled)"
+
+ try:
+ right_level_wrap = LevelWrapper(right_user.mls_level)
+ right_range_wrap = RangeWrapper(right_user.mls_range)
+ right_level = right_user.mls_level
+ right_range = right_user.mls_range
+ except MLSDisabled:
+ right_level_wrap = None
+ right_range_wrap = None
+ right_level = "None (MLS Disabled)"
+ right_range = "None (MLS Disabled)"
+
+ if left_level_wrap != right_level_wrap:
+ added_level = right_level
+ removed_level = left_level
+ else:
+ added_level = None
+ removed_level = None
+
+ if left_range_wrap != right_range_wrap:
+ added_range = right_range
+ removed_range = left_range
+ else:
+ added_range = None
+ removed_range = None
+
+ if added_roles or removed_roles or removed_level or removed_range:
+ self.modified_users[left_user] = modified_users_record(added_roles,
+ removed_roles,
+ matched_roles,
+ added_level,
+ removed_level,
+ added_range,
+ removed_range)
+
+ #
+ # Internal functions
+ #
+ def _reset_diff(self):
+ """Reset diff results on policy changes."""
+ self.log.debug("Resetting user differences")
+ self.added_users = None
+ self.removed_users = None
+ self.modified_users = None
diff --git a/lib/python2.7/site-packages/setools/fsusequery.py b/lib/python2.7/site-packages/setools/fsusequery.py
index f79f50e..131a649 100644
--- a/lib/python2.7/site-packages/setools/fsusequery.py
+++ b/lib/python2.7/site-packages/setools/fsusequery.py
@@ -20,7 +20,7 @@ import logging
import re
from . import contextquery
-from .descriptors import CriteriaDescriptor, CriteriaSetDescriptor, RuletypeDescriptor
+from .descriptors import CriteriaDescriptor, CriteriaSetDescriptor
class FSUseQuery(contextquery.ContextQuery):
@@ -56,7 +56,7 @@ class FSUseQuery(contextquery.ContextQuery):
No effect if not using set operations.
"""
- ruletype = RuletypeDescriptor("validate_fs_use_ruletype")
+ ruletype = CriteriaSetDescriptor(lookup_function="validate_fs_use_ruletype")
fs = CriteriaDescriptor("fs_regex")
fs_regex = False
diff --git a/lib/python2.7/site-packages/setools/mlsrulequery.py b/lib/python2.7/site-packages/setools/mlsrulequery.py
index 3a9e1bf..615964e 100644
--- a/lib/python2.7/site-packages/setools/mlsrulequery.py
+++ b/lib/python2.7/site-packages/setools/mlsrulequery.py
@@ -19,7 +19,7 @@
import logging
from . import mixins, query
-from .descriptors import CriteriaDescriptor, CriteriaSetDescriptor, RuletypeDescriptor
+from .descriptors import CriteriaDescriptor, CriteriaSetDescriptor
class MLSRuleQuery(mixins.MatchObjClass, query.PolicyQuery):
@@ -43,7 +43,7 @@ class MLSRuleQuery(mixins.MatchObjClass, query.PolicyQuery):
matching the rule's object class.
"""
- ruletype = RuletypeDescriptor("validate_mls_ruletype")
+ ruletype = CriteriaSetDescriptor(lookup_function="validate_mls_ruletype")
source = CriteriaDescriptor("source_regex", "lookup_type_or_attr")
source_regex = False
target = CriteriaDescriptor("target_regex", "lookup_type_or_attr")
diff --git a/lib/python2.7/site-packages/setools/policyrep/__init__.py b/lib/python2.7/site-packages/setools/policyrep/__init__.py
index 3a4b9a1..5894cdb 100644
--- a/lib/python2.7/site-packages/setools/policyrep/__init__.py
+++ b/lib/python2.7/site-packages/setools/policyrep/__init__.py
@@ -211,6 +211,11 @@ class SELinuxPolicy(object):
return sum(1 for c in self.constraints() if c.ruletype == "constrain")
@property
+ def default_count(self):
+ """The number of default_* rules."""
+ return sum(1 for d in self.defaults())
+
+ @property
def dontaudit_count(self):
"""The number of dontaudit rules."""
return self.policy.avrule_dontaudit_count()
@@ -505,27 +510,42 @@ class SELinuxPolicy(object):
@staticmethod
def validate_constraint_ruletype(types):
"""Validate constraint types."""
- constraint.validate_ruletype(types)
+ return constraint.validate_ruletype(types)
+
+ @staticmethod
+ def validate_default_ruletype(types):
+ """Validate default_* types."""
+ return default.validate_ruletype(types)
+
+ @staticmethod
+ def validate_default_value(value):
+ """Validate default_* values."""
+ return default.validate_default_value(value)
+
+ @staticmethod
+ def validate_default_range(value):
+ """Validate default_range range."""
+ return default.validate_default_range(value)
@staticmethod
def validate_fs_use_ruletype(types):
"""Validate fs_use_* rule types."""
- fscontext.validate_ruletype(types)
+ return fscontext.validate_ruletype(types)
@staticmethod
def validate_mls_ruletype(types):
"""Validate MLS rule types."""
- mlsrule.validate_ruletype(types)
+ return mlsrule.validate_ruletype(types)
@staticmethod
def validate_rbac_ruletype(types):
"""Validate RBAC rule types."""
- rbacrule.validate_ruletype(types)
+ return rbacrule.validate_ruletype(types)
@staticmethod
def validate_te_ruletype(types):
"""Validate type enforcement rule types."""
- terule.validate_ruletype(types)
+ return terule.validate_ruletype(types)
#
# Constraints generators
diff --git a/lib/python2.7/site-packages/setools/policyrep/_qpol.py b/lib/python2.7/site-packages/setools/policyrep/_qpol.py
new file mode 100644
index 0000000..97a341e
--- /dev/null
+++ b/lib/python2.7/site-packages/setools/policyrep/_qpol.py
@@ -0,0 +1,7 @@
+def __bootstrap__():
+ global __bootstrap__, __loader__, __file__
+ import sys, pkg_resources, imp
+ __file__ = pkg_resources.resource_filename(__name__,'_qpol.so')
+ __loader__ = None; del __bootstrap__, __loader__
+ imp.load_dynamic(__name__,__file__)
+__bootstrap__()
diff --git a/lib/python2.7/site-packages/setools/policyrep/_qpol.so b/lib/python2.7/site-packages/setools/policyrep/_qpol.so
index aad9de3..f459582 100755
--- a/lib/python2.7/site-packages/setools/policyrep/_qpol.so
+++ b/lib/python2.7/site-packages/setools/policyrep/_qpol.so
Binary files differ
diff --git a/lib/python2.7/site-packages/setools/policyrep/boolcond.py b/lib/python2.7/site-packages/setools/policyrep/boolcond.py
index c3c0608..f7df2c5 100644
--- a/lib/python2.7/site-packages/setools/policyrep/boolcond.py
+++ b/lib/python2.7/site-packages/setools/policyrep/boolcond.py
@@ -16,10 +16,15 @@
# License along with SETools. If not, see
# <http://www.gnu.org/licenses/>.
#
+from itertools import product
+from collections import namedtuple
+
from . import exception
from . import qpol
from . import symbol
+truth_table_row = namedtuple("truth_table_row", ["values", "result"])
+
def boolean_factory(policy, name):
"""Factory function for creating Boolean statement objects."""
@@ -163,5 +168,76 @@ class ConditionalExpr(symbol.PolicySymbol):
return bools
+ def evaluate(self, **kwargs):
+ """
+ Evaluate the expression with the stated boolean values.
+
+ Keyword Parameters:
+ Each keyword parameter name corresponds to a boolean name
+ in the expression
+
+ Return: bool
+ """
+ bools = sorted(self.booleans)
+
+ if sorted(kwargs.keys()) != bools:
+ raise ValueError("Boolean values not set correctly.")
+
+ stack = []
+ for expr_node in self.qpol_symbol.expr_node_iter(self.policy):
+ expr_node_type = expr_node.expr_type(self.policy)
+
+ if expr_node_type == qpol.QPOL_COND_EXPR_BOOL:
+ nodebool = boolean_factory(self.policy, expr_node.get_boolean(self.policy))
+ stack.append(kwargs[nodebool])
+ elif expr_node_type == qpol.QPOL_COND_EXPR_NOT:
+ operand = stack.pop()
+ operator = self._cond_expr_val_to_text[expr_node_type]
+ stack.append(not operand)
+ else:
+ operand1 = stack.pop()
+ operand2 = stack.pop()
+ operator = self._cond_expr_val_to_text[expr_node_type]
+ if operator == "||":
+ stack.append(operand1 or operand2)
+ elif operator == "&&":
+ stack.append(operand1 and operand2)
+ elif operator == "^":
+ stack.append(operand1 ^ operand2)
+ elif operator == "==":
+ stack.append(operand1 == operand2)
+ else: # not equal
+ stack.append(operand1 != operand2)
+
+ return stack[0]
+
+ def truth_table(self):
+ """
+ Generate a truth table for this expression.
+
+ Return: list
+
+ List item:
+ tuple: values, result
+
+ Tuple item:
+ values: Dictionary keyed on Boolean names
+ with each value being T/F.
+ result: Evaluation result for the expression
+ given the values.
+ """
+ bools = sorted(str(b) for b in self.booleans)
+
+ truth_table = []
+
+ # create a list of all combinations of T/F for each Boolean
+ truth_list = list(product([True, False], repeat=len(bools)))
+
+ for row in truth_list:
+ values = {bools[i]: row[i] for i in range(len(bools))}
+ truth_table.append(truth_table_row(values, self.evaluate(**values)))
+
+ return truth_table
+
def statement(self):
raise exception.NoStatement
diff --git a/lib/python2.7/site-packages/setools/policyrep/constraint.py b/lib/python2.7/site-packages/setools/policyrep/constraint.py
index 9994c5b..abaa6d1 100644
--- a/lib/python2.7/site-packages/setools/policyrep/constraint.py
+++ b/lib/python2.7/site-packages/setools/policyrep/constraint.py
@@ -38,11 +38,12 @@ def _is_mls(policy, sym):
return False
-def validate_ruletype(types):
+def validate_ruletype(t):
"""Validate constraint rule types."""
- for t in types:
- if t not in ["constrain", "mlsconstrain", "validatetrans", "mlsvalidatetrans"]:
- raise exception.InvalidConstraintType("{0} is not a valid constraint type.".format(t))
+ if t not in ["constrain", "mlsconstrain", "validatetrans", "mlsvalidatetrans"]:
+ raise exception.InvalidConstraintType("{0} is not a valid constraint type.".format(t))
+
+ return t
def constraint_factory(policy, sym):
diff --git a/lib/python2.7/site-packages/setools/policyrep/default.py b/lib/python2.7/site-packages/setools/policyrep/default.py
index 175b709..40c7197 100644
--- a/lib/python2.7/site-packages/setools/policyrep/default.py
+++ b/lib/python2.7/site-packages/setools/policyrep/default.py
@@ -1,4 +1,4 @@
-# Copyright 2014, Tresys Technology, LLC
+# Copyright 2014, 2016 Tresys Technology, LLC
#
# This file is part of SETools.
#
@@ -22,6 +22,28 @@ from . import objclass
from . import qpol
+def validate_ruletype(t):
+ """Validate default_* rule types."""
+ if t not in ["default_role", "default_range", "default_type", "default_user"]:
+ raise exception.InvalidDefaultType("{0} is not a valid default_* rule type.".format(t))
+
+ return t
+
+
+def validate_default_value(default):
+ if default not in ["source", "target"]:
+ raise exception.InvalidDefaultValue("{0} is not a valid default_* value.".format(default))
+
+ return default
+
+
+def validate_default_range(default):
+ if default not in ["low", "high", "low_high"]:
+ raise exception.InvalidDefaultRange("{0} is not a valid default_* range.".format(default))
+
+ return default
+
+
def default_factory(policy, sym):
"""Factory generator for creating default_* statement objects."""
@@ -34,95 +56,73 @@ def default_factory(policy, sym):
raise NotImplementedError
# qpol will essentially iterate over all classes
- # and emit None for classes that don't set a default
+ # and emit None for classes that don't set a default.
+ # Because of all of this processing, extract almost
+ # all of the information out of the qpol representation.
+ # (we have to determine almost all of it anyway)
if not sym.object_class(policy):
raise exception.NoDefaults
- if sym.user_default(policy):
- yield UserDefault(policy, sym)
-
- if sym.role_default(policy):
- yield RoleDefault(policy, sym)
-
- if sym.type_default(policy):
- yield TypeDefault(policy, sym)
-
- if sym.range_default(policy):
- yield RangeDefault(policy, sym)
+ user = sym.user_default(policy)
+ role = sym.role_default(policy)
+ type_ = sym.type_default(policy)
+ range_ = sym.range_default(policy)
+
+ if user:
+ obj = Default(policy, sym)
+ obj.ruletype = "default_user"
+ obj.default = user
+ yield obj
+
+ if role:
+ obj = Default(policy, sym)
+ obj.ruletype = "default_role"
+ obj.default = role
+ yield obj
+
+ if type_:
+ obj = Default(policy, sym)
+ obj.ruletype = "default_type"
+ obj.default = type_
+ yield obj
+
+ if range_:
+ # range_ is something like "source low_high"
+ rng = range_.split()
+ obj = RangeDefault(policy, sym)
+ obj.ruletype = "default_range"
+ obj.default = rng[0]
+ obj.default_range = rng[1]
+ yield obj
class Default(symbol.PolicySymbol):
"""Base class for default_* statements."""
+ ruletype = None
+ default = None
+
def __str__(self):
- raise NotImplementedError
+ return "{0.ruletype} {0.tclass} {0.default};".format(self)
+
+ def __hash__(self):
+ return hash("{0.ruletype}|{0.tclass}".format(self))
@property
- def object_class(self):
+ def tclass(self):
"""The object class."""
return objclass.class_factory(self.policy, self.qpol_symbol.object_class(self.policy))
- @property
- def default(self):
- raise NotImplementedError
-
def statement(self):
return str(self)
-class UserDefault(Default):
-
- """A default_user statement."""
-
- def __str__(self):
- return "default_user {0.object_class} {0.default};".format(self)
-
- @property
- def default(self):
- """The default user location (source/target)."""
- return self.qpol_symbol.user_default(self.policy)
-
-
-class RoleDefault(Default):
-
- """A default_role statement."""
-
- def __str__(self):
- return "default_role {0.object_class} {0.default};".format(self)
-
- @property
- def default(self):
- """The default role location (source/target)."""
- return self.qpol_symbol.role_default(self.policy)
-
-
-class TypeDefault(Default):
-
- """A default_type statement."""
-
- def __str__(self):
- return "default_type {0.object_class} {0.default};".format(self)
-
- @property
- def default(self):
- """The default type location (source/target)."""
- return self.qpol_symbol.type_default(self.policy)
-
-
class RangeDefault(Default):
"""A default_range statement."""
- def __str__(self):
- return "default_range {0.object_class} {0.default} {0.default_range};".format(self)
-
- @property
- def default(self):
- """The default range location (source/target)."""
- return self.qpol_symbol.range_default(self.policy).split()[0]
+ default_range = None
- @property
- def default_range(self):
- """The default range setting (low/high/low_high)."""
- return self.qpol_symbol.range_default(self.policy).split()[1]
+ def __str__(self):
+ return "{0.ruletype} {0.tclass} {0.default} {0.default_range};".format(self)
diff --git a/lib/python2.7/site-packages/setools/policyrep/exception.py b/lib/python2.7/site-packages/setools/policyrep/exception.py
index 6935873..bab5792 100644
--- a/lib/python2.7/site-packages/setools/policyrep/exception.py
+++ b/lib/python2.7/site-packages/setools/policyrep/exception.py
@@ -1,4 +1,4 @@
-# Copyright 2015, Tresys Technology, LLC
+# Copyright 2015-2016, Tresys Technology, LLC
#
# This file is part of SETools.
#
@@ -157,7 +157,13 @@ class InvalidConstraintType(InvalidSymbol):
pass
-class InvalidFSUseType(InvalidSymbol):
+class InvalidDefaultType(InvalidRuleType):
+
+ """Exception for invalid default_* types."""
+ pass
+
+
+class InvalidFSUseType(InvalidRuleType):
"""Exception for invalid fs_use_* types."""
pass
@@ -184,7 +190,7 @@ class InvalidTERuleType(InvalidRuleType):
#
# Object use errors
#
-class SymbolUseError(PolicyrepException):
+class SymbolUseError(AttributeError, PolicyrepException):
"""
Base class for incorrectly using an object. Typically this is
@@ -220,9 +226,24 @@ class NoStatement(SymbolUseError):
#
+# Default rule exceptions
+#
+class InvalidDefaultValue(InvalidSymbol):
+
+ """Exception for invalid default (not source/target)"""
+ pass
+
+
+class InvalidDefaultRange(InvalidSymbol):
+
+ """Exception for invalid default range"""
+ pass
+
+
+#
# Other exceptions
#
-class NoCommon(PolicyrepException):
+class NoCommon(AttributeError, PolicyrepException):
"""
Exception when a class does not inherit a common permission set.
@@ -236,7 +257,7 @@ class NoDefaults(InvalidSymbol):
pass
-class RuleNotConditional(PolicyrepException):
+class RuleNotConditional(AttributeError, PolicyrepException):
"""
Exception when getting the conditional expression for rules
@@ -245,7 +266,7 @@ class RuleNotConditional(PolicyrepException):
pass
-class TERuleNoFilename(PolicyrepException):
+class TERuleNoFilename(AttributeError, PolicyrepException):
"""
Exception when getting the file name of a
diff --git a/lib/python2.7/site-packages/setools/policyrep/fscontext.py b/lib/python2.7/site-packages/setools/policyrep/fscontext.py
index 215dcd7..1dc648e 100644
--- a/lib/python2.7/site-packages/setools/policyrep/fscontext.py
+++ b/lib/python2.7/site-packages/setools/policyrep/fscontext.py
@@ -1,4 +1,4 @@
-# Copyright 2014, Tresys Technology, LLC
+# Copyright 2014, 2016, Tresys Technology, LLC
#
# This file is part of SETools.
#
@@ -24,11 +24,12 @@ from . import symbol
from . import context
-def validate_ruletype(types):
+def validate_ruletype(t):
"""Validate fs_use_* rule types."""
- for t in types:
- if t not in ["fs_use_xattr", "fs_use_trans", "fs_use_task"]:
- raise exception.InvalidConstraintType("{0} is not a valid fs_use_* type.".format(t))
+ if t not in ["fs_use_xattr", "fs_use_trans", "fs_use_task"]:
+ raise exception.InvalidFSUseType("{0} is not a valid fs_use_* type.".format(t))
+
+ return t
def fs_use_factory(policy, name):
@@ -70,9 +71,18 @@ class FSContext(symbol.PolicySymbol):
return str(self)
-class Genfscon(FSContext):
+class GenfsFiletype(int):
- """A genfscon statement."""
+ """
+ A genfscon file type.
+
+ The possible values are equivalent to file type
+ values in the stat module, e.g. S_IFBLK, but
+ overrides the string representation with the
+ corresponding genfscon file type string
+ (-b, -c, etc.) If the genfscon has no specific
+ file type, this is 0, (empty string).
+ """
_filetype_to_text = {
0: "",
@@ -85,8 +95,18 @@ class Genfscon(FSContext):
stat.S_IFSOCK: "-s"}
def __str__(self):
- return "genfscon {0.fs} {0.path} {1} {0.context}".format(
- self, self._filetype_to_text[self.filetype])
+ return self._filetype_to_text[self]
+
+
+class Genfscon(FSContext):
+
+ """A genfscon statement."""
+
+ def __str__(self):
+ return "genfscon {0.fs} {0.path} {0.filetype} {0.context}".format(self)
+
+ def __hash__(self):
+ return hash("genfscon|{0.fs}|{0.path}|{0.filetype}".format(self))
def __eq__(self, other):
# Libqpol allocates new C objects in the
@@ -103,7 +123,7 @@ class Genfscon(FSContext):
@property
def filetype(self):
"""The file type (e.g. stat.S_IFBLK) for this genfscon statement."""
- return self.qpol_symbol.object_class(self.policy)
+ return GenfsFiletype(self.qpol_symbol.object_class(self.policy))
@property
def path(self):
@@ -125,6 +145,9 @@ class FSUse(FSContext):
def __str__(self):
return "{0.ruletype} {0.fs} {0.context};".format(self)
+ def __hash__(self):
+ return hash("{0.ruletype}|{0.fs}".format(self))
+
@property
def ruletype(self):
"""The rule type for this fs_use_* statement."""
diff --git a/lib/python2.7/site-packages/setools/policyrep/mls.py b/lib/python2.7/site-packages/setools/policyrep/mls.py
index cc24026..65a5ddc 100644
--- a/lib/python2.7/site-packages/setools/policyrep/mls.py
+++ b/lib/python2.7/site-packages/setools/policyrep/mls.py
@@ -1,4 +1,4 @@
-# Copyright 2014-2015, Tresys Technology, LLC
+# Copyright 2014-2016, Tresys Technology, LLC
#
# This file is part of SETools.
#
@@ -43,7 +43,7 @@ from . import symbol
def enabled(policy):
"""Determine if MLS is enabled."""
- return policy.capability(qpol.QPOL_CAP_MLS)
+ return bool(policy.capability(qpol.QPOL_CAP_MLS))
def category_factory(policy, sym):
@@ -253,12 +253,6 @@ class Sensitivity(BaseMLSComponent):
"""An MLS sensitivity"""
- def __eq__(self, other):
- try:
- return self._value == other._value
- except AttributeError:
- return str(self) == str(other)
-
def __ge__(self, other):
return self._value >= other._value
@@ -330,6 +324,10 @@ class LevelDecl(BaseMLSLevel):
level s7:c0.c1023;
"""
+
+ def __hash__(self):
+ return hash(self.sensitivity)
+
# below comparisons are only based on sensitivity
# dominance since, in this context, the allowable
# category set is being defined for the level.
diff --git a/lib/python2.7/site-packages/setools/policyrep/mlsrule.py b/lib/python2.7/site-packages/setools/policyrep/mlsrule.py
index 5c91c59..77f2df4 100644
--- a/lib/python2.7/site-packages/setools/policyrep/mlsrule.py
+++ b/lib/python2.7/site-packages/setools/policyrep/mlsrule.py
@@ -1,4 +1,4 @@
-# Copyright 2014, Tresys Technology, LLC
+# Copyright 2014, 2016, Tresys Technology, LLC
#
# This file is part of SETools.
#
@@ -16,6 +16,8 @@
# License along with SETools. If not, see
# <http://www.gnu.org/licenses/>.
#
+import itertools
+
from . import exception
from . import qpol
from . import rule
@@ -31,11 +33,34 @@ def mls_rule_factory(policy, symbol):
return MLSRule(policy, symbol)
-def validate_ruletype(types):
+def expanded_mls_rule_factory(original, source, target):
+ """
+ Factory function for creating expanded MLS rules.
+
+ original The MLS rule the expanded rule originates from.
+ source The source type of the expanded rule.
+ target The target type of the expanded rule.
+ """
+
+ if isinstance(original, MLSRule):
+ rule = ExpandedMLSRule(original.policy, original.qpol_symbol)
+ elif isinstance(original, MLSRule):
+ return original
+ else:
+ raise TypeError("The original rule must be a MLS rule class.")
+
+ rule.source = source
+ rule.target = target
+ rule.origin = original
+ return rule
+
+
+def validate_ruletype(t):
"""Validate MLS rule types."""
- for t in types:
- if t not in ["range_transition"]:
- raise exception.InvalidMLSRuleType("{0} is not a valid MLS rule type.".format(t))
+ if t not in ["range_transition"]:
+ raise exception.InvalidMLSRuleType("{0} is not a valid MLS rule type.".format(t))
+
+ return t
class MLSRule(rule.PolicyRule):
@@ -43,8 +68,9 @@ class MLSRule(rule.PolicyRule):
"""An MLS rule."""
def __str__(self):
- # TODO: If we ever get more MLS rules, fix this format.
- return "range_transition {0.source} {0.target}:{0.tclass} {0.default};".format(self)
+ return "{0.ruletype} {0.source} {0.target}:{0.tclass} {0.default};".format(self)
+
+ ruletype = "range_transition"
@property
def source(self):
@@ -60,3 +86,17 @@ class MLSRule(rule.PolicyRule):
def default(self):
"""The rule's default range."""
return mls.range_factory(self.policy, self.qpol_symbol.range(self.policy))
+
+ def expand(self):
+ """Expand the rule into an equivalent set of rules without attributes."""
+ for s, t in itertools.product(self.source.expand(), self.target.expand()):
+ yield expanded_mls_rule_factory(self, s, t)
+
+
+class ExpandedMLSRule(MLSRule):
+
+ """An expanded MLS rule."""
+
+ source = None
+ target = None
+ origin = None
diff --git a/lib/python2.7/site-packages/setools/policyrep/netcontext.py b/lib/python2.7/site-packages/setools/policyrep/netcontext.py
index 5aeed5c..4c9b6ec 100644
--- a/lib/python2.7/site-packages/setools/policyrep/netcontext.py
+++ b/lib/python2.7/site-packages/setools/policyrep/netcontext.py
@@ -1,4 +1,4 @@
-# Copyright 2014, Tresys Technology, LLC
+# Copyright 2014, 2016, Tresys Technology, LLC
#
# This file is part of SETools.
#
@@ -76,6 +76,9 @@ class Netifcon(NetContext):
def __str__(self):
return "netifcon {0.netif} {0.context} {0.packet}".format(self)
+ def __hash__(self):
+ return hash("netifcon|{0.netif}".format(self))
+
@property
def netif(self):
"""The network interface name."""
@@ -99,6 +102,9 @@ class Nodecon(NetContext):
def __str__(self):
return "nodecon {0.address} {0.netmask} {0.context}".format(self)
+ def __hash__(self):
+ return hash("nodecon|{0.address}|{0.netmask}".format(self))
+
def __eq__(self, other):
# Libqpol allocates new C objects in the
# nodecons iterator, so pointer comparison
@@ -129,21 +135,38 @@ class Nodecon(NetContext):
return self.qpol_symbol.mask(self.policy)
-class Portcon(NetContext):
+class PortconProtocol(int):
- """A portcon statement."""
+ """
+ A portcon protocol type.
+
+ The possible values are equivalent to protocol
+ values in the socket module, e.g. IPPROTO_TCP, but
+ overrides the string representation with the
+ corresponding protocol string (udp, tcp).
+ """
_proto_to_text = {socket.IPPROTO_TCP: 'tcp',
socket.IPPROTO_UDP: 'udp'}
def __str__(self):
+ return self._proto_to_text[self]
+
+
+class Portcon(NetContext):
+
+ """A portcon statement."""
+
+ def __str__(self):
low, high = self.ports
- proto = self._proto_to_text[self.protocol]
if low == high:
- return "portcon {0} {1} {2}".format(proto, low, self.context)
+ return "portcon {0.protocol} {1} {0.context}".format(self, low)
else:
- return "portcon {0} {1}-{2} {3}".format(proto, low, high, self.context)
+ return "portcon {0.protocol} {1}-{2} {0.context}".format(self, low, high)
+
+ def __hash__(self):
+ return hash("portcon|{0.protocol}|{1.low}|{1.high}".format(self, self.ports))
@property
def protocol(self):
@@ -151,7 +174,7 @@ class Portcon(NetContext):
The protocol number for the portcon (socket.IPPROTO_TCP
or socket.IPPROTO_UDP).
"""
- return self.qpol_symbol.protocol(self.policy)
+ return PortconProtocol(self.qpol_symbol.protocol(self.policy))
@property
def ports(self):
diff --git a/lib/python2.7/site-packages/setools/policyrep/rbacrule.py b/lib/python2.7/site-packages/setools/policyrep/rbacrule.py
index aa6a0d0..d327775 100644
--- a/lib/python2.7/site-packages/setools/policyrep/rbacrule.py
+++ b/lib/python2.7/site-packages/setools/policyrep/rbacrule.py
@@ -1,4 +1,4 @@
-# Copyright 2014, Tresys Technology, LLC
+# Copyright 2014, 2016, Tresys Technology, LLC
#
# This file is part of SETools.
#
@@ -16,6 +16,8 @@
# License along with SETools. If not, see
# <http://www.gnu.org/licenses/>.
#
+import itertools
+
from . import exception
from . import qpol
from . import rule
@@ -34,11 +36,36 @@ def rbac_rule_factory(policy, name):
raise TypeError("RBAC rules cannot be looked up.")
-def validate_ruletype(types):
+def expanded_rbac_rule_factory(original, source, target):
+ """
+ Factory function for creating expanded RBAC rules.
+
+ original The RBAC rule the expanded rule originates from.
+ source The source type of the expanded rule.
+ target The target type of the expanded rule.
+ """
+
+ if isinstance(original, RoleAllow):
+ rule = ExpandedRoleAllow(original.policy, original.qpol_symbol)
+ elif isinstance(original, RoleTransition):
+ rule = ExpandedRoleTransition(original.policy, original.qpol_symbol)
+ elif isinstance(original, (ExpandedRoleAllow, ExpandedRoleTransition)):
+ return original
+ else:
+ raise TypeError("The original rule must be an RBAC rule class.")
+
+ rule.source = source
+ rule.target = target
+ rule.origin = original
+ return rule
+
+
+def validate_ruletype(t):
"""Validate RBAC rule types."""
- for t in types:
- if t not in ["allow", "role_transition"]:
- raise exception.InvalidRBACRuleType("{0} is not a valid RBAC rule type.".format(t))
+ if t not in ["allow", "role_transition"]:
+ raise exception.InvalidRBACRuleType("{0} is not a valid RBAC rule type.".format(t))
+
+ return t
class RoleAllow(rule.PolicyRule):
@@ -46,7 +73,12 @@ class RoleAllow(rule.PolicyRule):
"""A role allow rule."""
def __str__(self):
- return "allow {0.source} {0.target};".format(self)
+ return "{0.ruletype} {0.source} {0.target};".format(self)
+
+ def __hash__(self):
+ return hash("{0.ruletype}|{0.source}|{0.target}".format(self))
+
+ ruletype = "allow"
@property
def source(self):
@@ -68,13 +100,20 @@ class RoleAllow(rule.PolicyRule):
"""The rule's default role."""
raise exception.RuleUseError("Role allow rules do not have a default role.")
+ def expand(self):
+ """Expand the rule into an equivalent set of rules without attributes."""
+ for s, t in itertools.product(self.source.expand(), self.target.expand()):
+ yield expanded_rbac_rule_factory(self, s, t)
+
class RoleTransition(rule.PolicyRule):
"""A role_transition rule."""
def __str__(self):
- return "role_transition {0.source} {0.target}:{0.tclass} {0.default};".format(self)
+ return "{0.ruletype} {0.source} {0.target}:{0.tclass} {0.default};".format(self)
+
+ ruletype = "role_transition"
@property
def source(self):
@@ -90,3 +129,26 @@ class RoleTransition(rule.PolicyRule):
def default(self):
"""The rule's default role."""
return role.role_factory(self.policy, self.qpol_symbol.default_role(self.policy))
+
+ def expand(self):
+ """Expand the rule into an equivalent set of rules without attributes."""
+ for s, t in itertools.product(self.source.expand(), self.target.expand()):
+ yield expanded_rbac_rule_factory(self, s, t)
+
+
+class ExpandedRoleAllow(RoleAllow):
+
+ """An expanded role allow rule."""
+
+ source = None
+ target = None
+ origin = None
+
+
+class ExpandedRoleTransition(RoleTransition):
+
+ """An expanded role_transition rule."""
+
+ source = None
+ target = None
+ origin = None
diff --git a/lib/python2.7/site-packages/setools/policyrep/rule.py b/lib/python2.7/site-packages/setools/policyrep/rule.py
index 73fc812..b7a6a9d 100644
--- a/lib/python2.7/site-packages/setools/policyrep/rule.py
+++ b/lib/python2.7/site-packages/setools/policyrep/rule.py
@@ -1,4 +1,4 @@
-# Copyright 2014, Tresys Technology, LLC
+# Copyright 2014, 2016, Tresys Technology, LLC
#
# This file is part of SETools.
#
@@ -28,6 +28,17 @@ class PolicyRule(symbol.PolicySymbol):
def __str__(self):
raise NotImplementedError
+ def __hash__(self):
+ try:
+ cond = self.conditional
+ cond_block = self.conditional_block
+ except exception.RuleNotConditional:
+ cond = None
+ cond_block = None
+
+ return hash("{0.ruletype}|{0.source}|{0.target}|{0.tclass}|{1}|{2}".format(
+ self, cond, cond_block))
+
@property
def ruletype(self):
"""The rule type for the rule."""
@@ -68,5 +79,15 @@ class PolicyRule(symbol.PolicySymbol):
# Most rules cannot be conditional.
raise exception.RuleNotConditional
+ @property
+ def conditional_block(self):
+ """The conditional block of the rule (T/F)"""
+ # Most rules cannot be conditional.
+ raise exception.RuleNotConditional
+
+ def expand(self):
+ """Expand the rule into an equivalent set of rules without attributes."""
+ raise NotImplementedError
+
def statement(self):
return str(self)
diff --git a/lib/python2.7/site-packages/setools/policyrep/symbol.py b/lib/python2.7/site-packages/setools/policyrep/symbol.py
index 4712d7f..556cc2d 100644
--- a/lib/python2.7/site-packages/setools/policyrep/symbol.py
+++ b/lib/python2.7/site-packages/setools/policyrep/symbol.py
@@ -38,7 +38,7 @@ class PolicySymbol(object):
return self.qpol_symbol.name(self.policy)
def __hash__(self):
- return hash(self.qpol_symbol.name(self.policy))
+ return hash(str(self))
def __eq__(self, other):
try:
diff --git a/lib/python2.7/site-packages/setools/policyrep/terule.py b/lib/python2.7/site-packages/setools/policyrep/terule.py
index d8a9e94..7fe56cc 100644
--- a/lib/python2.7/site-packages/setools/policyrep/terule.py
+++ b/lib/python2.7/site-packages/setools/policyrep/terule.py
@@ -1,4 +1,4 @@
-# Copyright 2014, Tresys Technology, LLC
+# Copyright 2014-2016, Tresys Technology, LLC
#
# This file is part of SETools.
#
@@ -16,6 +16,8 @@
# License along with SETools. If not, see
# <http://www.gnu.org/licenses/>.
#
+import itertools
+
from . import exception
from . import qpol
from . import rule
@@ -34,12 +36,37 @@ def te_rule_factory(policy, symbol):
raise TypeError("TE rules cannot be looked-up.")
-def validate_ruletype(types):
+def expanded_te_rule_factory(original, source, target):
+ """
+ Factory function for creating expanded TE rules.
+
+ original The TE rule the expanded rule originates from.
+ source The source type of the expanded rule.
+ target The target type of the expanded rule.
+ """
+
+ if isinstance(original, AVRule):
+ rule = ExpandedAVRule(original.policy, original.qpol_symbol)
+ elif isinstance(original, TERule):
+ rule = ExpandedTERule(original.policy, original.qpol_symbol)
+ elif isinstance(original, (ExpandedAVRule, ExpandedTERule)):
+ return original
+ else:
+ raise TypeError("The original rule must be a TE rule class.")
+
+ rule.source = source
+ rule.target = target
+ rule.origin = original
+ return rule
+
+
+def validate_ruletype(t):
"""Validate TE Rule types."""
- for t in types:
- if t not in ["allow", "auditallow", "dontaudit", "neverallow",
- "type_transition", "type_member", "type_change"]:
- raise exception.InvalidTERuleType("{0} is not a valid TE rule type.".format(t))
+ if t not in ["allow", "auditallow", "dontaudit", "neverallow",
+ "type_transition", "type_member", "type_change"]:
+ raise exception.InvalidTERuleType("{0} is not a valid TE rule type.".format(t))
+
+ return t
class BaseTERule(rule.PolicyRule):
@@ -71,30 +98,48 @@ class BaseTERule(rule.PolicyRule):
# ValueError: The rule is not conditional
raise exception.RuleNotConditional
+ @property
+ def conditional_block(self):
+ """The conditional block of the rule (T/F)"""
+ try:
+ return bool(self.qpol_symbol.which_list(self.policy))
+ except (AttributeError, ValueError):
+ # AttributeError: name filetrans rules cannot be conditional
+ # so no member function
+ # ValueError: The rule is not conditional
+ raise exception.RuleNotConditional
+
+ def expand(self):
+ """Expand the rule into an equivalent set of rules without attributes."""
+ for s, t in itertools.product(self.source.expand(), self.target.expand()):
+ yield expanded_te_rule_factory(self, s, t)
+
class AVRule(BaseTERule):
"""An access vector type enforcement rule."""
def __str__(self):
- rule_string = "{0.ruletype} {0.source} {0.target}:{0.tclass} ".format(
- self)
+ try:
+ return self._rule_string
+ except AttributeError:
+ self._rule_string = "{0.ruletype} {0.source} {0.target}:{0.tclass} ".format(self)
- perms = self.perms
+ perms = self.perms
- # allow/dontaudit/auditallow/neverallow rules
- if len(perms) > 1:
- rule_string += "{{ {0} }};".format(' '.join(perms))
- else:
- # convert to list since sets cannot be indexed
- rule_string += "{0};".format(list(perms)[0])
+ # allow/dontaudit/auditallow/neverallow rules
+ if len(perms) > 1:
+ self._rule_string += "{{ {0} }};".format(' '.join(perms))
+ else:
+ # convert to list since sets cannot be indexed
+ self._rule_string += "{0};".format(list(perms)[0])
- try:
- rule_string += " [ {0} ]".format(self.conditional)
- except exception.RuleNotConditional:
- pass
+ try:
+ self._rule_string += " [ {0.conditional} ]:{0.conditional_block}".format(self)
+ except exception.RuleNotConditional:
+ pass
- return rule_string
+ return self._rule_string
@property
def perms(self):
@@ -116,20 +161,40 @@ class TERule(BaseTERule):
"""A type_* type enforcement rule."""
def __str__(self):
- rule_string = "{0.ruletype} {0.source} {0.target}:{0.tclass} {0.default}".format(self)
-
try:
- rule_string += " \"{0}\";".format(self.filename)
- except (exception.TERuleNoFilename, exception.RuleUseError):
- # invalid use for type_change/member
- rule_string += ";"
+ return self._rule_string
+ except AttributeError:
+ self._rule_string = "{0.ruletype} {0.source} {0.target}:{0.tclass} {0.default}".format(
+ self)
+ try:
+ self._rule_string += " \"{0}\";".format(self.filename)
+ except (exception.TERuleNoFilename, exception.RuleUseError):
+ # invalid use for type_change/member
+ self._rule_string += ";"
+
+ try:
+ self._rule_string += " [ {0.conditional} ]:{0.conditional_block}".format(self)
+ except exception.RuleNotConditional:
+ pass
+
+ return self._rule_string
+
+ def __hash__(self):
try:
- rule_string += " [ {0} ]".format(self.conditional)
+ cond = self.conditional
+ cond_block = self.conditional_block
except exception.RuleNotConditional:
- pass
+ cond = None
+ cond_block = None
- return rule_string
+ try:
+ filename = self.filename
+ except (exception.TERuleNoFilename, exception.RuleUseError):
+ filename = None
+
+ return hash("{0.ruletype}|{0.source}|{0.target}|{0.tclass}|{1}|{2}|{3}".format(
+ self, filename, cond, cond_block))
@property
def perms(self):
@@ -153,3 +218,21 @@ class TERule(BaseTERule):
else:
raise exception.RuleUseError("{0} rules do not have file names".
format(self.ruletype))
+
+
+class ExpandedAVRule(AVRule):
+
+ """An expanded access vector type enforcement rule."""
+
+ source = None
+ target = None
+ origin = None
+
+
+class ExpandedTERule(TERule):
+
+ """An expanded type_* type enforcement rule."""
+
+ source = None
+ target = None
+ origin = None
diff --git a/lib/python2.7/site-packages/setools/rbacrulequery.py b/lib/python2.7/site-packages/setools/rbacrulequery.py
index 240b921..5e9a139 100644
--- a/lib/python2.7/site-packages/setools/rbacrulequery.py
+++ b/lib/python2.7/site-packages/setools/rbacrulequery.py
@@ -20,7 +20,7 @@ import logging
import re
from . import mixins, query
-from .descriptors import CriteriaDescriptor, CriteriaSetDescriptor, RuletypeDescriptor
+from .descriptors import CriteriaDescriptor, CriteriaSetDescriptor
from .policyrep.exception import InvalidType, RuleUseError
@@ -54,7 +54,7 @@ class RBACRuleQuery(mixins.MatchObjClass, query.PolicyQuery):
be used on the default role.
"""
- ruletype = RuletypeDescriptor("validate_rbac_ruletype")
+ ruletype = CriteriaSetDescriptor(lookup_function="validate_rbac_ruletype")
source = CriteriaDescriptor("source_regex", "lookup_role")
source_regex = False
source_indirect = True
diff --git a/lib/python2.7/site-packages/setools/rolequery.py b/lib/python2.7/site-packages/setools/rolequery.py
index e95dfa6..37de123 100644
--- a/lib/python2.7/site-packages/setools/rolequery.py
+++ b/lib/python2.7/site-packages/setools/rolequery.py
@@ -57,13 +57,6 @@ class RoleQuery(compquery.ComponentQuery):
"eq: {0.types_equal}".format(self))
for r in self.policy.roles():
- if r == "object_r":
- # all types are implicitly added to object_r by the compiler.
- # technically it is incorrect to skip it, but policy writers
- # and analysts don't expect to see it in results, and it
- # will confuse, especially for set equality type queries.
- continue
-
if not self._match_name(r):
continue
diff --git a/lib/python2.7/site-packages/setools/terulequery.py b/lib/python2.7/site-packages/setools/terulequery.py
index 3694160..eff8df1 100644
--- a/lib/python2.7/site-packages/setools/terulequery.py
+++ b/lib/python2.7/site-packages/setools/terulequery.py
@@ -20,7 +20,7 @@ import logging
import re
from . import mixins, query
-from .descriptors import CriteriaDescriptor, CriteriaSetDescriptor, RuletypeDescriptor
+from .descriptors import CriteriaDescriptor, CriteriaSetDescriptor
from .policyrep.exception import RuleUseError, RuleNotConditional
@@ -80,7 +80,7 @@ class TERuleQuery(mixins.MatchObjClass, mixins.MatchPermission, query.PolicyQuer
will match. Default is false.
"""
- ruletype = RuletypeDescriptor("validate_te_ruletype")
+ ruletype = CriteriaSetDescriptor(lookup_function="validate_te_ruletype")
source = CriteriaDescriptor("source_regex", "lookup_type_or_attr")
source_regex = False
source_indirect = True