diff --git a/0002-Disabling-gracelimit-does-not-prevent-LDAP-binds.patch b/0002-Disabling-gracelimit-does-not-prevent-LDAP-binds.patch new file mode 100644 index 0000000..bd6f518 --- /dev/null +++ b/0002-Disabling-gracelimit-does-not-prevent-LDAP-binds.patch @@ -0,0 +1,125 @@ +From 1bb4ff9ed2313fb3c2bd1418258c5bcec557b6a5 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.1 + diff --git a/0003-webui-Allow-grace-login-limit.patch b/0003-webui-Allow-grace-login-limit.patch new file mode 100644 index 0000000..9083175 --- /dev/null +++ b/0003-webui-Allow-grace-login-limit.patch @@ -0,0 +1,56 @@ +From 7a1e1d9f1cb13679c28f12d05b156a08bcc4d856 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.1 + diff --git a/0004-DNSResolver-Fix-use-of-nameservers-with-ports.patch b/0004-DNSResolver-Fix-use-of-nameservers-with-ports.patch new file mode 100644 index 0000000..b75e79d --- /dev/null +++ b/0004-DNSResolver-Fix-use-of-nameservers-with-ports.patch @@ -0,0 +1,137 @@ +From 77803587d6e15b41a66fc0ee0a87ad55ee196dfe Mon Sep 17 00:00:00 2001 +From: Thomas Woerner +Date: Wed, 3 Aug 2022 18:22:47 +0200 +Subject: [PATCH] DNSResolver: Fix use of nameservers with ports + +IPA DNS zone and forwardzone commands allow to use nameservers with ports +as "SERVER_IP port PORT_NUMBER". bind is supporting this syntax, but the +Resolver in dnspython that is used to verify the list of forwarders +(nameservers) is only allowing to have IP addresses in this list. With +dnspython version 2.20 there is a new validator in dns.resolver.BaseResolver +that ensures this. + +Refs: +- https://bind9.readthedocs.io/en/v9_18_4/reference.html#zone-statement-grammar +- https://github.com/rthalley/dnspython/blob/master/dns/resolver.py#L1094 + +ipapython/dnsutil.DNSResolver derives from dns.resolver.Resolver. The setter +for nameservers has been overloaded in the DNSResolver class to split out +the port numbers into the nameserver_ports dict { SERVER_IP: PORT_NUMBER }. +After the setter for nameservers succeeded, nameserver_ports is set. +nameserver_ports is used in the resolve() method of dns.resolver.Resolver. + +Additional tests have been added to verify that nameservers and also +nameserver_ports are properly set and also valid. + +Fixes: https://pagure.io/freeipa/issue/9158 + +Signed-off-by: Thomas Woerner +Reviewed-By: Alexander Bokovoy +--- + ipapython/dnsutil.py | 41 +++++++++++++++++++++++++ + ipatests/test_ipapython/test_dnsutil.py | 40 ++++++++++++++++++++++++ + 2 files changed, 81 insertions(+) + +diff --git a/ipapython/dnsutil.py b/ipapython/dnsutil.py +index 4baeaf8cc..58de365ab 100644 +--- a/ipapython/dnsutil.py ++++ b/ipapython/dnsutil.py +@@ -144,6 +144,47 @@ class DNSResolver(dns.resolver.Resolver): + nameservers.remove(ipv4_loopback) + self.nameservers = nameservers + ++ @dns.resolver.Resolver.nameservers.setter ++ def nameservers(self, nameservers): ++ """ ++ *nameservers*, a ``list`` of nameservers with optional ports: ++ "SERVER_IP port PORT_NUMBER". ++ ++ Overloads dns.resolver.Resolver.nameservers setter to split off ports ++ into nameserver_ports after setting nameservers successfully with the ++ setter in dns.resolver.Resolver. ++ """ ++ # Get nameserver_ports if it is already set ++ if hasattr(self, "nameserver_ports"): ++ nameserver_ports = self.nameserver_ports ++ else: ++ nameserver_ports = {} ++ ++ # Check nameserver items in list and split out converted port number ++ # into nameserver_ports: { nameserver: port } ++ if isinstance(nameservers, list): ++ _nameservers = [] ++ for nameserver in nameservers: ++ splits = nameserver.split() ++ if len(splits) == 3 and splits[1] == "port": ++ nameserver = splits[0] ++ try: ++ port = int(splits[2]) ++ if port < 0 or port > 65535: ++ raise ValueError() ++ except ValueError: ++ raise ValueError( ++ "invalid nameserver: %s is not a valid port" % ++ splits[2]) ++ nameserver_ports[nameserver] = port ++ _nameservers.append(nameserver) ++ nameservers = _nameservers ++ ++ # Call dns.resolver.Resolver.nameservers setter ++ dns.resolver.Resolver.nameservers.__set__(self, nameservers) ++ # Set nameserver_ports after successfull call to setter ++ self.nameserver_ports = nameserver_ports ++ + + class DNSZoneAlreadyExists(dns.exception.DNSException): + supp_kwargs = {'zone', 'ns'} +diff --git a/ipatests/test_ipapython/test_dnsutil.py b/ipatests/test_ipapython/test_dnsutil.py +index 5e7a46197..09463c69d 100644 +--- a/ipatests/test_ipapython/test_dnsutil.py ++++ b/ipatests/test_ipapython/test_dnsutil.py +@@ -101,3 +101,43 @@ class TestSortURI: + assert dnsutil.sort_prio_weight([h3, h2, h1]) == [h1, h2, h3] + assert dnsutil.sort_prio_weight([h3, h3, h3]) == [h3] + assert dnsutil.sort_prio_weight([h2, h2, h1, h1]) == [h1, h2] ++ ++ ++class TestDNSResolver: ++ def test_nameservers(self): ++ res = dnsutil.DNSResolver() ++ res.nameservers = ["4.4.4.4", "8.8.8.8"] ++ assert res.nameservers == ["4.4.4.4", "8.8.8.8"] ++ ++ def test_nameservers_with_ports(self): ++ res = dnsutil.DNSResolver() ++ res.nameservers = ["4.4.4.4 port 53", "8.8.8.8 port 8053"] ++ assert res.nameservers == ["4.4.4.4", "8.8.8.8"] ++ assert res.nameserver_ports == {"4.4.4.4": 53, "8.8.8.8": 8053} ++ ++ res.nameservers = ["4.4.4.4 port 53", "8.8.8.8 port 8053"] ++ assert res.nameservers == ["4.4.4.4", "8.8.8.8"] ++ assert res.nameserver_ports == {"4.4.4.4": 53, "8.8.8.8": 8053} ++ ++ def test_nameservers_with_bad_ports(self): ++ res = dnsutil.DNSResolver() ++ try: ++ res.nameservers = ["4.4.4.4 port a"] ++ except ValueError: ++ pass ++ else: ++ pytest.fail("No fail on bad port a") ++ ++ try: ++ res.nameservers = ["4.4.4.4 port -1"] ++ except ValueError: ++ pass ++ else: ++ pytest.fail("No fail on bad port -1") ++ ++ try: ++ res.nameservers = ["4.4.4.4 port 65536"] ++ except ValueError: ++ pass ++ else: ++ pytest.fail("No fail on bad port 65536") +-- +2.37.1 + diff --git a/0005_Set_passwordgracelimit_to_match_global_policy_on_group_pw_policies_#6419.patch b/0005_Set_passwordgracelimit_to_match_global_policy_on_group_pw_policies_#6419.patch new file mode 100644 index 0000000..efb82ee --- /dev/null +++ b/0005_Set_passwordgracelimit_to_match_global_policy_on_group_pw_policies_#6419.patch @@ -0,0 +1,231 @@ +From 1aa39529cda4ab9620539dbad705cedd23c21b42 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.1 + +From 45e6d49b94da78cd82eb016b3266a17a1359a087 Mon Sep 17 00:00:00 2001 +From: Rob Crittenden +Date: Thu, 4 Aug 2022 12:04:22 -0400 +Subject: [PATCH 1/2] 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 66929b921..210bfc495 100644 +--- a/API.txt ++++ b/API.txt +@@ -4076,7 +4076,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.1 + +From de6f074538f6641fd9d84bed204a3d4d50eccbe5 Mon Sep 17 00:00:00 2001 +From: Rob Crittenden +Date: Thu, 4 Aug 2022 12:04:41 -0400 +Subject: [PATCH 2/2] 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.1 + diff --git a/freeipa.spec b/freeipa.spec index a74bef9..34174fd 100644 --- a/freeipa.spec +++ b/freeipa.spec @@ -188,7 +188,7 @@ Name: %{package_name} Version: %{IPA_VERSION} -Release: 3%{?rc_version:.%rc_version}%{?dist} +Release: 4%{?rc_version:.%rc_version}%{?dist} Summary: The Identity, Policy and Audit system License: GPLv3+ @@ -206,6 +206,10 @@ Source1: https://releases.pagure.org/freeipa/freeipa-%{version}%{?rc_vers # RHEL spec file only: END: Change branding to IPA and Identity Management Patch0001: 0001-Only-calculate-LDAP-password-grace-when-the-password.patch +Patch0002: 0002-Disabling-gracelimit-does-not-prevent-LDAP-binds.patch +Patch0003: 0003-webui-Allow-grace-login-limit.patch +Patch0004: 0004-DNSResolver-Fix-use-of-nameservers-with-ports.patch +Patch0005: 0005_Set_passwordgracelimit_to_match_global_policy_on_group_pw_policies_#6419.patch # RHEL spec file only: START %if %{NON_DEVELOPER_BUILD} @@ -1718,6 +1722,12 @@ fi %endif %changelog +* Wed Aug 24 2022 Thomas Woerner - 4.10.0-4 +- Disabling gracelimit does not prevent LDAP binds +- webui: Allow grace login limit +- Fix dns resolver for nameservers with ports +- Set passwordgracelimit to match global policy on group pw policies + * Tue Aug 09 2022 Adam Williamson - 4.10.0-3 - Rebuild against new libndr