]> git.ipfire.org Git - thirdparty/samba.git/commitdiff
idmap_ad: add and use ldap_timeout and fix LDAP server failover
authorRalph Boehme <slow@samba.org>
Thu, 24 Jul 2025 13:49:19 +0000 (15:49 +0200)
committerGünther Deschner <gd@samba.org>
Wed, 13 Aug 2025 18:30:44 +0000 (18:30 +0000)
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 <slow@samba.org>
Reviewed-by: Guenther Deschner <gd@samba.org>
source3/winbindd/idmap_ad.c
source3/winbindd/wb_queryuser.c
source3/winbindd/wb_sids2xids.c
source3/winbindd/wb_xids2sids.c
source3/winbindd/winbindd_cm.c
source3/winbindd/winbindd_proto.h

index 38e902b82925ac54f9b84acf4709cd999af563e3..0644b844df15e7cedcaf40d235f2e280a14627e3 100644 (file)
@@ -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;
index db8e946ba71722bcb4856ba57d0a02ba2c5135ca..0f318f8b63120b9530286d6a619dddd4a589418e 100644 (file)
@@ -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;
                }
index 03e5e7e0258120e6a9d551790307c9a85c4e5f5d..f5ff9223034fafb8e6d59ad158ae6894b80bf940 100644 (file)
@@ -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;
                }
index 6fcf524d94fd98a3f69bcae62f5540ec01fc5252..0384740d17d17bb0c04cd379dc24b614b6efdf76 100644 (file)
@@ -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;
                }
index f45bb6cda99d3345d77a3c0fafa7d034db1b6da7..3963881ca4540df29722ea4a5a92f595b93baab8 100644 (file)
@@ -90,6 +90,8 @@
 #include "lib/global_contexts.h"
 #include "librpc/gen_ndr/ndr_winbind_c.h"
 #include "libsmb/smbsock_connect.h"
+#include "source3/libsmb/namequery.h"
+#include "source3/libsmb/dsgetdcname.h"
 
 #undef DBGC_CLASS
 #define DBGC_CLASS DBGC_WINBIND
@@ -338,6 +340,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
index 45e20ba3a81ec82e681da1f30f69b9dda73672a4..ae41923b244050281c89e4ef94df10cc7c2ac1a9 100644 (file)
@@ -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,