diff options
-rw-r--r-- | rpkid/rpki/irdb/models.py | 8 | ||||
-rw-r--r-- | rpkid/rpki/irdb/zookeeper.py | 2 | ||||
-rw-r--r-- | rpkid/rpki/rootd.py | 2 | ||||
-rw-r--r-- | rpkid/rpki/up_down.py | 2 | ||||
-rw-r--r-- | rpkid/rpki/x509.py | 144 |
5 files changed, 99 insertions, 59 deletions
diff --git a/rpkid/rpki/irdb/models.py b/rpkid/rpki/irdb/models.py index 3553581e..b6fc28be 100644 --- a/rpkid/rpki/irdb/models.py +++ b/rpkid/rpki/irdb/models.py @@ -586,7 +586,7 @@ class EECertificateRequest(ResourceSet): # 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 teh + # 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 @@ -597,6 +597,12 @@ class EECertificateRequest(ResourceSet): # 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). + def _select_resource_bag(self): ee_asn = rpki.irdb.EECertificateRequestASN.objects.raw(""" SELECT * diff --git a/rpkid/rpki/irdb/zookeeper.py b/rpkid/rpki/irdb/zookeeper.py index 6c7d2206..97e56d5a 100644 --- a/rpkid/rpki/irdb/zookeeper.py +++ b/rpkid/rpki/irdb/zookeeper.py @@ -1600,7 +1600,7 @@ class Zookeeper(object): .load_asns() and .load_prefixes() for other strategies. """ - pkcs10.check_valid_rpki(kind = kind) + pkcs10.check_valid_request_ee(kind = kind) ee_request = self.resource_ca.ee_certificate_requests.create( pkcs10 = pkcs10, gski = pkcs10.gSKI(), diff --git a/rpkid/rpki/rootd.py b/rpkid/rpki/rootd.py index 49488602..e0c59726 100644 --- a/rpkid/rpki/rootd.py +++ b/rpkid/rpki/rootd.py @@ -49,7 +49,7 @@ class list_pdu(rpki.up_down.list_pdu): class issue_pdu(rpki.up_down.issue_pdu): def serve_pdu(self, q_msg, r_msg, ignored, callback, errback): - self.pkcs10.check_valid_rpki() + self.pkcs10.check_valid_request_ca() r_msg.payload = rpki.up_down.issue_response_pdu() rootd.compose_response(r_msg, self.pkcs10) callback() diff --git a/rpkid/rpki/up_down.py b/rpkid/rpki/up_down.py index 6d1fa45a..d2ad85d3 100644 --- a/rpkid/rpki/up_down.py +++ b/rpkid/rpki/up_down.py @@ -365,7 +365,7 @@ class issue_pdu(base_elt): raise rpki.exceptions.NotImplementedYet("req_* attributes not implemented yet, sorry") # Check the request - self.pkcs10.check_valid_rpki() + self.pkcs10.check_valid_request_ca() ca = child.ca_from_class_name(self.class_name) ca_detail = ca.active_ca_detail if ca_detail is None: diff --git a/rpkid/rpki/x509.py b/rpkid/rpki/x509.py index 99350ce7..30e32aa8 100644 --- a/rpkid/rpki/x509.py +++ b/rpkid/rpki/x509.py @@ -890,22 +890,19 @@ class PKCS10(DER_object): """ return self.getPublicKey().get_SKI() - def check_valid_rpki(self, kind = "ca"): + + def check_valid_request_common(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. + Common code for checking this certification requests to see + whether they conform to the RPKI certificate profile. 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. + You probably don't want to call this directly, as it only performs + the checks that are common to all RPKI certificates. """ - assert kind in ("ca", "ee", "router") - if not self.get_POW().verify(): raise rpki.exceptions.BadPKCS10("PKCS #10 signature check failed") @@ -914,73 +911,110 @@ class PKCS10(DER_object): if ver != 0: raise rpki.exceptions.BadPKCS10("PKCS #10 request has bad version number %s" % ver) - alg = self.get_POW().getSignatureAlgorithm() + ku = self.get_POW().getKeyUsage() - if alg != (rpki.oids.ecdsa_with_SHA256 if kind == "router" else rpki.oids.sha256WithRSAEncryption): - raise rpki.exceptions.BadPKCS10("PKCS #10 request has bad signature algorithm %s" % alg) + if ku is not None and self.expected_ca_keyUsage != ku: + raise rpki.exceptions.BadPKCS10("PKCS #10 keyUsage doesn't match profile: %r" % ku) - bc = self.get_POW().getBasicConstraints() - - if kind == "ca": - if bc is None or not bc[0]: - raise rpki.exceptions.BadPKCS10("Request for EE certificate not allowed here") - else: - if bc is not None and bc[0]: - raise rpki.exceptions.BadPKCS10("Request for CA certificate not allowed here") - - if bc is not None and bc[1] is not None: - raise rpki.exceptions.BadPKCS10("PKCS #10 basicConstraints must not specify Path Length") + forbidden_extensions = self.get_POW().getExtensionOIDs() - self.allowed_extensions - ku = self.get_POW().getKeyUsage() + if forbidden_extensions: + raise rpki.exceptions.BadExtension("Forbidden extension%s in PKCS #10 certificate request: %s" % ( + "" if len(forbidden_extensions) == 1 else "s", + ", ".join(forbidden_extensions))) - if ku is not None and self.expected_ca_keyUsage != ku: - raise rpki.exceptions.BadPKCS10("PKCS #10 keyUsage doesn't match basicConstraints: %r" % ku) - eku = self.get_POW().getEKU() + 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. - if kind == "ca" and eku is not None: - raise rpki.exceptions.BadPKCS10("EKU not allowed in CA certificate PKCS #10") - elif kind == "router" and (eku is None or rpki.oids.id_kp_bgpsec_router not in eku): - raise rpki.exceptions.BadPKCS10("EKU required for router certificate PKCS #10") + Throws an exception if the request isn't valid, so if this method + returns at all, the request is ok. - if any(oid not in self.allowed_extensions - for oid in self.get_POW().getExtensionOIDs()): - raise rpki.exceptions.BadExtension("Forbidden extension(s) in PKCS #10 certificate request") + This needs refactoring, as the nested conditionals to handle the + different kinds of certificates have gotten rather nasty. + """ + self.check_valid_request_common() + + alg = self.get_POW().getSignatureAlgorithm() + bc = self.get_POW().getBasicConstraints() + eku = self.get_POW().getEKU() sias = self.get_POW().getSIA() - if kind == "router": + if alg != rpki.oids.sha256WithRSAEncryption: + raise rpki.exceptions.BadPKCS10("PKCS #10 request has bad signature algorithm %s" % alg) + + if bc is None or not bc[0] or bc[1] is not None: + raise rpki.exceptions.BadPKCS10("PKCS #10 bad basicConstraints") - if sias is not None: - raise rpki.exceptions.BadPKCS10("router certificate PKCS #10 must not contain SIA extension") + if eku is not None: + raise rpki.exceptions.BadPKCS10("EKU not allowed in CA certificate PKCS #10") + + if sias is None: + raise rpki.exceptions.BadPKCS10("PKCS #10 is missing SIA extension") + + caRepository, rpkiManifest, signedObject = sias - elif kind == "ca": + if signedObject: + raise rpki.exceptions.BadPKCS10("PKCS #10 CA certificate request has SIA id-ad-signedObject") - if sias is None: - raise rpki.exceptions.BadPKCS10("PKCS #10 is missing SIA extension") + if not caRepository: + raise rpki.exceptions.BadPKCS10("PKCS #10 CA certificate request is missing SIA id-ad-caRepository") - caRepository, rpkiManifest, signedObject = sias + 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") - if signedObject: - raise rpki.exceptions.BadPKCS10("PKCS #10 CA certificate request has SIA id-ad-signedObject") + if not rpkiManifest: + raise rpki.exceptions.BadPKCS10("PKCS #10 CA certificate request is missing SIA id-ad-rpkiManifest") - if not caRepository: - raise rpki.exceptions.BadPKCS10("PKCS #10 CA certificate request is missing SIA id-ad-caRepository") + 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 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") + 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") - if not rpkiManifest: - raise rpki.exceptions.BadPKCS10("PKCS #10 CA certificate request is missing SIA id-ad-rpkiManifest") + 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") + + + def check_valid_request_router(self): + """ + Check this certification request to see whether it's a valid + request for a BGPSEC router certificate. + + Throws an exception if the request isn't valid, so if this method + returns at all, the request is ok. + + draft-ietf-sidr-bgpsec-pki-profiles 3.2 says follow RFC 6487 3 + except where explicitly overriden, and does not override for SIA. + But draft-ietf-sidr-bgpsec-pki-profiles also says that router + certificates don't get SIA, while RFC 6487 requires SIA. So what + do we do with SIA in PKCS #10 for router certificates? + + For the moment, ignore it, but make sure we don't include it in + the certificate when we get to the code that generates that. + """ + + self.check_valid_request_common() + + 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 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 bc is not None and (bc[0] or bc[1] is not None): + raise rpki.exceptions.BadPKCS10("PKCS #10 has bad basicConstraints for router") - 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") + if eku is None or rpki.oids.id_kp_bgpsec_router not in eku: + raise rpki.exceptions.BadPKCS10("PKCS #10 for router requires EKU") - 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") @classmethod def create(cls, keypair, exts = None, is_ca = False, |