1# Copyright 2015, Tresys Technology, LLC
2#
3# This file is part of SETools.
4#
5# SETools is free software: you can redistribute it and/or modify
6# it under the terms of the GNU Lesser General Public License as
7# published by the Free Software Foundation, either version 2.1 of
8# the License, or (at your option) any later version.
9#
10# SETools is distributed in the hope that it will be useful,
11# but WITHOUT ANY WARRANTY; without even the implied warranty of
12# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13# GNU Lesser General Public License for more details.
14#
15# You should have received a copy of the GNU Lesser General Public
16# License along with SETools.  If not, see
17# <http://www.gnu.org/licenses/>.
18#
19import logging
20import re
21
22from .descriptors import CriteriaDescriptor, CriteriaSetDescriptor
23from .mixins import MatchObjClass, MatchPermission
24from .policyrep.exception import ConstraintUseError
25from .query import PolicyQuery
26from .util import match_in_set
27
28
29class ConstraintQuery(MatchObjClass, MatchPermission, PolicyQuery):
30
31    """
32    Query constraint rules, (mls)constrain/(mls)validatetrans.
33
34    Parameter:
35    policy            The policy to query.
36
37    Keyword Parameters/Class attributes:
38    ruletype          The list of rule type(s) to match.
39    tclass            The object class(es) to match.
40    tclass_regex      If true, use a regular expression for
41                      matching the rule's object class.
42    perms             The permission(s) to match.
43    perms_equal       If true, the permission set of the rule
44                      must exactly match the permissions
45                      criteria.  If false, any set intersection
46                      will match.
47    perms_regex       If true, regular expression matching will be used
48                      on the permission names instead of set logic.
49    role              The name of the role to match in the
50                      constraint expression.
51    role_indirect     If true, members of an attribute will be
52                      matched rather than the attribute itself.
53    role_regex        If true, regular expression matching will
54                      be used on the role.
55    type_             The name of the type/attribute to match in the
56                      constraint expression.
57    type_indirect     If true, members of an attribute will be
58                      matched rather than the attribute itself.
59    type_regex        If true, regular expression matching will
60                      be used on the type/attribute.
61    user              The name of the user to match in the
62                      constraint expression.
63    user_regex        If true, regular expression matching will
64                      be used on the user.
65    """
66
67    ruletype = CriteriaSetDescriptor(lookup_function="validate_constraint_ruletype")
68    user = CriteriaDescriptor("user_regex", "lookup_user")
69    user_regex = False
70    role = CriteriaDescriptor("role_regex", "lookup_role")
71    role_regex = False
72    role_indirect = True
73    type_ = CriteriaDescriptor("type_regex", "lookup_type_or_attr")
74    type_regex = False
75    type_indirect = True
76
77    def __init__(self, policy, **kwargs):
78        super(ConstraintQuery, self).__init__(policy, **kwargs)
79        self.log = logging.getLogger(__name__)
80
81    def _match_expr(self, expr, criteria, indirect, regex):
82        """
83        Match roles/types/users in a constraint expression,
84        optionally by expanding the contents of attributes.
85
86        Parameters:
87        expr        The expression to match.
88        criteria    The criteria to match.
89        indirect    If attributes in the expression should be expanded.
90        regex       If regular expression matching should be used.
91        """
92
93        if indirect:
94            obj = set()
95            for item in expr:
96                obj.update(item.expand())
97        else:
98            obj = expr
99
100        return match_in_set(obj, criteria, regex)
101
102    def results(self):
103        """Generator which yields all matching constraints rules."""
104        self.log.info("Generating constraint results from {0.policy}".format(self))
105        self.log.debug("Ruletypes: {0.ruletype}".format(self))
106        self._match_object_class_debug(self.log)
107        self._match_perms_debug(self.log)
108        self.log.debug("User: {0.user!r}, regex: {0.user_regex}".format(self))
109        self.log.debug("Role: {0.role!r}, regex: {0.role_regex}".format(self))
110        self.log.debug("Type: {0.type_!r}, regex: {0.type_regex}".format(self))
111
112        for c in self.policy.constraints():
113            if self.ruletype:
114                if c.ruletype not in self.ruletype:
115                    continue
116
117            if not self._match_object_class(c):
118                continue
119
120            try:
121                if not self._match_perms(c):
122                    continue
123            except ConstraintUseError:
124                    continue
125
126            if self.role and not self._match_expr(
127                        c.roles,
128                        self.role,
129                        self.role_indirect,
130                        self.role_regex):
131                    continue
132
133            if self.type_ and not self._match_expr(
134                        c.types,
135                        self.type_,
136                        self.type_indirect,
137                        self.type_regex):
138                    continue
139
140            if self.user and not self._match_expr(
141                        c.users,
142                        self.user,
143                        False,
144                        self.user_regex):
145                    continue
146
147            yield c
148