[SSSD] [PATCH] Global Catalog lookups and Active Directory trusted domains

Jakub Hrozek jhrozek at redhat.com
Wed May 29 16:39:46 UTC 2013


Hi,

the attached patches implement lookups in Global Catalog and AD
subdomains. Unfortunately they turned out to be quite complex and I'd like
to make them shorter and remove some of the complexity during the review
if possible.

The patches implement identity lookups, authentication. There will be
additional patch that implements SRV discovery, but I wanted to get
the patches reviewed so that I don't waste time if the approach was not
correct. Authentication currently requires the subdomain to be present in
krb5.conf otherwise the Kerberos libraries would search for _kerberos-master,
not _kerberos and fail. Sumit, who kindly helped me debug this problem
might already have a MIT bug handy.

There is couple of LDAP provider patches that instead of using a single
connection towards LDAP allows for a linked list in the sdap_id_ctx.
Then the caller of the LDAP ID function can choose himself what
connection he would like to use for the particular lookup. For instance,
for trusted AD users, GC lookups are always used, but for native AD
users, LDAP can be used (and is actually preferred).

One part of the complexity comes from the fact that initially I wanted
the ID code to be flexible and allow "failover" between the connection.
In other words, the ID code could try GC lookup first and optionally
fail over to LDAP lookup using the same context if the entry was not
found using the first connection. 

But after testing the identities I'm not sure if that is actually needed. The
trusted users and groups have to be looked up from GC anyway and the local
users and groups would be available in LDAP. Maybe we could conserve
some resources by attempting to look up local users from GC and only
keep one connection open when possible, but I'm not quite sure it is
worth it. Removing the connection failover would get rid of having to
report the LDAP return code to AD identity functions and maybe we could
even tie the connection with sdap_domain, too.

So can anyone think of a lookup where we would like to try one
connection and then the other? Maybe groups that contain members from
both primary and trusted domains might be such a case, but there we
could handle the failover in the group request.

[PATCH 01/15] Do not obfuscate calls with booleans
This is purely a personal preference, but I found it hard to follow what
the various booleans meant when reading function calls. Maybe macros
would make the code more readable, but I'm OK with not applying this
patch if others disagree.



[PATCH 02/15] LDAP: sdap_id_ctx might contain several connections
With some LDAP server implementations, one server might provide different
"views" of the identites on different ports. One example is the Active
Directory Global catalog. The provider would contact different view
depending on which operation it is performing and against which SSSD domain.

At the same time, these views run on the same server, which means the same
server options, enumeration, cleanup or Kerberos service should be used.
So instead of using several different failover ports or several instances
of sdap_id_ctx, this patch introduces a new "struct sdap_id_conn_ctx" that
contains the connection cache to the particular view and an instance of
"struct sdap_options" that contains the URI.

No functional changes are present in this patch, currently all providers
use a single connection. Multiple connections will be used later in the
upcoming patches.



[PATCH 03/15] LDAP: Refactor account info handler into a tevent request
The sdap account handler was a function with its own private callback
that directly called the back end handlers. This patch refactors the
handler into a new tevent request that the current sdap handler calls.

This refactoring would allow the caller to specify a custom sdap
connection for use by the handler and optionally retry the same request
with another connection inside a single per-provider handler.

No functional changes are present in this patch.



[PATCH 04/15] LDAP: Pass in a connection to ID functions
Instead of using the default connection from the sdap_id_ctx, allow the
caller to specify which connection shall be used for this particular
request. Again, no functional change is present in this patch, just
another parameter is added.



[PATCH 05/15] LDAP: new SDAP domain structure
Previously an sdap_id_ctx was always tied to one domain with a single
set of search bases. But with the introduction of Global Catalog
lookups, primary domain and subdomains might have different search
bases.

This patch introduces a new structure sdap_domain that contains an sssd
domain or subdomain and a set of search bases. With this patch, there is
only one sdap_domain that describes the primary domain.



[PATCH 06/15] LDAP: return sdap search return code to ID
By default, the LDAP searches delete the entry from cache if it wasn't
found during a search. But if a search wants to try both Global Catalog
and LDAP, for example, it might be beneficial to have an option to only
delete the entry from cache after the last operation fails to prevent
unnecessary memberof operations for example.



[PATCH 07/15] Move domain_to_basedn outside IPA subtree
The utility function will be reused to guess search base from the base
DN of AD trusted domains.



[PATCH 08/15] LDAP: split a function to create search bases
This function will be used later to fill the sdap_domain structures with
search bases.



[PATCH 09/15] LDAP: store FQDNs for trusted users and groups
Because the NSS responder expects the name attribute to contain FQDN,
we must save the name as FQDN in the LDAP provider if the domain we save
to is a subdomain.



[PATCH 10/15] Split generating primary GID for ID mapped users into a
separate function
Move the part of sdap_save_user into a separate function so that it can
be special cased an only called for users in primary domains, not
subdomain users.



[PATCH 11/15] LDAP: Do not store separate GID for subdomain users
As the subdomains are MPG domains, we don't want to store a separate GID
for the subdomain users, but rather just create a UPG.



[PATCH 12/15] New utility function sss_get_domain_name
Instead of copying a block of code that checks whether domain is a subdomain
and uses only name of FQDN as appropriate, wrap the logic into a function.

This patch might need rebase on top of the flat name patches on the
list.



[PATCH 13/15] AD: Add additional service to support Global Catalog lookups
When fixed host names of AD servers are configured in the config file,
we can't know (unlike when service discovery is at play) if the servers
are Global Catalogs or not. This patch adds a private data to servers
read from the config file that denote whether the server can be tried for
contacting the Global Catalog port or just LDAP. The GC or LDAP URIs are
generated based on contents of this private data structure.

Because SSSD sticks to a working server, we don't have to disable or
remove the faulty GC servers from the list. Service lookups will be
supported in additional patch.



[PATCH 14/15] AD ID lookups - choose GC or LDAP as appropriate
https://fedorahosted.org/sssd/ticket/1557

Some lookups should be performed from GC only -- for example trusted
users are only present in the Global Catalog, while some lookups should be
performed from LDAP only as not all objects or attributes are replicated
to Global Catalog.

This patch adds a generic failover mechanism for identity lookups in the
AD provider that allows to choose the appropriate source and even fail
over to the other source if available.



[PATCH 15/15] AD: Store trusted AD domains as subdomains
https://fedorahosted.org/sssd/ticket/364

Looks up trusted domain objects in the LDAP and stores them as AD subdomains.

Currently only trusted domains that run NT5 or newe from the same forest
are looked up and stored.
-------------- next part --------------
>From 5dc30f6134e9ad5b98b71d07b12b696b1f6948c8 Mon Sep 17 00:00:00 2001
From: Jakub Hrozek <jhrozek at redhat.com>
Date: Sun, 12 May 2013 12:29:21 +0200
Subject: [PATCH 01/15] Do not obfuscate calls with booleans

---
 src/providers/ad/ad_common.c     | 19 ++++++++++++-------
 src/providers/ipa/ipa_common.c   | 19 ++++++++++++-------
 src/providers/krb5/krb5_common.c | 20 +++++++++++++-------
 src/providers/ldap/ldap_common.c | 25 +++++++++++++++----------
 4 files changed, 52 insertions(+), 31 deletions(-)

diff --git a/src/providers/ad/ad_common.c b/src/providers/ad/ad_common.c
index e34199959cb7ca3cd21f7d188a624e03fb12162e..e34b2db4cd7317ceebdc843eb5381183b83ad64f 100644
--- a/src/providers/ad/ad_common.c
+++ b/src/providers/ad/ad_common.c
@@ -140,12 +140,17 @@ done:
 static void
 ad_resolve_callback(void *private_data, struct fo_server *server);
 
+#define ad_primary_servers_init(mem_ctx, bectx, servers, options) \
+    _ad_servers_init(mem_ctx, bectx, servers, options, true)
+#define ad_backup_servers_init(mem_ctx, bectx, servers, options) \
+    _ad_servers_init(mem_ctx, bectx, servers, options, false)
+
 static errno_t
