Blob Blame History Raw
From 854ceb13a72630ba357ca5c1ec8ac5b320a4c9c5 Mon Sep 17 00:00:00 2001
From: Jan Cholasta <jcholast@redhat.com>
Date: Wed, 19 Apr 2017 12:55:47 +0000
Subject: [PATCH] renew agent: allow reusing existing certs

Add a switch which makes `dogtag-ipa-ca-renew-agent-submit` reuse the
existing certificate rather than request a new one from the CA while
maintaining LDAP replication of the certificate.

Make this available as a new `dogtag-ipa-ca-renew-agent-reuse` certmonger
CA.

This allows redoing the LDAP replication and reexecuting pre- and post-save
commands of a tracking request without reissuing the certificate.

https://pagure.io/freeipa/issue/5799

Reviewed-By: David Kupka <dkupka@redhat.com>
Reviewed-By: Stanislav Laznicka <slaznick@redhat.com>
---
 .../certmonger/dogtag-ipa-ca-renew-agent-submit    | 67 ++++++++++++++++------
 ipaserver/install/cainstance.py                    |  8 ++-
 ipaserver/install/dogtaginstance.py                | 15 +++--
 3 files changed, 63 insertions(+), 27 deletions(-)

diff --git a/install/certmonger/dogtag-ipa-ca-renew-agent-submit b/install/certmonger/dogtag-ipa-ca-renew-agent-submit
index 51b0880c5b57758845e2ffa0c9545bbca7e8c751..7b5489555d069856a6da7a21b5ab2b0f4dd4a41c 100755
--- a/install/certmonger/dogtag-ipa-ca-renew-agent-submit
+++ b/install/certmonger/dogtag-ipa-ca-renew-agent-submit
@@ -193,10 +193,18 @@ def call_handler(_handler, *args, **kwargs):
 
     return result
 
-def request_cert():
+
+def request_cert(reuse_existing, **kwargs):
     """
     Request certificate from IPA CA.
     """
+    if reuse_existing:
+        cert = os.environ.get('CERTMONGER_CERTIFICATE')
+        if cert:
+            return (ISSUED, cert)
+        else:
+            return (REJECTED, "New certificate requests not supported")
+
     syslog.syslog(syslog.LOG_NOTICE,
                   "Forwarding request to dogtag-ipa-renew-agent")
 
@@ -231,7 +239,8 @@ def request_cert():
     else:
         return (rc, stdout)
 
-def store_cert():
+
+def store_cert(**kwargs):
     """
     Store certificate in LDAP.
     """
@@ -292,7 +301,8 @@ def store_cert():
 
     return (ISSUED, cert)
 
-def request_and_store_cert():
+
+def request_and_store_cert(**kwargs):
     """
     Request certificate from IPA CA and store it in LDAP.
     """
@@ -318,7 +328,7 @@ def request_and_store_cert():
         else:
             os.environ['CERTMONGER_CA_COOKIE'] = cookie
 
-        result = call_handler(request_cert)
+        result = call_handler(request_cert, **kwargs)
         if result[0] == WAIT:
             return (result[0], 'request:%s' % result[1])
         elif result[0] == WAIT_WITH_DELAY:
@@ -337,7 +347,7 @@ def request_and_store_cert():
         os.environ['CERTMONGER_CA_COOKIE'] = cookie
     os.environ['CERTMONGER_CERTIFICATE'] = cert
 
-    result = call_handler(store_cert)
+    result = call_handler(store_cert, **kwargs)
     if result[0] == WAIT:
         return (result[0], 'store:%s:%s' % (cert, result[1]))
     elif result[0] == WAIT_WITH_DELAY:
@@ -345,7 +355,8 @@ def request_and_store_cert():
     else:
         return result
 
-def retrieve_or_reuse_cert():
+
+def retrieve_or_reuse_cert(**kwargs):
     """
     Retrieve certificate from LDAP. If the certificate is not available, reuse
     the old certificate.
@@ -373,7 +384,8 @@ def retrieve_or_reuse_cert():
 
     return (ISSUED, cert)
 
-def retrieve_cert_continuous():
+
+def retrieve_cert_continuous(reuse_existing, **kwargs):
     """
     Retrieve new certificate from LDAP. Repeat every eight hours until the
     certificate is available.
@@ -382,8 +394,10 @@ def retrieve_cert_continuous():
     if old_cert:
         old_cert = x509.normalize_certificate(old_cert)
 
-    result = call_handler(retrieve_or_reuse_cert)
-    if result[0] != ISSUED:
+    result = call_handler(retrieve_or_reuse_cert,
+                          reuse_existing=reuse_existing,
+                          **kwargs)
+    if result[0] != ISSUED or reuse_existing:
         return result
 
     new_cert = x509.normalize_certificate(result[1])
@@ -394,17 +408,19 @@ def retrieve_cert_continuous():
 
     return result
 
-def retrieve_cert():
+
+def retrieve_cert(**kwargs):
     """
     Retrieve new certificate from LDAP.
     """
-    result = call_handler(retrieve_cert_continuous)
+    result = call_handler(retrieve_cert_continuous, **kwargs)
     if result[0] == WAIT_WITH_DELAY:
         return (REJECTED, "Updated certificate not available")
 
     return result
 
-def export_csr():
+
+def export_csr(**kwargs):
     """
     This does not actually renew the cert, it just writes the CSR provided
     by certmonger to /var/lib/ipa/ca.csr and returns the existing cert.
@@ -430,7 +446,8 @@ def export_csr():
 
     return (ISSUED, cert)
 
-def renew_ca_cert():
+
+def renew_ca_cert(reuse_existing, **kwargs):
     """
     This is used for automatic CA certificate renewal.
     """
@@ -443,7 +460,7 @@ def renew_ca_cert():
     if operation == 'SUBMIT':
         state = 'retrieve'
 
-        if is_self_signed and is_renewal_master():
+        if is_self_signed and not reuse_existing and is_renewal_master():
             state = 'request'
     elif operation == 'POLL':
         cookie = os.environ.get('CERTMONGER_CA_COOKIE')
@@ -460,8 +477,10 @@ def renew_ca_cert():
         return (OPERATION_NOT_SUPPORTED_BY_HELPER,)
 
     if state == 'retrieve':
-        result = call_handler(retrieve_cert)
-        if result[0] == REJECTED and not is_self_signed:
+        result = call_handler(retrieve_cert,
+                              reuse_existing=reuse_existing,
+                              **kwargs)
+        if result[0] == REJECTED and not is_self_signed and not reuse_existing:
             syslog.syslog(syslog.LOG_ALERT,
                           "Certificate with subject '%s' is about to expire, "
                           "use ipa-cacert-manage to renew it"
@@ -469,7 +488,9 @@ def renew_ca_cert():
     elif state == 'request':
         profile = os.environ['CERTMONGER_CA_PROFILE']
         os.environ['CERTMONGER_CA_PROFILE'] = 'caCACert'
-        result = call_handler(request_and_store_cert)
+        result = call_handler(request_and_store_cert,
+                              reuse_existing=reuse_existing,
+                              **kwargs)
         os.environ['CERTMONGER_CA_PROFILE'] = profile
 
     if result[0] == WAIT:
@@ -480,6 +501,16 @@ def renew_ca_cert():
         return result
 
 def main():
+    kwargs = {
+        'reuse_existing': False,
+    }
+    try:
+        sys.argv.remove('--reuse-existing')
+    except ValueError:
+        pass
+    else:
+        kwargs['reuse_existing'] = True
+
     handlers = {
         'ipaStorage':           store_cert,
         'ipaRetrievalOrReuse':  retrieve_or_reuse_cert,
@@ -515,7 +546,7 @@ def main():
             handler = request_cert
         handler = handlers.get(profile, handler)
 
-        res = call_handler(handler)
+        res = call_handler(handler, **kwargs)
         for item in res[1:]:
             print(item)
         return res[0]
diff --git a/ipaserver/install/cainstance.py b/ipaserver/install/cainstance.py
index 546e1b7b39b323bbaeae3fb57d31ea4152d5e418..ff5432d1a7460f1b853c7cf7490b3604c82cd1f7 100644
--- a/ipaserver/install/cainstance.py
+++ b/ipaserver/install/cainstance.py
@@ -964,9 +964,11 @@ class CAInstance(DogtagInstance):
         obj = bus.get_object('org.fedorahosted.certmonger',
                              '/org/fedorahosted/certmonger')
         iface = dbus.Interface(obj, 'org.fedorahosted.certmonger')
-        path = iface.find_ca_by_nickname('dogtag-ipa-ca-renew-agent')
-        if path:
-            iface.remove_known_ca(path)
+        for suffix in ['', '-reuse']:
+            name = 'dogtag-ipa-ca-renew-agent' + suffix
+            path = iface.find_ca_by_nickname(name)
+            if path:
+                iface.remove_known_ca(path)
 
         cmonger.stop()
 
diff --git a/ipaserver/install/dogtaginstance.py b/ipaserver/install/dogtaginstance.py
index 356358adf1b60e236ce821fb44a77ca5f8c1942f..e0515973f7a598b30c6f12675b9ebdbfd0cf3423 100644
--- a/ipaserver/install/dogtaginstance.py
+++ b/ipaserver/install/dogtaginstance.py
@@ -265,12 +265,15 @@ class DogtagInstance(service.Service):
         obj = bus.get_object('org.fedorahosted.certmonger',
                              '/org/fedorahosted/certmonger')
         iface = dbus.Interface(obj, 'org.fedorahosted.certmonger')
-        path = iface.find_ca_by_nickname('dogtag-ipa-ca-renew-agent')
-        if not path:
-            iface.add_known_ca(
-                'dogtag-ipa-ca-renew-agent',
-                paths.DOGTAG_IPA_CA_RENEW_AGENT_SUBMIT,
-                dbus.Array([], dbus.Signature('s')))
+        for suffix, args in [('', ''), ('-reuse', ' --reuse-existing')]:
+            name = 'dogtag-ipa-ca-renew-agent' + suffix
+            path = iface.find_ca_by_nickname(name)
+            if not path:
+                command = paths.DOGTAG_IPA_CA_RENEW_AGENT_SUBMIT + args
+                iface.add_known_ca(
+                    name,
+                    command,
+                    dbus.Array([], dbus.Signature('s')))
 
     def __get_pin(self):
         try:
-- 
2.9.3