This patch adds the capability of installing a Dogtag DRM
    to an IPA instance.  With this patch, when ipa-server-install
    is run, a Dogtag CA and a Dogtag DRM are created.  The DRM
    shares the same tomcat instance and DS instance as the Dogtag CA.
    Moreover, the same admin user/agent (and agent cert) can be used
    for both subsystems.  Certmonger is also confgured to monitor the
    new subsystem certificates.
    
    It is also possible to clone the DRM.  When the IPA instance is
    cloned, if --enable-ca and --enable-drm are specified, the DRM
    is cloned as well.
    
    Installing a DRM requires the user to have a Dogtag CA instance.
    We can look into possibly relaxing that requirement in a later patch.
    
    I am still working on patches for a ipa-drm-install script, which
    would be used to add a DRM to an existing master (that includes
    a dogtag CA), or an existing clone.

   Please review,

   Thanks, 
   Ade
>From 298aa20b554b5e17a0f7a1d4cf13e246fba9c8dc Mon Sep 17 00:00:00 2001
From: Ade Lee <a...@redhat.com>
Date: Tue, 18 Mar 2014 11:23:30 -0400
Subject: [PATCH] Add a DRM to IPA

This patch adds the capability of installing a Dogtag DRM
to an IPA instance.  With this patch, when ipa-server-install
is run, a Dogtag CA and a Dogtag DRM are created.  The DRM
shares the same tomcat instance and DS instance as the Dogtag CA.
Moreover, the same admin user/agent (and agent cert) can be used
for both subsystems.  Certmonger is also confgured to monitor the
new subsystem certificates.

It is also possible to clone the DRM.  When the IPA instance is
cloned, if --enable-ca and --enable-drm are specified, the DRM
is cloned as well.

Installing a DRM requires the user to have a Dogtag CA instance.
We can look into possibly relaxing that requirement in a later patch.

I am still working on patches for a ipa-drm-install script, which
would be used to add a DRM to an existing master (that includes
a dogtag CA), or an existing clone.
---
 install/conf/ipa-pki-proxy.conf          |   4 +-
 install/tools/ipa-replica-install        |  29 +-
 install/tools/ipa-server-install         |  36 +-
 ipapython/dogtag.py                      |   1 +
 ipaserver/install/cainstance.py          |   4 +-
 ipaserver/install/drminstance.py         | 642 +++++++++++++++++++++++++++++++
 ipaserver/install/installutils.py        |  17 +
 ipaserver/install/ipa_replica_prepare.py |   1 +
 8 files changed, 722 insertions(+), 12 deletions(-)
 create mode 100644 ipaserver/install/drminstance.py

diff --git a/install/conf/ipa-pki-proxy.conf b/install/conf/ipa-pki-proxy.conf
index 224cdd45b5b5f72671a179570fd15772fe8cfaab..9a6345898bfa017da03bf108a82c3639edbb0470 100644
--- a/install/conf/ipa-pki-proxy.conf
+++ b/install/conf/ipa-pki-proxy.conf
@@ -11,7 +11,7 @@ ProxyRequests Off
 </LocationMatch>
 
 # matches for admin port and installer
-<LocationMatch "^/ca/admin/ca/getCertChain|^/ca/admin/ca/getConfigEntries|^/ca/admin/ca/getCookie|^/ca/admin/ca/getStatus|^/ca/admin/ca/securityDomainLogin|^/ca/admin/ca/getDomainXML|^/ca/rest/installer/installToken|^/ca/admin/ca/updateNumberRange|^/ca/rest/securityDomain/domainInfo|^/ca/rest/account/login|^/ca/admin/ca/tokenAuthenticate|^/ca/admin/ca/updateNumberRange|^/ca/admin/ca/updateDomainXML|^/ca/rest/account/logout|^/ca/rest/securityDomain/installToken">
+<LocationMatch "^/ca/admin/ca/getCertChain|^/ca/admin/ca/getConfigEntries|^/ca/admin/ca/getCookie|^/ca/admin/ca/getStatus|^/ca/admin/ca/securityDomainLogin|^/ca/admin/ca/getDomainXML|^/ca/rest/installer/installToken|^/ca/admin/ca/updateNumberRange|^/ca/rest/securityDomain/domainInfo|^/ca/rest/account/login|^/ca/admin/ca/tokenAuthenticate|^/ca/admin/ca/updateNumberRange|^/ca/admin/ca/updateDomainXML|^/ca/rest/account/logout|^/ca/rest/securityDomain/installToken|^/ca/admin/ca/updateConnector|^/ca/admin/ca/getSubsystemCert|^/kra/admin/kra/updateNumberRange|^/kra/admin/kra/getConfigEntries">
     NSSOptions +StdEnvVars +ExportCertData +StrictRequire +OptRenegotiate
     NSSVerifyClient none
     ProxyPassMatch ajp://localhost:$DOGTAG_PORT
@@ -19,7 +19,7 @@ ProxyRequests Off
 </LocationMatch>
 
 # matches for agent port and eeca port
