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
This commit is contained in:
Rob Crittenden
2014-12-02 13:18:36 -05:00
parent b382a77fc3
commit 045b6e6ed9
9 changed files with 274 additions and 100 deletions

View File

@@ -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: