ipa-4.12.2-4

- Related: RHEL-59777 Rebase Samba to the latest 4.21.x release
- Resolves: RHEL-59659 ipa dns-zone --allow-query '!198.18.2.0/24;any;' fails with Unrecognized IPAddress flags
- Resolves: RHEL-61636 Uninstall ACME separately during PKI uninstallation
- Resolves: RHEL-61723 Include latest fixes in python3-ipatests packages
- Resolves: RHEL-63325 Last expired OTP token would be considered as still assigned to the user

Signed-off-by: Florence Blanc-Renaud <flo@redhat.com>
This commit is contained in:
Florence Blanc-Renaud 2024-10-21 17:39:50 +02:00
parent c94e6ae745
commit 66cc1eaeec
13 changed files with 1352 additions and 4 deletions

View File

@ -0,0 +1,34 @@
From a9e653ca36a0829ae59cd204e7388d7a6c91e082 Mon Sep 17 00:00:00 2001
From: Florence Blanc-Renaud <flo@redhat.com>
Date: Fri, 13 Sep 2024 09:58:36 +0200
Subject: [PATCH] UnsafeIPAddress: pass flag=0 to IPNetwork
When parsing a string, the constructor tries to parse the value
as an IP Address first, or falls back to an IPNetwork with the
flags INET_PTON.
Use the flag 0 instead for an IPNetwork.
Fixes: https://pagure.io/freeipa/issue/9645
Signed-off-by: Florence Blanc-Renaud <flo@redhat.com>
Reviewed-By: Alexander Bokovoy <abokovoy@redhat.com>
---
ipapython/ipautil.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/ipapython/ipautil.py b/ipapython/ipautil.py
index 3e98bfd6a66f24933e7e4de8efb79f4f5bf8bd0e..c237d59fb4b8be4187fb0efb04b097ff4df6c182 100644
--- a/ipapython/ipautil.py
+++ b/ipapython/ipautil.py
@@ -119,7 +119,7 @@ class UnsafeIPAddress(netaddr.IPAddress):
if addr.version != 6:
raise
except ValueError:
- self._net = netaddr.IPNetwork(addr, flags=self.netaddr_ip_flags)
+ self._net = netaddr.IPNetwork(addr, flags=0)
addr = self._net.ip
super(UnsafeIPAddress, self).__init__(addr,
flags=self.netaddr_ip_flags)
--
2.46.2

View File

@ -0,0 +1,67 @@
From a343c149838a3058794f33c75c58b75bc1748f7f Mon Sep 17 00:00:00 2001
From: Florence Blanc-Renaud <flo@redhat.com>
Date: Tue, 17 Sep 2024 17:00:49 +0200
Subject: [PATCH] ipatests: provide a ccache to rpcclient deletetrustdom
With samba update to samba-4.20.4, rpcclient now needs a
ccache otherwise it prompts for a password.
Fixes: https://pagure.io/freeipa/issue/9667
Signed-off-by: Florence Blanc-Renaud <flo@redhat.com>
Reviewed-By: Rob Crittenden <rcritten@redhat.com>
Reviewed-By: Alexander Bokovoy <abokovoy@redhat.com>
---
ipatests/pytest_ipa/integration/tasks.py | 23 ++++++++++++++++++++---
1 file changed, 20 insertions(+), 3 deletions(-)
diff --git a/ipatests/pytest_ipa/integration/tasks.py b/ipatests/pytest_ipa/integration/tasks.py
index 9d6b5f67a311a28c335801d59e0ff0f0c7faccdd..677fb7534256a65940fb5280fa6412789dcba54f 100755
--- a/ipatests/pytest_ipa/integration/tasks.py
+++ b/ipatests/pytest_ipa/integration/tasks.py
@@ -795,15 +795,22 @@ def remove_trust_info_from_ad(master, ad_domain, ad_hostname):
kinit_as_user(master,
'Administrator@{}'.format(ad_domain.upper()),
master.config.ad_admin_password)
+ # Find cache for the user
+ cache_args = []
+ cache = get_credential_cache(master)
+ if cache:
+ cache_args = ["--use-krb5-ccache", cache]
+
# Detect whether rpcclient supports -k or --use-kerberos option
res = master.run_command(['rpcclient', '-h'], raiseonerr=False)
if "--use-kerberos" in res.stderr_text:
rpcclient_krb5_knob = "--use-kerberos=desired"
else:
rpcclient_krb5_knob = "-k"
- master.run_command(['rpcclient', rpcclient_krb5_knob, ad_hostname,
- '-c', 'deletetrustdom {}'.format(master.domain.name)],
- raiseonerr=False)
+ cmd_args = ['rpcclient', rpcclient_krb5_knob, ad_hostname]
+ cmd_args.extend(cache_args)
+ cmd_args.extend(['-c', 'deletetrustdom {}'.format(master.domain.name)])
+ master.run_command(cmd_args, raiseonerr=False)
def configure_auth_to_local_rule(master, ad):
@@ -1086,6 +1093,16 @@ def kinit_admin(host, raiseonerr=True):
raiseonerr=raiseonerr)
+def get_credential_cache(host):
+ # Return the credential cache currently in use on host or None
+ result = host.run_command(["klist"]).stdout_text
+ pattern = re.compile(r'Ticket cache: (?P<cache>.*)\n')
+ res = pattern.search(result)
+ if res:
+ return res['cache']
+ return None
+
+
def uninstall_master(host, ignore_topology_disconnect=True,
ignore_last_of_role=True, clean=True, verbose=False):
uninstall_cmd = ['ipa-server-install', '--uninstall', '-U']
--
2.46.2

View File

@ -0,0 +1,60 @@
From 743c7b46e463bef666dc84e9f513eb7dee7f59f6 Mon Sep 17 00:00:00 2001
From: Florence Blanc-Renaud <flo@redhat.com>
Date: Tue, 17 Sep 2024 14:48:58 +0200
Subject: [PATCH] test_adtrust_install: add --use-krb5-ccache to smbclient
command
With samba 4.20.4 the smbclient commands needs a ccache otherwise it
prompts for a password.
Fixes: https://pagure.io/freeipa/issue/9666
Signed-off-by: Florence Blanc-Renaud <flo@redhat.com>
Reviewed-By: Rob Crittenden <rcritten@redhat.com>
Reviewed-By: Alexander Bokovoy <abokovoy@redhat.com>
---
.../test_integration/test_adtrust_install.py | 28 ++++++++++++-------
1 file changed, 18 insertions(+), 10 deletions(-)
diff --git a/ipatests/test_integration/test_adtrust_install.py b/ipatests/test_integration/test_adtrust_install.py
index de252db1705ad940c3b5ee4df967d7c17a4203a7..79a91dfaa61276de74b10777c6d44b5942ed1be0 100644
--- a/ipatests/test_integration/test_adtrust_install.py
+++ b/ipatests/test_integration/test_adtrust_install.py
@@ -873,17 +873,25 @@ class TestIpaAdTrustInstall(IntegrationTest):
"path", "/freeipa4234"])
self.master.run_command(["touch", "before"])
self.master.run_command(["touch", "after"])
- self.master.run_command(
- ["smbclient", "--use-kerberos=desired",
- "-c=put before", "//{}/share".format(
- self.master.hostname)]
- )
+ # Find cache for the admin user
+ cache_args = []
+ cache = tasks.get_credential_cache(self.master)
+ if cache:
+ cache_args = ["--use-krb5-ccache", cache]
+
+ cmd_args = ["smbclient", "--use-kerberos=desired"]
+ cmd_args.extend(cache_args)
+ cmd_args.extend([
+ "-c=put before", "//{}/share".format(self.master.hostname)
+ ])
+ self.master.run_command(cmd_args)
self.master.run_command(
["net", "conf", "setparm", "share",
"valid users", "@admins"])
- result = self.master.run_command(
- ["smbclient", "--use-kerberos=desired",
- "-c=put after", "//{}/share".format(
- self.master.hostname)]
- )
+ cmd_args = ["smbclient", "--use-kerberos=desired"]
+ cmd_args.extend(cache_args)
+ cmd_args.extend([
+ "-c=put after", "//{}/share".format(self.master.hostname)
+ ])
+ result = self.master.run_command(cmd_args)
assert msg not in result.stdout_text
--
2.46.2