-<LocationMatch "^/ca/agent/ca/displayBySerial|^/ca/agent/ca/doRevoke|^/ca/agent/ca/doUnrevoke|^/ca/agent/ca/updateDomainXML|^/ca/eeca/ca/profileSubmitSSLClient">
+<LocationMatch "^/ca/agent/ca/displayBySerial|^/ca/agent/ca/doRevoke|^/ca/agent/ca/doUnrevoke|^/ca/agent/ca/updateDomainXML|^/ca/eeca/ca/profileSubmitSSLClient|^/kra/agent/kra/connector">
     NSSOptions +StdEnvVars +ExportCertData +StrictRequire +OptRenegotiate
     NSSVerifyClient require
     ProxyPassMatch ajp://localhost:$DOGTAG_PORT
diff --git a/install/tools/ipa-replica-install b/install/tools/ipa-replica-install
index f5e7197b5c2fcccc03b0b070a33761debcc07a9c..70a9cb21bd48a1fbcd56c30e1f4089172f639f90 100755
--- a/install/tools/ipa-replica-install
+++ b/install/tools/ipa-replica-install
@@ -38,9 +38,10 @@ from ipaserver.install import otpdinstance
 from ipaserver.install.replication import replica_conn_check, ReplicationManager
 from ipaserver.install.installutils import (ReplicaConfig, expand_replica_info,
         read_replica_info, get_host_name, BadHostError, private_ccache,
-        read_replica_info_dogtag_port)
+        read_replica_info_dogtag_port, read_replica_info_drm_enabled)
 from ipaserver.plugins.ldap2 import ldap2
 from ipaserver.install import cainstance
+from ipaserver.install import drminstance
 from ipalib import api, errors, util
 from ipalib.constants import CACERT
 from ipapython import version
@@ -63,6 +64,8 @@ def parse_options():
     basic_group = OptionGroup(parser, "basic options")
     basic_group.add_option("--setup-ca", dest="setup_ca", action="store_true",
                       default=False, help="configure a dogtag CA")
+    basic_group.add_option("--setup-drm", dest="setup_drm", action="store_true",
+                      default=False, help="configure a dogtag DRM")
     basic_group.add_option("--ip-address", dest="ip_address",
                       type="ip", ip_local=True,
                       help="Replica server IP Address")
@@ -540,6 +543,15 @@ def main():
         print 'CA cannot be installed in CA-less setup.'
         sys.exit(1)
 
+    config.setup_drm = options.setup_drm
+    if config.setup_drm:
+        if not config.setup_ca:
+            print "CA must be installed with the KRA"
+            sys.exit(1)
+        if not read_replica_info_drm_enabled(config.dir):
+            print "DRM is not installed on the master system"
+            sys.exit(1)
+
     installutils.verify_fqdn(config.master_host_name, options.no_host_dns)
 
     # check connection
@@ -573,6 +585,10 @@ def main():
         else:
             fd.write("enable_ra=False\n")
             fd.write("ra_plugin=none\n")
+        if config.setup_drm:
+            fd.write("enable_drm=True\n")
+        else:
+            fd.write("enable_drm=False\n")
         fd.write("mode=production\n")
         fd.close()
     finally:
@@ -706,9 +722,14 @@ def main():
     service.print_msg("Applying LDAP updates")
     ds.apply_updates()
 
-    # Restart ds and krb after configurations have been changed
-    service.print_msg("Restarting the directory server")
-    ds.restart()
+    if options.setup_drm:
+        drm = drminstance.install_replica_drm(config)
+        service.print_msg("Restarting the directory server")
+        ds.restart()
+        drm.enable_client_auth_to_db()
+    else:
+        service.print_msg("Restarting the directory server")
+        ds.restart()
 
     service.print_msg("Restarting the KDC")
     krb.restart()
diff --git a/install/tools/ipa-server-install b/install/tools/ipa-server-install
index 34393b7df0a95a76b0c2660dcaafca13b21d2dfb..4997e7c58843de3dfae27615e1a2625320d03a00 100755
--- a/install/tools/ipa-server-install
+++ b/install/tools/ipa-server-install
@@ -3,7 +3,7 @@
 #          Simo Sorce <sso...@redhat.com>
 #          Rob Crittenden <rcrit...@redhat.com>
 #
-# Copyright (C) 2007-2010  Red Hat
+# Copyright (C) 2007-2014  Red Hat
 # see file 'COPYING' for use and warranty information
 #
 # This program is free software; you can redistribute it and/or modify
@@ -53,6 +53,7 @@ from ipaserver.install import httpinstance
 from ipaserver.install import ntpinstance
 from ipaserver.install import certs
 from ipaserver.install import cainstance
+from ipaserver.install import drminstance
 from ipaserver.install import memcacheinstance
 from ipaserver.install import otpdinstance
 from ipaserver.install import sysupgrade
@@ -492,11 +493,19 @@ def uninstall():
             dogtag_constants=dogtag_constants)
         if cads_instance.is_configured():
             cads_instance.uninstall()
+
+    drminstance.stop_tracking_certificates(dogtag_constants)
+    drm_instance = drminstance.DRMInstance(
+        api.env.realm, dogtag_constants=dogtag_constants)
+    if drm_instance.is_installed():
+        drm_instance.uninstall()
+
     cainstance.stop_tracking_certificates(dogtag_constants)
     ca_instance = cainstance.CAInstance(
         api.env.realm, certs.NSS_DIR, dogtag_constants=dogtag_constants)
     if ca_instance.is_configured():
         ca_instance.uninstall()
+
     bindinstance.BindInstance(fstore).uninstall()
     httpinstance.HTTPInstance(fstore).uninstall()
     krbinstance.KrbInstance(fstore).uninstall()
