From eb33cf6ec99879a6405269a3a08d383c47de7fbe Mon Sep 17 00:00:00 2001
From: Matthew Harmsen <mharmsen@redhat.com>
Date: Tue, 14 Aug 2012 17:55: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
---
 .../cms/servlet/csadmin/UpdateDomainXML.java       |    9 +-
 base/deploy/src/scriptlets/configuration.py        |    1 +
 base/deploy/src/scriptlets/pkihelper.py            |  146 ++++++++++++++++++++
 base/deploy/src/scriptlets/pkimessages.py          |   19 +++
 base/deploy/src/scriptlets/pkiparser.py            |   26 ++++
 specs/dogtag-pki.spec                              |    7 +-
 specs/pki-core.spec                                |    6 +-
 7 files changed, 206 insertions(+), 8 deletions(-)

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..79217c6 100644
--- a/base/common/src/com/netscape/cms/servlet/csadmin/UpdateDomainXML.java
+++ b/base/common/src/com/netscape/cms/servlet/csadmin/UpdateDomainXML.java
@@ -214,12 +214,12 @@ public class UpdateDomainXML extends CMSServlet {
         } catch (Exception e) {
             log(ILogger.LL_FAILURE,
                     CMS.getLogMessage("ADMIN_SRVLT_AUTH_FAILURE", e.toString()));
-            outputError(httpResp, AUTH_FAILURE, "Error: Not authenticated");
+            outputError(httpResp, "Error: Not authenticated");
             return;
         }
         if (authToken == null) {
             CMS.debug("UpdateDomainXML process: authToken is null");
-            outputError(httpResp, AUTH_FAILURE, "Error: not authenticated");
+            outputError(httpResp, "Error: not authenticated");
             return;
         }
         CMS.debug("UpdateDomainXML process: authentication done");
@@ -232,19 +232,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/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..4bec003 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,149 @@ 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.setdefault('service.machineName')
+            sport = cs_cfg.setdefault('service.securityDomainPort')
+            ncsport = cs_cfg.setdefault('service.non_clientauth_securePort', '')
+            sechost = cs_cfg.setdefault('securitydomain.host')
+            httpport = cs_cfg.setdefault('securitydomain.httpport')
+            seceeport = cs_cfg.setdefault('securitydomain.httpseeport')
+            secagentport = cs_cfg.setdefault('securitydomain.httpsagentport')
+            secadminport = cs_cfg.setdefault('securitydomain.httpsadminport')
+            secselect = cs_cfg.setdefault('securitydomain.select')
+            adminsport = cs_cfg.setdefault('pkicreate.admin_secure_port', '')
+            typeval = cs_cfg.setdefault('cs.type', '')
+            agentsport = cs_cfg.setdefault('pkicreate.agent_secure_port', '')
+            token_pwd = None
+
+            # retrieve subsystem nickname
+            subsystemnick_param = typeval.lower() + ".cert.subsystem.nickname"
+            subsystemnick = cs_cfg.setdefault(subsystemnick_param)
+            if subsystemnick is None:
+                config.pki_log.error(
+                    log.PKIHELPER_UNDEFINED_SUBSYSTEM_NICKNAME,
+                    extra=config.PKI_INDENTATION_LEVEL_2)
+                sys.exit(-1)
+
+            nick = subsystemnick.split(' ', 2)
+            if ':' in nick[0]:
+                token_name = nick[0].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.error(
+                    log.PKIHELPER_UNDEFINED_SECURITY_DOMAIN,
+                    extra=config.PKI_INDENTATION_LEVEL_2)
+                sys.exit(-1)
+
+            if secselect != "new":
+                # This is not a domain master, so we need to update the master
+                config.pki_log.info(log.PKIHELPER_CONTACT_SECURITY_DOMAIN,
+                                    extra=config.PKI_INDENTATION_LEVEL_2)
+                listval = master['pki_subsystem'].lower() + "List"
+                urlheader = "https://" + sechost + ":" + seceeport
+                urlagentheader = "https://" + sechost + ":" + secagentport
+                urladminheader = "https://" + 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.error(log.PKIHELPER_PASSWORD_NOT_FOUND_1,
+                                         token_name,
+                                         extra=config.PKI_INDENTATION_LEVEL_2)
+                    sys.exit(-1)
+
+                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" + " " + "\"" + subsystemnick + "\"" + " " +\
+                          "-p" + " " + "\"" + token_pwd + "\"" + " " +\
+                          "-d" + " " + master['pki_database_path'] + " " +\
+                          "-e" + " " + "\"" + params + "\"" + " " +\
+                          "-v" + " " +\
+                          "-r" + " " + "\"" + updateURL + "\"" + " " +\
+                          sechost + ":" + secagentport + " " +\
+                          "2>&1"
+                # 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 status[0] != "0":
+                        error = re.findall("\<Error\>(.*?)\<\/Error\>", output)
+                        if critical_failure == True:
+                            config.pki_log.error(
+                                log.PKIHELPER_SECURITY_DOMAIN_UPDATE_FAILURE_2,
+                                master['pki_subsystem'],
+                                error[0],
+                                extra=config.PKI_INDENTATION_LEVEL_2)
+                            sys.exit(-1)
+                        else:
+                            config.pki_log.warning(
+                                log.PKIHELPER_SECURITY_DOMAIN_UPDATE_FAILURE_2,
+                                master['pki_subsystem'],
+                                error[0],
+                                extra=config.PKI_INDENTATION_LEVEL_2)
+                    else:
+                        config.pki_log.info(
+                            log.PKIHELPER_SECURITY_DOMAIN_UPDATE_SUCCESS_1,
+                            master['pki_subsystem'],
+                            extra=config.PKI_INDENTATION_LEVEL_2)
+        except subprocess.CalledProcessError as exc:
+            config.pki_log.error(log.PKI_SUBPROCESS_ERROR_1, exc,
+                                 extra=config.PKI_INDENTATION_LEVEL_2)
+            sys.exit(-1)
+
+
 # PKI Deployment 'systemd' Execution Management Class
 class systemd:
     def start(self, critical_failure=True):
