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