implementation of arch_mmap_rnd checks
authorvalera disgrace <ya411508@gmail.com>
Sun, 27 Oct 2024 23:24:28 +0000 (09:24 +1000)
committervalera disgrace <ya411508@gmail.com>
Sun, 27 Oct 2024 23:24:28 +0000 (09:24 +1000)
kernel_hardening_checker/__init__.py
kernel_hardening_checker/checks.py

index 3e0535a6a03b938e96f62d4594a2bd32cb5e4b67..04964a8abd7bc93d29c71ff87b59a2febafdd0f4 100755 (executable)
@@ -247,6 +247,23 @@ def parse_sysctl_file(mode: StrOrNone, parsed_options: Dict[str, str], fname: st
         print(f'[!] WARNING: sysctl options available for root are not found in {fname}, please use the output of `sudo sysctl -a`')
 
 
+def refine_check(mode: StrOrNone, checklist: List[ChecklistObjType], parsed_options: Dict[str, str], target: str, source: str) -> None:
+    source_option = parsed_options.get(source, None)
+    if source_option:
+        override_expected_value(checklist, target, source_option)
+    else:
+        # remove the target check to avoid false results
+        if mode != 'json':
+            print(f'[-] Can\'t check {target} without {source}')
+        checklist[:] = [o for o in checklist if o.name != target]
+
+
+def clean_unoverrided(mode: StrOrNone, checklist: List[ChecklistObjType], target: str, source: str) -> None:
+    if mode != 'json':
+        print(f'[-] Can\'t check {target} without {source}: no config')
+    checklist[:] = [o for o in checklist if o.name != target]
+
+
 def perform_checking(mode: StrOrNone, version: TupleOrNone,
                      kconfig: StrOrNone, cmdline: StrOrNone, sysctl: StrOrNone) -> None:
     config_checklist = [] # type: List[ChecklistObjType]
@@ -301,16 +318,8 @@ def perform_checking(mode: StrOrNone, version: TupleOrNone,
         parsed_kconfig_options = {} # type: Dict[str, str]
         parse_kconfig_file(mode, parsed_kconfig_options, kconfig)
         populate_with_data(config_checklist, parsed_kconfig_options, 'kconfig')
-
-        # hackish refinement of the CONFIG_ARCH_MMAP_RND_BITS check
-        mmap_rnd_bits_max = parsed_kconfig_options.get('CONFIG_ARCH_MMAP_RND_BITS_MAX', None)
-        if mmap_rnd_bits_max:
-            override_expected_value(config_checklist, 'CONFIG_ARCH_MMAP_RND_BITS', mmap_rnd_bits_max)
-        else:
-            # remove the CONFIG_ARCH_MMAP_RND_BITS check to avoid false results
-            if mode != 'json':
-                print('[-] Can\'t check CONFIG_ARCH_MMAP_RND_BITS without CONFIG_ARCH_MMAP_RND_BITS_MAX')
-            config_checklist[:] = [o for o in config_checklist if o.name != 'CONFIG_ARCH_MMAP_RND_BITS']
+        refine_check(mode, config_checklist, parsed_kconfig_options, 'CONFIG_ARCH_MMAP_RND_BITS', 'CONFIG_ARCH_MMAP_RND_BITS_MAX')
+        refine_check(mode, config_checklist, parsed_kconfig_options, 'CONFIG_ARCH_MMAP_RND_COMPAT_BITS', 'CONFIG_ARCH_MMAP_RND_COMPAT_BITS_MAX')
 
     if cmdline:
         # populate the checklist with the parsed cmdline data
@@ -323,6 +332,13 @@ def perform_checking(mode: StrOrNone, version: TupleOrNone,
         parsed_sysctl_options = {} # type: Dict[str, str]
         parse_sysctl_file(mode, parsed_sysctl_options, sysctl)
         populate_with_data(config_checklist, parsed_sysctl_options, 'sysctl')
+        if kconfig:
+            refine_check(mode, config_checklist, parsed_kconfig_options, 'vm.mmap_rnd_bits', 'CONFIG_ARCH_MMAP_RND_BITS_MAX')
+            refine_check(mode, config_checklist, parsed_kconfig_options, 'vm.mmap_rnd_compat_bits', 'CONFIG_ARCH_MMAP_RND_COMPAT_BITS_MAX')
+        else:
+            clean_unoverrided(mode, config_checklist, 'vm.mmap_rnd_bits', 'CONFIG_ARCH_MMAP_RND_BITS_MAX')
+            clean_unoverrided(mode, config_checklist, 'vm.mmap_rnd_compat_bits', 'CONFIG_ARCH_MMAP_RND_COMPAT_BITS_MAX')
+
 
     # now everything is ready, perform the checks
     perform_checks(config_checklist)
index 246190ad8b56b2aeda023f0d0318358875a5208e..68cfb80cd365d0997128c347d41eb3183887bdca 100755 (executable)
@@ -472,6 +472,7 @@ def add_kconfig_checks(l: List[ChecklistObjType], arch: str) -> None:
         l += [KconfigCheck('harden_userspace', 'defconfig', 'VMSPLIT_3G', 'y')]
     l += [KconfigCheck('harden_userspace', 'clipos', 'COREDUMP', 'is not set')]
     l += [KconfigCheck('harden_userspace', 'a13xp0p0v', 'ARCH_MMAP_RND_BITS', 'MAX')] # 'MAX' value is refined using ARCH_MMAP_RND_BITS_MAX
+    l += [KconfigCheck('harden_userspace', 'a13xp0p0v', 'ARCH_MMAP_RND_COMPAT_BITS', 'MAX')] # 'MAX' value is refined using ARCH_MMAP_RND_COMPAT_BITS_MAX
     if arch == 'X86_64':
         l += [KconfigCheck('harden_userspace', 'kspp', 'X86_USER_SHADOW_STACK', 'y')]
 
@@ -788,3 +789,5 @@ def add_sysctl_checks(l: List[ChecklistObjType], arch: StrOrNone) -> None:
     l += [SysctlCheck('harden_userspace', 'kspp', 'fs.suid_dumpable', '0')]
     l += [SysctlCheck('harden_userspace', 'kspp', 'kernel.randomize_va_space', '2')]
     l += [SysctlCheck('harden_userspace', 'kspp', 'kernel.yama.ptrace_scope', '3')]
+    l += [SysctlCheck('harden_userspace', 'a13xp0p0v', 'vm.mmap_rnd_bits', 'MAX')] # 'MAX' value is refined using ARCH_MMAP_RND_BITS_MAX
+    l += [SysctlCheck('harden_userspace', 'a13xp0p0v', 'vm.mmap_rnd_compat_bits', 'MAX')] # 'MAX' value is refined using ARCH_MMAP_RND_COMPAT_BITS_MAX