From 02def66122ce6d160e07ec29ea10f0ce8adc47c5 Mon Sep 17 00:00:00 2001
From: Matthew Harmsen <mharmsen@redhat.com>
Date: Thu, 16 Aug 2012 20:09:20 -0700
Subject: [PATCH] PKI Deployment Scriptlets * TRAC Ticket #266 - for
 non-master CA subsystems, pkidestroy needs to   contact the
 security domain to update the domain * Made Fedora 17 rely
 upon tomcatjss 7.0.0 or later * Changed Dogtag 10
 build-time and runtime requirements for 'pki-deploy' *
 Altered PKI Package Dependency Chain (top-to-bottom):  
 pki-ca, pki-kra, pki-ocsp, pki-tks --> pki-deploy -->
 pki-common * Changed TPS to require a build-time dependency
 of 'httpd-devel >= 2.4.2'

---
 .../cms/servlet/csadmin/DownloadPKCS12.java        |    6 +-
 .../cms/servlet/csadmin/GetConfigEntries.java      |    3 +-
 .../cms/servlet/csadmin/GetTransportCert.java      |    6 +-
 .../cms/servlet/csadmin/ImportTransportCert.java   |    6 +-
 .../netscape/cms/servlet/csadmin/RegisterUser.java |    6 +-
 .../cms/servlet/csadmin/UpdateConnector.java       |    6 +-
 .../cms/servlet/csadmin/UpdateDomainXML.java       |   14 +-
 .../cms/servlet/csadmin/UpdateNumberRange.java     |    3 +-
 .../cms/servlet/csadmin/UpdateOCSPConfig.java      |    3 +-
 base/deploy/src/scriptlets/configuration.py        |    1 +
 base/deploy/src/scriptlets/pkihelper.py            |  190 ++++++++++++++++++++
 base/deploy/src/scriptlets/pkimessages.py          |   25 +++
 base/deploy/src/scriptlets/pkiparser.py            |   26 +++
 scripts/compose_functions                          |   26 +--
 specs/dogtag-pki.spec                              |    9 +-
 specs/pki-core.spec                                |  137 +++++++-------
 specs/pki-ra.spec                                  |   12 +-
 specs/pki-tps.spec                                 |   15 +-
 18 files changed, 385 insertions(+), 109 deletions(-)

