Dne 26.9.2014 v 19:54 Jan Cholasta napsal(a):
Dne 26.9.2014 v 17:13 Rob Crittenden napsal(a):
Jan Cholasta wrote:
Dne 23.9.2014 v 20:39 Rob Crittenden napsal(a):
Jan Cholasta wrote:
Hi,

the attached patches fix various bugs and shortcomings in the CA
management and renewal code. Related tickets:
<https://fedorahosted.org/freeipa/ticket/4416>,
<https://fedorahosted.org/freeipa/ticket/4460>.

(Patch 319 was originally posted at
<http://www.redhat.com/archives/freeipa-devel/2014-September/msg00132.html>.)



Only two of the patches includes what ticket(s) they address. Most have
the tersest of commit messages. If got more and more difficult to see
why the changes were needed at all, as you'll see.

Sorry, fixed (hopefully).

Note that most of these patches fix stuff I didn't have time to fix
before I posted the original CA management patches, hence the missing
tickets.

Well, the policy is that every commit should have a ticket. So I guess
re-open the old tickets or open new ones. This will help someone in the
future know the "why" of a patch. I've certainly been guilty

OK, I will reopen the related tickets.


Here is a new set of reviews as trying to intermingle was making my eyes
cross:

319:

I guess I still don't understand why you can't pull the certs out of
LDAP when creating this database. When this code runs, we know that the
client is configured, so we have access to authentication. Why can't
create_ipa_nssdb pull the certs directly? It seems more robust to me,
and the code is already written in ipa-client-install to do this.

Well, I don't understand why do you want them to be updated so much, as
nothing will break if they are not. Also try to imagine what would
happen if, say, 10k clients were updated at the same moment...


When adding the CA certificates to the temporary database it will report
that a failure occurred, but not the exception:

+        except CalledProcessError, e:
+            root_logger.info("Failed to add CA to temporary NSS
database.")
+            return CLIENT_INSTALL_ERROR

Granted, NSS errors are often obtuse, but should it at least DEBUG log
it?

It is already logged in ipautil.run. The exception only says that the
command failed, there's no point in logging it.


324, 325, 326: ACK

327:

So the idea is to just mirror the certs and us the new db to know what
was added?

Exactly.

What if someone has the same nickname, different cert in the
two databases? Is that too much of a corner case?

IMO it is too much of a corner case, but I plan on adding a better
diff/patch algorithm in the future if it turns out to be a problem.


328, 329, 330, 331: ACK

As an aside, do you know why the global certs are seen by mod_nss?
libnssckbi.so is symlinked into the directory (certutil -L -d
/etc/httpd/alias -h all will show all the certs).

I'm not sure why it is this way, but the module is linked to the NSS
database:

$ sudo modutil -list -dbdir /etc/httpd/alias

Listing of PKCS #11 Modules
-----------------------------------------------------------
   1. NSS Internal PKCS #11 Module
      slots: 2 slots attached
     status: loaded

      slot: NSS Internal Cryptographic Services
     token: NSS Generic Crypto Services

      slot: NSS User Private Key and Certificate Services
     token: NSS Certificate DB

   2. Root Certs
     library name: /etc/httpd/alias/libnssckbi.so
      slots: 2 slots attached
     status: loaded

      slot: /etc/pki/ca-trust/source
     token: System Trust

      slot: /usr/share/pki/ca-trust-source
     token: Default Trust
-----------------------------------------------------------


332-335

I think the naming and/or comments can be improved. For example, there
are now 3 *retrieve_cert commands, all of which do slightly different
things. All have external handlers (via the oddly named profile), but
some are called internally as well.

I have spent quite some time trying to come up with good names for them,
but I was not able to do so. Suggestions are welcome.


This is rather complex code: a command passes a call onto certmonger
which then exuecutes the IPA CA helper... More documentation would
definitely help a newcomer figure out how renewal, CA retrieval, etc.
works.

OK, I'll try to add some.


Not to be too pedantic but there is a lot of this in
dogtag-ipa-ca-renew-agent-submit:

if variable: OR if not variable:

Where variable defaults to None. Shouldn't the test be:

if variable is [not] None:

This doesn't catch empty strings, which may occur in some of these checks.


340: ACK

Through some combination of ipa-certupdate and ipa-cacert-manage I seem
to have hosed things up:

ipa: DEBUG: certmonger request is in state
dbus.String(u'CA_UNREACHABLE', variant_level=1)
ipa.ipaserver.install.ipa_cacert_manage.CACertManage: DEBUG:   File
"/usr/lib/python2.7/site-packages/ipapython/admintool.py", line 171, in
execute
     return_value = self.run()
   File
"/usr/lib/python2.7/site-packages/ipaserver/install/ipa_cacert_manage.py",

line 118, in run
     rc = self.renew()
   File
"/usr/lib/python2.7/site-packages/ipaserver/install/ipa_cacert_manage.py",

line 181, in renew
     return self.renew_self_signed(ca)
   File
"/usr/lib/python2.7/site-packages/ipaserver/install/ipa_cacert_manage.py",

line 193, in renew_self_signed
     self.resubmit_request(ca, 'caCACert')
   File
"/usr/lib/python2.7/site-packages/ipaserver/install/ipa_cacert_manage.py",

line 315, in resubmit_request
     "please check the request manually" % self.request_id)

ipa.ipaserver.install.ipa_cacert_manage.CACertManage: DEBUG: The
ipa-cacert-manage command failed, exception: ScriptError: Error
resubmitting certmonger request '20140909142830', please check the
request manually
ipa.ipaserver.install.ipa_cacert_manage.CACertManage: ERROR: Error
resubmitting certmonger request '20140909142830', please check the
request manually

Incidentally, IMHO it should include the command to execute to check the
request manually.

Will add.


The CA is actually unreachable. Restarting it fixed things. I'll chalk
this up to cosmic rays.

OK.


Re-running ipa-cacert-manage renew was successful.

Good.


I confirmed that the CA signing cert was updated in the dogtag database.

I then ran ipa-certupdate to distribute this new CA cert around and none
of the databases got the updated CA cert, nor did /etc/ipa/ca.crt.

Can you see the new CA cert in cn=certificates,cn=ipa,cn=etc,$SUFFIX?


Still doing some functional testing.

Unrelated to this:

    9:freeipa-python-4.0.0GITf6875d4-0.#################################
[100%]
Update failed: Operations error:

Seems to be related to this:

2014-09-25T19:28:22Z DEBUG ---------------------------------------------
2014-09-25T19:28:22Z DEBUG Final value after applying updates
2014-09-25T19:28:22Z DEBUG dn: cn=referential integrity
postoperation,cn=plugins,cn=config
2014-09-25T19:28:22Z DEBUG cn:
2014-09-25T19:28:22Z DEBUG      referential integrity postoperation
2014-09-25T19:28:22Z DEBUG objectClass:
2014-09-25T19:28:22Z DEBUG      top
2014-09-25T19:28:22Z DEBUG      nsSlapdPlugin
2014-09-25T19:28:22Z DEBUG      extensibleObject
2014-09-25T19:28:22Z DEBUG nsslapd-pluginprecedence:
2014-09-25T19:28:22Z DEBUG      40
2014-09-25T19:28:22Z DEBUG nsslapd-pluginPath:
2014-09-25T19:28:22Z DEBUG      libreferint-plugin
2014-09-25T19:28:22Z DEBUG nsslapd-pluginVersion:
2014-09-25T19:28:22Z DEBUG      1.3.3.2
2014-09-25T19:28:22Z DEBUG nsslapd-pluginarg5:
2014-09-25T19:28:22Z DEBUG      owner
2014-09-25T19:28:22Z DEBUG nsslapd-pluginarg4:
2014-09-25T19:28:22Z DEBUG      uniquemember
2014-09-25T19:28:22Z DEBUG nsslapd-pluginarg7:
2014-09-25T19:28:22Z DEBUG nsslapd-pluginarg6:
2014-09-25T19:28:22Z DEBUG      seeAlso
2014-09-25T19:28:22Z DEBUG nsslapd-pluginarg1:
2014-09-25T19:28:22Z DEBUG
/var/log/dirsrv/slapd-GREYOAK-COM/referint
2014-09-25T19:28:22Z DEBUG nsslapd-pluginarg0:
2014-09-25T19:28:22Z DEBUG      0
2014-09-25T19:28:22Z DEBUG nsslapd-pluginarg3:
2014-09-25T19:28:22Z DEBUG      member
2014-09-25T19:28:22Z DEBUG nsslapd-pluginarg2:
2014-09-25T19:28:22Z DEBUG      0
2014-09-25T19:28:22Z DEBUG nsslapd-pluginarg9:
2014-09-25T19:28:22Z DEBUG nsslapd-pluginarg8:
2014-09-25T19:28:22Z DEBUG nsslapd-pluginInitfunc:
2014-09-25T19:28:22Z DEBUG      referint_postop_init
2014-09-25T19:28:22Z DEBUG nsslapd-pluginexcludeentryscope:
2014-09-25T19:28:22Z DEBUG      cn=provisioning,dc=greyoak,dc=com
2014-09-25T19:28:22Z DEBUG nsslapd-pluginarg11:
2014-09-25T19:28:22Z DEBUG nsslapd-pluginarg10:
2014-09-25T19:28:22Z DEBUG nsslapd-pluginarg13:
2014-09-25T19:28:22Z DEBUG nsslapd-pluginarg12:
2014-09-25T19:28:22Z DEBUG nsslapd-plugin-depends-on-type:
2014-09-25T19:28:22Z DEBUG      database
2014-09-25T19:28:22Z DEBUG nsslapd-pluginVendor:
2014-09-25T19:28:22Z DEBUG      389 Project
2014-09-25T19:28:22Z DEBUG nsslapd-pluginarg17:
2014-09-25T19:28:22Z DEBUG nsslapd-pluginarg16:
2014-09-25T19:28:22Z DEBUG nsslapd-pluginarg18:
2014-09-25T19:28:22Z DEBUG nsslapd-pluginType:
2014-09-25T19:28:22Z DEBUG      betxnpostoperation
2014-09-25T19:28:22Z DEBUG nsslapd-pluginDescription:
2014-09-25T19:28:22Z DEBUG      referential integrity plugin
2014-09-25T19:28:22Z DEBUG nsslapd-pluginentryscope:
2014-09-25T19:28:22Z DEBUG      dc=greyoak,dc=com
2014-09-25T19:28:22Z DEBUG nsslapd-pluginEnabled:
2014-09-25T19:28:22Z DEBUG      on
2014-09-25T19:28:22Z DEBUG nsslapd-pluginId:
2014-09-25T19:28:22Z DEBUG      referint
2014-09-25T19:28:22Z DEBUG nsslapd-plugincontainerscope:
2014-09-25T19:28:22Z DEBUG      dc=greyoak,dc=com
2014-09-25T19:28:22Z DEBUG nsslapd-pluginarg15:
2014-09-25T19:28:22Z DEBUG referint-membership-attr:
2014-09-25T19:28:22Z DEBUG      manager
2014-09-25T19:28:22Z DEBUG      secretary
2014-09-25T19:28:22Z DEBUG      memberuser
2014-09-25T19:28:22Z DEBUG      memberhost
2014-09-25T19:28:22Z DEBUG      sourcehost
2014-09-25T19:28:22Z DEBUG      memberservice
2014-09-25T19:28:22Z DEBUG      managedby
2014-09-25T19:28:22Z DEBUG      memberallowcmd
2014-09-25T19:28:22Z DEBUG      memberdenycmd
2014-09-25T19:28:22Z DEBUG      ipasudorunas
2014-09-25T19:28:22Z DEBUG      ipasudorunasgroup
2014-09-25T19:28:22Z DEBUG      ipatokenradiusconfiglink
2014-09-25T19:28:22Z DEBUG nsslapd-pluginarg14:
2014-09-25T19:28:22Z DEBUG [(1, u'nsslapd-pluginarg16', None), (1,
u'nsslapd-pluginarg7', None), (1, u'nsslapd-pluginarg9', None), (1,
u'nsslapd-pluginarg8', None), (1, u'nsslapd-pluginarg11', None), (1,
u'nsslapd-pluginarg10', None), (1, u'nsslapd-pluginarg13', None), (1,
u'nsslapd-pluginarg12', None), (1, u'nsslapd-pluginarg17', None), (1,
u'nsslapd-pluginarg18', None), (1, u'nsslapd-pluginarg15', None), (2,
u'referint-membership-attr', ['sourcehost', 'memberallowcmd',
'memberdenycmd', 'memberuser', 'managedby', 'manager', 'memberservice',
'ipasudorunas', 'ipasudorunasgroup', 'secretary', 'memberhost',
'ipatokenradiusconfiglink']), (1, u'nsslapd-pluginarg14', None)]
2014-09-25T19:28:22Z DEBUG Live 1, updated 1
2014-09-25T19:28:22Z DEBUG Unhandled LDAPError: OPERATIONS_ERROR:
{'desc': 'Operations error'}
2014-09-25T19:28:22Z ERROR Update failed: Operations error:


Updated rebased patches attached, now with ticket URLs.

I have decided to drop 332-335 for now, since I won't be able to finish them before the release. We can continue with them later.

--
Jan Cholasta
>From 4233abd78f9aa835ca511ed67c52bb79b049b24a Mon Sep 17 00:00:00 2001
From: Jan Cholasta <jchol...@redhat.com>
Date: Thu, 18 Sep 2014 16:28:59 +0200
Subject: [PATCH 01/10] Introduce NSS database /etc/ipa/nssdb

This is the new default NSS database for IPA.

/etc/pki/nssdb is still maintained for backward compatibility.

https://fedorahosted.org/freeipa/ticket/3259
---
 freeipa.spec.in                           |  17 ++++
 ipa-client/ipa-install/ipa-client-install | 159 ++++++++++++++++++------------
 ipa-client/ipaclient/ipa_certupdate.py    |   9 ++
 ipalib/rpc.py                             |   2 +-
 ipaplatform/base/paths.py                 |   2 +-
 ipapython/certdb.py                       |  28 ++++++
 6 files changed, 153 insertions(+), 64 deletions(-)

diff --git a/freeipa.spec.in b/freeipa.spec.in
index 9171c82..d16a865 100644
--- a/freeipa.spec.in
+++ b/freeipa.spec.in
@@ -423,6 +423,7 @@ mkdir -p %{buildroot}/%{_localstatedir}/lib/ipa/backup
 mkdir -p %{buildroot}%{_sysconfdir}/ipa/
 /bin/touch %{buildroot}%{_sysconfdir}/ipa/default.conf
 /bin/touch %{buildroot}%{_sysconfdir}/ipa/ca.crt
+mkdir -p %{buildroot}%{_sysconfdir}/ipa/nssdb
 mkdir -p %{buildroot}/%{_localstatedir}/lib/ipa-client/sysrestore
 mkdir -p %{buildroot}%{_sysconfdir}/bash_completion.d
 install -pm 644 contrib/completion/ipa.bash_completion %{buildroot}%{_sysconfdir}/bash_completion.d/ipa
@@ -537,6 +538,17 @@ if [ $1 -gt 1 ] ; then
             /bin/systemctl condrestart ntpd.service 2>&1 || :
         fi
     fi
+
+    if [ ! -f '/etc/ipa/nssdb/cert8.db' -a $restore -ge 2 ]; then
+        python2 -c 'from ipapython.certdb import create_ipa_nssdb; create_ipa_nssdb()' >/dev/null 2>&1
+        tempfile=$(mktemp)
+        if certutil -L -d /etc/pki/nssdb -n 'IPA CA' -a >"$tempfile" 2>/var/log/ipaupgrade.log; then
+            certutil -A -d /etc/ipa/nssdb -n 'IPA CA' -t CT,C,C -a -i "$tempfile" >/var/log/ipaupgrade.log 2>&1
+        elif certutil -L -d /etc/pki/nssdb -n 'External CA cert' -a >"$tempfile" 2>/var/log/ipaupgrade.log; then
+            certutil -A -d /etc/ipa/nssdb -n 'External CA cert' -t C,, -a -i "$tempfile" >/var/log/ipaupgrade.log 2>&1
+        fi
+        rm -f "$tempfile"
+    fi
 fi
 
 %triggerin -n freeipa-client -- openssh-server
@@ -795,6 +807,11 @@ fi
 %dir %attr(0755,root,root) %{_sysconfdir}/ipa/
 %ghost %attr(0644,root,apache) %config(noreplace) %{_sysconfdir}/ipa/default.conf
 %ghost %attr(0644,root,apache) %config(noreplace) %{_sysconfdir}/ipa/ca.crt
+%dir %attr(0755,root,root) %{_sysconfdir}/ipa/nssdb
+%ghost %config(noreplace) %{_sysconfdir}/ipa/nssdb/cert8.db
+%ghost %config(noreplace) %{_sysconfdir}/ipa/nssdb/key3.db
+%ghost %config(noreplace) %{_sysconfdir}/ipa/nssdb/secmod.db
+%ghost %config(noreplace) %{_sysconfdir}/ipa/nssdb/pwdfile.txt
 
 %if ! %{ONLY_CLIENT}
 %files tests -f tests-python.list
diff --git a/ipa-client/ipa-install/ipa-client-install b/ipa-client/ipa-install/ipa-client-install
index 45e8022..ab40cd8 100755
--- a/ipa-client/ipa-install/ipa-client-install
+++ b/ipa-client/ipa-install/ipa-client-install
@@ -46,7 +46,7 @@ try:
     from ipaplatform import services
     from ipaplatform.paths import paths
     from ipapython import ipautil, sysrestore, version, certmonger, ipaldap
-    from ipapython import kernel_keyring
+    from ipapython import kernel_keyring, certdb
     from ipapython.config import IPAOptionParser
     from ipalib import api, errors
     from ipalib import x509, certstore
@@ -550,6 +550,15 @@ def uninstall(options, env):
             cmonger.service_name, str(e))
 
     # Remove our host cert and CA cert
