From 35d1d345c16fe1adb4cda2e0c33b715d85297dae Mon Sep 17 00:00:00 2001 From: Rob Crittenden Date: Nov 13 2018 09:44:14 +0000 Subject: 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 https://pagure.io/freeipa/issue/7579 Reviewed-By: Florence Blanc-Renaud --- diff --git a/install/tools/man/ipa-cacert-manage.1 b/install/tools/man/ipa-cacert-manage.1 index bacd56b..0cd34ee 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 ebe3f37..3f113c3 100644 --- a/ipaserver/install/ipa_cacert_manage.py +++ b/ipaserver/install/ipa_cacert_manage.py @@ -110,6 +110,8 @@ class CACertManage(admintool.AdminTool): 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 @@ class CACertManage(admintool.AdminTool): return self.renew() elif command == 'install': return self.install() + elif command == 'list': + return self.list() else: raise NotImplementedError finally: @@ -379,17 +383,6 @@ class CACertManage(admintool.AdminTool): 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 @@ class CACertManage(admintool.AdminTool): 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): """