ac7d03
From 854ceb13a72630ba357ca5c1ec8ac5b320a4c9c5 Mon Sep 17 00:00:00 2001
ac7d03
From: Jan Cholasta <jcholast@redhat.com>
ac7d03
Date: Wed, 19 Apr 2017 12:55:47 +0000
ac7d03
Subject: [PATCH] renew agent: allow reusing existing certs
ac7d03
ac7d03
Add a switch which makes `dogtag-ipa-ca-renew-agent-submit` reuse the
ac7d03
existing certificate rather than request a new one from the CA while
ac7d03
maintaining LDAP replication of the certificate.
ac7d03
ac7d03
Make this available as a new `dogtag-ipa-ca-renew-agent-reuse` certmonger
ac7d03
CA.
ac7d03
ac7d03
This allows redoing the LDAP replication and reexecuting pre- and post-save
ac7d03
commands of a tracking request without reissuing the certificate.
ac7d03
ac7d03
https://pagure.io/freeipa/issue/5799
ac7d03
ac7d03
Reviewed-By: David Kupka <dkupka@redhat.com>
ac7d03
Reviewed-By: Stanislav Laznicka <slaznick@redhat.com>
ac7d03
---
ac7d03
 .../certmonger/dogtag-ipa-ca-renew-agent-submit    | 67 ++++++++++++++++------
ac7d03
 ipaserver/install/cainstance.py                    |  8 ++-
ac7d03
 ipaserver/install/dogtaginstance.py                | 15 +++--
ac7d03
 3 files changed, 63 insertions(+), 27 deletions(-)
ac7d03
ac7d03
diff --git a/install/certmonger/dogtag-ipa-ca-renew-agent-submit b/install/certmonger/dogtag-ipa-ca-renew-agent-submit
ac7d03
index 51b0880c5b57758845e2ffa0c9545bbca7e8c751..7b5489555d069856a6da7a21b5ab2b0f4dd4a41c 100755
ac7d03
--- a/install/certmonger/dogtag-ipa-ca-renew-agent-submit
ac7d03
+++ b/install/certmonger/dogtag-ipa-ca-renew-agent-submit
ac7d03
@@ -193,10 +193,18 @@ def call_handler(_handler, *args, **kwargs):
ac7d03
 
ac7d03
     return result
ac7d03
 
ac7d03
-def request_cert():
ac7d03
+
ac7d03
+def request_cert(reuse_existing, **kwargs):
ac7d03
     """
ac7d03
     Request certificate from IPA CA.
ac7d03
     """
ac7d03
+    if reuse_existing:
ac7d03
+        cert = os.environ.get('CERTMONGER_CERTIFICATE')
ac7d03
+        if cert:
ac7d03
+            return (ISSUED, cert)
ac7d03
+        else:
ac7d03
+            return (REJECTED, "New certificate requests not supported")
ac7d03
+
ac7d03
     syslog.syslog(syslog.LOG_NOTICE,
ac7d03
                   "Forwarding request to dogtag-ipa-renew-agent")
ac7d03
 
ac7d03
@@ -231,7 +239,8 @@ def request_cert():
ac7d03
     else:
ac7d03
         return (rc, stdout)
ac7d03
 
ac7d03
-def store_cert():
ac7d03
+
ac7d03
+def store_cert(**kwargs):
ac7d03
     """
ac7d03
     Store certificate in LDAP.
ac7d03
     """
ac7d03
@@ -292,7 +301,8 @@ def store_cert():
ac7d03
 
ac7d03
     return (ISSUED, cert)
ac7d03
 
ac7d03
-def request_and_store_cert():
ac7d03
+
ac7d03
+def request_and_store_cert(**kwargs):
ac7d03
     """
ac7d03
     Request certificate from IPA CA and store it in LDAP.
ac7d03
     """
ac7d03
@@ -318,7 +328,7 @@ def request_and_store_cert():
ac7d03
         else:
ac7d03
             os.environ['CERTMONGER_CA_COOKIE'] = cookie
ac7d03
 
ac7d03
-        result = call_handler(request_cert)
ac7d03
+        result = call_handler(request_cert, **kwargs)
ac7d03
         if result[0] == WAIT:
ac7d03
             return (result[0], 'request:%s' % result[1])
ac7d03
         elif result[0] == WAIT_WITH_DELAY:
ac7d03
@@ -337,7 +347,7 @@ def request_and_store_cert():
ac7d03
         os.environ['CERTMONGER_CA_COOKIE'] = cookie
ac7d03
     os.environ['CERTMONGER_CERTIFICATE'] = cert
ac7d03
 
ac7d03
-    result = call_handler(store_cert)
ac7d03
+    result = call_handler(store_cert, **kwargs)
ac7d03
     if result[0] == WAIT:
ac7d03
         return (result[0], 'store:%s:%s' % (cert, result[1]))
