diff --git a/SOURCES/0069-SYSDB-Add-search-index-originalADgidNumber.patch b/SOURCES/0069-SYSDB-Add-search-index-originalADgidNumber.patch
new file mode 100644
index 0000000..b975313
--- /dev/null
+++ b/SOURCES/0069-SYSDB-Add-search-index-originalADgidNumber.patch
@@ -0,0 +1,165 @@
+From c4b1b8208cd26916dc99b29e09df071c5659b9d4 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Pawe=C5=82=20Po=C5=82awski?= <ppolawsk@redhat.com>
+Date: Wed, 7 Jul 2021 00:29:59 +0200
+Subject: [PATCH 69/71] SYSDB: Add search index "originalADgidNumber"
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Commit 03bc962 introduced a change which can result in
+unindexed search in some scenarios. The result is performance
+drop comparing to older SSSD version.
+
+This PR adds missing search index: originalADgidNumber
+
+:relnote: Add search index "originalADgidNumber" to SYSDB
+
+Resolves: https://github.com/SSSD/sssd/issues/5430
+
+Reviewed-by: Iker Pedrosa <ipedrosa@redhat.com>
+Reviewed-by: Tomáš Halman <thalman@redhat.com>
+
+(cherry picked with changes from commit 17e339d58c57861c093fc53b241873dce00ae958)
+
+Reviewed-by: Alexey Tikhonov <atikhono@redhat.com>
+---
+ src/db/sysdb.h         |  2 ++
+ src/db/sysdb_init.c    |  7 ++++++
+ src/db/sysdb_private.h |  5 +++-
+ src/db/sysdb_upgrade.c | 52 ++++++++++++++++++++++++++++++++++++++++++
+ 4 files changed, 65 insertions(+), 1 deletion(-)
+
+diff --git a/src/db/sysdb.h b/src/db/sysdb.h
+index d47099eff..c771ce633 100644
+--- a/src/db/sysdb.h
++++ b/src/db/sysdb.h
+@@ -176,6 +176,8 @@
+ #define OVERRIDE_PREFIX "override"
+ #define SYSDB_DEFAULT_OVERRIDE_NAME "defaultOverrideName"
+ 
++#define SYSDB_ORIG_AD_GID_NUMBER "originalADgidNumber"
++
+ #define SYSDB_AD_ACCOUNT_EXPIRES "adAccountExpires"
+ #define SYSDB_AD_USER_ACCOUNT_CONTROL "adUserAccountControl"
+ 
+diff --git a/src/db/sysdb_init.c b/src/db/sysdb_init.c
+index 48e21baab..3632d5a19 100644
+--- a/src/db/sysdb_init.c
++++ b/src/db/sysdb_init.c
+@@ -566,6 +566,13 @@ static errno_t sysdb_domain_cache_upgrade(TALLOC_CTX *mem_ctx,
+     }
+ 
+ 
++    if (strcmp(version, SYSDB_VERSION_0_21) == 0) {
++        ret = sysdb_upgrade_21(sysdb, &version);
++        if (ret != EOK) {
++            goto done;
++        }
++    }
++
+     ret = EOK;
+ done:
+     sysdb->ldb = save_ldb;
+diff --git a/src/db/sysdb_private.h b/src/db/sysdb_private.h
+index 0ccfa43ac..895cc4ea0 100644
+--- a/src/db/sysdb_private.h
++++ b/src/db/sysdb_private.h
+@@ -23,6 +23,7 @@
+ #ifndef __INT_SYS_DB_H__
+ #define __INT_SYS_DB_H__
+ 
++#define SYSDB_VERSION_0_22 "0.22"
+ #define SYSDB_VERSION_0_21 "0.21"
+ #define SYSDB_VERSION_0_20 "0.20"
+ #define SYSDB_VERSION_0_19 "0.19"
+@@ -45,7 +46,7 @@
+ #define SYSDB_VERSION_0_2 "0.2"
+ #define SYSDB_VERSION_0_1 "0.1"
+ 
+-#define SYSDB_VERSION SYSDB_VERSION_0_21
++#define SYSDB_VERSION SYSDB_VERSION_0_22
+ 
+ #define SYSDB_BASE_LDIF \
+      "dn: @ATTRIBUTES\n" \
+@@ -81,6 +82,7 @@
+      "@IDXATTR: mail\n" \
+      "@IDXATTR: userMappedCertificate\n" \
+      "@IDXATTR: ccacheFile\n" \
++     "@IDXATTR: originalADgidNumber\n" \
+      "\n" \
+      "dn: @MODULES\n" \
+      "@LIST: asq,memberof\n" \
+@@ -174,6 +176,7 @@ int sysdb_upgrade_17(struct sysdb_ctx *sysdb,
+ int sysdb_upgrade_18(struct sysdb_ctx *sysdb, const char **ver);
+ int sysdb_upgrade_19(struct sysdb_ctx *sysdb, const char **ver);
+ int sysdb_upgrade_20(struct sysdb_ctx *sysdb, const char **ver);
++int sysdb_upgrade_21(struct sysdb_ctx *sysdb, const char **ver);
+ 
+ int sysdb_ts_upgrade_01(struct sysdb_ctx *sysdb, const char **ver);
+ 
+diff --git a/src/db/sysdb_upgrade.c b/src/db/sysdb_upgrade.c
+index 392d04b07..6f160f520 100644
+--- a/src/db/sysdb_upgrade.c
++++ b/src/db/sysdb_upgrade.c
+@@ -2553,6 +2553,58 @@ done:
+     return ret;
+ }
+ 
++int sysdb_upgrade_21(struct sysdb_ctx *sysdb, const char **ver)
++{
++    struct upgrade_ctx *ctx;
++    errno_t ret;
++    struct ldb_message *msg = NULL;
++
++    ret = commence_upgrade(sysdb, sysdb->ldb, SYSDB_VERSION_0_22, &ctx);
++    if (ret) {
++        return ret;
++    }
++
++    /* Add missing indices */
++    msg = ldb_msg_new(ctx);
++    if (msg == NULL) {
++        ret = ENOMEM;
++        goto done;
++    }
++
++    msg->dn = ldb_dn_new(msg, sysdb->ldb, "@INDEXLIST");
++    if (msg->dn == NULL) {
++        ret = ENOMEM;
++        goto done;
++    }
++
++    ret = ldb_msg_add_empty(msg, "@IDXATTR", LDB_FLAG_MOD_ADD, NULL);
++    if (ret != LDB_SUCCESS) {
++        ret = ENOMEM;
++        goto done;
++    }
++
++    ret = ldb_msg_add_string(msg, "@IDXATTR", SYSDB_ORIG_AD_GID_NUMBER);
++    if (ret != LDB_SUCCESS) {
++        ret = ENOMEM;
++        goto done;
++    }
++
++    ret = ldb_modify(sysdb->ldb, msg);
++    if (ret != LDB_SUCCESS) {
++        ret = sysdb_error_to_errno(ret);
++        goto done;
++    }
++
++    talloc_free(msg);
++
++    /* conversion done, update version number */
++    ret = update_version(ctx);
++
++done:
++    ret = finish_upgrade(ret, &ctx, ver);
++    return ret;
++}
++
+ int sysdb_ts_upgrade_01(struct sysdb_ctx *sysdb, const char **ver)
+ {
+     struct upgrade_ctx *ctx;
+-- 
+2.26.3
+
diff --git a/SOURCES/0070-AD-do-not-override-LDAP-data-during-GC-lookups.patch b/SOURCES/0070-AD-do-not-override-LDAP-data-during-GC-lookups.patch
new file mode 100644
index 0000000..0ed8c6a
--- /dev/null
+++ b/SOURCES/0070-AD-do-not-override-LDAP-data-during-GC-lookups.patch
@@ -0,0 +1,66 @@
+From 7afd36a4c4b35d72742eec2d23bd6908e635c097 Mon Sep 17 00:00:00 2001
+From: Sumit Bose <sbose@redhat.com>
+Date: Fri, 19 Jun 2020 13:36:49 +0200
+Subject: [PATCH 70/71] AD: do not override LDAP data during GC lookups
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+The Global Catalog contains user and group information of the whole
+forest and hence any Global Catalog server can be used. Currently when a
+Global Catalog server is looked up the data of the LDAP server is
+overwritten as well. I guess the original intention was to use a single
+server for both services.
+
+However since the Global Catalog server can come from any domain in the
+forest this might overwrite the LDAP data of a DC from the local domain
+with the data from a AD of a remote domain and as a result lookups for
+users and groups from the local domain might fail since the remote DC
+does not has this information available at the LDAP port. In most cases
+this overwrite is hidden by a following lookup to find a KDC for
+authentication which is searched only in the local domain again where
+the LDAP data is overwritten again to make sure the same DC is used for
+LDAP and Kerberos communication. But depending on the connection
+timeouts and lifetime of Kerberos tickets the KDC lookup might be
+skipped because new credentials are not needed and as a result the wrong
+LDAP data is used.
+
+To avoid this the LDAP data is now only set if the current lookup is not
+a Global Catalog lookup.
+
+Resolves: https://github.com/SSSD/sssd/issues/5351
+
+Reviewed-by: Pavel Březina <pbrezina@redhat.com>
+(cherry picked from commit 5f3b9e1d45df77bca1b2665e67bbd73b26fafbc2)
+
+Reviewed-by: Alexey Tikhonov <atikhono@redhat.com>
+---
+ src/providers/ad/ad_common.c | 12 ++++++++----
+ 1 file changed, 8 insertions(+), 4 deletions(-)
+
+diff --git a/src/providers/ad/ad_common.c b/src/providers/ad/ad_common.c
+index 4e51d08e6..c99c4d110 100644
+--- a/src/providers/ad/ad_common.c
++++ b/src/providers/ad/ad_common.c
+@@ -942,10 +942,14 @@ ad_resolve_callback(void *private_data, struct fo_server *server)
+     }
+ 
+     /* free old one and replace with new one */
+-    talloc_zfree(service->sdap->uri);
+-    service->sdap->uri = new_uri;
+-    talloc_zfree(service->sdap->sockaddr);
+-    service->sdap->sockaddr = talloc_steal(service->sdap, sockaddr);
++    if (sdata == NULL || !sdata->gc) {
++        /* do not update LDAP data during GC lookups because the selected server
++         * might be from a different domain. */
++        talloc_zfree(service->sdap->uri);
++        service->sdap->uri = new_uri;
++        talloc_zfree(service->sdap->sockaddr);
++        service->sdap->sockaddr = talloc_steal(service->sdap, sockaddr);
++    }
+ 
+     talloc_zfree(service->gc->uri);
+     talloc_zfree(service->gc->sockaddr);
+-- 
+2.26.3
+
diff --git a/SOURCES/0071-simple-fix-memory-leak-while-reloading-lists.patch b/SOURCES/0071-simple-fix-memory-leak-while-reloading-lists.patch
new file mode 100644
index 0000000..0e2877d
--- /dev/null
+++ b/SOURCES/0071-simple-fix-memory-leak-while-reloading-lists.patch
@@ -0,0 +1,103 @@
+From 137924c7894fd5989446ebefd96010a0878004f1 Mon Sep 17 00:00:00 2001
+From: Sumit Bose <sbose@redhat.com>
+Date: Tue, 12 Jan 2021 16:40:56 +0100
+Subject: [PATCH 71/71] simple: fix memory leak while reloading lists
+
+The simple access provider will reload the access and deny lists at
+runtime to make sure that users and groups from domains which are
+discovered at runtime are properly processed.
+
+While reloading the lists the original lists are not freed and an
+intermediate list wasn't removed as well.
+
+Resolves: https://github.com/SSSD/sssd/issues/5456
+
+:fixes: Memory leak in the simple access provider
+
+Reviewed-by: Alexey Tikhonov <atikhono@redhat.com>
+(cherry picked from commit 19c2c641e669ee1c08d6706c132625dc30e64609)
+
+Reviewed-by: Alexey Tikhonov <atikhono@redhat.com>
+---
+ src/providers/simple/simple_access.c | 28 +++++++++++++++++++++-------
+ 1 file changed, 21 insertions(+), 7 deletions(-)
+
+diff --git a/src/providers/simple/simple_access.c b/src/providers/simple/simple_access.c
+index 1868569b1..49226adf2 100644
+--- a/src/providers/simple/simple_access.c
++++ b/src/providers/simple/simple_access.c
+@@ -117,17 +117,13 @@ int simple_access_obtain_filter_lists(struct simple_ctx *ctx)
+         const char *name;
+         const char *option;
+         char **orig_list;
+-        char ***ctx_list;
++        char **ctx_list;
+     } lists[] = {{"Allow users", CONFDB_SIMPLE_ALLOW_USERS, NULL, NULL},
+                  {"Deny users", CONFDB_SIMPLE_DENY_USERS, NULL, NULL},
+                  {"Allow groups", CONFDB_SIMPLE_ALLOW_GROUPS, NULL, NULL},
+                  {"Deny groups", CONFDB_SIMPLE_DENY_GROUPS, NULL, NULL},
+                  {NULL, NULL, NULL, NULL}};
+ 
+-    lists[0].ctx_list = &ctx->allow_users;
+-    lists[1].ctx_list = &ctx->deny_users;
+-    lists[2].ctx_list = &ctx->allow_groups;
+-    lists[3].ctx_list = &ctx->deny_groups;
+ 
+     ret = sysdb_master_domain_update(bectx->domain);
+     if (ret != EOK) {
+@@ -141,7 +137,6 @@ int simple_access_obtain_filter_lists(struct simple_ctx *ctx)
+                                         lists[i].option, &lists[i].orig_list);
+         if (ret == ENOENT) {
+             DEBUG(SSSDBG_FUNC_DATA, "%s list is empty.\n", lists[i].name);
+-            *lists[i].ctx_list = NULL;
+             continue;
+         } else if (ret != EOK) {
+             DEBUG(SSSDBG_CRIT_FAILURE, "confdb_get_string_as_list failed.\n");
+@@ -149,7 +144,8 @@ int simple_access_obtain_filter_lists(struct simple_ctx *ctx)
+         }
+ 
+         ret = simple_access_parse_names(ctx, bectx, lists[i].orig_list,
+-                                        lists[i].ctx_list);
++                                        &lists[i].ctx_list);
++        talloc_free(lists[i].orig_list);
+         if (ret != EOK) {
+             DEBUG(SSSDBG_CRIT_FAILURE, "Unable to parse %s list [%d]: %s\n",
+                                         lists[i].name, ret, sss_strerror(ret));
+@@ -157,6 +153,18 @@ int simple_access_obtain_filter_lists(struct simple_ctx *ctx)
+         }
+     }
+ 
++    talloc_free(ctx->allow_users);
++    ctx->allow_users = talloc_steal(ctx, lists[0].ctx_list);
++
++    talloc_free(ctx->deny_users);
++    ctx->deny_users = talloc_steal(ctx, lists[1].ctx_list);
++
++    talloc_free(ctx->allow_groups);
++    ctx->allow_groups = talloc_steal(ctx, lists[2].ctx_list);
++
++    talloc_free(ctx->deny_groups);
++    ctx->deny_groups = talloc_steal(ctx, lists[3].ctx_list);
++
+     if (!ctx->allow_users &&
+             !ctx->allow_groups &&
+             !ctx->deny_users &&
+@@ -165,9 +173,15 @@ int simple_access_obtain_filter_lists(struct simple_ctx *ctx)
+               "No rules supplied for simple access provider. "
+                "Access will be granted for all users.\n");
+     }
++
++
+     return EOK;
+ 
+ failed:
++    for (i = 0; lists[i].name != NULL; i++) {
++        talloc_free(lists[i].ctx_list);
++    }
++
+     return ret;
+ }
+ 
+-- 
+2.26.3
+
diff --git a/SOURCES/0072-TOOLS-replace-system-with-execvp.patch b/SOURCES/0072-TOOLS-replace-system-with-execvp.patch
new file mode 100644
index 0000000..a5ae447
--- /dev/null
+++ b/SOURCES/0072-TOOLS-replace-system-with-execvp.patch
@@ -0,0 +1,277 @@
+From a0d310e2c86facc8727b781e060b2b769313a5dd Mon Sep 17 00:00:00 2001
+From: Alexey Tikhonov <atikhono@redhat.com>
+Date: Fri, 30 Jul 2021 19:05:31 +0200
+Subject: [PATCH] TOOLS: replace system() with execvp() to avoid execution of
+ user supplied command
+
+A flaw was found in SSSD, where the sssctl command was vulnerable
+to shell command injection via the logs-fetch and cache-expire
+subcommands. This flaw allows an attacker to trick the root user
+into running a specially crafted sssctl command, such as via sudo,
+to gain root access. The highest threat from this vulnerability is
+to confidentiality, integrity, as well as system availability.
+
+:fixes: CVE-2021-3621
+---
+ src/tools/sssctl/sssctl.c      | 40 +++++++++++++++++-------
+ src/tools/sssctl/sssctl.h      |  2 +-
+ src/tools/sssctl/sssctl_data.c | 57 +++++++++++-----------------------
+ src/tools/sssctl/sssctl_logs.c | 31 ++++++++++++++----
+ 4 files changed, 73 insertions(+), 57 deletions(-)
+
+diff --git a/src/tools/sssctl/sssctl.c b/src/tools/sssctl/sssctl.c
+index 4a50a1d..bfc791b 100644
+--- a/src/tools/sssctl/sssctl.c
++++ b/src/tools/sssctl/sssctl.c
+@@ -97,22 +97,37 @@ sssctl_prompt(const char *message,
+     return SSSCTL_PROMPT_ERROR;
+ }
+ 
+-errno_t sssctl_run_command(const char *command)
++errno_t sssctl_run_command(const char *const argv[])
+ {
+     int ret;
++    int wstatus;
+ 
+-    DEBUG(SSSDBG_TRACE_FUNC, "Running %s\n", command);
++    DEBUG(SSSDBG_TRACE_FUNC, "Running '%s'\n", argv[0]);
+ 
+-    ret = system(command);
++    ret = fork();
+     if (ret == -1) {
+-        DEBUG(SSSDBG_CRIT_FAILURE, "Unable to execute %s\n", command);
+         fprintf(stderr, _("Error while executing external command\n"));
+         return EFAULT;
+-    } else if (WEXITSTATUS(ret) != 0) {
+-        DEBUG(SSSDBG_CRIT_FAILURE, "Command %s failed with [%d]\n",
+-              command, WEXITSTATUS(ret));
++    }
++
++    if (ret == 0) {
++        /* cast is safe - see
++        https://pubs.opengroup.org/onlinepubs/9699919799/functions/exec.html
++        "The statement about argv[] and envp[] being constants ... "
++        */
++        execvp(argv[0], discard_const_p(char * const, argv));
+         fprintf(stderr, _("Error while executing external command\n"));
+-        return EIO;
++        _exit(1);
++    } else {
++        if (waitpid(ret, &wstatus, 0) == -1) {
++            fprintf(stderr,
++                    _("Error while executing external command '%s'\n"), argv[0]);
++            return EFAULT;
++        } else if (WEXITSTATUS(wstatus) != 0) {
++            fprintf(stderr,
++                    _("Command '%s' failed with [%d]\n"), argv[0], WEXITSTATUS(wstatus));
++            return EIO;
++        }
+     }
+ 
+     return EOK;
+@@ -132,11 +147,14 @@ static errno_t sssctl_manage_service(enum sssctl_svc_action action)
+ #elif defined(HAVE_SERVICE)
+     switch (action) {
+     case SSSCTL_SVC_START:
+-        return sssctl_run_command(SERVICE_PATH" sssd start");
++        return sssctl_run_command(
++                      (const char *[]){SERVICE_PATH, "sssd", "start", NULL});
+     case SSSCTL_SVC_STOP:
+-        return sssctl_run_command(SERVICE_PATH" sssd stop");
++        return sssctl_run_command(
++                      (const char *[]){SERVICE_PATH, "sssd", "stop", NULL});
+     case SSSCTL_SVC_RESTART:
+-        return sssctl_run_command(SERVICE_PATH" sssd restart");
++        return sssctl_run_command(
++                      (const char *[]){SERVICE_PATH, "sssd", "restart", NULL});
+     }
+ #endif
+ 
+diff --git a/src/tools/sssctl/sssctl.h b/src/tools/sssctl/sssctl.h
+index 0115b24..599ef65 100644
+--- a/src/tools/sssctl/sssctl.h
++++ b/src/tools/sssctl/sssctl.h
+@@ -47,7 +47,7 @@ enum sssctl_prompt_result
+ sssctl_prompt(const char *message,
+               enum sssctl_prompt_result defval);
+ 
+-errno_t sssctl_run_command(const char *command);
++errno_t sssctl_run_command(const char *const argv[]); /* argv[0] - command */
+ bool sssctl_start_sssd(bool force);
+ bool sssctl_stop_sssd(bool force);
+ bool sssctl_restart_sssd(bool force);
+diff --git a/src/tools/sssctl/sssctl_data.c b/src/tools/sssctl/sssctl_data.c
+index cc46caf..8a04266 100644
+--- a/src/tools/sssctl/sssctl_data.c
++++ b/src/tools/sssctl/sssctl_data.c
+@@ -105,15 +105,15 @@ static errno_t sssctl_backup(bool force)
+         }
+     }
+ 
+-    ret = sssctl_run_command("sss_override user-export "
+-                             SSS_BACKUP_USER_OVERRIDES);
++    ret = sssctl_run_command((const char *[]){"sss_override", "user-export",
++                                              SSS_BACKUP_USER_OVERRIDES, NULL});
+     if (ret != EOK) {
+         fprintf(stderr, _("Unable to export user overrides\n"));
+         return ret;
+     }
+ 
+-    ret = sssctl_run_command("sss_override group-export "
+-                             SSS_BACKUP_GROUP_OVERRIDES);
++    ret = sssctl_run_command((const char *[]){"sss_override", "group-export",
++                                              SSS_BACKUP_GROUP_OVERRIDES, NULL});
+     if (ret != EOK) {
+         fprintf(stderr, _("Unable to export group overrides\n"));
+         return ret;
+@@ -158,8 +158,8 @@ static errno_t sssctl_restore(bool force_start, bool force_restart)
+     }
+ 
+     if (sssctl_backup_file_exists(SSS_BACKUP_USER_OVERRIDES)) {
+-        ret = sssctl_run_command("sss_override user-import "
+-                                 SSS_BACKUP_USER_OVERRIDES);
++        ret = sssctl_run_command((const char *[]){"sss_override", "user-import",
++                                                  SSS_BACKUP_USER_OVERRIDES, NULL});
+         if (ret != EOK) {
+             fprintf(stderr, _("Unable to import user overrides\n"));
+             return ret;
+@@ -167,8 +167,8 @@ static errno_t sssctl_restore(bool force_start, bool force_restart)
+     }
+ 
+     if (sssctl_backup_file_exists(SSS_BACKUP_USER_OVERRIDES)) {
+-        ret = sssctl_run_command("sss_override group-import "
+-                                 SSS_BACKUP_GROUP_OVERRIDES);
++        ret = sssctl_run_command((const char *[]){"sss_override", "group-import",
++                                                  SSS_BACKUP_GROUP_OVERRIDES, NULL});
+         if (ret != EOK) {
+             fprintf(stderr, _("Unable to import group overrides\n"));
+             return ret;
+@@ -296,40 +296,19 @@ errno_t sssctl_cache_expire(struct sss_cmdline *cmdline,
+                             void *pvt)
+ {
+     errno_t ret;
+-    char *cmd_args = NULL;
+-    const char *cachecmd = SSS_CACHE;
+-    char *cmd = NULL;
+-    int i;
+-
+-    if (cmdline->argc == 0) {
+-        ret = sssctl_run_command(cachecmd);
+-        goto done;
+-    }
+ 
+-    cmd_args = talloc_strdup(tool_ctx, "");
+-    if (cmd_args == NULL) {
+-        ret = ENOMEM;
+-        goto done;
++    const char **args = talloc_array_size(tool_ctx,
++                                          sizeof(char *),
++                                          cmdline->argc + 2);
++    if (!args) {
++        return ENOMEM;
+     }
++    memcpy(&args[1], cmdline->argv, sizeof(char *) * cmdline->argc);
++    args[0] = SSS_CACHE;
++    args[cmdline->argc + 1] = NULL;
+ 
+-    for (i = 0; i < cmdline->argc; i++) {
+-        cmd_args = talloc_strdup_append(cmd_args, cmdline->argv[i]);
+-        if (i != cmdline->argc - 1) {
+-            cmd_args = talloc_strdup_append(cmd_args, " ");
+-        }
+-    }
+-
+-    cmd = talloc_asprintf(tool_ctx, "%s %s", cachecmd, cmd_args);
+-    if (cmd == NULL) {
+-        ret = ENOMEM;
+-        goto done;
+-    }
+-
+-    ret = sssctl_run_command(cmd);
+-
+-done:
+-    talloc_free(cmd_args);
+-    talloc_free(cmd);
++    ret = sssctl_run_command(args);
+ 
++    talloc_free(args);
+     return ret;
+ }
+diff --git a/src/tools/sssctl/sssctl_logs.c b/src/tools/sssctl/sssctl_logs.c
+index aca988c..c85cc7a 100644
+--- a/src/tools/sssctl/sssctl_logs.c
++++ b/src/tools/sssctl/sssctl_logs.c
+@@ -32,6 +32,7 @@
+ #include <popt.h>
+ #include <stdio.h>
+ #include <signal.h>
++#include <glob.h>
+ 
+ #include "util/util.h"
+ #include "tools/common/sss_process.h"
+@@ -231,6 +232,7 @@ errno_t sssctl_logs_remove(struct sss_cmdline *cmdline,
+ {
+     struct sssctl_logs_opts opts = {0};
+     errno_t ret;
++    glob_t globbuf;
+ 
+     /* Parse command line. */
+     struct poptOption options[] = {
+@@ -254,8 +256,19 @@ errno_t sssctl_logs_remove(struct sss_cmdline *cmdline,
+ 
+         sss_signal(SIGHUP);
+     } else {
++        globbuf.gl_offs = 4;
++        ret = glob(LOG_PATH"/*.log", GLOB_ERR|GLOB_DOOFFS, NULL, &globbuf);
++        if (ret != 0) {
++            DEBUG(SSSDBG_CRIT_FAILURE, "Unable to expand log files list\n");
++            return ret;
++        }
++        globbuf.gl_pathv[0] = discard_const_p(char, "truncate");
++        globbuf.gl_pathv[2] = discard_const_p(char, "--size");
++        globbuf.gl_pathv[3] = discard_const_p(char, "0");
++
+         printf(_("Truncating log files...\n"));
+-        ret = sssctl_run_command("truncate --size 0 " LOG_FILES);
++        ret = sssctl_run_command((const char * const*)globbuf.gl_pathv);
++        globfree(&globbuf);
+         if (ret != EOK) {
+             fprintf(stderr, _("Unable to truncate log files\n"));
+             return ret;
+@@ -270,8 +283,8 @@ errno_t sssctl_logs_fetch(struct sss_cmdline *cmdline,
+                           void *pvt)
+ {
+     const char *file;
+-    const char *cmd;
+     errno_t ret;
++    glob_t globbuf;
+ 
+     /* Parse command line. */
+     ret = sss_tool_popt_ex(cmdline, NULL, SSS_TOOL_OPT_OPTIONAL, NULL, NULL,
+@@ -281,13 +294,19 @@ errno_t sssctl_logs_fetch(struct sss_cmdline *cmdline,
+         return ret;
+     }
+ 
+-    cmd = talloc_asprintf(tool_ctx, "tar -czf %s %s", file, LOG_FILES);
+-    if (cmd == NULL) {
+-        fprintf(stderr, _("Out of memory!"));
++    globbuf.gl_offs = 3;
++    ret = glob(LOG_PATH"/*.log", GLOB_ERR|GLOB_DOOFFS, NULL, &globbuf);
++    if (ret != 0) {
++        DEBUG(SSSDBG_CRIT_FAILURE, "Unable to expand log files list\n");
++        return ret;
+     }
++    globbuf.gl_pathv[0] = discard_const_p(char, "tar");
++    globbuf.gl_pathv[1] = discard_const_p(char, "-czf");
++    globbuf.gl_pathv[2] = discard_const_p(char, file);
+ 
+     printf(_("Archiving log files into %s...\n"), file);
+-    ret = sssctl_run_command(cmd);
++    ret = sssctl_run_command((const char * const*)globbuf.gl_pathv);
++    globfree(&globbuf);
+     if (ret != EOK) {
+         fprintf(stderr, _("Unable to archive log files\n"));
+         return ret;
+-- 
+2.26.3
+
diff --git a/SOURCES/0073-cldap.patch b/SOURCES/0073-cldap.patch
new file mode 100644
index 0000000..18ba08f
--- /dev/null
+++ b/SOURCES/0073-cldap.patch
@@ -0,0 +1,2158 @@
+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
+
diff --git a/SPECS/sssd.spec b/SPECS/sssd.spec
index a9f72f5..f78d819 100644
--- a/SPECS/sssd.spec
+++ b/SPECS/sssd.spec
@@ -50,7 +50,7 @@
 
 Name: sssd
 Version: 1.16.5
-Release: 10%{?dist}.8
+Release: 10%{?dist}.10
 Group: Applications/System
 Summary: System Security Services Daemon
 License: GPLv3+
@@ -127,6 +127,11 @@ Patch0065: 0065-sss_domain_info-add-not_found_counter.patch
 Patch0066: 0066-AD-read-trusted-domains-from-local-domain-as-well.patch
 Patch0067: 0067-negcache-use-right-domain-in-nss_protocol_fill_initg.patch
 Patch0068: 0068-ldap-retry-ldap_install_tls-when-watchdog-interrupti.patch
+Patch0069: 0069-SYSDB-Add-search-index-originalADgidNumber.patch
+Patch0070: 0070-AD-do-not-override-LDAP-data-during-GC-lookups.patch
+Patch0071: 0071-simple-fix-memory-leak-while-reloading-lists.patch
+Patch0072: 0072-TOOLS-replace-system-with-execvp.patch
+Patch0073: 0073-cldap.patch
 
 #Those patches should not be removed in RHEL-7
 Patch0999: 0999-NOUPSTREAM-Default-to-root-if-sssd-user-is-not-spec
@@ -1302,6 +1307,15 @@ systemctl try-restart sssd >/dev/null 2>&1 || :
 }
 
 %changelog
+* Wed Aug 11 2021 Alexey Tikhonov <atikhono@redhat.com> 1.16.5-10.10
+- Resolves: rhbz#1973796 - SSSD is NOT able to contact the Global Catalog when local site is down
+
+* Mon Aug 09 2021 Alexey Tikhonov <atikhono@redhat.com> 1.16.5-10.9
+- Resolves: rhbz#1988463 - Missing search index for `originalADgidNumber` [rhel-7.9.z]
+- Resolves: rhbz#1968330 - id lookup is failing intermittently
+- Resolves: rhbz#1964415 - Memory leak in the simple access provider
+- Resolves: rhbz#1985457 - EMBARGOED CVE-2021-3621 sssd: shell command injection in sssctl [rhel-7.9.z]
+
 * Mon Apr 26 2021 Alexey Tikhonov <atikhono@redhat.com> 1.16.5-10.8
 - Resolves: rhbz#1910131 - sssd throwing error " Unable to parse name test' [1432158283]: The internal name format cannot be parsed" at debug_level 2 [rhel-7.9.z]
 - Resolves: rhbz#1922244 - First smart refresh query contains modifyTimestamp even if the modifyTimestamp is 0. [rhel-7.9.z]