diff options
author | Rob Austein <sra@hactrn.net> | 2013-04-19 20:33:14 +0000 |
---|---|---|
committer | Rob Austein <sra@hactrn.net> | 2013-04-19 20:33:14 +0000 |
commit | 05eaca3a52b1049ec69b7788deb4872df1c0d7c5 (patch) | |
tree | bb5303470d2dba45850b6af6f4a6c69c3994b723 | |
parent | ddfb8c4eb8139b94e1031c6f8555aee4b6287afb (diff) |
Throw exception when asked to issue a certificate with notAfter <=
notBefore.
Don't stomp ghostbuster_obj if we've just reused it.
Clean up properly when deleting a ca_detail.
Don't reissue expiring certificate if IRDB valid_until field says it's
supposed to expire.
svn path=/trunk/; revision=5302
-rw-r--r-- | rpkid/rpki/exceptions.py | 7 | ||||
-rw-r--r-- | rpkid/rpki/rpkid.py | 63 | ||||
-rw-r--r-- | rpkid/rpki/rpkid_tasks.py | 12 | ||||
-rw-r--r-- | rpkid/rpki/sql.py | 21 | ||||
-rw-r--r-- | rpkid/rpki/x509.py | 65 |
5 files changed, 112 insertions, 56 deletions
diff --git a/rpkid/rpki/exceptions.py b/rpkid/rpki/exceptions.py index 0f5dbc49..12abed7e 100644 --- a/rpkid/rpki/exceptions.py +++ b/rpkid/rpki/exceptions.py @@ -3,7 +3,7 @@ Exception definitions for RPKI modules. $Id$ -Copyright (C) 2009--2012 Internet Systems Consortium ("ISC") +Copyright (C) 2009--2013 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 @@ -351,3 +351,8 @@ class CMSReplay(RPKI_Exception): """ Possible CMS replay attack detected. """ + +class PastNotAfter(RPKI_Exception): + """ + Requested notAfter value is already in the past. + """ diff --git a/rpkid/rpki/rpkid.py b/rpkid/rpki/rpkid.py index 2a0b7c4b..8c193c31 100644 --- a/rpkid/rpki/rpkid.py +++ b/rpkid/rpki/rpkid.py @@ -660,12 +660,13 @@ class ca_obj(rpki.sql.sql_persistent): callback() def done(): + rpki.log.debug("Deleting %r" % self) self.sql_delete() callback() publisher = publication_queue() for ca_detail in self.ca_details: - ca_detail.delete(ca = self, publisher = publisher) + ca_detail.delete(ca = self, publisher = publisher, allow_failure = True) publisher.call_pubd(done, lose) def next_serial_number(self): @@ -771,6 +772,7 @@ class ca_detail_obj(rpki.sql.sql_persistent): latest_ca_cert = None latest_crl = None latest_manifest = None + ca_cert_uri = None def __repr__(self): return rpki.log.log_repr(self, repr(self.ca), self.state, self.ca_cert_uri) @@ -911,29 +913,43 @@ class ca_detail_obj(rpki.sql.sql_persistent): """ repository = ca.parent.repository + handler = False if allow_failure else None for child_cert in self.child_certs: - publisher.withdraw(cls = rpki.publication.certificate_elt, uri = child_cert.uri, obj = child_cert.cert, repository = repository, - handler = False if allow_failure else None) + publisher.withdraw(cls = rpki.publication.certificate_elt, + uri = child_cert.uri, + obj = child_cert.cert, + repository = repository, + handler = handler) + child_cert.sql_mark_deleted() for roa in self.roas: - roa.revoke(publisher = publisher, allow_failure = allow_failure) + roa.revoke(publisher = publisher, allow_failure = allow_failure, fast = True) for ghostbuster in self.ghostbusters: - ghostbuster.revoke(publisher = publisher, allow_failure = allow_failure) + ghostbuster.revoke(publisher = publisher, allow_failure = allow_failure, fast = True) try: latest_manifest = self.latest_manifest except AttributeError: latest_manifest = None if latest_manifest is not None: - publisher.withdraw(cls = rpki.publication.manifest_elt, uri = self.manifest_uri, obj = self.latest_manifest, repository = repository, - handler = False if allow_failure else None) + publisher.withdraw(cls = rpki.publication.manifest_elt, + uri = self.manifest_uri, + obj = self.latest_manifest, + repository = repository, + handler = handler) try: latest_crl = self.latest_crl except AttributeError: latest_crl = None if latest_crl is not None: - publisher.withdraw(cls = rpki.publication.crl_elt, uri = self.crl_uri, obj = self.latest_crl, repository = repository, - handler = False if allow_failure else None) - for cert in self.child_certs + self.revoked_certs: + publisher.withdraw(cls = rpki.publication.crl_elt, + uri = self.crl_uri, + obj = self.latest_crl, + repository = repository, + handler = handler) + self.gctx.sql.sweep() + for cert in self.revoked_certs: # + self.child_certs + rpki.log.debug("Deleting %r" % cert) cert.sql_delete() + rpki.log.debug("Deleting %r" % self) self.sql_delete() def revoke(self, cb, eb): @@ -1225,7 +1241,10 @@ class ca_detail_obj(rpki.sql.sql_persistent): self.manifest_published = rpki.sundial.now() self.sql_mark_dirty() - publisher.publish(cls = rpki.publication.manifest_elt, uri = uri, obj = self.latest_manifest, repository = parent.repository, + publisher.publish(cls = rpki.publication.manifest_elt, + uri = uri, + obj = self.latest_manifest, + repository = parent.repository, handler = self.manifest_published_callback) def manifest_published_callback(self, pdu): @@ -1837,7 +1856,8 @@ class roa_obj(rpki.sql.sql_persistent): roa = self.roa uri = self.uri - rpki.log.debug("Regenerating ROA %r, ca_detail %r state is %s" % ( + rpki.log.debug("%s %r, ca_detail %r state is %s" % ( + "Regenerating" if regenerate else "Not regenerating", self, ca_detail, ca_detail.state)) if regenerate: @@ -1845,7 +1865,8 @@ class roa_obj(rpki.sql.sql_persistent): rpki.log.debug("Withdrawing %r %s and revoking its EE cert" % (self, uri)) rpki.rpkid.revoked_cert_obj.revoke(cert = cert, ca_detail = ca_detail) - publisher.withdraw(cls = rpki.publication.roa_elt, uri = uri, obj = roa, repository = ca_detail.ca.parent.repository, + publisher.withdraw(cls = rpki.publication.roa_elt, uri = uri, obj = roa, + repository = ca_detail.ca.parent.repository, handler = False if allow_failure else None) if not regenerate: @@ -1949,7 +1970,7 @@ class ghostbuster_obj(rpki.sql.sql_persistent): regen_time = self.cert.getNotAfter() - rpki.sundial.timedelta(seconds = self.self.regen_margin) if rpki.sundial.now() > regen_time: - rpki.log.debug("Ghostbuster record past threshold %s, regenerating" % (regen_time,)) + rpki.log.debug("%r past threshold %s, regenerating" % (self, regen_time)) return self.regenerate(publisher = publisher, fast = fast) def generate(self, publisher, fast = False): @@ -2020,15 +2041,23 @@ class ghostbuster_obj(rpki.sql.sql_persistent): ghostbuster = self.ghostbuster uri = self.uri + rpki.log.debug("%s %r, ca_detail %r state is %s" % ( + "Regenerating" if regenerate else "Not regenerating", + self, ca_detail, ca_detail.state)) + if regenerate: assert ca_detail.state == 'active' self.generate(publisher = publisher, fast = fast) - rpki.log.debug("Withdrawing Ghostbuster record %r and revoking its EE cert" % uri) + rpki.log.debug("Withdrawing %r %s and revoking its EE cert" % (self, uri)) rpki.rpkid.revoked_cert_obj.revoke(cert = cert, ca_detail = ca_detail) - publisher.withdraw(cls = rpki.publication.ghostbuster_elt, uri = uri, obj = ghostbuster, repository = ca_detail.ca.parent.repository, + publisher.withdraw(cls = rpki.publication.ghostbuster_elt, uri = uri, obj = ghostbuster, + repository = ca_detail.ca.parent.repository, handler = False if allow_failure else None) - self.sql_mark_deleted() + + if not regenerate: + self.sql_mark_deleted() + if not fast: ca_detail.generate_crl(publisher = publisher) ca_detail.generate_manifest(publisher = publisher) diff --git a/rpkid/rpki/rpkid_tasks.py b/rpkid/rpki/rpkid_tasks.py index f735f0f7..abfbdcb3 100644 --- a/rpkid/rpki/rpkid_tasks.py +++ b/rpkid/rpki/rpkid_tasks.py @@ -4,7 +4,7 @@ because interactions with rpkid scheduler were getting too complicated. $Id$ -Copyright (C) 2012 Internet Systems Consortium ("ISC") +Copyright (C) 2012-2013 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 @@ -264,8 +264,16 @@ class UpdateChildrenTask(AbstractTask): ca_detail.generate_crl(publisher = self.publisher) ca_detail.generate_manifest(publisher = self.publisher) - elif old_resources != new_resources or (old_resources.valid_until < self.rsn and irdb_resources.valid_until > self.now): + elif old_resources != new_resources or (old_resources.valid_until < self.rsn and + irdb_resources.valid_until > self.now and + old_resources.valid_until != irdb_resources.valid_until): + rpki.log.debug("Need to reissue child %s certificate SKI %s" % (self.child.child_handle, child_cert.cert.gSKI())) + if old_resources != new_resources: + rpki.log.debug("Child %s SKI %s resources changed: old %s new %s" % (self.child.child_handle, child_cert.cert.gSKI(), old_resources, new_resources)) + if old_resources.valid_until != irdb_resources.valid_until: + rpki.log.debug("Child %s SKI %s validity changed: old %s new %s" % (self.child.child_handle, child_cert.cert.gSKI(), old_resources.valid_until, irdb_resources.valid_until)) + new_resources.valid_until = irdb_resources.valid_until child_cert.reissue( ca_detail = ca_detail, diff --git a/rpkid/rpki/sql.py b/rpkid/rpki/sql.py index d4426680..871524e1 100644 --- a/rpkid/rpki/sql.py +++ b/rpkid/rpki/sql.py @@ -3,7 +3,7 @@ SQL interface code. $Id$ -Copyright (C) 2009-2012 Internet Systems Consortium ("ISC") +Copyright (C) 2009-2013 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 @@ -130,7 +130,8 @@ class session(object): Write any dirty objects out to SQL. """ for s in self.dirty.copy(): - rpki.log.debug("Sweeping %r" % s) + #if s.sql_cache_debug: + rpki.log.debug("Sweeping (%s) %r" % ("deleting" if s.sql_deleted else "storing", s)) if s.sql_deleted: s.sql_delete() else: @@ -183,6 +184,11 @@ class sql_persistent(object): sql_debug = False + ## @var sql_cache_debug + # Enable debugging of SQL cache actions + + sql_cache_debug = False + @classmethod def sql_fetch(cls, gctx, id): # pylint: disable=W0622 """ @@ -272,14 +278,19 @@ class sql_persistent(object): """ Mark this object as needing to be written back to SQL. """ + if self.sql_cache_debug and not self.sql_is_dirty: + rpki.log.debug("Marking %r SQL dirty" % self) self.gctx.sql.dirty.add(self) def sql_mark_clean(self): """ Mark this object as not needing to be written back to SQL. """ + if self.sql_cache_debug and self.sql_is_dirty: + rpki.log.debug("Marking %r SQL clean" % self) self.gctx.sql.dirty.discard(self) + @property def sql_is_dirty(self): """ Query whether this object needs to be written back to SQL. @@ -300,14 +311,14 @@ class sql_persistent(object): args = self.sql_encode() if not self.sql_in_db: if self.sql_debug: - rpki.log.debug("sql_fetch_store(%r, %r)" % (self.sql_template.insert, args)) + rpki.log.debug("sql_store(%r, %r)" % (self.sql_template.insert, args)) self.gctx.sql.execute(self.sql_template.insert, args) setattr(self, self.sql_template.index, self.gctx.sql.lastrowid()) self.gctx.sql.cache[(self.__class__, self.gctx.sql.lastrowid())] = self self.sql_insert_hook() else: if self.sql_debug: - rpki.log.debug("sql_fetch_store(%r, %r)" % (self.sql_template.update, args)) + rpki.log.debug("sql_store(%r, %r)" % (self.sql_template.update, args)) self.gctx.sql.execute(self.sql_template.update, args) self.sql_update_hook() key = (self.__class__, getattr(self, self.sql_template.index)) @@ -322,7 +333,7 @@ class sql_persistent(object): if self.sql_in_db: id = getattr(self, self.sql_template.index) # pylint: disable=W0622 if self.sql_debug: - rpki.log.debug("sql_fetch_delete(%r, %r)" % (self.sql_template.delete, id)) + rpki.log.debug("sql_delete(%r, %r)" % (self.sql_template.delete, id)) self.sql_delete_hook() self.gctx.sql.execute(self.sql_template.delete, id) key = (self.__class__, id) diff --git a/rpkid/rpki/x509.py b/rpkid/rpki/x509.py index da151cd6..7e0e37ea 100644 --- a/rpkid/rpki/x509.py +++ b/rpkid/rpki/x509.py @@ -13,7 +13,7 @@ some of the nasty details. This involves a lot of format conversion. $Id$ -Copyright (C) 2009--2012 Internet Systems Consortium ("ISC") +Copyright (C) 2009--2013 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 @@ -98,7 +98,7 @@ class PEM_converter(object): while lines and lines.pop(-1) != self.e: pass if not lines: - raise rpki.exceptions.EmptyPEM, "Could not find PEM in:\n%s" % pem + raise rpki.exceptions.EmptyPEM("Could not find PEM in:\n%s" % pem) return base64.b64decode("".join(lines)) def to_PEM(self, der): @@ -279,7 +279,7 @@ class DER_object(object): self.clear() self.DER = value return - raise rpki.exceptions.DERObjectConversionError, "Can't honor conversion request %r" % (kw,) + raise rpki.exceptions.DERObjectConversionError("Can't honor conversion request %r" % (kw,)) def check_auto_update(self): """ @@ -317,7 +317,7 @@ class DER_object(object): self.check() if self.DER: return self.DER - raise rpki.exceptions.DERObjectConversionError, "No conversion path to DER available" + raise rpki.exceptions.DERObjectConversionError("No conversion path to DER available") def get_Base64(self): """ @@ -534,7 +534,7 @@ class X509(DER_object): if self.POW: self.DER = self.POW.derWrite() return self.get_DER() - raise rpki.exceptions.DERObjectConversionError, "No conversion path to DER available" + raise rpki.exceptions.DERObjectConversionError("No conversion path to DER available") def get_POW(self): """ @@ -656,6 +656,9 @@ class X509(DER_object): if cn is None: cn = "".join(("%02X" % ord(i) for i in ski)) + if now >= notAfter: + raise rpki.exceptions.PastNotAfter("notAfter value %s is already in the past" % notAfter) + cert = rpki.POW.X509() cert.setVersion(2) @@ -841,7 +844,7 @@ class PKCS10(DER_object): if self.POW: self.DER = self.POW.derWrite() return self.get_DER() - raise rpki.exceptions.DERObjectConversionError, "No conversion path to DER available" + raise rpki.exceptions.DERObjectConversionError("No conversion path to DER available") def get_POW(self): """ @@ -880,62 +883,62 @@ class PKCS10(DER_object): """ if not self.get_POW().verify(): - raise rpki.exceptions.BadPKCS10, "Signature check failed" + raise rpki.exceptions.BadPKCS10("Signature check failed") ver = self.get_POW().getVersion() if ver != 0: - raise rpki.exceptions.BadPKCS10, "Bad version number %s" % ver + raise rpki.exceptions.BadPKCS10("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("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" + 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("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("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 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("Certificate request 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("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("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("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("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("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("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("Certificate request SIA id-ad-rpkiManifest ends with slash") @classmethod def create(cls, keypair, exts = None, is_ca = False, @@ -1021,7 +1024,7 @@ class RSA(DER_object): if self.POW: self.DER = self.POW.derWritePrivate() return self.get_DER() - raise rpki.exceptions.DERObjectConversionError, "No conversion path to DER available" + raise rpki.exceptions.DERObjectConversionError("No conversion path to DER available") def get_POW(self): """ @@ -1080,7 +1083,7 @@ class RSApublic(DER_object): if self.POW: self.DER = self.POW.derWritePublic() return self.get_DER() - raise rpki.exceptions.DERObjectConversionError, "No conversion path to DER available" + raise rpki.exceptions.DERObjectConversionError("No conversion path to DER available") def get_POW(self): """ @@ -1167,7 +1170,7 @@ class CMS_object(DER_object): if self.POW: self.DER = self.POW.derWrite() return self.get_DER() - raise rpki.exceptions.DERObjectConversionError, "No conversion path to DER available" + raise rpki.exceptions.DERObjectConversionError("No conversion path to DER available") def get_POW(self): """ @@ -1200,8 +1203,8 @@ class CMS_object(DER_object): raise rpki.exceptions.UnparsableCMSDER if cms.eContentType() != self.econtent_oid: - raise rpki.exceptions.WrongEContentType, "Got CMS eContentType %s, expected %s" % ( - cms.eContentType(), self.econtent_oid) + raise rpki.exceptions.WrongEContentType("Got CMS eContentType %s, expected %s" % ( + cms.eContentType(), self.econtent_oid)) certs = [X509(POW = x) for x in cms.certs()] crls = [CRL(POW = c) for c in cms.crls()] @@ -1281,7 +1284,7 @@ class CMS_object(DER_object): rpki.log.warn("CMS verification failed, dumping ASN.1 (%d octets):" % len(self.get_DER())) for line in dbg.splitlines(): rpki.log.warn(line) - raise rpki.exceptions.CMSVerificationFailed, "CMS verification failed" + raise rpki.exceptions.CMSVerificationFailed("CMS verification failed") return content @@ -1306,8 +1309,8 @@ class CMS_object(DER_object): raise rpki.exceptions.UnparsableCMSDER if cms.eContentType() != self.econtent_oid: - raise rpki.exceptions.WrongEContentType, "Got CMS eContentType %s, expected %s" % ( - cms.eContentType(), self.econtent_oid) + raise rpki.exceptions.WrongEContentType("Got CMS eContentType %s, expected %s" % ( + cms.eContentType(), self.econtent_oid)) return cms.verify(rpki.POW.X509Store(), None, (rpki.POW.CMS_NOCRL | rpki.POW.CMS_NO_SIGNER_CERT_VERIFY | @@ -1373,7 +1376,7 @@ class Wrapped_CMS_object(CMS_object): Get the inner content of this Wrapped_CMS_object. """ if self.content is None: - raise rpki.exceptions.CMSContentNotSet, "Inner content of CMS object %r is not set" % self + raise rpki.exceptions.CMSContentNotSet("Inner content of CMS object %r is not set" % self) return self.content def set_content(self, content): @@ -1760,7 +1763,7 @@ class CRL(DER_object): if self.POW: self.DER = self.POW.derWrite() return self.get_DER() - raise rpki.exceptions.DERObjectConversionError, "No conversion path to DER available" + raise rpki.exceptions.DERObjectConversionError("No conversion path to DER available") def get_POW(self): """ |