diff options
author | Rob Austein <sra@hactrn.net> | 2014-01-26 19:19:39 +0000 |
---|---|---|
committer | Rob Austein <sra@hactrn.net> | 2014-01-26 19:19:39 +0000 |
commit | 2edf966870ae87327c381d4091178aa025ff09ff (patch) | |
tree | 44cc0d8f8dde15c1503cfead21a1e8d68251c088 /rpkid | |
parent | 3bc8ab851de7d5be2b9c6258de56c0cf41634f0e (diff) |
Checkpoint.
svn path=/branches/tk671/; revision=5646
Diffstat (limited to 'rpkid')
-rw-r--r-- | rpkid/rpki/exceptions.py | 10 | ||||
-rw-r--r-- | rpkid/rpki/irdb/models.py | 16 | ||||
-rw-r--r-- | rpkid/rpki/irdb/zookeeper.py | 52 | ||||
-rw-r--r-- | rpkid/rpki/x509.py | 63 |
4 files changed, 124 insertions, 17 deletions
diff --git a/rpkid/rpki/exceptions.py b/rpkid/rpki/exceptions.py index d390d67b..7a0eb71c 100644 --- a/rpkid/rpki/exceptions.py +++ b/rpkid/rpki/exceptions.py @@ -350,3 +350,13 @@ class NullValidityInterval(RPKI_Exception): """ Requested validity interval is null. """ + +class BadX510DN(RPKI_Exception): + """ + X.510 distinguished name does not match profile. + """ + +class BadAutonomousSystemNumber(RPKI_Exception): + """ + Bad AutonomousSystem number. + """ diff --git a/rpkid/rpki/irdb/models.py b/rpkid/rpki/irdb/models.py index 03b6643c..3553581e 100644 --- a/rpkid/rpki/irdb/models.py +++ b/rpkid/rpki/irdb/models.py @@ -581,10 +581,22 @@ class GhostbusterRequest(django.db.models.Model): class EECertificateRequest(ResourceSet): issuer = django.db.models.ForeignKey(ResourceHolderCA, related_name = "ee_certificate_requests") - router_id = django.db.models.BigIntegerField(null = True) pkcs10 = PKCS10Field() gski = django.db.models.CharField(max_length = 27) - + + # 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 + # 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. + # + # I guess we could have left-right XML attributes corresponding to + # X.509 commonName and serialNumber if necessary, question is whether + # this is necessary. + 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 1c2d2d16..bb52bddd 100644 --- a/rpkid/rpki/irdb/zookeeper.py +++ b/rpkid/rpki/irdb/zookeeper.py @@ -1586,3 +1586,55 @@ class Zookeeper(object): if rpkid_query: rpkid_reply = self.call_rpkid(rpkid_query) self.check_error_report(rpkid_reply) + + + @django.db.transaction.commit_on_success + 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 + EECertificateRequest for it to the IRDB. + + 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_rpki(ee = True) + ee_request = self.resource_ca.ee_certificate_requests.create( + pkcs10 = pkcs10, + gski = pkcs10.gSKI(), + valid_until = resources.valid_until) + for range in resources.asn: + ee_request.asns.create(start_as = str(range.min), end_as = str(range.max)) + for range in resources.v4: + ee_request.address_ranges.create(start_ip = str(range.min), end_ip = str(range.max), version = 4) + for range in resources.v6: + ee_request.address_ranges.create(start_ip = str(range.min), end_ip = str(range.max), version = 6) + + + def add_router_certificate_request(self, pkcs10, asn): + """ + 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. + """ + + if isinstance(asn, (str, unicode)): + asn = long(asn) + if not isinstance(asn, (int, long)) or asn < 0 or asn > 0xFFFFFFFF: + raise rpki.exceptions.BadAutonomousSystemNumber("Bad AutonomousSystem number: %s" % asn) + + # 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. + 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) != asn \ + 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()) + + raise NotImplementedError, "Not finished" diff --git a/rpkid/rpki/x509.py b/rpkid/rpki/x509.py index 5475a452..2e09cb35 100644 --- a/rpkid/rpki/x509.py +++ b/rpkid/rpki/x509.py @@ -161,6 +161,30 @@ class X501DN(object): def get_POW(self): return self.dn + def extract_cn_and_sn(self): + cn = None + sn = None + + for rdn in self.dn: + if len(rdn) == 1 and len(rdn[0]) == 2: + oid = rpki.oids.safe_dotted2name(rdn[0][0]) + val = rdn[0][1] + print "OID:", oid + print "Val:", val + if oid == "commonName" and cn is None: + cn = val + continue + if oid == "serialNumber" and sn is None: + sn = val + continue + raise rpki.exceptions.BadX510DN("Bad subject name: %s" % (self.dn,)) + + if cn is None: + raise rpki.exceptions.BadX510DN("Subject name is missing CN: %s" % (self.dn,)) + + return cn, sn + + class DER_object(object): """ Virtual class to hold a generic DER object. @@ -875,7 +899,13 @@ class PKCS10(DER_object): """ return RSApublic(POW = self.get_POW().getPublicKey()) - def check_valid_rpki(self): + def get_SKI(self): + """ + Compute SKI for public key from this certification request. + """ + return self.getPublicKey().get_SKI() + + def check_valid_rpki(self, ee = False): """ Check this certification request to see whether it's a valid request for an RPKI certificate. This is broken out of the @@ -890,18 +920,21 @@ class PKCS10(DER_object): RPKI profile only allows EKU for EE certificates. """ + if ee: + raise NotImplementedError("Haven't written EE-certificate checking yet, oops") + if not self.get_POW().verify(): - raise rpki.exceptions.BadPKCS10("Signature check failed") + raise rpki.exceptions.BadPKCS10("PKCS #10 signature check failed") ver = self.get_POW().getVersion() if ver != 0: - raise rpki.exceptions.BadPKCS10("Bad version number %s" % ver) + raise rpki.exceptions.BadPKCS10("PKCS #10 request has bad version number %s" % ver) alg = rpki.oids.safe_dotted2name(self.get_POW().getSignatureAlgorithm()) if alg != "sha256WithRSAEncryption": - raise rpki.exceptions.BadPKCS10("Bad signature algorithm %s" % alg) + raise rpki.exceptions.BadPKCS10("PKCS #10 request has bad signature algorithm %s" % alg) bc = self.get_POW().getBasicConstraints() @@ -909,44 +942,44 @@ class PKCS10(DER_object): raise rpki.exceptions.BadPKCS10("Request for EE certificate not allowed here") if bc[1] is not None: - raise rpki.exceptions.BadPKCS10("basicConstraints must not specify Path Length") + raise rpki.exceptions.BadPKCS10("PKCS #10 basicConstraints must not specify Path Length") ku = self.get_POW().getKeyUsage() if ku is not None and self.expected_ca_keyUsage != ku: - raise rpki.exceptions.BadPKCS10("keyUsage doesn't match basicConstraints: %r" % ku) + raise rpki.exceptions.BadPKCS10("PKCS #10 keyUsage doesn't match basicConstraints: %r" % ku) if any(oid not in self.allowed_extensions for oid in self.get_POW().getExtensionOIDs()): - raise rpki.exceptions.BadExtension("Forbidden extension(s) in certificate request") + raise rpki.exceptions.BadExtension("Forbidden extension(s) in PKCS #10 certificate request") sias = self.get_POW().getSIA() if sias is None: - raise rpki.exceptions.BadPKCS10("Certificate request is missing SIA extension") + raise rpki.exceptions.BadPKCS10("PKCS #10 is missing SIA extension") caRepository, rpkiManifest, signedObject = sias if signedObject: - raise rpki.exceptions.BadPKCS10("CA certificate request has SIA id-ad-signedObject") + raise rpki.exceptions.BadPKCS10("PKCS #10 CA certificate request has SIA id-ad-signedObject") if not caRepository: - raise rpki.exceptions.BadPKCS10("Certificate request is missing SIA id-ad-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 caRepository): - raise rpki.exceptions.BadPKCS10("Certificate request SIA id-ad-caRepository contains no rsync URIs") + raise rpki.exceptions.BadPKCS10("PKCS #10 CA certificate request SIA id-ad-caRepository contains no rsync URIs") if not rpkiManifest: - raise rpki.exceptions.BadPKCS10("Certificate request is missing SIA id-ad-rpkiManifest") + raise rpki.exceptions.BadPKCS10("PKCS #10 CA certificate request is missing SIA id-ad-rpkiManifest") if not any(uri.startswith("rsync://") for uri in rpkiManifest): - raise rpki.exceptions.BadPKCS10("Certificate request SIA id-ad-rpkiManifest contains no rsync URIs") + 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("Certificate request SIA id-ad-caRepository does not end with slash") + raise rpki.exceptions.BadPKCS10("PKCS #10 CA certificate request SIA id-ad-caRepository does not end with slash") if any(uri.startswith("rsync://") and uri.endswith("/") for uri in rpkiManifest): - raise rpki.exceptions.BadPKCS10("Certificate request SIA id-ad-rpkiManifest ends with slash") + 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, |