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