URL: https://github.com/freeipa/freeipa/pull/367
Author: stlaz
 Title: #367: Remove nsslib from IPA
Action: synchronized

To pull the PR as Git branch:
git remote add ghfreeipa https://github.com/freeipa/freeipa
git fetch ghfreeipa pull/367/head:pr367
git checkout pr367
From 186c84b68e541dabc51707f0bc93f0c69baa2f6e Mon Sep 17 00:00:00 2001
From: Stanislav Laznicka <slazn...@redhat.com>
Date: Tue, 20 Dec 2016 10:05:36 +0100
Subject: [PATCH 01/12] Remove NSSConnection from the Python RPC module

NSSConnection was causing a lot of trouble in the past and there is
a lot of logic around it just to make it not fail. What's more,
when using NSS to create an SSL connection in FIPS mode, NSS
always requires database password which makes the `ipa` command
totally unusable.

NSSConnection is therefore replaced with Python's
httplib.HTTPSConnection which is OpenSSL based.

The new HTTPS handling class, IPAHTTPSConnection, is prepared
to handle authentication with client certificate for connections
to Dogtag server as RA agent. It allows handling even for handling
separate client cert/private key in separate files and also for
encrypted private key files.

https://fedorahosted.org/freeipa/ticket/5695
---
 ipalib/config.py    |   3 ++
 ipalib/constants.py |   1 +
 ipalib/rpc.py       |  70 +++++++---------------------------
 ipalib/util.py      | 106 ++++++++++++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 124 insertions(+), 56 deletions(-)

diff --git a/ipalib/config.py b/ipalib/config.py
index 20591db..8ecada6 100644
--- a/ipalib/config.py
+++ b/ipalib/config.py
@@ -493,6 +493,9 @@ def _bootstrap(self, **overrides):
         if 'nss_dir' not in self:
             self.nss_dir = self._join('confdir', 'nssdb')
 
+        if 'ca_certfile' not in self:
+            self.ca_certfile = self._join('confdir', 'ca.crt')
+
         # Set plugins_on_demand:
         if 'plugins_on_demand' not in self:
             self.plugins_on_demand = (self.context == 'cli')
diff --git a/ipalib/constants.py b/ipalib/constants.py
index fa20624..82147f3 100644
--- a/ipalib/constants.py
+++ b/ipalib/constants.py
@@ -226,6 +226,7 @@
     ('conf_default', object),  # File containing context independent config
     ('plugins_on_demand', object),  # Whether to finalize plugins on-demand (bool)
     ('nss_dir', object),  # Path to nssdb, default {confdir}/nssdb
+    ('ca_certfile', object),  # Path to CA cert file
 
     # Set in Env._finalize_core():
     ('in_server', object),  # Whether or not running in-server (bool)
diff --git a/ipalib/rpc.py b/ipalib/rpc.py
index 31ed64e..1ea5d60 100644
--- a/ipalib/rpc.py
+++ b/ipalib/rpc.py
@@ -44,7 +44,7 @@
 import gssapi
 from dns import resolver, rdatatype
 from dns.exception import DNSException
-from nss.error import NSPRError
+from ssl import SSLError
 import six
 from six.moves import urllib
 
@@ -60,8 +60,7 @@
 from ipapython.cookie import Cookie
 from ipapython.dnsutil import DNSName
 from ipalib.text import _
-import ipapython.nsslib
-from ipapython.nsslib import NSSConnection
+from ipalib.util import IPAHTTPSConnection
 from ipalib.krb_utils import KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN, KRB5KRB_AP_ERR_TKT_EXPIRED, \
                              KRB5_FCC_PERM, KRB5_FCC_NOFILE, KRB5_CC_FORMAT, \
                              KRB5_REALM_CANT_RESOLVE, KRB5_CC_NOTFOUND, get_principal
@@ -470,48 +469,20 @@ def get_host_info(self, host):
 
         return (host, extra_headers, x509)
 
+
 class SSLTransport(LanguageAwareTransport):
     """Handles an HTTPS transaction to an XML-RPC server."""
-
-    def get_connection_dbdir(self):
-        """
-        If there is a connections open it may have already initialized
-        NSS database. Return the database location used by the connection.
-        """
-        for value in context.__dict__.values():
-            if not isinstance(value, Connection):
-                continue
-            if not isinstance(
-                    getattr(value.conn, '_ServerProxy__transport', None),
-                    SSLTransport):
-                continue
-            if hasattr(value.conn._ServerProxy__transport, 'dbdir'):
-                return value.conn._ServerProxy__transport.dbdir
-        return None
-
     def make_connection(self, host):
         host, self._extra_headers, _x509 = self.get_host_info(host)
 
         if self._connection and host == self._connection[0]:
             return self._connection[1]
 
-        dbdir = context.nss_dir
-        connection_dbdir = self.get_connection_dbdir()
-
-        if connection_dbdir:
-            # If an existing connection is already using the same NSS
-            # database there is no need to re-initialize.
-            no_init = dbdir == connection_dbdir
-
-        else:
-            # If the NSS database is already being used there is no
-            # need to re-initialize.
-            no_init = dbdir == ipapython.nsslib.current_dbdir
-
-        conn = NSSConnection(host, 443, dbdir=dbdir, no_init=no_init,
-                             tls_version_min=api.env.tls_version_min,
-                             tls_version_max=api.env.tls_version_max)
-        self.dbdir=dbdir
+        conn = IPAHTTPSConnection(
+            host, 443,
+            api.env.ca_certfile,
+            tls_version_min=api.env.tls_version_min,
+            tls_version_max=api.env.tls_version_max)
 
         conn.connect()
 
@@ -891,15 +862,15 @@ def apply_session_cookie(self, url):
         return session_url
 
     def create_connection(self, ccache=None, verbose=None, fallback=None,
-                          delegate=None, nss_dir=None):
+                          delegate=None, ca_certfile=None):
         if verbose is None:
             verbose = self.api.env.verbose
         if fallback is None:
             fallback = self.api.env.fallback
         if delegate is None:
             delegate = self.api.env.delegate
-        if nss_dir is None:
-            nss_dir = self.api.env.nss_dir
+        if ca_certfile is None:
+            ca_certfile = self.api.env.ca_certfile
         try:
             rpc_uri = self.env[self.env_rpc_uri_key]
             principal = get_principal(ccache_name=ccache)
@@ -917,7 +888,7 @@ def create_connection(self, ccache=None, verbose=None, fallback=None,
         except (errors.CCacheError, ValueError):
             # No session key, do full Kerberos auth
             pass
-        context.nss_dir = nss_dir
+        context.ca_certfile = ca_certfile
         urls = self.get_url_list(rpc_uri)
         serverproxy = None
         for url in urls:
@@ -1027,7 +998,7 @@ def forward(self, name, *args, **kw):
                 error=e.faultString,
                 server=server,
             )
-        except NSPRError as e:
+        except SSLError as e:
             raise NetworkError(uri=server, error=str(e))
         except ProtocolError as e:
             # By catching a 401 here we can detect the case where we have
@@ -1044,22 +1015,9 @@ def forward(self, name, *args, **kw):
                     # This shouldn't happen if we have a session but it isn't fatal.
                     pass
 
-                # Create a new serverproxy with the non-session URI. If there
-                # is an existing connection we need to save the NSS dbdir so
-                # we can skip an unnecessary NSS_Initialize() and avoid
-                # NSS_Shutdown issues.
+                # Create a new serverproxy with the non-session URI
                 serverproxy = self.create_connection(os.environ.get('KRB5CCNAME'), self.env.verbose, self.env.fallback, self.env.delegate)
-
-                dbdir = None
-                current_conn = getattr(context, self.id, None)
-                if current_conn is not None:
-                    dbdir = getattr(current_conn.conn._ServerProxy__transport, 'dbdir', None)
-                    if dbdir is not None:
-                        self.debug('Using dbdir %s' % dbdir)
                 setattr(context, self.id, Connection(serverproxy, self.disconnect))
-                if dbdir is not None:
-                    current_conn = getattr(context, self.id, None)
-                    current_conn.conn._ServerProxy__transport.dbdir = dbdir
                 return self.forward(name, *args, **kw)
             raise NetworkError(uri=server, error=e.errmsg)
         except socket.error as e:
diff --git a/ipalib/util.py b/ipalib/util.py
index 1509607..c3bcd6d 100644
--- a/ipalib/util.py
+++ b/ipalib/util.py
@@ -33,6 +33,7 @@
 import dns
 import encodings
 import sys
+import ssl
 from weakref import WeakKeyDictionary
 
 import netaddr
@@ -42,6 +43,12 @@
 from netaddr.core import AddrFormatError
 import six
 
+try:
+    from httplib import HTTPSConnection
+except ImportError:
+    # Python 3
+    from http.client import HTTPSConnection
+
 from ipalib import errors, messages
 from ipalib.constants import DOMAIN_LEVEL_0
 from ipalib.text import _
@@ -51,6 +58,7 @@
 from ipapython.dnsutil import resolve_ip_addresses
 from ipapython.ipa_log_manager import root_logger
 
+
 if six.PY3:
     unicode = str
 
@@ -187,6 +195,104 @@ def normalize_zone(zone):
         return zone
 
 
+class IPAHTTPSConnection(HTTPSConnection, object):
+    """
+    This class inherits from `object` because HTTPSConnection does not in
+    Python 2.7. This is to allow the use of super() in its and derived
+    classes' methods.
+    """
+
+    # pylint: disable=no-member
+    tls_cutoff_map = {
+        "ssl2": ssl.OP_NO_SSLv2,
+        "tls1.0": ssl.OP_NO_TLSv1,
+        "tls1.1": ssl.OP_NO_TLSv1_1,
+        "tls1.2": ssl.OP_NO_TLSv1_2,
+    }
+    # pylint: enable=no-member
+
+    def __init__(self, host, port=HTTPSConnection.default_port,
+                 cafile=None,
+                 client_certfile=None, client_keyfile=None,
+                 keyfile_passwd=None,
+                 tls_version_min="tls1.1",
+                 tls_version_max="tls1.2",
+                 **kwargs):
+        """
+        Set up a client HTTPS connection.
+
+        :param host:  The host to connect to
+        :param port:  The port to connect to, defaults to
+                   HTTPSConnection.default_port
+        :param cafile:  A PEM-format file containning the trusted
+                        CA certificates
+        :param client_certfile:
+                A PEM-format client certificate file that will be used to
+                identificate the user to the server.
+        :param client_keyfile:
+                A file with the client private key. If this argument is not
+                supplied, the key will be sought in client_certfile.
+        :param keyfile_passwd:
+                A path to the file which stores the password that is used to
+                encrypt client_keyfile. Leave default value if the keyfile
+                is not encrypted.
+        :returns An established HTTPS connection to host:port
+        """
+        if cafile is None:
+            raise RuntimeError("IPAHTTPSConnection requires cafile argument "
+                               "to perform server certificate verification")
+
+        # pylint: disable=no-member
+        tls_cutoff = [
+            ssl.OP_NO_SSLv2,
+            ssl.OP_NO_TLSv1,
+            ssl.OP_NO_TLSv1_1,
+            ssl.OP_NO_TLSv1_2
+        ]
+        # remove the slice of negating protocol options according to options
+        min_idx = tls_cutoff.index(self.tls_cutoff_map[tls_version_min])
+        max_idx = tls_cutoff.index(self.tls_cutoff_map[tls_version_max])
+        tls_use = tls_cutoff[min_idx:max_idx+1]
+        del(tls_cutoff[min_idx:max_idx+1])
+
+        # official Python documentation states that the best option to get
+        # TLSv1 and later is to setup SSLContext with PROTOCOL_SSLv23
+        # and then negate the insecure SSLv2 and SSLv3
+        ctx = ssl.SSLContext(ssl.PROTOCOL_SSLv23)
+        ctx.options |= (
+            ssl.OP_ALL | ssl.OP_NO_COMPRESSION | ssl.OP_SINGLE_DH_USE |
+            ssl.OP_SINGLE_ECDH_USE | ssl.OP_NO_SSLv3
+        )
+
+        # high ciphers without RC4, MD5, TripleDES, pre-shared key
+        # and secure remote password
+        ctx.set_ciphers("HIGH:!aNULL:!eNULL:!MD5:!RC4:!3DES:!PSK:!SRP")
+
+        # pylint: enable=no-member
+        for version in tls_cutoff:
+            ctx.options |= version
+
+        # make sure the given TLS version is available if Python decides to
+        # remove it from default TLS flags
+        for version in tls_use:
+            ctx.options &= ~version
+
+        ctx.verify_mode = ssl.CERT_REQUIRED
+        ctx.check_hostname = True
+        ctx.load_verify_locations(cafile)
+
+        if client_certfile is not None:
+            if keyfile_passwd is not None:
+                with open(keyfile_passwd) as pwd_f:
+                    passwd = pwd_f.read()
+            else:
+                passwd = None
+            ctx.load_cert_chain(client_certfile, client_keyfile, passwd)
+
+        super(IPAHTTPSConnection, self).__init__(host, port, context=ctx,
+                                                 **kwargs)
+
+
 def validate_dns_label(dns_label, allow_underscore=False, allow_slash=False):
     base_chars = 'a-z0-9'
     extra_chars = ''

From c07bb718660a787b971d691e39864566e8c516f0 Mon Sep 17 00:00:00 2001
From: Stanislav Laznicka <slazn...@redhat.com>
Date: Mon, 2 Jan 2017 17:00:00 +0100
Subject: [PATCH 02/12] Move RA agent certificate file export to a different
 location

HTTPS connection to certificate server requires client authentication
so we need a file with client certificate and private key prior to
its first occurence which happens during migration of certificate
profiles to LDAP.

https://fedorahosted.org/freeipa/ticket/5695
https://fedorahosted.org/freeipa/ticket/6392
---
 install/restart_scripts/renew_ra_cert |  4 ++--
 ipaplatform/base/paths.py             |  2 +-
 ipaserver/install/cainstance.py       |  5 ++++-
 ipaserver/install/dogtaginstance.py   |  4 ++--
 ipaserver/install/ipa_backup.py       |  2 +-
 ipaserver/install/krainstance.py      |  7 +------
 ipaserver/install/server/upgrade.py   | 11 +++++------
 ipaserver/plugins/dogtag.py           |  2 +-
 8 files changed, 17 insertions(+), 20 deletions(-)