+    for filename in (os.path.join(paths.IPA_NSSDB_DIR, 'cert8.db'),
+                     os.path.join(paths.IPA_NSSDB_DIR, 'key3.db'),
+                     os.path.join(paths.IPA_NSSDB_DIR, 'secmod.db'),
+                     os.path.join(paths.IPA_NSSDB_DIR, 'pwdfile.txt')):
+        try:
+            os.remove(filename)
+        except OSError, e:
+            root_logger.error("Failed to remove %s: %s", filename, e)
+
     purge_ipa_certs({client_nss_nickname, 'IPA CA', 'External CA cert'})
 
     try:
@@ -2524,62 +2533,69 @@ def install(options, env, fstore, statestore):
     except ValueError:
         pass
 
-    # Add CA certs to a temporary NSS database
+    tmp_nss_dir = tempfile.mkdtemp()
     try:
-        os.mkdir(paths.IPA_NSSDB_DIR)
-        pwd_file = ipautil.write_tmp_file(ipautil.ipa_generate_password())
-        run([paths.CERTUTIL, '-N',
-             '-d', paths.IPA_NSSDB_DIR,
-             '-f', pwd_file.name])
-
-        ca_certs = x509.load_certificate_list_from_file(CACERT)
-        ca_certs = [cert.der_data for cert in ca_certs]
-        for i, cert in enumerate(ca_certs):
-            run([paths.CERTUTIL, '-A',
-                 '-d', paths.IPA_NSSDB_DIR,
-                 '-n', 'CA certificate %d' % (i + 1),
-                 '-t', 'C,,'],
-                 stdin=cert)
-    except CalledProcessError, e:
-        root_logger.info("Failed to add CA to temporary NSS database.")
-        return CLIENT_INSTALL_ERROR
+        # Add CA certs to a temporary NSS database
+        try:
+            pwd_file = ipautil.write_tmp_file(ipautil.ipa_generate_password())
+            run([paths.CERTUTIL, '-N',
+                 '-d', tmp_nss_dir,
+                 '-f', pwd_file.name])
 
-    # Now, let's try to connect to the server's XML-RPC interface
-    connected = False
-    try:
-        api.Backend.rpcclient.connect(nss_dir=paths.IPA_NSSDB_DIR)
-        connected = True
-        root_logger.debug('Try RPC connection')
-        api.Backend.rpcclient.forward('ping')
-    except errors.KerberosError, e:
-        if connected:
-            api.Backend.rpcclient.disconnect()
-        root_logger.info('Cannot connect to the server due to ' +
-            'Kerberos error: %s. Trying with delegate=True', str(e))
+            ca_certs = x509.load_certificate_list_from_file(CACERT)
+            ca_certs = [cert.der_data for cert in ca_certs]
+            for i, cert in enumerate(ca_certs):
+                run([paths.CERTUTIL, '-A',
+                     '-d', tmp_nss_dir,
+                     '-n', 'CA certificate %d' % (i + 1),
+                     '-t', 'C,,'],
+                     stdin=cert)
+        except CalledProcessError, e:
+            root_logger.info("Failed to add CA to temporary NSS database.")
+            return CLIENT_INSTALL_ERROR
+
+        # Now, let's try to connect to the server's RPC interface
+        connected = False
         try:
-            api.Backend.rpcclient.connect(delegate=True, nss_dir=paths.IPA_NSSDB_DIR)
-            root_logger.debug('Try RPC connection')
+            api.Backend.rpcclient.connect(nss_dir=tmp_nss_dir)
+            connected = True
+            root_logger.debug("Try RPC connection")
             api.Backend.rpcclient.forward('ping')
+        except errors.KerberosError, e:
+            if connected:
+                api.Backend.rpcclient.disconnect()
+            root_logger.info(
+                "Cannot connect to the server due to Kerberos error: %s. "
+                "Trying with delegate=True", e)
+            try:
+                api.Backend.rpcclient.connect(delegate=True,
+                                              nss_dir=tmp_nss_dir)
+                root_logger.debug("Try RPC connection")
+                api.Backend.rpcclient.forward('ping')
 
-            root_logger.info('Connection with delegate=True successful')
+                root_logger.info("Connection with delegate=True successful")
 
-            # The remote server is not capable of Kerberos S4U2Proxy delegation
-            # This features is implemented in IPA server version 2.2 and higher
-            root_logger.warning("Target IPA server has a lower version than " +
-                "the enrolled client")
-            root_logger.warning("Some capabilities including the ipa " +
-                "command capability may not be available")
-        except errors.PublicError, e2:
-            root_logger.warning(
-                'Second connect with delegate=True also failed: %s', str(e2))
+                # The remote server is not capable of Kerberos S4U2Proxy
+                # delegation. This features is implemented in IPA server
+                # version 2.2 and higher
+                root_logger.warning(
+                    "Target IPA server has a lower version than the enrolled "
+                    "client")
+                root_logger.warning(
+                    "Some capabilities including the ipa command capability "
+                    "may not be available")
+            except errors.PublicError, e2:
+                root_logger.warning(
+                    "Second connect with delegate=True also failed: %s", e2)
+                root_logger.error(
+                    "Cannot connect to the IPA server RPC interface: %s", e2)
+                return CLIENT_INSTALL_ERROR
+        except errors.PublicError, e:
             root_logger.error(
-                "Cannot connect to the IPA server XML-RPC interface: %s",
-                str(e2))
+                "Cannot connect to the server due to generic error: %s", e)
             return CLIENT_INSTALL_ERROR
-    except errors.PublicError, e:
-        root_logger.error(
-            'Cannot connect to the server due to generic error: %s', str(e))
-        return CLIENT_INSTALL_ERROR
+    finally:
+        shutil.rmtree(tmp_nss_dir)
 
     # Use the RPC directly so older servers are supported
     result = api.Backend.rpcclient.forward(
@@ -2591,14 +2607,38 @@ def install(options, env, fstore, statestore):
     if not remote_env['enable_ra']:
         disable_ra()
 
+    # Create IPA NSS database
+    try:
+        certdb.create_ipa_nssdb()
+    except ipautil.CalledProcessError, e:
+        root_logger.error("Failed to create IPA NSS database: %s", e)
+        return CLIENT_INSTALL_ERROR
+
     # Get CA certificates from the certificate store
     ca_certs = get_certs_from_ldap(cli_server[0], cli_basedn, cli_realm,
                                    remote_env['enable_ra'])
+    ca_certs_trust = [(c, n, certstore.key_policy_to_trust_flags(t, True, u))
+                      for (c, n, t, u) in ca_certs]
 
-    # Add the CA to the platform-dependant systemwide CA store
+    # Add the CA certificates to the IPA NSS database
+    root_logger.debug("Adding CA certificates to the IPA NSS database.")
+    for cert, nickname, trust_flags in ca_certs_trust:
+        try:
+            run([paths.CERTUTIL,
+                 "-A",
+                 "-d", paths.IPA_NSSDB_DIR,
+                 "-n", nickname,
+                 "-t", trust_flags],
+                stdin=cert)
+        except CalledProcessError, e:
+            root_logger.error(
+                "Failed to add %s to the IPA NSS database.", nickname)
+            return CLIENT_INSTALL_ERROR
+
+    # Add the CA certificates to the platform-dependant systemwide CA store
     tasks.insert_ca_certs_into_systemwide_ca_store(ca_certs)
 
-    # Add the CA to the default NSS database and trust it
+    # Add the CA certificates to the default NSS database
     if not purge_ipa_certs():
         root_logger.info(
             "Failed to remove old IPA certificates from the default NSS "
@@ -2611,12 +2651,10 @@ def install(options, env, fstore, statestore):
         root_logger.error("Failed to open /etc/pki/nssdb/ipa.txt: %s", e)
         return CLIENT_INSTALL_ERROR
 
-    for cert, nickname, trusted, ext_key_usage in ca_certs:
+    root_logger.debug(
+        "Attempting to add CA certificates to the default NSS database.")
+    for cert, nickname, trust_flags in ca_certs_trust:
         try:
-            root_logger.debug("Attempting to add CA directly to the "
-                              "default NSS database.")
-            trust_flags = certstore.key_policy_to_trust_flags(
-                trusted, True, ext_key_usage)
             run([paths.CERTUTIL,
                  "-A",
                  "-d", paths.NSS_DB_DIR,
@@ -2624,12 +2662,13 @@ def install(options, env, fstore, statestore):
                  "-t", trust_flags],
                 stdin=cert)
         except CalledProcessError, e:
-            root_logger.info("Failed to add CA to the default NSS database.")
+            root_logger.error(
+                "Failed to add %s to the default NSS database.", nickname)
             list_file.close()
             return CLIENT_INSTALL_ERROR
         else:
-            root_logger.info('Added the CA to the default NSS database.')
             list_file.write(nickname + '\n')
+    root_logger.info("Added CA certificates to the default NSS database.")
 
     list_file.close()
 
@@ -2855,7 +2894,3 @@ finally:
         os.remove(CCACHE_FILE)
     except Exception:
         pass
-    try:
-        shutil.rmtree(paths.IPA_NSSDB_DIR)
-    except Exception:
-        pass
diff --git a/ipa-client/ipaclient/ipa_certupdate.py b/ipa-client/ipaclient/ipa_certupdate.py
index 8e7fe04..57dbf20 100644
--- a/ipa-client/ipaclient/ipa_certupdate.py
+++ b/ipa-client/ipaclient/ipa_certupdate.py
@@ -70,6 +70,15 @@ class CertUpdate(admintool.AdminTool):
 
     def update_client(self, certs):
         self.update_file(paths.IPA_CA_CRT, certs)
+        self.update_db(paths.IPA_NSSDB_DIR, certs)
+
+        for nickname in ('IPA CA', 'External CA cert'):
+            try:
+                ipautil.run([paths.CERTUTIL, '-D',
+                             '-d', paths.NSS_DB_DIR,
+                             '-n', nickname])
+            except ipautil.CalledProcessError, e:
+                pass
 
         self.update_db(paths.NSS_DB_DIR, certs)
 
diff --git a/ipalib/rpc.py b/ipalib/rpc.py
index e7e60f4..5934f0c 100644
--- a/ipalib/rpc.py
+++ b/ipalib/rpc.py
@@ -483,7 +483,7 @@ class SSLTransport(LanguageAwareTransport):
             if self._connection and host == self._connection[0]:
                 return self._connection[1]
 
-        dbdir = getattr(context, 'nss_dir', paths.NSS_DB_DIR)
+        dbdir = getattr(context, 'nss_dir', paths.IPA_NSSDB_DIR)
         no_init = self.__nss_initialized(dbdir)
         if sys.version_info < (2, 7):
             conn = NSSHTTPS(host, 443, dbdir=dbdir, no_init=no_init)
diff --git a/ipaplatform/base/paths.py b/ipaplatform/base/paths.py
index 6f2a29e..1493918 100644
--- a/ipaplatform/base/paths.py
+++ b/ipaplatform/base/paths.py
@@ -63,7 +63,7 @@ class BasePathNamespace(object):
     IPA_DNS_UPDATE_TXT = "/etc/ipa/.dns_update.txt"
     IPA_CA_CRT = "/etc/ipa/ca.crt"
     IPA_DEFAULT_CONF = "/etc/ipa/default.conf"
-    IPA_NSSDB_DIR = "/etc/ipa/.nssdb"
+    IPA_NSSDB_DIR = "/etc/ipa/nssdb"
     KRB5_CONF = "/etc/krb5.conf"
     KRB5_KEYTAB = "/etc/krb5.keytab"
     LDAP_CONF = "/etc/ldap.conf"
diff --git a/ipapython/certdb.py b/ipapython/certdb.py
index a858313..426c809 100644
--- a/ipapython/certdb.py
+++ b/ipapython/certdb.py
@@ -17,6 +17,34 @@
 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
 #
 
+import os
+
+from ipaplatform.paths import paths
+from ipapython import ipautil
+
 CA_NICKNAME_FMT = "%s IPA CA"
+
+
 def get_ca_nickname(realm, format=CA_NICKNAME_FMT):
     return format % realm
+
+
+def create_ipa_nssdb():
+    pwdfile = os.path.join(paths.IPA_NSSDB_DIR, 'pwdfile.txt')
+
+    ipautil.backup_file(pwdfile)
+    ipautil.backup_file(os.path.join(paths.IPA_NSSDB_DIR, 'cert8.db'))
+    ipautil.backup_file(os.path.join(paths.IPA_NSSDB_DIR, 'key3.db'))
+    ipautil.backup_file(os.path.join(paths.IPA_NSSDB_DIR, 'secmod.db'))
+
+    with open(pwdfile, 'w') as f:
+        f.write(ipautil.ipa_generate_password(pwd_len=40))
+    os.chmod(pwdfile, 0600)
+
+    ipautil.run([paths.CERTUTIL,
+         "-N",
+         "-d", paths.IPA_NSSDB_DIR,
+         "-f", pwdfile])
+    os.chmod(os.path.join(paths.IPA_NSSDB_DIR, 'cert8.db'), 0644)
+    os.chmod(os.path.join(paths.IPA_NSSDB_DIR, 'key3.db'), 0644)
+    os.chmod(os.path.join(paths.IPA_NSSDB_DIR, 'secmod.db'), 0644)
-- 
1.9.3

>From eb9f5135eb6594527696fcbc84a58249f6dc5d33 Mon Sep 17 00:00:00 2001
From: Jan Cholasta <jchol...@redhat.com>
Date: Thu, 18 Sep 2014 11:19:46 +0200
Subject: [PATCH 02/10] Move NSSDatabase from ipaserver.certs to
 ipapython.certdb

https://fedorahosted.org/freeipa/ticket/4416
---
 ipapython/certdb.py        | 256 +++++++++++++++++++++++++++++++++++++++++++++
 ipaserver/install/certs.py | 254 +-------------------------------------------
 2 files changed, 257 insertions(+), 253 deletions(-)

diff --git a/ipapython/certdb.py b/ipapython/certdb.py
index 426c809..9c1dfc4 100644
--- a/ipapython/certdb.py
+++ b/ipapython/certdb.py
@@ -18,8 +18,14 @@
 #
 
 import os
+import re
+import tempfile
+import shutil
+from nss import nss
+from nss.error import NSPRError
 
 from ipaplatform.paths import paths
+from ipapython.ipa_log_manager import root_logger
 from ipapython import ipautil
 
 CA_NICKNAME_FMT = "%s IPA CA"
@@ -48,3 +54,253 @@ def create_ipa_nssdb():
     os.chmod(os.path.join(paths.IPA_NSSDB_DIR, 'cert8.db'), 0644)
     os.chmod(os.path.join(paths.IPA_NSSDB_DIR, 'key3.db'), 0644)
     os.chmod(os.path.join(paths.IPA_NSSDB_DIR, 'secmod.db'), 0644)
+
+
+def find_cert_from_txt(cert, start=0):
+    """
+    Given a cert blob (str) which may or may not contian leading and
+    trailing text, pull out just the certificate part. This will return
+    the FIRST cert in a stream of data.
+
+    Returns a tuple (certificate, last position in cert)
+    """
+    s = cert.find('-----BEGIN CERTIFICATE-----', start)
+    e = cert.find('-----END CERTIFICATE-----', s)
+    if e > 0:
+        e = e + 25
+
+    if s < 0 or e < 0:
+        raise RuntimeError("Unable to find certificate")
+
+    cert = cert[s:e]
+    return (cert, e)
+
+
+class NSSDatabase(object):
+    """A general-purpose wrapper around a NSS cert database
+
+    For permanent NSS databases, pass the cert DB directory to __init__
+
+    For temporary databases, do not pass nssdir, and call close() when done
+    to remove the DB. Alternatively, a NSSDatabase can be used as a
+    context manager that calls close() automatically.
+    """
+    # Traditionally, we used CertDB for our NSS DB operations, but that class
+    # got too tied to IPA server details, killing reusability.
+    # BaseCertDB is a class that knows nothing about IPA.
+    # Generic NSS DB code should be moved here.
+    def __init__(self, nssdir=None):
+        if nssdir is None:
+            self.secdir = tempfile.mkdtemp()
+            self._is_temporary = True
+        else:
+            self.secdir = nssdir
+            self._is_temporary = False
+
+    def close(self):
+        if self._is_temporary:
+            shutil.rmtree(self.secdir)
+
+    def __enter__(self):
+        return self
+
+    def __exit__(self, type, value, tb):
+        self.close()
+
+    def run_certutil(self, args, stdin=None):
+        new_args = [paths.CERTUTIL, "-d", self.secdir]
+        new_args = new_args + args
+        return ipautil.run(new_args, stdin)
+
+    def create_db(self, password_filename):
+        """Create cert DB
+
+        :param password_filename: Name of file containing the database password
+        """
+        self.run_certutil(["-N", "-f", password_filename])
+
+    def list_certs(self):
+        """Return nicknames and cert flags for all certs in the database
+
+        :return: List of (name, trust_flags) tuples
+        """
+        certs, stderr, returncode = self.run_certutil(["-L"])
+        certs = certs.splitlines()
+
+        # FIXME, this relies on NSS never changing the formatting of certutil
+        certlist = []
+        for cert in certs:
+            match = re.match(r'^(.+?)\s+(\w*,\w*,\w*)\s*$', cert)
+            if match:
+                certlist.append(match.groups())
+
+        return tuple(certlist)
+
+    def find_server_certs(self):
+        """Return nicknames and cert flags for server certs in the database
+
+        Server certs have an "u" character in the trust flags.
+
+        :return: List of (name, trust_flags) tuples
+        """
+        server_certs = []
+        for name, flags in self.list_certs():
+            if 'u' in flags:
+                server_certs.append((name, flags))
+
+        return server_certs
+
+    def get_trust_chain(self, nickname):
+        """Return names of certs in a given cert's trust chain
+
+        :param nickname: Name of the cert
+        :return: List of certificate names
+        """
+        root_nicknames = []
+        chain, stderr, returncode = self.run_certutil([
+            "-O", "-n", nickname])
+        chain = chain.splitlines()
+
+        for c in chain:
+            m = re.match('\s*"(.*)" \[.*', c)
+            if m:
+                root_nicknames.append(m.groups()[0])
+
+        return root_nicknames
+
+    def import_pkcs12(self, pkcs12_filename, db_password_filename,
+                      pkcs12_passwd=None):
+        args = [paths.PK12UTIL, "-d", self.secdir,
+                "-i", pkcs12_filename,
+                "-k", db_password_filename, '-v']
+        if pkcs12_passwd is not None:
+            pkcs12_passwd = pkcs12_passwd + '\n'
+            args = args + ["-w", paths.DEV_STDIN]
+        try:
+            ipautil.run(args, stdin=pkcs12_passwd)
+        except ipautil.CalledProcessError, e:
+            if e.returncode == 17:
+                raise RuntimeError("incorrect password for pkcs#12 file %s" %
+                                   pkcs12_filename)
+            elif e.returncode == 10:
+                raise RuntimeError("Failed to open %s" % pkcs12_filename)
+            else:
+                raise RuntimeError("unknown error import pkcs#12 file %s" %
+                                   pkcs12_filename)
+
+    def trust_root_cert(self, root_nickname, trust_flags=None):
+        if root_nickname[:7] == "Builtin":
+            root_logger.debug(
+                "No need to add trust for built-in root CAs, skipping %s" %
+                root_nickname)
+        else:
+            if trust_flags is None:
+                trust_flags = 'C,,'
+            try:
+                self.run_certutil(["-M", "-n", root_nickname,
+                                   "-t", trust_flags])
+            except ipautil.CalledProcessError, e:
+                raise RuntimeError(
+                    "Setting trust on %s failed" % root_nickname)
+
+    def get_cert(self, nickname, pem=False):
+        args = ['-L', '-n', nickname]
+        if pem:
+            args.append('-a')
+        else:
+            args.append('-r')
+        try:
+            cert, err, returncode = self.run_certutil(args)
+        except ipautil.CalledProcessError:
+            raise RuntimeError("Failed to get %s" % nickname)
+        return cert
+
+    def export_pem_cert(self, nickname, location):
+        """Export the given cert to PEM file in the given location"""
+        cert = self.get_cert(nickname)
+        with open(location, "w+") as fd:
+            fd.write(cert)
+        os.chmod(location, 0444)
+
+    def import_pem_cert(self, nickname, flags, location):
+        """Import a cert form the given PEM file.
+
+        The file must contain exactly one certificate.
+        """
+        try:
+            with open(location) as fd:
+                certs = fd.read()
+        except IOError as e:
+            raise RuntimeError(
+                "Failed to open %s: %s" % (location, e.strerror)
+            )
+
+        cert, st = find_cert_from_txt(certs)
+        self.add_cert(cert, nickname, flags, pem=True)
+
+        try:
+            find_cert_from_txt(certs, st)
+        except RuntimeError:
+            pass
+        else:
+            raise ValueError('%s contains more than one certificate' %
+                             location)
+
+    def add_cert(self, cert, nick, flags, pem=False):
+        args = ["-A", "-n", nick, "-t", flags]
+        if pem:
+            args.append("-a")
+        self.run_certutil(args, stdin=cert)
+
+    def delete_cert(self, nick):
+        self.run_certutil(["-D", "-n", nick])
+
+    def verify_server_cert_validity(self, nickname, hostname):
+        """Verify a certificate is valid for a SSL server with given hostname
+
+        Raises a ValueError if the certificate is invalid.
+        """
+        certdb = cert = None
+        if nss.nss_is_initialized():
+            nss.nss_shutdown()
+        nss.nss_init(self.secdir)
+        try:
+            certdb = nss.get_default_certdb()
+            cert = nss.find_cert_from_nickname(nickname)
+            intended_usage = nss.certificateUsageSSLServer
+            try:
+                approved_usage = cert.verify_now(certdb, True, intended_usage)
+            except NSPRError, e:
+                if e.errno != -8102:
+                    raise ValueError(e.strerror)
+                approved_usage = 0
+            if not approved_usage & intended_usage:
+                raise ValueError('invalid for a SSL server')
+            if not cert.verify_hostname(hostname):
+                raise ValueError('invalid for server %s' % hostname)
+        finally:
+            del certdb, cert
+            nss.nss_shutdown()
+
+        return None
+
+    def verify_ca_cert_validity(self, nickname):
+        certdb = cert = None
+        if nss.nss_is_initialized():
+            nss.nss_shutdown()
+        nss.nss_init(self.secdir)
+        try:
+            certdb = nss.get_default_certdb()
+            cert = nss.find_cert_from_nickname(nickname)
+            intended_usage = nss.certificateUsageSSLCA
+            try:
+                approved_usage = cert.verify_now(certdb, True, intended_usage)
+            except NSPRError, e:
+                if e.errno != -8102:    # SEC_ERROR_INADEQUATE_KEY_USAGE
+                    raise ValueError(e.strerror)
+                approved_usage = 0
+            if approved_usage & intended_usage != intended_usage:
+                raise ValueError('invalid for a CA')
+        finally:
+            del certdb, cert
+            nss.nss_shutdown()
diff --git a/ipaserver/install/certs.py b/ipaserver/install/certs.py
index 4d508cd..c2ab59e 100644
--- a/ipaserver/install/certs.py
+++ b/ipaserver/install/certs.py
@@ -19,7 +19,6 @@
 
 import os
 import stat
-import re
 import sys
 import tempfile
 import shutil
@@ -28,15 +27,12 @@ import pwd
 import base64
 from hashlib import sha1
 
-from nss import nss
-from nss.error import NSPRError
-
 from ipapython.ipa_log_manager import root_logger
 from ipapython import dogtag
 from ipapython import sysrestore
 from ipapython import ipautil
 from ipapython import certmonger
-from ipapython.certdb import get_ca_nickname
+from ipapython.certdb import get_ca_nickname, find_cert_from_txt, NSSDatabase
 from ipapython.dn import DN
 from ipalib import pkcs10, x509, api
 from ipalib.errors import CertificateOperationError
@@ -48,24 +44,6 @@ from ipaplatform.paths import paths
 # where apache can reach
 NSS_DIR = paths.HTTPD_ALIAS_DIR
 
-def find_cert_from_txt(cert, start=0):
-    """
-    Given a cert blob (str) which may or may not contian leading and
-    trailing text, pull out just the certificate part. This will return
-    the FIRST cert in a stream of data.
-
-    Returns a tuple (certificate, last position in cert)
-    """
-    s = cert.find('-----BEGIN CERTIFICATE-----', start)
-    e = cert.find('-----END CERTIFICATE-----', s)
-    if e > 0: e = e + 25
-
-    if s < 0 or e < 0:
-        raise RuntimeError("Unable to find certificate")
-
-    cert = cert[s:e]
-    return (cert, e)
-
 def get_cert_nickname(cert):
     """
     Using the subject from cert come up with a nickname suitable
@@ -83,236 +61,6 @@ def get_cert_nickname(cert):
     return (str(dn[0]), dn)
 
 
-class NSSDatabase(object):
-    """A general-purpose wrapper around a NSS cert database
-
-    For permanent NSS databases, pass the cert DB directory to __init__
-
-    For temporary databases, do not pass nssdir, and call close() when done
-    to remove the DB. Alternatively, a NSSDatabase can be used as a
-    context manager that calls close() automatically.
-    """
-    # Traditionally, we used CertDB for our NSS DB operations, but that class
-    # got too tied to IPA server details, killing reusability.
-    # BaseCertDB is a class that knows nothing about IPA.
-    # Generic NSS DB code should be moved here.
-    def __init__(self, nssdir=None):
-        if nssdir is None:
-            self.secdir = tempfile.mkdtemp()
-            self._is_temporary = True
-        else:
-            self.secdir = nssdir
-            self._is_temporary = False
-
-    def close(self):
-        if self._is_temporary:
-            shutil.rmtree(self.secdir)
-
-    def __enter__(self):
-        return self
-
-    def __exit__(self, type, value, tb):
-        self.close()
-
-    def run_certutil(self, args, stdin=None):
-        new_args = [paths.CERTUTIL, "-d", self.secdir]
-        new_args = new_args + args
-        return ipautil.run(new_args, stdin)
-
-    def create_db(self, password_filename):
-        """Create cert DB
-
-        :param password_filename: Name of file containing the database password
-        """
-        self.run_certutil(["-N", "-f", password_filename])
-
-    def list_certs(self):
-        """Return nicknames and cert flags for all certs in the database
-
-        :return: List of (name, trust_flags) tuples
-        """
-        certs, stderr, returncode = self.run_certutil(["-L"])
-        certs = certs.splitlines()
-
-        # FIXME, this relies on NSS never changing the formatting of certutil
-        certlist = []
-        for cert in certs:
-            match = re.match(r'^(.+?)\s+(\w*,\w*,\w*)\s*$', cert)
-            if match:
-                certlist.append(match.groups())
-
-        return tuple(certlist)
-
-    def find_server_certs(self):
-        """Return nicknames and cert flags for server certs in the database
-
-        Server certs have an "u" character in the trust flags.
-
-        :return: List of (name, trust_flags) tuples
-        """
-        server_certs = []
-        for name, flags in self.list_certs():
-            if 'u' in flags:
-                server_certs.append((name, flags))
-
-        return server_certs
-
-    def get_trust_chain(self, nickname):
-        """Return names of certs in a given cert's trust chain
-
-        :param nickname: Name of the cert
-        :return: List of certificate names
-        """
-        root_nicknames = []
-        chain, stderr, returncode = self.run_certutil([
-            "-O", "-n", nickname])
-        chain = chain.splitlines()
-
-        for c in chain:
-            m = re.match('\s*"(.*)" \[.*', c)
-            if m:
-                root_nicknames.append(m.groups()[0])
-
-        return root_nicknames
-
-    def import_pkcs12(self, pkcs12_filename, db_password_filename,
-                      pkcs12_passwd=None):
-        args = [paths.PK12UTIL, "-d", self.secdir,
-                "-i", pkcs12_filename,
-                "-k", db_password_filename, '-v']
-        if pkcs12_passwd is not None:
-            pkcs12_passwd = pkcs12_passwd + '\n'
-            args = args + ["-w", paths.DEV_STDIN]
-        try:
-            ipautil.run(args, stdin=pkcs12_passwd)
-        except ipautil.CalledProcessError, e:
-            if e.returncode == 17:
-                raise RuntimeError("incorrect password for pkcs#12 file %s" %
-                    pkcs12_filename)
-            elif e.returncode == 10:
-                raise RuntimeError("Failed to open %s" % pkcs12_filename)
-            else:
-                raise RuntimeError("unknown error import pkcs#12 file %s" %
-                    pkcs12_filename)
-
-    def trust_root_cert(self, root_nickname, trust_flags=None):
-        if root_nickname[:7] == "Builtin":
-            root_logger.debug(
-                "No need to add trust for built-in root CAs, skipping %s" %
-                root_nickname)
-        else:
-            if trust_flags is None:
-                trust_flags = 'C,,'
-            try:
-                self.run_certutil(["-M", "-n", root_nickname,
-                                   "-t", trust_flags])
-            except ipautil.CalledProcessError, e:
-                raise RuntimeError(
-                    "Setting trust on %s failed" % root_nickname)
-
-    def get_cert(self, nickname, pem=False):
-        args = ['-L', '-n', nickname]
-        if pem:
-            args.append('-a')
-        else:
-            args.append('-r')
-        try:
-            cert, err, returncode = self.run_certutil(args)
-        except ipautil.CalledProcessError:
-            raise RuntimeError("Failed to get %s" % nickname)
-        return cert
-
-    def export_pem_cert(self, nickname, location):
-        """Export the given cert to PEM file in the given location"""
-        cert = self.get_cert(nickname)
-        with open(location, "w+") as fd:
-            fd.write(cert)
-        os.chmod(location, 0444)
-
-    def import_pem_cert(self, nickname, flags, location):
-        """Import a cert form the given PEM file.
-
-        The file must contain exactly one certificate.
-        """
-        try:
-            with open(location) as fd:
-                certs = fd.read()
-        except IOError as e:
-            raise RuntimeError(
-                "Failed to open %s: %s" % (location, e.strerror)
-            )
-
-        cert, st = find_cert_from_txt(certs)
-        self.add_cert(cert, nickname, flags, pem=True)
-
-        try:
-            find_cert_from_txt(certs, st)
-        except RuntimeError:
-            pass
-        else:
-            raise ValueError('%s contains more than one certificate' %
-                             location)
-
-    def add_cert(self, cert, nick, flags, pem=False):
-        args = ["-A", "-n", nick, "-t", flags]
-        if pem:
-            args.append("-a")
-        self.run_certutil(args, stdin=cert)
-
-    def delete_cert(self, nick):
-        self.run_certutil(["-D", "-n", nick])
-
-    def verify_server_cert_validity(self, nickname, hostname):
-        """Verify a certificate is valid for a SSL server with given hostname
-
-        Raises a ValueError if the certificate is invalid.
-        """
-        certdb = cert = None
-        if nss.nss_is_initialized():
-            nss.nss_shutdown()
-        nss.nss_init(self.secdir)
-        try:
-            certdb = nss.get_default_certdb()
-            cert = nss.find_cert_from_nickname(nickname)
-            intended_usage = nss.certificateUsageSSLServer
-            try:
-                approved_usage = cert.verify_now(certdb, True, intended_usage)
-            except NSPRError, e:
-                if e.errno != -8102:
-                    raise ValueError(e.strerror)
-                approved_usage = 0
-            if not approved_usage & intended_usage:
-                raise ValueError('invalid for a SSL server')
-            if not cert.verify_hostname(hostname):
-                raise ValueError('invalid for server %s' % hostname)
-        finally:
-            del certdb, cert
-            nss.nss_shutdown()
-
-        return None
-
-    def verify_ca_cert_validity(self, nickname):
-        certdb = cert = None
-        if nss.nss_is_initialized():
-            nss.nss_shutdown()
-        nss.nss_init(self.secdir)
-        try:
-            certdb = nss.get_default_certdb()
-            cert = nss.find_cert_from_nickname(nickname)
-            intended_usage = nss.certificateUsageSSLCA
-            try:
-                approved_usage = cert.verify_now(certdb, True, intended_usage)
-            except NSPRError, e:
-                if e.errno != -8102:    # SEC_ERROR_INADEQUATE_KEY_USAGE
-                    raise ValueError(e.strerror)
-                approved_usage = 0
-            if approved_usage & intended_usage != intended_usage:
-                raise ValueError('invalid for a CA')
-        finally:
-            del certdb, cert
-            nss.nss_shutdown()
-
-
 class CertDB(object):
     """An IPA-server-specific wrapper around NSS
 
-- 
1.9.3

>From 4a6c9d1ba218099e0cc7aaf7d1cda64e576845d2 Mon Sep 17 00:00:00 2001
From: Jan Cholasta <jchol...@redhat.com>
Date: Thu, 18 Sep 2014 11:42:14 +0200
Subject: [PATCH 03/10] Add NSSDatabase.has_nickname for checking nickname
 presence in a NSS DB

https://fedorahosted.org/freeipa/ticket/4416
---
 ipapython/certdb.py | 9 +++++++++
 1 file changed, 9 insertions(+)

diff --git a/ipapython/certdb.py b/ipapython/certdb.py
index 9c1dfc4..a6f00d3 100644
--- a/ipapython/certdb.py
+++ b/ipapython/certdb.py
@@ -215,6 +215,15 @@ class NSSDatabase(object):
             raise RuntimeError("Failed to get %s" % nickname)
         return cert
 
+    def has_nickname(self, nickname):
+        try:
+            self.get_cert(nickname)
+        except RuntimeError:
+            # This might be error other than "nickname not found". Beware.
+            return False
+        else:
+            return True
+
     def export_pem_cert(self, nickname, location):
         """Export the given cert to PEM file in the given location"""
         cert = self.get_cert(nickname)
-- 
1.9.3

>From fee7267b21e5ca571c546aed98b4c1a1a614c105 Mon Sep 17 00:00:00 2001
From: Jan Cholasta <jchol...@redhat.com>
Date: Thu, 18 Sep 2014 12:00:15 +0200
Subject: [PATCH 04/10] Use NSSDatabase instead of direct certutil calls in
 client code

https://fedorahosted.org/freeipa/ticket/4416
---
 ipa-client/ipa-install/ipa-client-install | 50 ++++++++-----------------------
 ipa-client/ipaclient/ipa_certupdate.py    | 14 ++++-----
 ipapython/certdb.py                       | 20 ++++++-------
 3 files changed, 26 insertions(+), 58 deletions(-)

diff --git a/ipa-client/ipa-install/ipa-client-install b/ipa-client/ipa-install/ipa-client-install
index ab40cd8..22085ec 100755
--- a/ipa-client/ipa-install/ipa-client-install
+++ b/ipa-client/ipa-install/ipa-client-install
@@ -226,14 +226,6 @@ def logging_setup(options):
 def log_service_error(name, action, error):
     root_logger.error("%s failed to %s: %s", name, action, str(error))
 
-def nickname_exists(nickname):
-        (sout, serr, returncode) = run([paths.CERTUTIL, "-L", "-d", paths.NSS_DB_DIR, "-n", nickname], raiseonerr=False)
-
-        if returncode == 0:
-            return True
-        else:
-            return False
-
 def purge_ipa_certs(additional=[]):
     filename = paths.NSSDB_IPA_TXT
     if file_exists(filename):
@@ -258,12 +250,11 @@ def purge_ipa_certs(additional=[]):
         if nickname:
             nicknames.add(nickname)
 
+    sys_db = certdb.NSSDatabase(paths.NSS_DB_DIR)
     for nickname in nicknames:
-        while nickname_exists(nickname):
+        while sys_db.has_nickname(nickname):
             try:
-                run([paths.CERTUTIL, "-D",
-                     "-d", paths.NSS_DB_DIR,
-                     "-n", nickname])
+                sys_db.delete_cert(nickname)
             except Exception, e:
                 root_logger.error(
                     "Failed to remove %s from /etc/pki/nssdb: %s", nickname, e)
@@ -2533,23 +2524,16 @@ def install(options, env, fstore, statestore):
     except ValueError:
         pass
 
-    tmp_nss_dir = tempfile.mkdtemp()
-    try:
+    with certdb.NSSDatabase() as tmp_db:
         # Add CA certs to a temporary NSS database
         try:
             pwd_file = ipautil.write_tmp_file(ipautil.ipa_generate_password())
-            run([paths.CERTUTIL, '-N',
-                 '-d', tmp_nss_dir,
-                 '-f', pwd_file.name])
+            tmp_db.create_db(pwd_file.name)
 
             ca_certs = x509.load_certificate_list_from_file(CACERT)
             ca_certs = [cert.der_data for cert in ca_certs]
             for i, cert in enumerate(ca_certs):
-                run([paths.CERTUTIL, '-A',
-                     '-d', tmp_nss_dir,
-                     '-n', 'CA certificate %d' % (i + 1),
-                     '-t', 'C,,'],
-                     stdin=cert)
+                tmp_db.add_cert(cert, 'CA certificate %d' % (i + 1), 'C,,')
         except CalledProcessError, e:
             root_logger.info("Failed to add CA to temporary NSS database.")
             return CLIENT_INSTALL_ERROR
@@ -2557,7 +2541,7 @@ def install(options, env, fstore, statestore):
         # Now, let's try to connect to the server's RPC interface
         connected = False
         try:
-            api.Backend.rpcclient.connect(nss_dir=tmp_nss_dir)
+            api.Backend.rpcclient.connect(nss_dir=tmp_db.secdir)
             connected = True
             root_logger.debug("Try RPC connection")
             api.Backend.rpcclient.forward('ping')
@@ -2569,7 +2553,7 @@ def install(options, env, fstore, statestore):
                 "Trying with delegate=True", e)
             try:
                 api.Backend.rpcclient.connect(delegate=True,
-                                              nss_dir=tmp_nss_dir)
+                                              nss_dir=tmp_db.secdir)
                 root_logger.debug("Try RPC connection")
                 api.Backend.rpcclient.forward('ping')
 
@@ -2594,8 +2578,6 @@ def install(options, env, fstore, statestore):
             root_logger.error(
                 "Cannot connect to the server due to generic error: %s", e)
             return CLIENT_INSTALL_ERROR
-    finally:
-        shutil.rmtree(tmp_nss_dir)
 
     # Use the RPC directly so older servers are supported
     result = api.Backend.rpcclient.forward(
@@ -2622,14 +2604,10 @@ def install(options, env, fstore, statestore):
 
     # Add the CA certificates to the IPA NSS database
     root_logger.debug("Adding CA certificates to the IPA NSS database.")
+    ipa_db = certdb.NSSDatabase(paths.IPA_NSSDB_DIR)
     for cert, nickname, trust_flags in ca_certs_trust:
         try:
-            run([paths.CERTUTIL,
-                 "-A",
-                 "-d", paths.IPA_NSSDB_DIR,
-                 "-n", nickname,
-                 "-t", trust_flags],
-                stdin=cert)
+            ipa_db.add_cert(cert, nickname, trust_flags)
         except CalledProcessError, e:
             root_logger.error(
                 "Failed to add %s to the IPA NSS database.", nickname)
@@ -2653,14 +2631,10 @@ def install(options, env, fstore, statestore):
 
     root_logger.debug(
         "Attempting to add CA certificates to the default NSS database.")
+    sys_db = certdb.NSSDatabase(paths.NSS_DB_DIR)
     for cert, nickname, trust_flags in ca_certs_trust:
         try:
-            run([paths.CERTUTIL,
-                 "-A",
-                 "-d", paths.NSS_DB_DIR,
-                 "-n", nickname,
-                 "-t", trust_flags],
-                stdin=cert)
+            sys_db.add_cert(cert, nickname, trust_flags)
         except CalledProcessError, e:
             root_logger.error(
                 "Failed to add %s to the default NSS database.", nickname)
diff --git a/ipa-client/ipaclient/ipa_certupdate.py b/ipa-client/ipaclient/ipa_certupdate.py
index 57dbf20..f7b0e29 100644
--- a/ipa-client/ipaclient/ipa_certupdate.py
+++ b/ipa-client/ipaclient/ipa_certupdate.py
@@ -22,7 +22,7 @@ import tempfile
 import shutil
 
 from ipapython import (admintool, ipautil, ipaldap, sysrestore, dogtag,
-                       certmonger)
+                       certmonger, certdb)
 from ipaplatform import services
 from ipaplatform.paths import paths
 from ipaplatform.tasks import tasks
