diff options
-rwxr-xr-x | scripts/rp-sqlite | 148 |
1 files changed, 110 insertions, 38 deletions
diff --git a/scripts/rp-sqlite b/scripts/rp-sqlite index 56f0c440..4926bb93 100755 --- a/scripts/rp-sqlite +++ b/scripts/rp-sqlite @@ -29,7 +29,10 @@ import os import sys +import yaml +import base64 import sqlite3 +import weakref import rpki.POW import rpki.x509 import rpki.resource_set @@ -43,13 +46,18 @@ sqlite3.register_converter("RangeVal", def main(): rpdb = RPDB() + rpdb.load() test(rpdb) rpdb.close() - def test(rpdb): fn2s = [None] + rpdb.fn2map.keys() + print + print "Testing YAML parsing" + parse_yaml(rpdb) + + print print "Testing range functions" for fn2 in fn2s: if fn2 is not None: @@ -84,11 +92,82 @@ def test(rpdb): print "Trying", expr for r in rpdb.find_by_resource_bag(rpki.resource_set.resource_bag.from_str(expr), fn2): print r, r.uris - + + +def parse_xki(s): + """ + Parse text form of an SKI or AKI. We accept two encodings: + colon-delimited hexadecimal, and URL-safe Base64. The former is + what OpenSSL prints in its text representation of SKI and AKI + extensions; the latter is the g(SKI) value that some RPKI CA engines + (including rpkid) use when constructing filenames. + + In either case, we check that the decoded result contains the right + number of octets to be a SHA-1 hash. + """ + + if ":" in s: + b = "".join(chr(int(c, 16)) for c in s.split(":")) + else: + b = base64.urlsafe_b64decode(s + ("=" * (4 - len(s) % 4))) + if len(b) != 20: + raise RuntimeError("Bad length for SHA1 xKI value: %r" % s) + return b + + +def parse_yaml(rpdb, fn = "rp-sqlite.yaml"): + yy = yaml.safe_load(open(fn, "r")) + for y in yy: + + ski = None + uri = None + obj = set() + + print + + if "ski" in y: + ski = parse_xki(y["ski"]) + obj.update(rpdb.find_by_ski(ski)) + if "uri" in y: + uri = y["uri"] + obj.update(rpdb.find_by_uri(uri)) + if len(obj) == 1: + obj = obj.pop() + else: + raise RuntimeError("Policy entry must name a unique object using SKI, URI, or both (%r, %r, %r)" % ( + ski, uri, obj)) + + print "URI:", uri + print "SKI:", " ".join("%02X" % ord(c) for c in ski), "(" + y["ski"] + ")" + + new_resources = old_resources = obj.get_3779resources() + + if "set" in y: + new_resources = rpki.resource_set.resource_bag.from_str(y["set"]) + + if "add" in y: + new_resources = new_resources | rpki.resource_set.resource_bag.from_str(y["add"]) + + if "sub" in y: + new_resources = new_resources - rpki.resource_set.resource_bag.from_str(y["sub"]) + + if new_resources == old_resources: + print "No resource change, skipping" + continue + + print "Old:", old_resources + print "New:", new_resources + print "Add:", new_resources - old_resources + print "Sub:", old_resources - new_resources + class RPDB(object): """ Relying party database. + + For now just wire in the database name and rcynic root, fix this + later if overall approach seems usable. Might even end up just + being an in-memory SQL database, who knows? """ fn2map = dict(cer = rpki.x509.X509, @@ -97,16 +176,7 @@ class RPDB(object): roa = rpki.x509.ROA, gbr = rpki.x509.Ghostbuster) - def __init__(self, - db_name = "rp-sqlite.db", - rcynic_root = os.path.expanduser("~/rpki/subvert-rpki.hactrn.net/trunk/" - "rcynic/rcynic-data/unauthenticated"), - delete_old_db = True, - spinner = 100): - - # For now just wire in the database name and rcynic root, fix this - # later if overall approach seems usable. Might even end up just - # being an in-memory SQL database, who knows? + def __init__(self, db_name = "rp-sqlite.db", delete_old_db = True): if delete_old_db: try: @@ -114,10 +184,17 @@ class RPDB(object): except: pass + exists = os.path.exists(db_name) + self.db = sqlite3.connect(db_name, detect_types = sqlite3.PARSE_DECLTYPES) self.db.text_factory = str - self.cur = self.db.cursor() + + self.cache = weakref.WeakValueDictionary() + + if exists: + return + self.cur.executescript(''' PRAGMA foreign_keys = on; @@ -152,6 +229,11 @@ class RPDB(object): CREATE INDEX range_index ON range(min, max); ''') + def load(self, + rcynic_root = os.path.expanduser("~/rpki/subvert-rpki.hactrn.net/trunk/" + "rcynic/rcynic-data/unauthenticated"), + spinner = 100): + nobj = 0 for root, dirs, files in os.walk(rcynic_root): @@ -224,25 +306,13 @@ class RPDB(object): def find_by_ski(self, ski, fn2 = None): - return self._find_results( - fn2, - """ - SELECT id, fn2, der - FROM object - WHERE ski = ? - """, - [buffer(ski)]) - + return self._find_results(fn2, "SELECT id, fn2, der FROM object WHERE ski = ?", [buffer(ski)]) def find_by_aki(self, aki, fn2 = None): - return self._find_results( - fn2, - """ - SELECT id, fn2, der - FROM object - WHERE aki = ? - """, - [buffer(aki)]) + return self._find_results(fn2, "SELECT id, fn2, der FROM object WHERE aki = ?", [buffer(aki)]) + + def find_by_uri(self, uri): + return self._find_results(None, "SELECT object.id, fn2, der FROM object, uri WHERE uri.uri = ? AND object.id = uri.id", [uri]) # It's easiest to understand overlap conditions by understanding @@ -262,8 +332,7 @@ class RPDB(object): return self._find_results( fn2, """ - SELECT object.id, fn2, der - FROM object, range + SELECT object.id, fn2, der FROM object, range WHERE ? <= max AND ? >= min AND object.id = range.id """, [range_min, range_max]) @@ -282,8 +351,7 @@ class RPDB(object): return self._find_results( fn2, """ - SELECT object.id, fn2, der - FROM object, range + SELECT object.id, fn2, der FROM object, range WHERE object.id = range.id AND (%s) """ % (" OR ".join(qset)), aset) @@ -299,10 +367,14 @@ class RPDB(object): self.cur.execute(query, args) selections = self.cur.fetchall() for rowid, fn2, der in selections: - obj = self.fn2map[fn2](DER = der) - self.cur.execute("SELECT uri FROM uri WHERE id = ?", (rowid,)) - obj.uris = [u[0] for u in self.cur.fetchall()] - obj.uri = obj.uris[0] if len(obj.uris) == 1 else None + if rowid in self.cache: + obj = self.cache[rowid] + else: + obj = self.fn2map[fn2](DER = der) + self.cur.execute("SELECT uri FROM uri WHERE id = ?", (rowid,)) + obj.uris = [u[0] for u in self.cur.fetchall()] + obj.uri = obj.uris[0] if len(obj.uris) == 1 else None + self.cache[rowid] = obj results.append(obj) return results |