diff --git a/install/restart_scripts/renew_ra_cert b/install/restart_scripts/renew_ra_cert
index d978f94..4dc6c2e 100644
--- a/install/restart_scripts/renew_ra_cert
+++ b/install/restart_scripts/renew_ra_cert
@@ -29,7 +29,7 @@ import traceback
 
 from ipalib.install.kinit import kinit_keytab
 from ipalib import api
-from ipaserver.install import certs, cainstance, krainstance
+from ipaserver.install import certs, cainstance, dogtaginstance
 from ipaplatform.paths import paths
 
 
@@ -61,7 +61,7 @@ def _main():
             cainstance.update_people_entry(dercert)
 
         if api.Command.kra_is_enabled()['result']:
-            krainstance.export_kra_agent_pem()
+            dogtaginstance.export_ra_agent_pem()
     finally:
         shutil.rmtree(tmpdir)
         api.Backend.ldap2.disconnect()
diff --git a/ipaplatform/base/paths.py b/ipaplatform/base/paths.py
index 8db9e61..6c35b08 100644
--- a/ipaplatform/base/paths.py
+++ b/ipaplatform/base/paths.py
@@ -138,7 +138,7 @@ class BasePathNamespace(object):
     ROOT_IPA_CACHE = "/root/.ipa_cache"
     ROOT_PKI = "/root/.pki"
     DOGTAG_ADMIN_P12 = "/root/ca-agent.p12"
-    KRA_AGENT_PEM = "/var/lib/ipa/radb/kra-agent.pem"
+    RA_AGENT_PEM = "/var/lib/ipa/ra-agent.pem"
     CACERT_P12 = "/root/cacert.p12"
     ROOT_IPA_CSR = "/root/ipa.csr"
     NAMED_PID = "/run/named/named.pid"
diff --git a/ipaserver/install/cainstance.py b/ipaserver/install/cainstance.py
index 1b7ada4..233ed50 100644
--- a/ipaserver/install/cainstance.py
+++ b/ipaserver/install/cainstance.py
@@ -64,7 +64,8 @@
 from ipaserver.install import ldapupdate
 from ipaserver.install import replication
 from ipaserver.install import sysupgrade
-from ipaserver.install.dogtaginstance import DogtagInstance
+from ipaserver.install.dogtaginstance import (
+    DogtagInstance, export_ra_agent_pem)
 from ipaserver.plugins import ldap2
 
 # We need to reset the template because the CA uses the regular boot
@@ -415,6 +416,8 @@ def configure_instance(self, host_name, dm_password, admin_password,
                 else:
                     self.step("importing RA certificate from PKCS #12 file",
                               lambda: self.import_ra_cert(ra_p12))
+                self.step("exporting KRA agent cert", export_ra_agent_pem)
+
             if not ra_only:
                 self.step("importing CA chain to RA certificate database", self.__import_ca_chain)
                 self.step("setting up signing cert profile", self.__setup_sign_profile)
diff --git a/ipaserver/install/dogtaginstance.py b/ipaserver/install/dogtaginstance.py
index 968f4b2..6b54359 100644
--- a/ipaserver/install/dogtaginstance.py
+++ b/ipaserver/install/dogtaginstance.py
@@ -72,7 +72,7 @@ def is_installing_replica(sys_type):
         return False
 
 
-def export_kra_agent_pem():
+def export_ra_agent_pem():
     """
     Export ipaCert with private key for client authentication.
     """
@@ -90,7 +90,7 @@ def export_kra_agent_pem():
     os.chown(filename, 0, pent.pw_gid)
     os.chmod(filename, 0o440)
 
-    os.rename(filename, paths.KRA_AGENT_PEM)
+    os.rename(filename, paths.RA_AGENT_PEM)
 
 
 class DogtagInstance(service.Service):
diff --git a/ipaserver/install/ipa_backup.py b/ipaserver/install/ipa_backup.py
index 9cf0d4c..9f35bf6 100644
--- a/ipaserver/install/ipa_backup.py
+++ b/ipaserver/install/ipa_backup.py
@@ -158,7 +158,7 @@ class Backup(admintool.AdminTool):
         paths.SMB_CONF,
         paths.SAMBA_KEYTAB,
         paths.DOGTAG_ADMIN_P12,
-        paths.KRA_AGENT_PEM,
+        paths.RA_AGENT_PEM,
         paths.CACERT_P12,
         paths.KRACERT_P12,
         paths.KRB5KDC_KDC_CONF,
diff --git a/ipaserver/install/krainstance.py b/ipaserver/install/krainstance.py
index ec38801..1d823bf 100644
--- a/ipaserver/install/krainstance.py
+++ b/ipaserver/install/krainstance.py
@@ -36,8 +36,7 @@
 from ipaserver.install import cainstance
 from ipaserver.install import installutils
 from ipaserver.install import ldapupdate
-from ipaserver.install.dogtaginstance import (export_kra_agent_pem,
-                                              DogtagInstance)
+from ipaserver.install.dogtaginstance import DogtagInstance
 from ipaserver.plugins import ldap2
 from ipapython.ipa_log_manager import log_mgr
 
@@ -118,7 +117,6 @@ def configure_instance(self, realm_name, host_name, dm_password,
             if not self.clone:
                 self.step("create KRA agent",
                           self.__create_kra_agent)
-        self.step("exporting KRA agent cert", export_kra_agent_pem)
         if not ra_only:
             if promote:
                 self.step("destroying installation admin user", self.teardown_admin)
@@ -279,9 +277,6 @@ def __spawn_instance(self):
             os.remove(cfg_file)
 
         shutil.move(paths.KRA_BACKUP_KEYS_P12, paths.KRACERT_P12)
-
-        export_kra_agent_pem()
-
         self.log.debug("completed creating KRA instance")
 
     def __create_kra_agent(self):
diff --git a/ipaserver/install/server/upgrade.py b/ipaserver/install/server/upgrade.py
index 509f196..71087fe 100644
--- a/ipaserver/install/server/upgrade.py
+++ b/ipaserver/install/server/upgrade.py
@@ -44,7 +44,6 @@
 from ipaserver.install import custodiainstance
 from ipaserver.install import sysupgrade
 from ipaserver.install import dnskeysyncinstance
-from ipaserver.install import krainstance
 from ipaserver.install import dogtaginstance
 from ipaserver.install import krbinstance
 from ipaserver.install import adtrustinstance
@@ -1383,10 +1382,10 @@ def fix_trust_flags():
     sysupgrade.set_upgrade_state('http', 'fix_trust_flags', True)
 
 
-def export_kra_agent_pem():
+def export_ra_agent_pem():
     root_logger.info('[Exporting KRA agent PEM file]')
 
-    if sysupgrade.get_upgrade_state('http', 'export_kra_agent_pem'):
+    if sysupgrade.get_upgrade_state('http', 'export_ra_agent_pem'):
         root_logger.info("KRA agent PEM file already exported")
         return
 
@@ -1394,9 +1393,9 @@ def export_kra_agent_pem():
         root_logger.info("KRA is not enabled")
         return
 
-    krainstance.export_kra_agent_pem()
+    dogtaginstance.export_ra_agent_pem()
 
-    sysupgrade.set_upgrade_state('http', 'export_kra_agent_pem', True)
+    sysupgrade.set_upgrade_state('http', 'export_ra_agent_pem', True)
 
 
 def update_mod_nss_protocol(http):
@@ -1636,7 +1635,7 @@ def upgrade_configuration():
     update_mod_nss_protocol(http)
     update_mod_nss_cipher_suite(http)
     fix_trust_flags()
-    export_kra_agent_pem()
+    export_ra_agent_pem()
     update_http_keytab(http)
     http.configure_gssproxy()
     http.start()
diff --git a/ipaserver/plugins/dogtag.py b/ipaserver/plugins/dogtag.py
index 6ff6d29..2156f07 100644
--- a/ipaserver/plugins/dogtag.py
+++ b/ipaserver/plugins/dogtag.py
@@ -2026,7 +2026,7 @@ def get_client(self):
             str(self.kra_port),
             'kra')
 
-        connection.set_authentication_cert(paths.KRA_AGENT_PEM)
+        connection.set_authentication_cert(paths.RA_AGENT_PEM)
 
         return KRAClient(connection, crypto)
 

From 3b89508f85c695e031313e6de549d0613209817f Mon Sep 17 00:00:00 2001
From: Stanislav Laznicka <slazn...@redhat.com>
Date: Wed, 4 Jan 2017 08:41:26 +0100
Subject: [PATCH 03/12] Don't run kra.configure_instance if not necessary

If kra should not be set up, don't run the code as it would only
prolong the installations.

Previously, krainstance configuration would be performed just to
export the client certificate and private key to authenticate to
certificate server. This is now performed somewhere else therefore
there's no need to run KRAInstance.configure_instance.

The kra.install() method still performs actions on replicas and
we're keeping it in server installer to conform to the installers
design.

https://fedorahosted.org/freeipa/ticket/5695
---
 ipaserver/install/kra.py         | 16 +++++-----
 ipaserver/install/krainstance.py | 65 ++++++++++++++++++++--------------------
 2 files changed, 40 insertions(+), 41 deletions(-)

diff --git a/ipaserver/install/kra.py b/ipaserver/install/kra.py
index 6f5c22b..eb74452 100644
--- a/ipaserver/install/kra.py
+++ b/ipaserver/install/kra.py
@@ -69,6 +69,8 @@ def install_check(api, replica_config, options):
 
 def install(api, replica_config, options):
     if replica_config is None:
+        if not options.setup_kra:
+            return
         realm_name = api.env.realm
         dm_password = options.dm_password
         host_name = api.env.host
@@ -76,7 +78,6 @@ def install(api, replica_config, options):
 
         pkcs12_info = None
         master_host = None
-        ra_only = not options.setup_kra
         promote = False
     else:
         krafile = os.path.join(replica_config.dir, 'kracert.p12')
@@ -96,6 +97,9 @@ def install(api, replica_config, options):
                     "  cacert.p12 file not found in replica file")
             shutil.copy(cafile, krafile)
 
+        if not replica_config.setup_kra:
+            return
+
         realm_name = replica_config.realm_name
         dm_password = replica_config.dirman_password
         host_name = replica_config.host_name
@@ -103,7 +107,6 @@ def install(api, replica_config, options):
 
         pkcs12_info = (krafile,)
         master_host = replica_config.kra_host_name
-        ra_only = not replica_config.setup_kra
         promote = options.promote
 
     kra = krainstance.KRAInstance(realm_name)
@@ -111,18 +114,15 @@ def install(api, replica_config, options):
                            subject_base=subject_base,
                            pkcs12_info=pkcs12_info,
                            master_host=master_host,
-                           ra_only=ra_only,
                            promote=promote)
 
     _service.print_msg("Restarting the directory server")
     ds = dsinstance.DsInstance()
     ds.restart()
+    kra.enable_client_auth_to_db(paths.KRA_CS_CFG_PATH)
 
-    if not ra_only:
-        kra.enable_client_auth_to_db(paths.KRA_CS_CFG_PATH)
-
-        # Restart apache for new proxy config file
-        services.knownservices.httpd.restart(capture_output=True)
+    # Restart apache for new proxy config file
+    services.knownservices.httpd.restart(capture_output=True)
 
 
 def uninstall(standalone):
diff --git a/ipaserver/install/krainstance.py b/ipaserver/install/krainstance.py
index 1d823bf..be17637 100644
--- a/ipaserver/install/krainstance.py
+++ b/ipaserver/install/krainstance.py
@@ -77,7 +77,7 @@ def __init__(self, realm):
     def configure_instance(self, realm_name, host_name, dm_password,
                            admin_password, pkcs12_info=None, master_host=None,
                            subject_base=None, subject=None,
-                           ra_only=False, promote=False):
+                           promote=False):
         """Create a KRA instance.
 
            To create a clone, pass in pkcs12_info.
@@ -99,38 +99,37 @@ def configure_instance(self, realm_name, host_name, dm_password,
         self.realm = realm_name
         self.suffix = ipautil.realm_to_suffix(realm_name)
 
-        if not ra_only:
-            # Confirm that a KRA does not already exist
-            if self.is_installed():
-                raise RuntimeError(
-                    "KRA already installed.")
-            # Confirm that a Dogtag 10 CA instance already exists
-            ca = cainstance.CAInstance(self.realm)
-            if not ca.is_installed():
-                raise RuntimeError(
-                    "KRA configuration failed.  "
-                    "A Dogtag CA must be installed first")
-
-            if promote:
-                self.step("creating installation admin user", self.setup_admin)
-            self.step("configuring KRA instance", self.__spawn_instance)
-            if not self.clone:
-                self.step("create KRA agent",
-                          self.__create_kra_agent)
-        if not ra_only:
-            if promote:
-                self.step("destroying installation admin user", self.teardown_admin)
-            self.step("restarting KRA", self.restart_instance)
-            self.step("configure certmonger for renewals",
-                      self.configure_certmonger_renewal)
-            self.step("configure certificate renewals", self.configure_renewal)
-            self.step("configure HTTP to proxy connections",
-                      self.http_proxy)
-            if not self.clone:
-                self.step("add vault container", self.__add_vault_container)
-            self.step("apply LDAP updates", self.__apply_updates)
-
-            self.step("enabling KRA instance", self.__enable_instance)
+        # Confirm that a KRA does not already exist
+        if self.is_installed():
+            raise RuntimeError(
+                "KRA already installed.")
+        # Confirm that a Dogtag 10 CA instance already exists
+        ca = cainstance.CAInstance(self.realm)
+        if not ca.is_installed():
+            raise RuntimeError(
+                "KRA configuration failed.  "
+                "A Dogtag CA must be installed first")
+
+        if promote:
+            self.step("creating installation admin user", self.setup_admin)
+        self.step("configuring KRA instance", self.__spawn_instance)
+        if not self.clone:
+            self.step("create KRA agent",
+                      self.__create_kra_agent)
+        if promote:
+            self.step("destroying installation admin user",
+                      self.teardown_admin)
+        self.step("restarting KRA", self.restart_instance)
+        self.step("configure certmonger for renewals",
+                  self.configure_certmonger_renewal)
+        self.step("configure certificate renewals", self.configure_renewal)
+        self.step("configure HTTP to proxy connections",
+                  self.http_proxy)
+        if not self.clone:
+            self.step("add vault container", self.__add_vault_container)
+        self.step("apply LDAP updates", self.__apply_updates)
+
+        self.step("enabling KRA instance", self.__enable_instance)
 
         self.start_creation(runtime=126)
 

From 9aaaef2ca36bd09711483ecc83a79f78c4411227 Mon Sep 17 00:00:00 2001
From: Stanislav Laznicka <slazn...@redhat.com>
Date: Tue, 3 Jan 2017 09:49:48 +0100
Subject: [PATCH 04/12] Move publishing of CA cert to cainstance creation on
 master

IPAHTTPSConnection which is set up first time in certificate profiles
migration to LDAP requires CA cert to be stored in a file.

https://fedorahosted.org/freeipa/ticket/5695
---
 ipaserver/install/cainstance.py | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/ipaserver/install/cainstance.py b/ipaserver/install/cainstance.py
index 233ed50..8c9e528 100644
--- a/ipaserver/install/cainstance.py
+++ b/ipaserver/install/cainstance.py
@@ -424,6 +424,8 @@ def configure_instance(self, host_name, dm_password, admin_password,
                 self.step("setting audit signing renewal to 2 years", self.set_audit_renewal)
                 self.step("restarting certificate server", self.restart_instance)
                 if not self.clone:
+                    self.step("publishing the CA certificate",
+                              lambda: self.publish_ca_cert(paths.IPA_CA_CRT))
                     self.step("adding RA agent as a trusted user", self.__create_ca_agent)
                 self.step("authorizing RA to modify profiles", configure_profiles_acl)
                 self.step("authorizing RA to manage lightweight CAs",

From e5decada75fc993c7df6cf4d84271de7ebbe4cbc Mon Sep 17 00:00:00 2001
From: Stanislav Laznicka <slazn...@redhat.com>
Date: Tue, 20 Dec 2016 10:23:47 +0100
Subject: [PATCH 05/12] Remove NSSConnection from Dogtag

Replaced NSSConnection with Python's httplib.HTTPSConnection.
This class is OpenSSL-based.

A client certificate with a private key is required to authenticate
against the certificate server. We facilitate the RA_AGENT_PEM which
already exists.

https://fedorahosted.org/freeipa/ticket/5695
---
 ipapython/dogtag.py             | 20 +++++++-------------
 ipaserver/install/cainstance.py |  6 ------
 ipaserver/install/certs.py      | 10 ++--------
 ipaserver/plugins/dogtag.py     | 38 ++++++++++++--------------------------
 4 files changed, 21 insertions(+), 53 deletions(-)

diff --git a/ipapython/dogtag.py b/ipapython/dogtag.py
index 01fc5cb..2e2b1c7 100644
--- a/ipapython/dogtag.py
+++ b/ipapython/dogtag.py
@@ -20,16 +20,16 @@
 import collections
 import xml.dom.minidom
 
-import nss.nss as nss
 import six
 # pylint: disable=import-error
 from six.moves.urllib.parse import urlencode
 # pylint: enable=import-error
 
 from ipalib import api, errors
+from ipalib.util import IPAHTTPSConnection
 from ipalib.errors import NetworkError
 from ipalib.text import _
-from ipapython import nsslib, ipautil
+from ipapython import ipautil
 from ipapython.ipa_log_manager import root_logger
 
 # Python 3 rename. The package is available in "six.moves.http_client", but
@@ -131,8 +131,8 @@ def ca_status(ca_host=None):
     return _parse_ca_status(body)
 
 
-def https_request(host, port, url, secdir, password, nickname,
-        method='POST', headers=None, body=None, **kw):
+def https_request(host, port, url, cafile, client_certfile,
+                  method='POST', headers=None, body=None, **kw):
     """
     :param method: HTTP request method (defalut: 'POST')
     :param url: The path (not complete URL!) to post to.
@@ -145,15 +145,9 @@ def https_request(host, port, url, secdir, password, nickname,
     """
 
     def connection_factory(host, port):
-        no_init = secdir == nsslib.current_dbdir
-        conn = nsslib.NSSConnection(host, port, dbdir=secdir, no_init=no_init,
-                                    tls_version_min=api.env.tls_version_min,
-                                    tls_version_max=api.env.tls_version_max)
-        conn.set_debuglevel(0)
-        conn.connect()
-        conn.sock.set_client_auth_data_callback(
-            nsslib.client_auth_data_callback,
-            nickname, password, nss.get_default_certdb())
+        conn = IPAHTTPSConnection(host, port, cafile, client_certfile,
+                                  tls_version_min=api.env.tls_version_min,
+                                  tls_version_max=api.env.tls_version_max)
         return conn
 
     if body is None:
diff --git a/ipaserver/install/cainstance.py b/ipaserver/install/cainstance.py
index 8c9e528..0f58ffe 100644
--- a/ipaserver/install/cainstance.py
+++ b/ipaserver/install/cainstance.py
@@ -1567,7 +1567,6 @@ def import_included_profiles():
         cn=['certprofiles'],
     )
 
-    api.Backend.ra_certprofile._read_password()
     api.Backend.ra_certprofile.override_port = 8443
 
     for (profile_id, desc, store_issued) in dogtag.INCLUDED_PROFILES:
@@ -1604,7 +1603,6 @@ def repair_profile_caIPAserviceCert():
     This function detects and repairs occurrences of this problem.
 
     """
-    api.Backend.ra_certprofile._read_password()
     api.Backend.ra_certprofile.override_port = 8443
 
     profile_id = 'caIPAserviceCert'
@@ -1647,8 +1645,6 @@ def migrate_profiles_to_ldap():
 
     """
     ensure_ldap_profiles_container()
-
-    api.Backend.ra_certprofile._read_password()
     api.Backend.ra_certprofile.override_port = 8443
 
     with open(paths.CA_CS_CFG_PATH) as f:
@@ -1733,8 +1729,6 @@ def ensure_ipa_authority_entry():
     """
 
     # find out authority id, issuer DN and subject DN of IPA CA
-    #
-    api.Backend.ra_lightweight_ca._read_password()
     api.Backend.ra_lightweight_ca.override_port = 8443
     with api.Backend.ra_lightweight_ca as lwca:
         data = lwca.read_ca('host-authority')
diff --git a/ipaserver/install/certs.py b/ipaserver/install/certs.py
index bca2504..e6b94f5 100644
--- a/ipaserver/install/certs.py
+++ b/ipaserver/install/certs.py
@@ -397,12 +397,9 @@ def issue_server_cert(self, certreq_fname, cert_fname):
                 'xmlOutput': 'true'}
 
         # Send the request to the CA
-        f = open(self.passwd_fname, "r")
-        password = f.readline()
-        f.close()
         result = dogtag.https_request(
             self.host_name, 8443, "/ca/ee/ca/profileSubmitSSLClient",
-            self.secdir, password, "ipaCert", **params)
+            api.env.ca_certfile, paths.RA_AGENT_PEM, **params)
         http_status, _http_headers, http_body = result
         root_logger.debug("CA answer: %s", http_body)
 
@@ -451,12 +448,9 @@ def issue_signing_cert(self, certreq_fname, cert_fname):
                 'xmlOutput': 'true'}
 
         # Send the request to the CA
-        f = open(self.passwd_fname, "r")
-        password = f.readline()
-        f.close()
         result = dogtag.https_request(
             self.host_name, 8443, "/ca/ee/ca/profileSubmitSSLClient",
-            self.secdir, password, "ipaCert", **params)
+            api.env.ca_certfile, paths.RA_AGENT_PEM, **params)
         http_status, _http_headers, http_body = result
         if http_status != 200:
             raise RuntimeError("Unable to submit cert request")
diff --git a/ipaserver/plugins/dogtag.py b/ipaserver/plugins/dogtag.py
index 2156f07..6963c4f 100644
--- a/ipaserver/plugins/dogtag.py
+++ b/ipaserver/plugins/dogtag.py
@@ -1238,30 +1238,18 @@ def _parse_dogtag_error(body):
             return None
 
     def __init__(self, api):
+        self.ca_cert = api.env.ca_certfile
         if api.env.in_tree:
-            self.sec_dir = api.env.dot_ipa + os.sep + 'alias'
-            self.pwd_file = self.sec_dir + os.sep + '.pwd'
+            self.client_certfile = os.path.join(
+                api.env.dot_ipa, 'ra-agent.pem')
         else:
-            self.sec_dir = paths.IPA_RADB_DIR
-            self.pwd_file = os.path.join(paths.IPA_RADB_DIR, 'pwdfile.txt')
-        self.noise_file = self.sec_dir + os.sep + '.noise'
-        self.ipa_key_size = "2048"
-        self.ipa_certificate_nickname = "ipaCert"
-        self.ca_certificate_nickname = "caCert"
-        self._read_password()
+            self.client_certfile = paths.RA_AGENT_PEM
         super(RestClient, self).__init__(api)
 
         # session cookie
         self.override_port = None
         self.cookie = None
 
-    def _read_password(self):
-        try:
-            with open(self.pwd_file) as f:
-                self.password = f.readline().strip()
-        except IOError:
-            self.password = ''
-
     @cachedproperty
     def ca_host(self):
         """
@@ -1288,9 +1276,8 @@ def __enter__(self):
             return
         status, resp_headers, _resp_body = dogtag.https_request(
             self.ca_host, self.override_port or self.env.ca_agent_port,
-            '/ca/rest/account/login',
-            self.sec_dir, self.password, self.ipa_certificate_nickname,
-            method='GET'
+            '/ca/rest/account/login', self.ca_cert,
+            self.client_certfile, 'GET'
         )
         cookies = ipapython.cookie.Cookie.parse(resp_headers.get('set-cookie', ''))
         if status != 200 or len(cookies) == 0:
@@ -1302,9 +1289,8 @@ def __exit__(self, exc_type, exc_value, traceback):
         """Log out of the REST API"""
         dogtag.https_request(
             self.ca_host, self.override_port or self.env.ca_agent_port,
-            '/ca/rest/account/logout',
-            self.sec_dir, self.password, self.ipa_certificate_nickname,
-            method='GET'
+            '/ca/rest/account/logout', self.ca_cert,
+            self.client_certfile, 'GET'
         )
         self.cookie = None
 
@@ -1344,9 +1330,8 @@ def _ssldo(self, method, path, headers=None, body=None, use_session=True):
         # perform main request
         status, resp_headers, resp_body = dogtag.https_request(
             self.ca_host, self.override_port or self.env.ca_agent_port,
-            resource,
-            self.sec_dir, self.password, self.ipa_certificate_nickname,
-            method=method, headers=headers, body=body
+            resource, self.ca_cert, self.client_certfile,
+            method, headers, body
         )
         if status < 200 or status >= 300:
             explanation = self._parse_dogtag_error(resp_body) or ''
@@ -1426,7 +1411,8 @@ def _sslget(self, url, port, **kw):
 
         Perform an HTTPS request
         """
