From: Ralph Boehme Date: Thu, 24 Jul 2025 13:49:19 +0000 (+0200) Subject: idmap_ad: add and use ldap_timeout and fix LDAP server failover X-Git-Tag: samba-4.22.4~6 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=5e685641fcc0b8aaaa2cb3acd4945dcaed9412d3;p=thirdparty%2Fsamba.git idmap_ad: add and use ldap_timeout and fix LDAP server failover The key parts are: 1. If an LDAP search fails with the hardcoded fatal error, remove the retry. That would only retry the query against the same server, taken from the DCINFO cache key. Instead, force a DC rediscovery. 2. Set a default ldap_timeout and pass it to tldap_search(). This avoids tldap_search() hanging forever on a stale TCP connection. 3. The LDAP server idmap_ad is using is not necessarily the same DC we're using for RPC, so in case we learn about a dead DC, put it in the negative-conn-cache. BUG: https://bugzilla.samba.org/show_bug.cgi?id=15844 Signed-off-by: Ralph Boehme Reviewed-by: Guenther Deschner (cherry picked from commit 4d69ec473b7be763399c9787eda8e659a1582184) --- diff --git a/source3/winbindd/idmap_ad.c b/source3/winbindd/idmap_ad.c index 38e902b8292..0644b844df1 100644 --- a/source3/winbindd/idmap_ad.c +++ b/source3/winbindd/idmap_ad.c @@ -50,6 +50,7 @@ struct idmap_ad_context { bool unix_primary_group; bool unix_nss_info; + int ldap_timeout; struct ldb_context *ldb; struct ldb_dn **deny_ous; @@ -576,6 +577,8 @@ static NTSTATUS idmap_ad_context_create(TALLOC_CTX *mem_ctx, domname, "unix_primary_group", false); ctx->unix_nss_info = idmap_config_bool( domname, "unix_nss_info", false); + ctx->ldap_timeout = idmap_config_int( + domname, "ldap_timeout", 10); schema_mode = idmap_config_const_string( domname, "schema_mode", "rfc2307"); @@ -742,7 +745,7 @@ static NTSTATUS idmap_ad_query_user(struct idmap_domain *domain, rc = tldap_search(ctx->ld, ctx->default_nc, TLDAP_SCOPE_SUB, filter, attrs, ARRAY_SIZE(attrs), 0, NULL, 0, NULL, 0, - 0, 0, 0, talloc_tos(), &msgs); + ctx->ldap_timeout, 0, 0, talloc_tos(), &msgs); if (!TLDAP_RC_IS_SUCCESS(rc)) { return NT_STATUS_LDAP(TLDAP_RC_V(rc)); } @@ -815,13 +818,17 @@ static NTSTATUS idmap_ad_query_user_retry(struct idmap_domain *domain, { const NTSTATUS status_server_down = NT_STATUS_LDAP(TLDAP_RC_V(TLDAP_SERVER_DOWN)); + const NTSTATUS status_timeout = + NT_STATUS_LDAP(TLDAP_RC_V(TLDAP_TIMEOUT)); NTSTATUS status; status = idmap_ad_query_user(domain, info); - if (NT_STATUS_EQUAL(status, status_server_down)) { + if (NT_STATUS_EQUAL(status, status_server_down) || + NT_STATUS_EQUAL(status, status_timeout)) + { TALLOC_FREE(domain->private_data); - status = idmap_ad_query_user(domain, info); + return NT_STATUS_HOST_UNREACHABLE; } return status; @@ -978,7 +985,7 @@ static NTSTATUS idmap_ad_unixids_to_sids(struct idmap_domain *dom, rc = tldap_search(ctx->ld, ctx->default_nc, TLDAP_SCOPE_SUB, filter, attrs, ARRAY_SIZE(attrs), 0, NULL, 0, NULL, 0, - 0, 0, 0, talloc_tos(), &msgs); + ctx->ldap_timeout, 0, 0, talloc_tos(), &msgs); if (!TLDAP_RC_IS_SUCCESS(rc)) { return NT_STATUS_LDAP(TLDAP_RC_V(rc)); } @@ -1142,7 +1149,7 @@ static NTSTATUS idmap_ad_sids_to_unixids(struct idmap_domain *dom, rc = tldap_search(ctx->ld, ctx->default_nc, TLDAP_SCOPE_SUB, filter, attrs, ARRAY_SIZE(attrs), 0, NULL, 0, NULL, 0, - 0, 0, 0, talloc_tos(), &msgs); + ctx->ldap_timeout, 0, 0, talloc_tos(), &msgs); if (!TLDAP_RC_IS_SUCCESS(rc)) { return NT_STATUS_LDAP(TLDAP_RC_V(rc)); } @@ -1249,13 +1256,17 @@ static NTSTATUS idmap_ad_unixids_to_sids_retry(struct idmap_domain *dom, { const NTSTATUS status_server_down = NT_STATUS_LDAP(TLDAP_RC_V(TLDAP_SERVER_DOWN)); + const NTSTATUS status_timeout = + NT_STATUS_LDAP(TLDAP_RC_V(TLDAP_TIMEOUT)); NTSTATUS status; status = idmap_ad_unixids_to_sids(dom, ids); - if (NT_STATUS_EQUAL(status, status_server_down)) { + if (NT_STATUS_EQUAL(status, status_server_down) || + NT_STATUS_EQUAL(status, status_timeout)) + { TALLOC_FREE(dom->private_data); - status = idmap_ad_unixids_to_sids(dom, ids); + return NT_STATUS_HOST_UNREACHABLE; } return status; @@ -1266,13 +1277,17 @@ static NTSTATUS idmap_ad_sids_to_unixids_retry(struct idmap_domain *dom, { const NTSTATUS status_server_down = NT_STATUS_LDAP(TLDAP_RC_V(TLDAP_SERVER_DOWN)); + const NTSTATUS status_timeout = + NT_STATUS_LDAP(TLDAP_RC_V(TLDAP_TIMEOUT)); NTSTATUS status; status = idmap_ad_sids_to_unixids(dom, ids); - if (NT_STATUS_EQUAL(status, status_server_down)) { + if (NT_STATUS_EQUAL(status, status_server_down) || + NT_STATUS_EQUAL(status, status_timeout)) + { TALLOC_FREE(dom->private_data); - status = idmap_ad_sids_to_unixids(dom, ids); + return NT_STATUS_HOST_UNREACHABLE; } return status; diff --git a/source3/winbindd/wb_queryuser.c b/source3/winbindd/wb_queryuser.c index db8e946ba71..0f318f8b631 100644 --- a/source3/winbindd/wb_queryuser.c +++ b/source3/winbindd/wb_queryuser.c @@ -279,6 +279,7 @@ static void wb_queryuser_done(struct tevent_req *subreq) NTSTATUS status, result; bool need_group_name = false; const char *tmpl = NULL; + uint32_t dsgetdcname_flags = DS_RETURN_DNS_NAME; status = dcerpc_wbint_GetNssInfo_recv(subreq, info, &result); TALLOC_FREE(subreq); @@ -287,6 +288,13 @@ static void wb_queryuser_done(struct tevent_req *subreq) return; } + if (NT_STATUS_EQUAL(result, NT_STATUS_HOST_UNREACHABLE)) { + winbind_idmap_add_failed_connection_entry(info->domain_name); + /* Trigger DC lookup and reconnect below */ + result = NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND; + dsgetdcname_flags |= DS_FORCE_REDISCOVERY; + } + if (NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND) && !state->tried_dclookup) { const char *domain_name = find_dns_domain_name( @@ -301,7 +309,7 @@ static void wb_queryuser_done(struct tevent_req *subreq) domain_name, NULL, NULL, - DS_RETURN_DNS_NAME); + dsgetdcname_flags); if (tevent_req_nomem(subreq, req)) { return; } diff --git a/source3/winbindd/wb_sids2xids.c b/source3/winbindd/wb_sids2xids.c index 03e5e7e0258..f5ff9223034 100644 --- a/source3/winbindd/wb_sids2xids.c +++ b/source3/winbindd/wb_sids2xids.c @@ -598,6 +598,7 @@ static void wb_sids2xids_done(struct tevent_req *subreq) NTSTATUS status, result; const struct wbint_TransIDArray *src = NULL; struct wbint_TransIDArray *dst = NULL; + uint32_t dsgetdcname_flags = DS_RETURN_DNS_NAME; uint32_t si; status = dcerpc_wbint_Sids2UnixIDs_recv(subreq, state, &result); @@ -608,6 +609,15 @@ static void wb_sids2xids_done(struct tevent_req *subreq) return; } + if (NT_STATUS_EQUAL(result, NT_STATUS_HOST_UNREACHABLE)) { + struct lsa_DomainInfo *d = + &state->idmap_doms.domains[state->dom_index]; + winbind_idmap_add_failed_connection_entry(d->name.string); + /* Trigger DC lookup and reconnect below */ + result = NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND; + dsgetdcname_flags |= DS_FORCE_REDISCOVERY; + } + if (NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND) && !state->tried_dclookup) { @@ -627,7 +637,7 @@ static void wb_sids2xids_done(struct tevent_req *subreq) domain_name, NULL, NULL, - DS_RETURN_DNS_NAME); + dsgetdcname_flags); if (tevent_req_nomem(subreq, req)) { return; } diff --git a/source3/winbindd/wb_xids2sids.c b/source3/winbindd/wb_xids2sids.c index 6fcf524d94f..0384740d17d 100644 --- a/source3/winbindd/wb_xids2sids.c +++ b/source3/winbindd/wb_xids2sids.c @@ -130,6 +130,7 @@ static void wb_xids2sids_dom_done(struct tevent_req *subreq) struct wb_xids2sids_dom_state *state = tevent_req_data( req, struct wb_xids2sids_dom_state); const struct wb_parent_idmap_config_dom *dom_map = state->dom_map; + uint32_t dsgetdcname_flags = DS_RETURN_DNS_NAME; NTSTATUS status, result; size_t i; size_t dom_sid_idx; @@ -140,6 +141,13 @@ static void wb_xids2sids_dom_done(struct tevent_req *subreq) return; } + if (NT_STATUS_EQUAL(result, NT_STATUS_HOST_UNREACHABLE)) { + winbind_idmap_add_failed_connection_entry(dom_map->name); + /* Trigger DC lookup and reconnect below */ + result = NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND; + dsgetdcname_flags |= DS_FORCE_REDISCOVERY; + } + if (NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND) && !state->tried_dclookup) { @@ -151,7 +159,7 @@ static void wb_xids2sids_dom_done(struct tevent_req *subreq) domain_name, NULL, NULL, - DS_RETURN_DNS_NAME); + dsgetdcname_flags); if (tevent_req_nomem(subreq, req)) { return; } diff --git a/source3/winbindd/winbindd_cm.c b/source3/winbindd/winbindd_cm.c index d52deccf430..2c18aeba060 100644 --- a/source3/winbindd/winbindd_cm.c +++ b/source3/winbindd/winbindd_cm.c @@ -88,6 +88,8 @@ #include "lib/util/string_wrappers.h" #include "lib/global_contexts.h" #include "librpc/gen_ndr/ndr_winbind_c.h" +#include "source3/libsmb/namequery.h" +#include "source3/libsmb/dsgetdcname.h" #undef DBGC_CLASS #define DBGC_CLASS DBGC_WINBIND @@ -336,6 +338,56 @@ void winbind_add_failed_connection_entry( winbindd_unset_locator_kdc_env(domain); } +void winbind_idmap_add_failed_connection_entry(const char *_domain_name) +{ + struct netr_DsRGetDCNameInfo *dcinfo = NULL; + const char *dc_unc = NULL; + const char *dc_address = NULL; + char *domain_name = NULL; + struct winbindd_domain *domain = NULL; + NTSTATUS failed_status = NT_STATUS_HOST_UNREACHABLE; + NTSTATUS status; + + domain_name = talloc_strdup_upper(talloc_tos(), _domain_name); + if (domain_name == NULL) { + DBG_ERR("talloc_strdup_upper failed\n"); + return; + } + + status = wb_dsgetdcname_gencache_get(talloc_tos(), domain_name, &dcinfo); + if (!NT_STATUS_IS_OK(status)) { + DBG_DEBUG("Missing DC cache for domain '%s'\n", domain_name); + goto done; + } + + dc_unc = dcinfo->dc_unc; + while (dc_unc[0] == '\\') { + dc_unc++; + } + dc_address = dcinfo->dc_address; + while (dc_address[0] == '\\') { + dc_address++; + } + + add_failed_connection_entry(domain_name, dc_unc, failed_status); + add_failed_connection_entry(domain_name, dc_address, failed_status); + + domain = find_domain_from_name_noinit(domain_name); + if (domain == NULL) { + goto done; + } + if (domain->alt_name == NULL) { + goto done; + } + + add_failed_connection_entry(domain->alt_name, dc_unc, failed_status); + add_failed_connection_entry(domain->alt_name, dc_address, failed_status); + +done: + TALLOC_FREE(domain_name); + TALLOC_FREE(dcinfo); +} + /* Choose between anonymous or authenticated connections. We need to use an authenticated connection if DCs have the RestrictAnonymous registry entry set > 0, or the "Additional restrictions for anonymous diff --git a/source3/winbindd/winbindd_proto.h b/source3/winbindd/winbindd_proto.h index cf18266628c..3708fd2150c 100644 --- a/source3/winbindd/winbindd_proto.h +++ b/source3/winbindd/winbindd_proto.h @@ -210,6 +210,7 @@ void winbind_add_failed_connection_entry( const struct winbindd_domain *domain, const char *server, NTSTATUS result); +void winbind_idmap_add_failed_connection_entry(const char *domain_name); struct cli_credentials; NTSTATUS winbindd_get_trust_credentials(struct winbindd_domain *domain,