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.
417 lines
16 KiB
417 lines
16 KiB
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..352b187 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)) |
|
@@ -281,6 +284,11 @@ class SecurityContext(Leaf): |
|
|
|
Raises ValueError if the string is not parsable as a security context. |
|
""" |
|
+ # try to translate the context string to raw form |
|
+ raw = selinux.selinux_trans_to_raw_context(context) |
|
+ if raw[0] == 0: |
|
+ context = raw[1] |
|
+ |
|
fields = context.split(":") |
|
if len(fields) < 3: |
|
raise ValueError("context string [%s] not in a valid format" % context) |
|
@@ -522,6 +530,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 };
|
|
|