URL: https://github.com/freeipa/freeipa/pull/5733 Author: rcritten Title: #5733: Revert the way hosts/services certs are searched for Action: opened
PR body: """ Revert the way hosts/services certs are searched for This reverts a change that caused all service certs to be revoked if any other service was deleted. There is special handling when passing in host, service or user into cert_find. The _ldap_search method creates filters for the object which later in cert_find removes unrelated certificates. https://pagure.io/freeipa/issue/7835 Signed-off-by: Rob Crittenden <rcrit...@redhat.com> """ To pull the PR as Git branch: git remote add ghfreeipa https://github.com/freeipa/freeipa git fetch ghfreeipa pull/5733/head:pr5733 git checkout pr5733
From bb56b0543d72917ee7ffede5749b30ee1203aa44 Mon Sep 17 00:00:00 2001 From: Rob Crittenden <rcrit...@redhat.com> Date: Fri, 23 Apr 2021 17:04:39 +0000 Subject: [PATCH 1/2] Revert the way hosts/services certs are searched for This reverts a change that caused all service certs to be revoked if any other service was deleted. There is special handling when passing in host, service or user into cert_find. The _ldap_search method creates filters for the object which later in cert_find removes unrelated certificates. https://pagure.io/freeipa/issue/7835 Signed-off-by: Rob Crittenden <rcrit...@redhat.com> --- ipaserver/plugins/cert.py | 16 +++++++++++++-- ipaserver/plugins/host.py | 2 +- ipaserver/plugins/service.py | 39 +++++++++++++----------------------- 3 files changed, 29 insertions(+), 28 deletions(-) diff --git a/ipaserver/plugins/cert.py b/ipaserver/plugins/cert.py index 7decee439c1..f63f8f9031b 100644 --- a/ipaserver/plugins/cert.py +++ b/ipaserver/plugins/cert.py @@ -1650,8 +1650,7 @@ def _ca_search(self, raw, pkey_only, exactly, **options): 'validnotafter_from', 'validnotafter_to', 'validnotbefore_from', 'validnotbefore_to', 'issuedon_from', 'issuedon_to', - 'revokedon_from', 'revokedon_to', - 'status'): + 'revokedon_from', 'revokedon_to',): try: value = options[name] except KeyError: @@ -1688,6 +1687,11 @@ def _ca_search(self, raw, pkey_only, exactly, **options): elif len(users) == 1 and not services and not hosts: ra_options['subject'] = users[0] + # Make an exception for status so we can only retrieve VALID + # certificates when searching for hosts and services. + if 'status' in options: + ra_options['status'] = options.get('status') + try: ca_enabled_check(self.api) except errors.NotFound: @@ -1733,6 +1737,14 @@ def _ca_search(self, raw, pkey_only, exactly, **options): return result, False, complete def _ldap_search(self, all, pkey_only, no_members, **options): + """Search for certificates directly in the LDAP tree + + This is only done when one of the three special options + user, host, service are passed in. + + The effect of this is that objects not requested will be + filtered out later because complete will be True. + """ ldap = self.api.Backend.ldap2 filters = [] diff --git a/ipaserver/plugins/host.py b/ipaserver/plugins/host.py index 8d5cf3c6003..8c84a9e3bc7 100644 --- a/ipaserver/plugins/host.py +++ b/ipaserver/plugins/host.py @@ -872,7 +872,7 @@ def pre_callback(self, ldap, dn, *keys, **options): if self.api.Command.ca_is_enabled()['result']: certs = self.api.Command.cert_find( - subject=fqdn, status='VALID' + host=keys, status='VALID' )['result'] revoke_certs(certs) diff --git a/ipaserver/plugins/service.py b/ipaserver/plugins/service.py index 27d3fc8db2f..b77a47b002f 100644 --- a/ipaserver/plugins/service.py +++ b/ipaserver/plugins/service.py @@ -825,16 +825,10 @@ def pre_callback(self, ldap, dn, *keys, **options): # custom services allow them to manage them. check_required_principal(ldap, keys[-1]) if self.api.Command.ca_is_enabled()['result']: - # only try to revoke certs for valid principals - try: - subject = keys[-1].hostname - except ValueError: - pass - else: - certs = self.api.Command.cert_find( - subject=subject, status='VALID' - )['result'] - revoke_certs(certs) + certs = self.api.Command.cert_find( + service=keys, status='VALID' + )['result'] + revoke_certs(certs) return dn @@ -1108,21 +1102,16 @@ def execute(self, *keys, **options): done_work = False if self.api.Command.ca_is_enabled()['result']: - try: - subject = keys[-1].hostname - except ValueError: - pass - else: - certs = self.api.Command.cert_find( - subject=subject, status='VALID' - )['result'] - - if len(certs) > 0: - revoke_certs(certs) - # Remove the usercertificate altogether - entry_attrs['usercertificate'] = None - ldap.update_entry(entry_attrs) - done_work = True + certs = self.api.Command.cert_find( + service=keys, status='VALID' + )['result'] + + if len(certs) > 0: + revoke_certs(certs) + # Remove the usercertificate altogether + entry_attrs['usercertificate'] = None + ldap.update_entry(entry_attrs) + done_work = True self.obj.get_password_attributes(ldap, dn, entry_attrs) if entry_attrs['has_keytab']: From 9f38ec61efc9be0f1115d2f23f16e72a24f2d3a8 Mon Sep 17 00:00:00 2001 From: Rob Crittenden <rcrit...@redhat.com> Date: Fri, 23 Apr 2021 17:35:30 +0000 Subject: [PATCH 2/2] ipatests: Test that service-del revokes the right certs The cert-find code can be tricky. When deleting a service check that its certificate is revoked but also check whether another service for the same host is not. https://pagure.io/freeipa/issue/7835 Signed-off-by: Rob Crittenden <rcrit...@redhat.com> --- ipatests/test_integration/test_cert.py | 37 ++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/ipatests/test_integration/test_cert.py b/ipatests/test_integration/test_cert.py index b6bb2f08ac0..6c8b2952d86 100644 --- a/ipatests/test_integration/test_cert.py +++ b/ipatests/test_integration/test_cert.py @@ -168,6 +168,43 @@ def test_ipa_getcert_san_aci(self): ipaddrs = ext.value.get_values_for_type(x509.IPAddress) assert ipaddrs == [ipaddress.ip_address(self.clients[0].ip)] + def test_service_del_revocation(self): + """Test that removing a service revokes its certificate + + This removes the service created in the previous test + and ensures that other certificates are not revoked when + one is. + """ + hostname = self.clients[0].hostname + certfile = os.path.join(paths.OPENSSL_CERTS_DIR, "testservice.pem") + + cmd_arg = ['ipa-getcert', 'list', '-f', certfile] + result = self.clients[0].run_command(cmd_arg) + request_id = re.findall(r'\d+', result.stdout_text) + + # This assumes that the cert was issued in the previous test + certdata = self.clients[0].get_file_contents(certfile) + cert = x509.load_pem_x509_certificate( + certdata, default_backend() + ) + serial = cert.serial_number + + # Baseline: the cert should not be revoked yet + result = self.master.run_command(["ipa", "cert-show", str(serial)]) + assert "Revoked: False" in result.stdout_text + + # Delete the service and confirm the cert is revoked + self.master.run_command(["ipa", "service-del", f"test/{hostname}"]) + result = self.master.run_command(["ipa", "cert-show", str(serial)]) + assert "Revoked: True" in result.stdout_text + + # Get the web cert so we can ensure it isn't revoked + certfile = self.master.get_file_contents(paths.HTTPD_CERT_FILE) + cert = x509.load_certificate_list(certfile) + serial = cert[0].serial_number + result = self.master.run_command(["ipa", "cert-show", str(serial)]) + assert "Revoked: False" in result.stdout_text + def test_getcert_list_profile(self): """ Test that getcert list command displays the profile
_______________________________________________ FreeIPA-devel mailing list -- freeipa-devel@lists.fedorahosted.org To unsubscribe send an email to freeipa-devel-le...@lists.fedorahosted.org Fedora Code of Conduct: https://docs.fedoraproject.org/en-US/project/code-of-conduct/ List Guidelines: https://fedoraproject.org/wiki/Mailing_list_guidelines List Archives: https://lists.fedorahosted.org/archives/list/freeipa-devel@lists.fedorahosted.org Do not reply to spam on the list, report it: https://pagure.io/fedora-infrastructure