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