From 8fb9669ae414f06e61dbe112704540e6b8ce55ca Mon Sep 17 00:00:00 2001 From: Ludwig Nussel Date: Tue, 16 Jun 2009 14:46:02 +0200 Subject: [PATCH] verify files using rpm bindings and keys supplied by buildservice The build service is not ready for that yet. Almost all projects that are not built but only imported to the build service have wrong keys. To enable the new code set builtin_signature_check in .oscrc. You may need to manually overwrite wrong _pubkey files in the packagecachedir with correct keys until the build service is fixed. --- osc/build.py | 12 ++++-- osc/checker.py | 100 +++++++++++++++++++++++++++++++++++++++++++++++++ osc/fetch.py | 71 +++++++++++++++++++++++++++++++++-- 3 files changed, 176 insertions(+), 7 deletions(-) create mode 100644 osc/checker.py diff --git a/osc/build.py b/osc/build.py index 811120bc..ede06175 100644 --- a/osc/build.py +++ b/osc/build.py @@ -63,6 +63,8 @@ class Buildinfo: root = tree.getroot() + self.apiurl = apiurl + if root.find('error') != None: sys.stderr.write('buildinfo is broken... it says:\n') error = root.find('error').text @@ -95,9 +97,13 @@ class Buildinfo: pass self.deps = [] + self.projects = {} + self.keys = [] for node in root.findall('bdep'): p = Pac(node, self.buildarch, self.pacsuffix, apiurl, localpkgs) + if p.project: + self.projects[p.project] = 1 self.deps.append(p) self.vminstall_list = [ dep.name for dep in self.deps if dep.vminstall ] @@ -621,13 +627,11 @@ def main(opts, argv): os.symlink(sffn, tffn) if bi.pacsuffix == 'rpm': - if config['build-type'] == "xen" or config['build-type'] == "kvm": - print 'Skipping verification of package signatures due to secure VM build' - elif opts.no_verify or opts.noinit: + if opts.no_verify or opts.noinit: print 'Skipping verification of package signatures' else: print 'Verifying integrity of cached packages' - verify_pacs([ i.fullfilename for i in bi.deps ]) + verify_pacs([ i.fullfilename for i in bi.deps ], bi.keys) elif bi.pacsuffix == 'deb': if config['build-type'] == "xen" or config['build-type'] == "kvm": print 'Skipping verification of package signatures due to secure VM build' diff --git a/osc/checker.py b/osc/checker.py new file mode 100644 index 00000000..1586310f --- /dev/null +++ b/osc/checker.py @@ -0,0 +1,100 @@ +#!/usr/bin/python + +from tempfile import mkdtemp +import os +from shutil import rmtree +import rpm +import base64 + +class KeyError(Exception): + def __init__(self, key, *args): + Exception.__init__(self) + self.args = args + self.key = key + def __str__(self): + return ''+self.key+' :'+' '.join(self.args) + +class Checker: + def __init__(self): + self.dbdir = mkdtemp(prefix='oscrpmdb') + self.imported = {} + rpm.addMacro('_dbpath', self.dbdir) + self.ts = rpm.TransactionSet() + self.ts.initDB() + self.ts.openDB() + self.ts.setVSFlags(0) + #self.ts.Debug(1) + + def readkeys(self, keys=[]): + rpm.addMacro('_dbpath', self.dbdir) + for key in keys: + self.readkey(key) + + rpm.delMacro("_dbpath") + +# python is an idiot +# def __del__(self): +# self.cleanup() + + def cleanup(self): + self.ts.closeDB() + rmtree(self.dbdir) + + def readkey(self, file): + if file in self.imported: + return + + fd = open(file, "r") + line = fd.readline() + if line and line[0:14] == "-----BEGIN PGP": + line = fd.readline() + while line and line != "\n": + line = fd.readline() + if not line: + raise KeyError(file, "not a pgp public key") + else: + raise KeyError(file, "not a pgp public key") + + key = '' + line = fd.readline() + while line: + if line[0:12] == "-----END PGP": + break + line = line.rstrip() + key += line + line = fd.readline() + fd.close() + if not line or line[0:12] != "-----END PGP": + raise KeyError(file, "not a pgp public key") + + bkey = base64.b64decode(key) + + r = self.ts.pgpImportPubkey(bkey) + if r != 0: + raise KeyError(file, "failed to import pubkey") + self.imported[file] = 1 + + def check(self, pkg): + fd = os.open(pkg, os.O_RDONLY) + hdr = self.ts.hdrFromFdno(fd) + os.close(fd) + +if __name__ == "__main__": + import sys + keyfiles = [] + pkgs = [] + for arg in sys.argv[1:]: + if arg[-4:] == '.rpm': + pkgs.append(arg) + else: + keyfiles.append(arg) + + checker = Checker() + try: + checker.readkeys(keyfiles) + for pkg in pkgs: + checker.check(pkg) + except Exception, e: + checker.cleanup() + raise e + diff --git a/osc/fetch.py b/osc/fetch.py index 9a951024..6f7a3827 100644 --- a/osc/fetch.py +++ b/osc/fetch.py @@ -9,6 +9,7 @@ from urlgrabber.grabber import URLGrabber, URLGrabError from urlgrabber.mirror import MirrorGroup from core import makeurl from util import packagequery, cpio +import conf import tempfile try: from meter import TextMeter @@ -37,6 +38,7 @@ class Fetcher: self.progress_obj = None + self.nopac = False self.cachedir = cachedir self.urllist = urllist self.http_debug = http_debug @@ -63,8 +65,9 @@ class Fetcher: #log(0, '%s: %s' % (errobj.url, str(errobj.exception))) #log(0, 'Trying other mirror.') - print 'Trying openSUSE Build Service server for %s (%s), since it is not on %s.' \ - % (self.curpac, self.curpac.project, errobj.url.split('/')[2]) + if not self.nopac: + print 'Trying openSUSE Build Service server for %s (%s), since it is not on %s.' \ + % (self.curpac, self.curpac.project, errobj.url.split('/')[2]) raise errobj.exception @@ -197,7 +200,27 @@ class Fetcher: if os.path.exists(tmpfile): os.unlink(tmpfile) -def verify_pacs(pac_list): + self.nopac = True + for i in buildinfo.projects: + dest = "%s/%s/_pubkey" % (self.cachedir, i) + if os.path.exists(dest): + buildinfo.keys.append(dest) + else: + url = "%s/source/%s/_pubkey" % (buildinfo.apiurl, i) + try: + self.gr.urlgrab(url, dest, text="key for %s" % i) + buildinfo.keys.append(dest) + except KeyboardInterrupt: + print 'Cancelled by user (ctrl-c)' + print 'Exiting.' + if os.path.exists(dest): + os.unlink(dest) + sys.exit(0) + except URLGrabError, e: + print "can't fetch key for %s: %s" %(i, e.strerror) + self.nopac = False + +def verify_pacs_old(pac_list): """Take a list of rpm filenames and run rpm -K on them. In case of failure, exit. @@ -264,3 +287,45 @@ def verify_pacs(pac_list): sys.exit(1) +def verify_pacs(pac_list, key_list): + """Take a list of rpm filenames and verify their signatures. + + In case of failure, exit. + """ + + # XXX: remove if new code stable + if not conf.config.get('builtin_signature_check', False): + return verify_pacs_old(pac_list) + + if not pac_list: + return + + if not key_list: + print "no keys" + sys.exit(1) + return + + print key_list + + import checker + failed = False + checker = checker.Checker() + try: + checker.readkeys(key_list) + for pkg in pac_list: + try: + checker.check(pkg) + except Exception, e: + failed = True + print pkg, ':', e + except Exception, e: + checker.cleanup() + sys.exit(1) + + if failed: + checker.cleanup() + sys.exit(1) + + checker.cleanup() + +# vim: sw=4 et