X-Git-Url: https://jxself.org/git/?a=blobdiff_plain;f=kconfig_hardened_check%2F__init__.py;h=f03efcb12eeb7072f3ea310dbecdb2d254991623;hb=cbd83d7364be51dcebc1f8e3111195cdc5b2d483;hp=fe69e1e8d0bbaa69b00741bd63de90f80ee0e8b5;hpb=4c7a125eb7a2fe97c5b3e81dad4768b45d915d0a;p=kconfig-hardened-check.git diff --git a/kconfig_hardened_check/__init__.py b/kconfig_hardened_check/__init__.py index fe69e1e..f03efcb 100644 --- a/kconfig_hardened_check/__init__.py +++ b/kconfig_hardened_check/__init__.py @@ -146,6 +146,10 @@ class PresenceCheck: class ComplexOptCheck: def __init__(self, *opts): self.opts = opts + if not self.opts: + sys.exit('[!] ERROR: empty {} check'.format(self.__class__.__name__)) + if not isinstance(opts[0], OptCheck): + sys.exit('[!] ERROR: invalid {} check: {}'.format(self.__class__.__name__, opts)) self.result = None @property @@ -192,9 +196,13 @@ class OR(ComplexOptCheck): for i, opt in enumerate(self.opts): ret = opt.check() if ret: - if i == 0 or not hasattr(opt, 'expected'): + if opt.result != 'OK' or i == 0: + # Preserve additional explanation of this OK result. + # Simple OK is enough only for the main option that + # this OR-check is about. self.result = opt.result else: + # Simple OK is not enough for additional checks. self.result = 'OK: CONFIG_{} "{}"'.format(opt.name, opt.expected) return True self.result = self.opts[0].result @@ -215,9 +223,13 @@ class AND(ComplexOptCheck): self.result = opt.result return ret if not ret: - if hasattr(opt, 'expected'): + # This FAIL is caused by additional checks, + # and not by the main option that this AND-check is about. + if opt.result.startswith('FAIL: \"'): + # Describe the reason of the FAIL. self.result = 'FAIL: CONFIG_{} not "{}"'.format(opt.name, opt.expected) else: + # This FAIL message is self-explaining. self.result = opt.result return False @@ -379,7 +391,7 @@ def construct_checklist(l, arch): iommu_support_is_set)] # 'self_protection', 'my' - l += [OptCheck('self_protection', 'my', 'SLUB_DEBUG_ON', 'y')] + l += [OptCheck('self_protection', 'my', 'SLUB_DEBUG_ON', 'y')] # TODO: is it better to set that via kernel cmd? l += [OptCheck('self_protection', 'my', 'RESET_ATTACK_MITIGATION', 'y')] # needs userspace support (systemd) if arch == 'X86_64': l += [AND(OptCheck('self_protection', 'my', 'AMD_IOMMU_V2', 'y'), @@ -476,13 +488,14 @@ def construct_checklist(l, arch): l += [OptCheck('cut_attack_surface', 'clipos', 'X86_CPUID', 'is not set')] l += [OptCheck('cut_attack_surface', 'clipos', 'IO_URING', 'is not set')] l += [OptCheck('cut_attack_surface', 'clipos', 'X86_IOPL_IOPERM', 'is not set')] # refers to LOCKDOWN + l += [OptCheck('cut_attack_surface', 'clipos', 'ACPI_TABLE_UPGRADE', 'is not set')] # refers to LOCKDOWN + l += [OptCheck('cut_attack_surface', 'clipos', 'EFI_CUSTOM_SSDT_OVERLAYS', 'is not set')] l += [AND(OptCheck('cut_attack_surface', 'clipos', 'LDISC_AUTOLOAD', 'is not set'), PresenceCheck('LDISC_AUTOLOAD'))] if arch in ('X86_64', 'X86_32'): l += [OptCheck('cut_attack_surface', 'clipos', 'X86_INTEL_TSX_MODE_OFF', 'y')] # tsx=off # 'cut_attack_surface', 'lockdown' - l += [OptCheck('cut_attack_surface', 'lockdown', 'ACPI_TABLE_UPGRADE', 'is not set')] # refers to LOCKDOWN l += [OptCheck('cut_attack_surface', 'lockdown', 'EFI_TEST', 'is not set')] # refers to LOCKDOWN l += [OptCheck('cut_attack_surface', 'lockdown', 'BPF_SYSCALL', 'is not set')] # refers to LOCKDOWN l += [OptCheck('cut_attack_surface', 'lockdown', 'MMIOTRACE_TEST', 'is not set')] # refers to LOCKDOWN @@ -576,23 +589,30 @@ def print_checklist(mode, checklist, with_results): print('[+] Config check is finished: \'OK\' - {}{} / \'FAIL\' - {}{}'.format(ok_count, ok_suppressed, fail_count, fail_suppressed)) -def perform_checks(checklist, parsed_options, kernel_version): - for opt in checklist: +def perform_check(opt, parsed_options, kernel_version): if hasattr(opt, 'opts'): # prepare ComplexOptCheck for o in opt.opts: + if hasattr(o, 'opts'): + # Recursion for nested ComplexOptChecks + perform_check(o, parsed_options, kernel_version) if hasattr(o, 'state'): o.state = parsed_options.get(o.name, None) if hasattr(o, 'ver'): o.ver = kernel_version else: - # prepare simple check + # prepare simple check, opt.state is mandatory if not hasattr(opt, 'state'): sys.exit('[!] ERROR: bad simple check {}'.format(vars(opt))) opt.state = parsed_options.get(opt.name, None) opt.check() +def perform_checks(checklist, parsed_options, kernel_version): + for opt in checklist: + perform_check(opt, parsed_options, kernel_version) + + def parse_config_file(parsed_options, fname): with open(fname, 'r') as f: opt_is_on = re.compile("CONFIG_[a-zA-Z0-9_]*=[a-zA-Z0-9_\"]*")