Rob Crittenden wrote:
Petr Viktorin wrote:
On 01/07/2013 05:42 PM, Rob Crittenden wrote:
Petr Viktorin wrote:
On 01/07/2013 03:09 PM, Rob Crittenden wrote:
Petr Viktorin wrote:
[...]

Works for me, but I have some questions (this is an area I know
little
about).

Can we be 100% sure these certs are always renewed together? Is
certmonger the only possible mechanism to update them?

You raise a good point. If though some mechanism someone replaces
one of
these certs it will cause the script to fail. Some notification of
this
failure will be logged though, and of course, the certs won't be
renewed.

One could conceivably manually renew one of these certificates. It is
probably a very remote possibility but it is non-zero.

Can we be sure certmonger always does the updates in parallel? If it
managed to update the audit cert before starting on the others, we'd
get
no CA restart for the others.

These all get issued at the same time so should expire at the same
time
as well (see problem above). The script will hang around for 10
minutes
waiting for the renewal to complete, then give up.

The certs might take different amounts of time to update, right?
Eventually, the expirations could go out of sync enough for it to
matter.
AFAICS, without proper locking we still get a race condition when the
other certs start being renewed some time (much less than 10 min) after
the audit one:

(time axis goes down)

         audit cert                  other cert
         ----------                  ----------
     certmonger does renew                .
   post-renew script starts               .
  check state of other certs: OK          .
             .                   certmonger starts renew
  certutil modifies NSS DB  +  certmonger modifies NSS DB  == boom!

This can't happen because we count the # of expected certs and wait
until all are in MONITORING before continuing.

The problem is that they're also in MONITORING before the whole renewal
starts. If the script happens to check just before the state changes
from MONITORING to GENERATING_CSR or whatever, we can get corruption.

The worse that would
happen is the trust wouldn't be set on the audit cert and dogtag
wouldn't be restarted.



The state the system would be in is this:

- audit cert trust not updated, so next restart of CA will fail
- CA is not restarted so will not use updated certificates

And anyway, why does certmonger do renewals in parallel? It seems
that
if it did one at a time, always waiting until the post-renew
script is
done, this patch wouldn't be necessary.


 From what Nalin told me certmonger has some coarse locking such that
renewals in a the same NSS database are serialized. As you point
out, it
would be nice to extend this locking to the post renewal scripts. We
can
ask Nalin about it. That would fix the potential corruption issue.
It is
still much nicer to not have to restart dogtag 4 times.


Well, three extra restarts every few years seems like a small price to
pay for robustness.

It is a bit of a problem though because the certs all renew within
seconds so end up fighting over who is restarting dogtag. This can cause
some renewals go into a failure state to be retried later. This is fine
functionally but makes QE a bit of a pain. You then have to make sure
that renewal is basically done, then restart certmonger and check
everything again, over and over until all the certs are renewed. This is
difficult to automate.

So we need to extend the certmonger lock, and wait until Dogtag is back
up before exiting the script. That way it'd still take longer than 1
restart, but all the renews should succeed.


Right, but older dogtag versions don't have the handy servlet to tell
that the service is actually up and responding. So it is difficult to
tell from tomcat alone whether the CA is actually up and handling requests.


Revised patch that takes advantage of new version of certmonger. certmonger-0.65 adds locking from the time renewal begins to the end of the post_save_command. This lets us be sure that no other certmonger renewals will have the NSS database open in read-write mode.

We need to be sure that tomcat is shut down before we let certmonger save the certificate to the NSS database because dogtag opens its database read/write and two writers can cause corruption.

rob

>From 78c0e087df1d1ad1c04517e70e2e8bbecc62d591 Mon Sep 17 00:00:00 2001
From: Rob Crittenden <rcrit...@redhat.com>
Date: Tue, 2 Dec 2014 13:18:36 -0500
Subject: [PATCH] Use new certmonger locking to prevent NSS database
 corruption.

dogtag opens its NSS database in read/write mode so we need to be very
careful during renewal that we don't also open it up read/write. We
basically need to serialize access to the database. certmonger does the
majority of this work via internal locking from the point where it generates
a new key/submits a rewewal through the pre_save and releases the lock after
the post_save command. This lock is held per NSS database so we're save
from certmonger. dogtag needs to be shutdown in the pre_save state so
certmonger can safely add the certificate and we can manipulate trust
in the post_save command.

Fix a number of bugs in renewal. The CA wasn't actually being restarted
at all due to a naming change upstream. In python we need to reference
services using python-ish names but the service is pki-cad. We need a
translation for non-Fedora systems as well.

Update the CA ou=People entry when he CA subsystem certificate is
renewed. This certificate is used as an identity certificate to bind
to the DS instance.

https://fedorahosted.org/freeipa/ticket/3292
https://fedorahosted.org/freeipa/ticket/3322
---
 freeipa.spec.in                        |  6 ++-
 install/restart_scripts/Makefile.am    |  1 +
 install/restart_scripts/renew_ca_cert  | 38 ++++++++--------
 install/restart_scripts/renew_ra_cert  | 54 +++--------------------
 install/restart_scripts/restart_pkicad | 20 ++++++---
 install/restart_scripts/stop_pkicad    | 40 +++++++++++++++++
 ipapython/certmonger.py                | 36 +++++++++++-----
 ipaserver/install/cainstance.py        | 79 +++++++++++++++++++++++++++++++---
 8 files changed, 184 insertions(+), 90 deletions(-)
 create mode 100644 install/restart_scripts/stop_pkicad

diff --git a/freeipa.spec.in b/freeipa.spec.in
index f1c45b6cce0ba109638bd538aa468c47d2024652..e7dd0c53dc39a8de881de3e4fe53731c0430abe7 100644
--- a/freeipa.spec.in
+++ b/freeipa.spec.in
@@ -213,7 +213,7 @@ Requires: wget
 Requires: libcurl >= 7.21.7-2
 Requires: xmlrpc-c >= 1.27.4
 Requires: sssd >= 1.8.0
-Requires: certmonger >= 0.60
+Requires: certmonger >= 0.65
 Requires: nss-tools
 Requires: bind-utils
 Requires: oddjob-mkhomedir
@@ -752,6 +752,10 @@ fi
 %ghost %attr(0644,root,apache) %config(noreplace) %{_sysconfdir}/ipa/ca.crt
 
 %changelog
+* Fri Jan 11 2013 Rob Crittenden <rcrit...@redhat.com> - 3.0.99-10
+- Set certmonger minimum version to 0.65 for NSS locking during
+  renewal
+
 * Fri Dec 7 2012 Endi S. Dewata <edew...@redhat.com> - 3.0.99-9
 - Bump minimum version of pki-ca to 10.0.0-0.54.b3
 
diff --git a/install/restart_scripts/Makefile.am b/install/restart_scripts/Makefile.am
index 210c4863e3d7d057627370093638e5bf9c47c802..fc45ecc888a9e3a728ef49564c45b7e89ebed465 100644
--- a/install/restart_scripts/Makefile.am
+++ b/install/restart_scripts/Makefile.am
@@ -7,6 +7,7 @@ app_DATA =                              \
 	restart_pkicad			\
 	renew_ca_cert			\
 	renew_ra_cert			\
+	stop_pkicad			\
 	$(NULL)
 
 EXTRA_DIST =                            \
diff --git a/install/restart_scripts/renew_ca_cert b/install/restart_scripts/renew_ca_cert
index 5317835fc6ad7b598290fa5387be0afa46a6ca5b..b7e4ebaae89472dd12f3767616e004f96358df7e 100644
--- a/install/restart_scripts/renew_ca_cert
+++ b/install/restart_scripts/renew_ca_cert
@@ -34,8 +34,10 @@ from ipapython import services as ipaservices
 from ipapython import ipautil
 from ipapython import dogtag
 from ipaserver.install import certs
