X-Git-Url: https://jxself.org/git/?a=blobdiff_plain;f=kconfig-hardened-check.py;h=02bef29915568acfa8537af09b5ed1a7b698c3ee;hb=61b5ca3c8f95212141284be8eb4036c8c1bda9e7;hp=4762219b1354e63b76c9c5559ee3fe249a46d680;hpb=0ace19012b626203d14332090cdcd40ed2237100;p=kconfig-hardened-check.git diff --git a/kconfig-hardened-check.py b/kconfig-hardened-check.py index 4762219..02bef29 100755 --- a/kconfig-hardened-check.py +++ b/kconfig-hardened-check.py @@ -55,6 +55,8 @@ debug_mode = False # set it to True to print the unknown options from the confi json_mode = False # if True, print results in JSON format supported_archs = [ 'X86_64', 'X86_32', 'ARM64', 'ARM' ] +config_checklist = [] +kernel_version = None class OptCheck: @@ -86,6 +88,26 @@ class OptCheck: return '{} = {}'.format(self.name, self.state) +class VerCheck: + def __init__(self, ver_expected): + self.ver_expected = ver_expected + self.result = None + + def check(self): + if kernel_version[0] > self.ver_expected[0]: + self.result = 'OK: version >= ' + str(self.ver_expected[0]) + '.' + str(self.ver_expected[1]) + return True, self.result + if kernel_version[0] < self.ver_expected[0]: + self.result = 'FAIL: version < ' + str(self.ver_expected[0]) + '.' + str(self.ver_expected[1]) + return False, self.result + if kernel_version[1] >= self.ver_expected[1]: + self.result = 'OK: version >= ' + str(self.ver_expected[0]) + '.' + str(self.ver_expected[1]) + return True, self.result + else: + self.result = 'FAIL: version < ' + str(self.ver_expected[0]) + '.' + str(self.ver_expected[1]) + return False, self.result + + class ComplexOptCheck: def __init__(self, *opts): self.opts = opts @@ -125,7 +147,7 @@ class OR(ComplexOptCheck): for i, opt in enumerate(self.opts): ret, msg = opt.check() if ret: - if i == 0: + if i == 0 or not hasattr(opt, 'name'): self.result = opt.result else: self.result = 'OK: CONFIG_{} "{}"'.format(opt.name, opt.expected) @@ -146,7 +168,10 @@ class AND(ComplexOptCheck): self.result = opt.result return ret, self.result elif not ret: - self.result = 'FAIL: CONFIG_{} is needed'.format(opt.name) + if hasattr(opt, 'name'): + self.result = 'FAIL: CONFIG_{} is needed'.format(opt.name) + else: + self.result = opt.result return False, self.result sys.exit('[!] ERROR: invalid AND check') @@ -207,6 +232,8 @@ def construct_checklist(checklist, arch): OptCheck('DEBUG_SET_MODULE_RONX', 'y', 'defconfig', 'self_protection'), \ modules_not_set)) # DEBUG_SET_MODULE_RONX was before v4.11 checklist.append(OptCheck('GCC_PLUGINS', 'y', 'defconfig', 'self_protection')) + checklist.append(OR(OptCheck('REFCOUNT_FULL', 'y', 'defconfig', 'self_protection'), \ + VerCheck((5, 5)))) # REFCOUNT_FULL is enabled by default since v5.5 if debug_mode or arch == 'X86_64' or arch == 'X86_32': checklist.append(OptCheck('MICROCODE', 'y', 'defconfig', 'self_protection')) # is needed for mitigating CPU bugs checklist.append(OptCheck('RETPOLINE', 'y', 'defconfig', 'self_protection')) @@ -237,7 +264,6 @@ def construct_checklist(checklist, arch): checklist.append(OptCheck('CPU_SW_DOMAIN_PAN', 'y', 'defconfig', 'self_protection')) checklist.append(OptCheck('STACKPROTECTOR_PER_TASK', 'y', 'defconfig', 'self_protection')) if debug_mode or arch == 'ARM64' or arch == 'ARM': - checklist.append(OptCheck('REFCOUNT_FULL', 'y', 'defconfig', 'self_protection')) checklist.append(OptCheck('HARDEN_BRANCH_PREDICTOR', 'y', 'defconfig', 'self_protection')) checklist.append(OptCheck('BUG_ON_DATA_CORRUPTION', 'y', 'kspp', 'self_protection')) @@ -268,7 +294,6 @@ def construct_checklist(checklist, arch): modules_not_set)) # refers to LOCK_DOWN_KERNEL if debug_mode or arch == 'X86_64' or arch == 'X86_32': checklist.append(OptCheck('DEFAULT_MMAP_MIN_ADDR', '65536', 'kspp', 'self_protection')) - checklist.append(OptCheck('REFCOUNT_FULL', 'y', 'kspp', 'self_protection')) if debug_mode or arch == 'X86_32': checklist.append(OptCheck('HIGHMEM64G', 'y', 'kspp', 'self_protection')) checklist.append(OptCheck('X86_PAE', 'y', 'kspp', 'self_protection')) @@ -386,7 +411,8 @@ def construct_checklist(checklist, arch): checklist.append(OptCheck('MAGIC_SYSRQ', 'is not set', 'clipos', 'cut_attack_surface')) checklist.append(OptCheck('KEXEC_FILE', 'is not set', 'clipos', 'cut_attack_surface')) # refers to LOCK_DOWN_KERNEL (permissive) checklist.append(OptCheck('USER_NS', 'is not set', 'clipos', 'cut_attack_surface')) # user.max_user_namespaces=0 - checklist.append(OptCheck('LDISC_AUTOLOAD', 'is not set', 'clipos', 'cut_attack_surface')) + checklist.append(AND(OptCheck('LDISC_AUTOLOAD', 'is not set', 'clipos', 'cut_attack_surface'), \ + VerCheck((5, 1)))) # LDISC_AUTOLOAD can be disabled since v5.1 checklist.append(OptCheck('MMIOTRACE', 'is not set', 'my', 'cut_attack_surface')) # refers to LOCK_DOWN_KERNEL (permissive) checklist.append(OptCheck('LIVEPATCH', 'is not set', 'my', 'cut_attack_surface')) @@ -442,9 +468,12 @@ def perform_checks(checklist, parsed_options): if hasattr(opt, 'opts'): # prepare ComplexOptCheck for o in opt.opts: - o.state = parsed_options.get(o.name, None) + if hasattr(o, 'name'): + o.state = parsed_options.get(o.name, None) else: - # prepare OptCheck + # prepare simple OptCheck + if not hasattr(opt, 'name'): + sys.exit('[!] ERROR: bad OptCheck {}'.format(vars(opt))) opt.state = parsed_options.get(opt.name, None) opt.check() @@ -487,8 +516,6 @@ def check_config_file(checklist, fname): if __name__ == '__main__': - config_checklist = [] - parser = ArgumentParser(description='Checks the hardening options in the Linux kernel config') parser.add_argument('-p', '--print', choices=supported_archs, help='print hardening preferences for selected architecture')