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(): ...@@ -87,12 +87,14 @@ def main():
if r: if r:
sys.exit(r) sys.exit(r)
reserved = 'CN', 'serial'
req = crypto.X509Req() req = crypto.X509Req()
try: try:
with open(cert_path) as f: with open(cert_path) as f:
cert = loadCert(f.read()) cert = loadCert(f.read())
components = dict(cert.get_subject().get_components()) components = dict(cert.get_subject().get_components())
components.pop('CN', None) for k in reserved:
components.pop(k, None)
except IOError, e: except IOError, e:
if e.errno != errno.ENOENT: if e.errno != errno.ENOENT:
raise raise
...@@ -101,8 +103,8 @@ def main(): ...@@ -101,8 +103,8 @@ def main():
components.update(config.req) components.update(config.req)
subj = req.get_subject() subj = req.get_subject()
for k, v in components.iteritems(): for k, v in components.iteritems():
if k == 'CN': if k in reserved:
sys.exit("CN field is reserved.") sys.exit(k + " field is reserved.")
if v: if v:
setattr(subj, k, v) setattr(subj, k, v)
......
...@@ -129,6 +129,15 @@ class RegistryServer(object): ...@@ -129,6 +129,15 @@ class RegistryServer(object):
def babel_dump(self): def babel_dump(self):
self._wait_dump = False 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): def onTimeout(self):
# XXX: Because we use threads to process requests, the statements # XXX: Because we use threads to process requests, the statements
# 'self.timeout = 1' below have no effect as long as the # 'self.timeout = 1' below have no effect as long as the
...@@ -145,12 +154,7 @@ class RegistryServer(object): ...@@ -145,12 +154,7 @@ class RegistryServer(object):
q("DELETE FROM token WHERE token=?", (token,)) q("DELETE FROM token WHERE token=?", (token,))
elif not_after is None or x < not_after: elif not_after is None or x < not_after:
not_after = x not_after = x
for prefix, email, cert in q("SELECT * FROM cert" for cert, prefix, email in self.iterCert():
" WHERE cert IS NOT NULL"):
try:
cert = crypto.load_certificate(crypto.FILETYPE_PEM, cert)
except crypto.Error:
continue
x = x509.notAfter(cert) x = x509.notAfter(cert)
if x <= old: if x <= old:
if prefix == self.prefix: if prefix == self.prefix:
...@@ -303,18 +307,39 @@ class RegistryServer(object): ...@@ -303,18 +307,39 @@ class RegistryServer(object):
if self.prefix is None: if self.prefix is None:
self.prefix = prefix self.prefix = prefix
self.setConfig('prefix', prefix) self.setConfig('prefix', prefix)
return self.createCertificate(prefix, req.get_subject(), subject = req.get_subject()
req.get_pubkey()) subject.serialNumber = str(self.getSubjectSerial())
return self.createCertificate(prefix, subject, req.get_pubkey())
def createCertificate(self, client_prefix, subject, 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 = crypto.X509()
cert.set_serial_number(0) # required for libssl < 1.0
cert.gmtime_adj_notBefore(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()) cert.set_issuer(self.cert.ca.get_subject())
subject.CN = "%u/%u" % (int(client_prefix, 2), len(client_prefix)) subject.CN = "%u/%u" % (int(client_prefix, 2), len(client_prefix))
cert.set_subject(subject) cert.set_subject(subject)
cert.set_pubkey(pubkey) 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.sign(self.cert.key, 'sha1')
cert = crypto.dump_certificate(crypto.FILETYPE_PEM, cert) cert = crypto.dump_certificate(crypto.FILETYPE_PEM, cert)
self.db.execute("UPDATE cert SET cert = ? WHERE prefix = ?", self.db.execute("UPDATE cert SET cert = ? WHERE prefix = ?",
...@@ -329,9 +354,13 @@ class RegistryServer(object): ...@@ -329,9 +354,13 @@ class RegistryServer(object):
pem = self.getCert(cn) pem = self.getCert(cn)
cert = crypto.load_certificate(crypto.FILETYPE_PEM, pem) cert = crypto.load_certificate(crypto.FILETYPE_PEM, pem)
if x509.notAfter(cert) - RENEW_PERIOD < time.time(): if x509.notAfter(cert) - RENEW_PERIOD < time.time():
pem = self.createCertificate(cn, cert.get_subject(), not_after = None
cert.get_pubkey()) elif cert.get_serial_number():
return pem return pem
else:
not_after = cert.get_notAfter()
return self.createCertificate(cn,
cert.get_subject(), cert.get_pubkey(), not_after)
@rpc @rpc
def getCa(self): def getCa(self):
......
...@@ -45,9 +45,10 @@ def fingerprint(cert, alg='sha1'): ...@@ -45,9 +45,10 @@ def fingerprint(cert, alg='sha1'):
def maybe_renew(path, cert, info, renew): def maybe_renew(path, cert, info, renew):
from .registry import RENEW_PERIOD from .registry import RENEW_PERIOD
while True: while True:
next_renew = notAfter(cert) - RENEW_PERIOD if cert.get_serial_number():
if time.time() < next_renew: next_renew = notAfter(cert) - RENEW_PERIOD
return cert, next_renew if time.time() < next_renew:
return cert, next_renew
try: try:
pem = renew() pem = renew()
if not pem or pem == crypto.dump_certificate( 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