ac7d03
     elif result[0] == WAIT_WITH_DELAY:
ac7d03
@@ -345,7 +355,8 @@ def request_and_store_cert():
ac7d03
     else:
ac7d03
         return result
ac7d03
 
ac7d03
-def retrieve_or_reuse_cert():
ac7d03
+
ac7d03
+def retrieve_or_reuse_cert(**kwargs):
ac7d03
     """
ac7d03
     Retrieve certificate from LDAP. If the certificate is not available, reuse
ac7d03
     the old certificate.
ac7d03
@@ -373,7 +384,8 @@ def retrieve_or_reuse_cert():
ac7d03
 
ac7d03
     return (ISSUED, cert)
ac7d03
 
ac7d03
-def retrieve_cert_continuous():
ac7d03
+
ac7d03
+def retrieve_cert_continuous(reuse_existing, **kwargs):
ac7d03
     """
ac7d03
     Retrieve new certificate from LDAP. Repeat every eight hours until the
ac7d03
     certificate is available.
ac7d03
@@ -382,8 +394,10 @@ def retrieve_cert_continuous():
ac7d03
     if old_cert:
ac7d03
         old_cert = x509.normalize_certificate(old_cert)
ac7d03
 
ac7d03
-    result = call_handler(retrieve_or_reuse_cert)
ac7d03
-    if result[0] != ISSUED:
ac7d03
+    result = call_handler(retrieve_or_reuse_cert,
ac7d03
+                          reuse_existing=reuse_existing,
ac7d03
+                          **kwargs)
ac7d03
+    if result[0] != ISSUED or reuse_existing:
ac7d03
         return result
ac7d03
 
ac7d03
     new_cert = x509.normalize_certificate(result[1])
ac7d03
@@ -394,17 +408,19 @@ def retrieve_cert_continuous():
ac7d03
 
ac7d03
     return result
ac7d03
 
ac7d03
-def retrieve_cert():
ac7d03
+
ac7d03
+def retrieve_cert(**kwargs):
ac7d03
     """
ac7d03
     Retrieve new certificate from LDAP.
ac7d03
     """
ac7d03
-    result = call_handler(retrieve_cert_continuous)
ac7d03
+    result = call_handler(retrieve_cert_continuous, **kwargs)
ac7d03
     if result[0] == WAIT_WITH_DELAY:
ac7d03
         return (REJECTED, "Updated certificate not available")
ac7d03
 
ac7d03
     return result
ac7d03
 
ac7d03
-def export_csr():
ac7d03
+
ac7d03
+def export_csr(**kwargs):
ac7d03
     """
ac7d03
     This does not actually renew the cert, it just writes the CSR provided
ac7d03
     by certmonger to /var/lib/ipa/ca.csr and returns the existing cert.
ac7d03
@@ -430,7 +446,8 @@ def export_csr():
ac7d03
 
ac7d03
     return (ISSUED, cert)
ac7d03
 
ac7d03
-def renew_ca_cert():
ac7d03
+
ac7d03
+def renew_ca_cert(reuse_existing, **kwargs):
ac7d03
     """
ac7d03
     This is used for automatic CA certificate renewal.
ac7d03
     """
ac7d03
@@ -443,7 +460,7 @@ def renew_ca_cert():
ac7d03
     if operation == 'SUBMIT':
ac7d03
         state = 'retrieve'
ac7d03
 
ac7d03
-        if is_self_signed and is_renewal_master():
ac7d03
+        if is_self_signed and not reuse_existing and is_renewal_master():
ac7d03
             state = 'request'
ac7d03
     elif operation == 'POLL':
ac7d03
         cookie = os.environ.get('CERTMONGER_CA_COOKIE')
ac7d03
@@ -460,8 +477,10 @@ def renew_ca_cert():
ac7d03
         return (OPERATION_NOT_SUPPORTED_BY_HELPER,)
ac7d03
 
ac7d03
     if state == 'retrieve':
ac7d03
-        result = call_handler(retrieve_cert)
ac7d03
-        if result[0] == REJECTED and not is_self_signed:
ac7d03
+        result = call_handler(retrieve_cert,
ac7d03
+                              reuse_existing=reuse_existing,
ac7d03
+                              **kwargs)
ac7d03
+        if result[0] == REJECTED and not is_self_signed and not reuse_existing:
ac7d03
             syslog.syslog(syslog.LOG_ALERT,
ac7d03
                           "Certificate with subject '%s' is about to expire, "
ac7d03
                           "use ipa-cacert-manage to renew it"
ac7d03
@@ -469,7 +488,9 @@ def renew_ca_cert():
ac7d03
     elif state == 'request':
ac7d03
         profile = os.environ['CERTMONGER_CA_PROFILE']
ac7d03
         os.environ['CERTMONGER_CA_PROFILE'] = 'caCACert'
ac7d03
-        result = call_handler(request_and_store_cert)
ac7d03
+        result = call_handler(request_and_store_cert,
ac7d03
+                              reuse_existing=reuse_existing,
ac7d03
+                              **kwargs)
ac7d03
         os.environ['CERTMONGER_CA_PROFILE'] = profile
ac7d03
 
ac7d03
     if result[0] == WAIT:
ac7d03
@@ -480,6 +501,16 @@ def renew_ca_cert():
ac7d03
         return result
ac7d03
 
ac7d03
 def main():
ac7d03
+    kwargs = {
ac7d03
+        'reuse_existing': False,
ac7d03
+    }
ac7d03
+    try:
ac7d03
+        sys.argv.remove('--reuse-existing')
ac7d03
+    except ValueError:
ac7d03
+        pass
ac7d03
+    else:
ac7d03
+        kwargs['reuse_existing'] = True
ac7d03
+
ac7d03
     handlers = {
ac7d03
         'ipaStorage':           store_cert,
ac7d03
         'ipaRetrievalOrReuse':  retrieve_or_reuse_cert,
ac7d03
@@ -515,7 +546,7 @@ def main():
ac7d03
             handler = request_cert
ac7d03
         handler = handlers.get(profile, handler)
ac7d03
 
ac7d03
-        res = call_handler(handler)
ac7d03
+        res = call_handler(handler, **kwargs)
ac7d03
         for item in res[1:]:
ac7d03
             print(item)
ac7d03
         return res[0]
ac7d03
diff --git a/ipaserver/install/cainstance.py b/ipaserver/install/cainstance.py
ac7d03
index 546e1b7b39b323bbaeae3fb57d31ea4152d5e418..ff5432d1a7460f1b853c7cf7490b3604c82cd1f7 100644
ac7d03
--- a/ipaserver/install/cainstance.py
ac7d03
+++ b/ipaserver/install/cainstance.py
ac7d03
@@ -964,9 +964,11 @@ class CAInstance(DogtagInstance):
ac7d03
         obj = bus.get_object('org.fedorahosted.certmonger',
ac7d03
                              '/org/fedorahosted/certmonger')
ac7d03
         iface = dbus.Interface(obj, 'org.fedorahosted.certmonger')
ac7d03
-        path = iface.find_ca_by_nickname('dogtag-ipa-ca-renew-agent')
ac7d03
-        if path:
ac7d03
-            iface.remove_known_ca(path)
ac7d03
+        for suffix in ['', '-reuse']:
ac7d03
+            name = 'dogtag-ipa-ca-renew-agent' + suffix
ac7d03
+            path = iface.find_ca_by_nickname(name)
ac7d03
+            if path:
ac7d03
+                iface.remove_known_ca(path)
ac7d03
 
ac7d03
         cmonger.stop()
ac7d03
 
ac7d03
diff --git a/ipaserver/install/dogtaginstance.py b/ipaserver/install/dogtaginstance.py
ac7d03
index 356358adf1b60e236ce821fb44a77ca5f8c1942f..e0515973f7a598b30c6f12675b9ebdbfd0cf3423 100644
ac7d03
--- a/ipaserver/install/dogtaginstance.py
ac7d03
+++ b/ipaserver/install/dogtaginstance.py
ac7d03
@@ -265,12 +265,15 @@ class DogtagInstance(service.Service):
ac7d03
         obj = bus.get_object('org.fedorahosted.certmonger',
ac7d03
                              '/org/fedorahosted/certmonger')
ac7d03
         iface = dbus.Interface(obj, 'org.fedorahosted.certmonger')
ac7d03
-        path = iface.find_ca_by_nickname('dogtag-ipa-ca-renew-agent')
ac7d03
-        if not path:
ac7d03
-            iface.add_known_ca(
ac7d03
-                'dogtag-ipa-ca-renew-agent',
ac7d03
-                paths.DOGTAG_IPA_CA_RENEW_AGENT_SUBMIT,
ac7d03
-                dbus.Array([], dbus.Signature('s')))
ac7d03
+        for suffix, args in [('', ''), ('-reuse', ' --reuse-existing')]:
ac7d03
+            name = 'dogtag-ipa-ca-renew-agent' + suffix
ac7d03
+            path = iface.find_ca_by_nickname(name)
ac7d03
+            if not path:
ac7d03
+                command = paths.DOGTAG_IPA_CA_RENEW_AGENT_SUBMIT + args
ac7d03
+                iface.add_known_ca(
ac7d03
+                    name,
ac7d03
+                    command,
ac7d03
+                    dbus.Array([], dbus.Signature('s')))
ac7d03
 
ac7d03
     def __get_pin(self):
ac7d03
         try:
ac7d03
-- 
ac7d03
2.9.3
ac7d03