From db83624e48e813c9624bc1003cf746de98471aef Mon Sep 17 00:00:00 2001 From: eabdullin Date: Tue, 7 Nov 2023 13:16:55 +0000 Subject: [PATCH] import CS ipa-4.10.2-4.el9_3 --- .gitignore | 2 +- .ipa.metadata | 2 +- ...erManager-ACI-to-allow-managers-from.patch | 44 -- ...-notification-which-hides-Add-button.patch | 35 + ...lient-depends-on-krb5-pkinit-openssl.patch | 42 -- ...hat-SSSD_PUBCONF_KRB5_INCLUDE_D_DIR-.patch | 48 ++ ...penSSL-certificate-parser-in-cert-fi.patch | 103 +++ ...emove-error-log-about-missing-bkup-f.patch | 54 -- ...4-Revert-cert_find-fix-call-with-all.patch | 32 + ...NG-before-kinit-command-to-fix-issue.patch | 118 --- ...ryptography-parser-directly-in-cert-.patch | 115 +++ ...d-handle-missing-msSFU30MaxGidNumber.patch | 48 -- ...rade-add-PKI-drop-in-file-if-missing.patch | 38 + ...6-doc-Design-for-certificate-pruning.patch | 337 --------- ...-add-a-test-for-upgrade-and-PKI-drop.patch | 53 ++ ...add-certificate-request-pruning-mana.patch | 684 ------------------ ...stall-PKI-before-shutting-down-servi.patch | 129 ++++ ...run-command-for-manual-job-execution.patch | 138 ---- SOURCES/0009-Detection-of-PKI-subsystem.patch | 44 ++ ...s-add-wrapper-around-ACME-RSNv3-test.patch | 43 -- .../0010-Upgrade-fix-replica-agreement.patch | 164 +++++ ...ld-add-a-notice-about-high-CPU-usage.patch | 68 -- ...-setting-values-of-0-in-ACME-pruning.patch | 158 ---- ...sts-add-a-test-to-ipa-server-upgrade.patch | 69 ++ ...DNS-record-when-updating-system-reco.patch | 69 -- ...backup-restore-scenario-with-replica.patch | 55 ++ ...-data-type-to-avoid-endianness-issue.patch | 54 ++ ...istency-checker-needs-to-handle-chil.patch | 111 --- ...14-Add-test-for-SSH-with-GSSAPI-auth.patch | 83 --- ...firewall-rule-for-http-service-on-ac.patch | 42 ++ ...ove-error-related-to-non-existing-id.patch | 79 ++ ...tests-fix-assertion-in-test_subid.py.patch | 36 - ...0016-ipatests-mark-test_smb-as-xfail.patch | 29 - ...-a-test-for-user-plugin-with-non-exi.patch | 122 ++++ ...ry-leak-in-the-OTP-last-token-plugin.patch | 117 +++ ...7-Tests-force-key-type-in-ACME-tests.patch | 43 -- ...nt-the-admin-user-from-being-deleted.patch | 136 ++++ ..._name-checking-to-DNS-system-records.patch | 36 - ...fix-error-handling-of-is_master_host.patch | 87 +++ ...a-ca-error-messages-to-IPADNSSystemR.patch | 53 -- ...atests-tests-for-certificate-pruning.patch | 445 ------------ ...expected-webui-msg-for-admin-deletio.patch | 38 + ...that-ipa-automember-rebuild-prints-a.patch | 65 -- ...fixture-call-and-wait-to-get-things-.patch | 70 ++ SOURCES/0022-ipatests-fix-test_topology.patch | 60 ++ ...-ipatests-fix-tests-in-TestACMEPrune.patch | 74 -- ...-of-PAC-ticket-signature-depending-o.patch | 574 --------------- .../0023-ipatests-idm-api-related-tests.patch | 560 ++++++++++++++ ...stpone-ticket-checksum-configuration.patch | 269 ------- ...tests-fixture-can-produce-IndexError.patch | 45 ++ ...te-nss-and-pam-services-in-sssd.conf.patch | 42 ++ SOURCES/freeipa-4.10.1.tar.gz.asc | 16 - SOURCES/freeipa-4.10.2.tar.gz.asc | 16 + SPECS/freeipa.spec | 103 ++- 54 files changed, 2422 insertions(+), 3675 deletions(-) delete mode 100644 SOURCES/0001-updates-fix-memberManager-ACI-to-allow-managers-from.patch create mode 100644 SOURCES/0001-webuitests-close-notification-which-hides-Add-button.patch delete mode 100644 SOURCES/0002-Spec-file-ipa-client-depends-on-krb5-pkinit-openssl.patch create mode 100644 SOURCES/0002-ipatests-Check-that-SSSD_PUBCONF_KRB5_INCLUDE_D_DIR-.patch create mode 100644 SOURCES/0003-Revert-Use-the-OpenSSL-certificate-parser-in-cert-fi.patch delete mode 100644 SOURCES/0003-server-install-remove-error-log-about-missing-bkup-f.patch create mode 100644 SOURCES/0004-Revert-cert_find-fix-call-with-all.patch delete mode 100644 SOURCES/0004-ipa-tests-Add-LANG-before-kinit-command-to-fix-issue.patch create mode 100644 SOURCES/0005-Use-the-python-cryptography-parser-directly-in-cert-.patch delete mode 100644 SOURCES/0005-trust-add-handle-missing-msSFU30MaxGidNumber.patch create mode 100644 SOURCES/0006-Upgrade-add-PKI-drop-in-file-if-missing.patch delete mode 100644 SOURCES/0006-doc-Design-for-certificate-pruning.patch create mode 100644 SOURCES/0007-Integration-test-add-a-test-for-upgrade-and-PKI-drop.patch delete mode 100644 SOURCES/0007-ipa-acme-manage-add-certificate-request-pruning-mana.patch create mode 100644 SOURCES/0008-Uninstaller-uninstall-PKI-before-shutting-down-servi.patch delete mode 100644 SOURCES/0008-doc-add-the-run-command-for-manual-job-execution.patch create mode 100644 SOURCES/0009-Detection-of-PKI-subsystem.patch delete mode 100644 SOURCES/0009-tests-add-wrapper-around-ACME-RSNv3-test.patch create mode 100644 SOURCES/0010-Upgrade-fix-replica-agreement.patch delete mode 100644 SOURCES/0010-automember-rebuild-add-a-notice-about-high-CPU-usage.patch delete mode 100644 SOURCES/0011-Fix-setting-values-of-0-in-ACME-pruning.patch create mode 100644 SOURCES/0011-Integration-tests-add-a-test-to-ipa-server-upgrade.patch delete mode 100644 SOURCES/0012-Wipe-the-ipa-ca-DNS-record-when-updating-system-reco.patch create mode 100644 SOURCES/0012-tests-fix-backup-restore-scenario-with-replica.patch create mode 100644 SOURCES/0013-OTP-fix-data-type-to-avoid-endianness-issue.patch delete mode 100644 SOURCES/0013-ipa-kdb-PAC-consistency-checker-needs-to-handle-chil.patch delete mode 100644 SOURCES/0014-Add-test-for-SSH-with-GSSAPI-auth.patch create mode 100644 SOURCES/0014-ipatests-enable-firewall-rule-for-http-service-on-ac.patch create mode 100644 SOURCES/0015-User-plugin-improve-error-related-to-non-existing-id.patch delete mode 100644 SOURCES/0015-webui-tests-fix-assertion-in-test_subid.py.patch delete mode 100644 SOURCES/0016-ipatests-mark-test_smb-as-xfail.patch create mode 100644 SOURCES/0016-xmlrpc-tests-add-a-test-for-user-plugin-with-non-exi.patch create mode 100644 SOURCES/0017-Fix-memory-leak-in-the-OTP-last-token-plugin.patch delete mode 100644 SOURCES/0017-Tests-force-key-type-in-ACME-tests.patch create mode 100644 SOURCES/0018-Prevent-the-admin-user-from-being-deleted.patch delete mode 100644 SOURCES/0018-tests-Add-ipa_ca_name-checking-to-DNS-system-records.patch create mode 100644 SOURCES/0019-ipa-kdb-fix-error-handling-of-is_master_host.patch delete mode 100644 SOURCES/0019-tests-Add-new-ipa-ca-error-messages-to-IPADNSSystemR.patch delete mode 100644 SOURCES/0020-ipatests-tests-for-certificate-pruning.patch create mode 100644 SOURCES/0020-ipatests-update-expected-webui-msg-for-admin-deletio.patch delete mode 100644 SOURCES/0021-ipatests-ensure-that-ipa-automember-rebuild-prints-a.patch create mode 100644 SOURCES/0021-ipatests-remove-fixture-call-and-wait-to-get-things-.patch create mode 100644 SOURCES/0022-ipatests-fix-test_topology.patch delete mode 100644 SOURCES/0022-ipatests-fix-tests-in-TestACMEPrune.patch delete mode 100644 SOURCES/0023-Tolerate-absence-of-PAC-ticket-signature-depending-o.patch create mode 100644 SOURCES/0023-ipatests-idm-api-related-tests.patch delete mode 100644 SOURCES/0024-ipa-kdb-postpone-ticket-checksum-configuration.patch create mode 100644 SOURCES/0024-ipatests-fixture-can-produce-IndexError.patch create mode 100644 SOURCES/0025-Installer-activate-nss-and-pam-services-in-sssd.conf.patch delete mode 100644 SOURCES/freeipa-4.10.1.tar.gz.asc create mode 100644 SOURCES/freeipa-4.10.2.tar.gz.asc diff --git a/.gitignore b/.gitignore index 27cf303..e26cdcd 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1 @@ -SOURCES/freeipa-4.10.1.tar.gz +SOURCES/freeipa-4.10.2.tar.gz diff --git a/.ipa.metadata b/.ipa.metadata index 8789124..51a266d 100644 --- a/.ipa.metadata +++ b/.ipa.metadata @@ -1 +1 @@ -6203cf7c2e003c35eb9ac40e4fd2954c6bea1856 SOURCES/freeipa-4.10.1.tar.gz +17d580b7dccb1586898aa46c7ecdd2bd2754ee5f SOURCES/freeipa-4.10.2.tar.gz diff --git a/SOURCES/0001-updates-fix-memberManager-ACI-to-allow-managers-from.patch b/SOURCES/0001-updates-fix-memberManager-ACI-to-allow-managers-from.patch deleted file mode 100644 index 3c5dc68..0000000 --- a/SOURCES/0001-updates-fix-memberManager-ACI-to-allow-managers-from.patch +++ /dev/null @@ -1,44 +0,0 @@ -From 42be04fe4ff317efe599dcbc2637f94ecc6fa220 Mon Sep 17 00:00:00 2001 -From: Alexander Bokovoy -Date: Mon, 21 Nov 2022 16:12:46 +0200 -Subject: [PATCH] updates: fix memberManager ACI to allow managers from a - specified group - -The original implementation of the member manager added support for both -user and group managers but left out upgrade scenario. This means when -upgrading existing installation a manager whose rights defined by the -group membership would not be able to add group members until the ACI is -fixed. - -Remove old ACI and add a full one during upgrade step. - -Fixes: https://pagure.io/freeipa/issue/9286 -Signed-off-by: Alexander Bokovoy -Reviewed-By: Florence Blanc-Renaud ---- - install/updates/20-aci.update | 6 ++++-- - 1 file changed, 4 insertions(+), 2 deletions(-) - -diff --git a/install/updates/20-aci.update b/install/updates/20-aci.update -index a168bb9573a9fbb9ff15f0b19bb8ec75b48d82a9..4a7ba137c4711aa3f8b064fdd482ffee76c59949 100644 ---- a/install/updates/20-aci.update -+++ b/install/updates/20-aci.update -@@ -141,11 +141,13 @@ add:aci:(targetattr = "usercertificate")(version 3.0;acl "selfservice:Users can - - # Allow member managers to modify members of user groups - dn: cn=groups,cn=accounts,$SUFFIX --add:aci: (targetattr = "member")(targetfilter = "(objectclass=ipaUserGroup)")(version 3.0; acl "Allow member managers to modify members of user groups"; allow (write) userattr = "memberManager#USERDN";) -+remove:aci: (targetattr = "member")(targetfilter = "(objectclass=ipaUserGroup)")(version 3.0; acl "Allow member managers to modify members of user groups"; allow (write) userattr = "memberManager#USERDN";) -+add:aci: (targetattr = "member")(targetfilter = "(objectclass=ipaUserGroup)")(version 3.0; acl "Allow member managers to modify members of user groups"; allow (write) userattr = "memberManager#USERDN" or userattr = "memberManager#GROUPDN";) - - # Allow member managers to modify members of host groups - dn: cn=hostgroups,cn=accounts,$SUFFIX --add:aci: (targetattr = "member")(targetfilter = "(objectclass=ipaHostGroup)")(version 3.0; acl "Allow member managers to modify members of host groups"; allow (write) userattr = "memberManager#USERDN";) -+remove:aci: (targetattr = "member")(targetfilter = "(objectclass=ipaHostGroup)")(version 3.0; acl "Allow member managers to modify members of host groups"; allow (write) userattr = "memberManager#USERDN";) -+add:aci: (targetattr = "member")(targetfilter = "(objectclass=ipaHostGroup)")(version 3.0; acl "Allow member managers to modify members of host groups"; allow (write) userattr = "memberManager#USERDN" or userattr = "memberManager#GROUPDN";) - - # Hosts can add and delete their own services - dn: cn=services,cn=accounts,$SUFFIX --- -2.38.1 - diff --git a/SOURCES/0001-webuitests-close-notification-which-hides-Add-button.patch b/SOURCES/0001-webuitests-close-notification-which-hides-Add-button.patch new file mode 100644 index 0000000..f554010 --- /dev/null +++ b/SOURCES/0001-webuitests-close-notification-which-hides-Add-button.patch @@ -0,0 +1,35 @@ +From 4f6ebb5fdf6d2514d4683247e5d1bfc6022a500e Mon Sep 17 00:00:00 2001 +From: Florence Blanc-Renaud +Date: Tue, 6 Jun 2023 09:04:48 +0200 +Subject: [PATCH] webuitests: close notification which hides Add button + +The webui test test_service.py::test_service::test_arbitrary_certificates +randomly fails. +The test is creating a new service then navigates to the Service page +and clicks on the Add Certificate button. +The notification area may still be present and hide the button, with +the message "Service successfully added". +Close all notifications before navigating to the Service page. + +Fixes: https://pagure.io/freeipa/issue/9389 +Signed-off-by: Florence Blanc-Renaud +Reviewed-By: Michal Polovka +--- + ipatests/test_webui/test_service.py | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/ipatests/test_webui/test_service.py b/ipatests/test_webui/test_service.py +index f1d9a9d624bc587634e03a86050627a648feb26d..e2976d73acd1f574f1164828be9d8e871b737d7f 100644 +--- a/ipatests/test_webui/test_service.py ++++ b/ipatests/test_webui/test_service.py +@@ -296,6 +296,7 @@ class test_service(sevice_tasks): + cert_widget_sel = "div.certificate-widget" + + self.add_record(ENTITY, data) ++ self.close_notifications() + self.navigate_to_record(pkey) + + # check whether certificate section is present +-- +2.41.0 + diff --git a/SOURCES/0002-Spec-file-ipa-client-depends-on-krb5-pkinit-openssl.patch b/SOURCES/0002-Spec-file-ipa-client-depends-on-krb5-pkinit-openssl.patch deleted file mode 100644 index 3d6e689..0000000 --- a/SOURCES/0002-Spec-file-ipa-client-depends-on-krb5-pkinit-openssl.patch +++ /dev/null @@ -1,42 +0,0 @@ -From 2d0a0cc40fb8674f30ba62980b1953cef840009e Mon Sep 17 00:00:00 2001 -From: Florence Blanc-Renaud -Date: Thu, 1 Dec 2022 13:58:58 +0100 -Subject: [PATCH] Spec file: ipa-client depends on krb5-pkinit-openssl - -Now that ipa-client-installs supports pkinit, the package -depends on krb5-pkinit-openssl. -Update the spec file, move the dependency from ipa-server -to ipa-client subpackage. - -Fixes: https://pagure.io/freeipa/issue/9290 - -Signed-off-by: Florence Blanc-Renaud -Reviewed-By: Alexander Bokovoy ---- - freeipa.spec.in | 3 ++- - 1 file changed, 2 insertions(+), 1 deletion(-) - -diff --git a/freeipa.spec.in b/freeipa.spec.in -index f09741d7ad6c09e52c4bd24fcc9300584f83a49d..7dcf2e66abe40e6bde3491268b9c012f7578a8b6 100755 ---- a/freeipa.spec.in -+++ b/freeipa.spec.in -@@ -449,7 +449,6 @@ Requires: nss-tools >= %{nss_version} - Requires(post): krb5-server >= %{krb5_version} - Requires(post): krb5-server >= %{krb5_base_version} - Requires: krb5-kdb-version = %{krb5_kdb_version} --Requires: krb5-pkinit-openssl >= %{krb5_version} - Requires: cyrus-sasl-gssapi%{?_isa} - Requires: chrony - Requires: httpd >= %{httpd_version} -@@ -675,6 +674,8 @@ Requires: python3-sssdconfig >= %{sssd_version} - Requires: cyrus-sasl-gssapi%{?_isa} - Requires: chrony - Requires: krb5-workstation >= %{krb5_version} -+# support pkinit with client install -+Requires: krb5-pkinit-openssl >= %{krb5_version} - # authselect: sssd profile with-subid - %if 0%{?fedora} >= 36 - Requires: authselect >= 1.4.0 --- -2.38.1 - diff --git a/SOURCES/0002-ipatests-Check-that-SSSD_PUBCONF_KRB5_INCLUDE_D_DIR-.patch b/SOURCES/0002-ipatests-Check-that-SSSD_PUBCONF_KRB5_INCLUDE_D_DIR-.patch new file mode 100644 index 0000000..34d91f6 --- /dev/null +++ b/SOURCES/0002-ipatests-Check-that-SSSD_PUBCONF_KRB5_INCLUDE_D_DIR-.patch @@ -0,0 +1,48 @@ +From 8d34f453fb139c4cef055a4963f307a760316a73 Mon Sep 17 00:00:00 2001 +From: Anuja More +Date: Thu, 11 May 2023 12:50:10 +0530 +Subject: [PATCH] ipatests: Check that SSSD_PUBCONF_KRB5_INCLUDE_D_DIR is not + included in krb5.conf + +SSSD already provides a config snippet which includes +SSSD_PUBCONF_KRB5_INCLUDE_D_DIR, and having both breaks Java. +Test checks that krb5.conf does not include +SSSD_PUBCONF_KRB5_INCLUDE_D_DIR. + +Related: https://pagure.io/freeipa/issue/9267 + +Signed-off-by: Anuja More +Reviewed-By: Florence Blanc-Renaud +--- + .../test_integration/test_installation_client.py | 15 +++++++++++++++ + 1 file changed, 15 insertions(+) + +diff --git a/ipatests/test_integration/test_installation_client.py b/ipatests/test_integration/test_installation_client.py +index 014b0f6ab34dd92c00c0187c2c86b8bfbaa70e81..56e1593bfcfa3eb7f9918fc6f2993d836884ea38 100644 +--- a/ipatests/test_integration/test_installation_client.py ++++ b/ipatests/test_integration/test_installation_client.py +@@ -76,6 +76,21 @@ class TestInstallClient(IntegrationTest): + result = self.clients[0].run_command(['cat', '/etc/ssh/ssh_config']) + assert 'HostKeyAlgorithms' not in result.stdout_text + ++ def test_client_install_with_krb5(self): ++ """Test that SSSD_PUBCONF_KRB5_INCLUDE_D_DIR is not added in krb5.conf ++ ++ SSSD already provides a config snippet which includes ++ SSSD_PUBCONF_KRB5_INCLUDE_D_DIR, and having both breaks Java. ++ Test checks that krb5.conf does not include ++ SSSD_PUBCONF_KRB5_INCLUDE_D_DIR. ++ ++ related: https://pagure.io/freeipa/issue/9267 ++ """ ++ krb5_cfg = self.master.get_file_contents(paths.KRB5_CONF) ++ assert 'includedir {dir}'.format( ++ dir=paths.SSSD_PUBCONF_KRB5_INCLUDE_D_DIR ++ ).encode() not in krb5_cfg ++ + + class TestClientInstallBind(IntegrationTest): + """ +-- +2.41.0 + diff --git a/SOURCES/0003-Revert-Use-the-OpenSSL-certificate-parser-in-cert-fi.patch b/SOURCES/0003-Revert-Use-the-OpenSSL-certificate-parser-in-cert-fi.patch new file mode 100644 index 0000000..9f55246 --- /dev/null +++ b/SOURCES/0003-Revert-Use-the-OpenSSL-certificate-parser-in-cert-fi.patch @@ -0,0 +1,103 @@ +From 276138087158c6b2ea76b43c754084144e543c0b Mon Sep 17 00:00:00 2001 +From: Rob Crittenden +Date: Wed, 7 Jun 2023 11:32:21 -0400 +Subject: [PATCH] Revert "Use the OpenSSL certificate parser in cert-find" + +This reverts commit 191880bc9f77c3e8a3cecc82e6eea33ab5ad03e4. + +The problem isn't with python-cryptography, it is with the +IPACertificate class which does way more work on a certificate +than is necessary in cert-find. + +Related: https://pagure.io/freeipa/issue/9331 +Reviewed-By: Florence Blanc-Renaud +--- + freeipa.spec.in | 2 -- + ipaserver/plugins/cert.py | 26 +++----------------------- + 2 files changed, 3 insertions(+), 25 deletions(-) + +diff --git a/freeipa.spec.in b/freeipa.spec.in +index 3e23bbfe9d054a3a9febf468de0bcb4a6e81bb32..bec9780a82fe0d9bc5a50a93bdce8aa7e27a9f30 100755 +--- a/freeipa.spec.in ++++ b/freeipa.spec.in +@@ -412,7 +412,6 @@ BuildRequires: python3-pylint + BuildRequires: python3-pytest-multihost + BuildRequires: python3-pytest-sourceorder + BuildRequires: python3-qrcode-core >= 5.0.0 +-BuildRequires: python3-pyOpenSSL + BuildRequires: python3-samba + BuildRequires: python3-six + BuildRequires: python3-sss +@@ -884,7 +883,6 @@ Requires: python3-netifaces >= 0.10.4 + Requires: python3-pyasn1 >= 0.3.2-2 + Requires: python3-pyasn1-modules >= 0.3.2-2 + Requires: python3-pyusb +-Requires: python3-pyOpenSSL + Requires: python3-qrcode-core >= 5.0.0 + Requires: python3-requests + Requires: python3-six +diff --git a/ipaserver/plugins/cert.py b/ipaserver/plugins/cert.py +index 400b1b3cec0aba82e699a4a981516e121f3e0c77..2e32f4ecd50ac92c28bcaffcebe9c2c87557858a 100644 +--- a/ipaserver/plugins/cert.py ++++ b/ipaserver/plugins/cert.py +@@ -30,7 +30,6 @@ import cryptography.x509 + from cryptography.hazmat.primitives import hashes, serialization + from dns import resolver, reversename + import six +-import sys + + from ipalib import Command, Str, Int, Flag, StrEnum, SerialNumber + from ipalib import api +@@ -1618,19 +1617,7 @@ class cert_find(Search, CertMethod): + ) + + def _get_cert_key(self, cert): +- # for cert-find with a certificate value +- if isinstance(cert, x509.IPACertificate): +- return (DN(cert.issuer), cert.serial_number) +- +- issuer = [] +- for oid, value in cert.get_issuer().get_components(): +- issuer.append( +- '{}={}'.format(oid.decode('utf-8'), value.decode('utf-8')) +- ) +- issuer = ','.join(issuer) +- # Use this to flip from OpenSSL reverse to X500 ordering +- issuer = DN(issuer).x500_text() +- return (DN(issuer), cert.get_serial_number()) ++ return (DN(cert.issuer), cert.serial_number) + + def _cert_search(self, pkey_only, **options): + result = collections.OrderedDict() +@@ -1750,11 +1737,6 @@ class cert_find(Search, CertMethod): + return result, False, complete + + def _ldap_search(self, all, pkey_only, no_members, **options): +- # defer import of the OpenSSL module to not affect the requests +- # module which will use pyopenssl if this is available. +- if sys.modules.get('OpenSSL.SSL', False) is None: +- del sys.modules["OpenSSL.SSL"] +- import OpenSSL.crypto + ldap = self.api.Backend.ldap2 + + filters = [] +@@ -1813,14 +1795,12 @@ class cert_find(Search, CertMethod): + ca_enabled = getattr(context, 'ca_enabled') + for entry in entries: + for attr in ('usercertificate', 'usercertificate;binary'): +- for der in entry.raw.get(attr, []): +- cert = OpenSSL.crypto.load_certificate( +- OpenSSL.crypto.FILETYPE_ASN1, der) ++ for cert in entry.get(attr, []): + cert_key = self._get_cert_key(cert) + try: + obj = result[cert_key] + except KeyError: +- obj = {'serial_number': cert.get_serial_number()} ++ obj = {'serial_number': cert.serial_number} + if not pkey_only and (all or not ca_enabled): + # Retrieving certificate details is now deferred + # until after all certificates are collected. +-- +2.41.0 + diff --git a/SOURCES/0003-server-install-remove-error-log-about-missing-bkup-f.patch b/SOURCES/0003-server-install-remove-error-log-about-missing-bkup-f.patch deleted file mode 100644 index 4bb3177..0000000 --- a/SOURCES/0003-server-install-remove-error-log-about-missing-bkup-f.patch +++ /dev/null @@ -1,54 +0,0 @@ -From 894dca12c120f0bfa705307a0609da47326b8fb2 Mon Sep 17 00:00:00 2001 -From: Florence Blanc-Renaud -Date: Thu, 12 Jan 2023 11:26:53 +0100 -Subject: [PATCH] server install: remove error log about missing bkup file - -The client installer code can be called in 3 different ways: -- from ipa-client-install CLI -- from ipa-replica-install CLI if the client is not already installed -- from ipa-server-install - -In the last case, the client installer is called with -options.on_master=True -As a result, it's skipping the part that is creating the krb5 -configuration: - if not options.on_master: - nolog = tuple() - configure_krb5_conf(...) - -The configure_krb5_conf method is the place where the krb5.conf file is -backup'ed with the extention ".ipabkp". For a master installation, this -code is not called and the ipabkp file does not exist => delete raises -an error. - -When delete fails because the file does not exist, no need to log an -error message. - -Fixes: https://pagure.io/freeipa/issue/9306 -Signed-off-by: Florence Blanc-Renaud -Reviewed-By: Rob Crittenden ---- - ipaclient/install/client.py | 7 +++---- - 1 file changed, 3 insertions(+), 4 deletions(-) - -diff --git a/ipaclient/install/client.py b/ipaclient/install/client.py -index e5d3e8223efa1ebb69a1b7e963c394f9e1f38816..6e7f17d5b69581320866627cb5747a050cb6e32e 100644 ---- a/ipaclient/install/client.py -+++ b/ipaclient/install/client.py -@@ -124,10 +124,9 @@ def cleanup(func): - os.rmdir(ccache_dir) - except OSError: - pass -- try: -- os.remove(krb_name + ".ipabkp") -- except OSError: -- logger.error("Could not remove %s.ipabkp", krb_name) -+ # During master installation, the .ipabkp file is not created -+ # Ignore the delete error if it is "file does not exist" -+ remove_file(krb_name + ".ipabkp") - - return inner - --- -2.39.1 - diff --git a/SOURCES/0004-Revert-cert_find-fix-call-with-all.patch b/SOURCES/0004-Revert-cert_find-fix-call-with-all.patch new file mode 100644 index 0000000..baaa558 --- /dev/null +++ b/SOURCES/0004-Revert-cert_find-fix-call-with-all.patch @@ -0,0 +1,32 @@ +From d83a4b0babdc7beb124d3748b5815ce309739eb7 Mon Sep 17 00:00:00 2001 +From: Rob Crittenden +Date: Tue, 13 Jun 2023 17:01:49 -0400 +Subject: [PATCH] Revert "cert_find: fix call with --all" + +This reverts commit 1f30cc65276a532e7288217f216b72a2b0628c8f. + +The problem isn't with python-cryptography, it is with the +IPACertificate class which does way more work on a certificate +than is necessary in cert-find. + +Related: https://pagure.io/freeipa/issue/9331 +Reviewed-By: Florence Blanc-Renaud +--- + ipaserver/plugins/cert.py | 1 - + 1 file changed, 1 deletion(-) + +diff --git a/ipaserver/plugins/cert.py b/ipaserver/plugins/cert.py +index 2e32f4ecd50ac92c28bcaffcebe9c2c87557858a..36a0e8cb31b4dbdd9bff09165d1d8aa203936d37 100644 +--- a/ipaserver/plugins/cert.py ++++ b/ipaserver/plugins/cert.py +@@ -1807,7 +1807,6 @@ class cert_find(Search, CertMethod): + # For the case of CA-less we need to keep + # the certificate because getting it again later + # would require unnecessary LDAP searches. +- cert = cert.to_cryptography() + obj['certificate'] = ( + base64.b64encode( + cert.public_bytes(x509.Encoding.DER)) +-- +2.41.0 + diff --git a/SOURCES/0004-ipa-tests-Add-LANG-before-kinit-command-to-fix-issue.patch b/SOURCES/0004-ipa-tests-Add-LANG-before-kinit-command-to-fix-issue.patch deleted file mode 100644 index 02bca15..0000000 --- a/SOURCES/0004-ipa-tests-Add-LANG-before-kinit-command-to-fix-issue.patch +++ /dev/null @@ -1,118 +0,0 @@ -From 2520a7adff7a49ddcddaaf19f0e586425dc0d878 Mon Sep 17 00:00:00 2001 -From: Filip Dvorak -Date: Tue, 6 Dec 2022 15:51:27 +0100 -Subject: [PATCH] ipa tests: Add LANG before kinit command to fix issue with - locale settings - -Reviewed-By: Rob Crittenden -Reviewed-By: Alexander Bokovoy -Reviewed-By: Florence Blanc-Renaud -Reviewed-By: Michal Polovka ---- - ipatests/test_integration/test_krbtpolicy.py | 20 ++++++++++---------- - 1 file changed, 10 insertions(+), 10 deletions(-) - -diff --git a/ipatests/test_integration/test_krbtpolicy.py b/ipatests/test_integration/test_krbtpolicy.py -index eae16247bdfb195c1d91209cf2d11eac4c25018f..269cfb0a191821c229aaeb5a3eda0181c6e3ae62 100644 ---- a/ipatests/test_integration/test_krbtpolicy.py -+++ b/ipatests/test_integration/test_krbtpolicy.py -@@ -23,7 +23,7 @@ PASSWORD = "Secret123" - USER1 = "testuser1" - USER2 = "testuser2" - MAXLIFE = 86400 -- -+LANG_PKG = ["langpacks-en"] - - def maxlife_within_policy(input, maxlife, slush=3600): - """Given klist output of the TGT verify that it is within policy -@@ -45,7 +45,6 @@ def maxlife_within_policy(input, maxlife, slush=3600): - - return maxlife >= diff >= maxlife - slush - -- - @pytest.fixture - def reset_to_default_policy(): - """Reset default user authentication and user authentication type""" -@@ -70,7 +69,7 @@ def reset_to_default_policy(): - def kinit_check_life(master, user): - """Acquire a TGT and check if it's within the lifetime window""" - master.run_command(["kinit", user], stdin_text=f"{PASSWORD}\n") -- result = master.run_command("klist | grep krbtgt") -+ result = master.run_command("LANG=en_US.utf-8 klist | grep krbtgt") - assert maxlife_within_policy(result.stdout_text, MAXLIFE) is True - - -@@ -81,6 +80,7 @@ class TestPWPolicy(IntegrationTest): - - @classmethod - def install(cls, mh): -+ tasks.install_packages(cls.master, LANG_PKG) - tasks.install_master(cls.master) - tasks.create_active_user(cls.master, USER1, PASSWORD) - tasks.create_active_user(cls.master, USER2, PASSWORD) -@@ -100,7 +100,7 @@ class TestPWPolicy(IntegrationTest): - - master.run_command(['kinit', USER1], - stdin_text=PASSWORD + '\n') -- result = master.run_command('klist | grep krbtgt') -+ result = master.run_command("LANG=en_US.utf-8 klist | grep krbtgt") - assert maxlife_within_policy(result.stdout_text, MAXLIFE) is True - - def test_krbtpolicy_password_and_hardended(self): -@@ -122,7 +122,7 @@ class TestPWPolicy(IntegrationTest): - - master.run_command(['kinit', USER1], - stdin_text=PASSWORD + '\n') -- result = master.run_command('klist | grep krbtgt') -+ result = master.run_command('LANG=en_US.utf-8 klist | grep krbtgt') - assert maxlife_within_policy(result.stdout_text, 600, - slush=600) is True - -@@ -131,7 +131,7 @@ class TestPWPolicy(IntegrationTest): - # Verify that the short policy only applies to USER1 - master.run_command(['kinit', USER2], - stdin_text=PASSWORD + '\n') -- result = master.run_command('klist | grep krbtgt') -+ result = master.run_command('LANG=en_US.utf-8 klist | grep krbtgt') - assert maxlife_within_policy(result.stdout_text, MAXLIFE) is True - - def test_krbtpolicy_hardended(self): -@@ -151,7 +151,7 @@ class TestPWPolicy(IntegrationTest): - - master.run_command(['kinit', USER1], - stdin_text=PASSWORD + '\n') -- result = master.run_command('klist | grep krbtgt') -+ result = master.run_command('LANG=en_US.utf-8 klist | grep krbtgt') - assert maxlife_within_policy(result.stdout_text, 1800, - slush=1800) is True - -@@ -160,7 +160,7 @@ class TestPWPolicy(IntegrationTest): - # Verify that the short policy only applies to USER1 - master.run_command(['kinit', USER2], - stdin_text=PASSWORD + '\n') -- result = master.run_command('klist | grep krbtgt') -+ result = master.run_command('LANG=en_US.utf-8 klist | grep krbtgt') - assert maxlife_within_policy(result.stdout_text, MAXLIFE) is True - - def test_krbtpolicy_password(self): -@@ -173,7 +173,7 @@ class TestPWPolicy(IntegrationTest): - - master.run_command(['kinit', USER2], - stdin_text=PASSWORD + '\n') -- result = master.run_command('klist | grep krbtgt') -+ result = master.run_command('LANG=en_US.utf-8 klist | grep krbtgt') - assert maxlife_within_policy(result.stdout_text, 1200, - slush=1200) is True - -@@ -183,7 +183,7 @@ class TestPWPolicy(IntegrationTest): - master.run_command(['ipa', 'krbtpolicy-reset', USER2]) - master.run_command(['kinit', USER2], - stdin_text=PASSWORD + '\n') -- result = master.run_command('klist | grep krbtgt') -+ result = master.run_command('LANG=en_US.utf-8 klist | grep krbtgt') - assert maxlife_within_policy(result.stdout_text, MAXLIFE) is True - - def test_krbtpolicy_otp(self, reset_to_default_policy): --- -2.39.1 - diff --git a/SOURCES/0005-Use-the-python-cryptography-parser-directly-in-cert-.patch b/SOURCES/0005-Use-the-python-cryptography-parser-directly-in-cert-.patch new file mode 100644 index 0000000..1293cd0 --- /dev/null +++ b/SOURCES/0005-Use-the-python-cryptography-parser-directly-in-cert-.patch @@ -0,0 +1,115 @@ +From d9aa75459d650e5282a160a3eef09ed175dc5b51 Mon Sep 17 00:00:00 2001 +From: Rob Crittenden +Date: Wed, 7 Jun 2023 11:44:05 -0400 +Subject: [PATCH] Use the python-cryptography parser directly in cert-find + +cert-find is a rather complex beast because it not only +looks for certificates in the optional CA but within the +IPA LDAP database as well. It has a process to deduplicate +the certificates since any PKI issued certificates will +also be associated with an IPA record. + +In order to obtain the data to deduplicate the certificates +the cert from LDAP must be parser for issuer and serial number. +ipaldap has automation to determine the datatype of an +attribute and will use the ipalib.x509 IPACertificate class to +decode a certificate automatically if you access +entry['usercertificate']. + +The downside is that this is comparatively slow. Here is the +parse time in microseconds: + +cryptography 0.0081 +OpenSSL.crypto 0.2271 +ipalib.x509 2.6814 + +Since only issuer and subject are required there is no need to +make the expensive IPACertificate call. + +The IPACertificate parsing time is fine if you're parsing one +certificate but if the LDAP search returns a lot of certificates, +say in the thousands, then those microseconds add up quickly. +In testing it took ~17 seconds to parse 5k certificates (excluding +transmission overhead, etc). + +cert-find when there are a lot of certificates has been +historically slow. It isn't related to the CA which returns +large sets (well, 5k anyway) in a second or two. It was the +LDAP comparision adding tens of seconds to the runtime. + +When searching with the default sizelimit of 100 the time is +~10s without this patch. With it the time is 1.5s. + +CLI times from before and after searching for all certs: + +original: + +------------------------------- +Number of entries returned 5038 +------------------------------- +real 0m15.507s +user 0m0.828s +sys 0m0.241s + +using cryptography: + +real 0m4.037s +user 0m0.816s +sys 0m0.193s + +Fixes: https://pagure.io/freeipa/issue/9331 + +Signed-off-by: Rob Crittenden +Reviewed-By: Florence Blanc-Renaud +--- + ipaserver/plugins/cert.py | 3 ++- + ipatests/test_xmlrpc/test_cert_plugin.py | 12 +++++++++++- + 2 files changed, 13 insertions(+), 2 deletions(-) + +diff --git a/ipaserver/plugins/cert.py b/ipaserver/plugins/cert.py +index 36a0e8cb31b4dbdd9bff09165d1d8aa203936d37..4fb85069a835d94969c9d05789714345ecc60e2e 100644 +--- a/ipaserver/plugins/cert.py ++++ b/ipaserver/plugins/cert.py +@@ -1795,7 +1795,8 @@ class cert_find(Search, CertMethod): + ca_enabled = getattr(context, 'ca_enabled') + for entry in entries: + for attr in ('usercertificate', 'usercertificate;binary'): +- for cert in entry.get(attr, []): ++ for der in entry.raw.get(attr, []): ++ cert = cryptography.x509.load_der_x509_certificate(der) + cert_key = self._get_cert_key(cert) + try: + obj = result[cert_key] +diff --git a/ipatests/test_xmlrpc/test_cert_plugin.py b/ipatests/test_xmlrpc/test_cert_plugin.py +index 433cebcd79f792e5c97307c6d599e50855ff4151..583c67fd942a61f42e578cc51a0e456cacf9a5e5 100644 +--- a/ipatests/test_xmlrpc/test_cert_plugin.py ++++ b/ipatests/test_xmlrpc/test_cert_plugin.py +@@ -254,6 +254,16 @@ class test_cert(BaseCert): + result = _emails_are_valid(email_addrs, []) + assert not result + ++ def test_00012_cert_find_all(self): ++ """ ++ Test that cert-find --all returns successfully. ++ ++ We don't know how many we'll get but there should be at least 10 ++ by default. ++ """ ++ res = api.Command['cert_find'](all=True) ++ assert 'count' in res and res['count'] >= 10 ++ + def test_99999_cleanup(self): + """ + Clean up cert test data +@@ -283,7 +293,7 @@ class test_cert_find(XMLRPC_test): + + short = api.env.host.split('.', maxsplit=1)[0] + +- def test_0001_find_all(self): ++ def test_0001_find_all_certs(self): + """ + Search for all certificates. + +-- +2.41.0 + diff --git a/SOURCES/0005-trust-add-handle-missing-msSFU30MaxGidNumber.patch b/SOURCES/0005-trust-add-handle-missing-msSFU30MaxGidNumber.patch deleted file mode 100644 index 4c702d0..0000000 --- a/SOURCES/0005-trust-add-handle-missing-msSFU30MaxGidNumber.patch +++ /dev/null @@ -1,48 +0,0 @@ -From 97fc368df2db3b559a9def236d3c3e0a12bcdd0a Mon Sep 17 00:00:00 2001 -From: Florence Blanc-Renaud -Date: Mon, 23 Jan 2023 20:28:17 +0100 -Subject: [PATCH] trust-add: handle missing msSFU30MaxGidNumber - -When ipa trust-add is executed with --range-type ad-trust-posix, -the server tries to find the max uidnumber and max gidnumber -from AD domain controller. -The values are extracted from the entry -CN=,CN=ypservers,CN=ypServ30,CN=RpcServices,CN=System, -in the msSFU30MaxUidNumber and msSFU30MaxGidNumber attributes. - -msSFU30MaxUidNumber is required but not msSFU30MaxGidNumber. -In case msSFU30MaxGidNumber is missing, the code is currently assigning -a "None" value and later on evaluates the max between this value and -msSFU30MaxUidNumber. The max function cannot compare None and a list -of string and triggers an exception. - -To avoid the exception, assign [b'0'] to max gid if msSFU30MaxGidNumber -is missing. This way, the comparison succeeds and max returns the -value from msSFU30MaxUidNumber. - -Fixes: https://pagure.io/freeipa/issue/9310 -Signed-off-by: Florence Blanc-Renaud -Reviewed-By: Rob Crittenden ---- - ipaserver/plugins/trust.py | 5 ++++- - 1 file changed, 4 insertions(+), 1 deletion(-) - -diff --git a/ipaserver/plugins/trust.py b/ipaserver/plugins/trust.py -index c074f6d6e609476e416c95bcbe607654718ae9ce..79264b8d8a3b15dd4e5d0553e4ce42194b0ae044 100644 ---- a/ipaserver/plugins/trust.py -+++ b/ipaserver/plugins/trust.py -@@ -379,7 +379,10 @@ def add_range(myapi, trustinstance, range_name, dom_sid, *keys, **options): - range_type = u'ipa-ad-trust-posix' - - max_uid = info.get('msSFU30MaxUidNumber') -- max_gid = info.get('msSFU30MaxGidNumber', None) -+ # if max_gid is missing, assume 0 and the max will -+ # be obtained from max_uid. We just checked that -+ # msSFU30MaxUidNumber is defined -+ max_gid = info.get('msSFU30MaxGidNumber', [b'0']) - max_id = int(max(max_uid, max_gid)[0]) - - base_id = int(info.get('msSFU30OrderNumber')[0]) --- -2.39.1 - diff --git a/SOURCES/0006-Upgrade-add-PKI-drop-in-file-if-missing.patch b/SOURCES/0006-Upgrade-add-PKI-drop-in-file-if-missing.patch new file mode 100644 index 0000000..407363f --- /dev/null +++ b/SOURCES/0006-Upgrade-add-PKI-drop-in-file-if-missing.patch @@ -0,0 +1,38 @@ +From f25003a730c0e28c22fae5fce607df734b55525c Mon Sep 17 00:00:00 2001 +From: Florence Blanc-Renaud +Date: Mon, 19 Jun 2023 19:01:25 +0200 +Subject: [PATCH] Upgrade: add PKI drop-in file if missing + +During the installation of IPA server, the installer adds a drop-in +file in /etc/systemd/system/pki-tomcatd@pki-tomcat.service.d/ipa.conf +that ensures the CA is reachable before the start command returns. +If the file is missing (for instance because the server was installed +with an old version before this drop-in was created), the upgrade +should add the file. + +Fixes: https://pagure.io/freeipa/issue/9381 + +Signed-off-by: Florence Blanc-Renaud +Reviewed-By: Rob Crittenden +--- + ipaserver/install/server/upgrade.py | 4 ++++ + 1 file changed, 4 insertions(+) + +diff --git a/ipaserver/install/server/upgrade.py b/ipaserver/install/server/upgrade.py +index f8701c8a0d43c7c1c1090e8576976b1c370b0104..8f3d57353605f28103c69cb0a34bf1c16fc4ae19 100644 +--- a/ipaserver/install/server/upgrade.py ++++ b/ipaserver/install/server/upgrade.py +@@ -1742,6 +1742,10 @@ def upgrade_configuration(): + os.path.join(paths.USR_SHARE_IPA_DIR, + "ipa-kdc-proxy.conf.template")) + if ca.is_configured(): ++ # Ensure that the drop-in file is present ++ if not os.path.isfile(paths.SYSTEMD_PKI_TOMCAT_IPA_CONF): ++ ca.add_ipa_wait() ++ + # Handle upgrade of AJP connector configuration + rewrite = ca.secure_ajp_connector() + if ca.ajp_secret: +-- +2.41.0 + diff --git a/SOURCES/0006-doc-Design-for-certificate-pruning.patch b/SOURCES/0006-doc-Design-for-certificate-pruning.patch deleted file mode 100644 index 07eb3c9..0000000 --- a/SOURCES/0006-doc-Design-for-certificate-pruning.patch +++ /dev/null @@ -1,337 +0,0 @@ -From 51b1c22d025bf40e9ef488bb0faf0c8dff303ccd Mon Sep 17 00:00:00 2001 -From: Rob Crittenden -Date: Thu, 8 Dec 2022 16:18:07 -0500 -Subject: [PATCH] doc: Design for certificate pruning - -This describes how the certificate pruning capability of PKI -introduced in v11.3.0 will be integrated into IPA, primarily for -ACME. - -Related: https://pagure.io/freeipa/issue/9294 - -Signed-off-by: Rob Crittenden -Reviewed-By: Florence Blanc-Renaud ---- - doc/designs/expired_certificate_pruning.md | 297 +++++++++++++++++++++ - doc/designs/index.rst | 1 + - 2 files changed, 298 insertions(+) - create mode 100644 doc/designs/expired_certificate_pruning.md - -diff --git a/doc/designs/expired_certificate_pruning.md b/doc/designs/expired_certificate_pruning.md -new file mode 100644 -index 0000000000000000000000000000000000000000..2c10d914020d3c12b6abb028323cd6796ec33e00 ---- /dev/null -+++ b/doc/designs/expired_certificate_pruning.md -@@ -0,0 +1,297 @@ -+# Expired Certificate Pruning -+ -+## Overview -+ -+https://pagure.io/dogtagpki/issue/1750 -+ -+When using short-lived certs and regular issuance, the expired certs can build up in the PKI database and cause issues with replication, performance and overall database size. -+ -+PKI has provided a new feature in 11.3.0, pruning, which is a job that can be executed on a schedule or manually to remove expired certificates and requests. -+ -+Random Serial Numbers v3 (RSNv3) is mandatory to enable pruning. -+ -+Both pruning and RSNv3 require PKI 11.3.0 or higher. -+ -+## Use Cases -+ -+ACME certificates in particular are generally short-lived and expired certificates can build up quickly in a dynamic environment. An example is a CI system that requests one or more certificates per run. These will build up infinitely without a way to remove the expired certificates. -+ -+Another case is simply a very long-lived installation. Over time as hosts come and go certificates build up. -+ -+## How to Use -+ -+https://github.com/dogtagpki/pki/wiki/Configuring-CA-Database-Pruning provides a thorough description of the capabilities of the pruning job. -+ -+The default configuration is to remove expired certificates and incomplete requests after 30 days. -+ -+Pruning is disabled by default. -+ -+Configuration is a four-step process: -+ -+1. Configure the expiration thresholds -+2. Enable the job -+3. Schedule the job -+4. Restart the CA -+ -+The job will be scheduled to use the PKI built-in cron-like timer. It is configured nearly identically to `crontab(5)`. On execution it will remove certificates and requests that fall outside the configured thresholds. LDAP search/time limits can be used to control how many are removed at once. -+ -+In addition to the automated schedule it is possible to manually run the pruning job. -+ -+The tool will not restart the CA. It will be left as an exercise for the user, who will be notified as needed. -+ -+### Where to use -+ -+The pruning configuration is not replicated. It should not be necessary to enable this task on all IPA servers, or more than one. -+ -+Running the task simultaneously on multiple servers has a few downsides: -+ -+* Additional stress on the LDAP server searching for expired certificates and requests -+* Unnecessary replication load deleting the same entries on multiple servers -+ -+While enabling this on a single server represents a single-point-of-failure there should be no catastrophic consequences other than expired certificates and requests potentially building up. This can be cleared by enabling pruning on a different server. Depending on the size of the backlog this could take a couple of executions to catch up. -+ -+## Design -+ -+There are several operations, most of which act locally and one of which uses the PKI REST API. -+ -+1. Updating the job configuration (enable, thresholds, etc). This will be done by running the `pki-server ca-config-set` command which modifies CS.cfg directly per the PKI wiki. A restart is required. -+ -+2. Retrieving the current configuration for display. The `pki-server ca-config-find` command returns the entire configuration so the results will need to be filtered. -+ -+3. Managing the job. This can be done using the REST API, https://github.com/dogtagpki/pki/wiki/PKI-REST-API . Operations include enabling the job and triggering it to run now. -+ -+Theoretically for operations 1 and 2 we could use existing code to manually update `CS.cfg` and retrieve values. For future-proofing purposes calling `pki-server` is probably the better long-term option given the limited number of times this will be used. Configuration is likely to be one and done. -+ -+There are four values each that can be managed for pruning certificates and requests: -+ -+* expired cert/incomplete request time -+* time unit -+* LDAP search size limit -+* LDAP search time limit -+ -+The first two configure when an expired certificate or incomplete request will be deleted. The unit can be one of: minute, hour, day, year. By default it is 30 days. -+ -+The LDAP limits control how many entries are returned and how long the search can take. By default it is 1000 entries and unlimited time. -+ -+### Configuration settings -+ -+The configuration values will be set by running `pki-server ca-config-set` This will ensure best forward compatibility. The options are case-sensitive and not validated by the CA until restart. The values are not applied until the CA is restarted. -+ -+### Configuring job execution time -+ -+The CA provides a cron-like interface for scheduling jobs. To configure the job to run at midnight on the first of every month the PKI equivalent command-line is: -+ -+``` -+pki-server ca-config-set jobsScheduler.job.pruning.cron `"0 0 1 * *"` -+``` -+ -+This will be the default when pruning is enabled. A separate configuration option will be available for fine-tuning execution time. -+ -+The format is defined https://access.redhat.com/documentation/en-us/red_hat_certificate_system/9/html/administration_guide/setting_up_specific_jobs#Frequency_Settings_for_Automated_Jobs -+ -+### REST Authentication and Authorization -+ -+The REST API for pruning is documented at https://github.com/dogtagpki/pki/wiki/PKI-Start-Job-REST-API -+ -+A PKI job can define an owner that can manage the job over the REST API. We will automatically define the owner as `ipara` when pruning is enabled. -+ -+Manually running the job will be done using the PKI REST API. Authentication to this API for our purposes is done at the `/ca/rest/account/login` endpoint. A cookie is returned which will be used in any subsequent calls. The IPA RA agent certificate will be used for authentication and authorization. -+ -+### Commands -+ -+This will be implemented in the ipa-acme-manage command. While strictly not completely ACME-related this is the primary driver for pruning. -+ -+A new verb will be added, pruning, to be used for enabling and configuring pruning. -+ -+### Enabling pruning -+ -+`# ipa-acme-manage pruning --enable=TRUE` -+ -+Enabling the job will call -+ -+`# pki-server ca-config-set jobsScheduler.job.pruning.enabled true` -+ -+This will also set jobsScheduler.job.pruning.cron to `"0 0 1 * *"` if it has not already been set. -+ -+Additionally it will set the job owner to `ipara` with: -+ -+`# pki-server ca-config-set jobsScheduler.job.pruning.owner ipara` -+ -+Disabling the job will call -+ -+`# pki-server ca-config-unset jobsScheduler.job.pruning.enabled` -+ -+### Cron settings -+ -+To modify the cron settings: -+ -+`# ipa-acme-manage pruning --cron="Minute Hour Day_of_month Month_of_year Day_of_week"` -+ -+Validation of the value will be: -+* each of the options is an integer -+* minute is within 0-59 -+* hour is within 0-23 -+* day of month is within 0-31 -+* month of year is within 1-12 -+* day of week is within 0-6 -+ -+No validation of setting February 31st will be done. That will be left to PKI. Buyer beware. -+ -+### Disabling pruning -+ -+`$ ipa-acme-manage pruning --enable=FALSE` -+ -+This will remove the configuration option for `jobsScheduler.job.pruning.cron` just to be sure it no longer runs. -+ -+### Configuration -+ -+#### Pruning certificates -+ -+`$ ipa-acme-manage pruning --certretention=VALUE --certretentionunit=UNIT` -+ -+will be the equivalent of: -+ -+`$ pki-server ca-config-set jobsScheduler.job.pruning.certRetentionTime 30` -+ -+`$ pki-server ca-config-set jobsScheduler.job.pruning.certRetentionUnit day` -+ -+The unit will always be required when modifying the time. -+ -+`$ ipa-acme-manage pruning --certsearchsizelimit=VALUE --certsearchtimelimit=VALUE` -+ -+will be the equivalent of: -+ -+`$ pki-server ca-config-set jobsScheduler.job.pruning.certSearchSizeLimit 1000` -+ -+`$ pki-server ca-config-set jobsScheduler.job.pruning.certSearchTimeLimit 0` -+ -+A value of 0 for searchtimelimit is unlimited. -+ -+#### Pruning requests -+ -+`$ ipa-acme-manage pruning --requestretention=VALUE --requestretentionunit=UNIT` -+ -+will be the equivalent of: -+ -+`$ pki-server ca-config-set jobsScheduler.job.pruning.requestRetentionTime 30` -+ -+`$ pki-server ca-config-set jobsScheduler.job.pruning.requestRetentionUnit day` -+ -+The unit will always be required when modifying the time. -+ -+`$ ipa-acme-manage pruning --requestsearchsizelimit=VALUE --requestsearchtimelimit=VALUE` -+ -+ -+will be the equivalent of: -+ -+`$ pki-server ca-config-set jobsScheduler.job.pruning.requestSearchSizeLimit 1000` -+ -+`$ pki-server ca-config-set jobsScheduler.job.pruning.requestSearchTimeLimit 0` -+ -+A value of 0 for searchtimelimit is unlimited. -+ -+These options set the client-side limits. The server imposes its own search size and look through limits. This can be tuned for the uid=pkidbuser,ou=people,o=ipaca user via https://access.redhat.com/documentation/en-us/red_hat_directory_server/11/html/administration_guide/ldapsearch-ex-complex-range -+ -+### Showing the Configuration -+ -+To display the current configuration run `pki-server ca-config-find` and filter the results to only those that contain `jobsScheduler.job.pruning`. -+ -+Default values are not included so will need to be set by `ipa-acme-manage` before displaying. -+ -+Output may look something like: -+ -+```console -+# ipa-acme-manage pruning --config-show -+Enabled: TRUE -+Certificate retention time: 30 days -+Certificate search size limit: 1000 -+Certificate search time limit: 0 -+Request retention time: 30 days -+Request search size limit: 1000 -+Request search time limit: 0 -+Cron: 0 0 1 * * -+``` -+ -+## Implementation -+ -+For online REST operations (login, run job) we will use the `ipaserver/plugins/dogtag.py::RestClient` class to manage the requests. This will take care of the authentication cookie, etc. -+ -+The class uses dogtag.https_request() will can take PEM cert and key files as arguments. These will be used for authentication. -+ -+For the non-REST operations (configuration, cron settings) the tool will fork out to pki-server ca-config-set. -+ -+### UI -+ -+This will only be configurable on the command-line. -+ -+### CLI -+ -+Overview of the CLI commands. Example: -+ -+ -+| Command | Options | -+| --- | ----- | -+| ipa-acme-manage pruning | --enable=TRUE | -+| ipa-acme-manage pruning | --enable=FALSE | -+| ipa-acme-manage pruning | --cron=`"0 0 1 * *"` | -+| ipa-acme-manage pruning | --certretention=30 --certretentionunit=day | -+| ipa-acme-manage pruning | --certsearchsizelimit=1000 --certsearchtimelimit=0 | -+| ipa-acme-manage pruning | --requestretention=30 --requestretentionunit=day | -+| ipa-acme-manage pruning | --requestsearchsizelimit=1000 --requestsearchtimelimit=0 | -+| ipa-acme-manage pruning | --config-show | -+ -+ipa-acme-manage can only be run as root. -+ -+### Configuration -+ -+Configuration changes will be made to /etc/pki/pki-tomcat/ca/CS.cfg -+ -+## Upgrade -+ -+No expected impact on upgrades. -+ -+## Test plan -+ -+Testing will consist of: -+ -+* Use the default configuration -+* enabling the pruning job -+* issue one or more certificates -+* move time forward +1 days after expiration -+* manually running the job -+* validating that the certificates are removed -+ -+For size/time limit testing, create a large number of certificates/requests and set the search limit to a low value, then ensure that the number of deleted certs is equal to the search limit. Testing timelimit in this way may be less predictable as it may require a massive number of entries to find to timeout on a non-busy server. -+ -+## Troubleshooting and debugging -+ -+The PKI debug log will contain job information. -+ -+``` -+2022-12-08 21:14:25 [https-jsse-nio-8443-exec-8] INFO: JobService: Starting job pruning -+2022-12-08 21:14:25 [https-jsse-nio-8443-exec-8] INFO: JobService: - principal: null -+2022-12-08 21:14:51 [https-jsse-nio-8443-exec-10] INFO: JobService: Starting job pruning 2022-12-08 21:14:51 [https-jsse-nio-8443-exec-10] INFO: JobService: - principal: null -+2022-12-08 21:15:11 [https-jsse-nio-8443-exec-11] INFO: PKIRealm: Authenticating certificate chain: -+2022-12-08 21:15:11 [https-jsse-nio-8443-exec-11] INFO: PKIRealm: - CN=IPA RA,O=EXAMPLE.TEST -+2022-12-08 21:15:11 [https-jsse-nio-8443-exec-11] INFO: PKIRealm: - CN=Certificate Authority,O=EXAMPLE.TEST -+2022-12-08 21:15:11 [https-jsse-nio-8443-exec-11] INFO: LDAPSession: Retrieving cn=19072098145751813471503860299601579276,ou=certificateRepository, ou=ca,o=ipaca -+2022-12-08 21:15:11 [https-jsse-nio-8443-exec-11] INFO: CertUserDBAuthentication: UID ipara authenticated. -+2022-12-08 21:15:11 [https-jsse-nio-8443-exec-11] INFO: PKIRealm: User ipara authenticated -+2022-12-08 21:15:11 [https-jsse-nio-8443-exec-11] INFO: UGSubsystem: Retrieving user uid=ipara,ou=People,o=ipaca -+2022-12-08 21:15:11 [https-jsse-nio-8443-exec-11] INFO: PKIRealm: User DN: uid=ipara,ou=people,o=ipaca -+2022-12-08 21:15:11 [https-jsse-nio-8443-exec-11] INFO: PKIRealm: Roles: -+2022-12-08 21:15:11 [https-jsse-nio-8443-exec-11] INFO: PKIRealm: - Certificate Manager Agents -+2022-12-08 21:15:11 [https-jsse-nio-8443-exec-11] INFO: PKIRealm: - Registration Manager Agents -+2022-12-08 21:15:11 [https-jsse-nio-8443-exec-11] INFO: PKIRealm: - Security Domain Administrators -+2022-12-08 21:15:11 [https-jsse-nio-8443-exec-11] INFO: PKIRealm: - Enterprise ACME Administrators -+2022-12-08 21:15:24 [https-jsse-nio-8443-exec-12] INFO: JobService: Starting job pruning -+2022-12-08 21:15:24 [https-jsse-nio-8443-exec-12] INFO: JobService: - principal: GenericPrincipal[ipara(Certificate Manager Agents,Enterprise ACME Administrators,Registration Manager Agents,Security Domain Administrators,)] -+2022-12-08 21:15:24 [https-jsse-nio-8443-exec-12] INFO: JobsScheduler: Starting job pruning -+2022-12-08 21:15:24 [pruning] INFO: PruningJob: Running pruning job at Thu Dec 08 21:15:24 UTC 2022 -+2022-12-08 21:15:24 [pruning] INFO: PruningJob: Pruning certs expired before Tue Nov 08 21:15:24 UTC 2022 -+2022-12-08 21:15:24 [pruning] INFO: PruningJob: - filter: (&(x509Cert.notAfter<=1667942124527)(!(x509Cert.notAfter=1667942124527))) -+2022-12-08 21:15:24 [pruning] INFO: LDAPSession: Searching ou=certificateRepository, ou=ca,o=ipaca for (&(notAfter<=20221108211524Z)(!(notAfter=20221108211524Z))) -+2022-12-08 21:15:24 [pruning] INFO: PruningJob: Pruning incomplete requests last modified before Tue Nov 08 21:15:24 UTC 2022 -+2022-12-08 21:15:24 [pruning] INFO: PruningJob: - filter: (&(!(requestState=complete))(requestModifyTime<=1667942124527)(!(requestModifyTime=1667942124527))) -+2022-12-08 21:15:24 [pruning] INFO: LDAPSession: Searching ou=ca, ou=requests,o=ipaca for (&(!(requestState=complete))(dateOfModify<=20221108211524Z)(!(dateOfModify=20221108211524Z))) -+``` -diff --git a/doc/designs/index.rst b/doc/designs/index.rst -index 570e526fe35d510feeac62a44dd59224289e0506..1d41c0f84f0d7d3d5f184a47e31b4e71a890805d 100644 ---- a/doc/designs/index.rst -+++ b/doc/designs/index.rst -@@ -14,6 +14,7 @@ FreeIPA design documentation - hsm.md - krb-ticket-policy.md - extdom-plugin-protocol.md -+ expired_certificate_pruning.md - expiring-password-notification.md - ldap_grace_period.md - ldap_pam_passthrough.md --- -2.39.1 - diff --git a/SOURCES/0007-Integration-test-add-a-test-for-upgrade-and-PKI-drop.patch b/SOURCES/0007-Integration-test-add-a-test-for-upgrade-and-PKI-drop.patch new file mode 100644 index 0000000..1e2912d --- /dev/null +++ b/SOURCES/0007-Integration-test-add-a-test-for-upgrade-and-PKI-drop.patch @@ -0,0 +1,53 @@ +From 392e60e3fa0e39a2e364268a21d869a2f3a85905 Mon Sep 17 00:00:00 2001 +From: Florence Blanc-Renaud +Date: Mon, 19 Jun 2023 19:04:32 +0200 +Subject: [PATCH] Integration test: add a test for upgrade and PKI drop-in file + +Add an upgrade test with the following scenario: +- remove PKI drop-in file (to simulate an upgrade from an old +version) +- remove caECServerCertWithSCT profile from LDAP +- launch the ipa-server-upgrade command +- check that the upgrade added the file + +Related: https://pagure.io/freeipa/issue/9381 + +Signed-off-by: Florence Blanc-Renaud +Reviewed-By: Rob Crittenden +--- + ipatests/test_integration/test_upgrade.py | 22 ++++++++++++++++++++++ + 1 file changed, 22 insertions(+) + +diff --git a/ipatests/test_integration/test_upgrade.py b/ipatests/test_integration/test_upgrade.py +index 9203503cdccf4478f9864bd487e458761e9a2a2f..182e3b5da3c758cc10913ad4eed119b0983fcc23 100644 +--- a/ipatests/test_integration/test_upgrade.py ++++ b/ipatests/test_integration/test_upgrade.py +@@ -455,3 +455,25 @@ class TestUpgrade(IntegrationTest): + assert 'tXTRecord' in location_krb_rec + assert len(location_krb_rec['tXTRecord']) == 1 + assert location_krb_rec['tXTRecord'][0] == f'"{realm}"' ++ ++ def test_pki_dropin_file(self): ++ """Test that upgrade adds the drop-in file if missing ++ ++ Test for ticket 9381 ++ Simulate an update from a version that didn't provide ++ /etc/systemd/system/pki-tomcatd@pki-tomcat.service.d/ipa.conf, ++ remove one of the certificate profiles from LDAP and check that upgrade ++ completes successfully and adds the missing file. ++ When the drop-in file is missing, the upgrade tries to login to ++ PKI in order to migrate the profile and fails because PKI failed to ++ start. ++ """ ++ self.master.run_command(["rm", "-f", paths.SYSTEMD_PKI_TOMCAT_IPA_CONF]) ++ ldif = textwrap.dedent(""" ++ dn: cn=caECServerCertWithSCT,ou=certificateProfiles,ou=ca,o=ipaca ++ changetype: delete ++ """) ++ tasks.ldapmodify_dm(self.master, ldif) ++ self.master.run_command(['ipa-server-upgrade']) ++ assert self.master.transport.file_exists( ++ paths.SYSTEMD_PKI_TOMCAT_IPA_CONF) +-- +2.41.0 + diff --git a/SOURCES/0007-ipa-acme-manage-add-certificate-request-pruning-mana.patch b/SOURCES/0007-ipa-acme-manage-add-certificate-request-pruning-mana.patch deleted file mode 100644 index cbc2095..0000000 --- a/SOURCES/0007-ipa-acme-manage-add-certificate-request-pruning-mana.patch +++ /dev/null @@ -1,684 +0,0 @@ -From 9246a8a003b2b0062e07c289cd7cde8fe902b16f Mon Sep 17 00:00:00 2001 -From: Rob Crittenden -Date: Thu, 12 Jan 2023 15:06:27 -0500 -Subject: [PATCH] ipa-acme-manage: add certificate/request pruning management - -Configures PKI to remove expired certificates and non-resolved -requests on a schedule. - -This is geared towards ACME which can generate a lot of certificates -over a short period of time but is general purpose. It lives in -ipa-acme-manage because that is the primary reason for including it. - -Random Serial Numbers v3 must be enabled for this to work. - -Enabling pruning enables the job scheduler within CS and sets the -job user as the IPA RA user which has full rights to certificates -and requests. - -Disabling pruning does not disable the job scheduler because the -tool is stateless. Having the scheduler enabled should not be a -problem. - -A restart of PKI is required to apply any changes. This tool forks -out to pki-server which does direct writes to CS.cfg. It might -be easier to use our own tooling for this but this makes the -integration tighter so we pick up any improvements in PKI. - -The "cron" setting is quite limited, taking only integer values -and *. It does not accept ranges, either - or /. - -No error checking is done in PKI when setting a value, only when -attempting to use it, so some rudimentary validation is done. - -Fixes: https://pagure.io/freeipa/issue/9294 - -Signed-off-by: Rob Crittenden rcritten@redhat.com -Reviewed-By: Florence Blanc-Renaud ---- - install/tools/man/ipa-acme-manage.1 | 83 +++++++ - ipaserver/install/ipa_acme_manage.py | 303 ++++++++++++++++++++++++- - ipatests/test_integration/test_acme.py | 158 +++++++++++++ - 3 files changed, 534 insertions(+), 10 deletions(-) - -diff --git a/install/tools/man/ipa-acme-manage.1 b/install/tools/man/ipa-acme-manage.1 -index e15d25bd0017d8bd71e425fcb633827fa6f67693..e6cec4e4a7fd460c514a72456a2dc9a2e3682ebd 100644 ---- a/install/tools/man/ipa-acme-manage.1 -+++ b/install/tools/man/ipa-acme-manage.1 -@@ -27,6 +27,89 @@ Disable the ACME service on this host. - .TP - \fBstatus\fR - Display the status of the ACME service. -+.TP -+\fBpruning\fR -+Configure certificate and request pruning. -+ -+.SH "PRUNING" -+Pruning is a job that runs in the CA that can remove expired -+certificates and certificate requests which have not been issued. -+This is particularly important when using short-lived certificates -+like those issued with the ACME protocol. Pruning requires that -+the IPA server be installed with random serial numbers enabled. -+ -+The CA needs to be restarted after modifying the pruning configuration. -+ -+The job is a cron-like task within the CA that is controlled by a -+number of options which dictate how long after the certificate or -+request is considered no longer valid and removed from the LDAP -+database. -+ -+The cron time and date fields are: -+.IP -+.ta 1.5i -+field allowed values -+.br -+----- -------------- -+.br -+minute 0-59 -+.br -+hour 0-23 -+.br -+day of month 1-31 -+.br -+month 1-12 -+.br -+day of week 0-6 (0 is Sunday) -+.br -+.PP -+ -+The cron syntax is limited to * or specific numbers. Ranges are not supported. -+ -+.TP -+\fB\-\-enable\fR -+Enable certificate pruning. -+.TP -+\fB\-\-disable\fR -+Disable certificate pruning. -+.TP -+\fB\-\-cron=CRON\fR -+Configure the pruning cron job. The syntax is similar to crontab(5) syntax. -+For example, "0 0 1 * *" schedules the job to run at 12:00am on the first -+day of each month. -+.TP -+\fB\-\-certretention=CERTRETENTION\fR -+Certificate retention time. The default is 30. -+.TP -+\fB\-\-certretentionunit=CERTRETENTIONUNIT\fR -+Certificate retention units. Valid units are: minute, hour, day, year. -+The default is days. -+.TP -+\fB\-\-certsearchsizelimit=CERTSEARCHSIZELIMIT\fR -+LDAP search size limit searching for expired certificates. The default is 1000. This is a client-side limit. There may be additional server-side limitations. -+.TP -+\fB\-\-certsearchtimelimit=CERTSEARCHTIMELIMIT\fR -+LDAP search time limit searching for expired certificates. The default is 0, no limit. This is a client-side limit. There may be additional server-side limitations. -+.TP -+\fB\-\-requestretention=REQUESTRETENTION\fR -+Request retention time. The default is 30. -+.TP -+\fB\-\-requestretentionunit=REQUESTRETENTIONUNIT\fR -+Request retention units. Valid units are: minute, hour, day, year. -+The default is days. -+.TP -+\fB\-\-requestsearchsizelimit=REQUESTSEARCHSIZELIMIT\fR -+LDAP search size limit searching for unfulfilled requests. The default is 1000. There may be additional server-side limitations. -+.TP -+\fB\-\-requestsearchtimelimit=REQUESTSEARCHTIMELIMIT\fR -+LDAP search time limit searching for unfulfilled requests. The default is 0, no limit. There may be additional server-side limitations. -+.TP -+\fB\-\-config\-show\fR -+Show the current pruning configuration -+.TP -+\fB\-\-run\fR -+Run the pruning job now. The IPA RA certificate is used to authenticate to the PKI REST backend. -+ - - .SH "EXIT STATUS" - 0 if the command was successful -diff --git a/ipaserver/install/ipa_acme_manage.py b/ipaserver/install/ipa_acme_manage.py -index 0474b9f4a051063ac6df41a81877a2af9d4a2096..b7b2111d9edcec2580aa4a485d7a7340146ff065 100644 ---- a/ipaserver/install/ipa_acme_manage.py -+++ b/ipaserver/install/ipa_acme_manage.py -@@ -2,7 +2,12 @@ - # Copyright (C) 2020 FreeIPA Contributors see COPYING for license - # - -+ - import enum -+import pki.util -+import logging -+ -+from optparse import OptionGroup # pylint: disable=deprecated-module - - from ipalib import api, errors, x509 - from ipalib import _ -@@ -10,10 +15,64 @@ from ipalib.facts import is_ipa_configured - from ipaplatform.paths import paths - from ipapython.admintool import AdminTool - from ipapython import cookie, dogtag -+from ipapython.ipautil import run -+from ipapython.certdb import NSSDatabase, EXTERNAL_CA_TRUST_FLAGS - from ipaserver.install import cainstance -+from ipaserver.install.ca import lookup_random_serial_number_version - - from ipaserver.plugins.dogtag import RestClient - -+logger = logging.getLogger(__name__) -+ -+default_pruning_options = { -+ 'certRetentionTime': '30', -+ 'certRetentionUnit': 'day', -+ 'certSearchSizeLimit': '1000', -+ 'certSearchTimeLimit': '0', -+ 'requestRetentionTime': 'day', -+ 'requestRetentionUnit': '30', -+ 'requestSearchSizeLimit': '1000', -+ 'requestSearchTimeLimit': '0', -+ 'cron': '' -+} -+ -+pruning_labels = { -+ 'certRetentionTime': 'Certificate Retention Time', -+ 'certRetentionUnit': 'Certificate Retention Unit', -+ 'certSearchSizeLimit': 'Certificate Search Size Limit', -+ 'certSearchTimeLimit': 'Certificate Search Time Limit', -+ 'requestRetentionTime': 'Request Retention Time', -+ 'requestRetentionUnit': 'Request Retention Unit', -+ 'requestSearchSizeLimit': 'Request Search Size Limit', -+ 'requestSearchTimeLimit': 'Request Search Time Limit', -+ 'cron': 'cron Schedule' -+} -+ -+ -+def validate_range(val, min, max): -+ """dogtag appears to have no error checking in the cron -+ entry so do some minimum amount of validation. It is -+ left as an exercise for the user to do month/day -+ validation so requesting Feb 31 will be accepted. -+ -+ Only * and a number within a min/max range are allowed. -+ """ -+ if val == '*': -+ return -+ -+ if '-' in val or '/' in val: -+ raise ValueError(f"{val} ranges are not supported") -+ -+ try: -+ int(val) -+ except ValueError: -+ # raise a clearer error -+ raise ValueError(f"{val} is not a valid integer") -+ -+ if int(val) < min or int(val) > max: -+ raise ValueError(f"{val} not within the range {min}-{max}") -+ -+ - # Manages the FreeIPA ACME service on a per-server basis. - # - # This program is a stop-gap until the deployment-wide management of -@@ -66,32 +125,121 @@ class acme_state(RestClient): - status, unused, _unused = self._request('/acme/disable', - headers=headers) - if status != 200: -- raise RuntimeError('Failed to disble ACME') -+ raise RuntimeError('Failed to disable ACME') - - - class Command(enum.Enum): - ENABLE = 'enable' - DISABLE = 'disable' - STATUS = 'status' -+ PRUNE = 'pruning' - - - class IPAACMEManage(AdminTool): - command_name = "ipa-acme-manage" -- usage = "%prog [enable|disable|status]" -+ usage = "%prog [enable|disable|status|pruning]" - description = "Manage the IPA ACME service" - -+ @classmethod -+ def add_options(cls, parser): -+ -+ group = OptionGroup(parser, 'Pruning') -+ group.add_option( -+ "--enable", dest="enable", action="store_true", -+ default=False, help="Enable certificate pruning") -+ group.add_option( -+ "--disable", dest="disable", action="store_true", -+ default=False, help="Disable certificate pruning") -+ group.add_option( -+ "--cron", dest="cron", action="store", -+ default=None, help="Configure the pruning cron job") -+ group.add_option( -+ "--certretention", dest="certretention", action="store", -+ default=None, help="Certificate retention time", type=int) -+ group.add_option( -+ "--certretentionunit", dest="certretentionunit", action="store", -+ choices=['minute', 'hour', 'day', 'year'], -+ default=None, help="Certificate retention units") -+ group.add_option( -+ "--certsearchsizelimit", dest="certsearchsizelimit", -+ action="store", -+ default=None, help="LDAP search size limit", type=int) -+ group.add_option( -+ "--certsearchtimelimit", dest="certsearchtimelimit", action="store", -+ default=None, help="LDAP search time limit", type=int) -+ group.add_option( -+ "--requestretention", dest="requestretention", action="store", -+ default=None, help="Request retention time", type=int) -+ group.add_option( -+ "--requestretentionunit", dest="requestretentionunit", -+ choices=['minute', 'hour', 'day', 'year'], -+ action="store", default=None, help="Request retention units") -+ group.add_option( -+ "--requestsearchsizelimit", dest="requestsearchsizelimit", -+ action="store", -+ default=None, help="LDAP search size limit", type=int) -+ group.add_option( -+ "--requestsearchtimelimit", dest="requestsearchtimelimit", -+ action="store", -+ default=None, help="LDAP search time limit", type=int) -+ group.add_option( -+ "--config-show", dest="config_show", action="store_true", -+ default=False, help="Show the current pruning configuration") -+ group.add_option( -+ "--run", dest="run", action="store_true", -+ default=False, help="Run the pruning job now") -+ parser.add_option_group(group) -+ super(IPAACMEManage, cls).add_options(parser, debug_option=True) -+ -+ - def validate_options(self): -- # needs root now - if/when this program changes to an API -- # wrapper we will no longer need root. - super(IPAACMEManage, self).validate_options(needs_root=True) - - if len(self.args) < 1: - self.option_parser.error(f'missing command argument') -- else: -- try: -- self.command = Command(self.args[0]) -- except ValueError: -- self.option_parser.error(f'unknown command "{self.args[0]}"') -+ -+ if self.args[0] == "pruning": -+ if self.options.enable and self.options.disable: -+ self.option_parser.error("Cannot both enable and disable") -+ elif ( -+ any( -+ [ -+ self.options.enable, -+ self.options.disable, -+ self.options.cron, -+ self.options.certretention, -+ self.options.certretentionunit, -+ self.options.requestretention, -+ self.options.requestretentionunit, -+ self.options.certsearchsizelimit, -+ self.options.certsearchtimelimit, -+ self.options.requestsearchsizelimit, -+ self.options.requestsearchtimelimit, -+ ] -+ ) -+ and (self.options.config_show or self.options.run) -+ ): -+ -+ self.option_parser.error( -+ "Cannot change and show config or run at the same time" -+ ) -+ elif self.options.cron: -+ if len(self.options.cron.split()) != 5: -+ self.option_parser.error("Invalid format for --cron") -+ # dogtag does no validation when setting an option so -+ # do the minimum. The dogtag cron is limited compared to -+ # crontab(5). -+ opt = self.options.cron.split() -+ validate_range(opt[0], 0, 59) -+ validate_range(opt[1], 0, 23) -+ validate_range(opt[2], 1, 31) -+ validate_range(opt[3], 1, 12) -+ validate_range(opt[4], 0, 6) -+ -+ try: -+ self.command = Command(self.args[0]) -+ except ValueError: -+ self.option_parser.error(f'unknown command "{self.args[0]}"') - - def check_san_status(self): - """ -@@ -100,6 +248,140 @@ class IPAACMEManage(AdminTool): - cert = x509.load_certificate_from_file(paths.HTTPD_CERT_FILE) - cainstance.check_ipa_ca_san(cert) - -+ def pruning(self): -+ def run_pki_server(command, directive, prefix, value=None): -+ """Take a set of arguments to append to pki-server""" -+ args = [ -+ 'pki-server', command, -+ f'{prefix}.{directive}' -+ ] -+ if value: -+ args.extend([str(value)]) -+ logger.debug(args) -+ result = run(args, raiseonerr=False, capture_output=True, -+ capture_error=True) -+ if result.returncode != 0: -+ raise RuntimeError(result.error_output) -+ return result -+ -+ def ca_config_set(directive, value, -+ prefix='jobsScheduler.job.pruning'): -+ run_pki_server('ca-config-set', directive, prefix, value) -+ # ca-config-set always succeeds, even if the option is -+ # not supported. -+ newvalue = ca_config_show(directive) -+ if str(value) != newvalue.strip(): -+ raise RuntimeError('Updating %s failed' % directive) -+ -+ def ca_config_show(directive): -+ result = run_pki_server('ca-config-show', directive, -+ prefix='jobsScheduler.job.pruning') -+ return result.output.strip() -+ -+ def config_show(): -+ status = ca_config_show('enabled') -+ if status.strip() == 'true': -+ print("Status: enabled") -+ else: -+ print("Status: disabled") -+ for option in ( -+ 'certRetentionTime', 'certRetentionUnit', -+ 'certSearchSizeLimit', 'certSearchTimeLimit', -+ 'requestRetentionTime', 'requestRetentionUnit', -+ 'requestSearchSizeLimit', 'requestSearchTimeLimit', -+ 'cron', -+ ): -+ value = ca_config_show(option) -+ if value: -+ print("{}: {}".format(pruning_labels[option], value)) -+ else: -+ print("{}: {}".format(pruning_labels[option], -+ default_pruning_options[option])) -+ -+ def run_pruning(): -+ """Run the pruning job manually""" -+ -+ with NSSDatabase() as tmpdb: -+ print("Preparing...") -+ tmpdb.create_db() -+ tmpdb.import_files((paths.RA_AGENT_PEM, paths.RA_AGENT_KEY), -+ import_keys=True) -+ tmpdb.import_files((paths.IPA_CA_CRT,)) -+ for nickname, trust_flags in tmpdb.list_certs(): -+ if trust_flags.has_key: -+ ra_nickname = nickname -+ continue -+ # external is suffucient for our purposes: C,, -+ tmpdb.trust_root_cert(nickname, EXTERNAL_CA_TRUST_FLAGS) -+ print("Starting job...") -+ args = ['pki', '-C', tmpdb.pwd_file, '-d', tmpdb.secdir, -+ '-n', ra_nickname, -+ 'ca-job-start', 'pruning'] -+ logger.debug(args) -+ run(args, stdin='y') -+ -+ pki_version = pki.util.Version(pki.specification_version()) -+ if pki_version < pki.util.Version("11.3.0"): -+ raise RuntimeError( -+ 'Certificate pruning is not supported in PKI version %s' -+ % pki_version -+ ) -+ -+ if lookup_random_serial_number_version(api) == 0: -+ raise RuntimeError( -+ 'Certificate pruning requires random serial numbers' -+ ) -+ -+ if self.options.config_show: -+ config_show() -+ return -+ -+ if self.options.run: -+ run_pruning() -+ return -+ -+ # Don't play the enable/disable at the same time game -+ if self.options.enable: -+ ca_config_set('owner', 'ipara') -+ ca_config_set('enabled', 'true') -+ ca_config_set('enabled', 'true', 'jobsScheduler') -+ elif self.options.disable: -+ ca_config_set('enabled', 'false') -+ -+ # pki-server ca-config-set can only set one option at a time so -+ # loop through all the options and set what is there. -+ if self.options.certretention: -+ ca_config_set('certRetentionTime', -+ self.options.certretention) -+ if self.options.certretentionunit: -+ ca_config_set('certRetentionUnit', -+ self.options.certretentionunit) -+ if self.options.certsearchtimelimit: -+ ca_config_set('certSearchTimeLimit', -+ self.options.certsearchtimelimit) -+ if self.options.certsearchsizelimit: -+ ca_config_set('certSearchSizeLimit', -+ self.options.certsearchsizelimit) -+ if self.options.requestretention: -+ ca_config_set('requestRetentionTime', -+ self.options.requestretention) -+ if self.options.requestretentionunit: -+ ca_config_set('requestRetentionUnit', -+ self.options.requestretentionunit) -+ if self.options.requestsearchsizelimit: -+ ca_config_set('requestSearchSizeLimit', -+ self.options.requestsearchsizelimit) -+ if self.options.requestsearchtimelimit: -+ ca_config_set('requestSearchTimeLimit', -+ self.options.requestsearchtimelimit) -+ if self.options.cron: -+ ca_config_set('cron', self.options.cron) -+ -+ config_show() -+ -+ print("The CA service must be restarted for changes to take effect") -+ -+ - def run(self): - if not is_ipa_configured(): - print("IPA is not configured.") -@@ -123,7 +405,8 @@ class IPAACMEManage(AdminTool): - elif self.command == Command.STATUS: - status = "enabled" if dogtag.acme_status() else "disabled" - print("ACME is {}".format(status)) -- return 0 -+ elif self.command == Command.PRUNE: -+ self.pruning() - else: - raise RuntimeError('programmer error: unhandled enum case') - -diff --git a/ipatests/test_integration/test_acme.py b/ipatests/test_integration/test_acme.py -index 15d7543cfb0fa0fcb921166f7cd8f13d0535a41d..93e785d8febd9fa8d7b3ef87ecb3f2eb42ac5da2 100644 ---- a/ipatests/test_integration/test_acme.py -+++ b/ipatests/test_integration/test_acme.py -@@ -12,6 +12,9 @@ from ipalib.constants import IPA_CA_RECORD - from ipatests.test_integration.base import IntegrationTest - from ipatests.pytest_ipa.integration import tasks - from ipatests.test_integration.test_caless import CALessBase, ipa_certs_cleanup -+from ipatests.test_integration.test_random_serial_numbers import ( -+ pki_supports_RSNv3 -+) - from ipaplatform.osinfo import osinfo - from ipaplatform.paths import paths - from ipatests.test_integration.test_external_ca import ( -@@ -388,6 +391,16 @@ class TestACME(CALessBase): - status = check_acme_status(self.replicas[0], 'disabled') - assert status == 'disabled' - -+ def test_acme_pruning_no_random_serial(self): -+ """This ACME install is configured without random serial -+ numbers. Verify that we can't enable pruning on it.""" -+ self.master.run_command(['ipa-acme-manage', 'enable']) -+ result = self.master.run_command( -+ ['ipa-acme-manage', 'pruning', '--enable'], -+ raiseonerr=False) -+ assert result.returncode == 1 -+ assert "requires random serial numbers" in result.stderr_text -+ - @server_install_teardown - def test_third_party_certs(self): - """Require ipa-ca SAN on replacement web certificates""" -@@ -630,3 +643,148 @@ class TestACMERenew(IntegrationTest): - renewed_expiry = cert.not_valid_after - - assert initial_expiry != renewed_expiry -+ -+ -+class TestACMEPrune(IntegrationTest): -+ """Validate that ipa-acme-manage configures dogtag for pruning""" -+ -+ random_serial = True -+ -+ @classmethod -+ def install(cls, mh): -+ if not pki_supports_RSNv3(mh.master): -+ raise pytest.skip("RNSv3 not supported") -+ tasks.install_master(cls.master, setup_dns=True, -+ random_serial=True) -+ -+ @classmethod -+ def uninstall(cls, mh): -+ if not pki_supports_RSNv3(mh.master): -+ raise pytest.skip("RSNv3 not supported") -+ super(TestACMEPrune, cls).uninstall(mh) -+ -+ def test_enable_pruning(self): -+ if (tasks.get_pki_version(self.master) -+ < tasks.parse_version('11.3.0')): -+ raise pytest.skip("Certificate pruning is not available") -+ cs_cfg = self.master.get_file_contents(paths.CA_CS_CFG_PATH) -+ assert "jobsScheduler.job.pruning.enabled=false".encode() in cs_cfg -+ -+ self.master.run_command(['ipa-acme-manage', 'pruning', '--enable']) -+ -+ cs_cfg = self.master.get_file_contents(paths.CA_CS_CFG_PATH) -+ assert "jobsScheduler.enabled=true".encode() in cs_cfg -+ assert "jobsScheduler.job.pruning.enabled=true".encode() in cs_cfg -+ assert "jobsScheduler.job.pruning.owner=ipara".encode() in cs_cfg -+ -+ def test_pruning_options(self): -+ if (tasks.get_pki_version(self.master) -+ < tasks.parse_version('11.3.0')): -+ raise pytest.skip("Certificate pruning is not available") -+ -+ self.master.run_command( -+ ['ipa-acme-manage', 'pruning', -+ '--certretention=60', -+ '--certretentionunit=minute', -+ '--certsearchsizelimit=2000', -+ '--certsearchtimelimit=5',] -+ ) -+ cs_cfg = self.master.get_file_contents(paths.CA_CS_CFG_PATH) -+ assert ( -+ "jobsScheduler.job.pruning.certRetentionTime=60".encode() -+ in cs_cfg -+ ) -+ assert ( -+ "jobsScheduler.job.pruning.certRetentionUnit=minute".encode() -+ in cs_cfg -+ ) -+ assert ( -+ "jobsScheduler.job.pruning.certSearchSizeLimit=2000".encode() -+ in cs_cfg -+ ) -+ assert ( -+ "jobsScheduler.job.pruning.certSearchTimeLimit=5".encode() -+ in cs_cfg -+ ) -+ -+ self.master.run_command( -+ ['ipa-acme-manage', 'pruning', -+ '--requestretention=60', -+ '--requestretentionunit=minute', -+ '--requestresearchsizelimit=2000', -+ '--requestsearchtimelimit=5',] -+ ) -+ cs_cfg = self.master.get_file_contents(paths.CA_CS_CFG_PATH) -+ assert ( -+ "jobsScheduler.job.pruning.requestRetentionTime=60".encode() -+ in cs_cfg -+ ) -+ assert ( -+ "jobsScheduler.job.pruning.requestRetentionUnit=minute".encode() -+ in cs_cfg -+ ) -+ assert ( -+ "jobsScheduler.job.pruning.requestSearchSizeLimit=2000".encode() -+ in cs_cfg -+ ) -+ assert ( -+ "jobsScheduler.job.pruning.requestSearchTimeLimit=5".encode() -+ in cs_cfg -+ ) -+ -+ self.master.run_command( -+ ['ipa-acme-manage', 'pruning', -+ '--cron="0 23 1 * *',] -+ ) -+ cs_cfg = self.master.get_file_contents(paths.CA_CS_CFG_PATH) -+ assert ( -+ "jobsScheduler.job.pruning.cron=0 23 1 * *".encode() -+ in cs_cfg -+ ) -+ -+ def test_pruning_negative_options(self): -+ """Negative option testing for things we directly cover""" -+ if (tasks.get_pki_version(self.master) -+ < tasks.parse_version('11.3.0')): -+ raise pytest.skip("Certificate pruning is not available") -+ -+ result = self.master.run_command( -+ ['ipa-acme-manage', 'pruning', -+ '--enable', '--disable'], -+ raiseonerr=False -+ ) -+ assert result.returncode == 1 -+ assert "Cannot both enable and disable" in result.stderr_text -+ -+ for cmd in ('--config-show', '--run'): -+ result = self.master.run_command( -+ ['ipa-acme-manage', 'pruning', -+ cmd, '--enable'], -+ raiseonerr=False -+ ) -+ assert result.returncode == 1 -+ assert "Cannot change and show config" in result.stderr_text -+ -+ result = self.master.run_command( -+ ['ipa-acme-manage', 'pruning', -+ '--cron="* *"'], -+ raiseonerr=False -+ ) -+ assert result.returncode == 1 -+ assert "Invalid format format --cron" in result.stderr_text -+ -+ result = self.master.run_command( -+ ['ipa-acme-manage', 'pruning', -+ '--cron="100 * * * *"'], -+ raiseonerr=False -+ ) -+ assert result.returncode == 1 -+ assert "100 not within the range 0-59" in result.stderr_text -+ -+ result = self.master.run_command( -+ ['ipa-acme-manage', 'pruning', -+ '--cron="10 1-5 * * *"'], -+ raiseonerr=False -+ ) -+ assert result.returncode == 1 -+ assert "1-5 ranges are not supported" in result.stderr_text --- -2.39.1 - diff --git a/SOURCES/0008-Uninstaller-uninstall-PKI-before-shutting-down-servi.patch b/SOURCES/0008-Uninstaller-uninstall-PKI-before-shutting-down-servi.patch new file mode 100644 index 0000000..2877404 --- /dev/null +++ b/SOURCES/0008-Uninstaller-uninstall-PKI-before-shutting-down-servi.patch @@ -0,0 +1,129 @@ +From f93a6d3ff52247ce5e582816fec689b8901fc984 Mon Sep 17 00:00:00 2001 +From: Florence Blanc-Renaud +Date: Wed, 14 Jun 2023 15:12:39 +0200 +Subject: [PATCH] Uninstaller: uninstall PKI before shutting down services + +The uninstaller is stopping all the services before +calling pkidestroy to uninstall the CA. +With PKI 11.4+ this sequence fails as pkidestroy tries +to connect to PKI server in order to unregister from the +security domain. The error interrupts the full completion +of pkidestroy, is logged but doesn't make ipa uninstallation +fail. +The issue is that trying to re-install later on would fail because +pkidestroy did not completely uninstall the CA. + +To avoid this, call pkidestroy before shutting down the services. +Also add an uninstall_check method that restarts IPA if it is +not running, and use pkidestroy --force to make sure that PKI +is uninstalled even if restart failed. + +Fixes: https://pagure.io/freeipa/issue/9330 + +Signed-off-by: Florence Blanc-Renaud +Reviewed-By: Rob Crittenden +--- + ipaserver/install/ca.py | 18 ++++++++++++++++++ + ipaserver/install/dogtaginstance.py | 2 +- + ipaserver/install/kra.py | 2 ++ + ipaserver/install/server/install.py | 8 +++++--- + 4 files changed, 26 insertions(+), 4 deletions(-) + +diff --git a/ipaserver/install/ca.py b/ipaserver/install/ca.py +index be0e732e8ff6966ccc0077d9339f9f0bc66ae6ec..c93ae1fce4c8848d493677eafee7952740e51631 100644 +--- a/ipaserver/install/ca.py ++++ b/ipaserver/install/ca.py +@@ -169,6 +169,24 @@ def print_ca_configuration(options): + + + def uninstall_check(options): ++ """IPA needs to be running so pkidestroy can unregister CA""" ++ ca = cainstance.CAInstance(api.env.realm) ++ if not ca.is_installed(): ++ return ++ ++ result = ipautil.run([paths.IPACTL, 'status'], ++ raiseonerr=False) ++ ++ if result.returncode not in [0, 4]: ++ try: ++ logger.info( ++ "Starting services to unregister CA from security domain") ++ ipautil.run([paths.IPACTL, 'start']) ++ except Exception: ++ logger.info("Re-starting IPA failed, continuing uninstall") ++ ++ ++def uninstall_crl_check(options): + """Check if the host is CRL generation master""" + # Skip the checks if the host is not a CA instance + ca = cainstance.CAInstance(api.env.realm) +diff --git a/ipaserver/install/dogtaginstance.py b/ipaserver/install/dogtaginstance.py +index c2c6b3f49243f096448c178fafd09f429f0f46c8..4967aca01807e58dfcc3157af10b92eff5dba206 100644 +--- a/ipaserver/install/dogtaginstance.py ++++ b/ipaserver/install/dogtaginstance.py +@@ -305,7 +305,7 @@ class DogtagInstance(service.Service): + self.print_msg("Unconfiguring %s" % self.subsystem) + + args = [paths.PKIDESTROY, +- "-i", "pki-tomcat", ++ "-i", "pki-tomcat", "--force", + "-s", self.subsystem] + + # specify --log-file on PKI 11.0.0 or later +diff --git a/ipaserver/install/kra.py b/ipaserver/install/kra.py +index 857c5165b808baee3f0815e78828fb899eb78a2d..59cbda812a853997752f7d932e0690e3a950aa1f 100644 +--- a/ipaserver/install/kra.py ++++ b/ipaserver/install/kra.py +@@ -132,6 +132,8 @@ def uninstall_check(options): + + if result.returncode not in [0, 4]: + try: ++ logger.info( ++ "Starting services to unregister KRA from security domain") + ipautil.run([paths.IPACTL, 'start']) + except Exception: + logger.info("Re-starting IPA failed, continuing uninstall") +diff --git a/ipaserver/install/server/install.py b/ipaserver/install/server/install.py +index 4e4076410f1c1af188a0ab3606ef13be39702b7d..ccb958232935de2166f2d4867b626f59d7ba5333 100644 +--- a/ipaserver/install/server/install.py ++++ b/ipaserver/install/server/install.py +@@ -1110,6 +1110,7 @@ def uninstall_check(installer): + raise ScriptError("Aborting uninstall operation.") + + kra.uninstall_check(options) ++ ca.uninstall_check(options) + + try: + api.Backend.ldap2.connect(autobind=True) +@@ -1132,7 +1133,7 @@ def uninstall_check(installer): + else: + dns.uninstall_check(options) + +- ca.uninstall_check(options) ++ ca.uninstall_crl_check(options) + + cleanup_dogtag_server_specific_data() + +@@ -1181,6 +1182,9 @@ def uninstall(installer): + # Uninstall the KRA prior to shutting the services down so it + # can un-register with the CA. + kra.uninstall() ++ # Uninstall the CA priori to shutting the services down so it ++ # can unregister from the security domain ++ ca.uninstall() + + print("Shutting down all IPA services") + try: +@@ -1194,8 +1198,6 @@ def uninstall(installer): + + restore_time_sync(sstore, fstore) + +- ca.uninstall() +- + dns.uninstall() + + httpinstance.HTTPInstance(fstore).uninstall() +-- +2.41.0 + diff --git a/SOURCES/0008-doc-add-the-run-command-for-manual-job-execution.patch b/SOURCES/0008-doc-add-the-run-command-for-manual-job-execution.patch deleted file mode 100644 index 4dd7587..0000000 --- a/SOURCES/0008-doc-add-the-run-command-for-manual-job-execution.patch +++ /dev/null @@ -1,138 +0,0 @@ -From f10d1a0f84ed0f16ab4a1469f16ffadb3e79e59e Mon Sep 17 00:00:00 2001 -From: Rob Crittenden -Date: Fri, 27 Jan 2023 14:05:37 -0500 -Subject: [PATCH] doc: add the --run command for manual job execution - -A manual method was mentioned with no specificity. Include -the --run command. Also update the troubleshooting section -to show what failure to restart the CA after configuration -looks like. - -Import the IPA CA chain for manual execution. - -Also fix up some $ -> # to indicate root is needed. - -Related: https://pagure.io/freeipa/issue/9294 - -Signed-off-by: Rob Crittenden -Reviewed-By: Florence Blanc-Renaud ---- - doc/designs/expired_certificate_pruning.md | 46 +++++++++++++++------- - 1 file changed, 32 insertions(+), 14 deletions(-) - -diff --git a/doc/designs/expired_certificate_pruning.md b/doc/designs/expired_certificate_pruning.md -index 2c10d914020d3c12b6abb028323cd6796ec33e00..a23e452696ba2a150c4ad5a3e57360ae0a16a338 100644 ---- a/doc/designs/expired_certificate_pruning.md -+++ b/doc/designs/expired_certificate_pruning.md -@@ -139,7 +139,7 @@ No validation of setting February 31st will be done. That will be left to PKI. B - - ### Disabling pruning - --`$ ipa-acme-manage pruning --enable=FALSE` -+`# ipa-acme-manage pruning --enable=FALSE` - - This will remove the configuration option for `jobsScheduler.job.pruning.cron` just to be sure it no longer runs. - -@@ -147,46 +147,46 @@ This will remove the configuration option for `jobsScheduler.job.pruning.cron` j - - #### Pruning certificates - --`$ ipa-acme-manage pruning --certretention=VALUE --certretentionunit=UNIT` -+`# ipa-acme-manage pruning --certretention=VALUE --certretentionunit=UNIT` - - will be the equivalent of: - --`$ pki-server ca-config-set jobsScheduler.job.pruning.certRetentionTime 30` -+`# pki-server ca-config-set jobsScheduler.job.pruning.certRetentionTime 30` - --`$ pki-server ca-config-set jobsScheduler.job.pruning.certRetentionUnit day` -+`# pki-server ca-config-set jobsScheduler.job.pruning.certRetentionUnit day` - - The unit will always be required when modifying the time. - --`$ ipa-acme-manage pruning --certsearchsizelimit=VALUE --certsearchtimelimit=VALUE` -+`# ipa-acme-manage pruning --certsearchsizelimit=VALUE --certsearchtimelimit=VALUE` - - will be the equivalent of: - --`$ pki-server ca-config-set jobsScheduler.job.pruning.certSearchSizeLimit 1000` -+`# pki-server ca-config-set jobsScheduler.job.pruning.certSearchSizeLimit 1000` - --`$ pki-server ca-config-set jobsScheduler.job.pruning.certSearchTimeLimit 0` -+`# pki-server ca-config-set jobsScheduler.job.pruning.certSearchTimeLimit 0` - - A value of 0 for searchtimelimit is unlimited. - - #### Pruning requests - --`$ ipa-acme-manage pruning --requestretention=VALUE --requestretentionunit=UNIT` -+`# ipa-acme-manage pruning --requestretention=VALUE --requestretentionunit=UNIT` - - will be the equivalent of: - --`$ pki-server ca-config-set jobsScheduler.job.pruning.requestRetentionTime 30` -+`# pki-server ca-config-set jobsScheduler.job.pruning.requestRetentionTime 30` - --`$ pki-server ca-config-set jobsScheduler.job.pruning.requestRetentionUnit day` -+`# pki-server ca-config-set jobsScheduler.job.pruning.requestRetentionUnit day` - - The unit will always be required when modifying the time. - --`$ ipa-acme-manage pruning --requestsearchsizelimit=VALUE --requestsearchtimelimit=VALUE` -+`# ipa-acme-manage pruning --requestsearchsizelimit=VALUE --requestsearchtimelimit=VALUE` - - - will be the equivalent of: - --`$ pki-server ca-config-set jobsScheduler.job.pruning.requestSearchSizeLimit 1000` -+`# pki-server ca-config-set jobsScheduler.job.pruning.requestSearchSizeLimit 1000` - --`$ pki-server ca-config-set jobsScheduler.job.pruning.requestSearchTimeLimit 0` -+`# pki-server ca-config-set jobsScheduler.job.pruning.requestSearchTimeLimit 0` - - A value of 0 for searchtimelimit is unlimited. - -@@ -212,10 +212,15 @@ Request search time limit: 0 - Cron: 0 0 1 * * - ``` - -+### Manual pruning -+ -+`# ipa-acme-manage pruning --run` -+ -+This is useful for testing the configuration or if the user wants to use the system cron or systemd timers for handling automation. -+ - ## Implementation - - For online REST operations (login, run job) we will use the `ipaserver/plugins/dogtag.py::RestClient` class to manage the requests. This will take care of the authentication cookie, etc. -- - The class uses dogtag.https_request() will can take PEM cert and key files as arguments. These will be used for authentication. - - For the non-REST operations (configuration, cron settings) the tool will fork out to pki-server ca-config-set. -@@ -239,6 +244,7 @@ Overview of the CLI commands. Example: - | ipa-acme-manage pruning | --requestretention=30 --requestretentionunit=day | - | ipa-acme-manage pruning | --requestsearchsizelimit=1000 --requestsearchtimelimit=0 | - | ipa-acme-manage pruning | --config-show | -+| ipa-acme-manage pruning | --run | - - ipa-acme-manage can only be run as root. - -@@ -295,3 +301,15 @@ The PKI debug log will contain job information. - 2022-12-08 21:15:24 [pruning] INFO: PruningJob: - filter: (&(!(requestState=complete))(requestModifyTime<=1667942124527)(!(requestModifyTime=1667942124527))) - 2022-12-08 21:15:24 [pruning] INFO: LDAPSession: Searching ou=ca, ou=requests,o=ipaca for (&(!(requestState=complete))(dateOfModify<=20221108211524Z)(!(dateOfModify=20221108211524Z))) - ``` -+ -+### Manual execution fails with Forbidden -+ -+If manually running pruning fails with a message like: -+ -+```console -+# ipa-acme-manage pruning --run -+CalledProcessError(Command ['pki', '-C', '/tmp/tmppyyd3hfq/pwdfile.txt', '-d', '/tmp/tmppyyd3hfq', '-n', 'CN=IPA RA,O=EXAMPLE.TEST', 'ca-job-start', 'pruning'] returned non-zero exit status 255: 'PKIException: Forbidden\n') -+The ipa-acme-manage command failed. -+``` -+ -+You probably forgot to restart the CA after enabling pruning. --- -2.39.1 - diff --git a/SOURCES/0009-Detection-of-PKI-subsystem.patch b/SOURCES/0009-Detection-of-PKI-subsystem.patch new file mode 100644 index 0000000..c85677a --- /dev/null +++ b/SOURCES/0009-Detection-of-PKI-subsystem.patch @@ -0,0 +1,44 @@ +From b9a07b1e97ee4e310b50860103872685da540da4 Mon Sep 17 00:00:00 2001 +From: Florence Blanc-Renaud +Date: Mon, 19 Jun 2023 15:40:39 +0200 +Subject: [PATCH] Detection of PKI subsystem + +In order to know if ca/kra is installed locally, the code +is calling pki-server subsystem-show _subsystem_ +and ensures that "Enabled: True" is in the output. + +If a subsystem fails to start, the command returns +"Enabled: False" but it doesn't mean that the subsystem +is not installed, it just means that it is not active +right now. +Same output if the subsystem has been disabled with +pki-server subsystem-disable _subsystem_. + +The correct way to check if a subsystem is installed is to +ensure that subsystem-show does not exit on error and +contains "Enabled: ", whatever the value. + +Related: https://pagure.io/freeipa/issue/9330 + +Signed-off-by: Florence Blanc-Renaud +Reviewed-By: Rob Crittenden +--- + ipaserver/install/dogtaginstance.py | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/ipaserver/install/dogtaginstance.py b/ipaserver/install/dogtaginstance.py +index 4967aca01807e58dfcc3157af10b92eff5dba206..7fdf2e0ed0f3ed99a6672f527d38dda0ce5ef8bb 100644 +--- a/ipaserver/install/dogtaginstance.py ++++ b/ipaserver/install/dogtaginstance.py +@@ -184,7 +184,7 @@ class DogtagInstance(service.Service): + ['pki-server', 'subsystem-show', self.subsystem.lower()], + capture_output=True) + # parse the command output +- return 'Enabled: True' in result.output ++ return 'Enabled: ' in result.output + except ipautil.CalledProcessError: + return False + +-- +2.41.0 + diff --git a/SOURCES/0009-tests-add-wrapper-around-ACME-RSNv3-test.patch b/SOURCES/0009-tests-add-wrapper-around-ACME-RSNv3-test.patch deleted file mode 100644 index 0137fbb..0000000 --- a/SOURCES/0009-tests-add-wrapper-around-ACME-RSNv3-test.patch +++ /dev/null @@ -1,43 +0,0 @@ -From d24b69981d94fce7b1e1aa4a5c1ab88a123f96b5 Mon Sep 17 00:00:00 2001 -From: Rob Crittenden -Date: Fri, 3 Feb 2023 10:04:31 -0500 -Subject: [PATCH] tests: add wrapper around ACME RSNv3 test - -This test is located outside of the TestACMEPrune because -it enables RSNv3 while the server installed by TestACME doesn't. - -It still needs a wrapper to enforce a version of PKI that -supports pruning because that is checked first in the tool. -Re-ordering that wouldn't be a good user experience. - -https://pagure.io/freeipa/issue/9322 - -Signed-off-by: Rob Crittenden -Reviewed-By: Rob Crittenden ---- - ipatests/test_integration/test_acme.py | 9 ++++++++- - 1 file changed, 8 insertions(+), 1 deletion(-) - -diff --git a/ipatests/test_integration/test_acme.py b/ipatests/test_integration/test_acme.py -index 93e785d8febd9fa8d7b3ef87ecb3f2eb42ac5da2..5ceba05976059de69414a79634d98045c3ab68bb 100644 ---- a/ipatests/test_integration/test_acme.py -+++ b/ipatests/test_integration/test_acme.py -@@ -393,7 +393,14 @@ class TestACME(CALessBase): - - def test_acme_pruning_no_random_serial(self): - """This ACME install is configured without random serial -- numbers. Verify that we can't enable pruning on it.""" -+ numbers. Verify that we can't enable pruning on it. -+ -+ This test is located here because by default installs -+ don't enable RSNv3. -+ """ -+ if (tasks.get_pki_version(self.master) -+ < tasks.parse_version('11.3.0')): -+ raise pytest.skip("Certificate pruning is not available") - self.master.run_command(['ipa-acme-manage', 'enable']) - result = self.master.run_command( - ['ipa-acme-manage', 'pruning', '--enable'], --- -2.39.1 - diff --git a/SOURCES/0010-Upgrade-fix-replica-agreement.patch b/SOURCES/0010-Upgrade-fix-replica-agreement.patch new file mode 100644 index 0000000..46dc830 --- /dev/null +++ b/SOURCES/0010-Upgrade-fix-replica-agreement.patch @@ -0,0 +1,164 @@ +From ad77c4c6512f82019d1970d910647761b60aaedb Mon Sep 17 00:00:00 2001 +From: Florence Blanc-Renaud +Date: Mon, 19 Jun 2023 10:36:29 +0200 +Subject: [PATCH] Upgrade: fix replica agreement + +The upgrade checks the replication agreements to ensure that +some attributes are excluded from replication. The agreements +are stored in entries like +cn=serverToreplica,cn=replica,cn=_suffix_,cn=mapping tree,cn=config +but those entries are managed by the replication topology plugin +and should not be updated directly. The consequence is that the update +of the attributes fails and ipa-server-update prints an error message: + +Error caught updating nsDS5ReplicatedAttributeList: Server is unwilling +to perform: Entry and attributes are managed by topology plugin.No direct +modifications allowed. +Error caught updating nsDS5ReplicatedAttributeListTotal: Server is +unwilling to perform: Entry and attributes are managed by topology +plugin.No direct modifications allowed. + +The upgrade continues but the replication is not excluding +passwordgraceusertime. + +Instead of editing the agreements, perform the modifications on +the topology segments. + +Fixes: https://pagure.io/freeipa/issue/9385 +Signed-off-by: Florence Blanc-Renaud +Reviewed-By: Rob Crittenden +--- + .../install/plugins/fix_replica_agreements.py | 80 +++++++++---------- + 1 file changed, 38 insertions(+), 42 deletions(-) + +diff --git a/ipaserver/install/plugins/fix_replica_agreements.py b/ipaserver/install/plugins/fix_replica_agreements.py +index c0cdd3eb19c486121f727476360ce421b1d82728..d963753d0bf9ee65285a86fa9a249198c28a66e2 100644 +--- a/ipaserver/install/plugins/fix_replica_agreements.py ++++ b/ipaserver/install/plugins/fix_replica_agreements.py +@@ -22,6 +22,7 @@ import logging + from ipaserver.install import replication + from ipalib import Registry + from ipalib import Updater ++from ipalib import errors + + logger = logging.getLogger(__name__) + +@@ -41,35 +42,42 @@ class update_replica_attribute_lists(Updater): + def execute(self, **options): + # We need an LDAPClient connection to the backend + logger.debug("Start replication agreement exclude list update task") +- conn = self.api.Backend.ldap2 + +- repl = replication.ReplicationManager(self.api.env.realm, +- self.api.env.host, +- None, conn=conn) +- +- # We need to update only IPA replica agreements, not winsync +- ipa_replicas = repl.find_ipa_replication_agreements() +- +- logger.debug("Found %d agreement(s)", len(ipa_replicas)) +- +- for replica in ipa_replicas: +- for desc in replica.get('description', []): +- logger.debug('%s', desc) +- +- self._update_attr(repl, replica, +- 'nsDS5ReplicatedAttributeList', +- replication.EXCLUDES, template=EXCLUDE_TEMPLATE) +- self._update_attr(repl, replica, +- 'nsDS5ReplicatedAttributeListTotal', +- replication.TOTAL_EXCLUDES, template=EXCLUDE_TEMPLATE) +- self._update_attr(repl, replica, +- 'nsds5ReplicaStripAttrs', replication.STRIP_ATTRS) ++ # Find suffixes ++ suffixes = self.api.Command.topologysuffix_find()['result'] ++ for suffix in suffixes: ++ suffix_name = suffix['cn'][0] ++ # Find segments ++ sgmts = self.api.Command.topologysegment_find( ++ suffix_name, all=True)['result'] ++ for segment in sgmts: ++ updates = {} ++ updates = self._update_attr( ++ segment, updates, ++ 'nsds5replicatedattributelist', ++ replication.EXCLUDES, template=EXCLUDE_TEMPLATE) ++ updates = self._update_attr( ++ segment, updates, ++ 'nsds5replicatedattributelisttotal', ++ replication.TOTAL_EXCLUDES, template=EXCLUDE_TEMPLATE) ++ updates = self._update_attr( ++ segment, updates, ++ 'nsds5replicastripattrs', replication.STRIP_ATTRS) ++ if updates: ++ try: ++ self.api.Command.topologysegment_mod( ++ suffix_name, segment['cn'][0], ++ **updates) ++ except errors.EmptyModlist: ++ # No update done ++ logger.debug("No update required for the segment %s", ++ segment['cn'][0]) + + logger.debug("Done updating agreements") + + return False, [] # No restart, no updates + +- def _update_attr(self, repl, replica, attribute, values, template='%s'): ++ def _update_attr(self, segment, updates, attribute, values, template='%s'): + """Add or update an attribute of a replication agreement + + If the attribute doesn't already exist, it is added and set to +@@ -77,27 +85,21 @@ class update_replica_attribute_lists(Updater): + If the attribute does exist, `values` missing from it are just + appended to the end, also space-separated. + +- :param repl: Replication manager +- :param replica: Replica agreement ++ :param: updates: dict containing the updates ++ :param segment: dict containing segment information + :param attribute: Attribute to add or update + :param values: List of values the attribute should hold + :param template: Template to use when adding attribute + """ +- attrlist = replica.single_value.get(attribute) ++ attrlist = segment.get(attribute) + if attrlist is None: + logger.debug("Adding %s", attribute) + + # Need to add it altogether +- replica[attribute] = [template % " ".join(values)] +- +- try: +- repl.conn.update_entry(replica) +- logger.debug("Updated") +- except Exception as e: +- logger.error("Error caught updating replica: %s", str(e)) ++ updates[attribute] = template % " ".join(values) + + else: +- attrlist_normalized = attrlist.lower().split() ++ attrlist_normalized = attrlist[0].lower().split() + missing = [a for a in values + if a.lower() not in attrlist_normalized] + +@@ -105,14 +107,8 @@ class update_replica_attribute_lists(Updater): + logger.debug("%s needs updating (missing: %s)", attribute, + ', '.join(missing)) + +- replica[attribute] = [ +- '%s %s' % (attrlist, ' '.join(missing))] ++ updates[attribute] = '%s %s' % (attrlist[0], ' '.join(missing)) + +- try: +- repl.conn.update_entry(replica) +- logger.debug("Updated %s", attribute) +- except Exception as e: +- logger.error("Error caught updating %s: %s", +- attribute, str(e)) + else: + logger.debug("%s: No update necessary", attribute) ++ return updates +-- +2.41.0 + diff --git a/SOURCES/0010-automember-rebuild-add-a-notice-about-high-CPU-usage.patch b/SOURCES/0010-automember-rebuild-add-a-notice-about-high-CPU-usage.patch deleted file mode 100644 index f3891fd..0000000 --- a/SOURCES/0010-automember-rebuild-add-a-notice-about-high-CPU-usage.patch +++ /dev/null @@ -1,68 +0,0 @@ -From 2857bc69957bde7e59fff1c66c5a83c7f560616b Mon Sep 17 00:00:00 2001 -From: Florence Blanc-Renaud -Date: Tue, 31 Jan 2023 15:53:08 +0100 -Subject: [PATCH] automember-rebuild: add a notice about high CPU usage - -The automember-rebuild task may require high CPU usage -if many users/hosts/groups are processed. -Add a note in the ipa automember-rebuild CLI output -and in the WebUI confirmation message. - -Fixes: https://pagure.io/freeipa/issue/9320 -Signed-off-by: Florence Blanc-Renaud -Reviewed-By: Francisco Trivino ---- - install/ui/test/data/i18n_messages.json | 2 +- - ipaclient/plugins/automember.py | 8 ++++++++ - ipaserver/plugins/internal.py | 6 +++++- - 3 files changed, 14 insertions(+), 2 deletions(-) - -diff --git a/install/ui/test/data/i18n_messages.json b/install/ui/test/data/i18n_messages.json -index 49d288326d8cea192a16e93a274599805b0ea666..5b735487bf33805e8f0534d378d1497f05a11be8 100644 ---- a/install/ui/test/data/i18n_messages.json -+++ b/install/ui/test/data/i18n_messages.json -@@ -7,7 +7,7 @@ - "actions": { - "apply": "Apply", - "automember_rebuild": "Rebuild auto membership", -- "automember_rebuild_confirm": "Are you sure you want to rebuild auto membership?", -+ "automember_rebuild_confirm": "Are you sure you want to rebuild auto membership? In case of a high number of users, hosts or groups, the operation may require high CPU usage.", - "automember_rebuild_success": "Automember rebuild membership task completed", - "confirm": "Are you sure you want to proceed with the action?", - "delete_confirm": "Are you sure you want to delete ${object}?", -diff --git a/ipaclient/plugins/automember.py b/ipaclient/plugins/automember.py -index df4a2e5a01744e0ff22c74180e13c2e7dc33fbaa..7108dc948753b9f6a4439842bd75e7c5e064bda6 100644 ---- a/ipaclient/plugins/automember.py -+++ b/ipaclient/plugins/automember.py -@@ -34,3 +34,11 @@ class automember_add_condition(MethodOverride): - flags=['suppress_empty'], - ), - ) -+ -+ -+@register(override=True, no_fail=True) -+class automember_rebuild(MethodOverride): -+ def interactive_prompt_callback(self, kw): -+ msg = _('IMPORTANT: In case of a high number of users, hosts or ' -+ 'groups, the operation may require high CPU usage.') -+ self.Backend.textui.print_plain(msg) -diff --git a/ipaserver/plugins/internal.py b/ipaserver/plugins/internal.py -index 5ffa7a281548a0658386f8740dbddd96fd0bc7d6..e1e920f8bb49dd8ba8f30b727111bb1316f6a918 100644 ---- a/ipaserver/plugins/internal.py -+++ b/ipaserver/plugins/internal.py -@@ -160,7 +160,11 @@ class i18n_messages(Command): - "actions": { - "apply": _("Apply"), - "automember_rebuild": _("Rebuild auto membership"), -- "automember_rebuild_confirm": _("Are you sure you want to rebuild auto membership?"), -+ "automember_rebuild_confirm": _( -+ "Are you sure you want to rebuild auto membership? In case of " -+ "a high number of users, hosts or groups, the operation " -+ "may require high CPU usage." -+ ), - "automember_rebuild_success": _("Automember rebuild membership task completed"), - "confirm": _("Are you sure you want to proceed with the action?"), - "delete_confirm": _("Are you sure you want to delete ${object}?"), --- -2.39.1 - diff --git a/SOURCES/0011-Fix-setting-values-of-0-in-ACME-pruning.patch b/SOURCES/0011-Fix-setting-values-of-0-in-ACME-pruning.patch deleted file mode 100644 index 55f2f0d..0000000 --- a/SOURCES/0011-Fix-setting-values-of-0-in-ACME-pruning.patch +++ /dev/null @@ -1,158 +0,0 @@ -From 20ff7c16022793c707f6c2b8fb38a801870bc0e2 Mon Sep 17 00:00:00 2001 -From: Rob Crittenden -Date: Wed, 8 Feb 2023 10:42:58 -0500 -Subject: [PATCH] Fix setting values of 0 in ACME pruning - -Replace comparisons of "if value" with "if value is not None" -in order to handle 0. - -Add a short reference to the man page to indicat that a cert -or request retention time of 0 means remove at the next -execution. - -Also indicate that the search time limit is in seconds. - -Fixes: https://pagure.io/freeipa/issue/9325 - -Signed-off-by: Rob Crittenden -Reviewed-By: Francisco Trivino ---- - doc/designs/expired_certificate_pruning.md | 4 ++-- - install/tools/man/ipa-acme-manage.1 | 8 +++---- - ipaserver/install/ipa_acme_manage.py | 28 +++++++++++----------- - 3 files changed, 20 insertions(+), 20 deletions(-) - -diff --git a/doc/designs/expired_certificate_pruning.md b/doc/designs/expired_certificate_pruning.md -index a23e452696ba2a150c4ad5a3e57360ae0a16a338..35ead7b00145b5df44caf542cba277f0e6e08b6a 100644 ---- a/doc/designs/expired_certificate_pruning.md -+++ b/doc/designs/expired_certificate_pruning.md -@@ -67,11 +67,11 @@ There are four values each that can be managed for pruning certificates and requ - * expired cert/incomplete request time - * time unit - * LDAP search size limit --* LDAP search time limit -+* LDAP search time limit (in seconds) - - The first two configure when an expired certificate or incomplete request will be deleted. The unit can be one of: minute, hour, day, year. By default it is 30 days. - --The LDAP limits control how many entries are returned and how long the search can take. By default it is 1000 entries and unlimited time. -+The LDAP limits control how many entries are returned and how long the search can take. By default it is 1000 entries and unlimited time (0 == unlimited, unit is seconds). - - ### Configuration settings - -diff --git a/install/tools/man/ipa-acme-manage.1 b/install/tools/man/ipa-acme-manage.1 -index e6cec4e4a7fd460c514a72456a2dc9a2e3682ebd..b8383c14f482698d2bcc8b08f0c0bf5882c3c298 100644 ---- a/install/tools/man/ipa-acme-manage.1 -+++ b/install/tools/man/ipa-acme-manage.1 -@@ -79,7 +79,7 @@ For example, "0 0 1 * *" schedules the job to run at 12:00am on the first - day of each month. - .TP - \fB\-\-certretention=CERTRETENTION\fR --Certificate retention time. The default is 30. -+Certificate retention time. The default is 30. A value of 0 will remove expired certificates with no delay. - .TP - \fB\-\-certretentionunit=CERTRETENTIONUNIT\fR - Certificate retention units. Valid units are: minute, hour, day, year. -@@ -89,10 +89,10 @@ The default is days. - LDAP search size limit searching for expired certificates. The default is 1000. This is a client-side limit. There may be additional server-side limitations. - .TP - \fB\-\-certsearchtimelimit=CERTSEARCHTIMELIMIT\fR --LDAP search time limit searching for expired certificates. The default is 0, no limit. This is a client-side limit. There may be additional server-side limitations. -+LDAP search time limit (seconds) searching for expired certificates. The default is 0, no limit. This is a client-side limit. There may be additional server-side limitations. - .TP - \fB\-\-requestretention=REQUESTRETENTION\fR --Request retention time. The default is 30. -+Request retention time. The default is 30. A value of 0 will remove expired requests with no delay. - .TP - \fB\-\-requestretentionunit=REQUESTRETENTIONUNIT\fR - Request retention units. Valid units are: minute, hour, day, year. -@@ -102,7 +102,7 @@ The default is days. - LDAP search size limit searching for unfulfilled requests. The default is 1000. There may be additional server-side limitations. - .TP - \fB\-\-requestsearchtimelimit=REQUESTSEARCHTIMELIMIT\fR --LDAP search time limit searching for unfulfilled requests. The default is 0, no limit. There may be additional server-side limitations. -+LDAP search time limit (seconds) searching for unfulfilled requests. The default is 0, no limit. There may be additional server-side limitations. - .TP - \fB\-\-config\-show\fR - Show the current pruning configuration -diff --git a/ipaserver/install/ipa_acme_manage.py b/ipaserver/install/ipa_acme_manage.py -index b7b2111d9edcec2580aa4a485d7a7340146ff065..e7c35ff6fb5b7a30ac9e2c0c18f8db805cf06ee9 100644 ---- a/ipaserver/install/ipa_acme_manage.py -+++ b/ipaserver/install/ipa_acme_manage.py -@@ -207,14 +207,14 @@ class IPAACMEManage(AdminTool): - self.options.enable, - self.options.disable, - self.options.cron, -- self.options.certretention, -+ self.options.certretention is not None, - self.options.certretentionunit, -- self.options.requestretention, -+ self.options.requestretention is not None, - self.options.requestretentionunit, -- self.options.certsearchsizelimit, -- self.options.certsearchtimelimit, -- self.options.requestsearchsizelimit, -- self.options.requestsearchtimelimit, -+ self.options.certsearchsizelimit is not None, -+ self.options.certsearchtimelimit is not None, -+ self.options.requestsearchsizelimit is not None, -+ self.options.requestsearchtimelimit is not None, - ] - ) - and (self.options.config_show or self.options.run) -@@ -226,7 +226,7 @@ class IPAACMEManage(AdminTool): - elif self.options.cron: - if len(self.options.cron.split()) != 5: - self.option_parser.error("Invalid format for --cron") -- # dogtag does no validation when setting an option so -+ # dogtag does no validation when setting this option so - # do the minimum. The dogtag cron is limited compared to - # crontab(5). - opt = self.options.cron.split() -@@ -255,7 +255,7 @@ class IPAACMEManage(AdminTool): - 'pki-server', command, - f'{prefix}.{directive}' - ] -- if value: -+ if value is not None: - args.extend([str(value)]) - logger.debug(args) - result = run(args, raiseonerr=False, capture_output=True, -@@ -350,28 +350,28 @@ class IPAACMEManage(AdminTool): - - # pki-server ca-config-set can only set one option at a time so - # loop through all the options and set what is there. -- if self.options.certretention: -+ if self.options.certretention is not None: - ca_config_set('certRetentionTime', - self.options.certretention) - if self.options.certretentionunit: - ca_config_set('certRetentionUnit', - self.options.certretentionunit) -- if self.options.certsearchtimelimit: -+ if self.options.certsearchtimelimit is not None: - ca_config_set('certSearchTimeLimit', - self.options.certsearchtimelimit) -- if self.options.certsearchsizelimit: -+ if self.options.certsearchsizelimit is not None: - ca_config_set('certSearchSizeLimit', - self.options.certsearchsizelimit) -- if self.options.requestretention: -+ if self.options.requestretention is not None: - ca_config_set('requestRetentionTime', - self.options.requestretention) - if self.options.requestretentionunit: - ca_config_set('requestRetentionUnit', - self.options.requestretentionunit) -- if self.options.requestsearchsizelimit: -+ if self.options.requestsearchsizelimit is not None: - ca_config_set('requestSearchSizeLimit', - self.options.requestsearchsizelimit) -- if self.options.requestsearchtimelimit: -+ if self.options.requestsearchtimelimit is not None: - ca_config_set('requestSearchTimeLimit', - self.options.requestsearchtimelimit) - if self.options.cron: --- -2.39.1 - diff --git a/SOURCES/0011-Integration-tests-add-a-test-to-ipa-server-upgrade.patch b/SOURCES/0011-Integration-tests-add-a-test-to-ipa-server-upgrade.patch new file mode 100644 index 0000000..27161d9 --- /dev/null +++ b/SOURCES/0011-Integration-tests-add-a-test-to-ipa-server-upgrade.patch @@ -0,0 +1,69 @@ +From 3b58487c7b2f8ac133e37e8f90f85ff2fb05bf34 Mon Sep 17 00:00:00 2001 +From: Florence Blanc-Renaud +Date: Mon, 19 Jun 2023 10:36:59 +0200 +Subject: [PATCH] Integration tests: add a test to ipa-server-upgrade + +Add an integration test ensuring that the upgrade +properly updates the attributes to be excluded from +replication. + +Related: https://pagure.io/freeipa/issue/9385 +Signed-off-by: Florence Blanc-Renaud +Reviewed-By: Rob Crittenden +--- + .../test_simple_replication.py | 30 +++++++++++++++++++ + 1 file changed, 30 insertions(+) + +diff --git a/ipatests/test_integration/test_simple_replication.py b/ipatests/test_integration/test_simple_replication.py +index 17092a49966e61d5a4a9b04c15abcb1de8be9683..d1e65ef7cc3e748670f2cdebe2a5cb7172af27f0 100644 +--- a/ipatests/test_integration/test_simple_replication.py ++++ b/ipatests/test_integration/test_simple_replication.py +@@ -23,8 +23,10 @@ import pytest + + from ipaplatform.paths import paths + from ipapython.dn import DN ++from ipaserver.install.replication import EXCLUDES + from ipatests.pytest_ipa.integration import tasks + from ipatests.test_integration.base import IntegrationTest ++from ipatests.test_integration.test_topology import find_segment + + + def check_replication(source_host, dest_host, login): +@@ -104,6 +106,34 @@ class TestSimpleReplication(IntegrationTest): + [paths.IPA_CUSTODIA_CHECK, self.master.hostname] + ) + ++ def test_fix_agreements(self): ++ """Test that upgrade fixes the list of attributes excluded from repl ++ ++ Test for ticket 9385 ++ """ ++ # Prepare the server by removing some values from ++ # from the nsDS5ReplicatedAttributeList ++ segment = find_segment(self.master, self.replicas[0], "domain") ++ self.master.run_command([ ++ "ipa", "topologysegment-mod", "domain", segment, ++ "--replattrs", ++ "(objectclass=*) $ EXCLUDE memberof idnssoaserial entryusn"]) ++ # Run the upgrade ++ result = self.master.run_command(["ipa-server-upgrade"]) ++ # Ensure that the upgrade updated the attribute without error ++ errmsg = "Error caught updating nsDS5ReplicatedAttributeList" ++ assert errmsg not in result.stdout_text ++ # Check the updated value ++ suffix = DN(self.master.domain.basedn) ++ dn = DN(('cn', str(suffix)), ('cn', 'mapping tree'), ('cn', 'config')) ++ result = tasks.ldapsearch_dm(self.master, str(dn), ++ ["nsDS5ReplicatedAttributeList"]) ++ output = result.stdout_text.lower() ++ ++ template = 'nsDS5ReplicatedAttributeList: (objectclass=*) $ EXCLUDE %s' ++ expected_value = template % " ".join(EXCLUDES) ++ assert expected_value.lower() in output ++ + def test_replica_removal(self): + """Test replica removal""" + result = self.master.run_command(['ipa-replica-manage', 'list']) +-- +2.41.0 + diff --git a/SOURCES/0012-Wipe-the-ipa-ca-DNS-record-when-updating-system-reco.patch b/SOURCES/0012-Wipe-the-ipa-ca-DNS-record-when-updating-system-reco.patch deleted file mode 100644 index ea70bca..0000000 --- a/SOURCES/0012-Wipe-the-ipa-ca-DNS-record-when-updating-system-reco.patch +++ /dev/null @@ -1,69 +0,0 @@ -From 4e0ad96fbd9f438c884eeeaa60c2fb0c910a2b61 Mon Sep 17 00:00:00 2001 -From: Rob Crittenden -Date: Mon, 11 Jul 2022 14:20:32 -0400 -Subject: [PATCH] Wipe the ipa-ca DNS record when updating system records - -If a server with a CA has been marked as hidden and -contains the last A or AAAA address then that address -would remain in the ipa-ca entry. - -This is because update-dns-system-records did not delete -values, it just re-computed them. So if no A or AAAA -records were found then the existing value was left. - -Fixes: https://pagure.io/freeipa/issue/9195 - -Signed-off-by: Rob Crittenden -Reviewed-By: Francisco Trivino -Reviewed-By: Stanislav Levin ---- - ipaserver/dns_data_management.py | 12 +++++++++++- - 1 file changed, 11 insertions(+), 1 deletion(-) - -diff --git a/ipaserver/dns_data_management.py b/ipaserver/dns_data_management.py -index e2bc530ee8a8d7ade853652680c524ccd229205c..aaae5446856aba5e39ca9bb9c03decd434e4f71a 100644 ---- a/ipaserver/dns_data_management.py -+++ b/ipaserver/dns_data_management.py -@@ -19,6 +19,7 @@ from dns import ( - from time import sleep, time - - from ipalib import errors -+from ipalib.constants import IPA_CA_RECORD - from ipalib.dns import record_name_format - from ipapython.dnsutil import DNSName - from ipaserver.install import installutils -@@ -187,7 +188,7 @@ class IPASystemRecords: - - def __add_ca_records_from_hostname(self, zone_obj, hostname): - assert isinstance(hostname, DNSName) and hostname.is_absolute() -- r_name = DNSName('ipa-ca') + self.domain_abs -+ r_name = DNSName(IPA_CA_RECORD) + self.domain_abs - rrsets = None - end_time = time() + CA_RECORDS_DNS_TIMEOUT - while True: -@@ -210,6 +211,7 @@ class IPASystemRecords: - - for rrset in rrsets: - for rd in rrset: -+ logger.debug("Adding CA IP %s for %s", rd.to_text(), hostname) - rdataset = zone_obj.get_rdataset( - r_name, rd.rdtype, create=True) - rdataset.add(rd, ttl=self.TTL) -@@ -461,6 +463,14 @@ class IPASystemRecords: - ) - ) - -+ # Remove the ipa-ca record(s). They will be reconstructed in -+ # get_base_records(). -+ r_name = DNSName(IPA_CA_RECORD) + self.domain_abs -+ try: -+ self.api_instance.Command.dnsrecord_del( -+ self.domain_abs, r_name, del_all=True) -+ except errors.NotFound: -+ pass - base_zone = self.get_base_records() - for record_name, node in base_zone.items(): - set_cname_template = record_name in names_requiring_cname_templates --- -2.39.1 - diff --git a/SOURCES/0012-tests-fix-backup-restore-scenario-with-replica.patch b/SOURCES/0012-tests-fix-backup-restore-scenario-with-replica.patch new file mode 100644 index 0000000..3c188e3 --- /dev/null +++ b/SOURCES/0012-tests-fix-backup-restore-scenario-with-replica.patch @@ -0,0 +1,55 @@ +From 5e291da42898cc646f699c21a44b03b833d346e8 Mon Sep 17 00:00:00 2001 +From: Florence Blanc-Renaud +Date: Tue, 27 Jun 2023 15:30:08 +0200 +Subject: [PATCH] tests: fix backup-restore scenario with replica + +The test TestBackupAndRestoreWithReplica is simulating a +master crash in order to check the behavior after ipa-restore. + +Since commit 67a33e5, the uninstaller restarts the services in +order to unregister the server from PKI security domain. An +indirect consequence is that master/replica communication is re- +established and operations removing entries (done by the uninstaller) +are replicated to the replica. +This means that the scenario does not really simulate a server crash. + +To make sure that no replication happens during this "crash", stop +the replica first, then uninstall the master, and finally restart +the replica before calling the ipa-restore command on the master. + +Fixes: https://pagure.io/freeipa/issue/9404 + +Signed-off-by: Florence Blanc-Renaud +Reviewed-By: Rob Crittenden +--- + ipatests/test_integration/test_backup_and_restore.py | 7 +++++++ + 1 file changed, 7 insertions(+) + +diff --git a/ipatests/test_integration/test_backup_and_restore.py b/ipatests/test_integration/test_backup_and_restore.py +index 390c065f373e9a8a667f228a09eebd9ac033a19f..83b6a6b44d805fb0615e2128d4be984c6f858bf9 100644 +--- a/ipatests/test_integration/test_backup_and_restore.py ++++ b/ipatests/test_integration/test_backup_and_restore.py +@@ -602,6 +602,12 @@ class TestBackupAndRestoreWithReplica(IntegrationTest): + tasks.user_add(self.replica1, 'test2_replica') + + # simulate master crash ++ # the replica is stopped to make sure master uninstallation ++ # does not delete any entry on the replica. In case of a ++ # real master crash there would not be any communication between ++ # master and replica ++ self.replica1.run_command(['ipactl', 'stop']) ++ + self.master.run_command(['ipactl', 'stop']) + tasks.uninstall_master(self.master, clean=False) + +@@ -612,6 +618,7 @@ class TestBackupAndRestoreWithReplica(IntegrationTest): + self.master.run_command([ + "systemctl", "disable", "oddjobd" + ]) ++ self.replica1.run_command(['ipactl', 'start']) + + self.master.run_command(['ipa-restore', '-U', backup_path]) + +-- +2.41.0 + diff --git a/SOURCES/0013-OTP-fix-data-type-to-avoid-endianness-issue.patch b/SOURCES/0013-OTP-fix-data-type-to-avoid-endianness-issue.patch new file mode 100644 index 0000000..e3dd65d --- /dev/null +++ b/SOURCES/0013-OTP-fix-data-type-to-avoid-endianness-issue.patch @@ -0,0 +1,54 @@ +From 631dd72369385b0793e5bc0e019c088b4f1e2bb3 Mon Sep 17 00:00:00 2001 +From: Florence Blanc-Renaud +Date: Mon, 26 Jun 2023 18:24:46 +0200 +Subject: [PATCH] OTP: fix data type to avoid endianness issue + +When 389-ds process an OTP authentication, the ipa-pwd-extop +plugin reads a buffer to extract the authentication type. +The type is stored in an int but the data is a ber_tag_t. + +On big endian machines the type cast does not cause any issue +but on s390x the buffer that should return 128 is seen as 0. + +As a consequence, the plugin considers that the method is not +LDAP_AUTH_SIMPLE and exits early, without processing the OTP. + +The fix is simple and consists in using the right type +(ber_tag_t is an unsigned long). + +Fixes: https://pagure.io/freeipa/issue/9402 + +Signed-off-by: Florence Blanc-Renaud +Reviewed-By: Rob Crittenden +--- + daemons/ipa-slapi-plugins/ipa-pwd-extop/prepost.c | 6 ++++-- + 1 file changed, 4 insertions(+), 2 deletions(-) + +diff --git a/daemons/ipa-slapi-plugins/ipa-pwd-extop/prepost.c b/daemons/ipa-slapi-plugins/ipa-pwd-extop/prepost.c +index 937594117956d57540d4cf4eabeef6d22860aec8..45626523ffa1030cdff4f3e0ccdfa1618a51ccaf 100644 +--- a/daemons/ipa-slapi-plugins/ipa-pwd-extop/prepost.c ++++ b/daemons/ipa-slapi-plugins/ipa-pwd-extop/prepost.c +@@ -1433,7 +1433,7 @@ static int ipapwd_pre_bind(Slapi_PBlock *pb) + Slapi_DN *target_sdn = NULL; + Slapi_DN *sdn = NULL; + const char *dn = NULL; +- int method = 0; ++ ber_tag_t method = 0; + bool syncreq; + bool otpreq; + int ret = 0; +@@ -1454,8 +1454,10 @@ static int ipapwd_pre_bind(Slapi_PBlock *pb) + } + + /* We're only interested in simple authentication. */ +- if (method != LDAP_AUTH_SIMPLE || credentials->bv_len == 0) ++ if (method != LDAP_AUTH_SIMPLE || credentials->bv_len == 0) { ++ LOG("Not handled (not simple bind or NULL dn/credentials)\n"); + return 0; ++ } + + /* Retrieve the user's entry. */ + sdn = slapi_sdn_dup(target_sdn); +-- +2.41.0 + diff --git a/SOURCES/0013-ipa-kdb-PAC-consistency-checker-needs-to-handle-chil.patch b/SOURCES/0013-ipa-kdb-PAC-consistency-checker-needs-to-handle-chil.patch deleted file mode 100644 index 95d8ad1..0000000 --- a/SOURCES/0013-ipa-kdb-PAC-consistency-checker-needs-to-handle-chil.patch +++ /dev/null @@ -1,111 +0,0 @@ -From 0206369eec8530e96c66986c4ca501d8962193ce Mon Sep 17 00:00:00 2001 -From: Alexander Bokovoy -Date: Mon, 30 Jan 2023 14:22:30 +0200 -Subject: [PATCH] ipa-kdb: PAC consistency checker needs to handle child - domains as well - -When PAC check is performed, we might get a signing TGT instead of the -client DB entry. This means it is a principal from a trusted domain but -we don't know which one exactly because we only have a krbtgt for the -forest root. This happens in MIT Kerberos 1.20 or later where KDB's -issue_pac() callback never gets the original client principal directly. - -Look into known child domains as well and make pass the check if both -NetBIOS name and SID correspond to one of the trusted domains under this -forest root. Move check for the SID before NetBIOS name check because we -can use SID of the domain in PAC to find out the right child domain in -our trusted domains' topology list. - -Fixes: https://pagure.io/freeipa/issue/9316 - -Signed-off-by: Alexander Bokovoy -Reviewed-By: Rafael Guterres Jeffman -Reviewed-By: Rob Crittenden ---- - daemons/ipa-kdb/ipa_kdb_mspac.c | 51 +++++++++++++++++++++------------ - 1 file changed, 32 insertions(+), 19 deletions(-) - -diff --git a/daemons/ipa-kdb/ipa_kdb_mspac.c b/daemons/ipa-kdb/ipa_kdb_mspac.c -index a15050e2166f95c227d2e3c7d238e1ea2fe01235..476d1cb558a53420821ccfb1b794cb6bedce7794 100644 ---- a/daemons/ipa-kdb/ipa_kdb_mspac.c -+++ b/daemons/ipa-kdb/ipa_kdb_mspac.c -@@ -1827,11 +1827,43 @@ krb5_error_code filter_logon_info(krb5_context context, - bool result; - char *domstr = NULL; - -+ ipactx = ipadb_get_context(context); -+ if (!ipactx || !ipactx->mspac) { -+ return KRB5_KDB_DBNOTINITED; -+ } -+ - domain = get_domain_from_realm_update(context, realm); - if (!domain) { - return EINVAL; - } - -+ /* check exact sid */ -+ result = dom_sid_check(&domain->domsid, info->info->info3.base.domain_sid, true); -+ if (!result) { -+ struct ipadb_mspac *mspac_ctx = ipactx->mspac; -+ result = FALSE; -+ /* Didn't match but perhaps the original PAC was issued by a child domain's DC? */ -+ for (k = 0; k < mspac_ctx->num_trusts; k++) { -+ result = dom_sid_check(&mspac_ctx->trusts[k].domsid, -+ info->info->info3.base.domain_sid, true); -+ if (result) { -+ domain = &mspac_ctx->trusts[k]; -+ break; -+ } -+ } -+ if (!result) { -+ domstr = dom_sid_string(NULL, info->info->info3.base.domain_sid); -+ krb5_klog_syslog(LOG_ERR, "PAC Info mismatch: domain = %s, " -+ "expected domain SID = %s, " -+ "found domain SID = %s", -+ domain->domain_name, domain->domain_sid, -+ domstr ? domstr : ""); -+ talloc_free(domstr); -+ return EINVAL; -+ } -+ } -+ -+ /* At this point we may have changed the domain we look at, */ - /* check netbios/flat name */ - if (strcasecmp(info->info->info3.base.logon_domain.string, - domain->flat_name) != 0) { -@@ -1843,21 +1875,6 @@ krb5_error_code filter_logon_info(krb5_context context, - return EINVAL; - } - -- /* check exact sid */ -- result = dom_sid_check(&domain->domsid, info->info->info3.base.domain_sid, true); -- if (!result) { -- domstr = dom_sid_string(NULL, info->info->info3.base.domain_sid); -- if (!domstr) { -- return EINVAL; -- } -- krb5_klog_syslog(LOG_ERR, "PAC Info mismatch: domain = %s, " -- "expected domain SID = %s, " -- "found domain SID = %s", -- domain->domain_name, domain->domain_sid, domstr); -- talloc_free(domstr); -- return EINVAL; -- } -- - /* Check if this domain has been filtered out by the trust itself*/ - if (domain->parent != NULL) { - for(k = 0; k < domain->parent->len_sid_blocklist_incoming; k++) { -@@ -1944,10 +1961,6 @@ krb5_error_code filter_logon_info(krb5_context context, - * should include different possibilities into account - * */ - if (info->info->info3.sidcount != 0) { -- ipactx = ipadb_get_context(context); -- if (!ipactx || !ipactx->mspac) { -- return KRB5_KDB_DBNOTINITED; -- } - count = info->info->info3.sidcount; - i = 0; - j = 0; --- -2.39.1 - diff --git a/SOURCES/0014-Add-test-for-SSH-with-GSSAPI-auth.patch b/SOURCES/0014-Add-test-for-SSH-with-GSSAPI-auth.patch deleted file mode 100644 index 00630b5..0000000 --- a/SOURCES/0014-Add-test-for-SSH-with-GSSAPI-auth.patch +++ /dev/null @@ -1,83 +0,0 @@ -From a6cb905de74da38d62f9c3bd7957018924282521 Mon Sep 17 00:00:00 2001 -From: Anuja More -Date: Mon, 30 Jan 2023 19:27:49 +0530 -Subject: [PATCH] Add test for SSH with GSSAPI auth. - -Added test for aduser with GSSAPI authentication. - -Related : https://pagure.io/freeipa/issue/9316 - -Signed-off-by: Anuja More -Reviewed-By: Rafael Guterres Jeffman -Reviewed-By: Rob Crittenden ---- - ipatests/test_integration/test_trust.py | 46 +++++++++++++++++++++++++ - 1 file changed, 46 insertions(+) - -diff --git a/ipatests/test_integration/test_trust.py b/ipatests/test_integration/test_trust.py -index c4b3b99ce1abbc16817b6530939fd9bae3f9500a..0d5b71cb0277a79eed7c34eb7e3d7eb6c09faa5e 100644 ---- a/ipatests/test_integration/test_trust.py -+++ b/ipatests/test_integration/test_trust.py -@@ -527,6 +527,35 @@ class TestTrust(BaseTestTrust): - .format(self.ad_domain, subordinate_suffix)) - self.ad.run_command(['powershell', '-c', cmd]) - -+ def test_ssh_aduser(self): -+ """Test ssh with GSSAPI is working with aduser -+ -+ When kerberos ticket is obtained for child domain user -+ and ssh with this ticket should be successful -+ with no password prompt. -+ -+ Related : https://pagure.io/freeipa/issue/9316 -+ """ -+ testuser = 'testuser@{0}'.format(self.ad_domain) -+ testusersub = 'subdomaintestuser@{0}'.format(self.ad_subdomain) -+ -+ def sshuser(host, user): -+ tasks.kdestroy_all(host) -+ try: -+ tasks.kinit_as_user(host, user, -+ host.config.ad_admin_password -+ ) -+ ssh_cmd = "ssh -q -K -l {user} {host} hostname" -+ valid_ssh = host.run_command( -+ ssh_cmd.format(user=user, host=host.hostname) -+ ) -+ assert host.hostname in valid_ssh.stdout_text -+ finally: -+ tasks.kdestroy_all(host) -+ -+ sshuser(self.master, testuser) -+ sshuser(self.master, testusersub) -+ - def test_remove_nonposix_trust(self): - self.remove_trust(self.ad) - tasks.unconfigure_dns_for_trust(self.master, self.ad) -@@ -785,6 +814,23 @@ class TestTrust(BaseTestTrust): - assert re.search( - testuser_regex, result.stdout_text), result.stdout_text - -+ def test_ssh_adtreeuser(self): -+ testuser = 'treetestuser@{0}'.format(self.ad_treedomain) -+ self.master.run_command(["id", testuser]) -+ tasks.clear_sssd_cache(self.master) -+ tasks.kdestroy_all(self.master) -+ try: -+ tasks.kinit_as_user(self.master, testuser, -+ password="Secret123456" -+ ) -+ ssh_cmd = "ssh -q -K -l {user} {host} hostname" -+ valid_ssh = self.master.run_command( -+ ssh_cmd.format(user=testuser, host=self.master.hostname) -+ ) -+ assert self.master.hostname in valid_ssh.stdout_text -+ finally: -+ tasks.kdestroy_all(self.master) -+ - def test_remove_external_treedomain_trust(self): - self.remove_trust(self.tree_ad) - tasks.unconfigure_dns_for_trust(self.master, self.ad, self.tree_ad) --- -2.39.1 - diff --git a/SOURCES/0014-ipatests-enable-firewall-rule-for-http-service-on-ac.patch b/SOURCES/0014-ipatests-enable-firewall-rule-for-http-service-on-ac.patch new file mode 100644 index 0000000..693cfad --- /dev/null +++ b/SOURCES/0014-ipatests-enable-firewall-rule-for-http-service-on-ac.patch @@ -0,0 +1,42 @@ +From 1e8352486cd5f77ff79e18798f04f406baf0a9a1 Mon Sep 17 00:00:00 2001 +From: Mohammad Rizwan +Date: Wed, 14 Jun 2023 17:32:02 +0530 +Subject: [PATCH] ipatests: enable firewall rule for http service on acme + client + +when system hardning done i.e in case of STIG, sometimes http challanges +can't be validated by CA if port 80 is not open. This fix enable it to facilitate +the communication. + +Signed-off-by: Mohammad Rizwan +Reviewed-By: Rob Crittenden +Reviewed-By: Florence Blanc-Renaud +--- + ipatests/test_integration/test_acme.py | 4 ++++ + 1 file changed, 4 insertions(+) + +diff --git a/ipatests/test_integration/test_acme.py b/ipatests/test_integration/test_acme.py +index 9718c408b7f48dd78dc2abae32fb9ecb85445dfb..cca20983e65f99d5ba0bb7bc6dc2b5684a6f37d9 100644 +--- a/ipatests/test_integration/test_acme.py ++++ b/ipatests/test_integration/test_acme.py +@@ -10,6 +10,7 @@ import pytest + + from ipalib.constants import IPA_CA_RECORD + from ipatests.test_integration.base import IntegrationTest ++from ipatests.pytest_ipa.integration.firewall import Firewall + from ipatests.pytest_ipa.integration import tasks + from ipatests.test_integration.test_caless import CALessBase, ipa_certs_cleanup + from ipatests.test_integration.test_random_serial_numbers import ( +@@ -85,6 +86,9 @@ def prepare_acme_client(master, client): + acme_host = f'{IPA_CA_RECORD}.{master.domain.name}' + acme_server = f'https://{acme_host}/acme/directory' + ++ # enable firewall rule on client ++ Firewall(client).enable_services(["http", "https"]) ++ + # install acme client packages + if not skip_certbot_tests: + tasks.install_packages(client, ['certbot']) +-- +2.41.0 + diff --git a/SOURCES/0015-User-plugin-improve-error-related-to-non-existing-id.patch b/SOURCES/0015-User-plugin-improve-error-related-to-non-existing-id.patch new file mode 100644 index 0000000..c92a18c --- /dev/null +++ b/SOURCES/0015-User-plugin-improve-error-related-to-non-existing-id.patch @@ -0,0 +1,79 @@ +From 387873080f1bc14aeaad89311b06dc46934be1ab Mon Sep 17 00:00:00 2001 +From: Florence Blanc-Renaud +Date: Wed, 19 Jul 2023 13:24:55 +0200 +Subject: [PATCH] User plugin: improve error related to non existing idp + +The user and stageuser commands return the following error +when the user is created/updated with a non existing idp: +$ ipa user-add testuser --first test --last user --idp dummy +ipa: ERROR: no such entry + +The error is not descriptive enough and has been modified to +display instead: +$ ipa user-add testuser --first test --last user --idp dummy +ipa: ERROR: External IdP configuration dummy not found + +Fixes: https://pagure.io/freeipa/issue/9416 + +Signed-off-by: Florence Blanc-Renaud +Reviewed-By: Rob Crittenden +--- + ipaserver/plugins/baseuser.py | 6 +++++- + ipaserver/plugins/stageuser.py | 6 +++++- + ipaserver/plugins/user.py | 6 +++++- + 3 files changed, 15 insertions(+), 3 deletions(-) + +diff --git a/ipaserver/plugins/baseuser.py b/ipaserver/plugins/baseuser.py +index 73b76d328a88639afd40bd261c8a35f324ec865b..ba5f9b7763662b32f238c0fb0ca548ff2f07db0d 100644 +--- a/ipaserver/plugins/baseuser.py ++++ b/ipaserver/plugins/baseuser.py +@@ -708,7 +708,11 @@ class baseuser_mod(LDAPUpdate): + if 'ipaidpuser' not in obj_classes: + entry_attrs['objectclass'].append('ipaidpuser') + +- answer = self.api.Object['idp'].get_dn_if_exists(cl) ++ try: ++ answer = self.api.Object['idp'].get_dn_if_exists(cl) ++ except errors.NotFound: ++ reason = "External IdP configuration {} not found" ++ raise errors.NotFound(reason=_(reason).format(cl)) + entry_attrs['ipaidpconfiglink'] = answer + + # Note: we could have used the method add_missing_object_class +diff --git a/ipaserver/plugins/stageuser.py b/ipaserver/plugins/stageuser.py +index 51438a83a95d15fb320148d2934a52f13a38f390..852e51b0eb0d757940b84721a6f01e43c5f36dd2 100644 +--- a/ipaserver/plugins/stageuser.py ++++ b/ipaserver/plugins/stageuser.py +@@ -404,7 +404,11 @@ class stageuser_add(baseuser_add): + if 'ipaidpuser' not in entry_attrs['objectclass']: + entry_attrs['objectclass'].append('ipaidpuser') + +- answer = self.api.Object['idp'].get_dn_if_exists(cl) ++ try: ++ answer = self.api.Object['idp'].get_dn_if_exists(cl) ++ except errors.NotFound: ++ reason = "External IdP configuration {} not found" ++ raise errors.NotFound(reason=_(reason).format(cl)) + entry_attrs['ipaidpconfiglink'] = answer + + self.pre_common_callback(ldap, dn, entry_attrs, attrs_list, *keys, +diff --git a/ipaserver/plugins/user.py b/ipaserver/plugins/user.py +index 643b44f141e3add76f95cbeec6e90fec0ad4c9ad..a337e1fc7b44ef41ad16e18bd965b7af0a767d05 100644 +--- a/ipaserver/plugins/user.py ++++ b/ipaserver/plugins/user.py +@@ -638,7 +638,11 @@ class user_add(baseuser_add): + if 'ipaidpuser' not in entry_attrs['objectclass']: + entry_attrs['objectclass'].append('ipaidpuser') + +- answer = self.api.Object['idp'].get_dn_if_exists(rcl) ++ try: ++ answer = self.api.Object['idp'].get_dn_if_exists(rcl) ++ except errors.NotFound: ++ reason = "External IdP configuration {} not found" ++ raise errors.NotFound(reason=_(reason).format(rcl)) + entry_attrs['ipaidpconfiglink'] = answer + + self.pre_common_callback(ldap, dn, entry_attrs, attrs_list, *keys, +-- +2.41.0 + diff --git a/SOURCES/0015-webui-tests-fix-assertion-in-test_subid.py.patch b/SOURCES/0015-webui-tests-fix-assertion-in-test_subid.py.patch deleted file mode 100644 index 7fc4350..0000000 --- a/SOURCES/0015-webui-tests-fix-assertion-in-test_subid.py.patch +++ /dev/null @@ -1,36 +0,0 @@ -From c411c2e7b2e400829ffac250db81609ef3c56faa Mon Sep 17 00:00:00 2001 -From: Florence Blanc-Renaud -Date: Tue, 29 Nov 2022 10:04:41 +0100 -Subject: [PATCH] webui tests: fix assertion in test_subid.py - -The test wants to check the error related to an -exception obtained inside a "with pytest.raises" instruction. -The object is an ExceptionInfo and offers a match method -to check the content of the string representation. -Use this match() method instead of str(excinfo) which now -returns -'' - -Fixes: https://pagure.io/freeipa/issue/9282 - -Signed-off-by: Florence Blanc-Renaud -Reviewed-By: Mohammad Rizwan Yusuf ---- - ipatests/test_webui/test_subid.py | 4 ++-- - 1 file changed, 2 insertions(+), 2 deletions(-) - -diff --git a/ipatests/test_webui/test_subid.py b/ipatests/test_webui/test_subid.py -index 104b5692da94437880e638c0b2bc8efd41bd969e..3aaf80ac885fea08d0bac7e2f46645fe207f2cb0 100644 ---- a/ipatests/test_webui/test_subid.py -+++ b/ipatests/test_webui/test_subid.py -@@ -146,5 +146,5 @@ class test_subid(UI_driver): - with pytest.raises(NoSuchElementException) as excinfo: - self.delete_record(admin_uid, table_name="ipauniqueid") - # Ensure that the exception is really related to missing remove button -- msg = "Unable to locate element: .facet-controls button[name=remove]" -- assert msg in str(excinfo) -+ msg = r"Unable to locate element: .facet-controls button\[name=remove\]" -+ assert excinfo.match(msg) --- -2.39.1 - diff --git a/SOURCES/0016-ipatests-mark-test_smb-as-xfail.patch b/SOURCES/0016-ipatests-mark-test_smb-as-xfail.patch deleted file mode 100644 index 1d4267a..0000000 --- a/SOURCES/0016-ipatests-mark-test_smb-as-xfail.patch +++ /dev/null @@ -1,29 +0,0 @@ -From b5f2b0b1b213149b5bfe2653c9e40de98249dc73 Mon Sep 17 00:00:00 2001 -From: Florence Blanc-Renaud -Date: Tue, 10 Jan 2023 11:45:17 +0100 -Subject: [PATCH] ipatests: mark test_smb as xfail - -Mark the test test_smb.py::TestSMB::test_smb_service_s4u2self as xfail. - -Related: https://pagure.io/freeipa/issue/9124 -Signed-off-by: Florence Blanc-Renaud -Reviewed-By: Alexander Bokovoy ---- - ipatests/test_integration/test_smb.py | 1 + - 1 file changed, 1 insertion(+) - -diff --git a/ipatests/test_integration/test_smb.py b/ipatests/test_integration/test_smb.py -index eb3981bddb7ca9f72a0d2cb6c46e5c73de8623ac..30f8d5901afbcda95f27cd966ac03d47205dbb26 100644 ---- a/ipatests/test_integration/test_smb.py -+++ b/ipatests/test_integration/test_smb.py -@@ -349,6 +349,7 @@ class TestSMB(IntegrationTest): - @pytest.mark.skipif( - osinfo.id == 'fedora' and osinfo.version_number <= (31,), - reason='Test requires krb 1.18') -+ @pytest.mark.xfail(reason="Pagure ticket 9124", strict=True) - def test_smb_service_s4u2self(self): - """Test S4U2Self operation by IPA service - against both AD and IPA users --- -2.39.1 - diff --git a/SOURCES/0016-xmlrpc-tests-add-a-test-for-user-plugin-with-non-exi.patch b/SOURCES/0016-xmlrpc-tests-add-a-test-for-user-plugin-with-non-exi.patch new file mode 100644 index 0000000..e5bae94 --- /dev/null +++ b/SOURCES/0016-xmlrpc-tests-add-a-test-for-user-plugin-with-non-exi.patch @@ -0,0 +1,122 @@ +From caacccc6b92c08f510fba2e31d9c56eb372abddc Mon Sep 17 00:00:00 2001 +From: Florence Blanc-Renaud +Date: Wed, 19 Jul 2023 13:28:43 +0200 +Subject: [PATCH] xmlrpc tests: add a test for user plugin with non-existing + idp + +Add new tests checking the error returned for +ipa user-add ... --idp nonexistingidp +ipa user-mod ... --idp nonexistingidp +ipa stageuser-add ... --idp nonexistingidp +ipa stageuser-mod ... --idp nonexistingidp + +The expected error message is: +ipa: ERROR: External IdP configuration nonexistingidp not found + +Related: https://pagure.io/freeipa/issue/9416 + +Signed-off-by: Florence Blanc-Renaud +Reviewed-By: Rob Crittenden +--- + ipatests/test_xmlrpc/test_stageuser_plugin.py | 20 +++++++++++++++ + ipatests/test_xmlrpc/test_user_plugin.py | 25 +++++++++++++++++++ + 2 files changed, 45 insertions(+) + +diff --git a/ipatests/test_xmlrpc/test_stageuser_plugin.py b/ipatests/test_xmlrpc/test_stageuser_plugin.py +index 394015f87f9f4bd275a15bab930e28f16b299274..9ae5561dfa4e0d54fe1231501bfea3c0ba261849 100644 +--- a/ipatests/test_xmlrpc/test_stageuser_plugin.py ++++ b/ipatests/test_xmlrpc/test_stageuser_plugin.py +@@ -39,6 +39,8 @@ gid = u'456' + invalidrealm1 = u'suser1@NOTFOUND.ORG' + invalidrealm2 = u'suser1@BAD@NOTFOUND.ORG' + ++nonexistentidp = 'IdPDoesNotExist' ++ + invaliduser1 = u'+tuser1' + invaliduser2 = u'tuser1234567890123456789012345678901234567890' + +@@ -431,6 +433,15 @@ class TestCreateInvalidAttributes(XMLRPC_test): + invalidrealm2))): + command() + ++ def test_create_invalid_idp(self, stageduser): ++ stageduser.ensure_missing() ++ command = stageduser.make_create_command( ++ options={u'ipaidpconfiglink': nonexistentidp}) ++ with raises_exact(errors.NotFound( ++ reason="External IdP configuration {} not found".format( ++ nonexistentidp))): ++ command() ++ + + @pytest.mark.tier1 + class TestUpdateInvalidAttributes(XMLRPC_test): +@@ -466,6 +477,15 @@ class TestUpdateInvalidAttributes(XMLRPC_test): + message=u'invalid \'gidnumber\': must be at least 1')): + command() + ++ def test_update_invalididp(self, stageduser): ++ stageduser.ensure_exists() ++ command = stageduser.make_update_command( ++ updates={u'ipaidpconfiglink': nonexistentidp}) ++ with raises_exact(errors.NotFound( ++ reason="External IdP configuration {} not found".format( ++ nonexistentidp))): ++ command() ++ + + @pytest.mark.tier1 + class TestActive(XMLRPC_test): +diff --git a/ipatests/test_xmlrpc/test_user_plugin.py b/ipatests/test_xmlrpc/test_user_plugin.py +index 8ac19a4f9ce4f341838282ecd3ed1bb491ac7004..baa28672e7552140a703ecdfa5772b445298cb37 100644 +--- a/ipatests/test_xmlrpc/test_user_plugin.py ++++ b/ipatests/test_xmlrpc/test_user_plugin.py +@@ -86,6 +86,8 @@ expired_expiration_string = "1991-12-07T19:54:13Z" + # Date in ISO format (2013-12-10T12:00:00) + isodate_re = re.compile(r'^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}Z$') + ++nonexistentidp = 'IdPDoesNotExist' ++ + + @pytest.fixture(scope='class') + def user_min(request, xmlrpc_setup): +@@ -542,6 +544,18 @@ class TestUpdate(XMLRPC_test): + command() + user.delete() + ++ def test_update_invalid_idp(self, user): ++ """ Test user-mod --idp with a non-existent idp """ ++ user.ensure_exists() ++ command = user.make_update_command( ++ updates=dict(ipaidpconfiglink=nonexistentidp) ++ ) ++ with raises_exact(errors.NotFound( ++ reason="External IdP configuration {} not found".format( ++ nonexistentidp) ++ )): ++ command() ++ + + @pytest.mark.tier1 + class TestCreate(XMLRPC_test): +@@ -770,6 +784,17 @@ class TestCreate(XMLRPC_test): + user_radius.check_create(result) + user_radius.delete() + ++ def test_create_with_invalididp(self): ++ testuser = UserTracker( ++ name='idpuser', givenname='idp', sn='user', ++ ipaidpconfiglink=nonexistentidp ++ ) ++ with raises_exact(errors.NotFound( ++ reason="External IdP configuration {} not found".format( ++ nonexistentidp) ++ )): ++ testuser.create() ++ + + @pytest.mark.tier1 + class TestUserWithGroup(XMLRPC_test): +-- +2.41.0 + diff --git a/SOURCES/0017-Fix-memory-leak-in-the-OTP-last-token-plugin.patch b/SOURCES/0017-Fix-memory-leak-in-the-OTP-last-token-plugin.patch new file mode 100644 index 0000000..08b2092 --- /dev/null +++ b/SOURCES/0017-Fix-memory-leak-in-the-OTP-last-token-plugin.patch @@ -0,0 +1,117 @@ +From 421e8e9ac886c50b4bb463a62b8ad5de8da94f31 Mon Sep 17 00:00:00 2001 +From: Rob Crittenden +Date: Mon, 26 Jun 2023 13:06:51 -0400 +Subject: [PATCH] Fix memory leak in the OTP last token plugin + +Three memory leaks are addressed: + +1. String values retrieved from the pblock need to be manually +freed. + +2. The list of objectclasses retreived from the pblock need to be +freed. + +3. Internal search results need to be freed. + +Fixes: https://pagure.io/freeipa/issue/9403 + +Signed-off-by: Rob Crittenden +Reviewed-By: Rafael Guterres Jeffman +Reviewed-By: Alexander Bokovoy +--- + .../ipa-otp-lasttoken/ipa_otp_lasttoken.c | 38 +++++++++++++------ + daemons/ipa-slapi-plugins/libotp/otp_token.c | 1 + + 2 files changed, 27 insertions(+), 12 deletions(-) + +diff --git a/daemons/ipa-slapi-plugins/ipa-otp-lasttoken/ipa_otp_lasttoken.c b/daemons/ipa-slapi-plugins/ipa-otp-lasttoken/ipa_otp_lasttoken.c +index b7a2ba7f012fdbf90284ee6605788e196aa4793b..11106b239f9de9074125979cfae7c02e434936e1 100644 +--- a/daemons/ipa-slapi-plugins/ipa-otp-lasttoken/ipa_otp_lasttoken.c ++++ b/daemons/ipa-slapi-plugins/ipa-otp-lasttoken/ipa_otp_lasttoken.c +@@ -54,7 +54,7 @@ void *ipa_otp_lasttoken_plugin_id; + + static bool entry_is_token(Slapi_Entry *entry) + { +- char **ocls; ++ char **ocls = NULL; + + ocls = slapi_entry_attr_get_charray(entry, SLAPI_ATTR_OBJECTCLASS); + for (size_t i = 0; ocls != NULL && ocls[i] != NULL; i++) { +@@ -64,6 +64,7 @@ static bool entry_is_token(Slapi_Entry *entry) + } + } + ++ slapi_ch_array_free(ocls); + return false; + } + +@@ -138,7 +139,8 @@ static bool is_pwd_enabled(const char *user_dn) + static bool is_allowed(Slapi_PBlock *pb, Slapi_Entry *entry) + { + Slapi_DN *target_sdn = NULL; +- const char *bind_dn; ++ char *bind_dn; ++ bool rv = false; + + /* Ignore internal operations. */ + if (slapi_op_internal(pb)) +@@ -147,23 +149,35 @@ static bool is_allowed(Slapi_PBlock *pb, Slapi_Entry *entry) + /* Load parameters. */ + (void) slapi_pblock_get(pb, SLAPI_TARGET_SDN, &target_sdn); + (void) slapi_pblock_get(pb, SLAPI_CONN_DN, &bind_dn); +- if (target_sdn == NULL || bind_dn == NULL) { +- LOG_FATAL("Missing parameters!\n"); +- return false; ++ if (bind_dn == NULL) { ++ LOG_FATAL("bind_dn parameter missing!\n"); ++ goto done; ++ } ++ if (target_sdn == NULL) { ++ LOG_FATAL("target_sdn parameter missing!\n"); ++ goto done; + } + + if (entry != NULL + ? !entry_is_token(entry) +- : !sdn_in_otp_container(target_sdn)) +- return true; ++ : !sdn_in_otp_container(target_sdn)) { ++ rv = true; ++ goto done; ++ } + +- if (!sdn_is_only_enabled_token(target_sdn, bind_dn)) +- return true; ++ if (!sdn_is_only_enabled_token(target_sdn, bind_dn)) { ++ rv = true; ++ goto done; ++ } + +- if (is_pwd_enabled(bind_dn)) +- return true; ++ if (is_pwd_enabled(bind_dn)) { ++ rv = true; ++ goto done; ++ } + +- return false; ++done: ++ slapi_ch_free_string(&bind_dn); ++ return rv; + } + + static inline int send_error(Slapi_PBlock *pb, int rc, const char *errstr) +diff --git a/daemons/ipa-slapi-plugins/libotp/otp_token.c b/daemons/ipa-slapi-plugins/libotp/otp_token.c +index a3cbfb0621c071f8addb29f7ce02f870a807c61d..4be4ede07cbbd0d26bcc9952ef4d84d777076ae7 100644 +--- a/daemons/ipa-slapi-plugins/libotp/otp_token.c ++++ b/daemons/ipa-slapi-plugins/libotp/otp_token.c +@@ -398,6 +398,7 @@ static struct otp_token **find(const struct otp_config *cfg, const char *user_dn + } + + error: ++ slapi_free_search_results_internal(pb); + slapi_pblock_destroy(pb); + return tokens; + } +-- +2.41.0 + diff --git a/SOURCES/0017-Tests-force-key-type-in-ACME-tests.patch b/SOURCES/0017-Tests-force-key-type-in-ACME-tests.patch deleted file mode 100644 index 5ae7d8f..0000000 --- a/SOURCES/0017-Tests-force-key-type-in-ACME-tests.patch +++ /dev/null @@ -1,43 +0,0 @@ -From 36cba23f3f671886f5e7fa310c25a6e500c76e0b Mon Sep 17 00:00:00 2001 -From: Florence Blanc-Renaud -Date: Mon, 16 Jan 2023 09:31:57 +0100 -Subject: [PATCH] Tests: force key type in ACME tests - -PKI can issue ACME certs only when the key type is rsa. - -With version 2.0.0, certbot defaults to ecdsa key type, -and this causes test failures. -For now, force rsa when requesting an ACME certificate. -This change can be reverted when PKI fixes the issue -on their side (https://github.com/dogtagpki/pki/issues/4273) - -Related: https://pagure.io/freeipa/issue/9298 -Signed-off-by: Florence Blanc-Renaud -Reviewed-By: Rob Crittenden ---- - ipatests/test_integration/test_acme.py | 2 ++ - 1 file changed, 2 insertions(+) - -diff --git a/ipatests/test_integration/test_acme.py b/ipatests/test_integration/test_acme.py -index a30f2fc756783c0a5c28ecf32c1e40f422c47a19..15d7543cfb0fa0fcb921166f7cd8f13d0535a41d 100644 ---- a/ipatests/test_integration/test_acme.py -+++ b/ipatests/test_integration/test_acme.py -@@ -131,6 +131,7 @@ def certbot_standalone_cert(host, acme_server): - 'certonly', - '--domain', host.hostname, - '--standalone', -+ '--key-type', 'rsa', - ] - ) - -@@ -305,6 +306,7 @@ class TestACME(CALessBase): - '--manual-public-ip-logging-ok', - '--manual-auth-hook', CERTBOT_DNS_IPA_SCRIPT, - '--manual-cleanup-hook', CERTBOT_DNS_IPA_SCRIPT, -+ '--key-type', 'rsa', - ]) - - ############## --- -2.39.1 - diff --git a/SOURCES/0018-Prevent-the-admin-user-from-being-deleted.patch b/SOURCES/0018-Prevent-the-admin-user-from-being-deleted.patch new file mode 100644 index 0000000..ee01e45 --- /dev/null +++ b/SOURCES/0018-Prevent-the-admin-user-from-being-deleted.patch @@ -0,0 +1,136 @@ +From 4b02322fc786ee9caaa0380659507a2cec0d4101 Mon Sep 17 00:00:00 2001 +From: Rob Crittenden +Date: Thu, 25 May 2023 18:24:29 -0400 +Subject: [PATCH] Prevent the admin user from being deleted + +admin is required for trust operations + +Note that testing for removing the last member is now +irrelevant because admin must always exist so the test +for it was removed, but the code check remains. It is done +after the protected member check. + +Fixes: https://pagure.io/freeipa/issue/8878 + +Signed-off-by: Rob Crittenden +Reviewed-By: Alexander Bokovoy +--- + ipaserver/plugins/user.py | 19 +++++++++-- + ipatests/test_xmlrpc/test_user_plugin.py | 40 ++++++++++++------------ + 2 files changed, 37 insertions(+), 22 deletions(-) + +diff --git a/ipaserver/plugins/user.py b/ipaserver/plugins/user.py +index a337e1fc7b44ef41ad16e18bd965b7af0a767d05..6f5e34917e1b838a463dee146a4e9390f20c130a 100644 +--- a/ipaserver/plugins/user.py ++++ b/ipaserver/plugins/user.py +@@ -138,14 +138,23 @@ MEMBEROF_ADMINS = "(memberOf={})".format( + ) + + NOT_MEMBEROF_ADMINS = '(!{})'.format(MEMBEROF_ADMINS) ++PROTECTED_USERS = ('admin',) + + + def check_protected_member(user, protected_group_name=u'admins'): + ''' +- Ensure the last enabled member of a protected group cannot be deleted or +- disabled by raising LastMemberError. ++ Ensure admin and the last enabled member of a protected group cannot ++ be deleted or disabled by raising ProtectedEntryError or ++ LastMemberError as appropriate. + ''' + ++ if user in PROTECTED_USERS: ++ raise errors.ProtectedEntryError( ++ label=_("user"), ++ key=user, ++ reason=_("privileged user"), ++ ) ++ + # Get all users in the protected group + result = api.Command.user_find(in_group=protected_group_name) + +@@ -868,6 +877,12 @@ class user_mod(baseuser_mod): + + def pre_callback(self, ldap, dn, entry_attrs, attrs_list, *keys, **options): + dn, oc = self.obj.get_either_dn(*keys, **options) ++ if options.get('rename') and keys[-1] in PROTECTED_USERS: ++ raise errors.ProtectedEntryError( ++ label=_("user"), ++ key=keys[-1], ++ reason=_("privileged user"), ++ ) + if 'objectclass' not in entry_attrs and 'rename' not in options: + entry_attrs.update({'objectclass': oc}) + self.pre_common_callback(ldap, dn, entry_attrs, attrs_list, *keys, +diff --git a/ipatests/test_xmlrpc/test_user_plugin.py b/ipatests/test_xmlrpc/test_user_plugin.py +index baa28672e7552140a703ecdfa5772b445298cb37..df105a23529b29944411a6418e5db55d56e2c72a 100644 +--- a/ipatests/test_xmlrpc/test_user_plugin.py ++++ b/ipatests/test_xmlrpc/test_user_plugin.py +@@ -978,22 +978,32 @@ class TestManagers(XMLRPC_test): + + @pytest.mark.tier1 + class TestAdmins(XMLRPC_test): +- def test_remove_original_admin(self): +- """ Try to remove the only admin """ ++ def test_delete_admin(self): ++ """ Try to delete the protected admin user """ + tracker = Tracker() +- command = tracker.make_command('user_del', [admin1]) ++ command = tracker.make_command('user_del', admin1) + +- with raises_exact(errors.LastMemberError( +- key=admin1, label=u'group', container=admin_group)): ++ with raises_exact(errors.ProtectedEntryError(label=u'user', ++ key=admin1, reason='privileged user')): ++ command() ++ ++ def test_rename_admin(self): ++ """ Try to rename the admin user """ ++ tracker = Tracker() ++ command = tracker.make_command('user_mod', admin1, ++ **dict(rename=u'newadmin')) ++ ++ with raises_exact(errors.ProtectedEntryError(label=u'user', ++ key=admin1, reason='privileged user')): + command() + + def test_disable_original_admin(self): +- """ Try to disable the only admin """ ++ """ Try to disable the original admin """ + tracker = Tracker() + command = tracker.make_command('user_disable', admin1) + +- with raises_exact(errors.LastMemberError( +- key=admin1, label=u'group', container=admin_group)): ++ with raises_exact(errors.ProtectedEntryError(label=u'user', ++ key=admin1, reason='privileged user')): + command() + + def test_create_admin2(self, admin2): +@@ -1011,21 +1021,11 @@ class TestAdmins(XMLRPC_test): + admin2.disable() + tracker = Tracker() + +- with raises_exact(errors.LastMemberError( +- key=admin1, label=u'group', container=admin_group)): ++ with raises_exact(errors.ProtectedEntryError(label=u'user', ++ key=admin1, reason='privileged user')): + tracker.run_command('user_disable', admin1) +- with raises_exact(errors.LastMemberError( +- key=admin1, label=u'group', container=admin_group)): +- tracker.run_command('user_del', admin1) + admin2.delete() + +- with raises_exact(errors.LastMemberError( +- key=admin1, label=u'group', container=admin_group)): +- tracker.run_command('user_disable', admin1) +- with raises_exact(errors.LastMemberError( +- key=admin1, label=u'group', container=admin_group)): +- tracker.run_command('user_del', admin1) +- + + @pytest.mark.tier1 + class TestPreferredLanguages(XMLRPC_test): +-- +2.41.0 + diff --git a/SOURCES/0018-tests-Add-ipa_ca_name-checking-to-DNS-system-records.patch b/SOURCES/0018-tests-Add-ipa_ca_name-checking-to-DNS-system-records.patch deleted file mode 100644 index 7af48d9..0000000 --- a/SOURCES/0018-tests-Add-ipa_ca_name-checking-to-DNS-system-records.patch +++ /dev/null @@ -1,36 +0,0 @@ -From ff31b0c40cc5e046f839b98b80bd16bb649205ac Mon Sep 17 00:00:00 2001 -From: Rob Crittenden -Date: Mon, 30 Jan 2023 11:54:36 -0500 -Subject: [PATCH] tests: Add ipa_ca_name checking to DNS system records - -freeipa-healthcheck 0.12 includes a SUCCESS message if the -ipa-ca records are as expected so a user will know they -were checked. For that version and beyond test that it -is included. - -Related: https://pagure.io/freeipa/issue/9291 - -Signed-off-by: Rob Crittenden -Reviewed-By: Florence Blanc-Renaud ---- - ipatests/test_integration/test_ipahealthcheck.py | 4 +++- - 1 file changed, 3 insertions(+), 1 deletion(-) - -diff --git a/ipatests/test_integration/test_ipahealthcheck.py b/ipatests/test_integration/test_ipahealthcheck.py -index 49a5779307ef05617fe9ae200f7149d120977355..94b0db0b7869e722955e232e1dddb26a2dc3d41e 100644 ---- a/ipatests/test_integration/test_ipahealthcheck.py -+++ b/ipatests/test_integration/test_ipahealthcheck.py -@@ -810,7 +810,9 @@ class TestIpaHealthCheck(IntegrationTest): - + [str(ip) for ip in resolve_ip_addresses_nss(h.external_hostname)] - ] - SYSTEM_RECORDS.append(f'"{self.master.domain.realm.upper()}"') -- -+ version = tasks.get_healthcheck_version(self.master) -+ if parse_version(version) >= parse_version("0.12"): -+ SYSTEM_RECORDS.append('ipa_ca_check') - - returncode, data = run_healthcheck( - self.master, --- -2.39.1 - diff --git a/SOURCES/0019-ipa-kdb-fix-error-handling-of-is_master_host.patch b/SOURCES/0019-ipa-kdb-fix-error-handling-of-is_master_host.patch new file mode 100644 index 0000000..957d97f --- /dev/null +++ b/SOURCES/0019-ipa-kdb-fix-error-handling-of-is_master_host.patch @@ -0,0 +1,87 @@ +From fd32e6a3d95f28d2d11d41ee5dabb0d563cb5d51 Mon Sep 17 00:00:00 2001 +From: Julien Rische +Date: Mon, 31 Jul 2023 11:26:43 +0200 +Subject: [PATCH] ipa-kdb: fix error handling of is_master_host() + +Adding proper error handling to the is_master_host() function to allow +it to make the difference between the absence of a master host object +and a connection failure. This will keep the krb5kdc daemon from +continuing to run with a NULL LDAP context. + +Fixes: https://pagure.io/freeipa/issue/9422 + +Signed-off-by: Julien Rische +Reviewed-By: Alexander Bokovoy +--- + daemons/ipa-kdb/ipa_kdb_mspac.c | 41 +++++++++++++++++++-------------- + 1 file changed, 24 insertions(+), 17 deletions(-) + +diff --git a/daemons/ipa-kdb/ipa_kdb_mspac.c b/daemons/ipa-kdb/ipa_kdb_mspac.c +index 83b507cb422c735f933edaebfc7b903b8fa908e4..1558e2bead288d9d00014e9b3b059934e80b54e4 100644 +--- a/daemons/ipa-kdb/ipa_kdb_mspac.c ++++ b/daemons/ipa-kdb/ipa_kdb_mspac.c +@@ -401,27 +401,29 @@ static krb5_error_code ipadb_add_asserted_identity(struct ipadb_context *ipactx, + return 0; + } + +-static bool is_master_host(struct ipadb_context *ipactx, const char *fqdn) ++static krb5_error_code ++is_master_host(struct ipadb_context *ipactx, const char *fqdn, bool *result) + { +- int ret; ++ int err; + char *master_host_base = NULL; +- LDAPMessage *result = NULL; +- krb5_error_code err; ++ LDAPMessage *ldap_res = NULL; + +- ret = asprintf(&master_host_base, "cn=%s,cn=masters,cn=ipa,cn=etc,%s", ++ err = asprintf(&master_host_base, "cn=%s,cn=masters,cn=ipa,cn=etc,%s", + fqdn, ipactx->base); +- if (ret == -1) { +- return false; +- } ++ if (err == -1) ++ return ENOMEM; ++ + err = ipadb_simple_search(ipactx, master_host_base, LDAP_SCOPE_BASE, +- NULL, NULL, &result); ++ NULL, NULL, &ldap_res); + free(master_host_base); +- ldap_msgfree(result); +- if (err == 0) { +- return true; +- } ++ ldap_msgfree(ldap_res); ++ if (err != KRB5_KDB_NOENTRY && err != 0) ++ return err; ++ ++ if (result) ++ *result = err != KRB5_KDB_NOENTRY; + +- return false; ++ return 0; + } + + static krb5_error_code ipadb_fill_info3(struct ipadb_context *ipactx, +@@ -692,9 +694,14 @@ static krb5_error_code ipadb_fill_info3(struct ipadb_context *ipactx, + if ((is_host || is_service)) { + /* it is either host or service, so get the hostname first */ + char *sep = strchr(info3->base.account_name.string, '/'); +- bool is_master = is_master_host( +- ipactx, +- sep ? sep + 1 : info3->base.account_name.string); ++ bool is_master; ++ ++ ret = is_master_host(ipactx, ++ sep ? sep + 1 : info3->base.account_name.string, ++ &is_master); ++ if (ret) ++ return ret; ++ + if (is_master) { + /* Well known RID of domain controllers group */ + if (info3->base.rid == 0) { +-- +2.41.0 + diff --git a/SOURCES/0019-tests-Add-new-ipa-ca-error-messages-to-IPADNSSystemR.patch b/SOURCES/0019-tests-Add-new-ipa-ca-error-messages-to-IPADNSSystemR.patch deleted file mode 100644 index 53aca9c..0000000 --- a/SOURCES/0019-tests-Add-new-ipa-ca-error-messages-to-IPADNSSystemR.patch +++ /dev/null @@ -1,53 +0,0 @@ -From 6ca119686aadfa72c0474f72758b63cd671952d4 Mon Sep 17 00:00:00 2001 -From: Rob Crittenden -Date: Mon, 30 Jan 2023 12:00:03 -0500 -Subject: [PATCH] tests: Add new ipa-ca error messages to - IPADNSSystemRecordsCheck - -freeipa-healthcheck changed some messages related to ipa-ca -DNS record validation in IPADNSSystemRecordsCheck. Include support -for it and retain backwards compatibility. - -Fixes: https://pagure.io/freeipa/issue/9291 - -Signed-off-by: Rob Crittenden -Reviewed-By: Florence Blanc-Renaud ---- - .../test_integration/test_ipahealthcheck.py | 21 +++++++++++++------ - 1 file changed, 15 insertions(+), 6 deletions(-) - -diff --git a/ipatests/test_integration/test_ipahealthcheck.py b/ipatests/test_integration/test_ipahealthcheck.py -index 94b0db0b7869e722955e232e1dddb26a2dc3d41e..47f64f2cb36904ef61211423de7cf33d21a199c3 100644 ---- a/ipatests/test_integration/test_ipahealthcheck.py -+++ b/ipatests/test_integration/test_ipahealthcheck.py -@@ -1614,12 +1614,21 @@ class TestIpaHealthCheckWithoutDNS(IntegrationTest): - Test checks the result of IPADNSSystemRecordsCheck - when ipa-server is configured without DNS. - """ -- expected_msgs = { -- "Expected SRV record missing", -- "Got {count} ipa-ca A records, expected {expected}", -- "Got {count} ipa-ca AAAA records, expected {expected}", -- "Expected URI record missing", -- } -+ version = tasks.get_healthcheck_version(self.master) -+ if (parse_version(version) < parse_version('0.12')): -+ expected_msgs = { -+ "Expected SRV record missing", -+ "Got {count} ipa-ca A records, expected {expected}", -+ "Got {count} ipa-ca AAAA records, expected {expected}", -+ "Expected URI record missing", -+ } -+ else: -+ expected_msgs = { -+ "Expected SRV record missing", -+ "Unexpected ipa-ca address {ipaddr}", -+ "expected ipa-ca to contain {ipaddr} for {server}", -+ "Expected URI record missing", -+ } - - tasks.install_packages(self.master, HEALTHCHECK_PKG) - returncode, data = run_healthcheck( --- -2.39.1 - diff --git a/SOURCES/0020-ipatests-tests-for-certificate-pruning.patch b/SOURCES/0020-ipatests-tests-for-certificate-pruning.patch deleted file mode 100644 index 8095d99..0000000 --- a/SOURCES/0020-ipatests-tests-for-certificate-pruning.patch +++ /dev/null @@ -1,445 +0,0 @@ -From 0f77b359e241fc4055fb8d785e18f96338451ebf Mon Sep 17 00:00:00 2001 -From: Mohammad Rizwan -Date: Mon, 6 Feb 2023 15:31:27 +0530 -Subject: [PATCH] ipatests: tests for certificate pruning - -1. Test to prune the expired certificate by manual run -2. Test to prune expired certificate by cron job -3. Test to prune expired certificate with retention unit option -4. Test to prune expired certificate with search size limit option -5. Test to check config-show command shows set param -6. Test prune command shows proper status after disabling the pruning - -related: https://pagure.io/freeipa/issue/9294 - -Signed-off-by: Mohammad Rizwan -Reviewed-By: Rob Crittenden ---- - ipatests/test_integration/test_acme.py | 306 +++++++++++++++++++++---- - 1 file changed, 260 insertions(+), 46 deletions(-) - -diff --git a/ipatests/test_integration/test_acme.py b/ipatests/test_integration/test_acme.py -index 5ceba05976059de69414a79634d98045c3ab68bb..1334be52f4530dd8b2a4207744146cd0eb5477a3 100644 ---- a/ipatests/test_integration/test_acme.py -+++ b/ipatests/test_integration/test_acme.py -@@ -122,21 +122,23 @@ def certbot_register(host, acme_server): - ) - - --def certbot_standalone_cert(host, acme_server): -+def certbot_standalone_cert(host, acme_server, no_of_cert=1): - """method to issue a certbot's certonly standalone cert""" - # Get a cert from ACME service using HTTP challenge and Certbot's - # standalone HTTP server mode - host.run_command(['systemctl', 'stop', 'httpd']) -- host.run_command( -- [ -- 'certbot', -- '--server', acme_server, -- 'certonly', -- '--domain', host.hostname, -- '--standalone', -- '--key-type', 'rsa', -- ] -- ) -+ for _i in range(0, no_of_cert): -+ host.run_command( -+ [ -+ 'certbot', -+ '--server', acme_server, -+ 'certonly', -+ '--domain', host.hostname, -+ '--standalone', -+ '--key-type', 'rsa', -+ '--force-renewal' -+ ] -+ ) - - - class TestACME(CALessBase): -@@ -573,43 +575,41 @@ class TestACMEwithExternalCA(TestACME): - tasks.install_replica(cls.master, cls.replicas[0]) - - --class TestACMERenew(IntegrationTest): -- -- num_clients = 1 -+@pytest.fixture -+def issue_and_expire_acme_cert(): -+ """Fixture to expire cert by moving date past expiry of acme cert""" -+ hosts = [] - -- @classmethod -- def install(cls, mh): -- -- # install packages before client install in case of IPA DNS problems -- cls.acme_server = prepare_acme_client(cls.master, cls.clients[0]) -+ def _issue_and_expire_acme_cert( -+ master, client, -+ acme_server_url, no_of_cert=1 -+ ): - -- tasks.install_master(cls.master, setup_dns=True) -- tasks.install_client(cls.master, cls.clients[0]) -+ hosts.append(master) -+ hosts.append(client) - -- @pytest.fixture -- def issue_and_expire_cert(self): -- """Fixture to expire cert by moving date past expiry of acme cert""" - # enable the ACME service on master -- self.master.run_command(['ipa-acme-manage', 'enable']) -+ master.run_command(['ipa-acme-manage', 'enable']) - - # register the account with certbot -- certbot_register(self.clients[0], self.acme_server) -+ certbot_register(client, acme_server_url) - - # request a standalone acme cert -- certbot_standalone_cert(self.clients[0], self.acme_server) -+ certbot_standalone_cert(client, acme_server_url, no_of_cert) - - # move system date to expire acme cert -- for host in self.clients[0], self.master: -+ for host in hosts: - tasks.kdestroy_all(host) - tasks.move_date(host, 'stop', '+90days') - -+ time.sleep(10) - tasks.get_kdcinfo(host) - # Note raiseonerr=False: - # the assert is located after kdcinfo retrieval. -- result = host.run_command( -+ result = master.run_command( - "KRB5_TRACE=/dev/stdout kinit admin", - stdin_text='{0}\n{0}\n{0}\n'.format( -- self.clients[0].config.admin_password -+ master.config.admin_password - ), - raiseonerr=False - ) -@@ -618,16 +618,28 @@ class TestACMERenew(IntegrationTest): - tasks.get_kdcinfo(host) - assert result.returncode == 0 - -- yield -+ yield _issue_and_expire_acme_cert - -- # move back date -- for host in self.clients[0], self.master: -- tasks.kdestroy_all(host) -- tasks.move_date(host, 'start', '-90days') -- tasks.kinit_admin(host) -+ # move back date -+ for host in hosts: -+ tasks.move_date(host, 'start', '-90days') -+ -+ -+class TestACMERenew(IntegrationTest): -+ -+ num_clients = 1 -+ -+ @classmethod -+ def install(cls, mh): -+ -+ # install packages before client install in case of IPA DNS problems -+ cls.acme_server = prepare_acme_client(cls.master, cls.clients[0]) -+ -+ tasks.install_master(cls.master, setup_dns=True) -+ tasks.install_client(cls.master, cls.clients[0]) - - @pytest.mark.skipif(skip_certbot_tests, reason='certbot not available') -- def test_renew(self, issue_and_expire_cert): -+ def test_renew(self, issue_and_expire_acme_cert): - """Test if ACME renews the issued cert with cerbot - - This test is to check if ACME certificate renews upon -@@ -635,6 +647,8 @@ class TestACMERenew(IntegrationTest): - - related: https://pagure.io/freeipa/issue/4751 - """ -+ issue_and_expire_acme_cert( -+ self.master, self.clients[0], self.acme_server) - data = self.clients[0].get_file_contents( - f'/etc/letsencrypt/live/{self.clients[0].hostname}/cert.pem' - ) -@@ -656,6 +670,7 @@ class TestACMEPrune(IntegrationTest): - """Validate that ipa-acme-manage configures dogtag for pruning""" - - random_serial = True -+ num_clients = 1 - - @classmethod - def install(cls, mh): -@@ -663,6 +678,8 @@ class TestACMEPrune(IntegrationTest): - raise pytest.skip("RNSv3 not supported") - tasks.install_master(cls.master, setup_dns=True, - random_serial=True) -+ cls.acme_server = prepare_acme_client(cls.master, cls.clients[0]) -+ tasks.install_client(cls.master, cls.clients[0]) - - @classmethod - def uninstall(cls, mh): -@@ -718,7 +735,7 @@ class TestACMEPrune(IntegrationTest): - ['ipa-acme-manage', 'pruning', - '--requestretention=60', - '--requestretentionunit=minute', -- '--requestresearchsizelimit=2000', -+ '--requestsearchsizelimit=2000', - '--requestsearchtimelimit=5',] - ) - cs_cfg = self.master.get_file_contents(paths.CA_CS_CFG_PATH) -@@ -741,7 +758,7 @@ class TestACMEPrune(IntegrationTest): - - self.master.run_command( - ['ipa-acme-manage', 'pruning', -- '--cron="0 23 1 * *',] -+ '--cron=0 23 1 * *',] - ) - cs_cfg = self.master.get_file_contents(paths.CA_CS_CFG_PATH) - assert ( -@@ -760,7 +777,7 @@ class TestACMEPrune(IntegrationTest): - '--enable', '--disable'], - raiseonerr=False - ) -- assert result.returncode == 1 -+ assert result.returncode == 2 - assert "Cannot both enable and disable" in result.stderr_text - - for cmd in ('--config-show', '--run'): -@@ -769,20 +786,20 @@ class TestACMEPrune(IntegrationTest): - cmd, '--enable'], - raiseonerr=False - ) -- assert result.returncode == 1 -+ assert result.returncode == 2 - assert "Cannot change and show config" in result.stderr_text - - result = self.master.run_command( - ['ipa-acme-manage', 'pruning', -- '--cron="* *"'], -+ '--cron=* *'], - raiseonerr=False - ) -- assert result.returncode == 1 -- assert "Invalid format format --cron" in result.stderr_text -+ assert result.returncode == 2 -+ assert "Invalid format for --cron" in result.stderr_text - - result = self.master.run_command( - ['ipa-acme-manage', 'pruning', -- '--cron="100 * * * *"'], -+ '--cron=100 * * * *'], - raiseonerr=False - ) - assert result.returncode == 1 -@@ -790,8 +807,205 @@ class TestACMEPrune(IntegrationTest): - - result = self.master.run_command( - ['ipa-acme-manage', 'pruning', -- '--cron="10 1-5 * * *"'], -+ '--cron=10 1-5 * * *'], - raiseonerr=False - ) - assert result.returncode == 1 - assert "1-5 ranges are not supported" in result.stderr_text -+ -+ def test_prune_cert_manual(self, issue_and_expire_acme_cert): -+ """Test to prune expired certificate by manual run""" -+ if (tasks.get_pki_version(self.master) -+ < tasks.parse_version('11.3.0')): -+ raise pytest.skip("Certificate pruning is not available") -+ -+ issue_and_expire_acme_cert( -+ self.master, self.clients[0], self.acme_server) -+ -+ # check that the certificate issued for the client -+ result = self.master.run_command( -+ ['ipa', 'cert-find', '--subject', self.clients[0].hostname] -+ ) -+ assert f'CN={self.clients[0].hostname}' in result.stdout_text -+ -+ # run prune command manually -+ self.master.run_command(['ipa-acme-manage', 'pruning', '--enable']) -+ self.master.run_command(['ipactl', 'restart']) -+ self.master.run_command(['ipa-acme-manage', 'pruning', '--run']) -+ # wait for cert to get prune -+ time.sleep(50) -+ -+ # check if client cert is removed -+ result = self.master.run_command( -+ ['ipa', 'cert-find', '--subject', self.clients[0].hostname], -+ raiseonerr=False -+ ) -+ assert f'CN={self.clients[0].hostname}' not in result.stdout_text -+ -+ def test_prune_cert_cron(self, issue_and_expire_acme_cert): -+ """Test to prune expired certificate by cron job""" -+ if (tasks.get_pki_version(self.master) -+ < tasks.parse_version('11.3.0')): -+ raise pytest.skip("Certificate pruning is not available") -+ -+ issue_and_expire_acme_cert( -+ self.master, self.clients[0], self.acme_server) -+ -+ # check that the certificate issued for the client -+ result = self.master.run_command( -+ ['ipa', 'cert-find', '--subject', self.clients[0].hostname] -+ ) -+ assert f'CN={self.clients[0].hostname}' in result.stdout_text -+ -+ # enable pruning -+ self.master.run_command(['ipa-acme-manage', 'pruning', '--enable']) -+ -+ # cron would be set to run the next minute -+ cron_minute = self.master.run_command( -+ [ -+ "python3", -+ "-c", -+ ( -+ "from datetime import datetime; " -+ "print(int(datetime.now().strftime('%M')) + 5)" -+ ), -+ ] -+ ).stdout_text.strip() -+ self.master.run_command( -+ ['ipa-acme-manage', 'pruning', -+ f'--cron={cron_minute} * * * *'] -+ ) -+ self.master.run_command(['ipactl', 'restart']) -+ # wait for 5 minutes to cron to execute and 20 sec for just in case -+ time.sleep(320) -+ -+ # check if client cert is removed -+ result = self.master.run_command( -+ ['ipa', 'cert-find', '--subject', self.clients[0].hostname], -+ raiseonerr=False -+ ) -+ assert f'CN={self.clients[0].hostname}' not in result.stdout_text -+ -+ def test_prune_cert_retention_unit(self, issue_and_expire_acme_cert): -+ """Test to prune expired certificate with retention unit option""" -+ if (tasks.get_pki_version(self.master) -+ < tasks.parse_version('11.3.0')): -+ raise pytest.skip("Certificate pruning is not available") -+ issue_and_expire_acme_cert( -+ self.master, self.clients[0], self.acme_server) -+ -+ # check that the certificate issued for the client -+ result = self.master.run_command( -+ ['ipa', 'cert-find', '--subject', self.clients[0].hostname] -+ ) -+ assert f'CN={self.clients[0].hostname}' in result.stdout_text -+ -+ # enable pruning -+ self.master.run_command(['ipa-acme-manage', 'pruning', '--enable']) -+ -+ # certretention set to 5 min -+ self.master.run_command( -+ ['ipa-acme-manage', 'pruning', -+ '--certretention=5', '--certretentionunit=minute'] -+ ) -+ self.master.run_command(['ipactl', 'restart']) -+ -+ # wait for 5 min and check if expired cert is removed -+ time.sleep(310) -+ self.master.run_command(['ipa-acme-manage', 'pruning', '--run']) -+ result = self.master.run_command( -+ ['ipa', 'cert-find', '--subject', self.clients[0].hostname], -+ raiseonerr=False -+ ) -+ assert f'CN={self.clients[0].hostname}' not in result.stdout_text -+ -+ def test_prune_cert_search_size_limit(self, issue_and_expire_acme_cert): -+ """Test to prune expired certificate with search size limit option""" -+ if (tasks.get_pki_version(self.master) -+ < tasks.parse_version('11.3.0')): -+ raise pytest.skip("Certificate pruning is not available") -+ no_of_cert = 10 -+ search_size_limit = 5 -+ issue_and_expire_acme_cert( -+ self.master, self.clients[0], self.acme_server, no_of_cert) -+ -+ # check that the certificate issued for the client -+ result = self.master.run_command( -+ ['ipa', 'cert-find', '--subject', self.clients[0].hostname] -+ ) -+ assert f'CN={self.clients[0].hostname}' in result.stdout_text -+ assert f'Number of entries returned {no_of_cert}' -+ -+ # enable pruning -+ self.master.run_command(['ipa-acme-manage', 'pruning', '--enable']) -+ -+ # certretention set to 5 min -+ self.master.run_command( -+ ['ipa-acme-manage', 'pruning', -+ f'--certsearchsizelimit={search_size_limit}', -+ '--certsearchtimelimit=100'] -+ ) -+ self.master.run_command(['ipactl', 'restart']) -+ -+ # prune the certificates -+ self.master.run_command(['ipa-acme-manage', 'pruning', '--run']) -+ -+ # check if 5 expired cert is removed -+ result = self.master.run_command( -+ ['ipa', 'cert-find', '--subject', self.clients[0].hostname] -+ ) -+ assert f'Number of entries returned {no_of_cert - search_size_limit}' -+ -+ def test_prune_config_show(self, issue_and_expire_acme_cert): -+ """Test to check config-show command shows set param""" -+ if (tasks.get_pki_version(self.master) -+ < tasks.parse_version('11.3.0')): -+ raise pytest.skip("Certificate pruning is not available") -+ -+ self.master.run_command(['ipa-acme-manage', 'pruning', '--enable']) -+ self.master.run_command( -+ ['ipa-acme-manage', 'pruning', -+ '--cron=0 0 1 * *'] -+ ) -+ self.master.run_command( -+ ['ipa-acme-manage', 'pruning', -+ '--certretention=30', '--certretentionunit=day'] -+ ) -+ self.master.run_command( -+ ['ipa-acme-manage', 'pruning', -+ '--certsearchsizelimit=1000', '--certsearchtimelimit=0'] -+ ) -+ self.master.run_command( -+ ['ipa-acme-manage', 'pruning', -+ '--requestretention=30', '--requestretentionunit=day'] -+ ) -+ self.master.run_command( -+ ['ipa-acme-manage', 'pruning', -+ '--requestsearchsizelimit=1000', '--requestsearchtimelimit=0'] -+ ) -+ result = self.master.run_command( -+ ['ipa-acme-manage', 'pruning', '--config-show'] -+ ) -+ assert 'Status: enabled' in result.stdout_text -+ assert 'Certificate Retention Time: 30' in result.stdout_text -+ assert 'Certificate Retention Unit: day' in result.stdout_text -+ assert 'Certificate Search Size Limit: 1000' in result.stdout_text -+ assert 'Certificate Search Time Limit: 100' in result.stdout_text -+ assert 'Request Retention Time: 30' in result.stdout_text -+ assert 'Request Retention Unit: day' in result.stdout_text -+ assert 'Request Search Size Limit' in result.stdout_text -+ assert 'Request Search Time Limit: 100' in result.stdout_text -+ assert 'cron Schedule: 0 0 1 * *' in result.stdout_text -+ -+ def test_prune_disable(self, issue_and_expire_acme_cert): -+ """Test prune command throw error after disabling the pruning""" -+ if (tasks.get_pki_version(self.master) -+ < tasks.parse_version('11.3.0')): -+ raise pytest.skip("Certificate pruning is not available") -+ -+ self.master.run_command(['ipa-acme-manage', 'pruning', '--disable']) -+ result = self.master.run_command( -+ ['ipa-acme-manage', 'pruning', -+ '--cron=0 0 1 * *'] -+ ) -+ assert 'Status: disabled' in result.stdout_text --- -2.39.1 - diff --git a/SOURCES/0020-ipatests-update-expected-webui-msg-for-admin-deletio.patch b/SOURCES/0020-ipatests-update-expected-webui-msg-for-admin-deletio.patch new file mode 100644 index 0000000..17ad7c5 --- /dev/null +++ b/SOURCES/0020-ipatests-update-expected-webui-msg-for-admin-deletio.patch @@ -0,0 +1,38 @@ +From 13d5e88eb4ebb7a0132cbb050a9d230304ecbcff Mon Sep 17 00:00:00 2001 +From: Florence Blanc-Renaud +Date: Wed, 2 Aug 2023 15:41:57 +0200 +Subject: [PATCH] ipatests: update expected webui msg for admin deletion + +The deletion of the admin is now forbidden (even if it is +not the last member of the admins group) and the error +message has changed from "admin cannot be deleted or +disabled because it is the last member of group admins" +to " user admin cannot be deleted/modified: privileged user". + +Update the expected message in the webui test. + +Related: https://pagure.io/freeipa/issue/8878 + +Signed-off-by: Florence Blanc-Renaud +Reviewed-By: Alexander Bokovoy +--- + ipatests/test_webui/test_user.py | 3 +-- + 1 file changed, 1 insertion(+), 2 deletions(-) + +diff --git a/ipatests/test_webui/test_user.py b/ipatests/test_webui/test_user.py +index 8d44fbdb9380c94058307b02a96299d0e178cdc7..a8a92d00c7e1f40ef10eb9133cea8752daafe730 100644 +--- a/ipatests/test_webui/test_user.py ++++ b/ipatests/test_webui/test_user.py +@@ -50,8 +50,7 @@ INV_FIRSTNAME = ("invalid 'first': Leading and trailing spaces are " + FIELD_REQ = 'Required field' + ERR_INCLUDE = 'may only include letters, numbers, _, -, . and $' + ERR_MISMATCH = 'Passwords must match' +-ERR_ADMIN_DEL = ('admin cannot be deleted or disabled because it is the last ' +- 'member of group admins') ++ERR_ADMIN_DEL = ('user admin cannot be deleted/modified: privileged user') + USR_EXIST = 'user with name "{}" already exists' + ENTRY_EXIST = 'This entry already exists' + ACTIVE_ERR = 'active user with name "{}" already exists' +-- +2.41.0 + diff --git a/SOURCES/0021-ipatests-ensure-that-ipa-automember-rebuild-prints-a.patch b/SOURCES/0021-ipatests-ensure-that-ipa-automember-rebuild-prints-a.patch deleted file mode 100644 index f9281bf..0000000 --- a/SOURCES/0021-ipatests-ensure-that-ipa-automember-rebuild-prints-a.patch +++ /dev/null @@ -1,65 +0,0 @@ -From 88b9be29036a3580a8bccd31986fc30faa9852df Mon Sep 17 00:00:00 2001 -From: mbhalodi -Date: Tue, 14 Feb 2023 15:04:58 +0530 -Subject: [PATCH] ipatests: ensure that ipa automember-rebuild prints a warning - -ipa automember-rebuild now prints a warning about CPU usage. -Ensure that the warning is properly displayed. - -Related: https://pagure.io/freeipa/issue/9320 - -Signed-off-by: mbhalodi -Reviewed-By: Florence Blanc-Renaud ---- - ipatests/test_integration/test_automember.py | 13 ++++++++++--- - 1 file changed, 10 insertions(+), 3 deletions(-) - -diff --git a/ipatests/test_integration/test_automember.py b/ipatests/test_integration/test_automember.py -index f013964140714db046a1aa6a92409244b2137727..7acd0d7bf895fec970f2bda8b54f4496280525b6 100644 ---- a/ipatests/test_integration/test_automember.py -+++ b/ipatests/test_integration/test_automember.py -@@ -10,6 +10,9 @@ from ipapython.dn import DN - from ipatests.pytest_ipa.integration import tasks - from ipatests.test_integration.base import IntegrationTest - -+msg = ('IMPORTANT: In case of a high number of users, hosts or ' -+ 'groups, the operation may require high CPU usage.') -+ - - class TestAutounmembership(IntegrationTest): - """Tests for autounmembership feature. -@@ -206,11 +209,13 @@ class TestAutounmembership(IntegrationTest): - assert self.is_user_member_of_group(user2, group1) - - # Running automember-build so that user is part of correct group -- self.master.run_command(['ipa', 'automember-rebuild', -- '--users=%s' % user2]) -+ result = self.master.run_command(['ipa', 'automember-rebuild', -+ '--users=%s' % user2]) - assert self.is_user_member_of_group(user2, group2) - assert not self.is_user_member_of_group(user2, group1) - -+ assert msg in result.stdout_text -+ - finally: - # testcase cleanup - self.remove_user_automember(user2, raiseonerr=False) -@@ -240,12 +245,14 @@ class TestAutounmembership(IntegrationTest): - assert self.is_host_member_of_hostgroup(host2, hostgroup1) - - # Running the automember-build so host is part of correct hostgroup -- self.master.run_command( -+ result = self.master.run_command( - ['ipa', 'automember-rebuild', '--hosts=%s' % host2] - ) - assert self.is_host_member_of_hostgroup(host2, hostgroup2) - assert not self.is_host_member_of_hostgroup(host2, hostgroup1) - -+ assert msg in result.stdout_text -+ - finally: - # testcase cleanup - self.remove_host_automember(host2, raiseonerr=False) --- -2.39.1 - diff --git a/SOURCES/0021-ipatests-remove-fixture-call-and-wait-to-get-things-.patch b/SOURCES/0021-ipatests-remove-fixture-call-and-wait-to-get-things-.patch new file mode 100644 index 0000000..1a01ef2 --- /dev/null +++ b/SOURCES/0021-ipatests-remove-fixture-call-and-wait-to-get-things-.patch @@ -0,0 +1,70 @@ +From ff6cfcacd67a0461a0341e17854732cbe301f1d6 Mon Sep 17 00:00:00 2001 +From: Mohammad Rizwan +Date: Wed, 2 Aug 2023 12:48:40 +0530 +Subject: [PATCH] ipatests: remove fixture call and wait to get things settle + +system date moved in order to expire the certs. Sometime it +is observed that subsequent operation fails with 500 error for CA, +hence restart the services after moving date and wait for sometime +to get things settle. + +Also the tests was calling fixture which is not required for it, hence +removed it as well. + +Fixes: https://pagure.io/freeipa/issue/9348 + +Signed-off-by: Mohammad Rizwan +Reviewed-By: Florence Blanc-Renaud +--- + ipatests/test_integration/test_acme.py | 14 ++++++++++++-- + 1 file changed, 12 insertions(+), 2 deletions(-) + +diff --git a/ipatests/test_integration/test_acme.py b/ipatests/test_integration/test_acme.py +index cca20983e65f99d5ba0bb7bc6dc2b5684a6f37d9..c7389732cd067d49541cd04ea6687a6b95b4669f 100644 +--- a/ipatests/test_integration/test_acme.py ++++ b/ipatests/test_integration/test_acme.py +@@ -606,6 +606,11 @@ def issue_and_expire_acme_cert(): + tasks.kdestroy_all(host) + tasks.move_date(host, 'stop', '+90days+60minutes') + ++ # restart ipa services as date moved and wait to get things settle ++ time.sleep(10) ++ master.run_command(['ipactl', 'restart']) ++ time.sleep(10) ++ + tasks.get_kdcinfo(master) + # Note raiseonerr=False: + # the assert is located after kdcinfo retrieval. +@@ -627,6 +632,11 @@ def issue_and_expire_acme_cert(): + for host in hosts: + tasks.move_date(host, 'start', '-90days-60minutes') + ++ # restart ipa services as date moved and wait to get things settle ++ time.sleep(10) ++ hosts[0].run_command(['ipactl', 'restart']) ++ time.sleep(10) ++ + + class TestACMERenew(IntegrationTest): + +@@ -960,7 +970,7 @@ class TestACMEPrune(IntegrationTest): + ) + assert f'Number of entries returned {no_of_cert - search_size_limit}' + +- def test_prune_config_show(self, issue_and_expire_acme_cert): ++ def test_prune_config_show(self): + """Test to check config-show command shows set param""" + if (tasks.get_pki_version(self.master) + < tasks.parse_version('11.3.0')): +@@ -1001,7 +1011,7 @@ class TestACMEPrune(IntegrationTest): + assert 'Request Search Time Limit: 0' in result.stdout_text + assert 'cron Schedule: 0 0 1 * *' in result.stdout_text + +- def test_prune_disable(self, issue_and_expire_acme_cert): ++ def test_prune_disable(self): + """Test prune command throw error after disabling the pruning""" + if (tasks.get_pki_version(self.master) + < tasks.parse_version('11.3.0')): +-- +2.41.0 + diff --git a/SOURCES/0022-ipatests-fix-test_topology.patch b/SOURCES/0022-ipatests-fix-test_topology.patch new file mode 100644 index 0000000..92729ef --- /dev/null +++ b/SOURCES/0022-ipatests-fix-test_topology.patch @@ -0,0 +1,60 @@ +From 1278e614dd93bf0ac3d6e0c36cb9c277808afb2c Mon Sep 17 00:00:00 2001 +From: Florence Blanc-Renaud +Date: Fri, 11 Aug 2023 08:01:18 +0200 +Subject: [PATCH] ipatests: fix test_topology + +The test TestTopologyOptions::test_add_remove_segment is +randomly failing downstream. Test scenario: +- create a line topology master <-> repl1 <-> repl2 +- create user on master +- wait for repl success on master +- check that the user is seen on repl2 + +The test waits for replication to complete on the master but +it should also wait for the replication to complete on repl1 +before checking the user presence on repl2. + +Signed-off-by: Florence Blanc-Renaud +Reviewed-By: Anuja More +--- + ipatests/test_integration/test_topology.py | 7 +++++++ + 1 file changed, 7 insertions(+) + +diff --git a/ipatests/test_integration/test_topology.py b/ipatests/test_integration/test_topology.py +index 8a240fa3c081a05b8f4501fe48694e01086003a1..618c9d5dcce994cd0359a291b044eb2cf0bddc74 100644 +--- a/ipatests/test_integration/test_topology.py ++++ b/ipatests/test_integration/test_topology.py +@@ -124,6 +124,9 @@ class TestTopologyOptions(IntegrationTest): + self.replicas[0], + self.replicas[1]) + assert err == "", err ++ # At this point we have replicas[1] <-> master <-> replicas[0] ++ # ^--------------------------^ ++ + # Make sure the new segment is shown by `ipa topologysegment-find` + result1 = self.master.run_command(['ipa', 'topologysegment-find', + DOMAIN_SUFFIX_NAME]).stdout_text +@@ -137,9 +140,12 @@ class TestTopologyOptions(IntegrationTest): + deleteme = find_segment(self.master, self.replicas[1]) + returncode, error = tasks.destroy_segment(self.master, deleteme) + assert returncode == 0, error ++ # At this point we have master <-> replicas[0] <-> replicas[1] ++ + # Wait till replication ends and make sure replica1 does not have + # segment that was deleted on master + master_ldap = self.master.ldap_connect() ++ repl_ldap = self.replicas[0].ldap_connect() + tasks.wait_for_replication(master_ldap) + result3 = self.replicas[0].run_command(['ipa', 'topologysegment-find', + DOMAIN_SUFFIX_NAME]).stdout_text +@@ -150,6 +156,7 @@ class TestTopologyOptions(IntegrationTest): + '--first', 'test', + '--last', 'user']) + tasks.wait_for_replication(master_ldap) ++ tasks.wait_for_replication(repl_ldap) + result4 = self.replicas[1].run_command(['ipa', 'user-find']) + assert('someuser' in result4.stdout_text), 'User not found: someuser' + # We end up having a line topology: master <-> replica1 <-> replica2 +-- +2.41.0 + diff --git a/SOURCES/0022-ipatests-fix-tests-in-TestACMEPrune.patch b/SOURCES/0022-ipatests-fix-tests-in-TestACMEPrune.patch deleted file mode 100644 index c757a68..0000000 --- a/SOURCES/0022-ipatests-fix-tests-in-TestACMEPrune.patch +++ /dev/null @@ -1,74 +0,0 @@ -From e76b219c21d53b6bccce4ea3d18e2b61ac835e1f Mon Sep 17 00:00:00 2001 -From: Mohammad Rizwan -Date: Mon, 20 Feb 2023 15:33:09 +0530 -Subject: [PATCH] ipatests: fix tests in TestACMEPrune - -When cron_minute + 5 > 59, cron job throwing error for it. -i.e 58 + 5 = 63 which is not acceptable value for cron minute. - -Second fix is related to mismatch of confing setting and corresponding -assert. - -Third fix is related to extending time by 60 minutes to properly -expire the certs. - -related: https://pagure.io/freeipa/issue/9294 - -Signed-off-by: Mohammad Rizwan -Reviewed-By: Florence Blanc-Renaud ---- - ipatests/test_integration/test_acme.py | 15 ++++++++------- - 1 file changed, 8 insertions(+), 7 deletions(-) - -diff --git a/ipatests/test_integration/test_acme.py b/ipatests/test_integration/test_acme.py -index 1334be52f4530dd8b2a4207744146cd0eb5477a3..49b173060f88d4b8e876d8e3461a935938518b44 100644 ---- a/ipatests/test_integration/test_acme.py -+++ b/ipatests/test_integration/test_acme.py -@@ -600,7 +600,7 @@ def issue_and_expire_acme_cert(): - # move system date to expire acme cert - for host in hosts: - tasks.kdestroy_all(host) -- tasks.move_date(host, 'stop', '+90days') -+ tasks.move_date(host, 'stop', '+90days+60minutes') - - time.sleep(10) - tasks.get_kdcinfo(host) -@@ -622,7 +622,7 @@ def issue_and_expire_acme_cert(): - - # move back date - for host in hosts: -- tasks.move_date(host, 'start', '-90days') -+ tasks.move_date(host, 'start', '-90days-60minutes') - - - class TestACMERenew(IntegrationTest): -@@ -866,8 +866,9 @@ class TestACMEPrune(IntegrationTest): - "python3", - "-c", - ( -- "from datetime import datetime; " -- "print(int(datetime.now().strftime('%M')) + 5)" -+ "from datetime import datetime, timedelta; " -+ "print(int((datetime.now() + " -+ "timedelta(minutes=5)).strftime('%M')))" - ), - ] - ).stdout_text.strip() -@@ -990,11 +991,11 @@ class TestACMEPrune(IntegrationTest): - assert 'Certificate Retention Time: 30' in result.stdout_text - assert 'Certificate Retention Unit: day' in result.stdout_text - assert 'Certificate Search Size Limit: 1000' in result.stdout_text -- assert 'Certificate Search Time Limit: 100' in result.stdout_text -+ assert 'Certificate Search Time Limit: 0' in result.stdout_text - assert 'Request Retention Time: 30' in result.stdout_text - assert 'Request Retention Unit: day' in result.stdout_text -- assert 'Request Search Size Limit' in result.stdout_text -- assert 'Request Search Time Limit: 100' in result.stdout_text -+ assert 'Request Search Size Limit: 1000' in result.stdout_text -+ assert 'Request Search Time Limit: 0' in result.stdout_text - assert 'cron Schedule: 0 0 1 * *' in result.stdout_text - - def test_prune_disable(self, issue_and_expire_acme_cert): --- -2.39.1 - diff --git a/SOURCES/0023-Tolerate-absence-of-PAC-ticket-signature-depending-o.patch b/SOURCES/0023-Tolerate-absence-of-PAC-ticket-signature-depending-o.patch deleted file mode 100644 index a221759..0000000 --- a/SOURCES/0023-Tolerate-absence-of-PAC-ticket-signature-depending-o.patch +++ /dev/null @@ -1,574 +0,0 @@ -From 51c378f66fcf59322a0774a6d9b37e7e9ac55a17 Mon Sep 17 00:00:00 2001 -From: Julien Rische -Date: Fri, 7 Apr 2023 17:04:06 +0200 -Subject: [PATCH] Tolerate absence of PAC ticket signature depending of server - capabilities - -Since November 2020, Active Directory KDC generates a new type of -signature as part of the PAC. It is called "ticket signature", and is -generated based on the encrypted part of the ticket. The presence of -this signature is not mandatory in order for the PAC to be accepted for -S4U requests. - -However, the behavior is different for MIT krb5. Support was added as -part of the 1.20 release, and this signature is required in order to -process S4U requests. Contrary to the PAC extended KDC signature, the -code generating this signature cannot be isolated and backported to -older krb5 versions because this version of the KDB API does not allow -passing the content of the ticket's encrypted part to IPA. - -This is an issue in gradual upgrade scenarios where some IPA servers -rely on 1.19 and older versions of MIT krb5, while others use version -1.20 or newer. A service ticket that was provided by 1.19- IPA KDC will -be rejected when used by a service against a 1.20+ IPA KDC for S4U -requests. - -On Fedora, CentOS 9 Stream, and RHEL 9, when the krb5 version is 1.20 or -newer, it will include a downstream-only update adding the -"optional_pac_tkt_chksum" KDB string attribute allowing to tolerate the -absence of PAC ticket signatures, if necessary. - -This commit adds an extra step during the installation and update -processes where it adds a "pacTktSignSupported" ipaConfigString -attribute in "cn=KDC,cn=[server],cn=masters,cn=ipa,cn=etc,[basedn]" if -the MIT krb5 version IPA what built with was 1.20 or newer. - -This commit also set "optional_pac_tkt_chksum" as a virtual KDB entry -attribute. This means the value of the attribute is not actually stored -in the database (to avoid race conditions), but its value is determined -at the KDC starting time by search the "pacTktSignSupported" -ipaConfigString in the server list. If this value is missing for at -least of them is missing, enforcement of the PAC ticket signature is -disabled by setting "optional_pac_tkt_chksum" to true for the local -realm TGS KDB entry. - -For foreign realm TGS KDB entries, the "optional_pac_tkt_chksum" virtual -string attribute is set to true systematically, because, at least for -now, trusted AD domains can still have PAC ticket signature support -disabled. - -Given the fact the "pacTktSignSupported" ipaConfigString for a single -server is added when this server is updated, and that the value of -"optional_pac_tkt_chksum" is determined at KDC starting time based on -the ipaConfigString attributes of all the KDCs in the domain, this -requires to restart all the KDCs in the domain after all IPA servers -were updated in order for PAC ticket signature enforcement to actually -take effect. - -Fixes: https://pagure.io/freeipa/issue/9371 -Signed-off-by: Julien Rische -Reviewed-By: Rob Crittenden -Reviewed-By: Alexander Bokovoy -(cherry picked from commit bbe545ff9feb972e549c743025e4a26b14ef8f89) ---- - VERSION.m4 | 6 ++ - configure.ac | 1 + - daemons/ipa-kdb/ipa_kdb.c | 55 +++++++++++ - daemons/ipa-kdb/ipa_kdb.h | 1 + - daemons/ipa-kdb/ipa_kdb_principals.c | 139 +++++++++++++++++++++++---- - ipapython/Makefile.am | 15 +-- - ipapython/version.py.in | 4 + - ipaserver/install/krbinstance.py | 25 ++++- - ipaserver/install/server/upgrade.py | 5 + - ipaserver/masters.py | 2 + - 10 files changed, 225 insertions(+), 28 deletions(-) - -diff --git a/VERSION.m4 b/VERSION.m4 -index e5d60c4c3..9b727feca 100644 ---- a/VERSION.m4 -+++ b/VERSION.m4 -@@ -137,6 +137,11 @@ ifelse(IPA_VERSION_IS_GIT_SNAPSHOT, yes, - IPA_GIT_VERSION), - NEWLINE)) dnl IPA_VERSION end - -+######################################################## -+# Version of MIT krb5 used to build IPA -+######################################################## -+define(IPA_KRB5_BUILD_VERSION, translit(esyscmd(krb5-config --version | awk '{ print $NF }'), NEWLINE)) -+ - dnl DEBUG: uncomment following lines and run command m4 VERSION.m4 - dnl `IPA_VERSION: ''IPA_VERSION' - dnl `IPA_GIT_VERSION: ''IPA_GIT_VERSION' -@@ -144,3 +149,4 @@ dnl `IPA_GIT_BRANCH: ''IPA_GIT_BRANCH' - dnl `IPA_API_VERSION: ''IPA_API_VERSION' - dnl `IPA_DATA_VERSION: ''IPA_DATA_VERSION' - dnl `IPA_NUM_VERSION: ''IPA_NUM_VERSION' -+dnl `IPA_KRB5_BUILD_VERSION: ''IPA_KRB5_BUILD_VERSION' -diff --git a/configure.ac b/configure.ac -index 140045821..973cba33c 100644 ---- a/configure.ac -+++ b/configure.ac -@@ -460,6 +460,7 @@ AC_SUBST(VENDOR_SUFFIX) - AC_SUBST([VERSION], [IPA_VERSION]) - AC_SUBST([GIT_VERSION], [IPA_GIT_VERSION]) - AC_SUBST([GIT_BRANCH], [IPA_GIT_BRANCH]) -+AC_SUBST([KRB5_BUILD_VERSION], [IPA_KRB5_BUILD_VERSION]) - # used by Makefile.am for files depending on templates - AC_SUBST([CONFIG_STATUS]) - -diff --git a/daemons/ipa-kdb/ipa_kdb.c b/daemons/ipa-kdb/ipa_kdb.c -index 93563536c..9a56640ff 100644 ---- a/daemons/ipa-kdb/ipa_kdb.c -+++ b/daemons/ipa-kdb/ipa_kdb.c -@@ -524,6 +524,52 @@ static krb5_principal ipadb_create_local_tgs(krb5_context kcontext, - return tgtp; - } - -+static char *no_attrs[] = { -+ LDAP_NO_ATTRS, -+ -+ NULL -+}; -+ -+static krb5_error_code -+should_support_pac_tkt_sign(krb5_context kcontext, bool *result) -+{ -+ struct ipadb_context *ipactx; -+ krb5_error_code kerr; -+ LDAPMessage *res = NULL; -+ char *masters_dn = NULL; -+ int count; -+ -+ char *kdc_filter = "(&(cn=KDC)(objectClass=ipaConfigObject)" -+ "(!(ipaConfigString=pacTktSignSupported)))"; -+ -+ ipactx = ipadb_get_context(kcontext); -+ if (!ipactx) { -+ kerr = KRB5_KDB_DBNOTINITED; -+ goto done; -+ } -+ -+ count = asprintf(&masters_dn, "cn=masters,cn=ipa,cn=etc,%s", ipactx->base); -+ if (count < 0) { -+ kerr = ENOMEM; -+ goto done; -+ } -+ -+ kerr = ipadb_simple_search(ipactx, masters_dn, LDAP_SCOPE_SUBTREE, -+ kdc_filter, no_attrs, &res); -+ if (kerr) -+ goto done; -+ -+ count = ldap_count_entries(ipactx->lcontext, res); -+ -+ if (result) -+ *result = (count == 0); -+ -+done: -+ free(masters_dn); -+ ldap_msgfree(res); -+ return kerr; -+} -+ - /* INTERFACE */ - - static krb5_error_code ipadb_init_library(void) -@@ -544,6 +590,7 @@ static krb5_error_code ipadb_init_module(krb5_context kcontext, - krb5_error_code kerr; - int ret; - int i; -+ bool pac_tkt_sign_supported; - - /* make sure the context is freed to avoid leaking it */ - ipactx = ipadb_get_context(kcontext); -@@ -628,6 +675,14 @@ static krb5_error_code ipadb_init_module(krb5_context kcontext, - goto fail; - } - -+ /* Enforce PAC ticket signature verification if supported by all KDCs */ -+ kerr = should_support_pac_tkt_sign(kcontext, &pac_tkt_sign_supported); -+ if (kerr) { -+ ret = kerr; -+ goto fail; -+ } -+ ipactx->optional_pac_tkt_chksum = !pac_tkt_sign_supported; -+ - return 0; - - fail: -diff --git a/daemons/ipa-kdb/ipa_kdb.h b/daemons/ipa-kdb/ipa_kdb.h -index 7aa5be494..0f4d3e431 100644 ---- a/daemons/ipa-kdb/ipa_kdb.h -+++ b/daemons/ipa-kdb/ipa_kdb.h -@@ -143,6 +143,7 @@ struct ipadb_context { - krb5_key_salt_tuple *def_encs; - int n_def_encs; - struct ipadb_mspac *mspac; -+ bool optional_pac_tkt_chksum; - #ifdef HAVE_KRB5_CERTAUTH_PLUGIN - krb5_certauth_moddata certauth_moddata; - #endif -diff --git a/daemons/ipa-kdb/ipa_kdb_principals.c b/daemons/ipa-kdb/ipa_kdb_principals.c -index e95cb453c..e6c3fba21 100644 ---- a/daemons/ipa-kdb/ipa_kdb_principals.c -+++ b/daemons/ipa-kdb/ipa_kdb_principals.c -@@ -113,6 +113,8 @@ static char *std_principal_obj_classes[] = { - - #define DEFAULT_TL_DATA_CONTENT "\x00\x00\x00\x00principal@UNINITIALIZED" - -+#define OPT_PAC_TKT_CHKSUM_STR_ATTR_NAME "optional_pac_tkt_chksum" -+ - static int ipadb_ldap_attr_to_tl_data(LDAP *lcontext, LDAPMessage *le, - char *attrname, - krb5_tl_data **result, int *num) -@@ -178,10 +180,56 @@ done: - return ret; - } - --static krb5_error_code ipadb_set_tl_data(krb5_db_entry *entry, -- krb5_int16 type, -- krb5_ui_2 length, -- const krb5_octet *data) -+static bool -+is_tgs_princ(krb5_context kcontext, krb5_const_principal princ) -+{ -+ krb5_data *primary; -+ size_t l_tgs_name; -+ -+ if (2 != krb5_princ_size(kcontext, princ)) -+ return false; -+ -+ primary = krb5_princ_component(kcontext, princ, 0); -+ -+ l_tgs_name = strlen(KRB5_TGS_NAME); -+ -+ if (l_tgs_name != primary->length) -+ return false; -+ -+ return 0 == memcmp(primary->data, KRB5_TGS_NAME, l_tgs_name); -+} -+ -+static krb5_error_code -+cmp_local_tgs_princ(krb5_context kcontext, const char *local_realm, -+ krb5_const_principal princ, bool *result) -+{ -+ krb5_principal local_tgs_princ; -+ size_t l_local_realm; -+ krb5_error_code kerr; -+ bool res; -+ -+ l_local_realm = strlen(local_realm); -+ -+ kerr = krb5_build_principal(kcontext, &local_tgs_princ, -+ l_local_realm, local_realm, -+ KRB5_TGS_NAME, local_realm, NULL); -+ if (kerr) -+ goto end; -+ -+ res = (bool) krb5_principal_compare(kcontext, local_tgs_princ, princ); -+ -+ if (result) -+ *result = res; -+ -+end: -+ krb5_free_principal(kcontext, local_tgs_princ); -+ return kerr; -+} -+ -+krb5_error_code ipadb_set_tl_data(krb5_db_entry *entry, -+ krb5_int16 type, -+ krb5_ui_2 length, -+ const krb5_octet *data) - { - krb5_error_code kerr; - krb5_tl_data *new_td = NULL; -@@ -1632,6 +1680,8 @@ krb5_error_code ipadb_get_principal(krb5_context kcontext, - krb5_db_entry **entry) - { - struct ipadb_context *ipactx; -+ bool is_local_tgs_princ; -+ const char *opt_pac_tkt_chksum_val; - krb5_error_code kerr; - - *entry = NULL; -@@ -1647,11 +1697,33 @@ krb5_error_code ipadb_get_principal(krb5_context kcontext, - - /* Lookup local names and aliases first. */ - kerr = dbget_princ(kcontext, ipactx, search_for, flags, entry); -- if (kerr != KRB5_KDB_NOENTRY) { -+ if (kerr == KRB5_KDB_NOENTRY) { -+ kerr = dbget_alias(kcontext, ipactx, search_for, flags, entry); -+ } -+ if (kerr) - return kerr; -+ -+ /* If TGS principal, some virtual attributes may be added */ -+ if (is_tgs_princ(kcontext, (*entry)->princ)) { -+ kerr = cmp_local_tgs_princ(kcontext, ipactx->realm, (*entry)->princ, -+ &is_local_tgs_princ); -+ if (kerr) -+ return kerr; -+ -+ /* PAC ticket signature should be optional for foreign realms, and local -+ * realm if not supported by all servers -+ */ -+ if (!is_local_tgs_princ || ipactx->optional_pac_tkt_chksum) -+ opt_pac_tkt_chksum_val = "true"; -+ else -+ opt_pac_tkt_chksum_val = "false"; -+ -+ kerr = krb5_dbe_set_string(kcontext, *entry, -+ OPT_PAC_TKT_CHKSUM_STR_ATTR_NAME, -+ opt_pac_tkt_chksum_val); - } - -- return dbget_alias(kcontext, ipactx, search_for, flags, entry); -+ return kerr; - } - - void ipadb_free_principal_e_data(krb5_context kcontext, krb5_octet *e_data) -@@ -1954,6 +2026,20 @@ done: - return kerr; - } - -+static bool should_filter_out_attr(krb5_tl_data *data) -+{ -+ switch (data->tl_data_type) { -+ case KRB5_TL_DB_ARGS: -+ case KRB5_TL_KADM_DATA: -+ case KRB5_TL_LAST_ADMIN_UNLOCK: -+ case KRB5_TL_LAST_PWD_CHANGE: -+ case KRB5_TL_MKVNO: -+ return true; -+ default: -+ return false; -+ } -+} -+ - static krb5_error_code ipadb_get_ldap_mod_extra_data(struct ipadb_mods *imods, - krb5_tl_data *tl_data, - int mod_op) -@@ -1965,13 +2051,8 @@ static krb5_error_code ipadb_get_ldap_mod_extra_data(struct ipadb_mods *imods, - int n, i; - - for (n = 0, data = tl_data; data; data = data->tl_data_next) { -- if (data->tl_data_type == KRB5_TL_LAST_PWD_CHANGE || -- data->tl_data_type == KRB5_TL_KADM_DATA || -- data->tl_data_type == KRB5_TL_DB_ARGS || -- data->tl_data_type == KRB5_TL_MKVNO || -- data->tl_data_type == KRB5_TL_LAST_ADMIN_UNLOCK) { -+ if (should_filter_out_attr(data)) - continue; -- } - n++; - } - -@@ -1987,13 +2068,8 @@ static krb5_error_code ipadb_get_ldap_mod_extra_data(struct ipadb_mods *imods, - - for (i = 0, data = tl_data; data; data = data->tl_data_next) { - -- if (data->tl_data_type == KRB5_TL_LAST_PWD_CHANGE || -- data->tl_data_type == KRB5_TL_KADM_DATA || -- data->tl_data_type == KRB5_TL_DB_ARGS || -- data->tl_data_type == KRB5_TL_MKVNO || -- data->tl_data_type == KRB5_TL_LAST_ADMIN_UNLOCK) { -+ if (should_filter_out_attr(data)) - continue; -- } - - be_type = htons(data->tl_data_type); - -@@ -2745,10 +2821,37 @@ done: - return kerr; - } - -+static krb5_error_code -+remove_virtual_str_attrs(krb5_context kcontext, krb5_db_entry *entry) -+{ -+ char *str_attr_val; -+ krb5_error_code kerr; -+ -+ kerr = krb5_dbe_get_string(kcontext, entry, -+ OPT_PAC_TKT_CHKSUM_STR_ATTR_NAME, -+ &str_attr_val); -+ if (kerr) -+ return kerr; -+ -+ if (str_attr_val) -+ kerr = krb5_dbe_set_string(kcontext, entry, -+ OPT_PAC_TKT_CHKSUM_STR_ATTR_NAME, -+ NULL); -+ -+ krb5_dbe_free_string(kcontext, str_attr_val); -+ return kerr; -+} -+ - krb5_error_code ipadb_put_principal(krb5_context kcontext, - krb5_db_entry *entry, - char **db_args) - { -+ krb5_error_code kerr; -+ -+ kerr = remove_virtual_str_attrs(kcontext, entry); -+ if (kerr) -+ return kerr; -+ - if (entry->mask & KMASK_PRINCIPAL) { - return ipadb_add_principal(kcontext, entry); - } else { -diff --git a/ipapython/Makefile.am b/ipapython/Makefile.am -index 7038e8b57..6b336d8fe 100644 ---- a/ipapython/Makefile.am -+++ b/ipapython/Makefile.am -@@ -13,11 +13,12 @@ bdist_wheel: version.py - $(AM_V_GEN)awk '$$1 == "default:" { print $$2 }' $< >$@ - - version.py: version.py.in .DEFAULT_PLUGINS $(top_builddir)/$(CONFIG_STATUS) -- $(AM_V_GEN)sed \ -- -e 's|@API_VERSION[@]|$(API_VERSION)|g' \ -- -e 's|@NUM_VERSION[@]|$(NUM_VERSION)|g' \ -- -e 's|@VERSION[@]|$(VERSION)|g' \ -- -e 's|@VENDOR_SUFFIX[@]|$(VENDOR_SUFFIX)|g' \ -- -e '/@DEFAULT_PLUGINS[@]/r .DEFAULT_PLUGINS' \ -- -e '/@DEFAULT_PLUGINS[@]/d' \ -+ $(AM_V_GEN)sed \ -+ -e 's|@API_VERSION[@]|$(API_VERSION)|g' \ -+ -e 's|@NUM_VERSION[@]|$(NUM_VERSION)|g' \ -+ -e 's|@VERSION[@]|$(VERSION)|g' \ -+ -e 's|@VENDOR_SUFFIX[@]|$(VENDOR_SUFFIX)|g' \ -+ -e 's|@KRB5_BUILD_VERSION[@]|$(KRB5_BUILD_VERSION)|g' \ -+ -e '/@DEFAULT_PLUGINS[@]/r .DEFAULT_PLUGINS' \ -+ -e '/@DEFAULT_PLUGINS[@]/d' \ - $< > $@ -diff --git a/ipapython/version.py.in b/ipapython/version.py.in -index 5a71fb8cf..a8f4218a7 100644 ---- a/ipapython/version.py.in -+++ b/ipapython/version.py.in -@@ -17,6 +17,8 @@ - # along with this program. If not, see . - # - -+from pkg_resources import parse_version -+ - # The full version including strings - VERSION = "@VERSION@" - -@@ -51,3 +53,5 @@ API_VERSION = "@API_VERSION@" - DEFAULT_PLUGINS = frozenset(l.strip() for l in """ - @DEFAULT_PLUGINS@ - """.strip().splitlines()) -+ -+KRB5_BUILD_VERSION = parse_version("@KRB5_BUILD_VERSION@") -diff --git a/ipaserver/install/krbinstance.py b/ipaserver/install/krbinstance.py -index a5eaa7b17..acb7419d6 100644 ---- a/ipaserver/install/krbinstance.py -+++ b/ipaserver/install/krbinstance.py -@@ -26,6 +26,7 @@ import socket - import dbus - - import dns.name -+from pkg_resources import parse_version - - from ipalib import x509 - from ipalib.install import certstore -@@ -34,6 +35,7 @@ from ipaserver.install import installutils - from ipapython import ipaldap - from ipapython import ipautil - from ipapython import kernel_keyring -+from ipapython.version import KRB5_BUILD_VERSION - from ipalib import api, errors - from ipalib.constants import ANON_USER - from ipalib.install import certmonger -@@ -42,15 +44,17 @@ from ipapython.dogtag import KDC_PROFILE - - from ipaserver.install import replication - from ipaserver.install import certs --from ipaserver.masters import find_providing_servers -+from ipaserver.masters import ( -+ find_providing_servers, -+ PAC_TKT_SIGN_SUPPORTED, -+ PKINIT_ENABLED, -+) - from ipaplatform.constants import constants - from ipaplatform.tasks import tasks - from ipaplatform.paths import paths - - logger = logging.getLogger(__name__) - --PKINIT_ENABLED = 'pkinitEnabled' -- - MASTER_KEY_TYPE = 'aes256-sha2' - SUPPORTED_ENCTYPES = ('aes256-sha2:special', 'aes128-sha2:special', - 'aes256-sha2:normal', 'aes128-sha2:normal', -@@ -169,6 +173,13 @@ class KrbInstance(service.Service): - # Add the host to the ipaserver host group - self._ldap_update(['20-ipaservers_hostgroup.update']) - -+ def pac_tkt_sign_support_enable(self): -+ """ -+ Advertise PAC ticket signature support in master's KDC entry in LDAP -+ """ -+ service.set_service_entry_config( -+ 'KDC', self.fqdn, [PAC_TKT_SIGN_SUPPORTED], self.suffix) -+ - def __common_setup(self, realm_name, host_name, domain_name, admin_password): - self.fqdn = host_name - self.realm = realm_name.upper() -@@ -212,6 +223,10 @@ class KrbInstance(service.Service): - - self.__common_post_setup() - -+ if KRB5_BUILD_VERSION >= parse_version('1.20'): -+ self.step("enable PAC ticket signature support", -+ self.pac_tkt_sign_support_enable) -+ - self.start_creation() - - self.kpasswd = KpasswdInstance() -@@ -235,6 +250,10 @@ class KrbInstance(service.Service): - - self.__common_post_setup() - -+ if KRB5_BUILD_VERSION >= parse_version('1.20'): -+ self.step("enable PAC ticket signature support", -+ self.pac_tkt_sign_support_enable) -+ - self.start_creation() - - self.kpasswd = KpasswdInstance() -diff --git a/ipaserver/install/server/upgrade.py b/ipaserver/install/server/upgrade.py -index 5f5a60d10..f8701c8a0 100644 ---- a/ipaserver/install/server/upgrade.py -+++ b/ipaserver/install/server/upgrade.py -@@ -18,6 +18,7 @@ import sys - import tempfile - from contextlib import contextmanager - from augeas import Augeas -+from pkg_resources import parse_version - - from ipalib import api, x509 - from ipalib.constants import RENEWAL_CA_NAME, RA_AGENT_PROFILE, IPA_CA_RECORD -@@ -36,6 +37,7 @@ from ipapython import ipautil, version - from ipapython import ipaldap - from ipapython import directivesetter - from ipapython.dn import DN -+from ipapython.version import KRB5_BUILD_VERSION - from ipaplatform.constants import constants - from ipaplatform.paths import paths - from ipaserver import servroles -@@ -1961,6 +1963,9 @@ def upgrade_configuration(): - enable_server_snippet() - setup_kpasswd_server(krb) - -+ if KRB5_BUILD_VERSION >= parse_version('1.20'): -+ krb.pac_tkt_sign_support_enable() -+ - # Must be executed after certificate_renewal_update - # (see function docstring for details) - http_certificate_ensure_ipa_ca_dnsname(http) -diff --git a/ipaserver/masters.py b/ipaserver/masters.py -index b532f2b72..c9b57b2a5 100644 ---- a/ipaserver/masters.py -+++ b/ipaserver/masters.py -@@ -20,6 +20,8 @@ logger = logging.getLogger(__name__) - CONFIGURED_SERVICE = u'configuredService' - ENABLED_SERVICE = u'enabledService' - HIDDEN_SERVICE = u'hiddenService' -+PAC_TKT_SIGN_SUPPORTED = u'pacTktSignSupported' -+PKINIT_ENABLED = u'pkinitEnabled' - - # The service name as stored in cn=masters,cn=ipa,cn=etc. The values are: - # 0: systemd service name --- -2.39.2 - diff --git a/SOURCES/0023-ipatests-idm-api-related-tests.patch b/SOURCES/0023-ipatests-idm-api-related-tests.patch new file mode 100644 index 0000000..34c072a --- /dev/null +++ b/SOURCES/0023-ipatests-idm-api-related-tests.patch @@ -0,0 +1,560 @@ +From ac6a2172f5dcb46701148c7b096ffa1b44076816 Mon Sep 17 00:00:00 2001 +From: Sudhir Menon +Date: Thu, 27 Jul 2023 14:33:08 +0530 +Subject: [PATCH] ipatests: idm api related tests. + +IDM API related tests are automated in the +above PR +Ref: https://freeipa.readthedocs.io/en/latest/api/basic_usage.html + +Signed-off-by: Sudhir Menon +Reviewed-By: Alexander Bokovoy +Reviewed-By: Florence Blanc-Renaud +--- + ipatests/test_integration/test_idm_api.py | 534 ++++++++++++++++++++++ + 1 file changed, 534 insertions(+) + create mode 100644 ipatests/test_integration/test_idm_api.py + +diff --git a/ipatests/test_integration/test_idm_api.py b/ipatests/test_integration/test_idm_api.py +new file mode 100644 +index 0000000000000000000000000000000000000000..eafef5dd8526bc14725d6bc32819cb5c7387f868 +--- /dev/null ++++ b/ipatests/test_integration/test_idm_api.py +@@ -0,0 +1,534 @@ ++# ++# Copyright (C) 2018 FreeIPA Contributors see COPYING for license ++# ++from __future__ import absolute_import ++ ++from ipatests.test_integration.base import IntegrationTest ++import textwrap ++ ++API_INIT = """ ++ from ipalib import api, errors ++ api.bootstrap_with_global_options(context="server") ++ api.finalize() ++ api.Backend.ldap2.connect() ++ """ ++ ++CERT = ( ++ b"MIIEkDCCAvigAwIBAgIBCzANBgkqhkiG9w0BAQsFADA5MRcwFQYDVQQKD\n" ++ b"A5URVNUUkVBTE0uVEVTVDEeMBwGA1UEAwwVQ2VydGlmaWNhdGUgQXV0aG\n" ++ b"9yaXR5MB4XDTIzMDcyODE3MTIxOVoXDTI1MDcyODE3MTIxOVowKjEXMBU\n" ++ b"GA1UECgwOVEVTVFJFQUxNLlRFU1QxDzANBgNVBAMMBmpzbWl0aDCCASIw\n" ++ b"DQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAOF0XFrdVXmKp95AVZW5o\n" ++ b"BWcij6vJPqeU3UpzTLbM+fROhNaKMX9S+yXrJHifOmhCOuNA8TtptKVJx\n" ++ b"CIDZ1/5KwPBk4vrnwOBtVMCftHj87MabBqV/nmQQrCiKTcJu4aQEDI9Qh\n" ++ b"yza09EJKvG8KkpnyuShtkP2LgkUxIqkjBg4DLV7grO+I+aG17QTuQxUTy\n" ++ b"icfYDBnzD4hTKPLf7d9KNyG+sEeyN0gceLFMUYaQ4lyapcSzYJwOSAc2B\n" ++ b"EU73tLaJlQORHL7HmhxrjD1IgZyxFjp/ofLVZFFoJAqjz2FWzOxmQw+bc\n" ++ b"0WTzQjeSTGx+l3htj7MmhIRBMqr3Um6zXkLKMCAwEAAaOCATAwggEsMB8\n" ++ b"GA1UdIwQYMBaAFCIXu6QtsiBVo1yZQZ7MMHTl5Wj6MEAGCCsGAQUFBwEB\n" ++ b"BDQwMjAwBggrBgEFBQcwAYYkaHR0cDovL2lwYS1jYS50ZXN0cmVhbG0ud\n" ++ b"GVzdC9jYS9vY3NwMA4GA1UdDwEB/wQEAwIE8DAdBgNVHSUEFjAUBggrBg\n" ++ b"EFBQcDAQYIKwYBBQUHAwIweQYDVR0fBHIwcDBuoDagNIYyaHR0cDovL2l\n" ++ b"wYS1jYS50ZXN0cmVhbG0udGVzdC9pcGEvY3JsL01hc3RlckNSTC5iaW6i\n" ++ b"NKQyMDAxDjAMBgNVBAoMBWlwYWNhMR4wHAYDVQQDDBVDZXJ0aWZpY2F0Z\n" ++ b"SBBdXRob3JpdHkwHQYDVR0OBBYEFNwQNQAG8MsKQPwMFyGzRiMzRAa5MA\n" ++ b"0GCSqGSIb3DQEBCwUAA4IBgQB2g0mS8XAPI+aRBa5q7Vbp1245CvMP0Eq\n" ++ b"Cz6gvCNwtxW0UDKnB++d/YQ13ft+x9Xj3rB/M2YXxdxTpQnQQv34CUcyh\n" ++ b"PQKJthAsbKBpdusCGrbS54zKFR0MjxwOwIIDHuI6eu2AoSpsmYs5UGzQm\n" ++ b"oCfQhbImK7iGLy0rOHaON1cWAFmC6lzJ2TFELc4N3eLYGVZy2ZtyZTgA3\n" ++ b"l97rBCwbDDFF1JWoOByIq8Ij99ksyMXws++sNUpo/1l8Jt0Gn6RBiidZB\n" ++ b"ef4+kJN+t6RAAwRQ / 3cmEggXcFoV13KZ70PeMXeX6CKMwXIwt3q7A78\n" ++ b"Wc/0OIBREZLhXpkmogCzWCuatdzeBIhMhx0vDEzaxlhf32ZWfN5pFMpgq\n" ++ b"wLZsdwMf6J65kGbE5Pg3Yxk7OiByxZJnR8UlvbU3r6RhMWutD6C0aqqNt\n" ++ b"o3us5gTmfRc8Mf1l/BUgDqkBKOTU8FHREGemG1HoklBym/Pbua0VMUA+s\n" ++ b"0nECR4LLM/o9PCJ2Y3QPBZy8Hg=\n" ++) ++ ++ ++class TestAPIScenario(IntegrationTest): ++ """ ++ Tests for IDM API scenarios ++ """ ++ ++ topology = "line" ++ ++ def create_and_run_script(self, filename, user_code_script): ++ self.master.put_file_contents(filename, user_code_script) ++ self.master.run_command(["python3", filename]) ++ self.master.run_command(["rm", filename]) ++ ++ def test_idm_user_add(self): ++ """ ++ This test checks that ipa user using api.Command["user_add"] ++ and then checks that user is displayed using ++ api.Command["user_show"] ++ """ ++ user_code_script = textwrap.dedent( ++ f""" ++ {API_INIT} ++ api.Command["user_add"]("jsmith", givenname="John", sn="Smith", ++ ipauserauthtype="otp") ++ cmd = api.Command["user_show"]("jsmith", all=True)["result"] ++ assert 'otp' in cmd['ipauserauthtype'] ++ assert 'John Smith' in cmd['cn'] ++ """ ++ ) ++ self.create_and_run_script( ++ "/tmp/user_add.py", user_code_script ++ ) ++ ++ def test_idm_user_find(self): ++ """ ++ This test checks that user is displayed ++ using api.Command["user_find"] ++ """ ++ user_code_script = textwrap.dedent( ++ f""" ++ {API_INIT} ++ cmd = api.Command["user_find"]("jsmith") ++ assert '1 user matched' in cmd['summary'] ++ """ ++ ) ++ self.create_and_run_script( ++ "/tmp/user_find.py", user_code_script ++ ) ++ ++ def test_idm_user_mod(self): ++ """ ++ This test checks that user attribute is modified ++ using api.Command["user_mod"] ++ """ ++ user_code_script = textwrap.dedent( ++ f""" ++ {API_INIT} ++ cmd = api.Command["user_mod"]("jsmith", ++ mail="jsmith@example.org")["result"] ++ assert 'jsmith@example.org' in cmd['mail'] ++ """ ++ ) ++ self.create_and_run_script( ++ "/tmp/user_mod.py", user_code_script ++ ) ++ ++ def test_disable_user(self): ++ """ ++ This test checks that user is disabled ++ using api.Command["user_disable"] ++ """ ++ user_code_script = textwrap.dedent( ++ f""" ++ {API_INIT} ++ cmd = api.Command["user_disable"]("jsmith") ++ assert 'Disabled user account "jsmith"' in cmd['summary'] ++ """ ++ ) ++ self.create_and_run_script( ++ "/tmp/disable_user.py", user_code_script ++ ) ++ ++ def test_enable_user(self): ++ """ ++ This test checks that user is enabled ++ using api.Command["user_enable"] ++ """ ++ user_code_script = textwrap.dedent( ++ f""" ++ {API_INIT} ++ cmd = api.Command["user_enable"]("jsmith") ++ assert 'Enabled user account "jsmith"' in cmd['summary'] ++ """ ++ ) ++ self.create_and_run_script( ++ "/tmp/enable_user.py", user_code_script ++ ) ++ ++ def test_create_ipa_group(self): ++ """ ++ This test checks that group is created ++ using api.Command["group_add"] ++ """ ++ user_code_script = textwrap.dedent( ++ f""" ++ {API_INIT} ++ cmd = api.Command["group_add"]("developers", gidnumber=500, ++ description="Developers") ++ assert 'Added group "developers"' in cmd['summary'] ++ """ ++ ) ++ self.create_and_run_script( ++ "/tmp/create_group.py", user_code_script ++ ) ++ ++ def test_show_ipa_group(self): ++ """ ++ This test checks that group is displayed ++ using api.Command["group_show"] ++ """ ++ user_code_script = textwrap.dedent( ++ f""" ++ {API_INIT} ++ cmd = api.Command["group_show"]("developers") ++ assert 'developers' in cmd['result']['cn'] ++ """ ++ ) ++ self.create_and_run_script( ++ "/tmp/group_show.py", user_code_script ++ ) ++ ++ def test_ipa_group_mod(self): ++ """ ++ This test checks that group description is modified ++ using api.Command["group_mod"] ++ """ ++ user_code_script = textwrap.dedent( ++ f""" ++ {API_INIT} ++ cmd = api.Command["group_mod"]("developers", description='developer') ++ ["result"] ++ assert 'Modified group "developers"' in cmd['summary'] ++ """ ++ ) ++ self.create_and_run_script( ++ "/tmp/group_mod.py", user_code_script ++ ) ++ ++ def test_add_members_to_ipa_group(self): ++ """ ++ This test checks that member is added to group ++ using api.Command["group_add_member"] ++ """ ++ user_code_script = textwrap.dedent( ++ f""" ++ {API_INIT} ++ cmd = api.Command["group_add_member"]("developers", ++ user='jsmith')["result"] ++ assert 'jsmith' in cmd['member_user'] ++ """ ++ ) ++ self.create_and_run_script( ++ "/tmp/create_group_members.py", user_code_script ++ ) ++ ++ def test_ipa_group_find(self): ++ """ ++ This test checks that group is displayed ++ using api.Command["group_find"] ++ """ ++ user_code_script = textwrap.dedent( ++ f""" ++ {API_INIT} ++ cmd = api.Command["group_find"]("developers") ++ assert '1 group matched' in cmd['summary'] ++ """ ++ ) ++ self.create_and_run_script( ++ "/tmp/group_find.py", user_code_script ++ ) ++ ++ def test_remove_member_group(self): ++ """ ++ This test checks that group member is removed ++ using api.Command["group_remove_member"] ++ """ ++ user_code_script = textwrap.dedent( ++ f""" ++ {API_INIT} ++ cmd = api.Command["group_remove_member"]("developers", ++ user="jsmith") ++ assert 'member_user' not in cmd ++ """ ++ ) ++ self.create_and_run_script( ++ "/tmp/remove_member_group.py", user_code_script ++ ) ++ ++ def test_add_permission(self): ++ """ ++ This test checks that permission is added ++ using api.Command["permission_add"] ++ """ ++ user_code_script = textwrap.dedent( ++ f""" ++ {API_INIT} ++ cmd = api.Command["permission_add"]("Create users", ++ ipapermright='add', type='user') ++ assert 'Added permission "Create users"' in cmd['summary'] ++ """ ++ ) ++ self.create_and_run_script( ++ "/tmp/add_perm.py", user_code_script ++ ) ++ ++ def test_create_hbac_rule(self): ++ """ ++ This test checks that hbac rule is added ++ using api.Command["hbacrule_add"] ++ """ ++ user_code_script = textwrap.dedent( ++ f""" ++ {API_INIT} ++ cmd = api.Command["hbacrule_add"]("sshd_rule") ++ assert 'Added HBAC rule "sshd_rule"' in cmd['summary'] ++ """ ++ ) ++ self.create_and_run_script( ++ "/tmp/add_hbac_rule.py", user_code_script ++ ) ++ ++ def test_add_hbac_service(self): ++ """ ++ This test checks that hbac service is added using ++ api.Command["hbacsvc_add"] ++ """ ++ user_code_script = textwrap.dedent( ++ f""" ++ {API_INIT} ++ cmd = api.Command["hbacsvc_add"]("chronyd") ++ assert 'Added HBAC service "chronyd"' in cmd['summary'] ++ """ ++ ) ++ self.create_and_run_script( ++ "/tmp/add_hbac_svc.py", user_code_script ++ ) ++ ++ def test_enable_hbac_rule(self): ++ """ ++ This test checks that hbac rule is enabled using ++ api.Command["hbacrule_enable"] ++ """ ++ user_code_script = textwrap.dedent( ++ f""" ++ {API_INIT} ++ cmd = api.Command["hbacrule_enable"]("sshd_rule") ++ assert 'Enabled HBAC rule "sshd_rule"' in cmd['summary'] ++ """ ++ ) ++ self.create_and_run_script( ++ "/tmp/enable_hbacrule.py", user_code_script ++ ) ++ ++ def test_create_sudo_rule(self): ++ """ ++ This test checks that sudo rule is created using ++ api.Command["sudorule_add"] ++ """ ++ user_code_script = textwrap.dedent( ++ f""" ++ {API_INIT} ++ cmd = api.Command["sudorule_add"]("timechange") ++ assert 'Added Sudo Rule "timechange"' in cmd['summary'] ++ """ ++ ) ++ self.create_and_run_script( ++ "/tmp/create_sudos.py", user_code_script ++ ) ++ ++ def test_add_user_certificate(self): ++ """ ++ This test checks user certificate is added using ++ api.Command["user_add_cert"] ++ """ ++ user_code_script = textwrap.dedent( ++ f""" ++ {API_INIT} ++ msg = 'Added certificates to user "jsmith"' ++ cmd = api.Command["user_add_cert"]("jsmith", usercertificate={CERT}) ++ assert msg in cmd["summary"] ++ """ ++ ) ++ self.create_and_run_script( ++ "/tmp/add_cert.py", user_code_script ++ ) ++ ++ def test_remove_user_certificate(self): ++ """ ++ This test checks that user certificate is removed ++ using api.Command["user_remove_cert"] ++ """ ++ user_code_script = textwrap.dedent( ++ f""" ++ {API_INIT} ++ msg = 'Removed certificates from user "jsmith"' ++ cmd = api.Command["user_remove_cert"]("jsmith", usercertificate={CERT}) ++ assert msg in cmd["summary"] ++ """ ++ ) ++ self.create_and_run_script( ++ "/tmp/remove_cert.py", user_code_script ++ ) ++ ++ def test_certmaprule_add(self): ++ """ ++ This test checks that certmap rule is added using ++ api.Command["certmaprule_add"] ++ """ ++ user_code_script = textwrap.dedent( ++ f""" ++ {API_INIT} ++ msg = ('Added Certificate Identity Mapping Rule "testrule"') ++ cmd = api.Command["certmaprule_add"]("testrule") ++ assert msg in cmd['summary'] ++ """ ++ ) ++ self.create_and_run_script( ++ "/tmp/certmap_rule_add.py", user_code_script ++ ) ++ ++ def test_certmaprule_enable(self): ++ """ ++ This test checks that certmap rule is enabled ++ using api.Command["certmaprule_enable"] ++ """ ++ user_code_script = textwrap.dedent( ++ f""" ++ {API_INIT} ++ msg = ('Enabled Certificate Identity Mapping Rule "testrule"') ++ cmd = api.Command["certmaprule_enable"]("testrule") ++ assert msg in cmd["summary"] ++ """ ++ ) ++ self.create_and_run_script( ++ "/tmp/certmap_rule_enable.py", user_code_script ++ ) ++ ++ def test_certmaprule_disable(self): ++ """ ++ This test checks that certmap rule is disabled using ++ api.Command["certmaprule_disable"] ++ """ ++ user_code_script = textwrap.dedent( ++ f""" ++ {API_INIT} ++ msg = ('Disabled Certificate Identity Mapping Rule "testrule"') ++ cmd = api.Command["certmaprule_disable"]("testrule") ++ assert msg in cmd["summary"] ++ """ ++ ) ++ self.create_and_run_script( ++ "/tmp/certmap_rule_disable.py", user_code_script ++ ) ++ ++ def test_certmaprule_del(self): ++ """ ++ This test checks that certmap rule is deleted using ++ api.Command["certmaprule_del"] ++ """ ++ user_code_script = textwrap.dedent( ++ f""" ++ {API_INIT} ++ msg = ('Deleted Certificate Identity Mapping Rule "testrule"') ++ cmd = api.Command["certmaprule_del"]("testrule") ++ assert msg in cmd['summary'] ++ """ ++ ) ++ self.create_and_run_script( ++ "/tmp/certmap_rule_del.py", user_code_script ++ ) ++ ++ def test_add_role(self): ++ """ ++ This test checks that role and privilege is added using ++ api.Command["role_add"] ++ """ ++ user_code_script = textwrap.dedent( ++ f""" ++ {API_INIT} ++ cmd1 = api.Command["role_add"]("junioradmin", ++ description="Junior admin") ++ assert 'Added role "junioradmin"' in cmd1["summary"] ++ cmd2 = api.Command.role_add_privilege("junioradmin", ++ privilege="Vault Administrators")["result"] ++ assert 'Vault Administrators' in cmd2["memberof_privilege"] ++ """ ++ ) ++ self.create_and_run_script( ++ "/tmp/add_role.py", user_code_script ++ ) ++ ++ def test_add_subid(self): ++ """ ++ This test checks that subid is added for IPA user ++ using api.Command["subid_add"] ++ """ ++ user_code_script = textwrap.dedent( ++ f""" ++ {API_INIT} ++ cmd = api.Command["subid_add"](ipaowner="jsmith") ++ assert 'Added subordinate id ' in cmd['summary'] ++ """ ++ ) ++ self.create_and_run_script( ++ "/tmp/add_subid.py", user_code_script ++ ) ++ ++ def test_add_otptoken(self): ++ """ ++ This test checks that otp token is added for IPA user ++ using api.Command["otptoken_add"] ++ """ ++ user_code_script = textwrap.dedent( ++ f""" ++ {API_INIT} ++ cmd = api.Command["otptoken_add"]( ++ type='HOTP', description='testotp', ++ ipatokenotpalgorithm='sha512', ipatokenowner='jsmith', ++ ipatokenotpdigits='6') ++ assert 'Added OTP token' in cmd['summary'] ++ """ ++ ) ++ self.create_and_run_script( ++ "/tmp/add_otptoken.py", user_code_script ++ ) ++ ++ def test_user_del(self): ++ """ ++ This test checks that user is deleted ++ using api.Command["user_del"] ++ """ ++ user_code_script = textwrap.dedent( ++ f""" ++ {API_INIT} ++ cmd = api.Command["user_del"]("jsmith") ++ assert 'Deleted user "jsmith"' in cmd['summary'] ++ """ ++ ) ++ self.create_and_run_script( ++ "/tmp/user_del.py", user_code_script ++ ) ++ ++ def test_remove_ipa_group(self): ++ """ ++ This test checks that group is removed ++ using api.Command["group_del"] ++ """ ++ user_code_script = textwrap.dedent( ++ f""" ++ {API_INIT} ++ cmd = api.Command["group_del"]("developers") ++ assert 'Deleted group "developers"' in cmd['summary'] ++ """ ++ ) ++ self.create_and_run_script( ++ "/tmp/show_group.py", user_code_script ++ ) ++ ++ def test_batch_command(self): ++ """ ++ This test checks that batch commands ++ can be run using api. ++ """ ++ user_code_script = textwrap.dedent( ++ f""" ++ {API_INIT} ++ batch_args = [] ++ for i in range(5): ++ user_id = "user%i" % i ++ args = [user_id] ++ kw = {{'givenname' : user_id, 'sn' : user_id}} ++ batch_args.append({{'method' : 'user_add', 'params' : [args, kw]}}) ++ api.Command["batch"](*batch_args) ++ """ ++ ) ++ self.create_and_run_script( ++ "/tmp/batch.py", user_code_script ++ ) +-- +2.41.0 + diff --git a/SOURCES/0024-ipa-kdb-postpone-ticket-checksum-configuration.patch b/SOURCES/0024-ipa-kdb-postpone-ticket-checksum-configuration.patch deleted file mode 100644 index dc2112f..0000000 --- a/SOURCES/0024-ipa-kdb-postpone-ticket-checksum-configuration.patch +++ /dev/null @@ -1,269 +0,0 @@ -From 33242a967011b9cbce74b6b3c39a7247d66eda19 Mon Sep 17 00:00:00 2001 -From: Alexander Bokovoy -Date: Thu, 25 May 2023 09:19:57 +0300 -Subject: [PATCH] ipa-kdb: postpone ticket checksum configuration - -Postpone ticket checksum configuration after KDB module was initialized. -This, in practice, should now happen when a master key is retrieved. - -Signed-off-by: Alexander Bokovoy -Reviewed-By: Julien Rische -(cherry picked from commit fefa0248296413b6ee5ad2543d8feb1b31840aee) ---- - daemons/ipa-kdb/ipa_kdb.c | 56 +---------------------- - daemons/ipa-kdb/ipa_kdb.h | 8 +++- - daemons/ipa-kdb/ipa_kdb_common.c | 67 +++++++++++++++++++++++++++- - daemons/ipa-kdb/ipa_kdb_principals.c | 14 ++++-- - 4 files changed, 84 insertions(+), 61 deletions(-) - -diff --git a/daemons/ipa-kdb/ipa_kdb.c b/daemons/ipa-kdb/ipa_kdb.c -index 9a56640ff..a3c3746c2 100644 ---- a/daemons/ipa-kdb/ipa_kdb.c -+++ b/daemons/ipa-kdb/ipa_kdb.c -@@ -524,52 +524,6 @@ static krb5_principal ipadb_create_local_tgs(krb5_context kcontext, - return tgtp; - } - --static char *no_attrs[] = { -- LDAP_NO_ATTRS, -- -- NULL --}; -- --static krb5_error_code --should_support_pac_tkt_sign(krb5_context kcontext, bool *result) --{ -- struct ipadb_context *ipactx; -- krb5_error_code kerr; -- LDAPMessage *res = NULL; -- char *masters_dn = NULL; -- int count; -- -- char *kdc_filter = "(&(cn=KDC)(objectClass=ipaConfigObject)" -- "(!(ipaConfigString=pacTktSignSupported)))"; -- -- ipactx = ipadb_get_context(kcontext); -- if (!ipactx) { -- kerr = KRB5_KDB_DBNOTINITED; -- goto done; -- } -- -- count = asprintf(&masters_dn, "cn=masters,cn=ipa,cn=etc,%s", ipactx->base); -- if (count < 0) { -- kerr = ENOMEM; -- goto done; -- } -- -- kerr = ipadb_simple_search(ipactx, masters_dn, LDAP_SCOPE_SUBTREE, -- kdc_filter, no_attrs, &res); -- if (kerr) -- goto done; -- -- count = ldap_count_entries(ipactx->lcontext, res); -- -- if (result) -- *result = (count == 0); -- --done: -- free(masters_dn); -- ldap_msgfree(res); -- return kerr; --} -- - /* INTERFACE */ - - static krb5_error_code ipadb_init_library(void) -@@ -590,7 +544,6 @@ static krb5_error_code ipadb_init_module(krb5_context kcontext, - krb5_error_code kerr; - int ret; - int i; -- bool pac_tkt_sign_supported; - - /* make sure the context is freed to avoid leaking it */ - ipactx = ipadb_get_context(kcontext); -@@ -662,6 +615,8 @@ static krb5_error_code ipadb_init_module(krb5_context kcontext, - goto fail; - } - -+ ipactx->optional_pac_tkt_chksum = IPADB_TRISTATE_UNDEFINED; -+ - ret = ipadb_get_connection(ipactx); - if (ret != 0) { - /* Not a fatal failure, as the LDAP server may be temporarily down. */ -@@ -675,13 +630,6 @@ static krb5_error_code ipadb_init_module(krb5_context kcontext, - goto fail; - } - -- /* Enforce PAC ticket signature verification if supported by all KDCs */ -- kerr = should_support_pac_tkt_sign(kcontext, &pac_tkt_sign_supported); -- if (kerr) { -- ret = kerr; -- goto fail; -- } -- ipactx->optional_pac_tkt_chksum = !pac_tkt_sign_supported; - - return 0; - -diff --git a/daemons/ipa-kdb/ipa_kdb.h b/daemons/ipa-kdb/ipa_kdb.h -index 0f4d3e431..edf3b0dfc 100644 ---- a/daemons/ipa-kdb/ipa_kdb.h -+++ b/daemons/ipa-kdb/ipa_kdb.h -@@ -126,6 +126,12 @@ struct ipadb_global_config { - bool disable_preauth_for_spns; - }; - -+enum ipadb_tristate_option { -+ IPADB_TRISTATE_FALSE = FALSE, -+ IPADB_TRISTATE_TRUE = TRUE, -+ IPADB_TRISTATE_UNDEFINED, -+}; -+ - #define IPA_CONTEXT_MAGIC 0x0c027ea7 - struct ipadb_context { - int magic; -@@ -143,7 +149,7 @@ struct ipadb_context { - krb5_key_salt_tuple *def_encs; - int n_def_encs; - struct ipadb_mspac *mspac; -- bool optional_pac_tkt_chksum; -+ enum ipadb_tristate_option optional_pac_tkt_chksum; - #ifdef HAVE_KRB5_CERTAUTH_PLUGIN - krb5_certauth_moddata certauth_moddata; - #endif -diff --git a/daemons/ipa-kdb/ipa_kdb_common.c b/daemons/ipa-kdb/ipa_kdb_common.c -index 42e0856d0..ae7742a32 100644 ---- a/daemons/ipa-kdb/ipa_kdb_common.c -+++ b/daemons/ipa-kdb/ipa_kdb_common.c -@@ -158,12 +158,75 @@ static bool ipadb_need_retry(struct ipadb_context *ipactx, int error) - return false; - } - -+static char *no_attrs[] = { -+ LDAP_NO_ATTRS, -+ -+ NULL -+}; -+ -+static int -+should_support_pac_tkt_sign(struct ipadb_context *ipactx, bool *result) -+{ -+ int ret; -+ LDAPMessage *res = NULL; -+ char *masters_dn = NULL; -+ int count; -+ -+ char *kdc_filter = "(&(cn=KDC)(objectClass=ipaConfigObject)" -+ "(!(ipaConfigString=pacTktSignSupported)))"; -+ -+ if (!ipactx) { -+ ret = KRB5_KDB_DBNOTINITED; -+ goto done; -+ } -+ -+ count = asprintf(&masters_dn, "cn=masters,cn=ipa,cn=etc,%s", ipactx->base); -+ if (count < 0) { -+ ret = ENOMEM; -+ goto done; -+ } -+ -+ ret = ipadb_simple_search(ipactx, masters_dn, LDAP_SCOPE_SUBTREE, -+ kdc_filter, no_attrs, &res); -+ if (ret) -+ goto done; -+ -+ count = ldap_count_entries(ipactx->lcontext, res); -+ -+ if (result) -+ *result = (count == 0); -+ -+done: -+ free(masters_dn); -+ ldap_msgfree(res); -+ return ret; -+} -+ - static int ipadb_check_connection(struct ipadb_context *ipactx) - { -+ int ret = 0; -+ - if (ipactx->lcontext == NULL) { -- return ipadb_get_connection(ipactx); -+ ret = ipadb_get_connection(ipactx); -+ } -+ if ((ret == 0) && (ipactx->optional_pac_tkt_chksum == IPADB_TRISTATE_UNDEFINED)) { -+ bool pac_tkt_sign_supported; -+ -+ /* Enforce PAC ticket signature verification if supported by all KDCs -+ * To avoid loops as all search functions call into -+ * ipadb_check_connection(), mark that the init is complete at this -+ * point. Default to not issuing PAC to be safe. -+ */ -+ ipactx->optional_pac_tkt_chksum = IPADB_TRISTATE_FALSE; -+ ret = should_support_pac_tkt_sign(ipactx, -+ &pac_tkt_sign_supported); -+ if (ret == 0) { -+ ipactx->optional_pac_tkt_chksum = !pac_tkt_sign_supported; -+ } else { -+ ipactx->optional_pac_tkt_chksum = IPADB_TRISTATE_UNDEFINED; -+ } - } -- return 0; -+ return ret; - } - - krb5_error_code ipadb_simple_search(struct ipadb_context *ipactx, -diff --git a/daemons/ipa-kdb/ipa_kdb_principals.c b/daemons/ipa-kdb/ipa_kdb_principals.c -index e6c3fba21..d35cec2e0 100644 ---- a/daemons/ipa-kdb/ipa_kdb_principals.c -+++ b/daemons/ipa-kdb/ipa_kdb_principals.c -@@ -113,7 +113,9 @@ static char *std_principal_obj_classes[] = { - - #define DEFAULT_TL_DATA_CONTENT "\x00\x00\x00\x00principal@UNINITIALIZED" - --#define OPT_PAC_TKT_CHKSUM_STR_ATTR_NAME "optional_pac_tkt_chksum" -+#ifndef KRB5_KDB_SK_OPTIONAL_PAC_TKT_CHKSUM -+#define KRB5_KDB_SK_OPTIONAL_PAC_TKT_CHKSUM "optional_pac_tkt_chksum" -+#endif - - static int ipadb_ldap_attr_to_tl_data(LDAP *lcontext, LDAPMessage *le, - char *attrname, -@@ -1710,6 +1712,10 @@ krb5_error_code ipadb_get_principal(krb5_context kcontext, - if (kerr) - return kerr; - -+ /* We should have been initialized at this point already */ -+ if (ipactx->optional_pac_tkt_chksum == IPADB_TRISTATE_UNDEFINED) { -+ return KRB5_KDB_SERVER_INTERNAL_ERR; -+ } - /* PAC ticket signature should be optional for foreign realms, and local - * realm if not supported by all servers - */ -@@ -1719,7 +1725,7 @@ krb5_error_code ipadb_get_principal(krb5_context kcontext, - opt_pac_tkt_chksum_val = "false"; - - kerr = krb5_dbe_set_string(kcontext, *entry, -- OPT_PAC_TKT_CHKSUM_STR_ATTR_NAME, -+ KRB5_KDB_SK_OPTIONAL_PAC_TKT_CHKSUM, - opt_pac_tkt_chksum_val); - } - -@@ -2828,14 +2834,14 @@ remove_virtual_str_attrs(krb5_context kcontext, krb5_db_entry *entry) - krb5_error_code kerr; - - kerr = krb5_dbe_get_string(kcontext, entry, -- OPT_PAC_TKT_CHKSUM_STR_ATTR_NAME, -+ KRB5_KDB_SK_OPTIONAL_PAC_TKT_CHKSUM, - &str_attr_val); - if (kerr) - return kerr; - - if (str_attr_val) - kerr = krb5_dbe_set_string(kcontext, entry, -- OPT_PAC_TKT_CHKSUM_STR_ATTR_NAME, -+ KRB5_KDB_SK_OPTIONAL_PAC_TKT_CHKSUM, - NULL); - - krb5_dbe_free_string(kcontext, str_attr_val); --- -2.39.2 - diff --git a/SOURCES/0024-ipatests-fixture-can-produce-IndexError.patch b/SOURCES/0024-ipatests-fixture-can-produce-IndexError.patch new file mode 100644 index 0000000..83ce90c --- /dev/null +++ b/SOURCES/0024-ipatests-fixture-can-produce-IndexError.patch @@ -0,0 +1,45 @@ +From f816b4d9e6ff7a47b0da1a368d2454add78af07c Mon Sep 17 00:00:00 2001 +From: Florence Blanc-Renaud +Date: Fri, 11 Aug 2023 09:10:30 +0200 +Subject: [PATCH] ipatests: fixture can produce IndexError + +The fixture issue_and_expire_acme_cert returns a function +that fills the hosts array. If the function is not called in +the test (for instance because a test is skipped, as in +TestACMEPrune::test_prune_cert_search_size_limit), hosts = [] +and hosts[0] raises an IndexError. + +Fix the fixture to check first that hosts is not empty. + +Related: https://pagure.io/freeipa/issue/9348 + +Signed-off-by: Florence Blanc-Renaud +Reviewed-By: Mohammad Rizwan Yusuf +--- + ipatests/test_integration/test_acme.py | 10 +++++++--- + 1 file changed, 7 insertions(+), 3 deletions(-) + +diff --git a/ipatests/test_integration/test_acme.py b/ipatests/test_integration/test_acme.py +index c7389732cd067d49541cd04ea6687a6b95b4669f..bc989a9a2a00b0ac68b9dcffd4ccea269314961b 100644 +--- a/ipatests/test_integration/test_acme.py ++++ b/ipatests/test_integration/test_acme.py +@@ -633,9 +633,13 @@ def issue_and_expire_acme_cert(): + tasks.move_date(host, 'start', '-90days-60minutes') + + # restart ipa services as date moved and wait to get things settle +- time.sleep(10) +- hosts[0].run_command(['ipactl', 'restart']) +- time.sleep(10) ++ # if the internal fixture was not called (for instance because the test ++ # was skipped), hosts = [] and hosts[0] would produce an IndexError ++ # exception. ++ if hosts: ++ time.sleep(10) ++ hosts[0].run_command(['ipactl', 'restart']) ++ time.sleep(10) + + + class TestACMERenew(IntegrationTest): +-- +2.41.0 + diff --git a/SOURCES/0025-Installer-activate-nss-and-pam-services-in-sssd.conf.patch b/SOURCES/0025-Installer-activate-nss-and-pam-services-in-sssd.conf.patch new file mode 100644 index 0000000..a6a0eb3 --- /dev/null +++ b/SOURCES/0025-Installer-activate-nss-and-pam-services-in-sssd.conf.patch @@ -0,0 +1,42 @@ +From 4a62a21499a4884f0db55d01966a6ff532a4ed1e Mon Sep 17 00:00:00 2001 +From: Florence Blanc-Renaud +Date: Mon, 14 Aug 2023 10:53:05 +0200 +Subject: [PATCH] Installer: activate nss and pam services in sssd.conf + +If there is already a sssd.conf file before the installer is +executed, the nss and pam services may not be enabled by the +installer. This happens for instance if the machine is hardened +for STIG and sssd.conf does not define services=... in the +[sssd] section. + +The consequence is that trust cannot be established with an AD +domain. + +The installer must enable nss and pam services even if there is +a pre-existing sssd.conf file. + +Fixes: https://pagure.io/freeipa/issue/9427 + +Signed-off-by: Florence Blanc-Renaud +Reviewed-By: Alexander Bokovoy +--- + ipaclient/install/client.py | 3 +++ + 1 file changed, 3 insertions(+) + +diff --git a/ipaclient/install/client.py b/ipaclient/install/client.py +index ef29a2c8a3f673860cb22e0e6953853fd96a8572..07d62a748f77e990a38e28e3675abb05eef0da8d 100644 +--- a/ipaclient/install/client.py ++++ b/ipaclient/install/client.py +@@ -969,6 +969,9 @@ def configure_sssd_conf( + nss_service.set_option('memcache_timeout', 600) + sssdconfig.save_service(nss_service) + ++ sssd_enable_service(sssdconfig, 'nss') ++ sssd_enable_service(sssdconfig, 'pam') ++ + domain.set_option('ipa_domain', cli_domain) + domain.set_option('ipa_hostname', client_hostname) + if cli_domain.lower() != cli_realm.lower(): +-- +2.41.0 + diff --git a/SOURCES/freeipa-4.10.1.tar.gz.asc b/SOURCES/freeipa-4.10.1.tar.gz.asc deleted file mode 100644 index 1e67a5b..0000000 --- a/SOURCES/freeipa-4.10.1.tar.gz.asc +++ /dev/null @@ -1,16 +0,0 @@ ------BEGIN PGP SIGNATURE----- - -iQIzBAABCAAdFiEE11Z2TU1+KXxtrRFyaYdvcqbi008FAmN/lzsACgkQaYdvcqbi -00+f7w//a5Y4tgTD2A52fQdihv/Q2ZZqhPlSt8rP7bTLtGSjvANHpuF2u0FcjmtB -tADL4zljBGGMGONGt1qMF3d0BkAXhyVp8YNIBcL+abEhgeKL++3egOrI/8iyl4FB -Sr8Gvr1T4AJXjz+yZ/SsPNKerqFrJwi3flavZ7Bk1c2W2V3rJubSAvK9XwO7Cyxs -4afYpLAiknPh999q3zt7NyviivBTYXirif2T45Lelln9sQaB1ksqDA7oYYAfhV0B -wmUJMTtDoaMAcsLT29aC8wGSB5dttI5GmrF60gnbq/t5Tr+7x9ch8VYE60qkynU9 -n1WcouuIwvcwfPpYSONak0vFrnMV2USNQj5OnzlKXl1HnHsSqoaQWRUA6bbBPjdb -xQLdQRrZiYtjG5YcwuZyg8TRP2ph1aY3yL2gAR1ZI13yJeNV0pwU+IwouJDiVPvS -dwiG8en1jUxYCeXFKjPX3A8/2ei+LvLTVY65qZKT+BsXyE2CymmUx7P20lFN5Ksb -RN+g37mJI8CwqmlemdTZiF+wJnlswS9SOS5zxhpVRiJHAV1sfxu66515c9+n890A -Hlm9sZn9UBQN5nLwuak8jaOr73E+d8uieXsSCA4wTtXKQSJs67fNY6ldeUmNximc -fq6Yh+lDau14rDlPZUkXx0Jd/oPEWDrtnJzfHECeoRn3KoNzezU= -=pxPY ------END PGP SIGNATURE----- diff --git a/SOURCES/freeipa-4.10.2.tar.gz.asc b/SOURCES/freeipa-4.10.2.tar.gz.asc new file mode 100644 index 0000000..da2e960 --- /dev/null +++ b/SOURCES/freeipa-4.10.2.tar.gz.asc @@ -0,0 +1,16 @@ +-----BEGIN PGP SIGNATURE----- + +iQIzBAABCAAdFiEE11Z2TU1+KXxtrRFyaYdvcqbi008FAmR+6S4ACgkQaYdvcqbi +008kvQ/9HuW6rWRJPY9/bqfaaeTKAT9JPCdq6jwDj7rSxpzJRE2r8vuwL3XxvXEa +8qW3qwx4qcBV3hrOPX3annilCgfyUN0ayF72rfDxGmVzTifooEVd0mmaDJOd9JHy +wLVYbImvsitC59luFF/XTIx5Xgo8Msu7BtAC5Tf5+mws0/i6ZxmyufYBPEmydgVM +jdpVSZfq4hd4HwfYH0Ej5c8sWfv2OTTENyBWn5x9LiWvC6rJi42u7ubcnWoCi6If +kXzJCRf7JTDzDvmMDVZMoTJCQa0EwlST7Yr9V7rLJViUGDwqxVO7VoeQi2WoV5PN +zDSoLoCVEgLdDt0EAYRlBxPzoe65kNBdGFhC3eT0uIO676NZuTMVEXaRtId1wfnU +o52xx3r5OPvRrjRLhKsAsMSiqX0Cr/wgH1L8QuSpriM1gMO6jZTkFpGtjD1XEvWd +VsiSVqR0y+E8U3bkLOTHvByPvWk9QlmN0e6WDyV0FvGPaPlD2VCGNLqraldeEKvr +wQxVvMWMqX+CKs2ZWYlzfgzFd0ZlUfsYhTcDaliJIh5Tcn5Ow+CSX/vWMXS6Msyh +80fS5k7NswIsa3bA0QSoFpAcSkNxxovKyIWpXamL/MgX/nqZqJE74aUtchANl9jd +j5yAU2s4eUuvbTMQkQjEJHl97a/jjGCGKnCunF/RO3WLaGdi9Dk= +=Bq0h +-----END PGP SIGNATURE----- diff --git a/SPECS/freeipa.spec b/SPECS/freeipa.spec index 21ebbf8..1c0672b 100644 --- a/SPECS/freeipa.spec +++ b/SPECS/freeipa.spec @@ -66,7 +66,7 @@ %if 0%{?rhel} %global package_name ipa %global alt_name freeipa -%global krb5_version 1.20.1-9 +%global krb5_version 1.20.1-1 %global krb5_kdb_version 9.0 # 0.7.16: https://github.com/drkjam/netaddr/issues/71 %global python_netaddr_version 0.7.19 @@ -178,7 +178,7 @@ # RHEL 8.2+, F32+ has 3.58 %global nss_version 3.44.0-4 -%define krb5_base_version %(LC_ALL=C /usr/bin/pkgconf --modversion krb5 | grep -Eo '^[^.]+\.[^.]+' || echo %krb5_version) +%define krb5_base_version %(LC_ALL=C /usr/bin/pkgconf --modversion krb5 2>/dev/null | grep -Eo '^[^.]+\.[^.]+' || echo %krb5_version) %global kdcproxy_version 0.4-3 %if 0%{?fedora} >= 33 || 0%{?rhel} >= 9 @@ -210,7 +210,7 @@ # Work-around fact that RPM SPEC parser does not accept # "Version: @VERSION@" in freeipa.spec.in used for Autoconf string replacement -%define IPA_VERSION 4.10.1 +%define IPA_VERSION 4.10.2 # Release candidate version -- uncomment with one percent for RC versions #%%global rc_version %%nil %define AT_SIGN @ @@ -223,10 +223,10 @@ Name: %{package_name} Version: %{IPA_VERSION} -Release: 7%{?rc_version:.%rc_version}%{?dist} +Release: 4%{?rc_version:.%rc_version}%{?dist} Summary: The Identity, Policy and Audit system -License: GPLv3+ +License: GPL-3.0-or-later URL: http://www.freeipa.org/ Source0: https://releases.pagure.org/freeipa/freeipa-%{version}%{?rc_version}.tar.gz # Only use detached signature for the distribution builds. If it is a developer build, skip it @@ -247,30 +247,31 @@ Patch1001: 1001-Change-branding-to-IPA-and-Identity-Management.patch Patch1002: 1002-Revert-freeipa.spec-depend-on-bind-dnssec-utils.patch %endif %if 0%{?rhel} == 9 -Patch0001: 0001-updates-fix-memberManager-ACI-to-allow-managers-from.patch -Patch0002: 0002-Spec-file-ipa-client-depends-on-krb5-pkinit-openssl.patch -Patch0003: 0003-server-install-remove-error-log-about-missing-bkup-f.patch -Patch0004: 0004-ipa-tests-Add-LANG-before-kinit-command-to-fix-issue.patch -Patch0005: 0005-trust-add-handle-missing-msSFU30MaxGidNumber.patch -Patch0006: 0006-doc-Design-for-certificate-pruning.patch -Patch0007: 0007-ipa-acme-manage-add-certificate-request-pruning-mana.patch -Patch0008: 0008-doc-add-the-run-command-for-manual-job-execution.patch -Patch0009: 0009-tests-add-wrapper-around-ACME-RSNv3-test.patch -Patch0010: 0010-automember-rebuild-add-a-notice-about-high-CPU-usage.patch -Patch0011: 0011-Fix-setting-values-of-0-in-ACME-pruning.patch -Patch0012: 0012-Wipe-the-ipa-ca-DNS-record-when-updating-system-reco.patch -Patch0013: 0013-ipa-kdb-PAC-consistency-checker-needs-to-handle-chil.patch -Patch0014: 0014-Add-test-for-SSH-with-GSSAPI-auth.patch -Patch0015: 0015-webui-tests-fix-assertion-in-test_subid.py.patch -Patch0016: 0016-ipatests-mark-test_smb-as-xfail.patch -Patch0017: 0017-Tests-force-key-type-in-ACME-tests.patch -Patch0018: 0018-tests-Add-ipa_ca_name-checking-to-DNS-system-records.patch -Patch0019: 0019-tests-Add-new-ipa-ca-error-messages-to-IPADNSSystemR.patch -Patch0020: 0020-ipatests-tests-for-certificate-pruning.patch -Patch0021: 0021-ipatests-ensure-that-ipa-automember-rebuild-prints-a.patch -Patch0022: 0022-ipatests-fix-tests-in-TestACMEPrune.patch -Patch0023: 0023-Tolerate-absence-of-PAC-ticket-signature-depending-o.patch -Patch0024: 0024-ipa-kdb-postpone-ticket-checksum-configuration.patch +Patch0001: 0001-webuitests-close-notification-which-hides-Add-button.patch +Patch0002: 0002-ipatests-Check-that-SSSD_PUBCONF_KRB5_INCLUDE_D_DIR-.patch +Patch0003: 0003-Revert-Use-the-OpenSSL-certificate-parser-in-cert-fi.patch +Patch0004: 0004-Revert-cert_find-fix-call-with-all.patch +Patch0005: 0005-Use-the-python-cryptography-parser-directly-in-cert-.patch +Patch0006: 0006-Upgrade-add-PKI-drop-in-file-if-missing.patch +Patch0007: 0007-Integration-test-add-a-test-for-upgrade-and-PKI-drop.patch +Patch0008: 0008-Uninstaller-uninstall-PKI-before-shutting-down-servi.patch +Patch0009: 0009-Detection-of-PKI-subsystem.patch +Patch0010: 0010-Upgrade-fix-replica-agreement.patch +Patch0011: 0011-Integration-tests-add-a-test-to-ipa-server-upgrade.patch +Patch0012: 0012-tests-fix-backup-restore-scenario-with-replica.patch +Patch0013: 0013-OTP-fix-data-type-to-avoid-endianness-issue.patch +Patch0014: 0014-ipatests-enable-firewall-rule-for-http-service-on-ac.patch +Patch0015: 0015-User-plugin-improve-error-related-to-non-existing-id.patch +Patch0016: 0016-xmlrpc-tests-add-a-test-for-user-plugin-with-non-exi.patch +Patch0017: 0017-Fix-memory-leak-in-the-OTP-last-token-plugin.patch +Patch0018: 0018-Prevent-the-admin-user-from-being-deleted.patch +Patch0019: 0019-ipa-kdb-fix-error-handling-of-is_master_host.patch +Patch0020: 0020-ipatests-update-expected-webui-msg-for-admin-deletio.patch +Patch0021: 0021-ipatests-remove-fixture-call-and-wait-to-get-things-.patch +Patch0022: 0022-ipatests-fix-test_topology.patch +Patch0023: 0023-ipatests-idm-api-related-tests.patch +Patch0024: 0024-ipatests-fixture-can-produce-IndexError.patch +Patch0025: 0025-Installer-activate-nss-and-pam-services-in-sssd.conf.patch Patch1001: 1001-Change-branding-to-IPA-and-Identity-Management.patch %endif %endif @@ -321,7 +322,12 @@ BuildRequires: libpwquality-devel BuildRequires: libsss_idmap-devel BuildRequires: libsss_certmap-devel BuildRequires: libsss_nss_idmap-devel >= %{sssd_version} +%if 0%{?fedora} >= 39 || 0%{?rhel} >= 10 +# Do not use nodejs20 on fedora < 39, https://pagure.io/freeipa/issue/9374 BuildRequires: nodejs(abi) +%else +BuildRequires: nodejs(abi) < 111 +%endif # use old dependency on RHEL 8 for now %if 0%{?fedora} >= 31 || 0%{?rhel} >= 9 BuildRequires: python3-rjsmin @@ -502,7 +508,7 @@ Requires: policycoreutils >= 2.1.12-5 Requires: tar Requires(pre): certmonger >= %{certmonger_version} Requires(pre): 389-ds-base >= %{ds_version} -Requires: fontawesome-fonts +Requires: font(fontawesome) Requires: open-sans-fonts %if 0%{?fedora} >= 32 || 0%{?rhel} >= 9 # https://pagure.io/freeipa/issue/8632 @@ -716,6 +722,7 @@ Requires: jansson %endif Requires: sssd-ipa >= %{sssd_version} Requires: sssd-idp >= %{sssd_version} +Requires: sssd-krb5 >= %{sssd_version} Requires: certmonger >= %{certmonger_version} Requires: nss-tools >= %{nss_version} Requires: bind-utils @@ -1261,10 +1268,8 @@ if [ $1 -gt 1 ] ; then test -f '/var/lib/ipa-client/sysrestore/sysrestore.index' && restore=$(wc -l '/var/lib/ipa-client/sysrestore/sysrestore.index' | awk '{print $1}') if [ -f '/etc/sssd/sssd.conf' -a $restore -ge 2 ]; then - if ! grep -E -q '/var/lib/sss/pubconf/krb5.include.d/' /etc/krb5.conf 2>/dev/null ; then - echo "includedir /var/lib/sss/pubconf/krb5.include.d/" > /etc/krb5.conf.ipanew - cat /etc/krb5.conf >> /etc/krb5.conf.ipanew - mv -Z /etc/krb5.conf.ipanew /etc/krb5.conf + if grep -E -q '/var/lib/sss/pubconf/krb5.include.d/' /etc/krb5.conf 2>/dev/null ; then + sed -i '\;includedir /var/lib/sss/pubconf/krb5.include.d;d' /etc/krb5.conf fi fi @@ -1758,8 +1763,34 @@ fi %endif %changelog -* Thu Jun 01 2023 Julien Rische - 4.10.1-7 -- Resolves: rhbz#2211389 Handle PAC signatures based on domain and server capabilities +* Thu Aug 17 2023 Florence Blanc-Renaud - 4.10.2-4 +- Resolves: rhbz#2231847 RHEL 8.8 & 9.2 fails to create AD trust with STIG applied +- Resolves: rhbz#2232056 Include latest test fixes in python3-ipatests + +* Thu Aug 10 2023 Florence Blanc-Renaud - 4.10.2-3 +- Resolves: rhbz#2229712 Delete operation protection for admin user +- Resolves: rhbz#2227831 Interrupt request processing in ipadb_fill_info3() if connection to 389ds is lost +- Resolves: rhbz#2227784 libipa_otp_lasttoken plugin memory leak +- Resolves: rhbz#2224570 Improved error messages are needed when attempting to add a non-existing idp to a user +- Resolves: rhbz#2230251 Backport latest test fixes to python3-ipatests + +* Thu Jun 29 2023 Florence Blanc-Renaud - 4.10.2-2 +- Resolves: rhbz#2192969 Better handling of the command line and web UI cert search and/or list features +- Resolves: rhbz#2214933 Uninstalling of the IPA server is encountering a failure during the unconfiguration of the CA (Unconfiguring CA) +- Resolves: rhbz#2216114 After updating the RHEL from 8.7 to 8.8, IPA services fails to start +- Resolves: rhbz#2216549 Upgrade to 4.9.10-6.0.1 fails: attributes are managed by topology plugin +- Resolves: rhbz#2216611 Backport latest test fixes in python3-ipatests +- Resolves: rhbz#2216872 User authentication failing on OTP validation using multiple tokens, succeeds with password only + +* Tue Jun 06 2023 Florence Blanc-Renaud - 4.10.2-1 +- Resolves: rhbz#2196426 [Rebase] Rebase ipa to latest 4.10.x release for RHEL 9.3 +- Resolves: rhbz#2192969 Better handling of the command line and web UI cert search and/or list features +- Resolves: rhbz#2192625 Better catch of the IPA web UI event "IPA Error 4301:CertificateOperationError", and IPA httpd error CertificateOperationError +- Resolves: rhbz#2188567 IPA client Kerberos configuration incompatible with java +- Resolves: rhbz#2182683 Tolerate absence of PAC ticket signature depending of domain and servers capabilities [rhel-9] +- Resolves: rhbz#2180914 Sequence processing failures for group_add using server context +- Resolves: rhbz#2165880 Add RBCD support to IPA +- Resolves: rhbz#2160399 get_ranges - [file ipa_sidgen_common.c, line 276]: Failed to convert LDAP entry to range struct * Wed Feb 22 2023 Florence Blanc-Renaud - 4.10.1-6 - Resolves: rhbz#2169632 Backport latest test fixes in python3-ipatests