implementation of detect_arch_sysctl() 161/head
authorflipthewho <ya411508@gmail.com>
Thu, 26 Sep 2024 07:59:15 +0000 (17:59 +1000)
committerflipthewho <ya411508@gmail.com>
Thu, 26 Sep 2024 07:59:15 +0000 (17:59 +1000)
.github/workflows/functional_test.sh
kernel_hardening_checker/__init__.py

index 0947efc0c2e7e3ff027511be7d9439ca31dd6a08..bdeefc4d2ea10aa641ff3a628e6b94cd7e6bec6d 100644 (file)
@@ -66,6 +66,30 @@ do
 done
 echo "\n>>>>> have checked $COUNT kconfigs <<<<<"
 
+echo ">>>>> test kconfig arch detection <<<<<"
+cp $CONFIG_DIR/defconfigs/x86_64_defconfig_6.6.config ./test.config
+coverage run -a --branch bin/kernel-hardening-checker -c ./test.config | grep "Detected microarchitecture: X86_64"
+cp $CONFIG_DIR/defconfigs/x86_32_defconfig_6.6.config ./test.config
+coverage run -a --branch bin/kernel-hardening-checker -c ./test.config | grep "Detected microarchitecture: X86_32"
+cp $CONFIG_DIR/defconfigs/arm_defconfig_6.6.config ./test.config
+coverage run -a --branch bin/kernel-hardening-checker -c ./test.config | grep "Detected microarchitecture: ARM"
+cp $CONFIG_DIR/defconfigs/arm64_defconfig_6.6.config ./test.config
+coverage run -a --branch bin/kernel-hardening-checker -c ./test.config | grep "Detected microarchitecture: ARM64"
+
+echo ">>>>> test sysctl arch detection <<<<<"
+echo "kernel.arch = x86_64" > /tmp/sysctl_arch # same as output of `sysctl kernel.arch`
+coverage run -a --branch bin/kernel-hardening-checker -s /tmp/sysctl_arch | grep "Detected microarchitecture: X86_64"
+echo "kernel.arch = i386" > /tmp/sysctl_arch
+coverage run -a --branch bin/kernel-hardening-checker -s /tmp/sysctl_arch | grep "Detected microarchitecture: X86_32"
+echo "kernel.arch = armv7l" > /tmp/sysctl_arch
+coverage run -a --branch bin/kernel-hardening-checker -s /tmp/sysctl_arch | grep "Detected microarchitecture: ARM"
+echo "kernel.arch = aarch64" > /tmp/sysctl_arch
+coverage run -a --branch bin/kernel-hardening-checker -s /tmp/sysctl_arch | grep "Detected microarchitecture: ARM64"
+echo "kernel.arch = armv8b" > /tmp/sysctl_arch
+coverage run -a --branch bin/kernel-hardening-checker -s /tmp/sysctl_arch | grep "Detected microarchitecture: ARM64"
+echo "kernel.arch = bad" > /tmp/sysctl_arch
+coverage run -a --branch bin/kernel-hardening-checker -s /tmp/sysctl_arch | grep "bad is an unsupported arch, arch-dependent checks will be dropped"
+
 echo ">>>>> check sysctl separately <<<<<"
 coverage run -a --branch bin/kernel-hardening-checker -s $SYSCTL_EXAMPLE
 coverage run -a --branch bin/kernel-hardening-checker -s $SYSCTL_EXAMPLE -m verbose > /dev/null
index c3bb75f0d3f3c2ec35f6e6bf8d50ac9b937369f2..f68bba840484347cf407f170f3f1d645883caa77 100755 (executable)
@@ -36,7 +36,7 @@ def _open(file: str) -> TextIO:
         sys.exit(f'[!] ERROR: unable to open {file}, are you sure it exists?')
 
 
-def detect_arch(fname: str, supported_archs: List[str]) -> Tuple[StrOrNone, str]:
+def detect_arch_kconfig(fname: str, supported_archs: List[str]) -> Tuple[StrOrNone, str]:
     arch = None
 
     with _open(fname) as f:
@@ -48,13 +48,32 @@ def detect_arch(fname: str, supported_archs: List[str]) -> Tuple[StrOrNone, str]
                 if arch is None:
                     arch = option
                 else:
-                    return None, 'detected more than one microarchitecture'
+                    return None, 'detected more than one microarchitecture in kconfig'
 
     if arch is None:
-        return None, 'failed to detect microarchitecture'
+        return None, 'failed to detect microarchitecture in kconfig'
     return arch, 'OK'
 
 
+def detect_arch_sysctl(fname: str, supported_archs: List[str]) -> Tuple[StrOrNone, str]:
+    arch_mapping = {
+        'ARM64': r'^aarch64|armv8', # armv8+ is 64-bit
+        'ARM': r'^armv[3-7]',       # armv? is 32-bit (below 8)
+        'X86_32': r'^i[3-6]?86',    # i?86 is 32-bit
+        'X86_64': r'^x86_64'
+    }
+    with _open(fname) as f:
+        for line in f.readlines():
+            if line.startswith('kernel.arch'):
+                value = line.split('=', 1)[1].strip()
+                for arch, pattern in arch_mapping.items():
+                    assert(arch in supported_archs), 'invalid arch mapping in sysctl'
+                    if re.search(pattern, value):
+                        return arch, value
+                return None, f'{value} is an unsupported arch'
+        return None, 'failed to detect microarchitecture in sysctl'
+
+
 def detect_kernel_version(fname: str) -> Tuple[TupleOrNone, str]:
     with _open(fname) as f:
         ver_pattern = re.compile(r"^# Linux/.+ Kernel Configuration$|^Linux version .+")
@@ -221,7 +240,7 @@ def parse_sysctl_file(mode: StrOrNone, parsed_options: Dict[str, str], fname: st
 
     # let's check the presence of some ancient sysctl option
     # to ensure that we are parsing the output of `sudo sysctl -a > file`
-    if 'kernel.printk' not in parsed_options:
+    if 'kernel.printk' not in parsed_options and mode != 'json':
         print(f'[!] WARNING: ancient sysctl options are not found in {fname}, please use the output of `sudo sysctl -a`')
 
     # let's check the presence of a sysctl option available for root
@@ -277,7 +296,7 @@ def main() -> None:
             if args.sysctl:
                 print(f'[+] Sysctl output file to check: {args.sysctl}')
 
-        arch, msg = detect_arch(args.config, supported_archs)
+        arch, msg = detect_arch_kconfig(args.config, supported_archs)
         if arch is None:
             sys.exit(f'[!] ERROR: {msg}')
         if mode != 'json':
@@ -366,11 +385,16 @@ def main() -> None:
         if args.generate:
             sys.exit('[!] ERROR: --sysctl and --generate can\'t be used together')
 
+        arch, msg = detect_arch_sysctl(args.sysctl, supported_archs)
         if mode != 'json':
             print(f'[+] Sysctl output file to check: {args.sysctl}')
+            if arch is None:
+                print(f'[!] WARNING: {msg}, arch-dependent checks will be dropped')
+            else:
+                print(f'[+] Detected microarchitecture: {arch} ({msg})')
 
         # add relevant sysctl checks to the checklist
-        add_sysctl_checks(config_checklist, None)
+        add_sysctl_checks(config_checklist, arch)
 
         # populate the checklist with the parsed sysctl data
         parsed_sysctl_options = {}