Home | History | Annotate | Download | only in sepolgen
      1 # Authors: Karl MacMillan <kmacmillan (at] 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 """
     21 Classes for representing and manipulating interfaces.
     22 """
     23 
     24 import copy
     25 import itertools
     26 
     27 from . import access
     28 from . import refpolicy
     29 from . import objectmodel
     30 from . import matching
     31 from .sepolgeni18n import _
     32 
     33 
     34 class 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
     62 def __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 
    106 def 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 
    163 def role_extract_params(role, params):
    164     if access.is_idparam(role.role):
    165         return __param_insert(role.role, refpolicy.ROLE, None, params)
    166     
    167 def 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 
    192 def 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 
    203 class 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 
    211 class 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 
    242 class 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 
    332 class 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 
    510