• 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 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 self.interfaces.values():
345            fd.write("[InterfaceVector %s " % iv.name)
346            for param in iv.params.values():
347                fd.write("%s:%s " % (param.name, refpolicy.field_to_str[param.type]))
348            fd.write("]\n")
349            avl = 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
510