@@ -2535,5 +2680,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..cfff3a6 100644
--- a/base/deploy/src/scriptlets/pkimessages.py
+++ b/base/deploy/src/scriptlets/pkimessages.py
@@ -167,6 +167,8 @@ PKIHELPER_CHOWN_H_3 = "chown -h %s:%s %s"
 PKIHELPER_COMMAND_LINE_PARAMETER_MISMATCH_2 = "the command-line parameter "\
                                               "'%s' DOES NOT match the "\
                                               "configuration file value '%s'!"
+PKIHELPER_CONTACT_SECURITY_DOMAIN =\
+    "contacting the security domain master to update the security domain"
 PKIHELPER_COPY_WITH_SLOT_SUBSTITUTION_2 = "copying '%s' --> '%s' "\
                                           "with slot substitution"
 PKIHELPER_CP_P_2 = "cp -p %s %s"
@@ -216,14 +218,26 @@ 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_UPDATE_FAILURE_2 =\
+    "updateDomainXML FAILED to delete the '%s' entry from the "\
+    "Security Domain: '%s'"
+PKIHELPER_SECURITY_DOMAIN_UPDATE_SUCCESS_1 =\
+    "updateDomainXML SUCCESSFULLY deleted the '%s' entry from the "\
+    "Security Domain"
 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 +250,11 @@ 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_SECURITY_DOMAIN =\
+    "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_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/specs/dogtag-pki.spec b/specs/dogtag-pki.spec
index 4b07975..de8973a 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
@@ -198,6 +198,9 @@ 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
+
 * 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 87c610c..c0fa509 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}21%{?prerel}%{?dist}
+Release:          %{?relprefix}22%{?prerel}%{?dist}
 Summary:          Certificate System - PKI Core Components
 URL:              http://pki.fedoraproject.org/
 License:          GPLv2
@@ -370,6 +370,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
@@ -1424,6 +1425,9 @@ fi
 
 
 %changelog
+* Tue Aug 14 2012 Matthew Harmsen <mharmsen@redhat.com> 10.0.0-0.22.a1
+- Updated release of 'tomcatjss' to rely on Tomcat 7 for Fedora 17 or later
+
 * Fri Jul 27 2012 Endi S. Dewata <edewata@redhat.com> 10.0.0-0.21.a1
 - Merged pki-jndi-realm.jar into pki-cmscore.jar.
 
-- 
1.7.7.6

