From 045b6e6ed995b4c1e5dab8dbcdf1af4896b52d19 Mon Sep 17 00:00:00 2001 From: Rob Crittenden Date: Tue, 2 Dec 2014 13:18:36 -0500 Subject: 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 --- ipaserver/install/cainstance.py | 113 +++++++++++++++++++++++++++++++++++----- 1 file changed, 100 insertions(+), 13 deletions(-) (limited to 'ipaserver/install/cainstance.py') diff --git a/ipaserver/install/cainstance.py b/ipaserver/install/cainstance.py index 3d028a6a..a5cfc6fb 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,11 @@ 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') + try: + certmonger.dogtag_start_tracking('dogtag-ipa-retrieve-agent-submit', 'ipaCert', None, '/etc/httpd/alias/pwdfile.txt', '/etc/httpd/alias', None, 'restart_httpd') + except (ipautil.CalledProcessError, RuntimeError), e: + root_logger.error( + "certmonger failed to start tracking certificate: %s" % str(e)) def __configure_ra(self): # Create an RA user in the CA LDAP server and add that user to @@ -1534,11 +1540,19 @@ 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"') + try: + certmonger.dogtag_start_tracking( + 'dogtag-ipa-renew-agent', 'Server-Cert cert-pki-ca', pin, None, + self.dogtag_constants.ALIAS_DIR, None, None) + except (ipautil.CalledProcessError, RuntimeError), e: + root_logger.error( + "certmonger failed to start tracking certificate: %s" % str(e)) def configure_renewal(self): cmonger = ipaservices.knownservices.certmonger @@ -1552,12 +1566,20 @@ class CAInstance(service.Service): for nickname in ['auditSigningCert cert-pki-ca', 'ocspSigningCert cert-pki-ca', '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) + try: + certmonger.dogtag_start_tracking( + 'dogtag-ipa-renew-agent', nickname, pin, None, + self.dogtag_constants.ALIAS_DIR, 'stop_pkicad', 'renew_ca_cert "%s"' % nickname) + except (ipautil.CalledProcessError, RuntimeError), e: + root_logger.error( + "certmonger failed to start tracking certificate: %s" % str(e)) # 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') + try: + certmonger.dogtag_start_tracking('dogtag-ipa-renew-agent', 'ipaCert', None, '/etc/httpd/alias/pwdfile.txt', '/etc/httpd/alias', None, 'renew_ra_cert') + except (ipautil.CalledProcessError, RuntimeError), e: + root_logger.error( + "certmonger failed to start tracking certificate: %s" % str(e)) def configure_certmonger_renewal(self): """ @@ -1595,10 +1617,14 @@ class CAInstance(service.Service): for nickname in ['auditSigningCert cert-pki-ca', 'ocspSigningCert cert-pki-ca', 'subsystemCert cert-pki-ca']: - certmonger.dogtag_start_tracking( - 'dogtag-ipa-retrieve-agent-submit', nickname, pin, None, - self.dogtag_constants.ALIAS_DIR, - 'restart_pkicad "%s"' % nickname) + try: + certmonger.dogtag_start_tracking( + 'dogtag-ipa-retrieve-agent-submit', nickname, pin, None, + self.dogtag_constants.ALIAS_DIR, 'stop_pkicad', + 'restart_pkicad "%s"' % nickname) + except (ipautil.CalledProcessError, RuntimeError), e: + root_logger.error( + "certmonger failed to start tracking certificate: %s" % str(e)) # The agent renewal is configured in import_ra_cert which is called # after the HTTP instance is created. @@ -1861,6 +1887,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: -- cgit