diff --git a/0014-test_install-restart-services-after-date-change.patch b/0014-test_install-restart-services-after-date-change.patch new file mode 100644 index 0000000..cd4bcd7 --- /dev/null +++ b/0014-test_install-restart-services-after-date-change.patch @@ -0,0 +1,43 @@ +From c7f999599efe9f3f237f8ad3b7c739714051e3e9 Mon Sep 17 00:00:00 2001 +From: Florence Blanc-Renaud +Date: Thu, 7 Dec 2023 08:35:45 +0100 +Subject: [PATCH] test_install: restart services after date change + +The test TestKRAinstallAfterCertRenew is moving the +date in the future in order to reach the grace period where +certmonger detects some certificates need to be renewed. +Restart the services after the date change. + +Fixes: https://pagure.io/freeipa/issue/9405 + +Signed-off-by: Florence Blanc-Renaud +Reviewed-By: Michal Polovka +--- + ipatests/test_integration/test_installation.py | 4 ++++ + 1 file changed, 4 insertions(+) + +diff --git a/ipatests/test_integration/test_installation.py b/ipatests/test_integration/test_installation.py +index bf4163abc0f138ed42c639eee3e95df52da43a71..02fa5bc56d63421c28a5dd1fa02f9f75d305e7bf 100644 +--- a/ipatests/test_integration/test_installation.py ++++ b/ipatests/test_integration/test_installation.py +@@ -1569,6 +1569,8 @@ class TestKRAinstallAfterCertRenew(IntegrationTest): + grace_date = cert_expiry - timedelta(days=10) + grace_date = datetime.strftime(grace_date, "%Y-%m-%d %H:%M:%S") + self.master.run_command(['date', '-s', grace_date]) ++ # restart service after date change ++ self.master.run_command(['ipactl', 'restart']) + + # get the count of certs track by certmonger + cmd = self.master.run_command(['getcert', 'list']) +@@ -1591,6 +1593,8 @@ class TestKRAinstallAfterCertRenew(IntegrationTest): + cert_expiry = cert_expiry + timedelta(days=3) + cert_expiry = datetime.strftime(cert_expiry, "%Y-%m-%d %H:%M:%S") + self.master.run_command(['date', '-s', cert_expiry]) ++ # restart service after date change ++ self.master.run_command(['ipactl', 'restart']) + + passwd = "{passwd}\n{passwd}\n{passwd}".format(passwd=admin_pass) + self.master.run_command(['kinit', 'admin'], stdin_text=passwd) +-- +2.43.0 + diff --git a/0015-Issue-9497-Add-new-password-policy-logging-function.patch b/0015-Issue-9497-Add-new-password-policy-logging-function.patch new file mode 100644 index 0000000..1512cfc --- /dev/null +++ b/0015-Issue-9497-Add-new-password-policy-logging-function.patch @@ -0,0 +1,40 @@ +From eabdbbc00613963deffe42ea17dfb0a690c62e3f Mon Sep 17 00:00:00 2001 +From: Mark Reynolds +Date: Tue, 12 Dec 2023 08:34:44 -0500 +Subject: [PATCH] Issue 9497 - Add new password policy logging function + +Fixes: https://pagure.io/freeipa/issue/9497 + +Signed-off-by: Mark Reynolds +Reviewed-By: Alexander Bokovoy +--- + daemons/ipa-slapi-plugins/common/util.h | 6 +++++- + 1 file changed, 5 insertions(+), 1 deletion(-) + +diff --git a/daemons/ipa-slapi-plugins/common/util.h b/daemons/ipa-slapi-plugins/common/util.h +index 1eaf47facb717fe6a95d89fe02311205eabc3e96..db7cf7181ceaf710a5a082c4e80eb66567180be5 100644 +--- a/daemons/ipa-slapi-plugins/common/util.h ++++ b/daemons/ipa-slapi-plugins/common/util.h +@@ -30,7 +30,7 @@ + * Program may make changes or additions to the list of Approved + * Interfaces. + * +- * Copyright (C) 2010 Red Hat, Inc. ++ * Copyright (C) 2010-2023 Red Hat, Inc. + * All rights reserved. + * END COPYRIGHT BLOCK **/ + +@@ -67,6 +67,10 @@ + "[file %s, line %d]: " fmt, \ + __FILE__, __LINE__, ##__VA_ARGS__) + ++#define LOG_PWDPOLICY(fmt, ...) \ ++ slapi_log_error(SLAPI_LOG_PWDPOLICY, log_func, fmt, ##__VA_ARGS__) ++ ++/* "Trace" logging is very expensive and should be avoided/replaced. TBD */ + #define LOG_TRACE(fmt, ...) \ + slapi_log_error(SLAPI_LOG_TRACE, log_func, fmt, ##__VA_ARGS__) + +-- +2.43.0 + diff --git a/0016-Issue-9497-Update-logging-in-ipa_enrollment.patch b/0016-Issue-9497-Update-logging-in-ipa_enrollment.patch new file mode 100644 index 0000000..ae5ef17 --- /dev/null +++ b/0016-Issue-9497-Update-logging-in-ipa_enrollment.patch @@ -0,0 +1,68 @@ +From 7e31f111c83bed966157b0660e9640e18450b1a2 Mon Sep 17 00:00:00 2001 +From: Mark Reynolds +Date: Tue, 12 Dec 2023 08:36:49 -0500 +Subject: [PATCH] Issue 9497 - Update logging in ipa_enrollment + +Fixes: https://pagure.io/freeipa/issue/9497 + +Signed-off-by: Mark Reynolds +Reviewed-By: Alexander Bokovoy +--- + .../ipa-enrollment/ipa_enrollment.c | 13 +++++++------ + 1 file changed, 7 insertions(+), 6 deletions(-) + +diff --git a/daemons/ipa-slapi-plugins/ipa-enrollment/ipa_enrollment.c b/daemons/ipa-slapi-plugins/ipa-enrollment/ipa_enrollment.c +index 26cbb69d713767909fd62fb77e7defdd323ec7ac..b72ad5ef1c81997d89b2f94528da516b5df3d285 100644 +--- a/daemons/ipa-slapi-plugins/ipa-enrollment/ipa_enrollment.c ++++ b/daemons/ipa-slapi-plugins/ipa-enrollment/ipa_enrollment.c +@@ -30,7 +30,7 @@ + * Program may make changes or additions to the list of Approved + * Interfaces. + * +- * Copyright (C) 2005 Red Hat, Inc. ++ * Copyright (C) 2005-2023 Red Hat, Inc. + * All rights reserved. + * END COPYRIGHT BLOCK **/ + +@@ -132,7 +132,8 @@ ipa_join(Slapi_PBlock *pb) + Slapi_DN *sdn; + Slapi_Backend *be; + Slapi_Entry **es = NULL; +- int rc=0, ret=0, res, i; ++ int rc=0, ret=0, res; ++ size_t i; + int is_root=0; + char *krbLastPwdChange = NULL; + char *fqdn = NULL; +@@ -204,7 +205,7 @@ ipa_join(Slapi_PBlock *pb) + + /* if there is none or more than one, freak out */ + if (i != 1) { +- LOG_TRACE("Too many entries, or entry no found (%d)", i); ++ LOG_TRACE("Too many entries, or entry no found (%lu)\n", i); + if (i == 0) + errMesg = "Host not found.\n"; + else +@@ -217,7 +218,7 @@ ipa_join(Slapi_PBlock *pb) + /* Is this host already enrolled? */ + krbLastPwdChange = slapi_entry_attr_get_charptr(targetEntry, "krbLastPwdChange"); + if (NULL != krbLastPwdChange) { +- LOG_TRACE("Host already enrolled"); ++ LOG_TRACE("Host already enrolled\n"); + errMesg = "Host already enrolled.\n"; + rc = LDAP_OPERATIONS_ERROR; + goto free_and_return; +@@ -313,8 +314,8 @@ done: + ret = slapi_pblock_set(pb, SLAPI_EXT_OP_RET_OID, JOIN_OID); + if (!ret) ret = slapi_pblock_set(pb, SLAPI_EXT_OP_RET_VALUE, &retbval); + if (ret) { +- errMesg = "Could not set return values"; +- LOG("%s\n", errMesg); ++ errMesg = "Could not set return values\n"; ++ LOG("%s", errMesg); + rc = SLAPI_PLUGIN_EXTENDED_SENT_RESULT; + } + +-- +2.43.0 + diff --git a/0017-Issue-9497-update-debug-logging-in-ipa_graceperiod.patch b/0017-Issue-9497-update-debug-logging-in-ipa_graceperiod.patch new file mode 100644 index 0000000..e375136 --- /dev/null +++ b/0017-Issue-9497-update-debug-logging-in-ipa_graceperiod.patch @@ -0,0 +1,47 @@ +From e4aebc121c9242390da86fe6bda3e8c28edfb746 Mon Sep 17 00:00:00 2001 +From: Mark Reynolds +Date: Tue, 12 Dec 2023 08:37:41 -0500 +Subject: [PATCH] Issue 9497 - update debug logging in ipa_graceperiod + +Fixes: https://pagure.io/freeipa/issue/9497 + +Signed-off-by: Mark Reynolds +Reviewed-By: Alexander Bokovoy +--- + daemons/ipa-slapi-plugins/ipa-graceperiod/ipa_graceperiod.c | 6 +++--- + 1 file changed, 3 insertions(+), 3 deletions(-) + +diff --git a/daemons/ipa-slapi-plugins/ipa-graceperiod/ipa_graceperiod.c b/daemons/ipa-slapi-plugins/ipa-graceperiod/ipa_graceperiod.c +index 345e1dee7d163167373ca82dedb1e827f0e1bc8c..7a2d4f2aaea677d1fb3553fe49e6aa17c3e7a38c 100644 +--- a/daemons/ipa-slapi-plugins/ipa-graceperiod/ipa_graceperiod.c ++++ b/daemons/ipa-slapi-plugins/ipa-graceperiod/ipa_graceperiod.c +@@ -30,7 +30,7 @@ + * Program may make changes or additions to the list of Approved + * Interfaces. + * +- * Copyright (C) 2022 Red Hat, Inc. ++ * Copyright (C) 2022-2023 Red Hat, Inc. + * All rights reserved. + * END COPYRIGHT BLOCK **/ + +@@ -447,7 +447,7 @@ static int ipagraceperiod_preop(Slapi_PBlock *pb) + LOG_TRACE("grace limit disabled, skipping\n"); + goto done; + } else if (grace_limit < -1) { +- LOG_FATAL("Invalid passwordGraceLimit value %d\n", grace_limit); ++ LOG_FATAL("Invalid passwordGraceLimit value %ld\n", grace_limit); + return LDAP_OPERATIONS_ERROR; + } + +@@ -480,7 +480,7 @@ static int ipagraceperiod_preop(Slapi_PBlock *pb) + slapi_pwpolicy_make_response_control(pb, -1, grace_limit - grace_user_time , -1); + } + } else if (grace_user_time >= grace_limit) { +- LOG_TRACE("%s password is expired and out of grace limit\n", dn); ++ LOG_PWDPOLICY("%s password is expired and out of grace limit\n", dn); + errstr = "Password is expired.\n"; + ret = LDAP_INVALID_CREDENTIALS; + +-- +2.43.0 + diff --git a/0018-Issue-9497-update-debug-logging-in-ipa_lockout.patch b/0018-Issue-9497-update-debug-logging-in-ipa_lockout.patch new file mode 100644 index 0000000..ae8554f --- /dev/null +++ b/0018-Issue-9497-update-debug-logging-in-ipa_lockout.patch @@ -0,0 +1,46 @@ +From be805c1150fd0c2e6ac2276f8535b14d57557aad Mon Sep 17 00:00:00 2001 +From: Mark Reynolds +Date: Tue, 12 Dec 2023 08:38:47 -0500 +Subject: [PATCH] Issue 9497 - update debug logging in ipa_lockout + +Fixes: https://pagure.io/freeipa/issue/9497 + +Signed-off-by: Mark Reynolds +Reviewed-By: Alexander Bokovoy +--- + daemons/ipa-slapi-plugins/ipa-lockout/ipa_lockout.c | 6 ++++-- + 1 file changed, 4 insertions(+), 2 deletions(-) + +diff --git a/daemons/ipa-slapi-plugins/ipa-lockout/ipa_lockout.c b/daemons/ipa-slapi-plugins/ipa-lockout/ipa_lockout.c +index a8095ccd371bfd29e3148ab2ad8c982a08f0b7e0..366018094bdc42c914d7743a89519ba1e1a6e124 100644 +--- a/daemons/ipa-slapi-plugins/ipa-lockout/ipa_lockout.c ++++ b/daemons/ipa-slapi-plugins/ipa-lockout/ipa_lockout.c +@@ -30,7 +30,7 @@ + * Program may make changes or additions to the list of Approved + * Interfaces. + * +- * Copyright (C) 2010 Red Hat, Inc. ++ * Copyright (C) 2010-2023 Red Hat, Inc. + * All rights reserved. + * END COPYRIGHT BLOCK **/ + +@@ -823,13 +823,15 @@ static int ipalockout_preop(Slapi_PBlock *pb) + if (failedcount >= max_fail) { + if (lockout_duration == 0) { + errstr = "Entry permanently locked.\n"; ++ LOG_PWDPOLICY("Entry '%s' is permanently locked.\n", dn); + ret = LDAP_UNWILLING_TO_PERFORM; + goto done; + } + + if (time_now < last_failed + lockout_duration) { + /* Too many failures */ +- LOG_TRACE("Too many failed logins. %lu out of %d\n", failedcount, max_fail); ++ LOG_PWDPOLICY("Too many failed logins for '%s'. %lu out of %d\n", ++ dn, failedcount, max_fail); + errstr = "Too many failed logins.\n"; + ret = LDAP_UNWILLING_TO_PERFORM; + } +-- +2.43.0 + diff --git a/0019-Issue-9497-update-debug-logging-in-ipa_modrdn.patch b/0019-Issue-9497-update-debug-logging-in-ipa_modrdn.patch new file mode 100644 index 0000000..df1c28a --- /dev/null +++ b/0019-Issue-9497-update-debug-logging-in-ipa_modrdn.patch @@ -0,0 +1,56 @@ +From 473b1e465ba93ec313bc7cea62bb8d545f37e8bd Mon Sep 17 00:00:00 2001 +From: Mark Reynolds +Date: Tue, 12 Dec 2023 08:39:14 -0500 +Subject: [PATCH] Issue 9497 - update debug logging in ipa_modrdn + +Fixes: https://pagure.io/freeipa/issue/9497 + +Signed-off-by: Mark Reynolds +Reviewed-By: Alexander Bokovoy +--- + daemons/ipa-slapi-plugins/ipa-modrdn/ipa_modrdn.c | 8 ++++---- + 1 file changed, 4 insertions(+), 4 deletions(-) + +diff --git a/daemons/ipa-slapi-plugins/ipa-modrdn/ipa_modrdn.c b/daemons/ipa-slapi-plugins/ipa-modrdn/ipa_modrdn.c +index 6cec5f242b7d23d3752e5bc30c67e034abc96abb..8be192a5e94211f94a7f3a8a62409250b723ddb5 100644 +--- a/daemons/ipa-slapi-plugins/ipa-modrdn/ipa_modrdn.c ++++ b/daemons/ipa-slapi-plugins/ipa-modrdn/ipa_modrdn.c +@@ -30,7 +30,7 @@ + * Program may make changes or additions to the list of Approved + * Interfaces. + * +- * Copyright (C) 2010 Red Hat, Inc. ++ * Copyright (C) 2010-2023 Red Hat, Inc. + * All rights reserved. + * END COPYRIGHT BLOCK **/ + +@@ -350,7 +350,6 @@ ipamodrdn_load_plugin_config(void) + { + int status = EOK; + int result; +- int i; + Slapi_PBlock *search_pb; + Slapi_Entry **entries = NULL; + +@@ -379,7 +378,7 @@ ipamodrdn_load_plugin_config(void) + goto cleanup; + } + +- for (i = 0; (entries[i] != NULL); i++) { ++ for (size_t i = 0; (entries[i] != NULL); i++) { + /* We don't care about the status here because we may have + * some invalid config entries, but we just want to continue + * looking for valid ones. */ +@@ -680,7 +679,8 @@ ipamodrdn_change_attr(struct configEntry *cfgentry, + slapi_modify_internal_pb(mod_pb); + slapi_pblock_get(mod_pb, SLAPI_PLUGIN_INTOP_RESULT, &ret); + if (ret != LDAP_SUCCESS) { +- LOG_FATAL("Failed to change attribute with error %d\n", ret); ++ LOG_FATAL("Failed to change attribute '%s' in '%s' with error %d\n", ++ cfgentry->tattr, targetdn, ret); + ret = EFAIL; + } + ret = EOK; +-- +2.43.0 + diff --git a/0020-Issue-9497-update-debug-logging-in-ipa_otp_counter.patch b/0020-Issue-9497-update-debug-logging-in-ipa_otp_counter.patch new file mode 100644 index 0000000..5713c8a --- /dev/null +++ b/0020-Issue-9497-update-debug-logging-in-ipa_otp_counter.patch @@ -0,0 +1,82 @@ +From 0f78feeca51a7abe49fbabf22991bf89eba7b12a Mon Sep 17 00:00:00 2001 +From: Mark Reynolds +Date: Tue, 12 Dec 2023 08:39:47 -0500 +Subject: [PATCH] Issue 9497 - update debug logging in ipa_otp_counter + +Fixes: https://pagure.io/freeipa/issue/9497 + +Signed-off-by: Mark Reynolds +Reviewed-By: Alexander Bokovoy +--- + .../ipa-otp-counter/ipa_otp_counter.c | 13 +++++++++---- + daemons/ipa-slapi-plugins/ipa-otp-counter/ldapmod.h | 4 +++- + 2 files changed, 12 insertions(+), 5 deletions(-) + +diff --git a/daemons/ipa-slapi-plugins/ipa-otp-counter/ipa_otp_counter.c b/daemons/ipa-slapi-plugins/ipa-otp-counter/ipa_otp_counter.c +index da047d7dc58e27b37ad29c39bde44e33602ab4c5..5e03450c5164ee450736fc61b40ef769bc4572dd 100644 +--- a/daemons/ipa-slapi-plugins/ipa-otp-counter/ipa_otp_counter.c ++++ b/daemons/ipa-slapi-plugins/ipa-otp-counter/ipa_otp_counter.c +@@ -33,7 +33,7 @@ + * Authors: + * Nathaniel McCallum + * +- * Copyright (C) 2014 Red Hat, Inc. ++ * Copyright (C) 2014-2023 Red Hat, Inc. + * All rights reserved. + * END COPYRIGHT BLOCK **/ + +@@ -295,14 +295,16 @@ preop_mod(Slapi_PBlock *pb) + } + + if (!simulate(mods, attr, cpre, &cpost) && repl == 0) { +- msg = slapi_ch_smprintf("Invalid operation sequence on %s", attr); ++ msg = slapi_ch_smprintf("Invalid operation sequence on %s (%s)", ++ attr, slapi_entry_get_dn_const(epre)); + goto error; + } + + if (cpost < cpre) { + if (repl == 0) { +- msg = slapi_ch_smprintf("Will not %s %s", +- cpost == COUNTER_UNSET ? "delete" : "decrement", attr); ++ msg = slapi_ch_smprintf("Will not %s %s (%s)", ++ cpost == COUNTER_UNSET ? "delete" : "decrement", ++ attr, slapi_entry_get_dn_const(epre)); + goto error; + } + +@@ -321,6 +323,9 @@ preop_mod(Slapi_PBlock *pb) + + error: + rc = LDAP_UNWILLING_TO_PERFORM; ++ if (msg) { ++ LOG("%s - error %d\n", msg, rc); ++ } + slapi_send_ldap_result(pb, rc, NULL, msg, 0, NULL); + if (slapi_pblock_set(pb, SLAPI_RESULT_CODE, &rc)) { + LOG_FATAL("slapi_pblock_set failed!\n"); +diff --git a/daemons/ipa-slapi-plugins/ipa-otp-counter/ldapmod.h b/daemons/ipa-slapi-plugins/ipa-otp-counter/ldapmod.h +index 45f43904b2288a97802ad2d698a30be972e2d8b7..324107c487f53e11774c51f248b00043e44b0bcc 100644 +--- a/daemons/ipa-slapi-plugins/ipa-otp-counter/ldapmod.h ++++ b/daemons/ipa-slapi-plugins/ipa-otp-counter/ldapmod.h +@@ -33,7 +33,7 @@ + * Authors: + * Nathaniel McCallum + * +- * Copyright (C) 2014 Red Hat, Inc. ++ * Copyright (C) 2014-2023 Red Hat, Inc. + * All rights reserved. + * END COPYRIGHT BLOCK **/ + +@@ -41,6 +41,8 @@ + + #include + ++#define IPA_PLUGIN_NAME "ipa-otp-counter" ++ + long long + ldapmod_get_value(const LDAPMod *mod, long long def); + +-- +2.43.0 + diff --git a/0021-Issue-9497-update-debug-logging-in-ipa_otp_lasttoken.patch b/0021-Issue-9497-update-debug-logging-in-ipa_otp_lasttoken.patch new file mode 100644 index 0000000..1d82729 --- /dev/null +++ b/0021-Issue-9497-update-debug-logging-in-ipa_otp_lasttoken.patch @@ -0,0 +1,96 @@ +From 5eb6af01873d0f70ff5b02c972867877da8e7c50 Mon Sep 17 00:00:00 2001 +From: Mark Reynolds +Date: Tue, 12 Dec 2023 08:40:13 -0500 +Subject: [PATCH] Issue 9497 - update debug logging in ipa_otp_lasttoken + +Fixes: https://pagure.io/freeipa/issue/9497 + +Signed-off-by: Mark Reynolds +Reviewed-By: Alexander Bokovoy +--- + .../ipa-otp-lasttoken/ipa_otp_lasttoken.c | 25 ++++++++++++------- + 1 file changed, 16 insertions(+), 9 deletions(-) + +diff --git a/daemons/ipa-slapi-plugins/ipa-otp-lasttoken/ipa_otp_lasttoken.c b/daemons/ipa-slapi-plugins/ipa-otp-lasttoken/ipa_otp_lasttoken.c +index 11106b239f9de9074125979cfae7c02e434936e1..c1318f8eb19a5ff7da016eb145eece2f56925235 100644 +--- a/daemons/ipa-slapi-plugins/ipa-otp-lasttoken/ipa_otp_lasttoken.c ++++ b/daemons/ipa-slapi-plugins/ipa-otp-lasttoken/ipa_otp_lasttoken.c +@@ -33,7 +33,7 @@ + * Authors: + * Nathaniel McCallum + * +- * Copyright (C) 2013 Red Hat, Inc. ++ * Copyright (C) 2013-2023 Red Hat, Inc. + * All rights reserved. + * END COPYRIGHT BLOCK **/ + +@@ -46,7 +46,7 @@ + + #include "util.h" + +-#define PLUGIN_NAME "ipa-otp-lasttoken" ++#define IPA_PLUGIN_NAME "ipa-otp-lasttoken" + #define OTP_CONTAINER "cn=otp,%s" + + static struct otp_config *otp_config; +@@ -191,9 +191,14 @@ static inline int send_error(Slapi_PBlock *pb, int rc, const char *errstr) + + static int preop_del(Slapi_PBlock *pb) + { ++ char *dn = NULL; ++ + if (is_allowed(pb, NULL)) + return 0; + ++ slapi_pblock_get(pb, SLAPI_TARGET_DN, &dn); ++ LOG("Can't delete last active token (%s)", dn); ++ + return send_error(pb, LDAP_UNWILLING_TO_PERFORM, + "Can't delete last active token"); + } +@@ -221,10 +226,12 @@ static int preop_mod(Slapi_PBlock *pb) + return 0; + + /* If a protected attribute is modified, deny. */ +- for (int i = 0; mods != NULL && mods[i] != NULL; i++) { +- for (int j = 0; errors[j].attr != NULL; j++) { +- if (strcasecmp(mods[i]->mod_type, errors[j].attr) == 0) ++ for (size_t i = 0; mods != NULL && mods[i] != NULL; i++) { ++ for (size_t j = 0; errors[j].attr != NULL; j++) { ++ if (strcasecmp(mods[i]->mod_type, errors[j].attr) == 0) { ++ LOG("%s (%s)", errors[j].msg, slapi_entry_get_dn_const(entry)); + return send_error(pb, LDAP_UNWILLING_TO_PERFORM, errors[j].msg); ++ } + } + } + +@@ -284,7 +291,7 @@ static int ipa_otp_lasttoken_start(Slapi_PBlock *pb) + int ipa_otp_lasttoken_init(Slapi_PBlock *pb) + { + static const Slapi_PluginDesc preop_desc = { +- PLUGIN_NAME, ++ IPA_PLUGIN_NAME, + "FreeIPA", + "FreeIPA/1.0", + "Protect the user's last active token" +@@ -297,14 +304,14 @@ int ipa_otp_lasttoken_init(Slapi_PBlock *pb) + ret |= slapi_pblock_set(pb, SLAPI_PLUGIN_VERSION, SLAPI_PLUGIN_VERSION_01); + ret |= slapi_pblock_set(pb, SLAPI_PLUGIN_DESCRIPTION, (void *) &preop_desc); + ret |= slapi_register_plugin("betxnpreoperation", 1, __func__, preop_init, +- PLUGIN_NAME " betxnpreoperation", NULL, ++ IPA_PLUGIN_NAME " betxnpreoperation", NULL, + ipa_otp_lasttoken_plugin_id); + ret |= slapi_register_plugin("postoperation", 1, __func__, postop_init, +- PLUGIN_NAME " postoperation", NULL, ++ IPA_PLUGIN_NAME " postoperation", NULL, + ipa_otp_lasttoken_plugin_id); + ret |= slapi_register_plugin("internalpostoperation", 1, __func__, + intpostop_init, +- PLUGIN_NAME " internalpostoperation", NULL, ++ IPA_PLUGIN_NAME " internalpostoperation", NULL, + ipa_otp_lasttoken_plugin_id); + ret |= slapi_pblock_set(pb, SLAPI_PLUGIN_START_FN, + (void *)ipa_otp_lasttoken_start); +-- +2.43.0 + diff --git a/0022-Issue-9497-update-debug-logging-in-ipa-pwd-extop.patch b/0022-Issue-9497-update-debug-logging-in-ipa-pwd-extop.patch new file mode 100644 index 0000000..602b81c --- /dev/null +++ b/0022-Issue-9497-update-debug-logging-in-ipa-pwd-extop.patch @@ -0,0 +1,766 @@ +From 3a8fe8c3a9de8d0e17ab4064ac689bce2b4b5042 Mon Sep 17 00:00:00 2001 +From: Mark Reynolds +Date: Tue, 12 Dec 2023 08:41:10 -0500 +Subject: [PATCH] Issue 9497 - update debug logging in ipa-pwd-extop + +Fixes: https://pagure.io/freeipa/issue/9497 + +Signed-off-by: Mark Reynolds +Reviewed-By: Alexander Bokovoy +--- + .../ipa-slapi-plugins/ipa-pwd-extop/common.c | 25 +++-- + .../ipa-pwd-extop/encoding.c | 5 +- + .../ipa-pwd-extop/ipa_pwd_extop.c | 106 ++++++++++-------- + .../ipa-slapi-plugins/ipa-pwd-extop/prepost.c | 59 +++++----- + 4 files changed, 105 insertions(+), 90 deletions(-) + +diff --git a/daemons/ipa-slapi-plugins/ipa-pwd-extop/common.c b/daemons/ipa-slapi-plugins/ipa-pwd-extop/common.c +index 5251713c68855e10b0980af71696d944e683ae90..d30764bb2a05c7ca4a33ea114a2dc19af39e216f 100644 +--- a/daemons/ipa-slapi-plugins/ipa-pwd-extop/common.c ++++ b/daemons/ipa-slapi-plugins/ipa-pwd-extop/common.c +@@ -33,7 +33,7 @@ + * Authors: + * Simo Sorce + * +- * Copyright (C) 2007-2010 Red Hat, Inc. ++ * Copyright (C) 2007-2023 Red Hat, Inc. + * All rights reserved. + * END COPYRIGHT BLOCK **/ + +@@ -81,7 +81,8 @@ static struct ipapwd_krbcfg *ipapwd_getConfig(void) + char **encsalts; + char **tmparray; + char *tmpstr; +- int i, ret; ++ int ret; ++ size_t i; + + config = calloc(1, sizeof(struct ipapwd_krbcfg)); + if (!config) { +@@ -327,7 +328,8 @@ int ipapwd_getPolicy(const char *dn, + "ipaPwdUserCheck", NULL}; + Slapi_Entry **es = NULL; + Slapi_Entry *pe = NULL; +- int ret, res, scope, i; ++ int ret, res, scope; ++ size_t i; + int buffer_flags=0; + Slapi_ValueSet* results = NULL; + char *actual_type_name = NULL; +@@ -545,7 +547,7 @@ int ipapwd_gen_checks(Slapi_PBlock *pb, char **errMesg, + } + sdn = slapi_sdn_new_dn_byref(dn); + if (!sdn) { +- LOG_FATAL("Unable to convert dn to sdn %s", dn ? dn : ""); ++ LOG_FATAL("Unable to convert dn to sdn %s\n", dn ? dn : ""); + *errMesg = "Internal Error"; + rc = LDAP_OPERATIONS_ERROR; + goto done; +@@ -564,7 +566,7 @@ int ipapwd_gen_checks(Slapi_PBlock *pb, char **errMesg, + /* get the kerberos context and master key */ + *config = ipapwd_getConfig(); + if (NULL == *config) { +- LOG_FATAL("Error Retrieving Master Key"); ++ LOG_FATAL("Error Retrieving Master Key\n"); + *errMesg = "Fatal Internal Error"; + rc = LDAP_OPERATIONS_ERROR; + } +@@ -594,7 +596,7 @@ int ipapwd_CheckPolicy(struct ipapwd_data *data) + /* Find the entry with the password policy */ + ret = ipapwd_getPolicy(data->dn, data->target, &pol); + if (ret) { +- LOG_TRACE("No password policy, use defaults"); ++ LOG_TRACE("No password policy, use defaults\n"); + } + break; + case IPA_CHANGETYPE_ADMIN: +@@ -620,14 +622,14 @@ int ipapwd_CheckPolicy(struct ipapwd_data *data) + */ + ret = ipapwd_getPolicy(data->dn, data->target, &tmppol); + if (ret) { +- LOG_TRACE("No password policy, use defaults"); ++ LOG_TRACE("No password policy, use defaults\n"); + } else { + pol.max_pwd_life = tmppol.max_pwd_life; + pol.history_length = tmppol.history_length; + } + break; + default: +- LOG_TRACE("Unknown password change type, use defaults"); ++ LOG_TRACE("Unknown password change type, use defaults\n"); + break; + } + +@@ -860,7 +862,7 @@ int ipapwd_SetPassword(struct ipapwd_krbcfg *krbcfg, + case IPA_CHANGETYPE_DSMGR: + case IPA_CHANGETYPE_ADMIN: + /* Mark as administratively reset which will unlock acct */ +- ret = ipapwd_setdate(data->target, smods, ++ ret = ipapwd_setdate(data->target, smods, + "krbLastAdminUnlock", + data->timeNow, false); + if (ret != LDAP_SUCCESS) +@@ -951,7 +953,7 @@ Slapi_Value **ipapwd_setPasswordHistory(Slapi_Mods *smods, + char **new_pwd_history = NULL; + int n = 0; + int ret; +- int i; ++ size_t i; + + pwd_history = slapi_entry_attr_get_charray(data->target, + "passwordHistory"); +@@ -1083,10 +1085,9 @@ int ipapwd_set_extradata(const char *dn, + void ipapwd_free_slapi_value_array(Slapi_Value ***svals) + { + Slapi_Value **sv = *svals; +- int i; + + if (sv) { +- for (i = 0; sv[i]; i++) { ++ for (size_t i = 0; sv[i]; i++) { + slapi_value_free(&sv[i]); + } + } +diff --git a/daemons/ipa-slapi-plugins/ipa-pwd-extop/encoding.c b/daemons/ipa-slapi-plugins/ipa-pwd-extop/encoding.c +index 7b2f341229b4f3bf48105c3856c0d6778da154a5..43ae6f0a645c8f3ff0fa2d147891f93efff0eb20 100644 +--- a/daemons/ipa-slapi-plugins/ipa-pwd-extop/encoding.c ++++ b/daemons/ipa-slapi-plugins/ipa-pwd-extop/encoding.c +@@ -33,7 +33,7 @@ + * Authors: + * Simo Sorce + * +- * Copyright (C) 2007-2010 Red Hat, Inc. ++ * Copyright (C) 2007-2023 Red Hat, Inc. + * All rights reserved. + * END COPYRIGHT BLOCK **/ + +@@ -231,7 +231,7 @@ int ipapwd_gen_hashes(struct ipapwd_krbcfg *krbcfg, + + if (!*svals) { + /* errMesg should have been set in encrypt_encode_key() */ +- LOG_FATAL("key encryption/encoding failed\n"); ++ LOG_FATAL("key encryption/encoding failed (%s)\n", *errMesg); + rc = LDAP_OPERATIONS_ERROR; + goto done; + } +@@ -267,6 +267,7 @@ int ipapwd_gen_hashes(struct ipapwd_krbcfg *krbcfg, + } + (*ntvals)[0] = slapi_value_new(); + if (slapi_value_set((*ntvals)[0], nt_key, 16) == NULL) { ++ LOG("Failed to set value for nt_key"); + rc = LDAP_OPERATIONS_ERROR; + goto done; + } +diff --git a/daemons/ipa-slapi-plugins/ipa-pwd-extop/ipa_pwd_extop.c b/daemons/ipa-slapi-plugins/ipa-pwd-extop/ipa_pwd_extop.c +index 0d630ca04c38b739bb0d8bf22c162af9d3e15566..43c31becae45c1c91c7c2adf498aedbd05af9a69 100644 +--- a/daemons/ipa-slapi-plugins/ipa-pwd-extop/ipa_pwd_extop.c ++++ b/daemons/ipa-slapi-plugins/ipa-pwd-extop/ipa_pwd_extop.c +@@ -33,7 +33,7 @@ + * Authors: + * Simo Sorce + * +- * Copyright (C) 2007-2010 Red Hat, Inc. ++ * Copyright (C) 2007-2023 Red Hat, Inc. + * All rights reserved. + * END COPYRIGHT BLOCK **/ + +@@ -108,7 +108,7 @@ static void filter_keys(struct ipapwd_krbcfg *krbcfg, + struct ipapwd_keyset *kset, + bool allow_nthash) + { +- int i, j; ++ size_t i, j; + + for (i = 0; i < kset->num_keys; i++) { + for (j = 0; j < krbcfg->num_supp_encsalts; j++) { +@@ -151,11 +151,11 @@ static void filter_enctypes(struct ipapwd_krbcfg *krbcfg, + bool allow_nthash) + { + /* first filter for duplicates */ +- for (int i = 0; i + 1 < *num_kenctypes; i++) { +- for (int j = i + 1; j < *num_kenctypes; j++) { ++ for (size_t i = 0; i + 1 < *num_kenctypes; i++) { ++ for (size_t j = i + 1; j < *num_kenctypes; j++) { + if (kenctypes[i].ks_enctype == kenctypes[j].ks_enctype) { + /* duplicate, filter out */ +- for (int k = j; k + 1 < *num_kenctypes; k++) { ++ for (size_t k = j; k + 1 < *num_kenctypes; k++) { + kenctypes[k].ks_enctype = kenctypes[k + 1].ks_enctype; + kenctypes[k].ks_salttype = kenctypes[k + 1].ks_salttype; + } +@@ -166,8 +166,8 @@ static void filter_enctypes(struct ipapwd_krbcfg *krbcfg, + } + + /* then filter for supported */ +- for (int i = 0; i < *num_kenctypes; i++) { +- int j; ++ for (size_t i = 0; i < *num_kenctypes; i++) { ++ size_t j; + + /* Check if supported */ + for (j = 0; j < krbcfg->num_supp_encsalts; j++) { +@@ -184,7 +184,7 @@ static void filter_enctypes(struct ipapwd_krbcfg *krbcfg, + } + if (j == krbcfg->num_supp_encsalts) { + /* Unsupported, filter out */ +- for (int k = i; k + 1 < *num_kenctypes; k++) { ++ for (size_t k = i; k + 1 < *num_kenctypes; k++) { + kenctypes[k].ks_enctype = kenctypes[k + 1].ks_enctype; + kenctypes[k].ks_salttype = kenctypes[k + 1].ks_salttype; + } +@@ -344,6 +344,8 @@ parse_req_done: + + rc = ipapwd_check_max_pwd_len(strlen(newPasswd), &errMesg); + if (rc) { ++ LOG_PWDPOLICY("Failed to set password credentials for '%s': %s\n", ++ bindDN, errMesg); + goto free_and_return; + } + +@@ -456,7 +458,7 @@ parse_req_done: + char *cur_pw; + + if (oldPasswd == NULL || *oldPasswd == '\0') { +- LOG_FATAL("Old password was not provided!\n"); ++ LOG_FATAL("Old password was not provided for '%s'!\n", dn); + rc = LDAP_INVALID_CREDENTIALS; + goto free_and_return; + } +@@ -466,7 +468,7 @@ parse_req_done: + cur_pw = slapi_entry_attr_get_charptr(targetEntry, + "userPassword"); + if (!cur_pw) { +- LOG_FATAL("User has no current password?\n"); ++ LOG_FATAL("User '%s' does not have a current password?\n", dn); + rc = LDAP_UNWILLING_TO_PERFORM; + goto free_and_return; + } +@@ -485,7 +487,7 @@ parse_req_done: + slapi_value_free(&pw); + + if (ret != 0) { +- LOG_TRACE("Invalid password!\n"); ++ LOG_TRACE("Invalid password for '%s'!\n", dn); + rc = LDAP_INVALID_CREDENTIALS; + goto free_and_return; + } +@@ -579,11 +581,9 @@ parse_req_done: + /* special cases */ + if ((strcasecmp(dn, bindDN) != 0) && + (strcasecmp(ipa_changepw_principal_dn, bindDN) != 0)) { +- int i; +- + pwdata.changetype = IPA_CHANGETYPE_ADMIN; + +- for (i = 0; i < krbcfg->num_passsync_mgrs; i++) { ++ for (size_t i = 0; i < krbcfg->num_passsync_mgrs; i++) { + if (strcasecmp(krbcfg->passsync_mgrs[i], bindDN) == 0) { + pwdata.changetype = IPA_CHANGETYPE_DSMGR; + break; +@@ -606,6 +606,8 @@ parse_req_done: + errMesg = ipapwd_error2string(ret); + ret = ipapwd_to_ldap_pwpolicy_error(ret); + slapi_pwpolicy_make_response_control(pb, -1, -1, ret); ++ LOG_PWDPOLICY("Failed to set password credentials for" ++ " '%s': %s\n", dn, errMesg); + rc = LDAP_CONSTRAINT_VIOLATION; + goto free_and_return; + } +@@ -666,7 +668,7 @@ free_and_return: + if (targetEntry) slapi_entry_free(targetEntry); + if (ber) ber_free(ber, 1); + +- LOG("%s", errMesg ? errMesg : "success"); ++ LOG("%s\n", errMesg ? errMesg : "success"); + slapi_send_ldap_result(pb, rc, NULL, errMesg, 0, NULL); + + return SLAPI_PLUGIN_EXTENDED_SENT_RESULT; +@@ -732,7 +734,8 @@ static Slapi_Entry *get_entry_by_principal(const char *principal) + "krbCanonicalName", + "enrolledBy", NULL }; + Slapi_Entry **es = NULL; +- int res, ret, i; ++ int res, ret; ++ size_t i; + Slapi_Entry *entry = NULL; + + /* Find ancestor base DN */ +@@ -774,7 +777,7 @@ static Slapi_Entry *get_entry_by_principal(const char *principal) + + /* if there is none or more than one, freak out */ + if (i != 1) { +- LOG_TRACE("Too many entries, or entry no found (%d)", i); ++ LOG_TRACE("Too many entries, or entry no found (%ld)\n", i); + goto free_and_return; + } + entry = slapi_entry_dup(es[0]); +@@ -809,7 +812,7 @@ static bool is_allowed_to_access_attr(Slapi_PBlock *pb, char *bindDN, + */ + be = get_realm_backend(); + if (!be) { +- LOG_FATAL("Could not fetch REALM backend!"); ++ LOG_FATAL("Could not fetch REALM backend!\n"); + return false; + } + if (slapi_pblock_set(pb, SLAPI_BACKEND, be)) { +@@ -868,7 +871,8 @@ static void remove_user_password(Slapi_Mods *smods, + if ((NULL != pw) && (NULL == krbLastPwdChange)) { + slapi_mods_add_mod_values(smods, LDAP_MOD_DELETE, + "userPassword", NULL); +- LOG_TRACE("Removing userPassword from host entry\n"); ++ LOG_TRACE("Removing userPassword from host entry '%s'\n", ++ slapi_entry_get_dn_const(targetEntry)); + } + } + if (krbLastPwdChange) slapi_ch_free_string(&krbLastPwdChange); +@@ -891,8 +895,9 @@ static int store_new_keys(Slapi_Entry *target, char *svcname, char *bind_dn, + rc = set_krbLastPwdChange(smods, time_now); + if (rc) { + rc = LDAP_OPERATIONS_ERROR; +- LOG_FATAL("Failed to set krbLastPwdChange"); +- err_msg = "Internal error while storing keytab data\n"; ++ LOG_FATAL("Failed to set krbLastPwdChange for target '%s'\n", ++ slapi_entry_get_dn_const(target)); ++ err_msg = "Internal error while storing keytab data"; + goto done; + } + +@@ -905,8 +910,9 @@ static int store_new_keys(Slapi_Entry *target, char *svcname, char *bind_dn, + rc = ipapwd_apply_mods(slapi_entry_get_dn_const(target), smods); + if (rc != LDAP_SUCCESS) { + rc = LDAP_OPERATIONS_ERROR; +- LOG_FATAL("Failed to apply mods"); +- err_msg = "Internal error while saving keys\n"; ++ LOG_FATAL("Failed to apply mods to target '%s'\n", ++ slapi_entry_get_dn_const(target)); ++ err_msg = "Internal error while saving keys"; + goto done; + } + +@@ -914,8 +920,9 @@ static int store_new_keys(Slapi_Entry *target, char *svcname, char *bind_dn, + svcname, time_now); + if (rc != LDAP_SUCCESS) { + rc = LDAP_OPERATIONS_ERROR; +- LOG_FATAL("Failed to set extradata"); +- err_msg = "Internal error while saving keytab extradata\n"; ++ LOG_FATAL("Failed to set extradata for target '%s'\n", ++ slapi_entry_get_dn_const(target)); ++ err_msg = "Internal error while saving keytab extradata"; + goto done; + } + +@@ -1003,7 +1010,7 @@ static int decode_setkeytab_request(krb5_context krbctx, + kset->mkvno = mkvno; + + rtag = ber_peek_tag(ber, &tlen); +- for (int i = 0; rtag == LBER_SEQUENCE; i++) { ++ for (size_t i = 0; rtag == LBER_SEQUENCE; i++) { + krb5_key_data *newset; + ber_tag_t ctag; + ber_int_t type; +@@ -1181,29 +1188,29 @@ static int encode_setkeytab_reply(struct ipapwd_keyset *kset, + rc = ber_printf(ber, "{i{", (ber_int_t)kset->keys[0].key_data_kvno); + if (rc == -1) { + rc = LDAP_OPERATIONS_ERROR; +- LOG_FATAL("Failed to ber_printf the kvno"); ++ LOG_FATAL("Failed to ber_printf the kvno\n"); + goto done; + } + +- for (int i = 0; i < kset->num_keys; i++) { ++ for (size_t i = 0; i < kset->num_keys; i++) { + rc = ber_printf(ber, "{i}", (ber_int_t)kset->keys[i].key_data_type[0]); + if (rc == -1) { + rc = LDAP_OPERATIONS_ERROR; +- LOG_FATAL("Failed to ber_printf the enctype"); ++ LOG_FATAL("Failed to ber_printf the enctype\n"); + goto done; + } + } + rc = ber_printf(ber, "}}"); + if (rc == -1) { + rc = LDAP_OPERATIONS_ERROR; +- LOG_FATAL("Failed to ber_printf the termination"); ++ LOG_FATAL("Failed to ber_printf the termination\n"); + goto done; + } + + rc = ber_flatten(ber, &bvp); + if (rc == -1) { + rc = LDAP_OPERATIONS_ERROR; +- LOG_FATAL("Failed to ber_flatten the buffer"); ++ LOG_FATAL("Failed to ber_flatten the buffer\n"); + goto done; + } + +@@ -1306,7 +1313,7 @@ static int ipapwd_setkeytab(Slapi_PBlock *pb, struct ipapwd_krbcfg *krbcfg) + + /* get next kvno for entry (will be 1 if this is new) and fix keyset */ + kvno = ipapwd_get_cur_kvno(targetEntry) + 1; +- for (int i = 0; i < kset->num_keys; i++) { ++ for (size_t i = 0; i < kset->num_keys; i++) { + kset->keys[i].key_data_kvno = kvno; + } + +@@ -1352,7 +1359,7 @@ static int ipapwd_setkeytab(Slapi_PBlock *pb, struct ipapwd_krbcfg *krbcfg) + + rc = encode_setkeytab_reply(kset, &bvp); + if (rc) { +- errMesg = "Internal Error.\n"; ++ errMesg = "Internal Error."; + goto free_and_return; + } + +@@ -1372,7 +1379,7 @@ free_and_return: + if (targetEntry) slapi_entry_free(targetEntry); + + if (svals) { +- for (int i = 0; svals[i]; i++) { ++ for (size_t i = 0; svals[i]; i++) { + slapi_value_free(&svals[i]); + } + free(svals); +@@ -1382,7 +1389,7 @@ free_and_return: + + if (rc == LDAP_SUCCESS) + errMesg = NULL; +- LOG("%s", errMesg ? errMesg : "success"); ++ LOG("%s\n", errMesg ? errMesg : "success"); + slapi_send_ldap_result(pb, rc, NULL, errMesg, 0, NULL); + + return SLAPI_PLUGIN_EXTENDED_SENT_RESULT; +@@ -1403,7 +1410,6 @@ static int decode_getkeytab_request(struct berval *extop, bool *wantold, + krb5_key_salt_tuple *enctypes = NULL; + bool newkt; + bool ret; +- int i; + + ret = ipaasn1_dec_getkt(extop->bv_val, extop->bv_len, &newkt, + &svcname, &password, &etypes, &numtypes); +@@ -1423,7 +1429,7 @@ static int decode_getkeytab_request(struct berval *extop, bool *wantold, + goto done; + } + +- for (i = 0; i < numtypes; i++) { ++ for (size_t i = 0; i < numtypes; i++) { + enctypes[i].ks_enctype = etypes[i]; + enctypes[i].ks_salttype = KRB5_KDB_SALTTYPE_NORMAL; + } +@@ -1466,7 +1472,7 @@ static int encode_getkeytab_reply(krb5_context krbctx, + /* uses last key kvno */ + kvno = keys[num_keys-1].key_data_kvno; + +- for (int i = 0; i < num_keys; i++) { ++ for (size_t i = 0; i < num_keys; i++) { + krb5_enc_data cipher = { 0 }; + krb5_data plain = { 0 }; + krb5_int16 plen; +@@ -1516,7 +1522,7 @@ static int encode_getkeytab_reply(krb5_context krbctx, + rc = LDAP_SUCCESS; + + done: +- for (int i = 0; i < ksc.nkeys; i ++) { ++ for (size_t i = 0; i < ksc.nkeys; i++) { + free(ksc.ksdata[i].key.contents); + } + if (rc != LDAP_SUCCESS) { +@@ -1632,7 +1638,7 @@ static int ipapwd_getkeytab(Slapi_PBlock *pb, struct ipapwd_krbcfg *krbcfg) + * this operation. */ + if (bind_dn == NULL || *bind_dn == '\0') { + /* Refuse the operation because they're bound anonymously */ +- err_msg = "Anonymous Binds are not allowed.\n"; ++ err_msg = "Anonymous Binds are not allowed."; + rc = LDAP_INSUFFICIENT_ACCESS; + goto free_and_return; + } +@@ -1648,7 +1654,7 @@ static int ipapwd_getkeytab(Slapi_PBlock *pb, struct ipapwd_krbcfg *krbcfg) + slapi_pblock_get(pb, SLAPI_EXT_OP_REQ_VALUE, &extop_value); + if (!extop_value) { + LOG_FATAL("Failed to retrieve extended op value from pblock\n"); +- err_msg = "Failed to retrieve extended operation value\n"; ++ err_msg = "Failed to retrieve extended operation value"; + rc = LDAP_OPERATIONS_ERROR; + goto free_and_return; + } +@@ -1674,7 +1680,7 @@ static int ipapwd_getkeytab(Slapi_PBlock *pb, struct ipapwd_krbcfg *krbcfg) + /* get Entry by krbPrincipalName */ + target_entry = get_entry_by_principal(service_name); + if (!target_entry) { +- err_msg = "PrincipalName not found.\n"; ++ err_msg = "PrincipalName not found."; + rc = LDAP_NO_SUCH_OBJECT; + goto free_and_return; + } +@@ -1690,7 +1696,7 @@ static int ipapwd_getkeytab(Slapi_PBlock *pb, struct ipapwd_krbcfg *krbcfg) + if (!acl_ok) { + LOG_FATAL("Not allowed to retrieve keytab on [%s] as user [%s]!\n", + service_name, bind_dn); +- err_msg = "Insufficient access rights\n"; ++ err_msg = "Insufficient access rights"; + rc = LDAP_INSUFFICIENT_ACCESS; + goto free_and_return; + } +@@ -1701,6 +1707,8 @@ static int ipapwd_getkeytab(Slapi_PBlock *pb, struct ipapwd_krbcfg *krbcfg) + /* if password was passed-in, check its length */ + rc = ipapwd_check_max_pwd_len(strlen(password), &err_msg); + if (rc) { ++ LOG_PWDPOLICY("Failed to set password credentials for '%s': %s\n", ++ bind_dn, err_msg); + goto free_and_return; + } + } +@@ -1712,7 +1720,7 @@ static int ipapwd_getkeytab(Slapi_PBlock *pb, struct ipapwd_krbcfg *krbcfg) + if (!acl_ok) { + LOG_FATAL("Not allowed to set keytab on [%s]!\n", + service_name); +- err_msg = "Insufficient access rights\n"; ++ err_msg = "Insufficient access rights"; + rc = LDAP_INSUFFICIENT_ACCESS; + goto free_and_return; + } +@@ -1745,7 +1753,7 @@ static int ipapwd_getkeytab(Slapi_PBlock *pb, struct ipapwd_krbcfg *krbcfg) + if (!svals) { + rc = LDAP_OPERATIONS_ERROR; + LOG_FATAL("encrypt_encode_keys failed!\n"); +- err_msg = "Internal error while encrypting keys\n"; ++ err_msg = "Internal error while encrypting keys"; + goto free_and_return; + } + +@@ -1765,7 +1773,7 @@ static int ipapwd_getkeytab(Slapi_PBlock *pb, struct ipapwd_krbcfg *krbcfg) + rc = encode_getkeytab_reply(krbctx, krbcfg->kmkey, mkvno, + keys, num_keys, &bvp); + if (rc != LDAP_SUCCESS) { +- err_msg = "Internal Error.\n"; ++ err_msg = "Internal Error."; + goto free_and_return; + } + +@@ -1776,7 +1784,7 @@ static int ipapwd_getkeytab(Slapi_PBlock *pb, struct ipapwd_krbcfg *krbcfg) + + free_and_return: + if (rc == LDAP_SUCCESS) err_msg = NULL; +- LOG("%s", err_msg ? err_msg : "success"); ++ LOG("%s\n", err_msg ? err_msg : "success"); + slapi_send_ldap_result(pb, rc, NULL, err_msg, 0, NULL); + + /* Free anything that we allocated above */ +@@ -1787,7 +1795,7 @@ free_and_return: + if (target_entry) slapi_entry_free(target_entry); + if (keys) ipa_krb5_free_key_data(keys, num_keys); + if (svals) { +- for (int i = 0; svals[i]; i++) { ++ for (size_t i = 0; svals[i]; i++) { + slapi_value_free(&svals[i]); + } + free(svals); +@@ -2031,7 +2039,7 @@ int ipapwd_init( Slapi_PBlock *pb ) + "ipapwd_post_init_betxn", ipapwd_post_init_betxn, + "IPA pwd post ops betxn", NULL, + ipapwd_plugin_id); +- } ++ } + + slapi_register_plugin("preoperation", 1, + "ipapwd_pre_init", ipapwd_pre_init, +diff --git a/daemons/ipa-slapi-plugins/ipa-pwd-extop/prepost.c b/daemons/ipa-slapi-plugins/ipa-pwd-extop/prepost.c +index 45626523ffa1030cdff4f3e0ccdfa1618a51ccaf..6898e6596e1cbbb2cc69ba592401619ce86899d8 100644 +--- a/daemons/ipa-slapi-plugins/ipa-pwd-extop/prepost.c ++++ b/daemons/ipa-slapi-plugins/ipa-pwd-extop/prepost.c +@@ -33,7 +33,7 @@ + * Authors: + * Simo Sorce + * +- * Copyright (C) 2007-2010 Red Hat, Inc. ++ * Copyright (C) 2007-2023 Red Hat, Inc. + * All rights reserved. + * END COPYRIGHT BLOCK **/ + +@@ -248,6 +248,13 @@ static int ipapwd_pre_add(Slapi_PBlock *pb) + return 0; + } + ++ /* Get target DN */ ++ ret = slapi_pblock_get(pb, SLAPI_TARGET_SDN, &sdn); ++ if (ret) { ++ rc = LDAP_OPERATIONS_ERROR; ++ goto done; ++ } ++ + /* Ok this is interesting, + * Check this is a clear text password, or refuse operation */ + if ('{' == userpw[0]) { +@@ -280,6 +287,8 @@ static int ipapwd_pre_add(Slapi_PBlock *pb) + } else { + rc = ipapwd_check_max_pwd_len(strlen(userpw_clear), &errMesg); + if (rc) { ++ LOG_PWDPOLICY("Failed to set password credentials for '%s': %s\n", ++ slapi_sdn_get_dn(sdn), errMesg); + goto done; + } + userpw = slapi_ch_strdup(userpw_clear); +@@ -329,13 +338,6 @@ static int ipapwd_pre_add(Slapi_PBlock *pb) + goto done; + } + +- /* Get target DN */ +- ret = slapi_pblock_get(pb, SLAPI_TARGET_SDN, &sdn); +- if (ret) { +- rc = LDAP_OPERATIONS_ERROR; +- goto done; +- } +- + /* time to get the operation handler */ + ret = slapi_pblock_get(pb, SLAPI_OPERATION, &op); + if (ret != 0) { +@@ -359,7 +361,6 @@ static int ipapwd_pre_add(Slapi_PBlock *pb) + pwdop->pwdata.changetype = IPA_CHANGETYPE_DSMGR; + } else { + char *binddn; +- int i; + + pwdop->pwdata.changetype = IPA_CHANGETYPE_ADMIN; + +@@ -367,7 +368,7 @@ static int ipapwd_pre_add(Slapi_PBlock *pb) + slapi_pblock_get(pb, SLAPI_CONN_DN, &binddn); + + /* if it is a passsync manager we also need to skip resets */ +- for (i = 0; i < krbcfg->num_passsync_mgrs; i++) { ++ for (size_t i = 0; i < krbcfg->num_passsync_mgrs; i++) { + if (strcasecmp(krbcfg->passsync_mgrs[i], binddn) == 0) { + pwdop->pwdata.changetype = IPA_CHANGETYPE_DSMGR; + break; +@@ -385,6 +386,8 @@ static int ipapwd_pre_add(Slapi_PBlock *pb) + if ((pwdop->pwdata.changetype != IPA_CHANGETYPE_DSMGR) && + (ret != 0) ) { + errMesg = ipapwd_error2string(ret); ++ LOG_PWDPOLICY("Failed to add password credentials for '%s': %s\n", ++ slapi_sdn_get_dn(sdn), errMesg); + rc = LDAP_CONSTRAINT_VIOLATION; + goto done; + } +@@ -507,6 +510,13 @@ static int ipapwd_pre_mod(Slapi_PBlock *pb) + goto done; + } + ++ /* Get target DN */ ++ ret = slapi_pblock_get(pb, SLAPI_TARGET_SDN, &sdn); ++ if (ret) { ++ rc = LDAP_OPERATIONS_ERROR; ++ goto done; ++ } ++ + /* grab the mods - we'll put them back later with + * our modifications appended + */ +@@ -568,6 +578,8 @@ static int ipapwd_pre_mod(Slapi_PBlock *pb) + + rc = ipapwd_check_max_pwd_len(bv->bv_len, &errMesg); + if (rc) { ++ LOG_PWDPOLICY("Failed to set password credentials for '%s': %s\n", ++ slapi_sdn_get_dn(sdn), errMesg); + goto done; + } + slapi_ch_free_string(&unhashedpw); +@@ -591,14 +603,6 @@ static int ipapwd_pre_mod(Slapi_PBlock *pb) + + /* OK we have something interesting here, start checking for + * pre-requisites */ +- +- /* Get target DN */ +- ret = slapi_pblock_get(pb, SLAPI_TARGET_SDN, &sdn); +- if (ret) { +- rc = LDAP_OPERATIONS_ERROR; +- goto done; +- } +- + tmp_sdn = slapi_sdn_dup(sdn); + if (tmp_sdn) { + /* xxxPAR: Ideally SLAPI_MODIFY_EXISTING_ENTRY should be +@@ -795,6 +799,8 @@ static int ipapwd_pre_mod(Slapi_PBlock *pb) + const char *userpw_clear = &userpw[strlen("{CLEAR}")]; + rc = ipapwd_check_max_pwd_len(strlen(userpw_clear), &errMesg); + if (rc) { ++ LOG_PWDPOLICY("Failed to set password credentials for '%s': %s\n", ++ slapi_sdn_get_dn(sdn), errMesg); + goto done; + } + unhashedpw = slapi_ch_strdup(userpw_clear); +@@ -806,9 +812,8 @@ static int ipapwd_pre_mod(Slapi_PBlock *pb) + slapi_ch_free_string(&userpw); + + } else if (slapi_is_encoded(userpw)) { +- +- LOG("Pre-Encoded passwords are not valid\n"); +- errMesg = "Pre-Encoded passwords are not valid\n"; ++ errMesg = "Pre-Encoded passwords are not valid"; ++ LOG("%s (%s)\n", errMesg, slapi_sdn_get_dn(sdn)); + rc = LDAP_CONSTRAINT_VIOLATION; + goto done; + } +@@ -843,7 +848,6 @@ static int ipapwd_pre_mod(Slapi_PBlock *pb) + } else { + char *binddn; + Slapi_DN *bdn, *tdn; +- int i; + + /* Check Bind DN */ + slapi_pblock_get(pb, SLAPI_CONN_DN, &binddn); +@@ -857,18 +861,16 @@ static int ipapwd_pre_mod(Slapi_PBlock *pb) + pwdop->pwdata.changetype = IPA_CHANGETYPE_ADMIN; + + /* if it is a passsync manager we also need to skip resets */ +- for (i = 0; i < krbcfg->num_passsync_mgrs; i++) { ++ for (size_t i = 0; i < krbcfg->num_passsync_mgrs; i++) { + if (strcasecmp(krbcfg->passsync_mgrs[i], binddn) == 0) { + pwdop->pwdata.changetype = IPA_CHANGETYPE_DSMGR; + break; + } + } +- + } + + slapi_sdn_free(&bdn); + slapi_sdn_free(&tdn); +- + } + + pwdop->pwdata.dn = slapi_ch_strdup(slapi_sdn_get_dn(sdn)); +@@ -884,6 +886,8 @@ static int ipapwd_pre_mod(Slapi_PBlock *pb) + if ((pwdop->pwdata.changetype != IPA_CHANGETYPE_DSMGR) && + (ret != 0)) { + errMesg = ipapwd_error2string(ret); ++ LOG_PWDPOLICY("Check Password Policy failed for (%s) - %s/n", ++ pwdop->pwdata.dn, errMesg); + rc = LDAP_CONSTRAINT_VIOLATION; + goto done; + } +@@ -976,7 +980,6 @@ static int ipapwd_regen_nthash(Slapi_PBlock *pb, Slapi_Mods *smods, + int num_keys; + int mkvno; + int ret; +- int i; + + ret = slapi_entry_attr_find(entry, "ipaNTHash", &attr); + if (ret == 0) { +@@ -1008,7 +1011,7 @@ static int ipapwd_regen_nthash(Slapi_PBlock *pb, Slapi_Mods *smods, + + ret = LDAP_UNWILLING_TO_PERFORM; + +- for (i = 0; i < num_keys; i++) { ++ for (size_t i = 0; i < num_keys; i++) { + char nthash[16]; + krb5_enc_data cipher; + krb5_data plain; +@@ -1511,6 +1514,8 @@ static int ipapwd_pre_bind(Slapi_PBlock *pb) + } else { + rc = ipapwd_check_max_pwd_len(credentials->bv_len, &errMesg); + if (rc) { ++ LOG_PWDPOLICY("Failed to set password credentials for '%s': %s\n", ++ slapi_sdn_get_dn(sdn), errMesg); + goto invalid_creds; + } + } +-- +2.43.0 + diff --git a/0023-Issue-9497-update-debug-logging-in-ipa_uuid.patch b/0023-Issue-9497-update-debug-logging-in-ipa_uuid.patch new file mode 100644 index 0000000..660c715 --- /dev/null +++ b/0023-Issue-9497-update-debug-logging-in-ipa_uuid.patch @@ -0,0 +1,47 @@ +From a2fcfb9e17c4e7f2b4c57fa1eccdfe27d0c085d3 Mon Sep 17 00:00:00 2001 +From: Mark Reynolds +Date: Tue, 12 Dec 2023 08:41:43 -0500 +Subject: [PATCH] Issue 9497 - update debug logging in ipa_uuid + +Fixes: https://pagure.io/freeipa/issue/9497 + +Signed-off-by: Mark Reynolds +Reviewed-By: Alexander Bokovoy +--- + daemons/ipa-slapi-plugins/ipa-uuid/ipa_uuid.c | 6 +++--- + 1 file changed, 3 insertions(+), 3 deletions(-) + +diff --git a/daemons/ipa-slapi-plugins/ipa-uuid/ipa_uuid.c b/daemons/ipa-slapi-plugins/ipa-uuid/ipa_uuid.c +index 87d8be2d88d9ff9bbf7d47eab57b765063f7a230..2fa84f5167341667050e3cfd4bda4c4a4991d06d 100644 +--- a/daemons/ipa-slapi-plugins/ipa-uuid/ipa_uuid.c ++++ b/daemons/ipa-slapi-plugins/ipa-uuid/ipa_uuid.c +@@ -30,7 +30,7 @@ + * Program may make changes or additions to the list of Approved + * Interfaces. + * +- * Copyright (C) 2010 Red Hat, Inc. ++ * Copyright (C) 2010-2023 Red Hat, Inc. + * All rights reserved. + * END COPYRIGHT BLOCK **/ + +@@ -1185,7 +1185,7 @@ static int ipauuid_pre_op(Slapi_PBlock *pb, int modtype) + * enforce is enabled. */ + errstr = slapi_ch_smprintf("Only the Directory Manager " + "can set arbitrary values " +- "for %s\n", cfgentry->attr); ++ "for %s", cfgentry->attr); + ret = LDAP_INSUFFICIENT_ACCESS; + goto done; + } +@@ -1221,7 +1221,7 @@ done: + } + + if (ret) { +- LOG("operation failure [%d]\n", ret); ++ LOG("operation failure [%d] - %s\n", ret, errstr); + slapi_send_ldap_result(pb, ret, NULL, errstr, 0, NULL); + slapi_ch_free((void **)&errstr); + ret = EFAIL; +-- +2.43.0 + diff --git a/0024-hbactest-was-not-collecting-or-returning-messages.patch b/0024-hbactest-was-not-collecting-or-returning-messages.patch new file mode 100644 index 0000000..c04ee23 --- /dev/null +++ b/0024-hbactest-was-not-collecting-or-returning-messages.patch @@ -0,0 +1,82 @@ +From 9e950f89bedeb83267369d60b4a83c77f89e71d6 Mon Sep 17 00:00:00 2001 +From: Rob Crittenden +Date: Mon, 27 Nov 2023 16:11:08 -0500 +Subject: [PATCH] hbactest was not collecting or returning messages + +hbactest does a number of internal searches, one of which +can exceed the configured sizelimit: hbacrule-find + +Collect any messages returned from thsi call and display them +to the user on the cli. + +Fixes: https://pagure.io/freeipa/issue/9486 + +Signed-off-by: Rob Crittenden +Reviewed-By: Florence Blanc-Renaud +--- + ipaclient/plugins/hbactest.py | 2 ++ + ipaserver/plugins/hbactest.py | 14 +++++++++++--- + 2 files changed, 13 insertions(+), 3 deletions(-) + +diff --git a/ipaclient/plugins/hbactest.py b/ipaclient/plugins/hbactest.py +index 1b54530b236cf654bc8ece7ab4e329850f5a6815..e0f93b9c265a176cb872fcf2728dbb3a66a264d9 100644 +--- a/ipaclient/plugins/hbactest.py ++++ b/ipaclient/plugins/hbactest.py +@@ -38,6 +38,8 @@ class hbactest(CommandOverride): + # Note that we don't actually use --detail below to see if details need + # to be printed as our execute() method will return None for corresponding + # entries and None entries will be skipped. ++ self.log_messages(output) ++ + for o in self.output: + if o == 'value': + continue +diff --git a/ipaserver/plugins/hbactest.py b/ipaserver/plugins/hbactest.py +index 887a35b7e67b257a2e54d51990af953ff8fbb316..568c13174ba617f2742b8f42c11b36dbde549cc2 100644 +--- a/ipaserver/plugins/hbactest.py ++++ b/ipaserver/plugins/hbactest.py +@@ -24,6 +24,8 @@ from ipalib import Command, Str, Flag, Int + from ipalib import _ + from ipapython.dn import DN + from ipalib.plugable import Registry ++from ipalib.messages import VersionMissing ++ + if api.env.in_server: + try: + import ipaserver.dcerpc +@@ -323,6 +325,9 @@ class hbactest(Command): + # 2. Required options are (user, target host, service) + # 3. Options: rules to test (--rules, --enabled, --disabled), request for detail output + rules = [] ++ result = { ++ 'warning':None, 'matched':None, 'notmatched':None, 'error':None ++ } + + # Use all enabled IPA rules by default + all_enabled = True +@@ -351,8 +356,12 @@ class hbactest(Command): + + hbacset = [] + if len(testrules) == 0: +- hbacset = self.api.Command.hbacrule_find( +- sizelimit=sizelimit, no_members=False)['result'] ++ hbacrules = self.api.Command.hbacrule_find( ++ sizelimit=sizelimit, no_members=False) ++ hbacset = hbacrules['result'] ++ for message in hbacrules['messages']: ++ if message['code'] != VersionMissing.errno: ++ result.setdefault('messages', []).append(message) + else: + for rule in testrules: + try: +@@ -469,7 +478,6 @@ class hbactest(Command): + error_rules = [] + warning_rules = [] + +- result = {'warning':None, 'matched':None, 'notmatched':None, 'error':None} + if not options['nodetail']: + # Validate runs rules one-by-one and reports failed ones + for ipa_rule in rules: +-- +2.43.0 + diff --git a/0025-ipatests-Verify-that-hbactest-will-return-messages.patch b/0025-ipatests-Verify-that-hbactest-will-return-messages.patch new file mode 100644 index 0000000..9cd1660 --- /dev/null +++ b/0025-ipatests-Verify-that-hbactest-will-return-messages.patch @@ -0,0 +1,56 @@ +From e8810696a38b70af286a2a2aae464ba4294e1fb5 Mon Sep 17 00:00:00 2001 +From: Rob Crittenden +Date: Tue, 28 Nov 2023 13:35:13 -0500 +Subject: [PATCH] ipatests: Verify that hbactest will return messages + +Limit the sizelimit of the hbactest request to confirm that +the output includes a SearchResultTruncated message. + +Fixes: https://pagure.io/freeipa/issue/9486 + +Signed-off-by: Rob Crittenden +Reviewed-By: Florence Blanc-Renaud +--- + ipatests/test_xmlrpc/test_hbactest_plugin.py | 19 ++++++++++++++++++- + 1 file changed, 18 insertions(+), 1 deletion(-) + +diff --git a/ipatests/test_xmlrpc/test_hbactest_plugin.py b/ipatests/test_xmlrpc/test_hbactest_plugin.py +index 73c4ce232066dd7e45bc9a636f9fd955d50d6818..e2e66c759ab4bd1ac3e6dd2ab380c6359fc90042 100644 +--- a/ipatests/test_xmlrpc/test_hbactest_plugin.py ++++ b/ipatests/test_xmlrpc/test_hbactest_plugin.py +@@ -134,6 +134,7 @@ class test_hbactest(XMLRPC_test): + assert ret['value'] + assert ret['error'] is None + assert ret['matched'] is None ++ assert 'messages' not in ret + assert ret['notmatched'] is None + + def test_c_hbactest_check_rules_enabled_detail(self): +@@ -200,7 +201,23 @@ class test_hbactest(XMLRPC_test): + nodetail=True + ) + +- def test_g_hbactest_clear_testing_data(self): ++ def test_g_hbactest_searchlimit_message(self): ++ """ ++ Test running 'ipa hbactest' with limited --sizelimit ++ ++ We know there are at least 6 rules, 4 created here + 2 default. ++ """ ++ ret = api.Command['hbactest']( ++ user=self.test_user, ++ targethost=self.test_host, ++ service=self.test_service, ++ nodetail=True, ++ sizelimit=2, ++ ) ++ ++ assert ret['messages'] is not None ++ ++ def test_h_hbactest_clear_testing_data(self): + """ + Clear data for HBAC test plugin testing. + """ +-- +2.43.0 + diff --git a/0026-ipa-kdb-add-better-detection-of-allowed-user-auth-ty.patch b/0026-ipa-kdb-add-better-detection-of-allowed-user-auth-ty.patch new file mode 100644 index 0000000..4f6c600 --- /dev/null +++ b/0026-ipa-kdb-add-better-detection-of-allowed-user-auth-ty.patch @@ -0,0 +1,126 @@ +From c90ba9478b663bd5bcac9bb3af4272ee1406816b Mon Sep 17 00:00:00 2001 +From: Alexander Bokovoy +Date: Fri, 24 Nov 2023 11:46:19 +0200 +Subject: [PATCH] ipa-kdb: add better detection of allowed user auth type + +If default user authentication type is set to a list that does not +include a password or a hardened credential, the resulting configuration +might be incorrect for special service principals, including a krbtgt/.. +one. + +Add detection of special principals to avoid these situations and always +allow password or hardened for services. + +Special handling is needed for the following principals: + + - krbtgt/.. -- TGT service principals + - K/M -- master key principal + - kadmin/changepw -- service for changing passwords + - kadmin/kadmin -- kadmin service principal + - kadmin/history -- key used to encrypt history + +Additionally, implicitly allow password or hardened credential use for +IPA services and IPA hosts since applications typically use keytabs for +that purpose. + +Fixes: https://pagure.io/freeipa/issue/9485 + +Signed-off-by: Alexander Bokovoy +Reviewed-By: Francisco Trivino +--- + daemons/ipa-kdb/ipa_kdb.c | 62 ++++++++++++++++++++++++++++++++++----- + 1 file changed, 54 insertions(+), 8 deletions(-) + +diff --git a/daemons/ipa-kdb/ipa_kdb.c b/daemons/ipa-kdb/ipa_kdb.c +index 06d511c762006f6a1e6e7a0ec663bc059489cf64..dbb98dba6d6d273e86e39e8ca8b8877d13f4299b 100644 +--- a/daemons/ipa-kdb/ipa_kdb.c ++++ b/daemons/ipa-kdb/ipa_kdb.c +@@ -26,6 +26,7 @@ + #include "ipa_kdb.h" + #include "ipa_krb5.h" + #include "ipa_hostname.h" ++#include + + #define IPADB_GLOBAL_CONFIG_CACHE_TIME 60 + +@@ -207,6 +208,19 @@ static const struct { + { "idp", IPADB_USER_AUTH_IDP }, + { "passkey", IPADB_USER_AUTH_PASSKEY }, + { } ++}, ++ objclass_table[] = { ++ { "ipaservice", IPADB_USER_AUTH_PASSWORD }, ++ { "ipahost", IPADB_USER_AUTH_PASSWORD }, ++ { } ++}, ++ princname_table[] = { ++ { KRB5_TGS_NAME, IPADB_USER_AUTH_PASSWORD }, ++ { KRB5_KDB_M_NAME, IPADB_USER_AUTH_PASSWORD }, ++ { KADM5_ADMIN_SERVICE, IPADB_USER_AUTH_PASSWORD }, ++ { KADM5_CHANGEPW_SERVICE, IPADB_USER_AUTH_PASSWORD }, ++ { KADM5_HIST_PRINCIPAL, IPADB_USER_AUTH_PASSWORD }, ++ { } + }; + + void ipadb_parse_user_auth(LDAP *lcontext, LDAPMessage *le, +@@ -217,17 +231,49 @@ void ipadb_parse_user_auth(LDAP *lcontext, LDAPMessage *le, + + *userauth = IPADB_USER_AUTH_NONE; + vals = ldap_get_values_len(lcontext, le, IPA_USER_AUTH_TYPE); +- if (!vals) +- return; +- +- for (i = 0; vals[i]; i++) { +- for (j = 0; userauth_table[j].name; j++) { +- if (strcasecmp(vals[i]->bv_val, userauth_table[j].name) == 0) { +- *userauth |= userauth_table[j].flag; +- break; ++ if (!vals) { ++ /* if there is no explicit ipaUserAuthType set, use objectclass */ ++ vals = ldap_get_values_len(lcontext, le, "objectclass"); ++ if (!vals) ++ return; ++ ++ for (i = 0; vals[i]; i++) { ++ for (j = 0; objclass_table[j].name; j++) { ++ if (strcasecmp(vals[i]->bv_val, objclass_table[j].name) == 0) { ++ *userauth |= objclass_table[j].flag; ++ break; ++ } ++ } ++ } ++ } else { ++ for (i = 0; vals[i]; i++) { ++ for (j = 0; userauth_table[j].name; j++) { ++ if (strcasecmp(vals[i]->bv_val, userauth_table[j].name) == 0) { ++ *userauth |= userauth_table[j].flag; ++ break; ++ } + } + } + } ++ ++ /* If neither ipaUserAuthType nor objectClass were definitive, ++ * check the krbPrincipalName to see if it is krbtgt/ or K/M one */ ++ if (*userauth == IPADB_USER_AUTH_NONE) { ++ ldap_value_free_len(vals); ++ vals = ldap_get_values_len(lcontext, le, "krbprincipalname"); ++ if (!vals) ++ return; ++ for (i = 0; vals[i]; i++) { ++ for (j = 0; princname_table[j].name; j++) { ++ if (strncmp(vals[i]->bv_val, princname_table[j].name, ++ strlen(princname_table[j].name)) == 0) { ++ *userauth |= princname_table[j].flag; ++ break; ++ } ++ } ++ } ++ ++ } + /* If password auth is enabled, enable hardened policy too. */ + if (*userauth & IPADB_USER_AUTH_PASSWORD) { + *userauth |= IPADB_USER_AUTH_HARDENED; +-- +2.43.0 + diff --git a/0027-ipa-kdb-when-applying-ticket-policy-do-not-deny-PKIN.patch b/0027-ipa-kdb-when-applying-ticket-policy-do-not-deny-PKIN.patch new file mode 100644 index 0000000..ef2ac93 --- /dev/null +++ b/0027-ipa-kdb-when-applying-ticket-policy-do-not-deny-PKIN.patch @@ -0,0 +1,41 @@ +From 1fb026105ef397612a504722b2bcac29fbc69676 Mon Sep 17 00:00:00 2001 +From: Alexander Bokovoy +Date: Fri, 24 Nov 2023 11:54:04 +0200 +Subject: [PATCH] ipa-kdb: when applying ticket policy, do not deny PKINIT + +PKINIT differs from other pre-authentication methods by the fact that it +can be matched indepedently of the user authentication types via certmap +plugin in KDC. + +Since PKINIT is a strong authentication method, allow its authentication +indicator and only apply the ticket policy. + +Fixes: https://pagure.io/freeipa/issue/9485 + +Signed-off-by: Alexander Bokovoy +Reviewed-By: Francisco Trivino +--- + daemons/ipa-kdb/ipa_kdb_kdcpolicy.c | 7 ++----- + 1 file changed, 2 insertions(+), 5 deletions(-) + +diff --git a/daemons/ipa-kdb/ipa_kdb_kdcpolicy.c b/daemons/ipa-kdb/ipa_kdb_kdcpolicy.c +index 436ee0e62665594062e7be37e5b7925f76e921a0..2802221c79fe63ab4bd33bfbe4859517f3d91ec5 100644 +--- a/daemons/ipa-kdb/ipa_kdb_kdcpolicy.c ++++ b/daemons/ipa-kdb/ipa_kdb_kdcpolicy.c +@@ -119,11 +119,8 @@ ipa_kdcpolicy_check_as(krb5_context context, krb5_kdcpolicy_moddata moddata, + pol_limits = &(ied->pol_limits[IPADB_USER_AUTH_IDX_RADIUS]); + } else if (strcmp(auth_indicator, "pkinit") == 0) { + valid_auth_indicators++; +- if (!(ua & IPADB_USER_AUTH_PKINIT)) { +- *status = "PKINIT pre-authentication not allowed for this user."; +- kerr = KRB5KDC_ERR_POLICY; +- goto done; +- } ++ /* allow PKINIT unconditionally -- it has passed already at this ++ * point so some certificate was useful, only apply the limits */ + pol_limits = &(ied->pol_limits[IPADB_USER_AUTH_IDX_PKINIT]); + } else if (strcmp(auth_indicator, "hardened") == 0) { + valid_auth_indicators++; +-- +2.43.0 + diff --git a/0028-ipa-kdb-clarify-user-auth-table-mapping-use-of-_AUTH.patch b/0028-ipa-kdb-clarify-user-auth-table-mapping-use-of-_AUTH.patch new file mode 100644 index 0000000..d56a1fd --- /dev/null +++ b/0028-ipa-kdb-clarify-user-auth-table-mapping-use-of-_AUTH.patch @@ -0,0 +1,31 @@ +From fab08337dac0eb6322dc5ebe730b2541f4bb6111 Mon Sep 17 00:00:00 2001 +From: Alexander Bokovoy +Date: Fri, 24 Nov 2023 12:20:55 +0200 +Subject: [PATCH] ipa-kdb: clarify user auth table mapping use of + _AUTH_PASSWORD + +Related: https://pagure.io/freeipa/issue/9485 + +Signed-off-by: Alexander Bokovoy +Reviewed-By: Francisco Trivino +--- + daemons/ipa-kdb/ipa_kdb.c | 3 +++ + 1 file changed, 3 insertions(+) + +diff --git a/daemons/ipa-kdb/ipa_kdb.c b/daemons/ipa-kdb/ipa_kdb.c +index dbb98dba6d6d273e86e39e8ca8b8877d13f4299b..4e6cacf24e27b05538db2c95ab85400bb83e3d58 100644 +--- a/daemons/ipa-kdb/ipa_kdb.c ++++ b/daemons/ipa-kdb/ipa_kdb.c +@@ -195,6 +195,9 @@ done: + return base; + } + ++/* In this table all _AUTH_PASSWORD entries will be ++ * expanded to include _AUTH_HARDENED in ipadb_parse_user_auth() ++ * which means there is no need to explicitly add it here */ + static const struct { + const char *name; + enum ipadb_user_auth flag; +-- +2.43.0 + diff --git a/0029-ipatests-make-sure-PKINIT-enrollment-works-with-a-st.patch b/0029-ipatests-make-sure-PKINIT-enrollment-works-with-a-st.patch new file mode 100644 index 0000000..bfbe6bb --- /dev/null +++ b/0029-ipatests-make-sure-PKINIT-enrollment-works-with-a-st.patch @@ -0,0 +1,71 @@ +From 02b17c8560a6aabb4be1109a3a794412f527c83c Mon Sep 17 00:00:00 2001 +From: Alexander Bokovoy +Date: Fri, 24 Nov 2023 13:00:48 +0200 +Subject: [PATCH] ipatests: make sure PKINIT enrollment works with a strict + policy + +Previously, for a global policy which does not include +'password', krb5kdc restart was failing. Now it should succeed. + +We set admin user authentication type to PASSWORD to simplify +configuration in the test. + +What matters here is that global policy does not include PKINIT and that +means a code in the ticket policy check will allow PKINIT implicitly +rather than explicitly. + +Related: https://pagure.io/freeipa/issue/9485 + +Signed-off-by: Alexander Bokovoy +Reviewed-By: Francisco Trivino +--- + .../test_integration/test_pkinit_install.py | 26 +++++++++++++++++++ + 1 file changed, 26 insertions(+) + +diff --git a/ipatests/test_integration/test_pkinit_install.py b/ipatests/test_integration/test_pkinit_install.py +index caa0e6a34dc7e50359a41314e419a0d5be0c3aa8..5c2e7af0231677d4653ea2f82fa3dffed711a10d 100644 +--- a/ipatests/test_integration/test_pkinit_install.py ++++ b/ipatests/test_integration/test_pkinit_install.py +@@ -23,6 +23,24 @@ class TestPkinitClientInstall(IntegrationTest): + def install(cls, mh): + tasks.install_master(cls.master) + ++ def enforce_password_and_otp(self): ++ """enforce otp by default and password for admin """ ++ self.master.run_command( ++ [ ++ "ipa", ++ "config-mod", ++ "--user-auth-type=otp", ++ ] ++ ) ++ self.master.run_command( ++ [ ++ "ipa", ++ "user-mod", ++ "admin", ++ "--user-auth-type=password", ++ ] ++ ) ++ + def add_certmaperule(self): + """add certmap rule to map SAN dNSName to host entry""" + self.master.run_command( +@@ -86,6 +104,14 @@ class TestPkinitClientInstall(IntegrationTest): + cabundle = self.master.get_file_contents(paths.KDC_CA_BUNDLE_PEM) + client.put_file_contents(self.tmpbundle, cabundle) + ++ def test_restart_krb5kdc(self): ++ tasks.kinit_admin(self.master) ++ self.enforce_password_and_otp() ++ self.master.run_command(['systemctl', 'stop', 'krb5kdc.service']) ++ self.master.run_command(['systemctl', 'start', 'krb5kdc.service']) ++ self.master.run_command(['systemctl', 'stop', 'kadmin.service']) ++ self.master.run_command(['systemctl', 'start', 'kadmin.service']) ++ + def test_client_install_pkinit(self): + tasks.kinit_admin(self.master) + self.add_certmaperule() +-- +2.43.0 + diff --git a/0030-Check-the-HTTP-Referer-header-on-all-requests.patch b/0030-Check-the-HTTP-Referer-header-on-all-requests.patch new file mode 100644 index 0000000..ce9cbc1 --- /dev/null +++ b/0030-Check-the-HTTP-Referer-header-on-all-requests.patch @@ -0,0 +1,121 @@ +From 2c52a7dfd26ac561786e72e4304acbf9585698b6 Mon Sep 17 00:00:00 2001 +From: Rob Crittenden +Date: Fri, 6 Oct 2023 20:16:29 +0000 +Subject: [PATCH] Check the HTTP Referer header on all requests + +The referer was only checked in WSGIExecutioner classes: + + - jsonserver + - KerberosWSGIExecutioner + - xmlserver + - jsonserver_kerb + +This left /i18n_messages, /session/login_kerberos, +/session/login_x509, /session/login_password, +/session/change_password and /session/sync_token unprotected +against CSRF attacks. + +CVE-2023-5455 + +Signed-off-by: Rob Crittenden +--- + ipaserver/rpcserver.py | 34 +++++++++++++++++++++++++++++++--- + 1 file changed, 31 insertions(+), 3 deletions(-) + +diff --git a/ipaserver/rpcserver.py b/ipaserver/rpcserver.py +index b7116469d73f9a8595dbb2d1a3f39abe851f4fc3..198fc9e7dbae281f797dcccf96d21d475ff31e8c 100644 +--- a/ipaserver/rpcserver.py ++++ b/ipaserver/rpcserver.py +@@ -156,6 +156,19 @@ _success_template = """ + """ + + class HTTP_Status(plugable.Plugin): ++ def check_referer(self, environ): ++ if "HTTP_REFERER" not in environ: ++ logger.error("Rejecting request with missing Referer") ++ return False ++ if (not environ["HTTP_REFERER"].startswith( ++ "https://%s/ipa" % self.api.env.host) ++ and not self.env.in_tree): ++ logger.error("Rejecting request with bad Referer %s", ++ environ["HTTP_REFERER"]) ++ return False ++ logger.debug("Valid Referer %s", environ["HTTP_REFERER"]) ++ return True ++ + def not_found(self, environ, start_response, url, message): + """ + Return a 404 Not Found error. +@@ -331,9 +344,6 @@ class wsgi_dispatch(Executioner, HTTP_Status): + self.__apps[key] = app + + +- +- +- + class WSGIExecutioner(Executioner): + """ + Base class for execution backends with a WSGI application interface. +@@ -898,6 +908,9 @@ class jsonserver_session(jsonserver, KerberosSession): + + logger.debug('WSGI jsonserver_session.__call__:') + ++ if not self.check_referer(environ): ++ return self.bad_request(environ, start_response, 'denied') ++ + # Redirect to login if no Kerberos credentials + ccache_name = self.get_environ_creds(environ) + if ccache_name is None: +@@ -950,6 +963,9 @@ class KerberosLogin(Backend, KerberosSession): + def __call__(self, environ, start_response): + logger.debug('WSGI KerberosLogin.__call__:') + ++ if not self.check_referer(environ): ++ return self.bad_request(environ, start_response, 'denied') ++ + # Redirect to login if no Kerberos credentials + user_ccache_name = self.get_environ_creds(environ) + if user_ccache_name is None: +@@ -968,6 +984,9 @@ class login_x509(KerberosLogin): + def __call__(self, environ, start_response): + logger.debug('WSGI login_x509.__call__:') + ++ if not self.check_referer(environ): ++ return self.bad_request(environ, start_response, 'denied') ++ + if 'KRB5CCNAME' not in environ: + return self.unauthorized( + environ, start_response, 'KRB5CCNAME not set', +@@ -1016,6 +1035,9 @@ class login_password(Backend, KerberosSession): + + logger.debug('WSGI login_password.__call__:') + ++ if not self.check_referer(environ): ++ return self.bad_request(environ, start_response, 'denied') ++ + # Get the user and password parameters from the request + content_type = environ.get('CONTENT_TYPE', '').lower() + if not content_type.startswith('application/x-www-form-urlencoded'): +@@ -1148,6 +1170,9 @@ class change_password(Backend, HTTP_Status): + def __call__(self, environ, start_response): + logger.info('WSGI change_password.__call__:') + ++ if not self.check_referer(environ): ++ return self.bad_request(environ, start_response, 'denied') ++ + # Get the user and password parameters from the request + content_type = environ.get('CONTENT_TYPE', '').lower() + if not content_type.startswith('application/x-www-form-urlencoded'): +@@ -1365,6 +1390,9 @@ class xmlserver_session(xmlserver, KerberosSession): + + logger.debug('WSGI xmlserver_session.__call__:') + ++ if not self.check_referer(environ): ++ return self.bad_request(environ, start_response, 'denied') ++ + ccache_name = environ.get('KRB5CCNAME') + + # Redirect to /ipa/xml if no Kerberos credentials +-- +2.43.0 + diff --git a/0031-Integration-tests-for-verifying-Referer-header-in-th.patch b/0031-Integration-tests-for-verifying-Referer-header-in-th.patch new file mode 100644 index 0000000..068d77a --- /dev/null +++ b/0031-Integration-tests-for-verifying-Referer-header-in-th.patch @@ -0,0 +1,359 @@ +From 14720c7690bda2b538dfc1d742eb4eb152dfd8a2 Mon Sep 17 00:00:00 2001 +From: Rob Crittenden +Date: Thu, 12 Oct 2023 20:34:01 +0000 +Subject: [PATCH] Integration tests for verifying Referer header in the UI + +Validate that the change_password and login_password endpoints +verify the HTTP Referer header. There is some overlap in the +tests: belt and suspenders. + +All endpoints except session/login_x509 are covered, sometimes +having to rely on expected bad results (see the i18n endpoint). + +session/login_x509 is not tested yet as it requires significant +additional setup in order to associate a user certificate with +a user entry, etc. + +This can be manually verified by modifying /etc/httpd/conf.d/ipa.conf +and adding: + +Satisfy Any +Require all granted + +Then comment out Auth and SSLVerify, etc. and restart httpd. + +With a valid Referer will fail with a 401 and log that there is no +KRB5CCNAME. This comes after the referer check. + +With an invalid Referer it will fail with a 400 Bad Request as +expected. + +CVE-2023-5455 + +Signed-off-by: Rob Crittenden +--- + ipatests/test_ipaserver/httptest.py | 7 +- + ipatests/test_ipaserver/test_changepw.py | 12 +- + .../test_ipaserver/test_login_password.py | 88 ++++++++++++ + ipatests/test_ipaserver/test_referer.py | 136 ++++++++++++++++++ + ipatests/util.py | 4 +- + 5 files changed, 242 insertions(+), 5 deletions(-) + create mode 100644 ipatests/test_ipaserver/test_login_password.py + create mode 100644 ipatests/test_ipaserver/test_referer.py + +diff --git a/ipatests/test_ipaserver/httptest.py b/ipatests/test_ipaserver/httptest.py +index 6cd034a719690646ee9238a5c6061e791e1c6fb5..8924798fc93c14e45beaf232a958d22398f61954 100644 +--- a/ipatests/test_ipaserver/httptest.py ++++ b/ipatests/test_ipaserver/httptest.py +@@ -36,7 +36,7 @@ class Unauthorized_HTTP_test: + content_type = 'application/x-www-form-urlencoded' + accept_language = 'en-us' + +- def send_request(self, method='POST', params=None): ++ def send_request(self, method='POST', params=None, host=None): + """ + Send a request to HTTP server + +@@ -45,7 +45,10 @@ class Unauthorized_HTTP_test: + if params is not None: + if self.content_type == 'application/x-www-form-urlencoded': + params = urllib.parse.urlencode(params, True) +- url = 'https://' + self.host + self.app_uri ++ if host: ++ url = 'https://' + host + self.app_uri ++ else: ++ url = 'https://' + self.host + self.app_uri + + headers = {'Content-Type': self.content_type, + 'Accept-Language': self.accept_language, +diff --git a/ipatests/test_ipaserver/test_changepw.py b/ipatests/test_ipaserver/test_changepw.py +index c3a47ab265f08db11ddfee2182401ccba90cf8df..df38ddb3d9e74baf908372be0780fcefbb258a5d 100644 +--- a/ipatests/test_ipaserver/test_changepw.py ++++ b/ipatests/test_ipaserver/test_changepw.py +@@ -53,10 +53,11 @@ class test_changepw(XMLRPC_test, Unauthorized_HTTP_test): + + request.addfinalizer(fin) + +- def _changepw(self, user, old_password, new_password): ++ def _changepw(self, user, old_password, new_password, host=None): + return self.send_request(params={'user': str(user), + 'old_password' : str(old_password), + 'new_password' : str(new_password)}, ++ host=host + ) + + def _checkpw(self, user, password): +@@ -89,6 +90,15 @@ class test_changepw(XMLRPC_test, Unauthorized_HTTP_test): + # make sure that password is NOT changed + self._checkpw(testuser, old_password) + ++ def test_invalid_referer(self): ++ response = self._changepw(testuser, old_password, new_password, ++ 'attacker.test') ++ ++ assert_equal(response.status, 400) ++ ++ # make sure that password is NOT changed ++ self._checkpw(testuser, old_password) ++ + def test_pwpolicy_error(self): + response = self._changepw(testuser, old_password, '1') + +diff --git a/ipatests/test_ipaserver/test_login_password.py b/ipatests/test_ipaserver/test_login_password.py +new file mode 100644 +index 0000000000000000000000000000000000000000..9425cb7977fbc87210bf91464e0257830a938baf +--- /dev/null ++++ b/ipatests/test_ipaserver/test_login_password.py +@@ -0,0 +1,88 @@ ++# Copyright (C) 2023 Red Hat ++# see file 'COPYING' for use and warranty information ++# ++# This program is free software; you can redistribute it and/or modify ++# it under the terms of the GNU General Public License as published by ++# the Free Software Foundation, either version 3 of the License, or ++# (at your option) any later version. ++# ++# This program is distributed in the hope that it will be useful, ++# but WITHOUT ANY WARRANTY; without even the implied warranty of ++# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++# GNU General Public License for more details. ++# ++# You should have received a copy of the GNU General Public License ++# along with this program. If not, see . ++ ++import os ++import pytest ++import uuid ++ ++from ipatests.test_ipaserver.httptest import Unauthorized_HTTP_test ++from ipatests.test_xmlrpc.xmlrpc_test import XMLRPC_test ++from ipatests.util import assert_equal ++from ipalib import api, errors ++from ipapython.ipautil import run ++ ++testuser = u'tuser' ++password = u'password' ++ ++ ++@pytest.mark.tier1 ++class test_login_password(XMLRPC_test, Unauthorized_HTTP_test): ++ app_uri = '/ipa/session/login_password' ++ ++ @pytest.fixture(autouse=True) ++ def login_setup(self, request): ++ ccache = os.path.join('/tmp', str(uuid.uuid4())) ++ try: ++ api.Command['user_add'](uid=testuser, givenname=u'Test', sn=u'User') ++ api.Command['passwd'](testuser, password=password) ++ run(['kinit', testuser], stdin='{0}\n{0}\n{0}\n'.format(password), ++ env={"KRB5CCNAME": ccache}) ++ except errors.ExecutionError as e: ++ pytest.skip( ++ 'Cannot set up test user: %s' % e ++ ) ++ ++ def fin(): ++ try: ++ api.Command['user_del']([testuser]) ++ except errors.NotFound: ++ pass ++ os.unlink(ccache) ++ ++ request.addfinalizer(fin) ++ ++ def _login(self, user, password, host=None): ++ return self.send_request(params={'user': str(user), ++ 'password' : str(password)}, ++ host=host) ++ ++ def test_bad_options(self): ++ for params in ( ++ None, # no params ++ {"user": "foo"}, # missing options ++ {"user": "foo", "password": ""}, # empty option ++ ): ++ response = self.send_request(params=params) ++ assert_equal(response.status, 400) ++ assert_equal(response.reason, 'Bad Request') ++ ++ def test_invalid_auth(self): ++ response = self._login(testuser, 'wrongpassword') ++ ++ assert_equal(response.status, 401) ++ assert_equal(response.getheader('X-IPA-Rejection-Reason'), ++ 'invalid-password') ++ ++ def test_invalid_referer(self): ++ response = self._login(testuser, password, 'attacker.test') ++ ++ assert_equal(response.status, 400) ++ ++ def test_success(self): ++ response = self._login(testuser, password) ++ ++ assert_equal(response.status, 200) ++ assert response.getheader('X-IPA-Rejection-Reason') is None +diff --git a/ipatests/test_ipaserver/test_referer.py b/ipatests/test_ipaserver/test_referer.py +new file mode 100644 +index 0000000000000000000000000000000000000000..4eade8bbaf304c48bf71c16892858d899b43cf88 +--- /dev/null ++++ b/ipatests/test_ipaserver/test_referer.py +@@ -0,0 +1,136 @@ ++# Copyright (C) 2023 Red Hat ++# see file 'COPYING' for use and warranty information ++# ++# This program is free software; you can redistribute it and/or modify ++# it under the terms of the GNU General Public License as published by ++# the Free Software Foundation, either version 3 of the License, or ++# (at your option) any later version. ++# ++# This program is distributed in the hope that it will be useful, ++# but WITHOUT ANY WARRANTY; without even the implied warranty of ++# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++# GNU General Public License for more details. ++# ++# You should have received a copy of the GNU General Public License ++# along with this program. If not, see . ++ ++import os ++import pytest ++import uuid ++ ++from ipatests.test_ipaserver.httptest import Unauthorized_HTTP_test ++from ipatests.test_xmlrpc.xmlrpc_test import XMLRPC_test ++from ipatests.util import assert_equal ++from ipalib import api, errors ++from ipapython.ipautil import run ++ ++testuser = u'tuser' ++password = u'password' ++ ++ ++@pytest.mark.tier1 ++class test_referer(XMLRPC_test, Unauthorized_HTTP_test): ++ ++ @pytest.fixture(autouse=True) ++ def login_setup(self, request): ++ ccache = os.path.join('/tmp', str(uuid.uuid4())) ++ tokenid = None ++ try: ++ api.Command['user_add'](uid=testuser, givenname=u'Test', sn=u'User') ++ api.Command['passwd'](testuser, password=password) ++ run(['kinit', testuser], stdin='{0}\n{0}\n{0}\n'.format(password), ++ env={"KRB5CCNAME": ccache}) ++ result = api.Command["otptoken_add"]( ++ type='HOTP', description='testotp', ++ ipatokenotpalgorithm='sha512', ipatokenowner=testuser, ++ ipatokenotpdigits='6') ++ tokenid = result['result']['ipatokenuniqueid'][0] ++ except errors.ExecutionError as e: ++ pytest.skip( ++ 'Cannot set up test user: %s' % e ++ ) ++ ++ def fin(): ++ try: ++ api.Command['user_del']([testuser]) ++ api.Command['otptoken_del']([tokenid]) ++ except errors.NotFound: ++ pass ++ os.unlink(ccache) ++ ++ request.addfinalizer(fin) ++ ++ def _request(self, params={}, host=None): ++ # implicit is that self.app_uri is set to the appropriate value ++ return self.send_request(params=params, host=host) ++ ++ def test_login_password_valid(self): ++ """Valid authentication of a user""" ++ self.app_uri = "/ipa/session/login_password" ++ response = self._request( ++ params={'user': 'tuser', 'password': password}) ++ assert_equal(response.status, 200, self.app_uri) ++ ++ def test_change_password_valid(self): ++ """This actually changes the user password""" ++ self.app_uri = "/ipa/session/change_password" ++ response = self._request( ++ params={'user': 'tuser', ++ 'old_password': password, ++ 'new_password': 'new_password'} ++ ) ++ assert_equal(response.status, 200, self.app_uri) ++ ++ def test_sync_token_valid(self): ++ """We aren't testing that sync works, just that we can get there""" ++ self.app_uri = "/ipa/session/sync_token" ++ response = self._request( ++ params={'user': 'tuser', ++ 'first_code': '1234', ++ 'second_code': '5678', ++ 'password': 'password'}) ++ assert_equal(response.status, 200, self.app_uri) ++ ++ def test_i18n_messages_valid(self): ++ # i18n_messages requires a valid JSON request and we send ++ # nothing. If we get a 500 error then it got past the ++ # referer check. ++ self.app_uri = "/ipa/i18n_messages" ++ response = self._request() ++ assert_equal(response.status, 500, self.app_uri) ++ ++ # /ipa/session/login_x509 is not tested yet as it requires ++ # significant additional setup. ++ # This can be manually verified by adding ++ # Satisfy Any and Require all granted to the configuration ++ # section and comment out all Auth directives. The request ++ # will fail and log that there is no KRB5CCNAME which comes ++ # after the referer check. ++ ++ def test_endpoints_auth_required(self): ++ """Test endpoints that require pre-authorization which will ++ fail before we even get to the Referer check ++ """ ++ self.endpoints = { ++ "/ipa/xml", ++ "/ipa/session/login_kerberos", ++ "/ipa/session/json", ++ "/ipa/session/xml" ++ } ++ for self.app_uri in self.endpoints: ++ response = self._request(host="attacker.test") ++ ++ # referer is checked after auth ++ assert_equal(response.status, 401, self.app_uri) ++ ++ def notest_endpoints_invalid(self): ++ """Pass in a bad Referer, expect a 400 Bad Request""" ++ self.endpoints = { ++ "/ipa/session/login_password", ++ "/ipa/session/change_password", ++ "/ipa/session/sync_token", ++ } ++ for self.app_uri in self.endpoints: ++ response = self._request(host="attacker.test") ++ ++ assert_equal(response.status, 400, self.app_uri) +diff --git a/ipatests/util.py b/ipatests/util.py +index 929c3e899c3317acf59f2030b069898f4b282abc..61af0c40d07b31ef9e8ce1f069b05b2088605231 100644 +--- a/ipatests/util.py ++++ b/ipatests/util.py +@@ -163,12 +163,12 @@ class ExceptionNotRaised(Exception): + return self.msg % self.expected.__name__ + + +-def assert_equal(val1, val2): ++def assert_equal(val1, val2, msg=''): + """ + Assert ``val1`` and ``val2`` are the same type and of equal value. + """ + assert type(val1) is type(val2), '%r != %r' % (val1, val2) +- assert val1 == val2, '%r != %r' % (val1, val2) ++ assert val1 == val2, '%r != %r %r' % (val1, val2, msg) + + + def assert_not_equal(val1, val2): +-- +2.43.0 + diff --git a/0032-ipatests-Skip-ds_encryption-tests-on-RHEL9-SUT.patch b/0032-ipatests-Skip-ds_encryption-tests-on-RHEL9-SUT.patch new file mode 100644 index 0000000..8e2efe5 --- /dev/null +++ b/0032-ipatests-Skip-ds_encryption-tests-on-RHEL9-SUT.patch @@ -0,0 +1,46 @@ +From 8bdfbe8d2b203c64444390985011b2372f3bc08e Mon Sep 17 00:00:00 2001 +From: Sudhir Menon +Date: Wed, 20 Dec 2023 18:42:25 +0530 +Subject: [PATCH] ipatests: Skip ds_encryption tests on RHEL9 SUT. + +test_ipahealthcheck_ds_encryption tests are failing +in RHEL9 SUT because in this test tls protocol version +is set to TLS1.0 using the below command, but its +reset to TLS1.2 causing the test to fail. + +'dsconf', 'slapd-TESTREALM-TEST', 'security', 'set', '--tls-protocol-min=TLS1.0' + +Hence the test is skipped to be run on RHEL9.0 SUT. + +Signed-off-by: Sudhir Menon +Reviewed-By: Florence Blanc-Renaud +--- + ipatests/test_integration/test_ipahealthcheck.py | 5 ++++- + 1 file changed, 4 insertions(+), 1 deletion(-) + +diff --git a/ipatests/test_integration/test_ipahealthcheck.py b/ipatests/test_integration/test_ipahealthcheck.py +index 785e9abbae3b807f100a3d875e0c0b23f868be83..40c84898894681d8daf386b522118a6a7f793227 100644 +--- a/ipatests/test_integration/test_ipahealthcheck.py ++++ b/ipatests/test_integration/test_ipahealthcheck.py +@@ -158,7 +158,6 @@ TOMCAT_CONFIG_FILES = ( + paths.CA_CS_CFG_PATH, + ) + +- + def run_healthcheck(host, source=None, check=None, output_type="json", + failures_only=False, config=None): + """ +@@ -1262,6 +1261,10 @@ class TestIpaHealthCheck(IntegrationTest): + ) + self.master.run_command(cmd) + ++ @pytest.mark.skipif((osinfo.id == 'rhel' ++ and osinfo.version_number >= (9,0)), ++ reason=" TLS versions below 1.2 are not " ++ "supported anymore in RHEL9.0 and above.") + def test_ipahealthcheck_ds_encryption(self, modify_tls): + """ + This testcase modifies the default TLS version of +-- +2.43.0 + diff --git a/0033-ACME-Don-t-treat-pki-server-ca-config-show-failures-.patch b/0033-ACME-Don-t-treat-pki-server-ca-config-show-failures-.patch new file mode 100644 index 0000000..559ee5f --- /dev/null +++ b/0033-ACME-Don-t-treat-pki-server-ca-config-show-failures-.patch @@ -0,0 +1,61 @@ +From b465cf6ea596907a2845c38df9c2446efe8e65ae Mon Sep 17 00:00:00 2001 +From: Rob Crittenden +Date: Thu, 4 Jan 2024 17:32:45 -0500 +Subject: [PATCH] ACME: Don't treat pki-server ca-config-show failures as fatal + +Up to PKI 11.5.0 even when a pki-server call failed it had a +return value of 0. This was fixed in 11.5.0 which breaks +ipa-acme-manage pruning. If a configuration value is not set +then the call fails and the tool gives up with an error like: + +ERROR: No such parameter: jobsScheduler.job.pruning.certRetentionUnit + +In previous versions this resulted in an empty string so the tool +displayed the default value. + +So now upon failure look in the stderr output for "No such parameter" +and return an empty string so the behavior is consistent between +both old and new PKI server versions. + +Fixes: https://pagure.io/freeipa/issue/9503 + +Signed-off-by: Rob Crittenden +Reviewed-By: Florence Blanc-Renaud +--- + ipaserver/install/ipa_acme_manage.py | 12 ++++++++---- + 1 file changed, 8 insertions(+), 4 deletions(-) + +diff --git a/ipaserver/install/ipa_acme_manage.py b/ipaserver/install/ipa_acme_manage.py +index e7c35ff6fb5b7a30ac9e2c0c18f8db805cf06ee9..dc2359f49dfdd5c8f44ab96ee11a7240f8937e11 100644 +--- a/ipaserver/install/ipa_acme_manage.py ++++ b/ipaserver/install/ipa_acme_manage.py +@@ -261,8 +261,13 @@ class IPAACMEManage(AdminTool): + result = run(args, raiseonerr=False, capture_output=True, + capture_error=True) + if result.returncode != 0: ++ # See if the parameter doesn't exist. If not then no ++ # user-specified value has been set. ++ # ERROR: No such parameter: jobsScheduler... ++ if 'No such parameter' in result.error_output: ++ return '' + raise RuntimeError(result.error_output) +- return result ++ return result.output.strip() + + def ca_config_set(directive, value, + prefix='jobsScheduler.job.pruning'): +@@ -274,9 +279,8 @@ class IPAACMEManage(AdminTool): + raise RuntimeError('Updating %s failed' % directive) + + def ca_config_show(directive): +- result = run_pki_server('ca-config-show', directive, +- prefix='jobsScheduler.job.pruning') +- return result.output.strip() ++ return run_pki_server('ca-config-show', directive, ++ prefix='jobsScheduler.job.pruning') + + def config_show(): + status = ca_config_show('enabled') +-- +2.43.0 + diff --git a/0034-Fix-ipa-client-automount-install-uninstall-with-new-.patch b/0034-Fix-ipa-client-automount-install-uninstall-with-new-.patch new file mode 100644 index 0000000..891632c --- /dev/null +++ b/0034-Fix-ipa-client-automount-install-uninstall-with-new-.patch @@ -0,0 +1,125 @@ +From 6340e88341b09b06391b35e50e8c4d7619b12dab Mon Sep 17 00:00:00 2001 +From: Rob Crittenden +Date: Fri, 1 Dec 2023 08:51:05 -0500 +Subject: [PATCH] Fix ipa-client-automount install/uninstall with new install + states + +Issue 8384 introduced a new installation state for the statestore +to identify when client/server installation is completely finished +rather than relying on has_files(). + +The problem is that ipa-client-automount may be called during +ipa-client-install and since installation is not complete at that +point the automount install was failing with "IPA client not +configured". + +Add a new state, 'automount', to designate that automount installation +is in process. If check_client_configuration() fails it checks to +see if [installation] automount is True. If so it continues with the +installation. + +This also addresses an issue where the filestore and statestore are +shared between the client and automount installers but the client +wasn't refreshing state after automount completed. This resulted in +an incomplete state and index file of backed-up files which caused +files to not be restored on uninstall and the state file to be +orphaned. + +Fixes: https://pagure.io/freeipa/issue/9487 + +Signed-off-by: Rob Crittenden +Reviewed-By: Christian Heimes +Reviewed-By: Florence Blanc-Renaud +--- + ipaclient/install/client.py | 14 ++++++++++++-- + ipaclient/install/ipa_client_automount.py | 14 ++++++++------ + 2 files changed, 20 insertions(+), 8 deletions(-) + +diff --git a/ipaclient/install/client.py b/ipaclient/install/client.py +index 7e3adee351ae31ed9fcbba422fcc03a1f904e1f9..976d3821dd6d66b5b7653298c628a2bc267fa8c6 100644 +--- a/ipaclient/install/client.py ++++ b/ipaclient/install/client.py +@@ -1273,7 +1273,7 @@ def create_sshd_ipa_config(options): + logger.info('Configured %s', paths.SSHD_IPA_CONFIG) + + +-def configure_automount(options): ++def configure_automount(options, statestore): + logger.info('\nConfiguring automount:') + + args = [ +@@ -1286,12 +1286,15 @@ def configure_automount(options): + if not options.sssd: + args.append('--no-sssd') + ++ statestore.backup_state('installation', 'automount', True) + try: + result = run(args) + except Exception as e: + logger.error('Automount configuration failed: %s', str(e)) + else: + logger.info('%s', result.output_log) ++ finally: ++ statestore.delete_state('installation', 'automount') + + + def configure_nisdomain(options, domain, statestore): +@@ -3305,7 +3308,11 @@ def _install(options, tdict): + configure_sshd_config(fstore, options) + + if options.location: +- configure_automount(options) ++ configure_automount(options, statestore) ++ ++ # Reload the state as automount install may have modified it ++ fstore._load() ++ statestore._load() + + if options.configure_firefox: + configure_firefox(options, statestore, cli_domain) +@@ -3368,12 +3375,15 @@ def uninstall(options): + fstore = sysrestore.FileStore(paths.IPA_CLIENT_SYSRESTORE) + statestore = sysrestore.StateFile(paths.IPA_CLIENT_SYSRESTORE) + ++ statestore.backup_state('installation', 'automount', True) + try: + run([paths.IPA_CLIENT_AUTOMOUNT, "--uninstall", "--debug"]) + except CalledProcessError as e: + if e.returncode != CLIENT_NOT_CONFIGURED: + logger.error( + "Unconfigured automount client failed: %s", str(e)) ++ finally: ++ statestore.delete_state('installation', 'automount') + + # Reload the state as automount unconfigure may have modified it + fstore._load() +diff --git a/ipaclient/install/ipa_client_automount.py b/ipaclient/install/ipa_client_automount.py +index b4b3387530afa9e80d13dd69e9d80080702f9e07..ee27872868b9ceaffdc58a9cf3fa89938e045526 100644 +--- a/ipaclient/install/ipa_client_automount.py ++++ b/ipaclient/install/ipa_client_automount.py +@@ -340,14 +340,16 @@ def configure_nfs(fstore, statestore, options): + + + def configure_automount(): +- try: +- check_client_configuration() +- except ScriptError as e: +- print(e.msg) +- sys.exit(e.rval) ++ statestore = sysrestore.StateFile(paths.IPA_CLIENT_SYSRESTORE) ++ if not statestore.get_state('installation', 'automount'): ++ # not called from ipa-client-install ++ try: ++ check_client_configuration() ++ except ScriptError as e: ++ print(e.msg) ++ sys.exit(e.rval) + + fstore = sysrestore.FileStore(paths.IPA_CLIENT_SYSRESTORE) +- statestore = sysrestore.StateFile(paths.IPA_CLIENT_SYSRESTORE) + + options, _args = parse_options() + +-- +2.43.0 + diff --git a/0035-ipatests-Test-client-install-uninstall-with-automoun.patch b/0035-ipatests-Test-client-install-uninstall-with-automoun.patch new file mode 100644 index 0000000..f275038 --- /dev/null +++ b/0035-ipatests-Test-client-install-uninstall-with-automoun.patch @@ -0,0 +1,73 @@ +From 18764964b72ba237eba5f7b1078185b2f0393d72 Mon Sep 17 00:00:00 2001 +From: Rob Crittenden +Date: Fri, 1 Dec 2023 10:47:24 -0500 +Subject: [PATCH] ipatests: Test client install/uninstall with automount + enabled + +The automount installation was failing. Confirm that it is fixed. + +The uninstall was not restoring all files/configuration. Verify +that the index and state files are gone which means that all state +and files were restored. + +Fixes: https://pagure.io/freeipa/issue/9487 + +Signed-off-by: Rob Crittenden +Reviewed-By: Christian Heimes +Reviewed-By: Florence Blanc-Renaud +--- + .../test_installation_client.py | 25 +++++++++++++++++++ + 1 file changed, 25 insertions(+) + +diff --git a/ipatests/test_integration/test_installation_client.py b/ipatests/test_integration/test_installation_client.py +index 56e1593bfcfa3eb7f9918fc6f2993d836884ea38..f8567b39eead4dffd522aad504fa72a086969257 100644 +--- a/ipatests/test_integration/test_installation_client.py ++++ b/ipatests/test_integration/test_installation_client.py +@@ -8,12 +8,14 @@ Module provides tests for various options of ipa-client-install. + + from __future__ import absolute_import + ++import os + import pytest + import re + import shlex + import textwrap + + from ipaplatform.paths import paths ++from ipalib.sysrestore import SYSRESTORE_STATEFILE, SYSRESTORE_INDEXFILE + from ipatests.test_integration.base import IntegrationTest + from ipatests.pytest_ipa.integration import tasks + from ipatests.pytest_ipa.integration.firewall import Firewall +@@ -90,6 +92,29 @@ class TestInstallClient(IntegrationTest): + assert 'includedir {dir}'.format( + dir=paths.SSSD_PUBCONF_KRB5_INCLUDE_D_DIR + ).encode() not in krb5_cfg ++ 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], ++ extra_args=['--automount-location', 'default']) ++ ++ def test_uninstall_with_automount(self): ++ """Test that uninstall with automount is successful and complete""" ++ tasks.uninstall_client(self.clients[0]) ++ index = os.path.join( ++ paths.IPA_CLIENT_SYSRESTORE, SYSRESTORE_INDEXFILE ++ ) ++ state = os.path.join( ++ paths.IPA_CLIENT_SYSRESTORE, SYSRESTORE_STATEFILE ++ ) ++ for filepath in (index, state): ++ try: ++ self.clients[0].get_file_contents(filepath) ++ except IOError: ++ pass ++ else: ++ pytest.fail("The client file %s was not removed" % filepath) + + + class TestClientInstallBind(IntegrationTest): +-- +2.43.0 + diff --git a/0036-ipa-client-automount-Don-t-use-deprecated-ipadiscove.patch b/0036-ipa-client-automount-Don-t-use-deprecated-ipadiscove.patch new file mode 100644 index 0000000..1c93282 --- /dev/null +++ b/0036-ipa-client-automount-Don-t-use-deprecated-ipadiscove.patch @@ -0,0 +1,66 @@ +From 526147ec9362124191a54c9ae8debd0234af3d49 Mon Sep 17 00:00:00 2001 +From: Rob Crittenden +Date: Fri, 1 Dec 2023 09:08:48 -0500 +Subject: [PATCH] ipa-client-automount: Don't use deprecated + ipadiscovery.IPADiscovery + +This class was moved to ipaclient/discovery.py in e6d560af66 to make +it available to PyPI. + +Related: https://pagure.io/freeipa/issue/9487 + +Signed-off-by: Rob Crittenden +Reviewed-By: Christian Heimes +Reviewed-By: Florence Blanc-Renaud +--- + ipaclient/install/ipa_client_automount.py | 10 +++++----- + 1 file changed, 5 insertions(+), 5 deletions(-) + +diff --git a/ipaclient/install/ipa_client_automount.py b/ipaclient/install/ipa_client_automount.py +index ee27872868b9ceaffdc58a9cf3fa89938e045526..297a784c4e1b6f1d29d51b6d3fd4b91d05672b9c 100644 +--- a/ipaclient/install/ipa_client_automount.py ++++ b/ipaclient/install/ipa_client_automount.py +@@ -36,7 +36,7 @@ from six.moves.urllib.parse import urlsplit + + from optparse import OptionParser # pylint: disable=deprecated-module + from ipapython import ipachangeconf +-from ipaclient.install import ipadiscovery ++from ipaclient import discovery + from ipaclient.install.client import ( + CLIENT_NOT_CONFIGURED, + CLIENT_ALREADY_CONFIGURED, +@@ -384,12 +384,12 @@ def configure_automount(): + sys.exit(CLIENT_ALREADY_CONFIGURED) + + autodiscover = False +- ds = ipadiscovery.IPADiscovery() ++ ds = discovery.IPADiscovery() + if not options.server: + print("Searching for IPA server...") + ret = ds.search(ca_cert_path=ca_cert_path) + logger.debug('Executing DNS discovery') +- if ret == ipadiscovery.NO_LDAP_SERVER: ++ if ret == discovery.NO_LDAP_SERVER: + logger.debug('Autodiscovery did not find LDAP server') + s = urlsplit(api.env.xmlrpc_uri) + server = [s.netloc] +@@ -409,14 +409,14 @@ def configure_automount(): + server = options.server + logger.debug("Verifying that %s is an IPA server", server) + ldapret = ds.ipacheckldap(server, api.env.realm, ca_cert_path) +- if ldapret[0] == ipadiscovery.NO_ACCESS_TO_LDAP: ++ if ldapret[0] == discovery.NO_ACCESS_TO_LDAP: + print("Anonymous access to the LDAP server is disabled.") + print("Proceeding without strict verification.") + print( + "Note: This is not an error if anonymous access has been " + "explicitly restricted." + ) +- elif ldapret[0] == ipadiscovery.NO_TLS_LDAP: ++ elif ldapret[0] == discovery.NO_TLS_LDAP: + logger.warning("Unencrypted access to LDAP is not supported.") + elif ldapret[0] != 0: + sys.exit('Unable to confirm that %s is an IPA server' % server) +-- +2.43.0 + diff --git a/0037-Server-affinity-Retain-user-requested-remote-server.patch b/0037-Server-affinity-Retain-user-requested-remote-server.patch new file mode 100644 index 0000000..66a939b --- /dev/null +++ b/0037-Server-affinity-Retain-user-requested-remote-server.patch @@ -0,0 +1,98 @@ +From d2ffa10df62bba45aa63232d3ad9a5ebf7158eea Mon Sep 17 00:00:00 2001 +From: Rob Crittenden +Date: Tue, 5 Dec 2023 14:34:31 -0500 +Subject: [PATCH] Server affinity: Retain user-requested remote server + +We want to avoid splitting a replica server installation between +two hosts where possible so if a CA or KRA is requested then +we only try to install against a remote server that also provides +those capabilities. This avoids race conditions. + +If a CA or KRA is not requested and the user has provided a +server to install against then use that instead of overriding it. + +Extend the logic of picking the remote Custodia mode +(KRA, CA, *MASTER*) to include considering whether the +CA and KRA services are requested. If the service(s) are +not requested the the associated hostname may not be +reliable. + +Fixes: https://pagure.io/freeipa/issue/9491 +Related: https://pagure.io/freeipa/issue/9289 + +Signed-off-by: Rob Crittenden +Reviewed-By: Florence Blanc-Renaud +--- + ipaserver/install/server/replicainstall.py | 19 +++++++++---------- + 1 file changed, 9 insertions(+), 10 deletions(-) + +diff --git a/ipaserver/install/server/replicainstall.py b/ipaserver/install/server/replicainstall.py +index 27fbdef8ec9aa5ae343352ebf3c61d74d65c8958..8096b6accb4c94fefdfcc06f19584c63c24d7baf 100644 +--- a/ipaserver/install/server/replicainstall.py ++++ b/ipaserver/install/server/replicainstall.py +@@ -782,6 +782,7 @@ def promotion_check_host_principal_auth_ind(conn, hostdn): + + + def remote_connection(config): ++ logger.debug("Creating LDAP connection to %s", config.master_host_name) + ldapuri = 'ldaps://%s' % ipautil.format_netloc(config.master_host_name) + xmlrpc_uri = 'https://{}/ipa/xml'.format( + ipautil.format_netloc(config.master_host_name)) +@@ -1087,7 +1088,7 @@ def promote_check(installer): + 'CA', conn, preferred_cas + ) + if ca_host is not None: +- if config.master_host_name != ca_host: ++ if options.setup_ca and config.master_host_name != ca_host: + conn.disconnect() + del remote_api + config.master_host_name = ca_host +@@ -1096,8 +1097,7 @@ def promote_check(installer): + conn = remote_api.Backend.ldap2 + conn.connect(ccache=installer._ccache) + config.ca_host_name = ca_host +- config.master_host_name = ca_host +- ca_enabled = True ++ ca_enabled = True # There is a CA somewhere in the topology + if options.dirsrv_cert_files: + logger.error("Certificates could not be provided when " + "CA is present on some master.") +@@ -1135,7 +1135,7 @@ def promote_check(installer): + 'KRA', conn, preferred_kras + ) + if kra_host is not None: +- if config.master_host_name != kra_host: ++ if options.setup_kra and config.master_host_name != kra_host: + conn.disconnect() + del remote_api + config.master_host_name = kra_host +@@ -1143,10 +1143,9 @@ def promote_check(installer): + installer._remote_api = remote_api + conn = remote_api.Backend.ldap2 + conn.connect(ccache=installer._ccache) +- config.kra_host_name = kra_host +- config.ca_host_name = kra_host +- config.master_host_name = kra_host +- kra_enabled = True ++ config.kra_host_name = kra_host ++ config.ca_host_name = kra_host ++ kra_enabled = True # There is a KRA somewhere in the topology + if options.setup_kra and options.server and \ + kra_host != options.server: + # Installer was provided with a specific master +@@ -1372,10 +1371,10 @@ def install(installer): + otpd.create_instance('OTPD', config.host_name, + ipautil.realm_to_suffix(config.realm_name)) + +- if kra_enabled: ++ if options.setup_kra and kra_enabled: + # A KRA peer always provides a CA, too. + mode = custodiainstance.CustodiaModes.KRA_PEER +- elif ca_enabled: ++ elif options.setup_ca and ca_enabled: + mode = custodiainstance.CustodiaModes.CA_PEER + else: + mode = custodiainstance.CustodiaModes.MASTER_PEER +-- +2.43.0 + diff --git a/0038-get_directive-don-t-error-out-on-substring-mismatch.patch b/0038-get_directive-don-t-error-out-on-substring-mismatch.patch new file mode 100644 index 0000000..1447e72 --- /dev/null +++ b/0038-get_directive-don-t-error-out-on-substring-mismatch.patch @@ -0,0 +1,120 @@ +From 95b066d629de935bfb52e732ce52026e18e9c64d Mon Sep 17 00:00:00 2001 +From: Rob Crittenden +Date: Wed, 10 Jan 2024 16:45:12 -0500 +Subject: [PATCH] get_directive: don't error out on substring mismatch + +This function is designed to retrieve a value from an +ini-like file. In particular PKI CS.cfg. + +In an attempt to be more efficient a substring search, +using startswith(), is used before calling a regular +expression match. + +The problem is that if the requested directive is a +substring of a different one then it will pass the +startswith() and fail the regular expression match +with a ValueError, assuming it is malformed. + +There is no need for this. The caller must be able to +handle None as a response anyway. So continue if +no match is found. + +This was seen when PKI dropped storing certificate blobs +in CS.cfg. The CA certificate is stored in ca.signing.cert. +If it isn't present then ca.signing.certnickname will match +the substring but not the directive. This should not be +treated as an error. + +Fixes: https://pagure.io/freeipa/issue/9506 + +Signed-off-by: Rob Crittenden +Reviewed-By: Florence Blanc-Renaud +--- + ipapython/directivesetter.py | 5 ++- + .../test_ipapython/test_directivesetter.py | 33 +++++++++++++++++++ + 2 files changed, 37 insertions(+), 1 deletion(-) + +diff --git a/ipapython/directivesetter.py b/ipapython/directivesetter.py +index f4e496c7f0f785a909bfb5b8196582fb5dd865ea..732e1c239ca375e6ec08882e4731f97cb1ff58a9 100644 +--- a/ipapython/directivesetter.py ++++ b/ipapython/directivesetter.py +@@ -182,6 +182,9 @@ def get_directive(filename, directive, separator=' '): + if separator == ' ': + separator = '[ \t]+' + ++ if directive is None: ++ return None ++ + result = None + with open(filename, "r") as fd: + for line in fd: +@@ -193,7 +196,7 @@ def get_directive(filename, directive, separator=' '): + if match: + value = match.group(1) + else: +- raise ValueError("Malformed directive: {}".format(line)) ++ continue + + result = unquote_directive_value(value.strip(), '"') + result = result.strip(' ') +diff --git a/ipatests/test_ipapython/test_directivesetter.py b/ipatests/test_ipapython/test_directivesetter.py +index 08a30124b12c3bd8edf8fa7930377faf7b181f5d..ff86559e0a3eb018e4a26a489c190a0da380ce1f 100644 +--- a/ipatests/test_ipapython/test_directivesetter.py ++++ b/ipatests/test_ipapython/test_directivesetter.py +@@ -18,6 +18,10 @@ WHITESPACE_CONFIG = [ + 'foobar\t2\n', + ] + ++SUBSTRING_CONFIG = [ ++ 'foobar=2\n', ++] ++ + + class test_set_directive_lines: + def test_remove_directive(self): +@@ -88,6 +92,7 @@ class test_set_directive: + + class test_get_directive: + def test_get_directive(self, tmpdir): ++ """Test retrieving known values from a config file""" + configfile = tmpdir.join('config') + configfile.write(''.join(EXAMPLE_CONFIG)) + +@@ -97,6 +102,34 @@ class test_get_directive: + assert '2' == directivesetter.get_directive(str(configfile), + 'foobar', + separator='=') ++ assert None is directivesetter.get_directive(str(configfile), ++ 'notfound', ++ separator='=') ++ ++ def test_get_directive_substring(self, tmpdir): ++ """Test retrieving values from a config file where there is ++ a similar substring that is not present. ++ """ ++ configfile = tmpdir.join('config') ++ configfile.write(''.join(SUBSTRING_CONFIG)) ++ ++ assert None is directivesetter.get_directive(str(configfile), ++ 'foo', ++ separator='=') ++ assert '2' == directivesetter.get_directive(str(configfile), ++ 'foobar', ++ separator='=') ++ ++ def test_get_directive_none(self, tmpdir): ++ """Test retrieving a value from a config file where the ++ directive is None. i.e. don't fail. ++ """ ++ configfile = tmpdir.join('config') ++ configfile.write(''.join(EXAMPLE_CONFIG)) ++ ++ assert None is directivesetter.get_directive(str(configfile), ++ None, ++ separator='=') + + + class test_get_directive_whitespace: +-- +2.43.0 + diff --git a/0039-host-update-System-Manage-Host-Keytab-permission.patch b/0039-host-update-System-Manage-Host-Keytab-permission.patch new file mode 100644 index 0000000..a4c46a9 --- /dev/null +++ b/0039-host-update-System-Manage-Host-Keytab-permission.patch @@ -0,0 +1,101 @@ +From 3842116185de6ae8714f30b57bd75c7eddde53d8 Mon Sep 17 00:00:00 2001 +From: Alexander Bokovoy +Date: Thu, 21 Dec 2023 09:38:57 +0200 +Subject: [PATCH] host: update System: Manage Host Keytab permission + +Since commit 5c0e7a5fb420377dcc06a956695afdcb35196444, a new extended +operation to get a keytab is supposed to be used. This keytab +setting/retrieval extended operation checks access rights of the bound +DN to write to a virtual attribute 'ipaProtectedOperation;write_keys'. + +If the write isn't allowed, the operation is rejected and ipa-getkeytab +tool falls back to an older code that generates the keytab on the client +and forcibly sets to the LDAP entry. For the latter, a check is done to +make sure the bound DN is allowed to write to 'krbPrincipalKey' attribute. + +This fallback should never happen for newer deployments. When enrollemnt +operation is delegated to non-administrative user with the help of 'Host +Enrollment' role, a host can be pre-created or created at enrollment +time, if this non-administrative user has 'Host Administrators' role. In +the latter case a system permission 'System: Manage Host Keytab' grants +write access to 'krbPrincipalKey' attribute but lacks any access to the +virtual attributes expected by the new extended operation. + +There is a second virtual attribute, 'ipaProtectedOperation;read_keys', +that allows to retrieve existing keys for a host. However, during +initial enrollment we do not allow to retrieve and reuse existing +Kerberos key: while 'ipa-getkeytab -r' would give ability to retrieve +the existing key, 'ipa-join' has no way to trigger that operation. +Hence, permission 'System: Manage Host Keytab' will not grant the right +to read the Kerberos key via extended operation used by 'ipa-getkeytab +-r'. Such operation can be done later by utilizing 'ipa +service/host-allow-retrieve-keytab' commands. + +Fix 'System: Manage Host Keytab' permission and extend a permission test +to see that we do not fallback to the old extended operation. + +Fixes: https://pagure.io/freeipa/issue/9496 + +Signed-off-by: Alexander Bokovoy +Reviewed-By: Rob Crittenden +--- + ACI.txt | 2 +- + ipaserver/plugins/host.py | 3 ++- + ipatests/test_integration/test_user_permissions.py | 7 +++++++ + 3 files changed, 10 insertions(+), 2 deletions(-) + +diff --git a/ACI.txt b/ACI.txt +index e6d6e3d1586c098f528d17fe940a1364b415654f..236bb43677bd9d84798a7ab418412b337fbf5c59 100644 +--- a/ACI.txt ++++ b/ACI.txt +@@ -147,7 +147,7 @@ aci: (targetattr = "usercertificate")(targetfilter = "(objectclass=ipahost)")(ve + dn: cn=computers,cn=accounts,dc=ipa,dc=example + aci: (targetattr = "userpassword")(targetfilter = "(objectclass=ipahost)")(version 3.0;acl "permission:System: Manage Host Enrollment Password";allow (write) groupdn = "ldap:///cn=System: Manage Host Enrollment Password,cn=permissions,cn=pbac,dc=ipa,dc=example";) + dn: cn=computers,cn=accounts,dc=ipa,dc=example +-aci: (targetattr = "krblastpwdchange || krbprincipalkey")(targetfilter = "(&(!(memberOf=cn=ipaservers,cn=hostgroups,cn=accounts,dc=ipa,dc=example))(objectclass=ipahost))")(version 3.0;acl "permission:System: Manage Host Keytab";allow (write) groupdn = "ldap:///cn=System: Manage Host Keytab,cn=permissions,cn=pbac,dc=ipa,dc=example";) ++aci: (targetattr = "ipaprotectedoperation;write_keys || krblastpwdchange || krbprincipalkey")(targetfilter = "(&(!(memberOf=cn=ipaservers,cn=hostgroups,cn=accounts,dc=ipa,dc=example))(objectclass=ipahost))")(version 3.0;acl "permission:System: Manage Host Keytab";allow (write) groupdn = "ldap:///cn=System: Manage Host Keytab,cn=permissions,cn=pbac,dc=ipa,dc=example";) + dn: cn=computers,cn=accounts,dc=ipa,dc=example + aci: (targetattr = "createtimestamp || entryusn || ipaallowedtoperform;read_keys || ipaallowedtoperform;write_keys || modifytimestamp || objectclass")(targetfilter = "(objectclass=ipahost)")(version 3.0;acl "permission:System: Manage Host Keytab Permissions";allow (compare,read,search,write) groupdn = "ldap:///cn=System: Manage Host Keytab Permissions,cn=permissions,cn=pbac,dc=ipa,dc=example";) + dn: cn=computers,cn=accounts,dc=ipa,dc=example +diff --git a/ipaserver/plugins/host.py b/ipaserver/plugins/host.py +index 3ef510edc77a07ad504f07614d0c5524a3c34646..b02c8b55fde037c5fa0a9c73575b22e3c7177806 100644 +--- a/ipaserver/plugins/host.py ++++ b/ipaserver/plugins/host.py +@@ -409,7 +409,8 @@ class host(LDAPObject): + api.env.container_hostgroup, + api.env.basedn), + ], +- 'ipapermdefaultattr': {'krblastpwdchange', 'krbprincipalkey'}, ++ 'ipapermdefaultattr': {'krblastpwdchange', 'krbprincipalkey', ++ 'ipaprotectedoperation;write_keys'}, + 'replaces': [ + '(targetattr = "krbprincipalkey || krblastpwdchange")(target = "ldap:///fqdn=*,cn=computers,cn=accounts,$SUFFIX")(version 3.0;acl "permission:Manage host keytab";allow (write) groupdn = "ldap:///cn=Manage host keytab,cn=permissions,cn=pbac,$SUFFIX";)', + ], +diff --git a/ipatests/test_integration/test_user_permissions.py b/ipatests/test_integration/test_user_permissions.py +index 3333a4f6b961961aea7dadf2eb36d7ed3b31e410..cd1096ff3582f9c0cfdb16b5ed876164139f1a1b 100644 +--- a/ipatests/test_integration/test_user_permissions.py ++++ b/ipatests/test_integration/test_user_permissions.py +@@ -277,6 +277,9 @@ class TestInstallClientNoAdmin(IntegrationTest): + self.master.run_command(['ipa', 'privilege-add-permission', + '--permissions', 'System: Add Hosts', + 'Add Hosts']) ++ self.master.run_command(['ipa', 'privilege-add-permission', ++ '--permissions', 'System: Manage Host Keytab', ++ 'Add Hosts']) + + self.master.run_command(['ipa', 'role-add-privilege', 'useradmin', + '--privileges', 'Host Enrollment']) +@@ -301,6 +304,10 @@ class TestInstallClientNoAdmin(IntegrationTest): + encoding='utf-8') + assert msg in install_log + ++ # Make sure we do not fallback to an old keytab retrieval method anymore ++ msg = "Retrying with pre-4.0 keytab retrieval method..." ++ assert msg not in install_log ++ + # check that user is able to request a host cert, too + result = tasks.run_certutil(client, ['-L'], paths.IPA_NSSDB_DIR) + assert 'Local IPA host' in result.stdout_text +-- +2.43.0 + diff --git a/0040-adtrustinstance-make-sure-NetBIOS-name-defaults-are-.patch b/0040-adtrustinstance-make-sure-NetBIOS-name-defaults-are-.patch new file mode 100644 index 0000000..fbd49d7 --- /dev/null +++ b/0040-adtrustinstance-make-sure-NetBIOS-name-defaults-are-.patch @@ -0,0 +1,35 @@ +From eab52d3cda9bbec716008c040551bd11facd0e11 Mon Sep 17 00:00:00 2001 +From: Alexander Bokovoy +Date: Wed, 17 Jan 2024 12:27:26 +0200 +Subject: [PATCH] adtrustinstance: make sure NetBIOS name defaults are set + properly + +Some tools may pass None as NetBIOS name if not put explicitly by a +user. This meant to use default NetBIOS name generator based on the +domain (realm) name. However, this wasn't done properly, so None is +passed later to python-ldap and it rejects such LDAP entry. + +Fixes: https://pagure.io/freeipa/issue/9514 + +Signed-off-by: Alexander Bokovoy +Reviewed-By: Florence Blanc-Renaud +--- + ipaserver/install/adtrustinstance.py | 2 ++ + 1 file changed, 2 insertions(+) + +diff --git a/ipaserver/install/adtrustinstance.py b/ipaserver/install/adtrustinstance.py +index d55ba849157bee8e335e2e0772514fc15ec11193..2ff68dfb46371a6118eb67515347eb762a37e1ec 100644 +--- a/ipaserver/install/adtrustinstance.py ++++ b/ipaserver/install/adtrustinstance.py +@@ -189,6 +189,8 @@ class ADTRUSTInstance(service.Service): + self.fqdn = self.fqdn or api.env.host + self.host_netbios_name = make_netbios_name(self.fqdn) + self.realm = self.realm or api.env.realm ++ if not self.netbios_name: ++ self.netbios_name = make_netbios_name(self.realm) + + self.suffix = ipautil.realm_to_suffix(self.realm) + self.ldapi_socket = "%%2fvar%%2frun%%2fslapd-%s.socket" % \ +-- +2.43.0 + diff --git a/0041-Server-affinity-Don-t-rely-just-on-ca-kra-_enabled-f.patch b/0041-Server-affinity-Don-t-rely-just-on-ca-kra-_enabled-f.patch new file mode 100644 index 0000000..e6aed29 --- /dev/null +++ b/0041-Server-affinity-Don-t-rely-just-on-ca-kra-_enabled-f.patch @@ -0,0 +1,54 @@ +From 851ce93ac07044172a7db56d54ab9e1d7c7ec79f Mon Sep 17 00:00:00 2001 +From: Rob Crittenden +Date: Mon, 15 Jan 2024 09:05:58 -0500 +Subject: [PATCH] Server affinity: Don't rely just on [ca|kra]_enabled for + installs + +ca_enable and kra_enabled are intended to be used to identify that +a CA or KRA is available in the topology. It was also being used +to determine whether a CA or KRA service is desired on a replica +install, rather than options.setup_[ca|kra] + +Fixes: https://pagure.io/freeipa/issue/9510 + +Signed-off-by: Rob Crittenden +Reviewed-By: Florence Blanc-Renaud +--- + ipaserver/install/server/replicainstall.py | 7 ++++--- + 1 file changed, 4 insertions(+), 3 deletions(-) + +diff --git a/ipaserver/install/server/replicainstall.py b/ipaserver/install/server/replicainstall.py +index 8096b6accb4c94fefdfcc06f19584c63c24d7baf..191913ddb973b94bcd8ad920570edcee27349ffd 100644 +--- a/ipaserver/install/server/replicainstall.py ++++ b/ipaserver/install/server/replicainstall.py +@@ -1143,7 +1143,8 @@ def promote_check(installer): + installer._remote_api = remote_api + conn = remote_api.Backend.ldap2 + conn.connect(ccache=installer._ccache) +- config.kra_host_name = kra_host ++ config.kra_host_name = kra_host ++ if options.setup_kra: # only reset ca_host if KRA is requested + config.ca_host_name = kra_host + kra_enabled = True # There is a KRA somewhere in the topology + if options.setup_kra and options.server and \ +@@ -1381,7 +1382,7 @@ def install(installer): + custodia = custodiainstance.get_custodia_instance(config, mode) + custodia.create_instance() + +- if ca_enabled: ++ if options.setup_ca and ca_enabled: + options.realm_name = config.realm_name + options.domain_name = config.domain_name + options.host_name = config.host_name +@@ -1397,7 +1398,7 @@ def install(installer): + service.print_msg("Finalize replication settings") + ds.finalize_replica_config() + +- if kra_enabled: ++ if options.setup_kra and kra_enabled: + kra.install(api, config, options, custodia=custodia) + + service.print_msg("Restarting the KDC") +-- +2.43.0 + diff --git a/freeipa.spec b/freeipa.spec index 7f2c12e..c85aca9 100644 --- a/freeipa.spec +++ b/freeipa.spec @@ -223,7 +223,7 @@ Name: %{package_name} Version: %{IPA_VERSION} -Release: 4%{?rc_version:.%rc_version}%{?dist} +Release: 5%{?rc_version:.%rc_version}%{?dist} Summary: The Identity, Policy and Audit system License: GPL-3.0-or-later @@ -260,6 +260,34 @@ Patch0010: 0010-ipatests-ignore-nsslapd-accesslog-logbuffering-WARN-.patch Patch0011: 0011-ipatests-fix-expected-output-for-ipahealthcheck.ipa..patch Patch0012: 0012-group-add-member-fails-with-an-external-member.patch Patch0013: 0013-Handle-samba-changes-in-samba.security.dom_sid.patch +Patch0014: 0014-test_install-restart-services-after-date-change.patch +Patch0015: 0015-Issue-9497-Add-new-password-policy-logging-function.patch +Patch0016: 0016-Issue-9497-Update-logging-in-ipa_enrollment.patch +Patch0017: 0017-Issue-9497-update-debug-logging-in-ipa_graceperiod.patch +Patch0018: 0018-Issue-9497-update-debug-logging-in-ipa_lockout.patch +Patch0019: 0019-Issue-9497-update-debug-logging-in-ipa_modrdn.patch +Patch0020: 0020-Issue-9497-update-debug-logging-in-ipa_otp_counter.patch +Patch0021: 0021-Issue-9497-update-debug-logging-in-ipa_otp_lasttoken.patch +Patch0022: 0022-Issue-9497-update-debug-logging-in-ipa-pwd-extop.patch +Patch0023: 0023-Issue-9497-update-debug-logging-in-ipa_uuid.patch +Patch0024: 0024-hbactest-was-not-collecting-or-returning-messages.patch +Patch0025: 0025-ipatests-Verify-that-hbactest-will-return-messages.patch +Patch0026: 0026-ipa-kdb-add-better-detection-of-allowed-user-auth-ty.patch +Patch0027: 0027-ipa-kdb-when-applying-ticket-policy-do-not-deny-PKIN.patch +Patch0028: 0028-ipa-kdb-clarify-user-auth-table-mapping-use-of-_AUTH.patch +Patch0029: 0029-ipatests-make-sure-PKINIT-enrollment-works-with-a-st.patch +Patch0030: 0030-Check-the-HTTP-Referer-header-on-all-requests.patch +Patch0031: 0031-Integration-tests-for-verifying-Referer-header-in-th.patch +Patch0032: 0032-ipatests-Skip-ds_encryption-tests-on-RHEL9-SUT.patch +Patch0033: 0033-ACME-Don-t-treat-pki-server-ca-config-show-failures-.patch +Patch0034: 0034-Fix-ipa-client-automount-install-uninstall-with-new-.patch +Patch0035: 0035-ipatests-Test-client-install-uninstall-with-automoun.patch +Patch0036: 0036-ipa-client-automount-Don-t-use-deprecated-ipadiscove.patch +Patch0037: 0037-Server-affinity-Retain-user-requested-remote-server.patch +Patch0038: 0038-get_directive-don-t-error-out-on-substring-mismatch.patch +Patch0039: 0039-host-update-System-Manage-Host-Keytab-permission.patch +Patch0040: 0040-adtrustinstance-make-sure-NetBIOS-name-defaults-are-.patch +Patch0041: 0041-Server-affinity-Don-t-rely-just-on-ca-kra-_enabled-f.patch Patch1001: 1001-Change-branding-to-IPA-and-Identity-Management.patch %endif %endif @@ -1752,6 +1780,19 @@ fi %endif %changelog +* Thu Jan 18 2024 Florence Blanc-Renaud - 4.11.0-4 +- Resolves: RHEL-12589 ipa: Invalid CSRF protection +- Resolves: RHEL-19748 ipa hbac-test did not report that it hit an arbitrary search limit +- Resolves: RHEL-21059 'DogtagCertsConfigCheck' fails, displaying the error message 'Malformed directive: ca.signing.certnickname=caSigningCert cert-pki-ca' +- Resolves: RHEL-21804 ipa client 4.10.2 - Failed to obtain host TGT +- Resolves: RHEL-21809 CA less servers are failing to be added in topology segment for domain suffix +- Resolves: RHEL-21810 ipa-client-install --automount-location does not work +- Resolves: RHEL-21811 Handle change in behavior of pki-server ca-config-show in pki 11.5.0 +- Resolves: RHEL-21812 Backport latest test fixes in ipa +- Resolves: RHEL-21813 krb5kdc fails to start when pkinit and otp auth type is enabled in ipa +- Resolves: RHEL-21815 IPA 389ds plugins need to have better logging and tracing +- Resolves: RHEL-21937 Make sure a default NetBIOS name is set if not passed in by ADTrust instance constructor + * Fri Dec 1 2023 Florence Blanc-Renaud - 4.11.0-4 - Resolves: RHEL-16985 Handle samba 4.19 changes in samba.security.dom_sid()