+from ipaserver.install.cainstance import update_people_entry
 from ipaserver.plugins.ldap2 import ldap2
 from ipaserver.install.cainstance import update_cert_config
+from ipapython import certmonger
 
 # This script a post-cert-install command for certmonger. When certmonger
 # has renewed a CA subsystem certificate a copy is put into the replicated
@@ -82,8 +84,13 @@ except Exception, e:
 finally:
     shutil.rmtree(tmpdir)
 
-# Fix permissions on the audit cert if we're updating it
+update_cert_config(nickname, cert)
+
+if nickname == 'subsystemCert cert-pki-ca':
+    update_people_entry('pkidbuser', cert)
+
 if nickname == 'auditSigningCert cert-pki-ca':
+    # Fix trust on the audit cert
     db = certs.CertDB(api.env.realm, nssdir=alias_dir)
     args = ['-M',
             '-n', nickname,
@@ -91,25 +98,20 @@ if nickname == 'auditSigningCert cert-pki-ca':
            ]
     try:
         db.run_certutil(args)
+        syslog.syslog(syslog.LOG_NOTICE, 'Updated trust on certificate %s in %s' % (nickname, db.secdir))
     except ipautil.CalledProcessError:
-        syslog.syslog(syslog.LOG_ERR, 'Updating trust on certificate %s failed in %s' % (nickname, db.secdir))
-
-update_cert_config(nickname, cert)
-
-syslog.syslog(
-    syslog.LOG_NOTICE, 'certmonger restarted %sd instance %s to renew %s' %
-        (dogtag_instance, dogtag_instance, nickname))
+         syslog.syslog(syslog.LOG_ERR, 'Updating trust on certificate %s failed in %s' % (nickname, db.secdir))
 
-# We monitor 3 certs that are all likely to be renewed by certmonger more or
-# less at the same time. Each cert renewal is going to need to restart
-# the CA. Add a bit of randomness in this so not all three try to start it
-# at the same time. A restart is needed for each because there is no guarantee
-# that they will all be renewed at the same time.
-pause = random.randint(10,360)
-syslog.syslog(syslog.LOG_NOTICE, 'Pausing %d seconds to restart pki-ca' % pause)
-time.sleep(pause)
+# Now we can start the CA. Using the ipaservices start should fire
+# off the servlet to verify that the CA is actually up and responding so
+# when this returns it should be good-to-go. The CA was stopped in the
+# pre-save state.
+syslog.syslog(syslog.LOG_NOTICE, 'Starting %sd' % dogtag_instance)
 try:
-    ipaservices.knownservices.pki_cad.restart(dogtag_instance)
+    if configured_constants.DOGTAG_VERSION == 9:
+        ipaservices.knownservices.pki_cad.start(dogtag_instance)
+    else:
+        ipaservices.knownservices.pki_tomcatd.start(dogtag_instance)
 except Exception, e:
-    syslog.syslog(syslog.LOG_ERR, "Cannot restart %sd: %s" %
+    syslog.syslog(syslog.LOG_ERR, "Cannot start %sd: %s" %
                   (dogtag_instance, str(e)))
diff --git a/install/restart_scripts/renew_ra_cert b/install/restart_scripts/renew_ra_cert
index 1f359062b49eed400aaa7b6aeea4742253707b00..a70ba5c1a842f5724ea655d579f5a71f69d0ea21 100644
--- a/install/restart_scripts/renew_ra_cert
+++ b/install/restart_scripts/renew_ra_cert
@@ -25,13 +25,11 @@ import tempfile
 import syslog
 import time
 from ipapython import services as ipaservices
-from ipapython.certmonger import get_pin
 from ipapython import ipautil
 from ipaserver.install import certs
-from ipaserver.install.cainstance import DEFAULT_DSPORT
+from ipaserver.install.cainstance import update_people_entry
 from ipalib import api
 from ipapython.dn import DN
-from ipalib import x509
 from ipalib import errors
 from ipaserver.plugins.ldap2 import ldap2
 import ldap as _ldap
@@ -41,52 +39,10 @@ api.finalize()
 
 # Fetch the new certificate
 db = certs.CertDB(api.env.realm)
-cert = db.get_cert_from_db('ipaCert', pem=False)
-serial_number = x509.get_serial_number(cert, datatype=x509.DER)
-subject = x509.get_subject(cert, datatype=x509.DER)
-issuer = x509.get_issuer(cert, datatype=x509.DER)
+dercert = db.get_cert_from_db('ipaCert', pem=False)
 
 # Load it into dogtag
-dn = DN(('uid','ipara'),('ou','People'),('o','ipaca'))
-
-try:
-    dm_password = get_pin('internaldb')
-except IOError, e:
-    syslog.syslog(syslog.LOG_ERR, 'Unable to determine PIN for CA instance: %s' % e)
-    sys.exit(1)
-
-attempts = 0
-dogtag_uri='ldap://localhost:%d' % DEFAULT_DSPORT
-updated = False
-
-while attempts < 10:
-    conn = None
-    try:
-        conn = ldap2(shared_instance=False, ldap_uri=dogtag_uri)
-        conn.connect(bind_dn=DN(('cn', 'directory manager')), bind_pw=dm_password)
-        (entry_dn, entry_attrs) = conn.get_entry(dn, ['usercertificate'], normalize=False)
-        entry_attrs['usercertificate'].append(cert)
-        entry_attrs['description'] = '2;%d;%s;%s' % (serial_number, issuer, subject)
-        conn.update_entry(dn, entry_attrs, normalize=False)
-        updated = True
-        break
-    except errors.NetworkError:
-        syslog.syslog(syslog.LOG_ERR, 'Connection to %s failed, sleeping 30s' % dogtag_uri)
-        time.sleep(30)
-        attempts += 1
-    except errors.EmptyModlist:
-        updated = True
-        break
-    except Exception, e:
-        syslog.syslog(syslog.LOG_ERR, 'Updating agent entry failed: %s' % e)
-        break
-    finally:
-        if conn.isconnected():
-            conn.disconnect()
-
-if not updated:
-    syslog.syslog(syslog.LOG_ERR, '%s: Giving up. This script may be safely re-executed.' % sys.argv[0])
-    sys.exit(1)
+update_people_entry('ipara', dercert)
 
 attempts = 0
 updated = False
@@ -104,11 +60,11 @@ while attempts < 10:
         conn.connect(ccache=ccache)
         try:
             (entry_dn, entry_attrs) = conn.get_entry(dn, ['usercertificate'])
-            entry_attrs['usercertificate'] = cert
+            entry_attrs['usercertificate'] = dercert
             conn.update_entry(dn, entry_attrs, normalize=False)
         except errors.NotFound:
             entry_attrs = dict(objectclass=['top', 'pkiuser', 'nscontainer'],
-                                            usercertificate=cert)
+                                            usercertificate=dercert)
             conn.add_entry(dn, entry_attrs, normalize=False)
         except errors.EmptyModlist:
             pass
diff --git a/install/restart_scripts/restart_pkicad b/install/restart_scripts/restart_pkicad
index 0b6040a9d1f48d3dfcf41149cf3251c53bf2a612..62d9b5a1890c8abfa2940df2c1f1ec9d08712e75 100644
--- a/install/restart_scripts/restart_pkicad
+++ b/install/restart_scripts/restart_pkicad
@@ -35,8 +35,16 @@ configured_constants = dogtag.configured_constants(api)
 alias_dir = configured_constants.ALIAS_DIR
 dogtag_instance = configured_constants.PKI_INSTANCE_NAME
 
