URL: https://github.com/freeipa/freeipa/pull/2555
Author: tiran
 Title: #2555: [Backport][ipa-4-7] Add support for multiple 
certificates/formats to ipa-cacert-manage
Action: opened

PR body:
"""
This PR was opened automatically because PR #2465 was pushed to master and 
backport to ipa-4-7 is required.
"""

To pull the PR as Git branch:
git remote add ghfreeipa https://github.com/freeipa/freeipa
git fetch ghfreeipa pull/2555/head:pr2555
git checkout pr2555
From 947674825f1a40fc71c4d5be826dfb198644c685 Mon Sep 17 00:00:00 2001
From: Rob Crittenden <[email protected]>
Date: Wed, 17 Oct 2018 18:12:52 -0400
Subject: [PATCH 1/2] Add tests for ipa-cacert-manage install

Some basic tests like re-loading a certificate, loading a
PKCS#7 cert and bad cert handling.

Signed-off-by: Rob Crittenden <[email protected]>

https://pagure.io/freeipa/issue/7579
---
 ipatests/test_integration/test_commands.py | 35 ++++++++++++++++++++++
 1 file changed, 35 insertions(+)

diff --git a/ipatests/test_integration/test_commands.py b/ipatests/test_integration/test_commands.py
index 1aa1bb3313..cfb2fa48d8 100644
--- a/ipatests/test_integration/test_commands.py
+++ b/ipatests/test_integration/test_commands.py
@@ -27,6 +27,7 @@
 from ipatests.test_integration.base import IntegrationTest
 from ipatests.pytest_ipa.integration import tasks
 from ipatests.create_external_ca import ExternalCA
+from ipatests.test_ipalib.test_x509 import good_pkcs7, badcert
 
 logger = logging.getLogger(__name__)
 
@@ -460,3 +461,37 @@ def test_sssd_ifp_access_ipaapi(self):
             ['sudo', '-u', IPAAPI_USER, '--'] + cmd
         )
         assert uid in result.stdout_text
+
+    def test_ipa_cacert_manage_install(self):
+        # Re-install the IPA CA
+        self.master.run_command([
+            paths.IPA_CACERT_MANAGE,
+            'install',
+            paths.IPA_CA_CRT])
+
+        # Test a non-existent file
+        result = self.master.run_command([
+            paths.IPA_CACERT_MANAGE,
+            'install',
+            '/var/run/cert_not_found'], raiseonerr=False)
+        assert result.returncode == 1
+
+        cmd = self.master.run_command(['mktemp'])
+        filename = cmd.stdout_text.strip()
+
+        for contents in (good_pkcs7,):
+            self.master.put_file_contents(filename, contents)
+            result = self.master.run_command([
+                paths.IPA_CACERT_MANAGE,
+                'install',
+                filename])
+
+        for contents in (badcert,):
+            self.master.put_file_contents(filename, contents)
+            result = self.master.run_command([
+                paths.IPA_CACERT_MANAGE,
+                'install',
+                filename], raiseonerr=False)
+            assert result.returncode == 1
+
+        self.master.run_command(['rm', '-f', filename])

From 2aa16420703f7699146c518de29b78bafc1429e6 Mon Sep 17 00:00:00 2001
From: Rob Crittenden <[email protected]>
Date: Thu, 8 Nov 2018 13:07:24 -0500
Subject: [PATCH 2/2] Add support for multiple certificates/formats to
 ipa-cacert-manage

Only a single cert in DER or PEM format would be loaded from the
provided file. Extend this to include PKCS#7 format and load all
certificates found in the file.

Signed-off-by: Rob Crittenden <[email protected]>

https://pagure.io/freeipa/issue/7579
---
 install/tools/man/ipa-cacert-manage.1  |  18 +++-
 ipaserver/install/ipa_cacert_manage.py | 116 +++++++++++++++----------
 2 files changed, 86 insertions(+), 48 deletions(-)

diff --git a/install/tools/man/ipa-cacert-manage.1 b/install/tools/man/ipa-cacert-manage.1
index bacd56b5a8..0cd34ee77e 100644
--- a/install/tools/man/ipa-cacert-manage.1
+++ b/install/tools/man/ipa-cacert-manage.1
@@ -22,7 +22,9 @@ ipa\-cacert\-manage \- Manage CA certificates in IPA
 .SH "SYNOPSIS"
 \fBipa\-cacert\-manage\fR [\fIOPTIONS\fR...] renew
 .RE
