diff --git a/setools/query.py b/setools/query.py index e50aa20..74e9626 100644 --- a/setools/query.py +++ b/setools/query.py @@ -133,6 +133,27 @@ class PolicyQuery(object): else: return (crit_low == obj_low and obj_high == crit_high) + @staticmethod + def _match_level(obj, criteria, dom, domby, incomp): + """ + Match the an MLS level. + + obj The level to match. + criteria The criteria to match. (a level) + dom If true, the criteria will match if it dominates obj. + domby If true, the criteria will match if it is dominated by obj. + incomp If true, the criteria will match if it is incomparable to obj. + """ + + if dom: + return (criteria >= obj) + elif domby: + return (criteria <= obj) + elif incomp: + return (criteria ^ obj) + else: + return (criteria == obj) + def results(self): """ Generator which returns the matches for the query. This method diff --git a/setools/userquery.py b/setools/userquery.py index 982698e..70805ce 100644 --- a/setools/userquery.py +++ b/setools/userquery.py @@ -27,26 +27,41 @@ class UserQuery(compquery.ComponentQuery): def __init__(self, policy, name="", name_regex=False, - roles=set(), roles_equal=False, roles_regex=False): + roles=set(), roles_equal=False, roles_regex=False, + level="", level_dom=False, level_domby=False, level_incomp=False, + range_="", range_overlap=False, range_subset=False, + range_superset=False, range_proper=False): """ Parameter: - policy The policy to query. - name The user name to match. - name_regex If true, regular expression matching - will be used on the user names. - roles The attribute to match. - roles_equal If true, only types with role sets - that are equal to the criteria will - match. Otherwise, any intersection - will match. - roles_regex If true, regular expression matching - will be used on the role names instead - of set logic. + policy The policy to query. + name The user name to match. + name_regex If true, regular expression matching + will be used on the user names. + roles The attribute to match. + roles_equal If true, only types with role sets + that are equal to the criteria will + match. Otherwise, any intersection + will match. + roles_regex If true, regular expression matching + will be used on the role names instead + of set logic. + range_ The criteria to match the user's range. + range_subset If true, the criteria will match if it is a subset + of the user's range. + range_overlap If true, the criteria will match if it overlaps + any of the user's range. + range_superset If true, the criteria will match if it is a superset + of the user's range. + range_proper If true, use proper superset/subset operations. + No effect if not using set operations. """ self.policy = policy self.set_name(name, regex=name_regex) self.set_roles(roles, regex=roles_regex, equal=roles_equal) + self.set_level(level, dom=level_dom, domby=level_domby, incomp=level_incomp) + self.set_range(range_, overlap=range_overlap, subset=range_subset, + superset=range_superset, proper=range_proper) def results(self): """Generator which yields all matching users.""" @@ -65,10 +80,94 @@ class UserQuery(compquery.ComponentQuery): self.roles_regex): continue - # TODO: default level and range + if self.level and not self._match_level( + u.mls_level, + self.level, + self.level_dom, + self.level_domby, + self.level_incomp): + continue + + if self.range_ and not self._match_range( + (u.mls_range.low, u.mls_range.high), + (self.range_.low, self.range_.high), + self.range_subset, + self.range_overlap, + self.range_superset, + self.range_proper): + continue yield u + def set_level(self, level, **opts): + """ + Set the criteria for matching the user's default level. + + Parameter: + level Criteria to match the user's default level. + + Keyword Parameters: + dom If true, the criteria will match if it dominates the user's default level. + domby If true, the criteria will match if it is dominated by the user's default level. + incomp If true, the criteria will match if it incomparable to the user's default level. + + Exceptions: + NameError Invalid keyword option. + """ + + if level: + self.level = self.policy.lookup_level(level) + else: + self.level = None + + for k in list(opts.keys()): + if k == "dom": + self.level_dom = opts[k] + elif k == "domby": + self.level_domby = opts[k] + elif k == "incomp": + self.level_incomp = opts[k] + else: + raise NameError("Invalid name option: {0}".format(k)) + + def set_range(self, range_, **opts): + """ + Set the criteria for matching the user's range. + + Parameter: + range_ Criteria to match the user's range. + + Keyword Parameters: + subset If true, the criteria will match if it is a subset + of the user's range. + overlap If true, the criteria will match if it overlaps + any of the user's range. + superset If true, the criteria will match if it is a superset + of the user's range. + proper If true, use proper superset/subset operations. + No effect if not using set operations. + + Exceptions: + NameError Invalid keyword option. + """ + + if range_: + self.range_ = self.policy.lookup_range(range_) + else: + self.range_ = None + + for k in list(opts.keys()): + if k == "subset": + self.range_subset = opts[k] + elif k == "overlap": + self.range_overlap = opts[k] + elif k == "superset": + self.range_superset = opts[k] + elif k == "proper": + self.range_proper = opts[k] + else: + raise NameError("Invalid name option: {0}".format(k)) + def set_roles(self, roles, **opts): """ Set the criteria for the users's roles. diff --git a/tests/userquery.conf b/tests/userquery.conf index 35a4e24..079a7a0 100644 --- a/tests/userquery.conf +++ b/tests/userquery.conf @@ -53,20 +53,33 @@ inherits infoflow super_unmapped } -sensitivity low_s; -sensitivity medium_s alias med; -sensitivity high_s; +sensitivity s0; +sensitivity s1; +sensitivity s2; +sensitivity s3; +sensitivity s4; +sensitivity s5; +sensitivity s6; -dominance { low_s med high_s } +dominance { s0 s1 s2 s3 s4 s5 s6 } -category here; -category there; -category elsewhere alias lost; +category c0; +category c1; +category c2; +category c3; +category c4; +category c5; +category c6; +category c7; #level decl -level low_s:here.there; -level med:here, elsewhere; -level high_s:here.lost; +level s0:c0.c7; +level s1:c0.c7; +level s2:c0.c7; +level s3:c0.c7; +level s4:c0.c7; +level s5:c0.c7; +level s6:c0.c7; #some constraints mlsconstrain infoflow hi_r ((l1 dom l2) or (t1 == mls_exempt)); @@ -105,75 +118,143 @@ role test12c_r; # test 1 # name: test1_u # roles: unset -user test1_u roles test1_r level med range med; +user test1_u roles test1_r level s3:c5,c7 range s3:c5,c7; # test 2 # name: test2_u(1|2) regex # roles: unset -user test2_u1 roles test2a_r level med range med; -user test2_u2 roles test2b_r level med range med; +user test2_u1 roles test2a_r level s3:c5,c7 range s3:c5,c7; +user test2_u2 roles test2b_r level s3:c5,c7 range s3:c5,c7; # test 10 # name: unset # roles: test10a_r,test10b_r -user test10_u1 roles test10a_r level med range med; -user test10_u2 roles { test10a_r test10b_r } level med range med; -user test10_u3 roles { test10a_r test10b_r test10c_r } level med range med; -user test10_u4 roles { test10b_r test10c_r } level med range med; -user test10_u5 roles { test10a_r test10c_r } level med range med; -user test10_u6 roles test10b_r level med range med; -user test10_u7 roles test10c_r level med range med; +user test10_u1 roles test10a_r level s3:c5,c7 range s3:c5,c7; +user test10_u2 roles { test10a_r test10b_r } level s3:c5,c7 range s3:c5,c7; +user test10_u3 roles { test10a_r test10b_r test10c_r } level s3:c5,c7 range s3:c5,c7; +user test10_u4 roles { test10b_r test10c_r } level s3:c5,c7 range s3:c5,c7; +user test10_u5 roles { test10a_r test10c_r } level s3:c5,c7 range s3:c5,c7; +user test10_u6 roles test10b_r level s3:c5,c7 range s3:c5,c7; +user test10_u7 roles test10c_r level s3:c5,c7 range s3:c5,c7; # test 11 # name: unset # roles: test11a_r,test11b_r equal -user test11_u1 roles test11a_r level med range med; -user test11_u2 roles { test11a_r test11b_r } level med range med; -user test11_u3 roles { test11a_r test11b_r test11c_r } level med range med; -user test11_u4 roles { test11b_r test11c_r } level med range med; -user test11_u5 roles { test11a_r test11c_r } level med range med; -user test11_u6 roles test11b_r level med range med; -user test11_u7 roles test11c_r level med range med; +user test11_u1 roles test11a_r level s3:c5,c7 range s3:c5,c7; +user test11_u2 roles { test11a_r test11b_r } level s3:c5,c7 range s3:c5,c7; +user test11_u3 roles { test11a_r test11b_r test11c_r } level s3:c5,c7 range s3:c5,c7; +user test11_u4 roles { test11b_r test11c_r } level s3:c5,c7 range s3:c5,c7; +user test11_u5 roles { test11a_r test11c_r } level s3:c5,c7 range s3:c5,c7; +user test11_u6 roles test11b_r level s3:c5,c7 range s3:c5,c7; +user test11_u7 roles test11c_r level s3:c5,c7 range s3:c5,c7; # test 12 # name: unset # roles: test12(a|b)_r regex -user test12_u1 roles test12a_r level med range med; -user test12_u2 roles { test12a_r test12b_r } level med range med; -user test12_u3 roles { test12a_r test12b_r test12c_r } level med range med; -user test12_u4 roles { test12b_r test12c_r } level med range med; -user test12_u5 roles { test12a_r test12c_r } level med range med; -user test12_u6 roles test12b_r level med range med; -user test12_u7 roles test12c_r level med range med; +user test12_u1 roles test12a_r level s3:c5,c7 range s3:c5,c7; +user test12_u2 roles { test12a_r test12b_r } level s3:c5,c7 range s3:c5,c7; +user test12_u3 roles { test12a_r test12b_r test12c_r } level s3:c5,c7 range s3:c5,c7; +user test12_u4 roles { test12b_r test12c_r } level s3:c5,c7 range s3:c5,c7; +user test12_u5 roles { test12a_r test12c_r } level s3:c5,c7 range s3:c5,c7; +user test12_u6 roles test12b_r level s3:c5,c7 range s3:c5,c7; +user test12_u7 roles test12c_r level s3:c5,c7 range s3:c5,c7; +# test 20 +# name: unset +# roles: unset +# level: equal +# range: unset +user test20 roles system level s3:c0,c4 range s3 - s3:c0.c4,c7; +# test 21 +# name: unset +# roles: unset +# level: high, dom +# range: unset +user test21 roles system level s2:c1,c4 range s2 - s2:c0.c4,c7; + +# test 22 +# name: unset +# roles: unset +# level: high, domby +# range: unset +user test22 roles system level s4:c2,c4 range s4 - s4:c0.c4,c7; + +# test 23 +# name: unset +# roles: unset +# level: high, incomp +# range: unset +user test23 roles system level s3:c6 range s3 - s3:c0.c7; + +# test 40: +# name: unset +# roles: unset +# level: unset +# range: equal +user test40 roles system level s0:c5 range s0:c5 - s0:c0.c5; + +# test 41: +# name: unset +# roles: unset +# level: unset +# range: overlap +user test41 roles system level s1:c5 range s1:c5 - s1:c1.c3,c5; + +# test 42: +# name: unset +# roles: unset +# level: unset +# range: subset +user test42 roles system level s2:c5 range s2:c5 - s2:c1.c3,c5; + +# test 43: +# name: unset +# roles: unset +# level: unset +# range: superset +user test43 roles system level s3:c5 range s3:c5 - s3:c1.c3,c5,c6; + +# test 44: +# name: unset +# roles: unset +# level: unset +# range: proper subset +user test44 roles system level s4:c5 range s4:c5 - s4:c1.c3,c5; + +# test 45: +# name: unset +# roles: unset +# level: unset +# range: proper superset +user test45 roles system level s5:c5 range s5:c5 - s5:c1.c3,c5; ################################################################################ #users -user system roles system level med range low_s - high_s:here.lost; +user system roles system level s0:c5 range s0:c5; #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 +sid kernel system:system:system:s0:c5 +sid security system:system:system:s0:c5 #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; +fs_use_trans devpts system:object_r:system:s0:c5; +fs_use_xattr ext3 system:object_r:system:s0:c5; +fs_use_task pipefs system:object_r:system:s0:c5; #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 +genfscon proc / system:object_r:system:s3 +genfscon proc /sys system:object_r:system:s0:c5 +genfscon selinuxfs / system:object_r:system:s3:c0.c4 -portcon tcp 80 system:object_r:system:low_s +portcon tcp 80 system:object_r:system:s0:c5 -netifcon eth0 system:object_r:system:low_s system:object_r:system:low_s +netifcon eth0 system:object_r:system:s0:c5 system:object_r:system:s0:c5 -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 +nodecon 127.0.0.1 255.255.255.255 system:object_r:system:s0:c5 +nodecon ::1 ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff system:object_r:system:s0:c5 diff --git a/tests/userquery.py b/tests/userquery.py index 558ebcf..24b2273 100644 --- a/tests/userquery.py +++ b/tests/userquery.py @@ -1,4 +1,4 @@ -# Copyright 2014, Tresys Technology, LLC +# Copyright 2014-2015, Tresys Technology, LLC # # This file is part of SETools. # @@ -73,3 +73,171 @@ class UserQueryTest(unittest.TestCase): users = sorted(str(u) for u in q.results()) self.assertListEqual(["test12_u1", "test12_u2", "test12_u3", "test12_u4", "test12_u5", "test12_u6"], users) + + def test_020_level_equal(self): + """User query with default level equality.""" + q = UserQuery(self.p, level="s3:c0,c4") + + users = sorted(str(u) for u in q.results()) + self.assertListEqual(["test20"], users) + + def test_021_level_dom1(self): + """User query with default level dominance.""" + q = UserQuery(self.p, level="s2:c1,c2,c4", level_dom=True) + + users = sorted(str(u) for u in q.results()) + self.assertListEqual(["test21"], users) + + def test_021_level_dom2(self): + """User query with default level dominance (equal).""" + q = UserQuery(self.p, level="s2:c1,c4", level_dom=True) + + users = sorted(str(u) for u in q.results()) + self.assertListEqual(["test21"], users) + + def test_022_level_domby1(self): + """User query with default level dominated-by.""" + q = UserQuery(self.p, level="s3:c2", level_domby=True) + + users = sorted(str(u) for u in q.results()) + self.assertListEqual(["test22"], users) + + def test_022_level_domby2(self): + """User query with default level dominated-by (equal).""" + q = UserQuery(self.p, level="s3:c2,c4", level_domby=True) + + users = sorted(str(u) for u in q.results()) + self.assertListEqual(["test22"], users) + + def test_023_level_incomp(self): + """User query with default level icomparable.""" + q = UserQuery(self.p, level="s5:c0.c5,c7", level_incomp=True) + + users = sorted(str(u) for u in q.results()) + self.assertListEqual(["test23"], users) + + def test_040_range_exact(self): + """User query with range exact match""" + q = UserQuery(self.p, range_="s0:c5 - s0:c0.c5") + + users = sorted(str(u) for u in q.results()) + self.assertListEqual(["test40"], users) + + def test_041_range_overlap1(self): + """User query with range overlap match (equal)""" + q = UserQuery(self.p, range_="s1:c5 - s1:c1.c3,c5", range_overlap=True) + + users = sorted(str(u) for u in q.results()) + self.assertListEqual(["test41"], users) + + def test_041_range_overlap2(self): + """User query with range overlap match (subset)""" + q = UserQuery(self.p, range_="s1:c2,c5 - s1:c2.c3,c5", range_overlap=True) + + users = sorted(str(u) for u in q.results()) + self.assertListEqual(["test41"], users) + + def test_041_range_overlap3(self): + """User query with range overlap match (superset)""" + q = UserQuery(self.p, range_="s1 - s1:c0.c5", range_overlap=True) + + users = sorted(str(u) for u in q.results()) + self.assertListEqual(["test41"], users) + + def test_041_range_overlap4(self): + """User query with range overlap match (overlap low level)""" + q = UserQuery(self.p, range_="s1:c5 - s1:c2,c5", range_overlap=True) + + users = sorted(str(u) for u in q.results()) + self.assertListEqual(["test41"], users) + + def test_041_range_overlap5(self): + """User query with range overlap match (overlap high level)""" + q = UserQuery(self.p, range_="s1:c5,c2 - s1:c1.c3,c5", range_overlap=True) + + users = sorted(str(u) for u in q.results()) + self.assertListEqual(["test41"], users) + + def test_042_range_subset1(self): + """User query with range subset match""" + q = UserQuery(self.p, range_="s2:c2,c5 - s2:c2.c3,c5", range_overlap=True) + + users = sorted(str(u) for u in q.results()) + self.assertListEqual(["test42"], users) + + def test_042_range_subset2(self): + """User query with range subset match (equal)""" + q = UserQuery(self.p, range_="s2:c5 - s2:c1.c3,c5", range_overlap=True) + + users = sorted(str(u) for u in q.results()) + self.assertListEqual(["test42"], users) + + def test_043_range_superset1(self): + """User query with range superset match""" + q = UserQuery(self.p, range_="s3 - s3:c0.c6", range_superset=True) + + users = sorted(str(u) for u in q.results()) + self.assertListEqual(["test43"], users) + + def test_043_range_superset2(self): + """User query with range superset match (equal)""" + q = UserQuery(self.p, range_="s3:c5 - s3:c1.c3,c5.c6", range_superset=True) + + users = sorted(str(u) for u in q.results()) + self.assertListEqual(["test43"], users) + + def test_044_range_proper_subset1(self): + """User query with range proper subset match""" + q = UserQuery(self.p, range_="s4:c2,c5", range_subset=True, range_proper=True) + + users = sorted(str(u) for u in q.results()) + self.assertListEqual(["test44"], users) + + def test_044_range_proper_subset2(self): + """User query with range proper subset match (equal)""" + q = UserQuery(self.p, range_="s4:c5 - s4:c1.c3,c5", range_subset=True, range_proper=True) + + users = sorted(str(u) for u in q.results()) + self.assertListEqual([], users) + + def test_044_range_proper_subset3(self): + """User query with range proper subset match (equal low)""" + q = UserQuery(self.p, range_="s4:c5 - s4:c1.c2,c5", range_subset=True, range_proper=True) + + users = sorted(str(u) for u in q.results()) + self.assertListEqual(["test44"], users) + + def test_044_range_proper_subset4(self): + """User query with range proper subset match (equal high)""" + q = UserQuery(self.p, range_="s4:c1,c5 - s4:c1.c3,c5", range_subset=True, range_proper=True) + + users = sorted(str(u) for u in q.results()) + self.assertListEqual(["test44"], users) + + def test_045_range_proper_superset1(self): + """User query with range proper superset match""" + q = UserQuery(self.p, range_="s5 - s5:c0.c5", range_superset=True, range_proper=True) + + users = sorted(str(u) for u in q.results()) + self.assertListEqual(["test45"], users) + + def test_045_range_proper_superset2(self): + """User query with range proper superset match (equal)""" + q = UserQuery(self.p, range_="s5:c5 - s5:c1.c3,c5", range_superset=True, range_proper=True) + + users = sorted(str(u) for u in q.results()) + self.assertListEqual([], users) + + def test_045_range_proper_superset3(self): + """User query with range proper superset match (equal low)""" + q = UserQuery(self.p, range_="s5:c5 - s5:c1.c5", range_superset=True, range_proper=True) + + users = sorted(str(u) for u in q.results()) + self.assertListEqual(["test45"], users) + + def test_045_range_proper_superset4(self): + """User query with range proper superset match (equal high)""" + q = UserQuery(self.p, range_="s5 - s5:c1.c3,c5", range_superset=True, range_proper=True) + + users = sorted(str(u) for u in q.results()) + self.assertListEqual(["test45"], users)