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