aboutsummaryrefslogtreecommitdiff
path: root/scripts/rpki/up_down.py
diff options
context:
space:
mode:
Diffstat (limited to 'scripts/rpki/up_down.py')
-rw-r--r--scripts/rpki/up_down.py518
1 files changed, 0 insertions, 518 deletions
diff --git a/scripts/rpki/up_down.py b/scripts/rpki/up_down.py
deleted file mode 100644
index f902d86c..00000000
--- a/scripts/rpki/up_down.py
+++ /dev/null
@@ -1,518 +0,0 @@
-# $Id$
-
-# Copyright (C) 2007--2008 American Registry for Internet Numbers ("ARIN")
-#
-# Permission to use, copy, modify, and distribute this software for any
-# purpose with or without fee is hereby granted, provided that the above
-# copyright notice and this permission notice appear in all copies.
-#
-# THE SOFTWARE IS PROVIDED "AS IS" AND ARIN DISCLAIMS ALL WARRANTIES WITH
-# REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
-# AND FITNESS. IN NO EVENT SHALL ARIN BE LIABLE FOR ANY SPECIAL, DIRECT,
-# INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
-# LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
-# OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
-# PERFORMANCE OF THIS SOFTWARE.
-
-"""RPKI "up-down" protocol."""
-
-import base64, lxml.etree, time
-import rpki.sax_utils, rpki.resource_set, rpki.x509, rpki.exceptions
-
-xmlns="http://www.apnic.net/specs/rescerts/up-down/"
-
-nsmap = { None : xmlns }
-
-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("{%s}%s" % (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=None):
- """Construct a sub-element with Base64 text content."""
- if value is None:
- value = getattr(self, name, None)
- if value is not None:
- lxml.etree.SubElement(elt, "{%s}%s" % (xmlns, name), nsmap=nsmap).text = base64.b64encode(value)
-
- def serve_pdu(self, gctx, q_msg, r_msg, child):
- """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."""
-
- def __init__(self, ini):
- """Initialize a set of URIs, which includes basic some syntax checking."""
- if isinstance(ini, (list, tuple)):
- self[:] = ini
- elif isinstance(ini, str):
- self[:] = ini.split(",")
- for s in self:
- if s.strip() != s or s.find("://") < 0:
- raise rpki.exceptions.BadURISyntax, "Bad URI \"%s\"" % s
- else:
- raise TypeError
-
- def __str__(self):
- """Convert a multi_uri back to a string representation."""
- return ",".join(self)
-
- def rsync(self):
- """Find first rsync://... URI in self."""
- for s in self:
- if s.startswith("rsync://"):
- 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."""
- 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])
- if self.issuer is not None:
- self.make_b64elt(elt, "issuer", self.issuer.get_DER())
- 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.as
- 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, gctx, q_msg, r_msg, child):
- """Serve one "list" PDU."""
- r_msg.payload = list_response_pdu()
-
- # This will require a callback when we go event-driven
- irdb_resources = rpki.left_right.irdb_query(gctx, child.self_id, child.child_id)
-
- for parent in child.parents(gctx):
- for ca in parent.cas(gctx):
- ca_detail = ca.fetch_active(gctx)
- if not ca_detail:
- continue
- resources = ca_detail.latest_ca_cert.get_3779resources().intersection(irdb_resources)
- if resources.empty():
- 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.child_certs(gctx, ca_detail = ca_detail):
- c = certificate_elt()
- c.cert_url = multi_uri(child_cert.uri(ca))
- c.cert = child_cert.cert
- rc.certs.append(c)
- rc.issuer = ca_detail.latest_ca_cert
- r_msg.payload.classes.append(rc)
-
- @classmethod
- def query(cls, gctx, parent):
- """Send a "list" query to parent."""
- return parent.query_up_down(gctx, cls())
-
-class class_response_syntax(base_elt):
- """Syntax for Up-Down protocol "list_response" and "issue_response" PDUs."""
-
- def __init__(self):
- """Initialize class_response_syntax."""
- 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, gctx, q_msg, r_msg, child):
- """Serve one issue request PDU."""
-
- # Check the request
- ca = child.ca_from_class_name(gctx, self.class_name)
- ca_detail = ca.fetch_active(gctx)
- self.pkcs10.check_valid_rpki()
-
- # Check current cert, if any
-
- # This will require a callback when we go event-driven
- irdb_resources = rpki.left_right.irdb_query(gctx, child.self_id, child.child_id)
-
- resources = irdb_resources.intersection(ca_detail.latest_ca_cert.get_3779resources())
- req_key = self.pkcs10.getPublicKey()
- req_sia = self.pkcs10.get_SIA()
- child_cert = child.child_certs(gctx, ca_detail = ca_detail, ski = req_key.get_SKI(), unique = True)
-
- # Generate new cert or regenerate old one if necessary
-
- if child_cert is None:
- child_cert = ca_detail.issue(
- gctx = gctx,
- ca = ca,
- child = child,
- subject_key = req_key,
- sia = req_sia,
- resources = resources)
- else:
- child_cert = child_cert.reissue(
- gctx = gctx,
- ca_detail = ca_detail,
- sia = req_sia,
- resources = resources)
-
- # Save anything we modified and generate response
- rpki.sql.sql_sweep(gctx)
- assert child_cert and child_cert.sql_in_db
- c = certificate_elt()
- c.cert_url = multi_uri(child_cert.uri(ca))
- 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)
-
- @classmethod
- def query(cls, gctx, parent, ca, ca_detail):
- """Send an "issue" request to parent associated with ca."""
- assert ca_detail is not None and ca_detail.state in ("pending", "active")
- sia = ((rpki.oids.name2oid["id-ad-caRepository"], ("uri", ca.sia_uri)),
- (rpki.oids.name2oid["id-ad-rpkiManifest"], ("uri", ca_detail.manifest_uri(ca))))
- self = cls()
- self.class_name = ca.parent_resource_class
- self.pkcs10 = rpki.x509.PKCS10.create_ca(ca_detail.private_key_id, sia)
- return parent.query_up_down(gctx, self)
-
-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."""
- self.class_name = attrs["class_name"]
- self.ski = attrs["ski"]
-
- def toXML(self):
- """Generate payload of "revoke" PDU."""
- return [self.make_elt("key", "class_name", "ski")]
-
-class revoke_pdu(revoke_syntax):
- """Up-Down protocol "revoke" PDU."""
-
- def get_SKI(self):
- """Convert g(SKI) encoding from PDU back to raw SKI."""
- return base64.urlsafe_b64decode(self.ski + "=")
-
- def serve_pdu(self, gctx, q_msg, r_msg, child):
- """Serve one revoke request PDU."""
- for ca_detail in child.ca_from_class_name(gctx, self.class_name).ca_details(gctx):
- for child_cert in child.child_certs(gctx, ca_detail = ca_detail, ski = self.get_SKI()):
- child_cert.revoke(gctx)
- rpki.sql.sql_sweep(gctx)
- r_msg.payload = revoke_response_pdu()
- r_msg.payload.class_name = self.class_name
- r_msg.payload.ski = self.ski
-
- @classmethod
- def query(cls, gctx, ca_detail):
- """Send a "revoke" request to parent associated with ca_detail."""
- ca = ca_detail.ca(gctx)
- parent = ca.parent(gctx)
- self = cls()
- self.class_name = ca.parent_resource_class
- self.ski = ca_detail.latest_ca_cert.gSKI()
- return parent.query_up_down(gctx, self)
-
-class revoke_response_pdu(revoke_syntax):
- """Up-Down protocol "revoke_response" PDU."""
-
- pass
-
-class error_response_pdu(base_elt):
- """Up-Down protocol "error_response" PDU."""
-
- 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" }
-
- exceptions = {}
-
- def __init__(self, exception = None):
- """Initialize an error_response PDU from an exception object."""
- if exception is not None:
- if exception in self.exceptions:
- self.status = exceptions[exception]
- else:
- self.status = 2001
- self.description = str(exception)
-
- 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"
- 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.
- """
- raise rpki.exceptions.UpstreamError, self.codes[self.status]
-
-class message_pdu(base_elt):
- """Up-Down protocol message wrapper PDU."""
-
- version = 1
-
- name2type = {
- "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.items())
-
- def toXML(self):
- """Generate payload of message PDU."""
- elt = self.make_elt("message", "version", "sender", "recipient", "type")
- elt.extend(self.payload.toXML())
- return elt
-
- 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."""
- lxml.etree.tostring(self.toXML(), pretty_print = True, encoding = "UTF-8")
-
- def serve_top_level(self, gctx, child):
- """Serve one message request PDU."""
- r_msg = message_pdu()
- r_msg.sender = self.recipient
- r_msg.recipient = self.sender
- self.payload.serve_pdu(gctx, self, r_msg, child)
- r_msg.type = self.type2name[type(r_msg.payload)]
- return r_msg
-
- 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 = error_response_pdu(exception)
- 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.sax_utils.handler):
- """SAX handler for Up-Down protocol."""
-
- def create_top_level(self, name, attrs):
- """Top-level PDU for this protocol is <message/>."""
- return message_pdu()