X-Git-Url: https://jxself.org/git/?a=blobdiff_plain;f=kconfig_hardened_check%2F__init__.py;h=ec858f0c1afee9cdb77831d990351753cfe7a998;hb=759ed4e9ad84dbdff8a5b6d62067125d63fec951;hp=8f59b34f3fc17ba0ee287a08235f3f888499575d;hpb=f47bd6f7ed3c79d485e44f3aea2b173655707b1c;p=kconfig-hardened-check.git diff --git a/kconfig_hardened_check/__init__.py b/kconfig_hardened_check/__init__.py index 8f59b34..ec858f0 100644 --- a/kconfig_hardened_check/__init__.py +++ b/kconfig_hardened_check/__init__.py @@ -12,7 +12,6 @@ # # N.B Hardening command line parameters: # iommu=force (does it help against DMA attacks?) -# loadpin.enforce=1 # # Mitigations of CPU vulnerabilities: # Аrch-independent: @@ -78,12 +77,30 @@ SIMPLE_OPTION_TYPES = ('kconfig', 'version', 'cmdline') class OptCheck: # Constructor without the 'expected' parameter is for option presence checks (any value is OK) def __init__(self, reason, decision, name, expected=None): - assert(reason and decision and name), \ - 'invalid {} check for "{}"'.format(self.__class__.__name__, name) + assert(name and name == name.strip() and len(name.split()) == 1), \ + 'invalid name "{}" for {}'.format(name, self.__class__.__name__) self.name = name - self.expected = expected + + assert(decision and decision == decision.strip() and len(decision.split()) == 1), \ + 'invalid decision "{}" for "{}" check'.format(decision, name) self.decision = decision + + assert(reason and reason == reason.strip() and len(reason.split()) == 1), \ + 'invalid reason "{}" for "{}" check'.format(reason, name) self.reason = reason + + if expected: + assert(expected == expected.strip()), \ + 'invalid expected value "{}" for "{}" check (1)'.format(expected, name) + val_len = len(expected.split()) + if val_len == 3: + assert(expected == 'is not set'), \ + 'invalid expected value "{}" for "{}" check (2)'.format(expected, name) + else: + assert(val_len == 1), \ + 'invalid expected value "{}" for "{}" check (3)'.format(expected, name) + self.expected = expected + self.state = None self.result = None @@ -145,6 +162,8 @@ class CmdlineCheck(OptCheck): class VersionCheck: def __init__(self, ver_expected): + assert(ver_expected and isinstance(ver_expected, tuple) and len(ver_expected) == 2), \ + 'invalid version "{}" for VersionCheck'.format(ver_expected) self.ver_expected = ver_expected self.ver = () self.result = None @@ -365,6 +384,9 @@ def add_kconfig_checks(l, arch): if arch in ('X86_64', 'ARM64'): l += [KconfigCheck('self_protection', 'defconfig', 'VMAP_STACK', 'y')] if arch in ('X86_64', 'X86_32'): + l += [KconfigCheck('self_protection', 'defconfig', 'X86_MCE', 'y')] + l += [KconfigCheck('self_protection', 'defconfig', 'X86_MCE_INTEL', 'y')] + l += [KconfigCheck('self_protection', 'defconfig', 'X86_MCE_AMD', 'y')] l += [KconfigCheck('self_protection', 'defconfig', 'MICROCODE', 'y')] # is needed for mitigating CPU bugs l += [KconfigCheck('self_protection', 'defconfig', 'RETPOLINE', 'y')] l += [OR(KconfigCheck('self_protection', 'defconfig', 'X86_SMAP', 'y'), @@ -385,6 +407,7 @@ def add_kconfig_checks(l, arch): l += [KconfigCheck('self_protection', 'defconfig', 'ARM64_PAN', 'y')] l += [KconfigCheck('self_protection', 'defconfig', 'ARM64_EPAN', 'y')] l += [KconfigCheck('self_protection', 'defconfig', 'UNMAP_KERNEL_AT_EL0', 'y')] + l += [KconfigCheck('self_protection', 'defconfig', 'ARM64_E0PD', 'y')] l += [OR(KconfigCheck('self_protection', 'defconfig', 'HARDEN_EL2_VECTORS', 'y'), AND(KconfigCheck('self_protection', 'defconfig', 'RANDOMIZE_BASE', 'y'), VersionCheck((5, 9))))] # HARDEN_EL2_VECTORS was included in RANDOMIZE_BASE in v5.9 @@ -410,6 +433,7 @@ def add_kconfig_checks(l, arch): l += [KconfigCheck('self_protection', 'kspp', 'SHUFFLE_PAGE_ALLOCATOR', 'y')] l += [KconfigCheck('self_protection', 'kspp', 'FORTIFY_SOURCE', 'y')] l += [KconfigCheck('self_protection', 'kspp', 'DEBUG_LIST', 'y')] + l += [KconfigCheck('self_protection', 'kspp', 'DEBUG_VIRTUAL', 'y')] l += [KconfigCheck('self_protection', 'kspp', 'DEBUG_SG', 'y')] l += [KconfigCheck('self_protection', 'kspp', 'DEBUG_CREDENTIALS', 'y')] l += [KconfigCheck('self_protection', 'kspp', 'DEBUG_NOTIFIERS', 'y')] @@ -421,9 +445,15 @@ def add_kconfig_checks(l, arch): l += [KconfigCheck('self_protection', 'kspp', 'IOMMU_DEFAULT_DMA_STRICT', 'y')] l += [KconfigCheck('self_protection', 'kspp', 'IOMMU_DEFAULT_PASSTHROUGH', 'is not set')] # true if IOMMU_DEFAULT_DMA_STRICT is set l += [KconfigCheck('self_protection', 'kspp', 'ZERO_CALL_USED_REGS', 'y')] + l += [KconfigCheck('self_protection', 'kspp', 'HW_RANDOM_TPM', 'y')] + l += [KconfigCheck('self_protection', 'kspp', 'STATIC_USERMODEHELPER', 'y')] # needs userspace support + l += [KconfigCheck('self_protection', 'kspp', 'SCHED_CORE', 'y')] randstruct_is_set = OR(KconfigCheck('self_protection', 'kspp', 'RANDSTRUCT_FULL', 'y'), KconfigCheck('self_protection', 'kspp', 'GCC_PLUGIN_RANDSTRUCT', 'y')) l += [randstruct_is_set] + l += [AND(KconfigCheck('self_protection', 'kspp', 'RANDSTRUCT_PERFORMANCE', 'is not set'), + KconfigCheck('self_protection', 'kspp', 'GCC_PLUGIN_RANDSTRUCT_PERFORMANCE', 'is not set'), + randstruct_is_set)] hardened_usercopy_is_set = KconfigCheck('self_protection', 'kspp', 'HARDENED_USERCOPY', 'y') l += [hardened_usercopy_is_set] l += [AND(KconfigCheck('self_protection', 'kspp', 'HARDENED_USERCOPY_FALLBACK', 'is not set'), @@ -447,77 +477,66 @@ def add_kconfig_checks(l, arch): # Starting from v5.11 CONFIG_PAGE_POISONING unconditionally checks # the 0xAA poison pattern on allocation. # That brings higher performance penalty. + l += [OR(KconfigCheck('self_protection', 'kspp', 'EFI_DISABLE_PCI_DMA', 'y'), + efi_not_set)] + l += [OR(KconfigCheck('self_protection', 'kspp', 'RESET_ATTACK_MITIGATION', 'y'), + efi_not_set)] # needs userspace support (systemd) + ubsan_bounds_is_set = KconfigCheck('self_protection', 'kspp', 'UBSAN_BOUNDS', 'y') + l += [ubsan_bounds_is_set] + l += [OR(KconfigCheck('self_protection', 'kspp', 'UBSAN_LOCAL_BOUNDS', 'y'), + AND(ubsan_bounds_is_set, + cc_is_gcc))] + l += [AND(KconfigCheck('self_protection', 'kspp', 'UBSAN_TRAP', 'y'), + ubsan_bounds_is_set, + KconfigCheck('self_protection', 'kspp', 'UBSAN_SHIFT', 'is not set'), + KconfigCheck('self_protection', 'kspp', 'UBSAN_DIV_ZERO', 'is not set'), + KconfigCheck('self_protection', 'kspp', 'UBSAN_UNREACHABLE', 'is not set'), + KconfigCheck('self_protection', 'kspp', 'UBSAN_BOOL', 'is not set'), + KconfigCheck('self_protection', 'kspp', 'UBSAN_ENUM', 'is not set'), + KconfigCheck('self_protection', 'kspp', 'UBSAN_ALIGNMENT', 'is not set'))] # only array index bounds checking with traps if arch in ('X86_64', 'ARM64', 'X86_32'): + l += [AND(KconfigCheck('self_protection', 'kspp', 'UBSAN_SANITIZE_ALL', 'y'), + ubsan_bounds_is_set)] # ARCH_HAS_UBSAN_SANITIZE_ALL is not enabled for ARM stackleak_is_set = KconfigCheck('self_protection', 'kspp', 'GCC_PLUGIN_STACKLEAK', 'y') l += [AND(stackleak_is_set, gcc_plugins_support_is_set)] + l += [AND(KconfigCheck('self_protection', 'kspp', 'STACKLEAK_METRICS', 'is not set'), + stackleak_is_set, + gcc_plugins_support_is_set)] + l += [AND(KconfigCheck('self_protection', 'kspp', 'STACKLEAK_RUNTIME_DISABLE', 'is not set'), + stackleak_is_set, + gcc_plugins_support_is_set)] l += [KconfigCheck('self_protection', 'kspp', 'RANDOMIZE_KSTACK_OFFSET_DEFAULT', 'y')] + if arch in ('X86_64', 'ARM64'): + cfi_clang_is_set = KconfigCheck('self_protection', 'kspp', 'CFI_CLANG', 'y') + l += [cfi_clang_is_set] + l += [AND(KconfigCheck('self_protection', 'kspp', 'CFI_PERMISSIVE', 'is not set'), + cfi_clang_is_set)] if arch in ('X86_64', 'X86_32'): - l += [KconfigCheck('self_protection', 'kspp', 'SCHED_CORE', 'y')] l += [KconfigCheck('self_protection', 'kspp', 'DEFAULT_MMAP_MIN_ADDR', '65536')] + l += [AND(KconfigCheck('self_protection', 'kspp', 'INTEL_IOMMU_DEFAULT_ON', 'y'), + iommu_support_is_set)] if arch in ('ARM64', 'ARM'): l += [KconfigCheck('self_protection', 'kspp', 'DEFAULT_MMAP_MIN_ADDR', '32768')] l += [KconfigCheck('self_protection', 'kspp', 'SYN_COOKIES', 'y')] # another reason? + if arch == 'X86_64': + l += [KconfigCheck('self_protection', 'kspp', 'SLS', 'y')] # vs CVE-2021-26341 in Straight-Line-Speculation + l += [AND(KconfigCheck('self_protection', 'kspp', 'INTEL_IOMMU_SVM', 'y'), + iommu_support_is_set)] + l += [AND(KconfigCheck('self_protection', 'kspp', 'AMD_IOMMU_V2', 'y'), + iommu_support_is_set)] if arch == 'ARM64': l += [KconfigCheck('self_protection', 'kspp', 'ARM64_SW_TTBR0_PAN', 'y')] + l += [KconfigCheck('self_protection', 'kspp', 'SHADOW_CALL_STACK', 'y')] + l += [KconfigCheck('self_protection', 'kspp', 'KASAN_HW_TAGS', 'y')] if arch == 'X86_32': l += [KconfigCheck('self_protection', 'kspp', 'PAGE_TABLE_ISOLATION', 'y')] l += [KconfigCheck('self_protection', 'kspp', 'HIGHMEM64G', 'y')] l += [KconfigCheck('self_protection', 'kspp', 'X86_PAE', 'y')] - - # 'self_protection', 'maintainer' - ubsan_bounds_is_set = KconfigCheck('self_protection', 'maintainer', 'UBSAN_BOUNDS', 'y') # only array index bounds checking - l += [ubsan_bounds_is_set] # recommended by Kees Cook in /issues/53 - if arch in ('X86_64', 'ARM64', 'X86_32'): # ARCH_HAS_UBSAN_SANITIZE_ALL is not enabled for ARM - l += [AND(KconfigCheck('self_protection', 'maintainer', 'UBSAN_SANITIZE_ALL', 'y'), - ubsan_bounds_is_set)] # recommended by Kees Cook in /issues/53 - l += [AND(KconfigCheck('self_protection', 'maintainer', 'UBSAN_TRAP', 'y'), - ubsan_bounds_is_set)] # recommended by Kees Cook in /issues/53 + l += [AND(KconfigCheck('self_protection', 'kspp', 'INTEL_IOMMU', 'y'), + iommu_support_is_set)] # 'self_protection', 'clipos' - l += [KconfigCheck('self_protection', 'clipos', 'DEBUG_VIRTUAL', 'y')] - l += [KconfigCheck('self_protection', 'clipos', 'STATIC_USERMODEHELPER', 'y')] # needs userspace support - 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')] - l += [KconfigCheck('self_protection', 'clipos', 'RANDOM_TRUST_BOOTLOADER', 'is not set')] - l += [KconfigCheck('self_protection', 'clipos', 'RANDOM_TRUST_CPU', 'is not set')] - l += [AND(KconfigCheck('self_protection', 'clipos', 'RANDSTRUCT_PERFORMANCE', 'is not set'), - KconfigCheck('self_protection', 'clipos', 'GCC_PLUGIN_RANDSTRUCT_PERFORMANCE', 'is not set'), - randstruct_is_set)] - if arch in ('X86_64', 'ARM64', 'X86_32'): - l += [AND(KconfigCheck('self_protection', 'clipos', 'STACKLEAK_METRICS', 'is not set'), - stackleak_is_set, - gcc_plugins_support_is_set)] - l += [AND(KconfigCheck('self_protection', 'clipos', 'STACKLEAK_RUNTIME_DISABLE', 'is not set'), - stackleak_is_set, - gcc_plugins_support_is_set)] - if arch in ('X86_64', 'X86_32'): - l += [AND(KconfigCheck('self_protection', 'clipos', 'INTEL_IOMMU_DEFAULT_ON', 'y'), - iommu_support_is_set)] - if arch == 'X86_64': - l += [AND(KconfigCheck('self_protection', 'clipos', 'INTEL_IOMMU_SVM', 'y'), - iommu_support_is_set)] - if arch == 'X86_32': - l += [AND(KconfigCheck('self_protection', 'clipos', 'INTEL_IOMMU', 'y'), - iommu_support_is_set)] - - # 'self_protection', 'my' - l += [OR(KconfigCheck('self_protection', 'my', 'RESET_ATTACK_MITIGATION', 'y'), - efi_not_set)] # needs userspace support (systemd) - l += [OR(KconfigCheck('self_protection', 'my', 'UBSAN_LOCAL_BOUNDS', 'y'), - AND(ubsan_bounds_is_set, - cc_is_gcc))] - 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': - l += [KconfigCheck('self_protection', 'my', 'SHADOW_CALL_STACK', 'y')] # maybe it's alternative to STACKPROTECTOR_STRONG - l += [KconfigCheck('self_protection', 'my', 'KASAN_HW_TAGS', 'y')] - cfi_clang_is_set = KconfigCheck('self_protection', 'my', 'CFI_CLANG', 'y') - l += [cfi_clang_is_set] - l += [AND(KconfigCheck('self_protection', 'my', 'CFI_PERMISSIVE', 'is not set'), - cfi_clang_is_set)] # 'security_policy' if arch in ('X86_64', 'ARM64', 'X86_32'): @@ -527,15 +546,12 @@ def add_kconfig_checks(l, arch): l += [KconfigCheck('security_policy', 'kspp', 'SECURITY_YAMA', 'y')] l += [KconfigCheck('security_policy', 'kspp', 'SECURITY_LANDLOCK', 'y')] l += [KconfigCheck('security_policy', 'kspp', 'SECURITY_SELINUX_DISABLE', 'is not set')] - l += [KconfigCheck('security_policy', 'clipos', 'SECURITY_LOCKDOWN_LSM', 'y')] - l += [KconfigCheck('security_policy', 'clipos', 'SECURITY_LOCKDOWN_LSM_EARLY', 'y')] - l += [KconfigCheck('security_policy', 'clipos', 'LOCK_DOWN_KERNEL_FORCE_CONFIDENTIALITY', 'y')] - l += [KconfigCheck('security_policy', 'my', 'SECURITY_WRITABLE_HOOKS', 'is not set')] # refers to SECURITY_SELINUX_DISABLE - l += [KconfigCheck('security_policy', 'my', 'SECURITY_SAFESETID', 'y')] - loadpin_is_set = KconfigCheck('security_policy', 'my', 'SECURITY_LOADPIN', 'y') - l += [loadpin_is_set] # needs userspace support - l += [AND(KconfigCheck('security_policy', 'my', 'SECURITY_LOADPIN_ENFORCE', 'y'), - loadpin_is_set)] + l += [KconfigCheck('security_policy', 'kspp', 'SECURITY_SELINUX_BOOTPARAM', 'is not set')] + l += [KconfigCheck('security_policy', 'kspp', 'SECURITY_SELINUX_DEVELOP', 'is not set')] + l += [KconfigCheck('security_policy', 'kspp', 'SECURITY_LOCKDOWN_LSM', 'y')] + l += [KconfigCheck('security_policy', 'kspp', 'SECURITY_LOCKDOWN_LSM_EARLY', 'y')] + l += [KconfigCheck('security_policy', 'kspp', 'LOCK_DOWN_KERNEL_FORCE_CONFIDENTIALITY', 'y')] + l += [KconfigCheck('security_policy', 'kspp', 'SECURITY_WRITABLE_HOOKS', 'is not set')] # refers to SECURITY_SELINUX_DISABLE # 'cut_attack_surface', 'defconfig' l += [OR(KconfigCheck('cut_attack_surface', 'defconfig', 'BPF_UNPRIV_DEFAULT_OFF', 'y'), @@ -567,6 +583,8 @@ def add_kconfig_checks(l, arch): l += [devmem_not_set] l += [OR(KconfigCheck('cut_attack_surface', 'kspp', 'IO_STRICT_DEVMEM', 'y'), devmem_not_set)] # refers to LOCKDOWN + l += [AND(KconfigCheck('cut_attack_surface', 'kspp', 'LDISC_AUTOLOAD', 'is not set'), + KconfigCheck('cut_attack_surface', 'kspp', 'LDISC_AUTOLOAD'))] # option presence check if arch == 'ARM': l += [OR(KconfigCheck('cut_attack_surface', 'kspp', 'STRICT_DEVMEM', 'y'), devmem_not_set)] # refers to LOCKDOWN @@ -637,8 +655,7 @@ def add_kconfig_checks(l, arch): l += [KconfigCheck('cut_attack_surface', 'clipos', 'X86_IOPL_IOPERM', 'is not set')] # refers to LOCKDOWN l += [KconfigCheck('cut_attack_surface', 'clipos', 'ACPI_TABLE_UPGRADE', 'is not set')] # refers to LOCKDOWN l += [KconfigCheck('cut_attack_surface', 'clipos', 'EFI_CUSTOM_SSDT_OVERLAYS', 'is not set')] - l += [AND(KconfigCheck('cut_attack_surface', 'clipos', 'LDISC_AUTOLOAD', 'is not set'), - KconfigCheck('cut_attack_surface', 'clipos', 'LDISC_AUTOLOAD'))] # option presence check + l += [KconfigCheck('cut_attack_surface', 'clipos', 'COREDUMP', 'is not set')] # cut userspace attack surface if arch in ('X86_64', 'X86_32'): l += [KconfigCheck('cut_attack_surface', 'clipos', 'X86_INTEL_TSX_MODE_OFF', 'y')] # tsx=off @@ -749,7 +766,8 @@ def add_cmdline_checks(l, arch): # 'self_protection', 'clipos' l += [CmdlineCheck('self_protection', 'clipos', 'page_alloc.shuffle', '1')] if arch in ('X86_64', 'X86_32'): - l += [CmdlineCheck('self_protection', 'clipos', 'spectre_v2', 'on')] + l += [AND(CmdlineCheck('self_protection', 'clipos', 'spectre_v2', 'on'), + CmdlineCheck('self_protection', 'defconfig', 'nospectre_v2', 'is not set'))] # 'cut_attack_surface', 'kspp' if arch == 'X86_64':