This module performs input/output.
"""
-# pylint: disable=missing-function-docstring,line-too-long,invalid-name,too-many-branches,too-many-statements
+# pylint: disable=missing-function-docstring,line-too-long,too-many-branches,too-many-statements
+import os
import gzip
import sys
from argparse import ArgumentParser
-from collections import OrderedDict
-from typing import List, Tuple, OrderedDict, TextIO
+from typing import List, Tuple, Dict, TextIO
import re
import json
-from .__about__ import __version__
from .checks import add_kconfig_checks, add_cmdline_checks, normalize_cmdline_options, add_sysctl_checks
-from .engine import StrOrNone, TupleOrNone, print_unknown_options, populate_with_data, perform_checks, override_expected_value
+from .engine import StrOrNone, TupleOrNone, ChecklistObjType
+from .engine import print_unknown_options, populate_with_data, perform_checks, override_expected_value
-def _open(file: str, *args, **kwargs) -> TextIO:
- if file.endswith('.gz'):
- return gzip.open(file, *args, **kwargs)
- return open(file, *args, **kwargs)
+# kernel-hardening-checker version
+__version__ = '0.6.6'
+
+
+def _open(file: str) -> TextIO:
+ try:
+ if file.endswith('.gz'):
+ return gzip.open(file, 'rt', encoding='utf-8')
+ return open(file, 'rt', encoding='utf-8')
+ except FileNotFoundError:
+ sys.exit(f'[!] ERROR: unable to open {file}, are you sure it exists?')
def detect_arch(fname: str, archs: List[str]) -> Tuple[StrOrNone, str]:
- with _open(fname, 'rt', encoding='utf-8') as f:
+ with _open(fname) as f:
arch_pattern = re.compile(r"CONFIG_[a-zA-Z0-9_]+=y$")
arch = None
for line in f.readlines():
def detect_kernel_version(fname: str) -> Tuple[TupleOrNone, str]:
- with _open(fname, 'rt', encoding='utf-8') as f:
+ with _open(fname) as f:
ver_pattern = re.compile(r"^# Linux/.+ Kernel Configuration$|^Linux version .+")
for line in f.readlines():
if ver_pattern.match(line):
ver_str = parts[2].split('-', 1)[0]
ver_numbers = ver_str.split('.')
if len(ver_numbers) >= 3:
- if all(map(lambda x: x.isdigit(), ver_numbers)):
+ if all(map(lambda x: x.isdecimal(), ver_numbers)):
return tuple(map(int, ver_numbers)), 'OK'
msg = f'failed to parse the version "{parts[2]}"'
return None, msg
def detect_compiler(fname: str) -> Tuple[StrOrNone, str]:
gcc_version = None
clang_version = None
- with _open(fname, 'rt', encoding='utf-8') as f:
+ with _open(fname) as f:
for line in f.readlines():
if line.startswith('CONFIG_GCC_VERSION='):
gcc_version = line[19:-1]
sys.exit(f'[!] ERROR: invalid GCC_VERSION and CLANG_VERSION: {gcc_version} {clang_version}')
-def print_checklist(mode: StrOrNone, checklist: List, with_results: bool) -> None:
+def print_checklist(mode: StrOrNone, checklist: List[ChecklistObjType], with_results: bool) -> None:
if mode == 'json':
output = []
for opt in checklist:
print('=' * sep_line_len)
# table contents
+ ok_count = 0
+ fail_count = 0
for opt in checklist:
if with_results:
- if mode == 'show_ok':
- if not opt.result.startswith('OK'):
+ assert(opt.result), f'unexpected empty result of {opt.name} check'
+ if opt.result.startswith('OK'):
+ ok_count += 1
+ if mode == 'show_fail':
continue
- if mode == 'show_fail':
- if not opt.result.startswith('FAIL'):
+ elif opt.result.startswith('FAIL'):
+ fail_count += 1
+ if mode == 'show_ok':
continue
+ else:
+ assert(False), f'unexpected result "{opt.result}" of {opt.name} check'
opt.table_print(mode, with_results)
print()
if mode == 'verbose':
# final score
if with_results:
- fail_count = len(list(filter(lambda opt: opt.result.startswith('FAIL'), checklist)))
fail_suppressed = ''
- ok_count = len(list(filter(lambda opt: opt.result.startswith('OK'), checklist)))
ok_suppressed = ''
if mode == 'show_ok':
fail_suppressed = ' (suppressed in output)'
print(f'[+] Config check is finished: \'OK\' - {ok_count}{ok_suppressed} / \'FAIL\' - {fail_count}{fail_suppressed}')
-def parse_kconfig_file(_mode: StrOrNone, parsed_options: OrderedDict[str, str], fname: str) -> None:
- with _open(fname, 'rt', encoding='utf-8') as f:
+def parse_kconfig_file(_mode: StrOrNone, parsed_options: Dict[str, str], fname: str) -> None:
+ with _open(fname) as f:
opt_is_on = re.compile(r"CONFIG_[a-zA-Z0-9_]+=.+$")
opt_is_off = re.compile(r"# CONFIG_[a-zA-Z0-9_]+ is not set$")
parsed_options[option] = value
-def parse_cmdline_file(mode: StrOrNone, parsed_options: OrderedDict[str, str], fname: str) -> None:
+def parse_cmdline_file(mode: StrOrNone, parsed_options: Dict[str, str], fname: str) -> None:
+ if not os.path.isfile(fname):
+ sys.exit(f'[!] ERROR: unable to open {fname}, are you sure it exists?')
+
with open(fname, 'r', encoding='utf-8') as f:
line = f.readline()
+ if not line:
+ sys.exit(f'[!] ERROR: empty "{fname}"')
+
opts = line.split()
line = f.readline()
parsed_options[name] = value
-def parse_sysctl_file(mode: StrOrNone, parsed_options: OrderedDict[str, str], fname: str) -> None:
+def parse_sysctl_file(mode: StrOrNone, parsed_options: Dict[str, str], fname: str) -> None:
+ if not os.path.isfile(fname):
+ sys.exit(f'[!] ERROR: unable to open {fname}, are you sure it exists?')
+
with open(fname, 'r', encoding='utf-8') as f:
sysctl_pattern = re.compile(r"[a-zA-Z0-9/\._-]+ =.*$")
for line in f.readlines():
if mode != 'json':
print(f'[+] Special report mode: {mode}')
- config_checklist = [] # type: List
+ config_checklist = [] # type: List[ChecklistObjType]
if args.config:
if args.print:
add_sysctl_checks(config_checklist, arch)
# populate the checklist with the parsed Kconfig data
- parsed_kconfig_options = OrderedDict() # type: OrderedDict[str, str]
+ parsed_kconfig_options = {} # type: Dict[str, str]
parse_kconfig_file(mode, parsed_kconfig_options, args.config)
populate_with_data(config_checklist, parsed_kconfig_options, 'kconfig')
if args.cmdline:
# populate the checklist with the parsed cmdline data
- parsed_cmdline_options = OrderedDict() # type: OrderedDict[str, str]
+ parsed_cmdline_options = {} # type: Dict[str, str]
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() # type: OrderedDict[str, str]
+ parsed_sysctl_options = {} # type: Dict[str, str]
parse_sysctl_file(mode, parsed_sysctl_options, args.sysctl)
populate_with_data(config_checklist, parsed_sysctl_options, 'sysctl')
add_sysctl_checks(config_checklist, None)
# populate the checklist with the parsed sysctl data
- parsed_sysctl_options = OrderedDict()
+ parsed_sysctl_options = {}
parse_sysctl_file(mode, parsed_sysctl_options, args.sysctl)
populate_with_data(config_checklist, parsed_sysctl_options, 'sysctl')
if mode and mode not in ('verbose', 'json'):
sys.exit(f'[!] ERROR: wrong mode "{mode}" for --print')
arch = args.print
- assert arch, 'unexpected empty arch from ArgumentParser'
+ assert(arch), 'unexpected empty arch from ArgumentParser'
add_kconfig_checks(config_checklist, arch)
add_cmdline_checks(config_checklist, arch)
add_sysctl_checks(config_checklist, arch)
sys.exit(0)
if args.generate:
- assert(args.config is None and args.cmdline is None and args.sysctl is None and args.print 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
- assert arch, 'unexpected empty arch from ArgumentParser'
+ assert(arch), 'unexpected empty arch from ArgumentParser'
add_kconfig_checks(config_checklist, arch)
print(f'CONFIG_{arch}=y') # the Kconfig fragment should describe the microarchitecture
for opt in config_checklist: