diff options
Diffstat (limited to 'lib/python2.7/site-packages/setools')
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 Binary files differindex aad9de3..f459582 100755 --- a/lib/python2.7/site-packages/setools/policyrep/_qpol.so +++ b/lib/python2.7/site-packages/setools/policyrep/_qpol.so 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 |