Blob Blame History Raw
From 5a479577ca778dd8f35501bba79eb97aa2087549 Mon Sep 17 00:00:00 2001
From: "Endi S. Dewata" <edewata@redhat.com>
Date: Mon, 18 Dec 2017 21:41:08 +0100
Subject: [PATCH 01/30] Fixed missing admin PKCS #12 file on external KRA/OCSP
 installation.

The deployment tool has been modified to generate a PKCS #12 file
that contains the admin certificate for KRA/OCSP installation with
external certificates.

https://pagure.io/dogtagpki/issue/2873

Change-Id: Ide6b08ba8f2121b4cdf21208c32d745534893f0f
(cherry picked from commit ffac807486d36e031c1afbcbb2b246536d4ae240)
---
 base/server/python/pki/server/deployment/scriptlets/configuration.py | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/base/server/python/pki/server/deployment/scriptlets/configuration.py b/base/server/python/pki/server/deployment/scriptlets/configuration.py
index 0fe4191..16a827d 100644
--- a/base/server/python/pki/server/deployment/scriptlets/configuration.py
+++ b/base/server/python/pki/server/deployment/scriptlets/configuration.py
@@ -1193,7 +1193,7 @@ class PkiScriptlet(pkiscriptlet.AbstractBasePkiScriptlet):
         # Cloned PKI subsystems do not return an Admin Certificate
         if not clone:
 
-            if standalone:
+            if external or standalone:
                 if not step_two:
                     # NOTE:  Do nothing for Stand-alone PKI (Step 1)
                     #        as this has already been addressed
-- 
1.8.3.1


From 6bdb3184284ecad0d532e6fd05ca9b129ae3821f Mon Sep 17 00:00:00 2001
From: Ade Lee <alee@redhat.com>
Date: Tue, 2 Jan 2018 13:24:23 -0500
Subject: [PATCH 03/30] Fix various PEP8 and pylint issues

Change-Id: I8b2b52599ab6b2d4738b748f36598319f11477c7
(cherry picked from commit e9b5fc7ef000abfd2cbdd6be6bfd4b2d015816a2)
---
 .../python/pki/server/deployment/pkihelper.py      | 50 ++++++++++++----------
 .../python/pki/server/deployment/pkimessages.py    | 25 ++++++-----
 .../python/pki/server/deployment/pkiparser.py      |  4 --
 .../server/deployment/scriptlets/configuration.py  | 12 +++---
 base/server/sbin/pkidestroy                        |  4 +-
 base/server/sbin/pkispawn                          |  4 +-
 6 files changed, 52 insertions(+), 47 deletions(-)

diff --git a/base/server/python/pki/server/deployment/pkihelper.py b/base/server/python/pki/server/deployment/pkihelper.py
index 49945b9..9c732d0 100644
--- a/base/server/python/pki/server/deployment/pkihelper.py
+++ b/base/server/python/pki/server/deployment/pkihelper.py
@@ -428,7 +428,8 @@ class ConfigurationFile:
 
         self.existing = config.str2bool(self.mdict['pki_existing'])
         self.external = config.str2bool(self.mdict['pki_external'])
-        self.external_step_one = not config.str2bool(self.mdict['pki_external_step_two'])
+        self.external_step_one = not config.str2bool(
+            self.mdict['pki_external_step_two'])
         self.external_step_two = not self.external_step_one
 
         if self.external:
@@ -496,7 +497,8 @@ class ConfigurationFile:
         # ALWAYS defined via 'pkiparser.py'
         if self.external_step_two:
             # Only allowed for External CA/KRA/OCSP, or Stand-alone PKI
-            if self.subsystem not in ['CA', 'KRA', 'OCSP'] and not self.standalone:
+            if (self.subsystem not in ['CA', 'KRA', 'OCSP'] and
+                    not self.standalone):
                 config.pki_log.error(log.PKI_EXTERNAL_STEP_TWO_UNSUPPORTED_1,
                                      self.subsystem,
                                      extra=config.PKI_INDENTATION_LEVEL_2)
@@ -557,8 +559,9 @@ class ConfigurationFile:
             # Verify existence of PKCS #12 Password (ONLY for non-HSM Clones)
             if not config.str2bool(self.mdict['pki_hsm_enable']):
 
-                # If system certificates are already provided via pki_server_pkcs12
-                # there's no need to provide pki_clone_pkcs12.
+                # If system certificates are already provided via
+                # pki_server_pkcs12, there's no need to provide
+                # pki_clone_pkcs12.
                 if not self.mdict['pki_server_pkcs12_path']:
                     self.confirm_data_exists("pki_clone_pkcs12_password")
 
@@ -680,8 +683,9 @@ class ConfigurationFile:
             # Check clone parameters for non-HSM clone
             if not config.str2bool(self.mdict['pki_hsm_enable']):
 
-                # If system certificates are already provided via pki_server_pkcs12
-                # there's no need to provide pki_clone_pkcs12.
+                # If system certificates are already provided via
+                # pki_server_pkcs12, there's no need to provide
+                # pki_clone_pkcs12.
                 if not self.mdict['pki_server_pkcs12_path']:
                     self.confirm_data_exists("pki_clone_pkcs12_path")
                     self.confirm_file_exists("pki_clone_pkcs12_path")
@@ -1045,7 +1049,7 @@ class Instance:
                 "No connection - server may still be down",
                 extra=config.PKI_INDENTATION_LEVEL_3)
             config.pki_log.debug(
-                "No connection - exception thrown: " + str(exc),
+                "No connection - exception thrown: %s", exc,
                 extra=config.PKI_INDENTATION_LEVEL_3)
             return None
 
@@ -1707,7 +1711,8 @@ class File:
 
     def substitute_deployment_params(self, line):
         """
-        Replace all occurrences of [param] in the line with the value of the deployment parameter.
+        Replace all occurrences of [param] in the line with the value of the
+        deployment parameter.
         """
 
         # find the first parameter in the line
@@ -2181,7 +2186,7 @@ class FIPS:
             with open(os.devnull, "w") as fnull:
                 output = subprocess.check_output(command, stderr=fnull,
                                                  close_fds=True)
-                if (output != "0"):
+                if output != "0":
                     # Set FIPS mode as enabled
                     self.mdict['pki_fips_mode_enabled'] = True
                     config.pki_log.info(log.PKIHELPER_FIPS_MODE_IS_ENABLED,
@@ -2214,7 +2219,7 @@ class HSM:
 
     def initialize(self):
         if config.str2bool(self.mdict['pki_hsm_enable']):
-            if (self.mdict['pki_hsm_libfile'] == config.PKI_HSM_NCIPHER_LIB):
+            if self.mdict['pki_hsm_libfile'] == config.PKI_HSM_NCIPHER_LIB:
                 self.initialize_ncipher()
         return
 
@@ -3026,8 +3031,8 @@ class KRAConnector:
                     sechost, secport)
             except Exception as e:
                 config.pki_log.error(
-                    "unable to access security domain. Continuing .. " +
-                    str(e),
+                    "unable to access security domain. Continuing .. %s ",
+                    e,
                     extra=config.PKI_INDENTATION_LEVEL_2)
                 ca_list = []
 
@@ -3849,8 +3854,7 @@ class ConfigClient:
         # Store the Administration Certificate in a file
         admin_cert_file = self.mdict['pki_client_admin_cert']
         admin_cert_bin_file = admin_cert_file + ".der"
-        self.save_admin_cert(log.PKI_CONFIG_ADMIN_CERT_SAVE_1,
-                             admin_cert, admin_cert_file,
+        self.save_admin_cert(admin_cert, admin_cert_file,
                              self.mdict['pki_subsystem_name'])
 
         # convert the cert file to binary
@@ -3983,8 +3987,9 @@ class ConfigClient:
 
     def save_admin_csr(self):
         config.pki_log.info(
-            log.PKI_CONFIG_EXTERNAL_CSR_SAVE_PKI_ADMIN_1 + " '" +
-            self.mdict['pki_admin_csr_path'] + "'", self.subsystem,
+            log.PKI_CONFIG_EXTERNAL_CSR_SAVE_PKI_ADMIN_2,
+            self.subsystem,
+            self.mdict['pki_admin_csr_path'],
             extra=config.PKI_INDENTATION_LEVEL_2)
         self.deployer.directory.create(
             os.path.dirname(self.mdict['pki_admin_csr_path']))
@@ -4005,20 +4010,20 @@ class ConfigClient:
             log.PKI_CONFIG_CDATA_REQUEST + "\n" + admin_certreq,
             extra=config.PKI_INDENTATION_LEVEL_2)
 
-    def save_admin_cert(self, message, input_data, output_file,
-                        subsystem_name):
-        config.pki_log.debug(message + " '" + output_file + "'",
+    def save_admin_cert(self, input_data, output_file, subsystem_name):
+        config.pki_log.debug(log.PKI_CONFIG_ADMIN_CERT_SAVE_2,
                              subsystem_name,
+                             output_file,
                              extra=config.PKI_INDENTATION_LEVEL_2)
         with open(output_file, "w") as f:
             f.write(input_data)
 
     def save_system_csr(self, csr, message, path, subsystem=None):
         if subsystem is not None:
-            config.pki_log.info(message + " '" + path + "'", subsystem,
+            config.pki_log.info(message, subsystem, path,
                                 extra=config.PKI_INDENTATION_LEVEL_2)
         else:
-            config.pki_log.info(message + " '" + path + "'",
+            config.pki_log.info(message, path,
                                 extra=config.PKI_INDENTATION_LEVEL_2)
         self.deployer.directory.create(os.path.dirname(path))
         with open(path, "w") as f:
@@ -4310,7 +4315,8 @@ class ConfigClient:
                 password=self.mdict['pki_client_database_password'])
 
             try:
-                data.adminCert = client_nssdb.get_cert(self.mdict['pki_admin_nickname'])
+                data.adminCert = client_nssdb.get_cert(
+                    self.mdict['pki_admin_nickname'])
                 if data.adminCert:  # already imported, return
                     return
 
diff --git a/base/server/python/pki/server/deployment/pkimessages.py b/base/server/python/pki/server/deployment/pkimessages.py
index 1158383..7f2d749 100644
--- a/base/server/python/pki/server/deployment/pkimessages.py
+++ b/base/server/python/pki/server/deployment/pkimessages.py
@@ -358,7 +358,7 @@ PKIHELPER_USER_ADD_DEFAULT_2 = "adding default UID '%s' for user '%s' . . ."
 PKIHELPER_USER_ADD_KEYERROR_1 = "KeyError:  pki_user %s"
 PKIHELPER_USER_ADD_UID_KEYERROR_1 = "KeyError:  pki_uid %s"
 
-PKI_CONFIG_ADMIN_CERT_SAVE_1 = "saving %s Admin Certificate to file:"
+PKI_CONFIG_ADMIN_CERT_SAVE_2 = "saving %s Admin Certificate to file: %s"
 PKI_CONFIG_ADMIN_CERT_ATOB_1 = "converting %s Admin Certificate to binary:"
 PKI_CONFIG_CDATA_TAG = "tag:"
 PKI_CONFIG_CDATA_CERT = "cert:"
@@ -384,16 +384,19 @@ PKI_CONFIG_EXTERNAL_CERT_LOAD_PKI_SUBSYSTEM_1 = \
 PKI_CONFIG_EXTERNAL_CERT_LOAD_PKI_AUDIT_SIGNING_1 = \
     "loading external CA signed %s Audit Signing certificate from file:"
 PKI_CONFIG_EXTERNAL_CSR_SAVE = "saving CA Signing CSR to file:"
-PKI_CONFIG_EXTERNAL_CSR_SAVE_KRA_STORAGE = "saving KRA Storage CSR to file:"
-PKI_CONFIG_EXTERNAL_CSR_SAVE_KRA_TRANSPORT = "saving KRA Transport CSR to file:"
-PKI_CONFIG_EXTERNAL_CSR_SAVE_OCSP_SIGNING = "saving OCSP Signing CSR to file:"
-PKI_CONFIG_EXTERNAL_CSR_SAVE_PKI_ADMIN_1 = "saving %s Admin CSR to file:"
-PKI_CONFIG_EXTERNAL_CSR_SAVE_PKI_AUDIT_SIGNING_1 = \
-    "saving %s Audit Signing CSR to file:"
-PKI_CONFIG_EXTERNAL_CSR_SAVE_PKI_SSLSERVER_1 = \
-    "saving %s SSL Server CSR to file:"
-PKI_CONFIG_EXTERNAL_CSR_SAVE_PKI_SUBSYSTEM_1 = \
-    "saving %s Subsystem CSR to file:"
+PKI_CONFIG_EXTERNAL_CSR_SAVE_KRA_STORAGE_1 = (
+    "saving KRA Storage CSR to file: %s")
+PKI_CONFIG_EXTERNAL_CSR_SAVE_KRA_TRANSPORT_1 = (
+    "saving KRA Transport CSR to file: %s")
+PKI_CONFIG_EXTERNAL_CSR_SAVE_OCSP_SIGNING_1 = (
+    "saving OCSP Signing CSR to file: %s")
+PKI_CONFIG_EXTERNAL_CSR_SAVE_PKI_ADMIN_2 = "saving %s Admin CSR to file: '%s'"
+PKI_CONFIG_EXTERNAL_CSR_SAVE_PKI_AUDIT_SIGNING_2 = \
+    "saving %s Audit Signing CSR to file: %s"
+PKI_CONFIG_EXTERNAL_CSR_SAVE_PKI_SSLSERVER_2 = (
+    "saving %s SSL Server CSR to file: %s")
+PKI_CONFIG_EXTERNAL_CSR_SAVE_PKI_SUBSYSTEM_2 = (
+    "saving %s Subsystem CSR to file: %s")
 PKI_CONFIG_JAVA_CONFIGURATION_EXCEPTION = \
     "Exception from Java Configuration Servlet:"
 PKI_CONFIG_RESPONSE_ADMIN_CERT = "adminCert:"
diff --git a/base/server/python/pki/server/deployment/pkiparser.py b/base/server/python/pki/server/deployment/pkiparser.py
index 8c9f1aa..18cf19d 100644
--- a/base/server/python/pki/server/deployment/pkiparser.py
+++ b/base/server/python/pki/server/deployment/pkiparser.py
@@ -617,8 +617,6 @@ class PKIConfigParser:
             self.ds_connect()
             self.ds_bind()
             self.ds_search()
-        except:
-            raise
         finally:
             self.ds_close()
 
@@ -636,8 +634,6 @@ class PKIConfigParser:
 
             except ldap.NO_SUCH_OBJECT:
                 base_dn_exists = False
-        except:
-            raise
         finally:
             self.ds_close()
         return base_dn_exists
diff --git a/base/server/python/pki/server/deployment/scriptlets/configuration.py b/base/server/python/pki/server/deployment/scriptlets/configuration.py
index 16a827d..8f7460c 100644
--- a/base/server/python/pki/server/deployment/scriptlets/configuration.py
+++ b/base/server/python/pki/server/deployment/scriptlets/configuration.py
@@ -1133,7 +1133,7 @@ class PkiScriptlet(pkiscriptlet.AbstractBasePkiScriptlet):
                     # (Step 1)
                     deployer.config_client.save_system_csr(
                         cdata['request'],
-                        log.PKI_CONFIG_EXTERNAL_CSR_SAVE_PKI_AUDIT_SIGNING_1,
+                        log.PKI_CONFIG_EXTERNAL_CSR_SAVE_PKI_AUDIT_SIGNING_2,
                         deployer.mdict['pki_audit_signing_csr_path'],
                         subsystem.name)
 
@@ -1142,7 +1142,7 @@ class PkiScriptlet(pkiscriptlet.AbstractBasePkiScriptlet):
                     # CSR (Step 1)
                     deployer.config_client.save_system_csr(
                         cdata['request'],
-                        log.PKI_CONFIG_EXTERNAL_CSR_SAVE_OCSP_SIGNING,
+                        log.PKI_CONFIG_EXTERNAL_CSR_SAVE_OCSP_SIGNING_1,
                         deployer.mdict['pki_signing_csr_path'])
 
                 elif cdata['tag'].lower() == "sslserver":
@@ -1150,7 +1150,7 @@ class PkiScriptlet(pkiscriptlet.AbstractBasePkiScriptlet):
                     # (Step 1)
                     deployer.config_client.save_system_csr(
                         cdata['request'],
-                        log.PKI_CONFIG_EXTERNAL_CSR_SAVE_PKI_SSLSERVER_1,
+                        log.PKI_CONFIG_EXTERNAL_CSR_SAVE_PKI_SSLSERVER_2,
                         deployer.mdict['pki_sslserver_csr_path'],
                         subsystem.name)
 
@@ -1159,7 +1159,7 @@ class PkiScriptlet(pkiscriptlet.AbstractBasePkiScriptlet):
                     # (Step 1)
                     deployer.config_client.save_system_csr(
                         cdata['request'],
-                        log.PKI_CONFIG_EXTERNAL_CSR_SAVE_KRA_STORAGE,
+                        log.PKI_CONFIG_EXTERNAL_CSR_SAVE_KRA_STORAGE_1,
                         deployer.mdict['pki_storage_csr_path'])
 
                 elif cdata['tag'].lower() == "subsystem":
@@ -1167,7 +1167,7 @@ class PkiScriptlet(pkiscriptlet.AbstractBasePkiScriptlet):
                     # (Step 1)
                     deployer.config_client.save_system_csr(
                         cdata['request'],
-                        log.PKI_CONFIG_EXTERNAL_CSR_SAVE_PKI_SUBSYSTEM_1,
+                        log.PKI_CONFIG_EXTERNAL_CSR_SAVE_PKI_SUBSYSTEM_2,
                         deployer.mdict['pki_subsystem_csr_path'],
                         subsystem.name)
 
@@ -1176,7 +1176,7 @@ class PkiScriptlet(pkiscriptlet.AbstractBasePkiScriptlet):
                     # (Step 1)
                     deployer.config_client.save_system_csr(
                         cdata['request'],
-                        log.PKI_CONFIG_EXTERNAL_CSR_SAVE_KRA_TRANSPORT,
+                        log.PKI_CONFIG_EXTERNAL_CSR_SAVE_KRA_TRANSPORT_1,
                         deployer.mdict['pki_transport_csr_path'])
 
             else:
diff --git a/base/server/sbin/pkidestroy b/base/server/sbin/pkidestroy
index 4a0c6a0..036c5ef 100755
--- a/base/server/sbin/pkidestroy
+++ b/base/server/sbin/pkidestroy
@@ -255,9 +255,9 @@ def main(argv):
 def log_error_details():
     e_type, e_value, e_stacktrace = sys.exc_info()
     config.pki_log.debug(
-        "Error Type: " + e_type.__name__, extra=config.PKI_INDENTATION_LEVEL_2)
+        "Error Type: %s", e_type.__name__, extra=config.PKI_INDENTATION_LEVEL_2)
     config.pki_log.debug(
-        "Error Message: " + str(e_value), extra=config.PKI_INDENTATION_LEVEL_2)
+        "Error Message: %s", e_value, extra=config.PKI_INDENTATION_LEVEL_2)
     stacktrace_list = traceback.format_list(traceback.extract_tb(e_stacktrace))
     e_stacktrace = ""
     for l in stacktrace_list:
diff --git a/base/server/sbin/pkispawn b/base/server/sbin/pkispawn
index 5489622..a715efc 100755
--- a/base/server/sbin/pkispawn
+++ b/base/server/sbin/pkispawn
@@ -864,9 +864,9 @@ def print_final_install_information(mdict):
 def log_error_details():
     e_type, e_value, e_stacktrace = sys.exc_info()
     config.pki_log.debug(
-        "Error Type: " + e_type.__name__, extra=config.PKI_INDENTATION_LEVEL_2)
+        "Error Type: %s", e_type.__name__, extra=config.PKI_INDENTATION_LEVEL_2)
     config.pki_log.debug(
-        "Error Message: " + str(e_value), extra=config.PKI_INDENTATION_LEVEL_2)
+        "Error Message: %s", e_value, extra=config.PKI_INDENTATION_LEVEL_2)
     stacktrace_list = traceback.format_list(traceback.extract_tb(e_stacktrace))
     e_stacktrace = ""
     for l in stacktrace_list:
-- 
1.8.3.1


From ae39e98d64fa5ca7e92e8fc378be400c5160cbaf Mon Sep 17 00:00:00 2001
From: Ade Lee <alee@redhat.com>
Date: Tue, 2 Jan 2018 13:38:40 -0500
Subject: [PATCH 04/30] Modified systemd invocations in pkispawn to handle
 nuxwdog

The systemd invocations in pkispawn/pkidestroy did not account for
nuxwdog enabled instances.  This patch allows pkispawn/pkidestroy to
use the right service name if the nuxwdog service unit files exist.

Also modified instance_layout deployment script to delete the right
systemd link.

Change-Id: I25eac0555aad022784d7728913ae4a335eab3463
(cherry picked from commit c7c907c07599ef1d9b52638c25153f7bd82de999)
---
 .../python/pki/server/deployment/pkihelper.py      | 55 ++++++++++------------
 .../deployment/scriptlets/instance_layout.py       |  5 +-
 2 files changed, 28 insertions(+), 32 deletions(-)

diff --git a/base/server/python/pki/server/deployment/pkihelper.py b/base/server/python/pki/server/deployment/pkihelper.py
index 9c732d0..3736782 100644
--- a/base/server/python/pki/server/deployment/pkihelper.py
+++ b/base/server/python/pki/server/deployment/pkihelper.py
@@ -3491,17 +3491,28 @@ class Systemd(object):
 
         Args:
           deployer (dictionary):  PKI Deployment name/value parameters
-
-        Attributes:
-
-        Returns:
-
-        Raises:
-
-        Examples:
-
         """
         self.mdict = deployer.mdict
+        instance_name = deployer.mdict['pki_instance_name']
+
+        unit_file = 'pki-tomcatd@%s.service' % instance_name
+        systemd_link = os.path.join(
+            '/etc/systemd/system/pki-tomcatd.target.wants',
+            unit_file)
+
+        nuxwdog_unit_file = 'pki-tomcatd-nuxwdog@%s.service' % instance_name
+        nuxwdog_systemd_link = os.path.join(
+            '/etc/systemd/system/pki-tomcatd-nuxwdog.target.wants',
+            nuxwdog_unit_file)
+
+        if os.path.exists(nuxwdog_systemd_link):
+            self.is_nuxwdog_enabled = True
+            self.service_name = nuxwdog_unit_file
+            self.systemd_link = nuxwdog_systemd_link
+        else:
+            self.is_nuxwdog_enabled = False
+            self.service_name = unit_file
+            self.systemd_link = systemd_link
 
     def daemon_reload(self, critical_failure=True):
         """PKI Deployment execution management lifecycle function.
@@ -3575,7 +3586,7 @@ class Systemd(object):
                 command = ["rm", "/etc/rc3.d/*" +
                            self.mdict['pki_instance_name']]
             else:
-                command = ["systemctl", "disable", "pki-tomcatd.target"]
+                command = ["systemctl", "disable", self.service_name]
 
             # Display this "systemd" execution managment command
             config.pki_log.info(
@@ -3625,7 +3636,7 @@ class Systemd(object):
                 command = ["ln", "-s", "/etc/init.d/pki-tomcatd",
                            "/etc/rc3.d/S89" + self.mdict['pki_instance_name']]
             else:
-                command = ["systemctl", "enable", "pki-tomcatd.target"]
+                command = ["systemctl", "enable", self.service_name]
 
             # Display this "systemd" execution managment command
             config.pki_log.info(
@@ -3669,20 +3680,15 @@ class Systemd(object):
 
         """
         try:
-            service = None
             # Execute the "systemd daemon-reload" management lifecycle command
             if reload_daemon:
                 self.daemon_reload(critical_failure)
-            # Compose this "systemd" execution management command
-            service = "pki-tomcatd" + "@" +\
-                      self.mdict['pki_instance_name'] + "." +\
-                      "service"
 
             if pki.system.SYSTEM_TYPE == "debian":
                 command = ["/etc/init.d/pki-tomcatd", "start",
                            self.mdict['pki_instance_name']]
             else:
-                command = ["systemctl", "start", service]
+                command = ["systemctl", "start", self.service_name]
 
             # Display this "systemd" execution managment command
             config.pki_log.info(
@@ -3722,17 +3728,11 @@ class Systemd(object):
 
         """
         try:
-            service = None
-            # Compose this "systemd" execution management command
-            service = "pki-tomcatd" + "@" +\
-                      self.mdict['pki_instance_name'] + "." +\
-                      "service"
-
             if pki.system.SYSTEM_TYPE == "debian":
                 command = ["/etc/init.d/pki-tomcatd", "stop",
                            self.mdict['pki_instance_name']]
             else:
-                command = ["systemctl", "stop", service]
+                command = ["systemctl", "stop", self.service_name]
 
             # Display this "systemd" execution managment command
             config.pki_log.info(
@@ -3773,21 +3773,16 @@ class Systemd(object):
 
         """
         try:
-            service = None
             # Compose this "systemd" execution management command
             # Execute the "systemd daemon-reload" management lifecycle command
             if reload_daemon:
                 self.daemon_reload(critical_failure)
 
-            service = "pki-tomcatd" + "@" +\
-                      self.mdict['pki_instance_name'] + "." +\
-                      "service"
-
             if pki.system.SYSTEM_TYPE == "debian":
                 command = ["/etc/init.d/pki-tomcatd", "restart",
                            self.mdict['pki_instance_name']]
             else:
-                command = ["systemctl", "restart", service]
+                command = ["systemctl", "restart", self.service_name]
 
             # Display this "systemd" execution managment command
             config.pki_log.info(
diff --git a/base/server/python/pki/server/deployment/scriptlets/instance_layout.py b/base/server/python/pki/server/deployment/scriptlets/instance_layout.py
index fc08fe1..15c0441 100644
--- a/base/server/python/pki/server/deployment/scriptlets/instance_layout.py
+++ b/base/server/python/pki/server/deployment/scriptlets/instance_layout.py
@@ -59,7 +59,8 @@ class PkiScriptlet(pkiscriptlet.AbstractBasePkiScriptlet):
         # Link /etc/pki/<instance>/logging.properties
         # to /usr/share/pki/server/conf/logging.properties.
         deployer.symlink.create(
-            os.path.join(deployer.mdict['pki_source_server_path'], "logging.properties"),
+            os.path.join(deployer.mdict['pki_source_server_path'],
+                         "logging.properties"),
             os.path.join(deployer.mdict['pki_instance_configuration_path'],
                          "logging.properties"))
 
@@ -176,7 +177,7 @@ class PkiScriptlet(pkiscriptlet.AbstractBasePkiScriptlet):
             return
 
         # remove Tomcat instance systemd service link
-        deployer.symlink.delete(deployer.mdict['pki_systemd_service_link'])
+        deployer.symlink.delete(deployer.systemd.systemd_link)
 
         # remove Tomcat instance base
         deployer.directory.delete(deployer.mdict['pki_instance_path'])
-- 
1.8.3.1


From cf50a28a14908be726cefb1e7e79233a2b13a575 Mon Sep 17 00:00:00 2001
From: Ade Lee <alee@redhat.com>
Date: Tue, 2 Jan 2018 14:52:32 -0500
Subject: [PATCH 05/30] Allow prompting for token passwords if not present

Change-Id: Ifa2e60424d713ebe15bf9aa92f1d5b7691b7e0ff
(cherry picked from commit 6716b82ecc38b23de81c8f0fe18863e1df4bfddb)
---
 .../python/pki/server/deployment/__init__.py       |  4 +++-
 .../python/pki/server/deployment/pkihelper.py      | 27 ++++++++--------------
 base/server/sbin/pkidestroy                        |  2 +-
 base/server/sbin/pkispawn                          |  2 +-
 4 files changed, 15 insertions(+), 20 deletions(-)

diff --git a/base/server/python/pki/server/deployment/__init__.py b/base/server/python/pki/server/deployment/__init__.py
index 70e9c9b..dbacc57 100644
--- a/base/server/python/pki/server/deployment/__init__.py
+++ b/base/server/python/pki/server/deployment/__init__.py
@@ -66,6 +66,7 @@ class PKIDeployer:
         self.systemd = None
         self.tps_connector = None
         self.config_client = None
+        self.parser = None
 
         # Set installation time
         ticks = time.time()
@@ -88,7 +89,7 @@ class PKIDeployer:
         if not len(self.dns_domainname):
             self.dns_domainname = self.hostname
 
-    def init(self):
+    def init(self, parser):
 
         # Utility objects
         self.identity = util.Identity(self)
@@ -111,6 +112,7 @@ class PKIDeployer:
         self.systemd = util.Systemd(self)
         self.tps_connector = util.TPSConnector(self)
         self.config_client = util.ConfigClient(self)
+        self.parser = parser
 
     def flatten_master_dict(self):
 
diff --git a/base/server/python/pki/server/deployment/pkihelper.py b/base/server/python/pki/server/deployment/pkihelper.py
index 3736782..26aca14 100644
--- a/base/server/python/pki/server/deployment/pkihelper.py
+++ b/base/server/python/pki/server/deployment/pkihelper.py
@@ -2053,6 +2053,7 @@ class Password:
 
     def __init__(self, deployer):
         self.mdict = deployer.mdict
+        self.deployer = deployer
 
     def create_password_conf(self, path, pin, pin_sans_token=False,
                              overwrite_flag=False, critical_failure=True):
@@ -2142,7 +2143,7 @@ class Password:
                 raise
         return
 
-    def get_password(self, path, token_name, critical_failure=True):
+    def get_password(self, path, token_name):
         token_pwd = None
         if os.path.exists(path) and os.path.isfile(path) and\
            os.access(path, os.R_OK):
@@ -2155,16 +2156,11 @@ class Password:
                 token_pwd = tokens[token_name]
 
         if token_pwd is None or token_pwd == '':
-            # TODO prompt for this password
-            config.pki_log.error(log.PKIHELPER_PASSWORD_NOT_FOUND_1,
-                                 token_name,
-                                 extra=config.PKI_INDENTATION_LEVEL_2)
-            if critical_failure:
-                raise Exception(
-                    log.PKIHELPER_PASSWORD_NOT_FOUND_1 %
-                    token_name)
-            else:
-                return
+            self.deployer.parser.read_password(
+                'Password for token {}'.format(token_name),
+                self.deployer.subsystem_name,
+                'token_pwd')
+            token_pwd = self.mdict['token_pwd']
         return token_pwd
 
 
@@ -2994,8 +2990,7 @@ class KRAConnector:
 
             token_pwd = self.password.get_password(
                 self.mdict['pki_shared_password_conf'],
-                token_name,
-                critical_failure)
+                token_name)
 
             if token_pwd is None or token_pwd == '':
                 config.pki_log.warning(
@@ -3199,8 +3194,7 @@ class TPSConnector:
 
             token_pwd = self.password.get_password(
                 self.mdict['pki_shared_password_conf'],
-                token_name,
-                critical_failure)
+                token_name)
 
             if token_pwd is None or token_pwd == '':
                 config.pki_log.warning(
@@ -3437,8 +3431,7 @@ class SecurityDomain:
 
         token_pwd = self.password.get_password(
             self.mdict['pki_shared_password_conf'],
-            token_name,
-            critical_failure)
+            token_name)
 
         if token_pwd is None or token_pwd == '':
             config.pki_log.warning(
diff --git a/base/server/sbin/pkidestroy b/base/server/sbin/pkidestroy
index 036c5ef..0b6ef6b 100755
--- a/base/server/sbin/pkidestroy
+++ b/base/server/sbin/pkidestroy
@@ -227,7 +227,7 @@ def main(argv):
 
     # Process the various "scriptlets" to remove the specified PKI subsystem.
     pki_subsystem_scriptlets = parser.mdict['destroy_scriplets'].split()
-    deployer.init()
+    deployer.init(parser)
 
     try:
         for scriptlet_name in pki_subsystem_scriptlets:
diff --git a/base/server/sbin/pkispawn b/base/server/sbin/pkispawn
index a715efc..d671ba7 100755
--- a/base/server/sbin/pkispawn
+++ b/base/server/sbin/pkispawn
@@ -519,7 +519,7 @@ def main(argv):
 
     # Process the various "scriptlets" to create the specified PKI subsystem.
     pki_subsystem_scriptlets = parser.mdict['spawn_scriplets'].split()
-    deployer.init()
+    deployer.init(parser)
 
     try:
         for scriptlet_name in pki_subsystem_scriptlets:
-- 
1.8.3.1


From af8c909653fae1d02dc801021ad73a43b548aacf Mon Sep 17 00:00:00 2001
From: "Endi S. Dewata" <edewata@redhat.com>
Date: Tue, 9 Jan 2018 21:29:39 -0600
Subject: [PATCH 06/30] Added pki-server <subsystem>-audit-event-find command.

A new pki-server <subsystem>-audit-event-find command has been
added to list audit events and their attributes (e.g. filter).
Currently the command can only list enabled events.

https://pagure.io/dogtagpki/issue/2656

Change-Id: I7319ac4e449045d7456e9ae225aca58075093bcd
(cherry picked from commit 9f3a7d6c3a190a2e937ffb3586d6c9d72fe9d2f1)
---
 base/server/python/pki/server/__init__.py  | 20 +++++++
 base/server/python/pki/server/cli/audit.py | 83 ++++++++++++++++++++++++++++++
 2 files changed, 103 insertions(+)

diff --git a/base/server/python/pki/server/__init__.py b/base/server/python/pki/server/__init__.py
index 9969dcc..bb01dcf 100644
--- a/base/server/python/pki/server/__init__.py
+++ b/base/server/python/pki/server/__init__.py
@@ -419,6 +419,26 @@ class PKISubsystem(object):
 
         pki.util.customize_file(input_file, output_file, params)
 
+    def find_audit_events(self, enabled=None):
+
+        if not enabled:
+            raise Exception('This operation is not yet supported. Specify --enabled True.')
+
+        events = []
+
+        names = self.config['log.instance.SignedAudit.events'].split(',')
+        names = list(map(str.strip, names))
+        names.sort()
+
+        for name in names:
+            event = {}
+            event['name'] = name
+            event['enabled'] = True
+            event['filter'] = self.config.get('log.instance.SignedAudit.filters.%s' % name)
+            events.append(event)
+
+        return events
+
     def get_audit_log_dir(self):
 
         current_file_path = self.config['log.instance.SignedAudit.fileName']
diff --git a/base/server/python/pki/server/cli/audit.py b/base/server/python/pki/server/cli/audit.py
index 665fe91..bbbdd10 100644
--- a/base/server/python/pki/server/cli/audit.py
+++ b/base/server/python/pki/server/cli/audit.py
@@ -37,10 +37,93 @@ class AuditCLI(pki.cli.CLI):
             'audit', 'Audit management commands')
 
         self.parent = parent
+        self.add_module(AuditEventFindCLI(self))
         self.add_module(AuditFileFindCLI(self))
         self.add_module(AuditFileVerifyCLI(self))
 
 
+class AuditEventFindCLI(pki.cli.CLI):
+
+    def __init__(self, parent):
+        super(AuditEventFindCLI, self).__init__(
+            'event-find', 'Find audit event configurations')
+
+        self.parent = parent
+
+    def print_help(self):
+        print('Usage: pki-server %s-audit-event-find [OPTIONS]' % self.parent.parent.name)
+        print()
+        print('  -i, --instance <instance ID>       Instance ID (default: pki-tomcat).')
+        print('      --enabled <True|False>         Show enabled/disabled events only.')
+        print('  -v, --verbose                      Run in verbose mode.')
+        print('      --help                         Show help message.')
+        print()
+
+    def execute(self, argv):
+
+        try:
+            opts, _ = getopt.gnu_getopt(argv, 'i:v', [
+                'instance=',
+                'enabled=',
+                'verbose', 'help'])
+
+        except getopt.GetoptError as e:
+            print('ERROR: ' + str(e))
+            self.print_help()
+            sys.exit(1)
+
+        instance_name = 'pki-tomcat'
+        enabled = None
+
+        for o, a in opts:
+            if o in ('-i', '--instance'):
+                instance_name = a
+
+            elif o == '--enabled':
+                enabled = a == 'True'
+
+            elif o in ('-v', '--verbose'):
+                self.set_verbose(True)
+
+            elif o == '--help':
+                self.print_help()
+                sys.exit()
+
+            else:
+                print('ERROR: unknown option ' + o)
+                self.print_help()
+                sys.exit(1)
+
+        instance = pki.server.PKIInstance(instance_name)
+        if not instance.is_valid():
+            print('ERROR: Invalid instance %s.' % instance_name)
+            sys.exit(1)
+
+        instance.load()
+
+        subsystem_name = self.parent.parent.name
+        subsystem = instance.get_subsystem(subsystem_name)
+        if not subsystem:
+            print('ERROR: No %s subsystem in instance %s.'
+                  % (subsystem_name.upper(), instance_name))
+            sys.exit(1)
+
+        events = subsystem.find_audit_events(enabled)
+
+        self.print_message('%s entries matched' % len(events))
+
+        first = True
+        for event in events:
+            if first:
+                first = False
+            else:
+                print()
+
+            print('  Event Name: %s' % event.get('name'))
+            print('  Enabled: %s' % event.get('enabled'))
+            print('  Filter: %s' % event.get('filter'))
+
+
 class AuditFileFindCLI(pki.cli.CLI):
 
     def __init__(self, parent):
-- 
1.8.3.1


From 2942a723169fbd2d46605dc847ca702accf0c79e Mon Sep 17 00:00:00 2001
From: Christina Fu <cfu@redhat.com>
Date: Wed, 20 Dec 2017 14:08:58 -0800
Subject: [PATCH 07/30] Ticket #2604 adding FIPS support-RFE: shared token
 storage and retrieval mechanism

This patch adds FIPS support to the original ticket 2604.  Two changes were
made:
1. in CMCSharedToken tool, "-p" is used to specify the password for token login
and "-s" is used to specify the shared secret (or passphrase)
2. on the server side, in SharedSecret, an existing configuration parameter, cmc.token is utilized for admin to specify
the token where the issuance protection cert's private key resides on.

Change-Id: Ia454598bca7843bfc0a6ad21f57f6a74d05d67fe
(cherry picked from commit fc92dbafb9051ac7d1019924b62e356eb17bd0f1)
---
 .../src/com/netscape/cmstools/CMCSharedToken.java  | 19 ++++++++++++----
 .../netscape/cms/authentication/SharedSecret.java  | 25 +++++++++++-----------
 2 files changed, 27 insertions(+), 17 deletions(-)

diff --git a/base/java-tools/src/com/netscape/cmstools/CMCSharedToken.java b/base/java-tools/src/com/netscape/cmstools/CMCSharedToken.java
index d16dd0c..f52199a 100644
--- a/base/java-tools/src/com/netscape/cmstools/CMCSharedToken.java
+++ b/base/java-tools/src/com/netscape/cmstools/CMCSharedToken.java
@@ -38,6 +38,7 @@ import org.mozilla.jss.crypto.ObjectNotFoundException;
 import org.mozilla.jss.crypto.PrivateKey;
 import org.mozilla.jss.crypto.SymmetricKey;
 import org.mozilla.jss.crypto.X509Certificate;
+import org.mozilla.jss.util.Password;
 
 import com.netscape.cmsutil.crypto.CryptoUtil;
 import com.netscape.cmsutil.util.Cert;
@@ -77,7 +78,11 @@ public class CMCSharedToken {
         option.setArgName("output");
         options.addOption(option);
 
-        option = new Option("p", true, "passphrase");
+        option = new Option("p", true, "password");
+        option.setArgName("password");
+        options.addOption(option);
+
+        option = new Option("s", true, "passphrase");
         option.setArgName("passphrase");
         options.addOption(option);
 
@@ -104,7 +109,8 @@ public class CMCSharedToken {
         System.out.println("Options:");
         System.out.println("  -d <database>                Security database location (default: current directory)");
         System.out.println("  -h <token>                   Security token name (default: internal)");
-        System.out.println("  -p <passphrase>              CMC enrollment passphrase (put in \"\" if containing spaces)");
+        System.out.println("  -p <password>                Security token password");
+        System.out.println("  -s <passphrase>              CMC enrollment passphrase (shared secret) (put in \"\" if containing spaces)");
         System.out.println("     Use either -b OR -n below");
         System.out.println("  -b <issuance protection cert>          PEM issuance protection certificate");
         System.out.println("  -n <issuance protection cert nickname>          issuance protection certificate nickname");
@@ -177,7 +183,7 @@ public class CMCSharedToken {
         boolean verbose = cmd.hasOption("v");
 
         String databaseDir = cmd.getOptionValue("d", ".");
-        String passphrase = cmd.getOptionValue("p");
+        String passphrase = cmd.getOptionValue("s");
         if (passphrase == null) {
             printError("Missing passphrase");
             System.exit(1);
@@ -188,6 +194,7 @@ public class CMCSharedToken {
             System.out.println(Arrays.toString(passphrase.getBytes("UTF-8")));
         }
         String tokenName = cmd.getOptionValue("h");
+        String tokenPassword = cmd.getOptionValue("p");
 
         String issuanceProtCertFilename = cmd.getOptionValue("b");
         String issuanceProtCertNick = cmd.getOptionValue("n");
@@ -201,6 +208,10 @@ public class CMCSharedToken {
             CryptoToken token = CryptoUtil.getKeyStorageToken(tokenName);
             tokenName = token.getName();
             manager.setThreadToken(token);
+
+            Password password = new Password(tokenPassword.toCharArray());
+            token.login(password);
+
             X509Certificate  issuanceProtCert = null;
             if (issuanceProtCertFilename != null) {
                 if (verbose) System.out.println("Loading issuance protection certificate");
@@ -295,7 +306,7 @@ public class CMCSharedToken {
 
                 SymmetricKey ver_session = CryptoUtil.unwrap(token,  SymmetricKey.AES, 128, SymmetricKey.Usage.UNWRAP, wrappingKey, wrapped_session, wrapAlgorithm);
                 byte[] ver_passphrase = CryptoUtil.decryptUsingSymmetricKey(token, new IVParameterSpec(iv), wrapped_passphrase,
-                ver_session, EncryptionAlgorithm.AES_128_CBC_PAD);
+                ver_session, encryptAlgorithm);
 
                 String ver_spassphrase = new String(ver_passphrase, "UTF-8");
 
diff --git a/base/server/cms/src/com/netscape/cms/authentication/SharedSecret.java b/base/server/cms/src/com/netscape/cms/authentication/SharedSecret.java
index ee7a7d7..1a3d877 100644
--- a/base/server/cms/src/com/netscape/cms/authentication/SharedSecret.java
+++ b/base/server/cms/src/com/netscape/cms/authentication/SharedSecret.java
@@ -21,7 +21,6 @@ import java.math.BigInteger;
 // ldap java sdk
 import java.util.Enumeration;
 
-import org.mozilla.jss.CryptoManager;
 import org.mozilla.jss.crypto.CryptoToken;
 import org.mozilla.jss.crypto.EncryptionAlgorithm;
 import org.mozilla.jss.crypto.IVParameterSpec;
@@ -143,8 +142,7 @@ public class SharedSecret extends DirBasedAuthentication
     private IConfigStore shrTokLdapConfigStore = null;
 
     private PrivateKey issuanceProtPrivKey = null;
-    protected CryptoManager cm = null;
-    protected CryptoToken tmpToken = null;
+    protected CryptoToken token = null;
     protected byte iv[] = { 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1 };
     EncryptionAlgorithm encryptAlgorithm = EncryptionAlgorithm.AES_128_CBC_PAD;
     ICertificateRepository certRepository = null;
@@ -193,15 +191,16 @@ public class SharedSecret extends DirBasedAuthentication
         }
 
         try {
-            cm = CryptoManager.getInstance();
+            String tokenName =
+                    CMS.getConfigStore().getString("cmc.token", CryptoUtil.INTERNAL_TOKEN_NAME);
+            CMS.debug(method + "getting token :" + tokenName);
+            token = CryptoUtil.getKeyStorageToken(tokenName);
         } catch (Exception e) {
-            msg = method + e.toString();
-            CMS.debug(msg);
-            throw new EBaseException(msg);
+            CMS.debug(method + e);
+            throw new EBaseException(e);
         }
-        tmpToken = cm.getInternalKeyStorageToken();
-        if (tmpToken == null) {
-            msg = method + "tmpToken null";
+        if (token == null) {
+            msg = method + "token null";
             CMS.debug(msg);
             throw new EBaseException(msg);
         }
@@ -355,11 +354,11 @@ public class SharedSecret extends DirBasedAuthentication
             byte wrapped_passphrase[] = wrapped_dPassphrase.getOctetString();
             CMS.debug(method + "wrapped passphrase retrieved");
 
-            SymmetricKey ver_session = CryptoUtil.unwrap(tmpToken, SymmetricKey.AES, 128, SymmetricKey.Usage.UNWRAP,
+            SymmetricKey ver_session = CryptoUtil.unwrap(token, SymmetricKey.AES, 128, SymmetricKey.Usage.UNWRAP,
                     issuanceProtPrivKey, wrapped_session, wrapAlgorithm);
-            ver_passphrase = CryptoUtil.decryptUsingSymmetricKey(tmpToken, new IVParameterSpec(iv),
+            ver_passphrase = CryptoUtil.decryptUsingSymmetricKey(token, new IVParameterSpec(iv),
                     wrapped_passphrase,
-                    ver_session, EncryptionAlgorithm.AES_128_CBC_PAD);
+                    ver_session, encryptAlgorithm);
 
             char[] ver_spassphraseChars = CryptoUtil.bytesToChars(ver_passphrase);
             return ver_spassphraseChars;
-- 
1.8.3.1


From 3320b3942eb27e96169bee1289af648262258ec9 Mon Sep 17 00:00:00 2001
From: "Endi S. Dewata" <edewata@redhat.com>
Date: Tue, 9 Jan 2018 21:41:13 -0600
Subject: [PATCH 08/30] Merged CMC_USER_SIGNED_REQUEST_SIG_VERIFY events.

The CMC_USER_SIGNED_REQUEST_SIG_VERIFY_SUCCESS and
CMC_USER_SIGNED_REQUEST_SIG_VERIFY_FAILURE events have
been merged into CMC_USER_SIGNED_REQUEST_SIG_VERIFY event,
and encapsulated using CMCUserSignedRequestSigVerifyEvent
class.

https://pagure.io/dogtagpki/issue/2656

Change-Id: I85ec9c871526da9ca8711ebcd6c9281086e2199f
(cherry picked from commit b142b035b86d7c47818c21def377ff6d6ba903c8)
(cherry picked from commit 21c56574c2aaf9e96f50a0807bc81bc0c285b238)
---
 base/ca/shared/conf/CS.cfg                         |  4 +-
 .../com/netscape/certsrv/logging/AuditEvent.java   |  4 --
 .../event/CMCUserSignedRequestSigVerifyEvent.java  | 74 ++++++++++++++++++++++
 .../cms/authentication/CMCUserSignedAuth.java      | 50 ++++++---------
 base/server/cmsbundle/src/LogMessages.properties   |  4 +-
 5 files changed, 97 insertions(+), 39 deletions(-)
 create mode 100644 base/common/src/com/netscape/certsrv/logging/event/CMCUserSignedRequestSigVerifyEvent.java

diff --git a/base/ca/shared/conf/CS.cfg b/base/ca/shared/conf/CS.cfg
index aa9bf5a..908407b 100644
--- a/base/ca/shared/conf/CS.cfg
+++ b/base/ca/shared/conf/CS.cfg
@@ -905,11 +905,11 @@ log.instance.SignedAudit._001=## Signed Audit Logging
 log.instance.SignedAudit._002=##
 log.instance.SignedAudit._003=##
 log.instance.SignedAudit._004=## Available Audit events:
-log.instance.SignedAudit._005=## AUDIT_LOG_STARTUP,AUDIT_LOG_SHUTDOWN,ROLE_ASSUME,CONFIG_CERT_POLICY,CONFIG_CERT_PROFILE,CONFIG_CRL_PROFILE,CONFIG_OCSP_PROFILE,CONFIG_AUTH,CONFIG_ROLE,CONFIG_ACL,CONFIG_SIGNED_AUDIT,CONFIG_ENCRYPTION,CONFIG_TRUSTED_PUBLIC_KEY,CONFIG_DRM,SELFTESTS_EXECUTION,AUDIT_LOG_DELETE,LOG_PATH_CHANGE,LOG_EXPIRATION_CHANGE,PRIVATE_KEY_ARCHIVE_REQUEST,PRIVATE_KEY_ARCHIVE_REQUEST_PROCESSED,PRIVATE_KEY_EXPORT_REQUEST_PROCESSED_SUCCESS,PRIVATE_KEY_EXPORT_REQUEST_PROCESSED_FAILURE,KEY_RECOVERY_REQUEST,KEY_RECOVERY_REQUEST_ASYNC,KEY_RECOVERY_AGENT_LOGIN,KEY_RECOVERY_REQUEST_PROCESSED,KEY_RECOVERY_REQUEST_PROCESSED_ASYNC,KEY_GEN_ASYMMETRIC,CERT_SIGNING_INFO,OCSP_SIGNING_INFO,CRL_SIGNING_INFO,NON_PROFILE_CERT_REQUEST,PROFILE_CERT_REQUEST,CERT_REQUEST_PROCESSED,CERT_STATUS_CHANGE_REQUEST,CERT_STATUS_CHANGE_REQUEST_PROCESSED,AUTHZ,INTER_BOUNDARY,AUTH,CERT_PROFILE_APPROVAL,PROOF_OF_POSSESSION,CMC_PROOF_OF_IDENTIFICATION,CMC_ID_POP_LINK_WITNESS,SCHEDULE_CRL_GENERATION,DELTA_CRL_GENERATION,DELTA_CRL_PUBLISHING,FULL_CRL_GENERATION,FULL_CRL_PUBLISHING,CRL_RETRIEVAL,CRL_VALIDATION,CMC_SIGNED_REQUEST_SIG_VERIFY,CMC_USER_SIGNED_REQUEST_SIG_VERIFY_SUCCESS,CMC_USER_SIGNED_REQUEST_SIG_VERIFY_FAILURE,SERVER_SIDE_KEYGEN_REQUEST_PROCESSED_FAILURE,SERVER_SIDE_KEYGEN_REQUEST_PROCESSED_SUCCESS,SERVER_SIDE_KEYGEN_REQUEST,COMPUTE_SESSION_KEY_REQUEST,COMPUTE_SESSION_KEY_REQUEST_PROCESSED_SUCCESS, COMPUTE_SESSION_KEY_REQUEST_PROCESSED_FAILURE,DIVERSIFY_KEY_REQUEST,DIVERSIFY_KEY_REQUEST_PROCESSED_SUCCESS, DIVERSIFY_KEY_REQUEST_PROCESSED_FAILURE,ENCRYPT_DATA_REQUEST,ENCRYPT_DATA_REQUEST_PROCESSED_SUCCESS,ENCRYPT_DATA_REQUEST_PROCESSED_FAILURE,OCSP_GENERATION,COMPUTE_RANDOM_DATA_REQUEST,COMPUTE_RANDOM_DATA_REQUEST_PROCESSED_SUCCESS,COMPUTE_RANDOM_DATA_REQUEST_PROCESSED_FAILURE,CIMC_CERT_VERIFICATION,SECURITY_DOMAIN_UPDATE,CONFIG_SERIAL_NUMBER,AUTHORITY_CONFIG,ACCESS_SESSION_ESTABLISH,ACCESS_SESSION_TERMINATED,SECURITY_DATA_ARCHIVAL_REQUEST,RANDOM_GENERATION
+log.instance.SignedAudit._005=## AUDIT_LOG_STARTUP,AUDIT_LOG_SHUTDOWN,ROLE_ASSUME,CONFIG_CERT_POLICY,CONFIG_CERT_PROFILE,CONFIG_CRL_PROFILE,CONFIG_OCSP_PROFILE,CONFIG_AUTH,CONFIG_ROLE,CONFIG_ACL,CONFIG_SIGNED_AUDIT,CONFIG_ENCRYPTION,CONFIG_TRUSTED_PUBLIC_KEY,CONFIG_DRM,SELFTESTS_EXECUTION,AUDIT_LOG_DELETE,LOG_PATH_CHANGE,LOG_EXPIRATION_CHANGE,PRIVATE_KEY_ARCHIVE_REQUEST,PRIVATE_KEY_ARCHIVE_REQUEST_PROCESSED,PRIVATE_KEY_EXPORT_REQUEST_PROCESSED_SUCCESS,PRIVATE_KEY_EXPORT_REQUEST_PROCESSED_FAILURE,KEY_RECOVERY_REQUEST,KEY_RECOVERY_REQUEST_ASYNC,KEY_RECOVERY_AGENT_LOGIN,KEY_RECOVERY_REQUEST_PROCESSED,KEY_RECOVERY_REQUEST_PROCESSED_ASYNC,KEY_GEN_ASYMMETRIC,CERT_SIGNING_INFO,OCSP_SIGNING_INFO,CRL_SIGNING_INFO,NON_PROFILE_CERT_REQUEST,PROFILE_CERT_REQUEST,CERT_REQUEST_PROCESSED,CERT_STATUS_CHANGE_REQUEST,CERT_STATUS_CHANGE_REQUEST_PROCESSED,AUTHZ,INTER_BOUNDARY,AUTH,CERT_PROFILE_APPROVAL,PROOF_OF_POSSESSION,CMC_PROOF_OF_IDENTIFICATION,CMC_ID_POP_LINK_WITNESS,SCHEDULE_CRL_GENERATION,DELTA_CRL_GENERATION,DELTA_CRL_PUBLISHING,FULL_CRL_GENERATION,FULL_CRL_PUBLISHING,CRL_RETRIEVAL,CRL_VALIDATION,CMC_SIGNED_REQUEST_SIG_VERIFY,CMC_USER_SIGNED_REQUEST_SIG_VERIFY,SERVER_SIDE_KEYGEN_REQUEST_PROCESSED_FAILURE,SERVER_SIDE_KEYGEN_REQUEST_PROCESSED_SUCCESS,SERVER_SIDE_KEYGEN_REQUEST,COMPUTE_SESSION_KEY_REQUEST,COMPUTE_SESSION_KEY_REQUEST_PROCESSED_SUCCESS, COMPUTE_SESSION_KEY_REQUEST_PROCESSED_FAILURE,DIVERSIFY_KEY_REQUEST,DIVERSIFY_KEY_REQUEST_PROCESSED_SUCCESS, DIVERSIFY_KEY_REQUEST_PROCESSED_FAILURE,ENCRYPT_DATA_REQUEST,ENCRYPT_DATA_REQUEST_PROCESSED_SUCCESS,ENCRYPT_DATA_REQUEST_PROCESSED_FAILURE,OCSP_GENERATION,COMPUTE_RANDOM_DATA_REQUEST,COMPUTE_RANDOM_DATA_REQUEST_PROCESSED_SUCCESS,COMPUTE_RANDOM_DATA_REQUEST_PROCESSED_FAILURE,CIMC_CERT_VERIFICATION,SECURITY_DOMAIN_UPDATE,CONFIG_SERIAL_NUMBER,AUTHORITY_CONFIG,ACCESS_SESSION_ESTABLISH,ACCESS_SESSION_TERMINATED,SECURITY_DATA_ARCHIVAL_REQUEST,RANDOM_GENERATION
 log.instance.SignedAudit._006=##
 log.instance.SignedAudit.bufferSize=512
 log.instance.SignedAudit.enable=true
-log.instance.SignedAudit.events=AUDIT_LOG_STARTUP,AUDIT_LOG_SHUTDOWN,ROLE_ASSUME,CONFIG_CERT_POLICY,CONFIG_CERT_PROFILE,CONFIG_CRL_PROFILE,CONFIG_OCSP_PROFILE,CONFIG_AUTH,CONFIG_ROLE,CONFIG_ACL,CONFIG_SIGNED_AUDIT,CONFIG_ENCRYPTION,CONFIG_TRUSTED_PUBLIC_KEY,CONFIG_DRM,SELFTESTS_EXECUTION,AUDIT_LOG_DELETE,LOG_PATH_CHANGE,PRIVATE_KEY_ARCHIVE_REQUEST,PRIVATE_KEY_ARCHIVE_REQUEST_PROCESSED,PRIVATE_KEY_EXPORT_REQUEST_PROCESSED_SUCCESS,PRIVATE_KEY_EXPORT_REQUEST_PROCESSED_FAILURE,KEY_RECOVERY_REQUEST,KEY_RECOVERY_REQUEST_ASYNC,KEY_RECOVERY_AGENT_LOGIN,KEY_RECOVERY_REQUEST_PROCESSED,KEY_RECOVERY_REQUEST_PROCESSED_ASYNC,KEY_GEN_ASYMMETRIC,CERT_SIGNING_INFO,OCSP_SIGNING_INFO,CRL_SIGNING_INFO,NON_PROFILE_CERT_REQUEST,PROFILE_CERT_REQUEST,CERT_REQUEST_PROCESSED,CERT_STATUS_CHANGE_REQUEST,CERT_STATUS_CHANGE_REQUEST_PROCESSED,AUTHZ,INTER_BOUNDARY,AUTH,CERT_PROFILE_APPROVAL,PROOF_OF_POSSESSION,CMC_PROOF_OF_IDENTIFICATION,CMC_ID_POP_LINK_WITNESS,SCHEDULE_CRL_GENERATION,DELTA_CRL_GENERATION,DELTA_CRL_PUBLISHING,FULL_CRL_GENERATION,FULL_CRL_PUBLISHING,CRL_RETRIEVAL,CRL_VALIDATION,CMC_SIGNED_REQUEST_SIG_VERIFY,CMC_USER_SIGNED_REQUEST_SIG_VERIFY_SUCCESS,CMC_USER_SIGNED_REQUEST_SIG_VERIFY_FAILURE,SERVER_SIDE_KEYGEN_REQUEST_PROCESSED_FAILURE,SERVER_SIDE_KEYGEN_REQUEST_PROCESSED_SUCCESS,SERVER_SIDE_KEYGEN_REQUEST,COMPUTE_SESSION_KEY_REQUEST,COMPUTE_SESSION_KEY_REQUEST_PROCESSED_SUCCESS, COMPUTE_SESSION_KEY_REQUEST_PROCESSED_FAILURE,DIVERSIFY_KEY_REQUEST,DIVERSIFY_KEY_REQUEST_PROCESSED_SUCCESS, DIVERSIFY_KEY_REQUEST_PROCESSED_FAILURE,ENCRYPT_DATA_REQUEST,ENCRYPT_DATA_REQUEST_PROCESSED_SUCCESS,ENCRYPT_DATA_REQUEST_PROCESSED_FAILURE,OCSP_GENERATION,COMPUTE_RANDOM_DATA_REQUEST,COMPUTE_RANDOM_DATA_REQUEST_PROCESSED_SUCCESS,COMPUTE_RANDOM_DATA_REQUEST_PROCESSED_FAILURE,CIMC_CERT_VERIFICATION,SECURITY_DOMAIN_UPDATE,CONFIG_SERIAL_NUMBER,AUTHORITY_CONFIG,ACCESS_SESSION_ESTABLISH,ACCESS_SESSION_TERMINATED,SECURITY_DATA_ARCHIVAL_REQUEST,RANDOM_GENERATION
+log.instance.SignedAudit.events=AUDIT_LOG_STARTUP,AUDIT_LOG_SHUTDOWN,ROLE_ASSUME,CONFIG_CERT_POLICY,CONFIG_CERT_PROFILE,CONFIG_CRL_PROFILE,CONFIG_OCSP_PROFILE,CONFIG_AUTH,CONFIG_ROLE,CONFIG_ACL,CONFIG_SIGNED_AUDIT,CONFIG_ENCRYPTION,CONFIG_TRUSTED_PUBLIC_KEY,CONFIG_DRM,SELFTESTS_EXECUTION,AUDIT_LOG_DELETE,LOG_PATH_CHANGE,PRIVATE_KEY_ARCHIVE_REQUEST,PRIVATE_KEY_ARCHIVE_REQUEST_PROCESSED,PRIVATE_KEY_EXPORT_REQUEST_PROCESSED_SUCCESS,PRIVATE_KEY_EXPORT_REQUEST_PROCESSED_FAILURE,KEY_RECOVERY_REQUEST,KEY_RECOVERY_REQUEST_ASYNC,KEY_RECOVERY_AGENT_LOGIN,KEY_RECOVERY_REQUEST_PROCESSED,KEY_RECOVERY_REQUEST_PROCESSED_ASYNC,KEY_GEN_ASYMMETRIC,CERT_SIGNING_INFO,OCSP_SIGNING_INFO,CRL_SIGNING_INFO,NON_PROFILE_CERT_REQUEST,PROFILE_CERT_REQUEST,CERT_REQUEST_PROCESSED,CERT_STATUS_CHANGE_REQUEST,CERT_STATUS_CHANGE_REQUEST_PROCESSED,AUTHZ,INTER_BOUNDARY,AUTH,CERT_PROFILE_APPROVAL,PROOF_OF_POSSESSION,CMC_PROOF_OF_IDENTIFICATION,CMC_ID_POP_LINK_WITNESS,SCHEDULE_CRL_GENERATION,DELTA_CRL_GENERATION,DELTA_CRL_PUBLISHING,FULL_CRL_GENERATION,FULL_CRL_PUBLISHING,CRL_RETRIEVAL,CRL_VALIDATION,CMC_SIGNED_REQUEST_SIG_VERIFY,CMC_USER_SIGNED_REQUEST_SIG_VERIFY,SERVER_SIDE_KEYGEN_REQUEST_PROCESSED_FAILURE,SERVER_SIDE_KEYGEN_REQUEST_PROCESSED_SUCCESS,SERVER_SIDE_KEYGEN_REQUEST,COMPUTE_SESSION_KEY_REQUEST,COMPUTE_SESSION_KEY_REQUEST_PROCESSED_SUCCESS, COMPUTE_SESSION_KEY_REQUEST_PROCESSED_FAILURE,DIVERSIFY_KEY_REQUEST,DIVERSIFY_KEY_REQUEST_PROCESSED_SUCCESS, DIVERSIFY_KEY_REQUEST_PROCESSED_FAILURE,ENCRYPT_DATA_REQUEST,ENCRYPT_DATA_REQUEST_PROCESSED_SUCCESS,ENCRYPT_DATA_REQUEST_PROCESSED_FAILURE,OCSP_GENERATION,COMPUTE_RANDOM_DATA_REQUEST,COMPUTE_RANDOM_DATA_REQUEST_PROCESSED_SUCCESS,COMPUTE_RANDOM_DATA_REQUEST_PROCESSED_FAILURE,CIMC_CERT_VERIFICATION,SECURITY_DOMAIN_UPDATE,CONFIG_SERIAL_NUMBER,AUTHORITY_CONFIG,ACCESS_SESSION_ESTABLISH,ACCESS_SESSION_TERMINATED,SECURITY_DATA_ARCHIVAL_REQUEST,RANDOM_GENERATION
 log.instance.SignedAudit.filters.RANDOM_GENERATION=(Outcome=Failure)
 log.instance.SignedAudit.expirationTime=0
 log.instance.SignedAudit.fileName=[PKI_INSTANCE_PATH]/logs/[PKI_SUBSYSTEM_TYPE]/signedAudit/ca_audit
diff --git a/base/common/src/com/netscape/certsrv/logging/AuditEvent.java b/base/common/src/com/netscape/certsrv/logging/AuditEvent.java
index f4b813c..8aebd0f 100644
--- a/base/common/src/com/netscape/certsrv/logging/AuditEvent.java
+++ b/base/common/src/com/netscape/certsrv/logging/AuditEvent.java
@@ -82,10 +82,6 @@ public class AuditEvent extends LogEvent {
             "LOGGING_SIGNED_AUDIT_CRL_RETRIEVAL_3";
     public final static String CRL_VALIDATION =
             "LOGGING_SIGNED_AUDIT_CRL_VALIDATION_2";
-    public final static String CMC_USER_SIGNED_REQUEST_SIG_VERIFY_SUCCESS =
-            "LOGGING_SIGNED_AUDIT_CMC_USER_SIGNED_REQUEST_SIG_VERIFY_SUCCESS_5";
-    public final static String CMC_USER_SIGNED_REQUEST_SIG_VERIFY_FAILURE =
-            "LOGGING_SIGNED_AUDIT_CMC_USER_SIGNED_REQUEST_SIG_VERIFY_FAILURE_6";
 
     public final static String COMPUTE_RANDOM_DATA_REQUEST =
             "LOGGING_SIGNED_AUDIT_COMPUTE_RANDOM_DATA_REQUEST_2";
diff --git a/base/common/src/com/netscape/certsrv/logging/event/CMCUserSignedRequestSigVerifyEvent.java b/base/common/src/com/netscape/certsrv/logging/event/CMCUserSignedRequestSigVerifyEvent.java
new file mode 100644
index 0000000..76b4a9a
--- /dev/null
+++ b/base/common/src/com/netscape/certsrv/logging/event/CMCUserSignedRequestSigVerifyEvent.java
@@ -0,0 +1,74 @@
+// --- BEGIN COPYRIGHT BLOCK ---
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; version 2 of the License.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License along
+// with this program; if not, write to the Free Software Foundation, Inc.,
+// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+//
+// (C) 2018 Red Hat, Inc.
+// All rights reserved.
+// --- END COPYRIGHT BLOCK ---
+package com.netscape.certsrv.logging.event;
+
+import com.netscape.certsrv.logging.ILogger;
+import com.netscape.certsrv.logging.SignedAuditEvent;
+
+public class CMCUserSignedRequestSigVerifyEvent extends SignedAuditEvent {
+
+    private static final long serialVersionUID = 1L;
+
+    public final static String CMC_USER_SIGNED_REQUEST_SIG_VERIFY_SUCCESS =
+            "LOGGING_SIGNED_AUDIT_CMC_USER_SIGNED_REQUEST_SIG_VERIFY_SUCCESS";
+
+    public final static String CMC_USER_SIGNED_REQUEST_SIG_VERIFY_FAILURE =
+            "LOGGING_SIGNED_AUDIT_CMC_USER_SIGNED_REQUEST_SIG_VERIFY_FAILURE";
+
+    public CMCUserSignedRequestSigVerifyEvent(String messageID) {
+        super(messageID);
+    }
+
+    public static CMCUserSignedRequestSigVerifyEvent createSuccessEvent(
+            String subjectID,
+            String reqType,
+            String certSubject,
+            String signerInfo) {
+
+        CMCUserSignedRequestSigVerifyEvent event = new CMCUserSignedRequestSigVerifyEvent(
+                CMC_USER_SIGNED_REQUEST_SIG_VERIFY_SUCCESS);
+
+        event.setAttribute("SubjectID", subjectID);
+        event.setAttribute("Outcome", ILogger.SUCCESS);
+        event.setAttribute("ReqType", reqType);
+        event.setAttribute("CertSubject", certSubject);
+        event.setAttribute("SignerInfo", signerInfo);
+
+        return event;
+    }
+
+    public static CMCUserSignedRequestSigVerifyEvent createFailureEvent(
+            String subjectID,
+            String reqType,
+            String certSubject,
+            String cmcSignerInfo,
+            String info) {
+
+        CMCUserSignedRequestSigVerifyEvent event = new CMCUserSignedRequestSigVerifyEvent(
+                CMC_USER_SIGNED_REQUEST_SIG_VERIFY_FAILURE);
+
+        event.setAttribute("SubjectID", subjectID);
+        event.setAttribute("Outcome", ILogger.FAILURE);
+        event.setAttribute("ReqType", reqType);
+        event.setAttribute("CertSubject", certSubject);
+        event.setAttribute("CMCSignerInfo", cmcSignerInfo);
+        event.setAttribute("info", info);
+
+        return event;
+    }
+}
diff --git a/base/server/cms/src/com/netscape/cms/authentication/CMCUserSignedAuth.java b/base/server/cms/src/com/netscape/cms/authentication/CMCUserSignedAuth.java
index dd73055..9dbf787 100644
--- a/base/server/cms/src/com/netscape/cms/authentication/CMCUserSignedAuth.java
+++ b/base/server/cms/src/com/netscape/cms/authentication/CMCUserSignedAuth.java
@@ -82,8 +82,8 @@ import com.netscape.certsrv.base.EBaseException;
 import com.netscape.certsrv.base.IConfigStore;
 import com.netscape.certsrv.base.IExtendedPluginInfo;
 import com.netscape.certsrv.base.SessionContext;
-import com.netscape.certsrv.logging.AuditEvent;
 import com.netscape.certsrv.logging.ILogger;
+import com.netscape.certsrv.logging.event.CMCUserSignedRequestSigVerifyEvent;
 import com.netscape.certsrv.profile.EProfileException;
 import com.netscape.certsrv.profile.IProfile;
 import com.netscape.certsrv.profile.IProfileAuthenticator;
@@ -260,7 +260,6 @@ public class CMCUserSignedAuth implements IAuthManager, IExtendedPluginInfo,
         String msg = "";
         CMS.debug(method + "begins");
 
-        String auditMessage = null;
         String auditSubjectID = getAuditSubjectID();
         String auditReqType = ILogger.UNIDENTIFIED;
         String requestCertSubject = ILogger.UNIDENTIFIED;
@@ -725,22 +724,20 @@ public class CMCUserSignedAuth implements IAuthManager, IExtendedPluginInfo,
             }
 
             // For accuracy, make sure revocation by shared secret doesn't
-            // log CMC_USER_SIGNED_REQUEST_SIG_VERIFY_SUCCESS
+            // log successful CMC_USER_SIGNED_REQUEST_SIG_VERIFY audit event
             if (authToken.get(IAuthManager.CRED_CMC_SIGNING_CERT) != null ||
                     authToken.get(IAuthManager.CRED_CMC_SELF_SIGNED) != null) {
-                // store a message in the signed audit log file
-                auditMessage = CMS.getLogMessage(
-                        AuditEvent.CMC_USER_SIGNED_REQUEST_SIG_VERIFY_SUCCESS,
+
+                signedAuditLogger.log(
+                        CMCUserSignedRequestSigVerifyEvent.createSuccessEvent(
                         getAuditSubjectID(),
-                        ILogger.SUCCESS,
                         auditReqType,
                         getRequestCertSubject(auditContext),
-                        getAuditSignerInfo(auditContext));
+                        getAuditSignerInfo(auditContext)));
 
-                signedAuditLogger.log(auditMessage);
             } else {
                 CMS.debug(method
-                        + "audit event CMC_USER_SIGNED_REQUEST_SIG_VERIFY_SUCCESS not logged due to unsigned data for revocation with shared secret.");
+                        + "successful CMC_USER_SIGNED_REQUEST_SIG_VERIFY audit event not logged due to unsigned data for revocation with shared secret.");
             }
 
             CMS.debug(method + "ends successfully; returning authToken");
@@ -752,49 +749,40 @@ public class CMCUserSignedAuth implements IAuthManager, IExtendedPluginInfo,
             throw eAudit1;
         } catch (EInvalidCredentials eAudit2) {
             CMS.debug(method + eAudit2);
-            // store a message in the signed audit log file
-            auditMessage = CMS.getLogMessage(
-                    AuditEvent.CMC_USER_SIGNED_REQUEST_SIG_VERIFY_FAILURE,
+
+            signedAuditLogger.log(
+                    CMCUserSignedRequestSigVerifyEvent.createFailureEvent(
                     getAuditSubjectID(),
-                    ILogger.FAILURE,
                     auditReqType,
                     getRequestCertSubject(auditContext),
                     getAuditSignerInfo(auditContext),
-                    eAudit2.toString());
-
-            signedAuditLogger.log(auditMessage);
+                    eAudit2.toString()));
 
             // rethrow the specific exception to be handled later
             throw eAudit2;
         } catch (EBaseException eAudit3) {
             CMS.debug(method + eAudit3);
-            // store a message in the signed audit log file
-            auditMessage = CMS.getLogMessage(
-                    AuditEvent.CMC_USER_SIGNED_REQUEST_SIG_VERIFY_FAILURE,
+
+            signedAuditLogger.log(
+                    CMCUserSignedRequestSigVerifyEvent.createFailureEvent(
                     getAuditSubjectID(),
-                    ILogger.FAILURE,
                     auditReqType,
                     getRequestCertSubject(auditContext),
                     getAuditSignerInfo(auditContext),
-                    eAudit3.toString());
-
-            signedAuditLogger.log(auditMessage);
+                    eAudit3.toString()));
 
             // rethrow the specific exception to be handled later
             throw eAudit3;
         } catch (Exception eAudit4) {
             CMS.debug(method + eAudit4);
-            // store a message in the signed audit log file
-            auditMessage = CMS.getLogMessage(
-                    AuditEvent.CMC_USER_SIGNED_REQUEST_SIG_VERIFY_FAILURE,
+
+            signedAuditLogger.log(
+                    CMCUserSignedRequestSigVerifyEvent.createFailureEvent(
                     getAuditSubjectID(),
-                    ILogger.FAILURE,
                     auditReqType,
                     getRequestCertSubject(auditContext),
                     getAuditSignerInfo(auditContext),
-                    eAudit4.toString());
-
-            signedAuditLogger.log(auditMessage);
+                    eAudit4.toString()));
 
             // rethrow the exception to be handled later
             throw new EBaseException(eAudit4);
diff --git a/base/server/cmsbundle/src/LogMessages.properties b/base/server/cmsbundle/src/LogMessages.properties
index ceb57ca..4ec6c39 100644
--- a/base/server/cmsbundle/src/LogMessages.properties
+++ b/base/server/cmsbundle/src/LogMessages.properties
@@ -2235,8 +2235,8 @@ LOGGING_SIGNED_AUDIT_CMC_SIGNED_REQUEST_SIG_VERIFY=<type=CMC_SIGNED_REQUEST_SIG_
 # CertSubject must be the certificate subject name of the certificate request
 # CMCSignerInfo must be a unique String representation for the CMC request signer
 #
-LOGGING_SIGNED_AUDIT_CMC_USER_SIGNED_REQUEST_SIG_VERIFY_SUCCESS_5=<type=CMC_USER_SIGNED_REQUEST_SIG_VERIFY_SUCCESS>:[AuditEvent=CMC_USER_SIGNED_REQUEST_SIG_VERIFY_SUCCESS][SubjectID={0}][Outcome={1}][ReqType={2}][CertSubject={3}][SignerInfo={4}] User signed CMC request signature verification success
-LOGGING_SIGNED_AUDIT_CMC_USER_SIGNED_REQUEST_SIG_VERIFY_FAILURE_6=<type=CMC_USER_SIGNED_REQUEST_SIG_VERIFY_FAILURE>:[AuditEvent=CMC_USER_SIGNED_REQUEST_SIG_VERIFY_FAILURE][SubjectID={0}][Outcome={1}][ReqType={2}][CertSubject={3}][CMCSignerInfo={4}][info={5}] User signed CMC request signature verification failure
+LOGGING_SIGNED_AUDIT_CMC_USER_SIGNED_REQUEST_SIG_VERIFY_SUCCESS=<type=CMC_USER_SIGNED_REQUEST_SIG_VERIFY>:[AuditEvent=CMC_USER_SIGNED_REQUEST_SIG_VERIFY] User signed CMC request signature verification success
+LOGGING_SIGNED_AUDIT_CMC_USER_SIGNED_REQUEST_SIG_VERIFY_FAILURE=<type=CMC_USER_SIGNED_REQUEST_SIG_VERIFY>:[AuditEvent=CMC_USER_SIGNED_REQUEST_SIG_VERIFY] User signed CMC request signature verification failure
 
 # LOGGING_SIGNED_AUDIT_COMPUTE_RANDOM_DATA_REQUEST
 # - used for TPS to TKS to get random challenge data
-- 
1.8.3.1


From 57f7313a7def443fc68f952e065c69d0b47a239c Mon Sep 17 00:00:00 2001
From: Ade Lee <alee@redhat.com>
Date: Thu, 11 Jan 2018 15:16:02 -0500
Subject: [PATCH 09/30] Modify get_cert to get rid of spurious certutil error
 messages

Also shortened some lines to comply with PEP8
rhbz# 1520277

Change-Id: I71d5ecb24c979c1be642a0c3529aebfae6e98aa7
(cherry picked from commit 2e5f4408a09819242de0b1b0eb023e5ddf074acd)
---
 base/common/python/pki/nssdb.py | 99 +++++++++++++++++++++++++----------------
 1 file changed, 61 insertions(+), 38 deletions(-)

diff --git a/base/common/python/pki/nssdb.py b/base/common/python/pki/nssdb.py
index bd270ab..8edfca2 100644
--- a/base/common/python/pki/nssdb.py
+++ b/base/common/python/pki/nssdb.py
@@ -72,19 +72,23 @@ def convert_data(data, input_format, output_format, header=None, footer=None):
 
         return ''.join(lines)
 
-    raise Exception('Unable to convert data from %s to %s' % (input_format, output_format))
+    raise Exception('Unable to convert data from {} to {}'.format(
+        input_format, output_format))
 
 
 def convert_csr(csr_data, input_format, output_format):
-    return convert_data(csr_data, input_format, output_format, CSR_HEADER, CSR_FOOTER)
+    return convert_data(csr_data, input_format, output_format,
+                        CSR_HEADER, CSR_FOOTER)
 
 
 def convert_cert(cert_data, input_format, output_format):
-    return convert_data(cert_data, input_format, output_format, CERT_HEADER, CERT_FOOTER)
+    return convert_data(cert_data, input_format, output_format,
+                        CERT_HEADER, CERT_FOOTER)
 
 
 def convert_pkcs7(pkcs7_data, input_format, output_format):
-    return convert_data(pkcs7_data, input_format, output_format, PKCS7_HEADER, PKCS7_FOOTER)
+    return convert_data(pkcs7_data, input_format, output_format,
+                        PKCS7_HEADER, PKCS7_FOOTER)
 
 
 def get_file_type(filename):
@@ -105,11 +109,13 @@ def get_file_type(filename):
 
 class NSSDatabase(object):
 
-    def __init__(self, directory=None, token=None, password=None, password_file=None,
-                 internal_password=None, internal_password_file=None):
+    def __init__(self, directory=None, token=None, password=None,
+                 password_file=None, internal_password=None,
+                 internal_password_file=None):
 
         if not directory:
-            directory = os.path.join(os.path.expanduser("~"), '.dogtag', 'nssdb')
+            directory = os.path.join(
+                os.path.expanduser("~"), '.dogtag', 'nssdb')
 
         self.directory = directory
 
@@ -133,7 +139,8 @@ class NSSDatabase(object):
 
         if internal_password:
             # Store the specified internal token into password file.
-            self.internal_password_file = os.path.join(self.tmpdir, 'internal_password.txt')
+            self.internal_password_file = os.path.join(
+                self.tmpdir, 'internal_password.txt')
             with open(self.internal_password_file, 'w') as f:
                 f.write(internal_password)
 
@@ -281,7 +288,8 @@ class NSSDatabase(object):
 
                 keystroke += '\n'
 
-                # Enter the path length constraint, enter to skip [<0 for unlimited path]:
+                # Enter the path length constraint,
+                # enter to skip [<0 for unlimited path]:
                 if basic_constraints_ext['path_length'] is not None:
                     keystroke += basic_constraints_ext['path_length']
 
@@ -316,7 +324,8 @@ class NSSDatabase(object):
                     with open(data_file, 'w') as f:
                         f.write(generic_ext['data'])
 
-                    critical = 'critical' if generic_ext['critical'] else 'not-critical'
+                    critical = ('critical' if generic_ext['critical']
+                                else 'not-critical')
 
                     ext = generic_ext['oid']
                     ext += ':' + critical
@@ -328,7 +337,9 @@ class NSSDatabase(object):
                 cmd.append(','.join(exts))
 
             # generate binary request
-            p = subprocess.Popen(cmd, stdin=subprocess.PIPE, stdout=subprocess.PIPE,
+            p = subprocess.Popen(cmd,
+                                 stdin=subprocess.PIPE,
+                                 stdout=subprocess.PIPE,
                                  stderr=subprocess.STDOUT)
 
             p.communicate(keystroke)
@@ -336,7 +347,8 @@ class NSSDatabase(object):
             rc = p.wait()
 
             if rc:
-                raise Exception('Failed to generate certificate request. RC: %d' % rc)
+                raise Exception(
+                    'Failed to generate certificate request. RC: %d' % rc)
 
             # encode binary request in base-64
             b64_request_file = os.path.join(tmpdir, 'request.b64')
@@ -358,8 +370,8 @@ class NSSDatabase(object):
 
     def create_cert(self, request_file, cert_file, serial, issuer=None,
                     key_usage_ext=None, basic_constraints_ext=None,
-                    aki_ext=None, ski_ext=None, aia_ext=None, ext_key_usage_ext=None,
-                    validity=None):
+                    aki_ext=None, ski_ext=None, aia_ext=None,
+                    ext_key_usage_ext=None, validity=None):
         cmd = [
             'certutil',
             '-C',
@@ -448,7 +460,8 @@ class NSSDatabase(object):
 
             keystroke += '\n'
 
-            # Enter the path length constraint, enter to skip [<0 for unlimited path]:
+            # Enter the path length constraint,
+            # enter to skip [<0 for unlimited path]:
             if basic_constraints_ext['path_length']:
                 keystroke += basic_constraints_ext['path_length']
 
@@ -482,7 +495,7 @@ class NSSDatabase(object):
             # To ensure whether this is the first AIA being added
             firstentry = True
 
-            # Enter access method type for Authority Information Access extension:
+            # Enter access method type for AIA extension:
             for s in aia_ext:
                 if not firstentry:
                     keystroke += 'y\n'
@@ -507,7 +520,8 @@ class NSSDatabase(object):
                 # One entry is done.
                 firstentry = False
 
-            # Add another location to the Authority Information Access extension [y/N]
+            # Add another location to the Authority Information
+            # Access extension [y/N]
             keystroke += '\n'
 
             # Is this a critical extension [y/N]?
@@ -516,7 +530,9 @@ class NSSDatabase(object):
 
             keystroke += '\n'
 
-        p = subprocess.Popen(cmd, stdin=subprocess.PIPE, stdout=subprocess.PIPE,
+        p = subprocess.Popen(cmd,
+                             stdin=subprocess.PIPE,
+                             stdout=subprocess.PIPE,
                              stderr=subprocess.STDOUT)
 
         p.communicate(keystroke)
@@ -575,7 +591,8 @@ class NSSDatabase(object):
             aia_ext=aia_ext)
 
         if rc:
-            raise Exception('Failed to generate self-signed CA certificate. RC: %d' % rc)
+            raise Exception(
+                'Failed to generate self-signed CA certificate. RC: %d' % rc)
 
     def show_certs(self):
 
@@ -616,20 +633,22 @@ class NSSDatabase(object):
             output_format_option
         ])
 
-        try:
-            cert_data = subprocess.check_output(cmd)
+        pipes = subprocess.Popen(cmd, stdout=subprocess.PIPE,
+                                 stderr=subprocess.PIPE)
+        cert_data, std_err = pipes.communicate()
+
+        if pipes.returncode != 0:
+            # certutil returned an error
+            # raise exception unless its not cert not found
+            if std_err.startswith('certutil: Could not find cert: '):
+                return None
 
-            if output_format == 'base64':
-                cert_data = base64.b64encode(cert_data)
+            raise Exception(std_err.strip())
 
-            return cert_data
+        if output_format == 'base64':
+            cert_data = base64.b64encode(cert_data)
 
-        except subprocess.CalledProcessError:
-            # All certutil errors return the same code (i.e. 255).
-            # For now assume it was caused by missing certificate.
-            # TODO: Check error message. If it's caused by other
-            # issue, throw exception.
-            return None
+        return cert_data
 
     def get_cert_info(self, nickname):
 
@@ -641,23 +660,27 @@ class NSSDatabase(object):
             '-n', nickname
         ]
 
-        cert_details = subprocess.check_output(cmd_extract_serial, stderr=subprocess.STDOUT)
+        cert_details = subprocess.check_output(
+            cmd_extract_serial, stderr=subprocess.STDOUT)
         cert_pem = subprocess.check_output(
             cmd_extract_serial + ['-a'], stderr=subprocess.STDOUT)
 
-        cert_obj = x509.load_pem_x509_certificate(cert_pem, backend=default_backend())
+        cert_obj = x509.load_pem_x509_certificate(
+            cert_pem, backend=default_backend())
 
         cert["serial_number"] = cert_obj.serial_number
 
-        cert["issuer"] = re.search(r'Issuer:(.*)', cert_details).group(1).strip()\
-            .replace('"', '')
-        cert["subject"] = re.search(r'Subject:(.*)', cert_details).group(1).strip()\
-            .replace('"', '')
+        cert["issuer"] = re.search(
+            r'Issuer:(.*)', cert_details).group(1).strip().replace('"', '')
+        cert["subject"] = re.search(
+            r'Subject:(.*)', cert_details).group(1).strip().replace('"', '')
 
-        str_not_before = re.search(r'Not Before.?:(.*)', cert_details).group(1).strip()
+        str_not_before = re.search(
+            r'Not Before.?:(.*)', cert_details).group(1).strip()
         cert["not_before"] = self.convert_time_to_millis(str_not_before)
 
-        str_not_after = re.search(r'Not After.?:(.*)', cert_details).group(1).strip()
+        str_not_after = re.search(
+            r'Not After.?:(.*)', cert_details).group(1).strip()
         cert["not_after"] = self.convert_time_to_millis(str_not_after)
 
         return cert
-- 
1.8.3.1


From 21b7b2ed80c5916492c89b61a758f0c337a3db4d Mon Sep 17 00:00:00 2001
From: "Endi S. Dewata" <edewata@redhat.com>
Date: Thu, 11 Jan 2018 02:01:37 +0100
Subject: [PATCH 10/30] Fixed pki-server cert-find to work with HSM.

Previously the pki-server cert-find command would prompt for
token password if used with HSM. It has been fixed with the
following changes:

The PKISubsystem.create_subsystem_cert_object() was modified to
get the certificate info from the proper token.

The NSSDatabase.get_cert_info() was modified to specify the token
name in the certutil command if provided.

https://pagure.io/dogtagpki/issue/2901

Change-Id: If8862abe4c3057f3094c414134b9719088796963
(cherry picked from commit e715c8a9445de93bc131ab0332d0fc64e44cca12)
(cherry picked from commit ebf46355270cb1f5a64a70336f991cda310067eb)
---
 base/common/python/pki/nssdb.py           | 24 +++++++++++++++++-------
 base/server/python/pki/server/__init__.py |  6 +++---
 2 files changed, 20 insertions(+), 10 deletions(-)

diff --git a/base/common/python/pki/nssdb.py b/base/common/python/pki/nssdb.py
index 8edfca2..cc9194e 100644
--- a/base/common/python/pki/nssdb.py
+++ b/base/common/python/pki/nssdb.py
@@ -382,7 +382,7 @@ class NSSDatabase(object):
         if issuer:
             cmd.extend(['-c', issuer])
         else:
-            cmd.extend('-x')
+            cmd.extend(['-x'])
 
         if self.token:
             cmd.extend(['-h', self.token])
@@ -392,7 +392,7 @@ class NSSDatabase(object):
             '-a',
             '-i', request_file,
             '-o', cert_file,
-            '-m', serial
+            '-m', str(serial)
         ])
 
         if validity:
@@ -653,17 +653,27 @@ class NSSDatabase(object):
     def get_cert_info(self, nickname):
 
         cert = dict()
-        cmd_extract_serial = [
+        cmd = [
             'certutil',
             '-L',
-            '-d', self.directory,
-            '-n', nickname
+            '-d', self.directory
         ]
 
+        fullname = nickname
+
+        if self.token:
+            cmd.extend(['-h', self.token])
+            fullname = self.token + ':' + fullname
+
+        cmd.extend([
+            '-f', self.password_file,
+            '-n', fullname
+        ])
+
         cert_details = subprocess.check_output(
-            cmd_extract_serial, stderr=subprocess.STDOUT)
+            cmd, stderr=subprocess.STDOUT)
         cert_pem = subprocess.check_output(
-            cmd_extract_serial + ['-a'], stderr=subprocess.STDOUT)
+            cmd + ['-a'], stderr=subprocess.STDOUT)
 
         cert_obj = x509.load_pem_x509_certificate(
             cert_pem, backend=default_backend())
diff --git a/base/server/python/pki/server/__init__.py b/base/server/python/pki/server/__init__.py
index bb01dcf..65aee2f 100644
--- a/base/server/python/pki/server/__init__.py
+++ b/base/server/python/pki/server/__init__.py
@@ -161,12 +161,12 @@ class PKISubsystem(object):
     def create_subsystem_cert_object(self, cert_id):
 
         nickname = self.config.get('%s.%s.nickname' % (self.name, cert_id))
+        token = self.config.get('%s.%s.tokenname' % (self.name, cert_id))
 
         cert = {}
         cert['id'] = cert_id
         cert['nickname'] = nickname
-        cert['token'] = self.config.get(
-            '%s.%s.tokenname' % (self.name, cert_id), None)
+        cert['token'] = token
         cert['data'] = self.config.get(
             '%s.%s.cert' % (self.name, cert_id), None)
         cert['request'] = self.config.get(
@@ -177,7 +177,7 @@ class PKISubsystem(object):
         if not nickname:
             return cert
 
-        nssdb = self.instance.open_nssdb()
+        nssdb = self.instance.open_nssdb(token)
         try:
             cert_info = nssdb.get_cert_info(nickname)
             cert.update(cert_info)
-- 
1.8.3.1


From 6a074d7519a475bc5245ff5a9759cbcbcfa7973c Mon Sep 17 00:00:00 2001
From: "Endi S. Dewata" <edewata@redhat.com>
Date: Mon, 15 Jan 2018 16:57:13 +0100
Subject: [PATCH 11/30] Fixed pki-server subsystem-cert-verify to work with
 HSM.

The pki-server subsystem-cert-verify has been modified to use the
proper token name to call pki client-cert-verify.

https://pagure.io/dogtagpki/issue/2901

Change-Id: Ifc496beb0f81c1c6310b183175037243b71a1926
(cherry picked from commit f65ea152af4492de845295fb12180de3aac3c2f1)
(cherry picked from commit f59768f3cdc920a27f1ca90184083142fec98f63)
---
 base/server/python/pki/server/cli/subsystem.py | 29 ++++++++++++++++++--------
 1 file changed, 20 insertions(+), 9 deletions(-)

diff --git a/base/server/python/pki/server/cli/subsystem.py b/base/server/python/pki/server/cli/subsystem.py
index ca5652d..0abf90a 100644
--- a/base/server/python/pki/server/cli/subsystem.py
+++ b/base/server/python/pki/server/cli/subsystem.py
@@ -1000,23 +1000,34 @@ class SubsystemCertValidateCLI(pki.cli.CLI):
         token = cert.get('token', '')
         print('  Token: %s' % token)
 
-        # get internal token password and store in temporary file
-        passwd = instance.get_token_password()
+        # get token password and store in temporary file
+        passwd = instance.get_token_password(token)
 
         pwfile_handle, pwfile_path = tempfile.mkstemp()
         os.write(pwfile_handle, passwd)
         os.close(pwfile_handle)
 
         try:
-            cmd = ['pki',
-                   '-d', instance.nssdb_dir,
-                   '-C', pwfile_path,
-                   'client-cert-validate',
-                   nickname,
-                   '--certusage', usage]
+            cmd = [
+                'pki',
+                '-d', instance.nssdb_dir
+            ]
+
+            fullname = nickname
+
+            if token:
+                cmd.extend(['--token', token])
+                fullname = token + ':' + fullname
+
+            cmd.extend([
+                '-C', pwfile_path,
+                'client-cert-validate',
+                fullname,
+                '--certusage', usage
+            ])
 
             if self.verbose:
-                print('Command: %s' % cmd)
+                print('Command: %s' % ' '.join(cmd))
 
             subprocess.check_output(cmd, stderr=subprocess.STDOUT)
             print('  Status: VALID')
-- 
1.8.3.1


From 311a196d934c5748a61e0094f37dc24e5a120c09 Mon Sep 17 00:00:00 2001
From: "Endi S. Dewata" <edewata@redhat.com>
Date: Mon, 15 Jan 2018 16:57:13 +0100
Subject: [PATCH 12/30] Fixed nssdb.add_cert() for HSM.

The nssdb.add_cert() has been modified to import certificates
properly. If HSM is used, the certificate will be imported into
HSM without trust attributes. If trust attributes are specified,
the certificate will be imported into internal token as well with
the trust attributes. If no HSM is used, the certificate will be
imported into the internal token with the trust attributes if
available.

https://pagure.io/dogtagpki/issue/2901

Change-Id: I4027b3064694ecf41bc616cf1b67581e4d103531
(cherry picked from commit c8f9058432776500dcfe5b2c40e205018919896b)
(cherry picked from commit 3e10aa429cde0ad22f43b1217609230fca3a2517)
---
 base/common/python/pki/nssdb.py | 27 +++++++++++++++------------
 1 file changed, 15 insertions(+), 12 deletions(-)

diff --git a/base/common/python/pki/nssdb.py b/base/common/python/pki/nssdb.py
index cc9194e..11758f1 100644
--- a/base/common/python/pki/nssdb.py
+++ b/base/common/python/pki/nssdb.py
@@ -159,13 +159,14 @@ class NSSDatabase(object):
 
         # Add cert in two steps due to bug #1393668.
 
-        # First, import cert into HSM without trust attributes.
+        # If HSM is used, import cert into HSM without trust attributes.
         if self.token:
             cmd = [
                 'certutil',
                 '-A',
                 '-d', self.directory,
                 '-h', self.token,
+                '-P', self.token,
                 '-f', self.password_file,
                 '-n', nickname,
                 '-i', cert_file,
@@ -175,18 +176,20 @@ class NSSDatabase(object):
             # Ignore return code due to bug #1393668.
             subprocess.call(cmd)
 
-        # Then, import cert into internal token with trust attributes.
-        cmd = [
-            'certutil',
-            '-A',
-            '-d', self.directory,
-            '-f', self.internal_password_file,
-            '-n', nickname,
-            '-i', cert_file,
-            '-t', trust_attributes
-        ]
+        # If HSM is not used, or cert has trust attributes,
+        # import cert into internal token.
+        if not self.token or trust_attributes != ',,':
+            cmd = [
+                'certutil',
+                '-A',
+                '-d', self.directory,
+                '-f', self.internal_password_file,
+                '-n', nickname,
+                '-i', cert_file,
+                '-t', trust_attributes
+            ]
 
-        subprocess.check_call(cmd)
+            subprocess.check_call(cmd)
 
     def modify_cert(self, nickname, trust_attributes):
         cmd = [
-- 
1.8.3.1


From 1fafa7125d93eeedf627c63539fe00aad7b1e15f Mon Sep 17 00:00:00 2001
From: Ade Lee <alee@redhat.com>
Date: Tue, 16 Jan 2018 15:10:15 -0500
Subject: [PATCH 13/30] Make sure tomcat is running as pki user with nuxwdog

The nuxwdog process needs to run as a privileged user to be able
to retrieve the passwords from the systemd tty agent in systemctl.
Therefore, the nuxwdog unit file should NOT specify the PKI user
there.

However, we have added an option to nuxwdog to specify the user
in the nuxwdog config file, so that the process that nuxwdog spawns
(ie. tomcat) will run as the specified user.

The code changes in this patch ensure that when the nuxwdog conf
file is created, the user is set correctly as the value of the
variable TOMCAT_USER.

Change-Id: I0b4f8caedb048aaedf6a8a8f72b24fab39ad7bbf
(cherry picked from commit 96e99209b278b5ba380a61486a5b6ce6a87326a0)
---
 base/server/sbin/pki-server-nuxwdog | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/base/server/sbin/pki-server-nuxwdog b/base/server/sbin/pki-server-nuxwdog
index cb89938..ffdbc33 100755
--- a/base/server/sbin/pki-server-nuxwdog
+++ b/base/server/sbin/pki-server-nuxwdog
@@ -39,7 +39,7 @@ set_javacmd
 # create the nuxwdog config file
 nux_fname="${CATALINA_BASE}/conf/nuxwdog.conf"
 touch ${nux_fname}
-chown pkiuser: ${nux_fname}
+chown ${TOMCAT_USER}: ${nux_fname}
 
 echo "ExeFile ${JAVA_HOME}/bin/java" > $nux_fname
 echo "ExeArgs ${JAVA_HOME}/bin/java ${JAVACMD_OPTS} ${FLAGS} -classpath ${CLASSPATH} ${OPTIONS} ${MAIN_CLASS} start" >> $nux_fname
@@ -50,4 +50,5 @@ echo "ExeErr ${CATALINA_BASE}/logs/catalina.out" >> $nux_fname
 echo "ExeBackground 1" >> $nux_fname
 echo "PidFile $NUXWDOG_PID" >> $nux_fname
 echo "ChildPidFile $CATALINA_PID" >> $nux_fname
+echo "User ${TOMCAT_USER}" >> $nux_fname
 
-- 
1.8.3.1


From 2ac0712fb41c49db35e2482039330d20d5bb8366 Mon Sep 17 00:00:00 2001
From: Ade Lee <alee@redhat.com>
Date: Wed, 17 Jan 2018 18:31:19 -0500
Subject: [PATCH 14/30] Allow instances to be created with custom users

Some folks want to run instances under a different user and
group (ie. not pkiuser).  They may even want a different user for
each instance.  The way to do this in systemd is to create systemd
override files for the specific instance.

The deployment scriptlets have been updated to create (and delete)
these override files.

Change-Id: Icb0b6d15c6c8542dbbd565987d5fb3f1bddf6037
(cherry picked from commit afe0a2525cace41a1bef2ff7fe0f8f53aa5990e5)
---
 .../python/pki/server/deployment/pkihelper.py      | 55 ++++++++++++++++++++++
 .../deployment/scriptlets/instance_layout.py       | 18 +++++++
 2 files changed, 73 insertions(+)

diff --git a/base/server/python/pki/server/deployment/pkihelper.py b/base/server/python/pki/server/deployment/pkihelper.py
index 26aca14..96ae092 100644
--- a/base/server/python/pki/server/deployment/pkihelper.py
+++ b/base/server/python/pki/server/deployment/pkihelper.py
@@ -21,6 +21,12 @@
 # System Imports
 from __future__ import absolute_import
 from __future__ import print_function
+
+try:
+    import configparser
+except ImportError:
+    import ConfigParser as configparser
+
 import errno
 import sys
 import os
@@ -3486,26 +3492,75 @@ class Systemd(object):
           deployer (dictionary):  PKI Deployment name/value parameters
         """
         self.mdict = deployer.mdict
+        self.deployer = deployer
         instance_name = deployer.mdict['pki_instance_name']
 
         unit_file = 'pki-tomcatd@%s.service' % instance_name
         systemd_link = os.path.join(
             '/etc/systemd/system/pki-tomcatd.target.wants',
             unit_file)
+        override_dir = '/etc/systemd/system/pki-tomcatd@{}.service.d'.format(
+            instance_name)
+        self.base_override_dir = override_dir
 
         nuxwdog_unit_file = 'pki-tomcatd-nuxwdog@%s.service' % instance_name
         nuxwdog_systemd_link = os.path.join(
             '/etc/systemd/system/pki-tomcatd-nuxwdog.target.wants',
             nuxwdog_unit_file)
+        nuxwdog_override_dir = (
+            '/etc/systemd/system/pki-tomcatd-nuxwdog@{}.service.d'.format(
+                instance_name))
+        self.nuxwdog_override_dir = nuxwdog_override_dir
+
+        # self.overrides will be a hash of ConfigParsers indexed by filename
+        # once the overrides have been constructed, the caller should call
+        # write_overrides()
+        self.overrides = {}
 
         if os.path.exists(nuxwdog_systemd_link):
             self.is_nuxwdog_enabled = True
             self.service_name = nuxwdog_unit_file
             self.systemd_link = nuxwdog_systemd_link
+            self.override_dir = nuxwdog_override_dir
         else:
             self.is_nuxwdog_enabled = False
             self.service_name = unit_file
             self.systemd_link = systemd_link
+            self.override_dir = override_dir
+
+    def create_override_directory(self):
+        self.deployer.directory.create(self.override_dir, uid=0, gid=0)
+
+    def create_override_file(self, fname):
+        self.create_override_directory()
+        self.deployer.file.create(
+            os.path.join(self.override_dir, fname),
+            uid=0, gid=0
+        )
+
+    def set_override(self, section, param, value, fname='local.conf'):
+        if fname not in self.overrides:
+            parser = configparser.ConfigParser()
+            parser.optionxform = str
+            override_file = os.path.join(self.override_dir, fname)
+            if os.path.exists(override_file):
+                parser.read(override_file)
+            self.overrides[fname] = parser
+        else:
+            parser = self.overrides[fname]
+
+        if not parser.has_section(section):
+            parser.add_section(section)
+
+        parser[section][param] = value
+
+    def write_overrides(self):
+        for fname, parser in self.overrides.items():
+            override_file = os.path.join(self.override_dir, fname)
+            if not os.path.exists(override_file):
+                self.create_override_file(override_file)
+            with open(override_file, 'w') as fp:
+                parser.write(fp, space_around_delimiters=False)
 
     def daemon_reload(self, critical_failure=True):
         """PKI Deployment execution management lifecycle function.
diff --git a/base/server/python/pki/server/deployment/scriptlets/instance_layout.py b/base/server/python/pki/server/deployment/scriptlets/instance_layout.py
index 15c0441..4eb5496 100644
--- a/base/server/python/pki/server/deployment/scriptlets/instance_layout.py
+++ b/base/server/python/pki/server/deployment/scriptlets/instance_layout.py
@@ -145,10 +145,21 @@ class PkiScriptlet(pkiscriptlet.AbstractBasePkiScriptlet):
         deployer.symlink.create(
             deployer.mdict['pki_tomcat_bin_path'],
             deployer.mdict['pki_tomcat_bin_link'])
+
+        # create systemd links
         deployer.symlink.create(
             deployer.mdict['pki_tomcat_systemd'],
             deployer.mdict['pki_instance_systemd_link'],
             uid=0, gid=0)
+        user = deployer.mdict['pki_user']
+        group = deployer.mdict['pki_group']
+        if user != 'pkiuser' or group != 'pkiuser':
+            deployer.systemd.set_override(
+                'Service', 'User', user, 'user.conf')
+            deployer.systemd.set_override(
+                'Service', 'Group', group, 'user.conf')
+        deployer.systemd.write_overrides()
+        deployer.systemd.daemon_reload()
 
         # establish shared NSS security databases for this instance
         deployer.directory.create(deployer.mdict['pki_database_path'])
@@ -179,6 +190,13 @@ class PkiScriptlet(pkiscriptlet.AbstractBasePkiScriptlet):
         # remove Tomcat instance systemd service link
         deployer.symlink.delete(deployer.systemd.systemd_link)
 
+        # delete systemd override directories
+        if deployer.directory.exists(deployer.systemd.base_override_dir):
+            deployer.directory.delete(deployer.systemd.base_override_dir)
+        if deployer.directory.exists(deployer.systemd.nuxwdog_override_dir):
+            deployer.directory.delete(deployer.systemd.nuxwdog_override_dir)
+        deployer.systemd.daemon_reload()
+
         # remove Tomcat instance base
         deployer.directory.delete(deployer.mdict['pki_instance_path'])
         # remove Tomcat instance logs
-- 
1.8.3.1


From 623313038b5a90bec95faaf5bcb6afcd2dedfcbb Mon Sep 17 00:00:00 2001
From: "Endi S. Dewata" <edewata@redhat.com>
Date: Fri, 12 Jan 2018 06:58:04 +0100
Subject: [PATCH 15/30] Added default CA cert nickname in pki
 client-cert-import.

The pki client-cert-import has been modified to support optional
nickname for CA cert. If not specified, a default nickname will
be generated based on the subject DN.

https://pagure.io/dogtagpki/issue/2901

Change-Id: I285a6f1ceb68d388fdf8bb5638f3767a312854a5
(cherry picked from commit 1cda0ab32279097b73427bcfb2b4afd37408e0c4)
(cherry picked from commit 67c8376024f2ae141a26efb4f82ca1c873cd4264)
---
 .../cmstools/client/ClientCertImportCLI.java       | 41 +++++++++++++++++-----
 1 file changed, 33 insertions(+), 8 deletions(-)

diff --git a/base/java-tools/src/com/netscape/cmstools/client/ClientCertImportCLI.java b/base/java-tools/src/com/netscape/cmstools/client/ClientCertImportCLI.java
index ddab6ab..c6ad761 100644
--- a/base/java-tools/src/com/netscape/cmstools/client/ClientCertImportCLI.java
+++ b/base/java-tools/src/com/netscape/cmstools/client/ClientCertImportCLI.java
@@ -46,6 +46,7 @@ import com.netscape.certsrv.client.PKIClient;
 import com.netscape.certsrv.dbs.certdb.CertId;
 import com.netscape.cmstools.cli.CLI;
 import com.netscape.cmstools.cli.MainCLI;
+import com.netscape.cmsutil.util.Cert;
 
 import netscape.security.pkcs.PKCS12;
 import netscape.security.pkcs.PKCS7;
@@ -180,10 +181,13 @@ public class ClientCertImportCLI extends CLI {
 
             if (verbose) System.out.println("Importing CA certificate from " + caCertPath + ".");
 
+            // initialize JSS
+            mainCLI.init();
+
             if (trustAttributes == null)
                 trustAttributes = "CT,C,C";
 
-            importCert(
+            importCACert(
                     mainCLI.certDatabase,
                     nssdbPasswordFile,
                     caCertPath,
@@ -300,13 +304,6 @@ public class ClientCertImportCLI extends CLI {
         } else {
             throw new Exception("Missing certificate to import");
         }
-
-        if (nickname == null) {
-            MainCLI.printMessage("Imported certificates from PKCS #12 file");
-
-        } else {
-            MainCLI.printMessage("Imported certificate \"" + nickname + "\"");
-        }
     }
 
     public void setTrustAttributes(X509Certificate cert, String trustAttributes)
@@ -355,6 +352,30 @@ public class ClientCertImportCLI extends CLI {
         } catch (Exception e) {
             throw new Exception("Unable to import certificate file", e);
         }
+
+        MainCLI.printMessage("Imported certificate \"" + nickname + "\"");
+    }
+
+    public void importCACert(
+            File dbPath,
+            File dbPasswordFile,
+            String certFile,
+            String nickname,
+            String trustAttributes) throws Exception {
+
+        if (nickname != null) {
+            importCert(dbPath, dbPasswordFile, certFile, nickname, trustAttributes);
+            return;
+        }
+
+        String pemCert = new String(Files.readAllBytes(Paths.get(certFile))).trim();
+        byte[] binCert = Cert.parseCertificate(pemCert);
+
+        CryptoManager manager = CryptoManager.getInstance();
+        X509Certificate cert = manager.importCACertPackage(binCert);
+        setTrustAttributes(cert, trustAttributes);
+
+        MainCLI.printMessage("Imported certificate \"" + cert.getNickname() + "\"");
     }
 
     /**
@@ -532,6 +553,8 @@ public class ClientCertImportCLI extends CLI {
                     "Setting trust attributes to CT,C,C");
         }
         setTrustAttributes(root, "CT,C,C");
+
+        MainCLI.printMessage("Imported certificate \"" + nickname + "\"");
     }
 
     public void importPKCS12(
@@ -560,5 +583,7 @@ public class ClientCertImportCLI extends CLI {
         } catch (Exception e) {
             throw new Exception("Unable to import PKCS #12 file", e);
         }
+
+        MainCLI.printMessage("Imported certificates from PKCS #12 file");
     }
 }
-- 
1.8.3.1


From 8fb7b7af8a8c19093c6bb1225f251f5a9aefef3e Mon Sep 17 00:00:00 2001
From: "Endi S. Dewata" <edewata@redhat.com>
Date: Fri, 19 Jan 2018 06:36:58 +0100
Subject: [PATCH 16/30] Added NSSDatabase.add_ca_cert().

A new NSSDatabase.add_ca_cert() method has been added to import
CA cert without nickname using pki client-cert-import.

https://pagure.io/dogtagpki/issue/2901

Change-Id: I45d83938e92293dd54ec5af6e05c6edb215f80ea
(cherry picked from commit ca5e4fde0eaa1b3d26fa7c2cc4c483016833b4bb)
(cherry picked from commit e71e5957c79e03536f6adeef6e79adc8c728db5b)
---
 base/common/python/pki/nssdb.py | 20 ++++++++++++++++++++
 1 file changed, 20 insertions(+)

diff --git a/base/common/python/pki/nssdb.py b/base/common/python/pki/nssdb.py
index 11758f1..97746c9 100644
--- a/base/common/python/pki/nssdb.py
+++ b/base/common/python/pki/nssdb.py
@@ -191,6 +191,26 @@ class NSSDatabase(object):
 
             subprocess.check_call(cmd)
 
+    def add_ca_cert(self, cert_file, trust_attributes=None):
+        cmd = [
+            'pki',
+            '-d', self.directory,
+            '-C', self.password_file
+        ]
+
+        if self.token:
+            cmd.extend(['--token', self.token])
+
+        cmd.extend([
+            'client-cert-import',
+            '--ca-cert', cert_file
+        ])
+
+        if trust_attributes:
+            cmd.extend(['--trust', trust_attributes])
+
+        subprocess.check_call(cmd)
+
     def modify_cert(self, nickname, trust_attributes):
         cmd = [
             'certutil',
-- 
1.8.3.1


From dbca3d74b6c3b296cca5afd834d7b27a9912187b Mon Sep 17 00:00:00 2001
From: "Endi S. Dewata" <edewata@redhat.com>
Date: Fri, 12 Jan 2018 02:50:28 +0100
Subject: [PATCH 17/30] Refactored ClientCertImportCLI.sort().

The ClientCertImportCLI.sort() has been changed to support sorting
in both directions. It also has been renamed to sortCertificateChain().

https://pagure.io/dogtagpki/issue/2901

Change-Id: I431b80e65e4a859d8d6deadf43af6af6aeefad4d
(cherry picked from commit ad67ee9989771598c85d8b3aa87523a7ae5e2d38)
(cherry picked from commit 57a0f4294d9d1858e7e59c6e605c85ee31bff2a3)
---
 .../cmstools/client/ClientCertImportCLI.java       | 45 ++++++++++++++++------
 1 file changed, 33 insertions(+), 12 deletions(-)

diff --git a/base/java-tools/src/com/netscape/cmstools/client/ClientCertImportCLI.java b/base/java-tools/src/com/netscape/cmstools/client/ClientCertImportCLI.java
index c6ad761..46d820a 100644
--- a/base/java-tools/src/com/netscape/cmstools/client/ClientCertImportCLI.java
+++ b/base/java-tools/src/com/netscape/cmstools/client/ClientCertImportCLI.java
@@ -29,11 +29,13 @@ import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.HashMap;
 import java.util.LinkedHashMap;
+import java.util.LinkedList;
 import java.util.List;
 import java.util.Map;
 
 import org.apache.commons.cli.CommandLine;
 import org.apache.commons.cli.Option;
+import org.apache.commons.lang.ArrayUtils;
 import org.mozilla.jss.CryptoManager;
 import org.mozilla.jss.crypto.InternalCertificate;
 import org.mozilla.jss.crypto.X509Certificate;
@@ -379,21 +381,22 @@ public class ClientCertImportCLI extends CLI {
     }
 
     /**
-     * Sorts certificate chain from leaf to root.
+     * Sorts certificate chain from root to leaf.
      *
      * This method sorts an array of certificates (e.g. from a PKCS #7
-     * data) that represents a certificate chain from leaf to root
+     * data) that represents a certificate chain from root to leaf
      * according to the subject DNs and issuer DNs.
      *
-     * The array must contain exactly one unbranched certificate chain
-     * with one leaf and one root. The subject DNs must be unique.
+     * The input array is a set of certificates that are part of a
+     * chain but not in specific order.
      *
-     * The result is returned in a new array. The input array is unchanged.
+     * The result is a new array that contains the certificate chain
+     * sorted from root to leaf. The input array is unchanged.
      *
-     * @param certs array of certificates
+     * @param certs input array of certificates
      * @return new array containing sorted certificates
      */
-    public java.security.cert.X509Certificate[] sort(java.security.cert.X509Certificate[] certs) throws Exception {
+    public java.security.cert.X509Certificate[] sortCertificateChain(java.security.cert.X509Certificate[] certs) throws Exception {
 
         // lookup map: subject DN -> cert
         Map<String, java.security.cert.X509Certificate> certMap = new LinkedHashMap<>();
@@ -416,7 +419,7 @@ public class ClientCertImportCLI extends CLI {
 
             certMap.put(subjectDN, cert);
 
-            // ignore self-signed certificate when building hierarchy maps
+            // ignore self-signed certificate
             if (subjectDN.equals(issuerDN)) continue;
 
             if (childMap.containsKey(issuerDN)) {
@@ -464,21 +467,39 @@ public class ClientCertImportCLI extends CLI {
             throw new Exception("Multiple leaf certificates: " + sb);
         }
 
-        // build cert chain from leaf cert
-        List<java.security.cert.X509Certificate> chain = new ArrayList<>();
+        // build sorted chain
+        LinkedList<java.security.cert.X509Certificate> chain = new LinkedList<>();
+
+        // start from leaf
         String current = leafCerts.get(0);
 
         while (current != null) {
 
             java.security.cert.X509Certificate cert = certMap.get(current);
-            chain.add(cert);
 
+            // add to the beginning of chain
+            chain.addFirst(cert);
+
+            // follow parent to root
             current = parentMap.get(current);
         }
 
         return chain.toArray(new java.security.cert.X509Certificate[chain.size()]);
     }
 
+    public java.security.cert.X509Certificate[] sortCertificateChain(
+            java.security.cert.X509Certificate[] certs,
+            boolean reverse) throws Exception {
+
+        certs = sortCertificateChain(certs);
+
+        if (reverse) {
+            ArrayUtils.reverse(certs);
+        }
+
+        return certs;
+    }
+
     public void importPKCS7(
             String pkcs7Path,
             String nickname,
@@ -499,7 +520,7 @@ public class ClientCertImportCLI extends CLI {
         }
 
         // sort certs from leaf to root
-        certs = sort(certs);
+        certs = sortCertificateChain(certs, true);
 
         CryptoManager manager = CryptoManager.getInstance();
 
-- 
1.8.3.1


From c0c55cadcb45758730393b03a73720f818834bb9 Mon Sep 17 00:00:00 2001
From: "Endi S. Dewata" <edewata@redhat.com>
Date: Fri, 19 Jan 2018 06:15:59 +0100
Subject: [PATCH 18/30] Moved ClientCertImportCLI.sortCertificateChain().

The ClientCertImportCLI.sortCertificateChain() has been moved into
CryptoUtil for reusability. It also has been changed to use SLF4J
logger.

https://pagure.io/dogtagpki/issue/2901

Change-Id: I465c99b9763147357c38ad0526137302acf90a5e
(cherry picked from commit 1622094ac8800bc06e932dea1be77316067f63b3)
(cherry picked from commit ee0918be9bb45611c14b2684954d14459bcb0627)
---
 .../cmstools/client/ClientCertImportCLI.java       | 128 +--------------------
 base/util/src/CMakeLists.txt                       |   1 +
 .../com/netscape/cmsutil/crypto/CryptoUtil.java    | 128 +++++++++++++++++++++
 3 files changed, 131 insertions(+), 126 deletions(-)

diff --git a/base/java-tools/src/com/netscape/cmstools/client/ClientCertImportCLI.java b/base/java-tools/src/com/netscape/cmstools/client/ClientCertImportCLI.java
index 46d820a..99b215e 100644
--- a/base/java-tools/src/com/netscape/cmstools/client/ClientCertImportCLI.java
+++ b/base/java-tools/src/com/netscape/cmstools/client/ClientCertImportCLI.java
@@ -27,15 +27,10 @@ import java.nio.file.Files;
 import java.nio.file.Paths;
 import java.util.ArrayList;
 import java.util.Arrays;
-import java.util.HashMap;
-import java.util.LinkedHashMap;
-import java.util.LinkedList;
 import java.util.List;
-import java.util.Map;
 
 import org.apache.commons.cli.CommandLine;
 import org.apache.commons.cli.Option;
-import org.apache.commons.lang.ArrayUtils;
 import org.mozilla.jss.CryptoManager;
 import org.mozilla.jss.crypto.InternalCertificate;
 import org.mozilla.jss.crypto.X509Certificate;
@@ -48,6 +43,7 @@ import com.netscape.certsrv.client.PKIClient;
 import com.netscape.certsrv.dbs.certdb.CertId;
 import com.netscape.cmstools.cli.CLI;
 import com.netscape.cmstools.cli.MainCLI;
+import com.netscape.cmsutil.crypto.CryptoUtil;
 import com.netscape.cmsutil.util.Cert;
 
 import netscape.security.pkcs.PKCS12;
@@ -380,126 +376,6 @@ public class ClientCertImportCLI extends CLI {
         MainCLI.printMessage("Imported certificate \"" + cert.getNickname() + "\"");
     }
 
-    /**
-     * Sorts certificate chain from root to leaf.
-     *
-     * This method sorts an array of certificates (e.g. from a PKCS #7
-     * data) that represents a certificate chain from root to leaf
-     * according to the subject DNs and issuer DNs.
-     *
-     * The input array is a set of certificates that are part of a
-     * chain but not in specific order.
-     *
-     * The result is a new array that contains the certificate chain
-     * sorted from root to leaf. The input array is unchanged.
-     *
-     * @param certs input array of certificates
-     * @return new array containing sorted certificates
-     */
-    public java.security.cert.X509Certificate[] sortCertificateChain(java.security.cert.X509Certificate[] certs) throws Exception {
-
-        // lookup map: subject DN -> cert
-        Map<String, java.security.cert.X509Certificate> certMap = new LinkedHashMap<>();
-
-        // hierarchy map: subject DN -> issuer DN
-        Map<String, String> parentMap = new HashMap<>();
-
-        // reverse hierarchy map: issuer DN -> subject DN
-        Map<String, String> childMap = new HashMap<>();
-
-        // build maps
-        for (java.security.cert.X509Certificate cert : certs) {
-
-            String subjectDN = cert.getSubjectDN().toString();
-            String issuerDN = cert.getIssuerDN().toString();
-
-            if (certMap.containsKey(subjectDN)) {
-                throw new Exception("Duplicate certificate: " + subjectDN);
-            }
-
-            certMap.put(subjectDN, cert);
-
-            // ignore self-signed certificate
-            if (subjectDN.equals(issuerDN)) continue;
-
-            if (childMap.containsKey(issuerDN)) {
-                throw new Exception("Branched chain: " + issuerDN);
-            }
-
-            parentMap.put(subjectDN, issuerDN);
-            childMap.put(issuerDN, subjectDN);
-        }
-
-        if (verbose) {
-            System.out.println("Certificates:");
-            for (String subjectDN : certMap.keySet()) {
-                System.out.println(" - " + subjectDN);
-
-                String parent = parentMap.get(subjectDN);
-                if (parent != null) System.out.println("   parent: " + parent);
-
-                String child = childMap.get(subjectDN);
-                if (child != null) System.out.println("   child: " + child);
-            }
-        }
-
-        // find leaf cert
-        List<String> leafCerts = new ArrayList<>();
-
-        for (String subjectDN : certMap.keySet()) {
-
-            // if cert has a child, skip
-            if (childMap.containsKey(subjectDN)) continue;
-
-            // found leaf cert
-            leafCerts.add(subjectDN);
-        }
-
-        if (leafCerts.isEmpty()) {
-            throw new Exception("Unable to find leaf certificate");
-
-        } else if (leafCerts.size() > 1) {
-            StringBuilder sb = new StringBuilder();
-            for (String subjectDN : leafCerts) {
-                if (sb.length() > 0) sb.append(", ");
-                sb.append("[" + subjectDN + "]");
-            }
-            throw new Exception("Multiple leaf certificates: " + sb);
-        }
-
-        // build sorted chain
-        LinkedList<java.security.cert.X509Certificate> chain = new LinkedList<>();
-
-        // start from leaf
-        String current = leafCerts.get(0);
-
-        while (current != null) {
-
-            java.security.cert.X509Certificate cert = certMap.get(current);
-
-            // add to the beginning of chain
-            chain.addFirst(cert);
-
-            // follow parent to root
-            current = parentMap.get(current);
-        }
-
-        return chain.toArray(new java.security.cert.X509Certificate[chain.size()]);
-    }
-
-    public java.security.cert.X509Certificate[] sortCertificateChain(
-            java.security.cert.X509Certificate[] certs,
-            boolean reverse) throws Exception {
-
-        certs = sortCertificateChain(certs);
-
-        if (reverse) {
-            ArrayUtils.reverse(certs);
-        }
-
-        return certs;
-    }
-
     public void importPKCS7(
             String pkcs7Path,
             String nickname,
@@ -520,7 +396,7 @@ public class ClientCertImportCLI extends CLI {
         }
 
         // sort certs from leaf to root
-        certs = sortCertificateChain(certs, true);
+        certs = CryptoUtil.sortCertificateChain(certs, true);
 
         CryptoManager manager = CryptoManager.getInstance();
 
diff --git a/base/util/src/CMakeLists.txt b/base/util/src/CMakeLists.txt
index 2a00967..a2269b2 100644
--- a/base/util/src/CMakeLists.txt
+++ b/base/util/src/CMakeLists.txt
@@ -120,6 +120,7 @@ javac(pki-cmsutil-classes
         ${APACHE_COMMONS_LANG_JAR} ${HTTPCORE_JAR} ${HTTPCLIENT_JAR}
         ${LDAPJDK_JAR} ${XALAN_JAR} ${XERCES_JAR}
         ${JSS_JAR} ${COMMONS_CODEC_JAR} ${NUXWDOG_JAR}
+        ${SLF4J_API_JAR}
     OUTPUT_DIR
         ${CMAKE_CURRENT_BINARY_DIR}/classes
     DEPENDS
diff --git a/base/util/src/com/netscape/cmsutil/crypto/CryptoUtil.java b/base/util/src/com/netscape/cmsutil/crypto/CryptoUtil.java
index 7a68c9b..f527967 100644
--- a/base/util/src/com/netscape/cmsutil/crypto/CryptoUtil.java
+++ b/base/util/src/com/netscape/cmsutil/crypto/CryptoUtil.java
@@ -47,10 +47,14 @@ import java.util.Arrays;
 import java.util.Date;
 import java.util.Enumeration;
 import java.util.HashMap;
+import java.util.LinkedHashMap;
+import java.util.LinkedList;
 import java.util.List;
+import java.util.Map;
 import java.util.StringTokenizer;
 import java.util.Vector;
 
+import org.apache.commons.lang.ArrayUtils;
 import org.apache.commons.lang.StringUtils;
 import org.mozilla.jss.CryptoManager;
 import org.mozilla.jss.CryptoManager.NotInitializedException;
@@ -117,6 +121,8 @@ import org.mozilla.jss.ssl.SSLSocket.SSLProtocolVariant;
 import org.mozilla.jss.ssl.SSLSocket.SSLVersionRange;
 import org.mozilla.jss.util.Base64OutputStream;
 import org.mozilla.jss.util.Password;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
 import com.netscape.cmsutil.util.Cert;
 import com.netscape.cmsutil.util.Utils;
@@ -158,6 +164,8 @@ import netscape.security.x509.X509Key;
 @SuppressWarnings("serial")
 public class CryptoUtil {
 
+    private static Logger logger = LoggerFactory.getLogger(CryptoUtil.class);
+
     public static enum SSLVersion {
         SSL_3_0(SSLVersionRange.ssl3),
         TLS_1_0(SSLVersionRange.tls1_0),
@@ -1218,6 +1226,126 @@ public class CryptoUtil {
         }
         return val.toString();
     }
+    /**
+     * Sorts certificate chain from root to leaf.
+     *
+     * This method sorts an array of certificates (e.g. from a PKCS #7
+     * data) that represents a certificate chain from root to leaf
+     * according to the subject DNs and issuer DNs.
+     *
+     * The input array is a set of certificates that are part of a
+     * chain but not in specific order.
+     *
+     * The result is a new array that contains the certificate chain
+     * sorted from root to leaf. The input array is unchanged.
+     *
+     * @param certs input array of certificates
+     * @return new array containing sorted certificates
+     */
+    public static java.security.cert.X509Certificate[] sortCertificateChain(java.security.cert.X509Certificate[] certs) throws Exception {
+
+        // lookup map: subject DN -> cert
+        Map<String, java.security.cert.X509Certificate> certMap = new LinkedHashMap<>();
+
+        // hierarchy map: subject DN -> issuer DN
+        Map<String, String> parentMap = new HashMap<>();
+
+        // reverse hierarchy map: issuer DN -> subject DN
+        Map<String, String> childMap = new HashMap<>();
+
+        // build maps
+        for (java.security.cert.X509Certificate cert : certs) {
+
+            String subjectDN = cert.getSubjectDN().toString();
+            String issuerDN = cert.getIssuerDN().toString();
+
+            if (certMap.containsKey(subjectDN)) {
+                throw new Exception("Duplicate certificate: " + subjectDN);
+            }
+
+            certMap.put(subjectDN, cert);
+
+            // ignore self-signed certificate
+            if (subjectDN.equals(issuerDN)) continue;
+
+            if (childMap.containsKey(issuerDN)) {
+                throw new Exception("Branched chain: " + issuerDN);
+            }
+
+            parentMap.put(subjectDN, issuerDN);
+            childMap.put(issuerDN, subjectDN);
+        }
+
+        if (logger.isDebugEnabled()) {
+            logger.debug("Certificates:");
+            for (String subjectDN : certMap.keySet()) {
+                logger.debug(" - " + subjectDN);
+
+                String parent = parentMap.get(subjectDN);
+                if (parent != null) logger.debug("   parent: " + parent);
+
+                String child = childMap.get(subjectDN);
+                if (child != null) logger.debug("   child: " + child);
+            }
+        }
+
+        // find leaf cert
+        List<String> leafCerts = new ArrayList<>();
+
+        for (String subjectDN : certMap.keySet()) {
+
+            // if cert has a child, skip
+            if (childMap.containsKey(subjectDN)) continue;
+
+            // found leaf cert
+            leafCerts.add(subjectDN);
+        }
+
+        if (leafCerts.isEmpty()) {
+            throw new Exception("Unable to find leaf certificate");
+        }
+
+        if (leafCerts.size() > 1) {
+            StringBuilder sb = new StringBuilder();
+            for (String subjectDN : leafCerts) {
+                if (sb.length() > 0) sb.append(", ");
+                sb.append("[" + subjectDN + "]");
+            }
+            throw new Exception("Multiple leaf certificates: " + sb);
+        }
+
+        // build sorted chain
+        LinkedList<java.security.cert.X509Certificate> chain = new LinkedList<>();
+
+        // start from leaf
+        String current = leafCerts.get(0);
+
+        while (current != null) {
+
+            java.security.cert.X509Certificate cert = certMap.get(current);
+
+            // add to the beginning of chain
+            chain.addFirst(cert);
+
+            // follow parent to root
+            current = parentMap.get(current);
+        }
+
+        return chain.toArray(new java.security.cert.X509Certificate[chain.size()]);
+    }
+
+    public static java.security.cert.X509Certificate[] sortCertificateChain(
+            java.security.cert.X509Certificate[] certs,
+            boolean reverse) throws Exception {
+
+        certs = sortCertificateChain(certs);
+
+        if (reverse) {
+            ArrayUtils.reverse(certs);
+        }
+
+        return certs;
+    }
 
     public static void importCertificateChain(byte[] bytes)
              throws IOException,
-- 
1.8.3.1


From 68ac5378e421e2d4afd40f8c1b4caa788362d96c Mon Sep 17 00:00:00 2001
From: "Endi S. Dewata" <edewata@redhat.com>
Date: Fri, 12 Jan 2018 04:29:30 +0100
Subject: [PATCH 19/30] Added pki pkcs7 CLI.

A new pki pkcs7 CLI has been added to manage a certificate chain in
a PKCS #7 file. The pki pkcs7-cert-find can be used to inspect the
certificates. The pki pkcs7-cert-export can be used to export the
certificates into separate files. The output certificates are sorted
from root to leaf so they can be processed further more consistently.

https://pagure.io/dogtagpki/issue/2901

Change-Id: I7e5c9e2dc0ddd12db126955114b3314f75d475d7
(cherry picked from commit 165c786512141815d3cb77caef119357ecf5a7e4)
(cherry picked from commit 949d4731575fedc96706eb62e2359846a8668718)
---
 .../src/com/netscape/cmstools/cli/MainCLI.java     |   2 +
 .../src/com/netscape/cmstools/pkcs7/PKCS7CLI.java  |  40 +++++++
 .../com/netscape/cmstools/pkcs7/PKCS7CertCLI.java  |  41 +++++++
 .../cmstools/pkcs7/PKCS7CertExportCLI.java         | 131 +++++++++++++++++++++
 .../netscape/cmstools/pkcs7/PKCS7CertFindCLI.java  | 111 +++++++++++++++++
 5 files changed, 325 insertions(+)
 create mode 100644 base/java-tools/src/com/netscape/cmstools/pkcs7/PKCS7CLI.java
 create mode 100644 base/java-tools/src/com/netscape/cmstools/pkcs7/PKCS7CertCLI.java
 create mode 100644 base/java-tools/src/com/netscape/cmstools/pkcs7/PKCS7CertExportCLI.java
 create mode 100644 base/java-tools/src/com/netscape/cmstools/pkcs7/PKCS7CertFindCLI.java

diff --git a/base/java-tools/src/com/netscape/cmstools/cli/MainCLI.java b/base/java-tools/src/com/netscape/cmstools/cli/MainCLI.java
index 945e10e..711625a 100644
--- a/base/java-tools/src/com/netscape/cmstools/cli/MainCLI.java
+++ b/base/java-tools/src/com/netscape/cmstools/cli/MainCLI.java
@@ -59,6 +59,7 @@ import com.netscape.cmstools.key.KeyCLI;
 import com.netscape.cmstools.kra.KRACLI;
 import com.netscape.cmstools.ocsp.OCSPCLI;
 import com.netscape.cmstools.pkcs12.PKCS12CLI;
+import com.netscape.cmstools.pkcs7.PKCS7CLI;
 import com.netscape.cmstools.system.SecurityDomainCLI;
 import com.netscape.cmstools.tks.TKSCLI;
 import com.netscape.cmstools.tps.TPSCLI;
@@ -113,6 +114,7 @@ public class MainCLI extends CLI {
         addModule(new TKSCLI(this));
         addModule(new TPSCLI(this));
 
+        addModule(new PKCS7CLI(this));
         addModule(new PKCS12CLI(this));
 
         createOptions();
diff --git a/base/java-tools/src/com/netscape/cmstools/pkcs7/PKCS7CLI.java b/base/java-tools/src/com/netscape/cmstools/pkcs7/PKCS7CLI.java
new file mode 100644
index 0000000..782d574
--- /dev/null
+++ b/base/java-tools/src/com/netscape/cmstools/pkcs7/PKCS7CLI.java
@@ -0,0 +1,40 @@
+// --- BEGIN COPYRIGHT BLOCK ---
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; version 2 of the License.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License along
+// with this program; if not, write to the Free Software Foundation, Inc.,
+// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+//
+// (C) 2018 Red Hat, Inc.
+// All rights reserved.
+// --- END COPYRIGHT BLOCK ---
+
+package com.netscape.cmstools.pkcs7;
+
+import com.netscape.cmstools.cli.CLI;
+import com.netscape.cmstools.cli.MainCLI;
+
+public class PKCS7CLI extends CLI {
+
+    public PKCS7CLI(CLI parent) {
+        super("pkcs7", "PKCS #7 utilities", parent);
+
+        addModule(new PKCS7CertCLI(this));
+    }
+
+    public String getFullName() {
+        if (parent instanceof MainCLI) {
+            // do not include MainCLI's name
+            return name;
+        } else {
+            return parent.getFullName() + "-" + name;
+        }
+    }
+}
diff --git a/base/java-tools/src/com/netscape/cmstools/pkcs7/PKCS7CertCLI.java b/base/java-tools/src/com/netscape/cmstools/pkcs7/PKCS7CertCLI.java
new file mode 100644
index 0000000..03a8a4c
--- /dev/null
+++ b/base/java-tools/src/com/netscape/cmstools/pkcs7/PKCS7CertCLI.java
@@ -0,0 +1,41 @@
+// --- BEGIN COPYRIGHT BLOCK ---
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; version 2 of the License.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License along
+// with this program; if not, write to the Free Software Foundation, Inc.,
+// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+//
+// (C) 2018 Red Hat, Inc.
+// All rights reserved.
+// --- END COPYRIGHT BLOCK ---
+
+package com.netscape.cmstools.pkcs7;
+
+import java.security.cert.X509Certificate;
+
+import com.netscape.certsrv.dbs.certdb.CertId;
+import com.netscape.cmstools.cli.CLI;
+
+public class PKCS7CertCLI extends CLI {
+
+    public PKCS7CertCLI(PKCS7CLI parent) {
+        super("cert", "PKCS #7 certificate management commands", parent);
+
+        addModule(new PKCS7CertFindCLI(this));
+        addModule(new PKCS7CertExportCLI(this));
+    }
+
+    public static void printCertInfo(X509Certificate cert) throws Exception {
+
+        System.out.println("  Serial Number: " + new CertId(cert.getSerialNumber()).toHexString());
+        System.out.println("  Subject DN: " + cert.getSubjectDN());
+        System.out.println("  Issuer DN: " + cert.getIssuerDN());
+    }
+}
diff --git a/base/java-tools/src/com/netscape/cmstools/pkcs7/PKCS7CertExportCLI.java b/base/java-tools/src/com/netscape/cmstools/pkcs7/PKCS7CertExportCLI.java
new file mode 100644
index 0000000..a57f34a
--- /dev/null
+++ b/base/java-tools/src/com/netscape/cmstools/pkcs7/PKCS7CertExportCLI.java
@@ -0,0 +1,131 @@
+// --- BEGIN COPYRIGHT BLOCK ---
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; version 2 of the License.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License along
+// with this program; if not, write to the Free Software Foundation, Inc.,
+// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+//
+// (C) 2018 Red Hat, Inc.
+// All rights reserved.
+// --- END COPYRIGHT BLOCK ---
+
+package com.netscape.cmstools.pkcs7;
+
+import java.io.FileWriter;
+import java.io.PrintWriter;
+import java.nio.file.Files;
+import java.nio.file.Paths;
+import java.security.cert.X509Certificate;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import org.apache.commons.cli.CommandLine;
+import org.apache.commons.cli.Option;
+
+import com.netscape.cmstools.cli.CLI;
+import com.netscape.cmsutil.crypto.CryptoUtil;
+import com.netscape.cmsutil.util.Cert;
+import com.netscape.cmsutil.util.Utils;
+
+import netscape.security.pkcs.PKCS7;
+
+public class PKCS7CertExportCLI extends CLI {
+
+    private static Logger logger = Logger.getLogger(PKCS7CertExportCLI.class.getName());
+
+    public PKCS7CertExportCLI(PKCS7CertCLI certCLI) {
+        super("export", "Export certificates from PKCS #7 file", certCLI);
+
+        createOptions();
+    }
+
+    public void printHelp() {
+        formatter.printHelp(getFullName() + " [OPTIONS...] [nicknames...]", options);
+    }
+
+    public void createOptions() {
+        Option option = new Option(null, "pkcs7-file", true, "PKCS #7 file");
+        option.setArgName("path");
+        options.addOption(option);
+
+        option = new Option(null, "output-prefix", true, "Prefix for output file");
+        option.setArgName("string");
+        options.addOption(option);
+
+        option = new Option(null, "output-suffix", true, "Suffix for output file");
+        option.setArgName("string");
+        options.addOption(option);
+
+        options.addOption("v", "verbose", false, "Run in verbose mode.");
+        options.addOption(null, "debug", false, "Run in debug mode.");
+        options.addOption(null, "help", false, "Show help message.");
+    }
+
+    public void execute(String[] args) throws Exception {
+
+        CommandLine cmd = parser.parse(options, args, true);
+
+        if (cmd.hasOption("help")) {
+            printHelp();
+            return;
+        }
+
+        if (cmd.hasOption("verbose")) {
+            Logger.getLogger("org.dogtagpki").setLevel(Level.INFO);
+            Logger.getLogger("com.netscape").setLevel(Level.INFO);
+            Logger.getLogger("netscape").setLevel(Level.INFO);
+
+        } else if (cmd.hasOption("debug")) {
+            Logger.getLogger("org.dogtagpki").setLevel(Level.FINE);
+            Logger.getLogger("com.netscape").setLevel(Level.FINE);
+            Logger.getLogger("netscape").setLevel(Level.FINE);
+        }
+
+        String filename = cmd.getOptionValue("pkcs7-file");
+
+        if (filename == null) {
+            throw new Exception("Missing PKCS #7 file.");
+        }
+
+        logger.info("Loading PKCS #7 data from " + filename);
+        String str = new String(Files.readAllBytes(Paths.get(filename))).trim();
+        PKCS7 pkcs7 = new PKCS7(str);
+
+        X509Certificate[] certs = pkcs7.getCertificates();
+        if (certs == null || certs.length == 0) {
+            System.out.println("PKCS #7 data contains no certificates");
+            return;
+        }
+
+        // sort certs from root to leaf
+        certs = CryptoUtil.sortCertificateChain(certs);
+
+        String prefix = cmd.getOptionValue("output-prefix", filename + "-");
+        String suffix = cmd.getOptionValue("output-suffix", "");
+        int i = 0;
+
+        for (X509Certificate cert : certs) {
+
+            logger.info("Exporting certificate #" + i + ": " + cert.getSubjectDN());
+
+            String output = prefix + i + suffix;
+
+            try (PrintWriter out = new PrintWriter(new FileWriter(output))) {
+                out.println(Cert.HEADER);
+                out.print(Utils.base64encode(cert.getEncoded(), true));
+                out.println(Cert.FOOTER);
+            }
+
+            System.out.println(output + ": " + cert.getSubjectDN());
+
+            i++;
+        }
+    }
+}
diff --git a/base/java-tools/src/com/netscape/cmstools/pkcs7/PKCS7CertFindCLI.java b/base/java-tools/src/com/netscape/cmstools/pkcs7/PKCS7CertFindCLI.java
new file mode 100644
index 0000000..7b84393
--- /dev/null
+++ b/base/java-tools/src/com/netscape/cmstools/pkcs7/PKCS7CertFindCLI.java
@@ -0,0 +1,111 @@
+// --- BEGIN COPYRIGHT BLOCK ---
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; version 2 of the License.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License along
+// with this program; if not, write to the Free Software Foundation, Inc.,
+// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+//
+// (C) 2018 Red Hat, Inc.
+// All rights reserved.
+// --- END COPYRIGHT BLOCK ---
+
+package com.netscape.cmstools.pkcs7;
+
+import java.nio.file.Files;
+import java.nio.file.Paths;
+import java.security.cert.X509Certificate;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import org.apache.commons.cli.CommandLine;
+import org.apache.commons.cli.Option;
+
+import com.netscape.cmstools.cli.CLI;
+import com.netscape.cmsutil.crypto.CryptoUtil;
+
+import netscape.security.pkcs.PKCS7;
+
+public class PKCS7CertFindCLI extends CLI {
+
+    private static Logger logger = Logger.getLogger(PKCS7CertFindCLI.class.getName());
+
+    public PKCS7CertFindCLI(PKCS7CertCLI certCLI) {
+        super("find", "Find certificates in PKCS #7 file", certCLI);
+
+        createOptions();
+    }
+
+    public void printHelp() {
+        formatter.printHelp(getFullName() + " [OPTIONS...] [nicknames...]", options);
+    }
+
+    public void createOptions() {
+        Option option = new Option(null, "pkcs7-file", true, "PKCS #7 file");
+        option.setArgName("path");
+        options.addOption(option);
+
+        options.addOption("v", "verbose", false, "Run in verbose mode.");
+        options.addOption(null, "debug", false, "Run in debug mode.");
+        options.addOption(null, "help", false, "Show help message.");
+    }
+
+    public void execute(String[] args) throws Exception {
+
+        CommandLine cmd = parser.parse(options, args, true);
+
+        if (cmd.hasOption("help")) {
+            printHelp();
+            return;
+        }
+
+        if (cmd.hasOption("verbose")) {
+            Logger.getLogger("org.dogtagpki").setLevel(Level.INFO);
+            Logger.getLogger("com.netscape").setLevel(Level.INFO);
+            Logger.getLogger("netscape").setLevel(Level.INFO);
+
+        } else if (cmd.hasOption("debug")) {
+            Logger.getLogger("org.dogtagpki").setLevel(Level.FINE);
+            Logger.getLogger("com.netscape").setLevel(Level.FINE);
+            Logger.getLogger("netscape").setLevel(Level.FINE);
+        }
+
+        String filename = cmd.getOptionValue("pkcs7-file");
+
+        if (filename == null) {
+            throw new Exception("Missing PKCS #7 file.");
+        }
+
+        logger.info("Loading PKCS #7 data from " + filename);
+        String str = new String(Files.readAllBytes(Paths.get(filename))).trim();
+        PKCS7 pkcs7 = new PKCS7(str);
+
+        X509Certificate[] certs = pkcs7.getCertificates();
+        if (certs == null || certs.length == 0) {
+            System.out.println("PKCS #7 data contains no certificates");
+            return;
+        }
+
+        // sort certs from root to leaf
+        certs = CryptoUtil.sortCertificateChain(certs);
+
+        boolean first = true;
+
+        for (X509Certificate cert : certs) {
+
+            if (first) {
+                first = false;
+            } else {
+                System.out.println();
+            }
+
+            PKCS7CertCLI.printCertInfo(cert);
+        }
+    }
+}
-- 
1.8.3.1


From 845abb8d8bd5a7e8168576de63a9b5860d05e51e Mon Sep 17 00:00:00 2001
From: Ade Lee <alee@redhat.com>
Date: Tue, 9 Jan 2018 12:14:23 -0500
Subject: [PATCH 20/30] Fix masking in the archived deployment.cfg

Cherry-picked from 26bc698847b5348033ce3abb225ed24ebce4386d
Resolves rhbz#1532759
Change-Id: Ia464852bab792b1629436ddbb963be1479579bc4

(cherry picked from commit 0c9d093b790aba0d2912f7ba848e280d960c7387)
---
 .../pki/server/deployment/scriptlets/infrastructure_layout.py       | 6 ++++++
 1 file changed, 6 insertions(+)

diff --git a/base/server/python/pki/server/deployment/scriptlets/infrastructure_layout.py b/base/server/python/pki/server/deployment/scriptlets/infrastructure_layout.py
index b105378..2573e4e 100644
--- a/base/server/python/pki/server/deployment/scriptlets/infrastructure_layout.py
+++ b/base/server/python/pki/server/deployment/scriptlets/infrastructure_layout.py
@@ -70,9 +70,15 @@ class PkiScriptlet(pkiscriptlet.AbstractBasePkiScriptlet):
         # parameters
         sensitive_parameters = deployer.mdict['sensitive_parameters'].split()
         sections = deployer.user_config.sections()
+        sections.append('DEFAULT')
         for s in sections:
             for k in sensitive_parameters:
                 deployer.user_config.set(s, k, 'XXXXXXXX')
+
+        deployer.file.create(
+            deployer.mdict['pki_user_deployment_cfg_replica']
+        )
+
         with open(deployer.mdict['pki_user_deployment_cfg_replica'], 'w') as f:
             deployer.user_config.write(f)
 
-- 
1.8.3.1


From 9df879bd000951d8cc23482a78af01b9a711da5d Mon Sep 17 00:00:00 2001
From: "Endi S. Dewata" <edewata@redhat.com>
Date: Mon, 15 Jan 2018 16:57:13 +0100
Subject: [PATCH 21/30] Fixed NSSDatabase.import_pkcs7() for HSM.

Previously NSSDatabase.import_pkcs7() was implemented using pki
client-cert-import --pkcs7 which uses JSS to import the certificate
chain from a PKCS #7 file. Apparently, when it is used with HSM
outside of PKI server JSS imports the certificates incorrectly.

The method has been changed to use pki pkcs7-cert-export to sort
and split the certificate chain into separate files. The CA certs
will be imported with pki client-cert-import --ca-cert (such that
the nickname will be consistently generated by JSS), and the user
certificate will be imported using certutil with the nickname
provided by the caller. This method seems to be working fine with
HSM.

https://pagure.io/dogtagpki/issue/2901

Change-Id: If04963eb6ad86737593df7d64eef8b17f7bde75f
(cherry picked from commit 3d231ae0d6bd2a2627fc223aa86cd8e7f84872f5)
(cherry picked from commit a032321c8324b4fcd1135bb1c623615fbd0c4fdf)
---
 base/common/python/pki/nssdb.py | 46 +++++++++++++++++++++++++++++++++--------
 1 file changed, 37 insertions(+), 9 deletions(-)

diff --git a/base/common/python/pki/nssdb.py b/base/common/python/pki/nssdb.py
index 97746c9..b2339aa 100644
--- a/base/common/python/pki/nssdb.py
+++ b/base/common/python/pki/nssdb.py
@@ -800,15 +800,43 @@ class NSSDatabase(object):
     def import_pkcs7(self, pkcs7_file, nickname, trust_attributes=None,
                      output_format='pem'):
 
-        subprocess.check_call([
-            'pki',
-            '-d', self.directory,
-            '-C', self.password_file,
-            'client-cert-import',
-            '--pkcs7', pkcs7_file,
-            '--trust', trust_attributes,
-            nickname
-        ])
+        tmpdir = tempfile.mkdtemp()
+
+        try:
+            # Sort and split the certs from root to leaf.
+            prefix = os.path.join(tmpdir, 'cert')
+            suffix = '.crt'
+
+            cmd = [
+                'pki',
+                '-d', self.directory,
+                'pkcs7-cert-export',
+                '--pkcs7-file', pkcs7_file,
+                '--output-prefix', prefix,
+                '--output-suffix', suffix
+            ]
+
+            subprocess.check_call(cmd)
+
+            # Count the number of certs in the chain.
+            n = 0
+            while True:
+                cert_file = prefix + str(n) + suffix
+                if not os.path.exists(cert_file):
+                    break
+                n = n + 1
+
+            # Import CA certs with default nicknames and trust attributes.
+            for i in range(0, n - 1):
+                cert_file = prefix + str(i) + suffix
+                self.add_ca_cert(cert_file)
+
+            # Import user cert with specified nickname and trust attributes.
+            cert_file = prefix + str(n - 1) + suffix
+            self.add_cert(nickname, cert_file, trust_attributes)
+
+        finally:
+            shutil.rmtree(tmpdir)
 
         # convert PKCS #7 data to the requested format
         with open(pkcs7_file, 'r') as f:
-- 
1.8.3.1


From d989c7c3576b4ad737128a1714bfc0030087646c Mon Sep 17 00:00:00 2001
From: Matthew Harmsen <mharmsen@redhat.com>
Date: Thu, 18 Jan 2018 15:53:27 -0700
Subject: [PATCH 22/30] Fixed setup of ECC CA

Restored ECC functionality that was lost during
'Refactoring SSL server cert creation'
(https://pagure.io/dogtagpki/issue/2786).

Additionally, to avoid confusion, deprecated
'pki_admin_keysize' and use 'pki_admin_key_size'
to make parameters consistent across different
certificate key types.

Fixes:  https://pagure.io/dogtagpki/issue/2887
Change-Id: I1206b37a00b7da5e30fef5b2d12fb266e2779cfb
(cherry picked from commit 5d83be16f89bcd010c9deb25ead6028e8c619574)
---
 base/server/etc/default.cfg                        |  1 +
 base/server/man/man5/pki_default.cfg.5             | 10 +++---
 base/server/man/man8/pkispawn.8                    | 37 +++++++++++-----------
 .../python/pki/server/deployment/pkihelper.py      | 35 ++++++++++++++++++--
 .../python/pki/server/deployment/pkimessages.py    |  4 +++
 .../python/pki/server/deployment/pkiparser.py      |  2 ++
 .../server/deployment/scriptlets/configuration.py  |  2 ++
 7 files changed, 65 insertions(+), 26 deletions(-)

diff --git a/base/server/etc/default.cfg b/base/server/etc/default.cfg
index 73f884f..9d326e2 100644
--- a/base/server/etc/default.cfg
+++ b/base/server/etc/default.cfg
@@ -72,6 +72,7 @@ pki_admin_cert_file=%(pki_client_dir)s/ca_admin.cert
 pki_admin_cert_request_type=pkcs10
 pki_admin_dualkey=False
 pki_admin_key_algorithm=SHA256withRSA
+# DEPRECATED: Use 'pki_admin_key_size' instead.
 pki_admin_keysize=2048
 pki_admin_key_size=%(pki_admin_keysize)s
 pki_admin_key_type=rsa
diff --git a/base/server/man/man5/pki_default.cfg.5 b/base/server/man/man5/pki_default.cfg.5
index a505c4b..fe3cdc7 100644
--- a/base/server/man/man5/pki_default.cfg.5
+++ b/base/server/man/man5/pki_default.cfg.5
@@ -123,10 +123,10 @@ If an optional hardware security module (HSM) is being utilized (rather than the
 .PP
 Each system certificate can be customized using the parameters below:
 .TP
-.B pki_<tag>_key_type, pki_<type>_keysize, pki_<tag>_key_algorithm
+.B pki_<tag>_key_type, pki_<type>_key_size, pki_<tag>_key_algorithm
 .IP
 Characteristics of the private key. See the Red Hat Certificate System documentation at https://access.redhat.com/knowledge/docs/Red_Hat_Certificate_System/ for possible options.  The defaults are RSA for the type, 2048 bits for the key size, and SHA256withRSA for the algorithm.
-.TP 
+.TP
 .B pki_<tag>_signing_algorithm
 .IP
 For signing certificates, the algorithm used for signing.  Defaults to SHA256withRSA.
@@ -157,7 +157,7 @@ Password for the admin user.  This password is used to log into the pki-console
 .IP
 Email address for the admin user.
 .TP
-.B pki_admin_dualkey, pki_admin_keysize, pki_admin_key_type
+.B pki_admin_dualkey, pki_admin_key_size, pki_admin_key_type, pki_admin_key_algorithm
 .IP
 Settings for the administrator certificate and keys.
 .TP
@@ -211,7 +211,7 @@ Hostname and ports for the internal database.  Defaults to localhost, 389, and 6
 .PP
 .B pki_ds_bind_dn, pki_ds_password
 .IP
-Credentials to connect to the database during installation.  Directory Manager-level access is required during installation to set up the relevant schema and database.  During the installation, a more restricted Certificate Server user is set up to client authentication connections to the database.  Some additional configuration is required, including setting up the directory server to use SSL.  See the documentation for details. 
+Credentials to connect to the database during installation.  Directory Manager-level access is required during installation to set up the relevant schema and database.  During the installation, a more restricted Certificate Server user is set up to client authentication connections to the database.  Some additional configuration is required, including setting up the directory server to use SSL.  See the documentation for details.
 .PP
 .B pki_ds_secure_connection
 .IP
@@ -315,7 +315,7 @@ If you would like the Dogtag installer to add the indexes and reindex the data i
 .TP
 .B pki_clone_replication_master_port, pki_clone_replication_clone_port
 .IP
-Ports on which replication occurs.  These are the ports on the master and clone databases respectively.  Defaults to the internal database port. 
+Ports on which replication occurs.  These are the ports on the master and clone databases respectively.  Defaults to the internal database port.
 .TP
 .B pki_clone_replicate_schema
 .IP
diff --git a/base/server/man/man8/pkispawn.8 b/base/server/man/man8/pkispawn.8
index c1feac3..1411592 100644
--- a/base/server/man/man8/pkispawn.8
+++ b/base/server/man/man8/pkispawn.8
@@ -24,14 +24,14 @@ pkispawn \-s <subsystem> \-f <config_file> [\-h] [\-v] [\-p <prefix>]
 Sets up a Certificate Server subsystem (CA, KRA, OCSP, TKS, or TPS) in a
 Tomcat instance.
 .TP
-\fBNote:\fP 
+\fBNote:\fP
 A 389 Directory Server instance must be configured and running before this script can be run. Certificate Server requires an internal directory database. The default configuration assumes a Directory Server instance running on the same machine on port 389.  For more information on creating a Directory Server instance, see
 .B setup-ds.pl(8).
 .PP
-An instance can contain multiple subsystems, although it may contain at most one of each type of subsystem on a single machine.  So, for example, an instance could contain CA and KRA subsystems,  but not two CA subsystems.  To create an instance with a CA and a KRA, simply run pkispawn twice, with values 
-.I -s CA 
-and 
-.I -s KRA 
+An instance can contain multiple subsystems, although it may contain at most one of each type of subsystem on a single machine.  So, for example, an instance could contain CA and KRA subsystems,  but not two CA subsystems.  To create an instance with a CA and a KRA, simply run pkispawn twice, with values
+.I -s CA
+and
+.I -s KRA
 respectively.
 .PP
 The instances are created based on values for configuration parameters in the default configuration (/etc/pki/default.cfg) and the user-provided configuration file.  The user-provided configuration file is read after the default configuration file, so any parameters defined in that file will override parameters in the default configuration file.  In general, most users will store only those parameters which are different from the default configuration in their user-provided configuration file.
@@ -48,22 +48,22 @@ customization.
 \fBNote:\fP
 Any non-password related parameter values in the configuration file that needs to contain a \fB%\fP character must be properly escaped.  For example, a value of \fBfoo%bar\fP would be specified as \fBfoo%%bar\fP in the configuration file.
 .PP
-At a minimum, the user-defined configuration file must provide some passwords needed for the install.  An example configuration file is provided in the 
+At a minimum, the user-defined configuration file must provide some passwords needed for the install.  An example configuration file is provided in the
 .B EXAMPLES
 section below.  For more information on the default configuration file and the parameters it contains (and can be customized), see
 .B pki_default.cfg(5).
 .PP
 The \fBpkispawn\fP run creates several different installation files that can be referenced later, if need be:
 .IP
-* For Tomcat-based instances, a Tomcat instance is created at \fT/var/lib/pki/<pki_instance_name>\fP, where pki_instance_name is defined in the configuration file.  
+* For Tomcat-based instances, a Tomcat instance is created at \fT/var/lib/pki/<pki_instance_name>\fP, where pki_instance_name is defined in the configuration file.
 .IP
-* A log file of \fBpkispawn\fP operations is written to \fI/var/log/pki/pki-<subsystem>-spawn.<timestamp>.log\fP.  
+* A log file of \fBpkispawn\fP operations is written to \fI/var/log/pki/pki-<subsystem>-spawn.<timestamp>.log\fP.
 .IP
-* A .p12 (PKCS #12) file containing a certificate for a subsystem administrator is stored in pki_client_dir. 
+* A .p12 (PKCS #12) file containing a certificate for a subsystem administrator is stored in pki_client_dir.
 .PP
-When the utility is done running, the CA can be accessed by pointing a browser to https://<hostname>:<pki_https_port>/. The agent pages can be accessed by importing the CA certificate and administrator certificate into the browser. 
+When the utility is done running, the CA can be accessed by pointing a browser to https://<hostname>:<pki_https_port>/. The agent pages can be accessed by importing the CA certificate and administrator certificate into the browser.
 .PP
-The Certificate Server instance can also be accessed using the \fBpki\fP command line interface.  See 
+The Certificate Server instance can also be accessed using the \fBpki\fP command line interface.  See
 \fBpki(1)\fP. For more extensive documentation on how to use Certificate Server features, see the Red Hat Certificate System Documentation at https://access.redhat.com/knowledge/docs/Red_Hat_Certificate_System/.
 .PP
 Instances created using \fBpkispawn\fP can be removed using \fBpkidestroy\fP.  See
@@ -71,7 +71,7 @@ Instances created using \fBpkispawn\fP can be removed using \fBpkidestroy\fP.  S
 .PP
 \fBpkispawn\fP supersedes and combines the functionality of \fBpkicreate\fP and \fBpkisilent\fP, which were available in earlier releases of Certificate Server.  It is now possible to completely create and configure the Certificate Server subsystem in a single step using \fBpkispawn\fP.
 .TP
-\fBNote:\fP 
+\fBNote:\fP
 Previously, as an alternative to using \fBpkisilent\fP to perform a non-interactive batch configuration, a PKI instance could be interactively configured by a GUI-based configuration wizard via a Firefox browser.  GUI-based configuration of a PKI instance is unavailable in this version of the product.
 .SH OPTIONS
 .TP
@@ -89,7 +89,7 @@ Prints additional help information.
 .TP
 .B -v
 Displays verbose information about the installation.  This flag can be provided multiple times to increase verbosity.  See
-.B pkispawn -h 
+.B pkispawn -h
 for details.
 
 .SH SEPARATE VERSUS SHARED INSTANCES
@@ -165,7 +165,7 @@ the AJP port of the Tomcat instance. The default value is 8009.
 \fIManagement port:\fP
 the management port of the Tomcat instance. The default value is 8005.
 .TP
-\fBNote:\fP When deploying a new subsystem into an existing instance, pkispawn will attempt to read the ports from \fBdeployment.cfg\fP files stored for previously installed subsystems for this instance.  If successful, the installer will not prompt for these ports. 
+\fBNote:\fP When deploying a new subsystem into an existing instance, pkispawn will attempt to read the ports from \fBdeployment.cfg\fP files stored for previously installed subsystems for this instance.  If successful, the installer will not prompt for these ports.
 .PP
 \fBAdministrative User Parameters\fP
 .TP
@@ -274,7 +274,7 @@ its default configuration:
 * Installed on the local machine
 
 .IP
-* Listening on port 389 
+* Listening on port 389
 
 .IP
 * The user is cn=Directory Manager, with the password specified in pki_ds_password
@@ -315,7 +315,8 @@ where \fImyconfig.txt\fP contains the following text:
 .nf
 [DEFAULT]
 pki_admin_password=\fISecret123\fP
-pki_admin_keysize=nistp256
+pki_admin_key_algorithm=SHA256withEC
+pki_admin_key_size=nistp256
 pki_admin_key_type=ecc
 pki_client_pkcs12_password=\fISecret123\fP
 pki_ds_password=\fISecret123\fP
@@ -698,7 +699,7 @@ The subsystem section is [KRA], [OCSP], [TKS], or [TPS].
 This example assumes that the specified CA hosts the security
 domain.  The CA must be running and accessible.
 
-.PP 
+.PP
 A new administrator certificate is generated for the new
 subsystem and stored in a PKCS #12 file
 in \fI$HOME/.dogtag/<pki_instance_name>\fP.
@@ -942,7 +943,7 @@ To install a CA clone execute the following command:
 .IP
 \x'-1'\fBpkispawn \-s CA \-f myconfig.txt\fR
 
-.PP 
+.PP
 where \fImyconfig.txt\fP contains the following text:
 
 .IP
diff --git a/base/server/python/pki/server/deployment/pkihelper.py b/base/server/python/pki/server/deployment/pkihelper.py
index 96ae092..a5edaa4 100644
--- a/base/server/python/pki/server/deployment/pkihelper.py
+++ b/base/server/python/pki/server/deployment/pkihelper.py
@@ -2414,8 +2414,8 @@ class Certutil:
 
     def generate_self_signed_certificate(self, path, pki_cert_database,
                                          pki_key_database, pki_secmod_database,
-                                         token, nickname,
-                                         subject, serial_number,
+                                         token, nickname, subject,
+                                         key_type, key_size, serial_number,
                                          validity_period, issuer_name,
                                          trustargs, noise_file,
                                          password_file=None,
@@ -2455,6 +2455,35 @@ class Certutil:
                     log.PKIHELPER_CERTUTIL_MISSING_SUBJECT,
                     extra=config.PKI_INDENTATION_LEVEL_2)
                 raise Exception(log.PKIHELPER_CERTUTIL_MISSING_SUBJECT)
+            #   Specify the key type
+            if key_type:
+                if key_type == "ecc":
+                    command.extend(["-k", "ec"])
+                    #   Specify the curve name
+                    if key_size:
+                        command.extend(["-q", key_size])
+                    else:
+                        config.pki_log.error(
+                            log.PKIHELPER_CERTUTIL_MISSING_CURVE_NAME,
+                            extra=config.PKI_INDENTATION_LEVEL_2)
+                        raise Exception(
+                            log.PKIHELPER_CERTUTIL_MISSING_CURVE_NAME)
+                else:
+                    command.extend(["-k", key_type])
+                    #   Specify the key size
+                    if key_size:
+                        command.extend(["-g", key_size])
+                    else:
+                        config.pki_log.error(
+                            log.PKIHELPER_CERTUTIL_MISSING_KEY_SIZE,
+                            extra=config.PKI_INDENTATION_LEVEL_2)
+                        raise Exception(
+                            log.PKIHELPER_CERTUTIL_MISSING_KEY_SIZE)
+            else:
+                config.pki_log.error(
+                    log.PKIHELPER_CERTUTIL_MISSING_KEY_TYPE,
+                    extra=config.PKI_INDENTATION_LEVEL_2)
+                raise Exception(log.PKIHELPER_CERTUTIL_MISSING_KEY_TYPE)
             #   Specify the serial number
             if serial_number is not None:
                 command.extend(["-m", str(serial_number)])
@@ -4415,7 +4444,7 @@ class ConfigClient:
                 self.deployer.certutil.generate_certificate_request(
                     self.mdict['pki_admin_subject_dn'],
                     self.mdict['pki_admin_key_type'],
-                    self.mdict['pki_admin_keysize'],
+                    self.mdict['pki_admin_key_size'],
                     self.mdict['pki_client_password_conf'],
                     noise_file,
                     output_file,
diff --git a/base/server/python/pki/server/deployment/pkimessages.py b/base/server/python/pki/server/deployment/pkimessages.py
index 7f2d749..7bb79ca 100644
--- a/base/server/python/pki/server/deployment/pkimessages.py
+++ b/base/server/python/pki/server/deployment/pkimessages.py
@@ -179,6 +179,10 @@ PKIHELPER_CERTUTIL_MISSING_ISSUER_NAME = \
     "certutil:  Missing '-c issuer-name' option!"
 PKIHELPER_CERTUTIL_MISSING_KEY_TYPE = \
     "certutil:  Missing '-k key-type-or-id' option (must be 'ecc' or 'rsa')!"
+PKIHELPER_CERTUTIL_MISSING_KEY_SIZE = \
+    "certutil:  Missing '-g keysize' option!"
+PKIHELPER_CERTUTIL_MISSING_CURVE_NAME = \
+    "certutil:  Missing '-q curve-name' option!"
 PKIHELPER_CERTUTIL_MISSING_NICKNAME = \
     "certutil:  Missing '-n nickname' option!"
 PKIHELPER_CERTUTIL_MISSING_NOISE_FILE = \
diff --git a/base/server/python/pki/server/deployment/pkiparser.py b/base/server/python/pki/server/deployment/pkiparser.py
index 18cf19d..b91c674 100644
--- a/base/server/python/pki/server/deployment/pkiparser.py
+++ b/base/server/python/pki/server/deployment/pkiparser.py
@@ -65,6 +65,8 @@ class PKIConfigParser:
     # The 'new_param' is the new param name.
 
     DEPRECATED_DEFAULT_PARAMS = [
+        (None, 'pki_admin_keysize',
+         None, 'pki_admin_key_size'),
         (None, 'pki_external_ca_cert_path',
          None, 'pki_ca_signing_cert_path'),
         (None, 'pki_external_ca_cert_chain_path',
diff --git a/base/server/python/pki/server/deployment/scriptlets/configuration.py b/base/server/python/pki/server/deployment/scriptlets/configuration.py
index 8f7460c..c59e679 100644
--- a/base/server/python/pki/server/deployment/scriptlets/configuration.py
+++ b/base/server/python/pki/server/deployment/scriptlets/configuration.py
@@ -866,6 +866,8 @@ class PkiScriptlet(pkiscriptlet.AbstractBasePkiScriptlet):
                 deployer.mdict['pki_self_signed_token'],
                 deployer.mdict['pki_self_signed_nickname'],
                 deployer.mdict['pki_self_signed_subject'],
+                deployer.mdict['pki_sslserver_key_type'],
+                deployer.mdict['pki_sslserver_key_size'],
                 deployer.mdict['pki_self_signed_serial_number'],
                 deployer.mdict['pki_self_signed_validity_period'],
                 deployer.mdict['pki_self_signed_issuer_name'],
-- 
1.8.3.1


From e7b2dae4cf003e35b463b39a641e6235f5658be9 Mon Sep 17 00:00:00 2001
From: Christina Fu <cfu@redhat.com>
Date: Tue, 16 Jan 2018 18:15:21 -0800
Subject: [PATCH 23/30] Ticket #2675 additional fix to allow requests without
 POP

This patch adds support for requests without POP to be served even when cmc.popLinkWitnessRequired is true. Requests without POP will be handled with EncryptedPOP/DecryptedPOP two-trip mechanism.

Fixes: https://pagure.io/dogtagpki/issue/2675
Change-Id: Id4aab1a85dcaeaa65e625873e617af86b44a271b
(cherry picked from commit c52c51c6516cd39caec52441d0756b1756050ae3)
(cherry picked from commit bb10545e728f0ff86ea4b3899d2de42b2398acfa)
---
 base/server/cms/src/com/netscape/cms/profile/common/EnrollProfile.java | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/base/server/cms/src/com/netscape/cms/profile/common/EnrollProfile.java b/base/server/cms/src/com/netscape/cms/profile/common/EnrollProfile.java
index 9051baf..7c57064 100644
--- a/base/server/cms/src/com/netscape/cms/profile/common/EnrollProfile.java
+++ b/base/server/cms/src/com/netscape/cms/profile/common/EnrollProfile.java
@@ -1052,7 +1052,10 @@ public abstract class EnrollProfile extends BasicProfile
                     msgs[i] = (TaggedRequest) reqSeq.elementAt(i);
                     if (id_cmc_revokeRequest)
                         continue;
+
+                    CertReqMsg crm = msgs[i].getCrm();
                     if (popLinkWitnessRequired &&
+                            crm.hasPop() && // popLinkWitness needs POP
                             !context.containsKey("POPLinkWitnessV2") &&
                             !context.containsKey("POPLinkWitness")) {
                         CMS.debug(method + "popLinkWitness(V2) required");
-- 
1.8.3.1


From 39deb83ed9fc573165c0e806eccd8152d696e935 Mon Sep 17 00:00:00 2001
From: Christina Fu <cfu@redhat.com>
Date: Fri, 19 Jan 2018 14:45:17 -0800
Subject: [PATCH 24/30] Ticket #2675 take care of PKCS#10 for
 cmc.popLinkWitnessRequired

This patch adds support to handle PKCS#10 which was neglected in previous
"additional" fix.

Fixes: https://pagure.io/dogtagpki/issue/2675
Change-Id: Ifc824d64c83f979ffd610658a6e7114598ce8055
(cherry picked from commit 91c6c781e5e2c26b77619e6f4c08dc5d77bb5adf)
(cherry picked from commit d69c11d56d0e1f4368ab21715c2c5496fb08f969)
---
 .../cms/src/com/netscape/cms/profile/common/EnrollProfile.java   | 9 +++++++--
 1 file changed, 7 insertions(+), 2 deletions(-)

diff --git a/base/server/cms/src/com/netscape/cms/profile/common/EnrollProfile.java b/base/server/cms/src/com/netscape/cms/profile/common/EnrollProfile.java
index 7c57064..caa466c 100644
--- a/base/server/cms/src/com/netscape/cms/profile/common/EnrollProfile.java
+++ b/base/server/cms/src/com/netscape/cms/profile/common/EnrollProfile.java
@@ -1053,9 +1053,14 @@ public abstract class EnrollProfile extends BasicProfile
                     if (id_cmc_revokeRequest)
                         continue;
 
-                    CertReqMsg crm = msgs[i].getCrm();
+                    boolean hasPop = true;
+                    if (msgs[i].getType().equals(TaggedRequest.CRMF)) {
+                        CertReqMsg crm = msgs[i].getCrm();
+                        if (!crm.hasPop())
+                            hasPop = false;
+                    }
                     if (popLinkWitnessRequired &&
-                            crm.hasPop() && // popLinkWitness needs POP
+                            hasPop && // popLinkWitness needs POP
                             !context.containsKey("POPLinkWitnessV2") &&
                             !context.containsKey("POPLinkWitness")) {
                         CMS.debug(method + "popLinkWitness(V2) required");
-- 
1.8.3.1


From 3ee72c5c688af6f4434fdac3c082d00619a49535 Mon Sep 17 00:00:00 2001
From: "Endi S. Dewata" <edewata@redhat.com>
Date: Thu, 18 Jan 2018 05:00:28 +0100
Subject: [PATCH 25/30] Fixed cert chain importation.

For KRA/OCSP installation with external certs, the installer has
been modified to always import the cert chain into the internal
token regardless if HSM is used.

https://pagure.io/dogtagpki/issue/2901

Change-Id: Ifedb54e88ea6c8fc2ef3b562e15fb4077ec5179a
(cherry picked from commit 2ffa44852bf5286529877203f60f1084e740f7df)
(cherry picked from commit a338f3fab9b4a9986657a7517dc6c1e3bacaad0b)
---
 .../pki/server/deployment/scriptlets/configuration.py    | 16 ++++++++++++++--
 1 file changed, 14 insertions(+), 2 deletions(-)

diff --git a/base/server/python/pki/server/deployment/scriptlets/configuration.py b/base/server/python/pki/server/deployment/scriptlets/configuration.py
index c59e679..5aad372 100644
--- a/base/server/python/pki/server/deployment/scriptlets/configuration.py
+++ b/base/server/python/pki/server/deployment/scriptlets/configuration.py
@@ -593,13 +593,25 @@ class PkiScriptlet(pkiscriptlet.AbstractBasePkiScriptlet):
             self.import_ca_ocsp_signing_cert(deployer, nssdb)
 
         if subsystem.name == 'kra':
-            self.import_ca_signing_cert(deployer, nssdb)
+            # Always import cert chain into internal token.
+            internal_nssdb = subsystem.instance.open_nssdb()
+            try:
+                self.import_ca_signing_cert(deployer, internal_nssdb)
+            finally:
+                internal_nssdb.close()
+
             self.import_kra_storage_cert(deployer, nssdb)
             self.import_kra_transport_cert(deployer, nssdb)
             self.import_admin_cert(deployer)
 
         if subsystem.name == 'ocsp':
-            self.import_ca_signing_cert(deployer, nssdb)
+            # Always import cert chain into internal token.
+            internal_nssdb = subsystem.instance.open_nssdb()
+            try:
+                self.import_ca_signing_cert(deployer, internal_nssdb)
+            finally:
+                internal_nssdb.close()
+
             self.import_ocsp_signing_cert(deployer, nssdb)
             self.import_admin_cert(deployer)
 
-- 
1.8.3.1


From 705354698c8967c7a97238721a20e33c302bbfd5 Mon Sep 17 00:00:00 2001
From: "Endi S. Dewata" <edewata@redhat.com>
Date: Sat, 20 Jan 2018 03:37:28 +0100
Subject: [PATCH 26/30] Refactored replace_sslserver_cert() in
 configuration.py.

The replace_sslserver_cert() in configuration.py has been split into
separate methods for removing the temp SSL server cert and importing
the permanent SSL server cert.

https://pagure.io/dogtagpki/issue/2901

Change-Id: I35cb95e61959ff99c235f116304c7272a39694e5
(cherry picked from commit c86eb1bc43529b24fc49444eeff8cb4e44204fef)
(cherry picked from commit 5ad7efb44f434f8695c404ba40b4baea27d6bfec)
---
 .../pki/server/deployment/scriptlets/configuration.py       | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/base/server/python/pki/server/deployment/scriptlets/configuration.py b/base/server/python/pki/server/deployment/scriptlets/configuration.py
index 5aad372..ef40963 100644
--- a/base/server/python/pki/server/deployment/scriptlets/configuration.py
+++ b/base/server/python/pki/server/deployment/scriptlets/configuration.py
@@ -899,7 +899,7 @@ class PkiScriptlet(pkiscriptlet.AbstractBasePkiScriptlet):
         finally:
             nssdb.close()
 
-    def replace_sslserver_cert(self, deployer, instance, sslserver):
+    def remove_temp_sslserver_cert(self, deployer, instance, sslserver):
 
         if len(deployer.instance.tomcat_instance_subsystems()) == 1:
             # Modify contents of 'serverCertNick.conf' (if necessary)
@@ -922,6 +922,9 @@ class PkiScriptlet(pkiscriptlet.AbstractBasePkiScriptlet):
         finally:
             nssdb.close()
 
+    def import_perm_sslserver_cert(self, deployer, instance, sslserver):
+
+        nickname = sslserver['nickname']
         token = deployer.mdict['pki_token_name']
 
         config.pki_log.info(
@@ -1225,7 +1228,13 @@ class PkiScriptlet(pkiscriptlet.AbstractBasePkiScriptlet):
         # replace it with the perm cert.
         if create_temp_sslserver_cert and sslserver and sslserver['cert']:
             deployer.systemd.stop()
-            self.replace_sslserver_cert(deployer, instance, sslserver)
+
+            # Remove temp SSL server cert.
+            self.remove_temp_sslserver_cert(deployer, instance, sslserver)
+
+            # Import perm SSL server cert.
+            self.import_perm_sslserver_cert(deployer, instance, sslserver)
+
             deployer.systemd.start()
 
         elif config.str2bool(deployer.mdict['pki_restart_configured_instance']):
-- 
1.8.3.1


From 88e67a6db3dc4f942c662b7a4ba02178cf887b3b Mon Sep 17 00:00:00 2001
From: "Endi S. Dewata" <edewata@redhat.com>
Date: Sat, 20 Jan 2018 03:37:28 +0100
Subject: [PATCH 27/30] Fixed SSL server cert creation and replacement.

The configuration.py has been modified to generate the temp SSL certificates
(and remove it later) in internal token regardless of HSM. It also has been
modified to import the perm cert if it has not been imported already.

https://pagure.io/dogtagpki/issue/2901

Change-Id: If473e2b314727399854638a94c6ec5a148fc52fb
(cherry picked from commit 249c323d23f38a6f778a46054360e705040bdda1)
(cherry picked from commit 30b499d21d731a1c3903bbfe7b23111963971038)
---
 .../server/deployment/scriptlets/configuration.py  | 23 +++++++++++++++-------
 1 file changed, 16 insertions(+), 7 deletions(-)

diff --git a/base/server/python/pki/server/deployment/scriptlets/configuration.py b/base/server/python/pki/server/deployment/scriptlets/configuration.py
index ef40963..43e9d83 100644
--- a/base/server/python/pki/server/deployment/scriptlets/configuration.py
+++ b/base/server/python/pki/server/deployment/scriptlets/configuration.py
@@ -809,12 +809,12 @@ class PkiScriptlet(pkiscriptlet.AbstractBasePkiScriptlet):
         self.validate_system_cert(deployer, nssdb, subsystem, 'subsystem')
         self.validate_system_cert(deployer, nssdb, subsystem, 'audit_signing')
 
-    def create_temp_sslserver_cert(self, deployer, instance, token):
+    def create_temp_sslserver_cert(self, deployer, instance):
 
         if len(deployer.instance.tomcat_instance_subsystems()) > 1:
             return False
 
-        nssdb = instance.open_nssdb(token)
+        nssdb = instance.open_nssdb()
 
         try:
             nickname = deployer.mdict['pki_self_signed_nickname']
@@ -908,6 +908,7 @@ class PkiScriptlet(pkiscriptlet.AbstractBasePkiScriptlet):
         # TODO: replace with pki-server cert-import sslserver
 
         nickname = sslserver['nickname']
+        token = deployer.mdict['pki_token_name']
 
         config.pki_log.info(
             "removing temp SSL server cert from internal token: %s" % nickname,
@@ -916,8 +917,13 @@ class PkiScriptlet(pkiscriptlet.AbstractBasePkiScriptlet):
         nssdb = instance.open_nssdb()
 
         try:
-            # remove temp SSL server cert but keep the key
-            nssdb.remove_cert(nickname)
+            # Remove temp SSL server cert from internal token.
+            # Remove temp key too if the perm cert uses HSM.
+            if not token or token == 'internal':
+                remove_key = False
+            else:
+                remove_key = True
+            nssdb.remove_cert(nickname, remove_key=remove_key)
 
         finally:
             nssdb.close()
@@ -1066,7 +1072,7 @@ class PkiScriptlet(pkiscriptlet.AbstractBasePkiScriptlet):
         finally:
             nssdb.close()
 
-        create_temp_sslserver_cert = self.create_temp_sslserver_cert(deployer, instance, token)
+        create_temp_sslserver_cert = self.create_temp_sslserver_cert(deployer, instance)
 
         # Start/Restart this Tomcat PKI Process
         # Optionally prepare to enable a java debugger
@@ -1232,8 +1238,11 @@ class PkiScriptlet(pkiscriptlet.AbstractBasePkiScriptlet):
             # Remove temp SSL server cert.
             self.remove_temp_sslserver_cert(deployer, instance, sslserver)
 
-            # Import perm SSL server cert.
-            self.import_perm_sslserver_cert(deployer, instance, sslserver)
+            # Import perm SSL server cert unless it's already imported
+            # earlier in external/standalone installation.
+
+            if not (standalone or external and subsystem.name in ['kra', 'ocsp']):
+                self.import_perm_sslserver_cert(deployer, instance, sslserver)
 
             deployer.systemd.start()
 
-- 
1.8.3.1


From f69f1bfa7929203e677077aa65eedaf9252a786d Mon Sep 17 00:00:00 2001
From: "Endi S. Dewata" <edewata@redhat.com>
Date: Fri, 19 Jan 2018 23:26:37 +0100
Subject: [PATCH 28/30] Fixed admin cert processing.

For KRA/OCSP installation with external certs, the installation
tool has been modified to import the externaly-generated admin
cert and also copy it to a location normally expected by admin.

https://pagure.io/dogtagpki/issue/2901

Change-Id: Id18ec2b6b8b1c3f307af11e2acba7866b2b5ee75
(cherry picked from commit 1127a63cb473b471982c273dbbaa270f19f5d90d)
(cherry picked from commit b3664cd26b90d804e311f56cf16e4e31223d5960)
---
 base/server/python/pki/server/deployment/pkihelper.py | 9 ++++-----
 1 file changed, 4 insertions(+), 5 deletions(-)

diff --git a/base/server/python/pki/server/deployment/pkihelper.py b/base/server/python/pki/server/deployment/pkihelper.py
index a5edaa4..7f88873 100644
--- a/base/server/python/pki/server/deployment/pkihelper.py
+++ b/base/server/python/pki/server/deployment/pkihelper.py
@@ -4369,7 +4369,7 @@ class ConfigClient:
         data.adminUID = self.mdict['pki_admin_uid']
         data.adminSubjectDN = self.mdict['pki_admin_subject_dn']
 
-        if self.standalone:
+        if self.standalone or self.external and self.subsystem in ['KRA', 'OCSP']:
             if not self.external_step_two:
                 # IMPORTANT:  ALWAYS set 'pki_import_admin_cert' FALSE for
                 #             Stand-alone PKI (Step 1)
@@ -4395,11 +4395,10 @@ class ConfigClient:
             finally:
                 client_nssdb.close()
 
-            if self.standalone:
-                # Stand-alone PKI (Step 2)
+            if self.standalone or self.external and self.subsystem in ['KRA', 'OCSP']:
+                # Stand-alone/External PKI (Step 2)
                 #
-                # Copy the Stand-alone PKI 'Admin Certificate'
-                # (that was previously generated via an external CA) into
+                # Copy the externally-issued admin certificate into
                 # 'ca_admin.cert' under the specified 'pki_client_dir'
                 # stripping the certificate HEADER/FOOTER prior to saving it.
                 imported_admin_cert = ""
-- 
1.8.3.1


From 73dee32e2636e33f8c6af43b27dc6a3f65d2b7a5 Mon Sep 17 00:00:00 2001
From: Fraser Tweedale <ftweedal@redhat.com>
Date: Tue, 16 Jan 2018 14:53:51 +1100
Subject: [PATCH 29/30] Set nextUpdate in OCSP responses

Some OCSP clients adhere to the Lightweight OCSP Profile (RFC 5019)
which requires that the OCSP response include the nextUpdate field.

Update the CA subsystem's OCSP responder to include the nextUpdate
field when it is configured to use the CRL cache.  The nextUpdate
field in the OCSP response is set to the nextUpdate time of the
"master" CRL issuing point.

If the OCSP responder is not configured to use the CRL cache, there
is no reasonable value for nextUpdate.  In this case, we continue to
omit it.

Fixes: https://pagure.io/dogtagpki/issue/2661
Change-Id: Idbf7354b0ecc45c0498c4b7c05458f726f40336f
(cherry picked from commit fc3067f2e955e18405f735269bfe5fe135382d21)
(cherry picked from commit eb14d5bc86fa1c68282f84246cbaf33c9f55399b)
---
 base/ca/src/com/netscape/ca/CertificateAuthority.java | 14 ++++++++++++--
 1 file changed, 12 insertions(+), 2 deletions(-)

diff --git a/base/ca/src/com/netscape/ca/CertificateAuthority.java b/base/ca/src/com/netscape/ca/CertificateAuthority.java
index 6309c1b..e5d21eb 100644
--- a/base/ca/src/com/netscape/ca/CertificateAuthority.java
+++ b/base/ca/src/com/netscape/ca/CertificateAuthority.java
@@ -2571,7 +2571,6 @@ public class CertificateAuthority
 
         CertStatus certStatus = null;
         GeneralizedTime thisUpdate = new GeneralizedTime(CMS.getCurrentDate());
-        GeneralizedTime nextUpdate = null;
 
         byte[] nameHash = null;
         String digestName = cid.getDigestName();
@@ -2609,6 +2608,12 @@ public class CertificateAuthority
             CRLIssuingPoint point = (CRLIssuingPoint)
                     getCRLIssuingPoint(issuingPointId);
 
+            /* set nextUpdate to the nextUpdate time of the CRL */
+            GeneralizedTime nextUpdate = null;
+            Date crlNextUpdate = point.getNextUpdate();
+            if (crlNextUpdate != null)
+                nextUpdate = new GeneralizedTime(crlNextUpdate);
+
             if (point.isCRLCacheEnabled()) {
                 // only do this if cache is enabled
                 BigInteger sno = new BigInteger(serialNo.toString());
@@ -2660,7 +2665,12 @@ public class CertificateAuthority
             certStatus = new UnknownInfo(); // not issued not all
         }
 
-        return new SingleResponse(cid, certStatus, thisUpdate, nextUpdate);
+        return new SingleResponse(
+            cid, certStatus, thisUpdate,
+            /* We are not using a CRL cache for generating OCSP
+             * responses, so there is no reasonable value for
+             * nextUpdate. */
+            null /* nextUpdate */);
     }
 
     /**
-- 
1.8.3.1


From 138ae505719adb0a0e43114d6acaa8ef8cc0fcec Mon Sep 17 00:00:00 2001
From: "Endi S. Dewata" <edewata@redhat.com>
Date: Mon, 22 Jan 2018 06:37:26 +0100
Subject: [PATCH 30/30] Fixed cert import for exiting certs case.

The configuration servlet has been fixed to properly import the
externally-signed certs in existing CA and external KRA/OCSP cases.

https://pagure.io/dogtagpki/issue/2901

Change-Id: Ida7bd7758670c72063765462b7d735f69a465804
(cherry picked from commit 441b832fb2172680ba3c83c3048a97c38c0c1ec0)
(cherry picked from commit 4196d2ab35c4b683228e3ff03cda98c120726702)
---
 .../org/dogtagpki/server/rest/SystemConfigService.java   | 16 ++++++++++++----
 1 file changed, 12 insertions(+), 4 deletions(-)

diff --git a/base/server/cms/src/org/dogtagpki/server/rest/SystemConfigService.java b/base/server/cms/src/org/dogtagpki/server/rest/SystemConfigService.java
index 97508b7..575f97c 100644
--- a/base/server/cms/src/org/dogtagpki/server/rest/SystemConfigService.java
+++ b/base/server/cms/src/org/dogtagpki/server/rest/SystemConfigService.java
@@ -401,12 +401,19 @@ public class SystemConfigService extends PKIService implements SystemConfigResou
         cert.setSubsystem(subsystem);
         cert.setType(cs.getString("preop.cert." + tag + ".type"));
 
+        String fullName;
+        if (!CryptoUtil.isInternalToken(tokenName)) {
+            fullName = tokenName + ":" + nickname;
+        } else {
+            fullName = nickname;
+        }
+
         CMS.debug("SystemConfigService: checking " + tag + " cert in NSS database");
 
         CryptoManager cm = CryptoManager.getInstance();
         X509Certificate x509Cert;
         try {
-            x509Cert = cm.findCertByNickname(nickname);
+            x509Cert = cm.findCertByNickname(fullName);
         } catch (ObjectNotFoundException e) {
             x509Cert = null;
         }
@@ -414,11 +421,12 @@ public class SystemConfigService extends PKIService implements SystemConfigResou
         // For external/existing CA case, some/all system certs may be provided.
         // The SSL server cert will always be generated for the current host.
 
-        // For standalone KRA/OCSP case, all system certs will be provided.
+        // For external/standalone KRA/OCSP case, all system certs will be provided.
         // No system certs will be generated including the SSL server cert.
 
-        if (request.isExternal() && !tag.equals("sslserver") && x509Cert != null
-                || request.getStandAlone()) {
+        if (request.isExternal() && "ca".equals(subsystem) && !tag.equals("sslserver") && x509Cert != null
+                || request.getStandAlone()
+                || request.isExternal() && ("kra".equals(subsystem) || "ocsp".equals(subsystem))) {
 
             CMS.debug("SystemConfigService: loading existing " + tag + " cert");
             byte[] bytes = x509Cert.getEncoded();
-- 
1.8.3.1