diff options
author | Rob Austein <sra@hactrn.net> | 2011-12-25 18:36:49 +0000 |
---|---|---|
committer | Rob Austein <sra@hactrn.net> | 2011-12-25 18:36:49 +0000 |
commit | 523a1f269dc1c19e3537fc0d1dc9b96a1e7fb8dc (patch) | |
tree | 69159fc376fc35ad525600d184c3598d3532f6f6 | |
parent | 75c16c86b64dc47bc8559946d4e133586b9a2919 (diff) |
Clean up rootd cross-certification nastiness (another TLS relic).
svn path=/branches/tk100/; revision=4134
-rw-r--r-- | rpkid/rpki/irdb/models.py | 45 | ||||
-rw-r--r-- | rpkid/rpki/rpkic.py | 93 | ||||
-rw-r--r-- | scripts/convert-from-entitydb-to-sql.py | 59 |
3 files changed, 123 insertions, 74 deletions
diff --git a/rpkid/rpki/irdb/models.py b/rpkid/rpki/irdb/models.py index 2cc3f5aa..fad3b31a 100644 --- a/rpkid/rpki/irdb/models.py +++ b/rpkid/rpki/irdb/models.py @@ -27,13 +27,22 @@ import rpki.x509 import rpki.sundial import socket +## @var ip_version_choices +# Choice argument for fields implementing IP version numbers. + +ip_version_choices = ((4, "IPv4"), (6, "IPv6")) + ### +# Field types + class HandleField(django.db.models.CharField): """ A handle field type. """ + description = 'A "handle" in one of the RPKI protocols' + def __init__(self, *args, **kwargs): kwargs["max_length"] = 120 django.db.models.CharField.__init__(self, *args, **kwargs) @@ -44,6 +53,8 @@ class EnumField(django.db.models.PositiveSmallIntegerField): in SQL. """ + description = "An enumeration type" + __metaclass__ = django.db.models.SubfieldBase def __init__(self, *args, **kwargs): @@ -64,6 +75,8 @@ class SundialField(django.db.models.DateTimeField): A field type for our customized datetime objects. """ + description = "A datetime type using our customized datetime objects" + def to_python(self, value): return rpki.sundial.datetime.fromdatetime( django.db.models.DateTimeField.to_python(self, value)) @@ -76,7 +89,7 @@ class SundialField(django.db.models.DateTimeField): class DERField(django.db.models.Field): """ - A field type for DER objects. + A field type to represent ASN.1 DER objects as SQL BLOBs. This is an abstract class, subclasses need to define rpki_type. """ @@ -131,10 +144,9 @@ class SignedReferralField(DERField): description = "CMS signed object containing XML" rpki_type = rpki.x509.SignedReferral -## @var ip_version_choices -# Choice argument for fields implementing IP version numbers. +### -ip_version_choices = ((4, "IPv4"), (6, "IPv6")) +# Custom managers class CertificateManager(django.db.models.Manager): @@ -183,7 +195,7 @@ class ServerCAManager(CertificateManager): def _get_or_certify_keys(self, kwargs): return { "pk" : 1 } -class ReferralCertificateManager(CertificateManager): +class ResourceHolderEEManager(CertificateManager): def _get_or_certify_keys(self, kwargs): return { "issuer" : kwargs["issuer"] } @@ -366,9 +378,9 @@ class EECertificate(Certificate): validity_interval = self.default_interval, is_ca = False) -class ServerCertificate(EECertificate): +class ServerEE(EECertificate): issuer = django.db.models.ForeignKey(ServerCA, related_name = "ee_certificates") - purpose = EnumField(choices = ("rpkid", "pubd", "irdbd", "irbe", "rootd")) + purpose = EnumField(choices = ("rpkid", "pubd", "irdbd", "irbe")) class Meta: unique_together = ("issuer", "purpose") @@ -377,14 +389,24 @@ class ServerCertificate(EECertificate): def subject_name(self): return rpki.x509.X501DN("%s BPKI %s EE" % (socket.gethostname(), self.get_purpose_display())) -class ReferralCertificate(EECertificate): +class Referral(EECertificate): issuer = django.db.models.OneToOneField(ResourceHolderCA, related_name = "referral_certificate") - objects = ReferralCertificateManager() + objects = ResourceHolderEEManager() @property def subject_name(self): return rpki.x509.X501DN("%s BPKI Referral EE" % self.issuer.handle) +class Turtle(django.db.models.Model): + service_uri = django.db.models.CharField(max_length = 255) + +class Rootd(EECertificate, Turtle): + issuer = django.db.models.OneToOneField(ResourceHolderCA, related_name = "rootd") + objects = ResourceHolderEEManager() + + @property + def subject_name(self): + return rpki.x509.X501DN("%s BPKI rootd EE" % self.issuer.handle) class BSC(Certificate): issuer = django.db.models.ForeignKey(ResourceHolderCA, related_name = "bscs") @@ -423,11 +445,10 @@ class ChildNet(django.db.models.Model): class Meta: unique_together = ("child", "start_ip", "end_ip", "version") -class Parent(CrossCertification): +class Parent(CrossCertification, Turtle): issuer = django.db.models.ForeignKey(ResourceHolderCA, related_name = "parents") parent_handle = HandleField() child_handle = HandleField() - service_uri = django.db.models.CharField(max_length = 255) repository_type = EnumField(choices = ("none", "offer", "referral")) referrer = HandleField(null = True, blank = True) referral_authorization = SignedReferralField(null = True, blank = True) @@ -456,7 +477,7 @@ class Repository(CrossCertification): client_handle = HandleField() service_uri = django.db.models.CharField(max_length = 255) sia_base = django.db.models.TextField() - parent = django.db.models.OneToOneField(Parent, related_name = "repository") + turtle = django.db.models.OneToOneField(Turtle, related_name = "repository") class Client(CrossCertification): issuer = django.db.models.ForeignKey(ServerCA, related_name = "clients") diff --git a/rpkid/rpki/rpkic.py b/rpkid/rpki/rpkic.py index 88e3f26b..48749a15 100644 --- a/rpkid/rpki/rpkic.py +++ b/rpkid/rpki/rpkic.py @@ -270,29 +270,18 @@ class main(rpki.cli.Cmd): if created: print "Created new BPKI resource CA for identity %s" % self.handle - if self.run_rpkid or self.run_pubd or self.run_rootd: + if self.run_rpkid or self.run_pubd: self.server_ca, created = rpki.irdb.ServerCA.objects.get_or_certify() if created: print "Created new BPKI server CA" - if self.run_rpkid: - rpki.irdb.ServerCertificate.objects.get_or_certify(issuer = self.server_ca, purpose = "rpkid") - rpki.irdb.ServerCertificate.objects.get_or_certify(issuer = self.server_ca, purpose = "irdbd") - if self.run_pubd: - rpki.irdb.ServerCertificate.objects.get_or_certify(issuer = self.server_ca, purpose = "pubd") - if self.run_rpkid or self.run_pubd: - rpki.irdb.ServerCertificate.objects.get_or_certify(issuer = self.server_ca, purpose = "irbe") - if self.run_rootd: - rpki.irdb.ServerCertificate.objects.get_or_certify(issuer = self.server_ca, purpose = "rootd") - - ## @todo - # Why do we issue root's EE certificate under our server CA? - # We've "always" done this, but does it make sense now? rootd - # only speaks up-down, so it's really just another resource - # holder. If we just issued it under our resource CA, we - # wouldn't have to cross certify anything to talk to it. Which - # might in itself break something, as it'd be the only parent we - # -didn't- have to cross-certify. Leave alone for now, but - # think about this later. + rpki.irdb.ServerEE.objects.get_or_certify(issuer = self.server_ca, purpose = "irbe") + + if self.run_rpkid: + rpki.irdb.ServerEE.objects.get_or_certify(issuer = self.server_ca, purpose = "rpkid") + rpki.irdb.ServerEE.objects.get_or_certify(issuer = self.server_ca, purpose = "irdbd") + + if self.run_pubd: + rpki.irdb.ServerEE.objects.get_or_certify(issuer = self.server_ca, purpose = "pubd") # Build the identity.xml file. Need to check for existing file so we don't # overwrite? Worry about that later. @@ -302,25 +291,16 @@ class main(rpki.cli.Cmd): etree_write(e, "identity.xml", msg = None if self.run_rootd else 'This is the "identity" file you will need to send to your parent') - # If we're running rootd, construct a fake parent to go with it, - # and cross-certify in both directions so we can talk to rootd. - if self.run_rootd: + assert self.run_rpkid and self.run_pubd - rpki.irdb.Parent.objects.get_or_certify( - issuer = self.resource_ca, - handle = self.handle, - parent_handle = self.handle, - child_handle = self.handle, - ta = self.server_ca.certificate, - service_uri = "http://localhost:%s/" % self.cfg.get("rootd_server_port"), - repository_type = "offer") - - rpki.irdb.Child.objects.get_or_certify( - issuer = self.server_ca, - handle = self.handle, - ta = self.resource_ca.certificate, - valid_until = self.resource_ca.certificate.getNotAfter()) + rpki.irdb.Rootd.objects.get_or_certify( + issuer = self.resource_ca, + service_uri = "http://localhost:%s/" % self.cfg.get("rootd_server_port")) + + # The following assumes we'll set up the respository manually. + # Not sure this is a reasonable assumption, particularly if we + # ever fix rootd to use the publication protocol. try: self.resource_ca.repositories.get(handle = self.handle) @@ -347,9 +327,10 @@ class main(rpki.cli.Cmd): for model in (rpki.irdb.ServerCA, rpki.irdb.ResourceHolderCA, - rpki.irdb.ServerCertificate, + rpki.irdb.ServerEE, + rpki.irdb.Referral, + rpki.irdb.Rootd, rpki.irdb.HostedCA, - rpki.irdb.ReferralCertificate, rpki.irdb.BSC, rpki.irdb.Child, rpki.irdb.Parent, @@ -661,9 +642,12 @@ class main(rpki.cli.Cmd): print "Repository response associated with parent_handle %r" % parent_handle try: - parent = self.resource_ca.parents.get(handle = parent_handle) + if parent_handle == self.handle: + turtle = self.resource_ca.rootd + else: + turtle = self.resource_ca.parents.get(handle = parent_handle) - except rpki.irdb.Parent.DoesNotExist: + except (rpki.irdb.Parent.DoesNotExist, rpki.irdb.Rootd.DoesNotExist): print "Could not find parent %r in our database" % parent_handle else: @@ -674,7 +658,7 @@ class main(rpki.cli.Cmd): service_uri = r.get("service_uri"), sia_base = r.get("sia_base"), ta = rpki.x509.X509(Base64 = r.findtext("bpki_server_ta")), - parent = parent) + turtle = turtle) def do_delete_repository(self, arg): """ @@ -1032,6 +1016,31 @@ class main(rpki.cli.Cmd): recipient_name = parent.parent_handle, bpki_cms_cert = parent.certificate)) + if ca.rootd: + + parent_pdu = parent_pdus.pop(ca.handle, None) + + if (parent_pdu is None or + parent_pdu.bsc_handle != bsc_handle or + parent_pdu.repository_handle != ca.handle or + parent_pdu.peer_contact_uri != ca.rootd.service_uri or + parent_pdu.sia_base != ca.rootd.repository.sia_base or + parent_pdu.sender_name != ca.handle or + parent_pdu.recipient_name != ca.handle or + parent_pdu.bpki_cms_cert != ca.rootd.certificate): + rpkid_query.append(rpki.left_right.parent_elt.make_pdu( + action = "create" if parent_pdu is None else "set", + tag = ca.handle, + self_handle = ca.handle, + parent_handle = ca.handle, + bsc_handle = bsc_handle, + repository_handle = ca.handle, + peer_contact_uri = ca.rootd.service_uri, + sia_base = ca.rootd.repository.sia_base, + sender_name = ca.handle, + recipient_name = ca.handle, + bpki_cms_cert = ca.rootd.certificate)) + rpkid_query.extend(rpki.left_right.parent_elt.make_pdu( action = "destroy", self_handle = ca.handle, parent_handle = p) for p in parent_pdus) diff --git a/scripts/convert-from-entitydb-to-sql.py b/scripts/convert-from-entitydb-to-sql.py index 64f0d31a..3ba5241a 100644 --- a/scripts/convert-from-entitydb-to-sql.py +++ b/scripts/convert-from-entitydb-to-sql.py @@ -109,16 +109,16 @@ def read_openssl_serial(filename): f.close() return int(text.strip(), 16) -def get_or_create_ServerCertificate(issuer, purpose): +def get_or_create_ServerEE(issuer, purpose): cer = rpki.x509.X509(Auto_file = os.path.join(bpki, "servers", purpose + ".cer")) key = rpki.x509.RSA(Auto_file = os.path.join(bpki, "servers", purpose + ".key")) - rpki.irdb.ServerCertificate.objects.get_or_create( + rpki.irdb.ServerEE.objects.get_or_create( issuer = issuer, purpose = purpose, certificate = cer, private_key = key) -# Load BPKI CA data +# Load BPKI CAs and directly certified EEs cer = rpki.x509.X509(Auto_file = os.path.join(bpki, "resources", "ca.cer")) key = rpki.x509.RSA(Auto_file = os.path.join(bpki, "resources", "ca.key")) @@ -139,23 +139,21 @@ resource_ca = rpki.irdb.ResourceHolderCA.objects.get_or_create( if os.path.exists(os.path.join(bpki, "resources", "referral.cer")): cer = rpki.x509.X509(Auto_file = os.path.join(bpki, "resources", "referral.cer")) key = rpki.x509.RSA(Auto_file = os.path.join(bpki, "resources", "referral.key")) - rpki.irdb.ReferralCertificate.objects.get_or_create( + rpki.irdb.Referral.objects.get_or_create( issuer = resource_ca, certificate = cer, private_key = key) -# Load BPKI server EE certificates and keys +run_rpkid = cfg.getboolean("run_rpkid", section = "myrpki") +run_pubd = cfg.getboolean("run_pubd", section = "myrpki") +run_rootd = cfg.getboolean("run_rootd", section = "myrpki") -run_flags = dict((i, cfg.getboolean(i, section = "myrpki")) - for i in ("run_rpkid", "run_pubd", "run_rootd")) - -if any(run_flags.itervalues()): +if run_rpkid or run_pubd: cer = rpki.x509.X509(Auto_file = os.path.join(bpki, "servers", "ca.cer")) key = rpki.x509.RSA(Auto_file = os.path.join(bpki, "servers", "ca.key")) crl = rpki.x509.CRL(Auto_file = os.path.join(bpki, "servers", "ca.crl")) serial = read_openssl_serial(os.path.join(bpki, "servers", "serial")) crl_number = read_openssl_serial(os.path.join(bpki, "servers", "crl_number")) - server_ca = rpki.irdb.ServerCA.objects.get_or_create( certificate = cer, private_key = key, @@ -164,18 +162,29 @@ if any(run_flags.itervalues()): next_crl_number = crl_number, last_crl_update = crl.getThisUpdate().to_sql(), next_crl_update = crl.getNextUpdate().to_sql())[0] + get_or_create_ServerEE(server_ca, "irbe") - get_or_create_ServerCertificate(server_ca, "irbe") - if run_flags["run_rpkid"]: - get_or_create_ServerCertificate(server_ca, "rpkid") - get_or_create_ServerCertificate(server_ca, "irdbd") - if run_flags["run_pubd"]: - get_or_create_ServerCertificate(server_ca, "pubd") - if run_flags["run_rootd"]: - get_or_create_ServerCertificate(server_ca, "rootd") else: server_ca = None +if run_rpkid: + get_or_create_ServerEE(server_ca, "rpkid") + get_or_create_ServerEE(server_ca, "irdbd") + +if run_pubd: + get_or_create_ServerEE(server_ca, "pubd") + +# Certification model for rootd has changed. We can reuse the old +# key, but we have to recertify under a different CA than previously. +# Yes, we're pulling a key from the servers BPKI tree and certifying +# it under the resource holder CA, that's part of the change. + +if run_rootd: + rpki.irdb.Rootd.objects.get_or_certify( + issuer = resource_ca, + service_uri = "http://localhost:%s/" % cfg.get("rootd_server_port", section = "myrpki"), + private_key = rpki.x509.RSA(Auto_file = os.path.join(bpki, "servers", "rootd.key"))) + # Load BSC certificates and requests. Yes, this currently wires in # exactly one BSC handle, "bsc". So does the old myrpki code. Ick. @@ -277,6 +286,11 @@ for filename in glob.iglob(os.path.join(entitydb, "parents", "*.xml")): rpki.relaxng.myrpki.assertValid(e) assert e.tag == tag_parent + if parent_handle == self_handle: + assert run_rootd + assert e.get("service_uri") == "http://localhost:%s/" % cfg.get("rootd_server_port", section = "myrpki") + continue + ta = rpki.x509.X509(Base64 = e.findtext(tag_bpki_resource_ta)) xcfn = os.path.join(bpki, "resources", "xcert.%s.cer" % xcert_hash(ta)) xcert_filenames.discard(xcfn) @@ -298,6 +312,7 @@ for filename in glob.iglob(os.path.join(entitydb, "parents", "*.xml")): child_handle = e.get("child_handle"), ta = ta, certificate = xcert, + service_uri = e.get("service_uri"), repository_type = repository_type, referrer = referrer, referral_authorization = referral_authorization, @@ -334,7 +349,11 @@ for filename in glob.iglob(os.path.join(entitydb, "repositories", "*.xml")): xcert_filenames.discard(xcfn) xcert = rpki.x509.X509(Auto_file = xcfn) - parent = rpki.irdb.Parent.objects.get(handle = e.get("parent_handle")) + parent_handle = e.get("parent_handle") + if parent_handle == self_handle: + turtle = resource_ca.rootd + else: + turtle = rpki.irdb.Parent.objects.get(handle = parent_handle) rpki.irdb.Repository.objects.get_or_create( handle = repository_handle, @@ -343,7 +362,7 @@ for filename in glob.iglob(os.path.join(entitydb, "repositories", "*.xml")): certificate = xcert, service_uri = e.get("service_uri"), sia_base = e.get("sia_base"), - parent = parent, + turtle = turtle, issuer = resource_ca) # Scrape client data out of the entitydb. |