@@ -754,6 +763,7 @@ def main():
         setup_ca = False
     else:
         setup_ca = True
+        setup_drm = True
 
     # Figure out what external CA step we're in. See cainstance.py for more
     # info on the 3 states.
@@ -770,6 +780,8 @@ def main():
     print "This includes:"
     if setup_ca:
         print "  * Configure a stand-alone CA (dogtag) for certificate management"
+    if setup_drm:
+        print "  * Configure a stand-alone DRM (dogtag) for key storage"
     if options.conf_ntp:
         print "  * Configure the Network Time Daemon (ntpd)"
     print "  * Create and configure an instance of Directory Server"
@@ -1016,6 +1028,10 @@ def main():
     else:
         fd.write("enable_ra=False\n")
         fd.write("ra_plugin=none\n")
+    if setup_drm:
+        fd.write("enable_drm=True\n")
+    else:
+        fd.write("enable_drm=False\n")
     fd.write("mode=production\n")
     fd.close()
 
@@ -1194,6 +1210,17 @@ def main():
     service.print_msg("Restarting the web server")
     http.restart()
 
+    if setup_drm:
+        # code to create drm here
+        drm = drminstance.DRMInstance(realm_name,
+            dogtag_constants=dogtag.install_constants)
+        drm.configure_instance(host_name, domain_name, dm_password,
+                               dm_password, subject_base=options.subject)
+
+        # This is done within stopped_service context, which restarts DRM
+        ds.restart()
+        drm.enable_client_auth_to_db()
+
     # Set the admin user kerberos password
     ds.change_admin_password(admin_password)
 
@@ -1246,9 +1273,10 @@ def main():
 
     print ""
     if setup_ca:
-        print "Be sure to back up the CA certificate stored in /root/cacert.p12"
-        print "This file is required to create replicas. The password for this"
-        print "file is the Directory Manager password"
+        print "Be sure to back up the CA certificates stored in /root/cacert.p12"
+        print "and the DRM certificates stored in /root/drmcert.p12"
+        print "These files are required to create replicas. The password for these"
+        print "files is the Directory Manager password"
     else:
         print "In order for Firefox autoconfiguration to work you will need to"
         print "use a SSL signing certificate. See the IPA documentation for more details."
diff --git a/ipapython/dogtag.py b/ipapython/dogtag.py
index f829b9340d1ce55b2adae4817018de11b894c92d..8c6c8ff0687508aa448b63bdb7db2569e08c3bdf 100644
--- a/ipapython/dogtag.py
+++ b/ipapython/dogtag.py
@@ -61,6 +61,7 @@ class Dogtag10Constants(object):
     PASSWORD_CONF_PATH = '%s/conf/password.conf' % PKI_ROOT
     SERVICE_PROFILE_DIR = '%s/ca/profiles/ca' % PKI_ROOT
     ALIAS_DIR = '/etc/pki/pki-tomcat/alias'
+    DRM_CS_CFG_PATH = '%s/conf/kra/CS.cfg' % PKI_ROOT
 
     SERVICE_NAME = 'pki_tomcatd'
 
diff --git a/ipaserver/install/cainstance.py b/ipaserver/install/cainstance.py
index f52870424fd6918352e46b7765e3cd00fbc23ed4..9bee385556aef3f1c9d2fb914fe8087db1f83e7f 100644
--- a/ipaserver/install/cainstance.py
+++ b/ipaserver/install/cainstance.py
@@ -1281,7 +1281,7 @@ class CAInstance(service.Service):
                 'SslClientAuth', quotes=False, separator='=')
             installutils.set_directive(caconfig,
                 'authz.instance.DirAclAuthz.ldap.ldapauth.bindDN',
-                'uid=pkidbuser,ou=people,o=ipa-ca', quotes=False, separator='=')
+                'uid=pkidbuser,ou=people,o=ipaca', quotes=False, separator='=')
             installutils.set_directive(caconfig,
                 'authz.instance.DirAclAuthz.ldap.ldapauth.clientCertNickname',
                 'subsystemCert cert-pki-ca', quotes=False, separator='=')
@@ -1296,7 +1296,7 @@ class CAInstance(service.Service):
             installutils.set_directive(caconfig, 'internaldb.ldapauth.authtype',
                 'SslClientAuth', quotes=False, separator='=')
             installutils.set_directive(caconfig, 'internaldb.ldapauth.bindDN',
-                'uid=pkidbuser,ou=people,o=ipa-ca', quotes=False, separator='=')
+                'uid=pkidbuser,ou=people,o=ipaca', quotes=False, separator='=')
             installutils.set_directive(caconfig,
                 'internaldb.ldapauth.clientCertNickname',
                 'subsystemCert cert-pki-ca', quotes=False, separator='=')
