Commit acc0568a authored by Julien Muchembled's avatar Julien Muchembled

Generate certificates with 2 serials for future needs (crl & ivp4)

And automatic renewal of existing certificates.
parent 37943a26
......@@ -87,12 +87,14 @@ def main():
if r:
sys.exit(r)
reserved = 'CN', 'serial'
req = crypto.X509Req()
try:
with open(cert_path) as f:
cert = loadCert(f.read())
components = dict(cert.get_subject().get_components())
components.pop('CN', None)
for k in reserved:
components.pop(k, None)
except IOError, e:
if e.errno != errno.ENOENT:
raise
......@@ -101,8 +103,8 @@ def main():
components.update(config.req)
subj = req.get_subject()
for k, v in components.iteritems():
if k == 'CN':
sys.exit("CN field is reserved.")
if k in reserved:
sys.exit(k + " field is reserved.")
if v:
setattr(subj, k, v)
......
......@@ -129,6 +129,15 @@ class RegistryServer(object):
def babel_dump(self):
self._wait_dump = False
def iterCert(self):
for prefix, email, cert in self.db.execute(
"SELECT * FROM cert WHERE cert IS NOT NULL"):
try:
yield (crypto.load_certificate(crypto.FILETYPE_PEM, cert),
prefix, email)
except crypto.Error:
pass
def onTimeout(self):
# XXX: Because we use threads to process requests, the statements
# 'self.timeout = 1' below have no effect as long as the
......@@ -145,12 +154,7 @@ class RegistryServer(object):
q("DELETE FROM token WHERE token=?", (token,))
elif not_after is None or x < not_after:
not_after = x
for prefix, email, cert in q("SELECT * FROM cert"
" WHERE cert IS NOT NULL"):
try:
cert = crypto.load_certificate(crypto.FILETYPE_PEM, cert)
except crypto.Error:
continue
for cert, prefix, email in self.iterCert():
x = x509.notAfter(cert)
if x <= old:
if prefix == self.prefix:
......@@ -303,18 +307,39 @@ class RegistryServer(object):
if self.prefix is None:
self.prefix = prefix
self.setConfig('prefix', prefix)
return self.createCertificate(prefix, req.get_subject(),
req.get_pubkey())
def createCertificate(self, client_prefix, subject, pubkey):
subject = req.get_subject()
subject.serialNumber = str(self.getSubjectSerial())
return self.createCertificate(prefix, subject, req.get_pubkey())
def getSubjectSerial(self):
# Smallest unique number, for IPv4 support.
serials = []
for x in self.iterCert():
serial = x[0].get_subject().serialNumber
if serial:
serials.append(int(serial))
serials.sort()
for serial, x in enumerate(serials):
if serial != x:
return serial
return len(serials)
def createCertificate(self, client_prefix, subject, pubkey, not_after=None):
cert = crypto.X509()
cert.set_serial_number(0) # required for libssl < 1.0
cert.gmtime_adj_notBefore(0)
cert.gmtime_adj_notAfter(self.cert_duration)
if not_after:
cert.set_notAfter(not_after)
else:
cert.gmtime_adj_notAfter(self.cert_duration)
cert.set_issuer(self.cert.ca.get_subject())
subject.CN = "%u/%u" % (int(client_prefix, 2), len(client_prefix))
cert.set_subject(subject)
cert.set_pubkey(pubkey)
# Certificate serial, for revocation support. Contrary to
# subject serial, it does not need to be as small as possible.
serial = 1 + self.getConfig('_serial', 0)
self.setConfig('_serial', serial)
cert.set_serial_number(serial)
cert.sign(self.cert.key, 'sha1')
cert = crypto.dump_certificate(crypto.FILETYPE_PEM, cert)
self.db.execute("UPDATE cert SET cert = ? WHERE prefix = ?",
......@@ -329,9 +354,13 @@ class RegistryServer(object):
pem = self.getCert(cn)
cert = crypto.load_certificate(crypto.FILETYPE_PEM, pem)
if x509.notAfter(cert) - RENEW_PERIOD < time.time():
pem = self.createCertificate(cn, cert.get_subject(),
cert.get_pubkey())
return pem
not_after = None
elif cert.get_serial_number():
return pem
else:
not_after = cert.get_notAfter()
return self.createCertificate(cn,
cert.get_subject(), cert.get_pubkey(), not_after)
@rpc
def getCa(self):
......
......@@ -45,9 +45,10 @@ def fingerprint(cert, alg='sha1'):
def maybe_renew(path, cert, info, renew):
from .registry import RENEW_PERIOD
while True:
next_renew = notAfter(cert) - RENEW_PERIOD
if time.time() < next_renew:
return cert, next_renew
if cert.get_serial_number():
next_renew = notAfter(cert) - RENEW_PERIOD
if time.time() < next_renew:
return cert, next_renew
try:
pem = renew()
if not pem or pem == crypto.dump_certificate(
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment