Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Backport][ipa-4-8] Debian: write out only one CA certificate per file #4509

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions ipaplatform/base/paths.py
Original file line number Diff line number Diff line change
Expand Up @@ -101,8 +101,12 @@ class BasePathNamespace:
OPENLDAP_LDAP_CONF = "/etc/openldap/ldap.conf"
PAM_LDAP_CONF = "/etc/pam_ldap.conf"
PASSWD = "/etc/passwd"
# Trusted CA certificates used to be written out to this file. In newer
# versions of FreeIPA, it has been replaced by IPA_P11_KIT.
SYSTEMWIDE_IPA_CA_CRT = "/etc/pki/ca-trust/source/anchors/ipa-ca.crt"
IPA_P11_KIT = "/etc/pki/ca-trust/source/ipa.p11-kit"
CA_CERTIFICATES_BUNDLE_PEM = None
CA_CERTIFICATES_DIR = None
NSS_DB_DIR = "/etc/pki/nssdb"
PKI_TOMCAT = "/etc/pki/pki-tomcat"
PKI_TOMCAT_ALIAS_DIR = "/etc/pki/pki-tomcat/alias"
Expand Down
36 changes: 36 additions & 0 deletions ipaplatform/base/tasks.py
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,23 @@ def insert_ca_certs_into_systemwide_ca_store(self, ca_certs):
Returns True if the operation succeeded, False otherwise.
"""

try:
if self.platform_insert_ca_certs(ca_certs):
return self.reload_systemwide_ca_store()
except Exception:
logger.exception('Could not populate systemwide CA store')

return False

def platform_insert_ca_certs(self, ca_certs):
"""
Platform implementations override this method to implement
population of the systemwide CA store.

Returns True if changes were made to the CA store, False otherwise.

Raises Exception if something went wrong.
"""
raise NotImplementedError()

def remove_ca_certs_from_systemwide_ca_store(self):
Expand All @@ -81,6 +98,25 @@ def remove_ca_certs_from_systemwide_ca_store(self):
Returns True if the operation succeeded, False otherwise.
"""

try:
if self.platform_remove_ca_certs():
return self.reload_systemwide_ca_store()
except Exception:
logger.exception(
'Could not remove certificates from systemwide CA store'
)

return False

def platform_remove_ca_certs(self):
"""
Platform implementations override this method to implement
removal of certificates from the systemwide CA store.

Returns True if changes were made to the CA store, False otherwise.

Raises Exception if something went wrong.
"""
raise NotImplementedError()

def get_svc_list_file(self):
Expand Down
8 changes: 7 additions & 1 deletion ipaplatform/debian/paths.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,13 @@ class DebianPathNamespace(BasePathNamespace):
CHRONY_CONF = "/etc/chrony/chrony.conf"
OPENLDAP_LDAP_CONF = "/etc/ldap/ldap.conf"
ETC_DEBIAN_VERSION = "/etc/debian_version"
IPA_P11_KIT = "/usr/local/share/ca-certificates/ipa-ca.crt"
# Old versions of freeipa wrote all trusted certificates to a single
# file, which is not supported by ca-certificates.
CA_CERTIFICATES_BUNDLE_PEM = "/usr/local/share/ca-certificates/ipa-ca.crt"
CA_CERTIFICATES_DIR = "/usr/local/share/ca-certificates/ipa-ca"
# Debian's p11-kit does not use ipa.p11-kit, so the file is provided
# for information only.
IPA_P11_KIT = "/usr/local/share/ca-certificates/ipa.p11-kit"
ETC_SYSCONFIG_DIR = "/etc/default"
SYSCONFIG_AUTOFS = "/etc/default/autofs"
SYSCONFIG_DIRSRV = "/etc/default/dirsrv"
Expand Down
118 changes: 118 additions & 0 deletions ipaplatform/debian/tasks.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,21 @@

from __future__ import absolute_import

import logging
import os
import shutil
from pathlib import Path

from ipaplatform.base.tasks import BaseTaskNamespace
from ipaplatform.redhat.tasks import RedHatTaskNamespace
from ipaplatform.paths import paths

from ipapython import directivesetter
from ipapython import ipautil
from ipapython.dn import DN

logger = logging.getLogger(__name__)


class DebianTaskNamespace(RedHatTaskNamespace):
@staticmethod
Expand Down Expand Up @@ -88,4 +97,113 @@ def configure_pkcs11_modules(self, fstore):
def restore_pkcs11_modules(self, fstore):
pass

def platform_insert_ca_certs(self, ca_certs):
# ca-certificates does not use this file, so it doesn't matter if we
# fail to create it.
try:
self.write_p11kit_certs(paths.IPA_P11_KIT, ca_certs),
except Exception:
logger.exception("""\
Could not create p11-kit anchor trust file. On Debian this file is not
used by ca-certificates and is provided for information only.\
""")

return any([
self.write_ca_certificates_dir(
paths.CA_CERTIFICATES_DIR, ca_certs
),
self.remove_ca_certificates_bundle(
paths.CA_CERTIFICATES_BUNDLE_PEM
),
])

def write_ca_certificates_dir(self, directory, ca_certs):
# pylint: disable=ipa-forbidden-import
from ipalib import x509 # FixMe: break import cycle
# pylint: enable=ipa-forbidden-import

path = Path(directory)
try:
path.mkdir(mode=0o755, exist_ok=True)
except Exception:
logger.error("Could not create %s", path)
raise

for cert, nickname, trusted, _ext_key_usage in ca_certs:
if not trusted:
continue

# I'm not handling errors here because they have already
# been checked by the time we get here
subject = DN(cert.subject)
issuer = DN(cert.issuer)

# Construct the certificate filename using the Subject DN so that
# the user can see which CA a particular file is for, and include
# the serial number to disambiguate clashes where a subordinate CA
# had a new certificate issued.
#
# Strictly speaking, certificates are uniquely idenified by (Issuer
# DN, Serial Number). Do we care about the possibility of a clash
# where a subordinate CA had two certificates issued by different
# CAs who used the same serial number?)
filename = f'{subject.ldap_text()} {cert.serial_number}.crt'

# pylint: disable=old-division
cert_path = path / filename
# pylint: enable=old-division
try:
f = open(cert_path, 'w')
except Exception:
logger.error("Could not create %s", cert_path)
raise

with f:
try:
os.fchmod(f.fileno(), 0o644)
except Exception:
logger.error("Could not set mode of %s", cert_path)
raise

try:
f.write(f"""\
This file was created by IPA. Do not edit.

Description: {nickname}
Subject: {subject.ldap_text()}
Issuer: {issuer.ldap_text()}
Serial Number (dec): {cert.serial_number}
Serial Number (hex): {cert.serial_number:#x}

""")
pem = cert.public_bytes(x509.Encoding.PEM).decode('ascii')
f.write(pem)
except Exception:
logger.error("Could not write to %s", cert_path)
raise

return True

def platform_remove_ca_certs(self):
return any([
self.remove_ca_certificates_dir(paths.CA_CERTIFICATES_DIR),
self.remove_ca_certificates_bundle(paths.IPA_P11_KIT),
self.remove_ca_certificates_bundle(
paths.CA_CERTIFICATES_BUNDLE_PEM
),
])

def remove_ca_certificates_dir(self, directory):
path = Path(paths.CA_CERTIFICATES_DIR)
if not path.exists():
return False

try:
shutil.rmtree(path)
except Exception:
logger.error("Could not remove %s", path)
raise

return True

tasks = DebianTaskNamespace()
Loading