From 1bf24010d5886b39eff2add906602db4819625bb Mon Sep 17 00:00:00 2001 From: Jan Dittberner Date: Wed, 21 Jan 2009 14:39:29 +0100 Subject: [PATCH] add tools for handling keyring data - add model/keyringanalyzer.py inspired by Debian qa's carnivore's extract_data - add model/keyfinder.py using the database built by model/keyringanalyzer.py - store the keyring search directory in model/ddportfolio.ini --- ddportfolioservice/model/ddportfolio.ini | 3 + ddportfolioservice/model/keyfinder.py | 48 +++++++++ ddportfolioservice/model/keyringanalyzer.py | 109 ++++++++++++++++++++ 3 files changed, 160 insertions(+) create mode 100644 ddportfolioservice/model/keyfinder.py create mode 100644 ddportfolioservice/model/keyringanalyzer.py diff --git a/ddportfolioservice/model/ddportfolio.ini b/ddportfolioservice/model/ddportfolio.ini index 37c6d89..74ec9ac 100644 --- a/ddportfolioservice/model/ddportfolio.ini +++ b/ddportfolioservice/model/ddportfolio.ini @@ -1,3 +1,6 @@ +[DEFAULT] +keyring.dir=/usr/share/keyrings + [overview] urls=ddpo,alladdresses ddpo.pattern=http://qa.debian.org/developer.php?login=%(email)s diff --git a/ddportfolioservice/model/keyfinder.py b/ddportfolioservice/model/keyfinder.py new file mode 100644 index 0000000..f19dfff --- /dev/null +++ b/ddportfolioservice/model/keyfinder.py @@ -0,0 +1,48 @@ +# -*- python -*- +# -*- coding: utf-8 -*- +""" +This module provides tools for finding PGP key information from a +given keyring. +""" + +db = None + +def _get_keyring_cache(): + if db = None: + import anydbm + import pkg_resources + import os.path + filename = pkg_resources.resource_filename(__name__, + 'keyringcache') + assert os.path.exists(filename) and os.path.isfile(filename) + db = anydbm.open(filename, 'r') + return db + +def _get_cached(cachekey): + cache = _get_keyring_cache() + if cachekey in cache: + return cache[cachekey].split(':') + return None + +def getFingerprintByEmail(email): + """Gets the fingerprints associated with the given email address + if available.""" + return _get_cached('fpr:email:%s' % email) + + +def getRealnameByEmail(email): + """Gets the real names associated with the given email address if + available.""" + return _get_cached('name:email:%s' % email) + + +def getLoginByEmail(email): + """Gets the logins associated with the given email address if + available.""" + return _get_cached('login:email:%s' % email) + + +def getLoginByFingerprint(fpr): + """Gets the login associated with the given fingerprint if + available.""" + return _get_cached('login:fpr:%s' % fpr) diff --git a/ddportfolioservice/model/keyringanalyzer.py b/ddportfolioservice/model/keyringanalyzer.py new file mode 100644 index 0000000..6691a6a --- /dev/null +++ b/ddportfolioservice/model/keyringanalyzer.py @@ -0,0 +1,109 @@ +# -*- python -*- +# -*- coding: utf-8 -*- +""" +This is a tool that analyzes GPG and PGP keyrings and stores the +retrieved data in a file database. The tool was inspired by Debian +qa's carnivore. +""" + +import anydbm +import pkg_resources +import glob +import ConfigParser +import os + + +def _get_keyrings(): + """Gets the available keyring files from the keyring directory + configured in ddportfolio.ini.""" + my_config = ConfigParser.ConfigParser() + my_config.readfp(pkg_resources.resource_stream(__name__, 'ddportfolio.ini')) + keyrings = glob.glob(my_config.get('DEFAULT', 'keyring.dir') + + '/*.gpg') + keyrings.extend(glob.glob(my_config.get('DEFAULT', 'keyring.dir') + + '/*.pgp')) + keyrings.sort() + return keyrings + + +def _parse_uid(uid): + """Parse a uid of the form 'Real Name ' into + email and realname parts.""" + uid = uid.strip() + # First, strip comment + s = uid.find('(') + e = uid.find(')') + if s >= 0 and e >= 0: + uid = uid[:s] + uid[e+1:] + s = uid.find('<') + e = uid.find('>') + email = None + if s >= 0 and e >= 0: + email = uid[s+1:e] + uid = uid[:s] + uid[e+1:] + uid = uid.strip() + if not email and uid.find('@') >= 0: + email, uid = uid, email + return (uid, email) + +resultdict = {} + +def _get_canonical(key): + if not key in resultdict: + resultdict[key] = [] + return key + + +def _add_to_result(key, newvalue): + thekey = _get_canonical(key) + if newvalue not in resultdict[thekey]: + resultdict[thekey].append(newvalue) + + +def process_keyrings(): + """Process the keyrings and store the extracted data in an anydbm + file.""" + for keyring in _get_keyrings(): + contents = os.popen("gpg --no-default-keyring \ + --no-expensive-trust-checks \ + --keyring %s --list-keys \ + --with-colons --fingerprint" % (keyring)) + fpr = None + entry = None + lastpub = None + for line in contents.readlines(): + items = line.split(':') + uid = None + if items[0] == 'pub': + fpr = entry = None + lastpub = items[9].strip() + continue + elif items[0] == 'fpr': + fpr = items[9].strip() + uid = lastpub + elif items[0] == 'uid': + uid = items[9].strip() + else: + continue + # Do stuff with 'uid' + uid, email = _parse_uid(uid) + if email: + if email.endswith('@debian.org'): + login = email[0:-len('@debian.org')] + _add_to_result('login:email:%s' % email, login) + _add_to_result('login:fpr:%s' % fpr, login) + _add_to_result('email:fpr:%s' % fpr, email) + if uid: + _add_to_result('name:fpr:%s' % fpr, uid) + if email: + _add_to_result('name:email:%s' % email, uid) + contents.close() + db = anydbm.open(pkg_resources.resource_filename(__name__, + 'keyringcache'), 'c') + for key in resultdict: + db[key] = ":".join(resultdict[key]) + db.close() + + +if __name__ == '__main__': + process_keyrings()