projects
/
kconfig-hardened-check.git
/ blobdiff
commit
grep
author
committer
pickaxe
?
search:
re
summary
|
shortlog
|
log
|
commit
|
commitdiff
|
tree
raw
|
inline
| side by side
Create multiple pipelines for Woodpecker-CI at Codeberg
[kconfig-hardened-check.git]
/
kconfig_hardened_check
/
__init__.py
diff --git
a/kconfig_hardened_check/__init__.py
b/kconfig_hardened_check/__init__.py
index b1a316e783c6c588907ad8ad9266231007170d4b..cdb08288fa0f3ccf2b938cf88fb92462070da61f 100644
(file)
--- a/
kconfig_hardened_check/__init__.py
+++ b/
kconfig_hardened_check/__init__.py
@@
-12,6
+12,7
@@
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,invalid-name,too-many-branches,too-many-statements
+import gzip
import sys
from argparse import ArgumentParser
from collections import OrderedDict
import sys
from argparse import ArgumentParser
from collections import OrderedDict
@@
-19,28
+20,36
@@
import re
import json
from .__about__ import __version__
from .checks import add_kconfig_checks, add_cmdline_checks, normalize_cmdline_options
import json
from .__about__ import __version__
from .checks import add_kconfig_checks, add_cmdline_checks, normalize_cmdline_options
-from .engine import populate_with_data, perform_checks
+from .engine import populate_with_data, perform_checks, override_expected_value
+
+
+def _open(file: str, *args, **kwargs):
+ open_method = open
+ if file.endswith(".gz"):
+ open_method = gzip.open
+
+ return open_method(file, *args, **kwargs)
def detect_arch(fname, archs):
def detect_arch(fname, archs):
- with
open(fname, 'r
', encoding='utf-8') as f:
+ with
_open(fname, 'rt
', encoding='utf-8') as f:
arch_pattern = re.compile("CONFIG_[a-zA-Z0-9_]*=y")
arch = None
for line in f.readlines():
if arch_pattern.match(line):
option, _ = line[7:].split('=', 1)
if option in archs:
arch_pattern = re.compile("CONFIG_[a-zA-Z0-9_]*=y")
arch = None
for line in f.readlines():
if arch_pattern.match(line):
option, _ = line[7:].split('=', 1)
if option in archs:
- if
not arch
:
+ if
arch is None
:
arch = option
else:
return None, 'more than one supported architecture is detected'
arch = option
else:
return None, 'more than one supported architecture is detected'
- if
not arch
:
+ if
arch is None
:
return None, 'failed to detect architecture'
return arch, 'OK'
def detect_kernel_version(fname):
return None, 'failed to detect architecture'
return arch, 'OK'
def detect_kernel_version(fname):
- with
open(fname, 'r
', encoding='utf-8') as f:
+ with
_open(fname, 'rt
', encoding='utf-8') as f:
ver_pattern = re.compile("# Linux/.* Kernel Configuration")
for line in f.readlines():
if ver_pattern.match(line):
ver_pattern = re.compile("# Linux/.* Kernel Configuration")
for line in f.readlines():
if ver_pattern.match(line):
@@
-49,7
+58,7
@@
def detect_kernel_version(fname):
ver_str = parts[2]
ver_numbers = ver_str.split('.')
if len(ver_numbers) < 3 or not ver_numbers[0].isdigit() or not ver_numbers[1].isdigit():
ver_str = parts[2]
ver_numbers = ver_str.split('.')
if len(ver_numbers) < 3 or not ver_numbers[0].isdigit() or not ver_numbers[1].isdigit():
- msg =
'failed to parse the version "' + ver_str + '
"'
+ msg =
f'failed to parse the version "{ver_str}
"'
return None, msg
return (int(ver_numbers[0]), int(ver_numbers[1])), None
return None, 'no kernel version detected'
return None, msg
return (int(ver_numbers[0]), int(ver_numbers[1])), None
return None, 'no kernel version detected'
@@
-58,7
+67,7
@@
def detect_kernel_version(fname):
def detect_compiler(fname):
gcc_version = None
clang_version = None
def detect_compiler(fname):
gcc_version = None
clang_version = None
- with
open(fname, 'r
', encoding='utf-8') as f:
+ with
_open(fname, 'rt
', encoding='utf-8') as f:
gcc_version_pattern = re.compile("CONFIG_GCC_VERSION=[0-9]*")
clang_version_pattern = re.compile("CONFIG_CLANG_VERSION=[0-9]*")
for line in f.readlines():
gcc_version_pattern = re.compile("CONFIG_GCC_VERSION=[0-9]*")
clang_version_pattern = re.compile("CONFIG_CLANG_VERSION=[0-9]*")
for line in f.readlines():
@@
-66,7
+75,7
@@
def detect_compiler(fname):
gcc_version = line[19:-1]
if clang_version_pattern.match(line):
clang_version = line[21:-1]
gcc_version = line[19:-1]
if clang_version_pattern.match(line):
clang_version = line[21:-1]
- if
not gcc_version or not clang_version
:
+ if
gcc_version is None or clang_version is None
:
return None, 'no CONFIG_GCC_VERSION or CONFIG_CLANG_VERSION'
if gcc_version == '0' and clang_version != '0':
return 'CLANG ' + clang_version, 'OK'
return None, 'no CONFIG_GCC_VERSION or CONFIG_CLANG_VERSION'
if gcc_version == '0' and clang_version != '0':
return 'CLANG ' + clang_version, 'OK'
@@
-101,8
+110,8
@@
def print_unknown_options(checklist, parsed_options):
def print_checklist(mode, checklist, with_results):
if mode == 'json':
output = []
def print_checklist(mode, checklist, with_results):
if mode == 'json':
output = []
- for o in checklist:
- output.append(o.json_dump(with_results))
+ for o
pt
in checklist:
+ output.append(o
pt
.json_dump(with_results))
print(json.dumps(output))
return
print(json.dumps(output))
return
@@
-111,7
+120,7
@@
def print_checklist(mode, checklist, with_results):
if with_results:
sep_line_len += 30
print('=' * sep_line_len)
if with_results:
sep_line_len += 30
print('=' * sep_line_len)
- print(f
"{'option name':^40}|{'type':^7}|{'desired val':^12}|{'decision':^10}|{'reason':^18}"
, end='')
+ print(f
'{"option name":^40}|{"type":^7}|{"desired val":^12}|{"decision":^10}|{"reason":^18}'
, end='')
if with_results:
print('| check result', end='')
print()
if with_results:
print('| check result', end='')
print()
@@
-142,12
+151,11
@@
def print_checklist(mode, checklist, with_results):
fail_suppressed = ' (suppressed in output)'
if mode == 'show_fail':
ok_suppressed = ' (suppressed in output)'
fail_suppressed = ' (suppressed in output)'
if mode == 'show_fail':
ok_suppressed = ' (suppressed in output)'
- if mode != 'json':
- print(f'[+] Config check is finished: \'OK\' - {ok_count}{ok_suppressed} / \'FAIL\' - {fail_count}{fail_suppressed}')
+ 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(parsed_options, fname):
- with
open(fname, 'r
', encoding='utf-8') as f:
+ with
_open(fname, 'rt
', encoding='utf-8') as f:
opt_is_on = re.compile("CONFIG_[a-zA-Z0-9_]*=[a-zA-Z0-9_\"]*")
opt_is_off = re.compile("# CONFIG_[a-zA-Z0-9_]* is not set")
opt_is_on = re.compile("CONFIG_[a-zA-Z0-9_]*=[a-zA-Z0-9_\"]*")
opt_is_off = re.compile("# CONFIG_[a-zA-Z0-9_]* is not set")
@@
-205,7
+213,7
@@
def main():
parser.add_argument('-p', '--print', choices=supported_archs,
help='print security hardening preferences for the selected architecture')
parser.add_argument('-c', '--config',
parser.add_argument('-p', '--print', choices=supported_archs,
help='print security hardening preferences for the selected architecture')
parser.add_argument('-c', '--config',
- help='check the kernel kconfig file against these preferences')
+ help='check the kernel kconfig file against these preferences
(also supports *.gz files)
')
parser.add_argument('-l', '--cmdline',
help='check the kernel cmdline file against these preferences')
parser.add_argument('-m', '--mode', choices=report_modes,
parser.add_argument('-l', '--cmdline',
help='check the kernel cmdline file against these preferences')
parser.add_argument('-m', '--mode', choices=report_modes,
@@
-230,13
+238,13
@@
def main():
print(f'[+] Kernel cmdline file to check: {args.cmdline}')
arch, msg = detect_arch(args.config, supported_archs)
print(f'[+] Kernel cmdline file to check: {args.cmdline}')
arch, msg = detect_arch(args.config, supported_archs)
- if
not arch
:
+ if
arch is None
:
sys.exit(f'[!] ERROR: {msg}')
if mode != 'json':
print(f'[+] Detected architecture: {arch}')
kernel_version, msg = detect_kernel_version(args.config)
sys.exit(f'[!] ERROR: {msg}')
if mode != 'json':
print(f'[+] Detected architecture: {arch}')
kernel_version, msg = detect_kernel_version(args.config)
- if
not kernel_version
:
+ if
kernel_version is None
:
sys.exit(f'[!] ERROR: {msg}')
if mode != 'json':
print(f'[+] Detected kernel version: {kernel_version[0]}.{kernel_version[1]}')
sys.exit(f'[!] ERROR: {msg}')
if mode != 'json':
print(f'[+] Detected kernel version: {kernel_version[0]}.{kernel_version[1]}')
@@
-259,14
+267,21
@@
def main():
parsed_kconfig_options = OrderedDict()
parse_kconfig_file(parsed_kconfig_options, args.config)
populate_with_data(config_checklist, parsed_kconfig_options, 'kconfig')
parsed_kconfig_options = OrderedDict()
parse_kconfig_file(parsed_kconfig_options, args.config)
populate_with_data(config_checklist, parsed_kconfig_options, 'kconfig')
+
+ # populate the checklist with the kernel version data
populate_with_data(config_checklist, kernel_version, 'version')
if args.cmdline:
populate_with_data(config_checklist, kernel_version, 'version')
if args.cmdline:
- # populate the checklist with the parsed
kconfig
data
+ # populate the checklist with the parsed
cmdline
data
parsed_cmdline_options = OrderedDict()
parse_cmdline_file(parsed_cmdline_options, args.cmdline)
populate_with_data(config_checklist, parsed_cmdline_options, 'cmdline')
parsed_cmdline_options = OrderedDict()
parse_cmdline_file(parsed_cmdline_options, args.cmdline)
populate_with_data(config_checklist, parsed_cmdline_options, 'cmdline')
+ # 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)
+
# now everything is ready, perform the checks
perform_checks(config_checklist)
# now everything is ready, perform the checks
perform_checks(config_checklist)