X-Git-Url: https://jxself.org/git/?a=blobdiff_plain;f=kconfig_hardened_check%2F__init__.py;h=586658e6432963e2727dee337bdac140e8b63c6b;hb=f5ba73c39a6f7480a593c92d14e09c8c4acf8746;hp=7db4f5db98815a14e475a61fd7a40831ad897542;hpb=b68df4d3611bc9f2a4c501191616c2b92f38c80c;p=kconfig-hardened-check.git diff --git a/kconfig_hardened_check/__init__.py b/kconfig_hardened_check/__init__.py index 7db4f5d..586658e 100644 --- a/kconfig_hardened_check/__init__.py +++ b/kconfig_hardened_check/__init__.py @@ -29,9 +29,9 @@ # pti=on # spec_store_bypass_disable=on # l1tf=full,force +# l1d_flush=on (a part of the l1tf option) # mds=full,nosmt # tsx=off -# l1d_flush=on # ARM64: # kpti=on # ssbd=force-on @@ -122,6 +122,12 @@ class KconfigCheck(OptCheck): def type(self): return 'kconfig' + def json_dump(self, with_results): + dump = [self.name, self.type, self.expected, self.decision, self.reason] + if with_results: + dump.append(self.result) + return dump + class VersionCheck: def __init__(self, ver_expected): @@ -221,6 +227,12 @@ class ComplexOptCheck: if with_results: print('| {}'.format(self.result), end='') + def json_dump(self, with_results): + dump = self.opts[0].json_dump(False) + if with_results: + dump.append(self.result) + return dump + class OR(ComplexOptCheck): # self.opts[0] is the option that this OR-check is about. @@ -313,6 +325,7 @@ def add_kconfig_checks(l, arch): modules_not_set = KconfigCheck('cut_attack_surface', 'kspp', 'MODULES', 'is not set') devmem_not_set = KconfigCheck('cut_attack_surface', 'kspp', 'DEVMEM', 'is not set') # refers to LOCKDOWN + efi_not_set = KconfigCheck('cut_attack_surface', 'my', 'EFI', 'is not set') # 'self_protection', 'defconfig' l += [KconfigCheck('self_protection', 'defconfig', 'BUG', 'y')] @@ -327,11 +340,11 @@ def add_kconfig_checks(l, arch): modules_not_set)] # DEBUG_SET_MODULE_RONX was before v4.11 l += [OR(KconfigCheck('self_protection', 'defconfig', 'REFCOUNT_FULL', 'y'), VersionCheck((5, 5)))] # REFCOUNT_FULL is enabled by default since v5.5 + l += [KconfigCheck('self_protection', 'defconfig', 'THREAD_INFO_IN_TASK', 'y')] iommu_support_is_set = KconfigCheck('self_protection', 'defconfig', 'IOMMU_SUPPORT', 'y') l += [iommu_support_is_set] # is needed for mitigating DMA attacks if arch in ('X86_64', 'ARM64', 'X86_32'): l += [KconfigCheck('self_protection', 'defconfig', 'RANDOMIZE_BASE', 'y')] - l += [KconfigCheck('self_protection', 'defconfig', 'THREAD_INFO_IN_TASK', 'y')] if arch in ('X86_64', 'ARM64'): l += [KconfigCheck('self_protection', 'defconfig', 'VMAP_STACK', 'y')] if arch in ('X86_64', 'X86_32'): @@ -362,6 +375,7 @@ def add_kconfig_checks(l, arch): l += [KconfigCheck('self_protection', 'defconfig', 'ARM64_BTI_KERNEL', 'y')] l += [OR(KconfigCheck('self_protection', 'defconfig', 'HARDEN_BRANCH_PREDICTOR', 'y'), VersionCheck((5, 10)))] # HARDEN_BRANCH_PREDICTOR is enabled by default since v5.10 + l += [KconfigCheck('self_protection', 'defconfig', 'MITIGATE_SPECTRE_BRANCH_HISTORY', 'y')] l += [KconfigCheck('self_protection', 'defconfig', 'ARM64_MTE', 'y')] if arch == 'ARM': l += [KconfigCheck('self_protection', 'defconfig', 'CPU_SW_DOMAIN_PAN', 'y')] @@ -434,7 +448,8 @@ def add_kconfig_checks(l, arch): # 'self_protection', 'clipos' l += [KconfigCheck('self_protection', 'clipos', 'DEBUG_VIRTUAL', 'y')] l += [KconfigCheck('self_protection', 'clipos', 'STATIC_USERMODEHELPER', 'y')] # needs userspace support - l += [KconfigCheck('self_protection', 'clipos', 'EFI_DISABLE_PCI_DMA', 'y')] + l += [OR(KconfigCheck('self_protection', 'clipos', 'EFI_DISABLE_PCI_DMA', 'y'), + efi_not_set)] l += [KconfigCheck('self_protection', 'clipos', 'SLAB_MERGE_DEFAULT', 'is not set')] # slab_nomerge l += [KconfigCheck('self_protection', 'clipos', 'RANDOM_TRUST_BOOTLOADER', 'is not set')] l += [KconfigCheck('self_protection', 'clipos', 'RANDOM_TRUST_CPU', 'is not set')] @@ -456,8 +471,10 @@ def add_kconfig_checks(l, arch): iommu_support_is_set)] # 'self_protection', 'my' - l += [KconfigCheck('self_protection', 'my', 'RESET_ATTACK_MITIGATION', 'y')] # needs userspace support (systemd) + l += [OR(KconfigCheck('self_protection', 'my', 'RESET_ATTACK_MITIGATION', 'y'), + efi_not_set)] # needs userspace support (systemd) if arch == 'X86_64': + l += [KconfigCheck('self_protection', 'my', 'SLS', 'y')] # vs CVE-2021-26341 in Straight-Line-Speculation l += [AND(KconfigCheck('self_protection', 'my', 'AMD_IOMMU_V2', 'y'), iommu_support_is_set)] if arch == 'ARM64': @@ -486,6 +503,7 @@ def add_kconfig_checks(l, arch): loadpin_is_set)] # 'cut_attack_surface', 'defconfig' + l += [KconfigCheck('cut_attack_surface', 'defconfig', 'BPF_UNPRIV_DEFAULT_OFF', 'y')] # see unprivileged_bpf_disabled l += [KconfigCheck('cut_attack_surface', 'defconfig', 'SECCOMP', 'y')] l += [KconfigCheck('cut_attack_surface', 'defconfig', 'SECCOMP_FILTER', 'y')] if arch in ('X86_64', 'ARM64', 'X86_32'): @@ -624,16 +642,16 @@ def print_unknown_options(checklist, parsed_options): known_options = [] for o1 in checklist: - if not hasattr(o1, 'opts'): + if o1.type != 'complex': known_options.append(o1.name) continue for o2 in o1.opts: - if not hasattr(o2, 'opts'): + if o2.type != 'complex': if hasattr(o2, 'name'): known_options.append(o2.name) continue for o3 in o2.opts: - if hasattr(o3, 'opts'): + if o3.type == 'complex': sys.exit('[!] ERROR: unexpected ComplexOptCheck inside {}'.format(o2.name)) if hasattr(o3, 'name'): known_options.append(o3.name) @@ -645,13 +663,10 @@ def print_unknown_options(checklist, parsed_options): def print_checklist(mode, checklist, with_results): if mode == 'json': - opts = [] + output = [] for o in checklist: - opt = [o.name, o.type, o.expected, o.decision, o.reason] - if with_results: - opt.append(o.result) - opts.append(opt) - print(json.dumps(opts)) + output.append(o.json_dump(with_results)) + print(json.dumps(output)) return # table header @@ -695,12 +710,14 @@ def print_checklist(mode, checklist, with_results): def populate_simple_opt_with_data(opt, data, data_type): - if hasattr(opt, 'opts'): + if opt.type == 'complex': sys.exit('[!] ERROR: unexpected ComplexOptCheck {}: {}'.format(opt.name, vars(opt))) if data_type not in TYPES_OF_CHECKS: sys.exit('[!] ERROR: invalid data type "{}"'.format(data_type)) + if data_type != opt.type: return + if data_type == 'kconfig': opt.state = data.get(opt.name, None) elif data_type == 'version': @@ -708,17 +725,16 @@ def populate_simple_opt_with_data(opt, data, data_type): def populate_opt_with_data(opt, data, data_type): - if hasattr(opt, 'opts'): + if opt.type == 'complex': for o in opt.opts: - if hasattr(o, 'opts'): + if o.type == 'complex': # Recursion for nested ComplexOptCheck objects populate_opt_with_data(o, data, data_type) else: populate_simple_opt_with_data(o, data, data_type) else: - # The 'state' is mandatory for simple checks - if not hasattr(opt, 'state'): - sys.exit('[!] ERROR: bad simple check {}'.format(vars(opt))) + if opt.type != 'kconfig': + sys.exit('[!] ERROR: bad type "{}" for a simple check {}'.format(opt.type, opt.name)) populate_simple_opt_with_data(opt, data, data_type)