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 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 + str(self.ranges[0][0]) 411 412 vals = map(lambda x: str(x[0]) if x[0] == x[1] else "%s-%s" % x, 413 self.ranges) 414 415 return "%s{ %s }" % (compl, " ".join(vals)) 416 417# Basic statements 418 419class TypeAttribute(Leaf): 420 """SElinux typeattribute statement. 421 422 This class represents a typeattribute statement. 423 """ 424 def __init__(self, parent=None): 425 Leaf.__init__(self, parent) 426 self.type = "" 427 self.attributes = IdSet() 428 429 def to_string(self): 430 return "typeattribute %s %s;" % (self.type, self.attributes.to_comma_str()) 431 432class RoleAttribute(Leaf): 433 """SElinux roleattribute statement. 434 435 This class represents a roleattribute statement. 436 """ 437 def __init__(self, parent=None): 438 Leaf.__init__(self, parent) 439 self.role = "" 440 self.roleattributes = IdSet() 441 442 def to_string(self): 443 return "roleattribute %s %s;" % (self.role, self.roleattributes.to_comma_str()) 444 445 446class Role(Leaf): 447 def __init__(self, parent=None): 448 Leaf.__init__(self, parent) 449 self.role = "" 450 self.types = IdSet() 451 452 def to_string(self): 453 s = "" 454 for t in self.types: 455 s += "role %s types %s;\n" % (self.role, t) 456 return s 457 458class Type(Leaf): 459 def __init__(self, name="", parent=None): 460 Leaf.__init__(self, parent) 461 self.name = name 462 self.attributes = IdSet() 463 self.aliases = IdSet() 464 465 def to_string(self): 466 s = "type %s" % self.name 467 if len(self.aliases) > 0: 468 s = s + "alias %s" % self.aliases.to_space_str() 469 if len(self.attributes) > 0: 470 s = s + ", %s" % self.attributes.to_comma_str() 471 return s + ";" 472 473class TypeAlias(Leaf): 474 def __init__(self, parent=None): 475 Leaf.__init__(self, parent) 476 self.type = "" 477 self.aliases = IdSet() 478 479 def to_string(self): 480 return "typealias %s alias %s;" % (self.type, self.aliases.to_space_str()) 481 482class Attribute(Leaf): 483 def __init__(self, name="", parent=None): 484 Leaf.__init__(self, parent) 485 self.name = name 486 487 def to_string(self): 488 return "attribute %s;" % self.name 489 490class Attribute_Role(Leaf): 491 def __init__(self, name="", parent=None): 492 Leaf.__init__(self, parent) 493 self.name = name 494 495 def to_string(self): 496 return "attribute_role %s;" % self.name 497 498 499# Classes representing rules 500 501class AVRule(Leaf): 502 """SELinux access vector (AV) rule. 503 504 The AVRule class represents all varieties of AV rules including 505 allow, dontaudit, and auditallow (indicated by the flags self.ALLOW, 506 self.DONTAUDIT, and self.AUDITALLOW respectively). 507 508 The source and target types, object classes, and perms are all represented 509 by sets containing strings. Sets are used to make it simple to add 510 strings repeatedly while avoiding duplicates. 511 512 No checking is done to make certain that the symbols are valid or 513 consistent (e.g., perms that don't match the object classes). It is 514 even possible to put invalid types like '$1' into the rules to allow 515 storage of the reference policy interfaces. 516 """ 517 ALLOW = 0 518 DONTAUDIT = 1 519 AUDITALLOW = 2 520 NEVERALLOW = 3 521 522 def __init__(self, av=None, parent=None): 523 Leaf.__init__(self, parent) 524 self.src_types = IdSet() 525 self.tgt_types = IdSet() 526 self.obj_classes = IdSet() 527 self.perms = IdSet() 528 self.rule_type = self.ALLOW 529 if av: 530 self.from_av(av) 531 532 def __rule_type_str(self): 533 if self.rule_type == self.ALLOW: 534 return "allow" 535 elif self.rule_type == self.DONTAUDIT: 536 return "dontaudit" 537 elif self.rule_type == self.AUDITALLOW: 538 return "auditallow" 539 elif self.rule_type == self.NEVERALLOW: 540 return "neverallow" 541 542 def from_av(self, av): 543 """Add the access from an access vector to this allow 544 rule. 545 """ 546 self.src_types.add(av.src_type) 547 if av.src_type == av.tgt_type: 548 self.tgt_types.add("self") 549 else: 550 self.tgt_types.add(av.tgt_type) 551 self.obj_classes.add(av.obj_class) 552 self.perms.update(av.perms) 553 554 def to_string(self): 555 """Return a string representation of the rule 556 that is a valid policy language representation (assuming 557 that the types, object class, etc. are valie). 558 """ 559 return "%s %s %s:%s %s;" % (self.__rule_type_str(), 560 self.src_types.to_space_str(), 561 self.tgt_types.to_space_str(), 562 self.obj_classes.to_space_str(), 563 self.perms.to_space_str()) 564 565class AVExtRule(Leaf): 566 """Extended permission access vector rule. 567 568 The AVExtRule class represents allowxperm, dontauditxperm, 569 auditallowxperm, and neverallowxperm rules. 570 571 The source and target types, and object classes are represented 572 by sets containing strings. The operation is a single string, 573 e.g. 'ioctl'. Extended permissions are represented by an XpermSet. 574 """ 575 ALLOWXPERM = 0 576 DONTAUDITXPERM = 1 577 AUDITALLOWXPERM = 2 578 NEVERALLOWXPERM = 3 579 580 def __init__(self, av=None, op=None, parent=None): 581 Leaf.__init__(self, parent) 582 self.src_types = IdSet() 583 self.tgt_types = IdSet() 584 self.obj_classes = IdSet() 585 self.rule_type = self.ALLOWXPERM 586 self.xperms = XpermSet() 587 self.operation = op 588 if av: 589 self.from_av(av, op) 590 591 def __rule_type_str(self): 592 if self.rule_type == self.ALLOWXPERM: 593 return "allowxperm" 594 elif self.rule_type == self.DONTAUDITXPERM: 595 return "dontauditxperm" 596 elif self.rule_type == self.AUDITALLOWXPERM: 597 return "auditallowxperm" 598 elif self.rule_type == self.NEVERALLOWXPERM: 599 return "neverallowxperm" 600 601 def from_av(self, av, op): 602 self.src_types.add(av.src_type) 603 if av.src_type == av.tgt_type: 604 self.tgt_types.add("self") 605 else: 606 self.tgt_types.add(av.tgt_type) 607 self.obj_classes.add(av.obj_class) 608 self.operation = op 609 self.xperms = av.xperms[op] 610 611 def to_string(self): 612 """Return a string representation of the rule that is 613 a valid policy language representation (assuming that 614 the types, object class, etc. are valid). 615 """ 616 return "%s %s %s:%s %s %s;" % (self.__rule_type_str(), 617 self.src_types.to_space_str(), 618 self.tgt_types.to_space_str(), 619 self.obj_classes.to_space_str(), 620 self.operation, 621 self.xperms.to_string()) 622 623class TypeRule(Leaf): 624 """SELinux type rules. 625 626 This class is very similar to the AVRule class, but is for representing 627 the type rules (type_trans, type_change, and type_member). The major 628 difference is the lack of perms and only and sing destination type. 629 """ 630 TYPE_TRANSITION = 0 631 TYPE_CHANGE = 1 632 TYPE_MEMBER = 2 633 634 def __init__(self, parent=None): 635 Leaf.__init__(self, parent) 636 self.src_types = IdSet() 637 self.tgt_types = IdSet() 638 self.obj_classes = IdSet() 639 self.dest_type = "" 640 self.rule_type = self.TYPE_TRANSITION 641 642 def __rule_type_str(self): 643 if self.rule_type == self.TYPE_TRANSITION: 644 return "type_transition" 645 elif self.rule_type == self.TYPE_CHANGE: 646 return "type_change" 647 else: 648 return "type_member" 649 650 def to_string(self): 651 return "%s %s %s:%s %s;" % (self.__rule_type_str(), 652 self.src_types.to_space_str(), 653 self.tgt_types.to_space_str(), 654 self.obj_classes.to_space_str(), 655 self.dest_type) 656class TypeBound(Leaf): 657 """SElinux typebound statement. 658 659 This class represents a typebound statement. 660 """ 661 def __init__(self, parent=None): 662 Leaf.__init__(self, parent) 663 self.type = "" 664 self.tgt_types = IdSet() 665 666 def to_string(self): 667 return "typebounds %s %s;" % (self.type, self.tgt_types.to_comma_str()) 668 669 670class RoleAllow(Leaf): 671 def __init__(self, parent=None): 672 Leaf.__init__(self, parent) 673 self.src_roles = IdSet() 674 self.tgt_roles = IdSet() 675 676 def to_string(self): 677 return "allow %s %s;" % (self.src_roles.to_comma_str(), 678 self.tgt_roles.to_comma_str()) 679 680class RoleType(Leaf): 681 def __init__(self, parent=None): 682 Leaf.__init__(self, parent) 683 self.role = "" 684 self.types = IdSet() 685 686 def to_string(self): 687 s = "" 688 for t in self.types: 689 s += "role %s types %s;\n" % (self.role, t) 690 return s 691 692class ModuleDeclaration(Leaf): 693 def __init__(self, parent=None): 694 Leaf.__init__(self, parent) 695 self.name = "" 696 self.version = "" 697 self.refpolicy = False 698 699 def to_string(self): 700 if self.refpolicy: 701 return "policy_module(%s, %s)" % (self.name, self.version) 702 else: 703 return "module %s %s;" % (self.name, self.version) 704 705class Conditional(Node): 706 def __init__(self, parent=None): 707 Node.__init__(self, parent) 708 self.cond_expr = [] 709 710 def to_string(self): 711 return "[If %s]" % list_to_space_str(self.cond_expr, cont=("", "")) 712 713class Bool(Leaf): 714 def __init__(self, parent=None): 715 Leaf.__init__(self, parent) 716 self.name = "" 717 self.state = False 718 719 def to_string(self): 720 s = "bool %s " % self.name 721 if s.state: 722 return s + "true" 723 else: 724 return s + "false" 725 726class InitialSid(Leaf): 727 def __init(self, parent=None): 728 Leaf.__init__(self, parent) 729 self.name = "" 730 self.context = None 731 732 def to_string(self): 733 return "sid %s %s" % (self.name, str(self.context)) 734 735class GenfsCon(Leaf): 736 def __init__(self, parent=None): 737 Leaf.__init__(self, parent) 738 self.filesystem = "" 739 self.path = "" 740 self.context = None 741 742 def to_string(self): 743 return "genfscon %s %s %s" % (self.filesystem, self.path, str(self.context)) 744 745class FilesystemUse(Leaf): 746 XATTR = 1 747 TRANS = 2 748 TASK = 3 749 750 def __init__(self, parent=None): 751 Leaf.__init__(self, parent) 752 self.type = self.XATTR 753 self.filesystem = "" 754 self.context = None 755 756 def to_string(self): 757 s = "" 758 if self.type == self.XATTR: 759 s = "fs_use_xattr " 760 elif self.type == self.TRANS: 761 s = "fs_use_trans " 762 elif self.type == self.TASK: 763 s = "fs_use_task " 764 765 return "%s %s %s;" % (s, self.filesystem, str(self.context)) 766 767class PortCon(Leaf): 768 def __init__(self, parent=None): 769 Leaf.__init__(self, parent) 770 self.port_type = "" 771 self.port_number = "" 772 self.context = None 773 774 def to_string(self): 775 return "portcon %s %s %s" % (self.port_type, self.port_number, str(self.context)) 776 777class NodeCon(Leaf): 778 def __init__(self, parent=None): 779 Leaf.__init__(self, parent) 780 self.start = "" 781 self.end = "" 782 self.context = None 783 784 def to_string(self): 785 return "nodecon %s %s %s" % (self.start, self.end, str(self.context)) 786 787class NetifCon(Leaf): 788 def __init__(self, parent=None): 789 Leaf.__init__(self, parent) 790 self.interface = "" 791 self.interface_context = None 792 self.packet_context = None 793 794 def to_string(self): 795 return "netifcon %s %s %s" % (self.interface, str(self.interface_context), 796 str(self.packet_context)) 797class PirqCon(Leaf): 798 def __init__(self, parent=None): 799 Leaf.__init__(self, parent) 800 self.pirq_number = "" 801 self.context = None 802 803 def to_string(self): 804 return "pirqcon %s %s" % (self.pirq_number, str(self.context)) 805 806class IomemCon(Leaf): 807 def __init__(self, parent=None): 808 Leaf.__init__(self, parent) 809 self.device_mem = "" 810 self.context = None 811 812 def to_string(self): 813 return "iomemcon %s %s" % (self.device_mem, str(self.context)) 814 815class IoportCon(Leaf): 816 def __init__(self, parent=None): 817 Leaf.__init__(self, parent) 818 self.ioport = "" 819 self.context = None 820 821 def to_string(self): 822 return "ioportcon %s %s" % (self.ioport, str(self.context)) 823 824class PciDeviceCon(Leaf): 825 def __init__(self, parent=None): 826 Leaf.__init__(self, parent) 827 self.device = "" 828 self.context = None 829 830 def to_string(self): 831 return "pcidevicecon %s %s" % (self.device, str(self.context)) 832 833class DeviceTreeCon(Leaf): 834 def __init__(self, parent=None): 835 Leaf.__init__(self, parent) 836 self.path = "" 837 self.context = None 838 839 def to_string(self): 840 return "devicetreecon %s %s" % (self.path, str(self.context)) 841 842# Reference policy specific types 843 844def print_tree(head): 845 for node, depth in walktree(head, showdepth=True): 846 s = "" 847 for i in range(depth): 848 s = s + "\t" 849 print(s + str(node)) 850 851 852class Headers(Node): 853 def __init__(self, parent=None): 854 Node.__init__(self, parent) 855 856 def to_string(self): 857 return "[Headers]" 858 859 860class Module(Node): 861 def __init__(self, parent=None): 862 Node.__init__(self, parent) 863 864 def to_string(self): 865 return "" 866 867class Interface(Node): 868 """A reference policy interface definition. 869 870 This class represents a reference policy interface definition. 871 """ 872 def __init__(self, name="", parent=None): 873 Node.__init__(self, parent) 874 self.name = name 875 876 def to_string(self): 877 return "[Interface name: %s]" % self.name 878 879class TunablePolicy(Node): 880 def __init__(self, parent=None): 881 Node.__init__(self, parent) 882 self.cond_expr = [] 883 884 def to_string(self): 885 return "[Tunable Policy %s]" % list_to_space_str(self.cond_expr, cont=("", "")) 886 887class Template(Node): 888 def __init__(self, name="", parent=None): 889 Node.__init__(self, parent) 890 self.name = name 891 892 def to_string(self): 893 return "[Template name: %s]" % self.name 894 895class IfDef(Node): 896 def __init__(self, name="", parent=None): 897 Node.__init__(self, parent) 898 self.name = name 899 900 def to_string(self): 901 return "[Ifdef name: %s]" % self.name 902 903class InterfaceCall(Leaf): 904 def __init__(self, ifname="", parent=None): 905 Leaf.__init__(self, parent) 906 self.ifname = ifname 907 self.args = [] 908 self.comments = [] 909 910 def matches(self, other): 911 if self.ifname != other.ifname: 912 return False 913 if len(self.args) != len(other.args): 914 return False 915 for a,b in zip(self.args, other.args): 916 if a != b: 917 return False 918 return True 919 920 def to_string(self): 921 s = "%s(" % self.ifname 922 i = 0 923 for a in self.args: 924 if isinstance(a, list): 925 str = list_to_space_str(a) 926 else: 927 str = a 928 929 if i != 0: 930 s = s + ", %s" % str 931 else: 932 s = s + str 933 i += 1 934 return s + ")" 935 936class OptionalPolicy(Node): 937 def __init__(self, parent=None): 938 Node.__init__(self, parent) 939 940 def to_string(self): 941 return "[Optional Policy]" 942 943class SupportMacros(Node): 944 def __init__(self, parent=None): 945 Node.__init__(self, parent) 946 self.map = None 947 948 def to_string(self): 949 return "[Support Macros]" 950 951 def __expand_perm(self, perm): 952 # Recursive expansion - the assumption is that these 953 # are ordered correctly so that no macro is used before 954 # it is defined 955 s = set() 956 if perm in self.map: 957 for p in self.by_name(perm): 958 s.update(self.__expand_perm(p)) 959 else: 960 s.add(perm) 961 return s 962 963 def __gen_map(self): 964 self.map = {} 965 for x in self: 966 exp_perms = set() 967 for perm in x.perms: 968 exp_perms.update(self.__expand_perm(perm)) 969 self.map[x.name] = exp_perms 970 971 def by_name(self, name): 972 if not self.map: 973 self.__gen_map() 974 return self.map[name] 975 976 def has_key(self, name): 977 if not self.map: 978 self.__gen_map() 979 return name in self.map 980 981class Require(Leaf): 982 def __init__(self, parent=None): 983 Leaf.__init__(self, parent) 984 self.types = IdSet() 985 self.obj_classes = { } 986 self.roles = IdSet() 987 self.data = IdSet() 988 self.users = IdSet() 989 990 def add_obj_class(self, obj_class, perms): 991 p = self.obj_classes.setdefault(obj_class, IdSet()) 992 p.update(perms) 993 994 995 def to_string(self): 996 s = [] 997 s.append("require {") 998 for type in self.types: 999 s.append("\ttype %s;" % type) 1000 for obj_class, perms in self.obj_classes.items(): 1001 s.append("\tclass %s %s;" % (obj_class, perms.to_space_str())) 1002 for role in self.roles: 1003 s.append("\trole %s;" % role) 1004 for bool in self.data: 1005 s.append("\tbool %s;" % bool) 1006 for user in self.users: 1007 s.append("\tuser %s;" % user) 1008 s.append("}") 1009 1010 # Handle empty requires 1011 if len(s) == 2: 1012 return "" 1013 1014 return "\n".join(s) 1015 1016 1017class ObjPermSet: 1018 def __init__(self, name): 1019 self.name = name 1020 self.perms = set() 1021 1022 def to_string(self): 1023 return "define(`%s', `%s')" % (self.name, self.perms.to_space_str()) 1024 1025class ClassMap: 1026 def __init__(self, obj_class, perms): 1027 self.obj_class = obj_class 1028 self.perms = perms 1029 1030 def to_string(self): 1031 return self.obj_class + ": " + self.perms 1032 1033class Comment: 1034 def __init__(self, l=None): 1035 if l: 1036 self.lines = l 1037 else: 1038 self.lines = [] 1039 1040 def to_string(self): 1041 # If there are no lines, treat this as a spacer between 1042 # policy statements and return a new line. 1043 if len(self.lines) == 0: 1044 return "" 1045 else: 1046 out = [] 1047 for line in self.lines: 1048 out.append("#" + line) 1049 return "\n".join(out) 1050 1051 def merge(self, other): 1052 if len(other.lines): 1053 for line in other.lines: 1054 if line != "": 1055 self.lines.append(line) 1056 1057 def __str__(self): 1058 return self.to_string() 1059 1060 1061