aboutsummaryrefslogtreecommitdiff
path: root/rpkid/rpki
diff options
context:
space:
mode:
authorRob Austein <sra@hactrn.net>2010-12-14 20:35:28 +0000
committerRob Austein <sra@hactrn.net>2010-12-14 20:35:28 +0000
commitd97690e09b1485269e5af6a1445a14df3802c531 (patch)
tree7a133ff2d9a80dc479e738508065fdceef99d239 /rpkid/rpki
parentcc9aca313010413d28ffb913245cbeda182b2d9b (diff)
Move all daemons to modules
svn path=/rpkid/irdbd.py; revision=3569
Diffstat (limited to 'rpkid/rpki')
-rw-r--r--rpkid/rpki/irdbd.py225
-rw-r--r--rpkid/rpki/pubd.py169
-rw-r--r--rpkid/rpki/rootd.py320
-rw-r--r--rpkid/rpki/rpkid.py106
4 files changed, 820 insertions, 0 deletions
diff --git a/rpkid/rpki/irdbd.py b/rpkid/rpki/irdbd.py
new file mode 100644
index 00000000..a90f71ce
--- /dev/null
+++ b/rpkid/rpki/irdbd.py
@@ -0,0 +1,225 @@
+"""
+IR database daemon.
+
+Usage: python irdbd.py [ { -c | --config } configfile ] [ { -h | --help } ]
+
+Default configuration file is irdbd.conf, override with --config option.
+
+$Id$
+
+Copyright (C) 2009--2010 Internet Systems Consortium ("ISC")
+
+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 ISC DISCLAIMS ALL WARRANTIES WITH
+REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+AND FITNESS. IN NO EVENT SHALL ISC 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.
+
+Portions 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.
+"""
+
+from __future__ import with_statement
+
+import sys, os, time, getopt, urlparse, warnings
+import rpki.http, rpki.config, rpki.resource_set, rpki.relaxng
+import rpki.exceptions, rpki.left_right, rpki.log, rpki.x509
+
+# Silence warning while loading MySQLdb in Python 2.6, sigh
+if hasattr(warnings, "catch_warnings"):
+ with warnings.catch_warnings():
+ warnings.simplefilter("ignore", DeprecationWarning)
+ import MySQLdb
+else:
+ import MySQLdb
+
+class main(object):
+
+ def handle_list_resources(self, q_pdu, r_msg):
+
+ r_pdu = rpki.left_right.list_resources_elt()
+ r_pdu.tag = q_pdu.tag
+ r_pdu.self_handle = q_pdu.self_handle
+ r_pdu.child_handle = q_pdu.child_handle
+
+ self.cur.execute(
+ "SELECT registrant_id, valid_until FROM registrant WHERE registry_handle = %s AND registrant_handle = %s",
+ (q_pdu.self_handle, q_pdu.child_handle))
+
+ if self.cur.rowcount != 1:
+ raise rpki.exceptions.NotInDatabase, \
+ "This query should have produced a single exact match, something's messed up (rowcount = %d, self_handle = %s, child_handle = %s)" \
+ % (self.cur.rowcount, q_pdu.self_handle, q_pdu.child_handle)
+
+ registrant_id, valid_until = self.cur.fetchone()
+
+ r_pdu.valid_until = valid_until.strftime("%Y-%m-%dT%H:%M:%SZ")
+
+ r_pdu.asn = rpki.resource_set.resource_set_as.from_sql(
+ self.cur,
+ "SELECT start_as, end_as FROM registrant_asn WHERE registrant_id = %s",
+ (registrant_id,))
+
+ r_pdu.ipv4 = rpki.resource_set.resource_set_ipv4.from_sql(
+ self.cur,
+ "SELECT start_ip, end_ip FROM registrant_net WHERE registrant_id = %s AND version = 4",
+ (registrant_id,))
+
+ r_pdu.ipv6 = rpki.resource_set.resource_set_ipv6.from_sql(
+ self.cur,
+ "SELECT start_ip, end_ip FROM registrant_net WHERE registrant_id = %s AND version = 6",
+ (registrant_id,))
+
+ r_msg.append(r_pdu)
+
+ def handle_list_roa_requests(self, q_pdu, r_msg):
+
+ self.cur.execute(
+ "SELECT roa_request_id, asn FROM roa_request WHERE roa_request_handle = %s",
+ (q_pdu.self_handle,))
+
+ for roa_request_id, asn in self.cur.fetchall():
+
+ r_pdu = rpki.left_right.list_roa_requests_elt()
+ r_pdu.tag = q_pdu.tag
+ r_pdu.self_handle = q_pdu.self_handle
+ r_pdu.asn = asn
+
+ r_pdu.ipv4 = rpki.resource_set.roa_prefix_set_ipv4.from_sql(
+ self.cur,
+ "SELECT prefix, prefixlen, max_prefixlen FROM roa_request_prefix WHERE roa_request_id = %s AND version = 4",
+ (roa_request_id,))
+
+ r_pdu.ipv6 = rpki.resource_set.roa_prefix_set_ipv6.from_sql(
+ self.cur,
+ "SELECT prefix, prefixlen, max_prefixlen FROM roa_request_prefix WHERE roa_request_id = %s AND version = 6",
+ (roa_request_id,))
+
+ r_msg.append(r_pdu)
+
+ handle_dispatch = {
+ rpki.left_right.list_resources_elt : handle_list_resources,
+ rpki.left_right.list_roa_requests_elt : handle_list_roa_requests }
+
+ def handler(self, query, path, cb):
+ try:
+
+ self.db.ping(True)
+
+ r_msg = rpki.left_right.msg.reply()
+
+ try:
+
+ q_msg = rpki.left_right.cms_msg(DER = query).unwrap((self.bpki_ta, self.rpkid_cert))
+
+ if not isinstance(q_msg, rpki.left_right.msg) or not q_msg.is_query():
+ raise rpki.exceptions.BadQuery, "Unexpected %r PDU" % q_msg
+
+ for q_pdu in q_msg:
+
+ try:
+
+ try:
+ h = self.handle_dispatch[type(q_pdu)]
+ except KeyError:
+ raise rpki.exceptions.BadQuery, "Unexpected %r PDU" % q_pdu
+ else:
+ h(self, q_pdu, r_msg)
+
+ except (rpki.async.ExitNow, SystemExit):
+ raise
+
+ except Exception, data:
+ rpki.log.traceback()
+ r_msg.append(rpki.left_right.report_error_elt.from_exception(data, q_pdu.self_handle, q_pdu.tag))
+
+ except (rpki.async.ExitNow, SystemExit):
+ raise
+
+ except Exception, data:
+ rpki.log.traceback()
+ r_msg.append(rpki.left_right.report_error_elt.from_exception(data))
+
+ cb(200, rpki.left_right.cms_msg().wrap(r_msg, self.irdbd_key, self.irdbd_cert))
+
+ except (rpki.async.ExitNow, SystemExit):
+ raise
+
+ except Exception, data:
+ rpki.log.traceback()
+
+ # We only get here in cases where we couldn't or wouldn't generate
+ # <report_error/>, so just return HTTP failure.
+
+ cb(500, "Unhandled exception %s: %s" % (data.__class__.__name__, data))
+
+ def __init__(self):
+
+ os.environ["TZ"] = "UTC"
+ time.tzset()
+
+ cfg_file = "irdbd.conf"
+
+ opts, argv = getopt.getopt(sys.argv[1:], "c:dh?", ["config=", "debug", "help"])
+ for o, a in opts:
+ if o in ("-h", "--help", "-?"):
+ print __doc__
+ sys.exit(0)
+ if o in ("-c", "--config"):
+ cfg_file = a
+ elif o in ("-d", "--debug"):
+ rpki.log.use_syslog = False
+ if argv:
+ raise rpki.exceptions.CommandParseFailure, "Unexpected arguments %s" % argv
+
+ rpki.log.init("irdbd")
+
+ self.cfg = rpki.config.parser(cfg_file, "irdbd")
+
+ startup_msg = self.cfg.get("startup-message", "")
+ if startup_msg:
+ rpki.log.info(startup_msg)
+
+ self.cfg.set_global_flags()
+
+ self.db = MySQLdb.connect(user = self.cfg.get("sql-username"),
+ db = self.cfg.get("sql-database"),
+ passwd = self.cfg.get("sql-password"))
+
+ self.cur = self.db.cursor()
+ self.db.autocommit(True)
+
+ self.bpki_ta = rpki.x509.X509(Auto_update = self.cfg.get("bpki-ta"))
+ self.rpkid_cert = rpki.x509.X509(Auto_update = self.cfg.get("rpkid-cert"))
+ self.irdbd_cert = rpki.x509.X509(Auto_update = self.cfg.get("irdbd-cert"))
+ self.irdbd_key = rpki.x509.RSA( Auto_update = self.cfg.get("irdbd-key"))
+
+ u = urlparse.urlparse(self.cfg.get("http-url"))
+
+ assert u.scheme in ("", "http") and \
+ u.username is None and \
+ u.password is None and \
+ u.params == "" and \
+ u.query == "" and \
+ u.fragment == ""
+
+ rpki.http.server(host = u.hostname or "localhost",
+ port = u.port or 443,
+ handlers = ((u.path, self.handler),))
diff --git a/rpkid/rpki/pubd.py b/rpkid/rpki/pubd.py
new file mode 100644
index 00000000..c842b6c6
--- /dev/null
+++ b/rpkid/rpki/pubd.py
@@ -0,0 +1,169 @@
+"""
+RPKI publication engine.
+
+Usage: python pubd.py [ { -c | --config } configfile ]
+ [ { -h | --help } ]
+ [ { -p | --profile } outputfile ]
+
+Default configuration file is pubd.conf, override with --config option.
+
+$Id$
+
+Copyright (C) 2009--2010 Internet Systems Consortium ("ISC")
+
+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 ISC DISCLAIMS ALL WARRANTIES WITH
+REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+AND FITNESS. IN NO EVENT SHALL ISC 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.
+
+Portions 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.
+"""
+
+import os, time, getopt, sys, re
+import rpki.resource_set, rpki.up_down, rpki.x509, rpki.sql
+import rpki.http, rpki.config, rpki.exceptions, rpki.relaxng
+import rpki.log, rpki.publication
+
+class main(object):
+ """
+ Main program for pubd.
+ """
+
+ def __init__(self):
+
+ os.environ["TZ"] = "UTC"
+ time.tzset()
+
+ self.cfg_file = "pubd.conf"
+ self.profile = False
+
+ opts, argv = getopt.getopt(sys.argv[1:], "c:dhp:?", ["config=", "debug", "help"])
+ for o, a in opts:
+ if o in ("-h", "--help", "-?"):
+ print __doc__
+ sys.exit(0)
+ elif o in ("-c", "--config"):
+ self.cfg_file = a
+ elif o in ("-d", "--debug"):
+ rpki.log.use_syslog = False
+ elif o in ("-p", "--profile"):
+ self.profile = a
+ if argv:
+ raise rpki.exceptions.CommandParseFailure, "Unexpected arguments %s" % argv
+
+ rpki.log.init("pubd")
+
+ if self.profile:
+ import cProfile
+ cProfile.run("self.main()", self.profile)
+ else:
+ self.main()
+
+ def main(self):
+
+ self.cfg = rpki.config.parser(self.cfg_file, "pubd")
+
+ if self.profile:
+ rpki.log.info("Running in profile mode with output to %s" % self.profile)
+
+ self.cfg.set_global_flags()
+
+ self.sql = rpki.sql.session(self.cfg)
+
+ self.bpki_ta = rpki.x509.X509(Auto_update = self.cfg.get("bpki-ta"))
+ self.irbe_cert = rpki.x509.X509(Auto_update = self.cfg.get("irbe-cert"))
+ self.pubd_cert = rpki.x509.X509(Auto_update = self.cfg.get("pubd-cert"))
+ self.pubd_key = rpki.x509.RSA( Auto_update = self.cfg.get("pubd-key"))
+
+ self.http_server_host = self.cfg.get("server-host", "")
+ self.http_server_port = int(self.cfg.get("server-port", "4434"))
+
+ self.publication_base = self.cfg.get("publication-base", "publication/")
+
+ self.publication_multimodule = self.cfg.getboolean("publication-multimodule", False)
+
+ rpki.http.server(
+ host = self.http_server_host,
+ port = self.http_server_port,
+ handlers = (("/control", self.control_handler),
+ ("/client/", self.client_handler)))
+
+ def handler_common(self, query, client, cb, certs, crl = None):
+ """
+ Common PDU handler code.
+ """
+
+ def done(r_msg):
+ reply = rpki.publication.cms_msg().wrap(r_msg, self.pubd_key, self.pubd_cert, crl)
+ self.sql.sweep()
+ cb(reply)
+
+ q_msg = rpki.publication.cms_msg(DER = query).unwrap(certs)
+ q_msg.serve_top_level(self, client, done)
+
+ def control_handler(self, query, path, cb):
+ """
+ Process one PDU from the IRBE.
+ """
+
+ def done(x):
+ cb(200, x)
+
+ rpki.log.trace()
+ try:
+ self.sql.ping()
+ self.handler_common(query, None, done, (self.bpki_ta, self.irbe_cert))
+ except (rpki.async.ExitNow, SystemExit):
+ raise
+ except Exception, data:
+ rpki.log.traceback()
+ cb(500, "Unhandled exception %s" % data)
+
+ client_url_regexp = re.compile("/client/([-A-Z0-9_/]+)$", re.I)
+
+ def client_handler(self, query, path, cb):
+ """
+ Process one PDU from a client.
+ """
+
+ def done(x):
+ cb(200, x)
+
+ rpki.log.trace()
+ try:
+ self.sql.ping()
+ match = self.client_url_regexp.search(path)
+ if match is None:
+ raise rpki.exceptions.BadContactURL, "Bad path: %s" % path
+ client_handle = match.group(1)
+ client = rpki.publication.client_elt.sql_fetch_where1(self, "client_handle = %s", (client_handle,))
+ if client is None:
+ raise rpki.exceptions.ClientNotFound, "Could not find client %s" % client_handle
+ config = rpki.publication.config_elt.fetch(self)
+ if config is None or config.bpki_crl is None:
+ raise rpki.exceptions.CMSCRLNotSet
+ self.handler_common(query, client, done, (self.bpki_ta, client.bpki_cert, client.bpki_glue), config.bpki_crl)
+ except (rpki.async.ExitNow, SystemExit):
+ raise
+ except Exception, data:
+ rpki.log.traceback()
+ cb(500, "Could not process PDU: %s" % data)
diff --git a/rpkid/rpki/rootd.py b/rpkid/rpki/rootd.py
new file mode 100644
index 00000000..74674b57
--- /dev/null
+++ b/rpkid/rpki/rootd.py
@@ -0,0 +1,320 @@
+"""
+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.
+
+$Id$
+
+Copyright (C) 2009--2010 Internet Systems Consortium ("ISC")
+
+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 ISC DISCLAIMS ALL WARRANTIES WITH
+REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+AND FITNESS. IN NO EVENT SHALL ISC 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.
+
+Portions 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.
+"""
+
+import os, time, getopt, sys
+import rpki.resource_set, rpki.up_down, rpki.left_right, rpki.x509
+import rpki.http, rpki.config, rpki.exceptions, rpki.relaxng
+import rpki.sundial, rpki.log
+
+rootd = None
+
+class list_pdu(rpki.up_down.list_pdu):
+ def serve_pdu(self, q_msg, r_msg, ignored, callback, errback):
+ r_msg.payload = rpki.up_down.list_response_pdu()
+ rootd.compose_response(r_msg)
+ callback()
+
+class issue_pdu(rpki.up_down.issue_pdu):
+ def serve_pdu(self, q_msg, r_msg, ignored, callback, errback):
+ self.pkcs10.check_valid_rpki()
+ r_msg.payload = rpki.up_down.issue_response_pdu()
+ rootd.compose_response(r_msg, self.pkcs10)
+ callback()
+
+class revoke_pdu(rpki.up_down.revoke_pdu):
+ def serve_pdu(self, q_msg, r_msg, ignored, callback, errback):
+ rootd.subject_cert = get_subject_cert()
+ if subject_cert is None or subject_cert.gSKI() != self.ski:
+ raise rpki.exceptions.NotInDatabase
+ rootd.del_subject_cert()
+ rootd.del_subject_pkcs10()
+ r_msg.payload = rpki.up_down.revoke_response_pdu()
+ r_msg.payload.class_name = self.class_name
+ r_msg.payload.ski = self.ski
+ callback()
+
+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())
+
+ def log_query(self, child):
+ """
+ Log query we're handling.
+ """
+ rpki.log.info("Serving %s query" % self.type)
+
+class sax_handler(rpki.up_down.sax_handler):
+ pdu = message_pdu
+
+class cms_msg(rpki.up_down.cms_msg):
+ saxify = sax_handler.saxify
+
+class main(object):
+
+ rpki_root_cert = None
+
+ def get_root_cert(self):
+ rpki.log.debug("Read root cert %s" % self.rpki_root_cert_file)
+ self.rpki_root_cert = rpki.x509.X509(Auto_file = self.rpki_root_cert_file)
+
+ def root_newer_than_subject(self):
+ return os.stat(self.rpki_root_cert_file).st_mtime > os.stat(self.rpki_root_dir + self.rpki_subject_cert).st_mtime
+
+ def get_subject_cert(self):
+ filename = self.rpki_root_dir + self.rpki_subject_cert
+ try:
+ x = rpki.x509.X509(Auto_file = filename)
+ rpki.log.debug("Read subject cert %s" % filename)
+ return x
+ except IOError:
+ return None
+
+ def set_subject_cert(self, cert):
+ filename = self.rpki_root_dir + self.rpki_subject_cert
+ rpki.log.debug("Writing subject cert %s, SKI %s" % (filename, cert.hSKI()))
+ f = open(filename, "wb")
+ f.write(cert.get_DER())
+ f.close()
+
+ def del_subject_cert(self):
+ filename = self.rpki_root_dir + self.rpki_subject_cert
+ rpki.log.debug("Deleting subject cert %s" % filename)
+ os.remove(filename)
+
+ def get_subject_pkcs10(self):
+ try:
+ x = rpki.x509.PKCS10(Auto_file = self.rpki_subject_pkcs10)
+ rpki.log.debug("Read subject PKCS #10 %s" % self.rpki_subject_pkcs10)
+ return x
+ except IOError:
+ return None
+
+ def set_subject_pkcs10(self, pkcs10):
+ rpki.log.debug("Writing subject PKCS #10 %s" % self.rpki_subject_pkcs10)
+ f = open(self.rpki_subject_pkcs10, "wb")
+ f.write(pkcs10.get_DER())
+ f.close()
+
+ def del_subject_pkcs10(self):
+ rpki.log.debug("Deleting subject PKCS #10 %s" % self.rpki_subject_pkcs10)
+ try:
+ os.remove(self.rpki_subject_pkcs10)
+ except OSError:
+ pass
+
+ def issue_subject_cert_maybe(self, new_pkcs10):
+ now = rpki.sundial.now()
+ subject_cert = self.get_subject_cert()
+ old_pkcs10 = self.get_subject_pkcs10()
+ if new_pkcs10 is not None and new_pkcs10 != old_pkcs10:
+ self.set_subject_pkcs10(new_pkcs10)
+ if subject_cert is not None:
+ rpki.log.debug("PKCS #10 changed, regenerating subject certificate")
+ subject_cert = None
+ if subject_cert is not None and subject_cert.getNotAfter() <= now + self.rpki_subject_regen:
+ rpki.log.debug("Subject certificate has reached expiration threshold, regenerating")
+ subject_cert = None
+ if subject_cert is not None and self.root_newer_than_subject():
+ rpki.log.debug("Root certificate has changed, regenerating subject")
+ subject_cert = None
+ self.get_root_cert()
+ if subject_cert is not None:
+ return subject_cert
+ pkcs10 = old_pkcs10 if new_pkcs10 is None else new_pkcs10
+ if pkcs10 is None:
+ rpki.log.debug("No PKCS #10 request, can't generate subject certificate yet")
+ return None
+ resources = self.rpki_root_cert.get_3779resources()
+ rpki.log.info("Generating subject cert with resources " + str(resources))
+ req_key = pkcs10.getPublicKey()
+ req_sia = pkcs10.get_SIA()
+ crldp = self.rpki_base_uri + self.rpki_root_crl
+ serial = now.totimestamp()
+ subject_cert = self.rpki_root_cert.issue(
+ keypair = self.rpki_root_key,
+ subject_key = req_key,
+ serial = serial,
+ sia = req_sia,
+ aia = self.rpki_root_cert_uri,
+ crldp = crldp,
+ resources = resources,
+ notAfter = now + self.rpki_subject_lifetime)
+ crl = rpki.x509.CRL.generate(
+ keypair = self.rpki_root_key,
+ issuer = self.rpki_root_cert,
+ serial = serial,
+ thisUpdate = now,
+ nextUpdate = now + self.rpki_subject_lifetime,
+ revokedCertificates = ())
+ rpki.log.debug("Writing CRL %s" % (self.rpki_root_dir + self.rpki_root_crl))
+ f = open(self.rpki_root_dir + self.rpki_root_crl, "wb")
+ f.write(crl.get_DER())
+ f.close()
+ manifest_resources = rpki.resource_set.resource_bag.from_inheritance()
+ manifest_keypair = rpki.x509.RSA.generate()
+ manifest_cert = self.rpki_root_cert.issue(
+ keypair = self.rpki_root_key,
+ subject_key = manifest_keypair.get_RSApublic(),
+ serial = serial + 1,
+ sia = None,
+ aia = self.rpki_root_cert_uri,
+ crldp = crldp,
+ resources = manifest_resources,
+ notAfter = now + self.rpki_subject_lifetime,
+ is_ca = False)
+ manifest = rpki.x509.SignedManifest.build(
+ serial = serial,
+ thisUpdate = now,
+ nextUpdate = now + self.rpki_subject_lifetime,
+ names_and_objs = [(self.rpki_subject_cert, subject_cert), (self.rpki_root_crl, crl)],
+ keypair = manifest_keypair,
+ certs = manifest_cert)
+ rpki.log.debug("Writing manifest %s" % (self.rpki_root_dir + self.rpki_root_manifest))
+ f = open(self.rpki_root_dir + self.rpki_root_manifest, "wb")
+ f.write(manifest.get_DER())
+ f.close()
+ self.set_subject_cert(subject_cert)
+ return subject_cert
+
+ def compose_response(self, r_msg, pkcs10 = None):
+ subject_cert = self.issue_subject_cert_maybe(pkcs10)
+ rc = rpki.up_down.class_elt()
+ rc.class_name = self.rpki_class_name
+ rc.cert_url = rpki.up_down.multi_uri(self.rpki_root_cert_uri)
+ rc.from_resource_bag(self.rpki_root_cert.get_3779resources())
+ rc.issuer = self.rpki_root_cert
+ r_msg.payload.classes.append(rc)
+ if subject_cert is not None:
+ rc.certs.append(rpki.up_down.certificate_elt())
+ rc.certs[0].cert_url = rpki.up_down.multi_uri(self.rpki_base_uri + self.rpki_subject_cert)
+ rc.certs[0].cert = subject_cert
+
+ def up_down_handler(self, query, path, cb):
+ try:
+ q_msg = cms_msg(DER = query).unwrap((self.bpki_ta, self.child_bpki_cert))
+ except (rpki.async.ExitNow, SystemExit):
+ raise
+ except Exception, e:
+ rpki.log.traceback()
+ return cb(400, "Could not process PDU: %s" % e)
+
+ def done(r_msg):
+ cb(200, cms_msg().wrap(r_msg, self.rootd_bpki_key, self.rootd_bpki_cert, self.rootd_bpki_crl))
+
+ try:
+ q_msg.serve_top_level(None, done)
+ except (rpki.async.ExitNow, SystemExit):
+ raise
+ except Exception, e:
+ rpki.log.traceback()
+ try:
+ done(q_msg.serve_error(e))
+ except (rpki.async.ExitNow, SystemExit):
+ raise
+ except Exception, e:
+ rpki.log.traceback()
+ cb(500, "Could not process PDU: %s" % e)
+
+ def __init__(self):
+
+ global rootd
+ rootd = self # Gross, but simpler than what we'd have to do otherwise
+
+ os.environ["TZ"] = "UTC"
+ time.tzset()
+
+ self.cfg_file = "rootd.conf"
+
+ opts, argv = getopt.getopt(sys.argv[1:], "c:dh?", ["config=", "debug", "help"])
+ for o, a in opts:
+ if o in ("-h", "--help", "-?"):
+ print __doc__
+ sys.exit(0)
+ elif o in ("-c", "--config"):
+ self.cfg_file = a
+ elif o in ("-d", "--debug"):
+ rpki.log.use_syslog = False
+ if argv:
+ raise rpki.exceptions.CommandParseFailure, "Unexpected arguments %s" % argv
+
+ rpki.log.init("rootd")
+
+ self.cfg = rpki.config.parser(self.cfg_file, "rootd")
+
+ self.cfg.set_global_flags()
+
+ self.bpki_ta = rpki.x509.X509(Auto_file = self.cfg.get("bpki-ta"))
+ self.rootd_bpki_key = rpki.x509.RSA( Auto_file = self.cfg.get("rootd-bpki-key"))
+ self.rootd_bpki_cert = rpki.x509.X509(Auto_file = self.cfg.get("rootd-bpki-cert"))
+ self.rootd_bpki_crl = rpki.x509.CRL( Auto_file = self.cfg.get("rootd-bpki-crl"))
+ self.child_bpki_cert = rpki.x509.X509(Auto_file = self.cfg.get("child-bpki-cert"))
+
+ self.http_server_host = self.cfg.get("server-host", "")
+ self.http_server_port = int(self.cfg.get("server-port"))
+
+ self.rpki_class_name = self.cfg.get("rpki-class-name", "wombat")
+
+ self.rpki_root_dir = self.cfg.get("rpki-root-dir")
+ self.rpki_base_uri = self.cfg.get("rpki-base-uri", "rsync://" + self.rpki_class_name + ".invalid/")
+
+ self.rpki_root_key = rpki.x509.RSA( Auto_file = self.cfg.get("rpki-root-key"))
+ self.rpki_root_cert_file = self.cfg.get("rpki-root-cert")
+ self.rpki_root_cert_uri = self.cfg.get("rpki-root-cert-uri", self.rpki_base_uri + "Root.cer")
+
+ self.rpki_root_manifest = self.cfg.get("rpki-root-manifest", "Root.mnf")
+ self.rpki_root_crl = self.cfg.get("rpki-root-crl", "Root.crl")
+ self.rpki_subject_cert = self.cfg.get("rpki-subject-cert", "Child.cer")
+ self.rpki_subject_pkcs10 = self.cfg.get("rpki-subject-pkcs10", "Child.pkcs10")
+
+ self.rpki_subject_lifetime = rpki.sundial.timedelta.parse(self.cfg.get("rpki-subject-lifetime", "30d"))
+ self.rpki_subject_regen = rpki.sundial.timedelta.parse(self.cfg.get("rpki-subject-regen", self.rpki_subject_lifetime.convert_to_seconds() / 2))
+
+ rpki.http.server(host = self.http_server_host,
+ port = self.http_server_port,
+ handlers = self.up_down_handler)
diff --git a/rpkid/rpki/rpkid.py b/rpkid/rpki/rpkid.py
new file mode 100644
index 00000000..927e1456
--- /dev/null
+++ b/rpkid/rpki/rpkid.py
@@ -0,0 +1,106 @@
+"""
+RPKI engine daemon.
+
+Usage: python rpkid.py [ { -c | --config } configfile ]
+ [ { -h | --help } ]
+ [ { -p | --profile } outputfile ]
+
+Default configuration file is rpkid.conf, override with --config option.
+
+$Id$
+
+Copyright (C) 2009--2010 Internet Systems Consortium ("ISC")
+
+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 ISC DISCLAIMS ALL WARRANTIES WITH
+REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+AND FITNESS. IN NO EVENT SHALL ISC 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.
+
+Portions 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.
+"""
+
+import os, time, getopt, sys
+import rpki.resource_set, rpki.up_down, rpki.left_right, rpki.x509, rpki.sql
+import rpki.http, rpki.config, rpki.exceptions, rpki.relaxng, rpki.log
+import rpki.rpki_engine
+
+class main(object):
+ """
+ Main program for rpkid. This class probably ought to be merged with
+ rpki.rpki_engine.rpkid_context, but that can wait, for today the
+ task is to move all the real code out of the scripts, this is the
+ minimal necessary change to do that.
+ """
+
+ def __init__(self):
+
+ os.environ["TZ"] = "UTC"
+ time.tzset()
+
+ self.cfg_file = "rpkid.conf"
+ self.profile = None
+
+ opts, argv = getopt.getopt(sys.argv[1:], "c:dhp:?", ["config=", "debug", "help", "profile="])
+ for o, a in opts:
+ if o in ("-h", "--help", "-?"):
+ print __doc__
+ sys.exit(0)
+ elif o in ("-d", "--debug"):
+ rpki.log.use_syslog = False
+ elif o in ("-c", "--config"):
+ self.cfg_file = a
+ elif o in ("-p", "--profile"):
+ self.profile = a
+ if argv:
+ raise rpki.exceptions.CommandParseFailure, "Unexpected arguments %s" % argv
+
+ rpki.log.init("rpkid")
+
+ if self.profile:
+ import cProfile
+ cProfile.run("self.main()", self.profile)
+ else:
+ self.main()
+
+ def main(self):
+
+ self.cfg = rpki.config.parser(self.cfg_file, "rpkid")
+
+ startup_msg = self.cfg.get("startup-message", "")
+ if startup_msg:
+ rpki.log.info(startup_msg)
+
+ if self.profile:
+ rpki.log.info("Running in profile mode with output to %s" % self.profile)
+
+ self.cfg.set_global_flags()
+
+ gctx = rpki.rpki_engine.rpkid_context(self.cfg)
+
+ gctx.start_cron()
+
+ rpki.http.server(
+ host = gctx.http_server_host,
+ port = gctx.http_server_port,
+ handlers = (("/left-right", gctx.left_right_handler),
+ ("/up-down/", gctx.up_down_handler),
+ ("/cronjob", gctx.cronjob_handler)))