@@ -72,11 +72,10 @@ class CertUpdate(admintool.AdminTool):
         self.update_file(paths.IPA_CA_CRT, certs)
         self.update_db(paths.IPA_NSSDB_DIR, certs)
 
+        sys_db = certdb.NSSDatabase(paths.NSS_DB_DIR)
         for nickname in ('IPA CA', 'External CA cert'):
             try:
-                ipautil.run([paths.CERTUTIL, '-D',
-                             '-d', paths.NSS_DB_DIR,
-                             '-n', nickname])
+                sys_db.delete_cert(nickname)
             except ipautil.CalledProcessError, e:
                 pass
 
@@ -165,15 +164,12 @@ class CertUpdate(admintool.AdminTool):
             self.log.error("failed to update %s: %s", filename, e)
 
     def update_db(self, path, certs):
+        db = certdb.NSSDatabase(path)
         for cert, nickname, trusted, eku in certs:
             trust_flags = certstore.key_policy_to_trust_flags(
                 trusted, True, eku)
             try:
-                ipautil.run([paths.CERTUTIL, '-A',
-                             '-d', path,
-                             '-n', nickname,
-                             '-t', trust_flags],
-                             stdin=cert)
+                db.add_cert(cert, nickname, trust_flags)
             except ipautil.CalledProcessError, e:
                 self.log.error(
                     "failed to update %s in %s: %s", nickname, path, e)
diff --git a/ipapython/certdb.py b/ipapython/certdb.py
index a6f00d3..56cbe36 100644
--- a/ipapython/certdb.py
+++ b/ipapython/certdb.py
@@ -36,24 +36,22 @@ def get_ca_nickname(realm, format=CA_NICKNAME_FMT):
 
 
 def create_ipa_nssdb():
-    pwdfile = os.path.join(paths.IPA_NSSDB_DIR, 'pwdfile.txt')
+    db = NSSDatabase(paths.IPA_NSSDB_DIR)
+    pwdfile = os.path.join(db.secdir, 'pwdfile.txt')
 
     ipautil.backup_file(pwdfile)
-    ipautil.backup_file(os.path.join(paths.IPA_NSSDB_DIR, 'cert8.db'))
-    ipautil.backup_file(os.path.join(paths.IPA_NSSDB_DIR, 'key3.db'))
-    ipautil.backup_file(os.path.join(paths.IPA_NSSDB_DIR, 'secmod.db'))
+    ipautil.backup_file(os.path.join(db.secdir, 'cert8.db'))
+    ipautil.backup_file(os.path.join(db.secdir, 'key3.db'))
+    ipautil.backup_file(os.path.join(db.secdir, 'secmod.db'))
 
     with open(pwdfile, 'w') as f:
         f.write(ipautil.ipa_generate_password(pwd_len=40))
     os.chmod(pwdfile, 0600)
 
-    ipautil.run([paths.CERTUTIL,
-         "-N",
-         "-d", paths.IPA_NSSDB_DIR,
-         "-f", pwdfile])
-    os.chmod(os.path.join(paths.IPA_NSSDB_DIR, 'cert8.db'), 0644)
-    os.chmod(os.path.join(paths.IPA_NSSDB_DIR, 'key3.db'), 0644)
-    os.chmod(os.path.join(paths.IPA_NSSDB_DIR, 'secmod.db'), 0644)
+    db.create_db(pwdfile)
+    os.chmod(os.path.join(db.secdir, 'cert8.db'), 0644)
+    os.chmod(os.path.join(db.secdir, 'key3.db'), 0644)
+    os.chmod(os.path.join(db.secdir, 'secmod.db'), 0644)
 
 
 def find_cert_from_txt(cert, start=0):
-- 
1.9.3

>From ad4e508520c83a2d201b2454f538dc25e6fc94de Mon Sep 17 00:00:00 2001
From: Jan Cholasta <jchol...@redhat.com>
Date: Mon, 22 Sep 2014 11:13:15 +0200
Subject: [PATCH 05/10] Use /etc/ipa/nssdb to get nicknames of IPA certs
 installed in /etc/pki/nssdb

