• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# Authors: Karl MacMillan <kmacmillan@mentalrootkit.com>
2#
3# Copyright (C) 2006 Red Hat
4# see file 'COPYING' for use and warranty information
5#
6# This program is free software; you can redistribute it and/or
7# modify it under the terms of the GNU General Public License as
8# published by the Free Software Foundation; version 2 only
9#
10# This program is distributed in the hope that it will be useful,
11# but WITHOUT ANY WARRANTY; without even the implied warranty of
12# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13# GNU General Public License for more details.
14#
15# You should have received a copy of the GNU General Public License
16# along with this program; if not, write to the Free Software
17# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18#
19
20import string
21import selinux
22
23# OVERVIEW
24#
25# This file contains objects and functions used to represent the reference
26# policy (including the headers, M4 macros, and policy language statements).
27#
28# This representation is very different from the semantic representation
29# used in libsepol. Instead, it is a more typical abstract representation
30# used by the first stage of compilers. It is basically a parse tree.
31#
32# This choice is intentional as it allows us to handle the unprocessed
33# M4 statements - including the $1 style arguments - and to more easily generate
34# the data structures that we need for policy generation.
35#
36
37# Constans for referring to fields
38SRC_TYPE  = 0
39TGT_TYPE  = 1
40OBJ_CLASS = 2
41PERMS     = 3
42ROLE      = 4
43DEST_TYPE = 5
44
45# String represenations of the above constants
46field_to_str = ["source", "target", "object", "permission", "role", "destination" ]
47str_to_field = { "source" : SRC_TYPE, "target" : TGT_TYPE, "object" : OBJ_CLASS,
48                "permission" : PERMS, "role" : ROLE, "destination" : DEST_TYPE }
49
50# Base Classes
51
52class PolicyBase:
53    def __init__(self, parent=None):
54        self.parent = None
55        self.comment = None
56
57class Node(PolicyBase):
58    """Base class objects produced from parsing the reference policy.
59
60    The Node class is used as the base class for any non-leaf
61    object produced by parsing the reference policy. This object
62    should contain a reference to its parent (or None for a top-level
63    object) and 0 or more children.
64
65    The general idea here is to have a very simple tree structure. Children
66    are not separated out by type. Instead the tree structure represents
67    fairly closely the real structure of the policy statements.
68
69    The object should be iterable - by default over all children but
70    subclasses are free to provide additional iterators over a subset
71    of their childre (see Interface for example).
72    """
73
74    def __init__(self, parent=None):
75        PolicyBase.__init__(self, parent)
76        self.children = []
77
78    def __iter__(self):
79        return iter(self.children)
80
81    # Not all of the iterators will return something on all Nodes, but
82    # they won't explode either. Putting them here is just easier.
83
84    # Top level nodes
85
86    def nodes(self):
87        return filter(lambda x: isinstance(x, Node), walktree(self))
88
89    def modules(self):
90        return filter(lambda x: isinstance(x, Module), walktree(self))
91
92    def interfaces(self):
93        return filter(lambda x: isinstance(x, Interface), walktree(self))
94
95    def templates(self):
96        return filter(lambda x: isinstance(x, Template), walktree(self))
97
98    def support_macros(self):
99        return filter(lambda x: isinstance(x, SupportMacros), walktree(self))
100
101    # Common policy statements
102
103    def module_declarations(self):
104        return filter(lambda x: isinstance(x, ModuleDeclaration), walktree(self))
105
106    def interface_calls(self):
107        return filter(lambda x: isinstance(x, InterfaceCall), walktree(self))
108
109    def avrules(self):
110        return filter(lambda x: isinstance(x, AVRule), walktree(self))
111
112    def 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    paramater. 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 conver 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 + str(self.ranges[0][0])
411
412        vals = map(lambda x: str(x[0]) if x[0] == x[1] else "%s-%s" % x,
413                   self.ranges)
414
415        return "%s{ %s }" % (compl, " ".join(vals))
416
417# Basic statements
418
419class TypeAttribute(Leaf):
420    """SElinux typeattribute statement.
421
422    This class represents a typeattribute statement.
423    """
424    def __init__(self, parent=None):
425        Leaf.__init__(self, parent)
426        self.type = ""
427        self.attributes = IdSet()
428
429    def to_string(self):
430        return "typeattribute %s %s;" % (self.type, self.attributes.to_comma_str())
431
432class RoleAttribute(Leaf):
433    """SElinux roleattribute statement.
434
435    This class represents a roleattribute statement.
436    """
437    def __init__(self, parent=None):
438        Leaf.__init__(self, parent)
439        self.role = ""
440        self.roleattributes = IdSet()
441
442    def to_string(self):
443        return "roleattribute %s %s;" % (self.role, self.roleattributes.to_comma_str())
444
445
446class Role(Leaf):
447    def __init__(self, parent=None):
448        Leaf.__init__(self, parent)
449        self.role = ""
450        self.types = IdSet()
451
452    def to_string(self):
453        s = ""
454        for t in self.types:
455            s += "role %s types %s;\n" % (self.role, t)
456        return s
457
458class Type(Leaf):
459    def __init__(self, name="", parent=None):
460        Leaf.__init__(self, parent)
461        self.name = name
462        self.attributes = IdSet()
463        self.aliases = IdSet()
464
465    def to_string(self):
466        s = "type %s" % self.name
467        if len(self.aliases) > 0:
468            s = s + "alias %s" % self.aliases.to_space_str()
469        if len(self.attributes) > 0:
470            s = s + ", %s" % self.attributes.to_comma_str()
471        return s + ";"
472
473class TypeAlias(Leaf):
474    def __init__(self, parent=None):
475        Leaf.__init__(self, parent)
476        self.type = ""
477        self.aliases = IdSet()
478
479    def to_string(self):
480        return "typealias %s alias %s;" % (self.type, self.aliases.to_space_str())
481
482class Attribute(Leaf):
483    def __init__(self, name="", parent=None):
484        Leaf.__init__(self, parent)
485        self.name = name
486
487    def to_string(self):
488        return "attribute %s;" % self.name
489
490class Attribute_Role(Leaf):
491    def __init__(self, name="", parent=None):
492        Leaf.__init__(self, parent)
493        self.name = name
494
495    def to_string(self):
496        return "attribute_role %s;" % self.name
497
498
499# Classes representing rules
500
501class AVRule(Leaf):
502    """SELinux access vector (AV) rule.
503
504    The AVRule class represents all varieties of AV rules including
505    allow, dontaudit, and auditallow (indicated by the flags self.ALLOW,
506    self.DONTAUDIT, and self.AUDITALLOW respectively).
507
508    The source and target types, object classes, and perms are all represented
509    by sets containing strings. Sets are used to make it simple to add
510    strings repeatedly while avoiding duplicates.
511
512    No checking is done to make certain that the symbols are valid or
513    consistent (e.g., perms that don't match the object classes). It is
514    even possible to put invalid types like '$1' into the rules to allow
515    storage of the reference policy interfaces.
516    """
517    ALLOW = 0
518    DONTAUDIT = 1
519    AUDITALLOW = 2
520    NEVERALLOW = 3
521
522    def __init__(self, av=None, parent=None):
523        Leaf.__init__(self, parent)
524        self.src_types = IdSet()
525        self.tgt_types = IdSet()
526        self.obj_classes = IdSet()
527        self.perms = IdSet()
528        self.rule_type = self.ALLOW
529        if av:
530            self.from_av(av)
531
532    def __rule_type_str(self):
533        if self.rule_type == self.ALLOW:
534            return "allow"
535        elif self.rule_type == self.DONTAUDIT:
536            return "dontaudit"
537        elif self.rule_type == self.AUDITALLOW:
538            return "auditallow"
539        elif self.rule_type == self.NEVERALLOW:
540            return "neverallow"
541
542    def from_av(self, av):
543        """Add the access from an access vector to this allow
544        rule.
545        """
546        self.src_types.add(av.src_type)
547        if av.src_type == av.tgt_type:
548            self.tgt_types.add("self")
549        else:
550            self.tgt_types.add(av.tgt_type)
551        self.obj_classes.add(av.obj_class)
552        self.perms.update(av.perms)
553
554    def to_string(self):
555        """Return a string representation of the rule
556        that is a valid policy language representation (assuming
557        that the types, object class, etc. are valie).
558        """
559        return "%s %s %s:%s %s;" % (self.__rule_type_str(),
560                                     self.src_types.to_space_str(),
561                                     self.tgt_types.to_space_str(),
562                                     self.obj_classes.to_space_str(),
563                                     self.perms.to_space_str())
564
565class AVExtRule(Leaf):
566    """Extended permission access vector rule.
567
568    The AVExtRule class represents allowxperm, dontauditxperm,
569    auditallowxperm, and neverallowxperm rules.
570
571    The source and target types, and object classes are represented
572    by sets containing strings. The operation is a single string,
573    e.g. 'ioctl'. Extended permissions are represented by an XpermSet.
574    """
575    ALLOWXPERM = 0
576    DONTAUDITXPERM = 1
577    AUDITALLOWXPERM = 2
578    NEVERALLOWXPERM = 3
579
580    def __init__(self, av=None, op=None, parent=None):
581        Leaf.__init__(self, parent)
582        self.src_types = IdSet()
583        self.tgt_types = IdSet()
584        self.obj_classes = IdSet()
585        self.rule_type = self.ALLOWXPERM
586        self.xperms = XpermSet()
587        self.operation = op
588        if av:
589            self.from_av(av, op)
590
591    def __rule_type_str(self):
592        if self.rule_type == self.ALLOWXPERM:
593            return "allowxperm"
594        elif self.rule_type == self.DONTAUDITXPERM:
595            return "dontauditxperm"
596        elif self.rule_type == self.AUDITALLOWXPERM:
597            return "auditallowxperm"
598        elif self.rule_type == self.NEVERALLOWXPERM:
599            return "neverallowxperm"
600
601    def from_av(self, av, op):
602        self.src_types.add(av.src_type)
603        if av.src_type == av.tgt_type:
604            self.tgt_types.add("self")
605        else:
606            self.tgt_types.add(av.tgt_type)
607        self.obj_classes.add(av.obj_class)
608        self.operation = op
609        self.xperms = av.xperms[op]
610
611    def to_string(self):
612        """Return a string representation of the rule that is
613        a valid policy language representation (assuming that
614        the types, object class, etc. are valid).
615        """
616        return "%s %s %s:%s %s %s;" % (self.__rule_type_str(),
617                                     self.src_types.to_space_str(),
618                                     self.tgt_types.to_space_str(),
619                                     self.obj_classes.to_space_str(),
620                                     self.operation,
621                                     self.xperms.to_string())
622
623class TypeRule(Leaf):
624    """SELinux type rules.
625
626    This class is very similar to the AVRule class, but is for representing
627    the type rules (type_trans, type_change, and type_member). The major
628    difference is the lack of perms and only and sing destination type.
629    """
630    TYPE_TRANSITION = 0
631    TYPE_CHANGE = 1
632    TYPE_MEMBER = 2
633
634    def __init__(self, parent=None):
635        Leaf.__init__(self, parent)
636        self.src_types = IdSet()
637        self.tgt_types = IdSet()
638        self.obj_classes = IdSet()
639        self.dest_type = ""
640        self.rule_type = self.TYPE_TRANSITION
641
642    def __rule_type_str(self):
643        if self.rule_type == self.TYPE_TRANSITION:
644            return "type_transition"
645        elif self.rule_type == self.TYPE_CHANGE:
646            return "type_change"
647        else:
648            return "type_member"
649
650    def to_string(self):
651        return "%s %s %s:%s %s;" % (self.__rule_type_str(),
652                                     self.src_types.to_space_str(),
653                                     self.tgt_types.to_space_str(),
654                                     self.obj_classes.to_space_str(),
655                                     self.dest_type)
656class TypeBound(Leaf):
657    """SElinux typebound statement.
658
659    This class represents a typebound statement.
660    """
661    def __init__(self, parent=None):
662        Leaf.__init__(self, parent)
663        self.type = ""
664        self.tgt_types = IdSet()
665
666    def to_string(self):
667        return "typebounds %s %s;" % (self.type, self.tgt_types.to_comma_str())
668
669
670class RoleAllow(Leaf):
671    def __init__(self, parent=None):
672        Leaf.__init__(self, parent)
673        self.src_roles = IdSet()
674        self.tgt_roles = IdSet()
675
676    def to_string(self):
677        return "allow %s %s;" % (self.src_roles.to_comma_str(),
678                                 self.tgt_roles.to_comma_str())
679
680class RoleType(Leaf):
681    def __init__(self, parent=None):
682        Leaf.__init__(self, parent)
683        self.role = ""
684        self.types = IdSet()
685
686    def to_string(self):
687        s = ""
688        for t in self.types:
689            s += "role %s types %s;\n" % (self.role, t)
690        return s
691
692class ModuleDeclaration(Leaf):
693    def __init__(self, parent=None):
694        Leaf.__init__(self, parent)
695        self.name = ""
696        self.version = ""
697        self.refpolicy = False
698
699    def to_string(self):
700        if self.refpolicy:
701            return "policy_module(%s, %s)" % (self.name, self.version)
702        else:
703            return "module %s %s;" % (self.name, self.version)
704
705class Conditional(Node):
706    def __init__(self, parent=None):
707        Node.__init__(self, parent)
708        self.cond_expr = []
709
710    def to_string(self):
711        return "[If %s]" % list_to_space_str(self.cond_expr, cont=("", ""))
712
713class Bool(Leaf):
714    def __init__(self, parent=None):
715        Leaf.__init__(self, parent)
716        self.name = ""
717        self.state = False
718
719    def to_string(self):
720        s = "bool %s " % self.name
721        if s.state:
722            return s + "true"
723        else:
724            return s + "false"
725
726class InitialSid(Leaf):
727    def __init(self, parent=None):
728        Leaf.__init__(self, parent)
729        self.name = ""
730        self.context = None
731
732    def to_string(self):
733        return "sid %s %s" % (self.name, str(self.context))
734
735class GenfsCon(Leaf):
736    def __init__(self, parent=None):
737        Leaf.__init__(self, parent)
738        self.filesystem = ""
739        self.path = ""
740        self.context = None
741
742    def to_string(self):
743        return "genfscon %s %s %s" % (self.filesystem, self.path, str(self.context))
744
745class FilesystemUse(Leaf):
746    XATTR = 1
747    TRANS = 2
748    TASK = 3
749
750    def __init__(self, parent=None):
751        Leaf.__init__(self, parent)
752        self.type = self.XATTR
753        self.filesystem = ""
754        self.context = None
755
756    def to_string(self):
757        s = ""
758        if self.type == self.XATTR:
759            s = "fs_use_xattr "
760        elif self.type == self.TRANS:
761            s = "fs_use_trans "
762        elif self.type == self.TASK:
763            s = "fs_use_task "
764
765        return "%s %s %s;" % (s, self.filesystem, str(self.context))
766
767class PortCon(Leaf):
768    def __init__(self, parent=None):
769        Leaf.__init__(self, parent)
770        self.port_type = ""
771        self.port_number = ""
772        self.context = None
773
774    def to_string(self):
775        return "portcon %s %s %s" % (self.port_type, self.port_number, str(self.context))
776
777class NodeCon(Leaf):
778    def __init__(self, parent=None):
779        Leaf.__init__(self, parent)
780        self.start = ""
781        self.end = ""
782        self.context = None
783
784    def to_string(self):
785        return "nodecon %s %s %s" % (self.start, self.end, str(self.context))
786
787class NetifCon(Leaf):
788    def __init__(self, parent=None):
789        Leaf.__init__(self, parent)
790        self.interface = ""
791        self.interface_context = None
792        self.packet_context = None
793
794    def to_string(self):
795        return "netifcon %s %s %s" % (self.interface, str(self.interface_context),
796                                   str(self.packet_context))
797class PirqCon(Leaf):
798    def __init__(self, parent=None):
799        Leaf.__init__(self, parent)
800        self.pirq_number = ""
801        self.context = None
802
803    def to_string(self):
804        return "pirqcon %s %s" % (self.pirq_number, str(self.context))
805
806class IomemCon(Leaf):
807    def __init__(self, parent=None):
808        Leaf.__init__(self, parent)
809        self.device_mem = ""
810        self.context = None
811
812    def to_string(self):
813        return "iomemcon %s %s" % (self.device_mem, str(self.context))
814
815class IoportCon(Leaf):
816    def __init__(self, parent=None):
817        Leaf.__init__(self, parent)
818        self.ioport = ""
819        self.context = None
820
821    def to_string(self):
822        return "ioportcon %s %s" % (self.ioport, str(self.context))
823
824class PciDeviceCon(Leaf):
825    def __init__(self, parent=None):
826        Leaf.__init__(self, parent)
827        self.device = ""
828        self.context = None
829
830    def to_string(self):
831        return "pcidevicecon %s %s" % (self.device, str(self.context))
832
833class DeviceTreeCon(Leaf):
834    def __init__(self, parent=None):
835        Leaf.__init__(self, parent)
836        self.path = ""
837        self.context = None
838
839    def to_string(self):
840        return "devicetreecon %s %s" % (self.path, str(self.context))
841
842# Reference policy specific types
843
844def print_tree(head):
845    for node, depth in walktree(head, showdepth=True):
846        s = ""
847        for i in range(depth):
848            s = s + "\t"
849        print(s + str(node))
850
851
852class Headers(Node):
853    def __init__(self, parent=None):
854        Node.__init__(self, parent)
855
856    def to_string(self):
857        return "[Headers]"
858
859
860class Module(Node):
861    def __init__(self, parent=None):
862        Node.__init__(self, parent)
863
864    def to_string(self):
865        return ""
866
867class Interface(Node):
868    """A reference policy interface definition.
869
870    This class represents a reference policy interface definition.
871    """
872    def __init__(self, name="", parent=None):
873        Node.__init__(self, parent)
874        self.name = name
875
876    def to_string(self):
877        return "[Interface name: %s]" % self.name
878
879class TunablePolicy(Node):
880    def __init__(self, parent=None):
881        Node.__init__(self, parent)
882        self.cond_expr = []
883
884    def to_string(self):
885        return "[Tunable Policy %s]" % list_to_space_str(self.cond_expr, cont=("", ""))
886
887class Template(Node):
888    def __init__(self, name="", parent=None):
889        Node.__init__(self, parent)
890        self.name = name
891
892    def to_string(self):
893        return "[Template name: %s]" % self.name
894
895class IfDef(Node):
896    def __init__(self, name="", parent=None):
897        Node.__init__(self, parent)
898        self.name = name
899
900    def to_string(self):
901        return "[Ifdef name: %s]" % self.name
902
903class InterfaceCall(Leaf):
904    def __init__(self, ifname="", parent=None):
905        Leaf.__init__(self, parent)
906        self.ifname = ifname
907        self.args = []
908        self.comments = []
909
910    def matches(self, other):
911        if self.ifname != other.ifname:
912            return False
913        if len(self.args) != len(other.args):
914            return False
915        for a,b in zip(self.args, other.args):
916            if a != b:
917                return False
918        return True
919
920    def to_string(self):
921        s = "%s(" % self.ifname
922        i = 0
923        for a in self.args:
924            if isinstance(a, list):
925                str = list_to_space_str(a)
926            else:
927                str = a
928
929            if i != 0:
930                s = s + ", %s" % str
931            else:
932                s = s + str
933            i += 1
934        return s + ")"
935
936class OptionalPolicy(Node):
937    def __init__(self, parent=None):
938        Node.__init__(self, parent)
939
940    def to_string(self):
941        return "[Optional Policy]"
942
943class SupportMacros(Node):
944    def __init__(self, parent=None):
945        Node.__init__(self, parent)
946        self.map = None
947
948    def to_string(self):
949        return "[Support Macros]"
950
951    def __expand_perm(self, perm):
952        # Recursive expansion - the assumption is that these
953        # are ordered correctly so that no macro is used before
954        # it is defined
955        s = set()
956        if perm in self.map:
957            for p in self.by_name(perm):
958                s.update(self.__expand_perm(p))
959        else:
960            s.add(perm)
961        return s
962
963    def __gen_map(self):
964        self.map = {}
965        for x in self:
966            exp_perms = set()
967            for perm in x.perms:
968                exp_perms.update(self.__expand_perm(perm))
969            self.map[x.name] = exp_perms
970
971    def by_name(self, name):
972        if not self.map:
973            self.__gen_map()
974        return self.map[name]
975
976    def has_key(self, name):
977        if not self.map:
978            self.__gen_map()
979        return name in self.map
980
981class Require(Leaf):
982    def __init__(self, parent=None):
983        Leaf.__init__(self, parent)
984        self.types = IdSet()
985        self.obj_classes = { }
986        self.roles = IdSet()
987        self.data = IdSet()
988        self.users = IdSet()
989
990    def add_obj_class(self, obj_class, perms):
991        p = self.obj_classes.setdefault(obj_class, IdSet())
992        p.update(perms)
993
994
995    def to_string(self):
996        s = []
997        s.append("require {")
998        for type in self.types:
999            s.append("\ttype %s;" % type)
1000        for obj_class, perms in self.obj_classes.items():
1001            s.append("\tclass %s %s;" % (obj_class, perms.to_space_str()))
1002        for role in self.roles:
1003            s.append("\trole %s;" % role)
1004        for bool in self.data:
1005            s.append("\tbool %s;" % bool)
1006        for user in self.users:
1007            s.append("\tuser %s;" % user)
1008        s.append("}")
1009
1010        # Handle empty requires
1011        if len(s) == 2:
1012            return ""
1013
1014        return "\n".join(s)
1015
1016
1017class ObjPermSet:
1018    def __init__(self, name):
1019        self.name = name
1020        self.perms = set()
1021
1022    def to_string(self):
1023        return "define(`%s', `%s')" % (self.name, self.perms.to_space_str())
1024
1025class ClassMap:
1026    def __init__(self, obj_class, perms):
1027        self.obj_class = obj_class
1028        self.perms = perms
1029
1030    def to_string(self):
1031        return self.obj_class + ": " + self.perms
1032
1033class Comment:
1034    def __init__(self, l=None):
1035        if l:
1036            self.lines = l
1037        else:
1038            self.lines = []
1039
1040    def to_string(self):
1041        # If there are no lines, treat this as a spacer between
1042        # policy statements and return a new line.
1043        if len(self.lines) == 0:
1044            return ""
1045        else:
1046            out = []
1047            for line in self.lines:
1048                out.append("#" + line)
1049            return "\n".join(out)
1050
1051    def merge(self, other):
1052        if len(other.lines):
1053            for line in other.lines:
1054                if line != "":
1055                    self.lines.append(line)
1056
1057    def __str__(self):
1058        return self.to_string()
1059
1060
1061