#!/usr/bin/python # masssigverify.py # Robin Hugh Johnson # This is a prototype mass signature checker for Gentoo Manifests # It needs to be converted to the Portage exec framework still. # The core of it is two threads # A. reads Manifests & passes them GPG # B. reads GPG results and correlates them with the input. # For the purposes of this demo, thread B also prints out the results it is # generating. # # Performance testing: # 1. Spread 40k signed manifest chunks over ~10k files. # 2. Place the 10k of files into tmpfs. # 3. Generate a listfile (because scanning is slow). # 4. Run this script on the listfile. # 5. On a 2.4Ghz Xeon, this app completes in 4 minutes for all 40k signatures. # # Questions: # Is Python's threading optimal? For best case, we need three processors # 1 - reading listfile/manifests # 2 - running GPG # 3 - parsing GPG output # If we concatenate ALL of the Manifest contents to a single file, and verify # that, processing time drops to 40 seconds. This implies that fopen+read is a # major slowdown. import subprocess import threading import select import sys # listfile is a file containing a list of other files that have signatures to # verify if(len(sys.argv) != 2): print 'Syntax: ./verifier.py LISTFILE' print 'Where LISTFILE is a file containing a list of files to verify.' sys.exit(1) listfile = file(sys.argv[1],'r') list = listfile.readlines() myargv = sys.argv[1:] results = {} #r, w = popen2.popen4('gpg --no-auto-check-trustdb --verify',bufsize=2**23) cmd = 'gpg --no-auto-check-trustdb --verify' p = subprocess.Popen([cmd], shell=True, bufsize=2**20, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, close_fds=True) (r, w) = (p.stdout, p.stdin) def feedManifest(): count = 0 for filename in list: filename = filename.strip() print 'FILENAME(%d): %s' % (count,filename) count = count + 1 f = file(filename,'r',4096) content = f.readlines() f.close() #print content if "-----BEGIN PGP SIGNED MESSAGE-----\n" in content: # push to GPG results[filename] = 'signed','untested' w.writelines(content) #w.flush() else: results[filename] = 'unsigned', w.close() def processoutput(): #sys.stderr.write('Processing'+"\n") print 'PROCESSING' resultcount = -1 while(1): l = r.readline() #print l if(len(l) == 0): break l = l[:-1] # remove \n #print 'RES:',resultcount,' LINE:"',l,'"' filename = list[resultcount].strip() if(l.find('gpg: Signature made') == 0): resultcount = resultcount + 1 continue elif(l.find('gpg: BAD signature') == 0): print 'EXTRA ',l print 'BAD ',filename results[filename] = 'signed','bad' elif(l.find('gpg: Good signature') == 0): print 'GOOD ',filename results[filename] = 'signed','good' else: print 'WEIRD ',l r.close() in_t=threading.Thread(target=feedManifest, args=(), kwargs={}) in_t.start() out_t=threading.Thread(target=processoutput, args=(), kwargs={}) out_t.start() out_t.join() # after you're doing with everything and want to block for it to complete #print r.readlines() #r.close() #w.close() print p.returncode ret = p.wait() print ret # vim: sts=4 ts=4 sw=4: