Dne 5.8.2014 v 10:30 Jan Cholasta napsal(a):
Hi,

the attached patch fixes the code part of
<https://fedorahosted.org/freeipa/ticket/4457>.

Also the patch depends on my patch 295, which is already available in ipa-4-1 and master. Attaching the current version of the patch.


The next step is to review and update CA-less articles in our wiki.

--
Jan Cholasta
>From 34e036bc1a9cc3592c0c3672a5a9073c5b54bf8f Mon Sep 17 00:00:00 2001
From: Jan Cholasta <jchol...@redhat.com>
Date: Fri, 13 Jun 2014 14:44:03 +0200
Subject: [PATCH 50/56] Add new NSSDatabase method get_cert for getting certs
 from NSS databases.

Part of https://fedorahosted.org/freeipa/ticket/3737
---
 ipaserver/install/certs.py | 14 +++++++++++++-
 1 file changed, 13 insertions(+), 1 deletion(-)

diff --git a/ipaserver/install/certs.py b/ipaserver/install/certs.py
index 815f3bf..f958e36 100644
--- a/ipaserver/install/certs.py
+++ b/ipaserver/install/certs.py
@@ -211,9 +211,21 @@ class NSSDatabase(object):
                 raise RuntimeError(
                     "Setting trust on %s failed" % root_nickname)
 
+    def get_cert(self, nickname, pem=False):
+        args = ['-L', '-n', nickname]
+        if pem:
+            args.append('-a')
+        else:
+            args.append('-r')
+        try:
+            cert, err, returncode = self.run_certutil(args)
+        except ipautil.CalledProcessError:
+            raise RuntimeError("Failed to get %s" % nickname)
+        return cert
+
     def export_pem_cert(self, nickname, location):
         """Export the given cert to PEM file in the given location"""
-        cert, err, returncode = self.run_certutil(["-L", "-n", nickname, "-a"])
+        cert = self.get_cert(nickname)
         with open(location, "w+") as fd:
             fd.write(cert)
         os.chmod(location, 0444)
-- 
1.9.3

>From 3d3c5077d83000b57dd70a6b7bfacde7b39fb707 Mon Sep 17 00:00:00 2001
From: Jan Cholasta <jchol...@redhat.com>
Date: Tue, 5 Aug 2014 09:06:39 +0200
Subject: [PATCH] Make CA-less ipa-server-install option --root-ca-file
 optional.

The CA cert specified by --root-ca-file option must always be the CA cert of
the CA which issued the server certificates in the PKCS#12 files. As the cert
is not actually user selectable, use CA cert from the PKCS#12 files by default
if it is present.

Document --root-ca-file in ipa-server-install man page.

https://fedorahosted.org/freeipa/ticket/4457
---
 install/tools/ipa-server-install            | 22 +++++++-------
 install/tools/man/ipa-server-install.1      |  3 ++
 ipaserver/install/dsinstance.py             | 15 +++++-----
 ipaserver/install/installutils.py           | 46 ++++++++++++++++-------------
 ipaserver/install/ipa_replica_prepare.py    | 14 +++++++--
 ipaserver/install/ipa_server_certinstall.py |  3 +-
 6 files changed, 59 insertions(+), 44 deletions(-)

diff --git a/install/tools/ipa-server-install b/install/tools/ipa-server-install
index 1f158a4..b0a59d8 100755
--- a/install/tools/ipa-server-install
+++ b/install/tools/ipa-server-install
@@ -68,7 +68,7 @@ from ipapython import sysrestore
 from ipapython.ipautil import *
 from ipapython import ipautil
 from ipapython import dogtag
-from ipalib import api, errors, util
+from ipalib import api, errors, util, x509
 from ipapython.config import IPAOptionParser
 from ipalib.x509 import load_certificate_from_file, load_certificate_chain_from_file
 from ipalib.util import validate_domain_name
@@ -199,7 +199,7 @@ def parse_options():
     cert_group.add_option("--pkinit_pin", dest="pkinit_pin",
                       help="The password of the Kerberos KDC PKCS#12 file")
     cert_group.add_option("--root-ca-file", dest="root_ca_file",
-                      help="PEM file with root CA certificate(s) to trust")
+                      help="PEM file containing the CA certificate for the PKCS#12 files")
     cert_group.add_option("--subject", action="callback", callback=subject_callback,
                       type="string",
                       help="The certificate subject base (default O=<realm-name>)")
@@ -289,10 +289,6 @@ def parse_options():
         if options.pkinit_pkcs12 and options.pkinit_pin is None:
             parser.error("You must specify --pkinit_pin with --pkinit_pkcs12")
 
-    if options.dirsrv_pkcs12 and not options.root_ca_file:
-        parser.error(
-            "--root-ca-file must be given with the PKCS#12 options.")
-
     if (options.external_cert_file or options.external_ca_file) and options.dirsrv_pkcs12:
         parser.error(
             "PKCS#12 options cannot be used with the external CA options.")
@@ -913,7 +909,7 @@ def main():
             if options.http_pin is None:
                 sys.exit("%s unlock password required" % options.http_pkcs12)
         http_pkcs12_info = (options.http_pkcs12, options.http_pin)
-        http_cert_name = installutils.check_pkcs12(
+        http_ca_cert = installutils.check_pkcs12(
             http_pkcs12_info, ca_file, host_name)
 
     if options.dirsrv_pkcs12:
@@ -924,7 +920,7 @@ def main():
             if options.dirsrv_pin is None:
                 sys.exit("%s unlock password required" % options.dirsrv_pkcs12)
         dirsrv_pkcs12_info = (options.dirsrv_pkcs12, options.dirsrv_pin)
-        dirsrv_cert_name = installutils.check_pkcs12(
+        dirsrv_ca_cert = installutils.check_pkcs12(
             dirsrv_pkcs12_info, ca_file, host_name)
 
     if options.pkinit_pkcs12:
@@ -936,6 +932,11 @@ def main():
                 sys.exit("%s unlock password required" % options.pkinit_pkcs12)
         pkinit_pkcs12_info = (options.pkinit_pkcs12, options.pkinit_pin)
 
+    if (options.http_pkcs12 and options.dirsrv_pkcs12 and
+        http_ca_cert != dirsrv_ca_cert):
+        sys.exit("%s and %s are not signed by the same CA certificate" %
+                 (options.http_pkcs12, options.dirsrv_pkcs12))
+
     if not options.dm_password:
         dm_password = read_dm_password()
 
@@ -1065,8 +1066,7 @@ def main():
                 ntp.create_instance()
 
         if options.dirsrv_pkcs12:
-            ds = dsinstance.DsInstance(fstore=fstore,
-                cert_nickname=dirsrv_cert_name)
+            ds = dsinstance.DsInstance(fstore=fstore)
             ds.create_instance(realm_name, host_name, domain_name,
                             dm_password, dirsrv_pkcs12_info,
                             idstart=options.idstart, idmax=options.idmax,
@@ -1120,7 +1120,7 @@ def main():
         ca.publish_ca_cert(CACERT)
     else:
         # Put the CA cert where other instances expect it
-        shutil.copy(options.root_ca_file, CACERT)
+        x509.write_certificate(http_ca_cert, CACERT)
         os.chmod(CACERT, 0444)
 
     # we now need to enable ssl on the ds
diff --git a/install/tools/man/ipa-server-install.1 b/install/tools/man/ipa-server-install.1
index 4adf1d0..34de845 100644
--- a/install/tools/man/ipa-server-install.1
+++ b/install/tools/man/ipa-server-install.1
@@ -114,6 +114,9 @@ The password of the Apache Server PKCS#12 file
 \fB\-\-pkinit_pin\fR=\fIPKINIT_PIN\fR
 The password of the Kerberos KDC PKCS#12 file
 .TP
+\fB\-\-root\-ca\-file\fR=\fIFILE\fR
+PEM file containing the CA certificate of the CA which issued the Directory Server, Apache Server and Kerberos KDC SSL certificates. Use this option if the CA certificate is not present in the PKCS#12 files.
+.TP
 \fB\-\-subject\fR=\fISUBJECT\fR
 The certificate subject base (default O=REALM.NAME)
 
diff --git a/ipaserver/install/dsinstance.py b/ipaserver/install/dsinstance.py
index 2a9f3b6..61ea52a 100644
--- a/ipaserver/install/dsinstance.py
+++ b/ipaserver/install/dsinstance.py
@@ -221,14 +221,14 @@ info: IPA V2.0
 
 class DsInstance(service.Service):
     def __init__(self, realm_name=None, domain_name=None, dm_password=None,
-                 fstore=None, cert_nickname='Server-Cert'):
+                 fstore=None):
         service.Service.__init__(self, "dirsrv",
             service_desc="directory server",
             dm_password=dm_password,
             ldapi=False,
             autobind=service.DISABLED
             )
-        self.nickname = cert_nickname
+        self.nickname = 'Server-Cert'
         self.dm_password = dm_password
         self.realm = realm_name
         self.sub_dict = None
@@ -632,24 +632,23 @@ class DsInstance(service.Service):
                 raise RuntimeError("Could not find a suitable server cert in import in %s" % self.pkcs12_info[0])
 
             # We only handle one server cert
-            nickname = server_certs[0][0]
-            self.dercert = dsdb.get_cert_from_db(nickname, pem=False)
+            self.nickname = server_certs[0][0]
+            self.dercert = dsdb.get_cert_from_db(self.nickname, pem=False)
         else:
-            nickname = self.nickname
             cadb = certs.CertDB(self.realm, host_name=self.fqdn, subject_base=self.subject_base)
 
             # FIXME, need to set this nickname in the RA plugin
             cadb.export_ca_cert('ipaCert', False)
             dsdb.create_from_cacert(cadb.cacert_fname, passwd=None)
             self.dercert = dsdb.create_server_cert(
-                nickname, self.fqdn, cadb)
+                self.nickname, self.fqdn, cadb)
             dsdb.create_pin_file()
 
         self.cacert_name = dsdb.cacert_name
 
         if self.ca_is_configured:
             dsdb.track_server_cert(
-                nickname, self.principal, dsdb.passwd_fname,
+                self.nickname, self.principal, dsdb.passwd_fname,
                 'restart_dirsrv %s' % self.serverid)
 
         conn = ipaldap.IPAdmin(self.fqdn)
@@ -670,7 +669,7 @@ class DsInstance(service.Service):
             DN(('cn', 'RSA'), ('cn', 'encryption'), ('cn', 'config')),
             objectclass=["top", "nsEncryptionModule"],
             cn=["RSA"],
-            nsSSLPersonalitySSL=[nickname],
+            nsSSLPersonalitySSL=[self.nickname],
             nsSSLToken=["internal (software)"],
             nsSSLActivation=["on"],
         )
diff --git a/ipaserver/install/installutils.py b/ipaserver/install/installutils.py
index 2c74797..97b851f 100644
--- a/ipaserver/install/installutils.py
+++ b/ipaserver/install/installutils.py
@@ -734,8 +734,6 @@ def check_pkcs12(pkcs12_info, ca_file, hostname):
 
     This is used for files given to --*_pkcs12 to ipa-server-install and
     ipa-replica-prepare.
-
-    Return a (server cert name, CA cert names) tuple
     """
     pkcs12_filename, pkcs12_passwd = pkcs12_info
     root_logger.debug('Checking PKCS#12 certificate %s', pkcs12_filename)
@@ -746,13 +744,18 @@ def check_pkcs12(pkcs12_info, ca_file, hostname):
         # Import the CA cert first so it has a known nickname
         # (if it's present in the PKCS#12 it won't be overwritten)
         ca_cert_name = 'The Root CA'
-        try:
-            nssdb.import_pem_cert(ca_cert_name, "CT,C,C", ca_file)
-        except (ValueError, RuntimeError) as e:
-            raise ScriptError(str(e))
+        if ca_file:
+            try:
+                nssdb.import_pem_cert(ca_cert_name, "CT,C,C", ca_file)
+            except (ValueError, RuntimeError) as e:
+                raise ScriptError(str(e))
 
         # Import everything in the PKCS#12
-        nssdb.import_pkcs12(pkcs12_filename, db_pwd_file.name, pkcs12_passwd)
+        try:
+            nssdb.import_pkcs12(
+                pkcs12_filename, db_pwd_file.name, pkcs12_passwd)
+        except RuntimeError as e:
+            raise ScriptError(str(e))
 
         # Check we have exactly one server cert (one with a private key)
         server_certs = nssdb.find_server_certs()
@@ -767,21 +770,23 @@ def check_pkcs12(pkcs12_info, ca_file, hostname):
 
         # Check we have the whole cert chain & the CA is in it
         trust_chain = nssdb.get_trust_chain(server_cert_name)
-        while trust_chain:
-            if trust_chain[0] == ca_cert_name:
-                break
-            trust_chain = trust_chain[1:]
-        else:
-            raise ScriptError(
-                '%s is not signed by %s, or the full certificate chain is not '
-                'present in the PKCS#12 file' % (pkcs12_filename, ca_file))
-        if len(trust_chain) != 2:
+        if len(trust_chain) < 2:
+            if ca_file:
+                raise ScriptError(
+                    '%s is not signed by %s, or the full certificate chain is '
+                    'not present in the PKCS#12 file' %
+                    (pkcs12_filename, ca_file))
+            else:
+                raise ScriptError(
+                    'The full certificate chain is not present in %s' %
+                    pkcs12_filename)
+        if ca_file and trust_chain[-2] != ca_cert_name:
             raise ScriptError(
-                'trust chain of the server certificate in %s contains %s '
-                'certificates, expected 2' %
-                (pkcs12_filename, len(trust_chain)))
+                '%s is not signed by %s' % (pkcs12_filename, ca_file))
+        ca_cert_name = trust_chain[-2]
 
         # Check server validity
+        nssdb.trust_root_cert(ca_cert_name)
         try:
             nssdb.verify_server_cert_validity(server_cert_name, hostname)
         except ValueError as e:
@@ -789,8 +794,7 @@ def check_pkcs12(pkcs12_info, ca_file, hostname):
                 'The server certificate in %s is not valid: %s' %
                 (pkcs12_filename, e))
 
-        return server_cert_name
-
+        return nssdb.get_cert(ca_cert_name)
 
 @contextmanager
 def private_ccache(path=None):
diff --git a/ipaserver/install/ipa_replica_prepare.py b/ipaserver/install/ipa_replica_prepare.py
index 81b5421..c1ced3d 100644
--- a/ipaserver/install/ipa_replica_prepare.py
+++ b/ipaserver/install/ipa_replica_prepare.py
@@ -139,7 +139,7 @@ class ReplicaPrepare(admintool.AdminTool):
                 "could not find directory instance: %s" % config_dir)
 
     def check_pkcs12(self, pkcs12_file, pkcs12_pin):
-        installutils.check_pkcs12(
+        return installutils.check_pkcs12(
             pkcs12_info=(pkcs12_file, pkcs12_pin),
             ca_file=CACERT,
             hostname=self.replica_fqdn)
@@ -221,7 +221,8 @@ class ReplicaPrepare(admintool.AdminTool):
                 if options.http_pin is None:
                     raise admintool.ScriptError(
                         "%s unlock password required" % options.http_pkcs12)
-            self.check_pkcs12(options.http_pkcs12, options.http_pin)
+            http_ca_cert = self.check_pkcs12(
+                options.http_pkcs12, options.http_pin)
 
         if options.dirsrv_pkcs12:
             if options.dirsrv_pin is None:
@@ -231,7 +232,8 @@ class ReplicaPrepare(admintool.AdminTool):
                 if options.dirsrv_pin is None:
                     raise admintool.ScriptError(
                         "%s unlock password required" % options.dirsrv_pkcs12)
-            self.check_pkcs12(options.dirsrv_pkcs12, options.dirsrv_pin)
+            dirsrv_ca_cert = self.check_pkcs12(
+                options.dirsrv_pkcs12, options.dirsrv_pin)
 
         if options.pkinit_pkcs12:
             if options.pkinit_pin is None:
@@ -242,6 +244,12 @@ class ReplicaPrepare(admintool.AdminTool):
                     raise admintool.ScriptError(
                         "%s unlock password required" % options.pkinit_pkcs12)
 
+        if (options.http_pkcs12 and options.dirsrv_pkcs12 and
+            http_ca_cert != dirsrv_ca_cert):
+            raise admintool.ScriptError(
+                "%s and %s are not signed by the same CA certificate" %
+                (options.http_pkcs12, options.dirsrv_pkcs12))
+
         if (not ipautil.file_exists(
                     dogtag.configured_constants().CS_CFG_PATH) and
                 options.dirsrv_pin is None):
diff --git a/ipaserver/install/ipa_server_certinstall.py b/ipaserver/install/ipa_server_certinstall.py
index af5d21a..6300a14 100644
--- a/ipaserver/install/ipa_server_certinstall.py
+++ b/ipaserver/install/ipa_server_certinstall.py
@@ -154,7 +154,7 @@ class ServerCertInstall(admintool.AdminTool):
         os.chown(os.path.join(dirname, 'secmod.db'), 0, pent.pw_gid)
 
     def import_cert(self, dirname, pkcs12_passwd, old_cert, principal, command):
-        server_cert = installutils.check_pkcs12(
+        installutils.check_pkcs12(
             pkcs12_info=(self.pkcs12_fname, pkcs12_passwd),
             ca_file=CACERT,
             hostname=api.env.host)
@@ -166,6 +166,7 @@ class ServerCertInstall(admintool.AdminTool):
 
             cdb.delete_cert(old_cert)
             cdb.import_pkcs12(self.pkcs12_fname, pkcs12_passwd)
+            server_cert = cdb.find_server_certs()[0][0]
 
             if api.env.enable_ra:
                 cdb.track_server_cert(server_cert, principal, cdb.passwd_fname,
-- 
1.9.3

_______________________________________________
Freeipa-devel mailing list
Freeipa-devel@redhat.com
https://www.redhat.com/mailman/listinfo/freeipa-devel

Reply via email to