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