aboutsummaryrefslogtreecommitdiff
path: root/rpkid/rootd.py
diff options
context:
space:
mode:
Diffstat (limited to 'rpkid/rootd.py')
-rwxr-xr-xrpkid/rootd.py202
1 files changed, 202 insertions, 0 deletions
diff --git a/rpkid/rootd.py b/rpkid/rootd.py
new file mode 100755
index 00000000..212e6469
--- /dev/null
+++ b/rpkid/rootd.py
@@ -0,0 +1,202 @@
+# $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.
+
+"""
+Trivial RPKI up-down protocol root server, for testing. Not suitable
+for production use. Overrides a bunch of method definitions from the
+rpki.* classes in order to reuse as much code as possible.
+
+Usage: python rootd.py [ { -c | --config } configfile ] [ { -h | --help } ]
+
+Default configuration file is rootd.conf, override with --config option.
+"""
+
+import traceback, os, time, getopt, sys, lxml
+import rpki.resource_set, rpki.up_down, rpki.left_right, rpki.x509
+import rpki.https, rpki.config, rpki.cms, rpki.exceptions, rpki.relaxng
+import rpki.sundial, rpki.log
+
+rpki_subject_lifetime = rpki.sundial.timedelta(days = 30)
+
+def get_subject_cert():
+ try:
+ x = rpki.x509.X509(Auto_file = rpki_subject_filename)
+ return x
+ except IOError:
+ return None
+
+def set_subject_cert(cert):
+ f = open(rpki_subject_filename, "wb")
+ f.write(cert.get_DER())
+ f.close()
+
+def del_subject_cert():
+ os.remove(rpki_subject_filename)
+
+def stash_subject_pkcs10(pkcs10):
+ if rpki_pkcs10_filename:
+ f = open(rpki_pkcs10_filename, "wb")
+ f.write(pkcs10.get_DER())
+ f.close()
+
+def compose_response(r_msg):
+ rc = rpki.up_down.class_elt()
+ rc.class_name = rootd_name
+ rc.cert_url = rpki.up_down.multi_uri(rootd_cert)
+ rc.from_resource_bag(rpki_issuer.get_3779resources())
+ rc.issuer = rpki_issuer
+ r_msg.payload.classes.append(rc)
+ rpki_subject = get_subject_cert()
+ if rpki_subject is not None:
+ rc.certs.append(rpki.up_down.certificate_elt())
+ rc.certs[0].cert_url = rpki.up_down.multi_uri(rootd_cert)
+ rc.certs[0].cert = rpki_subject
+
+class list_pdu(rpki.up_down.list_pdu):
+ def serve_pdu(self, xxx1, q_msg, r_msg, xxx2):
+ r_msg.payload = rpki.up_down.list_response_pdu()
+ compose_response(r_msg)
+
+class issue_pdu(rpki.up_down.issue_pdu):
+ def serve_pdu(self, xxx1, q_msg, r_msg, xxx2):
+ stash_subject_pkcs10(self.pkcs10)
+ self.pkcs10.check_valid_rpki()
+ r_msg.payload = rpki.up_down.issue_response_pdu()
+ rpki_subject = get_subject_cert()
+ if rpki_subject is None:
+ resources = rpki_issuer.get_3779resources()
+ rpki.log.info("Generating subject cert with resources " + str(resources))
+ req_key = self.pkcs10.getPublicKey()
+ req_sia = self.pkcs10.get_SIA()
+ crldp = rootd_base + rpki_issuer.gSKI() + ".crl"
+ set_subject_cert(rpki_issuer.issue(keypair = rpki_key,
+ subject_key = req_key,
+ serial = int(time.time()),
+ sia = req_sia,
+ aia = rootd_cert,
+ crldp = crldp,
+ resources = resources,
+ notAfter = rpki.sundial.datetime.utcnow() + rpki_subject_lifetime))
+ now = rpki.sundial.datetime.utcnow()
+ crl = rpki.x509.CRL.generate(
+ keypair = rpki_key,
+ issuer = rpki_issuer,
+ serial = 1,
+ thisUpdate = now,
+ nextUpdate = now + rpki_subject_lifetime,
+ revokedCertificates = ())
+ f = open(os.path.dirname(rpki_subject_filename) + "/" + rpki_issuer.gSKI() + ".crl", "wb")
+ f.write(crl.get_DER())
+ f.close()
+ compose_response(r_msg)
+
+class revoke_pdu(rpki.up_down.revoke_pdu):
+ def serve_pdu(self, xxx1, q_msg, r_msg, xxx2):
+ rpki_subject = get_subject_cert()
+ if rpki_subject is None or rpki_subject.gSKI() != self.ski:
+ raise rpki.exceptions.NotInDatabase
+ del_subject_cert()
+ r_msg.payload = rpki.up_down.revoke_response_pdu()
+ r_msg.payload.class_name = self.class_name
+ r_msg.payload.ski = self.ski
+
+class message_pdu(rpki.up_down.message_pdu):
+ name2type = {
+ "list" : list_pdu,
+ "list_response" : rpki.up_down.list_response_pdu,
+ "issue" : issue_pdu,
+ "issue_response" : rpki.up_down.issue_response_pdu,
+ "revoke" : revoke_pdu,
+ "revoke_response" : rpki.up_down.revoke_response_pdu,
+ "error_response" : rpki.up_down.error_response_pdu }
+ type2name = dict((v,k) for k,v in name2type.items())
+
+class sax_handler(rpki.sax_utils.handler):
+ def create_top_level(self, name, attrs):
+ return message_pdu()
+
+def up_down_handler(query, path):
+ try:
+ q_elt = rpki.cms.xml_verify(query, cms_ta)
+ rpki.relaxng.up_down.assertValid(q_elt)
+ q_msg = sax_handler.saxify(q_elt)
+ except Exception, data:
+ rpki.log.error(traceback.format_exc())
+ return 400, "Could not process PDU: %s" % data
+ try:
+ r_msg = q_msg.serve_top_level(None, None)
+ r_elt = r_msg.toXML()
+ try:
+ rpki.relaxng.up_down.assertValid(r_elt)
+ except lxml.etree.DocumentInvalid:
+ rpki.log.debug(lxml.etree.tostring(r_elt, pretty_print = True, encoding ="utf-8", xml_declaration = True))
+ raise
+ return 200, rpki.cms.xml_sign(r_elt, cms_key, cms_certs, encoding = "utf-8")
+ except Exception, data:
+ rpki.log.error(traceback.format_exc())
+ try:
+ r_msg = q_msg.serve_error(data)
+ r_elt = r_msg.toXML()
+ rpki.relaxng.up_down.assertValid(r_elt)
+ return 200, rpki.cms.xml_sign(r_elt, cms_key, cms_certs, encoding = "utf-8")
+ except Exception, data:
+ rpki.log.error(traceback.format_exc())
+ return 500, "Could not process PDU: %s" % data
+
+os.environ["TZ"] = "UTC"
+time.tzset()
+
+rpki.log.init("rootd")
+
+cfg_file = "rootd.conf"
+
+opts,argv = getopt.getopt(sys.argv[1:], "c:h?", ["config=", "help"])
+for o,a in opts:
+ if o in ("-h", "--help", "-?"):
+ print __doc__
+ sys.exit(0)
+ if o in ("-c", "--config"):
+ cfg_file = a
+if argv:
+ raise RuntimeError, "Unexpected arguments %s" % argv
+
+cfg = rpki.config.parser(cfg_file, "rootd")
+
+cms_ta = rpki.x509.X509(Auto_file = cfg.get("cms-ta"))
+cms_key = rpki.x509.RSA(Auto_file = cfg.get("cms-key"))
+cms_certs = rpki.x509.X509_chain(Auto_files = cfg.multiget("cms-certs"))
+
+https_key = rpki.x509.RSA(Auto_file = cfg.get("https-key"))
+https_certs = rpki.x509.X509_chain(Auto_files = cfg.multiget("https-certs"))
+
+https_server_host = cfg.get("server-host", "")
+https_server_port = int(cfg.get("server-port"))
+
+rpki_key = rpki.x509.RSA(Auto_file = cfg.get("rpki-key"))
+rpki_issuer = rpki.x509.X509(Auto_file = cfg.get("rpki-issuer"))
+
+rpki_subject_filename = cfg.get("rpki-subject-filename")
+rpki_pkcs10_filename = cfg.get("rpki-pkcs10-filename", "")
+
+rootd_name = cfg.get("rootd_name", "wombat")
+rootd_base = cfg.get("rootd_base", "rsync://" + rootd_name + ".invalid/")
+rootd_cert = cfg.get("rootd_cert", rootd_base + "rootd.cer")
+
+rpki.https.server(privateKey = https_key,
+ certChain = https_certs,
+ host = https_server_host,
+ port = https_server_port,
+ handlers = up_down_handler)