diff --git a/lemur/certificates/verify.py b/lemur/certificates/verify.py index ebc29d21..c35e6f5a 100644 --- a/lemur/certificates/verify.py +++ b/lemur/certificates/verify.py @@ -7,7 +7,7 @@ """ import requests import subprocess -from requests.exceptions import ConnectionError +from requests.exceptions import ConnectionError, InvalidSchema from cryptography import x509 from cryptography.hazmat.backends import default_backend @@ -69,6 +69,9 @@ def crl_verify(cert_path): if response.status_code != 200: raise Exception("Unable to retrieve CRL: {0}".format(point)) + except InvalidSchema: + # Unhandled URI scheme (like ldap://); skip this distribution point. + continue except ConnectionError: raise Exception("Unable to retrieve CRL: {0}".format(point)) diff --git a/lemur/tests/conftest.py b/lemur/tests/conftest.py index cf773930..c0a3149e 100644 --- a/lemur/tests/conftest.py +++ b/lemur/tests/conftest.py @@ -1,11 +1,17 @@ import os + +import datetime import pytest +from cryptography import x509 +from cryptography.hazmat.backends import default_backend +from cryptography.hazmat.primitives.serialization import load_pem_private_key from flask import current_app from flask_principal import identity_changed, Identity from lemur import create_app from lemur.database import db as _db from lemur.auth.service import create_token +from lemur.tests.vectors import PRIVATE_KEY_STR from .factories import ApiKeyFactory, AuthorityFactory, NotificationFactory, DestinationFactory, \ CertificateFactory, UserFactory, RoleFactory, SourceFactory, EndpointFactory, RotationPolicyFactory @@ -193,3 +199,19 @@ def logged_in_admin(session, app): with app.test_request_context(): identity_changed.send(current_app._get_current_object(), identity=Identity(2)) yield + + +@pytest.fixture +def private_key(): + return load_pem_private_key(PRIVATE_KEY_STR.encode(), password=None, backend=default_backend()) + + +@pytest.fixture +def cert_builder(private_key): + return (x509.CertificateBuilder() + .subject_name(x509.Name([x509.NameAttribute(x509.NameOID.COMMON_NAME, 'foo.com')])) + .issuer_name(x509.Name([x509.NameAttribute(x509.NameOID.COMMON_NAME, 'foo.com')])) + .serial_number(1) + .public_key(private_key.public_key()) + .not_valid_before(datetime.datetime(2017, 12, 22)) + .not_valid_after(datetime.datetime(2040, 1, 1))) diff --git a/lemur/tests/test_verify.py b/lemur/tests/test_verify.py new file mode 100644 index 00000000..6b0f79a7 --- /dev/null +++ b/lemur/tests/test_verify.py @@ -0,0 +1,50 @@ +import pytest +from cryptography import x509 +from cryptography.hazmat.backends import default_backend +from cryptography.hazmat.primitives import serialization, hashes +from cryptography.x509 import UniformResourceIdentifier + +from lemur.certificates.verify import verify_string, crl_verify +from lemur.tests.vectors import INTERNAL_VALID_LONG_STR +from lemur.utils import mktempfile + + +def test_verify_simple_cert(): + """Simple certificate without CRL or OCSP.""" + # Verification raises an exception for "unknown" if there are no means to verify it + with pytest.raises(Exception, match="Failed to verify"): + verify_string(INTERNAL_VALID_LONG_STR, '') + + +def test_verify_crl_unknown_scheme(cert_builder, private_key): + """Unknown distribution point URI schemes should be ignored.""" + ldap_uri = 'ldap://ldap.example.org/cn=Example%20Certificate%20Authority?certificateRevocationList;binary' + crl_dp = x509.DistributionPoint([UniformResourceIdentifier(ldap_uri)], + relative_name=None, reasons=None, crl_issuer=None) + cert = (cert_builder + .add_extension(x509.CRLDistributionPoints([crl_dp]), critical=False) + .sign(private_key, hashes.SHA256(), default_backend())) + + with mktempfile() as cert_tmp: + with open(cert_tmp, 'wb') as f: + f.write(cert.public_bytes(serialization.Encoding.PEM)) + + # Must not raise exception + crl_verify(cert_tmp) + + +def test_verify_crl_unreachable(cert_builder, private_key): + """Unreachable CRL distribution point results in error.""" + ldap_uri = 'http://invalid.example.org/crl/foobar.crl' + crl_dp = x509.DistributionPoint([UniformResourceIdentifier(ldap_uri)], + relative_name=None, reasons=None, crl_issuer=None) + cert = (cert_builder + .add_extension(x509.CRLDistributionPoints([crl_dp]), critical=False) + .sign(private_key, hashes.SHA256(), default_backend())) + + with mktempfile() as cert_tmp: + with open(cert_tmp, 'wb') as f: + f.write(cert.public_bytes(serialization.Encoding.PEM)) + + with pytest.raises(Exception, match="Unable to retrieve CRL:"): + crl_verify(cert_tmp)