Previously a list of nicknames was kept in /etc/pki/nssdb/ipa.txt. The file
is removed now.

https://fedorahosted.org/freeipa/ticket/3259
---
 ipa-client/ipa-install/ipa-client-install | 78 +++++++++----------------------
 ipa-client/ipaclient/ipa_certupdate.py    | 59 +++++++++--------------
 ipaplatform/base/paths.py                 |  1 -
 3 files changed, 42 insertions(+), 96 deletions(-)

diff --git a/ipa-client/ipa-install/ipa-client-install b/ipa-client/ipa-install/ipa-client-install
index 22085ec..2e59df9 100755
--- a/ipa-client/ipa-install/ipa-client-install
+++ b/ipa-client/ipa-install/ipa-client-install
@@ -226,41 +226,6 @@ def logging_setup(options):
 def log_service_error(name, action, error):
     root_logger.error("%s failed to %s: %s", name, action, str(error))
 
-def purge_ipa_certs(additional=[]):
-    filename = paths.NSSDB_IPA_TXT
-    if file_exists(filename):
-        try:
-            with open(filename, 'r') as f:
-                lines = f.readlines()
-        except IOError, e:
-            root_logger.error("Failed to open %s: %s", filename, e)
-            return False
-        finally:
-            try:
-                os.unlink(filename)
-            except OSError, e:
-                root_logger.error("Failed to remove %s: %s", filename, e)
-                return False
-    else:
-        lines = []
-
-    nicknames = set(additional)
-    for line in lines:
-        nickname = line.strip()
-        if nickname:
-            nicknames.add(nickname)
-
-    sys_db = certdb.NSSDatabase(paths.NSS_DB_DIR)
-    for nickname in nicknames:
-        while sys_db.has_nickname(nickname):
-            try:
-                sys_db.delete_cert(nickname)
-            except Exception, e:
-                root_logger.error(
-                    "Failed to remove %s from /etc/pki/nssdb: %s", nickname, e)
-
-    return True
-
 def cert_summary(msg, certs, indent='    '):
     if msg:
         s = '%s\n' % msg
@@ -541,16 +506,32 @@ def uninstall(options, env):
             cmonger.service_name, str(e))
 
     # Remove our host cert and CA cert
-    for filename in (os.path.join(paths.IPA_NSSDB_DIR, 'cert8.db'),
-                     os.path.join(paths.IPA_NSSDB_DIR, 'key3.db'),
-                     os.path.join(paths.IPA_NSSDB_DIR, 'secmod.db'),
-                     os.path.join(paths.IPA_NSSDB_DIR, 'pwdfile.txt')):
+    ipa_db = certdb.NSSDatabase(paths.IPA_NSSDB_DIR)
+    try:
+        ipa_certs = ipa_db.list_certs()
+    except CalledProcessError, e:
+        root_logger.error(
+            "Failed to list certificates in %s: %s", ipa_db.secdir, e)
+        ipa_certs = []
+
+    for filename in (os.path.join(ipa_db.secdir, 'cert8.db'),
+                     os.path.join(ipa_db.secdir, 'key3.db'),
+                     os.path.join(ipa_db.secdir, 'secmod.db'),
+                     os.path.join(ipa_db.secdir, 'pwdfile.txt')):
         try:
             os.remove(filename)
         except OSError, e:
             root_logger.error("Failed to remove %s: %s", filename, e)
 
-    purge_ipa_certs({client_nss_nickname, 'IPA CA', 'External CA cert'})
+    sys_db = certdb.NSSDatabase(paths.NSS_DB_DIR)
+    for nickname, trust_flags in ipa_certs:
+        while sys_db.has_nickname(nickname):
+            try:
+                sys_db.delete_cert(nickname)
+            except Exception, e:
+                root_logger.error("Failed to remove %s from %s: %s",
+                                  nickname, sys_db.secdir, e)
+                break
 
     try:
         cmonger.stop()
@@ -2617,18 +2598,6 @@ def install(options, env, fstore, statestore):
     tasks.insert_ca_certs_into_systemwide_ca_store(ca_certs)
 
     # Add the CA certificates to the default NSS database
-    if not purge_ipa_certs():
-        root_logger.info(
-            "Failed to remove old IPA certificates from the default NSS "
-            "database.")
-        return CLIENT_INSTALL_ERROR
-
-    try:
-        list_file = open(paths.NSSDB_IPA_TXT, 'w')
-    except IOError, e:
-        root_logger.error("Failed to open /etc/pki/nssdb/ipa.txt: %s", e)
-        return CLIENT_INSTALL_ERROR
-
     root_logger.debug(
         "Attempting to add CA certificates to the default NSS database.")
     sys_db = certdb.NSSDatabase(paths.NSS_DB_DIR)
@@ -2638,14 +2607,9 @@ def install(options, env, fstore, statestore):
         except CalledProcessError, e:
             root_logger.error(
                 "Failed to add %s to the default NSS database.", nickname)
-            list_file.close()
             return CLIENT_INSTALL_ERROR
-        else:
-            list_file.write(nickname + '\n')
     root_logger.info("Added CA certificates to the default NSS database.")
 
-    list_file.close()
-
     if not options.on_master:
         client_dns(cli_server[0], hostname, options.dns_updates)
 
diff --git a/ipa-client/ipaclient/ipa_certupdate.py b/ipa-client/ipaclient/ipa_certupdate.py
index f7b0e29..8259755 100644
--- a/ipa-client/ipaclient/ipa_certupdate.py
+++ b/ipa-client/ipaclient/ipa_certupdate.py
@@ -70,49 +70,32 @@ class CertUpdate(admintool.AdminTool):
 
     def update_client(self, certs):
         self.update_file(paths.IPA_CA_CRT, certs)
-        self.update_db(paths.IPA_NSSDB_DIR, certs)
 
+        ipa_db = certdb.NSSDatabase(paths.IPA_NSSDB_DIR)
         sys_db = certdb.NSSDatabase(paths.NSS_DB_DIR)
-        for nickname in ('IPA CA', 'External CA cert'):
-            try:
-                sys_db.delete_cert(nickname)
-            except ipautil.CalledProcessError, e:
-                pass
-
-        self.update_db(paths.NSS_DB_DIR, certs)
 
-        new_nicknames = set(c[1] for c in certs)
-        old_nicknames = set()
-        if ipautil.file_exists(paths.NSSDB_IPA_TXT):
-            try:
-                list_file = open(paths.NSSDB_IPA_TXT, 'r')
-            except IOError, e:
-                self.log.error("failed to open %s: %s", paths.NSSDB_IPA_TXT, e)
-            else:
+        # Remove IPA certs from /etc/pki/nssdb
+        for nickname, trust_flags in ipa_db.list_certs():
+            while sys_db.has_nickname(nickname):
                 try:
-                    lines = list_file.readlines()
-                except IOError, e:
-                    self.log.error(
-                        "failed to read %s: %s", paths.NSSDB_IPA_TXT, e)
-                else:
-                    for line in lines:
-                        nickname = line.strip()
-                        if nickname:
-                            old_nicknames.add(nickname)
-                list_file.close()
-        if new_nicknames != old_nicknames:
-            try:
-                list_file = open(paths.NSSDB_IPA_TXT, 'w')
-            except IOError, e:
-                self.log.error("failed to open %s: %s", paths.NSSDB_IPA_TXT, e)
-            else:
+                    sys_db.delete_cert(nickname)
+                except ipautil.CalledProcessError, e:
+                    self.log.error("Failed to remove %s from %s: %s",
+                                   nickname, sys_db.secdir, e)
+                    break
+
+        # Remove old IPA certs from /etc/ipa/nssdb
+        for nickname in ('IPA CA', 'External CA cert'):
+            while ipa_db.has_nickname(nickname):
                 try:
-                    for nickname in new_nicknames:
-                        list_file.write(nickname + '\n')
-                except IOError, e:
-                    self.log.error(
-                        "failed to write %s: %s", paths.NSSDB_IPA_TXT, e)
-                list_file.close()
+                    ipa_db.delete_cert(nickname)
+                except ipautil.CalledProcessError, e:
+                    self.log.error("Failed to remove %s from %s: %s",
+                                   nickname, ipa_db.secdir, e)
+                    break
+
+        self.update_db(ipa_db.secdir, certs)
+        self.update_db(sys_db.secdir, certs)
 
         tasks.remove_ca_certs_from_systemwide_ca_store()
         tasks.insert_ca_certs_into_systemwide_ca_store(certs)
diff --git a/ipaplatform/base/paths.py b/ipaplatform/base/paths.py
index 1493918..5f73d85 100644
--- a/ipaplatform/base/paths.py
+++ b/ipaplatform/base/paths.py
@@ -85,7 +85,6 @@ class BasePathNamespace(object):
     NSSDB_CERT8_DB = "/etc/pki/nssdb/cert8.db"
     NSSDB_KEY3_DB = "/etc/pki/nssdb/key3.db"
     NSSDB_SECMOD_DB = "/etc/pki/nssdb/secmod.db"
-    NSSDB_IPA_TXT = "/etc/pki/nssdb/ipa.txt"
     PKI_TOMCAT = "/etc/pki/pki-tomcat"
     PKI_TOMCAT_ALIAS_DIR = "/etc/pki/pki-tomcat/alias/"
     PKI_TOMCAT_PASSWORD_CONF = "/etc/pki/pki-tomcat/password.conf"
-- 
1.9.3

>From 9aaeaa84fb140ea6bd5ecc21cec25fdb346b5142 Mon Sep 17 00:00:00 2001
From: Jan Cholasta <jchol...@redhat.com>
Date: Thu, 18 Sep 2014 10:52:56 +0200
Subject: [PATCH 06/10] Check if IPA client is configured in ipa-certupdate

https://fedorahosted.org/freeipa/ticket/4460
---
 ipa-client/ipaclient/ipa_certupdate.py | 6 ++++++
 1 file changed, 6 insertions(+)

diff --git a/ipa-client/ipaclient/ipa_certupdate.py b/ipa-client/ipaclient/ipa_certupdate.py
index 8259755..c25dcae 100644
--- a/ipa-client/ipaclient/ipa_certupdate.py
+++ b/ipa-client/ipaclient/ipa_certupdate.py
@@ -41,6 +41,12 @@ class CertUpdate(admintool.AdminTool):
         super(CertUpdate, self).validate_options(needs_root=True)
 
     def run(self):
