Commit 21f38e4b authored by Vincent Pelletier's avatar Vincent Pelletier

client: Fix CA and CRL update when a CA is expired.

Otherwise, the expired CA causes an error when it is being loaded, before
the time comparison.
Also, CRL signed by that CA also causes an error (as its signature cannot
be checked).
Catch these errors so the corresponding unusable PEMs are discarded.
parent f7d8281d
0.9.10 (not released yet) 0.9.10 (2021-04-07)
=================== ===================
* Properly handle present but expired CA certificates.
* Properly handle CRLs whose CA certificate is missing.
* Add caucase.client.CaucaseClient.close method. * Add caucase.client.CaucaseClient.close method.
0.9.9 (2021-03-02) 0.9.9 (2021-03-02)
......
...@@ -22,7 +22,6 @@ ...@@ -22,7 +22,6 @@
Caucase - Certificate Authority for Users, Certificate Authority for SErvices Caucase - Certificate Authority for Users, Certificate Authority for SErvices
""" """
from __future__ import absolute_import from __future__ import absolute_import
import datetime
import httplib import httplib
import json import json
import ssl import ssl
...@@ -31,6 +30,7 @@ from cryptography import x509 ...@@ -31,6 +30,7 @@ from cryptography import x509
from cryptography.hazmat.backends import default_backend from cryptography.hazmat.backends import default_backend
import cryptography.exceptions import cryptography.exceptions
import pem import pem
from . import exceptions
from . import utils from . import utils
from . import version from . import version
...@@ -96,12 +96,14 @@ class CaucaseClient(object): ...@@ -96,12 +96,14 @@ class CaucaseClient(object):
loaded_ca_pem_list = utils.getCertList(ca_crt_path) loaded_ca_pem_list = utils.getCertList(ca_crt_path)
else: else:
updated = False updated = False
now = datetime.datetime.utcnow() ca_pem_list = []
ca_pem_list = [ for loaded_ca_pem in loaded_ca_pem_list:
x try:
for x in loaded_ca_pem_list utils.load_ca_certificate(loaded_ca_pem)
if utils.load_ca_certificate(x).not_valid_after > now except exceptions.CertificateVerificationError:
] continue
else:
ca_pem_list.append(loaded_ca_pem)
with cls(ca_url=url, ca_crt_pem_list=ca_pem_list) as client: with cls(ca_url=url, ca_crt_pem_list=ca_pem_list) as client:
ca_pem_list.extend(client.getCACertificateChain()) ca_pem_list.extend(client.getCACertificateChain())
if ca_pem_list != loaded_ca_pem_list: if ca_pem_list != loaded_ca_pem_list:
...@@ -126,14 +128,19 @@ class CaucaseClient(object): ...@@ -126,14 +128,19 @@ class CaucaseClient(object):
Return whether an update happened. Return whether an update happened.
""" """
def _asCRLDict(crl_list): def _asCRLDict(crl_pem_list):
return { result = {}
utils.getAuthorityKeyIdentifier(utils.load_crl(x, ca_list)): x for crl_pem in crl_pem_list:
for x in crl_list try:
} crl = utils.load_crl(crl_pem, ca_list)
except cryptography.exceptions.InvalidSignature:
continue
else:
result[utils.getAuthorityKeyIdentifier(crl)] = crl_pem
return result
local_crl_list = utils.getCRLList(crl_path) local_crl_list = utils.getCRLList(crl_path)
try: try:
local_crl_dict = _asCRLDict(crl_list=local_crl_list) local_crl_dict = _asCRLDict(crl_pem_list=local_crl_list)
except x509.extensions.ExtensionNotFound: except x509.extensions.ExtensionNotFound:
# BBB: caucased used to issue CRLs without the AuthorityKeyIdentifier # BBB: caucased used to issue CRLs without the AuthorityKeyIdentifier
# extension. In such case, local CRLs need to be replaced. # extension. In such case, local CRLs need to be replaced.
......
...@@ -55,6 +55,7 @@ import unittest ...@@ -55,6 +55,7 @@ import unittest
from urllib import quote, urlencode from urllib import quote, urlencode
import urlparse import urlparse
from cryptography import x509 from cryptography import x509
import cryptography.exceptions
from cryptography.hazmat.backends import default_backend from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives import serialization, hashes from cryptography.hazmat.primitives import serialization, hashes
from caucase import cli from caucase import cli
...@@ -1745,6 +1746,25 @@ class CaucaseTest(unittest.TestCase): ...@@ -1745,6 +1746,25 @@ class CaucaseTest(unittest.TestCase):
[old_cau_pem, new_cau_pem], [old_cau_pem, new_cau_pem],
) )
self._startServer() self._startServer()
# The old CA certificate is properly expired
self.assertRaises(
exceptions.CertificateVerificationError,
utils.load_ca_certificate,
old_cau_pem,
)
# Loading the CRL signed by the old CA certificate fails
new_cau = utils.load_ca_certificate(new_cau_pem)
try:
for crl_pem in (crl_pem_a, crl_pem_b):
utils.load_crl(crl_pem, [new_cau])
except cryptography.exceptions.InvalidSignature:
pass
else:
raise AssertionError(
'cryptography.exceptions.InvalidSignature was not raised',
)
# CA renewal works
updateCAFile()
# Non-renewed user certificate is rejected # Non-renewed user certificate is rejected
self.assertRaises(CaucaseError, _checkUserAccess, user_key_path) self.assertRaises(CaucaseError, _checkUserAccess, user_key_path)
# Revoked and non-renewed user certificate is rejected # Revoked and non-renewed user certificate is rejected
...@@ -1755,7 +1775,7 @@ class CaucaseTest(unittest.TestCase): ...@@ -1755,7 +1775,7 @@ class CaucaseTest(unittest.TestCase):
_checkUserAccess(user3_key) _checkUserAccess(user3_key)
# A client without established trust anchor only gets valid CA certs. # A client without established trust anchor only gets valid CA certs.
os.unlink(self._client_user_ca_crt) os.unlink(self._client_user_ca_crt)
self._runClient('--mode', 'user') self._runClient('--mode', 'user', '--update-user')
self.assertItemsEqual( self.assertItemsEqual(
[new_cau_pem], [new_cau_pem],
utils.getCertList(self._client_user_ca_crt), utils.getCertList(self._client_user_ca_crt),
......
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