flo-renaud's pull request #50: "Add cert checks in ipa-server-certinstall" was 
opened

PR body:
"""
When ipa-server-certinstall is called to install a new server certificate,
the prerequisite is that the certificate issuer must be already known by IPA.
This fix adds new checks to make sure that the tool exits before
modifying the target NSS database if it is not the case.
The fix consists in creating a temp NSS database with the CA certs from the
target NSS database + the new server cert and checking the new server cert
validity.

https://fedorahosted.org/freeipa/ticket/6263
"""

See the full pull-request at https://github.com/freeipa/freeipa/pull/50
... or pull the PR as Git branch:
git remote add ghfreeipa https://github.com/freeipa/freeipa
git fetch ghfreeipa pull/50/head:pr50
git checkout pr50
From b68d1a1c65d8c4a5ed65d2091e755ae80cdf72a6 Mon Sep 17 00:00:00 2001
From: Florence Blanc-Renaud <f...@redhat.com>
Date: Thu, 1 Sep 2016 13:56:24 +0200
Subject: [PATCH] Add cert checks in ipa-server-certinstall

When ipa-server-certinstall is called to install a new server certificate,
the prerequisite is that the certificate issuer must be already known by IPA.
This fix adds new checks to make sure that the tool exits before
modifying the target NSS database if it is not the case.
The fix consists in creating a temp NSS database with the CA certs from the
target NSS database + the new server cert and checking the new server cert
validity.

https://fedorahosted.org/freeipa/ticket/6263
---
 ipaserver/install/installutils.py           | 32 +++++++++++++++++++++++++
 ipaserver/install/ipa_server_certinstall.py | 37 +++++++++++++++++++++++++++--
 2 files changed, 67 insertions(+), 2 deletions(-)

diff --git a/ipaserver/install/installutils.py b/ipaserver/install/installutils.py
index 7578bf8..9140891 100644
--- a/ipaserver/install/installutils.py
+++ b/ipaserver/install/installutils.py
@@ -46,6 +46,7 @@
 
 from ipapython import ipautil, sysrestore, admintool, version
 from ipapython.admintool import ScriptError
+from ipapython.certdb import NSSDatabase
 from ipapython.ipa_log_manager import root_logger
 from ipalib.util import validate_hostname
 from ipapython import config
@@ -943,6 +944,37 @@ def load_pkcs12(cert_files, key_password, key_nickname, ca_cert_files,
     return out_file, out_password, ca_cert
 
 
+def get_server_cert_from_pkcs12(pkcs12_filename, pkcs12_pin):
+    """
+    Extract the server certificate from a PKCS#12 file
+
+    :param pkcs12_filename: Name of the file containing the certificates
+    :param pkcs12_pin: Password to unlock the PKCS#12 file
+    :returns: The server certificate, nickname and flags
+    """
+    # Create a temp nssdb
+    with NSSDatabase() as tempnssdb:
+        db_password = ipautil.ipa_generate_password()
+        db_pwdfile = ipautil.write_tmp_file(db_password)
+        tempnssdb.create_db(db_pwdfile.name)
+
+        # Import the pkcs12 file into the temp nssdb
+        try:
+            tempnssdb.import_pkcs12(pkcs12_filename, db_pwdfile.name,
+                pkcs12_pin)
+        except RuntimeError as e:
+            raise ScriptError(str(e))
+        server_certs = tempnssdb.find_server_certs()
+        if len(server_certs) != 1:
+            raise ScriptError("Expecting only one server certificate")
+        nickname, flags = server_certs[0]
+        try:
+            cert = tempnssdb.get_cert(nickname, False)
+        except RuntimeError as e:
+            raise ScriptError(str(e))
+        return cert, nickname, flags
+
+
 @contextmanager
 def stopped_service(service, instance_name=""):
     """
diff --git a/ipaserver/install/ipa_server_certinstall.py b/ipaserver/install/ipa_server_certinstall.py
index 0a8fb21..1ab5871 100644
--- a/ipaserver/install/ipa_server_certinstall.py
+++ b/ipaserver/install/ipa_server_certinstall.py
@@ -25,8 +25,8 @@
 
 from ipaplatform.constants import constants
 from ipaplatform.paths import paths
-from ipapython import admintool
-from ipapython.certdb import get_ca_nickname
+from ipapython import admintool, ipautil
+from ipapython.certdb import get_ca_nickname, NSSDatabase
 from ipapython.dn import DN
 from ipalib import api, errors
 from ipalib.constants import CACERT
@@ -157,6 +157,30 @@ def install_http_cert(self):
         os.chown(os.path.join(dirname, 'key3.db'), 0, pent.pw_gid)
         os.chown(os.path.join(dirname, 'secmod.db'), 0, pent.pw_gid)
 
+    def check_chain(self, pkcs12_filename, pkcs12_pin, nssdb):
+        # create a temp nssdb
+        with NSSDatabase() as tempnssdb:
+            db_password = ipautil.ipa_generate_password()
+            db_pwdfile = ipautil.write_tmp_file(db_password)
+            tempnssdb.create_db(db_pwdfile.name)
+
+            # import all the CA certs from nssdb into the temp db
+            for nickname, flags in nssdb.list_certs():
+                if 'C' in flags:
+                    cert = nssdb.get_cert_from_db(nickname)
+                    tempnssdb.add_cert(cert, nickname, flags)
+
+            # now import the server cert and check its validity
+            cert, nickname, flags = installutils.get_server_cert_from_pkcs12(
+                    pkcs12_filename, pkcs12_pin)
+            tempnssdb.add_cert(cert, nickname, flags)
+            try:
+                tempnssdb.verify_server_cert_validity(
+                    nickname, api.env.host)
+            except ValueError as e:
+                return False
+
+        return True
     def import_cert(self, dirname, pkcs12_passwd, old_cert, principal, command):
         pkcs12_file, pin, ca_cert = installutils.load_pkcs12(
             cert_files=self.args,
@@ -167,6 +191,15 @@ def import_cert(self, dirname, pkcs12_passwd, old_cert, principal, command):
 
         dirname = os.path.normpath(dirname)
         cdb = certs.CertDB(api.env.realm, nssdir=dirname)
+
+        # Check that the ca_cert is known and trusted
+        trusted = self.check_chain(pkcs12_file.name, pin, cdb)
+        if not trusted:
+            raise admintool.ScriptError(
+                "Peer's certificate issuer is not trusted. "
+                "Please run ipa-cacert-manage install and ipa-certupdate "
+                "to install the CA certificate.")
+
         try:
             ca_enabled = api.Command.ca_is_enabled()['result']
             if ca_enabled:
-- 
Manage your subscription for the Freeipa-devel mailing list:
https://www.redhat.com/mailman/listinfo/freeipa-devel
Contribute to FreeIPA: http://www.freeipa.org/page/Contribute/Code

Reply via email to