View File

@ -0,0 +1,209 @@
From a785d0c561b8e22bd9d56739481095e07e0a7eb7 Mon Sep 17 00:00:00 2001
From: Rob Crittenden <rcritten@redhat.com>
Date: Mon, 30 Sep 2024 13:30:46 -0400
Subject: [PATCH] Don't rely on removing the CA to uninstall the ACME depoyment
There has always been a pki-server commnd acme-remove. We were
not aware that it should be called prior to removing a CA. In
11.5.0 this is strongly encouraged by the PKI team. In 11.6.0
ACME is treated as a full subsystem so will be removed in the
future using pkidestroy -s ACME
The new class acmeinstance.ACMEInstance is introduced so its
uninstallation can be handled in a similar way as the other
PKI services via DogtagInstance. It is, right now, a pretty
thin wrapper.
We can discuss moving the ACME installation routines here at
some point. It would be ok as long as we don't have to introduce
another PKI restart as part of it.
In PKI 11.6.0 pkidestroy has new options to ensure a clean
uninstall: --remove-conf --remove-logs. Pass those options
into pkidestroy calls for 11.6.0+.
Clean up an additional IPA-generated file that needs to be
cleaned up during uninstall: /root/kracert.p12. 11.6.0 is
more sensitive to leftover files than previous versions.
Fixes: https://pagure.io/freeipa/issue/9673
Fixes: https://pagure.io/freeipa/issue/9674
Signed-off-by: Rob Crittenden <rcritten@redhat.com>
Reviewed-By: Alexander Bokovoy <abokovoy@redhat.com>
---
ipaserver/install/acmeinstance.py | 31 +++++++++++++
ipaserver/install/ca.py | 5 ++-
ipaserver/install/cainstance.py | 1 +
ipaserver/install/dogtaginstance.py | 44 +++++++++++++------
.../test_integration/test_uninstallation.py | 21 +++++++++
5 files changed, 87 insertions(+), 15 deletions(-)
create mode 100644 ipaserver/install/acmeinstance.py
diff --git a/ipaserver/install/acmeinstance.py b/ipaserver/install/acmeinstance.py
new file mode 100644
index 0000000000000000000000000000000000000000..0027c314545f384d9b6ee24b279479e5360d8bef
--- /dev/null
+++ b/ipaserver/install/acmeinstance.py
@@ -0,0 +1,31 @@
+#
+# Copyright (C) 2024 FreeIPA Contributors see COPYING for license
+#
+
+import logging
+
+from ipaserver.install.dogtaginstance import DogtagInstance
+
+logger = logging.getLogger(__name__)
+
+
+class ACMEInstance(DogtagInstance):
+ """
+ ACME is deployed automatically with a CA subsystem but it is the
+ responsibility of IPA to uninstall the service.
+
+ This is mostly a placeholder for the uninstaller. We can
+ eventually move the ACME installation routines into this class
+ if we want but it might result in an extra PKI restart which
+ would be slow.
+ """
+ def __init__(self, realm=None, host_name=None):
+ super(ACMEInstance, self).__init__(
+ realm=realm,
+ subsystem="ACME",
+ service_desc="ACME server",
+ host_name=host_name
+ )
+
+ def uninstall(self):
+ DogtagInstance.uninstall(self)
diff --git a/ipaserver/install/ca.py b/ipaserver/install/ca.py
index ffcb5268399ce71128fc8de5f54d433d35e99dd2..520e3fc5de1084e7c22c0cf7eaa86e1d3c421373 100644
--- a/ipaserver/install/ca.py
+++ b/ipaserver/install/ca.py
@@ -22,7 +22,7 @@ from ipaplatform.constants import constants
from ipaserver.install import sysupgrade
from ipapython.install import typing
from ipapython.install.core import group, knob, extend_knob
-from ipaserver.install import cainstance, bindinstance, dsinstance
+from ipaserver.install import acmeinstance, cainstance, bindinstance, dsinstance
from ipapython import ipautil, certdb
from ipapython import ipaldap
from ipapython.admintool import ScriptError
@@ -715,6 +715,9 @@ def install_step_1(standalone, replica_config, options, custodia):
def uninstall():
+ acme = acmeinstance.ACMEInstance(api.env.realm)
+ acme.uninstall()
+
ca_instance = cainstance.CAInstance(api.env.realm)
ca_instance.stop_tracking_certificates()
ipautil.remove_file(paths.RA_AGENT_PEM)
diff --git a/ipaserver/install/cainstance.py b/ipaserver/install/cainstance.py
index 5dac2c0441752e7bb569cde1fc93bc17c3128cdf..5c2c9f8b981cf5d587865f7680e2b231eae655e2 100644
--- a/ipaserver/install/cainstance.py
+++ b/ipaserver/install/cainstance.py
@@ -1118,6 +1118,7 @@ class CAInstance(DogtagInstance):
ipautil.remove_file(paths.DOGTAG_ADMIN_P12)
ipautil.remove_file(paths.CACERT_P12)
+ ipautil.remove_file(paths.ADMIN_CERT_PATH)
def unconfigure_certmonger_renewal_guard(self):
if not self.is_configured():
diff --git a/ipaserver/install/dogtaginstance.py b/ipaserver/install/dogtaginstance.py
index e89492312deb8ca20668a62fd7a2a20e2866a3fb..4b0f4d274b0c33140ed6f939f1a3fd8b75930ff9 100644
--- a/ipaserver/install/dogtaginstance.py
+++ b/ipaserver/install/dogtaginstance.py
@@ -304,21 +304,37 @@ class DogtagInstance(service.Service):
if self.is_installed():
self.print_msg("Unconfiguring %s" % self.subsystem)
- args = [paths.PKIDESTROY,
- "-i", "pki-tomcat", "--force",
- "-s", self.subsystem]
-
- # specify --log-file <path> on PKI 11.0.0 or later
-
+ args = []
pki_version = pki.util.Version(pki.specification_version())
- if pki_version >= pki.util.Version("11.0.0"):
- timestamp = time.strftime(
- "%Y%m%d%H%M%S",
- time.localtime(time.time()))
- log_file = os.path.join(
- paths.VAR_LOG_PKI_DIR,
- "pki-%s-destroy.%s.log" % (self.subsystem.lower(), timestamp))
- args.extend(["--log-file", log_file])
+ if self.subsystem == "ACME":
+ if pki_version < pki.util.Version("11.0.0"):
+ return
+ elif (
+ pki.util.Version("11.0.0") <= pki_version
+ <= pki.util.Version("11.5.0")
+ ):
+ args = ['pki-server', 'acme-remove']
+ else:
+ # fall through for PKI >= 11.6.0
+ pass
+ if not args:
+ args = [paths.PKIDESTROY,
+ "-i", "pki-tomcat", "--force",
+ "-s", self.subsystem]
+
+ # specify --log-file <path> on PKI 11.0.0 or later
+
+ if pki_version >= pki.util.Version("11.0.0"):
+ timestamp = time.strftime(
+ "%Y%m%d%H%M%S",
+ time.localtime(time.time()))
+ log_file = os.path.join(
+ paths.VAR_LOG_PKI_DIR,
+ "pki-%s-destroy.%s.log" %
+ (self.subsystem.lower(), timestamp))
+ args.extend(["--log-file", log_file])
+ if pki_version >= pki.util.Version("11.6.0"):
+ args.extend(["--remove-conf", "--remove-logs"])
try:
ipautil.run(args)
diff --git a/ipatests/test_integration/test_uninstallation.py b/ipatests/test_integration/test_uninstallation.py
index 4f8f17ce3ad8d5376ecba11442f379e5691de7f7..049c50db536ae1070f5f958e76b12a1518da0aba 100644
--- a/ipatests/test_integration/test_uninstallation.py
+++ b/ipatests/test_integration/test_uninstallation.py
@@ -197,6 +197,7 @@ class TestUninstallCleanup(IntegrationTest):
'/var/lib/sss/pubconf/krb5.include.d/localauth_plugin',
'/var/named/dynamic/managed-keys.bind',
'/var/named/dynamic/managed-keys.bind.jnl',
+ '/var/lib/systemd/coredump/',
]
leftovers = []
@@ -217,3 +218,23 @@ class TestUninstallCleanup(IntegrationTest):
leftovers.append(line)
assert len(leftovers) == 0
+
+
+class TestUninstallReinstall(IntegrationTest):
+ """Test install, uninstall, re-install.
+
+ Reinstall with PKI 11.6.0 was failing
+ https://pagure.io/freeipa/issue/9673
+ """
+
+ num_replicas = 0
+
+ @classmethod
+ def install(cls, mh):
+ tasks.install_master(cls.master, setup_dns=False)
+
+ def test_uninstall_server(self):
+ tasks.uninstall_master(self.master)
+
+ def test_reinstall_server(self):
+ tasks.install_master(self.master, setup_dns=False)
--
2.46.2

