Blob Blame History Raw
From db644a3a24c123a964129d41934365d8eb2174c7 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Pavel=20B=C5=99ezina?= <pbrezina@redhat.com>
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 <sbose@redhat.com>
(cherry picked from commit 414593cca65ed09fe4659e2786370a4553664cd0)

Reviewed-by: Sumit Bose <sbose@redhat.com>
---
 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?= <pbrezina@redhat.com>
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 <sbose@redhat.com>
(cherry picked from commit 8265674a055e5cdb57acebad72d935356408540a)

Reviewed-by: Sumit Bose <sbose@redhat.com>
---
 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?= <pbrezina@redhat.com>
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 <sbose@redhat.com>
(cherry picked from commit 1889ca60a9c642f0cca60b20a5b94de7a66924f6)

Reviewed-by: Sumit Bose <sbose@redhat.com>
---
 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 <pbrezina@redhat.com>
+
+    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 <http://www.gnu.org/licenses/>.
+*/
+
+#include <string.h>
+#include <talloc.h>
+#include <tevent.h>
+#include <ndr.h>
+#include <ndr/ndr_nbt.h>
+
+#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?= <pbrezina@redhat.com>
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 <sbose@redhat.com>
(cherry picked from commit fcfd834c9d80d7690f938582335d81231a5f6e60)

Reviewed-by: Sumit Bose <sbose@redhat.com>
---
 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?= <pbrezina@redhat.com>
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 <sbose@redhat.com>
(cherry picked from commit 9fdf5cfacd1a425691d44db53897096887bb3e6f)

Reviewed-by: Sumit Bose <sbose@redhat.com>
---
 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?= <pbrezina@redhat.com>
Date: Wed, 30 Sep 2020 13:45:43 +0200
Subject: [PATCH 6/7] tevent: correctly handle req timeout error

Reviewed-by: Sumit Bose <sbose@redhat.com>
(cherry picked from commit f0d650799d4390f90890d17c56a4e395e931d8cb)

Reviewed-by: Sumit Bose <sbose@redhat.com>
---
 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 <sbose@redhat.com>
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 <pbrezina@redhat.com>
(cherry picked from commit 37ba37a425453d8222584176ae5975a795422091)

Reviewed-by: Sumit Bose <sbose@redhat.com>
---
 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