diff --git a/seinfo b/seinfo index 77aa105..ac8ea32 100755 --- a/seinfo +++ b/seinfo @@ -97,6 +97,13 @@ try: q = setools.objclassquery.ObjClassQuery(p) components.append(("Classes", q)) + if args.constraintquery or args.all: + if isinstance(args.constraintquery, str): + q = setools.constraintquery.ConstraintQuery(p, tclass=args.constraintquery) + else: + q = setools.constraintquery.ConstraintQuery(p) + components.append(("Constraints", q)) + if args.fsusequery or args.all: if isinstance(args.fsusequery, str): q = setools.fsusequery.FSUseQuery(p, fs=args.fsusequery) diff --git a/setools/__init__.py b/setools/__init__.py index 3e12e09..79dd9d6 100644 --- a/setools/__init__.py +++ b/setools/__init__.py @@ -43,6 +43,9 @@ from . import mlsrulequery from . import rbacrulequery from . import terulequery +# Constraint queries +from . import constraintquery + # In-policy Context Queries from . import fsusequery from . import genfsconquery diff --git a/setools/constraintquery.py b/setools/constraintquery.py new file mode 100644 index 0000000..c94aaed --- /dev/null +++ b/setools/constraintquery.py @@ -0,0 +1,78 @@ +# 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 +# . +# +import re + +from . import mixins +from .query import PolicyQuery + + +class ConstraintQuery(mixins.MatchObjClass, mixins.MatchPermission, PolicyQuery): + + """Query constraint rules (constraint/mlsconstraint).""" + + def __init__(self, policy, + ruletype=[], + tclass="", tclass_regex=False, + perms=set(), perms_equal=False): + + """ + Parameter: + policy The policy to query. + 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. + perms The permission(s) to match. + perms_equal If true, the permission set of the rule + must exactly match the permissions + criteria. If false, any set intersection + will match. + """ + + self.policy = policy + + self.set_ruletype(ruletype) + self.set_tclass(tclass, regex=tclass_regex) + self.set_perms(perms, equal=perms_equal) + + def results(self): + """Generator which yields all matching constraints rules.""" + + for c in self.policy.constraints(): + if self.ruletype: + if c.ruletype not in self.ruletype: + continue + + if self.tclass and not self._match_object_class(c.tclass): + continue + + if self.perms and not self._match_perms(c.perms): + continue + + yield c + + def set_ruletype(self, ruletype): + """ + Set the rule types for the rule query. + + Parameter: + ruletype The rule types to match. + """ + + self.ruletype = ruletype diff --git a/setools/policyrep/__init__.py b/setools/policyrep/__init__.py index 1e33b7a..e8dc474 100644 --- a/setools/policyrep/__init__.py +++ b/setools/policyrep/__init__.py @@ -143,7 +143,7 @@ class SELinuxPolicy(object): @property def constraint_count(self): """The number of standard constraints.""" - return sum(1 for _ in self.constraints()) + return sum(1 for c in self.constraints() if c.ruletype == "constrain") @property def dontaudit_count(self): @@ -173,12 +173,12 @@ class SELinuxPolicy(object): @property def mlsconstraint_count(self): """The number of MLS constraints.""" - return sum(1 for _ in self.mlsconstraints()) + return sum(1 for c in self.constraints() if c.ruletype == "mlsconstrain") @property def mlsvalidatetrans_count(self): """The number of MLS validatetrans.""" - return sum(1 for _ in self.mlsvalidatetrans()) + return sum(1 for v in self.validatetrans() if v.ruletype == "mlsvalidatetrans") @property def netifcon_count(self): @@ -263,12 +263,11 @@ class SELinuxPolicy(object): @property def validatetrans_count(self): """The number of validatetrans.""" - return sum(1 for _ in self.validatetrans()) + return sum(1 for v in self.validatetrans() if v.ruletype == "validatetrans") # # Policy components lookup functions # - def lookup_attribute(self, name): """Look up an attribute by name.""" return typeattr.attribute_factory(self.policy, name) @@ -446,40 +445,16 @@ class SELinuxPolicy(object): # def constraints(self): - """Generator which yields all constraints.""" + """Generator which yields all constraints (regular and MLS).""" for constraint_ in self.policy.constraint_iter(): - try: - yield constraint.constraint_factory(self.policy, constraint_) - except TypeError: - pass - - def mlsconstraints(self): - """Generator which yields all MLS constraints.""" - - for constraint_ in self.policy.constraint_iter(): - try: - yield constraint.mlsconstraint_factory(self.policy, constraint_) - except TypeError: - pass - - def mlsvalidatetrans(self): - """Generator which yields all mlsvalidatetrans.""" - - for validatetrans in self.policy.validatetrans_iter(): - try: - yield constraint.mlsvalidatetrans_factory(self.policy, validatetrans) - except TypeError: - pass + yield constraint.constraint_factory(self.policy, constraint_) def validatetrans(self): - """Generator which yields all validatetrans.""" + """Generator which yields all validatetrans (regular and MLS).""" for validatetrans in self.policy.validatetrans_iter(): - try: - yield constraint.validatetrans_factory(self.policy, validatetrans) - except TypeError: - pass + yield constraint.validatetrans_factory(self.policy, validatetrans) # # In-policy Labeling statement generators diff --git a/setools/policyrep/constraint.py b/setools/policyrep/constraint.py index d2fead7..ede8781 100644 --- a/setools/policyrep/constraint.py +++ b/setools/policyrep/constraint.py @@ -35,52 +35,30 @@ def _is_mls(policy, symbol): def constraint_factory(policy, symbol): - """Factory function for creating regular constraint objects.""" + """Factory function for creating constraint objects.""" try: if _is_mls(policy, symbol): - raise TypeError("Symbol is not a constrain.") + return Constraint(policy, symbol, "mlsconstrain") + else: + return Constraint(policy, symbol, "constrain") + except AttributeError: raise TypeError("Constraints cannot be looked-up.") - return Constraint(policy, symbol, "constrain") - - -def mlsconstraint_factory(policy, symbol): - """Factory function for creating MLS constraint objects.""" - - try: - if not _is_mls(policy, symbol): - raise TypeError("Symbol is not a mlsconstrain.") - except AttributeError: - raise TypeError("mlsconstrain cannot be looked-up.") - - return Constraint(policy, symbol, "mlsconstrain") - def validatetrans_factory(policy, symbol): - """Factory function for creating regular validatetrans objects.""" + """Factory function for creating validatetrans objects.""" try: if _is_mls(policy, symbol): - raise TypeError("Symbol is not a validatetrans.") + return Validatetrans(policy, symbol, "mlsvalidatetrans") + else: + return Validatetrans(policy, symbol, "validatetrans") + except AttributeError: raise TypeError("validatetrans cannot be looked-up.") - return Validatetrans(policy, symbol, "validatetrans") - - -def mlsvalidatetrans_factory(policy, symbol): - """Factory function for creating MLS validatetrans objects.""" - - try: - if not _is_mls(policy, symbol): - raise TypeError("Symbol is not a mlsvalidatetrans.") - except AttributeError: - raise TypeError("mlsvalidatetrans cannot be looked-up.") - - return Validatetrans(policy, symbol, "mlsvalidatetrans") - class BaseConstraint(symbol.PolicySymbol): diff --git a/tests/constraintquery.conf b/tests/constraintquery.conf new file mode 100644 index 0000000..5187ee3 --- /dev/null +++ b/tests/constraintquery.conf @@ -0,0 +1,176 @@ +class test1 +class test10 +class test11a +class test11b +class test11c +class test12a +class test12b +class test12c +class test20a +class test20b +class test20c +class test21a +class test21b +class test21c + +sid kernel +sid security + +common test +{ + low_w + med_w + hi_w + low_r + med_r + hi_r +} + + +class test1 +inherits test + +class test10 +inherits test + +class test11a +inherits test + +class test11b +inherits test + +class test11c +inherits test + +class test12a +inherits test + +class test12b +inherits test + +class test12c +inherits test + +class test20a +{ + test20ap + test20bp +} + +class test20b +{ + test20ap + test20bp +} + +class test20c +{ + test20ap + test20bp +} + +class test21a +{ + test21ap + test21bp +} + +class test21b +{ + test21ap + test21bp +} + +class test21c +{ + test21ap + test21bp +} + + +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 low_s:here.there; +level med:here, elsewhere; +level high_s:here.lost; + +# test 1: +# ruletype: mlsconstrain +# tclass: unset +# perms: unset +mlsconstrain test1 hi_r ((l1 dom l2) or (t1 == mls_exempt)); + +attribute mls_exempt; + +type system; +role system; +role system types system; + +user system roles system level med range low_s - high_s:here.lost; + +# test 10: +# ruletype: unset +# tclass: test10 +# perms: unset +constrain test10 hi_w (u1 == u2); + +# test 11: +# ruletype: unset +# tclass: test11a, test11b +# perms: unset +constrain test11a hi_w (u1 == u2); +constrain test11b hi_w (u1 == u2); +constrain test11c hi_w (u1 == u2); + +# test 12: +# ruletype: unset +# tclass: intoflow12(a|c), regex +# perms: unset +constrain test12a hi_w (u1 == u2); +constrain test12b hi_w (u1 == u2); +constrain test12c hi_w (u1 == u2); + +# test 20: +# ruletype: unset +# tclass: unset +# perms: test20ap, test20bp +constrain test20a test20ap (u1 == u2); +constrain test20b test20bp (u1 == u2); + +# test 21: +# ruletype: unset +# tclass: unset +# perms: test21ap, test21bp, equal +constrain test21a test21ap (u1 == u2); +constrain test21b test21bp (u1 == u2); +constrain test21c { test21bp test21ap } (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/constraintquery.py b/tests/constraintquery.py new file mode 100644 index 0000000..9ed73ed --- /dev/null +++ b/tests/constraintquery.py @@ -0,0 +1,78 @@ +# 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 + +from setools import SELinuxPolicy +from setools.constraintquery import ConstraintQuery + + +class ConstraintQueryTest(unittest.TestCase): + + def setUp(self): + self.p = SELinuxPolicy("tests/constraintquery.conf") + + def test_000_unset(self): + """Constraint query with no criteria.""" + allconstraint = sorted(c.tclass for c in self.p.constraints()) + + q = ConstraintQuery(self.p) + qconstraint = sorted(c.tclass for c in q.results()) + + self.assertListEqual(allconstraint, qconstraint) + + def test_001_ruletype(self): + """Constraint query with rule type match.""" + q = ConstraintQuery(self.p, ruletype=["mlsconstrain"]) + + constraint = sorted(c.tclass for c in q.results()) + self.assertListEqual(["test1"], constraint) + + def test_010_class_exact(self): + """Constraint query with exact object class match.""" + q = ConstraintQuery(self.p, tclass="test10") + + constraint = sorted(c.tclass for c in q.results()) + self.assertListEqual(["test10"], constraint) + + def test_011_class_list(self): + """Constraint query with object class list match.""" + q = ConstraintQuery(self.p, tclass=["test11a", "test11b"]) + + constraint = sorted(c.tclass for c in q.results()) + self.assertListEqual(["test11a", "test11b"], constraint) + + def test_012_class_list(self): + """Constraint query with object class list match.""" + q = ConstraintQuery(self.p, tclass="test12(a|c)", tclass_regex=True) + + constraint = sorted(c.tclass for c in q.results()) + self.assertListEqual(["test12a", "test12c"], constraint) + + def test_020_perms_any(self): + """Constraint query with permission set intersection match.""" + q = ConstraintQuery(self.p, perms=["test20ap", "test20bp"]) + + constraint = sorted(c.tclass for c in q.results()) + self.assertListEqual(["test20a", "test20b"], constraint) + + def test_020_perms_equal(self): + """Constraint query with permission set equality match.""" + q = ConstraintQuery(self.p, perms=["test21ap", "test21bp"], perms_equal=True) + + constraint = sorted(c.tclass for c in q.results()) + self.assertListEqual(["test21c"], constraint)