-        return dogtag.https_request(self.ca_host, port, url, self.sec_dir, self.password, self.ipa_certificate_nickname, **kw)
+        return dogtag.https_request(self.ca_host, port, url, self.ca_cert,
+                                    self.client_certfile, **kw)
 
     def get_parse_result_xml(self, xml_text, parse_func):
         '''

From 7b2ff7a1e1864152e467dbe630784f79894fee4a Mon Sep 17 00:00:00 2001
From: Stanislav Laznicka <slazn...@redhat.com>
Date: Tue, 3 Jan 2017 13:31:01 +0100
Subject: [PATCH 06/12] Remove NSSConnection from otptoken plugin

Replace NSSConnection with IPAHTTPSConnection to be able to remove
NSSConnection for good.

https://fedorahosted.org/freeipa/ticket/5695
---
 ipaclient/plugins/otptoken.py | 13 ++++++-------
 1 file changed, 6 insertions(+), 7 deletions(-)

diff --git a/ipaclient/plugins/otptoken.py b/ipaclient/plugins/otptoken.py
index 885a612..1d57527 100644
--- a/ipaclient/plugins/otptoken.py
+++ b/ipaclient/plugins/otptoken.py
@@ -25,8 +25,8 @@
 from ipalib.messages import add_message, ResultFormattingError
 from ipalib.plugable import Registry
 from ipalib.frontend import Local
+from ipalib.util import IPAHTTPSConnection
 from ipapython.dn import DN
-from ipapython.nsslib import NSSConnection
 from ipapython.version import API_VERSION
 
 import locale
@@ -126,9 +126,7 @@ def __init__(self, **kwargs):
     def __inner(self, host, **kwargs):
         tmp = self.__kwargs.copy()
         tmp.update(kwargs)
-        # NSSConnection doesn't support timeout argument
-        tmp.pop('timeout', None)
-        return NSSConnection(host, **tmp)
+        return IPAHTTPSConnection(host, **tmp)
 
     def https_open(self, req):
         # pylint: disable=no-member
@@ -173,9 +171,10 @@ def forward(self, *args, **kwargs):
 
         # Sync the token.
         # pylint: disable=E1101
-        handler = HTTPSHandler(dbdir=api.env.nss_dir,
-                               tls_version_min=api.env.tls_version_min,
-                               tls_version_max=api.env.tls_version_max)
+        handler = HTTPSHandler(
+            cafile=api.env.ca_certfile,
+            tls_version_min=api.env.tls_version_min,
+            tls_version_max=api.env.tls_version_max)
         rsp = urllib.request.build_opener(handler).open(sync_uri, query)
         if rsp.getcode() == 200:
             status['result'][self.header] = rsp.info().get(self.header, 'unknown')

From 88094333c7d0a47fb6e8f9e16c728cd71a2fb1a5 Mon Sep 17 00:00:00 2001
From: Stanislav Laznicka <slazn...@redhat.com>
Date: Wed, 4 Jan 2017 08:47:59 +0100
Subject: [PATCH 07/12] Remove ipapython.nsslib as it is not used anymore

Previous changes allowed the removal of nsslib.

So long, and thanks for all the fish.

https://fedorahosted.org/freeipa/ticket/5695
---
 ipapython/nsslib.py | 287 ----------------------------------------------------
 1 file changed, 287 deletions(-)
 delete mode 100644 ipapython/nsslib.py

diff --git a/ipapython/nsslib.py b/ipapython/nsslib.py
deleted file mode 100644
index 08d05fc..0000000
--- a/ipapython/nsslib.py
+++ /dev/null
@@ -1,287 +0,0 @@
-# Authors: Rob Crittenden <rcrit...@redhat.com>
-#          John Dennis <jden...@redhat.com>
-#
-# Copyright (C) 2009    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/>.
-#
-
-from __future__ import print_function
-
-import getpass
-import socket
-from ipapython.ipa_log_manager import root_logger
-
-from nss.error import NSPRError
-import nss.io as io
-import nss.nss as nss
-import nss.ssl as ssl
-import nss.error as error
-
-# Python 3 rename. The package is available in "six.moves.http_client", but
-# pylint cannot handle classes from that alias
-try:
-    import httplib
-except ImportError:
-    # pylint: disable=import-error
-    import http.client as httplib
-
-# NSS database currently open
-current_dbdir = None
-
-def auth_certificate_callback(sock, check_sig, is_server, certdb):
-    cert_is_valid = False
-
-    cert = sock.get_peer_certificate()
-
-    pin_args = sock.get_pkcs11_pin_arg()
-    if pin_args is None:
-        pin_args = ()
-
-    # Define how the cert is being used based upon the is_server flag.  This may
-    # seem backwards, but isn't. If we're a server we're trying to validate a
-    # client cert. If we're a client we're trying to validate a server cert.
-    if is_server:
-        intended_usage = nss.certificateUsageSSLClient
-    else:
-        intended_usage = nss.certificateUsageSSLServer
-
-    try:
-        # If the cert fails validation it will raise an exception, the errno attribute
-        # will be set to the error code matching the reason why the validation failed
-        # and the strerror attribute will contain a string describing the reason.
-        approved_usage = cert.verify_now(certdb, check_sig, intended_usage, *pin_args)
-    except Exception as e:
-        root_logger.error(
-            'cert validation failed for "%s" (%s)', cert.subject,
-            e.strerror)  # pylint: disable=no-member
-        cert_is_valid = False
-        return cert_is_valid
-
-    root_logger.debug("approved_usage = %s intended_usage = %s",
-                              ', '.join(nss.cert_usage_flags(approved_usage)),
-                              ', '.join(nss.cert_usage_flags(intended_usage)))
-
-    # Is the intended usage a proper subset of the approved usage
-    cert_is_valid = bool(approved_usage & intended_usage)
-
-    # If this is a server, we're finished
-    if is_server or not cert_is_valid:
-        root_logger.debug('cert valid %s for "%s"', cert_is_valid,  cert.subject)
-        return cert_is_valid
-
-    # Certificate is OK.  Since this is the client side of an SSL
-    # connection, we need to verify that the name field in the cert
-    # matches the desired hostname.  This is our defense against
-    # man-in-the-middle attacks.
-
-    hostname = sock.get_hostname()
-    try:
-        # If the cert fails validation it will raise an exception
-        cert_is_valid = cert.verify_hostname(hostname)
-    except Exception as e:
-        root_logger.error('failed verifying socket hostname "%s" matches cert subject "%s" (%s)',
-                          hostname, cert.subject,
-                          e.strerror)  # pylint: disable=no-member
-        cert_is_valid = False
-        return cert_is_valid
-
-    root_logger.debug('cert valid %s for "%s"', cert_is_valid,  cert.subject)
-    return cert_is_valid
-
-def client_auth_data_callback(ca_names, chosen_nickname, password, certdb):
-    cert = None
-    if chosen_nickname:
-        try:
-            cert = nss.find_cert_from_nickname(chosen_nickname, password)
-            priv_key = nss.find_key_by_any_cert(cert, password)
-            return cert, priv_key
-        except NSPRError:
-            return False
-    else:
-        nicknames = nss.get_cert_nicknames(certdb, nss.SEC_CERT_NICKNAMES_USER)
-        for nickname in nicknames:
-            try:
-                cert = nss.find_cert_from_nickname(nickname, password)
-                if cert.check_valid_times():
-                    if cert.has_signer_in_ca_names(ca_names):
-                        priv_key = nss.find_key_by_any_cert(cert, password)
-                        return cert, priv_key
-            except NSPRError:
-                return False
-        return False
-
-_af_dict = {
-    socket.AF_INET: io.PR_AF_INET,
-    socket.AF_INET6: io.PR_AF_INET6,
-    socket.AF_UNSPEC: io.PR_AF_UNSPEC
-}
-
-class NSSAddressFamilyFallback(object):
-    def __init__(self, family):
-        self.sock_family = family
-        self.family = self._get_nss_family(self.sock_family)
-
-    def _get_nss_family(self, sock_family):
-        """
-        Translate a family from python socket module to nss family.
-        """
-        try:
-            return _af_dict[sock_family]
-        except KeyError:
-            raise ValueError('Uknown socket family %d\n', sock_family)
-
-    def _create_socket(self):
-        self.sock = io.Socket(family=self.family)
-
-    def connect_socket(self, host, port):
-        try:
-            addr_info = io.AddrInfo(host, family=self.family)
-        except Exception:
-            raise NSPRError(
-                error_code=error.PR_ADDRESS_NOT_SUPPORTED_ERROR,
-                error_message="Cannot resolve %s using family %s" % (host,
-                    io.addr_family_name(self.family)))
-
-        for net_addr in addr_info:
-            root_logger.debug("Connecting: %s", net_addr)
-            net_addr.port = port
-            self.family = net_addr.family
-            try:
-                self._create_socket()
-                self.sock.connect(net_addr)
-                return
-            except Exception as e:
-                root_logger.debug("Could not connect socket to %s, error: %s",
-                        net_addr, str(e))
-                root_logger.debug("Try to continue with next family...")
-                continue
-
-        raise NSPRError(
-            error_code=error.PR_ADDRESS_NOT_SUPPORTED_ERROR,
-            error_message="Could not connect to %s using any address" % host)
-
-
-class NSSConnection(httplib.HTTPConnection, NSSAddressFamilyFallback):
-    default_port = httplib.HTTPSConnection.default_port
-
-    def __init__(self, host, port=None, strict=None,
-                 dbdir=None, family=socket.AF_UNSPEC, no_init=False,
-                 tls_version_min='tls1.1', tls_version_max='tls1.2'):
-        """
-        :param host: the server to connect to
-        :param port: the port to use (default is set in HTTPConnection)
-        :param dbdir: the NSS database directory
-        :param family: network family to use (default AF_UNSPEC)
-        :param no_init: do not initialize the NSS database. This requires
-                        that the database has already been initialized or
-                        the request will fail.
-        :param tls_min_version: mininum version of SSL/TLS supported
-        :param tls_max_version: maximum version of SSL/TLS supported.
-        """
-        httplib.HTTPConnection.__init__(self, host, port, strict)
-        NSSAddressFamilyFallback.__init__(self, family)
-
-        root_logger.debug('%s init %s', self.__class__.__name__, host)
-
-        # If initialization is requested, initialize the new database.
-        if not no_init:
-
-            if nss.nss_is_initialized():
-                ssl.clear_session_cache()
-                try:
-                    nss.nss_shutdown()
-                except NSPRError as e:
-                    if e.errno != error.SEC_ERROR_NOT_INITIALIZED:
-                        raise e
-
-            if not dbdir:
-                raise RuntimeError("dbdir is required")
-
-            nss.nss_init(dbdir)
-
-            global current_dbdir
-            current_dbdir = dbdir
-
-        ssl.set_domestic_policy()
-        nss.set_password_callback(self.password_callback)
-        self.tls_version_min = str(tls_version_min)
-        self.tls_version_max = str(tls_version_max)
-
-    def _create_socket(self):
-        ssl_enable_renegotiation = getattr(
-            ssl, 'SSL_ENABLE_RENEGOTIATION', 20)
-        ssl_require_safe_negotiation = getattr(
-            ssl,'SSL_REQUIRE_SAFE_NEGOTIATION', 21)
-        ssl_renegotiate_requires_xtn = getattr(
-            ssl, 'SSL_RENEGOTIATE_REQUIRES_XTN', 2)
-
-        # Create the socket here so we can do things like let the caller
-        # override the NSS callbacks
-        self.sock = ssl.SSLSocket(family=self.family)
-        self.sock.set_ssl_option(ssl.SSL_SECURITY, True)
-        self.sock.set_ssl_option(ssl.SSL_HANDSHAKE_AS_CLIENT, True)
-        try:
-            self.sock.set_ssl_version_range(self.tls_version_min, self.tls_version_max)
-        except NSPRError:
-            root_logger.error('Failed to set TLS range to %s, %s' % (self.tls_version_min, self.tls_version_max))
-            raise
-        self.sock.set_ssl_option(ssl_require_safe_negotiation, False)
-        self.sock.set_ssl_option(ssl_enable_renegotiation, ssl_renegotiate_requires_xtn)
-        # Provide a callback which notifies us when the SSL handshake is complete
-        self.sock.set_handshake_callback(self.handshake_callback)
-
-        # Provide a callback to verify the servers certificate
-        self.sock.set_auth_certificate_callback(auth_certificate_callback,
-                                                nss.get_default_certdb())
-        self.sock.set_hostname(self.host)
-
-    def password_callback(self, slot, retry, password):
-        if not retry and password: return password
-        return getpass.getpass("Enter password for %s: " % slot.token_name)
-
-    def handshake_callback(self, sock):
-        """
-        Verify callback. If we get here then the certificate is ok.
-        """
-        channel = sock.get_ssl_channel_info()
-        suite = ssl.get_cipher_suite_info(channel.cipher_suite)
-        root_logger.debug("handshake complete, peer = %s", sock.get_peer_name())
-        root_logger.debug('Protocol: %s' % channel.protocol_version_str.upper())
-        root_logger.debug('Cipher: %s' % suite.cipher_suite_name)
-
-    def connect(self):
-        self.connect_socket(self.host, self.port)
-
-    def close(self):
-        """Close the connection to the HTTP server."""
-        if self.sock:
-            self.sock.close()   # close it manually... there may be other refs
-            self.sock = None
-            ssl.clear_session_cache()
-
-    def endheaders(self, message=None):
-        """
-        Explicitly close the connection if an error is returned after the
-        headers are sent. This will likely mean the initial SSL handshake
-        failed. If this isn't done then the connection is never closed and
-        subsequent NSS activities will fail with a BUSY error.
-        """
-        try:
-            # FIXME: httplib uses old-style classes so super doesn't work
-            httplib.HTTPConnection.endheaders(self, message)
-        except NSPRError as e:
-            self.close()
-            raise e

From 952577e4384906fd3150b933bef646cea08493ce Mon Sep 17 00:00:00 2001
From: Stanislav Laznicka <slazn...@redhat.com>
Date: Fri, 27 Jan 2017 08:58:00 +0100
Subject: [PATCH 08/12] Workaround for certmonger's "Subject" representations

If an OpenSSL certificate is requested in Certmonger
(CERT_STORAGE == "FILE") the "Subject" field of such Certificate
is ordered as received. However, when an NSS certificate is
requested, the "Subject" field takes the LDAP order
(components get reversed). This is a workaround so that the behavior
stays the same.

The workaround should be removed when
https://fedorahosted.org/certmonger/ticket/62 gets fixed.

https://fedorahosted.org/freeipa/ticket/5695
---
 install/certmonger/dogtag-ipa-ca-renew-agent-submit | 11 ++++++++++-
 ipalib/install/certmonger.py                        |  4 ++++
 2 files changed, 14 insertions(+), 1 deletion(-)

diff --git a/install/certmonger/dogtag-ipa-ca-renew-agent-submit b/install/certmonger/dogtag-ipa-ca-renew-agent-submit
index 750893d..ee47f61 100755
--- a/install/certmonger/dogtag-ipa-ca-renew-agent-submit
+++ b/install/certmonger/dogtag-ipa-ca-renew-agent-submit
@@ -35,6 +35,9 @@ import base64
 import contextlib
 import json
 
+from cryptography import x509 as crypto_x509
+from cryptography.hazmat.backends import default_backend
+
 import six
 
 from ipapython import ipautil
@@ -64,8 +67,14 @@ if six.PY3:
 
 IPA_CA_NICKNAME = 'caSigningCert cert-pki-ca'
 
+
 def get_nickname():
-    subject = os.environ.get('CERTMONGER_REQ_SUBJECT')
+    # we need to get the subject from a CSR in case we are requesting
+    # an OpenSSL certificate for which we have to reverse the order of its DN
+    # components thus changing the CERTMONGER_REQ_SUBJECT
+    csr = os.environ.get('CERTMONGER_CSR')
+    csr_obj = crypto_x509.load_pem_x509_csr(csr, default_backend())
+    subject = csr_obj.subject
     if not subject:
         return None
 
diff --git a/ipalib/install/certmonger.py b/ipalib/install/certmonger.py
index 951ca9a..999205b 100644
--- a/ipalib/install/certmonger.py
+++ b/ipalib/install/certmonger.py
@@ -32,6 +32,7 @@
 import tempfile
 from ipalib import api
 from ipapython.ipa_log_manager import root_logger
+from ipapython.dn import DN
 from ipaplatform.paths import paths
 from ipaplatform import services
 
@@ -329,6 +330,9 @@ def request_cert(
     """
     if storage == 'FILE':
         certfile, keyfile = certpath
+        # This is a workaround for certmonger having different Subject
+        # representation with NSS and OpenSSL
+        subject = str(DN(*reversed(DN(subject))))
     else:
         certfile = certpath
         keyfile = certpath

From 7863af1f31609b9275e75a2c87fda8a689dc4be8 Mon Sep 17 00:00:00 2001
From: Stanislav Laznicka <slazn...@redhat.com>
Date: Fri, 27 Jan 2017 09:34:23 +0100
Subject: [PATCH 09/12] Refactor certmonger for OpenSSL certificates

Currently, it was only possible to request an SSL certificate
via certmonger. Refactored start_tracking methods to allow for
OpenSSL certificates tracking.

https://fedorahosted.org/freeipa/ticket/5695
---
 ipalib/install/certmonger.py        | 87 ++++++++++++++++++++++++-------------
 ipaserver/install/cainstance.py     |  6 +--
 ipaserver/install/certs.py          |  3 +-
 ipaserver/install/dogtaginstance.py |  6 +--
 ipaserver/install/krbinstance.py    |  3 +-
 5 files changed, 63 insertions(+), 42 deletions(-)

diff --git a/ipalib/install/certmonger.py b/ipalib/install/certmonger.py
index 999205b..7ad19e7 100644
--- a/ipalib/install/certmonger.py
+++ b/ipalib/install/certmonger.py
@@ -300,7 +300,7 @@ def add_subject(request_id, subject):
 
 
 def request_and_wait_for_cert(
-        certpath, nickname, subject, principal, passwd_fname=None,
+        certpath, subject, principal, nickname=None, passwd_fname=None,
         dns=None, ca='IPA', profile=None,
         pre_command=None, post_command=None, storage='NSSDB'):
     """
@@ -308,7 +308,7 @@ def request_and_wait_for_cert(
 
     The method also waits for the certificate to be available.
     """
-    reqId = request_cert(certpath, nickname, subject, principal,
+    reqId = request_cert(certpath, subject, principal, nickname,
                          passwd_fname, dns, ca, profile,
                          pre_command, post_command, storage)
     state = wait_for_request(reqId, api.env.startup_timeout)
@@ -319,7 +319,7 @@ def request_and_wait_for_cert(
 
 
 def request_cert(
-        certpath, nickname, subject, principal, passwd_fname=None,
+        certpath, subject, principal, nickname=None, passwd_fname=None,
         dns=None, ca='IPA', profile=None,
         pre_command=None, post_command=None, storage='NSSDB'):
     """
@@ -342,9 +342,11 @@ def request_cert(
     if not ca_path:
         raise RuntimeError('{} CA not found'.format(ca))
     request_parameters = dict(KEY_STORAGE=storage, CERT_STORAGE=storage,
-                              CERT_LOCATION=certfile, CERT_NICKNAME=nickname,
-                              KEY_LOCATION=keyfile, KEY_NICKNAME=nickname,
+                              CERT_LOCATION=certfile, KEY_LOCATION=keyfile,
                               SUBJECT=subject, CA=ca_path)
+    if nickname:
+        request_parameters["CERT_NICKNAME"] = nickname
+        request_parameters["KEY_NICKNAME"] = nickname
     if principal:
         request_parameters['PRINCIPAL'] = [principal]
     if dns is not None and len(dns) > 0:
@@ -378,33 +380,46 @@ def request_cert(
     return request.obj_if.get_nickname()
 
 
-def start_tracking(nickname, secdir, password_file=None, command=None):
+def start_tracking(
+        certpath, nickname=None, password_file=None, post_command=None,
+        storage='NSSDB'):
     """
-    Tell certmonger to track the given certificate nickname in NSS
-    database in secdir protected by optional password file password_file.
+    Tell certmonger to track the given certificate in either a file or an NSS
+    database. The certificate access can be protected by a password_file.
 
-    command is an optional parameter which specifies a command for
+    post_command is an optional parameter which specifies a command for
     certmonger to run when it renews a certificate. This command must
     reside in /usr/lib/ipa/certmonger to work with SELinux.
 
     Returns certificate nickname.
     """
+    if storage == 'FILE':
+        certfile, keyfile = certpath
+    else:
+        certfile = certpath
+        keyfile = certpath
+
     cm = _certmonger()
-    params = {'TRACK': True}
-    params['cert-nickname'] = nickname
-    params['cert-database'] = os.path.abspath(secdir)
-    params['cert-storage'] = 'NSSDB'
-    params['key-nickname'] = nickname
-    params['key-database'] = os.path.abspath(secdir)
-    params['key-storage'] = 'NSSDB'
     ca_path = cm.obj_if.find_ca_by_nickname('IPA')
     if not ca_path:
         raise RuntimeError('IPA CA not found')
-    params['ca'] = ca_path
-    if command:
-        params['cert-postsave-command'] = command
+
+    params = {
+        'TRACK': True,
+        'CERT_STORAGE': storage,
+        'KEY_STORAGE': storage,
+        'CERT_LOCATION': certfile,
+        'KEY_LOCATION': keyfile,
+        'CA': ca_path
+    }
+    if nickname:
+        params['CERT_NICKNAME'] = nickname
+        params['KEY_NICKNAME'] = nickname
     if password_file:
         params['KEY_PIN_FILE'] = os.path.abspath(password_file)
+    if post_command:
+        params['cert-postsave-command'] = post_command
+
     result = cm.obj_if.add_request(params)
     try:
         if result[0]:
@@ -544,8 +559,9 @@ def get_pin(token):
     return None
 
 
-def dogtag_start_tracking(ca, nickname, pin, pinfile, secdir, pre_command,
-                          post_command, profile=None):
+def dogtag_start_tracking(
+        certpath, ca, nickname=None, pin=None, pinfile=None,
+        pre_command=None, post_command=None, profile=None, storage="NSSDB"):
     """
     Tell certmonger to start tracking a dogtag CA certificate. These
     are handled differently because their renewal must be done directly
@@ -559,20 +575,29 @@ def dogtag_start_tracking(ca, nickname, pin, pinfile, secdir, pre_command,
 
     Both commands can be None.
     """
+    if storage == 'FILE':
+        certfile, keyfile = certpath
+    else:
+        certfile = certpath
+        keyfile = certpath
 
     cm = _certmonger()
     certmonger_cmd_template = paths.CERTMONGER_COMMAND_TEMPLATE
 
-    params = {'TRACK': True}
-    params['cert-nickname'] = nickname
-    params['cert-database'] = os.path.abspath(secdir)
-    params['cert-storage'] = 'NSSDB'
-    params['key-nickname'] = nickname
-    params['key-database'] = os.path.abspath(secdir)
-    params['key-storage'] = 'NSSDB'
+    params = {
+        'TRACK': True,
+        'CERT_STORAGE': storage,
+        'KEY_STORAGE': storage,
+        'CERT_LOCATION': certfile,
+        'KEY_LOCATION': keyfile
+    }
     ca_path = cm.obj_if.find_ca_by_nickname(ca)
+    if nickname:
+        params['CERT_NICKNAME'] = nickname
+        params['KEY_NICKNAME'] = nickname
+
     if ca_path:
-        params['ca'] = ca_path
+        params['CA'] = ca_path
     if pin:
         params['KEY_PIN'] = pin
     if pinfile:
@@ -622,9 +647,9 @@ def wait_for_request(request_id, timeout=120):
     return state
 
 if __name__ == '__main__':
-    request_id = request_cert(paths.HTTPD_ALIAS_DIR, "Test",
+    request_id = request_cert(paths.HTTPD_ALIAS_DIR,
                               "cn=tiger.example.com,O=IPA",
-                              "HTTP/tiger.example....@example.com")
+                              "HTTP/tiger.example....@example.com", "Test")
     csr = get_request_value(request_id, 'csr')
     print(csr)
     stop_tracking(request_id)
diff --git a/ipaserver/install/cainstance.py b/ipaserver/install/cainstance.py
index 0f58ffe..90883b6 100644
--- a/ipaserver/install/cainstance.py
+++ b/ipaserver/install/cainstance.py
@@ -996,11 +996,10 @@ def unconfigure_certmonger_renewal_guard(self):
     def configure_agent_renewal(self):
         try:
             certmonger.dogtag_start_tracking(
+                certpath=paths.IPA_RADB_DIR,
                 ca='dogtag-ipa-ca-renew-agent',
                 nickname='ipaCert',
-                pin=None,
                 pinfile=os.path.join(paths.IPA_RADB_DIR, 'pwdfile.txt'),
-                secdir=paths.IPA_RADB_DIR,
                 pre_command='renew_ra_cert_pre',
                 post_command='renew_ra_cert')
         except RuntimeError as e:
@@ -1797,9 +1796,8 @@ def add_lightweight_ca_tracking_requests(logger, lwcas):
         if request_id is None:
             try:
                 certmonger.dogtag_start_tracking(
-                    secdir=paths.PKI_TOMCAT_ALIAS_DIR,
+                    certpath=paths.PKI_TOMCAT_ALIAS_DIR,
                     pin=certmonger.get_pin('internal'),
-                    pinfile=None,
                     nickname=nickname,
                     ca=ipalib.constants.RENEWAL_CA_NAME,
                     pre_command='stop_pkicad',
diff --git a/ipaserver/install/certs.py b/ipaserver/install/certs.py
index e6b94f5..15f9545 100644
--- a/ipaserver/install/certs.py
+++ b/ipaserver/install/certs.py
@@ -310,7 +310,8 @@ def track_server_cert(self, nickname, principal, password_file=None, command=Non
         if command is not None and not os.path.isabs(command):
             command = paths.CERTMONGER_COMMAND_TEMPLATE % (command)
         try:
-            request_id = certmonger.start_tracking(nickname, self.secdir, password_file, command)
+            request_id = certmonger.start_tracking(
+                self.secdir, nickname, password_file, command)
         except RuntimeError as e:
             root_logger.error("certmonger failed starting to track certificate: %s" % str(e))
             return
diff --git a/ipaserver/install/dogtaginstance.py b/ipaserver/install/dogtaginstance.py
index 6b54359..86c819c 100644
--- a/ipaserver/install/dogtaginstance.py
+++ b/ipaserver/install/dogtaginstance.py
@@ -303,11 +303,10 @@ def configure_renewal(self):
         for nickname, profile in self.tracking_reqs:
             try:
                 certmonger.dogtag_start_tracking(
+                    certpath=self.nss_db,
                     ca='dogtag-ipa-ca-renew-agent',
                     nickname=nickname,
                     pin=pin,
-                    pinfile=None,
-                    secdir=self.nss_db,
                     pre_command='stop_pkicad',
                     post_command='renew_ca_cert "%s"' % nickname,
                     profile=profile)
@@ -324,11 +323,10 @@ def track_servercert(self):
         pin = self.__get_pin()
         try:
             certmonger.dogtag_start_tracking(
+                certpath=self.nss_db,
                 ca='dogtag-ipa-ca-renew-agent',
                 nickname=self.server_cert_name,
                 pin=pin,
-                pinfile=None,
-                secdir=self.nss_db,
                 pre_command='stop_pkicad',
                 post_command='renew_ca_cert "%s"' % self.server_cert_name)
         except RuntimeError as e:
diff --git a/ipaserver/install/krbinstance.py b/ipaserver/install/krbinstance.py
index 44b3821..8d0d79d 100644
--- a/ipaserver/install/krbinstance.py
+++ b/ipaserver/install/krbinstance.py
@@ -361,8 +361,7 @@ def setup_pkinit(self):
             krbtgt = "krbtgt/" + self.realm + "@" + self.realm
             certpath = (paths.KDC_CERT, paths.KDC_KEY)
             try:
-                reqid = certmonger.request_cert(certpath, u'KDC-Cert',
-                                                subject, krbtgt,
+                reqid = certmonger.request_cert(certpath, subject, krbtgt,
                                                 dns=self.fqdn, storage='FILE',
                                                 profile='KDCs_PKINIT_Certs')
             except dbus.DBusException as e:

From 0f7b039752e818751f22ed6ac52bac4f885651a2 Mon Sep 17 00:00:00 2001
From: Stanislav Laznicka <slazn...@redhat.com>
Date: Wed, 1 Feb 2017 09:14:56 +0100
Subject: [PATCH 10/12] Added an OpenSSL handler for Custodia store

This is a preparation step to be able to handle sending RA agent
certificate over Custodia during domain level 1 replica installation.

https://fedorahosted.org/freeipa/ticket/5695
---
 ipaserver/secrets/store.py | 61 ++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 61 insertions(+)

diff --git a/ipaserver/secrets/store.py b/ipaserver/secrets/store.py
index b2d724d..1051c50 100644
--- a/ipaserver/secrets/store.py
+++ b/ipaserver/secrets/store.py
@@ -191,6 +191,67 @@ def import_key(self, value):
         conn.modify_s('cn=config', mods)
 
 
+class OpenSSLHandler(DBMAPHandler):
+    def __init__(self, config, dbmap, nickname=None):
+        if 'type' not in dbmap or dbmap['type'] != 'OPENSSL':
+            raise ValueError('Invalid type "{t}", expected OPENSSL'
+                             .format(t=dbmap['type']))
+        self.certfile = dbmap['certfile']
+        self.keyfile = dbmap.get(['keyfile'])
+
+    def export_key(self):
+        _fd, tmpfile = tempfile.mkstemp(dir=paths.TMP)
+        password = ipautil.ipa_generate_password()
+        args = [
+            paths.OPENSSL,
+            "pkcs12", "-export",
+            "-in", self.certfile,
+            "-out", tmpfile,
+            "-password", "pass:{pwd}".format(pwd=password)
+        ]
+        if self.keyfile is not None:
+            args.extend(["-inkey", self.keyfile])
+
+        try:
+            ipautil.run(args, nolog=password)
+            with open(tmpfile, 'r') as f:
+                data = f.read()
+        finally:
+            os.remove(tmpfile)
+        return json_encode({'export password': password,
+                            'pkcs12 data': b64encode(data)})
+
+    def import_key(self, value):
+        v = json_decode(value)
+        data = b64decode(v['pkcs12 data'])
+        password = v['export password']
+        try:
+            _fd, tmpdata = tempfile.mkstemp(dir=paths.TMP)
+            with open(tmpdata, 'w') as f:
+                f.write(data)
+
+            # get the certificate from the file
+            ipautil.run([paths.OPENSSL,
+                         "pkcs12",
+                         "-in", tmpdata,
+                         "-clcerts", "-nokeys",
+                         "-out", self.certfile,
+                         "-passin", "pass:{pwd}".format(pwd=password)],
+                        nolog=(password))
+
+            if self.keyfile is not None:
+                # get the private key from the file
+                ipautil.run([paths.OPENSSL,
+                             "pkcs12",
+                             "-in", tmpdata,
+                             "-nocerts", "-nodes",
+                             "-out", self.keyfile,
+                             "-passin", "pass:{pwd}".format(pwd=password)],
+                            nolog=(password))
+        finally:
+            os.remove(tmpdata)
+
+
 NAME_DB_MAP = {
     'ca': {
         'type': 'NSSDB',

From 911505dd22682bf0a1f69f63e1d129b2200a08d3 Mon Sep 17 00:00:00 2001
From: Stanislav Laznicka <slazn...@redhat.com>
Date: Fri, 13 Jan 2017 09:08:42 +0100
Subject: [PATCH 11/12] Moving ipaCert from HTTPD_ALIAS_DIR

The "ipaCert" nicknamed certificate is not required to be
in /etc/httpd/alias NSSDB anymore as we were keeping a copy
of this file in a separate file anyway. Remove it from there
and track only the file.

https://fedorahosted.org/freeipa/ticket/5695
---
 .../certmonger/dogtag-ipa-ca-renew-agent-submit    |   9 +-
 install/restart_scripts/renew_ra_cert              |  30 ++--
 ipaplatform/base/paths.py                          |   2 +
 ipapython/dogtag.py                                |   6 +-
 ipaserver/install/ca.py                            |  15 +-
 ipaserver/install/cainstance.py                    |  72 ++++----
 ipaserver/install/certs.py                         |   6 +-
 ipaserver/install/custodiainstance.py              |   4 +
 ipaserver/install/dogtaginstance.py                |  22 ---
 ipaserver/install/dsinstance.py                    |  17 +-
 ipaserver/install/httpinstance.py                  |  11 +-
 ipaserver/install/ipa_replica_prepare.py           |  24 ++-
 ipaserver/install/krainstance.py                   |  12 +-
 ipaserver/install/plugins/ca_renewal_master.py     |  11 +-
 ipaserver/install/server/upgrade.py                | 187 +++++++++++----------
 ipaserver/plugins/dogtag.py                        |  20 ++-
 ipaserver/secrets/store.py                         |   8 +-
 17 files changed, 237 insertions(+), 219 deletions(-)

diff --git a/install/certmonger/dogtag-ipa-ca-renew-agent-submit b/install/certmonger/dogtag-ipa-ca-renew-agent-submit
index ee47f61..c64f7be 100755
--- a/install/certmonger/dogtag-ipa-ca-renew-agent-submit
+++ b/install/certmonger/dogtag-ipa-ca-renew-agent-submit
@@ -216,10 +216,11 @@ def request_cert():
     syslog.syslog(syslog.LOG_NOTICE,
                   "Forwarding request to dogtag-ipa-renew-agent")
 
-    path = paths.DOGTAG_IPA_RENEW_AGENT_SUBMIT
-    args = [path, '--dbdir', paths.IPA_RADB_DIR]
-    args.extend(sys.argv[1:])
-    args.extend(['--submit-option', "requestor_name=IPA"])
+    args = ([paths.DOGTAG_IPA_RENEW_AGENT_SUBMIT,
+             "--cafile", paths.IPA_CA_CRT,
+             "--certfile", paths.RA_AGENT_PEM,
+             "--keyfile", paths.RA_AGENT_KEY] +
+            sys.argv[1:] + ['--submit-option', "requestor_name=IPA"])
     if os.environ.get('CERTMONGER_CA_PROFILE') == 'caCACert':
         args += ['-N', '-O', 'bypassCAnotafter=true']
     result = ipautil.run(args, raiseonerr=False, env=os.environ,
diff --git a/install/restart_scripts/renew_ra_cert b/install/restart_scripts/renew_ra_cert
index 4dc6c2e..dcbdb55 100644
--- a/install/restart_scripts/renew_ra_cert
+++ b/install/restart_scripts/renew_ra_cert
@@ -28,14 +28,14 @@ import shutil
 import traceback
 
 from ipalib.install.kinit import kinit_keytab
-from ipalib import api
-from ipaserver.install import certs, cainstance, dogtaginstance
+from ipalib import api, x509
+from ipaserver.install import certs, cainstance
 from ipaplatform.paths import paths
 
+from cryptography.hazmat.primitives import serialization
 
-def _main():
-    nickname = 'ipaCert'
 
+def _main():
     api.bootstrap(in_server=True, context='restart', confdir=paths.ETC_IPA)
     api.finalize()
     api.Backend.ldap2.connect()
@@ -48,20 +48,28 @@ def _main():
         os.environ['KRB5CCNAME'] = ccache_filename
 
         ca = cainstance.CAInstance(host_name=api.env.host)
+        ra_certpath = paths.RA_AGENT_PEM
         if ca.is_renewal_master():
             # Fetch the new certificate
-            db = certs.CertDB(api.env.realm)
-            dercert = db.get_cert_from_db(nickname, pem=False)
-            if not dercert:
+            try:
+                cert = x509.load_certificate_from_file(ra_certpath)
+            except IOError as e:
+                syslog.syslog(
+                    syslog.LOG_ERR, "Can't open '{certpath}': {err}"
+                    .format(certpath=ra_certpath, err=e)
+                )
+                sys.exit(1)
+            except (TypeError, ValueError):
                 syslog.syslog(
-                    syslog.LOG_ERR, "No certificate %s found." % nickname)
+                    syslog.LOG_ERR, "'{certpath}' is not a valid certificate "
+                    "file".format(certpath=ra_certpath)
+                )
                 sys.exit(1)
 
+            dercert = cert.public_bytes(serialization.Encoding.DER)
+
             # Load it into dogtag
             cainstance.update_people_entry(dercert)
-
-        if api.Command.kra_is_enabled()['result']:
-            dogtaginstance.export_ra_agent_pem()
     finally:
         shutil.rmtree(tmpdir)
         api.Backend.ldap2.disconnect()
diff --git a/ipaplatform/base/paths.py b/ipaplatform/base/paths.py
index 6c35b08..85c584d 100644
--- a/ipaplatform/base/paths.py
+++ b/ipaplatform/base/paths.py
@@ -139,6 +139,7 @@ class BasePathNamespace(object):
     ROOT_PKI = "/root/.pki"
     DOGTAG_ADMIN_P12 = "/root/ca-agent.p12"
     RA_AGENT_PEM = "/var/lib/ipa/ra-agent.pem"
+    RA_AGENT_KEY = "/var/lib/ipa/ra-agent.key"
     CACERT_P12 = "/root/cacert.p12"
     ROOT_IPA_CSR = "/root/ipa.csr"
     NAMED_PID = "/run/named/named.pid"
@@ -194,6 +195,7 @@ class BasePathNamespace(object):
     PAM_KRB5_SO_64 = "/usr/lib64/security/pam_krb5.so"
     DOGTAG_IPA_CA_RENEW_AGENT_SUBMIT = "/usr/libexec/certmonger/dogtag-ipa-ca-renew-agent-submit"
     DOGTAG_IPA_RENEW_AGENT_SUBMIT = "/usr/libexec/certmonger/dogtag-ipa-renew-agent-submit"
+    CERTMONGER_DOGTAG_SUBMIT = "/usr/libexec/certmonger/dogtag-submit"
     IPA_SERVER_GUARD = "/usr/libexec/certmonger/ipa-server-guard"
     GENERATE_RNDC_KEY = "/usr/libexec/generate-rndc-key.sh"
     IPA_DNSKEYSYNCD_REPLICA = "/usr/libexec/ipa/ipa-dnskeysync-replica"
diff --git a/ipapython/dogtag.py b/ipapython/dogtag.py
index 2e2b1c7..72602fe 100644
--- a/ipapython/dogtag.py
+++ b/ipapython/dogtag.py
@@ -131,8 +131,9 @@ def ca_status(ca_host=None):
     return _parse_ca_status(body)
 
 
-def https_request(host, port, url, cafile, client_certfile,
-                  method='POST', headers=None, body=None, **kw):
+def https_request(
+    host, port, url, cafile, client_certfile, client_keyfile,
+    method='POST', headers=None, body=None, **kw):
     """
     :param method: HTTP request method (defalut: 'POST')
     :param url: The path (not complete URL!) to post to.
@@ -146,6 +147,7 @@ def https_request(host, port, url, cafile, client_certfile,
 
     def connection_factory(host, port):
         conn = IPAHTTPSConnection(host, port, cafile, client_certfile,
+                                  client_keyfile,
                                   tls_version_min=api.env.tls_version_min,
                                   tls_version_max=api.env.tls_version_max)
         return conn
diff --git a/ipaserver/install/ca.py b/ipaserver/install/ca.py
index 8e92ef0..d01e426 100644
--- a/ipaserver/install/ca.py
+++ b/ipaserver/install/ca.py
@@ -183,8 +183,7 @@ def install_check(standalone, replica_config, options):
 
         for db in (cadb, dsdb):
             for nickname, _trust_flags in db.list_certs():
-                if nickname in (certdb.get_ca_nickname(realm_name),
-                                'ipaCert'):
+                if nickname == certdb.get_ca_nickname(realm_name):
                     raise ScriptError(
                         "Certificate with nickname %s is present in %s, "
                         "cannot continue." % (nickname, db.secdir))
@@ -317,15 +316,15 @@ def install_step_1(standalone, replica_config, options):
         cadb = certs.CertDB(realm_name, subject_base=subject_base)
         dsdb = certs.CertDB(realm_name, nssdir=dirname, subject_base=subject_base)
         trust_flags = dict(reversed(cadb.list_certs()))
-        trust_chain = cadb.find_root_cert('ipaCert')[:-1]
-        for nickname in trust_chain[:-1]:
-            cert = cadb.get_cert_from_db(nickname, pem=False)
+        trust_chain = certstore.get_ca_certs(
+            api.Backend.ldap2, api.env.basedn, api.env.realm, True)
+        for cert, nickname, _trusted, _ext_key_usage in trust_chain[:-1]:
             dsdb.add_cert(cert, nickname, trust_flags[nickname])
             certstore.put_ca_cert_nss(api.Backend.ldap2, api.env.basedn,
                                       cert, nickname, trust_flags[nickname])
 
-        nickname = trust_chain[-1]
-        cert = cadb.get_cert_from_db(nickname, pem=False)
+        nickname = trust_chain[-1][1]
+        cert = trust_chain[-1][0]
         dsdb.add_cert(cert, nickname, trust_flags[nickname])
         certstore.put_ca_cert_nss(api.Backend.ldap2, api.env.basedn,
                                   cert, nickname, trust_flags[nickname],
@@ -358,6 +357,8 @@ def install_step_1(standalone, replica_config, options):
 def uninstall():
     ca_instance = cainstance.CAInstance(api.env.realm, paths.IPA_RADB_DIR)
     ca_instance.stop_tracking_certificates()
+    installutils.remove_file(paths.RA_AGENT_PEM)
+    installutils.remove_file(paths.RA_AGENT_KEY)
     if ca_instance.is_configured():
         ca_instance.uninstall()
 
diff --git a/ipaserver/install/cainstance.py b/ipaserver/install/cainstance.py
index 90883b6..bb3b90f 100644
--- a/ipaserver/install/cainstance.py
+++ b/ipaserver/install/cainstance.py
@@ -26,6 +26,7 @@
 import ldap
 import os
 import pwd
+import grp
 import re
 import shutil
 import sys
@@ -64,10 +65,11 @@
 from ipaserver.install import ldapupdate
 from ipaserver.install import replication
 from ipaserver.install import sysupgrade
-from ipaserver.install.dogtaginstance import (
-    DogtagInstance, export_ra_agent_pem)
+from ipaserver.install.dogtaginstance import DogtagInstance
 from ipaserver.plugins import ldap2
 
+from cryptography.hazmat.primitives import serialization
+
 # We need to reset the template because the CA uses the regular boot
 # information
 INF_TEMPLATE = """
@@ -381,8 +383,7 @@ def configure_instance(self, host_name, dm_password, admin_password,
             self.external = 2
 
         if self.clone:
-            cert_db = certs.CertDB(self.realm)
-            has_ra_cert = (cert_db.get_cert_from_db('ipaCert') != '')
+            has_ra_cert = os.path.exists(paths.RA_AGENT_PEM)
         else:
             has_ra_cert = False
 
@@ -416,7 +417,6 @@ def configure_instance(self, host_name, dm_password, admin_password,
                 else:
                     self.step("importing RA certificate from PKCS #12 file",
                               lambda: self.import_ra_cert(ra_p12))
-                self.step("exporting KRA agent cert", export_ra_agent_pem)
 
             if not ra_only:
                 self.step("importing CA chain to RA certificate database", self.__import_ca_chain)
@@ -661,13 +661,21 @@ def import_ra_cert(self, rafile):
 
         Used when setting up replication
         """
-        # Add the new RA cert into the RA database
-        with tempfile.NamedTemporaryFile(mode="w") as agent_file:
-            agent_file.write(self.dm_password)
-            agent_file.flush()
-
-            import_pkcs12(
-                rafile, agent_file.name, self.ra_agent_db, self.ra_agent_pwd)
+        # get the private key from the file
+        ipautil.run([paths.OPENSSL,
+                     "pkcs12",
+                     "-in", rafile,
+                     "-nocerts", "-nodes",
+                     "-out", paths.RA_AGENT_KEY,
+                     "-passin", "pass:"])
+
+        # get the certificate from the pkcs12 file
+        ipautil.run([paths.OPENSSL,
+                     "pkcs12",
+                     "-in", rafile,
+                     "-clcerts", "-nokeys",
+                     "-out", paths.RA_AGENT_PEM,
+                     "-passin", "pass:"])
 
         self.configure_agent_renewal()
 
@@ -684,9 +692,8 @@ def __create_ca_agent(self):
         the appropriate groups for accessing CA services.
         """
 
-        # get ipaCert certificate
-        cert_data = base64.b64decode(self.ra_cert)
-        cert = x509.load_certificate(cert_data, x509.DER)
+        # get RA certificate
+        cert_data = self.ra_cert.public_bytes(serialization.Encoding.DER)
 
         # connect to CA database
         server_id = installutils.realm_to_serverid(api.env.realm)
@@ -694,7 +701,7 @@ def __create_ca_agent(self):
         conn = ldap2.ldap2(api, ldap_uri=dogtag_uri)
         conn.connect(autobind=True)
 
-        # create ipara user with ipaCert certificate
+        # create ipara user with RA certificate
         user_dn = DN(('uid', "ipara"), ('ou', 'People'), self.basedn)
         entry = conn.make_entry(
             user_dn,
@@ -707,7 +714,7 @@ def __create_ca_agent(self):
             userstate=["1"],
             userCertificate=[cert_data],
             description=['2;%s;%s;%s' % (
-                cert.serial_number,
+                self.ra_cert.serial_number,
                 DN(self.ca_subject),
                 DN(('CN', 'IPA RA'), self.subject_base))])
         conn.add_entry(entry)
@@ -796,7 +803,7 @@ def __request_ra_certificate(self):
 
         chain = self.__get_ca_chain()
         data = base64.b64decode(chain)
-        result = ipautil.run(
+        ipautil.run(
             [paths.OPENSSL,
              "pkcs7",
              "-inform",
@@ -823,23 +830,18 @@ def __request_ra_certificate(self):
             # The certificate must be requested using caServerCert profile
             # because this profile does not require agent authentication
             reqId = certmonger.request_and_wait_for_cert(
-                certpath=self.ra_agent_db,
-                nickname='ipaCert',
+                certpath=(paths.RA_AGENT_PEM, paths.RA_AGENT_KEY),
                 principal='host/%s' % self.fqdn,
-                passwd_fname=self.ra_agent_pwd,
                 subject=str(DN(('CN', 'IPA RA'), self.subject_base)),
                 ca=ipalib.constants.RENEWAL_CA_NAME,
                 profile='caServerCert',
                 pre_command='renew_ra_cert_pre',
-                post_command='renew_ra_cert')
+                post_command='renew_ra_cert',
+                storage="FILE")
 
             self.requestId = str(reqId)
-            result = self.__run_certutil(
-                ['-L', '-n', 'ipaCert', '-a'], capture_output=True)
-            self.ra_cert = x509.strip_header(result.output)
-            self.ra_cert = "\n".join(
-                line.strip() for line
-                in self.ra_cert.splitlines() if line.strip())
+            self.ra_cert = x509.load_certificate_from_file(
+                paths.RA_AGENT_PEM)
         finally:
             # we can restore the helper parameters
             certmonger.modify_ca_helper(
@@ -851,6 +853,11 @@ def __request_ra_certificate(self):
                 except OSError:
                     pass
 
+        apache_gid = grp.getgrnam(constants.HTTPD_USER).gr_gid
+        for fname in (paths.RA_AGENT_PEM, paths.RA_AGENT_KEY):
+            os.chown(fname, -1, apache_gid)
+            os.chmod(fname, 0o440)
+
     def __setup_sign_profile(self):
         # Tell the profile to automatically issue certs for RAs
         installutils.set_directive(
@@ -996,12 +1003,11 @@ def unconfigure_certmonger_renewal_guard(self):
     def configure_agent_renewal(self):
         try:
             certmonger.dogtag_start_tracking(
-                certpath=paths.IPA_RADB_DIR,
+                certpath=(paths.RA_AGENT_PEM, paths.RA_AGENT_KEY),
                 ca='dogtag-ipa-ca-renew-agent',
-                nickname='ipaCert',
-                pinfile=os.path.join(paths.IPA_RADB_DIR, 'pwdfile.txt'),
                 pre_command='renew_ra_cert_pre',
-                post_command='renew_ra_cert')
+                post_command='renew_ra_cert',
+                storage='FILE')
         except RuntimeError as e:
             self.log.error(
                 "certmonger failed to start tracking certificate: %s", e)
@@ -1018,7 +1024,7 @@ def stop_tracking_certificates(self):
                 certmonger.stop_tracking(self.nss_db, nickname=nickname)
 
         try:
-            certmonger.stop_tracking(paths.IPA_RADB_DIR, nickname='ipaCert')
+            certmonger.stop_tracking(certfile=paths.RA_AGENT_PEM)
         except RuntimeError as e:
             root_logger.error(
                 "certmonger failed to stop tracking certificate: %s", e)
diff --git a/ipaserver/install/certs.py b/ipaserver/install/certs.py
index 15f9545..8d0af42 100644
--- a/ipaserver/install/certs.py
+++ b/ipaserver/install/certs.py
@@ -400,7 +400,8 @@ def issue_server_cert(self, certreq_fname, cert_fname):
         # Send the request to the CA
         result = dogtag.https_request(
             self.host_name, 8443, "/ca/ee/ca/profileSubmitSSLClient",
-            api.env.ca_certfile, paths.RA_AGENT_PEM, **params)
+            api.env.ca_certfile, paths.RA_AGENT_PEM, paths.RA_AGENT_KEY,
+            **params)
         http_status, _http_headers, http_body = result
         root_logger.debug("CA answer: %s", http_body)
 
@@ -451,7 +452,8 @@ def issue_signing_cert(self, certreq_fname, cert_fname):
         # Send the request to the CA
         result = dogtag.https_request(
             self.host_name, 8443, "/ca/ee/ca/profileSubmitSSLClient",
-            api.env.ca_certfile, paths.RA_AGENT_PEM, **params)
+            api.env.ca_certfile, paths.RA_AGENT_PEM, paths.RA_AGENT_KEY,
+            **params)
         http_status, _http_headers, http_body = result
         if http_status != 200:
             raise RuntimeError("Unable to submit cert request")
diff --git a/ipaserver/install/custodiainstance.py b/ipaserver/install/custodiainstance.py
index a0bb399..7ec0269 100644
--- a/ipaserver/install/custodiainstance.py
+++ b/ipaserver/install/custodiainstance.py
@@ -112,6 +112,10 @@ def __create_container(self):
 
     def import_ra_key(self, master_host_name):
         cli = self.__CustodiaClient(server=master_host_name)
+        # please note that ipaCert part has to stay here for historical
+        # reasons (old servers expect you to ask for ra/ipaCert during
+        # replication as they store the RA agent cert in an NSS database
+        # with this nickname)
         cli.fetch_key('ra/ipaCert')
 
     def import_dm_password(self, master_host_name):
diff --git a/ipaserver/install/dogtaginstance.py b/ipaserver/install/dogtaginstance.py
index 86c819c..ef73e39 100644
--- a/ipaserver/install/dogtaginstance.py
+++ b/ipaserver/install/dogtaginstance.py
@@ -24,7 +24,6 @@
 import tempfile
 import traceback
 import dbus
-import pwd
 
 from pki.client import PKIConnection
 import pki.system
@@ -72,27 +71,6 @@ def is_installing_replica(sys_type):
         return False
 
 
-def export_ra_agent_pem():
-    """
-    Export ipaCert with private key for client authentication.
-    """
-    fd, filename = tempfile.mkstemp(dir=paths.IPA_RADB_DIR)
-    os.close(fd)
-
-    args = ["/usr/bin/pki",
-            "-d", paths.IPA_RADB_DIR,
-            "-C", os.path.join(paths.IPA_RADB_DIR, 'pwdfile.txt'),
-            "client-cert-show", "ipaCert",
-            "--client-cert", filename]
-    ipautil.run(args)
-
-    pent = pwd.getpwnam(IPAAPI_USER)
-    os.chown(filename, 0, pent.pw_gid)
-    os.chmod(filename, 0o440)
-
-    os.rename(filename, paths.RA_AGENT_PEM)
-
-
 class DogtagInstance(service.Service):
     """
     This is the base class for a Dogtag 10+ instance, which uses a
diff --git a/ipaserver/install/dsinstance.py b/ipaserver/install/dsinstance.py
index 9172b65..e3d0d12 100644
--- a/ipaserver/install/dsinstance.py
+++ b/ipaserver/install/dsinstance.py
@@ -810,14 +810,15 @@ def __enable_ssl(self):
                     self.nickname, self.principal, dsdb.passwd_fname,
                     'restart_dirsrv %s' % self.serverid)
         else:
-            dsdb.create_from_cacert()
-            ca_args = ['/usr/libexec/certmonger/dogtag-submit',
-                       '--ee-url', 'https://%s:8443/ca/ee/ca' % self.fqdn,
-                       '--dbdir', paths.IPA_RADB_DIR,
-                       '--nickname', 'ipaCert',
-                       '--sslpinfile', os.path.join(paths.IPA_RADB_DIR,
-                                                    'pwdfile.txt'),
-                       '--agent-submit']
+            dsdb.create_from_cacert(paths.IPA_CA_CRT, passwd=None)
+            ca_args = [
+                paths.CERTMONGER_DOGTAG_SUBMIT,
+                '--ee-url', 'https://%s:8443/ca/ee/ca' % self.fqdn,
+                '--certfile', paths.RA_AGENT_PEM,
+                '--keyfile', paths.RA_AGENT_KEY,
+                '--cafile', paths.IPA_CA_CRT,
+                '--agent-submit'
+            ]
             helper = " ".join(ca_args)
             prev_helper = certmonger.modify_ca_helper('IPA', helper)
             try:
diff --git a/ipaserver/install/httpinstance.py b/ipaserver/install/httpinstance.py
index 7317fba..57e4357 100644
--- a/ipaserver/install/httpinstance.py
+++ b/ipaserver/install/httpinstance.py
@@ -376,14 +376,13 @@ def __setup_ssl(self):
             if not self.promote:
                 self.create_password_conf()
                 ca_args = [
-                    '/usr/libexec/certmonger/dogtag-submit',
+                    paths.CERTMONGER_DOGTAG_SUBMIT,
                     '--ee-url', 'https://%s:8443/ca/ee/ca' % self.fqdn,
-                    '--dbdir', paths.IPA_RADB_DIR,
-                    '--nickname', 'ipaCert',
-                    '--sslpinfile', os.path.join(paths.IPA_RADB_DIR,
-                                                 'pwdfile.txt'),
+                    '--certfile', paths.RA_AGENT_PEM,
+                    '--keyfile', paths.RA_AGENT_KEY,
+                    '--cafile', paths.IPA_CA_CRT,
                     '--agent-submit'
-                    ]
+                ]
                 helper = " ".join(ca_args)
                 prev_helper = certmonger.modify_ca_helper('IPA', helper)
 
diff --git a/ipaserver/install/ipa_replica_prepare.py b/ipaserver/install/ipa_replica_prepare.py
index ece5f55..5152f62 100644
--- a/ipaserver/install/ipa_replica_prepare.py
+++ b/ipaserver/install/ipa_replica_prepare.py
@@ -427,7 +427,7 @@ def copy_httpd_certificate(self):
             self.log.info("Creating SSL certificate for the Web Server")
             self.export_certdb("httpcert", passwd_fname)
 
-            self.log.info("Exporting RA certificate")
+            self.log.info("Copying RA certificate")
             self.export_ra_pkcs12()
 
     def copy_pkinit_certificate(self):
@@ -634,18 +634,16 @@ def export_certdb(self, fname, passwd_fname, is_kdc=False):
             raise admintool.ScriptError(str(e))
 
     def export_ra_pkcs12(self):
-        agent_fd, agent_name = tempfile.mkstemp()
-        os.write(agent_fd, self.dirman_password)
-        os.close(agent_fd)
-
-        try:
-            db = certs.CertDB(api.env.realm, host_name=api.env.host)
-
-            if db.has_nickname("ipaCert"):
-                pkcs12_fname = os.path.join(self.dir, "ra.p12")
-                db.export_pkcs12(pkcs12_fname, agent_name, "ipaCert")
-        finally:
-            os.remove(agent_name)
+        if (os.path.exists(paths.RA_AGENT_PEM) and
+           os.path.exists(paths.RA_AGENT_KEY)):
+            ipautil.run([
+                paths.OPENSSL,
+                "pkcs12", "-export",
+                "-inkey", paths.RA_AGENT_KEY,
+                "-in", paths.RA_AGENT_PEM,
+                "-out", os.path.join(self.dir, "ra.p12"),
+                "-passout", "pass:"
+            ])
 
     def update_pki_admin_password(self):
         dn = DN('uid=admin', 'ou=people', 'o=ipaca')
diff --git a/ipaserver/install/krainstance.py b/ipaserver/install/krainstance.py
index be17637..695b121 100644
--- a/ipaserver/install/krainstance.py
+++ b/ipaserver/install/krainstance.py
@@ -30,7 +30,6 @@
 from ipalib import api
 from ipalib import x509
 from ipaplatform.paths import paths
-from ipapython import certdb
 from ipapython import ipautil
 from ipapython.dn import DN
 from ipaserver.install import cainstance
@@ -40,6 +39,8 @@
 from ipaserver.plugins import ldap2
 from ipapython.ipa_log_manager import log_mgr
 
+from cryptography.hazmat.primitives import serialization
+
 # When IPA is installed with DNS support, this CNAME should hold all IPA
 # replicas with KRA configured
 IPA_KRA_RECORD = "ipa-kra"
@@ -284,10 +285,9 @@ def __create_kra_agent(self):
         the appropriate groups for accessing KRA services.
         """
 
-        # get ipaCert certificate
-        with certdb.NSSDatabase(paths.IPA_RADB_DIR) as ipa_nssdb:
-           cert_data = ipa_nssdb.get_cert("ipaCert")
-        cert = x509.load_certificate(cert_data, x509.DER)
+        # get RA agent certificate
+        cert = x509.load_certificate_from_file(paths.RA_AGENT_PEM)
+        cert_data = cert.public_bytes(serialization.Encoding.DER)
 
         # connect to KRA database
         server_id = installutils.realm_to_serverid(api.env.realm)
@@ -295,7 +295,7 @@ def __create_kra_agent(self):
         conn = ldap2.ldap2(api, ldap_uri=dogtag_uri)
         conn.connect(autobind=True)
 
-        # create ipakra user with ipaCert certificate
+        # create ipakra user with RA agent certificate
         user_dn = DN(('uid', "ipakra"), ('ou', 'people'), self.basedn)
         entry = conn.make_entry(
             user_dn,
diff --git a/ipaserver/install/plugins/ca_renewal_master.py b/ipaserver/install/plugins/ca_renewal_master.py
index 4fa4edb..99503ad 100644
--- a/ipaserver/install/plugins/ca_renewal_master.py
+++ b/ipaserver/install/plugins/ca_renewal_master.py
@@ -74,17 +74,16 @@ def execute(self, **options):
                 return False, []
 
         criteria = {
-            'cert-database': paths.IPA_RADB_DIR,
-            'cert-nickname': 'ipaCert',
+            'cert-file': paths.RA_AGENT_PEM,
         }
         request_id = certmonger.get_request_id(criteria)
         if request_id is not None:
-            self.debug("found certmonger request for ipaCert")
+            self.debug("found certmonger request for RA cert")
 
             ca_name = certmonger.get_request_value(request_id, 'ca-name')
             if ca_name is None:
                 self.warning(
-                    "certmonger request for ipaCert is missing ca_name, "
+                    "certmonger request for RA cert is missing ca_name, "
                     "assuming local CA is renewal slave")
                 return False, []
             ca_name = ca_name.strip()
@@ -97,11 +96,11 @@ def execute(self, **options):
                 return False, []
             else:
                 self.warning(
-                    "certmonger request for ipaCert has unknown ca_name '%s', "
+                    "certmonger request for RA cert has unknown ca_name '%s', "
                     "assuming local CA is renewal slave", ca_name)
                 return False, []
         else:
-            self.debug("certmonger request for ipaCert not found")
+            self.debug("certmonger request for RA cert not found")
 
             config = installutils.get_directive(
                 paths.CA_CS_CFG_PATH, 'subsystem.select', '=')
diff --git a/ipaserver/install/server/upgrade.py b/ipaserver/install/server/upgrade.py
index 71087fe..a0aaa7d 100644
--- a/ipaserver/install/server/upgrade.py
+++ b/ipaserver/install/server/upgrade.py
@@ -10,6 +10,7 @@
 import pwd
 import fileinput
 import sys
+import tempfile
 
 import dns.exception
 
@@ -18,6 +19,10 @@
 from six.moves.configparser import SafeConfigParser
 # pylint: enable=import-error
 
+from cryptography import x509 as crypto_x509
+from cryptography.hazmat.primitives import serialization
+from cryptography.hazmat.backends import default_backend
+
 from ipalib import api
 from ipalib.install import certmonger, sysrestore
 import SSSDConfig
@@ -880,77 +885,50 @@ def certificate_renewal_update(ca, ds, http):
 
     template = paths.CERTMONGER_COMMAND_TEMPLATE
     serverid = installutils.realm_to_serverid(api.env.realm)
-    dirsrv_dir = dsinstance.config_dirname(serverid)
 
     # bump version when requests is changed
     version = 6
-    requests = (
-        (
-            paths.PKI_TOMCAT_ALIAS_DIR,
-            'auditSigningCert cert-pki-ca',
-            'dogtag-ipa-ca-renew-agent',
-            template % 'stop_pkicad',
-            '%s "auditSigningCert cert-pki-ca"' % (template % 'renew_ca_cert'),
-            None,
-        ),
-        (
-            paths.PKI_TOMCAT_ALIAS_DIR,
-            'ocspSigningCert cert-pki-ca',
-            'dogtag-ipa-ca-renew-agent',
-            template % 'stop_pkicad',
-            '%s "ocspSigningCert cert-pki-ca"' % (template % 'renew_ca_cert'),
-            None,
-        ),
-        (
-            paths.PKI_TOMCAT_ALIAS_DIR,
-            'subsystemCert cert-pki-ca',
-            'dogtag-ipa-ca-renew-agent',
-            template % 'stop_pkicad',
-            '%s "subsystemCert cert-pki-ca"' % (template % 'renew_ca_cert'),
-            None,
-        ),
-        (
-            paths.PKI_TOMCAT_ALIAS_DIR,
-            'caSigningCert cert-pki-ca',
-            'dogtag-ipa-ca-renew-agent',
-            template % 'stop_pkicad',
-            '%s "caSigningCert cert-pki-ca"' % (template % 'renew_ca_cert'),
-            'ipaCACertRenewal',
-        ),
-        (
-            paths.IPA_RADB_DIR,
-            'ipaCert',
-            'dogtag-ipa-ca-renew-agent',
-            template % 'renew_ra_cert_pre',
-            template % 'renew_ra_cert',
-            None,
-        ),
-        (
-            paths.PKI_TOMCAT_ALIAS_DIR,
-            'Server-Cert cert-pki-ca',
-            'dogtag-ipa-ca-renew-agent',
-            template % 'stop_pkicad',
-            '%s "Server-Cert cert-pki-ca"' % (template % 'renew_ca_cert'),
-            None,
-        ),
-        (
-            paths.HTTPD_ALIAS_DIR,
-            'Server-Cert',
-            'IPA',
-            None,
-            template % 'restart_httpd',
-            None,
-        ),
-        (
-            dirsrv_dir,
-            'Server-Cert',
-            'IPA',
-            None,
-            '%s %s' % (template % 'restart_dirsrv', serverid),
-            None,
-        ),
-
-    )
+    requests = []
+    for certnick in ('auditSigningCert cert-pki-ca',
+                     'ocspSigningCert cert-pki-ca',
+                     'subsystemCert cert-pki-ca',
+                     'caSigningCert cert-pki-ca',
+                     'Server-Cert cert-pki-ca',
+                     ):
+        requests.append(
+            {
+                'cert-database': paths.PKI_TOMCAT_ALIAS_DIR,
+                'cert-nickname': certnick,
+                'ca': 'dogtag-ipa-ca-renew-agent',
+                'cert-presave-command': template % 'stop_pkicad',
+                'cert-postsave-command':
+                    (template % 'renew_ca_cert "{}"').format(certnick),
+            }
+        )
+    # set profile for caSigningCert
+    requests[3]['template-profile'] = 'ipaCACertRenewal'
+    requests.extend([
+        {
+            'cert-file': paths.RA_AGENT_PEM,
+            'key-file': paths.RA_AGENT_KEY,
+            'ca': 'dogtag-ipa-ca-renew-agent',
+            'cert-presave-command': template % 'renew_ra_cert_pre',
+            'cert-postsave-command': template % 'renew_ra_cert',
+        },
+        {
+            'cert-database': paths.HTTPD_ALIAS_DIR,
+            'cert-nickname': 'Server-Cert',
+            'ca': 'IPA',
+            'cert-postsave-command': template % 'restart_httpd',
+        },
+        {
+            'cert-database': dsinstance.config_dirname(serverid),
+            'cert-nickname': 'Server-Cert',
+            'ca': 'IPA',
+            'cert-postsave-command':
+                '%s %s' % (template % 'restart_dirsrv', serverid),
+        }
+    ])
 
     root_logger.info("[Update certmonger certificate renewal configuration to "
                      "version %d]" % version)
@@ -964,16 +942,7 @@ def certificate_renewal_update(ca, ds, http):
 
     # State not set, lets see if we are already configured
     for request in requests:
-        nss_dir, nickname, ca_name, pre_command, post_command, profile = request
-        criteria = {
-            'cert-database': nss_dir,
-            'cert-nickname': nickname,
-            'ca-name': ca_name,
-            'template-profile': profile,
-            'cert-presave-command': pre_command,
-            'cert-postsave-command': post_command,
-        }
-        request_id = certmonger.get_request_id(criteria)
+        request_id = certmonger.get_request_id(request)
         if request_id is None:
             break
     else:
@@ -1382,20 +1351,60 @@ def fix_trust_flags():
     sysupgrade.set_upgrade_state('http', 'fix_trust_flags', True)
 
 
-def export_ra_agent_pem():
-    root_logger.info('[Exporting KRA agent PEM file]')
-
-    if sysupgrade.get_upgrade_state('http', 'export_ra_agent_pem'):
-        root_logger.info("KRA agent PEM file already exported")
+def update_ra_agent_crt():
+    if os.path.exists(paths.RA_AGENT_PEM):
         return
 
-    if not api.Command.kra_is_enabled()['result']:
-        root_logger.info("KRA is not enabled")
+    root_logger.info('[Exporting RA agent PEM files]')
+    _fd, filename = tempfile.mkstemp(dir=paths.VAR_LIB_IPA)
+
+    if sysupgrade.get_upgrade_state('http', 'update_ra_agent_crt'):
+        root_logger.info("RA agent PEM files already exported")
         return
 
-    dogtaginstance.export_ra_agent_pem()
+    sysupgrade.set_upgrade_state('http', 'update_ra_agent_crt', True)
+
+    ra_nick = 'ipaCert'
+    args = [paths.PKI,
+            "-d", paths.HTTPD_ALIAS_DIR,
+            "-C", paths.ALIAS_PWDFILE_TXT,
+            "client-cert-show", ra_nick,
+            "--client-cert", filename]
+    ipautil.run(args)
+
+    # get the private key and certificate from the file
+    with open(filename, 'r') as f:
+        data = f.read()
+        cert = crypto_x509.load_pem_x509_certificate(
+            data, default_backend())
+        key = serialization.load_pem_private_key(
+            data, password=None, backend=default_backend())
+
+    os.remove(filename)
+
+    with open(paths.RA_AGENT_PEM, 'w') as f:
+        f.write(cert.public_bytes(serialization.Encoding.PEM))
+
+    with open(paths.RA_AGENT_KEY, 'w') as f:
+        f.write(key.private_bytes(
+            encoding=serialization.Encoding.PEM,
+            format=serialization.PrivateFormat.PKCS8,
+            encryption_algorithm=serialization.NoEncryption()
+        ))
+
+    pent = pwd.getpwnam(constants.HTTPD_USER)
+    for pem_file in (paths.RA_AGENT_PEM, paths.RA_AGENT_KEY):
+        os.chown(pem_file, 0, pent.pw_gid)
+        os.chmod(pem_file, 0o440)
+
+    if os.path.exists('/etc/httpd/alias/kra-agent.pem'):
+        os.remove('/etc/httpd/alias/kra-agent.pem')
 
-    sysupgrade.set_upgrade_state('http', 'export_ra_agent_pem', True)
+    httpdb = certs.CertDB(api.env.realm)
+    oldcert = httpdb.get_cert_from_db(ra_nick)
+    if oldcert:
+        certmonger.stop_tracking(paths.HTTPD_ALIAS_DIR, nickname=ra_nick)
+        httpdb.delete_cert(ra_nick)
 
 
 def update_mod_nss_protocol(http):
@@ -1635,9 +1644,9 @@ def upgrade_configuration():
     update_mod_nss_protocol(http)
     update_mod_nss_cipher_suite(http)
     fix_trust_flags()
+==== BASE ====
     export_ra_agent_pem()
-    update_http_keytab(http)
-    http.configure_gssproxy()
+==== BASE ====
     http.start()
 
     uninstall_selfsign(ds, http)
diff --git a/ipaserver/plugins/dogtag.py b/ipaserver/plugins/dogtag.py
index 6963c4f..5b60c90 100644
--- a/ipaserver/plugins/dogtag.py
+++ b/ipaserver/plugins/dogtag.py
@@ -1241,9 +1241,13 @@ def __init__(self, api):
         self.ca_cert = api.env.ca_certfile
         if api.env.in_tree:
             self.client_certfile = os.path.join(
-                api.env.dot_ipa, 'ra-agent.pem')
+                api.env.dot_ipa, 'dogtag-agent.pem')
+
+            self.client_keyfile = os.path.join(
+                api.env.dot_ipa, 'dogtag-agent.key')
         else:
             self.client_certfile = paths.RA_AGENT_PEM
+            self.client_keyfile = paths.RA_AGENT_KEY
         super(RestClient, self).__init__(api)
 
         # session cookie
@@ -1277,7 +1281,8 @@ def __enter__(self):
         status, resp_headers, _resp_body = dogtag.https_request(
             self.ca_host, self.override_port or self.env.ca_agent_port,
             '/ca/rest/account/login', self.ca_cert,
-            self.client_certfile, 'GET'
+            self.client_certfile, self.client_keyfile,
+            'GET'
         )
         cookies = ipapython.cookie.Cookie.parse(resp_headers.get('set-cookie', ''))
         if status != 200 or len(cookies) == 0:
@@ -1290,7 +1295,8 @@ def __exit__(self, exc_type, exc_value, traceback):
         dogtag.https_request(
             self.ca_host, self.override_port or self.env.ca_agent_port,
             '/ca/rest/account/logout', self.ca_cert,
-            self.client_certfile, 'GET'
+            self.client_certfile, self.client_keyfile,
+            'GET'
         )
         self.cookie = None
 
@@ -1331,7 +1337,7 @@ def _ssldo(self, method, path, headers=None, body=None, use_session=True):
         status, resp_headers, resp_body = dogtag.https_request(
             self.ca_host, self.override_port or self.env.ca_agent_port,
             resource, self.ca_cert, self.client_certfile,
-            method, headers, body
+            self.client_keyfile, method, headers, body
         )
         if status < 200 or status >= 300:
             explanation = self._parse_dogtag_error(resp_body) or ''
@@ -1412,7 +1418,8 @@ def _sslget(self, url, port, **kw):
         Perform an HTTPS request
         """
         return dogtag.https_request(self.ca_host, port, url, self.ca_cert,
-                                    self.client_certfile, **kw)
+                                    self.client_certfile, self.client_keyfile,
+                                    **kw)
 
     def get_parse_result_xml(self, xml_text, parse_func):
         '''
