aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRob Austein <sra@hactrn.net>2013-04-19 20:33:14 +0000
committerRob Austein <sra@hactrn.net>2013-04-19 20:33:14 +0000
commit05eaca3a52b1049ec69b7788deb4872df1c0d7c5 (patch)
treebb5303470d2dba45850b6af6f4a6c69c3994b723
parentddfb8c4eb8139b94e1031c6f8555aee4b6287afb (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.py7
-rw-r--r--rpkid/rpki/rpkid.py63
-rw-r--r--rpkid/rpki/rpkid_tasks.py12
-rw-r--r--rpkid/rpki/sql.py21
-rw-r--r--rpkid/rpki/x509.py65
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):
"""