aboutsummaryrefslogtreecommitdiff
path: root/rpkid/rpki/rootd.py
diff options
context:
space:
mode:
Diffstat (limited to 'rpkid/rpki/rootd.py')
-rw-r--r--rpkid/rpki/rootd.py385
1 files changed, 0 insertions, 385 deletions
diff --git a/rpkid/rpki/rootd.py b/rpkid/rpki/rootd.py
deleted file mode 100644
index 43e84873..00000000
--- a/rpkid/rpki/rootd.py
+++ /dev/null
@@ -1,385 +0,0 @@
-# $Id$
-#
-# Copyright (C) 2013--2014 Dragon Research Labs ("DRL")
-# Portions copyright (C) 2009--2012 Internet Systems Consortium ("ISC")
-# 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 notices and this permission notice appear in all copies.
-#
-# THE SOFTWARE IS PROVIDED "AS IS" AND DRL, ISC, AND ARIN DISCLAIM ALL
-# WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
-# WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL DRL,
-# ISC, OR 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. Not recommended for
-production use. Overrides a bunch of method definitions from the
-rpki.* classes in order to reuse as much code as possible.
-"""
-
-import os
-import time
-import argparse
-import sys
-import rpki.resource_set
-import rpki.up_down
-import rpki.left_right
-import rpki.x509
-import rpki.http
-import rpki.config
-import rpki.exceptions
-import rpki.relaxng
-import rpki.sundial
-import rpki.log
-import rpki.daemonize
-
-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_request_ca()
- 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):
- rpki.log.debug("Revocation requested for SKI %s" % self.ski)
- subject_cert = rootd.get_subject_cert()
- if subject_cert is None:
- rpki.log.debug("No subject certificate, nothing to revoke")
- raise rpki.exceptions.NotInDatabase
- if subject_cert.gSKI() != self.ski:
- rpki.log.debug("Subject certificate has different SKI %s, not revoking" % subject_cert.gSKI())
- raise rpki.exceptions.NotInDatabase
- rpki.log.debug("Revoking certificate %s" % self.ski)
- now = rpki.sundial.now()
- rootd.revoke_subject_cert(now)
- rootd.del_subject_cert()
- rootd.del_subject_pkcs10()
- rootd.generate_crl_and_manifest(now)
- 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 error_response_pdu(rpki.up_down.error_response_pdu):
- exceptions = rpki.up_down.error_response_pdu.exceptions.copy()
- exceptions[rpki.exceptions.ClassNameUnknown, revoke_pdu] = 1301
- exceptions[rpki.exceptions.NotInDatabase, revoke_pdu] = 1302
-
-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" : error_response_pdu }
-
- type2name = dict((v, k) for k, v in name2type.items())
-
- error_pdu_type = error_response_pdu
-
- 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):
-
- 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(os.path.join(self.rpki_root_dir, self.rpki_subject_cert)).st_mtime
-
- def get_subject_cert(self):
- filename = os.path.join(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 = os.path.join(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 = os.path.join(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")
- self.revoke_subject_cert(now)
- 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")
- self.revoke_subject_cert(now)
- subject_cert = None
- if subject_cert is not None and self.root_newer_than_subject():
- rpki.log.debug("Root certificate has changed, regenerating subject")
- self.revoke_subject_cert(now)
- 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()
- notAfter = now + self.rpki_subject_lifetime
- rpki.log.info("Generating subject cert %s with resources %s, expires %s" % (
- self.rpki_base_uri + self.rpki_subject_cert, resources, notAfter))
- req_key = pkcs10.getPublicKey()
- req_sia = pkcs10.get_SIA()
- self.next_serial_number()
- subject_cert = self.rpki_root_cert.issue(
- keypair = self.rpki_root_key,
- subject_key = req_key,
- serial = self.serial_number,
- sia = req_sia,
- aia = self.rpki_root_cert_uri,
- crldp = self.rpki_base_uri + self.rpki_root_crl,
- resources = resources,
- notBefore = now,
- notAfter = notAfter)
- self.set_subject_cert(subject_cert)
- self.generate_crl_and_manifest(now)
- return subject_cert
-
- def generate_crl_and_manifest(self, now):
- subject_cert = self.get_subject_cert()
- self.next_serial_number()
- self.next_crl_number()
- while self.revoked and self.revoked[0][1] + 2 * self.rpki_subject_regen < now:
- del self.revoked[0]
- crl = rpki.x509.CRL.generate(
- keypair = self.rpki_root_key,
- issuer = self.rpki_root_cert,
- serial = self.crl_number,
- thisUpdate = now,
- nextUpdate = now + self.rpki_subject_regen,
- revokedCertificates = self.revoked)
- rpki.log.debug("Writing CRL %s" % os.path.join(self.rpki_root_dir, self.rpki_root_crl))
- f = open(os.path.join(self.rpki_root_dir, self.rpki_root_crl), "wb")
- f.write(crl.get_DER())
- f.close()
- manifest_content = [(self.rpki_root_crl, crl)]
- if subject_cert is not None:
- manifest_content.append((self.rpki_subject_cert, subject_cert))
- 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_public(),
- serial = self.serial_number,
- sia = (None, None, self.rpki_base_uri + self.rpki_root_manifest),
- aia = self.rpki_root_cert_uri,
- crldp = self.rpki_base_uri + self.rpki_root_crl,
- resources = manifest_resources,
- notBefore = now,
- notAfter = now + self.rpki_subject_lifetime,
- is_ca = False)
- manifest = rpki.x509.SignedManifest.build(
- serial = self.crl_number,
- thisUpdate = now,
- nextUpdate = now + self.rpki_subject_regen,
- names_and_objs = manifest_content,
- keypair = manifest_keypair,
- certs = manifest_cert)
- rpki.log.debug("Writing manifest %s" % os.path.join(self.rpki_root_dir, self.rpki_root_manifest))
- f = open(os.path.join(self.rpki_root_dir, self.rpki_root_manifest), "wb")
- f.write(manifest.get_DER())
- f.close()
-
- def revoke_subject_cert(self, now):
- self.revoked.append((self.get_subject_cert().getSerial(), now))
-
- 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_cms = cms_msg(DER = query)
- q_msg = q_cms.unwrap((self.bpki_ta, self.child_bpki_cert))
- self.cms_timestamp = q_cms.check_replay(self.cms_timestamp, path)
- except (rpki.async.ExitNow, SystemExit):
- raise
- except Exception, e:
- rpki.log.traceback()
- return cb(400, reason = "Could not process PDU: %s" % e)
-
- def done(r_msg):
- cb(200, body = cms_msg().wrap(
- r_msg, self.rootd_bpki_key, self.rootd_bpki_cert,
- self.rootd_bpki_crl if self.include_bpki_crl else None))
-
- 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, reason = "Could not process PDU: %s" % e)
-
-
- def next_crl_number(self):
- if self.crl_number is None:
- try:
- crl = rpki.x509.CRL(DER_file = os.path.join(self.rpki_root_dir, self.rpki_root_crl))
- self.crl_number = crl.getCRLNumber()
- except: # pylint: disable=W0702
- self.crl_number = 0
- self.crl_number += 1
- return self.crl_number
-
-
- def next_serial_number(self):
- if self.serial_number is None:
- subject_cert = self.get_subject_cert()
- if subject_cert is not None:
- self.serial_number = subject_cert.getSerial() + 1
- else:
- self.serial_number = 0
- self.serial_number += 1
- return self.serial_number
-
-
- def __init__(self):
-
- global rootd
- rootd = self # Gross, but simpler than what we'd have to do otherwise
-
- self.rpki_root_cert = None
- self.serial_number = None
- self.crl_number = None
- self.revoked = []
- self.cms_timestamp = None
-
- os.environ["TZ"] = "UTC"
- time.tzset()
-
- parser = argparse.ArgumentParser(description = __doc__)
- parser.add_argument("-c", "--config",
- help = "override default location of configuration file")
- parser.add_argument("-d", "--debug", action = "store_true",
- help = "enable debugging mode")
- parser.add_argument("-f", "--foreground", action = "store_true",
- help = "do not daemonize")
- parser.add_argument("--pidfile",
- help = "override default location of pid file")
- args = parser.parse_args()
-
- rpki.log.init("rootd", use_syslog = not args.debug)
-
- self.cfg = rpki.config.parser(args.config, "rootd")
- self.cfg.set_global_flags()
-
- if not args.foreground and not args.debug:
- rpki.daemonize.daemon(pidfile = args.pidfile)
-
- self.bpki_ta = rpki.x509.X509(Auto_update = self.cfg.get("bpki-ta"))
- self.rootd_bpki_key = rpki.x509.RSA( Auto_update = self.cfg.get("rootd-bpki-key"))
- self.rootd_bpki_cert = rpki.x509.X509(Auto_update = self.cfg.get("rootd-bpki-cert"))
- self.rootd_bpki_crl = rpki.x509.CRL( Auto_update = self.cfg.get("rootd-bpki-crl"))
- self.child_bpki_cert = rpki.x509.X509(Auto_update = self.cfg.get("child-bpki-cert"))
-
- self.http_server_host = self.cfg.get("server-host", "")
- self.http_server_port = self.cfg.getint("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_update = 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.mft")
- 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", "8w"))
- self.rpki_subject_regen = rpki.sundial.timedelta.parse(self.cfg.get("rpki-subject-regen", self.rpki_subject_lifetime.convert_to_seconds() / 2))
-
- self.include_bpki_crl = self.cfg.getboolean("include-bpki-crl", False)
-
- rpki.http.server(host = self.http_server_host,
- port = self.http_server_port,
- handlers = self.up_down_handler)