You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
406 lines
15 KiB
406 lines
15 KiB
7 years ago
|
diff --git sepolgen-1.2.3/ChangeLog sepolgen-1.2.3/ChangeLog
|
||
|
index 7cc0a18..bda7a2e 100644
|
||
|
--- sepolgen-1.2.3/ChangeLog
|
||
|
+++ sepolgen-1.2.3/ChangeLog
|
||
|
@@ -1,3 +1,6 @@
|
||
|
+ * Remove additional files when cleaning, from Nicolas Iooss.
|
||
|
+ * Add support for TYPEBOUNDS statement in INTERFACE policy files, from Miroslav Grepl.
|
||
|
+
|
||
|
1.2.3 2016-02-23
|
||
|
* Support latest refpolicy interfaces, from Nicolas Iooss.
|
||
|
* Make sepolgen-ifgen output deterministic with Python>=3.3, from Nicolas Iooss.
|
||
|
diff --git sepolgen-1.2.3/src/sepolgen/Makefile sepolgen-1.2.3/src/sepolgen/Makefile
|
||
|
index 9ac7651..d3aa771 100644
|
||
|
--- sepolgen-1.2.3/src/sepolgen/Makefile
|
||
|
+++ sepolgen-1.2.3/src/sepolgen/Makefile
|
||
|
@@ -11,5 +11,4 @@ install: all
|
||
|
clean:
|
||
|
rm -f parser.out parsetab.py
|
||
|
rm -f *~ *.pyc
|
||
|
-
|
||
|
-
|
||
|
+ rm -rf __pycache__
|
||
|
diff --git sepolgen-1.2.3/src/sepolgen/access.py sepolgen-1.2.3/src/sepolgen/access.py
|
||
|
index a5d8698..7606561 100644
|
||
|
--- sepolgen-1.2.3/src/sepolgen/access.py
|
||
|
+++ sepolgen-1.2.3/src/sepolgen/access.py
|
||
|
@@ -90,6 +90,8 @@ class AccessVector(util.Comparison):
|
||
|
self.audit_msgs = []
|
||
|
self.type = audit2why.TERULE
|
||
|
self.data = []
|
||
|
+ self.obj_path = None
|
||
|
+ self.base_type = None
|
||
|
# when implementing __eq__ also __hash__ is needed on py2
|
||
|
# if object is muttable __hash__ should be None
|
||
|
self.__hash__ = None
|
||
|
@@ -138,6 +140,29 @@ class AccessVector(util.Comparison):
|
||
|
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)
|
||
|
+ if self.tgt_type != other.tgt_type:
|
||
|
+ return cmp(self.tgt_type, other.tgt_type)
|
||
|
+ if self.obj_class != self.obj_class:
|
||
|
+ return cmp(self.obj_class, other.obj_class)
|
||
|
+ if len(self.perms) != len(other.perms):
|
||
|
+ return cmp(len(self.perms), len(other.perms))
|
||
|
+ x = list(self.perms)
|
||
|
+ x.sort()
|
||
|
+ y = list(other.perms)
|
||
|
+ y.sort()
|
||
|
+ for pa, pb in zip(x, y):
|
||
|
+ if pa != pb:
|
||
|
+ return cmp(pa, pb)
|
||
|
+ return 0
|
||
|
+
|
||
|
def _compare(self, other, method):
|
||
|
try:
|
||
|
x = list(self.perms)
|
||
|
@@ -257,7 +282,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, { })
|
||
|
@@ -270,7 +296,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 sepolgen-1.2.3/src/sepolgen/audit.py sepolgen-1.2.3/src/sepolgen/audit.py
|
||
|
index 724d3ea..ef1f6cc 100644
|
||
|
--- sepolgen-1.2.3/src/sepolgen/audit.py
|
||
|
+++ sepolgen-1.2.3/src/sepolgen/audit.py
|
||
|
@@ -176,6 +176,7 @@ class AVCMessage(AuditMessage):
|
||
|
self.exe = ""
|
||
|
self.path = ""
|
||
|
self.name = ""
|
||
|
+ self.ino = ""
|
||
|
self.accesses = []
|
||
|
self.denial = True
|
||
|
self.type = audit2why.TERULE
|
||
|
@@ -237,6 +238,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)
|
||
|
@@ -361,7 +366,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
|
||
|
@@ -376,7 +383,9 @@ class AuditParser:
|
||
|
# AuditMessage (or subclass) - object representing a parsed
|
||
|
# and valid audit message.
|
||
|
def __parse_line(self, line):
|
||
|
- rec = line.split()
|
||
|
+ # strip("\x1c\x1d\x1e\x85") is only needed for python2
|
||
|
+ # since str.split() in python3 already does this
|
||
|
+ rec = [x.strip("\x1c\x1d\x1e\x85") for x in line.split()]
|
||
|
for i in rec:
|
||
|
found = False
|
||
|
if i == "avc:" or i == "message=avc:" or i == "msg='avc:":
|
||
|
@@ -499,6 +508,61 @@ 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,
|
||
|
+ universal_newlines=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.
|
||
|
|
||
|
@@ -517,16 +581,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 sepolgen-1.2.3/src/sepolgen/policygen.py sepolgen-1.2.3/src/sepolgen/policygen.py
|
||
|
index 34c8401..f374132 100644
|
||
|
--- sepolgen-1.2.3/src/sepolgen/policygen.py
|
||
|
+++ sepolgen-1.2.3/src/sepolgen/policygen.py
|
||
|
@@ -82,8 +82,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.
|
||
|
|
||
|
@@ -153,6 +154,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)
|
||
|
@@ -161,6 +174,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#!!!! $ semanage 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 sepolgen-1.2.3/src/sepolgen/refparser.py sepolgen-1.2.3/src/sepolgen/refparser.py
|
||
|
index 9b1d0c8..2cef8e8 100644
|
||
|
--- sepolgen-1.2.3/src/sepolgen/refparser.py
|
||
|
+++ sepolgen-1.2.3/src/sepolgen/refparser.py
|
||
|
@@ -113,6 +113,7 @@ tokens = (
|
||
|
'AUDITALLOW',
|
||
|
'NEVERALLOW',
|
||
|
'PERMISSIVE',
|
||
|
+ 'TYPEBOUNDS',
|
||
|
'TYPE_TRANSITION',
|
||
|
'TYPE_CHANGE',
|
||
|
'TYPE_MEMBER',
|
||
|
@@ -178,6 +179,7 @@ reserved = {
|
||
|
'auditallow' : 'AUDITALLOW',
|
||
|
'neverallow' : 'NEVERALLOW',
|
||
|
'permissive' : 'PERMISSIVE',
|
||
|
+ 'typebounds' : 'TYPEBOUNDS',
|
||
|
'type_transition' : 'TYPE_TRANSITION',
|
||
|
'type_change' : 'TYPE_CHANGE',
|
||
|
'type_member' : 'TYPE_MEMBER',
|
||
|
@@ -502,6 +504,7 @@ def p_policy_stmt(p):
|
||
|
'''policy_stmt : gen_require
|
||
|
| avrule_def
|
||
|
| typerule_def
|
||
|
+ | typebound_def
|
||
|
| typeattribute_def
|
||
|
| roleattribute_def
|
||
|
| interface_call
|
||
|
@@ -823,6 +826,13 @@ def p_typerule_def(p):
|
||
|
t.file_name = p[7]
|
||
|
p[0] = t
|
||
|
|
||
|
+def p_typebound_def(p):
|
||
|
+ '''typebound_def : TYPEBOUNDS IDENTIFIER comma_list SEMI'''
|
||
|
+ t = refpolicy.TypeBound()
|
||
|
+ t.type = p[2]
|
||
|
+ t.tgt_types.update(p[3])
|
||
|
+ p[0] = t
|
||
|
+
|
||
|
def p_bool(p):
|
||
|
'''bool : BOOL IDENTIFIER TRUE SEMI
|
||
|
| BOOL IDENTIFIER FALSE SEMI'''
|
||
|
diff --git sepolgen-1.2.3/src/sepolgen/refpolicy.py sepolgen-1.2.3/src/sepolgen/refpolicy.py
|
||
|
index 31b40d8..2ee029c 100644
|
||
|
--- sepolgen-1.2.3/src/sepolgen/refpolicy.py
|
||
|
+++ sepolgen-1.2.3/src/sepolgen/refpolicy.py
|
||
|
@@ -112,6 +112,9 @@ class Node(PolicyBase):
|
||
|
def typerules(self):
|
||
|
return filter(lambda x: isinstance(x, TypeRule), walktree(self))
|
||
|
|
||
|
+ def typebounds(self):
|
||
|
+ return filter(lambda x: isinstance(x, TypeBound), walktree(self))
|
||
|
+
|
||
|
def typeattributes(self):
|
||
|
"""Iterate over all of the TypeAttribute children of this Interface."""
|
||
|
return filter(lambda x: isinstance(x, TypeAttribute), walktree(self))
|
||
|
@@ -522,6 +525,19 @@ class TypeRule(Leaf):
|
||
|
self.tgt_types.to_space_str(),
|
||
|
self.obj_classes.to_space_str(),
|
||
|
self.dest_type)
|
||
|
+class TypeBound(Leaf):
|
||
|
+ """SElinux typebound statement.
|
||
|
+
|
||
|
+ This class represents a typebound statement.
|
||
|
+ """
|
||
|
+ def __init__(self, parent=None):
|
||
|
+ Leaf.__init__(self, parent)
|
||
|
+ self.type = ""
|
||
|
+ self.tgt_types = IdSet()
|
||
|
+
|
||
|
+ def to_string(self):
|
||
|
+ return "typebounds %s %s;" % (self.type, self.tgt_types.to_comma_str())
|
||
|
+
|
||
|
|
||
|
class RoleAllow(Leaf):
|
||
|
def __init__(self, parent=None):
|
||
|
diff --git sepolgen-1.2.3/tests/.gitignore sepolgen-1.2.3/tests/.gitignore
|
||
|
new file mode 100644
|
||
|
index 0000000..c120af8
|
||
|
--- /dev/null
|
||
|
+++ sepolgen-1.2.3/tests/.gitignore
|
||
|
@@ -0,0 +1,4 @@
|
||
|
+module_compile_test.fc
|
||
|
+module_compile_test.if
|
||
|
+output
|
||
|
+tmp/
|
||
|
diff --git sepolgen-1.2.3/tests/Makefile sepolgen-1.2.3/tests/Makefile
|
||
|
index 924a9be..e17eef2 100644
|
||
|
--- sepolgen-1.2.3/tests/Makefile
|
||
|
+++ sepolgen-1.2.3/tests/Makefile
|
||
|
@@ -4,8 +4,11 @@ clean:
|
||
|
rm -f *~ *.pyc
|
||
|
rm -f parser.out parsetab.py
|
||
|
rm -f out.txt
|
||
|
+ rm -f module_compile_test.fc
|
||
|
+ rm -f module_compile_test.if
|
||
|
rm -f module_compile_test.pp
|
||
|
rm -f output
|
||
|
+ rm -rf __pycache__ tmp
|
||
|
|
||
|
test:
|
||
|
$(PYTHON) run-tests.py
|
||
|
diff --git sepolgen-1.2.3/tests/module_compile_test.te sepolgen-1.2.3/tests/module_compile_test.te
|
||
|
index 446c8dc..b365448 100644
|
||
|
--- sepolgen-1.2.3/tests/module_compile_test.te
|
||
|
+++ sepolgen-1.2.3/tests/module_compile_test.te
|
||
|
@@ -1,8 +1,8 @@
|
||
|
-module foo 1.0;
|
||
|
+module module_compile_test 1.0;
|
||
|
|
||
|
require {
|
||
|
type foo, bar;
|
||
|
class file { read write };
|
||
|
}
|
||
|
|
||
|
-allow foo bar : file { read write };
|
||
|
\ No newline at end of file
|
||
|
+allow foo bar : file { read write };
|