-ad_servers_init(TALLOC_CTX *mem_ctx,
-                struct be_ctx *bectx,
-                const char *servers,
-                struct ad_options *options,
-                bool primary)
+_ad_servers_init(TALLOC_CTX *mem_ctx,
+                 struct be_ctx *bectx,
+                 const char *servers,
+                 struct ad_options *options,
+                 bool primary)
 {
     size_t i;
     errno_t ret = 0;
@@ -286,13 +291,13 @@ ad_failover_init(TALLOC_CTX *mem_ctx, struct be_ctx *bectx,
         primary_servers = BE_SRV_IDENTIFIER;
     }
 
-    ret = ad_servers_init(mem_ctx, bectx, primary_servers, options, true);
+    ret = ad_primary_servers_init(mem_ctx, bectx, primary_servers, options);
     if (ret != EOK) {
         goto done;
     }
 
     if (backup_servers) {
-        ret = ad_servers_init(mem_ctx, bectx, backup_servers, options, false);
+        ret = ad_backup_servers_init(mem_ctx, bectx, backup_servers, options);
         if (ret != EOK) {
             goto done;
         }
diff --git a/src/providers/ipa/ipa_common.c b/src/providers/ipa/ipa_common.c
index 509b2abd0b9b2ff1af04b353e0a44df573e3790a..e371824840584056a6217b8b960159c03ce55fdd 100644
--- a/src/providers/ipa/ipa_common.c
+++ b/src/providers/ipa/ipa_common.c
@@ -754,11 +754,16 @@ static void ipa_resolve_callback(void *private_data, struct fo_server *server)
     talloc_free(tmp_ctx);
 }
 
-errno_t ipa_servers_init(struct be_ctx *ctx,
-                         struct ipa_service *service,
-                         struct ipa_options *options,
-                         const char *servers,
-                         bool primary)
+#define ipa_primary_servers_init(ctx, service, options, servers) \
+    _ipa_servers_init(ctx, service, options, servers, true)
+#define ipa_backup_servers_init(ctx, service, options, servers) \
+    _ipa_servers_init(ctx, service, options, servers, false)
+
+static errno_t _ipa_servers_init(struct be_ctx *ctx,
+                                 struct ipa_service *service,
+                                 struct ipa_options *options,
+                                 const char *servers,
+                                 bool primary)
 {
     TALLOC_CTX *tmp_ctx;
     char **list = NULL;
@@ -900,13 +905,13 @@ int ipa_service_init(TALLOC_CTX *memctx, struct be_ctx *ctx,
         primary_servers = BE_SRV_IDENTIFIER;
     }
 
-    ret = ipa_servers_init(ctx, service, options, primary_servers, true);
+    ret = ipa_primary_servers_init(ctx, service, options, primary_servers);
     if (ret != EOK) {
         goto done;
     }
 
     if (backup_servers) {
-        ret = ipa_servers_init(ctx, service, options, backup_servers, false);
+        ret = ipa_backup_servers_init(ctx, service, options, backup_servers);
         if (ret != EOK) {
             goto done;
         }
diff --git a/src/providers/krb5/krb5_common.c b/src/providers/krb5/krb5_common.c
index c6865c09960463b8648957cb777d0d2b4e7b1dde..6c26fcc11731dd437f45f2b411c918e3bb468712 100644
--- a/src/providers/krb5/krb5_common.c
+++ b/src/providers/krb5/krb5_common.c
@@ -465,11 +465,16 @@ static void krb5_resolve_callback(void *private_data, struct fo_server *server)
     return;
 }
 
-errno_t krb5_servers_init(struct be_ctx *ctx,
-                          struct krb5_service *service,
-                          const char *service_name,
-                          const char *servers,
-                          bool primary)
+#define krb5_primary_servers_init(ctx, service, service_name, servers) \
+    _krb5_servers_init(ctx, service, service_name, servers, true)
+#define krb5_backup_servers_init(ctx, service, service_name, servers) \
+    _krb5_servers_init(ctx, service, service_name, servers, false)
+
+static errno_t _krb5_servers_init(struct be_ctx *ctx,
+                                  struct krb5_service *service,
+                                  const char *service_name,
+                                  const char *servers,
+                                  bool primary)
 {
     TALLOC_CTX *tmp_ctx;
     char **list = NULL;
@@ -642,13 +647,14 @@ int krb5_service_init(TALLOC_CTX *memctx, struct be_ctx *ctx,
         primary_servers = BE_SRV_IDENTIFIER;
     }
 
-    ret = krb5_servers_init(ctx, service, service_name, primary_servers, true);
+    ret = krb5_primary_servers_init(ctx, service, service_name, primary_servers);
     if (ret != EOK) {
         goto done;
     }
 
     if (backup_servers) {
-        ret = krb5_servers_init(ctx, service, service_name, backup_servers, false);
+        ret = krb5_backup_servers_init(ctx, service, service_name,
+                                       backup_servers);
         if (ret != EOK) {
             goto done;
         }
diff --git a/src/providers/ldap/ldap_common.c b/src/providers/ldap/ldap_common.c
index acb24b190157efe06967963ad05014cf9017db7f..155019ec7a430b133dbdf6e57a6ec81a46f0dc0b 100644
--- a/src/providers/ldap/ldap_common.c
+++ b/src/providers/ldap/ldap_common.c
@@ -1194,12 +1194,17 @@ done:
     return ret;
 }
 
-errno_t sdap_urls_init(struct be_ctx *ctx,
-                       struct sdap_service *service,
-                       const char *service_name,
-                       const char *dns_service_name,
-                       const char *urls,
-                       bool primary)
+#define sdap_primary_urls_init(ctx, service, service_name, dns_service_name, urls) \
+    _sdap_urls_init(ctx, service, service_name, dns_service_name, urls, true)
+#define sdap_backup_urls_init(ctx, service, service_name, dns_service_name, urls) \
+    _sdap_urls_init(ctx, service, service_name, dns_service_name, urls, false)
+
+static errno_t _sdap_urls_init(struct be_ctx *ctx,
+                               struct sdap_service *service,
+                               const char *service_name,
+                               const char *dns_service_name,
+                               const char *urls,
+                               bool primary)
 {
     TALLOC_CTX *tmp_ctx;
     char *srv_user_data;
@@ -1337,15 +1342,15 @@ int sdap_service_init(TALLOC_CTX *memctx, struct be_ctx *ctx,
         urls = BE_SRV_IDENTIFIER;
     }
 
-    ret = sdap_urls_init(ctx, service, service_name, dns_service_name,
-                         urls, true);
+    ret = sdap_primary_urls_init(ctx, service, service_name, dns_service_name,
+                                 urls);
     if (ret != EOK) {
         goto done;
     }
 
     if (backup_urls) {
-        ret = sdap_urls_init(ctx, service, service_name, dns_service_name,
-                             backup_urls, false);
+        ret = sdap_backup_urls_init(ctx, service, service_name,
+                                    dns_service_name, backup_urls);
         if (ret != EOK) {
             goto done;
         }
-- 
1.8.2.1

-------------- next part --------------
>From bed8db62a72b731752e9c668a52c1494f35f9c78 Mon Sep 17 00:00:00 2001
From: Jakub Hrozek <jhrozek at redhat.com>
Date: Tue, 21 May 2013 17:18:03 +0200
Subject: [PATCH 02/15] LDAP: sdap_id_ctx might contain several connections

With some LDAP server implementations, one server might provide
different "views" of the identites on different ports. One example is
the Active Directory Global catalog. The provider would contact
different view depending on which operation it is performing and against
which SSSD domain.

At the same time, these views run on the same server, which means the same
server options, enumeration, cleanup or Kerberos service should be used.
So instead of using several different failover ports or several
instances of sdap_id_ctx, this patch introduces a new "struct
sdap_id_conn_ctx" that contains the connection cache to the particular
view and an instance of "struct sdap_options" that contains the URI.

No functional changes are present in this patch, currently all providers
use a single connection. Multiple connections will be used later in the
upcoming patches.
---
 src/providers/ad/ad_init.c            | 49 ++++++++++++---------------
 src/providers/ad/ad_subdomains.c      |  2 +-
 src/providers/ipa/ipa_access.c        |  2 +-
 src/providers/ipa/ipa_auth.c          |  3 +-
 src/providers/ipa/ipa_hostid.c        |  2 +-
 src/providers/ipa/ipa_id.c            |  2 +-
 src/providers/ipa/ipa_init.c          | 10 ++----
 src/providers/ipa/ipa_selinux.c       |  3 +-
 src/providers/ipa/ipa_subdomains.c    |  2 +-
 src/providers/ipa/ipa_subdomains_id.c |  2 +-
 src/providers/ldap/ldap_common.c      | 47 ++++++++++++++++++++++++++
 src/providers/ldap/ldap_common.h      | 32 ++++++++++++++----
 src/providers/ldap/ldap_id.c          | 10 +++---
 src/providers/ldap/ldap_id_enum.c     |  2 +-
 src/providers/ldap/ldap_id_netgroup.c |  2 +-
 src/providers/ldap/ldap_id_services.c |  2 +-
 src/providers/ldap/ldap_init.c        | 42 +++++++++++------------
 src/providers/ldap/sdap_access.c      |  3 +-
 src/providers/ldap/sdap_autofs.c      |  2 +-
 src/providers/ldap/sdap_dyndns.c      |  4 +--
 src/providers/ldap/sdap_id_op.c       | 63 ++++++++++++++++++-----------------
 src/providers/ldap/sdap_id_op.h       |  2 ++
 src/providers/ldap/sdap_sudo.c        |  6 ++--
 23 files changed, 177 insertions(+), 117 deletions(-)

diff --git a/src/providers/ad/ad_init.c b/src/providers/ad/ad_init.c
index d5488ad07d179f654a53e6d10824b8db7e842bef..aada14ec7dadb2db5cc67ecc0e1d9c631c974f07 100644
--- a/src/providers/ad/ad_init.c
+++ b/src/providers/ad/ad_init.c
@@ -135,37 +135,12 @@ sssm_ad_id_init(struct be_ctx *bectx,
     ad_ctx->ad_options = ad_options;
     ad_options->id_ctx = ad_ctx;
 
-    sdap_ctx = talloc_zero(ad_options, struct sdap_id_ctx);
-    if (!sdap_ctx) {
+    sdap_ctx = sdap_id_ctx_new(ad_options, bectx, ad_options->service->sdap);
+    if (sdap_ctx == NULL) {
         return ENOMEM;
     }
-    sdap_ctx->be = bectx;
-    sdap_ctx->service = ad_options->service->sdap;
     ad_ctx->sdap_id_ctx = sdap_ctx;
 
-    ret = ad_get_id_options(ad_options, bectx->cdb,
-                            bectx->conf_path,
-                            &sdap_ctx->opts);
-    if (ret != EOK) {
-        goto done;
-    }
-
-    ret = setup_tls_config(sdap_ctx->opts->basic);
-    if (ret != EOK) {
-        DEBUG(SSSDBG_CRIT_FAILURE,
-              ("setup_tls_config failed [%s]\n", strerror(ret)));
-        goto done;
-    }
-
-    ret = sdap_id_conn_cache_create(sdap_ctx, sdap_ctx, &sdap_ctx->conn_cache);
-    if (ret != EOK) {
-        goto done;
-    }
-
-    /* Set up the ID mapping object */
-    ret = sdap_idmap_init(sdap_ctx, sdap_ctx, &sdap_ctx->opts->idmap_ctx);
-    if (ret != EOK) goto done;
-
     ret = ad_dyndns_init(sdap_ctx->be, ad_options);
     if (ret != EOK) {
         DEBUG(SSSDBG_MINOR_FAILURE,
@@ -186,6 +161,26 @@ sssm_ad_id_init(struct be_ctx *bectx,
         goto done;
     }
 
+    /* Set up various SDAP options */
+    ret = ad_get_id_options(ad_options, bectx->cdb,
+                            bectx->conf_path,
+                            &sdap_ctx->opts);
+    if (ret != EOK) {
+        goto done;
+    }
+
+    /* Set up the ID mapping object */
+    ret = sdap_idmap_init(sdap_ctx, sdap_ctx, &sdap_ctx->opts->idmap_ctx);
+    if (ret != EOK) goto done;
+
+
+    ret = setup_tls_config(sdap_ctx->opts->basic);
+    if (ret != EOK) {
+        DEBUG(SSSDBG_CRIT_FAILURE,
+              ("setup_tls_config failed [%s]\n", strerror(ret)));
+        goto done;
+    }
+
     /* setup SRV lookup plugin */
     hostname = dp_opt_get_string(ad_options->basic, AD_HOSTNAME);
     if (dp_opt_get_bool(ad_options->basic, AD_ENABLE_DNS_SITES)) {
diff --git a/src/providers/ad/ad_subdomains.c b/src/providers/ad/ad_subdomains.c
index 51c6955410fffa3e8c8c8bd124594d96aa882f6a..da0c85e760ff65272970ae68f7024c71c8c28421 100644
--- a/src/providers/ad/ad_subdomains.c
+++ b/src/providers/ad/ad_subdomains.c
@@ -93,7 +93,7 @@ static void ad_subdomains_retrieve(struct ad_subdomains_ctx *ctx,
     req_ctx->reply = NULL;
 
     req_ctx->sdap_op = sdap_id_op_create(req_ctx,
-                                         ctx->sdap_id_ctx->conn_cache);
+                                         ctx->sdap_id_ctx->conn->conn_cache);
     if (req_ctx->sdap_op == NULL) {
         DEBUG(SSSDBG_OP_FAILURE, ("sdap_id_op_create failed.\n"));
         ret = ENOMEM;
diff --git a/src/providers/ipa/ipa_access.c b/src/providers/ipa/ipa_access.c
index c43974e3c208fdd89930e67ca4c7052108c09d7b..3760c6f71f77154bf97ef4708fe43af42990fedc 100644
--- a/src/providers/ipa/ipa_access.c
+++ b/src/providers/ipa/ipa_access.c
@@ -208,7 +208,7 @@ static int hbac_retry(struct hbac_ctx *hbac_ctx)
     if (!offline) {
         if (hbac_ctx->sdap_op == NULL) {
             hbac_ctx->sdap_op = sdap_id_op_create(hbac_ctx,
-                                            hbac_ctx->sdap_ctx->conn_cache);
+                                          hbac_ctx->sdap_ctx->conn->conn_cache);
             if (hbac_ctx->sdap_op == NULL) {
                 DEBUG(1, ("sdap_id_op_create failed.\n"));
                 return EIO;
diff --git a/src/providers/ipa/ipa_auth.c b/src/providers/ipa/ipa_auth.c
index b528c544df90efd30570273d9731223b531e77f6..651196a96400ebbc4e3575d0f447accdf5da408d 100644
--- a/src/providers/ipa/ipa_auth.c
+++ b/src/providers/ipa/ipa_auth.c
@@ -71,7 +71,8 @@ static struct tevent_req *get_password_migration_flag_send(TALLOC_CTX *memctx,
     state->password_migration = false;
     state->ipa_realm = ipa_realm;
 
-    state->sdap_op = sdap_id_op_create(state, state->sdap_id_ctx->conn_cache);
+    state->sdap_op = sdap_id_op_create(state,
+                                       state->sdap_id_ctx->conn->conn_cache);
     if (state->sdap_op == NULL) {
         DEBUG(SSSDBG_OP_FAILURE, ("sdap_id_op_create failed.\n"));
         goto fail;
diff --git a/src/providers/ipa/ipa_hostid.c b/src/providers/ipa/ipa_hostid.c
index cb37e9a4dc8b5986d14b616db74b5e2980cb5567..a697dbf665d4c7e6d41ffb979b5147468144cc44 100644
--- a/src/providers/ipa/ipa_hostid.c
+++ b/src/providers/ipa/ipa_hostid.c
@@ -165,7 +165,7 @@ hosts_get_send(TALLOC_CTX *memctx,
     state->ctx = hostid_ctx;
     state->dp_error = DP_ERR_FATAL;
 
-    state->op = sdap_id_op_create(state, ctx->conn_cache);
+    state->op = sdap_id_op_create(state, ctx->conn->conn_cache);
     if (!state->op) {
         DEBUG(SSSDBG_OP_FAILURE, ("sdap_id_op_create failed\n"));
         ret = ENOMEM;
diff --git a/src/providers/ipa/ipa_id.c b/src/providers/ipa/ipa_id.c
index 5f94eb2c1089dea6b231fc75fa15a223742ed63e..b7ae81f6629b922b252b6f2cfb4d566a6c207947 100644
--- a/src/providers/ipa/ipa_id.c
+++ b/src/providers/ipa/ipa_id.c
@@ -174,7 +174,7 @@ static struct tevent_req *ipa_id_get_netgroup_send(TALLOC_CTX *memctx,
     state->ctx = ipa_ctx;
     state->dp_error = DP_ERR_FATAL;
 
-    state->op = sdap_id_op_create(state, ctx->conn_cache);
+    state->op = sdap_id_op_create(state, ctx->conn->conn_cache);
     if (!state->op) {
         DEBUG(2, ("sdap_id_op_create failed\n"));
         ret = ENOMEM;
diff --git a/src/providers/ipa/ipa_init.c b/src/providers/ipa/ipa_init.c
index 9676b781d1880b0869792a04e97a244d85e6417a..8363ca6d78346ba0ae49491191764c163a42052b 100644
--- a/src/providers/ipa/ipa_init.c
+++ b/src/providers/ipa/ipa_init.c
@@ -138,12 +138,10 @@ int sssm_ipa_id_init(struct be_ctx *bectx,
     ipa_options->id_ctx = ipa_ctx;
     ipa_ctx->ipa_options = ipa_options;
 
-    sdap_ctx = talloc_zero(ipa_options, struct sdap_id_ctx);
-    if (!sdap_ctx) {
+    sdap_ctx = sdap_id_ctx_new(ipa_options, bectx, ipa_options->service->sdap);
+    if (sdap_ctx == NULL) {
         return ENOMEM;
     }
-    sdap_ctx->be = bectx;
-    sdap_ctx->service = ipa_options->service->sdap;
     ipa_ctx->sdap_id_ctx = sdap_ctx;
 
     ret = ipa_get_id_options(ipa_options, bectx->cdb,
@@ -188,10 +186,6 @@ int sssm_ipa_id_init(struct be_ctx *bectx,
         goto done;
     }
 
-    ret = sdap_id_conn_cache_create(sdap_ctx, sdap_ctx, &sdap_ctx->conn_cache);
-    if (ret != EOK) {
-        goto done;
-    }
 
     /* Set up the ID mapping object */
     ret = sdap_idmap_init(sdap_ctx, sdap_ctx, &sdap_ctx->opts->idmap_ctx);
diff --git a/src/providers/ipa/ipa_selinux.c b/src/providers/ipa/ipa_selinux.c
index ce8f39cccaedc86771e6642a64ec88b33ab15c45..39bebebfd8f4880c4093dfd922f2dfaa79c38a2e 100644
--- a/src/providers/ipa/ipa_selinux.c
+++ b/src/providers/ipa/ipa_selinux.c
@@ -864,7 +864,8 @@ ipa_get_selinux_send(TALLOC_CTX *mem_ctx,
     }
 
     if (!offline) {
-        state->op = sdap_id_op_create(state, selinux_ctx->id_ctx->sdap_id_ctx->conn_cache);
+        state->op = sdap_id_op_create(state,
+                        selinux_ctx->id_ctx->sdap_id_ctx->conn->conn_cache);
         if (!state->op) {
             DEBUG(SSSDBG_OP_FAILURE, ("sdap_id_op_create failed\n"));
             ret = ENOMEM;
diff --git a/src/providers/ipa/ipa_subdomains.c b/src/providers/ipa/ipa_subdomains.c
index 95a11198a99aa60e93a1f5af18c88ff918dcd53a..18878ae33dc014639cfce0be54f9ca3a44c4ddbb 100644
--- a/src/providers/ipa/ipa_subdomains.c
+++ b/src/providers/ipa/ipa_subdomains.c
@@ -558,7 +558,7 @@ static void ipa_subdomains_retrieve(struct ipa_subdomains_ctx *ctx, struct be_re
     req_ctx->reply = NULL;
 
     req_ctx->sdap_op = sdap_id_op_create(req_ctx,
-                                         ctx->sdap_id_ctx->conn_cache);
+                                         ctx->sdap_id_ctx->conn->conn_cache);
     if (req_ctx->sdap_op == NULL) {
         DEBUG(SSSDBG_OP_FAILURE, ("sdap_id_op_create failed.\n"));
         ret = ENOMEM;
diff --git a/src/providers/ipa/ipa_subdomains_id.c b/src/providers/ipa/ipa_subdomains_id.c
index ea313cbafe65273d332f1aac1198336604e7da18..7fa09bd98cb5b2b87290daeaf0c34c2504b1be90 100644
--- a/src/providers/ipa/ipa_subdomains_id.c
+++ b/src/providers/ipa/ipa_subdomains_id.c
@@ -66,7 +66,7 @@ struct tevent_req *ipa_get_subdom_acct_send(TALLOC_CTX *memctx,
     state->ctx = ctx;
     state->dp_error = DP_ERR_FATAL;
 
-    state->op = sdap_id_op_create(state, state->ctx->conn_cache);
+    state->op = sdap_id_op_create(state, state->ctx->conn->conn_cache);
     if (!state->op) {
         DEBUG(SSSDBG_OP_FAILURE, ("sdap_id_op_create failed\n"));
         ret = ENOMEM;
diff --git a/src/providers/ldap/ldap_common.c b/src/providers/ldap/ldap_common.c
index 155019ec7a430b133dbdf6e57a6ec81a46f0dc0b..7939cd0e99c6747d2edf5101238a0502b8d1141a 100644
--- a/src/providers/ldap/ldap_common.c
+++ b/src/providers/ldap/ldap_common.c
@@ -1614,3 +1614,50 @@ sdap_attrs_get_sid_str(TALLOC_CTX *mem_ctx,
 
     return EOK;
 }
+
+struct sdap_id_conn_ctx *
+sdap_id_ctx_conn_add(struct sdap_id_ctx *id_ctx,
+                     struct sdap_service *sdap_service)
+{
+    struct sdap_id_conn_ctx *conn;
+    errno_t ret;
+
+    conn = talloc_zero(id_ctx, struct sdap_id_conn_ctx);
+    if (conn == NULL) {
+        return NULL;
+    }
+    conn->service = talloc_steal(conn, sdap_service);
+    conn->id_ctx = id_ctx;
+
+    /* Create a connection cache */
+    ret = sdap_id_conn_cache_create(conn, id_ctx, conn, &conn->conn_cache);
+    if (ret != EOK) {
+        talloc_free(conn);
+        return NULL;
+    }
+    DLIST_ADD_END(id_ctx->conn, conn, struct sdap_id_conn_ctx *);
+
+    return conn;
+}
+
+struct sdap_id_ctx *
+sdap_id_ctx_new(TALLOC_CTX *mem_ctx, struct be_ctx *bectx,
+                struct sdap_service *sdap_service)
+{
+    struct sdap_id_ctx *sdap_ctx;
+
+    sdap_ctx = talloc_zero(mem_ctx, struct sdap_id_ctx);
+    if (sdap_ctx == NULL) {
+        return NULL;
+    }
+    sdap_ctx->be = bectx;
+
+    /* There should be at least one connection context */
+    sdap_ctx->conn = sdap_id_ctx_conn_add(sdap_ctx, sdap_service);
+    if (sdap_ctx->conn == NULL) {
+        talloc_free(sdap_ctx);
+        return NULL;
+    }
+
+    return sdap_ctx;
+}
diff --git a/src/providers/ldap/ldap_common.h b/src/providers/ldap/ldap_common.h
index 2d17b755808e58831b458bcc86a50eb74b1f1057..367be449e4ccb90eb4b927d20ff7db502abd9e51 100644
--- a/src/providers/ldap/ldap_common.h
+++ b/src/providers/ldap/ldap_common.h
@@ -42,17 +42,27 @@
 /* a fd the child process would log into */
 extern int ldap_child_debug_fd;
 
-struct sdap_id_ctx {
-    struct be_ctx *be;
-    struct sdap_options *opts;
+struct sdap_id_ctx;
+
+struct sdap_id_conn_ctx {
+    struct sdap_id_ctx *id_ctx;
+
     struct fo_service *fo_service;
     struct sdap_service *service;
-
-    /* If using GSSAPI */
-    struct krb5_service *krb5_service;
-
     /* LDAP connection cache */
     struct sdap_id_conn_cache *conn_cache;
+    /* dlinklist pointers */
+    struct sdap_id_conn_ctx *prev, *next;
+};
+
+struct sdap_id_ctx {
+    struct be_ctx *be;
+    struct sdap_options *opts;
+
+    /* If using GSSAPI */
+    struct krb5_service *krb5_service;
+    /* connection to a server */
+    struct sdap_id_conn_ctx *conn;
 
     /* enumeration loop timer */
     struct timeval last_enum;
@@ -235,4 +245,12 @@ sdap_set_sasl_options(struct sdap_options *id_opts,
                       char *default_realm,
                       const char *keytab_path);
 
+struct sdap_id_conn_ctx *
+sdap_id_ctx_conn_add(struct sdap_id_ctx *id_ctx,
+                     struct sdap_service *sdap_service);
+
+struct sdap_id_ctx *
+sdap_id_ctx_new(TALLOC_CTX *mem_ctx, struct be_ctx *bectx,
+                struct sdap_service *sdap_service);
+
 #endif /* _LDAP_COMMON_H_ */
diff --git a/src/providers/ldap/ldap_id.c b/src/providers/ldap/ldap_id.c
index 72fdd9c260383913e1fe24bf4daa283415840d0e..bad25f89614e1f2a91518a9e8816c0448cb19197 100644
--- a/src/providers/ldap/ldap_id.c
+++ b/src/providers/ldap/ldap_id.c
@@ -80,7 +80,7 @@ struct tevent_req *users_get_send(TALLOC_CTX *memctx,
     state->ctx = ctx;
     state->dp_error = DP_ERR_FATAL;
 
-    state->op = sdap_id_op_create(state, state->ctx->conn_cache);
+    state->op = sdap_id_op_create(state, state->ctx->conn->conn_cache);
     if (!state->op) {
         DEBUG(2, ("sdap_id_op_create failed\n"));
         ret = ENOMEM;
@@ -403,7 +403,7 @@ struct tevent_req *groups_get_send(TALLOC_CTX *memctx,
     state->ctx = ctx;
     state->dp_error = DP_ERR_FATAL;
 
-    state->op = sdap_id_op_create(state, state->ctx->conn_cache);
+    state->op = sdap_id_op_create(state, state->ctx->conn->conn_cache);
     if (!state->op) {
         DEBUG(2, ("sdap_id_op_create failed\n"));
         ret = ENOMEM;
@@ -698,7 +698,7 @@ static struct tevent_req *groups_by_user_send(TALLOC_CTX *memctx,
     state->ctx = ctx;
     state->dp_error = DP_ERR_FATAL;
 
-    state->op = sdap_id_op_create(state, state->ctx->conn_cache);
+    state->op = sdap_id_op_create(state, state->ctx->conn->conn_cache);
     if (!state->op) {
         DEBUG(2, ("sdap_id_op_create failed\n"));
         ret = ENOMEM;
@@ -862,7 +862,7 @@ void sdap_do_online_check(struct be_req *be_req, struct sdap_id_ctx *ctx)
     check_ctx->be_req = be_req;
 
     req = sdap_cli_connect_send(be_req, be_ctx->ev, ctx->opts,
-                                be_ctx, ctx->service, false,
+                                be_ctx, ctx->conn->service, false,
                                 CON_TLS_DFL, false);
     if (req == NULL) {
         DEBUG(1, ("sdap_cli_connect_send failed.\n"));
@@ -1280,7 +1280,7 @@ static struct tevent_req *get_user_and_group_send(TALLOC_CTX *memctx,
     state->id_ctx = id_ctx;
     state->dp_error = DP_ERR_FATAL;
 
-    state->op = sdap_id_op_create(state, state->id_ctx->conn_cache);
+    state->op = sdap_id_op_create(state, state->id_ctx->conn->conn_cache);
     if (!state->op) {
         DEBUG(SSSDBG_OP_FAILURE, ("sdap_id_op_create failed\n"));
         ret = ENOMEM;
diff --git a/src/providers/ldap/ldap_id_enum.c b/src/providers/ldap/ldap_id_enum.c
index 6c5a378ed93d8ad8194d00f24caf0bcaba280758..7a2129d97fcbcae49e627ff3bca481dd3f568201 100644
--- a/src/providers/ldap/ldap_id_enum.c
+++ b/src/providers/ldap/ldap_id_enum.c
@@ -212,7 +212,7 @@ struct tevent_req *ldap_id_enumerate_send(struct tevent_context *ev,
 
     state->ev = ev;
     state->ctx = ctx;
-    state->op = sdap_id_op_create(state, state->ctx->conn_cache);
+    state->op = sdap_id_op_create(state, state->ctx->conn->conn_cache);
     if (!state->op) {
         DEBUG(2, ("sdap_id_op_create failed\n"));
         talloc_zfree(req);
diff --git a/src/providers/ldap/ldap_id_netgroup.c b/src/providers/ldap/ldap_id_netgroup.c
index 5080cfb62fa1c37667155744fdb12fc82a50233f..6788a52e52fdc11663f8a264717cceb3145d6f34 100644
--- a/src/providers/ldap/ldap_id_netgroup.c
+++ b/src/providers/ldap/ldap_id_netgroup.c
@@ -70,7 +70,7 @@ struct tevent_req *ldap_netgroup_get_send(TALLOC_CTX *memctx,
     state->ctx = ctx;
     state->dp_error = DP_ERR_FATAL;
 
-    state->op = sdap_id_op_create(state, state->ctx->conn_cache);
+    state->op = sdap_id_op_create(state, state->ctx->conn->conn_cache);
     if (!state->op) {
         DEBUG(2, ("sdap_id_op_create failed\n"));
         ret = ENOMEM;
diff --git a/src/providers/ldap/ldap_id_services.c b/src/providers/ldap/ldap_id_services.c
index 5699bf0dce6a2bf63791e493ec28c813c6dd259b..5c3c53f2a6bea73eb26e43e00cb4dca723660c9d 100644
--- a/src/providers/ldap/ldap_id_services.c
+++ b/src/providers/ldap/ldap_id_services.c
@@ -82,7 +82,7 @@ services_get_send(TALLOC_CTX *mem_ctx,
     state->protocol = protocol;
     state->filter_type = filter_type;
 
-    state->op = sdap_id_op_create(state, state->id_ctx->conn_cache);
+    state->op = sdap_id_op_create(state, state->id_ctx->conn->conn_cache);
     if (!state->op) {
         DEBUG(SSSDBG_MINOR_FAILURE, ("sdap_id_op_create failed\n"));
         ret = ENOMEM;
diff --git a/src/providers/ldap/ldap_init.c b/src/providers/ldap/ldap_init.c
index f70c8f1b0b03e4acf434c6559a11ad79f9139b42..cc502d53798d1f8c3a385a72468d2ffb68b2a38e 100644
--- a/src/providers/ldap/ldap_init.c
+++ b/src/providers/ldap/ldap_init.c
@@ -92,6 +92,8 @@ int sssm_ldap_id_init(struct be_ctx *bectx,
     const char *backup_urls;
     const char *dns_service_name;
     const char *sasl_mech;
+    struct sdap_service *sdap_service;
+    struct sdap_options *opts;
     int ret;
 
     /* If we're already set up, just return that */
@@ -103,37 +105,40 @@ int sssm_ldap_id_init(struct be_ctx *bectx,
         return EOK;
     }
 
-    ctx = talloc_zero(bectx, struct sdap_id_ctx);
-    if (!ctx) return ENOMEM;
-
-    ctx->be = bectx;
-
-    ret = ldap_get_options(ctx, bectx->cdb,
-                           bectx->conf_path, &ctx->opts);
+    ret = ldap_get_options(bectx, bectx->cdb,
+                           bectx->conf_path, &opts);
     if (ret != EOK) {
         goto done;
     }
 
-    dns_service_name = dp_opt_get_string(ctx->opts->basic,
+    dns_service_name = dp_opt_get_string(opts->basic,
                                          SDAP_DNS_SERVICE_NAME);
-    DEBUG(7, ("Service name for discovery set to %s\n", dns_service_name));
+    DEBUG(SSSDBG_CONF_SETTINGS,
+          ("Service name for discovery set to %s\n", dns_service_name));
 
-    urls = dp_opt_get_string(ctx->opts->basic, SDAP_URI);
-    backup_urls = dp_opt_get_string(ctx->opts->basic, SDAP_BACKUP_URI);
+    urls = dp_opt_get_string(opts->basic, SDAP_URI);
+    backup_urls = dp_opt_get_string(opts->basic, SDAP_BACKUP_URI);
 
-    ret = sdap_service_init(ctx, ctx->be, "LDAP",
+    ret = sdap_service_init(bectx, bectx, "LDAP",
                             dns_service_name, urls, backup_urls,
-                            &ctx->service);
+                            &sdap_service);
     if (ret != EOK) {
-        DEBUG(1, ("Failed to initialize failover service!\n"));
+        DEBUG(SSSDBG_OP_FAILURE, ("Failed to initialize failover service!\n"));
         goto done;
     }
 
+    ctx = sdap_id_ctx_new(bectx, bectx, sdap_service);
+    if (!ctx) {
+        ret = ENOMEM;
+        goto done;
+    }
+    ctx->opts = talloc_steal(ctx, opts);
+
     sasl_mech = dp_opt_get_string(ctx->opts->basic, SDAP_SASL_MECH);
     if (sasl_mech && strcasecmp(sasl_mech, "GSSAPI") == 0) {
         if (dp_opt_get_bool(ctx->opts->basic, SDAP_KRB5_KINIT)) {
             ret = sdap_gssapi_init(ctx, ctx->opts->basic,
-                                   ctx->be, ctx->service,
+                                   ctx->be, ctx->conn->service,
                                    &ctx->krb5_service);
             if (ret !=  EOK) {
                 DEBUG(1, ("sdap_gssapi_init failed [%d][%s].\n",
@@ -150,11 +155,6 @@ int sssm_ldap_id_init(struct be_ctx *bectx,
         goto done;
     }
 
-    ret = sdap_id_conn_cache_create(ctx, ctx, &ctx->conn_cache);
-    if (ret != EOK) {
-        goto done;
-    }
-
     /* Set up the ID mapping object */
     ret = sdap_idmap_init(ctx, ctx, &ctx->opts->idmap_ctx);
     if (ret != EOK) goto done;
@@ -208,7 +208,7 @@ int sssm_ldap_auth_init(struct be_ctx *bectx,
 
         ctx->be = bectx;
         ctx->opts = id_ctx->opts;
-        ctx->service = id_ctx->service;
+        ctx->service = id_ctx->conn->service;
         ctx->chpass_service = NULL;
 
         *ops = &sdap_auth_ops;
diff --git a/src/providers/ldap/sdap_access.c b/src/providers/ldap/sdap_access.c
index 1b2f6993d162f97b2d3dedb74c9bdd192c04f50d..e74542346631e0edfa04435a7839456ae9182fe8 100644
--- a/src/providers/ldap/sdap_access.c
+++ b/src/providers/ldap/sdap_access.c
@@ -718,7 +718,8 @@ static struct tevent_req *sdap_access_filter_send(TALLOC_CTX *mem_ctx,
 
     DEBUG(6, ("Checking filter against LDAP\n"));
 
-    state->sdap_op = sdap_id_op_create(state, state->sdap_ctx->conn_cache);
+    state->sdap_op = sdap_id_op_create(state,
+                                       state->sdap_ctx->conn->conn_cache);
     if (!state->sdap_op) {
         DEBUG(2, ("sdap_id_op_create failed\n"));
         ret = ENOMEM;
diff --git a/src/providers/ldap/sdap_autofs.c b/src/providers/ldap/sdap_autofs.c
index 0bb211aa3e3ae9ec822e117f8febf8378bb09f79..e7947c9b223636cf1263b22889c53fd18106a02d 100644
--- a/src/providers/ldap/sdap_autofs.c
+++ b/src/providers/ldap/sdap_autofs.c
@@ -154,7 +154,7 @@ sdap_autofs_get_map_send(TALLOC_CTX *mem_ctx,
     state->dp_error = DP_ERR_FATAL;
     state->map_name = map_name;
 
-    state->op = sdap_id_op_create(state, state->ctx->conn_cache);
+    state->op = sdap_id_op_create(state, state->ctx->conn->conn_cache);
     if (!state->op) {
         DEBUG(SSSDBG_OP_FAILURE, ("sdap_id_op_create failed\n"));
         ret = ENOMEM;
diff --git a/src/providers/ldap/sdap_dyndns.c b/src/providers/ldap/sdap_dyndns.c
index d7e20ca4ffada5e775d45b86f73b69bddb2180a1..8fe2011d14ad0dda71b1cf2992b9493e6ae5ec90 100644
--- a/src/providers/ldap/sdap_dyndns.c
+++ b/src/providers/ldap/sdap_dyndns.c
@@ -500,7 +500,7 @@ sdap_dyndns_get_addrs_send(TALLOC_CTX *mem_ctx,
     }
 
     /* Detect DYNDNS address from LDAP connection */
-    state->sdap_op = sdap_id_op_create(state, sdap_ctx->conn_cache);
+    state->sdap_op = sdap_id_op_create(state, sdap_ctx->conn->conn_cache);
     if (!state->sdap_op) {
         ret = ENOMEM;
         DEBUG(SSSDBG_OP_FAILURE, ("sdap_id_op_create failed\n"));
@@ -664,7 +664,7 @@ sdap_dyndns_timer_conn_send(TALLOC_CTX *mem_ctx,
     state->dyndns_ctx->timer_in_progress = true;
 
     /* Make sure to have a valid LDAP connection */
-    state->sdap_op = sdap_id_op_create(state, state->sdap_ctx->conn_cache);
+    state->sdap_op = sdap_id_op_create(state, state->sdap_ctx->conn->conn_cache);
     if (state->sdap_op == NULL) {
         DEBUG(SSSDBG_OP_FAILURE, ("sdap_id_op_create failed\n"));
         ret = ENOMEM;
diff --git a/src/providers/ldap/sdap_id_op.c b/src/providers/ldap/sdap_id_op.c
index 02142103b1266e00b2728f0ea7441560a133cb7a..be25b5da45ef764b8eb0ce0ac4360785cebbeb39 100644
--- a/src/providers/ldap/sdap_id_op.c
+++ b/src/providers/ldap/sdap_id_op.c
@@ -28,7 +28,7 @@
 
 /* LDAP async connection cache */
 struct sdap_id_conn_cache {
-    struct sdap_id_ctx *id_ctx;
+    struct sdap_id_conn_ctx *id_conn;
 
     /* list of all open connections */
     struct sdap_id_conn_data *connections;
@@ -103,6 +103,7 @@ static void sdap_id_op_connect_done(struct tevent_req *subreq);
 /* Create a connection cache */
 int sdap_id_conn_cache_create(TALLOC_CTX *memctx,
                               struct sdap_id_ctx *id_ctx,
+                              struct sdap_id_conn_ctx *id_conn,
                               struct sdap_id_conn_cache** conn_cache_out)
 {
     int ret;
@@ -113,9 +114,9 @@ int sdap_id_conn_cache_create(TALLOC_CTX *memctx,
         goto fail;
     }
 
-    conn_cache->id_ctx = id_ctx;
+    conn_cache->id_conn = id_conn;
 
-    ret = be_add_offline_cb(conn_cache, id_ctx->be,
+    ret = be_add_offline_cb(conn_cache, id_conn->id_ctx->be,
                             sdap_id_conn_cache_be_offline_cb, conn_cache,
                             NULL);
     if (ret != EOK) {
@@ -123,7 +124,7 @@ int sdap_id_conn_cache_create(TALLOC_CTX *memctx,
         goto fail;
     }
 
-    ret = be_add_reconnect_cb(conn_cache, id_ctx->be,
+    ret = be_add_reconnect_cb(conn_cache, id_conn->id_ctx->be,
                               sdap_id_conn_cache_fo_reconnect_cb, conn_cache,
                               NULL);
     if (ret != EOK) {
@@ -224,7 +225,7 @@ static bool sdap_can_reuse_connection(struct sdap_id_conn_data *conn_data)
         return false;
     }
 
-    timeout = dp_opt_get_int(conn_data->conn_cache->id_ctx->opts->basic,
+    timeout = dp_opt_get_int(conn_data->conn_cache->id_conn->id_ctx->opts->basic,
                              SDAP_OPT_TIMEOUT);
     return !sdap_is_connection_expired(conn_data, timeout);
 }
@@ -242,7 +243,7 @@ static int sdap_id_conn_data_set_expire_timer(struct sdap_id_conn_data *conn_dat
         return EOK;
     }
 
-    timeout = dp_opt_get_int(conn_data->conn_cache->id_ctx->opts->basic,
+    timeout = dp_opt_get_int(conn_data->conn_cache->id_conn->id_ctx->opts->basic,
                              SDAP_OPT_TIMEOUT);
     if (timeout > 0) {
         tv.tv_sec -= timeout;
@@ -255,10 +256,10 @@ static int sdap_id_conn_data_set_expire_timer(struct sdap_id_conn_data *conn_dat
     talloc_zfree(conn_data->expire_timer);
 
     conn_data->expire_timer =
-                        tevent_add_timer(conn_data->conn_cache->id_ctx->be->ev,
-                                         conn_data, tv,
-                                         sdap_id_conn_data_expire_handler,
-                                         conn_data);
+              tevent_add_timer(conn_data->conn_cache->id_conn->id_ctx->be->ev,
+                               conn_data, tv,
+                               sdap_id_conn_data_expire_handler,
+                               conn_data);
     if (!conn_data->expire_timer) {
         return ENOMEM;
     }
@@ -349,8 +350,8 @@ static bool sdap_id_op_can_reconnect(struct sdap_id_op *op)
     int max_retries;
     int count;
 
-    count = be_fo_get_server_count(op->conn_cache->id_ctx->be,
-                                   op->conn_cache->id_ctx->service->name);
+    count = be_fo_get_server_count(op->conn_cache->id_conn->id_ctx->be,
+                                   op->conn_cache->id_conn->service->name);
     max_retries = 2 * count -1;
     if (max_retries < 1) {
         max_retries = 1;
@@ -361,7 +362,7 @@ static bool sdap_id_op_can_reconnect(struct sdap_id_op *op)
 
 /* state of connect request */
 struct sdap_id_op_connect_state {
-    struct sdap_id_ctx *id_ctx;
+    struct sdap_id_conn_ctx *id_conn;
     struct tevent_context *ev;
     struct sdap_id_op *op;
     int dp_error;
@@ -411,8 +412,8 @@ struct tevent_req *sdap_id_op_connect_send(struct sdap_id_op *op,
 
     talloc_set_destructor((void*)state, sdap_id_op_connect_state_destroy);
 
-    state->id_ctx = op->conn_cache->id_ctx;
-    state->ev = state->id_ctx->be->ev;
+    state->id_conn = op->conn_cache->id_conn;
+    state->ev = state->id_conn->id_ctx->be->ev;
     state->op = op;
     op->connect_req = req;
 
@@ -489,9 +490,9 @@ static int sdap_id_op_connect_step(struct tevent_req *req)
 
     conn_data->conn_cache = conn_cache;
     subreq = sdap_cli_connect_send(conn_data, state->ev,
-                                   state->id_ctx->opts,
-                                   state->id_ctx->be,
-                                   state->id_ctx->service, false,
+                                   state->id_conn->id_ctx->opts,
+                                   state->id_conn->id_ctx->be,
+                                   state->id_conn->service, false,
                                    CON_TLS_DFL, false);
 
     if (!subreq) {
@@ -555,12 +556,12 @@ static void sdap_id_op_connect_done(struct tevent_req *subreq)
         /* be is going offline as there is no more servers to try */
         DEBUG(1, ("Failed to connect, going offline (%d [%s])\n",
                   ret, strerror(ret)));
-        be_mark_offline(conn_cache->id_ctx->be);
+        be_mark_offline(conn_cache->id_conn->id_ctx->be);
         is_offline = true;
     }
 
     if (ret == EOK) {
-        current_srv_opts = conn_cache->id_ctx->srv_opts;
+        current_srv_opts = conn_cache->id_conn->id_ctx->srv_opts;
         if (current_srv_opts) {
             DEBUG(8, ("Old USN: %lu, New USN: %lu\n", current_srv_opts->last_usn, srv_opts->last_usn));
 
@@ -579,7 +580,7 @@ static void sdap_id_op_connect_done(struct tevent_req *subreq)
             }
         }
         ret = sdap_id_conn_data_set_expire_timer(conn_data);
-        sdap_steal_server_opts(conn_cache->id_ctx, &srv_opts);
+        sdap_steal_server_opts(conn_cache->id_conn->id_ctx, &srv_opts);
     }
 
     if (can_retry) {
@@ -596,7 +597,7 @@ static void sdap_id_op_connect_done(struct tevent_req *subreq)
                 /* do not attempt to retry on errors like ENOMEM */
                 can_retry = false;
                 is_offline = true;
-                be_mark_offline(conn_cache->id_ctx->be);
+                be_mark_offline(conn_cache->id_conn->id_ctx->be);
                 break;
         }
     }
@@ -635,7 +636,7 @@ static void sdap_id_op_connect_done(struct tevent_req *subreq)
 
             if (can_retry) {
                 /* determining whether retry is possible */
-                if (be_is_offline(conn_cache->id_ctx->be)) {
+                if (be_is_offline(conn_cache->id_conn->id_ctx->be)) {
                     /* be is offline, no retry possible */
                     if (ret == EOK) {
                         DEBUG(9, ("skipping automatic retry on op #%d as be is offline\n", notify_count));
@@ -686,12 +687,12 @@ static void sdap_id_op_connect_done(struct tevent_req *subreq)
 
     if ((ret == EOK) &&
         conn_data->sh->connected &&
-        !be_is_offline(conn_cache->id_ctx->be)) {
+        !be_is_offline(conn_cache->id_conn->id_ctx->be)) {
         DEBUG(9, ("caching successful connection after %d notifies\n", notify_count));
         conn_cache->cached_connection = conn_data;
 
         /* Run any post-connection routines */
-        be_run_online_cb(conn_cache->id_ctx->be);
+        be_run_online_cb(conn_cache->id_conn->id_ctx->be);
 
     } else {
         if (conn_cache->cached_connection == conn_data) {
@@ -704,9 +705,9 @@ static void sdap_id_op_connect_done(struct tevent_req *subreq)
     if (reinit) {
         DEBUG(SSSDBG_TRACE_FUNC, ("Server reinitialization detected. "
                                   "Cleaning cache.\n"));
-        reinit_req = sdap_reinit_cleanup_send(conn_cache->id_ctx->be,
-                                              conn_cache->id_ctx->be,
-                                              conn_cache->id_ctx);
+        reinit_req = sdap_reinit_cleanup_send(conn_cache->id_conn->id_ctx->be,
+                                              conn_cache->id_conn->id_ctx->be,
+                                              conn_cache->id_conn->id_ctx);
         if (reinit_req == NULL) {
             DEBUG(SSSDBG_CRIT_FAILURE, ("Unable to perform reinitialization "
                                         "clean up.\n"));
@@ -804,14 +805,14 @@ int sdap_id_op_done(struct sdap_id_op *op, int retval, int *dp_err_out)
         op->conn_cache->cached_connection = NULL;
 
         DEBUG(5, ("communication error on cached connection, moving to next server\n"));
-        be_fo_try_next_server(op->conn_cache->id_ctx->be,
-                              op->conn_cache->id_ctx->service->name);
+        be_fo_try_next_server(op->conn_cache->id_conn->id_ctx->be,
+                              op->conn_cache->id_conn->service->name);
     }
 
     int dp_err;
     if (retval == EOK) {
         dp_err = DP_ERR_OK;
-    } else if (be_is_offline(op->conn_cache->id_ctx->be)) {
+    } else if (be_is_offline(op->conn_cache->id_conn->id_ctx->be)) {
         /* if backend is already offline, just report offline, do not duplicate errors */
         dp_err = DP_ERR_OFFLINE;
         retval = EAGAIN;
diff --git a/src/providers/ldap/sdap_id_op.h b/src/providers/ldap/sdap_id_op.h
index f36037a9c942da57400dd24ba2452863f96775e2..b808dd89aebb096b7163c10df39784a54b7e0b03 100644
--- a/src/providers/ldap/sdap_id_op.h
+++ b/src/providers/ldap/sdap_id_op.h
@@ -26,6 +26,7 @@
 #define _SDAP_ID_OP_H_
 
 struct sdap_id_ctx;
+struct sdap_id_conn_ctx;
 
 /* LDAP async connection cache */
 struct sdap_id_conn_cache;
@@ -38,6 +39,7 @@ struct sdap_id_op;
 /* Create a connection cache */
 int sdap_id_conn_cache_create(TALLOC_CTX *memctx,
                               struct sdap_id_ctx *id_ctx,
+                              struct sdap_id_conn_ctx *id_conn,
                               struct sdap_id_conn_cache** conn_cache_out);
 
 /* Create an operation object */
diff --git a/src/providers/ldap/sdap_sudo.c b/src/providers/ldap/sdap_sudo.c
index 3472da67c0b211ebae0782828addc361bbfeeab3..315f254a3a0c00d48c36d439e0da23e76f2ffce0 100644
--- a/src/providers/ldap/sdap_sudo.c
+++ b/src/providers/ldap/sdap_sudo.c
@@ -508,7 +508,7 @@ void sdap_sudo_handler(struct be_req *be_req)
     case BE_REQ_SUDO_RULES:
         DEBUG(SSSDBG_TRACE_FUNC, ("Issuing a refresh of specific sudo rules\n"));
         req = sdap_sudo_rules_refresh_send(be_req, sudo_ctx, id_ctx->be,
-                                           id_ctx->opts, id_ctx->conn_cache,
+                                           id_ctx->opts, id_ctx->conn->conn_cache,
                                            sudo_req->rules);
         break;
     default:
@@ -585,7 +585,7 @@ static struct tevent_req *sdap_sudo_full_refresh_send(TALLOC_CTX *mem_ctx,
     DEBUG(SSSDBG_TRACE_FUNC, ("Issuing a full refresh of sudo rules\n"));
 
     subreq = sdap_sudo_refresh_send(state, id_ctx->be, id_ctx->opts,
-                                    id_ctx->conn_cache,
+                                    id_ctx->conn->conn_cache,
                                     ldap_full_filter, sysdb_filter);
     if (subreq == NULL) {
         ret = ENOMEM;
@@ -901,7 +901,7 @@ static struct tevent_req *sdap_sudo_smart_refresh_send(TALLOC_CTX *mem_ctx,
                               "(USN > %s)\n", (usn == NULL ? "0" : usn)));
 
     subreq = sdap_sudo_refresh_send(state, id_ctx->be, id_ctx->opts,
-                                    id_ctx->conn_cache,
+                                    id_ctx->conn->conn_cache,
                                     ldap_full_filter, NULL);
     if (subreq == NULL) {
         ret = ENOMEM;
-- 
1.8.2.1

-------------- next part --------------
>From f9de1e9f6889c19f6b22192b7f9044abc6b0d454 Mon Sep 17 00:00:00 2001
From: Jakub Hrozek <jhrozek at redhat.com>
Date: Sun, 19 May 2013 23:01:41 +0200
Subject: [PATCH 03/15] LDAP: Refactor account info handler into a tevent
 request

The sdap account handler was a function with its own private callback
that directly called the back end handlers. This patch refactors the
handler into a new tevent request that the current sdap handler calls.

This refactoring would allow the caller to specify a custom sdap
connection for use by the handler and optionally retry the same request
with another connection inside a single per-provider handler.

No functional changes are present in this patch.
---
 src/providers/ldap/ldap_common.h |  17 +-
 src/providers/ldap/ldap_id.c     | 374 ++++++++++++++++++++++-----------------
 2 files changed, 218 insertions(+), 173 deletions(-)

diff --git a/src/providers/ldap/ldap_common.h b/src/providers/ldap/ldap_common.h
index 367be449e4ccb90eb4b927d20ff7db502abd9e51..c1b62f727af98dda23833954d9fd05a13214b363 100644
--- a/src/providers/ldap/ldap_common.h
+++ b/src/providers/ldap/ldap_common.h
@@ -97,6 +97,15 @@ void sdap_account_info_handler(struct be_req *breq);
 void sdap_handle_account_info(struct be_req *breq, struct sdap_id_ctx *ctx);
 int sdap_id_setup_tasks(struct sdap_id_ctx *ctx);
 
+struct tevent_req *
+sdap_handle_acct_req_send(TALLOC_CTX *mem_ctx,
+                          struct be_req *breq,
+                          struct be_acct_req *ar,
+                          struct sdap_id_ctx *id_ctx);
+errno_t
+sdap_handle_acct_req_recv(struct tevent_req *req,
+                          int *_dp_error, const char **_err);
+
 /* auth */
 void sdap_pam_auth_handler(struct be_req *breq);
 
@@ -161,14 +170,6 @@ struct tevent_req *ldap_id_enumerate_send(struct tevent_context *ev,
 
 void sdap_mark_offline(struct sdap_id_ctx *ctx);
 
-struct tevent_req *users_get_send(TALLOC_CTX *memctx,
-                                  struct tevent_context *ev,
-                                  struct sdap_id_ctx *ctx,
-                                  const char *name,
-                                  int filter_type,
-                                  int attrs_type);
-int users_get_recv(struct tevent_req *req, int *dp_error_out);
-
 struct tevent_req *groups_get_send(TALLOC_CTX *memctx,
                                    struct tevent_context *ev,
                                    struct sdap_id_ctx *ctx,
diff --git a/src/providers/ldap/ldap_id.c b/src/providers/ldap/ldap_id.c
index bad25f89614e1f2a91518a9e8816c0448cb19197..38f5529b68b574f9621a26bd30f5c3a58469daba 100644
--- a/src/providers/ldap/ldap_id.c
+++ b/src/providers/ldap/ldap_id.c
@@ -971,11 +971,6 @@ static void sdap_check_online_reinit_done(struct tevent_req *req)
 /* FIXME: embed this function in sssd_be and only call out
  * specific functions from modules ? */
 
-static void sdap_account_info_users_done(struct tevent_req *req);
-static void sdap_account_info_groups_done(struct tevent_req *req);
-static void sdap_account_info_initgr_done(struct tevent_req *req);
-static void sdap_account_info_netgroups_done(struct tevent_req *req);
-static void sdap_account_info_services_done(struct tevent_req *req);
 void sdap_handle_account_info(struct be_req *breq, struct sdap_id_ctx *ctx);
 
 static struct tevent_req *get_user_and_group_send(TALLOC_CTX *memctx,
@@ -984,7 +979,6 @@ static struct tevent_req *get_user_and_group_send(TALLOC_CTX *memctx,
                                                   const char *name,
                                                   int filter_type,
                                                   int attrs_type);
-static void sdap_get_user_and_group_done(struct tevent_req *req);
 
 void sdap_account_info_handler(struct be_req *breq)
 {
@@ -1000,244 +994,306 @@ void sdap_account_info_handler(struct be_req *breq)
     return sdap_handle_account_info(breq, ctx);
 }
 
-void sdap_handle_account_info(struct be_req *breq, struct sdap_id_ctx *ctx)
-{
-    struct be_ctx *be_ctx = be_req_get_be_ctx(breq);
+/* A generic LDAP account info handler */
+struct sdap_handle_acct_req_state {
+    struct be_req *breq;
     struct be_acct_req *ar;
+    const char *err;
+    int dp_error;
+};
+
+static void sdap_handle_acct_req_done(struct tevent_req *subreq);
+
+struct tevent_req *
+sdap_handle_acct_req_send(TALLOC_CTX *mem_ctx,
+                          struct be_req *breq,
+                          struct be_acct_req *ar,
+                          struct sdap_id_ctx *id_ctx)
+{
     struct tevent_req *req;
-    const char *err = "Unknown Error";
-    int ret = EOK;
+    struct tevent_req *subreq;
+    struct be_ctx *be_ctx;
+    struct sdap_handle_acct_req_state *state;
+    errno_t ret;
 
-    if (be_is_offline(ctx->be)) {
-        return sdap_handler_done(breq, DP_ERR_OFFLINE, EAGAIN, "Offline");
+    req = tevent_req_create(mem_ctx, &state,
+                            struct sdap_handle_acct_req_state);
+    if (!req) {
+        ret = ENOMEM;
+        goto done;
     }
+    state->breq = breq;
+    state->ar = ar;
 
-    ar = talloc_get_type(be_req_get_data(breq), struct be_acct_req);
+    be_ctx = be_req_get_be_ctx(breq);
+
+    if (ar == NULL) {
+        ret = EINVAL;
+        goto done;
+    }
 
     switch (ar->entry_type & BE_REQ_TYPE_MASK) {
     case BE_REQ_USER: /* user */
 
         /* skip enumerations on demand */
         if (ar->filter_type == BE_FILTER_ENUM) {
-            return sdap_handler_done(breq, DP_ERR_OK, EOK, "Success");
+            DEBUG(SSSDBG_TRACE_LIBS,
+                  ("Skipping user enumeration on demand\n"));
+            state->err = "Success";
+            ret = EOK;
+            goto done;
         }
 
-        req = users_get_send(breq, be_ctx->ev, ctx,
-                             ar->filter_value,
-                             ar->filter_type,
-                             ar->attr_type);
-        if (!req) {
-            return sdap_handler_done(breq, DP_ERR_FATAL, ENOMEM, "Out of memory");
-        }
-
-        tevent_req_set_callback(req, sdap_account_info_users_done, breq);
-
+        subreq = users_get_send(breq, be_ctx->ev, id_ctx,
+                                ar->filter_value,
+                                ar->filter_type,
+                                ar->attr_type);
         break;
 
     case BE_REQ_GROUP: /* group */
 
         /* skip enumerations on demand */
         if (ar->filter_type == BE_FILTER_ENUM) {
-            return sdap_handler_done(breq, DP_ERR_OK, EOK, "Success");
+            DEBUG(SSSDBG_TRACE_LIBS,
+                  ("Skipping group enumeration on demand\n"));
+            state->err = "Success";
+            ret = EOK;
+            goto done;
         }
 
-        req = groups_get_send(breq, be_ctx->ev, ctx,
-                              ar->filter_value,
-                              ar->filter_type,
-                              ar->attr_type);
-        if (!req) {
-            return sdap_handler_done(breq, DP_ERR_FATAL, ENOMEM, "Out of memory");
-        }
-
-        tevent_req_set_callback(req, sdap_account_info_groups_done, breq);
-
+        subreq = groups_get_send(breq, be_ctx->ev, id_ctx,
+                                 ar->filter_value,
+                                 ar->filter_type,
+                                 ar->attr_type);
         break;
 
     case BE_REQ_INITGROUPS: /* init groups for user */
         if (ar->filter_type != BE_FILTER_NAME) {
             ret = EINVAL;
-            err = "Invalid filter type";
-            break;
+            state->err = "Invalid filter type";
+            goto done;
         }
         if (ar->attr_type != BE_ATTR_CORE) {
             ret = EINVAL;
-            err = "Invalid attr type";
-            break;
+            state->err = "Invalid attr type";
+            goto done;
         }
-        req = groups_by_user_send(breq, be_ctx->ev, ctx,
-                                  ar->filter_value);
-        if (!req) ret = ENOMEM;
-
-        tevent_req_set_callback(req, sdap_account_info_initgr_done, breq);
 
+        subreq = groups_by_user_send(breq, be_ctx->ev, id_ctx,
+                                     ar->filter_value);
         break;
 
     case BE_REQ_NETGROUP:
         if (ar->filter_type != BE_FILTER_NAME) {
             ret = EINVAL;
-            err = "Invalid filter type";
-            break;
+            state->err = "Invalid filter type";
+            goto done;
         }
 
-        req = ldap_netgroup_get_send(breq, be_ctx->ev, ctx, ar->filter_value);
-        if (!req) {
-            return sdap_handler_done(breq, DP_ERR_FATAL, ENOMEM, "Out of memory");
-        }
-
-        tevent_req_set_callback(req, sdap_account_info_netgroups_done, breq);
+        subreq = ldap_netgroup_get_send(breq, be_ctx->ev, id_ctx,
+                                        ar->filter_value);
         break;
 
     case BE_REQ_SERVICES:
         /* skip enumerations on demand */
         if (ar->filter_type == BE_FILTER_ENUM) {
-            return sdap_handler_done(breq, DP_ERR_OK, EOK, "Success");
+            DEBUG(SSSDBG_TRACE_LIBS,
+                  ("Skipping service enumeration on demand\n"));
+            state->err = "Success";
+            ret = EOK;
+            goto done;
         }
 
         if (ar->filter_type == BE_FILTER_SECID) {
             ret = EINVAL;
-            err = "Invalid filter type";
-            break;
+            state->err = "Invalid filter type";
+            goto done;
         }
 
-        req = services_get_send(breq, be_ctx->ev, ctx,
-                                ar->filter_value,
-                                ar->extra_value,
-                                ar->filter_type);
-        if (!req) {
-            return sdap_handler_done(breq, DP_ERR_FATAL,
-                                     ENOMEM, "Out of memory");
-        }
-        tevent_req_set_callback(req, sdap_account_info_services_done, breq);
-
+        subreq = services_get_send(breq, be_ctx->ev, id_ctx,
+                                   ar->filter_value,
+                                   ar->extra_value,
+                                   ar->filter_type);
         break;
 
     case BE_REQ_BY_SECID:
         if (ar->filter_type != BE_FILTER_SECID) {
             ret = EINVAL;
-            err = "Invalid filter type";
-            break;
+            state->err = "Invalid filter type";
+            goto done;
         }
 
-        req = get_user_and_group_send(breq, be_ctx->ev, ctx,
-                                      ar->filter_value,
-                                      ar->filter_type,
-                                      ar->attr_type);
-        if (!req) {
-            return sdap_handler_done(breq, DP_ERR_FATAL,
-                                     ENOMEM,"Out of memory");
-        }
-
-        tevent_req_set_callback(req, sdap_get_user_and_group_done, breq);
-
+        subreq = get_user_and_group_send(breq, be_ctx->ev, id_ctx,
+                                         ar->filter_value,
+                                         ar->filter_type,
+                                         ar->attr_type);
         break;
 
     case BE_REQ_USER_AND_GROUP:
         if (!(ar->filter_type == BE_FILTER_NAME ||
               ar->filter_type == BE_FILTER_IDNUM)) {
             ret = EINVAL;
-            err = "Invalid filter type";
-            break;
+            state->err = "Invalid filter type";
+            goto done;
         }
 
-        req = get_user_and_group_send(breq, be_ctx->ev, ctx,
-                                      ar->filter_value,
-                                      ar->filter_type,
-                                      ar->attr_type);
-        if (!req) {
-            return sdap_handler_done(breq, DP_ERR_FATAL,
-                                     ENOMEM,"Out of memory");
-        }
+        subreq = get_user_and_group_send(breq, be_ctx->ev, id_ctx,
+                                         ar->filter_value,
+                                         ar->filter_type,
+                                         ar->attr_type);
+        break;
 
-        tevent_req_set_callback(req, sdap_get_user_and_group_done, breq);
+    default: /*fail*/
+        ret = EINVAL;
+        state->err = "Invalid request type";
+        goto done;
+    }
 
+    if (!subreq) {
+        ret = ENOMEM;
+        goto done;
+    }
+
+    tevent_req_set_callback(subreq, sdap_handle_acct_req_done, req);
+    return req;
+
+done:
+    if (ret == EOK) {
+        tevent_req_done(req);
+    } else {
+        tevent_req_error(req, ret);
+    }
+
+    tevent_req_post(req, be_ctx->ev);
+    return req;
+}
+
+static void
+sdap_handle_acct_req_done(struct tevent_req *subreq)
+{
+    struct tevent_req *req = tevent_req_callback_data(subreq, struct tevent_req);
+    struct sdap_handle_acct_req_state *state;
+    errno_t ret;
+    const char *err = "Invalid request type";
+
+    state = tevent_req_data(req, struct sdap_handle_acct_req_state);
+
+    switch (state->ar->entry_type & BE_REQ_TYPE_MASK) {
+    case BE_REQ_USER: /* user */
+        err = "User lookup failed";
+        ret = users_get_recv(subreq, &state->dp_error);
+        break;
+    case BE_REQ_GROUP: /* group */
+        err = "Group lookup failed";
+        ret = groups_get_recv(subreq, &state->dp_error);
+        break;
+    case BE_REQ_INITGROUPS: /* init groups for user */
+        err = "Init group lookup failed";
+        ret = groups_by_user_recv(subreq, &state->dp_error);
+        break;
+    case BE_REQ_NETGROUP:
+        err = "Netgroup lookup failed";
+        ret = ldap_netgroup_get_recv(subreq, &state->dp_error);
+        break;
+    case BE_REQ_SERVICES:
+        err = "Service lookup failed";
+        ret = services_get_recv(subreq, &state->dp_error);
+        break;
+    case BE_REQ_BY_SECID:
+        /* Fallthrough */
+    case BE_REQ_USER_AND_GROUP:
+        err = "Lookup by SID failed";
+        ret = sdap_get_user_and_group_recv(subreq, &state->dp_error);
         break;
     default: /*fail*/
         ret = EINVAL;
-        err = "Invalid request type";
+        break;
     }
+    talloc_zfree(subreq);
 
-    if (ret != EOK) return sdap_handler_done(breq, DP_ERR_FATAL, ret, err);
+    if (ret != EOK) {
+        state->err = err;
+        tevent_req_error(req, ret);
+        return;
+    }
+
+    state->err = "Success";
+    tevent_req_done(req);
+}
+
+errno_t
+sdap_handle_acct_req_recv(struct tevent_req *req,
+                          int *_dp_error, const char **_err)
+{
+    struct sdap_handle_acct_req_state *state;
+
+    state = tevent_req_data(req, struct sdap_handle_acct_req_state);
+
+    if (_dp_error) {
+        *_dp_error = state->dp_error;
+    }
+
+    if (_err) {
+        *_err = state->err;
+    }
+
+    TEVENT_REQ_RETURN_ON_ERROR(req);
+    return EOK;
+}
+
+static void sdap_account_info_complete(struct tevent_req *req);
+
+void sdap_handle_account_info(struct be_req *breq, struct sdap_id_ctx *ctx)
+{
+    struct be_acct_req *ar;
+    struct tevent_req *req;
+
+    if (be_is_offline(ctx->be)) {
+        return sdap_handler_done(breq, DP_ERR_OFFLINE, EAGAIN, "Offline");
+    }
+
+    ar = talloc_get_type(be_req_get_data(breq), struct be_acct_req);
+    if (ar == NULL) {
+        return sdap_handler_done(breq, DP_ERR_FATAL,
+                                 EINVAL, "Invalid private data");
+    }
+
+    req = sdap_handle_acct_req_send(breq, breq, ar, ctx);
+    if (req == NULL) {
+        return sdap_handler_done(breq, DP_ERR_FATAL, ENOMEM, "Out of memory");
+    }
+    tevent_req_set_callback(req, sdap_account_info_complete, breq);
 }
 
-static void sdap_account_info_complete(struct be_req *breq, int dp_error,
-                                       int ret, const char *default_error_text)
+static void sdap_account_info_complete(struct tevent_req *req)
 {
-    const char* error_text;
+    const char *error_text;
+    const char *req_error_text;
+    struct be_req *breq = tevent_req_callback_data(req, struct be_req);
+    int ret, dp_error;
 
+    ret = sdap_handle_acct_req_recv(req, &dp_error, &req_error_text);
+    talloc_zfree(req);
     if (dp_error == DP_ERR_OK) {
         if (ret == EOK) {
             error_text = NULL;
         } else {
-            DEBUG(1, ("Bug: dp_error is OK on failed request"));
+            DEBUG(SSSDBG_CRIT_FAILURE,
+                  ("Bug: dp_error is OK on failed request"));
             dp_error = DP_ERR_FATAL;
-            error_text = default_error_text;
+            error_text = req_error_text;
         }
     } else if (dp_error == DP_ERR_OFFLINE) {
         error_text = "Offline";
     } else if (dp_error == DP_ERR_FATAL && ret == ENOMEM) {
         error_text = "Out of memory";
     } else {
-        error_text = default_error_text;
+        error_text = req_error_text;
     }
 
     sdap_handler_done(breq, dp_error, ret, error_text);
 }
 
-static void sdap_account_info_users_done(struct tevent_req *req)
-{
-    struct be_req *breq = tevent_req_callback_data(req, struct be_req);
-    int ret, dp_error;
-
-    ret = users_get_recv(req, &dp_error);
-    talloc_zfree(req);
-
-    sdap_account_info_complete(breq, dp_error, ret, "User lookup failed");
-}
-
-static void sdap_account_info_groups_done(struct tevent_req *req)
-{
-    struct be_req *breq = tevent_req_callback_data(req, struct be_req);
-    int ret, dp_error;
-
-    ret = groups_get_recv(req, &dp_error);
-    talloc_zfree(req);
-
-    sdap_account_info_complete(breq, dp_error, ret, "Group lookup failed");
-}
-
-static void sdap_account_info_initgr_done(struct tevent_req *req)
-{
-    struct be_req *breq = tevent_req_callback_data(req, struct be_req);
-    int ret, dp_error;
-
-    ret = groups_by_user_recv(req, &dp_error);
-    talloc_zfree(req);
-
-    sdap_account_info_complete(breq, dp_error, ret, "Init Groups Failed");
-}
-
-static void sdap_account_info_netgroups_done(struct tevent_req *req)
-{
-    struct be_req *breq = tevent_req_callback_data(req, struct be_req);
-    int ret, dp_error;
-
-    ret = ldap_netgroup_get_recv(req, &dp_error);
-    talloc_zfree(req);
-
-    sdap_account_info_complete(breq, dp_error, ret, "Netgroup lookup failed");
-}
-
-static void sdap_account_info_services_done(struct tevent_req *req)
-{
-    struct be_req *breq = tevent_req_callback_data(req, struct be_req);
-    int ret, dp_error;
-
-    ret = services_get_recv(req, &dp_error);
-    talloc_zfree(req);
-
-    sdap_account_info_complete(breq, dp_error, ret, "Service lookup failed");
-}
-
 struct get_user_and_group_state {
     struct tevent_context *ev;
     struct sdap_id_ctx *id_ctx;
@@ -1374,15 +1430,3 @@ errno_t sdap_get_user_and_group_recv(struct tevent_req *req, int *dp_error_out)
 
     return EOK;
 }
-
-static void sdap_get_user_and_group_done(struct tevent_req *req)
-{
-    struct be_req *breq = tevent_req_callback_data(req, struct be_req);
-    int ret;
-    int dp_error;
-
-    ret = sdap_get_user_and_group_recv(req, &dp_error);
-    talloc_zfree(req);
-
-    sdap_account_info_complete(breq, dp_error, ret, "Lookup by SID failed");
-}
-- 
1.8.2.1

-------------- next part --------------
>From 8de3596be268fdcb3c753d616b65a77226fa9760 Mon Sep 17 00:00:00 2001
From: Jakub Hrozek <jhrozek at redhat.com>
Date: Thu, 23 May 2013 01:15:02 +0200
Subject: [PATCH 04/15] LDAP: Pass in a connection to ID functions

Instead of using the default connection from the sdap_id_ctx, allow the
caller to specify which connection shall be used for this particular
request. Again, no functional change is present in this patch, just
another parameter is added.
---
 src/providers/ad/ad_id.c                   |  2 +-
 src/providers/ipa/ipa_id.c                 |  2 +-
 src/providers/ldap/ldap_common.h           | 10 +++--
 src/providers/ldap/ldap_id.c               | 59 ++++++++++++++++++++----------
 src/providers/ldap/ldap_id_netgroup.c      | 11 ++++--
 src/providers/ldap/ldap_id_services.c      |  5 ++-
 src/providers/ldap/sdap_async.h            |  1 +
 src/providers/ldap/sdap_async_initgroups.c |  5 ++-
 8 files changed, 64 insertions(+), 31 deletions(-)

diff --git a/src/providers/ad/ad_id.c b/src/providers/ad/ad_id.c
index f6e975e113c87461952bc435d0823b48a881c945..5d8c78371a2e91fbec554a2a90d424d758ffd1a2 100644
--- a/src/providers/ad/ad_id.c
+++ b/src/providers/ad/ad_id.c
@@ -34,7 +34,7 @@ ad_account_info_handler(struct be_req *be_req)
                              struct ad_id_ctx);
     sdap_id_ctx = ad_ctx->sdap_id_ctx;
 
-    return sdap_handle_account_info(be_req, sdap_id_ctx);
+    return sdap_handle_account_info(be_req, sdap_id_ctx, sdap_id_ctx->conn);
 }
 
 void
diff --git a/src/providers/ipa/ipa_id.c b/src/providers/ipa/ipa_id.c
index b7ae81f6629b922b252b6f2cfb4d566a6c207947..b11abaa7dbdfdae119d9ec18e1d53538b5382259 100644
--- a/src/providers/ipa/ipa_id.c
+++ b/src/providers/ipa/ipa_id.c
@@ -97,7 +97,7 @@ void ipa_account_info_handler(struct be_req *breq)
     } else {
         /* any account request is handled by sdap,
          * any invalid request is caught there. */
-        return sdap_handle_account_info(breq, ctx);
+        return sdap_handle_account_info(breq, ctx, ctx->conn);
     }
 
     if (!req) {
diff --git a/src/providers/ldap/ldap_common.h b/src/providers/ldap/ldap_common.h
index c1b62f727af98dda23833954d9fd05a13214b363..dbf6c7d59cc0a2006920717e3f733fc123fd3cb7 100644
--- a/src/providers/ldap/ldap_common.h
+++ b/src/providers/ldap/ldap_common.h
@@ -94,14 +94,16 @@ errno_t sdap_reinit_cleanup_recv(struct tevent_req *req);
 
 /* id */
 void sdap_account_info_handler(struct be_req *breq);
-void sdap_handle_account_info(struct be_req *breq, struct sdap_id_ctx *ctx);
+void sdap_handle_account_info(struct be_req *breq, struct sdap_id_ctx *ctx,
+                              struct sdap_id_conn_ctx *conn);
 int sdap_id_setup_tasks(struct sdap_id_ctx *ctx);
 
 struct tevent_req *
 sdap_handle_acct_req_send(TALLOC_CTX *mem_ctx,
                           struct be_req *breq,
                           struct be_acct_req *ar,
-                          struct sdap_id_ctx *id_ctx);
+                          struct sdap_id_ctx *id_ctx,
+                          struct sdap_id_conn_ctx *conn);
 errno_t
 sdap_handle_acct_req_recv(struct tevent_req *req,
                           int *_dp_error, const char **_err);
@@ -173,15 +175,16 @@ void sdap_mark_offline(struct sdap_id_ctx *ctx);
 struct tevent_req *groups_get_send(TALLOC_CTX *memctx,
                                    struct tevent_context *ev,
                                    struct sdap_id_ctx *ctx,
+                                   struct sdap_id_conn_ctx *conn,
                                    const char *name,
                                    int filter_type,
                                    int attrs_type);
 int groups_get_recv(struct tevent_req *req, int *dp_error_out);
 
-
 struct tevent_req *ldap_netgroup_get_send(TALLOC_CTX *memctx,
                                           struct tevent_context *ev,
                                           struct sdap_id_ctx *ctx,
+                                          struct sdap_id_conn_ctx *conn,
                                           const char *name);
 int ldap_netgroup_get_recv(struct tevent_req *req, int *dp_error_out);
 
@@ -189,6 +192,7 @@ struct tevent_req *
 services_get_send(TALLOC_CTX *mem_ctx,
                   struct tevent_context *ev,
                   struct sdap_id_ctx *id_ctx,
+                  struct sdap_id_conn_ctx *conn,
                   const char *name,
                   const char *protocol,
                   int filter_type);
diff --git a/src/providers/ldap/ldap_id.c b/src/providers/ldap/ldap_id.c
index 38f5529b68b574f9621a26bd30f5c3a58469daba..3aeffa83954ae0d058fbe47f9859507c60597f1e 100644
--- a/src/providers/ldap/ldap_id.c
+++ b/src/providers/ldap/ldap_id.c
@@ -39,6 +39,7 @@
 struct users_get_state {
     struct tevent_context *ev;
     struct sdap_id_ctx *ctx;
+    struct sdap_id_conn_ctx *conn;
     struct sdap_id_op *op;
     struct sysdb_ctx *sysdb;
     struct sss_domain_info *domain;
@@ -59,6 +60,7 @@ static void users_get_done(struct tevent_req *subreq);
 struct tevent_req *users_get_send(TALLOC_CTX *memctx,
                                   struct tevent_context *ev,
                                   struct sdap_id_ctx *ctx,
+                                  struct sdap_id_conn_ctx *conn,
                                   const char *name,
                                   int filter_type,
                                   int attrs_type)
@@ -78,9 +80,10 @@ struct tevent_req *users_get_send(TALLOC_CTX *memctx,
 
     state->ev = ev;
     state->ctx = ctx;
+    state->conn = conn;
     state->dp_error = DP_ERR_FATAL;
 
-    state->op = sdap_id_op_create(state, state->ctx->conn->conn_cache);
+    state->op = sdap_id_op_create(state, state->conn->conn_cache);
     if (!state->op) {
         DEBUG(2, ("sdap_id_op_create failed\n"));
         ret = ENOMEM;
@@ -360,6 +363,7 @@ int users_get_recv(struct tevent_req *req, int *dp_error_out)
 struct groups_get_state {
     struct tevent_context *ev;
     struct sdap_id_ctx *ctx;
+    struct sdap_id_conn_ctx *conn;
     struct sdap_id_op *op;
     struct sysdb_ctx *sysdb;
     struct sss_domain_info *domain;
@@ -380,6 +384,7 @@ static void groups_get_done(struct tevent_req *subreq);
 struct tevent_req *groups_get_send(TALLOC_CTX *memctx,
                                    struct tevent_context *ev,
                                    struct sdap_id_ctx *ctx,
+                                   struct sdap_id_conn_ctx *conn,
                                    const char *name,
                                    int filter_type,
                                    int attrs_type)
@@ -401,9 +406,10 @@ struct tevent_req *groups_get_send(TALLOC_CTX *memctx,
 
     state->ev = ev;
     state->ctx = ctx;
+    state->conn = conn;
     state->dp_error = DP_ERR_FATAL;
 
-    state->op = sdap_id_op_create(state, state->ctx->conn->conn_cache);
+    state->op = sdap_id_op_create(state, state->conn->conn_cache);
     if (!state->op) {
         DEBUG(2, ("sdap_id_op_create failed\n"));
         ret = ENOMEM;
@@ -671,6 +677,7 @@ int groups_get_recv(struct tevent_req *req, int *dp_error_out)
 struct groups_by_user_state {
     struct tevent_context *ev;
     struct sdap_id_ctx *ctx;
+    struct sdap_id_conn_ctx *conn;
     struct sdap_id_op *op;
     const char *name;
     const char **attrs;
@@ -685,6 +692,7 @@ static void groups_by_user_done(struct tevent_req *subreq);
 static struct tevent_req *groups_by_user_send(TALLOC_CTX *memctx,
                                               struct tevent_context *ev,
                                               struct sdap_id_ctx *ctx,
+                                              struct sdap_id_conn_ctx *conn,
                                               const char *name)
 {
     struct tevent_req *req;
@@ -697,8 +705,9 @@ static struct tevent_req *groups_by_user_send(TALLOC_CTX *memctx,
     state->ev = ev;
     state->ctx = ctx;
     state->dp_error = DP_ERR_FATAL;
+    state->conn = conn;
 
-    state->op = sdap_id_op_create(state, state->ctx->conn->conn_cache);
+    state->op = sdap_id_op_create(state, state->conn->conn_cache);
     if (!state->op) {
         DEBUG(2, ("sdap_id_op_create failed\n"));
         ret = ENOMEM;
@@ -762,6 +771,7 @@ static void groups_by_user_connect_done(struct tevent_req *subreq)
                                   state->ev,
                                   sdap_id_op_handle(state->op),
                                   state->ctx,
+                                  state->conn,
                                   state->name,
                                   state->attrs);
     if (!subreq) {
@@ -971,11 +981,13 @@ static void sdap_check_online_reinit_done(struct tevent_req *req)
 /* FIXME: embed this function in sssd_be and only call out
  * specific functions from modules ? */
 
-void sdap_handle_account_info(struct be_req *breq, struct sdap_id_ctx *ctx);
+void sdap_handle_account_info(struct be_req *breq, struct sdap_id_ctx *ctx,
+                              struct sdap_id_conn_ctx *conn);
 
 static struct tevent_req *get_user_and_group_send(TALLOC_CTX *memctx,
                                                   struct tevent_context *ev,
                                                   struct sdap_id_ctx *ctx,
+                                                  struct sdap_id_conn_ctx *conn,
                                                   const char *name,
                                                   int filter_type,
                                                   int attrs_type);
@@ -991,7 +1003,7 @@ void sdap_account_info_handler(struct be_req *breq)
         return sdap_handler_done(breq, DP_ERR_FATAL,
                                  EINVAL, "Invalid request data\n");
     }
-    return sdap_handle_account_info(breq, ctx);
+    return sdap_handle_account_info(breq, ctx, ctx->conn);
 }
 
 /* A generic LDAP account info handler */
@@ -1008,7 +1020,8 @@ struct tevent_req *
 sdap_handle_acct_req_send(TALLOC_CTX *mem_ctx,
                           struct be_req *breq,
                           struct be_acct_req *ar,
-                          struct sdap_id_ctx *id_ctx)
+                          struct sdap_id_ctx *id_ctx,
+                          struct sdap_id_conn_ctx *conn)
 {
     struct tevent_req *req;
     struct tevent_req *subreq;
@@ -1044,7 +1057,7 @@ sdap_handle_acct_req_send(TALLOC_CTX *mem_ctx,
             goto done;
         }
 
-        subreq = users_get_send(breq, be_ctx->ev, id_ctx,
+        subreq = users_get_send(breq, be_ctx->ev, id_ctx, conn,
                                 ar->filter_value,
                                 ar->filter_type,
                                 ar->attr_type);
@@ -1061,7 +1074,7 @@ sdap_handle_acct_req_send(TALLOC_CTX *mem_ctx,
             goto done;
         }
 
-        subreq = groups_get_send(breq, be_ctx->ev, id_ctx,
+        subreq = groups_get_send(breq, be_ctx->ev, id_ctx, conn,
                                  ar->filter_value,
                                  ar->filter_type,
                                  ar->attr_type);
@@ -1079,7 +1092,7 @@ sdap_handle_acct_req_send(TALLOC_CTX *mem_ctx,
             goto done;
         }
 
-        subreq = groups_by_user_send(breq, be_ctx->ev, id_ctx,
+        subreq = groups_by_user_send(breq, be_ctx->ev, id_ctx, conn,
                                      ar->filter_value);
         break;
 
@@ -1090,7 +1103,7 @@ sdap_handle_acct_req_send(TALLOC_CTX *mem_ctx,
             goto done;
         }
 
-        subreq = ldap_netgroup_get_send(breq, be_ctx->ev, id_ctx,
+        subreq = ldap_netgroup_get_send(breq, be_ctx->ev, id_ctx, conn,
                                         ar->filter_value);
         break;
 
@@ -1110,7 +1123,7 @@ sdap_handle_acct_req_send(TALLOC_CTX *mem_ctx,
             goto done;
         }
 
-        subreq = services_get_send(breq, be_ctx->ev, id_ctx,
+        subreq = services_get_send(breq, be_ctx->ev, id_ctx, conn,
                                    ar->filter_value,
                                    ar->extra_value,
                                    ar->filter_type);
@@ -1123,7 +1136,7 @@ sdap_handle_acct_req_send(TALLOC_CTX *mem_ctx,
             goto done;
         }
 
-        subreq = get_user_and_group_send(breq, be_ctx->ev, id_ctx,
+        subreq = get_user_and_group_send(breq, be_ctx->ev, id_ctx, conn,
                                          ar->filter_value,
                                          ar->filter_type,
                                          ar->attr_type);
@@ -1137,7 +1150,7 @@ sdap_handle_acct_req_send(TALLOC_CTX *mem_ctx,
             goto done;
         }
 
-        subreq = get_user_and_group_send(breq, be_ctx->ev, id_ctx,
+        subreq = get_user_and_group_send(breq, be_ctx->ev, id_ctx, conn,
                                          ar->filter_value,
                                          ar->filter_type,
                                          ar->attr_type);
@@ -1243,7 +1256,8 @@ sdap_handle_acct_req_recv(struct tevent_req *req,
 
 static void sdap_account_info_complete(struct tevent_req *req);
 
-void sdap_handle_account_info(struct be_req *breq, struct sdap_id_ctx *ctx)
+void sdap_handle_account_info(struct be_req *breq, struct sdap_id_ctx *ctx,
+                              struct sdap_id_conn_ctx *conn)
 {
     struct be_acct_req *ar;
     struct tevent_req *req;
@@ -1258,7 +1272,7 @@ void sdap_handle_account_info(struct be_req *breq, struct sdap_id_ctx *ctx)
                                  EINVAL, "Invalid private data");
     }
 
-    req = sdap_handle_acct_req_send(breq, breq, ar, ctx);
+    req = sdap_handle_acct_req_send(breq, breq, ar, ctx, conn);
     if (req == NULL) {
         return sdap_handler_done(breq, DP_ERR_FATAL, ENOMEM, "Out of memory");
     }
@@ -1297,6 +1311,7 @@ static void sdap_account_info_complete(struct tevent_req *req)
 struct get_user_and_group_state {
     struct tevent_context *ev;
     struct sdap_id_ctx *id_ctx;
+    struct sdap_id_conn_ctx *conn;
     struct sdap_id_op *op;
     struct sysdb_ctx *sysdb;
     struct sss_domain_info *domain;
@@ -1317,6 +1332,7 @@ static void get_user_and_group_groups_done(struct tevent_req *subreq);
 static struct tevent_req *get_user_and_group_send(TALLOC_CTX *memctx,
                                                   struct tevent_context *ev,
                                                   struct sdap_id_ctx *id_ctx,
+                                                  struct sdap_id_conn_ctx *conn,
                                                   const char *filter_val,
                                                   int filter_type,
                                                   int attrs_type)
@@ -1334,9 +1350,10 @@ static struct tevent_req *get_user_and_group_send(TALLOC_CTX *memctx,
 
     state->ev = ev;
     state->id_ctx = id_ctx;
+    state->conn = conn;
     state->dp_error = DP_ERR_FATAL;
 
-    state->op = sdap_id_op_create(state, state->id_ctx->conn->conn_cache);
+    state->op = sdap_id_op_create(state, state->conn->conn_cache);
     if (!state->op) {
         DEBUG(SSSDBG_OP_FAILURE, ("sdap_id_op_create failed\n"));
         ret = ENOMEM;
@@ -1349,8 +1366,9 @@ static struct tevent_req *get_user_and_group_send(TALLOC_CTX *memctx,
     state->filter_type = filter_type;
     state->attrs_type = attrs_type;
 
-    subreq = users_get_send(req, state->ev, state->id_ctx, state->filter_val,
-                            state->filter_type, state->attrs_type);
+    subreq = users_get_send(req, state->ev, state->id_ctx, state->conn,
+                            state->filter_val, state->filter_type,
+                            state->attrs_type);
     if (subreq == NULL) {
         DEBUG(SSSDBG_OP_FAILURE, ("users_get_send failed.\n"));
         ret = ENOMEM;
@@ -1386,8 +1404,9 @@ static void get_user_and_group_users_done(struct tevent_req *subreq)
         return;
     }
 
-    subreq = groups_get_send(req, state->ev, state->id_ctx, state->filter_val,
-                            state->filter_type, state->attrs_type);
+    subreq = groups_get_send(req, state->ev, state->id_ctx, state->conn,
+                             state->filter_val, state->filter_type,
+                             state->attrs_type);
     if (subreq == NULL) {
         DEBUG(SSSDBG_OP_FAILURE, ("groups_get_send failed.\n"));
         tevent_req_error(req, ENOMEM);
diff --git a/src/providers/ldap/ldap_id_netgroup.c b/src/providers/ldap/ldap_id_netgroup.c
index 6788a52e52fdc11663f8a264717cceb3145d6f34..5f44c0a9b195500c566c7c20e2ae16c661102f85 100644
--- a/src/providers/ldap/ldap_id_netgroup.c
+++ b/src/providers/ldap/ldap_id_netgroup.c
@@ -34,6 +34,7 @@ struct ldap_netgroup_get_state {
     struct tevent_context *ev;
     struct sdap_id_ctx *ctx;
     struct sdap_id_op *op;
+    struct sdap_id_conn_ctx *conn;
     struct sysdb_ctx *sysdb;
     struct sss_domain_info *domain;
 
@@ -54,9 +55,10 @@ static void ldap_netgroup_get_connect_done(struct tevent_req *subreq);
 static void ldap_netgroup_get_done(struct tevent_req *subreq);
 
 struct tevent_req *ldap_netgroup_get_send(TALLOC_CTX *memctx,
-                                     struct tevent_context *ev,
-                                     struct sdap_id_ctx *ctx,
-                                     const char *name)
+                                          struct tevent_context *ev,
+                                          struct sdap_id_ctx *ctx,
+                                          struct sdap_id_conn_ctx *conn,
+                                          const char *name)
 {
     struct tevent_req *req;
     struct ldap_netgroup_get_state *state;
@@ -68,9 +70,10 @@ struct tevent_req *ldap_netgroup_get_send(TALLOC_CTX *memctx,
 
     state->ev = ev;
     state->ctx = ctx;
+    state->conn = conn;
     state->dp_error = DP_ERR_FATAL;
 
-    state->op = sdap_id_op_create(state, state->ctx->conn->conn_cache);
+    state->op = sdap_id_op_create(state, state->conn->conn_cache);
     if (!state->op) {
         DEBUG(2, ("sdap_id_op_create failed\n"));
         ret = ENOMEM;
diff --git a/src/providers/ldap/ldap_id_services.c b/src/providers/ldap/ldap_id_services.c
index 5c3c53f2a6bea73eb26e43e00cb4dca723660c9d..2a3f104c7383144383f2411f2d5dc664f45d3779 100644
--- a/src/providers/ldap/ldap_id_services.c
+++ b/src/providers/ldap/ldap_id_services.c
@@ -36,6 +36,7 @@ struct sdap_services_get_state {
     struct sdap_id_op *op;
     struct sysdb_ctx *sysdb;
     struct sss_domain_info *domain;
+    struct sdap_id_conn_ctx *conn;
 
     const char *name;
     const char *protocol;
@@ -59,6 +60,7 @@ struct tevent_req *
 services_get_send(TALLOC_CTX *mem_ctx,
                   struct tevent_context *ev,
                   struct sdap_id_ctx *id_ctx,
+                  struct sdap_id_conn_ctx *conn,
                   const char *name,
                   const char *protocol,
                   int filter_type)
@@ -75,6 +77,7 @@ services_get_send(TALLOC_CTX *mem_ctx,
 
     state->ev = ev;
     state->id_ctx = id_ctx;
+    state->conn = conn;
     state->dp_error = DP_ERR_FATAL;
     state->sysdb = id_ctx->be->domain->sysdb;
     state->domain = state->id_ctx->be->domain;
@@ -82,7 +85,7 @@ services_get_send(TALLOC_CTX *mem_ctx,
     state->protocol = protocol;
     state->filter_type = filter_type;
 
-    state->op = sdap_id_op_create(state, state->id_ctx->conn->conn_cache);
+    state->op = sdap_id_op_create(state, state->conn->conn_cache);
     if (!state->op) {
         DEBUG(SSSDBG_MINOR_FAILURE, ("sdap_id_op_create failed\n"));
         ret = ENOMEM;
diff --git a/src/providers/ldap/sdap_async.h b/src/providers/ldap/sdap_async.h
index eedf8a706edadac437fadc80df7af4c4066bab2a..38c8302dc2afe81539e6b3dda0e840ad523f0178 100644
--- a/src/providers/ldap/sdap_async.h
+++ b/src/providers/ldap/sdap_async.h
@@ -117,6 +117,7 @@ struct tevent_req *sdap_get_initgr_send(TALLOC_CTX *memctx,
                                         struct tevent_context *ev,
                                         struct sdap_handle *sh,
                                         struct sdap_id_ctx *id_ctx,
+                                        struct sdap_id_conn_ctx *conn,
                                         const char *name,
                                         const char **grp_attrs);
 int sdap_get_initgr_recv(struct tevent_req *req);
diff --git a/src/providers/ldap/sdap_async_initgroups.c b/src/providers/ldap/sdap_async_initgroups.c
index 5a6ba02851be1534cebe7e604573f61a80a62b38..57193f71d28e5d908b258782af4062c59499fb77 100644
--- a/src/providers/ldap/sdap_async_initgroups.c
+++ b/src/providers/ldap/sdap_async_initgroups.c
@@ -2510,6 +2510,7 @@ struct sdap_get_initgr_state {
     struct sss_domain_info *dom;
     struct sdap_handle *sh;
     struct sdap_id_ctx *id_ctx;
+    struct sdap_id_conn_ctx *conn;
     const char *name;
     const char **grp_attrs;
     const char **user_attrs;
@@ -2531,6 +2532,7 @@ struct tevent_req *sdap_get_initgr_send(TALLOC_CTX *memctx,
                                         struct tevent_context *ev,
                                         struct sdap_handle *sh,
                                         struct sdap_id_ctx *id_ctx,
+                                        struct sdap_id_conn_ctx *conn,
                                         const char *name,
                                         const char **grp_attrs)
 {
@@ -2550,6 +2552,7 @@ struct tevent_req *sdap_get_initgr_send(TALLOC_CTX *memctx,
     state->dom = id_ctx->be->domain;
     state->sh = sh;
     state->id_ctx = id_ctx;
+    state->conn = conn;
     state->name = name;
     state->grp_attrs = grp_attrs;
     state->orig_user = NULL;
@@ -2947,7 +2950,7 @@ static void sdap_get_initgr_done(struct tevent_req *subreq)
         goto fail;
     }
 
-    subreq = groups_get_send(req, state->ev, state->id_ctx, gid,
+    subreq = groups_get_send(req, state->ev, state->id_ctx, state->conn, gid,
                              BE_FILTER_IDNUM, BE_ATTR_ALL);
     if (!subreq) {
         ret = ENOMEM;
-- 
1.8.2.1

-------------- next part --------------
>From 9b79044482c598f7f68c16ceba6864fe1b5f8864 Mon Sep 17 00:00:00 2001
From: Jakub Hrozek <jhrozek at redhat.com>
Date: Mon, 27 May 2013 08:48:02 +0200
Subject: [PATCH 05/15] LDAP: new SDAP domain structure

Previously an sdap_id_ctx was always tied to one domain with a single
set of search bases. But with the introduction of Global Catalog
lookups, primary domain and subdomains might have different search
bases.

This patch introduces a new structure sdap_domain that contains an sssd
domain or subdomain and a set of search bases. With this patch, there is
only one sdap_domain that describes the primary domain.
---
 src/providers/ad/ad_common.c                  | 17 +++--
 src/providers/ad/ad_subdomains.c              |  4 +-
 src/providers/ipa/ipa_common.c                | 21 ++++---
 src/providers/ipa/ipa_netgroups.c             | 10 +--
 src/providers/ldap/ldap_common.c              | 90 ++++++++++++++++++++++++---
 src/providers/ldap/ldap_common.h              | 14 +++++
 src/providers/ldap/ldap_id.c                  | 68 ++++++++++++++------
 src/providers/ldap/ldap_id_enum.c             | 38 ++++++-----
 src/providers/ldap/ldap_id_netgroup.c         |  9 ++-
 src/providers/ldap/ldap_id_services.c         |  9 ++-
 src/providers/ldap/ldap_init.c                |  2 +-
 src/providers/ldap/sdap.c                     | 58 ++++++++---------
 src/providers/ldap/sdap.h                     | 28 ++++++---
 src/providers/ldap/sdap_async.h               |  5 +-
 src/providers/ldap/sdap_async_autofs.c        |  2 +-
 src/providers/ldap/sdap_async_connection.c    |  3 +-
 src/providers/ldap/sdap_async_groups.c        | 16 ++---
 src/providers/ldap/sdap_async_groups_ad.c     |  2 +-
 src/providers/ldap/sdap_async_initgroups.c    | 18 +++---
 src/providers/ldap/sdap_async_initgroups_ad.c |  2 +-
 src/providers/ldap/sdap_async_nested_groups.c | 37 ++++++-----
 src/providers/ldap/sdap_async_netgroups.c     |  2 +-
 src/providers/ldap/sdap_async_private.h       |  3 +-
 src/providers/ldap/sdap_async_services.c      |  2 +-
 src/providers/ldap/sdap_async_sudo.c          |  2 +-
 25 files changed, 317 insertions(+), 145 deletions(-)

diff --git a/src/providers/ad/ad_common.c b/src/providers/ad/ad_common.c
index e34b2db4cd7317ceebdc843eb5381183b83ad64f..b32237b7b74508d2936729ae5e049dc8201b37a2 100644
--- a/src/providers/ad/ad_common.c
+++ b/src/providers/ad/ad_common.c
@@ -439,6 +439,13 @@ ad_get_id_options(struct ad_options *ad_opts,
         goto done;
     }
 
+    ret = sdap_domain_add(id_opts,
+                          ad_opts->id_ctx->sdap_id_ctx->be->domain,
+                          NULL);
+    if (ret != EOK) {
+        goto done;
+    }
+
     ret = dp_get_options(id_opts, cdb, conf_path,
                          ad_def_ldap_opts,
                          SDAP_OPTS_BASIC,
@@ -610,31 +617,31 @@ ad_set_search_bases(struct sdap_options *id_opts)
     /* Default search */
     ret = sdap_parse_search_base(id_opts, id_opts->basic,
                                  SDAP_SEARCH_BASE,
-                                 &id_opts->search_bases);
+                                 &id_opts->sdom->search_bases);
     if (ret != EOK && ret != ENOENT) goto done;
 
     /* User search */
     ret = sdap_parse_search_base(id_opts, id_opts->basic,
                                  SDAP_USER_SEARCH_BASE,
-                                 &id_opts->user_search_bases);
+                                 &id_opts->sdom->user_search_bases);
     if (ret != EOK && ret != ENOENT) goto done;
 
     /* Group search base */
     ret = sdap_parse_search_base(id_opts, id_opts->basic,
                                  SDAP_GROUP_SEARCH_BASE,
-                                 &id_opts->group_search_bases);
+                                 &id_opts->sdom->group_search_bases);
     if (ret != EOK && ret != ENOENT) goto done;
 
     /* Netgroup search */
     ret = sdap_parse_search_base(id_opts, id_opts->basic,
                                  SDAP_NETGROUP_SEARCH_BASE,
-                                 &id_opts->netgroup_search_bases);
+                                 &id_opts->sdom->netgroup_search_bases);
     if (ret != EOK && ret != ENOENT) goto done;
 
     /* Service search */
     ret = sdap_parse_search_base(id_opts, id_opts->basic,
                                  SDAP_SERVICE_SEARCH_BASE,
-                                 &id_opts->service_search_bases);
+                                 &id_opts->sdom->service_search_bases);
     if (ret != EOK && ret != ENOENT) goto done;
 
     ret = EOK;
diff --git a/src/providers/ad/ad_subdomains.c b/src/providers/ad/ad_subdomains.c
index da0c85e760ff65272970ae68f7024c71c8c28421..098663cc824cd56e1d633a1f70cc88ca79260f52 100644
--- a/src/providers/ad/ad_subdomains.c
+++ b/src/providers/ad/ad_subdomains.c
@@ -44,6 +44,7 @@
 struct ad_subdomains_ctx {
     struct be_ctx *be_ctx;
     struct sdap_id_ctx *sdap_id_ctx;
+    struct sdap_domain *sdom;
     struct sss_idmap_ctx *idmap_ctx;
     char *domain_name;
 
@@ -164,7 +165,7 @@ static errno_t ad_subdomains_get_master_sid(struct ad_subdomains_req_ctx *ctx)
     const char *master_sid_attrs[] = {AD_AT_OBJECT_SID, NULL};
 
 
-    base = ctx->sd_ctx->sdap_id_ctx->opts->search_bases[ctx->base_iter];
+    base = ctx->sd_ctx->sdom->search_bases[ctx->base_iter];
     if (base == NULL) {
         return EOK;
     }
@@ -497,6 +498,7 @@ int ad_subdom_init(struct be_ctx *be_ctx,
     }
 
     ctx->be_ctx = be_ctx;
+    ctx->sdom = id_ctx->sdap_id_ctx->opts->sdom;
     ctx->sdap_id_ctx = id_ctx->sdap_id_ctx;
     ctx->domain_name = talloc_strdup(ctx, ad_domain);
     if (ctx->domain_name == NULL) {
diff --git a/src/providers/ipa/ipa_common.c b/src/providers/ipa/ipa_common.c
index e371824840584056a6217b8b960159c03ce55fdd..a5e20393805d51aa36bc899b1554d127f4e9bf53 100644
--- a/src/providers/ipa/ipa_common.c
+++ b/src/providers/ipa/ipa_common.c
@@ -187,6 +187,13 @@ int ipa_get_id_options(struct ipa_options *ipa_opts,
         goto done;
     }
 
+    ret = sdap_domain_add(ipa_opts->id,
+                          ipa_opts->id_ctx->sdap_id_ctx->be->domain,
+                          NULL);
+    if (ret != EOK) {
+        goto done;
+    }
+
     /* get sdap options */
     ret = dp_get_options(ipa_opts->id, cdb, conf_path,
                          ipa_def_ldap_opts,
@@ -223,7 +230,7 @@ int ipa_get_id_options(struct ipa_options *ipa_opts,
     }
     ret = sdap_parse_search_base(ipa_opts->id, ipa_opts->id->basic,
                                  SDAP_SEARCH_BASE,
-                                 &ipa_opts->id->search_bases);
+                                 &ipa_opts->id->sdom->search_bases);
     if (ret != EOK) goto done;
 
     /* set krb realm */
@@ -277,7 +284,7 @@ int ipa_get_id_options(struct ipa_options *ipa_opts,
     }
     ret = sdap_parse_search_base(ipa_opts->id, ipa_opts->id->basic,
                                  SDAP_USER_SEARCH_BASE,
-                                 &ipa_opts->id->user_search_bases);
+                                 &ipa_opts->id->sdom->user_search_bases);
     if (ret != EOK) goto done;
 
     if (NULL == dp_opt_get_string(ipa_opts->id->basic,
@@ -296,7 +303,7 @@ int ipa_get_id_options(struct ipa_options *ipa_opts,
     }
     ret = sdap_parse_search_base(ipa_opts->id, ipa_opts->id->basic,
                                  SDAP_GROUP_SEARCH_BASE,
-                                 &ipa_opts->id->group_search_bases);
+                                 &ipa_opts->id->sdom->group_search_bases);
     if (ret != EOK) goto done;
 
     if (NULL == dp_opt_get_string(ipa_opts->id->basic,
@@ -334,7 +341,7 @@ int ipa_get_id_options(struct ipa_options *ipa_opts,
     }
     ret = sdap_parse_search_base(ipa_opts->id, ipa_opts->id->basic,
                                  SDAP_SUDO_SEARCH_BASE,
-                                 &ipa_opts->id->sudo_search_bases);
+                                 &ipa_opts->id->sdom->sudo_search_bases);
     if (ret != EOK) goto done;
 
     if (NULL == dp_opt_get_string(ipa_opts->id->basic,
@@ -357,7 +364,7 @@ int ipa_get_id_options(struct ipa_options *ipa_opts,
     }
     ret = sdap_parse_search_base(ipa_opts->id, ipa_opts->id->basic,
                                  SDAP_NETGROUP_SEARCH_BASE,
-                                 &ipa_opts->id->netgroup_search_bases);
+                                 &ipa_opts->id->sdom->netgroup_search_bases);
     if (ret != EOK) goto done;
 
     if (NULL == dp_opt_get_string(ipa_opts->basic,
@@ -450,7 +457,7 @@ int ipa_get_id_options(struct ipa_options *ipa_opts,
     }
     ret = sdap_parse_search_base(ipa_opts->id, ipa_opts->id->basic,
                                  SDAP_SERVICE_SEARCH_BASE,
-                                 &ipa_opts->id->service_search_bases);
+                                 &ipa_opts->id->sdom->service_search_bases);
     if (ret != EOK) goto done;
 
     if (NULL == dp_opt_get_string(ipa_opts->basic,
@@ -983,7 +990,7 @@ int ipa_get_autofs_options(struct ipa_options *ipa_opts,
 
     ret = sdap_parse_search_base(ipa_opts->id, ipa_opts->id->basic,
                                  SDAP_AUTOFS_SEARCH_BASE,
-                                 &ipa_opts->id->autofs_search_bases);
+                                 &ipa_opts->id->sdom->autofs_search_bases);
     if (ret != EOK && ret != ENOENT) {
         DEBUG(SSSDBG_OP_FAILURE, ("Could not parse autofs search base\n"));
         goto done;
diff --git a/src/providers/ipa/ipa_netgroups.c b/src/providers/ipa/ipa_netgroups.c
index f9e43b87fd5b373239369eb998b639fec697c6bd..0f36fc9f542f7b58ebb45664edc0e964ad2f92ff 100644
--- a/src/providers/ipa/ipa_netgroups.c
+++ b/src/providers/ipa/ipa_netgroups.c
@@ -215,7 +215,7 @@ struct tevent_req *ipa_get_netgroups_send(TALLOC_CTX *memctx,
     state->netgr_base_iter = 0;
     state->dom = dom;
 
-    if (!ipa_options->id->netgroup_search_bases) {
+    if (!ipa_options->id->sdom->netgroup_search_bases) {
         DEBUG(SSSDBG_CRIT_FAILURE,
               ("Netgroup lookup request without a search base\n"));
         ret = EINVAL;
@@ -248,7 +248,7 @@ static errno_t ipa_netgr_next_base(struct tevent_req *req)
     struct sdap_search_base **netgr_bases;
 
     state = tevent_req_data(req, struct ipa_get_netgroups_state);
-    netgr_bases = state->ipa_opts->id->netgroup_search_bases;
+    netgr_bases = state->ipa_opts->id->sdom->netgroup_search_bases;
 
     talloc_zfree(state->filter);
     state->filter = sdap_get_id_specific_filter(
@@ -307,7 +307,7 @@ static void ipa_get_netgroups_process(struct tevent_req *subreq)
     hash_key_t key;
     hash_value_t value;
 
-    netgr_bases = state->ipa_opts->id->netgroup_search_bases;
+    netgr_bases = state->ipa_opts->id->sdom->netgroup_search_bases;
 
     ret = sdap_get_generic_recv(subreq, state, &netgroups_count, &netgroups);
     talloc_zfree(subreq);
@@ -432,7 +432,7 @@ static int ipa_netgr_fetch_netgroups(struct ipa_get_netgroups_state *state,
     struct tevent_req *subreq;
     struct sdap_search_base **bases;
 
-    bases = state->ipa_opts->id->netgroup_search_bases;
+    bases = state->ipa_opts->id->sdom->netgroup_search_bases;
     if (bases[state->netgr_base_iter] == NULL) {
         /* No more bases to try */
         return ENOENT;
@@ -473,7 +473,7 @@ static int ipa_netgr_fetch_users(struct ipa_get_netgroups_state *state,
     struct tevent_req *subreq;
     struct sdap_search_base **bases;
 
-    bases = state->ipa_opts->id->user_search_bases;
+    bases = state->ipa_opts->id->sdom->user_search_bases;
     if (bases[state->user_base_iter] == NULL) {
         return ENOENT;
     }
diff --git a/src/providers/ldap/ldap_common.c b/src/providers/ldap/ldap_common.c
index 7939cd0e99c6747d2edf5101238a0502b8d1141a..7a319b1e5ceef1a58d503da1b5f437b9c62bd321 100644
--- a/src/providers/ldap/ldap_common.c
+++ b/src/providers/ldap/ldap_common.c
@@ -39,8 +39,79 @@
 /* a fd the child process would log into */
 int ldap_child_debug_fd = -1;
 
+int
+sdap_domain_destructor(TALLOC_CTX *ctx)
+{
+    struct sdap_domain *dom =
+            talloc_get_type(ctx, struct sdap_domain);
+    DLIST_REMOVE(*(dom->head), dom);
+    return 0;
+}
+
+struct sdap_domain *
+sdap_domain_get(struct sdap_options *opts,
+                struct sss_domain_info *dom)
+{
+    struct sdap_domain *sditer;
+
+    DLIST_FOR_EACH(sditer, opts->sdom) {
+        if (sditer->dom == dom) {
+            break;
+        }
+    }
+
+    return sditer;
+}
+
+errno_t
+sdap_domain_add(struct sdap_options *opts,
+                struct sss_domain_info *dom,
+                struct sdap_domain **_sdom)
+{
+    struct sdap_domain *sdom;
+
+    sdom = talloc_zero(opts, struct sdap_domain);
+    if (sdom == NULL) {
+        return ENOMEM;
+    }
+    sdom->dom = dom;
+    sdom->head = &opts->sdom;
+
+    if (opts->sdom) {
+        /* Only allow subdomains of the parent domain */
+        if (dom->parent == NULL ||
+            dom->parent != opts->sdom->dom) {
+            DEBUG(SSSDBG_OP_FAILURE, ("Domain %s is not a subdomain of %s\n",
+                  dom->name, opts->sdom->dom->name));
+            return EINVAL;
+        }
+    }
+
+    talloc_set_destructor((TALLOC_CTX *)sdom, sdap_domain_destructor);
+    DLIST_ADD_END(opts->sdom, sdom, struct sdap_domain *);
+
+    if (_sdom) *_sdom = sdom;
+    return EOK;
+}
+
+void
+sdap_domain_remove(struct sss_domain_info *subdom)
+{
+    struct sss_domain_info *diter;
+
+    if (subdom->parent == NULL) return;
+
+    for (diter = subdom->parent->subdomains;
+         diter;
+         diter = get_next_domain(diter, false)) {
+        if (diter == subdom) break;
+    }
+
+    talloc_free(diter);
+}
 
 int ldap_get_options(TALLOC_CTX *memctx,
+                     struct sss_domain_info *dom,
                      struct confdb_ctx *cdb,
                      const char *conf_path,
                      struct sdap_options **_opts)
@@ -72,6 +143,11 @@ int ldap_get_options(TALLOC_CTX *memctx,
     opts = talloc_zero(memctx, struct sdap_options);
     if (!opts) return ENOMEM;
 
+    ret = sdap_domain_add(opts, dom, NULL);
+    if (ret != EOK) {
+        goto done;
+    }
+
     ret = dp_get_options(opts, cdb, conf_path,
                          default_basic_opts,
                          SDAP_OPTS_BASIC,
@@ -105,31 +181,31 @@ int ldap_get_options(TALLOC_CTX *memctx,
     /* Default search */
     ret = sdap_parse_search_base(opts, opts->basic,
                                  SDAP_SEARCH_BASE,
-                                 &opts->search_bases);
+                                 &opts->sdom->search_bases);
     if (ret != EOK && ret != ENOENT) goto done;
 
     /* User search */
     ret = sdap_parse_search_base(opts, opts->basic,
                                  SDAP_USER_SEARCH_BASE,
-                                 &opts->user_search_bases);
+                                 &opts->sdom->user_search_bases);
     if (ret != EOK && ret != ENOENT) goto done;
 
     /* Group search base */
     ret = sdap_parse_search_base(opts, opts->basic,
                                  SDAP_GROUP_SEARCH_BASE,
-                                 &opts->group_search_bases);
+                                 &opts->sdom->group_search_bases);
     if (ret != EOK && ret != ENOENT) goto done;
 
     /* Netgroup search */
     ret = sdap_parse_search_base(opts, opts->basic,
                                  SDAP_NETGROUP_SEARCH_BASE,
-                                 &opts->netgroup_search_bases);
+                                 &opts->sdom->netgroup_search_bases);
     if (ret != EOK && ret != ENOENT) goto done;
 
     /* Service search */
     ret = sdap_parse_search_base(opts, opts->basic,
                                  SDAP_SERVICE_SEARCH_BASE,
-                                 &opts->service_search_bases);
+                                 &opts->sdom->service_search_bases);
     if (ret != EOK && ret != ENOENT) goto done;
 
     pwd_policy = dp_opt_get_string(opts->basic, SDAP_PWD_POLICY);
@@ -377,7 +453,7 @@ int ldap_get_sudo_options(TALLOC_CTX *memctx,
 
     ret = sdap_parse_search_base(opts, opts->basic,
                                  SDAP_SUDO_SEARCH_BASE,
-                                 &opts->sudo_search_bases);
+                                 &opts->sdom->sudo_search_bases);
     if (ret != EOK && ret != ENOENT) {
         DEBUG(SSSDBG_OP_FAILURE, ("Could not parse SUDO search base\n"));
         return ret;
@@ -435,7 +511,7 @@ int ldap_get_autofs_options(TALLOC_CTX *memctx,
 
     ret = sdap_parse_search_base(opts, opts->basic,
                                  SDAP_AUTOFS_SEARCH_BASE,
-                                 &opts->autofs_search_bases);
+                                 &opts->sdom->autofs_search_bases);
     if (ret != EOK && ret != ENOENT) {
         DEBUG(SSSDBG_OP_FAILURE, ("Could not parse autofs search base\n"));
         return ret;
diff --git a/src/providers/ldap/ldap_common.h b/src/providers/ldap/ldap_common.h
index dbf6c7d59cc0a2006920717e3f733fc123fd3cb7..4945853e6cc1e7657d39639e2a39ba2b90646e9e 100644
--- a/src/providers/ldap/ldap_common.h
+++ b/src/providers/ldap/ldap_common.h
@@ -103,6 +103,7 @@ sdap_handle_acct_req_send(TALLOC_CTX *mem_ctx,
                           struct be_req *breq,
                           struct be_acct_req *ar,
                           struct sdap_id_ctx *id_ctx,
+                          struct sdap_domain *sdom,
                           struct sdap_id_conn_ctx *conn);
 errno_t
 sdap_handle_acct_req_recv(struct tevent_req *req,
@@ -147,6 +148,7 @@ void sdap_remove_kdcinfo_files_callback(void *pvt);
 
 /* options parser */
 int ldap_get_options(TALLOC_CTX *memctx,
+                     struct sss_domain_info *dom,
                      struct confdb_ctx *cdb,
                      const char *conf_path,
                      struct sdap_options **_opts);
@@ -175,6 +177,7 @@ void sdap_mark_offline(struct sdap_id_ctx *ctx);
 struct tevent_req *groups_get_send(TALLOC_CTX *memctx,
                                    struct tevent_context *ev,
                                    struct sdap_id_ctx *ctx,
+                                   struct sdap_domain *sdom,
                                    struct sdap_id_conn_ctx *conn,
                                    const char *name,
                                    int filter_type,
@@ -184,6 +187,7 @@ int groups_get_recv(struct tevent_req *req, int *dp_error_out);
 struct tevent_req *ldap_netgroup_get_send(TALLOC_CTX *memctx,
                                           struct tevent_context *ev,
                                           struct sdap_id_ctx *ctx,
+                                          struct sdap_domain *sdom,
                                           struct sdap_id_conn_ctx *conn,
                                           const char *name);
 int ldap_netgroup_get_recv(struct tevent_req *req, int *dp_error_out);
@@ -192,6 +196,7 @@ struct tevent_req *
 services_get_send(TALLOC_CTX *mem_ctx,
                   struct tevent_context *ev,
                   struct sdap_id_ctx *id_ctx,
+                  struct sdap_domain *sdom,
                   struct sdap_id_conn_ctx *conn,
                   const char *name,
                   const char *protocol,
@@ -228,6 +233,15 @@ errno_t msgs2attrs_array(TALLOC_CTX *mem_ctx, size_t count,
                          struct ldb_message **msgs,
                          struct sysdb_attrs ***attrs);
 
+errno_t sdap_domain_add(struct sdap_options *opts,
+                        struct sss_domain_info *dom,
+                        struct sdap_domain **_sdom);
+
+void sdap_domain_remove(struct sss_domain_info *subdom);
+
+struct sdap_domain *sdap_domain_get(struct sdap_options *opts,
+                                    struct sss_domain_info *dom);
+
 errno_t sdap_parse_search_base(TALLOC_CTX *mem_ctx,
                                struct dp_option *opts, int class,
                                struct sdap_search_base ***_search_bases);
diff --git a/src/providers/ldap/ldap_id.c b/src/providers/ldap/ldap_id.c
index 3aeffa83954ae0d058fbe47f9859507c60597f1e..bfb34cc70b8f098245c2eada505e65b9589cb6a5 100644
--- a/src/providers/ldap/ldap_id.c
+++ b/src/providers/ldap/ldap_id.c
@@ -39,6 +39,7 @@
 struct users_get_state {
     struct tevent_context *ev;
     struct sdap_id_ctx *ctx;
+    struct sdap_domain *sdom;
     struct sdap_id_conn_ctx *conn;
     struct sdap_id_op *op;
     struct sysdb_ctx *sysdb;
@@ -60,6 +61,7 @@ static void users_get_done(struct tevent_req *subreq);
 struct tevent_req *users_get_send(TALLOC_CTX *memctx,
                                   struct tevent_context *ev,
                                   struct sdap_id_ctx *ctx,
+                                  struct sdap_domain *sdom,
                                   struct sdap_id_conn_ctx *conn,
                                   const char *name,
                                   int filter_type,
@@ -80,6 +82,7 @@ struct tevent_req *users_get_send(TALLOC_CTX *memctx,
 
     state->ev = ev;
     state->ctx = ctx;
+    state->sdom = sdom;
     state->conn = conn;
     state->dp_error = DP_ERR_FATAL;
 
@@ -90,8 +93,8 @@ struct tevent_req *users_get_send(TALLOC_CTX *memctx,
         goto fail;
     }
 
-    state->sysdb = ctx->be->domain->sysdb;
-    state->domain = state->ctx->be->domain;
+    state->domain = sdom->dom;
+    state->sysdb = sdom->dom->sysdb;
     state->name = name;
     state->filter_type = filter_type;
 
@@ -223,7 +226,7 @@ static void users_get_connect_done(struct tevent_req *subreq)
     subreq = sdap_get_users_send(state, state->ev,
                                  state->domain, state->sysdb,
                                  state->ctx->opts,
-                                 state->ctx->opts->user_search_bases,
+                                 state->sdom->user_search_bases,
                                  sdap_id_op_handle(state->op),
                                  state->attrs, state->filter,
                                  dp_opt_get_int(state->ctx->opts->basic,
@@ -341,6 +344,7 @@ static void users_get_done(struct tevent_req *subreq)
     }
 
     state->dp_error = DP_ERR_OK;
+    /* FIXME - return sdap error so that we know the user was not found */
     tevent_req_done(req);
 }
 
@@ -363,6 +367,7 @@ int users_get_recv(struct tevent_req *req, int *dp_error_out)
 struct groups_get_state {
     struct tevent_context *ev;
     struct sdap_id_ctx *ctx;
+    struct sdap_domain *sdom;
     struct sdap_id_conn_ctx *conn;
     struct sdap_id_op *op;
     struct sysdb_ctx *sysdb;
@@ -384,6 +389,7 @@ static void groups_get_done(struct tevent_req *subreq);
 struct tevent_req *groups_get_send(TALLOC_CTX *memctx,
                                    struct tevent_context *ev,
                                    struct sdap_id_ctx *ctx,
+                                   struct sdap_domain *sdom,
                                    struct sdap_id_conn_ctx *conn,
                                    const char *name,
                                    int filter_type,
@@ -406,6 +412,7 @@ struct tevent_req *groups_get_send(TALLOC_CTX *memctx,
 
     state->ev = ev;
     state->ctx = ctx;
+    state->sdom = sdom;
     state->conn = conn;
     state->dp_error = DP_ERR_FATAL;
 
@@ -416,8 +423,8 @@ struct tevent_req *groups_get_send(TALLOC_CTX *memctx,
         goto fail;
     }
 
-    state->sysdb = ctx->be->domain->sysdb;
-    state->domain = state->ctx->be->domain;
+    state->domain = sdom->dom;
+    state->sysdb = sdom->dom->sysdb;
     state->name = name;
     state->filter_type = filter_type;
 
@@ -571,9 +578,8 @@ static void groups_get_connect_done(struct tevent_req *subreq)
     }
 
     subreq = sdap_get_groups_send(state, state->ev,
-                                  state->domain, state->sysdb,
+                                  state->sdom,
                                   state->ctx->opts,
-                                  state->ctx->opts->group_search_bases,
                                   sdap_id_op_handle(state->op),
                                   state->attrs, state->filter,
                                   dp_opt_get_int(state->ctx->opts->basic,
@@ -677,8 +683,12 @@ int groups_get_recv(struct tevent_req *req, int *dp_error_out)
 struct groups_by_user_state {
     struct tevent_context *ev;
     struct sdap_id_ctx *ctx;
+    struct sdap_domain *sdom;
     struct sdap_id_conn_ctx *conn;
     struct sdap_id_op *op;
+    struct sysdb_ctx *sysdb;
+    struct sss_domain_info *domain;
+
     const char *name;
     const char **attrs;
 
@@ -692,6 +702,7 @@ static void groups_by_user_done(struct tevent_req *subreq);
 static struct tevent_req *groups_by_user_send(TALLOC_CTX *memctx,
                                               struct tevent_context *ev,
                                               struct sdap_id_ctx *ctx,
+                                              struct sdap_domain *sdom,
                                               struct sdap_id_conn_ctx *conn,
                                               const char *name)
 {
@@ -706,6 +717,7 @@ static struct tevent_req *groups_by_user_send(TALLOC_CTX *memctx,
     state->ctx = ctx;
     state->dp_error = DP_ERR_FATAL;
     state->conn = conn;
+    state->sdom = sdom;
 
     state->op = sdap_id_op_create(state, state->conn->conn_cache);
     if (!state->op) {
@@ -715,6 +727,8 @@ static struct tevent_req *groups_by_user_send(TALLOC_CTX *memctx,
     }
 
     state->name = name;
+    state->domain = sdom->dom;
+    state->sysdb = sdom->dom->sysdb;
 
     ret = build_attrs_from_map(state, ctx->opts->group_map, SDAP_OPTS_GROUP,
                                NULL, &state->attrs, NULL);
@@ -769,6 +783,7 @@ static void groups_by_user_connect_done(struct tevent_req *subreq)
 
     subreq = sdap_get_initgr_send(state,
                                   state->ev,
+                                  state->sdom,
                                   sdap_id_op_handle(state->op),
                                   state->ctx,
                                   state->conn,
@@ -987,6 +1002,7 @@ void sdap_handle_account_info(struct be_req *breq, struct sdap_id_ctx *ctx,
 static struct tevent_req *get_user_and_group_send(TALLOC_CTX *memctx,
                                                   struct tevent_context *ev,
                                                   struct sdap_id_ctx *ctx,
+                                                  struct sdap_domain *sdom,
                                                   struct sdap_id_conn_ctx *conn,
                                                   const char *name,
                                                   int filter_type,
@@ -1021,6 +1037,7 @@ sdap_handle_acct_req_send(TALLOC_CTX *mem_ctx,
                           struct be_req *breq,
                           struct be_acct_req *ar,
                           struct sdap_id_ctx *id_ctx,
+                          struct sdap_domain *sdom,
                           struct sdap_id_conn_ctx *conn)
 {
     struct tevent_req *req;
@@ -1057,7 +1074,8 @@ sdap_handle_acct_req_send(TALLOC_CTX *mem_ctx,
             goto done;
         }
 
-        subreq = users_get_send(breq, be_ctx->ev, id_ctx, conn,
+        subreq = users_get_send(breq, be_ctx->ev, id_ctx,
+                                sdom, conn,
                                 ar->filter_value,
                                 ar->filter_type,
                                 ar->attr_type);
@@ -1074,7 +1092,8 @@ sdap_handle_acct_req_send(TALLOC_CTX *mem_ctx,
             goto done;
         }
 
-        subreq = groups_get_send(breq, be_ctx->ev, id_ctx, conn,
+        subreq = groups_get_send(breq, be_ctx->ev, id_ctx,
+                                 sdom, conn,
                                  ar->filter_value,
                                  ar->filter_type,
                                  ar->attr_type);
@@ -1092,7 +1111,8 @@ sdap_handle_acct_req_send(TALLOC_CTX *mem_ctx,
             goto done;
         }
 
-        subreq = groups_by_user_send(breq, be_ctx->ev, id_ctx, conn,
+        subreq = groups_by_user_send(breq, be_ctx->ev, id_ctx,
+                                     id_ctx->opts->sdom, sdom, conn,
                                      ar->filter_value);
         break;
 
@@ -1103,7 +1123,8 @@ sdap_handle_acct_req_send(TALLOC_CTX *mem_ctx,
             goto done;
         }
 
-        subreq = ldap_netgroup_get_send(breq, be_ctx->ev, id_ctx, conn,
+        subreq = ldap_netgroup_get_send(breq, be_ctx->ev, id_ctx,
+                                        id_ctx->opts->sdom, sdom, conn,
                                         ar->filter_value);
         break;
 
@@ -1123,7 +1144,8 @@ sdap_handle_acct_req_send(TALLOC_CTX *mem_ctx,
             goto done;
         }
 
-        subreq = services_get_send(breq, be_ctx->ev, id_ctx, conn,
+        subreq = services_get_send(breq, be_ctx->ev, id_ctx,
+                                   sdom, conn,
                                    ar->filter_value,
                                    ar->extra_value,
                                    ar->filter_type);
@@ -1136,7 +1158,8 @@ sdap_handle_acct_req_send(TALLOC_CTX *mem_ctx,
             goto done;
         }
 
-        subreq = get_user_and_group_send(breq, be_ctx->ev, id_ctx, conn,
+        subreq = get_user_and_group_send(breq, be_ctx->ev, id_ctx,
+                                         sdom, conn,
                                          ar->filter_value,
                                          ar->filter_type,
                                          ar->attr_type);
@@ -1150,7 +1173,8 @@ sdap_handle_acct_req_send(TALLOC_CTX *mem_ctx,
             goto done;
         }
 
-        subreq = get_user_and_group_send(breq, be_ctx->ev, id_ctx, conn,
+        subreq = get_user_and_group_send(breq, be_ctx->ev, id_ctx,
+                                         sdom, conn,
                                          ar->filter_value,
                                          ar->filter_type,
                                          ar->attr_type);
@@ -1272,7 +1296,8 @@ void sdap_handle_account_info(struct be_req *breq, struct sdap_id_ctx *ctx,
                                  EINVAL, "Invalid private data");
     }
 
-    req = sdap_handle_acct_req_send(breq, breq, ar, ctx, conn);
+    req = sdap_handle_acct_req_send(breq, breq, ar, ctx,
+                                    ctx->opts->sdom, conn);
     if (req == NULL) {
         return sdap_handler_done(breq, DP_ERR_FATAL, ENOMEM, "Out of memory");
     }
@@ -1311,6 +1336,7 @@ static void sdap_account_info_complete(struct tevent_req *req)
 struct get_user_and_group_state {
     struct tevent_context *ev;
     struct sdap_id_ctx *id_ctx;
+    struct sdap_domain *sdom;
     struct sdap_id_conn_ctx *conn;
     struct sdap_id_op *op;
     struct sysdb_ctx *sysdb;
@@ -1332,6 +1358,7 @@ static void get_user_and_group_groups_done(struct tevent_req *subreq);
 static struct tevent_req *get_user_and_group_send(TALLOC_CTX *memctx,
                                                   struct tevent_context *ev,
                                                   struct sdap_id_ctx *id_ctx,
+                                                  struct sdap_domain *sdom,
                                                   struct sdap_id_conn_ctx *conn,
                                                   const char *filter_val,
                                                   int filter_type,
@@ -1350,6 +1377,7 @@ static struct tevent_req *get_user_and_group_send(TALLOC_CTX *memctx,
 
     state->ev = ev;
     state->id_ctx = id_ctx;
+    state->sdom = sdom;
     state->conn = conn;
     state->dp_error = DP_ERR_FATAL;
 
@@ -1360,13 +1388,14 @@ static struct tevent_req *get_user_and_group_send(TALLOC_CTX *memctx,
         goto fail;
     }
 
-    state->sysdb = state->id_ctx->be->domain->sysdb;
-    state->domain = state->id_ctx->be->domain;
+    state->domain = sdom->dom;
+    state->sysdb = sdom->dom->sysdb;
     state->filter_val = filter_val;
     state->filter_type = filter_type;
     state->attrs_type = attrs_type;
 
-    subreq = users_get_send(req, state->ev, state->id_ctx, state->conn,
+    subreq = users_get_send(req, state->ev, state->id_ctx,
+                            state->sdom, state->conn,
                             state->filter_val, state->filter_type,
                             state->attrs_type);
     if (subreq == NULL) {
@@ -1404,7 +1433,8 @@ static void get_user_and_group_users_done(struct tevent_req *subreq)
         return;
     }
 
-    subreq = groups_get_send(req, state->ev, state->id_ctx, state->conn,
+    subreq = groups_get_send(req, state->ev, state->id_ctx,
+                             state->sdom, state->conn,
                              state->filter_val, state->filter_type,
                              state->attrs_type);
     if (subreq == NULL) {
diff --git a/src/providers/ldap/ldap_id_enum.c b/src/providers/ldap/ldap_id_enum.c
index 7a2129d97fcbcae49e627ff3bca481dd3f568201..719b130105b6c15ec053d5ed8c11dfc7b66bcda5 100644
--- a/src/providers/ldap/ldap_id_enum.c
+++ b/src/providers/ldap/ldap_id_enum.c
@@ -188,12 +188,14 @@ struct global_enum_state {
 static struct tevent_req *enum_users_send(TALLOC_CTX *memctx,
                                           struct tevent_context *ev,
                                           struct sdap_id_ctx *ctx,
+                                          struct sdap_domain *sdom,
                                           struct sdap_id_op *op,
                                           bool purge);
 static void ldap_id_enum_users_done(struct tevent_req *subreq);
 static struct tevent_req *enum_groups_send(TALLOC_CTX *memctx,
                                           struct tevent_context *ev,
                                           struct sdap_id_ctx *ctx,
+                                          struct sdap_domain *sdom,
                                           struct sdap_id_op *op,
                                           bool purge);
 static void ldap_id_enum_groups_done(struct tevent_req *subreq);
@@ -277,8 +279,8 @@ static void ldap_id_enumerate_connect_done(struct tevent_req *subreq)
     }
 
     subreq = enum_users_send(state, state->ev,
-                             state->ctx, state->op,
-                             state->purge);
+                             state->ctx, state->ctx->opts->sdom,
+                             state->op, state->purge);
     if(!subreq) {
         tevent_req_error(req, ENOMEM);
         return;
@@ -332,7 +334,9 @@ static void ldap_id_enum_users_done(struct tevent_req *subreq)
         return;
     }
 
-    subreq = enum_groups_send(state, state->ev, state->ctx, state->op, state->purge);
+    subreq = enum_groups_send(state, state->ev, state->ctx,
+                              state->ctx->opts->sdom,
+                              state->op, state->purge);
     if (!subreq) {
         tevent_req_error(req, ENOMEM);
         return;
@@ -465,6 +469,7 @@ static void ldap_id_enum_cleanup_done(struct tevent_req *subreq)
 struct enum_users_state {
     struct tevent_context *ev;
     struct sdap_id_ctx *ctx;
+    struct sdap_domain *sdom;
     struct sdap_id_op *op;
 
     char *filter;
@@ -476,6 +481,7 @@ static void enum_users_op_done(struct tevent_req *subreq);
 static struct tevent_req *enum_users_send(TALLOC_CTX *memctx,
                                           struct tevent_context *ev,
                                           struct sdap_id_ctx *ctx,
+                                          struct sdap_domain *sdom,
                                           struct sdap_id_op *op,
                                           bool purge)
 {
@@ -488,6 +494,7 @@ static struct tevent_req *enum_users_send(TALLOC_CTX *memctx,
     if (!req) return NULL;
 
     state->ev = ev;
+    state->sdom = sdom;
     state->ctx = ctx;
     state->op = op;
 
@@ -564,10 +571,10 @@ static struct tevent_req *enum_users_send(TALLOC_CTX *memctx,
      */
 
     subreq = sdap_get_users_send(state, state->ev,
-                                 state->ctx->be->domain,
-                                 state->ctx->be->domain->sysdb,
+                                 state->sdom->dom,
+                                 state->sdom->dom->sysdb,
                                  state->ctx->opts,
-                                 state->ctx->opts->user_search_bases,
+                                 state->sdom->user_search_bases,
                                  sdap_id_op_handle(state->op),
                                  state->attrs, state->filter,
                                  dp_opt_get_int(state->ctx->opts->basic,
@@ -627,6 +634,7 @@ static void enum_users_op_done(struct tevent_req *subreq)
 struct enum_groups_state {
     struct tevent_context *ev;
     struct sdap_id_ctx *ctx;
+    struct sdap_domain *sdom;
     struct sdap_id_op *op;
 
     char *filter;
@@ -638,6 +646,7 @@ static void enum_groups_op_done(struct tevent_req *subreq);
 static struct tevent_req *enum_groups_send(TALLOC_CTX *memctx,
                                           struct tevent_context *ev,
                                           struct sdap_id_ctx *ctx,
+                                          struct sdap_domain *sdom,
                                           struct sdap_id_op *op,
                                           bool purge)
 {
@@ -650,6 +659,7 @@ static struct tevent_req *enum_groups_send(TALLOC_CTX *memctx,
     if (!req) return NULL;
 
     state->ev = ev;
+    state->sdom = sdom;
     state->ctx = ctx;
     state->op = op;
 
@@ -723,15 +733,13 @@ static struct tevent_req *enum_groups_send(TALLOC_CTX *memctx,
      */
 
     subreq = sdap_get_groups_send(state, state->ev,
-                                 state->ctx->be->domain,
-                                 state->ctx->be->domain->sysdb,
-                                 state->ctx->opts,
-                                 state->ctx->opts->group_search_bases,
-                                 sdap_id_op_handle(state->op),
-                                 state->attrs, state->filter,
-                                 dp_opt_get_int(state->ctx->opts->basic,
-                                                SDAP_ENUM_SEARCH_TIMEOUT),
-                                 true);
+                                  state->sdom,
+                                  state->ctx->opts,
+                                  sdap_id_op_handle(state->op),
+                                  state->attrs, state->filter,
+                                  dp_opt_get_int(state->ctx->opts->basic,
+                                                 SDAP_ENUM_SEARCH_TIMEOUT),
+                                  true);
     if (!subreq) {
         ret = ENOMEM;
         goto fail;
diff --git a/src/providers/ldap/ldap_id_netgroup.c b/src/providers/ldap/ldap_id_netgroup.c
index 5f44c0a9b195500c566c7c20e2ae16c661102f85..759a9353bf93ed866d527969fb3006b3bff46216 100644
--- a/src/providers/ldap/ldap_id_netgroup.c
+++ b/src/providers/ldap/ldap_id_netgroup.c
@@ -33,6 +33,7 @@
 struct ldap_netgroup_get_state {
     struct tevent_context *ev;
     struct sdap_id_ctx *ctx;
+    struct sdap_domain *sdom;
     struct sdap_id_op *op;
     struct sdap_id_conn_ctx *conn;
     struct sysdb_ctx *sysdb;
@@ -57,6 +58,7 @@ static void ldap_netgroup_get_done(struct tevent_req *subreq);
 struct tevent_req *ldap_netgroup_get_send(TALLOC_CTX *memctx,
                                           struct tevent_context *ev,
                                           struct sdap_id_ctx *ctx,
+                                          struct sdap_domain *sdom,
                                           struct sdap_id_conn_ctx *conn,
                                           const char *name)
 {
@@ -70,6 +72,7 @@ struct tevent_req *ldap_netgroup_get_send(TALLOC_CTX *memctx,
 
     state->ev = ev;
     state->ctx = ctx;
+    state->sdom = sdom;
     state->conn = conn;
     state->dp_error = DP_ERR_FATAL;
 
@@ -80,8 +83,8 @@ struct tevent_req *ldap_netgroup_get_send(TALLOC_CTX *memctx,
         goto fail;
     }
 
-    state->sysdb = ctx->be->domain->sysdb;
-    state->domain = state->ctx->be->domain;
+    state->domain = sdom->dom;
+    state->sysdb = sdom->dom->sysdb;
     state->name = name;
     state->timeout = dp_opt_get_int(ctx->opts->basic, SDAP_SEARCH_TIMEOUT);
 
@@ -155,7 +158,7 @@ static void ldap_netgroup_get_connect_done(struct tevent_req *subreq)
     subreq = sdap_get_netgroups_send(state, state->ev,
                                      state->domain, state->sysdb,
                                      state->ctx->opts,
-                                     state->ctx->opts->netgroup_search_bases,
+                                     state->sdom->netgroup_search_bases,
                                      sdap_id_op_handle(state->op),
                                      state->attrs, state->filter,
                                      state->timeout);
diff --git a/src/providers/ldap/ldap_id_services.c b/src/providers/ldap/ldap_id_services.c
index 2a3f104c7383144383f2411f2d5dc664f45d3779..8b331cac42756e5a88bbd9222a98baa9ce1014b3 100644
--- a/src/providers/ldap/ldap_id_services.c
+++ b/src/providers/ldap/ldap_id_services.c
@@ -33,6 +33,7 @@
 struct sdap_services_get_state {
     struct tevent_context *ev;
     struct sdap_id_ctx *id_ctx;
+    struct sdap_domain *sdom;
     struct sdap_id_op *op;
     struct sysdb_ctx *sysdb;
     struct sss_domain_info *domain;
@@ -60,6 +61,7 @@ struct tevent_req *
 services_get_send(TALLOC_CTX *mem_ctx,
                   struct tevent_context *ev,
                   struct sdap_id_ctx *id_ctx,
+                  struct sdap_domain *sdom,
                   struct sdap_id_conn_ctx *conn,
                   const char *name,
                   const char *protocol,
@@ -77,10 +79,11 @@ services_get_send(TALLOC_CTX *mem_ctx,
 
     state->ev = ev;
     state->id_ctx = id_ctx;
+    state->sdom = sdom;
     state->conn = conn;
     state->dp_error = DP_ERR_FATAL;
-    state->sysdb = id_ctx->be->domain->sysdb;
-    state->domain = state->id_ctx->be->domain;
+    state->domain = sdom->dom;
+    state->sysdb = sdom->dom->sysdb;
     state->name = name;
     state->protocol = protocol;
     state->filter_type = filter_type;
@@ -192,7 +195,7 @@ services_get_connect_done(struct tevent_req *subreq)
     subreq = sdap_get_services_send(state, state->ev,
                                     state->domain, state->sysdb,
                                     state->id_ctx->opts,
-                                    state->id_ctx->opts->service_search_bases,
+                                    state->sdom->service_search_bases,
                                     sdap_id_op_handle(state->op),
                                     state->attrs, state->filter,
                                     dp_opt_get_int(state->id_ctx->opts->basic,
diff --git a/src/providers/ldap/ldap_init.c b/src/providers/ldap/ldap_init.c
index cc502d53798d1f8c3a385a72468d2ffb68b2a38e..d9930bed1844612d26ecbb7e3c0adb367450e5b1 100644
--- a/src/providers/ldap/ldap_init.c
+++ b/src/providers/ldap/ldap_init.c
@@ -105,7 +105,7 @@ int sssm_ldap_id_init(struct be_ctx *bectx,
         return EOK;
     }
 
-    ret = ldap_get_options(bectx, bectx->cdb,
+    ret = ldap_get_options(bectx, bectx->domain, bectx->cdb,
                            bectx->conf_path, &opts);
     if (ret != EOK) {
         goto done;
diff --git a/src/providers/ldap/sdap.c b/src/providers/ldap/sdap.c
index daa081ce79920f739b62b78d9a4cc9481b604bd3..0492be05d78b78967989ecd783d92ce94315c551 100644
--- a/src/providers/ldap/sdap.c
+++ b/src/providers/ldap/sdap.c
@@ -732,6 +732,7 @@ static char *get_naming_context(TALLOC_CTX *mem_ctx,
 }
 
 static errno_t sdap_set_search_base(struct sdap_options *opts,
+                                    struct sdap_domain *sdom,
                                     enum sdap_basic_opt class,
                                     char *naming_context)
 {
@@ -740,25 +741,25 @@ static errno_t sdap_set_search_base(struct sdap_options *opts,
 
     switch(class) {
     case SDAP_SEARCH_BASE:
-        bases = &opts->search_bases;
+        bases = &sdom->search_bases;
         break;
     case SDAP_USER_SEARCH_BASE:
-        bases = &opts->user_search_bases;
+        bases = &sdom->user_search_bases;
         break;
     case SDAP_GROUP_SEARCH_BASE:
-        bases = &opts->group_search_bases;
+        bases = &sdom->group_search_bases;
         break;
     case SDAP_NETGROUP_SEARCH_BASE:
-        bases = &opts->netgroup_search_bases;
+        bases = &sdom->netgroup_search_bases;
         break;
     case SDAP_SUDO_SEARCH_BASE:
-        bases = &opts->sudo_search_bases;
+        bases = &sdom->sudo_search_bases;
         break;
     case SDAP_SERVICE_SEARCH_BASE:
-        bases = &opts->service_search_bases;
+        bases = &sdom->service_search_bases;
         break;
     case SDAP_AUTOFS_SEARCH_BASE:
-        bases = &opts->autofs_search_bases;
+        bases = &sdom->autofs_search_bases;
         break;
     default:
         return EINVAL;
@@ -783,17 +784,18 @@ done:
 }
 
 errno_t sdap_set_config_options_with_rootdse(struct sysdb_attrs *rootdse,
-                                             struct sdap_options *opts)
+                                             struct sdap_options *opts,
+                                             struct sdap_domain *sdom)
 {
     int ret;
     char *naming_context = NULL;
 
-    if (!opts->search_bases
-            ||!opts->user_search_bases
-            || !opts->group_search_bases
-            || !opts->netgroup_search_bases
-            || !opts->sudo_search_bases
-            || !opts->autofs_search_bases) {
+    if (!sdom->search_bases
+            || !sdom->user_search_bases
+            || !sdom->group_search_bases
+            || !sdom->netgroup_search_bases
+            || !sdom->sudo_search_bases
+            || !sdom->autofs_search_bases) {
         naming_context = get_naming_context(opts->basic, rootdse);
         if (naming_context == NULL) {
             DEBUG(1, ("get_naming_context failed.\n"));
@@ -808,56 +810,56 @@ errno_t sdap_set_config_options_with_rootdse(struct sysdb_attrs *rootdse,
     }
 
     /* Default */
-    if (!opts->search_bases) {
-        ret = sdap_set_search_base(opts,
+    if (!sdom->search_bases) {
+        ret = sdap_set_search_base(opts, sdom,
                                    SDAP_SEARCH_BASE,
                                    naming_context);
         if (ret != EOK) goto done;
     }
 
     /* Users */
-    if (!opts->user_search_bases) {
-        ret = sdap_set_search_base(opts,
+    if (!sdom->user_search_bases) {
+        ret = sdap_set_search_base(opts, sdom,
                                    SDAP_USER_SEARCH_BASE,
                                    naming_context);
         if (ret != EOK) goto done;
     }
 
     /* Groups */
-    if (!opts->group_search_bases) {
-        ret = sdap_set_search_base(opts,
+    if (!sdom->group_search_bases) {
+        ret = sdap_set_search_base(opts, sdom,
                                    SDAP_GROUP_SEARCH_BASE,
                                    naming_context);
         if (ret != EOK) goto done;
     }
 
     /* Netgroups */
-    if (!opts->netgroup_search_bases) {
-        ret = sdap_set_search_base(opts,
+    if (!sdom->netgroup_search_bases) {
+        ret = sdap_set_search_base(opts, sdom,
                                    SDAP_NETGROUP_SEARCH_BASE,
                                    naming_context);
         if (ret != EOK) goto done;
     }
 
     /* Sudo */
-    if (!opts->sudo_search_bases) {
-       ret = sdap_set_search_base(opts,
+    if (!sdom->sudo_search_bases) {
+       ret = sdap_set_search_base(opts, sdom,
                                    SDAP_SUDO_SEARCH_BASE,
                                    naming_context);
         if (ret != EOK) goto done;
     }
 
     /* Services */
-    if (!opts->service_search_bases) {
-       ret = sdap_set_search_base(opts,
+    if (!sdom->service_search_bases) {
+       ret = sdap_set_search_base(opts, sdom,
                                   SDAP_SERVICE_SEARCH_BASE,
                                   naming_context);
         if (ret != EOK) goto done;
     }
 
     /* autofs */
-    if (!opts->autofs_search_bases) {
-       ret = sdap_set_search_base(opts,
+    if (!sdom->autofs_search_bases) {
+       ret = sdap_set_search_base(opts, sdom,
                                   SDAP_AUTOFS_SEARCH_BASE,
                                   naming_context);
         if (ret != EOK) goto done;
diff --git a/src/providers/ldap/sdap.h b/src/providers/ldap/sdap.h
index 162250fff76295807cce0779bebdf88577ed755c..f77636b3c642ea9affb5dbc47bd7290b2bde46a2 100644
--- a/src/providers/ldap/sdap.h
+++ b/src/providers/ldap/sdap.h
@@ -366,6 +366,22 @@ enum dc_functional_level {
     DS_BEHAVIOR_WIN2012 = 5
 };
 
+struct sdap_domain {
+    struct sss_domain_info *dom;
+
+    struct sdap_search_base **search_bases;
+    struct sdap_search_base **user_search_bases;
+    struct sdap_search_base **group_search_bases;
+    struct sdap_search_base **netgroup_search_bases;
+    struct sdap_search_base **sudo_search_bases;
+    struct sdap_search_base **service_search_bases;
+    struct sdap_search_base **autofs_search_bases;
+
+    struct sdap_domain *next, *prev;
+    /* Need to modify the list from a talloc destructor */
+    struct sdap_domain **head;
+};
+
 struct sdap_options {
     struct dp_option *basic;
     struct sdap_attr_map *gen_map;
@@ -390,13 +406,8 @@ struct sdap_options {
         SDAP_SCHEMA_AD = 4          /* AD's member/memberof */
     } schema_type;
 
-    struct sdap_search_base **search_bases;
-    struct sdap_search_base **user_search_bases;
-    struct sdap_search_base **group_search_bases;
-    struct sdap_search_base **netgroup_search_bases;
-    struct sdap_search_base **sudo_search_bases;
-    struct sdap_search_base **service_search_bases;
-    struct sdap_search_base **autofs_search_bases;
+    /* The search bases for the domain or its subdomain */
+    struct sdap_domain *sdom;
 
     bool support_matching_rule;
     enum dc_functional_level dc_functional_level;
@@ -474,7 +485,8 @@ int sdap_control_create(struct sdap_handle *sh, const char *oid, int iscritical,
 int sdap_replace_id(struct sysdb_attrs *entry, const char *attr, id_t val);
 
 errno_t sdap_set_config_options_with_rootdse(struct sysdb_attrs *rootdse,
-                                             struct sdap_options *opts);
+                                             struct sdap_options *opts,
+                                             struct sdap_domain *sdom);
 int sdap_get_server_opts_from_rootdse(TALLOC_CTX *memctx,
                                       const char *server,
                                       struct sysdb_attrs *rootdse,
diff --git a/src/providers/ldap/sdap_async.h b/src/providers/ldap/sdap_async.h
index 38c8302dc2afe81539e6b3dda0e840ad523f0178..38d94d8b49dad1a45e618fb1de853e103916f1cb 100644
--- a/src/providers/ldap/sdap_async.h
+++ b/src/providers/ldap/sdap_async.h
@@ -74,10 +74,8 @@ int sdap_get_users_recv(struct tevent_req *req,
 
 struct tevent_req *sdap_get_groups_send(TALLOC_CTX *memctx,
                                        struct tevent_context *ev,
-                                       struct sss_domain_info *dom,
-                                       struct sysdb_ctx *sysdb,
+                                       struct sdap_domain *sdom,
                                        struct sdap_options *opts,
-                                       struct sdap_search_base **search_bases,
                                        struct sdap_handle *sh,
                                        const char **attrs,
                                        const char *filter,
@@ -115,6 +113,7 @@ errno_t sdap_auth_recv(struct tevent_req *req,
 
 struct tevent_req *sdap_get_initgr_send(TALLOC_CTX *memctx,
                                         struct tevent_context *ev,
+                                        struct sdap_domain *sdom,
                                         struct sdap_handle *sh,
                                         struct sdap_id_ctx *id_ctx,
                                         struct sdap_id_conn_ctx *conn,
diff --git a/src/providers/ldap/sdap_async_autofs.c b/src/providers/ldap/sdap_async_autofs.c
index 8e874d64b5eb047fae3f36816e3ff4d342fb8575..ae70e703564acebaa1ca9adfb2395ca1c06fd802 100644
--- a/src/providers/ldap/sdap_async_autofs.c
+++ b/src/providers/ldap/sdap_async_autofs.c
@@ -716,7 +716,7 @@ sdap_autofs_setautomntent_send(TALLOC_CTX *memctx,
 
     subreq = sdap_get_automntmap_send(state, ev, dom,
                                       sysdb, state->opts,
-                                      state->opts->autofs_search_bases,
+                                      state->opts->sdom->autofs_search_bases,
                                       state->sh,
                                       state->attrs, state->filter,
                                       dp_opt_get_int(state->opts->basic,
diff --git a/src/providers/ldap/sdap_async_connection.c b/src/providers/ldap/sdap_async_connection.c
index 4f6986fcf84b098c5b861e461998a8358543a870..e97bcf15e1f43432520ca85fa18d26c630893c0d 100644
--- a/src/providers/ldap/sdap_async_connection.c
+++ b/src/providers/ldap/sdap_async_connection.c
@@ -1666,7 +1666,8 @@ static errno_t sdap_cli_use_rootdse(struct sdap_cli_connect_state *state)
             return ret;
         }
 
-        ret = sdap_set_config_options_with_rootdse(state->rootdse, state->opts);
+        ret = sdap_set_config_options_with_rootdse(state->rootdse, state->opts,
+                                                   state->opts->sdom);
         if (ret) {
             DEBUG(SSSDBG_OP_FAILURE,
                   ("sdap_set_config_options_with_rootdse failed.\n"));
diff --git a/src/providers/ldap/sdap_async_groups.c b/src/providers/ldap/sdap_async_groups.c
index dc7fba42678e50f367704e03b1a5d476a61f6c56..5a5bedc8f8bfc7ec0e998d093d3c7ddc3471e63f 100644
--- a/src/providers/ldap/sdap_async_groups.c
+++ b/src/providers/ldap/sdap_async_groups.c
@@ -1451,6 +1451,7 @@ struct sdap_get_groups_state {
     struct sdap_options *opts;
     struct sdap_handle *sh;
     struct sss_domain_info *dom;
+    struct sdap_domain *sdom;
     struct sysdb_ctx *sysdb;
     const char **attrs;
     const char *base_filter;
@@ -1476,10 +1477,8 @@ static void sdap_get_groups_done(struct tevent_req *subreq);
 
 struct tevent_req *sdap_get_groups_send(TALLOC_CTX *memctx,
                                        struct tevent_context *ev,
-                                       struct sss_domain_info *dom,
-                                       struct sysdb_ctx *sysdb,
+                                       struct sdap_domain *sdom,
                                        struct sdap_options *opts,
-                                       struct sdap_search_base **search_bases,
                                        struct sdap_handle *sh,
                                        const char **attrs,
                                        const char *filter,
@@ -1495,9 +1494,10 @@ struct tevent_req *sdap_get_groups_send(TALLOC_CTX *memctx,
 
     state->ev = ev;
     state->opts = opts;
-    state->dom = dom;
+    state->sdom = sdom;
+    state->dom = sdom->dom;
     state->sh = sh;
-    state->sysdb = sysdb;
+    state->sysdb = sdom->dom->sysdb;
     state->attrs = attrs;
     state->higher_usn = NULL;
     state->groups =  NULL;
@@ -1506,9 +1506,9 @@ struct tevent_req *sdap_get_groups_send(TALLOC_CTX *memctx,
     state->enumeration = enumeration;
     state->base_filter = filter;
     state->base_iter = 0;
-    state->search_bases = search_bases;
+    state->search_bases = sdom->group_search_bases;
 
-    if (!search_bases) {
+    if (!state->search_bases) {
         DEBUG(SSSDBG_CRIT_FAILURE,
               ("Group lookup request without a search base\n"));
         ret = EINVAL;
@@ -1653,7 +1653,7 @@ static void sdap_get_groups_process(struct tevent_req *subreq)
         if ((state->opts->schema_type != SDAP_SCHEMA_RFC2307)
                 && (dp_opt_get_int(state->opts->basic, SDAP_NESTING_LEVEL) != 0)
                 && !dp_opt_get_bool(state->opts->basic, SDAP_AD_MATCHING_RULE_GROUPS)) {
-            subreq = sdap_nested_group_send(state, state->ev, state->dom,
+            subreq = sdap_nested_group_send(state, state->ev, state->sdom,
                                             state->opts, state->sh,
                                             state->groups[0]);
             if (!subreq) {
diff --git a/src/providers/ldap/sdap_async_groups_ad.c b/src/providers/ldap/sdap_async_groups_ad.c
index 1082957f962561cb10b00dff505d095d299f8f37..1268f7e7de30849d1948265e50a327501982749d 100644
--- a/src/providers/ldap/sdap_async_groups_ad.c
+++ b/src/providers/ldap/sdap_async_groups_ad.c
@@ -69,7 +69,7 @@ sdap_get_ad_match_rule_members_send(TALLOC_CTX *mem_ctx,
     state->timeout = timeout;
     state->count = 0;
     state->base_iter = 0;
-    state->search_bases = opts->user_search_bases;
+    state->search_bases = opts->sdom->user_search_bases;
 
     /* Request all of the user attributes that we know about. */
     ret = build_attrs_from_map(state, opts->user_map, SDAP_OPTS_USER,
diff --git a/src/providers/ldap/sdap_async_initgroups.c b/src/providers/ldap/sdap_async_initgroups.c
index 57193f71d28e5d908b258782af4062c59499fb77..68647cfa78e9509867bf4cd78f9eca1a99293b24 100644
--- a/src/providers/ldap/sdap_async_initgroups.c
+++ b/src/providers/ldap/sdap_async_initgroups.c
@@ -352,7 +352,7 @@ struct tevent_req *sdap_initgr_rfc2307_send(TALLOC_CTX *memctx,
     state->ldap_groups = NULL;
     state->ldap_groups_count = 0;
     state->base_iter = 0;
-    state->search_bases = opts->group_search_bases;
+    state->search_bases = opts->sdom->group_search_bases;
 
     if (!state->search_bases) {
         DEBUG(SSSDBG_CRIT_FAILURE,
@@ -1486,7 +1486,7 @@ static struct tevent_req *sdap_initgr_rfc2307bis_send(
     state->num_direct_parents = 0;
     state->timeout = dp_opt_get_int(state->opts->basic, SDAP_SEARCH_TIMEOUT);
     state->base_iter = 0;
-    state->search_bases = opts->group_search_bases;
+    state->search_bases = opts->sdom->group_search_bases;
     state->orig_dn = orig_dn;
 
     if (!state->search_bases) {
@@ -2118,7 +2118,7 @@ struct tevent_req *rfc2307bis_nested_groups_send(
     state->timeout = dp_opt_get_int(state->opts->basic,
                                     SDAP_SEARCH_TIMEOUT);
     state->base_iter = 0;
-    state->search_bases = opts->group_search_bases;
+    state->search_bases = opts->sdom->group_search_bases;
     if (!state->search_bases) {
         DEBUG(SSSDBG_CRIT_FAILURE,
               ("Initgroups nested lookup request "
@@ -2530,6 +2530,7 @@ static void sdap_get_initgr_done(struct tevent_req *subreq);
 
 struct tevent_req *sdap_get_initgr_send(TALLOC_CTX *memctx,
                                         struct tevent_context *ev,
+                                        struct sdap_domain *sdom,
                                         struct sdap_handle *sh,
                                         struct sdap_id_ctx *id_ctx,
                                         struct sdap_id_conn_ctx *conn,
@@ -2548,8 +2549,8 @@ struct tevent_req *sdap_get_initgr_send(TALLOC_CTX *memctx,
 
     state->ev = ev;
     state->opts = id_ctx->opts;
-    state->sysdb = id_ctx->be->domain->sysdb;
-    state->dom = id_ctx->be->domain;
+    state->dom = sdom->dom;
+    state->sysdb = sdom->dom->sysdb;
     state->sh = sh;
     state->id_ctx = id_ctx;
     state->conn = conn;
@@ -2558,7 +2559,7 @@ struct tevent_req *sdap_get_initgr_send(TALLOC_CTX *memctx,
     state->orig_user = NULL;
     state->timeout = dp_opt_get_int(state->opts->basic, SDAP_SEARCH_TIMEOUT);
     state->user_base_iter = 0;
-    state->user_search_bases = id_ctx->opts->user_search_bases;
+    state->user_search_bases = sdom->user_search_bases;
     if (!state->user_search_bases) {
         DEBUG(SSSDBG_CRIT_FAILURE,
               ("Initgroups lookup request without a user search base\n"));
@@ -2950,8 +2951,9 @@ static void sdap_get_initgr_done(struct tevent_req *subreq)
         goto fail;
     }
 
-    subreq = groups_get_send(req, state->ev, state->id_ctx, state->conn, gid,
-                             BE_FILTER_IDNUM, BE_ATTR_ALL);
+    subreq = groups_get_send(req, state->ev, state->id_ctx,
+                             state->id_ctx->opts->sdom, state->conn,
+                             gid, BE_FILTER_IDNUM, BE_ATTR_ALL);
     if (!subreq) {
         ret = ENOMEM;
         goto fail;
diff --git a/src/providers/ldap/sdap_async_initgroups_ad.c b/src/providers/ldap/sdap_async_initgroups_ad.c
index c2dec37363e3138128b059d616ab82d7f337e01f..89789204a0273d0fd34565104cee38d89bf6436b 100644
--- a/src/providers/ldap/sdap_async_initgroups_ad.c
+++ b/src/providers/ldap/sdap_async_initgroups_ad.c
@@ -82,7 +82,7 @@ sdap_get_ad_match_rule_initgroups_send(TALLOC_CTX *mem_ctx,
     state->name = name;
     state->orig_dn = orig_dn;
     state->base_iter = 0;
-    state->search_bases = opts->group_search_bases;
+    state->search_bases = opts->sdom->group_search_bases;
 
     /* Request all of the group attributes that we know
      * about, except for 'member' because that wastes a
diff --git a/src/providers/ldap/sdap_async_nested_groups.c b/src/providers/ldap/sdap_async_nested_groups.c
index 9e8df99be2358ec1a5b85561c0deaea30135079f..e8d5295cc31319599212f96d7b58c8f5bd38245a 100644
--- a/src/providers/ldap/sdap_async_nested_groups.c
+++ b/src/providers/ldap/sdap_async_nested_groups.c
@@ -56,6 +56,8 @@ struct sdap_nested_group_member {
 struct sdap_nested_group_ctx {
     struct sss_domain_info *domain;
     struct sdap_options *opts;
+    struct sdap_search_base **user_search_bases;
+    struct sdap_search_base **group_search_bases;
     struct sdap_handle *sh;
     hash_table_t *users;
     hash_table_t *groups;
@@ -466,10 +468,12 @@ sdap_nested_group_split_members(TALLOC_CTX *mem_ctx,
         if (type == SDAP_NESTED_GROUP_DN_UNKNOWN) {
             /* user */
             is_user = sss_ldap_dn_in_search_bases(tmp_ctx, dn,
-                           group_ctx->opts->user_search_bases, &user_filter);
+                           group_ctx->user_search_bases,
+                           &user_filter);
 
             is_group = sss_ldap_dn_in_search_bases(tmp_ctx, dn,
-                           group_ctx->opts->group_search_bases, &group_filter);
+                           group_ctx->group_search_bases,
+                           &group_filter);
 
             if (is_user && is_group) {
                 /* search bases overlap */
@@ -551,12 +555,13 @@ struct sdap_nested_group_state {
 
 static void sdap_nested_group_done(struct tevent_req *subreq);
 
-struct tevent_req *sdap_nested_group_send(TALLOC_CTX *mem_ctx,
-                                          struct tevent_context *ev,
-                                          struct sss_domain_info *domain,
-                                          struct sdap_options *opts,
-                                          struct sdap_handle *sh,
-                                          struct sysdb_attrs *group)
+struct tevent_req *
+sdap_nested_group_send(TALLOC_CTX *mem_ctx,
+                       struct tevent_context *ev,
+                       struct sdap_domain *sdom,
+                       struct sdap_options *opts,
+                       struct sdap_handle *sh,
+                       struct sysdb_attrs *group)
 {
     struct sdap_nested_group_state *state = NULL;
     struct tevent_req *req = NULL;
@@ -596,8 +601,10 @@ struct tevent_req *sdap_nested_group_send(TALLOC_CTX *mem_ctx,
                                                       SDAP_DEREF_THRESHOLD);
     state->group_ctx->max_nesting_level = dp_opt_get_int(opts->basic,
                                                          SDAP_NESTING_LEVEL);
-    state->group_ctx->domain = domain;
+    state->group_ctx->domain = sdom->dom;
     state->group_ctx->opts = opts;
+    state->group_ctx->user_search_bases = sdom->user_search_bases;
+    state->group_ctx->group_search_bases = sdom->group_search_bases;
     state->group_ctx->sh = sh;
     state->group_ctx->try_deref = sdap_has_deref_support(sh, opts);
 
@@ -608,8 +615,8 @@ struct tevent_req *sdap_nested_group_send(TALLOC_CTX *mem_ctx,
 
     /* if any search base contains filter, disable dereference. */
     if (state->group_ctx->try_deref) {
-        for (i = 0; opts->user_search_bases[i] != NULL; i++) {
-            if (opts->user_search_bases[i]->filter != NULL) {
+        for (i = 0; opts->sdom->user_search_bases[i] != NULL; i++) {
+            if (opts->sdom->user_search_bases[i]->filter != NULL) {
                 DEBUG(SSSDBG_TRACE_FUNC, ("User search base contains filter, "
                                           "dereference will be disabled\n"));
                 state->group_ctx->try_deref = false;
@@ -619,8 +626,8 @@ struct tevent_req *sdap_nested_group_send(TALLOC_CTX *mem_ctx,
     }
 
     if (state->group_ctx->try_deref) {
-        for (i = 0; opts->group_search_bases[i] != NULL; i++) {
-            if (opts->group_search_bases[i]->filter != NULL) {
+        for (i = 0; opts->sdom->group_search_bases[i] != NULL; i++) {
+            if (opts->sdom->group_search_bases[i]->filter != NULL) {
                 DEBUG(SSSDBG_TRACE_FUNC, ("Group search base contains filter, "
                                           "dereference will be disabled\n"));
                 state->group_ctx->try_deref = false;
@@ -2092,7 +2099,7 @@ sdap_nested_group_deref_direct_process(struct tevent_req *subreq)
 
             /* skip the user if it is not amongst configured search bases */
             bret = sss_ldap_dn_in_search_bases(state, orig_dn,
-                                               opts->user_search_bases, NULL);
+                                               opts->sdom->user_search_bases, NULL);
             if (!bret) {
                 continue;
             }
@@ -2119,7 +2126,7 @@ sdap_nested_group_deref_direct_process(struct tevent_req *subreq)
 
             /* skip the group if it is not amongst configured search bases */
             bret = sss_ldap_dn_in_search_bases(state, orig_dn,
-                                               opts->group_search_bases, NULL);
+                                               opts->sdom->group_search_bases, NULL);
             if (!bret) {
                 continue;
             }
diff --git a/src/providers/ldap/sdap_async_netgroups.c b/src/providers/ldap/sdap_async_netgroups.c
index 2b382c1cfabf9e5f1156668b93ae1a6df981c410..57dbcde8103a59ae2443a55df4fa602e0a666f4f 100644
--- a/src/providers/ldap/sdap_async_netgroups.c
+++ b/src/providers/ldap/sdap_async_netgroups.c
@@ -420,7 +420,7 @@ static errno_t netgr_translate_members_ldap_step(struct tevent_req *req)
     }
 
     if (!sss_ldap_dn_in_search_bases(state, state->dn_item->dn,
-                                     state->opts->netgroup_search_bases,
+                                     state->opts->sdom->netgroup_search_bases,
                                      &filter)) {
         /* not in search base, skip it */
         state->dn_idx = state->dn_item->next;
diff --git a/src/providers/ldap/sdap_async_private.h b/src/providers/ldap/sdap_async_private.h
index 488387eb597f1cef849c6044ba012be545901a8b..944c8a82b217268206a8ede846df92ea4f4dba03 100644
--- a/src/providers/ldap/sdap_async_private.h
+++ b/src/providers/ldap/sdap_async_private.h
@@ -113,10 +113,9 @@ errno_t get_sysdb_grouplist(TALLOC_CTX *mem_ctx,
                             char ***grouplist);
 
 /* from sdap_async_nested_groups.c */
-
 struct tevent_req *sdap_nested_group_send(TALLOC_CTX *mem_ctx,
                                           struct tevent_context *ev,
-                                          struct sss_domain_info *domain,
+                                          struct sdap_domain *sdom,
                                           struct sdap_options *opts,
                                           struct sdap_handle *sh,
                                           struct sysdb_attrs *group);
diff --git a/src/providers/ldap/sdap_async_services.c b/src/providers/ldap/sdap_async_services.c
index 15a725b8b9db94a593e560899cb97b350f91c040..d637d22e59a68a0957ce01f92ddb99ee8d111f5f 100644
--- a/src/providers/ldap/sdap_async_services.c
+++ b/src/providers/ldap/sdap_async_services.c
@@ -582,7 +582,7 @@ enum_services_send(TALLOC_CTX *memctx,
     subreq = sdap_get_services_send(state, state->ev,
                                     state->domain, state->sysdb,
                                     state->id_ctx->opts,
-                                    state->id_ctx->opts->service_search_bases,
+                                    state->id_ctx->opts->sdom->service_search_bases,
                                     sdap_id_op_handle(state->op),
                                     state->attrs, state->filter,
                                     dp_opt_get_int(state->id_ctx->opts->basic,
diff --git a/src/providers/ldap/sdap_async_sudo.c b/src/providers/ldap/sdap_async_sudo.c
index 67b12b04857d132871cf3d4ffad4d1125277642c..ed4cf75ad5e38df3b955f5fe061a8b188fe89927 100644
--- a/src/providers/ldap/sdap_async_sudo.c
+++ b/src/providers/ldap/sdap_async_sudo.c
@@ -298,7 +298,7 @@ static struct tevent_req * sdap_sudo_load_sudoers_send(TALLOC_CTX *mem_ctx,
     state->opts = opts;
     state->sh = sh;
     state->base_iter = 0;
-    state->search_bases = opts->sudo_search_bases;
+    state->search_bases = opts->sdom->sudo_search_bases;
     state->filter = ldap_filter;
     state->timeout = dp_opt_get_int(opts->basic, SDAP_SEARCH_TIMEOUT);
     state->ldap_rules = NULL;
-- 
1.8.2.1

-------------- next part --------------
>From 6199ccd06ce9bfa7416838ff0ad6f302f7032873 Mon Sep 17 00:00:00 2001
From: Jakub Hrozek <jhrozek at redhat.com>
Date: Tue, 28 May 2013 18:43:13 +0200
Subject: [PATCH 06/15] LDAP: return sdap search return code to ID

By default, the LDAP searches delete the entry from cache if it wasn't
found during a search. But if a search wants to try both Global Catalog
and LDAP, for example, it might be beneficial to have an option to only
delete the entry from cache after the last operation fails to prevent
unnecessary memberof operations for example.
---
 src/providers/ldap/ldap_common.h           |  21 +++--
 src/providers/ldap/ldap_id.c               | 122 ++++++++++++++++++++---------
 src/providers/ldap/ldap_id_netgroup.c      |  15 +++-
 src/providers/ldap/ldap_id_services.c      |  15 +++-
 src/providers/ldap/sdap_async_initgroups.c |   4 +-
 5 files changed, 126 insertions(+), 51 deletions(-)

diff --git a/src/providers/ldap/ldap_common.h b/src/providers/ldap/ldap_common.h
index 4945853e6cc1e7657d39639e2a39ba2b90646e9e..ee48ed6891c48bc243e22f0bdf42707617ee88ad 100644
--- a/src/providers/ldap/ldap_common.h
+++ b/src/providers/ldap/ldap_common.h
@@ -104,10 +104,12 @@ sdap_handle_acct_req_send(TALLOC_CTX *mem_ctx,
                           struct be_acct_req *ar,
                           struct sdap_id_ctx *id_ctx,
                           struct sdap_domain *sdom,
-                          struct sdap_id_conn_ctx *conn);
+                          struct sdap_id_conn_ctx *conn,
+                          bool noexist_delete);
 errno_t
 sdap_handle_acct_req_recv(struct tevent_req *req,
-                          int *_dp_error, const char **_err);
+                          int *_dp_error, const char **_err,
+                          int *sdap_ret);
 
 /* auth */
 void sdap_pam_auth_handler(struct be_req *breq);
@@ -181,16 +183,18 @@ struct tevent_req *groups_get_send(TALLOC_CTX *memctx,
                                    struct sdap_id_conn_ctx *conn,
                                    const char *name,
                                    int filter_type,
-                                   int attrs_type);
-int groups_get_recv(struct tevent_req *req, int *dp_error_out);
+                                   int attrs_type,
+                                   bool noexist_delete);
+int groups_get_recv(struct tevent_req *req, int *dp_error_out, int *sdap_ret);
 
 struct tevent_req *ldap_netgroup_get_send(TALLOC_CTX *memctx,
                                           struct tevent_context *ev,
                                           struct sdap_id_ctx *ctx,
                                           struct sdap_domain *sdom,
                                           struct sdap_id_conn_ctx *conn,
-                                          const char *name);
-int ldap_netgroup_get_recv(struct tevent_req *req, int *dp_error_out);
+                                          const char *name,
+                                          bool noexist_delete);
+int ldap_netgroup_get_recv(struct tevent_req *req, int *dp_error_out, int *sdap_ret);
 
 struct tevent_req *
 services_get_send(TALLOC_CTX *mem_ctx,
@@ -200,10 +204,11 @@ services_get_send(TALLOC_CTX *mem_ctx,
                   struct sdap_id_conn_ctx *conn,
                   const char *name,
                   const char *protocol,
-                  int filter_type);
+                  int filter_type,
+                  bool noexist_delete);
 
 errno_t
-services_get_recv(struct tevent_req *req, int *dp_error_out);
+services_get_recv(struct tevent_req *req, int *dp_error_out, int *sdap_ret);
 
 /* setup child logging */
 int sdap_setup_child(void);
diff --git a/src/providers/ldap/ldap_id.c b/src/providers/ldap/ldap_id.c
index bfb34cc70b8f098245c2eada505e65b9589cb6a5..b29ab99301bee1f150f1554f0ce659507b627e90 100644
--- a/src/providers/ldap/ldap_id.c
+++ b/src/providers/ldap/ldap_id.c
@@ -52,6 +52,8 @@ struct users_get_state {
     const char **attrs;
 
     int dp_error;
+    int sdap_ret;
+    bool noexist_delete;
 };
 
 static int users_get_retry(struct tevent_req *req);
@@ -65,7 +67,8 @@ struct tevent_req *users_get_send(TALLOC_CTX *memctx,
                                   struct sdap_id_conn_ctx *conn,
                                   const char *name,
                                   int filter_type,
-                                  int attrs_type)
+                                  int attrs_type,
+                                  bool noexist_delete)
 {
     struct tevent_req *req;
     struct users_get_state *state;
@@ -85,6 +88,7 @@ struct tevent_req *users_get_send(TALLOC_CTX *memctx,
     state->sdom = sdom;
     state->conn = conn;
     state->dp_error = DP_ERR_FATAL;
+    state->noexist_delete = noexist_delete;
 
     state->op = sdap_id_op_create(state, state->conn->conn_cache);
     if (!state->op) {
@@ -302,6 +306,7 @@ static void users_get_done(struct tevent_req *subreq)
             }
         }
     }
+    state->sdap_ret = ret;
 
     if (ret && ret != ENOENT) {
         state->dp_error = dp_error;
@@ -309,7 +314,7 @@ static void users_get_done(struct tevent_req *subreq)
         return;
     }
 
-    if (ret == ENOENT) {
+    if (ret == ENOENT && state->noexist_delete == true) {
         switch (state->filter_type) {
         case BE_FILTER_ENUM:
             tevent_req_error(req, ret);
@@ -348,7 +353,7 @@ static void users_get_done(struct tevent_req *subreq)
     tevent_req_done(req);
 }
 
-int users_get_recv(struct tevent_req *req, int *dp_error_out)
+int users_get_recv(struct tevent_req *req, int *dp_error_out, int *sdap_ret)
 {
     struct users_get_state *state = tevent_req_data(req,
                                                     struct users_get_state);
@@ -357,6 +362,10 @@ int users_get_recv(struct tevent_req *req, int *dp_error_out)
         *dp_error_out = state->dp_error;
     }
 
+    if (sdap_ret) {
+        *sdap_ret = state->sdap_ret;
+    }
+
     TEVENT_REQ_RETURN_ON_ERROR(req);
 
     return EOK;
@@ -380,6 +389,8 @@ struct groups_get_state {
     const char **attrs;
 
     int dp_error;
+    int sdap_ret;
+    bool noexist_delete;
 };
 
 static int groups_get_retry(struct tevent_req *req);
@@ -393,7 +404,8 @@ struct tevent_req *groups_get_send(TALLOC_CTX *memctx,
                                    struct sdap_id_conn_ctx *conn,
                                    const char *name,
                                    int filter_type,
-                                   int attrs_type)
+                                   int attrs_type,
+                                   bool noexist_delete)
 {
     struct tevent_req *req;
     struct groups_get_state *state;
@@ -415,6 +427,7 @@ struct tevent_req *groups_get_send(TALLOC_CTX *memctx,
     state->sdom = sdom;
     state->conn = conn;
     state->dp_error = DP_ERR_FATAL;
+    state->noexist_delete = noexist_delete;
 
     state->op = sdap_id_op_create(state, state->conn->conn_cache);
     if (!state->op) {
@@ -617,6 +630,7 @@ static void groups_get_done(struct tevent_req *subreq)
 
         return;
     }
+    state->sdap_ret = ret;
 
     if (ret && ret != ENOENT) {
         state->dp_error = dp_error;
@@ -624,7 +638,7 @@ static void groups_get_done(struct tevent_req *subreq)
         return;
     }
 
-    if (ret == ENOENT) {
+    if (ret == ENOENT && state->noexist_delete == true) {
         switch (state->filter_type) {
         case BE_FILTER_ENUM:
             tevent_req_error(req, ret);
@@ -663,7 +677,7 @@ static void groups_get_done(struct tevent_req *subreq)
     tevent_req_done(req);
 }
 
-int groups_get_recv(struct tevent_req *req, int *dp_error_out)
+int groups_get_recv(struct tevent_req *req, int *dp_error_out, int *sdap_ret)
 {
     struct groups_get_state *state = tevent_req_data(req,
                                                      struct groups_get_state);
@@ -672,6 +686,10 @@ int groups_get_recv(struct tevent_req *req, int *dp_error_out)
         *dp_error_out = state->dp_error;
     }
 
+    if (sdap_ret) {
+        *sdap_ret = state->sdap_ret;
+    }
+
     TEVENT_REQ_RETURN_ON_ERROR(req);
 
     return EOK;
@@ -693,6 +711,8 @@ struct groups_by_user_state {
     const char **attrs;
 
     int dp_error;
+    int sdap_ret;
+    bool noexist_delete;
 };
 
 static int groups_by_user_retry(struct tevent_req *req);
@@ -704,7 +724,8 @@ static struct tevent_req *groups_by_user_send(TALLOC_CTX *memctx,
                                               struct sdap_id_ctx *ctx,
                                               struct sdap_domain *sdom,
                                               struct sdap_id_conn_ctx *conn,
-                                              const char *name)
+                                              const char *name,
+                                              bool noexist_delete)
 {
     struct tevent_req *req;
     struct groups_by_user_state *state;
@@ -718,6 +739,7 @@ static struct tevent_req *groups_by_user_send(TALLOC_CTX *memctx,
     state->dp_error = DP_ERR_FATAL;
     state->conn = conn;
     state->sdom = sdom;
+    state->noexist_delete = noexist_delete;
 
     state->op = sdap_id_op_create(state, state->conn->conn_cache);
     if (!state->op) {
@@ -819,6 +841,7 @@ static void groups_by_user_done(struct tevent_req *subreq)
 
         return;
     }
+    state->sdap_ret = ret;
 
     if (ret && ret != ENOENT) {
         state->dp_error = dp_error;
@@ -826,7 +849,7 @@ static void groups_by_user_done(struct tevent_req *subreq)
         return;
     }
 
-    if (ret == ENOENT) {
+    if (ret == ENOENT && state->noexist_delete == true) {
         ret = sysdb_delete_user(state->ctx->be->domain->sysdb,
                                 state->ctx->be->domain, state->name, 0);
         if (ret != EOK && ret != ENOENT) {
@@ -839,7 +862,7 @@ static void groups_by_user_done(struct tevent_req *subreq)
     tevent_req_done(req);
 }
 
-int groups_by_user_recv(struct tevent_req *req, int *dp_error_out)
+int groups_by_user_recv(struct tevent_req *req, int *dp_error_out, int *sdap_ret)
 {
     struct groups_by_user_state *state = tevent_req_data(req,
                                                              struct groups_by_user_state);
@@ -848,6 +871,10 @@ int groups_by_user_recv(struct tevent_req *req, int *dp_error_out)
         *dp_error_out = state->dp_error;
     }
 
+    if (sdap_ret) {
+        *sdap_ret = state->sdap_ret;
+    }
+
     TEVENT_REQ_RETURN_ON_ERROR(req);
 
     return EOK;
@@ -1006,7 +1033,8 @@ static struct tevent_req *get_user_and_group_send(TALLOC_CTX *memctx,
                                                   struct sdap_id_conn_ctx *conn,
                                                   const char *name,
                                                   int filter_type,
-                                                  int attrs_type);
+                                                  int attrs_type,
+                                                  bool noexist_delete);
 
 void sdap_account_info_handler(struct be_req *breq)
 {
@@ -1028,6 +1056,7 @@ struct sdap_handle_acct_req_state {
     struct be_acct_req *ar;
     const char *err;
     int dp_error;
+    int sdap_ret;
 };
 
 static void sdap_handle_acct_req_done(struct tevent_req *subreq);
@@ -1038,7 +1067,8 @@ sdap_handle_acct_req_send(TALLOC_CTX *mem_ctx,
                           struct be_acct_req *ar,
                           struct sdap_id_ctx *id_ctx,
                           struct sdap_domain *sdom,
-                          struct sdap_id_conn_ctx *conn)
+                          struct sdap_id_conn_ctx *conn,
+                          bool noexist_delete)
 {
     struct tevent_req *req;
     struct tevent_req *subreq;
@@ -1078,7 +1108,8 @@ sdap_handle_acct_req_send(TALLOC_CTX *mem_ctx,
                                 sdom, conn,
                                 ar->filter_value,
                                 ar->filter_type,
-                                ar->attr_type);
+                                ar->attr_type,
+                                noexist_delete);
         break;
 
     case BE_REQ_GROUP: /* group */
@@ -1096,7 +1127,8 @@ sdap_handle_acct_req_send(TALLOC_CTX *mem_ctx,
                                  sdom, conn,
                                  ar->filter_value,
                                  ar->filter_type,
-                                 ar->attr_type);
+                                 ar->attr_type,
+                                 noexist_delete);
         break;
 
     case BE_REQ_INITGROUPS: /* init groups for user */
@@ -1112,8 +1144,9 @@ sdap_handle_acct_req_send(TALLOC_CTX *mem_ctx,
         }
 
         subreq = groups_by_user_send(breq, be_ctx->ev, id_ctx,
-                                     id_ctx->opts->sdom, sdom, conn,
-                                     ar->filter_value);
+                                     sdom, conn,
+                                     ar->filter_value,
+                                     noexist_delete);
         break;
 
     case BE_REQ_NETGROUP:
@@ -1124,8 +1157,9 @@ sdap_handle_acct_req_send(TALLOC_CTX *mem_ctx,
         }
 
         subreq = ldap_netgroup_get_send(breq, be_ctx->ev, id_ctx,
-                                        id_ctx->opts->sdom, sdom, conn,
-                                        ar->filter_value);
+                                        sdom, conn,
+                                        ar->filter_value,
+                                        noexist_delete);
         break;
 
     case BE_REQ_SERVICES:
@@ -1148,7 +1182,8 @@ sdap_handle_acct_req_send(TALLOC_CTX *mem_ctx,
                                    sdom, conn,
                                    ar->filter_value,
                                    ar->extra_value,
-                                   ar->filter_type);
+                                   ar->filter_type,
+                                   noexist_delete);
         break;
 
     case BE_REQ_BY_SECID:
@@ -1162,7 +1197,8 @@ sdap_handle_acct_req_send(TALLOC_CTX *mem_ctx,
                                          sdom, conn,
                                          ar->filter_value,
                                          ar->filter_type,
-                                         ar->attr_type);
+                                         ar->attr_type,
+                                         noexist_delete);
         break;
 
     case BE_REQ_USER_AND_GROUP:
@@ -1177,7 +1213,8 @@ sdap_handle_acct_req_send(TALLOC_CTX *mem_ctx,
                                          sdom, conn,
                                          ar->filter_value,
                                          ar->filter_type,
-                                         ar->attr_type);
+                                         ar->attr_type,
+                                         noexist_delete);
         break;
 
     default: /*fail*/
@@ -1218,29 +1255,30 @@ sdap_handle_acct_req_done(struct tevent_req *subreq)
     switch (state->ar->entry_type & BE_REQ_TYPE_MASK) {
     case BE_REQ_USER: /* user */
         err = "User lookup failed";
-        ret = users_get_recv(subreq, &state->dp_error);
+        ret = users_get_recv(subreq, &state->dp_error, &state->sdap_ret);
         break;
     case BE_REQ_GROUP: /* group */
         err = "Group lookup failed";
-        ret = groups_get_recv(subreq, &state->dp_error);
+        ret = groups_get_recv(subreq, &state->dp_error, &state->sdap_ret);
         break;
     case BE_REQ_INITGROUPS: /* init groups for user */
         err = "Init group lookup failed";
-        ret = groups_by_user_recv(subreq, &state->dp_error);
+        ret = groups_by_user_recv(subreq, &state->dp_error, &state->sdap_ret);
         break;
     case BE_REQ_NETGROUP:
         err = "Netgroup lookup failed";
-        ret = ldap_netgroup_get_recv(subreq, &state->dp_error);
+        ret = ldap_netgroup_get_recv(subreq, &state->dp_error, &state->sdap_ret);
         break;
     case BE_REQ_SERVICES:
         err = "Service lookup failed";
-        ret = services_get_recv(subreq, &state->dp_error);
+        ret = services_get_recv(subreq, &state->dp_error, &state->sdap_ret);
         break;
     case BE_REQ_BY_SECID:
         /* Fallthrough */
     case BE_REQ_USER_AND_GROUP:
         err = "Lookup by SID failed";
-        ret = sdap_get_user_and_group_recv(subreq, &state->dp_error);
+        ret = sdap_get_user_and_group_recv(subreq, &state->dp_error,
+                                           &state->sdap_ret);
         break;
     default: /*fail*/
         ret = EINVAL;
@@ -1260,7 +1298,8 @@ sdap_handle_acct_req_done(struct tevent_req *subreq)
 
 errno_t
 sdap_handle_acct_req_recv(struct tevent_req *req,
-                          int *_dp_error, const char **_err)
+                          int *_dp_error, const char **_err,
+                          int *sdap_ret)
 {
     struct sdap_handle_acct_req_state *state;
 
@@ -1274,6 +1313,10 @@ sdap_handle_acct_req_recv(struct tevent_req *req,
         *_err = state->err;
     }
 
+    if (sdap_ret) {
+        *sdap_ret = state->sdap_ret;
+    }
+
     TEVENT_REQ_RETURN_ON_ERROR(req);
     return EOK;
 }
@@ -1297,7 +1340,7 @@ void sdap_handle_account_info(struct be_req *breq, struct sdap_id_ctx *ctx,
     }
 
     req = sdap_handle_acct_req_send(breq, breq, ar, ctx,
-                                    ctx->opts->sdom, conn);
+                                    ctx->opts->sdom, conn, NULL);
     if (req == NULL) {
         return sdap_handler_done(breq, DP_ERR_FATAL, ENOMEM, "Out of memory");
     }
@@ -1311,7 +1354,7 @@ static void sdap_account_info_complete(struct tevent_req *req)
     struct be_req *breq = tevent_req_callback_data(req, struct be_req);
     int ret, dp_error;
 
-    ret = sdap_handle_acct_req_recv(req, &dp_error, &req_error_text);
+    ret = sdap_handle_acct_req_recv(req, &dp_error, &req_error_text, NULL);
     talloc_zfree(req);
     if (dp_error == DP_ERR_OK) {
         if (ret == EOK) {
@@ -1350,6 +1393,8 @@ struct get_user_and_group_state {
     const char **attrs;
 
     int dp_error;
+    int sdap_ret;
+    bool noexist_delete;
 };
 
 static void get_user_and_group_users_done(struct tevent_req *subreq);
@@ -1362,7 +1407,8 @@ static struct tevent_req *get_user_and_group_send(TALLOC_CTX *memctx,
                                                   struct sdap_id_conn_ctx *conn,
                                                   const char *filter_val,
                                                   int filter_type,
-                                                  int attrs_type)
+                                                  int attrs_type,
+                                                  bool noexist_delete)
 {
     struct tevent_req *req;
     struct tevent_req *subreq;
@@ -1380,6 +1426,7 @@ static struct tevent_req *get_user_and_group_send(TALLOC_CTX *memctx,
     state->sdom = sdom;
     state->conn = conn;
     state->dp_error = DP_ERR_FATAL;
+    state->noexist_delete = noexist_delete;
 
     state->op = sdap_id_op_create(state, state->conn->conn_cache);
     if (!state->op) {
@@ -1397,7 +1444,7 @@ static struct tevent_req *get_user_and_group_send(TALLOC_CTX *memctx,
     subreq = users_get_send(req, state->ev, state->id_ctx,
                             state->sdom, state->conn,
                             state->filter_val, state->filter_type,
-                            state->attrs_type);
+                            state->attrs_type, NULL);
     if (subreq == NULL) {
         DEBUG(SSSDBG_OP_FAILURE, ("users_get_send failed.\n"));
         ret = ENOMEM;
@@ -1422,7 +1469,7 @@ static void get_user_and_group_users_done(struct tevent_req *subreq)
                                                struct get_user_and_group_state);
     int ret;
 
-    ret = users_get_recv(subreq, &state->dp_error);
+    ret = users_get_recv(subreq, &state->dp_error, NULL);
     talloc_zfree(subreq);
 
     if (ret == EOK) { /* Matching user found */
@@ -1436,7 +1483,7 @@ static void get_user_and_group_users_done(struct tevent_req *subreq)
     subreq = groups_get_send(req, state->ev, state->id_ctx,
                              state->sdom, state->conn,
                              state->filter_val, state->filter_type,
-                             state->attrs_type);
+                             state->attrs_type, state->noexist_delete);
     if (subreq == NULL) {
         DEBUG(SSSDBG_OP_FAILURE, ("groups_get_send failed.\n"));
         tevent_req_error(req, ENOMEM);
@@ -1454,7 +1501,7 @@ static void get_user_and_group_groups_done(struct tevent_req *subreq)
                                                struct get_user_and_group_state);
     int ret;
 
-    ret = groups_get_recv(subreq, &state->dp_error);
+    ret = groups_get_recv(subreq, &state->dp_error, &state->sdap_ret);
     talloc_zfree(subreq);
 
     if (ret == EOK) { /* Matching group found */
@@ -1466,7 +1513,8 @@ static void get_user_and_group_groups_done(struct tevent_req *subreq)
     return;
 }
 
-errno_t sdap_get_user_and_group_recv(struct tevent_req *req, int *dp_error_out)
+errno_t sdap_get_user_and_group_recv(struct tevent_req *req,
+                                     int *dp_error_out, int *sdap_ret)
 {
     struct get_user_and_group_state *state = tevent_req_data(req,
                                                struct get_user_and_group_state);
@@ -1475,6 +1523,10 @@ errno_t sdap_get_user_and_group_recv(struct tevent_req *req, int *dp_error_out)
         *dp_error_out = state->dp_error;
     }
 
+    if (sdap_ret) {
+        *sdap_ret = state->sdap_ret;
+    }
+
     TEVENT_REQ_RETURN_ON_ERROR(req);
 
     return EOK;
diff --git a/src/providers/ldap/ldap_id_netgroup.c b/src/providers/ldap/ldap_id_netgroup.c
index 759a9353bf93ed866d527969fb3006b3bff46216..e48a7da6c81be5a3fecc5f17a4797e0aacc3bacf 100644
--- a/src/providers/ldap/ldap_id_netgroup.c
+++ b/src/providers/ldap/ldap_id_netgroup.c
@@ -49,6 +49,8 @@ struct ldap_netgroup_get_state {
     struct sysdb_attrs **netgroups;
 
     int dp_error;
+    int sdap_ret;
+    bool noexist_delete;
 };
 
 static int ldap_netgroup_get_retry(struct tevent_req *req);
@@ -60,7 +62,8 @@ struct tevent_req *ldap_netgroup_get_send(TALLOC_CTX *memctx,
                                           struct sdap_id_ctx *ctx,
                                           struct sdap_domain *sdom,
                                           struct sdap_id_conn_ctx *conn,
-                                          const char *name)
+                                          const char *name,
+                                          bool noexist_delete)
 {
     struct tevent_req *req;
     struct ldap_netgroup_get_state *state;
@@ -75,6 +78,7 @@ struct tevent_req *ldap_netgroup_get_send(TALLOC_CTX *memctx,
     state->sdom = sdom;
     state->conn = conn;
     state->dp_error = DP_ERR_FATAL;
+    state->noexist_delete = noexist_delete;
 
     state->op = sdap_id_op_create(state, state->conn->conn_cache);
     if (!state->op) {
@@ -195,6 +199,7 @@ static void ldap_netgroup_get_done(struct tevent_req *subreq)
 
         return;
     }
+    state->sdap_ret = ret;
 
     if (ret && ret != ENOENT) {
         state->dp_error = dp_error;
@@ -209,7 +214,7 @@ static void ldap_netgroup_get_done(struct tevent_req *subreq)
         return;
     }
 
-    if (ret == ENOENT) {
+    if (ret == ENOENT && state->noexist_delete == true) {
         ret = sysdb_delete_netgroup(state->sysdb, state->domain, state->name);
         if (ret != EOK && ret != ENOENT) {
             tevent_req_error(req, ret);
@@ -222,7 +227,7 @@ static void ldap_netgroup_get_done(struct tevent_req *subreq)
     return;
 }
 
-int ldap_netgroup_get_recv(struct tevent_req *req, int *dp_error_out)
+int ldap_netgroup_get_recv(struct tevent_req *req, int *dp_error_out, int *sdap_ret)
 {
     struct ldap_netgroup_get_state *state = tevent_req_data(req,
                                                     struct ldap_netgroup_get_state);
@@ -231,6 +236,10 @@ int ldap_netgroup_get_recv(struct tevent_req *req, int *dp_error_out)
         *dp_error_out = state->dp_error;
     }
 
+    if (sdap_ret) {
+        *sdap_ret = state->sdap_ret;
+    }
+
     TEVENT_REQ_RETURN_ON_ERROR(req);
 
     return EOK;
diff --git a/src/providers/ldap/ldap_id_services.c b/src/providers/ldap/ldap_id_services.c
index 8b331cac42756e5a88bbd9222a98baa9ce1014b3..1f3fd8043cb2741a96756909ec88abc659934a22 100644
--- a/src/providers/ldap/ldap_id_services.c
+++ b/src/providers/ldap/ldap_id_services.c
@@ -48,6 +48,8 @@ struct sdap_services_get_state {
     int filter_type;
 
     int dp_error;
+    int sdap_ret;
+    bool noexist_delete;
 };
 
 static errno_t
@@ -65,7 +67,8 @@ services_get_send(TALLOC_CTX *mem_ctx,
                   struct sdap_id_conn_ctx *conn,
                   const char *name,
                   const char *protocol,
-                  int filter_type)
+                  int filter_type,
+                  bool noexist_delete)
 {
     errno_t ret;
     struct tevent_req *req;
@@ -87,6 +90,7 @@ services_get_send(TALLOC_CTX *mem_ctx,
     state->name = name;
     state->protocol = protocol;
     state->filter_type = filter_type;
+    state->noexist_delete = noexist_delete;
 
     state->op = sdap_id_op_create(state, state->conn->conn_cache);
     if (!state->op) {
@@ -237,6 +241,7 @@ services_get_done(struct tevent_req *subreq)
         /* Return to the mainloop to retry */
         return;
     }
+    state->sdap_ret = ret;
 
     /* An error occurred. */
     if (ret && ret != ENOENT) {
@@ -245,7 +250,7 @@ services_get_done(struct tevent_req *subreq)
         return;
     }
 
-    if (ret == ENOENT) {
+    if (ret == ENOENT && state->noexist_delete == true) {
         /* Ensure that this entry is removed from the sysdb */
         switch(state->filter_type) {
         case BE_FILTER_NAME:
@@ -283,7 +288,7 @@ services_get_done(struct tevent_req *subreq)
 }
 
 errno_t
-services_get_recv(struct tevent_req *req, int *dp_error_out)
+services_get_recv(struct tevent_req *req, int *dp_error_out, int *sdap_ret)
 {
     struct sdap_services_get_state *state =
             tevent_req_data(req, struct sdap_services_get_state);
@@ -292,6 +297,10 @@ services_get_recv(struct tevent_req *req, int *dp_error_out)
         *dp_error_out = state->dp_error;
     }
 
+    if (sdap_ret) {
+        *sdap_ret = state->sdap_ret;
+    }
+
     TEVENT_REQ_RETURN_ON_ERROR(req);
 
     return EOK;
diff --git a/src/providers/ldap/sdap_async_initgroups.c b/src/providers/ldap/sdap_async_initgroups.c
index 68647cfa78e9509867bf4cd78f9eca1a99293b24..7678c7b36d79e40b76305477854ab2d051bbfaa3 100644
--- a/src/providers/ldap/sdap_async_initgroups.c
+++ b/src/providers/ldap/sdap_async_initgroups.c
@@ -2953,7 +2953,7 @@ static void sdap_get_initgr_done(struct tevent_req *subreq)
 
     subreq = groups_get_send(req, state->ev, state->id_ctx,
                              state->id_ctx->opts->sdom, state->conn,
-                             gid, BE_FILTER_IDNUM, BE_ATTR_ALL);
+                             gid, BE_FILTER_IDNUM, BE_ATTR_ALL, NULL);
     if (!subreq) {
         ret = ENOMEM;
         goto fail;
@@ -2976,7 +2976,7 @@ static void sdap_get_initgr_pgid(struct tevent_req *subreq)
             tevent_req_callback_data(subreq, struct tevent_req);
     errno_t ret;
 
-    ret = groups_get_recv(subreq, NULL);
+    ret = groups_get_recv(subreq, NULL, NULL);
     talloc_zfree(subreq);
     if (ret != EOK) {
         tevent_req_error(req, ret);
-- 
1.8.2.1

-------------- next part --------------
>From 89081f432158ff2a16c65d78f1c9baf467d2a153 Mon Sep 17 00:00:00 2001
From: Jakub Hrozek <jhrozek at redhat.com>
Date: Tue, 28 May 2013 21:11:21 +0200
Subject: [PATCH 07/15] Move domain_to_basedn outside IPA subtree

The utility function will be reused to guess search base from the base
DN of AD trusted domains.
---
 Makefile.am                    |  3 +-
 src/providers/ipa/ipa_common.h |  2 --
 src/providers/ipa/ipa_utils.c  | 63 ------------------------------------------
 src/util/util.c                | 35 +++++++++++++++++++++++
 src/util/util.h                |  2 ++
 5 files changed, 38 insertions(+), 67 deletions(-)
 delete mode 100644 src/providers/ipa/ipa_utils.c

diff --git a/Makefile.am b/Makefile.am
index 2bcb71457af4a21ab4144a379148bc31820b7469..7ceeec06340f6c91f7a5988e36fcc1c0007caf38 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -1049,7 +1049,6 @@ auth_tests_LDADD = \
     libsss_test_common.la
 
 ipa_ldap_opt_tests_SOURCES = \
-    src/providers/ipa/ipa_utils.c \
     src/tests/ipa_ldap_opt-tests.c
 ipa_ldap_opt_tests_CFLAGS = \
     $(AM_CFLAGS) \
@@ -1057,6 +1056,7 @@ ipa_ldap_opt_tests_CFLAGS = \
 ipa_ldap_opt_tests_LDADD = \
     $(CHECK_LIBS) \
     $(TALLOC_LIBS) \
+    libsss_util.la \
     libsss_test_common.la
 
 ad_ldap_opt_tests_SOURCES = \
@@ -1554,7 +1554,6 @@ libsss_ipa_la_SOURCES = \
     src/providers/ipa/ipa_init.c \
     src/providers/ipa/ipa_common.c \
     src/providers/ipa/ipa_config.c \
-    src/providers/ipa/ipa_utils.c \
     src/providers/ipa/ipa_id.c \
     src/providers/ipa/ipa_netgroups.c \
     src/providers/ipa/ipa_auth.c \
diff --git a/src/providers/ipa/ipa_common.h b/src/providers/ipa/ipa_common.h
index a32867dd4533ba00ae58893c721b18a18537a98f..b660e66e2246cbbe5da69a745e46e67b3f20ec15 100644
--- a/src/providers/ipa/ipa_common.h
+++ b/src/providers/ipa/ipa_common.h
@@ -143,8 +143,6 @@ struct ipa_options {
     struct ipa_auth_ctx *auth_ctx;
 };
 
-int domain_to_basedn(TALLOC_CTX *memctx, const char *domain, char **basedn);
-
 /* options parsers */
 int ipa_get_options(TALLOC_CTX *memctx,
                     struct confdb_ctx *cdb,
diff --git a/src/providers/ipa/ipa_utils.c b/src/providers/ipa/ipa_utils.c
deleted file mode 100644
index a1e48f2d7830bde53584e83d3f75b174e0a43fd8..0000000000000000000000000000000000000000
--- a/src/providers/ipa/ipa_utils.c
+++ /dev/null
@@ -1,63 +0,0 @@
-/*
-    SSSD
-
-    IPA Provider Utility Functions
-
-    Authors:
-        Simo Sorce <ssorce at redhat.com>, Sumit Bose <sbose at redhat.com>
-
-    Copyright (C) 2009-2010 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 <ctype.h>
-
-#include "providers/ipa/ipa_common.h"
-
-int domain_to_basedn(TALLOC_CTX *memctx, const char *domain, char **basedn)
-{
-    const char *s;
-    char *dn;
-    char *p;
-    int l;
-
-    if (!domain || !basedn) {
-        return EINVAL;
-    }
-
-    s = domain;
-    dn = talloc_strdup(memctx, "dc=");
-
-    while ((p = strchr(s, '.'))) {
-        l = p - s;
-        dn = talloc_asprintf_append_buffer(dn, "%.*s,dc=", l, s);
-        if (!dn) {
-            return ENOMEM;
-        }
-        s = p + 1;
-    }
-    dn = talloc_strdup_append_buffer(dn, s);
-    if (!dn) {
-        return ENOMEM;
-    }
-
-    for (p=dn; *p; ++p) {
-        *p = tolower(*p);
-    }
-
-    *basedn = dn;
-    return EOK;
-}
diff --git a/src/util/util.c b/src/util/util.c
index ba85e0da2a2e03784c5bb9abbbd038ddaeb3455c..63cffe85e145953d3b2f4f85e649d2a0ac5b1b83 100644
--- a/src/util/util.c
+++ b/src/util/util.c
@@ -688,3 +688,38 @@ void safezero(void *data, size_t size)
         *p++ = 0;
     }
 }
+
+int domain_to_basedn(TALLOC_CTX *memctx, const char *domain, char **basedn)
+{
+    const char *s;
+    char *dn;
+    char *p;
+    int l;
+
+    if (!domain || !basedn) {
+        return EINVAL;
+    }
+
+    s = domain;
+    dn = talloc_strdup(memctx, "dc=");
+
+    while ((p = strchr(s, '.'))) {
+        l = p - s;
+        dn = talloc_asprintf_append_buffer(dn, "%.*s,dc=", l, s);
+        if (!dn) {
+            return ENOMEM;
+        }
+        s = p + 1;
+    }
+    dn = talloc_strdup_append_buffer(dn, s);
+    if (!dn) {
+        return ENOMEM;
+    }
+
+    for (p=dn; *p; ++p) {
+        *p = tolower(*p);
+    }
+
+    *basedn = dn;
+    return EOK;
+}
diff --git a/src/util/util.h b/src/util/util.h
index bdb04a8f3f6cf565743edded84fd798043cbe286..8a15afba7a93e6914cdb713b4f759e70e94d7825 100644
--- a/src/util/util.h
+++ b/src/util/util.h
@@ -491,6 +491,8 @@ bool string_in_list(const char *string, char **list, bool case_sensitive);
  */
 void safezero(void *data, size_t size);
 
+int domain_to_basedn(TALLOC_CTX *memctx, const char *domain, char **basedn);
+
 /* from nscd.c */
 enum nscd_db {
     NSCD_DB_PASSWD,
-- 
1.8.2.1

-------------- next part --------------
>From 203bc6ef8a92faea1a0f877be5a4484283cc6792 Mon Sep 17 00:00:00 2001
From: Jakub Hrozek <jhrozek at redhat.com>
Date: Tue, 28 May 2013 22:57:55 +0200
Subject: [PATCH 08/15] LDAP: split a function to create search bases

This function will be used later to fill the sdap_domain structures with
search bases.
---
 src/providers/ldap/ldap_common.c | 96 ++++++++++++++++++++++++++++------------
 src/providers/ldap/ldap_common.h |  6 +++
 2 files changed, 73 insertions(+), 29 deletions(-)

diff --git a/src/providers/ldap/ldap_common.c b/src/providers/ldap/ldap_common.c
index 7a319b1e5ceef1a58d503da1b5f437b9c62bd321..8ea41d6df28f76019568ece5edcd45a40c5e4783 100644
--- a/src/providers/ldap/ldap_common.c
+++ b/src/providers/ldap/ldap_common.c
@@ -607,6 +607,69 @@ errno_t sdap_parse_search_base(TALLOC_CTX *mem_ctx,
                                     _search_bases);
 }
 
+errno_t
+sdap_create_search_base(TALLOC_CTX *mem_ctx,
+                        const char *unparsed_base,
+                        int scope,
+                        const char *filter,
+                        struct sdap_search_base **_base)
+{
+    struct sdap_search_base *base;
+    TALLOC_CTX *tmp_ctx;
+    errno_t ret;
+    struct ldb_dn *ldn;
+    struct ldb_context *ldb;
+
+    tmp_ctx = talloc_new(NULL);
+    if (!tmp_ctx) {
+        ret = ENOMEM;
+        goto done;
+    }
+
+    /* Create a throwaway LDB context for validating the DN */
+    ldb = ldb_init(tmp_ctx, NULL);
+    if (!ldb) {
+        ret = ENOMEM;
+        goto done;
+    }
+
+    base = talloc_zero(tmp_ctx, struct sdap_search_base);
+    if (base == NULL) {
+        ret = ENOMEM;
+        goto done;
+    }
+
+    base->basedn = talloc_strdup(base, unparsed_base);
+    if (base->basedn == NULL) {
+        ret = ENOMEM;
+        goto done;
+    }
+
+    /* Validate the basedn */
+    ldn = ldb_dn_new(tmp_ctx, ldb, unparsed_base);
+    if (!ldn) {
+        ret = ENOMEM;
+        goto done;
+    }
+
+    if (!ldb_dn_validate(ldn)) {
+        DEBUG(SSSDBG_CRIT_FAILURE,
+                ("Invalid base DN [%s]\n",
+                 unparsed_base));
+        ret = EINVAL;
+        goto done;
+    }
+
+    base->scope = scope;
+    base->filter = filter;
+
+    *_base = talloc_steal(mem_ctx, base);
+    ret = EOK;
+done:
+    talloc_free(tmp_ctx);
+    return ret;
+}
+
 errno_t common_parse_search_base(TALLOC_CTX *mem_ctx,
                                  const char *unparsed_base,
                                  const char *class_name,
@@ -659,40 +722,15 @@ errno_t common_parse_search_base(TALLOC_CTX *mem_ctx,
             ret = ENOMEM;
             goto done;
         }
-        search_bases[0] = talloc_zero(search_bases, struct sdap_search_base);
+
+        ret = sdap_create_search_base(search_bases, unparsed_base,
+                                      LDAP_SCOPE_SUBTREE, old_filter,
+                                      &search_bases[0]);
         if (!search_bases[0]) {
             ret = ENOMEM;
             goto done;
         }
 
-        search_bases[0]->basedn = talloc_strdup(search_bases[0],
-                                                unparsed_base);
-        if (!search_bases[0]->basedn) {
-            ret = ENOMEM;
-            goto done;
-        }
-
-        /* Validate the basedn */
-        ldn = ldb_dn_new(tmp_ctx, ldb, unparsed_base);
-        if (!ldn) {
-            ret = ENOMEM;
-            goto done;
-        }
-
-        if (!ldb_dn_validate(ldn)) {
-            DEBUG(SSSDBG_CRIT_FAILURE,
-                  ("Invalid base DN [%s]\n",
-                   unparsed_base));
-            ret = EINVAL;
-            goto done;
-        }
-        talloc_zfree(ldn);
-
-        search_bases[0]->scope = LDAP_SCOPE_SUBTREE;
-
-        /* Use a search filter specified in the old style if available */
-        search_bases[0]->filter = old_filter;
-
         DEBUG(SSSDBG_CONF_SETTINGS,
               ("Search base added: [%s][%s][%s][%s]\n",
                class_name,
diff --git a/src/providers/ldap/ldap_common.h b/src/providers/ldap/ldap_common.h
index ee48ed6891c48bc243e22f0bdf42707617ee88ad..87f0d8ff1527d2273d39ad26eacce2ba501106a2 100644
--- a/src/providers/ldap/ldap_common.h
+++ b/src/providers/ldap/ldap_common.h
@@ -246,6 +246,12 @@ void sdap_domain_remove(struct sss_domain_info *subdom);
 
 struct sdap_domain *sdap_domain_get(struct sdap_options *opts,
                                     struct sss_domain_info *dom);
+errno_t
+sdap_create_search_base(TALLOC_CTX *mem_ctx,
+                        const char *unparsed_base,
+                        int scope,
+                        const char *filter,
+                        struct sdap_search_base **_base);
 
 errno_t sdap_parse_search_base(TALLOC_CTX *mem_ctx,
                                struct dp_option *opts, int class,
-- 
1.8.2.1

-------------- next part --------------
>From ef718a175135923ff16de73e33e163ddf117c0ac Mon Sep 17 00:00:00 2001
From: Jakub Hrozek <jhrozek at redhat.com>
Date: Wed, 29 May 2013 00:56:53 +0200
Subject: [PATCH 09/15] LDAP: store FQDNs for trusted users and groups

Because the NSS responder expects the name attribute to contain FQDN,
we must save the name as FQDN in the LDAP provider if the domain we save
to is a subdomain.
---
 src/providers/ldap/sdap_async.c           | 13 ++++++++++---
 src/providers/ldap/sdap_async.h           |  9 +++++----
 src/providers/ldap/sdap_async_groups.c    | 12 ++++++++++--
 src/providers/ldap/sdap_async_netgroups.c |  2 +-
 src/providers/ldap/sdap_async_users.c     | 12 ++++++++++--
 5 files changed, 36 insertions(+), 12 deletions(-)

diff --git a/src/providers/ldap/sdap_async.c b/src/providers/ldap/sdap_async.c
index b2bf0360ee1d1eb57ca096d5970eec1db7d94989..6a85cb16672f90f59bf2e613b9c5eae63a6f4da2 100644
--- a/src/providers/ldap/sdap_async.c
+++ b/src/providers/ldap/sdap_async.c
@@ -2267,13 +2267,15 @@ sdap_attrs_add_ldap_attr(struct sysdb_attrs *ldap_attrs,
 errno_t
 sdap_save_all_names(const char *name,
                     struct sysdb_attrs *ldap_attrs,
-                    bool lowercase,
+                    struct sss_domain_info *dom,
                     struct sysdb_attrs *attrs)
 {
     const char **aliases = NULL;
+    const char *domname;
     errno_t ret;
     TALLOC_CTX *tmp_ctx;
     int i;
+    bool lowercase = !dom->case_sensitive;
 
     tmp_ctx = talloc_new(NULL);
     if (!tmp_ctx) {
@@ -2289,8 +2291,13 @@ sdap_save_all_names(const char *name,
     }
 
     for (i = 0; aliases[i]; i++) {
-        ret = sysdb_attrs_add_string(attrs, SYSDB_NAME_ALIAS,
-                                     aliases[i]);
+        domname = sss_get_domain_name(tmp_ctx, aliases[i], dom);
+        if (domname == NULL) {
+            ret = ENOMEM;
+            goto done;
+        }
+
+        ret = sysdb_attrs_add_string(attrs, SYSDB_NAME_ALIAS, domname);
         if (ret) {
             DEBUG(SSSDBG_OP_FAILURE, ("Failed to add alias [%s] into the "
                                       "attribute list\n", aliases[i]));
diff --git a/src/providers/ldap/sdap_async.h b/src/providers/ldap/sdap_async.h
index 38d94d8b49dad1a45e618fb1de853e103916f1cb..c8031c9a9d527a6d808f1ddce096de23850ebfd6 100644
--- a/src/providers/ldap/sdap_async.h
+++ b/src/providers/ldap/sdap_async.h
@@ -210,10 +210,11 @@ sdap_attrs_add_ldap_attr(struct sysdb_attrs *ldap_attrs,
     sdap_attrs_add_ldap_attr(ldap_attrs, attr_name, attr_desc,   \
                              true, name, attrs)
 
-errno_t sdap_save_all_names(const char *name,
-                            struct sysdb_attrs *ldap_attrs,
-                            bool lowercase,
-                            struct sysdb_attrs *attrs);
+errno_t
+sdap_save_all_names(const char *name,
+                    struct sysdb_attrs *ldap_attrs,
+                    struct sss_domain_info *dom,
+                    struct sysdb_attrs *attrs);
 
 struct tevent_req *
 sdap_get_services_send(TALLOC_CTX *memctx,
diff --git a/src/providers/ldap/sdap_async_groups.c b/src/providers/ldap/sdap_async_groups.c
index 5a5bedc8f8bfc7ec0e998d093d3c7ddc3471e63f..04ce2f9fb3a7f105b5351045cd2da3b966cab7d6 100644
--- a/src/providers/ldap/sdap_async_groups.c
+++ b/src/providers/ldap/sdap_async_groups.c
@@ -429,6 +429,7 @@ static int sdap_save_group(TALLOC_CTX *memctx,
     struct ldb_message_element *el;
     struct sysdb_attrs *group_attrs;
     const char *name = NULL;
+    char *group_name;
     gid_t gid;
     errno_t ret;
     char *usn_value = NULL;
@@ -614,7 +615,7 @@ static int sdap_save_group(TALLOC_CTX *memctx,
         goto done;
     }
 
-    ret = sdap_save_all_names(name, attrs, !dom->case_sensitive, group_attrs);
+    ret = sdap_save_all_names(name, attrs, dom, group_attrs);
     if (ret != EOK) {
         DEBUG(1, ("Failed to save group names\n"));
         goto done;
@@ -622,8 +623,15 @@ static int sdap_save_group(TALLOC_CTX *memctx,
 
     DEBUG(6, ("Storing info for group %s\n", name));
 
+    group_name = sss_get_domain_name(tmpctx, name, dom);
+    if (!group_name) {
+        DEBUG(SSSDBG_OP_FAILURE, ("failed to format user name,\n"));
+        ret = ENOMEM;
+        goto done;
+    }
+
     ret = sdap_store_group_with_gid(ctx, dom,
-                                    name, gid, group_attrs,
+                                    group_name, gid, group_attrs,
                                     dom->group_timeout,
                                     posix_group, now);
     if (ret) {
diff --git a/src/providers/ldap/sdap_async_netgroups.c b/src/providers/ldap/sdap_async_netgroups.c
index 57dbcde8103a59ae2443a55df4fa602e0a666f4f..265fcf0ea8d3ac3a743433ce234a5faf8a673c61 100644
--- a/src/providers/ldap/sdap_async_netgroups.c
+++ b/src/providers/ldap/sdap_async_netgroups.c
@@ -121,7 +121,7 @@ static errno_t sdap_save_netgroup(TALLOC_CTX *memctx,
 
     DEBUG(6, ("Storing info for netgroup %s\n", name));
 
-    ret = sdap_save_all_names(name, attrs, !dom->case_sensitive,
+    ret = sdap_save_all_names(name, attrs, dom,
                               netgroup_attrs);
     if (ret != EOK) {
         DEBUG(1, ("Failed to save netgroup names\n"));
diff --git a/src/providers/ldap/sdap_async_users.c b/src/providers/ldap/sdap_async_users.c
index 68e646cb936c5e85a4c548d6a9324491b0fcd58f..2c9fa293965eebd17fd8f52aeab0eec71a51cbd1 100644
--- a/src/providers/ldap/sdap_async_users.c
+++ b/src/providers/ldap/sdap_async_users.c
@@ -44,6 +44,7 @@ int sdap_save_user(TALLOC_CTX *memctx,
     struct ldb_message_element *el;
     int ret;
     const char *name = NULL;
+    const char *user_name = NULL;
     const char *fullname = NULL;
     const char *pwd;
     const char *gecos;
@@ -381,7 +382,7 @@ int sdap_save_user(TALLOC_CTX *memctx,
         }
     }
 
-    ret = sdap_save_all_names(name, attrs, !dom->case_sensitive, user_attrs);
+    ret = sdap_save_all_names(name, attrs, dom, user_attrs);
     if (ret != EOK) {
         DEBUG(1, ("Failed to save user names\n"));
         goto done;
@@ -398,7 +399,14 @@ int sdap_save_user(TALLOC_CTX *memctx,
 
     DEBUG(6, ("Storing info for user %s\n", name));
 
-    ret = sysdb_store_user(ctx, dom, name, pwd, uid, gid,
+    user_name = sss_get_domain_name(tmpctx, name, dom);
+    if (!user_name) {
+        DEBUG(SSSDBG_OP_FAILURE, ("failed to format user name,\n"));
+        ret = ENOMEM;
+        goto done;
+    }
+
+    ret = sysdb_store_user(ctx, dom, user_name, pwd, uid, gid,
                            gecos, homedir, shell, orig_dn,
                            user_attrs, missing, cache_timeout, now);
     if (ret) goto done;
-- 
1.8.2.1

-------------- next part --------------
>From e29bcb09a5f393228502fd86a3cca65f8dbd1854 Mon Sep 17 00:00:00 2001
From: Jakub Hrozek <jhrozek at redhat.com>
Date: Wed, 29 May 2013 15:51:38 +0200
Subject: [PATCH 10/15] Split generating primary GID for ID mapped users into a
 separate function

Move the part of sdap_save_user into a separate function so that it can
be special cased an only called for users in primary domains, not
subdomain users.
---
 src/providers/ldap/sdap_async_users.c | 111 +++++++++++++++++++++-------------
 1 file changed, 70 insertions(+), 41 deletions(-)

diff --git a/src/providers/ldap/sdap_async_users.c b/src/providers/ldap/sdap_async_users.c
index 2c9fa293965eebd17fd8f52aeab0eec71a51cbd1..e4d224cc8b3db9a58f07a24ea53ae5e457fbac89 100644
--- a/src/providers/ldap/sdap_async_users.c
+++ b/src/providers/ldap/sdap_async_users.c
@@ -31,6 +31,68 @@
 
 /* ==Save-User-Entry====================================================== */
 
+static errno_t
+sdap_get_idmap_primary_gid(struct sdap_options *opts,
+                           struct sysdb_attrs *attrs,
+                           char *sid_str,
+                           char *dom_sid_str,
+                           gid_t *_gid)
+{
+    errno_t ret;
+    TALLOC_CTX *tmpctx = NULL;
+    gid_t gid, primary_gid;
+    char *group_sid_str;
+
+    tmpctx = talloc_new(NULL);
+    if (!tmpctx) {
+        ret = ENOMEM;
+        goto done;
+    }
+
+    ret = sysdb_attrs_get_uint32_t(attrs,
+                opts->user_map[SDAP_AT_USER_PRIMARY_GROUP].sys_name,
+                &primary_gid);
+    if (ret != EOK) {
+        DEBUG(SSSDBG_MINOR_FAILURE, ("no primary group ID provided\n"));
+        ret = EINVAL;
+        goto done;
+    }
+
+    /* The primary group ID is just the RID part of the objectSID
+     * of the group. Generate the GID by adding this to the domain
+     * SID value.
+     */
+
+    /* First, get the domain SID if we didn't do so above */
+    if (!dom_sid_str) {
+        ret = sdap_idmap_get_dom_sid_from_object(tmpctx, sid_str,
+                                                 &dom_sid_str);
+        if (ret != EOK) {
+            DEBUG(SSSDBG_MINOR_FAILURE,
+                  ("Could not parse domain SID from [%s]\n", sid_str));
+            goto done;
+        }
+    }
+
+    /* Add the RID to the end */
+    group_sid_str = talloc_asprintf(tmpctx, "%s-%lu", dom_sid_str,
+                                   (unsigned long) primary_gid);
+    if (!group_sid_str) {
+        ret = ENOMEM;
+        goto done;
+    }
+
+    /* Convert the SID into a UNIX group ID */
+    ret = sdap_idmap_sid_to_unix(opts->idmap_ctx, group_sid_str, &gid);
+    if (ret != EOK) goto done;
+
+    ret = EOK;
+    *_gid = gid;
+done:
+    talloc_free(tmpctx);
+    return ret;
+}
+
 /* FIXME: support storing additional attributes */
 int sdap_save_user(TALLOC_CTX *memctx,
                    struct sysdb_ctx *ctx,
@@ -52,7 +114,7 @@ int sdap_save_user(TALLOC_CTX *memctx,
     const char *shell;
     const char *orig_dn = NULL;
     uid_t uid;
-    gid_t gid, primary_gid;
+    gid_t gid;
     struct sysdb_attrs *user_attrs;
     char *upn = NULL;
     size_t i;
@@ -63,7 +125,6 @@ int sdap_save_user(TALLOC_CTX *memctx,
     bool use_id_mapping = dp_opt_get_bool(opts->basic, SDAP_ID_MAPPING);
     char *sid_str;
     char *dom_sid_str = NULL;
-    char *group_sid_str;
 
     DEBUG(9, ("Save user\n"));
 
@@ -206,50 +267,18 @@ int sdap_save_user(TALLOC_CTX *memctx,
     }
 
     if (use_id_mapping) {
-        ret = sysdb_attrs_get_uint32_t(
-                attrs,
-                opts->user_map[SDAP_AT_USER_PRIMARY_GROUP].sys_name,
-                &primary_gid);
-        if (ret != EOK) {
-            DEBUG(SSSDBG_MINOR_FAILURE,
-                  ("no primary group ID provided for [%s] in domain [%s].\n",
+        ret = sdap_get_idmap_primary_gid(opts, attrs, sid_str, dom_sid_str,
+                                         &gid);
+        if (ret) {
+            DEBUG(SSSDBG_CRIT_FAILURE,
+                  ("Cannot get the GID for [%s] in domain [%s].\n",
                    name, dom->name));
-            ret = EINVAL;
             goto done;
         }
 
-        /* The primary group ID is just the RID part of the objectSID
-         * of the group. Generate the GID by adding this to the domain
-         * SID value.
-         */
-
-        /* First, get the domain SID if we didn't do so above */
-        if (!dom_sid_str) {
-            ret = sdap_idmap_get_dom_sid_from_object(tmpctx, sid_str,
-                                                     &dom_sid_str);
-            if (ret != EOK) {
-                DEBUG(SSSDBG_MINOR_FAILURE,
-                      ("Could not parse domain SID from [%s]\n", sid_str));
-                goto done;
-            }
-        }
-
-        /* Add the RID to the end */
-        group_sid_str = talloc_asprintf(tmpctx, "%s-%lu",
-                                        dom_sid_str,
-                                        (unsigned long)primary_gid);
-        if (!group_sid_str) {
-            ret = ENOMEM;
-            goto done;
-        }
-
-        /* Convert the SID into a UNIX group ID */
-        ret = sdap_idmap_sid_to_unix(opts->idmap_ctx, group_sid_str, &gid);
-        if (ret != EOK) goto done;
-
         /* Store the GID in the ldap_attrs so it doesn't get
-         * treated as a missing attribute from LDAP and removed.
-         */
+        * treated as a missing attribute from LDAP and removed.
+        */
         ret = sysdb_attrs_add_uint32(attrs, SYSDB_GIDNUM, gid);
         if (ret != EOK) goto done;
     } else {
-- 
1.8.2.1

-------------- next part --------------
>From e0fba806bc2e8fd916029894cf0d8ea2e735865d Mon Sep 17 00:00:00 2001
From: Jakub Hrozek <jhrozek at redhat.com>
Date: Wed, 29 May 2013 16:01:23 +0200
Subject: [PATCH 11/15] LDAP: Do not store separate GID for subdomain users

As the subdomains are MPG domains, we don't want to store a separate GID
for the subdomain users, but rather just create a UPG.
---
 src/providers/ldap/sdap_async_users.c | 28 ++++++++++++++++++----------
 1 file changed, 18 insertions(+), 10 deletions(-)

diff --git a/src/providers/ldap/sdap_async_users.c b/src/providers/ldap/sdap_async_users.c
index e4d224cc8b3db9a58f07a24ea53ae5e457fbac89..a368965f68d89508c0faee9ba699c86f5792db02 100644
--- a/src/providers/ldap/sdap_async_users.c
+++ b/src/providers/ldap/sdap_async_users.c
@@ -267,13 +267,20 @@ int sdap_save_user(TALLOC_CTX *memctx,
     }
 
     if (use_id_mapping) {
-        ret = sdap_get_idmap_primary_gid(opts, attrs, sid_str, dom_sid_str,
-                                         &gid);
-        if (ret) {
-            DEBUG(SSSDBG_CRIT_FAILURE,
-                  ("Cannot get the GID for [%s] in domain [%s].\n",
-                   name, dom->name));
-            goto done;
+        if (IS_SUBDOMAIN(dom) == false) {
+            ret = sdap_get_idmap_primary_gid(opts, attrs, sid_str, dom_sid_str,
+                                             &gid);
+            if (ret) {
+                DEBUG(SSSDBG_CRIT_FAILURE,
+                    ("Cannot get the GID for [%s] in domain [%s].\n",
+                    name, dom->name));
+                goto done;
+            }
+        } else {
+            /* For subdomain users, only create the private group as
+             * the subdomain is an MPG domain
+             */
+            gid = 0;
         }
 
         /* Store the GID in the ldap_attrs so it doesn't get
@@ -294,9 +301,10 @@ int sdap_save_user(TALLOC_CTX *memctx,
     }
 
     /* check that the gid is valid for this domain */
-    if (OUT_OF_ID_RANGE(gid, dom->id_min, dom->id_max)) {
-            DEBUG(2, ("User [%s] filtered out! (primary gid out of range)\n",
-                      name));
+    if (IS_SUBDOMAIN(dom) == false &&
+            OUT_OF_ID_RANGE(gid, dom->id_min, dom->id_max)) {
+        DEBUG(SSSDBG_CRIT_FAILURE,
+              ("User [%s] filtered out! (primary gid out of range)\n", name));
         ret = EINVAL;
         goto done;
     }
-- 
1.8.2.1

-------------- next part --------------
>From 289a56d27b9beff2e5272287a8abfed08ef45ede Mon Sep 17 00:00:00 2001
From: Jakub Hrozek <jhrozek at redhat.com>
Date: Wed, 29 May 2013 00:56:18 +0200
Subject: [PATCH 12/15] New utility function sss_get_domain_name

Instead of copying a block of code that checks whether domain is a subdomain
and uses only name of FQDN as appropriate, wrap the logic into a function.
---
 src/db/sysdb_search.c | 20 ++++----------------
 src/tools/sss_cache.c | 13 ++++---------
 src/util/usertools.c  | 18 ++++++++++++++++++
 src/util/util.h       |  5 +++++
 4 files changed, 31 insertions(+), 25 deletions(-)

diff --git a/src/db/sysdb_search.c b/src/db/sysdb_search.c
index 843c746d2a724aa63d885ff40d025633ba401833..d70d0cc3eee6fe5fa55bee6a36d4e8382d69c3ab 100644
--- a/src/db/sysdb_search.c
+++ b/src/db/sysdb_search.c
@@ -55,15 +55,9 @@ int sysdb_getpwnam(TALLOC_CTX *mem_ctx,
 
     /* If this is a subomain we need to use fully qualified names for the
      * search as well by default */
-    if (IS_SUBDOMAIN(domain) && domain->fqnames) {
-        ret = ENOMEM;
-        src_name = talloc_asprintf(tmp_ctx, domain->names->fq_fmt,
-                                   name, domain->name);
-    } else {
-        ret = EINVAL;
-        src_name = name;
-    }
+    src_name = sss_get_domain_name(tmp_ctx, name, domain);
     if (!src_name) {
+        ret = ENOMEM;
         goto done;
     }
 
@@ -243,15 +237,9 @@ int sysdb_getgrnam(TALLOC_CTX *mem_ctx,
 
     /* If this is a subomain we need to use fully qualified names for the
      * search as well by default */
-    if (IS_SUBDOMAIN(domain) && domain->fqnames) {
-        ret = ENOMEM;
-        src_name = talloc_asprintf(tmp_ctx, domain->names->fq_fmt,
-                                   name, domain->name);
-    } else {
-        ret = EINVAL;
-        src_name = name;
-    }
+    src_name = sss_get_domain_name(tmp_ctx, name, domain);
     if (!src_name) {
+        ret = ENOMEM;
         goto done;
     }
 
diff --git a/src/tools/sss_cache.c b/src/tools/sss_cache.c
index 9942e6a7f4a252f55bf2a01ec7c80b202db72f51..c9096fa91e562dc5c07bf14a4afacc692348f3b5 100644
--- a/src/tools/sss_cache.c
+++ b/src/tools/sss_cache.c
@@ -219,15 +219,10 @@ static errno_t update_filter(struct cache_tool_ctx *tctx,
     }
 
     if (parsed_domain) {
-        if (IS_SUBDOMAIN(dinfo)) {
-            /* Use fqdn for subdomains */
-            use_name = talloc_asprintf(tmp_ctx, tctx->nctx->fq_fmt, use_name,
-                                       dinfo->name);
-            if (use_name == NULL) {
-                DEBUG(SSSDBG_CRIT_FAILURE, ("Out of memory\n"));
-                ret = ENOMEM;
-                goto done;
-            }
+        use_name = sss_get_domain_name(tmp_ctx, use_name, dinfo);
+        if (!use_name) {
+            ret = ENOMEM;
+            goto done;
         }
 
         if (!strcasecmp(dinfo->name, parsed_domain)) {
diff --git a/src/util/usertools.c b/src/util/usertools.c
index 91110f263657de9ba53ed305e7c4710eb006bec6..4dc1947a7597f3903e304414990cc618939c4ca7 100644
--- a/src/util/usertools.c
+++ b/src/util/usertools.c
@@ -507,3 +507,21 @@ sss_get_cased_name_list(TALLOC_CTX *mem_ctx, const char * const *orig,
     *_cased = out;
     return EOK;
 }
+
+char *
+sss_get_domain_name(TALLOC_CTX *mem_ctx,
+                    const char *orig_name,
+                    struct sss_domain_info *dom)
+{
+    char *user_name;
+
+    if (IS_SUBDOMAIN(dom) && dom->fqnames) {
+        /* we always use the fully qualified name for subdomain users */
+        user_name = talloc_asprintf(mem_ctx, dom->names->fq_fmt,
+                                    orig_name, dom->name);
+    } else {
+        user_name = talloc_strdup(mem_ctx, orig_name);
+    }
+
+    return user_name;
+}
diff --git a/src/util/util.h b/src/util/util.h
index 8a15afba7a93e6914cdb713b4f759e70e94d7825..0314c5048bb96dc42bdcc8f9da6a641b8c7e0a11 100644
--- a/src/util/util.h
+++ b/src/util/util.h
@@ -43,6 +43,7 @@
 #include <ldb.h>
 #include <dhash.h>
 
+#include "confdb/confdb.h"
 #include "util/atomic_io.h"
 #include "util/util_errors.h"
 #include "util/util_safealign.h"
@@ -365,6 +366,10 @@ errno_t
 sss_get_cased_name_list(TALLOC_CTX *mem_ctx, const char * const *orig,
                         bool case_sensitive, const char ***_cased);
 
+char *
+sss_get_domain_name(TALLOC_CTX *mem_ctx, const char *orig_name,
+                    struct sss_domain_info *dom);
+
 /* from backup-file.c */
 int backup_file(const char *src, int dbglvl);
 
-- 
1.8.2.1

-------------- next part --------------
>From 85d46691d35f795b51477cadda944ed2b3cb35ef Mon Sep 17 00:00:00 2001
From: Jakub Hrozek <jhrozek at redhat.com>
Date: Wed, 15 May 2013 17:36:44 +0200
Subject: [PATCH 13/15] AD: Add additional service to support Global Catalog
 lookups

When fixed host names of AD servers are configured in the config file,
we can't know (unlike when service discovery is at play) if the servers
are Global Catalogs or not. This patch adds a private data to servers
read from the config file that denote whether the server can be tried
for contacting the Global Catalog port or just LDAP. The GC or LDAP URIs
are generated based on contents of this private data structure.

Because SSSD sticks to a working server, we don't have to disable or
remove the faulty GC servers from the list.
---
 src/providers/ad/ad_common.c | 163 ++++++++++++++++++++++++++++++++++++-------
 src/providers/ad/ad_common.h |   5 ++
 src/providers/ad/ad_init.c   |  16 +++--
 3 files changed, 153 insertions(+), 31 deletions(-)

diff --git a/src/providers/ad/ad_common.c b/src/providers/ad/ad_common.c
index b32237b7b74508d2936729ae5e049dc8201b37a2..608188c4eacbdbe9417d6395e2506d9425c931b0 100644
--- a/src/providers/ad/ad_common.c
+++ b/src/providers/ad/ad_common.c
@@ -25,6 +25,11 @@
 #include "providers/ad/ad_opts.h"
 #include "providers/dp_dyndns.h"
 
+struct ad_server_data {
+    bool gc;
+    const char *server;
+};
+
 errno_t
 ad_get_common_options(TALLOC_CTX *mem_ctx,
                       struct confdb_ctx *cdb,
@@ -140,13 +145,14 @@ done:
 static void
 ad_resolve_callback(void *private_data, struct fo_server *server);
 
-#define ad_primary_servers_init(mem_ctx, bectx, servers, options) \
-    _ad_servers_init(mem_ctx, bectx, servers, options, true)
-#define ad_backup_servers_init(mem_ctx, bectx, servers, options) \
-    _ad_servers_init(mem_ctx, bectx, servers, options, false)
+#define ad_primary_servers_init(mem_ctx, service, bectx, servers, options) \
+    _ad_servers_init(mem_ctx, service, bectx, servers, options, true)
+#define ad_backup_servers_init(mem_ctx, service, bectx, servers, options) \
+    _ad_servers_init(mem_ctx, service, bectx, servers, options, false)
 
 static errno_t
 _ad_servers_init(TALLOC_CTX *mem_ctx,
+                 struct ad_service *service,
                  struct be_ctx *bectx,
                  const char *servers,
                  struct ad_options *options,
@@ -156,6 +162,7 @@ _ad_servers_init(TALLOC_CTX *mem_ctx,
     errno_t ret = 0;
     char **list;
     char *ad_domain;
+    struct ad_server_data *sdata;
     TALLOC_CTX *tmp_ctx;
 
     tmp_ctx = talloc_new(NULL);
@@ -202,7 +209,37 @@ _ad_servers_init(TALLOC_CTX *mem_ctx,
             goto done;
         }
 
-        ret = be_fo_add_server(bectx, AD_SERVICE_NAME, list[i], 0, NULL, primary);
+        sdata = talloc(service, struct ad_server_data);
+        if (sdata == NULL) {
+            ret = ENOMEM;
+            goto done;
+        }
+        sdata->gc = true;
+        sdata->server = talloc_strdup(sdata, list[i]);
+        if (sdata->server == NULL) {
+            ret = ENOMEM;
+            goto done;
+        }
+
+        ret = be_fo_add_server(bectx, AD_SERVICE_NAME, list[i], 0, sdata, primary);
+        if (ret && ret != EEXIST) {
+            DEBUG(SSSDBG_FATAL_FAILURE, ("Failed to add server\n"));
+            goto done;
+        }
+
+        sdata = talloc(service, struct ad_server_data);
+        if (sdata == NULL) {
+            ret = ENOMEM;
+            goto done;
+        }
+        sdata->gc = false;
+        sdata->server = talloc_strdup(sdata, list[i]);
+        if (sdata->server == NULL) {
+            ret = ENOMEM;
+            goto done;
+        }
+
+        ret = be_fo_add_server(bectx, AD_SERVICE_NAME, list[i], 0, sdata, primary);
         if (ret && ret != EEXIST) {
             DEBUG(SSSDBG_FATAL_FAILURE, ("Failed to add server\n"));
             goto done;
@@ -217,7 +254,37 @@ done:
 
 static int ad_user_data_cmp(void *ud1, void *ud2)
 {
-    return strcasecmp((char*) ud1, (char*) ud2);
+    struct ad_server_data *sd1, *sd2;
+
+    sd1 = talloc_get_type(ud1, struct ad_server_data);
+    sd2 = talloc_get_type(ud2, struct ad_server_data);
+    if (sd1 == NULL || sd2 == NULL) {
+        DEBUG(SSSDBG_TRACE_FUNC, ("No user data\n"));
+        return sd1 == sd2 ? 0 : 1;
+    }
+
+    DEBUG(SSSDBG_TRACE_LIBS, ("Comparing %s (%s) with %s (%s)\n",
+          sd1->server, sd1->gc ? "GC" : "LDAP",
+          sd2->server, sd2->gc ? "GC" : "LDAP"));
+
+    if (strcasecmp(sd1->server, sd2->server) == 0 &&
+        sd1->gc == sd2->gc) {
+        return 0;
+    }
+
+    return 1;
+}
+
+static void ad_online_cb(void *pvt)
+{
+    struct ad_service *service = talloc_get_type(pvt, struct ad_service);
+
+    if (service == NULL) {
+        DEBUG(SSSDBG_CRIT_FAILURE, ("Invalid private pointer\n"));
+        return;
+    }
+
+    DEBUG(SSSDBG_TRACE_FUNC, ("The AD provider is online\n"));
 }
 
 errno_t
@@ -242,7 +309,15 @@ ad_failover_init(TALLOC_CTX *mem_ctx, struct be_ctx *bectx,
     }
 
     service->sdap = talloc_zero(service, struct sdap_service);
-    if (!service->sdap) {
+    service->gc = talloc_zero(service, struct sdap_service);
+    if (!service->sdap || !service->gc) {
+        ret = ENOMEM;
+        goto done;
+    }
+
+    service->sdap->name = talloc_strdup(service->sdap, AD_SERVICE_NAME);
+    service->gc->name = talloc_strdup(service->gc, AD_SERVICE_NAME);
+    if (!service->sdap->name || !service->gc->name) {
         ret = ENOMEM;
         goto done;
     }
@@ -259,18 +334,14 @@ ad_failover_init(TALLOC_CTX *mem_ctx, struct be_ctx *bectx,
         goto done;
     }
 
-    service->sdap->name = talloc_strdup(service, AD_SERVICE_NAME);
-    if (!service->sdap->name) {
-        ret = ENOMEM;
-        goto done;
-    }
-
-    service->krb5_service->name = talloc_strdup(service, AD_SERVICE_NAME);
+    service->krb5_service->name = talloc_strdup(service->krb5_service,
+                                                AD_SERVICE_NAME);
     if (!service->krb5_service->name) {
         ret = ENOMEM;
         goto done;
     }
     service->sdap->kinit_service_name = service->krb5_service->name;
+    service->gc->kinit_service_name = service->krb5_service->name;
 
     realm = dp_opt_get_string(options->basic, AD_KRB5_REALM);
     if (!realm) {
@@ -291,18 +362,26 @@ ad_failover_init(TALLOC_CTX *mem_ctx, struct be_ctx *bectx,
         primary_servers = BE_SRV_IDENTIFIER;
     }
 
-    ret = ad_primary_servers_init(mem_ctx, bectx, primary_servers, options);
+    ret = ad_primary_servers_init(mem_ctx, service, bectx,
+                                  primary_servers, options);
     if (ret != EOK) {
         goto done;
     }
 
     if (backup_servers) {
-        ret = ad_backup_servers_init(mem_ctx, bectx, backup_servers, options);
+        ret = ad_backup_servers_init(mem_ctx, service, bectx,
+                                     backup_servers, options);
         if (ret != EOK) {
             goto done;
         }
     }
 
+    ret = be_add_online_cb(bectx, bectx, ad_online_cb, service, NULL);
+    if (ret != EOK) {
+        DEBUG(SSSDBG_CRIT_FAILURE, ("Could not set up AD online callback\n"));
+        return ret;
+    }
+
     ret = be_fo_service_add_callback(mem_ctx, bectx, AD_SERVICE_NAME,
                                      ad_resolve_callback, service);
     if (ret != EOK) {
@@ -330,8 +409,9 @@ ad_resolve_callback(void *private_data, struct fo_server *server)
     struct sockaddr_storage *sockaddr;
     char *address;
     const char *safe_address;
-    char *new_uri;
+    char *new_uri, *new_gc_uri;
     const char *srv_name;
+    struct ad_server_data *sdata = NULL;
 
     tmp_ctx = talloc_new(NULL);
     if (!tmp_ctx) {
@@ -339,6 +419,12 @@ ad_resolve_callback(void *private_data, struct fo_server *server)
         return;
     }
 
+    sdata = fo_get_server_user_data(server);
+    if (fo_is_srv_lookup(server) == false && sdata == NULL) {
+        DEBUG(SSSDBG_CRIT_FAILURE, ("No user data?\n"));
+        return;
+    }
+
     service = talloc_get_type(private_data, struct ad_service);
     if (!service) {
         ret = EINVAL;
@@ -354,13 +440,6 @@ ad_resolve_callback(void *private_data, struct fo_server *server)
         goto done;
     }
 
-    sockaddr = resolv_get_sockaddr_address(tmp_ctx, srvaddr, LDAP_PORT);
-    if (sockaddr == NULL) {
-        DEBUG(SSSDBG_CRIT_FAILURE, ("resolv_get_sockaddr_address failed.\n"));
-        ret = EIO;
-        goto done;
-    }
-
     address = resolv_get_string_address(tmp_ctx, srvaddr);
     if (address == NULL) {
         DEBUG(SSSDBG_CRIT_FAILURE, ("resolv_get_string_address failed.\n"));
@@ -375,7 +454,7 @@ ad_resolve_callback(void *private_data, struct fo_server *server)
         goto done;
     }
 
-    new_uri = talloc_asprintf(service, "ldap://%s", srv_name);
+    new_uri = talloc_asprintf(service->sdap, "ldap://%s", srv_name);
     if (!new_uri) {
         DEBUG(SSSDBG_CRIT_FAILURE, ("Failed to copy URI\n"));
         ret = ENOMEM;
@@ -383,12 +462,44 @@ ad_resolve_callback(void *private_data, struct fo_server *server)
     }
     DEBUG(SSSDBG_CONF_SETTINGS, ("Constructed uri '%s'\n", new_uri));
 
+    talloc_zfree(service->gc->uri);
+    talloc_zfree(service->gc->sockaddr);
+    if (sdata && sdata->gc) {
+        new_gc_uri = talloc_asprintf(service->gc, "%s:%d",
+                                     new_uri, AD_GC_PORT);
+        if (!new_gc_uri) {
+            DEBUG(SSSDBG_CRIT_FAILURE, ("Failed to append to URI\n"));
+            ret = ENOMEM;
+            goto done;
+        }
+        DEBUG(SSSDBG_CONF_SETTINGS, ("Constructed GC uri '%s'\n", new_gc_uri));
+
+        sockaddr = resolv_get_sockaddr_address(tmp_ctx, srvaddr, AD_GC_PORT);
+        if (sockaddr == NULL) {
+            DEBUG(SSSDBG_CRIT_FAILURE,
+                  ("resolv_get_sockaddr_address failed.\n"));
+            ret = EIO;
+            goto done;
+        }
+
+        service->gc->uri = new_gc_uri;
+        service->gc->sockaddr = talloc_steal(service->gc, sockaddr);
+    }
+
+    sockaddr = resolv_get_sockaddr_address(tmp_ctx, srvaddr, LDAP_PORT);
+    if (sockaddr == NULL) {
+        DEBUG(SSSDBG_CRIT_FAILURE, ("resolv_get_sockaddr_address failed.\n"));
+        ret = EIO;
+        goto done;
+    }
+
     /* 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, sockaddr);
+    service->sdap->sockaddr = talloc_steal(service->sdap, sockaddr);
 
+    /* Write krb5 info files */
     safe_address = sss_escape_ip_address(tmp_ctx,
                                          srvaddr->family,
                                          address);
diff --git a/src/providers/ad/ad_common.h b/src/providers/ad/ad_common.h
index 792f32e0876ad73b0fb422c4cc7ebc86058e7ed6..801815528c30ef05956eb51dce7cc6f8b161ffa8 100644
--- a/src/providers/ad/ad_common.h
+++ b/src/providers/ad/ad_common.h
@@ -27,6 +27,8 @@
 #include "providers/ldap/ldap_common.h"
 
 #define AD_SERVICE_NAME "AD"
+/* The port the Global Catalog runs on */
+#define AD_GC_PORT      3268
 
 struct ad_options;
 
@@ -44,11 +46,14 @@ enum ad_basic_opt {
 
 struct ad_id_ctx {
     struct sdap_id_ctx *sdap_id_ctx;
+    struct sdap_id_conn_ctx *ldap_ctx;
+    struct sdap_id_conn_ctx *gc_ctx;
     struct ad_options *ad_options;
 };
 
 struct ad_service {
     struct sdap_service *sdap;
+    struct sdap_service *gc;
     struct krb5_service *krb5_service;
 };
 
diff --git a/src/providers/ad/ad_init.c b/src/providers/ad/ad_init.c
index aada14ec7dadb2db5cc67ecc0e1d9c631c974f07..cb73aca3afb2cd80b165670e1fa84d375a80ace1 100644
--- a/src/providers/ad/ad_init.c
+++ b/src/providers/ad/ad_init.c
@@ -140,6 +140,12 @@ sssm_ad_id_init(struct be_ctx *bectx,
         return ENOMEM;
     }
     ad_ctx->sdap_id_ctx = sdap_ctx;
+    ad_ctx->ldap_ctx = sdap_ctx->conn;
+
+    ad_ctx->gc_ctx = sdap_id_ctx_conn_add(sdap_ctx, ad_options->service->gc);
+    if (sdap_ctx == NULL) {
+        return ENOMEM;
+    }
 
     ret = ad_dyndns_init(sdap_ctx->be, ad_options);
     if (ret != EOK) {
@@ -148,11 +154,6 @@ sssm_ad_id_init(struct be_ctx *bectx,
         /* Continue without DNS updates */
     }
 
-    ret = sdap_id_setup_tasks(sdap_ctx);
-    if (ret != EOK) {
-        goto done;
-    }
-
     ret = sdap_setup_child();
     if (ret != EOK) {
         DEBUG(SSSDBG_FATAL_FAILURE,
@@ -169,6 +170,11 @@ sssm_ad_id_init(struct be_ctx *bectx,
         goto done;
     }
 
+    ret = sdap_id_setup_tasks(sdap_ctx);
+    if (ret != EOK) {
+        goto done;
+    }
+
     /* Set up the ID mapping object */
     ret = sdap_idmap_init(sdap_ctx, sdap_ctx, &sdap_ctx->opts->idmap_ctx);
     if (ret != EOK) goto done;
-- 
1.8.2.1

-------------- next part --------------
>From 37a4d83070b23fefce9154480f095c55b7f4cfd0 Mon Sep 17 00:00:00 2001
From: Jakub Hrozek <jhrozek at redhat.com>
Date: Thu, 23 May 2013 13:03:51 +0200
Subject: [PATCH 14/15] AD ID lookups - choose GC or LDAP as appropriate

https://fedorahosted.org/sssd/ticket/1557

Some lookups should be performed from GC only -- for example trusted
users are only present in the Global Catalog, while some lookups should
be performed from LDAP only as not all objects or attributes are
replicated to Global Catalog.

This patch adds a generic failover mechanism for identity lookups in the
AD provider that allows to choose the appropriate source and even fail over
to the other source if available.
---
 src/providers/ad/ad_id.c | 259 ++++++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 258 insertions(+), 1 deletion(-)

diff --git a/src/providers/ad/ad_id.c b/src/providers/ad/ad_id.c
index 5d8c78371a2e91fbec554a2a90d424d758ffd1a2..986f37759f9e153a13d48805a3d53d4bf454b71a 100644
--- a/src/providers/ad/ad_id.c
+++ b/src/providers/ad/ad_id.c
@@ -23,18 +23,275 @@
 #include "providers/ad/ad_common.h"
 #include "providers/ad/ad_id.h"
 
+struct ad_handle_acct_info_state {
+    struct be_req *breq;
+    struct be_acct_req *ar;
+    struct sdap_id_ctx *ctx;
+    struct sdap_id_conn_ctx **conn;
+    struct sdap_domain *sdom;
+    size_t cindex;
+
+    int dp_error;
+    const char *err;
+};
+
+static errno_t ad_handle_acct_info_step(struct tevent_req *req);
+static void ad_handle_acct_info_done(struct tevent_req *subreq);
+
+static struct tevent_req *
+ad_handle_acct_info_send(TALLOC_CTX *mem_ctx,
+                         struct be_req *breq,
+                         struct be_acct_req *ar,
+                         struct sdap_id_ctx *ctx,
+                         struct sdap_domain *sdom,
+                         struct sdap_id_conn_ctx **conn)
+{
+    struct tevent_req *req;
+    struct ad_handle_acct_info_state *state;
+    struct be_ctx *be_ctx = be_req_get_be_ctx(breq);
+    errno_t ret;
+
+    req = tevent_req_create(mem_ctx, &state, struct ad_handle_acct_info_state);
+    if (req == NULL) {
+        return NULL;
+    }
+    state->breq = breq;
+    state->ar = ar;
+    state->ctx = ctx;
+    state->sdom = sdom;
+    state->conn = conn;
+    state->cindex = 0;
+
+    ret = ad_handle_acct_info_step(req);
+    if (ret == EOK) {
+        tevent_req_done(req);
+        tevent_req_post(req, be_ctx->ev);
+    } else if (ret != EAGAIN) {
+        tevent_req_error(req, ret);
+        tevent_req_post(req, be_ctx->ev);
+    }
+
+    /* Lookup in progress */
+    return req;
+}
+
+static errno_t
+ad_handle_acct_info_step(struct tevent_req *req)
+{
+    struct tevent_req *subreq;
+    struct ad_handle_acct_info_state *state = tevent_req_data(req,
+                                            struct ad_handle_acct_info_state);
+    bool noexist_delete = false;
+
+    if (state->conn[state->cindex] == NULL) {
+        return EOK;
+    }
+
+    if (state->conn[state->cindex+1] == NULL) {
+        noexist_delete = true;
+    }
+
+    subreq = sdap_handle_acct_req_send(state, state->breq,
+                                       state->ar, state->ctx,
+                                       state->sdom,
+                                       state->conn[state->cindex],
+                                       noexist_delete);
+    if (req == NULL) {
+        return ENOMEM;
+    }
+    tevent_req_set_callback(subreq, ad_handle_acct_info_done, req);
+    return EAGAIN;
+}
+
+static void
+ad_handle_acct_info_done(struct tevent_req *subreq)
+{
+    errno_t ret;
+    int dp_error;
+    int sdap_err;
+    const char *err;
+    struct tevent_req *req = tevent_req_callback_data(subreq,
+                                                      struct tevent_req);
+    struct ad_handle_acct_info_state *state = tevent_req_data(req,
+                                            struct ad_handle_acct_info_state);
+
+    ret = sdap_handle_acct_req_recv(subreq, &dp_error, &err, &sdap_err);
+    talloc_zfree(subreq);
+    if (ret != EOK) {
+        tevent_req_error(req, ret);
+        return;
+    }
+
+    if (sdap_err == EOK) {
+        tevent_req_done(req);
+        return;
+    } else if (sdap_err != ENOENT) {
+        tevent_req_error(req, EIO);
+        return;
+    }
+
+    /* Ret is only ENOENT now. Try the next connection */
+    state->cindex++;
+    ret = ad_handle_acct_info_step(req);
+    if (ret != EAGAIN) {
+        /* No additional search in progress. Save the last
+         * error status, we'll be returning it.
+         */
+        state->dp_error = dp_error;
+        state->err = err;
+
+        if (ret == EOK) {
+            /* No more connections */
+            tevent_req_done(req);
+        } else {
+            tevent_req_error(req, ret);
+        }
+        return;
+    }
+
+    /* Another lookup in progress */
+}
+
+static errno_t
+ad_handle_acct_info_recv(struct tevent_req *req,
+                         int *_dp_error, const char **_err)
+{
+    struct ad_handle_acct_info_state *state = tevent_req_data(req,
+                                            struct ad_handle_acct_info_state);
+
+    if (_dp_error) {
+        *_dp_error = state->dp_error;
+    }
+
+    if (_err) {
+        *_err = state->err;
+    }
+
+    TEVENT_REQ_RETURN_ON_ERROR(req);
+    return EOK;
+}
+
+struct sdap_id_conn_ctx **
+get_conn_list(struct be_req *breq, struct ad_id_ctx *ad_ctx,
+              struct sss_domain_info *dom, struct be_acct_req *ar)
+{
+    struct sdap_id_conn_ctx **clist;
+    int i=0;
+
+    /* LDAP, GC, sentinel */
+    clist = talloc_zero_array(breq, struct sdap_id_conn_ctx *, 3);
+    if (clist == NULL) return NULL;
+
+    switch (ar->entry_type & BE_REQ_TYPE_MASK) {
+    case BE_REQ_USER: /* user */
+    case BE_REQ_GROUP: /* group */
+    case BE_REQ_INITGROUPS: /* init groups for user */
+        if (ad_ctx->gc_ctx && IS_SUBDOMAIN(dom)) {
+            clist[i] = ad_ctx->gc_ctx;
+            i++;
+        } else {
+            clist[i] = ad_ctx->ldap_ctx;
+        }
+        break;
+
+    default:
+        clist[0] = ad_ctx->ldap_ctx;
+        break;
+    }
+
+    return clist;
+}
+
+static void ad_account_info_complete(struct tevent_req *req);
+
 void
 ad_account_info_handler(struct be_req *be_req)
 {
     struct ad_id_ctx *ad_ctx;
+    struct be_acct_req *ar;
     struct sdap_id_ctx *sdap_id_ctx;
     struct be_ctx *be_ctx = be_req_get_be_ctx(be_req);
+    struct tevent_req *req;
+    struct sss_domain_info *dom;
+    struct sdap_domain *sdom;
+    struct sdap_id_conn_ctx **clist;
+    errno_t ret;
 
     ad_ctx = talloc_get_type(be_ctx->bet_info[BET_ID].pvt_bet_data,
                              struct ad_id_ctx);
+    ar = talloc_get_type(be_req_get_data(be_req), struct be_acct_req);
     sdap_id_ctx = ad_ctx->sdap_id_ctx;
 
-    return sdap_handle_account_info(be_req, sdap_id_ctx, sdap_id_ctx->conn);
+    if (be_is_offline(be_ctx)) {
+        return be_req_terminate(be_req, DP_ERR_OFFLINE, EAGAIN, "Offline");
+    }
+
+    dom = be_ctx->domain;
+    if (strcasecmp(ar->domain, be_ctx->domain->name) != 0) {
+        /* Subdomain request, verify subdomain */
+        dom = find_subdomain_by_name(be_ctx->domain, ar->domain, true);
+    }
+
+    if (dom == NULL) {
+        ret = EINVAL;
+        goto fail;
+    }
+
+    /* Determine whether to connect to GC, LDAP or try both */
+    clist = get_conn_list(be_req, ad_ctx, dom, ar);
+    if (clist == NULL) {
+        ret = EIO;
+        goto fail;
+    }
+
+    sdom = sdap_domain_get(sdap_id_ctx->opts, dom);
+    if (sdom == NULL) {
+        ret = EIO;
+        goto fail;
+    }
+
+    req = ad_handle_acct_info_send(be_req, be_req, ar, sdap_id_ctx,
+                                   sdom, clist);
+    if (req == NULL) {
+        ret = ENOMEM;
+        goto fail;
+    }
+    tevent_req_set_callback(req, ad_account_info_complete, be_req);
+    return;
+
+fail:
+    be_req_terminate(be_req, DP_ERR_FATAL, ret, NULL);
+}
+
+static void
+ad_account_info_complete(struct tevent_req *req)
+{
+    struct be_req *be_req;
+    errno_t ret;
+    int dp_error;
+    const char *req_error_text;
+
+    be_req = tevent_req_callback_data(req, struct be_req);
+
+    ret = ad_handle_acct_info_recv(req, &dp_error, &req_error_text);
+    talloc_zfree(req);
+    if (dp_error == DP_ERR_OK) {
+        if (ret == EOK) {
+            req_error_text = NULL;
+        } else {
+            DEBUG(1, ("Bug: dp_error is OK on failed request"));
+            dp_error = DP_ERR_FATAL;
+            req_error_text = req_error_text;
+        }
+    } else if (dp_error == DP_ERR_OFFLINE) {
+        req_error_text = "Offline";
+    } else if (dp_error == DP_ERR_FATAL && ret == ENOMEM) {
+        req_error_text = "Out of memory";
+    } else {
+        req_error_text = req_error_text;
+    }
+
+    return be_req_terminate(be_req, dp_error, ret, req_error_text);
 }
 
 void
-- 
1.8.2.1

-------------- next part --------------
>From f6f2d5779c5718b2fe379347ee7cc296c4baeb43 Mon Sep 17 00:00:00 2001
From: Jakub Hrozek <jhrozek at redhat.com>
Date: Sun, 19 May 2013 21:14:39 +0200
Subject: [PATCH 15/15] AD: Store trusted AD domains as subdomains

https://fedorahosted.org/sssd/ticket/364

Looks up trusted domain objects in the LDAP and stores them as AD
subdomains.

Currently only trusted domains that run NT5 or newe from the same forest
are looked up and stored.
---
 src/providers/ad/ad_subdomains.c | 368 ++++++++++++++++++++++++++++++++++++++-
 1 file changed, 365 insertions(+), 3 deletions(-)

diff --git a/src/providers/ad/ad_subdomains.c b/src/providers/ad/ad_subdomains.c
index 098663cc824cd56e1d633a1f70cc88ca79260f52..902cb784f48bc80e8260ab120306b1af9b456c57 100644
--- a/src/providers/ad/ad_subdomains.c
+++ b/src/providers/ad/ad_subdomains.c
@@ -33,8 +33,25 @@
 #define AD_AT_NT_VERSION "NtVer"
 #define AD_AT_NETLOGON "netlogon"
 
+/* Attributes of AD trusted domains */
+#define AD_AT_FLATNAME      "flatName"
+#define AD_AT_SID           "securityIdentifier"
+#define AD_AT_TRUST_TYPE    "trustType"
+#define AD_AT_TRUST_PARTNER "trustPartner"
+#define AD_AT_TRUST_ATTRS   "trustAttributes"
+
 #define MASTER_DOMAIN_SID_FILTER "objectclass=domain"
 
+/* trustType=2 denotes uplevel (NT5 and later) trusted domains. See
+ * http://msdn.microsoft.com/en-us/library/windows/desktop/ms680342%28v=vs.85%29.aspx
+ * for example.
+ *
+ * The absence of msDS-TrustForestTrustInfo attribute denotes a domain from
+ * the same forest. See http://msdn.microsoft.com/en-us/library/cc223786.aspx
+ * for more information.
+ */
+#define SLAVE_DOMAIN_FILTER "(&(objectclass=trustedDomain)(trustType=2)(!(msDS-TrustForestTrustInfo=*)))"
+
 /* do not refresh more often than every 5 seconds for now */
 #define AD_SUBDOMAIN_REFRESH_LIMIT 5
 
@@ -45,6 +62,7 @@ struct ad_subdomains_ctx {
     struct be_ctx *be_ctx;
     struct sdap_id_ctx *sdap_id_ctx;
     struct sdap_domain *sdom;
+    struct sdap_id_conn_ctx *ldap_ctx;
     struct sss_idmap_ctx *idmap_ctx;
     char *domain_name;
 
@@ -67,10 +85,250 @@ struct ad_subdomains_req_ctx {
     char *flat_name;
 };
 
+static errno_t
+ads_store_sdap_subdom(struct ad_subdomains_ctx *ctx,
+                      struct sss_domain_info *parent)
+{
+    struct sss_domain_info *dom;
+    struct sdap_domain *sdom, *sditer;
+    char *basedn;
+    errno_t ret;
+
+    for (dom = get_next_domain(parent, true);
+         dom && IS_SUBDOMAIN(dom); /* if we get back to a parent, stop */
+         dom = get_next_domain(dom, false)) {
+
+        DLIST_FOR_EACH(sditer, ctx->sdom) {
+            if (sditer->dom == dom) {
+                break;
+            }
+        }
+
+        if (sditer == NULL) {
+            /* New sdap domain */
+            DEBUG(SSSDBG_TRACE_FUNC, ("subdomain %s is a new one, will "
+                  "create a new sdap domain object\n", dom->name));
+
+            ret = sdap_domain_add(ctx->sdap_id_ctx->opts, dom, &sdom);
+            if (ret != EOK) {
+                DEBUG(SSSDBG_OP_FAILURE,
+                    ("Cannot add new sdap domain for domain %s [%d]: %s\n",
+                    parent->name, ret, strerror(ret)));
+                return ret;
+            }
+            sditer = sdom;
+        }
+
+        /* Convert the domain name into search base */
+        ret = domain_to_basedn(sdom, sditer->dom->name, &basedn);
+        if (ret != EOK) {
+            DEBUG(SSSDBG_OP_FAILURE,
+                ("Cannot convert domain name [%s] to base DN [%d]: %s\n",
+                dom->name, ret, strerror(ret)));
+            talloc_free(basedn);
+            return ret;
+        }
+
+        /* Update search bases */
+        talloc_zfree(sdom->search_bases);
+        sdom->search_bases = talloc_array(sdom, struct sdap_search_base *, 2);
+        if (sdom->search_bases == NULL) {
+            return ret;
+        }
+        sdom->search_bases[1] = NULL;
+
+        ret = sdap_create_search_base(sdom, basedn, LDAP_SCOPE_SUBTREE, NULL,
+                                      &sdom->search_bases[0]);
+        talloc_free(basedn);
+        if (ret) {
+            DEBUG(SSSDBG_OP_FAILURE, ("Cannot create new sdap search base\n"));
+            return ret;
+        }
+
+        sdom->user_search_bases = sdom->search_bases;
+        sdom->group_search_bases = sdom->search_bases;
+        sdom->netgroup_search_bases = sdom->search_bases;
+        sdom->sudo_search_bases = sdom->search_bases;
+        sdom->service_search_bases = sdom->search_bases;
+        sdom->autofs_search_bases = sdom->search_bases;
+    }
+
+    return EOK;
+}
+
+static errno_t
+ad_subdom_store(struct ad_subdomains_ctx *ctx,
+                struct sss_domain_info *domain,
+                struct sysdb_attrs *subdom_attrs)
+{
+    TALLOC_CTX *tmp_ctx;
+    const char *name;
+    char *realm;
+    const char *flat;
+    errno_t ret;
+    enum idmap_error_code err;
+    struct ldb_message_element *el;
+    char *sid_str;
+    uint32_t trust_type;
+
+    tmp_ctx = talloc_new(NULL);
+    if (tmp_ctx == NULL) {
+        ret = ENOMEM;
+        goto done;
+    }
+
+    ret = sysdb_attrs_get_uint32_t(subdom_attrs, AD_AT_TRUST_TYPE,
+                                   &trust_type);
+    if (ret != EOK) {
+        DEBUG(SSSDBG_OP_FAILURE, ("sysdb_attrs_get_uint32_t failed.\n"));
+        goto done;
+    }
+
+    ret = sysdb_attrs_get_string(subdom_attrs, AD_AT_TRUST_PARTNER, &name);
+    if (ret != EOK) {
+        DEBUG(SSSDBG_OP_FAILURE, ("failed to get subdomain name\n"));
+        goto done;
+    }
+
+    realm = get_uppercase_realm(tmp_ctx, name);
+    if (!realm) {
+        ret = ENOMEM;
+        goto done;
+    }
+
+    ret = sysdb_attrs_get_string(subdom_attrs, AD_AT_FLATNAME, &flat);
+    if (ret) {
+        DEBUG(SSSDBG_OP_FAILURE, ("failed to get flat name of subdomain %s\n",
+                                  name));
+        goto done;
+    }
+
+    ret = sysdb_attrs_get_el(subdom_attrs, AD_AT_SID, &el);
+    if (ret != EOK || el->num_values != 1) {
+        DEBUG(SSSDBG_OP_FAILURE, ("sdap_attrs_get_el failed.\n"));
+        goto done;
+    }
+
+    err = sss_idmap_bin_sid_to_sid(ctx->idmap_ctx,
+                                   el->values[0].data,
+                                   el->values[0].length,
+                                   &sid_str);
+    if (err != IDMAP_SUCCESS) {
+        DEBUG(SSSDBG_MINOR_FAILURE,
+              ("Could not convert SID: [%s].\n", idmap_error_string(err)));
+        ret = EFAULT;
+        goto done;
+    }
+
+    ret = sysdb_subdomain_store(domain->sysdb, name, realm, flat, sid_str);
+    if (ret != EOK) {
+        DEBUG(SSSDBG_OP_FAILURE, ("sysdb_subdomain_store failed.\n"));
+        goto done;
+    }
+
+    ret = EOK;
+done:
+    talloc_free(tmp_ctx);
+    return ret;
+}
+
+static errno_t ad_subdomains_refresh(struct ad_subdomains_ctx *ctx,
+                                     int count, struct sysdb_attrs **reply,
+                                     bool *changes)
+{
+    struct sss_domain_info *domain, *dom;
+    bool handled[count];
+    const char *value;
+    int c, h;
+    int ret;
+
+    domain = ctx->be_ctx->domain;
+    memset(handled, 0, sizeof(bool) * count);
+    h = 0;
+
+    /* check existing subdomains */
+    for (dom = get_next_domain(domain, true);
+         dom && IS_SUBDOMAIN(dom); /* if we get back to a parent, stop */
+         dom = get_next_domain(dom, false)) {
+        for (c = 0; c < count; c++) {
+            if (handled[c]) {
+                continue;
+            }
+            ret = sysdb_attrs_get_string(reply[c], AD_AT_TRUST_PARTNER, &value);
+            if (ret != EOK) {
+                DEBUG(SSSDBG_OP_FAILURE, ("sysdb_attrs_get_string failed.\n"));
+                goto done;
+            }
+            if (strcmp(value, dom->name) == 0) {
+                break;
+            }
+        }
+
+        if (c >= count) {
+            /* ok this subdomain does not exist anymore, let's clean up */
+            dom->disabled = true;
+            ret = sysdb_subdomain_delete(dom->sysdb, dom->name);
+            if (ret != EOK) {
+                goto done;
+            }
+
+            /* Remove the subdomain from the list of LDAP domains */
+            sdap_domain_remove(dom);
+        } else {
+            /* ok let's try to update it */
+            ret = ad_subdom_store(ctx, domain, reply[c]);
+            if (ret) {
+                /* Nothing we can do about the error. Let's at least try
+                 * to reuse the existing domains
+                 */
+                DEBUG(SSSDBG_MINOR_FAILURE, ("Failed to parse subdom data, "
+                      "will try to use cached subdomain\n"));
+            }
+            handled[c] = true;
+            h++;
+        }
+    }
+
+    if (count == h) {
+        /* all domains were already accounted for and have been updated */
+        ret = EOK;
+        goto done;
+    }
+
+    /* if we get here it means we have changes to the subdomains list */
+    *changes = true;
+
+    for (c = 0; c < count; c++) {
+        if (handled[c]) {
+            continue;
+        }
+        /* Nothing we can do about the error. Let's at least try
+         * to reuse the existing domains.
+         */
+        ret = ad_subdom_store(ctx, domain, reply[c]);
+        if (ret) {
+            DEBUG(SSSDBG_MINOR_FAILURE, ("Failed to parse subdom data, "
+                  "will try to use cached subdomain\n"));
+        }
+    }
+
+    ret = EOK;
+
+done:
+    if (ret != EOK) {
+        ctx->last_refreshed = 0;
+    } else {
+        ctx->last_refreshed = time(NULL);
+    }
+
+    return ret;
+}
+
 static void ad_subdomains_get_conn_done(struct tevent_req *req);
 static errno_t ad_subdomains_get_master_sid(struct ad_subdomains_req_ctx *ctx);
 static void ad_subdomains_get_master_sid_done(struct tevent_req *req);
 static void ad_subdomains_get_netlogon_done(struct tevent_req *req);
+static errno_t ad_subdomains_get_slave(struct ad_subdomains_req_ctx *ctx);
 
 static void ad_subdomains_retrieve(struct ad_subdomains_ctx *ctx,
                                    struct be_req *be_req)
@@ -94,7 +352,7 @@ static void ad_subdomains_retrieve(struct ad_subdomains_ctx *ctx,
     req_ctx->reply = NULL;
 
     req_ctx->sdap_op = sdap_id_op_create(req_ctx,
-                                         ctx->sdap_id_ctx->conn->conn_cache);
+                                         ctx->ldap_ctx->conn_cache);
     if (req_ctx->sdap_op == NULL) {
         DEBUG(SSSDBG_OP_FAILURE, ("sdap_id_op_create failed.\n"));
         ret = ENOMEM;
@@ -375,10 +633,113 @@ static void ad_subdomains_get_netlogon_done(struct tevent_req *req)
     ret = sysdb_master_domain_add_info(ctx->sd_ctx->be_ctx->domain,
                                        NULL, ctx->flat_name, ctx->master_sid);
 
+    ret = ad_subdomains_get_slave(ctx);
+    if (ret == EAGAIN) {
+        return;
+    } else if (ret != EOK) {
+        goto done;
+    }
+
+done:
+    be_req_terminate(ctx->be_req, dp_error, ret, NULL);
+}
+
+static void ad_subdomains_get_slave_domain_done(struct tevent_req *req);
+
+static errno_t ad_subdomains_get_slave(struct ad_subdomains_req_ctx *ctx)
+{
+    struct tevent_req *req;
+    struct sdap_search_base *base;
+    const char *slave_dom_attrs[] = { AD_AT_FLATNAME, AD_AT_TRUST_PARTNER,
+                                      AD_AT_SID, AD_AT_TRUST_TYPE,
+                                      AD_AT_TRUST_ATTRS, NULL };
+
+    base = ctx->sd_ctx->sdap_id_ctx->opts->sdom->search_bases[ctx->base_iter];
+    if (base == NULL) {
+        return EOK;
+    }
+
+    req = sdap_get_generic_send(ctx, ctx->sd_ctx->be_ctx->ev,
+                           ctx->sd_ctx->sdap_id_ctx->opts,
+                           sdap_id_op_handle(ctx->sdap_op),
+                           base->basedn, LDAP_SCOPE_SUBTREE,
+                           SLAVE_DOMAIN_FILTER, slave_dom_attrs,
+                           NULL, 0,
+                           dp_opt_get_int(ctx->sd_ctx->sdap_id_ctx->opts->basic,
+                                          SDAP_SEARCH_TIMEOUT),
+                           false);
+
+    if (req == NULL) {
+        DEBUG(SSSDBG_OP_FAILURE, ("sdap_get_generic_send failed.\n"));
+        return ENOMEM;
+    }
+
+    tevent_req_set_callback(req, ad_subdomains_get_slave_domain_done, ctx);
+    return EAGAIN;
+}
+
+static void ad_subdomains_get_slave_domain_done(struct tevent_req *req)
+{
+    int ret;
+    size_t reply_count;
+    struct sysdb_attrs **reply = NULL;
+    struct ad_subdomains_req_ctx *ctx;
+    int dp_error = DP_ERR_FATAL;
+    bool refresh_has_changes = false;
+
+    ctx = tevent_req_callback_data(req, struct ad_subdomains_req_ctx);
+
+    ret = sdap_get_generic_recv(req, ctx, &reply_count, &reply);
+    talloc_zfree(req);
+    if (ret != EOK) {
+        DEBUG(SSSDBG_OP_FAILURE, ("sdap_get_generic_send request failed.\n"));
+        goto done;
+    }
+
+    if (reply_count) {
+        ctx->reply = talloc_realloc(ctx, ctx->reply, struct sysdb_attrs *,
+                                    ctx->reply_count + reply_count);
+        if (ctx->reply == NULL) {
+            ret = ENOMEM;
+            goto done;
+        }
+        memcpy(ctx->reply+ctx->reply_count, reply,
+               reply_count * sizeof(struct sysdb_attrs *));
+        ctx->reply_count += reply_count;
+    }
+
+    ctx->base_iter++;
+    ret = ad_subdomains_get_slave(ctx);
+    if (ret == EAGAIN) {
+        return;
+    } else if (ret != EOK) {
+        goto done;
+    }
+
+    /* Got all the subdomains, let's process them */
+    ret = ad_subdomains_refresh(ctx->sd_ctx, ctx->reply_count, ctx->reply,
+                                &refresh_has_changes);
+    if (ret != EOK) {
+        DEBUG(SSSDBG_OP_FAILURE, ("Failed to refresh subdomains.\n"));
+        goto done;
+    }
+
+    if (refresh_has_changes) {
+        ret = sysdb_update_subdomains(ctx->sd_ctx->be_ctx->domain);
+        if (ret != EOK) {
+            DEBUG(SSSDBG_OP_FAILURE, ("sysdb_update_subdomains failed.\n"));
+            goto done;
+        }
+
+        ret = ads_store_sdap_subdom(ctx->sd_ctx, ctx->sd_ctx->be_ctx->domain);
+        if (ret != EOK) {
+            DEBUG(SSSDBG_OP_FAILURE, ("ads_store_sdap_subdom failed.\n"));
+            goto done;
+        }
+    }
+
     ret = EOK;
-
 done:
-
     if (ret == EOK) {
         ctx->sd_ctx->last_refreshed = time(NULL);
         dp_error = DP_ERR_OK;
@@ -499,6 +860,7 @@ int ad_subdom_init(struct be_ctx *be_ctx,
 
     ctx->be_ctx = be_ctx;
     ctx->sdom = id_ctx->sdap_id_ctx->opts->sdom;
+    ctx->ldap_ctx = id_ctx->ldap_ctx;
     ctx->sdap_id_ctx = id_ctx->sdap_id_ctx;
     ctx->domain_name = talloc_strdup(ctx, ad_domain);
     if (ctx->domain_name == NULL) {
-- 
1.8.2.1



More information about the sssd-devel mailing list