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