xref: /aosp_15_r20/external/selinux/python/sepolgen/src/sepolgen/refpolicy.py (revision 2d543d20722ada2425b5bdab9d0d1d29470e7bba)
1# Authors: Karl MacMillan <[email protected]>
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# Constants for referring to fields
38SRC_TYPE  = 0
39TGT_TYPE  = 1
40OBJ_CLASS = 2
41PERMS     = 3
42ROLE      = 4
43DEST_TYPE = 5
44
45# String representations 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        self.gen_cil = False
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 filter(lambda x: isinstance(x, Node), walktree(self))
89
90    def modules(self):
91        return filter(lambda x: isinstance(x, Module), walktree(self))
92
93    def interfaces(self):
94        return filter(lambda x: isinstance(x, Interface), walktree(self))
95
96    def templates(self):
97        return filter(lambda x: isinstance(x, Template), walktree(self))
98
99    def support_macros(self):
100        return filter(lambda x: isinstance(x, SupportMacros), walktree(self))
101
102    # Common policy statements
103
104    def module_declarations(self):
105        return filter(lambda x: isinstance(x, ModuleDeclaration), walktree(self))
106
107    def interface_calls(self):
108        return filter(lambda x: isinstance(x, InterfaceCall), walktree(self))
109
110    def avrules(self):
111        return filter(lambda x: isinstance(x, AVRule), walktree(self))
112
113    def avextrules(self):
114        return filter(lambda x: isinstance(x, AVExtRule), walktree(self))
115
116    def typerules(self):
117        return filter(lambda x: isinstance(x, TypeRule), walktree(self))
118
119    def typebounds(self):
120        return filter(lambda x: isinstance(x, TypeBound), walktree(self))
121
122    def typeattributes(self):
123        """Iterate over all of the TypeAttribute children of this Interface."""
124        return filter(lambda x: isinstance(x, TypeAttribute), walktree(self))
125
126    def roleattributes(self):
127        """Iterate over all of the RoleAttribute children of this Interface."""
128        return filter(lambda x: isinstance(x, RoleAttribute), walktree(self))
129
130    def requires(self):
131        return filter(lambda x: isinstance(x, Require), walktree(self))
132
133    def roles(self):
134        return filter(lambda x: isinstance(x, Role), walktree(self))
135
136    def role_allows(self):
137        return filter(lambda x: isinstance(x, RoleAllow), walktree(self))
138
139    def role_types(self):
140        return filter(lambda x: isinstance(x, RoleType), walktree(self))
141
142    def __str__(self):
143        if self.comment:
144            return str(self.comment) + "\n" + self.to_string()
145        else:
146            return self.to_string()
147
148    def __repr__(self):
149        return "<%s(%s)>" % (self.__class__.__name__, self.to_string())
150
151    def to_string(self):
152        return ""
153
154    def set_gen_cil(self, gen_cil):
155        self.gen_cil = gen_cil
156
157class Leaf(PolicyBase):
158    def __init__(self, parent=None):
159        PolicyBase.__init__(self, parent)
160
161    def __str__(self):
162        if self.comment:
163            return str(self.comment) + "\n" + self.to_string()
164        else:
165            return self.to_string()
166
167    def __repr__(self):
168        return "<%s(%s)>" % (self.__class__.__name__, self.to_string())
169
170    def to_string(self):
171        return ""
172
173    def set_gen_cil(self, gen_cil):
174        self.gen_cil = gen_cil
175
176
177# Utility functions
178
179def walktree(node, depthfirst=True, showdepth=False, type=None):
180    """Iterate over a Node and its Children.
181
182    The walktree function iterates over a tree containing Nodes and
183    leaf objects. The iteration can perform a depth first or a breadth
184    first traversal of the tree (controlled by the depthfirst
185    parameter. The passed in node will be returned.
186
187    This function will only work correctly for trees - arbitrary graphs
188    will likely cause infinite looping.
189    """
190    # We control depth first / versus breadth first by
191    # how we pop items off of the node stack.
192    if depthfirst:
193        index = -1
194    else:
195        index = 0
196
197    stack = [(node, 0)]
198    while len(stack) > 0:
199        cur, depth = stack.pop(index)
200        if showdepth:
201            yield cur, depth
202        else:
203            yield cur
204
205        # If the node is not a Node instance it must
206        # be a leaf - so no need to add it to the stack
207        if isinstance(cur, Node):
208            items = []
209            i = len(cur.children) - 1
210            while i >= 0:
211                if type is None or isinstance(cur.children[i], type):
212                    items.append((cur.children[i], depth + 1))
213                i -= 1
214
215            stack.extend(items)
216
217def walknode(node, type=None):
218    """Iterate over the direct children of a Node.
219
220    The walktree function iterates over the children of a Node.
221    Unlike walktree it does note return the passed in node or
222    the children of any Node objects (that is, it does not go
223    beyond the current level in the tree).
224    """
225    for x in node:
226        if type is None or isinstance(x, type):
227            yield x
228
229
230def list_to_space_str(s, cont=('{', '}')):
231    """Convert a set (or any sequence type) into a string representation
232    formatted to match SELinux space separated list conventions.
233
234    For example the list ['read', 'write'] would be converted into:
235    '{ read write }'
236    """
237    l = len(s)
238    str = ""
239    if l < 1:
240        raise ValueError("cannot convert 0 len set to string")
241    str = " ".join(s)
242    if l == 1:
243        return str
244    else:
245        return cont[0] + " " + str + " " + cont[1]
246
247def list_to_comma_str(s):
248    l = len(s)
249    if l < 1:
250        raise ValueError("cannot convert 0 len set to comma string")
251
252    return ", ".join(s)
253
254# Basic SELinux types
255
256class IdSet(set):
257    def __init__(self, list=None):
258        if list:
259            set.__init__(self, list)
260        else:
261            set.__init__(self)
262        self.compliment = False
263
264    def to_space_str(self):
265        return list_to_space_str(sorted(self))
266
267    def to_comma_str(self):
268        return list_to_comma_str(sorted(self))
269
270class SecurityContext(Leaf):
271    """An SELinux security context with optional MCS / MLS fields."""
272    def __init__(self, context=None, parent=None):
273        """Create a SecurityContext object, optionally from a string.
274
275        Parameters:
276           [context] - string representing a security context. Same format
277              as a string passed to the from_string method.
278        """
279        Leaf.__init__(self, parent)
280        self.user = ""
281        self.role = ""
282        self.type = ""
283        self.level = None
284        if context is not None:
285            self.from_string(context)
286
287    def from_string(self, context):
288        """Parse a string representing a context into a SecurityContext.
289
290        The string should be in the standard format - e.g.,
291        'user:role:type:level'.
292
293        Raises ValueError if the string is not parsable as a security context.
294        """
295        # try to translate the context string to raw form
296        raw = selinux.selinux_trans_to_raw_context(context)
297        if raw[0] == 0:
298            context = raw[1]
299
300        fields = context.split(":")
301        if len(fields) < 3:
302            raise ValueError("context string [%s] not in a valid format" % context)
303
304        self.user = fields[0]
305        self.role = fields[1]
306        self.type = fields[2]
307        if len(fields) > 3:
308            # FUTURE - normalize level fields to allow more comparisons to succeed.
309            self.level = ':'.join(fields[3:])
310        else:
311            self.level = None
312
313    def __eq__(self, other):
314        """Compare two SecurityContext objects - all fields must be exactly the
315        the same for the comparison to work. It is possible for the level fields
316        to be semantically the same yet syntactically different - in this case
317        this function will return false.
318        """
319        return self.user == other.user and \
320               self.role == other.role and \
321               self.type == other.type and \
322               self.level == other.level
323
324    def to_string(self, default_level=None):
325        """Return a string representing this security context.
326
327        By default, the string will contiain a MCS / MLS level
328        potentially from the default which is passed in if none was
329        set.
330
331        Arguments:
332           default_level - the default level to use if self.level is an
333             empty string.
334
335        Returns:
336           A string represening the security context in the form
337              'user:role:type:level'.
338        """
339        fields = [self.user, self.role, self.type]
340        if self.level is None:
341            if default_level is None:
342                if selinux.is_selinux_mls_enabled() == 1:
343                    fields.append("s0")
344            else:
345                fields.append(default_level)
346        else:
347            fields.append(self.level)
348        return ":".join(fields)
349
350class ObjectClass(Leaf):
351    """SELinux object class and permissions.
352
353    This class is a basic representation of an SELinux object
354    class - it does not represent separate common permissions -
355    just the union of the common and class specific permissions.
356    It is meant to be convenient for policy generation.
357    """
358    def __init__(self, name="", parent=None):
359        Leaf.__init__(self, parent)
360        self.name = name
361        self.perms = IdSet()
362
363class XpermSet():
364    """Extended permission set.
365
366    This class represents one or more extended permissions
367    represented by numeric values or ranges of values. The
368    .complement attribute is used to specify all permission
369    except those specified.
370
371    Two xperm set can be merged using the .extend() method.
372    """
373    def __init__(self, complement=False):
374        self.complement = complement
375        self.ranges = []
376
377    def __normalize_ranges(self):
378        """Ensure that ranges are not overlapping.
379        """
380        self.ranges.sort()
381
382        i = 0
383        while i < len(self.ranges):
384            while i + 1 < len(self.ranges):
385                if self.ranges[i + 1][0] <= self.ranges[i][1] + 1:
386                    self.ranges[i] = (self.ranges[i][0], max(self.ranges[i][1],
387                                                             self.ranges[i + 1][1]))
388                    del self.ranges[i + 1]
389                else:
390                    break
391            i += 1
392
393    def extend(self, s):
394        """Add ranges from an xperm set
395        """
396        self.ranges.extend(s.ranges)
397        self.__normalize_ranges()
398
399    def add(self, minimum, maximum=None):
400        """Add value of range of values to the xperm set.
401        """
402        if maximum is None:
403            maximum = minimum
404        self.ranges.append((minimum, maximum))
405        self.__normalize_ranges()
406
407    def to_string(self):
408        if not self.ranges:
409            return ""
410
411        compl = "~ " if self.complement else ""
412
413        # print single value without braces
414        if len(self.ranges) == 1 and self.ranges[0][0] == self.ranges[0][1]:
415            return compl + hex(self.ranges[0][0])
416
417        vals = map(lambda x: hex(x[0]) if x[0] == x[1] else "%s-%s" % (hex(x[0]), hex(x[1]), ), self.ranges)
418
419        return "%s{ %s }" % (compl, " ".join(vals))
420
421    def to_string_cil(self):
422        if not self.ranges:
423            return ""
424
425        compl = ("not (", ")") if self.complement else ("", "")
426
427        vals = map(lambda x: hex(x[0]) if x[0] == x[1] else "(range %s %s)" % (hex(x[0]), hex(x[1]), ), self.ranges)
428
429        return "(%s%s%s)" % (compl[0], " ".join(vals), compl[1])
430
431# Basic statements
432
433class TypeAttribute(Leaf):
434    """SElinux typeattribute statement.
435
436    This class represents a typeattribute statement.
437    """
438    def __init__(self, parent=None):
439        Leaf.__init__(self, parent)
440        self.type = ""
441        self.attributes = IdSet()
442
443    def to_string(self):
444        if self.gen_cil:
445            s = ""
446            for a in self.attributes:
447                s += "(typeattribute %s)\n" % a
448                s += "(typeattributeset %s %s)\n" % (a, self.type)
449            return s
450        else:
451            return "typeattribute %s %s;" % (self.type, self.attributes.to_comma_str())
452
453class RoleAttribute(Leaf):
454    """SElinux roleattribute statement.
455
456    This class represents a roleattribute statement.
457    """
458    def __init__(self, parent=None):
459        Leaf.__init__(self, parent)
460        self.role = ""
461        self.roleattributes = IdSet()
462
463    def to_string(self):
464        if self.gen_cil:
465            s = ""
466            for a in self.roleattributes:
467                s += "(roleattribute %s)\n" % a
468                s += "(roleattributeset %s %s)\n" % (a, self.type)
469            return s
470        else:
471            return "roleattribute %s %s;" % (self.role, self.roleattributes.to_comma_str())
472
473
474class Role(Leaf):
475    def __init__(self, parent=None):
476        Leaf.__init__(self, parent)
477        self.role = ""
478        self.types = IdSet()
479
480    def to_string(self):
481        if self.gen_cil:
482            s = "(role %s)\n" % self.role
483            for t in self.types:
484                s += "(roletype %s %s)\n" % (self.role, t)
485            return s
486        else:
487            s = ""
488            for t in self.types:
489                s += "role %s types %s;\n" % (self.role, t)
490            return s
491
492class Type(Leaf):
493    def __init__(self, name="", parent=None):
494        Leaf.__init__(self, parent)
495        self.name = name
496        self.attributes = IdSet()
497        self.aliases = IdSet()
498
499    def to_string(self):
500        if self.gen_cil:
501            s = "(type %s)\n" % self.name
502            for a in self.aliases:
503                s += "(typealiasactual %s %s)\n" % (a, self.name)
504            for a in self.attributes:
505                s += "(typeattributeset %s %s)\n" % (a, self.name)
506            return s
507        else:
508            s = "type %s" % self.name
509            if len(self.aliases) > 0:
510                s = s + "alias %s" % self.aliases.to_space_str()
511            if len(self.attributes) > 0:
512                s = s + ", %s" % self.attributes.to_comma_str()
513            return s + ";"
514
515class TypeAlias(Leaf):
516    def __init__(self, parent=None):
517        Leaf.__init__(self, parent)
518        self.type = ""
519        self.aliases = IdSet()
520
521    def to_string(self):
522        if self.gen_cil:
523            s = ""
524            for a in self.aliases:
525                s += "(typealias %s)\n" % a
526                s += "(typealiasactual %s %s)\n" % (a, self.type)
527            return s
528        else:
529            return "typealias %s alias %s;" % (self.type, self.aliases.to_space_str())
530
531class Attribute(Leaf):
532    def __init__(self, name="", parent=None):
533        Leaf.__init__(self, parent)
534        self.name = name
535
536    def to_string(self):
537        if self.gen_cil:
538            return "attribute %s;" % self.name
539        else:
540            return "(typeattribute %s)" % self.name
541
542class Attribute_Role(Leaf):
543    def __init__(self, name="", parent=None):
544        Leaf.__init__(self, parent)
545        self.name = name
546
547    def to_string(self):
548        if self.gen_cil:
549            return "(roleattribute %s)" % self.name
550        else:
551            return "attribute_role %s;" % self.name
552
553
554# Classes representing rules
555
556class AVRule(Leaf):
557    """SELinux access vector (AV) rule.
558
559    The AVRule class represents all varieties of AV rules including
560    allow, dontaudit, and auditallow (indicated by the flags self.ALLOW,
561    self.DONTAUDIT, and self.AUDITALLOW respectively).
562
563    The source and target types, object classes, and perms are all represented
564    by sets containing strings. Sets are used to make it simple to add
565    strings repeatedly while avoiding duplicates.
566
567    No checking is done to make certain that the symbols are valid or
568    consistent (e.g., perms that don't match the object classes). It is
569    even possible to put invalid types like '$1' into the rules to allow
570    storage of the reference policy interfaces.
571    """
572    ALLOW = 0
573    DONTAUDIT = 1
574    AUDITALLOW = 2
575    NEVERALLOW = 3
576
577    def __init__(self, av=None, parent=None):
578        Leaf.__init__(self, parent)
579        self.src_types = IdSet()
580        self.tgt_types = IdSet()
581        self.obj_classes = IdSet()
582        self.perms = IdSet()
583        self.rule_type = self.ALLOW
584        if av:
585            self.from_av(av)
586
587    def __rule_type_str(self):
588        if self.rule_type == self.ALLOW:
589            return "allow"
590        elif self.rule_type == self.DONTAUDIT:
591            return "dontaudit"
592        elif self.rule_type == self.AUDITALLOW:
593            return "auditallow"
594        elif self.rule_type == self.NEVERALLOW:
595            return "neverallow"
596
597    def from_av(self, av):
598        """Add the access from an access vector to this allow
599        rule.
600        """
601        self.src_types.add(av.src_type)
602        if av.src_type == av.tgt_type:
603            self.tgt_types.add("self")
604        else:
605            self.tgt_types.add(av.tgt_type)
606        self.obj_classes.add(av.obj_class)
607        self.perms.update(av.perms)
608
609    def to_string(self):
610        """Return a string representation of the rule
611        that is a valid policy language representation (assuming
612        that the types, object class, etc. are valie).
613        """
614        if self.gen_cil:
615            s = ""
616            for src in self.src_types:
617                for tgt in self.tgt_types:
618                    for obj in self.obj_classes:
619                        s += "(%s %s %s (%s (%s)))" % (self.__rule_type_str(),
620                                                       src, tgt, obj,
621                                                       " ".join(self.perms))
622            return s
623        else:
624            return "%s %s %s:%s %s;" % (self.__rule_type_str(),
625                                        self.src_types.to_space_str(),
626                                        self.tgt_types.to_space_str(),
627                                        self.obj_classes.to_space_str(),
628                                        self.perms.to_space_str())
629
630class AVExtRule(Leaf):
631    """Extended permission access vector rule.
632
633    The AVExtRule class represents allowxperm, dontauditxperm,
634    auditallowxperm, and neverallowxperm rules.
635
636    The source and target types, and object classes are represented
637    by sets containing strings. The operation is a single string,
638    e.g. 'ioctl'. Extended permissions are represented by an XpermSet.
639    """
640    ALLOWXPERM = 0
641    DONTAUDITXPERM = 1
642    AUDITALLOWXPERM = 2
643    NEVERALLOWXPERM = 3
644
645    def __init__(self, av=None, op=None, parent=None):
646        Leaf.__init__(self, parent)
647        self.src_types = IdSet()
648        self.tgt_types = IdSet()
649        self.obj_classes = IdSet()
650        self.rule_type = self.ALLOWXPERM
651        self.xperms = XpermSet()
652        self.operation = op
653        if av:
654            self.from_av(av, op)
655
656    def __rule_type_str(self):
657        if self.rule_type == self.ALLOWXPERM:
658            return "allowxperm"
659        elif self.rule_type == self.DONTAUDITXPERM:
660            return "dontauditxperm"
661        elif self.rule_type == self.AUDITALLOWXPERM:
662            return "auditallowxperm"
663        elif self.rule_type == self.NEVERALLOWXPERM:
664            return "neverallowxperm"
665
666    def __rule_type_str_cil(self):
667        if self.rule_type == self.ALLOWXPERM:
668            return "allowx"
669        elif self.rule_type == self.DONTAUDITXPERM:
670            return "dontauditx"
671        elif self.rule_type == self.AUDITALLOWXPERM:
672            return "auditallowx"
673        elif self.rule_type == self.NEVERALLOWXPERM:
674            return "neverallowx"
675
676    def from_av(self, av, op):
677        self.src_types.add(av.src_type)
678        if av.src_type == av.tgt_type:
679            self.tgt_types.add("self")
680        else:
681            self.tgt_types.add(av.tgt_type)
682        self.obj_classes.add(av.obj_class)
683        self.operation = op
684        self.xperms = av.xperms[op]
685
686    def to_string(self):
687        """Return a string representation of the rule that is
688        a valid policy language representation (assuming that
689        the types, object class, etc. are valid).
690        """
691        if self.gen_cil:
692            s = ""
693            for src in self.src_types:
694                for tgt in self.tgt_types:
695                    for obj in self.obj_classes:
696                        s += "(%s %s %s (%s %s %s))" % (self.__rule_type_str_cil(),
697                                                        src, tgt,
698                                                        self.operation,
699                                                        obj,
700                                                        self.xperms.to_string_cil())
701            return s
702        else:
703            return "%s %s %s:%s %s %s;" % (self.__rule_type_str(),
704                                           self.src_types.to_space_str(),
705                                           self.tgt_types.to_space_str(),
706                                           self.obj_classes.to_space_str(),
707                                           self.operation,
708                                           self.xperms.to_string())
709
710
711class TypeRule(Leaf):
712    """SELinux type rules.
713
714    This class is very similar to the AVRule class, but is for representing
715    the type rules (type_trans, type_change, and type_member). The major
716    difference is the lack of perms and only and sing destination type.
717    """
718    TYPE_TRANSITION = 0
719    TYPE_CHANGE = 1
720    TYPE_MEMBER = 2
721
722    # NB. Filename type transitions are not generated by audit2allow.
723    def __init__(self, parent=None):
724        Leaf.__init__(self, parent)
725        self.src_types = IdSet()
726        self.tgt_types = IdSet()
727        self.obj_classes = IdSet()
728        self.dest_type = ""
729        self.rule_type = self.TYPE_TRANSITION
730
731    def __rule_type_str(self):
732        if self.rule_type == self.TYPE_TRANSITION:
733            return "type_transition"
734        elif self.rule_type == self.TYPE_CHANGE:
735            return "type_change"
736        else:
737            return "type_member"
738
739    def __rule_type_str_cil(self):
740        if self.rule_type == self.TYPE_TRANSITION:
741            return "typetransition"
742        elif self.rule_type == self.TYPE_CHANGE:
743            return "typechange"
744        else:
745            return "typemember"
746
747    def to_string(self):
748        if self.gen_cil:
749            return "(%s %s %s %s %s)" % (self.__rule_type_str_cil(),
750                                         self.src_types.to_space_str(),
751                                         self.tgt_types.to_space_str(),
752                                         self.obj_classes.to_space_str(),
753                                         self.dest_type)
754        else:
755            return "%s %s %s:%s %s;" % (self.__rule_type_str(),
756                                        self.src_types.to_space_str(),
757                                        self.tgt_types.to_space_str(),
758                                        self.obj_classes.to_space_str(),
759                                        self.dest_type)
760
761class TypeBound(Leaf):
762    """SElinux typebound statement.
763
764    This class represents a typebound statement.
765    """
766    def __init__(self, parent=None):
767        Leaf.__init__(self, parent)
768        self.type = ""
769        self.tgt_types = IdSet()
770
771    def to_string(self):
772        if self.gen_cil:
773            s = ""
774            for t in self.tgt_types:
775                s += "(typebounds %s %s)" % (self.type, t)
776            return s
777        else:
778            return "typebounds %s %s;" % (self.type, self.tgt_types.to_comma_str())
779
780class RoleAllow(Leaf):
781    def __init__(self, parent=None):
782        Leaf.__init__(self, parent)
783        self.src_roles = IdSet()
784        self.tgt_roles = IdSet()
785
786    def to_string(self):
787        if self.gen_cil:
788            s = ""
789            for src in self.src_roles:
790                for tgt in self.tgt_roles:
791                    s += "(roleallow %s %s)" % (src, tgt)
792            return s
793        else:
794            return "allow %s %s;" % (self.src_roles.to_comma_str(),
795                                     self.tgt_roles.to_comma_str())
796
797class RoleType(Leaf):
798    def __init__(self, parent=None):
799        Leaf.__init__(self, parent)
800        self.role = ""
801        self.types = IdSet()
802
803    def to_string(self):
804        s = ""
805        for t in self.types:
806            if self.gen_cil:
807                s += "(roletype %s %s)\n" % (self.role, t)
808            else:
809                s += "role %s types %s;\n" % (self.role, t)
810        return s
811
812class ModuleDeclaration(Leaf):
813    def __init__(self, parent=None):
814        Leaf.__init__(self, parent)
815        self.name = ""
816        self.version = ""
817        self.refpolicy = False
818
819    def to_string(self):
820        if self.gen_cil:
821            return ""
822        else:
823            if self.refpolicy:
824                return "policy_module(%s, %s)" % (self.name, self.version)
825            else:
826                return "module %s %s;" % (self.name, self.version)
827
828class Conditional(Node):
829    def __init__(self, parent=None):
830        Node.__init__(self, parent)
831        self.cond_expr = []
832
833    def to_string(self):
834        return "[If %s]" % list_to_space_str(self.cond_expr, cont=("", ""))
835
836class Bool(Leaf):
837    def __init__(self, parent=None):
838        Leaf.__init__(self, parent)
839        self.name = ""
840        self.state = False
841
842    def to_string(self):
843        s = "bool %s " % self.name
844        if s.state:
845            return s + "true"
846        else:
847            return s + "false"
848
849class InitialSid(Leaf):
850    def __init(self, parent=None):
851        Leaf.__init__(self, parent)
852        self.name = ""
853        self.context = None
854
855    def to_string(self):
856        if self.gen_cil:
857            return "(sid %s %s)" % (self.name, str(self.context))
858        else:
859            return "sid %s %s" % (self.name, str(self.context))
860
861class GenfsCon(Leaf):
862    def __init__(self, parent=None):
863        Leaf.__init__(self, parent)
864        self.filesystem = ""
865        self.path = ""
866        self.context = None
867
868    def to_string(self):
869        if self.gen_cil:
870            return "(genfscon %s %s %s)" % (self.filesystem, self.path, str(self.context))
871        else:
872            return "genfscon %s %s %s" % (self.filesystem, self.path, str(self.context))
873
874class FilesystemUse(Leaf):
875    XATTR = 1
876    TRANS = 2
877    TASK = 3
878
879    def __init__(self, parent=None):
880        Leaf.__init__(self, parent)
881        self.type = self.XATTR
882        self.filesystem = ""
883        self.context = None
884
885    def to_string(self):
886        s = ""
887        if self.gen_cil:
888            if self.type == self.XATTR:
889                s = "fsuse xattr "
890            elif self.type == self.TRANS:
891                s = "fsuse trans "
892            elif self.type == self.TASK:
893                s = "fsuse task "
894
895            return "(%s %s %s)" % (s, self.filesystem, str(self.context))
896        else:
897            if self.type == self.XATTR:
898                s = "fs_use_xattr "
899            elif self.type == self.TRANS:
900                s = "fs_use_trans "
901            elif self.type == self.TASK:
902                s = "fs_use_task "
903
904            return "%s %s %s;" % (s, self.filesystem, str(self.context))
905
906class PortCon(Leaf):
907    def __init__(self, parent=None):
908        Leaf.__init__(self, parent)
909        self.port_type = ""
910        self.port_number = ""
911        self.context = None
912
913    def to_string(self):
914        if self.gen_cil:
915            return "(portcon %s %s %s)" % (self.port_type, self.port_number, str(self.context))
916        else:
917            return "portcon %s %s %s" % (self.port_type, self.port_number, str(self.context))
918
919class NodeCon(Leaf):
920    def __init__(self, parent=None):
921        Leaf.__init__(self, parent)
922        self.start = ""
923        self.end = ""
924        self.context = None
925
926    def to_string(self):
927        if self.gen_cil:
928            return "(nodecon %s %s %s)" % (self.start, self.end, str(self.context))
929        else:
930            return "nodecon %s %s %s" % (self.start, self.end, str(self.context))
931
932class NetifCon(Leaf):
933    def __init__(self, parent=None):
934        Leaf.__init__(self, parent)
935        self.interface = ""
936        self.interface_context = None
937        self.packet_context = None
938
939    def to_string(self):
940        if self.gen_cil:
941            return "(netifcon %s %s %s)" % (self.interface, str(self.interface_context),
942                                            str(self.packet_context))
943        else:
944            return "netifcon %s %s %s" % (self.interface, str(self.interface_context),
945                                          str(self.packet_context))
946
947class PirqCon(Leaf):
948    def __init__(self, parent=None):
949        Leaf.__init__(self, parent)
950        self.pirq_number = ""
951        self.context = None
952
953    def to_string(self):
954        if self.gen_cil:
955            return "(pirqcon %s %s)" % (self.pirq_number, str(self.context))
956        else:
957            return "pirqcon %s %s" % (self.pirq_number, str(self.context))
958
959class IomemCon(Leaf):
960    def __init__(self, parent=None):
961        Leaf.__init__(self, parent)
962        self.device_mem = ""
963        self.context = None
964
965    def to_string(self):
966        if self.gen_cil:
967            return "(iomemcon %s %s)" % (self.device_mem, str(self.context))
968        else:
969            return "iomemcon %s %s" % (self.device_mem, str(self.context))
970
971class IoportCon(Leaf):
972    def __init__(self, parent=None):
973        Leaf.__init__(self, parent)
974        self.ioport = ""
975        self.context = None
976
977    def to_string(self):
978        if self.gen_cil:
979            return "(ioportcon %s %s)" % (self.ioport, str(self.context))
980        else:
981            return "ioportcon %s %s" % (self.ioport, str(self.context))
982
983class PciDeviceCon(Leaf):
984    def __init__(self, parent=None):
985        Leaf.__init__(self, parent)
986        self.device = ""
987        self.context = None
988
989    def to_string(self):
990        if self.gen_cil:
991            return "(pcidevicecon %s %s)" % (self.device, str(self.context))
992        else:
993            return "pcidevicecon %s %s" % (self.device, str(self.context))
994
995class DeviceTreeCon(Leaf):
996    def __init__(self, parent=None):
997        Leaf.__init__(self, parent)
998        self.path = ""
999        self.context = None
1000
1001    def to_string(self):
1002        if self.gen_cil:
1003            return "(devicetreecon %s %s)" % (self.path, str(self.context))
1004        else:
1005            return "devicetreecon %s %s" % (self.path, str(self.context))
1006
1007# Reference policy specific types
1008
1009def print_tree(head):
1010    for node, depth in walktree(head, showdepth=True):
1011        s = ""
1012        for i in range(depth):
1013            s = s + "\t"
1014        print(s + str(node))
1015
1016
1017class Headers(Node):
1018    def __init__(self, parent=None):
1019        Node.__init__(self, parent)
1020
1021    def to_string(self):
1022        return "[Headers]"
1023
1024
1025class Module(Node):
1026    def __init__(self, parent=None):
1027        Node.__init__(self, parent)
1028
1029    def to_string(self):
1030        return ""
1031
1032class Interface(Node):
1033    """A reference policy interface definition.
1034
1035    This class represents a reference policy interface definition.
1036    """
1037    def __init__(self, name="", parent=None):
1038        Node.__init__(self, parent)
1039        self.name = name
1040
1041    def to_string(self):
1042        return "[Interface name: %s]" % self.name
1043
1044class TunablePolicy(Node):
1045    def __init__(self, parent=None):
1046        Node.__init__(self, parent)
1047        self.cond_expr = []
1048
1049    def to_string(self):
1050        return "[Tunable Policy %s]" % list_to_space_str(self.cond_expr, cont=("", ""))
1051
1052class Template(Node):
1053    def __init__(self, name="", parent=None):
1054        Node.__init__(self, parent)
1055        self.name = name
1056
1057    def to_string(self):
1058        return "[Template name: %s]" % self.name
1059
1060class IfDef(Node):
1061    def __init__(self, name="", parent=None):
1062        Node.__init__(self, parent)
1063        self.name = name
1064
1065    def to_string(self):
1066        return "[Ifdef name: %s]" % self.name
1067
1068class IfElse(Node):
1069    def __init__(self, name="", parent=None):
1070        Node.__init__(self, parent)
1071        self.name = name
1072
1073    def to_string(self):
1074        return "[Ifelse name: %s]" % self.name
1075
1076class InterfaceCall(Leaf):
1077    def __init__(self, ifname="", parent=None):
1078        Leaf.__init__(self, parent)
1079        self.ifname = ifname
1080        self.args = []
1081        self.comments = []
1082
1083    def matches(self, other):
1084        if self.ifname != other.ifname:
1085            return False
1086        if len(self.args) != len(other.args):
1087            return False
1088        for a,b in zip(self.args, other.args):
1089            if a != b:
1090                return False
1091        return True
1092
1093    def to_string(self):
1094        s = "%s(" % self.ifname
1095        i = 0
1096        for a in self.args:
1097            if isinstance(a, list):
1098                str = list_to_space_str(a)
1099            else:
1100                str = a
1101
1102            if i != 0:
1103                s = s + ", %s" % str
1104            else:
1105                s = s + str
1106            i += 1
1107        return s + ")"
1108
1109class OptionalPolicy(Node):
1110    def __init__(self, parent=None):
1111        Node.__init__(self, parent)
1112
1113    def to_string(self):
1114        return "[Optional Policy]"
1115
1116class SupportMacros(Node):
1117    def __init__(self, parent=None):
1118        Node.__init__(self, parent)
1119        self.map = None
1120
1121    def to_string(self):
1122        return "[Support Macros]"
1123
1124    def __expand_perm(self, perm):
1125        # Recursive expansion - the assumption is that these
1126        # are ordered correctly so that no macro is used before
1127        # it is defined
1128        s = set()
1129        if perm in self.map:
1130            for p in self.by_name(perm):
1131                s.update(self.__expand_perm(p))
1132        else:
1133            s.add(perm)
1134        return s
1135
1136    def __gen_map(self):
1137        self.map = {}
1138        for x in self:
1139            exp_perms = set()
1140            for perm in x.perms:
1141                exp_perms.update(self.__expand_perm(perm))
1142            self.map[x.name] = exp_perms
1143
1144    def by_name(self, name):
1145        if not self.map:
1146            self.__gen_map()
1147        return self.map[name]
1148
1149    def has_key(self, name):
1150        if not self.map:
1151            self.__gen_map()
1152        return name in self.map
1153
1154class Require(Leaf):
1155    def __init__(self, parent=None):
1156        Leaf.__init__(self, parent)
1157        self.types = IdSet()
1158        self.obj_classes = { }
1159        self.roles = IdSet()
1160        self.data = IdSet()
1161        self.users = IdSet()
1162
1163    def add_obj_class(self, obj_class, perms):
1164        p = self.obj_classes.setdefault(obj_class, IdSet())
1165        p.update(perms)
1166
1167
1168    def to_string(self):
1169        s = []
1170        if self.gen_cil:
1171            # Can't require classes, perms, booleans, users
1172            for type in self.types:
1173                s.append("(typeattributeset cil_gen_require %s)" % type)
1174            for role in self.roles:
1175                s.append("(roleattributeset cil_gen_require %s)" % role)
1176
1177            return "\n".join(s)
1178        else:
1179            s.append("require {")
1180            for type in self.types:
1181                s.append("\ttype %s;" % type)
1182            for obj_class, perms in self.obj_classes.items():
1183                s.append("\tclass %s %s;" % (obj_class, perms.to_space_str()))
1184            for role in self.roles:
1185                s.append("\trole %s;" % role)
1186            for bool in self.data:
1187                s.append("\tbool %s;" % bool)
1188            for user in self.users:
1189                s.append("\tuser %s;" % user)
1190            s.append("}")
1191
1192            # Handle empty requires
1193            if len(s) == 2:
1194                return ""
1195
1196            return "\n".join(s)
1197
1198class ObjPermSet:
1199    def __init__(self, name):
1200        self.name = name
1201        self.perms = set()
1202
1203    def to_string(self):
1204        return "define(`%s', `%s')" % (self.name, self.perms.to_space_str())
1205
1206class ClassMap:
1207    def __init__(self, obj_class, perms):
1208        self.obj_class = obj_class
1209        self.perms = perms
1210
1211    def to_string(self):
1212        return self.obj_class + ": " + self.perms
1213
1214class Comment:
1215    def __init__(self, l=None):
1216        if l:
1217            self.lines = l
1218        else:
1219            self.lines = []
1220        self.gen_cil = False
1221
1222    def to_string(self):
1223        # If there are no lines, treat this as a spacer between
1224        # policy statements and return a new line.
1225        if len(self.lines) == 0:
1226            return ""
1227        else:
1228            out = []
1229            for line in self.lines:
1230                if self.gen_cil:
1231                    out.append(";" + line)
1232                else:
1233                    out.append("#" + line)
1234            return "\n".join(out)
1235
1236    def merge(self, other):
1237        if len(other.lines):
1238            for line in other.lines:
1239                if line != "":
1240                    self.lines.append(line)
1241
1242    def __str__(self):
1243        return self.to_string()
1244
1245    def set_gen_cil(self, gen_cil):
1246        self.gen_cil = gen_cil
1247