>From 5c55ddb0a3bed84e2687ed20d7586792c03d6813 Mon Sep 17 00:00:00 2001
From: Ade Lee <alee@redhat.com>
Date: Thu, 27 Mar 2014 11:08:32 -0400
Subject: [PATCH] Share subsytem cert in shared tomcat instances

In shared tomcat instances, we need to share the subsystem cert and
not create a new one for each additional subsystem added to the instance.
In addtion, if the instances share the same database, then only one
pkidebuser should be created with the relevant subsystem cert and seeAlso
attribute.

Ticket 893
---
 .../certsrv/system/ConfigurationRequest.java       | 45 +++++++++++++
 .../cms/servlet/csadmin/ConfigurationUtils.java    |  4 +-
 .../dogtagpki/server/rest/SystemConfigService.java | 78 ++++++++++++++++++----
 base/server/etc/default.cfg                        | 11 +++
 .../python/pki/server/deployment/pkihelper.py      | 32 +++++++++
 5 files changed, 155 insertions(+), 15 deletions(-)

diff --git a/base/common/src/com/netscape/certsrv/system/ConfigurationRequest.java b/base/common/src/com/netscape/certsrv/system/ConfigurationRequest.java
index 23f9676fe4934d1f762b2e7b3fe7b4aa5b4c5807..96a8e13c33dcb9f406b5768828e40e10afce7b93 100644
--- a/base/common/src/com/netscape/certsrv/system/ConfigurationRequest.java
+++ b/base/common/src/com/netscape/certsrv/system/ConfigurationRequest.java
@@ -88,6 +88,11 @@ public class ConfigurationRequest {
     // TKS/TPS shared secret parameters
     private static final String IMPORT_SHARED_SECRET = "importSharedSecret";
 
+    // Parameters for shared tomcat instances
+    private static final String GENERATE_SUBSYSTEM_CERT="generateSubsystemCert";
+    private static final String SHARE_DB = "shareDB";
+    private static final String SHARED_DBUSER_DN = "sharedDbuserDN";
+
     //defaults
     public static final String TOKEN_DEFAULT = "Internal Key Storage Token";
     public static final String NEW_DOMAIN = "newdomain";
@@ -252,6 +257,15 @@ public class ConfigurationRequest {
     @XmlElement(defaultValue="false")
     protected String importSharedSecret;
 
+    @XmlElement(defaultValue="true")
+    protected String generateSubsystemCert;
+
+    @XmlElement(defaultValue="false")
+    protected String shareDB;
+
+    @XmlElement
+    protected String sharedDBUserDN;
+
     public ConfigurationRequest() {
         // required for JAXB
     }
@@ -309,6 +323,10 @@ public class ConfigurationRequest {
         kraUri = form.getFirst(KRA_URI);
         enableServerSideKeyGen = form.getFirst(ENABLE_SERVER_SIDE_KEYGEN);
         importSharedSecret = form.getFirst(IMPORT_SHARED_SECRET);
+
+        generateSubsystemCert = form.getFirst(GENERATE_SUBSYSTEM_CERT);
+        shareDB = form.getFirst(SHARE_DB);
+        sharedDBUserDN = form.getFirst(SHARED_DBUSER_DN);
     }
 
     public String getSubsystemName() {
@@ -905,6 +923,30 @@ public class ConfigurationRequest {
         this.importSharedSecret = importSharedSecret;
     }
 
+    public boolean getGenerateSubsystemCert() {
+        return generateSubsystemCert != null && generateSubsystemCert.equalsIgnoreCase("true");
+    }
+
+    public void setGenerateSubsystemCert(String generateSubsystemCert) {
+        this.generateSubsystemCert = generateSubsystemCert;
+    }
+
+    public boolean getShareDB() {
+        return (shareDB != null && shareDB.equalsIgnoreCase("true"));
+    }
+
+    public void setShareDB(String shareDB) {
+        this.shareDB = shareDB;
+    }
+
+    public String getSharedDBUserDN() {
+        return sharedDBUserDN;
+    }
+
+    public void setSharedDBUserDN(String sharedDBUserDN) {
+        this.sharedDBUserDN = sharedDBUserDN;
+    }
+
     @Override
     public String toString() {
         return "ConfigurationRequest [pin=XXXX" +
@@ -959,6 +1001,9 @@ public class ConfigurationRequest {
                ", tksUri=" + tksUri +
                ", enableServerSideKeyGen=" + enableServerSideKeyGen +
                ", importSharedSecret=" + importSharedSecret +
+               ", generateSubsystemCert=" + generateSubsystemCert +
+               ", shareDB=" +  shareDB +
+               ", sharedDBUserDN=" + sharedDBUserDN +
                "]";
     }
 }
diff --git a/base/server/cms/src/com/netscape/cms/servlet/csadmin/ConfigurationUtils.java b/base/server/cms/src/com/netscape/cms/servlet/csadmin/ConfigurationUtils.java
index 5da4dddfe8de14315bfc7ec9a4bcea1fdbff40d1..c7c312952f0d8008e53ba1e9f22889444c08ef81 100644
--- a/base/server/cms/src/com/netscape/cms/servlet/csadmin/ConfigurationUtils.java
+++ b/base/server/cms/src/com/netscape/cms/servlet/csadmin/ConfigurationUtils.java
@@ -1425,8 +1425,8 @@ public class ConfigurationUtils {
         String instancePath = cs.getString("instanceRoot");
         String instanceId = cs.getString("instanceId");
         String cstype = cs.getString("cs.type");
-
-        String dbuser = "uid=" + DBUSER + ",ou= people," + baseDN;
+        String dbuser = cs.getString("preop.internaldb.dbuser",
+                "uid=" + DBUSER + ",ou= people," + baseDN);
 
         String configDir = instancePath + File.separator + cstype.toLowerCase() + File.separator + "conf";
 
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 901d5176981f07e5a29e27dd571df60c635ce8eb..093018ee0601b716e11e3e09ada8268a6deee701 100644
--- a/base/server/cms/src/org/dogtagpki/server/rest/SystemConfigService.java
+++ b/base/server/cms/src/org/dogtagpki/server/rest/SystemConfigService.java
@@ -23,6 +23,7 @@ import java.net.URI;
 import java.net.URISyntaxException;
 import java.net.URL;
 import java.security.NoSuchAlgorithmException;
+import java.security.PublicKey;
 import java.util.Collection;
 import java.util.Enumeration;
 import java.util.Iterator;
@@ -39,11 +40,15 @@ import javax.ws.rs.core.UriInfo;
 
 import netscape.security.x509.X509CertImpl;
 
+import org.apache.commons.lang.StringUtils;
 import org.mozilla.jss.CryptoManager;
 import org.mozilla.jss.CryptoManager.NotInitializedException;
 import org.mozilla.jss.NoSuchTokenException;
 import org.mozilla.jss.crypto.CryptoToken;
+import org.mozilla.jss.crypto.ObjectNotFoundException;
+import org.mozilla.jss.crypto.PrivateKey;
 import org.mozilla.jss.crypto.TokenException;
+import org.mozilla.jss.crypto.X509Certificate;
 import org.mozilla.jss.util.IncorrectPasswordException;
 
 import com.netscape.certsrv.apps.CMS;
@@ -205,8 +210,6 @@ public class SystemConfigService extends PKIService implements SystemConfigResou
             // CA Info Panel
             caInfoPanel(data, subsystemNick);
 
-            // retrieve and import CA cert
-
             // TKS Info Panel
             tksInfoPanel(data, subsystemNick);
 
@@ -269,6 +272,8 @@ public class SystemConfigService extends PKIService implements SystemConfigResou
         }
 
         boolean generateServerCert = data.getGenerateServerCert().equalsIgnoreCase("false")? false : true;
+        boolean generateSubsystemCert = data.getGenerateSubsystemCert();
+
         boolean hasSigningCert = false;
         Vector<Cert> certs = new Vector<Cert>();
         try {
@@ -323,16 +328,16 @@ public class SystemConfigService extends PKIService implements SystemConfigResou
                 }
 
                 if (!generateServerCert && ct.equals("sslserver")) {
-                    if (!cdata.getToken().equals("internal")) {
-                        cs.putString(csSubsystem + ".cert.sslserver.nickname", cdata.getNickname());
-                    } else {
-                        cs.putString(csSubsystem + ".cert.sslserver.nickname", data.getToken() +
-                                ":" + cdata.getNickname());
-                    }
-                    cs.putString(csSubsystem + ".sslserver.nickname", cdata.getNickname());
-                    cs.putString(csSubsystem + ".sslserver.cert", cdata.getCert());
-                    cs.putString(csSubsystem + ".sslserver.certreq", cdata.getRequest());
-                    cs.putString(csSubsystem + ".sslserver.tokenname", cdata.getToken());
+                    updateConfiguration(data, cdata, "sslserver");
+                    continue;
+                }
+
+                if (!generateSubsystemCert && ct.equals("subsystem")) {
+                    // update the details for the shared subsystem cert here.
+                    updateConfiguration(data, cdata, "subsystem");
+
+                    // get parameters needed for cloning
+                    updateCloneConfiguration(cdata, "subsystem");
                     continue;
                 }
 
@@ -574,7 +579,7 @@ public class SystemConfigService extends PKIService implements SystemConfigResou
         }
 
         try {
-            ConfigurationUtils.setupDBUser();
+            if (!data.getShareDB()) ConfigurationUtils.setupDBUser();
         } catch (Exception e) {
             e.printStackTrace();
             throw new PKIException("Errors in creating or updating dbuser: " + e);
@@ -638,6 +643,40 @@ public class SystemConfigService extends PKIService implements SystemConfigResou
         return response;
     }
 
+    private void updateCloneConfiguration(SystemCertData cdata, String tag) throws NotInitializedException,
+            ObjectNotFoundException, TokenException {
+        // TODO - some of these parameters may only be valid for RSA
+        CryptoManager cryptoManager = CryptoManager.getInstance();
+        X509Certificate cert = cryptoManager.findCertByNickname(cdata.getNickname());
+        PublicKey pubk = cert.getPublicKey();
+        byte[] exponent = CryptoUtil.getPublicExponent(pubk);
+        byte[] modulus = CryptoUtil.getModulus(pubk);
+        PrivateKey privk = cryptoManager.findPrivKeyByCert(cert);
+
+        cs.putString("preop.cert." + tag + ".pubkey.modulus", CryptoUtil.byte2string(modulus));
+        cs.putString("preop.cert." + tag + ".pubkey.exponent", CryptoUtil.byte2string(exponent));
+        cs.putString("preop.cert." + tag + ".privkey.id", CryptoUtil.byte2string(privk.getUniqueID()));
+        cs.putString("preop.cert." + tag + ".dn", cdata.getSubjectDN());
+        cs.putString("preop.cert." + tag + ".keyalgorithm", cdata.getKeyAlgorithm());
+        cs.putString("preop.cert." + tag + ".keytype", cdata.getKeyType());
+        cs.putString("preop.cert." + tag + ".nickname", cdata.getNickname());
+    }
+
+    private void updateConfiguration(ConfigurationRequest data, SystemCertData cdata, String tag) {
+        if (cdata.getToken().equals("Internal Key Storage Token")) {
+            cs.putString(csSubsystem + ".cert." + tag + ".nickname", cdata.getNickname());
+        } else {
+            cs.putString(csSubsystem + ".cert." + tag + ".nickname", data.getToken() +
+                    ":" + cdata.getNickname());
+        }
+
+        cs.putString(csSubsystem + "." + tag + ".nickname", cdata.getNickname());
+        cs.putString(csSubsystem + "." + tag + ".tokenname", cdata.getToken());
+        cs.putString(csSubsystem + "." + tag + ".certreq", cdata.getRequest());
+        cs.putString(csSubsystem + "." + tag + ".cert", cdata.getCert());
+        cs.putString(csSubsystem + "." + tag + ".dn", cdata.getSubjectDN());
+    }
+
     private void caInfoPanel(ConfigurationRequest data, String subsystemNick) {
         URI caUri = null;
         try {
@@ -816,6 +855,9 @@ public class SystemConfigService extends PKIService implements SystemConfigResou
 
                 cs.putString("preop.internaldb.replicationpwd", replicationpwd);
                 cs.putString("preop.database.removeData", "false");
+                if (data.getShareDB()) {
+                    cs.putString("preop.internaldb.dbuser", data.getSharedDBUserDN());
+                }
                 cs.commit(false);
 
                 if (data.getIsClone().equals("true")) {
@@ -1234,6 +1276,16 @@ public class SystemConfigService extends PKIService implements SystemConfigResou
             data.setGenerateServerCert("true");
         }
 
+        if (! data.getGenerateSubsystemCert()) {
+            // No subsystem cert to be generated.  All interactions use a shared subsystem cert.
+            if (data.getShareDB() && StringUtils.isEmpty(data.getSharedDBUserDN())) {
+                throw new BadRequestException("Shared db user DN not provided");
+            }
+        } else {
+            // if the subsystem cert is not shared, we do not need to worry about sharing the db
+            data.setShareDB("false");
+        }
+
         if (csType.equals("TPS")) {
             if ((data.getCaUri() == null) || data.getCaUri().isEmpty()) {
                 throw new BadRequestException("CA URI not provided");
diff --git a/base/server/etc/default.cfg b/base/server/etc/default.cfg
index ea9c54019ba7d65c81accc0b730c09c758dff148..8636fb9d7a1b7e0660e1013aa3039c6032c7c486 100644
--- a/base/server/etc/default.cfg
+++ b/base/server/etc/default.cfg
@@ -401,6 +401,7 @@ pki_ds_hostname=%(pki_hostname)s
 pki_subsystem_name=CA %(pki_hostname)s %(pki_https_port)s
 pki_subsystem_nickname=subsystemCert cert-%(pki_instance_name)s CA
 pki_subsystem_subject_dn=cn=CA Subsystem Certificate,o=%(pki_security_domain_name)s
+pki_share_db=False
 
 # Paths
 # These are used in the processing of pkispawn and are not supposed
@@ -481,6 +482,9 @@ pki_ds_hostname=%(pki_hostname)s
 pki_subsystem_name=KRA %(pki_hostname)s %(pki_https_port)s
 pki_subsystem_nickname=subsystemCert cert-%(pki_instance_name)s KRA
 pki_subsystem_subject_dn=cn=KRA Subsystem Certificate,o=%(pki_security_domain_name)s
+pki_share_db=True
+pki_share_dbuser_dn=uid=pkidbuser,ou=people,o=%(pki_instance_name)s-CA
+
 
 # Paths
 # These are used in the processing of pkispawn and are not supposed
@@ -542,6 +546,9 @@ pki_ds_hostname=%(pki_hostname)s
 pki_subsystem_name=OCSP %(pki_hostname)s %(pki_https_port)s
 pki_subsystem_nickname=subsystemCert cert-%(pki_instance_name)s OCSP
 pki_subsystem_subject_dn=cn=OCSP Subsystem Certificate,o=%(pki_security_domain_name)s
+pki_share_db=True
+pki_share_dbuser_dn=uid=pkidbuser,ou=people,o=%(pki_instance_name)s-CA
+
 
 ###############################################################################
 ##  RA Configuration:                                                        ##
@@ -573,6 +580,8 @@ pki_ds_hostname=%(pki_hostname)s
 pki_subsystem_name=TKS %(pki_hostname)s %(pki_https_port)s
 pki_subsystem_nickname=subsystemCert cert-%(pki_instance_name)s TKS
 pki_subsystem_subject_dn=cn=TKS Subsystem Certificate,o=%(pki_security_domain_name)s
+pki_share_db=True
+pki_share_dbuser_dn=uid=pkidbuser,ou=people,o=%(pki_instance_name)s-CA
 
 ###############################################################################
 ##  TPS Configuration:                                                       ##
@@ -603,6 +612,8 @@ pki_kra_uri=https://%(pki_hostname)s:%(pki_https_port)s
 pki_tks_uri=https://%(pki_hostname)s:%(pki_https_port)s
 pki_enable_server_side_keygen=False
 pki_import_shared_secret=False
+pki_share_db=True
+pki_share_dbuser_dn=uid=pkidbuser,ou=people,o=%(pki_instance_name)s-CA
 
 # Paths
 # These are used in the processing of pkispawn and are not supposed
diff --git a/base/server/python/pki/server/deployment/pkihelper.py b/base/server/python/pki/server/deployment/pkihelper.py
index 66ea3620f6ee9d6961ed8cdbb24997ea69deddd3..0bdec92036ea7347398f2bd4608e14b172eb9ab8 100644
--- a/base/server/python/pki/server/deployment/pkihelper.py
+++ b/base/server/python/pki/server/deployment/pkihelper.py
@@ -3690,6 +3690,7 @@ class ConfigClient:
         # Create 'Subsystem Certificate'
         if not self.clone:
             if self.standalone and self.external_step_two:
+                data.generateSubsystemCert = "true"
                 # Stand-alone PKI (Step 2)
                 cert4 = self.create_system_cert("subsystem")
                 # Load the Stand-alone PKI 'Subsystem Certificate' (Step 2)
@@ -3698,9 +3699,20 @@ class ConfigClient:
                     self.master_dict['pki_external_subsystem_cert_path'],
                     self.subsystem)
                 systemCerts.append(cert4)
+            elif len(system_list) >= 2:
+                # Existing PKI Instance
+                data.generateSubsystemCert = "false"
+                for subsystem in system_list:
+                    dst = self.master_dict['pki_instance_path'] + '/conf/' + \
+                        subsystem.lower() + '/CS.cfg'
+                    if subsystem != self.subsystem and os.path.exists(dst):
+                        cert4 = self.retrieve_existing_subsystem_cert(dst)
+                        systemCerts.append(cert4)
+                        break
             else:
                 # PKI KRA, PKI OCSP, PKI RA, PKI TKS, PKI TPS,
                 # Subordinate CA, or External CA
+                data.generateSubsystemCert = "true"
                 cert4 = self.create_system_cert("subsystem")
                 systemCerts.append(cert4)
 
@@ -3807,6 +3819,11 @@ class ConfigClient:
             data.secureConn = "true"
         else:
             data.secureConn = "false"
+        if config.str2bool(self.master_dict['pki_share_db']):
+            data.shareDB = "true"
+            data.sharedDBUserDN = self.master_dict['pki_share_dbuser_dn']
+        else:
+            data.shareDB = "false"
 
     def set_backup_parameters(self, data):
         if config.str2bool(self.master_dict['pki_backup_keys']):
@@ -3957,6 +3974,21 @@ class ConfigClient:
         cert.token = cs_cfg.get(cstype + ".sslserver.tokenname")
         return cert
 
+    def retrieve_existing_subsystem_cert(self, cfg_file):
+        cs_cfg = PKIConfigParser.read_simple_configuration_file(cfg_file)
+        cstype = cs_cfg.get('cs.type').lower()
+        cert = pki.system.SystemCertData()
+        cert.tag = self.master_dict["pki_subsystem_tag"]
+        cert.keyAlgorithm = cs_cfg.get("cloning.subsystem.keyalgorithm")
+        cert.keySize = self.master_dict["pki_subsystem_key_size"]
+        cert.keyType = cs_cfg.get("cloning.subsystem.keytype")
+        cert.nickname = cs_cfg.get(cstype + ".subsystem.nickname")
+        cert.cert = cs_cfg.get(cstype + ".subsystem.cert")
+        cert.request = cs_cfg.get(cstype + ".subsystem.certreq")
+        cert.subjectDN = cs_cfg.get("cloning.subsystem.dn")
+        cert.token = cs_cfg.get(cstype + ".subsystem.tokenname")
+        return cert
+
 class PKIDeployer:
     """Holds the global dictionaries and the utility objects"""
 
-- 
1.8.4.2

