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