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