From 320c5e60f2b8956c5732badfb4474a91e03e566c Mon Sep 17 00:00:00 2001 From: Chris PeBenito Date: Fri, 6 Mar 2015 09:19:35 -0500 Subject: [PATCH] Implement MLSCategoryQuery. closes #21 --- seinfo | 9 ++- setools/__init__.py | 1 + setools/mlscategoryquery.py | 90 +++++++++++++++++++++++ setools/policyrep/__init__.py | 12 +++- setools/policyrep/mls.py | 13 +++- setools/policyrep/qpol.i | 2 + tests/mlscategoryquery.conf | 132 ++++++++++++++++++++++++++++++++++ tests/mlscategoryquery.py | 65 +++++++++++++++++ 8 files changed, 321 insertions(+), 3 deletions(-) create mode 100644 setools/mlscategoryquery.py create mode 100644 tests/mlscategoryquery.conf create mode 100644 tests/mlscategoryquery.py diff --git a/seinfo b/seinfo index 9bec9c2..c276307 100755 --- a/seinfo +++ b/seinfo @@ -1,5 +1,5 @@ #!/usr/bin/python -# Copyright 2014, Tresys Technology, LLC +# Copyright 2014-2015, Tresys Technology, LLC # # This file is part of SETools. # @@ -110,6 +110,13 @@ try: q = setools.initsidquery.InitialSIDQuery(p) components.append(("Initial SIDs", q)) + if args.mlscatsquery or args.all: + if isinstance(args.mlscatsquery, str): + q = setools.mlscategoryquery.MLSCategoryQuery(p, name=args.mlscatsquery) + else: + q = setools.mlscategoryquery.MLSCategoryQuery(p) + components.append(("Categories", q)) + if args.netifconquery or args.all: if isinstance(args.netifconquery, str): q = setools.netifconquery.NetifconQuery(p, name=args.netifconquery) diff --git a/setools/__init__.py b/setools/__init__.py index f2f5382..f3df759 100644 --- a/setools/__init__.py +++ b/setools/__init__.py @@ -29,6 +29,7 @@ from .policyrep import SELinuxPolicy # Component Queries from . import commonquery +from . import mlscategoryquery from . import objclassquery from . import typequery from . import rolequery diff --git a/setools/mlscategoryquery.py b/setools/mlscategoryquery.py new file mode 100644 index 0000000..753377d --- /dev/null +++ b/setools/mlscategoryquery.py @@ -0,0 +1,90 @@ +# 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 compquery + + +class MLSCategoryQuery(compquery.ComponentQuery): + + """Query MLS Categories""" + + def __init__(self, policy, + name="", name_regex=False, + alias="", alias_regex=False): + """ + Parameters: + name The name of the category to match. + name_regex If true, regular expression matching will + be used for matching the name. + alias The alias name to match. + alias_regex If true, regular expression matching + will be used on the alias names. + """ + + self.policy = policy + self.set_name(name, regex=name_regex) + self.set_alias(alias, regex=alias_regex) + + def results(self): + """Generator which yields all matching categories.""" + + for cat in self.policy.mlscategories(): + if self.name and not self._match_regex( + cat, + self.name_cmp, + self.name_regex): + continue + + if self.alias and not self._match_in_set( + cat.aliases(), + self.alias_cmp, + self.alias_regex): + continue + + yield cat + + def set_alias(self, alias, **opts): + """ + Set the criteria for the category's aliases. + + Parameter: + alias Name to match the category's aliases. + + Keyword Options: + regex If true, regular expression matching will be used. + + Exceptions: + NameError Invalid keyword option. + """ + + self.alias = alias + + for k in list(opts.keys()): + if k == "regex": + self.alias_regex = opts[k] + else: + raise NameError("Invalid alias option: {0}".format(k)) + + if not self.alias: + self.alias_cmp = None + elif self.alias_regex: + self.alias_cmp = re.compile(self.alias) + else: + self.alias_cmp = self.alias diff --git a/setools/policyrep/__init__.py b/setools/policyrep/__init__.py index 89c2084..5d0943a 100644 --- a/setools/policyrep/__init__.py +++ b/setools/policyrep/__init__.py @@ -341,7 +341,17 @@ class SELinuxPolicy(object): # where a class has no default_* settings. pass - def levels(self): + def mlscategories(self): + """Generator which yields all MLS categories.""" + + for cat in self.policy.cat_iter(): + try: + yield mls.category_factory(self.policy, cat) + except TypeError: + # libqpol unfortunately iterates over aliases too + pass + + def mlslevels(self): """Generator which yields all level declarations.""" for level in self.policy.level_iter(): diff --git a/setools/policyrep/mls.py b/setools/policyrep/mls.py index 0e239b6..55ac866 100644 --- a/setools/policyrep/mls.py +++ b/setools/policyrep/mls.py @@ -77,6 +77,9 @@ def category_factory(policy, symbol): if not isinstance(symbol, qpol.qpol_cat_t): raise NotImplementedError + if symbol.isalias(policy): + raise TypeError("{0} is an alias".format(symbol.name(policy))) + return MLSCategory(policy, symbol) @@ -212,7 +215,15 @@ class MLSCategory(symbol.PolicySymbol): yield alias def statement(self): - return "category {0};".format(self) + aliases = list(self.aliases()) + stmt = "category {0}".format(self) + if aliases: + if len(aliases) > 1: + stmt += " alias {{ {0} }}".format(' '.join(aliases)) + else: + stmt += " alias {0}".format(aliases[0]) + stmt += ";" + return stmt class MLSSensitivity(symbol.PolicySymbol): diff --git a/setools/policyrep/qpol.i b/setools/policyrep/qpol.i index f140ae9..e1ff778 100644 --- a/setools/policyrep/qpol.i +++ b/setools/policyrep/qpol.i @@ -424,6 +424,7 @@ typedef enum qpol_capability }; %newobject cat_iter(); + %pythoncode %{ @QpolGenerator(_qpol.qpol_cat_from_void) %} qpol_iterator_t *cat_iter() { BEGIN_EXCEPTION qpol_iterator_t *iter; @@ -1503,6 +1504,7 @@ typedef struct qpol_cat {} qpol_cat_t; return NULL; }; %newobject alias_iter(qpol_policy_t*); + %pythoncode %{ @QpolGenerator(_qpol.to_str) %} qpol_iterator_t *alias_iter(qpol_policy_t *p) { qpol_iterator_t *iter; BEGIN_EXCEPTION diff --git a/tests/mlscategoryquery.conf b/tests/mlscategoryquery.conf new file mode 100644 index 0000000..1c21f51 --- /dev/null +++ b/tests/mlscategoryquery.conf @@ -0,0 +1,132 @@ +class infoflow +class infoflow2 +class infoflow3 +class infoflow4 +class infoflow5 +class infoflow6 +class infoflow7 + +sid kernel +sid security + +common infoflow +{ + low_w + med_w + hi_w + low_r + med_r + hi_r +} + +class infoflow +inherits infoflow + +class infoflow2 +inherits infoflow +{ + super_w + super_r +} + +class infoflow3 +{ + null +} + +class infoflow4 +inherits infoflow + +class infoflow5 +inherits infoflow + +class infoflow6 +inherits infoflow + +class infoflow7 +inherits infoflow +{ + super_w + super_r + super_none + super_both + super_unmapped +} + +sensitivity sens; + +dominance { sens } + +category begin; + +# test1 +# name: test1 +# alias: unset +category test1; + +# test2 +# name: test2(a|b) +# alias: unset +category test2a; +category test2b; + +# test 10 +# name: unset +# alias: test10a +category test10c1 alias { test10a test10c }; +category test10c2 alias { test10b test10d }; + +# test 11 +# name: unset +# alias: test11(a|b) +category test11c1 alias { test11a test11c }; +category test11c2 alias { test11b test11d }; +category test11c3 alias { test11e test11f }; + +category end; + +#level decl +level sens:begin.end; + +#some constraints +mlsconstrain infoflow hi_r ((l1 dom l2) or (t1 == mls_exempt)); + +attribute mls_exempt; + +type system; +role system; +role system types system; + +################################################################################ +# Type enforcement declarations and rules + + +################################################################################ + +#users +user system roles system level sens range sens - sens:begin.end; + +#normal constraints +constrain infoflow hi_w (u1 == u2); + +#isids +sid kernel system:system:system:sens:begin +sid security system:system:system:sens:begin + +#fs_use +fs_use_trans devpts system:object_r:system:sens; +fs_use_xattr ext3 system:object_r:system:sens; +fs_use_task pipefs system:object_r:system:sens; + +#genfscon +genfscon proc / system:object_r:system:sens +genfscon proc /sys system:object_r:system:sens +genfscon selinuxfs / system:object_r:system:sens:begin.end + +portcon tcp 80 system:object_r:system:sens + +netifcon eth0 system:object_r:system:sens system:object_r:system:sens + +nodecon 127.0.0.1 255.255.255.255 system:object_r:system:sens:begin +nodecon ::1 ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff system:object_r:system:sens:begin + diff --git a/tests/mlscategoryquery.py b/tests/mlscategoryquery.py new file mode 100644 index 0000000..fcfbce1 --- /dev/null +++ b/tests/mlscategoryquery.py @@ -0,0 +1,65 @@ +# 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.mlscategoryquery import MLSCategoryQuery + + +class MLSCategoryQueryTest(unittest.TestCase): + + def setUp(self): + self.p = SELinuxPolicy("tests/mlscategoryquery.conf") + + def test_000_unset(self): + """MLS category query with no criteria.""" + # query with no parameters gets all categories. + allcats = sorted(str(c) for c in self.p.mlscategories()) + + q = MLSCategoryQuery(self.p) + qcats = sorted(str(c) for c in q.results()) + + self.assertListEqual(allcats, qcats) + + def test_001_name_exact(self): + """MLS category query with exact name match.""" + q = MLSCategoryQuery(self.p, name="test1") + + cats = sorted(str(c) for c in q.results()) + self.assertListEqual(["test1"], cats) + + def test_002_name_regex(self): + """MLS category query with regex name match.""" + q = MLSCategoryQuery(self.p, name="test2(a|b)", name_regex=True) + + cats = sorted(str(c) for c in q.results()) + self.assertListEqual(["test2a", "test2b"], cats) + + def test_010_alias_exact(self): + """MLS category query with exact alias match.""" + q = MLSCategoryQuery(self.p, alias="test10a") + + cats = sorted(str(t) for t in q.results()) + self.assertListEqual(["test10c1"], cats) + + def test_011_alias_regex(self): + """MLS category query with regex alias match.""" + q = MLSCategoryQuery(self.p, alias="test11(a|b)", alias_regex=True) + + cats = sorted(str(t) for t in q.results()) + self.assertListEqual(["test11c1", "test11c2"], cats)