From 0f528845e478a48905d253b5a57b4ab0621de1eb Mon Sep 17 00:00:00 2001 From: CentOS Sources Date: Tue, 22 Nov 2022 15:48:34 +0000 Subject: [PATCH] import ipa-4.9.10-7.module+el8.8.0+17159+ac558a8b --- ...d-integration-tests-for-external-IdP.patch | 346 ++++++++++++++++++ ...w-empty-pagination-size_rhbz#2094672.patch | 67 ++++ ...Allow-grace-login-limit_rhbz#2109243.patch | 56 +++ ...n-progress-is-a-boolean_rhbz#2117303.patch | 35 ++ ...-not-prevent-LDAP-binds_rhbz#2109236.patch | 125 +++++++ ...cy-on-group-pw-policies_rhbz#2115475.patch | 230 ++++++++++++ SPECS/ipa.spec | 36 +- 7 files changed, 890 insertions(+), 5 deletions(-) create mode 100644 SOURCES/0005-Add-end-to-end-integration-tests-for-external-IdP.patch create mode 100644 SOURCES/0006-webui-Do-not-allow-empty-pagination-size_rhbz#2094672.patch create mode 100644 SOURCES/0007-webui-Allow-grace-login-limit_rhbz#2109243.patch create mode 100644 SOURCES/0008-check_repl_update-in-progress-is-a-boolean_rhbz#2117303.patch create mode 100644 SOURCES/0009-Disabling-gracelimit-does-not-prevent-LDAP-binds_rhbz#2109236.patch create mode 100644 SOURCES/0010-Set-passwordgracelimit-to-match-global-policy-on-group-pw-policies_rhbz#2115475.patch diff --git a/SOURCES/0005-Add-end-to-end-integration-tests-for-external-IdP.patch b/SOURCES/0005-Add-end-to-end-integration-tests-for-external-IdP.patch new file mode 100644 index 0000000..700df13 --- /dev/null +++ b/SOURCES/0005-Add-end-to-end-integration-tests-for-external-IdP.patch @@ -0,0 +1,346 @@ +From 857713c5a9c8e0b62c06dd92e69c09eeb34b2e99 Mon Sep 17 00:00:00 2001 +From: Anuja More +Date: Mon, 23 May 2022 12:26:34 +0530 +Subject: [PATCH] Add end to end integration tests for external IdP + +Added tests for HBAC and SUDO rule and other +test scenarios. + +Related : https://pagure.io/freeipa/issue/8805 +Related: https://pagure.io/freeipa/issue/8803 +Related: https://pagure.io/freeipa/issue/8804 + +Signed-off-by: Anuja More +Reviewed-By: Florence Blanc-Renaud +Reviewed-By: Florence Blanc-Renaud +--- + ipatests/test_integration/test_idp.py | 260 ++++++++++++++++++++++---- + 1 file changed, 226 insertions(+), 34 deletions(-) + +diff --git a/ipatests/test_integration/test_idp.py b/ipatests/test_integration/test_idp.py +index 8f9e92e6a..2ffe6a208 100644 +--- a/ipatests/test_integration/test_idp.py ++++ b/ipatests/test_integration/test_idp.py +@@ -1,6 +1,8 @@ + from __future__ import absolute_import + + import time ++import pytest ++import re + + import textwrap + from ipaplatform.paths import paths +@@ -22,12 +24,12 @@ driver.get(verification_uri) + try: + element = WebDriverWait(driver, 90).until( + EC.presence_of_element_located((By.ID, "username"))) +- driver.find_element_by_id("username").send_keys("testuser1") +- driver.find_element_by_id("password").send_keys("{passwd}") +- driver.find_element_by_id("kc-login").click() ++ driver.find_element(By.ID, "username").send_keys("testuser1") ++ driver.find_element(By.ID, "password").send_keys("{passwd}") ++ driver.find_element(By.ID, "kc-login").click() + element = WebDriverWait(driver, 90).until( + EC.presence_of_element_located((By.ID, "kc-login"))) +- driver.find_element_by_id("kc-login").click() ++ driver.find_element(By.ID, "kc-login").click() + assert "Device Login Successful" in driver.page_source + finally: + now = datetime.now().strftime("%M-%S") +@@ -39,18 +41,12 @@ finally: + def add_user_code(host, verification_uri): + contents = user_code_script.format(uri=verification_uri, + passwd=host.config.admin_password) +- host.put_file_contents("/tmp/add_user_code.py", contents) +- tasks.run_repeatedly( +- host, ['python3', '/tmp/add_user_code.py']) +- +- +-def get_verification_uri(host, since, keycloak_server_name): +- command = textwrap.dedent(""" +- journalctl -u ipa-otpd\\* --since="%s" | grep "user_code:" | awk '{ print substr($7,2,9) }'""" % since) # noqa: E501 +- user_code = host.run_command(command).stdout_text.rstrip("\r\n") +- uri = ("https://{0}:8443/auth/realms/master/device?user_code={1}".format( +- keycloak_server_name, user_code)) +- return uri ++ try: ++ host.put_file_contents("/tmp/add_user_code.py", contents) ++ tasks.run_repeatedly( ++ host, ['python3', '/tmp/add_user_code.py']) ++ finally: ++ host.run_command(["rm", "-f", "/tmp/add_user_code.py"]) + + + def kinit_idp(host, user, keycloak_server): +@@ -58,11 +54,14 @@ def kinit_idp(host, user, keycloak_server): + tasks.kdestroy_all(host) + # create armor for FAST + host.run_command(["kinit", "-n", "-c", ARMOR]) +- since = time.strftime('%Y-%m-%d %H:%M:%S') + cmd = ["kinit", "-T", ARMOR, user] ++ + with host.spawn_expect(cmd, default_timeout=100) as e: +- e.expect('Authenticate at .+: ') +- uri = get_verification_uri(host, since, keycloak_server.hostname) ++ e.expect('Authenticate at (.+) and press ENTER.:') ++ prompt = e.get_last_output() ++ uri = re.search(r'Authenticate at (.*?) and press ENTER.:', prompt ++ ).group(1) ++ time.sleep(15) + if uri: + add_user_code(keycloak_server, uri) + e.sendline('\n') +@@ -74,21 +73,27 @@ def kinit_idp(host, user, keycloak_server): + + class TestIDPKeycloak(IntegrationTest): + +- num_replicas = 1 ++ num_replicas = 2 + topology = 'line' + + @classmethod + def install(cls, mh): +- tasks.install_master(cls.master, setup_dns=True) +- tasks.install_client(cls.master, cls.replicas[0]) +- content = cls.master.get_file_contents(paths.IPA_DEFAULT_CONF, +- encoding='utf-8') +- new_content = content + "\noidc_child_debug_level = 10" +- cls.master.put_file_contents(paths.IPA_DEFAULT_CONF, new_content) ++ cls.client = cls.replicas[0] ++ cls.replica = cls.replicas[1] ++ tasks.install_master(cls.master) ++ tasks.install_client(cls.master, cls.replicas[0], ++ extra_args=["--mkhomedir"]) ++ tasks.install_replica(cls.master, cls.replicas[1]) ++ for host in [cls.master, cls.replicas[0], cls.replicas[1]]: ++ content = host.get_file_contents(paths.IPA_DEFAULT_CONF, ++ encoding='utf-8') ++ new_content = content + "\noidc_child_debug_level = 10" ++ host.put_file_contents(paths.IPA_DEFAULT_CONF, new_content) + with tasks.remote_sssd_config(cls.master) as sssd_config: + sssd_config.edit_domain( + cls.master.domain, 'krb5_auth_timeout', 1100) + tasks.clear_sssd_cache(cls.master) ++ tasks.clear_sssd_cache(cls.replicas[0]) + tasks.kinit_admin(cls.master) + cls.master.run_command(["ipa", "config-mod", "--user-auth-type=idp", + "--user-auth-type=password"]) +@@ -97,20 +102,207 @@ class TestIDPKeycloak(IntegrationTest): + cls.replicas[0].run_command(xvfb) + + def test_auth_keycloak_idp(self): +- keycloak_srv = self.replicas[0] +- create_quarkus.setup_keycloakserver(keycloak_srv) ++ """ ++ Test case to check that OAuth 2.0 Device ++ Authorization Grant is working as ++ expected for user configured with external idp. ++ """ ++ create_quarkus.setup_keycloakserver(self.client) + time.sleep(60) +- create_quarkus.setup_keycloak_client(keycloak_srv) ++ create_quarkus.setup_keycloak_client(self.client) + tasks.kinit_admin(self.master) +- cmd = ["ipa", "idp-add", "keycloak", "--provider=keycloak", ++ cmd = ["ipa", "idp-add", "keycloakidp", "--provider=keycloak", + "--client-id=ipa_oidc_client", "--org=master", +- "--base-url={0}:8443/auth".format(keycloak_srv.hostname)] ++ "--base-url={0}:8443/auth".format(self.client.hostname)] + self.master.run_command(cmd, stdin_text="{0}\n{0}".format( +- keycloak_srv.config.admin_password)) ++ self.client.config.admin_password)) + tasks.user_add(self.master, 'keycloakuser', + extra_args=["--user-auth-type=idp", + "--idp-user-id=testuser1@ipa.test", +- "--idp=keycloak"] ++ "--idp=keycloakidp"] + ) ++ list_user = self.master.run_command( ++ ["ipa", "user-find", "--idp-user-id=testuser1@ipa.test"] ++ ) ++ assert "keycloakuser" in list_user.stdout_text ++ list_by_idp = self.master.run_command(["ipa", "user-find", ++ "--idp=keycloakidp"] ++ ) ++ assert "keycloakuser" in list_by_idp.stdout_text ++ list_by_user = self.master.run_command( ++ ["ipa", "user-find", "--idp-user-id=testuser1@ipa.test", "--all"] ++ ) ++ assert "keycloakidp" in list_by_user.stdout_text ++ tasks.clear_sssd_cache(self.master) ++ kinit_idp(self.master, 'keycloakuser', keycloak_server=self.client) ++ ++ @pytest.fixture ++ def hbac_setup_teardown(self): ++ # allow sshd only on given host ++ tasks.kinit_admin(self.master) ++ self.master.run_command(["ipa", "hbacrule-disable", "allow_all"]) ++ self.master.run_command(["ipa", "hbacrule-add", "rule1"]) ++ self.master.run_command(["ipa", "hbacrule-add-user", "rule1", ++ "--users=keycloakuser"] ++ ) ++ self.master.run_command(["ipa", "hbacrule-add-host", "rule1", ++ "--hosts", self.replica.hostname]) ++ self.master.run_command(["ipa", "hbacrule-add-service", "rule1", ++ "--hbacsvcs=sshd"] ++ ) ++ tasks.clear_sssd_cache(self.master) ++ tasks.clear_sssd_cache(self.replica) ++ yield ++ ++ # cleanup ++ tasks.kinit_admin(self.master) ++ self.master.run_command(["ipa", "hbacrule-enable", "allow_all"]) ++ self.master.run_command(["ipa", "hbacrule-del", "rule1"]) ++ ++ def test_auth_hbac(self, hbac_setup_teardown): ++ """ ++ Test case to check that hbacrule is working as ++ expected for user configured with external idp. ++ """ ++ kinit_idp(self.master, 'keycloakuser', keycloak_server=self.client) ++ ssh_cmd = "ssh -q -K -l keycloakuser {0} whoami" ++ valid_ssh = self.master.run_command( ++ ssh_cmd.format(self.replica.hostname)) ++ assert "keycloakuser" in valid_ssh.stdout_text ++ negative_ssh = self.master.run_command( ++ ssh_cmd.format(self.master.hostname), raiseonerr=False ++ ) ++ assert negative_ssh.returncode == 255 ++ ++ def test_auth_sudo_idp(self): ++ """ ++ Test case to check that sudorule is working as ++ expected for user configured with external idp. ++ """ ++ tasks.kdestroy_all(self.master) ++ tasks.kinit_admin(self.master) ++ # rule: keycloakuser are allowed to execute yum on ++ # the client machine as root. ++ cmdlist = [ ++ ["ipa", "sudocmd-add", "/usr/bin/yum"], ++ ["ipa", "sudorule-add", "sudorule"], ++ ['ipa', 'sudorule-add-user', '--users=keycloakuser', ++ 'sudorule'], ++ ['ipa', 'sudorule-add-host', '--hosts', ++ self.client.hostname, 'sudorule'], ++ ['ipa', 'sudorule-add-runasuser', ++ '--users=root', 'sudorule'], ++ ['ipa', 'sudorule-add-allow-command', ++ '--sudocmds=/usr/bin/yum', 'sudorule'], ++ ['ipa', 'sudorule-show', 'sudorule', '--all'], ++ ['ipa', 'sudorule-add-option', ++ 'sudorule', '--sudooption', "!authenticate"] ++ ] ++ for cmd in cmdlist: ++ self.master.run_command(cmd) ++ tasks.clear_sssd_cache(self.master) ++ tasks.clear_sssd_cache(self.client) ++ try: ++ cmd = 'sudo -ll -U keycloakuser' ++ test = self.client.run_command(cmd).stdout_text ++ assert "User keycloakuser may run the following commands" in test ++ assert "/usr/bin/yum" in test ++ kinit_idp(self.client, 'keycloakuser', self.client) ++ test_sudo = 'su -c "sudo yum list wget" keycloakuser' ++ self.client.run_command(test_sudo) ++ list_fail = self.master.run_command(cmd).stdout_text ++ assert "User keycloakuser is not allowed to run sudo" in list_fail ++ finally: ++ tasks.kinit_admin(self.master) ++ self.master.run_command(['ipa', 'sudorule-del', 'sudorule']) ++ self.master.run_command(["ipa", "sudocmd-del", "/usr/bin/yum"]) ++ ++ def test_auth_replica(self): ++ """ ++ Test case to check that OAuth 2.0 Device ++ Authorization is working as expected on replica. ++ """ ++ tasks.clear_sssd_cache(self.master) ++ tasks.clear_sssd_cache(self.replica) ++ tasks.kinit_admin(self.replica) ++ list_user = self.master.run_command( ++ ["ipa", "user-find", "--idp-user-id=testuser1@ipa.test"] ++ ) ++ assert "keycloakuser" in list_user.stdout_text ++ list_by_idp = self.replica.run_command(["ipa", "user-find", ++ "--idp=keycloakidp"] ++ ) ++ assert "keycloakuser" in list_by_idp.stdout_text ++ list_by_user = self.replica.run_command( ++ ["ipa", "user-find", "--idp-user-id=testuser1@ipa.test", "--all"] ++ ) ++ assert "keycloakidp" in list_by_user.stdout_text ++ kinit_idp(self.replica, 'keycloakuser', keycloak_server=self.client) ++ ++ def test_idp_with_services(self): ++ """ ++ Test case to check that services can be configured ++ auth indicator as idp. ++ """ + tasks.clear_sssd_cache(self.master) +- kinit_idp(self.master, 'keycloakuser', keycloak_srv) ++ tasks.kinit_admin(self.master) ++ domain = self.master.domain.name.upper() ++ services = [ ++ "DNS/{0}@{1}".format(self.master.hostname, domain), ++ "HTTP/{0}@{1}".format(self.client.hostname, domain), ++ "dogtag/{0}@{1}".format(self.master.hostname, domain), ++ "ipa-dnskeysyncd/{0}@{1}".format(self.master.hostname, domain) ++ ] ++ try: ++ for service in services: ++ test = self.master.run_command(["ipa", "service-mod", service, ++ "--auth-ind=idp"] ++ ) ++ assert "Authentication Indicators: idp" in test.stdout_text ++ finally: ++ for service in services: ++ self.master.run_command(["ipa", "service-mod", service, ++ "--auth-ind="]) ++ ++ def test_idp_backup_restore(self): ++ """ ++ Test case to check that after restore data is retrieved ++ with related idp configuration. ++ """ ++ tasks.kinit_admin(self.master) ++ user = "backupuser" ++ cmd = ["ipa", "idp-add", "testidp", "--provider=keycloak", ++ "--client-id=ipa_oidc_client", "--org=master", ++ "--base-url={0}:8443/auth".format(self.client.hostname)] ++ self.master.run_command(cmd, stdin_text="{0}\n{0}".format( ++ self.client.config.admin_password)) ++ ++ tasks.user_add(self.master, user, ++ extra_args=["--user-auth-type=idp", ++ "--idp-user-id=testuser1@ipa.test", ++ "--idp=testidp"] ++ ) ++ ++ backup_path = tasks.get_backup_dir(self.master) ++ # change data after backup ++ self.master.run_command(['ipa', 'user-del', user]) ++ self.master.run_command(['ipa', 'idp-del', 'testidp']) ++ dirman_password = self.master.config.dirman_password ++ self.master.run_command(['ipa-restore', backup_path], ++ stdin_text=dirman_password + '\nyes') ++ try: ++ list_user = self.master.run_command( ++ ['ipa', 'user-show', 'backupuser', '--all'] ++ ).stdout_text ++ assert "External IdP configuration: testidp" in list_user ++ assert "User authentication types: idp" in list_user ++ assert ("External IdP user identifier: " ++ "testuser1@ipa.test") in list_user ++ list_idp = self.master.run_command(['ipa', 'idp-find', 'testidp']) ++ assert "testidp" in list_idp.stdout_text ++ kinit_idp(self.master, user, self.client) ++ finally: ++ tasks.kdestroy_all(self.master) ++ tasks.kinit_admin(self.master) ++ self.master.run_command(["rm", "-rf", backup_path]) ++ self.master.run_command(["ipa", "idp-del", "testidp"]) +-- +2.36.1 + diff --git a/SOURCES/0006-webui-Do-not-allow-empty-pagination-size_rhbz#2094672.patch b/SOURCES/0006-webui-Do-not-allow-empty-pagination-size_rhbz#2094672.patch new file mode 100644 index 0000000..25e9f72 --- /dev/null +++ b/SOURCES/0006-webui-Do-not-allow-empty-pagination-size_rhbz#2094672.patch @@ -0,0 +1,67 @@ +From 991849cf58fa990ad4540a61214b5ab4fcd4baa1 Mon Sep 17 00:00:00 2001 +From: Armando Neto +Date: Fri, 8 Jul 2022 15:56:31 -0300 +Subject: [PATCH] webui: Do not allow empty pagination size + +Pagination size must be required, the current validators are triggered after +form is submitted, thus the only way for check if data is not empty is by making +the field required. + +Fixes: https://pagure.io/freeipa/issue/9192 + +Signed-off-by: Armando Neto +Reviewed-By: Florence Blanc-Renaud +--- + .../ui/src/freeipa/Application_controller.js | 1 + + ipatests/test_webui/test_misc_cases.py | 19 +++++++++++++++++++ + 2 files changed, 20 insertions(+) + +diff --git a/install/ui/src/freeipa/Application_controller.js b/install/ui/src/freeipa/Application_controller.js +index 46aabc9c4..140ee8fe0 100644 +--- a/install/ui/src/freeipa/Application_controller.js ++++ b/install/ui/src/freeipa/Application_controller.js +@@ -318,6 +318,7 @@ define([ + $type: 'text', + name: 'pagination_size', + label: '@i18n:customization.table_pagination', ++ required: true, + validators: ['positive_integer'] + } + ] +diff --git a/ipatests/test_webui/test_misc_cases.py b/ipatests/test_webui/test_misc_cases.py +index 5f7ffb54e..aca9e1a99 100644 +--- a/ipatests/test_webui/test_misc_cases.py ++++ b/ipatests/test_webui/test_misc_cases.py +@@ -11,6 +11,11 @@ from ipatests.test_webui.ui_driver import screenshot + import pytest + import re + ++try: ++ from selenium.webdriver.common.by import By ++except ImportError: ++ pass ++ + + @pytest.mark.tier1 + class TestMiscCases(UI_driver): +@@ -26,3 +31,17 @@ class TestMiscCases(UI_driver): + ver_re = re.compile('version: .*') + assert re.search(ver_re, about_text), 'Version not found' + self.dialog_button_click('ok') ++ ++ @screenshot ++ def test_customization_pagination_input_required(self): ++ """Test if 'pagination size' is required when submitting the form.""" ++ self.init_app() ++ ++ self.profile_menu_action('configuration') ++ self.fill_input('pagination_size', '') ++ self.dialog_button_click('save') ++ ++ pagination_size_elem = self.find( ++ ".widget[name='pagination_size']", By.CSS_SELECTOR) ++ ++ self.assert_field_validation_required(parent=pagination_size_elem) +-- +2.36.1 + diff --git a/SOURCES/0007-webui-Allow-grace-login-limit_rhbz#2109243.patch b/SOURCES/0007-webui-Allow-grace-login-limit_rhbz#2109243.patch new file mode 100644 index 0000000..93cfab9 --- /dev/null +++ b/SOURCES/0007-webui-Allow-grace-login-limit_rhbz#2109243.patch @@ -0,0 +1,56 @@ +From ade5093b08f92b279c200f341e96972a74f644d8 Mon Sep 17 00:00:00 2001 +From: Carla Martinez +Date: Fri, 29 Jul 2022 13:16:16 +0200 +Subject: [PATCH] webui: Allow grace login limit + +There was no support for setting the grace login limit on the WebUI. The +only way to so was only via CLI: + + `ipa pwpolicy-mod --gracelimit=2 global_policy` + +Thus, the grace login limit must be updated from the policy section and +this will reflect also on the user settings (under the 'Password Policy' +section) + +Fixes: https://pagure.io/freeipa/issue/9211 + +Signed-off-by: Carla Martinez +Reviewed-By: Florence Blanc-Renaud +--- + install/ui/src/freeipa/policy.js | 3 +++ + install/ui/src/freeipa/user.js | 5 +++++ + 2 files changed, 8 insertions(+) + +diff --git a/install/ui/src/freeipa/policy.js b/install/ui/src/freeipa/policy.js +index fa2028a52..7ec103636 100644 +--- a/install/ui/src/freeipa/policy.js ++++ b/install/ui/src/freeipa/policy.js +@@ -72,6 +72,9 @@ return { + { + name: 'cospriority', + required: true ++ }, ++ { ++ name: 'passwordgracelimit' + } + ] + }] +diff --git a/install/ui/src/freeipa/user.js b/install/ui/src/freeipa/user.js +index a580db035..b47c97f72 100644 +--- a/install/ui/src/freeipa/user.js ++++ b/install/ui/src/freeipa/user.js +@@ -318,6 +318,11 @@ return { + label: '@mo-param:pwpolicy:krbpwdlockoutduration:label', + read_only: true, + measurement_unit: 'seconds' ++ }, ++ { ++ name: 'passwordgracelimit', ++ label: '@mo-param:pwpolicy:passwordgracelimit:label', ++ read_only: true + } + ] + }, +-- +2.37.2 + diff --git a/SOURCES/0008-check_repl_update-in-progress-is-a-boolean_rhbz#2117303.patch b/SOURCES/0008-check_repl_update-in-progress-is-a-boolean_rhbz#2117303.patch new file mode 100644 index 0000000..36629da --- /dev/null +++ b/SOURCES/0008-check_repl_update-in-progress-is-a-boolean_rhbz#2117303.patch @@ -0,0 +1,35 @@ +From 05a298f56485222583cb7dd4f6a3a4c5c77fc8cf Mon Sep 17 00:00:00 2001 +From: Florence Blanc-Renaud +Date: Sun, 7 Aug 2022 12:44:47 +0200 +Subject: [PATCH] check_repl_update: in progress is a boolean + +With the fix for https://pagure.io/freeipa/issue/9171, +nsds5replicaUpdateInProgress is now handled as a boolean. +One remaining occurrence was still handling it as a string +and calling lower() on its value. + +Replace with direct boolean comparison. + +Fixes: https://pagure.io/freeipa/issue/9218 +Signed-off-by: Florence Blanc-Renaud +Reviewed-By: Alexander Bokovoy +--- + ipaserver/install/replication.py | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/ipaserver/install/replication.py b/ipaserver/install/replication.py +index 16be3760c..9d9aa1c4b 100644 +--- a/ipaserver/install/replication.py ++++ b/ipaserver/install/replication.py +@@ -1152,7 +1152,7 @@ class ReplicationManager: + except (ValueError, TypeError, KeyError): + end = 0 + # incremental update is done if inprogress is false and end >= start +- done = inprogress and inprogress.lower() == 'false' and start <= end ++ done = inprogress is not None and not inprogress and start <= end + logger.info("Replication Update in progress: %s: status: %s: " + "start: %d: end: %d", + inprogress, status, start, end) +-- +2.37.2 + diff --git a/SOURCES/0009-Disabling-gracelimit-does-not-prevent-LDAP-binds_rhbz#2109236.patch b/SOURCES/0009-Disabling-gracelimit-does-not-prevent-LDAP-binds_rhbz#2109236.patch new file mode 100644 index 0000000..17088cf --- /dev/null +++ b/SOURCES/0009-Disabling-gracelimit-does-not-prevent-LDAP-binds_rhbz#2109236.patch @@ -0,0 +1,125 @@ +From 1316cd8b2252c2543cf2ef2186956a8833037b1e Mon Sep 17 00:00:00 2001 +From: Rob Crittenden +Date: Thu, 21 Jul 2022 09:28:46 -0400 +Subject: [PATCH] Disabling gracelimit does not prevent LDAP binds + +Originally the code treated 0 as disabled. This was +changed during the review process to -1 but one remnant +was missed effetively allowing gracelimit 0 to also mean +disabled. + +Add explicit tests for testing with gracelimit = 0 and +gracelimit = -1. + +Also remove some extranous "str(self.master.domain.basedn)" +lines from some of the tests. + +Fixes: https://pagure.io/freeipa/issue/9206 + +Signed-off-by: Rob Crittenden +Reviewed-By: Francisco Trivino +--- + .../ipa-graceperiod/ipa_graceperiod.c | 2 +- + ipatests/test_integration/test_pwpolicy.py | 55 ++++++++++++++++++- + 2 files changed, 53 insertions(+), 4 deletions(-) + +diff --git a/daemons/ipa-slapi-plugins/ipa-graceperiod/ipa_graceperiod.c b/daemons/ipa-slapi-plugins/ipa-graceperiod/ipa_graceperiod.c +index a3f57cb4b..345e1dee7 100644 +--- a/daemons/ipa-slapi-plugins/ipa-graceperiod/ipa_graceperiod.c ++++ b/daemons/ipa-slapi-plugins/ipa-graceperiod/ipa_graceperiod.c +@@ -479,7 +479,7 @@ static int ipagraceperiod_preop(Slapi_PBlock *pb) + if (pwresponse_requested) { + slapi_pwpolicy_make_response_control(pb, -1, grace_limit - grace_user_time , -1); + } +- } else if ((grace_limit > 0) && (grace_user_time >= grace_limit)) { ++ } else if (grace_user_time >= grace_limit) { + LOG_TRACE("%s password is expired and out of grace limit\n", dn); + errstr = "Password is expired.\n"; + ret = LDAP_INVALID_CREDENTIALS; +diff --git a/ipatests/test_integration/test_pwpolicy.py b/ipatests/test_integration/test_pwpolicy.py +index 6d6698284..41d6e9070 100644 +--- a/ipatests/test_integration/test_pwpolicy.py ++++ b/ipatests/test_integration/test_pwpolicy.py +@@ -36,7 +36,7 @@ class TestPWPolicy(IntegrationTest): + cls.master.run_command(['ipa', 'group-add-member', POLICY, + '--users', USER]) + cls.master.run_command(['ipa', 'pwpolicy-add', POLICY, +- '--priority', '1']) ++ '--priority', '1', '--gracelimit', '-1']) + cls.master.run_command(['ipa', 'passwd', USER], + stdin_text='{password}\n{password}\n'.format( + password=PASSWORD +@@ -265,7 +265,6 @@ class TestPWPolicy(IntegrationTest): + + def test_graceperiod_expired(self): + """Test the LDAP bind grace period""" +- str(self.master.domain.basedn) + dn = "uid={user},cn=users,cn=accounts,{base_dn}".format( + user=USER, base_dn=str(self.master.domain.basedn)) + +@@ -308,7 +307,6 @@ class TestPWPolicy(IntegrationTest): + + def test_graceperiod_not_replicated(self): + """Test that the grace period is reset on password reset""" +- str(self.master.domain.basedn) + dn = "uid={user},cn=users,cn=accounts,{base_dn}".format( + user=USER, base_dn=str(self.master.domain.basedn)) + +@@ -341,3 +339,54 @@ class TestPWPolicy(IntegrationTest): + ) + assert 'passwordgraceusertime: 0' in result.stdout_text.lower() + self.reset_password(self.master) ++ ++ def test_graceperiod_zero(self): ++ """Test the LDAP bind with zero grace period""" ++ dn = "uid={user},cn=users,cn=accounts,{base_dn}".format( ++ user=USER, base_dn=str(self.master.domain.basedn)) ++ ++ self.master.run_command( ++ ["ipa", "pwpolicy-mod", POLICY, "--gracelimit", "0", ], ++ ) ++ ++ # Resetting the password will mark it as expired ++ self.reset_password(self.master) ++ ++ # Now grace is done and binds should fail. ++ result = self.master.run_command( ++ ["ldapsearch", "-e", "ppolicy", "-D", dn, ++ "-w", PASSWORD, "-b", dn], raiseonerr=False ++ ) ++ assert result.returncode == 49 ++ ++ assert 'Password is expired' in result.stderr_text ++ assert 'Password expired, 0 grace logins remain' in result.stderr_text ++ ++ def test_graceperiod_disabled(self): ++ """Test the LDAP bind with grace period disabled (-1)""" ++ str(self.master.domain.basedn) ++ dn = "uid={user},cn=users,cn=accounts,{base_dn}".format( ++ user=USER, base_dn=str(self.master.domain.basedn)) ++ ++ # This can fail if gracelimit is already -1 so ignore it ++ self.master.run_command( ++ ["ipa", "pwpolicy-mod", POLICY, "--gracelimit", "-1",], ++ raiseonerr=False, ++ ) ++ ++ # Ensure the password is expired ++ self.reset_password(self.master) ++ ++ result = self.kinit_as_user(self.master, PASSWORD, PASSWORD) ++ ++ for _i in range(0, 10): ++ result = self.master.run_command( ++ ["ldapsearch", "-e", "ppolicy", "-D", dn, ++ "-w", PASSWORD, "-b", dn] ++ ) ++ ++ # With graceperiod disabled it should not increment ++ result = tasks.ldapsearch_dm( ++ self.master, dn, ['passwordgraceusertime',], ++ ) ++ assert 'passwordgraceusertime: 0' in result.stdout_text.lower() +-- +2.37.2 + diff --git a/SOURCES/0010-Set-passwordgracelimit-to-match-global-policy-on-group-pw-policies_rhbz#2115475.patch b/SOURCES/0010-Set-passwordgracelimit-to-match-global-policy-on-group-pw-policies_rhbz#2115475.patch new file mode 100644 index 0000000..952e49b --- /dev/null +++ b/SOURCES/0010-Set-passwordgracelimit-to-match-global-policy-on-group-pw-policies_rhbz#2115475.patch @@ -0,0 +1,230 @@ +From 434620ee342ac4767beccec647a318bfa7743dfa Mon Sep 17 00:00:00 2001 +From: Rob Crittenden +Date: Thu, 18 Aug 2022 08:21:58 -0400 +Subject: [PATCH] doc: Update LDAP grace period design with default values + +New group password policies will get -1 (unlimited) on creation +by default. + +Existing group password policies will remain untouched and +those created prior will be treated as no BIND allowed. + +Fixes: https://pagure.io/freeipa/issue/9212 + +Signed-off-by: Rob Crittenden +Reviewed-By: Florence Blanc-Renaud +--- + doc/designs/ldap_grace_period.md | 17 ++++++++++++++++- + 1 file changed, 16 insertions(+), 1 deletion(-) + +diff --git a/doc/designs/ldap_grace_period.md b/doc/designs/ldap_grace_period.md +index 4b9db3424..e26aedda9 100644 +--- a/doc/designs/ldap_grace_period.md ++++ b/doc/designs/ldap_grace_period.md +@@ -51,7 +51,22 @@ The basic flow is: + + On successful password reset (by anyone) reset the user's passwordGraceUserTime to 0. + +-The default value on install/upgrade will be -1 to retail existing behavior. ++Range values for passwordgracelimit are: ++ ++-1 : password grace checking is disabled ++ 0 : no grace BIND are allowed at all post-expiration ++ 1..MAXINT: the number of BIND allowed post-expiration ++ ++The default value for the global policy on install/upgrade will be -1 to ++retain existing behavior. ++ ++New group password policies will default to -1 to retain previous ++behavior. ++ ++Existing group policies with no grace limit set are updated to use ++the default unlimited value, -1. This is done because lack of value in ++LDAP is treated as 0 so any existing group policies would not allow ++post-expiration BIND so this will avoid confusion. + + The per-user attempts will not be replicated. + +-- +2.37.2 + +From 497a57e7a6872fa30d1855a1d91a455bfdbf9300 Mon Sep 17 00:00:00 2001 +From: Rob Crittenden +Date: Thu, 4 Aug 2022 12:04:22 -0400 +Subject: [PATCH] Set default gracelimit on group password policies to -1 + +This will retain previous behavior of unlimited LDAP BIND +post-expiration. + +Fixes: https://pagure.io/freeipa/issue/9212 + +Signed-off-by: Rob Crittenden +Reviewed-By: Florence Blanc-Renaud +--- + API.txt | 2 +- + ipaserver/plugins/pwpolicy.py | 2 ++ + ipatests/test_xmlrpc/test_pwpolicy_plugin.py | 2 ++ + 3 files changed, 5 insertions(+), 1 deletion(-) + +diff --git a/API.txt b/API.txt +index 5ba9add13..d7ea74f08 100644 +--- a/API.txt ++++ b/API.txt +@@ -4075,7 +4075,7 @@ option: Int('krbpwdlockoutduration?', cli_name='lockouttime') + option: Int('krbpwdmaxfailure?', cli_name='maxfail') + option: Int('krbpwdmindiffchars?', cli_name='minclasses') + option: Int('krbpwdminlength?', cli_name='minlength') +-option: Int('passwordgracelimit?', cli_name='gracelimit', default=-1) ++option: Int('passwordgracelimit?', autofill=True, cli_name='gracelimit', default=-1) + option: Flag('raw', autofill=True, cli_name='raw', default=False) + option: Str('setattr*', cli_name='setattr') + option: Str('version?') +diff --git a/ipaserver/plugins/pwpolicy.py b/ipaserver/plugins/pwpolicy.py +index 4428aede2..f4ebffd5c 100644 +--- a/ipaserver/plugins/pwpolicy.py ++++ b/ipaserver/plugins/pwpolicy.py +@@ -408,6 +408,7 @@ class pwpolicy(LDAPObject): + minvalue=-1, + maxvalue=Int.MAX_UINT32, + default=-1, ++ autofill=True, + ), + ) + +@@ -539,6 +540,7 @@ class pwpolicy_add(LDAPCreate): + keys[-1], krbpwdpolicyreference=dn, + cospriority=options.get('cospriority') + ) ++ + return dn + + def post_callback(self, ldap, dn, entry_attrs, *keys, **options): +diff --git a/ipatests/test_xmlrpc/test_pwpolicy_plugin.py b/ipatests/test_xmlrpc/test_pwpolicy_plugin.py +index 8eee69c18..fc785223b 100644 +--- a/ipatests/test_xmlrpc/test_pwpolicy_plugin.py ++++ b/ipatests/test_xmlrpc/test_pwpolicy_plugin.py +@@ -387,6 +387,7 @@ class test_pwpolicy_mod_cospriority(Declarative): + krbpwdhistorylength=[u'10'], + krbpwdmindiffchars=[u'3'], + krbpwdminlength=[u'8'], ++ passwordgracelimit=[u'-1'], + objectclass=objectclasses.pwpolicy, + ), + summary=None, +@@ -417,6 +418,7 @@ class test_pwpolicy_mod_cospriority(Declarative): + krbpwdhistorylength=[u'10'], + krbpwdmindiffchars=[u'3'], + krbpwdminlength=[u'8'], ++ passwordgracelimit=[u'-1'], + ), + summary=None, + value=u'ipausers', +-- +2.37.2 + +From a4ddaaf3048c4e8d78a1807af7266ee40ab3a30b Mon Sep 17 00:00:00 2001 +From: Rob Crittenden +Date: Thu, 4 Aug 2022 12:04:41 -0400 +Subject: [PATCH] Set default on group pwpolicy with no grace limit in upgrade + +If an existing group policy lacks a password grace limit +update it to -1 on upgrade. + +Fixes: https://pagure.io/freeipa/issue/9212 + +Signed-off-by: Rob Crittenden +Reviewed-By: Florence Blanc-Renaud +--- + .../updates/90-post_upgrade_plugins.update | 1 + + ipaserver/install/plugins/update_pwpolicy.py | 66 +++++++++++++++++++ + 2 files changed, 67 insertions(+) + +diff --git a/install/updates/90-post_upgrade_plugins.update b/install/updates/90-post_upgrade_plugins.update +index c7ec71d49..6fe91aa6c 100644 +--- a/install/updates/90-post_upgrade_plugins.update ++++ b/install/updates/90-post_upgrade_plugins.update +@@ -26,6 +26,7 @@ plugin: update_ra_cert_store + plugin: update_mapping_Guests_to_nobody + plugin: fix_kra_people_entry + plugin: update_pwpolicy ++plugin: update_pwpolicy_grace + + # last + # DNS version 1 +diff --git a/ipaserver/install/plugins/update_pwpolicy.py b/ipaserver/install/plugins/update_pwpolicy.py +index dca44ce43..4185f0343 100644 +--- a/ipaserver/install/plugins/update_pwpolicy.py ++++ b/ipaserver/install/plugins/update_pwpolicy.py +@@ -78,3 +78,69 @@ class update_pwpolicy(Updater): + return False, [] + + return False, [] ++ ++ ++@register() ++class update_pwpolicy_grace(Updater): ++ """ ++ Ensure all group policies have a grace period set. ++ """ ++ ++ def execute(self, **options): ++ ldap = self.api.Backend.ldap2 ++ ++ base_dn = DN(('cn', self.api.env.realm), ('cn', 'kerberos'), ++ self.api.env.basedn) ++ search_filter = ( ++ "(&(objectClass=krbpwdpolicy)(!(passwordgracelimit=*)))" ++ ) ++ ++ while True: ++ # Run the search in loop to avoid issues when LDAP limits are hit ++ # during update ++ ++ try: ++ (entries, truncated) = ldap.find_entries( ++ search_filter, ['objectclass'], base_dn, time_limit=0, ++ size_limit=0) ++ ++ except errors.EmptyResult: ++ logger.debug("update_pwpolicy: no policies without " ++ "passwordgracelimit set") ++ return False, [] ++ ++ except errors.ExecutionError as e: ++ logger.error("update_pwpolicy: cannot retrieve list " ++ "of policies missing passwordgracelimit: %s", e) ++ return False, [] ++ ++ logger.debug("update_pwpolicy: found %d " ++ "policies to update, truncated: %s", ++ len(entries), truncated) ++ ++ error = False ++ ++ for entry in entries: ++ # Set unlimited BIND by default ++ entry['passwordgracelimit'] = -1 ++ try: ++ ldap.update_entry(entry) ++ except (errors.EmptyModlist, errors.NotFound): ++ pass ++ except errors.ExecutionError as e: ++ logger.debug("update_pwpolicy: cannot " ++ "update policy: %s", e) ++ error = True ++ ++ if error: ++ # Exit loop to avoid infinite cycles ++ logger.error("update_pwpolicy: error(s) " ++ "detected during pwpolicy update") ++ return False, [] ++ ++ elif not truncated: ++ # All affected entries updated, exit the loop ++ logger.debug("update_pwpolicy: all policies updated") ++ return False, [] ++ ++ return False, [] +-- +2.37.2 + diff --git a/SPECS/ipa.spec b/SPECS/ipa.spec index d53f198..af97919 100644 --- a/SPECS/ipa.spec +++ b/SPECS/ipa.spec @@ -68,8 +68,7 @@ %global krb5_kdb_version 8.0 # 0.7.16: https://github.com/drkjam/netaddr/issues/71 %global python_netaddr_version 0.7.19 -# Require 4.14.5-13 which brings CVE-2020-25717 fixes -%global samba_version 4.14.5-13 +%global samba_version 4.17.2-1 %global selinux_policy_version 3.14.3-52 %global slapi_nis_version 0.56.4 %global python_ldap_version 3.1.0-1 @@ -93,8 +92,7 @@ # 0.7.16: https://github.com/drkjam/netaddr/issues/71 %global python_netaddr_version 0.7.16 -# Require 4.14.6 which brings CVE-2020-25717 fixes -%global samba_version 2:4.14.6 +%global samba_version 2:4.17.2 # 3.14.5-45 or later includes a number of interfaces fixes for IPA interface %global selinux_policy_version 3.14.5-45 @@ -191,7 +189,7 @@ Name: %{package_name} Version: %{IPA_VERSION} -Release: 3%{?rc_version:.%rc_version}%{?dist} +Release: 7%{?rc_version:.%rc_version}%{?dist} Summary: The Identity, Policy and Audit system License: GPLv3+ @@ -215,6 +213,12 @@ Patch0001: 0001-ipa-otpd-Fix-build-on-older-versions-of-gcc.patch Patch0002: 0002-webui-IdP-Remove-arrow-notation-due-to-uglify-js-lim.patch Patch0003: 0003-Preserve-user-fix-the-confusing-summary_rhbz#2022028.patch Patch0004: 0004-Only-calculate-LDAP-password-grace-when-the-password_rhbz#782917.patch +Patch0005: 0005-Add-end-to-end-integration-tests-for-external-IdP.patch +Patch0006: 0006-webui-Do-not-allow-empty-pagination-size_rhbz#2094672.patch +Patch0007: 0007-webui-Allow-grace-login-limit_rhbz#2109243.patch +Patch0008: 0008-check_repl_update-in-progress-is-a-boolean_rhbz#2117303.patch +Patch0009: 0009-Disabling-gracelimit-does-not-prevent-LDAP-binds_rhbz#2109236.patch +Patch0010: 0010-Set-passwordgracelimit-to-match-global-policy-on-group-pw-policies_rhbz#2115475.patch Patch1001: 1001-Change-branding-to-IPA-and-Identity-Management.patch Patch1002: 1002-Revert-freeipa.spec-depend-on-bind-dnssec-utils.patch %endif @@ -1717,6 +1721,28 @@ fi %endif %changelog +* Tue Nov 1 2022 Rafael Jeffman - 4.9.10-7 +- Rebuild to samba 4.17.2. + Related: RHBZ#2132051 + +* Mon Aug 22 2022 Rafael Jeffman - 4.9.10-6 +- webui: Allow grace login limit + Resolves: RHBZ#2109243 +- check_repl_update: in progress is a boolean + Resolves: RHBZ#2117303 +- Disabling gracelimit does not prevent LDAP binds + Resolves: RHBZ#2109236 +- Set passwordgracelimit to match global policy on group pw policies + Resolves: RHBZ#2115475 + +* Tue Jul 19 2022 Rafael Jeffman - 4.9.10-5 +- webui: Do not allow empty pagination size + Resolves: RHBZ#2094672 + +* Tue Jul 12 2022 Rafael Jeffman - 4.9.10-4 +- Add end to end integration tests for external IdP + Resolves: RHBZ#2106346 + * Thu Jul 07 2022 Rafael Jeffman - 4.9.10-3 - Add explicit dependency for libvert-libev Resolves: RHBZ#2104929