aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRob Austein <sra@hactrn.net>2014-01-29 21:48:30 +0000
committerRob Austein <sra@hactrn.net>2014-01-29 21:48:30 +0000
commit36e21b634a78d046860104cc462d2fa02b5822c4 (patch)
tree704c4c0d4ff99f7472464bfb40ef645e7979eaa2
parent2645eabce95e2cbca2e81339b093f457d0c3310e (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
-rw-r--r--rpkid/rpki/irdb/models.py25
-rw-r--r--rpkid/rpki/irdb/zookeeper.py57
-rw-r--r--rpkid/rpki/x509.py88
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