-syslog.syslog(syslog.LOG_NOTICE, "certmonger restarted %sd, nickname '%s'" %
-              (dogtag_instance, nickname))
+# dogtag opens its NSS database in read/write mode so we need it
+# shut down so certmonger can open it read/write mode. This avoids
+# database corruption. It should already be stopped by the pre-command
+# but lets be sure.
+if ipaservices.knownservices.pki_cad.is_running(dogtag_instance):
+    try:
+        ipaservices.knownservices.pki_cad.stop(dogtag_instance)
+    except Exception, e:
+        syslog.syslog(syslog.LOG_ERR, "Cannot stop %sd: %s" %
+                      (dogtag_instance, str(e)))
 
 # Fix permissions on the audit cert if we're updating it
 if nickname == 'auditSigningCert cert-pki-ca':
@@ -48,10 +56,10 @@ if nickname == 'auditSigningCert cert-pki-ca':
     db.run_certutil(args)
 
 try:
-    # I've seen times where systemd restart does not actually restart
-    # the process. A full stop/start is required. This works around that
-    ipaservices.knownservices.pki_cad.stop(dogtag_instance)
     ipaservices.knownservices.pki_cad.start(dogtag_instance)
 except Exception, e:
-    syslog.syslog(syslog.LOG_ERR, "Cannot restart %sd: %s" %
+    syslog.syslog(syslog.LOG_ERR, "Cannot start %sd: %s" %
                   (dogtag_instance, str(e)))
+else:
+    syslog.syslog(syslog.LOG_NOTICE, "certmonger started %sd, nickname '%s'" %
+                  (dogtag_instance, nickname))
diff --git a/install/restart_scripts/stop_pkicad b/install/restart_scripts/stop_pkicad
new file mode 100644
index 0000000000000000000000000000000000000000..53a4d768317cda4944cf091c7dd7a179e85dcf61
--- /dev/null
+++ b/install/restart_scripts/stop_pkicad
@@ -0,0 +1,40 @@
+#!/usr/bin/python -E
+#
+# Authors:
+#   Rob Crittenden <rcrit...@redhat.com>
+#
+# Copyright (C) 2012  Red Hat
+# see file 'COPYING' for use and warranty information
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+import sys
+import syslog
+from ipapython import services as ipaservices
+from ipapython import dogtag
+from ipalib import api
+
+api.bootstrap(context='restart')
+api.finalize()
+
+configured_constants = dogtag.configured_constants(api)
+dogtag_instance = configured_constants.PKI_INSTANCE_NAME
+
+syslog.syslog(syslog.LOG_NOTICE, "certmonger stopped %sd" % dogtag_instance)
+
+try:
+    ipaservices.knownservices.pki_cad.stop(dogtag_instance)
+except Exception, e:
+    syslog.syslog(syslog.LOG_ERR, "Cannot stop %sd: %s" %
+                  (dogtag_instance, str(e)))
diff --git a/ipapython/certmonger.py b/ipapython/certmonger.py
index f29050ea96318a9564358f42405fc8849a2e27b7..a0adbd7941a68a624c90a0821897cb03145ac82c 100644
--- a/ipapython/certmonger.py
+++ b/ipapython/certmonger.py
@@ -368,7 +368,8 @@ def get_pin(token, dogtag_constants=None):
                 return pin.strip()
     return None
 
-def dogtag_start_tracking(ca, nickname, pin, pinfile, secdir, command):
+def dogtag_start_tracking(ca, nickname, pin, pinfile, secdir, pre_command,
+                          post_command):
     """
     Tell certmonger to start tracking a dogtag CA certificate. These
     are handled differently because their renewal must be done directly
@@ -377,7 +378,10 @@ def dogtag_start_tracking(ca, nickname, pin, pinfile, secdir, command):
     This uses the generic certmonger command getcert so we can specify
     a different helper.
 
-    command is the script to execute.
+    pre_command is the script to execute before a renewal is done.
+    post_command is the script to execute after a renewal is done.
+
+    Both commands can be None.
 
     Returns the stdout, stderr and returncode from running ipa-getcert
 
@@ -386,20 +390,32 @@ def dogtag_start_tracking(ca, nickname, pin, pinfile, secdir, command):
     if not cert_exists(nickname, os.path.abspath(secdir)):
         raise RuntimeError('Nickname "%s" doesn\'t exist in NSS database "%s"' % (nickname, secdir))
 
-    if command is not None and not os.path.isabs(command):
-        if sys.maxsize > 2**32:
-            libpath = 'lib64'
-        else:
-            libpath = 'lib'
-        command = '/usr/%s/ipa/certmonger/%s' % (libpath, command)
-
     args = ["/usr/bin/getcert", "start-tracking",
             "-d", os.path.abspath(secdir),
             "-n", nickname,
             "-c", ca,
-            "-C", command,
            ]
 
+    if pre_command is not None:
+        if not os.path.isabs(pre_command):
+            if sys.maxsize > 2**32:
+                libpath = 'lib64'
+            else:
+                libpath = 'lib'
+            pre_command = '/usr/%s/ipa/certmonger/%s' % (libpath, pre_command)
+        args.append("-B")
+        args.append(pre_command)
+
+    if post_command is not None:
+        if not os.path.isabs(post_command):
+            if sys.maxsize > 2**32:
+                libpath = 'lib64'
+            else:
+                libpath = 'lib'
+            post_command = '/usr/%s/ipa/certmonger/%s' % (libpath, post_command)
+        args.append("-C")
+        args.append(post_command)
+
     if pinfile:
         args.append("-p")
         args.append(pinfile)
diff --git a/ipaserver/install/cainstance.py b/ipaserver/install/cainstance.py
index 86c075c803e0a1e2a170c15c1b81a2456063704b..c6515e3f848aaef9cc8447595bdd0253bb1d1446 100644
--- a/ipaserver/install/cainstance.py
+++ b/ipaserver/install/cainstance.py
@@ -35,11 +35,13 @@ import urllib
 import xml.dom.minidom
 import stat
 import socket
+import syslog
 import ConfigParser
 from ipapython import dogtag
 from ipapython.certdb import get_ca_nickname
 from ipapython import certmonger
 from ipalib import pkcs10, x509
+from ipalib import errors
 from ipapython.dn import DN
 import subprocess
 import traceback
@@ -1048,7 +1050,7 @@ class CAInstance(service.Service):
 
         On upgrades this needs to be called from ipa-upgradeconfig.
         """
-        certmonger.dogtag_start_tracking('dogtag-ipa-retrieve-agent-submit', 'ipaCert', None, '/etc/httpd/alias/pwdfile.txt', '/etc/httpd/alias', 'restart_httpd')
+        certmonger.dogtag_start_tracking('dogtag-ipa-retrieve-agent-submit', 'ipaCert', None, '/etc/httpd/alias/pwdfile.txt', '/etc/httpd/alias', None, 'restart_httpd')
 
     def __configure_ra(self):
         # Create an RA user in the CA LDAP server and add that user to
@@ -1534,11 +1536,15 @@ class CAInstance(service.Service):
                 'Unable to determine PIN for CA instance: %s' % str(e))
 
     def track_servercert(self):
