From ceb6736962580f9e11ee2d4a5ae684da920d4b45 Mon Sep 17 00:00:00 2001 From: Chris PeBenito Date: Mon, 16 Feb 2015 16:31:18 -0500 Subject: [PATCH] Implement included/excluded classes/permissions in PermissionMap. closes #24 --- README | 13 ++++ setools/permmap.py | 107 +++++++++++++++++++++++++- tests/__init__.py | 1 + tests/permmap.py | 186 +++++++++++++++++++++++++++++++++++++++++++++ tox.ini | 1 + 5 files changed, 307 insertions(+), 1 deletion(-) create mode 100644 tests/permmap.py diff --git a/README b/README index cf472a6..aeb27a2 100644 --- a/README +++ b/README @@ -55,6 +55,19 @@ To run SETools, the following packages are required: libsepol 2.4+ libbz2 +To run SETools unit tests, the following packages are required: + Python 2.7 or 3.3+ + setuptools + gcc + bison + flex + libselinux + libsepol 2.4+ + libbz2 + SWIG 2.0.12+ or 3.0+ + mock (on Python 2.7 only) + tox (optional) + 2.1. building SETools --------------------- diff --git a/setools/permmap.py b/setools/permmap.py index 8df8627..9bffda1 100644 --- a/setools/permmap.py +++ b/setools/permmap.py @@ -19,6 +19,18 @@ from . import policyrep +class RuleTypeError(Exception): + + """Exception for using rules with incorrect rule type.""" + pass + + +class UnmappedClass(Exception): + + """Exception for classes that are unmapped""" + pass + + class UnmappedPermission(Exception): """Exception for permissions that are unmapped""" @@ -135,6 +147,89 @@ class PermissionMap(object): if perm_count >= num_perms: state = 2 + def exclude_class(self, class_): + """ + Exclude all permissions in an object class for calculating rule weights. + + Parameter: + class_ The object class to exclude. + + Exceptions: + UnmappedClass The specified object class is not mapped. + """ + + classname = str(class_) + + try: + for perm in self.permmap[classname]: + self.permmap[classname][perm]['enabled'] = False + except KeyError: + raise UnmappedClass("{0} is not mapped.".format(classname)) + + def exclude_permission(self, class_, permission): + """ + Exclude a permission for calculating rule weights. + + Parameter: + class_ The object class of the permission. + permission The permission name to exclude. + + Exceptions: + UnmappedClass The specified object class is not mapped. + UnmappedPermission The specified permission is not mapped for the object class. + """ + classname = str(class_) + + if classname not in self.permmap: + raise UnmappedClass("{0} is not mapped.".format(classname)) + + try: + self.permmap[classname][permission]['enabled'] = False + except KeyError: + raise UnmappedPermission("{0}:{1} is not mapped.".format(classname, permission)) + + def include_class(self, class_): + """ + Include all permissions in an object class for calculating rule weights. + + Parameter: + class_ The object class to include. + + Exceptions: + UnmappedClass The specified object class is not mapped. + """ + + classname = str(class_) + + try: + for perm in self.permmap[classname]: + self.permmap[classname][perm]['enabled'] = True + except KeyError: + raise UnmappedClass("{0} is not mapped.".format(classname)) + + def include_permission(self, class_, permission): + """ + Include a permission for calculating rule weights. + + Parameter: + class_ The object class of the permission. + permission The permission name to include. + + Exceptions: + UnmappedClass The specified object class is not mapped. + UnmappedPermission The specified permission is not mapped for the object class. + """ + + classname = str(class_) + + if classname not in self.permmap: + raise UnmappedClass("{0} is not mapped.".format(classname)) + + try: + self.permmap[classname][permission]['enabled'] = True + except KeyError: + raise UnmappedPermission("{0}:{1} is not mapped.".format(classname, permission)) + def rule_weight(self, rule): """ Get the type enforcement rule's information flow read and write weights. @@ -151,11 +246,21 @@ class PermissionMap(object): read_weight = 0 class_name = str(rule.tclass) + if rule.ruletype != 'allow': + raise RuleTypeError("{0} rules cannot be used for calculating a weight". + format(rule.ruletype)) + + if class_name not in self.permmap: + raise UnmappedClass("{0} is not mapped.".format(class_name)) + # iterate over the permissions and determine the # weight of the rule in each direction. The result # is the largest-weight permission in each direction for perm_name in rule.perms: - mapping = self.permmap[class_name][perm_name] + try: + mapping = self.permmap[class_name][perm_name] + except KeyError: + raise UnmappedPermission("{0}:{1} is not mapped.".format(class_name, perm_name)) if not mapping['enabled']: continue diff --git a/tests/__init__.py b/tests/__init__.py index 10f6bc4..cef8a69 100644 --- a/tests/__init__.py +++ b/tests/__init__.py @@ -25,6 +25,7 @@ from . import mlsrulequery from . import netifconquery from . import nodeconquery from . import objclassquery +from . import permmap from . import polcapquery from . import infoflow from . import terulequery diff --git a/tests/permmap.py b/tests/permmap.py new file mode 100644 index 0000000..b7ce22b --- /dev/null +++ b/tests/permmap.py @@ -0,0 +1,186 @@ +# Copyright 2015, Tresys Technology, LLC +# +# This file is part of SETools. +# +# SETools is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 2 of the License, or +# (at your option) any later version. +# +# SETools is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with SETools. If not, see . +# +import unittest + +try: + from unittest.mock import MagicMock +except ImportError: + from mock import MagicMock + +from setools import SELinuxPolicy +from setools.permmap import PermissionMap, UnmappedClass, UnmappedPermission, RuleTypeError + + +class PermissionMapTest(unittest.TestCase): + + def test_001_load(self): + """PermMap open from path.""" + permmap = PermissionMap("tests/perm_map") + self.assertTrue(permmap) + + # 100 get/set weight + # 110 get/set direction + + def test_120_exclude_perm(self): + """PermMap exclude permission.""" + + permmap = PermissionMap("tests/perm_map") + permmap.exclude_permission("infoflow", "med_w") + self.assertFalse(permmap.permmap['infoflow']['med_w']['enabled']) + + def test_121_reinclude_perm(self): + """PermMap include permission.""" + + permmap = PermissionMap("tests/perm_map") + permmap.exclude_permission("infoflow", "med_w") + self.assertFalse(permmap.permmap['infoflow']['med_w']['enabled']) + + permmap.include_permission("infoflow", "med_w") + self.assertTrue(permmap.permmap['infoflow']['med_w']['enabled']) + + def test_122_exclude_class(self): + """PermMap exclude class.""" + + permmap = PermissionMap("tests/perm_map") + permmap.exclude_class("file") + self.assertFalse(permmap.permmap['file']['execute']['enabled']) + self.assertFalse(permmap.permmap['file']['entrypoint']['enabled']) + + def test_123_include_class(self): + """PermMap exclude class.""" + + permmap = PermissionMap("tests/perm_map") + permmap.exclude_class("file") + self.assertFalse(permmap.permmap['file']['execute']['enabled']) + self.assertFalse(permmap.permmap['file']['entrypoint']['enabled']) + + permmap.include_class("file") + self.assertTrue(permmap.permmap['file']['execute']['enabled']) + self.assertTrue(permmap.permmap['file']['entrypoint']['enabled']) + + def test_130_weight_read_only(self): + """PermMap get weight of read-only rule.""" + + rule = MagicMock() + rule.ruletype = "allow" + rule.tclass = "infoflow" + rule.perms = set(["med_r", "hi_r"]) + + permmap = PermissionMap("tests/perm_map") + r, w = permmap.rule_weight(rule) + self.assertEqual(r, 10) + self.assertEqual(w, 0) + + def test_131_weight_write_only(self): + """PermMap get weight of write-only rule.""" + + rule = MagicMock() + rule.ruletype = "allow" + rule.tclass = "infoflow" + rule.perms = set(["low_w", "med_w"]) + + permmap = PermissionMap("tests/perm_map") + r, w = permmap.rule_weight(rule) + self.assertEqual(r, 0) + self.assertEqual(w, 5) + + def test_132_weight_both(self): + """PermMap get weight of both rule.""" + + rule = MagicMock() + rule.ruletype = "allow" + rule.tclass = "infoflow" + rule.perms = set(["low_r", "hi_w"]) + + permmap = PermissionMap("tests/perm_map") + r, w = permmap.rule_weight(rule) + self.assertEqual(r, 1) + self.assertEqual(w, 10) + + def test_133_weight_none(self): + """PermMap get weight of none rule.""" + + rule = MagicMock() + rule.ruletype = "allow" + rule.tclass = "infoflow3" + rule.perms = set(["null"]) + + permmap = PermissionMap("tests/perm_map") + r, w = permmap.rule_weight(rule) + self.assertEqual(r, 0) + self.assertEqual(w, 0) + + def test_134_weight_unmapped_class(self): + """PermMap get weight of rule with unmapped class.""" + + rule = MagicMock() + rule.ruletype = "allow" + rule.tclass = "unmapped" + rule.perms = set(["null"]) + + permmap = PermissionMap("tests/perm_map") + self.assertRaises(UnmappedClass, permmap.rule_weight, rule) + + def test_135_weight_unmapped_permission(self): + """PermMap get weight of rule with unmapped permission.""" + + rule = MagicMock() + rule.ruletype = "allow" + rule.tclass = "infoflow" + rule.perms = set(["low_r", "unmapped"]) + + permmap = PermissionMap("tests/perm_map") + self.assertRaises(UnmappedPermission, permmap.rule_weight, rule) + + def test_136_weight_wrong_rule_type(self): + """PermMap get weight of rule with wrong rule type.""" + + rule = MagicMock() + rule.ruletype = "type_transition" + rule.tclass = "infoflow" + + permmap = PermissionMap("tests/perm_map") + self.assertRaises(RuleTypeError, permmap.rule_weight, rule) + + def test_133_weight_excluded_permission(self): + """PermMap get weight of a rule with excluded permission.""" + + rule = MagicMock() + rule.ruletype = "allow" + rule.tclass = "infoflow" + rule.perms = set(["med_r", "hi_r"]) + + permmap = PermissionMap("tests/perm_map") + permmap.exclude_permission("infoflow", "hi_r") + r, w = permmap.rule_weight(rule) + self.assertEqual(r, 5) + self.assertEqual(w, 0) + + def test_133_weight_excluded_class(self): + """PermMap get weight of a rule with excluded class.""" + + rule = MagicMock() + rule.ruletype = "allow" + rule.tclass = "infoflow" + rule.perms = set(["low_r", "med_r", "hi_r", "low_w", "med_w", "hi_w"]) + + permmap = PermissionMap("tests/perm_map") + permmap.exclude_class("infoflow") + r, w = permmap.rule_weight(rule) + self.assertEqual(r, 0) + self.assertEqual(w, 0) diff --git a/tox.ini b/tox.ini index ceda372..b9388cf 100644 --- a/tox.ini +++ b/tox.ini @@ -40,5 +40,6 @@ commands = coverage --version [testenv] deps = networkx==1.9 + py27: mock commands = {envpython} setup.py test recreate = True