diff --git a/ipaserver/install/drminstance.py b/ipaserver/install/drminstance.py
new file mode 100644
index 0000000000000000000000000000000000000000..e12be450d040cd144efcbea831d3e5b8eeaeb1c3
--- /dev/null
+++ b/ipaserver/install/drminstance.py
@@ -0,0 +1,642 @@
+# Authors: Ade Lee <a...@redhat.com>
+#
+# Copyright (C) 2014  Red Hat
+# see file 'COPYING' for use and warranty information
+#
+# 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, either version 3 of the License, or
+# (at your option) any later version.
+#
+# 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, see <http://www.gnu.org/licenses/>.
+#
+
+import pwd
+import os
+import sys
+import time
+import base64
+import tempfile
+import shutil
+import syslog
+import ConfigParser
+
+from ipapython import dogtag
+from ipapython import certmonger
+from ipalib import x509
+from ipalib import errors
+from ipapython.dn import DN
+import traceback
+
+from ipapython import ipautil
+from ipapython import services as ipaservices
+from ipaserver.install import service
+from ipaserver.install import installutils
+from ipaserver.install import dsinstance
+from ipaserver.install.installutils import stopped_service
+from ipaserver.plugins import ldap2
+from ipapython.ipa_log_manager import *
+
+HTTPD_CONFD = "/etc/httpd/conf.d/"
+DEFAULT_DSPORT = dogtag.install_constants.DS_PORT
+
+PKI_USER = "pkiuser"
+PKI_DS_USER = dogtag.install_constants.DS_USER
+
+# When IPA is installed with DNS support, this CNAME should hold all IPA
+# replicas with DRM configured
+IPA_DRM_RECORD = "ipa-drm"
+
+
+def check_inst():
+    """
+    Validate that the appropriate dogtag/RHCS packages have been installed.
+    """
+
+    # Check for a couple of binaries we need
+    if not os.path.exists(dogtag.install_constants.SPAWN_BINARY):
+        return False
+    if not os.path.exists(dogtag.install_constants.DESTROY_BINARY):
+        return False
+
+    # This is the template tomcat file for a DRM
+    if not os.path.exists('/usr/share/pki/kra/conf/server.xml'):
+        return False
+
+    return True
+
+def stop_tracking_certificates(dogtag_constants):
+    """Stop tracking our certificates. Called on uninstall.
+    """
+    cmonger = ipaservices.knownservices.certmonger
+    ipaservices.knownservices.messagebus.start()
+    cmonger.start()
+
+    for nickname in ['auditSigningCert cert-pki-drm',
+                        'storageCert cert-pki-drm',
+                        'transportCert cert-pki-drm']:
+        try:
+            certmonger.stop_tracking(
+                dogtag_constants.ALIAS_DIR, nickname=nickname)
+        except (ipautil.CalledProcessError, RuntimeError), e:
+            root_logger.error(
+                "certmonger failed to stop tracking certificate: %s" % str(e))
+
+    cmonger.stop()
+
+class DRMInstance(service.Service):
+    """
+    We assume that the CA has already been installed, and we use the
+    same tomcat instance to host both the CA and DRM.
+    The mod_nss database will contain the RA agent cert that will be used
+    to do authenticated requests against dogtag.  The RA agent cert will
+    be the same for both the CA and DRM.
+    """
+
+    def __init__(self, realm, dogtag_constants=None):
+        if dogtag_constants is None:
+            dogtag_constants = dogtag.configured_constants()
+
+        service.Service.__init__(self,
+                '%sd' % dogtag_constants.PKI_INSTANCE_NAME,
+                service_desc="data recovery manager server"
+                )
+
+        self.dogtag_constants = dogtag_constants
+        self.realm = realm
+        self.dm_password = None
+        self.admin_password = None
+        self.fqdn = None
+        self.domain = None
+        self.pkcs12_info = None
+        self.clone = False
+
+        self.basedn = DN(('o', 'ipadrm'))
+        self.drm_agent_db = tempfile.mkdtemp(prefix = "tmp-")
+        self.ds_port = DEFAULT_DSPORT
+        self.server_root = dogtag_constants.SERVER_ROOT
+
+    def __del__(self):
+        shutil.rmtree(self.drm_agent_db, ignore_errors=True)
+
+    def is_installed(self):
+        """
+        Determine if DRM instance has been installed.
+
+        Returns True/False
+        """
+        return os.path.exists(os.path.join(
+            self.server_root, self.dogtag_constants.PKI_INSTANCE_NAME, "kra"))
+
+    def is_ca_installed(self):
+        """
+        Determine if CA instance has been installed.
+
+        Returns True/False
+        """
+        return os.path.exists(os.path.join(
+            self.server_root, self.dogtag_constants.PKI_INSTANCE_NAME, "ca"))
+
+    def configure_instance(self, host_name, domain, dm_password,
+                           admin_password, ds_port=DEFAULT_DSPORT,
+                           pkcs12_info=None, master_host=None,
+                           master_replication_port=None,
+                           subject_base=None):
+        """Create a DRM instance.
+
+           To create a clone, pass in pkcs12_info.
+        """
+        self.fqdn = host_name
+        self.domain = domain
+        self.dm_password = dm_password
+        self.admin_password = admin_password
+        self.ds_port = ds_port
+        self.pkcs12_info = pkcs12_info
+        if self.pkcs12_info is not None:
+            self.clone = True
+        self.master_host = master_host
+        self.master_replication_port = master_replication_port
+        if subject_base is None:
+            self.subject_base = DN(('O', self.realm))
+        else:
+            self.subject_base = subject_base
+
+        # Confirm that a DRM does not already exist
+        if self.is_installed():
+            raise RuntimeError(
+                "DRM already installed.  Please uninstall it first")
+        # Confirm that a Dogtag 10 CA instance already exists
+        if not self.is_ca_installed():
+            raise RuntimeError(
+                "DRM configuration failed.  "
+                "A Dogtag CA must be installed first")
+
+        self.step("configuring DRM instance", self.__spawn_instance)
+
+        if not self.clone:
+            self.step("restarting DRM", self.__restart_instance)
+            self.step("configure certificate renewals", self.configure_renewal)
+        else:
+            self.step("configure clone certificate renewals",
+                      self.configure_clone_renewal)
+        self.step("Configure HTTP to proxy connections", self.__http_proxy)
+
+        self.start_creation(runtime=210)
+
+    def __stop(self):
+        self.stop()
+
+    def __start(self):
+        self.start()
+
+
+    def __spawn_instance(self):
+        """
+        Create and configure a new DRM instance using pkispawn.
+        pkispawn requires a configuration file with IPA-specific
+        parameters.
+        """
+
+        # Create an empty and secured file
+        (cfg_fd, cfg_file) = tempfile.mkstemp()
+        os.close(cfg_fd)
+        pent = pwd.getpwnam(PKI_USER)
+        os.chown(cfg_file, pent.pw_uid, pent.pw_gid)
+
+        # Create KRA configuration
+        config = ConfigParser.ConfigParser()
+        config.optionxform = str
+        config.add_section("KRA")
+
+        # Security Domain Authentication
+        config.set("KRA", "pki_security_domain_https_port", "443")
+        config.set("KRA", "pki_security_domain_password", self.admin_password)
+        config.set("KRA", "pki_security_domain_user", "admin")
+
+        # issuing ca
+        config.set("KRA", "pki_issuing_ca_uri",
+                   "https:" + self.fqdn + ":" + "443")
+
+        # Server
+        config.set("KRA", "pki_enable_proxy", "True")
+        config.set("KRA", "pki_restart_configured_instance", "False")
+        config.set("KRA", "pki_backup_keys", "True")
+        config.set("KRA", "pki_backup_password", self.admin_password)
+
+        # Client security database
+        config.set("KRA", "pki_client_database_dir", self.drm_agent_db)
+        config.set("KRA", "pki_client_database_password", self.admin_password)
+        config.set("KRA", "pki_client_database_purge", "False")
+        config.set("KRA", "pki_client_pkcs12_password", self.admin_password)
+
+        # Administrator
+        config.set("KRA", "pki_admin_name", "admin")
+        config.set("KRA", "pki_admin_uid", "admin")
+        config.set("KRA", "pki_admin_email", "root@localhost")
+        config.set("KRA", "pki_admin_password", self.admin_password)
+        config.set("KRA", "pki_admin_nickname", "ipa-ca-agent")
+        config.set("KRA", "pki_admin_subject_dn",
+            str(DN(('cn', 'ipa-ca-agent'), self.subject_base)))
+        config.set("KRA", "pki_import_admin_cert", "True")
+        config.set("KRA", "pki_admin_cert_file",
+                   "/root/.dogtag/pki-tomcat/ca_admin.cert")
+        config.set("KRA", "pki_client_admin_cert_p12", "/root/ca-agent.p12")
+
+        # Directory server
+        config.set("KRA", "pki_ds_ldap_port", str(self.ds_port))
+        config.set("KRA", "pki_ds_password", self.dm_password)
+        config.set("KRA", "pki_ds_base_dn", self.basedn)
+        config.set("KRA", "pki_ds_database", "ipakra")
+
+        # Certificate subject DN's
+        config.set("KRA", "pki_subsystem_subject_dn",
+            str(DN(('cn', 'CA Subsystem'), self.subject_base)))
+        config.set("KRA", "pki_ssl_server_subject_dn",
+            str(DN(('cn', self.fqdn), self.subject_base)))
+        config.set("KRA", "pki_audit_signing_subject_dn",
+            str(DN(('cn', 'DRM Audit'), self.subject_base)))
+        config.set("KRA", "pki_transport_subject_dn",
+            str(DN(('cn', 'DRM Transport Certificate'), self.subject_base)))
+        config.set("KRA", "pki_storage_subject_dn",
+            str(DN(('cn', 'DRM Storage Certificate'), self.subject_base)))
+
+        # Certificate nicknames
+        # Note that both the server certs and subsystem certs reuse
+        # the ca certs.
+        config.set("KRA", "pki_subsystem_nickname",
+                   "subsystemCert cert-pki-ca")
+        config.set("KRA", "pki_ssl_server_nickname",
+                   "Server-Cert cert-pki-ca")
+        config.set("KRA", "pki_audit_signing_nickname",
+                   "auditSigningCert cert-pki-drm")
+        config.set("KRA", "pki_transport_nickname",
+                   "transportCert cert-pki-drm")
+        config.set("KRA", "pki_storage_nickname",
+                   "storageCert cert-pki-drm")
+
+        # Shared db settings
+        # Needed because CA and KRA share the same database
+        # We will use the dbuser created for the CA
+        config.set("KRA", "pki_share_db", "True")
+        config.set("KRA", "pki_share_dbuser_dn",
+            str(DN(('uid', 'pkidbuser'),('ou', 'people'),('o','ipaca'))))
+
+
+        if (self.clone):
+            drmfile = self.pkcs12_info[0]
+            shutil.copy(drmfile, "/tmp/drm.p12")
+            pent = pwd.getpwnam(PKI_USER)
+            os.chown("/tmp/drm.p12", pent.pw_uid, pent.pw_gid)
+
+            # Security domain registration
+            config.set("KRA", "pki_security_domain_hostname", self.master_host)
+            config.set("KRA", "pki_security_domain_https_port", "443")
+            config.set("KRA", "pki_security_domain_user", "admin")
+            config.set("KRA", "pki_security_domain_password",
+                       self.admin_password)
+
+            # Clone
+            config.set("KRA", "pki_clone", "True")
+            config.set("KRA", "pki_clone_pkcs12_path", "/tmp/drm.p12")
+            config.set("KRA", "pki_clone_pkcs12_password", self.dm_password)
+            config.set("KRA", "pki_clone_replication_security", "TLS")
+            config.set("KRA", "pki_clone_replication_master_port",
+                    str(self.master_replication_port))
+            config.set("KRA", "pki_clone_replication_clone_port",
+                    dogtag.install_constants.DS_PORT)
+            config.set("KRA", "pki_clone_replicate_schema", "False")
+            config.set("KRA", "pki_clone_uri",
+                    "https://%s"; % ipautil.format_netloc(self.master_host, 443))
+
+        # Generate configuration file
+        with open(cfg_file, "wb") as f:
+            config.write(f)
+
+        # Define the things we don't want logged
+        nolog = (self.admin_password, self.dm_password,)
+
+        args = ["/usr/sbin/pkispawn", "-s", "KRA", "-f", cfg_file ]
+
+        with open(cfg_file) as f:
+            root_logger.debug(
+                'Contents of pkispawn configuration file (%s):\n%s' %
+                    (cfg_file, ipautil.nolog_replace(f.read(), nolog)))
+
+        try:
+            ipautil.run(args, nolog=nolog)
+        except ipautil.CalledProcessError, e:
+            root_logger.critical("failed to configure DRM instance %s" % e)
+            raise RuntimeError('Configuration of DRM failed')
+        finally:
+            os.remove(cfg_file)
+
+        shutil.move("/var/lib/pki/pki-tomcat/alias/kra_backup_keys.p12", \
+                     "/root/drmcert.p12")
+
+        root_logger.debug("completed creating DRM instance")
+
+    def __enable(self):
+        self.backup_state("enabled", self.is_enabled())
+        # This function is not called during installation because
+        # we assume that the DRM shares its instance with the CA
+        # and is enabled when that component is enabled.
+
+    def __restart_instance(self):
+        try:
+            self.restart(self.dogtag_constants.PKI_INSTANCE_NAME)
+        except Exception:
+            # TODO: roll back here?
+            root_logger.debug(traceback.format_exc())
+            root_logger.critical(
+                "Failed to restart the DRM. See the installation log for details.")
+
+    def enable_client_auth_to_db(self):
+        """
+        Enable client auth connection to the internal db.
+        """
+        drmconfig = dogtag.install_constants.DRM_CS_CFG_PATH
+
+        with stopped_service(self.dogtag_constants.SERVICE_NAME,
+                        instance_name=self.dogtag_constants.PKI_INSTANCE_NAME):
+
+            # Enable file publishing, disable LDAP
+            installutils.set_directive(drmconfig,
+                'authz.instance.DirAclAuthz.ldap.ldapauth.authtype',
+                'SslClientAuth', quotes=False, separator='=')
+            installutils.set_directive(drmconfig,
+                'authz.instance.DirAclAuthz.ldap.ldapauth.bindDN',
+                'uid=pkidbuser,ou=people,o=ipaca', quotes=False, separator='=')
+            installutils.set_directive(drmconfig,
+                'authz.instance.DirAclAuthz.ldap.ldapauth.clientCertNickname',
+                'subsystemCert cert-pki-ca', quotes=False, separator='=')
+            installutils.set_directive(drmconfig,
+                'authz.instance.DirAclAuthz.ldap.ldapconn.port',
+                str(dogtag.install_constants.DS_SECURE_PORT),
+                quotes=False, separator='=')
+            installutils.set_directive(drmconfig,
+                'authz.instance.DirAclAuthz.ldap.ldapconn.secureConn',
+                'true', quotes=False, separator='=')
+
+            installutils.set_directive(drmconfig, 'internaldb.ldapauth.authtype',
+                'SslClientAuth', quotes=False, separator='=')
+            installutils.set_directive(drmconfig, 'internaldb.ldapauth.bindDN',
+                'uid=pkidbuser,ou=people,o=ipaca', quotes=False, separator='=')
+            installutils.set_directive(drmconfig,
+                'internaldb.ldapauth.clientCertNickname',
+                'subsystemCert cert-pki-ca', quotes=False, separator='=')
+            installutils.set_directive(drmconfig, 'internaldb.ldapconn.port',
+                str(dogtag.install_constants.DS_SECURE_PORT),
+                quotes=False, separator='=')
+            installutils.set_directive(drmconfig,
+                 'internaldb.ldapconn.secureConn', 'true', quotes=False,
+                 separator='=')
+
+    def uninstall(self):
+        if self.is_installed():
+            self.print_msg("Unconfiguring DRM")
+
+        try:
+            ipautil.run(["/usr/sbin/pkidestroy", "-i",
+                         self.dogtag_constants.PKI_INSTANCE_NAME,
+                         "-s", "KRA"])
+        except ipautil.CalledProcessError, e:
+            root_logger.critical("failed to uninstall DRM instance %s" % e)
+
+    def __http_proxy(self):
+        ''' Update the http proxy file
+
+            This will be necessary for older systems where the proxy.conf
+            file does not include DRM functionality '''
+        template_filename = ipautil.SHARE_DIR + "ipa-pki-proxy.conf"
+        sub_dict = dict(
+            DOGTAG_PORT=self.dogtag_constants.AJP_PORT,
+            CLONE='' if self.clone else '#',
+            FQDN=self.fqdn,
+        )
+        template = ipautil.template_file(template_filename, sub_dict)
+        with open(HTTPD_CONFD + "ipa-pki-proxy.conf", "w") as fd:
+            fd.write(template)
+
+    def __get_drm_pin(self):
+        try:
+            return certmonger.get_pin('internal',
+                dogtag_constants=self.dogtag_constants)
+        except IOError, e:
+            root_logger.debug(
+                'Unable to determine PIN for DRM instance: %s' % str(e))
+            raise RuntimeError(e)
+
+    def configure_renewal(self):
+        cmonger = ipaservices.knownservices.certmonger
+        cmonger.enable()
+        ipaservices.knownservices.messagebus.start()
+        cmonger.start()
+
+        pin = self.__get_drm_pin()
+
+        # Server-Cert cert-pki-ca is renewed per-server
+        for nickname in ['auditSigningCert cert-pki-drm',
+                         'transportCert cert-pki-drm',
+                         'storageCert cert-pki-drm']:
+            try:
+                certmonger.dogtag_start_tracking(
+                    'dogtag-ipa-renew-agent', nickname, pin, None,
+                    self.dogtag_constants.ALIAS_DIR, 'stop_pkicad',
+                    'renew_ca_cert "%s"' % nickname)
+            except (ipautil.CalledProcessError, RuntimeError), e:
+                root_logger.error(
+                    "certmonger failed to start tracking certificate: %s" % str(e))
+
+    def configure_clone_renewal(self):
+        """
+        The actual renewal is done on the master. On the clone side we
+        use a separate certmonger CA that polls LDAP to see if an updated
+        certificate is available. If it is then it gets installed.
+        """
+
+        pin = self.__get_drm_pin()
+
+        # Server-Cert cert-pki-ca is renewed per-server
+        for nickname in ['auditSigningCert cert-pki-drm',
+                         'transportCert cert-pki-drm',
+                         'storageCert cert-pki-drm']:
+            try:
+                certmonger.dogtag_start_tracking(
+                    'dogtag-ipa-retrieve-agent-submit', nickname, pin, None,
+                    self.dogtag_constants.ALIAS_DIR, 'stop_pkicad',
+                    'restart_pkicad "%s"' % nickname)
+            except (ipautil.CalledProcessError, RuntimeError), e:
+                    root_logger.error(
+                        "certmonger failed to start tracking certificate: %s" % str(e))
+
+    def is_master(self):
+        """
+        There are some tasks that are only done on a single dogtag master.
+        By default this is the first one installed. Use this to determine if
+        that is the case.
+
+        If users have changed their topology so the initial master is either
+        gone or no longer performing certain duties then it is their
+        responsibility to handle changes on upgrades.
+        """
+        master = installutils.get_directive(
+            self.dogtag_constants.DRM_CS_CFG_PATH, 'subsystem.select', '=')
+
+        return master == 'New'
+
+def install_replica_drm(config, postinstall=False):
+    """
+    Install a DRM on a replica.
+
+    There are two modes of doing this controlled:
+      - While the replica is being installed
+      - Post-replica installation
+
+    config is a ReplicaConfig object
+
+    Returns a DRM instance
+    """
+    # note that the cacert.p12 file is regenerated during the
+    # ipa-replica-prepare process and should include all the certs
+    # for the CA and DRM
+    drmfile = config.dir + "/cacert.p12"
+
+    if not ipautil.file_exists(drmfile):
+        raise RuntimeError(
+                "Unable to clone DRM."
+                "  cacert.p12 file not found in replica file")
+
+    drm = DRMInstance(config.realm_name,
+        dogtag_constants=dogtag.install_constants)
+    drm.dm_password = config.dirman_password
+    drm.subject_base = config.subject_base
+    if drm.is_installed():
+        sys.exit("A DRM is already configured on this system.")
+
+    drm.configure_instance(config.host_name, config.domain_name,
+                          config.dirman_password, config.dirman_password,
+                          pkcs12_info=(drmfile,),
+                          master_host=config.master_host_name,
+                          master_replication_port=config.ca_ds_port,
+                          subject_base=config.subject_base)
+
+    # Restart httpd since we changed it's config and added ipa-pki-proxy.conf
+    if postinstall:
+        ipaservices.knownservices.httpd.restart()
+
+
+    # The dogtag DS instance needs to be restarted after installation.
+    # The procedure for this is: stop dogtag, stop DS, start DS, start
+    # dogtag
+
+    service.print_msg("Restarting the directory and DRM servers")
+    drm.stop(dogtag.install_constants.PKI_INSTANCE_NAME)
+    ipaservices.knownservices.dirsrv.restart()
+    drm.start(dogtag.install_constants.PKI_INSTANCE_NAME)
+
+    return drm
+
+def update_cert_config(nickname, cert, dogtag_constants=None):
+    """
+    When renewing a DRM subsystem certificate the configuration file
+    needs to get the new certificate as well.
+
+    nickname is one of the known nicknames.
+    cert is a DER-encoded certificate.
+    """
+
+    if dogtag_constants is None:
+        dogtag_constants = dogtag.configured_constants()
+
+    # The cert directive to update per nickname
+    directives = {'auditSigningCert cert-pki-drm': 'kra.audit_signing.cert',
+                  'storageCert cert-pki-drm': 'kra.storage.cert',
+                  'transportCert cert-pki-drm': 'kra.transport.cert',
+                  'subsystemCert cert-pki-drm': 'kra.subsystem.cert',
+                  'Server-Cert cert-pki-ca': 'kra.sslserver.cert'}
+
+    with stopped_service(dogtag_constants.SERVICE_NAME,
+                         instance_name=dogtag_constants.PKI_INSTANCE_NAME):
+
+        installutils.set_directive(
+            dogtag.configured_constants().DRM_CS_CFG_PATH,
+            directives[nickname],
+            base64.b64encode(cert),
+            quotes=False, separator='=')
+
+def update_people_entry(uid, dercert):
+    """
+    Update the userCerticate for an entry in the dogtag ou=People. This
+    is needed when a certificate is renewed.
+
+    uid: uid of user to update
+    dercert: An X509.3 certificate in DER format
+
+    Logging is done via syslog
+
+    Returns True or False
+    """
+    dn = DN(('uid',uid),('ou','People'),('o','ipadrm'))
+    serial_number = x509.get_serial_number(dercert, datatype=x509.DER)
+    subject = x509.get_subject(dercert, datatype=x509.DER)
+    issuer = x509.get_issuer(dercert, datatype=x509.DER)
+
+    attempts = 0
+    dogtag_uri='ldap://localhost:%d' % DEFAULT_DSPORT
+    updated = False
+
+    try:
+        dm_password = certmonger.get_pin('internaldb')
+    except IOError, e:
+        syslog.syslog(syslog.LOG_ERR,
+                      'Unable to determine PIN for DRM instance: %s' % e)
+        return False
+
+    while attempts < 10:
+        conn = None
+        try:
+            conn = ldap2.ldap2(shared_instance=False, ldap_uri=dogtag_uri)
+            conn.connect(bind_dn=DN(('cn', 'directory manager')),
+                bind_pw=dm_password)
+            entry_attrs = conn.get_entry(dn, ['usercertificate'])
+            entry_attrs['usercertificate'].append(dercert)
+            entry_attrs['description'] = '2;%d;%s;%s' % (serial_number, issuer,
+                subject)
+            conn.update_entry(entry_attrs)
+            updated = True
+            break
+        except errors.NetworkError:
+            syslog.syslog(syslog.LOG_ERR,
+                          'Connection to %s failed, sleeping 30s' % dogtag_uri)
+            time.sleep(30)
+            attempts += 1
+        except errors.EmptyModlist:
+            updated = True
+            break
+        except Exception, e:
+            syslog.syslog(syslog.LOG_ERR,
+                          'Updating %s entry failed: %s' % (str(dn), e))
+            break
+        finally:
+            if conn.isconnected():
+                conn.disconnect()
+
+    if not updated:
+        syslog.syslog(syslog.LOG_ERR, 'Update failed.')
+        return False
+
+    return True
+
+if __name__ == "__main__":
+    standard_logging_setup("install.log")
+    ds = dsinstance.DsInstance()
+
+    drm = DRMInstance("EXAMPLE.COM")
+    drm.configure_instance("drmtest.example.com", "example.com",
+                           "password", "password")
diff --git a/ipaserver/install/installutils.py b/ipaserver/install/installutils.py
index daf81e890c33e9a9bd30763ff8d0788313a1dbda..d7d8160d010b6dd8d424fbed3ee87f8aa528202a 100644
--- a/ipaserver/install/installutils.py
+++ b/ipaserver/install/installutils.py
@@ -554,6 +554,23 @@ def read_replica_info_dogtag_port(config_dir):
 
     return dogtag_master_ds_port
 