-\fBipa\-cacert\-manage\fR [\fIOPTIONS\fR...] install \fICERTFILE\fR
+\fBipa\-cacert\-manage\fR [\fIOPTIONS\fR...] install \fICERTFILE\fR...
+.RE
+\fBipa\-cacert\-manage\fR [\fIOPTIONS\fR...] list
 .SH "DESCRIPTION"
 \fBipa\-cacert\-manage\fR can be used to manage CA certificates in IPA.
 .SH "COMMANDS"
@@ -41,14 +43,22 @@ When the IPA CA is not configured, this command is not available.
 .RE
 .TP
 \fBinstall\fR
-\- Install a CA certificate
+\- Install one or more CA certificates
 .sp
 .RS
-This command can be used to install the certificate contained in \fICERTFILE\fR as an additional CA certificate to IPA.
+This command can be used to install the certificates contained in \fICERTFILE\fR as additional CA certificates to IPA.
 .sp
 Important: this does not replace IPA CA but adds the provided certificate as a known CA. This is useful for instance when using ipa-server-certinstall to replace HTTP/LDAP certificates with third-party certificates signed by this additional CA.
 .sp
 Please do not forget to run ipa-certupdate on the master, all the replicas and all the clients after this command in order to update IPA certificates databases.
+.sp
+The supported formats for the certificate files are DER, PEM and PKCS#7 format.
+.RE
+\fBlist\fR
+\- List the stored CA certificates
+.sp
+.RS
+Display a list of the nicknames or subjects of the CA certificates that have been installed.
 .RE
 .SH "COMMON OPTIONS"
 .TP
@@ -106,7 +116,7 @@ File containing the IPA CA certificate and the external CA certificate chain. Th
 .SH "INSTALL OPTIONS"
 .TP
 \fB\-n\fR \fINICKNAME\fR, \fB\-\-nickname\fR=\fINICKNAME\fR
-Nickname for the certificate.
+Nickname for the certificate. Applicable only when a single certificate is being installed.
 .TP
 \fB\-t\fR \fITRUST_FLAGS\fR, \fB\-\-trust\-flags\fR=\fITRUST_FLAGS\fR
 Trust flags for the certificate in certutil format. Trust flags are of the form "A,B,C" or "A,B,C,D" where A is for SSL, B is for S/MIME, C is for code signing, and D is for PKINIT. Use ",," for no explicit trust.
diff --git a/ipaserver/install/ipa_cacert_manage.py b/ipaserver/install/ipa_cacert_manage.py
index ebe3f3776d..3f113c35bf 100644
--- a/ipaserver/install/ipa_cacert_manage.py
+++ b/ipaserver/install/ipa_cacert_manage.py
@@ -110,6 +110,8 @@ def validate_options(self):
         elif command == 'install':
             if len(self.args) < 2:
                 parser.error("certificate file name not provided")
+        elif command == 'list':
+            pass
         else:
             parser.error("unknown command \"%s\"" % command)
 
@@ -126,6 +128,8 @@ def run(self):
                 return self.renew()
             elif command == 'install':
                 return self.install()
+            elif command == 'list':
+                return self.list()
             else:
                 raise NotImplementedError
         finally:
@@ -379,17 +383,6 @@ def install(self):
         print("Installing CA certificate, please wait")
 
         options = self.options
-        cert_filename = self.args[1]
-
-        try:
-            cert = x509.load_certificate_from_file(cert_filename)
-        except IOError as e:
-            raise admintool.ScriptError(
-                "Can't open \"%s\": %s" % (cert_filename, e))
-        except (TypeError, ValueError) as e:
-            raise admintool.ScriptError("Not a valid certificate: %s" % e)
-
-        nickname = options.nickname or str(DN(cert.subject))
 
         ca_certs = certstore.get_ca_certs_nss(api.Backend.ldap2,
                                               api.env.basedn,
@@ -398,46 +391,81 @@ def install(self):
 
         with certs.NSSDatabase() as tmpdb:
             tmpdb.create_db()
-            tmpdb.add_cert(cert, nickname, EXTERNAL_CA_TRUST_FLAGS)
-            for ca_cert, ca_nickname, ca_trust_flags in ca_certs:
-                tmpdb.add_cert(ca_cert, ca_nickname, ca_trust_flags)
+            tmpdb.import_files(self.args[1:])
+            imported = tmpdb.list_certs()
+            logger.debug("loaded raw certs '%s'", imported)
 
-            try:
-                tmpdb.verify_ca_cert_validity(nickname)
-            except ValueError as e:
+            if len(imported) > 1 and options.nickname:
                 raise admintool.ScriptError(
-                    "Not a valid CA certificate: %s (visit "
-                    "http://www.freeipa.org/page/Troubleshooting for "
-                    "troubleshooting guide)" % e)
+                    "Nickname can only be used if only a single "
+                    "certificate is loaded")
 
-        trust_flags = options.trust_flags.split(',')
-        if (set(options.trust_flags) - set(',CPTcgpuw') or
-                len(trust_flags) not in [3, 4]):
-            raise admintool.ScriptError("Invalid trust flags")
-
-        extra_flags = trust_flags[3:]
-        extra_usages = set()
-        if extra_flags:
-            if 'C' in extra_flags[0]:
-                extra_usages.add(x509.EKU_PKINIT_KDC)
-            if 'T' in extra_flags[0]:
-                extra_usages.add(x509.EKU_PKINIT_CLIENT_AUTH)
-
-        trust_flags = parse_trust_flags(','.join(trust_flags[:3]))
-        trust_flags = TrustFlags(trust_flags.has_key,
-                                 trust_flags.trusted,
-                                 trust_flags.ca,
-                                 trust_flags.usages | extra_usages)
+            # If a nickname was provided re-import the cert
+            if options.nickname:
+                (nickname, trust_flags) = imported[0]
+                cert = tmpdb.get_cert(nickname)
+                tmpdb.delete_cert(nickname)
+                tmpdb.add_cert(cert, options.nickname, EXTERNAL_CA_TRUST_FLAGS)
+                imported = tmpdb.list_certs()
 
-        try:
-            certstore.put_ca_cert_nss(
-                api.Backend.ldap2, api.env.basedn, cert, nickname, trust_flags)
-        except ValueError as e:
-            raise admintool.ScriptError(
-                "Failed to install the certificate: %s" % e)
+            for ca_cert, ca_nickname, ca_trust_flags in ca_certs:
+                tmpdb.add_cert(ca_cert, ca_nickname, ca_trust_flags)
+
+            for nickname, trust_flags in imported:
+                if trust_flags.has_key:
+                    continue
+                tmpdb.trust_root_cert(nickname, EXTERNAL_CA_TRUST_FLAGS)
+
+            for nickname, trust_flags in imported:
+                try:
+                    tmpdb.verify_ca_cert_validity(nickname)
+                except ValueError as e:
+                    raise admintool.ScriptError(
+                        "Not a valid CA certificate: %s (visit "
+                        "http://www.freeipa.org/page/Troubleshooting for "
+                        "troubleshooting guide)" % e)
+                else:
+                    print("Verified %s" % nickname)
+
+            trust_flags = options.trust_flags.split(',')
+            if (set(options.trust_flags) - set(',CPTcgpuw') or
+                    len(trust_flags) not in [3, 4]):
+                raise admintool.ScriptError("Invalid trust flags")
+
+            extra_flags = trust_flags[3:]
+            extra_usages = set()
+            if extra_flags:
+                if 'C' in extra_flags[0]:
+                    extra_usages.add(x509.EKU_PKINIT_KDC)
+                if 'T' in extra_flags[0]:
+                    extra_usages.add(x509.EKU_PKINIT_CLIENT_AUTH)
+
+            trust_flags = parse_trust_flags(','.join(trust_flags[:3]))
+            trust_flags = TrustFlags(trust_flags.has_key,
+                                     trust_flags.trusted,
+                                     trust_flags.ca,
+                                     trust_flags.usages | extra_usages)
+
+            for nickname, _trust_flags in imported:
+                try:
+                    cert = tmpdb.get_cert(nickname)
+                    certstore.put_ca_cert_nss(
+                        api.Backend.ldap2, api.env.basedn, cert, nickname,
+                        trust_flags)
+                except ValueError as e:
+                    raise admintool.ScriptError(
+                        "Failed to install the certificate: %s" % e)
 
         print("CA certificate successfully installed")
 
+    def list(self):
+        ca_certs = certstore.get_ca_certs_nss(api.Backend.ldap2,
+                                              api.env.basedn,
+                                              api.env.realm,
+                                              False)
+        for _ca_cert, ca_nickname, _ca_trust_flags in ca_certs:
+            print(ca_nickname)
+
 
 def update_ipa_ca_entry(api, cert):
     """
_______________________________________________
FreeIPA-devel mailing list -- [email protected]
To unsubscribe send an email to [email protected]
Fedora Code of Conduct: https://getfedora.org/code-of-conduct.html
List Guidelines: https://fedoraproject.org/wiki/Mailing_list_guidelines
List Archives: 
https://lists.fedorahosted.org/archives/list/[email protected]

Reply via email to