#!/usr/bin/env python # kernel-check -- Kernel security information # Copyright 2009-2009 Gentoo Foundation # Distributed under the terms of the GNU General Public License v2 import getopt import portage import sys import textwrap import os import kernellib as lib info = portage.output.EOutput().einfo warn = portage.output.EOutput().ewarn error = portage.output.EOutput().eerror color = portage.output.colorize term = portage.output.get_term_size() def main(argv): 'Main function' try: opts, args = getopt.getopt(argv, 'dhnr:s:v', ['debug', 'help', 'nocolor', 'report=', 'show=', 'verbose']) except getopt.GetoptError: usage() return for opt, arg in opts: if opt in ('-d', '--debug'): lib.DEBUG = True elif opt in ('-h', '--help'): usage() return elif opt in ('-n', '--nocolor'): portage.output.nocolor() elif opt in ('-r', '--report'): error('--report not yet implemented') return elif opt in ('-s', '--show'): print_bug(arg) return elif opt in ('-v', '--verbose'): lib.VERBOSE = True print '>>> Gathering system information' uname = os.uname() if uname[0] != 'Linux': error('This tool currently only works for Linux kernels.') error('Apparantly you are using "%s".' % uname[0]) sys.exit() kernel = lib.extract_version(uname[2]) if kernel is None: error('No kernel information found!') return info('Kernel version : %s' % (color('GOOD', '%s-%s' % (kernel.version, kernel.revision)))) info('Kernel source : %s' % color('GOOD', kernel.source)) kernel.genpatch = lib.get_genpatch(lib.read_genpatch_file(lib.DIR['out']), kernel) if kernel.genpatch is not None: info('Gen(too)patch : %s' % color('GOOD', '%s %s' % (kernel.genpatch.version, repr(kernel.genpatch)))) elif kernel.source == 'gentoo': warn('No genpatch information found!') arch = portage.settings['ARCH'] if arch: info('Architecture : %s' % color('GOOD', arch)) else: error('No architecture found!') return print '\n>>> Reading all kernel vulnerabilities' supported = list() for item in lib.SUPPORTED: best = (lib.all_version(item)) if best and best is not None: for i in best: if item == 'gentoo': i.genpatch = lib.get_genpatch(lib.read_genpatch_file( lib.DIR['out']), i) supported.append(i) kernel_eval = lib.eval_cve_files(lib.DIR['out'], kernel, arch) if not kernel_eval: error('No kernel vulnerability files found!') return info('%s vulnerabilities read.' % color('GOOD', str(kernel_eval.read))) info('%s apply to this architecture.' % color('GOOD', str(kernel_eval.arch))) info('%s do not affect this kernel.' % color('GOOD', str(len(kernel_eval.unaffected)))) if (len(kernel_eval.affected) is 0): info('Your kernel is not affected by any known vulnerabilites!') return error('%s affect this kernel: ' % color('BAD', str(len(kernel_eval.affected)))) print_summary(kernel_eval.affected) info('You have the following choices: ') print '' info('[1] Recommended') info('Keep your current kernel: %s' % color('BRACKET', 'sys-kernel/%s-sources-%s-%s' % ( kernel.source, kernel.version, kernel.revision))) print '' choice = 1 for item in supported: supported_eval = lib.eval_cve_files(lib.DIR['out'], item, arch) if not supported_eval or kernel == item: continue else: comparison = lib.compare_evaluation(kernel_eval, supported_eval) if comparison is not None: choice += 1; score = 0 for fix in comparison.fixed: for cve in fix.cves: score += float(cve.score) for new in comparison.new: for cve in new.cves: score -= float(cve.score) info('[%s] Recommended: (Score %s)' % (str(choice), score)) info('Upgrade to this kernel: %s' % color('BRACKET', 'sys-kernel/%s-sources-%s-%s' % ( item.source, item.version, item.revision))) info('which fixes %s of %s vulnerabilities and introduces %s' \ ' new' % (color('GOOD', str(len(comparison.fixed))), color('BAD', str(len(kernel_eval.affected))), color('BAD', str(len(comparison.new))))) print '' print_information() print_beta() def print_summary(vullist): 'Prints the vulnerability summary' for item in vullist: print '' whiteboard = str() for interval in item.affected: whiteboard += '[' + str(interval) + '] ' if len(item.cves) is 0: #TODO Explicit cve score instead of 0.0 print '\nBugid %s %-32s %s %s\n"%s..."' % (item.bugid, color('GOOD', 'Low' + ' (' + str(0.0) + ')'), lib.NOCVE, whiteboard, lib.NOCVEDESC[:term[1]-6]) for cve in item.cves: severity = 'BAD' if cve.severity == 'Low': severity = 'GOOD' elif cve.severity == 'Medium': severity = 'WARN' print '\nBugid %s %-32s %s %s\n"%s..."' % (item.bugid, color(severity, cve.severity + ' (' + cve.score + ')'), cve.cve, whiteboard, cve.desc[:term[1]-6]) print '\n' def print_bug(bugid): 'Prints information about a particular bugid' if 'cve' in bugid.lower(): print_cve(bugid.upper()) return whiteboard = str() cves = str() vul = lib.read_cve_file(lib.DIR['out'], bugid) if vul is None: error('Could not find bugid: %s' % bugid) return for i, interval in enumerate(vul.affected): if i is not 0: whiteboard += ' ' * 14 whiteboard += '[' + str(interval) + ']\n' info('Bugid : %s - %s' % (vul.bugid, vul.status.capitalize())) info('Reported : %s - %s' % (vul.reporter, vul.reported[:-5])) info('Affected : %s' % whiteboard[:-1]) #TODO arch = str() for cve in vul.cves: print '' print_cve(cve.cve) def print_cve(cveid): 'Prints information about a cve' cve = lib.Cve('cveid') vul = lib.find_cve(cveid, lib.DIR['out']) if vul is None: error('Could not find cve: %s' % cveid) return else: for item in vul.cves: if item.cve == cveid: cve = item info('Cve : %s - %s - Bugid %s' % (cve.cve, cve.published, vul.bugid)) info('Severity : %s %s - %s' % (cve.severity, cve.score, cve.vector)) #TODO print cve.refs for i, string in enumerate(textwrap.wrap('"%s"' % cve.desc , (term[1] - 14))): if i is 0: info('Desc : %s' % string) else: print ' ' * 15 + string return def print_beta(): 'Prints a beta warning message' print('') error('%s You are using a very early version of kernel-check.' % color('BAD', 'IMPORTANT')) error('Please note that this tool might not operate as expected.') error('Moreover the given information are most likely incorrect.') def print_information(): 'Prints an information message' info('To print more information about a vulnerability try:') info('') info(' $ %s -s [bugid|cve]' % sys.argv[0]) def usage(): 'Prints the usage screen' print 'Usage: %s [OPTION]...' % sys.argv[0][:-3] print 'Kernel security information %s\r\n' % lib.VERSION print ' -d, --debug display debugging information' print ' -h, --help display help information' print ' -n, --nocolor disable colors' print ' -r, --report [file] create a security report' print ' -s, --show [bugid|cve] display information about a bug or cve' print ' -v, --verbose display additional information' if __name__ == '__main__': main(sys.argv[1:])