aboutsummaryrefslogtreecommitdiff
path: root/rpki
diff options
context:
space:
mode:
Diffstat (limited to 'rpki')
-rw-r--r--rpki/left_right.py30
-rw-r--r--rpki/log.py2
-rw-r--r--rpki/pubd.py21
-rw-r--r--rpki/publication.py167
-rw-r--r--rpki/rootd.py73
-rw-r--r--rpki/rpkid.py38
-rw-r--r--rpki/x509.py2
7 files changed, 108 insertions, 225 deletions
diff --git a/rpki/left_right.py b/rpki/left_right.py
index e4cf25fe..2dfa6efd 100644
--- a/rpki/left_right.py
+++ b/rpki/left_right.py
@@ -36,13 +36,17 @@ import rpki.publication
import rpki.async
import rpki.rpkid_tasks
+from lxml.etree import Element, SubElement
+
logger = logging.getLogger(__name__)
+
## @var enforce_strict_up_down_xml_sender
# Enforce strict checking of XML "sender" field in up-down protocol
enforce_strict_up_down_xml_sender = False
+
class left_right_namespace(object):
"""
XML namespace parameters for left-right protocol.
@@ -543,52 +547,42 @@ class repository_elt(data_elt):
self.sql_mark_dirty()
cb()
- @staticmethod
- def default_pubd_handler(pdu):
- """
- Default handler for publication response PDUs.
- """
-
- pdu.raise_if_error()
- def call_pubd(self, callback, errback, q_msg, handlers = None):
+ def call_pubd(self, callback, errback, q_msg, handlers = {}):
"""
Send a message to publication daemon and return the response.
As a convenience, attempting to send an empty message returns
immediate success without sending anything.
- Handlers is a dict of handler functions to process the response
+ handlers is a dict of handler functions to process the response
PDUs. If the tag value in the response PDU appears in the dict,
the associated handler is called to process the PDU. If no tag
- matches, default_pubd_handler() is called. A handler value of
- False suppresses calling of the default handler.
+ matches, a default handler is called to check for errors; a
+ handler value of False suppresses calling of the default handler.
"""
try:
self.gctx.sql.sweep()
- if not q_msg:
+ if len(q_msg) == 0:
return callback()
- if handlers is None:
- handlers = {}
-
for q_pdu in q_msg:
logger.info("Sending %r to pubd", q_pdu)
bsc = self.bsc
- q_der = rpki.publication.cms_msg().wrap(q_msg, bsc.private_key_id, bsc.signing_cert, bsc.signing_cert_crl)
+ q_der = rpki.publication.cms_msg_no_sax().wrap(q_msg, bsc.private_key_id, bsc.signing_cert, bsc.signing_cert_crl)
bpki_ta_path = (self.gctx.bpki_ta, self.self.bpki_cert, self.self.bpki_glue, self.bpki_cert, self.bpki_glue)
def done(r_der):
try:
logger.debug("Received response from pubd")
- r_cms = rpki.publication.cms_msg(DER = r_der)
+ r_cms = rpki.publication.cms_msg_no_sax(DER = r_der)
r_msg = r_cms.unwrap(bpki_ta_path)
r_cms.check_replay_sql(self, self.peer_contact_uri)
for r_pdu in r_msg:
- handler = handlers.get(r_pdu.tag, self.default_pubd_handler)
+ handler = handlers.get(r_pdu.get("tag"), rpki.publication.raise_if_error)
if handler:
logger.debug("Calling pubd handler %r", handler)
handler(r_pdu)
diff --git a/rpki/log.py b/rpki/log.py
index 4fe2a808..0794a68f 100644
--- a/rpki/log.py
+++ b/rpki/log.py
@@ -48,7 +48,7 @@ show_python_ids = False
# Whether tracebacks are enabled globally. Individual classes and
# modules may choose to override this.
-enable_tracebacks = False
+enable_tracebacks = True
## @var use_setproctitle
# Whether to use setproctitle (if available) to change name shown for
diff --git a/rpki/pubd.py b/rpki/pubd.py
index 335da0e6..6e50e9a6 100644
--- a/rpki/pubd.py
+++ b/rpki/pubd.py
@@ -51,10 +51,6 @@ rrdp_xmlns = rpki.relaxng.rrdp.xmlns
rrdp_nsmap = rpki.relaxng.rrdp.nsmap
rrdp_version = "1"
-pub_xmlns = rpki.relaxng.publication.xmlns
-pub_nsmap = rpki.relaxng.publication.nsmap
-pub_version = rpki.relaxng.publication.version
-
rrdp_tag_delta = rrdp_xmlns + "delta"
rrdp_tag_deltas = rrdp_xmlns + "deltas"
rrdp_tag_notification = rrdp_xmlns + "notification"
@@ -62,12 +58,6 @@ rrdp_tag_publish = rrdp_xmlns + "publish"
rrdp_tag_snapshot = rrdp_xmlns + "snapshot"
rrdp_tag_withdraw = rrdp_xmlns + "withdraw"
-pub_tag_msg = pub_xmlns + "msg"
-pub_tag_list = pub_xmlns + "list"
-pub_tag_publish = pub_xmlns + "publish"
-pub_tag_withdraw = pub_xmlns + "withdraw"
-pub_tag_report_error = pub_xmlns + "report_error"
-
def DERSubElement(elt, name, der, attrib = None, **kwargs):
"""
@@ -210,22 +200,23 @@ class main(object):
self.sql.commit() # commit the replay timestamp
if q_msg.get("type") != "query":
raise rpki.exceptions.BadQuery("Message type is %s, expected query" % q_msg.get("type"))
- r_msg = Element(pub_tag_msg, nsmap = pub_nsmap, type = "reply", version = pub_version)
+ r_msg = Element(rpki.publication.tag_msg, nsmap = rpki.publication.nsmap,
+ type = "reply", version = rpki.publication.version)
delta = None
failed = False
for q_pdu in q_msg:
try:
- if q_pdu.tag == pub_tag_list:
+ if q_pdu.tag == rpki.publication.tag_list:
for obj in client.objects:
r_pdu = SubElement(r_msg, q_pdu.tag, uri = obj.uri, hash = obj.hash)
if q_pdu.get("tag") is not None:
r_pdu.set("tag", q_pdu.get("tag"))
else:
- assert q_pdu.tag in (pub_tag_publish, pub_tag_withdraw)
+ assert q_pdu.tag in (rpki.publication.tag_publish, rpki.publication.tag_withdraw)
if delta is None:
delta = self.session.new_delta()
client.check_allowed_uri(q_pdu.get("uri"))
- if q_pdu.tag == pub_tag_publish:
+ if q_pdu.tag == rpki.publication.tag_publish:
der = q_pdu.text.decode("base64")
logger.info("Publishing %s", rpki.x509.uri_dispatch(q_pdu.get("uri"))(DER = der).tracking_data(q_pdu.get("uri")))
delta.publish(client, der, q_pdu.get("uri"), q_pdu.get("hash"))
@@ -237,7 +228,7 @@ class main(object):
r_pdu.set("tag", q_pdu.get("tag"))
except Exception, e:
logger.exception("Exception processing PDU %r", q_pdu)
- r_pdu = SubElement(r_msg, pub_tag_report_error, error_code = e.__class__.__name__)
+ r_pdu = SubElement(r_msg, rpki.publication.tag_report_error, error_code = e.__class__.__name__)
r_pdu.text = str(e)
if q_pdu.get("tag") is not None:
r_pdu.set("tag", q_pdu.get("tag"))
diff --git a/rpki/publication.py b/rpki/publication.py
index ca7f7792..9b5bfcbd 100644
--- a/rpki/publication.py
+++ b/rpki/publication.py
@@ -38,161 +38,44 @@ import rpki.log
logger = logging.getLogger(__name__)
-class publication_namespace(object):
- xmlns = rpki.relaxng.publication.xmlns
- nsmap = rpki.relaxng.publication.nsmap
+nsmap = rpki.relaxng.publication.nsmap
+version = rpki.relaxng.publication.version
+tag_msg = rpki.relaxng.publication.xmlns + "msg"
+tag_list = rpki.relaxng.publication.xmlns + "list"
+tag_publish = rpki.relaxng.publication.xmlns + "publish"
+tag_withdraw = rpki.relaxng.publication.xmlns + "withdraw"
+tag_report_error = rpki.relaxng.publication.xmlns + "report_error"
-class base_publication_elt(rpki.xml_utils.base_elt, publication_namespace):
- """
- Base element for publication protocol. Publish and withdraw PDUs subclass this.
- """
-
- attributes = ("tag", "uri", "hash")
-
- tag = None
- uri = None
- der = None
- hash = None
-
- _payload = None
-
- def __repr__(self):
- return rpki.log.log_repr(self, self.tag, self.uri, self.hash, self.payload)
-
- @property
- def payload(self):
- if self._payload is None and self.der is not None:
- self._payload = rpki.x509.uri_dispatch(self.uri)(DER = self.der)
- return self._payload
- def raise_if_error(self):
- """
- No-op unless this is a <report_error/> PDU.
- """
-
- pass
-
-
-class publish_elt(base_publication_elt):
+def raise_if_error(pdu):
"""
- <publish/> element.
- """
-
- element_name = "publish"
-
- def endElement(self, stack, name, text):
- """
- Handle reading of the object to be published
- """
+ Raise an appropriate error if this is a <report_error/> PDU.
- assert name == self.element_name, "Unexpected name %s, stack %s" % (name, stack)
- if text:
- self.der = text.decode("base64")
- stack.pop()
-
- def toXML(self):
- """
- Generate XML element for publishable object.
- """
-
- elt = self.make_elt()
- if self.der is not None:
- elt.text = self.der.encode("base64")
- return elt
-
-
-class withdraw_elt(base_publication_elt):
- """
- <withdraw/> element.
+ As a convience, this will also accept a <msg/> PDU and raise an
+ appropriate error if it contains any <report_error/> PDUs.
"""
- element_name = "withdraw"
-
+ if pdu.tag == tag_report_error:
+ code = pdu.get("error_code")
+ logger.debug("<report_error/> code %r", code)
+ e = getattr(rpki.exceptions, code, None)
+ if e is not None and issubclass(e, rpki.exceptions.RPKI_Exception):
+ raise e(pdu.text)
+ else:
+ raise rpki.exceptions.BadPublicationReply("Unexpected response from pubd: %r, %r" % (code, pdu))
-class list_elt(base_publication_elt):
- """
- <list/> element.
- """
-
- element_name = "list"
+ if pdu.tag == tag_msg:
+ for p in pdu:
+ raise_if_error(p)
-class report_error_elt(rpki.xml_utils.text_elt, publication_namespace):
- """
- <report_error/> element.
- """
-
- element_name = "report_error"
- attributes = ("tag", "error_code")
- text_attribute = "error_text"
-
- error_code = None
- error_text = None
-
- def __repr__(self):
- return rpki.log.log_repr(self, self.error_code, self.error_text)
-
- def __str__(self):
- s = ""
- if getattr(self, "tag", None) is not None:
- s += "[%s] " % self.tag
- s += self.error_code
- if getattr(self, "error_text", None) is not None:
- s += ": " + self.error_text
- return s
-
- def raise_if_error(self):
- """
- Raise exception associated with this <report_error/> PDU.
- """
-
- try:
- e = getattr(rpki.exceptions, self.error_code)
- if issubclass(e, rpki.exceptions.RPKI_Exception):
- raise e(getattr(self, "text", None))
- except (TypeError, AttributeError):
- pass
- raise rpki.exceptions.BadPublicationReply("Unexpected response from pubd: %s" % self)
-
-
-class msg(rpki.xml_utils.msg, publication_namespace):
- """
- Publication PDU.
- """
-
- ## @var version
- # Protocol version
- version = int(rpki.relaxng.publication.version)
-
- ## @var pdus
- # Dispatch table of PDUs for this protocol.
- pdus = dict((x.element_name, x) for x in (publish_elt, withdraw_elt, list_elt, report_error_elt))
-
-
-class sax_handler(rpki.xml_utils.sax_handler):
- """
- SAX handler for publication protocol.
- """
-
- pdu = msg
- name = "msg"
- version = rpki.relaxng.publication.version
-
-
-class cms_msg(rpki.x509.XML_CMS_object):
+class cms_msg_no_sax(rpki.x509.XML_CMS_object):
"""
Class to hold a CMS-signed publication PDU.
+
+ Name is a transition kludge: once we ditch SAX, this will become cms_msg.
"""
encoding = "us-ascii"
schema = rpki.relaxng.publication
- saxify = sax_handler.saxify
-
-class cms_msg_no_sax(cms_msg):
- """
- Transition kludge: varient of cms_msg (q.v.) with SAX parsing disabled.
- If and when we ditch SAX entirely, this will become cms_msg.
- """
-
- saxify = None
diff --git a/rpki/rootd.py b/rpki/rootd.py
index 4ded1081..b6e351ac 100644
--- a/rpki/rootd.py
+++ b/rpki/rootd.py
@@ -39,6 +39,8 @@ import rpki.sundial
import rpki.log
import rpki.daemonize
+from lxml.etree import Element, SubElement
+
logger = logging.getLogger(__name__)
rootd = None
@@ -66,7 +68,8 @@ class revoke_pdu(rpki.up_down.revoke_pdu):
raise rpki.exceptions.NotInDatabase
logger.debug("Revoking certificate %s", self.ski)
now = rpki.sundial.now()
- pubd_msg = rpki.publication.msg.query()
+ pubd_msg = Element(rpki.publication.tag_msg, nsmap = rpki.publication.nsmap,
+ type = "query", version = rpki.publication.version)
rootd.revoke_subject_cert(now)
rootd.del_subject_cert()
rootd.del_subject_pkcs10()
@@ -196,11 +199,12 @@ class main(object):
notBefore = now,
notAfter = notAfter)
self.set_subject_cert(subject_cert)
- pubd_msg = rpki.publication.msg.query()
- pubd_msg.append(rpki.publication.publish_elt.make_pdu(
- uri = self.rpki_subject_cert_uri,
- hash = hash,
- der = subject_cert.get_DER()))
+ pubd_msg = Element(rpki.publication.tag_msg, nsmap = rpki.publication.nsmap,
+ type = "query", version = rpki.publication.version)
+ pdu = SubElement(pubd_msg, rpki.publication.tag_publish, uri = self.rpki_subject_cert_uri)
+ pdu.text = subject_cert.get_Base64()
+ if hash is not None:
+ pdu.set("hash", hash)
self.generate_crl_and_manifest(now, pubd_msg)
return subject_cert, pubd_msg
@@ -222,10 +226,10 @@ class main(object):
logger.debug("Writing CRL %s", self.rpki_root_crl_file)
with open(self.rpki_root_crl_file, "wb") as f:
f.write(crl.get_DER())
- pubd_msg.append(rpki.publication.publish_elt.make_pdu(
- uri = self.rpki_root_crl_uri,
- hash = hash,
- der = crl.get_DER()))
+ pdu = SubElement(pubd_msg, rpki.publication.tag_publish, uri = self.rpki_root_crl_uri)
+ pdu.text = crl.get_Base64()
+ if hash is not None:
+ pdu.set("hash", hash)
manifest_content = [(os.path.basename(self.rpki_root_crl_uri), crl)]
if subject_cert is not None:
manifest_content.append((os.path.basename(self.rpki_subject_cert_uri), subject_cert))
@@ -253,16 +257,16 @@ class main(object):
logger.debug("Writing manifest %s", self.rpki_root_manifest_file)
with open(self.rpki_root_manifest_file, "wb") as f:
f.write(manifest.get_DER())
- pubd_msg.append(rpki.publication.publish_elt.make_pdu(
- uri = self.rpki_root_manifest_uri,
- hash = hash,
- der = manifest.get_DER()))
+ pdu = SubElement(pubd_msg, rpki.publication.tag_publish, uri = self.rpki_root_manifest_uri)
+ pdu.text = manifest.get_Base64()
+ if hash is not None:
+ pdu.set("hash", hash)
hash = rpki.x509.sha256(self.rpki_root_cert.get_DER()).encode("hex")
if hash != self.rpki_root_cert_hash:
- pubd_msg.append(rpki.publication.publish_elt.make_pdu(
- uri = self.rpki_root_cert_uri,
- hash = self.rpki_root_cert_hash,
- der = self.rpki_root_cert.get_DER()))
+ pdu = SubElement(pubd_msg, rpki.publication.tag_publish, uri = self.rpki_root_cert_uri)
+ pdu.text = self.rpki_root_cert.get_Base64()
+ if self.rpki_root_cert_hash is not None:
+ pdu.set("hash", self.rpki_root_cert_hash)
self.rpki_root_cert_hash = hash
@@ -302,45 +306,46 @@ class main(object):
def done(r_msg):
if len(q_msg) != len(r_msg):
- raise rpki.exceptions.BadPublicationReply("Wrong number of response PDUs from pubd: sent %r, got %r" % (q_msg, r_msg))
+ raise rpki.exceptions.BadPublicationReply("Wrong number of response PDUs from pubd: sent %s, got %s" % (len(q_msg), len(r_msg)))
callback()
def fix_hashes(r_msg):
- published_hash = dict((r_pdu.uri, r_pdu.hash) for r_pdu in r_msg)
+ published_hash = dict((r_pdu.get("uri"), r_pdu.get("hash")) for r_pdu in r_msg)
for q_pdu in q_msg:
- if q_pdu.hash is None and published_hash.get(q_pdu.uri) is not None:
- logger.debug("Updating hash of %r to %s from previously published data", q_pdu, published_hash[q_pdu.uri])
- q_pdu.hash = published_hash[q_pdu.uri]
+ if q_pdu.get("hash") is None and published_hash.get(q_pdu.get("uri")) is not None:
+ logger.debug("Updating hash of %s to %s from previously published data", q_pdu.get("uri"), published_hash[q_pdu.get("uri")])
+ q_pdu.set("hash", published_hash[q_pdu.get("uri")])
self.call_pubd(done, errback, q_msg)
- if not q_msg:
+ assert q_msg is None or len(q_msg) > 0
+
+ if q_msg is None:
callback()
- elif all(q_pdu.hash is not None for q_pdu in q_msg):
+ elif all(q_pdu.get("hash") is not None for q_pdu in q_msg):
self.call_pubd(done, errback, q_msg)
else:
logger.debug("Some publication PDUs are missing hashes, checking...")
- self.call_pubd(fix_hashes, errback, rpki.publication.msg.query(rpki.publication.list_elt()))
+ list_msg = Element(rpki.publication.tag_msg, nsmap = rpki.publication.nsmap,
+ type = "query", version = rpki.publication.version)
+ SubElement(list_msg, rpki.publication.tag_list)
+ self.call_pubd(fix_hashes, errback, list_msg)
def call_pubd(self, callback, errback, q_msg):
try:
- if not q_msg:
- return callback(())
-
for q_pdu in q_msg:
- logger.info("Sending %r to pubd", q_pdu)
+ logger.info("Sending %s to pubd", q_pdu.get("uri"))
- q_der = rpki.publication.cms_msg().wrap(q_msg, self.rootd_bpki_key, self.rootd_bpki_cert, self.rootd_bpki_crl)
+ q_der = rpki.publication.cms_msg_no_sax().wrap(q_msg, self.rootd_bpki_key, self.rootd_bpki_cert, self.rootd_bpki_crl)
def done(r_der):
try:
logger.debug("Received response from pubd")
- r_cms = rpki.publication.cms_msg(DER = r_der)
+ r_cms = rpki.publication.cms_msg_no_sax(DER = r_der)
r_msg = r_cms.unwrap((self.bpki_ta, self.pubd_bpki_cert))
self.pubd_cms_timestamp = r_cms.check_replay(self.pubd_cms_timestamp, self.pubd_contact_uri)
- for r_pdu in r_msg:
- r_pdu.raise_if_error()
+ rpki.publication.raise_if_error(r_msg)
callback(r_msg)
except (rpki.async.ExitNow, SystemExit):
raise
diff --git a/rpki/rpkid.py b/rpki/rpkid.py
index a338f7e6..8a672403 100644
--- a/rpki/rpkid.py
+++ b/rpki/rpkid.py
@@ -42,8 +42,11 @@ import rpki.async
import rpki.daemonize
import rpki.rpkid_tasks
+from lxml.etree import Element, SubElement
+
logger = logging.getLogger(__name__)
+
class main(object):
"""
Main program for rpkid.
@@ -1282,7 +1285,7 @@ class ca_detail_obj(rpki.sql.sql_persistent):
Check result of CRL publication.
"""
- pdu.raise_if_error()
+ rpki.publication.raise_if_error(pdu)
self.crl_published = None
self.sql_mark_dirty()
@@ -1342,7 +1345,7 @@ class ca_detail_obj(rpki.sql.sql_persistent):
Check result of manifest publication.
"""
- pdu.raise_if_error()
+ rpki.publication.raise_if_error(pdu)
self.manifest_published = None
self.sql_mark_dirty()
@@ -1676,7 +1679,7 @@ class child_cert_obj(rpki.sql.sql_persistent):
Publication callback: check result and mark published.
"""
- pdu.raise_if_error()
+ rpki.publication.raise_if_error(pdu)
self.published = None
self.sql_mark_dirty()
@@ -1965,7 +1968,8 @@ class roa_obj(rpki.sql.sql_persistent):
"""
Check publication result.
"""
- pdu.raise_if_error()
+
+ rpki.publication.raise_if_error(pdu)
self.published = None
self.sql_mark_dirty()
@@ -2174,7 +2178,7 @@ class ghostbuster_obj(rpki.sql.sql_persistent):
Check publication result.
"""
- pdu.raise_if_error()
+ rpki.publication.raise_if_error(pdu)
self.published = None
self.sql_mark_dirty()
@@ -2496,7 +2500,7 @@ class ee_cert_obj(rpki.sql.sql_persistent):
Publication callback: check result and mark published.
"""
- pdu.raise_if_error()
+ rpki.publication.raise_if_error(pdu)
self.published = None
self.sql_mark_dirty()
@@ -2532,31 +2536,37 @@ class publication_queue(object):
logger.debug("Queuing publication action: uri %s, old %r, new %r", uri, old_obj, new_obj)
+ # id(repository) may need to change to repository.peer_contact_uri
+ # once we convert from our custom SQL cache to Django ORM.
+
rid = id(repository)
if rid not in self.repositories:
self.repositories[rid] = repository
- self.msgs[rid] = rpki.publication.msg.query()
+ self.msgs[rid] = Element(rpki.publication.tag_msg, nsmap = rpki.publication.nsmap,
+ type = "query", version = rpki.publication.version)
if self.replace and uri in self.uris:
logger.debug("Removing publication duplicate %r", self.uris[uri])
old_pdu = self.uris.pop(uri)
self.msgs[rid].remove(old_pdu)
- hash = old_pdu.hash
+ hash = old_pdu.get("hash")
elif old_obj is None:
hash = None
else:
hash = rpki.x509.sha256(old_obj.get_DER()).encode("hex")
if new_obj is None:
- pdu = rpki.publication.withdraw_elt.make_pdu(uri = uri, hash = hash)
+ pdu = SubElement(self.msgs[rid], rpki.publication.tag_withdraw, uri = uri, hash = hash)
else:
- pdu = rpki.publication.publish_elt.make_pdu( uri = uri, hash = hash, der = new_obj.get_DER())
+ pdu = SubElement(self.msgs[rid], rpki.publication.tag_publish, uri = uri)
+ pdu.text = new_obj.get_Base64()
+ if hash is not None:
+ pdu.set("hash", hash)
if handler is not None:
- self.handlers[id(pdu)] = handler
- pdu.tag = id(pdu)
-
- self.msgs[rid].append(pdu)
+ tag = str(id(pdu))
+ self.handlers[tag] = handler
+ pdu.set("tag", tag)
if self.replace:
self.uris[uri] = pdu
diff --git a/rpki/x509.py b/rpki/x509.py
index da949305..89b598d4 100644
--- a/rpki/x509.py
+++ b/rpki/x509.py
@@ -1888,7 +1888,7 @@ class XML_CMS_object(Wrapped_CMS_object):
## @var check_outbound_schema
# If set, perform RelaxNG schema check on outbound messages.
- check_outbound_schema = False
+ check_outbound_schema = True
def encode(self):
"""