[Pkg-freeipa-devel] [Git][freeipa-team/freeipa][master] 2 commits: ci: allow blhc and piuparts to fail

Timo Aaltonen gitlab at salsa.debian.org
Thu Feb 27 13:44:11 GMT 2020



Timo Aaltonen pushed to branch master at FreeIPA packaging / freeipa


Commits:
01148081 by Timo Aaltonen at 2020-02-27T15:41:20+02:00
ci: allow blhc and piuparts to fail

- - - - -
1b61634d by Timo Aaltonen at 2020-02-27T15:42:26+02:00
write-out-only-one-cert-per-file.diff: Fix writing CA cert to file.

- - - - -


4 changed files:

- debian/changelog
- debian/gitlab-ci.yml
- debian/patches/series
- + debian/patches/write-out-only-one-cert-per-file.diff


Changes:

=====================================
debian/changelog
=====================================
@@ -5,6 +5,8 @@ freeipa (4.8.4-1) UNRELEASED; urgency=medium
   * Fix-font-awesome-path.patch: Dropped, upstream.
   * Use debhelper-compat.
   * Add debian/gitlab-ci.yml.
+    - allow blhc and piuparts to fail
+  * write-out-only-one-cert-per-file.diff: Fix writing CA cert to file.
 
  -- Timo Aaltonen <tjaalton at debian.org>  Thu, 28 Nov 2019 15:07:31 +0200
 


=====================================
debian/gitlab-ci.yml
=====================================
@@ -1,3 +1,9 @@
 include:
   - https://salsa.debian.org/salsa-ci-team/pipeline/raw/master/salsa-ci.yml
   - https://salsa.debian.org/salsa-ci-team/pipeline/raw/master/pipeline-jobs.yml
+
+blhc:
+  allow_failure: true
+
+piuparts:
+  allow_failure: true


=====================================
debian/patches/series
=====================================
@@ -4,3 +4,4 @@
 fix-ods-conf-template.diff
 
 # send upstream
+write-out-only-one-cert-per-file.diff


