From 4150d10206fa8a42fda5949a1dd1f976c9ecbea1 Mon Sep 17 00:00:00 2001 From: flipthewho Date: Thu, 26 Sep 2024 17:59:15 +1000 Subject: [PATCH] implementation of detect_arch_sysctl() --- .github/workflows/functional_test.sh | 24 +++++++++++++++++++ kernel_hardening_checker/__init__.py | 36 +++++++++++++++++++++++----- 2 files changed, 54 insertions(+), 6 deletions(-) diff --git a/.github/workflows/functional_test.sh b/.github/workflows/functional_test.sh index 0947efc..bdeefc4 100644 --- a/.github/workflows/functional_test.sh +++ b/.github/workflows/functional_test.sh @@ -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 diff --git a/kernel_hardening_checker/__init__.py b/kernel_hardening_checker/__init__.py index c3bb75f..f68bba8 100755 --- a/kernel_hardening_checker/__init__.py +++ b/kernel_hardening_checker/__init__.py @@ -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 = {} -- 2.31.1