1# Author: Dan Walsh <dwalsh@redhat.com> 2# Author: Ryan Hallisey <rhallise@redhat.com> 3# Author: Jason Zaman <perfinion@gentoo.org> 4 5import errno 6import selinux 7import glob 8import sepolgen.defaults as defaults 9import sepolgen.interfaces as interfaces 10import sys 11import os 12import re 13import gzip 14 15from setools.boolquery import BoolQuery 16from setools.portconquery import PortconQuery 17from setools.policyrep import SELinuxPolicy 18from setools.objclassquery import ObjClassQuery 19from setools.rbacrulequery import RBACRuleQuery 20from setools.rolequery import RoleQuery 21from setools.terulequery import TERuleQuery 22from setools.typeattrquery import TypeAttributeQuery 23from setools.typequery import TypeQuery 24from setools.userquery import UserQuery 25 26PROGNAME = "selinux-python" 27try: 28 import gettext 29 kwargs = {} 30 if sys.version_info < (3,): 31 kwargs['unicode'] = True 32 t = gettext.translation(PROGNAME, 33 localedir="/usr/share/locale", 34 **kwargs, 35 fallback=True) 36 _ = t.gettext 37except: 38 try: 39 import builtins 40 builtins.__dict__['_'] = str 41 except ImportError: 42 import __builtin__ 43 __builtin__.__dict__['_'] = unicode 44 45TYPE = 1 46ROLE = 2 47ATTRIBUTE = 3 48PORT = 4 49USER = 5 50BOOLEAN = 6 51TCLASS = 7 52 53ALLOW = 'allow' 54AUDITALLOW = 'auditallow' 55NEVERALLOW = 'neverallow' 56DONTAUDIT = 'dontaudit' 57SOURCE = 'source' 58TARGET = 'target' 59PERMS = 'permlist' 60CLASS = 'class' 61TRANSITION = 'transition' 62ROLE_ALLOW = 'role_allow' 63 64 65# Autofill for adding files ************************* 66DEFAULT_DIRS = {} 67DEFAULT_DIRS["/etc"] = "etc_t" 68DEFAULT_DIRS["/tmp"] = "tmp_t" 69DEFAULT_DIRS["/usr/lib/systemd/system"] = "unit_file_t" 70DEFAULT_DIRS["/lib/systemd/system"] = "unit_file_t" 71DEFAULT_DIRS["/etc/systemd/system"] = "unit_file_t" 72DEFAULT_DIRS["/var/cache"] = "var_cache_t" 73DEFAULT_DIRS["/var/lib"] = "var_lib_t" 74DEFAULT_DIRS["/var/log"] = "log_t" 75DEFAULT_DIRS["/var/run"] = "var_run_t" 76DEFAULT_DIRS["/run"] = "var_run_t" 77DEFAULT_DIRS["/run/lock"] = "var_lock_t" 78DEFAULT_DIRS["/var/run/lock"] = "var_lock_t" 79DEFAULT_DIRS["/var/spool"] = "var_spool_t" 80DEFAULT_DIRS["/var/www"] = "content_t" 81 82file_type_str = {} 83file_type_str["a"] = _("all files") 84file_type_str["f"] = _("regular file") 85file_type_str["d"] = _("directory") 86file_type_str["c"] = _("character device") 87file_type_str["b"] = _("block device") 88file_type_str["s"] = _("socket file") 89file_type_str["l"] = _("symbolic link") 90file_type_str["p"] = _("named pipe") 91 92trans_file_type_str = {} 93trans_file_type_str[""] = "a" 94trans_file_type_str["--"] = "f" 95trans_file_type_str["-d"] = "d" 96trans_file_type_str["-c"] = "c" 97trans_file_type_str["-b"] = "b" 98trans_file_type_str["-s"] = "s" 99trans_file_type_str["-l"] = "l" 100trans_file_type_str["-p"] = "p" 101 102# the setools policy handle 103_pol = None 104 105# cache the lookup results 106file_equiv_modified = None 107file_equiv = None 108local_files = None 109fcdict = None 110methods = [] 111all_types = None 112all_types_info = None 113user_types = None 114role_allows = None 115portrecs = None 116portrecsbynum = None 117all_domains = None 118roles = None 119selinux_user_list = None 120login_mappings = None 121file_types = None 122port_types = None 123bools = None 124all_attributes = None 125booleans = None 126booleans_dict = None 127all_allow_rules = None 128all_bool_rules = None 129all_transitions = None 130 131 132def policy_sortkey(policy_path): 133 # Parse the extension of a policy path which looks like .../policy/policy.31 134 extension = policy_path.rsplit('/policy.', 1)[1] 135 try: 136 return int(extension), policy_path 137 except ValueError: 138 # Fallback with sorting on the full path 139 return 0, policy_path 140 141def get_installed_policy(root="/"): 142 try: 143 path = root + selinux.selinux_binary_policy_path() 144 policies = glob.glob("%s.*" % path) 145 policies.sort(key=policy_sortkey) 146 return policies[-1] 147 except: 148 pass 149 raise ValueError(_("No SELinux Policy installed")) 150 151def get_store_policy(store): 152 """Get the path to the policy file located in the given store name""" 153 policies = glob.glob("%s%s/policy/policy.*" % 154 (selinux.selinux_path(), store)) 155 if not policies: 156 return None 157 # Return the policy with the higher version number 158 policies.sort(key=policy_sortkey) 159 return policies[-1] 160 161def policy(policy_file): 162 global all_domains 163 global all_attributes 164 global bools 165 global all_types 166 global role_allows 167 global users 168 global roles 169 global file_types 170 global port_types 171 all_domains = None 172 all_attributes = None 173 bools = None 174 all_types = None 175 role_allows = None 176 users = None 177 roles = None 178 file_types = None 179 port_types = None 180 global _pol 181 182 try: 183 _pol = SELinuxPolicy(policy_file) 184 except: 185 raise ValueError(_("Failed to read %s policy file") % policy_file) 186 187def load_store_policy(store): 188 policy_file = get_store_policy(store) 189 if not policy_file: 190 return None 191 policy(policy_file) 192 193def init_policy(): 194 policy_file = get_installed_policy() 195 policy(policy_file) 196 197def info(setype, name=None): 198 global _pol 199 if not _pol: 200 init_policy() 201 202 if setype == TYPE: 203 q = TypeQuery(_pol) 204 q.name = name 205 results = list(q.results()) 206 207 if name and len(results) < 1: 208 # type not found, try alias 209 q.name = None 210 q.alias = name 211 results = list(q.results()) 212 213 return ({ 214 'aliases': list(map(str, x.aliases())), 215 'name': str(x), 216 'permissive': bool(x.ispermissive), 217 'attributes': list(map(str, x.attributes())) 218 } for x in results) 219 220 elif setype == ROLE: 221 q = RoleQuery(_pol) 222 if name: 223 q.name = name 224 225 return ({ 226 'name': str(x), 227 'roles': list(map(str, x.expand())), 228 'types': list(map(str, x.types())), 229 } for x in q.results()) 230 231 elif setype == ATTRIBUTE: 232 q = TypeAttributeQuery(_pol) 233 if name: 234 q.name = name 235 236 return ({ 237 'name': str(x), 238 'types': list(map(str, x.expand())), 239 } for x in q.results()) 240 241 elif setype == PORT: 242 q = PortconQuery(_pol) 243 if name: 244 ports = [int(i) for i in name.split("-")] 245 if len(ports) == 2: 246 q.ports = ports 247 elif len(ports) == 1: 248 q.ports = (ports[0], ports[0]) 249 250 if _pol.mls: 251 return ({ 252 'high': x.ports.high, 253 'protocol': str(x.protocol), 254 'range': str(x.context.range_), 255 'type': str(x.context.type_), 256 'low': x.ports.low, 257 } for x in q.results()) 258 return ({ 259 'high': x.ports.high, 260 'protocol': str(x.protocol), 261 'type': str(x.context.type_), 262 'low': x.ports.low, 263 } for x in q.results()) 264 265 elif setype == USER: 266 q = UserQuery(_pol) 267 if name: 268 q.name = name 269 270 if _pol.mls: 271 return ({ 272 'range': str(x.mls_range), 273 'name': str(x), 274 'roles': list(map(str, x.roles)), 275 'level': str(x.mls_level), 276 } for x in q.results()) 277 return ({ 278 'name': str(x), 279 'roles': list(map(str, x.roles)), 280 } for x in q.results()) 281 282 elif setype == BOOLEAN: 283 q = BoolQuery(_pol) 284 if name: 285 q.name = name 286 287 return ({ 288 'name': str(x), 289 'state': x.state, 290 } for x in q.results()) 291 292 elif setype == TCLASS: 293 q = ObjClassQuery(_pol) 294 if name: 295 q.name = name 296 297 return ({ 298 'name': str(x), 299 'permlist': list(x.perms), 300 } for x in q.results()) 301 302 else: 303 raise ValueError("Invalid type") 304 305 306def _setools_rule_to_dict(rule): 307 d = { 308 'type': str(rule.ruletype), 309 'source': str(rule.source), 310 'target': str(rule.target), 311 'class': str(rule.tclass), 312 } 313 314 # Evaluate boolean expression associated with given rule (if there is any) 315 try: 316 # Get state of all booleans in the conditional expression 317 boolstate = {} 318 for boolean in rule.conditional.booleans: 319 boolstate[str(boolean)] = boolean.state 320 # evaluate if the rule is enabled 321 enabled = rule.conditional.evaluate(**boolstate) == rule.conditional_block 322 except AttributeError: 323 # non-conditional rules are always enabled 324 enabled = True 325 326 d['enabled'] = enabled 327 328 try: 329 d['permlist'] = list(map(str, rule.perms)) 330 except AttributeError: 331 pass 332 333 try: 334 d['transtype'] = str(rule.default) 335 except AttributeError: 336 pass 337 338 try: 339 d['booleans'] = [(str(b), b.state) for b in rule.conditional.booleans] 340 except AttributeError: 341 pass 342 343 try: 344 d['conditional'] = str(rule.conditional) 345 except AttributeError: 346 pass 347 348 try: 349 d['filename'] = rule.filename 350 except AttributeError: 351 pass 352 353 return d 354 355 356def search(types, seinfo=None): 357 global _pol 358 if not _pol: 359 init_policy() 360 if not seinfo: 361 seinfo = {} 362 valid_types = set([ALLOW, AUDITALLOW, NEVERALLOW, DONTAUDIT, TRANSITION, ROLE_ALLOW]) 363 for setype in types: 364 if setype not in valid_types: 365 raise ValueError("Type has to be in %s" % " ".join(valid_types)) 366 367 source = None 368 if SOURCE in seinfo: 369 source = str(seinfo[SOURCE]) 370 371 target = None 372 if TARGET in seinfo: 373 target = str(seinfo[TARGET]) 374 375 tclass = None 376 if CLASS in seinfo: 377 tclass = str(seinfo[CLASS]).split(',') 378 379 toret = [] 380 381 tertypes = [] 382 if ALLOW in types: 383 tertypes.append(ALLOW) 384 if NEVERALLOW in types: 385 tertypes.append(NEVERALLOW) 386 if AUDITALLOW in types: 387 tertypes.append(AUDITALLOW) 388 if DONTAUDIT in types: 389 tertypes.append(DONTAUDIT) 390 391 if len(tertypes) > 0: 392 q = TERuleQuery(_pol, 393 ruletype=tertypes, 394 source=source, 395 target=target, 396 tclass=tclass) 397 398 if PERMS in seinfo: 399 q.perms = seinfo[PERMS] 400 401 toret += [_setools_rule_to_dict(x) for x in q.results()] 402 403 if TRANSITION in types: 404 rtypes = ['type_transition', 'type_change', 'type_member'] 405 q = TERuleQuery(_pol, 406 ruletype=rtypes, 407 source=source, 408 target=target, 409 tclass=tclass) 410 411 if PERMS in seinfo: 412 q.perms = seinfo[PERMS] 413 414 toret += [_setools_rule_to_dict(x) for x in q.results()] 415 416 if ROLE_ALLOW in types: 417 ratypes = ['allow'] 418 q = RBACRuleQuery(_pol, 419 ruletype=ratypes, 420 source=source, 421 target=target, 422 tclass=tclass) 423 424 for r in q.results(): 425 toret.append({'source': str(r.source), 426 'target': str(r.target)}) 427 428 return toret 429 430 431def get_conditionals(src, dest, tclass, perm): 432 tdict = {} 433 tlist = [] 434 src_list = [src] 435 dest_list = [dest] 436 # add assigned attributes 437 try: 438 src_list += list(filter(lambda x: x['name'] == src, get_all_types_info()))[0]['attributes'] 439 except: 440 pass 441 try: 442 dest_list += list(filter(lambda x: x['name'] == dest, get_all_types_info()))[0]['attributes'] 443 except: 444 pass 445 allows = map(lambda y: y, filter(lambda x: 446 x['source'] in src_list and 447 x['target'] in dest_list and 448 set(perm).issubset(x[PERMS]) and 449 'conditional' in x, 450 get_all_allow_rules())) 451 452 try: 453 for i in allows: 454 tdict.update({'source': i['source'], 'conditional': (i['conditional'], i['enabled'])}) 455 if tdict not in tlist: 456 tlist.append(tdict) 457 tdict = {} 458 except KeyError: 459 return tlist 460 461 return tlist 462 463 464def get_conditionals_format_text(cond): 465 466 enabled = False 467 for x in cond: 468 if x['conditional'][1]: 469 enabled = True 470 break 471 return _("-- Allowed %s [ %s ]") % (enabled, " || ".join(set(map(lambda x: "%s=%d" % (x['conditional'][0], x['conditional'][1]), cond)))) 472 473 474def get_types_from_attribute(attribute): 475 return list(info(ATTRIBUTE, attribute))[0]["types"] 476 477 478def get_file_types(setype): 479 flist = [] 480 mpaths = {} 481 for f in get_all_file_types(): 482 if f.startswith(gen_short_name(setype)): 483 flist.append(f) 484 fcdict = get_fcdict() 485 for f in flist: 486 try: 487 mpaths[f] = (fcdict[f]["regex"], file_type_str[fcdict[f]["ftype"]]) 488 except KeyError: 489 mpaths[f] = [] 490 return mpaths 491 492 493def get_real_type_name(name): 494 """Return the real name of a type 495 496 * If 'name' refers to a type alias, return the corresponding type name. 497 * Otherwise return the original name (even if the type does not exist). 498 """ 499 if not name: 500 return name 501 502 try: 503 return next(info(TYPE, name))["name"] 504 except (RuntimeError, StopIteration): 505 return name 506 507def get_writable_files(setype): 508 file_types = get_all_file_types() 509 all_writes = [] 510 mpaths = {} 511 permlist = search([ALLOW], {'source': setype, 'permlist': ['open', 'write'], 'class': 'file'}) 512 if permlist is None or len(permlist) == 0: 513 return mpaths 514 515 fcdict = get_fcdict() 516 517 attributes = ["proc_type", "sysctl_type"] 518 for i in permlist: 519 if i['target'] in attributes: 520 continue 521 if "enabled" in i: 522 if not i["enabled"]: 523 continue 524 if i['target'].endswith("_t"): 525 if i['target'] not in file_types: 526 continue 527 if i['target'] not in all_writes: 528 if i['target'] != setype: 529 all_writes.append(i['target']) 530 else: 531 for t in get_types_from_attribute(i['target']): 532 if t not in all_writes: 533 all_writes.append(t) 534 535 for f in all_writes: 536 try: 537 mpaths[f] = (fcdict[f]["regex"], file_type_str[fcdict[f]["ftype"]]) 538 except KeyError: 539 mpaths[f] = [] # {"regex":[],"paths":[]} 540 return mpaths 541 542 543def find_file(reg): 544 if os.path.exists(reg): 545 return [reg] 546 try: 547 pat = re.compile(r"%s$" % reg) 548 except: 549 print("bad reg:", reg) 550 return [] 551 p = reg 552 if p.endswith("(/.*)?"): 553 p = p[:-6] + "/" 554 555 path = os.path.dirname(p) 556 557 try: # Bug fix: when "all files on system" 558 if path[-1] != "/": # is pass in it breaks without try block 559 path += "/" 560 except IndexError: 561 print("try failed got an IndexError") 562 563 try: 564 pat = re.compile(r"%s$" % reg) 565 return [x for x in map(lambda x: path + x, os.listdir(path)) if pat.match(x)] 566 except: 567 return [] 568 569 570def find_all_files(domain, exclude_list=[]): 571 executable_files = get_entrypoints(domain) 572 for exe in executable_files.keys(): 573 if exe.endswith("_exec_t") and exe not in exclude_list: 574 for path in executable_files[exe]: 575 for f in find_file(path): 576 return f 577 return None 578 579 580def find_entrypoint_path(exe, exclude_list=[]): 581 fcdict = get_fcdict() 582 try: 583 if exe.endswith("_exec_t") and exe not in exclude_list: 584 for path in fcdict[exe]["regex"]: 585 for f in find_file(path): 586 return f 587 except KeyError: 588 pass 589 return None 590 591 592def read_file_equiv(edict, fc_path, modify): 593 try: 594 with open(fc_path, "r") as fd: 595 for e in fd: 596 f = e.split() 597 if f and not f[0].startswith('#'): 598 edict[f[0]] = {"equiv": f[1], "modify": modify} 599 except OSError as e: 600 if e.errno != errno.ENOENT: 601 raise 602 return edict 603 604 605def get_file_equiv_modified(fc_path=selinux.selinux_file_context_path()): 606 global file_equiv_modified 607 if file_equiv_modified: 608 return file_equiv_modified 609 file_equiv_modified = {} 610 file_equiv_modified = read_file_equiv(file_equiv_modified, fc_path + ".subs", modify=True) 611 return file_equiv_modified 612 613 614def get_file_equiv(fc_path=selinux.selinux_file_context_path()): 615 global file_equiv 616 if file_equiv: 617 return file_equiv 618 file_equiv = get_file_equiv_modified(fc_path) 619 file_equiv = read_file_equiv(file_equiv, fc_path + ".subs_dist", modify=False) 620 return file_equiv 621 622 623def get_local_file_paths(fc_path=selinux.selinux_file_context_path()): 624 global local_files 625 if local_files: 626 return local_files 627 local_files = [] 628 try: 629 with open(fc_path + ".local", "r") as fd: 630 fc = fd.readlines() 631 except OSError as e: 632 if e.errno != errno.ENOENT: 633 raise 634 return [] 635 for i in fc: 636 rec = i.split() 637 if len(rec) == 0: 638 continue 639 try: 640 if len(rec) > 2: 641 ftype = trans_file_type_str[rec[1]] 642 else: 643 ftype = "a" 644 645 local_files.append((rec[0], ftype)) 646 except KeyError: 647 pass 648 return local_files 649 650 651def get_fcdict(fc_path=selinux.selinux_file_context_path()): 652 global fcdict 653 if fcdict: 654 return fcdict 655 fd = open(fc_path, "r") 656 fc = fd.readlines() 657 fd.close() 658 fd = open(fc_path + ".homedirs", "r") 659 fc += fd.readlines() 660 fd.close() 661 fcdict = {} 662 try: 663 with open(fc_path + ".local", "r") as fd: 664 fc += fd.readlines() 665 except OSError as e: 666 if e.errno != errno.ENOENT: 667 raise 668 669 for i in fc: 670 rec = i.split() 671 try: 672 if len(rec) > 2: 673 ftype = trans_file_type_str[rec[1]] 674 else: 675 ftype = "a" 676 677 t = rec[-1].split(":")[2] 678 if t in fcdict: 679 fcdict[t]["regex"].append(rec[0]) 680 else: 681 fcdict[t] = {"regex": [rec[0]], "ftype": ftype} 682 except: 683 pass 684 685 fcdict["logfile"] = {"regex": ["all log files"]} 686 fcdict["user_tmp_type"] = {"regex": ["all user tmp files"]} 687 fcdict["user_home_type"] = {"regex": ["all user home files"]} 688 fcdict["virt_image_type"] = {"regex": ["all virtual image files"]} 689 fcdict["noxattrfs"] = {"regex": ["all files on file systems which do not support extended attributes"]} 690 fcdict["sandbox_tmpfs_type"] = {"regex": ["all sandbox content in tmpfs file systems"]} 691 fcdict["user_tmpfs_type"] = {"regex": ["all user content in tmpfs file systems"]} 692 fcdict["file_type"] = {"regex": ["all files on the system"]} 693 fcdict["samba_share_t"] = {"regex": ["use this label for random content that will be shared using samba"]} 694 return fcdict 695 696 697def get_transitions_into(setype): 698 try: 699 return [x for x in search([TRANSITION], {'class': 'process'}) if x["transtype"] == setype] 700 except (TypeError, AttributeError): 701 pass 702 return None 703 704 705def get_transitions(setype): 706 try: 707 return search([TRANSITION], {'source': setype, 'class': 'process'}) 708 except (TypeError, AttributeError): 709 pass 710 return None 711 712 713def get_file_transitions(setype): 714 try: 715 return [x for x in search([TRANSITION], {'source': setype}) if x['class'] != "process"] 716 except (TypeError, AttributeError): 717 pass 718 return None 719 720 721def get_boolean_rules(setype, boolean): 722 boollist = [] 723 permlist = search([ALLOW], {'source': setype}) 724 for p in permlist: 725 if "booleans" in p: 726 try: 727 for b in p["booleans"]: 728 if boolean in b: 729 boollist.append(p) 730 except: 731 pass 732 return boollist 733 734 735def get_all_entrypoints(): 736 return get_types_from_attribute("entry_type") 737 738 739def get_entrypoint_types(setype): 740 q = TERuleQuery(_pol, 741 ruletype=[ALLOW], 742 source=setype, 743 tclass=["file"], 744 perms=["entrypoint"]) 745 return [str(x.target) for x in q.results() if x.source == setype] 746 747 748def get_init_transtype(path): 749 entrypoint = selinux.getfilecon(path)[1].split(":")[2] 750 try: 751 entrypoints = list(filter(lambda x: x['target'] == entrypoint, search([TRANSITION], {'source': "init_t", 'class': 'process'}))) 752 return entrypoints[0]["transtype"] 753 except (TypeError, AttributeError, IndexError): 754 pass 755 return None 756 757 758def get_init_entrypoint(transtype): 759 q = TERuleQuery(_pol, 760 ruletype=["type_transition"], 761 source="init_t", 762 tclass=["process"]) 763 entrypoints = [] 764 for i in q.results(): 765 try: 766 if i.default == transtype: 767 entrypoints.append(i.target) 768 except AttributeError: 769 continue 770 771 return entrypoints 772 773def get_init_entrypoints_str(): 774 q = TERuleQuery(_pol, 775 ruletype=["type_transition"], 776 source="init_t", 777 tclass=["process"]) 778 entrypoints = {} 779 for i in q.results(): 780 try: 781 transtype = str(i.default) 782 if transtype in entrypoints: 783 entrypoints[transtype].append(str(i.target)) 784 else: 785 entrypoints[transtype] = [str(i.target)] 786 except AttributeError: 787 continue 788 789 return entrypoints 790 791def get_init_entrypoint_target(entrypoint): 792 try: 793 entrypoints = map(lambda x: x['transtype'], search([TRANSITION], {'source': "init_t", 'target': entrypoint, 'class': 'process'})) 794 return list(entrypoints)[0] 795 except (TypeError, IndexError): 796 pass 797 return None 798 799 800def get_entrypoints(setype): 801 fcdict = get_fcdict() 802 mpaths = {} 803 for f in get_entrypoint_types(setype): 804 try: 805 mpaths[f] = (fcdict[f]["regex"], file_type_str[fcdict[f]["ftype"]]) 806 except KeyError: 807 mpaths[f] = [] 808 return mpaths 809 810 811def get_methods(): 812 global methods 813 if len(methods) > 0: 814 return methods 815 gen_interfaces() 816 fn = defaults.interface_info() 817 try: 818 fd = open(fn) 819 # List of per_role_template interfaces 820 ifs = interfaces.InterfaceSet() 821 ifs.from_file(fd) 822 methods = list(ifs.interfaces.keys()) 823 fd.close() 824 except: 825 sys.stderr.write("could not open interface info [%s]\n" % fn) 826 sys.exit(1) 827 828 methods.sort() 829 return methods 830 831 832def get_all_types(): 833 global all_types 834 if all_types is None: 835 all_types = [x['name'] for x in info(TYPE)] 836 return all_types 837 838def get_all_types_info(): 839 global all_types_info 840 if all_types_info is None: 841 all_types_info = list(info(TYPE)) 842 return all_types_info 843 844def get_user_types(): 845 global user_types 846 if user_types is None: 847 user_types = list(list(info(ATTRIBUTE, "userdomain"))[0]["types"]) 848 return user_types 849 850 851def get_all_role_allows(): 852 global role_allows 853 if role_allows: 854 return role_allows 855 role_allows = {} 856 857 q = RBACRuleQuery(_pol, ruletype=[ALLOW]) 858 for r in q.results(): 859 src = str(r.source) 860 tgt = str(r.target) 861 if src == "system_r" or tgt == "system_r": 862 continue 863 if src in role_allows: 864 role_allows[src].append(tgt) 865 else: 866 role_allows[src] = [tgt] 867 868 return role_allows 869 870 871def get_all_entrypoint_domains(): 872 import re 873 all_domains = [] 874 types = sorted(get_all_types()) 875 for i in types: 876 m = re.findall("(.*)%s" % "_exec_t$", i) 877 if len(m) > 0: 878 if len(re.findall("(.*)%s" % "_initrc$", m[0])) == 0 and m[0] not in all_domains: 879 all_domains.append(m[0]) 880 return all_domains 881 882 883def gen_interfaces(): 884 try: 885 from commands import getstatusoutput 886 except ImportError: 887 from subprocess import getstatusoutput 888 ifile = defaults.interface_info() 889 headers = defaults.headers() 890 try: 891 if os.stat(headers).st_mtime <= os.stat(ifile).st_mtime: 892 return 893 except OSError: 894 pass 895 896 if os.getuid() != 0: 897 raise ValueError(_("You must regenerate interface info by running /usr/bin/sepolgen-ifgen")) 898 print(getstatusoutput("/usr/bin/sepolgen-ifgen")[1]) 899 900 901def gen_port_dict(): 902 global portrecs 903 global portrecsbynum 904 if portrecs: 905 return (portrecs, portrecsbynum) 906 portrecsbynum = {} 907 portrecs = {} 908 for i in info(PORT): 909 if i['low'] == i['high']: 910 port = str(i['low']) 911 else: 912 port = "%s-%s" % (str(i['low']), str(i['high'])) 913 914 if (i['type'], i['protocol']) in portrecs: 915 portrecs[(i['type'], i['protocol'])].append(port) 916 else: 917 portrecs[(i['type'], i['protocol'])] = [port] 918 919 if 'range' in i: 920 portrecsbynum[(i['low'], i['high'], i['protocol'])] = (i['type'], i['range']) 921 else: 922 portrecsbynum[(i['low'], i['high'], i['protocol'])] = (i['type']) 923 924 return (portrecs, portrecsbynum) 925 926 927def get_all_domains(): 928 global all_domains 929 if not all_domains: 930 all_domains = list(list(info(ATTRIBUTE, "domain"))[0]["types"]) 931 return all_domains 932 933 934def get_all_roles(): 935 global roles 936 if roles: 937 return roles 938 939 global _pol 940 if not _pol: 941 init_policy() 942 943 q = RoleQuery(_pol) 944 roles = [str(x) for x in q.results() if str(x) != "object_r"] 945 return roles 946 947 948def get_selinux_users(): 949 global selinux_user_list 950 if not selinux_user_list: 951 selinux_user_list = list(info(USER)) 952 if _pol.mls: 953 for x in selinux_user_list: 954 x['range'] = "".join(x['range'].split(" ")) 955 return selinux_user_list 956 957 958def get_login_mappings(): 959 global login_mappings 960 if login_mappings: 961 return login_mappings 962 963 fd = open(selinux.selinux_usersconf_path(), "r") 964 buf = fd.read() 965 fd.close() 966 login_mappings = [] 967 for b in buf.split("\n"): 968 b = b.strip() 969 if len(b) == 0 or b.startswith("#"): 970 continue 971 x = b.split(":") 972 login_mappings.append({"name": x[0], "seuser": x[1], "mls": ":".join(x[2:])}) 973 return login_mappings 974 975 976def get_all_users(): 977 return sorted(map(lambda x: x['name'], get_selinux_users())) 978 979 980def get_all_file_types(): 981 global file_types 982 if file_types: 983 return file_types 984 file_types = list(sorted(info(ATTRIBUTE, "file_type"))[0]["types"]) 985 return file_types 986 987 988def get_all_port_types(): 989 global port_types 990 if port_types: 991 return port_types 992 port_types = list(sorted(info(ATTRIBUTE, "port_type"))[0]["types"]) 993 return port_types 994 995 996def get_all_bools(): 997 global bools 998 if not bools: 999 bools = list(info(BOOLEAN)) 1000 return bools 1001 1002 1003def prettyprint(f, trim): 1004 return " ".join(f[:-len(trim)].split("_")) 1005 1006 1007def markup(f): 1008 return f 1009 1010 1011def get_description(f, markup=markup): 1012 1013 txt = "Set files with the %s type, if you want to " % markup(f) 1014 1015 if f.endswith("_var_run_t"): 1016 return txt + "store the %s files under the /run or /var/run directory." % prettyprint(f, "_var_run_t") 1017 if f.endswith("_pid_t"): 1018 return txt + "store the %s files under the /run directory." % prettyprint(f, "_pid_t") 1019 if f.endswith("_var_lib_t"): 1020 return txt + "store the %s files under the /var/lib directory." % prettyprint(f, "_var_lib_t") 1021 if f.endswith("_var_t"): 1022 return txt + "store the %s files under the /var directory." % prettyprint(f, "_var_lib_t") 1023 if f.endswith("_var_spool_t"): 1024 return txt + "store the %s files under the /var/spool directory." % prettyprint(f, "_spool_t") 1025 if f.endswith("_spool_t"): 1026 return txt + "store the %s files under the /var/spool directory." % prettyprint(f, "_spool_t") 1027 if f.endswith("_cache_t") or f.endswith("_var_cache_t"): 1028 return txt + "store the files under the /var/cache directory." 1029 if f.endswith("_keytab_t"): 1030 return txt + "treat the files as kerberos keytab files." 1031 if f.endswith("_lock_t"): 1032 return txt + "treat the files as %s lock data, stored under the /var/lock directory" % prettyprint(f, "_lock_t") 1033 if f.endswith("_log_t"): 1034 return txt + "treat the data as %s log data, usually stored under the /var/log directory." % prettyprint(f, "_log_t") 1035 if f.endswith("_config_t"): 1036 return txt + "treat the files as %s configuration data, usually stored under the /etc directory." % prettyprint(f, "_config_t") 1037 if f.endswith("_conf_t"): 1038 return txt + "treat the files as %s configuration data, usually stored under the /etc directory." % prettyprint(f, "_conf_t") 1039 if f.endswith("_exec_t"): 1040 return txt + "transition an executable to the %s_t domain." % f[:-len("_exec_t")] 1041 if f.endswith("_cgi_content_t"): 1042 return txt + "treat the files as %s cgi content." % prettyprint(f, "_cgi_content_t") 1043 if f.endswith("_rw_content_t"): 1044 return txt + "treat the files as %s read/write content." % prettyprint(f, "_rw_content_t") 1045 if f.endswith("_rw_t"): 1046 return txt + "treat the files as %s read/write content." % prettyprint(f, "_rw_t") 1047 if f.endswith("_write_t"): 1048 return txt + "treat the files as %s read/write content." % prettyprint(f, "_write_t") 1049 if f.endswith("_db_t"): 1050 return txt + "treat the files as %s database content." % prettyprint(f, "_db_t") 1051 if f.endswith("_ra_content_t"): 1052 return txt + "treat the files as %s read/append content." % prettyprint(f, "_ra_content_t") 1053 if f.endswith("_cert_t"): 1054 return txt + "treat the files as %s certificate data." % prettyprint(f, "_cert_t") 1055 if f.endswith("_key_t"): 1056 return txt + "treat the files as %s key data." % prettyprint(f, "_key_t") 1057 1058 if f.endswith("_secret_t"): 1059 return txt + "treat the files as %s secret data." % prettyprint(f, "_secret_t") 1060 1061 if f.endswith("_ra_t"): 1062 return txt + "treat the files as %s read/append content." % prettyprint(f, "_ra_t") 1063 1064 if f.endswith("_ro_t"): 1065 return txt + "treat the files as %s read/only content." % prettyprint(f, "_ro_t") 1066 1067 if f.endswith("_modules_t"): 1068 return txt + "treat the files as %s modules." % prettyprint(f, "_modules_t") 1069 1070 if f.endswith("_content_t"): 1071 return txt + "treat the files as %s content." % prettyprint(f, "_content_t") 1072 1073 if f.endswith("_state_t"): 1074 return txt + "treat the files as %s state data." % prettyprint(f, "_state_t") 1075 1076 if f.endswith("_files_t"): 1077 return txt + "treat the files as %s content." % prettyprint(f, "_files_t") 1078 1079 if f.endswith("_file_t"): 1080 return txt + "treat the files as %s content." % prettyprint(f, "_file_t") 1081 1082 if f.endswith("_data_t"): 1083 return txt + "treat the files as %s content." % prettyprint(f, "_data_t") 1084 1085 if f.endswith("_file_t"): 1086 return txt + "treat the data as %s content." % prettyprint(f, "_file_t") 1087 1088 if f.endswith("_tmp_t"): 1089 return txt + "store %s temporary files in the /tmp directories." % prettyprint(f, "_tmp_t") 1090 if f.endswith("_etc_t"): 1091 return txt + "store %s files in the /etc directories." % prettyprint(f, "_etc_t") 1092 if f.endswith("_home_t"): 1093 return txt + "store %s files in the users home directory." % prettyprint(f, "_home_t") 1094 if f.endswith("_tmpfs_t"): 1095 return txt + "store %s files on a tmpfs file system." % prettyprint(f, "_tmpfs_t") 1096 if f.endswith("_unit_file_t"): 1097 return txt + "treat files as a systemd unit file." 1098 if f.endswith("_htaccess_t"): 1099 return txt + "treat the file as a %s access file." % prettyprint(f, "_htaccess_t") 1100 1101 return txt + "treat the files as %s data." % prettyprint(f, "_t") 1102 1103 1104def get_all_attributes(): 1105 global all_attributes 1106 if not all_attributes: 1107 all_attributes = list(sorted(map(lambda x: x['name'], info(ATTRIBUTE)))) 1108 return all_attributes 1109 1110 1111def _dict_has_perms(dict, perms): 1112 for perm in perms: 1113 if perm not in dict[PERMS]: 1114 return False 1115 return True 1116 1117 1118def gen_short_name(setype): 1119 all_domains = get_all_domains() 1120 if setype.endswith("_t"): 1121 # replace aliases with corresponding types 1122 setype = get_real_type_name(setype) 1123 domainname = setype[:-2] 1124 else: 1125 domainname = setype 1126 if domainname + "_t" not in all_domains: 1127 raise ValueError("domain %s_t does not exist" % domainname) 1128 if domainname[-1] == 'd': 1129 short_name = domainname[:-1] + "_" 1130 else: 1131 short_name = domainname + "_" 1132 return (domainname, short_name) 1133 1134def get_all_allow_rules(): 1135 global all_allow_rules 1136 if not all_allow_rules: 1137 all_allow_rules = search([ALLOW]) 1138 return all_allow_rules 1139 1140def get_all_bool_rules(): 1141 global all_bool_rules 1142 if not all_bool_rules: 1143 q = TERuleQuery(_pol, boolean=".*", boolean_regex=True, 1144 ruletype=[ALLOW, DONTAUDIT]) 1145 all_bool_rules = [_setools_rule_to_dict(x) for x in q.results()] 1146 return all_bool_rules 1147 1148def get_all_transitions(): 1149 global all_transitions 1150 if not all_transitions: 1151 all_transitions = list(search([TRANSITION])) 1152 return all_transitions 1153 1154def get_bools(setype): 1155 bools = [] 1156 domainbools = [] 1157 domainname, short_name = gen_short_name(setype) 1158 for i in map(lambda x: x['booleans'], filter(lambda x: 'booleans' in x and x['source'] == setype, get_all_bool_rules())): 1159 for b in i: 1160 if not isinstance(b, tuple): 1161 continue 1162 try: 1163 enabled = selinux.security_get_boolean_active(b[0]) 1164 except OSError: 1165 enabled = b[1] 1166 if b[0].startswith(short_name) or b[0].startswith(domainname): 1167 if (b[0], enabled) not in domainbools and (b[0], not enabled) not in domainbools: 1168 domainbools.append((b[0], enabled)) 1169 else: 1170 if (b[0], enabled) not in bools and (b[0], not enabled) not in bools: 1171 bools.append((b[0], enabled)) 1172 return (domainbools, bools) 1173 1174 1175def get_all_booleans(): 1176 global booleans 1177 if not booleans: 1178 booleans = selinux.security_get_boolean_names()[1] 1179 return booleans 1180 1181 1182def policy_xml(path="/usr/share/selinux/devel/policy.xml"): 1183 try: 1184 fd = gzip.open(path) 1185 buf = fd.read() 1186 fd.close() 1187 except IOError: 1188 fd = open(path) 1189 buf = fd.read() 1190 fd.close() 1191 return buf 1192 1193 1194def gen_bool_dict(path="/usr/share/selinux/devel/policy.xml"): 1195 global booleans_dict 1196 if booleans_dict: 1197 return booleans_dict 1198 import xml.etree.ElementTree 1199 booleans_dict = {} 1200 try: 1201 tree = xml.etree.ElementTree.fromstring(policy_xml(path)) 1202 for l in tree.findall("layer"): 1203 for m in l.findall("module"): 1204 for b in m.findall("tunable"): 1205 desc = b.find("desc").find("p").text.strip("\n") 1206 desc = re.sub("\n", " ", desc) 1207 booleans_dict[b.get('name')] = (m.get("name"), b.get('dftval'), desc) 1208 for b in m.findall("bool"): 1209 desc = b.find("desc").find("p").text.strip("\n") 1210 desc = re.sub("\n", " ", desc) 1211 booleans_dict[b.get('name')] = (m.get("name"), b.get('dftval'), desc) 1212 for i in tree.findall("bool"): 1213 desc = i.find("desc").find("p").text.strip("\n") 1214 desc = re.sub("\n", " ", desc) 1215 booleans_dict[i.get('name')] = ("global", i.get('dftval'), desc) 1216 for i in tree.findall("tunable"): 1217 desc = i.find("desc").find("p").text.strip("\n") 1218 desc = re.sub("\n", " ", desc) 1219 booleans_dict[i.get('name')] = ("global", i.get('dftval'), desc) 1220 except IOError: 1221 pass 1222 return booleans_dict 1223 1224 1225def boolean_category(boolean): 1226 booleans_dict = gen_bool_dict() 1227 if boolean in booleans_dict: 1228 return _(booleans_dict[boolean][0]) 1229 else: 1230 return _("unknown") 1231 1232 1233def boolean_desc(boolean): 1234 booleans_dict = gen_bool_dict() 1235 if boolean in booleans_dict: 1236 return _(booleans_dict[boolean][2]) 1237 else: 1238 desc = boolean.split("_") 1239 return _("Allow {subject} to {rest}").format(subject=desc[0], rest=" ".join(desc[1:])) 1240 1241 1242def get_os_version(): 1243 system_release = "" 1244 try: 1245 import distro 1246 system_release = distro.name(pretty=True) 1247 except IOError: 1248 system_release = "Misc" 1249 1250 return system_release 1251 1252 1253def reinit(): 1254 global all_attributes 1255 global all_domains 1256 global all_types 1257 global booleans 1258 global booleans_dict 1259 global bools 1260 global fcdict 1261 global file_types 1262 global local_files 1263 global methods 1264 global methods 1265 global portrecs 1266 global portrecsbynum 1267 global port_types 1268 global role_allows 1269 global roles 1270 global login_mappings 1271 global selinux_user_list 1272 global user_types 1273 all_attributes = None 1274 all_domains = None 1275 all_types = None 1276 booleans = None 1277 booleans_dict = None 1278 bools = None 1279 fcdict = None 1280 file_types = None 1281 local_files = None 1282 methods = None 1283 methods = None 1284 portrecs = None 1285 portrecsbynum = None 1286 port_types = None 1287 role_allows = None 1288 roles = None 1289 user_types = None 1290 login_mappings = None 1291 selinux_user_list = None 1292