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 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(self) 255 256 def to_comma_str(self): 257 return list_to_comma_str(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 917