Implement object class and commons queries.

This commit is contained in:
Chris PeBenito 2014-08-01 08:56:29 -04:00
parent a15c7ffd9b
commit 997be5787b
9 changed files with 739 additions and 0 deletions

View File

@ -25,6 +25,8 @@ import policyrep
from policyrep import SELinuxPolicy
# Component Queries
import commonquery
import objclassquery
import typequery
import rolequery
import userquery

101
libapol/commonquery.py Normal file
View File

@ -0,0 +1,101 @@
# Copyright 2014, 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
import compquery
class CommonQuery(compquery.ComponentQuery):
"""Query common permission sets."""
def __init__(self, policy,
name="", name_regex=False,
perms=set(), perms_equal=False, perms_regex=False):
"""
Parameters:
name The name of the common to match.
name_regex If true, regular expression matching will
be used for matching the name.
perms The permissions to match.
perms_equal If true, only commons with permission sets
that are equal to the criteria will
match. Otherwise, any intersection
will match.
perms_regex If true, regular expression matching will be used
on the permission names instead of set logic.
"""
self.policy = policy
self.set_name(name, regex=name_regex)
self.set_perms(perms, regex=perms_regex, equal=perms_equal)
def results(self):
"""Generator which yields all matching commons."""
for com in self.policy.commons():
if self.name and not self._match_regex(
com,
self.name,
self.name_regex,
self.name_cmp):
continue
if self.perms and not self._match_regex_or_set(
com.perms,
self.perms,
self.perms_equal,
self.perms_regex,
self.perms_cmp):
continue
yield com
def set_perms(self, perms, **opts):
"""
Set the criteria for the common's permissions.
Parameter:
perms Name to match the common's permissions.
Keyword Options:
regex If true, regular expression matching will be used.
equal If true, the permisison set of the common
must equal the permissions criteria to
match. If false, any intersection in the
critera will cause a match.
Exceptions:
NameError Invalid keyword option.
"""
self.perms = perms
for k in opts.keys():
if k == "regex":
self.perms_regex = opts[k]
elif k == "equal":
self.perms_equal = opts[k]
else:
raise NameError("Invalid permissions option: {0}".format(k))
if self.perms_regex:
self.perms_cmp = re.compile(self.perms)
else:
self.perms_cmp = None

165
libapol/objclassquery.py Normal file
View File

@ -0,0 +1,165 @@
# Copyright 2014, 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
import compquery
from policyrep.objclass import NoCommon
class ObjClassQuery(compquery.ComponentQuery):
"""Query object classes."""
def __init__(self, policy,
name="", name_regex=False,
common="", common_regex=False,
perms=set(), perms_equal=False, perms_regex=False,
perms_indirect=True):
"""
Parameters:
name The name of the object set to match.
name_regex If true, regular expression matching will
be used for matching the name.
common The name of the inherited common to match.
common_regex If true, regular expression matching will
be used for matching the common name.
perms The permissions to match.
perms_equal If true, only commons with permission sets
that are equal to the criteria will
match. Otherwise, any intersection
will match.
perms_regex If true, regular expression matching
will be used on the permission names instead
of set logic.
comparison will not be used.
perms_indirect If false, permissions inherited from a common
permission set not will be evaluated. Default
is true.
"""
self.policy = policy
self.set_name(name, regex=name_regex)
self.set_common(common, regex=common_regex)
self.set_perms(perms,
regex=perms_regex,
equal=perms_equal,
indirect=perms_indirect)
def results(self):
"""Generator which yields all matching object classes."""
for class_ in self.policy.classes():
if self.name and not self._match_regex(
class_,
self.name,
self.name_regex,
self.name_cmp):
continue
if self.common:
try:
if not self._match_regex(
class_.common,
self.common,
self.common_regex,
self.common_cmp):
continue
except NoCommon:
continue
if self.perms:
perms = class_.perms
if self.perms_indirect:
try:
perms |= class_.common.perms
except NoCommon:
pass
if not self._match_regex_or_set(
perms,
self.perms,
self.perms_equal,
self.perms_regex,
self.perms_cmp):
continue
yield class_
def set_common(self, common, **opts):
"""
Set the criteria for matching the common's name.
Parameter:
name Name to match the common's name.
regex If true, regular expression matching will be used.
Exceptions:
NameError Invalid keyword option.
"""
self.common = str(common)
for k in opts.keys():
if k == "regex":
self.common_regex = opts[k]
else:
raise NameError("Invalid common option: {0}".format(k))
if self.common_regex:
self.common_cmp = re.compile(self.common)
else:
self.common_cmp = None
def set_perms(self, perms, **opts):
"""
Set the criteria for the common's permissions.
Parameter:
perms Name to match the common's permissions.
Keyword Options:
regex If true, regular expression matching will be used.
equal If true, the permisison set of the common
must equal the permissions criteria to
match. If false, any intersection in the
critera will cause a match.
indirect If true, the permissions inherited from a common
permission set will be included.
Exceptions:
NameError Invalid keyword option.
"""
self.perms = perms
for k in opts.keys():
if k == "regex":
self.perms_regex = opts[k]
elif k == "equal":
self.perms_equal = opts[k]
elif k == "indirect":
self.perms_indirect = opts[k]
else:
raise NameError("Invalid permissions option: {0}".format(k))
if self.perms_regex:
self.perms_cmp = re.compile(self.perms)
else:
self.perms_cmp = None

