Add CONFIG_INPUT_EVBUG
[kconfig-hardened-check.git] / kconfig_hardened_check / __init__.py
index 8e224c9164baa2b2025988c64aae5d6bb8062009..15ea3e4e5d9c412a2990c324c114374fd4dfc42e 100755 (executable)
@@ -59,6 +59,7 @@ from argparse import ArgumentParser
 from collections import OrderedDict
 import re
 import json
+from .__about__ import __version__
 
 # debug_mode enables:
 #    - reporting about unknown kernel options in the config,
@@ -98,6 +99,11 @@ class OptCheck:
         else:
             return False, self.result
 
+    def table_print(self, with_results):
+        print('CONFIG_{:<38}|{:^13}|{:^10}|{:^20}'.format(self.name, self.expected, self.decision, self.reason), end='')
+        if with_results:
+            print('|   {}'.format(self.result), end='')
+
 
 class VerCheck:
     def __init__(self, ver_expected):
@@ -118,6 +124,32 @@ class VerCheck:
             self.result = 'FAIL: version < ' + str(self.ver_expected[0]) + '.' + str(self.ver_expected[1])
             return False, self.result
 
+    def table_print(self, with_results):
+        ver_req = 'kernel version >= ' + str(self.ver_expected[0]) + '.' + str(self.ver_expected[1])
+        print('{:<91}'.format(ver_req), end='')
+        if with_results:
+            print('|   {}'.format(self.result), end='')
+
+
+class PresenceCheck:
+    def __init__(self, name):
+        self.name = name
+        self.state = None
+        self.result = None
+
+    def check(self):
+        if self.state is None:
+            self.result = 'FAIL: not present'
+            return False, self.result
+        else:
+            self.result = 'OK: is present'
+            return True, self.result
+
+    def table_print(self, with_results):
+        print('CONFIG_{:<84}'.format(self.name + ' is present'), end='')
+        if with_results:
+            print('|   {}'.format(self.result), end='')
+
 
 class ComplexOptCheck:
     def __init__(self, *opts):
@@ -144,6 +176,20 @@ class ComplexOptCheck:
     def reason(self):
         return self.opts[0].reason
 
+    def table_print(self, with_results):
+        if debug_mode:
+            print('    {:87}'.format('<<< ' + self.__class__.__name__ + ' >>>'), end='')
+            if with_results:
+                print('|   {}'.format(self.result), end='')
+            for o in self.opts:
+                print()
+                o.table_print(with_results)
+        else:
+            o = self.opts[0]
+            o.table_print(False)
+            if with_results:
+                print('|   {}'.format(self.result), end='')
+
 
 class OR(ComplexOptCheck):
     # self.opts[0] is the option that this OR-check is about.
@@ -158,7 +204,7 @@ class OR(ComplexOptCheck):
         for i, opt in enumerate(self.opts):
             ret, msg = opt.check()
             if ret:
-                if i == 0 or not hasattr(opt, 'name'):
+                if i == 0 or not hasattr(opt, 'expected'):
                     self.result = opt.result
                 else:
                     self.result = 'OK: CONFIG_{} "{}"'.format(opt.name, opt.expected)
@@ -179,7 +225,7 @@ class AND(ComplexOptCheck):
                 self.result = opt.result
                 return ret, self.result
             elif not ret:
-                if hasattr(opt, 'name'):
+                if hasattr(opt, 'expected'):
                     self.result = 'FAIL: CONFIG_{} is needed'.format(opt.name)
                 else:
                     self.result = opt.result
@@ -414,6 +460,10 @@ def construct_checklist(checklist, arch):
     checklist.append(OptCheck('DEBUG_FS',                'is not set', 'grsecurity', 'cut_attack_surface')) # refers to LOCKDOWN
     checklist.append(OptCheck('NOTIFIER_ERROR_INJECTION','is not set', 'grsecurity', 'cut_attack_surface'))
 
+    checklist.append(OptCheck('DRM_LEGACY',     'is not set', 'maintainer', 'cut_attack_surface'))
+    checklist.append(OptCheck('FB',             'is not set', 'maintainer', 'cut_attack_surface'))
+    checklist.append(OptCheck('VT',             'is not set', 'maintainer', 'cut_attack_surface'))
+
     checklist.append(OptCheck('ACPI_TABLE_UPGRADE',   'is not set', 'lockdown', 'cut_attack_surface')) # refers to LOCKDOWN
     checklist.append(OptCheck('X86_IOPL_IOPERM',      'is not set', 'lockdown', 'cut_attack_surface')) # refers to LOCKDOWN
     checklist.append(OptCheck('EFI_TEST',             'is not set', 'lockdown', 'cut_attack_surface')) # refers to LOCKDOWN
