From e78e243919b2e58b006e6afedad99bef0d75a4e7 Mon Sep 17 00:00:00 2001 From: Ad Schellevis Date: Mon, 21 Oct 2024 11:52:17 +0200 Subject: [PATCH] system: CRL/cert subject hash mismatch in certctl.py, closes https://github.com/opnsense/core/issues/7993 Since pyOpenSSL doesn't support generating hashses for CRL's, we calculated one with the functions available in the libraries. Unfortunately X509Name doesn't seem to support duplicate attributes, causing mismatches on our end. This commit replaces the previous logic with a direct call to openssl, although it's slower, it will deliver the expected outcome. --- src/opnsense/scripts/system/certctl.py | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/src/opnsense/scripts/system/certctl.py b/src/opnsense/scripts/system/certctl.py index 1472bf136..7ae2ef54d 100755 --- a/src/opnsense/scripts/system/certctl.py +++ b/src/opnsense/scripts/system/certctl.py @@ -30,6 +30,7 @@ import glob import sys import os +import subprocess import warnings warnings.filterwarnings('ignore', message='.*cryptography', ) import OpenSSL.crypto @@ -43,26 +44,27 @@ BLACKLISTDESTDIR = '/etc/ssl/untrusted' def certificate_iterator(filename): fext = os.path.splitext(filename)[1][1:].lower() + needs_copy = False try: if fext == 'crl': - x509_items = [x509.load_pem_x509_crl(open(filename, 'rb').read())] + x509_items = [filename] elif fext in ['pem', 'cer', 'crt']: x509_items = x509.load_pem_x509_certificates(open(filename, 'rb').read()) + needs_copy = len(x509_items) > 1 else: # not supported return None except (ValueError, TypeError): return None - needs_copy = len(x509_items) > 1 for x509_item in x509_items: - data = x509_item.public_bytes(serialization.Encoding.PEM) if needs_copy else filename - # XXX: need to check subject_name_hash as below for crl does not offer the same results in all cases if fext == 'crl': - tmp = OpenSSL.crypto.X509().get_issuer() - for item in x509_item.issuer: - setattr(tmp, item.rfc4514_attribute_name, item.value) - hashval = hex(tmp.hash()).lstrip('0x').zfill(8) + # XXX: pyOpenSSL doesn't offer access to subject_name_hash(), so we have to call openssl direct here + spparams = ['/usr/bin/openssl', 'crl', '-in', filename, '-hash', '-noout'] + sp = subprocess.run(spparams, capture_output=True, text=True) + if sp.returncode != 0: + continue + hashval = sp.stdout else: cert = OpenSSL.crypto.load_certificate( OpenSSL.crypto.FILETYPE_PEM, @@ -73,7 +75,7 @@ def certificate_iterator(filename): 'hash': hashval, 'target_pattern': '%s.%s%%d' % (hashval, 'r' if fext == 'crl' else ''), 'type': 'copy' if needs_copy else 'link', - 'data': data, + 'data': x509_item.public_bytes(serialization.Encoding.PEM) if needs_copy and x509_item else filename, 'filename': filename }