From 2a1e3be82873e1b7fc3b41aa0de7019d0a838f21 Mon Sep 17 00:00:00 2001 From: Miroslav Grepl Date: Tue, 23 Sep 2014 15:25:06 +0200 Subject: [PATCH] - Improvements to audit2allow from rhallise@redhat.com * Check for mislabeled files. * Check for base file use and * Suggest writable files as alternatives --- 0002-audit2allow-improvements.patch | 235 ++++++++++++++++++++++++++++ policycoreutils.spec | 9 +- 2 files changed, 243 insertions(+), 1 deletion(-) create mode 100644 0002-audit2allow-improvements.patch diff --git a/0002-audit2allow-improvements.patch b/0002-audit2allow-improvements.patch new file mode 100644 index 0000000..30766fa --- /dev/null +++ b/0002-audit2allow-improvements.patch @@ -0,0 +1,235 @@ +diff --git a/sepolgen/src/sepolgen/access.py b/sepolgen/src/sepolgen/access.py +index cf13210..9154887 100644 +--- a/sepolgen/src/sepolgen/access.py ++++ b/sepolgen/src/sepolgen/access.py +@@ -88,6 +88,8 @@ class AccessVector: + self.audit_msgs = [] + self.type = audit2why.TERULE + self.data = [] ++ self.obj_path = None ++ self.base_type = None + + # The direction of the information flow represented by this + # access vector - used for matching +@@ -133,6 +135,11 @@ class AccessVector: + return "allow %s %s:%s %s;" % (self.src_type, self.tgt_type, + self.obj_class, self.perms.to_space_str()) + ++ def base_file_type(self): ++ base_type_array = [] ++ base_type_array = [self.base_type, self.tgt_type, self.src_type] ++ return base_type_array ++ + def __cmp__(self, other): + if self.src_type != other.src_type: + return cmp(self.src_type, other.src_type) +@@ -256,7 +263,8 @@ class AccessVectorSet: + for av in l: + self.add_av(AccessVector(av)) + +- def add(self, src_type, tgt_type, obj_class, perms, audit_msg=None, avc_type=audit2why.TERULE, data=[]): ++ def add(self, src_type, tgt_type, obj_class, perms, obj_path=None, ++ base_type=None, audit_msg=None, avc_type=audit2why.TERULE, data=[]): + """Add an access vector to the set. + """ + tgt = self.src.setdefault(src_type, { }) +@@ -269,7 +277,9 @@ class AccessVectorSet: + access.src_type = src_type + access.tgt_type = tgt_type + access.obj_class = obj_class ++ access.obj_path = obj_path + access.data = data ++ access.base_type = base_type + access.type = avc_type + cls[obj_class, avc_type] = access + +diff --git a/sepolgen/src/sepolgen/audit.py b/sepolgen/src/sepolgen/audit.py +index 56919be..57263d0 100644 +--- a/sepolgen/src/sepolgen/audit.py ++++ b/sepolgen/src/sepolgen/audit.py +@@ -169,6 +169,7 @@ class AVCMessage(AuditMessage): + self.exe = "" + self.path = "" + self.name = "" ++ self.ino = "" + self.accesses = [] + self.denial = True + self.type = audit2why.TERULE +@@ -230,6 +231,10 @@ class AVCMessage(AuditMessage): + self.exe = fields[1][1:-1] + elif fields[0] == "name": + self.name = fields[1][1:-1] ++ elif fields[0] == "path": ++ self.path = fields[1][1:-1] ++ elif fields[0] == "ino": ++ self.ino = fields[1] + + if not found_src or not found_tgt or not found_class or not found_access: + raise ValueError("AVC message in invalid format [%s]\n" % self.message) +@@ -354,7 +359,9 @@ class AuditParser: + self.path_msgs = [] + self.by_header = { } + self.check_input_file = False +- ++ self.inode_dict = { } ++ self.__store_base_types() ++ + # Low-level parsing function - tries to determine if this audit + # message is an SELinux related message and then parses it into + # the appropriate AuditMessage subclass. This function deliberately +@@ -492,6 +499,60 @@ class AuditParser: + + return role_types + ++ def __restore_path(self, name, inode): ++ import subprocess ++ import os ++ path = "" ++ # Optimizing ++ if name == "" or inode == "": ++ return path ++ for d in self.inode_dict: ++ if d == inode and self.inode_dict[d] == name: ++ return path ++ if d == inode and self.inode_dict[d] != name: ++ return self.inode_dict[d] ++ if inode not in self.inode_dict.keys(): ++ self.inode_dict[inode] = name ++ ++ command = "locate -b '\%s'" % name ++ try: ++ output = subprocess.check_output(command, ++ stderr=subprocess.STDOUT, ++ shell=True) ++ try: ++ ino = int(inode) ++ except ValueError: ++ pass ++ for file in output.split("\n"): ++ try: ++ if int(os.lstat(file).st_ino) == ino: ++ self.inode_dict[inode] = path = file ++ return path ++ except: ++ pass ++ except subprocess.CalledProcessError as e: ++ pass ++ return path ++ ++ def __store_base_types(self): ++ import sepolicy ++ self.base_types = sepolicy.get_types_from_attribute("base_file_type") ++ ++ def __get_base_type(self, tcontext, scontext): ++ import sepolicy ++ # Prevent unnecessary searching ++ if (self.old_scontext == scontext and ++ self.old_tcontext == tcontext): ++ return ++ self.old_scontext = scontext ++ self.old_tcontext = tcontext ++ for btype in self.base_types: ++ if btype == tcontext: ++ for writable in sepolicy.get_writable_files(scontext): ++ if writable.endswith(tcontext) and writable.startswith(scontext.rstrip("_t")): ++ return writable ++ return 0 ++ + def to_access(self, avc_filter=None, only_denials=True): + """Convert the audit logs access into a an access vector set. + +@@ -510,16 +571,23 @@ class AuditParser: + audit logs parsed by this object. + """ + av_set = access.AccessVectorSet() ++ self.old_scontext = "" ++ self.old_tcontext = "" + for avc in self.avc_msgs: + if avc.denial != True and only_denials: + continue ++ base_type = self.__get_base_type(avc.tcontext.type, avc.scontext.type) ++ if avc.path == "": ++ avc.path = self.__restore_path(avc.name, avc.ino) + if avc_filter: + if avc_filter.filter(avc): + av_set.add(avc.scontext.type, avc.tcontext.type, avc.tclass, +- avc.accesses, avc, avc_type=avc.type, data=avc.data) ++ avc.accesses, avc.path, base_type, avc, ++ avc_type=avc.type, data=avc.data) + else: + av_set.add(avc.scontext.type, avc.tcontext.type, avc.tclass, +- avc.accesses, avc, avc_type=avc.type, data=avc.data) ++ avc.accesses, avc.path, base_type, avc, ++ avc_type=avc.type, data=avc.data) + return av_set + + class AVCTypeFilter: +diff --git a/sepolgen/src/sepolgen/policygen.py b/sepolgen/src/sepolgen/policygen.py +index 5f38577..39b0ce1 100644 +--- a/sepolgen/src/sepolgen/policygen.py ++++ b/sepolgen/src/sepolgen/policygen.py +@@ -81,8 +81,9 @@ class PolicyGenerator: + self.module = refpolicy.Module() + + self.dontaudit = False +- ++ self.mislabled = None + self.domains = None ++ + def set_gen_refpol(self, if_set=None, perm_maps=None): + """Set whether reference policy interfaces are generated. + +@@ -152,6 +153,18 @@ class PolicyGenerator: + """Return the generated module""" + return self.module + ++ def __restore_label(self, av): ++ import selinux ++ try: ++ context = selinux.matchpathcon(av.obj_path, 0) ++ split = context[1].split(":")[2] ++ if split != av.tgt_type: ++ self.mislabled = split ++ return ++ except OSError: ++ pass ++ self.mislabled = None ++ + def __add_allow_rules(self, avs): + for av in avs: + rule = refpolicy.AVRule(av) +@@ -160,6 +173,34 @@ class PolicyGenerator: + rule.comment = "" + if self.explain: + rule.comment = str(refpolicy.Comment(explain_access(av, verbosity=self.explain))) ++ # base_type[0] == 0 means there exists a base type but not the path ++ # base_type[0] == None means user isn't using base type ++ # base_type[1] contains the target context ++ # base_type[2] contains the source type ++ base_type = av.base_file_type() ++ if base_type[0] == 0 and av.type != audit2why.ALLOW: ++ rule.comment += "\n#!!!! WARNING: '%s' is a base type." % "".join(base_type[1]) ++ for perm in av.perms: ++ if perm == "write" or perm == "create": ++ permission = True ++ break ++ else: ++ permission = False ++ ++ # Catch perms 'write' and 'create' for base types ++ if (base_type[0] is not None and base_type[0] != 0 ++ and permission and av.type != audit2why.ALLOW): ++ if av.obj_class == dir: ++ comp = "(/.*?)" ++ else: ++ comp = "" ++ rule.comment += "\n#!!!! WARNING '%s' is not allowed to write or create to %s. Change the label to %s." % ("".join(base_type[2]), "".join(base_type[1]), "".join(base_type[0])) ++ if av.obj_path != "": ++ rule.comment += "\n#!!!! $ semange fcontext -a -t %s %s%s \n#!!!! $ restorecon -R -v %s" % ("".join(base_type[0]), "".join(av.obj_path), "".join(comp) ,"".join(av.obj_path)) ++ ++ self.__restore_label(av) ++ if self.mislabled is not None and av.type != audit2why.ALLOW: ++ rule.comment += "\n#!!!! The file '%s' is mislabeled on your system. \n#!!!! Fix with $ restorecon -R -v %s" % ("".join(av.obj_path), "".join(av.obj_path)) + if av.type == audit2why.ALLOW: + rule.comment += "\n#!!!! This avc is allowed in the current policy" + if av.type == audit2why.DONTAUDIT: diff --git a/policycoreutils.spec b/policycoreutils.spec index a411969..5937038 100644 --- a/policycoreutils.spec +++ b/policycoreutils.spec @@ -7,7 +7,7 @@ Summary: SELinux policy core utilities Name: policycoreutils Version: 2.3 -Release: 10%{?dist} +Release: 11%{?dist} License: GPLv2 Group: System Environment/Base # Based on git repository with tag 20101221 @@ -19,6 +19,7 @@ Source3: system-config-selinux.png Source4: sepolicy-icons.tgz Patch: policycoreutils-rhat.patch Patch1: 0001-Fix-setfiles-to-work-correctly-if-r-option-is-define.patch +Patch2: 0002-audit2allow-improvements.patch Obsoletes: policycoreutils < 2.0.61-2 Conflicts: filesystem < 3 Provides: /sbin/fixfiles @@ -383,6 +384,12 @@ The policycoreutils-restorecond package contains the restorecond service. %systemd_postun_with_restart restorecond.service %changelog +* Tue Sep 23 2014 Miroslav Grepl - 2.3-11 +- Improvements to audit2allow from rhallise@redhat.com + * Check for mislabeled files. + * Check for base file use and + * Suggest writable files as alternatives + * Sun Aug 17 2014 Fedora Release Engineering - 2.3-10 - Rebuilt for https://fedoraproject.org/wiki/Fedora_21_22_Mass_Rebuild