+        fstore = sysrestore.FileStore(paths.IPA_CLIENT_SYSRESTORE)
+        if (not fstore.has_files() and
+            not os.path.exists(paths.IPA_DEFAULT_CONF)):
+            raise admintool.ScriptError(
+                "IPA client is not configured on this system.")
+
         api.bootstrap(context='cli_installer')
         api.finalize()
 
-- 
1.9.3

>From d88e4ec5a8fa291e95d108c309a83c1f19ab9e1a Mon Sep 17 00:00:00 2001
From: Jan Cholasta <jchol...@redhat.com>
Date: Thu, 18 Sep 2014 11:15:49 +0200
Subject: [PATCH 07/10] Get server hostname from jsonrpc_uri in ipa-certupdate

https://fedorahosted.org/freeipa/ticket/3259
---
 ipa-client/ipaclient/ipa_certupdate.py | 6 ++----
 1 file changed, 2 insertions(+), 4 deletions(-)

diff --git a/ipa-client/ipaclient/ipa_certupdate.py b/ipa-client/ipaclient/ipa_certupdate.py
index c25dcae..fd6c80d 100644
--- a/ipa-client/ipaclient/ipa_certupdate.py
+++ b/ipa-client/ipaclient/ipa_certupdate.py
@@ -20,6 +20,7 @@
 import os
 import tempfile
 import shutil
+import urlparse
 
 from ipapython import (admintool, ipautil, ipaldap, sysrestore, dogtag,
                        certmonger, certdb)
@@ -50,10 +51,7 @@ class CertUpdate(admintool.AdminTool):
         api.bootstrap(context='cli_installer')
         api.finalize()
 
-        try:
-            server = api.env.server
-        except AttributeError:
-            server = api.env.host
+        server = urlparse.urlsplit(api.env.jsonrpc_uri).hostname
         ldap = ipaldap.IPAdmin(server)
 
         tmpdir = tempfile.mkdtemp(prefix="tmp-")
-- 
1.9.3

>From 29bc4238d022b69a3272060c4cb5f2cb60d408d1 Mon Sep 17 00:00:00 2001
From: Jan Cholasta <jchol...@redhat.com>
Date: Wed, 17 Sep 2014 15:04:11 +0200
Subject: [PATCH 08/10] Remove ipa-ca.crt from systemwide CA store on client
 uninstall and cert update

The file was used by previous versions of IPA to provide the IPA CA certificate
to p11-kit and has since been obsoleted by ipa.p11-kit, a file which contains
all the CA certificates and associated trust policy from the LDAP certificate
store.

Since p11-kit is hooked into /etc/httpd/alias, ipa-ca.crt must be removed to
prevent certificate import failures in installer code.

Also add ipa.p11-kit to the files owned by the freeipa-python package.

https://fedorahosted.org/freeipa/ticket/3259
---
 freeipa.spec.in             |  1 +
 ipaplatform/base/paths.py   |  1 +
 ipaplatform/fedora/tasks.py | 38 ++++++++++++++++++++++++++++----------
 3 files changed, 30 insertions(+), 10 deletions(-)

diff --git a/freeipa.spec.in b/freeipa.spec.in
index d16a865..99cd6df 100644
--- a/freeipa.spec.in
+++ b/freeipa.spec.in
@@ -812,6 +812,7 @@ fi
 %ghost %config(noreplace) %{_sysconfdir}/ipa/nssdb/key3.db
 %ghost %config(noreplace) %{_sysconfdir}/ipa/nssdb/secmod.db
 %ghost %config(noreplace) %{_sysconfdir}/ipa/nssdb/pwdfile.txt
+%ghost %config(noreplace) %{_sysconfdir}/pki/ca-trust/source/ipa.p11-kit
 
 %if ! %{ONLY_CLIENT}
 %files tests -f tests-python.list
diff --git a/ipaplatform/base/paths.py b/ipaplatform/base/paths.py
index 5f73d85..baaa109 100644
--- a/ipaplatform/base/paths.py
+++ b/ipaplatform/base/paths.py
@@ -80,6 +80,7 @@ class BasePathNamespace(object):
     PAM_LDAP_CONF = "/etc/pam_ldap.conf"
     PASSWD = "/etc/passwd"
     ETC_PKI_CA_DIR = "/etc/pki-ca"
+    SYSTEMWIDE_CA_STORE = "/etc/pki/ca-trust/source/anchors/"
     IPA_P11_KIT = "/etc/pki/ca-trust/source/ipa.p11-kit"
     NSS_DB_DIR = "/etc/pki/nssdb"
     NSSDB_CERT8_DB = "/etc/pki/nssdb/cert8.db"
diff --git a/ipaplatform/fedora/tasks.py b/ipaplatform/fedora/tasks.py
index 9f4a76b..351f523 100644
--- a/ipaplatform/fedora/tasks.py
+++ b/ipaplatform/fedora/tasks.py
@@ -158,6 +158,16 @@ class FedoraTaskNamespace(BaseTaskNamespace):
         auth_config.execute()
 
     def insert_ca_certs_into_systemwide_ca_store(self, ca_certs):
+        new_cacert_path = os.path.join(paths.SYSTEMWIDE_CA_STORE, 'ipa-ca.crt')
+
+        if os.path.exists(new_cacert_path):
+            try:
+                os.remove(new_cacert_path)
+            except OSError, e:
+                root_logger.error(
+                    "Could not remove %s: %s", new_cacert_path, e)
+                return False
+
         new_cacert_path = paths.IPA_P11_KIT
 
         try:
@@ -250,25 +260,33 @@ class FedoraTaskNamespace(BaseTaskNamespace):
         return False
 
     def remove_ca_certs_from_systemwide_ca_store(self):
-        new_cacert_path = paths.IPA_P11_KIT
+        ipa_ca_crt = os.path.join(paths.SYSTEMWIDE_CA_STORE, 'ipa-ca.crt')
+        update = False
 
         # Remove CA cert from systemwide store
-        if os.path.exists(new_cacert_path):
+        for new_cacert_path in (paths.IPA_P11_KIT, ipa_ca_crt):
+            if not os.path.exists(new_cacert_path):
+                continue
             try:
                 os.remove(new_cacert_path)
-                ipautil.run([paths.UPDATE_CA_TRUST])
             except OSError, e:
-                root_logger.error('Could not remove: %s, %s'
-                                   % (new_cacert_path, str(e)))
-                return False
+                root_logger.error(
+                    "Could not remove %s: %s", new_cacert_path, e)
+            else:
+                update = True
+
+        if update:
+            try:
+                ipautil.run([paths.UPDATE_CA_TRUST])
             except CalledProcessError, e:
-                root_logger.error('Could not update systemwide CA trust '
-                                  'database: %s' % str(e))
+                root_logger.error(
+                    "Could not update systemwide CA trust database: %s", e)
                 return False
             else:
-                root_logger.info('Systemwide CA database updated.')
+                root_logger.info("Systemwide CA database updated.")
+                return True
 
-        return True
+        return False
 
     def backup_and_replace_hostname(self, fstore, statestore, hostname):
         old_hostname = socket.gethostname()
-- 
1.9.3

>From f9338db90b45ff751b8c61ce8be4964f8245b1f9 Mon Sep 17 00:00:00 2001
From: Jan Cholasta <jchol...@redhat.com>
Date: Tue, 16 Sep 2014 10:34:57 +0200
Subject: [PATCH 09/10] Fix certmonger.wait_for_request

https://fedorahosted.org/freeipa/ticket/4558
---
 ipapython/certmonger.py | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/ipapython/certmonger.py b/ipapython/certmonger.py
index 85b0e9a..bcfafda 100644
--- a/ipapython/certmonger.py
+++ b/ipapython/certmonger.py
@@ -474,7 +474,7 @@ def check_state(dirs):
 
 def wait_for_request(request_id, timeout=120):
     for i in range(0, timeout, 5):
-        state = get_request_value(request_id, 'state').strip()
+        state = get_request_value(request_id, 'status')
         root_logger.debug("certmonger request is in state %r", state)
         if state in ('CA_REJECTED', 'CA_UNREACHABLE', 'CA_UNCONFIGURED',
                      'NEED_GUIDANCE', 'NEED_CA', 'MONITORING'):
-- 
1.9.3

>From 04a207885e7bd98204a9e316ee8c6205662b6cac Mon Sep 17 00:00:00 2001
From: Jan Cholasta <jchol...@redhat.com>
Date: Wed, 24 Sep 2014 19:22:59 +0200
Subject: [PATCH 10/10] Fix certmonger search for the CA cert in ipa-certupdate
 and ipa-cacert-manage

The search criteria did not include the CA agent name.

https://fedorahosted.org/freeipa/ticket/3259
---
 ipa-client/ipaclient/ipa_certupdate.py | 1 +
 ipaserver/install/ipa_cacert_manage.py | 7 +++++--
 2 files changed, 6 insertions(+), 2 deletions(-)

diff --git a/ipa-client/ipaclient/ipa_certupdate.py b/ipa-client/ipaclient/ipa_certupdate.py
index fd6c80d..ff16b9b 100644
--- a/ipa-client/ipaclient/ipa_certupdate.py
+++ b/ipa-client/ipaclient/ipa_certupdate.py
@@ -120,6 +120,7 @@ class CertUpdate(admintool.AdminTool):
         criteria = {
             'cert-database': dogtag_constants.ALIAS_DIR,
             'cert-nickname': nickname,
+            'ca-name': 'dogtag-ipa-ca-renew-agent',
         }
         request_id = certmonger.get_request_id(criteria)
         if request_id is not None:
diff --git a/ipaserver/install/ipa_cacert_manage.py b/ipaserver/install/ipa_cacert_manage.py
index c681261..155aa00 100644
--- a/ipaserver/install/ipa_cacert_manage.py
+++ b/ipaserver/install/ipa_cacert_manage.py
@@ -153,8 +153,11 @@ class CACertManage(admintool.AdminTool):
             raise admintool.ScriptError("CA is not configured on this system")
 
         nss_dir = ca.dogtag_constants.ALIAS_DIR
-        criteria = {'cert-database': nss_dir,
-                    'cert-nickname': self.cert_nickname}
+        criteria = {
+            'cert-database': nss_dir,
+            'cert-nickname': self.cert_nickname,
+            'ca-name': 'dogtag-ipa-ca-renew-agent',
+        }
         self.request_id = certmonger.get_request_id(criteria)
         if self.request_id is None:
             raise admintool.ScriptError(
-- 
1.9.3

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

Reply via email to