diff options
Diffstat (limited to 'meta-security-isafw/lib/isafw/isaplugins/ISA_cfa_plugin.py')
-rw-r--r-- | meta-security-isafw/lib/isafw/isaplugins/ISA_cfa_plugin.py | 392 |
1 files changed, 0 insertions, 392 deletions
diff --git a/meta-security-isafw/lib/isafw/isaplugins/ISA_cfa_plugin.py b/meta-security-isafw/lib/isafw/isaplugins/ISA_cfa_plugin.py deleted file mode 100644 index daecba1..0000000 --- a/meta-security-isafw/lib/isafw/isaplugins/ISA_cfa_plugin.py +++ /dev/null @@ -1,392 +0,0 @@ -# -# ISA_cfa_plugin.py - Compile flag analyzer plugin, part of ISA FW -# Main functionality is based on build_comp script from Clear linux project -# -# Copyright (c) 2015 - 2016, Intel Corporation -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are met: -# -# * Redistributions of source code must retain the above copyright notice, -# this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# * Neither the name of Intel Corporation nor the names of its contributors -# may be used to endorse or promote products derived from this software -# without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE -# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, -# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -import subprocess -import os -import sys -import re -import copy -try: - from lxml import etree -except ImportError: - try: - import xml.etree.cElementTree as etree - except ImportError: - import xml.etree.ElementTree as etree - - -CFChecker = None - - -class ISA_CFChecker(): - initialized = False - no_relro = [] - partial_relro = [] - no_canary = [] - no_pie = [] - execstack = [] - execstack_not_defined = [] - nodrop_groups = [] - no_mpx = [] - - def __init__(self, ISA_config): - self.logfile = ISA_config.logdir + "/isafw_cfalog" - self.full_report_name = ISA_config.reportdir + "/cfa_full_report_" + \ - ISA_config.machine + "_" + ISA_config.timestamp - self.problems_report_name = ISA_config.reportdir + \ - "/cfa_problems_report_" + ISA_config.machine + "_" + ISA_config.timestamp - self.full_reports = ISA_config.full_reports - self.ISA_filesystem = "" - # check that checksec and other tools are installed - tools_errors = _check_tools() - if tools_errors: - with open(self.logfile, 'w') as flog: - flog.write(tools_errors) - return - self.initialized = True - with open(self.logfile, 'w') as flog: - flog.write("\nPlugin ISA_CFChecker initialized!\n") - return - - def process_filesystem(self, ISA_filesystem): - self.ISA_filesystem = ISA_filesystem - fs_path = self.ISA_filesystem.path_to_fs - img_name = self.ISA_filesystem.img_name - if (self.initialized): - if (img_name and fs_path): - with open(self.logfile, 'a') as flog: - flog.write("\n\nFilesystem path is: " + fs_path) - if self.full_reports: - with open(self.full_report_name + "_" + img_name, 'w') as ffull_report: - ffull_report.write( - "Security-relevant flags for executables for image: " + img_name + '\n') - ffull_report.write("With rootfs location at " + fs_path + "\n\n") - files = self.find_files(fs_path) - import multiprocessing - pool = multiprocessing.Pool() - results = pool.imap(process_file_wrapper, files) - pool.close() - pool.join() - self.process_results(results) - else: - with open(self.logfile, 'a') as flog: - flog.write( - "Mandatory arguments such as image name and path to the filesystem are not provided!\n") - flog.write("Not performing the call.\n") - else: - with open(self.logfile, 'a') as flog: - flog.write("Plugin hasn't initialized! Not performing the call.\n") - - def process_results(self, results): - fs_path = self.ISA_filesystem.path_to_fs - for result in results: - if not result: - with open(self.logfile, 'a') as flog: - flog.write("\nError in returned result") - continue - with open(self.logfile, 'a') as flog: - flog.write("\n\nFor file: " + str(result[0]) + "\nlog is: " + str(result[5])) - if result[1]: - with open(self.logfile, 'a') as flog: - flog.write("\n\nsec_field: " + str(result[1])) - if "No RELRO" in result[1]: - self.no_relro.append(result[0].replace(fs_path, "")) - elif "Partial RELRO" in result[1]: - self.partial_relro.append(result[0].replace(fs_path, "")) - if "No canary found" in result[1]: - self.no_canary.append(result[0].replace(fs_path, "")) - if "No PIE" in result[1]: - self.no_pie.append(result[0].replace(fs_path, "")) - if result[2]: - if result[2] == "execstack": - self.execstack.append(result[0].replace(fs_path, "")) - elif result[2] == "not_defined": - self.execstack_not_defined.append(result[0].replace(fs_path, "")) - if result[3] and (result[3] == True): - self.nodrop_groups.append(result[0].replace(fs_path, "")) - if result[4] and (result[4] == True): - self.no_mpx.append(result[0].replace(fs_path, "")) - self.write_full_report(result) - self.write_report() - self.write_report_xml() - - def write_full_report(self, result): - if not self.full_reports: - return - fs_path = self.ISA_filesystem.path_to_fs - img_name = self.ISA_filesystem.img_name - with open(self.full_report_name + "_" + img_name, 'a') as ffull_report: - ffull_report.write('\nFile: ' + result[0].replace(fs_path, "")) - ffull_report.write('\nsecurity flags: ' + str(result[1])) - ffull_report.write('\nexecstack: ' + str(result[2])) - ffull_report.write('\nnodrop_groups: ' + str(result[3])) - ffull_report.write('\nno mpx: ' + str(result[4])) - ffull_report.write('\n') - - def write_report(self): - fs_path = self.ISA_filesystem.path_to_fs - img_name = self.ISA_filesystem.img_name - with open(self.problems_report_name + "_" + img_name, 'w') as fproblems_report: - fproblems_report.write("Report for image: " + img_name + '\n') - fproblems_report.write("With rootfs location at " + fs_path + "\n\n") - fproblems_report.write("Relocation Read-Only\n") - fproblems_report.write("More information about RELRO and how to enable it:") - fproblems_report.write( - " http://tk-blog.blogspot.de/2009/02/relro-not-so-well-known-memory.html\n") - fproblems_report.write("Files with no RELRO:\n") - for item in self.no_relro: - fproblems_report.write(item + '\n') - fproblems_report.write("Files with partial RELRO:\n") - for item in self.partial_relro: - fproblems_report.write(item + '\n') - fproblems_report.write("\n\nStack protection\n") - fproblems_report.write( - "More information about canary stack protection and how to enable it:") - fproblems_report.write("https://lwn.net/Articles/584225/ \n") - fproblems_report.write("Files with no canary:\n") - for item in self.no_canary: - fproblems_report.write(item + '\n') - fproblems_report.write("\n\nPosition Independent Executable\n") - fproblems_report.write("More information about PIE protection and how to enable it:") - fproblems_report.write( - "https://securityblog.redhat.com/2012/11/28/position-independent-executables-pie/\n") - fproblems_report.write("Files with no PIE:\n") - for item in self.no_pie: - fproblems_report.write(item + '\n') - fproblems_report.write("\n\nNon-executable stack\n") - fproblems_report.write("Files with executable stack enabled:\n") - for item in self.execstack: - fproblems_report.write(item + '\n') - fproblems_report.write("\n\nFiles with no ability to fetch executable stack status:\n") - for item in self.execstack_not_defined: - fproblems_report.write(item + '\n') - fproblems_report.write("\n\nGrop initialization:\n") - fproblems_report.write( - "If using setuid/setgid calls in code, one must call initgroups or setgroups\n") - fproblems_report.write( - "Files that don't initialize groups while using setuid/setgid:\n") - for item in self.nodrop_groups: - fproblems_report.write(item + '\n') - fproblems_report.write("\n\nMemory Protection Extensions\n") - fproblems_report.write("More information about MPX protection and how to enable it:") - fproblems_report.write( - "https://software.intel.com/sites/default/files/managed/9d/f6/Intel_MPX_EnablingGuide.pdf\n") - fproblems_report.write("Files that don't have MPX protection enabled:\n") - for item in self.no_mpx: - fproblems_report.write(item + '\n') - - def write_report_xml(self): - numTests = len(self.no_relro) + len(self.partial_relro) + len(self.no_canary) + len(self.no_pie) + \ - len(self.execstack) + len(self.execstack_not_defined) + \ - len(self.nodrop_groups) + len(self.no_mpx) - root = etree.Element('testsuite', name='ISA_CFChecker', tests=str(numTests)) - if self.no_relro: - for item in self.no_relro: - tcase1 = etree.SubElement( - root, 'testcase', classname='files_with_no_RELRO', name=item) - etree.SubElement(tcase1, 'failure', message=item, type='violation') - if self.partial_relro: - for item in self.partial_relro: - tcase1 = etree.SubElement( - root, 'testcase', classname='files_with_partial_RELRO', name=item) - etree.SubElement(tcase1, 'failure', message=item, type='violation') - if self.no_canary: - for item in self.no_canary: - tcase2 = etree.SubElement( - root, 'testcase', classname='files_with_no_canary', name=item) - etree.SubElement(tcase2, 'failure', message=item, type='violation') - if self.no_pie: - for item in self.no_pie: - tcase3 = etree.SubElement( - root, 'testcase', classname='files_with_no_PIE', name=item) - etree.SubElement(tcase3, 'failure', message=item, type='violation') - if self.execstack: - for item in self.execstack: - tcase5 = etree.SubElement( - root, 'testcase', classname='files_with_execstack', name=item) - etree.SubElement(tcase5, 'failure', message=item, type='violation') - if self.execstack_not_defined: - for item in self.execstack_not_defined: - tcase6 = etree.SubElement( - root, 'testcase', classname='files_with_execstack_not_defined', name=item) - etree.SubElement(tcase6, 'failure', message=item, type='violation') - if self.nodrop_groups: - for item in self.nodrop_groups: - tcase7 = etree.SubElement( - root, 'testcase', classname='files_with_nodrop_groups', name=item) - etree.SubElement(tcase7, 'failure', message=item, type='violation') - if self.no_mpx: - for item in self.no_mpx: - tcase8 = etree.SubElement( - root, 'testcase', classname='files_with_no_mpx', name=item) - etree.SubElement(tcase8, 'failure', message=item, type='violation') - tree = etree.ElementTree(root) - output = self.problems_report_name + "_" + self.ISA_filesystem.img_name + '.xml' - try: - tree.write(output, encoding='UTF-8', pretty_print=True, xml_declaration=True) - except TypeError: - tree.write(output, encoding='UTF-8', xml_declaration=True) - - def find_files(self, init_path): - list_of_files = [] - for (dirpath, dirnames, filenames) in os.walk(init_path): - for f in filenames: - list_of_files.append(str(dirpath + "/" + f)[:]) - return list_of_files - - -def _check_tools(): - - def _is_in_path(executable): - "Check for presence of executable in PATH" - for path in os.environ["PATH"].split(os.pathsep): - path = path.strip('"') - if (os.path.isfile(os.path.join(path, executable)) and - os.access(os.path.join(path, executable), os.X_OK)): - return True - return False - - tools = { - "checksec.sh": "Please install checksec from http://www.trapkit.de/tools/checksec.html\n", - "execstack": "Please install execstack from prelink package\n", - "readelf": "Please install binutils\n", - "objdump": "Please install binutils\n", - } - output = "" - for tool in tools: - if not _is_in_path(tool): - output += tools[tool] - return output - - -def get_info(tool, args, file_name): - env = copy.deepcopy(os.environ) - env['PSEUDO_UNLOAD'] = "1" - cmd = [tool, args, file_name] - with open(os.devnull, 'wb') as DEVNULL: - try: - result = subprocess.check_output(cmd, stderr=DEVNULL, env=env).decode('utf-8') - except: - return "" - else: - return result - -def get_security_flags(file_name): - env = copy.deepcopy(os.environ) - env['PSEUDO_UNLOAD'] = "1" - cmd = ['checksec.sh', '--file', file_name] - try: - result = subprocess.check_output(cmd, env=env).decode('utf-8').splitlines()[1] - except: - return "Not able to fetch flags" - else: - # remove ansi escape color sequences - result = re.sub(r'\x1b[^m]*m', '', result) - return re.split(r' {2,}', result)[:-1] - - -def process_file(file): - log = "File from map " + file - fun_results = [file, [], "", False, False, log] - if not os.path.isfile(file): - return fun_results - env = copy.deepcopy(os.environ) - env['PSEUDO_UNLOAD'] = "1" - # getting file type - cmd = ['file', '--mime-type', file] - try: - result = subprocess.check_output(cmd, env=env).decode('utf-8') - except: - fun_results[-1] += "\nNot able to decode mime type" - return fun_results - file_type = result.split()[-1] - # looking for links - if "symlink" in file_type: - file = os.path.realpath(file) - cmd = ['file', '--mime-type', file] - try: - result = subprocess.check_output(cmd, env=env).decode('utf-8') - except: - fun_results[-1] += "\nNot able to decode mime type" - return fun_results - file_type = result.split()[-1] - # checking security flags if applies - if "application" not in file_type: - return fun_results - fun_results[-1] += "\nFile type: " + file_type - if (("octet-stream" in file_type) or ("dosexec" in file_type) or - ("archive" in file_type) or ("xml" in file_type) or - ("gzip" in file_type) or ("postscript" in file_type) or - ("pdf" in file_type)): - return fun_results - fun_results[1] = get_security_flags(file) - tmp = get_info("execstack", '-q', file) - if tmp.startswith("X "): - fun_results[2] = "execstack" - elif tmp.startswith("? "): - fun_results[2] = "not_defined" - tmp = get_info("readelf", '-s', file) - if ("setgid@GLIBC" in tmp) or ("setegid@GLIBC" in tmp) or ("setresgid@GLIBC" in tmp): - if ("setuid@GLIBC" in tmp) or ("seteuid@GLIBC" in tmp) or ("setresuid@GLIBC" in tmp): - if ("setgroups@GLIBC" not in tmp) and ("initgroups@GLIBC" not in tmp): - fun_results[3] = True - tmp = get_info("objdump", '-d', file) - if ("bndcu" not in tmp) and ("bndcl" not in tmp) and ("bndmov" not in tmp): - fun_results[4] = True - return fun_results - -def process_file_wrapper(file): - # Ensures that exceptions get logged with the original backtrace. - # Without this, they appear with a backtrace rooted in - # the code which transfers back the result to process_results(). - try: - return process_file(file) - except: - from isafw import isafw - import traceback - isafw.error('Internal error:\n%s' % traceback.format_exc()) - raise - -# ======== supported callbacks from ISA ============ # - - -def init(ISA_config): - global CFChecker - CFChecker = ISA_CFChecker(ISA_config) - - -def getPluginName(): - return "ISA_CFChecker" - - -def process_filesystem(ISA_filesystem): - global CFChecker - return CFChecker.process_filesystem(ISA_filesystem) - -# =================================================== # |