diff options
-rw-r--r-- | scripts/README | 24 | ||||
-rw-r--r-- | scripts/biz-certs/Bob-CA.srl | 2 | ||||
-rw-r--r-- | scripts/manifests.py | 22 | ||||
-rw-r--r-- | scripts/rpki/left_right.py | 30 | ||||
-rw-r--r-- | scripts/rpki/sql.py | 28 | ||||
-rw-r--r-- | scripts/rpki/up_down.py | 4 | ||||
-rw-r--r-- | scripts/rpki/x509.py | 27 | ||||
-rwxr-xr-x | scripts/rpkid.py | 7 | ||||
-rw-r--r-- | scripts/testroot.sh | 2 |
9 files changed, 94 insertions, 52 deletions
diff --git a/scripts/README b/scripts/README index b0d17fa4..f43ac9b8 100644 --- a/scripts/README +++ b/scripts/README @@ -97,16 +97,9 @@ Current TO DO list: - Publication protocol and implementation thereof. Defer until core functionality in the main engine is done. - As an interim measure, hack some kind of stub publication (not real - protocol yet, just dump to local filesystem so can see outputs and - maybe rcynic against them); this is a stop-gap to let me concentrate - on the main engine and defer work on the publication protocol and - engine. + As an interim measure, I hacked up a local filesystem publication kludge. -- Publication hooks everywhere - need not wait for protocol, can just - log what would happen for now, or write to local file store (perhaps - even in a form that we can use with rcynic as a relying party). - Hooks for this go into: + Need publication hooks for: - Cert publication @@ -116,10 +109,6 @@ Current TO DO list: - Withdrawal of any of the above - Consolidate generation of filenames and URIs into methods, duh. - - (tags-search "\\.\\(publish\\|withdraw\\)(\\|\\+ \"\\.") - - Logging subsystem, including syslog support. - Child batch processing loop, eg, regeneration or removal of expired @@ -128,14 +117,7 @@ Current TO DO list: everything that might need to be done. Figuring out whether to regenerate or remove expired certs requires - some of the same data as CRL generation. - - - Code to clean up expired certs - - - Code to revoke certs -- need to sort out when we do this - automatically vs waiting for explicit revoke PDU from child - - - Code to generate CRLs + looking in the IRDB. - Subsetting (req_* attributes in up-down protocol) diff --git a/scripts/biz-certs/Bob-CA.srl b/scripts/biz-certs/Bob-CA.srl index de578a4d..fc2f754e 100644 --- a/scripts/biz-certs/Bob-CA.srl +++ b/scripts/biz-certs/Bob-CA.srl @@ -1 +1 @@ -90801F1ED19454C4 +90801F1ED19454CF diff --git a/scripts/manifests.py b/scripts/manifests.py index b352b461..0204e882 100644 --- a/scripts/manifests.py +++ b/scripts/manifests.py @@ -1,6 +1,7 @@ # $Id$ -import rpki.x509, rpki.manifest, time, glob, os +import time, glob, os +import rpki.x509, rpki.manifest, rpki.sundial show_content_1 = False show_signed_manifest_PEM = False @@ -9,6 +10,7 @@ show_content_2 = False show_content_3 = False dump_signed_manifest_DER = False dump_manifest_content_DER = False +test_empty_manifest = False def dumpasn1(thing): # Save to file rather than using popen4() because dumpasn1 uses @@ -24,18 +26,22 @@ def dumpasn1(thing): finally: os.unlink(fn) +if test_empty_manifest: + names_and_objs = [] +else: + names_and_objs = [(fn, rpki.x509.X509(Auto_file = fn)) for fn in glob.glob("resource-cert-samples/*.cer")] + m = rpki.x509.SignedManifest() -m.build(serial = 17, - nextUpdate = rpki.datetime.datetime.utcnow() + rpki.datetime.timedelta(days = 1), - names_and_objs = [(fn, rpki.x509.X509(Auto_file = fn)) - for fn in glob.glob("resource-cert-samples/*.cer")]) +m.build( + serial = 17, + nextUpdate = rpki.sundial.datetime.utcnow() + rpki.sundial.timedelta(days = 1), + names_and_objs = names_and_objs, + keypair = rpki.x509.RSA(Auto_file = "biz-certs/Alice-EE.key"), + certs = rpki.x509.X509_chain(Auto_files = ("biz-certs/Alice-EE.cer", "biz-certs/Alice-CA.cer"))) if show_content_1: dumpasn1(m.get_content().toString()) -m.sign(keypair = rpki.x509.RSA(Auto_file = "biz-certs/Alice-EE.key"), - certs = rpki.x509.X509_chain(Auto_files = ("biz-certs/Alice-EE.cer", "biz-certs/Alice-CA.cer"))) - if show_signed_manifest_PEM: print m.get_PEM() diff --git a/scripts/rpki/left_right.py b/scripts/rpki/left_right.py index 63ebc385..b67ff84d 100644 --- a/scripts/rpki/left_right.py +++ b/scripts/rpki/left_right.py @@ -260,6 +260,7 @@ class self_elt(data_elt): def client_poll(self, gctx): """Run the regular client poll cycle with each of this self's parents in turn.""" + for parent in parent_elt.sql_fetch_where(gctx, "self_id = %s" % self.self_id): # This will need a callback when we go event-driven @@ -278,6 +279,35 @@ class self_elt(data_elt): ca.delete(gctx, parent) # CA not listed by parent rpki.sql.sql_sweep(gctx) + def update_children(self, gctx): + """Check for updated IRDB data for all of this self's children and + issue new certs as necessary. Must handle changes both in + resources and in expiration date. + """ + print "Code to check IRDB for updates to children not yet written" + + def regenerate_crls_and_manifests(self, gctx): + """Generate new CRLs and manifests as necessary for all of this + self's CAs. Extracting nextUpdate from a manifest is hard at the + moment due to implementation silliness, so for now we generate a + new manifest whenever we generate a new CRL + """ + + now = rpki.sundial.datetime.utcnow() + for parent in parent_elt.sql_fetch_where(gctx, "self_id = %s" % self.self_id): + repository = repository_elt.sql_fetch(gctx, parent.repository_id) + for ca in rpki.sql.ca_obj.sql_fetch_where(gctx, "parent_id = %s" % parent.parent_id): + ca_detail = ca.fetch_active(gctx) + # + # Temporary kludge until I sort out initial publication. + # + if True or now > ca_detail.latest_crl.getNextUpdate(): + ca_detail.generate_crl(gctx) + ca_detail.generate_manifest(gctx) + repository.publish(gctx, + (ca_detail.latest_crl, ca_detail.crl_uri(ca)), + (ca_detail.latest_manifest, ca_detail.manifest_uri(ca))) + class bsc_elt(data_elt): """<bsc/> (Business Signing Context) element.""" diff --git a/scripts/rpki/sql.py b/scripts/rpki/sql.py index b40bb672..0e148988 100644 --- a/scripts/rpki/sql.py +++ b/scripts/rpki/sql.py @@ -275,6 +275,7 @@ class ca_obj(sql_persistant): issue_response = rpki.up_down.issue_pdu.query(gctx, parent, self, ca_detail) ca_detail.activate( + gctx = gctx, ca = self, cert = issue_response.payload.classes[0].certs[0].cert, uri = issue_response.payload.classes[0].certs[0].cert_url) @@ -317,6 +318,10 @@ class ca_obj(sql_persistant): self.sql_mark_dirty() return self.last_crl_sn + def fetch_active(self, gctx): + """Fetch the current active ca_detail for this ca.""" + return ca_detail_obj.sql_fetch_where1(gctx, "ca_id = %s AND state = 'active'" % self.ca_id) + class ca_detail_obj(sql_persistant): """Internal CA detail object.""" @@ -344,11 +349,6 @@ class ca_detail_obj(sql_persistant): assert (self.manifest_public_key is None and self.manifest_private_key_id is None) or \ self.manifest_public_key.get_DER() == self.manifest_private_key_id.get_public_DER() - @classmethod - def sql_fetch_active(cls, gctx, ca_id): - """Fetch the current active ca_detail_obj associated with a given ca_id.""" - return cls.sql_fetch_where1(gctx, "ca_id = %s AND state = 'active'" % ca_id) - def crl_uri(self, ca): """Return publication URI for this ca_detail's CRL.""" return ca.sia_uri + self.public_key.gSKI() + ".crl" @@ -357,12 +357,14 @@ class ca_detail_obj(sql_persistant): """Return publication URI for this ca_detail's manifest.""" return ca.sia_uri + self.public_key.gSKI() + ".mnf" - def activate(self, ca, cert, uri, predecessor = None): + def activate(self, gctx, ca, cert, uri, predecessor = None): """Activate this ca_detail.""" self.latest_ca_cert = cert self.ca_cert_uri = uri.rsync() self.generate_manifest_cert(ca) + self.generate_crl(gctx) + self.generate_manifest(gctx) self.state = "active" self.sql_mark_dirty() @@ -488,7 +490,7 @@ class ca_detail_obj(sql_persistant): certlist.append((child_cert.cert.getSerial(), child_cert.revoked, ())) certlist.sort() - return rpki.x509.CRL.generate( + self.latest_crl = rpki.x509.CRL.generate( keypair = self.private_key_id, issuer = self.latest_ca_cert, serial = ca.next_crl_number(), @@ -505,12 +507,12 @@ class ca_detail_obj(sql_persistant): certs = child_cert_obj.sql_fetch_where(gctx, "child_cert.ca_detail_id = %s AND child_cert.revoked IS NULL" % self.ca_detail_id) m = rpki.x509.SignedManifest() - m.build(serial = ca.next_manifest_number(), - nextUpdate = rpki.sundial.datetime.utcnow() + rpki.sundial.timedelta(seconds = self_obj.crl_interval), - names_and_objs = [(c.uri_tail(), c.cert) for c in certs]) - m.sign(keypair = self.manifest_private_key_id, - certs = rpki.x509.X509_chain(self.latest_manifest_cert)) - + m.build( + serial = ca.next_manifest_number(), + nextUpdate = rpki.sundial.datetime.utcnow() + rpki.sundial.timedelta(seconds = self_obj.crl_interval), + names_and_objs = [(c.uri_tail(), c.cert) for c in certs], + keypair = self.manifest_private_key_id, + certs = rpki.x509.X509_chain(self.latest_manifest_cert)) self.latest_manifest = m class child_cert_obj(sql_persistant): diff --git a/scripts/rpki/up_down.py b/scripts/rpki/up_down.py index a094b308..d2b8bf43 100644 --- a/scripts/rpki/up_down.py +++ b/scripts/rpki/up_down.py @@ -171,7 +171,7 @@ class list_pdu(base_elt): irdb_resources = rpki.left_right.irdb_query(gctx, child.self_id, child.child_id) for parent in rpki.left_right.parent_elt.sql_fetch_where(gctx, "parent.self_id = %s" % child.self_id): for ca in rpki.sql.ca_obj.sql_fetch_where(gctx, "ca.parent_id = %s" % parent.parent_id): - ca_detail = rpki.sql.ca_detail_obj.sql_fetch_active(gctx, ca.ca_id) + ca_detail = ca.fetch_active(gctx) if not ca_detail: continue resources = ca_detail.latest_ca_cert.get_3779resources().intersection(irdb_resources) @@ -252,7 +252,7 @@ class issue_pdu(base_elt): raise rpki.exceptions.BadClassNameSyntax, "Bad class name %s" % self.class_name ca_id = long(self.class_name) ca = rpki.sql.ca_obj.sql_fetch(gctx, ca_id) - ca_detail = rpki.sql.ca_detail_obj.sql_fetch_active(gctx, ca_id) + ca_detail = ca.fetch_active(gctx) if ca is None or ca_detail is None: raise rpki.exceptions.NotInDatabase self.pkcs10.check_valid_rpki() diff --git a/scripts/rpki/x509.py b/scripts/rpki/x509.py index b6c56d1e..47a47b51 100644 --- a/scripts/rpki/x509.py +++ b/scripts/rpki/x509.py @@ -587,9 +587,13 @@ class SignedManifest(DER_object): self.clear() self.content = content - def sign(self, keypair, certs): - """Sign this manifest.""" - self.DER = rpki.cms.sign(self.content.toString(), keypair, certs) + def getThisUpdate(self): + """Get thisUpdate value from this manifest.""" + return rpki.sundial.datetime.fromGeneralizedTime(self.get_content()) + + def getNextUpdate(self): + """Get nextUpdate value from this manifest.""" + return rpki.sundial.datetime.fromGeneralizedTime(self.get_content()) def verify(self, ta): """Verify this manifest.""" @@ -598,8 +602,8 @@ class SignedManifest(DER_object): m.fromString(s) self.content = m - def build(self, serial, nextUpdate, names_and_objs, version = 0): - """Build the inner content of this manifest.""" + def build(self, serial, nextUpdate, names_and_objs, keypair, certs, version = 0): + """Build the inner content of this manifest and sign it with CMS.""" filelist = [] for name, obj in names_and_objs: d = POW.Digest(POW.SHA256_DIGEST) @@ -614,6 +618,7 @@ class SignedManifest(DER_object): m.fileHashAlg.set((2, 16, 840, 1, 101, 3, 4, 2, 1)) # id-sha256 m.fileList.set(filelist) self.set_content(m) + self.DER = rpki.cms.sign(m.toString(), keypair, certs) class CRL(DER_object): """Class to hold a Certificate Revocation List.""" @@ -650,6 +655,14 @@ class CRL(DER_object): self.POWpkix = crl return self.POWpkix + def getThisUpdate(self): + """Get thisUpdate value from this CRL.""" + return rpki.sundial.datetime.fromASN1tuple(self.get_POWpkix().getThisUpdate()) + + def getNextUpdate(self): + """Get nextUpdate value from this CRL.""" + return rpki.sundial.datetime.fromASN1tuple(self.get_POWpkix().getNextUpdate()) + @classmethod def generate(cls, keypair, issuer, serial, thisUpdate, nextUpdate, revokedCertificates, version = 1, digestType = "sha256WithRSAEncryption"): crl = POW.pkix.CertificateList() @@ -660,7 +673,7 @@ class CRL(DER_object): if revokedCertificates: crl.setRevokedCertificates(revokedCertificates) crl.setExtensions( - (rpki.oids.name2oid["authorityKeyIdentifier"], False, (issuer.get_SKI(), (), None)), - (rpki.oids.name2oid["cRLNumber"], False, serial)) + ((rpki.oids.name2oid["authorityKeyIdentifier"], False, (issuer.get_SKI(), (), None)), + (rpki.oids.name2oid["cRLNumber"], False, serial))) crl.sign(keypair.get_POW(), digestType) return cls(POWpkix = crl) diff --git a/scripts/rpkid.py b/scripts/rpkid.py index 4cfd295a..74ba77c1 100755 --- a/scripts/rpkid.py +++ b/scripts/rpkid.py @@ -13,6 +13,7 @@ import rpki.resource_set, rpki.up_down, rpki.left_right, rpki.x509 import rpki.https, rpki.config, rpki.cms, rpki.exceptions, rpki.relaxng def left_right_handler(query, path): + """Process one left-right PDU.""" try: q_elt = rpki.cms.xml_verify(query, gctx.cms_ta_irbe) rpki.relaxng.left_right.assertValid(q_elt) @@ -26,6 +27,7 @@ def left_right_handler(query, path): return 500, "Unhandled exception %s" % data def up_down_handler(query, path): + """Process one up-down PDU.""" try: child_id = path.partition("/up-down/")[2] if not child_id.isdigit(): @@ -39,8 +41,13 @@ def up_down_handler(query, path): return 400, "Could not process PDU: %s" % data def cronjob_handler(query, path): + """Periodic tasks. As simple as possible for now, may need to break + this up into separate handlers later. + """ for s in rpki.left_right.self_elt.sql_fetch_all(gctx): s.client_poll(gctx) + s.update_children(gctx) + s.regenerate_crls_and_manifests(gctx) return 200, "OK" class global_context(object): diff --git a/scripts/testroot.sh b/scripts/testroot.sh index ebc61d95..f3818496 100644 --- a/scripts/testroot.sh +++ b/scripts/testroot.sh @@ -67,6 +67,8 @@ python irbe-cli.py child --self_id 1 --action create --bsc_id 1 --cms_ta biz-cer if test "$1" = "run" then + rm -rf publication + python testroot.py & testroot=$! python irdb.py & irdb=$! trap "kill $rpkid $irdb $testroot" 0 |