12
seinfo
View File

@ -132,3 +132,15 @@ if args.rolequery:
print(r.statement())
else:
print(r)
if args.classquery:
if isinstance(args.classquery, str):
q = libapol.objclassquery.ObjClassQuery(p, args.classquery)
else:
q = libapol.objclassquery.ObjClassQuery(p)
for c in sorted(q.results()):
if args.expand:
print(c.statement())
else:
print(c)

View File

@ -16,7 +16,9 @@
# along with SETools. If not, see <http://www.gnu.org/licenses/>.
#
import boolquery
import commonquery
import dta
import objclassquery
import polcapquery
import infoflow
import terulequery

116
tests/commonquery.conf Normal file
View File

@ -0,0 +1,116 @@
class infoflow
sid kernel
sid security
common test1
{
hi_w
hi_r
super_r
super_w
}
common test2a
{
send
recv
}
common test2b
{
sig
}
common test10a
{
null
}
common test10b
{
null
ping
}
common test11a
{
read
write
}
common test11b
{
read
}
common test11c
{
write
}
common test12a
{
signal
sigchld
}
common test12b
{
sigkill
}
class infoflow
inherits test1
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

74
tests/commonquery.py Normal file
View File

@ -0,0 +1,74 @@
# Copyright 2014, 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 libapol import SELinuxPolicy
from libapol.commonquery import CommonQuery
class CommonQueryTest(unittest.TestCase):
def setUp(self):
self.p = SELinuxPolicy("tests/commonquery.conf")
def test_000_unset(self):
"""Common query with no criteria."""
# query with no parameters gets all types.
for numcommons, c in enumerate(self.p.commons(), start=1):
pass
q = CommonQuery(self.p)
for q_numcommons, u in enumerate(q.results(), start=1):
pass
self.assertEqual(numcommons, q_numcommons)
def test_001_name_exact(self):
"""Common query with exact name match."""
q = CommonQuery(self.p, name="test1")
commons = sorted(str(c) for c in q.results())
self.assertListEqual(["test1"], commons)
def test_002_name_regex(self):
"""Common query with regex name match."""
q = CommonQuery(self.p, name="test2(a|b)", name_regex=True)
commons = sorted(str(c) for c in q.results())
self.assertListEqual(["test2a", "test2b"], commons)
def test_010_perm_indirect_intersect(self):
"""Common query with intersect permission name patch."""
q = CommonQuery(self.p, perms=set(["null"]), perms_equal=False)
commons = sorted(str(c) for c in q.results())
self.assertListEqual(["test10a", "test10b"], commons)
def test_011_perm_indirect_equal(self):
"""Common query with equal permission name patch."""
q = CommonQuery(self.p, perms=set(["read", "write"]), perms_equal=True)
commons = sorted(str(c) for c in q.results())
self.assertListEqual(["test11a"], commons)
def test_012_perm_indirect_regex(self):
"""Common query with regex permission name patch."""
q = CommonQuery(self.p, perms="sig.+", perms_regex=True)
commons = sorted(str(c) for c in q.results())
self.assertListEqual(["test12a", "test12b"], commons)

