From 544169197ea8c580b3c408df7f94ab79aced2d76 Mon Sep 17 00:00:00 2001 From: xzyang Date: Thu, 23 Dec 2021 18:43:43 +0800 Subject: [PATCH] update to sssd-1.16.5-10.el7_9.10.src.rpm Signed-off-by: xzyang --- ...t-try-to-refresh-domains-from-other-.patch | 37 + 0040-UTIL-DN-sanitization.patch | 278 +++ ...ss_sanitize_dn-where-we-deal-with-DN.patch | 121 + ..._sanitize_dn-where-we-deal-with-DN-2.patch | 63 + ...r-DN-to-create-ghost-user-hash-table.patch | 123 + ...sysdb_attrs-fixed-to-avoid-NULL-ptr-.patch | 65 + 0045-DEBUG-journal_send-was-made-static.patch | 30 + ...program-identifier-as-seen-in-syslog.patch | 72 + 0047-ifp-fix-use-after-free.patch | 37 + ...inal-fix-use-after-free-1.16-version.patch | 44 + ...ry-pkinit-with-Smartcard-credentials.patch | 40 + ...re-domain-config-does-not-leak-into-.patch | 37 + ...D_SUBDOMAINS-flag-for-get_next_domai.patch | 108 + ...re-short-names-are-added-to-sub-doma.patch | 445 ++++ ...che-do-not-use-default_domain_suffix.patch | 155 ++ ...-ad_allow_remote_domain_local_groups.patch | 193 ++ ...scription-of-dns_resolver_op_timeout.patch | 37 + ...-description-of-dns_resolver_timeout.patch | 32 + ...d-dns_resolver_server_timeout-option.patch | 277 +++ ...roups-are-filtered-during-initgroups.patch | 113 + 0059-CACHE-Create-timestamp-if-missing.patch | 50 + ...-test-for-recreating-cache-timestamp.patch | 142 ++ 0061-cert-matching.patch | 2180 +++++++++++++++++ ..._by_object_name_ex-changed-log-level.patch | 36 + ...ch-by-low-usn-value-to-improve-perfo.patch | 122 + ...-modifytimestamp-debugging-leftovers.patch | 36 + ...ss_domain_info-add-not_found_counter.patch | 67 + ...ed-domains-from-local-domain-as-well.patch | 244 ++ ...ht-domain-in-nss_protocol_fill_initg.patch | 107 + ...install_tls-when-watchdog-interrupti.patch | 180 ++ ...Add-search-index-originalADgidNumber.patch | 165 ++ ...override-LDAP-data-during-GC-lookups.patch | 66 + ...ix-memory-leak-while-reloading-lists.patch | 103 + 0072-TOOLS-replace-system-with-execvp.patch | 277 +++ 0073-cldap.patch | 2158 ++++++++++++++++ sssd.spec | 111 +- 36 files changed, 8340 insertions(+), 11 deletions(-) create mode 100644 0039-BE_REFRESH-Do-not-try-to-refresh-domains-from-other-.patch create mode 100644 0040-UTIL-DN-sanitization.patch create mode 100644 0041-UTIL-Use-sss_sanitize_dn-where-we-deal-with-DN.patch create mode 100644 0042-UTIL-Use-sss_sanitize_dn-where-we-deal-with-DN-2.patch create mode 100644 0043-ldap-use-member-DN-to-create-ghost-user-hash-table.patch create mode 100644 0044-SYSDB-merge_res_sysdb_attrs-fixed-to-avoid-NULL-ptr-.patch create mode 100644 0045-DEBUG-journal_send-was-made-static.patch create mode 100644 0046-DEBUG-fixes-program-identifier-as-seen-in-syslog.patch create mode 100644 0047-ifp-fix-use-after-free.patch create mode 100644 0048-fp-fix-original-fix-use-after-free-1.16-version.patch create mode 100644 0049-krb5-only-try-pkinit-with-Smartcard-credentials.patch create mode 100644 0050-negcache-make-sure-domain-config-does-not-leak-into-.patch create mode 100644 0051-utils-add-SSS_GND_SUBDOMAINS-flag-for-get_next_domai.patch create mode 100644 0052-negcache-make-sure-short-names-are-added-to-sub-doma.patch create mode 100644 0053-negcache-do-not-use-default_domain_suffix.patch create mode 100644 0054-ad-add-ad_allow_remote_domain_local_groups.patch create mode 100644 0055-man-fix-description-of-dns_resolver_op_timeout.patch create mode 100644 0056-man-fix-description-of-dns_resolver_timeout.patch create mode 100644 0057-failover-add-dns_resolver_server_timeout-option.patch create mode 100644 0058-nss-check-if-groups-are-filtered-during-initgroups.patch create mode 100644 0059-CACHE-Create-timestamp-if-missing.patch create mode 100644 0060-TESTS-Add-test-for-recreating-cache-timestamp.patch create mode 100644 0061-cert-matching.patch create mode 100644 0062-UTIL-find_domain_by_object_name_ex-changed-log-level.patch create mode 100644 0063-sudo-do-not-search-by-low-usn-value-to-improve-perfo.patch create mode 100644 0064-ldap-fix-modifytimestamp-debugging-leftovers.patch create mode 100644 0065-sss_domain_info-add-not_found_counter.patch create mode 100644 0066-AD-read-trusted-domains-from-local-domain-as-well.patch create mode 100644 0067-negcache-use-right-domain-in-nss_protocol_fill_initg.patch create mode 100644 0068-ldap-retry-ldap_install_tls-when-watchdog-interrupti.patch create mode 100644 0069-SYSDB-Add-search-index-originalADgidNumber.patch create mode 100644 0070-AD-do-not-override-LDAP-data-during-GC-lookups.patch create mode 100644 0071-simple-fix-memory-leak-while-reloading-lists.patch create mode 100644 0072-TOOLS-replace-system-with-execvp.patch create mode 100644 0073-cldap.patch diff --git a/0039-BE_REFRESH-Do-not-try-to-refresh-domains-from-other-.patch b/0039-BE_REFRESH-Do-not-try-to-refresh-domains-from-other-.patch new file mode 100644 index 0000000..5eeafe6 --- /dev/null +++ b/0039-BE_REFRESH-Do-not-try-to-refresh-domains-from-other-.patch @@ -0,0 +1,37 @@ +From 154d73a06f1eb1fda85da54034ad79ba9bcd485b Mon Sep 17 00:00:00 2001 +From: Lukas Slebodnik +Date: Wed, 22 Jan 2020 09:43:21 +0000 +Subject: [PATCH] BE_REFRESH: Do not try to refresh domains from other backends + +We cannot refresh domains from different sssd_be processes. +We can refresh just subdomains + +Resolves: +https://pagure.io/SSSD/sssd/issue/4142 + +Merges: https://pagure.io/SSSD/sssd/pull-request/4139 + +Reviewed-by: Sumit Bose +(cherry picked from commit 007d5b79b7aef67dd843ed9a3b65095faaeb580f) +--- + src/providers/be_refresh.c | 4 ++++ + 1 file changed, 4 insertions(+) + +diff --git a/src/providers/be_refresh.c b/src/providers/be_refresh.c +index 8fcfb86b4..fd63dae66 100644 +--- a/src/providers/be_refresh.c ++++ b/src/providers/be_refresh.c +@@ -388,6 +388,10 @@ static errno_t be_refresh_step(struct tevent_req *req) + if (state->index == BE_REFRESH_TYPE_SENTINEL) { + state->domain = get_next_domain(state->domain, + SSS_GND_DESCEND); ++ /* we can update just subdomains */ ++ if (state->domain != NULL && !IS_SUBDOMAIN(state->domain)) { ++ break; ++ } + state->index = 0; + continue; + } +-- +2.21.3 + diff --git a/0040-UTIL-DN-sanitization.patch b/0040-UTIL-DN-sanitization.patch new file mode 100644 index 0000000..e2bbb32 --- /dev/null +++ b/0040-UTIL-DN-sanitization.patch @@ -0,0 +1,278 @@ +From d0cc9f3b8b8b7aebb4980d1eb7228f0df2051672 Mon Sep 17 00:00:00 2001 +From: Tomas Halman +Date: Fri, 31 Jul 2020 11:12:02 +0200 +Subject: [PATCH 40/41] UTIL: DN sanitization + +Some of the ldap servers returns DN in attributes such as isMemberOf +with spaces like dc=example, dc=com. That should be fine and we +should ignore them (cut them out) instead of escaping. + +Resolves: +https://github.com/SSSD/sssd/issues/5261 +(cherry picked from commit 882307cdc1b596ba0cc346a0001f4fc014818d82) +--- + src/tests/cmocka/test_utils.c | 70 +++++++++++++++++++ + src/util/util.c | 127 ++++++++++++++++++++++++++++++++++ + src/util/util.h | 20 ++++++ + 3 files changed, 217 insertions(+) + +diff --git a/src/tests/cmocka/test_utils.c b/src/tests/cmocka/test_utils.c +index bd2c9e65d..aa245f00b 100644 +--- a/src/tests/cmocka/test_utils.c ++++ b/src/tests/cmocka/test_utils.c +@@ -1935,6 +1935,73 @@ static void test_sss_get_domain_mappings_content(void **state) + * capaths might not be as expected. */ + } + ++ ++static void test_sss_filter_sanitize_dn(void **state) ++{ ++ TALLOC_CTX *tmp_ctx; ++ char *trimmed; ++ int ret; ++ const char *DN = "cn=user,ou=people,dc=example,dc=com"; ++ ++ tmp_ctx = talloc_new(NULL); ++ assert_non_null(tmp_ctx); ++ ++ /* test that we remove spaces around '=' and ','*/ ++ ret = sss_filter_sanitize_dn(tmp_ctx, DN, &trimmed); ++ assert_int_equal(ret, EOK); ++ assert_string_equal(DN, trimmed); ++ talloc_free(trimmed); ++ ++ ret = sss_filter_sanitize_dn(tmp_ctx, "cn=user,ou=people,dc=example,dc=com", &trimmed); ++ assert_int_equal(ret, EOK); ++ assert_string_equal(DN, trimmed); ++ talloc_free(trimmed); ++ ++ ret = sss_filter_sanitize_dn(tmp_ctx, "cn= user,ou =people,dc = example,dc = com", &trimmed); ++ assert_int_equal(ret, EOK); ++ assert_string_equal(DN, trimmed); ++ talloc_free(trimmed); ++ ++ ret = sss_filter_sanitize_dn(tmp_ctx, "cn=user, ou=people ,dc=example , dc=com", &trimmed); ++ assert_int_equal(ret, EOK); ++ assert_string_equal(DN, trimmed); ++ talloc_free(trimmed); ++ ++ ret = sss_filter_sanitize_dn(tmp_ctx, "cn=user, ou=people ,dc=example , dc=com", &trimmed); ++ assert_int_equal(ret, EOK); ++ assert_string_equal(DN, trimmed); ++ talloc_free(trimmed); ++ ++ ret = sss_filter_sanitize_dn(tmp_ctx, "cn= user, ou =people ,dc = example , dc = com", &trimmed); ++ assert_int_equal(ret, EOK); ++ assert_string_equal(DN, trimmed); ++ talloc_free(trimmed); ++ ++ ret = sss_filter_sanitize_dn(tmp_ctx, " cn=user,ou=people,dc=example,dc=com ", &trimmed); ++ assert_int_equal(ret, EOK); ++ assert_string_equal(DN, trimmed); ++ talloc_free(trimmed); ++ ++ ret = sss_filter_sanitize_dn(tmp_ctx, " cn=user, ou=people, dc=example, dc=com ", &trimmed); ++ assert_int_equal(ret, EOK); ++ assert_string_equal(DN, trimmed); ++ talloc_free(trimmed); ++ ++ /* test that we keep spaces inside a value */ ++ ret = sss_filter_sanitize_dn(tmp_ctx, "cn = user one, ou=people branch, dc=example, dc=com", &trimmed); ++ assert_int_equal(ret, EOK); ++ assert_string_equal("cn=user\\20one,ou=people\\20\\20branch,dc=example,dc=com", trimmed); ++ talloc_free(trimmed); ++ ++ /* test that we keep escape special chars like () */ ++ ret = sss_filter_sanitize_dn(tmp_ctx, "cn = user one, ou=p(e)ople, dc=example, dc=com", &trimmed); ++ assert_int_equal(ret, EOK); ++ assert_string_equal("cn=user\\20one,ou=p\\28e\\29ople,dc=example,dc=com", trimmed); ++ talloc_free(trimmed); ++ ++ talloc_free(tmp_ctx); ++} ++ + int main(int argc, const char *argv[]) + { + poptContext pc; +@@ -2044,6 +2111,9 @@ int main(int argc, const char *argv[]) + cmocka_unit_test_setup_teardown(test_sss_ptr_hash_without_cb, + setup_leak_tests, + teardown_leak_tests), ++ cmocka_unit_test_setup_teardown(test_sss_filter_sanitize_dn, ++ setup_leak_tests, ++ teardown_leak_tests), + }; + + /* Set debug level to invalid value so we can decide if -d 0 was used. */ +diff --git a/src/util/util.c b/src/util/util.c +index e3efa7fef..4051c1f4e 100644 +--- a/src/util/util.c ++++ b/src/util/util.c +@@ -530,6 +530,133 @@ errno_t sss_filter_sanitize(TALLOC_CTX *mem_ctx, + return sss_filter_sanitize_ex(mem_ctx, input, sanitized, NULL); + } + ++ ++/* There is similar function ldap_dn_normalize in openldap. ++ * To avoid dependecies across project we have this own func. ++ * Also ldb can do this but doesn't handle all the cases ++ */ ++static errno_t sss_trim_dn(TALLOC_CTX *mem_ctx, ++ const char *input, ++ char **trimmed) ++{ ++ int i = 0; ++ int o = 0; ++ int s; ++ char *output; ++ enum sss_trim_dn_state { ++ SSS_TRIM_DN_STATE_READING_NAME, ++ SSS_TRIM_DN_STATE_READING_VALUE ++ } state = SSS_TRIM_DN_STATE_READING_NAME; ++ ++ *trimmed = NULL; ++ ++ output = talloc_array(mem_ctx, char, strlen(input) + 1); ++ if (!output) { ++ return ENOMEM; ++ } ++ ++ /* skip leading spaces */ ++ while(isspace(input[i])) { ++ ++i; ++ } ++ ++ while(input[i] != '\0') { ++ if (!isspace(input[i])) { ++ switch (input[i]) { ++ case '=': ++ output[o++] = input[i++]; ++ if (state == SSS_TRIM_DN_STATE_READING_NAME) { ++ while (isspace(input[i])) { ++ ++i; ++ } ++ state = SSS_TRIM_DN_STATE_READING_VALUE; ++ } ++ break; ++ case ',': ++ output[o++] = input[i++]; ++ if (state == SSS_TRIM_DN_STATE_READING_VALUE) { ++ while (isspace(input[i])) { ++ ++i; ++ } ++ state = SSS_TRIM_DN_STATE_READING_NAME; ++ } ++ break; ++ case '\\': ++ output[o++] = input[i++]; ++ if (input[i] != '\0') { ++ output[o++] = input[i++]; ++ } ++ break; ++ default: ++ if (input[i] != '\0') { ++ output[o++] = input[i++]; ++ } ++ break; ++ } ++ ++ continue; ++ } ++ ++ /* non escaped space found */ ++ s = 1; ++ while (isspace(input[i + s])) { ++ ++s; ++ } ++ ++ switch (state) { ++ case SSS_TRIM_DN_STATE_READING_NAME: ++ if (input[i + s] != '=') { ++ /* this is not trailing space - should not be removed */ ++ while (isspace(input[i])) { ++ output[o++] = input[i++]; ++ } ++ } else { ++ i += s; ++ } ++ break; ++ case SSS_TRIM_DN_STATE_READING_VALUE: ++ if (input[i + s] != ',') { ++ /* this is not trailing space - should not be removed */ ++ while (isspace(input[i])) { ++ output[o++] = input[i++]; ++ } ++ } else { ++ i += s; ++ } ++ break; ++ } ++ } ++ ++ output[o--] = '\0'; ++ ++ /* trim trailing space */ ++ while (o >= 0 && isspace(output[o])) { ++ output[o--] = '\0'; ++ } ++ ++ *trimmed = output; ++ return EOK; ++} ++ ++errno_t sss_filter_sanitize_dn(TALLOC_CTX *mem_ctx, ++ const char *input, ++ char **sanitized) ++{ ++ errno_t ret; ++ char *trimmed_dn = NULL; ++ ++ ret = sss_trim_dn(mem_ctx, input, &trimmed_dn); ++ if (ret != EOK) { ++ goto done; ++ } ++ ++ ret = sss_filter_sanitize_ex(mem_ctx, trimmed_dn, sanitized, NULL); ++ ++ done: ++ talloc_free(trimmed_dn); ++ return ret; ++} ++ + char * + sss_escape_ip_address(TALLOC_CTX *mem_ctx, int family, const char *addr) + { +diff --git a/src/util/util.h b/src/util/util.h +index 8dc887cab..d7d2017fa 100644 +--- a/src/util/util.h ++++ b/src/util/util.h +@@ -477,6 +477,26 @@ errno_t sss_filter_sanitize_for_dom(TALLOC_CTX *mem_ctx, + char **sanitized, + char **lc_sanitized); + ++/* Sanitize an input string (e.g. a DN) for use in ++ * an LDAP/LDB filter ++ * ++ * It is basically the same as sss_filter_sanitize(_ex), ++ * just extra spaces inside DN around '=' and ',' are removed ++ * before sanitizing other characters . According the documentation ++ * spaces in DN are allowed and some ldap servers can return them ++ * in isMemberOf or member attributes. ++ * ++ * (dc = my example, dc = com => dc=my\20example,dc=com) ++ * ++ * Returns a newly-constructed string attached to mem_ctx ++ * It will fail only on an out of memory condition, where it ++ * will return ENOMEM. ++ * ++ */ ++errno_t sss_filter_sanitize_dn(TALLOC_CTX *mem_ctx, ++ const char *input, ++ char **sanitized); ++ + char * + sss_escape_ip_address(TALLOC_CTX *mem_ctx, int family, const char *addr); + +-- +2.21.3 + diff --git a/0041-UTIL-Use-sss_sanitize_dn-where-we-deal-with-DN.patch b/0041-UTIL-Use-sss_sanitize_dn-where-we-deal-with-DN.patch new file mode 100644 index 0000000..e5ae271 --- /dev/null +++ b/0041-UTIL-Use-sss_sanitize_dn-where-we-deal-with-DN.patch @@ -0,0 +1,121 @@ +From a7edcbca35800ff697c002168b0d566e9563eadf Mon Sep 17 00:00:00 2001 +From: Tomas Halman +Date: Fri, 31 Jul 2020 11:21:44 +0200 +Subject: [PATCH 41/41] UTIL: Use sss_sanitize_dn where we deal with DN + +Resolves: +https://github.com/SSSD/sssd/issues/5261 +(cherry picked from commit 2635e1538a1ef8c01a6587ef3f28ab3367e3459f) +--- + src/db/sysdb_ops.c | 2 +- + src/providers/ipa/ipa_deskprofile_rules.c | 2 +- + src/providers/ipa/ipa_hbac_rules.c | 2 +- + src/providers/ipa/ipa_netgroups.c | 2 +- + src/providers/ldap/sdap_async_groups.c | 2 +- + src/providers/ldap/sdap_async_groups_ad.c | 2 +- + src/providers/ldap/sdap_async_initgroups.c | 4 ++-- + 7 files changed, 8 insertions(+), 8 deletions(-) + +diff --git a/src/db/sysdb_ops.c b/src/db/sysdb_ops.c +index b51c821ae..d4ad69e39 100644 +--- a/src/db/sysdb_ops.c ++++ b/src/db/sysdb_ops.c +@@ -3494,7 +3494,7 @@ errno_t sysdb_search_by_orig_dn(TALLOC_CTX *mem_ctx, + return ENOMEM; + } + +- ret = sss_filter_sanitize(tmp_ctx, member_dn, &sanitized_dn); ++ ret = sss_filter_sanitize_dn(tmp_ctx, member_dn, &sanitized_dn); + if (ret != EOK) { + goto done; + } +diff --git a/src/providers/ipa/ipa_deskprofile_rules.c b/src/providers/ipa/ipa_deskprofile_rules.c +index 65994356e..cce6184db 100644 +--- a/src/providers/ipa/ipa_deskprofile_rules.c ++++ b/src/providers/ipa/ipa_deskprofile_rules.c +@@ -91,7 +91,7 @@ ipa_deskprofile_rule_info_send(TALLOC_CTX *mem_ctx, + goto immediate; + } + +- ret = sss_filter_sanitize(state, host_dn, &host_dn_clean); ++ ret = sss_filter_sanitize_dn(state, host_dn, &host_dn_clean); + if (ret != EOK) { + goto immediate; + } +diff --git a/src/providers/ipa/ipa_hbac_rules.c b/src/providers/ipa/ipa_hbac_rules.c +index 0634a277e..e2c97ae3d 100644 +--- a/src/providers/ipa/ipa_hbac_rules.c ++++ b/src/providers/ipa/ipa_hbac_rules.c +@@ -84,7 +84,7 @@ ipa_hbac_rule_info_send(TALLOC_CTX *mem_ctx, + goto immediate; + } + +- ret = sss_filter_sanitize(state, host_dn, &host_dn_clean); ++ ret = sss_filter_sanitize_dn(state, host_dn, &host_dn_clean); + if (ret != EOK) goto immediate; + + state->ev = ev; +diff --git a/src/providers/ipa/ipa_netgroups.c b/src/providers/ipa/ipa_netgroups.c +index 05ebac758..e14f48fb0 100644 +--- a/src/providers/ipa/ipa_netgroups.c ++++ b/src/providers/ipa/ipa_netgroups.c +@@ -376,7 +376,7 @@ static void ipa_get_netgroups_process(struct tevent_req *subreq) + continue; + } + +- ret = sss_filter_sanitize(state, orig_dn, &dn); ++ ret = sss_filter_sanitize_dn(state, orig_dn, &dn); + if (ret != EOK) { + goto done; + } +diff --git a/src/providers/ldap/sdap_async_groups.c b/src/providers/ldap/sdap_async_groups.c +index 09e15bc3d..abe2ed275 100644 +--- a/src/providers/ldap/sdap_async_groups.c ++++ b/src/providers/ldap/sdap_async_groups.c +@@ -52,7 +52,7 @@ static int sdap_find_entry_by_origDN(TALLOC_CTX *memctx, + return ENOMEM; + } + +- ret = sss_filter_sanitize(tmpctx, orig_dn, &sanitized_dn); ++ ret = sss_filter_sanitize_dn(tmpctx, orig_dn, &sanitized_dn); + if (ret != EOK) { + ret = ENOMEM; + goto done; +diff --git a/src/providers/ldap/sdap_async_groups_ad.c b/src/providers/ldap/sdap_async_groups_ad.c +index 3f842b26d..c954398bb 100644 +--- a/src/providers/ldap/sdap_async_groups_ad.c ++++ b/src/providers/ldap/sdap_async_groups_ad.c +@@ -91,7 +91,7 @@ sdap_get_ad_match_rule_members_send(TALLOC_CTX *mem_ctx, + } + + /* Sanitize it in case we have special characters in DN */ +- ret = sss_filter_sanitize(state, group_dn, &sanitized_group_dn); ++ ret = sss_filter_sanitize_dn(state, group_dn, &sanitized_group_dn); + if (ret != EOK) { + DEBUG(SSSDBG_MINOR_FAILURE, + "Could not sanitize group DN: %s\n", +diff --git a/src/providers/ldap/sdap_async_initgroups.c b/src/providers/ldap/sdap_async_initgroups.c +index 620782b6f..055bdaefc 100644 +--- a/src/providers/ldap/sdap_async_initgroups.c ++++ b/src/providers/ldap/sdap_async_initgroups.c +@@ -1647,7 +1647,7 @@ static struct tevent_req *sdap_initgr_rfc2307bis_send( + attr_filter, &state->attrs, NULL); + if (ret != EOK) goto done; + +- ret = sss_filter_sanitize(state, orig_dn, &clean_orig_dn); ++ ret = sss_filter_sanitize_dn(state, orig_dn, &clean_orig_dn); + if (ret != EOK) goto done; + + use_id_mapping = sdap_idmap_domain_has_algorithmic_mapping( +@@ -2429,7 +2429,7 @@ static errno_t rfc2307bis_nested_groups_step(struct tevent_req *req) + goto done; + } + +- ret = sss_filter_sanitize(tmp_ctx, state->orig_dn, &clean_orig_dn); ++ ret = sss_filter_sanitize_dn(tmp_ctx, state->orig_dn, &clean_orig_dn); + if (ret != EOK) { + goto done; + } +-- +2.21.3 + diff --git a/0042-UTIL-Use-sss_sanitize_dn-where-we-deal-with-DN-2.patch b/0042-UTIL-Use-sss_sanitize_dn-where-we-deal-with-DN-2.patch new file mode 100644 index 0000000..b410846 --- /dev/null +++ b/0042-UTIL-Use-sss_sanitize_dn-where-we-deal-with-DN-2.patch @@ -0,0 +1,63 @@ +From 63bc622a1c6558f7dd51645031f95d0890aeec7c Mon Sep 17 00:00:00 2001 +From: Tomas Halman +Date: Wed, 19 Aug 2020 15:17:44 +0200 +Subject: [PATCH] UTIL: Use sss_sanitize_dn where we deal with DN 2 + +Tests show that also ldb_dn_get_linearized can +return DN with extra spaces. We have to trim that too. + +Resolves: +https://github.com/SSSD/sssd/issues/5261 +(cherry picked from commit 12bbd26e6c551d59793ba9a02a1d7cae4062f189) +--- + src/ldb_modules/memberof.c | 6 +++--- + src/providers/ldap/ldap_id_cleanup.c | 2 +- + 2 files changed, 4 insertions(+), 4 deletions(-) + +diff --git a/src/ldb_modules/memberof.c b/src/ldb_modules/memberof.c +index dae51938b..5de3b7c3b 100644 +--- a/src/ldb_modules/memberof.c ++++ b/src/ldb_modules/memberof.c +@@ -1364,7 +1364,7 @@ static int memberof_del(struct ldb_module *module, struct ldb_request *req) + return LDB_ERR_OPERATIONS_ERROR; + } + +- sret = sss_filter_sanitize(del_ctx, dn, &clean_dn); ++ sret = sss_filter_sanitize_dn(del_ctx, dn, &clean_dn); + if (sret != 0) { + talloc_free(ctx); + return LDB_ERR_OPERATIONS_ERROR; +@@ -1781,7 +1781,7 @@ static int mbof_del_execute_op(struct mbof_del_operation *delop) + return LDB_ERR_OPERATIONS_ERROR; + } + +- ret = sss_filter_sanitize(del_ctx, dn, &clean_dn); ++ ret = sss_filter_sanitize_dn(del_ctx, dn, &clean_dn); + if (ret != 0) { + return LDB_ERR_OPERATIONS_ERROR; + } +@@ -3054,7 +3054,7 @@ static int mbof_get_ghost_from_parent(struct mbof_mod_del_op *igh) + return LDB_ERR_OPERATIONS_ERROR; + } + +- ret = sss_filter_sanitize(igh, dn, &clean_dn); ++ ret = sss_filter_sanitize_dn(igh, dn, &clean_dn); + if (ret != 0) { + return LDB_ERR_OPERATIONS_ERROR; + } +diff --git a/src/providers/ldap/ldap_id_cleanup.c b/src/providers/ldap/ldap_id_cleanup.c +index 8c0f0c18b..cd10126f4 100644 +--- a/src/providers/ldap/ldap_id_cleanup.c ++++ b/src/providers/ldap/ldap_id_cleanup.c +@@ -422,7 +422,7 @@ static int cleanup_groups(TALLOC_CTX *memctx, + } + + /* sanitize dn */ +- ret = sss_filter_sanitize(tmpctx, dn, &sanitized_dn); ++ ret = sss_filter_sanitize_dn(tmpctx, dn, &sanitized_dn); + if (ret != EOK) { + DEBUG(SSSDBG_MINOR_FAILURE, + "sss_filter_sanitize failed: %s:[%d]\n", +-- +2.21.3 + diff --git a/0043-ldap-use-member-DN-to-create-ghost-user-hash-table.patch b/0043-ldap-use-member-DN-to-create-ghost-user-hash-table.patch new file mode 100644 index 0000000..7dad92a --- /dev/null +++ b/0043-ldap-use-member-DN-to-create-ghost-user-hash-table.patch @@ -0,0 +1,123 @@ +From 4d6be3c36169c954c4d61399607fde229902cb07 Mon Sep 17 00:00:00 2001 +From: Sumit Bose +Date: Wed, 26 Aug 2020 15:40:53 +0200 +Subject: [PATCH] ldap: use member DN to create ghost user hash table + +--- + src/db/sysdb.h | 1 + + src/providers/ldap/sdap.c | 10 ++++++++++ + src/providers/ldap/sdap_async_groups.c | 17 +++++++++++++++- + src/providers/ldap/sdap_async_nested_groups.c | 20 +++++++++++++++++-- + 4 files changed, 45 insertions(+), 3 deletions(-) + +diff --git a/src/db/sysdb.h b/src/db/sysdb.h +index a2bc8ed3b..679763bad 100644 +--- a/src/db/sysdb.h ++++ b/src/db/sysdb.h +@@ -129,6 +129,7 @@ + #define SYSDB_UPN "userPrincipalName" + #define SYSDB_CANONICAL_UPN "canonicalUserPrincipalName" + #define SYSDB_CCACHE_FILE "ccacheFile" ++#define SYSDB_DN_FOR_MEMBER_HASH_TABLE "dnForMemberHashTable" + + #define SYSDB_ORIG_DN "originalDN" + #define SYSDB_ORIG_MODSTAMP "originalModifyTimestamp" +diff --git a/src/providers/ldap/sdap.c b/src/providers/ldap/sdap.c +index a9c8b92b8..a1a00df56 100644 +--- a/src/providers/ldap/sdap.c ++++ b/src/providers/ldap/sdap.c +@@ -771,6 +771,16 @@ errno_t sdap_parse_deref(TALLOC_CTX *mem_ctx, + goto done; + } + ++ /* The dereference control seems to return the DN from the dereference ++ * attribute (e.g. member) so we can use it as key for the hash table ++ * later. */ ++ ret = sysdb_attrs_add_string(res[mi]->attrs, ++ SYSDB_DN_FOR_MEMBER_HASH_TABLE, orig_dn); ++ if (ret) { ++ DEBUG(SSSDBG_OP_FAILURE, "sysdb_attrs_add_string failed.\n"); ++ goto done; ++ } ++ + for (dval = dref->attrVals; dval != NULL; dval = dval->next) { + DEBUG(SSSDBG_TRACE_INTERNAL, + "Dereferenced attribute: %s\n", dval->type); +diff --git a/src/providers/ldap/sdap_async_groups.c b/src/providers/ldap/sdap_async_groups.c +index abe2ed275..4e3c524a4 100644 +--- a/src/providers/ldap/sdap_async_groups.c ++++ b/src/providers/ldap/sdap_async_groups.c +@@ -2509,6 +2509,7 @@ static errno_t sdap_nested_group_populate_users(TALLOC_CTX *mem_ctx, + struct ldb_message_element *el; + const char *username; + const char *original_dn; ++ const char *hash_key_dn; + struct sss_domain_info *user_dom; + struct sdap_domain *sdap_dom; + +@@ -2607,8 +2608,22 @@ static errno_t sdap_nested_group_populate_users(TALLOC_CTX *mem_ctx, + SYSDB_MOD_REP); + if (ret != EOK) goto done; + } else { ++ /* The DN of the user object and the DN in the member attribute ++ * might differ, e.g. in case. Since we later search the hash with ++ * DNs from the member attribute we should try to use DN from the ++ * member attribute here as well. This should be added earlier in ++ * the SYSDB_DN_FOR_MEMBER_HASH_TABLE attribute. If this does not ++ * exists we fall-back to original_dn which should work in the ++ * most cases as well. */ ++ ret = sysdb_attrs_get_string(users[i], ++ SYSDB_DN_FOR_MEMBER_HASH_TABLE, ++ &hash_key_dn); ++ if (ret != EOK) { ++ hash_key_dn = original_dn; ++ } ++ + key.type = HASH_KEY_STRING; +- key.str = talloc_steal(ghosts, discard_const(original_dn)); ++ key.str = talloc_steal(ghosts, discard_const(hash_key_dn)); + value.type = HASH_VALUE_PTR; + /* Already qualified from sdap_get_user_primary_name() */ + value.ptr = talloc_steal(ghosts, discard_const(username)); +diff --git a/src/providers/ldap/sdap_async_nested_groups.c b/src/providers/ldap/sdap_async_nested_groups.c +index 055de29ca..635b46403 100644 +--- a/src/providers/ldap/sdap_async_nested_groups.c ++++ b/src/providers/ldap/sdap_async_nested_groups.c +@@ -241,9 +241,12 @@ static errno_t sdap_nested_group_hash_entry(hash_table_t *table, + const char *name = NULL; + errno_t ret; + +- ret = sysdb_attrs_get_string(entry, SYSDB_ORIG_DN, &name); ++ ret = sysdb_attrs_get_string(entry, SYSDB_DN_FOR_MEMBER_HASH_TABLE, &name); + if (ret != EOK) { +- return ret; ++ ret = sysdb_attrs_get_string(entry, SYSDB_ORIG_DN, &name); ++ if (ret != EOK) { ++ return ret; ++ } + } + + return sdap_nested_group_hash_insert(table, name, entry, false, table_name); +@@ -1495,6 +1498,19 @@ sdap_nested_group_single_step_process(struct tevent_req *subreq) + } + } + ++ /* The original DN of the user object itself might differ from the one ++ * used inthe member attribute, e.g. different case. To make sure if ++ * can be found in a hash table when iterating over group members the ++ * DN from the member attribute used for the search as saved as well. ++ */ ++ ret = sysdb_attrs_add_string(entry, ++ SYSDB_DN_FOR_MEMBER_HASH_TABLE, ++ state->current_member->dn); ++ if (ret != EOK) { ++ DEBUG(SSSDBG_OP_FAILURE, "sysdb_attrs_add_string failed.\n"); ++ goto done; ++ } ++ + /* save user in hash table */ + ret = sdap_nested_group_hash_user(state->group_ctx, entry); + if (ret == EEXIST) { +-- +2.21.3 + diff --git a/0044-SYSDB-merge_res_sysdb_attrs-fixed-to-avoid-NULL-ptr-.patch b/0044-SYSDB-merge_res_sysdb_attrs-fixed-to-avoid-NULL-ptr-.patch new file mode 100644 index 0000000..3b4dcbc --- /dev/null +++ b/0044-SYSDB-merge_res_sysdb_attrs-fixed-to-avoid-NULL-ptr-.patch @@ -0,0 +1,65 @@ +From 9ace3a7899e6b3753ef428088303e0a646db4096 Mon Sep 17 00:00:00 2001 +From: Alexey Tikhonov +Date: Sun, 22 Nov 2020 17:44:07 +0100 +Subject: [PATCH] SYSDB: merge_res_sysdb_attrs() fixed to avoid NULL ptr in + msgs[] +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +This helps to avoid sssd_be segfaults at be_refresh_get_values_ex() due to NULL +ptrs in results of sysdb_search_with_ts_attr() + +Resolves: https://github.com/SSSD/sssd/issues/5412 + +Reviewed-by: Pavel Březina +(cherry picked from commit ff24d1538af88f83d0a3cc2817952cf70e7ca580) +--- + src/db/sysdb_search.c | 11 +++++++---- + 1 file changed, 7 insertions(+), 4 deletions(-) + +diff --git a/src/db/sysdb_search.c b/src/db/sysdb_search.c +index 6c89b4c98..7939944ba 100644 +--- a/src/db/sysdb_search.c ++++ b/src/db/sysdb_search.c +@@ -221,6 +221,7 @@ static errno_t merge_res_sysdb_attrs(TALLOC_CTX *mem_ctx, + const char *attrs[]) + { + errno_t ret; ++ size_t ts_cache_res_count = 0; + struct ldb_result *ts_cache_res = NULL; + + if (ts_res == NULL || ctx->ldb_ts == NULL) { +@@ -231,7 +232,6 @@ static errno_t merge_res_sysdb_attrs(TALLOC_CTX *mem_ctx, + if (ts_cache_res == NULL) { + return ENOMEM; + } +- ts_cache_res->count = ts_res->count; + ts_cache_res->msgs = talloc_zero_array(ts_cache_res, + struct ldb_message *, + ts_res->count); +@@ -244,15 +244,18 @@ static errno_t merge_res_sysdb_attrs(TALLOC_CTX *mem_ctx, + ret = merge_msg_sysdb_attrs(ts_cache_res->msgs, + ctx, + ts_res->msgs[c], +- &ts_cache_res->msgs[c], attrs); +- if (ret != EOK) { ++ &ts_cache_res->msgs[ts_cache_res_count], ++ attrs); ++ if ((ret != EOK) || (ts_cache_res->msgs[ts_cache_res_count] == NULL)) { + DEBUG(SSSDBG_MINOR_FAILURE, + "Cannot merge sysdb cache values for %s\n", + ldb_dn_get_linearized(ts_res->msgs[c]->dn)); +- /* non-fatal, we just get only the non-timestamp attrs */ ++ /* non-fatal, just skip */ + continue; + } ++ ts_cache_res_count += 1; + } ++ ts_cache_res->count = ts_cache_res_count; + + *_ts_cache_res = ts_cache_res; + return EOK; +-- +2.21.3 + diff --git a/0045-DEBUG-journal_send-was-made-static.patch b/0045-DEBUG-journal_send-was-made-static.patch new file mode 100644 index 0000000..5525677 --- /dev/null +++ b/0045-DEBUG-journal_send-was-made-static.patch @@ -0,0 +1,30 @@ +From 208372c7f6ad7d1d1d4b5cad57aaa08affaa0fac Mon Sep 17 00:00:00 2001 +From: Alexey Tikhonov +Date: Wed, 21 Oct 2020 18:47:32 +0200 +Subject: [PATCH 45/46] DEBUG: journal_send() was made static +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Reviewed-by: Tomáš Halman +(cherry picked from commit 833034f5332d2492d413a9c97fded1480b58bf14) +--- + src/util/debug.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/src/util/debug.c b/src/util/debug.c +index e2a76a414..e9633fb8c 100644 +--- a/src/util/debug.c ++++ b/src/util/debug.c +@@ -201,7 +201,7 @@ static void debug_printf(const char *format, ...) + } + + #ifdef WITH_JOURNALD +-errno_t journal_send(const char *file, ++static errno_t journal_send(const char *file, + long line, + const char *function, + int level, +-- +2.21.3 + diff --git a/0046-DEBUG-fixes-program-identifier-as-seen-in-syslog.patch b/0046-DEBUG-fixes-program-identifier-as-seen-in-syslog.patch new file mode 100644 index 0000000..2b684fe --- /dev/null +++ b/0046-DEBUG-fixes-program-identifier-as-seen-in-syslog.patch @@ -0,0 +1,72 @@ +From 647a130b501374c22b63937decdcf97da5c92505 Mon Sep 17 00:00:00 2001 +From: Alexey Tikhonov +Date: Wed, 21 Oct 2020 19:20:03 +0200 +Subject: [PATCH 46/46] DEBUG: fixes program identifier as seen in syslog +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Commit 225fe9950f2807d5fb226f6b3be1ff4cefd731f0 changed `debug_prg_name` +to accomodate needs of own SSSD logs, but this affected journal/syslog +as well. + +This patch amends situation: + - journal messages gets "umbrella" identifier "sssd[]" + - syslog uses default which is program name + +Resolves: https://github.com/SSSD/sssd/issues/5384 + +Reviewed-by: Tomáš Halman +(cherry picked from commit 18233532b72e62452eac6886652fa633ba055d8c) +--- + src/util/debug.c | 2 +- + src/util/sss_log.c | 12 +++--------- + 2 files changed, 4 insertions(+), 10 deletions(-) + +diff --git a/src/util/debug.c b/src/util/debug.c +index e9633fb8c..ee5fc12f9 100644 +--- a/src/util/debug.c ++++ b/src/util/debug.c +@@ -250,7 +250,7 @@ static errno_t journal_send(const char *file, + "MESSAGE=%s", message, + "PRIORITY=%i", LOG_DEBUG, + "SSSD_DOMAIN=%s", domain, +- "SSSD_PRG_NAME=%s", debug_prg_name, ++ "SSSD_PRG_NAME=sssd[%s]", debug_prg_name, + "SSSD_DEBUG_LEVEL=%x", level, + NULL); + ret = -res; +diff --git a/src/util/sss_log.c b/src/util/sss_log.c +index 48e73dbea..c6b7435c6 100644 +--- a/src/util/sss_log.c ++++ b/src/util/sss_log.c +@@ -107,7 +107,7 @@ static void sss_log_internal(int priority, int facility, const char *format, + "SSSD_DOMAIN=%s", domain, + "PRIORITY=%i", syslog_priority, + "SYSLOG_FACILITY=%i", LOG_FAC(facility), +- "SYSLOG_IDENTIFIER=%s", debug_prg_name, ++ "SYSLOG_IDENTIFIER=sssd[%s]", debug_prg_name, + NULL); + + free(message); +@@ -118,15 +118,9 @@ static void sss_log_internal(int priority, int facility, const char *format, + static void sss_log_internal(int priority, int facility, const char *format, + va_list ap) + { +- int syslog_priority; +- +- syslog_priority = sss_to_syslog(priority); +- +- openlog(debug_prg_name, 0, facility); +- +- vsyslog(syslog_priority, format, ap); ++ int syslog_priority = sss_to_syslog(priority); + +- closelog(); ++ vsyslog(facility|syslog_priority, format, ap); + } + + #endif /* WITH_JOURNALD */ +-- +2.21.3 + diff --git a/0047-ifp-fix-use-after-free.patch b/0047-ifp-fix-use-after-free.patch new file mode 100644 index 0000000..b36076f --- /dev/null +++ b/0047-ifp-fix-use-after-free.patch @@ -0,0 +1,37 @@ +From 316b8cde698bd4586a70fc5ed50a70dedc4b424f Mon Sep 17 00:00:00 2001 +From: Sumit Bose +Date: Tue, 3 Nov 2020 10:12:15 +0100 +Subject: [PATCH] ifp: fix use-after-free +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +The variable fqdn is pointing to some data from state->res->msgs[0]. But +before fqdn is used in the next search state->res and the memory +hierarchy below is freed. As a result the location where fqdn is pointing +to might hold the expected data or other data and the search will fail +intermittently. + +Resolves: https://github.com/SSSD/sssd/issues/5382 + +Reviewed-by: Pavel Březina +(cherry picked from commit 81e757b7b1d69893b5725f9c148c55d89c779e7b) +--- + src/responder/ifp/ifpsrv_cmd.c | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/src/responder/ifp/ifpsrv_cmd.c b/src/responder/ifp/ifpsrv_cmd.c +index ddb03f6b0..d83600681 100644 +--- a/src/responder/ifp/ifpsrv_cmd.c ++++ b/src/responder/ifp/ifpsrv_cmd.c +@@ -124,6 +124,7 @@ ifp_user_get_attr_unpack_msg(struct ifp_attr_req *attr_req) + if (attr_req->attrs == NULL) { + return ENOMEM; + } ++ fqdn = talloc_steal(state, fqdn); + + ai = 0; + for (i = 0; i < nattrs; i++) { +-- +2.21.3 + diff --git a/0048-fp-fix-original-fix-use-after-free-1.16-version.patch b/0048-fp-fix-original-fix-use-after-free-1.16-version.patch new file mode 100644 index 0000000..9057579 --- /dev/null +++ b/0048-fp-fix-original-fix-use-after-free-1.16-version.patch @@ -0,0 +1,44 @@ +From 1b523ba2a6bfba6974c9c909d6180590d16cc6bd Mon Sep 17 00:00:00 2001 +From: Sumit Bose +Date: Mon, 23 Nov 2020 13:40:30 +0100 +Subject: [PATCH] fp: fix original fix use-after-free - 1.16 version + +This should have been a cherry-pick of +3b158934cbb8f87cbfaf1650389b8dcd654b92ca but the cherry-pick of the +original patch was broken and placed the change into a different function. + +This patch fixes this by directly placing the change at the right place. + +Resolves: +https://github.com/SSSD/sssd/issues/5382 + +Reviewed-by: Alexey Tikhonov +--- + src/responder/ifp/ifpsrv_cmd.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/src/responder/ifp/ifpsrv_cmd.c b/src/responder/ifp/ifpsrv_cmd.c +index d83600681..620afe86a 100644 +--- a/src/responder/ifp/ifpsrv_cmd.c ++++ b/src/responder/ifp/ifpsrv_cmd.c +@@ -124,7 +124,6 @@ ifp_user_get_attr_unpack_msg(struct ifp_attr_req *attr_req) + if (attr_req->attrs == NULL) { + return ENOMEM; + } +- fqdn = talloc_steal(state, fqdn); + + ai = 0; + for (i = 0; i < nattrs; i++) { +@@ -576,7 +575,8 @@ static void ifp_user_get_attr_done(struct tevent_req *subreq) + } + + if (state->search_type == SSS_DP_USER) { +- /* throw away the result and perform attr search */ ++ /* throw away the result but keep the fqdn and perform attr search */ ++ fqdn = talloc_steal(state, fqdn); + talloc_zfree(state->res); + + ret = sysdb_get_user_attr_with_views(state, state->dom, fqdn, +-- +2.21.3 + diff --git a/0049-krb5-only-try-pkinit-with-Smartcard-credentials.patch b/0049-krb5-only-try-pkinit-with-Smartcard-credentials.patch new file mode 100644 index 0000000..e2209a1 --- /dev/null +++ b/0049-krb5-only-try-pkinit-with-Smartcard-credentials.patch @@ -0,0 +1,40 @@ +From 277cd1fa71222f3bdf4d8b39d0bce7d07d0df07b Mon Sep 17 00:00:00 2001 +From: Sumit Bose +Date: Mon, 24 Aug 2020 11:29:23 +0200 +Subject: [PATCH] krb5: only try pkinit with Smartcard credentials + +Currently pkinit is tried if a Smartcard is present. But depending on +the used PAM service and other configurations it might happen that the +user didn't provide the Smartcard PIN but e.g. the password. Hence, +before trying pkinit we should check if the right credentials are +available. + +Resolves: +https://github.com/SSSD/sssd/issues/5290 + +Reviewed-by: Alexey Tikhonov +(cherry picked from commit bca413267f58395e22415edc662a7ba89fbe7b30) +--- + src/providers/krb5/krb5_child.c | 6 +++++- + 1 file changed, 5 insertions(+), 1 deletion(-) + +diff --git a/src/providers/krb5/krb5_child.c b/src/providers/krb5/krb5_child.c +index 47eb181ba..d293d195d 100644 +--- a/src/providers/krb5/krb5_child.c ++++ b/src/providers/krb5/krb5_child.c +@@ -805,7 +805,11 @@ static krb5_error_code sss_krb5_responder(krb5_context ctx, + return kerr; + } + } else if (strcmp(question_list[c], +- KRB5_RESPONDER_QUESTION_PKINIT) == 0) { ++ KRB5_RESPONDER_QUESTION_PKINIT) == 0 ++ && (sss_authtok_get_type(kr->pd->authtok) ++ == SSS_AUTHTOK_TYPE_SC_PIN ++ || sss_authtok_get_type(kr->pd->authtok) ++ == SSS_AUTHTOK_TYPE_SC_KEYPAD)) { + return answer_pkinit(ctx, kr, rctx); + } + } +-- +2.21.3 + diff --git a/0050-negcache-make-sure-domain-config-does-not-leak-into-.patch b/0050-negcache-make-sure-domain-config-does-not-leak-into-.patch new file mode 100644 index 0000000..8f36171 --- /dev/null +++ b/0050-negcache-make-sure-domain-config-does-not-leak-into-.patch @@ -0,0 +1,37 @@ +From 96bdcbb4441ddf05c065bbafa88e5691300424d1 Mon Sep 17 00:00:00 2001 +From: Sumit Bose +Date: Thu, 8 Oct 2020 12:18:41 +0200 +Subject: [PATCH 50/53] negcache: make sure domain config does not leak into + global + +Resolves: https://github.com/SSSD/sssd/issues/5238 + +Reviewed-by: Alexey Tikhonov +(cherry picked from commit 0e1bcf77bd73baa0fea64830eb1f4f65a63c7afe) +--- + src/responder/common/negcache.c | 2 ++ + 1 file changed, 2 insertions(+) + +diff --git a/src/responder/common/negcache.c b/src/responder/common/negcache.c +index d9bf1417e..b9586f315 100644 +--- a/src/responder/common/negcache.c ++++ b/src/responder/common/negcache.c +@@ -1048,6 +1048,7 @@ errno_t sss_ncache_prepopulate(struct sss_nc_ctx *ncache, + } + } + ++ talloc_zfree(filter_list); + /* Populate non domain-specific negative cache user entries */ + ret = confdb_get_string_as_list(cdb, tmpctx, CONFDB_NSS_CONF_ENTRY, + CONFDB_NSS_FILTER_USERS, &filter_list); +@@ -1183,6 +1184,7 @@ errno_t sss_ncache_prepopulate(struct sss_nc_ctx *ncache, + } + } + ++ talloc_zfree(filter_list); + /* Populate non domain-specific negative cache group entries */ + ret = confdb_get_string_as_list(cdb, tmpctx, CONFDB_NSS_CONF_ENTRY, + CONFDB_NSS_FILTER_GROUPS, &filter_list); +-- +2.21.3 + diff --git a/0051-utils-add-SSS_GND_SUBDOMAINS-flag-for-get_next_domai.patch b/0051-utils-add-SSS_GND_SUBDOMAINS-flag-for-get_next_domai.patch new file mode 100644 index 0000000..d5a0f00 --- /dev/null +++ b/0051-utils-add-SSS_GND_SUBDOMAINS-flag-for-get_next_domai.patch @@ -0,0 +1,108 @@ +From ea32d0eb61336858aa23697b4e91d420481fc3e2 Mon Sep 17 00:00:00 2001 +From: Sumit Bose +Date: Thu, 8 Oct 2020 17:57:29 +0200 +Subject: [PATCH 51/53] utils: add SSS_GND_SUBDOMAINS flag for + get_next_domain() + +To allow to only iterate over a singel domain an its sub-domains a new +flag is added to get_next_domain(). + +Resolves: https://github.com/SSSD/sssd/issues/5238 + +Reviewed-by: Alexey Tikhonov +(cherry picked from commit 385af99ff4d5a75d0c1edc9ad830da3eb7478295) +--- + src/tests/cmocka/test_utils.c | 31 +++++++++++++++++++++++++++++++ + src/util/domain_info_utils.c | 10 +++++++--- + src/util/util.h | 4 ++++ + 3 files changed, 42 insertions(+), 3 deletions(-) + +diff --git a/src/tests/cmocka/test_utils.c b/src/tests/cmocka/test_utils.c +index aa245f00b..8c0ce83f3 100644 +--- a/src/tests/cmocka/test_utils.c ++++ b/src/tests/cmocka/test_utils.c +@@ -876,6 +876,37 @@ static void test_get_next_domain_flags(void **state) + + dom = get_next_domain(dom, gnd_flags); + assert_null(dom); ++ ++ /* Descend only to subdomains */ ++ gnd_flags = SSS_GND_SUBDOMAINS | SSS_GND_INCLUDE_DISABLED; ++ ++ dom = get_next_domain(test_ctx->dom_list, gnd_flags); ++ assert_non_null(dom); ++ assert_string_equal(dom->name, "sub1a"); ++ ++ dom = get_next_domain(dom, gnd_flags); ++ assert_null(dom); ++ ++ dom = find_domain_by_name_ex(test_ctx->dom_list, "dom2", true, ++ SSS_GND_ALL_DOMAINS); ++ assert_non_null(dom); ++ assert_string_equal(dom->name, "dom2"); ++ ++ dom = get_next_domain(dom, gnd_flags); ++ assert_non_null(dom); ++ assert_string_equal(dom->name, "sub2a"); ++ ++ dom = get_next_domain(dom, gnd_flags); ++ assert_non_null(dom); ++ assert_string_equal(dom->name, "sub2b"); ++ ++ dom = get_next_domain(dom, gnd_flags); ++ assert_null(dom); ++ ++ /* Expect NULL if the domain has no sub-domains */ ++ test_ctx->dom_list->subdomains = NULL; ++ dom = get_next_domain(test_ctx->dom_list, gnd_flags); ++ assert_null(dom); + } + + struct name_init_test_ctx { +diff --git a/src/util/domain_info_utils.c b/src/util/domain_info_utils.c +index c56a0611e..71dfcba02 100644 +--- a/src/util/domain_info_utils.c ++++ b/src/util/domain_info_utils.c +@@ -39,16 +39,20 @@ struct sss_domain_info *get_next_domain(struct sss_domain_info *domain, + uint32_t gnd_flags) + { + struct sss_domain_info *dom; +- bool descend = gnd_flags & SSS_GND_DESCEND; ++ bool descend = gnd_flags & (SSS_GND_DESCEND | SSS_GND_SUBDOMAINS); + bool include_disabled = gnd_flags & SSS_GND_INCLUDE_DISABLED; ++ bool only_subdomains = gnd_flags & SSS_GND_SUBDOMAINS; + + dom = domain; + while (dom) { + if (descend && dom->subdomains) { + dom = dom->subdomains; +- } else if (dom->next) { ++ } else if (dom->next && only_subdomains && IS_SUBDOMAIN(dom)) { + dom = dom->next; +- } else if (descend && IS_SUBDOMAIN(dom) && dom->parent->next) { ++ } else if (dom->next && !only_subdomains) { ++ dom = dom->next; ++ } else if (descend && !only_subdomains && IS_SUBDOMAIN(dom) ++ && dom->parent->next) { + dom = dom->parent->next; + } else { + dom = NULL; +diff --git a/src/util/util.h b/src/util/util.h +index d7d2017fa..486394448 100644 +--- a/src/util/util.h ++++ b/src/util/util.h +@@ -558,7 +558,11 @@ struct sss_domain_info *get_domains_head(struct sss_domain_info *domain); + + #define SSS_GND_DESCEND 0x01 + #define SSS_GND_INCLUDE_DISABLED 0x02 ++/* Descend to sub-domains of current domain but do not go to next parent */ ++#define SSS_GND_SUBDOMAINS 0x04 + #define SSS_GND_ALL_DOMAINS (SSS_GND_DESCEND | SSS_GND_INCLUDE_DISABLED) ++#define SSS_GND_ALL_SUBDOMAINS (SSS_GND_SUBDOMAINS | SSS_GND_INCLUDE_DISABLED) ++ + struct sss_domain_info *get_next_domain(struct sss_domain_info *domain, + uint32_t gnd_flags); + struct sss_domain_info *find_domain_by_name(struct sss_domain_info *domain, +-- +2.21.3 + diff --git a/0052-negcache-make-sure-short-names-are-added-to-sub-doma.patch b/0052-negcache-make-sure-short-names-are-added-to-sub-doma.patch new file mode 100644 index 0000000..70d3965 --- /dev/null +++ b/0052-negcache-make-sure-short-names-are-added-to-sub-doma.patch @@ -0,0 +1,445 @@ +From c3207deee7411456827e69d0b72d7d44e7458853 Mon Sep 17 00:00:00 2001 +From: Sumit Bose +Date: Fri, 9 Oct 2020 11:56:21 +0200 +Subject: [PATCH 52/53] negcache: make sure short names are added to + sub-domains + +If short names are used with filter_users or filter_groups in a +[domain/...] section they should be added to the sub-domains of this +domain as well. + +Resolves: https://github.com/SSSD/sssd/issues/5238 + +Reviewed-by: Alexey Tikhonov +(cherry picked from commit 0dc81a52e2836010974e9f71b1f3e47c20fd498d) +--- + src/responder/common/negcache.c | 105 +++++++------ + src/tests/cmocka/test_negcache.c | 254 +++++++++++++++++++++++++++++++ + 2 files changed, 312 insertions(+), 47 deletions(-) + +diff --git a/src/responder/common/negcache.c b/src/responder/common/negcache.c +index b9586f315..47fbb2106 100644 +--- a/src/responder/common/negcache.c ++++ b/src/responder/common/negcache.c +@@ -969,6 +969,7 @@ errno_t sss_ncache_prepopulate(struct sss_nc_ctx *ncache, + char *name = NULL; + struct sss_domain_info *dom = NULL; + struct sss_domain_info *domain_list = rctx->domains; ++ struct sss_domain_info *ddom; + char *domainname = NULL; + char *conf_path = NULL; + TALLOC_CTX *tmpctx = talloc_new(NULL); +@@ -1011,39 +1012,44 @@ errno_t sss_ncache_prepopulate(struct sss_nc_ctx *ncache, + continue; + } + +- if (domainname && strcmp(domainname, dom->name)) { +- DEBUG(SSSDBG_TRACE_FUNC, +- "Mismatch between domain name (%s) and name " +- "set in FQN (%s), assuming %s is UPN\n", +- dom->name, domainname, filter_list[i]); +- ret = sss_ncache_set_upn(ncache, true, dom, filter_list[i]); ++ /* Check domain and its sub-domains */ ++ for (ddom = dom; ddom != NULL; ++ ddom = get_next_domain(ddom, SSS_GND_ALL_SUBDOMAINS)) { ++ ++ if (domainname && strcmp(domainname, ddom->name)) { ++ DEBUG(SSSDBG_TRACE_FUNC, ++ "Mismatch between domain name (%s) and name " ++ "set in FQN (%s), assuming %s is UPN\n", ++ ddom->name, domainname, filter_list[i]); ++ ret = sss_ncache_set_upn(ncache, true, ddom, filter_list[i]); ++ if (ret != EOK) { ++ DEBUG(SSSDBG_OP_FAILURE, ++ "sss_ncache_set_upn failed (%d [%s]), ignored\n", ++ ret, sss_strerror(ret)); ++ } ++ continue; ++ } ++ ++ fqname = sss_create_internal_fqname(tmpctx, name, ddom->name); ++ if (fqname == NULL) { ++ continue; ++ } ++ ++ ret = sss_ncache_set_upn(ncache, true, ddom, fqname); + if (ret != EOK) { + DEBUG(SSSDBG_OP_FAILURE, + "sss_ncache_set_upn failed (%d [%s]), ignored\n", + ret, sss_strerror(ret)); + } +- continue; +- } +- +- fqname = sss_create_internal_fqname(tmpctx, name, dom->name); +- if (fqname == NULL) { +- continue; +- } +- +- ret = sss_ncache_set_upn(ncache, true, dom, fqname); +- if (ret != EOK) { +- DEBUG(SSSDBG_OP_FAILURE, +- "sss_ncache_set_upn failed (%d [%s]), ignored\n", +- ret, sss_strerror(ret)); +- } +- ret = sss_ncache_set_user(ncache, true, dom, fqname); +- talloc_zfree(fqname); +- if (ret != EOK) { +- DEBUG(SSSDBG_CRIT_FAILURE, +- "Failed to store permanent user filter for [%s]" +- " (%d [%s])\n", filter_list[i], +- ret, sss_strerror(ret)); +- continue; ++ ret = sss_ncache_set_user(ncache, true, ddom, fqname); ++ talloc_zfree(fqname); ++ if (ret != EOK) { ++ DEBUG(SSSDBG_CRIT_FAILURE, ++ "Failed to store permanent user filter for [%s]" ++ " (%d [%s])\n", filter_list[i], ++ ret, sss_strerror(ret)); ++ continue; ++ } + } + } + } +@@ -1159,27 +1165,32 @@ errno_t sss_ncache_prepopulate(struct sss_nc_ctx *ncache, + continue; + } + +- if (domainname && strcmp(domainname, dom->name)) { +- DEBUG(SSSDBG_CRIT_FAILURE, +- "Mismatch between domain name (%s) and name " +- "set in FQN (%s), skipping group %s\n", +- dom->name, domainname, name); +- continue; +- } ++ /* Check domain and its sub-domains */ ++ for (ddom = dom; ++ ddom != NULL && (ddom == dom || ddom->parent != NULL); ++ ddom = get_next_domain(ddom, SSS_GND_ALL_DOMAINS)) { ++ if (domainname && strcmp(domainname, ddom->name)) { ++ DEBUG(SSSDBG_CRIT_FAILURE, ++ "Mismatch between domain name (%s) and name " ++ "set in FQN (%s), skipping group %s\n", ++ ddom->name, domainname, name); ++ continue; ++ } + +- fqname = sss_create_internal_fqname(tmpctx, name, dom->name); +- if (fqname == NULL) { +- continue; +- } ++ fqname = sss_create_internal_fqname(tmpctx, name, ddom->name); ++ if (fqname == NULL) { ++ continue; ++ } + +- ret = sss_ncache_set_group(ncache, true, dom, fqname); +- talloc_zfree(fqname); +- if (ret != EOK) { +- DEBUG(SSSDBG_CRIT_FAILURE, +- "Failed to store permanent group filter for [%s]" +- " (%d [%s])\n", filter_list[i], +- ret, strerror(ret)); +- continue; ++ ret = sss_ncache_set_group(ncache, true, ddom, fqname); ++ talloc_zfree(fqname); ++ if (ret != EOK) { ++ DEBUG(SSSDBG_CRIT_FAILURE, ++ "Failed to store permanent group filter for [%s]" ++ " (%d [%s])\n", filter_list[i], ++ ret, strerror(ret)); ++ continue; ++ } + } + } + } +diff --git a/src/tests/cmocka/test_negcache.c b/src/tests/cmocka/test_negcache.c +index 0876cfdaf..7a8827685 100644 +--- a/src/tests/cmocka/test_negcache.c ++++ b/src/tests/cmocka/test_negcache.c +@@ -102,6 +102,8 @@ static int setup(void **state) + int ret; + struct test_state *ts; + ++ test_dom_suite_setup(TESTS_PATH); ++ + ts = talloc(NULL, struct test_state); + assert_non_null(ts); + +@@ -116,6 +118,7 @@ static int setup(void **state) + static int teardown(void **state) + { + struct test_state *ts = talloc_get_type_abort(*state, struct test_state); ++ test_dom_suite_cleanup(TESTS_PATH, TEST_CONF_DB, TEST_DOM_NAME); + talloc_free(ts); + return 0; + } +@@ -904,6 +907,255 @@ static void test_sss_ncache_reset_prepopulate(void **state) + assert_int_equal(ret, EEXIST); + } + ++/* The main purpose of test_sss_ncache_short_name_in_domain is to test that ++ * short names in the filter_users or filter_groups options in a [domain/...] ++ * section are properly added to the related sub-domains as well (if there are ++ * any) and not added to domains from other [domain/...] sections. For ++ * completeness entries with fully-qualified names of the parent and the ++ * sub-domain and the generic UPN are added as well. ++ * ++ * The result should of course be independent of the present domains. To ++ * verify this the domains are added one after the other and the negative ++ * cache is repopulated each time. ++ * ++ * With the given domains, users and group we have to following expectations: ++ * - the short name entry will be added to the domain and all sub-domains as ++ * name and as upn by expanding it to a fully-qualified name with the ++ * domain name or sub-domain name respectively ++ * - the fully-qualified name from the parent domain is added as name and upn ++ * to the parent domain and as upn to all sub-domains ++ * - the fully-qualified name from the sub-domain is added as name to the ++ * sub-domain and as upn to the parent and all sub-domains ++ * - the generic upn is nowhere added as name and as upn to the parent and all ++ * sub-domains ++ * - none of the names is added to a different parent domain ++ * ++ * The following table should illustrated the expectations: ++ * ++ * user (name): ++ * | shortuser | parentu@TEST_DOM_NAME | subdomu@subTEST_DOM_NAME | upn@upn.dom ++ *-----------------+-----------+-----------------------+--------------------------+------------ ++ * TEST_DOM_NAME | PRESENT | PRESENT | MISSING | MISSING ++ * subTEST_DOM_NAME| PRESENT | MISSING | PRESENT | MISSING ++ * TEST_DOM_NAME2 | MISSING | MISSING | MISSING | MISSING ++ * ++ * user (upn): ++ * | shortuser | parentu@TEST_DOM_NAME | subdomu@subTEST_DOM_NAME | upn@upn.dom ++ *-----------------+-----------+-----------------------+--------------------------+------------ ++ * TEST_DOM_NAME | PRESENT | PRESENT | PRESENT | PRESENT ++ * subTEST_DOM_NAME| PRESENT | PRESENT | PRESENT | PRESENT ++ * TEST_DOM_NAME2 | MISSING | MISSING | MISSING | MISSING ++ * ++ * ++ * ++ * groups: ++ * | shortgroup | parentg@TEST_DOM_NAME | subdomg@subTEST_DOM_NAME ++ *-----------------+------------+-----------------------+------------------------- ++ * TEST_DOM_NAME | PRESENT | PRESENT | MISSING ++ * subTEST_DOM_NAME| PRESENT | MISSING | PRESENT ++ * TEST_DOM_NAME2 | MISSING | MISSING | MISSING ++ * ++ * ++ * The following expect_*() implement checks for the expextations: ++ */ ++ ++static void expect_in_parent(struct sss_nc_ctx *ncache, ++ struct sss_domain_info *dom) ++{ ++ int ret; ++ ++ ret = check_user_in_ncache(ncache, dom, "shortuser"); ++ assert_int_equal(ret, EEXIST); ++ ret = sss_ncache_check_upn(ncache, dom, "shortuser@"TEST_DOM_NAME); ++ assert_int_equal(ret, EEXIST); ++ ++ ret = check_user_in_ncache(ncache, dom, "parentu"); ++ assert_int_equal(ret, EEXIST); ++ ret = sss_ncache_check_upn(ncache, dom, "parentu@"TEST_DOM_NAME); ++ assert_int_equal(ret, EEXIST); ++ ++ ret = check_user_in_ncache(ncache, dom, "subdomu"); ++ assert_int_equal(ret, ENOENT); ++ ret = sss_ncache_check_upn(ncache, dom, "subdomu@sub"TEST_DOM_NAME); ++ assert_int_equal(ret, EEXIST); ++ ++ ret = check_user_in_ncache(ncache, dom, "upn"); ++ assert_int_equal(ret, ENOENT); ++ ret = sss_ncache_check_upn(ncache, dom, "upn@upn.dom"); ++ assert_int_equal(ret, EEXIST); ++ ++ ret = check_group_in_ncache(ncache, dom, "shortgroup"); ++ assert_int_equal(ret, EEXIST); ++ ++ ret = check_group_in_ncache(ncache, dom, "parentg"); ++ assert_int_equal(ret, EEXIST); ++ ++ ret = check_group_in_ncache(ncache, dom, "subdomg"); ++ assert_int_equal(ret, ENOENT); ++} ++ ++static void expect_in_subdomain(struct sss_nc_ctx *ncache, ++ struct sss_domain_info *sub_dom) ++{ ++ int ret; ++ ++ ret = check_user_in_ncache(ncache, sub_dom, "shortuser"); ++ assert_int_equal(ret, EEXIST); ++ ret = sss_ncache_check_upn(ncache, sub_dom, "shortuser@sub"TEST_DOM_NAME); ++ assert_int_equal(ret, EEXIST); ++ ++ ret = check_user_in_ncache(ncache, sub_dom, "subdomu"); ++ assert_int_equal(ret, EEXIST); ++ ret = sss_ncache_check_upn(ncache, sub_dom, "subdomu@sub"TEST_DOM_NAME); ++ assert_int_equal(ret, EEXIST); ++ ++ ret = check_user_in_ncache(ncache, sub_dom, "upn"); ++ assert_int_equal(ret, ENOENT); ++ ret = sss_ncache_check_upn(ncache, sub_dom, "upn@upn.dom"); ++ assert_int_equal(ret, EEXIST); ++ ++ ret = check_user_in_ncache(ncache, sub_dom, "parentu"); ++ assert_int_equal(ret, ENOENT); ++ ret = sss_ncache_check_upn(ncache, sub_dom, "parentu@"TEST_DOM_NAME); ++ assert_int_equal(ret, EEXIST); ++ ++ ++ ret = check_group_in_ncache(ncache, sub_dom, "shortgroup"); ++ assert_int_equal(ret, EEXIST); ++ ++ ret = check_group_in_ncache(ncache, sub_dom, "parentg"); ++ assert_int_equal(ret, ENOENT); ++ ++ ret = check_group_in_ncache(ncache, sub_dom, "subdomg"); ++ assert_int_equal(ret, EEXIST); ++} ++static void expect_no_entries_in_dom(struct sss_nc_ctx *ncache, ++ struct sss_domain_info *dom2) ++{ ++ int ret; ++ ++ ret = check_user_in_ncache(ncache, dom2, "shortuser"); ++ assert_int_equal(ret, ENOENT); ++ ret = sss_ncache_check_upn(ncache, dom2, "shortuser"TEST_DOM_NAME); ++ assert_int_equal(ret, ENOENT); ++ ++ ret = check_user_in_ncache(ncache, dom2, "parentu"); ++ assert_int_equal(ret, ENOENT); ++ ret = sss_ncache_check_upn(ncache, dom2, "parentu@"TEST_DOM_NAME); ++ assert_int_equal(ret, ENOENT); ++ ++ ret = check_user_in_ncache(ncache, dom2, "subdomu"); ++ assert_int_equal(ret, ENOENT); ++ ret = sss_ncache_check_upn(ncache, dom2, "subdomu@sub"TEST_DOM_NAME); ++ assert_int_equal(ret, ENOENT); ++ ++ ret = check_user_in_ncache(ncache, dom2, "upn"); ++ assert_int_equal(ret, ENOENT); ++ ret = sss_ncache_check_upn(ncache, dom2, "upn@upn.dom"); ++ assert_int_equal(ret, ENOENT); ++ ++ ret = check_group_in_ncache(ncache, dom2, "shortgroup"); ++ assert_int_equal(ret, ENOENT); ++ ++ ret = check_group_in_ncache(ncache, dom2, "parentg"); ++ assert_int_equal(ret, ENOENT); ++ ++ ret = check_group_in_ncache(ncache, dom2, "subdomg"); ++ assert_int_equal(ret, ENOENT); ++} ++ ++static void test_sss_ncache_short_name_in_domain(void **state) ++{ ++ int ret; ++ struct test_state *ts; ++ struct tevent_context *ev; ++ struct sss_nc_ctx *ncache; ++ struct sss_test_ctx *tc; ++ struct sss_domain_info *dom; ++ struct sss_domain_info *dom2; ++ struct sss_domain_info *sub_dom; ++ ++ struct sss_test_conf_param params[] = { ++ { "filter_users", "shortuser, parentu@"TEST_DOM_NAME", " ++ "subdomu@sub"TEST_DOM_NAME", upn@upn.dom" }, ++ { "filter_groups", "shortgroup, parentg@"TEST_DOM_NAME", " ++ "subdomg@sub"TEST_DOM_NAME }, ++ { NULL, NULL }, ++ }; ++ ++ const char *nss_filter_users[] = { params[0].value, NULL}; ++ const char *nss_filter_groups[] = { params[1].value, NULL}; ++ ++ ts = talloc_get_type_abort(*state, struct test_state); ++ ++ ev = tevent_context_init(ts); ++ assert_non_null(ev); ++ ++ dom = talloc_zero(ts, struct sss_domain_info); ++ assert_non_null(dom); ++ dom->name = discard_const_p(char, TEST_DOM_NAME); ++ sss_domain_set_state(dom, DOM_ACTIVE); ++ ++ ts->nctx = mock_nctx(ts); ++ assert_non_null(ts->nctx); ++ ++ tc = create_dom_test_ctx(ts, TESTS_PATH, TEST_CONF_DB, ++ TEST_DOM_NAME, TEST_ID_PROVIDER, params); ++ assert_non_null(tc); ++ ++ ret = confdb_add_param(tc->confdb, true, "config/domain/"TEST_DOM_NAME, ++ "filter_users", nss_filter_users); ++ assert_int_equal(ret, EOK); ++ ++ ret = confdb_add_param(tc->confdb, true, "config/domain"TEST_DOM_NAME, ++ "filter_groups", nss_filter_groups); ++ assert_int_equal(ret, EOK); ++ ++ ncache = ts->ctx; ++ ts->rctx = mock_rctx(ts, ev, dom, ts->nctx); ++ assert_non_null(ts->rctx); ++ ts->rctx->cdb = tc->confdb; ++ ++ ret = sss_names_init(ts, tc->confdb, TEST_DOM_NAME, &dom->names); ++ assert_int_equal(ret, EOK); ++ ++ ret = sss_ncache_reset_repopulate_permanent(ts->rctx, ncache); ++ assert_int_equal(ret, EOK); ++ ++ /* Add another domain */ ++ dom2 = talloc_zero(ts, struct sss_domain_info); ++ assert_non_null(dom2); ++ dom2->name = discard_const_p(char, TEST_DOM_NAME"2"); ++ sss_domain_set_state(dom2, DOM_ACTIVE); ++ dom->next = dom2; ++ dom2->names = dom->names; ++ ++ expect_in_parent(ncache, dom); ++ expect_no_entries_in_dom(ncache, dom2); ++ ++ ret = sss_ncache_reset_repopulate_permanent(ts->rctx, ncache); ++ assert_int_equal(ret, EOK); ++ ++ expect_in_parent(ncache, dom); ++ expect_no_entries_in_dom(ncache, dom2); ++ ++ /* Add a sub domain */ ++ sub_dom = talloc_zero(ts, struct sss_domain_info); ++ assert_non_null(sub_dom); ++ sub_dom->name = discard_const_p(char, "sub"TEST_DOM_NAME); ++ sss_domain_set_state(sub_dom, DOM_ACTIVE); ++ sub_dom->parent = dom; ++ dom->subdomains = sub_dom; ++ sub_dom->names = dom->names; ++ ++ ret = sss_ncache_reset_repopulate_permanent(ts->rctx, ncache); ++ assert_int_equal(ret, EOK); ++ ++ expect_in_parent(ncache, dom); ++ expect_in_subdomain(ncache, sub_dom); ++ expect_no_entries_in_dom(ncache, dom2); ++} ++ + static void test_sss_ncache_reset(void **state) + { + errno_t ret; +@@ -1066,6 +1318,8 @@ int main(void) + setup, teardown), + cmocka_unit_test_setup_teardown(test_sss_ncache_reset_prepopulate, + setup, teardown), ++ cmocka_unit_test_setup_teardown(test_sss_ncache_short_name_in_domain, ++ setup, teardown), + cmocka_unit_test_setup_teardown(test_sss_ncache_reset, + setup, teardown), + cmocka_unit_test_setup_teardown(test_sss_ncache_locate_uid_gid, +-- +2.21.3 + diff --git a/0053-negcache-do-not-use-default_domain_suffix.patch b/0053-negcache-do-not-use-default_domain_suffix.patch new file mode 100644 index 0000000..eb0bcec --- /dev/null +++ b/0053-negcache-do-not-use-default_domain_suffix.patch @@ -0,0 +1,155 @@ +From 56d509ad3001101f04c4af050c3da7472032e4cb Mon Sep 17 00:00:00 2001 +From: Sumit Bose +Date: Fri, 9 Oct 2020 15:26:39 +0200 +Subject: [PATCH 53/53] negcache: do not use default_domain_suffix + +When splitting the names from the filter_users and filter_groups options +do not use the default_domain_suffix because it will hide that the +original name is a short name and should be added everywhere. + +Additionally this patch fixes a typo where sss_parse_name() was used +instead of sss_parse_name_for_domains(). + +Resolves: https://github.com/SSSD/sssd/issues/5238 + +Reviewed-by: Alexey Tikhonov +(cherry picked from commit fa4b46e7de7297da3c0e37913eab8cba7f103629) +--- + src/responder/common/negcache.c | 29 +++++++++++++++-------------- + src/tests/cmocka/test_negcache.c | 22 ++++++++++++++++++++-- + 2 files changed, 35 insertions(+), 16 deletions(-) + +diff --git a/src/responder/common/negcache.c b/src/responder/common/negcache.c +index 47fbb2106..ff05aaea5 100644 +--- a/src/responder/common/negcache.c ++++ b/src/responder/common/negcache.c +@@ -998,13 +998,13 @@ errno_t sss_ncache_prepopulate(struct sss_nc_ctx *ncache, + + for (i = 0; (filter_list && filter_list[i]); i++) { + ret = sss_parse_name_for_domains(tmpctx, domain_list, +- rctx->default_domain, ++ NULL, + filter_list[i], + &domainname, &name); + if (ret == EAGAIN) { + DEBUG(SSSDBG_MINOR_FAILURE, +- "cannot add [%s] to negcache because the required or " +- "default domain are not known yet\n", filter_list[i]); ++ "Can add [%s] only as UPN to negcache because the " ++ "required domain is not known yet\n", filter_list[i]); + } else if (ret != EOK) { + DEBUG(SSSDBG_CRIT_FAILURE, + "Invalid name in filterUsers list: [%s] (%d)\n", +@@ -1064,12 +1064,12 @@ errno_t sss_ncache_prepopulate(struct sss_nc_ctx *ncache, + + for (i = 0; (filter_list && filter_list[i]); i++) { + ret = sss_parse_name_for_domains(tmpctx, domain_list, +- rctx->default_domain, filter_list[i], ++ NULL, filter_list[i], + &domainname, &name); + if (ret == EAGAIN) { + DEBUG(SSSDBG_MINOR_FAILURE, +- "Cannot add [%s] to negcache because the required or " +- "default domain are not known yet\n", filter_list[i]); ++ "Can add [%s] only as UPN to negcache because the " ++ "required domain is not known yet\n", filter_list[i]); + } else if (ret != EOK) { + DEBUG(SSSDBG_CRIT_FAILURE, + "Invalid name in filterUsers list: [%s] (%d)\n", +@@ -1156,9 +1156,12 @@ errno_t sss_ncache_prepopulate(struct sss_nc_ctx *ncache, + if (ret != EOK) goto done; + + for (i = 0; (filter_list && filter_list[i]); i++) { +- ret = sss_parse_name(tmpctx, dom->names, filter_list[i], +- &domainname, &name); ++ ret = sss_parse_name_for_domains(tmpctx, domain_list, ++ NULL, filter_list[i], ++ &domainname, &name); + if (ret != EOK) { ++ /* Groups do not have UPNs, so domain names, if present, ++ * must be known */ + DEBUG(SSSDBG_CRIT_FAILURE, + "Invalid name in filterGroups list: [%s] (%d)\n", + filter_list[i], ret); +@@ -1205,13 +1208,11 @@ errno_t sss_ncache_prepopulate(struct sss_nc_ctx *ncache, + + for (i = 0; (filter_list && filter_list[i]); i++) { + ret = sss_parse_name_for_domains(tmpctx, domain_list, +- rctx->default_domain, filter_list[i], ++ NULL, filter_list[i], + &domainname, &name); +- if (ret == EAGAIN) { +- DEBUG(SSSDBG_MINOR_FAILURE, +- "Cannot add [%s] to negcache because the required or " +- "default domain are not known yet\n", filter_list[i]); +- } else if (ret != EOK) { ++ if (ret != EOK) { ++ /* Groups do not have UPNs, so domain names, if present, ++ * must be known */ + DEBUG(SSSDBG_CRIT_FAILURE, + "Invalid name in filterGroups list: [%s] (%d)\n", + filter_list[i], ret); +diff --git a/src/tests/cmocka/test_negcache.c b/src/tests/cmocka/test_negcache.c +index 7a8827685..f8cbbc1fe 100644 +--- a/src/tests/cmocka/test_negcache.c ++++ b/src/tests/cmocka/test_negcache.c +@@ -916,7 +916,9 @@ static void test_sss_ncache_reset_prepopulate(void **state) + * + * The result should of course be independent of the present domains. To + * verify this the domains are added one after the other and the negative +- * cache is repopulated each time. ++ * cache is repopulated each time. The result should be also independent of ++ * the setting of default_domain_suffix option which is tested by ++ * test_sss_ncache_short_name_in_domain_with_prefix. + * + * With the given domains, users and group we have to following expectations: + * - the short name entry will be added to the domain and all sub-domains as +@@ -1064,7 +1066,8 @@ static void expect_no_entries_in_dom(struct sss_nc_ctx *ncache, + assert_int_equal(ret, ENOENT); + } + +-static void test_sss_ncache_short_name_in_domain(void **state) ++static void run_sss_ncache_short_name_in_domain(void **state, ++ bool use_default_domain_prefix) + { + int ret; + struct test_state *ts; +@@ -1114,6 +1117,9 @@ static void test_sss_ncache_short_name_in_domain(void **state) + ncache = ts->ctx; + ts->rctx = mock_rctx(ts, ev, dom, ts->nctx); + assert_non_null(ts->rctx); ++ if (use_default_domain_prefix) { ++ ts->rctx->default_domain = discard_const(TEST_DOM_NAME); ++ } + ts->rctx->cdb = tc->confdb; + + ret = sss_names_init(ts, tc->confdb, TEST_DOM_NAME, &dom->names); +@@ -1156,6 +1162,16 @@ static void test_sss_ncache_short_name_in_domain(void **state) + expect_no_entries_in_dom(ncache, dom2); + } + ++static void test_sss_ncache_short_name_in_domain(void **state) ++{ ++ run_sss_ncache_short_name_in_domain(state, false); ++} ++ ++static void test_sss_ncache_short_name_in_domain_with_prefix(void **state) ++{ ++ run_sss_ncache_short_name_in_domain(state, true); ++} ++ + static void test_sss_ncache_reset(void **state) + { + errno_t ret; +@@ -1320,6 +1336,8 @@ int main(void) + setup, teardown), + cmocka_unit_test_setup_teardown(test_sss_ncache_short_name_in_domain, + setup, teardown), ++ cmocka_unit_test_setup_teardown(test_sss_ncache_short_name_in_domain_with_prefix, ++ setup, teardown), + cmocka_unit_test_setup_teardown(test_sss_ncache_reset, + setup, teardown), + cmocka_unit_test_setup_teardown(test_sss_ncache_locate_uid_gid, +-- +2.21.3 + diff --git a/0054-ad-add-ad_allow_remote_domain_local_groups.patch b/0054-ad-add-ad_allow_remote_domain_local_groups.patch new file mode 100644 index 0000000..a2924cc --- /dev/null +++ b/0054-ad-add-ad_allow_remote_domain_local_groups.patch @@ -0,0 +1,193 @@ +From 6f31f43ee15bcb0933ddca8726b166a93f9371a9 Mon Sep 17 00:00:00 2001 +From: Sumit Bose +Date: Tue, 1 Oct 2019 20:24:09 +0200 +Subject: [PATCH] ad: add ad_allow_remote_domain_local_groups +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +As the option name says if set to true Domain Local groups from remote +domains won't be filtered out. This option is added to facilitate the +migration from other solution and should only be used for this purpose. + +Resolves: https://github.com/SSSD/sssd/issues/5346 + +Reviewed-by: Pavel Březina +(cherry picked with changes from commit 4f65a8d15b8e5f3dd613e789d68f38e60e0addc5) + +Reviewed-by: Pavel Březina +--- + src/config/SSSDConfig/__init__.py.in | 1 + + src/config/cfg_rules.ini | 1 + + src/config/etc/sssd.api.d/sssd-ad.conf | 1 + + src/man/sssd-ad.5.xml | 49 ++++++++++++++++++++++++++ + src/providers/ad/ad_common.c | 3 ++ + src/providers/ad/ad_common.h | 1 + + src/providers/ad/ad_opts.c | 1 + + src/providers/ldap/sdap.h | 1 + + src/providers/ldap/sdap_ad_groups.c | 3 +- + 9 files changed, 60 insertions(+), 1 deletion(-) + +diff --git a/src/config/SSSDConfig/__init__.py.in b/src/config/SSSDConfig/__init__.py.in +index 1aa93f8cf..15ab632aa 100644 +--- a/src/config/SSSDConfig/__init__.py.in ++++ b/src/config/SSSDConfig/__init__.py.in +@@ -250,6 +250,7 @@ option_strings = { + 'ad_maximum_machine_account_password_age' : _('Maximum age in days before the machine account password should be renewed'), + 'ad_machine_account_password_renewal_opts' : _('Option for tuning the machine account renewal task'), + 'ad_use_ldaps' : _('Use LDAPS port for LDAP and Global Catalog requests'), ++ 'ad_allow_remote_domain_local_groups' : _('Do not filter domain local groups from other domains'), + + # [provider/krb5] + 'krb5_kdcip' : _('Kerberos server address'), +diff --git a/src/config/cfg_rules.ini b/src/config/cfg_rules.ini +index e8ea13081..8f8ef30c3 100644 +--- a/src/config/cfg_rules.ini ++++ b/src/config/cfg_rules.ini +@@ -459,6 +459,7 @@ option = ad_maximum_machine_account_password_age + option = ad_server + option = ad_site + option = ad_use_ldaps ++option = ad_allow_remote_domain_local_groups + + # IPA provider specific options + option = ipa_anchor_uuid +diff --git a/src/config/etc/sssd.api.d/sssd-ad.conf b/src/config/etc/sssd.api.d/sssd-ad.conf +index c53c08f37..74599a56d 100644 +--- a/src/config/etc/sssd.api.d/sssd-ad.conf ++++ b/src/config/etc/sssd.api.d/sssd-ad.conf +@@ -21,6 +21,7 @@ ad_site = str, None, false + ad_maximum_machine_account_password_age = int, None, false + ad_machine_account_password_renewal_opts = str, None, false + ad_use_ldaps = bool, None, false ++ad_allow_remote_domain_local_groups = bool, None, false + ldap_uri = str, None, false + ldap_backup_uri = str, None, false + ldap_search_base = str, None, false +diff --git a/src/man/sssd-ad.5.xml b/src/man/sssd-ad.5.xml +index 6fc57ca21..839279bad 100644 +--- a/src/man/sssd-ad.5.xml ++++ b/src/man/sssd-ad.5.xml +@@ -923,6 +923,55 @@ ad_gpo_map_deny = +my_pam_service + + + ++ ++ ad_allow_remote_domain_local_groups (boolean) ++ ++ ++ If this option is set to true SSSD ++ will not filter out Domain Local groups from remote ++ domains in the AD forest. By default they are ++ filtered out e.g. when following a nested group ++ hierarchy in remote domains because they are not ++ valid in the local domain. To be compatible with ++ other solutions which make AD users and groups ++ available on Linux client this option was added. ++ ++ ++ Please note that setting this option to ++ true will be against the intention of ++ Domain Local group in Active Directory and ++ SHOULD ONLY BE USED TO FACILITATE ++ MIGRATION FROM OTHER SOLUTIONS. Although ++ the group exists and user can be member of the group ++ the intention is that the group should be only used ++ in the domain it is defined and in no others. Since ++ there is only one type of POSIX groups the only way ++ to achieve this on the Linux side is to ignore those ++ groups. This is also done by Active Directory as can ++ be seen in the PAC of the Kerberos ticket for a ++ local service or in tokenGroups requests where ++ remote Domain Local groups are missing as well. ++ ++ ++ Given the comments above, if this option is set to ++ true the tokenGroups request must be ++ disabled by setting ++ ldap_use_tokengroups to ++ false to get consistent ++ group-memberships of a users. Additionally the ++ Global Catalog lookup should be skipped as well by ++ setting ad_enable_gc to ++ false. Finally it might be necessary ++ to modify ldap_group_nesting_level if ++ the remote Domain Local groups can only be found ++ with a deeper nesting level. ++ ++ ++ Default: False ++ ++ ++ ++ + + dyndns_update (boolean) + +diff --git a/src/providers/ad/ad_common.c b/src/providers/ad/ad_common.c +index 4e46da7f2..4e51d08e6 100644 +--- a/src/providers/ad/ad_common.c ++++ b/src/providers/ad/ad_common.c +@@ -1072,6 +1072,9 @@ ad_set_sdap_options(struct ad_options *ad_opts, + keytab_path); + } + ++ id_opts->allow_remote_domain_local_groups = dp_opt_get_bool(ad_opts->basic, ++ AD_ALLOW_REMOTE_DOMAIN_LOCAL); ++ + ret = sdap_set_sasl_options(id_opts, + dp_opt_get_string(ad_opts->basic, + AD_HOSTNAME), +diff --git a/src/providers/ad/ad_common.h b/src/providers/ad/ad_common.h +index 753394832..815b41419 100644 +--- a/src/providers/ad/ad_common.h ++++ b/src/providers/ad/ad_common.h +@@ -69,6 +69,7 @@ enum ad_basic_opt { + AD_MAXIMUM_MACHINE_ACCOUNT_PASSWORD_AGE, + AD_MACHINE_ACCOUNT_PASSWORD_RENEWAL_OPTS, + AD_USE_LDAPS, ++ AD_ALLOW_REMOTE_DOMAIN_LOCAL, + + AD_OPTS_BASIC /* opts counter */ + }; +diff --git a/src/providers/ad/ad_opts.c b/src/providers/ad/ad_opts.c +index f2596a935..950ea51ff 100644 +--- a/src/providers/ad/ad_opts.c ++++ b/src/providers/ad/ad_opts.c +@@ -55,6 +55,7 @@ struct dp_option ad_basic_opts[] = { + { "ad_maximum_machine_account_password_age", DP_OPT_NUMBER, { .number = 30 }, NULL_NUMBER }, + { "ad_machine_account_password_renewal_opts", DP_OPT_STRING, { "86400:750" }, NULL_STRING }, + { "ad_use_ldaps", DP_OPT_BOOL, BOOL_FALSE, BOOL_FALSE }, ++ { "ad_allow_remote_domain_local_groups", DP_OPT_BOOL, BOOL_FALSE, BOOL_FALSE }, + DP_OPTION_TERMINATOR + }; + +diff --git a/src/providers/ldap/sdap.h b/src/providers/ldap/sdap.h +index 0bf4fe12b..d9eff20ec 100644 +--- a/src/providers/ldap/sdap.h ++++ b/src/providers/ldap/sdap.h +@@ -511,6 +511,7 @@ struct sdap_options { + bool support_matching_rule; + enum dc_functional_level dc_functional_level; + const char *schema_basedn; ++ bool allow_remote_domain_local_groups; + + /* Certificate mapping support */ + struct sdap_certmap_ctx *sdap_certmap_ctx; +diff --git a/src/providers/ldap/sdap_ad_groups.c b/src/providers/ldap/sdap_ad_groups.c +index 0e36328b9..e8c6280d0 100644 +--- a/src/providers/ldap/sdap_ad_groups.c ++++ b/src/providers/ldap/sdap_ad_groups.c +@@ -38,7 +38,8 @@ errno_t sdap_check_ad_group_type(struct sss_domain_info *dom, + errno_t ret = EOK; + *_need_filter = false; + +- if (opts->schema_type == SDAP_SCHEMA_AD) { ++ if (opts->schema_type == SDAP_SCHEMA_AD ++ && !opts->allow_remote_domain_local_groups) { + ret = sysdb_attrs_get_int32_t(group_attrs, SYSDB_GROUP_TYPE, + &ad_group_type); + if (ret != EOK) { +-- +2.21.3 + diff --git a/0055-man-fix-description-of-dns_resolver_op_timeout.patch b/0055-man-fix-description-of-dns_resolver_op_timeout.patch new file mode 100644 index 0000000..df5070e --- /dev/null +++ b/0055-man-fix-description-of-dns_resolver_op_timeout.patch @@ -0,0 +1,37 @@ +From f768f73903bf0c3d0d1b4361ebff6b111a918470 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Pavel=20B=C5=99ezina?= +Date: Tue, 11 Jun 2019 13:49:13 +0200 +Subject: [PATCH 55/57] man: fix description of dns_resolver_op_timeout + +Resolves: +https://pagure.io/SSSD/sssd/issue/3217 + +Reviewed-by: Jakub Hrozek +Reviewed-by: Sumit Bose +(cherry picked from commit 7b4635c8428917ced63954f2c3c70491b45d7870) +--- + src/man/include/failover.xml | 8 +++++++- + 1 file changed, 7 insertions(+), 1 deletion(-) + +diff --git a/src/man/include/failover.xml b/src/man/include/failover.xml +index cd6fd4d79..11ff86a38 100644 +--- a/src/man/include/failover.xml ++++ b/src/man/include/failover.xml +@@ -77,7 +77,13 @@ + + + +- How long would SSSD talk to a single DNS server. ++ Time in seconds to tell how long would SSSD try ++ to resolve single DNS query (e.g. resolution of a ++ hostname or an SRV record) before trying the next ++ hostname or discovery domain. ++ ++ ++ Default: 6 + + + +-- +2.21.3 + diff --git a/0056-man-fix-description-of-dns_resolver_timeout.patch b/0056-man-fix-description-of-dns_resolver_timeout.patch new file mode 100644 index 0000000..e589679 --- /dev/null +++ b/0056-man-fix-description-of-dns_resolver_timeout.patch @@ -0,0 +1,32 @@ +From 9b1f6f88b1245bc8737a4b2616299a8427d822e0 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Pavel=20B=C5=99ezina?= +Date: Tue, 11 Jun 2019 13:49:33 +0200 +Subject: [PATCH 56/57] man: fix description of dns_resolver_timeout + +Resolves: +https://pagure.io/SSSD/sssd/issue/3217 + +Reviewed-by: Jakub Hrozek +Reviewed-by: Sumit Bose +(cherry picked from commit 3807de1d97fc87cf7c25af264a8b1bbabdef54e2) +--- + src/man/include/failover.xml | 3 +++ + 1 file changed, 3 insertions(+) + +diff --git a/src/man/include/failover.xml b/src/man/include/failover.xml +index 11ff86a38..7b451d831 100644 +--- a/src/man/include/failover.xml ++++ b/src/man/include/failover.xml +@@ -98,6 +98,9 @@ + include several steps, such as resolving DNS SRV + queries or locating the site. + ++ ++ Default: 6 ++ + + + +-- +2.21.3 + diff --git a/0057-failover-add-dns_resolver_server_timeout-option.patch b/0057-failover-add-dns_resolver_server_timeout-option.patch new file mode 100644 index 0000000..01fb7da --- /dev/null +++ b/0057-failover-add-dns_resolver_server_timeout-option.patch @@ -0,0 +1,277 @@ +From 2c7c69485883ab0f408cf2f8cdcc4617462d68ec Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Pavel=20B=C5=99ezina?= +Date: Tue, 11 Jun 2019 13:37:23 +0200 +Subject: [PATCH 57/57] failover: add dns_resolver_server_timeout option + +Resolves: +https://pagure.io/SSSD/sssd/issue/3217 + +Reviewed-by: Jakub Hrozek +Reviewed-by: Sumit Bose +(cherry picked with fixes from commit 99e2a107f01c625cb59cb88589db87294176d6c6) +--- + src/config/SSSDConfig/__init__.py.in | 1 + + src/config/SSSDConfigTest.py | 2 ++ + src/config/cfg_rules.ini | 1 + + src/config/etc/sssd.api.conf | 1 + + src/man/include/failover.xml | 17 ++++++++++++++++- + src/providers/data_provider.h | 1 + + src/providers/data_provider_fo.c | 3 +++ + src/resolv/async_resolv.c | 10 ++++++---- + src/resolv/async_resolv.h | 2 +- + src/tests/cmocka/test_fo_srv.c | 4 ++-- + src/tests/cmocka/test_resolv_fake.c | 2 +- + src/tests/fail_over-tests.c | 2 +- + src/tests/resolv-tests.c | 2 +- + 13 files changed, 37 insertions(+), 11 deletions(-) + +diff --git a/src/config/SSSDConfig/__init__.py.in b/src/config/SSSDConfig/__init__.py.in +index f3c6cfebf..1aa93f8cf 100644 +--- a/src/config/SSSDConfig/__init__.py.in ++++ b/src/config/SSSDConfig/__init__.py.in +@@ -170,6 +170,7 @@ option_strings = { + 'entry_cache_timeout' : _('Entry cache timeout length (seconds)'), + 'lookup_family_order' : _('Restrict or prefer a specific address family when performing DNS lookups'), + 'account_cache_expiration' : _('How long to keep cached entries after last successful login (days)'), ++ 'dns_resolver_server_timeout' : _('How long should SSSD talk to single DNS server before trying next server (miliseconds)'), + 'dns_resolver_op_timeout' : _('How long should keep trying to resolve single DNS query (seconds)'), + 'dns_resolver_timeout' : _('How long to wait for replies from DNS when resolving servers (seconds)'), + 'dns_discovery_domain' : _('The domain part of service discovery DNS query'), +diff --git a/src/config/SSSDConfigTest.py b/src/config/SSSDConfigTest.py +index 979b1806f..d484e527e 100755 +--- a/src/config/SSSDConfigTest.py ++++ b/src/config/SSSDConfigTest.py +@@ -608,6 +608,7 @@ class SSSDConfigTestSSSDDomain(unittest.TestCase): + 'refresh_expired_interval', + 'lookup_family_order', + 'account_cache_expiration', ++ 'dns_resolver_server_timeout', + 'dns_resolver_op_timeout', + 'dns_resolver_timeout', + 'dns_discovery_domain', +@@ -980,6 +981,7 @@ class SSSDConfigTestSSSDDomain(unittest.TestCase): + 'refresh_expired_interval', + 'account_cache_expiration', + 'lookup_family_order', ++ 'dns_resolver_server_timeout', + 'dns_resolver_op_timeout', + 'dns_resolver_timeout', + 'dns_discovery_domain', +diff --git a/src/config/cfg_rules.ini b/src/config/cfg_rules.ini +index 79e366875..e8ea13081 100644 +--- a/src/config/cfg_rules.ini ++++ b/src/config/cfg_rules.ini +@@ -367,6 +367,7 @@ option = account_cache_expiration + option = pwd_expiration_warning + option = filter_users + option = filter_groups ++option = dns_resolver_server_timeout + option = dns_resolver_op_timeout + option = dns_resolver_timeout + option = dns_discovery_domain +diff --git a/src/config/etc/sssd.api.conf b/src/config/etc/sssd.api.conf +index 355c1fc9b..a771a1148 100644 +--- a/src/config/etc/sssd.api.conf ++++ b/src/config/etc/sssd.api.conf +@@ -170,6 +170,7 @@ account_cache_expiration = int, None, false + pwd_expiration_warning = int, None, false + filter_users = list, str, false + filter_groups = list, str, false ++dns_resolver_server_timeout = int, None, false + dns_resolver_op_timeout = int, None, false + dns_resolver_timeout = int, None, false + dns_discovery_domain = str, None, false +diff --git a/src/man/include/failover.xml b/src/man/include/failover.xml +index 7b451d831..f2a01b933 100644 +--- a/src/man/include/failover.xml ++++ b/src/man/include/failover.xml +@@ -71,6 +71,20 @@ + , + manual page. + ++ ++ ++ dns_resolver_server_timeout ++ ++ ++ ++ Time in milliseconds that sets how long would SSSD ++ talk to a single DNS server before trying next one. ++ ++ ++ Default: 2000 ++ ++ ++ + + + dns_resolver_op_timeout +@@ -111,7 +125,8 @@ + ldap_opt_timeout> timeout should be set to + a larger value than dns_resolver_timeout + which in turn should be set to a larger value than +- dns_resolver_op_timeout. ++ dns_resolver_op_timeout which should be larger ++ than dns_resolver_server_timeout. + + + +diff --git a/src/providers/data_provider.h b/src/providers/data_provider.h +index d30d81bda..e8568993a 100644 +--- a/src/providers/data_provider.h ++++ b/src/providers/data_provider.h +@@ -328,6 +328,7 @@ enum dp_res_opts { + DP_RES_OPT_FAMILY_ORDER, + DP_RES_OPT_RESOLVER_TIMEOUT, + DP_RES_OPT_RESOLVER_OP_TIMEOUT, ++ DP_RES_OPT_RESOLVER_SERVER_TIMEOUT, + DP_RES_OPT_DNS_DOMAIN, + + DP_RES_OPTS /* attrs counter */ +diff --git a/src/providers/data_provider_fo.c b/src/providers/data_provider_fo.c +index 6e375147d..0cdb97323 100644 +--- a/src/providers/data_provider_fo.c ++++ b/src/providers/data_provider_fo.c +@@ -835,6 +835,7 @@ static struct dp_option dp_res_default_opts[] = { + { "lookup_family_order", DP_OPT_STRING, { "ipv4_first" }, NULL_STRING }, + { "dns_resolver_timeout", DP_OPT_NUMBER, { .number = 6 }, NULL_NUMBER }, + { "dns_resolver_op_timeout", DP_OPT_NUMBER, { .number = 6 }, NULL_NUMBER }, ++ { "dns_resolver_server_timeout", DP_OPT_NUMBER, { .number = 2000 }, NULL_NUMBER }, + { "dns_discovery_domain", DP_OPT_STRING, NULL_STRING, NULL_STRING }, + DP_OPTION_TERMINATOR + }; +@@ -896,6 +897,8 @@ errno_t be_res_init(struct be_ctx *ctx) + ret = resolv_init(ctx, ctx->ev, + dp_opt_get_int(ctx->be_res->opts, + DP_RES_OPT_RESOLVER_OP_TIMEOUT), ++ dp_opt_get_int(ctx->be_res->opts, ++ DP_RES_OPT_RESOLVER_SERVER_TIMEOUT), + &ctx->be_res->resolv); + if (ret != EOK) { + talloc_zfree(ctx->be_res); +diff --git a/src/resolv/async_resolv.c b/src/resolv/async_resolv.c +index bb2701154..b833d7211 100644 +--- a/src/resolv/async_resolv.c ++++ b/src/resolv/async_resolv.c +@@ -60,8 +60,6 @@ + #define DNS_RR_LEN(r) DNS__16BIT((r) + 8) + #define DNS_RR_TTL(r) DNS__32BIT((r) + 4) + +-#define RESOLV_TIMEOUTMS 2000 +- + enum host_database default_host_dbs[] = { DB_FILES, DB_DNS, DB_SENTINEL }; + + struct fd_watch { +@@ -83,6 +81,9 @@ struct resolv_ctx { + /* Time in milliseconds before canceling a DNS request */ + int timeout; + ++ /* Time in milliseconds for communication with single DNS server. */ ++ int ares_timeout; ++ + /* The timeout watcher periodically calls ares_process_fd() to check + * if our pending requests didn't timeout. */ + int pending_requests; +@@ -423,7 +424,7 @@ recreate_ares_channel(struct resolv_ctx *ctx) + */ + options.sock_state_cb = fd_event; + options.sock_state_cb_data = ctx; +- options.timeout = RESOLV_TIMEOUTMS; ++ options.timeout = ctx->ares_timeout; + /* Only affects ares_gethostbyname */ + options.lookups = discard_const("f"); + options.tries = 1; +@@ -450,7 +451,7 @@ recreate_ares_channel(struct resolv_ctx *ctx) + + int + resolv_init(TALLOC_CTX *mem_ctx, struct tevent_context *ev_ctx, +- int timeout, struct resolv_ctx **ctxp) ++ int timeout, int ares_timeout, struct resolv_ctx **ctxp) + { + int ret; + struct resolv_ctx *ctx; +@@ -467,6 +468,7 @@ resolv_init(TALLOC_CTX *mem_ctx, struct tevent_context *ev_ctx, + + ctx->ev_ctx = ev_ctx; + ctx->timeout = timeout; ++ ctx->ares_timeout = ares_timeout; + + ret = recreate_ares_channel(ctx); + if (ret != EOK) { +diff --git a/src/resolv/async_resolv.h b/src/resolv/async_resolv.h +index 90ed03707..d83a7be44 100644 +--- a/src/resolv/async_resolv.h ++++ b/src/resolv/async_resolv.h +@@ -52,7 +52,7 @@ + struct resolv_ctx; + + int resolv_init(TALLOC_CTX *mem_ctx, struct tevent_context *ev_ctx, +- int timeout, struct resolv_ctx **ctxp); ++ int timeout, int ares_timeout, struct resolv_ctx **ctxp); + + void resolv_reread_configuration(struct resolv_ctx *ctx); + +diff --git a/src/tests/cmocka/test_fo_srv.c b/src/tests/cmocka/test_fo_srv.c +index a11ebbb54..c13cf3a69 100644 +--- a/src/tests/cmocka/test_fo_srv.c ++++ b/src/tests/cmocka/test_fo_srv.c +@@ -49,7 +49,7 @@ struct resolv_ctx { + + /* mock resolver interface. The resolver test is separate */ + int resolv_init(TALLOC_CTX *mem_ctx, struct tevent_context *ev_ctx, +- int timeout, struct resolv_ctx **ctxp) ++ int timeout, int ares_timeout, struct resolv_ctx **ctxp) + { + *ctxp = talloc(mem_ctx, struct resolv_ctx); + return EOK; +@@ -230,7 +230,7 @@ static int test_fo_setup(void **state) + assert_non_null(test_ctx->ctx); + + ret = resolv_init(test_ctx, test_ctx->ctx->ev, +- TEST_RESOLV_TIMEOUT, &test_ctx->resolv); ++ TEST_RESOLV_TIMEOUT, 2000, &test_ctx->resolv); + assert_non_null(test_ctx->resolv); + + memset(&fopts, 0, sizeof(fopts)); +diff --git a/src/tests/cmocka/test_resolv_fake.c b/src/tests/cmocka/test_resolv_fake.c +index 4cb3d4027..0f4011a39 100644 +--- a/src/tests/cmocka/test_resolv_fake.c ++++ b/src/tests/cmocka/test_resolv_fake.c +@@ -240,7 +240,7 @@ static int test_resolv_fake_setup(void **state) + assert_non_null(test_ctx->ctx); + + ret = resolv_init(test_ctx, test_ctx->ctx->ev, +- TEST_DEFAULT_TIMEOUT, &test_ctx->resolv); ++ TEST_DEFAULT_TIMEOUT, 2000, &test_ctx->resolv); + assert_int_equal(ret, EOK); + + *state = test_ctx; +diff --git a/src/tests/fail_over-tests.c b/src/tests/fail_over-tests.c +index 5312b2772..b2269ef3b 100644 +--- a/src/tests/fail_over-tests.c ++++ b/src/tests/fail_over-tests.c +@@ -73,7 +73,7 @@ setup_test(void) + fail("Could not init tevent context"); + } + +- ret = resolv_init(ctx, ctx->ev, 5, &ctx->resolv); ++ ret = resolv_init(ctx, ctx->ev, 5, 2000, &ctx->resolv); + if (ret != EOK) { + talloc_free(ctx); + fail("Could not init resolv context"); +diff --git a/src/tests/resolv-tests.c b/src/tests/resolv-tests.c +index 4a2b3b904..bc4cd7cc1 100644 +--- a/src/tests/resolv-tests.c ++++ b/src/tests/resolv-tests.c +@@ -76,7 +76,7 @@ static int setup_resolv_test(int timeout, struct resolv_test_ctx **ctx) + return EFAULT; + } + +- ret = resolv_init(test_ctx, test_ctx->ev, timeout, &test_ctx->resolv); ++ ret = resolv_init(test_ctx, test_ctx->ev, timeout, 2000, &test_ctx->resolv); + if (ret != EOK) { + fail("Could not init resolv context"); + talloc_free(test_ctx); +-- +2.21.3 + diff --git a/0058-nss-check-if-groups-are-filtered-during-initgroups.patch b/0058-nss-check-if-groups-are-filtered-during-initgroups.patch new file mode 100644 index 0000000..b0dc81c --- /dev/null +++ b/0058-nss-check-if-groups-are-filtered-during-initgroups.patch @@ -0,0 +1,113 @@ +From bb736d3f02861366d11d2f03314295bd1c1be209 Mon Sep 17 00:00:00 2001 +From: Sumit Bose +Date: Tue, 17 Nov 2020 12:59:23 +0100 +Subject: [PATCH] nss: check if groups are filtered during initgroups +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +If groups are filtered, i.e. SSSD should not handle them, they should +not appear in the group list returned by an initgroups request. + +Resolves: https://github.com/SSSD/sssd/issues/5403 + +Reviewed-by: Pavel Březina +(cherry picked from commit c87b2208b9a58c12eeceb5b8ccf9c34dcd835b8d) +--- + src/responder/nss/nss_protocol_grent.c | 35 ++++++++++++++++++++++++++ + src/tests/intg/test_ldap.py | 12 +++++++++ + 2 files changed, 47 insertions(+) + +diff --git a/src/responder/nss/nss_protocol_grent.c b/src/responder/nss/nss_protocol_grent.c +index 2367d9ecd..4c7ea9aed 100644 +--- a/src/responder/nss/nss_protocol_grent.c ++++ b/src/responder/nss/nss_protocol_grent.c +@@ -308,6 +308,34 @@ done: + return EOK; + } + ++static bool is_group_filtered(struct sss_nc_ctx *ncache, ++ struct sss_domain_info *domain, ++ const char *grp_name, gid_t gid) ++{ ++ int ret; ++ ++ if (grp_name == NULL) { ++ DEBUG(SSSDBG_CRIT_FAILURE, ++ "Group with gid [%"SPRIgid"] has no name, this should never " ++ "happen, trying to continue without.\n", gid); ++ } else { ++ ret = sss_ncache_check_group(ncache, domain, grp_name); ++ if (ret == EEXIST) { ++ DEBUG(SSSDBG_TRACE_FUNC, "Group [%s] is filtered out! " ++ "(negative cache)", grp_name); ++ return true; ++ } ++ } ++ ret = sss_ncache_check_gid(ncache, domain, gid); ++ if (ret == EEXIST) { ++ DEBUG(SSSDBG_TRACE_FUNC, "Group [%"SPRIgid"] is filtered out! " ++ "(negative cache)", gid); ++ return true; ++ } ++ ++ return false; ++} ++ + errno_t + nss_protocol_fill_initgr(struct nss_ctx *nss_ctx, + struct nss_cmd_ctx *cmd_ctx, +@@ -326,6 +354,7 @@ nss_protocol_fill_initgr(struct nss_ctx *nss_ctx, + size_t body_len; + size_t rp; + gid_t gid; ++ const char *grp_name; + gid_t orig_gid; + errno_t ret; + int i; +@@ -374,6 +403,8 @@ nss_protocol_fill_initgr(struct nss_ctx *nss_ctx, + gid = sss_view_ldb_msg_find_attr_as_uint64(domain, msg, SYSDB_GIDNUM, + 0); + posix = ldb_msg_find_attr_as_string(msg, SYSDB_POSIX, NULL); ++ grp_name = sss_view_ldb_msg_find_attr_as_string(domain, msg, SYSDB_NAME, ++ NULL); + + if (gid == 0) { + if (posix != NULL && strcmp(posix, "FALSE") == 0) { +@@ -386,6 +417,10 @@ nss_protocol_fill_initgr(struct nss_ctx *nss_ctx, + } + } + ++ if (is_group_filtered(nss_ctx->rctx->ncache, domain, grp_name, gid)) { ++ continue; ++ } ++ + SAFEALIGN_COPY_UINT32(&body[rp], &gid, &rp); + num_results++; + +diff --git a/src/tests/intg/test_ldap.py b/src/tests/intg/test_ldap.py +index 2e88ac7d1..afd23c71e 100644 +--- a/src/tests/intg/test_ldap.py ++++ b/src/tests/intg/test_ldap.py +@@ -1190,6 +1190,18 @@ def test_nss_filters(ldap_conn, sanity_nss_filter): + with pytest.raises(KeyError): + grp.getgrgid(14) + ++ # test initgroups - user1 is member of group_two_one_user_groups (2019) ++ # which is filtered out ++ (res, errno, gids) = sssd_id.call_sssd_initgroups("user1", 2001) ++ assert res == sssd_id.NssReturnCode.SUCCESS ++ ++ user_with_group_ids = [2001, 2012, 2015, 2017, 2018] ++ assert sorted(gids) == sorted(user_with_group_ids), \ ++ "result: %s\n expected %s" % ( ++ ", ".join(["%s" % s for s in sorted(gids)]), ++ ", ".join(["%s" % s for s in sorted(user_with_group_ids)]) ++ ) ++ + + @pytest.fixture + def sanity_nss_filter_cached(request, ldap_conn): +-- +2.21.3 + diff --git a/0059-CACHE-Create-timestamp-if-missing.patch b/0059-CACHE-Create-timestamp-if-missing.patch new file mode 100644 index 0000000..1b910be --- /dev/null +++ b/0059-CACHE-Create-timestamp-if-missing.patch @@ -0,0 +1,50 @@ +From ca8cdbde6285d14181372f1ce8e5f6faf72e1d80 Mon Sep 17 00:00:00 2001 +From: Tomas Halman +Date: Mon, 16 Nov 2020 17:28:19 +0100 +Subject: [PATCH 59/60] CACHE: Create timestamp if missing +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +In some cases, object is stored in cache but the corresponding +record in timestamp cache is missing (for example when timestamp +cache file is deleted). The timestamp is never created in such +case. + +With this patch we create new timestamp object if update doesn't +work for this particular reason (missing object). + +Resolves: https://github.com/SSSD/sssd/issues/5121 + +Reviewed-by: Pavel Březina +(cherry picked from commit 37761b42f570e9019f9dd850912a29385e80a14c) +--- + src/db/sysdb_ops.c | 10 +++++++++- + 1 file changed, 9 insertions(+), 1 deletion(-) + +diff --git a/src/db/sysdb_ops.c b/src/db/sysdb_ops.c +index d4ad69e39..0e0a95961 100644 +--- a/src/db/sysdb_ops.c ++++ b/src/db/sysdb_ops.c +@@ -1371,9 +1371,17 @@ int sysdb_set_entry_attr(struct sysdb_ctx *sysdb, + + if (ret == EOK && is_ts_ldb_dn(entry_dn)) { + tret = sysdb_set_ts_entry_attr(sysdb, entry_dn, attrs, mod_op); ++ if (tret == ENOENT && mod_op == SYSDB_MOD_REP) { ++ /* Update failed because TS does non exist. Create missing TS */ ++ tret = sysdb_set_ts_entry_attr(sysdb, entry_dn, attrs, ++ SYSDB_MOD_ADD); ++ DEBUG(SSSDBG_TRACE_FUNC, ++ "The TS value for %s does not exist, trying to create it\n", ++ ldb_dn_get_linearized(entry_dn)); ++ } + if (tret != EOK) { + DEBUG(SSSDBG_MINOR_FAILURE, +- "Cannot set ts attrs for %s\n", ldb_dn_get_linearized(entry_dn)); ++ "Cannot set TS attrs for %s\n", ldb_dn_get_linearized(entry_dn)); + /* Not fatal */ + } else { + state_mask |= SSS_SYSDB_TS_CACHE; +-- +2.21.3 + diff --git a/0060-TESTS-Add-test-for-recreating-cache-timestamp.patch b/0060-TESTS-Add-test-for-recreating-cache-timestamp.patch new file mode 100644 index 0000000..4b240a5 --- /dev/null +++ b/0060-TESTS-Add-test-for-recreating-cache-timestamp.patch @@ -0,0 +1,142 @@ +From dc0fb9bc5e1b186b274739389437d68c7b257e1f Mon Sep 17 00:00:00 2001 +From: Tomas Halman +Date: Wed, 18 Nov 2020 13:32:28 +0100 +Subject: [PATCH 60/60] TESTS: Add test for recreating cache timestamp +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Reviewed-by: Pavel Březina +(cherry picked from commit 62b2b4972e41393cd43b58d9e6451a2c58942cb2) +--- + src/tests/cmocka/test_sysdb_ts_cache.c | 107 +++++++++++++++++++++++++ + 1 file changed, 107 insertions(+) + +diff --git a/src/tests/cmocka/test_sysdb_ts_cache.c b/src/tests/cmocka/test_sysdb_ts_cache.c +index ae8b1b16c..24b26d950 100644 +--- a/src/tests/cmocka/test_sysdb_ts_cache.c ++++ b/src/tests/cmocka/test_sysdb_ts_cache.c +@@ -1606,6 +1606,107 @@ static void test_sysdb_search_with_ts(void **state) + talloc_free(res); + } + ++static void test_sysdb_user_missing_ts(void **state) ++{ ++ int ret; ++ struct sysdb_ts_test_ctx *test_ctx = talloc_get_type_abort(*state, ++ struct sysdb_ts_test_ctx); ++ struct ldb_result *res = NULL; ++ struct sysdb_attrs *attrs = NULL; ++ ++ /* Nothing must be stored in either cache at the beginning of the test */ ++ res = sysdb_getpwnam_res(test_ctx, test_ctx->tctx->dom, TEST_USER_NAME); ++ assert_int_equal(res->count, 0); ++ talloc_free(res); ++ ++ /* add user to cache */ ++ attrs = create_modstamp_attrs(test_ctx, TEST_MODSTAMP_1); ++ assert_non_null(attrs); ++ ret = sysdb_store_user(test_ctx->tctx->dom, TEST_USER_NAME, NULL, ++ TEST_USER_UID, TEST_USER_GID, TEST_USER_NAME, ++ "/home/"TEST_USER_NAME, "/bin/bash", NULL, ++ attrs, NULL, TEST_CACHE_TIMEOUT, ++ TEST_NOW_1); ++ assert_int_equal(ret, EOK); ++ talloc_zfree(attrs); ++ ++ /* remove timestamp */ ++ struct ldb_dn *userdn = sysdb_user_dn(test_ctx, test_ctx->tctx->dom, TEST_USER_NAME); ++ ret = ldb_delete(test_ctx->tctx->dom->sysdb->ldb_ts, userdn); ++ assert_int_equal(ret, EOK); ++ ++ /* update user */ ++ attrs = create_modstamp_attrs(test_ctx, TEST_MODSTAMP_2); ++ assert_non_null(attrs); ++ ret = sysdb_store_user(test_ctx->tctx->dom, TEST_USER_NAME, NULL, ++ TEST_USER_UID, TEST_USER_GID, TEST_USER_NAME, ++ "/home/"TEST_USER_NAME, "/bin/bash", NULL, ++ attrs, NULL, TEST_CACHE_TIMEOUT, ++ TEST_NOW_2); ++ assert_int_equal(ret, EOK); ++ talloc_zfree(attrs); ++ ++ /* check that ts is back */ ++ SSS_LDB_SEARCH(ret, test_ctx->tctx->dom->sysdb->ldb_ts, test_ctx, &res, userdn, ++ LDB_SCOPE_BASE, NULL, NULL); ++ assert_int_equal(ret, EOK); ++ assert_int_equal(res->count, 1); ++ talloc_zfree(res); ++ talloc_zfree(userdn); ++} ++ ++static void test_sysdb_group_missing_ts(void **state) ++{ ++ int ret; ++ struct sysdb_ts_test_ctx *test_ctx = talloc_get_type_abort(*state, ++ struct sysdb_ts_test_ctx); ++ struct ldb_result *res = NULL; ++ struct sysdb_attrs *group_attrs = NULL; ++ struct ldb_dn *groupdn = NULL; ++ ++ /* Nothing must be stored in either cache at the beginning of the test */ ++ res = sysdb_getgrnam_res(test_ctx, test_ctx->tctx->dom, TEST_GROUP_NAME); ++ assert_int_equal(res->count, 0); ++ talloc_free(res); ++ ++ /* add group to cache */ ++ group_attrs = create_modstamp_attrs(test_ctx, TEST_MODSTAMP_1); ++ assert_non_null(group_attrs); ++ ret = sysdb_store_group(test_ctx->tctx->dom, ++ TEST_GROUP_NAME, ++ TEST_GROUP_GID, ++ group_attrs, ++ TEST_CACHE_TIMEOUT, ++ TEST_NOW_1); ++ assert_int_equal(ret, EOK); ++ talloc_zfree(group_attrs); ++ ++ /* remove timestamp */ ++ groupdn = sysdb_group_dn(test_ctx, test_ctx->tctx->dom, TEST_GROUP_NAME); ++ ret = ldb_delete(test_ctx->tctx->dom->sysdb->ldb_ts, groupdn); ++ assert_int_equal(ret, EOK); ++ ++ /* update group */ ++ group_attrs = create_modstamp_attrs(test_ctx, TEST_MODSTAMP_2); ++ assert_non_null(group_attrs); ++ ret = sysdb_store_group(test_ctx->tctx->dom, ++ TEST_GROUP_NAME, ++ TEST_GROUP_GID, ++ group_attrs, ++ TEST_CACHE_TIMEOUT, ++ TEST_NOW_2); ++ assert_int_equal(ret, EOK); ++ talloc_zfree(group_attrs); ++ ++ /* check that ts is back */ ++ SSS_LDB_SEARCH(ret, test_ctx->tctx->dom->sysdb->ldb_ts, test_ctx, &res, groupdn, ++ LDB_SCOPE_BASE, NULL, NULL); ++ assert_int_equal(ret, EOK); ++ assert_int_equal(res->count, 1); ++ talloc_zfree(res); ++ talloc_zfree(groupdn); ++} ++ + int main(int argc, const char *argv[]) + { + int rv; +@@ -1660,6 +1761,12 @@ int main(int argc, const char *argv[]) + cmocka_unit_test_setup_teardown(test_sysdb_search_with_ts, + test_sysdb_ts_setup, + test_sysdb_ts_teardown), ++ cmocka_unit_test_setup_teardown(test_sysdb_user_missing_ts, ++ test_sysdb_ts_setup, ++ test_sysdb_ts_teardown), ++ cmocka_unit_test_setup_teardown(test_sysdb_group_missing_ts, ++ test_sysdb_ts_setup, ++ test_sysdb_ts_teardown), + }; + + /* Set debug level to invalid value so we can decide if -d 0 was used. */ +-- +2.21.3 + diff --git a/0061-cert-matching.patch b/0061-cert-matching.patch new file mode 100644 index 0000000..cac94eb --- /dev/null +++ b/0061-cert-matching.patch @@ -0,0 +1,2180 @@ +From e13d8a12513bfba1531cc867fbf687ff8673241f Mon Sep 17 00:00:00 2001 +From: Alexey Tikhonov +Date: Thu, 10 Dec 2020 19:45:08 +0100 +Subject: [PATCH] Squashed commit of the following: +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +commit 6b3b4b0bf945814e8886b900dcda18de25f38bb4 +Author: Sumit Bose +Date: Thu Dec 12 13:10:16 2019 +0100 + + certmap: mention special regex characters in man page + + Since some of the matching rules use regular expressions some characters + must be escaped so that they can be used a ordinary characters in the + rules. + + Related to https://pagure.io/SSSD/sssd/issue/4127 + + Reviewed-by: Michal Židek + (cherry picked from commit 21cb9fb28db1f2eb4ee770eb029bfe20233e4392) + +commit 451410e72514bd68e4b56b1a42c97ade6783e74b +Author: Sumit Bose +Date: Tue Nov 27 16:42:38 2018 +0100 + + test: add certificate without KU to certmap tests + + Make sure there is a test for a certificate without key-usage (KU) + + Related to https://bugzilla.redhat.com/show_bug.cgi?id=1660899 + + Reviewed-by: Jakub Hrozek + (cherry picked from commit e1734ba828470d00370c44c95da56822fdcc104d) + +commit e7966dfa40b9a7fcde79a07f146ae5283a7bc8e5 +Author: Sumit Bose +Date: Mon Nov 26 12:03:13 2018 +0100 + + certmap: allow missing KU in OpenSSL version + + Make sure a missing key-usage (KU) is not treated as an error and is + handled equally in the NSS and OpenSSL implementation + + Related to https://bugzilla.redhat.com/show_bug.cgi?id=1660899 + + Reviewed-by: Jakub Hrozek + (cherry picked from commit aef8e49b7ee2e7743d6981070d61bc89b7c8fcfb) + +commit 6e9e6673916b61197df8a809f56c73d8bdbb868c +Author: Alexey Tikhonov +Date: Mon Jan 7 17:04:34 2019 +0100 + + CONFIG: validator rules & test + + Add support of 'certmap' config section to validator rules + + Resolves: + https://pagure.io/SSSD/sssd/issue/3845 + + Reviewed-by: Jakub Hrozek + (cherry picked from commit 8e9e8011ce17860bec67a572e4c11a9178c03b8e) + +commit eec9d72a242b2b05369f0eb89c4ebcda26d59802 +Author: Sumit Bose +Date: Fri Sep 7 22:26:21 2018 +0200 + + intg: add Smartcard authentication tests + + Two test for Smartcard authentication of a local user, i.e. a user + managed by the files provider, are added. One for a successful + authentication, the other for a failed authentication with a wrong PIN. + + Related to https://pagure.io/SSSD/sssd/issue/3500 + + Reviewed-by: Jakub Hrozek + (cherry picked with fixes from commit 657f3b89bca9adfb13f0867c91f1d76845d2d6dd) + +commit cc2840fbb494ac686e9a3ae0016827a44d14769f +Author: Sumit Bose +Date: Fri Sep 7 22:17:47 2018 +0200 + + test_ca: set a password/PIN to nss databases + + To make sure the PIN is properly checked during tests the NSS databases + need a password. + + Related to https://pagure.io/SSSD/sssd/issue/3500 + + Reviewed-by: Jakub Hrozek + (cherry picked from commit a45a410dc7fa7cf84bcac541e693ee8781e25431) + +commit 0a989c62b4a3b73f23d9b6956ac81afaed9901f7 +Author: Sumit Bose +Date: Mon Sep 10 22:03:55 2018 +0200 + + test_ca: test library only for readable + + On Debian libraries typically do not have the execute-bit set so it is + better to only check for readability. + + Related to https://pagure.io/SSSD/sssd/issue/3500 + + Reviewed-by: Jakub Hrozek + (cherry picked from commit 91aea762d02731193eb66a00b930ff1fe8bc5ab8) + +commit 5a47b213b11cbf74dad47594d1826985f6b68f22 +Author: Sumit Bose +Date: Fri Sep 7 22:16:50 2018 +0200 + + PAM: use better PAM error code for failed Smartcard authentication + + If the user enters a wrong PIN the PAM responder currently returns + PAM_USER_UNKNOWN better is PAM_AUTH_ERR. + + Related to https://pagure.io/SSSD/sssd/issue/3500 + + Reviewed-by: Jakub Hrozek + (cherry picked from commit 442ae7b1d0704cdd667d4f1ba4c165ce3f3ffed4) + +commit b6907d7cd5ab7568971ddb48f3932f106e86fe06 +Author: Sumit Bose +Date: Mon Sep 3 18:38:42 2018 +0200 + + doc: add certificate mapping section to man page + + Related to https://pagure.io/SSSD/sssd/issue/3500 + + Reviewed-by: Jakub Hrozek + (cherry picked with fixes from commit 0c739e969a617bdb4c06cdfd63772bf6d283c518) + +commit d75b196312c4cec767c196c663ff969b6aebcd6b +Author: Sumit Bose +Date: Mon Jul 9 18:56:26 2018 +0200 + + PAM: add certificate matching rules from all domains + + Currently the PAM responder only reads the certificate mapping and + matching rules from the first domain. To support Smartcard + authentication for local and remote users all configured domains must be + taken into account. + + Related to https://pagure.io/SSSD/sssd/issue/3500 + + Reviewed-by: Jakub Hrozek + (cherry picked from commit d42f44d54453d3ddb54875374c1b61dc1e7cd821) + +commit 167ab7206913c17617a8e5ada7567d91f8ed6e11 +Author: Sumit Bose +Date: Mon Jul 9 18:45:21 2018 +0200 + + responder: make sure SSS_DP_CERT is passed to files provider + + Currently the files provider is only contacted once in a while to update + the full cache with fresh data from the passwd file. To allow rule based + certificate mapping the lookup by certificate request must be always + send to the file provider so that it can evaluate the rules and add the + certificate to cached entry of the matching user. + + Related to https://pagure.io/SSSD/sssd/issue/3500 + + Reviewed-by: Jakub Hrozek + (cherry picked from commit 9fdc5f1d87a133885e6a22810a7eb980c60dcb55) + +commit 69def7a3e81313a30ceae937f9cde5d62e999c3d +Author: Sumit Bose +Date: Mon Jul 9 18:37:46 2018 +0200 + + files: add support for Smartcard authentication + + To support certificate based authentication the files provider must be + able to map a certificate to a user during a BE_REQ_BY_CERT request. + + Additionally the authentication request should be handled by the PAM + responder code which is responsible for the local Smartcard + authentication. To be consistent with the other backend an authentication + handler is added to the files provider which unconditionally returns the + offline error code telling the PAM responder to handle the + authentication if it has access to the needed credentials. + + Related to https://pagure.io/SSSD/sssd/issue/3500 + + Reviewed-by: Jakub Hrozek + (cherry picked from commit 275eeed24adc31f3df51cf278f509a4be76a3a3c) + +commit e96ba56ea8037d58e1335f7dacd3b19919bc4135 +Author: Sumit Bose +Date: Fri Jul 6 15:17:10 2018 +0200 + + confdb: add special handling for rules for the files provider + + To make the configuration more simple there are some special assumption + for local users, i.e. user managed by the files provider. + + Related to https://pagure.io/SSSD/sssd/issue/3500 + + Reviewed-by: Jakub Hrozek + (cherry picked from commit 9386ef605ffbc03abe2bc273efddbc099441fe3b) + +commit d304f5a9e60f7f6eb915a10067ee2e5e5f14c369 +Author: Sumit Bose +Date: Tue Jul 3 11:31:12 2018 +0200 + + sysdb: sysdb_certmap_add() handle domains more flexible + + sysdb_ldb_msg_attr_to_certmap_info() creates an empty list if there are + no domains defined, sysdb_certmap_add() should be able to handle both a + missing or an empty domains list. + + Related to https://pagure.io/SSSD/sssd/issue/3500 + + Reviewed-by: Jakub Hrozek + (cherry picked from commit 06f7005d38d164879b727708feff80004b422f91) + +commit 53befb320c2b60a420a2588425fd5004ceec791a +Author: Sumit Bose +Date: Mon Jul 2 12:20:53 2018 +0200 + + AD/LDAP: read certificate mapping rules from config file + + Related to https://pagure.io/SSSD/sssd/issue/3500 + + Reviewed-by: Jakub Hrozek + (cherry picked from commit 15301db1dc1e5e2aafc1805a30e3b28756218c9b) + +commit 670a1ca6b7b22bb3a1079111528ee7e4aafd97e5 +Author: Sumit Bose +Date: Mon Jul 2 10:38:54 2018 +0200 + + confdb: add confdb_certmap_to_sysdb() + + Add a function to write certificate mapping and matching rules from the + config database to the cache of a domain. + + Related to https://pagure.io/SSSD/sssd/issue/3500 + + Reviewed-by: Jakub Hrozek + (cherry picked with fixes from commit d9cc38008a51a8a5189904f175e4d10cbde4a974) + +commit 14c15cc6db16726419fbf6df76b5c83aec49192a +Author: Sumit Bose +Date: Fri Jun 29 18:13:59 2018 +0200 + + sysdb: add attr_map attribute to sysdb_ldb_msg_attr_to_certmap_info() + + Allow more flexible attribute mapping in + sysdb_ldb_msg_attr_to_certmap_info() + + Related to https://pagure.io/SSSD/sssd/issue/3500 + + Reviewed-by: Jakub Hrozek + (cherry picked from commit 0bf709ad348ca115443bd21e4e369abd5d7698c4) + +commit f867c2a293651043072afe1dd7a8a78a05e5fe4d +Author: Sumit Bose +Date: Tue Jul 3 11:30:07 2018 +0200 + + sysdb_ldb_msg_attr_to_certmap_info: set SSS_CERTMAP_MIN_PRIO + + Make sure that priority is always set. + + Related to https://pagure.io/SSSD/sssd/issue/3500 + + Reviewed-by: Jakub Hrozek + (cherry picked from commit d1dd7f7703b4f40d2fbb830e28969b31b8a1673e) + +commit 8ef2cc11008ef86f4dfcbc267c797bf8ee265455 +Author: Sumit Bose +Date: Fri Jun 29 17:49:50 2018 +0200 + + sysdb: extract sysdb_ldb_msg_attr_to_certmap_info() call + + Related to https://pagure.io/SSSD/sssd/issue/3500 + + Reviewed-by: Jakub Hrozek + (cherry picked from commit 7c619ae08f05a7595d15cf11b64461a7d19cfaa7) +--- + Makefile.am | 2 + + configure.ac | 1 + + contrib/sssd.spec.in | 1 + + src/confdb/confdb.c | 158 +++++++++++++ + src/confdb/confdb.h | 23 ++ + src/config/cfg_rules.ini | 10 + + src/db/sysdb.h | 5 + + src/db/sysdb_certmap.c | 223 +++++++++++------- + src/external/cwrap.m4 | 5 + + src/external/intgcheck.m4 | 1 + + src/external/test_ca.m4 | 2 +- + src/lib/certmap/sss_cert_content_crypto.c | 22 +- + src/lib/certmap/sss_cert_content_nss.c | 21 +- + src/man/sss-certmap.5.xml | 9 + + src/man/sssd.conf.5.xml | 149 ++++++++++++ + src/providers/ad/ad_init.c | 16 ++ + src/providers/files/files_auth.c | 69 ++++++ + src/providers/files/files_certmap.c | 186 +++++++++++++++ + src/providers/files/files_id.c | 20 ++ + src/providers/files/files_init.c | 29 +++ + src/providers/files/files_private.h | 17 ++ + src/providers/ldap/ldap_init.c | 16 ++ + src/responder/common/responder_dp.c | 20 +- + src/responder/pam/pamsrv.h | 2 +- + src/responder/pam/pamsrv_cmd.c | 6 +- + src/responder/pam/pamsrv_p11.c | 77 +++--- + src/tests/cmocka/test_certmap.c | 45 ++++ + src/tests/cmocka/test_config_check.c | 16 +- + src/tests/intg/test_pam_responder.py | 109 ++++++++- + src/tests/test_CA/Makefile.am | 24 +- + src/tests/test_CA/SSSD_test_cert_0004.config | 11 + + src/tests/test_CA/SSSD_test_cert_key_0004.pem | 28 +++ + 32 files changed, 1170 insertions(+), 153 deletions(-) + create mode 100644 src/providers/files/files_auth.c + create mode 100644 src/providers/files/files_certmap.c + create mode 100644 src/tests/test_CA/SSSD_test_cert_0004.config + create mode 100644 src/tests/test_CA/SSSD_test_cert_key_0004.pem + +diff --git a/Makefile.am b/Makefile.am +index 718041634..77f5faf6b 100644 +--- a/Makefile.am ++++ b/Makefile.am +@@ -4094,6 +4094,8 @@ libsss_proxy_la_LDFLAGS = \ + libsss_files_la_SOURCES = \ + src/providers/files/files_init.c \ + src/providers/files/files_id.c \ ++ src/providers/files/files_auth.c \ ++ src/providers/files/files_certmap.c \ + src/providers/files/files_ops.c \ + src/util/inotify.c \ + $(NULL) +diff --git a/configure.ac b/configure.ac +index 5154918b1..89abddef4 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -488,6 +488,7 @@ AM_CONDITIONAL([HAVE_CHECK], [test x$have_check != x]) + AM_CHECK_CMOCKA + AM_CHECK_UID_WRAPPER + AM_CHECK_NSS_WRAPPER ++AM_CHECK_PAM_WRAPPER + AM_CHECK_TEST_CA + + # Check if the user wants SSSD to be compiled with systemtap probes +diff --git a/contrib/sssd.spec.in b/contrib/sssd.spec.in +index 10b5bd56c..52a77ae7a 100644 +--- a/contrib/sssd.spec.in ++++ b/contrib/sssd.spec.in +@@ -236,6 +236,7 @@ BuildRequires: selinux-policy-targeted + BuildRequires: libcmocka-devel >= 1.0.0 + BuildRequires: uid_wrapper + BuildRequires: nss_wrapper ++BuildRequires: pam_wrapper + + # Test CA requires openssl independent if SSSD is build with NSS or openssl, + # openssh is needed for ssh-keygen and NSS builds need nss-tools for certutil. +diff --git a/src/confdb/confdb.c b/src/confdb/confdb.c +index e55f88e4e..97de6d3b1 100644 +--- a/src/confdb/confdb.c ++++ b/src/confdb/confdb.c +@@ -2209,3 +2209,161 @@ done: + talloc_free(tmp_ctx); + return ret; + } ++ ++static errno_t certmap_local_check(struct ldb_message *msg) ++{ ++ const char *rule_name; ++ const char *tmp_str; ++ int ret; ++ ++ rule_name = ldb_msg_find_attr_as_string(msg, CONFDB_CERTMAP_NAME, NULL); ++ if (rule_name == NULL) { ++ DEBUG(SSSDBG_CRIT_FAILURE, "Certficate mapping rule [%s] has no name.", ++ ldb_dn_get_linearized(msg->dn)); ++ return EINVAL; ++ } ++ ++ tmp_str = ldb_msg_find_attr_as_string(msg, CONFDB_CERTMAP_DOMAINS, NULL); ++ if (tmp_str != NULL) { ++ DEBUG(SSSDBG_CONF_SETTINGS, ++ "Option [%s] is ignored for local certmap rules.\n", ++ CONFDB_CERTMAP_DOMAINS); ++ } ++ ++ tmp_str = ldb_msg_find_attr_as_string(msg, CONFDB_CERTMAP_MAPRULE, NULL); ++ if (tmp_str != NULL) { ++ if (tmp_str[0] != '(' || tmp_str[strlen(tmp_str) - 1] != ')') { ++ DEBUG(SSSDBG_CONF_SETTINGS, ++ "Mapping rule must be in braces (...).\n"); ++ return EINVAL; ++ } ++ DEBUG(SSSDBG_TRACE_ALL, "Using [%s] mapping rule of [%s].\n", ++ tmp_str, ldb_dn_get_linearized(msg->dn)); ++ return EOK; ++ } ++ ++ tmp_str = talloc_asprintf(msg, "(%s)", rule_name); ++ if (tmp_str == NULL) { ++ DEBUG(SSSDBG_OP_FAILURE, "talloc_asprintf failed.\n"); ++ return ENOMEM; ++ } ++ ret = ldb_msg_add_string(msg, CONFDB_CERTMAP_MAPRULE, tmp_str); ++ if (ret != LDB_SUCCESS) { ++ talloc_free(discard_const(tmp_str)); ++ DEBUG(SSSDBG_OP_FAILURE, "ldb_msg_add_string failed.\n"); ++ return EIO; ++ } ++ ++ DEBUG(SSSDBG_TRACE_ALL, "Using [%s] as mapping rule for [%s].\n", ++ tmp_str, ldb_dn_get_linearized(msg->dn)); ++ ++ return EOK; ++} ++ ++static errno_t confdb_get_all_certmaps(TALLOC_CTX *mem_ctx, ++ struct confdb_ctx *cdb, ++ struct sss_domain_info *dom, ++ struct certmap_info ***_certmap_list) ++{ ++ TALLOC_CTX *tmp_ctx = NULL; ++ struct ldb_dn *dn = NULL; ++ struct ldb_result *res = NULL; ++ /* The attributte order is important, because it is used in ++ * sysdb_ldb_msg_attr_to_certmap_info and must match ++ * enum certmap_info_member. */ ++ static const char *attrs[] = { CONFDB_CERTMAP_NAME, ++ CONFDB_CERTMAP_MAPRULE, ++ CONFDB_CERTMAP_MATCHRULE, ++ CONFDB_CERTMAP_PRIORITY, ++ CONFDB_CERTMAP_DOMAINS, ++ NULL}; ++ struct certmap_info **certmap_list = NULL; ++ size_t c; ++ int ret; ++ ++ tmp_ctx = talloc_new(NULL); ++ if (tmp_ctx == NULL) { ++ return ENOMEM; ++ } ++ ++ dn = ldb_dn_new_fmt(tmp_ctx, cdb->ldb, "cn=%s,%s", dom->name, ++ CONFDB_CERTMAP_BASEDN); ++ if (dn == NULL) { ++ ret = ENOMEM; ++ goto done; ++ } ++ ++ ret = ldb_search(cdb->ldb, tmp_ctx, &res, dn, LDB_SCOPE_ONELEVEL, ++ attrs, NULL); ++ if (ret != LDB_SUCCESS) { ++ ret = EIO; ++ goto done; ++ } ++ ++ certmap_list = talloc_zero_array(tmp_ctx, struct certmap_info *, ++ res->count + 1); ++ if (certmap_list == NULL) { ++ ret = ENOMEM; ++ goto done; ++ } ++ ++ for (c = 0; c < res->count; c++) { ++ if (is_files_provider(dom)) { ++ ret = certmap_local_check(res->msgs[c]); ++ if (ret != EOK) { ++ DEBUG(SSSDBG_CONF_SETTINGS, ++ "Invalid certificate mapping [%s] for local user, " ++ "ignored.\n", ldb_dn_get_linearized(res->msgs[c]->dn)); ++ continue; ++ } ++ } ++ ret = sysdb_ldb_msg_attr_to_certmap_info(certmap_list, res->msgs[c], ++ attrs, &certmap_list[c]); ++ if (ret != EOK) { ++ DEBUG(SSSDBG_OP_FAILURE, ++ "sysdb_ldb_msg_attr_to_certmap_info failed.\n"); ++ goto done; ++ } ++ } ++ ++ *_certmap_list = talloc_steal(mem_ctx, certmap_list); ++ ++ ret = EOK; ++ ++done: ++ talloc_free(tmp_ctx); ++ return ret; ++} ++ ++int confdb_certmap_to_sysdb(struct confdb_ctx *cdb, ++ struct sss_domain_info *dom) ++{ ++ int ret; ++ TALLOC_CTX *tmp_ctx; ++ struct certmap_info **certmap_list; ++ ++ tmp_ctx = talloc_new(NULL); ++ if (tmp_ctx == NULL) { ++ DEBUG(SSSDBG_OP_FAILURE, "talloc_new failed.\n"); ++ return ENOMEM; ++ } ++ ++ ret = confdb_get_all_certmaps(tmp_ctx, cdb, dom, &certmap_list); ++ if (ret != EOK) { ++ DEBUG(SSSDBG_OP_FAILURE, "confdb_get_all_certmaps failed.\n"); ++ goto done; ++ } ++ ++ ret = sysdb_update_certmap(dom->sysdb, certmap_list, false /* TODO */); ++ if (ret != EOK) { ++ DEBUG(SSSDBG_OP_FAILURE, "sysdb_update_certmap failed.\n"); ++ goto done; ++ } ++ ++ ret = EOK; ++ ++done: ++ talloc_free(tmp_ctx); ++ ++ return ret; ++} +diff --git a/src/confdb/confdb.h b/src/confdb/confdb.h +index d3e71be86..b0d52ba49 100644 +--- a/src/confdb/confdb.h ++++ b/src/confdb/confdb.h +@@ -267,6 +267,14 @@ + #define CONFDB_KCM_SOCKET "socket_path" + #define CONFDB_KCM_DB "ccache_storage" /* Undocumented on purpose */ + ++/* Certificate mapping rules */ ++#define CONFDB_CERTMAP_BASEDN "cn=certmap,cn=config" ++#define CONFDB_CERTMAP_NAME "cn" ++#define CONFDB_CERTMAP_MAPRULE "maprule" ++#define CONFDB_CERTMAP_MATCHRULE "matchrule" ++#define CONFDB_CERTMAP_DOMAINS "domains" ++#define CONFDB_CERTMAP_PRIORITY "priority" ++ + /* Prompting */ + #define CONFDB_PC_CONF_ENTRY "config/prompting" + #define CONFDB_PC_TYPE_PASSWORD "password" +@@ -681,6 +689,21 @@ int confdb_get_sub_sections(TALLOC_CTX *mem_ctx, + const char *section, + char ***sections, + int *num_sections); ++ ++/** ++ * @brief Convenience function to write the certificate mapping and matching ++ * rules from the configuration database to the cache of a domain ++ * ++ * @param[in] cdb The connection object to the confdb ++ * @param[in] dom Target domain where to rules should be written to ++ * ++ * @return 0 - Successfully retrieved the entry (or used the default) ++ * @return ENOMEM - There was insufficient memory to complete the operation ++ * @return EINVAL - Typically internal processing error ++ */ ++int confdb_certmap_to_sysdb(struct confdb_ctx *cdb, ++ struct sss_domain_info *dom); ++ + /** + * @} + */ +diff --git a/src/config/cfg_rules.ini b/src/config/cfg_rules.ini +index 997ba5aec..79e366875 100644 +--- a/src/config/cfg_rules.ini ++++ b/src/config/cfg_rules.ini +@@ -17,6 +17,7 @@ section_re = ^secrets/kcm$ + section_re = ^domain/[^/\@]\+$ + section_re = ^domain/[^/\@]\+/[^/\@]\+$ + section_re = ^application/[^/\@]\+$ ++section_re = ^certmap/[^/\@]\+/[^/\@]\+$ + + + [rule/allowed_sssd_options] +@@ -765,3 +766,12 @@ option = use_fully_qualified_names + + [rule/sssd_checks] + validator = sssd_checks ++ ++[rule/allowed_certmap_options] ++validator = ini_allowed_options ++section_re = ^certmap/[^/\@]\+/[^/\@]\+$ ++ ++option = matchrule ++option = maprule ++option = priority ++option = domains +diff --git a/src/db/sysdb.h b/src/db/sysdb.h +index 018ec22ab..a2bc8ed3b 100644 +--- a/src/db/sysdb.h ++++ b/src/db/sysdb.h +@@ -737,6 +737,11 @@ errno_t sysdb_update_certmap(struct sysdb_ctx *sysdb, + struct certmap_info **certmaps, + bool user_name_hint); + ++errno_t sysdb_ldb_msg_attr_to_certmap_info(TALLOC_CTX *mem_ctx, ++ struct ldb_message *msg, ++ const char **attr_map, ++ struct certmap_info **certmap); ++ + errno_t sysdb_get_certmap(TALLOC_CTX *mem_ctx, struct sysdb_ctx *sysdb, + struct certmap_info ***certmaps, + bool *user_name_hint); +diff --git a/src/db/sysdb_certmap.c b/src/db/sysdb_certmap.c +index eda20f5a7..4cc5b5b41 100644 +--- a/src/db/sysdb_certmap.c ++++ b/src/db/sysdb_certmap.c +@@ -22,6 +22,7 @@ + + #include "util/util.h" + #include "db/sysdb_private.h" ++#include "lib/certmap/sss_certmap.h" + + static errno_t sysdb_create_certmap_container(struct sysdb_ctx *sysdb, + bool user_name_hint) +@@ -153,7 +154,7 @@ static errno_t sysdb_certmap_add(struct sysdb_ctx *sysdb, + } + } + +- if (certmap->domains != NULL) { ++ if (certmap->domains != NULL && certmap->domains[0] != NULL) { + for (c = 0; certmap->domains[c] != NULL; c++); + el = talloc_zero(tmp_ctx, struct ldb_message_element); + if (el == NULL) { +@@ -285,28 +286,151 @@ done: + return ret; + } + ++enum certmap_info_member { ++ SSS_CMIM_NAME = 0, ++ SSS_CMIM_MAPPING_RULE, ++ SSS_CMIM_MATCHING_RULE, ++ SSS_CMIM_PRIORITY, ++ SSS_CMIM_DOMAINS, ++ ++ SSS_CMIM_SENTINEL ++}; ++ ++errno_t sysdb_ldb_msg_attr_to_certmap_info(TALLOC_CTX *mem_ctx, ++ struct ldb_message *msg, ++ const char **attr_map, ++ struct certmap_info **certmap) ++{ ++ int ret; ++ size_t d; ++ size_t num_values; ++ struct certmap_info *map = NULL; ++ const char *tmp_str; ++ uint64_t tmp_uint; ++ struct ldb_message_element *tmp_el; ++ ++ if (msg == NULL || attr_map == NULL || certmap == NULL) { ++ DEBUG(SSSDBG_CRIT_FAILURE, "Invalid input.\n"); ++ return EINVAL; ++ } ++ ++ for (d = 0; d < SSS_CMIM_SENTINEL; d++) { ++ if (attr_map[d] == NULL) { ++ DEBUG(SSSDBG_CRIT_FAILURE, "Invalid attribute map"); ++ return EINVAL; ++ } ++ } ++ ++ map = talloc_zero(mem_ctx, struct certmap_info); ++ if (map == NULL) { ++ return ENOMEM; ++ } ++ ++ tmp_str = ldb_msg_find_attr_as_string(msg, attr_map[SSS_CMIM_NAME], NULL); ++ if (tmp_str == NULL) { ++ DEBUG(SSSDBG_MINOR_FAILURE, "The object [%s] doesn't have a name.\n", ++ ldb_dn_get_linearized(msg->dn)); ++ ret = EINVAL; ++ goto done; ++ } ++ ++ map->name = talloc_strdup(map, tmp_str); ++ if (map->name == NULL) { ++ ret = ENOMEM; ++ goto done; ++ } ++ ++ tmp_str = ldb_msg_find_attr_as_string(msg, attr_map[SSS_CMIM_MAPPING_RULE], ++ NULL); ++ if (tmp_str != NULL) { ++ map->map_rule = talloc_strdup(map, tmp_str); ++ if (map->map_rule == NULL) { ++ DEBUG(SSSDBG_OP_FAILURE, "talloc_strdup failed.\n"); ++ ret = ENOMEM; ++ goto done; ++ } ++ } ++ ++ tmp_str = ldb_msg_find_attr_as_string(msg, attr_map[SSS_CMIM_MATCHING_RULE], ++ NULL); ++ if (tmp_str != NULL) { ++ map->match_rule = talloc_strdup(map, tmp_str); ++ if (map->match_rule == NULL) { ++ DEBUG(SSSDBG_OP_FAILURE, "talloc_strdup failed.\n"); ++ ret = ENOMEM; ++ goto done; ++ } ++ } ++ ++ tmp_uint = ldb_msg_find_attr_as_uint64(msg, attr_map[SSS_CMIM_PRIORITY], ++ (uint64_t) -1); ++ if (tmp_uint != (uint64_t) -1) { ++ if (tmp_uint > UINT32_MAX) { ++ DEBUG(SSSDBG_OP_FAILURE, "Priority value [%lu] too large.\n", ++ (unsigned long) tmp_uint); ++ ret = EINVAL; ++ goto done; ++ } ++ ++ map->priority = (uint32_t) tmp_uint; ++ } else { ++ map->priority = SSS_CERTMAP_MIN_PRIO; ++ } ++ ++ tmp_el = ldb_msg_find_element(msg, attr_map[SSS_CMIM_DOMAINS]); ++ if (tmp_el != NULL) { ++ num_values = tmp_el->num_values; ++ } else { ++ num_values = 0; ++ } ++ ++ map->domains = talloc_zero_array(map, const char *, num_values + 1); ++ if (map->domains == NULL) { ++ DEBUG(SSSDBG_OP_FAILURE, "talloc_zero_array failed.\n"); ++ ret = ENOMEM; ++ goto done; ++ } ++ ++ for (d = 0; d < num_values; d++) { ++ map->domains[d] = talloc_strndup(map->domains, ++ (char *) tmp_el->values[d].data, ++ tmp_el->values[d].length); ++ if (map->domains[d] == NULL) { ++ DEBUG(SSSDBG_OP_FAILURE, "talloc_strndup failed.\n"); ++ ret = ENOMEM; ++ goto done; ++ } ++ } ++ ++ *certmap = map; ++ ++ ret = EOK; ++ ++done: ++ if (ret != EOK) { ++ talloc_free(map); ++ } ++ ++ return ret; ++} ++ + errno_t sysdb_get_certmap(TALLOC_CTX *mem_ctx, struct sysdb_ctx *sysdb, + struct certmap_info ***certmaps, bool *user_name_hint) + { + size_t c; +- size_t d; + struct ldb_dn *container_dn = NULL; + int ret; + struct certmap_info **maps = NULL; + TALLOC_CTX *tmp_ctx = NULL; + struct ldb_result *res; +- const char *tmp_str; +- uint64_t tmp_uint; +- struct ldb_message_element *tmp_el; + const char *attrs[] = {SYSDB_NAME, +- SYSDB_CERTMAP_PRIORITY, +- SYSDB_CERTMAP_MATCHING_RULE, + SYSDB_CERTMAP_MAPPING_RULE, ++ SYSDB_CERTMAP_MATCHING_RULE, ++ SYSDB_CERTMAP_PRIORITY, + SYSDB_CERTMAP_DOMAINS, + NULL}; + const char *config_attrs[] = {SYSDB_CERTMAP_USER_NAME_HINT, + NULL}; +- size_t num_values; + bool hint = false; + + tmp_ctx = talloc_new(NULL); +@@ -355,86 +479,13 @@ errno_t sysdb_get_certmap(TALLOC_CTX *mem_ctx, struct sysdb_ctx *sysdb, + } + + for (c = 0; c < res->count; c++) { +- maps[c] = talloc_zero(maps, struct certmap_info); +- if (maps[c] == NULL) { +- ret = ENOMEM; +- goto done; +- } +- tmp_str = ldb_msg_find_attr_as_string(res->msgs[c], SYSDB_NAME, NULL); +- if (tmp_str == NULL) { +- DEBUG(SSSDBG_MINOR_FAILURE, "The object [%s] doesn't have a name.\n", +- ldb_dn_get_linearized(res->msgs[c]->dn)); +- ret = EINVAL; +- goto done; +- } +- +- maps[c]->name = talloc_strdup(maps, tmp_str); +- if (maps[c]->name == NULL) { +- ret = ENOMEM; +- goto done; +- } +- +- tmp_str = ldb_msg_find_attr_as_string(res->msgs[c], +- SYSDB_CERTMAP_MAPPING_RULE, NULL); +- if (tmp_str != NULL) { +- maps[c]->map_rule = talloc_strdup(maps, tmp_str); +- if (maps[c]->map_rule == NULL) { +- DEBUG(SSSDBG_OP_FAILURE, "talloc_strdup failed.\n"); +- ret = ENOMEM; +- goto done; +- } +- } +- +- tmp_str = ldb_msg_find_attr_as_string(res->msgs[c], +- SYSDB_CERTMAP_MATCHING_RULE, NULL); +- if (tmp_str != NULL) { +- maps[c]->match_rule = talloc_strdup(maps, tmp_str); +- if (maps[c]->match_rule == NULL) { +- DEBUG(SSSDBG_OP_FAILURE, "talloc_strdup failed.\n"); +- ret = ENOMEM; +- goto done; +- } +- } +- +- tmp_uint = ldb_msg_find_attr_as_uint64(res->msgs[c], +- SYSDB_CERTMAP_PRIORITY, +- (uint64_t) -1); +- if (tmp_uint != (uint64_t) -1) { +- if (tmp_uint > UINT32_MAX) { +- DEBUG(SSSDBG_OP_FAILURE, "Priority value [%lu] too large.\n", +- (unsigned long) tmp_uint); +- ret = EINVAL; +- goto done; +- } +- +- maps[c]->priority = (uint32_t) tmp_uint; +- } +- +- tmp_el = ldb_msg_find_element(res->msgs[c], SYSDB_CERTMAP_DOMAINS); +- if (tmp_el != NULL) { +- num_values = tmp_el->num_values; +- } else { +- num_values = 0; +- } +- +- maps[c]->domains = talloc_zero_array(maps[c], const char *, +- num_values + 1); +- if (maps[c]->domains == NULL) { +- DEBUG(SSSDBG_OP_FAILURE, "talloc_zero_array failed.\n"); +- ret = ENOMEM; ++ ret = sysdb_ldb_msg_attr_to_certmap_info(maps, res->msgs[c], attrs, ++ &maps[c]); ++ if (ret != EOK) { ++ DEBUG(SSSDBG_OP_FAILURE, ++ "sysdb_ldb_msg_attr_to_certmap_info failed.\n"); + goto done; + } +- +- for (d = 0; d < num_values; d++) { +- maps[c]->domains[d] = talloc_strndup(maps[c]->domains, +- (char *) tmp_el->values[d].data, +- tmp_el->values[d].length); +- if (maps[c]->domains[d] == NULL) { +- DEBUG(SSSDBG_OP_FAILURE, "talloc_strndup failed.\n"); +- ret = ENOMEM; +- goto done; +- } +- } + } + + ret = EOK; +diff --git a/src/external/cwrap.m4 b/src/external/cwrap.m4 +index b8489cc76..6e3487c13 100644 +--- a/src/external/cwrap.m4 ++++ b/src/external/cwrap.m4 +@@ -28,3 +28,8 @@ AC_DEFUN([AM_CHECK_NSS_WRAPPER], + [ + AM_CHECK_WRAPPER(nss_wrapper, HAVE_NSS_WRAPPER) + ]) ++ ++AC_DEFUN([AM_CHECK_PAM_WRAPPER], ++[ ++ AM_CHECK_WRAPPER(pam_wrapper, HAVE_PAM_WRAPPER) ++]) +diff --git a/src/external/intgcheck.m4 b/src/external/intgcheck.m4 +index ef81ec718..d73c5dfb1 100644 +--- a/src/external/intgcheck.m4 ++++ b/src/external/intgcheck.m4 +@@ -75,6 +75,7 @@ AC_DEFUN([SSS_ENABLE_INTGCHECK_REQS], [ + if test x"$enable_intgcheck_reqs" = xyes; then + SSS_INTGCHECK_REQ([HAVE_UID_WRAPPER], [uid_wrapper]) + SSS_INTGCHECK_REQ([HAVE_NSS_WRAPPER], [nss_wrapper]) ++ SSS_INTGCHECK_REQ([HAVE_PAM_WRAPPER], [pam_wrapper]) + SSS_INTGCHECK_REQ([HAVE_SLAPD], [slapd]) + SSS_INTGCHECK_REQ([HAVE_LDAPMODIFY], [ldapmodify]) + SSS_INTGCHECK_REQ([HAVE_FAKEROOT], [fakeroot]) +diff --git a/src/external/test_ca.m4 b/src/external/test_ca.m4 +index 2cdb3c750..bb4872698 100644 +--- a/src/external/test_ca.m4 ++++ b/src/external/test_ca.m4 +@@ -58,7 +58,7 @@ AC_DEFUN([AM_CHECK_TEST_CA], + AC_MSG_NOTICE([Could not find p11tool]) + fi + +- AM_CONDITIONAL([BUILD_TEST_CA], [test -x "$OPENSSL" -a -x "$SSH_KEYGEN" -a -x "$SOFTHSM2_PATH" -a -x "$SOFTHSM2_UTIL" -a -x "$P11TOOL"]) ++ AM_CONDITIONAL([BUILD_TEST_CA], [test -x "$OPENSSL" -a -x "$SSH_KEYGEN" -a -r "$SOFTHSM2_PATH" -a -x "$SOFTHSM2_UTIL" -a -x "$P11TOOL"]) + fi + + AM_COND_IF([BUILD_TEST_CA], +diff --git a/src/lib/certmap/sss_cert_content_crypto.c b/src/lib/certmap/sss_cert_content_crypto.c +index ee9aec208..b01830aec 100644 +--- a/src/lib/certmap/sss_cert_content_crypto.c ++++ b/src/lib/certmap/sss_cert_content_crypto.c +@@ -771,11 +771,25 @@ int sss_cert_get_content(TALLOC_CTX *mem_ctx, + ret = EIO; + goto done; + } +- if (!(X509_get_extension_flags(cert) & EXFLAG_KUSAGE)) { +- ret = EINVAL; +- goto done; ++ if ((X509_get_extension_flags(cert) & EXFLAG_KUSAGE)) { ++ cont->key_usage = X509_get_key_usage(cert); ++ } else { ++ /* According to X.509 https://www.itu.int/rec/T-REC-X.509-201610-I ++ * section 13.3.2 "Certificate match" "keyUsage matches if all of the ++ * bits set in the presented value are also set in the key usage ++ * extension in the stored attribute value, or if there is no key ++ * usage extension in the stored attribute value;". So we set all bits ++ * in our key_usage to make sure everything matches is keyUsage is not ++ * set in the certificate. ++ * ++ * Please note that NSS currently ++ * (https://bugzilla.mozilla.org/show_bug.cgi?id=549952) does not ++ * support 'decipherOnly' and will only use 0xff in this case. To have ++ * a consistent behavior with both libraries we will use UINT32_MAX ++ * for NSS as well. Since comparisons should be always done with a ++ * bit-wise and-operation the difference should not matter. */ ++ cont->key_usage = UINT32_MAX; + } +- cont->key_usage = X509_get_key_usage(cert); + + ret = get_extended_key_usage_oids(cont, cert, + &(cont->extended_key_usage_oids)); +diff --git a/src/lib/certmap/sss_cert_content_nss.c b/src/lib/certmap/sss_cert_content_nss.c +index ed7ce24c4..cef1efc26 100644 +--- a/src/lib/certmap/sss_cert_content_nss.c ++++ b/src/lib/certmap/sss_cert_content_nss.c +@@ -887,8 +887,25 @@ int sss_cert_get_content(TALLOC_CTX *mem_ctx, + goto done; + } + +- +- cont->key_usage = cert->keyUsage; ++ /* According to X.509 https://www.itu.int/rec/T-REC-X.509-201610-I ++ * section 13.3.2 "Certificate match" "keyUsage matches if all of the ++ * bits set in the presented value are also set in the key usage ++ * extension in the stored attribute value, or if there is no key ++ * usage extension in the stored attribute value;". So we set all bits ++ * in our key_usage to make sure everything matches is keyUsage is not ++ * set in the certificate. ++ * ++ * Please note that NSS currently ++ * (https://bugzilla.mozilla.org/show_bug.cgi?id=549952) does not ++ * support 'decipherOnly' and will only use 0xff in this case. To have ++ * a consistent behavior with both libraries we will use UINT32_MAX ++ * for NSS as well. Since comparisons should be always done with a ++ * bit-wise and-operation the difference should not matter. */ ++ if (cert->keyUsagePresent == PR_FALSE) { ++ cont->key_usage = UINT32_MAX; ++ } else { ++ cont->key_usage = cert->keyUsage; ++ } + + ret = get_extended_key_usage_oids(cont, cert, + &(cont->extended_key_usage_oids)); +diff --git a/src/man/sss-certmap.5.xml b/src/man/sss-certmap.5.xml +index db258d14a..10343625e 100644 +--- a/src/man/sss-certmap.5.xml ++++ b/src/man/sss-certmap.5.xml +@@ -92,6 +92,15 @@ + + Example: <SUBJECT>.*,DC=MY,DC=DOMAIN + ++ ++ Please note that the characters "^.[$()|*+?{\" have a ++ special meaning in regular expressions and must be ++ escaped with the help of the '\' character so that they ++ are matched as ordinary characters. ++ ++ ++ Example: <SUBJECT>^CN=.* \(Admin\),DC=MY,DC=DOMAIN$ ++ + + + +diff --git a/src/man/sssd.conf.5.xml b/src/man/sssd.conf.5.xml +index 0e1a97a31..8adbb8de9 100644 +--- a/src/man/sssd.conf.5.xml ++++ b/src/man/sssd.conf.5.xml +@@ -3452,6 +3452,135 @@ ldap_user_extra_attrs = phone:telephoneNumber + + + ++ ++ CERTIFICATE MAPPING SECTION ++ ++ To allow authentication with Smartcards and certificates SSSD must ++ be able to map certificates to users. This can be done by adding the ++ full certificate to the LDAP object of the user or to a local ++ override. While using the full certificate is required to use the ++ Smartcard authentication feature of SSH (see ++ ++ sss_ssh_authorizedkeys ++ 8 ++ ++ for details) it might be cumbersome or not even possible to do this ++ for the general case where local services use PAM for ++ authentication. ++ ++ ++ To make the mapping more flexible mapping and matching rules were ++ added to SSSD (see ++ ++ sss-certmap ++ 5 ++ ++ for details). ++ ++ ++ A mapping and matching rule can be added to the SSSD configuration ++ in a section on its own with a name like ++ [certmap/DOMAIN_NAME/RULE_NAME]. ++ In this section the following options are allowed: ++ ++ ++ ++ matchrule (string) ++ ++ ++ Only certificates from the Smartcard which matches this ++ rule will be processed, all others are ignored. ++ ++ ++ Default: KRB5:<EKU>clientAuth, i.e. only ++ certificates which have the Extended Key Usage ++ clientAuth ++ ++ ++ ++ ++ maprule (string) ++ ++ ++ Defines how the user is found for a given certificate. ++ ++ ++ Default: ++ ++ ++ LDAP:(userCertificate;binary={cert!bin}) ++ for LDAP based providers like ++ ldap, AD or ++ ipa. ++ ++ ++ The RULE_NAME for the files ++ provider which tries to find a user with the ++ same name. ++ ++ ++ ++ ++ ++ ++ domains (string) ++ ++ ++ Comma separated list of domain names the rule should be ++ applied. By default a rule is only valid in the domain ++ configured in sssd.conf. If the provider supports ++ subdomains this option can be used to add the rule to ++ subdomains as well. ++ ++ ++ Default: the configured domain in sssd.conf ++ ++ ++ ++ ++ priority (integer) ++ ++ ++ Unsigned integer value defining the priority of the ++ rule. The higher the number the lower the priority. ++ 0 stands for the highest priority while ++ 4294967295 is the lowest. ++ ++ ++ Default: the lowest priority ++ ++ ++ ++ ++ ++ To make the configuration simple and reduce the amount of ++ configuration options the files provider has some ++ special properties: ++ ++ ++ ++ if maprule is not set the RULE_NAME name is assumed to ++ be the name of the matching user ++ ++ ++ ++ ++ if a maprule is used both a single user name or a ++ template like ++ {subject_rfc822_name.short_name} must ++ be in braces like e.g. (username) or ++ ({subject_rfc822_name.short_name}) ++ ++ ++ ++ ++ the domains option is ignored ++ ++ ++ ++ ++ ++ + + EXAMPLES + +@@ -3494,6 +3623,26 @@ enumerate = False + + [domain/ipa.com/child.ad.com] + use_fully_qualified_names = false ++ ++ ++ ++ 3. The following example shows the configuration for two certificate ++ mapping rules. The first is valid for the configured domain ++ my.domain and additionally for the subdomains ++ your.domain and uses the full certificate in the ++ search filter. The second example is valid for the domain ++ files where it is assumed the files provider is used ++ for this domain and contains a matching rule for the local user ++ myname. ++ ++[certmap/my.domain/rule_name] ++matchrule = <ISSUER>^CN=My-CA,DC=MY,DC=DOMAIN$ ++maprule = (userCertificate;binary={cert!bin}) ++domains = my.domain, your.domain ++priority = 10 ++ ++[certmap/files/myname] ++matchrule = <ISSUER>^CN=My-CA,DC=MY,DC=DOMAIN$<SUBJECT>^CN=User.Name,DC=MY,DC=DOMAIN$ + + + +diff --git a/src/providers/ad/ad_init.c b/src/providers/ad/ad_init.c +index 09397852b..fb24a28e1 100644 +--- a/src/providers/ad/ad_init.c ++++ b/src/providers/ad/ad_init.c +@@ -427,6 +427,22 @@ static errno_t ad_init_misc(struct be_ctx *be_ctx, + return ret; + } + ++ ret = confdb_certmap_to_sysdb(be_ctx->cdb, be_ctx->domain); ++ if (ret != EOK) { ++ DEBUG(SSSDBG_CRIT_FAILURE, ++ "Failed to initialize certificate mapping rules. " ++ "Authentication with certificates/Smartcards might not work " ++ "as expected.\n"); ++ /* not fatal, ignored */ ++ } ++ ++ ret = sdap_init_certmap(sdap_id_ctx, sdap_id_ctx); ++ if (ret != EOK) { ++ DEBUG(SSSDBG_CRIT_FAILURE, ++ "Failed to initialized certificate mapping.\n"); ++ return ret; ++ } ++ + return EOK; + } + +diff --git a/src/providers/files/files_auth.c b/src/providers/files/files_auth.c +new file mode 100644 +index 000000000..b71de6971 +--- /dev/null ++++ b/src/providers/files/files_auth.c +@@ -0,0 +1,69 @@ ++/* ++ SSSD ++ ++ files_auth.c - PAM operations on the files provider ++ ++ Copyright (C) 2018 Red Hat ++ ++ 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 . ++*/ ++ ++#include ++ ++#include "providers/data_provider/dp.h" ++#include "providers/data_provider.h" ++#include "providers/files/files_private.h" ++#include "util/cert.h" ++ ++struct files_auth_ctx { ++ struct pam_data *pd; ++}; ++ ++struct tevent_req * ++files_auth_handler_send(TALLOC_CTX *mem_ctx, ++ void *unused, ++ struct pam_data *pd, ++ struct dp_req_params *params) ++{ ++ struct files_auth_ctx *state; ++ struct tevent_req *req; ++ ++ req = tevent_req_create(mem_ctx, &state, struct files_auth_ctx); ++ if (req == NULL) { ++ DEBUG(SSSDBG_CRIT_FAILURE, "tevent_req_create() failed\n"); ++ return NULL; ++ } ++ ++ state->pd = pd; ++ state->pd->pam_status = PAM_AUTHINFO_UNAVAIL; ++ ++ tevent_req_done(req); ++ tevent_req_post(req, params->ev); ++ return req; ++} ++ ++errno_t files_auth_handler_recv(TALLOC_CTX *mem_ctx, ++ struct tevent_req *req, ++ struct pam_data **_data) ++{ ++ struct files_auth_ctx *state = NULL; ++ ++ state = tevent_req_data(req, struct files_auth_ctx); ++ ++ TEVENT_REQ_RETURN_ON_ERROR(req); ++ ++ *_data = talloc_steal(mem_ctx, state->pd); ++ ++ return EOK; ++} +diff --git a/src/providers/files/files_certmap.c b/src/providers/files/files_certmap.c +new file mode 100644 +index 000000000..7d90a1fec +--- /dev/null ++++ b/src/providers/files/files_certmap.c +@@ -0,0 +1,186 @@ ++/* ++ SSSD ++ ++ files_init.c - Initialization of the files provider ++ ++ Copyright (C) 2018 Red Hat ++ ++ 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 . ++*/ ++ ++#include "providers/files/files_private.h" ++#include "util/util.h" ++#include "util/cert.h" ++#include "lib/certmap/sss_certmap.h" ++ ++struct priv_sss_debug { ++ int level; ++}; ++ ++static void ext_debug(void *private, const char *file, long line, ++ const char *function, const char *format, ...) ++{ ++ va_list ap; ++ struct priv_sss_debug *data = private; ++ int level = SSSDBG_OP_FAILURE; ++ ++ if (data != NULL) { ++ level = data->level; ++ } ++ ++ if (DEBUG_IS_SET(level)) { ++ va_start(ap, format); ++ sss_vdebug_fn(file, line, function, level, APPEND_LINE_FEED, ++ format, ap); ++ va_end(ap); ++ } ++} ++ ++errno_t files_init_certmap(TALLOC_CTX *mem_ctx, struct files_id_ctx *id_ctx) ++{ ++ int ret; ++ bool hint; ++ struct certmap_info **certmap_list = NULL; ++ size_t c; ++ ++ ret = sysdb_get_certmap(mem_ctx, id_ctx->be->domain->sysdb, ++ &certmap_list, &hint); ++ if (ret != EOK) { ++ DEBUG(SSSDBG_OP_FAILURE, "sysdb_get_certmap failed.\n"); ++ goto done; ++ } ++ ++ if (certmap_list == NULL || *certmap_list == NULL) { ++ DEBUG(SSSDBG_TRACE_ALL, "No certmap data, nothing to do.\n"); ++ ret = EOK; ++ goto done; ++ } ++ ++ ret = sss_certmap_init(mem_ctx, ext_debug, NULL, &id_ctx->sss_certmap_ctx); ++ if (ret != EOK) { ++ DEBUG(SSSDBG_OP_FAILURE, "sss_certmap_init failed.\n"); ++ goto done; ++ } ++ ++ for (c = 0; certmap_list[c] != NULL; c++) { ++ DEBUG(SSSDBG_TRACE_ALL, "Trying to add rule [%s][%d][%s][%s].\n", ++ certmap_list[c]->name, ++ certmap_list[c]->priority, ++ certmap_list[c]->match_rule, ++ certmap_list[c]->map_rule); ++ ++ ret = sss_certmap_add_rule(id_ctx->sss_certmap_ctx, ++ certmap_list[c]->priority, ++ certmap_list[c]->match_rule, ++ certmap_list[c]->map_rule, ++ certmap_list[c]->domains); ++ if (ret != 0) { ++ DEBUG(SSSDBG_CRIT_FAILURE, ++ "sss_certmap_add_rule failed for rule [%s] " ++ "with error [%d][%s], skipping. " ++ "Please check for typos and if rule syntax is supported.\n", ++ certmap_list[c]->name, ret, sss_strerror(ret)); ++ continue; ++ } ++ } ++ ++ ret = EOK; ++ ++done: ++ talloc_free(certmap_list); ++ ++ return ret; ++} ++ ++errno_t files_map_cert_to_user(struct files_id_ctx *id_ctx, ++ struct dp_id_data *data) ++{ ++ errno_t ret; ++ char *filter; ++ char *user; ++ struct ldb_message *msg = NULL; ++ struct sysdb_attrs *attrs = NULL; ++ TALLOC_CTX *tmp_ctx; ++ ++ tmp_ctx = talloc_new(NULL); ++ if (tmp_ctx == NULL) { ++ DEBUG(SSSDBG_OP_FAILURE, "talloc_new failed.\n"); ++ return ENOMEM; ++ } ++ ++ ret = sss_cert_derb64_to_ldap_filter(tmp_ctx, data->filter_value, "", ++ id_ctx->sss_certmap_ctx, ++ id_ctx->domain, &filter); ++ if (ret != EOK) { ++ DEBUG(SSSDBG_OP_FAILURE, ++ "sss_cert_derb64_to_ldap_filter failed.\n"); ++ goto done; ++ } ++ if (filter == NULL || filter[0] != '(' ++ || filter[strlen(filter) - 1] != ')') { ++ DEBUG(SSSDBG_OP_FAILURE, ++ "sss_cert_derb64_to_ldap_filter returned bad filter [%s].\n", ++ filter); ++ ret = EINVAL; ++ goto done; ++ } ++ ++ filter[strlen(filter) - 1] = '\0'; ++ user = sss_create_internal_fqname(tmp_ctx, &filter[1], ++ id_ctx->domain->name); ++ if (user == NULL) { ++ DEBUG(SSSDBG_OP_FAILURE, "sss_create_internal_fqname failed.\n"); ++ ret = ENOMEM; ++ goto done; ++ } ++ DEBUG(SSSDBG_TRACE_ALL, "Certificate mapped to user: [%s].\n", user); ++ ++ ret = sysdb_search_user_by_name(tmp_ctx, id_ctx->domain, user, NULL, &msg); ++ if (ret == EOK) { ++ attrs = sysdb_new_attrs(tmp_ctx); ++ if (attrs == NULL) { ++ DEBUG(SSSDBG_OP_FAILURE, "sysdb_new_attrs failed.\n"); ++ ret = ENOMEM; ++ goto done; ++ } ++ ++ ret = sysdb_attrs_add_base64_blob(attrs, SYSDB_USER_MAPPED_CERT, ++ data->filter_value); ++ if (ret != EOK) { ++ DEBUG(SSSDBG_OP_FAILURE, "sysdb_attrs_add_base64_blob failed.\n"); ++ goto done; ++ } ++ ++ ret = sysdb_set_entry_attr(id_ctx->domain->sysdb, msg->dn, attrs, ++ SYSDB_MOD_ADD); ++ if (ret != EOK) { ++ DEBUG(SSSDBG_OP_FAILURE, "sysdb_set_entry_attr failed.\n"); ++ goto done; ++ } ++ } else if (ret == ENOENT) { ++ DEBUG(SSSDBG_TRACE_ALL, "Mapped user [%s] not found.\n", user); ++ ret = EOK; ++ goto done; ++ } else { ++ DEBUG(SSSDBG_OP_FAILURE, "sysdb_search_user_by_name failed.\n"); ++ goto done; ++ } ++ ++ ret = EOK; ++ ++done: ++ talloc_free(tmp_ctx); ++ ++ return ret; ++} +diff --git a/src/providers/files/files_id.c b/src/providers/files/files_id.c +index 41314c66b..f6f8c7311 100644 +--- a/src/providers/files/files_id.c ++++ b/src/providers/files/files_id.c +@@ -87,6 +87,26 @@ files_account_info_handler_send(TALLOC_CTX *mem_ctx, + ? true \ + : false; + break; ++ case BE_REQ_BY_CERT: ++ if (data->filter_type != BE_FILTER_CERT) { ++ DEBUG(SSSDBG_CRIT_FAILURE, ++ "Unexpected filter type for lookup by cert: %d\n", ++ data->filter_type); ++ ret = EINVAL; ++ goto immediate; ++ } ++ if (id_ctx->sss_certmap_ctx == NULL) { ++ DEBUG(SSSDBG_TRACE_ALL, "Certificate mapping not configured.\n"); ++ ret = EOK; ++ goto immediate; ++ } ++ ++ ret = files_map_cert_to_user(id_ctx, data); ++ if (ret != EOK) { ++ DEBUG(SSSDBG_OP_FAILURE, "files_map_cert_to_user failed"); ++ } ++ goto immediate; ++ break; + default: + DEBUG(SSSDBG_CRIT_FAILURE, + "Unexpected entry type: %d\n", data->entry_type & BE_REQ_TYPE_MASK); +diff --git a/src/providers/files/files_init.c b/src/providers/files/files_init.c +index 746c04af1..1ce4bcf27 100644 +--- a/src/providers/files/files_init.c ++++ b/src/providers/files/files_init.c +@@ -189,6 +189,23 @@ int sssm_files_init(TALLOC_CTX *mem_ctx, + goto done; + } + ++ ret = confdb_certmap_to_sysdb(be_ctx->cdb, be_ctx->domain); ++ if (ret != EOK) { ++ DEBUG(SSSDBG_CRIT_FAILURE, ++ "Failed to initialize certificate mapping rules. " ++ "Authentication with certificates/Smartcards might not work " ++ "as expected.\n"); ++ /* not fatal, ignored */ ++ } else { ++ ret = files_init_certmap(ctx, ctx); ++ if (ret != EOK) { ++ DEBUG(SSSDBG_CRIT_FAILURE, "files_init_certmap failed. " ++ "Authentication with certificates/Smartcards might not work " ++ "as expected.\n"); ++ /* not fatal, ignored */ ++ } ++ } ++ + *_module_data = ctx; + ret = EOK; + done: +@@ -224,3 +241,15 @@ int sssm_files_id_init(TALLOC_CTX *mem_ctx, + + return EOK; + } ++ ++int sssm_files_auth_init(TALLOC_CTX *mem_ctx, ++ struct be_ctx *be_ctx, ++ void *module_data, ++ struct dp_method *dp_methods) ++{ ++ dp_set_method(dp_methods, DPM_AUTH_HANDLER, ++ files_auth_handler_send, files_auth_handler_recv, NULL, void, ++ struct pam_data, struct pam_data *); ++ ++ return EOK; ++} +diff --git a/src/providers/files/files_private.h b/src/providers/files/files_private.h +index f44e6d458..fd1781930 100644 +--- a/src/providers/files/files_private.h ++++ b/src/providers/files/files_private.h +@@ -38,6 +38,7 @@ struct files_id_ctx { + struct be_ctx *be; + struct sss_domain_info *domain; + struct files_ctx *fctx; ++ struct sss_certmap_ctx *sss_certmap_ctx; + + const char **passwd_files; + const char **group_files; +@@ -71,4 +72,20 @@ errno_t files_account_info_handler_recv(TALLOC_CTX *mem_ctx, + void files_account_info_finished(struct files_id_ctx *id_ctx, + int req_type, + errno_t ret); ++ ++/* files_auth.c */ ++struct tevent_req *files_auth_handler_send(TALLOC_CTX *mem_ctx, ++ void *unused, ++ struct pam_data *pd, ++ struct dp_req_params *params); ++ ++errno_t files_auth_handler_recv(TALLOC_CTX *mem_ctx, ++ struct tevent_req *req, ++ struct pam_data **_data); ++ ++/* files_certmap.c */ ++errno_t files_init_certmap(TALLOC_CTX *mem_ctx, struct files_id_ctx *id_ctx); ++ ++errno_t files_map_cert_to_user(struct files_id_ctx *id_ctx, ++ struct dp_id_data *data); + #endif /* __FILES_PRIVATE_H_ */ +diff --git a/src/providers/ldap/ldap_init.c b/src/providers/ldap/ldap_init.c +index 3714d5d79..7998cc8ff 100644 +--- a/src/providers/ldap/ldap_init.c ++++ b/src/providers/ldap/ldap_init.c +@@ -439,6 +439,22 @@ static errno_t ldap_init_misc(struct be_ctx *be_ctx, + "[%d]: %s\n", ret, sss_strerror(ret)); + } + ++ ret = confdb_certmap_to_sysdb(be_ctx->cdb, be_ctx->domain); ++ if (ret != EOK) { ++ DEBUG(SSSDBG_CRIT_FAILURE, ++ "Failed to initialize certificate mapping rules. " ++ "Authentication with certificates/Smartcards might not work " ++ "as expected.\n"); ++ /* not fatal, ignored */ ++ } ++ ++ ret = sdap_init_certmap(id_ctx, id_ctx); ++ if (ret != EOK) { ++ DEBUG(SSSDBG_CRIT_FAILURE, ++ "Failed to initialized certificate mapping.\n"); ++ return ret; ++ } ++ + return EOK; + } + +diff --git a/src/responder/common/responder_dp.c b/src/responder/common/responder_dp.c +index 208c415ac..a49b31528 100644 +--- a/src/responder/common/responder_dp.c ++++ b/src/responder/common/responder_dp.c +@@ -598,15 +598,17 @@ static int sss_dp_account_files_params(struct sss_domain_info *dom, + enum sss_dp_acct_type *_type_out, + const char **_opt_name_out) + { +- if (sss_domain_get_state(dom) != DOM_INCONSISTENT) { ++ if (type_in != SSS_DP_CERT) { ++ if (sss_domain_get_state(dom) != DOM_INCONSISTENT) { ++ DEBUG(SSSDBG_TRACE_INTERNAL, ++ "The entries in the files domain are up-to-date\n"); ++ return EOK; ++ } ++ + DEBUG(SSSDBG_TRACE_INTERNAL, +- "The entries in the files domain are up-to-date\n"); +- return EOK; ++ "Domain files is not consistent, issuing update\n"); + } + +- DEBUG(SSSDBG_TRACE_INTERNAL, +- "Domain files is not consistent, issuing update\n"); +- + switch(type_in) { + case SSS_DP_USER: + case SSS_DP_GROUP: +@@ -620,12 +622,16 @@ static int sss_dp_account_files_params(struct sss_domain_info *dom, + *_type_out = type_in; + *_opt_name_out = DP_REQ_OPT_FILES_INITGR; + return EAGAIN; ++ case SSS_DP_CERT: ++ /* Let the backend handle certificate mapping for local users */ ++ *_type_out = type_in; ++ *_opt_name_out = opt_name_in; ++ return EAGAIN; + /* These are not handled by the files provider, just fall back */ + case SSS_DP_NETGR: + case SSS_DP_SERVICES: + case SSS_DP_SECID: + case SSS_DP_USER_AND_GROUP: +- case SSS_DP_CERT: + case SSS_DP_WILDCARD_USER: + case SSS_DP_WILDCARD_GROUP: + return EOK; +diff --git a/src/responder/pam/pamsrv.h b/src/responder/pam/pamsrv.h +index 319362a95..ccb2037b1 100644 +--- a/src/responder/pam/pamsrv.h ++++ b/src/responder/pam/pamsrv.h +@@ -123,7 +123,7 @@ errno_t add_pam_cert_response(struct pam_data *pd, const char *sysdb_username, + bool may_do_cert_auth(struct pam_ctx *pctx, struct pam_data *pd); + + errno_t p11_refresh_certmap_ctx(struct pam_ctx *pctx, +- struct certmap_info **certmap_list); ++ struct sss_domain_info *domains); + + errno_t + pam_set_last_online_auth_with_curr_token(struct sss_domain_info *domain, +diff --git a/src/responder/pam/pamsrv_cmd.c b/src/responder/pam/pamsrv_cmd.c +index 00302be75..139f4260e 100644 +--- a/src/responder/pam/pamsrv_cmd.c ++++ b/src/responder/pam/pamsrv_cmd.c +@@ -1461,7 +1461,9 @@ static void pam_forwarder_cert_cb(struct tevent_req *req) + if (pd->cmd == SSS_PAM_AUTHENTICATE) { + DEBUG(SSSDBG_CRIT_FAILURE, + "No certificate returned, authentication failed.\n"); +- ret = ENOENT; ++ preq->pd->pam_status = PAM_AUTH_ERR; ++ pam_reply(preq); ++ return; + } else { + ret = pam_check_user_search(preq); + } +@@ -1762,7 +1764,7 @@ static void pam_forwarder_cb(struct tevent_req *req) + goto done; + } + +- ret = p11_refresh_certmap_ctx(pctx, pctx->rctx->domains->certmaps); ++ ret = p11_refresh_certmap_ctx(pctx, pctx->rctx->domains); + if (ret != EOK) { + DEBUG(SSSDBG_OP_FAILURE, + "p11_refresh_certmap_ctx failed, " +diff --git a/src/responder/pam/pamsrv_p11.c b/src/responder/pam/pamsrv_p11.c +index c7e57be17..c58a8bedd 100644 +--- a/src/responder/pam/pamsrv_p11.c ++++ b/src/responder/pam/pamsrv_p11.c +@@ -141,11 +141,14 @@ static void ext_debug(void *private, const char *file, long line, + } + + errno_t p11_refresh_certmap_ctx(struct pam_ctx *pctx, +- struct certmap_info **certmap_list) ++ struct sss_domain_info *domains) + { + int ret; + struct sss_certmap_ctx *sss_certmap_ctx = NULL; + size_t c; ++ struct sss_domain_info *dom; ++ bool certmap_found = false; ++ struct certmap_info **certmap_list; + + ret = sss_certmap_init(pctx, ext_debug, NULL, &sss_certmap_ctx); + if (ret != EOK) { +@@ -153,7 +156,15 @@ errno_t p11_refresh_certmap_ctx(struct pam_ctx *pctx, + goto done; + } + +- if (certmap_list == NULL || *certmap_list == NULL) { ++ DLIST_FOR_EACH(dom, domains) { ++ certmap_list = dom->certmaps; ++ if (certmap_list != NULL && *certmap_list != NULL) { ++ certmap_found = true; ++ break; ++ } ++ } ++ ++ if (!certmap_found) { + /* Try to add default matching rule */ + ret = sss_certmap_add_rule(sss_certmap_ctx, SSS_CERTMAP_MIN_PRIO, + CERT_AUTH_DEFAULT_MATCHING_RULE, NULL, NULL); +@@ -165,24 +176,32 @@ errno_t p11_refresh_certmap_ctx(struct pam_ctx *pctx, + goto done; + } + +- for (c = 0; certmap_list[c] != NULL; c++) { +- DEBUG(SSSDBG_TRACE_ALL, +- "Trying to add rule [%s][%d][%s][%s].\n", +- certmap_list[c]->name, certmap_list[c]->priority, +- certmap_list[c]->match_rule, certmap_list[c]->map_rule); +- +- ret = sss_certmap_add_rule(sss_certmap_ctx, certmap_list[c]->priority, +- certmap_list[c]->match_rule, +- certmap_list[c]->map_rule, +- certmap_list[c]->domains); +- if (ret != 0) { +- DEBUG(SSSDBG_CRIT_FAILURE, +- "sss_certmap_add_rule failed for rule [%s] " +- "with error [%d][%s], skipping. " +- "Please check for typos and if rule syntax is supported.\n", +- certmap_list[c]->name, ret, sss_strerror(ret)); ++ DLIST_FOR_EACH(dom, domains) { ++ certmap_list = dom->certmaps; ++ if (certmap_list == NULL || *certmap_list == NULL) { + continue; + } ++ ++ for (c = 0; certmap_list[c] != NULL; c++) { ++ DEBUG(SSSDBG_TRACE_ALL, ++ "Trying to add rule [%s][%d][%s][%s].\n", ++ certmap_list[c]->name, certmap_list[c]->priority, ++ certmap_list[c]->match_rule, certmap_list[c]->map_rule); ++ ++ ret = sss_certmap_add_rule(sss_certmap_ctx, ++ certmap_list[c]->priority, ++ certmap_list[c]->match_rule, ++ certmap_list[c]->map_rule, ++ certmap_list[c]->domains); ++ if (ret != 0) { ++ DEBUG(SSSDBG_CRIT_FAILURE, ++ "sss_certmap_add_rule failed for rule [%s] " ++ "with error [%d][%s], skipping. " ++ "Please check for typos and if rule syntax is supported.\n", ++ certmap_list[c]->name, ret, sss_strerror(ret)); ++ continue; ++ } ++ } + } + + ret = EOK; +@@ -203,19 +222,21 @@ errno_t p11_child_init(struct pam_ctx *pctx) + int ret; + struct certmap_info **certmaps; + bool user_name_hint; +- struct sss_domain_info *dom = pctx->rctx->domains; ++ struct sss_domain_info *dom; + +- ret = sysdb_get_certmap(dom, dom->sysdb, &certmaps, &user_name_hint); +- if (ret != EOK) { +- DEBUG(SSSDBG_OP_FAILURE, "sysdb_get_certmap failed.\n"); +- return ret; +- } ++ DLIST_FOR_EACH(dom, pctx->rctx->domains) { ++ ret = sysdb_get_certmap(dom, dom->sysdb, &certmaps, &user_name_hint); ++ if (ret != EOK) { ++ DEBUG(SSSDBG_OP_FAILURE, "sysdb_get_certmap failed.\n"); ++ return ret; ++ } + +- dom->user_name_hint = user_name_hint; +- talloc_free(dom->certmaps); +- dom->certmaps = certmaps; ++ dom->user_name_hint = user_name_hint; ++ talloc_free(dom->certmaps); ++ dom->certmaps = certmaps; ++ } + +- ret = p11_refresh_certmap_ctx(pctx, dom->certmaps); ++ ret = p11_refresh_certmap_ctx(pctx, pctx->rctx->domains); + if (ret != EOK) { + DEBUG(SSSDBG_OP_FAILURE, "p11_refresh_certmap_ctx failed.\n"); + return ret; +diff --git a/src/tests/cmocka/test_certmap.c b/src/tests/cmocka/test_certmap.c +index 3091e1abe..c882202a0 100644 +--- a/src/tests/cmocka/test_certmap.c ++++ b/src/tests/cmocka/test_certmap.c +@@ -46,8 +46,10 @@ + + #ifdef HAVE_TEST_CA + #include "tests/test_CA/SSSD_test_cert_x509_0003.h" ++#include "tests/test_CA/SSSD_test_cert_x509_0004.h" + #else + #define SSSD_TEST_CERT_0003 "" ++#define SSSD_TEST_CERT_0004 "" + #endif + + struct priv_sss_debug { +@@ -960,6 +962,48 @@ void test_sss_cert_get_content_test_cert_0003(void **state) + talloc_free(content); + } + ++void test_sss_cert_get_content_test_cert_0004(void **state) ++{ ++ int ret; ++ uint8_t *der; ++ size_t der_size; ++ struct sss_cert_content *content; ++ ++ der = sss_base64_decode(NULL, SSSD_TEST_CERT_0004, &der_size); ++ assert_non_null(der); ++ ++ ret = sss_cert_get_content(NULL, der, der_size, &content); ++ assert_int_equal(ret, 0); ++ assert_non_null(content); ++ assert_non_null(content->issuer_str); ++ assert_string_equal(content->issuer_str, ++ "CN=SSSD test CA,OU=SSSD test,O=SSSD"); ++ ++ assert_non_null(content->issuer_rdn_list); ++ assert_string_equal(content->issuer_rdn_list[0], "O=SSSD"); ++ assert_string_equal(content->issuer_rdn_list[1], "OU=SSSD test"); ++ assert_string_equal(content->issuer_rdn_list[2], "CN=SSSD test CA"); ++ assert_null(content->issuer_rdn_list[3]); ++ ++ assert_non_null(content->subject_str); ++ assert_string_equal(content->subject_str, ++ "CN=SSSD test cert 0004,OU=SSSD test,O=SSSD"); ++ ++ assert_non_null(content->subject_rdn_list); ++ assert_string_equal(content->issuer_rdn_list[0], "O=SSSD"); ++ assert_string_equal(content->issuer_rdn_list[1], "OU=SSSD test"); ++ assert_string_equal(content->subject_rdn_list[2], "CN=SSSD test cert 0004"); ++ assert_null(content->subject_rdn_list[3]); ++ ++ assert_int_equal(content->key_usage, UINT32_MAX); ++ ++ assert_non_null(content->extended_key_usage_oids); ++ assert_null(content->extended_key_usage_oids[0]); ++ ++ assert_null(content->san_list); ++ ++ talloc_free(content); ++} + + static void test_sss_certmap_match_cert(void **state) + { +@@ -1572,6 +1616,7 @@ int main(int argc, const char *argv[]) + cmocka_unit_test(test_sss_cert_get_content_2), + #ifdef HAVE_TEST_CA + cmocka_unit_test(test_sss_cert_get_content_test_cert_0003), ++ cmocka_unit_test(test_sss_cert_get_content_test_cert_0004), + #endif + cmocka_unit_test(test_sss_certmap_match_cert), + cmocka_unit_test(test_sss_certmap_add_mapping_rule), +diff --git a/src/tests/cmocka/test_config_check.c b/src/tests/cmocka/test_config_check.c +index a2958de63..f1fdbc8eb 100644 +--- a/src/tests/cmocka/test_config_check.c ++++ b/src/tests/cmocka/test_config_check.c +@@ -213,6 +213,18 @@ void config_check_test_bad_subdom_option_name(void **state) + config_check_test_common(cfg_str, 1, expected_errors); + } + ++void config_check_test_bad_certmap_option_name(void **state) ++{ ++ char cfg_str[] = "[certmap/files/testuser]\n" ++ "debug_level = 10\n"; ++ const char *expected_errors[] = { ++ "[rule/allowed_certmap_options]: Attribute 'debug_level' is not " ++ "allowed in section 'certmap/files/testuser'. Check for typos.", ++ }; ++ ++ config_check_test_common(cfg_str, 1, expected_errors); ++} ++ + void config_check_test_good_sections(void **state) + { + char cfg_str[] = "[sssd]\n" +@@ -225,7 +237,8 @@ void config_check_test_good_sections(void **state) + "[secrets/users/1000]\n" + "[ssh]\n" + "[ifp]\n" +- "[pac]\n"; ++ "[pac]\n" ++ "[certmap/files/testuser]\n"; + const char *expected_errors[] = { NULL }; + + config_check_test_common(cfg_str, 0, expected_errors); +@@ -272,6 +285,7 @@ int main(int argc, const char *argv[]) + cmocka_unit_test(config_check_test_bad_ifp_option_name), + cmocka_unit_test(config_check_test_bad_appdomain_option_name), + cmocka_unit_test(config_check_test_bad_subdom_option_name), ++ cmocka_unit_test(config_check_test_bad_certmap_option_name), + cmocka_unit_test(config_check_test_good_sections), + cmocka_unit_test(config_check_test_inherit_from_in_normal_dom), + cmocka_unit_test(config_check_test_inherit_from_in_app_dom), +diff --git a/src/tests/intg/test_pam_responder.py b/src/tests/intg/test_pam_responder.py +index 7e5828dde..e97bd409b 100644 +--- a/src/tests/intg/test_pam_responder.py ++++ b/src/tests/intg/test_pam_responder.py +@@ -27,12 +27,9 @@ import signal + import errno + import subprocess + import time +-import pytest +- +-import config + import shutil +-from util import unindent + ++import config + import intg.ds_openldap + + import pytest +@@ -109,24 +106,35 @@ def format_basic_conf(ldap_conn): + """).format(**locals()) + + +-def format_pam_cert_auth_conf(): ++USER1 = dict(name='user1', passwd='x', uid=10001, gid=20001, ++ gecos='User for tests', ++ dir='/home/user1', ++ shell='/bin/bash') ++ ++ ++def format_pam_cert_auth_conf(config): + """Format a basic SSSD configuration""" + return unindent("""\ + [sssd] ++ debug_level = 10 + domains = auth_only +- services = pam ++ services = pam, nss + + [nss] ++ debug_level = 10 + + [pam] + pam_cert_auth = True ++ pam_p11_allowed_services = +pam_sss_service ++ pam_cert_db_path = {config.PAM_CERT_DB_PATH} + debug_level = 10 + + [domain/auth_only] +- id_provider = ldap +- auth_provider = ldap +- chpass_provider = ldap +- access_provider = ldap ++ debug_level = 10 ++ id_provider = files ++ ++ [certmap/auth_only/user1] ++ matchrule = .*CN=SSSD test cert 0001.* + """).format(**locals()) + + +@@ -193,12 +201,42 @@ def create_sssd_fixture(request): + request.addfinalizer(cleanup_sssd_process) + + ++def create_nssdb(): ++ os.mkdir(config.SYSCONFDIR + "/pki") ++ os.mkdir(config.SYSCONFDIR + "/pki/nssdb") ++ if subprocess.call(["certutil", "-N", "-d", ++ "sql:" + config.SYSCONFDIR + "/pki/nssdb/", ++ "--empty-password"]) != 0: ++ raise Exception("certutil failed") ++ ++ pkcs11_txt = open(config.SYSCONFDIR + "/pki/nssdb/pkcs11.txt", "w") ++ pkcs11_txt.write("library=libsoftokn3.so\nname=soft\n" + ++ "parameters=configdir='sql:" + config.ABS_BUILDDIR + ++ "/../test_CA/p11_nssdb' " + ++ "dbSlotDescription='SSSD Test Slot' " + ++ "dbTokenDescription='SSSD Test Token' " + ++ "secmod='secmod.db' flags=readOnly)\n\n") ++ pkcs11_txt.close() ++ ++ ++def cleanup_nssdb(): ++ shutil.rmtree(config.SYSCONFDIR + "/pki") ++ ++ ++def create_nssdb_fixture(request): ++ create_nssdb() ++ request.addfinalizer(cleanup_nssdb) ++ ++ + @pytest.fixture +-def simple_pam_cert_auth(request): ++def simple_pam_cert_auth(request, passwd_ops_setup): + """Setup SSSD with pam_cert_auth=True""" +- conf = format_pam_cert_auth_conf() ++ config.PAM_CERT_DB_PATH = os.environ['PAM_CERT_DB_PATH'] ++ conf = format_pam_cert_auth_conf(config) + create_conf_fixture(request, conf) + create_sssd_fixture(request) ++ create_nssdb_fixture(request) ++ passwd_ops_setup.useradd(**USER1) + return None + + +@@ -281,3 +319,50 @@ def env_for_sssctl(request): + env_for_sssctl['LD_PRELOAD'] += ':' + os.environ['PAM_WRAPPER_PATH'] + + return env_for_sssctl ++ ++ ++def test_sc_auth_wrong_pin(simple_pam_cert_auth, env_for_sssctl): ++ ++ sssctl = subprocess.Popen(["sssctl", "user-checks", "user1", ++ "--action=auth", "--service=pam_sss_service"], ++ universal_newlines=True, ++ env=env_for_sssctl, stdin=subprocess.PIPE, ++ stdout=subprocess.PIPE, stderr=subprocess.PIPE) ++ ++ try: ++ out, err = sssctl.communicate(input="111") ++ except: ++ sssctl.kill() ++ out, err = sssctl.communicate() ++ ++ sssctl.stdin.close() ++ sssctl.stdout.close() ++ ++ if sssctl.wait() != 0: ++ raise Exception("sssctl failed") ++ ++ assert err.find("pam_authenticate for user [user1]: " + ++ "Authentication failure") != -1 ++ ++ ++def test_sc_auth(simple_pam_cert_auth, env_for_sssctl): ++ ++ sssctl = subprocess.Popen(["sssctl", "user-checks", "user1", ++ "--action=auth", "--service=pam_sss_service"], ++ universal_newlines=True, ++ env=env_for_sssctl, stdin=subprocess.PIPE, ++ stdout=subprocess.PIPE, stderr=subprocess.PIPE) ++ ++ try: ++ out, err = sssctl.communicate(input="123456") ++ except: ++ sssctl.kill() ++ out, err = sssctl.communicate() ++ ++ sssctl.stdin.close() ++ sssctl.stdout.close() ++ ++ if sssctl.wait() != 0: ++ raise Exception("sssctl failed") ++ ++ assert err.find("pam_authenticate for user [user1]: Success") != -1 +diff --git a/src/tests/test_CA/Makefile.am b/src/tests/test_CA/Makefile.am +index 0c7099390..19772d150 100644 +--- a/src/tests/test_CA/Makefile.am ++++ b/src/tests/test_CA/Makefile.am +@@ -4,9 +4,11 @@ dist_noinst_DATA = \ + SSSD_test_cert_0001.config \ + SSSD_test_cert_0002.config \ + SSSD_test_cert_0003.config \ ++ SSSD_test_cert_0004.config \ + SSSD_test_cert_key_0001.pem \ + SSSD_test_cert_key_0002.pem \ + SSSD_test_cert_key_0003.pem \ ++ SSSD_test_cert_key_0004.pem \ + $(NULL) + + openssl_ca_config = $(srcdir)/SSSD_test_CA.config +@@ -33,14 +35,18 @@ endif + ca_all: clean serial SSSD_test_CA.pem $(certs) $(certs_h) $(pubkeys) $(pubkeys_h) $(pkcs12) $(extra) + + $(pwdfile): +- @echo "12345678" > $@ ++ @echo "123456" > $@ + + SSSD_test_CA.pem: $(openssl_ca_key) $(openssl_ca_config) serial + $(OPENSSL) req -batch -config ${openssl_ca_config} -x509 -new -nodes -key $< -sha256 -days 1024 -set_serial 0 -extensions v3_ca -out $@ + + + SSSD_test_cert_req_%.pem: $(srcdir)/SSSD_test_cert_key_%.pem $(srcdir)/SSSD_test_cert_%.config +- $(OPENSSL) req -new -nodes -key $< -reqexts req_exts -config $(srcdir)/SSSD_test_cert_$*.config -out $@ ++ if [ $(shell grep -c req_exts $(srcdir)/SSSD_test_cert_$*.config) -eq 0 ]; then \ ++ $(OPENSSL) req -new -nodes -key $< -config $(srcdir)/SSSD_test_cert_$*.config -out $@ ; \ ++ else \ ++ $(OPENSSL) req -new -nodes -key $< -reqexts req_exts -config $(srcdir)/SSSD_test_cert_$*.config -out $@ ; \ ++ fi + + SSSD_test_cert_x509_%.pem: SSSD_test_cert_req_%.pem $(openssl_ca_config) SSSD_test_CA.pem + $(OPENSSL) ca -config ${openssl_ca_config} -batch -notext -keyfile $(openssl_ca_key) -in $< -days 200 -extensions usr_cert -out $@ +@@ -65,18 +71,18 @@ SSSD_test_cert_pubsshkey_%.h: SSSD_test_cert_pubsshkey_%.pub + # - src/tests/cmocka/test_pam_srv.c + p11_nssdb: SSSD_test_cert_pkcs12_0001.pem SSSD_test_CA.pem $(pwdfile) + mkdir $@ +- $(CERTUTIL) -d sql:./$@ -N --empty-password +- $(CERTUTIL) -d sql:./$@ -A -n 'SSSD test CA' -t CT,CT,CT -a -i SSSD_test_CA.pem +- $(PK12UTIL) -d sql:./$@ -i SSSD_test_cert_pkcs12_0001.pem -w $(pwdfile) ++ $(CERTUTIL) -d sql:./$@ -N -f $(pwdfile) ++ $(CERTUTIL) -d sql:./$@ -A -n 'SSSD test CA' -t CT,CT,CT -a -i SSSD_test_CA.pem -f $(pwdfile) ++ $(PK12UTIL) -d sql:./$@ -i SSSD_test_cert_pkcs12_0001.pem -w $(pwdfile) -k $(pwdfile) + + # This nss db is used in + # - src/tests/cmocka/test_pam_srv.c + p11_nssdb_2certs: SSSD_test_cert_pkcs12_0001.pem SSSD_test_cert_pkcs12_0002.pem SSSD_test_CA.pem $(pwdfile) + mkdir $@ +- $(CERTUTIL) -d sql:./$@ -N --empty-password +- $(CERTUTIL) -d sql:./$@ -A -n 'SSSD test CA' -t CT,CT,CT -a -i SSSD_test_CA.pem +- $(PK12UTIL) -d sql:./$@ p11_nssdb -i SSSD_test_cert_pkcs12_0001.pem -w $(pwdfile) +- $(PK12UTIL) -d sql:./$@ p11_nssdb -i SSSD_test_cert_pkcs12_0002.pem -w $(pwdfile) ++ $(CERTUTIL) -d sql:./$@ -N -f $(pwdfile) ++ $(CERTUTIL) -d sql:./$@ -A -n 'SSSD test CA' -t CT,CT,CT -a -i SSSD_test_CA.pem -f $(pwdfile) ++ $(PK12UTIL) -d sql:./$@ -i SSSD_test_cert_pkcs12_0001.pem -w $(pwdfile) -k $(pwdfile) ++ $(PK12UTIL) -d sql:./$@ -i SSSD_test_cert_pkcs12_0002.pem -w $(pwdfile) -k $(pwdfile) + + # The softhsm2 PKCS#11 setups are used in + # - src/tests/cmocka/test_pam_srv.c +diff --git a/src/tests/test_CA/SSSD_test_cert_0004.config b/src/tests/test_CA/SSSD_test_cert_0004.config +new file mode 100644 +index 000000000..cb61b43ce +--- /dev/null ++++ b/src/tests/test_CA/SSSD_test_cert_0004.config +@@ -0,0 +1,11 @@ ++# This certificate is used in ++# - test_sss_cert_get_content_test_cert_0004 ++# as an example for a simple certificate without KU, EKU and SAN extensions ++[ req ] ++distinguished_name = req_distinguished_name ++prompt = no ++ ++[ req_distinguished_name ] ++O = SSSD ++OU = SSSD test ++CN = SSSD test cert 0004 +diff --git a/src/tests/test_CA/SSSD_test_cert_key_0004.pem b/src/tests/test_CA/SSSD_test_cert_key_0004.pem +new file mode 100644 +index 000000000..e7e1b1de7 +--- /dev/null ++++ b/src/tests/test_CA/SSSD_test_cert_key_0004.pem +@@ -0,0 +1,28 @@ ++-----BEGIN PRIVATE KEY----- ++MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQCdue/OaH/8xyzm ++PUXFJeVJe0GOLZv3Pv/wPuIlNjAU1JNSDQUUKBlv7SOJr7KZ+4se6RyTU22G0KMN ++0qswY5hlpOtCbRlH2fp5zaYakDVbAv00UBvllPuLetQA9hjCxvz3DZLfLC/N954N ++ZKJIrO2fqTDvJwKqhw7gvp4p1vZcpAcsDf/AYzPgw1oX3yZyzQhfQwQ5Gu3U0Fwc ++nQL/l++mDFD2faDBfn9CFu7hPHKMQvjITsK/RwuFqZa1BzU80x0iyRMhYyDuUDWD ++2tDgzgt4qlMIHJb0ACHuSZFNIiqCF2xkM63PP3is2w7DUpRSu26FR4JacoKq7v5g ++yhtw9y9HAgMBAAECggEAVpNKKy03G4QkhBib5HRBoAz01dr5IkTFbZTGwxA0Yiqw ++1rfo0sCT/djXyerUCSuGmKfyFHgVxYteBOdfKgdxDlHxBJwn5UWj9BnKlAgWEWfZ ++nk5eka0uSchY+FIdE0Twc5dSyAdUEiVZ7xYO8f9hy2KuRodOMlZB92EKJgMlZYGS ++/hYOfZYmz3c4LFWO+UEiXyKKjENtnp5CpOw+Vcrwlu77PbFiT4Y12dOOwDRP4a/a ++ddXQBUaApOMDBA6gpB4jaysq5EBnrLTL5fzHNpATVKFnAL7icPuIfefjU2kxQMoo ++siUL7RzZnLlx0mN37DIzTv4uGltvGzzqhkIA5X/TsQKBgQDNBt3+dih7YbDt/4cC ++HtuApUAbwYDYhETzU8Zt+WRiFZdOgBcm/bacxOqBWGJ1WCYnegPYmk4WVThH1zrF ++Pr2EN2sOn2KJzQlLIOR7hvtTXgLx1hqc9XVBq/8JKhvCPfk/RoCjt+GmkoLHdrWI ++w1kd2milRcFs09UCV8LGa/vYSQKBgQDE8JUXD8x77IP/CBXLBmFq6tSwYkRWh4jC ++l8HB75VWXrknzgjv4Iqx4FEm2T0Mp0QFZLF6WCoclUcsGiCTz7jm20eoRL+5dX1d ++yASi00GSpS1p2Q9eTTU4FHVg1nD1B5F1kQB8uqBj0oSjeQLLPngaIftxTGNrkw3J ++4mk5kVdrDwKBgF0iA3F1pwn05HQYIPHbpoYXirmQ+sBfxRprMbX/FZRgjmzATsQN ++eAhagtPinEcFlb9U865O2a3XZEtt/2peB6Spr93ilNZX5yLTfDaIqF3EVL4aLdii ++v3LneGBnWliv4irWEdVM0Bnkb7e/utK3OiIPdn2s5CJVT2tTBk0v/CTRAoGAIe04 ++IeLs3SRfkN25s2IEAkE2JrSnBSkQHEW8cUZuuZRT3VGXJIvQGNiF4mVmKPnfs/Ym ++xObPSmFFA4n0tsIAHnUEIS7GwJJG6JL+iXZPQ44FBskH5rzyQBj2J5qJlwyYuGIk ++bVhRLSElDGxaWN0IH6hfAqOgNPX+WBsS+YHaR20CgYAVUwTRA9kQgPZJfg+mepFG ++zw9Tx7/TSwILZDlL0AU/i12xn0RA7sweLW8cPEDx1OnTbv+/pqSZ46eeZDzTrlu7 ++ASy844law96NdhpKuTyz/jEl6aj0RLp1wzQZLSQkV0nv3f2Qlknhz83uShhxmxJv ++FqS4fShRFJNoQDwEUvE7ZA== ++-----END PRIVATE KEY----- +-- +2.21.3 + diff --git a/0062-UTIL-find_domain_by_object_name_ex-changed-log-level.patch b/0062-UTIL-find_domain_by_object_name_ex-changed-log-level.patch new file mode 100644 index 0000000..708327b --- /dev/null +++ b/0062-UTIL-find_domain_by_object_name_ex-changed-log-level.patch @@ -0,0 +1,36 @@ +From ba06302d262aa3571620455fafadb43aaa93139e Mon Sep 17 00:00:00 2001 +From: Alexey Tikhonov +Date: Tue, 15 Dec 2020 18:47:25 +0100 +Subject: [PATCH] UTIL: find_domain_by_object_name_ex() changed log level +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +It's up to user of this function to judge if fail to parse fqname is +a critical error. + +Reviewed-by: Pawel Polawski +Reviewed-by: Sumit Bose +(cherry picked from commit bd2f38abe95645b9b16b12d12dac6008b0d2a03b) + +Reviewed-by: Tomáš Halman +--- + src/util/domain_info_utils.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/src/util/domain_info_utils.c b/src/util/domain_info_utils.c +index 71dfcba02..5a374074b 100644 +--- a/src/util/domain_info_utils.c ++++ b/src/util/domain_info_utils.c +@@ -207,7 +207,7 @@ find_domain_by_object_name_ex(struct sss_domain_info *domain, + ret = sss_parse_internal_fqname(tmp_ctx, object_name, + NULL, &domainname); + if (ret != EOK) { +- DEBUG(SSSDBG_CRIT_FAILURE, "Unable to parse name '%s' [%d]: %s\n", ++ DEBUG(SSSDBG_MINOR_FAILURE, "Unable to parse name '%s' [%d]: %s\n", + object_name, ret, sss_strerror(ret)); + goto done; + } +-- +2.26.3 + diff --git a/0063-sudo-do-not-search-by-low-usn-value-to-improve-perfo.patch b/0063-sudo-do-not-search-by-low-usn-value-to-improve-perfo.patch new file mode 100644 index 0000000..cfd972d --- /dev/null +++ b/0063-sudo-do-not-search-by-low-usn-value-to-improve-perfo.patch @@ -0,0 +1,122 @@ +From 73f35e5e6836c3d63cfdc4d85dfbfed99f0bcf5a Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Pavel=20B=C5=99ezina?= +Date: Fri, 29 Jan 2021 12:41:28 +0100 +Subject: [PATCH] sudo: do not search by low usn value to improve performance + +This is a follow up on these two commits. + +- 819d70ef6e6fa0e736ebd60a7f8a26f672927d57 +- 6815844daa7701c76e31addbbdff74656cd30bea + +The first one improved the search filter little bit to achieve better +performance, however it also changed the behavior: we started to search +for `usn >= 1` in the filter if no usn number was known. + +This caused issues on OpenLDAP server which was fixed by the second patch. +However, the fix was wrong and searching by this meaningfully low number +can cause performance issues depending on how the filter is optimized and +evaluated on the server. + +Now we omit the usn attribute from the filter if there is no meaningful value. + +How to test: +1. Setup LDAP with no sudo rules defined +2. Make sure that the LDAP server does not support USN or use the following diff + to enforce modifyTimestamp (last USN is always available from rootDSE) +```diff + +Reviewed-by: Alexey Tikhonov +(cherry picked from commit b100efbfabd96dcfb2825777b75b9a9dfaacb937) +--- + src/providers/ldap/sdap.c | 4 ++-- + src/providers/ldap/sdap_sudo_refresh.c | 6 ++++-- + src/providers/ldap/sdap_sudo_shared.c | 21 ++++++--------------- + 3 files changed, 12 insertions(+), 19 deletions(-) + +diff --git a/src/providers/ldap/sdap.c b/src/providers/ldap/sdap.c +index a1a00df56..0413930bc 100644 +--- a/src/providers/ldap/sdap.c ++++ b/src/providers/ldap/sdap.c +@@ -1322,7 +1322,7 @@ int sdap_get_server_opts_from_rootdse(TALLOC_CTX *memctx, + last_usn_name = opts->gen_map[SDAP_AT_LAST_USN].name; + entry_usn_name = opts->gen_map[SDAP_AT_ENTRY_USN].name; + if (rootdse) { +- if (last_usn_name) { ++ if (false) { + ret = sysdb_attrs_get_string(rootdse, + last_usn_name, &last_usn_value); + if (ret != EOK) { +@@ -1431,7 +1431,7 @@ int sdap_get_server_opts_from_rootdse(TALLOC_CTX *memctx, + } + } + +- if (!last_usn_name) { ++ if (true) { + DEBUG(SSSDBG_FUNC_DATA, + "No known USN scheme is supported by this server!\n"); + if (!entry_usn_name) { +diff --git a/src/providers/ldap/sdap_sudo_refresh.c b/src/providers/ldap/sdap_sudo_refresh.c +index 5c72c6ec5..fd5deeb7a 100644 +--- a/src/providers/ldap/sdap_sudo_refresh.c ++++ b/src/providers/ldap/sdap_sudo_refresh.c +@@ -181,8 +181,10 @@ struct tevent_req *sdap_sudo_smart_refresh_send(TALLOC_CTX *mem_ctx, + state->sysdb = id_ctx->be->domain->sysdb; + + /* Download all rules from LDAP that are newer than usn */ +- if (srv_opts == NULL || srv_opts->max_sudo_value == 0) { +- DEBUG(SSSDBG_TRACE_FUNC, "USN value is unknown, assuming zero.\n"); ++ if (srv_opts == NULL || srv_opts->max_sudo_value == NULL ++ || strcmp(srv_opts->max_sudo_value, "0") == 0) { ++ DEBUG(SSSDBG_TRACE_FUNC, "USN value is unknown, assuming zero and " ++ "omitting it from the filter.\n"); + usn = "0"; + search_filter = talloc_asprintf(state, "(%s=%s)", + map[SDAP_AT_SUDO_OC].name, +diff --git a/src/providers/ldap/sdap_sudo_shared.c b/src/providers/ldap/sdap_sudo_shared.c +index bd3a24da0..5f6afb1ac 100644 +--- a/src/providers/ldap/sdap_sudo_shared.c ++++ b/src/providers/ldap/sdap_sudo_shared.c +@@ -127,25 +127,17 @@ sdap_sudo_ptask_setup_generic(struct be_ctx *be_ctx, + static char * + sdap_sudo_new_usn(TALLOC_CTX *mem_ctx, + unsigned long usn, +- const char *leftover, +- bool supports_usn) ++ const char *leftover) + { + const char *str = leftover == NULL ? "" : leftover; + char *newusn; + +- /* This is a fresh start and server uses modifyTimestamp. We need to +- * provide proper datetime value. */ +- if (!supports_usn && usn == 0) { +- newusn = talloc_strdup(mem_ctx, "00000101000000Z"); +- if (newusn == NULL) { +- DEBUG(SSSDBG_MINOR_FAILURE, "Unable to change USN value (OOM)!\n"); +- return NULL; +- } +- +- return newusn; ++ /* Current largest USN is unknown so we keep "0" to indicate it. */ ++ if (usn == 0) { ++ return talloc_strdup(mem_ctx, "0"); + } + +- /* We increment USN number so that we can later use simplify filter ++ /* We increment USN number so that we can later use simplified filter + * (just usn >= last+1 instead of usn >= last && usn != last). + */ + usn++; +@@ -217,8 +209,7 @@ sdap_sudo_set_usn(struct sdap_server_opts *srv_opts, + srv_opts->last_usn = usn_number; + } + +- newusn = sdap_sudo_new_usn(srv_opts, srv_opts->last_usn, timezone, +- srv_opts->supports_usn); ++ newusn = sdap_sudo_new_usn(srv_opts, srv_opts->last_usn, timezone); + if (newusn == NULL) { + return; + } +-- +2.26.3 + diff --git a/0064-ldap-fix-modifytimestamp-debugging-leftovers.patch b/0064-ldap-fix-modifytimestamp-debugging-leftovers.patch new file mode 100644 index 0000000..e985de6 --- /dev/null +++ b/0064-ldap-fix-modifytimestamp-debugging-leftovers.patch @@ -0,0 +1,36 @@ +From b816fa19ec6be3c5eb183d1caa03f0153538e2ac Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Pavel=20B=C5=99ezina?= +Date: Fri, 12 Feb 2021 15:30:59 +0100 +Subject: [PATCH] ldap: fix modifytimestamp debugging leftovers + +Reviewed-by: Alexey Tikhonov +(cherry picked from commit 75343ff575f05a69750a6482de9abc29d85100bf) +--- + src/providers/ldap/sdap.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/src/providers/ldap/sdap.c b/src/providers/ldap/sdap.c +index 0413930bc..a1a00df56 100644 +--- a/src/providers/ldap/sdap.c ++++ b/src/providers/ldap/sdap.c +@@ -1322,7 +1322,7 @@ int sdap_get_server_opts_from_rootdse(TALLOC_CTX *memctx, + last_usn_name = opts->gen_map[SDAP_AT_LAST_USN].name; + entry_usn_name = opts->gen_map[SDAP_AT_ENTRY_USN].name; + if (rootdse) { +- if (false) { ++ if (last_usn_name) { + ret = sysdb_attrs_get_string(rootdse, + last_usn_name, &last_usn_value); + if (ret != EOK) { +@@ -1431,7 +1431,7 @@ int sdap_get_server_opts_from_rootdse(TALLOC_CTX *memctx, + } + } + +- if (true) { ++ if (!last_usn_name) { + DEBUG(SSSDBG_FUNC_DATA, + "No known USN scheme is supported by this server!\n"); + if (!entry_usn_name) { +-- +2.26.3 + diff --git a/0065-sss_domain_info-add-not_found_counter.patch b/0065-sss_domain_info-add-not_found_counter.patch new file mode 100644 index 0000000..906cf28 --- /dev/null +++ b/0065-sss_domain_info-add-not_found_counter.patch @@ -0,0 +1,67 @@ +From f511e73867f22fff9d0867b07013accb4f44cb1f Mon Sep 17 00:00:00 2001 +From: Sumit Bose +Date: Wed, 14 Apr 2021 17:22:06 +0200 +Subject: [PATCH 65/66] sss_domain_info: add not_found_counter +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +This new counter should be used to track how often a domain could not be +found while discovering the environment so that it can be deleted after +a number of failed attempts. + +Resolves: https://github.com/SSSD/sssd/issues/5528 + +Reviewed-by: Pavel Březina +(cherry picked from commit 5d65411f1aa16af929ae2271ee4d3d9101728a67 with changes) + +Reviewed-by: Pavel Březina +--- + src/confdb/confdb.c | 1 + + src/confdb/confdb.h | 4 ++++ + src/db/sysdb_subdomains.c | 2 ++ + 3 files changed, 7 insertions(+) + +diff --git a/src/confdb/confdb.c b/src/confdb/confdb.c +index 97de6d3b1..a33f352e7 100644 +--- a/src/confdb/confdb.c ++++ b/src/confdb/confdb.c +@@ -1474,6 +1474,7 @@ static int confdb_get_domain_internal(struct confdb_ctx *cdb, + domain->view_name = NULL; + + domain->state = DOM_ACTIVE; ++ domain->not_found_counter = 0; + + *_domain = domain; + ret = EOK; +diff --git a/src/confdb/confdb.h b/src/confdb/confdb.h +index b0d52ba49..a1ac676a6 100644 +--- a/src/confdb/confdb.h ++++ b/src/confdb/confdb.h +@@ -406,6 +406,10 @@ struct sss_domain_info { + /* Do not use the _output_fqnames property directly in new code, but rather + * use sss_domain_info_{get,set}_output_fqnames(). */ + bool output_fqnames; ++ ++ /* Counts how often the domain was not found during a refresh of the ++ * domain list */ ++ size_t not_found_counter; + }; + + /** +diff --git a/src/db/sysdb_subdomains.c b/src/db/sysdb_subdomains.c +index ee3c7f1aa..cbb11342e 100644 +--- a/src/db/sysdb_subdomains.c ++++ b/src/db/sysdb_subdomains.c +@@ -171,6 +171,8 @@ struct sss_domain_info *new_subdomain(TALLOC_CTX *mem_ctx, + dom->homedir_substr = parent->homedir_substr; + dom->override_gid = parent->override_gid; + ++ dom->not_found_counter = 0; ++ + if (parent->sysdb == NULL) { + DEBUG(SSSDBG_OP_FAILURE, "Missing sysdb context in parent domain.\n"); + goto fail; +-- +2.26.3 + diff --git a/0066-AD-read-trusted-domains-from-local-domain-as-well.patch b/0066-AD-read-trusted-domains-from-local-domain-as-well.patch new file mode 100644 index 0000000..c735dc4 --- /dev/null +++ b/0066-AD-read-trusted-domains-from-local-domain-as-well.patch @@ -0,0 +1,244 @@ +From 2519d5ea3757862bebc17d73d74c7e5c57bdc815 Mon Sep 17 00:00:00 2001 +From: Sumit Bose +Date: Tue, 16 Feb 2021 14:30:55 +0100 +Subject: [PATCH 66/66] AD: read trusted domains from local domain as well +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Currently SSSD only uses information stored in a domain controller of +the forest root domain to get the names of other trusted domains in the +forest. Depending on how the forest was created the forest root might +not have LDAP objects for all domains in the forest. It looks like a +typical case are child domains of other domains in the forest. + +As a start SSSD can now include trusted domains stored in the LDAP tree +of a local domain controller as well. In a long run it would make sense +to allow SSSD to explicitly search for domain by looking up DNS entries +and checking a potential domain controller with a CLDAP ping. + +Resolves: https://github.com/SSSD/sssd/issues/5528 + +:feature: Besides trusted domains known by the forest root, trusted + domains known by the local domain are used as well. + +Reviewed-by: Pavel Březina +(cherry picked from commit 95adf488f94f5968f6cfba9e3bef74c07c02ccff) + +Reviewed-by: Pavel Březina +--- + src/providers/ad/ad_subdomains.c | 105 +++++++++++++++++++++++++------ + 1 file changed, 86 insertions(+), 19 deletions(-) + +diff --git a/src/providers/ad/ad_subdomains.c b/src/providers/ad/ad_subdomains.c +index ba4efe975..16aecbc64 100644 +--- a/src/providers/ad/ad_subdomains.c ++++ b/src/providers/ad/ad_subdomains.c +@@ -45,6 +45,7 @@ + #define AD_AT_TRUST_TYPE "trustType" + #define AD_AT_TRUST_PARTNER "trustPartner" + #define AD_AT_TRUST_ATTRS "trustAttributes" ++#define AD_AT_DOMAIN_NAME "cn" + + /* trustType=2 denotes uplevel (NT5 and later) trusted domains. See + * http://msdn.microsoft.com/en-us/library/windows/desktop/ms680342%28v=vs.85%29.aspx +@@ -56,7 +57,6 @@ + */ + #define SLAVE_DOMAIN_FILTER_BASE "(objectclass=trustedDomain)(trustType=2)(!(msDS-TrustForestTrustInfo=*))" + #define SLAVE_DOMAIN_FILTER "(&"SLAVE_DOMAIN_FILTER_BASE")" +-#define FOREST_ROOT_FILTER_FMT "(&"SLAVE_DOMAIN_FILTER_BASE"(cn=%s))" + + /* Attributes of schema objects. See e.g. + * https://docs.microsoft.com/en-us/windows/desktop/AD/characteristics-of-attributes +@@ -646,6 +646,10 @@ done: + return ret; + } + ++/* How many times we keep a domain not found during searches before it will be ++ * removed. */ ++#define MAX_NOT_FOUND 6 ++ + static errno_t ad_subdomains_refresh(struct be_ctx *be_ctx, + struct sdap_idmap_ctx *idmap_ctx, + struct sdap_options *opts, +@@ -706,6 +710,25 @@ static errno_t ad_subdomains_refresh(struct be_ctx *be_ctx, + } + + if (c >= num_subdomains) { ++ DEBUG(SSSDBG_CONF_SETTINGS, "Domain [%s] not in current list.\n", ++ dom->name); ++ /* Since the forest root might not have trustedDomain objects for ++ * each domain in the forest, especially e.g. for child-domains of ++ * child-domains, we cannot reliable say if a domain is still ++ * present or not. ++ * Maybe it would work to check the crossRef objects in ++ * CN=Partitions,CN=Configuration as well to understand if a ++ * domain is still known in the forest or not. ++ * For the time being we use a counter, if a domain was not found ++ * after multiple attempts it will be deleted. */ ++ ++ if (dom->not_found_counter++ < MAX_NOT_FOUND) { ++ DEBUG(SSSDBG_TRACE_ALL, ++ "Domain [%s] was not found [%zu] times.\n", dom->name, ++ dom->not_found_counter); ++ continue; ++ } ++ + /* ok this subdomain does not exist anymore, let's clean up */ + sss_domain_set_state(dom, DOM_DISABLED); + +@@ -743,6 +766,7 @@ static errno_t ad_subdomains_refresh(struct be_ctx *be_ctx, + /* terminate all requests for this subdomain so we can free it */ + dp_terminate_domain_requests(be_ctx->provider, dom->name); + talloc_zfree(sdom); ++ + } else { + /* ok let's try to update it */ + ret = ad_subdom_enumerates(domain, subdomains[c], &enumerate); +@@ -750,6 +774,7 @@ static errno_t ad_subdomains_refresh(struct be_ctx *be_ctx, + goto done; + } + ++ dom->not_found_counter = 0; + ret = ad_subdom_store(be_ctx->cdb, idmap_ctx, domain, + subdomains[c], enumerate); + if (ret) { +@@ -1310,10 +1335,9 @@ ad_get_root_domain_send(TALLOC_CTX *mem_ctx, + struct tevent_req *req; + struct sdap_options *opts; + errno_t ret; +- const char *filter; + const char *attrs[] = { AD_AT_FLATNAME, AD_AT_TRUST_PARTNER, + AD_AT_SID, AD_AT_TRUST_TYPE, +- AD_AT_TRUST_ATTRS, NULL }; ++ AD_AT_TRUST_ATTRS, AD_AT_DOMAIN_NAME, NULL }; + + req = tevent_req_create(mem_ctx, &state, struct ad_get_root_domain_state); + if (req == NULL) { +@@ -1338,15 +1362,10 @@ ad_get_root_domain_send(TALLOC_CTX *mem_ctx, + state->domain = domain; + state->forest = forest; + +- filter = talloc_asprintf(state, FOREST_ROOT_FILTER_FMT, forest); +- if (filter == NULL) { +- ret = ENOMEM; +- goto immediately; +- } +- + subreq = sdap_search_bases_return_first_send(state, ev, opts, sh, + opts->sdom->search_bases, +- NULL, false, 0, filter, attrs, ++ NULL, false, 0, ++ SLAVE_DOMAIN_FILTER, attrs, + NULL); + if (subreq == NULL) { + ret = ENOMEM; +@@ -1368,11 +1387,33 @@ immediately: + return req; + } + ++static struct sysdb_attrs *find_domain(size_t count, struct sysdb_attrs **reply, ++ const char *dom_name) ++{ ++ size_t c; ++ const char *name; ++ int ret; ++ ++ for (c = 0; c < count; c++) { ++ ret = sysdb_attrs_get_string(reply[c], AD_AT_DOMAIN_NAME, &name); ++ if (ret != EOK) { ++ DEBUG(SSSDBG_OP_FAILURE, "Failed to find domain name, skipping"); ++ continue; ++ } ++ if (strcasecmp(name, dom_name) == 0) { ++ return reply[c]; ++ } ++ } ++ ++ return NULL; ++} ++ + static void ad_get_root_domain_done(struct tevent_req *subreq) + { + struct tevent_req *req; + struct ad_get_root_domain_state *state; + errno_t ret; ++ bool has_changes = false; + + req = tevent_req_callback_data(subreq, struct tevent_req); + state = tevent_req_data(req, struct ad_get_root_domain_state); +@@ -1387,7 +1428,37 @@ static void ad_get_root_domain_done(struct tevent_req *subreq) + goto done; + } + +- if (state->reply_count == 0) { ++ find_domain(state->reply_count, state->reply, state->forest); ++ ++ if (state->reply_count == 0 ++ || find_domain(state->reply_count, state->reply, ++ state->forest) == NULL) { ++ ++ if (state->reply_count > 0) { ++ /* refresh the other domains we have found before checking forest ++ * root */ ++ ret = ad_subdomains_refresh(state->be_ctx, state->idmap_ctx, ++ state->opts, ++ state->reply, state->reply_count, false, ++ &state->sd_ctx->last_refreshed, ++ &has_changes); ++ if (ret != EOK) { ++ DEBUG(SSSDBG_OP_FAILURE, ++ "ad_subdomains_refresh failed [%d]: %s\n", ++ ret, sss_strerror(ret)); ++ goto done; ++ } ++ ++ if (has_changes) { ++ ret = ad_subdom_reinit(state->sd_ctx); ++ if (ret != EOK) { ++ DEBUG(SSSDBG_OP_FAILURE, ++ "Could not reinitialize subdomains\n"); ++ goto done; ++ } ++ } ++ } ++ + DEBUG(SSSDBG_OP_FAILURE, + "No information provided for root domain, trying directly.\n"); + subreq = ad_check_domain_send(state, state->ev, state->be_ctx, +@@ -1400,11 +1471,6 @@ static void ad_get_root_domain_done(struct tevent_req *subreq) + } + tevent_req_set_callback(subreq, ad_check_root_domain_done, req); + return; +- } else if (state->reply_count > 1) { +- DEBUG(SSSDBG_CRIT_FAILURE, "Multiple results for root domain search, " +- "domain list might be incomplete!\n"); +- ret = ERR_MALFORMED_ENTRY; +- goto done; + } + + ret = ad_get_root_domain_refresh(state); +@@ -1522,7 +1588,7 @@ ad_get_root_domain_refresh(struct ad_get_root_domain_state *state) + errno_t ret; + + ret = ad_subdomains_refresh(state->be_ctx, state->idmap_ctx, state->opts, +- state->reply, state->reply_count, true, ++ state->reply, state->reply_count, false, + &state->sd_ctx->last_refreshed, + &has_changes); + if (ret != EOK) { +@@ -1539,8 +1605,9 @@ ad_get_root_domain_refresh(struct ad_get_root_domain_state *state) + } + } + +- state->root_domain_attrs = state->reply[0]; +- root_domain = ads_get_root_domain(state->be_ctx, state->reply[0]); ++ state->root_domain_attrs = find_domain(state->reply_count, state->reply, ++ state->forest); ++ root_domain = ads_get_root_domain(state->be_ctx, state->root_domain_attrs); + if (root_domain == NULL) { + DEBUG(SSSDBG_OP_FAILURE, "Could not find the root domain\n"); + ret = EFAULT; +-- +2.26.3 + diff --git a/0067-negcache-use-right-domain-in-nss_protocol_fill_initg.patch b/0067-negcache-use-right-domain-in-nss_protocol_fill_initg.patch new file mode 100644 index 0000000..3a4f404 --- /dev/null +++ b/0067-negcache-use-right-domain-in-nss_protocol_fill_initg.patch @@ -0,0 +1,107 @@ +From 48f27f74c9a9d5aebf8d2be941dfb282578ba9ba Mon Sep 17 00:00:00 2001 +From: Sumit Bose +Date: Fri, 12 Mar 2021 14:38:54 +0100 +Subject: [PATCH] negcache: use right domain in nss_protocol_fill_initgr() + +When checking if a group returned by an initgroups request is filtered +in the negative cache the domain of the user was used. This does not +work reliable if the user can be a member of groups from multiple +domains. + +With this patch th domain the group belongs to is determined and used +while checking the negative cache. + +Resolves: https://github.com/SSSD/sssd/issues/5534 +(cherry picked from commit 231d1118727b989a4af9911a45a465912fe659d6 with changes) + +Reviewed-by: Alexey Tikhonov +--- + src/db/sysdb.c | 22 ++++++++++++++++++++++ + src/db/sysdb.h | 7 +++++++ + src/responder/nss/nss_protocol_grent.c | 8 +++++--- + 3 files changed, 34 insertions(+), 3 deletions(-) + +diff --git a/src/db/sysdb.c b/src/db/sysdb.c +index 279bd5839..f9929c7ba 100644 +--- a/src/db/sysdb.c ++++ b/src/db/sysdb.c +@@ -1978,3 +1978,25 @@ done: + talloc_free(tmp_ctx); + return differs; + } ++ ++struct sss_domain_info *find_domain_by_msg(struct sss_domain_info *dom, ++ struct ldb_message *msg) ++{ ++ const char *name; ++ struct sss_domain_info *obj_dom = NULL; ++ ++ name = ldb_msg_find_attr_as_string(msg, SYSDB_NAME, NULL); ++ if (name == NULL) { ++ DEBUG(SSSDBG_OP_FAILURE, ++ "Object does not have a name attribute.\n"); ++ return dom; ++ } ++ ++ obj_dom = find_domain_by_object_name(get_domains_head(dom), name); ++ if (obj_dom == NULL) { ++ DEBUG(SSSDBG_OP_FAILURE, "No domain found for [%s].\n", name); ++ return dom; ++ } ++ ++ return obj_dom; ++} +diff --git a/src/db/sysdb.h b/src/db/sysdb.h +index 679763bad..d47099eff 100644 +--- a/src/db/sysdb.h ++++ b/src/db/sysdb.h +@@ -1505,4 +1505,11 @@ errno_t sysdb_handle_original_uuid(const char *orig_name, + struct sysdb_attrs *dest_attrs, + const char *dest_name); + ++/* Try to detect the object domain from the object's SYSDB_NAME attribute and ++ * return the matching sss_domain_info. This should work reliable with user ++ * and group objects since fully-qualified names are used here. If the proper ++ * domain cannot be detected the given domain is returned. */ ++struct sss_domain_info *find_domain_by_msg(struct sss_domain_info *dom, ++ struct ldb_message *msg); ++ + #endif /* __SYS_DB_H__ */ +diff --git a/src/responder/nss/nss_protocol_grent.c b/src/responder/nss/nss_protocol_grent.c +index 4c7ea9aed..e4494826a 100644 +--- a/src/responder/nss/nss_protocol_grent.c ++++ b/src/responder/nss/nss_protocol_grent.c +@@ -343,6 +343,7 @@ nss_protocol_fill_initgr(struct nss_ctx *nss_ctx, + struct cache_req_result *result) + { + struct sss_domain_info *domain; ++ struct sss_domain_info *grp_dom; + struct ldb_message *user; + struct ldb_message *msg; + struct ldb_message *primary_group_msg; +@@ -400,10 +401,11 @@ nss_protocol_fill_initgr(struct nss_ctx *nss_ctx, + num_results = 0; + for (i = 1; i < result->count; i++) { + msg = result->msgs[i]; +- gid = sss_view_ldb_msg_find_attr_as_uint64(domain, msg, SYSDB_GIDNUM, ++ grp_dom = find_domain_by_msg(domain, msg); ++ gid = sss_view_ldb_msg_find_attr_as_uint64(grp_dom, msg, SYSDB_GIDNUM, + 0); + posix = ldb_msg_find_attr_as_string(msg, SYSDB_POSIX, NULL); +- grp_name = sss_view_ldb_msg_find_attr_as_string(domain, msg, SYSDB_NAME, ++ grp_name = sss_view_ldb_msg_find_attr_as_string(grp_dom, msg, SYSDB_NAME, + NULL); + + if (gid == 0) { +@@ -417,7 +419,7 @@ nss_protocol_fill_initgr(struct nss_ctx *nss_ctx, + } + } + +- if (is_group_filtered(nss_ctx->rctx->ncache, domain, grp_name, gid)) { ++ if (is_group_filtered(nss_ctx->rctx->ncache, grp_dom, grp_name, gid)) { + continue; + } + +-- +2.26.3 + diff --git a/0068-ldap-retry-ldap_install_tls-when-watchdog-interrupti.patch b/0068-ldap-retry-ldap_install_tls-when-watchdog-interrupti.patch new file mode 100644 index 0000000..9f16683 --- /dev/null +++ b/0068-ldap-retry-ldap_install_tls-when-watchdog-interrupti.patch @@ -0,0 +1,180 @@ +From ee16c609497f29731c5a590821d27d0db0ffc91f Mon Sep 17 00:00:00 2001 +From: Iker Pedrosa +Date: Wed, 3 Mar 2021 15:34:49 +0100 +Subject: [PATCH] ldap: retry ldap_install_tls() when watchdog interruption +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +When the call to ldap_install_tls() fails because the watchdog +interrupted it, retry it. The watchdog interruption is detected by +checking the value of the ticks before and after the call to +ldap_install_tls(). + +Resolves: https://github.com/SSSD/sssd/issues/5531 + +Reviewed-by: Pavel Březina +--- + src/providers/ldap/sdap_async_connection.c | 27 +++++++++++++++++++++- + src/util/sss_ldap.c | 12 ++++++++++ + src/util/util.h | 1 + + src/util/util_errors.c | 3 +++ + src/util/util_errors.h | 3 +++ + src/util/util_watchdog.c | 5 ++++ + 6 files changed, 50 insertions(+), 1 deletion(-) + +diff --git a/src/providers/ldap/sdap_async_connection.c b/src/providers/ldap/sdap_async_connection.c +index afa31ea0f..db963044e 100644 +--- a/src/providers/ldap/sdap_async_connection.c ++++ b/src/providers/ldap/sdap_async_connection.c +@@ -30,6 +30,8 @@ + #include "providers/ldap/sdap_async_private.h" + #include "providers/ldap/ldap_common.h" + ++#define MAX_RETRY_ATTEMPTS 1 ++ + /* ==Connect-to-LDAP-Server=============================================== */ + + struct sdap_rebind_proc_params { +@@ -1447,6 +1449,8 @@ struct sdap_cli_connect_state { + enum connect_tls force_tls; + bool do_auth; + bool use_tls; ++ ++ int retry_attempts; + }; + + static int sdap_cli_resolve_next(struct tevent_req *req); +@@ -1599,16 +1603,37 @@ static void sdap_cli_connect_done(struct tevent_req *subreq) + talloc_zfree(state->sh); + ret = sdap_connect_recv(subreq, state, &state->sh); + talloc_zfree(subreq); +- if (ret) { ++ if (ret == ERR_TLS_HANDSHAKE_INTERRUPTED && ++ state->retry_attempts < MAX_RETRY_ATTEMPTS) { ++ DEBUG(SSSDBG_OP_FAILURE, ++ "TLS handshake was interruped, provider will retry\n"); ++ state->retry_attempts++; ++ subreq = sdap_connect_send(state, state->ev, state->opts, ++ state->service->uri, ++ state->service->sockaddr, ++ state->use_tls); ++ ++ if (!subreq) { ++ tevent_req_error(req, ENOMEM); ++ return; ++ } ++ ++ tevent_req_set_callback(subreq, sdap_cli_connect_done, req); ++ return; ++ } else if (ret != EOK) { ++ state->retry_attempts = 0; + /* retry another server */ + be_fo_set_port_status(state->be, state->service->name, + state->srv, PORT_NOT_WORKING); ++ + ret = sdap_cli_resolve_next(req); + if (ret != EOK) { + tevent_req_error(req, ret); + } ++ + return; + } ++ state->retry_attempts = 0; + + if (state->use_rootdse) { + /* fetch the rootDSE this time */ +diff --git a/src/util/sss_ldap.c b/src/util/sss_ldap.c +index 9d1e95217..652b08ea7 100644 +--- a/src/util/sss_ldap.c ++++ b/src/util/sss_ldap.c +@@ -234,6 +234,8 @@ static void sss_ldap_init_sys_connect_done(struct tevent_req *subreq) + int ret; + int lret; + int optret; ++ int ticks_before_install; ++ int ticks_after_install; + + ret = sssd_async_socket_init_recv(subreq, &state->sd); + talloc_zfree(subreq); +@@ -261,7 +263,9 @@ static void sss_ldap_init_sys_connect_done(struct tevent_req *subreq) + } + + if (ldap_is_ldaps_url(state->uri)) { ++ ticks_before_install = get_watchdog_ticks(); + lret = ldap_install_tls(state->ldap); ++ ticks_after_install = get_watchdog_ticks(); + if (lret != LDAP_SUCCESS) { + if (lret == LDAP_LOCAL_ERROR) { + DEBUG(SSSDBG_FUNC_DATA, "TLS/SSL already in place.\n"); +@@ -283,6 +287,14 @@ static void sss_ldap_init_sys_connect_done(struct tevent_req *subreq) + "Check for certificate issues."); + } + ++ if (ticks_after_install > ticks_before_install) { ++ ret = ERR_TLS_HANDSHAKE_INTERRUPTED; ++ DEBUG(SSSDBG_CRIT_FAILURE, ++ "Assuming %s\n", ++ sss_ldap_err2string(ret)); ++ goto fail; ++ } ++ + ret = EIO; + goto fail; + } +diff --git a/src/util/util.h b/src/util/util.h +index 486394448..94c2e6e3b 100644 +--- a/src/util/util.h ++++ b/src/util/util.h +@@ -737,6 +737,7 @@ int sss_unique_filename(TALLOC_CTX *owner, char *path_tmpl); + /* from util_watchdog.c */ + int setup_watchdog(struct tevent_context *ev, int interval); + void teardown_watchdog(void); ++int get_watchdog_ticks(void); + + /* from files.c */ + int sss_remove_tree(const char *root); +diff --git a/src/util/util_errors.c b/src/util/util_errors.c +index c35a99a54..0eeaa346c 100644 +--- a/src/util/util_errors.c ++++ b/src/util/util_errors.c +@@ -121,6 +121,9 @@ struct err_string error_to_str[] = { + { "The last GetAccountDomain() result is still valid" }, /* ERR_GET_ACCT_DOM_CACHED */ + { "ID is outside the allowed range" }, /* ERR_ID_OUTSIDE_RANGE */ + { "Group ID is duplicated" }, /* ERR_GID_DUPLICATED */ ++ ++ { "TLS handshake was interrupted"}, /* ERR_TLS_HANDSHAKE_INTERRUPTED */ ++ + { "ERR_LAST" } /* ERR_LAST */ + }; + +diff --git a/src/util/util_errors.h b/src/util/util_errors.h +index 470f62f9e..366b75650 100644 +--- a/src/util/util_errors.h ++++ b/src/util/util_errors.h +@@ -143,6 +143,9 @@ enum sssd_errors { + ERR_GET_ACCT_DOM_CACHED, + ERR_ID_OUTSIDE_RANGE, + ERR_GID_DUPLICATED, ++ ++ ERR_TLS_HANDSHAKE_INTERRUPTED, ++ + ERR_LAST /* ALWAYS LAST */ + }; + +diff --git a/src/util/util_watchdog.c b/src/util/util_watchdog.c +index 69160fbdf..7642bfd53 100644 +--- a/src/util/util_watchdog.c ++++ b/src/util/util_watchdog.c +@@ -259,3 +259,8 @@ void teardown_watchdog(void) + /* and kill the watchdog event */ + talloc_free(watchdog_ctx.te); + } ++ ++int get_watchdog_ticks(void) ++{ ++ return __sync_add_and_fetch(&watchdog_ctx.ticks, 0); ++} +-- +2.26.3 + diff --git a/0069-SYSDB-Add-search-index-originalADgidNumber.patch b/0069-SYSDB-Add-search-index-originalADgidNumber.patch new file mode 100644 index 0000000..b975313 --- /dev/null +++ b/0069-SYSDB-Add-search-index-originalADgidNumber.patch @@ -0,0 +1,165 @@ +From c4b1b8208cd26916dc99b29e09df071c5659b9d4 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Pawe=C5=82=20Po=C5=82awski?= +Date: Wed, 7 Jul 2021 00:29:59 +0200 +Subject: [PATCH 69/71] SYSDB: Add search index "originalADgidNumber" +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Commit 03bc962 introduced a change which can result in +unindexed search in some scenarios. The result is performance +drop comparing to older SSSD version. + +This PR adds missing search index: originalADgidNumber + +:relnote: Add search index "originalADgidNumber" to SYSDB + +Resolves: https://github.com/SSSD/sssd/issues/5430 + +Reviewed-by: Iker Pedrosa +Reviewed-by: Tomáš Halman + +(cherry picked with changes from commit 17e339d58c57861c093fc53b241873dce00ae958) + +Reviewed-by: Alexey Tikhonov +--- + src/db/sysdb.h | 2 ++ + src/db/sysdb_init.c | 7 ++++++ + src/db/sysdb_private.h | 5 +++- + src/db/sysdb_upgrade.c | 52 ++++++++++++++++++++++++++++++++++++++++++ + 4 files changed, 65 insertions(+), 1 deletion(-) + +diff --git a/src/db/sysdb.h b/src/db/sysdb.h +index d47099eff..c771ce633 100644 +--- a/src/db/sysdb.h ++++ b/src/db/sysdb.h +@@ -176,6 +176,8 @@ + #define OVERRIDE_PREFIX "override" + #define SYSDB_DEFAULT_OVERRIDE_NAME "defaultOverrideName" + ++#define SYSDB_ORIG_AD_GID_NUMBER "originalADgidNumber" ++ + #define SYSDB_AD_ACCOUNT_EXPIRES "adAccountExpires" + #define SYSDB_AD_USER_ACCOUNT_CONTROL "adUserAccountControl" + +diff --git a/src/db/sysdb_init.c b/src/db/sysdb_init.c +index 48e21baab..3632d5a19 100644 +--- a/src/db/sysdb_init.c ++++ b/src/db/sysdb_init.c +@@ -566,6 +566,13 @@ static errno_t sysdb_domain_cache_upgrade(TALLOC_CTX *mem_ctx, + } + + ++ if (strcmp(version, SYSDB_VERSION_0_21) == 0) { ++ ret = sysdb_upgrade_21(sysdb, &version); ++ if (ret != EOK) { ++ goto done; ++ } ++ } ++ + ret = EOK; + done: + sysdb->ldb = save_ldb; +diff --git a/src/db/sysdb_private.h b/src/db/sysdb_private.h +index 0ccfa43ac..895cc4ea0 100644 +--- a/src/db/sysdb_private.h ++++ b/src/db/sysdb_private.h +@@ -23,6 +23,7 @@ + #ifndef __INT_SYS_DB_H__ + #define __INT_SYS_DB_H__ + ++#define SYSDB_VERSION_0_22 "0.22" + #define SYSDB_VERSION_0_21 "0.21" + #define SYSDB_VERSION_0_20 "0.20" + #define SYSDB_VERSION_0_19 "0.19" +@@ -45,7 +46,7 @@ + #define SYSDB_VERSION_0_2 "0.2" + #define SYSDB_VERSION_0_1 "0.1" + +-#define SYSDB_VERSION SYSDB_VERSION_0_21 ++#define SYSDB_VERSION SYSDB_VERSION_0_22 + + #define SYSDB_BASE_LDIF \ + "dn: @ATTRIBUTES\n" \ +@@ -81,6 +82,7 @@ + "@IDXATTR: mail\n" \ + "@IDXATTR: userMappedCertificate\n" \ + "@IDXATTR: ccacheFile\n" \ ++ "@IDXATTR: originalADgidNumber\n" \ + "\n" \ + "dn: @MODULES\n" \ + "@LIST: asq,memberof\n" \ +@@ -174,6 +176,7 @@ int sysdb_upgrade_17(struct sysdb_ctx *sysdb, + int sysdb_upgrade_18(struct sysdb_ctx *sysdb, const char **ver); + int sysdb_upgrade_19(struct sysdb_ctx *sysdb, const char **ver); + int sysdb_upgrade_20(struct sysdb_ctx *sysdb, const char **ver); ++int sysdb_upgrade_21(struct sysdb_ctx *sysdb, const char **ver); + + int sysdb_ts_upgrade_01(struct sysdb_ctx *sysdb, const char **ver); + +diff --git a/src/db/sysdb_upgrade.c b/src/db/sysdb_upgrade.c +index 392d04b07..6f160f520 100644 +--- a/src/db/sysdb_upgrade.c ++++ b/src/db/sysdb_upgrade.c +@@ -2553,6 +2553,58 @@ done: + return ret; + } + ++int sysdb_upgrade_21(struct sysdb_ctx *sysdb, const char **ver) ++{ ++ struct upgrade_ctx *ctx; ++ errno_t ret; ++ struct ldb_message *msg = NULL; ++ ++ ret = commence_upgrade(sysdb, sysdb->ldb, SYSDB_VERSION_0_22, &ctx); ++ if (ret) { ++ return ret; ++ } ++ ++ /* Add missing indices */ ++ msg = ldb_msg_new(ctx); ++ if (msg == NULL) { ++ ret = ENOMEM; ++ goto done; ++ } ++ ++ msg->dn = ldb_dn_new(msg, sysdb->ldb, "@INDEXLIST"); ++ if (msg->dn == NULL) { ++ ret = ENOMEM; ++ goto done; ++ } ++ ++ ret = ldb_msg_add_empty(msg, "@IDXATTR", LDB_FLAG_MOD_ADD, NULL); ++ if (ret != LDB_SUCCESS) { ++ ret = ENOMEM; ++ goto done; ++ } ++ ++ ret = ldb_msg_add_string(msg, "@IDXATTR", SYSDB_ORIG_AD_GID_NUMBER); ++ if (ret != LDB_SUCCESS) { ++ ret = ENOMEM; ++ goto done; ++ } ++ ++ ret = ldb_modify(sysdb->ldb, msg); ++ if (ret != LDB_SUCCESS) { ++ ret = sysdb_error_to_errno(ret); ++ goto done; ++ } ++ ++ talloc_free(msg); ++ ++ /* conversion done, update version number */ ++ ret = update_version(ctx); ++ ++done: ++ ret = finish_upgrade(ret, &ctx, ver); ++ return ret; ++} ++ + int sysdb_ts_upgrade_01(struct sysdb_ctx *sysdb, const char **ver) + { + struct upgrade_ctx *ctx; +-- +2.26.3 + diff --git a/0070-AD-do-not-override-LDAP-data-during-GC-lookups.patch b/0070-AD-do-not-override-LDAP-data-during-GC-lookups.patch new file mode 100644 index 0000000..0ed8c6a --- /dev/null +++ b/0070-AD-do-not-override-LDAP-data-during-GC-lookups.patch @@ -0,0 +1,66 @@ +From 7afd36a4c4b35d72742eec2d23bd6908e635c097 Mon Sep 17 00:00:00 2001 +From: Sumit Bose +Date: Fri, 19 Jun 2020 13:36:49 +0200 +Subject: [PATCH 70/71] AD: do not override LDAP data during GC lookups +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +The Global Catalog contains user and group information of the whole +forest and hence any Global Catalog server can be used. Currently when a +Global Catalog server is looked up the data of the LDAP server is +overwritten as well. I guess the original intention was to use a single +server for both services. + +However since the Global Catalog server can come from any domain in the +forest this might overwrite the LDAP data of a DC from the local domain +with the data from a AD of a remote domain and as a result lookups for +users and groups from the local domain might fail since the remote DC +does not has this information available at the LDAP port. In most cases +this overwrite is hidden by a following lookup to find a KDC for +authentication which is searched only in the local domain again where +the LDAP data is overwritten again to make sure the same DC is used for +LDAP and Kerberos communication. But depending on the connection +timeouts and lifetime of Kerberos tickets the KDC lookup might be +skipped because new credentials are not needed and as a result the wrong +LDAP data is used. + +To avoid this the LDAP data is now only set if the current lookup is not +a Global Catalog lookup. + +Resolves: https://github.com/SSSD/sssd/issues/5351 + +Reviewed-by: Pavel Březina +(cherry picked from commit 5f3b9e1d45df77bca1b2665e67bbd73b26fafbc2) + +Reviewed-by: Alexey Tikhonov +--- + src/providers/ad/ad_common.c | 12 ++++++++---- + 1 file changed, 8 insertions(+), 4 deletions(-) + +diff --git a/src/providers/ad/ad_common.c b/src/providers/ad/ad_common.c +index 4e51d08e6..c99c4d110 100644 +--- a/src/providers/ad/ad_common.c ++++ b/src/providers/ad/ad_common.c +@@ -942,10 +942,14 @@ ad_resolve_callback(void *private_data, struct fo_server *server) + } + + /* free old one and replace with new one */ +- talloc_zfree(service->sdap->uri); +- service->sdap->uri = new_uri; +- talloc_zfree(service->sdap->sockaddr); +- service->sdap->sockaddr = talloc_steal(service->sdap, sockaddr); ++ if (sdata == NULL || !sdata->gc) { ++ /* do not update LDAP data during GC lookups because the selected server ++ * might be from a different domain. */ ++ talloc_zfree(service->sdap->uri); ++ service->sdap->uri = new_uri; ++ talloc_zfree(service->sdap->sockaddr); ++ service->sdap->sockaddr = talloc_steal(service->sdap, sockaddr); ++ } + + talloc_zfree(service->gc->uri); + talloc_zfree(service->gc->sockaddr); +-- +2.26.3 + diff --git a/0071-simple-fix-memory-leak-while-reloading-lists.patch b/0071-simple-fix-memory-leak-while-reloading-lists.patch new file mode 100644 index 0000000..0e2877d --- /dev/null +++ b/0071-simple-fix-memory-leak-while-reloading-lists.patch @@ -0,0 +1,103 @@ +From 137924c7894fd5989446ebefd96010a0878004f1 Mon Sep 17 00:00:00 2001 +From: Sumit Bose +Date: Tue, 12 Jan 2021 16:40:56 +0100 +Subject: [PATCH 71/71] simple: fix memory leak while reloading lists + +The simple access provider will reload the access and deny lists at +runtime to make sure that users and groups from domains which are +discovered at runtime are properly processed. + +While reloading the lists the original lists are not freed and an +intermediate list wasn't removed as well. + +Resolves: https://github.com/SSSD/sssd/issues/5456 + +:fixes: Memory leak in the simple access provider + +Reviewed-by: Alexey Tikhonov +(cherry picked from commit 19c2c641e669ee1c08d6706c132625dc30e64609) + +Reviewed-by: Alexey Tikhonov +--- + src/providers/simple/simple_access.c | 28 +++++++++++++++++++++------- + 1 file changed, 21 insertions(+), 7 deletions(-) + +diff --git a/src/providers/simple/simple_access.c b/src/providers/simple/simple_access.c +index 1868569b1..49226adf2 100644 +--- a/src/providers/simple/simple_access.c ++++ b/src/providers/simple/simple_access.c +@@ -117,17 +117,13 @@ int simple_access_obtain_filter_lists(struct simple_ctx *ctx) + const char *name; + const char *option; + char **orig_list; +- char ***ctx_list; ++ char **ctx_list; + } lists[] = {{"Allow users", CONFDB_SIMPLE_ALLOW_USERS, NULL, NULL}, + {"Deny users", CONFDB_SIMPLE_DENY_USERS, NULL, NULL}, + {"Allow groups", CONFDB_SIMPLE_ALLOW_GROUPS, NULL, NULL}, + {"Deny groups", CONFDB_SIMPLE_DENY_GROUPS, NULL, NULL}, + {NULL, NULL, NULL, NULL}}; + +- lists[0].ctx_list = &ctx->allow_users; +- lists[1].ctx_list = &ctx->deny_users; +- lists[2].ctx_list = &ctx->allow_groups; +- lists[3].ctx_list = &ctx->deny_groups; + + ret = sysdb_master_domain_update(bectx->domain); + if (ret != EOK) { +@@ -141,7 +137,6 @@ int simple_access_obtain_filter_lists(struct simple_ctx *ctx) + lists[i].option, &lists[i].orig_list); + if (ret == ENOENT) { + DEBUG(SSSDBG_FUNC_DATA, "%s list is empty.\n", lists[i].name); +- *lists[i].ctx_list = NULL; + continue; + } else if (ret != EOK) { + DEBUG(SSSDBG_CRIT_FAILURE, "confdb_get_string_as_list failed.\n"); +@@ -149,7 +144,8 @@ int simple_access_obtain_filter_lists(struct simple_ctx *ctx) + } + + ret = simple_access_parse_names(ctx, bectx, lists[i].orig_list, +- lists[i].ctx_list); ++ &lists[i].ctx_list); ++ talloc_free(lists[i].orig_list); + if (ret != EOK) { + DEBUG(SSSDBG_CRIT_FAILURE, "Unable to parse %s list [%d]: %s\n", + lists[i].name, ret, sss_strerror(ret)); +@@ -157,6 +153,18 @@ int simple_access_obtain_filter_lists(struct simple_ctx *ctx) + } + } + ++ talloc_free(ctx->allow_users); ++ ctx->allow_users = talloc_steal(ctx, lists[0].ctx_list); ++ ++ talloc_free(ctx->deny_users); ++ ctx->deny_users = talloc_steal(ctx, lists[1].ctx_list); ++ ++ talloc_free(ctx->allow_groups); ++ ctx->allow_groups = talloc_steal(ctx, lists[2].ctx_list); ++ ++ talloc_free(ctx->deny_groups); ++ ctx->deny_groups = talloc_steal(ctx, lists[3].ctx_list); ++ + if (!ctx->allow_users && + !ctx->allow_groups && + !ctx->deny_users && +@@ -165,9 +173,15 @@ int simple_access_obtain_filter_lists(struct simple_ctx *ctx) + "No rules supplied for simple access provider. " + "Access will be granted for all users.\n"); + } ++ ++ + return EOK; + + failed: ++ for (i = 0; lists[i].name != NULL; i++) { ++ talloc_free(lists[i].ctx_list); ++ } ++ + return ret; + } + +-- +2.26.3 + diff --git a/0072-TOOLS-replace-system-with-execvp.patch b/0072-TOOLS-replace-system-with-execvp.patch new file mode 100644 index 0000000..a5ae447 --- /dev/null +++ b/0072-TOOLS-replace-system-with-execvp.patch @@ -0,0 +1,277 @@ +From a0d310e2c86facc8727b781e060b2b769313a5dd Mon Sep 17 00:00:00 2001 +From: Alexey Tikhonov +Date: Fri, 30 Jul 2021 19:05:31 +0200 +Subject: [PATCH] TOOLS: replace system() with execvp() to avoid execution of + user supplied command + +A flaw was found in SSSD, where the sssctl command was vulnerable +to shell command injection via the logs-fetch and cache-expire +subcommands. This flaw allows an attacker to trick the root user +into running a specially crafted sssctl command, such as via sudo, +to gain root access. The highest threat from this vulnerability is +to confidentiality, integrity, as well as system availability. + +:fixes: CVE-2021-3621 +--- + src/tools/sssctl/sssctl.c | 40 +++++++++++++++++------- + src/tools/sssctl/sssctl.h | 2 +- + src/tools/sssctl/sssctl_data.c | 57 +++++++++++----------------------- + src/tools/sssctl/sssctl_logs.c | 31 ++++++++++++++---- + 4 files changed, 73 insertions(+), 57 deletions(-) + +diff --git a/src/tools/sssctl/sssctl.c b/src/tools/sssctl/sssctl.c +index 4a50a1d..bfc791b 100644 +--- a/src/tools/sssctl/sssctl.c ++++ b/src/tools/sssctl/sssctl.c +@@ -97,22 +97,37 @@ sssctl_prompt(const char *message, + return SSSCTL_PROMPT_ERROR; + } + +-errno_t sssctl_run_command(const char *command) ++errno_t sssctl_run_command(const char *const argv[]) + { + int ret; ++ int wstatus; + +- DEBUG(SSSDBG_TRACE_FUNC, "Running %s\n", command); ++ DEBUG(SSSDBG_TRACE_FUNC, "Running '%s'\n", argv[0]); + +- ret = system(command); ++ ret = fork(); + if (ret == -1) { +- DEBUG(SSSDBG_CRIT_FAILURE, "Unable to execute %s\n", command); + fprintf(stderr, _("Error while executing external command\n")); + return EFAULT; +- } else if (WEXITSTATUS(ret) != 0) { +- DEBUG(SSSDBG_CRIT_FAILURE, "Command %s failed with [%d]\n", +- command, WEXITSTATUS(ret)); ++ } ++ ++ if (ret == 0) { ++ /* cast is safe - see ++ https://pubs.opengroup.org/onlinepubs/9699919799/functions/exec.html ++ "The statement about argv[] and envp[] being constants ... " ++ */ ++ execvp(argv[0], discard_const_p(char * const, argv)); + fprintf(stderr, _("Error while executing external command\n")); +- return EIO; ++ _exit(1); ++ } else { ++ if (waitpid(ret, &wstatus, 0) == -1) { ++ fprintf(stderr, ++ _("Error while executing external command '%s'\n"), argv[0]); ++ return EFAULT; ++ } else if (WEXITSTATUS(wstatus) != 0) { ++ fprintf(stderr, ++ _("Command '%s' failed with [%d]\n"), argv[0], WEXITSTATUS(wstatus)); ++ return EIO; ++ } + } + + return EOK; +@@ -132,11 +147,14 @@ static errno_t sssctl_manage_service(enum sssctl_svc_action action) + #elif defined(HAVE_SERVICE) + switch (action) { + case SSSCTL_SVC_START: +- return sssctl_run_command(SERVICE_PATH" sssd start"); ++ return sssctl_run_command( ++ (const char *[]){SERVICE_PATH, "sssd", "start", NULL}); + case SSSCTL_SVC_STOP: +- return sssctl_run_command(SERVICE_PATH" sssd stop"); ++ return sssctl_run_command( ++ (const char *[]){SERVICE_PATH, "sssd", "stop", NULL}); + case SSSCTL_SVC_RESTART: +- return sssctl_run_command(SERVICE_PATH" sssd restart"); ++ return sssctl_run_command( ++ (const char *[]){SERVICE_PATH, "sssd", "restart", NULL}); + } + #endif + +diff --git a/src/tools/sssctl/sssctl.h b/src/tools/sssctl/sssctl.h +index 0115b24..599ef65 100644 +--- a/src/tools/sssctl/sssctl.h ++++ b/src/tools/sssctl/sssctl.h +@@ -47,7 +47,7 @@ enum sssctl_prompt_result + sssctl_prompt(const char *message, + enum sssctl_prompt_result defval); + +-errno_t sssctl_run_command(const char *command); ++errno_t sssctl_run_command(const char *const argv[]); /* argv[0] - command */ + bool sssctl_start_sssd(bool force); + bool sssctl_stop_sssd(bool force); + bool sssctl_restart_sssd(bool force); +diff --git a/src/tools/sssctl/sssctl_data.c b/src/tools/sssctl/sssctl_data.c +index cc46caf..8a04266 100644 +--- a/src/tools/sssctl/sssctl_data.c ++++ b/src/tools/sssctl/sssctl_data.c +@@ -105,15 +105,15 @@ static errno_t sssctl_backup(bool force) + } + } + +- ret = sssctl_run_command("sss_override user-export " +- SSS_BACKUP_USER_OVERRIDES); ++ ret = sssctl_run_command((const char *[]){"sss_override", "user-export", ++ SSS_BACKUP_USER_OVERRIDES, NULL}); + if (ret != EOK) { + fprintf(stderr, _("Unable to export user overrides\n")); + return ret; + } + +- ret = sssctl_run_command("sss_override group-export " +- SSS_BACKUP_GROUP_OVERRIDES); ++ ret = sssctl_run_command((const char *[]){"sss_override", "group-export", ++ SSS_BACKUP_GROUP_OVERRIDES, NULL}); + if (ret != EOK) { + fprintf(stderr, _("Unable to export group overrides\n")); + return ret; +@@ -158,8 +158,8 @@ static errno_t sssctl_restore(bool force_start, bool force_restart) + } + + if (sssctl_backup_file_exists(SSS_BACKUP_USER_OVERRIDES)) { +- ret = sssctl_run_command("sss_override user-import " +- SSS_BACKUP_USER_OVERRIDES); ++ ret = sssctl_run_command((const char *[]){"sss_override", "user-import", ++ SSS_BACKUP_USER_OVERRIDES, NULL}); + if (ret != EOK) { + fprintf(stderr, _("Unable to import user overrides\n")); + return ret; +@@ -167,8 +167,8 @@ static errno_t sssctl_restore(bool force_start, bool force_restart) + } + + if (sssctl_backup_file_exists(SSS_BACKUP_USER_OVERRIDES)) { +- ret = sssctl_run_command("sss_override group-import " +- SSS_BACKUP_GROUP_OVERRIDES); ++ ret = sssctl_run_command((const char *[]){"sss_override", "group-import", ++ SSS_BACKUP_GROUP_OVERRIDES, NULL}); + if (ret != EOK) { + fprintf(stderr, _("Unable to import group overrides\n")); + return ret; +@@ -296,40 +296,19 @@ errno_t sssctl_cache_expire(struct sss_cmdline *cmdline, + void *pvt) + { + errno_t ret; +- char *cmd_args = NULL; +- const char *cachecmd = SSS_CACHE; +- char *cmd = NULL; +- int i; +- +- if (cmdline->argc == 0) { +- ret = sssctl_run_command(cachecmd); +- goto done; +- } + +- cmd_args = talloc_strdup(tool_ctx, ""); +- if (cmd_args == NULL) { +- ret = ENOMEM; +- goto done; ++ const char **args = talloc_array_size(tool_ctx, ++ sizeof(char *), ++ cmdline->argc + 2); ++ if (!args) { ++ return ENOMEM; + } ++ memcpy(&args[1], cmdline->argv, sizeof(char *) * cmdline->argc); ++ args[0] = SSS_CACHE; ++ args[cmdline->argc + 1] = NULL; + +- for (i = 0; i < cmdline->argc; i++) { +- cmd_args = talloc_strdup_append(cmd_args, cmdline->argv[i]); +- if (i != cmdline->argc - 1) { +- cmd_args = talloc_strdup_append(cmd_args, " "); +- } +- } +- +- cmd = talloc_asprintf(tool_ctx, "%s %s", cachecmd, cmd_args); +- if (cmd == NULL) { +- ret = ENOMEM; +- goto done; +- } +- +- ret = sssctl_run_command(cmd); +- +-done: +- talloc_free(cmd_args); +- talloc_free(cmd); ++ ret = sssctl_run_command(args); + ++ talloc_free(args); + return ret; + } +diff --git a/src/tools/sssctl/sssctl_logs.c b/src/tools/sssctl/sssctl_logs.c +index aca988c..c85cc7a 100644 +--- a/src/tools/sssctl/sssctl_logs.c ++++ b/src/tools/sssctl/sssctl_logs.c +@@ -32,6 +32,7 @@ + #include + #include + #include ++#include + + #include "util/util.h" + #include "tools/common/sss_process.h" +@@ -231,6 +232,7 @@ errno_t sssctl_logs_remove(struct sss_cmdline *cmdline, + { + struct sssctl_logs_opts opts = {0}; + errno_t ret; ++ glob_t globbuf; + + /* Parse command line. */ + struct poptOption options[] = { +@@ -254,8 +256,19 @@ errno_t sssctl_logs_remove(struct sss_cmdline *cmdline, + + sss_signal(SIGHUP); + } else { ++ globbuf.gl_offs = 4; ++ ret = glob(LOG_PATH"/*.log", GLOB_ERR|GLOB_DOOFFS, NULL, &globbuf); ++ if (ret != 0) { ++ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to expand log files list\n"); ++ return ret; ++ } ++ globbuf.gl_pathv[0] = discard_const_p(char, "truncate"); ++ globbuf.gl_pathv[2] = discard_const_p(char, "--size"); ++ globbuf.gl_pathv[3] = discard_const_p(char, "0"); ++ + printf(_("Truncating log files...\n")); +- ret = sssctl_run_command("truncate --size 0 " LOG_FILES); ++ ret = sssctl_run_command((const char * const*)globbuf.gl_pathv); ++ globfree(&globbuf); + if (ret != EOK) { + fprintf(stderr, _("Unable to truncate log files\n")); + return ret; +@@ -270,8 +283,8 @@ errno_t sssctl_logs_fetch(struct sss_cmdline *cmdline, + void *pvt) + { + const char *file; +- const char *cmd; + errno_t ret; ++ glob_t globbuf; + + /* Parse command line. */ + ret = sss_tool_popt_ex(cmdline, NULL, SSS_TOOL_OPT_OPTIONAL, NULL, NULL, +@@ -281,13 +294,19 @@ errno_t sssctl_logs_fetch(struct sss_cmdline *cmdline, + return ret; + } + +- cmd = talloc_asprintf(tool_ctx, "tar -czf %s %s", file, LOG_FILES); +- if (cmd == NULL) { +- fprintf(stderr, _("Out of memory!")); ++ globbuf.gl_offs = 3; ++ ret = glob(LOG_PATH"/*.log", GLOB_ERR|GLOB_DOOFFS, NULL, &globbuf); ++ if (ret != 0) { ++ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to expand log files list\n"); ++ return ret; + } ++ globbuf.gl_pathv[0] = discard_const_p(char, "tar"); ++ globbuf.gl_pathv[1] = discard_const_p(char, "-czf"); ++ globbuf.gl_pathv[2] = discard_const_p(char, file); + + printf(_("Archiving log files into %s...\n"), file); +- ret = sssctl_run_command(cmd); ++ ret = sssctl_run_command((const char * const*)globbuf.gl_pathv); ++ globfree(&globbuf); + if (ret != EOK) { + fprintf(stderr, _("Unable to archive log files\n")); + return ret; +-- +2.26.3 + diff --git a/0073-cldap.patch b/0073-cldap.patch new file mode 100644 index 0000000..18ba08f --- /dev/null +++ b/0073-cldap.patch @@ -0,0 +1,2158 @@ +From db644a3a24c123a964129d41934365d8eb2174c7 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Pavel=20B=C5=99ezina?= +Date: Thu, 30 Jul 2020 12:59:01 +0200 +Subject: [PATCH 1/7] ldap: add support for cldap and udp connections + +Reviewed-by: Sumit Bose +(cherry picked from commit 414593cca65ed09fe4659e2786370a4553664cd0) + +Reviewed-by: Sumit Bose +--- + src/util/sss_ldap.c | 28 ++++++++++++++++++++----- + src/util/sss_sockets.c | 47 ++++++++++++++++++++++++++++-------------- + src/util/sss_sockets.h | 1 + + 3 files changed, 56 insertions(+), 20 deletions(-) + +diff --git a/src/util/sss_ldap.c b/src/util/sss_ldap.c +index 652b08ea7..71fa21f4a 100644 +--- a/src/util/sss_ldap.c ++++ b/src/util/sss_ldap.c +@@ -116,6 +116,7 @@ struct sss_ldap_init_state { + LDAP *ldap; + int sd; + const char *uri; ++ bool use_udp; + }; + + static int sss_ldap_init_state_destructor(void *data) +@@ -159,11 +160,13 @@ struct tevent_req *sss_ldap_init_send(TALLOC_CTX *mem_ctx, + state->ldap = NULL; + state->sd = -1; + state->uri = uri; ++ state->use_udp = strncmp(uri, "cldap", 5) == 0 ? true : false; + + #ifdef HAVE_LDAP_INIT_FD + struct tevent_req *subreq; + +- subreq = sssd_async_socket_init_send(state, ev, addr, addr_len, timeout); ++ subreq = sssd_async_socket_init_send(state, ev, state->use_udp, addr, ++ addr_len, timeout); + if (subreq == NULL) { + ret = ENOMEM; + DEBUG(SSSDBG_CRIT_FAILURE, "sssd_async_socket_init_send failed.\n"); +@@ -246,14 +249,29 @@ static void sss_ldap_init_sys_connect_done(struct tevent_req *subreq) + goto fail; + } + +- ret = unset_fcntl_flags(state->sd, O_NONBLOCK); +- if (ret != EOK) { +- goto fail; ++ /* openldap < 2.5 does not correctly handle O_NONBLOCK during starttls for ++ * ldaps, so we need to remove the flag here. This is fine since I/O events ++ * are handled via tevent so we only read when there is data available. ++ * ++ * We need to keep O_NONBLOCK due to a bug in openldap to correctly perform ++ * a parallel CLDAP pings without timeout. See: ++ * https://bugs.openldap.org/show_bug.cgi?id=9328 ++ * ++ * @todo remove this when the bug is fixed and we can put a hard requirement ++ * on newer openldap. ++ */ ++ if (!state->use_udp) { ++ ret = unset_fcntl_flags(state->sd, O_NONBLOCK); ++ if (ret != EOK) { ++ goto fail; ++ } + } + + /* Initialize LDAP handler */ + +- lret = ldap_init_fd(state->sd, LDAP_PROTO_TCP, state->uri, &state->ldap); ++ lret = ldap_init_fd(state->sd, ++ state->use_udp ? LDAP_PROTO_UDP : LDAP_PROTO_TCP, ++ state->uri, &state->ldap); + if (lret != LDAP_SUCCESS) { + DEBUG(SSSDBG_CRIT_FAILURE, + "ldap_init_fd failed: %s. [%d][%s]\n", +diff --git a/src/util/sss_sockets.c b/src/util/sss_sockets.c +index 6f2b71bc8..733360f3b 100644 +--- a/src/util/sss_sockets.c ++++ b/src/util/sss_sockets.c +@@ -80,6 +80,18 @@ static errno_t set_fd_common_opts(int fd, int timeout) + int ret; + struct timeval tv; + unsigned int milli; ++ int type; ++ socklen_t optlen = sizeof(int); ++ ++ /* Get protocol type. */ ++ ret = getsockopt(fd, SOL_SOCKET, SO_TYPE, &type, &optlen); ++ if (ret != 0) { ++ ret = errno; ++ DEBUG(SSSDBG_FUNC_DATA, "Unable to get socket type [%d]: %s.\n", ++ ret, strerror(ret)); ++ /* Assume TCP. */ ++ type = SOCK_STREAM; ++ } + + /* SO_KEEPALIVE and TCP_NODELAY are set by OpenLDAP client libraries but + * failures are ignored.*/ +@@ -91,12 +103,14 @@ static errno_t set_fd_common_opts(int fd, int timeout) + strerror(ret)); + } + +- ret = setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &dummy, sizeof(dummy)); +- if (ret != 0) { +- ret = errno; +- DEBUG(SSSDBG_FUNC_DATA, +- "setsockopt TCP_NODELAY failed.[%d][%s].\n", ret, +- strerror(ret)); ++ if (type == SOCK_STREAM) { ++ ret = setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &dummy, sizeof(dummy)); ++ if (ret != 0) { ++ ret = errno; ++ DEBUG(SSSDBG_FUNC_DATA, ++ "setsockopt TCP_NODELAY failed.[%d][%s].\n", ret, ++ strerror(ret)); ++ } + } + + if (timeout > 0) { +@@ -119,14 +133,16 @@ static errno_t set_fd_common_opts(int fd, int timeout) + strerror(ret)); + } + +- milli = timeout * 1000; /* timeout in milliseconds */ +- ret = setsockopt(fd, IPPROTO_TCP, TCP_USER_TIMEOUT, &milli, +- sizeof(milli)); +- if (ret != 0) { +- ret = errno; +- DEBUG(SSSDBG_FUNC_DATA, +- "setsockopt TCP_USER_TIMEOUT failed.[%d][%s].\n", ret, +- strerror(ret)); ++ if (type == SOCK_STREAM) { ++ milli = timeout * 1000; /* timeout in milliseconds */ ++ ret = setsockopt(fd, IPPROTO_TCP, TCP_USER_TIMEOUT, &milli, ++ sizeof(milli)); ++ if (ret != 0) { ++ ret = errno; ++ DEBUG(SSSDBG_FUNC_DATA, ++ "setsockopt TCP_USER_TIMEOUT failed.[%d][%s].\n", ret, ++ strerror(ret)); ++ } + } + } + +@@ -271,6 +287,7 @@ static void sssd_async_socket_init_done(struct tevent_req *subreq); + + struct tevent_req *sssd_async_socket_init_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, ++ bool use_udp, + struct sockaddr_storage *addr, + socklen_t addr_len, int timeout) + { +@@ -289,7 +306,7 @@ struct tevent_req *sssd_async_socket_init_send(TALLOC_CTX *mem_ctx, + talloc_set_destructor((TALLOC_CTX *)state, + sssd_async_socket_state_destructor); + +- state->sd = socket(addr->ss_family, SOCK_STREAM, 0); ++ state->sd = socket(addr->ss_family, use_udp ? SOCK_DGRAM : SOCK_STREAM, 0); + if (state->sd == -1) { + ret = errno; + DEBUG(SSSDBG_CRIT_FAILURE, +diff --git a/src/util/sss_sockets.h b/src/util/sss_sockets.h +index ccb05cb84..2758e6ed1 100644 +--- a/src/util/sss_sockets.h ++++ b/src/util/sss_sockets.h +@@ -32,6 +32,7 @@ int sssd_async_connect_recv(struct tevent_req *req); + + struct tevent_req *sssd_async_socket_init_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, ++ bool use_udp, + struct sockaddr_storage *addr, + socklen_t addr_len, int timeout); + int sssd_async_socket_init_recv(struct tevent_req *req, int *sd); +-- +2.26.3 + + +From 3efae1df18f4bfe7782025a14bcfbb5965496b32 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Pavel=20B=C5=99ezina?= +Date: Thu, 30 Jul 2020 13:30:50 +0200 +Subject: [PATCH 2/7] ad: use cldap for site and forrest discover (perform + CLDAP ping) + +All Windows clients uses CLDAP (UDP) for LDAP ping. Even though AD +also supports LDAP ping over TCP IPA does not therefore it is crusial +for us to perform the ping over CLDAP protocol. + +Resolves: +https://github.com/SSSD/sssd/issues/5215 + +Reviewed-by: Sumit Bose +(cherry picked from commit 8265674a055e5cdb57acebad72d935356408540a) + +Reviewed-by: Sumit Bose +--- + src/providers/ad/ad_init.c | 6 +----- + src/providers/ad/ad_srv.c | 9 +++------ + src/providers/ad/ad_srv.h | 3 +-- + src/providers/ad/ad_subdomains.c | 2 +- + src/providers/ipa/ipa_subdomains_server.c | 2 +- + 5 files changed, 7 insertions(+), 15 deletions(-) + +diff --git a/src/providers/ad/ad_init.c b/src/providers/ad/ad_init.c +index fb24a28e1..5abd28b7c 100644 +--- a/src/providers/ad/ad_init.c ++++ b/src/providers/ad/ad_init.c +@@ -187,14 +187,11 @@ static errno_t ad_init_srv_plugin(struct be_ctx *be_ctx, + const char *ad_site_override; + bool sites_enabled; + errno_t ret; +- bool ad_use_ldaps; + + hostname = dp_opt_get_string(ad_options->basic, AD_HOSTNAME); + ad_domain = dp_opt_get_string(ad_options->basic, AD_DOMAIN); + ad_site_override = dp_opt_get_string(ad_options->basic, AD_SITE); + sites_enabled = dp_opt_get_bool(ad_options->basic, AD_ENABLE_DNS_SITES); +- ad_use_ldaps = dp_opt_get_bool(ad_options->basic, AD_USE_LDAPS); +- + + if (!sites_enabled) { + ret = be_fo_set_dns_srv_lookup_plugin(be_ctx, hostname); +@@ -210,8 +207,7 @@ static errno_t ad_init_srv_plugin(struct be_ctx *be_ctx, + srv_ctx = ad_srv_plugin_ctx_init(be_ctx, be_ctx, be_ctx->be_res, + default_host_dbs, ad_options->id, + hostname, ad_domain, +- ad_site_override, +- ad_use_ldaps); ++ ad_site_override); + if (srv_ctx == NULL) { + DEBUG(SSSDBG_FATAL_FAILURE, "Out of memory?\n"); + return ENOMEM; +diff --git a/src/providers/ad/ad_srv.c b/src/providers/ad/ad_srv.c +index ca15d3715..55e8f63f7 100644 +--- a/src/providers/ad/ad_srv.c ++++ b/src/providers/ad/ad_srv.c +@@ -335,9 +335,9 @@ static errno_t ad_get_client_site_next_dc(struct tevent_req *req) + state->be_res->resolv, + state->be_res->family_order, + state->host_db, +- state->ad_use_ldaps ? "ldaps" : "ldap", ++ "cldap", + state->dc.host, +- state->ad_use_ldaps ? 636 : state->dc.port, ++ state->dc.port, + false); + if (subreq == NULL) { + ret = ENOMEM; +@@ -497,7 +497,6 @@ struct ad_srv_plugin_ctx { + const char *ad_domain; + const char *ad_site_override; + const char *current_site; +- bool ad_use_ldaps; + }; + + struct ad_srv_plugin_ctx * +@@ -508,8 +507,7 @@ ad_srv_plugin_ctx_init(TALLOC_CTX *mem_ctx, + struct sdap_options *opts, + const char *hostname, + const char *ad_domain, +- const char *ad_site_override, +- bool ad_use_ldaps) ++ const char *ad_site_override) + { + struct ad_srv_plugin_ctx *ctx = NULL; + errno_t ret; +@@ -523,7 +521,6 @@ ad_srv_plugin_ctx_init(TALLOC_CTX *mem_ctx, + ctx->be_res = be_res; + ctx->host_dbs = host_dbs; + ctx->opts = opts; +- ctx->ad_use_ldaps = ad_use_ldaps; + + ctx->hostname = talloc_strdup(ctx, hostname); + if (ctx->hostname == NULL) { +diff --git a/src/providers/ad/ad_srv.h b/src/providers/ad/ad_srv.h +index 8e410ec26..e553d594d 100644 +--- a/src/providers/ad/ad_srv.h ++++ b/src/providers/ad/ad_srv.h +@@ -31,8 +31,7 @@ ad_srv_plugin_ctx_init(TALLOC_CTX *mem_ctx, + struct sdap_options *opts, + const char *hostname, + const char *ad_domain, +- const char *ad_site_override, +- bool ad_use_ldaps); ++ const char *ad_site_override); + + struct tevent_req *ad_srv_plugin_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, +diff --git a/src/providers/ad/ad_subdomains.c b/src/providers/ad/ad_subdomains.c +index 16aecbc64..9b32196b7 100644 +--- a/src/providers/ad/ad_subdomains.c ++++ b/src/providers/ad/ad_subdomains.c +@@ -411,7 +411,7 @@ ad_subdom_ad_ctx_new(struct be_ctx *be_ctx, + ad_id_ctx->ad_options->id, + hostname, + ad_domain, +- ad_site_override, ad_use_ldaps); ++ ad_site_override); + if (srv_ctx == NULL) { + DEBUG(SSSDBG_FATAL_FAILURE, "Out of memory?\n"); + return ENOMEM; +diff --git a/src/providers/ipa/ipa_subdomains_server.c b/src/providers/ipa/ipa_subdomains_server.c +index e2037b59d..f0d8a6a20 100644 +--- a/src/providers/ipa/ipa_subdomains_server.c ++++ b/src/providers/ipa/ipa_subdomains_server.c +@@ -344,7 +344,7 @@ ipa_ad_ctx_new(struct be_ctx *be_ctx, + ad_id_ctx->ad_options->id, + id_ctx->server_mode->hostname, + ad_domain, +- ad_site_override, false); ++ ad_site_override); + if (srv_ctx == NULL) { + DEBUG(SSSDBG_FATAL_FAILURE, "Out of memory?\n"); + return ENOMEM; +-- +2.26.3 + + +From 7e856adefeaa377a50b40d397684e48ba82aa055 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Pavel=20B=C5=99ezina?= +Date: Tue, 11 Aug 2020 13:27:42 +0200 +Subject: [PATCH 3/7] ad: connect to the first available server for cldap ping + +Resolves: +https://github.com/SSSD/sssd/issues/3743 + +Reviewed-by: Sumit Bose +(cherry picked from commit 1889ca60a9c642f0cca60b20a5b94de7a66924f6) + +Reviewed-by: Sumit Bose +--- + Makefile.am | 5 +- + src/providers/ad/ad_cldap_ping.c | 586 +++++++++++++++++++++++++++++++ + src/providers/ad/ad_srv.c | 434 +---------------------- + src/providers/ad/ad_srv.h | 14 + + 4 files changed, 613 insertions(+), 426 deletions(-) + create mode 100644 src/providers/ad/ad_cldap_ping.c + +diff --git a/Makefile.am b/Makefile.am +index b9ca9a7c6..17e20f1cb 100644 +--- a/Makefile.am ++++ b/Makefile.am +@@ -4202,7 +4202,9 @@ libsss_ipa_la_SOURCES = \ + src/providers/ad/ad_pac.c \ + src/providers/ad/ad_pac_common.c \ + src/providers/ad/ad_srv.c \ +- src/providers/ad/ad_domain_info.c ++ src/providers/ad/ad_domain_info.c \ ++ src/providers/ad/ad_cldap_ping.c \ ++ $(NULL) + libsss_ipa_la_CFLAGS = \ + $(AM_CFLAGS) \ + $(OPENLDAP_CFLAGS) \ +@@ -4269,6 +4271,7 @@ libsss_ad_la_SOURCES = \ + src/providers/ad/ad_subdomains.c \ + src/providers/ad/ad_domain_info.c \ + src/providers/ad/ad_refresh.c \ ++ src/providers/ad/ad_cldap_ping.c \ + $(NULL) + + +diff --git a/src/providers/ad/ad_cldap_ping.c b/src/providers/ad/ad_cldap_ping.c +new file mode 100644 +index 000000000..5fc1a4d20 +--- /dev/null ++++ b/src/providers/ad/ad_cldap_ping.c +@@ -0,0 +1,586 @@ ++/* ++ Authors: ++ Pavel Březina ++ ++ Copyright (C) 2020 Red Hat ++ ++ 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 . ++*/ ++ ++#include ++#include ++#include ++#include ++#include ++ ++#include "util/util.h" ++#include "util/sss_ldap.h" ++#include "resolv/async_resolv.h" ++#include "providers/backend.h" ++#include "providers/ad/ad_srv.h" ++#include "providers/ad/ad_common.h" ++#include "providers/fail_over.h" ++#include "providers/fail_over_srv.h" ++#include "providers/ldap/sdap.h" ++#include "providers/ldap/sdap_async.h" ++#include "db/sysdb.h" ++ ++struct ad_cldap_ping_dc_state { ++ struct tevent_context *ev; ++ struct sdap_options *opts; ++ struct fo_server_info *dc; ++ struct sdap_handle *sh; ++ const char *ad_domain; ++ ++ char *site; ++ char *forest; ++}; ++ ++static void ad_cldap_ping_dc_connect_done(struct tevent_req *subreq); ++static void ad_cldap_ping_dc_done(struct tevent_req *subreq); ++ ++static struct tevent_req *ad_cldap_ping_dc_send(TALLOC_CTX *mem_ctx, ++ struct tevent_context *ev, ++ struct sdap_options *opts, ++ struct be_resolv_ctx *be_res, ++ enum host_database *host_db, ++ struct fo_server_info *dc, ++ const char *ad_domain) ++{ ++ struct ad_cldap_ping_dc_state *state; ++ struct tevent_req *subreq; ++ struct tevent_req *req; ++ errno_t ret; ++ ++ req = tevent_req_create(mem_ctx, &state, ++ struct ad_cldap_ping_dc_state); ++ if (req == NULL) { ++ DEBUG(SSSDBG_CRIT_FAILURE, "tevent_req_create() failed\n"); ++ return NULL; ++ } ++ ++ state->ev = ev; ++ state->opts = opts; ++ state->dc = dc; ++ state->ad_domain = ad_domain; ++ ++ subreq = sdap_connect_host_send(state, ev, opts, be_res->resolv, ++ be_res->family_order, host_db, "cldap", ++ dc->host, dc->port, false); ++ if (subreq == NULL) { ++ ret = ENOMEM; ++ goto done; ++ } ++ ++ tevent_req_set_callback(subreq, ad_cldap_ping_dc_connect_done, req); ++ ++ return req; ++ ++done: ++ tevent_req_error(req, ret); ++ tevent_req_post(req, ev); ++ ++ return req; ++} ++ ++static void ad_cldap_ping_dc_connect_done(struct tevent_req *subreq) ++{ ++ static const char *attrs[] = {AD_AT_NETLOGON, NULL}; ++ struct ad_cldap_ping_dc_state *state; ++ struct tevent_req *req; ++ char *ntver; ++ char *filter; ++ int timeout; ++ errno_t ret; ++ ++ req = tevent_req_callback_data(subreq, struct tevent_req); ++ state = tevent_req_data(req, struct ad_cldap_ping_dc_state); ++ ++ ret = sdap_connect_host_recv(state, subreq, &state->sh); ++ talloc_zfree(subreq); ++ if (ret != EOK) { ++ goto done; ++ } ++ ++ ntver = sss_ldap_encode_ndr_uint32(state, NETLOGON_NT_VERSION_5EX | ++ NETLOGON_NT_VERSION_WITH_CLOSEST_SITE); ++ if (ntver == NULL) { ++ ret = ENOMEM; ++ goto done; ++ } ++ ++ filter = talloc_asprintf(state, "(&(%s=%s)(%s=%s))", AD_AT_DNS_DOMAIN, ++ state->ad_domain, AD_AT_NT_VERSION, ntver); ++ if (filter == NULL) { ++ ret = ENOMEM; ++ goto done; ++ } ++ ++ timeout = dp_opt_get_int(state->opts->basic, SDAP_SEARCH_TIMEOUT); ++ subreq = sdap_get_generic_send(state, state->ev, state->opts, state->sh, "", ++ LDAP_SCOPE_BASE, filter, attrs, NULL, ++ 0, timeout, false); ++ if (subreq == NULL) { ++ ret = ENOMEM; ++ goto done; ++ } ++ ++ tevent_req_set_callback(subreq, ad_cldap_ping_dc_done, req); ++ ++ ret = EOK; ++ ++done: ++ if (ret != EOK) { ++ tevent_req_error(req, ret); ++ } ++} ++ ++static void ad_cldap_ping_dc_done(struct tevent_req *subreq) ++{ ++ struct ad_cldap_ping_dc_state *state; ++ struct tevent_req *req; ++ struct sysdb_attrs **reply; ++ size_t reply_count; ++ errno_t ret; ++ ++ req = tevent_req_callback_data(subreq, struct tevent_req); ++ state = tevent_req_data(req, struct ad_cldap_ping_dc_state); ++ ++ ret = sdap_get_generic_recv(subreq, state, &reply_count, &reply); ++ ++ talloc_zfree(subreq); ++ talloc_zfree(state->sh); ++ ++ if (ret != EOK) { ++ DEBUG(SSSDBG_OP_FAILURE, "%s:%d: unable to get netlogon information\n", ++ state->dc->host, state->dc->port); ++ goto done; ++ } ++ ++ if (reply_count == 0) { ++ DEBUG(SSSDBG_OP_FAILURE, "%s:%d: no netlogon information available\n", ++ state->dc->host, state->dc->port); ++ ret = ENOENT; ++ goto done; ++ } ++ ++ ret = netlogon_get_domain_info(state, reply[0], true, NULL, &state->site, ++ &state->forest); ++ if (ret != EOK) { ++ DEBUG(SSSDBG_OP_FAILURE, ++ "%s:%d: unable to retrieve site name [%d]: %s\n", ++ state->dc->host, state->dc->port, ret, sss_strerror(ret)); ++ ret = ENOENT; ++ goto done; ++ } ++ ++ DEBUG(SSSDBG_TRACE_FUNC, "%s:%d: found site (%s) and forest (%s)\n", ++ state->dc->host, state->dc->port, state->site, state->forest); ++ ++ ret = EOK; ++ ++done: ++ if (ret != EOK) { ++ tevent_req_error(req, ret); ++ return; ++ } ++ ++ tevent_req_done(req); ++} ++ ++static errno_t ad_cldap_ping_dc_recv(TALLOC_CTX *mem_ctx, ++ struct tevent_req *req, ++ const char **_site, ++ const char **_forest) ++{ ++ struct ad_cldap_ping_dc_state *state = NULL; ++ state = tevent_req_data(req, struct ad_cldap_ping_dc_state); ++ ++ TEVENT_REQ_RETURN_ON_ERROR(req); ++ ++ *_site = talloc_steal(mem_ctx, state->site); ++ *_forest = talloc_steal(mem_ctx, state->forest); ++ ++ return EOK; ++} ++ ++struct ad_cldap_ping_parallel_state { ++ struct tevent_context *ev; ++ struct sdap_options *opts; ++ struct be_resolv_ctx *be_res; ++ enum host_database *host_db; ++ const char *ad_domain; ++ struct fo_server_info *dc_list; ++ size_t dc_count; ++ ++ TALLOC_CTX *reqs_ctx; ++ struct tevent_timer *te; ++ int active_requests; ++ size_t next_dc; ++ int batch; ++ ++ const char *site; ++ const char *forest; ++}; ++ ++static void ad_cldap_ping_parallel_batch(struct tevent_context *ev, ++ struct tevent_timer *te, ++ struct timeval tv, ++ void *data); ++static void ad_cldap_ping_parallel_done(struct tevent_req *subreq); ++ ++static struct tevent_req * ++ad_cldap_ping_parallel_send(TALLOC_CTX *mem_ctx, ++ struct tevent_context *ev, ++ struct sdap_options *opts, ++ struct be_resolv_ctx *be_res, ++ enum host_database *host_db, ++ struct fo_server_info *dc_list, ++ size_t dc_count, ++ const char *ad_domain) ++{ ++ struct ad_cldap_ping_parallel_state *state; ++ struct tevent_req *req; ++ struct timeval tv = {0, 0}; ++ errno_t ret; ++ ++ req = tevent_req_create(mem_ctx, &state, ++ struct ad_cldap_ping_parallel_state); ++ if (req == NULL) { ++ DEBUG(SSSDBG_CRIT_FAILURE, "tevent_req_create() failed\n"); ++ return NULL; ++ } ++ ++ state->ev = ev; ++ state->opts = opts; ++ state->be_res = be_res; ++ state->host_db = host_db; ++ state->ad_domain = ad_domain; ++ state->dc_list = dc_list; ++ state->dc_count = dc_count; ++ ++ state->reqs_ctx = talloc_new(state); ++ if (state->reqs_ctx == NULL) { ++ ret = ENOMEM; ++ goto done; ++ } ++ ++ state->next_dc = 0; ++ state->batch = 1; ++ ad_cldap_ping_parallel_batch(ev, NULL, tv, req); ++ ++ return req; ++ ++done: ++ tevent_req_error(req, ret); ++ tevent_req_post(req, ev); ++ ++ return req; ++} ++ ++static void ad_cldap_ping_parallel_batch(struct tevent_context *ev, ++ struct tevent_timer *te, ++ struct timeval tv, ++ void *data) ++{ ++ struct ad_cldap_ping_parallel_state *state; ++ struct tevent_req *req; ++ struct tevent_req *subreq; ++ uint32_t delay; ++ size_t limit; ++ size_t i; ++ ++ req = talloc_get_type(data, struct tevent_req); ++ state = tevent_req_data(req, struct ad_cldap_ping_parallel_state); ++ ++ state->te = NULL; ++ ++ /* Issue three batches in total to avoid pinging too many domain controllers ++ * if not necessary. The first batch (5 pings) is issued immediately and we ++ * will wait 400ms for it to finish. If we don't get a reply in time we ++ * issue next batch (5 pings) and wait 200ms. If we still have no reply, ++ * we contact remaining domain controllers. ++ * ++ * This follows algorithm described at section 5.4.5.3 of MS-DISO: ++ * https://winprotocoldoc.blob.core.windows.net/productionwindowsarchives/WinArchive/%5bMS-DISO%5d.pdf ++ */ ++ switch (state->batch) { ++ case 1: ++ case 2: ++ limit = MIN(state->dc_count, 5 + state->next_dc); ++ delay = 400000 / state->batch; ++ break; ++ default: ++ limit = state->dc_count; ++ delay = 0; ++ } ++ ++ for (i = state->next_dc; i < limit; i++) { ++ DEBUG(SSSDBG_TRACE_ALL, "Batch %d: %s:%d\n", state->batch, ++ state->dc_list[i].host, state->dc_list[i].port); ++ } ++ ++ for (; state->next_dc < limit; state->next_dc++) { ++ subreq = ad_cldap_ping_dc_send(state->reqs_ctx, ev, state->opts, ++ state->be_res, state->host_db, ++ &state->dc_list[state->next_dc], ++ state->ad_domain); ++ if (subreq == NULL) { ++ DEBUG(SSSDBG_OP_FAILURE, "Unable to create new ping request\n"); ++ goto fail; ++ } ++ ++ state->active_requests++; ++ tevent_req_set_callback(subreq, ad_cldap_ping_parallel_done, req); ++ } ++ ++ state->batch++; ++ if (delay > 0) { ++ tv = tevent_timeval_current_ofs(0, delay); ++ state->te = tevent_add_timer(ev, state->reqs_ctx, tv, ++ ad_cldap_ping_parallel_batch, req); ++ if (state->te == NULL) { ++ DEBUG(SSSDBG_OP_FAILURE, "Unable to schedule next batch!\n"); ++ goto fail; ++ } ++ } ++ ++ return; ++ ++fail: ++ if (state->active_requests == 0) { ++ tevent_req_error(req, ENOMEM); ++ if (state->batch == 1) { ++ tevent_req_post(req, ev); ++ } ++ } ++} ++ ++static void ad_cldap_ping_parallel_done(struct tevent_req *subreq) ++{ ++ struct ad_cldap_ping_parallel_state *state; ++ struct timeval tv = {0, 0}; ++ struct tevent_req *req; ++ errno_t ret; ++ ++ req = tevent_req_callback_data(subreq, struct tevent_req); ++ state = tevent_req_data(req, struct ad_cldap_ping_parallel_state); ++ ++ ret = ad_cldap_ping_dc_recv(state, subreq, &state->site, &state->forest); ++ talloc_zfree(subreq); ++ state->active_requests--; ++ ++ if (ret == EOK) { ++ /* We have the answer. Terminate other attempts and finish. */ ++ talloc_zfree(state->reqs_ctx); ++ tevent_req_done(req); ++ } else if (state->active_requests == 0) { ++ /* There are still servers to try, don't wait for the timer. */ ++ if (state->next_dc < state->dc_count) { ++ talloc_zfree(state->te); ++ ad_cldap_ping_parallel_batch(state->ev, NULL, tv, req); ++ return; ++ } ++ /* There is no available server. */ ++ tevent_req_error(req, ENOENT); ++ } ++ ++ /* Wait for another request to finish. */ ++} ++ ++static errno_t ad_cldap_ping_parallel_recv(TALLOC_CTX *mem_ctx, ++ struct tevent_req *req, ++ const char **_site, ++ const char **_forest) ++{ ++ struct ad_cldap_ping_parallel_state *state = NULL; ++ state = tevent_req_data(req, struct ad_cldap_ping_parallel_state); ++ ++ TEVENT_REQ_RETURN_ON_ERROR(req); ++ ++ *_site = talloc_steal(mem_ctx, state->site); ++ *_forest = talloc_steal(mem_ctx, state->forest); ++ ++ return EOK; ++} ++ ++struct ad_cldap_ping_state { ++ struct tevent_context *ev; ++ struct sdap_options *opts; ++ struct be_resolv_ctx *be_res; ++ enum host_database *host_db; ++ const char *ad_domain; ++ ++ struct fo_server_info *dc_list; ++ size_t dc_count; ++ const char *site; ++ const char *forest; ++}; ++ ++static void ad_cldap_ping_discovery_done(struct tevent_req *subreq); ++static void ad_cldap_ping_done(struct tevent_req *subreq); ++ ++struct tevent_req *ad_cldap_ping_send(TALLOC_CTX *mem_ctx, ++ struct tevent_context *ev, ++ struct sdap_options *opts, ++ struct be_resolv_ctx *be_res, ++ enum host_database *host_db, ++ const char *ad_domain, ++ const char *discovery_domain, ++ const char *current_site) ++{ ++ struct ad_cldap_ping_state *state; ++ struct tevent_req *subreq; ++ struct tevent_req *req; ++ const char **domains; ++ errno_t ret; ++ ++ req = tevent_req_create(mem_ctx, &state, struct ad_cldap_ping_state); ++ if (req == NULL) { ++ DEBUG(SSSDBG_CRIT_FAILURE, "tevent_req_create() failed\n"); ++ return NULL; ++ } ++ ++ state->ev = ev; ++ state->opts = opts; ++ state->be_res = be_res; ++ state->host_db = host_db; ++ state->ad_domain = ad_domain; ++ ++ domains = talloc_zero_array(state, const char *, 3); ++ if (domains == NULL) { ++ ret = ENOMEM; ++ goto done; ++ } ++ ++ if (current_site == NULL) { ++ domains[0] = discovery_domain; ++ domains[1] = NULL; ++ } else { ++ domains[0] = ad_site_dns_discovery_domain(state, current_site, ++ discovery_domain); ++ domains[1] = discovery_domain; ++ ++ if (domains[0] == NULL) { ++ DEBUG(SSSDBG_CRIT_FAILURE, "Out of memory!"); ++ ret = ENOMEM; ++ goto done; ++ } ++ } ++ ++ /* Even though we use CLDAP (UDP) to perform the ping we need to discover ++ * domain controllers in TCP namespace as they are not automatically ++ * available under UDP. */ ++ subreq = fo_discover_srv_send(state, ev, be_res->resolv, "ldap", ++ FO_PROTO_TCP, domains); ++ if (subreq == NULL) { ++ ret = ENOMEM; ++ goto done; ++ } ++ ++ tevent_req_set_callback(subreq, ad_cldap_ping_discovery_done, req); ++ ++ return req; ++ ++done: ++ tevent_req_error(req, ret); ++ tevent_req_post(req, ev); ++ ++ return req; ++} ++ ++static void ad_cldap_ping_discovery_done(struct tevent_req *subreq) ++{ ++ struct ad_cldap_ping_state *state; ++ struct tevent_req *req; ++ char *domain; ++ errno_t ret; ++ ++ req = tevent_req_callback_data(subreq, struct tevent_req); ++ state = tevent_req_data(req, struct ad_cldap_ping_state); ++ ++ ret = fo_discover_srv_recv(state, subreq, &domain, NULL, &state->dc_list, ++ &state->dc_count); ++ talloc_zfree(subreq); ++ if (ret != EOK) { ++ goto done; ++ } ++ ++ DEBUG(SSSDBG_TRACE_FUNC, "Found %zu domain controllers in domain %s\n", ++ state->dc_count, domain); ++ ++ subreq = ad_cldap_ping_parallel_send(state, state->ev, state->opts, ++ state->be_res, state->host_db, ++ state->dc_list, state->dc_count, ++ state->ad_domain); ++ if (subreq == NULL) { ++ ret = ENOMEM; ++ goto done; ++ } ++ ++ tevent_req_set_callback(subreq, ad_cldap_ping_done, req); ++ ++done: ++ if (ret != EOK) { ++ tevent_req_error(req, ret); ++ return; ++ } ++} ++ ++static void ad_cldap_ping_done(struct tevent_req *subreq) ++{ ++ struct ad_cldap_ping_state *state; ++ struct tevent_req *req; ++ errno_t ret; ++ ++ req = tevent_req_callback_data(subreq, struct tevent_req); ++ state = tevent_req_data(req, struct ad_cldap_ping_state); ++ ++ ret = ad_cldap_ping_parallel_recv(state, subreq, &state->site, ++ &state->forest); ++ talloc_zfree(subreq); ++ if (ret != EOK) { ++ DEBUG(SSSDBG_OP_FAILURE, ++ "Unable to get site and forest information [%d]: %s\n", ++ ret, sss_strerror(ret)); ++ goto done; ++ } ++ ++ DEBUG(SSSDBG_TRACE_FUNC, "Found site: %s\n", state->site); ++ DEBUG(SSSDBG_TRACE_FUNC, "Found forest: %s\n", state->forest); ++ ++done: ++ if (ret != EOK) { ++ tevent_req_error(req, ret); ++ return; ++ } ++ ++ tevent_req_done(req); ++} ++ ++errno_t ad_cldap_ping_recv(TALLOC_CTX *mem_ctx, ++ struct tevent_req *req, ++ const char **_site, ++ const char **_forest) ++{ ++ struct ad_cldap_ping_state *state = NULL; ++ state = tevent_req_data(req, struct ad_cldap_ping_state); ++ ++ TEVENT_REQ_RETURN_ON_ERROR(req); ++ ++ *_site = talloc_steal(mem_ctx, state->site); ++ *_forest = talloc_steal(mem_ctx, state->forest); ++ ++ return EOK; ++} +\ No newline at end of file +diff --git a/src/providers/ad/ad_srv.c b/src/providers/ad/ad_srv.c +index 55e8f63f7..d12f0971c 100644 +--- a/src/providers/ad/ad_srv.c ++++ b/src/providers/ad/ad_srv.c +@@ -116,378 +116,6 @@ static errno_t ad_sort_servers_by_dns(TALLOC_CTX *mem_ctx, + return EOK; + } + +-struct ad_get_dc_servers_state { +- struct fo_server_info *servers; +- size_t num_servers; +-}; +- +-static void ad_get_dc_servers_done(struct tevent_req *subreq); +- +-static struct tevent_req *ad_get_dc_servers_send(TALLOC_CTX *mem_ctx, +- struct tevent_context *ev, +- struct resolv_ctx *resolv_ctx, +- const char *discovery_domain, +- const char *site) +-{ +- struct ad_get_dc_servers_state *state = NULL; +- struct tevent_req *req = NULL; +- struct tevent_req *subreq = NULL; +- const char **domains = NULL; +- errno_t ret; +- +- req = tevent_req_create(mem_ctx, &state, +- struct ad_get_dc_servers_state); +- if (req == NULL) { +- DEBUG(SSSDBG_CRIT_FAILURE, "tevent_req_create() failed\n"); +- return NULL; +- } +- +- domains = talloc_zero_array(state, const char *, 3); +- if (domains == NULL) { +- ret = ENOMEM; +- goto immediately; +- } +- +- if (site == NULL) { +- DEBUG(SSSDBG_TRACE_FUNC, "Looking up domain controllers in domain " +- "%s\n", discovery_domain); +- +- domains[0] = talloc_strdup(domains, discovery_domain); +- if (domains[0] == NULL) { +- ret = ENOMEM; +- goto immediately; +- } +- } else { +- DEBUG(SSSDBG_TRACE_FUNC, "Looking up domain controllers in domain " +- "%s and site %s\n", discovery_domain, site); +- +- domains[0] = ad_site_dns_discovery_domain(domains, +- site, discovery_domain); +- if (domains[0] == NULL) { +- ret = ENOMEM; +- goto immediately; +- } +- +- domains[1] = talloc_strdup(domains, discovery_domain); +- if (domains[1] == NULL) { +- ret = ENOMEM; +- goto immediately; +- } +- } +- +- subreq = fo_discover_srv_send(state, ev, resolv_ctx, +- "ldap", FO_PROTO_TCP, domains); +- if (subreq == NULL) { +- ret = ENOMEM; +- goto immediately; +- } +- +- tevent_req_set_callback(subreq, ad_get_dc_servers_done, req); +- +- return req; +- +-immediately: +- tevent_req_error(req, ret); +- tevent_req_post(req, ev); +- +- return req; +-} +- +-static void ad_get_dc_servers_done(struct tevent_req *subreq) +-{ +- struct ad_get_dc_servers_state *state = NULL; +- struct tevent_req *req = NULL; +- char *domain = NULL; +- errno_t ret; +- +- req = tevent_req_callback_data(subreq, struct tevent_req); +- state = tevent_req_data(req, struct ad_get_dc_servers_state); +- +- ret = fo_discover_srv_recv(state, subreq, &domain, NULL, +- &state->servers, &state->num_servers); +- talloc_zfree(subreq); +- if (ret != EOK) { +- goto done; +- } +- +- DEBUG(SSSDBG_TRACE_FUNC, "Found %zu domain controllers in domain %s\n", +- state->num_servers, domain); +- +-done: +- if (ret != EOK) { +- tevent_req_error(req, ret); +- return; +- } +- +- tevent_req_done(req); +-} +- +-static int ad_get_dc_servers_recv(TALLOC_CTX *mem_ctx, +- struct tevent_req *req, +- struct fo_server_info **_dcs, +- size_t *_num_dcs) +-{ +- struct ad_get_dc_servers_state *state = NULL; +- state = tevent_req_data(req, struct ad_get_dc_servers_state); +- +- TEVENT_REQ_RETURN_ON_ERROR(req); +- +- *_dcs = talloc_steal(mem_ctx, state->servers); +- *_num_dcs = state->num_servers; +- +- return EOK; +-} +- +-struct ad_get_client_site_state { +- struct tevent_context *ev; +- struct be_resolv_ctx *be_res; +- enum host_database *host_db; +- struct sdap_options *opts; +- const char *ad_domain; +- bool ad_use_ldaps; +- struct fo_server_info *dcs; +- size_t num_dcs; +- size_t dc_index; +- struct fo_server_info dc; +- +- struct sdap_handle *sh; +- char *site; +- char *forest; +-}; +- +-static errno_t ad_get_client_site_next_dc(struct tevent_req *req); +-static void ad_get_client_site_connect_done(struct tevent_req *subreq); +-static void ad_get_client_site_done(struct tevent_req *subreq); +- +-struct tevent_req *ad_get_client_site_send(TALLOC_CTX *mem_ctx, +- struct tevent_context *ev, +- struct be_resolv_ctx *be_res, +- enum host_database *host_db, +- struct sdap_options *opts, +- const char *ad_domain, +- bool ad_use_ldaps, +- struct fo_server_info *dcs, +- size_t num_dcs) +-{ +- struct ad_get_client_site_state *state = NULL; +- struct tevent_req *req = NULL; +- errno_t ret; +- +- req = tevent_req_create(mem_ctx, &state, +- struct ad_get_client_site_state); +- if (req == NULL) { +- DEBUG(SSSDBG_CRIT_FAILURE, "tevent_req_create() failed\n"); +- return NULL; +- } +- +- if (be_res == NULL || host_db == NULL || opts == NULL) { +- ret = EINVAL; +- goto immediately; +- } +- +- state->ev = ev; +- state->be_res = be_res; +- state->host_db = host_db; +- state->opts = opts; +- state->ad_domain = ad_domain; +- state->ad_use_ldaps = ad_use_ldaps; +- state->dcs = dcs; +- state->num_dcs = num_dcs; +- +- state->dc_index = 0; +- ret = ad_get_client_site_next_dc(req); +- if (ret == EOK) { +- ret = ENOENT; +- goto immediately; +- } else if (ret != EAGAIN) { +- goto immediately; +- } +- +- return req; +- +-immediately: +- if (ret == EOK) { +- tevent_req_done(req); +- } else { +- tevent_req_error(req, ret); +- } +- tevent_req_post(req, ev); +- +- return req; +-} +- +-static errno_t ad_get_client_site_next_dc(struct tevent_req *req) +-{ +- struct ad_get_client_site_state *state = NULL; +- struct tevent_req *subreq = NULL; +- errno_t ret; +- +- state = tevent_req_data(req, struct ad_get_client_site_state); +- +- if (state->dc_index >= state->num_dcs) { +- ret = EOK; +- goto done; +- } +- +- state->dc = state->dcs[state->dc_index]; +- +- subreq = sdap_connect_host_send(state, state->ev, state->opts, +- state->be_res->resolv, +- state->be_res->family_order, +- state->host_db, +- "cldap", +- state->dc.host, +- state->dc.port, +- false); +- if (subreq == NULL) { +- ret = ENOMEM; +- goto done; +- } +- +- tevent_req_set_callback(subreq, ad_get_client_site_connect_done, req); +- +- state->dc_index++; +- ret = EAGAIN; +- +-done: +- return ret; +-} +- +-static void ad_get_client_site_connect_done(struct tevent_req *subreq) +-{ +- struct ad_get_client_site_state *state = NULL; +- struct tevent_req *req = NULL; +- static const char *attrs[] = {AD_AT_NETLOGON, NULL}; +- char *filter = NULL; +- char *ntver = NULL; +- errno_t ret; +- +- req = tevent_req_callback_data(subreq, struct tevent_req); +- state = tevent_req_data(req, struct ad_get_client_site_state); +- +- ret = sdap_connect_host_recv(state, subreq, &state->sh); +- talloc_zfree(subreq); +- if (ret != EOK) { +- DEBUG(SSSDBG_MINOR_FAILURE, "Unable to connect to domain controller " +- "[%s:%d]\n", state->dc.host, state->dc.port); +- +- ret = ad_get_client_site_next_dc(req); +- if (ret == EOK) { +- ret = ENOENT; +- } +- +- goto done; +- } +- +- ntver = sss_ldap_encode_ndr_uint32(state, NETLOGON_NT_VERSION_5EX | +- NETLOGON_NT_VERSION_WITH_CLOSEST_SITE); +- if (ntver == NULL) { +- ret = ENOMEM; +- goto done; +- } +- +- filter = talloc_asprintf(state, "(&(%s=%s)(%s=%s))", +- AD_AT_DNS_DOMAIN, state->ad_domain, +- AD_AT_NT_VERSION, ntver); +- if (filter == NULL) { +- ret = ENOMEM; +- goto done; +- } +- +- subreq = sdap_get_generic_send(state, state->ev, state->opts, state->sh, +- "", LDAP_SCOPE_BASE, filter, +- attrs, NULL, 0, +- dp_opt_get_int(state->opts->basic, +- SDAP_SEARCH_TIMEOUT), +- false); +- if (subreq == NULL) { +- ret = ENOMEM; +- goto done; +- } +- +- tevent_req_set_callback(subreq, ad_get_client_site_done, req); +- +- ret = EAGAIN; +- +-done: +- if (ret == EOK) { +- tevent_req_done(req); +- } else if (ret != EAGAIN) { +- tevent_req_error(req, ret); +- } +- +- return; +-} +- +-static void ad_get_client_site_done(struct tevent_req *subreq) +-{ +- struct ad_get_client_site_state *state = NULL; +- struct tevent_req *req = NULL; +- struct sysdb_attrs **reply = NULL; +- size_t reply_count; +- errno_t ret; +- +- req = tevent_req_callback_data(subreq, struct tevent_req); +- state = tevent_req_data(req, struct ad_get_client_site_state); +- +- ret = sdap_get_generic_recv(subreq, state, &reply_count, &reply); +- talloc_zfree(subreq); +- +- /* we're done with this LDAP, close connection */ +- talloc_zfree(state->sh); +- if (ret != EOK) { +- DEBUG(SSSDBG_OP_FAILURE, "Unable to get netlogon information\n"); +- +- ret = ad_get_client_site_next_dc(req); +- if (ret == EOK) { +- ret = ENOENT; +- } +- goto done; +- } +- +- if (reply_count == 0) { +- DEBUG(SSSDBG_OP_FAILURE, "No netlogon information retrieved\n"); +- ret = ENOENT; +- goto done; +- } +- +- ret = netlogon_get_domain_info(state, reply[0], true, NULL, &state->site, +- &state->forest); +- if (ret != EOK) { +- DEBUG(SSSDBG_OP_FAILURE, "Unable to retrieve site name [%d]: %s\n", +- ret, strerror(ret)); +- ret = ENOENT; +- goto done; +- } +- +- DEBUG(SSSDBG_TRACE_FUNC, "Found site: %s\n", state->site); +- DEBUG(SSSDBG_TRACE_FUNC, "Found forest: %s\n", state->forest); +- +-done: +- if (ret != EOK) { +- tevent_req_error(req, ret); +- return; +- } +- +- tevent_req_done(req); +-} +- +-int ad_get_client_site_recv(TALLOC_CTX *mem_ctx, +- struct tevent_req *req, +- const char **_site, +- const char **_forest) +-{ +- struct ad_get_client_site_state *state = NULL; +- state = tevent_req_data(req, struct ad_get_client_site_state); +- +- TEVENT_REQ_RETURN_ON_ERROR(req); +- +- *_site = talloc_steal(mem_ctx, state->site); +- *_forest = talloc_steal(mem_ctx, state->forest); +- +- return EOK; +-} +- + struct ad_srv_plugin_ctx { + struct be_ctx *be_ctx; + struct be_resolv_ctx *be_res; +@@ -610,8 +238,7 @@ struct ad_srv_plugin_state { + size_t num_backup_servers; + }; + +-static void ad_srv_plugin_dcs_done(struct tevent_req *subreq); +-static void ad_srv_plugin_site_done(struct tevent_req *subreq); ++static void ad_srv_plugin_ping_done(struct tevent_req *subreq); + static void ad_srv_plugin_servers_done(struct tevent_req *subreq); + + /* 1. Do a DNS lookup to find any DC in domain +@@ -677,15 +304,16 @@ struct tevent_req *ad_srv_plugin_send(TALLOC_CTX *mem_ctx, + + DEBUG(SSSDBG_TRACE_FUNC, "About to find domain controllers\n"); + +- subreq = ad_get_dc_servers_send(state, ev, ctx->be_res->resolv, +- state->discovery_domain, +- state->ctx->current_site); ++ subreq = ad_cldap_ping_send(state, ev, ctx->opts, ctx->be_res, ++ ctx->host_dbs, ctx->ad_domain, ++ state->discovery_domain, ++ state->ctx->current_site); + if (subreq == NULL) { + ret = ENOMEM; + goto immediately; + } + +- tevent_req_set_callback(subreq, ad_srv_plugin_dcs_done, req); ++ tevent_req_set_callback(subreq, ad_srv_plugin_ping_done, req); + + return req; + +@@ -696,52 +324,7 @@ immediately: + return req; + } + +-static void ad_srv_plugin_dcs_done(struct tevent_req *subreq) +-{ +- struct ad_srv_plugin_state *state = NULL; +- struct tevent_req *req = NULL; +- struct fo_server_info *dcs = NULL; +- size_t num_dcs = 0; +- errno_t ret; +- +- req = tevent_req_callback_data(subreq, struct tevent_req); +- state = tevent_req_data(req, struct ad_srv_plugin_state); +- +- ret = ad_get_dc_servers_recv(state, subreq, &dcs, &num_dcs); +- talloc_zfree(subreq); +- if (ret != EOK) { +- goto done; +- } +- +- DEBUG(SSSDBG_TRACE_FUNC, "About to locate suitable site\n"); +- +- subreq = ad_get_client_site_send(state, state->ev, +- state->ctx->be_res, +- state->ctx->host_dbs, +- state->ctx->opts, +- state->discovery_domain, +- state->ctx->ad_use_ldaps, +- dcs, num_dcs); +- if (subreq == NULL) { +- ret = ENOMEM; +- goto done; +- } +- +- tevent_req_set_callback(subreq, ad_srv_plugin_site_done, req); +- +- ret = EAGAIN; +- +-done: +- if (ret == EOK) { +- tevent_req_done(req); +- } else if (ret != EAGAIN) { +- tevent_req_error(req, ret); +- } +- +- return; +-} +- +-static void ad_srv_plugin_site_done(struct tevent_req *subreq) ++static void ad_srv_plugin_ping_done(struct tevent_req *subreq) + { + struct ad_srv_plugin_state *state = NULL; + struct tevent_req *req = NULL; +@@ -752,8 +335,9 @@ static void ad_srv_plugin_site_done(struct tevent_req *subreq) + req = tevent_req_callback_data(subreq, struct tevent_req); + state = tevent_req_data(req, struct ad_srv_plugin_state); + +- ret = ad_get_client_site_recv(state, subreq, &state->site, &state->forest); ++ ret = ad_cldap_ping_recv(state, subreq, &state->site, &state->forest); + talloc_zfree(subreq); ++ + /* Ignore AD site found by dns discovery if specific site is set in + * configuration file. */ + if (state->ctx->ad_site_override != NULL) { +diff --git a/src/providers/ad/ad_srv.h b/src/providers/ad/ad_srv.h +index e553d594d..c03ac873f 100644 +--- a/src/providers/ad/ad_srv.h ++++ b/src/providers/ad/ad_srv.h +@@ -53,4 +53,18 @@ char *ad_site_dns_discovery_domain(TALLOC_CTX *mem_ctx, + const char *site, + const char *domain); + ++struct tevent_req *ad_cldap_ping_send(TALLOC_CTX *mem_ctx, ++ struct tevent_context *ev, ++ struct sdap_options *opts, ++ struct be_resolv_ctx *be_res, ++ enum host_database *host_db, ++ const char *ad_domain, ++ const char *discovery_domain, ++ const char *current_site); ++ ++errno_t ad_cldap_ping_recv(TALLOC_CTX *mem_ctx, ++ struct tevent_req *req, ++ const char **_site, ++ const char **_forest); ++ + #endif /* __AD_SRV_H__ */ +-- +2.26.3 + + +From 27f394bb477aadb879c55df8e956fd37fa810109 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Pavel=20B=C5=99ezina?= +Date: Tue, 25 Aug 2020 12:11:19 +0200 +Subject: [PATCH 4/7] ad: if all in-site dc are unreachable try off-site + controllers + +Previous implementation would not fallback to the off-site domain +controllers. This would cause problems if the site actually changed. + +Reviewed-by: Sumit Bose +(cherry picked from commit fcfd834c9d80d7690f938582335d81231a5f6e60) + +Reviewed-by: Sumit Bose +--- + src/providers/ad/ad_cldap_ping.c | 227 ++++++++++++++++++++++++------- + 1 file changed, 181 insertions(+), 46 deletions(-) + +diff --git a/src/providers/ad/ad_cldap_ping.c b/src/providers/ad/ad_cldap_ping.c +index 5fc1a4d20..7ecdcdbef 100644 +--- a/src/providers/ad/ad_cldap_ping.c ++++ b/src/providers/ad/ad_cldap_ping.c +@@ -415,7 +415,7 @@ static errno_t ad_cldap_ping_parallel_recv(TALLOC_CTX *mem_ctx, + return EOK; + } + +-struct ad_cldap_ping_state { ++struct ad_cldap_ping_domain_state { + struct tevent_context *ev; + struct sdap_options *opts; + struct be_resolv_ctx *be_res; +@@ -428,25 +428,25 @@ struct ad_cldap_ping_state { + const char *forest; + }; + +-static void ad_cldap_ping_discovery_done(struct tevent_req *subreq); +-static void ad_cldap_ping_done(struct tevent_req *subreq); ++static void ad_cldap_ping_domain_discovery_done(struct tevent_req *subreq); ++static void ad_cldap_ping_domain_done(struct tevent_req *subreq); + +-struct tevent_req *ad_cldap_ping_send(TALLOC_CTX *mem_ctx, +- struct tevent_context *ev, +- struct sdap_options *opts, +- struct be_resolv_ctx *be_res, +- enum host_database *host_db, +- const char *ad_domain, +- const char *discovery_domain, +- const char *current_site) ++static struct tevent_req * ++ad_cldap_ping_domain_send(TALLOC_CTX *mem_ctx, ++ struct tevent_context *ev, ++ struct sdap_options *opts, ++ struct be_resolv_ctx *be_res, ++ enum host_database *host_db, ++ const char *ad_domain, ++ const char *discovery_domain) + { +- struct ad_cldap_ping_state *state; ++ struct ad_cldap_ping_domain_state *state; + struct tevent_req *subreq; + struct tevent_req *req; + const char **domains; + errno_t ret; + +- req = tevent_req_create(mem_ctx, &state, struct ad_cldap_ping_state); ++ req = tevent_req_create(mem_ctx, &state, struct ad_cldap_ping_domain_state); + if (req == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "tevent_req_create() failed\n"); + return NULL; +@@ -458,25 +458,18 @@ struct tevent_req *ad_cldap_ping_send(TALLOC_CTX *mem_ctx, + state->host_db = host_db; + state->ad_domain = ad_domain; + +- domains = talloc_zero_array(state, const char *, 3); ++ domains = talloc_zero_array(state, const char *, 2); + if (domains == NULL) { + ret = ENOMEM; + goto done; + } + +- if (current_site == NULL) { +- domains[0] = discovery_domain; +- domains[1] = NULL; +- } else { +- domains[0] = ad_site_dns_discovery_domain(state, current_site, +- discovery_domain); +- domains[1] = discovery_domain; +- +- if (domains[0] == NULL) { +- DEBUG(SSSDBG_CRIT_FAILURE, "Out of memory!"); +- ret = ENOMEM; +- goto done; +- } ++ domains[0] = discovery_domain; ++ domains[1] = NULL; ++ if (domains[0] == NULL) { ++ DEBUG(SSSDBG_CRIT_FAILURE, "Out of memory!"); ++ ret = ENOMEM; ++ goto done; + } + + /* Even though we use CLDAP (UDP) to perform the ping we need to discover +@@ -489,7 +482,7 @@ struct tevent_req *ad_cldap_ping_send(TALLOC_CTX *mem_ctx, + goto done; + } + +- tevent_req_set_callback(subreq, ad_cldap_ping_discovery_done, req); ++ tevent_req_set_callback(subreq, ad_cldap_ping_domain_discovery_done, req); + + return req; + +@@ -500,15 +493,15 @@ done: + return req; + } + +-static void ad_cldap_ping_discovery_done(struct tevent_req *subreq) ++static void ad_cldap_ping_domain_discovery_done(struct tevent_req *subreq) + { +- struct ad_cldap_ping_state *state; ++ struct ad_cldap_ping_domain_state *state; + struct tevent_req *req; + char *domain; + errno_t ret; + + req = tevent_req_callback_data(subreq, struct tevent_req); +- state = tevent_req_data(req, struct ad_cldap_ping_state); ++ state = tevent_req_data(req, struct ad_cldap_ping_domain_state); + + ret = fo_discover_srv_recv(state, subreq, &domain, NULL, &state->dc_list, + &state->dc_count); +@@ -529,7 +522,7 @@ static void ad_cldap_ping_discovery_done(struct tevent_req *subreq) + goto done; + } + +- tevent_req_set_callback(subreq, ad_cldap_ping_done, req); ++ tevent_req_set_callback(subreq, ad_cldap_ping_domain_done, req); + + done: + if (ret != EOK) { +@@ -538,41 +531,183 @@ done: + } + } + +-static void ad_cldap_ping_done(struct tevent_req *subreq) ++static void ad_cldap_ping_domain_done(struct tevent_req *subreq) + { +- struct ad_cldap_ping_state *state; ++ struct ad_cldap_ping_domain_state *state; + struct tevent_req *req; + errno_t ret; + + req = tevent_req_callback_data(subreq, struct tevent_req); +- state = tevent_req_data(req, struct ad_cldap_ping_state); ++ state = tevent_req_data(req, struct ad_cldap_ping_domain_state); + + ret = ad_cldap_ping_parallel_recv(state, subreq, &state->site, + &state->forest); + talloc_zfree(subreq); + if (ret != EOK) { +- DEBUG(SSSDBG_OP_FAILURE, +- "Unable to get site and forest information [%d]: %s\n", +- ret, sss_strerror(ret)); ++ tevent_req_error(req, ret); ++ return; ++ } ++ ++ tevent_req_done(req); ++} ++ ++static errno_t ad_cldap_ping_domain_recv(TALLOC_CTX *mem_ctx, ++ struct tevent_req *req, ++ const char **_site, ++ const char **_forest) ++{ ++ struct ad_cldap_ping_domain_state *state = NULL; ++ state = tevent_req_data(req, struct ad_cldap_ping_domain_state); ++ ++ TEVENT_REQ_RETURN_ON_ERROR(req); ++ ++ *_site = talloc_steal(mem_ctx, state->site); ++ *_forest = talloc_steal(mem_ctx, state->forest); ++ ++ return EOK; ++} ++ ++struct ad_cldap_ping_state { ++ struct tevent_context *ev; ++ struct sdap_options *opts; ++ struct be_resolv_ctx *be_res; ++ enum host_database *host_db; ++ const char *ad_domain; ++ const char *discovery_domain; ++ bool all_tried; ++ ++ const char *site; ++ const char *forest; ++}; ++ ++static errno_t ad_cldap_ping_step(struct tevent_req *req, ++ const char *domain); ++static void ad_cldap_ping_done(struct tevent_req *subreq); ++ ++struct tevent_req *ad_cldap_ping_send(TALLOC_CTX *mem_ctx, ++ struct tevent_context *ev, ++ struct sdap_options *opts, ++ struct be_resolv_ctx *be_res, ++ enum host_database *host_db, ++ const char *ad_domain, ++ const char *discovery_domain, ++ const char *current_site) ++{ ++ struct ad_cldap_ping_state *state; ++ struct tevent_req *req; ++ const char *domain; ++ errno_t ret; ++ ++ req = tevent_req_create(mem_ctx, &state, struct ad_cldap_ping_state); ++ if (req == NULL) { ++ DEBUG(SSSDBG_CRIT_FAILURE, "tevent_req_create() failed\n"); ++ return NULL; ++ } ++ ++ state->ev = ev; ++ state->opts = opts; ++ state->be_res = be_res; ++ state->host_db = host_db; ++ state->ad_domain = ad_domain; ++ state->discovery_domain = discovery_domain; ++ ++ /* If possible, lookup the information in the current site first. */ ++ if (current_site != NULL) { ++ state->all_tried = false; ++ domain = ad_site_dns_discovery_domain(state, current_site, ++ discovery_domain); ++ if (domain == NULL) { ++ DEBUG(SSSDBG_CRIT_FAILURE, "Out of memory!"); ++ ret = ENOMEM; ++ goto done; ++ } ++ } else { ++ state->all_tried = true; ++ domain = discovery_domain; ++ } ++ ++ ret = ad_cldap_ping_step(req, domain); ++ if (ret != EOK) { + goto done; + } + +- DEBUG(SSSDBG_TRACE_FUNC, "Found site: %s\n", state->site); +- DEBUG(SSSDBG_TRACE_FUNC, "Found forest: %s\n", state->forest); ++ return req; + + done: +- if (ret != EOK) { +- tevent_req_error(req, ret); ++ tevent_req_error(req, ret); ++ tevent_req_post(req, ev); ++ ++ return req; ++} ++ ++static errno_t ad_cldap_ping_step(struct tevent_req *req, ++ const char *domain) ++{ ++ struct ad_cldap_ping_state *state; ++ struct tevent_req *subreq; ++ struct timeval tv; ++ int timeout; ++ ++ state = tevent_req_data(req, struct ad_cldap_ping_state); ++ ++ subreq = ad_cldap_ping_domain_send(state, state->ev, state->opts, ++ state->be_res, state->host_db, ++ state->ad_domain, domain); ++ if (subreq == NULL) { ++ DEBUG(SSSDBG_CRIT_FAILURE, "Out of memory!"); ++ return ENOMEM; ++ } ++ ++ tevent_req_set_callback(subreq, ad_cldap_ping_done, req); ++ ++ timeout = dp_opt_get_int(state->be_res->opts, ++ DP_RES_OPT_RESOLVER_OP_TIMEOUT); ++ if (timeout > 0) { ++ tv = tevent_timeval_current_ofs(timeout, 0); ++ tevent_req_set_endtime(subreq, state->ev, tv); ++ } ++ ++ return EOK; ++} ++ ++static void ad_cldap_ping_done(struct tevent_req *subreq) ++{ ++ struct ad_cldap_ping_state *state; ++ struct tevent_req *req; ++ errno_t ret; ++ ++ req = tevent_req_callback_data(subreq, struct tevent_req); ++ state = tevent_req_data(req, struct ad_cldap_ping_state); ++ ++ ret = ad_cldap_ping_domain_recv(state, subreq, &state->site, ++ &state->forest); ++ talloc_zfree(subreq); ++ if (ret == EOK) { ++ DEBUG(SSSDBG_TRACE_FUNC, "Found site: %s\n", state->site); ++ DEBUG(SSSDBG_TRACE_FUNC, "Found forest: %s\n", state->forest); ++ tevent_req_done(req); + return; + } + +- tevent_req_done(req); ++ if (!state->all_tried) { ++ state->all_tried = true; ++ ret = ad_cldap_ping_step(req, state->discovery_domain); ++ if (ret == EOK) { ++ return; ++ } ++ } ++ ++ DEBUG(SSSDBG_OP_FAILURE, ++ "Unable to get site and forest information [%d]: %s\n", ++ ret, sss_strerror(ret)); ++ ++ tevent_req_error(req, ret); + } + + errno_t ad_cldap_ping_recv(TALLOC_CTX *mem_ctx, +- struct tevent_req *req, +- const char **_site, +- const char **_forest) ++ struct tevent_req *req, ++ const char **_site, ++ const char **_forest) + { + struct ad_cldap_ping_state *state = NULL; + state = tevent_req_data(req, struct ad_cldap_ping_state); +-- +2.26.3 + + +From 8249161d967a37847f42b62e0e6a384d063884af Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Pavel=20B=C5=99ezina?= +Date: Tue, 25 Aug 2020 13:43:32 +0200 +Subject: [PATCH 5/7] ad: renew site information only when SSSD was previously + offline + +Site and forest information is stable not dynamic. To avoid spamming +network with cldap pings all the time we will renew netlogon information +only when SSSD starts and when we are recovering from an offline state +to detect possible change (e.g. user moves to another location with laptop). + +Reviewed-by: Sumit Bose +(cherry picked from commit 9fdf5cfacd1a425691d44db53897096887bb3e6f) + +Reviewed-by: Sumit Bose +--- + src/providers/ad/ad_cldap_ping.c | 45 ++++++++++++++++---------- + src/providers/ad/ad_srv.c | 54 +++++++++++++++++++++----------- + src/providers/ad/ad_srv.h | 22 ++++++++----- + 3 files changed, 80 insertions(+), 41 deletions(-) + +diff --git a/src/providers/ad/ad_cldap_ping.c b/src/providers/ad/ad_cldap_ping.c +index 7ecdcdbef..dc25f6670 100644 +--- a/src/providers/ad/ad_cldap_ping.c ++++ b/src/providers/ad/ad_cldap_ping.c +@@ -586,12 +586,8 @@ static void ad_cldap_ping_done(struct tevent_req *subreq); + + struct tevent_req *ad_cldap_ping_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, +- struct sdap_options *opts, +- struct be_resolv_ctx *be_res, +- enum host_database *host_db, +- const char *ad_domain, +- const char *discovery_domain, +- const char *current_site) ++ struct ad_srv_plugin_ctx *srv_ctx, ++ const char *discovery_domain) + { + struct ad_cldap_ping_state *state; + struct tevent_req *req; +@@ -604,17 +600,30 @@ struct tevent_req *ad_cldap_ping_send(TALLOC_CTX *mem_ctx, + return NULL; + } + ++ if (!srv_ctx->renew_site) { ++ state->site = srv_ctx->current_site; ++ state->forest = srv_ctx->current_forest; ++ DEBUG(SSSDBG_TRACE_FUNC, ++ "CLDAP ping is not necessary, using site '%s' and forest '%s'\n", ++ state->site != NULL ? state->site : "unknown", ++ state->forest != NULL ? state->forest : "unknown"); ++ ret = EOK; ++ goto done; ++ } ++ ++ DEBUG(SSSDBG_TRACE_FUNC, "Sending CLDAP ping\n"); ++ + state->ev = ev; +- state->opts = opts; +- state->be_res = be_res; +- state->host_db = host_db; +- state->ad_domain = ad_domain; ++ state->opts = srv_ctx->opts; ++ state->be_res = srv_ctx->be_res; ++ state->host_db = srv_ctx->host_dbs; ++ state->ad_domain = srv_ctx->ad_domain; + state->discovery_domain = discovery_domain; + + /* If possible, lookup the information in the current site first. */ +- if (current_site != NULL) { ++ if (srv_ctx->current_site != NULL) { + state->all_tried = false; +- domain = ad_site_dns_discovery_domain(state, current_site, ++ domain = ad_site_dns_discovery_domain(state, srv_ctx->current_site, + discovery_domain); + if (domain == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "Out of memory!"); +@@ -634,7 +643,11 @@ struct tevent_req *ad_cldap_ping_send(TALLOC_CTX *mem_ctx, + return req; + + done: +- tevent_req_error(req, ret); ++ if (ret != EOK) { ++ tevent_req_error(req, ret); ++ } else { ++ tevent_req_done(req); ++ } + tevent_req_post(req, ev); + + return req; +@@ -705,9 +718,9 @@ static void ad_cldap_ping_done(struct tevent_req *subreq) + } + + errno_t ad_cldap_ping_recv(TALLOC_CTX *mem_ctx, +- struct tevent_req *req, +- const char **_site, +- const char **_forest) ++ struct tevent_req *req, ++ const char **_site, ++ const char **_forest) + { + struct ad_cldap_ping_state *state = NULL; + state = tevent_req_data(req, struct ad_cldap_ping_state); +diff --git a/src/providers/ad/ad_srv.c b/src/providers/ad/ad_srv.c +index d12f0971c..e58c19aac 100644 +--- a/src/providers/ad/ad_srv.c ++++ b/src/providers/ad/ad_srv.c +@@ -116,16 +116,13 @@ static errno_t ad_sort_servers_by_dns(TALLOC_CTX *mem_ctx, + return EOK; + } + +-struct ad_srv_plugin_ctx { +- struct be_ctx *be_ctx; +- struct be_resolv_ctx *be_res; +- enum host_database *host_dbs; +- struct sdap_options *opts; +- const char *hostname; +- const char *ad_domain; +- const char *ad_site_override; +- const char *current_site; +-}; ++static void ad_srv_mark_renew_site(void *pvt) ++{ ++ struct ad_srv_plugin_ctx *ctx; ++ ++ ctx = talloc_get_type(pvt, struct ad_srv_plugin_ctx); ++ ctx->renew_site = true; ++} + + struct ad_srv_plugin_ctx * + ad_srv_plugin_ctx_init(TALLOC_CTX *mem_ctx, +@@ -149,6 +146,7 @@ ad_srv_plugin_ctx_init(TALLOC_CTX *mem_ctx, + ctx->be_res = be_res; + ctx->host_dbs = host_dbs; + ctx->opts = opts; ++ ctx->renew_site = true; + + ctx->hostname = talloc_strdup(ctx, hostname); + if (ctx->hostname == NULL) { +@@ -181,6 +179,12 @@ ad_srv_plugin_ctx_init(TALLOC_CTX *mem_ctx, + } + } + ++ ret = be_add_offline_cb(ctx, be_ctx, ad_srv_mark_renew_site, ctx, NULL); ++ if (ret != EOK) { ++ DEBUG(SSSDBG_CRIT_FAILURE, "be_add_offline_cb failed.\n"); ++ goto fail; ++ } ++ + return ctx; + + fail: +@@ -190,11 +194,26 @@ fail: + + static errno_t + ad_srv_plugin_ctx_switch_site(struct ad_srv_plugin_ctx *ctx, +- const char *new_site) ++ const char *new_site, ++ const char *new_forest) + { + const char *site; ++ const char *forest; + errno_t ret; + ++ /* Switch forest. */ ++ if (new_forest != NULL ++ && (ctx->current_forest == NULL ++ || strcmp(ctx->current_forest, new_forest) != 0)) { ++ forest = talloc_strdup(ctx, new_forest); ++ if (forest == NULL) { ++ return ENOMEM; ++ } ++ ++ talloc_zfree(ctx->current_forest); ++ ctx->current_forest = forest; ++ } ++ + if (new_site == NULL) { + return EOK; + } +@@ -302,12 +321,7 @@ struct tevent_req *ad_srv_plugin_send(TALLOC_CTX *mem_ctx, + goto immediately; + } + +- DEBUG(SSSDBG_TRACE_FUNC, "About to find domain controllers\n"); +- +- subreq = ad_cldap_ping_send(state, ev, ctx->opts, ctx->be_res, +- ctx->host_dbs, ctx->ad_domain, +- state->discovery_domain, +- state->ctx->current_site); ++ subreq = ad_cldap_ping_send(state, ev, state->ctx, state->discovery_domain); + if (subreq == NULL) { + ret = ENOMEM; + goto immediately; +@@ -363,13 +377,17 @@ static void ad_srv_plugin_ping_done(struct tevent_req *subreq) + /* Remember current site so it can be used during next lookup so + * we can contact directory controllers within a known reachable + * site first. */ +- ret = ad_srv_plugin_ctx_switch_site(state->ctx, state->site); ++ ret = ad_srv_plugin_ctx_switch_site(state->ctx, state->site, ++ state->forest); + if (ret != EOK) { + DEBUG(SSSDBG_CRIT_FAILURE, "Unable to set site [%d]: %s\n", + ret, sss_strerror(ret)); + goto done; + } + ++ /* Do not renew the site again unless we go offline. */ ++ state->ctx->renew_site = false; ++ + if (strcmp(state->service, "gc") == 0) { + if (state->forest != NULL) { + if (state->site != NULL) { +diff --git a/src/providers/ad/ad_srv.h b/src/providers/ad/ad_srv.h +index c03ac873f..3c6a779ea 100644 +--- a/src/providers/ad/ad_srv.h ++++ b/src/providers/ad/ad_srv.h +@@ -21,7 +21,19 @@ + #ifndef __AD_SRV_H__ + #define __AD_SRV_H__ + +-struct ad_srv_plugin_ctx; ++struct ad_srv_plugin_ctx { ++ struct be_ctx *be_ctx; ++ struct be_resolv_ctx *be_res; ++ enum host_database *host_dbs; ++ struct sdap_options *opts; ++ const char *hostname; ++ const char *ad_domain; ++ const char *ad_site_override; ++ const char *current_site; ++ const char *current_forest; ++ ++ bool renew_site; ++}; + + struct ad_srv_plugin_ctx * + ad_srv_plugin_ctx_init(TALLOC_CTX *mem_ctx, +@@ -55,12 +67,8 @@ char *ad_site_dns_discovery_domain(TALLOC_CTX *mem_ctx, + + struct tevent_req *ad_cldap_ping_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, +- struct sdap_options *opts, +- struct be_resolv_ctx *be_res, +- enum host_database *host_db, +- const char *ad_domain, +- const char *discovery_domain, +- const char *current_site); ++ struct ad_srv_plugin_ctx *srv_ctx, ++ const char *discovery_domain); + + errno_t ad_cldap_ping_recv(TALLOC_CTX *mem_ctx, + struct tevent_req *req, +-- +2.26.3 + + +From 08fde220baab823f53fd359746c7e75eaeb0580f Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Pavel=20B=C5=99ezina?= +Date: Wed, 30 Sep 2020 13:45:43 +0200 +Subject: [PATCH 6/7] tevent: correctly handle req timeout error + +Reviewed-by: Sumit Bose +(cherry picked from commit f0d650799d4390f90890d17c56a4e395e931d8cb) + +Reviewed-by: Sumit Bose +--- + src/util/util.h | 14 +++++++++----- + 1 file changed, 9 insertions(+), 5 deletions(-) + +diff --git a/src/util/util.h b/src/util/util.h +index 94c2e6e3b..2cc7f98a3 100644 +--- a/src/util/util.h ++++ b/src/util/util.h +@@ -139,13 +139,17 @@ extern int dbus_activated; + \ + if (tevent_req_is_error(req, &TRROEstate, &TRROEuint64)) { \ + TRROEerr = (errno_t)TRROEuint64; \ +- if (TRROEstate == TEVENT_REQ_USER_ERROR) { \ +- if (TRROEerr == 0) { \ ++ switch (TRROEstate) { \ ++ case TEVENT_REQ_USER_ERROR: \ ++ if (TRROEerr == 0) { \ ++ return ERR_INTERNAL; \ ++ } \ ++ return TRROEerr; \ ++ case TEVENT_REQ_TIMED_OUT: \ ++ return ETIMEDOUT; \ ++ default: \ + return ERR_INTERNAL; \ +- } \ +- return TRROEerr; \ + } \ +- return ERR_INTERNAL; \ + } \ + } while (0) + +-- +2.26.3 + + +From a431a977852dde6af194f9306291d7fcc2624cb6 Mon Sep 17 00:00:00 2001 +From: Sumit Bose +Date: Mon, 5 Oct 2020 10:58:49 +0200 +Subject: [PATCH 7/7] ad: fix handling of current site and forest in cldap ping +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +The current site and forest are stored in a long living context and we +have to make sure that they are not moved to a different talloc parent +with a shorter lifetime. To achieve this the values are copied at the +start of a new cldap ping although it is expected that the values won't +change. + +Resolves: https://github.com/SSSD/sssd/issues/3743 + +Reviewed-by: Pavel Březina +(cherry picked from commit 37ba37a425453d8222584176ae5975a795422091) + +Reviewed-by: Sumit Bose +--- + src/providers/ad/ad_cldap_ping.c | 14 +++++++++++--- + 1 file changed, 11 insertions(+), 3 deletions(-) + +diff --git a/src/providers/ad/ad_cldap_ping.c b/src/providers/ad/ad_cldap_ping.c +index dc25f6670..ab234f4d7 100644 +--- a/src/providers/ad/ad_cldap_ping.c ++++ b/src/providers/ad/ad_cldap_ping.c +@@ -601,8 +601,16 @@ struct tevent_req *ad_cldap_ping_send(TALLOC_CTX *mem_ctx, + } + + if (!srv_ctx->renew_site) { +- state->site = srv_ctx->current_site; +- state->forest = srv_ctx->current_forest; ++ state->site = talloc_strdup(state, srv_ctx->current_site); ++ state->forest = talloc_strdup(state, srv_ctx->current_forest); ++ if ((srv_ctx->current_site != NULL && state->site == NULL) ++ || (srv_ctx->current_forest != NULL && state->forest == NULL)) { ++ DEBUG(SSSDBG_OP_FAILURE, ++ "Failed to copy current site or forest name.\n"); ++ ret = ENOMEM; ++ goto done; ++ } ++ + DEBUG(SSSDBG_TRACE_FUNC, + "CLDAP ping is not necessary, using site '%s' and forest '%s'\n", + state->site != NULL ? state->site : "unknown", +@@ -731,4 +739,4 @@ errno_t ad_cldap_ping_recv(TALLOC_CTX *mem_ctx, + *_forest = talloc_steal(mem_ctx, state->forest); + + return EOK; +-} +\ No newline at end of file ++} +-- +2.26.3 + diff --git a/sssd.spec b/sssd.spec index 50e2eb2..f78d819 100644 --- a/sssd.spec +++ b/sssd.spec @@ -50,7 +50,7 @@ Name: sssd Version: 1.16.5 -Release: 10%{?dist} +Release: 10%{?dist}.10 Group: Applications/System Summary: System Security Services Daemon License: GPLv3+ @@ -97,6 +97,41 @@ Patch0035: 0035-ad-add-ad_check_domain_-send-recv.patch Patch0036: 0036-ad-check-forest-root-directly-if-not-present-on-loca.patch Patch0037: 0037-pam_sss-add-SERVICE_IS_GDM_SMARTCARD.patch Patch0038: 0038-pam_sss-special-handling-for-gdm-smartcard.patch +Patch0039: 0039-BE_REFRESH-Do-not-try-to-refresh-domains-from-other-.patch +Patch0040: 0040-UTIL-DN-sanitization.patch +Patch0041: 0041-UTIL-Use-sss_sanitize_dn-where-we-deal-with-DN.patch +Patch0042: 0042-UTIL-Use-sss_sanitize_dn-where-we-deal-with-DN-2.patch +Patch0043: 0043-ldap-use-member-DN-to-create-ghost-user-hash-table.patch +Patch0044: 0044-SYSDB-merge_res_sysdb_attrs-fixed-to-avoid-NULL-ptr-.patch +Patch0045: 0045-DEBUG-journal_send-was-made-static.patch +Patch0046: 0046-DEBUG-fixes-program-identifier-as-seen-in-syslog.patch +Patch0047: 0047-ifp-fix-use-after-free.patch +Patch0048: 0048-fp-fix-original-fix-use-after-free-1.16-version.patch +Patch0049: 0049-krb5-only-try-pkinit-with-Smartcard-credentials.patch +Patch0050: 0050-negcache-make-sure-domain-config-does-not-leak-into-.patch +Patch0051: 0051-utils-add-SSS_GND_SUBDOMAINS-flag-for-get_next_domai.patch +Patch0052: 0052-negcache-make-sure-short-names-are-added-to-sub-doma.patch +Patch0053: 0053-negcache-do-not-use-default_domain_suffix.patch +Patch0054: 0054-ad-add-ad_allow_remote_domain_local_groups.patch +Patch0055: 0055-man-fix-description-of-dns_resolver_op_timeout.patch +Patch0056: 0056-man-fix-description-of-dns_resolver_timeout.patch +Patch0057: 0057-failover-add-dns_resolver_server_timeout-option.patch +Patch0058: 0058-nss-check-if-groups-are-filtered-during-initgroups.patch +Patch0059: 0059-CACHE-Create-timestamp-if-missing.patch +Patch0060: 0060-TESTS-Add-test-for-recreating-cache-timestamp.patch +Patch0061: 0061-cert-matching.patch +Patch0062: 0062-UTIL-find_domain_by_object_name_ex-changed-log-level.patch +Patch0063: 0063-sudo-do-not-search-by-low-usn-value-to-improve-perfo.patch +Patch0064: 0064-ldap-fix-modifytimestamp-debugging-leftovers.patch +Patch0065: 0065-sss_domain_info-add-not_found_counter.patch +Patch0066: 0066-AD-read-trusted-domains-from-local-domain-as-well.patch +Patch0067: 0067-negcache-use-right-domain-in-nss_protocol_fill_initg.patch +Patch0068: 0068-ldap-retry-ldap_install_tls-when-watchdog-interrupti.patch +Patch0069: 0069-SYSDB-Add-search-index-originalADgidNumber.patch +Patch0070: 0070-AD-do-not-override-LDAP-data-during-GC-lookups.patch +Patch0071: 0071-simple-fix-memory-leak-while-reloading-lists.patch +Patch0072: 0072-TOOLS-replace-system-with-execvp.patch +Patch0073: 0073-cldap.patch #Those patches should not be removed in RHEL-7 Patch0999: 0999-NOUPSTREAM-Default-to-root-if-sssd-user-is-not-spec @@ -1130,6 +1165,7 @@ getent passwd sssd >/dev/null || useradd -r -g sssd -d / -s /sbin/nologin -c "Us %pre common getent group sssd >/dev/null || groupadd -r sssd getent passwd sssd >/dev/null || useradd -r -g sssd -d / -s /sbin/nologin -c "User for sssd" sssd +rm -f /var/tmp/sssd_is_running /bin/systemctl is-active --quiet sssd.service && touch /var/tmp/sssd_is_running || : %post common @@ -1271,39 +1307,92 @@ systemctl try-restart sssd >/dev/null 2>&1 || : } %changelog -* Fri Jun 05 2020 Alexey Tikhonov 1.16.5-10 +* Wed Aug 11 2021 Alexey Tikhonov 1.16.5-10.10 +- Resolves: rhbz#1973796 - SSSD is NOT able to contact the Global Catalog when local site is down + +* Mon Aug 09 2021 Alexey Tikhonov 1.16.5-10.9 +- Resolves: rhbz#1988463 - Missing search index for `originalADgidNumber` [rhel-7.9.z] +- Resolves: rhbz#1968330 - id lookup is failing intermittently +- Resolves: rhbz#1964415 - Memory leak in the simple access provider +- Resolves: rhbz#1985457 - EMBARGOED CVE-2021-3621 sssd: shell command injection in sssctl [rhel-7.9.z] + +* Mon Apr 26 2021 Alexey Tikhonov 1.16.5-10.8 +- Resolves: rhbz#1910131 - sssd throwing error " Unable to parse name test' [1432158283]: The internal name format cannot be parsed" at debug_level 2 [rhel-7.9.z] +- Resolves: rhbz#1922244 - First smart refresh query contains modifyTimestamp even if the modifyTimestamp is 0. [rhel-7.9.z] +- Resolves: rhbz#1935685 - SSSD not detecting subdomain from AD forest (7.9z) +- Resolves: rhbz#1945552 - IPA missing secondary IPA Posix groups in latest sssd 1.16.5-10.el7_9.7 [rhel-7.9.z] +- Resolves: rhbz#1839972 - Authentication handshake (ldap_install_tls()) fails due to underlying openssl operation failing with EINTR [rhel-7.9.z] + +* Thu Dec 10 2020 Alexey Tikhonov 1.16.5-10.7 +- Resolves: rhbz#1875514 - filter_groups option partially filters the group from 'id' output of the user because gidNumber still appears in 'id' output [rhel-7.9.z] +- Resolves: rhbz#1772513 - SSSD is generating lot of LDAP queries in a very large environment [rhel-7.9.z] +- Resolves: rhbz#1736845 - [RFE] Backporting certificate matching rules for files, AD and LDAP provider [rhel-7.9.z] + +* Wed Nov 25 2020 Alexey Tikhonov 1.16.5-10.6 +- Resolves: rhbz#1899593 - sssd_be segfaults at be_refresh_get_values_ex() due to NULL ptrs in results of sysdb_search_with_ts_attr() [rhel-7.9.z] +- Resolves: rhbz#1888409 - sssd component logging is now too generic in syslog/journal [rhel-7.9.z] +- Resolves: rhbz#1852659 - sssd service is starting even though it is disabled state [rhel-7.9.z] +- Resolves: rhbz#1893443 - User lookups over the InfoPipe responder fail intermittently [rhel-7.9.z] +- Resolves: rhbz#1871288 - krb5_child denies ssh users when pki device detected [rhel-7.9.z] +- Resolves: rhbz#1853703 - Unexpected behavior and issue with filter_users/filter_groups option [rhel-7.9.z] +- Resolves: rhbz#1756240 - [RfE] Implement a new sssd.conf option to disable the filter for AD domain local groups from trusted domains [rhel-7.9.z] +- Resolves: rhbz#1851112 - LDAP bind can fail due to unconfigurable DNS server timeouts that inhibit SSSD failover [rhel-7.9.z] + +* Fri Aug 28 2020 Alexey Tikhonov 1.16.5-10.5 +- Resolves: rhbz#1859554 - Secondary LDAP group go missing from 'id' command on RHEL 7.8 with sssd-1.16.2-37.el7_8.1 [rhel-7.9.z] + (Previous attempt to fix this issue was incomplete (again)) +- just bumping the version to build for proper target + +* Fri Aug 28 2020 Alexey Tikhonov 1.16.5-10.4 +- Resolves: rhbz#1859554 - Secondary LDAP group go missing from 'id' command on RHEL 7.8 with sssd-1.16.2-37.el7_8.1 [rhel-7.9.z] + (Previous attempt to fix this issue was incomplete (again)) + +* Wed Aug 19 2020 Alexey Tikhonov 1.16.5-10.3 +- Resolves: rhbz#1859554 - Secondary LDAP group go missing from 'id' command on RHEL 7.8 with sssd-1.16.2-37.el7_8.1 [rhel-7.9.z] + (Previous attempt to fix this issue was incomplete) + +* Tue Aug 18 2020 Alexey Tikhonov 1.16.5-10.2 +- Resolves: rhbz#1854317 - sssd crashes after last update to sssd-common-1.16.4-37.el7_8.1 with servers configured with multiple domains [rhel-7.9.z] +- Resolves: rhbz#1859554 - Secondary LDAP group go missing from 'id' command on RHEL 7.8 with sssd-1.16.2-37.el7_8.1 [rhel-7.9.z] +- just bumping the version to build for proper target + +* Tue Aug 18 2020 Alexey Tikhonov 1.16.5-10.1 +- Resolves: rhbz#1854317 - sssd crashes after last update to sssd-common-1.16.4-37.el7_8.1 with servers configured with multiple domains [rhel-7.9.z] +- Resolves: rhbz#1859554 - Secondary LDAP group go missing from 'id' command on RHEL 7.8 with sssd-1.16.2-37.el7_8.1 [rhel-7.9.z] + +* Fri Jun 05 2020 Alexey Tikhonov 1.16.5-10 - Resolves: rhbz#1804005 - sssd doesn't follow the link order of AD Group Policy Management - Resolves: rhbz#1773409 - sssd is failing to discover other subdomains in the forest if LDAP entries do not contain AD forest root information - Resolves: rhbz#1551077 - GDM failure loop when no user mapped for smart card - Resolves: rhbz#1507683 - GDM password prompt when cert mapped to multiple users and promptusername is False -* Fri May 29 2020 Alexey Tikhonov 1.16.5-9 +* Fri May 29 2020 Alexey Tikhonov 1.16.5-9 - Resolves: rhbz#1796873 - [sssd] RHEL 7.9 Tier 0 Localization -* Wed May 27 2020 Alexey Tikhonov 1.16.5-8 +* Wed May 27 2020 Alexey Tikhonov 1.16.5-8 - Resolves: rhbz#1553784 - Document how to prevent invalid selinux context for default home directories in SSSD-AD direct integration. - Resolves: rhbz#1836910 - Rhel7.7 server have an issue regarding dyndns update for PTR-records which is done by sssd on active directory DNS servers. It is done in two steps (two different nsupdate messages). -* Thu May 21 2020 Alexey Tikhonov 1.16.5-7 +* Thu May 21 2020 Alexey Tikhonov 1.16.5-7 - Resolves: rhbz#1835813 - sssd boots offline if symlink for /etc/resolv.conf is broken/missing - Resolves: rhbz#1837545 - Users must be informed better when internal WATCHDOG terminates process. -* Wed May 20 2020 Alexey Tikhonov 1.16.5-6 +* Wed May 20 2020 Alexey Tikhonov 1.16.5-6 - Resolves: rhbz#1819013 - pam_sss reports PAM_CRED_ERR when providing wrong password for an existing IPA user, but this error's description is misleading - Resolves: rhbz#1800571 - Multiples Kerberos ticket on RHEL 7.7 after lock and unlock screen -* Tue May 19 2020 Alexey Tikhonov 1.16.5-5 +* Tue May 19 2020 Alexey Tikhonov 1.16.5-5 - Resolves: rhbz#1834266 - "off-by-one error" in watchdog implementation -* Mon May 11 2020 Alexey Tikhonov 1.16.5-4 +* Mon May 11 2020 Alexey Tikhonov 1.16.5-4 - Resolves: rhbz#1829806 - [Bug] Reduce logging about flat names - Resolves: rhbz#1800564 - `sssd.api.conf` and `sssd.api.d` should belong to `python-sssdconfig` package -* Thu May 07 2020 Alexey Tikhonov 1.16.5-3 +* Thu May 07 2020 Alexey Tikhonov 1.16.5-3 - Resolves: rhbz#1683946 - sssd or sssd-ad not updating their dependencies on "yum update" which breaks working setup -* Thu Apr 23 2020 Alexey Tikhonov 1.16.5-2 +* Thu Apr 23 2020 Alexey Tikhonov 1.16.5-2 - Resolves: rhbz#1513371 - [abrt] [faf] sssd: raise(): /usr/libexec/sssd/sssd_be[PROXY] killed by 6 - Resolves: rhbz#1568083 - subdomain lookup fails when certmaprule contains DN - Resolves: rhbz#1781539 - PKINIT with KCM does not work @@ -1314,7 +1403,7 @@ systemctl try-restart sssd >/dev/null 2>&1 || : - Resolves: rhbz#1822461 - background refresh task does not refresh updated netgroup entries - Added missing 'Requires' to resolves some of rpmdiff tool warnings -* Fri Mar 20 2020 Alexey Tikhonov 1.16.5-1 +* Fri Mar 20 2020 Alexey Tikhonov 1.16.5-1 - Resolves: rhbz#1796352 - Rebase SSSD for RHEL 7.9 * Wed Mar 18 2020 Michal Židek - 1.16.4-38 -- Gitee