blob: 48ae4f27a414a8fc102135f8f274ee78f789ded7 [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 for representing and manipulating interfaces.
22"""
23
24import copy
25import itertools
26
27from . import access
28from . import refpolicy
29from . import objectmodel
30from . import matching
31from .sepolgeni18n import _
32
33
34class Param:
35 """
36 Object representing a paramater for an interface.
37 """
38 def __init__(self):
39 self.__name = ""
40 self.type = refpolicy.SRC_TYPE
41 self.obj_classes = refpolicy.IdSet()
42 self.required = True
43
44 def set_name(self, name):
45 if not access.is_idparam(name):
46 raise ValueError("Name [%s] is not a param" % name)
47 self.__name = name
48
49 def get_name(self):
50 return self.__name
51
52 name = property(get_name, set_name)
53
54 num = property(fget=lambda self: int(self.name[1:]))
55
56 def __repr__(self):
57 return "<sepolgen.policygen.Param instance [%s, %s, %s]>" % \
58 (self.name, refpolicy.field_to_str[self.type], " ".join(self.obj_classes))
59
60
61# Helper for extract perms
62def __param_insert(name, type, av, params):
63 ret = 0
64 if name in params:
65 p = params[name]
66 # The entries are identical - we're done
67 if type == p.type:
68 return
69 # Hanldle implicitly typed objects (like process)
70 if (type == refpolicy.SRC_TYPE or type == refpolicy.TGT_TYPE) and \
71 (p.type == refpolicy.TGT_TYPE or p.type == refpolicy.SRC_TYPE):
72 #print name, refpolicy.field_to_str[p.type]
73 # If the object is not implicitly typed, tell the
74 # caller there is a likely conflict.
75 ret = 1
76 if av:
77 avobjs = [av.obj_class]
78 else:
79 avobjs = []
80 for obj in itertools.chain(p.obj_classes, avobjs):
81 if obj in objectmodel.implicitly_typed_objects:
82 ret = 0
83 break
84 # "Promote" to a SRC_TYPE as this is the likely usage.
85 # We do this even if the above test fails on purpose
86 # as there is really no sane way to resolve the conflict
87 # here. The caller can take other actions if needed.
88 p.type = refpolicy.SRC_TYPE
89 else:
90 # There is some conflict - no way to resolve it really
91 # so we just leave the first entry and tell the caller
92 # there was a conflict.
93 ret = 1
94 else:
95 p = Param()
96 p.name = name
97 p.type = type
98 params[p.name] = p
99
100 if av:
101 p.obj_classes.add(av.obj_class)
102 return ret
103
104
105
106def av_extract_params(av, params):
107 """Extract the paramaters from an access vector.
108
109 Extract the paramaters (in the form $N) from an access
110 vector, storing them as Param objects in a dictionary.
111 Some attempt is made at resolving conflicts with other
112 entries in the dict, but if an unresolvable conflict is
113 found it is reported to the caller.
114
115 The goal here is to figure out how interface paramaters are
116 actually used in the interface - e.g., that $1 is a domain used as
117 a SRC_TYPE. In general an interface will look like this:
118
119 interface(`foo', `
120 allow $1 foo : file read;
121 ')
122
123 This is simple to figure out - $1 is a SRC_TYPE. A few interfaces
124 are more complex, for example:
125
126 interface(`foo_trans',`
127 domain_auto_trans($1,fingerd_exec_t,fingerd_t)
128
129 allow $1 fingerd_t:fd use;
130 allow fingerd_t $1:fd use;
131 allow fingerd_t $1:fifo_file rw_file_perms;
132 allow fingerd_t $1:process sigchld;
133 ')
134
135 Here the usage seems ambigious, but it is not. $1 is still domain
136 and therefore should be returned as a SRC_TYPE.
137
138 Returns:
139 0 - success
140 1 - conflict found
141 """
142 ret = 0
143 found_src = False
144 if access.is_idparam(av.src_type):
145 if __param_insert(av.src_type, refpolicy.SRC_TYPE, av, params) == 1:
146 ret = 1
147
148 if access.is_idparam(av.tgt_type):
149 if __param_insert(av.tgt_type, refpolicy.TGT_TYPE, av, params) == 1:
150 ret = 1
151
152 if access.is_idparam(av.obj_class):
153 if __param_insert(av.obj_class, refpolicy.OBJ_CLASS, av, params) == 1:
154 ret = 1
155
156 for perm in av.perms:
157 if access.is_idparam(perm):
158 if __param_insert(perm, PERM) == 1:
159 ret = 1
160
161 return ret
162
163def role_extract_params(role, params):
164 if access.is_idparam(role.role):
165 return __param_insert(role.role, refpolicy.ROLE, None, params)
166
167def type_rule_extract_params(rule, params):
168 def extract_from_set(set, type):
169 ret = 0
170 for x in set:
171 if access.is_idparam(x):
172 if __param_insert(x, type, None, params):
173 ret = 1
174 return ret
175
176 ret = 0
177 if extract_from_set(rule.src_types, refpolicy.SRC_TYPE):
178 ret = 1
179
180 if extract_from_set(rule.tgt_types, refpolicy.TGT_TYPE):
181 ret = 1
182
183 if extract_from_set(rule.obj_classes, refpolicy.OBJ_CLASS):
184 ret = 1
185
186 if access.is_idparam(rule.dest_type):
187 if __param_insert(rule.dest_type, refpolicy.DEST_TYPE, None, params):
188 ret = 1
189
190 return ret
191
192def ifcall_extract_params(ifcall, params):
193 ret = 0
194 for arg in ifcall.args:
195 if access.is_idparam(arg):
196 # Assume interface arguments are source types. Fairly safe
197 # assumption for most interfaces
198 if __param_insert(arg, refpolicy.SRC_TYPE, None, params):
199 ret = 1
200
201 return ret
202
203class AttributeVector:
204 def __init__(self):
205 self.name = ""
206 self.access = access.AccessVectorSet()
207
208 def add_av(self, av):
209 self.access.add_av(av)
210
211class AttributeSet:
212 def __init__(self):
213 self.attributes = { }
214
215 def add_attr(self, attr):
216 self.attributes[attr.name] = attr
217
218 def from_file(self, fd):
219 def parse_attr(line):
220 fields = line[1:-1].split()
221 if len(fields) != 2 or fields[0] != "Attribute":
222 raise SyntaxError("Syntax error Attribute statement %s" % line)
223 a = AttributeVector()
224 a.name = fields[1]
225
226 return a
227
228 a = None
229 for line in fd:
230 line = line[:-1]
231 if line[0] == "[":
232 if a:
233 self.add_attr(a)
234 a = parse_attr(line)
235 elif a:
236 l = line.split(",")
237 av = access.AccessVector(l)
238 a.add_av(av)
239 if a:
240 self.add_attr(a)
241
242class InterfaceVector:
243 def __init__(self, interface=None, attributes={}):
244 # Enabled is a loose concept currently - we are essentially
245 # not enabling interfaces that we can't handle currently.
246 # See InterfaceVector.add_ifv for more information.
247 self.enabled = True
248 self.name = ""
249 # The access that is enabled by this interface - eventually
250 # this will include indirect access from typeattribute
251 # statements.
252 self.access = access.AccessVectorSet()
253 # Paramaters are stored in a dictionary (key: param name
254 # value: Param object).
255 self.params = { }
256 if interface:
257 self.from_interface(interface, attributes)
258 self.expanded = False
259
260 def from_interface(self, interface, attributes={}):
261 self.name = interface.name
262
263 # Add allow rules
264 for avrule in interface.avrules():
265 if avrule.rule_type != refpolicy.AVRule.ALLOW:
266 continue
267 # Handle some policy bugs
268 if "dontaudit" in interface.name:
269 #print "allow rule in interface: %s" % interface
270 continue
271 avs = access.avrule_to_access_vectors(avrule)
272 for av in avs:
273 self.add_av(av)
274
275 # Add typeattribute access
276 if attributes:
277 for typeattribute in interface.typeattributes():
278 for attr in typeattribute.attributes:
279 if attr not in attributes.attributes:
280 # print "missing attribute " + attr
281 continue
282 attr_vec = attributes.attributes[attr]
283 for a in attr_vec.access:
284 av = copy.copy(a)
285 if av.src_type == attr_vec.name:
286 av.src_type = typeattribute.type
287 if av.tgt_type == attr_vec.name:
288 av.tgt_type = typeattribute.type
289 self.add_av(av)
290
291
292 # Extract paramaters from roles
293 for role in interface.roles():
294 if role_extract_params(role, self.params):
295 pass
296 #print "found conflicting role param %s for interface %s" % \
297 # (role.name, interface.name)
298 # Extract paramaters from type rules
299 for rule in interface.typerules():
300 if type_rule_extract_params(rule, self.params):
301 pass
302 #print "found conflicting params in rule %s in interface %s" % \
303 # (str(rule), interface.name)
304
305 for ifcall in interface.interface_calls():
306 if ifcall_extract_params(ifcall, self.params):
307 pass
308 #print "found conflicting params in ifcall %s in interface %s" % \
309 # (str(ifcall), interface.name)
310
311
312 def add_av(self, av):
313 if av_extract_params(av, self.params) == 1:
314 pass
315 #print "found conflicting perms [%s]" % str(av)
316 self.access.add_av(av)
317
318 def to_string(self):
319 s = []
320 s.append("[InterfaceVector %s]" % self.name)
321 for av in self.access:
322 s.append(str(av))
323 return "\n".join(s)
324
325 def __str__(self):
326 return self.__repr__()
327
328 def __repr__(self):
329 return "<InterfaceVector %s:%s>" % (self.name, self.enabled)
330
331
332class InterfaceSet:
333 def __init__(self, output=None):
334 self.interfaces = { }
335 self.tgt_type_map = { }
336 self.tgt_type_all = []
337 self.output = output
338
339 def o(self, str):
340 if self.output:
341 self.output.write(str + "\n")
342
343 def to_file(self, fd):
344 for iv in sorted(self.interfaces.values(), key=lambda x: x.name):
345 fd.write("[InterfaceVector %s " % iv.name)
346 for param in sorted(iv.params.values(), key=lambda x: x.name):
347 fd.write("%s:%s " % (param.name, refpolicy.field_to_str[param.type]))
348 fd.write("]\n")
349 avl = sorted(iv.access.to_list())
350 for av in avl:
351 fd.write(",".join(av))
352 fd.write("\n")
353
354 def from_file(self, fd):
355 def parse_ifv(line):
356 fields = line[1:-1].split()
357 if len(fields) < 2 or fields[0] != "InterfaceVector":
358 raise SyntaxError("Syntax error InterfaceVector statement %s" % line)
359 ifv = InterfaceVector()
360 ifv.name = fields[1]
361 if len(fields) == 2:
362 return
363 for field in fields[2:]:
364 p = field.split(":")
365 if len(p) != 2:
366 raise SyntaxError("Invalid param in InterfaceVector statement %s" % line)
367 param = Param()
368 param.name = p[0]
369 param.type = refpolicy.str_to_field[p[1]]
370 ifv.params[param.name] = param
371 return ifv
372
373 ifv = None
374 for line in fd:
375 line = line[:-1]
376 if line[0] == "[":
377 if ifv:
378 self.add_ifv(ifv)
379 ifv = parse_ifv(line)
380 elif ifv:
381 l = line.split(",")
382 av = access.AccessVector(l)
383 ifv.add_av(av)
384 if ifv:
385 self.add_ifv(ifv)
386
387 self.index()
388
389 def add_ifv(self, ifv):
390 self.interfaces[ifv.name] = ifv
391
392 def index(self):
393 for ifv in self.interfaces.values():
394 tgt_types = set()
395 for av in ifv.access:
396 if access.is_idparam(av.tgt_type):
397 self.tgt_type_all.append(ifv)
398 tgt_types = set()
399 break
400 tgt_types.add(av.tgt_type)
401
402 for type in tgt_types:
403 l = self.tgt_type_map.setdefault(type, [])
404 l.append(ifv)
405
406 def add(self, interface, attributes={}):
407 ifv = InterfaceVector(interface, attributes)
408 self.add_ifv(ifv)
409
410 def add_headers(self, headers, output=None, attributes={}):
411 for i in itertools.chain(headers.interfaces(), headers.templates()):
412 self.add(i, attributes)
413
414 self.expand_ifcalls(headers)
415 self.index()
416
417 def map_param(self, id, ifcall):
418 if access.is_idparam(id):
419 num = int(id[1:])
420 if num > len(ifcall.args):
421 # Tell caller to drop this because it must have
422 # been generated from an optional param.
423 return None
424 else:
425 arg = ifcall.args[num - 1]
426 if isinstance(arg, list):
427 return arg
428 else:
429 return [arg]
430 else:
431 return [id]
432
433 def map_add_av(self, ifv, av, ifcall):
434 src_types = self.map_param(av.src_type, ifcall)
435 if src_types is None:
436 return
437
438 tgt_types = self.map_param(av.tgt_type, ifcall)
439 if tgt_types is None:
440 return
441
442 obj_classes = self.map_param(av.obj_class, ifcall)
443 if obj_classes is None:
444 return
445
446 new_perms = refpolicy.IdSet()
447 for perm in av.perms:
448 p = self.map_param(perm, ifcall)
449 if p is None:
450 continue
451 else:
452 new_perms.update(p)
453 if len(new_perms) == 0:
454 return
455
456 for src_type in src_types:
457 for tgt_type in tgt_types:
458 for obj_class in obj_classes:
459 ifv.access.add(src_type, tgt_type, obj_class, new_perms)
460
461 def do_expand_ifcalls(self, interface, if_by_name):
462 # Descend an interface call tree adding the access
463 # from each interface. This is a depth first walk
464 # of the tree.
465
466 stack = [(interface, None)]
467 ifv = self.interfaces[interface.name]
468 ifv.expanded = True
469
470 while len(stack) > 0:
471 cur, cur_ifcall = stack.pop(-1)
472
473 cur_ifv = self.interfaces[cur.name]
474 if cur != interface:
475
476 for av in cur_ifv.access:
477 self.map_add_av(ifv, av, cur_ifcall)
478
479 # If we have already fully expanded this interface
480 # there is no reason to descend further.
481 if cur_ifv.expanded:
482 continue
483
484 for ifcall in cur.interface_calls():
485 if ifcall.ifname == interface.name:
486 self.o(_("Found circular interface class"))
487 return
488 try:
489 newif = if_by_name[ifcall.ifname]
490 except KeyError:
491 self.o(_("Missing interface definition for %s" % ifcall.ifname))
492 continue
493
494 stack.append((newif, ifcall))
495
496
497 def expand_ifcalls(self, headers):
498 # Create a map of interface names to interfaces -
499 # this mirrors the interface vector map we already
500 # have.
501 if_by_name = { }
502
503 for i in itertools.chain(headers.interfaces(), headers.templates()):
504 if_by_name[i.name] = i
505
506
507 for interface in itertools.chain(headers.interfaces(), headers.templates()):
508 self.do_expand_ifcalls(interface, if_by_name)
509