mirror of
https://github.com/SELinuxProject/setools
synced 2025-03-25 04:26:28 +00:00
Refactor SETools queries/analyses to use descriptors instead of get/setters
This is Pythonic.
This commit is contained in:
parent
ab3772843e
commit
e6f59d04e5
47
seinfo
47
seinfo
@ -101,88 +101,84 @@ try:
|
||||
if args.boolquery or args.all:
|
||||
q = setools.BoolQuery(p)
|
||||
if isinstance(args.boolquery, str):
|
||||
q.set_name(args.boolquery)
|
||||
q.name = args.boolquery
|
||||
|
||||
components.append(("Booleans", q, lambda x: x.statement()))
|
||||
|
||||
if args.mlscatsquery or args.all:
|
||||
q = setools.CategoryQuery(p)
|
||||
if isinstance(args.mlscatsquery, str):
|
||||
q.set_name(args.mlscatsquery)
|
||||
q.name = args.mlscatsquery
|
||||
|
||||
components.append(("Categories", q, lambda x: x.statement()))
|
||||
|
||||
if args.classquery or args.all:
|
||||
q = setools.ObjClassQuery(p)
|
||||
if isinstance(args.classquery, str):
|
||||
q.set_name(args.classquery)
|
||||
q.name = args.classquery
|
||||
|
||||
components.append(("Classes", q, lambda x: x.statement()))
|
||||
|
||||
if args.commonquery or args.all:
|
||||
q = setools.CommonQuery(p)
|
||||
if isinstance(args.commonquery, str):
|
||||
q.set_name(args.commonquery)
|
||||
q.name = args.commonquery
|
||||
|
||||
components.append(("Commons", q, lambda x: x.statement()))
|
||||
|
||||
if args.constraintquery or args.all:
|
||||
q = setools.ConstraintQuery(p, ruletype=["constrain", "mlsconstrain"])
|
||||
if isinstance(args.constraintquery, str):
|
||||
# pylint: disable=no-member
|
||||
q.set_tclass(args.constraintquery)
|
||||
q.tclass = [args.constraintquery]
|
||||
|
||||
components.append(("Constraints", q, lambda x: x.statement()))
|
||||
|
||||
if args.fsusequery or args.all:
|
||||
q = setools.FSUseQuery(p)
|
||||
if isinstance(args.fsusequery, str):
|
||||
# pylint: disable=no-member
|
||||
q.set_fs(args.fsusequery)
|
||||
q.fs = args.fsusequery
|
||||
|
||||
components.append(("Fs_use", q, lambda x: x.statement()))
|
||||
|
||||
if args.genfsconquery or args.all:
|
||||
q = setools.GenfsconQuery(p)
|
||||
if isinstance(args.genfsconquery, str):
|
||||
# pylint: disable=no-member
|
||||
q.set_fs(args.genfsconquery)
|
||||
q.fs = args.genfsconquery
|
||||
|
||||
components.append(("Genfscon", q, lambda x: x.statement()))
|
||||
|
||||
if args.initialsidquery or args.all:
|
||||
q = setools.InitialSIDQuery(p)
|
||||
if isinstance(args.initialsidquery, str):
|
||||
q.set_name(args.initialsidquery)
|
||||
q.name = args.initialsidquery
|
||||
|
||||
components.append(("Initial SIDs", q, lambda x: x.statement()))
|
||||
|
||||
if args.netifconquery or args.all:
|
||||
q = setools.NetifconQuery(p)
|
||||
if isinstance(args.netifconquery, str):
|
||||
q.set_name(args.netifconquery)
|
||||
q.name = args.netifconquery
|
||||
|
||||
components.append(("Netifcon", q, lambda x: x.statement()))
|
||||
|
||||
if args.nodeconquery or args.all:
|
||||
q = setools.NodeconQuery(p)
|
||||
if isinstance(args.nodeconquery, str):
|
||||
# pylint: disable=no-member
|
||||
q.set_network(args.nodeconquery)
|
||||
q.network = args.nodeconquery
|
||||
|
||||
components.append(("Nodecon", q, lambda x: x.statement()))
|
||||
|
||||
if args.permissivequery or args.all:
|
||||
q = setools.TypeQuery(p, permissive=True, match_permissive=True)
|
||||
if isinstance(args.permissivequery, str):
|
||||
q.set_name(args.permissivequery)
|
||||
q.name = args.permissivequery
|
||||
|
||||
components.append(("Permissive Types", q, lambda x: x.statement()))
|
||||
|
||||
if args.polcapquery or args.all:
|
||||
q = setools.PolCapQuery(p)
|
||||
if isinstance(args.polcapquery, str):
|
||||
q.set_name(args.polcapquery)
|
||||
q.name = args.polcapquery
|
||||
|
||||
components.append(("Polcap", q, lambda x: x.statement()))
|
||||
|
||||
@ -195,11 +191,9 @@ try:
|
||||
parser.error("Enter a port number or range, e.g. 22 or 6000-6020")
|
||||
|
||||
if len(ports) == 2:
|
||||
# pylint: disable=no-member
|
||||
q.set_ports((ports[0], ports[1]))
|
||||
q.ports = ports
|
||||
elif len(ports) == 1:
|
||||
# pylint: disable=no-member
|
||||
q.set_ports((ports[0], ports[0]))
|
||||
q.ports = (ports[0], ports[0])
|
||||
else:
|
||||
parser.error("Enter a port number or range, e.g. 22 or 6000-6020")
|
||||
|
||||
@ -208,43 +202,42 @@ try:
|
||||
if args.rolequery or args.all:
|
||||
q = setools.RoleQuery(p)
|
||||
if isinstance(args.rolequery, str):
|
||||
q.set_name(args.rolequery)
|
||||
q.name = args.rolequery
|
||||
|
||||
components.append(("Roles", q, lambda x: x.statement()))
|
||||
|
||||
if args.mlssensquery or args.all:
|
||||
q = setools.SensitivityQuery(p)
|
||||
if isinstance(args.mlssensquery, str):
|
||||
q.set_name(args.mlssensquery)
|
||||
q.name = args.mlssensquery
|
||||
|
||||
components.append(("Sensitivities", q, lambda x: x.statement()))
|
||||
|
||||
if args.typequery or args.all:
|
||||
q = setools.TypeQuery(p)
|
||||
if isinstance(args.typequery, str):
|
||||
q.set_name(args.typequery)
|
||||
q.name = args.typequery
|
||||
|
||||
components.append(("Types", q, lambda x: x.statement()))
|
||||
|
||||
if args.typeattrquery or args.all:
|
||||
q = setools.TypeAttributeQuery(p)
|
||||
if isinstance(args.typeattrquery, str):
|
||||
q.set_name(args.typeattrquery)
|
||||
q.name = args.typeattrquery
|
||||
|
||||
components.append(("Type Attributes", q, expand_attr))
|
||||
|
||||
if args.userquery or args.all:
|
||||
q = setools.UserQuery(p)
|
||||
if isinstance(args.userquery, str):
|
||||
q.set_name(args.userquery)
|
||||
q.name = args.userquery
|
||||
|
||||
components.append(("Users", q, lambda x: x.statement()))
|
||||
|
||||
if args.validatetransquery or args.all:
|
||||
q = setools.ConstraintQuery(p, ruletype=["validatetrans", "mlsvalidatetrans"])
|
||||
if isinstance(args.validatetransquery, str):
|
||||
# pylint: disable=no-member
|
||||
q.set_tclass(args.validatetransquery)
|
||||
q.tclass = [args.validatetransquery]
|
||||
|
||||
components.append(("Validatetrans", q, lambda x: x.statement()))
|
||||
|
||||
|
@ -79,7 +79,7 @@ else:
|
||||
try:
|
||||
p = setools.SELinuxPolicy(args.policy)
|
||||
m = setools.PermissionMap(args.map)
|
||||
g = setools.InfoFlowAnalysis(p, m, minweight=args.min_weight, exclude=args.exclude)
|
||||
g = setools.InfoFlowAnalysis(p, m, min_weight=args.min_weight, exclude=args.exclude)
|
||||
|
||||
if args.shortest_path or args.all_paths:
|
||||
if args.shortest_path:
|
||||
|
18
sesearch
18
sesearch
@ -135,18 +135,18 @@ try:
|
||||
# with an empty string in it (split on empty string)
|
||||
if args.tclass:
|
||||
if args.tclass_regex:
|
||||
q.set_tclass(args.tclass)
|
||||
q.tclass = args.tclass
|
||||
else:
|
||||
q.set_tclass(args.tclass.split(","))
|
||||
q.tclass = args.tclass.split(",")
|
||||
|
||||
if args.perms:
|
||||
q.set_perms(args.perms.split(","))
|
||||
q.perms = args.perms.split(",")
|
||||
|
||||
if args.boolean:
|
||||
if args.boolean_regex:
|
||||
q.set_boolean(args.boolean)
|
||||
q.boolean = args.boolean
|
||||
else:
|
||||
q.set_boolean(args.boolean.split(","))
|
||||
q.boolean = args.boolean.split(",")
|
||||
|
||||
for r in sorted(q.results()):
|
||||
print(r)
|
||||
@ -168,9 +168,9 @@ try:
|
||||
# with an empty string in it (split on empty string)
|
||||
if args.tclass:
|
||||
if args.tclass_regex:
|
||||
q.set_tclass(args.tclass)
|
||||
q.tclass = args.tclass
|
||||
else:
|
||||
q.set_tclass(args.tclass.split(","))
|
||||
q.tclass = args.tclass.split(",")
|
||||
|
||||
for r in sorted(q.results()):
|
||||
print(r)
|
||||
@ -189,9 +189,9 @@ try:
|
||||
# with an empty string in it (split on empty string)
|
||||
if args.tclass:
|
||||
if args.tclass_regex:
|
||||
q.set_tclass(args.tclass)
|
||||
q.tclass = args.tclass
|
||||
else:
|
||||
q.set_tclass(args.tclass.split(","))
|
||||
q.tclass = args.tclass.split(",")
|
||||
|
||||
for r in sorted(q.results()):
|
||||
print(r)
|
||||
|
@ -19,61 +19,48 @@
|
||||
import logging
|
||||
|
||||
from . import compquery
|
||||
from .descriptors import CriteriaDescriptor
|
||||
|
||||
|
||||
class BoolQuery(compquery.ComponentQuery):
|
||||
|
||||
"""Query SELinux policy Booleans."""
|
||||
"""Query SELinux policy Booleans.
|
||||
|
||||
def __init__(self, policy,
|
||||
name=None, name_regex=False,
|
||||
default=False, match_default=False):
|
||||
"""
|
||||
Parameter:
|
||||
policy The policy to query.
|
||||
name The Boolean name to match.
|
||||
name_regex If true, regular expression matching
|
||||
will be used on the Boolean name.
|
||||
default The default state to match.
|
||||
match_default If true, the default state will be matched.
|
||||
"""
|
||||
self.log = logging.getLogger(self.__class__.__name__)
|
||||
Parameter:
|
||||
policy The policy to query.
|
||||
|
||||
self.policy = policy
|
||||
self.set_name(name, regex=name_regex)
|
||||
self.set_default(match_default, default=default)
|
||||
Keyword Parameters/Class attributes:
|
||||
name The Boolean name to match.
|
||||
name_regex If true, regular expression matching
|
||||
will be used on the Boolean name.
|
||||
default The default state to match. If this
|
||||
is None, the default state not be matched.
|
||||
"""
|
||||
|
||||
_default = None
|
||||
|
||||
@property
|
||||
def default(self):
|
||||
return self._default
|
||||
|
||||
@default.setter
|
||||
def default(self, value):
|
||||
if value is None:
|
||||
self._default = None
|
||||
else:
|
||||
self._default = bool(value)
|
||||
|
||||
def results(self):
|
||||
"""Generator which yields all Booleans matching the criteria."""
|
||||
self.log.info("Generating results from {0.policy}".format(self))
|
||||
self.log.debug("Name: {0.name_cmp!r}, regex: {0.name_regex}".format(self))
|
||||
self.log.debug("Default: {0.match_default}, state: {0.default}".format(self))
|
||||
self.log.debug("Name: {0.name!r}, regex: {0.name_regex}".format(self))
|
||||
self.log.debug("Default: {0.default}".format(self))
|
||||
|
||||
for boolean in self.policy.bools():
|
||||
if self.name and not self._match_name(boolean):
|
||||
if not self._match_name(boolean):
|
||||
continue
|
||||
|
||||
if self.match_default and boolean.state != self.default:
|
||||
if self.default is not None and boolean.state != self.default:
|
||||
continue
|
||||
|
||||
yield boolean
|
||||
|
||||
def set_default(self, match, **opts):
|
||||
"""
|
||||
Set if the default Boolean state should be matched.
|
||||
|
||||
Parameter:
|
||||
match If true, the default state will be matched.
|
||||
default The default state to match.
|
||||
|
||||
Exceptions:
|
||||
NameError Invalid keyword option.
|
||||
"""
|
||||
|
||||
self.match_default = bool(match)
|
||||
|
||||
for k in list(opts.keys()):
|
||||
if k == "default":
|
||||
self.default = bool(opts[k])
|
||||
else:
|
||||
raise NameError("Invalid default option: {0}".format(k))
|
||||
|
@ -24,37 +24,32 @@ from . import mixins
|
||||
|
||||
class CategoryQuery(mixins.MatchAlias, compquery.ComponentQuery):
|
||||
|
||||
"""Query MLS Categories"""
|
||||
"""
|
||||
Query MLS Categories
|
||||
|
||||
def __init__(self, policy,
|
||||
name=None, name_regex=False,
|
||||
alias=None, 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.log = logging.getLogger(self.__class__.__name__)
|
||||
Parameter:
|
||||
policy The policy to query.
|
||||
|
||||
self.policy = policy
|
||||
self.set_name(name, regex=name_regex)
|
||||
self.set_alias(alias, regex=alias_regex)
|
||||
Keyword Parameters/Class attributes:
|
||||
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.
|
||||
"""
|
||||
|
||||
def results(self):
|
||||
"""Generator which yields all matching categories."""
|
||||
self.log.info("Generating results from {0.policy}".format(self))
|
||||
self.log.debug("Name: {0.name_cmp!r}, regex: {0.name_regex}".format(self))
|
||||
self.log.debug("Alias: {0.alias_cmp}, regex: {0.alias_regex}".format(self))
|
||||
self.log.debug("Name: {0.name!r}, regex: {0.name_regex}".format(self))
|
||||
self.log.debug("Alias: {0.alias}, regex: {0.alias_regex}".format(self))
|
||||
|
||||
for cat in self.policy.categories():
|
||||
if self.name and not self._match_name(cat):
|
||||
if not self._match_name(cat):
|
||||
continue
|
||||
|
||||
if self.alias and not self._match_alias(cat.aliases()):
|
||||
if not self._match_alias(cat):
|
||||
continue
|
||||
|
||||
yield cat
|
||||
|
@ -19,84 +19,42 @@
|
||||
import logging
|
||||
import re
|
||||
|
||||
from . import compquery
|
||||
from . import compquery, mixins
|
||||
|
||||
|
||||
class CommonQuery(compquery.ComponentQuery):
|
||||
class CommonQuery(mixins.MatchPermission, compquery.ComponentQuery):
|
||||
|
||||
"""Query common permission sets."""
|
||||
"""
|
||||
Query common permission sets.
|
||||
|
||||
def __init__(self, policy,
|
||||
name=None, name_regex=False,
|
||||
perms=None, 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.log = logging.getLogger(self.__class__.__name__)
|
||||
Parameter:
|
||||
policy The policy to query.
|
||||
|
||||
self.policy = policy
|
||||
self.set_name(name, regex=name_regex)
|
||||
self.set_perms(perms, regex=perms_regex, equal=perms_equal)
|
||||
Keyword Parameters/Class attributes:
|
||||
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.
|
||||
"""
|
||||
|
||||
def results(self):
|
||||
"""Generator which yields all matching commons."""
|
||||
self.log.info("Generating results from {0.policy}".format(self))
|
||||
self.log.debug("Name: {0.name_cmp!r}, regex: {0.name_regex}".format(self))
|
||||
self.log.debug("Perms: {0.perms_cmp!r}, regex: {0.perms_regex}, eq: {0.perms_equal}".
|
||||
self.log.debug("Name: {0.name!r}, regex: {0.name_regex}".format(self))
|
||||
self.log.debug("Perms: {0.perms!r}, regex: {0.perms_regex}, eq: {0.perms_equal}".
|
||||
format(self))
|
||||
|
||||
for com in self.policy.commons():
|
||||
if self.name and not self._match_name(com):
|
||||
if not self._match_name(com):
|
||||
continue
|
||||
|
||||
if self.perms and not self._match_regex_or_set(
|
||||
com.perms,
|
||||
self.perms_cmp,
|
||||
self.perms_equal,
|
||||
self.perms_regex):
|
||||
if not self._match_perms(com):
|
||||
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 list(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 = self.perms
|
||||
|
@ -20,37 +20,20 @@
|
||||
import re
|
||||
|
||||
from . import query
|
||||
from .descriptors import CriteriaDescriptor
|
||||
|
||||
|
||||
class ComponentQuery(query.PolicyQuery):
|
||||
|
||||
"""Abstract base class for SETools component queries."""
|
||||
"""Base class for SETools component queries."""
|
||||
|
||||
name = CriteriaDescriptor("name_regex")
|
||||
name_regex = False
|
||||
|
||||
def _match_name(self, obj):
|
||||
"""Match the object to the name criteria."""
|
||||
return self._match_regex(obj, self.name_cmp, self.name_regex)
|
||||
if not self.name:
|
||||
# if there is no criteria, everything matches.
|
||||
return True
|
||||
|
||||
def set_name(self, name, **opts):
|
||||
"""
|
||||
Set the criteria for matching the component's name.
|
||||
|
||||
Parameter:
|
||||
name Name to match the component's name.
|
||||
regex If true, regular expression matching will be used.
|
||||
|
||||
Exceptions:
|
||||
NameError Invalid keyword option.
|
||||
"""
|
||||
|
||||
self.name = name
|
||||
|
||||
for k in list(opts.keys()):
|
||||
if k == "regex":
|
||||
self.name_regex = opts[k]
|
||||
else:
|
||||
raise NameError("Invalid name option: {0}".format(k))
|
||||
|
||||
if self.name_regex:
|
||||
self.name_cmp = re.compile(self.name)
|
||||
else:
|
||||
self.name_cmp = self.name
|
||||
return self._match_regex(obj, self.name, self.name_regex)
|
||||
|
@ -19,62 +19,58 @@
|
||||
import logging
|
||||
import re
|
||||
|
||||
from . import mixins
|
||||
from .query import PolicyQuery
|
||||
from . import mixins, query
|
||||
from .descriptors import CriteriaDescriptor, CriteriaSetDescriptor, RuletypeDescriptor
|
||||
from .policyrep.exception import ConstraintUseError
|
||||
|
||||
|
||||
class ConstraintQuery(mixins.MatchObjClass, mixins.MatchPermission, PolicyQuery):
|
||||
class ConstraintQuery(mixins.MatchObjClass, mixins.MatchPermission, query.PolicyQuery):
|
||||
|
||||
"""Query constraint rules, (mls)constrain/(mls)validatetrans."""
|
||||
"""
|
||||
Query constraint rules, (mls)constrain/(mls)validatetrans.
|
||||
|
||||
def __init__(self, policy,
|
||||
ruletype=None,
|
||||
tclass=None, tclass_regex=False,
|
||||
perms=None, perms_equal=False,
|
||||
role=None, role_regex=False, role_indirect=True,
|
||||
type_=None, type_regex=False, type_indirect=True,
|
||||
user=None, user_regex=False):
|
||||
Parameter:
|
||||
policy The policy to query.
|
||||
|
||||
"""
|
||||
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.
|
||||
role The name of the role to match in the
|
||||
constraint expression.
|
||||
role_indirect If true, members of an attribute will be
|
||||
matched rather than the attribute itself.
|
||||
role_regex If true, regular expression matching will
|
||||
be used on the role.
|
||||
type_ The name of the type/attribute to match in the
|
||||
constraint expression.
|
||||
type_indirect If true, members of an attribute will be
|
||||
matched rather than the attribute itself.
|
||||
type_regex If true, regular expression matching will
|
||||
be used on the type/attribute.
|
||||
user The name of the user to match in the
|
||||
constraint expression.
|
||||
user_regex If true, regular expression matching will
|
||||
be used on the user.
|
||||
"""
|
||||
self.log = logging.getLogger(self.__class__.__name__)
|
||||
Keyword Parameters/Class attributes:
|
||||
ruletype The list of 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.
|
||||
perms_regex If true, regular expression matching will be used
|
||||
on the permission names instead of set logic.
|
||||
role The name of the role to match in the
|
||||
constraint expression.
|
||||
role_indirect If true, members of an attribute will be
|
||||
matched rather than the attribute itself.
|
||||
role_regex If true, regular expression matching will
|
||||
be used on the role.
|
||||
type_ The name of the type/attribute to match in the
|
||||
constraint expression.
|
||||
type_indirect If true, members of an attribute will be
|
||||
matched rather than the attribute itself.
|
||||
type_regex If true, regular expression matching will
|
||||
be used on the type/attribute.
|
||||
user The name of the user to match in the
|
||||
constraint expression.
|
||||
user_regex If true, regular expression matching will
|
||||
be used on the user.
|
||||
"""
|
||||
|
||||
self.policy = policy
|
||||
|
||||
self.set_ruletype(ruletype)
|
||||
self.set_tclass(tclass, regex=tclass_regex)
|
||||
self.set_perms(perms, equal=perms_equal)
|
||||
self.set_role(role, regex=role_regex, indirect=role_indirect)
|
||||
self.set_type(type_, regex=type_regex, indirect=type_indirect)
|
||||
self.set_user(user, regex=user_regex)
|
||||
ruletype = RuletypeDescriptor("validate_constraint_ruletype")
|
||||
user = CriteriaDescriptor("user_regex", "lookup_user")
|
||||
user_regex = False
|
||||
role = CriteriaDescriptor("role_regex", "lookup_role")
|
||||
role_regex = False
|
||||
role_indirect = True
|
||||
type_ = CriteriaDescriptor("type_regex", "lookup_type_or_attr")
|
||||
type_regex = False
|
||||
type_indirect = True
|
||||
|
||||
def _match_expr(self, expr, criteria, indirect, regex):
|
||||
"""
|
||||
@ -101,143 +97,46 @@ class ConstraintQuery(mixins.MatchObjClass, mixins.MatchPermission, PolicyQuery)
|
||||
"""Generator which yields all matching constraints rules."""
|
||||
self.log.info("Generating results from {0.policy}".format(self))
|
||||
self.log.debug("Ruletypes: {0.ruletype}".format(self))
|
||||
self.log.debug("Class: {0.tclass_cmp!r}, regex: {0.tclass_regex}".format(self))
|
||||
self.log.debug("Perms: {0.perms_cmp}, eq: {0.perms_equal}".format(self))
|
||||
self.log.debug("User: {0.user_cmp!r}, regex: {0.user_regex}".format(self))
|
||||
self.log.debug("Role: {0.role_cmp!r}, regex: {0.role_regex}".format(self))
|
||||
self.log.debug("Type: {0.type_cmp!r}, regex: {0.type_regex}".format(self))
|
||||
self.log.debug("Class: {0.tclass!r}, regex: {0.tclass_regex}".format(self))
|
||||
self.log.debug("Perms: {0.perms!r}, regex: {0.perms_regex}, eq: {0.perms_equal}".
|
||||
format(self))
|
||||
self.log.debug("User: {0.user!r}, regex: {0.user_regex}".format(self))
|
||||
self.log.debug("Role: {0.role!r}, regex: {0.role_regex}".format(self))
|
||||
self.log.debug("Type: {0.type_!r}, regex: {0.type_regex}".format(self))
|
||||
|
||||
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):
|
||||
if not self._match_object_class(c):
|
||||
continue
|
||||
|
||||
if self.perms:
|
||||
try:
|
||||
if not self._match_perms(c.perms):
|
||||
continue
|
||||
except ConstraintUseError:
|
||||
continue
|
||||
try:
|
||||
if not self._match_perms(c):
|
||||
continue
|
||||
except ConstraintUseError:
|
||||
continue
|
||||
|
||||
if self.role and not self._match_expr(
|
||||
c.roles,
|
||||
self.role_cmp,
|
||||
self.role,
|
||||
self.role_indirect,
|
||||
self.role_regex):
|
||||
continue
|
||||
|
||||
if self.type_ and not self._match_expr(
|
||||
c.types,
|
||||
self.type_cmp,
|
||||
self.type_,
|
||||
self.type_indirect,
|
||||
self.type_regex):
|
||||
continue
|
||||
|
||||
if self.user and not self._match_expr(
|
||||
c.users,
|
||||
self.user_cmp,
|
||||
self.user,
|
||||
False,
|
||||
self.user_regex):
|
||||
continue
|
||||
|
||||
yield c
|
||||
|
||||
def set_ruletype(self, ruletype):
|
||||
"""
|
||||
Set the rule types for the rule query.
|
||||
|
||||
Parameter:
|
||||
ruletype The rule types to match.
|
||||
"""
|
||||
if ruletype:
|
||||
self.policy.validate_constraint_ruletype(ruletype)
|
||||
|
||||
self.ruletype = ruletype
|
||||
|
||||
def set_role(self, role, **opts):
|
||||
"""
|
||||
Set the criteria for matching the constraint's role.
|
||||
|
||||
Parameter:
|
||||
role Name to match the constraint's role.
|
||||
regex If true, regular expression matching will be used.
|
||||
|
||||
Exceptions:
|
||||
NameError Invalid keyword option.
|
||||
"""
|
||||
|
||||
self.role = role
|
||||
|
||||
for k in list(opts.keys()):
|
||||
if k == "regex":
|
||||
self.role_regex = opts[k]
|
||||
elif k == "indirect":
|
||||
self.role_indirect = opts[k]
|
||||
else:
|
||||
raise NameError("Invalid name option: {0}".format(k))
|
||||
|
||||
if not self.role:
|
||||
self.role_cmp = None
|
||||
elif self.role_regex:
|
||||
self.role_cmp = re.compile(self.role)
|
||||
else:
|
||||
self.role_cmp = self.policy.lookup_role(self.role)
|
||||
|
||||
def set_type(self, type_, **opts):
|
||||
"""
|
||||
Set the criteria for matching the constraint's type.
|
||||
|
||||
Parameter:
|
||||
type_ Name to match the constraint's type.
|
||||
regex If true, regular expression matching will be used.
|
||||
|
||||
Exceptions:
|
||||
NameError Invalid keyword option.
|
||||
"""
|
||||
|
||||
self.type_ = type_
|
||||
|
||||
for k in list(opts.keys()):
|
||||
if k == "regex":
|
||||
self.type_regex = opts[k]
|
||||
elif k == "indirect":
|
||||
self.type_indirect = opts[k]
|
||||
else:
|
||||
raise NameError("Invalid name option: {0}".format(k))
|
||||
|
||||
if not self.type_:
|
||||
self.type_cmp = None
|
||||
elif self.type_regex:
|
||||
self.type_cmp = re.compile(self.type_)
|
||||
else:
|
||||
self.type_cmp = self.policy.lookup_type(type_)
|
||||
|
||||
def set_user(self, user, **opts):
|
||||
"""
|
||||
Set the criteria for matching the constraint's user.
|
||||
|
||||
Parameter:
|
||||
user Name to match the constraint's user.
|
||||
regex If true, regular expression matching will be used.
|
||||
|
||||
Exceptions:
|
||||
NameError Invalid keyword option.
|
||||
"""
|
||||
|
||||
self.user = user
|
||||
|
||||
for k in list(opts.keys()):
|
||||
if k == "regex":
|
||||
self.user_regex = opts[k]
|
||||
else:
|
||||
raise NameError("Invalid name option: {0}".format(k))
|
||||
|
||||
if not self.user:
|
||||
self.user_cmp = None
|
||||
elif self.user_regex:
|
||||
self.user_cmp = re.compile(self.user)
|
||||
else:
|
||||
self.user_cmp = self.policy.lookup_user(self.user)
|
||||
|
@ -20,190 +20,79 @@
|
||||
import re
|
||||
|
||||
from . import query
|
||||
from .descriptors import CriteriaDescriptor
|
||||
|
||||
|
||||
class ContextQuery(query.PolicyQuery):
|
||||
|
||||
"""Abstract base class for SETools in-policy labeling/context queries."""
|
||||
"""
|
||||
Base class for SETools in-policy labeling/context queries.
|
||||
|
||||
@staticmethod
|
||||
def _match_context(context,
|
||||
user, user_regex,
|
||||
role, role_regex,
|
||||
type_, type_regex,
|
||||
range_, range_subset, range_overlap, range_superset, range_proper):
|
||||
"""
|
||||
Match the context with optional regular expression.
|
||||
Parameter:
|
||||
policy The policy to query.
|
||||
|
||||
Parameters:
|
||||
context The object to match.
|
||||
user The user to match in the context.
|
||||
user_regex If true, regular expression matching
|
||||
will be used on the user.
|
||||
role The role to match in the context.
|
||||
role_regex If true, regular expression matching
|
||||
will be used on the role.
|
||||
type_ The type to match in the context.
|
||||
type_regex If true, regular expression matching
|
||||
will be used on the type.
|
||||
range_ The range to match in the context.
|
||||
range_subset If true, the criteria will match if it
|
||||
is a subset of the context's range.
|
||||
range_overlap If true, the criteria will match if it
|
||||
overlaps any of the context's range.
|
||||
range_superset If true, the criteria will match if it
|
||||
is a superset of the context's range.
|
||||
range_proper If true, use proper superset/subset
|
||||
on range matching operations.
|
||||
No effect if not using set operations.
|
||||
"""
|
||||
Keyword Parameters/Class attributes:
|
||||
context The object to match.
|
||||
user The user to match in the context.
|
||||
user_regex If true, regular expression matching
|
||||
will be used on the user.
|
||||
role The role to match in the context.
|
||||
role_regex If true, regular expression matching
|
||||
will be used on the role.
|
||||
type_ The type to match in the context.
|
||||
type_regex If true, regular expression matching
|
||||
will be used on the type.
|
||||
range_ The range to match in the context.
|
||||
range_subset If true, the criteria will match if it
|
||||
is a subset of the context's range.
|
||||
range_overlap If true, the criteria will match if it
|
||||
overlaps any of the context's range.
|
||||
range_superset If true, the criteria will match if it
|
||||
is a superset of the context's range.
|
||||
range_proper If true, use proper superset/subset
|
||||
on range matching operations.
|
||||
No effect if not using set operations.
|
||||
"""
|
||||
|
||||
if user and not query.PolicyQuery._match_regex(
|
||||
user = CriteriaDescriptor("user_regex", "lookup_user")
|
||||
user_regex = False
|
||||
role = CriteriaDescriptor("role_regex", "lookup_role")
|
||||
role_regex = False
|
||||
type_ = CriteriaDescriptor("type_regex", "lookup_type")
|
||||
type_regex = False
|
||||
range_ = CriteriaDescriptor(lookup_function="lookup_range")
|
||||
range_overlap = False
|
||||
range_subset = False
|
||||
range_superset = False
|
||||
range_proper = False
|
||||
|
||||
def _match_context(self, context):
|
||||
|
||||
if self.user and not query.PolicyQuery._match_regex(
|
||||
context.user,
|
||||
user,
|
||||
user_regex):
|
||||
self.user,
|
||||
self.user_regex):
|
||||
return False
|
||||
|
||||
if role and not query.PolicyQuery._match_regex(
|
||||
if self.role and not query.PolicyQuery._match_regex(
|
||||
context.role,
|
||||
role,
|
||||
role_regex):
|
||||
self.role,
|
||||
self.role_regex):
|
||||
return False
|
||||
|
||||
if type_ and not query.PolicyQuery._match_regex(
|
||||
if self.type_ and not query.PolicyQuery._match_regex(
|
||||
context.type_,
|
||||
type_,
|
||||
type_regex):
|
||||
self.type_,
|
||||
self.type_regex):
|
||||
return False
|
||||
|
||||
if range_ and not query.PolicyQuery._match_range(
|
||||
if self.range_ and not query.PolicyQuery._match_range(
|
||||
context.range_,
|
||||
range_,
|
||||
range_subset,
|
||||
range_overlap,
|
||||
range_superset,
|
||||
range_proper):
|
||||
self.range_,
|
||||
self.range_subset,
|
||||
self.range_overlap,
|
||||
self.range_superset,
|
||||
self.range_proper):
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
def set_user(self, user, **opts):
|
||||
"""
|
||||
Set the criteria for matching the context's user.
|
||||
|
||||
Parameter:
|
||||
user Name to match the context's user.
|
||||
regex If true, regular expression matching will be used.
|
||||
|
||||
Exceptions:
|
||||
NameError Invalid keyword option.
|
||||
"""
|
||||
|
||||
self.user = user
|
||||
|
||||
for k in list(opts.keys()):
|
||||
if k == "regex":
|
||||
self.user_regex = opts[k]
|
||||
else:
|
||||
raise NameError("Invalid name option: {0}".format(k))
|
||||
|
||||
if not self.user:
|
||||
self.user_cmp = None
|
||||
elif self.user_regex:
|
||||
self.user_cmp = re.compile(self.user)
|
||||
else:
|
||||
self.user_cmp = self.policy.lookup_user(self.user)
|
||||
|
||||
def set_role(self, role, **opts):
|
||||
"""
|
||||
Set the criteria for matching the context's role.
|
||||
|
||||
Parameter:
|
||||
role Name to match the context's role.
|
||||
regex If true, regular expression matching will be used.
|
||||
|
||||
Exceptions:
|
||||
NameError Invalid keyword option.
|
||||
"""
|
||||
|
||||
self.role = role
|
||||
|
||||
for k in list(opts.keys()):
|
||||
if k == "regex":
|
||||
self.role_regex = opts[k]
|
||||
else:
|
||||
raise NameError("Invalid name option: {0}".format(k))
|
||||
|
||||
if not self.role:
|
||||
self.role_cmp = None
|
||||
elif self.role_regex:
|
||||
self.role_cmp = re.compile(self.role)
|
||||
else:
|
||||
self.role_cmp = self.policy.lookup_role(self.role)
|
||||
|
||||
def set_type(self, type_, **opts):
|
||||
"""
|
||||
Set the criteria for matching the context's type.
|
||||
|
||||
Parameter:
|
||||
type_ Name to match the context's type.
|
||||
regex If true, regular expression matching will be used.
|
||||
|
||||
Exceptions:
|
||||
NameError Invalid keyword option.
|
||||
"""
|
||||
|
||||
self.type_ = type_
|
||||
|
||||
for k in list(opts.keys()):
|
||||
if k == "regex":
|
||||
self.type_regex = opts[k]
|
||||
else:
|
||||
raise NameError("Invalid name option: {0}".format(k))
|
||||
|
||||
if not self.type_:
|
||||
self.type_cmp = None
|
||||
elif self.type_regex:
|
||||
self.type_cmp = re.compile(self.type_)
|
||||
else:
|
||||
self.type_cmp = self.policy.lookup_type(type_)
|
||||
|
||||
def set_range(self, range_, **opts):
|
||||
"""
|
||||
Set the criteria for matching the context's range.
|
||||
|
||||
Parameter:
|
||||
range_ Criteria to match the context's range.
|
||||
|
||||
Keyword Parameters:
|
||||
subset If true, the criteria will match if it is a subset
|
||||
of the context's range.
|
||||
overlap If true, the criteria will match if it overlaps
|
||||
any of the context's range.
|
||||
superset If true, the criteria will match if it is a superset
|
||||
of the context's range.
|
||||
proper If true, use proper superset/subset operations.
|
||||
No effect if not using set operations.
|
||||
|
||||
Exceptions:
|
||||
NameError Invalid keyword option.
|
||||
"""
|
||||
|
||||
self.range_ = range_
|
||||
|
||||
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))
|
||||
|
||||
if self.range_:
|
||||
self.range_cmp = self.policy.lookup_range(self.range_)
|
||||
else:
|
||||
self.range_cmp = None
|
||||
|
230
setools/descriptors.py
Normal file
230
setools/descriptors.py
Normal file
@ -0,0 +1,230 @@
|
||||
# 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/>.
|
||||
#
|
||||
"""
|
||||
SETools descriptors.
|
||||
|
||||
These classes override how a class's attributes are get/set/deleted.
|
||||
This is how the @property decorator works.
|
||||
|
||||
See https://docs.python.org/3/howto/descriptor.html
|
||||
for more details.
|
||||
"""
|
||||
|
||||
import re
|
||||
from collections import defaultdict
|
||||
from weakref import WeakKeyDictionary
|
||||
|
||||
#
|
||||
# Query criteria descriptors
|
||||
#
|
||||
# Implementation note: if the name_regex attribute value
|
||||
# is changed the criteria must be reset.
|
||||
#
|
||||
|
||||
|
||||
class CriteriaDescriptor(object):
|
||||
|
||||
"""
|
||||
Single item criteria descriptor.
|
||||
|
||||
Parameters:
|
||||
name_regex The name of instance's regex setting attribute;
|
||||
used as name_regex below. If unset,
|
||||
regular expressions will never be used.
|
||||
lookup_function The name of the SELinuxPolicy lookup function,
|
||||
e.g. lookup_type or lookup_boolean.
|
||||
default_value The default value of the criteria. The default
|
||||
is None.
|
||||
|
||||
Read-only instance attribute use (obj parameter):
|
||||
policy The instance of SELinuxPolicy
|
||||
name_regex This attribute is read to determine if
|
||||
the criteria should be looked up or
|
||||
compiled into a regex. If the attribute
|
||||
does not exist, False is assumed.
|
||||
"""
|
||||
|
||||
def __init__(self, name_regex=None, lookup_function=None, default_value=None):
|
||||
assert name_regex or lookup_function, "A simple attribute should be used if there is " \
|
||||
"no regex nor lookup function."
|
||||
self.regex = name_regex
|
||||
self.default_value = default_value
|
||||
self.lookup_function = lookup_function
|
||||
|
||||
# use weak references so instances can be
|
||||
# garbage collected, rather than unnecessarily
|
||||
# kept around due to this descriptor.
|
||||
self.instances = WeakKeyDictionary()
|
||||
|
||||
def __get__(self, obj, objtype=None):
|
||||
if obj is None:
|
||||
return self
|
||||
|
||||
return self.instances.setdefault(obj, self.default_value)
|
||||
|
||||
def __set__(self, obj, value):
|
||||
if not value:
|
||||
self.instances[obj] = None
|
||||
elif self.regex and getattr(obj, self.regex, False):
|
||||
self.instances[obj] = re.compile(value)
|
||||
elif self.lookup_function:
|
||||
lookup = getattr(obj.policy, self.lookup_function)
|
||||
self.instances[obj] = lookup(value)
|
||||
else:
|
||||
self.instances[obj] = value
|
||||
|
||||
|
||||
class CriteriaSetDescriptor(CriteriaDescriptor):
|
||||
|
||||
"""Descriptor for a set of criteria."""
|
||||
|
||||
def __set__(self, obj, value):
|
||||
if not value:
|
||||
self.instances[obj] = None
|
||||
elif self.regex and getattr(obj, self.regex, False):
|
||||
self.instances[obj] = re.compile(value)
|
||||
elif self.lookup_function:
|
||||
lookup = getattr(obj.policy, self.lookup_function)
|
||||
self.instances[obj] = set(lookup(v) for v in value)
|
||||
else:
|
||||
self.instances[obj] = set(value)
|
||||
|
||||
|
||||
class RuletypeDescriptor(object):
|
||||
|
||||
"""
|
||||
Descriptor for a list of rule types.
|
||||
|
||||
Parameters:
|
||||
validator The name of the SELinuxPolicy ruletype
|
||||
validator function, e.g. validate_te_ruletype
|
||||
default_value The default value of the criteria. The default
|
||||
is None.
|
||||
|
||||
Read-only instance attribute use (obj parameter):
|
||||
policy The instance of SELinuxPolicy
|
||||
"""
|
||||
|
||||
def __init__(self, validator):
|
||||
self.validator = validator
|
||||
|
||||
# use weak references so instances can be
|
||||
# garbage collected, rather than unnecessarily
|
||||
# kept around due to this descriptor.
|
||||
self.instances = WeakKeyDictionary()
|
||||
|
||||
def __get__(self, obj, objtype=None):
|
||||
if obj is None:
|
||||
return self
|
||||
|
||||
return self.instances.setdefault(obj, None)
|
||||
|
||||
def __set__(self, obj, value):
|
||||
if value:
|
||||
validate = getattr(obj.policy, self.validator)
|
||||
validate(value)
|
||||
self.instances[obj] = value
|
||||
else:
|
||||
self.instances[obj] = None
|
||||
|
||||
|
||||
#
|
||||
# NetworkX Graph Descriptors
|
||||
#
|
||||
# These descriptors are used to simplify all
|
||||
# of the dictionary use in the NetworkX graph.
|
||||
#
|
||||
|
||||
|
||||
class NetworkXGraphEdgeDescriptor(object):
|
||||
|
||||
"""
|
||||
Descriptor base class for NetworkX graph edge attributes.
|
||||
|
||||
Parameter:
|
||||
name The edge property name
|
||||
|
||||
Instance class attribute use (obj parameter):
|
||||
G The NetworkX graph
|
||||
source The edge's source node
|
||||
target The edge's target node
|
||||
"""
|
||||
|
||||
def __init__(self, propname):
|
||||
self.name = propname
|
||||
|
||||
def __get__(self, obj, objtype=None):
|
||||
if obj is None:
|
||||
return self
|
||||
|
||||
return obj.G[obj.source][obj.target][self.name]
|
||||
|
||||
def __set__(self, obj, value):
|
||||
raise NotImplementedError
|
||||
|
||||
def __delete__(self, obj):
|
||||
raise NotImplementedError
|
||||
|
||||
|
||||
class EdgeAttrDict(NetworkXGraphEdgeDescriptor):
|
||||
|
||||
"""A descriptor for edge attributes that are dictionaries."""
|
||||
|
||||
def __set__(self, obj, value):
|
||||
# None is a special value to initialize the attribute
|
||||
if value is None:
|
||||
obj.G[obj.source][obj.target][self.name] = defaultdict(list)
|
||||
else:
|
||||
raise ValueError("{0} dictionaries should not be assigned directly".format(self.name))
|
||||
|
||||
def __delete__(self, obj):
|
||||
obj.G[obj.source][obj.target][self.name].clear()
|
||||
|
||||
|
||||
class EdgeAttrIntMax(NetworkXGraphEdgeDescriptor):
|
||||
|
||||
"""
|
||||
A descriptor for edge attributes that are non-negative integers that always
|
||||
keep the max assigned value until re-initialized.
|
||||
"""
|
||||
|
||||
def __set__(self, obj, value):
|
||||
# None is a special value to initialize
|
||||
if value is None:
|
||||
obj.G[obj.source][obj.target][self.name] = 0
|
||||
else:
|
||||
current_value = obj.G[obj.source][obj.target][self.name]
|
||||
obj.G[obj.source][obj.target][self.name] = max(current_value, value)
|
||||
|
||||
|
||||
class EdgeAttrList(NetworkXGraphEdgeDescriptor):
|
||||
|
||||
"""A descriptor for edge attributes that are lists."""
|
||||
|
||||
def __set__(self, obj, value):
|
||||
# None is a special value to initialize
|
||||
if value is None:
|
||||
obj.G[obj.source][obj.target][self.name] = []
|
||||
else:
|
||||
raise ValueError("{0} lists should not be assigned directly".format(self.name))
|
||||
|
||||
def __delete__(self, obj):
|
||||
# in Python3 a .clear() function was added for lists
|
||||
# keep this implementation for Python 2 compat
|
||||
del obj.G[obj.source][obj.target][self.name][:]
|
@ -23,7 +23,7 @@ from collections import defaultdict, namedtuple
|
||||
import networkx as nx
|
||||
from networkx.exception import NetworkXError, NetworkXNoPath
|
||||
|
||||
from .infoflow import EdgeAttrList
|
||||
from .descriptors import EdgeAttrDict, EdgeAttrList
|
||||
|
||||
__all__ = ['DomainTransitionAnalysis']
|
||||
|
||||
@ -55,37 +55,32 @@ class DomainTransitionAnalysis(object):
|
||||
self.log = logging.getLogger(self.__class__.__name__)
|
||||
|
||||
self.policy = policy
|
||||
self.set_exclude(exclude)
|
||||
self.set_reverse(reverse)
|
||||
self.exclude = exclude
|
||||
self.reverse = reverse
|
||||
self.rebuildgraph = True
|
||||
self.rebuildsubgraph = True
|
||||
self.G = nx.DiGraph()
|
||||
self.subG = None
|
||||
|
||||
def set_reverse(self, reverse):
|
||||
"""
|
||||
Set forward/reverse DTA direction.
|
||||
@property
|
||||
def reverse(self):
|
||||
return self._reverse
|
||||
|
||||
Parameter:
|
||||
reverse If true, a reverse DTA is performed, otherwise a
|
||||
forward DTA is performed.
|
||||
"""
|
||||
|
||||
self.reverse = bool(reverse)
|
||||
@reverse.setter
|
||||
def reverse(self, direction):
|
||||
self._reverse = bool(direction)
|
||||
self.rebuildsubgraph = True
|
||||
|
||||
def set_exclude(self, exclude):
|
||||
"""
|
||||
Set the domains to exclude from the domain transition analysis.
|
||||
@property
|
||||
def exclude(self):
|
||||
return self._exclude
|
||||
|
||||
Parameter:
|
||||
exclude A list of types.
|
||||
"""
|
||||
|
||||
if exclude:
|
||||
self.exclude = [self.policy.lookup_type(t) for t in exclude]
|
||||
@exclude.setter
|
||||
def exclude(self, types):
|
||||
if types:
|
||||
self._exclude = [self.policy.lookup_type(t) for t in types]
|
||||
else:
|
||||
self.exclude = []
|
||||
self._exclude = None
|
||||
|
||||
self.rebuildsubgraph = True
|
||||
|
||||
@ -558,32 +553,6 @@ class DomainTransitionAnalysis(object):
|
||||
self.log.info("Completed building subgraph.")
|
||||
|
||||
|
||||
class EdgeAttrDict(object):
|
||||
|
||||
"""
|
||||
A descriptor for edge attributes that are dictionaries.
|
||||
|
||||
Parameter:
|
||||
name The edge property name
|
||||
"""
|
||||
|
||||
def __init__(self, propname):
|
||||
self.name = propname
|
||||
|
||||
def __get__(self, obj, type=None):
|
||||
return obj.G[obj.source][obj.target][self.name]
|
||||
|
||||
def __set__(self, obj, value):
|
||||
# None is a special value to initialize the attribute
|
||||
if value is None:
|
||||
obj.G[obj.source][obj.target][self.name] = defaultdict(list)
|
||||
else:
|
||||
raise ValueError("{0} dictionaries should not be assigned directly".format(self.name))
|
||||
|
||||
def __delete__(self, obj):
|
||||
obj.G[obj.source][obj.target][self.name].clear()
|
||||
|
||||
|
||||
class Edge(object):
|
||||
|
||||
"""
|
||||
|
@ -20,67 +20,54 @@ import logging
|
||||
import re
|
||||
|
||||
from . import contextquery
|
||||
from .descriptors import CriteriaDescriptor, CriteriaSetDescriptor
|
||||
|
||||
|
||||
class FSUseQuery(contextquery.ContextQuery):
|
||||
|
||||
"""Query fs_use_* statements."""
|
||||
"""
|
||||
Query fs_use_* statements.
|
||||
|
||||
def __init__(self, policy,
|
||||
ruletype=None,
|
||||
fs=None, fs_regex=False,
|
||||
user=None, user_regex=False,
|
||||
role=None, role_regex=False,
|
||||
type_=None, type_regex=False,
|
||||
range_=None, range_overlap=False, range_subset=False,
|
||||
range_superset=False, range_proper=False):
|
||||
"""
|
||||
Parameters:
|
||||
policy The policy to query.
|
||||
Parameter:
|
||||
policy The policy to query.
|
||||
|
||||
ruletype The rule type(s) to match.
|
||||
fs The criteria to match the file system type.
|
||||
fs_regex If true, regular expression matching
|
||||
will be used on the file system type.
|
||||
user The criteria to match the context's user.
|
||||
user_regex If true, regular expression matching
|
||||
will be used on the user.
|
||||
role The criteria to match the context's role.
|
||||
role_regex If true, regular expression matching
|
||||
will be used on the role.
|
||||
type_ The criteria to match the context's type.
|
||||
type_regex If true, regular expression matching
|
||||
will be used on the type.
|
||||
range_ The criteria to match the context's range.
|
||||
range_subset If true, the criteria will match if it is a subset
|
||||
of the context's range.
|
||||
range_overlap If true, the criteria will match if it overlaps
|
||||
any of the context's range.
|
||||
range_superset If true, the criteria will match if it is a superset
|
||||
of the context's range.
|
||||
range_proper If true, use proper superset/subset operations.
|
||||
No effect if not using set operations.
|
||||
"""
|
||||
self.log = logging.getLogger(self.__class__.__name__)
|
||||
Keyword Parameters/Class attributes:
|
||||
ruletype The rule type(s) to match.
|
||||
fs The criteria to match the file system type.
|
||||
fs_regex If true, regular expression matching
|
||||
will be used on the file system type.
|
||||
user The criteria to match the context's user.
|
||||
user_regex If true, regular expression matching
|
||||
will be used on the user.
|
||||
role The criteria to match the context's role.
|
||||
role_regex If true, regular expression matching
|
||||
will be used on the role.
|
||||
type_ The criteria to match the context's type.
|
||||
type_regex If true, regular expression matching
|
||||
will be used on the type.
|
||||
range_ The criteria to match the context's range.
|
||||
range_subset If true, the criteria will match if it is a subset
|
||||
of the context's range.
|
||||
range_overlap If true, the criteria will match if it overlaps
|
||||
any of the context's range.
|
||||
range_superset If true, the criteria will match if it is a superset
|
||||
of the context's range.
|
||||
range_proper If true, use proper superset/subset operations.
|
||||
No effect if not using set operations.
|
||||
"""
|
||||
|
||||
self.policy = policy
|
||||
|
||||
self.set_ruletype(ruletype)
|
||||
self.set_fs(fs, regex=fs_regex)
|
||||
self.set_user(user, regex=user_regex)
|
||||
self.set_role(role, regex=role_regex)
|
||||
self.set_type(type_, regex=type_regex)
|
||||
self.set_range(range_, overlap=range_overlap, subset=range_subset,
|
||||
superset=range_superset, proper=range_proper)
|
||||
ruletype = None
|
||||
fs = CriteriaDescriptor("fs_regex")
|
||||
fs_regex = False
|
||||
|
||||
def results(self):
|
||||
"""Generator which yields all matching fs_use_* statements."""
|
||||
self.log.info("Generating results from {0.policy}".format(self))
|
||||
self.log.debug("Ruletypes: {0.ruletype}".format(self))
|
||||
self.log.debug("FS: {0.fs_cmp!r}, regex: {0.fs_regex}".format(self))
|
||||
self.log.debug("User: {0.user_cmp!r}, regex: {0.user_regex}".format(self))
|
||||
self.log.debug("Role: {0.role_cmp!r}, regex: {0.role_regex}".format(self))
|
||||
self.log.debug("Type: {0.type_cmp!r}, regex: {0.type_regex}".format(self))
|
||||
self.log.debug("FS: {0.fs!r}, regex: {0.fs_regex}".format(self))
|
||||
self.log.debug("User: {0.user!r}, regex: {0.user_regex}".format(self))
|
||||
self.log.debug("Role: {0.role!r}, regex: {0.role_regex}".format(self))
|
||||
self.log.debug("Type: {0.type_!r}, regex: {0.type_regex}".format(self))
|
||||
self.log.debug("Range: {0.range_!r}, subset: {0.range_subset}, overlap: {0.range_overlap}, "
|
||||
"superset: {0.range_superset}, proper: {0.range_proper}".format(self))
|
||||
|
||||
@ -90,60 +77,11 @@ class FSUseQuery(contextquery.ContextQuery):
|
||||
|
||||
if self.fs and not self._match_regex(
|
||||
fsu.fs,
|
||||
self.fs_cmp,
|
||||
self.fs,
|
||||
self.fs_regex):
|
||||
continue
|
||||
|
||||
if not self._match_context(
|
||||
fsu.context,
|
||||
self.user_cmp,
|
||||
self.user_regex,
|
||||
self.role_cmp,
|
||||
self.role_regex,
|
||||
self.type_cmp,
|
||||
self.type_regex,
|
||||
self.range_cmp,
|
||||
self.range_subset,
|
||||
self.range_overlap,
|
||||
self.range_superset,
|
||||
self.range_proper):
|
||||
if not self._match_context(fsu.context):
|
||||
continue
|
||||
|
||||
yield fsu
|
||||
|
||||
def set_ruletype(self, ruletype):
|
||||
"""
|
||||
Set the rule types for the rule query.
|
||||
|
||||
Parameter:
|
||||
ruletype The rule types to match.
|
||||
"""
|
||||
|
||||
self.ruletype = ruletype
|
||||
|
||||
def set_fs(self, fs, **opts):
|
||||
"""
|
||||
Set the criteria for matching the file system type.
|
||||
|
||||
Parameter:
|
||||
fs Name to match the file system.
|
||||
regex If true, regular expression matching will be used.
|
||||
|
||||
Exceptions:
|
||||
NameError Invalid keyword option.
|
||||
"""
|
||||
|
||||
self.fs = fs
|
||||
|
||||
for k in list(opts.keys()):
|
||||
if k == "regex":
|
||||
self.fs_regex = opts[k]
|
||||
else:
|
||||
raise NameError("Invalid name option: {0}".format(k))
|
||||
|
||||
if not self.fs:
|
||||
self.fs_cmp = None
|
||||
elif self.fs_regex:
|
||||
self.fs_cmp = re.compile(self.fs)
|
||||
else:
|
||||
self.fs_cmp = self.fs
|
||||
|
@ -20,168 +20,79 @@ import logging
|
||||
import re
|
||||
|
||||
from . import contextquery
|
||||
from .descriptors import CriteriaDescriptor
|
||||
|
||||
|
||||
class GenfsconQuery(contextquery.ContextQuery):
|
||||
|
||||
"""Query genfscon statements."""
|
||||
"""
|
||||
Query genfscon statements.
|
||||
|
||||
def __init__(self, policy,
|
||||
fs=None, fs_regex=False,
|
||||
path=None, path_regex=False,
|
||||
filetype=None,
|
||||
user=None, user_regex=False,
|
||||
role=None, role_regex=False,
|
||||
type_=None, type_regex=False,
|
||||
range_=None, range_overlap=False, range_subset=False,
|
||||
range_superset=False, range_proper=False):
|
||||
"""
|
||||
Parameters:
|
||||
policy The policy to query.
|
||||
Parameter:
|
||||
policy The policy to query.
|
||||
|
||||
fs The criteria to match the file system type.
|
||||
fs_regex If true, regular expression matching
|
||||
will be used on the file system type.
|
||||
path The criteria to match the path.
|
||||
path_regex If true, regular expression matching
|
||||
will be used on the path.
|
||||
user The criteria to match the context's user.
|
||||
user_regex If true, regular expression matching
|
||||
will be used on the user.
|
||||
role The criteria to match the context's role.
|
||||
role_regex If true, regular expression matching
|
||||
will be used on the role.
|
||||
type_ The criteria to match the context's type.
|
||||
type_regex If true, regular expression matching
|
||||
will be used on the type.
|
||||
range_ The criteria to match the context's range.
|
||||
range_subset If true, the criteria will match if it is a subset
|
||||
of the context's range.
|
||||
range_overlap If true, the criteria will match if it overlaps
|
||||
any of the context's range.
|
||||
range_superset If true, the criteria will match if it is a superset
|
||||
of the context's range.
|
||||
range_proper If true, use proper superset/subset operations.
|
||||
No effect if not using set operations.
|
||||
"""
|
||||
self.log = logging.getLogger(self.__class__.__name__)
|
||||
Keyword Parameters/Class attributes:
|
||||
fs The criteria to match the file system type.
|
||||
fs_regex If true, regular expression matching
|
||||
will be used on the file system type.
|
||||
path The criteria to match the path.
|
||||
path_regex If true, regular expression matching
|
||||
will be used on the path.
|
||||
user The criteria to match the context's user.
|
||||
user_regex If true, regular expression matching
|
||||
will be used on the user.
|
||||
role The criteria to match the context's role.
|
||||
role_regex If true, regular expression matching
|
||||
will be used on the role.
|
||||
type_ The criteria to match the context's type.
|
||||
type_regex If true, regular expression matching
|
||||
will be used on the type.
|
||||
range_ The criteria to match the context's range.
|
||||
range_subset If true, the criteria will match if it is a subset
|
||||
of the context's range.
|
||||
range_overlap If true, the criteria will match if it overlaps
|
||||
any of the context's range.
|
||||
range_superset If true, the criteria will match if it is a superset
|
||||
of the context's range.
|
||||
range_proper If true, use proper superset/subset operations.
|
||||
No effect if not using set operations.
|
||||
"""
|
||||
|
||||
self.policy = policy
|
||||
|
||||
self.set_fs(fs, regex=fs_regex)
|
||||
self.set_path(path, regex=path_regex)
|
||||
self.set_filetype(filetype)
|
||||
self.set_user(user, regex=user_regex)
|
||||
self.set_role(role, regex=role_regex)
|
||||
self.set_type(type_, regex=type_regex)
|
||||
self.set_range(range_, overlap=range_overlap, subset=range_subset,
|
||||
superset=range_superset, proper=range_proper)
|
||||
filetype = None
|
||||
fs = CriteriaDescriptor("fs_regex")
|
||||
fs_regex = False
|
||||
path = CriteriaDescriptor("path_regex")
|
||||
path_regex = False
|
||||
|
||||
def results(self):
|
||||
"""Generator which yields all matching genfscons."""
|
||||
self.log.info("Generating results from {0.policy}".format(self))
|
||||
self.log.debug("FS: {0.fs_cmp!r}, regex: {0.fs_regex}".format(self))
|
||||
self.log.debug("Path: {0.path_cmp!r}, regex: {0.path_regex}".format(self))
|
||||
self.log.debug("FS: {0.fs!r}, regex: {0.fs_regex}".format(self))
|
||||
self.log.debug("Path: {0.path!r}, regex: {0.path_regex}".format(self))
|
||||
self.log.debug("Filetype: {0.filetype!r}".format(self))
|
||||
self.log.debug("User: {0.user_cmp!r}, regex: {0.user_regex}".format(self))
|
||||
self.log.debug("Role: {0.role_cmp!r}, regex: {0.role_regex}".format(self))
|
||||
self.log.debug("Type: {0.type_cmp!r}, regex: {0.type_regex}".format(self))
|
||||
self.log.debug("User: {0.user!r}, regex: {0.user_regex}".format(self))
|
||||
self.log.debug("Role: {0.role!r}, regex: {0.role_regex}".format(self))
|
||||
self.log.debug("Type: {0.type_!r}, regex: {0.type_regex}".format(self))
|
||||
self.log.debug("Range: {0.range_!r}, subset: {0.range_subset}, overlap: {0.range_overlap}, "
|
||||
"superset: {0.range_superset}, proper: {0.range_proper}".format(self))
|
||||
|
||||
for genfs in self.policy.genfscons():
|
||||
if self.fs and not self._match_regex(
|
||||
genfs.fs,
|
||||
self.fs_cmp,
|
||||
self.fs,
|
||||
self.fs_regex):
|
||||
continue
|
||||
|
||||
if self.path and not self._match_regex(
|
||||
genfs.path,
|
||||
self.path_cmp,
|
||||
self.path,
|
||||
self.path_regex):
|
||||
continue
|
||||
|
||||
if self.filetype and not self.filetype == genfs.filetype:
|
||||
continue
|
||||
|
||||
if not self._match_context(
|
||||
genfs.context,
|
||||
self.user_cmp,
|
||||
self.user_regex,
|
||||
self.role_cmp,
|
||||
self.role_regex,
|
||||
self.type_cmp,
|
||||
self.type_regex,
|
||||
self.range_cmp,
|
||||
self.range_subset,
|
||||
self.range_overlap,
|
||||
self.range_superset,
|
||||
self.range_proper):
|
||||
if not self._match_context(genfs.context):
|
||||
continue
|
||||
|
||||
yield genfs
|
||||
|
||||
def set_fs(self, fs, **opts):
|
||||
"""
|
||||
Set the criteria for matching the file system type.
|
||||
|
||||
Parameter:
|
||||
fs Name to match the file system.
|
||||
regex If true, regular expression matching will be used.
|
||||
|
||||
Exceptions:
|
||||
NameError Invalid keyword option.
|
||||
"""
|
||||
|
||||
self.fs = fs
|
||||
|
||||
for k in list(opts.keys()):
|
||||
if k == "regex":
|
||||
self.fs_regex = opts[k]
|
||||
else:
|
||||
raise NameError("Invalid name option: {0}".format(k))
|
||||
|
||||
if not self.fs:
|
||||
self.fs_cmp = None
|
||||
elif self.fs_regex:
|
||||
self.fs_cmp = re.compile(self.fs)
|
||||
else:
|
||||
self.fs_cmp = self.fs
|
||||
|
||||
def set_filetype(self, filetype):
|
||||
"""
|
||||
Set the criteria for matching the file type.
|
||||
|
||||
Parameter:
|
||||
filetype File type to match (e.g. stat.S_IFBLK or stat.S_IFREG).
|
||||
"""
|
||||
|
||||
self.filetype = filetype
|
||||
|
||||
def set_path(self, path, **opts):
|
||||
"""
|
||||
Set the criteria for matching the path.
|
||||
|
||||
Parameter:
|
||||
path Criteria to match the path.
|
||||
regex If true, regular expression matching will be used.
|
||||
|
||||
Exceptions:
|
||||
NameError Invalid keyword option.
|
||||
"""
|
||||
|
||||
self.path = path
|
||||
|
||||
for k in list(opts.keys()):
|
||||
if k == "regex":
|
||||
self.path_regex = opts[k]
|
||||
else:
|
||||
raise NameError("Invalid name option: {0}".format(k))
|
||||
|
||||
if not self.path:
|
||||
self.path_cmp = None
|
||||
elif self.path_regex:
|
||||
self.path_cmp = re.compile(self.path)
|
||||
else:
|
||||
self.path_cmp = self.path
|
||||
|
@ -23,6 +23,8 @@ from collections import namedtuple
|
||||
import networkx as nx
|
||||
from networkx.exception import NetworkXError, NetworkXNoPath
|
||||
|
||||
from .descriptors import EdgeAttrIntMax, EdgeAttrList
|
||||
|
||||
__all__ = ['InfoFlowAnalysis']
|
||||
|
||||
# Return values for the analysis
|
||||
@ -36,7 +38,7 @@ class InfoFlowAnalysis(object):
|
||||
|
||||
"""Information flow analysis."""
|
||||
|
||||
def __init__(self, policy, perm_map, minweight=1, exclude=None):
|
||||
def __init__(self, policy, perm_map, min_weight=1, exclude=None):
|
||||
"""
|
||||
Parameters:
|
||||
policy The policy to analyze.
|
||||
@ -50,59 +52,48 @@ class InfoFlowAnalysis(object):
|
||||
|
||||
self.policy = policy
|
||||
|
||||
self.set_min_weight(minweight)
|
||||
self.set_perm_map(perm_map)
|
||||
self.set_exclude(exclude)
|
||||
self.min_weight = min_weight
|
||||
self.perm_map = perm_map
|
||||
self.exclude = exclude
|
||||
self.rebuildgraph = True
|
||||
self.rebuildsubgraph = True
|
||||
|
||||
self.G = nx.DiGraph()
|
||||
self.subG = None
|
||||
|
||||
def set_min_weight(self, weight):
|
||||
"""
|
||||
Set the minimum permission weight for the information flow analysis.
|
||||
@property
|
||||
def min_weight(self):
|
||||
return self._min_weight
|
||||
|
||||
Parameter:
|
||||
weight Minimum permission weight (1-10)
|
||||
|
||||
Exceptions:
|
||||
ValueError The minimum weight is not 1-10.
|
||||
"""
|
||||
@min_weight.setter
|
||||
def min_weight(self, weight):
|
||||
if not 1 <= weight <= 10:
|
||||
raise ValueError(
|
||||
"Min information flow weight must be an integer 1-10.")
|
||||
|
||||
self.minweight = weight
|
||||
self._min_weight = weight
|
||||
self.rebuildsubgraph = True
|
||||
|
||||
def set_perm_map(self, perm_map):
|
||||
"""
|
||||
Set the permission map used for the information flow analysis.
|
||||
|
||||
Parameter:
|
||||
perm_map The permission map.
|
||||
|
||||
Exceptions:
|
||||
TypeError The map is not a file path or permission map object.
|
||||
"""
|
||||
self.perm_map = perm_map
|
||||
@property
|
||||
def perm_map(self):
|
||||
return self._perm_map
|
||||
|
||||
@perm_map.setter
|
||||
def perm_map(self, perm_map):
|
||||
self._perm_map = perm_map
|
||||
self.rebuildgraph = True
|
||||
self.rebuildsubgraph = True
|
||||
|
||||
def set_exclude(self, exclude):
|
||||
"""
|
||||
Set the types to exclude from the information flow analysis.
|
||||
@property
|
||||
def exclude(self):
|
||||
return self._exclude
|
||||
|
||||
Parameter:
|
||||
exclude A list of types.
|
||||
"""
|
||||
|
||||
if exclude:
|
||||
self.exclude = [self.policy.lookup_type(t) for t in exclude]
|
||||
@exclude.setter
|
||||
def exclude(self, types):
|
||||
if types:
|
||||
self._exclude = [self.policy.lookup_type(t) for t in types]
|
||||
else:
|
||||
self.exclude = []
|
||||
self._exclude = []
|
||||
|
||||
self.rebuildsubgraph = True
|
||||
|
||||
@ -344,7 +335,7 @@ class InfoFlowAnalysis(object):
|
||||
|
||||
self.log.info("Building subgraph...")
|
||||
self.log.debug("Excluding {0!r}".format(self.exclude))
|
||||
self.log.debug("Min weight {0}".format(self.minweight))
|
||||
self.log.debug("Min weight {0}".format(self.min_weight))
|
||||
|
||||
# delete excluded types from subgraph
|
||||
nodes = [n for n in self.G.nodes() if n not in self.exclude]
|
||||
@ -353,11 +344,11 @@ class InfoFlowAnalysis(object):
|
||||
# delete edges below minimum weight.
|
||||
# no need if weight is 1, since that
|
||||
# does not exclude any edges.
|
||||
if self.minweight > 1:
|
||||
if self.min_weight > 1:
|
||||
delete_list = []
|
||||
for s, t in self.subG.edges_iter():
|
||||
edge = Edge(self.subG, s, t)
|
||||
if edge.weight < self.minweight:
|
||||
if edge.weight < self.min_weight:
|
||||
delete_list.append(edge)
|
||||
|
||||
self.subG.remove_edges_from(delete_list)
|
||||
@ -366,59 +357,6 @@ class InfoFlowAnalysis(object):
|
||||
self.log.info("Completed building subgraph.")
|
||||
|
||||
|
||||
class EdgeAttrList(object):
|
||||
|
||||
"""
|
||||
A descriptor for edge attributes that are lists.
|
||||
|
||||
Parameter:
|
||||
name The edge property name
|
||||
"""
|
||||
|
||||
def __init__(self, propname):
|
||||
self.name = propname
|
||||
|
||||
def __get__(self, obj, type=None):
|
||||
return obj.G[obj.source][obj.target][self.name]
|
||||
|
||||
def __set__(self, obj, value):
|
||||
# None is a special value to initialize
|
||||
if value is None:
|
||||
obj.G[obj.source][obj.target][self.name] = []
|
||||
else:
|
||||
raise ValueError("{0} lists should not be assigned directly".format(self.name))
|
||||
|
||||
def __delete__(self, obj):
|
||||
# in Python3 a .clear() function was added for lists
|
||||
# keep this implementation for Python 2 compat
|
||||
del obj.G[obj.source][obj.target][self.name][:]
|
||||
|
||||
|
||||
class EdgeAttrIntMax(object):
|
||||
|
||||
"""
|
||||
A descriptor for edge attributes that are non-negative integers that always
|
||||
keep the max assigned value until re-initialized.
|
||||
|
||||
Parameter:
|
||||
name The edge property name
|
||||
"""
|
||||
|
||||
def __init__(self, propname):
|
||||
self.name = propname
|
||||
|
||||
def __get__(self, obj, type=None):
|
||||
return obj.G[obj.source][obj.target][self.name]
|
||||
|
||||
def __set__(self, obj, value):
|
||||
# None is a special value to initialize
|
||||
if value is None:
|
||||
obj.G[obj.source][obj.target][self.name] = 0
|
||||
else:
|
||||
current_value = obj.G[obj.source][obj.target][self.name]
|
||||
obj.G[obj.source][obj.target][self.name] = max(current_value, value)
|
||||
|
||||
|
||||
class Edge(object):
|
||||
|
||||
"""
|
||||
|
@ -24,79 +24,51 @@ from . import contextquery
|
||||
|
||||
class InitialSIDQuery(compquery.ComponentQuery, contextquery.ContextQuery):
|
||||
|
||||
"""Initial SID (context) query."""
|
||||
"""
|
||||
Initial SID (Initial context) query.
|
||||
|
||||
def __init__(self, policy,
|
||||
name=None, name_regex=False,
|
||||
user=None, user_regex=False,
|
||||
role=None, role_regex=False,
|
||||
type_=None, type_regex=False,
|
||||
range_=None, range_overlap=False, range_subset=False,
|
||||
range_superset=False, range_proper=False):
|
||||
"""
|
||||
Parameters:
|
||||
policy The policy to query.
|
||||
Parameter:
|
||||
policy The policy to query.
|
||||
|
||||
user The criteria to match the context's user.
|
||||
user_regex If true, regular expression matching
|
||||
will be used on the user.
|
||||
role The criteria to match the context's role.
|
||||
role_regex If true, regular expression matching
|
||||
will be used on the role.
|
||||
type_ The criteria to match the context's type.
|
||||
type_regex If true, regular expression matching
|
||||
will be used on the type.
|
||||
range_ The criteria to match the context's range.
|
||||
range_subset If true, the criteria will match if it is a subset
|
||||
of the context's range.
|
||||
range_overlap If true, the criteria will match if it overlaps
|
||||
any of the context's range.
|
||||
range_superset If true, the criteria will match if it is a superset
|
||||
of the context's range.
|
||||
range_proper If true, use proper superset/subset operations.
|
||||
No effect if not using set operations.
|
||||
"""
|
||||
self.log = logging.getLogger(self.__class__.__name__)
|
||||
|
||||
self.policy = policy
|
||||
|
||||
self.set_name(name, regex=name_regex)
|
||||
self.set_user(user, regex=user_regex)
|
||||
self.set_role(role, regex=role_regex)
|
||||
self.set_type(type_, regex=type_regex)
|
||||
self.set_range(range_, overlap=range_overlap, subset=range_subset,
|
||||
superset=range_superset, proper=range_proper)
|
||||
Keyword Parameters/Class attributes:
|
||||
name The Initial SID name to match.
|
||||
name_regex If true, regular expression matching
|
||||
will be used on the Initial SID name.
|
||||
user The criteria to match the context's user.
|
||||
user_regex If true, regular expression matching
|
||||
will be used on the user.
|
||||
role The criteria to match the context's role.
|
||||
role_regex If true, regular expression matching
|
||||
will be used on the role.
|
||||
type_ The criteria to match the context's type.
|
||||
type_regex If true, regular expression matching
|
||||
will be used on the type.
|
||||
range_ The criteria to match the context's range.
|
||||
range_subset If true, the criteria will match if it is a subset
|
||||
of the context's range.
|
||||
range_overlap If true, the criteria will match if it overlaps
|
||||
any of the context's range.
|
||||
range_superset If true, the criteria will match if it is a superset
|
||||
of the context's range.
|
||||
range_proper If true, use proper superset/subset operations.
|
||||
No effect if not using set operations.
|
||||
"""
|
||||
|
||||
def results(self):
|
||||
"""Generator which yields all matching initial SIDs."""
|
||||
self.log.info("Generating results from {0.policy}".format(self))
|
||||
self.log.debug("Name: {0.name_cmp!r}, regex: {0.name_regex}".format(self))
|
||||
self.log.debug("User: {0.user_cmp!r}, regex: {0.user_regex}".format(self))
|
||||
self.log.debug("Role: {0.role_cmp!r}, regex: {0.role_regex}".format(self))
|
||||
self.log.debug("Type: {0.type_cmp!r}, regex: {0.type_regex}".format(self))
|
||||
self.log.debug("Name: {0.name!r}, regex: {0.name_regex}".format(self))
|
||||
self.log.debug("User: {0.user!r}, regex: {0.user_regex}".format(self))
|
||||
self.log.debug("Role: {0.role!r}, regex: {0.role_regex}".format(self))
|
||||
self.log.debug("Type: {0.type_!r}, regex: {0.type_regex}".format(self))
|
||||
self.log.debug("Range: {0.range_!r}, subset: {0.range_subset}, overlap: {0.range_overlap}, "
|
||||
"superset: {0.range_superset}, proper: {0.range_proper}".format(self))
|
||||
|
||||
for i in self.policy.initialsids():
|
||||
if self.name and not self._match_regex(
|
||||
i,
|
||||
self.name_cmp,
|
||||
self.name_regex):
|
||||
if not self._match_name(i):
|
||||
continue
|
||||
|
||||
if not self._match_context(
|
||||
i.context,
|
||||
self.user_cmp,
|
||||
self.user_regex,
|
||||
self.role_cmp,
|
||||
self.role_regex,
|
||||
self.type_cmp,
|
||||
self.type_regex,
|
||||
self.range_cmp,
|
||||
self.range_subset,
|
||||
self.range_overlap,
|
||||
self.range_superset,
|
||||
self.range_proper):
|
||||
if not self._match_context(i.context):
|
||||
continue
|
||||
|
||||
yield i
|
||||
|
@ -19,129 +19,73 @@
|
||||
# pylint: disable=attribute-defined-outside-init,no-member
|
||||
import re
|
||||
|
||||
from .descriptors import CriteriaDescriptor, CriteriaSetDescriptor
|
||||
|
||||
|
||||
class MatchAlias(object):
|
||||
|
||||
"""Mixin for matching an object's aliases."""
|
||||
|
||||
def _match_alias(self, obj):
|
||||
"""Match the object to the alias criteria."""
|
||||
return self._match_in_set(obj, self.alias_cmp, self.alias_regex)
|
||||
alias = CriteriaDescriptor("alias_regex")
|
||||
alias_regex = False
|
||||
|
||||
def set_alias(self, alias, **opts):
|
||||
def _match_alias(self, obj):
|
||||
"""
|
||||
Set the criteria for the component's aliases.
|
||||
Match the alias criteria
|
||||
|
||||
Parameter:
|
||||
alias Name to match the component's aliases.
|
||||
|
||||
Keyword Options:
|
||||
regex If true, regular expression matching will be used.
|
||||
|
||||
Exceptions:
|
||||
NameError Invalid keyword option.
|
||||
obj An object with an alias generator method named "aliases"
|
||||
"""
|
||||
|
||||
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
|
||||
# if there is no criteria, everything matches.
|
||||
return True
|
||||
|
||||
return self._match_in_set(obj.aliases(), self.alias, self.alias_regex)
|
||||
|
||||
|
||||
class MatchObjClass(object):
|
||||
|
||||
"""Mixin for matching an object's class."""
|
||||
|
||||
tclass = CriteriaSetDescriptor("tclass_regex", "lookup_class")
|
||||
tclass_regex = False
|
||||
|
||||
def _match_object_class(self, obj):
|
||||
"""Match the object class criteria"""
|
||||
|
||||
if isinstance(self.tclass_cmp, set):
|
||||
return obj in self.tclass_cmp
|
||||
elif self.tclass_regex:
|
||||
return bool(self.tclass_cmp.search(str(obj)))
|
||||
else:
|
||||
return obj == self.tclass_cmp
|
||||
|
||||
def set_tclass(self, tclass, **opts):
|
||||
"""
|
||||
Set the object class(es) for the rule query.
|
||||
Match the object class criteria
|
||||
|
||||
Parameter:
|
||||
tclass The name of the object classes to match.
|
||||
This must be a string if regular expression
|
||||
matching is used.
|
||||
|
||||
Keyword Options:
|
||||
regex If true, use a regular expression for
|
||||
matching the object class. If false, any
|
||||
set intersection will match.
|
||||
|
||||
Exceptions:
|
||||
NameError Invalid keyword option.
|
||||
obj An object with an object class attribute named "tclass"
|
||||
"""
|
||||
|
||||
self.tclass = tclass
|
||||
|
||||
for k in list(opts.keys()):
|
||||
if k == "regex":
|
||||
self.tclass_regex = opts[k]
|
||||
else:
|
||||
raise NameError("Invalid object class option: {0}".format(k))
|
||||
|
||||
if not self.tclass:
|
||||
self.tclass_cmp = None
|
||||
# if there is no criteria, everything matches.
|
||||
return True
|
||||
elif self.tclass_regex:
|
||||
self.tclass_cmp = re.compile(self.tclass)
|
||||
elif isinstance(self.tclass, str):
|
||||
self.tclass_cmp = self.policy.lookup_class(self.tclass)
|
||||
return bool(self.tclass.search(str(obj.tclass)))
|
||||
else:
|
||||
self.tclass_cmp = set(self.policy.lookup_class(c) for c in self.tclass)
|
||||
return obj.tclass in self.tclass
|
||||
|
||||
|
||||
class MatchPermission(object):
|
||||
|
||||
"""Mixin for matching an object's permissions."""
|
||||
|
||||
def _match_perms(self, obj):
|
||||
"""Match the object to the permission criteria."""
|
||||
return self._match_set(obj, self.perms_cmp, self.perms_equal)
|
||||
perms = CriteriaSetDescriptor("perms_regex")
|
||||
perms_equal = False
|
||||
perms_regex = False
|
||||
|
||||
def set_perms(self, perms, **opts):
|
||||
def _match_perms(self, obj):
|
||||
"""
|
||||
Set the permission set for the TE rule query.
|
||||
Match the permission criteria
|
||||
|
||||
Parameter:
|
||||
perms The permissions to match.
|
||||
|
||||
Options:
|
||||
equal If true, the permission set of the rule
|
||||
must equal the permissions criteria to
|
||||
match. If false, permission in the critera
|
||||
will cause a rule match.
|
||||
|
||||
Exceptions:
|
||||
NameError Invalid permission set keyword option.
|
||||
obj An object with a permission set class attribute named "perms"
|
||||
"""
|
||||
|
||||
self.perms = perms
|
||||
|
||||
for k in list(opts.keys()):
|
||||
if k == "equal":
|
||||
self.perms_equal = opts[k]
|
||||
else:
|
||||
raise NameError("Invalid permission set option: {0}".format(k))
|
||||
|
||||
if not self.perms:
|
||||
self.perms_cmp = None
|
||||
else:
|
||||
self.perms_cmp = set(self.perms)
|
||||
# if there is no criteria, everything matches.
|
||||
return True
|
||||
|
||||
return self._match_regex_or_set(obj.perms, self.perms, self.perms_equal, self.perms_regex)
|
||||
|
@ -18,53 +18,52 @@
|
||||
#
|
||||
import logging
|
||||
|
||||
from . import rulequery
|
||||
from . import mixins, query
|
||||
from .descriptors import CriteriaDescriptor, CriteriaSetDescriptor, RuletypeDescriptor
|
||||
|
||||
|
||||
class MLSRuleQuery(rulequery.RuleQuery):
|
||||
class MLSRuleQuery(mixins.MatchObjClass, query.PolicyQuery):
|
||||
|
||||
"""Query MLS rules."""
|
||||
"""
|
||||
Query MLS rules.
|
||||
|
||||
def __init__(self, policy,
|
||||
ruletype=None,
|
||||
source=None, source_regex=False,
|
||||
target=None, target_regex=False,
|
||||
tclass=None, tclass_regex=False,
|
||||
default=None, default_overlap=False, default_subset=False,
|
||||
default_superset=False, default_proper=False):
|
||||
"""
|
||||
Parameters:
|
||||
policy The policy to query.
|
||||
ruletype The rule type(s) to match.
|
||||
source The name of the source type/attribute to match.
|
||||
source_regex If true, regular expression matching will
|
||||
be used on the source type/attribute.
|
||||
target The name of the target type/attribute to match.
|
||||
target_regex If true, regular expression matching will
|
||||
be used on the target type/attribute.
|
||||
tclass The object class(es) to match.
|
||||
tclass_regex If true, use a regular expression for
|
||||
matching the rule's object class.
|
||||
"""
|
||||
self.log = logging.getLogger(self.__class__.__name__)
|
||||
Parameter:
|
||||
policy The policy to query.
|
||||
|
||||
self.policy = policy
|
||||
Keyword Parameters/Class attributes:
|
||||
ruletype The list of rule type(s) to match.
|
||||
source The name of the source type/attribute to match.
|
||||
source_regex If true, regular expression matching will
|
||||
be used on the source type/attribute.
|
||||
target The name of the target type/attribute to match.
|
||||
target_regex If true, regular expression matching will
|
||||
be used on the target type/attribute.
|
||||
tclass The object class(es) to match.
|
||||
tclass_regex If true, use a regular expression for
|
||||
matching the rule's object class.
|
||||
"""
|
||||
|
||||
self.set_ruletype(ruletype)
|
||||
self.set_source(source, regex=source_regex)
|
||||
self.set_target(target, regex=target_regex)
|
||||
self.set_tclass(tclass, regex=tclass_regex)
|
||||
self.set_default(default, overlap=default_overlap, subset=default_subset,
|
||||
superset=default_superset, proper=default_proper)
|
||||
ruletype = RuletypeDescriptor("validate_mls_ruletype")
|
||||
source = CriteriaDescriptor("source_regex", "lookup_type_or_attr")
|
||||
source_regex = False
|
||||
target = CriteriaDescriptor("target_regex", "lookup_type_or_attr")
|
||||
target_regex = False
|
||||
tclass = CriteriaSetDescriptor("tclass_regex", "lookup_class")
|
||||
tclass_regex = False
|
||||
default = CriteriaDescriptor(lookup_function="lookup_range")
|
||||
default_overlap = False
|
||||
default_subset = False
|
||||
default_superset = False
|
||||
default_proper = False
|
||||
|
||||
def results(self):
|
||||
"""Generator which yields all matching MLS rules."""
|
||||
self.log.info("Generating results from {0.policy}".format(self))
|
||||
self.log.debug("Ruletypes: {0.ruletype}".format(self))
|
||||
self.log.debug("Source: {0.source_cmp!r}, regex: {0.source_regex}".format(self))
|
||||
self.log.debug("Target: {0.target_cmp!r}, regex: {0.target_regex}".format(self))
|
||||
self.log.debug("Class: {0.tclass_cmp!r}, regex: {0.tclass_regex}".format(self))
|
||||
self.log.debug("Default: {0.default_cmp!r}, overlap: {0.default_overlap}, "
|
||||
self.log.debug("Source: {0.source!r}, regex: {0.source_regex}".format(self))
|
||||
self.log.debug("Target: {0.target!r}, regex: {0.target_regex}".format(self))
|
||||
self.log.debug("Class: {0.tclass!r}, regex: {0.tclass_regex}".format(self))
|
||||
self.log.debug("Default: {0.default!r}, overlap: {0.default_overlap}, "
|
||||
"subset: {0.default_subset}, superset: {0.default_superset}, "
|
||||
"proper: {0.default_proper}".format(self))
|
||||
|
||||
@ -81,7 +80,7 @@ class MLSRuleQuery(rulequery.RuleQuery):
|
||||
#
|
||||
if self.source and not self._match_regex(
|
||||
rule.source,
|
||||
self.source_cmp,
|
||||
self.source,
|
||||
self.source_regex):
|
||||
continue
|
||||
|
||||
@ -90,14 +89,14 @@ class MLSRuleQuery(rulequery.RuleQuery):
|
||||
#
|
||||
if self.target and not self._match_regex(
|
||||
rule.target,
|
||||
self.target_cmp,
|
||||
self.target,
|
||||
self.target_regex):
|
||||
continue
|
||||
|
||||
#
|
||||
# Matching on object class
|
||||
#
|
||||
if self.tclass and not self._match_object_class(rule.tclass):
|
||||
if not self._match_object_class(rule):
|
||||
continue
|
||||
|
||||
#
|
||||
@ -105,7 +104,7 @@ class MLSRuleQuery(rulequery.RuleQuery):
|
||||
#
|
||||
if self.default and not self._match_range(
|
||||
rule.default,
|
||||
self.default_cmp,
|
||||
self.default,
|
||||
self.default_subset,
|
||||
self.default_overlap,
|
||||
self.default_superset,
|
||||
@ -114,55 +113,3 @@ class MLSRuleQuery(rulequery.RuleQuery):
|
||||
|
||||
# if we get here, we have matched all available criteria
|
||||
yield rule
|
||||
|
||||
def set_ruletype(self, ruletype):
|
||||
"""
|
||||
Set the rule types for the rule query.
|
||||
|
||||
Parameter:
|
||||
ruletype The rule types to match.
|
||||
"""
|
||||
if ruletype:
|
||||
self.policy.validate_mls_ruletype(ruletype)
|
||||
|
||||
self.ruletype = ruletype
|
||||
|
||||
def set_default(self, default, **opts):
|
||||
"""
|
||||
Set the criteria for matching the rule's default range.
|
||||
|
||||
Parameter:
|
||||
default Criteria to match the rule's default range.
|
||||
|
||||
Keyword Parameters:
|
||||
subset If true, the criteria will match if it is a subset
|
||||
of the rule's default range.
|
||||
overlap If true, the criteria will match if it overlaps
|
||||
any of the rule's default range.
|
||||
superset If true, the criteria will match if it is a superset
|
||||
of the rule's default range.
|
||||
proper If true, use proper superset/subset operations.
|
||||
No effect if not using set operations.
|
||||
|
||||
Exceptions:
|
||||
NameError Invalid keyword option.
|
||||
"""
|
||||
|
||||
self.default = default
|
||||
|
||||
for k in list(opts.keys()):
|
||||
if k == "subset":
|
||||
self.default_subset = opts[k]
|
||||
elif k == "overlap":
|
||||
self.default_overlap = opts[k]
|
||||
elif k == "superset":
|
||||
self.default_superset = opts[k]
|
||||
elif k == "proper":
|
||||
self.default_proper = opts[k]
|
||||
else:
|
||||
raise NameError("Invalid name option: {0}".format(k))
|
||||
|
||||
if not self.default:
|
||||
self.default_cmp = None
|
||||
else:
|
||||
self.default_cmp = self.policy.lookup_range(self.default)
|
||||
|
@ -24,79 +24,54 @@ from . import contextquery
|
||||
|
||||
class NetifconQuery(compquery.ComponentQuery, contextquery.ContextQuery):
|
||||
|
||||
"""Network interface context query."""
|
||||
"""
|
||||
Network interface context query.
|
||||
|
||||
def __init__(self, policy,
|
||||
name=None, name_regex=False,
|
||||
user=None, user_regex=False,
|
||||
role=None, role_regex=False,
|
||||
type_=None, type_regex=False,
|
||||
range_=None, range_overlap=False, range_subset=False,
|
||||
range_superset=False, range_proper=False):
|
||||
"""
|
||||
Parameters:
|
||||
policy The policy to query.
|
||||
Parameter:
|
||||
policy The policy to query.
|
||||
|
||||
user The criteria to match the context's user.
|
||||
user_regex If true, regular expression matching
|
||||
will be used on the user.
|
||||
role The criteria to match the context's role.
|
||||
role_regex If true, regular expression matching
|
||||
will be used on the role.
|
||||
type_ The criteria to match the context's type.
|
||||
type_regex If true, regular expression matching
|
||||
will be used on the type.
|
||||
range_ The criteria to match the context's range.
|
||||
range_subset If true, the criteria will match if it is a subset
|
||||
of the context's range.
|
||||
range_overlap If true, the criteria will match if it overlaps
|
||||
any of the context's range.
|
||||
range_superset If true, the criteria will match if it is a superset
|
||||
of the context's range.
|
||||
range_proper If true, use proper superset/subset operations.
|
||||
No effect if not using set operations.
|
||||
"""
|
||||
self.log = logging.getLogger(self.__class__.__name__)
|
||||
|
||||
self.policy = policy
|
||||
|
||||
self.set_name(name, regex=name_regex)
|
||||
self.set_user(user, regex=user_regex)
|
||||
self.set_role(role, regex=role_regex)
|
||||
self.set_type(type_, regex=type_regex)
|
||||
self.set_range(range_, overlap=range_overlap, subset=range_subset,
|
||||
superset=range_superset, proper=range_proper)
|
||||
Keyword Parameters/Class attributes:
|
||||
name The name of the network interface to match.
|
||||
name_regex If true, regular expression matching will
|
||||
be used for matching the name.
|
||||
user The criteria to match the context's user.
|
||||
user_regex If true, regular expression matching
|
||||
will be used on the user.
|
||||
role The criteria to match the context's role.
|
||||
role_regex If true, regular expression matching
|
||||
will be used on the role.
|
||||
type_ The criteria to match the context's type.
|
||||
type_regex If true, regular expression matching
|
||||
will be used on the type.
|
||||
range_ The criteria to match the context's range.
|
||||
range_subset If true, the criteria will match if it is a subset
|
||||
of the context's range.
|
||||
range_overlap If true, the criteria will match if it overlaps
|
||||
any of the context's range.
|
||||
range_superset If true, the criteria will match if it is a superset
|
||||
of the context's range.
|
||||
range_proper If true, use proper superset/subset operations.
|
||||
No effect if not using set operations.
|
||||
"""
|
||||
|
||||
def results(self):
|
||||
"""Generator which yields all matching netifcons."""
|
||||
self.log.info("Generating results from {0.policy}".format(self))
|
||||
self.log.debug("Name: {0.name_cmp!r}, regex: {0.name_regex}".format(self))
|
||||
self.log.debug("User: {0.user_cmp!r}, regex: {0.user_regex}".format(self))
|
||||
self.log.debug("Role: {0.role_cmp!r}, regex: {0.role_regex}".format(self))
|
||||
self.log.debug("Type: {0.type_cmp!r}, regex: {0.type_regex}".format(self))
|
||||
self.log.debug("Name: {0.name!r}, regex: {0.name_regex}".format(self))
|
||||
self.log.debug("User: {0.user!r}, regex: {0.user_regex}".format(self))
|
||||
self.log.debug("Role: {0.role!r}, regex: {0.role_regex}".format(self))
|
||||
self.log.debug("Type: {0.type_!r}, regex: {0.type_regex}".format(self))
|
||||
self.log.debug("Range: {0.range_!r}, subset: {0.range_subset}, overlap: {0.range_overlap}, "
|
||||
"superset: {0.range_superset}, proper: {0.range_proper}".format(self))
|
||||
|
||||
for netif in self.policy.netifcons():
|
||||
if self.name and not self._match_regex(
|
||||
netif.netif,
|
||||
self.name_cmp,
|
||||
self.name,
|
||||
self.name_regex):
|
||||
continue
|
||||
|
||||
if not self._match_context(
|
||||
netif.context,
|
||||
self.user_cmp,
|
||||
self.user_regex,
|
||||
self.role_cmp,
|
||||
self.role_regex,
|
||||
self.type_cmp,
|
||||
self.type_regex,
|
||||
self.range_cmp,
|
||||
self.range_subset,
|
||||
self.range_overlap,
|
||||
self.range_superset,
|
||||
self.range_proper):
|
||||
if not self._match_context(netif.context):
|
||||
continue
|
||||
|
||||
yield netif
|
||||
|
@ -29,62 +29,82 @@ from . import contextquery
|
||||
|
||||
class NodeconQuery(contextquery.ContextQuery):
|
||||
|
||||
"""Query nodecon statements."""
|
||||
"""
|
||||
Query nodecon statements.
|
||||
|
||||
def __init__(self, policy,
|
||||
network=None, network_overlap=False,
|
||||
ip_version=None,
|
||||
user=None, user_regex=False,
|
||||
role=None, role_regex=False,
|
||||
type_=None, type_regex=False,
|
||||
range_=None, range_overlap=False, range_subset=False,
|
||||
range_superset=False, range_proper=False):
|
||||
"""
|
||||
Parameters:
|
||||
policy The policy to query.
|
||||
Parameter:
|
||||
policy The policy to query.
|
||||
|
||||
network The network address.
|
||||
network_overlap If true, the net will match if it overlaps with
|
||||
the nodecon's network instead of equality.
|
||||
user The criteria to match the context's user.
|
||||
user_regex If true, regular expression matching
|
||||
will be used on the user.
|
||||
role The criteria to match the context's role.
|
||||
role_regex If true, regular expression matching
|
||||
will be used on the role.
|
||||
type_ The criteria to match the context's type.
|
||||
type_regex If true, regular expression matching
|
||||
will be used on the type.
|
||||
range_ The criteria to match the context's range.
|
||||
range_subset If true, the criteria will match if it is a subset
|
||||
of the context's range.
|
||||
range_overlap If true, the criteria will match if it overlaps
|
||||
any of the context's range.
|
||||
range_superset If true, the criteria will match if it is a superset
|
||||
of the context's range.
|
||||
range_proper If true, use proper superset/subset operations.
|
||||
No effect if not using set operations.
|
||||
"""
|
||||
self.log = logging.getLogger(self.__class__.__name__)
|
||||
Keyword Parameters/Class attributes:
|
||||
network The IPv4/IPv6 address or IPv4/IPv6 network address
|
||||
with netmask, e.g. 192.168.1.0/255.255.255.0 or
|
||||
"192.168.1.0/24".
|
||||
network_overlap If true, the net will match if it overlaps with
|
||||
the nodecon's network instead of equality.
|
||||
ip_version The IP version of the nodecon to match. (socket.AF_INET
|
||||
for IPv4 or socket.AF_INET6 for IPv6)
|
||||
user The criteria to match the context's user.
|
||||
user_regex If true, regular expression matching
|
||||
will be used on the user.
|
||||
role The criteria to match the context's role.
|
||||
role_regex If true, regular expression matching
|
||||
will be used on the role.
|
||||
type_ The criteria to match the context's type.
|
||||
type_regex If true, regular expression matching
|
||||
will be used on the type.
|
||||
range_ The criteria to match the context's range.
|
||||
range_subset If true, the criteria will match if it is a subset
|
||||
of the context's range.
|
||||
range_overlap If true, the criteria will match if it overlaps
|
||||
any of the context's range.
|
||||
range_superset If true, the criteria will match if it is a superset
|
||||
of the context's range.
|
||||
range_proper If true, use proper superset/subset operations.
|
||||
No effect if not using set operations.
|
||||
"""
|
||||
|
||||
self.policy = policy
|
||||
_network = None
|
||||
network_overlap = False
|
||||
_ip_version = None
|
||||
|
||||
self.set_network(network, overlap=network_overlap)
|
||||
self.set_ip_version(ip_version)
|
||||
self.set_user(user, regex=user_regex)
|
||||
self.set_role(role, regex=role_regex)
|
||||
self.set_type(type_, regex=type_regex)
|
||||
self.set_range(range_, overlap=range_overlap, subset=range_subset,
|
||||
superset=range_superset, proper=range_proper)
|
||||
@property
|
||||
def ip_version(self):
|
||||
return self._ip_version
|
||||
|
||||
@ip_version.setter
|
||||
def ip_version(self, value):
|
||||
if value:
|
||||
if not (value == AF_INET or value == AF_INET6):
|
||||
raise ValueError(
|
||||
"The address family must be {0} for IPv4 or {1} for IPv6.".
|
||||
format(AF_INET, AF_INET6))
|
||||
|
||||
self._ip_version = value
|
||||
else:
|
||||
self._ip_version = None
|
||||
|
||||
@property
|
||||
def network(self):
|
||||
return self._network
|
||||
|
||||
@network.setter
|
||||
def network(self, value):
|
||||
if value:
|
||||
try:
|
||||
self._network = ipaddress.ip_network(value)
|
||||
except NameError: # pragma: no cover
|
||||
raise RuntimeError("Nodecon IP address/network functions require Python 3.3+.")
|
||||
else:
|
||||
self._network = None
|
||||
|
||||
def results(self):
|
||||
"""Generator which yields all matching nodecons."""
|
||||
self.log.info("Generating results from {0.policy}".format(self))
|
||||
self.log.debug("Network: {0.network!r}, overlap: {0.network_overlap}".format(self))
|
||||
self.log.debug("Ver: {0.version}".format(self))
|
||||
self.log.debug("User: {0.user_cmp!r}, regex: {0.user_regex}".format(self))
|
||||
self.log.debug("Role: {0.role_cmp!r}, regex: {0.role_regex}".format(self))
|
||||
self.log.debug("Type: {0.type_cmp!r}, regex: {0.type_regex}".format(self))
|
||||
self.log.debug("IP Version: {0.ip_version}".format(self))
|
||||
self.log.debug("User: {0.user!r}, regex: {0.user_regex}".format(self))
|
||||
self.log.debug("Role: {0.role!r}, regex: {0.role_regex}".format(self))
|
||||
self.log.debug("Type: {0.type_!r}, regex: {0.type_regex}".format(self))
|
||||
self.log.debug("Range: {0.range_!r}, subset: {0.range_subset}, overlap: {0.range_overlap}, "
|
||||
"superset: {0.range_superset}, proper: {0.range_proper}".format(self))
|
||||
|
||||
@ -119,77 +139,10 @@ class NodeconQuery(contextquery.ContextQuery):
|
||||
if not net == self.network:
|
||||
continue
|
||||
|
||||
if self.version and self.version != nodecon.ip_version:
|
||||
if self.ip_version and self.ip_version != nodecon.ip_version:
|
||||
continue
|
||||
|
||||
if not self._match_context(
|
||||
nodecon.context,
|
||||
self.user_cmp,
|
||||
self.user_regex,
|
||||
self.role_cmp,
|
||||
self.role_regex,
|
||||
self.type_cmp,
|
||||
self.type_regex,
|
||||
self.range_cmp,
|
||||
self.range_subset,
|
||||
self.range_overlap,
|
||||
self.range_superset,
|
||||
self.range_proper):
|
||||
if not self._match_context(nodecon.context):
|
||||
continue
|
||||
|
||||
yield nodecon
|
||||
|
||||
def set_network(self, net, **opts):
|
||||
"""
|
||||
Set the criteria for matching the network.
|
||||
|
||||
Parameter:
|
||||
net String IPv4/IPv6 address or IPv4/IPv6 network address
|
||||
with netmask, e.g. 192.168.1.0/255.255.255.0 or
|
||||
"192.168.1.0/24".
|
||||
|
||||
Keyword parameters:
|
||||
overlap If true, the criteria will match if it overlaps with the
|
||||
nodecon's network instead of equality.
|
||||
|
||||
Exceptions:
|
||||
NameError Invalid keyword parameter.
|
||||
"""
|
||||
|
||||
if net:
|
||||
try:
|
||||
self.network = ipaddress.ip_network(net)
|
||||
except NameError: # pragma: no cover
|
||||
raise RuntimeError("Nodecon IP address/network functions require Python 3.3+.")
|
||||
else:
|
||||
# ensure self.network is set
|
||||
self.network = None
|
||||
|
||||
for k in list(opts.keys()):
|
||||
if k == "overlap":
|
||||
self.network_overlap = opts[k]
|
||||
else:
|
||||
raise NameError("Invalid name option: {0}".format(k))
|
||||
|
||||
def set_ip_version(self, version):
|
||||
"""
|
||||
Set the criteria for matching the IP version.
|
||||
|
||||
Parameter:
|
||||
version The address family to match. (socket.AF_INET for
|
||||
IPv4 or socket.AF_INET6 for IPv6)
|
||||
|
||||
Exceptions:
|
||||
ValueError Invalid address family number.
|
||||
"""
|
||||
|
||||
if version:
|
||||
if not (version == AF_INET or version == AF_INET6):
|
||||
raise ValueError(
|
||||
"The address family must be {0} for IPv4 or {1} for IPv6.".
|
||||
format(AF_INET, AF_INET6))
|
||||
|
||||
self.version = version
|
||||
|
||||
else:
|
||||
self.version = None
|
||||
|
@ -20,63 +20,63 @@ import logging
|
||||
import re
|
||||
|
||||
from . import compquery
|
||||
from .descriptors import CriteriaDescriptor, CriteriaSetDescriptor
|
||||
from .policyrep.exception import NoCommon
|
||||
|
||||
|
||||
class ObjClassQuery(compquery.ComponentQuery):
|
||||
|
||||
"""Query object classes."""
|
||||
"""
|
||||
Query object classes.
|
||||
|
||||
def __init__(self, policy,
|
||||
name=None, name_regex=False,
|
||||
common=None, common_regex=False,
|
||||
perms=None, 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.log = logging.getLogger(self.__class__.__name__)
|
||||
Parameter:
|
||||
policy The policy to query.
|
||||
|
||||
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)
|
||||
Keyword Parameters/Class attributes:
|
||||
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.
|
||||
"""
|
||||
|
||||
common = CriteriaDescriptor("common_regex", "lookup_common")
|
||||
common_regex = False
|
||||
perms = CriteriaSetDescriptor("perms_regex")
|
||||
perms_equal = False
|
||||
perms_indirect = True
|
||||
perms_regex = False
|
||||
|
||||
def results(self):
|
||||
"""Generator which yields all matching object classes."""
|
||||
self.log.info("Generating results from {0.policy}".format(self))
|
||||
self.log.debug("Name: {0.name_cmp!r}, regex: {0.name_regex}".format(self))
|
||||
self.log.debug("Common: {0.common_cmp!r}, regex: {0.common_regex}".format(self))
|
||||
self.log.debug("Perms: {0.perms_cmp}, regex: {0.perms_regex}, "
|
||||
self.log.debug("Name: {0.name!r}, regex: {0.name_regex}".format(self))
|
||||
self.log.debug("Common: {0.common!r}, regex: {0.common_regex}".format(self))
|
||||
self.log.debug("Perms: {0.perms}, regex: {0.perms_regex}, "
|
||||
"eq: {0.perms_equal}, indirect: {0.perms_indirect}".format(self))
|
||||
|
||||
for class_ in self.policy.classes():
|
||||
if self.name and not self._match_name(class_):
|
||||
if not self._match_name(class_):
|
||||
continue
|
||||
|
||||
if self.common:
|
||||
try:
|
||||
if not self._match_regex(
|
||||
class_.common,
|
||||
self.common_cmp,
|
||||
self.common,
|
||||
self.common_regex):
|
||||
continue
|
||||
except NoCommon:
|
||||
@ -93,75 +93,9 @@ class ObjClassQuery(compquery.ComponentQuery):
|
||||
|
||||
if not self._match_regex_or_set(
|
||||
perms,
|
||||
self.perms_cmp,
|
||||
self.perms,
|
||||
self.perms_equal,
|
||||
self.perms_regex):
|
||||
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 = common
|
||||
|
||||
for k in list(opts.keys()):
|
||||
if k == "regex":
|
||||
self.common_regex = opts[k]
|
||||
else:
|
||||
raise NameError("Invalid common option: {0}".format(k))
|
||||
|
||||
if not self.common:
|
||||
self.common_cmp = None
|
||||
elif self.common_regex:
|
||||
self.common_cmp = re.compile(self.common)
|
||||
else:
|
||||
self.common_cmp = self.policy.lookup_common(self.common)
|
||||
|
||||
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 list(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 not self.perms:
|
||||
self.perms_cmp = None
|
||||
elif self.perms_regex:
|
||||
self.perms_cmp = re.compile(self.perms)
|
||||
else:
|
||||
self.perms_cmp = self.perms
|
||||
|
@ -23,28 +23,25 @@ from . import compquery
|
||||
|
||||
class PolCapQuery(compquery.ComponentQuery):
|
||||
|
||||
"""Query SELinux policy capabilities"""
|
||||
"""
|
||||
Query SELinux policy capabilities
|
||||
|
||||
def __init__(self, policy,
|
||||
name=None, name_regex=False):
|
||||
"""
|
||||
Parameters:
|
||||
name The name of the policy capability to match.
|
||||
name_regex If true, regular expression matching will
|
||||
be used for matching the name.
|
||||
"""
|
||||
self.log = logging.getLogger(self.__class__.__name__)
|
||||
Parameter:
|
||||
policy The policy to query.
|
||||
|
||||
self.policy = policy
|
||||
self.set_name(name, regex=name_regex)
|
||||
Keyword Parameters/Class attributes:
|
||||
name The name of the policy capability to match.
|
||||
name_regex If true, regular expression matching will
|
||||
be used for matching the name.
|
||||
"""
|
||||
|
||||
def results(self):
|
||||
"""Generator which yields all matching policy capabilities."""
|
||||
self.log.info("Generating results from {0.policy}".format(self))
|
||||
self.log.debug("Name: {0.name_cmp!r}, regex: {0.name_regex}".format(self))
|
||||
self.log.debug("Name: {0.name!r}, regex: {0.name_regex}".format(self))
|
||||
|
||||
for cap in self.policy.polcaps():
|
||||
if self.name and not self._match_name(cap):
|
||||
if not self._match_name(cap):
|
||||
continue
|
||||
|
||||
yield cap
|
||||
|
@ -25,179 +25,122 @@ from .policyrep.netcontext import port_range
|
||||
|
||||
class PortconQuery(contextquery.ContextQuery):
|
||||
|
||||
"""Port context query."""
|
||||
"""
|
||||
Port context query.
|
||||
|
||||
def __init__(self, policy,
|
||||
protocol=None,
|
||||
ports=port_range(None, None), ports_subset=False, ports_overlap=False,
|
||||
ports_superset=False, ports_proper=False,
|
||||
user=None, user_regex=False,
|
||||
role=None, role_regex=False,
|
||||
type_=None, type_regex=False,
|
||||
range_=None, range_overlap=False, range_subset=False,
|
||||
range_superset=False, range_proper=False):
|
||||
"""
|
||||
Parameters:
|
||||
policy The policy to query.
|
||||
Parameter:
|
||||
policy The policy to query.
|
||||
|
||||
Keyword Parameters:
|
||||
protocol The protocol to match (socket.IPPROTO_TCP for
|
||||
TCP or socket.IPPROTO_UDP for UDP)
|
||||
Keyword Parameters/Class attributes:
|
||||
protocol The protocol to match (socket.IPPROTO_TCP for
|
||||
TCP or socket.IPPROTO_UDP for UDP)
|
||||
|
||||
ports A 2-tuple of the port range to match. (Set both to
|
||||
the same value for a single port)
|
||||
ports_subset If true, the criteria will match if it is a subset
|
||||
of the portcon's range.
|
||||
ports_overlap If true, the criteria will match if it overlaps
|
||||
any of the portcon's range.
|
||||
ports_superset If true, the criteria will match if it is a superset
|
||||
of the portcon's range.
|
||||
ports_proper If true, use proper superset/subset operations.
|
||||
No effect if not using set operations.
|
||||
ports A 2-tuple of the port range to match. (Set both to
|
||||
the same value for a single port)
|
||||
ports_subset If true, the criteria will match if it is a subset
|
||||
of the portcon's range.
|
||||
ports_overlap If true, the criteria will match if it overlaps
|
||||
any of the portcon's range.
|
||||
ports_superset If true, the criteria will match if it is a superset
|
||||
of the portcon's range.
|
||||
ports_proper If true, use proper superset/subset operations.
|
||||
No effect if not using set operations.
|
||||
|
||||
user The criteria to match the context's user.
|
||||
user_regex If true, regular expression matching
|
||||
will be used on the user.
|
||||
user The criteria to match the context's user.
|
||||
user_regex If true, regular expression matching
|
||||
will be used on the user.
|
||||
|
||||
role The criteria to match the context's role.
|
||||
role_regex If true, regular expression matching
|
||||
will be used on the role.
|
||||
role The criteria to match the context's role.
|
||||
role_regex If true, regular expression matching
|
||||
will be used on the role.
|
||||
|
||||
type_ The criteria to match the context's type.
|
||||
type_regex If true, regular expression matching
|
||||
will be used on the type.
|
||||
type_ The criteria to match the context's type.
|
||||
type_regex If true, regular expression matching
|
||||
will be used on the type.
|
||||
|
||||
range_ The criteria to match the context's range.
|
||||
range_subset If true, the criteria will match if it is a subset
|
||||
of the context's range.
|
||||
range_overlap If true, the criteria will match if it overlaps
|
||||
any of the context's range.
|
||||
range_superset If true, the criteria will match if it is a superset
|
||||
of the context's range.
|
||||
range_proper If true, use proper superset/subset operations.
|
||||
No effect if not using set operations.
|
||||
"""
|
||||
self.log = logging.getLogger(self.__class__.__name__)
|
||||
range_ The criteria to match the context's range.
|
||||
range_subset If true, the criteria will match if it is a subset
|
||||
of the context's range.
|
||||
range_overlap If true, the criteria will match if it overlaps
|
||||
any of the context's range.
|
||||
range_superset If true, the criteria will match if it is a superset
|
||||
of the context's range.
|
||||
range_proper If true, use proper superset/subset operations.
|
||||
No effect if not using set operations.
|
||||
"""
|
||||
|
||||
self.policy = policy
|
||||
_protocol = None
|
||||
_ports = None
|
||||
ports_subset = False
|
||||
ports_overlap = False
|
||||
ports_superset = False
|
||||
ports_proper = False
|
||||
|
||||
self.set_protocol(protocol)
|
||||
self.set_ports(ports, subset=ports_subset, overlap=ports_overlap,
|
||||
superset=ports_superset, proper=ports_proper)
|
||||
self.set_user(user, regex=user_regex)
|
||||
self.set_role(role, regex=role_regex)
|
||||
self.set_type(type_, regex=type_regex)
|
||||
self.set_range(range_, overlap=range_overlap, subset=range_subset,
|
||||
superset=range_superset, proper=range_proper)
|
||||
@property
|
||||
def ports(self):
|
||||
return self._ports
|
||||
|
||||
@ports.setter
|
||||
def ports(self, value):
|
||||
pending_ports = port_range(*value)
|
||||
|
||||
if all(pending_ports):
|
||||
if pending_ports.low < 1 or pending_ports.high < 1:
|
||||
raise ValueError("Port numbers must be positive: {0.low}-{0.high}".
|
||||
format(pending_ports))
|
||||
|
||||
if pending_ports.low > pending_ports.high:
|
||||
raise ValueError(
|
||||
"The low port must be smaller than the high port: {0.low}-{0.high}".
|
||||
format(pending_ports))
|
||||
|
||||
self._ports = pending_ports
|
||||
else:
|
||||
self._ports = None
|
||||
|
||||
@property
|
||||
def protocol(self):
|
||||
return self._protocol
|
||||
|
||||
@protocol.setter
|
||||
def protocol(self, value):
|
||||
if value:
|
||||
if not (value == IPPROTO_TCP or value == IPPROTO_UDP):
|
||||
raise ValueError(
|
||||
"The protocol must be {0} for TCP or {1} for UDP.".
|
||||
format(IPPROTO_TCP, IPPROTO_UDP))
|
||||
|
||||
self._protocol = value
|
||||
else:
|
||||
self._protocol = None
|
||||
|
||||
def results(self):
|
||||
"""Generator which yields all matching portcons."""
|
||||
self.log.info("Generating results from {0.policy}".format(self))
|
||||
self.log.debug("Ports: {0.ports_cmp}, overlap: {0.ports_overlap}, "
|
||||
self.log.debug("Ports: {0.ports}, overlap: {0.ports_overlap}, "
|
||||
"subset: {0.ports_subset}, superset: {0.ports_superset}, "
|
||||
"proper: {0.ports_proper}".format(self))
|
||||
self.log.debug("User: {0.user_cmp!r}, regex: {0.user_regex}".format(self))
|
||||
self.log.debug("Role: {0.role_cmp!r}, regex: {0.role_regex}".format(self))
|
||||
self.log.debug("Type: {0.type_cmp!r}, regex: {0.type_regex}".format(self))
|
||||
self.log.debug("User: {0.user!r}, regex: {0.user_regex}".format(self))
|
||||
self.log.debug("Role: {0.role!r}, regex: {0.role_regex}".format(self))
|
||||
self.log.debug("Type: {0.type_!r}, regex: {0.type_regex}".format(self))
|
||||
self.log.debug("Range: {0.range_!r}, subset: {0.range_subset}, overlap: {0.range_overlap}, "
|
||||
"superset: {0.range_superset}, proper: {0.range_proper}".format(self))
|
||||
|
||||
for portcon in self.policy.portcons():
|
||||
|
||||
if all(self.ports):
|
||||
if not self._match_range(
|
||||
portcon.ports,
|
||||
self.ports_cmp,
|
||||
self.ports_subset,
|
||||
self.ports_overlap,
|
||||
self.ports_superset,
|
||||
self.ports_proper):
|
||||
continue
|
||||
if self.ports and not self._match_range(
|
||||
portcon.ports,
|
||||
self.ports,
|
||||
self.ports_subset,
|
||||
self.ports_overlap,
|
||||
self.ports_superset,
|
||||
self.ports_proper):
|
||||
continue
|
||||
|
||||
if self.protocol and self.protocol != portcon.protocol:
|
||||
continue
|
||||
|
||||
if not self._match_context(
|
||||
portcon.context,
|
||||
self.user_cmp,
|
||||
self.user_regex,
|
||||
self.role_cmp,
|
||||
self.role_regex,
|
||||
self.type_cmp,
|
||||
self.type_regex,
|
||||
self.range_cmp,
|
||||
self.range_subset,
|
||||
self.range_overlap,
|
||||
self.range_superset,
|
||||
self.range_proper):
|
||||
if not self._match_context(portcon.context):
|
||||
continue
|
||||
|
||||
yield portcon
|
||||
|
||||
def set_ports(self, ports, **opts):
|
||||
"""
|
||||
Set the criteria for matching the port range.
|
||||
|
||||
Parameter:
|
||||
ports A 2-tuple of the port range to match. (Set both to
|
||||
the same value to match a single port)
|
||||
|
||||
Keyword Parameters:
|
||||
subset If true, the criteria will match if it is a subset
|
||||
of the portcon's range.
|
||||
overlap If true, the criteria will match if it overlaps
|
||||
any of the portcon's range.
|
||||
superset If true, the criteria will match if it is a superset
|
||||
of the portcon's range.
|
||||
proper If true, use proper superset/subset operations.
|
||||
No effect if not using set operations.
|
||||
"""
|
||||
|
||||
self.ports = ports
|
||||
|
||||
for k in list(opts.keys()):
|
||||
if k == "subset":
|
||||
self.ports_subset = opts[k]
|
||||
elif k == "overlap":
|
||||
self.ports_overlap = opts[k]
|
||||
elif k == "superset":
|
||||
self.ports_superset = opts[k]
|
||||
elif k == "proper":
|
||||
self.ports_proper = opts[k]
|
||||
else:
|
||||
raise NameError("Invalid name option: {0}".format(k))
|
||||
|
||||
if not all(self.ports):
|
||||
self.ports_cmp = None
|
||||
else:
|
||||
if self.ports[0] < 1 or self.ports[1] < 1:
|
||||
raise ValueError("Port numbers must be positive: {0[0]}-{0[1]}".format(ports))
|
||||
|
||||
if self.ports[0] > self.ports[1]:
|
||||
raise ValueError(
|
||||
"The low port must be smaller than the high port: {0[0]}-{0[1]}".format(ports))
|
||||
|
||||
self.ports_cmp = port_range(*ports)
|
||||
|
||||
def set_protocol(self, protocol):
|
||||
"""
|
||||
Set the criteria for matching the IP protocol.
|
||||
|
||||
Parameter:
|
||||
version The protocol number to match. (socket.IPPROTO_TCP for
|
||||
TCP or socket.IPPROTO_UDP for UDP)
|
||||
|
||||
Exceptions:
|
||||
ValueError Invalid protocol number.
|
||||
"""
|
||||
|
||||
if protocol:
|
||||
if not (protocol == IPPROTO_TCP or protocol == IPPROTO_UDP):
|
||||
raise ValueError(
|
||||
"The protocol must be {0} for TCP or {1} for UDP.".
|
||||
format(IPPROTO_TCP, IPPROTO_UDP))
|
||||
|
||||
self.protocol = protocol
|
||||
|
||||
else:
|
||||
self.protocol = None
|
||||
|
@ -16,11 +16,29 @@
|
||||
# License along with SETools. If not, see
|
||||
# <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
import logging
|
||||
|
||||
|
||||
class PolicyQuery(object):
|
||||
|
||||
"""Abstract base class for SELinux policy queries."""
|
||||
"""Base class for SELinux policy queries."""
|
||||
|
||||
def __init__(self, policy, **kwargs):
|
||||
self.log = logging.getLogger(self.__class__.__name__)
|
||||
|
||||
self.policy = policy
|
||||
|
||||
# keys are sorted in reverse order so regex settings
|
||||
# are set before the criteria, e.g. name_regex
|
||||
# is set before name. This ensures correct behavior
|
||||
# since the criteria descriptors are sensitve to
|
||||
# regex settings.
|
||||
for name in sorted(kwargs.keys(), reverse=True):
|
||||
attr = getattr(self, name, None) # None is not callable
|
||||
if callable(attr):
|
||||
raise ValueError("Keyword parameter {0} conflicts with a callable.".format(name))
|
||||
|
||||
setattr(self, name, kwargs[name])
|
||||
|
||||
@staticmethod
|
||||
def _match_regex(obj, criteria, regex):
|
||||
@ -72,6 +90,24 @@ class PolicyQuery(object):
|
||||
else:
|
||||
return criteria in obj
|
||||
|
||||
@staticmethod
|
||||
def _match_indirect_regex(obj, criteria, indirect, regex):
|
||||
"""
|
||||
Match the object with optional regular expression and indirection.
|
||||
|
||||
Parameters:
|
||||
obj The object to match.
|
||||
criteria The criteria to match.
|
||||
regex If regular expression matching should be used.
|
||||
indirect If object indirection should be used, e.g.
|
||||
expanding an attribute.
|
||||
"""
|
||||
|
||||
if indirect:
|
||||
return PolicyQuery._match_in_set((obj.expand()), criteria, regex)
|
||||
else:
|
||||
return PolicyQuery._match_regex(obj, criteria, regex)
|
||||
|
||||
@staticmethod
|
||||
def _match_regex_or_set(obj, criteria, equal, regex):
|
||||
"""
|
||||
|
@ -19,64 +19,79 @@
|
||||
import logging
|
||||
import re
|
||||
|
||||
from . import mixins, query
|
||||
from .descriptors import CriteriaDescriptor, CriteriaSetDescriptor, RuletypeDescriptor
|
||||
from .policyrep.exception import InvalidType, RuleUseError
|
||||
|
||||
from . import rulequery
|
||||
|
||||
class RBACRuleQuery(mixins.MatchObjClass, query.PolicyQuery):
|
||||
|
||||
class RBACRuleQuery(rulequery.RuleQuery):
|
||||
"""
|
||||
Query the RBAC rules.
|
||||
|
||||
"""Query the RBAC rules."""
|
||||
Parameter:
|
||||
policy The policy to query.
|
||||
|
||||
def __init__(self, policy,
|
||||
ruletype=None,
|
||||
source=None, source_regex=False, source_indirect=True,
|
||||
target=None, target_regex=False, target_indirect=True,
|
||||
tclass=None, tclass_regex=False,
|
||||
default=None, default_regex=False):
|
||||
"""
|
||||
Parameters:
|
||||
policy The policy to query.
|
||||
ruletype The rule type(s) to match.
|
||||
source The name of the source role/attribute to match.
|
||||
source_indirect If true, members of an attribute will be
|
||||
matched rather than the attribute itself.
|
||||
source_regex If true, regular expression matching will
|
||||
be used on the source role/attribute.
|
||||
Obeys the source_indirect option.
|
||||
target The name of the target role/attribute to match.
|
||||
target_indirect If true, members of an attribute will be
|
||||
matched rather than the attribute itself.
|
||||
target_regex If true, regular expression matching will
|
||||
be used on the target role/attribute.
|
||||
Obeys target_indirect option.
|
||||
tclass The object class(es) to match.
|
||||
tclass_regex If true, use a regular expression for
|
||||
matching the rule's object class.
|
||||
default The name of the default role to match.
|
||||
default_regex If true, regular expression matching will
|
||||
be used on the default role.
|
||||
"""
|
||||
self.log = logging.getLogger(self.__class__.__name__)
|
||||
Keyword Parameters/Class attributes:
|
||||
ruletype The list of rule type(s) to match.
|
||||
source The name of the source role/attribute to match.
|
||||
source_indirect If true, members of an attribute will be
|
||||
matched rather than the attribute itself.
|
||||
source_regex If true, regular expression matching will
|
||||
be used on the source role/attribute.
|
||||
Obeys the source_indirect option.
|
||||
target The name of the target role/attribute to match.
|
||||
target_indirect If true, members of an attribute will be
|
||||
matched rather than the attribute itself.
|
||||
target_regex If true, regular expression matching will
|
||||
be used on the target role/attribute.
|
||||
Obeys target_indirect option.
|
||||
tclass The object class(es) to match.
|
||||
tclass_regex If true, use a regular expression for
|
||||
matching the rule's object class.
|
||||
default The name of the default role to match.
|
||||
default_regex If true, regular expression matching will
|
||||
be used on the default role.
|
||||
"""
|
||||
|
||||
self.policy = policy
|
||||
ruletype = RuletypeDescriptor("validate_rbac_ruletype")
|
||||
source = CriteriaDescriptor("source_regex", "lookup_role")
|
||||
source_regex = False
|
||||
source_indirect = True
|
||||
_target = None
|
||||
target_regex = False
|
||||
target_indirect = True
|
||||
tclass = CriteriaSetDescriptor("tclass_regex", "lookup_class")
|
||||
tclass_regex = False
|
||||
default = CriteriaDescriptor("default_regex", "lookup_role")
|
||||
default_regex = False
|
||||
|
||||
self.set_ruletype(ruletype)
|
||||
self.set_source(source, indirect=source_indirect, regex=source_regex)
|
||||
self.set_target(target, indirect=target_indirect, regex=target_regex)
|
||||
self.set_tclass(tclass, regex=tclass_regex)
|
||||
self.set_default(default, regex=default_regex)
|
||||
@property
|
||||
def target(self):
|
||||
return self._target
|
||||
|
||||
@target.setter
|
||||
def target(self, value):
|
||||
if not value:
|
||||
self._target = None
|
||||
elif self.target_regex:
|
||||
self._target = re.compile(value)
|
||||
else:
|
||||
try:
|
||||
self._target = self.policy.lookup_type_or_attr(value)
|
||||
except InvalidType:
|
||||
self._target = self.policy.lookup_role(value)
|
||||
|
||||
def results(self):
|
||||
"""Generator which yields all matching RBAC rules."""
|
||||
self.log.info("Generating results from {0.policy}".format(self))
|
||||
self.log.debug("Ruletypes: {0.ruletype}".format(self))
|
||||
self.log.debug("Source: {0.source_cmp!r}, indirect: {0.source_indirect}, "
|
||||
self.log.debug("Source: {0.source!r}, indirect: {0.source_indirect}, "
|
||||
"regex: {0.source_regex}".format(self))
|
||||
self.log.debug("Target: {0.target_cmp!r}, indirect: {0.target_indirect}, "
|
||||
self.log.debug("Target: {0.target!r}, indirect: {0.target_indirect}, "
|
||||
"regex: {0.target_regex}".format(self))
|
||||
self.log.debug("Class: {0.tclass_cmp!r}, regex: {0.tclass_regex}".format(self))
|
||||
self.log.debug("Default: {0.default_cmp!r}, regex: {0.default_regex}".format(self))
|
||||
self.log.debug("Class: {0.tclass!r}, regex: {0.tclass_regex}".format(self))
|
||||
self.log.debug("Default: {0.default!r}, regex: {0.default_regex}".format(self))
|
||||
|
||||
for rule in self.policy.rbacrules():
|
||||
#
|
||||
@ -91,7 +106,7 @@ class RBACRuleQuery(rulequery.RuleQuery):
|
||||
#
|
||||
if self.source and not self._match_indirect_regex(
|
||||
rule.source,
|
||||
self.source_cmp,
|
||||
self.source,
|
||||
self.source_indirect,
|
||||
self.source_regex):
|
||||
continue
|
||||
@ -101,7 +116,7 @@ class RBACRuleQuery(rulequery.RuleQuery):
|
||||
#
|
||||
if self.target and not self._match_indirect_regex(
|
||||
rule.target,
|
||||
self.target_cmp,
|
||||
self.target,
|
||||
self.target_indirect,
|
||||
self.target_regex):
|
||||
continue
|
||||
@ -109,12 +124,11 @@ class RBACRuleQuery(rulequery.RuleQuery):
|
||||
#
|
||||
# Matching on object class
|
||||
#
|
||||
if self.tclass:
|
||||
try:
|
||||
if not self._match_object_class(rule.tclass):
|
||||
continue
|
||||
except RuleUseError:
|
||||
try:
|
||||
if not self._match_object_class(rule):
|
||||
continue
|
||||
except RuleUseError:
|
||||
continue
|
||||
|
||||
#
|
||||
# Matching on default role
|
||||
@ -123,7 +137,7 @@ class RBACRuleQuery(rulequery.RuleQuery):
|
||||
try:
|
||||
if not self._match_regex(
|
||||
rule.default,
|
||||
self.default_cmp,
|
||||
self.default,
|
||||
self.default_regex):
|
||||
continue
|
||||
except RuleUseError:
|
||||
@ -131,115 +145,3 @@ class RBACRuleQuery(rulequery.RuleQuery):
|
||||
|
||||
# if we get here, we have matched all available criteria
|
||||
yield rule
|
||||
|
||||
def set_ruletype(self, ruletype):
|
||||
"""
|
||||
Set the rule types for the rule query.
|
||||
|
||||
Parameter:
|
||||
ruletype The rule types to match.
|
||||
"""
|
||||
if ruletype:
|
||||
self.policy.validate_rbac_ruletype(ruletype)
|
||||
|
||||
self.ruletype = ruletype
|
||||
|
||||
def set_source(self, source, **opts):
|
||||
"""
|
||||
Set the criteria for the rule's source.
|
||||
|
||||
Parameter:
|
||||
source Name to match the rule's source.
|
||||
|
||||
Keyword Options:
|
||||
indirect If true, members of an attribute will be
|
||||
matched rather than the attribute itself.
|
||||
regex If true, regular expression matching will
|
||||
be used. Obeys the indirect option.
|
||||
|
||||
Exceptions:
|
||||
NameError Invalid keyword option.
|
||||
"""
|
||||
|
||||
self.source = source
|
||||
|
||||
for k in list(opts.keys()):
|
||||
if k == "indirect":
|
||||
self.source_indirect = opts[k]
|
||||
elif k == "regex":
|
||||
self.source_regex = opts[k]
|
||||
else:
|
||||
raise NameError("Invalid source option: {0}".format(k))
|
||||
|
||||
if not self.source:
|
||||
self.source_cmp = None
|
||||
elif self.source_regex:
|
||||
self.source_cmp = re.compile(self.source)
|
||||
else:
|
||||
self.source_cmp = self.policy.lookup_role(self.source)
|
||||
|
||||
def set_target(self, target, **opts):
|
||||
"""
|
||||
Set the criteria for the rule's target.
|
||||
|
||||
Parameter:
|
||||
target Name to match the rule's target.
|
||||
|
||||
Keyword Options:
|
||||
indirect If true, members of an attribute will be
|
||||
matched rather than the attribute itself.
|
||||
regex If true, regular expression matching will
|
||||
be used. Obeys the indirect option.
|
||||
|
||||
Exceptions:
|
||||
NameError Invalid keyword option.
|
||||
"""
|
||||
|
||||
self.target = target
|
||||
|
||||
for k in list(opts.keys()):
|
||||
if k == "indirect":
|
||||
self.target_indirect = opts[k]
|
||||
elif k == "regex":
|
||||
self.target_regex = opts[k]
|
||||
else:
|
||||
raise NameError("Invalid target option: {0}".format(k))
|
||||
|
||||
if not self.target:
|
||||
self.target_cmp = None
|
||||
elif self.target_regex:
|
||||
self.target_cmp = re.compile(self.target)
|
||||
else:
|
||||
try:
|
||||
self.target_cmp = self.policy.lookup_type_or_attr(self.target)
|
||||
except InvalidType:
|
||||
self.target_cmp = self.policy.lookup_role(self.target)
|
||||
|
||||
def set_default(self, default, **opts):
|
||||
"""
|
||||
Set the criteria for the rule's default role.
|
||||
|
||||
Parameter:
|
||||
default Name to match the rule's default role.
|
||||
|
||||
Keyword Options:
|
||||
regex If true, regular expression matching will be used.
|
||||
|
||||
Exceptions:
|
||||
NameError Invalid keyword option.
|
||||
"""
|
||||
|
||||
self.default = default
|
||||
|
||||
for k in list(opts.keys()):
|
||||
if k == "regex":
|
||||
self.default_regex = opts[k]
|
||||
else:
|
||||
raise NameError("Invalid default option: {0}".format(k))
|
||||
|
||||
if not self.default:
|
||||
self.default_cmp = None
|
||||
elif self.default_regex:
|
||||
self.default_cmp = re.compile(self.default)
|
||||
else:
|
||||
self.default_cmp = self.policy.lookup_role(self.default)
|
||||
|
@ -20,41 +20,40 @@ import logging
|
||||
import re
|
||||
|
||||
from . import compquery
|
||||
from .descriptors import CriteriaSetDescriptor
|
||||
|
||||
|
||||
class RoleQuery(compquery.ComponentQuery):
|
||||
|
||||
"""Query SELinux policy roles."""
|
||||
"""
|
||||
Query SELinux policy roles.
|
||||
|
||||
def __init__(self, policy,
|
||||
name=None, name_regex=False,
|
||||
types=None, types_equal=False, types_regex=False):
|
||||
"""
|
||||
Parameter:
|
||||
policy The policy to query.
|
||||
name The role name to match.
|
||||
name_regex If true, regular expression matching
|
||||
will be used on the role names.
|
||||
types The type to match.
|
||||
types_equal If true, only roles with type sets
|
||||
that are equal to the criteria will
|
||||
match. Otherwise, any intersection
|
||||
will match.
|
||||
types_regex If true, regular expression matching
|
||||
will be used on the type names instead
|
||||
of set logic.
|
||||
"""
|
||||
self.log = logging.getLogger(self.__class__.__name__)
|
||||
Parameter:
|
||||
policy The policy to query.
|
||||
|
||||
self.policy = policy
|
||||
self.set_name(name, regex=name_regex)
|
||||
self.set_types(types, regex=types_regex, equal=types_equal)
|
||||
Keyword Parameters/Class attributes:
|
||||
name The role name to match.
|
||||
name_regex If true, regular expression matching
|
||||
will be used on the role names.
|
||||
types The type to match.
|
||||
types_equal If true, only roles with type sets
|
||||
that are equal to the criteria will
|
||||
match. Otherwise, any intersection
|
||||
will match.
|
||||
types_regex If true, regular expression matching
|
||||
will be used on the type names instead
|
||||
of set logic.
|
||||
"""
|
||||
|
||||
types = CriteriaSetDescriptor("types_regex", "lookup_type")
|
||||
types_equal = False
|
||||
types_regex = False
|
||||
|
||||
def results(self):
|
||||
"""Generator which yields all matching roles."""
|
||||
self.log.info("Generating results from {0.policy}".format(self))
|
||||
self.log.debug("Name: {0.name_cmp!r}, regex: {0.name_regex}".format(self))
|
||||
self.log.debug("Types: {0.types_cmp!r}, regex: {0.types_regex}, "
|
||||
self.log.debug("Name: {0.name!r}, regex: {0.name_regex}".format(self))
|
||||
self.log.debug("Types: {0.types!r}, regex: {0.types_regex}, "
|
||||
"eq: {0.types_equal}".format(self))
|
||||
|
||||
for r in self.policy.roles():
|
||||
@ -65,50 +64,14 @@ class RoleQuery(compquery.ComponentQuery):
|
||||
# will confuse, especially for set equality type queries.
|
||||
continue
|
||||
|
||||
if self.name and not self._match_name(r):
|
||||
if not self._match_name(r):
|
||||
continue
|
||||
|
||||
if self.types and not self._match_regex_or_set(
|
||||
set(r.types()),
|
||||
self.types_cmp,
|
||||
self.types,
|
||||
self.types_equal,
|
||||
self.types_regex):
|
||||
continue
|
||||
|
||||
yield r
|
||||
|
||||
def set_types(self, types, **opts):
|
||||
"""
|
||||
Set the criteria for the role's types.
|
||||
|
||||
Parameter:
|
||||
types Name to match the role's types.
|
||||
|
||||
Keyword Options:
|
||||
regex If true, regular expression matching will be used
|
||||
instead of set logic.
|
||||
equal If true, the type set of the role
|
||||
must equal the attributes criteria to
|
||||
match. If false, any intersection in the
|
||||
critera will cause a rule match.
|
||||
|
||||
Exceptions:
|
||||
NameError Invalid keyword option.
|
||||
"""
|
||||
|
||||
self.types = types
|
||||
|
||||
for k in list(opts.keys()):
|
||||
if k == "regex":
|
||||
self.types_regex = opts[k]
|
||||
elif k == "equal":
|
||||
self.types_equal = opts[k]
|
||||
else:
|
||||
raise NameError("Invalid types option: {0}".format(k))
|
||||
|
||||
if not self.types:
|
||||
self.types_cmp = None
|
||||
elif self.types_regex:
|
||||
self.types_cmp = re.compile(self.types)
|
||||
else:
|
||||
self.types_cmp = set(self.policy.lookup_type(t) for t in self.types)
|
||||
|
@ -1,126 +0,0 @@
|
||||
# Copyright 2014-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/>.
|
||||
#
|
||||
# pylint: disable=no-member,attribute-defined-outside-init,abstract-method
|
||||
import re
|
||||
|
||||
from . import mixins
|
||||
from .query import PolicyQuery
|
||||
|
||||
|
||||
class RuleQuery(mixins.MatchObjClass, PolicyQuery):
|
||||
|
||||
"""Abstract base class for rule queries."""
|
||||
|
||||
@staticmethod
|
||||
def _match_indirect_regex(obj, criteria, indirect, regex):
|
||||
"""
|
||||
Match the object with optional regular expression and indirection.
|
||||
|
||||
Parameters:
|
||||
obj The object to match.
|
||||
criteria The criteria to match.
|
||||
regex If regular expression matching should be used.
|
||||
indirect If object indirection should be used, e.g.
|
||||
expanding an attribute.
|
||||
"""
|
||||
|
||||
if indirect:
|
||||
return PolicyQuery._match_in_set(
|
||||
(obj.expand()),
|
||||
criteria,
|
||||
regex)
|
||||
else:
|
||||
return PolicyQuery._match_regex(
|
||||
obj,
|
||||
criteria,
|
||||
regex)
|
||||
|
||||
def set_ruletype(self, ruletype):
|
||||
raise NotImplementedError
|
||||
|
||||
def set_source(self, source, **opts):
|
||||
"""
|
||||
Set the criteria for the rule's source.
|
||||
|
||||
Parameter:
|
||||
source Name to match the rule's source.
|
||||
|
||||
Keyword Options:
|
||||
indirect If true, members of an attribute will be
|
||||
matched rather than the attribute itself.
|
||||
regex If true, regular expression matching will
|
||||
be used. Obeys the indirect option.
|
||||
|
||||
Exceptions:
|
||||
NameError Invalid keyword option.
|
||||
"""
|
||||
|
||||
self.source = source
|
||||
|
||||
for k in list(opts.keys()):
|
||||
if k == "indirect":
|
||||
self.source_indirect = opts[k]
|
||||
elif k == "regex":
|
||||
self.source_regex = opts[k]
|
||||
else:
|
||||
raise NameError("Invalid source option: {0}".format(k))
|
||||
|
||||
if not self.source:
|
||||
self.source_cmp = None
|
||||
elif self.source_regex:
|
||||
self.source_cmp = re.compile(self.source)
|
||||
else:
|
||||
self.source_cmp = self.policy.lookup_type_or_attr(self.source)
|
||||
|
||||
def set_target(self, target, **opts):
|
||||
"""
|
||||
Set the criteria for the rule's target.
|
||||
|
||||
Parameter:
|
||||
target Name to match the rule's target.
|
||||
|
||||
Keyword Options:
|
||||
indirect If true, members of an attribute will be
|
||||
matched rather than the attribute itself.
|
||||
regex If true, regular expression matching will
|
||||
be used. Obeys the indirect option.
|
||||
|
||||
Exceptions:
|
||||
NameError Invalid keyword option.
|
||||
"""
|
||||
|
||||
self.target = target
|
||||
|
||||
for k in list(opts.keys()):
|
||||
if k == "indirect":
|
||||
self.target_indirect = opts[k]
|
||||
elif k == "regex":
|
||||
self.target_regex = opts[k]
|
||||
else:
|
||||
raise NameError("Invalid target option: {0}".format(k))
|
||||
|
||||
if not self.target:
|
||||
self.target_cmp = None
|
||||
elif self.target_regex:
|
||||
self.target_cmp = re.compile(self.target)
|
||||
else:
|
||||
self.target_cmp = self.policy.lookup_type_or_attr(self.target)
|
||||
|
||||
def set_default(self, default, **opts):
|
||||
raise NotImplementedError
|
@ -20,49 +20,47 @@ import logging
|
||||
|
||||
from . import compquery
|
||||
from . import mixins
|
||||
from .descriptors import CriteriaDescriptor
|
||||
|
||||
|
||||
class SensitivityQuery(mixins.MatchAlias, compquery.ComponentQuery):
|
||||
|
||||
"""Query MLS Sensitivities"""
|
||||
"""
|
||||
Query MLS Sensitivities
|
||||
|
||||
def __init__(self, policy,
|
||||
name=None, name_regex=False,
|
||||
alias=None, alias_regex=False,
|
||||
sens=None, sens_dom=False, sens_domby=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.
|
||||
sens The criteria to match the sensitivity by dominance.
|
||||
sens_dom If true, the criteria will match if it dominates
|
||||
the sensitivity.
|
||||
sens_domby If true, the criteria will match if it is dominated
|
||||
by the sensitivity.
|
||||
"""
|
||||
self.log = logging.getLogger(self.__class__.__name__)
|
||||
Parameter:
|
||||
policy The policy to query.
|
||||
|
||||
self.policy = policy
|
||||
self.set_name(name, regex=name_regex)
|
||||
self.set_alias(alias, regex=alias_regex)
|
||||
self.set_sensitivity(sens, dom=sens_dom, domby=sens_domby)
|
||||
Keyword Parameters/Class attributes:
|
||||
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.
|
||||
sens The criteria to match the sensitivity by dominance.
|
||||
sens_dom If true, the criteria will match if it dominates
|
||||
the sensitivity.
|
||||
sens_domby If true, the criteria will match if it is dominated
|
||||
by the sensitivity.
|
||||
"""
|
||||
|
||||
sens = CriteriaDescriptor(lookup_function="lookup_sensitivity")
|
||||
sens_dom = False
|
||||
sens_domby = False
|
||||
|
||||
def results(self):
|
||||
"""Generator which yields all matching sensitivities."""
|
||||
self.log.info("Generating results from {0.policy}".format(self))
|
||||
self.log.debug("Name: {0.name_cmp!r}, regex: {0.name_regex}".format(self))
|
||||
self.log.debug("Alias: {0.alias_cmp}, regex: {0.alias_regex}".format(self))
|
||||
self.log.debug("Name: {0.name!r}, regex: {0.name_regex}".format(self))
|
||||
self.log.debug("Alias: {0.alias}, regex: {0.alias_regex}".format(self))
|
||||
self.log.debug("Sens: {0.sens!r}, dom: {0.sens_dom}, domby: {0.sens_domby}".format(self))
|
||||
|
||||
for s in self.policy.sensitivities():
|
||||
if self.name and not self._match_name(s):
|
||||
if not self._match_name(s):
|
||||
continue
|
||||
|
||||
if self.alias and not self._match_alias(s.aliases()):
|
||||
if not self._match_alias(s):
|
||||
continue
|
||||
|
||||
if self.sens and not self._match_level(
|
||||
@ -74,33 +72,3 @@ class SensitivityQuery(mixins.MatchAlias, compquery.ComponentQuery):
|
||||
continue
|
||||
|
||||
yield s
|
||||
|
||||
def set_sensitivity(self, sens, **opts):
|
||||
"""
|
||||
Set the criteria for matching the sensitivity by dominance.
|
||||
|
||||
Parameter:
|
||||
sens Criteria to match the sensitivity.
|
||||
|
||||
Keyword Parameters:
|
||||
dom If true, the criteria will match if it
|
||||
dominates the sensitivity.
|
||||
domby If true, the criteria will match if it
|
||||
is dominated by the sensitivity.
|
||||
|
||||
Exceptions:
|
||||
NameError Invalid keyword option.
|
||||
"""
|
||||
|
||||
if sens:
|
||||
self.sens = self.policy.lookup_sensitivity(sens)
|
||||
else:
|
||||
self.sens = None
|
||||
|
||||
for k in list(opts.keys()):
|
||||
if k == "dom":
|
||||
self.sens_dom = opts[k]
|
||||
elif k == "domby":
|
||||
self.sens_domby = opts[k]
|
||||
else:
|
||||
raise NameError("Invalid name option: {0}".format(k))
|
||||
|
@ -19,75 +19,89 @@
|
||||
import logging
|
||||
import re
|
||||
|
||||
from . import mixins, query
|
||||
from .descriptors import CriteriaDescriptor, CriteriaSetDescriptor, RuletypeDescriptor
|
||||
from .policyrep.exception import RuleUseError, RuleNotConditional
|
||||
from . import mixins
|
||||
from . import rulequery
|
||||
|
||||
|
||||
class TERuleQuery(mixins.MatchPermission, rulequery.RuleQuery):
|
||||
class TERuleQuery(mixins.MatchObjClass, mixins.MatchPermission, query.PolicyQuery):
|
||||
|
||||
"""Query the Type Enforcement rules."""
|
||||
"""
|
||||
Query the Type Enforcement rules.
|
||||
|
||||
def __init__(self, policy,
|
||||
ruletype=None,
|
||||
source=None, source_regex=False, source_indirect=True,
|
||||
target=None, target_regex=False, target_indirect=True,
|
||||
tclass=None, tclass_regex=False,
|
||||
perms=None, perms_equal=False,
|
||||
default=None, default_regex=False,
|
||||
boolean=None, boolean_regex=False, boolean_equal=False):
|
||||
"""
|
||||
Parameter:
|
||||
policy The policy to query.
|
||||
ruletype The rule type(s) to match.
|
||||
source The name of the source type/attribute to match.
|
||||
source_indirect If true, members of an attribute will be
|
||||
matched rather than the attribute itself.
|
||||
source_regex If true, regular expression matching will
|
||||
be used on the source type/attribute.
|
||||
Obeys the source_indirect option.
|
||||
target The name of the target type/attribute to match.
|
||||
target_indirect If true, members of an attribute will be
|
||||
matched rather than the attribute itself.
|
||||
target_regex If true, regular expression matching will
|
||||
be used on the target type/attribute.
|
||||
Obeys target_indirect option.
|
||||
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.
|
||||
default The name of the default type to match.
|
||||
default_regex If true, regular expression matching will be
|
||||
used on the default type.
|
||||
"""
|
||||
self.log = logging.getLogger(self.__class__.__name__)
|
||||
Parameter:
|
||||
policy The policy to query.
|
||||
|
||||
self.policy = policy
|
||||
Keyword Parameters/Class attributes:
|
||||
ruletype The list of rule type(s) to match.
|
||||
source The name of the source type/attribute to match.
|
||||
source_indirect If true, members of an attribute will be
|
||||
matched rather than the attribute itself.
|
||||
Default is true.
|
||||
source_regex If true, regular expression matching will
|
||||
be used on the source type/attribute.
|
||||
Obeys the source_indirect option.
|
||||
Default is false.
|
||||
target The name of the target type/attribute to match.
|
||||
target_indirect If true, members of an attribute will be
|
||||
matched rather than the attribute itself.
|
||||
Default is true.
|
||||
target_regex If true, regular expression matching will
|
||||
be used on the target type/attribute.
|
||||
Obeys target_indirect option.
|
||||
Default is false.
|
||||
tclass The object class(es) to match.
|
||||
tclass_regex If true, use a regular expression for
|
||||
matching the rule's object class.
|
||||
Default is false.
|
||||
perms The set of 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.
|
||||
Default is false.
|
||||
perms_regex If true, regular expression matching will be used
|
||||
on the permission names instead of set logic.
|
||||
default The name of the default type to match.
|
||||
default_regex If true, regular expression matching will be
|
||||
used on the default type.
|
||||
Default is false.
|
||||
boolean The set of boolean(s) to match.
|
||||
boolean_regex If true, regular expression matching will be
|
||||
used on the booleans.
|
||||
Default is false.
|
||||
boolean_equal If true, the booleans in the conditional
|
||||
expression of the rule must exactly match the
|
||||
criteria. If false, any set intersection
|
||||
will match. Default is false.
|
||||
"""
|
||||
|
||||
self.set_ruletype(ruletype)
|
||||
self.set_source(source, indirect=source_indirect, regex=source_regex)
|
||||
self.set_target(target, indirect=target_indirect, regex=target_regex)
|
||||
self.set_tclass(tclass, regex=tclass_regex)
|
||||
self.set_perms(perms, equal=perms_equal)
|
||||
self.set_default(default, regex=default_regex)
|
||||
self.set_boolean(boolean, regex=boolean_regex, equal=boolean_equal)
|
||||
ruletype = RuletypeDescriptor("validate_te_ruletype")
|
||||
source = CriteriaDescriptor("source_regex", "lookup_type_or_attr")
|
||||
source_regex = False
|
||||
source_indirect = True
|
||||
target = CriteriaDescriptor("target_regex", "lookup_type_or_attr")
|
||||
target_regex = False
|
||||
target_indirect = True
|
||||
default = CriteriaDescriptor("default_regex", "lookup_type")
|
||||
default_regex = False
|
||||
boolean = CriteriaSetDescriptor("boolean_regex", "lookup_boolean")
|
||||
boolean_regex = False
|
||||
boolean_equal = False
|
||||
|
||||
def results(self):
|
||||
"""Generator which yields all matching TE rules."""
|
||||
self.log.info("Generating results from {0.policy}".format(self))
|
||||
self.log.debug("Ruletypes: {0.ruletype}".format(self))
|
||||
self.log.debug("Source: {0.source_cmp!r}, indirect: {0.source_indirect}, "
|
||||
self.log.debug("Source: {0.source!r}, indirect: {0.source_indirect}, "
|
||||
"regex: {0.source_regex}".format(self))
|
||||
self.log.debug("Target: {0.target_cmp!r}, indirect: {0.target_indirect}, "
|
||||
self.log.debug("Target: {0.target!r}, indirect: {0.target_indirect}, "
|
||||
"regex: {0.target_regex}".format(self))
|
||||
self.log.debug("Class: {0.tclass_cmp!r}, regex: {0.tclass_regex}".format(self))
|
||||
self.log.debug("Perms: {0.perms_cmp}, eq: {0.perms_equal}".format(self))
|
||||
self.log.debug("Default: {0.default_cmp!r}, regex: {0.default_regex}".format(self))
|
||||
self.log.debug("Boolean: {0.boolean_cmp!r}, eq: {0.boolean_equal}, "
|
||||
self.log.debug("Class: {0.tclass!r}, regex: {0.tclass_regex}".format(self))
|
||||
self.log.debug("Perms: {0.perms!r}, regex: {0.perms_regex}, eq: {0.perms_equal}".
|
||||
format(self))
|
||||
self.log.debug("Default: {0.default!r}, regex: {0.default_regex}".format(self))
|
||||
self.log.debug("Boolean: {0.boolean!r}, eq: {0.boolean_equal}, "
|
||||
"regex: {0.boolean_regex}".format(self))
|
||||
|
||||
for rule in self.policy.terules():
|
||||
@ -103,7 +117,7 @@ class TERuleQuery(mixins.MatchPermission, rulequery.RuleQuery):
|
||||
#
|
||||
if self.source and not self._match_indirect_regex(
|
||||
rule.source,
|
||||
self.source_cmp,
|
||||
self.source,
|
||||
self.source_indirect,
|
||||
self.source_regex):
|
||||
continue
|
||||
@ -113,7 +127,7 @@ class TERuleQuery(mixins.MatchPermission, rulequery.RuleQuery):
|
||||
#
|
||||
if self.target and not self._match_indirect_regex(
|
||||
rule.target,
|
||||
self.target_cmp,
|
||||
self.target,
|
||||
self.target_indirect,
|
||||
self.target_regex):
|
||||
continue
|
||||
@ -121,18 +135,17 @@ class TERuleQuery(mixins.MatchPermission, rulequery.RuleQuery):
|
||||
#
|
||||
# Matching on object class
|
||||
#
|
||||
if self.tclass and not self._match_object_class(rule.tclass):
|
||||
if not self._match_object_class(rule):
|
||||
continue
|
||||
|
||||
#
|
||||
# Matching on permission set
|
||||
#
|
||||
if self.perms:
|
||||
try:
|
||||
if not self._match_perms(rule.perms):
|
||||
continue
|
||||
except RuleUseError:
|
||||
try:
|
||||
if not self._match_perms(rule):
|
||||
continue
|
||||
except RuleUseError:
|
||||
continue
|
||||
|
||||
#
|
||||
# Matching on default type
|
||||
@ -141,7 +154,7 @@ class TERuleQuery(mixins.MatchPermission, rulequery.RuleQuery):
|
||||
try:
|
||||
if not self._match_regex(
|
||||
rule.default,
|
||||
self.default_cmp,
|
||||
self.default,
|
||||
self.default_regex):
|
||||
continue
|
||||
except RuleUseError:
|
||||
@ -154,7 +167,7 @@ class TERuleQuery(mixins.MatchPermission, rulequery.RuleQuery):
|
||||
try:
|
||||
if not self._match_regex_or_set(
|
||||
rule.conditional.booleans,
|
||||
self.boolean_cmp,
|
||||
self.boolean,
|
||||
self.boolean_equal,
|
||||
self.boolean_regex):
|
||||
continue
|
||||
@ -163,76 +176,3 @@ class TERuleQuery(mixins.MatchPermission, rulequery.RuleQuery):
|
||||
|
||||
# if we get here, we have matched all available criteria
|
||||
yield rule
|
||||
|
||||
def set_boolean(self, boolean, **opts):
|
||||
"""
|
||||
Set the Boolean for the TE rule query.
|
||||
|
||||
Parameter:
|
||||
boolean The Boolean names to match in the TE rule
|
||||
conditional expression.
|
||||
|
||||
Options:
|
||||
regex If true, regular expression matching will be used.
|
||||
|
||||
Exceptions:
|
||||
NameError Invalid permission set keyword option.
|
||||
"""
|
||||
|
||||
self.boolean = boolean
|
||||
|
||||
for k in list(opts.keys()):
|
||||
if k == "regex":
|
||||
self.boolean_regex = opts[k]
|
||||
elif k == "equal":
|
||||
self.boolean_equal = opts[k]
|
||||
else:
|
||||
raise NameError("Invalid permission set option: {0}".format(k))
|
||||
|
||||
if not self.boolean:
|
||||
self.boolean_cmp = None
|
||||
elif self.boolean_regex:
|
||||
self.boolean_cmp = re.compile(self.boolean)
|
||||
else:
|
||||
self.boolean_cmp = set(self.policy.lookup_boolean(b) for b in self.boolean)
|
||||
|
||||
def set_ruletype(self, ruletype):
|
||||
"""
|
||||
Set the rule types for the rule query.
|
||||
|
||||
Parameter:
|
||||
ruletype The rule types to match.
|
||||
"""
|
||||
if ruletype:
|
||||
self.policy.validate_te_ruletype(ruletype)
|
||||
|
||||
self.ruletype = ruletype
|
||||
|
||||
def set_default(self, default, **opts):
|
||||
"""
|
||||
Set the criteria for the rule's default type.
|
||||
|
||||
Parameter:
|
||||
default Name to match the rule's default type.
|
||||
|
||||
Keyword Options:
|
||||
regex If true, regular expression matching will be used.
|
||||
|
||||
Exceptions:
|
||||
NameError Invalid keyword option.
|
||||
"""
|
||||
|
||||
self.default = default
|
||||
|
||||
for k in list(opts.keys()):
|
||||
if k == "regex":
|
||||
self.default_regex = opts[k]
|
||||
else:
|
||||
raise NameError("Invalid default option: {0}".format(k))
|
||||
|
||||
if not self.default:
|
||||
self.default_cmp = None
|
||||
elif self.default_regex:
|
||||
self.default_cmp = re.compile(self.default)
|
||||
else:
|
||||
self.default_cmp = self.policy.lookup_type(self.default)
|
||||
|
@ -20,88 +20,51 @@ import logging
|
||||
import re
|
||||
|
||||
from . import compquery
|
||||
from .descriptors import CriteriaSetDescriptor
|
||||
|
||||
|
||||
class TypeAttributeQuery(compquery.ComponentQuery):
|
||||
|
||||
"""Query SELinux policy type attributes."""
|
||||
"""
|
||||
Query SELinux policy type attributes.
|
||||
|
||||
def __init__(self, policy,
|
||||
name=None, name_regex=False,
|
||||
types=None, types_equal=False, types_regex=False):
|
||||
"""
|
||||
Parameter:
|
||||
policy The policy to query.
|
||||
name The type name to match.
|
||||
name_regex If true, regular expression matching
|
||||
will be used on the type names.
|
||||
types The type to match.
|
||||
types_equal If true, only attributes with type sets
|
||||
that are equal to the criteria will
|
||||
match. Otherwise, any intersection
|
||||
will match.
|
||||
types_regex If true, regular expression matching
|
||||
will be used on the type names instead
|
||||
of set logic.
|
||||
"""
|
||||
self.log = logging.getLogger(self.__class__.__name__)
|
||||
Parameter:
|
||||
policy The policy to query.
|
||||
|
||||
self.policy = policy
|
||||
self.set_name(name, regex=name_regex)
|
||||
self.set_types(types, regex=types_regex, equal=types_equal)
|
||||
Keyword Parameters/Class attributes:
|
||||
name The type name to match.
|
||||
name_regex If true, regular expression matching
|
||||
will be used on the type names.
|
||||
types The type to match.
|
||||
types_equal If true, only attributes with type sets
|
||||
that are equal to the criteria will
|
||||
match. Otherwise, any intersection
|
||||
will match.
|
||||
types_regex If true, regular expression matching
|
||||
will be used on the type names instead
|
||||
of set logic.
|
||||
"""
|
||||
|
||||
types = CriteriaSetDescriptor("types_regex", "lookup_type")
|
||||
types_equal = False
|
||||
types_regex = False
|
||||
|
||||
def results(self):
|
||||
"""Generator which yields all matching types."""
|
||||
self.log.info("Generating results from {0.policy}".format(self))
|
||||
self.log.debug("Name: {0.name_cmp!r}, regex: {0.name_regex}".format(self))
|
||||
self.log.debug("Types: {0.types_cmp!r}, regex: {0.types_regex}, "
|
||||
self.log.debug("Name: {0.name!r}, regex: {0.name_regex}".format(self))
|
||||
self.log.debug("Types: {0.types!r}, regex: {0.types_regex}, "
|
||||
"eq: {0.types_equal}".format(self))
|
||||
|
||||
for attr in self.policy.typeattributes():
|
||||
if self.name and not self._match_name(attr):
|
||||
if not self._match_name(attr):
|
||||
continue
|
||||
|
||||
if self.types and not self._match_regex_or_set(
|
||||
set(attr.expand()),
|
||||
self.types_cmp,
|
||||
self.types,
|
||||
self.types_equal,
|
||||
self.types_regex):
|
||||
continue
|
||||
|
||||
yield attr
|
||||
|
||||
def set_types(self, types, **opts):
|
||||
"""
|
||||
Set the criteria for the attribute's types.
|
||||
|
||||
Parameter:
|
||||
alias Name to match the component's types.
|
||||
|
||||
Keyword Options:
|
||||
regex If true, regular expression matching will be used
|
||||
instead of set logic.
|
||||
equal If true, the type set of the attribute
|
||||
must equal the type criteria to
|
||||
match. If false, any intersection in the
|
||||
critera will cause a rule match.
|
||||
|
||||
Exceptions:
|
||||
NameError Invalid keyword option.
|
||||
"""
|
||||
|
||||
self.types = types
|
||||
|
||||
for k in list(opts.keys()):
|
||||
if k == "regex":
|
||||
self.types_regex = opts[k]
|
||||
elif k == "equal":
|
||||
self.types_equal = opts[k]
|
||||
else:
|
||||
raise NameError("Invalid types option: {0}".format(k))
|
||||
|
||||
if not self.types:
|
||||
self.types_cmp = None
|
||||
elif self.types_regex:
|
||||
self.types_cmp = re.compile(self.types)
|
||||
else:
|
||||
self.types_cmp = set(self.policy.lookup_type(t) for t in self.types)
|
||||
|
@ -21,126 +21,76 @@ import re
|
||||
|
||||
from . import compquery
|
||||
from . import mixins
|
||||
from .descriptors import CriteriaSetDescriptor
|
||||
|
||||
|
||||
class TypeQuery(mixins.MatchAlias, compquery.ComponentQuery):
|
||||
|
||||
"""Query SELinux policy types."""
|
||||
"""
|
||||
Query SELinux policy types.
|
||||
|
||||
def __init__(self, policy,
|
||||
name=None, name_regex=False,
|
||||
alias=None, alias_regex=False,
|
||||
attrs=None, attrs_equal=False, attrs_regex=False,
|
||||
permissive=False, match_permissive=False):
|
||||
"""
|
||||
Parameter:
|
||||
policy The policy to query.
|
||||
name The type name to match.
|
||||
name_regex If true, regular expression matching
|
||||
will be used on the type names.
|
||||
alias The alias name to match.
|
||||
alias_regex If true, regular expression matching
|
||||
will be used on the alias names.
|
||||
attrs The attribute to match.
|
||||
attrs_equal If true, only types with attribute sets
|
||||
that are equal to the criteria will
|
||||
match. Otherwise, any intersection
|
||||
will match.
|
||||
attrs_regex If true, regular expression matching
|
||||
will be used on the attribute names instead
|
||||
of set logic.
|
||||
match_permissive If true, the permissive state will be matched.
|
||||
permissive The permissive state to match.
|
||||
"""
|
||||
self.log = logging.getLogger(self.__class__.__name__)
|
||||
Parameter:
|
||||
policy The policy to query.
|
||||
|
||||
self.policy = policy
|
||||
self.set_name(name, regex=name_regex)
|
||||
self.set_alias(alias, regex=alias_regex)
|
||||
self.set_attrs(attrs, regex=attrs_regex, equal=attrs_equal)
|
||||
self.set_permissive(match_permissive, permissive=permissive)
|
||||
Keyword Parameters/Class attributes:
|
||||
name The type name to match.
|
||||
name_regex If true, regular expression matching
|
||||
will be used on the type names.
|
||||
alias The alias name to match.
|
||||
alias_regex If true, regular expression matching
|
||||
will be used on the alias names.
|
||||
attrs The attribute to match.
|
||||
attrs_equal If true, only types with attribute sets
|
||||
that are equal to the criteria will
|
||||
match. Otherwise, any intersection
|
||||
will match.
|
||||
attrs_regex If true, regular expression matching
|
||||
will be used on the attribute names instead
|
||||
of set logic.
|
||||
permissive The permissive state to match. If this
|
||||
is None, the state is not matched.
|
||||
"""
|
||||
|
||||
attrs = CriteriaSetDescriptor("attrs_regex", "lookup_typeattr")
|
||||
attrs_regex = False
|
||||
attrs_equal = False
|
||||
_permissive = None
|
||||
|
||||
@property
|
||||
def permissive(self):
|
||||
return self._permissive
|
||||
|
||||
@permissive.setter
|
||||
def permissive(self, value):
|
||||
if value is None:
|
||||
self._permissive = None
|
||||
else:
|
||||
self._permissive = bool(value)
|
||||
|
||||
def results(self):
|
||||
"""Generator which yields all matching types."""
|
||||
self.log.info("Generating results from {0.policy}".format(self))
|
||||
self.log.debug("Name: {0.name_cmp!r}, regex: {0.name_regex}".format(self))
|
||||
self.log.debug("Alias: {0.alias_cmp}, regex: {0.alias_regex}".format(self))
|
||||
self.log.debug("Attrs: {0.attrs_cmp!r}, regex: {0.attrs_regex}, "
|
||||
self.log.debug("Name: {0.name!r}, regex: {0.name_regex}".format(self))
|
||||
self.log.debug("Alias: {0.alias}, regex: {0.alias_regex}".format(self))
|
||||
self.log.debug("Attrs: {0.attrs!r}, regex: {0.attrs_regex}, "
|
||||
"eq: {0.attrs_equal}".format(self))
|
||||
self.log.debug("Permissive: {0.match_permissive}, state: {0.permissive}".format(self))
|
||||
self.log.debug("Permissive: {0.permissive}".format(self))
|
||||
|
||||
for t in self.policy.types():
|
||||
if self.name and not self._match_name(t):
|
||||
if not self._match_name(t):
|
||||
continue
|
||||
|
||||
if self.alias and not self._match_alias(t.aliases()):
|
||||
if not self._match_alias(t):
|
||||
continue
|
||||
|
||||
if self.attrs and not self._match_regex_or_set(
|
||||
set(t.attributes()),
|
||||
self.attrs_cmp,
|
||||
self.attrs,
|
||||
self.attrs_equal,
|
||||
self.attrs_regex):
|
||||
continue
|
||||
|
||||
if self.match_permissive and t.ispermissive != self.permissive:
|
||||
if self.permissive is not None and t.ispermissive != self.permissive:
|
||||
continue
|
||||
|
||||
yield t
|
||||
|
||||
def set_attrs(self, attrs, **opts):
|
||||
"""
|
||||
Set the criteria for the type's attributes.
|
||||
|
||||
Parameter:
|
||||
alias Name to match the component's attributes.
|
||||
|
||||
Keyword Options:
|
||||
regex If true, regular expression matching will be used
|
||||
instead of set logic.
|
||||
equal If true, the attribute set of the type
|
||||
must equal the attributes criteria to
|
||||
match. If false, any intersection in the
|
||||
critera will cause a rule match.
|
||||
|
||||
Exceptions:
|
||||
NameError Invalid keyword option.
|
||||
"""
|
||||
|
||||
self.attrs = attrs
|
||||
|
||||
for k in list(opts.keys()):
|
||||
if k == "regex":
|
||||
self.attrs_regex = opts[k]
|
||||
elif k == "equal":
|
||||
self.attrs_equal = opts[k]
|
||||
else:
|
||||
raise NameError("Invalid alias option: {0}".format(k))
|
||||
|
||||
if not self.attrs:
|
||||
self.attrs_cmp = None
|
||||
elif self.attrs_regex:
|
||||
self.attrs_cmp = re.compile(self.attrs)
|
||||
else:
|
||||
self.attrs_cmp = set(self.policy.lookup_typeattr(a) for a in self.attrs)
|
||||
|
||||
def set_permissive(self, match, **opts):
|
||||
"""
|
||||
Set if the permissive state should be matched.
|
||||
|
||||
Parameter:
|
||||
match If true, the permissive state will be matched.
|
||||
permissive If true, permissive types will match, otherwise
|
||||
enforcing types will match.
|
||||
|
||||
Exceptions:
|
||||
NameError Invalid keyword option.
|
||||
"""
|
||||
|
||||
self.match_permissive = bool(match)
|
||||
|
||||
for k in list(opts.keys()):
|
||||
if k == "permissive":
|
||||
self.permissive = bool(opts[k])
|
||||
else:
|
||||
raise NameError("Invalid permissive option: {0}".format(k))
|
||||
|
@ -20,63 +20,65 @@ import logging
|
||||
import re
|
||||
|
||||
from . import compquery
|
||||
from .descriptors import CriteriaDescriptor, CriteriaSetDescriptor
|
||||
|
||||
|
||||
class UserQuery(compquery.ComponentQuery):
|
||||
|
||||
"""Query SELinux policy users."""
|
||||
"""
|
||||
Query SELinux policy users.
|
||||
|
||||
def __init__(self, policy,
|
||||
name=None, name_regex=False,
|
||||
roles=None, roles_equal=False, roles_regex=False,
|
||||
level=None, level_dom=False, level_domby=False, level_incomp=False,
|
||||
range_=None, 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.
|
||||
level The criteria to match the user's default level.
|
||||
level_dom If true, the criteria will match if it dominates
|
||||
the user's default level.
|
||||
level_domby If true, the criteria will match if it is dominated
|
||||
by the user's default level.
|
||||
level_incomp If true, the criteria will match if it is incomparable
|
||||
to the user's default level.
|
||||
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.log = logging.getLogger(self.__class__.__name__)
|
||||
Parameter:
|
||||
policy The policy to query.
|
||||
|
||||
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)
|
||||
Keyword Parameters/Class attributes:
|
||||
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.
|
||||
level The criteria to match the user's default level.
|
||||
level_dom If true, the criteria will match if it dominates
|
||||
the user's default level.
|
||||
level_domby If true, the criteria will match if it is dominated
|
||||
by the user's default level.
|
||||
level_incomp If true, the criteria will match if it is incomparable
|
||||
to the user's default level.
|
||||
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.
|
||||
"""
|
||||
|
||||
level = CriteriaDescriptor(lookup_function="lookup_level")
|
||||
level_dom = False
|
||||
level_domby = False
|
||||
level_incomp = False
|
||||
range_ = CriteriaDescriptor(lookup_function="lookup_range")
|
||||
range_overlap = False
|
||||
range_subset = False
|
||||
range_superset = False
|
||||
range_proper = False
|
||||
roles = CriteriaSetDescriptor("roles_regex", "lookup_role")
|
||||
roles_equal = False
|
||||
roles_regex = False
|
||||
|
||||
def results(self):
|
||||
"""Generator which yields all matching users."""
|
||||
self.log.info("Generating results from {0.policy}".format(self))
|
||||
self.log.debug("Name: {0.name_cmp!r}, regex: {0.name_regex}".format(self))
|
||||
self.log.debug("Roles: {0.roles_cmp!r}, regex: {0.roles_regex}, "
|
||||
self.log.debug("Name: {0.name!r}, regex: {0.name_regex}".format(self))
|
||||
self.log.debug("Roles: {0.roles!r}, regex: {0.roles_regex}, "
|
||||
"eq: {0.roles_equal}".format(self))
|
||||
self.log.debug("Level: {0.level!r}, dom: {0.level_dom}, domby: {0.level_domby}, "
|
||||
"incomp: {0.level_incomp}".format(self))
|
||||
@ -84,15 +86,12 @@ class UserQuery(compquery.ComponentQuery):
|
||||
"superset: {0.range_superset}, proper: {0.range_proper}".format(self))
|
||||
|
||||
for user in self.policy.users():
|
||||
if self.name and not self._match_regex(
|
||||
user,
|
||||
self.name_cmp,
|
||||
self.name_regex):
|
||||
if not self._match_name(user):
|
||||
continue
|
||||
|
||||
if self.roles and not self._match_regex_or_set(
|
||||
user.roles,
|
||||
self.roles_cmp,
|
||||
self.roles,
|
||||
self.roles_equal,
|
||||
self.roles_regex):
|
||||
continue
|
||||
@ -115,108 +114,3 @@ class UserQuery(compquery.ComponentQuery):
|
||||
continue
|
||||
|
||||
yield user
|
||||
|
||||
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.
|
||||
|
||||
Parameter:
|
||||
roles Name to match the component's attributes.
|
||||
|
||||
Keyword Options:
|
||||
regex If true, regular expression matching will be used
|
||||
instead of set logic.
|
||||
equal If true, the role set of the user
|
||||
must equal the attributes criteria to
|
||||
match. If false, any intersection in the
|
||||
critera will cause a rule match.
|
||||
|
||||
Exceptions:
|
||||
NameError Invalid keyword option.
|
||||
"""
|
||||
|
||||
self.roles = roles
|
||||
|
||||
for k in list(opts.keys()):
|
||||
if k == "regex":
|
||||
self.roles_regex = opts[k]
|
||||
elif k == "equal":
|
||||
self.roles_equal = opts[k]
|
||||
else:
|
||||
raise NameError("Invalid roles option: {0}".format(k))
|
||||
|
||||
if not self.roles:
|
||||
self.roles_cmp = None
|
||||
elif self.roles_regex:
|
||||
self.roles_cmp = re.compile(self.roles)
|
||||
else:
|
||||
self.roles_cmp = set(self.policy.lookup_role(r) for r in self.roles)
|
||||
|
@ -52,7 +52,7 @@ class BoolQueryTest(unittest.TestCase):
|
||||
|
||||
def test_010_default(self):
|
||||
"""Boolean query with default state match."""
|
||||
q = BoolQuery(self.p, match_default=True, default=False)
|
||||
q = BoolQuery(self.p, default=False)
|
||||
|
||||
bools = sorted(str(b) for b in q.results())
|
||||
self.assertListEqual(["test10a", "test10b"], bools)
|
||||
|
@ -42,6 +42,7 @@ class ConstraintQueryTest(unittest.TestCase):
|
||||
constraint = sorted(c.tclass for c in q.results())
|
||||
self.assertListEqual(["test1"], constraint)
|
||||
|
||||
@unittest.skip("Setting tclass to a string is no longer supported.")
|
||||
def test_010_class_exact(self):
|
||||
"""Constraint query with exact object class match."""
|
||||
q = ConstraintQuery(self.p, tclass="test10")
|
||||
|
147
tests/dta.py
147
tests/dta.py
@ -347,7 +347,7 @@ class DomainTransitionAnalysisTest(mixins.ValidateRule, unittest.TestCase):
|
||||
# Don't check node list since the disconnected nodes are not
|
||||
# removed after removing invalid domain transitions
|
||||
|
||||
self.a.set_reverse(False)
|
||||
self.a.reverse = False
|
||||
self.a._build_subgraph()
|
||||
|
||||
start = self.p.lookup_type("start")
|
||||
@ -375,7 +375,7 @@ class DomainTransitionAnalysisTest(mixins.ValidateRule, unittest.TestCase):
|
||||
# Don't check node list since the disconnected nodes are not
|
||||
# removed after removing invalid domain transitions
|
||||
|
||||
self.a.set_reverse(True)
|
||||
self.a.reverse = True
|
||||
self.a._build_subgraph()
|
||||
|
||||
start = self.p.lookup_type("start")
|
||||
@ -399,8 +399,8 @@ class DomainTransitionAnalysisTest(mixins.ValidateRule, unittest.TestCase):
|
||||
# Don't check node list since the disconnected nodes are not
|
||||
# removed after removing invalid domain transitions
|
||||
|
||||
self.a.set_reverse(False)
|
||||
self.a.set_exclude(["trans1"])
|
||||
self.a.reverse = False
|
||||
self.a.exclude = ["trans1"]
|
||||
self.a._build_subgraph()
|
||||
|
||||
start = self.p.lookup_type("start")
|
||||
@ -421,8 +421,8 @@ class DomainTransitionAnalysisTest(mixins.ValidateRule, unittest.TestCase):
|
||||
# Don't check node list since the disconnected nodes are not
|
||||
# removed after removing invalid domain transitions
|
||||
|
||||
self.a.set_reverse(False)
|
||||
self.a.set_exclude(["trans3_exec1"])
|
||||
self.a.reverse = False
|
||||
self.a.exclude = ["trans3_exec1"]
|
||||
self.a._build_subgraph()
|
||||
|
||||
start = self.p.lookup_type("start")
|
||||
@ -446,8 +446,8 @@ class DomainTransitionAnalysisTest(mixins.ValidateRule, unittest.TestCase):
|
||||
# Don't check node list since the disconnected nodes are not
|
||||
# removed after removing invalid domain transitions
|
||||
|
||||
self.a.set_reverse(False)
|
||||
self.a.set_exclude(["bothtrans200_exec"])
|
||||
self.a.reverse = False
|
||||
self.a.exclude = ["bothtrans200_exec"]
|
||||
self.a._build_subgraph()
|
||||
|
||||
start = self.p.lookup_type("start")
|
||||
@ -471,8 +471,8 @@ class DomainTransitionAnalysisTest(mixins.ValidateRule, unittest.TestCase):
|
||||
# Don't check node list since the disconnected nodes are not
|
||||
# removed after removing invalid domain transitions
|
||||
|
||||
self.a.set_reverse(False)
|
||||
self.a.set_exclude(["trans2_exec"])
|
||||
self.a.reverse = False
|
||||
self.a.exclude = ["trans2_exec"]
|
||||
self.a._build_subgraph()
|
||||
|
||||
start = self.p.lookup_type("start")
|
||||
@ -492,8 +492,8 @@ class DomainTransitionAnalysisTest(mixins.ValidateRule, unittest.TestCase):
|
||||
|
||||
def test_300_all_paths(self):
|
||||
"""DTA: all paths output"""
|
||||
self.a.set_reverse(False)
|
||||
self.a.set_exclude(None)
|
||||
self.a.reverse = False
|
||||
self.a.exclude = None
|
||||
|
||||
expected_path = ["start", "dyntrans100", "bothtrans200"]
|
||||
|
||||
@ -533,8 +533,8 @@ class DomainTransitionAnalysisTest(mixins.ValidateRule, unittest.TestCase):
|
||||
|
||||
def test_301_all_shortest_paths(self):
|
||||
"""DTA: all shortest paths output"""
|
||||
self.a.set_reverse(False)
|
||||
self.a.set_exclude(None)
|
||||
self.a.reverse = False
|
||||
self.a.exclude = None
|
||||
|
||||
expected_path = ["start", "dyntrans100", "bothtrans200"]
|
||||
|
||||
@ -574,8 +574,8 @@ class DomainTransitionAnalysisTest(mixins.ValidateRule, unittest.TestCase):
|
||||
|
||||
def test_302_shortest_path(self):
|
||||
"""DTA: shortest path output"""
|
||||
self.a.set_reverse(False)
|
||||
self.a.set_exclude(None)
|
||||
self.a.reverse = False
|
||||
self.a.exclude = None
|
||||
|
||||
expected_path = ["start", "dyntrans100", "bothtrans200"]
|
||||
|
||||
@ -615,8 +615,8 @@ class DomainTransitionAnalysisTest(mixins.ValidateRule, unittest.TestCase):
|
||||
|
||||
def test_303_transitions(self):
|
||||
"""DTA: transitions output"""
|
||||
self.a.set_reverse(False)
|
||||
self.a.set_exclude(None)
|
||||
self.a.reverse = False
|
||||
self.a.exclude = None
|
||||
|
||||
transitions = list(self.a.transitions("start"))
|
||||
self.assertEqual(2, len(transitions))
|
||||
@ -652,8 +652,8 @@ class DomainTransitionAnalysisTest(mixins.ValidateRule, unittest.TestCase):
|
||||
|
||||
def test_310_all_paths_reversed(self):
|
||||
"""DTA: all paths output reverse DTA"""
|
||||
self.a.set_reverse(True)
|
||||
self.a.set_exclude(None)
|
||||
self.a.reverse = True
|
||||
self.a.exclude = None
|
||||
|
||||
expected_path = ["bothtrans200", "dyntrans100", "start"]
|
||||
|
||||
@ -693,8 +693,8 @@ class DomainTransitionAnalysisTest(mixins.ValidateRule, unittest.TestCase):
|
||||
|
||||
def test_311_all_shortest_paths_reversed(self):
|
||||
"""DTA: all shortest paths output reverse DTA"""
|
||||
self.a.set_reverse(True)
|
||||
self.a.set_exclude(None)
|
||||
self.a.reverse = True
|
||||
self.a.exclude = None
|
||||
|
||||
expected_path = ["bothtrans200", "dyntrans100", "start"]
|
||||
|
||||
@ -734,8 +734,8 @@ class DomainTransitionAnalysisTest(mixins.ValidateRule, unittest.TestCase):
|
||||
|
||||
def test_312_shortest_path_reversed(self):
|
||||
"""DTA: shortest path output reverse DTA"""
|
||||
self.a.set_reverse(True)
|
||||
self.a.set_exclude(None)
|
||||
self.a.reverse = True
|
||||
self.a.exclude = None
|
||||
|
||||
expected_path = ["bothtrans200", "dyntrans100", "start"]
|
||||
|
||||
@ -775,8 +775,8 @@ class DomainTransitionAnalysisTest(mixins.ValidateRule, unittest.TestCase):
|
||||
|
||||
def test_313_transitions_reversed(self):
|
||||
"""DTA: transitions output reverse DTA"""
|
||||
self.a.set_reverse(True)
|
||||
self.a.set_exclude(None)
|
||||
self.a.reverse = True
|
||||
self.a.exclude = None
|
||||
|
||||
transitions = list(self.a.transitions("bothtrans200"))
|
||||
self.assertEqual(1, len(transitions))
|
||||
@ -812,160 +812,161 @@ class DomainTransitionAnalysisTest(mixins.ValidateRule, unittest.TestCase):
|
||||
|
||||
def test_900_set_exclude_invalid_type(self):
|
||||
"""DTA: set invalid excluded type."""
|
||||
self.a.set_reverse(False)
|
||||
self.a.set_exclude(None)
|
||||
self.assertRaises(InvalidType, self.a.set_exclude, ["trans1", "invalid_type"])
|
||||
self.a.reverse = False
|
||||
self.a.exclude = None
|
||||
with self.assertRaises(InvalidType):
|
||||
self.a.exclude = ["trans1", "invalid_type"]
|
||||
|
||||
def test_910_all_paths_invalid_source(self):
|
||||
"""DTA: all paths with invalid source type."""
|
||||
self.a.set_reverse(False)
|
||||
self.a.set_exclude(None)
|
||||
self.a.reverse = False
|
||||
self.a.exclude = None
|
||||
with self.assertRaises(InvalidType):
|
||||
list(self.a.all_paths("invalid_type", "trans1"))
|
||||
|
||||
def test_911_all_paths_invalid_target(self):
|
||||
"""DTA: all paths with invalid target type."""
|
||||
self.a.set_reverse(False)
|
||||
self.a.set_exclude(None)
|
||||
self.a.reverse = False
|
||||
self.a.exclude = None
|
||||
with self.assertRaises(InvalidType):
|
||||
list(self.a.all_paths("trans1", "invalid_type"))
|
||||
|
||||
def test_912_all_paths_invalid_maxlen(self):
|
||||
"""DTA: all paths with invalid max path length."""
|
||||
self.a.set_reverse(False)
|
||||
self.a.set_exclude(None)
|
||||
self.a.reverse = False
|
||||
self.a.exclude = None
|
||||
with self.assertRaises(ValueError):
|
||||
list(self.a.all_paths("trans1", "trans2", maxlen=-2))
|
||||
|
||||
def test_913_all_paths_source_excluded(self):
|
||||
"""DTA: all paths with excluded source type."""
|
||||
self.a.set_reverse(False)
|
||||
self.a.set_exclude(["trans1"])
|
||||
self.a.reverse = False
|
||||
self.a.exclude = ["trans1"]
|
||||
paths = list(self.a.all_paths("trans1", "trans2"))
|
||||
self.assertEqual(0, len(paths))
|
||||
|
||||
def test_914_all_paths_target_excluded(self):
|
||||
"""DTA: all paths with excluded target type."""
|
||||
self.a.set_reverse(False)
|
||||
self.a.set_exclude(["trans2"])
|
||||
self.a.reverse = False
|
||||
self.a.exclude = ["trans2"]
|
||||
paths = list(self.a.all_paths("trans1", "trans2"))
|
||||
self.assertEqual(0, len(paths))
|
||||
|
||||
def test_915_all_paths_source_disconnected(self):
|
||||
"""DTA: all paths with disconnected source type."""
|
||||
self.a.set_reverse(False)
|
||||
self.a.set_exclude(None)
|
||||
self.a.reverse = False
|
||||
self.a.exclude = None
|
||||
paths = list(self.a.all_paths("trans5", "trans2"))
|
||||
self.assertEqual(0, len(paths))
|
||||
|
||||
def test_916_all_paths_target_disconnected(self):
|
||||
"""DTA: all paths with disconnected target type."""
|
||||
self.a.set_reverse(False)
|
||||
self.a.set_exclude(["trans3"])
|
||||
self.a.reverse = False
|
||||
self.a.exclude = ["trans3"]
|
||||
paths = list(self.a.all_paths("trans2", "trans5"))
|
||||
self.assertEqual(0, len(paths))
|
||||
|
||||
def test_920_shortest_path_invalid_source(self):
|
||||
"""DTA: shortest path with invalid source type."""
|
||||
self.a.set_reverse(False)
|
||||
self.a.set_exclude(None)
|
||||
self.a.reverse = False
|
||||
self.a.exclude = None
|
||||
with self.assertRaises(InvalidType):
|
||||
list(self.a.shortest_path("invalid_type", "trans1"))
|
||||
|
||||
def test_921_shortest_path_invalid_target(self):
|
||||
"""DTA: shortest path with invalid target type."""
|
||||
self.a.set_reverse(False)
|
||||
self.a.set_exclude(None)
|
||||
self.a.reverse = False
|
||||
self.a.exclude = None
|
||||
with self.assertRaises(InvalidType):
|
||||
list(self.a.shortest_path("trans1", "invalid_type"))
|
||||
|
||||
def test_922_shortest_path_source_excluded(self):
|
||||
"""DTA: shortest path with excluded source type."""
|
||||
self.a.set_reverse(False)
|
||||
self.a.set_exclude(["trans1"])
|
||||
self.a.reverse = False
|
||||
self.a.exclude = ["trans1"]
|
||||
paths = list(self.a.shortest_path("trans1", "trans2"))
|
||||
self.assertEqual(0, len(paths))
|
||||
|
||||
def test_923_shortest_path_target_excluded(self):
|
||||
"""DTA: shortest path with excluded target type."""
|
||||
self.a.set_reverse(False)
|
||||
self.a.set_exclude(["trans2"])
|
||||
self.a.reverse = False
|
||||
self.a.exclude = ["trans2"]
|
||||
paths = list(self.a.shortest_path("trans1", "trans2"))
|
||||
self.assertEqual(0, len(paths))
|
||||
|
||||
def test_924_shortest_path_source_disconnected(self):
|
||||
"""DTA: shortest path with disconnected source type."""
|
||||
self.a.set_reverse(False)
|
||||
self.a.set_exclude(None)
|
||||
self.a.reverse = False
|
||||
self.a.exclude = None
|
||||
paths = list(self.a.shortest_path("trans5", "trans2"))
|
||||
self.assertEqual(0, len(paths))
|
||||
|
||||
def test_925_shortest_path_target_disconnected(self):
|
||||
"""DTA: shortest path with disconnected target type."""
|
||||
self.a.set_reverse(False)
|
||||
self.a.set_exclude(["trans3"])
|
||||
self.a.reverse = False
|
||||
self.a.exclude = ["trans3"]
|
||||
paths = list(self.a.shortest_path("trans2", "trans5"))
|
||||
self.assertEqual(0, len(paths))
|
||||
|
||||
def test_930_all_shortest_paths_invalid_source(self):
|
||||
"""DTA: all shortest paths with invalid source type."""
|
||||
self.a.set_reverse(False)
|
||||
self.a.set_exclude(None)
|
||||
self.a.reverse = False
|
||||
self.a.exclude = None
|
||||
with self.assertRaises(InvalidType):
|
||||
list(self.a.all_shortest_paths("invalid_type", "trans1"))
|
||||
|
||||
def test_931_all_shortest_paths_invalid_target(self):
|
||||
"""DTA: all shortest paths with invalid target type."""
|
||||
self.a.set_reverse(False)
|
||||
self.a.set_exclude(None)
|
||||
self.a.reverse = False
|
||||
self.a.exclude = None
|
||||
with self.assertRaises(InvalidType):
|
||||
list(self.a.all_shortest_paths("trans1", "invalid_type"))
|
||||
|
||||
def test_932_all_shortest_paths_source_excluded(self):
|
||||
"""DTA: all shortest paths with excluded source type."""
|
||||
self.a.set_reverse(False)
|
||||
self.a.set_exclude(["trans1"])
|
||||
self.a.reverse = False
|
||||
self.a.exclude = ["trans1"]
|
||||
paths = list(self.a.all_shortest_paths("trans1", "trans2"))
|
||||
self.assertEqual(0, len(paths))
|
||||
|
||||
def test_933_all_shortest_paths_target_excluded(self):
|
||||
"""DTA: all shortest paths with excluded target type."""
|
||||
self.a.set_reverse(False)
|
||||
self.a.set_exclude(["trans2"])
|
||||
self.a.reverse = False
|
||||
self.a.exclude = ["trans2"]
|
||||
paths = list(self.a.all_shortest_paths("trans1", "trans2"))
|
||||
self.assertEqual(0, len(paths))
|
||||
|
||||
def test_934_all_shortest_paths_source_disconnected(self):
|
||||
"""DTA: all shortest paths with disconnected source type."""
|
||||
self.a.set_reverse(False)
|
||||
self.a.set_exclude(None)
|
||||
self.a.reverse = False
|
||||
self.a.exclude = None
|
||||
paths = list(self.a.all_shortest_paths("trans5", "trans2"))
|
||||
self.assertEqual(0, len(paths))
|
||||
|
||||
def test_935_all_shortest_paths_target_disconnected(self):
|
||||
"""DTA: all shortest paths with disconnected target type."""
|
||||
self.a.set_reverse(False)
|
||||
self.a.set_exclude(["trans3"])
|
||||
self.a.reverse = False
|
||||
self.a.exclude = ["trans3"]
|
||||
paths = list(self.a.all_shortest_paths("trans2", "trans5"))
|
||||
self.assertEqual(0, len(paths))
|
||||
|
||||
def test_940_transitions_invalid_source(self):
|
||||
"""DTA: transitions with invalid source type."""
|
||||
self.a.set_reverse(False)
|
||||
self.a.set_exclude(None)
|
||||
self.a.reverse = False
|
||||
self.a.exclude = None
|
||||
with self.assertRaises(InvalidType):
|
||||
list(self.a.transitions("invalid_type"))
|
||||
|
||||
def test_941_transitions_source_excluded(self):
|
||||
"""DTA: transitions with excluded source type."""
|
||||
self.a.set_reverse(False)
|
||||
self.a.set_exclude(["trans1"])
|
||||
self.a.reverse = False
|
||||
self.a.exclude = ["trans1"]
|
||||
paths = list(self.a.transitions("trans1"))
|
||||
self.assertEqual(0, len(paths))
|
||||
|
||||
def test_942_transitions_source_disconnected(self):
|
||||
"""DTA: transitions with disconnected source type."""
|
||||
self.a.set_reverse(False)
|
||||
self.a.set_exclude(["trans3"])
|
||||
self.a.reverse = False
|
||||
self.a.exclude = ["trans3"]
|
||||
paths = list(self.a.transitions("trans5"))
|
||||
self.assertEqual(0, len(paths))
|
||||
|
@ -129,8 +129,8 @@ class InfoFlowAnalysisTest(mixins.ValidateRule, unittest.TestCase):
|
||||
def test_100_minimum_3(self):
|
||||
"""Information flow analysis with minimum weight 3."""
|
||||
|
||||
self.a.set_exclude(None)
|
||||
self.a.set_min_weight(3)
|
||||
self.a.exclude = None
|
||||
self.a.min_weight = 3
|
||||
self.a._build_subgraph()
|
||||
|
||||
disconnected1 = self.p.lookup_type("disconnected1")
|
||||
@ -166,8 +166,8 @@ class InfoFlowAnalysisTest(mixins.ValidateRule, unittest.TestCase):
|
||||
def test_200_minimum_8(self):
|
||||
"""Information flow analysis with minimum weight 8."""
|
||||
|
||||
self.a.set_exclude(None)
|
||||
self.a.set_min_weight(8)
|
||||
self.a.exclude = None
|
||||
self.a.min_weight = 8
|
||||
self.a._build_subgraph()
|
||||
|
||||
disconnected1 = self.p.lookup_type("disconnected1")
|
||||
@ -199,8 +199,8 @@ class InfoFlowAnalysisTest(mixins.ValidateRule, unittest.TestCase):
|
||||
|
||||
def test_300_all_paths(self):
|
||||
"""Information flow analysis: all paths output"""
|
||||
self.a.set_exclude(None)
|
||||
self.a.set_min_weight(1)
|
||||
self.a.exclude = None
|
||||
self.a.min_weight = 1
|
||||
|
||||
paths = list(self.a.all_paths("node1", "node4", 3))
|
||||
self.assertEqual(1, len(paths))
|
||||
@ -226,8 +226,8 @@ class InfoFlowAnalysisTest(mixins.ValidateRule, unittest.TestCase):
|
||||
|
||||
def test_301_all_shortest_paths(self):
|
||||
"""Information flow analysis: all shortest paths output"""
|
||||
self.a.set_exclude(None)
|
||||
self.a.set_min_weight(1)
|
||||
self.a.exclude = None
|
||||
self.a.min_weight = 1
|
||||
|
||||
paths = list(self.a.all_shortest_paths("node1", "node4"))
|
||||
self.assertEqual(1, len(paths))
|
||||
@ -253,8 +253,8 @@ class InfoFlowAnalysisTest(mixins.ValidateRule, unittest.TestCase):
|
||||
|
||||
def test_302_shortest_path(self):
|
||||
"""Information flow analysis: shortest path output"""
|
||||
self.a.set_exclude(None)
|
||||
self.a.set_min_weight(1)
|
||||
self.a.exclude = None
|
||||
self.a.min_weight = 1
|
||||
|
||||
paths = list(self.a.shortest_path("node1", "node4"))
|
||||
self.assertEqual(1, len(paths))
|
||||
@ -280,8 +280,8 @@ class InfoFlowAnalysisTest(mixins.ValidateRule, unittest.TestCase):
|
||||
|
||||
def test_303_infoflows_out(self):
|
||||
"""Information flow analysis: flows out of a type"""
|
||||
self.a.set_exclude(None)
|
||||
self.a.set_min_weight(1)
|
||||
self.a.exclude = None
|
||||
self.a.min_weight = 1
|
||||
|
||||
for flow in self.a.infoflows("node6"):
|
||||
self.assertIsInstance(flow.source, Type)
|
||||
@ -292,8 +292,8 @@ class InfoFlowAnalysisTest(mixins.ValidateRule, unittest.TestCase):
|
||||
|
||||
def test_304_infoflows_in(self):
|
||||
"""Information flow analysis: flows in to a type"""
|
||||
self.a.set_exclude(None)
|
||||
self.a.set_min_weight(1)
|
||||
self.a.exclude = None
|
||||
self.a.min_weight = 1
|
||||
|
||||
for flow in self.a.infoflows("node8", out=False):
|
||||
self.assertIsInstance(flow.source, Type)
|
||||
@ -304,168 +304,176 @@ class InfoFlowAnalysisTest(mixins.ValidateRule, unittest.TestCase):
|
||||
|
||||
def test_900_set_exclude_invalid_type(self):
|
||||
"""Information flow analysis: set invalid excluded type."""
|
||||
self.assertRaises(InvalidType, self.a.set_exclude, ["node1", "invalid_type"])
|
||||
with self.assertRaises(InvalidType):
|
||||
self.a.exclude = ["node1", "invalid_type"]
|
||||
|
||||
def test_901_set_small_min_weight(self):
|
||||
"""Information flow analysis: set too small weight."""
|
||||
self.assertRaises(ValueError, self.a.set_min_weight, 0)
|
||||
self.assertRaises(ValueError, self.a.set_min_weight, -3)
|
||||
|
||||
with self.assertRaises(ValueError):
|
||||
self.a.min_weight = 0
|
||||
|
||||
with self.assertRaises(ValueError):
|
||||
self.a.min_weight = -3
|
||||
|
||||
def test_902_set_large_min_weight(self):
|
||||
"""Information flow analysis: set too big weight."""
|
||||
self.assertRaises(ValueError, self.a.set_min_weight, 11)
|
||||
self.assertRaises(ValueError, self.a.set_min_weight, 50)
|
||||
with self.assertRaises(ValueError):
|
||||
self.a.min_weight = 11
|
||||
|
||||
with self.assertRaises(ValueError):
|
||||
self.a.min_weight = 50
|
||||
|
||||
def test_910_all_paths_invalid_source(self):
|
||||
"""Information flow analysis: all paths with invalid source type."""
|
||||
self.a.set_exclude(None)
|
||||
self.a.set_min_weight(1)
|
||||
self.a.exclude = None
|
||||
self.a.min_weight = 1
|
||||
with self.assertRaises(InvalidType):
|
||||
list(self.a.all_paths("invalid_type", "node1"))
|
||||
|
||||
def test_911_all_paths_invalid_target(self):
|
||||
"""Information flow analysis: all paths with invalid target type."""
|
||||
self.a.set_exclude(None)
|
||||
self.a.set_min_weight(1)
|
||||
self.a.exclude = None
|
||||
self.a.min_weight = 1
|
||||
with self.assertRaises(InvalidType):
|
||||
list(self.a.all_paths("node1", "invalid_type"))
|
||||
|
||||
def test_912_all_paths_invalid_maxlen(self):
|
||||
"""Information flow analysis: all paths with invalid max path length."""
|
||||
self.a.set_exclude(None)
|
||||
self.a.set_min_weight(1)
|
||||
self.a.exclude = None
|
||||
self.a.min_weight = 1
|
||||
with self.assertRaises(ValueError):
|
||||
list(self.a.all_paths("node1", "node2", maxlen=-2))
|
||||
|
||||
def test_913_all_paths_source_excluded(self):
|
||||
"""Information flow analysis: all paths with excluded source type."""
|
||||
self.a.set_exclude(["node1"])
|
||||
self.a.set_min_weight(1)
|
||||
self.a.exclude = ["node1"]
|
||||
self.a.min_weight = 1
|
||||
paths = list(self.a.all_paths("node1", "node2"))
|
||||
self.assertEqual(0, len(paths))
|
||||
|
||||
def test_914_all_paths_target_excluded(self):
|
||||
"""Information flow analysis: all paths with excluded target type."""
|
||||
self.a.set_exclude(["node2"])
|
||||
self.a.set_min_weight(1)
|
||||
self.a.exclude = ["node2"]
|
||||
self.a.min_weight = 1
|
||||
paths = list(self.a.all_paths("node1", "node2"))
|
||||
self.assertEqual(0, len(paths))
|
||||
|
||||
def test_915_all_paths_source_disconnected(self):
|
||||
"""Information flow analysis: all paths with disconnected source type."""
|
||||
self.a.set_exclude(None)
|
||||
self.a.set_min_weight(1)
|
||||
self.a.exclude = None
|
||||
self.a.min_weight = 1
|
||||
paths = list(self.a.all_paths("disconnected1", "node2"))
|
||||
self.assertEqual(0, len(paths))
|
||||
|
||||
def test_916_all_paths_target_disconnected(self):
|
||||
"""Information flow analysis: all paths with disconnected target type."""
|
||||
self.a.set_exclude(None)
|
||||
self.a.set_min_weight(1)
|
||||
self.a.exclude = None
|
||||
self.a.min_weight = 1
|
||||
paths = list(self.a.all_paths("node2", "disconnected1"))
|
||||
self.assertEqual(0, len(paths))
|
||||
|
||||
def test_920_shortest_path_invalid_source(self):
|
||||
"""Information flow analysis: shortest path with invalid source type."""
|
||||
self.a.set_exclude(None)
|
||||
self.a.set_min_weight(1)
|
||||
self.a.exclude = None
|
||||
self.a.min_weight = 1
|
||||
with self.assertRaises(InvalidType):
|
||||
list(self.a.shortest_path("invalid_type", "node1"))
|
||||
|
||||
def test_921_shortest_path_invalid_target(self):
|
||||
"""Information flow analysis: shortest path with invalid target type."""
|
||||
self.a.set_exclude(None)
|
||||
self.a.set_min_weight(1)
|
||||
self.a.exclude = None
|
||||
self.a.min_weight = 1
|
||||
with self.assertRaises(InvalidType):
|
||||
list(self.a.shortest_path("node1", "invalid_type"))
|
||||
|
||||
def test_922_shortest_path_source_excluded(self):
|
||||
"""Information flow analysis: shortest path with excluded source type."""
|
||||
self.a.set_exclude(["node1"])
|
||||
self.a.set_min_weight(1)
|
||||
self.a.exclude = ["node1"]
|
||||
self.a.min_weight = 1
|
||||
paths = list(self.a.shortest_path("node1", "node2"))
|
||||
self.assertEqual(0, len(paths))
|
||||
|
||||
def test_923_shortest_path_target_excluded(self):
|
||||
"""Information flow analysis: shortest path with excluded target type."""
|
||||
self.a.set_exclude(["node2"])
|
||||
self.a.set_min_weight(1)
|
||||
self.a.exclude = ["node2"]
|
||||
self.a.min_weight = 1
|
||||
paths = list(self.a.shortest_path("node1", "node2"))
|
||||
self.assertEqual(0, len(paths))
|
||||
|
||||
def test_924_shortest_path_source_disconnected(self):
|
||||
"""Information flow analysis: shortest path with disconnected source type."""
|
||||
self.a.set_exclude(None)
|
||||
self.a.set_min_weight(1)
|
||||
self.a.exclude = None
|
||||
self.a.min_weight = 1
|
||||
paths = list(self.a.shortest_path("disconnected1", "node2"))
|
||||
self.assertEqual(0, len(paths))
|
||||
|
||||
def test_925_shortest_path_target_disconnected(self):
|
||||
"""Information flow analysis: shortest path with disconnected target type."""
|
||||
self.a.set_exclude(None)
|
||||
self.a.set_min_weight(1)
|
||||
self.a.exclude = None
|
||||
self.a.min_weight = 1
|
||||
paths = list(self.a.shortest_path("node2", "disconnected1"))
|
||||
self.assertEqual(0, len(paths))
|
||||
|
||||
def test_930_all_shortest_paths_invalid_source(self):
|
||||
"""Information flow analysis: all shortest paths with invalid source type."""
|
||||
self.a.set_exclude(None)
|
||||
self.a.set_min_weight(1)
|
||||
self.a.exclude = None
|
||||
self.a.min_weight = 1
|
||||
with self.assertRaises(InvalidType):
|
||||
list(self.a.all_shortest_paths("invalid_type", "node1"))
|
||||
|
||||
def test_931_all_shortest_paths_invalid_target(self):
|
||||
"""Information flow analysis: all shortest paths with invalid target type."""
|
||||
self.a.set_exclude(None)
|
||||
self.a.set_min_weight(1)
|
||||
self.a.exclude = None
|
||||
self.a.min_weight = 1
|
||||
with self.assertRaises(InvalidType):
|
||||
list(self.a.all_shortest_paths("node1", "invalid_type"))
|
||||
|
||||
def test_932_all_shortest_paths_source_excluded(self):
|
||||
"""Information flow analysis: all shortest paths with excluded source type."""
|
||||
self.a.set_exclude(["node1"])
|
||||
self.a.set_min_weight(1)
|
||||
self.a.exclude = ["node1"]
|
||||
self.a.min_weight = 1
|
||||
paths = list(self.a.all_shortest_paths("node1", "node2"))
|
||||
self.assertEqual(0, len(paths))
|
||||
|
||||
def test_933_all_shortest_paths_target_excluded(self):
|
||||
"""Information flow analysis: all shortest paths with excluded target type."""
|
||||
self.a.set_exclude(["node2"])
|
||||
self.a.set_min_weight(1)
|
||||
self.a.exclude = ["node2"]
|
||||
self.a.min_weight = 1
|
||||
paths = list(self.a.all_shortest_paths("node1", "node2"))
|
||||
self.assertEqual(0, len(paths))
|
||||
|
||||
def test_934_all_shortest_paths_source_disconnected(self):
|
||||
"""Information flow analysis: all shortest paths with disconnected source type."""
|
||||
self.a.set_exclude(None)
|
||||
self.a.set_min_weight(1)
|
||||
self.a.exclude = None
|
||||
self.a.min_weight = 1
|
||||
paths = list(self.a.all_shortest_paths("disconnected1", "node2"))
|
||||
self.assertEqual(0, len(paths))
|
||||
|
||||
def test_935_all_shortest_paths_target_disconnected(self):
|
||||
"""Information flow analysis: all shortest paths with disconnected target type."""
|
||||
self.a.set_exclude(None)
|
||||
self.a.set_min_weight(1)
|
||||
self.a.exclude = None
|
||||
self.a.min_weight = 1
|
||||
paths = list(self.a.all_shortest_paths("node2", "disconnected1"))
|
||||
self.assertEqual(0, len(paths))
|
||||
|
||||
def test_940_infoflows_invalid_source(self):
|
||||
"""Information flow analysis: infoflows with invalid source type."""
|
||||
self.a.set_exclude(None)
|
||||
self.a.set_min_weight(1)
|
||||
self.a.exclude = None
|
||||
self.a.min_weight = 1
|
||||
with self.assertRaises(InvalidType):
|
||||
list(self.a.infoflows("invalid_type"))
|
||||
|
||||
def test_941_infoflows_source_excluded(self):
|
||||
"""Information flow analysis: infoflows with excluded source type."""
|
||||
self.a.set_exclude(["node1"])
|
||||
self.a.set_min_weight(1)
|
||||
self.a.exclude = ["node1"]
|
||||
self.a.min_weight = 1
|
||||
paths = list(self.a.infoflows("node1"))
|
||||
self.assertEqual(0, len(paths))
|
||||
|
||||
def test_942_infoflows_source_disconnected(self):
|
||||
"""Information flow analysis: infoflows with disconnected source type."""
|
||||
self.a.set_exclude(["disconnected2"])
|
||||
self.a.set_min_weight(1)
|
||||
self.a.exclude = ["disconnected2"]
|
||||
self.a.min_weight = 1
|
||||
paths = list(self.a.infoflows("disconnected1"))
|
||||
self.assertEqual(0, len(paths))
|
||||
|
@ -18,6 +18,7 @@
|
||||
import unittest
|
||||
|
||||
from setools import SELinuxPolicy, MLSRuleQuery
|
||||
from setools.policyrep.exception import InvalidMLSRuleType
|
||||
|
||||
from . import mixins
|
||||
|
||||
@ -82,6 +83,7 @@ class MLSRuleQueryTest(mixins.ValidateRule, unittest.TestCase):
|
||||
self.assertEqual(len(r), 1)
|
||||
self.validate_rule(r[0], "range_transition", "test12s", "test12aFAIL", "infoflow", "s2")
|
||||
|
||||
@unittest.skip("Setting tclass to a string is no longer supported.")
|
||||
def test_020_class(self):
|
||||
"""MLS rule query with exact object class match."""
|
||||
q = MLSRuleQuery(self.p, tclass="infoflow7", tclass_regex=False)
|
||||
@ -273,3 +275,8 @@ class MLSRuleQueryTest(mixins.ValidateRule, unittest.TestCase):
|
||||
self.assertEqual(len(r), 1)
|
||||
self.validate_rule(r[0], "range_transition", "test45", "test45", "infoflow",
|
||||
"s45:c1 - s45:c1.c3")
|
||||
|
||||
def test_900_invalid_ruletype(self):
|
||||
"""MLS rule query with invalid rule type."""
|
||||
with self.assertRaises(InvalidMLSRuleType):
|
||||
q = MLSRuleQuery(self.p, ruletype="type_transition")
|
||||
|
@ -135,6 +135,18 @@ role test11t3;
|
||||
allow test11s test11t1;
|
||||
role_transition test11s system:infoflow test11t3;
|
||||
|
||||
# test 12
|
||||
# ruletype: unset
|
||||
# source: unset
|
||||
# target: test12t
|
||||
# class: unset
|
||||
# default: unset
|
||||
role test12s;
|
||||
type test12t;
|
||||
role test12d;
|
||||
allow test12s test12d;
|
||||
role_transition test12s test12t:infoflow test12d;
|
||||
|
||||
# test 20
|
||||
# ruletype: unset
|
||||
# source: unset
|
||||
|
@ -90,6 +90,15 @@ class RBACRuleQueryTest(mixins.ValidateRule, unittest.TestCase):
|
||||
self.assertEqual(len(r), 1)
|
||||
self.validate_allow(r[0], "test11s", "test11t1")
|
||||
|
||||
def test_012_target_type(self):
|
||||
"""RBAC rule query with a type as target."""
|
||||
q = RBACRuleQuery(self.p, target="test12t")
|
||||
|
||||
r = sorted(q.results())
|
||||
self.assertEqual(len(r), 1)
|
||||
self.validate_rule(r[0], "role_transition", "test12s", "test12t", "infoflow", "test12d")
|
||||
|
||||
@unittest.skip("Setting tclass to a string is no longer supported.")
|
||||
def test_020_class(self):
|
||||
"""RBAC rule query with exact object class match."""
|
||||
q = RBACRuleQuery(self.p, tclass="infoflow2", tclass_regex=False)
|
||||
@ -146,4 +155,4 @@ class RBACRuleQueryTest(mixins.ValidateRule, unittest.TestCase):
|
||||
|
||||
# this will have to be updated as number of
|
||||
# role allows change in the test policy
|
||||
self.assertEqual(num, 8)
|
||||
self.assertEqual(num, 9)
|
||||
|
@ -117,6 +117,7 @@ class TERuleQueryTest(mixins.ValidateRule, unittest.TestCase):
|
||||
self.validate_rule(r[0], "allow", "test8a1", "test8a1", "infoflow", set(["hi_w"]))
|
||||
self.validate_rule(r[1], "allow", "test8a2", "test8a2", "infoflow", set(["low_r"]))
|
||||
|
||||
@unittest.skip("Setting tclass to a string is no longer supported.")
|
||||
def test_009_class(self):
|
||||
"""TE rule query with exact object class match."""
|
||||
q = TERuleQuery(self.p, tclass="infoflow2", tclass_regex=False)
|
||||
|
@ -89,7 +89,7 @@ class TypeQueryTest(unittest.TestCase):
|
||||
|
||||
def test_030_permissive(self):
|
||||
"""Type query with permissive match"""
|
||||
q = TypeQuery(self.p, match_permissive=True, permissive=True)
|
||||
q = TypeQuery(self.p, permissive=True)
|
||||
|
||||
types = sorted(str(t) for t in q.results())
|
||||
self.assertListEqual(["test30"], types)
|
||||
|
Loading…
Reference in New Issue
Block a user