149
tests/objclassquery.conf Normal file
View File

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

118
tests/objclassquery.py Normal file
View File

@ -0,0 +1,118 @@
# Copyright 2014, 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 libapol import SELinuxPolicy
from libapol.objclassquery import ObjClassQuery
class ObjClassQueryTest(unittest.TestCase):
def setUp(self):
self.p = SELinuxPolicy("tests/objclassquery.conf")
def test_000_unset(self):
"""Class query with no criteria."""
# query with no parameters gets all types.
for numclasses, c in enumerate(self.p.classes(), start=1):
pass
q = ObjClassQuery(self.p)
for q_numclasses, u in enumerate(q.results(), start=1):
pass
self.assertEqual(numclasses, q_numclasses)
def test_001_name_exact(self):
"""Class query with exact name match."""
q = ObjClassQuery(self.p, name="infoflow")
classes = sorted(str(c) for c in q.results())
self.assertListEqual(["infoflow"], classes)
def test_002_name_regex(self):
"""Class query with regex name match."""
q = ObjClassQuery(self.p, name="infoflow(2|3)", name_regex=True)
classes = sorted(str(c) for c in q.results())
self.assertListEqual(["infoflow2", "infoflow3"], classes)
def test_010_common_exact(self):
"""Class query with exact common name match."""
q = ObjClassQuery(self.p, common="infoflow")
classes = sorted(str(c) for c in q.results())
self.assertListEqual(["infoflow", "infoflow2",
"infoflow4", "infoflow7"], classes)
def test_011_common_regex(self):
"""Class query with regex common name match."""
q = ObjClassQuery(self.p, common="com_[ab]", common_regex=True)
classes = sorted(str(c) for c in q.results())
self.assertListEqual(["infoflow5", "infoflow6"], classes)
def test_020_perm_indirect_intersect(self):
"""Class query with indirect, intersect permission name patch."""
q = ObjClassQuery(
self.p, perms=set(["send"]), perms_indirect=True, perms_equal=False)
classes = sorted(str(c) for c in q.results())
self.assertListEqual(["infoflow6"], classes)
def test_021_perm_direct_intersect(self):
"""Class query with direct, intersect permission name patch."""
q = ObjClassQuery(
self.p, perms=set(["super_r"]), perms_indirect=False, perms_equal=False)
classes = sorted(str(c) for c in q.results())
self.assertListEqual(["infoflow2", "infoflow4", "infoflow8"], classes)
def test_022_perm_indirect_equal(self):
"""Class query with indirect, equal permission name patch."""
q = ObjClassQuery(self.p, perms=set(
["low_w", "med_w", "hi_w", "low_r", "med_r", "hi_r", "unmapped"]), perms_indirect=True, perms_equal=True)
classes = sorted(str(c) for c in q.results())
self.assertListEqual(["infoflow7"], classes)
def test_023_perm_direct_equal(self):
"""Class query with direct, equal permission name patch."""
q = ObjClassQuery(self.p, perms=set(
["super_r", "super_w"]), perms_indirect=False, perms_equal=True)
classes = sorted(str(c) for c in q.results())
self.assertListEqual(["infoflow2", "infoflow8"], classes)
def test_024_perm_indirect_regex(self):
"""Class query with indirect, regex permission name patch."""
q = ObjClassQuery(
self.p, perms="(send|setattr)", perms_indirect=True, perms_regex=True)
classes = sorted(str(c) for c in q.results())
self.assertListEqual(["infoflow6", "infoflow9"], classes)
def test_025_perm_direct_regex(self):
"""Class query with direct, regex permission name patch."""
q = ObjClassQuery(
self.p, perms="(read|super_r)", perms_indirect=False, perms_regex=True)
classes = sorted(str(c) for c in q.results())
self.assertListEqual(["infoflow10", "infoflow2",
"infoflow4", "infoflow8"],
classes)