=====================================
debian/patches/write-out-only-one-cert-per-file.diff
=====================================
@@ -0,0 +1,506 @@
+From 84db94dfa458d44ed2546382d13acd42ceb0b7d7 Mon Sep 17 00:00:00 2001
+From: Sam Morris <sam at robots.org.uk>
+Date: Tue, 17 Dec 2019 18:41:35 +0000
+Subject: [PATCH] Debian: write out only one CA certificate per file
+
+ca-certificates populates /etc/ssl/certs with symlinks to its input
+files and then runs 'openssl rehash' to create the symlinks that libssl
+uses to look up a CA certificate to see if it is trused.
+
+'openssl rehash' ignores any files that contain more than one
+certificate: <https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=945274>.
+
+With this change, we write out trusted CA certificates to
+/usr/local/share/ca-certificates/ipa-ca, one certificate per file.
+
+The logic that decides whether to reload the store is moved up into the
+original `insert_ca_certs_into_systemwide_ca_store` and
+`remove_ca_certs_from_systemwide_ca_store` methods. These methods now
+also handle any exceptions that may be thrown while updating the store.
+
+The functions that actually manipulate the store are factored out into
+new `platform_{insert,remove}_ca_certs` methods, which implementations
+must override.
+
+These new methods also orchestrate the cleanup of deprecated files (such
+as `/etc/pki/ca-trust/source/anchors/ipa-ca.crt`), rather than having
+the cleanup code be included in the same method that creates
+`/etc/pki/ca-trust/source/ipa.p11-kit`.
+
+As well as creating `/usr/local/share/ca-certificates/ipa-ca`, Debian
+systems will now also have
+`/usr/local/share/ca-certificates/ipa.p11-kit` be created. Note that
+`p11-kit` in Debian does not use this file.
+
+Fixes: https://pagure.io/freeipa/issue/8106
+---
+ ipaplatform/base/paths.py   |   4 +
+ ipaplatform/base/tasks.py   |  36 +++++++
+ ipaplatform/debian/paths.py |   8 +-
+ ipaplatform/debian/tasks.py | 118 ++++++++++++++++++++++
+ ipaplatform/redhat/tasks.py | 194 +++++++++++++++++-------------------
+ 5 files changed, 259 insertions(+), 101 deletions(-)
+
+diff --git a/ipaplatform/base/paths.py b/ipaplatform/base/paths.py
+index 2dc22ef084..caca55701b 100644
+--- a/ipaplatform/base/paths.py
++++ b/ipaplatform/base/paths.py
+@@ -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"
+diff --git a/ipaplatform/base/tasks.py b/ipaplatform/base/tasks.py
+index 86617a07f5..33ae5ed568 100644
+--- a/ipaplatform/base/tasks.py
++++ b/ipaplatform/base/tasks.py
+@@ -73,6 +73,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):
+@@ -83,6 +100,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):
+diff --git a/ipaplatform/debian/paths.py b/ipaplatform/debian/paths.py
+index 764b5a2815..2de7819e82 100644
+--- a/ipaplatform/debian/paths.py
++++ b/ipaplatform/debian/paths.py
+@@ -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"
+diff --git a/ipaplatform/debian/tasks.py b/ipaplatform/debian/tasks.py
+index 31982a0ee9..025c5d12d9 100644
+--- a/ipaplatform/debian/tasks.py
++++ b/ipaplatform/debian/tasks.py
+@@ -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
+@@ -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()
+diff --git a/ipaplatform/redhat/tasks.py b/ipaplatform/redhat/tasks.py
+index 0a4f4370df..3e1d427705 100644
+--- a/ipaplatform/redhat/tasks.py
++++ b/ipaplatform/redhat/tasks.py
+@@ -28,6 +28,7 @@
+ import ctypes
+ import logging
+ import os
++from pathlib import Path
+ import socket
+ import traceback
+ import errno
+@@ -296,127 +297,120 @@ def reload_systemwide_ca_store(self):
+             logger.info("Systemwide CA database updated.")
+             return True
+ 
+-    def insert_ca_certs_into_systemwide_ca_store(self, ca_certs):
++    def platform_insert_ca_certs(self, ca_certs):
++        return any([
++            self.write_p11kit_certs(paths.IPA_P11_KIT, ca_certs),
++            self.remove_ca_certificates_bundle(
++                paths.SYSTEMWIDE_IPA_CA_CRT
++            ),
++        ])
++
++    def write_p11kit_certs(self, filename, ca_certs):
+         # pylint: disable=ipa-forbidden-import
+         from ipalib import x509  # FixMe: break import cycle
+         from ipalib.errors import CertificateError
+         # pylint: enable=ipa-forbidden-import
+ 
+-        new_cacert_path = paths.SYSTEMWIDE_IPA_CA_CRT
+-
+-        if os.path.exists(new_cacert_path):
+-            try:
+-                os.remove(new_cacert_path)
+-            except OSError as e:
+-                logger.error(
+-                    "Could not remove %s: %s", new_cacert_path, e)
+-                return False
+-
+-        new_cacert_path = paths.IPA_P11_KIT
+-
++        path = Path(filename)
+         try:
+-            f = open(new_cacert_path, 'w')
+-            os.fchmod(f.fileno(), 0o644)
+-        except IOError as e:
+-            logger.info("Failed to open %s: %s", new_cacert_path, e)
+-            return False
++            f = open(path, 'w')
++        except IOError:
++            logger.error("Failed to open %s", path)
++            raise
+ 
+-        f.write("# This file was created by IPA. Do not edit.\n"
+-                "\n")
++        with f:
++            f.write("# This file was created by IPA. Do not edit.\n"
++                    "\n")
+ 
+-        has_eku = set()
+-        for cert, nickname, trusted, _ext_key_usage in ca_certs:
+             try:
+-                subject = cert.subject_bytes
+-                issuer = cert.issuer_bytes
+-                serial_number = cert.serial_number_bytes
+-                public_key_info = cert.public_key_info_bytes
+-            except (PyAsn1Error, ValueError, CertificateError) as e:
+-                logger.warning(
+-                    "Failed to decode certificate \"%s\": %s", nickname, e)
+-                continue
++                os.fchmod(f.fileno(), 0o644)
++            except IOError:
++                logger.error("Failed to set mode of %s", path)
++                raise
+ 
+-            label = urllib.parse.quote(nickname)
+-            subject = urllib.parse.quote(subject)
+-            issuer = urllib.parse.quote(issuer)
+-            serial_number = urllib.parse.quote(serial_number)
+-            public_key_info = urllib.parse.quote(public_key_info)
+-
+-            obj = ("[p11-kit-object-v1]\n"
+-                   "class: certificate\n"
+-                   "certificate-type: x-509\n"
+-                   "certificate-category: authority\n"
+-                   "label: \"%(label)s\"\n"
+-                   "subject: \"%(subject)s\"\n"
+-                   "issuer: \"%(issuer)s\"\n"
+-                   "serial-number: \"%(serial_number)s\"\n"
+-                   "x-public-key-info: \"%(public_key_info)s\"\n" %
+-                   dict(label=label,
+-                        subject=subject,
+-                        issuer=issuer,
+-                        serial_number=serial_number,
+-                        public_key_info=public_key_info))
+-            if trusted is True:
+-                obj += "trusted: true\n"
+-            elif trusted is False:
+-                obj += "x-distrusted: true\n"
+-            obj += "{pem}\n\n".format(
+-                pem=cert.public_bytes(x509.Encoding.PEM).decode('ascii'))
+-            f.write(obj)
+-
+-            if (cert.extended_key_usage is not None and
+-                    public_key_info not in has_eku):
++            has_eku = set()
++            for cert, nickname, trusted, _ext_key_usage in ca_certs:
+                 try:
+-                    ext_key_usage = cert.extended_key_usage_bytes
+-                except PyAsn1Error as e:
+-                    logger.warning(
+-                        "Failed to encode extended key usage for \"%s\": %s",
+-                        nickname, e)
+-                    continue
+-                value = urllib.parse.quote(ext_key_usage)
++                    subject = cert.subject_bytes
++                    issuer = cert.issuer_bytes
++                    serial_number = cert.serial_number_bytes
++                    public_key_info = cert.public_key_info_bytes
++                except (PyAsn1Error, ValueError, CertificateError):
++                    logger.error(
++                        "Failed to decode certificate \"%s\"", nickname)
++                    raise
++
++                label = urllib.parse.quote(nickname)
++                subject = urllib.parse.quote(subject)
++                issuer = urllib.parse.quote(issuer)
++                serial_number = urllib.parse.quote(serial_number)
++                public_key_info = urllib.parse.quote(public_key_info)
++
+                 obj = ("[p11-kit-object-v1]\n"
+-                       "class: x-certificate-extension\n"
+-                       "label: \"ExtendedKeyUsage for %(label)s\"\n"
+-                       "x-public-key-info: \"%(public_key_info)s\"\n"
+-                       "object-id: 2.5.29.37\n"
+-                       "value: \"%(value)s\"\n\n" %
++                       "class: certificate\n"
++                       "certificate-type: x-509\n"
++                       "certificate-category: authority\n"
++                       "label: \"%(label)s\"\n"
++                       "subject: \"%(subject)s\"\n"
++                       "issuer: \"%(issuer)s\"\n"
++                       "serial-number: \"%(serial_number)s\"\n"
++                       "x-public-key-info: \"%(public_key_info)s\"\n" %
+                        dict(label=label,
+-                            public_key_info=public_key_info,
+-                            value=value))
+-                f.write(obj)
+-                has_eku.add(public_key_info)
++                            subject=subject,
++                            issuer=issuer,
++                            serial_number=serial_number,
++                            public_key_info=public_key_info))
++                if trusted is True:
++                    obj += "trusted: true\n"
++                elif trusted is False:
++                    obj += "x-distrusted: true\n"
++                obj += "{pem}\n\n".format(
++                    pem=cert.public_bytes(x509.Encoding.PEM).decode('ascii'))
+ 
+-        f.close()
++                f.write(obj)
+ 
+-        # Add the CA to the systemwide CA trust database
+-        if not self.reload_systemwide_ca_store():
+-            return False
++                if (cert.extended_key_usage is not None and
++                        public_key_info not in has_eku):
++                    try:
++                        ext_key_usage = cert.extended_key_usage_bytes
++                    except PyAsn1Error:
++                        logger.error(
++                            "Failed to encode extended key usage for \"%s\"",
++                            nickname)
++                        raise
++                    value = urllib.parse.quote(ext_key_usage)
++                    obj = ("[p11-kit-object-v1]\n"
++                           "class: x-certificate-extension\n"
++                           "label: \"ExtendedKeyUsage for %(label)s\"\n"
++                           "x-public-key-info: \"%(public_key_info)s\"\n"
++                           "object-id: 2.5.29.37\n"
++                           "value: \"%(value)s\"\n\n" %
++                           dict(label=label,
++                                public_key_info=public_key_info,
++                                value=value))
++                    f.write(obj)
++                    has_eku.add(public_key_info)
+ 
+         return True
+ 
+-    def remove_ca_certs_from_systemwide_ca_store(self):
+-        result = True
+-        update = False
++    def platform_remove_ca_certs(self):
++        return any([
++            self.remove_ca_certificates_bundle(paths.IPA_P11_KIT),
++            self.remove_ca_certificates_bundle(paths.SYSTEMWIDE_IPA_CA_CRT),
++        ])
+ 
+-        # Remove CA cert from systemwide store
+-        for new_cacert_path in (paths.IPA_P11_KIT,
+-                                paths.SYSTEMWIDE_IPA_CA_CRT):
+-            if not os.path.exists(new_cacert_path):
+-                continue
+-            try:
+-                os.remove(new_cacert_path)
+-            except OSError as e:
+-                logger.error(
+-                    "Could not remove %s: %s", new_cacert_path, e)
+-                result = False
+-            else:
+-                update = True
++    def remove_ca_certificates_bundle(self, filename):
++        path = Path(filename)
++        if not path.is_file():
++            return False
+ 
+-        if update:
+-            if not self.reload_systemwide_ca_store():
+-                return False
++        try:
++            path.unlink()
++        except Exception:
++            logger.error("Could not remove %s", path)
++            raise
+ 
+-        return result
++        return True
+ 
+     def backup_hostname(self, fstore, statestore):
+         filepath = paths.ETC_HOSTNAME



View it on GitLab: https://salsa.debian.org/freeipa-team/freeipa/-/compare/341a561eafc71adcf8f2916f32d61d8e06f222b4...1b61634da74b4b862883e553b43bb8015819ef7f

-- 
View it on GitLab: https://salsa.debian.org/freeipa-team/freeipa/-/compare/341a561eafc71adcf8f2916f32d61d8e06f222b4...1b61634da74b4b862883e553b43bb8015819ef7f
You're receiving this email because of your account on salsa.debian.org.


-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://alioth-lists.debian.net/pipermail/pkg-freeipa-devel/attachments/20200227/01c0b763/attachment-0001.html>


More information about the Pkg-freeipa-devel mailing list