From 6de35b81f741b0fc3f90121dcce50a231da4a751 Mon Sep 17 00:00:00 2001 From: Chris PeBenito Date: Thu, 23 Apr 2015 11:04:37 -0400 Subject: [PATCH] Implement CommonTest and ObjClassTest. Also fix bugs uncovered by testing. --- libqpol/class_perm_query.c | 4 +- setools/policyrep/objclass.py | 11 ++- setools/policyrep/qpol.i | 40 +++++--- tests/policyrep/__init__.py | 1 + tests/policyrep/objclass.conf | 149 +++++++++++++++++++++++++++++ tests/policyrep/objclass.py | 175 ++++++++++++++++++++++++++++++++++ 6 files changed, 365 insertions(+), 15 deletions(-) create mode 100644 tests/policyrep/objclass.conf create mode 100644 tests/policyrep/objclass.py diff --git a/libqpol/class_perm_query.c b/libqpol/class_perm_query.c index ea25536..3b37a1a 100644 --- a/libqpol/class_perm_query.c +++ b/libqpol/class_perm_query.c @@ -376,7 +376,7 @@ int qpol_policy_get_class_by_name(const qpol_policy_t * policy, const char *name if (internal_datum == NULL) { *obj_class = NULL; ERR(policy, "could not find class %s", name); - errno = ENOENT; + errno = EINVAL; return STATUS_ERR; } @@ -541,7 +541,7 @@ int qpol_policy_get_common_by_name(const qpol_policy_t * policy, const char *nam if (internal_datum == NULL) { *common = NULL; ERR(policy, "could not find common %s", name); - errno = ENOENT; + errno = EINVAL; return STATUS_ERR; } *common = (qpol_common_t *) internal_datum; diff --git a/setools/policyrep/objclass.py b/setools/policyrep/objclass.py index fd3daca..ae1bdb6 100644 --- a/setools/policyrep/objclass.py +++ b/setools/policyrep/objclass.py @@ -56,7 +56,7 @@ class Common(symbol.PolicySymbol): """A common permission set.""" def __contains__(self, other): - return (other in self.perms) + return other in self.perms @property def perms(self): @@ -71,6 +71,15 @@ class ObjClass(Common): """An object class.""" + def __contains__(self, other): + try: + if other in self.common.perms: + return True + except exception.NoCommon: + pass + + return other in self.perms + @property def common(self): """ diff --git a/setools/policyrep/qpol.i b/setools/policyrep/qpol.i index b129c59..8bb26fb 100644 --- a/setools/policyrep/qpol.i +++ b/setools/policyrep/qpol.i @@ -1942,16 +1942,24 @@ typedef struct qpol_context {} qpol_context_t; /* qpol class */ typedef struct qpol_class {} qpol_class_t; %extend qpol_class { + %exception qpol_class { + $action + if (!result) { + if (errno == EINVAL) { + PyErr_SetString(PyExc_ValueError, "Invalid class."); + } else { + PyErr_SetFromErrno(PyExc_OSError); + } + + return NULL; + } + } qpol_class(qpol_policy_t *p, const char *name) { const qpol_class_t *c; - BEGIN_EXCEPTION - if (qpol_policy_get_class_by_name(p, name, &c)) { - SWIG_exception(SWIG_RuntimeError, "Class does not exist"); - } - END_EXCEPTION - fail: + qpol_policy_get_class_by_name(p, name, &c); return (qpol_class_t*)c; }; + ~qpol_class() { /* no op */ return; @@ -2041,16 +2049,24 @@ typedef struct qpol_class {} qpol_class_t; /* qpol common */ typedef struct qpol_common {} qpol_common_t; %extend qpol_common { + %exception qpol_common { + $action + if (!result) { + if (errno == EINVAL) { + PyErr_SetString(PyExc_ValueError, "Invalid common."); + } else { + PyErr_SetFromErrno(PyExc_OSError); + } + + return NULL; + } + } qpol_common(qpol_policy_t *p, const char *name) { const qpol_common_t *c; - BEGIN_EXCEPTION - if (qpol_policy_get_common_by_name(p, name, &c)) { - SWIG_exception(SWIG_RuntimeError, "Common does not exist"); - } - END_EXCEPTION - fail: + qpol_policy_get_common_by_name(p, name, &c); return (qpol_common_t*)c; }; + ~qpol_common() { /* no op */ return; diff --git a/tests/policyrep/__init__.py b/tests/policyrep/__init__.py index 7b88f49..3946a18 100644 --- a/tests/policyrep/__init__.py +++ b/tests/policyrep/__init__.py @@ -17,6 +17,7 @@ # from . import mls from . import mlsrule +from . import objclass from . import polcap from . import rbacrule from . import role diff --git a/tests/policyrep/objclass.conf b/tests/policyrep/objclass.conf new file mode 100644 index 0000000..e19200b --- /dev/null +++ b/tests/policyrep/objclass.conf @@ -0,0 +1,149 @@ +class infoflow +class infoflow2 +class infoflow3 +class infoflow4 +class infoflow5 +class infoflow6 +class infoflow7 +class infoflow8 +class infoflow9 +class infoflow10 + +sid kernel +sid security + +common infoflow +{ + low_w + med_w + hi_w + low_r + med_r + hi_r +} + +common com_a +{ + hi_w + hi_r + super_r + super_w +} + +common com_b +{ + send + recv +} + +common com_c +{ + getattr + setattr + read + write +} + +class infoflow +inherits infoflow + +class infoflow2 +inherits infoflow +{ + super_w + super_r +} + +class infoflow3 +{ + null +} + +class infoflow4 +inherits infoflow +{ + super_w + super_r + super_none + super_both + super_unmapped +} + +class infoflow5 +inherits com_a + +class infoflow6 +inherits com_b + +class infoflow7 +inherits infoflow +{ + unmapped +} + +class infoflow8 +{ + super_w + super_r +} + +class infoflow9 +inherits com_c + +class infoflow10 +{ + read + write +} + +sensitivity low_s; +sensitivity medium_s alias med; +sensitivity high_s; + +dominance { low_s med high_s } + +category here; +category there; +category elsewhere alias lost; + +#level decl +level low_s:here.there; +level med:here, elsewhere; +level high_s:here.lost; + +#some constraints +mlsconstrain infoflow hi_r ((l1 dom l2) or (t1 == mls_exempt)); + +attribute mls_exempt; + +type system; +role system; +role system types system; + +#users +user system roles system level med range low_s - high_s:here.lost; + +#normal constraints +constrain infoflow hi_w (u1 == u2); + +#isids +sid kernel system:system:system:medium_s:here +sid security system:system:system:high_s:lost + +#fs_use +fs_use_trans devpts system:object_r:system:low_s; +fs_use_xattr ext3 system:object_r:system:low_s; +fs_use_task pipefs system:object_r:system:low_s; + +#genfscon +genfscon proc / system:object_r:system:med +genfscon proc /sys system:object_r:system:low_s +genfscon selinuxfs / system:object_r:system:high_s:here.there + +portcon tcp 80 system:object_r:system:low_s + +netifcon eth0 system:object_r:system:low_s system:object_r:system:low_s + +nodecon 127.0.0.1 255.255.255.255 system:object_r:system:low_s:here +nodecon ::1 ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff system:object_r:system:low_s:here + diff --git a/tests/policyrep/objclass.py b/tests/policyrep/objclass.py new file mode 100644 index 0000000..544463a --- /dev/null +++ b/tests/policyrep/objclass.py @@ -0,0 +1,175 @@ +# 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 Mock +except ImportError: + from mock import Mock + +from setools import SELinuxPolicy +from setools.policyrep import qpol +from setools.policyrep.exception import InvalidCommon, InvalidClass +from setools.policyrep.objclass import common_factory, class_factory + + +class CommonTest(unittest.TestCase): + + @staticmethod + def mock_common(name, perms): + policy = Mock(qpol.qpol_policy_t) + com = Mock(qpol.qpol_common_t) + com.name.return_value = name + com.perm_iter = lambda x: iter(perms) + return common_factory(policy, com) + + @classmethod + def setUpClass(cls): + cls.p = SELinuxPolicy("tests/policyrep/objclass.conf") + + def test_001_lookup(self): + """Common: factory policy lookup.""" + com = common_factory(self.p.policy, "com_a") + self.assertEqual("com_a", com.qpol_symbol.name(self.p.policy)) + + def test_002_lookup_invalid(self): + """Common: factory policy invalid lookup.""" + with self.assertRaises(InvalidCommon): + common_factory(self.p.policy, "INVALID") + + def test_003_lookup_object(self): + """Common: factory policy lookup of Common object.""" + com1 = common_factory(self.p.policy, "com_b") + com2 = common_factory(self.p.policy, com1) + self.assertIs(com2, com1) + + def test_010_string(self): + """Common: string representation""" + com = self.mock_common("test10", ["perm1", "perm2"]) + self.assertEquals("test10", str(com)) + + def test_020_perms(self): + """Common: permissions""" + com = self.mock_common("test20", ["perm1", "perm2"]) + self.assertEquals(set(["perm1", "perm2"]), com.perms) + + def test_030_statment(self): + """Common: statement.""" + com = self.mock_common("test30", ["perm1", "perm2"]) + self.assertRegexpMatches(com.statement(), "(" + "common test30\n{\n\tperm1\n\tperm2\n}" + "|" + "common test30\n{\n\tperm2\n\tperm1\n}" + ")") + + def test_040_contains(self): + """Common: contains""" + com = self.mock_common("test40", ["perm1", "perm2"]) + self.assertIn("perm1", com) + self.assertNotIn("perm3", com) + + +class ObjClassTest(unittest.TestCase): + + @staticmethod + def mock_class(name, perms, com_perms=[]): + policy = Mock(qpol.qpol_policy_t) + + cls = Mock(qpol.qpol_class_t) + cls.name.return_value = name + cls.perm_iter = lambda x: iter(perms) + + if com_perms: + com = Mock(qpol.qpol_common_t) + com.name.return_value = name+"_common" + com.perm_iter = lambda x: iter(com_perms) + cls.common.return_value = com + else: + cls.common.side_effect = ValueError + + return class_factory(policy, cls) + + @classmethod + def setUpClass(cls): + cls.p = SELinuxPolicy("tests/policyrep/objclass.conf") + + def test_001_lookup(self): + """ObjClass: factory policy lookup.""" + cls = class_factory(self.p.policy, "infoflow") + self.assertEqual("infoflow", cls.qpol_symbol.name(self.p.policy)) + + def test_002_lookup_invalid(self): + """ObjClass: factory policy invalid lookup.""" + with self.assertRaises(InvalidClass): + class_factory(self.p.policy, "INVALID") + + def test_003_lookup_object(self): + """ObjClass: factory policy lookup of ObjClass object.""" + cls1 = class_factory(self.p.policy, "infoflow4") + cls2 = class_factory(self.p.policy, cls1) + self.assertIs(cls2, cls1) + + def test_010_string(self): + """ObjClass: string representation""" + cls = self.mock_class("test10", ["perm1", "perm2"]) + self.assertEquals("test10", str(cls)) + + def test_020_perms(self): + """ObjClass: permissions""" + cls = self.mock_class("test20", ["perm1", "perm2"], com_perms=["perm3", "perm4"]) + self.assertEquals(set(["perm1", "perm2"]), cls.perms) + + def test_030_statment(self): + """ObjClass: statement, no common.""" + cls = self.mock_class("test30", ["perm1", "perm2"]) + self.assertRegexpMatches(cls.statement(), "(" + "class test30\n{\n\tperm1\n\tperm2\n}" + "|" + "class test30\n{\n\tperm2\n\tperm1\n}" + ")") + + def test_031_statment(self): + """ObjClass: statement, with common.""" + cls = self.mock_class("test31", ["perm1", "perm2"], com_perms=["perm3", "perm4"]) + self.assertRegexpMatches(cls.statement(), "(" + "class test31\ninherits test31_common\n{\n\tperm1\n\tperm2\n}" + "|" + "class test31\ninherits test31_common\n{\n\tperm2\n\tperm1\n}" + ")") + + def test_032_statment(self): + """ObjClass: statement, with common, no class perms.""" + cls = self.mock_class("test32", [], com_perms=["perm3", "perm4"]) + self.assertRegexpMatches(cls.statement(), "(" + "class test32\ninherits test32_common" + "|" + "class test32\ninherits test32_common" + ")") + + def test_040_contains(self): + """ObjClass: contains""" + cls = self.mock_class("test40", ["perm1", "perm2"]) + self.assertIn("perm1", cls) + self.assertNotIn("perm3", cls) + + def test_041_contains_common(self): + """ObjClass: contains, with common""" + cls = self.mock_class("test41", ["perm1", "perm2"], com_perms=["perm3", "perm4"]) + self.assertIn("perm1", cls) + self.assertIn("perm3", cls) + self.assertNotIn("perm5", cls)