aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRob Austein <sra@hactrn.net>2014-09-25 04:13:23 +0000
committerRob Austein <sra@hactrn.net>2014-09-25 04:13:23 +0000
commit370b80a69e48dd36bdfd0922dcf65318d6db68f4 (patch)
tree09cd5e3a49078fe32aacb18393135805752094a7
parent86bf27d64be4125c9390e485825fa77adc325e0d (diff)
Convert remaining rpkid up-down code from SAX to etree.
svn path=/branches/tk705/; revision=5970
-rw-r--r--rpki/exceptions.py5
-rw-r--r--rpki/left_right.py81
-rw-r--r--rpki/rootd.py3
-rw-r--r--rpki/rpkid.py127
-rw-r--r--rpki/rpkid_tasks.py6
-rw-r--r--rpki/up_down.py771
6 files changed, 187 insertions, 806 deletions
diff --git a/rpki/exceptions.py b/rpki/exceptions.py
index 86c7fa27..3ca8bd81 100644
--- a/rpki/exceptions.py
+++ b/rpki/exceptions.py
@@ -375,3 +375,8 @@ class WrongEKU(RPKI_Exception):
"""
Extended Key Usage extension does not match profile.
"""
+
+class UnexpectedUpDownResponse(RPKI_Exception):
+ """
+ Up-down message is not of the expected type.
+ """
diff --git a/rpki/left_right.py b/rpki/left_right.py
index 12632fb1..3b2c9b9f 100644
--- a/rpki/left_right.py
+++ b/rpki/left_right.py
@@ -740,10 +740,11 @@ class parent_elt(data_elt):
"""
def done(r_msg):
- cb(dict((rc.class_name, set(c.cert.gSKI() for c in rc.certs))
- for rc in r_msg.payload.classes))
-
- rpki.up_down.list_pdu.query(self, done, eb)
+ cb(dict((rc.get("class_name"),
+ set(rpki.x509.X509(Base64 = c.text).gSKI()
+ for c in rc.getiterator(rpki.up_down.tag_certificate)))
+ for rc in r_msg.getiterator(rpki.up_down.tag_class)))
+ self.up_down_list_query(done, eb)
def revoke_skis(self, rc_name, skis_to_revoke, cb, eb):
@@ -752,12 +753,10 @@ class parent_elt(data_elt):
"""
def loop(iterator, ski):
+ def revoked(r_pdu):
+ iterator()
logger.debug("Asking parent %r to revoke class %r, SKI %s", self, rc_name, ski)
- q_pdu = rpki.up_down.revoke_pdu()
- q_pdu.class_name = rc_name
- q_pdu.ski = ski
- self.query_up_down(q_pdu, lambda r_pdu: iterator(), eb)
-
+ self.up_down_revoke_query(rc_name, ski, revoked, eb)
rpki.async.iterator(skis_to_revoke, loop, cb)
@@ -826,7 +825,51 @@ class parent_elt(data_elt):
self.delete(cb, delete_parent = False)
- def query_up_down(self, q_pdu, cb, eb):
+ def _compose_up_down_query(self, query_type):
+ """
+ Compose top level element of an up-down query to this parent.
+ """
+
+ return Element(rpki.up_down.tag_message, nsmap = rpki.up_down.nsmap, version = rpki.up_down.version,
+ sender = self.sender_name, recipient = self.recipient_name, type = query_type)
+
+
+ def up_down_list_query(self, cb, eb):
+ """
+ Send an up-down list query to this parent.
+ """
+
+ q_msg = self._compose_up_down_query("list")
+ self.query_up_down(q_msg, cb, eb)
+
+
+ def up_down_issue_query(self, ca, ca_detail, cb, eb):
+ """
+ Send an up-down issue query to this parent.
+ """
+
+ pkcs10 = rpki.x509.PKCS10.create(
+ keypair = ca_detail.private_key_id,
+ is_ca = True,
+ caRepository = ca.sia_uri,
+ rpkiManifest = ca_detail.manifest_uri)
+ q_msg = self._compose_up_down_query("issue")
+ q_pdu = SubElement(q_msg, rpki.up_down.tag_request, class_name = ca.parent_resource_class)
+ q_pdu.text = pkcs10.get_Base64()
+ self.query_up_down(q_msg, cb, eb)
+
+
+ def up_down_revoke_query(self, class_name, ski, cb, eb):
+ """
+ Send an up-down revoke query to this parent.
+ """
+
+ q_msg = self._compose_up_down_query("revoke")
+ SubElement(q_msg, rpki.up_down.tag_key, class_name = class_name, ski = ski)
+ self.query_up_down(q_msg, cb, eb)
+
+
+ def query_up_down(self, q_msg, cb, eb):
"""
Client code for sending one up-down query PDU to this parent.
"""
@@ -838,25 +881,21 @@ class parent_elt(data_elt):
if bsc.signing_cert is None:
raise rpki.exceptions.BSCNotReady("BSC %r[%s] is not yet usable" % (bsc.bsc_handle, bsc.bsc_id))
- q_msg = rpki.up_down.message_pdu.make_query(
- payload = q_pdu,
- sender = self.sender_name,
- recipient = self.recipient_name)
-
- q_der = rpki.up_down.cms_msg().wrap(q_msg, bsc.private_key_id,
- bsc.signing_cert,
- bsc.signing_cert_crl)
+ q_der = rpki.up_down.cms_msg_no_sax().wrap(q_msg, bsc.private_key_id,
+ bsc.signing_cert,
+ bsc.signing_cert_crl)
def unwrap(r_der):
try:
- r_cms = rpki.up_down.cms_msg(DER = r_der)
+ r_cms = rpki.up_down.cms_msg_no_sax(DER = r_der)
r_msg = r_cms.unwrap((self.gctx.bpki_ta,
self.self.bpki_cert,
self.self.bpki_glue,
self.bpki_cms_cert,
self.bpki_cms_glue))
r_cms.check_replay_sql(self, self.peer_contact_uri)
- r_msg.payload.check_response()
+ rpki.up_down.check_response(r_msg, q_msg.get("type"))
+
except (SystemExit, rpki.async.ExitNow):
raise
except Exception, e:
@@ -1131,7 +1170,7 @@ class child_elt(data_elt):
def lose(e, quiet = False):
logger.exception("Unhandled exception serving child %r", self)
- rpki.up_down.generate_error_response(r_msg, description = e)
+ rpki.up_down.generate_error_response_from_exception(r_msg, e, q_type)
done()
bsc = self.bsc
diff --git a/rpki/rootd.py b/rpki/rootd.py
index 2bd7ce44..987d8356 100644
--- a/rpki/rootd.py
+++ b/rpki/rootd.py
@@ -355,9 +355,8 @@ class main(object):
self.rpkid_cms_timestamp = q_cms.check_replay(self.rpkid_cms_timestamp, request.path)
getattr(self, "handle_" + q_type)(q_msg, r_msg)
except Exception, e:
- # Should catch specific exceptions here to give better error codes.
logger.exception("Exception processing up-down %s message", q_type)
- rpki.up_down.generate_error_response(r_msg, description = e)
+ rpki.up_down.generate_error_response_from_exception(r_msg, e, q_type)
request.send_cms_response(rpki.up_down.cms_msg_no_sax().wrap(r_msg, self.rootd_bpki_key, self.rootd_bpki_cert,
self.rootd_bpki_crl if self.include_bpki_crl else None))
except Exception, e:
diff --git a/rpki/rpkid.py b/rpki/rpkid.py
index 2be7e00f..07a81f79 100644
--- a/rpki/rpkid.py
+++ b/rpki/rpkid.py
@@ -119,17 +119,6 @@ class main(object):
self.publication_kludge_base = self.cfg.get("publication-kludge-base", "publication/")
- # Icky hack to let Iain do some testing quickly, should go away
- # once we sort out whether we can make this change permanent.
- #
- # OK, the stuff to add router certificate support makes enough
- # other changes that we're going to need a migration program in
- # any case, so might as well throw the switch here too, or at
- # least find out if it (still) works as expected.
-
- self.merge_publication_directories = self.cfg.getboolean("merge_publication_directories",
- True)
-
self.use_internal_cron = self.cfg.getboolean("use-internal-cron", True)
self.initial_delay = random.randint(self.cfg.getint("initial-delay-min", 10),
@@ -519,16 +508,12 @@ class ca_obj(rpki.sql.sql_persistent):
information and the parent's up-down protocol list_response PDU.
"""
- sia_uri = rc.suggested_sia_head and rc.suggested_sia_head.rsync()
- if not sia_uri or not sia_uri.startswith(parent.sia_base):
+ sia_uri = rc.get("suggested_sia_head", "")
+ if not sia_uri.startswith("rsync://") or not sia_uri.startswith(parent.sia_base):
sia_uri = parent.sia_base
if not sia_uri.endswith("/"):
raise rpki.exceptions.BadURISyntax("SIA URI must end with a slash: %s" % sia_uri)
- # With luck this can go away sometime soon.
- if self.gctx.merge_publication_directories:
- return sia_uri
- else:
- return sia_uri + str(self.ca_id) + "/"
+ return sia_uri
def check_for_updates(self, parent, rc, cb, eb):
"""
@@ -545,29 +530,40 @@ class ca_obj(rpki.sql.sql_persistent):
self.sia_uri = sia_uri
self.sql_mark_dirty()
- rc_resources = rc.to_resource_bag()
- cert_map = dict((c.cert.get_SKI(), c) for c in rc.certs)
+ class_name = rc.get("class_name")
+
+ rc_resources = rpki.resource_set.resource_bag(
+ rc.get("resource_set_as"),
+ rc.get("resource_set_ipv4"),
+ rc.get("resource_set_ipv6"),
+ rc.get("resource_set_notafter"))
+
+ cert_map = {}
+ for c in rc.getiterator(rpki.up_down.tag_certificate):
+ x = rpki.x509.X509(Base64 = c.text)
+ u = rpki.up_down.multi_uri(c.get("cert_url")).rsync()
+ cert_map[x.gSKI()] = (x, u)
def loop(iterator, ca_detail):
self.gctx.checkpoint()
- rc_cert = cert_map.pop(ca_detail.public_key.get_SKI(), None)
+ rc_cert, rc_cert_uri = cert_map.pop(ca_detail.public_key.gSKI(), (None, None))
if rc_cert is None:
logger.warning("SKI %s in resource class %s is in database but missing from list_response to %s from %s, "
"maybe parent certificate went away?",
- ca_detail.public_key.gSKI(), rc.class_name, parent.self.self_handle, parent.parent_handle)
+ ca_detail.public_key.gSKI(), class_name, parent.self.self_handle, parent.parent_handle)
publisher = publication_queue()
ca_detail.delete(ca = ca_detail.ca, publisher = publisher)
return publisher.call_pubd(iterator, eb)
else:
- if ca_detail.state == "active" and ca_detail.ca_cert_uri != rc_cert.cert_url.rsync():
- logger.debug("AIA changed: was %s now %s", ca_detail.ca_cert_uri, rc_cert.cert_url.rsync())
- ca_detail.ca_cert_uri = rc_cert.cert_url.rsync()
+ if ca_detail.state == "active" and ca_detail.ca_cert_uri != rc_cert_uri:
+ logger.debug("AIA changed: was %s now %s", ca_detail.ca_cert_uri, rc_cert_uri)
+ ca_detail.ca_cert_uri = rc_cert_uri
ca_detail.sql_mark_dirty()
if ca_detail.state in ("pending", "active"):
@@ -579,7 +575,7 @@ class ca_obj(rpki.sql.sql_persistent):
if (ca_detail.state == "pending" or
sia_uri_changed or
- ca_detail.latest_ca_cert != rc_cert.cert or
+ ca_detail.latest_ca_cert != rc_cert or
ca_detail.latest_ca_cert.getNotAfter() != rc_resources.valid_until or
current_resources.undersized(rc_resources) or
current_resources.oversized(rc_resources)):
@@ -597,9 +593,7 @@ class ca_obj(rpki.sql.sql_persistent):
def done():
if cert_map:
logger.warning("Unknown certificate SKI%s %s in resource class %s in list_response to %s from %s, maybe you want to \"revoke_forgotten\"?",
- "" if len(cert_map) == 1 else "s",
- ", ".join(c.cert.gSKI() for c in cert_map.values()),
- rc.class_name, parent.self.self_handle, parent.parent_handle)
+ "" if len(cert_map) == 1 else "s", ", ".join(cert_map), class_name, parent.self.self_handle, parent.parent_handle)
self.gctx.sql.sweep()
self.gctx.checkpoint()
cb()
@@ -607,26 +601,25 @@ class ca_obj(rpki.sql.sql_persistent):
ca_details = self.issue_response_candidate_ca_details
if True:
- skis_parent = set(x.cert.gSKI()
- for x in cert_map.itervalues())
+ skis_parent = set(cert_map)
skis_me = set(x.latest_ca_cert.gSKI()
for x in ca_details
if x.latest_ca_cert is not None)
for ski in skis_parent & skis_me:
logger.debug("Parent %s agrees that %s has SKI %s in resource class %s",
- parent.parent_handle, parent.self.self_handle, ski, rc.class_name)
+ parent.parent_handle, parent.self.self_handle, ski, class_name)
for ski in skis_parent - skis_me:
logger.debug("Parent %s thinks %s has SKI %s in resource class %s but I don't think so",
- parent.parent_handle, parent.self.self_handle, ski, rc.class_name)
+ parent.parent_handle, parent.self.self_handle, ski, class_name)
for ski in skis_me - skis_parent:
logger.debug("I think %s has SKI %s in resource class %s but parent %s doesn't think so",
- parent.self.self_handle, ski, rc.class_name, parent.parent_handle)
+ parent.self.self_handle, ski, class_name, parent.parent_handle)
if ca_details:
rpki.async.iterator(ca_details, loop, done)
else:
logger.warning("Existing resource class %s to %s from %s with no certificates, rekeying",
- rc.class_name, parent.self.self_handle, parent.parent_handle)
+ class_name, parent.self.self_handle, parent.parent_handle)
self.gctx.checkpoint()
self.rekey(cb, eb)
@@ -640,7 +633,7 @@ class ca_obj(rpki.sql.sql_persistent):
self = cls()
self.gctx = parent.gctx
self.parent_id = parent.parent_id
- self.parent_resource_class = rc.class_name
+ self.parent_resource_class = rc.get("class_name")
self.sql_store()
try:
self.sia_uri = self.construct_sia_uri(parent, rc)
@@ -649,18 +642,18 @@ class ca_obj(rpki.sql.sql_persistent):
raise
ca_detail = ca_detail_obj.create(self)
- def done(issue_response):
- c = issue_response.payload.classes[0].certs[0]
- logger.debug("CA %r received certificate %s", self, c.cert_url)
+ def done(r_msg):
+ c = r_msg[0][0]
+ logger.debug("CA %r received certificate %s", self, c.get("cert_url"))
ca_detail.activate(
ca = self,
- cert = c.cert,
- uri = c.cert_url,
+ cert = rpki.x509.X509(Base64 = c.text),
+ uri = c.get("cert_url"),
callback = cb,
errback = eb)
logger.debug("Sending issue request to %r from %r", parent, self.create)
- rpki.up_down.issue_pdu.query(parent, self, ca_detail, done, eb)
+ parent.up_down_issue_query(self, ca_detail, done, eb)
def delete(self, parent, callback):
"""
@@ -727,19 +720,19 @@ class ca_obj(rpki.sql.sql_persistent):
old_detail = self.active_ca_detail
new_detail = ca_detail_obj.create(self)
- def done(issue_response):
- c = issue_response.payload.classes[0].certs[0]
- logger.debug("CA %r received certificate %s", self, c.cert_url)
+ def done(r_msg):
+ c = r_msg[0][0]
+ logger.debug("CA %r received certificate %s", self, c.get("cert_url"))
new_detail.activate(
ca = self,
- cert = c.cert,
- uri = c.cert_url,
+ cert = rpki.x509.X509(Base64 = c.text),
+ uri = c.get("cert_url"),
predecessor = old_detail,
callback = cb,
errback = eb)
logger.debug("Sending issue request to %r from %r", parent, self.rekey)
- rpki.up_down.issue_pdu.query(parent, self, new_detail, done, eb)
+ parent.up_down_issue_query(self, new_detail, done, eb)
def revoke(self, cb, eb, revoke_all = False):
"""
@@ -942,7 +935,7 @@ class ca_detail_obj(rpki.sql.sql_persistent):
publisher = publication_queue()
self.latest_ca_cert = cert
- self.ca_cert_uri = uri.rsync()
+ self.ca_cert_uri = uri
self.generate_manifest_cert()
self.state = "active"
self.generate_crl(publisher = publisher)
@@ -1033,13 +1026,18 @@ class ca_detail_obj(rpki.sql.sql_persistent):
ca = self.ca
parent = ca.parent
+ class_name = ca.parent_resource_class
+ gski = self.latest_ca_cert.gSKI()
def parent_revoked(r_msg):
- if r_msg.payload.ski != self.latest_ca_cert.gSKI():
+ if r_msg[0].get("class_name") != class_name:
+ raise rpki.exceptions.ResourceClassMismatch
+
+ if r_msg[0].get("ski") != gski:
raise rpki.exceptions.SKIMismatch
- logger.debug("Parent revoked %s, starting cleanup", self.latest_ca_cert.gSKI())
+ logger.debug("Parent revoked %s, starting cleanup", gski)
crl_interval = rpki.sundial.timedelta(seconds = parent.self.crl_interval)
@@ -1077,8 +1075,9 @@ class ca_detail_obj(rpki.sql.sql_persistent):
self.sql_mark_dirty()
publisher.call_pubd(cb, eb)
- logger.debug("Asking parent to revoke CA certificate %s", self.latest_ca_cert.gSKI())
- rpki.up_down.revoke_pdu.query(ca, self.latest_ca_cert.gSKI(), parent_revoked, eb)
+ logger.debug("Asking parent to revoke CA certificate %s", gski)
+ parent.up_down_revoke_query(class_name, gski, parent_revoked, eb)
+
def update(self, parent, ca, rc, sia_uri_changed, old_resources, callback, errback):
"""
@@ -1086,24 +1085,27 @@ class ca_detail_obj(rpki.sql.sql_persistent):
children of this ca_detail.
"""
- def issued(issue_response):
- c = issue_response.payload.classes[0].certs[0]
- logger.debug("CA %r received certificate %s", self, c.cert_url)
+ def issued(r_msg):
+ c = r_msg[0][0]
+ cert = rpki.x509.X509(Base64 = c.text)
+ cert_url = c.get("cert_url")
+
+ logger.debug("CA %r received certificate %s", self, cert_url)
if self.state == "pending":
return self.activate(
ca = ca,
- cert = c.cert,
- uri = c.cert_url,
+ cert = cert,
+ uri = cert_url,
callback = callback,
errback = errback)
- validity_changed = self.latest_ca_cert is None or self.latest_ca_cert.getNotAfter() != c.cert.getNotAfter()
+ validity_changed = self.latest_ca_cert is None or self.latest_ca_cert.getNotAfter() != cert.getNotAfter()
publisher = publication_queue()
- if self.latest_ca_cert != c.cert:
- self.latest_ca_cert = c.cert
+ if self.latest_ca_cert != cert:
+ self.latest_ca_cert = cert
self.sql_mark_dirty()
self.generate_manifest_cert()
self.generate_crl(publisher = publisher)
@@ -1131,7 +1133,8 @@ class ca_detail_obj(rpki.sql.sql_persistent):
publisher.call_pubd(callback, errback)
logger.debug("Sending issue request to %r from %r", parent, self.update)
- rpki.up_down.issue_pdu.query(parent, ca, self, issued, errback)
+ parent.up_down_issue_query(ca, self, issued, errback)
+
@classmethod
def create(cls, ca):
diff --git a/rpki/rpkid_tasks.py b/rpki/rpkid_tasks.py
index 959d4223..5405834f 100644
--- a/rpki/rpkid_tasks.py
+++ b/rpki/rpkid_tasks.py
@@ -176,12 +176,12 @@ class PollParentTask(AbstractTask):
def parent_loop(self, parent_iterator, parent):
self.parent_iterator = parent_iterator
self.parent = parent
- rpki.up_down.list_pdu.query(parent, self.got_list, self.list_failed)
+ parent.up_down_list_query(self.got_list, self.list_failed)
def got_list(self, r_msg):
self.ca_map = dict((ca.parent_resource_class, ca) for ca in self.parent.cas)
self.gctx.checkpoint()
- rpki.async.iterator(r_msg.payload.classes, self.class_loop, self.class_done)
+ rpki.async.iterator(r_msg.getiterator(rpki.up_down.tag_class), self.class_loop, self.class_done)
def list_failed(self, e):
logger.exception("Couldn't get resource class list from parent %r, skipping", self.parent)
@@ -191,7 +191,7 @@ class PollParentTask(AbstractTask):
self.gctx.checkpoint()
self.class_iterator = class_iterator
try:
- ca = self.ca_map.pop(rc.class_name)
+ ca = self.ca_map.pop(rc.get("class_name"))
except KeyError:
rpki.rpkid.ca_obj.create(self.parent, rc, class_iterator, self.class_create_failed)
else:
diff --git a/rpki/up_down.py b/rpki/up_down.py
index 49d330bd..7b392640 100644
--- a/rpki/up_down.py
+++ b/rpki/up_down.py
@@ -31,7 +31,7 @@ import rpki.log
import rpki.xml_utils
import rpki.relaxng
-from lxml.etree import Element, SubElement
+from lxml.etree import Element, SubElement, tostring as ElementToString
logger = logging.getLogger(__name__)
@@ -49,105 +49,12 @@ tag_request = xmlns + "request"
tag_status = xmlns + "status"
-error_response_codes = {
- 1101 : "Already processing request",
- 1102 : "Version number error",
- 1103 : "Unrecognised request type",
- 1201 : "Request - no such resource class",
- 1202 : "Request - no resources allocated in resource class",
- 1203 : "Request - badly formed certificate request",
- 1301 : "Revoke - no such resource class",
- 1302 : "Revoke - no such key",
- 2001 : "Internal Server Error - Request not performed" }
-
-
-def generate_error_response(r_msg, status = 2001, description = None):
- """
- Generate an error response. If STATUS is given, it specifies the
- numeric code to use, otherwise we default to "internal error".
- If DESCRIPTION is specified, we use it as the description, otherwise
- we just use the default string associated with STATUS.
- """
-
- assert status in error_response_codes
- del r_msg[:]
- r_msg.set("type", "error_response")
- SubElement(r_msg, tag_status).text = str(status)
- se = SubElement(r_msg, tag_description)
- se.set("{http://www.w3.org/XML/1998/namespace}lang", "en-US")
- se.text = str(description or error_response_codes[status])
-
-
-class base_elt(object):
- """
- Generic PDU object.
-
- Virtual class, just provides some default methods.
- """
-
- def startElement(self, stack, name, attrs):
- """
- Ignore startElement() if there's no specific handler.
-
- Some elements have no attributes and we only care about their
- text content.
- """
-
- pass
-
- def endElement(self, stack, name, text):
- """
- Ignore endElement() if there's no specific handler.
-
- If we don't need to do anything else, just pop the stack.
- """
-
- stack.pop()
-
- def make_elt(self, name, *attrs):
- """
- Construct a element, copying over a set of attributes.
- """
-
- elt = lxml.etree.Element(xmlns + name, nsmap = nsmap)
- for key in attrs:
- val = getattr(self, key, None)
- if val is not None:
- elt.set(key, str(val))
- return elt
-
- def make_b64elt(self, elt, name, value):
- """
- Construct a sub-element with Base64 text content.
- """
-
- if value is not None and not value.empty():
- lxml.etree.SubElement(elt, xmlns + name, nsmap = nsmap).text = value.get_Base64()
-
- def serve_pdu(self, q_msg, r_msg, child, callback, errback):
- """
- Default PDU handler to catch unexpected types.
- """
-
- raise rpki.exceptions.BadQuery("Unexpected query type %s" % q_msg.type)
-
- def check_response(self):
- """
- Placeholder for response checking.
- """
-
- pass
-
class multi_uri(list):
"""
- Container for a set of URIs.
+ Container for a set of URIs. This probably could be simplified.
"""
def __init__(self, ini):
- """
- Initialize a set of URIs, which includes basic some syntax checking.
- """
-
list.__init__(self)
if isinstance(ini, (list, tuple)):
self[:] = ini
@@ -160,10 +67,6 @@ class multi_uri(list):
raise TypeError
def __str__(self):
- """
- Convert a multi_uri back to a string representation.
- """
-
return ",".join(self)
def rsync(self):
@@ -176,653 +79,85 @@ class multi_uri(list):
return s
return None
-class certificate_elt(base_elt):
- """
- Up-Down protocol representation of an issued certificate.
- """
-
- def startElement(self, stack, name, attrs):
- """
- Handle attributes of <certificate/> element.
- """
-
- assert name == "certificate", "Unexpected name %s, stack %s" % (name, stack)
- self.cert_url = multi_uri(attrs["cert_url"])
- self.req_resource_set_as = rpki.resource_set.resource_set_as(attrs.get("req_resource_set_as"))
- self.req_resource_set_ipv4 = rpki.resource_set.resource_set_ipv4(attrs.get("req_resource_set_ipv4"))
- self.req_resource_set_ipv6 = rpki.resource_set.resource_set_ipv6(attrs.get("req_resource_set_ipv6"))
-
- def endElement(self, stack, name, text):
- """
- Handle text content of a <certificate/> element.
- """
-
- assert name == "certificate", "Unexpected name %s, stack %s" % (name, stack)
- self.cert = rpki.x509.X509(Base64 = text)
- stack.pop()
-
- def toXML(self):
- """
- Generate a <certificate/> element.
- """
-
- elt = self.make_elt("certificate", "cert_url",
- "req_resource_set_as", "req_resource_set_ipv4", "req_resource_set_ipv6")
- elt.text = self.cert.get_Base64()
- return elt
-
-class class_elt(base_elt):
- """
- Up-Down protocol representation of a resource class.
- """
-
- issuer = None
-
- def __init__(self):
- """
- Initialize class_elt.
- """
-
- base_elt.__init__(self)
- self.certs = []
-
- def startElement(self, stack, name, attrs):
- """
- Handle <class/> elements and their children.
- """
-
- if name == "certificate":
- cert = certificate_elt()
- self.certs.append(cert)
- stack.append(cert)
- cert.startElement(stack, name, attrs)
- elif name != "issuer":
- assert name == "class", "Unexpected name %s, stack %s" % (name, stack)
- self.class_name = attrs["class_name"]
- self.cert_url = multi_uri(attrs["cert_url"])
- self.suggested_sia_head = attrs.get("suggested_sia_head")
- self.resource_set_as = rpki.resource_set.resource_set_as(attrs["resource_set_as"])
- self.resource_set_ipv4 = rpki.resource_set.resource_set_ipv4(attrs["resource_set_ipv4"])
- self.resource_set_ipv6 = rpki.resource_set.resource_set_ipv6(attrs["resource_set_ipv6"])
- self.resource_set_notafter = rpki.sundial.datetime.fromXMLtime(attrs.get("resource_set_notafter"))
-
- def endElement(self, stack, name, text):
- """
- Handle <class/> elements and their children.
- """
-
- if name == "issuer":
- self.issuer = rpki.x509.X509(Base64 = text)
- else:
- assert name == "class", "Unexpected name %s, stack %s" % (name, stack)
- stack.pop()
-
- def toXML(self):
- """
- Generate a <class/> element.
- """
-
- elt = self.make_elt("class", "class_name", "cert_url", "resource_set_as",
- "resource_set_ipv4", "resource_set_ipv6",
- "resource_set_notafter", "suggested_sia_head")
- elt.extend(i.toXML() for i in self.certs)
- self.make_b64elt(elt, "issuer", self.issuer)
- return elt
-
- def to_resource_bag(self):
- """
- Build a resource_bag from from this <class/> element.
- """
-
- return rpki.resource_set.resource_bag(self.resource_set_as,
- self.resource_set_ipv4,
- self.resource_set_ipv6,
- self.resource_set_notafter)
-
- def from_resource_bag(self, bag):
- """
- Set resources of this class element from a resource_bag.
- """
-
- self.resource_set_as = bag.asn
- self.resource_set_ipv4 = bag.v4
- self.resource_set_ipv6 = bag.v6
- self.resource_set_notafter = bag.valid_until
-
-class list_pdu(base_elt):
- """
- Up-Down protocol "list" PDU.
- """
-
- def toXML(self):
- """
- Generate (empty) payload of "list" PDU.
- """
-
- return []
-
- def serve_pdu(self, q_msg, r_msg, child, callback, errback):
- """
- Serve one "list" PDU.
- """
-
- def handle(irdb_resources):
-
- r_msg.payload = list_response_pdu()
-
- if irdb_resources.valid_until < rpki.sundial.now():
- logger.debug("Child %s's resources expired %s", child.child_handle, irdb_resources.valid_until)
- else:
- for parent in child.parents:
- for ca in parent.cas:
- ca_detail = ca.active_ca_detail
- if not ca_detail:
- logger.debug("No active ca_detail, can't issue to %s", child.child_handle)
- continue
- resources = ca_detail.latest_ca_cert.get_3779resources() & irdb_resources
- if resources.empty():
- logger.debug("No overlap between received resources and what child %s should get ([%s], [%s])",
- child.child_handle, ca_detail.latest_ca_cert.get_3779resources(), irdb_resources)
- continue
- rc = class_elt()
- rc.class_name = str(ca.ca_id)
- rc.cert_url = multi_uri(ca_detail.ca_cert_uri)
- rc.from_resource_bag(resources)
- for child_cert in child.fetch_child_certs(ca_detail = ca_detail):
- c = certificate_elt()
- c.cert_url = multi_uri(child_cert.uri)
- c.cert = child_cert.cert
- rc.certs.append(c)
- rc.issuer = ca_detail.latest_ca_cert
- r_msg.payload.classes.append(rc)
-
- callback()
-
- self.gctx.irdb_query_child_resources(child.self.self_handle, child.child_handle, handle, errback)
-
- @classmethod
- def query(cls, parent, cb, eb):
- """
- Send a "list" query to parent.
- """
-
- try:
- logger.info('Sending "list" request to parent %s', parent.parent_handle)
- parent.query_up_down(cls(), cb, eb)
- except (rpki.async.ExitNow, SystemExit):
- raise
- except Exception, e:
- eb(e)
-
-class class_response_syntax(base_elt):
- """
- Syntax for Up-Down protocol "list_response" and "issue_response" PDUs.
- """
-
- def __init__(self):
- """
- Initialize class_response_syntax.
- """
-
- base_elt.__init__(self)
- self.classes = []
-
- def startElement(self, stack, name, attrs):
- """
- Handle "list_response" and "issue_response" PDUs.
- """
-
- assert name == "class", "Unexpected name %s, stack %s" % (name, stack)
- c = class_elt()
- self.classes.append(c)
- stack.append(c)
- c.startElement(stack, name, attrs)
-
- def toXML(self):
- """
- Generate payload of "list_response" and "issue_response" PDUs.
- """
- return [c.toXML() for c in self.classes]
-
-class list_response_pdu(class_response_syntax):
- """
- Up-Down protocol "list_response" PDU.
- """
-
- pass
-
-class issue_pdu(base_elt):
- """
- Up-Down protocol "issue" PDU.
- """
-
- def startElement(self, stack, name, attrs):
- """
- Handle "issue" PDU.
- """
-
- assert name == "request", "Unexpected name %s, stack %s" % (name, stack)
- self.class_name = attrs["class_name"]
- self.req_resource_set_as = rpki.resource_set.resource_set_as(attrs.get("req_resource_set_as"))
- self.req_resource_set_ipv4 = rpki.resource_set.resource_set_ipv4(attrs.get("req_resource_set_ipv4"))
- self.req_resource_set_ipv6 = rpki.resource_set.resource_set_ipv6(attrs.get("req_resource_set_ipv6"))
-
- def endElement(self, stack, name, text):
- """
- Handle "issue" PDU.
- """
-
- assert name == "request", "Unexpected name %s, stack %s" % (name, stack)
- self.pkcs10 = rpki.x509.PKCS10(Base64 = text)
- stack.pop()
-
- def toXML(self):
- """
- Generate payload of "issue" PDU.
- """
-
- elt = self.make_elt("request", "class_name", "req_resource_set_as",
- "req_resource_set_ipv4", "req_resource_set_ipv6")
- elt.text = self.pkcs10.get_Base64()
- return [elt]
-
- def serve_pdu(self, q_msg, r_msg, child, callback, errback):
- """
- Serve one issue request PDU.
- """
-
- # Subsetting not yet implemented, this is the one place where we
- # have to handle it, by reporting that we're lame.
-
- if self.req_resource_set_as or \
- self.req_resource_set_ipv4 or \
- self.req_resource_set_ipv6:
- raise rpki.exceptions.NotImplementedYet("req_* attributes not implemented yet, sorry")
-
- # Check the request
- self.pkcs10.check_valid_request_ca()
- ca = child.ca_from_class_name(self.class_name)
- ca_detail = ca.active_ca_detail
- if ca_detail is None:
- raise rpki.exceptions.NoActiveCA("No active CA for class %r" % self.class_name)
-
- # Check current cert, if any
-
- def got_resources(irdb_resources):
-
- if irdb_resources.valid_until < rpki.sundial.now():
- raise rpki.exceptions.IRDBExpired("IRDB entry for child %s expired %s" % (
- child.child_handle, irdb_resources.valid_until))
-
- resources = irdb_resources & ca_detail.latest_ca_cert.get_3779resources()
- resources.valid_until = irdb_resources.valid_until
- req_key = self.pkcs10.getPublicKey()
- req_sia = self.pkcs10.get_SIA()
- child_cert = child.fetch_child_certs(ca_detail = ca_detail, ski = req_key.get_SKI(), unique = True)
-
- # Generate new cert or regenerate old one if necessary
-
- publisher = rpki.rpkid.publication_queue()
-
- if child_cert is None:
- child_cert = ca_detail.issue(
- ca = ca,
- child = child,
- subject_key = req_key,
- sia = req_sia,
- resources = resources,
- publisher = publisher)
- else:
- child_cert = child_cert.reissue(
- ca_detail = ca_detail,
- sia = req_sia,
- resources = resources,
- publisher = publisher)
-
- def done():
- c = certificate_elt()
- c.cert_url = multi_uri(child_cert.uri)
- c.cert = child_cert.cert
- rc = class_elt()
- rc.class_name = self.class_name
- rc.cert_url = multi_uri(ca_detail.ca_cert_uri)
- rc.from_resource_bag(resources)
- rc.certs.append(c)
- rc.issuer = ca_detail.latest_ca_cert
- r_msg.payload = issue_response_pdu()
- r_msg.payload.classes.append(rc)
- callback()
-
- self.gctx.sql.sweep()
- assert child_cert and child_cert.sql_in_db
- publisher.call_pubd(done, errback)
-
- self.gctx.irdb_query_child_resources(child.self.self_handle, child.child_handle, got_resources, errback)
-
- @classmethod
- def query(cls, parent, ca, ca_detail, callback, errback):
- """
- Send an "issue" request to parent associated with ca.
- """
-
- assert ca_detail is not None and ca_detail.state in ("pending", "active")
- self = cls()
- self.class_name = ca.parent_resource_class
- self.pkcs10 = rpki.x509.PKCS10.create(
- keypair = ca_detail.private_key_id,
- is_ca = True,
- caRepository = ca.sia_uri,
- rpkiManifest = ca_detail.manifest_uri)
- logger.info('Sending "issue" request to parent %s', parent.parent_handle)
- parent.query_up_down(self, callback, errback)
-
-class issue_response_pdu(class_response_syntax):
- """
- Up-Down protocol "issue_response" PDU.
- """
-
- def check_response(self):
- """
- Check whether this looks like a reasonable issue_response PDU.
- XML schema should be tighter for this response.
- """
-
- if len(self.classes) != 1 or len(self.classes[0].certs) != 1:
- raise rpki.exceptions.BadIssueResponse
-
-class revoke_syntax(base_elt):
- """
- Syntax for Up-Down protocol "revoke" and "revoke_response" PDUs.
- """
-
- def startElement(self, stack, name, attrs):
- """
- Handle "revoke" PDU.
- """
+error_response_codes = {
+ 1101 : "Already processing request",
+ 1102 : "Version number error",
+ 1103 : "Unrecognised request type",
+ 1201 : "Request - no such resource class",
+ 1202 : "Request - no resources allocated in resource class",
+ 1203 : "Request - badly formed certificate request",
+ 1301 : "Revoke - no such resource class",
+ 1302 : "Revoke - no such key",
+ 2001 : "Internal Server Error - Request not performed" }
- self.class_name = attrs["class_name"]
- self.ski = attrs["ski"]
- def toXML(self):
- """
- Generate payload of "revoke" PDU.
- """
+exception_map = {
+ rpki.exceptions.NoActiveCA : 1202,
+ (rpki.exceptions.ClassNameUnknown, "revoke") : 1301,
+ rpki.exceptions.ClassNameUnknown : 1201,
+ (rpki.exceptions.NotInDatabase, "revoke") : 1302 }
- return [self.make_elt("key", "class_name", "ski")]
-class revoke_pdu(revoke_syntax):
+def check_response(r_msg, q_type):
"""
- Up-Down protocol "revoke" PDU.
+ Additional checks beyond the XML schema for whether this looks like
+ a reasonable up-down response message.
"""
- def get_SKI(self):
- """
- Convert g(SKI) encoding from PDU back to raw SKI.
- """
-
- return base64.urlsafe_b64decode(self.ski + "=")
-
- def serve_pdu(self, q_msg, r_msg, child, cb, eb):
- """
- Serve one revoke request PDU.
- """
+ r_type = r_msg.get("type")
- def done():
- r_msg.payload = revoke_response_pdu()
- r_msg.payload.class_name = self.class_name
- r_msg.payload.ski = self.ski
- cb()
-
- ca = child.ca_from_class_name(self.class_name)
- publisher = rpki.rpkid.publication_queue()
- for ca_detail in ca.ca_details:
- for child_cert in child.fetch_child_certs(ca_detail = ca_detail, ski = self.get_SKI()):
- child_cert.revoke(publisher = publisher)
- self.gctx.sql.sweep()
- publisher.call_pubd(done, eb)
-
- @classmethod
- def query(cls, ca, gski, cb, eb):
- """
- Send a "revoke" request for certificate(s) named by gski to parent associated with ca.
- """
+ if r_type == "error_response":
+ raise rpki.exceptions.UpstreamError(error_response_codes[int(r_msg.findtext(tag_status))])
- parent = ca.parent
- self = cls()
- self.class_name = ca.parent_resource_class
- self.ski = gski
- logger.info('Sending "revoke" request for SKI %s to parent %s', gski, parent.parent_handle)
- parent.query_up_down(self, cb, eb)
+ if r_type != q_type + "_response":
+ raise UnexpectedUpDownResponse
-class revoke_response_pdu(revoke_syntax):
- """
- Up-Down protocol "revoke_response" PDU.
- """
+ if r_type == "issue_response" and (len(r_msg) != 1 or len(r_msg[0]) != 2):
+ logger.debug("Weird issue_response %r: len(r_msg) %s len(r_msg[0]) %s",
+ r_msg, len(r_msg), len(r_msg[0]) if len(r_msg) else None)
+ logger.debug("Offending message\n%s", ElementToString(r_msg))
+ raise rpki.exceptions.BadIssueResponse
- pass
-class error_response_pdu(base_elt):
+def generate_error_response(r_msg, status = 2001, description = None):
"""
- Up-Down protocol "error_response" PDU.
+ Generate an error response. If status is given, it specifies the
+ numeric code to use, otherwise we default to "internal error".
+ If description is specified, we use it as the description, otherwise
+ we just use the default string associated with status.
"""
- codes = error_response_codes
-
- exceptions = {
- rpki.exceptions.NoActiveCA : 1202,
- (rpki.exceptions.ClassNameUnknown, revoke_pdu) : 1301,
- rpki.exceptions.ClassNameUnknown : 1201,
- (rpki.exceptions.NotInDatabase, revoke_pdu) : 1302 }
-
- def __init__(self, exception = None, request_payload = None):
- """
- Initialize an error_response PDU from an exception object.
- """
-
- base_elt.__init__(self)
- if exception is not None:
- logger.debug("Constructing up-down error response from exception %s", exception)
- exception_type = type(exception)
- request_type = None if request_payload is None else type(request_payload)
- logger.debug("Constructing up-down error response: exception_type %s, request_type %s",
- exception_type, request_type)
- if False:
- self.status = self.exceptions.get((exception_type, request_type),
- self.exceptions.get(exception_type, 2001))
- else:
- self.status = self.exceptions.get((exception_type, request_type))
- if self.status is None:
- logger.debug("No request-type-specific match, trying exception match")
- self.status = self.exceptions.get(exception_type)
- if self.status is None:
- logger.debug("No exception match either, defaulting")
- self.status = 2001
- self.description = str(exception)
- logger.debug("Chosen status code: %s", self.status)
-
- def endElement(self, stack, name, text):
- """
- Handle "error_response" PDU.
- """
-
- if name == "status":
- code = int(text)
- if code not in self.codes:
- raise rpki.exceptions.BadStatusCode("%s is not a known status code" % code)
- self.status = code
- elif name == "description":
- self.description = text
- else:
- assert name == "message", "Unexpected name %s, stack %s" % (name, stack)
- stack.pop()
- stack[-1].endElement(stack, name, text)
-
- def toXML(self):
- """
- Generate payload of "error_response" PDU.
- """
-
- assert self.status in self.codes
- elt = self.make_elt("status")
- elt.text = str(self.status)
- payload = [elt]
- if self.description:
- elt = self.make_elt("description")
- elt.text = str(self.description)
- elt.set("{http://www.w3.org/XML/1998/namespace}lang", "en-US")
- payload.append(elt)
- return payload
-
- def check_response(self):
- """
- Handle an error response. For now, just raise an exception,
- perhaps figure out something more clever to do later.
- """
+ assert status in error_response_codes
+ del r_msg[:]
+ r_msg.set("type", "error_response")
+ SubElement(r_msg, tag_status).text = str(status)
+ se = SubElement(r_msg, tag_description)
+ se.set("{http://www.w3.org/XML/1998/namespace}lang", "en-US")
+ se.text = str(description or error_response_codes[status])
- raise rpki.exceptions.UpstreamError(self.codes[self.status])
-class message_pdu(base_elt):
+def generate_error_response_from_exception(r_msg, e, q_type):
"""
- Up-Down protocol message wrapper PDU.
+ Construct an error response from an exception. q_type
+ specifies the kind of query to which this is a response, since the
+ same exception can generate different codes in response to different
+ queries.
"""
- version = 1
-
- name2type = dict(
- list = list_pdu,
- list_response = list_response_pdu,
- issue = issue_pdu,
- issue_response = issue_response_pdu,
- revoke = revoke_pdu,
- revoke_response = revoke_response_pdu,
- error_response = error_response_pdu)
-
- type2name = dict((v, k) for k, v in name2type.iteritems())
-
- error_pdu_type = error_response_pdu
-
- def toXML(self):
- """
- Generate payload of message PDU.
- """
-
- elt = self.make_elt("message", "version", "sender", "recipient", "type")
- elt.extend(self.payload.toXML())
- return elt
+ t = type(e)
+ code = (exception_map.get((t, q_type)) or exception_map.get(t) or 2001)
+ generate_error_response(r_msg, code, e)
- def startElement(self, stack, name, attrs):
- """
- Handle message PDU.
-
- Payload of the <message/> element varies depending on the "type"
- attribute, so after some basic checks we have to instantiate the
- right class object to handle whatever kind of PDU this is.
- """
-
- assert name == "message", "Unexpected name %s, stack %s" % (name, stack)
- assert self.version == int(attrs["version"])
- self.sender = attrs["sender"]
- self.recipient = attrs["recipient"]
- self.type = attrs["type"]
- self.payload = self.name2type[attrs["type"]]()
- stack.append(self.payload)
-
- def __str__(self):
- """
- Convert a message PDU to a string.
- """
- return lxml.etree.tostring(self.toXML(), pretty_print = True, encoding = "UTF-8")
-
- def serve_top_level(self, child, callback):
- """
- Serve one message request PDU.
- """
-
- r_msg = message_pdu()
- r_msg.sender = self.recipient
- r_msg.recipient = self.sender
-
- def done():
- r_msg.type = self.type2name[type(r_msg.payload)]
- callback(r_msg)
-
- def lose(e):
- logger.exception("Unhandled exception serving child %r", child)
- callback(self.serve_error(e))
-
- try:
- self.log_query(child)
- self.payload.serve_pdu(self, r_msg, child, done, lose)
- except (rpki.async.ExitNow, SystemExit):
- raise
- except Exception, e:
- lose(e)
-
- def log_query(self, child):
- """
- Log query we're handling. Separate method so rootd can override.
- """
-
- logger.info("Serving %s query from child %s [sender %s, recipient %s]",
- self.type, child.child_handle, self.sender, self.recipient)
-
- def serve_error(self, exception):
- """
- Generate an error_response message PDU.
- """
-
- r_msg = message_pdu()
- r_msg.sender = self.recipient
- r_msg.recipient = self.sender
- r_msg.payload = self.error_pdu_type(exception, self.payload)
- r_msg.type = self.type2name[type(r_msg.payload)]
- return r_msg
-
- @classmethod
- def make_query(cls, payload, sender, recipient):
- """
- Construct one message PDU.
- """
-
- assert not cls.type2name[type(payload)].endswith("_response")
- if sender is None:
- sender = "tweedledee"
- if recipient is None:
- recipient = "tweedledum"
- self = cls()
- self.sender = sender
- self.recipient = recipient
- self.payload = payload
- self.type = self.type2name[type(payload)]
- return self
-
-class sax_handler(rpki.xml_utils.sax_handler):
- """
- SAX handler for Up-Down protocol.
- """
-
- pdu = message_pdu
- name = "message"
- version = "1"
-
-class cms_msg(rpki.x509.XML_CMS_object):
+class cms_msg_no_sax(rpki.x509.XML_CMS_object):
"""
Class to hold a CMS-signed up-down PDU.
+
+ Name is a transition kludge: once we ditch SAX, this will become cms_msg.
"""
encoding = "UTF-8"
schema = rpki.relaxng.up_down
- saxify = sax_handler.saxify
allow_extra_certs = True
allow_extra_crls = True
-
-class cms_msg_no_sax(cms_msg):
- """
- Class to hold a CMS-signed up-down PDU.
-
- Name is a transition kludge: once we ditch SAX, this will become cms_msg.
- """
-
- saxify = None