• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# Authors: Karl MacMillan <kmacmillan@mentalrootkit.com>
2#
3# Copyright (C) 2006 Red Hat
4# see file 'COPYING' for use and warranty information
5#
6# This program is free software; you can redistribute it and/or
7# modify it under the terms of the GNU General Public License as
8# published by the Free Software Foundation; version 2 only
9#
10# This program 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 General Public License for more details.
14#
15# You should have received a copy of the GNU General Public License
16# along with this program; if not, write to the Free Software
17# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18#
19
20"""
21Classes representing basic access.
22
23SELinux - at the most basic level - represents access as
24the 4-tuple subject (type or context), target (type or context),
25object class, permission. The policy language elaborates this basic
26access to facilitate more concise rules (e.g., allow rules can have multiple
27source or target types - see refpolicy for more information).
28
29This module has objects for representing the most basic access (AccessVector)
30and sets of that access (AccessVectorSet). These objects are used in Madison
31in a variety of ways, but they are the fundamental representation of access.
32"""
33
34from . import refpolicy
35from . import util
36
37from selinux import audit2why
38
39def is_idparam(id):
40    """Determine if an id is a parameter in the form $N, where N is
41    an integer.
42
43    Returns:
44      True if the id is a parameter
45      False if the id is not a parameter
46    """
47    if len(id) > 1 and id[0] == '$':
48        try:
49            int(id[1:])
50        except ValueError:
51            return False
52        return True
53    else:
54        return False
55
56class AccessVector(util.Comparison):
57    """
58    An access vector is the basic unit of access in SELinux.
59
60    Access vectors are the most basic representation of access within
61    SELinux. It represents the access a source type has to a target
62    type in terms of an object class and a set of permissions.
63
64    Access vectors are distinct from AVRules in that they can only
65    store a single source type, target type, and object class. The
66    simplicity of AccessVectors makes them useful for storing access
67    in a form that is easy to search and compare.
68
69    The source, target, and object are stored as string. No checking
70    done to verify that the strings are valid SELinux identifiers.
71    Identifiers in the form $N (where N is an integer) are reserved as
72    interface parameters and are treated as wild cards in many
73    circumstances.
74
75    Properties:
76     .src_type - The source type allowed access. [String or None]
77     .tgt_type - The target type to which access is allowed. [String or None]
78     .obj_class - The object class to which access is allowed. [String or None]
79     .perms - The permissions allowed to the object class. [IdSet]
80     .audit_msgs - The audit messages that generated this access vector [List of strings]
81     .xperms - Extended permissions attached to the AV. [Dictionary {operation: xperm set}]
82    """
83    def __init__(self, init_list=None):
84        if init_list:
85            self.from_list(init_list)
86        else:
87            self.src_type = None
88            self.tgt_type = None
89            self.obj_class = None
90            self.perms = refpolicy.IdSet()
91
92        self.audit_msgs = []
93        self.type = audit2why.TERULE
94        self.data = []
95        self.xperms = {}
96        # when implementing __eq__ also __hash__ is needed on py2
97        # if object is muttable __hash__ should be None
98        self.__hash__ = None
99
100        # The direction of the information flow represented by this
101        # access vector - used for matching
102        self.info_flow_dir = None
103
104    def from_list(self, list):
105        """Initialize an access vector from a list.
106
107        Initialize an access vector from a list treating the list as
108        positional arguments - i.e., 0 = src_type, 1 = tgt_type, etc.
109        All of the list elements 3 and greater are treated as perms.
110        For example, the list ['foo_t', 'bar_t', 'file', 'read', 'write']
111        would create an access vector list with the source type 'foo_t',
112        target type 'bar_t', object class 'file', and permissions 'read'
113        and 'write'.
114
115        This format is useful for very simple storage to strings or disc
116        (see to_list) and for initializing access vectors.
117        """
118        if len(list) < 4:
119            raise ValueError("List must contain at least four elements %s" % str(list))
120        self.src_type = list[0]
121        self.tgt_type = list[1]
122        self.obj_class = list[2]
123        self.perms = refpolicy.IdSet(list[3:])
124
125    def to_list(self):
126        """
127        Convert an access vector to a list.
128
129        Convert an access vector to a list treating the list as positional
130        values. See from_list for more information on how an access vector
131        is represented in a list.
132        """
133        l = [self.src_type, self.tgt_type, self.obj_class]
134        l.extend(sorted(self.perms))
135        return l
136
137    def merge(self, av):
138        """Add permissions and extended permissions from AV"""
139        self.perms.update(av.perms)
140
141        for op in av.xperms:
142            if op not in self.xperms:
143                self.xperms[op] = refpolicy.XpermSet()
144            self.xperms[op].extend(av.xperms[op])
145
146    def __str__(self):
147        return self.to_string()
148
149    def to_string(self):
150        return "allow %s %s:%s %s;" % (self.src_type, self.tgt_type,
151                                        self.obj_class, self.perms.to_space_str())
152
153    def _compare(self, other, method):
154        try:
155            x = list(self.perms)
156            a = (self.src_type, self.tgt_type, self.obj_class, x)
157            y = list(other.perms)
158            x.sort()
159            y.sort()
160            b = (other.src_type, other.tgt_type, other.obj_class, y)
161            return method(a, b)
162        except (AttributeError, TypeError):
163            # trying to compare to foreign type
164            return NotImplemented
165
166
167def avrule_to_access_vectors(avrule):
168    """Convert an avrule into a list of access vectors.
169
170    AccessVectors and AVRules are similarly, but differ in that
171    an AVRule can more than one source type, target type, and
172    object class. This function expands a single avrule into a
173    list of one or more AccessVectors representing the access
174    defined in the AVRule.
175
176
177    """
178    if isinstance(avrule, AccessVector):
179        return [avrule]
180    a = []
181    for src_type in avrule.src_types:
182        for tgt_type in avrule.tgt_types:
183            for obj_class in avrule.obj_classes:
184                access = AccessVector()
185                access.src_type = src_type
186                access.tgt_type = tgt_type
187                access.obj_class = obj_class
188                access.perms = avrule.perms.copy()
189                a.append(access)
190    return a
191
192class AccessVectorSet:
193    """A non-overlapping set of access vectors.
194
195    An AccessVectorSet is designed to store one or more access vectors
196    that are non-overlapping. Access can be added to the set
197    incrementally and access vectors will be added or merged as
198    necessary.  For example, adding the following access vectors using
199    add_av:
200       allow $1 etc_t : read;
201       allow $1 etc_t : write;
202       allow $1 var_log_t : read;
203    Would result in an access vector set with the access vectors:
204       allow $1 etc_t : { read write};
205       allow $1 var_log_t : read;
206    """
207    def __init__(self):
208        """Initialize an access vector set.
209        """
210        self.src = {}
211        # The information flow direction of this access vector
212        # set - see objectmodel.py for more information. This
213        # stored here to speed up searching - see matching.py.
214        self.info_dir = None
215
216    def __iter__(self):
217        """Iterate over all of the unique access vectors in the set."""
218        for tgts in self.src.values():
219            for objs in tgts.values():
220                for av in objs.values():
221                    yield av
222
223    def __len__(self):
224        """Return the number of unique access vectors in the set.
225
226        Because of the internal representation of the access vector set,
227        __len__ is not a constant time operation. Worst case is O(N)
228        where N is the number of unique access vectors, but the common
229        case is probably better.
230        """
231        l = 0
232        for tgts in self.src.values():
233            for objs in tgts.values():
234               l += len(objs)
235        return l
236
237    def to_list(self):
238        """Return the unique access vectors in the set as a list.
239
240        The format of the returned list is a set of nested lists,
241        each access vector represented by a list. This format is
242        designed to be simply  serializable to a file.
243
244        For example, consider an access vector set with the following
245        access vectors:
246          allow $1 user_t : file read;
247          allow $1 etc_t : file { read write};
248        to_list would return the following:
249          [[$1, user_t, file, read]
250           [$1, etc_t, file, read, write]]
251
252        See AccessVector.to_list for more information.
253        """
254        l = []
255        for av in self:
256            l.append(av.to_list())
257
258        return l
259
260    def from_list(self, l):
261        """Add access vectors stored in a list.
262
263        See to list for more information on the list format that this
264        method accepts.
265
266        This will add all of the access from the list. Any existing
267        access vectors in the set will be retained.
268        """
269        for av in l:
270            self.add_av(AccessVector(av))
271
272    def add(self, src_type, tgt_type, obj_class, perms, audit_msg=None, avc_type=audit2why.TERULE, data=[]):
273        """Add an access vector to the set.
274        """
275        av = AccessVector()
276        av.src_type = src_type
277        av.tgt_type = tgt_type
278        av.obj_class = obj_class
279        av.perms = perms
280        av.data = data
281        av.type = avc_type
282
283        self.add_av(av, audit_msg)
284
285    def add_av(self, av, audit_msg=None):
286        """Add an access vector to the set."""
287        tgt = self.src.setdefault(av.src_type, { })
288        cls = tgt.setdefault(av.tgt_type, { })
289
290        if (av.obj_class, av.type) in cls:
291            cls[av.obj_class, av.type].merge(av)
292        else:
293            cls[av.obj_class, av.type] = av
294
295        if audit_msg:
296            cls[av.obj_class, av.type].audit_msgs.append(audit_msg)
297
298def avs_extract_types(avs):
299    types = refpolicy.IdSet()
300    for av in avs:
301        types.add(av.src_type)
302        types.add(av.tgt_type)
303
304    return types
305
306def avs_extract_obj_perms(avs):
307    perms = { }
308    for av in avs:
309        if av.obj_class in perms:
310            s = perms[av.obj_class]
311        else:
312            s = refpolicy.IdSet()
313            perms[av.obj_class] = s
314        s.update(av.perms)
315    return perms
316
317class RoleTypeSet:
318    """A non-overlapping set of role type statements.
319
320    This class allows the incremental addition of role type statements and
321    maintains a non-overlapping list of statements.
322    """
323    def __init__(self):
324        """Initialize an access vector set."""
325        self.role_types = {}
326
327    def __iter__(self):
328        """Iterate over all of the unique role allows statements in the set."""
329        for role_type in self.role_types.values():
330            yield role_type
331
332    def __len__(self):
333        """Return the unique number of role allow statements."""
334        return len(self.role_types.keys())
335
336    def add(self, role, type):
337        if role in self.role_types:
338            role_type = self.role_types[role]
339        else:
340            role_type = refpolicy.RoleType()
341            role_type.role = role
342            self.role_types[role] = role_type
343
344        role_type.types.add(type)
345