core / trust - add support for certificate bundles in our certctl.py script.

Since we deploy ca_root_nss in a directory with less priority, we can exclude the certificates already part of the base install.
When ca_root_nss contains a certificate not yet known in base, it will copy the certificate from the bundle in the "hashed" file, which also gurantees our packed bundles contain the same content as openssl would use by default.

While here, also include /usr/share/certs/untrusted, which contains the base blacklisted certs.
This commit is contained in:
Ad Schellevis 2024-09-09 20:09:44 +02:00
parent e4d297157f
commit c4d0dd6154

View File

@ -31,36 +31,42 @@ import sys
import os
import OpenSSL.crypto
from cryptography import x509
from cryptography.hazmat.primitives import serialization
TRUSTPATH = ['/usr/share/certs/trusted', '/usr/local/share/certs', '/usr/local/etc/ssl/certs']
BLACKLISTPATH = ['/usr/share/certs/blacklisted', '/usr/local/etc/ssl/blacklisted']
BLACKLISTPATH = ['/usr/share/certs/untrusted', '/usr/share/certs/blacklisted', '/usr/local/etc/ssl/blacklisted']
CERTDESTDIR = '/etc/ssl/certs'
BLACKLISTDESTDIR = '/etc/ssl/blacklisted'
def get_name_hash_file_pattern(filename):
def certificate_iterator(filename):
fext = os.path.splitext(filename)[1][1:].lower()
try:
if fext == 'crl':
x509_item = x509.load_pem_x509_crl(open(filename, 'rb').read())
x509_items = [x509.load_pem_x509_crl(open(filename, 'rb').read())]
elif fext in ['pem', 'cer', 'crt']:
tmp = x509.load_pem_x509_certificates(open(filename, 'rb').read())
# XXX: should be enabled after investigating the ca_root_nss situation
# if len(tmp) > 1:
# print('Skipping %s as it does not contain exactly one certificate' % filename)
# return None
x509_item = tmp[0]
x509_items = x509.load_pem_x509_certificates(open(filename, 'rb').read())
else:
# not supported
return None
except (ValueError, TypeError):
return None
tmp = OpenSSL.crypto.X509().get_issuer()
for item in x509_item.issuer:
setattr(tmp, item.rfc4514_attribute_name, item.value)
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
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)
yield {
'hash': hashval,
'target_pattern': '%s.%s%%d' % (hashval, 'r' if fext == 'crl' else ''),
'type': 'copy' if needs_copy else 'link',
'data': data,
'filename': filename
}
return '%s.%s%%d' % (hex(tmp.hash()).lstrip('0x').zfill(8), 'r' if fext == 'crl' else '')
def get_cert_common_name(filename):
try:
@ -101,29 +107,40 @@ def cmd_rehash():
targetname = 'trusted' if path in TRUSTPATH else 'blacklisted'
print("Scanning %s for certificates..." % path)
for filename in glob.glob('%s/*' % path):
pattern = get_name_hash_file_pattern(filename)
if pattern:
for record in certificate_iterator(filename):
pattern = record['target_pattern']
if pattern not in targets[targetname]:
targets[targetname][pattern] = []
targets[targetname][pattern].append(filename)
if record['type'] == 'copy' and len(targets[targetname][pattern]) > 0:
# skip hardcopies when a link or hardcopy already exists
continue
targets[targetname][pattern].append(record)
for path in [BLACKLISTDESTDIR, CERTDESTDIR]:
for filename in glob.glob('%s/*.[0-9]' % path) + glob.glob('%s/*.r[0-9]' % path):
if os.path.islink(filename):
os.unlink(filename)
else:
os.remove(filename)
for target_name in targets:
for pattern in targets[target_name]:
for seq, filename in enumerate(targets[target_name][pattern]):
if target_name == 'blacklisted':
os.symlink(os.path.relpath(filename, BLACKLISTDESTDIR), "%s/%s" % (BLACKLISTDESTDIR, pattern % seq))
for seq, record in enumerate(targets[target_name][pattern]):
is_bl = target_name == 'blacklisted'
src_filename = os.path.relpath(record['filename'], BLACKLISTDESTDIR if is_bl else CERTDESTDIR)
dst_filename = "%s/%s" % (BLACKLISTDESTDIR if is_bl else CERTDESTDIR, pattern % seq)
if not is_bl and hash in targets['blacklisted']:
print(
"Skipping blacklisted certificate %s (%s/%s)" % (filename, BLACKLISTDESTDIR, pattern % seq)
)
continue
if record['type'] == 'copy':
with open(dst_filename, 'wb') as f_out:
f_out.write(record['data'])
os.chmod(dst_filename, 0o644)
else:
if hash in targets['blacklisted']:
print(
"Skipping blacklisted certificate %s (%s/%s)" % (filename, BLACKLISTDESTDIR, pattern % seq)
)
else:
os.symlink(os.path.relpath(filename, CERTDESTDIR), "%s/%s" % (CERTDESTDIR, pattern % seq))
os.symlink(src_filename, dst_filename)
if __name__ == '__main__':