From 7e8cadf94037c337f5ef13849f8122898fbb989a Mon Sep 17 00:00:00 2001 From: Bjoern Tropf Date: Thu, 3 Dec 2009 18:56:20 +0100 Subject: Implement kernel-check [BUGID|CVE] Implement -s, --sync Fix serveral bugs Clean up lots of old code --- TODO | 2 + bin/kernel-check | 2 +- pym/kernelcheck/kernelcheck.py | 233 +++++++++++++++------------------------ pym/kernelcheck/lib/kernellib.py | 57 +++++++++- setup.py | 6 +- tools/cron.py | 2 +- 6 files changed, 151 insertions(+), 151 deletions(-) diff --git a/TODO b/TODO index 9c61fcc..5b6db82 100644 --- a/TODO +++ b/TODO @@ -13,3 +13,5 @@ - Implement DTD for vulnerability files - Move arch into whiteboard e.g. {x86, amd64} - Explicitly mention the CVSS score e.g. (CVSS-5.6) +- Implement sync properly +- Sort print_items diff --git a/bin/kernel-check b/bin/kernel-check index 4184132..cf8d8d0 100644 --- a/bin/kernel-check +++ b/bin/kernel-check @@ -1,5 +1,5 @@ #!/usr/bin/env python -# kernel-check -- Kernel security information +# kernel-check -- Gentoo Kernel Security # Copyright 2009-2009 Gentoo Foundation # Distributed under the terms of the GNU General Public License v2 diff --git a/pym/kernelcheck/kernelcheck.py b/pym/kernelcheck/kernelcheck.py index ae4f490..1cb359c 100755 --- a/pym/kernelcheck/kernelcheck.py +++ b/pym/kernelcheck/kernelcheck.py @@ -1,14 +1,16 @@ #!/usr/bin/env python -# kernel-check -- Kernel security information +# kernel-check -- Gentoo Kernel Security # Copyright 2009-2009 Gentoo Foundation # Distributed under the terms of the GNU General Public License v2 from portage.output import blue, bold, colorize, EOutput, darkgreen #FIXME -#from _emerge.stdout_spinner import stdout_spinner #TODO + try: from _emerge.userquery import userquery + from _emerge.stdout_spinner import stdout_spinner except ImportError: from _emerge import userquery #FIXME proper checking without except + from _emerge import stdout_spinner import getopt import portage @@ -21,16 +23,15 @@ import lib.kernellib as lib info = EOutput().einfo #FIXME warn = EOutput().ewarn error = EOutput().eerror -#spin = stdout_spinner() -term = portage.output.get_term_size() +spin = stdout_spinner() def main(argv): 'Main function' try: - opts, args = getopt.getopt(argv, 'dhnr:s:v', - ['debug', 'help', 'nocolor', 'report=', 'show=', 'verbose']) - except getopt.GetoptError: + opts, args = getopt.getopt(argv, 'dhnr:sv', + ['debug', 'help', 'nocolor', 'report=', 'sync', 'verbose']) + except getopt.GetoptError, e: usage() return @@ -45,14 +46,23 @@ def main(argv): elif opt in ('-r', '--report'): error('--report not yet implemented') return - elif opt in ('-s', '--show'): - print_bug(arg) + 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'): lib.VERBOSE = True + for arg in argv: + if lib.REGEX['argument'].match(arg): + if 'cve' in arg.lower(): + vul = lib.find_cve(arg, lib.DIR['out']) + print_bug(vul.bugid) + else: + print_bug(arg) + return + information = dict() - configuration = dict() print '' print darkgreen('These are the specifications of your kernel:') @@ -69,49 +79,27 @@ def main(argv): error('No kernel information found!') return - information['Kernel source'] = kernel.source - information['Kernel version'] = '%s-%s' % (kernel.version, kernel.revision) + arch = portage.settings['ARCH'] + if not arch: + arch = '?' #FIXME kernel.genpatch = lib.get_genpatch(lib.PORTDIR, kernel) - - if kernel.genpatch is not None: #FIXME - information['Kernel patches'] = '%s %s (%s)' % ('genpatch', - kernel.genpatch.version, - repr(kernel.genpatch)) - - elif kernel.source == 'gentoo': - warn('No genpatch information found!') #FIXME - - arch = portage.settings['ARCH'] - if arch: - information['Architecture'] = arch + if not kernel.genpatch: + genpatch = '' else: - error('No architecture found!') #FIXME - return - - info(bold('Information:')) - print_items(information) + genpatch = '%s %s (%s)' % ('genpatch', kernel.genpatch.version, + repr(kernel.genpatch)) - min_addr = str() #TODO move to kernellib - try: - min_addr = open('/proc/sys/vm/mmap_min_addr').read().strip() - except: - min_addr = '?' - - configuration['Mmap_min_addr'] = min_addr - - modules = str() #TODO move to kernellib - try: - for line in open('/proc/modules').readlines(): - modules += '%s ' % line.split(' ')[0] - except: - modules = '?' - - configuration['Loaded modules'] = modules + information = { + 'Kernel source' : kernel.source, + 'Kernel version' : '%s-%s' % (kernel.version, kernel.revision), + 'Kernel patches' : genpatch, + 'Architecture' : arch + } + print_items(information, 'Information') print '' - info(bold('Configuration:')) - print_items(configuration) + print_items(lib.gather_configuration(), 'Configuration') print '\nDetermining vulnerabilities... done!' #TODO #spin print '' @@ -123,42 +111,8 @@ def main(argv): print_summary(kernel_eval.affected) - low = int() #TODO move to kernellib - medium = int() - high = int() - cvss_score = float() - cve_amount = int() - - for item in kernel_eval.affected: - for cve in item.cves: - if cve.severity == 'Low': - low += 1 - if cve.severity == 'Medium': - medium += 1 - if cve.severity == 'High': - high += 1 - if len(kernel_eval.affected) is not 0: - for cve in item.cves: - cve_amount += 1 - cvss_score += float(cve.score) - - cvss_score = cvss_score / cve_amount - - severity_eval = str() - - if high is not 0: - severity_eval += '%s high' % high - if medium is not 0: - if high is not 0: - severity_eval += ', ' - severity_eval += '%s medium' % medium - if low is not 0: - if high is not 0 or medium is not 0: - severity_eval += ', ' - severity_eval += '%s low' % low - print 'Total: %s vulnerabilities (%s), Average CVSS score: %.1f' % ( - len(kernel_eval.affected), severity_eval, cvss_score) + len(kernel_eval.affected), repr(kernel_eval), kernel_eval.avg_cvss) print '' @@ -171,15 +125,20 @@ def main(argv): else: print 'Not implemented yet ;)' -def print_items(category): +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] + + info(bold('%s%s' % (header, ':'))) for item in category.keys(): for i, string in enumerate(textwrap.wrap('%s' % category[item], - (term[1] - 23))): + (screenwidth - 23))): if i is 0: - print ' %s%s : %s' % (darkgreen(item), - ' ' * (14 - len(item)), string) + print '%s%s%s : %s' % (' ' * 6, darkgreen(item), + ' ' * (14 - len(item)), string) else: print '%s%s' % (' ' * 23, string) @@ -194,7 +153,6 @@ def print_summary(vullist): whiteboard += '[' + str(interval) + '] ' if item.cves: - for cve in item.cves: severity = 'BAD' if cve.severity == 'Low': @@ -203,32 +161,30 @@ def print_summary(vullist): severity = 'WARN' cve_text = str() - cve_area = str() if 'AV:L' in cve.vector: - cve_area += colorize('WARN', 'local') + cve_text += colorize('WARN', 'local') if 'AV:A' in cve.vector or 'AV:N' in cve.vector: - cve_area += colorize('BAD', 'network') - - if 'C:P' in cve.vector or 'C:C' in cve.vector: - cve_text += ' -confidentiality' - - if 'I:P' in cve.vector or 'I:C' in cve.vector: - cve_text += ' -integrity' - - if 'A:P' in cve.vector or 'A:C' in cve.vector: - cve_text += ' -availability' + cve_text += 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): - cve_text = ' -complete' + cve_text += '%s%s' % (' ', blue('-complete')) + else: + if 'C:P' in cve.vector or 'C:C' in cve.vector: + cve_text += '%s%s' % (' ', blue('-confidentiality')) + + if 'I:P' in cve.vector or 'I:C' in cve.vector: + cve_text += '%s%s' % (' ', blue('-integrity')) - first_text = textwrap.wrap(cve.desc, term[1] - 44)[0] - print '[%s %26s] %s CVSS="%s %s%s"' % (darkgreen('bugid'), + if 'A:P' in cve.vector or 'A:C' in cve.vector: + cve_text += '%s%s' % (' ', blue('-availability')) + + print '[%s %26s] %s CVSS="%s %s"' % (darkgreen('bugid'), colorize('GOOD', item.bugid), darkgreen(cve.cve), - colorize(severity, cve.score), cve_area, blue(cve_text)) + colorize(severity, cve.score), cve_text) print '' @@ -240,35 +196,32 @@ def print_bug(bugid): 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' + buginformation = { + 'Status' : vul.status.capitalize(), + 'Reporter' : vul.reporter, + 'Reported' : vul.reported[:-11], + 'Affected' : vul.affected, + 'Architecture' : vul.arch.capitalize() + } - 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() + print '' + print_items(buginformation, '%s%s' % ('Bugid ', bugid)) 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']) + cve = lib.Cve(cveid) + vul = lib.find_cve(cveid, lib.DIR['out']) #FIXME if vul is None: error('Could not find cve: %s' % cveid) return @@ -277,47 +230,37 @@ def print_cve(cveid): 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)) + cveinformation = { + 'Published' : cve.published, + 'Severity' : cve.severity, + 'Score' : cve.score, + 'Vector' : cve.vector, + 'Description' : cve.desc, + } #TODO print cve.refs - for i, string in enumerate(textwrap.wrap('"%s"' % cve.desc , - (term[1] - 15))): - 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 an early version of kernel-check.' % - colorize('BAD', 'IMPORTANT')) - error('Please note that this tool might not operate as expected.') + print '' + print_items(cveinformation, cve.cve) def print_information(): 'Prints an information message' info('To print more information about a vulnerability try:') - info(' $ %s -s [bugid|cve]' % sys.argv[0]) + info(' $ %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' + 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 ' -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__': diff --git a/pym/kernelcheck/lib/kernellib.py b/pym/kernelcheck/lib/kernellib.py index 515511b..6aa3270 100644 --- a/pym/kernelcheck/lib/kernellib.py +++ b/pym/kernelcheck/lib/kernellib.py @@ -1,5 +1,5 @@ #!/usr/bin/env python -# kernel-check -- Kernel security information +# kernel-check -- Gentoo Kernel Security # Copyright 2009-2009 Gentoo Foundation # Distributed under the terms of the GNU General Public License v2 @@ -24,6 +24,7 @@ ARCHES = [ ] REGEX = { + 'argument' : re.compile(r'(CVE-(\d{4})-(\d{4}))|(\d{5,7})'), 'gp_version' : re.compile(r'(?<=K_GENPATCHES_VER\=\").+(?=\")'), 'gp_want' : re.compile(r'(?<=K_WANT_GENPATCHES\=\").+(?=\")'), 'k_version' : re.compile(r'^((?:\d{1,2}\.){0,4}\d{1,2})(-.*)?$'), @@ -74,11 +75,30 @@ class Evaluation: arch = int() affected = list() unaffected = list() + avg_cvss = float() + low = int() + medium = int() + high = int() def __init__(self): self.affected = list() self.unaffected = list() + def __repr__(self): + severity = str() + if self.high is not 0: + severity += '%s high' % self.high + if self.medium is not 0: + if self.high is not 0: + severity += ', ' + severity += '%s medium' % self.medium + if self.low is not 0: + if self.high is not 0 or self.medium is not 0: + severity += ', ' + severity += '%s low' % self.low + + return severity + class Comparison: #TODO Check if deprecated """Comparison class @@ -418,6 +438,8 @@ def eval_cve_files(directory, kernel, arch, spin=None): if not files: return None + cve_amount = int() + cvss_score = int() evaluation = Evaluation() for item in files: @@ -435,9 +457,20 @@ def eval_cve_files(directory, kernel, arch, spin=None): if is_affected(item.affected, kernel, item): evaluation.affected.append(item) + for cve in item.cves: + if cve.severity == 'Low': + evaluation.low += 1 + if cve.severity == 'Medium': + evaluation.medium += 1 + if cve.severity == 'High': + evaluation.high += 1 + cve_amount += 1 + cvss_score += float(cve.score) else: evaluation.unaffected.append(item) + evaluation.avg_cvss = cvss_score / cve_amount + return evaluation #TODO Remove item @@ -614,3 +647,25 @@ def all_version(source): return versions +def gather_configuration(): + "" + + config = dict() + mmap_min_addr = str() + modules = str() + + try: + mmap_min_addr = open('/proc/sys/vm/mmap_min_addr').read().strip() + except: + mmap_min_addr = '?' + config['Mmap_min_addr'] = mmap_min_addr + + try: + for line in open('/proc/modules').readlines(): + modules += '%s ' % line.split(' ')[0] + except: + modules = '?' + config['Loaded modules'] = modules + + return config + diff --git a/setup.py b/setup.py index 58ff73a..67c1e6c 100644 --- a/setup.py +++ b/setup.py @@ -1,5 +1,5 @@ #!/usr/bin/env python -# kernel-check -- Kernel security information +# kernel-check -- Gentoo Kernel Security # Copyright 2009-2009 Gentoo Foundation # Distributed under the terms of the GNU General Public License v2 @@ -8,9 +8,9 @@ from distutils.core import setup __version__ = open("VERSION").read().strip() setup( - name='Kernel-check', + name='kernel-check', version=__version__, - description='Gentoo kernel tracking tool', + description='Gentoo Kernel Security', author='Bjoern Tropf', author_email='asym@gentoo.org', url='http://dev.gentoo.org/~asym/guide.xml', diff --git a/tools/cron.py b/tools/cron.py index 062da17..60fa725 100755 --- a/tools/cron.py +++ b/tools/cron.py @@ -1,5 +1,5 @@ #!/usr/bin/env python -# kernel-check -- Kernel security information +# kernel-check -- Gentoo Kernel Security # Copyright 2009-2009 Gentoo Foundation # Distributed under the terms of the GNU General Public License v2 -- cgit v1.2.3-65-gdbad