Implement MLSCategoryQuery.

closes #21
This commit is contained in:
Chris PeBenito 2015-03-06 09:19:35 -05:00
parent 34d6ce2dc5
commit 320c5e60f2
8 changed files with 321 additions and 3 deletions

9
seinfo
View File

@ -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)

View File

@ -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

View File

@ -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
# <http://www.gnu.org/licenses/>.
#
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

View File

@ -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():

View File

@ -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):

View File

@ -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

132
tests/mlscategoryquery.conf Normal file
View File

@ -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

65
tests/mlscategoryquery.py Normal file
View File

@ -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 <http://www.gnu.org/licenses/>.
#
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)