summaryrefslogtreecommitdiffstats
path: root/ipaserver/install
diff options
context:
space:
mode:
authorJan Cholasta <jcholast@redhat.com>2014-09-24 16:31:39 +0200
committerMartin Kosek <mkosek@redhat.com>2014-09-30 08:50:47 +0200
commit3aa0731fc660ea3d111a44926ab5dea71dc510e7 (patch)
treec4ce2ba7de1163ce699d7fb65ce19bf30e86aa68 /ipaserver/install
parent60ecba77cd98f37be0d2c0f69efd307a687e59dc (diff)
downloadfreeipa-3aa0731fc660ea3d111a44926ab5dea71dc510e7.tar.gz
freeipa-3aa0731fc660ea3d111a44926ab5dea71dc510e7.tar.xz
freeipa-3aa0731fc660ea3d111a44926ab5dea71dc510e7.zip
External CA installer options usability fixes
The --external_cert_file and --external_ca_file options of ipa-server-install and ipa-ca-install have been replaced by --external-cert-file option which accepts multiple files. The files are accepted in PEM and DER certificate and PKCS#7 certificate chain formats. https://fedorahosted.org/freeipa/ticket/4480 Reviewed-By: Petr Viktorin <pviktori@redhat.com>
Diffstat (limited to 'ipaserver/install')
-rw-r--r--ipaserver/install/cainstance.py4
-rw-r--r--ipaserver/install/installutils.py109
-rw-r--r--ipaserver/install/ipa_cacert_manage.py34
3 files changed, 81 insertions, 66 deletions
diff --git a/ipaserver/install/cainstance.py b/ipaserver/install/cainstance.py
index c26046c47..26c603702 100644
--- a/ipaserver/install/cainstance.py
+++ b/ipaserver/install/cainstance.py
@@ -585,7 +585,7 @@ class CAInstance(DogtagInstance):
if self.external == 1:
print "The next step is to get %s signed by your CA and re-run %s as:" % (self.csr_file, sys.argv[0])
- print "%s --external_cert_file=/path/to/signed_certificate --external_ca_file=/path/to/external_ca_certificate" % sys.argv[0]
+ print "%s --external-cert-file=/path/to/signed_certificate --external-cert-file=/path/to/external_ca_certificate" % sys.argv[0]
sys.exit(0)
else:
shutil.move(paths.CA_BACKUP_KEYS_P12,
@@ -726,7 +726,7 @@ class CAInstance(DogtagInstance):
if self.external == 1:
print "The next step is to get %s signed by your CA and re-run %s as:" % (self.csr_file, sys.argv[0])
- print "%s --external_cert_file=/path/to/signed_certificate --external_ca_file=/path/to/external_ca_certificate" % sys.argv[0]
+ print "%s --external-cert-file=/path/to/signed_certificate --external-cert-file=/path/to/external_ca_certificate" % sys.argv[0]
sys.exit(0)
# pkisilent makes a copy of the CA PKCS#12 file for us but gives
diff --git a/ipaserver/install/installutils.py b/ipaserver/install/installutils.py
index c8e1a8de9..395023f6c 100644
--- a/ipaserver/install/installutils.py
+++ b/ipaserver/install/installutils.py
@@ -942,52 +942,77 @@ def check_entropy():
except ValueError as e:
root_logger.debug("Invalid value in %s %s", paths.ENTROPY_AVAIL, e)
-def validate_external_cert(cert_file, ca_file, subject_base):
- extcert = None
- try:
- extcert = x509.load_certificate_from_file(cert_file)
- certsubject = DN(str(extcert.subject))
- certissuer = DN(str(extcert.issuer))
- except IOError, e:
- raise ValueError("Can't load the PEM certificate: %s." % e)
- except (TypeError, NSPRError):
- raise ValueError(
- "'%s' is not a valid PEM-encoded certificate." % cert_file)
- finally:
- del extcert
+def load_external_cert(files, subject_base):
+ """
+ Load and verify external CA certificate chain from multiple files.
- wantsubject = DN(('CN', 'Certificate Authority'), subject_base)
- if certsubject != wantsubject:
- raise ValueError(
- "Subject of the external certificate is not correct (got %s, "
- "expected %s)." % (certsubject, wantsubject))
+ The files are accepted in PEM and DER certificate and PKCS#7 certificate
+ chain formats.
- extchain = None
- try:
- extchain = x509.load_certificate_list_from_file(ca_file)
- certdict = dict((DN(str(cert.subject)), DN(str(cert.issuer)))
- for cert in extchain)
- except IOError, e:
- raise ValueError("Can't load the external CA chain: %s." % e)
- except (TypeError, NSPRError):
- raise ValueError(
- "'%s' is not a valid PEM-encoded certificate chain." % ca_file)
- finally:
- del extchain
-
- if certissuer not in certdict:
- raise ValueError(
- "The external certificate is not signed by the external CA "
- "(unknown issuer %s)." % certissuer)
+ :param files: Names of files to import
+ :param subject_base: Subject name base for IPA certificates
+ :returns: Temporary file with the IPA CA certificate and temporary file
+ with the external CA certificate chain
+ """
+ with certs.NSSDatabase() as nssdb:
+ db_password = ipautil.ipa_generate_password()
+ db_pwdfile = ipautil.write_tmp_file(db_password)
+ nssdb.create_db(db_pwdfile.name)
- while certsubject != certissuer:
- certsubject = certissuer
try:
- certissuer = certdict[certsubject]
- except KeyError:
- raise ValueError(
- "The external CA chain is incomplete (%s is missing from the "
- "chain)." % certsubject)
+ nssdb.import_files(files, db_pwdfile.name)
+ except RuntimeError as e:
+ raise ScriptError(str(e))
+
+ ca_subject = DN(('CN', 'Certificate Authority'), subject_base)
+ ca_nickname = None
+ cache = {}
+ for nickname, trust_flags in nssdb.list_certs():
+ cert = nssdb.get_cert(nickname, pem=True)
+
+ nss_cert = x509.load_certificate(cert)
+ subject = DN(str(nss_cert.subject))
+ issuer = DN(str(nss_cert.issuer))
+ del nss_cert
+
+ cache[nickname] = (cert, subject, issuer)
+ if subject == ca_subject:
+ ca_nickname = nickname
+ nssdb.trust_root_cert(nickname)
+
+ if ca_nickname is None:
+ raise ScriptError(
+ "IPA CA certificate not found in %s" % (", ".join(files)))
+
+ trust_chain = reversed(nssdb.get_trust_chain(ca_nickname))
+ ca_cert_chain = []
+ for nickname in trust_chain:
+ cert, subject, issuer = cache[nickname]
+ ca_cert_chain.append(cert)
+ if subject == issuer:
+ break
+ else:
+ raise ScriptError(
+ "CA certificate chain in %s is incomplete" %
+ (", ".join(files)))
+
+ for nickname in trust_chain:
+ try:
+ nssdb.verify_ca_cert_validity(nickname)
+ except ValueError, e:
+ raise ScriptError(
+ "CA certificate %s in %s is not valid: %s" %
+ (subject, ", ".join(files), e))
+
+ cert_file = tempfile.NamedTemporaryFile()
+ cert_file.write(ca_cert_chain[0] + '\n')
+ cert_file.flush()
+
+ ca_file = tempfile.NamedTemporaryFile()
+ ca_file.write('\n'.join(ca_cert_chain[1:]) + '\n')
+ ca_file.flush()
+
+ return cert_file, ca_file
def create_system_user(name, group, homedir, shell):
diff --git a/ipaserver/install/ipa_cacert_manage.py b/ipaserver/install/ipa_cacert_manage.py
index c681261e8..6a7fd0517 100644
--- a/ipaserver/install/ipa_cacert_manage.py
+++ b/ipaserver/install/ipa_cacert_manage.py
@@ -60,11 +60,10 @@ class CACertManage(admintool.AdminTool):
action='store_false',
help="Sign the renewed certificate by external CA")
renew_group.add_option(
- "--external-cert-file", dest='external_cert_file',
- help="PEM file containing a certificate signed by the external CA")
- renew_group.add_option(
- "--external-ca-file", dest='external_ca_file',
- help="PEM file containing the external CA chain")
+ "--external-cert-file", dest="external_cert_files",
+ action="append", metavar="FILE",
+ help="File containing the IPA CA certificate and the external CA "
+ "certificate chain")
parser.add_option_group(renew_group)
install_group = OptionGroup(parser, "Install options")
@@ -90,10 +89,7 @@ class CACertManage(admintool.AdminTool):
options = self.options
if command == 'renew':
- if options.external_cert_file and not options.external_ca_file:
- parser.error("--external-ca-file not specified")
- elif not options.external_cert_file and options.external_ca_file:
- parser.error("--external-cert-file not specified")
+ pass
elif command == 'install':
if len(self.args) < 2:
parser.error("certificate file name not provided")
@@ -107,7 +103,7 @@ class CACertManage(admintool.AdminTool):
api.bootstrap(in_server=True)
api.finalize()
- if ((command == 'renew' and options.external_cert_file) or
+ if ((command == 'renew' and options.external_cert_files) or
command == 'install'):
self.conn = self.ldap_connect()
else:
@@ -166,7 +162,7 @@ class CACertManage(admintool.AdminTool):
cert = db.get_cert_from_db(self.cert_nickname, pem=False)
options = self.options
- if options.external_cert_file:
+ if options.external_cert_files:
return self.renew_external_step_2(ca, cert)
if options.self_signed is not None:
@@ -200,31 +196,25 @@ class CACertManage(admintool.AdminTool):
"ipa-cacert-manage as:" % paths.IPA_CA_CSR)
print("ipa-cacert-manage renew "
"--external-cert-file=/path/to/signed_certificate "
- "--external-ca-file=/path/to/external_ca_certificate")
+ "--external-cert-file=/path/to/external_ca_certificate")
def renew_external_step_2(self, ca, old_cert):
print "Importing the renewed CA certificate, please wait"
options = self.options
- cert_filename = options.external_cert_file
- ca_filename = options.external_ca_file
+ cert_file, ca_file = installutils.load_external_cert(
+ options.external_cert_files, x509.subject_base())
nss_cert = None
nss.nss_init(ca.dogtag_constants.ALIAS_DIR)
try:
- try:
- installutils.validate_external_cert(
- cert_filename, ca_filename, x509.subject_base())
- except ValueError, e:
- raise admintool.ScriptError(e)
-
nss_cert = x509.load_certificate(old_cert, x509.DER)
subject = nss_cert.subject
#pylint: disable=E1101
pkinfo = nss_cert.subject_public_key_info.format()
#pylint: enable=E1101
- nss_cert = x509.load_certificate_from_file(cert_filename)
+ nss_cert = x509.load_certificate_from_file(cert_file.name)
if not nss_cert.is_ca_cert():
raise admintool.ScriptError("Not a CA certificate")
if nss_cert.subject != subject:
@@ -249,7 +239,7 @@ class CACertManage(admintool.AdminTool):
raise admintool.ScriptError(
"Not compatible with the current CA certificate: %s", e)
- ca_certs = x509.load_certificate_list_from_file(ca_filename)
+ ca_certs = x509.load_certificate_list_from_file(ca_file.name)
for ca_cert in ca_certs:
tmpdb.add_cert(ca_cert.der_data, str(ca_cert.subject), 'C,,')
del ca_certs