fix(tls): accept hashed non-CA certificates during trust checks

This commit is contained in:
RaresKeY
2026-10-21 18:59:02 +00:00
parent df1c2c8b19
commit c4fdaf59b3
3 changed files with 30 additions and 3 deletions
+5 -2
View File
@@ -30,9 +30,12 @@ def check_bundle(path: Path) -> None:
"""Raise on an unreadable, empty, malformed, or affected CA bundle."""
context = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT)
context.load_verify_locations(cafile=str(path))
# A hashed OpenSSL directory can include valid non-CA certificates (for
# example Ubuntu's snakeoil server certificate). get_ca_certs() omits them;
# use the total X.509 count to distinguish these from an empty store.
if not context.cert_store_stats()["x509"]:
raise ValueError("no certificates")
certificates = context.get_ca_certs(binary_form=True)
if not certificates:
raise ValueError("no CA certificates")
blocked = {
BLOCKED_ROOTS[fingerprint]
for certificate in certificates
+1 -1
View File
@@ -1,5 +1,5 @@
# CA trust regression fixtures
The public CA certificates embedded in `tests/test_ca_trust.py` exercise the October 2026 eMudhra trust-store check. They contain no private keys. `emsign-g3.pem` and `aaa.pem` are the incident's affected trust anchors; `emsign-g1.pem` is an unaffected emSign anchor that must remain usable. The test creates temporary PEM files from these public constants. The certificate fingerprints are the factual identity of each fixture.
The public certificates embedded in `tests/test_ca_trust.py` exercise the October 2026 eMudhra trust-store check. They contain no private keys. `emsign-g3.pem` and `aaa.pem` are the incident's affected trust anchors; `emsign-g1.pem` is an unaffected emSign anchor that must remain usable. `localhost-leaf.pem` is a synthetic self-signed Ed25519 server certificate with `CA:FALSE`, generated solely to reproduce hashed non-CA certificates found in Ubuntu's OpenSSL directory; its private key was discarded. The test creates temporary PEM files from these public constants. The certificate fingerprints are the factual identity of each fixture.
Sources: the [incident root list](https://emudhra.we-have-a-problem.com/roots.json) and the public Mozilla root certificates distributed in Debian's `ca-certificates` package. Behavioral chain verification should also cover the incident site's separate [twelve published server chains](https://emudhra.we-have-a-problem.com/chains.zip).
+24
View File
@@ -75,6 +75,20 @@ class CATrustTests(unittest.TestCase):
self.assertEqual(result, 1)
self.assertIn("emSign ECC Root CA - G3", errors.getvalue())
def test_hashed_non_ca_certificate_does_not_reject_clean_store(self):
# Ubuntu's OpenSSL directory can also hash ssl-cert-snakeoil.pem.
# A valid server certificate has no CA entries in get_ca_certs().
with tempfile.TemporaryDirectory() as tmp:
directory = Path(tmp)
(directory / "ce275665.0").symlink_to(self.fixtures / "localhost-leaf.pem")
defaults = ssl.get_default_verify_paths()._replace(
cafile=str(self.fixtures / "emsign-g1.pem"), capath=str(directory)
)
with patch.object(checker.ssl, "get_default_verify_paths", return_value=defaults), patch.dict(
os.environ, {defaults.openssl_capath_env: str(directory)}
), contextlib.redirect_stdout(io.StringIO()):
self.assertEqual(checker.main(["--system"]), 0)
def test_multiple_ca_directories_are_all_checked(self):
with tempfile.TemporaryDirectory() as tmp:
clean, stale = Path(tmp) / "clean", Path(tmp) / "stale"
@@ -96,6 +110,16 @@ class CATrustTests(unittest.TestCase):
# Public Mozilla CA certificates; provenance: tests/fixtures/ca-trust/README.md.
PUBLIC_CERTIFICATES = {
'localhost-leaf.pem': """-----BEGIN CERTIFICATE-----
MIIBOTCB7KADAgECAhRcVxnOQNEFWf92K4dm4I8ChyjVMzAFBgMrZXAwFDESMBAG
A1UEAwwJbG9jYWxob3N0MB4XDTI2MTAyMTE4NTgwN1oXDTM2MTAxODE4NTgwN1ow
FDESMBAGA1UEAwwJbG9jYWxob3N0MCowBQYDK2VwAyEARglphvNgp9qRCESLHnuu
W8VC5urdZl+YgS+Ux1BoLryjUDBOMB0GA1UdDgQWBBSaEckyR/76lX3a5Ijh8B42
oJwN1DAfBgNVHSMEGDAWgBSaEckyR/76lX3a5Ijh8B42oJwN1DAMBgNVHRMBAf8E
AjAAMAUGAytlcANBAJkq9ayy+d4AUZnjhWM31rFke5DT+l9QFcPl34+dpVhqJV/L
m4IpPHYagqrBRHn9Dv/C8qYyPBKnAwJRpNptsgw=
-----END CERTIFICATE-----
""",
'aaa.pem': """-----BEGIN CERTIFICATE-----
MIIEMjCCAxqgAwIBAgIBATANBgkqhkiG9w0BAQUFADB7MQswCQYDVQQGEwJHQjEb
MBkGA1UECAwSR3JlYXRlciBNYW5jaGVzdGVyMRAwDgYDVQQHDAdTYWxmb3JkMRow