diff options
author | Rob Austein <sra@hactrn.net> | 2014-01-29 21:48:30 +0000 |
---|---|---|
committer | Rob Austein <sra@hactrn.net> | 2014-01-29 21:48:30 +0000 |
commit | 36e21b634a78d046860104cc462d2fa02b5822c4 (patch) | |
tree | 704c4c0d4ff99f7472464bfb40ef645e7979eaa2 /rpkid/rpki | |
parent | 2645eabce95e2cbca2e81339b093f457d0c3310e (diff) |
First cut of zookeeper methods to add router certs and other EE certs.
Likely needs further refactoring before really usable by GUI or CLI.
svn path=/branches/tk671/; revision=5654
Diffstat (limited to 'rpkid/rpki')
-rw-r--r-- | rpkid/rpki/irdb/models.py | 25 | ||||
-rw-r--r-- | rpkid/rpki/irdb/zookeeper.py | 57 | ||||
-rw-r--r-- | rpkid/rpki/x509.py | 88 |
3 files changed, 97 insertions, 73 deletions
diff --git a/rpkid/rpki/irdb/models.py b/rpkid/rpki/irdb/models.py index b6fc28be..8e492409 100644 --- a/rpkid/rpki/irdb/models.py +++ b/rpkid/rpki/irdb/models.py @@ -582,26 +582,15 @@ class GhostbusterRequest(django.db.models.Model): class EECertificateRequest(ResourceSet): issuer = django.db.models.ForeignKey(ResourceHolderCA, related_name = "ee_certificate_requests") pkcs10 = PKCS10Field() - gski = django.db.models.CharField(max_length = 27) + gski = django.db.models.CharField(max_length = 27) + router_id = django.db.models.BigIntegerField(null = True) - # At one point I had a router_id field here, but I don't think it - # serves any real purpose. Put it back if I remember why I thought - # we needed it, but the current I-D has router-id encoded in the - # subject name. - - # Need subject name field here? It's in the PKCS #10, but then so - # is the public key from which we generate the g(SKI); question is - # whether we need to use the subject name or just transport it. + # Subject name isn't allowed in the PKCS #10, so we need to carry + # either a subject name or a router-id as a separate field. + # Carrying subject name would be more flexible, but is also a swamp + # if we start allowing more than just CN and SN. # - # I guess we could have left-right XML attributes corresponding to - # X.509 commonName and serialNumber if necessary, question is whether - # this is necessary. - - # Well, we need //some// way of storing the router-id, and the PKCS - # #10 doesn't contain a subject name, so we need an additional field. - # Question becomes whether user wants to control which AS is used - # in the router certificate's name in the rare case where there's - # more than one (AS aliasing, I gather). + # For the moment we just do router-id. def _select_resource_bag(self): ee_asn = rpki.irdb.EECertificateRequestASN.objects.raw(""" diff --git a/rpkid/rpki/irdb/zookeeper.py b/rpkid/rpki/irdb/zookeeper.py index 97e56d5a..fc330db4 100644 --- a/rpkid/rpki/irdb/zookeeper.py +++ b/rpkid/rpki/irdb/zookeeper.py @@ -1589,7 +1589,7 @@ class Zookeeper(object): @django.db.transaction.commit_on_success - def add_ee_certificate_request(self, pkcs10, resources, kind = "ee"): + def add_ee_certificate_request(self, pkcs10, resources): """ Check a PKCS #10 request to see if it complies with the specification for a RPKI EE certificate; if it does, add an @@ -1600,7 +1600,7 @@ class Zookeeper(object): .load_asns() and .load_prefixes() for other strategies. """ - pkcs10.check_valid_request_ee(kind = kind) + pkcs10.check_valid_request_ee() ee_request = self.resource_ca.ee_certificate_requests.create( pkcs10 = pkcs10, gski = pkcs10.gSKI(), @@ -1613,40 +1613,41 @@ class Zookeeper(object): ee_request.address_ranges.create(start_ip = str(range.min), end_ip = str(range.max), version = 6) - def add_router_certificate_request(self, pkcs10, *asns): + @django.db.transaction.commit_on_success + def add_router_certificate_request(self, pkcs10, router_id, *asns, valid_until = None): """ Check a PKCS #10 request to see if it complies with the specification for a router certificate; if it does, create an EE - certificate request for it along with a specified ASN. + certificate request for it along with a specified router-id and + ASN(s). + + Not yet sure what we want for update and delete semantics here, so + for the moment this is straight addition. See methods like + .load_asns() and .load_prefixes() for other strategies. """ + pkcs10.check_valid_request_router() + asns = tuple(long(a) if isinstance(a, (str, unicode)) else a for a in asns) if not asns or not all(isinstance(a, (int, long)) and a >= 0 and a <= 0xFFFFFFFF for a in asns): raise rpki.exceptions.BadAutonomousSystemNumber("Bad AutonomousSystem number%s: %s" % ( "" if len(asns) == 1 else "s", ", ".join(repr(a) for a in asns))) - # This attempts to enforce draft-ietf-sidr-bgpsec-pki-profiles-06 - # section 3.1.1.1, which may be a mistake, too early to tell. - # - # Upon further reading: this will have to go somewhere else, - # because the combination of draft-ietf-sidr-bgpsec-pki-profiles - # and RFC 6487 says that the subject-name-to-be can't be in the - # PKCS #10, it has to be carried separately like the ASNs. Save - # this code, refactor once I figure out where this belongs. - - cn, sn = pkcs10.getSubject().extract_cn_and_sn() - if (not cn.startswith("ROUTER-") or - len(cn) != 7 + 8 or - not cn[7:].isalnum() or - int(cn[7:], 16) not in asns or - not sn.isalnum() or - len(sn) != 8 or - int(sn, 16) > 0xFFFFFFFF): - raise rpki.exceptions.BadX510DN("Subject name doesn't match router profile: %s" % pkcs10.getSubject()) - - eku = pkcs10.getEKU() - if eku is None or rpki.oids.id_kp_bgpsec_router not in eku: - raise rpki.exceptions.WrongEKU("Router certificate EKU not present in request") - - raise NotImplementedError, "Not finished" + asn_set = rpki.resource_set.resource_set_as() + asn_set.extend(rpki.resource_set.resource_range_as(a, a) for a in asns) + asn_set.canonize() + + if valid_until is None: + valid_until = rpki.sundial.now() + rpki.sundial.timedelta(days = 365) + elif valid_until < rpki.sundial.now(): + raise PastExpiration, "Specified expiration date %s has already passed" % valid_until + + ee_request = self.resource_ca.ee_certificate_requests.create( + pkcs10 = pkcs10, + gski = pkcs10.gSKI(), + valid_until = valid_until, + router_id = router_id) + + for range in asn_set: + ee_request.asns.create(start_as = str(range.min), end_as = str(range.max)) diff --git a/rpkid/rpki/x509.py b/rpkid/rpki/x509.py index 30e32aa8..00ed3d58 100644 --- a/rpkid/rpki/x509.py +++ b/rpkid/rpki/x509.py @@ -927,15 +927,10 @@ class PKCS10(DER_object): def check_valid_request_ca(self): """ Check this certification request to see whether it's a valid - request for an RPKI certificate. This is broken out of the - up-down protocol code because it's somewhat involved and the - up-down code doesn't need to know the details. + request for an RPKI CA certificate. Throws an exception if the request isn't valid, so if this method returns at all, the request is ok. - - This needs refactoring, as the nested conditionals to handle the - different kinds of certificates have gotten rather nasty. """ self.check_valid_request_common() @@ -946,39 +941,83 @@ class PKCS10(DER_object): sias = self.get_POW().getSIA() if alg != rpki.oids.sha256WithRSAEncryption: - raise rpki.exceptions.BadPKCS10("PKCS #10 request has bad signature algorithm %s" % alg) + raise rpki.exceptions.BadPKCS10("PKCS #10 has bad signature algorithm for CA: %s" % alg) if bc is None or not bc[0] or bc[1] is not None: - raise rpki.exceptions.BadPKCS10("PKCS #10 bad basicConstraints") + raise rpki.exceptions.BadPKCS10("PKCS #10 CA bad basicConstraints") if eku is not None: - raise rpki.exceptions.BadPKCS10("EKU not allowed in CA certificate PKCS #10") + raise rpki.exceptions.BadPKCS10("PKCS #10 CA EKU not allowed") if sias is None: - raise rpki.exceptions.BadPKCS10("PKCS #10 is missing SIA extension") + raise rpki.exceptions.BadPKCS10("PKCS #10 CA SIA missing") caRepository, rpkiManifest, signedObject = sias if signedObject: - raise rpki.exceptions.BadPKCS10("PKCS #10 CA certificate request has SIA id-ad-signedObject") + raise rpki.exceptions.BadPKCS10("PKCS #10 CA SIA must not have id-ad-signedObject") if not caRepository: - raise rpki.exceptions.BadPKCS10("PKCS #10 CA certificate request is missing SIA id-ad-caRepository") + raise rpki.exceptions.BadPKCS10("PKCS #10 CA SIA must have id-ad-caRepository") if not any(uri.startswith("rsync://") for uri in caRepository): - raise rpki.exceptions.BadPKCS10("PKCS #10 CA certificate request SIA id-ad-caRepository contains no rsync URIs") + raise rpki.exceptions.BadPKCS10("PKCS #10 CA SIA id-ad-caRepository contains no rsync URIs") + + if any(uri.startswith("rsync://") and not uri.endswith("/") for uri in caRepository): + raise rpki.exceptions.BadPKCS10("PKCS #10 CA SIA id-ad-caRepository does not end with slash") if not rpkiManifest: - raise rpki.exceptions.BadPKCS10("PKCS #10 CA certificate request is missing SIA id-ad-rpkiManifest") + raise rpki.exceptions.BadPKCS10("PKCS #10 CA SIA must have id-ad-rpkiManifest") if not any(uri.startswith("rsync://") for uri in rpkiManifest): - raise rpki.exceptions.BadPKCS10("PKCS #10 CA certificate request SIA id-ad-rpkiManifest contains no rsync URIs") - - if any(uri.startswith("rsync://") and not uri.endswith("/") for uri in caRepository): - raise rpki.exceptions.BadPKCS10("PKCS #10 CA certificate request SIA id-ad-caRepository does not end with slash") + raise rpki.exceptions.BadPKCS10("PKCS #10 CA SIA id-ad-rpkiManifest contains no rsync URIs") if any(uri.startswith("rsync://") and uri.endswith("/") for uri in rpkiManifest): - raise rpki.exceptions.BadPKCS10("PKCS #10 CA certificate request SIA id-ad-rpkiManifest ends with slash") + raise rpki.exceptions.BadPKCS10("PKCS #10 CA SIA id-ad-rpkiManifest ends with slash") + + + def check_valid_request_ee(self): + """ + Check this certification request to see whether it's a valid + request for an RPKI EE certificate. + + Throws an exception if the request isn't valid, so if this method + returns at all, the request is ok. + + We're a bit less strict here than we are for either CA + certificates or BGPSEC router certificates, because the profile is + less tightly nailed down for unspecified-use RPKI EE certificates. + Future specific purposes may impose tighter constraints. + + Note that this method does NOT apply to so-called "infrastructure" + EE certificates (eg, the EE certificates embedded in manifests and + ROAs); those are constrained fairly tightly, but they're also + generated internally so we don't need to check them as user or + protocol input. + """ + + self.check_valid_request_common() + + alg = self.get_POW().getSignatureAlgorithm() + bc = self.get_POW().getBasicConstraints() + sia = self.get_POW().getSIA() + + if alg not in (rpki.oids.sha256WithRSAEncryption, rpki.oids.ecdsa_with_SHA256): + raise rpki.exceptions.BadPKCS10("PKCS #10 has bad signature algorithm for EE: %s" % alg) + + if bc is not None and (bc[0] or bc[1] is not None): + raise rpki.exceptions.BadPKCS10("PKCS #10 EE has bad basicConstraints") + + caRepository, rpkiManifest, signedObject = sias or (None, None, None) + + if caRepository: + raise rpki.exceptions.BadPKCS10("PKCS #10 EE must not have id-ad-caRepository") + + if rpkiManifest: + raise rpki.exceptions.BadPKCS10("PKCS #10 EE must not have id-ad-rpkiManifest") + + if signedObject and not any(uri.startswith("rsync://") for uri in signedObject): + raise rpki.exceptions.BadPKCS10("PKCS #10 EE SIA id-ad-signedObject contains no rsync URIs") def check_valid_request_router(self): @@ -999,21 +1038,16 @@ class PKCS10(DER_object): the certificate when we get to the code that generates that. """ - self.check_valid_request_common() + self.check_valid_request_ee() alg = self.get_POW().getSignatureAlgorithm() - bc = self.get_POW().getBasicConstraints() eku = self.get_POW().getEKU() - #sia = self.get_POW().getSIA() if alg != rpki.oids.ecdsa_with_SHA256: - raise rpki.exceptions.BadPKCS10("PKCS #10 request has bad signature algorithm %s" % alg) - - if bc is not None and (bc[0] or bc[1] is not None): - raise rpki.exceptions.BadPKCS10("PKCS #10 has bad basicConstraints for router") + raise rpki.exceptions.BadPKCS10("PKCS #10 has bad signature algorithm for router: %s" % alg) if eku is None or rpki.oids.id_kp_bgpsec_router not in eku: - raise rpki.exceptions.BadPKCS10("PKCS #10 for router requires EKU") + raise rpki.exceptions.BadPKCS10("PKCS #10 router must have EKU") @classmethod |