• 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 parameter 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        # Handle 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 parameters from an access vector.
108
109    Extract the parameters (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 parameters 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 ambiguous, 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    return ret
157
158def role_extract_params(role, params):
159    if access.is_idparam(role.role):
160        return __param_insert(role.role, refpolicy.ROLE, None, params)
161
162def type_rule_extract_params(rule, params):
163    def extract_from_set(set, type):
164        ret = 0
165        for x in set:
166            if access.is_idparam(x):
167                if __param_insert(x, type, None, params):
168                    ret = 1
169        return ret
170
171    ret = 0
172    if extract_from_set(rule.src_types, refpolicy.SRC_TYPE):
173        ret = 1
174
175    if extract_from_set(rule.tgt_types, refpolicy.TGT_TYPE):
176        ret = 1
177
178    if extract_from_set(rule.obj_classes, refpolicy.OBJ_CLASS):
179        ret = 1
180
181    if access.is_idparam(rule.dest_type):
182        if __param_insert(rule.dest_type, refpolicy.DEST_TYPE, None, params):
183            ret = 1
184
185    return ret
186
187def ifcall_extract_params(ifcall, params):
188    ret = 0
189    for arg in ifcall.args:
190        if access.is_idparam(arg):
191            # Assume interface arguments are source types. Fairly safe
192            # assumption for most interfaces
193            if __param_insert(arg, refpolicy.SRC_TYPE, None, params):
194                ret = 1
195
196    return ret
197
198class AttributeVector:
199    def __init__(self):
200        self.name = ""
201        self.access = access.AccessVectorSet()
202
203    def add_av(self, av):
204        self.access.add_av(av)
205
206class AttributeSet:
207    def __init__(self):
208        self.attributes = { }
209
210    def add_attr(self, attr):
211        self.attributes[attr.name] = attr
212
213    def from_file(self, fd):
214        def parse_attr(line):
215            fields = line[1:-1].split()
216            if len(fields) != 2 or fields[0] != "Attribute":
217                raise SyntaxError("Syntax error Attribute statement %s" % line)
218            a = AttributeVector()
219            a.name = fields[1]
220
221            return a
222
223        a = None
224        for line in fd:
225            line = line[:-1]
226            if line[0] == "[":
227                if a:
228                    self.add_attr(a)
229                a = parse_attr(line)
230            elif a:
231                l = line.split(",")
232                av = access.AccessVector(l)
233                a.add_av(av)
234        if a:
235            self.add_attr(a)
236
237class InterfaceVector:
238    def __init__(self, interface=None, attributes={}):
239        # Enabled is a loose concept currently - we are essentially
240        # not enabling interfaces that we can't handle currently.
241        # See InterfaceVector.add_ifv for more information.
242        self.enabled = True
243        self.name = ""
244        # The access that is enabled by this interface - eventually
245        # this will include indirect access from typeattribute
246        # statements.
247        self.access = access.AccessVectorSet()
248        # Parameters are stored in a dictionary (key: param name
249        # value: Param object).
250        self.params = { }
251        if interface:
252            self.from_interface(interface, attributes)
253        self.expanded = False
254
255    def from_interface(self, interface, attributes={}):
256        self.name = interface.name
257
258        # Add allow rules
259        for avrule in interface.avrules():
260            if avrule.rule_type != refpolicy.AVRule.ALLOW:
261                continue
262            # Handle some policy bugs
263            if "dontaudit" in interface.name:
264                #print "allow rule in interface: %s" % interface
265                continue
266            avs = access.avrule_to_access_vectors(avrule)
267            for av in avs:
268                self.add_av(av)
269
270        # Add typeattribute access
271        if attributes:
272            for typeattribute in interface.typeattributes():
273                for attr in typeattribute.attributes:
274                    if attr not in attributes.attributes:
275                        # print "missing attribute " + attr
276                        continue
277                    attr_vec = attributes.attributes[attr]
278                    for a in attr_vec.access:
279                        av = copy.copy(a)
280                        if av.src_type == attr_vec.name:
281                            av.src_type = typeattribute.type
282                        if av.tgt_type == attr_vec.name:
283                            av.tgt_type = typeattribute.type
284                        self.add_av(av)
285
286
287        # Extract parameters from roles
288        for role in interface.roles():
289            if role_extract_params(role, self.params):
290                pass
291                #print "found conflicting role param %s for interface %s" % \
292                #      (role.name, interface.name)
293        # Extract parameters from type rules
294        for rule in interface.typerules():
295            if type_rule_extract_params(rule, self.params):
296                pass
297                #print "found conflicting params in rule %s in interface %s" % \
298                #      (str(rule), interface.name)
299
300        for ifcall in interface.interface_calls():
301            if ifcall_extract_params(ifcall, self.params):
302                pass
303                #print "found conflicting params in ifcall %s in interface %s" % \
304                #      (str(ifcall), interface.name)
305
306
307    def add_av(self, av):
308        if av_extract_params(av, self.params) == 1:
309            pass
310            #print "found conflicting perms [%s]" % str(av)
311        self.access.add_av(av)
312
313    def to_string(self):
314        s = []
315        s.append("[InterfaceVector %s]" % self.name)
316        for av in self.access:
317            s.append(str(av))
318        return "\n".join(s)
319
320    def __str__(self):
321        return self.__repr__()
322
323    def __repr__(self):
324        return "<InterfaceVector %s:%s>" % (self.name, self.enabled)
325
326
327class InterfaceSet:
328    def __init__(self, output=None):
329        self.interfaces = { }
330        self.tgt_type_map = { }
331        self.tgt_type_all = []
332        self.output = output
333
334    def o(self, str):
335        if self.output:
336            self.output.write(str + "\n")
337
338    def to_file(self, fd):
339        for iv in sorted(self.interfaces.values(), key=lambda x: x.name):
340            fd.write("[InterfaceVector %s " % iv.name)
341            for param in sorted(iv.params.values(), key=lambda x: x.name):
342                fd.write("%s:%s " % (param.name, refpolicy.field_to_str[param.type]))
343            fd.write("]\n")
344            avl = sorted(iv.access.to_list())
345            for av in avl:
346                fd.write(",".join(av))
347                fd.write("\n")
348
349    def from_file(self, fd):
350        def parse_ifv(line):
351            fields = line[1:-1].split()
352            if len(fields) < 2 or fields[0] != "InterfaceVector":
353                raise SyntaxError("Syntax error InterfaceVector statement %s" % line)
354            ifv = InterfaceVector()
355            ifv.name = fields[1]
356            if len(fields) == 2:
357                return
358            for field in fields[2:]:
359                p = field.split(":")
360                if len(p) != 2:
361                    raise SyntaxError("Invalid param in InterfaceVector statement %s" % line)
362                param = Param()
363                param.name = p[0]
364                param.type = refpolicy.str_to_field[p[1]]
365                ifv.params[param.name] = param
366            return ifv
367
368        ifv = None
369        for line in fd:
370            line = line[:-1]
371            if line[0] == "[":
372                if ifv:
373                    self.add_ifv(ifv)
374                ifv = parse_ifv(line)
375            elif ifv:
376                l = line.split(",")
377                av = access.AccessVector(l)
378                ifv.add_av(av)
379        if ifv:
380            self.add_ifv(ifv)
381
382        self.index()
383
384    def add_ifv(self, ifv):
385        self.interfaces[ifv.name] = ifv
386
387    def index(self):
388        for ifv in self.interfaces.values():
389            tgt_types = set()
390            for av in ifv.access:
391                if access.is_idparam(av.tgt_type):
392                    self.tgt_type_all.append(ifv)
393                    tgt_types = set()
394                    break
395                tgt_types.add(av.tgt_type)
396
397            for type in tgt_types:
398                l = self.tgt_type_map.setdefault(type, [])
399                l.append(ifv)
400
401    def add(self, interface, attributes={}):
402        ifv = InterfaceVector(interface, attributes)
403        self.add_ifv(ifv)
404
405    def add_headers(self, headers, output=None, attributes={}):
406        for i in itertools.chain(headers.interfaces(), headers.templates()):
407            self.add(i, attributes)
408
409        self.expand_ifcalls(headers)
410        self.index()
411
412    def map_param(self, id, ifcall):
413        if access.is_idparam(id):
414            num = int(id[1:])
415            if num > len(ifcall.args):
416                # Tell caller to drop this because it must have
417                # been generated from an optional param.
418                return None
419            else:
420                arg = ifcall.args[num - 1]
421                if isinstance(arg, list):
422                    return arg
423                else:
424                    return [arg]
425        else:
426            return [id]
427
428    def map_add_av(self, ifv, av, ifcall):
429        src_types = self.map_param(av.src_type, ifcall)
430        if src_types is None:
431            return
432
433        tgt_types = self.map_param(av.tgt_type, ifcall)
434        if tgt_types is None:
435            return
436
437        obj_classes = self.map_param(av.obj_class, ifcall)
438        if obj_classes is None:
439            return
440
441        new_perms = refpolicy.IdSet()
442        for perm in av.perms:
443            p = self.map_param(perm, ifcall)
444            if p is None:
445                continue
446            else:
447                new_perms.update(p)
448        if len(new_perms) == 0:
449            return
450
451        for src_type in src_types:
452            for tgt_type in tgt_types:
453                for obj_class in obj_classes:
454                    ifv.access.add(src_type, tgt_type, obj_class, new_perms)
455
456    def do_expand_ifcalls(self, interface, if_by_name):
457        # Descend an interface call tree adding the access
458        # from each interface. This is a depth first walk
459        # of the tree.
460
461        stack = [(interface, None)]
462        ifv = self.interfaces[interface.name]
463        ifv.expanded = True
464
465        while len(stack) > 0:
466            cur, cur_ifcall = stack.pop(-1)
467
468            cur_ifv = self.interfaces[cur.name]
469            if cur != interface:
470
471                for av in cur_ifv.access:
472                    self.map_add_av(ifv, av, cur_ifcall)
473
474                # If we have already fully expanded this interface
475                # there is no reason to descend further.
476                if cur_ifv.expanded:
477                    continue
478
479            for ifcall in cur.interface_calls():
480                if ifcall.ifname == interface.name:
481                    self.o(_("Found circular interface class"))
482                    return
483                try:
484                    newif = if_by_name[ifcall.ifname]
485                except KeyError:
486                    self.o(_("Missing interface definition for %s" % ifcall.ifname))
487                    continue
488
489                stack.append((newif, ifcall))
490
491
492    def expand_ifcalls(self, headers):
493        # Create a map of interface names to interfaces -
494        # this mirrors the interface vector map we already
495        # have.
496        if_by_name = { }
497
498        for i in itertools.chain(headers.interfaces(), headers.templates()):
499            if_by_name[i.name] = i
500
501
502        for interface in itertools.chain(headers.interfaces(), headers.templates()):
503            self.do_expand_ifcalls(interface, if_by_name)
504
505