@@ -2012,7 +2019,8 @@ def get_client(self):
             str(self.kra_port),
             'kra')
 
-        connection.set_authentication_cert(paths.RA_AGENT_PEM)
+        connection.set_authentication_cert(paths.RA_AGENT_PEM,
+                                           paths.RA_AGENT_KEY)
 
         return KRAClient(connection, crypto)
 
diff --git a/ipaserver/secrets/store.py b/ipaserver/secrets/store.py
index 1051c50..78e6b78 100644
--- a/ipaserver/secrets/store.py
+++ b/ipaserver/secrets/store.py
@@ -266,10 +266,10 @@ def import_key(self, value):
         'wrap_nick': 'caSigningCert cert-pki-ca',
     },
     'ra': {
-        'type': 'NSSDB',
-        'path': paths.IPA_RADB_DIR,
-        'handler': NSSCertDB,
-        'pwcallback': HTTPD_password_callback,
+        'type': 'OPENSSL',
+        'handler': OpenSSLHandler,
+        'certfile': paths.RA_AGENT_PEM,
+        'keyfile': paths.RA_AGENT_KEY,
     },
     'dm': {
         'type': 'DMLDAP',

From d6bfdbb460490e87e93722b7484501a4bf8a0090 Mon Sep 17 00:00:00 2001
From: Stanislav Laznicka <slazn...@redhat.com>
Date: Thu, 2 Feb 2017 14:23:10 +0100
Subject: [PATCH 12/12] REVERT THIS COMMIT ON PKI-BASE UPDATE

This commit should be reverted when the following commit gets
in pki-base: https://git.fedorahosted.org/cgit/pki.git/commit/?id=71ae20c

TODO: when reverting this commit, also bump the pki-base version in
freeipa.spec.in
---
 ipaserver/plugins/dogtag.py | 7 +++++--
 1 file changed, 5 insertions(+), 2 deletions(-)

diff --git a/ipaserver/plugins/dogtag.py b/ipaserver/plugins/dogtag.py
index 5b60c90..1ef552d 100644
--- a/ipaserver/plugins/dogtag.py
+++ b/ipaserver/plugins/dogtag.py
@@ -2019,8 +2019,11 @@ def get_client(self):
             str(self.kra_port),
             'kra')
 
-        connection.set_authentication_cert(paths.RA_AGENT_PEM,
-                                           paths.RA_AGENT_KEY)
+        connection.session.cert = (paths.RA_AGENT_PEM, paths.RA_AGENT_KEY)
+        # uncomment the following when this commit makes it to release
+        # https://git.fedorahosted.org/cgit/pki.git/commit/?id=71ae20c
+        # connection.set_authentication_cert(paths.RA_AGENT_PEM,
+        #                                    paths.RA_AGENT_KEY)
 
         return KRAClient(connection, crypto)
 
-- 
Manage your subscription for the Freeipa-devel mailing list:
https://www.redhat.com/mailman/listinfo/freeipa-devel
Contribute to FreeIPA: http://www.freeipa.org/page/Contribute/Code

Reply via email to