View File

@ -0,0 +1,35 @@
From ae4c2ad6cd966d48c063814f494dcc16cf0ccd4c Mon Sep 17 00:00:00 2001
From: Sudhir Menon <sumenon@redhat.com>
Date: Tue, 24 Sep 2024 13:46:48 +0530
Subject: [PATCH] ipatests: Fixes for ipa-idrange-fix testsuite
This patch adds the line tasks.install_master(cls.master).
The kinit admin command fails with the below error as the
IPA is not configured on the test system
'ipa: ERROR: stderr: kinit: Configuration file does not specify default
realm when parsing name admin'
Signed-off-by: Sudhir Menon <sumenon@redhat.com>
Reviewed-By: Rob Crittenden <rcritten@redhat.com>
---
ipatests/test_integration/test_ipa_idrange_fix.py | 3 +++
1 file changed, 3 insertions(+)
diff --git a/ipatests/test_integration/test_ipa_idrange_fix.py b/ipatests/test_integration/test_ipa_idrange_fix.py
index ff8fbdac9d028d26fc55f5e357f89af879a61723..0c915bd0931ed11a3aa86c533ee8748aa8a7ec07 100644
--- a/ipatests/test_integration/test_ipa_idrange_fix.py
+++ b/ipatests/test_integration/test_ipa_idrange_fix.py
@@ -17,6 +17,9 @@ logger = logging.getLogger(__name__)
class TestIpaIdrangeFix(IntegrationTest):
+
+ topology = 'line'
+
@classmethod
def install(cls, mh):
super(TestIpaIdrangeFix, cls).install(mh)
--
2.46.2

View File

@ -0,0 +1,34 @@
From 642af014d9e7de8e53934af4ca3970977957cba7 Mon Sep 17 00:00:00 2001
From: Yaakov Selkowitz <yselkowi@redhat.com>
Date: Tue, 8 Oct 2024 02:13:00 -0400
Subject: [PATCH] spec: Use nodejs22 on RHEL 10 and ELN
nodejs22 is now the default nodejs version in RHEL 10 as well as ELN.
Signed-off-by: Yaakov Selkowitz <yselkowi@redhat.com>
Reviewed-By: Alexander Bokovoy <abbra@users.noreply.github.com>
Reviewed-By: Florence Blanc-Renaud <flo@redhat.com>
---
freeipa.spec.in | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/freeipa.spec.in b/freeipa.spec.in
index 171b6ad27b57553fdd46c7d041715949bb00b163..9b3d916f34c10a00070855578a593e299187bc44 100755
--- a/freeipa.spec.in
+++ b/freeipa.spec.in
@@ -308,10 +308,10 @@ BuildRequires: libpwquality-devel
BuildRequires: libsss_idmap-devel
BuildRequires: libsss_certmap-devel
BuildRequires: libsss_nss_idmap-devel >= %{sssd_version}
-%if 0%{?fedora} >= 41
+%if 0%{?fedora} >= 41 || 0%{?rhel} >= 10
# Do not use nodejs22 on fedora < 41, https://pagure.io/freeipa/issue/9643
BuildRequires: nodejs(abi)
-%elif 0%{?fedora} >= 39 || 0%{?rhel} >= 10
+%elif 0%{?fedora} >= 39
# Do not use nodejs20 on fedora < 39, https://pagure.io/freeipa/issue/9374
BuildRequires: nodejs(abi) < 127
%else
--
2.46.2

View File