+def read_replica_info_drm_enabled(config_dir):
+    """
+    Check the replica info to determine if a DRM has been installed
+    on the master
+    """
+    default_file = config_dir + "/default.conf"
+    if not ipautil.file_exists(default_file):
+        return False
+    else:
+        with open(default_file) as fd:
+            config = SafeConfigParser()
+            config.readfp(fd)
+
+            enable_drm = bool(config.get("global", "enable_drm"))
+            return enable_drm
+
+
 def check_server_configuration():
     """
     Check if IPA server is configured on the system.
diff --git a/ipaserver/install/ipa_replica_prepare.py b/ipaserver/install/ipa_replica_prepare.py
index e71dd22e4e19d7c79b96ebc12eec8cf70b436e4e..df3c1a17b4b9e1e650d869b332c11f2076cbbc75 100644
--- a/ipaserver/install/ipa_replica_prepare.py
+++ b/ipaserver/install/ipa_replica_prepare.py
@@ -370,6 +370,7 @@ class ReplicaPrepare(admintool.AdminTool):
         cacert_filename = "/var/kerberos/krb5kdc/cacert.pem"
         if ipautil.file_exists(cacert_filename):
             self.copy_info_file(cacert_filename, "cacert.pem")
+        self.copy_info_file("/etc/ipa/default.conf", "default.conf")
 
     def save_config(self):
         self.log.info("Finalizing configuration")
-- 
1.9.0

_______________________________________________
Freeipa-devel mailing list
Freeipa-devel@redhat.com
https://www.redhat.com/mailman/listinfo/freeipa-devel

Reply via email to