Refactor SETools queries/analyses to use descriptors instead of get/setters

This is Pythonic.
This commit is contained in:
Chris PeBenito 2015-05-16 21:59:10 -04:00
parent ab3772843e
commit e6f59d04e5
40 changed files with 1458 additions and 2574 deletions

47
seinfo
View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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
View 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][:]

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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