diff --git a/base/common/src/com/netscape/cms/servlet/csadmin/DownloadPKCS12.java b/base/common/src/com/netscape/cms/servlet/csadmin/DownloadPKCS12.java
index 57a1e62..7f79299 100644
--- a/base/common/src/com/netscape/cms/servlet/csadmin/DownloadPKCS12.java
+++ b/base/common/src/com/netscape/cms/servlet/csadmin/DownloadPKCS12.java
@@ -72,7 +72,8 @@ public class DownloadPKCS12 extends CMSServlet {
         String pin = (String) httpReq.getSession().getAttribute("pin");
         if (pin == null) {
             CMS.debug("DownloadPKCS12 process: Failed to get the pin from the cookie.");
-            outputError(httpResp, AUTH_FAILURE, "Error: Not authenticated");
+            outputError(httpResp, AUTH_FAILURE, "Error: Not authenticated",
+                        null);
             return;
         }
 
@@ -84,7 +85,8 @@ public class DownloadPKCS12 extends CMSServlet {
 
         if (!pin.equals(cspin)) {
             CMS.debug("DownloadPKCS12 process: Wrong pin");
-            outputError(httpResp, AUTH_FAILURE, "Error: Not authenticated");
+            outputError(httpResp, AUTH_FAILURE, "Error: Not authenticated",
+                        null);
             return;
         }
 
diff --git a/base/common/src/com/netscape/cms/servlet/csadmin/GetConfigEntries.java b/base/common/src/com/netscape/cms/servlet/csadmin/GetConfigEntries.java
index a9de5a5..ee013ef 100644
--- a/base/common/src/com/netscape/cms/servlet/csadmin/GetConfigEntries.java
+++ b/base/common/src/com/netscape/cms/servlet/csadmin/GetConfigEntries.java
@@ -88,7 +88,8 @@ public class GetConfigEntries extends CMSServlet {
             log(ILogger.LL_FAILURE,
                     CMS.getLogMessage("CMSGW_ERR_BAD_SERV_OUT_STREAM", "",
                             e.toString()));
-            outputError(httpResp, AUTH_FAILURE, "Error: Not authenticated");
+            outputError(httpResp, AUTH_FAILURE, "Error: Not authenticated",
+                        null);
             return;
         }
 
diff --git a/base/common/src/com/netscape/cms/servlet/csadmin/GetTransportCert.java b/base/common/src/com/netscape/cms/servlet/csadmin/GetTransportCert.java
index ae5edf6..649b121 100644
--- a/base/common/src/com/netscape/cms/servlet/csadmin/GetTransportCert.java
+++ b/base/common/src/com/netscape/cms/servlet/csadmin/GetTransportCert.java
@@ -86,13 +86,15 @@ public class GetTransportCert extends CMSServlet {
             log(ILogger.LL_FAILURE,
                     CMS.getLogMessage("CMSGW_ERR_BAD_SERV_OUT_STREAM", "",
                             e.toString()));
-            outputError(httpResp, AUTH_FAILURE, "Error: Not authenticated");
+            outputError(httpResp, AUTH_FAILURE, "Error: Not authenticated",
+                        null);
             return;
         }
 
         if (authToken == null) {
             CMS.debug("GetTransportCert: authentication failed.");
-            outputError(httpResp, AUTH_FAILURE, "Error: Not authenticated");
+            outputError(httpResp, AUTH_FAILURE, "Error: Not authenticated",
+                        null);
             return;
         }
 
diff --git a/base/common/src/com/netscape/cms/servlet/csadmin/ImportTransportCert.java b/base/common/src/com/netscape/cms/servlet/csadmin/ImportTransportCert.java
index 859ba84..b0dc966 100644
--- a/base/common/src/com/netscape/cms/servlet/csadmin/ImportTransportCert.java
+++ b/base/common/src/com/netscape/cms/servlet/csadmin/ImportTransportCert.java
@@ -86,13 +86,15 @@ public class ImportTransportCert extends CMSServlet {
             log(ILogger.LL_FAILURE,
                     CMS.getLogMessage("CMSGW_ERR_BAD_SERV_OUT_STREAM", "",
                             e.toString()));
-            outputError(httpResp, AUTH_FAILURE, "Error: Not authenticated");
+            outputError(httpResp, AUTH_FAILURE, "Error: Not authenticated",
+                        null);
             return;
         }
 
         if (authToken == null) {
             CMS.debug("ImportTransportCert: authentication failed.");
-            outputError(httpResp, AUTH_FAILURE, "Error: Not authenticated");
+            outputError(httpResp, AUTH_FAILURE, "Error: Not authenticated",
+                        null);
             return;
         }
 
diff --git a/base/common/src/com/netscape/cms/servlet/csadmin/RegisterUser.java b/base/common/src/com/netscape/cms/servlet/csadmin/RegisterUser.java
index d2cf540..74197a4 100644
--- a/base/common/src/com/netscape/cms/servlet/csadmin/RegisterUser.java
+++ b/base/common/src/com/netscape/cms/servlet/csadmin/RegisterUser.java
@@ -103,13 +103,15 @@ public class RegisterUser extends CMSServlet {
             log(ILogger.LL_FAILURE,
                     CMS.getLogMessage("CMSGW_ERR_BAD_SERV_OUT_STREAM", "",
                             e.toString()));
-            outputError(httpResp, AUTH_FAILURE, "Error: Not authenticated");
+            outputError(httpResp, AUTH_FAILURE, "Error: Not authenticated",
+                        null);
             return;
         }
 
         if (authToken == null) {
             CMS.debug("RegisterUser: authentication failed.");
-            outputError(httpResp, AUTH_FAILURE, "Error: Not authenticated");
+            outputError(httpResp, AUTH_FAILURE, "Error: Not authenticated",
+                        null);
             return;
         }
 
diff --git a/base/common/src/com/netscape/cms/servlet/csadmin/UpdateConnector.java b/base/common/src/com/netscape/cms/servlet/csadmin/UpdateConnector.java
index 240468d..b6f6476 100644
--- a/base/common/src/com/netscape/cms/servlet/csadmin/UpdateConnector.java
+++ b/base/common/src/com/netscape/cms/servlet/csadmin/UpdateConnector.java
@@ -86,13 +86,15 @@ public class UpdateConnector extends CMSServlet {
             log(ILogger.LL_FAILURE,
                     CMS.getLogMessage("CMSGW_ERR_BAD_SERV_OUT_STREAM", "",
                             e.toString()));
-            outputError(httpResp, AUTH_FAILURE, "Error: Not authenticated");
+            outputError(httpResp, AUTH_FAILURE, "Error: Not authenticated",
+                        null);
             return;
         }
 
         if (authToken == null) {
             CMS.debug("UpdateConnector: authentication failed.");
-            outputError(httpResp, AUTH_FAILURE, "Error: Not authenticated");
+            outputError(httpResp, AUTH_FAILURE, "Error: Not authenticated",
+                        null);
             return;
         }
 
diff --git a/base/common/src/com/netscape/cms/servlet/csadmin/UpdateDomainXML.java b/base/common/src/com/netscape/cms/servlet/csadmin/UpdateDomainXML.java
index fe6507f..b783c35 100644
--- a/base/common/src/com/netscape/cms/servlet/csadmin/UpdateDomainXML.java
+++ b/base/common/src/com/netscape/cms/servlet/csadmin/UpdateDomainXML.java
@@ -213,13 +213,16 @@ public class UpdateDomainXML extends CMSServlet {
             authToken = authenticate(cmsReq);
         } catch (Exception e) {
             log(ILogger.LL_FAILURE,
-                    CMS.getLogMessage("ADMIN_SRVLT_AUTH_FAILURE", e.toString()));
-            outputError(httpResp, AUTH_FAILURE, "Error: Not authenticated");
+                    CMS.getLogMessage("CMSGW_ERR_BAD_SERV_OUT_STREAM", "",
+                            e.toString()));
+            outputError(httpResp, AUTH_FAILURE, "Error: Not authenticated",
+                        null);
             return;
         }
         if (authToken == null) {
             CMS.debug("UpdateDomainXML process: authToken is null");
-            outputError(httpResp, AUTH_FAILURE, "Error: not authenticated");
+            outputError(httpResp, AUTH_FAILURE, "Error: not authenticated",
+                        null);
             return;
         }
         CMS.debug("UpdateDomainXML process: authentication done");
@@ -232,19 +235,18 @@ public class UpdateDomainXML extends CMSServlet {
         } catch (EAuthzAccessDenied e) {
             log(ILogger.LL_FAILURE,
                     CMS.getLogMessage("ADMIN_SRVLT_AUTH_FAILURE", e.toString()));
-            outputError(httpResp, AUTH_FAILURE, "Error: Not authorized");
+            outputError(httpResp, "Error: Not authorized");
             return;
         } catch (Exception e) {
             log(ILogger.LL_FAILURE,
                     CMS.getLogMessage("ADMIN_SRVLT_AUTH_FAILURE", e.toString()));
             outputError(httpResp,
-                    AUTH_FAILURE,
                     "Error: Encountered problem during authorization.");
             return;
         }
         if (authzToken == null) {
             CMS.debug("UpdateDomainXML process: authorization error");
-            outputError(httpResp, AUTH_FAILURE, "Error: Not authorized");
+            outputError(httpResp, "Error: Not authorized");
             return;
         }
 
diff --git a/base/common/src/com/netscape/cms/servlet/csadmin/UpdateNumberRange.java b/base/common/src/com/netscape/cms/servlet/csadmin/UpdateNumberRange.java
index eb88a1c..b99a298 100644
--- a/base/common/src/com/netscape/cms/servlet/csadmin/UpdateNumberRange.java
+++ b/base/common/src/com/netscape/cms/servlet/csadmin/UpdateNumberRange.java
@@ -89,7 +89,8 @@ public class UpdateNumberRange extends CMSServlet {
         IAuthToken authToken = authenticate(cmsReq);
         if (authToken == null) {
             CMS.debug("UpdateNumberRange process: authToken is null");
-            outputError(httpResp, AUTH_FAILURE, "Error: not authenticated");
+            outputError(httpResp, AUTH_FAILURE, "Error: not authenticated",
+                        null);
         }
 
         AuthzToken authzToken = null;
diff --git a/base/common/src/com/netscape/cms/servlet/csadmin/UpdateOCSPConfig.java b/base/common/src/com/netscape/cms/servlet/csadmin/UpdateOCSPConfig.java
index e1b33ea..3d564e9 100644
--- a/base/common/src/com/netscape/cms/servlet/csadmin/UpdateOCSPConfig.java
+++ b/base/common/src/com/netscape/cms/servlet/csadmin/UpdateOCSPConfig.java
@@ -74,7 +74,8 @@ public class UpdateOCSPConfig extends CMSServlet {
         IAuthToken authToken = authenticate(cmsReq);
         if (authToken == null) {
             CMS.debug("UpdateOCSPConfig process: authToken is null");
-            outputError(httpResp, AUTH_FAILURE, "Error: not authenticated");
+            outputError(httpResp, AUTH_FAILURE, "Error: not authenticated",
+                        null);
         }
 
         AuthzToken authzToken = null;
diff --git a/base/deploy/src/scriptlets/configuration.py b/base/deploy/src/scriptlets/configuration.py
index f7a9a66..7e99dd4 100644
--- a/base/deploy/src/scriptlets/configuration.py
+++ b/base/deploy/src/scriptlets/configuration.py
@@ -147,6 +147,7 @@ class PkiScriptlet(pkiscriptlet.AbstractBasePkiScriptlet):
     def destroy(self):
         config.pki_log.info(log.CONFIGURATION_DESTROY_1, __name__,
                             extra=config.PKI_INDENTATION_LEVEL_1)
+        util.security_domain.deregister()
         if not config.pki_dry_run_flag:
             if master['pki_subsystem'] in config.PKI_APACHE_SUBSYSTEMS and\
                util.instance.apache_instance_subsystems() == 1:
diff --git a/base/deploy/src/scriptlets/pkihelper.py b/base/deploy/src/scriptlets/pkihelper.py
index c0dc14d..038198a 100644
--- a/base/deploy/src/scriptlets/pkihelper.py
+++ b/base/deploy/src/scriptlets/pkihelper.py
@@ -27,6 +27,7 @@ import os
 import fileinput
 import pickle
 import random
+import re
 import shutil
 import string
 import subprocess
@@ -46,6 +47,7 @@ from pkiconfig import pki_slots_dict as slots
 from pkiconfig import pki_selinux_config_ports as ports
 import pkimanifest as manifest
 import pkimessages as log
+from pkiparser import read_simple_configuration_file
 
 
 # PKI Deployment Helper Functions
@@ -2400,6 +2402,193 @@ class certutil:
         return
 
 
+# PKI Deployment Security Domain Class
+class security_domain:
+    def deregister(self, critical_failure=False):
+        try:
+            # process this PKI subsystem instance's 'CS.cfg'
+            cs_cfg = read_simple_configuration_file(master['pki_target_cs_cfg'])
+
+            # assign key name/value pairs
+            machinename = cs_cfg.get('service.machineName')
+            sport = cs_cfg.get('service.securityDomainPort')
+            ncsport = cs_cfg.get('service.non_clientauth_securePort', '')
+            sechost = cs_cfg.get('securitydomain.host')
+            httpport = cs_cfg.get('securitydomain.httpport')
+            seceeport = cs_cfg.get('securitydomain.httpseeport')
+            secagentport = cs_cfg.get('securitydomain.httpsagentport')
+            secadminport = cs_cfg.get('securitydomain.httpsadminport')
+            secname = cs_cfg.get('securitydomain.name', 'unknown')
+            secselect = cs_cfg.get('securitydomain.select')
+            adminsport = cs_cfg.get('pkicreate.admin_secure_port', '')
+            typeval = cs_cfg.get('cs.type', '')
+            agentsport = cs_cfg.get('pkicreate.agent_secure_port', '')
+            token_pwd = None
+
+            # retrieve subsystem nickname
+            subsystemnick_param = typeval.lower() + ".cert.subsystem.nickname"
+            subsystemnick = cs_cfg.get(subsystemnick_param)
+            if subsystemnick is None:
+                config.pki_log.warning(
+                    log.PKIHELPER_SECURITY_DOMAIN_UPDATE_FAILURE_2,
+                    typeval,
+                    secname,
+                    extra=config.PKI_INDENTATION_LEVEL_2)
+                config.pki_log.error(
+                    log.PKIHELPER_UNDEFINED_SUBSYSTEM_NICKNAME,
+                    extra=config.PKI_INDENTATION_LEVEL_2)
+                if critical_failure == True:
+                    sys.exit(-1)
+                else:
+                    return
+
+            # retrieve name of token based upon type (hardware/software)
+            if ':' in subsystemnick:
+                token_name = subsystemnick.split(':')[0]
+            else:
+                token_name = "internal"
+
+            # NOTE:  Don't check for the existence of 'httpport', as this will
+            #        be undefined for a Security Domain that has been migrated!
+            if sechost is None or\
+               seceeport is None or\
+               secagentport is None or\
+               secadminport is None:
+                config.pki_log.warning(
+                    log.PKIHELPER_SECURITY_DOMAIN_UPDATE_FAILURE_2,
+                    typeval,
+                    secname,
+                    extra=config.PKI_INDENTATION_LEVEL_2)
+                config.pki_log.error(
+                    log.PKIHELPER_SECURITY_DOMAIN_UNDEFINED,
+                    extra=config.PKI_INDENTATION_LEVEL_2)
+                if critical_failure == True:
+                    sys.exit(-1)
+                else:
+                    return
+
+            if secselect != "new":
+                # This is not a domain master, so we need to update the master
+                config.pki_log.info(log.PKIHELPER_SECURITY_DOMAIN_CONTACT_1,
+                                    secname,
+                                    extra=config.PKI_INDENTATION_LEVEL_2)
+                listval = typeval.lower() + "List"
+                urlheader = "https://{}:{}".format(sechost, seceeport)
+                urlagentheader = "https://{}:{}".format(sechost, secagentport)
+                urladminheader = "https://{}:{}".format(sechost, secadminport)
+                updateURL = "/ca/agent/ca/updateDomainXML"
+
+                # process this PKI subsystem instance's 'password.conf'
+                #
+                #     REMINDER:  NEVER log this 'sensitive' information!
+                #
+                if os.path.exists(master['pki_shared_password_conf']) and\
+                   os.path.isfile(master['pki_shared_password_conf']) and\
+                   os.access(master['pki_shared_password_conf'], os.R_OK):
+                    tokens = read_simple_configuration_file(
+                                 master['pki_shared_password_conf'])
+                    hardware_token = "hardware-" + token_name
+                    if tokens.has_key(hardware_token):
+                        token_name = hardware_token
+                        token_pwd = tokens[hardware_token]
+                    elif tokens.has_key(token_name):
+                        token_pwd = tokens[token_name]
+
+                if token_pwd is None or token_pwd == '':
+                    # 'pkiremove' prompts with
+                    # "What is the password for this token?"
+                    config.pki_log.warning(
+                        log.PKIHELPER_SECURITY_DOMAIN_UPDATE_FAILURE_2,
+                        typeval,
+                        secname,
+                        extra=config.PKI_INDENTATION_LEVEL_2)
+                    config.pki_log.error(log.PKIHELPER_PASSWORD_NOT_FOUND_1,
+                                         token_name,
+                                         extra=config.PKI_INDENTATION_LEVEL_2)
+                    if critical_failure == True:
+                        sys.exit(-1)
+                    else:
+                        return
+
+                params = "name=" + "\"" + master['pki_instance_path'] + "\"" +\
+                         "&type=" + str(typeval) +\
+                         "&list=" + str(listval) +\
+                         "&host=" + str(machinename) +\
+                         "&sport=" + str(sport) +\
+                         "&ncsport=" + str(ncsport) +\
+                         "&adminsport=" + str(adminsport) +\
+                         "&agentsport=" + str(agentsport) +\
+                         "&operation=remove"
+
+                # Compose this "sslget" command
+                #
+                #     REMINDER:  NEVER log this command as it contains
+                #                an exposed password in plaintext!
+                #
+                command = "/usr/bin/sslget -n '{}' -p '{}' -d '{}' -e '{}' "\
+                          "-v -r '{}' {}:{} 2>&1".format(
+                              subsystemnick, token_pwd,
+                              master['pki_database_path'],
+                              params, updateURL,
+                              sechost, secagentport)
+                # update domainXML
+                if not config.pki_dry_run_flag:
+                    # Execute this "sslget" command
+                    output = subprocess.check_output(command,
+                                                     stderr=subprocess.STDOUT,
+                                                     shell=True)
+                    config.pki_log.debug(log.PKIHELPER_SSLGET_OUTPUT_1,
+                                         output,
+                                         extra=config.PKI_INDENTATION_LEVEL_2)
+                    # Search the output for Status
+                    status = re.findall("\<Status\>(.*?)\<\/Status\>", output)
+                    if not status:
+                        config.pki_log.warning(
+                            log.PKIHELPER_SECURITY_DOMAIN_UNREACHABLE_1,
+                            secname,
+                            extra=config.PKI_INDENTATION_LEVEL_2)
+                        if critical_failure == True:
+                            sys.exit(-1)
+                    elif status[0] != "0":
+                        error = re.findall("\<Error\>(.*?)\<\/Error\>", output)
+                        if not error:
+                            error = ""
+                        config.pki_log.warning(
+                            log.PKIHELPER_SECURITY_DOMAIN_UNREGISTERED_2,
+                            typeval,
+                            secname,
+                            extra=config.PKI_INDENTATION_LEVEL_2)
+                        config.pki_log.error(
+                            log.PKIHELPER_SECURITY_DOMAIN_UPDATE_FAILURE_3,
+                            typeval,
+                            secname,
+                            error[0],
+                            extra=config.PKI_INDENTATION_LEVEL_2)
+                        if critical_failure == True:
+                            sys.exit(-1)
+                    else:
+                        config.pki_log.info(
+                            log.PKIHELPER_SECURITY_DOMAIN_UPDATE_SUCCESS_2,
+                            typeval,
+                            secname,
+                            extra=config.PKI_INDENTATION_LEVEL_2)
+        except subprocess.CalledProcessError as exc:
+            config.pki_log.warning(
+                log.PKIHELPER_SECURITY_DOMAIN_UPDATE_FAILURE_2,
+                typeval,
+                secname,
+                extra=config.PKI_INDENTATION_LEVEL_2)
+            config.pki_log.warning(
+                log.PKIHELPER_SECURITY_DOMAIN_UNREACHABLE_1,
+                secname,
+                extra=config.PKI_INDENTATION_LEVEL_2)
+            config.pki_log.error(log.PKI_SUBPROCESS_ERROR_1, exc,
+                                 extra=config.PKI_INDENTATION_LEVEL_2)
+            if critical_failure == True:
+                sys.exit(-1)
+        return
+
+
 # PKI Deployment 'systemd' Execution Management Class
 class systemd:
     def start(self, critical_failure=True):
@@ -2535,5 +2724,6 @@ symlink = symlink()
 war = war()
 password = password()
 certutil = certutil()
+security_domain = security_domain()
 systemd = systemd()
 jython = jython()
diff --git a/base/deploy/src/scriptlets/pkimessages.py b/base/deploy/src/scriptlets/pkimessages.py
index 2b8a225..ba3f228 100644
--- a/base/deploy/src/scriptlets/pkimessages.py
+++ b/base/deploy/src/scriptlets/pkimessages.py
@@ -216,14 +216,38 @@ PKIHELPER_NAMESPACE_RESERVED_NAME_2 = "PKI instance '%s' is already a "\
 PKIHELPER_NOISE_FILE_2 = "generating noise file called '%s' and "\
                          "filling it with '%d' random bytes"
 PKIHELPER_PASSWORD_CONF_1 = "generating '%s'"
+PKIHELPER_PASSWORD_NOT_FOUND_1 = "no password found for '%s'!"
 PKIHELPER_PKI_INSTANCE_SUBSYSTEMS_2 = "instance '%s' contains '%d' "\
                                       "PKI subsystems"
 PKIHELPER_REMOVE_FILTER_SECTION_1 = "removing filter section from '%s'"
 PKIHELPER_RM_F_1 = "rm -f %s"
 PKIHELPER_RM_RF_1 = "rm -rf %s"
 PKIHELPER_RMDIR_1 = "rmdir %s"
+PKIHELPER_SECURITY_DOMAIN_CONTACT_1 =\
+    "contacting the security domain master to update security domain '%s'"
+PKIHELPER_SECURITY_DOMAIN_UNDEFINED =\
+    "No security domain defined.\n"\
+    "If this is an unconfigured instance, then that is OK.\n"\
+    "Otherwise, manually delete the entry from the security domain master."
+PKIHELPER_SECURITY_DOMAIN_UNREACHABLE_1 =\
+    "security domain '%s' may be offline or unreachable!"
+PKIHELPER_SECURITY_DOMAIN_UNREGISTERED_2 =\
+    "this '%s' entry may not be registered with security domain '%s'!"
+PKIHELPER_SECURITY_DOMAIN_UPDATE_FAILURE_2 =\
+    "this '%s' entry will NOT be deleted from security domain '%s'!"
+PKIHELPER_SECURITY_DOMAIN_UPDATE_FAILURE_3 =\
+    "updateDomainXML FAILED to delete this '%s' entry from "\
+    "security domain '%s': '%s'"
+PKIHELPER_SECURITY_DOMAIN_UPDATE_SUCCESS_2 =\
+    "updateDomainXML SUCCESSFULLY deleted this '%s' entry from "\
+    "security domain '%s'"
 PKIHELPER_SET_MODE_1 = "setting ownerships, permissions, and acls on '%s'"
 PKIHELPER_SLOT_SUBSTITUTION_2 = "slot substitution: '%s' ==> '%s'"
+PKIHELPER_SSLGET_OUTPUT_1 = "\n"\
+    "Dump of 'sslget' output:\n"\
+    "=====================================================\n"\
+    "%s\n"\
+    "====================================================="
 PKIHELPER_SYSTEMD_COMMAND_1 = "executing '%s'"
 PKIHELPER_TOMCAT_INSTANCE_SUBSYSTEMS_2 = "instance '%s' contains '%d' "\
                                          "Tomcat PKI subsystems"
@@ -236,6 +260,7 @@ PKIHELPER_UNDEFINED_CLIENT_DATABASE_PASSWORD_2 =\
     "the randomly generated client pin MUST be used"
 PKIHELPER_UNDEFINED_CONFIGURATION_FILE_ENTRY_2 =\
     "A value for '%s' MUST be defined in '%s'"
+PKIHELPER_UNDEFINED_SUBSYSTEM_NICKNAME = "subsystem nickname not defined"
 PKIHELPER_USER_1 = "retrieving UID for '%s' . . ."
 PKIHELPER_USER_ADD_2 = "adding UID '%s' for user '%s' . . ."
 PKIHELPER_USER_ADD_DEFAULT_2 = "adding default UID '%s' for user '%s' . . ."
diff --git a/base/deploy/src/scriptlets/pkiparser.py b/base/deploy/src/scriptlets/pkiparser.py
index dd1f93b..1fe74e8 100644
--- a/base/deploy/src/scriptlets/pkiparser.py
+++ b/base/deploy/src/scriptlets/pkiparser.py
@@ -188,6 +188,32 @@ def process_command_line_arguments(argv):
     return
 
 
+# The following code is based heavily upon
+# "http://www.decalage.info/en/python/configparser"
+COMMENT_CHAR = '#'
+OPTION_CHAR =  '='
+
+def read_simple_configuration_file(filename):
+    values = {}
+    f = open(filename)
+    for line in f:
+        # First, remove comments:
+        if COMMENT_CHAR in line:
+            # split on comment char, keep only the part before
+            line, comment = line.split(COMMENT_CHAR, 1)
+        # Second, find lines with an name=value:
+        if OPTION_CHAR in line:
+            # split on name char:
+            name, value = line.split(OPTION_CHAR, 1)
+            # strip spaces:
+            name = name.strip()
+            value = value.strip()
+            # store in dictionary:
+            values[name] = value
+    f.close()
+    return values
+
+
 def read_pki_configuration_file():
     "Read configuration file sections into dictionaries"
     rv = 0
diff --git a/scripts/compose_functions b/scripts/compose_functions
index 028a64a..a6a8ed5 100644
--- a/scripts/compose_functions
+++ b/scripts/compose_functions
@@ -72,37 +72,39 @@ Usage()
 	printf "Usage:  $0 [options] <target>\n\n"
 	printf "        where <target> is one of the following:\n\n"
 	printf "            srpm         - copies a spec file from local source,\n"
-	printf "                           generates a tarball from local source, "
-	printf "and\n"
+	printf "                           generates a tarball from local source,\n"
+	printf "                           copies any relevant patches from the\n"
+	printf "                           local 'patches' directory, and\n"
 	printf "                           produces an SRPM\n"
 	printf "                           [suitable for use by 'mock']\n\n"
 	printf "            rpms         - copies a spec file from local source,\n"
-	printf "                           generates a tarball from local source, "
-	printf "and\n"
+	printf "                           generates a tarball from local source,\n"
+	printf "                           copies any relevant patches from the\n"
+	printf "                           local 'patches' directory, and\n"
 	printf "                           produces an SRPM and one or more RPMS\n"
 	printf "                           ${MESSAGE}\n\n"
 	printf "            hybrid_srpm  - copies a spec file from local source,\n"
 	printf "                           generates a tarball from local source,\n"
-	printf "                           fetches patches from the spec's URL, "
-	printf "and\n"
+	printf "                           fetches any relevant patches from the\n"
+	printf "                           spec's URL, and\n"
 	printf "                           produces an SRPM\n"
 	printf "                           [suitable for use by 'mock']\n\n"
 	printf "            hybrid_rpms  - copies a spec file from local source,\n"
 	printf "                           generates a tarball from local source,\n"
-	printf "                           fetches all patches from the spec's URL,"
-	printf " and\n"
+	printf "                           fetches any relevant patches from the\n"
+	printf "                           spec's URL, and\n"
 	printf "                           produces an SRPM and one or more RPMS\n"
 	printf "                           ${MESSAGE}\n\n"
 	printf "            patched_srpm - copies a spec file from local source,\n"
 	printf "                           fetches a tarball from the spec's URL,\n"
-	printf "                           fetches all patches from the spec's URL,"
-	printf " and\n"
+	printf "                           fetches any relevant patches from the\n"
+	printf "                           spec's URL, and\n"
 	printf "                           produces an SRPM\n"
 	printf "                           [suitable for use by 'mock']\n\n"
 	printf "            patched_rpms - copies a spec file from local source,\n"
 	printf "                           fetches a tarball from the spec's URL,\n"
-	printf "                           fetches all patches from the spec's URL,"
-	printf " and\n"
+	printf "                           fetches any relevant patches from the\n"
+	printf "                           spec's URL, and\n"
 	printf "                           produces an SRPM and one or more RPMS\n"
 	printf "                           ${MESSAGE}\n\n"
 	printf "Options:\n"
diff --git a/specs/dogtag-pki.spec b/specs/dogtag-pki.spec
index 4b07975..10a2bae 100644
--- a/specs/dogtag-pki.spec
+++ b/specs/dogtag-pki.spec
@@ -8,7 +8,7 @@
 Summary:          Dogtag Public Key Infrastructure (PKI) Suite
 Name:             dogtag-pki
 Version:          10.0.0
-Release:          %{?relprefix}5%{?prerel}%{?dist}
+Release:          %{?relprefix}6%{?prerel}%{?dist}
 # The entire source code is GPLv2 except for 'pki-tps' which is LGPLv2
 License:          GPLv2 and LGPLv2
 URL:              http://pki.fedoraproject.org/
@@ -41,7 +41,7 @@ BuildArch:        noarch
 %define pki_tks_version            10.0.0
 %define pki_tps_version            10.0.0
 %define pki_console_version        10.0.0
-%define tomcatjss_version          6.0.2
+%define tomcatjss_version          7.0.0
 %else
 %if 0%{?fedora} >= 16
 %define dogtag_pki_theme_version   10.0.0
@@ -97,6 +97,7 @@ Requires:         dogtag-pki-tps-theme >= %{dogtag_pki_theme_version}
 # of ALL Dogtag PKI core packages
 Requires:         pki-ca >= %{pki_core_version}
 Requires:         pki-common >= %{pki_core_version}
+Requires:         pki-deploy >= %{pki_core_version}
 Requires:         pki-java-tools >= %{pki_core_version}
 Requires:         pki-native-tools >= %{pki_core_version}
 Requires:         pki-selinux >= %{pki_core_version}
@@ -198,6 +199,10 @@ rm -rf %{buildroot}
 %doc README
 
 %changelog
+* Tue Aug 14 2012 Matthew Harmsen <mharmsen@redhat.com> 10.0.0-0.6.a1
+- Updated release of 'tomcatjss' to rely on Tomcat 7 for Fedora 17
+- Added 'pki-deploy' runtime dependency
+
 * Thu Jun 14 2012 Matthew Harmsen <mharmsen@redhat.com> 10.0.0-0.5.a1
 - Updated release of 'tomcatjss' to rely on Tomcat 7 for Fedora 18
 
diff --git a/specs/pki-core.spec b/specs/pki-core.spec
index 5d682be..e9e941b 100644
--- a/specs/pki-core.spec
+++ b/specs/pki-core.spec
@@ -14,7 +14,7 @@ distutils.sysconfig import get_python_lib; print(get_python_lib(1))")}
 
 Name:             pki-core
 Version:          10.0.0
-Release:          %{?relprefix}22%{?prerel}%{?dist}
+Release:          %{?relprefix}23%{?prerel}%{?dist}
 Summary:          Certificate System - PKI Core Components
 URL:              http://pki.fedoraproject.org/
 License:          GPLv2
@@ -73,12 +73,6 @@ BuildRequires:    tomcatjss >= 2.0.0
 %endif
 %endif
 %endif
-# Add the following build-time requirements to support the "pki-deploy" package
-BuildRequires:    pki-common-theme
-BuildRequires:    pki-ca-theme
-BuildRequires:    pki-kra-theme
-BuildRequires:    pki-ocsp-theme
-BuildRequires:    pki-tks-theme
 
 Source0:          http://pki.fedoraproject.org/pki/sources/%{name}/%{name}-%{version}%{?prerel}.tar.gz
 
@@ -117,7 +111,6 @@ to manage enterprise Public Key Infrastructure (PKI) deployments.      \
                                                                        \
 PKI Core contains ALL top-level java-based Tomcat PKI components:      \
                                                                        \
-  * pki-deploy                                                         \
   * pki-setup                                                          \
   * pki-symkey                                                         \
   * pki-native-tools                                                   \
@@ -128,6 +121,7 @@ PKI Core contains ALL top-level java-based Tomcat PKI components:      \
   * pki-common                                                         \
   * pki-common-javadoc                                                 \
   * pki-selinux                                                        \
+  * pki-deploy                                                         \
   * pki-ca                                                             \
   * pki-kra                                                            \
   * pki-ocsp                                                           \
@@ -175,20 +169,6 @@ ONLY ONE of the following "Mutually-Exclusive" PKI Theme packages:     \
 %description %{overview}
 
 
-%package -n       pki-deploy
-Summary:          Certificate System - PKI Instance Deployment Scripts
-Group:            System Environment/Base
-
-BuildArch:        noarch
-
-%description -n   pki-deploy
-PKI deployment scripts are used to create and remove instances from PKI deployments.
-
-This package is a part of the PKI Core used by the Certificate System.
-
-%{overview}
-
-
 %package -n       pki-setup
 Summary:          Certificate System - PKI Instance Creation & Removal Scripts
 Group:            System Environment/Base
@@ -356,9 +336,8 @@ Requires:         java >= 1:1.6.0
 Requires:         javassist
 Requires:         jettison
 Requires:         jython >= 2.2.1
-Requires:         pki-common-theme >= 9.0.0
+Requires:         pki-common-theme >= 10.0.0
 Requires:         pki-java-tools = %{version}-%{release}
-Requires:         pki-deploy = %{version}-%{release}
 Requires:         pki-setup = %{version}-%{release}
 Requires:         %{_javadir}/ldapjdk.jar
 Requires:         %{_javadir}/velocity.jar
@@ -370,6 +349,7 @@ Requires:         %{_javadir}/xml-commons-resolver.jar
 Requires:         velocity
 %if 0%{?fedora} >= 17
 Requires:         resteasy >= 2.3.2-1
+Requires:         tomcatjss >= 7.0.0
 %endif
 %if 0%{?fedora} >= 18
 Requires:         apache-commons-lang
@@ -467,6 +447,23 @@ This package is a part of the PKI Core used by the Certificate System.
 %{overview}
 
 
+%package -n       pki-deploy
+Summary:          Certificate System - PKI Instance Deployment Scripts
+Group:            System Environment/Base
+
+BuildArch:        noarch
+
+Requires:         pki-common = %{version}-%{release}
+
+%description -n   pki-deploy
+PKI deployment scripts are used to create and remove instances from
+PKI deployments.
+
+This package is a part of the PKI Core used by the Certificate System.
+
+%{overview}
+
+
 %package -n       pki-ca
 Summary:          Certificate System - Certificate Authority
 Group:            System Environment/Daemons
@@ -474,8 +471,8 @@ Group:            System Environment/Daemons
 BuildArch:        noarch
 
 Requires:         java >= 1:1.6.0
-Requires:         pki-ca-theme >= 9.0.0
-Requires:         pki-common = %{version}-%{release}
+Requires:         pki-ca-theme >= 10.0.0
+Requires:         pki-deploy = %{version}-%{release}
 Requires:         pki-selinux = %{version}-%{release}
 %if 0%{?fedora} >= 16
 Requires(post):   systemd-units
@@ -523,8 +520,8 @@ Group:            System Environment/Daemons
 BuildArch:        noarch
 
 Requires:         java >= 1:1.6.0
-Requires:         pki-kra-theme >= 9.0.0
-Requires:         pki-common = %{version}-%{release}
+Requires:         pki-kra-theme >= 10.0.0
+Requires:         pki-deploy = %{version}-%{release}
 Requires:         pki-selinux = %{version}-%{release}
 %if 0%{?fedora} >= 16
 Requires(post):   systemd-units
@@ -578,8 +575,8 @@ Group:            System Environment/Daemons
 BuildArch:        noarch
 
 Requires:         java >= 1:1.6.0
-Requires:         pki-ocsp-theme >= 9.0.0
-Requires:         pki-common = %{version}-%{release}
+Requires:         pki-ocsp-theme >= 10.0.0
+Requires:         pki-deploy = %{version}-%{release}
 Requires:         pki-selinux = %{version}-%{release}
 %if 0%{?fedora} >= 16
 Requires(post):   systemd-units
@@ -640,8 +637,8 @@ Group:            System Environment/Daemons
 BuildArch:        noarch
 
 Requires:         java >= 1:1.6.0
-Requires:         pki-tks-theme >= 9.0.0
-Requires:         pki-common = %{version}-%{release}
+Requires:         pki-tks-theme >= 10.0.0
+Requires:         pki-deploy = %{version}-%{release}
 Requires:         pki-selinux = %{version}-%{release}
 Requires:         pki-symkey = %{version}-%{release}
 %if 0%{?fedora} >= 16
@@ -1127,41 +1124,6 @@ fi
 ##        PKI deployment process
 %endif
 
-%files -n pki-deploy
-%defattr(-,root,root,-)
-%doc base/deploy/LICENSE
-%{_bindir}/pkispawn
-%{_bindir}/pkidestroy
-#%{_bindir}/pki-setup-proxy
-%dir %{python_sitelib}/pki
-%{python_sitelib}/pki/_*
-%{python_sitelib}/pki/deployment/
-%dir %{_datadir}/pki
-%dir %{_datadir}/pki/deployment
-%{_datadir}/pki/deployment/config/
-%dir %{_datadir}/pki/deployment/spawn
-%{_datadir}/pki/deployment/spawn/ca/
-%{_datadir}/pki/deployment/spawn/kra/
-%{_datadir}/pki/deployment/spawn/ocsp/
-%{_datadir}/pki/deployment/spawn/ra/
-%{_datadir}/pki/deployment/spawn/tks/
-%{_datadir}/pki/deployment/spawn/tps/
-%dir %{_datadir}/pki/deployment/destroy
-%{_datadir}/pki/deployment/destroy/ca/
-%{_datadir}/pki/deployment/destroy/kra/
-%{_datadir}/pki/deployment/destroy/ocsp/
-%{_datadir}/pki/deployment/destroy/ra/
-%{_datadir}/pki/deployment/destroy/tks/
-%{_datadir}/pki/deployment/destroy/tps/
-%dir %{_datadir}/pki/scripts
-%{_datadir}/pki/scripts/operations
-%dir %{_localstatedir}/lock/pki
-%dir %{_localstatedir}/run/pki
-%if 0%{?fedora} >= 16
-%{_bindir}/pkidaemon
-%endif
-
-
 %files -n pki-setup
 %defattr(-,root,root,-)
 %doc base/setup/LICENSE
@@ -1297,6 +1259,41 @@ fi
 %{_datadir}/selinux/modules/pki.pp
 
 
+%files -n pki-deploy
+%defattr(-,root,root,-)
+%doc base/deploy/LICENSE
+%{_bindir}/pkispawn
+%{_bindir}/pkidestroy
+#%{_bindir}/pki-setup-proxy
+%dir %{python_sitelib}/pki
+%{python_sitelib}/pki/_*
+%{python_sitelib}/pki/deployment/
+%dir %{_datadir}/pki
+%dir %{_datadir}/pki/deployment
+%{_datadir}/pki/deployment/config/
+%dir %{_datadir}/pki/deployment/spawn
+%{_datadir}/pki/deployment/spawn/ca/
+%{_datadir}/pki/deployment/spawn/kra/
+%{_datadir}/pki/deployment/spawn/ocsp/
+%{_datadir}/pki/deployment/spawn/ra/
+%{_datadir}/pki/deployment/spawn/tks/
+%{_datadir}/pki/deployment/spawn/tps/
+%dir %{_datadir}/pki/deployment/destroy
+%{_datadir}/pki/deployment/destroy/ca/
+%{_datadir}/pki/deployment/destroy/kra/
+%{_datadir}/pki/deployment/destroy/ocsp/
+%{_datadir}/pki/deployment/destroy/ra/
+%{_datadir}/pki/deployment/destroy/tks/
+%{_datadir}/pki/deployment/destroy/tps/
+%dir %{_datadir}/pki/scripts
+%{_datadir}/pki/scripts/operations
+%dir %{_localstatedir}/lock/pki
+%dir %{_localstatedir}/run/pki
+%if 0%{?fedora} >= 16
+%{_bindir}/pkidaemon
+%endif
+
+
 %files -n pki-ca
 %defattr(-,root,root,-)
 %doc base/ca/LICENSE
@@ -1426,6 +1423,12 @@ fi
 
 
 %changelog
+* Thu Aug 16 2012 Matthew Harmsen <mharmsen@redhat.com> 10.0.0-0.23.a1
+- Updated release of 'tomcatjss' to rely on Tomcat 7 for Fedora 17
+- Changed Dogtag 10 build-time and runtime requirements for 'pki-deploy'
+- Altered PKI Package Dependency Chain (top-to-bottom):
+  pki-ca, pki-kra, pki-ocsp, pki-tks --> pki-deploy --> pki-common
+
 * Mon Aug 13 2012 Endi S. Dewata <edewata@redhat.com> 10.0.0-0.22.a1
 - Added pki-client.jar.
 
diff --git a/specs/pki-ra.spec b/specs/pki-ra.spec
index b52a4f4..e500826 100644
--- a/specs/pki-ra.spec
+++ b/specs/pki-ra.spec
@@ -7,7 +7,7 @@
 
 Name:             pki-ra
 Version:          10.0.0
-Release:          %{?relprefix}2%{?prerel}%{?dist}
+Release:          %{?relprefix}3%{?prerel}%{?dist}
 Summary:          Certificate System - Registration Authority
 URL:              http://pki.fedoraproject.org/
 License:          GPLv2
@@ -29,9 +29,10 @@ BuildRequires:    nss-devel
 Requires:         mod_nss >= 1.0.8
 Requires:         mod_perl >= 1.99_16
 Requires:         mod_revocator >= 1.0.3
-Requires:         pki-native-tools
-Requires:         pki-ra-theme >= 9.0.0
-Requires:         pki-selinux
+Requires:         pki-deploy >= 10.0.0
+Requires:         pki-native-tools >= 10.0.0
+Requires:         pki-ra-theme >= 10.0.0
+Requires:         pki-selinux >= 10.0.0
 Requires:         pki-setup
 Requires:         perl-DBD-SQLite
 Requires:         sqlite
@@ -244,6 +245,9 @@ fi
 
 
 %changelog
+* Thu Aug 16 2012 Matthew Harmsen <mharmsen@redhat.com> 10.0.0-0.3.a1
+- Added 'pki-deploy' runtime dependency
+
 * Mon Aug 13 2012 Ade Lee <alee@redhat.com> 10.0.0-0.2.a1
 - Added systemd scripts
 - Ported config files and init scripts to apache 2.4
diff --git a/specs/pki-tps.spec b/specs/pki-tps.spec
index c75711d..6845dd4 100644
--- a/specs/pki-tps.spec
+++ b/specs/pki-tps.spec
@@ -7,7 +7,7 @@
 
 Name:             pki-tps
 Version:          10.0.0
-Release:          %{?relprefix}2%{?prerel}%{?dist}
+Release:          %{?relprefix}3%{?prerel}%{?dist}
 Summary:          Certificate System - Token Processing System
 URL:              http://pki.fedoraproject.org/
 License:          LGPLv2
@@ -24,7 +24,7 @@ BuildRequires:    cmake
 BuildRequires:    apr-devel
 BuildRequires:    apr-util-devel
 BuildRequires:    cyrus-sasl-devel
-BuildRequires:    httpd-devel
+BuildRequires:    httpd-devel >= 2.4.2
 BuildRequires:    openldap-devel
 BuildRequires:    nspr-devel
 BuildRequires:    nss-devel
@@ -38,10 +38,11 @@ Requires:         mod_perl
 Requires:         mod_revocator
 Requires:         openldap-clients
 Requires:         perl-Mozilla-LDAP
-Requires:         pki-native-tools
-Requires:         pki-selinux
+Requires:         pki-deploy >= 10.0.0
+Requires:         pki-native-tools >= 10.0.0
+Requires:         pki-selinux >= 10.0.0
 Requires:         pki-setup
-Requires:         pki-tps-theme >= 9.0.0
+Requires:         pki-tps-theme >= 10.0.0
 
 %if 0%{?fedora} >= 16
 Requires(post):   systemd-units
@@ -279,6 +280,10 @@ fi
 
 
 %changelog
+* Thu Aug 16 2012 Matthew Harmsen <mharmsen@redhat.com> 10.0.0-0.3.a1
+- Changed 'httpd-devel' build-time dependency to require '2.4.2'
+- Added 'pki-deploy' runtime dependency
+
 * Mon Aug 13 2012 Ade Lee <alee@redhat.com> 10.0.0-0.2.a1
 - Added systemd scripts
 - Ported config files and init scripts to apache 2.4
-- 
1.7.10.4