@@ -433,7 +483,7 @@ def construct_checklist(checklist, arch):
     checklist.append(OptCheck('X86_MSR',                  'is not set', 'clipos', 'cut_attack_surface')) # refers to LOCKDOWN
     checklist.append(OptCheck('X86_CPUID',                'is not set', 'clipos', 'cut_attack_surface'))
     checklist.append(AND(OptCheck('LDISC_AUTOLOAD',           'is not set', 'clipos', 'cut_attack_surface'), \
-                         VerCheck((5, 1)))) # LDISC_AUTOLOAD can be disabled since v5.1
+                         PresenceCheck('LDISC_AUTOLOAD')))
 
     checklist.append(OptCheck('AIO',                  'is not set', 'grapheneos', 'cut_attack_surface'))
 
@@ -444,6 +494,7 @@ def construct_checklist(checklist, arch):
     checklist.append(OptCheck('FTRACE',               'is not set', 'my', 'cut_attack_surface')) # refers to LOCKDOWN
     checklist.append(OptCheck('BPF_JIT',              'is not set', 'my', 'cut_attack_surface'))
     checklist.append(OptCheck('VIDEO_VIVID',          'is not set', 'my', 'cut_attack_surface'))
+    checklist.append(OptCheck('INPUT_EVBUG',          'is not set', 'my', 'cut_attack_surface')) # Can be used as a keylogger
 
     checklist.append(OptCheck('INTEGRITY',       'y', 'defconfig', 'userspace_hardening'))
     if arch == 'ARM64':
@@ -458,13 +509,6 @@ def construct_checklist(checklist, arch):
 #   checklist.append(OptCheck('LKDTM',    'm', 'my', 'feature_test'))
 
 
-def print_opt(opt, with_results):
-    print('CONFIG_{:<38}|{:^13}|{:^10}|{:^20}'.format(opt.name, opt.expected, opt.decision, opt.reason), end='')
-    if with_results:
-        print('|   {}'.format(opt.result), end='')
-    print()
-
-
 def print_checklist(checklist, with_results):
     if json_mode:
         opts = []
@@ -489,22 +533,8 @@ def print_checklist(checklist, with_results):
 
     # table contents
     for opt in checklist:
-        if debug_mode and hasattr(opt, 'opts'):
-            print('    {:87}'.format('<<< ' + opt.__class__.__name__ + ' >>>'), end='')
-            if with_results:
-                print('|   {}'.format(opt.result), end='')
-            print()
-            for o in opt.opts:
-                if hasattr(o, 'ver_expected'):
-                    ver_req = 'kernel version >= ' + str(o.ver_expected[0]) + '.' + str(o.ver_expected[1])
-                    print('{:<91}'.format(ver_req), end='')
-                    if with_results:
-                        print('|   {}'.format(o.result), end='')
-                    print()
-                else:
-                    print_opt(o, with_results)
-        else:
-            print_opt(opt, with_results)
+        opt.table_print(with_results)
+        print()
         if debug_mode:
             print('-' * sep_line_len)
     print()
@@ -515,12 +545,12 @@ def perform_checks(checklist, parsed_options):
         if hasattr(opt, 'opts'):
             # prepare ComplexOptCheck
             for o in opt.opts:
-                if hasattr(o, 'name'):
+                if hasattr(o, 'state'):
                     o.state = parsed_options.get(o.name, None)
         else:
-            # prepare simple OptCheck
-            if not hasattr(opt, 'name'):
-                sys.exit('[!] ERROR: bad OptCheck {}'.format(vars(opt)))
+            # prepare simple check
+            if not hasattr(opt, 'state'):
+                sys.exit('[!] ERROR: bad simple check {}'.format(vars(opt)))
             opt.state = parsed_options.get(opt.name, None)
         opt.check()
 
@@ -575,7 +605,8 @@ def main():
 
     config_checklist = []
 
-    parser = ArgumentParser(description='Checks the hardening options in the Linux kernel config')
+    parser = ArgumentParser(prog='kconfig-hardened-check',
+                            description='Checks the hardening options in the Linux kernel config')
     parser.add_argument('-p', '--print', choices=supported_archs,
                         help='print hardening preferences for selected architecture')
     parser.add_argument('-c', '--config',
@@ -584,6 +615,7 @@ def main():
                         help='enable verbose debug mode')
     parser.add_argument('--json', action='store_true',
                         help='print results in JSON format')
+    parser.add_argument('--version', action='version', version='%(prog)s ' + __version__)
     args = parser.parse_args()
 
     if args.debug: