aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--rpkid/irdbd.sql8
-rw-r--r--rpkid/left-right-schema.rnc10
-rw-r--r--rpkid/left-right-schema.rng14
-rw-r--r--rpkid/publication-schema.rnc11
-rw-r--r--rpkid/publication-schema.rng48
-rw-r--r--rpkid/rpki/irdbd.py13
-rw-r--r--rpkid/rpki/left_right.py159
-rw-r--r--rpkid/rpki/publication.py16
-rw-r--r--rpkid/rpki/relaxng.py62
-rw-r--r--rpkid/rpki/rpkid.py219
-rw-r--r--rpkid/rpki/up_down.py4
-rw-r--r--rpkid/rpki/x509.py8
-rw-r--r--rpkid/rpkid.sql3
13 files changed, 453 insertions, 122 deletions
diff --git a/rpkid/irdbd.sql b/rpkid/irdbd.sql
index fd461b42..bf324cd8 100644
--- a/rpkid/irdbd.sql
+++ b/rpkid/irdbd.sql
@@ -41,7 +41,7 @@ DROP TABLE IF EXISTS roa_request;
DROP TABLE IF EXISTS registrant_net;
DROP TABLE IF EXISTS registrant_asn;
DROP TABLE IF EXISTS registrant;
-DROP TABLE IF EXISTS gbr_request;
+DROP TABLE IF EXISTS ghostbuster_request;
CREATE TABLE registrant (
registrant_id SERIAL NOT NULL,
@@ -92,12 +92,12 @@ CREATE TABLE roa_request_prefix (
FOREIGN KEY (roa_request_id) REFERENCES roa_request (roa_request_id) ON DELETE CASCADE
) ENGINE=InnoDB;
-CREATE TABLE gbr_request (
- gbr_request_id SERIAL NOT NULL,
+CREATE TABLE ghostbuster_request (
+ ghostbuster_request_id SERIAL NOT NULL,
self_handle VARCHAR(40) NOT NULL,
parent_handle VARCHAR(40),
vcard LONGBLOB NOT NULL,
- PRIMARY KEY (gbr_request_id)
+ PRIMARY KEY (ghostbuster_request_id)
) ENGINE=InnoDB;
-- Local Variables:
diff --git a/rpkid/left-right-schema.rnc b/rpkid/left-right-schema.rnc
index 85f2348a..3b058502 100644
--- a/rpkid/left-right-schema.rnc
+++ b/rpkid/left-right-schema.rnc
@@ -50,7 +50,7 @@ query_elt |= parent_query
query_elt |= child_query
query_elt |= repository_query
query_elt |= list_roa_requests_query
-query_elt |= list_gbr_requests_query
+query_elt |= list_ghostbuster_requests_query
query_elt |= list_resources_query
query_elt |= list_published_objects_query
query_elt |= list_received_resources_query
@@ -63,7 +63,7 @@ reply_elt |= child_reply
reply_elt |= repository_reply
reply_elt |= list_resources_reply
reply_elt |= list_roa_requests_reply
-reply_elt |= list_gbr_requests_reply
+reply_elt |= list_ghostbuster_requests_reply
reply_elt |= list_published_objects_reply
reply_elt |= list_received_resources_reply
reply_elt |= report_error_reply
@@ -252,13 +252,13 @@ list_roa_requests_reply = element list_roa_requests {
attribute ipv6 { ipv6_list }?
}
-# <list_gbr_requests/> element
+# <list_ghostbuster_requests/> element
-list_gbr_requests_query = element list_gbr_requests {
+list_ghostbuster_requests_query = element list_ghostbuster_requests {
tag, self_handle, parent_handle
}
-list_gbr_requests_reply = element list_gbr_requests {
+list_ghostbuster_requests_reply = element list_ghostbuster_requests {
tag, self_handle, parent_handle,
xsd:string
}
diff --git a/rpkid/left-right-schema.rng b/rpkid/left-right-schema.rng
index fe441006..2af25a10 100644
--- a/rpkid/left-right-schema.rng
+++ b/rpkid/left-right-schema.rng
@@ -84,7 +84,7 @@
<ref name="list_roa_requests_query"/>
</define>
<define name="query_elt" combine="choice">
- <ref name="list_gbr_requests_query"/>
+ <ref name="list_ghostbuster_requests_query"/>
</define>
<define name="query_elt" combine="choice">
<ref name="list_resources_query"/>
@@ -118,7 +118,7 @@
<ref name="list_roa_requests_reply"/>
</define>
<define name="reply_elt" combine="choice">
- <ref name="list_gbr_requests_reply"/>
+ <ref name="list_ghostbuster_requests_reply"/>
</define>
<define name="reply_elt" combine="choice">
<ref name="list_published_objects_reply"/>
@@ -889,16 +889,16 @@
</optional>
</element>
</define>
- <!-- <list_gbr_requests/> element -->
- <define name="list_gbr_requests_query">
- <element name="list_gbr_requests">
+ <!-- <list_ghostbuster_requests/> element -->
+ <define name="list_ghostbuster_requests_query">
+ <element name="list_ghostbuster_requests">
<ref name="tag"/>
<ref name="self_handle"/>
<ref name="parent_handle"/>
</element>
</define>
- <define name="list_gbr_requests_reply">
- <element name="list_gbr_requests">
+ <define name="list_ghostbuster_requests_reply">
+ <element name="list_ghostbuster_requests">
<ref name="tag"/>
<ref name="self_handle"/>
<ref name="parent_handle"/>
diff --git a/rpkid/publication-schema.rnc b/rpkid/publication-schema.rnc
index 5df646d1..18dd400e 100644
--- a/rpkid/publication-schema.rnc
+++ b/rpkid/publication-schema.rnc
@@ -44,10 +44,10 @@ start = element msg {
}
# PDUs allowed in a query
-query_elt = ( config_query | client_query | certificate_query | crl_query | manifest_query | roa_query )
+query_elt = ( config_query | client_query | certificate_query | crl_query | manifest_query | roa_query | ghostbuster_query )
# PDUs allowed in a reply
-reply_elt = ( config_reply | client_reply | certificate_reply | crl_reply | manifest_reply | roa_reply | report_error_reply )
+reply_elt = ( config_reply | client_reply | certificate_reply | crl_reply | manifest_reply | roa_reply | ghostbuster_reply | report_error_reply )
# Tag attributes for bulk operations
tag = attribute tag { xsd:token {maxLength="1024" } }
@@ -123,6 +123,13 @@ roa_reply |= element roa { attribute action { "publish" }, tag?, uri }
roa_query |= element roa { attribute action { "withdraw" }, tag?, uri }
roa_reply |= element roa { attribute action { "withdraw" }, tag?, uri }
+# <ghostbuster/> element
+
+ghostbuster_query |= element ghostbuster { attribute action { "publish" }, tag?, uri, base64 }
+ghostbuster_reply |= element ghostbuster { attribute action { "publish" }, tag?, uri }
+ghostbuster_query |= element ghostbuster { attribute action { "withdraw" }, tag?, uri }
+ghostbuster_reply |= element ghostbuster { attribute action { "withdraw" }, tag?, uri }
+
# <report_error/> element
error = xsd:token { maxLength="1024" }
diff --git a/rpkid/publication-schema.rng b/rpkid/publication-schema.rng
index 9dc8b751..7544d5eb 100644
--- a/rpkid/publication-schema.rng
+++ b/rpkid/publication-schema.rng
@@ -73,6 +73,7 @@
<ref name="crl_query"/>
<ref name="manifest_query"/>
<ref name="roa_query"/>
+ <ref name="ghostbuster_query"/>
</choice>
</define>
<!-- PDUs allowed in a reply -->
@@ -84,6 +85,7 @@
<ref name="crl_reply"/>
<ref name="manifest_reply"/>
<ref name="roa_reply"/>
+ <ref name="ghostbuster_reply"/>
<ref name="report_error_reply"/>
</choice>
</define>
@@ -500,6 +502,52 @@
<ref name="uri"/>
</element>
</define>
+ <!-- <ghostbuster/> element -->
+ <define name="ghostbuster_query" combine="choice">
+ <element name="ghostbuster">
+ <attribute name="action">
+ <value>publish</value>
+ </attribute>
+ <optional>
+ <ref name="tag"/>
+ </optional>
+ <ref name="uri"/>
+ <ref name="base64"/>
+ </element>
+ </define>
+ <define name="ghostbuster_reply" combine="choice">
+ <element name="ghostbuster">
+ <attribute name="action">
+ <value>publish</value>
+ </attribute>
+ <optional>
+ <ref name="tag"/>
+ </optional>
+ <ref name="uri"/>
+ </element>
+ </define>
+ <define name="ghostbuster_query" combine="choice">
+ <element name="ghostbuster">
+ <attribute name="action">
+ <value>withdraw</value>
+ </attribute>
+ <optional>
+ <ref name="tag"/>
+ </optional>
+ <ref name="uri"/>
+ </element>
+ </define>
+ <define name="ghostbuster_reply" combine="choice">
+ <element name="ghostbuster">
+ <attribute name="action">
+ <value>withdraw</value>
+ </attribute>
+ <optional>
+ <ref name="tag"/>
+ </optional>
+ <ref name="uri"/>
+ </element>
+ </define>
<!-- <report_error/> element -->
<define name="error">
<data type="token">
diff --git a/rpkid/rpki/irdbd.py b/rpkid/rpki/irdbd.py
index bb05fee3..a5bf2663 100644
--- a/rpkid/rpki/irdbd.py
+++ b/rpkid/rpki/irdbd.py
@@ -115,10 +115,10 @@ class main(object):
r_msg.append(r_pdu)
- def handle_list_gbr_requests(self, q_pdu, r_msg):
+ def handle_list_ghostbuster_requests(self, q_pdu, r_msg):
self.cur.execute(
- "SELECT vcard FROM gbr_request self_handle = %s and parent_handle = %s",
+ "SELECT vcard FROM ghostbuster_request WHERE self_handle = %s AND parent_handle = %s",
(q_pdu.self_handle, q_pdu.parent_handle))
vcards = [result[0] for result in self.cur.fetchall()]
@@ -126,13 +126,13 @@ class main(object):
if not vcards:
self.cur.execute(
- "SELECT vcard FROM gbr_request self_handle = %s and parent_handle IS NULL",
+ "SELECT vcard FROM ghostbuster_request WHERE self_handle = %s AND parent_handle IS NULL",
(q_pdu.self_handle,))
vcards = [result[0] for result in self.cur.fetchall()]
for vcard in vcards:
- r_pdu = rpki.left_right.list_gbr_requests_elt()
+ r_pdu = rpki.left_right.list_ghostbuster_requests_elt()
r_pdu.tag = q_pdu.tag
r_pdu.self_handle = q_pdu.self_handle
r_pdu.parent_handle = q_pdu.parent_handle
@@ -141,8 +141,9 @@ class main(object):
handle_dispatch = {
- rpki.left_right.list_resources_elt : handle_list_resources,
- rpki.left_right.list_roa_requests_elt : handle_list_roa_requests }
+ rpki.left_right.list_resources_elt : handle_list_resources,
+ rpki.left_right.list_roa_requests_elt : handle_list_roa_requests,
+ rpki.left_right.list_ghostbuster_requests_elt : handle_list_ghostbuster_requests}
def handler(self, query, path, cb):
diff --git a/rpkid/rpki/left_right.py b/rpkid/rpki/left_right.py
index f46e6dde..2c61b5ac 100644
--- a/rpkid/rpki/left_right.py
+++ b/rpkid/rpki/left_right.py
@@ -183,6 +183,13 @@ class self_elt(data_elt):
"""
return rpki.rpkid.roa_obj.sql_fetch_where(self.gctx, "self_id = %s", (self.self_id,))
+ @property
+ def ghostbusters(self):
+ """
+ Fetch all Ghostbuster record objects that link to this self object.
+ """
+ return rpki.rpkid.ghostbuster_obj.sql_fetch_where(self.gctx, "self_id = %s", (self.self_id,))
+
def serve_post_save_hook(self, q_pdu, r_pdu, cb, eb):
"""
Extra server actions for self_elt.
@@ -263,12 +270,13 @@ class self_elt(data_elt):
def loop(iterator, parent):
q_msg = rpki.publication.msg.query()
for ca in parent.cas:
- ca_detail = ca.fetch_active()
+ ca_detail = ca.active_ca_detail
if ca_detail is not None:
q_msg.append(rpki.publication.crl_elt.make_publish(ca_detail.crl_uri, ca_detail.latest_crl))
q_msg.append(rpki.publication.manifest_elt.make_publish(ca_detail.manifest_uri, ca_detail.latest_manifest))
q_msg.extend(rpki.publication.certificate_elt.make_publish(c.uri, c.cert) for c in ca_detail.child_certs)
q_msg.extend(rpki.publication.roa_elt.make_publish(r.uri, r.roa) for r in ca_detail.roas if r.roa is not None)
+ q_msg.extend(rpki.publication.ghostbusters_elt.make_publish(g.uri, g.ghostbuster) for g in ca_detail.ghostbusters)
parent.repository.call_pubd(iterator, eb, q_msg)
rpki.async.iterator(self.parents, loop, cb)
@@ -324,6 +332,11 @@ class self_elt(data_elt):
def four():
self.gctx.checkpoint()
+ rpki.log.debug("Self %s[%d] updating Ghostbuster records" % (self.self_handle, self.self_id))
+ self.update_ghostbusters(five)
+
+ def five():
+ self.gctx.checkpoint()
rpki.log.debug("Self %s[%d] regenerating CRLs and manifests" % (self.self_handle, self.self_id))
self.regenerate_crls_and_manifests(cb)
@@ -485,10 +498,10 @@ class self_elt(data_elt):
for parent in self.parents:
for ca in parent.cas:
try:
- for ca_detail in ca.fetch_revoked():
+ for ca_detail in ca.revoked_ca_details:
if now > ca_detail.latest_crl.getNextUpdate():
ca_detail.delete(ca = ca, publisher = publisher)
- ca_detail = ca.fetch_active()
+ ca_detail = ca.active_ca_detail
if ca_detail is not None and now + regen_margin > ca_detail.latest_crl.getNextUpdate():
ca_detail.generate_crl(publisher = publisher)
ca_detail.generate_manifest(publisher = publisher)
@@ -510,86 +523,91 @@ class self_elt(data_elt):
def update_ghostbusters(self, cb):
"""
- Generate or update Ghostbusters records for this self.
+ Generate or update Ghostbuster records for this self.
This is heavily based on .update_roas(), and probably both of them
need refactoring.
"""
- raise rpki.exceptions.NotImplementedYet
-
parents = dict((p.parent_handle, p) for p in self.parents)
- def got_gbr_requests(gbr_requests):
-
- self.gctx.checkpoint()
+ def got_ghostbuster_requests(ghostbuster_requests):
- if self.gctx.sql.dirty:
- rpki.log.warn("Unexpected dirty SQL cache, flushing")
- self.gctx.sql.sweep()
+ try:
+ self.gctx.checkpoint()
+ if self.gctx.sql.dirty:
+ rpki.log.warn("Unexpected dirty SQL cache, flushing")
+ self.gctx.sql.sweep()
+
+ ghostbusters = {}
+ orphans = []
+ for ghostbuster in self.ghostbusters:
+ k = (ghostbuster.ca_detail_id, ghostbuster.vcard)
+ if ghostbuster.ca_detail.state != "active" or k in ghostbusters:
+ orphans.append(ghostbuster)
+ else:
+ ghostbusters[k] = ghostbuster
- ghostbusters = {}
- orphans = []
- for ghostbuster in self.ghostbusters:
- k = (ghostbuster.ca_detail.ca.parent.parent_handle, ghostbuster.vcard)
- if k not in ghostbusters:
- ghostbusters[k] = ghostbuster
- elif ghostbuster.ca_detail.state == "active" and ghostbusters[k].ca_detail.state != "active":
- orphans.append(ghostbusters[k])
- ghostbusters[k] = ghostbuster
- else:
- orphans.append(ghostbusters[k])
+ publisher = rpki.rpkid.publication_queue()
+ ca_details = set()
- publisher = rpki.rpkid.publication_queue()
- ca_details = set()
-
- seen = set()
- for gbr_request in gbr_requests:
- if gbr_request.parent_handle not in parents:
- rpki.log.warn("Unknown parent_handle %r in Ghostbuster request, skipping" % gbr_request.parent_handle)
- continue
- k = (gbr_request.parent_handle, gbr_request.vcard)
- if k in seen:
- rpki.log.warn("Skipping duplicate Ghostbuster request %r" % gbr_request)
- continue
- see.add(k)
- ghostbuster = ghostbusters.pop(k, None)
- if ghostbuster is None:
- ghostbuster = rpki.rpkid.ghostbuster_obj(self.gctx, self.self_id, parents[gbr_request.parent_handle], vcard)
- rpki.log.debug("Created new Ghostbuster request for %r" % gbr_request.parent_handle)
- else:
- rpki.log.debug("Found existing Ghostbuster request for %r" % gbr_request.parent_handle)
- ghostbuster.update(publisher = publisher, fast = True)
- ca_details.add(ghostbuster.ca_detail)
+ seen = set()
+ for ghostbuster_request in ghostbuster_requests:
+ if ghostbuster_request.parent_handle not in parents:
+ rpki.log.warn("Unknown parent_handle %r in Ghostbuster request, skipping" % ghostbuster_request.parent_handle)
+ continue
+ k = (ghostbuster_request.parent_handle, ghostbuster_request.vcard)
+ if k in seen:
+ rpki.log.warn("Skipping duplicate Ghostbuster request %r" % ghostbuster_request)
+ continue
+ seen.add(k)
+ for ca in parents[ghostbuster_request.parent_handle].cas:
+ ca_detail = ca.active_ca_detail
+ if ca_detail is not None:
+ ghostbuster = ghostbusters.pop((ca_detail.ca_detail_id, ghostbuster_request.vcard), None)
+ if ghostbuster is None:
+ ghostbuster = rpki.rpkid.ghostbuster_obj(self.gctx, self.self_id, ca_detail.ca_detail_id, ghostbuster_request.vcard)
+ rpki.log.debug("Created new Ghostbuster request for %r" % ghostbuster_request.parent_handle)
+ else:
+ rpki.log.debug("Found existing Ghostbuster request for %r" % ghostbuster_request.parent_handle)
+ ghostbuster.update(publisher = publisher, fast = True)
+ ca_details.add(ca_detail)
+
+ orphans.extend(ghostbusters.itervalues())
+ for ghostbuster in orphans:
+ ca_details.add(ghostbuster.ca_detail)
+ ghostbuster.revoke(publisher = publisher, fast = True)
+
+ for ca_detail in ca_details:
+ ca_detail.generate_crl(publisher = publisher)
+ ca_detail.generate_manifest(publisher = publisher)
- orphans.extend(ghostbusters.itervalues())
- for ghostbuster in orphans:
- ca_details.add(ghostbuster.ca_detail)
- ghostbuster.revoke(publisher = publisher, fast = True)
+ self.gctx.sql.sweep()
- for ca_detail in ca_details:
- ca_detail.generate_crl(publisher = publisher)
- ca_detail.generate_manifest(publisher = publisher)
+ def publication_failed(e):
+ rpki.log.traceback()
+ rpki.log.warn("Couldn't publish Ghostbuster updates for %s, skipping: %s" % (self.self_handle, e))
+ self.gctx.checkpoint()
+ cb()
- self.gctx.sql.sweep()
+ self.gctx.checkpoint()
+ publisher.call_pubd(cb, publication_failed)
- def publication_failed(e):
+ except (SystemExit, rpki.async.ExitNow):
+ raise
+ except Exception, e:
rpki.log.traceback()
- rpki.log.warn("Couldn't publish Ghostbuster updates for %s, skipping: %s" % (self.self_handle, e))
- self.gctx.checkpoint()
+ rpki.log.warn("Could not update Ghostbuster records for %s, skipping: %s" % (self.self_handle, e))
cb()
- self.gctx.checkpoint()
- publisher.call_pubd(cb, publication_failed)
-
- def gbr_requests_failed(e):
+ def ghostbuster_requests_failed(e):
rpki.log.traceback()
- rpki.log.warn("Could not fetch Ghostbusters record requests for %s, skipping: %s" % (self.self_handle, e))
+ rpki.log.warn("Could not fetch Ghostbuster record requests for %s, skipping: %s" % (self.self_handle, e))
cb()
self.gctx.checkpoint()
- self.gctx.irdb_query_gbr_requests(self.self_handle, parents.iterkeys(),
- got_gbr_requests, gbr_requests_failed)
+ self.gctx.irdb_query_ghostbuster_requests(self.self_handle, parents.iterkeys(),
+ got_ghostbuster_requests, ghostbuster_requests_failed)
def update_roas(self, cb):
@@ -933,7 +951,7 @@ class parent_elt(data_elt):
ca = ca_map[rc.class_name]
skis_parent_knows_about = set(c.cert.gSKI() for c in rc.certs)
- skis_ca_knows_about = set(ca_detail.latest_ca_cert.gSKI() for ca_detail in ca.fetch_issue_response_candidates())
+ skis_ca_knows_about = set(ca_detail.latest_ca_cert.gSKI() for ca_detail in ca.issue_response_candidate_ca_details)
skis_only_parent_knows_about = skis_parent_knows_about - skis_ca_knows_about
rpki.async.iterator(skis_only_parent_knows_about, ski_loop, rc_iterator)
@@ -1166,12 +1184,12 @@ class list_roa_requests_elt(rpki.xml_utils.base_elt, left_right_namespace):
if self.ipv6 is not None:
self.ipv6 = rpki.resource_set.roa_prefix_set_ipv6(self.ipv6)
-class list_gbr_requests_elt(rpki.xml_utils.text_elt, left_right_namespace):
+class list_ghostbuster_requests_elt(rpki.xml_utils.text_elt, left_right_namespace):
"""
- <list_gbr_requests/> element.
+ <list_ghostbuster_requests/> element.
"""
- element_name = "list_gbr_requests"
+ element_name = "list_ghostbuster_requests"
attributes = ("self_handle", "tag", "parent_handle")
text_attribute = "vcard"
@@ -1195,14 +1213,15 @@ class list_published_objects_elt(rpki.xml_utils.text_elt, left_right_namespace):
misnomer here, there's no action attribute and no dispatch, we
just dump every published object for the specified <self/> and return.
"""
- for parent in self_elt.serve_fetch_handle(self.gctx, None, self.self_handle).parents:
+ for parent in self_elt.serve_fetch_handle(self.gctx, None, self.self_handle).parents:
for ca in parent.cas:
- ca_detail = ca.fetch_active()
+ ca_detail = ca.active_ca_detail
if ca_detail is not None:
r_msg.append(self.make_reply(ca_detail.crl_uri, ca_detail.latest_crl))
r_msg.append(self.make_reply(ca_detail.manifest_uri, ca_detail.latest_manifest))
r_msg.extend(self.make_reply(c.uri, c.cert) for c in ca_detail.child_certs)
r_msg.extend(self.make_reply(r.uri, r.roa) for r in ca_detail.roas if r.roa is not None)
+ r_msg.extend(self.make_reply(g.uri, g.ghostbuster) for g in ca_detail.ghostbusters)
cb()
def make_reply(self, uri, obj):
@@ -1231,7 +1250,7 @@ class list_received_resources_elt(rpki.xml_utils.base_elt, left_right_namespace)
"""
for parent in self_elt.serve_fetch_handle(self.gctx, None, self.self_handle).parents:
for ca in parent.cas:
- ca_detail = ca.fetch_active()
+ ca_detail = ca.active_ca_detail
if ca_detail is not None and ca_detail.latest_ca_cert is not None:
r_msg.append(self.make_reply(parent.parent_handle, ca_detail.ca_cert_uri, ca_detail.latest_ca_cert))
cb()
@@ -1291,7 +1310,7 @@ class msg(rpki.xml_utils.msg, left_right_namespace):
pdus = dict((x.element_name, x)
for x in (self_elt, child_elt, parent_elt, bsc_elt,
repository_elt, list_resources_elt,
- list_roa_requests_elt, list_gbr_requests_elt,
+ list_roa_requests_elt, list_ghostbuster_requests_elt,
list_published_objects_elt,
list_received_resources_elt, report_error_elt))
diff --git a/rpkid/rpki/publication.py b/rpkid/rpki/publication.py
index 4c6df14b..51baf505 100644
--- a/rpkid/rpki/publication.py
+++ b/rpkid/rpki/publication.py
@@ -300,7 +300,17 @@ class roa_elt(publication_object_elt):
element_name = "roa"
payload_type = rpki.x509.ROA
-publication_object_elt.obj2elt = dict((e.payload_type, e) for e in (certificate_elt, crl_elt, manifest_elt, roa_elt))
+class ghostbuster_elt(publication_object_elt):
+ """
+ <ghostbuster/> element.
+ """
+
+ element_name = "ghostbuster"
+ payload_type = rpki.x509.Ghostbuster
+
+publication_object_elt.obj2elt = dict(
+ (e.payload_type, e) for e in
+ (certificate_elt, crl_elt, manifest_elt, roa_elt, ghostbuster_elt))
class report_error_elt(rpki.xml_utils.text_elt, publication_namespace):
"""
@@ -354,8 +364,8 @@ class msg(rpki.xml_utils.msg, publication_namespace):
## @var pdus
# Dispatch table of PDUs for this protocol.
- pdus = dict((x.element_name, x)
- for x in (config_elt, client_elt, certificate_elt, crl_elt, manifest_elt, roa_elt, report_error_elt))
+ pdus = dict((x.element_name, x) for x in
+ (config_elt, client_elt, certificate_elt, crl_elt, manifest_elt, roa_elt, ghostbuster_elt, report_error_elt))
def serve_top_level(self, gctx, client, cb):
"""
diff --git a/rpkid/rpki/relaxng.py b/rpkid/rpki/relaxng.py
index 4a357246..cb6654b6 100644
--- a/rpkid/rpki/relaxng.py
+++ b/rpkid/rpki/relaxng.py
@@ -90,7 +90,7 @@ left_right = lxml.etree.RelaxNG(lxml.etree.fromstring('''<?xml version="1.0" enc
<ref name="list_roa_requests_query"/>
</define>
<define name="query_elt" combine="choice">
- <ref name="list_gbr_requests_query"/>
+ <ref name="list_ghostbuster_requests_query"/>
</define>
<define name="query_elt" combine="choice">
<ref name="list_resources_query"/>
@@ -124,7 +124,7 @@ left_right = lxml.etree.RelaxNG(lxml.etree.fromstring('''<?xml version="1.0" enc
<ref name="list_roa_requests_reply"/>
</define>
<define name="reply_elt" combine="choice">
- <ref name="list_gbr_requests_reply"/>
+ <ref name="list_ghostbuster_requests_reply"/>
</define>
<define name="reply_elt" combine="choice">
<ref name="list_published_objects_reply"/>
@@ -895,16 +895,16 @@ left_right = lxml.etree.RelaxNG(lxml.etree.fromstring('''<?xml version="1.0" enc
</optional>
</element>
</define>
- <!-- <list_gbr_requests/> element -->
- <define name="list_gbr_requests_query">
- <element name="list_gbr_requests">
+ <!-- <list_ghostbuster_requests/> element -->
+ <define name="list_ghostbuster_requests_query">
+ <element name="list_ghostbuster_requests">
<ref name="tag"/>
<ref name="self_handle"/>
<ref name="parent_handle"/>
</element>
</define>
- <define name="list_gbr_requests_reply">
- <element name="list_gbr_requests">
+ <define name="list_ghostbuster_requests_reply">
+ <element name="list_ghostbuster_requests">
<ref name="tag"/>
<ref name="self_handle"/>
<ref name="parent_handle"/>
@@ -1337,6 +1337,7 @@ publication = lxml.etree.RelaxNG(lxml.etree.fromstring('''<?xml version="1.0" en
<ref name="crl_query"/>
<ref name="manifest_query"/>
<ref name="roa_query"/>
+ <ref name="ghostbuster_query"/>
</choice>
</define>
<!-- PDUs allowed in a reply -->
@@ -1348,6 +1349,7 @@ publication = lxml.etree.RelaxNG(lxml.etree.fromstring('''<?xml version="1.0" en
<ref name="crl_reply"/>
<ref name="manifest_reply"/>
<ref name="roa_reply"/>
+ <ref name="ghostbuster_reply"/>
<ref name="report_error_reply"/>
</choice>
</define>
@@ -1764,6 +1766,52 @@ publication = lxml.etree.RelaxNG(lxml.etree.fromstring('''<?xml version="1.0" en
<ref name="uri"/>
</element>
</define>
+ <!-- <ghostbuster/> element -->
+ <define name="ghostbuster_query" combine="choice">
+ <element name="ghostbuster">
+ <attribute name="action">
+ <value>publish</value>
+ </attribute>
+ <optional>
+ <ref name="tag"/>
+ </optional>
+ <ref name="uri"/>
+ <ref name="base64"/>
+ </element>
+ </define>
+ <define name="ghostbuster_reply" combine="choice">
+ <element name="ghostbuster">
+ <attribute name="action">
+ <value>publish</value>
+ </attribute>
+ <optional>
+ <ref name="tag"/>
+ </optional>
+ <ref name="uri"/>
+ </element>
+ </define>
+ <define name="ghostbuster_query" combine="choice">
+ <element name="ghostbuster">
+ <attribute name="action">
+ <value>withdraw</value>
+ </attribute>
+ <optional>
+ <ref name="tag"/>
+ </optional>
+ <ref name="uri"/>
+ </element>
+ </define>
+ <define name="ghostbuster_reply" combine="choice">
+ <element name="ghostbuster">
+ <attribute name="action">
+ <value>withdraw</value>
+ </attribute>
+ <optional>
+ <ref name="tag"/>
+ </optional>
+ <ref name="uri"/>
+ </element>
+ </define>
<!-- <report_error/> element -->
<define name="error">
<data type="token">
diff --git a/rpkid/rpki/rpkid.py b/rpkid/rpki/rpkid.py
index 5a9da606..f5fe6b7b 100644
--- a/rpkid/rpki/rpkid.py
+++ b/rpkid/rpki/rpkid.py
@@ -203,7 +203,7 @@ class main(object):
self.irdb_query(callback, errback, q_pdu)
- def irdb_query_gbr_requests(self, self_handle, parent_handles, callback, errback):
+ def irdb_query_ghostbuster_requests(self, self_handle, parent_handles, callback, errback):
"""
Ask IRDB about self's ghostbuster record requests.
"""
@@ -213,7 +213,7 @@ class main(object):
q_pdus = []
for parent_handle in parent_handles:
- q_pdu = rpki.left_right.list_gbr_requests_elt()
+ q_pdu = rpki.left_right.list_ghostbuster_requests_elt()
q_pdu.self_handle = self_handle
q_pdu.parent_handle = parent_handle
q_pdus.append(q_pdu)
@@ -381,31 +381,36 @@ class ca_obj(rpki.sql.sql_persistent):
"""
return ca_detail_obj.sql_fetch_where(self.gctx, "ca_id = %s", (self.ca_id,))
- def fetch_pending(self):
+ @property
+ def pending_ca_details(self):
"""
Fetch the pending ca_details for this CA, if any.
"""
return ca_detail_obj.sql_fetch_where(self.gctx, "ca_id = %s AND state = 'pending'", (self.ca_id,))
- def fetch_active(self):
+ @property
+ def active_ca_detail(self):
"""
Fetch the active ca_detail for this CA, if any.
"""
return ca_detail_obj.sql_fetch_where1(self.gctx, "ca_id = %s AND state = 'active'", (self.ca_id,))
- def fetch_deprecated(self):
+ @property
+ def deprecated_ca_details(self):
"""
Fetch deprecated ca_details for this CA, if any.
"""
return ca_detail_obj.sql_fetch_where(self.gctx, "ca_id = %s AND state = 'deprecated'", (self.ca_id,))
- def fetch_revoked(self):
+ @property
+ def revoked_ca_details(self):
"""
Fetch revoked ca_details for this CA, if any.
"""
return ca_detail_obj.sql_fetch_where(self.gctx, "ca_id = %s AND state = 'revoked'", (self.ca_id,))
- def fetch_issue_response_candidates(self):
+ @property
+ def issue_response_candidate_ca_details(self):
"""
Fetch ca_details which are candidates for consideration when
processing an up-down issue_response PDU.
@@ -489,7 +494,7 @@ class ca_obj(rpki.sql.sql_persistent):
self.gctx.checkpoint()
cb()
- ca_details = self.fetch_issue_response_candidates()
+ ca_details = self.issue_response_candidate_ca_details
if True:
for x in cert_map.itervalues():
@@ -591,7 +596,7 @@ class ca_obj(rpki.sql.sql_persistent):
rpki.log.trace()
parent = self.parent
- old_detail = self.fetch_active()
+ old_detail = self.active_ca_detail
new_detail = ca_detail_obj.create(self)
def done(issue_response):
@@ -615,14 +620,14 @@ class ca_obj(rpki.sql.sql_persistent):
def loop(iterator, ca_detail):
ca_detail.revoke(cb = iterator, eb = eb)
- rpki.async.iterator(self.fetch_deprecated(), loop, cb)
+ rpki.async.iterator(self.deprecated_ca_details, loop, cb)
def reissue(self, cb, eb):
"""
Reissue all current certificates issued by this CA.
"""
- ca_detail = self.fetch_active()
+ ca_detail = self.active_ca_detail
if ca_detail:
ca_detail.reissue(cb, eb)
else:
@@ -697,6 +702,13 @@ class ca_detail_obj(rpki.sql.sql_persistent):
return rpki.rpkid.roa_obj.sql_fetch_where(self.gctx, "ca_detail_id = %s", (self.ca_detail_id,))
@property
+ def ghostbusters(self):
+ """
+ Fetch all Ghostbuster objects that link to this ca_detail.
+ """
+ return rpki.rpkid.ghostbuster_obj.sql_fetch_where(self.gctx, "ca_detail_id = %s", (self.ca_detail_id,))
+
+ @property
def crl_uri(self):
"""
Return publication URI for this ca_detail's CRL.
@@ -740,6 +752,8 @@ class ca_detail_obj(rpki.sql.sql_persistent):
for roa in predecessor.roas:
roa.regenerate(publisher = publisher)
+ # Need to do something to regenerate ghostbusters here?
+
publisher.call_pubd(callback, errback)
def delete(self, ca, publisher, allow_failure = False):
@@ -756,6 +770,8 @@ class ca_detail_obj(rpki.sql.sql_persistent):
handler = False if allow_failure else None)
for roa in self.roas:
roa.revoke(publisher = publisher, allow_failure = allow_failure)
+ for ghostbuster in self.ghostbusters:
+ ghostbuster.revoke(publisher = publisher, allow_failure = allow_failure)
try:
latest_manifest = self.latest_manifest
except AttributeError:
@@ -825,6 +841,14 @@ class ca_detail_obj(rpki.sql.sql_persistent):
nextUpdate = nextUpdate.later(child_cert.cert.getNotAfter())
child_cert.revoke(publisher = publisher)
+ for roa in self.roas:
+ nextUpdate = nextUpdate.later(roa.cert.getNotAfter())
+ roa.revoke(publisher = publisher)
+
+ for ghostbuster in self.ghostbusters:
+ nextUpdate = nextUpdate.later(ghostbuster.cert.getNotAfter())
+ ghostbuster.revoke(publisher = publisher)
+
nextUpdate += crl_interval
self.generate_crl(publisher = publisher, nextUpdate = nextUpdate)
self.generate_manifest(publisher = publisher, nextUpdate = nextUpdate)
@@ -1013,6 +1037,7 @@ class ca_detail_obj(rpki.sql.sql_persistent):
objs = [(self.crl_uri_tail, self.latest_crl)]
objs.extend((c.uri_tail, c.cert) for c in self.child_certs)
objs.extend((r.uri_tail, r.roa) for r in self.roas if r.roa is not None)
+ objs.extend((g.uri_tail, g.ghostbuster) for g in self.ghostbusters)
self.latest_manifest = rpki.x509.SignedManifest.build(
serial = ca.next_manifest_number(),
@@ -1044,6 +1069,8 @@ class ca_detail_obj(rpki.sql.sql_persistent):
publisher = publication_queue()
for roa in self.roas:
roa.regenerate(publisher, fast = True)
+ for ghostbuster in self.ghostbusters:
+ ghostbuster.regenerate(publisher, fast = True)
for child_cert in self.child_certs:
child_cert.reissue(self, publisher, force = True)
publisher.call_pubd(cb, eb)
@@ -1444,7 +1471,7 @@ class roa_obj(rpki.sql.sql_persistent):
ca_detail = None
for parent in self.self.parents:
for ca in parent.cas:
- ca_detail = ca.fetch_active()
+ ca_detail = ca.active_ca_detail
if ca_detail is not None:
resources = ca_detail.latest_ca_cert.get_3779resources()
if v4.issubset(resources.v4) and v6.issubset(resources.v6):
@@ -1551,6 +1578,174 @@ class roa_obj(rpki.sql.sql_persistent):
return self.cert.gSKI() + ".roa"
+class ghostbuster_obj(rpki.sql.sql_persistent):
+ """
+ Ghostbusters record.
+ """
+
+ sql_template = rpki.sql.template(
+ "ghostbuster",
+ "ghostbuster_id",
+ "ca_detail_id",
+ "self_id",
+ "vcard",
+ ("ghostbuster", rpki.x509.Ghostbuster),
+ ("cert", rpki.x509.X509),
+ ("published", rpki.sundial.datetime))
+
+ ca_detail_id = None
+ cert = None
+ ghostbuster = None
+ published = None
+ vcard = None
+
+ @property
+ def self(self):
+ """
+ Fetch self object to which this ghostbuster_obj links.
+ """
+ return rpki.left_right.self_elt.sql_fetch(self.gctx, self.self_id)
+
+ @property
+ def ca_detail(self):
+ """
+ Fetch ca_detail object to which this ghostbuster_obj links.
+ """
+ return rpki.rpkid.ca_detail_obj.sql_fetch(self.gctx, self.ca_detail_id)
+
+ def __init__(self, gctx = None, self_id = None, ca_detail_id = None, vcard = None):
+ rpki.sql.sql_persistent.__init__(self)
+ self.gctx = gctx
+ self.self_id = self_id
+ self.ca_detail_id = ca_detail_id
+ self.vcard = vcard
+
+ # Defer marking new ghostbuster as dirty until .generate() has a chance to
+ # finish setup, otherwise we get SQL consistency errors.
+
+ def update(self, publisher, fast = False):
+ """
+ Bring this ghostbuster_obj up to date if necesssary.
+ """
+
+ if self.ghostbuster is None:
+ rpki.log.debug("Ghostbuster record doesn't exist, generating")
+ return self.generate(publisher = publisher, fast = fast)
+
+ regen_time = self.cert.getNotAfter() - rpki.sundial.timedelta(seconds = self.self.regen_margin)
+
+ if rpki.sundial.now() > regen_time:
+ rpki.log.debug("Ghostbuster record past threshold %s, regenerating" % (regen_time,))
+ return self.regenerate(publisher = publisher, fast = fast)
+
+ def generate(self, publisher, fast = False):
+ """
+ Generate a Ghostbuster record
+
+ Once we have the right covering certificate, we generate the
+ ghostbuster payload, generate a new EE certificate, use the EE
+ certificate to sign the ghostbuster payload, publish the result,
+ then throw away the private key for the EE cert. This is modeled
+ after the way we handle ROAs.
+
+ If fast is set, we leave generating the new manifest for our
+ caller to handle, presumably at the end of a bulk operation.
+ """
+
+ ca_detail = self.ca_detail
+ ca = ca_detail.ca
+
+ resources = rpki.resource_set.resource_bag.from_inheritance()
+ keypair = rpki.x509.RSA.generate()
+
+ self.cert = ca_detail.issue_ee(
+ ca = ca,
+ resources = resources,
+ subject_key = keypair.get_RSApublic(),
+ sia = ((rpki.oids.name2oid["id-ad-signedObject"], ("uri", self.uri_from_key(keypair))),))
+ self.ghostbuster = rpki.x509.Ghostbuster.build(self.vcard, keypair, (self.cert,))
+ self.published = rpki.sundial.now()
+ self.sql_store()
+
+ rpki.log.debug("Generating Ghostbuster record %r" % self.uri)
+ publisher.publish(cls = rpki.publication.ghostbuster_elt, uri = self.uri, obj = self.ghostbuster, repository = ca.parent.repository, handler = self.published_callback)
+ if not fast:
+ ca_detail.generate_manifest(publisher = publisher)
+
+ def published_callback(self, pdu):
+ """
+ Check publication result.
+ """
+ pdu.raise_if_error()
+ self.published = None
+ self.sql_mark_dirty()
+
+ def revoke(self, publisher, regenerate = False, allow_failure = False, fast = False):
+ """
+ Withdraw Ghostbuster associated with this ghostbuster_obj.
+
+ In order to preserve make-before-break properties without
+ duplicating code, this method also handles generating a
+ replacement ghostbuster when requested.
+
+ If allow_failure is set, failing to withdraw the ghostbuster will not be
+ considered an error.
+
+ If fast is set, SQL actions will be deferred, on the assumption
+ that our caller will handle regenerating CRL and manifest and
+ flushing the SQL cache.
+ """
+
+ ca_detail = self.ca_detail
+ cert = self.cert
+ ghostbuster = self.ghostbuster
+ uri = self.uri
+
+ if regenerate:
+ assert ca_detail.state == 'active'
+ self.generate(publisher = publisher, fast = fast)
+
+ rpki.log.debug("Withdrawing Ghostbuster record %r and revoking its EE cert" % uri)
+ rpki.rpkid.revoked_cert_obj.revoke(cert = cert, ca_detail = ca_detail)
+ publisher.withdraw(cls = rpki.publication.ghostbuster_elt, uri = uri, obj = ghostbuster, repository = ca_detail.ca.parent.repository,
+ handler = False if allow_failure else None)
+ self.sql_mark_deleted()
+ if not fast:
+ ca_detail.generate_crl(publisher = publisher)
+ ca_detail.generate_manifest(publisher = publisher)
+ self.gctx.sql.sweep()
+
+ def regenerate(self, publisher, fast = False):
+ """
+ Reissue Ghostbuster associated with this ghostbuster_obj.
+ """
+ if self.ghostbuster is None:
+ self.generate(publisher = publisher, fast = fast)
+ else:
+ self.revoke(publisher = publisher, regenerate = True, fast = fast)
+
+ def uri_from_key(self, key):
+ """
+ Return publication URI for a public key.
+ """
+ return self.ca_detail.ca.sia_uri + key.gSKI() + ".gbr"
+
+ @property
+ def uri(self):
+ """
+ Return the publication URI for this ghostbuster_obj's ghostbuster.
+ """
+ return self.ca_detail.ca.sia_uri + self.uri_tail
+
+ @property
+ def uri_tail(self):
+ """
+ Return the tail (filename portion) of the publication URI for this
+ ghostbuster_obj's ghostbuster.
+ """
+ return self.cert.gSKI() + ".gbr"
+
+
class publication_queue(object):
"""
Utility to simplify publication from within rpkid.
diff --git a/rpkid/rpki/up_down.py b/rpkid/rpki/up_down.py
index ae374532..37c27983 100644
--- a/rpkid/rpki/up_down.py
+++ b/rpkid/rpki/up_down.py
@@ -253,7 +253,7 @@ class list_pdu(base_elt):
for parent in child.parents:
for ca in parent.cas:
- ca_detail = ca.fetch_active()
+ ca_detail = ca.active_ca_detail
if not ca_detail:
continue
resources = ca_detail.latest_ca_cert.get_3779resources().intersection(irdb_resources)
@@ -367,7 +367,7 @@ class issue_pdu(base_elt):
# Check the request
self.pkcs10.check_valid_rpki()
ca = child.ca_from_class_name(self.class_name)
- ca_detail = ca.fetch_active()
+ ca_detail = ca.active_ca_detail
if ca_detail is None:
raise rpki.exceptions.NoActiveCA, "No active CA for class %r" % self.class_name
diff --git a/rpkid/rpki/x509.py b/rpkid/rpki/x509.py
index 7fda419d..63939690 100644
--- a/rpkid/rpki/x509.py
+++ b/rpkid/rpki/x509.py
@@ -1218,9 +1218,11 @@ class XML_CMS_object(CMS_object):
self.schema_check()
return self.saxify(self.get_content())
-class GBR_object(CMS_object):
+class Ghostbuster(CMS_object):
"""
- Class to hold CMS-wrapped VCard (Ghostbuster record).
+ Class to hold Ghostbusters record (CMS-wrapped VCard). This is
+ quite minimal because we treat the VCard as an opaque byte string
+ managed by the back-end.
"""
pem_converter = PEM_converter("GHOSTBUSTERS RECORD")
@@ -1243,7 +1245,7 @@ class GBR_object(CMS_object):
@classmethod
def build(cls, vcard, keypair, certs):
"""
- Build a Ghostbusters record.
+ Build a Ghostbuster record.
"""
self = cls()
self.set_content(vcard)
diff --git a/rpkid/rpkid.sql b/rpkid/rpkid.sql
index 2813c1dc..93712fe0 100644
--- a/rpkid/rpkid.sql
+++ b/rpkid/rpkid.sql
@@ -33,6 +33,7 @@
-- DROP TABLE commands must be in correct (reverse dependency) order
-- to satisfy FOREIGN KEY constraints.
+DROP TABLE IF EXISTS gbr;
DROP TABLE IF EXISTS ghostbuster;
DROP TABLE IF EXISTS roa_prefix;
DROP TABLE IF EXISTS roa;
@@ -219,7 +220,7 @@ CREATE TABLE ghostbuster (
ghostbuster_id SERIAL NOT NULL,
vcard LONGBLOB NOT NULL,
cert LONGBLOB NOT NULL,
- gbr LONGBLOB NOT NULL,
+ ghostbuster LONGBLOB NOT NULL,
published DATETIME,
self_id BIGINT UNSIGNED NOT NULL,
ca_detail_id BIGINT UNSIGNED NOT NULL,