• 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 faciliate 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 paramater in the form $N, where N is
41    an integer.
42
43    Returns:
44      True if the id is a paramater
45      False if the id is not a paramater
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    """
82    def __init__(self, init_list=None):
83        if init_list:
84            self.from_list(init_list)
85        else:
86            self.src_type = None
87            self.tgt_type = None
88            self.obj_class = None
89            self.perms = refpolicy.IdSet()
90            self.audit_msgs = []
91            self.type = audit2why.TERULE
92            self.data = []
93        # when implementing __eq__ also __hash__ is needed on py2
94        # if object is muttable __hash__ should be None
95        self.__hash__ = None
96
97        # The direction of the information flow represented by this
98        # access vector - used for matching
99        self.info_flow_dir = None
100
101    def from_list(self, list):
102        """Initialize an access vector from a list.
103
104        Initialize an access vector from a list treating the list as
105        positional arguments - i.e., 0 = src_type, 1 = tgt_type, etc.
106        All of the list elements 3 and greater are treated as perms.
107        For example, the list ['foo_t', 'bar_t', 'file', 'read', 'write']
108        would create an access vector list with the source type 'foo_t',
109        target type 'bar_t', object class 'file', and permissions 'read'
110        and 'write'.
111
112        This format is useful for very simple storage to strings or disc
113        (see to_list) and for initializing access vectors.
114        """
115        if len(list) < 4:
116            raise ValueError("List must contain at least four elements %s" % str(list))
117        self.src_type = list[0]
118        self.tgt_type = list[1]
119        self.obj_class = list[2]
120        self.perms = refpolicy.IdSet(list[3:])
121
122    def to_list(self):
123        """
124        Convert an access vector to a list.
125
126        Convert an access vector to a list treating the list as positional
127        values. See from_list for more information on how an access vector
128        is represented in a list.
129        """
130        l = [self.src_type, self.tgt_type, self.obj_class]
131        l.extend(sorted(self.perms))
132        return l
133
134    def __str__(self):
135        return self.to_string()
136
137    def to_string(self):
138        return "allow %s %s:%s %s;" % (self.src_type, self.tgt_type,
139                                        self.obj_class, self.perms.to_space_str())
140
141    def _compare(self, other, method):
142        try:
143            x = list(self.perms)
144            a = (self.src_type, self.tgt_type, self.obj_class, x)
145            y = list(other.perms)
146            x.sort()
147            y.sort()
148            b = (other.src_type, other.tgt_type, other.obj_class, y)
149            return method(a, b)
150        except (AttributeError, TypeError):
151            # trying to compare to foreign type
152            return NotImplemented
153
154
155def avrule_to_access_vectors(avrule):
156    """Convert an avrule into a list of access vectors.
157
158    AccessVectors and AVRules are similary, but differ in that
159    an AVRule can more than one source type, target type, and
160    object class. This function expands a single avrule into a
161    list of one or more AccessVectors representing the access
162    defined in the AVRule.
163
164
165    """
166    if isinstance(avrule, AccessVector):
167        return [avrule]
168    a = []
169    for src_type in avrule.src_types:
170        for tgt_type in avrule.tgt_types:
171            for obj_class in avrule.obj_classes:
172                access = AccessVector()
173                access.src_type = src_type
174                access.tgt_type = tgt_type
175                access.obj_class = obj_class
176                access.perms = avrule.perms.copy()
177                a.append(access)
178    return a
179
180class AccessVectorSet:
181    """A non-overlapping set of access vectors.
182
183    An AccessVectorSet is designed to store one or more access vectors
184    that are non-overlapping. Access can be added to the set
185    incrementally and access vectors will be added or merged as
186    necessary.  For example, adding the following access vectors using
187    add_av:
188       allow $1 etc_t : read;
189       allow $1 etc_t : write;
190       allow $1 var_log_t : read;
191    Would result in an access vector set with the access vectors:
192       allow $1 etc_t : { read write};
193       allow $1 var_log_t : read;
194    """
195    def __init__(self):
196        """Initialize an access vector set.
197        """
198        self.src = {}
199        # The information flow direction of this access vector
200        # set - see objectmodel.py for more information. This
201        # stored here to speed up searching - see matching.py.
202        self.info_dir = None
203
204    def __iter__(self):
205        """Iterate over all of the unique access vectors in the set."""
206        for tgts in self.src.values():
207            for objs in tgts.values():
208                for av in objs.values():
209                    yield av
210
211    def __len__(self):
212        """Return the number of unique access vectors in the set.
213
214        Because of the inernal representation of the access vector set,
215        __len__ is not a constant time operation. Worst case is O(N)
216        where N is the number of unique access vectors, but the common
217        case is probably better.
218        """
219        l = 0
220        for tgts in self.src.values():
221            for objs in tgts.values():
222               l += len(objs)
223        return l
224
225    def to_list(self):
226        """Return the unique access vectors in the set as a list.
227
228        The format of the returned list is a set of nested lists,
229        each access vector represented by a list. This format is
230        designed to be simply  serializable to a file.
231
232        For example, consider an access vector set with the following
233        access vectors:
234          allow $1 user_t : file read;
235          allow $1 etc_t : file { read write};
236        to_list would return the following:
237          [[$1, user_t, file, read]
238           [$1, etc_t, file, read, write]]
239
240        See AccessVector.to_list for more information.
241        """
242        l = []
243        for av in self:
244            l.append(av.to_list())
245
246        return l
247
248    def from_list(self, l):
249        """Add access vectors stored in a list.
250
251        See to list for more information on the list format that this
252        method accepts.
253
254        This will add all of the access from the list. Any existing
255        access vectors in the set will be retained.
256        """
257        for av in l:
258            self.add_av(AccessVector(av))
259
260    def add(self, src_type, tgt_type, obj_class, perms, audit_msg=None, avc_type=audit2why.TERULE, data=[]):
261        """Add an access vector to the set.
262        """
263        tgt = self.src.setdefault(src_type, { })
264        cls = tgt.setdefault(tgt_type, { })
265
266        if (obj_class, avc_type) in cls:
267            access = cls[obj_class, avc_type]
268        else:
269            access = AccessVector()
270            access.src_type = src_type
271            access.tgt_type = tgt_type
272            access.obj_class = obj_class
273            access.data = data
274            access.type = avc_type
275            cls[obj_class, avc_type] = access
276
277        access.perms.update(perms)
278        if audit_msg:
279            access.audit_msgs.append(audit_msg)
280
281    def add_av(self, av, audit_msg=None):
282        """Add an access vector to the set."""
283        self.add(av.src_type, av.tgt_type, av.obj_class, av.perms)
284
285
286def avs_extract_types(avs):
287    types = refpolicy.IdSet()
288    for av in avs:
289        types.add(av.src_type)
290        types.add(av.tgt_type)
291
292    return types
293
294def avs_extract_obj_perms(avs):
295    perms = { }
296    for av in avs:
297        if av.obj_class in perms:
298            s = perms[av.obj_class]
299        else:
300            s = refpolicy.IdSet()
301            perms[av.obj_class] = s
302        s.update(av.perms)
303    return perms
304
305class RoleTypeSet:
306    """A non-overlapping set of role type statements.
307
308    This clas allows the incremental addition of role type statements and
309    maintains a non-overlapping list of statements.
310    """
311    def __init__(self):
312        """Initialize an access vector set."""
313        self.role_types = {}
314
315    def __iter__(self):
316        """Iterate over all of the unique role allows statements in the set."""
317        for role_type in self.role_types.values():
318            yield role_type
319
320    def __len__(self):
321        """Return the unique number of role allow statements."""
322        return len(self.role_types.keys())
323
324    def add(self, role, type):
325        if role in self.role_types:
326            role_type = self.role_types[role]
327        else:
328            role_type = refpolicy.RoleType()
329            role_type.role = role
330            self.role_types[role] = role_type
331
332        role_type.types.add(type)
333