X-Git-Url: https://jxself.org/git/?a=blobdiff_plain;f=kconfig_hardened_check%2F__init__.py;h=0502785b893b321796c6034720dad33df3b7d827;hb=bd3b0638e8c18cd629bd8c639d273d0e98d36cd2;hp=a98fbe81c582462a8c85b71999d16109a62305ea;hpb=88c0698ea5b5b1455ab6c52a9915d2bbcc00ee8b;p=kconfig-hardened-check.git diff --git a/kconfig_hardened_check/__init__.py b/kconfig_hardened_check/__init__.py index a98fbe8..0502785 100644 --- a/kconfig_hardened_check/__init__.py +++ b/kconfig_hardened_check/__init__.py @@ -150,7 +150,7 @@ def print_checklist(mode, checklist, with_results): print(f'[+] Config check is finished: \'OK\' - {ok_count}{ok_suppressed} / \'FAIL\' - {fail_count}{fail_suppressed}') -def parse_kconfig_file(parsed_options, fname): +def parse_kconfig_file(mode, parsed_options, fname): with _open(fname, 'rt', encoding='utf-8') as f: opt_is_on = re.compile("CONFIG_[a-zA-Z0-9_]+=.+$") opt_is_off = re.compile("# CONFIG_[a-zA-Z0-9_]+ is not set$") @@ -168,17 +168,17 @@ def parse_kconfig_file(parsed_options, fname): option, value = line[2:].split(' ', 1) assert(value == 'is not set'), \ f'unexpected value of disabled Kconfig option "{line}"' - elif line != '' and not line.startswith('#'): + elif line != '' and not line.startswith('#') and mode != 'json': print(f'[!] WARNING: strange line in Kconfig file: "{line}"') if option in parsed_options: - sys.exit(f'[!] ERROR: Kconfig option "{line}" exists multiple times') + sys.exit(f'[!] ERROR: Kconfig option "{line}" is found multiple times') if option: parsed_options[option] = value -def parse_cmdline_file(parsed_options, fname): +def parse_cmdline_file(mode, parsed_options, fname): with open(fname, 'r', encoding='utf-8') as f: line = f.readline() opts = line.split() @@ -193,14 +193,33 @@ def parse_cmdline_file(parsed_options, fname): else: name = opt value = '' # '' is not None - if name in parsed_options: - print(f'[!] WARNING: cmdline option "{name}" exists multiple times') + if name in parsed_options and mode != 'json': + print(f'[!] WARNING: cmdline option "{name}" is found multiple times') value = normalize_cmdline_options(name, value) parsed_options[name] = value -def parse_sysctl_file(parsed_options, fname): - print('parse_sysctl_file: TODO') +def parse_sysctl_file(mode, parsed_options, fname): + with open(fname, 'r', encoding='utf-8') as f: + sysctl_pattern = re.compile("[a-zA-Z0-9\._-]+ =.*$") + for line in f.readlines(): + line = line.strip() + if not sysctl_pattern.match(line): + sys.exit(f'[!] ERROR: unexpected line in sysctl file: {line}') + option, value = line.split('=', 1) + option = option.strip() + value = value.strip() + # sysctl options may be found multiple times, let's save the last value: + parsed_options[option] = value + + # 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: + sys.exit(f'[!] ERROR: {fname} doesn\'t look like a sysctl output file, please try `sudo sysctl -a > {fname}`') + + # let's check the presence of a sysctl option available for root + if 'net.core.bpf_jit_harden' not in parsed_options and mode != 'json': + print(f'[!] WARNING: sysctl option "net.core.bpf_jit_harden" available for root is not found in {fname}, please try `sudo sysctl -a > {fname}`') def main(): @@ -220,14 +239,13 @@ def main(): help='check the security hardening options in the kernel Kconfig file (also supports *.gz files)') parser.add_argument('-l', '--cmdline', help='check the security hardening options in the kernel cmdline file (contents of /proc/cmdline)') -# parser.add_argument('-s', '--sysctl', -# help='check the security hardening options in the sysctl output file (`sudo sysctl -a > file`)') + parser.add_argument('-s', '--sysctl', + help='check the security hardening options in the sysctl output file (`sudo sysctl -a > file`)') parser.add_argument('-p', '--print', choices=supported_archs, help='print the security hardening recommendations for the selected microarchitecture') parser.add_argument('-g', '--generate', choices=supported_archs, help='generate a Kconfig fragment with the security hardening options for the selected microarchitecture') args = parser.parse_args() - args.sysctl = None # FIXME mode = None if args.mode: @@ -249,7 +267,7 @@ def main(): if args.cmdline: print(f'[+] Kernel cmdline file to check: {args.cmdline}') if args.sysctl: - print(f'[+] Kernel sysctl output file to check: {args.sysctl}') + print(f'[+] Sysctl output file to check: {args.sysctl}') arch, msg = detect_arch(args.config, supported_archs) if arch is None: @@ -283,7 +301,7 @@ def main(): # populate the checklist with the parsed Kconfig data parsed_kconfig_options = OrderedDict() - parse_kconfig_file(parsed_kconfig_options, args.config) + parse_kconfig_file(mode, parsed_kconfig_options, args.config) populate_with_data(config_checklist, parsed_kconfig_options, 'kconfig') # populate the checklist with the kernel version data @@ -292,13 +310,13 @@ def main(): if args.cmdline: # populate the checklist with the parsed cmdline data parsed_cmdline_options = OrderedDict() - parse_cmdline_file(parsed_cmdline_options, args.cmdline) + parse_cmdline_file(mode, parsed_cmdline_options, args.cmdline) populate_with_data(config_checklist, parsed_cmdline_options, 'cmdline') if args.sysctl: # populate the checklist with the parsed sysctl data parsed_sysctl_options = OrderedDict() - parse_sysctl_file(parsed_sysctl_options, args.sysctl) + parse_sysctl_file(mode, parsed_sysctl_options, args.sysctl) populate_with_data(config_checklist, parsed_sysctl_options, 'sysctl') # hackish refinement of the CONFIG_ARCH_MMAP_RND_BITS check @@ -330,6 +348,8 @@ def main(): if args.print: assert(args.config is None and args.cmdline is None and args.sysctl is None), 'unexpected args' + if args.generate: + sys.exit('[!] ERROR: --print and --generate can\'t be used together') if mode and mode not in ('verbose', 'json'): sys.exit(f'[!] ERROR: wrong mode "{mode}" for --print') arch = args.print @@ -342,7 +362,7 @@ def main(): sys.exit(0) if args.generate: - assert(args.config is None and args.cmdline is None and args.sysctl is None), 'unexpected args' + assert(args.config is None and args.cmdline is None and args.sysctl is None and args.print is None), 'unexpected args' if mode: sys.exit(f'[!] ERROR: wrong mode "{mode}" for --generate') arch = args.generate