#!/usr/bin/env python # kernel-check -- Gentoo Kernel Security # Copyright 2009-2010 Gentoo Foundation # Distributed under the terms of the GNU General Public License v2 from portage.output import blue, bold, colorize, EOutput, darkgreen, teal try: from _emerge.userquery import userquery from _emerge.stdout_spinner import stdout_spinner as spinner except ImportError: from _emerge import userquery from _emerge import stdout_spinner as spinner import getopt import os import portage import sys import textwrap import lib.kernellib as lib def main(argv): 'Main function' for arg in argv: if lib.REGEX['argument'].match(arg): print_bug(arg) return try: opts, args = getopt.gnu_getopt(argv, 'dhk:nr:sv', ['debug', 'help', 'kernel=', 'nocolor', 'report=', 'sync', 'verbose']) except getopt.GetoptError, e: EOutput().eerror('Invalid argument: %s' % e) #TODO invalid bugid, cve return kernel = None arg_kernel = None for opt, arg in opts: if opt in ('-d', '--debug'): lib.DEBUG = True elif opt in ('-h', '--help'): usage() return elif opt in ('-k', '--kernel'): arg_kernel = arg kernel = lib.extract_version(arg) elif opt in ('-n', '--nocolor'): portage.output.nocolor() elif opt in ('-r', '--report'): EOutput().eerror('--report not yet implemented') return elif opt in ('-s', '--sync'): os.system('%s%s' % ('rsync -avz rsync://rbu.sh/gentoo-kernel ', '/usr/portage/metadata/kernel')) return elif opt in ('-v', '--verbose'): pass #TODO Implement uname = os.uname() if uname[0].lower() != 'linux': EOutput().eerror('This tool only works for linux kernels.') EOutput().eerror('Apparently you are using "%s".' % uname[0]) return if arg_kernel is not None: if kernel is None: EOutput().eerror('The kernel you specified could not be found.') EOutput().eerror('Your specification is "%s".' % arg_kernel) return else: kernel = lib.extract_version(uname[2]) if kernel is None: EOutput().eerror('Your kernel could not be identified.') return print(darkgreen('\nThese are the specifications of your kernel:')) arch = portage.settings['ARCH'] if not arch: kernel.arch = '' else: kernel.arch = arch nx_bit = lib.check_nx_bit() if nx_bit: kernel.arch += ' (nx-bit supported)' genpatch = lib.get_genpatch(kernel) if not genpatch: kernel.genpatch = None else: kernel.genpatch = genpatch information = { 'Kernel source' : kernel.source, 'Kernel version' : '%s-%s' % (kernel.version, kernel.revision), 'Kernel patches' : ('genpatch %s (%s)' % (kernel.genpatch.version, repr(kernel.genpatch)) if kernel.genpatch else ''), 'Architecture' : kernel.arch } configuration = lib.gather_configuration() print_items(information, 'Information') print_items(configuration, 'Configuration') print('\nDetermining vulnerabilities... '), evaluation = lib.eval_cve_files(lib.KERNELDIR, kernel, spinner()) if not evaluation: print("\b\b done!") EOutput().eerror('No kernel vulnerability files found!') return kernel_updates = lib.eval_kernel_updates(kernel, evaluation, spinner()) print("\b\b done!") if len(evaluation.affected) is not 0: print_summary(evaluation.affected, kernel_updates) print('\nTotal: %s vulnerabilities (%s), Average CVSS score: %.1f' % ( len(evaluation.affected), repr(evaluation), evaluation.avg_cvss)) if kernel_updates: print('Kernel updates:') for key in sorted(kernel_updates.keys()): print(' %s %s' % (teal('[%s]' % kernel_updates[key]), key)) prompt = "\nWould you like to upgrade your kernel?" if userquery(prompt, None) == 'No': print('\nQuitting.\n') else: print('Not implemented yet...') else: print('Total: 0 vulnerabilities, Average CVSS score: 0.0\n') print(bold('Your kernel is not affected by any known vulnerability!')) def print_items(category, header): 'Indents and prints items' screenwidth = 120 if portage.output.get_term_size()[1] < screenwidth: screenwidth = portage.output.get_term_size()[1] print('') EOutput().einfo(bold('%s:' % header)) for item in category.keys(): for i, string in enumerate(textwrap.wrap('%s' % category[item], (screenwidth - 23))): if i is 0: print('%s%s%s : %s' % (' ' * 6, darkgreen(item), ' ' * (14 - len(item)), string)) else: print('%s%s' % (' ' * 23, string)) def print_summary(vullist, kernel_updates): 'Prints the vulnerability summary' for item in vullist: if item.cves: for cve in item.cves: cvetype = str() if 'AV:L' in cve.vector: cvetype += colorize('BAD', 'local') if 'AV:A' in cve.vector or 'AV:N' in cve.vector: cvetype += colorize('BAD', 'network') if ('C:P' in cve.vector or 'C:C' in cve.vector) \ and ('I:P' in cve.vector or 'I:C' in cve.vector) \ and ('A:P' in cve.vector or 'A:C' in cve.vector): cvetype += '%s%s' % (' ', blue('-complete')) else: if 'C:P' in cve.vector or 'C:C' in cve.vector: cvetype += '%s%s' % (' ', blue('-confidentiality')) if 'I:P' in cve.vector or 'I:C' in cve.vector: cvetype += '%s%s' % (' ', blue('-integrity')) if 'A:P' in cve.vector or 'A:C' in cve.vector: cvetype += '%s%s' % (' ', blue('-availability')) #TODO add pending identifier if ' PENDING ' in cve.desc and '0000-00-00' == cve.published: cvetype = '%s%%' % colorize('WARN', 'pending') if 'GENERIC-MAP-NOMATCH' == cve.cve: cve.cve = 'CVE-0000-0000' cvetype = '%s%%' % colorize('WARN', 'nomatch') if cve.score == '10.0': cve.score = '10.' index = str() if repr(item.fixed) in kernel_updates: index = teal('[%s]' % kernel_updates[repr(item.fixed)]) print('[%s %26s] %s %s TYPE="%s" %s') % (darkgreen('bugid'), colorize('GOOD', item.bugid), darkgreen(cve.cve), blue('[%3s]' % cve.score), cvetype, index) def print_bug(bugid): 'Prints information about a particular bugid' if 'cve' in bugid.lower(): print_cve(bugid.upper()) return vul = lib.read_cve_file(lib.KERNELDIR, bugid) if vul is None: EOutput().eerror('Could not find bugid: %s' % bugid) return buginformation = { 'Status' : vul.status.capitalize(), 'Reporter' : vul.reporter, 'Reported' : vul.reported[:-11], 'Affected' : vul.affected, 'Architecture' : vul.arch.capitalize() } print_items(buginformation, 'Bugid %s' % bugid) for cve in vul.cves: print_cve(cve.cve) def print_cve(cveid): 'Prints information about a cve' cve = lib.Cve(cveid) vul = lib.find_cve(cveid, lib.KERNELDIR) if vul is None: EOutput().eerror('Could not find cve: %s' % cveid) return else: for item in vul.cves: if item.cve == cveid: cve = item cveinformation = { 'Published' : cve.published, 'Severity' : cve.severity, 'Score' : cve.score, 'Vector' : cve.vector, 'Description' : cve.desc, } #TODO print cve.refs print_items(cveinformation, cve.cve) def print_information(): 'Prints an information message' EOutput().einfo('To print more information about a vulnerability try:') EOutput().einfo(' $ %s [BUGID|CVE]' % sys.argv[0]) def usage(): 'Prints the usage screen' print('Usage: kernel-check [BUGID|CVE] [OPTION]...') print('Gentoo Kernel Security %s\n' % lib.VERSION) print(' -d, --debug display debugging information') print(' -h, --help display help information') print(' -k, --kernel specify a kernel') print(' -n, --nocolor disable colors') print(' -r, --report [file] create a security report') print(' -s, --sync receive the latest vulnerabilities') print(' -v, --verbose display additional information') if __name__ == '__main__': main(sys.argv[1:])