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