+        """
+        Specifically do not tell certmonger to restart the CA. This will be
+        done by the renewal script, renew_ca_cert once all the subsystem
+        certificates are renewed.
+        """
         pin = self.__get_ca_pin()
         certmonger.dogtag_start_tracking(
             'dogtag-ipa-renew-agent', 'Server-Cert cert-pki-ca', pin, None,
-            self.dogtag_constants.ALIAS_DIR,
-            'restart_pkicad "Server-Cert cert-pki-ca"')
+            self.dogtag_constants.ALIAS_DIR, None, None)
 
     def configure_renewal(self):
         cmonger = ipaservices.knownservices.certmonger
@@ -1554,10 +1560,10 @@ class CAInstance(service.Service):
                          'subsystemCert cert-pki-ca']:
             certmonger.dogtag_start_tracking(
                 'dogtag-ipa-renew-agent', nickname, pin, None,
-                self.dogtag_constants.ALIAS_DIR, 'renew_ca_cert "%s"' % nickname)
+                self.dogtag_constants.ALIAS_DIR, 'stop_pkicad', 'renew_ca_cert "%s"' % nickname)
 
         # Set up the agent cert for renewal
-        certmonger.dogtag_start_tracking('dogtag-ipa-renew-agent', 'ipaCert', None, '/etc/httpd/alias/pwdfile.txt', '/etc/httpd/alias', 'renew_ra_cert')
+        certmonger.dogtag_start_tracking('dogtag-ipa-renew-agent', 'ipaCert', None, '/etc/httpd/alias/pwdfile.txt', '/etc/httpd/alias', None, 'renew_ra_cert')
 
     def configure_certmonger_renewal(self):
         """
@@ -1597,7 +1603,7 @@ class CAInstance(service.Service):
                          'subsystemCert cert-pki-ca']:
             certmonger.dogtag_start_tracking(
                 'dogtag-ipa-retrieve-agent-submit', nickname, pin, None,
-                self.dogtag_constants.ALIAS_DIR,
+                self.dogtag_constants.ALIAS_DIR, 'stop_pkicad',
                 'restart_pkicad "%s"' % nickname)
 
         # The agent renewal is configured in import_ra_cert which is called
@@ -1861,6 +1867,67 @@ def update_cert_config(nickname, cert):
                                 base64.b64encode(cert),
                                 quotes=False, separator='=')
 
+def update_people_entry(uid, dercert):
+    """
+    Update the userCerticate for an entry in the dogtag ou=People. This
+    is needed when a certificate is renewed.
+
+    uid: uid of user to update
+    dercert: An X509.3 certificate in DER format
+
+    Logging is done via syslog
+
+    Returns True or False
+    """
+    dn = DN(('uid',uid),('ou','People'),('o','ipaca'))
+    serial_number = x509.get_serial_number(dercert, datatype=x509.DER)
+    subject = x509.get_subject(dercert, datatype=x509.DER)
+    issuer = x509.get_issuer(dercert, datatype=x509.DER)
+
+    attempts = 0
+    dogtag_uri='ldap://localhost:%d' % DEFAULT_DSPORT
+    updated = False
+
+    try:
+        dm_password = certmonger.get_pin('internaldb')
+    except IOError, e:
+        syslog.syslog(syslog.LOG_ERR, 'Unable to determine PIN for CA instance: %s' % e)
+        return False
+
+    while attempts < 10:
+        conn = None
+        try:
+            conn = ldap2.ldap2(shared_instance=False, ldap_uri=dogtag_uri)
+            conn.connect(bind_dn=DN(('cn', 'directory manager')),
+                bind_pw=dm_password)
+            (entry_dn, entry_attrs) = conn.get_entry(dn, ['usercertificate'],
+                normalize=False)
+            entry_attrs['usercertificate'].append(dercert)
+            entry_attrs['description'] = '2;%d;%s;%s' % (serial_number, issuer,
+                subject)
+            conn.update_entry(dn, entry_attrs, normalize=False)
+            updated = True
+            break
+        except errors.NetworkError:
+            syslog.syslog(syslog.LOG_ERR, 'Connection to %s failed, sleeping 30s' % dogtag_uri)
+            time.sleep(30)
+            attempts += 1
+        except errors.EmptyModlist:
+            updated = True
+            break
+        except Exception, e:
+            syslog.syslog(syslog.LOG_ERR, 'Updating %s entry failed: %s' % (str(dn), e))
+            break
+        finally:
+            if conn.isconnected():
+                conn.disconnect()
+
+    if not updated:
+        syslog.syslog(syslog.LOG_ERR, 'Update failed.')
+        return False
+
+    return True
+
 if __name__ == "__main__":
     standard_logging_setup("install.log")
     if not dogtag.install_constants.SHARED_DB:
-- 
1.8.0.2

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

Reply via email to