diff options
author | Rob Austein <sra@hactrn.net> | 2014-01-29 02:32:36 +0000 |
---|---|---|
committer | Rob Austein <sra@hactrn.net> | 2014-01-29 02:32:36 +0000 |
commit | f3ee7f51c0a091f244985ae7fd3d975a3259fee9 (patch) | |
tree | 4c61669687f037ed125abe55e794188934f20c6b /rpkid/rpki | |
parent | 27c6393b76fa9921f0bb1b3977f091579e44872a (diff) |
Checkpoint
svn path=/branches/tk671/; revision=5651
Diffstat (limited to 'rpkid/rpki')
-rw-r--r-- | rpkid/rpki/exceptions.py | 5 | ||||
-rw-r--r-- | rpkid/rpki/irdb/zookeeper.py | 40 | ||||
-rw-r--r-- | rpkid/rpki/oids.py | 73 | ||||
-rw-r--r-- | rpkid/rpki/x509.py | 77 |
4 files changed, 125 insertions, 70 deletions
diff --git a/rpkid/rpki/exceptions.py b/rpkid/rpki/exceptions.py index 7a0eb71c..d8d3774e 100644 --- a/rpkid/rpki/exceptions.py +++ b/rpkid/rpki/exceptions.py @@ -360,3 +360,8 @@ class BadAutonomousSystemNumber(RPKI_Exception): """ Bad AutonomousSystem number. """ + +class WrongEKU(RPKI_Exception): + """ + Extended Key Usage extension does not match profile. + """ diff --git a/rpkid/rpki/irdb/zookeeper.py b/rpkid/rpki/irdb/zookeeper.py index bb52bddd..d2bd0c75 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): + def add_ee_certificate_request(self, pkcs10, resources, kind = "ee"): """ 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_rpki(ee = True) + pkcs10.check_valid_rpki(kind = kind) ee_request = self.resource_ca.ee_certificate_requests.create( pkcs10 = pkcs10, gski = pkcs10.gSKI(), @@ -1613,28 +1613,40 @@ 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, asn): + def add_router_certificate_request(self, pkcs10, *asns): """ 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) + 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) != asn \ - or not sn.isalnum() \ - or len(sn) != 8 \ - or int(sn, 16) > 0xFFFFFFFF: + 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.name2oid["id-kp-bgpsec-router"] not in eku: + raise rpki.exceptions.WrongEKU("Router certificate EKU not present in request") + raise NotImplementedError, "Not finished" diff --git a/rpkid/rpki/oids.py b/rpkid/rpki/oids.py index 094fa1a2..1acc8035 100644 --- a/rpkid/rpki/oids.py +++ b/rpkid/rpki/oids.py @@ -1,41 +1,61 @@ # $Id$ # -# Copyright (C) 2009--2012 Internet Systems Consortium ("ISC") -# -# Permission to use, copy, modify, and distribute this software for any -# purpose with or without fee is hereby granted, provided that the above -# copyright notice and this permission notice appear in all copies. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH -# REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY -# AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, -# INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM -# LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE -# OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR -# PERFORMANCE OF THIS SOFTWARE. -# +# Copyright (C) 2013--2014 Dragon Research Labs ("DRL") +# Portions copyright (C) 2009--2012 Internet Systems Consortium ("ISC") # Portions copyright (C) 2007--2008 American Registry for Internet Numbers ("ARIN") # # Permission to use, copy, modify, and distribute this software for any # purpose with or without fee is hereby granted, provided that the above -# copyright notice and this permission notice appear in all copies. +# copyright notices and this permission notice appear in all copies. # -# THE SOFTWARE IS PROVIDED "AS IS" AND ARIN DISCLAIMS ALL WARRANTIES WITH -# REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY -# AND FITNESS. IN NO EVENT SHALL ARIN BE LIABLE FOR ANY SPECIAL, DIRECT, -# INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM -# LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE -# OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR -# PERFORMANCE OF THIS SOFTWARE. +# THE SOFTWARE IS PROVIDED "AS IS" AND DRL, ISC, AND ARIN DISCLAIM ALL +# WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED +# WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL DRL, +# ISC, OR ARIN BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR +# CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS +# OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, +# NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION +# WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. """ OID database. """ +def defoid(name, *numbers): + """ + Define a new OID, including adding it to a few dictionaries and + making an entry for it in the rpki.oids module symbol table, so + other code can refer to it as an ordinary symbol. + """ + + assert all(isinstance(n, (int, long)) for n in numbers) + + dotted = ".".join(str(n) for n in numbers) + name_ = name.replace("-", "_") + + assert name_ not in globals() + + global oid2name + oid2name[numbers] = name + + globals()[name_] = dotted + + global dotted2name + dotted2name[dotted] = name + + global dotted2name_ + dotted2name_[dotted] = name_ + + global name2dotted + name2dotted[name] = dotted + name2dotted[name_] = dotted + + ## @var oid2name # Mapping table of OIDs to conventional string names. oid2name = { + (1, 2, 840, 10045, 4, 3, 2) : "ecdsa-with-SHA256", (1, 2, 840, 113549, 1, 1, 11) : "sha256WithRSAEncryption", (1, 2, 840, 113549, 1, 1, 12) : "sha384WithRSAEncryption", (1, 2, 840, 113549, 1, 1, 13) : "sha512WithRSAEncryption", @@ -51,11 +71,12 @@ oid2name = { (1, 3, 6, 1, 5, 5, 7, 1, 7) : "sbgp-ipAddrBlock", (1, 3, 6, 1, 5, 5, 7, 1, 8) : "sbgp-autonomousSysNum", (1, 3, 6, 1, 5, 5, 7, 14, 2) : "id-cp-ipAddr-asNumber", + (1, 3, 6, 1, 5, 5, 7, 3, 666) : "id-kp-bgpsec-router", # {id-kp, 666} -- Real value not known yet + (1, 3, 6, 1, 5, 5, 7, 48, 10) : "id-ad-rpkiManifest", + (1, 3, 6, 1, 5, 5, 7, 48, 11) : "id-ad-signedObject", (1, 3, 6, 1, 5, 5, 7, 48, 2) : "id-ad-caIssuers", (1, 3, 6, 1, 5, 5, 7, 48, 5) : "id-ad-caRepository", (1, 3, 6, 1, 5, 5, 7, 48, 9) : "id-ad-signedObjectRepository", - (1, 3, 6, 1, 5, 5, 7, 48, 10) : "id-ad-rpkiManifest", - (1, 3, 6, 1, 5, 5, 7, 48, 11) : "id-ad-signedObject", (2, 16, 840, 1, 101, 3, 4, 2, 1) : "id-sha256", (2, 5, 29, 14) : "subjectKeyIdentifier", (2, 5, 29, 15) : "keyUsage", @@ -65,14 +86,14 @@ oid2name = { (2, 5, 29, 32) : "certificatePolicies", (2, 5, 29, 35) : "authorityKeyIdentifier", (2, 5, 29, 37) : "extendedKeyUsage", + (2, 5, 4, 10) : "organizationName", + (2, 5, 4, 11) : "organizationalUnitName", (2, 5, 4, 3) : "commonName", (2, 5, 4, 5) : "serialNumber", (2, 5, 4, 6) : "countryName", (2, 5, 4, 7) : "localityName", (2, 5, 4, 8) : "stateOrProvinceName", (2, 5, 4, 9) : "streetAddress", - (2, 5, 4, 10) : "organizationName", - (2, 5, 4, 11) : "organizationalUnitName", } ## @var name2oid diff --git a/rpkid/rpki/x509.py b/rpkid/rpki/x509.py index 2e09cb35..8d3ea634 100644 --- a/rpkid/rpki/x509.py +++ b/rpkid/rpki/x509.py @@ -864,7 +864,8 @@ class PKCS10(DER_object): allowed_extensions = frozenset(rpki.oids.safe_name2dotted(name) for name in ("basicConstraints", "keyUsage", - "subjectInfoAccess")) + "subjectInfoAccess", + "extendedKeyUsage")) def get_DER(self): """ @@ -905,7 +906,7 @@ class PKCS10(DER_object): """ return self.getPublicKey().get_SKI() - def check_valid_rpki(self, ee = False): + def check_valid_rpki(self, kind = "ca"): """ Check this certification request to see whether it's a valid request for an RPKI certificate. This is broken out of the @@ -915,13 +916,11 @@ class PKCS10(DER_object): Throws an exception if the request isn't valid, so if this method returns at all, the request is ok. - At the moment, this only allows requests for CA certificates; as a - direct consequence, it also rejects ExtendedKeyUsage, because the - RPKI profile only allows EKU for EE certificates. + This needs refactoring, as the nested conditionals to handle the + different kinds of certificates have gotten rather nasty. """ - if ee: - raise NotImplementedError("Haven't written EE-certificate checking yet, oops") + assert kind in ("ca", "ee", "router") if not self.get_POW().verify(): raise rpki.exceptions.BadPKCS10("PKCS #10 signature check failed") @@ -933,15 +932,19 @@ class PKCS10(DER_object): alg = rpki.oids.safe_dotted2name(self.get_POW().getSignatureAlgorithm()) - if alg != "sha256WithRSAEncryption": + if alg != ("ecdsa-with-SHA256" if kind == "router" else "sha256WithRSAEncryption"): raise rpki.exceptions.BadPKCS10("PKCS #10 request has bad signature algorithm %s" % alg) bc = self.get_POW().getBasicConstraints() - if bc is None or not bc[0]: - raise rpki.exceptions.BadPKCS10("Request for EE certificate not allowed here") - - if bc[1] is not None: + 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") ku = self.get_POW().getKeyUsage() @@ -949,37 +952,51 @@ class PKCS10(DER_object): 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() + + 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.name2oid["id-kp-bgpsec-router"] not in eku): + raise rpki.exceptions.BadPKCS10("EKU required for router certificate PKCS #10") + 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") sias = self.get_POW().getSIA() - if sias is None: - raise rpki.exceptions.BadPKCS10("PKCS #10 is missing SIA extension") + if kind == "router": - caRepository, rpkiManifest, signedObject = sias + if sias is not None: + raise rpki.exceptions.BadPKCS10("router certificate PKCS #10 must not contain SIA extension") - if signedObject: - raise rpki.exceptions.BadPKCS10("PKCS #10 CA certificate request has SIA id-ad-signedObject") + elif kind == "ca": - if not caRepository: - raise rpki.exceptions.BadPKCS10("PKCS #10 CA certificate request is missing SIA id-ad-caRepository") + if sias is None: + raise rpki.exceptions.BadPKCS10("PKCS #10 is missing SIA extension") - 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") + caRepository, rpkiManifest, signedObject = sias - if not 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("PKCS #10 CA certificate request SIA id-ad-rpkiManifest contains no rsync URIs") + if signedObject: + raise rpki.exceptions.BadPKCS10("PKCS #10 CA certificate request has SIA id-ad-signedObject") + + 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 caRepository): + raise rpki.exceptions.BadPKCS10("PKCS #10 CA certificate request SIA id-ad-caRepository contains no rsync URIs") + + if not 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("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") + 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 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") + 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, |