@ -0,0 +1,265 @@
From 18303b94bea4e08a0c889fc357df6ba2f308fa0d Mon Sep 17 00:00:00 2001
From: Mark Reynolds <mreynolds@redhat.com>
Date: Wed, 2 Oct 2024 21:26:34 -0400
Subject: [PATCH] Do not let user with an expired OTP token to log in if only
OTP is allowed
If only OTP authentication is allowed, and a user tries to login with an
expired token, do not let them log in with their password. Forcing the
admin to intervene. If the user does not have an OTP token then allow
them to log in with a password until an OTP token is configured
Fixes: https://pagure.io/freeipa/issue/9387
Signed-off-by: Mark Reynolds <mreynolds@redhat.com>
Reviewed-By: Rob Crittenden <rcritten@redhat.com>
Reviewed-By: Alexander Bokovoy <abokovoy@redhat.com>
Reviewed-By: Julien Rische <jrische@redhat.com>
---
daemons/ipa-kdb/ipa_kdb_principals.c | 63 +++++++++++--
.../ipa-slapi-plugins/ipa-pwd-extop/prepost.c | 3 +-
ipatests/test_integration/test_otp.py | 94 ++++++++++++++++++-
3 files changed, 151 insertions(+), 9 deletions(-)
diff --git a/daemons/ipa-kdb/ipa_kdb_principals.c b/daemons/ipa-kdb/ipa_kdb_principals.c
index 14603e528b43acb29234c425e97ad297ac6724a7..114957b884786dd3ca3b01c47f6bb82e8a040beb 100644
--- a/daemons/ipa-kdb/ipa_kdb_principals.c
+++ b/daemons/ipa-kdb/ipa_kdb_principals.c
@@ -107,7 +107,6 @@ static char *std_principal_obj_classes[] = {
"krbprincipal",
"krbprincipalaux",
"krbTicketPolicyAux",
-
NULL
};
@@ -338,14 +337,16 @@ static void ipadb_validate_otp(struct ipadb_context *ipactx,
if (dn == NULL)
return;
count = asprintf(&filter, ftmpl, dn, datetime, datetime);
- ldap_memfree(dn);
- if (count < 0)
+ if (count < 0) {
+ ldap_memfree(dn);
return;
+ }
/* Fetch the active token list. */
kerr = ipadb_simple_search(ipactx, ipactx->base, LDAP_SCOPE_SUBTREE,
filter, (char**) attrs, &res);
free(filter);
+ filter = NULL;
if (kerr != 0 || res == NULL)
return;
@@ -353,10 +354,60 @@ static void ipadb_validate_otp(struct ipadb_context *ipactx,
count = ldap_count_entries(ipactx->lcontext, res);
ldap_msgfree(res);
- /* If the user is configured for OTP, but has no active tokens, remove
- * OTP from the list since the user obviously can't log in this way. */
- if (count == 0)
+ /*
+ * If there are no valid tokens then we need to remove the OTP flag,
+ * unless OTP is the only auth type allowed...
+ */
+ if (count == 0) {
+ /* Remove the OTP flag for now */
*ua &= ~IPADB_USER_AUTH_OTP;
+
+ if (*ua == 0) {
+ /*
+ * Ok, we "only" allow OTP, so if there is an expired/disabled
+ * token then add back the OTP flag as the server will double
+ * check the validity and reject the entire bind. Otherwise, this
+ * is the first time the user is authenticating and the user
+ * should be allowed to bind using its password
+ */
+ static const char *expired_ftmpl = "(&"
+ "(objectClass=ipaToken)(ipatokenOwner=%s)"
+ "(|(ipatokenNotAfter<=%s)(!(ipatokenNotAfter=*))"
+ "(ipatokenDisabled=True))"
+ ")";
+ if (asprintf(&filter, expired_ftmpl, dn, datetime) < 0) {
+ ldap_memfree(dn);
+ return;
+ }
+
+ krb5_klog_syslog(LOG_INFO,
+ "Entry (%s) does not have a valid token and only OTP "
+ "authentication is supported, checking for expired tokens...",
+ dn);
+
+ kerr = ipadb_simple_search(ipactx, ipactx->base, LDAP_SCOPE_SUBTREE,
+ filter, (char**) attrs, &res);
+ free(filter);
+ if (kerr != 0 || res == NULL) {
+ ldap_memfree(dn);
+ return;
+ }
+
+ if (ldap_count_entries(ipactx->lcontext, res) > 0) {
+ /*
+ * Ok we only allow OTP, and there are expired/disabled tokens
+ * so add the OTP flag back, and the server will reject the
+ * bind
+ */
+ krb5_klog_syslog(LOG_INFO,
+ "Entry (%s) does have an expired/disabled token so this "
+ "user can not fall through to password auth", dn);
+ *ua |= IPADB_USER_AUTH_OTP;
+ }
+ ldap_msgfree(res);
+ }
+ }
+ ldap_memfree(dn);
}
static void ipadb_validate_radius(struct ipadb_context *ipactx,
diff --git a/daemons/ipa-slapi-plugins/ipa-pwd-extop/prepost.c b/daemons/ipa-slapi-plugins/ipa-pwd-extop/prepost.c
index c967e2cfffbd920280639f3188783ec150523b47..1c1340e31ac30cb01412a7065ea339cb5461e839 100644
--- a/daemons/ipa-slapi-plugins/ipa-pwd-extop/prepost.c
+++ b/daemons/ipa-slapi-plugins/ipa-pwd-extop/prepost.c
@@ -1528,7 +1528,8 @@ static int ipapwd_pre_bind(Slapi_PBlock *pb)
if (!syncreq && (otpreq == OTP_IS_NOT_REQUIRED)) {
ret = ipapwd_gen_checks(pb, &errMesg, &krbcfg, IPAPWD_CHECK_ONLY_CONFIG);
if (ret != 0) {
- LOG_FATAL("ipapwd_gen_checks failed!?\n");
+ LOG_FATAL("ipapwd_gen_checks failed for '%s': %s\n",
+ slapi_sdn_get_dn(sdn), errMesg);
slapi_entry_free(entry);
slapi_sdn_free(&sdn);
return 0;
diff --git a/ipatests/test_integration/test_otp.py b/ipatests/test_integration/test_otp.py
index 350371bfe1e4c1cc6dcc89f6584f813fcb0d32a0..878b4fb560ba8d7768ead54b065656462545babd 100644
--- a/ipatests/test_integration/test_otp.py
+++ b/ipatests/test_integration/test_otp.py
@@ -10,6 +10,7 @@ import re
import time
import textwrap
from urllib.parse import urlparse, parse_qs
+from paramiko import AuthenticationException
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives import hashes
@@ -83,7 +84,7 @@ def kinit_otp(host, user, *, password, otp, success=True):
)
-def ssh_2f(hostname, username, answers_dict, port=22):
+def ssh_2f(hostname, username, answers_dict, port=22, unwanted_prompt=""):
"""
:param hostname: hostname
:param username: username
@@ -103,6 +104,10 @@ def ssh_2f(hostname, username, answers_dict, port=22):
logger.info("Prompt is: '%s'", prmpt_str)
logger.info(
"Answer to ssh prompt is: '%s'", answers_dict[prmpt_str])
+ if unwanted_prompt and prmpt_str == unwanted_prompt:
+ # We should not see this prompt
+ raise ValueError("We got an unwanted prompt: "
+ + answers_dict[prmpt_str])
return resp
import paramiko
@@ -193,7 +198,8 @@ class TestOTPToken(IntegrationTest):
# skipping too many OTP fails
otp1 = hotp.generate(10).decode("ascii")
- kinit_otp(self.master, USER, password=PASSWORD, otp=otp1, success=False)
+ kinit_otp(self.master, USER, password=PASSWORD, otp=otp1,
+ success=False)
# Now the token is desynchronized
yield (otpuid, hotp)
@@ -536,3 +542,87 @@ class TestOTPToken(IntegrationTest):
finally:
master.run_command(['ipa', 'pwpolicy-mod', '--minlife', '1'])
master.run_command(['ipa', 'user-del', USER1])
+
+ def test_totp_expired_ldap(self):
+ master = self.master
+ basedn = master.domain.basedn
+ USER1 = 'user-expired-otp'
+ TMP_PASSWORD = 'Secret1234509'
+ binddn = DN(f"uid={USER1},cn=users,cn=accounts,{basedn}")
+ controls = [
+ BooleanControl(
+ controlType="2.16.840.1.113730.3.8.10.7",
+ booleanValue=True)
+ ]
+
+ tasks.kinit_admin(master)
+ master.run_command(['ipa', 'pwpolicy-mod', '--minlife', '0'])
+ tasks.user_add(master, USER1, password=TMP_PASSWORD)
+ # Enforce use of OTP token for this user
+ master.run_command(['ipa', 'user-mod', USER1,
+ '--user-auth-type=otp'])
+ try:
+ # Change initial password through the IPA endpoint
+ url = f'https://{master.hostname}/ipa/session/change_password'
+ master.run_command(['curl', '-d', f'user={USER1}',
+ '-d', f'old_password={TMP_PASSWORD}',
+ '-d', f'new_password={PASSWORD}',
+ '--referer', f'https://{master.hostname}/ipa',
+ url])
+ conn = master.ldap_connect()
+ # First, attempt authenticating with a password but without LDAP
+ # control to enforce OTP presence and without server-side
+ # enforcement of the OTP presence check.
+ conn.simple_bind(binddn, f"{PASSWORD}")
+
+ # Add an OTP token and then modify it to be expired
+ otpuid, totp = add_otptoken(master, USER1, otptype="totp")
+
+ # Make sure OTP auth is working
+ otpvalue = totp.generate(int(time.time())).decode("ascii")
+ conn = master.ldap_connect()
+ conn.simple_bind(binddn, f"{PASSWORD}{otpvalue}",
+ client_controls=controls)
+ conn.unbind()
+
+ # Modfy token so that is now expired
+ args = [
+ "ipa",
+ "otptoken-mod",
+ otpuid,
+ "--not-after",
+ "20241001010000Z",
+ ]
+ master.run_command(args)
+
+ # Next, authenticate with Password+OTP again and with the LDAP
+ # control this operation should now fail
+ time.sleep(45)
+ otpvalue = totp.generate(int(time.time())).decode("ascii")
+
+ conn = master.ldap_connect()
+ with pytest.raises(errors.ACIError):
+ conn.simple_bind(binddn, f"{PASSWORD}{otpvalue}",
+ client_controls=controls)
+
+ # Sleep to make sure we are going to use a different token value
+ time.sleep(45)
+
+ # Use OTP token again but authenticate over ssh and make sure it
+ # doesn't fallthrough to asking for a password
+ otpvalue = totp.generate(int(time.time())).decode("ascii")
+ answers = {
+ 'Enter first factor:': PASSWORD,
+ 'Enter second factor:': otpvalue
+ }
+ with pytest.raises(AuthenticationException):
+ # ssh should fail and NOT ask for a password
+ ssh_2f(master.hostname, USER1, answers,
+ unwanted_prompt="Password:")
+
+ # Remove token
+ del_otptoken(self.master, otpuid)
+
+ finally:
+ master.run_command(['ipa', 'pwpolicy-mod', '--minlife', '1'])
+ master.run_command(['ipa', 'user-del', USER1])
--
2.46.2

View File

@ -0,0 +1,41 @@
From 761647f842567713032709753b6d63467d9871a6 Mon Sep 17 00:00:00 2001
From: Sudhir Menon <sumenon@redhat.com>
Date: Mon, 23 Sep 2024 14:05:43 +0530
Subject: [PATCH] ipatests: Activate ssh in sssd.conf
This testcase checks that services: ssh
is included in the sssd.conf file when
ipa-client-install is successful.
Ref: https://pagure.io/freeipa/issue/9649
Signed-off-by: Sudhir Menon <sumenon@redhat.com>
Reviewed-By: Rob Crittenden <rcritten@redhat.com>
---
ipatests/test_integration/test_installation_client.py | 10 ++++++++++
1 file changed, 10 insertions(+)
diff --git a/ipatests/test_integration/test_installation_client.py b/ipatests/test_integration/test_installation_client.py
index f8567b39eead4dffd522aad504fa72a086969257..884bff2f2f3e49f66da391424e128d8e31b82a8a 100644
--- a/ipatests/test_integration/test_installation_client.py
+++ b/ipatests/test_integration/test_installation_client.py
@@ -94,6 +94,16 @@ class TestInstallClient(IntegrationTest):
).encode() not in krb5_cfg
tasks.uninstall_client(self.clients[0])
+ def test_check_ssh_service_is_activated(self):
+ """
+ This test checks all default services are activated
+ in sssd.conf including ssh
+ """
+ tasks.install_client(self.master, self.clients[0])
+ sssd_cfg = self.clients[0].get_file_contents(paths.SSSD_CONF)
+ assert 'services = nss, pam, ssh, sudo' in sssd_cfg.decode()
+ tasks.uninstall_client(self.clients[0])
+
def test_install_with_automount(self):
"""Test that installation with automount is successful"""
tasks.install_client(self.master, self.clients[0],
--
2.46.2

View File

@ -0,0 +1,131 @@
From f978fa05e3ed9d4ad9d20493c05c77fb9b4976a7 Mon Sep 17 00:00:00 2001
From: Florence Blanc-Renaud <flo@redhat.com>
Date: Tue, 15 Oct 2024 17:04:55 +0200
Subject: [PATCH] ipa-migrate man page: fix typos and errors
ipa-migrate man page mentions non-existing option --hostname.
Fix the SYNOPSIS and various typos.
Fixes: https://pagure.io/freeipa/issue/9681
Signed-off-by: Florence Blanc-Renaud <flo@redhat.com>
Reviewed-By: Mark Reynolds <mreynolds@redhat.com>
---
install/tools/man/ipa-migrate.1 | 38 +++++++++++++++------------------
1 file changed, 17 insertions(+), 21 deletions(-)
diff --git a/install/tools/man/ipa-migrate.1 b/install/tools/man/ipa-migrate.1
index 47ae47ea4afa3a5a6fe25dd9bbd14c27ab5f1fdb..5106f4f0f5b5928909ccd5abcef3bb6d1586f5df 100644
--- a/install/tools/man/ipa-migrate.1
+++ b/install/tools/man/ipa-migrate.1
@@ -5,11 +5,11 @@
.SH "NAME"
ipa\-migrate \- Migrate an IPA server from one machine to another
.SH "SYNOPSIS"
-ipa\-migrate
+\fBipa\-migrate\fR [OPTIONS] \fBprod\-mode\fR|\fBstage\-mode\fR \fIhostname\fR
.SH "DESCRIPTION"
Use the \fIipa-migrate\fR command to migrate one
-IPA server to an existing local IPA server installation.
+IPA server \fIhostname\fR to an existing local IPA server installation.
Migrate IPA schema, configuration, and database to a local IPA server. This
migration can be done online, where the tool will query the remote server. Or,
@@ -19,7 +19,6 @@ and then use an exported LDIF file for the database migration portion (this
might be more useful for very large databases as you don't need to worry about
network interruptions)
-.SH POSITIONAL ARGUMENTS
.TP
\fBprod\-mode\fR
In this mode everything will be migrated including the current user SIDs and
@@ -28,13 +27,10 @@ DNA ranges
\fBstage\-mode\fR
In this mode, SIDs & DNA ranges are not migrated, and DNA attributes are reset
-.SH "COMMANDS"
+.SH "OPTIONS"
.TP
\fB\-v\fR, \fB\-\-verbose\fR
-Use verbose output while running the migration tool.
-.TP
-\fB\-e\fR, \fB\-\-hostname=HOSTNAME\fR
-The host name of the remote IPA server that is being migrated from.
+Use verbose output while running the migration tool
.TP
\fB\-D\fR, \fB\-\-bind\-dn=BIND_DN\fR
The Bind DN (Distinguished Name) or an LDAP entry to bind to the remote IPA server with.
@@ -43,10 +39,10 @@ access to read the userPassword attribute. If ommitted the default is "cn=direc
.TP
\fB\-w\fR, \fB\-\-bind\-pw=PASSWORD\fR
The password for the Bind DN that is authenticating against the remote IPA server. If
-a password is not provided then the tool with prompt for the password if needed.
+a password is not provided then the tool with prompt for the password if needed
.TP
-\fB\-Just\fR, \fB\-\-bind\-pw\-file=FILE_PATH\fR
-Path to a file containing the password for the Bind DN.
+\fB\-j\fR, \fB\-\-bind\-pw\-file=FILE_PATH\fR
+Path to a file containing the password for the Bind DN
.TP
\fB\-Z\fR, \fB\-\-cacertfile=FILE_PATH\fR
Path to a file containing a CA Certificate that the remote server trusts
@@ -55,23 +51,23 @@ Path to a file containing a CA Certificate that the remote server trusts
Path to a file containing the migration log. By default the tool will use \fI/var/log/ipa-migrate.log\fR
.TP
\fB\-x\fR, \fB\-\-dryrun\fR
-Go through the migration process but do not write and data to the new IPA server.
+Go through the migration process but do not write any data to the new IPA server
.TP
\fB\-o\fR, \fB\-\-dryrun\-record=FILE_PATH\fR
Go through the migration process but do not write any data to the new IPA server. However, write the
-migration operations to an LDIF file which can be applied later or reused for multiple migrations.
+migration operations to an LDIF file which can be applied later or reused for multiple migrations
.TP
\fB\-r\fR, \fB\-\-reset\-range\fR
Reset the ID range for migrated users/groups. In "stage-mode" this is done automatically
.TP
\fB\-F\fR, \fB\-\-force\fR
-Ignore any errors and continue to proceed with migration effort.
+Ignore any errors and continue to proceed with migration effort
.TP
\fB\-q\fR, \fB\-\-quiet\fR
-Only log errors during the migration process.
+Only log errors during the migration process
.TP
\fB\-B\fR, \fB\-\-migrate\-dns\fR
-Migrate thr DNS records
+Migrate the DNS records
.TP
\fB\-S\fR, \fB\-\-skip\-schema\fR
Do not migrate the database schema
@@ -80,21 +76,21 @@ Do not migrate the database schema
Do not migrate the database configuration (dse.ldif/cn=config)
.TP
\fB\-O\fR, \fB\-\-schema\-overwrite\fR
-Overwrite existing schema definitions. By default duplicate schema is skipped.
+Overwrite existing schema definitions. By default duplicate schema is skipped
.TP
\fB\-s\fR, \fB\-\-subtree=DN\fR
Specifies a custom database subtree that should be included in the migration.
This is only needed if non-default subtrees/branches were added to the database
-outside of IPA.
+outside of IPA
.TP
\fB\-f\fR, \fB\-\-db\-ldif=FILE_PATH\fR
-LDIF file containing the entire backend. If omitted the tool will query the remote IPA server.
+LDIF file containing the entire backend. If omitted the tool will query the remote IPA server
.TP
\fB\-m\fR, \fB\-\-schema\-ldif=FILE_PATH\fR
-LDIF file containing the schema. If omitted the tool will query the remote IPA server.
+LDIF file containing the schema. If omitted the tool will query the remote IPA server
.TP
\fB\-g\fR, \fB\-\-config\-ldif=FILE_PATH\fR
-LDIF file containing the entire "cn=config" DIT. If omitted the tool will query the remote IPA server.
+LDIF file containing the entire "cn=config" DIT. If omitted the tool will query the remote IPA server
.TP
\fB\-n\fR, \fB\-\-no\-prompt\fR
Do not prompt for confirmation before starting migration. Use at your own risk!
--
2.46.2

View File

@ -0,0 +1,61 @@
From 7f4e7e1d6a2ae9d05a2dfcf620f4df07d09d9d2b Mon Sep 17 00:00:00 2001
From: Sudhir Menon <sumenon@redhat.com>
Date: Thu, 3 Oct 2024 18:45:31 +0530
Subject: [PATCH] ipatests: Test for ipa hbac rule duplication
This test checks that ipa-migrate is not creating duplicate default hbac rules
for allow_all and allow_systemd-user rules.
Related: https://pagure.io/freeipa/issue/9640
Signed-off-by: Sudhir Menon <sumenon@redhat.com>
Reviewed-By: Rob Crittenden <rcritten@redhat.com>
Reviewed-By: Florence Blanc-Renaud <frenaud@redhat.com>
---
.../test_ipa_ipa_migration.py | 26 +++++++++++++++++++
1 file changed, 26 insertions(+)
diff --git a/ipatests/test_integration/test_ipa_ipa_migration.py b/ipatests/test_integration/test_ipa_ipa_migration.py
index 288165e8a83a96e6f6bd4e52866f98617f497c56..70c268951a0d7e40806742b16e62b764b2bae37b 100644
--- a/ipatests/test_integration/test_ipa_ipa_migration.py
+++ b/ipatests/test_integration/test_ipa_ipa_migration.py
@@ -9,6 +9,7 @@ from __future__ import absolute_import
from ipatests.test_integration.base import IntegrationTest
from ipatests.pytest_ipa.integration import tasks
from ipaplatform.paths import paths
+from collections import Counter
import pytest
import textwrap
@@ -920,3 +921,28 @@ class TestIPAMigrateScenario1(IntegrationTest):
)
assert result.returncode == 1
assert ERR_MSG in result.stderr_text
+
+ def test_ipa_hbac_rule_duplication(self):
+ """
+ This testcase checks that default hbac rules
+ are not duplicated on the local server when
+ ipa-migrate command is run.
+ """
+ run_migrate(
+ self.replicas[0],
+ "prod-mode",
+ self.master.hostname,
+ "cn=Directory Manager",
+ self.master.config.admin_password,
+ extra_args=['-n']
+ )
+ result = self.replicas[0].run_command(
+ ['ipa', 'hbacrule-find']
+ )
+ lines = result.stdout_text.splitlines()
+ line = []
+ for i in lines:
+ line.append(i.strip())
+ count = Counter(line)
+ assert count.get('Rule name: allow_all') < 2
+ assert count.get('Rule name: allow_systemd-user') < 2
--
2.46.2

View File

@ -0,0 +1,116 @@
From 142f52fc981fe9f1d693b79a7b49506af2e98829 Mon Sep 17 00:00:00 2001
From: Mohammad Rizwan <myusuf@redhat.com>
Date: Mon, 19 Aug 2024 16:08:53 +0530
Subject: [PATCH] ipatests: refactor password file handling in TestHSMInstall
When token and associated certs are not being cleaned
up properly, the subsequent installation fails. Hence
Password file related scenarios moved out to new test class
so that it have fresh installation.
Signed-off-by: Mohammad Rizwan <myusuf@redhat.com>
Reviewed-By: Rob Crittenden <rcritten@redhat.com>
Reviewed-By: Florence Blanc-Renaud <frenaud@redhat.com>
Reviewed-By: Rob Crittenden <rcritten@redhat.com>
Reviewed-By: Florence Blanc-Renaud <frenaud@redhat.com>
---
.../nightly_ipa-4-12_latest.yaml | 12 ++++++++
.../nightly_ipa-4-12_latest_selinux.yaml | 13 ++++++++
ipatests/test_integration/test_hsm.py | 30 ++++++++++---------
3 files changed, 41 insertions(+), 14 deletions(-)
diff --git a/ipatests/prci_definitions/nightly_ipa-4-12_latest.yaml b/ipatests/prci_definitions/nightly_ipa-4-12_latest.yaml
index 6d18e708fb0512ce21d8db68d4f1ab26849f40b7..07e2a8399ae4cc953adb415b975101ed20c67fd2 100644
--- a/ipatests/prci_definitions/nightly_ipa-4-12_latest.yaml
+++ b/ipatests/prci_definitions/nightly_ipa-4-12_latest.yaml
@@ -1950,6 +1950,18 @@ jobs:
timeout: 6300
topology: *master_3repl_1client
+ fedora-latest-ipa-4-12/test_hsm_TestHSMInstallPasswordFile:
+ requires: [fedora-latest-ipa-4-12/build]
+ priority: 50
+ job:
+ class: RunPytest
+ args:
+ build_url: '{fedora-latest-ipa-4-12/build_url}'
+ test_suite: test_integration/test_hsm.py::TestHSMInstallPasswordFile
+ template: *ci-ipa-4-12-latest
+ timeout: 6300
+ topology: *master_1repl
+
fedora-latest-ipa-4-12/test_hsm_TestHSMInstallADTrustBase:
requires: [fedora-latest-ipa-4-12/build]
priority: 50
diff --git a/ipatests/prci_definitions/nightly_ipa-4-12_latest_selinux.yaml b/ipatests/prci_definitions/nightly_ipa-4-12_latest_selinux.yaml
index 52686df9713975c9590b8a99edb7c3442531fecc..11046be13fca1e7403d0fd74329a66ded3927a6c 100644
--- a/ipatests/prci_definitions/nightly_ipa-4-12_latest_selinux.yaml
+++ b/ipatests/prci_definitions/nightly_ipa-4-12_latest_selinux.yaml
@@ -2105,6 +2105,19 @@ jobs:
timeout: 6300
topology: *master_3repl_1client
+ fedora-latest-ipa-4-12/test_hsm_TestHSMInstallPasswordFile:
+ requires: [fedora-latest-ipa-4-12/build]
+ priority: 50
+ job:
+ class: RunPytest
+ args:
+ build_url: '{fedora-latest-ipa-4-12/build_url}'
+ selinux_enforcing: True
+ test_suite: test_integration/test_hsm.py::TestHSMInstallPasswordFile
+ template: *ci-ipa-4-12-latest
+ timeout: 6300
+ topology: *master_1repl
+
fedora-latest-ipa-4-12/test_hsm_TestHSMInstallADTrustBase:
requires: [fedora-latest-ipa-4-12/build]
priority: 50
diff --git a/ipatests/test_integration/test_hsm.py b/ipatests/test_integration/test_hsm.py
index 374f5c25fd3453cd45a15d2b0f20cee424282595..42895fcd60a7c02d3b6103c2f6751a367da30b2f 100644
--- a/ipatests/test_integration/test_hsm.py
+++ b/ipatests/test_integration/test_hsm.py
@@ -312,24 +312,26 @@ class TestHSMInstall(BaseHSMTest):
assert returncode == 0
assert output == "No issues found."
- def test_hsm_install_server_password_file(self):
- check_version(self.master)
- # cleanup before fresh install with password file
- for client in self.clients:
- tasks.uninstall_client(client)
- for replica in self.replicas:
- tasks.uninstall_master(replica)
+class TestHSMInstallPasswordFile(BaseHSMTest):
- tasks.uninstall_master(self.master)
+ num_replicas = 1
- delete_hsm_token([self.master] + self.replicas, self.token_name)
- self.token_name, self.token_password = get_hsm_token(self.master)
- self.master.put_file_contents(self.token_password_file,
- self.token_password)
- self.replicas[0].put_file_contents(self.token_password_file,
- self.token_password)
+ @classmethod
+ def install(cls, mh):
+ check_version(cls.master)
+ # Enable pkiuser to read softhsm tokens
+ cls.master.run_command(['usermod', 'pkiuser', '-a', '-G', 'ods'])
+ cls.token_name, cls.token_password = get_hsm_token(cls.master)
+ cls.master.put_file_contents(
+ cls.token_password_file, cls.token_password
+ )
+ cls.replicas[0].put_file_contents(
+ cls.token_password_file, cls.token_password
+ )
+ def test_hsm_install_server_password_file(self):
+ check_version(self.master)
tasks.install_master(
self.master, setup_dns=self.master_with_dns,
setup_kra=self.master_with_kra,
--
2.46.2

View File

@ -0,0 +1,276 @@
From 6ac11ae003740faf19f3c75bf542ec44f717114f Mon Sep 17 00:00:00 2001
From: Madhuri Upadhye <mupadhye@redhat.com>
Date: Tue, 23 Jul 2024 18:14:36 +0530
Subject: [PATCH] ipatests: 2FA test cases
Added following:
Added 'ssh_2fa_with_cmd' method for authentication,
as for '\n' with paramiko did not work. In a test case
need to just press `Enter` for `second factor`.
Advantage of above function is no having paramiko
dependancy.
We can run the any command in same session after
authentication of user.
Test cases:
1. Authenticate the user only with password,
just press enter at `Second factor` and check tgt after auth.
when User authentication types: otp, password
2. Authenticate the user with password and otpvalues and
check tgt of user after auth when
User authentication types: otp, password
related: https://github.com/SSSD/sssd/pull/7500
Signed-off-by: Madhuri Upadhye <mupadhye@redhat.com>
Reviewed-By: Florence Blanc-Renaud <flo@redhat.com>
---
ipatests/test_integration/test_otp.py | 192 ++++++++++++++++++++++++--
1 file changed, 181 insertions(+), 11 deletions(-)
diff --git a/ipatests/test_integration/test_otp.py b/ipatests/test_integration/test_otp.py
index 878b4fb560ba8d7768ead54b065656462545babd..0babb45897c6107bf354477dbb0d3a805a3116f5 100644
--- a/ipatests/test_integration/test_otp.py
+++ b/ipatests/test_integration/test_otp.py
@@ -5,26 +5,27 @@
"""
import base64
import logging
-import pytest
import re
-import time
+import tempfile
import textwrap
-from urllib.parse import urlparse, parse_qs
-from paramiko import AuthenticationException
+import time
+from urllib.parse import parse_qs, urlparse
+import pytest
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives.twofactor.hotp import HOTP
from cryptography.hazmat.primitives.twofactor.totp import TOTP
-
-from ipatests.test_integration.base import IntegrationTest
-from ipaplatform.paths import paths
-from ipatests.pytest_ipa.integration import tasks
-from ipapython.dn import DN
-
from ldap.controls.simple import BooleanControl
+from paramiko import AuthenticationException
from ipalib import errors
+from ipaplatform.osinfo import osinfo
+from ipaplatform.paths import paths
+from ipapython.dn import DN
+from ipatests.pytest_ipa.integration import tasks
+from ipatests.test_integration.base import IntegrationTest
+from ipatests.util import xfail_context
PASSWORD = "DummyPassword123"
USER = "opttestuser"
@@ -84,6 +85,65 @@ def kinit_otp(host, user, *, password, otp, success=True):
)
+def ssh_2fa_with_cmd(host, hostname, username, password, otpvalue,
+ command="exit 0"):
+ """ ssh to user and in same session pass the command to check tgt of user
+ :param host: host to ssh
+ :param hostname: hostname to ssh
+ :param str username: The name of user
+ :param str password: password, usually the first factor
+ :param str otpvalue: generated pin of user
+ :param str command: command to execute in same session,
+ by deafult set to "exit 0"
+ :return: object class of expect command run
+ """
+ temp_conf = tempfile.NamedTemporaryFile(suffix='.exp', delete=False)
+ with open(temp_conf.name, 'w') as tfile:
+ tfile.write('proc exitmsg { msg code } {\n')
+ tfile.write('\t# Close spawned program, if we are in the prompt\n')
+ tfile.write('\tcatch close\n\n')
+ tfile.write('\t# Wait for the exit code\n')
+ tfile.write('\tlassign [wait] pid spawnid os_error_flag rc\n\n')
+ tfile.write('\tputs ""\n')
+ tfile.write('\tputs "expect result: $msg"\n')
+ tfile.write('\tputs "expect exit code: $code"\n')
+ tfile.write('\tputs "expect spawn exit code: $rc"\n')
+ tfile.write('\texit $code\n')
+ tfile.write('}\n')
+ tfile.write('set timeout 60\n')
+ tfile.write('set prompt ".*\\[#\\$>\\] $"\n')
+ tfile.write(f'set password "{password}"\n')
+ tfile.write(f'set otpvalue "{otpvalue}"\n')
+ tfile.write(f'spawn ssh -o NumberOfPasswordPrompts=1 -o '
+ f'StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null'
+ f' -l {username} {hostname} {command}\n')
+ tfile.write('expect {\n')
+ tfile.write('"Enter first factor:*" {send -- "$password\r"}\n')
+ tfile.write('timeout {exitmsg "Unexpected output" 201}\n')
+ tfile.write('eof {exitmsg "Unexpected end of file" 202}\n')
+ tfile.write('}\n')
+ tfile.write('expect {\n')
+ tfile.write('"Enter second factor:*" {send -- "$otpvalue\r"}\n')
+ tfile.write('timeout {exitmsg "Unexpected output" 201}\n')
+ tfile.write('eof {exitmsg "Unexpected end of file" 202}\n')
+ tfile.write('}\n')
+ tfile.write('expect {\n')
+ tfile.write('"Authentication failure" '
+ '{exitmsg "Authentication failure" 1}\n')
+ tfile.write('eof {exitmsg "Authentication successful" 0}\n')
+ tfile.write('timeout {exitmsg "Unexpected output" 201}\n')
+ tfile.write('}\n')
+ tfile.write('expect {\n')
+ tfile.write('exitmsg "Unexpected code path" 203\n')
+ tfile.write('EOF\n')
+ tfile.write('}')
+ host.transport.put_file(temp_conf.name, '/tmp/ssh.exp')
+ tasks.clear_sssd_cache(host)
+ expect_cmd = 'expect -f /tmp/ssh.exp'
+ cmd = host.run_command(expect_cmd, raiseonerr=False)
+ return cmd
+
+
def ssh_2f(hostname, username, answers_dict, port=22, unwanted_prompt=""):
"""
:param hostname: hostname
@@ -91,6 +151,7 @@ def ssh_2f(hostname, username, answers_dict, port=22, unwanted_prompt=""):
:param answers_dict: dictionary of options with prompt_message and value.
:param port: port for ssh
"""
+
# Handler for server questions
def answer_handler(title, instructions, prompt_list):
resp = []
@@ -131,8 +192,9 @@ class TestOTPToken(IntegrationTest):
@classmethod
def install(cls, mh):
- super(TestOTPToken, cls).install(mh)
master = cls.master
+ tasks.install_packages(master, ['expect'])
+ super(TestOTPToken, cls).install(mh)
tasks.kinit_admin(master)
# create service with OTP auth indicator
@@ -398,6 +460,114 @@ class TestOTPToken(IntegrationTest):
self.master.run_command(['semanage', 'login', '-D'])
sssd_conf_backup.restore()
+ def test_2fa_only_with_password(self):
+ """Test ssh with 2FA only with the password(first factor) when
+ user-auth-type is opt and password.
+
+ Test for : https://github.com/SSSD/sssd/pull/7500
+
+ Add the IPA user and user-auth-type set to opt and password.
+ Authenticate the user only with password, just press enter
+ at `Second factor`
+ """
+ master = self.master
+ USER3 = 'sshuser3'
+ sssd_conf_backup = tasks.FileBackup(master, paths.SSSD_CONF)
+ first_prompt = 'Enter first factor:'
+ second_prompt = 'Enter second factor:'
+ add_contents = textwrap.dedent('''
+ [prompting/2fa/sshd]
+ single_prompt = False
+ first_prompt = {0}
+ second_prompt = {1}
+ ''').format(first_prompt, second_prompt)
+ set_sssd_conf(master, add_contents)
+ tasks.create_active_user(master, USER3, PASSWORD)
+ tasks.kinit_admin(master)
+ master.run_command(['ipa', 'user-mod', USER3, '--user-auth-type=otp',
+ '--user-auth-type=password'])
+ try:
+ otpuid, totp = add_otptoken(master, USER3, otptype='totp')
+ master.run_command(['ipa', 'otptoken-show', otpuid])
+ totp.generate(int(time.time())).decode('ascii')
+ otpvalue = "\n"
+ tasks.clear_sssd_cache(self.master)
+ github_ticket = "https://github.com/SSSD/sssd/pull/7500"
+ sssd_version = tasks.get_sssd_version(master)
+ rhel_fail = (
+ osinfo.id == 'rhel'
+ and sssd_version < tasks.parse_version("2.9.5")
+ )
+ fedora_fail = (
+ osinfo.id == 'fedora'
+ and sssd_version == tasks.parse_version("2.9.5")
+ )
+ with xfail_context(rhel_fail or fedora_fail, reason=github_ticket):
+ result = ssh_2fa_with_cmd(master,
+ self.master.external_hostname,
+ USER3, PASSWORD, otpvalue=otpvalue,
+ command="klist")
+ print(result.stdout_text)
+ assert ('Authentication successful') in result.stdout_text
+ assert USER3 in result.stdout_text
+ assert (f'Default principal: '
+ f'{USER3}@{self.master.domain.realm}' in
+ result.stdout_text)
+ cmd = self.master.run_command(['semanage', 'login', '-l'])
+ assert USER3 in cmd.stdout_text
+ finally:
+ master.run_command(['ipa', 'user-del', USER3])
+ self.master.run_command(['semanage', 'login', '-D'])
+ sssd_conf_backup.restore()
+
+ def test_2fa_with_otp_password(self):
+ """Test ssh with 2FA only with password and otpvalue when
+ user-auth-type is opt and password.
+
+ Test for : https://github.com/SSSD/sssd/pull/7500
+
+ Add the IPA user and user-auth-type set to opt and password.
+ Authenticate the user only with password and otpvalue.
+ """
+ master = self.master
+ USER4 = 'sshuser4'
+ sssd_conf_backup = tasks.FileBackup(master, paths.SSSD_CONF)
+ first_prompt = 'Enter first factor:'
+ second_prompt = 'Enter second factor:'
+ add_contents = textwrap.dedent('''
+ [prompting/2fa/sshd]
+ single_prompt = False
+ first_prompt = {0}
+ second_prompt = {1}
+ ''').format(first_prompt, second_prompt)
+ set_sssd_conf(master, add_contents)
+ tasks.create_active_user(master, USER4, PASSWORD)
+ tasks.kinit_admin(master)
+
+ master.run_command(['ipa', 'user-mod', USER4, '--user-auth-type=otp',
+ '--user-auth-type=password'])
+ try:
+ otpuid, totp = add_otptoken(master, USER4, otptype='totp')
+ master.run_command(['ipa', 'otptoken-show', otpuid])
+ otpvalue = totp.generate(int(time.time())).decode('ascii')
+ tasks.clear_sssd_cache(self.master)
+ result = ssh_2fa_with_cmd(master,
+ self.master.external_hostname,
+ USER4, PASSWORD, otpvalue=otpvalue,
+ command="klist")
+ print(result.stdout_text)
+ cmd = self.master.run_command(['semanage', 'login', '-l'])
+ # check the output
+ assert ('Authentication successful') in result.stdout_text
+ assert USER4 in result.stdout_text
+ assert (f'Default principal: {USER4}@'
+ f'{self.master.domain.realm}' in result.stdout_text)
+ assert USER4 in cmd.stdout_text
+ finally:
+ master.run_command(['ipa', 'user-del', USER4])
+ self.master.run_command(['semanage', 'login', '-D'])
+ sssd_conf_backup.restore()
+
@pytest.fixture
def setup_otp_nsslapd(self):
check_services = self.master.run_command(
--
2.46.2

View File

@ -70,7 +70,7 @@
%global krb5_kdb_version 9.0
# 0.7.16: https://github.com/drkjam/netaddr/issues/71
%global python_netaddr_version 0.7.19
%global samba_version 4.20.0
%global samba_version 4.21.1
%global slapi_nis_version 0.70.0
%global python_ldap_version 3.1.0-1
%if 0%{?rhel} < 9
@ -88,7 +88,7 @@
%global bind_version 9.11.20-6
# support for passkey
%global sssd_version 2.9.0
%global sssd_version 2.10.0
%else
# Fedora
@ -248,6 +248,18 @@ Patch0009: 0009-ipa-migrate-fix-migration-issues-with-entries-using-.patch
Patch0010: 0010-ipa-migrate-fix-alternate-entry-search-filter.patch
Patch0011: 0011-Custodia-in-fips-mode-add-nomac-or-nomacver-to-opens.patch
Patch0012: 0012-ipatests-make-TestDuplicates-teardowns-order-agnosti.patch
Patch0013: 0013-UnsafeIPAddress-pass-flag-0-to-IPNetwork.patch
Patch0014: 0014-ipatests-provide-a-ccache-to-rpcclient-deletetrustdo.patch
Patch0015: 0015-test_adtrust_install-add-use-krb5-ccache-to-smbclien.patch
Patch0016: 0016-Don-t-rely-on-removing-the-CA-to-uninstall-the-ACME-.patch
Patch0017: 0017-ipatests-Fixes-for-ipa-idrange-fix-testsuite.patch
Patch0018: 0018-spec-Use-nodejs22-on-RHEL-10-and-ELN.patch
Patch0019: 0019-Do-not-let-user-with-an-expired-OTP-token-to-log-in-.patch
Patch0020: 0020-ipatests-Activate-ssh-in-sssd.conf.patch
Patch0021: 0021-ipa-migrate-man-page-fix-typos-and-errors.patch
Patch0022: 0022-ipatests-Test-for-ipa-hbac-rule-duplication.patch
Patch0023: 0023-ipatests-refactor-password-file-handling-in-TestHSMI.patch
Patch0024: 0024-ipatests-2FA-test-cases.patch
Patch1001: 1001-Change-branding-to-IPA-and-Identity-Management.patch
%endif
%endif
@ -300,10 +312,10 @@ BuildRequires: libpwquality-devel
BuildRequires: libsss_idmap-devel
BuildRequires: libsss_certmap-devel
BuildRequires: libsss_nss_idmap-devel >= %{sssd_version}
%if 0%{?fedora} >= 41
%if 0%{?fedora} >= 41 || 0%{?rhel} >= 10
# Do not use nodejs22 on fedora < 41, https://pagure.io/freeipa/issue/9643
BuildRequires: nodejs(abi)
%elif 0%{?fedora} >= 39 || 0%{?rhel} >= 10
%elif 0%{?fedora} >= 39
# Do not use nodejs20 on fedora < 39, https://pagure.io/freeipa/issue/9374
BuildRequires: nodejs(abi) < 127
%else
@ -1870,6 +1882,13 @@ fi
%endif
%changelog
* Mon Oct 21 2024 Florence Blanc-Renaud <flo@redhat.com> - 4.12.2-4
- Related: RHEL-59777 Rebase Samba to the latest 4.21.x release
- Resolves: RHEL-59659 ipa dns-zone --allow-query '!198.18.2.0/24;any;' fails with Unrecognized IPAddress flags
- Resolves: RHEL-61636 Uninstall ACME separately during PKI uninstallation
- Resolves: RHEL-61723 Include latest fixes in python3-ipatests packages
- Resolves: RHEL-63325 Last expired OTP token would be considered as still assigned to the user
* Tue Sep 24 2024 Rafael Guteres Jeffman <rjeffman@redhat.com> - 4.12.2-3
- Resolves: RHEL-33818 Remove python3-ipalib's dependency on python3-netifaces