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 import string
     21 import selinux
     22 
     23 # OVERVIEW
     24 #
     25 # This file contains objects and functions used to represent the reference
     26 # policy (including the headers, M4 macros, and policy language statements).
     27 #
     28 # This representation is very different from the semantic representation
     29 # used in libsepol. Instead, it is a more typical abstract representation
     30 # used by the first stage of compilers. It is basically a parse tree.
     31 #
     32 # This choice is intentional as it allows us to handle the unprocessed
     33 # M4 statements - including the $1 style arguments - and to more easily generate
     34 # the data structures that we need for policy generation.
     35 #
     36 
     37 # Constans for referring to fields
     38 SRC_TYPE  = 0
     39 TGT_TYPE  = 1
     40 OBJ_CLASS = 2
     41 PERMS     = 3
     42 ROLE      = 4
     43 DEST_TYPE = 5
     44 
     45 # String represenations of the above constants
     46 field_to_str = ["source", "target", "object", "permission", "role", "destination" ]
     47 str_to_field = { "source" : SRC_TYPE, "target" : TGT_TYPE, "object" : OBJ_CLASS,
     48                 "permission" : PERMS, "role" : ROLE, "destination" : DEST_TYPE }
     49 
     50 # Base Classes
     51 
     52 class PolicyBase:
     53     def __init__(self, parent=None):
     54         self.parent = None
     55         self.comment = None
     56 
     57 class Node(PolicyBase):
     58     """Base class objects produced from parsing the reference policy.
     59 
     60     The Node class is used as the base class for any non-leaf
     61     object produced by parsing the reference policy. This object
     62     should contain a reference to its parent (or None for a top-level
     63     object) and 0 or more children.
     64 
     65     The general idea here is to have a very simple tree structure. Children
     66     are not separated out by type. Instead the tree structure represents
     67     fairly closely the real structure of the policy statements.
     68 
     69     The object should be iterable - by default over all children but
     70     subclasses are free to provide additional iterators over a subset
     71     of their childre (see Interface for example).
     72     """
     73 
     74     def __init__(self, parent=None):
     75         PolicyBase.__init__(self, parent)
     76         self.children = []
     77 
     78     def __iter__(self):
     79         return iter(self.children)
     80 
     81     # Not all of the iterators will return something on all Nodes, but
     82     # they won't explode either. Putting them here is just easier.
     83 
     84     # Top level nodes
     85 
     86     def nodes(self):
     87         return filter(lambda x: isinstance(x, Node), walktree(self))
     88 
     89     def modules(self):
     90         return filter(lambda x: isinstance(x, Module), walktree(self))
     91 
     92     def interfaces(self):
     93         return filter(lambda x: isinstance(x, Interface), walktree(self))
     94 
     95     def templates(self):
     96         return filter(lambda x: isinstance(x, Template), walktree(self))
     97 
     98     def support_macros(self):
     99         return filter(lambda x: isinstance(x, SupportMacros), walktree(self))
    100 
    101     # Common policy statements
    102 
    103     def module_declarations(self):
    104         return filter(lambda x: isinstance(x, ModuleDeclaration), walktree(self))
    105 
    106     def interface_calls(self):
    107         return filter(lambda x: isinstance(x, InterfaceCall), walktree(self))
    108 
    109     def avrules(self):
    110         return filter(lambda x: isinstance(x, AVRule), walktree(self))
    111 
    112     def typerules(self):
    113         return filter(lambda x: isinstance(x, TypeRule), walktree(self))
    114 
    115     def typeattributes(self):
    116         """Iterate over all of the TypeAttribute children of this Interface."""
    117         return filter(lambda x: isinstance(x, TypeAttribute), walktree(self))
    118 
    119     def roleattributes(self):
    120         """Iterate over all of the RoleAttribute children of this Interface."""
    121         return filter(lambda x: isinstance(x, RoleAttribute), walktree(self))
    122 
    123     def requires(self):
    124         return filter(lambda x: isinstance(x, Require), walktree(self))
    125 
    126     def roles(self):
    127         return filter(lambda x: isinstance(x, Role), walktree(self))
    128 
    129     def role_allows(self):
    130         return filter(lambda x: isinstance(x, RoleAllow), walktree(self))
    131 
    132     def role_types(self):
    133         return filter(lambda x: isinstance(x, RoleType), walktree(self))
    134 
    135     def __str__(self):
    136         if self.comment:
    137             return str(self.comment) + "\n" + self.to_string()
    138         else:
    139             return self.to_string()
    140 
    141     def __repr__(self):
    142         return "<%s(%s)>" % (self.__class__.__name__, self.to_string())
    143 
    144     def to_string(self):
    145         return ""
    146 
    147 
    148 class Leaf(PolicyBase):
    149     def __init__(self, parent=None):
    150         PolicyBase.__init__(self, parent)
    151 
    152     def __str__(self):
    153         if self.comment:
    154             return str(self.comment) + "\n" + self.to_string()
    155         else:
    156             return self.to_string()
    157 
    158     def __repr__(self):
    159         return "<%s(%s)>" % (self.__class__.__name__, self.to_string())
    160 
    161     def to_string(self):
    162         return ""
    163 
    164 
    165 
    166 # Utility functions
    167 
    168 def walktree(node, depthfirst=True, showdepth=False, type=None):
    169     """Iterate over a Node and its Children.
    170 
    171     The walktree function iterates over a tree containing Nodes and
    172     leaf objects. The iteration can perform a depth first or a breadth
    173     first traversal of the tree (controlled by the depthfirst
    174     paramater. The passed in node will be returned.
    175 
    176     This function will only work correctly for trees - arbitrary graphs
    177     will likely cause infinite looping.
    178     """
    179     # We control depth first / versus breadth first by
    180     # how we pop items off of the node stack.
    181     if depthfirst:
    182         index = -1
    183     else:
    184         index = 0
    185 
    186     stack = [(node, 0)]
    187     while len(stack) > 0:
    188         cur, depth = stack.pop(index)
    189         if showdepth:
    190             yield cur, depth
    191         else:
    192             yield cur
    193 
    194         # If the node is not a Node instance it must
    195         # be a leaf - so no need to add it to the stack
    196         if isinstance(cur, Node):
    197             items = []
    198             i = len(cur.children) - 1
    199             while i >= 0:
    200                 if type is None or isinstance(cur.children[i], type):
    201                     items.append((cur.children[i], depth + 1))
    202                 i -= 1
    203 
    204             stack.extend(items)
    205 
    206 def walknode(node, type=None):
    207     """Iterate over the direct children of a Node.
    208 
    209     The walktree function iterates over the children of a Node.
    210     Unlike walktree it does note return the passed in node or
    211     the children of any Node objects (that is, it does not go
    212     beyond the current level in the tree).
    213     """
    214     for x in node:
    215         if type is None or isinstance(x, type):
    216             yield x
    217 
    218 
    219 def list_to_space_str(s, cont=('{', '}')):
    220     """Convert a set (or any sequence type) into a string representation
    221     formatted to match SELinux space separated list conventions.
    222 
    223     For example the list ['read', 'write'] would be converted into:
    224     '{ read write }'
    225     """
    226     l = len(s)
    227     str = ""
    228     if l < 1:
    229         raise ValueError("cannot convert 0 len set to string")
    230     str = " ".join(s)
    231     if l == 1:
    232         return str
    233     else:
    234         return cont[0] + " " + str + " " + cont[1]
    235 
    236 def list_to_comma_str(s):
    237     l = len(s)
    238     if l < 1:
    239         raise ValueError("cannot conver 0 len set to comma string")
    240 
    241     return ", ".join(s)
    242 
    243 # Basic SELinux types
    244 
    245 class IdSet(set):
    246     def __init__(self, list=None):
    247         if list:
    248             set.__init__(self, list)
    249         else:
    250             set.__init__(self)
    251         self.compliment = False
    252 
    253     def to_space_str(self):
    254         return list_to_space_str(sorted(self))
    255 
    256     def to_comma_str(self):
    257         return list_to_comma_str(sorted(self))
    258 
    259 class SecurityContext(Leaf):
    260     """An SELinux security context with optional MCS / MLS fields."""
    261     def __init__(self, context=None, parent=None):
    262         """Create a SecurityContext object, optionally from a string.
    263 
    264         Parameters:
    265            [context] - string representing a security context. Same format
    266               as a string passed to the from_string method.
    267         """
    268         Leaf.__init__(self, parent)
    269         self.user = ""
    270         self.role = ""
    271         self.type = ""
    272         self.level = None
    273         if context is not None:
    274             self.from_string(context)
    275 
    276     def from_string(self, context):
    277         """Parse a string representing a context into a SecurityContext.
    278 
    279         The string should be in the standard format - e.g.,
    280         'user:role:type:level'.
    281 
    282         Raises ValueError if the string is not parsable as a security context.
    283         """
    284         fields = context.split(":")
    285         if len(fields) < 3:
    286             raise ValueError("context string [%s] not in a valid format" % context)
    287 
    288         self.user = fields[0]
    289         self.role = fields[1]
    290         self.type = fields[2]
    291         if len(fields) > 3:
    292             # FUTURE - normalize level fields to allow more comparisons to succeed.
    293             self.level = ':'.join(fields[3:])
    294         else:
    295             self.level = None
    296 
    297     def __eq__(self, other):
    298         """Compare two SecurityContext objects - all fields must be exactly the
    299         the same for the comparison to work. It is possible for the level fields
    300         to be semantically the same yet syntactically different - in this case
    301         this function will return false.
    302         """
    303         return self.user == other.user and \
    304                self.role == other.role and \
    305                self.type == other.type and \
    306                self.level == other.level
    307 
    308     def to_string(self, default_level=None):
    309         """Return a string representing this security context.
    310 
    311         By default, the string will contiain a MCS / MLS level
    312         potentially from the default which is passed in if none was
    313         set.
    314 
    315         Arguments:
    316            default_level - the default level to use if self.level is an
    317              empty string.
    318 
    319         Returns:
    320            A string represening the security context in the form
    321               'user:role:type:level'.
    322         """
    323         fields = [self.user, self.role, self.type]
    324         if self.level is None:
    325             if default_level is None:
    326                 if selinux.is_selinux_mls_enabled() == 1:
    327                     fields.append("s0")
    328             else:
    329                 fields.append(default_level)
    330         else:
    331             fields.append(self.level)
    332         return ":".join(fields)
    333 
    334 class ObjectClass(Leaf):
    335     """SELinux object class and permissions.
    336 
    337     This class is a basic representation of an SELinux object
    338     class - it does not represent separate common permissions -
    339     just the union of the common and class specific permissions.
    340     It is meant to be convenient for policy generation.
    341     """
    342     def __init__(self, name="", parent=None):
    343         Leaf.__init__(self, parent)
    344         self.name = name
    345         self.perms = IdSet()
    346 
    347 # Basic statements
    348 
    349 class TypeAttribute(Leaf):
    350     """SElinux typeattribute statement.
    351 
    352     This class represents a typeattribute statement.
    353     """
    354     def __init__(self, parent=None):
    355         Leaf.__init__(self, parent)
    356         self.type = ""
    357         self.attributes = IdSet()
    358 
    359     def to_string(self):
    360         return "typeattribute %s %s;" % (self.type, self.attributes.to_comma_str())
    361 
    362 class RoleAttribute(Leaf):
    363     """SElinux roleattribute statement.
    364 
    365     This class represents a roleattribute statement.
    366     """
    367     def __init__(self, parent=None):
    368         Leaf.__init__(self, parent)
    369         self.role = ""
    370         self.roleattributes = IdSet()
    371 
    372     def to_string(self):
    373         return "roleattribute %s %s;" % (self.role, self.roleattributes.to_comma_str())
    374 
    375 
    376 class Role(Leaf):
    377     def __init__(self, parent=None):
    378         Leaf.__init__(self, parent)
    379         self.role = ""
    380         self.types = IdSet()
    381 
    382     def to_string(self):
    383         s = ""
    384         for t in self.types:
    385             s += "role %s types %s;\n" % (self.role, t)
    386         return s
    387 
    388 class Type(Leaf):
    389     def __init__(self, name="", parent=None):
    390         Leaf.__init__(self, parent)
    391         self.name = name
    392         self.attributes = IdSet()
    393         self.aliases = IdSet()
    394 
    395     def to_string(self):
    396         s = "type %s" % self.name
    397         if len(self.aliases) > 0:
    398             s = s + "alias %s" % self.aliases.to_space_str()
    399         if len(self.attributes) > 0:
    400             s = s + ", %s" % self.attributes.to_comma_str()
    401         return s + ";"
    402 
    403 class TypeAlias(Leaf):
    404     def __init__(self, parent=None):
    405         Leaf.__init__(self, parent)
    406         self.type = ""
    407         self.aliases = IdSet()
    408 
    409     def to_string(self):
    410         return "typealias %s alias %s;" % (self.type, self.aliases.to_space_str())
    411 
    412 class Attribute(Leaf):
    413     def __init__(self, name="", parent=None):
    414         Leaf.__init__(self, parent)
    415         self.name = name
    416 
    417     def to_string(self):
    418         return "attribute %s;" % self.name
    419 
    420 class Attribute_Role(Leaf):
    421     def __init__(self, name="", parent=None):
    422         Leaf.__init__(self, parent)
    423         self.name = name
    424 
    425     def to_string(self):
    426         return "attribute_role %s;" % self.name
    427 
    428 
    429 # Classes representing rules
    430 
    431 class AVRule(Leaf):
    432     """SELinux access vector (AV) rule.
    433 
    434     The AVRule class represents all varieties of AV rules including
    435     allow, dontaudit, and auditallow (indicated by the flags self.ALLOW,
    436     self.DONTAUDIT, and self.AUDITALLOW respectively).
    437 
    438     The source and target types, object classes, and perms are all represented
    439     by sets containing strings. Sets are used to make it simple to add
    440     strings repeatedly while avoiding duplicates.
    441 
    442     No checking is done to make certain that the symbols are valid or
    443     consistent (e.g., perms that don't match the object classes). It is
    444     even possible to put invalid types like '$1' into the rules to allow
    445     storage of the reference policy interfaces.
    446     """
    447     ALLOW = 0
    448     DONTAUDIT = 1
    449     AUDITALLOW = 2
    450     NEVERALLOW = 3
    451 
    452     def __init__(self, av=None, parent=None):
    453         Leaf.__init__(self, parent)
    454         self.src_types = IdSet()
    455         self.tgt_types = IdSet()
    456         self.obj_classes = IdSet()
    457         self.perms = IdSet()
    458         self.rule_type = self.ALLOW
    459         if av:
    460             self.from_av(av)
    461 
    462     def __rule_type_str(self):
    463         if self.rule_type == self.ALLOW:
    464             return "allow"
    465         elif self.rule_type == self.DONTAUDIT:
    466             return "dontaudit"
    467         else:
    468             return "auditallow"
    469 
    470     def from_av(self, av):
    471         """Add the access from an access vector to this allow
    472         rule.
    473         """
    474         self.src_types.add(av.src_type)
    475         if av.src_type == av.tgt_type:
    476             self.tgt_types.add("self")
    477         else:
    478             self.tgt_types.add(av.tgt_type)
    479         self.obj_classes.add(av.obj_class)
    480         self.perms.update(av.perms)
    481 
    482     def to_string(self):
    483         """Return a string representation of the rule
    484         that is a valid policy language representation (assuming
    485         that the types, object class, etc. are valie).
    486         """
    487         return "%s %s %s:%s %s;" % (self.__rule_type_str(),
    488                                      self.src_types.to_space_str(),
    489                                      self.tgt_types.to_space_str(),
    490                                      self.obj_classes.to_space_str(),
    491                                      self.perms.to_space_str())
    492 class TypeRule(Leaf):
    493     """SELinux type rules.
    494 
    495     This class is very similar to the AVRule class, but is for representing
    496     the type rules (type_trans, type_change, and type_member). The major
    497     difference is the lack of perms and only and sing destination type.
    498     """
    499     TYPE_TRANSITION = 0
    500     TYPE_CHANGE = 1
    501     TYPE_MEMBER = 2
    502 
    503     def __init__(self, parent=None):
    504         Leaf.__init__(self, parent)
    505         self.src_types = IdSet()
    506         self.tgt_types = IdSet()
    507         self.obj_classes = IdSet()
    508         self.dest_type = ""
    509         self.rule_type = self.TYPE_TRANSITION
    510 
    511     def __rule_type_str(self):
    512         if self.rule_type == self.TYPE_TRANSITION:
    513             return "type_transition"
    514         elif self.rule_type == self.TYPE_CHANGE:
    515             return "type_change"
    516         else:
    517             return "type_member"
    518 
    519     def to_string(self):
    520         return "%s %s %s:%s %s;" % (self.__rule_type_str(),
    521                                      self.src_types.to_space_str(),
    522                                      self.tgt_types.to_space_str(),
    523                                      self.obj_classes.to_space_str(),
    524                                      self.dest_type)
    525 
    526 class RoleAllow(Leaf):
    527     def __init__(self, parent=None):
    528         Leaf.__init__(self, parent)
    529         self.src_roles = IdSet()
    530         self.tgt_roles = IdSet()
    531 
    532     def to_string(self):
    533         return "allow %s %s;" % (self.src_roles.to_comma_str(),
    534                                  self.tgt_roles.to_comma_str())
    535 
    536 class RoleType(Leaf):
    537     def __init__(self, parent=None):
    538         Leaf.__init__(self, parent)
    539         self.role = ""
    540         self.types = IdSet()
    541 
    542     def to_string(self):
    543         s = ""
    544         for t in self.types:
    545             s += "role %s types %s;\n" % (self.role, t)
    546         return s
    547 
    548 class ModuleDeclaration(Leaf):
    549     def __init__(self, parent=None):
    550         Leaf.__init__(self, parent)
    551         self.name = ""
    552         self.version = ""
    553         self.refpolicy = False
    554 
    555     def to_string(self):
    556         if self.refpolicy:
    557             return "policy_module(%s, %s)" % (self.name, self.version)
    558         else:
    559             return "module %s %s;" % (self.name, self.version)
    560 
    561 class Conditional(Node):
    562     def __init__(self, parent=None):
    563         Node.__init__(self, parent)
    564         self.cond_expr = []
    565 
    566     def to_string(self):
    567         return "[If %s]" % list_to_space_str(self.cond_expr, cont=("", ""))
    568 
    569 class Bool(Leaf):
    570     def __init__(self, parent=None):
    571         Leaf.__init__(self, parent)
    572         self.name = ""
    573         self.state = False
    574 
    575     def to_string(self):
    576         s = "bool %s " % self.name
    577         if s.state:
    578             return s + "true"
    579         else:
    580             return s + "false"
    581 
    582 class InitialSid(Leaf):
    583     def __init(self, parent=None):
    584         Leaf.__init__(self, parent)
    585         self.name = ""
    586         self.context = None
    587 
    588     def to_string(self):
    589         return "sid %s %s" % (self.name, str(self.context))
    590 
    591 class GenfsCon(Leaf):
    592     def __init__(self, parent=None):
    593         Leaf.__init__(self, parent)
    594         self.filesystem = ""
    595         self.path = ""
    596         self.context = None
    597 
    598     def to_string(self):
    599         return "genfscon %s %s %s" % (self.filesystem, self.path, str(self.context))
    600 
    601 class FilesystemUse(Leaf):
    602     XATTR = 1
    603     TRANS = 2
    604     TASK = 3
    605     
    606     def __init__(self, parent=None):
    607         Leaf.__init__(self, parent)
    608         self.type = self.XATTR
    609         self.filesystem = ""
    610         self.context = None
    611 
    612     def to_string(self):
    613         s = ""
    614         if self.type == XATTR:
    615             s = "fs_use_xattr "
    616         elif self.type == TRANS:
    617             s = "fs_use_trans "
    618         elif self.type == TASK:
    619             s = "fs_use_task "
    620 
    621         return "%s %s %s;" % (s, self.filesystem, str(self.context))
    622 
    623 class PortCon(Leaf):
    624     def __init__(self, parent=None):
    625         Leaf.__init__(self, parent)
    626         self.port_type = ""
    627         self.port_number = ""
    628         self.context = None
    629 
    630     def to_string(self):
    631         return "portcon %s %s %s" % (self.port_type, self.port_number, str(self.context))
    632 
    633 class NodeCon(Leaf):
    634     def __init__(self, parent=None):
    635         Leaf.__init__(self, parent)
    636         self.start = ""
    637         self.end = ""
    638         self.context = None
    639 
    640     def to_string(self):
    641         return "nodecon %s %s %s" % (self.start, self.end, str(self.context))
    642 
    643 class NetifCon(Leaf):
    644     def __init__(self, parent=None):
    645         Leaf.__init__(self, parent)
    646         self.interface = ""
    647         self.interface_context = None
    648         self.packet_context = None
    649 
    650     def to_string(self):
    651         return "netifcon %s %s %s" % (self.interface, str(self.interface_context),
    652                                    str(self.packet_context))
    653 class PirqCon(Leaf):
    654     def __init__(self, parent=None):
    655         Leaf.__init__(self, parent)
    656         self.pirq_number = ""
    657         self.context = None
    658 
    659     def to_string(self):
    660         return "pirqcon %s %s" % (self.pirq_number, str(self.context))
    661 
    662 class IomemCon(Leaf):
    663     def __init__(self, parent=None):
    664         Leaf.__init__(self, parent)
    665         self.device_mem = ""
    666         self.context = None
    667 
    668     def to_string(self):
    669         return "iomemcon %s %s" % (self.device_mem, str(self.context))
    670 
    671 class IoportCon(Leaf):
    672     def __init__(self, parent=None):
    673         Leaf.__init__(self, parent)
    674         self.ioport = ""
    675         self.context = None
    676 
    677     def to_string(self):
    678         return "ioportcon %s %s" % (self.ioport, str(self.context))
    679 
    680 class PciDeviceCon(Leaf):
    681     def __init__(self, parent=None):
    682         Leaf.__init__(self, parent)
    683         self.device = ""
    684         self.context = None
    685 
    686     def to_string(self):
    687         return "pcidevicecon %s %s" % (self.device, str(self.context))
    688 
    689 class DeviceTreeCon(Leaf):
    690     def __init__(self, parent=None):
    691         Leaf.__init__(self, parent)
    692         self.path = ""
    693         self.context = None
    694 
    695     def to_string(self):
    696         return "devicetreecon %s %s" % (self.path, str(self.context))
    697 
    698 # Reference policy specific types
    699 
    700 def print_tree(head):
    701     for node, depth in walktree(head, showdepth=True):
    702         s = ""
    703         for i in range(depth):
    704             s = s + "\t"
    705         print(s + str(node))
    706 
    707 
    708 class Headers(Node):
    709     def __init__(self, parent=None):
    710         Node.__init__(self, parent)
    711 
    712     def to_string(self):
    713         return "[Headers]"
    714 
    715 
    716 class Module(Node):
    717     def __init__(self, parent=None):
    718         Node.__init__(self, parent)
    719 
    720     def to_string(self):
    721         return ""
    722 
    723 class Interface(Node):
    724     """A reference policy interface definition.
    725 
    726     This class represents a reference policy interface definition.
    727     """
    728     def __init__(self, name="", parent=None):
    729         Node.__init__(self, parent)
    730         self.name = name
    731 
    732     def to_string(self):
    733         return "[Interface name: %s]" % self.name
    734 
    735 class TunablePolicy(Node):
    736     def __init__(self, parent=None):
    737         Node.__init__(self, parent)
    738         self.cond_expr = []
    739 
    740     def to_string(self):
    741         return "[Tunable Policy %s]" % list_to_space_str(self.cond_expr, cont=("", ""))
    742 
    743 class Template(Node):
    744     def __init__(self, name="", parent=None):
    745         Node.__init__(self, parent)
    746         self.name = name
    747 
    748     def to_string(self):
    749         return "[Template name: %s]" % self.name
    750 
    751 class IfDef(Node):
    752     def __init__(self, name="", parent=None):
    753         Node.__init__(self, parent)
    754         self.name = name
    755 
    756     def to_string(self):
    757         return "[Ifdef name: %s]" % self.name
    758 
    759 class InterfaceCall(Leaf):
    760     def __init__(self, ifname="", parent=None):
    761         Leaf.__init__(self, parent)
    762         self.ifname = ifname
    763         self.args = []
    764         self.comments = []
    765 
    766     def matches(self, other):
    767         if self.ifname != other.ifname:
    768             return False
    769         if len(self.args) != len(other.args):
    770             return False
    771         for a,b in zip(self.args, other.args):
    772             if a != b:
    773                 return False
    774         return True
    775 
    776     def to_string(self):
    777         s = "%s(" % self.ifname
    778         i = 0
    779         for a in self.args:
    780             if isinstance(a, list):
    781                 str = list_to_space_str(a)
    782             else:
    783                 str = a
    784                 
    785             if i != 0:
    786                 s = s + ", %s" % str
    787             else:
    788                 s = s + str
    789             i += 1
    790         return s + ")"
    791 
    792 class OptionalPolicy(Node):
    793     def __init__(self, parent=None):
    794         Node.__init__(self, parent)
    795 
    796     def to_string(self):
    797         return "[Optional Policy]"
    798 
    799 class SupportMacros(Node):
    800     def __init__(self, parent=None):
    801         Node.__init__(self, parent)
    802         self.map = None
    803 
    804     def to_string(self):
    805         return "[Support Macros]"
    806 
    807     def __expand_perm(self, perm):
    808         # Recursive expansion - the assumption is that these
    809         # are ordered correctly so that no macro is used before
    810         # it is defined
    811         s = set()
    812         if perm in self.map:
    813             for p in self.by_name(perm):
    814                 s.update(self.__expand_perm(p))
    815         else:
    816             s.add(perm)
    817         return s
    818 
    819     def __gen_map(self):
    820         self.map = {}
    821         for x in self:
    822             exp_perms = set()
    823             for perm in x.perms:
    824                 exp_perms.update(self.__expand_perm(perm))
    825             self.map[x.name] = exp_perms
    826 
    827     def by_name(self, name):
    828         if not self.map:
    829             self.__gen_map()
    830         return self.map[name]
    831 
    832     def has_key(self, name):
    833         if not self.map:
    834             self.__gen_map()
    835         return name in self.map
    836 
    837 class Require(Leaf):
    838     def __init__(self, parent=None):
    839         Leaf.__init__(self, parent)
    840         self.types = IdSet()
    841         self.obj_classes = { }
    842         self.roles = IdSet()
    843         self.data = IdSet()
    844         self.users = IdSet()
    845 
    846     def add_obj_class(self, obj_class, perms):
    847         p = self.obj_classes.setdefault(obj_class, IdSet())
    848         p.update(perms)
    849 
    850 
    851     def to_string(self):
    852         s = []
    853         s.append("require {")
    854         for type in self.types:
    855             s.append("\ttype %s;" % type)
    856         for obj_class, perms in self.obj_classes.items():
    857             s.append("\tclass %s %s;" % (obj_class, perms.to_space_str()))
    858         for role in self.roles:
    859             s.append("\trole %s;" % role)
    860         for bool in self.data:
    861             s.append("\tbool %s;" % bool)
    862         for user in self.users:
    863             s.append("\tuser %s;" % user)
    864         s.append("}")
    865 
    866         # Handle empty requires
    867         if len(s) == 2:
    868             return ""
    869 
    870         return "\n".join(s)
    871 
    872 
    873 class ObjPermSet:
    874     def __init__(self, name):
    875         self.name = name
    876         self.perms = set()
    877 
    878     def to_string(self):
    879         return "define(`%s', `%s')" % (self.name, self.perms.to_space_str())
    880 
    881 class ClassMap:
    882     def __init__(self, obj_class, perms):
    883         self.obj_class = obj_class
    884         self.perms = perms
    885 
    886     def to_string(self):
    887         return self.obj_class + ": " + self.perms
    888 
    889 class Comment:
    890     def __init__(self, l=None):
    891         if l:
    892             self.lines = l
    893         else:
    894             self.lines = []
    895 
    896     def to_string(self):
    897         # If there are no lines, treat this as a spacer between
    898         # policy statements and return a new line.
    899         if len(self.lines) == 0:
    900             return ""
    901         else:
    902             out = []
    903             for line in self.lines:
    904                 out.append("#" + line)
    905             return "\n".join(out)
    906 
    907     def merge(self, other):
    908         if len(other.lines):
    909             for line in other.lines:
    910                 if line != "":
    911                     self.lines.append(line)
    912 
    913     def __str__(self):
    914         return self.to_string()
    915 
    916 
    917