blob: a5d86982c0b138cd56f12f89fea2c8424306dac0 [file] [log] [blame]
Jeff Vander Stoep74e4f932016-02-08 15:27:10 -08001# 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)