[SSSD] [PATCHES] Support one-way trusts with AD domains in IPA server mode

Jakub Hrozek jhrozek at redhat.com
Thu Jun 4 11:15:55 UTC 2015


Hi,

the attached patches implement most of the one-way trust functionality.
The trust directions, fetching keytabs and using different keytabs and
different principals works well for me. I'm still working on changing
the ldap_child to either use ccache collection or using the environment
variables safer, also the failover changes are still missing. But the
feature is testable in my opinion.

There are two NOSUBMIT patches that are useful only for testing until
patches that allow the IPA server principal to read the direction are
available. I hope exposing my super-secret DM password like that is OK,
please change these patches in your testing..

Several patches are not strictly related to one-way trusts, but unify
the info we store for subdomains in IPA and AD or info we store for
subdomains that represent forest root versus member domains.

There are also patches that rename or refactor a bit functions in the
ad_common.c module. I hope this is acceptable, because I had a hard time
le-learning my way around the module. I still think we need to make the
code that selects the appropriate principal from keytab readable better,
currently the setting of "desired_primary" and "default_primary" is a
total mess.

Most of the code is also unit-tested, so several patches just change
tests.

Here are some points I'd like to get reviewed carefully as I'm not sure
about them myself:
    - do we need the SD_TRUST_DIRECTION_NOT_SET enum? I was going back
      and forth between having it and just using either a NULL pointer
      if the trust direction is uknown or a zero value.
    - is the reading of the direction OK? Do we fall back the way we
      should?
    - are the additional data stored with (sub)domains like forest
      stored for forest root subdomains and realm for master domains OK? In
      my opinion they make processing of domains easier as there's fewer
      special cases..
    - should I add a full-blown getter for the forest_root member of
      sss_domain_info a a first step towards making the structure
      opaque?

Also feel free to propose more tests, either scenarios that I should
test manually or something that should be unit tested.
-------------- next part --------------
>From 6e2f09bf0c3bd4ad4f9246166ce02deb6f0a9ec7 Mon Sep 17 00:00:00 2001
From: Jakub Hrozek <jhrozek at redhat.com>
Date: Tue, 12 May 2015 14:24:00 +0200
Subject: [PATCH 01/29] SYSDB: Store trust direction for subdomains

We need to store the subdomain trust direction in order to recover the
structure after SSSD restart.
---
 src/confdb/confdb.h                |  8 ++++++
 src/db/sysdb.h                     |  4 ++-
 src/db/sysdb_subdomains.c          | 51 ++++++++++++++++++++++++++++++++---
 src/providers/ad/ad_subdomains.c   |  3 ++-
 src/providers/ipa/ipa_subdomains.c |  3 ++-
 src/tests/cmocka/test_fqnames.c    |  3 ++-
 src/tests/cmocka/test_nss_srv.c    |  6 +++--
 src/tests/sysdb-tests.c            | 55 +++++++++++++++++++++++++++++++-------
 src/util/domain_info_utils.c       |  4 ++-
 src/util/util.h                    |  3 ++-
 10 files changed, 118 insertions(+), 22 deletions(-)

diff --git a/src/confdb/confdb.h b/src/confdb/confdb.h
index c8c91288cd63df7629a98802b7b5373df92d6ca4..03dc27195d43ed4758c84eede19941869abad7c7 100644
--- a/src/confdb/confdb.h
+++ b/src/confdb/confdb.h
@@ -206,6 +206,13 @@
 struct confdb_ctx;
 struct config_file_ctx;
 
+enum trust_direction {
+    SD_TRUST_DIRECTION_NOT_SET,
+    SD_TRUST_DIRECTION_OUTBOUND,
+    SD_TRUST_DIRECTION_TWOWAY
+};
+
+
 /**
  * Data structure storing all of the basic features
  * of a domain.
@@ -259,6 +266,7 @@ struct sss_domain_info {
     char *flat_name;
     char *domain_id;
     char *forest;
+    enum trust_direction trust_direction;
     struct timeval subdomains_last_checked;
 
     bool has_views;
diff --git a/src/db/sysdb.h b/src/db/sysdb.h
index c3d2c1406321e01b325e04e0e3f263893eb91b2e..3d0d07c6a56f2168ffe26f46d17973bc09330aaf 100644
--- a/src/db/sysdb.h
+++ b/src/db/sysdb.h
@@ -138,6 +138,7 @@
 #define SYSDB_SUBDOMAIN_MPG "mpg"
 #define SYSDB_SUBDOMAIN_ENUM "enumerate"
 #define SYSDB_SUBDOMAIN_FOREST "memberOfForest"
+#define SYSDB_SUBDOMAIN_TRUST_DIRECTION "trustDirection"
 
 #define SYSDB_BASE_ID "baseID"
 #define SYSDB_ID_RANGE_SIZE "idRangeSize"
@@ -430,7 +431,8 @@ errno_t sysdb_domain_create(struct sysdb_ctx *sysdb, const char *domain_name);
 errno_t sysdb_subdomain_store(struct sysdb_ctx *sysdb,
                               const char *name, const char *realm,
                               const char *flat_name, const char *domain_id,
-                              bool mpg, bool enumerate, const char *forest);
+                              bool mpg, bool enumerate, const char *forest,
+                              enum trust_direction trust_direction);
 
 errno_t sysdb_update_subdomains(struct sss_domain_info *domain);
 
diff --git a/src/db/sysdb_subdomains.c b/src/db/sysdb_subdomains.c
index 06e9e094e79df3ed554eb7f57579ec10e5b963bd..85edd859f269976713b6f35c37dc0bab3d1b6e2e 100644
--- a/src/db/sysdb_subdomains.c
+++ b/src/db/sysdb_subdomains.c
@@ -36,6 +36,7 @@ errno_t sysdb_update_subdomains(struct sss_domain_info *domain)
                            SYSDB_SUBDOMAIN_MPG,
                            SYSDB_SUBDOMAIN_ENUM,
                            SYSDB_SUBDOMAIN_FOREST,
+                           SYSDB_SUBDOMAIN_TRUST_DIRECTION,
                            NULL};
     struct sss_domain_info *dom;
     struct ldb_dn *basedn;
@@ -46,6 +47,7 @@ errno_t sysdb_update_subdomains(struct sss_domain_info *domain)
     const char *forest;
     bool mpg;
     bool enumerate;
+    int trust_direction;
 
     tmp_ctx = talloc_new(NULL);
     if (tmp_ctx == NULL) {
@@ -106,6 +108,10 @@ errno_t sysdb_update_subdomains(struct sss_domain_info *domain)
         forest = ldb_msg_find_attr_as_string(res->msgs[i],
                                              SYSDB_SUBDOMAIN_FOREST, NULL);
 
+        trust_direction = ldb_msg_find_attr_as_int(res->msgs[i],
+                                             SYSDB_SUBDOMAIN_TRUST_DIRECTION,
+                                             SD_TRUST_DIRECTION_NOT_SET);
+
         /* explicitly use dom->next as we need to check 'disabled' domains */
         for (dom = domain->subdomains; dom; dom = dom->next) {
             if (strcasecmp(dom->name, name) == 0) {
@@ -155,7 +161,7 @@ errno_t sysdb_update_subdomains(struct sss_domain_info *domain)
 
                 if (dom->enumerate != enumerate) {
                     DEBUG(SSSDBG_TRACE_INTERNAL,
-                          "MPG state change from [%s] to [%s]!\n",
+                          "enumerate state change from [%s] to [%s]!\n",
                            dom->enumerate ? "true" : "false",
                            enumerate ? "true" : "false");
                     dom->enumerate = enumerate;
@@ -204,13 +210,21 @@ errno_t sysdb_update_subdomains(struct sss_domain_info *domain)
                     }
                 }
 
+                if (dom->trust_direction != trust_direction) {
+                    DEBUG(SSSDBG_TRACE_INTERNAL,
+                          "Trust direction change from [%d] to [%d]!\n",
+                           dom->trust_direction, trust_direction);
+                    dom->trust_direction = trust_direction;
+                }
+
                 break;
             }
         }
         /* If not found in loop it is a new subdomain */
         if (dom == NULL) {
             dom = new_subdomain(domain, domain, name, realm,
-                                flat, id, mpg, enumerate, forest);
+                                flat, id, mpg, enumerate, forest,
+                                trust_direction);
             if (dom == NULL) {
                 ret = ENOMEM;
                 goto done;
@@ -524,7 +538,8 @@ done:
 errno_t sysdb_subdomain_store(struct sysdb_ctx *sysdb,
                               const char *name, const char *realm,
                               const char *flat_name, const char *domain_id,
-                              bool mpg, bool enumerate, const char *forest)
+                              bool mpg, bool enumerate, const char *forest,
+                              enum trust_direction trust_direction)
 {
     TALLOC_CTX *tmp_ctx;
     struct ldb_message *msg;
@@ -537,6 +552,7 @@ errno_t sysdb_subdomain_store(struct sysdb_ctx *sysdb,
                            SYSDB_SUBDOMAIN_MPG,
                            SYSDB_SUBDOMAIN_ENUM,
                            SYSDB_SUBDOMAIN_FOREST,
+                           SYSDB_SUBDOMAIN_TRUST_DIRECTION,
                            NULL};
     const char *tmp_str;
     bool tmp_bool;
@@ -547,6 +563,8 @@ errno_t sysdb_subdomain_store(struct sysdb_ctx *sysdb,
     int mpg_flags = 0;
     int enum_flags = 0;
     int forest_flags = 0;
+    int td_flags = 0;
+    enum trust_direction tmp_td;
     int ret;
 
     tmp_ctx = talloc_new(NULL);
@@ -578,6 +596,7 @@ errno_t sysdb_subdomain_store(struct sysdb_ctx *sysdb,
         mpg_flags = LDB_FLAG_MOD_ADD;
         enum_flags = LDB_FLAG_MOD_ADD;
         if (forest) forest_flags = LDB_FLAG_MOD_ADD;
+        if (trust_direction) td_flags = LDB_FLAG_MOD_ADD;
     } else if (res->count != 1) {
         ret = EINVAL;
         goto done;
@@ -622,10 +641,18 @@ errno_t sysdb_subdomain_store(struct sysdb_ctx *sysdb,
                 forest_flags = LDB_FLAG_MOD_REPLACE;
             }
         }
+
+        tmp_td = ldb_msg_find_attr_as_uint(res->msgs[0],
+                                           SYSDB_SUBDOMAIN_TRUST_DIRECTION,
+                                           SD_TRUST_DIRECTION_NOT_SET);
+        if (tmp_td != trust_direction) {
+            td_flags = LDB_FLAG_MOD_REPLACE;
+        }
     }
 
     if (!store && realm_flags == 0 && flat_flags == 0 && id_flags == 0
-            && mpg_flags == 0 && enum_flags == 0 && forest_flags == 0) {
+            && mpg_flags == 0 && enum_flags == 0 && forest_flags == 0
+            && td_flags == 0) {
         ret = EOK;
         goto done;
     }
@@ -738,6 +765,22 @@ errno_t sysdb_subdomain_store(struct sysdb_ctx *sysdb,
         }
     }
 
+    if (td_flags) {
+        ret = ldb_msg_add_empty(msg, SYSDB_SUBDOMAIN_TRUST_DIRECTION,
+                                td_flags, NULL);
+        if (ret != LDB_SUCCESS) {
+            ret = sysdb_error_to_errno(ret);
+            goto done;
+        }
+
+        ret = ldb_msg_add_fmt(msg, SYSDB_SUBDOMAIN_TRUST_DIRECTION,
+                              "%u", trust_direction);
+        if (ret != LDB_SUCCESS) {
+            ret = sysdb_error_to_errno(ret);
+            goto done;
+        }
+    }
+
     ret = ldb_modify(sysdb->ldb, msg);
     if (ret != LDB_SUCCESS) {
         DEBUG(SSSDBG_FATAL_FAILURE, "Failed to add subdomain attributes to "
diff --git a/src/providers/ad/ad_subdomains.c b/src/providers/ad/ad_subdomains.c
index 5a6e9338d6dc581fba4f76e8b28f92a558eb6294..da496b6dea63cfddf1c6a456a76c9547a0fca116 100644
--- a/src/providers/ad/ad_subdomains.c
+++ b/src/providers/ad/ad_subdomains.c
@@ -309,7 +309,8 @@ ad_subdom_store(struct ad_subdomains_ctx *ctx,
                                              sid_str);
 
     ret = sysdb_subdomain_store(domain->sysdb, name, realm, flat, sid_str,
-                                mpg, enumerate, domain->forest);
+                                mpg, enumerate, domain->forest,
+                                SD_TRUST_DIRECTION_NOT_SET);
     if (ret != EOK) {
         DEBUG(SSSDBG_OP_FAILURE, "sysdb_subdomain_store failed.\n");
         goto done;
diff --git a/src/providers/ipa/ipa_subdomains.c b/src/providers/ipa/ipa_subdomains.c
index f863b806c8fed01f498d18b953832ebeba7a85d3..b82bda23a9b5ae133cb2a6d67aae2f74e5a1f206 100644
--- a/src/providers/ipa/ipa_subdomains.c
+++ b/src/providers/ipa/ipa_subdomains.c
@@ -693,7 +693,8 @@ static errno_t ipa_subdom_store(struct sss_domain_info *parent,
     }
 
     ret = sysdb_subdomain_store(parent->sysdb, name, realm, flat,
-                                id, mpg, enumerate, forest);
+                                id, mpg, enumerate, forest,
+                                SD_TRUST_DIRECTION_NOT_SET);
     if (ret) {
         DEBUG(SSSDBG_OP_FAILURE, "sysdb_subdomain_store failed.\n");
         goto done;
diff --git a/src/tests/cmocka/test_fqnames.c b/src/tests/cmocka/test_fqnames.c
index 0326d5a645bad0396d8b20f635e6ee6b3e4b5587..a85e8337c333b8ad846ba39da6b8167193fc542b 100644
--- a/src/tests/cmocka/test_fqnames.c
+++ b/src/tests/cmocka/test_fqnames.c
@@ -309,7 +309,8 @@ static int parse_name_test_setup(void **state)
      * discovered
      */
     test_ctx->subdom = new_subdomain(dom, dom, SUBDOMNAME, NULL, SUBFLATNAME,
-                                     NULL, false, false, NULL);
+                                     NULL, false, false, NULL,
+                                     SD_TRUST_DIRECTION_NOT_SET);
     assert_non_null(test_ctx->subdom);
 
     check_leaks_push(test_ctx);
diff --git a/src/tests/cmocka/test_nss_srv.c b/src/tests/cmocka/test_nss_srv.c
index 9119d4b154473e1df72d23a795cc5bc2efd24d2a..659cfc68c54f2a1a162970a75f3fbccbeeeb8eaa 100644
--- a/src/tests/cmocka/test_nss_srv.c
+++ b/src/tests/cmocka/test_nss_srv.c
@@ -2666,12 +2666,14 @@ static int nss_subdom_test_setup(void **state)
 
     subdomain = new_subdomain(nss_test_ctx, nss_test_ctx->tctx->dom,
                               testdom[0], testdom[1], testdom[2], testdom[3],
-                              false, false, NULL);
+                              false, false, NULL,
+                              SD_TRUST_DIRECTION_NOT_SET);
     assert_non_null(subdomain);
 
     ret = sysdb_subdomain_store(nss_test_ctx->tctx->sysdb,
                                 testdom[0], testdom[1], testdom[2], testdom[3],
-                                false, false, NULL);
+                                false, false, NULL,
+                                SD_TRUST_DIRECTION_NOT_SET);
     assert_int_equal(ret, EOK);
 
     ret = sysdb_update_subdomains(nss_test_ctx->tctx->dom);
diff --git a/src/tests/sysdb-tests.c b/src/tests/sysdb-tests.c
index 6a77c6eebc4d8e29c09d67f9b1de17939591410a..1dc228df2c4a0c5dbada32ac8bf6efcfbdb080bd 100644
--- a/src/tests/sysdb-tests.c
+++ b/src/tests/sysdb-tests.c
@@ -1312,7 +1312,8 @@ START_TEST (test_sysdb_get_user_attr_subdomain)
     /* Create subdomain */
     subdomain = new_subdomain(test_ctx, test_ctx->domain,
                               "test.sub", "TEST.SUB", "test", "S-3",
-                              false, false, NULL);
+                              false, false, NULL,
+                              SD_TRUST_DIRECTION_NOT_SET);
     fail_if(subdomain == NULL, "Failed to create new subdomain.");
 
     ret = sss_names_init_from_args(test_ctx,
@@ -5235,21 +5236,25 @@ START_TEST(test_sysdb_subdomain_create)
 
     ret = sysdb_subdomain_store(test_ctx->sysdb,
                                 dom1[0], dom1[1], dom1[2], dom1[3],
-                                false, false, NULL);
+                                false, false, NULL,
+                                SD_TRUST_DIRECTION_OUTBOUND);
     fail_if(ret != EOK, "Could not set up the test (dom1)");
 
     ret = sysdb_update_subdomains(test_ctx->domain);
     fail_unless(ret == EOK, "sysdb_update_subdomains failed with [%d][%s]",
                             ret, strerror(ret));
 
-    fail_if(test_ctx->domain->subdomains == NULL, "Empyt sub-domain list.");
+    fail_if(test_ctx->domain->subdomains == NULL, "Empty sub-domain list.");
     fail_if(strcmp(test_ctx->domain->subdomains->name, dom1[0]) != 0,
             "Unexpected sub-domain found, expected [%s], got [%s]",
             dom1[0], test_ctx->domain->subdomains->name);
+    fail_unless(test_ctx->domain->subdomains->trust_direction == \
+                SD_TRUST_DIRECTION_OUTBOUND);
 
     ret = sysdb_subdomain_store(test_ctx->sysdb,
                                 dom2[0], dom2[1], dom2[2], dom2[3],
-                                false, false, NULL);
+                                false, false, NULL,
+                                SD_TRUST_DIRECTION_TWOWAY);
     fail_if(ret != EOK, "Could not set up the test (dom2)");
 
     ret = sysdb_update_subdomains(test_ctx->domain);
@@ -5260,6 +5265,30 @@ START_TEST(test_sysdb_subdomain_create)
     fail_if(strcmp(test_ctx->domain->subdomains->next->name, dom2[0]) != 0,
             "Unexpected sub-domain found, expected [%s], got [%s]",
             dom2[0], test_ctx->domain->subdomains->next->name);
+    fail_unless(test_ctx->domain->subdomains->next->trust_direction == \
+                SD_TRUST_DIRECTION_TWOWAY);
+
+    /* Reverse the trust directions */
+    ret = sysdb_subdomain_store(test_ctx->sysdb,
+                                dom1[0], dom1[1], dom1[2], dom1[3],
+                                false, false, NULL,
+                                SD_TRUST_DIRECTION_TWOWAY);
+    fail_if(ret != EOK, "Could not set up the test (dom1)");
+
+    ret = sysdb_subdomain_store(test_ctx->sysdb,
+                                dom2[0], dom2[1], dom2[2], dom2[3],
+                                false, false, NULL,
+                                SD_TRUST_DIRECTION_OUTBOUND);
+    fail_if(ret != EOK, "Could not set up the test (dom2)");
+
+    ret = sysdb_update_subdomains(test_ctx->domain);
+    fail_unless(ret == EOK, "sysdb_update_subdomains failed with [%d][%s]",
+                            ret, strerror(ret));
+
+    fail_unless(test_ctx->domain->subdomains->trust_direction == \
+                SD_TRUST_DIRECTION_TWOWAY);
+    fail_unless(test_ctx->domain->subdomains->next->trust_direction == \
+                SD_TRUST_DIRECTION_OUTBOUND);
 
     ret = sysdb_subdomain_delete(test_ctx->sysdb, dom2[0]);
     fail_if(ret != EOK, "Could not delete subdomain");
@@ -5294,11 +5323,13 @@ START_TEST(test_sysdb_subdomain_store_user)
 
     subdomain = new_subdomain(test_ctx, test_ctx->domain,
                               testdom[0], testdom[1], testdom[2], testdom[3],
-                              false, false, NULL);
+                              false, false, NULL,
+                              SD_TRUST_DIRECTION_NOT_SET);
     fail_unless(subdomain != NULL, "Failed to create new subdomin.");
     ret = sysdb_subdomain_store(test_ctx->sysdb,
                                 testdom[0], testdom[1], testdom[2], testdom[3],
-                                false, false, NULL);
+                                false, false, NULL,
+                                SD_TRUST_DIRECTION_NOT_SET);
     fail_if(ret != EOK, "Could not set up the test (test subdom)");
 
     ret = sysdb_update_subdomains(test_ctx->domain);
@@ -5365,11 +5396,13 @@ START_TEST(test_sysdb_subdomain_user_ops)
 
     subdomain = new_subdomain(test_ctx, test_ctx->domain,
                               testdom[0], testdom[1], testdom[2], testdom[3],
-                              false, false, NULL);
+                              false, false, NULL,
+                              SD_TRUST_DIRECTION_NOT_SET);
     fail_unless(subdomain != NULL, "Failed to create new subdomin.");
     ret = sysdb_subdomain_store(test_ctx->sysdb,
                                 testdom[0], testdom[1], testdom[2], testdom[3],
-                                false, false, NULL);
+                                false, false, NULL,
+                                SD_TRUST_DIRECTION_NOT_SET);
     fail_if(ret != EOK, "Could not set up the test (test subdom)");
 
     ret = sysdb_update_subdomains(test_ctx->domain);
@@ -5420,11 +5453,13 @@ START_TEST(test_sysdb_subdomain_group_ops)
 
     subdomain = new_subdomain(test_ctx, test_ctx->domain,
                               testdom[0], testdom[1], testdom[2], testdom[3],
-                              false, false, NULL);
+                              false, false, NULL,
+                              SD_TRUST_DIRECTION_NOT_SET);
     fail_unless(subdomain != NULL, "Failed to create new subdomin.");
     ret = sysdb_subdomain_store(test_ctx->sysdb,
                                 testdom[0], testdom[1], testdom[2], testdom[3],
-                                false, false, NULL);
+                                false, false, NULL,
+                                SD_TRUST_DIRECTION_NOT_SET);
     fail_if(ret != EOK, "Could not set up the test (test subdom)");
 
     ret = sysdb_update_subdomains(test_ctx->domain);
diff --git a/src/util/domain_info_utils.c b/src/util/domain_info_utils.c
index dc306848c3b3ba87a5ada1867878e13826da00a8..aa79a3a374f2ac8d060b5d81ca093670dbb4e4d3 100644
--- a/src/util/domain_info_utils.c
+++ b/src/util/domain_info_utils.c
@@ -203,7 +203,8 @@ struct sss_domain_info *new_subdomain(TALLOC_CTX *mem_ctx,
                                       const char *id,
                                       bool mpg,
                                       bool enumerate,
-                                      const char *forest)
+                                      const char *forest,
+                                      enum trust_direction trust_direction)
 {
     struct sss_domain_info *dom;
 
@@ -281,6 +282,7 @@ struct sss_domain_info *new_subdomain(TALLOC_CTX *mem_ctx,
     dom->enumerate = enumerate;
     dom->fqnames = true;
     dom->mpg = mpg;
+    dom->trust_direction = trust_direction;
     /* If the parent domain explicitly limits ID ranges, the subdomain
      * should honour the limits as well.
      */
diff --git a/src/util/util.h b/src/util/util.h
index c86bcea5b01f6bc02d18b17a2660262105648cf9..5be1c816b7566ff4ae97b5ff61a5824d982475f9 100644
--- a/src/util/util.h
+++ b/src/util/util.h
@@ -577,7 +577,8 @@ struct sss_domain_info *new_subdomain(TALLOC_CTX *mem_ctx,
                                       const char *id,
                                       bool mpg,
                                       bool enumerate,
-                                      const char *forest);
+                                      const char *forest,
+                                      enum trust_direction trust_direction);
 
 errno_t sssd_domain_init(TALLOC_CTX *mem_ctx,
                          struct confdb_ctx *cdb,
-- 
2.1.0

-------------- next part --------------
>From a113ef9c3405bd5f7ac891a6a4a6a49c1373f17c Mon Sep 17 00:00:00 2001
From: Jakub Hrozek <jhrozek at redhat.com>
Date: Mon, 1 Jun 2015 16:53:01 +0200
Subject: [PATCH 02/29] UTIL/SYSDB: Move new_subdomain() to sysdb_subdomains.c
 and make it private

In order to make updating the subdomain list a two-step process.
Therefore we need to make sure that update_subdomains() is the only
interface towards the SSSD that changes the subdomain list.

Move the new_subdomain() function to sysdb_subdomains.c and only make it
available through a private header so it's usable by unit tests.
---
 src/db/sysdb_private.h          |  17 ++++++
 src/db/sysdb_subdomains.c       | 124 ++++++++++++++++++++++++++++++++++++++++
 src/tests/cmocka/test_nss_srv.c |   1 +
 src/util/domain_info_utils.c    | 124 ----------------------------------------
 src/util/util.h                 |  11 ----
 5 files changed, 142 insertions(+), 135 deletions(-)

diff --git a/src/db/sysdb_private.h b/src/db/sysdb_private.h
index 8a5b8be8cbcf0513fa4c471ac41f803a4e2a5b24..b316183fd870f0f5470c55bc75152846c9031b63 100644
--- a/src/db/sysdb_private.h
+++ b/src/db/sysdb_private.h
@@ -125,4 +125,21 @@ int add_string(struct ldb_message *msg, int flags,
                const char *attr, const char *value);
 int add_ulong(struct ldb_message *msg, int flags,
               const char *attr, unsigned long value);
+
+/* The utility function to create a subdomain sss_domain_info object is handy
+ * for unit tests, so it should be available in a header, but not a public util
+ * one, because the only interface for the deamon itself should be adding
+ * the sysdb domain object and calling sysdb_update_subdomains()
+ */
+struct sss_domain_info *new_subdomain(TALLOC_CTX *mem_ctx,
+                                      struct sss_domain_info *parent,
+                                      const char *name,
+                                      const char *realm,
+                                      const char *flat_name,
+                                      const char *id,
+                                      bool mpg,
+                                      bool enumerate,
+                                      const char *forest,
+                                      enum trust_direction trust_direction);
+
 #endif /* __INT_SYS_DB_H__ */
diff --git a/src/db/sysdb_subdomains.c b/src/db/sysdb_subdomains.c
index 85edd859f269976713b6f35c37dc0bab3d1b6e2e..816dd5d2c211bc0c3180a1489eab6799e9b70f57 100644
--- a/src/db/sysdb_subdomains.c
+++ b/src/db/sysdb_subdomains.c
@@ -23,6 +23,130 @@
 #include "util/util.h"
 #include "db/sysdb_private.h"
 
+struct sss_domain_info *new_subdomain(TALLOC_CTX *mem_ctx,
+                                      struct sss_domain_info *parent,
+                                      const char *name,
+                                      const char *realm,
+                                      const char *flat_name,
+                                      const char *id,
+                                      bool mpg,
+                                      bool enumerate,
+                                      const char *forest,
+                                      enum trust_direction trust_direction)
+{
+    struct sss_domain_info *dom;
+
+    DEBUG(SSSDBG_TRACE_FUNC,
+          "Creating [%s] as subdomain of [%s]!\n", name, parent->name);
+
+    dom = talloc_zero(mem_ctx, struct sss_domain_info);
+    if (dom == NULL) {
+        DEBUG(SSSDBG_OP_FAILURE, "talloc_zero failed.\n");
+        return NULL;
+    }
+
+    dom->parent = parent;
+
+    /* Sub-domains always have the same view as the parent */
+    dom->has_views = parent->has_views;
+    if (parent->view_name != NULL) {
+        dom->view_name = talloc_strdup(dom, parent->view_name);
+        if (dom->view_name == NULL) {
+            DEBUG(SSSDBG_OP_FAILURE, "Failed to copy parent's view name.\n");
+            goto fail;
+        }
+    }
+
+    dom->name = talloc_strdup(dom, name);
+    if (dom->name == NULL) {
+        DEBUG(SSSDBG_OP_FAILURE, "Failed to copy domain name.\n");
+        goto fail;
+    }
+
+    dom->provider = talloc_strdup(dom, parent->provider);
+    if (dom->provider == NULL) {
+        DEBUG(SSSDBG_OP_FAILURE, "Failed to copy provider name.\n");
+        goto fail;
+    }
+
+    dom->conn_name = talloc_strdup(dom, parent->conn_name);
+    if (dom->conn_name == NULL) {
+        DEBUG(SSSDBG_OP_FAILURE, "Failed to copy connection name.\n");
+        goto fail;
+    }
+
+    if (realm != NULL) {
+        dom->realm = talloc_strdup(dom, realm);
+        if (dom->realm == NULL) {
+            DEBUG(SSSDBG_OP_FAILURE, "Failed to copy realm name.\n");
+            goto fail;
+        }
+    }
+
+    if (flat_name != NULL) {
+        dom->flat_name = talloc_strdup(dom, flat_name);
+        if (dom->flat_name == NULL) {
+            DEBUG(SSSDBG_OP_FAILURE, "Failed to copy flat name.\n");
+            goto fail;
+        }
+    }
+
+    if (id != NULL) {
+        dom->domain_id = talloc_strdup(dom, id);
+        if (dom->domain_id == NULL) {
+            DEBUG(SSSDBG_OP_FAILURE, "Failed to copy id.\n");
+            goto fail;
+        }
+    }
+
+    if (forest != NULL) {
+        dom->forest = talloc_strdup(dom, forest);
+        if (dom->forest == NULL) {
+            DEBUG(SSSDBG_OP_FAILURE, "Failed to copy forest.\n");
+            goto fail;
+        }
+    }
+
+    dom->enumerate = enumerate;
+    dom->fqnames = true;
+    dom->mpg = mpg;
+    dom->trust_direction = trust_direction;
+    /* If the parent domain explicitly limits ID ranges, the subdomain
+     * should honour the limits as well.
+     */
+    dom->id_min = parent->id_min ? parent->id_min : 0;
+    dom->id_max = parent->id_max ? parent->id_max : 0xffffffff;
+    dom->pwd_expiration_warning = parent->pwd_expiration_warning;
+    dom->cache_credentials = parent->cache_credentials;
+    dom->cache_credentials_min_ff_length =
+                                        parent->cache_credentials_min_ff_length;
+    dom->case_sensitive = false;
+    dom->user_timeout = parent->user_timeout;
+    dom->group_timeout = parent->group_timeout;
+    dom->netgroup_timeout = parent->netgroup_timeout;
+    dom->service_timeout = parent->service_timeout;
+    dom->names = parent->names;
+
+    dom->override_homedir = parent->override_homedir;
+    dom->fallback_homedir = parent->fallback_homedir;
+    dom->subdomain_homedir = parent->subdomain_homedir;
+    dom->override_shell = parent->override_shell;
+    dom->default_shell = parent->default_shell;
+    dom->homedir_substr = parent->homedir_substr;
+
+    if (parent->sysdb == NULL) {
+        DEBUG(SSSDBG_OP_FAILURE, "Missing sysdb context in parent domain.\n");
+        goto fail;
+    }
+    dom->sysdb = parent->sysdb;
+
+    return dom;
+
+fail:
+    talloc_free(dom);
+    return NULL;
+}
+
 errno_t sysdb_update_subdomains(struct sss_domain_info *domain)
 {
     int i;
diff --git a/src/tests/cmocka/test_nss_srv.c b/src/tests/cmocka/test_nss_srv.c
index 659cfc68c54f2a1a162970a75f3fbccbeeeb8eaa..23e945950bd6be08056db2477152051d3be9d4bc 100644
--- a/src/tests/cmocka/test_nss_srv.c
+++ b/src/tests/cmocka/test_nss_srv.c
@@ -32,6 +32,7 @@
 #include "responder/nss/nsssrv_private.h"
 #include "sss_client/idmap/sss_nss_idmap.h"
 #include "util/util_sss_idmap.h"
+#include "db/sysdb_private.h"   /* new_subdomain() */
 
 #define TESTS_PATH "tests_nss"
 #define TEST_CONF_DB "test_nss_conf.ldb"
diff --git a/src/util/domain_info_utils.c b/src/util/domain_info_utils.c
index aa79a3a374f2ac8d060b5d81ca093670dbb4e4d3..4eabcff7a0e0af342ec3833d24da26ede0cb5148 100644
--- a/src/util/domain_info_utils.c
+++ b/src/util/domain_info_utils.c
@@ -195,130 +195,6 @@ done:
     return dom;
 }
 
-struct sss_domain_info *new_subdomain(TALLOC_CTX *mem_ctx,
-                                      struct sss_domain_info *parent,
-                                      const char *name,
-                                      const char *realm,
-                                      const char *flat_name,
-                                      const char *id,
-                                      bool mpg,
-                                      bool enumerate,
-                                      const char *forest,
-                                      enum trust_direction trust_direction)
-{
-    struct sss_domain_info *dom;
-
-    DEBUG(SSSDBG_TRACE_FUNC,
-          "Creating [%s] as subdomain of [%s]!\n", name, parent->name);
-
-    dom = talloc_zero(mem_ctx, struct sss_domain_info);
-    if (dom == NULL) {
-        DEBUG(SSSDBG_OP_FAILURE, "talloc_zero failed.\n");
-        return NULL;
-    }
-
-    dom->parent = parent;
-
-    /* Sub-domains always have the same view as the parent */
-    dom->has_views = parent->has_views;
-    if (parent->view_name != NULL) {
-        dom->view_name = talloc_strdup(dom, parent->view_name);
-        if (dom->view_name == NULL) {
-            DEBUG(SSSDBG_OP_FAILURE, "Failed to copy parent's view name.\n");
-            goto fail;
-        }
-    }
-
-    dom->name = talloc_strdup(dom, name);
-    if (dom->name == NULL) {
-        DEBUG(SSSDBG_OP_FAILURE, "Failed to copy domain name.\n");
-        goto fail;
-    }
-
-    dom->provider = talloc_strdup(dom, parent->provider);
-    if (dom->provider == NULL) {
-        DEBUG(SSSDBG_OP_FAILURE, "Failed to copy provider name.\n");
-        goto fail;
-    }
-
-    dom->conn_name = talloc_strdup(dom, parent->conn_name);
-    if (dom->conn_name == NULL) {
-        DEBUG(SSSDBG_OP_FAILURE, "Failed to copy connection name.\n");
-        goto fail;
-    }
-
-    if (realm != NULL) {
-        dom->realm = talloc_strdup(dom, realm);
-        if (dom->realm == NULL) {
-            DEBUG(SSSDBG_OP_FAILURE, "Failed to copy realm name.\n");
-            goto fail;
-        }
-    }
-
-    if (flat_name != NULL) {
-        dom->flat_name = talloc_strdup(dom, flat_name);
-        if (dom->flat_name == NULL) {
-            DEBUG(SSSDBG_OP_FAILURE, "Failed to copy flat name.\n");
-            goto fail;
-        }
-    }
-
-    if (id != NULL) {
-        dom->domain_id = talloc_strdup(dom, id);
-        if (dom->domain_id == NULL) {
-            DEBUG(SSSDBG_OP_FAILURE, "Failed to copy id.\n");
-            goto fail;
-        }
-    }
-
-    if (forest != NULL) {
-        dom->forest = talloc_strdup(dom, forest);
-        if (dom->forest == NULL) {
-            DEBUG(SSSDBG_OP_FAILURE, "Failed to copy forest.\n");
-            goto fail;
-        }
-    }
-
-    dom->enumerate = enumerate;
-    dom->fqnames = true;
-    dom->mpg = mpg;
-    dom->trust_direction = trust_direction;
-    /* If the parent domain explicitly limits ID ranges, the subdomain
-     * should honour the limits as well.
-     */
-    dom->id_min = parent->id_min ? parent->id_min : 0;
-    dom->id_max = parent->id_max ? parent->id_max : 0xffffffff;
-    dom->pwd_expiration_warning = parent->pwd_expiration_warning;
-    dom->cache_credentials = parent->cache_credentials;
-    dom->cache_credentials_min_ff_length =
-                                        parent->cache_credentials_min_ff_length;
-    dom->case_sensitive = false;
-    dom->user_timeout = parent->user_timeout;
-    dom->group_timeout = parent->group_timeout;
-    dom->netgroup_timeout = parent->netgroup_timeout;
-    dom->service_timeout = parent->service_timeout;
-    dom->names = parent->names;
-
-    dom->override_homedir = parent->override_homedir;
-    dom->fallback_homedir = parent->fallback_homedir;
-    dom->subdomain_homedir = parent->subdomain_homedir;
-    dom->override_shell = parent->override_shell;
-    dom->default_shell = parent->default_shell;
-    dom->homedir_substr = parent->homedir_substr;
-
-    if (parent->sysdb == NULL) {
-        DEBUG(SSSDBG_OP_FAILURE, "Missing sysdb context in parent domain.\n");
-        goto fail;
-    }
-    dom->sysdb = parent->sysdb;
-
-    return dom;
-
-fail:
-    talloc_free(dom);
-    return NULL;
-}
-
 errno_t sssd_domain_init(TALLOC_CTX *mem_ctx,
                          struct confdb_ctx *cdb,
                          const char *domain_name,
diff --git a/src/util/util.h b/src/util/util.h
index 5be1c816b7566ff4ae97b5ff61a5824d982475f9..786ed303e1d84dd2783295c51c6126b17c344e5a 100644
--- a/src/util/util.h
+++ b/src/util/util.h
@@ -569,17 +569,6 @@ find_domain_by_object_name(struct sss_domain_info *domain,
 bool subdomain_enumerates(struct sss_domain_info *parent,
                           const char *sd_name);
 
-struct sss_domain_info *new_subdomain(TALLOC_CTX *mem_ctx,
-                                      struct sss_domain_info *parent,
-                                      const char *name,
-                                      const char *realm,
-                                      const char *flat_name,
-                                      const char *id,
-                                      bool mpg,
-                                      bool enumerate,
-                                      const char *forest,
-                                      enum trust_direction trust_direction);
-
 errno_t sssd_domain_init(TALLOC_CTX *mem_ctx,
                          struct confdb_ctx *cdb,
                          const char *domain_name,
-- 
2.1.0

-------------- next part --------------
>From 5a4d29de489e632e37440afea9684db2504b36c5 Mon Sep 17 00:00:00 2001
From: Jakub Hrozek <jhrozek at redhat.com>
Date: Tue, 2 Jun 2015 09:05:13 +0200
Subject: [PATCH 03/29] TESTS: Add a test for sysdb_subdomains.c

The sysdb_subdomains.c module should have its own sysdb test, not share
the generic sysdb one.
---
 Makefile.am                              |  16 +++
 src/tests/cmocka/test_sysdb_subdomains.c | 192 +++++++++++++++++++++++++++++++
 src/tests/sysdb-tests.c                  |  81 -------------
 3 files changed, 208 insertions(+), 81 deletions(-)
 create mode 100644 src/tests/cmocka/test_sysdb_subdomains.c

diff --git a/Makefile.am b/Makefile.am
index 9927391feb7861b596ec2b252f69fe00c4e253cd..4e042922da07c8c8fbc9c8ef1eeae6e510d809fd 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -216,6 +216,7 @@ if HAVE_CMOCKA
         test_sdap_access \
         sdap-tests \
         test_sysdb_views \
+        test_sysdb_subdomains \
         test_sysdb_utils \
         test_be_ptask \
         test_copy_ccache \
@@ -2235,6 +2236,21 @@ test_sysdb_views_LDADD = \
     libsss_test_common.la \
     $(NULL)
 
+test_sysdb_subdomains_SOURCES = \
+    src/tests/cmocka/test_sysdb_subdomains.c \
+    $(NULL)
+test_sysdb_subdomains_CFLAGS = \
+    $(AM_CFLAGS) \
+    $(NULL)
+test_sysdb_subdomains_LDADD = \
+    $(CMOCKA_LIBS) \
+    $(LDB_LIBS) \
+    $(POPT_LIBS) \
+    $(TALLOC_LIBS) \
+    $(SSSD_INTERNAL_LTLIBS) \
+    libsss_test_common.la \
+    $(NULL)
+
 test_sysdb_utils_SOURCES = \
     src/tests/cmocka/test_sysdb_utils.c \
     $(NULL)
diff --git a/src/tests/cmocka/test_sysdb_subdomains.c b/src/tests/cmocka/test_sysdb_subdomains.c
new file mode 100644
index 0000000000000000000000000000000000000000..441537467e98c58051e0cf91c6b1c243d6326ab3
--- /dev/null
+++ b/src/tests/cmocka/test_sysdb_subdomains.c
@@ -0,0 +1,192 @@
+/*
+    SSSD
+
+    sysdb_subdomains - Tests for subdomains and related calls
+
+    Authors:
+        Jakub Hrozek <jhrozek at redhat.com>
+
+    Copyright (C) 2015 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 <stdarg.h>
+#include <stddef.h>
+#include <setjmp.h>
+#include <cmocka.h>
+#include <popt.h>
+
+#include "tests/cmocka/common_mock.h"
+#include "tests/common.h"
+#include "db/sysdb_private.h" /* for sysdb->ldb member */
+
+#define TESTS_PATH "test_sysdb_subdomains"
+#define TEST_CONF_DB "test_sysdb_subdomains.ldb"
+#define TEST_DOM_NAME "test_sysdb_subdomains"
+#define TEST_ID_PROVIDER "local"
+
+struct subdom_test_ctx {
+    struct sss_test_ctx *tctx;
+};
+
+static int test_sysdb_subdom_setup(void **state)
+{
+    struct subdom_test_ctx *test_ctx;
+    struct sss_test_conf_param params[] = {
+        { NULL, NULL },             /* Sentinel */
+    };
+
+    assert_true(leak_check_setup());
+
+    test_ctx = talloc_zero(global_talloc_context,
+                           struct subdom_test_ctx);
+    assert_non_null(test_ctx);
+
+    test_ctx->tctx = create_dom_test_ctx(test_ctx, TESTS_PATH,
+                                         TEST_CONF_DB, TEST_DOM_NAME,
+                                         TEST_ID_PROVIDER, params);
+    assert_non_null(test_ctx->tctx);
+
+    *state = test_ctx;
+    return 0;
+}
+
+static int test_sysdb_subdom_teardown(void **state)
+{
+    struct subdom_test_ctx *test_ctx =
+        talloc_get_type(*state, struct subdom_test_ctx);
+
+    talloc_free(test_ctx);
+    assert_true(leak_check_teardown());
+    return 0;
+}
+
+static void test_sysdb_subdomain_create(void **state)
+{
+    errno_t ret;
+    struct subdom_test_ctx *test_ctx =
+        talloc_get_type(*state, struct subdom_test_ctx);
+
+    const char *const dom1[4] = { "dom1.sub", "DOM1.SUB", "dom1", "S-1" };
+    const char *const dom2[4] = { "dom2.sub", "DOM2.SUB", "dom2", "S-2" };
+
+    ret = sysdb_subdomain_store(test_ctx->tctx->sysdb,
+                                dom1[0], dom1[1], dom1[2], dom1[3],
+                                false, false, NULL,
+                                SD_TRUST_DIRECTION_OUTBOUND);
+    assert_int_equal(ret, EOK);
+
+    ret = sysdb_update_subdomains(test_ctx->tctx->dom);
+    assert_int_equal(ret, EOK);
+
+    assert_non_null(test_ctx->tctx->dom->subdomains);
+    assert_string_equal(test_ctx->tctx->dom->subdomains->name, dom1[0]);
+    assert_int_equal(test_ctx->tctx->dom->subdomains->trust_direction,
+                     SD_TRUST_DIRECTION_OUTBOUND);
+
+    ret = sysdb_subdomain_store(test_ctx->tctx->sysdb,
+                                dom2[0], dom2[1], dom2[2], dom2[3],
+                                false, false, NULL,
+                                SD_TRUST_DIRECTION_TWOWAY);
+    assert_int_equal(ret, EOK);
+
+    ret = sysdb_update_subdomains(test_ctx->tctx->dom);
+    assert_int_equal(ret, EOK);
+
+    assert_non_null(test_ctx->tctx->dom->subdomains->next);
+    assert_string_equal(test_ctx->tctx->dom->subdomains->next->name, dom2[0]);
+    assert_int_equal(test_ctx->tctx->dom->subdomains->next->trust_direction,
+                     SD_TRUST_DIRECTION_TWOWAY);
+
+    /* Reverse the trust directions */
+    ret = sysdb_subdomain_store(test_ctx->tctx->sysdb,
+                                dom1[0], dom1[1], dom1[2], dom1[3],
+                                false, false, NULL,
+                                SD_TRUST_DIRECTION_TWOWAY);
+    assert_int_equal(ret, EOK);
+
+    ret = sysdb_subdomain_store(test_ctx->tctx->sysdb,
+                                dom2[0], dom2[1], dom2[2], dom2[3],
+                                false, false, NULL,
+                                SD_TRUST_DIRECTION_OUTBOUND);
+    assert_int_equal(ret, EOK);
+
+    ret = sysdb_update_subdomains(test_ctx->tctx->dom);
+    assert_int_equal(ret, EOK);
+
+    assert_int_equal(test_ctx->tctx->dom->subdomains->trust_direction,
+                     SD_TRUST_DIRECTION_TWOWAY);
+    assert_int_equal(test_ctx->tctx->dom->subdomains->next->trust_direction,
+                     SD_TRUST_DIRECTION_OUTBOUND);
+
+    ret = sysdb_subdomain_delete(test_ctx->tctx->sysdb, dom2[0]);
+    assert_int_equal(ret, EOK);
+
+    ret = sysdb_subdomain_delete(test_ctx->tctx->sysdb, dom1[0]);
+    assert_int_equal(ret, EOK);
+
+    ret = sysdb_update_subdomains(test_ctx->tctx->dom);
+    assert_int_equal(ret, EOK);
+
+    assert_true(test_ctx->tctx->dom->subdomains->disabled);
+}
+
+int main(int argc, const char *argv[])
+{
+    int rv;
+    int no_cleanup = 0;
+    poptContext pc;
+    int opt;
+    struct poptOption long_options[] = {
+        POPT_AUTOHELP
+        SSSD_DEBUG_OPTS
+        {"no-cleanup", 'n', POPT_ARG_NONE, &no_cleanup, 0,
+         _("Do not delete the test database after a test run"), NULL },
+        POPT_TABLEEND
+    };
+
+    const struct CMUnitTest tests[] = {
+        cmocka_unit_test_setup_teardown(test_sysdb_subdomain_create,
+                                        test_sysdb_subdom_setup,
+                                        test_sysdb_subdom_teardown),
+    };
+
+    /* Set debug level to invalid value so we can deside if -d 0 was used. */
+    debug_level = SSSDBG_INVALID;
+
+    pc = poptGetContext(argv[0], argc, argv, long_options, 0);
+    while((opt = poptGetNextOpt(pc)) != -1) {
+        switch(opt) {
+        default:
+            fprintf(stderr, "\nInvalid option %s: %s\n\n",
+                    poptBadOption(pc, 0), poptStrerror(opt));
+            poptPrintUsage(pc, stderr, 0);
+            return 1;
+        }
+    }
+    poptFreeContext(pc);
+
+    DEBUG_CLI_INIT(debug_level);
+
+    tests_set_cwd();
+    test_dom_suite_cleanup(TESTS_PATH, TEST_CONF_DB, LOCAL_SYSDB_FILE);
+    test_dom_suite_setup(TESTS_PATH);
+    rv = cmocka_run_group_tests(tests, NULL, NULL);
+
+    if (rv == 0 && no_cleanup == 0) {
+        test_dom_suite_cleanup(TESTS_PATH, TEST_CONF_DB, LOCAL_SYSDB_FILE);
+    }
+    return rv;
+}
diff --git a/src/tests/sysdb-tests.c b/src/tests/sysdb-tests.c
index 1dc228df2c4a0c5dbada32ac8bf6efcfbdb080bd..9133eaca24dae7cd7aa9d83726b4836b9c21a7e1 100644
--- a/src/tests/sysdb-tests.c
+++ b/src/tests/sysdb-tests.c
@@ -5224,86 +5224,6 @@ START_TEST(test_sysdb_delete_by_sid)
 }
 END_TEST
 
-START_TEST(test_sysdb_subdomain_create)
-{
-    struct sysdb_test_ctx *test_ctx;
-    errno_t ret;
-    const char *const dom1[4] = { "dom1.sub", "DOM1.SUB", "dom1", "S-1" };
-    const char *const dom2[4] = { "dom2.sub", "DOM2.SUB", "dom2", "S-2" };
-
-    ret = setup_sysdb_tests(&test_ctx);
-    fail_if(ret != EOK, "Could not set up the test");
-
-    ret = sysdb_subdomain_store(test_ctx->sysdb,
-                                dom1[0], dom1[1], dom1[2], dom1[3],
-                                false, false, NULL,
-                                SD_TRUST_DIRECTION_OUTBOUND);
-    fail_if(ret != EOK, "Could not set up the test (dom1)");
-
-    ret = sysdb_update_subdomains(test_ctx->domain);
-    fail_unless(ret == EOK, "sysdb_update_subdomains failed with [%d][%s]",
-                            ret, strerror(ret));
-
-    fail_if(test_ctx->domain->subdomains == NULL, "Empty sub-domain list.");
-    fail_if(strcmp(test_ctx->domain->subdomains->name, dom1[0]) != 0,
-            "Unexpected sub-domain found, expected [%s], got [%s]",
-            dom1[0], test_ctx->domain->subdomains->name);
-    fail_unless(test_ctx->domain->subdomains->trust_direction == \
-                SD_TRUST_DIRECTION_OUTBOUND);
-
-    ret = sysdb_subdomain_store(test_ctx->sysdb,
-                                dom2[0], dom2[1], dom2[2], dom2[3],
-                                false, false, NULL,
-                                SD_TRUST_DIRECTION_TWOWAY);
-    fail_if(ret != EOK, "Could not set up the test (dom2)");
-
-    ret = sysdb_update_subdomains(test_ctx->domain);
-    fail_unless(ret == EOK, "sysdb_update_subdomains failed with [%d][%s]",
-                            ret, strerror(ret));
-
-    fail_if(test_ctx->domain->subdomains->next == NULL, "Missing sub-domain");
-    fail_if(strcmp(test_ctx->domain->subdomains->next->name, dom2[0]) != 0,
-            "Unexpected sub-domain found, expected [%s], got [%s]",
-            dom2[0], test_ctx->domain->subdomains->next->name);
-    fail_unless(test_ctx->domain->subdomains->next->trust_direction == \
-                SD_TRUST_DIRECTION_TWOWAY);
-
-    /* Reverse the trust directions */
-    ret = sysdb_subdomain_store(test_ctx->sysdb,
-                                dom1[0], dom1[1], dom1[2], dom1[3],
-                                false, false, NULL,
-                                SD_TRUST_DIRECTION_TWOWAY);
-    fail_if(ret != EOK, "Could not set up the test (dom1)");
-
-    ret = sysdb_subdomain_store(test_ctx->sysdb,
-                                dom2[0], dom2[1], dom2[2], dom2[3],
-                                false, false, NULL,
-                                SD_TRUST_DIRECTION_OUTBOUND);
-    fail_if(ret != EOK, "Could not set up the test (dom2)");
-
-    ret = sysdb_update_subdomains(test_ctx->domain);
-    fail_unless(ret == EOK, "sysdb_update_subdomains failed with [%d][%s]",
-                            ret, strerror(ret));
-
-    fail_unless(test_ctx->domain->subdomains->trust_direction == \
-                SD_TRUST_DIRECTION_TWOWAY);
-    fail_unless(test_ctx->domain->subdomains->next->trust_direction == \
-                SD_TRUST_DIRECTION_OUTBOUND);
-
-    ret = sysdb_subdomain_delete(test_ctx->sysdb, dom2[0]);
-    fail_if(ret != EOK, "Could not delete subdomain");
-
-    ret = sysdb_subdomain_delete(test_ctx->sysdb, dom1[0]);
-    fail_if(ret != EOK, "Could not delete subdomain");
-
-    ret = sysdb_update_subdomains(test_ctx->domain);
-    fail_unless(ret == EOK, "sysdb_update_subdomains failed with [%d][%s]",
-                            ret, strerror(ret));
-
-    fail_unless(test_ctx->domain->subdomains->disabled, "Subdomain not disabled.");
-}
-END_TEST
-
 const char *const testdom[4] = { "test.sub", "TEST.SUB", "test", "S-3" };
 
 START_TEST(test_sysdb_subdomain_store_user)
@@ -6586,7 +6506,6 @@ Suite *create_sysdb_suite(void)
 
     TCase *tc_subdomain = tcase_create("SYSDB sub-domain Tests");
 
-    tcase_add_test(tc_subdomain, test_sysdb_subdomain_create);
     tcase_add_test(tc_subdomain, test_sysdb_subdomain_store_user);
     tcase_add_test(tc_subdomain, test_sysdb_subdomain_user_ops);
     tcase_add_test(tc_subdomain, test_sysdb_subdomain_group_ops);
-- 
2.1.0

-------------- next part --------------
>From 32f57db9f9060a29a1c1e0540b05ee066229933e Mon Sep 17 00:00:00 2001
From: Jakub Hrozek <jhrozek at redhat.com>
Date: Tue, 2 Jun 2015 13:34:20 +0200
Subject: [PATCH 04/29] SYSDB: Add realm to sysdb_master_domain_add_info

Adding realm to both master domain and subdomain will make it easier to
set and select forest roots. Even master domains can be forest members,
it's preferable to avoid special-casing as much as possible.

Includes a unit test.
---
 src/db/sysdb.h                           |  4 +++-
 src/db/sysdb_subdomains.c                | 22 +++++++++++++++++++-
 src/providers/ad/ad_id.c                 | 11 +++++++++-
 src/providers/ad/ad_subdomains.c         | 10 +++++++++
 src/providers/ipa/ipa_subdomains.c       | 10 ++++++++-
 src/tests/cmocka/test_sysdb_subdomains.c | 35 ++++++++++++++++++++++++++++++++
 6 files changed, 88 insertions(+), 4 deletions(-)

diff --git a/src/db/sysdb.h b/src/db/sysdb.h
index 3d0d07c6a56f2168ffe26f46d17973bc09330aaf..9ee7ff541f731814c204fafc832f39da3ff7af9a 100644
--- a/src/db/sysdb.h
+++ b/src/db/sysdb.h
@@ -439,7 +439,9 @@ errno_t sysdb_update_subdomains(struct sss_domain_info *domain);
 errno_t sysdb_master_domain_update(struct sss_domain_info *domain);
 
 errno_t sysdb_master_domain_add_info(struct sss_domain_info *domain,
-                                     const char *flat, const char *id,
+                                     const char *realm,
+                                     const char *flat,
+                                     const char *id,
                                      const char* forest);
 
 errno_t sysdb_subdomain_delete(struct sysdb_ctx *sysdb, const char *name);
diff --git a/src/db/sysdb_subdomains.c b/src/db/sysdb_subdomains.c
index 816dd5d2c211bc0c3180a1489eab6799e9b70f57..05ec414d93764277bb19045da9fed354b0b2f2b7 100644
--- a/src/db/sysdb_subdomains.c
+++ b/src/db/sysdb_subdomains.c
@@ -552,7 +552,9 @@ done:
 }
 
 errno_t sysdb_master_domain_add_info(struct sss_domain_info *domain,
-                                     const char *flat, const char *id,
+                                     const char *realm,
+                                     const char *flat,
+                                     const char *id,
                                      const char* forest)
 {
     TALLOC_CTX *tmp_ctx;
@@ -632,6 +634,24 @@ errno_t sysdb_master_domain_add_info(struct sss_domain_info *domain,
         do_update = true;
     }
 
+   if (realm != NULL && (domain->realm == NULL ||
+                       strcmp(domain->realm, realm) != 0)) {
+        ret = ldb_msg_add_empty(msg, SYSDB_SUBDOMAIN_REALM,
+                                LDB_FLAG_MOD_REPLACE, NULL);
+        if (ret != LDB_SUCCESS) {
+            ret = sysdb_error_to_errno(ret);
+            goto done;
+        }
+
+        ret = ldb_msg_add_string(msg, SYSDB_SUBDOMAIN_REALM, realm);
+        if (ret != LDB_SUCCESS) {
+            ret = sysdb_error_to_errno(ret);
+            goto done;
+        }
+
+        do_update = true;
+    }
+
     if (do_update == false) {
         ret = EOK;
         goto done;
diff --git a/src/providers/ad/ad_id.c b/src/providers/ad/ad_id.c
index d8ea26875daf1ad38131eef7c1dd9957e776000f..3df91dfd5a7882de8e30d3bbcca7f69cef2c5a17 100644
--- a/src/providers/ad/ad_id.c
+++ b/src/providers/ad/ad_id.c
@@ -454,6 +454,7 @@ struct ad_enumeration_state {
     struct sdap_id_op *sdap_op;
     struct tevent_context *ev;
 
+    const char *realm;
     struct sdap_domain *sdom;
     struct sdap_domain *sditer;
 };
@@ -493,6 +494,14 @@ ad_enumeration_send(TALLOC_CTX *mem_ctx,
     state->sditer = state->sdom;
     state->id_ctx = talloc_get_type(ectx->pvt, struct ad_id_ctx);
 
+    state->realm = dp_opt_get_cstring(state->id_ctx->ad_options->basic,
+                                      AD_KRB5_REALM);
+    if (state->realm == NULL) {
+        DEBUG(SSSDBG_CONF_SETTINGS, "Missing realm or hostname.\n");
+        ret = EINVAL;
+        goto fail;
+    }
+
     state->sdap_op = sdap_id_op_create(state,
                                        state->id_ctx->ldap_ctx->conn_cache);
     if (state->sdap_op == NULL) {
@@ -575,7 +584,7 @@ ad_enumeration_master_done(struct tevent_req *subreq)
         return;
     }
 
-    ret = sysdb_master_domain_add_info(state->sdom->dom,
+    ret = sysdb_master_domain_add_info(state->sdom->dom, state->realm,
                                        flat_name, master_sid, forest);
     if (ret != EOK) {
         DEBUG(SSSDBG_OP_FAILURE, "Cannot save master domain info\n");
diff --git a/src/providers/ad/ad_subdomains.c b/src/providers/ad/ad_subdomains.c
index da496b6dea63cfddf1c6a456a76c9547a0fca116..8e69cef18cd2f64ee3860bcc328b76789312ccad 100644
--- a/src/providers/ad/ad_subdomains.c
+++ b/src/providers/ad/ad_subdomains.c
@@ -595,6 +595,7 @@ static void ad_subdomains_master_dom_done(struct tevent_req *req)
 {
     struct ad_subdomains_req_ctx *ctx;
     errno_t ret;
+    const char *realm;
 
     ctx = tevent_req_callback_data(req, struct ad_subdomains_req_ctx);
 
@@ -607,7 +608,16 @@ static void ad_subdomains_master_dom_done(struct tevent_req *req)
         goto done;
     }
 
+    realm = dp_opt_get_cstring(ctx->sd_ctx->ad_id_ctx->ad_options->basic,
+                               AD_KRB5_REALM);
+    if (realm == NULL) {
+        DEBUG(SSSDBG_CONF_SETTINGS, "Missing realm or hostname.\n");
+        ret = EINVAL;
+        goto done;
+    }
+
     ret = sysdb_master_domain_add_info(ctx->sd_ctx->be_ctx->domain,
+                                       realm,
                                        ctx->flat_name, ctx->master_sid,
                                        ctx->forest);
     if (ret != EOK) {
diff --git a/src/providers/ipa/ipa_subdomains.c b/src/providers/ipa/ipa_subdomains.c
index b82bda23a9b5ae133cb2a6d67aae2f74e5a1f206..7587aabe5a768e1817e5857f7e27fbfe1a8b455b 100644
--- a/src/providers/ipa/ipa_subdomains.c
+++ b/src/providers/ipa/ipa_subdomains.c
@@ -1416,6 +1416,7 @@ static void ipa_subdomains_handler_master_done(struct tevent_req *req)
     if (reply_count) {
         const char *flat = NULL;
         const char *id = NULL;
+        const char *realm;
 
         ret = sysdb_attrs_get_string(reply[0], IPA_FLATNAME, &flat);
         if (ret != EOK) {
@@ -1427,8 +1428,15 @@ static void ipa_subdomains_handler_master_done(struct tevent_req *req)
             goto done;
         }
 
+        realm = dp_opt_get_string(ctx->sd_ctx->id_ctx->ipa_options->basic,
+                                  IPA_KRB5_REALM);
+        if (realm == NULL) {
+            DEBUG(SSSDBG_CRIT_FAILURE, "No Kerberos realm for IPA?\n");
+            goto done;
+        }
+
         ret = sysdb_master_domain_add_info(ctx->sd_ctx->be_ctx->domain,
-                                           flat, id, NULL);
+                                           realm, flat, id, NULL);
     } else {
         ctx->search_base_iter++;
         ret = ipa_subdomains_handler_get(ctx, IPA_SUBDOMAINS_MASTER);
diff --git a/src/tests/cmocka/test_sysdb_subdomains.c b/src/tests/cmocka/test_sysdb_subdomains.c
index 441537467e98c58051e0cf91c6b1c243d6326ab3..6744a4226bcd3bb3b8c8c712830d79d7f43b62ae 100644
--- a/src/tests/cmocka/test_sysdb_subdomains.c
+++ b/src/tests/cmocka/test_sysdb_subdomains.c
@@ -143,6 +143,38 @@ static void test_sysdb_subdomain_create(void **state)
     assert_true(test_ctx->tctx->dom->subdomains->disabled);
 }
 
+static void test_sysdb_master_domain_ops(void **state)
+{
+    errno_t ret;
+    struct subdom_test_ctx *test_ctx =
+        talloc_get_type(*state, struct subdom_test_ctx);
+
+
+    ret = sysdb_master_domain_add_info(test_ctx->tctx->dom,
+                                       "realm1", "flat1", "id1", "forest1");
+    assert_int_equal(ret, EOK);
+
+    ret = sysdb_master_domain_update(test_ctx->tctx->dom);
+    assert_int_equal(ret, EOK);
+
+    assert_string_equal(test_ctx->tctx->dom->realm, "realm1");
+    assert_string_equal(test_ctx->tctx->dom->flat_name, "flat1");
+    assert_string_equal(test_ctx->tctx->dom->domain_id, "id1");
+    assert_string_equal(test_ctx->tctx->dom->forest, "forest1");
+
+    ret = sysdb_master_domain_add_info(test_ctx->tctx->dom,
+                                       "realm2", "flat2", "id2", "forest2");
+    assert_int_equal(ret, EOK);
+
+    ret = sysdb_master_domain_update(test_ctx->tctx->dom);
+    assert_int_equal(ret, EOK);
+
+    assert_string_equal(test_ctx->tctx->dom->realm, "realm2");
+    assert_string_equal(test_ctx->tctx->dom->flat_name, "flat2");
+    assert_string_equal(test_ctx->tctx->dom->domain_id, "id2");
+    assert_string_equal(test_ctx->tctx->dom->forest, "forest2");
+}
+
 int main(int argc, const char *argv[])
 {
     int rv;
@@ -158,6 +190,9 @@ int main(int argc, const char *argv[])
     };
 
     const struct CMUnitTest tests[] = {
+        cmocka_unit_test_setup_teardown(test_sysdb_master_domain_ops,
+                                        test_sysdb_subdom_setup,
+                                        test_sysdb_subdom_teardown),
         cmocka_unit_test_setup_teardown(test_sysdb_subdomain_create,
                                         test_sysdb_subdom_setup,
                                         test_sysdb_subdom_teardown),
-- 
2.1.0

-------------- next part --------------
>From 7c236fa99826519a3a000eb0a84eb748c8c08dc7 Mon Sep 17 00:00:00 2001
From: Jakub Hrozek <jhrozek at redhat.com>
Date: Mon, 1 Jun 2015 21:58:15 +0200
Subject: [PATCH 05/29] SYSDB: Add a forest root attribute to sss_domain_info

Instead of complex forest root search methods, establish forest root
during subdomain list update. The subdomain code can then just use the
forest_root pointer.
---
 src/confdb/confdb.h                      |   7 +-
 src/db/sysdb_subdomains.c                |  59 +++++++++
 src/tests/cmocka/test_sysdb_subdomains.c | 207 ++++++++++++++++++++++++++++++-
 3 files changed, 271 insertions(+), 2 deletions(-)

diff --git a/src/confdb/confdb.h b/src/confdb/confdb.h
index 03dc27195d43ed4758c84eede19941869abad7c7..b5e76f7f624bf8c9398a10a7cd61d58f43ceab06 100644
--- a/src/confdb/confdb.h
+++ b/src/confdb/confdb.h
@@ -265,7 +265,6 @@ struct sss_domain_info {
     char *realm;
     char *flat_name;
     char *domain_id;
-    char *forest;
     enum trust_direction trust_direction;
     struct timeval subdomains_last_checked;
 
@@ -276,6 +275,12 @@ struct sss_domain_info {
     struct sss_domain_info *next;
 
     bool disabled;
+
+    /* Do not use the forest pointer directly in new code, but rather the
+     * forest_root pointer. sss_domain_info will be more opaque in the future
+     */
+    char *forest;
+    struct sss_domain_info *forest_root;
 };
 
 /**
diff --git a/src/db/sysdb_subdomains.c b/src/db/sysdb_subdomains.c
index 05ec414d93764277bb19045da9fed354b0b2f2b7..dd272e81c6a40f75f411295f6766adc41a8ee1be 100644
--- a/src/db/sysdb_subdomains.c
+++ b/src/db/sysdb_subdomains.c
@@ -147,6 +147,63 @@ fail:
     return NULL;
 }
 
+static bool is_forest_root(struct sss_domain_info *d)
+{
+    if (d->forest == NULL) {
+        /* IPA subdomain provider saves/saved trusted forest root domains
+         * without the forest attribute. Those are automatically forest
+         * roots
+         */
+        return true;
+    }
+
+    if (d->realm && (strcmp(d->forest, d->realm) == 0)) {
+        return true;
+    }
+
+    return false;
+}
+
+static bool is_same_forest(struct sss_domain_info *root,
+                           struct sss_domain_info *member)
+{
+    if (member->forest != NULL
+            && root->realm != NULL
+            && strcmp(member->forest, root->realm) == 0) {
+        return true;
+    }
+
+    return false;
+}
+
+static void link_forest_roots(struct sss_domain_info *domain)
+{
+    struct sss_domain_info *d;
+    struct sss_domain_info *dd;
+
+    for (d = domain; d; d = get_next_domain(d, true)) {
+        d->forest_root = NULL;
+    }
+
+    for (d = domain; d; d = get_next_domain(d, true)) {
+        if (d->forest_root != NULL) {
+            continue;
+        }
+
+        if (is_forest_root(d) == true) {
+            for (dd = domain; dd; dd = get_next_domain(dd, true)) {
+                if (dd->forest_root != NULL) {
+                    continue;
+                }
+
+                if (is_same_forest(d, dd) == true) {
+                    dd->forest_root = d;
+                }
+            }
+        }
+    }
+}
+
 errno_t sysdb_update_subdomains(struct sss_domain_info *domain)
 {
     int i;
@@ -357,6 +414,8 @@ errno_t sysdb_update_subdomains(struct sss_domain_info *domain)
         }
     }
 
+    link_forest_roots(domain);
+
     ret = EOK;
 
 done:
diff --git a/src/tests/cmocka/test_sysdb_subdomains.c b/src/tests/cmocka/test_sysdb_subdomains.c
index 6744a4226bcd3bb3b8c8c712830d79d7f43b62ae..b0c86490dad7de610d77ed78d320cc564c426f08 100644
--- a/src/tests/cmocka/test_sysdb_subdomains.c
+++ b/src/tests/cmocka/test_sysdb_subdomains.c
@@ -35,6 +35,10 @@
 #define TESTS_PATH "test_sysdb_subdomains"
 #define TEST_CONF_DB "test_sysdb_subdomains.ldb"
 #define TEST_DOM_NAME "test_sysdb_subdomains"
+#define TEST_FLAT_NAME "TEST"
+#define TEST_SID    "S-1"
+#define TEST_REALM "TEST_SYSDB_SUBDOMAINS"
+#define TEST_FOREST TEST_REALM
 #define TEST_ID_PROVIDER "local"
 
 struct subdom_test_ctx {
@@ -149,7 +153,6 @@ static void test_sysdb_master_domain_ops(void **state)
     struct subdom_test_ctx *test_ctx =
         talloc_get_type(*state, struct subdom_test_ctx);
 
-
     ret = sysdb_master_domain_add_info(test_ctx->tctx->dom,
                                        "realm1", "flat1", "id1", "forest1");
     assert_int_equal(ret, EOK);
@@ -175,6 +178,199 @@ static void test_sysdb_master_domain_ops(void **state)
     assert_string_equal(test_ctx->tctx->dom->forest, "forest2");
 }
 
+/* Parent domain totally separate from subdomains that imitate
+ * IPA domain and two forests
+ */
+static void test_sysdb_link_forest_root_ipa(void **state)
+{
+    errno_t ret;
+    struct subdom_test_ctx *test_ctx =
+        talloc_get_type(*state, struct subdom_test_ctx);
+    struct sss_domain_info *sub;
+    struct sss_domain_info *child;
+
+    /* name, realm, flat, SID, forest */
+    const char *const dom1[5] = { "dom1.sub", "DOM1.SUB",
+                                  "DOM1", "S-1", "DOM1.SUB" };
+    const char *const child_dom1[5] = { "child1.dom1.sub", "CHILD1.DOM1.SUB",
+                                        "CHILD1.DOM1", "S-1-2", "DOM1.SUB" };
+    const char *const dom2[5] = { "dom2.sub", "DOM2.SUB",
+                                  "DOM2", "S-2", "DOM2.SUB" };
+    const char *const child_dom2[5] = { "child2.dom2.sub", "CHILD2.DOM1.SUB",
+                                        "CHILD2.DOM1", "S-2-2", "DOM2.SUB" };
+
+    ret = sysdb_subdomain_store(test_ctx->tctx->sysdb,
+                                dom1[0], dom1[1], dom1[2], dom1[3],
+                                false, false, dom1[4],
+                                SD_TRUST_DIRECTION_TWOWAY);
+    assert_int_equal(ret, EOK);
+
+    ret = sysdb_subdomain_store(test_ctx->tctx->sysdb,
+                                child_dom1[0], child_dom1[1],
+                                child_dom1[2], child_dom1[3],
+                                false, false, child_dom1[4],
+                                SD_TRUST_DIRECTION_TWOWAY);
+    assert_int_equal(ret, EOK);
+
+    ret = sysdb_subdomain_store(test_ctx->tctx->sysdb,
+                                dom2[0], dom2[1], dom2[2], dom2[3],
+                                false, false, dom2[4],
+                                SD_TRUST_DIRECTION_TWOWAY);
+    assert_int_equal(ret, EOK);
+
+    ret = sysdb_subdomain_store(test_ctx->tctx->sysdb,
+                                child_dom2[0], child_dom2[1],
+                                child_dom2[2], child_dom2[3],
+                                false, false, child_dom2[4],
+                                SD_TRUST_DIRECTION_TWOWAY);
+    assert_int_equal(ret, EOK);
+
+    ret = sysdb_update_subdomains(test_ctx->tctx->dom);
+    assert_int_equal(ret, EOK);
+
+    sub = find_domain_by_name(test_ctx->tctx->dom, dom1[0], true);
+    assert_non_null(sub->forest_root);
+    assert_true(sub->forest_root = sub);
+
+    child = find_domain_by_name(test_ctx->tctx->dom, child_dom1[0], true);
+    assert_non_null(child->forest_root);
+    assert_true(child->forest_root = sub);
+
+    sub = find_domain_by_name(test_ctx->tctx->dom, dom2[0], true);
+    assert_non_null(sub->forest_root);
+    assert_true(sub->forest_root = sub);
+
+    child = find_domain_by_name(test_ctx->tctx->dom, child_dom2[0], true);
+    assert_non_null(child->forest_root);
+    assert_true(child->forest_root = sub);
+}
+
+/* Parent domain is an AD forest root and there are two subdomains
+ * child and parallel
+ */
+static void test_sysdb_link_forest_root_ad(void **state)
+{
+    errno_t ret;
+    struct subdom_test_ctx *test_ctx =
+        talloc_get_type(*state, struct subdom_test_ctx);
+    struct sss_domain_info *sub;
+    struct sss_domain_info *child;
+
+    const char *const child_dom[5] = { "child.test_sysdb_subdomains",/* name  */
+                                       "CHILD.TEST_SYSDB_SUBDOMAINS",/* realm */
+                                       "CHILD",                      /* flat  */
+                                       "S-1-2",                      /* sid   */
+                                       TEST_FOREST };               /* forest */
+
+    const char *const sub_dom[5] = { "another.subdomain",         /* name   */
+                                     "ANOTHER.SUBDOMAIN",         /* realm  */
+                                     "ANOTHER",                   /* flat   */
+                                     "S-1-3",                     /* sid    */
+                                     TEST_FOREST };               /* forest */
+
+    ret = sysdb_master_domain_add_info(test_ctx->tctx->dom,
+                                       TEST_REALM,
+                                       TEST_FLAT_NAME,
+                                       TEST_SID,
+                                       TEST_FOREST);
+    assert_int_equal(ret, EOK);
+
+    ret = sysdb_subdomain_store(test_ctx->tctx->sysdb,
+                                child_dom[0], child_dom[1],
+                                child_dom[2], child_dom[3],
+                                false, false, child_dom[4],
+                                SD_TRUST_DIRECTION_TWOWAY);
+    assert_int_equal(ret, EOK);
+
+    ret = sysdb_subdomain_store(test_ctx->tctx->sysdb,
+                                sub_dom[0], sub_dom[1],
+                                sub_dom[2], sub_dom[3],
+                                false, false, sub_dom[4],
+                                SD_TRUST_DIRECTION_TWOWAY);
+    assert_int_equal(ret, EOK);
+
+    ret = sysdb_update_subdomains(test_ctx->tctx->dom);
+    assert_int_equal(ret, EOK);
+
+    assert_non_null(test_ctx->tctx->dom->forest_root);
+    assert_true(test_ctx->tctx->dom->forest_root == test_ctx->tctx->dom);
+
+    child = find_domain_by_name(test_ctx->tctx->dom, child_dom[0], true);
+    assert_non_null(child->forest_root);
+    assert_true(child->forest_root = test_ctx->tctx->dom);
+
+    sub = find_domain_by_name(test_ctx->tctx->dom, sub_dom[0], true);
+    assert_non_null(sub->forest_root);
+    assert_true(sub->forest_root = test_ctx->tctx->dom);
+}
+
+/* Parent domain is an AD member domain connected to a root domain
+ */
+static void test_sysdb_link_forest_member_ad(void **state)
+{
+    errno_t ret;
+    struct subdom_test_ctx *test_ctx =
+        talloc_get_type(*state, struct subdom_test_ctx);
+    struct sss_domain_info *sub;
+    struct sss_domain_info *root;
+
+    const char *const forest_root[5] = { test_ctx->tctx->dom->name, /* name  */
+                                         TEST_FOREST,               /* realm */
+                                         TEST_FLAT_NAME,            /* flat  */
+                                         TEST_SID,                  /* sid   */
+                                         TEST_FOREST };               /* forest */
+
+    const char *const child_dom[5] = { "child.test_sysdb_subdomains",/* name  */
+                                       "CHILD.TEST_SYSDB_SUBDOMAINS",/* realm */
+                                       "CHILD",                      /* flat  */
+                                       "S-1-2",                      /* sid   */
+                                       TEST_FOREST };               /* forest */
+
+    const char *const sub_dom[5] = { "another.subdomain",         /* name   */
+                                     "ANOTHER.SUBDOMAIN",         /* realm  */
+                                     "ANOTHER",                   /* flat   */
+                                     "S-1-3",                     /* sid    */
+                                     TEST_FOREST };               /* forest */
+
+    ret = sysdb_master_domain_add_info(test_ctx->tctx->dom,
+                                       child_dom[1],
+                                       child_dom[2],
+                                       child_dom[3],
+                                       TEST_FOREST);
+    assert_int_equal(ret, EOK);
+
+    ret = sysdb_subdomain_store(test_ctx->tctx->sysdb,
+                                sub_dom[0], sub_dom[1],
+                                sub_dom[2], sub_dom[3],
+                                false, false, sub_dom[4],
+                                SD_TRUST_DIRECTION_TWOWAY);
+    assert_int_equal(ret, EOK);
+
+    ret = sysdb_subdomain_store(test_ctx->tctx->sysdb,
+                                forest_root[0], forest_root[1],
+                                forest_root[2], forest_root[3],
+                                false, false, forest_root[4],
+                                SD_TRUST_DIRECTION_TWOWAY);
+    assert_int_equal(ret, EOK);
+
+    ret = sysdb_master_domain_update(test_ctx->tctx->dom);
+    assert_int_equal(ret, EOK);
+
+    ret = sysdb_update_subdomains(test_ctx->tctx->dom);
+    assert_int_equal(ret, EOK);
+
+    root = find_domain_by_name(test_ctx->tctx->dom, forest_root[0], true);
+    assert_non_null(root->forest_root);
+    assert_true(root->forest_root = root);
+
+    assert_non_null(test_ctx->tctx->dom->forest_root);
+    assert_true(test_ctx->tctx->dom->forest_root == root);
+
+    sub = find_domain_by_name(test_ctx->tctx->dom, sub_dom[0], true);
+    assert_non_null(sub->forest_root);
+    assert_true(sub->forest_root = root);
+}
+
 int main(int argc, const char *argv[])
 {
     int rv;
@@ -196,6 +392,15 @@ int main(int argc, const char *argv[])
         cmocka_unit_test_setup_teardown(test_sysdb_subdomain_create,
                                         test_sysdb_subdom_setup,
                                         test_sysdb_subdom_teardown),
+        cmocka_unit_test_setup_teardown(test_sysdb_link_forest_root_ipa,
+                                        test_sysdb_subdom_setup,
+                                        test_sysdb_subdom_teardown),
+        cmocka_unit_test_setup_teardown(test_sysdb_link_forest_root_ad,
+                                        test_sysdb_subdom_setup,
+                                        test_sysdb_subdom_teardown),
+        cmocka_unit_test_setup_teardown(test_sysdb_link_forest_member_ad,
+                                        test_sysdb_subdom_setup,
+                                        test_sysdb_subdom_teardown),
     };
 
     /* Set debug level to invalid value so we can deside if -d 0 was used. */
-- 
2.1.0

-------------- next part --------------
>From c85ccaaee886963962b6961fbeb5c9df319baa3b Mon Sep 17 00:00:00 2001
From: Jakub Hrozek <jhrozek at redhat.com>
Date: Mon, 25 May 2015 15:26:41 +0200
Subject: [PATCH 06/29] IPA: Add ipa_subdomains_handler_get_{start,cont}
 wrappers

Previously it was error-prone to move code around, because the functions
that started next subdomain request were scattered together with moving
to next base or assigning next base.

This patch creates a wrappers for better readability.
---
 src/providers/ipa/ipa_subdomains.c | 47 +++++++++++++++++++++++++++-----------
 1 file changed, 34 insertions(+), 13 deletions(-)

diff --git a/src/providers/ipa/ipa_subdomains.c b/src/providers/ipa/ipa_subdomains.c
index 7587aabe5a768e1817e5857f7e27fbfe1a8b455b..0b1b194886fcd58afe986dedb9213ca81e597dce 100644
--- a/src/providers/ipa/ipa_subdomains.c
+++ b/src/providers/ipa/ipa_subdomains.c
@@ -827,8 +827,12 @@ struct ipa_subdomains_req_ctx {
 
 static void ipa_subdomains_get_conn_done(struct tevent_req *req);
 static errno_t
-ipa_subdomains_handler_get(struct ipa_subdomains_req_ctx *ctx,
-                           enum ipa_subdomains_req_type type);
+ipa_subdomains_handler_get_start(struct ipa_subdomains_req_ctx *ctx,
+                                 struct sdap_search_base **search_bases,
+                                 enum ipa_subdomains_req_type type);
+static errno_t
+ipa_subdomains_handler_get_cont(struct ipa_subdomains_req_ctx *ctx,
+                                enum ipa_subdomains_req_type type);
 static void ipa_subdomains_handler_done(struct tevent_req *req);
 static void ipa_subdomains_handler_master_done(struct tevent_req *req);
 static void ipa_subdomains_handler_ranges_done(struct tevent_req *req);
@@ -923,7 +927,9 @@ static void ipa_subdomains_get_conn_done(struct tevent_req *req)
         goto fail;
     }
 
-    ret = ipa_subdomains_handler_get(ctx, IPA_SUBDOMAINS_RANGES);
+    ret = ipa_subdomains_handler_get_start(ctx,
+                                           ctx->sd_ctx->ranges_search_bases,
+                                           IPA_SUBDOMAINS_RANGES);
     if (ret != EOK && ret != EAGAIN) {
         goto fail;
     }
@@ -978,6 +984,24 @@ ipa_subdomains_handler_get(struct ipa_subdomains_req_ctx *ctx,
     return EAGAIN;
 }
 
+static errno_t
+ipa_subdomains_handler_get_start(struct ipa_subdomains_req_ctx *ctx,
+                                 struct sdap_search_base **search_bases,
+                                 enum ipa_subdomains_req_type type)
+{
+    ctx->search_base_iter = 0;
+    ctx->search_bases = search_bases;
+    return ipa_subdomains_handler_get(ctx, type);
+}
+
+static errno_t
+ipa_subdomains_handler_get_cont(struct ipa_subdomains_req_ctx *ctx,
+                                enum ipa_subdomains_req_type type)
+{
+    ctx->search_base_iter++;
+    return ipa_subdomains_handler_get(ctx, type);
+}
+
 static void ipa_get_view_name_done(struct tevent_req *req);
 static errno_t ipa_check_master(struct ipa_subdomains_req_ctx *ctx);
 
@@ -1242,8 +1266,7 @@ static void ipa_subdomains_handler_done(struct tevent_req *req)
         ctx->reply_count += reply_count;
     }
 
-    ctx->search_base_iter++;
-    ret = ipa_subdomains_handler_get(ctx, IPA_SUBDOMAINS_SLAVE);
+    ret = ipa_subdomains_handler_get_cont(ctx, IPA_SUBDOMAINS_SLAVE);
     if (ret == EAGAIN) {
         return;
     } else if (ret != EOK) {
@@ -1326,9 +1349,9 @@ static errno_t ipa_check_master(struct ipa_subdomains_req_ctx *ctx)
         domain->domain_id == NULL ||
         domain->realm == NULL) {
 
-        ctx->search_base_iter = 0;
-        ctx->search_bases = ctx->sd_ctx->master_search_bases;
-        ret = ipa_subdomains_handler_get(ctx, IPA_SUBDOMAINS_MASTER);
+        ret = ipa_subdomains_handler_get_start(ctx,
+                                               ctx->sd_ctx->master_search_bases,
+                                               IPA_SUBDOMAINS_MASTER);
         if (ret == EAGAIN) {
             return EAGAIN;
         } else if (ret != EOK) {
@@ -1377,9 +1400,8 @@ static void ipa_subdomains_handler_ranges_done(struct tevent_req *req)
         goto done;
     }
 
-    ctx->search_base_iter = 0;
-    ctx->search_bases = ctx->sd_ctx->search_bases;
-    ret = ipa_subdomains_handler_get(ctx, IPA_SUBDOMAINS_SLAVE);
+    ret = ipa_subdomains_handler_get_start(ctx, ctx->sd_ctx->search_bases,
+                                           IPA_SUBDOMAINS_SLAVE);
     if (ret == EAGAIN) {
         return;
     } else if (ret != EOK) {
@@ -1438,8 +1460,7 @@ static void ipa_subdomains_handler_master_done(struct tevent_req *req)
         ret = sysdb_master_domain_add_info(ctx->sd_ctx->be_ctx->domain,
                                            realm, flat, id, NULL);
     } else {
-        ctx->search_base_iter++;
-        ret = ipa_subdomains_handler_get(ctx, IPA_SUBDOMAINS_MASTER);
+        ret = ipa_subdomains_handler_get_cont(ctx, IPA_SUBDOMAINS_MASTER);
         if (ret == EAGAIN) {
             return;
         } else if (ret != EOK) {
-- 
2.1.0

-------------- next part --------------
>From f1759575b5ccf72e4a3dbaaae31e9aecb2094cb4 Mon Sep 17 00:00:00 2001
From: Jakub Hrozek <jhrozek at redhat.com>
Date: Mon, 25 May 2015 14:14:53 +0200
Subject: [PATCH 07/29] IPA: Check master domain record before subdomain
 records

For one-way trusts we need to know the flat name of the IPA domain when
we process subdomains, hence we need to swap the processing order and
read the master domain record sooner.

Previsouly the order was:
    - ranges
    - subdomains
    - if on client, views
    - master

Now the order is:
    - ranges
    - master
    - subdomains
    - if on client, views
---
 src/providers/ipa/ipa_subdomains.c | 42 +++++++++++++++++++++++++-------------
 1 file changed, 28 insertions(+), 14 deletions(-)

diff --git a/src/providers/ipa/ipa_subdomains.c b/src/providers/ipa/ipa_subdomains.c
index 0b1b194886fcd58afe986dedb9213ca81e597dce..6d69bf82cdc5569d68db929b2e307e0dd19458f8 100644
--- a/src/providers/ipa/ipa_subdomains.c
+++ b/src/providers/ipa/ipa_subdomains.c
@@ -1220,13 +1220,7 @@ static void ipa_get_view_name_done(struct tevent_req *req)
 
     }
 
-    ret = ipa_check_master(ctx);
-    if (ret == EAGAIN) {
-        return;
-    } else if (ret != EOK) {
-        goto done;
-    }
-
+    ret = EOK;
 done:
     if (ret == EOK) {
         dp_error = DP_ERR_OK;
@@ -1319,13 +1313,7 @@ static void ipa_subdomains_handler_done(struct tevent_req *req)
         }
     }
 
-    ret = ipa_check_master(ctx);
-    if (ret == EAGAIN) {
-        return;
-    } else if (ret != EOK) {
-        goto done;
-    }
-
+    ret = EOK;
 done:
     if (ret == EOK) {
         dp_error = DP_ERR_OK;
@@ -1400,6 +1388,17 @@ static void ipa_subdomains_handler_ranges_done(struct tevent_req *req)
         goto done;
     }
 
+    ret = ipa_check_master(ctx);
+    if (ret == EAGAIN) {
+        DEBUG(SSSDBG_TRACE_ALL, "Checking master record..\n");
+        return;
+    } else if (ret != EOK) {
+        DEBUG(SSSDBG_OP_FAILURE, "ipa_check_master failed.\n");
+        goto done;
+    }
+    /* Master domain is up-to-date. Continue checking subdomains */
+
+    DEBUG(SSSDBG_TRACE_ALL, "Master record up2date, checking subdomains\n");
     ret = ipa_subdomains_handler_get_start(ctx, ctx->sd_ctx->search_bases,
                                            IPA_SUBDOMAINS_SLAVE);
     if (ret == EAGAIN) {
@@ -1459,6 +1458,21 @@ static void ipa_subdomains_handler_master_done(struct tevent_req *req)
 
         ret = sysdb_master_domain_add_info(ctx->sd_ctx->be_ctx->domain,
                                            realm, flat, id, NULL);
+        if (ret != EOK) {
+            goto done;
+        }
+
+        /* There is only one master record. Don't bother checking other IPA
+         * search bases; move to checking subdomains instead
+         */
+        ret = ipa_subdomains_handler_get_start(ctx,
+                                               ctx->sd_ctx->search_bases,
+                                               IPA_SUBDOMAINS_SLAVE);
+        if (ret == EAGAIN) {
+            return;
+        }
+
+        /* Either no search bases or an error. End the request in both cases */
     } else {
         ret = ipa_subdomains_handler_get_cont(ctx, IPA_SUBDOMAINS_MASTER);
         if (ret == EAGAIN) {
-- 
2.1.0

-------------- next part --------------
>From e8622316668d956c749e1bbca97c9c7140eacb06 Mon Sep 17 00:00:00 2001
From: Jakub Hrozek <jhrozek at redhat.com>
Date: Tue, 12 May 2015 14:27:35 +0200
Subject: [PATCH 08/29] IPA: Fold ipa_subdom_enumerates into ipa_subdom_store

Reduced code duplication
---
 src/providers/ipa/ipa_subdomains.c | 25 ++++++++++---------------
 1 file changed, 10 insertions(+), 15 deletions(-)

diff --git a/src/providers/ipa/ipa_subdomains.c b/src/providers/ipa/ipa_subdomains.c
index 6d69bf82cdc5569d68db929b2e307e0dd19458f8..ce597502329172b3ec48cfe98166b465670a459d 100644
--- a/src/providers/ipa/ipa_subdomains.c
+++ b/src/providers/ipa/ipa_subdomains.c
@@ -643,8 +643,7 @@ done:
 
 static errno_t ipa_subdom_store(struct sss_domain_info *parent,
                                 struct sdap_idmap_ctx *sdap_idmap_ctx,
-                                struct sysdb_attrs *attrs,
-                                bool enumerate)
+                                struct sysdb_attrs *attrs)
 {
     TALLOC_CTX *tmp_ctx;
     const char *name;
@@ -654,6 +653,7 @@ static errno_t ipa_subdom_store(struct sss_domain_info *parent,
     char *forest = NULL;
     int ret;
     bool mpg;
+    bool enumerate;
 
     tmp_ctx = talloc_new(parent);
     if (tmp_ctx == NULL) {
@@ -692,6 +692,11 @@ static errno_t ipa_subdom_store(struct sss_domain_info *parent,
         goto done;
     }
 
+    ret = ipa_subdom_enumerates(parent, attrs, &enumerate);
+    if (ret != EOK) {
+        goto done;
+    }
+
     ret = sysdb_subdomain_store(parent->sysdb, name, realm, flat,
                                 id, mpg, enumerate, forest,
                                 SD_TRUST_DIRECTION_NOT_SET);
@@ -715,7 +720,6 @@ static errno_t ipa_subdomains_refresh(struct ipa_subdomains_ctx *ctx,
     const char *value;
     int c, h;
     int ret;
-    bool enumerate;
 
     parent = ctx->be_ctx->domain;
     memset(handled, 0, sizeof(bool) * count);
@@ -751,13 +755,8 @@ static errno_t ipa_subdomains_refresh(struct ipa_subdomains_ctx *ctx,
             ipa_ad_subdom_remove(ctx, dom);
         } else {
             /* ok let's try to update it */
-            ret = ipa_subdom_enumerates(parent, reply[c], &enumerate);
-            if (ret != EOK) {
-                goto done;
-            }
-
             ret = ipa_subdom_store(parent, ctx->sdap_id_ctx->opts->idmap_ctx,
-                                   reply[c], enumerate);
+                                   reply[c]);
             if (ret) {
                 /* Nothing we can do about the errorr. Let's at least try
                  * to reuse the existing domain
@@ -783,16 +782,12 @@ static errno_t ipa_subdomains_refresh(struct ipa_subdomains_ctx *ctx,
         if (handled[c]) {
             continue;
         }
+
         /* Nothing we can do about the errorr. Let's at least try
          * to reuse the existing domain.
          */
-        ret = ipa_subdom_enumerates(parent, reply[c], &enumerate);
-        if (ret != EOK) {
-            goto done;
-        }
-
         ret = ipa_subdom_store(parent, ctx->sdap_id_ctx->opts->idmap_ctx,
-                               reply[c], enumerate);
+                               reply[c]);
         if (ret) {
             DEBUG(SSSDBG_MINOR_FAILURE, "Failed to parse subdom data, "
                   "will try to use cached subdomain\n");
-- 
2.1.0

-------------- next part --------------
>From c515af9cfb7cd9409be7ef52e024530b77a8054f Mon Sep 17 00:00:00 2001
From: Jakub Hrozek <jhrozek at redhat.com>
Date: Wed, 13 May 2015 14:17:35 +0200
Subject: [PATCH 09/29] IPA: Also update master domain when initializing subdom
 handler

Updating master domain record from sysdb will ensure the flat name of
the master domain record is up-to-date.
---
 src/providers/ipa/ipa_subdomains.c | 9 +++++++++
 1 file changed, 9 insertions(+)

diff --git a/src/providers/ipa/ipa_subdomains.c b/src/providers/ipa/ipa_subdomains.c
index ce597502329172b3ec48cfe98166b465670a459d..bc5c9abdde63a909dc509d341ba0736b7084666b 100644
--- a/src/providers/ipa/ipa_subdomains.c
+++ b/src/providers/ipa/ipa_subdomains.c
@@ -325,6 +325,9 @@ ipa_subdom_reinit(struct ipa_subdomains_ctx *ctx)
 {
     errno_t ret;
 
+    DEBUG(SSSDBG_TRACE_INTERNAL,
+          "Re-initializing domain %s\n", ctx->be_ctx->domain->name);
+
     ret = sss_write_krb5_conf_snippet(
                               dp_opt_get_string(ctx->id_ctx->ipa_options->basic,
                                                 IPA_KRB5_CONFD_PATH));
@@ -333,6 +336,12 @@ ipa_subdom_reinit(struct ipa_subdomains_ctx *ctx)
         /* Just continue */
     }
 
+    ret = sysdb_master_domain_update(ctx->be_ctx->domain);
+    if (ret != EOK) {
+        DEBUG(SSSDBG_OP_FAILURE, "sysdb_master_domain_update failed.\n");
+        return ret;
+    }
+
     ret = sysdb_update_subdomains(ctx->be_ctx->domain);
     if (ret != EOK) {
         DEBUG(SSSDBG_OP_FAILURE, "sysdb_update_subdomains failed.\n");
-- 
2.1.0

-------------- next part --------------
>From a7df759e48d0a9310614c2c4435955e058ea75a4 Mon Sep 17 00:00:00 2001
From: Jakub Hrozek <jhrozek at redhat.com>
Date: Mon, 25 May 2015 21:59:15 +0200
Subject: [PATCH 10/29] IPA: Move server-mode functions to a separate module

There is already quite a few functions that are server-mode specific and
there will be even more with one-way trusts. Split the server-mode
specific functions into a separate module.
---
 Makefile.am                               |   1 +
 src/providers/ipa/ipa_init.c              |   6 -
 src/providers/ipa/ipa_subdomains.c        | 327 ++----------------------------
 src/providers/ipa/ipa_subdomains.h        |  12 ++
 src/providers/ipa/ipa_subdomains_server.c | 321 +++++++++++++++++++++++++++++
 5 files changed, 349 insertions(+), 318 deletions(-)
 create mode 100644 src/providers/ipa/ipa_subdomains_server.c

diff --git a/Makefile.am b/Makefile.am
index 4e042922da07c8c8fbc9c8ef1eeae6e510d809fd..b8529089694434fcc7b6aa51a23d05c12b51328d 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -2760,6 +2760,7 @@ libsss_ipa_la_SOURCES = \
     src/providers/ipa/ipa_hosts.c \
     src/providers/ipa/ipa_subdomains.c \
     src/providers/ipa/ipa_subdomains_id.c \
+    src/providers/ipa/ipa_subdomains_server.c \
     src/providers/ipa/ipa_subdomains_ext_groups.c \
     src/providers/ipa/ipa_views.c \
     src/providers/ipa/ipa_utils.c \
diff --git a/src/providers/ipa/ipa_init.c b/src/providers/ipa/ipa_init.c
index 15ec2339d95754db2e54f383bf8e423e780e9838..0e16dd97c78a087256fb77be500c9741484867c5 100644
--- a/src/providers/ipa/ipa_init.c
+++ b/src/providers/ipa/ipa_init.c
@@ -722,12 +722,6 @@ int sssm_ipa_subdomains_init(struct be_ctx *bectx,
         return ret;
     }
 
-    ret = ipa_ad_subdom_init(bectx, id_ctx);
-    if (ret != EOK) {
-        DEBUG(SSSDBG_CRIT_FAILURE, "ipa_ad_subdom_init failed.\n");
-        return ret;
-    }
-
     return EOK;
 }
 
diff --git a/src/providers/ipa/ipa_subdomains.c b/src/providers/ipa/ipa_subdomains.c
index bc5c9abdde63a909dc509d341ba0736b7084666b..279638ca6bddb123d523028ade33bcd3ade4fa92 100644
--- a/src/providers/ipa/ipa_subdomains.c
+++ b/src/providers/ipa/ipa_subdomains.c
@@ -106,221 +106,6 @@ struct be_ctx *ipa_get_subdomains_be_ctx(struct be_ctx *be_ctx)
 }
 
 static errno_t
-ipa_ad_ctx_new(struct be_ctx *be_ctx,
-               struct ipa_id_ctx *id_ctx,
-               struct sss_domain_info *subdom,
-               struct ad_id_ctx **_ad_id_ctx)
-{
-    struct ad_options *ad_options;
-    struct ad_id_ctx *ad_id_ctx;
-    const char *gc_service_name;
-    struct ad_srv_plugin_ctx *srv_ctx;
-    char *ad_domain;
-    const char *ad_site_override;
-    struct sdap_domain *sdom;
-    errno_t ret;
-    const char *extra_attrs;
-
-    ad_options = ad_create_default_options(id_ctx, id_ctx->server_mode->realm,
-                                           id_ctx->server_mode->hostname);
-    if (ad_options == NULL) {
-        DEBUG(SSSDBG_OP_FAILURE, "Cannot initialize AD options\n");
-        talloc_free(ad_options);
-        return ENOMEM;
-    }
-
-    ad_domain = subdom->name;
-
-    ret = dp_opt_set_string(ad_options->basic, AD_DOMAIN, ad_domain);
-    if (ret != EOK) {
-        DEBUG(SSSDBG_OP_FAILURE, "Cannot set AD domain\n");
-        talloc_free(ad_options);
-        return ret;
-    }
-
-    ret = dp_opt_set_string(ad_options->basic, AD_KRB5_REALM,
-                            id_ctx->server_mode->realm);
-    if (ret != EOK) {
-        DEBUG(SSSDBG_OP_FAILURE, "Cannot set AD realm\n");
-        talloc_free(ad_options);
-        return ret;
-    }
-
-    extra_attrs = dp_opt_get_string(id_ctx->sdap_id_ctx->opts->basic,
-                            SDAP_USER_EXTRA_ATTRS);
-    if (extra_attrs != NULL) {
-        DEBUG(SSSDBG_TRACE_ALL,
-              "Setting extra attrs for subdomain [%s] to [%s].\n", ad_domain,
-                                                                   extra_attrs);
-
-        ret = dp_opt_set_string(ad_options->id->basic, SDAP_USER_EXTRA_ATTRS,
-                                extra_attrs);
-        if (ret != EOK) {
-            DEBUG(SSSDBG_OP_FAILURE, "dp_opt_get_string failed.\n");
-            talloc_free(ad_options);
-            return ret;
-        }
-
-        ret = sdap_extend_map_with_list(ad_options->id, ad_options->id,
-                                        SDAP_USER_EXTRA_ATTRS,
-                                        ad_options->id->user_map,
-                                        SDAP_OPTS_USER,
-                                        &ad_options->id->user_map,
-                                        &ad_options->id->user_map_cnt);
-        if (ret != EOK) {
-            DEBUG(SSSDBG_OP_FAILURE, "sdap_extend_map_with_list failed.\n");
-            talloc_free(ad_options);
-            return ret;
-        }
-    } else {
-        DEBUG(SSSDBG_TRACE_ALL, "No extra attrs set.\n");
-    }
-
-    gc_service_name = talloc_asprintf(ad_options, "%s%s", "gc_", subdom->name);
-    if (gc_service_name == NULL) {
-        talloc_free(ad_options);
-        return ENOMEM;
-    }
-
-    /* Set KRB5 realm to same as the one of IPA when IPA
-     * is able to attach PAC. For testing, use hardcoded. */
-    ret = ad_failover_init(ad_options, be_ctx, NULL, NULL,
-                           id_ctx->server_mode->realm,
-                           subdom->name, gc_service_name,
-                           subdom->name, &ad_options->service);
-    if (ret != EOK) {
-        DEBUG(SSSDBG_OP_FAILURE, "Cannot initialize AD failover\n");
-        talloc_free(ad_options);
-        return ret;
-    }
-
-    ad_id_ctx = ad_id_ctx_init(ad_options, be_ctx);
-    if (ad_id_ctx == NULL) {
-        talloc_free(ad_options);
-        return ENOMEM;
-    }
-    ad_id_ctx->sdap_id_ctx->opts = ad_options->id;
-    ad_options->id_ctx = ad_id_ctx;
-
-    ad_site_override = dp_opt_get_string(ad_options->basic, AD_SITE);
-
-    /* use AD plugin */
-    srv_ctx = ad_srv_plugin_ctx_init(be_ctx, be_ctx->be_res,
-                                     default_host_dbs,
-                                     ad_id_ctx->ad_options->id,
-                                     id_ctx->server_mode->hostname,
-                                     ad_domain,
-                                     ad_site_override);
-    if (srv_ctx == NULL) {
-        DEBUG(SSSDBG_FATAL_FAILURE, "Out of memory?\n");
-        return ENOMEM;
-    }
-    be_fo_set_srv_lookup_plugin(be_ctx, ad_srv_plugin_send,
-                                ad_srv_plugin_recv, srv_ctx, "AD");
-
-    ret = sdap_domain_subdom_add(ad_id_ctx->sdap_id_ctx,
-                                 ad_id_ctx->sdap_id_ctx->opts->sdom,
-                                 subdom->parent);
-    if (ret != EOK) {
-        DEBUG(SSSDBG_OP_FAILURE, "Cannot initialize sdap domain\n");
-        talloc_free(ad_options);
-        return ret;
-    }
-
-    sdom = sdap_domain_get(ad_id_ctx->sdap_id_ctx->opts, subdom);
-    if (sdom == NULL) {
-        return EFAULT;
-    }
-
-    ret = sdap_id_setup_tasks(be_ctx,
-                              ad_id_ctx->sdap_id_ctx,
-                              sdom,
-                              ldap_enumeration_send,
-                              ldap_enumeration_recv,
-                              ad_id_ctx->sdap_id_ctx);
-    if (ret != EOK) {
-        talloc_free(ad_options);
-        return ret;
-    }
-
-    sdom->pvt = ad_id_ctx;
-
-    /* Set up the ID mapping object */
-    ad_id_ctx->sdap_id_ctx->opts->idmap_ctx =
-        id_ctx->sdap_id_ctx->opts->idmap_ctx;
-
-    *_ad_id_ctx = ad_id_ctx;
-    return EOK;
-}
-
-static errno_t
-ipa_server_trust_add(struct be_ctx *be_ctx,
-                     struct ipa_id_ctx *id_ctx,
-                     struct sss_domain_info *subdom)
-{
-    struct ipa_ad_server_ctx *trust_ctx;
-    struct ad_id_ctx *ad_id_ctx;
-    errno_t ret;
-
-    ret = ipa_ad_ctx_new(be_ctx, id_ctx, subdom, &ad_id_ctx);
-    if (ret != EOK) {
-        DEBUG(SSSDBG_OP_FAILURE,
-              "Cannot create ad_id_ctx for subdomain %s\n", subdom->name);
-        return ret;
-    }
-
-    trust_ctx = talloc(id_ctx->server_mode, struct ipa_ad_server_ctx);
-    if (trust_ctx == NULL) {
-        return ENOMEM;
-    }
-    trust_ctx->dom = subdom;
-    trust_ctx->ad_id_ctx = ad_id_ctx;
-
-    DLIST_ADD(id_ctx->server_mode->trusts, trust_ctx);
-    return EOK;
-}
-
-static errno_t
-ipa_ad_subdom_refresh(struct be_ctx *be_ctx,
-                      struct ipa_id_ctx *id_ctx,
-                      struct sss_domain_info *parent)
-{
-    struct sss_domain_info *dom;
-    struct ipa_ad_server_ctx *trust_iter;
-    errno_t ret;
-
-    if (dp_opt_get_bool(id_ctx->ipa_options->basic,
-                        IPA_SERVER_MODE) == false) {
-        return EOK;
-    }
-
-    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)) {
-
-        /* Check if we already have an ID context for this subdomain */
-        DLIST_FOR_EACH(trust_iter, id_ctx->server_mode->trusts) {
-            if (trust_iter->dom == dom) {
-                break;
-            }
-        }
-
-        /* Newly detected trust */
-        if (trust_iter == NULL) {
-            ret = ipa_server_trust_add(be_ctx, id_ctx, dom);
-            if (ret != EOK) {
-                DEBUG(SSSDBG_OP_FAILURE,
-                      "Cannot create ad_id_ctx for subdomain %s\n",
-                       dom->name);
-                continue;
-            }
-        }
-    }
-
-    return EOK;
-}
-
-static errno_t
 ipa_subdom_reinit(struct ipa_subdomains_ctx *ctx)
 {
     errno_t ret;
@@ -358,41 +143,6 @@ ipa_subdom_reinit(struct ipa_subdomains_ctx *ctx)
     return EOK;
 }
 
-static void
-ipa_ad_subdom_remove(struct ipa_subdomains_ctx *ctx,
-                     struct sss_domain_info *subdom)
-{
-    struct ipa_ad_server_ctx *iter;
-    struct sdap_domain *sdom;
-
-    if (dp_opt_get_bool(ctx->id_ctx->ipa_options->basic,
-                        IPA_SERVER_MODE) == false) {
-        return;
-    }
-
-    DLIST_FOR_EACH(iter, ctx->id_ctx->server_mode->trusts) {
-        if (iter->dom == subdom) break;
-    }
-
-    if (iter == NULL) {
-        DEBUG(SSSDBG_CRIT_FAILURE, "No IPA-AD context for subdomain %s\n",
-              subdom->name);
-        return;
-    }
-
-    sdom = sdap_domain_get(iter->ad_id_ctx->sdap_id_ctx->opts, subdom);
-    if (sdom == NULL) return;
-    be_ptask_destroy(&sdom->enum_task);
-    be_ptask_destroy(&sdom->cleanup_task);
-
-    sdap_domain_remove(iter->ad_id_ctx->sdap_id_ctx->opts, subdom);
-    DLIST_REMOVE(ctx->id_ctx->server_mode->trusts, iter);
-
-    /* terminate all requests for this subdomain so we can free it */
-    be_terminate_domain_requests(ctx->be_ctx, subdom->name);
-    talloc_zfree(sdom);
-}
-
 static errno_t ipa_ranges_parse_results(TALLOC_CTX *mem_ctx,
                                         char *domain_name,
                                         size_t count,
@@ -761,7 +511,7 @@ static errno_t ipa_subdomains_refresh(struct ipa_subdomains_ctx *ctx,
             }
 
             /* Remove the AD ID ctx from the list of LDAP domains */
-            ipa_ad_subdom_remove(ctx, dom);
+            ipa_ad_subdom_remove(ctx->be_ctx, ctx->id_ctx, dom);
         } else {
             /* ok let's try to update it */
             ret = ipa_subdom_store(parent, ctx->sdap_id_ctx->opts->idmap_ctx,
@@ -1285,11 +1035,14 @@ static void ipa_subdomains_handler_done(struct tevent_req *req)
             goto done;
         }
 
-        ret = ipa_ad_subdom_refresh(ctx->sd_ctx->be_ctx, ctx->sd_ctx->id_ctx,
-                                    domain);
-        if (ret != EOK) {
-            DEBUG(SSSDBG_OP_FAILURE, "ipa_ad_subdom_refresh failed.\n");
-            goto done;
+        if (ctx->sd_ctx->id_ctx->server_mode != NULL) {
+            ret = ipa_ad_subdom_refresh(ctx->sd_ctx->be_ctx,
+                                        ctx->sd_ctx->id_ctx,
+                                        domain);
+            if (ret != EOK) {
+                DEBUG(SSSDBG_OP_FAILURE, "ipa_ad_subdom_refresh failed.\n");
+                goto done;
+            }
         }
     }
 
@@ -1711,63 +1464,13 @@ int ipa_subdom_init(struct be_ctx *be_ctx,
               "Users from trusted domains might not be resolved correctly\n");
     }
 
-    return EOK;
-}
-
-int ipa_ad_subdom_init(struct be_ctx *be_ctx,
-                       struct ipa_id_ctx *id_ctx)
-{
-    char *realm;
-    char *hostname;
-    errno_t ret;
-
     if (dp_opt_get_bool(id_ctx->ipa_options->basic,
-                        IPA_SERVER_MODE) == false) {
-        return EOK;
-    }
-
-    /* The IPA code relies on the default FQDN format to unparse user
-     * names. Warn loudly if the full_name_format was customized on the
-     * IPA server
-     */
-    if ((strcmp(be_ctx->domain->names->fq_fmt,
-               CONFDB_DEFAULT_FULL_NAME_FORMAT) != 0)
-            && (strcmp(be_ctx->domain->names->fq_fmt,
-                       CONFDB_DEFAULT_FULL_NAME_FORMAT_INTERNAL) != 0)) {
-        DEBUG(SSSDBG_FATAL_FAILURE, "%s is set to a non-default value [%s] " \
-              "lookups of subdomain users will likely fail!\n",
-              CONFDB_FULL_NAME_FORMAT, be_ctx->domain->names->fq_fmt);
-        sss_log(SSS_LOG_ERR, "%s is set to a non-default value [%s] " \
-                "lookups of subdomain users will likely fail!\n",
-                CONFDB_FULL_NAME_FORMAT, be_ctx->domain->names->fq_fmt);
-        /* Attempt to continue */
-    }
-
-    realm = dp_opt_get_string(id_ctx->ipa_options->basic, IPA_KRB5_REALM);
-    if (realm == NULL) {
-        DEBUG(SSSDBG_CRIT_FAILURE, "No Kerberos realm for IPA?\n");
-        return EINVAL;
-    }
-
-    hostname = dp_opt_get_string(id_ctx->ipa_options->basic, IPA_HOSTNAME);
-    if (hostname == NULL) {
-        DEBUG(SSSDBG_CRIT_FAILURE, "No host name for IPA?\n");
-        return EINVAL;
-    }
-
-    id_ctx->server_mode = talloc_zero(id_ctx, struct ipa_server_mode_ctx);
-    if (id_ctx->server_mode == NULL) {
-        return ENOMEM;
-    }
-    id_ctx->server_mode->realm = realm;
-    id_ctx->server_mode->hostname = hostname;
-    id_ctx->server_mode->trusts = NULL;
-    id_ctx->server_mode->ext_groups = NULL;
-
-    ret = ipa_ad_subdom_refresh(be_ctx, id_ctx, be_ctx->domain);
-    if (ret != EOK) {
-        DEBUG(SSSDBG_OP_FAILURE, "ipa_ad_subdom_refresh failed.\n");
-        return ret;
+                        IPA_SERVER_MODE) == true) {
+        ret = ipa_ad_subdom_init(be_ctx, id_ctx);
+        if (ret != EOK) {
+            DEBUG(SSSDBG_CRIT_FAILURE, "ipa_ad_subdom_init failed.\n");
+            return ret;
+        }
     }
 
     return EOK;
diff --git a/src/providers/ipa/ipa_subdomains.h b/src/providers/ipa/ipa_subdomains.h
index 0cece2cc70a81f81ff143259e620be7e3d143244..d92bd01e8d841af83f92bf18796de51ad323d4f1 100644
--- a/src/providers/ipa/ipa_subdomains.h
+++ b/src/providers/ipa/ipa_subdomains.h
@@ -47,6 +47,18 @@ struct ipa_ad_server_ctx {
     struct ipa_ad_server_ctx *next, *prev;
 };
 
+/* To be used by ipa_subdomains.c only */
+errno_t ipa_ad_subdom_refresh(struct be_ctx *be_ctx,
+                              struct ipa_id_ctx *id_ctx,
+                              struct sss_domain_info *parent);
+
+void ipa_ad_subdom_remove(struct be_ctx *be_ctx,
+                          struct ipa_id_ctx *id_ctx,
+                          struct sss_domain_info *subdom);
+
+int ipa_ad_subdom_init(struct be_ctx *be_ctx,
+                       struct ipa_id_ctx *id_ctx);
+
 /* struct for external group memberships, defined in
  * ipa_subdomains_ext_groups.c */
 struct ipa_ext_groups;
diff --git a/src/providers/ipa/ipa_subdomains_server.c b/src/providers/ipa/ipa_subdomains_server.c
new file mode 100644
index 0000000000000000000000000000000000000000..978608ea4f469623b8309e9f5e4e96661163328b
--- /dev/null
+++ b/src/providers/ipa/ipa_subdomains_server.c
@@ -0,0 +1,321 @@
+/*
+    SSSD
+
+    IPA Subdomains Module - server mode
+
+    Authors:
+        Sumit Bose <sbose at redhat.com>
+
+    Copyright (C) 2015 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 "providers/ldap/sdap_async.h"
+#include "providers/ldap/sdap_idmap.h"
+#include "providers/ipa/ipa_subdomains.h"
+#include "providers/ipa/ipa_common.h"
+#include "providers/ipa/ipa_id.h"
+
+static errno_t
+ipa_ad_ctx_new(struct be_ctx *be_ctx,
+               struct ipa_id_ctx *id_ctx,
+               struct sss_domain_info *subdom,
+               struct ad_id_ctx **_ad_id_ctx)
+{
+    struct ad_options *ad_options;
+    struct ad_id_ctx *ad_id_ctx;
+    const char *gc_service_name;
+    struct ad_srv_plugin_ctx *srv_ctx;
+    char *ad_domain;
+    const char *ad_site_override;
+    struct sdap_domain *sdom;
+    errno_t ret;
+    const char *extra_attrs;
+
+    ad_options = ad_create_default_options(id_ctx, id_ctx->server_mode->realm,
+                                           id_ctx->server_mode->hostname);
+    if (ad_options == NULL) {
+        DEBUG(SSSDBG_OP_FAILURE, "Cannot initialize AD options\n");
+        talloc_free(ad_options);
+        return ENOMEM;
+    }
+
+    ad_domain = subdom->name;
+
+    ret = dp_opt_set_string(ad_options->basic, AD_DOMAIN, ad_domain);
+    if (ret != EOK) {
+        DEBUG(SSSDBG_OP_FAILURE, "Cannot set AD domain\n");
+        talloc_free(ad_options);
+        return ret;
+    }
+
+    ret = dp_opt_set_string(ad_options->basic, AD_KRB5_REALM,
+                            id_ctx->server_mode->realm);
+    if (ret != EOK) {
+        DEBUG(SSSDBG_OP_FAILURE, "Cannot set AD realm\n");
+        talloc_free(ad_options);
+        return ret;
+    }
+
+    extra_attrs = dp_opt_get_string(id_ctx->sdap_id_ctx->opts->basic,
+                            SDAP_USER_EXTRA_ATTRS);
+    if (extra_attrs != NULL) {
+        DEBUG(SSSDBG_TRACE_ALL,
+              "Setting extra attrs for subdomain [%s] to [%s].\n", ad_domain,
+                                                                   extra_attrs);
+
+        ret = dp_opt_set_string(ad_options->id->basic, SDAP_USER_EXTRA_ATTRS,
+                                extra_attrs);
+        if (ret != EOK) {
+            DEBUG(SSSDBG_OP_FAILURE, "dp_opt_get_string failed.\n");
+            talloc_free(ad_options);
+            return ret;
+        }
+
+        ret = sdap_extend_map_with_list(ad_options->id, ad_options->id,
+                                        SDAP_USER_EXTRA_ATTRS,
+                                        ad_options->id->user_map,
+                                        SDAP_OPTS_USER,
+                                        &ad_options->id->user_map,
+                                        &ad_options->id->user_map_cnt);
+        if (ret != EOK) {
+            DEBUG(SSSDBG_OP_FAILURE, "sdap_extend_map_with_list failed.\n");
+            talloc_free(ad_options);
+            return ret;
+        }
+    } else {
+        DEBUG(SSSDBG_TRACE_ALL, "No extra attrs set.\n");
+    }
+
+    gc_service_name = talloc_asprintf(ad_options, "%s%s", "gc_", subdom->name);
+    if (gc_service_name == NULL) {
+        talloc_free(ad_options);
+        return ENOMEM;
+    }
+
+    /* Set KRB5 realm to same as the one of IPA when IPA
+     * is able to attach PAC. For testing, use hardcoded. */
+    ret = ad_failover_init(ad_options, be_ctx, NULL, NULL,
+                           id_ctx->server_mode->realm,
+                           subdom->name, gc_service_name,
+                           subdom->name, &ad_options->service);
+    if (ret != EOK) {
+        DEBUG(SSSDBG_OP_FAILURE, "Cannot initialize AD failover\n");
+        talloc_free(ad_options);
+        return ret;
+    }
+
+    ad_id_ctx = ad_id_ctx_init(ad_options, be_ctx);
+    if (ad_id_ctx == NULL) {
+        talloc_free(ad_options);
+        return ENOMEM;
+    }
+    ad_id_ctx->sdap_id_ctx->opts = ad_options->id;
+    ad_options->id_ctx = ad_id_ctx;
+
+    ad_site_override = dp_opt_get_string(ad_options->basic, AD_SITE);
+
+    /* use AD plugin */
+    srv_ctx = ad_srv_plugin_ctx_init(be_ctx, be_ctx->be_res,
+                                     default_host_dbs,
+                                     ad_id_ctx->ad_options->id,
+                                     id_ctx->server_mode->hostname,
+                                     ad_domain,
+                                     ad_site_override);
+    if (srv_ctx == NULL) {
+        DEBUG(SSSDBG_FATAL_FAILURE, "Out of memory?\n");
+        return ENOMEM;
+    }
+    be_fo_set_srv_lookup_plugin(be_ctx, ad_srv_plugin_send,
+                                ad_srv_plugin_recv, srv_ctx, "AD");
+
+    ret = sdap_domain_subdom_add(ad_id_ctx->sdap_id_ctx,
+                                 ad_id_ctx->sdap_id_ctx->opts->sdom,
+                                 subdom->parent);
+    if (ret != EOK) {
+        DEBUG(SSSDBG_OP_FAILURE, "Cannot initialize sdap domain\n");
+        talloc_free(ad_options);
+        return ret;
+    }
+
+    sdom = sdap_domain_get(ad_id_ctx->sdap_id_ctx->opts, subdom);
+    if (sdom == NULL) {
+        return EFAULT;
+    }
+
+    ret = sdap_id_setup_tasks(be_ctx,
+                              ad_id_ctx->sdap_id_ctx,
+                              sdom,
+                              ldap_enumeration_send,
+                              ldap_enumeration_recv,
+                              ad_id_ctx->sdap_id_ctx);
+    if (ret != EOK) {
+        talloc_free(ad_options);
+        return ret;
+    }
+
+    sdom->pvt = ad_id_ctx;
+
+    /* Set up the ID mapping object */
+    ad_id_ctx->sdap_id_ctx->opts->idmap_ctx =
+        id_ctx->sdap_id_ctx->opts->idmap_ctx;
+
+    *_ad_id_ctx = ad_id_ctx;
+    return EOK;
+}
+
+static errno_t ipa_server_trust_add(struct be_ctx *be_ctx,
+                                    struct ipa_id_ctx *id_ctx,
+                                    struct sss_domain_info *subdom)
+{
+    struct ipa_ad_server_ctx *trust_ctx;
+    struct ad_id_ctx *ad_id_ctx;
+    errno_t ret;
+
+    ret = ipa_ad_ctx_new(be_ctx, id_ctx, subdom, &ad_id_ctx);
+    if (ret != EOK) {
+        DEBUG(SSSDBG_OP_FAILURE,
+              "Cannot create ad_id_ctx for subdomain %s\n", subdom->name);
+        return ret;
+    }
+
+    trust_ctx = talloc(id_ctx->server_mode, struct ipa_ad_server_ctx);
+    if (trust_ctx == NULL) {
+        return ENOMEM;
+    }
+    trust_ctx->dom = subdom;
+    trust_ctx->ad_id_ctx = ad_id_ctx;
+
+    DLIST_ADD(id_ctx->server_mode->trusts, trust_ctx);
+    return EOK;
+}
+
+errno_t ipa_ad_subdom_refresh(struct be_ctx *be_ctx,
+                              struct ipa_id_ctx *id_ctx,
+                              struct sss_domain_info *parent)
+{
+    struct sss_domain_info *dom;
+    struct ipa_ad_server_ctx *trust_iter;
+    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)) {
+
+        /* Check if we already have an ID context for this subdomain */
+        DLIST_FOR_EACH(trust_iter, id_ctx->server_mode->trusts) {
+            if (trust_iter->dom == dom) {
+                break;
+            }
+        }
+
+        /* Newly detected trust */
+        if (trust_iter == NULL) {
+            ret = ipa_server_trust_add(be_ctx, id_ctx, dom);
+            if (ret != EOK) {
+                DEBUG(SSSDBG_OP_FAILURE,
+                      "Cannot create ad_id_ctx for subdomain %s\n",
+                       dom->name);
+                continue;
+            }
+        }
+    }
+
+    return EOK;
+}
+
+void ipa_ad_subdom_remove(struct be_ctx *be_ctx,
+                          struct ipa_id_ctx *id_ctx,
+                          struct sss_domain_info *subdom)
+{
+    struct ipa_ad_server_ctx *iter;
+    struct sdap_domain *sdom;
+
+    DLIST_FOR_EACH(iter, id_ctx->server_mode->trusts) {
+        if (iter->dom == subdom) break;
+    }
+
+    if (iter == NULL) {
+        DEBUG(SSSDBG_CRIT_FAILURE, "No IPA-AD context for subdomain %s\n",
+              subdom->name);
+        return;
+    }
+
+    sdom = sdap_domain_get(iter->ad_id_ctx->sdap_id_ctx->opts, subdom);
+    if (sdom == NULL) return;
+    be_ptask_destroy(&sdom->enum_task);
+    be_ptask_destroy(&sdom->cleanup_task);
+
+    sdap_domain_remove(iter->ad_id_ctx->sdap_id_ctx->opts, subdom);
+    DLIST_REMOVE(id_ctx->server_mode->trusts, iter);
+
+    /* terminate all requests for this subdomain so we can free it */
+    be_terminate_domain_requests(be_ctx, subdom->name);
+    talloc_zfree(sdom);
+}
+
+int ipa_ad_subdom_init(struct be_ctx *be_ctx,
+                       struct ipa_id_ctx *id_ctx)
+{
+    char *realm;
+    char *hostname;
+    errno_t ret;
+
+    /* The IPA code relies on the default FQDN format to unparse user
+     * names. Warn loudly if the full_name_format was customized on the
+     * IPA server
+     */
+    if ((strcmp(be_ctx->domain->names->fq_fmt,
+               CONFDB_DEFAULT_FULL_NAME_FORMAT) != 0)
+            && (strcmp(be_ctx->domain->names->fq_fmt,
+                       CONFDB_DEFAULT_FULL_NAME_FORMAT_INTERNAL) != 0)) {
+        DEBUG(SSSDBG_FATAL_FAILURE, "%s is set to a non-default value [%s] " \
+              "lookups of subdomain users will likely fail!\n",
+              CONFDB_FULL_NAME_FORMAT, be_ctx->domain->names->fq_fmt);
+        sss_log(SSS_LOG_ERR, "%s is set to a non-default value [%s] " \
+                "lookups of subdomain users will likely fail!\n",
+                CONFDB_FULL_NAME_FORMAT, be_ctx->domain->names->fq_fmt);
+        /* Attempt to continue */
+    }
+
+    realm = dp_opt_get_string(id_ctx->ipa_options->basic, IPA_KRB5_REALM);
+    if (realm == NULL) {
+        DEBUG(SSSDBG_CRIT_FAILURE, "No Kerberos realm for IPA?\n");
+        return EINVAL;
+    }
+
+    hostname = dp_opt_get_string(id_ctx->ipa_options->basic, IPA_HOSTNAME);
+    if (hostname == NULL) {
+        DEBUG(SSSDBG_CRIT_FAILURE, "No host name for IPA?\n");
+        return EINVAL;
+    }
+
+    id_ctx->server_mode = talloc_zero(id_ctx, struct ipa_server_mode_ctx);
+    if (id_ctx->server_mode == NULL) {
+        return ENOMEM;
+    }
+    id_ctx->server_mode->realm = realm;
+    id_ctx->server_mode->hostname = hostname;
+    id_ctx->server_mode->trusts = NULL;
+    id_ctx->server_mode->ext_groups = NULL;
+
+    ret = ipa_ad_subdom_refresh(be_ctx, id_ctx, be_ctx->domain);
+    if (ret != EOK) {
+        DEBUG(SSSDBG_OP_FAILURE, "ipa_ad_subdom_refresh failed.\n");
+        return ret;
+    }
+
+    return EOK;
+}
-- 
2.1.0

-------------- next part --------------
>From 4d1d61587cb564541cb9a523bdc4a63d65e429b8 Mon Sep 17 00:00:00 2001
From: Jakub Hrozek <jhrozek at redhat.com>
Date: Tue, 26 May 2015 12:40:33 +0200
Subject: [PATCH 11/29] IPA: Split two functions to new module
 ipa_subdomains_utils.c

These functions will be later reused by the subdomains_server.c module.
Splitting them into a separate subdomains_utils.c module will make sure
there are no cyclic dependencies and the functions are testable in
isolation.
---
 Makefile.am                                  |  18 +++
 src/providers/ipa/ipa_subdomains.c           |  47 +-----
 src/providers/ipa/ipa_subdomains.h           |   6 +
 src/providers/ipa/ipa_subdomains_utils.c     | 100 ++++++++++++
 src/tests/cmocka/test_ipa_subdomains_utils.c | 227 +++++++++++++++++++++++++++
 5 files changed, 356 insertions(+), 42 deletions(-)
 create mode 100644 src/providers/ipa/ipa_subdomains_utils.c
 create mode 100644 src/tests/cmocka/test_ipa_subdomains_utils.c

diff --git a/Makefile.am b/Makefile.am
index b8529089694434fcc7b6aa51a23d05c12b51328d..816f18e4c3ea1d7ef9e0823569ddf3b6ce8fcb58 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -226,6 +226,7 @@ if HAVE_CMOCKA
         test_sbus_opath \
         test_fo_srv \
         pam-srv-tests \
+        test_ipa_subdom_util \
         $(NULL)
 
 if HAVE_LIBRESOLV
@@ -2418,6 +2419,22 @@ test_fo_srv_LDADD = \
     libsss_test_common.la \
     $(NULL)
 
+test_ipa_subdom_util_SOURCES = \
+    src/tests/cmocka/test_ipa_subdomains_utils.c \
+    src/providers/ipa/ipa_subdomains_utils.c \
+    $(NULL)
+test_ipa_subdom_util_CFLAGS = \
+    $(AM_CFLAGS) \
+    $(NULL)
+test_ipa_subdom_util_LDADD = \
+    $(CMOCKA_LIBS) \
+    $(POPT_LIBS) \
+    $(TALLOC_LIBS) \
+    $(LDB_LIBS) \
+    $(SSSD_INTERNAL_LTLIBS) \
+    libsss_test_common.la \
+    $(NULL)
+
 endif # HAVE_CMOCKA
 
 noinst_PROGRAMS = pam_test_client
@@ -2761,6 +2778,7 @@ libsss_ipa_la_SOURCES = \
     src/providers/ipa/ipa_subdomains.c \
     src/providers/ipa/ipa_subdomains_id.c \
     src/providers/ipa/ipa_subdomains_server.c \
+    src/providers/ipa/ipa_subdomains_utils.c \
     src/providers/ipa/ipa_subdomains_ext_groups.c \
     src/providers/ipa/ipa_views.c \
     src/providers/ipa/ipa_utils.c \
diff --git a/src/providers/ipa/ipa_subdomains.c b/src/providers/ipa/ipa_subdomains.c
index 279638ca6bddb123d523028ade33bcd3ade4fa92..be0846d997a435e256fca678b136c3f6834797d3 100644
--- a/src/providers/ipa/ipa_subdomains.c
+++ b/src/providers/ipa/ipa_subdomains.c
@@ -328,56 +328,18 @@ static errno_t ipa_subdom_get_forest(TALLOC_CTX *mem_ctx,
                                      char **_forest)
 {
     int ret;
-    const char *orig_dn;
     struct ldb_dn *dn = NULL;
     const struct ldb_val *val;
     char *forest = NULL;
 
-    ret = sysdb_attrs_get_string(attrs, SYSDB_ORIG_DN, &orig_dn);
-    if (ret) {
-        DEBUG(SSSDBG_OP_FAILURE, "sysdb_attrs_get_string failed.\n");
-        goto done;
-    }
-    DEBUG(SSSDBG_TRACE_ALL, "Checking if we need the forest name for [%s].\n",
-                             orig_dn);
-
-    dn = ldb_dn_new(mem_ctx, ldb_ctx, orig_dn);
+    dn = ipa_subdom_ldb_dn(mem_ctx, ldb_ctx, attrs);
     if (dn == NULL) {
-        DEBUG(SSSDBG_OP_FAILURE, "ldb_dn_new failed.\n");
+        DEBUG(SSSDBG_OP_FAILURE, "ipa_subdom_ldb_dn failed.\n");
+        ret = EIO;
         goto done;
     }
 
-    if (!ldb_dn_validate(dn)) {
-        DEBUG(SSSDBG_OP_FAILURE, "Original DN [%s] is not a valid DN.\n",
-                                  orig_dn);
-        ret = EINVAL;
-        goto done;
-    }
-
-    if (ldb_dn_get_comp_num(dn) < 5) {
-        /* We are only interested in the member domain objects. In IPA the
-         * forest root object is stored as e.g.
-         * cn=AD.DOM,cn=ad,cn=trusts,dc=example,dc=com. Member domains in the
-         * forest are children of the forest root object e.g.
-         * cn=SUB.AD.DOM,cn=AD.DOM,cn=ad,cn=trusts,dc=example,dc=com. Since
-         * the forest name is not stored in the member objects we derive it
-         * from the RDN of the forest root object. */
-        ret = EOK;
-        goto done;
-    }
-
-    val = ldb_dn_get_component_val(dn, 3);
-    if (strncasecmp("trusts", (const char *) val->data, val->length) != 0) {
-        DEBUG(SSSDBG_TRACE_FUNC,
-              "4th component is not 'trust', nothing to do.\n");
-        ret = EOK;
-        goto done;
-    }
-
-    val = ldb_dn_get_component_val(dn, 2);
-    if (strncasecmp("ad", (const char *) val->data, val->length) != 0) {
-        DEBUG(SSSDBG_TRACE_FUNC,
-              "3rd component is not 'ad', nothing to do.\n");
+    if (ipa_subdom_is_member_dom(dn) == false) {
         ret = EOK;
         goto done;
     }
@@ -390,6 +352,7 @@ static errno_t ipa_subdom_get_forest(TALLOC_CTX *mem_ctx,
         goto done;
     }
 
+    ret = EOK;
 done:
     talloc_free(dn);
 
diff --git a/src/providers/ipa/ipa_subdomains.h b/src/providers/ipa/ipa_subdomains.h
index d92bd01e8d841af83f92bf18796de51ad323d4f1..94027c091b547fdfdb7a5cdda49e8ae9778f24a2 100644
--- a/src/providers/ipa/ipa_subdomains.h
+++ b/src/providers/ipa/ipa_subdomains.h
@@ -59,6 +59,12 @@ void ipa_ad_subdom_remove(struct be_ctx *be_ctx,
 int ipa_ad_subdom_init(struct be_ctx *be_ctx,
                        struct ipa_id_ctx *id_ctx);
 
+struct ldb_dn *ipa_subdom_ldb_dn(TALLOC_CTX *mem_ctx,
+                                 struct ldb_context *ldb_ctx,
+                                 struct sysdb_attrs *attrs);
+
+bool ipa_subdom_is_member_dom(struct ldb_dn *dn);
+
 /* struct for external group memberships, defined in
  * ipa_subdomains_ext_groups.c */
 struct ipa_ext_groups;
diff --git a/src/providers/ipa/ipa_subdomains_utils.c b/src/providers/ipa/ipa_subdomains_utils.c
new file mode 100644
index 0000000000000000000000000000000000000000..27fc0a4d1edda13d855deddf6dde242a3ef92f5a
--- /dev/null
+++ b/src/providers/ipa/ipa_subdomains_utils.c
@@ -0,0 +1,100 @@
+/*
+    SSSD
+
+    IPA Subdomains Module - utilities
+
+    Authors:
+        Sumit Bose <sbose at redhat.com>
+
+    Copyright (C) 2015 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 "providers/ipa/ipa_subdomains.h"
+#include "providers/ipa/ipa_common.h"
+#include "providers/ipa/ipa_id.h"
+
+struct ldb_dn *ipa_subdom_ldb_dn(TALLOC_CTX *mem_ctx,
+                                 struct ldb_context *ldb_ctx,
+                                 struct sysdb_attrs *attrs)
+{
+    int ret;
+    const char *orig_dn;
+    struct ldb_dn *dn = NULL;
+
+    if (attrs == NULL || ldb_ctx == NULL) {
+        return NULL;
+    }
+
+    ret = sysdb_attrs_get_string(attrs, SYSDB_ORIG_DN, &orig_dn);
+    if (ret) {
+        DEBUG(SSSDBG_OP_FAILURE, "sysdb_attrs_get_string failed: %d\n", ret);
+        return NULL;
+    }
+
+    dn = ldb_dn_new(mem_ctx, ldb_ctx, orig_dn);
+    if (dn == NULL) {
+        DEBUG(SSSDBG_OP_FAILURE, "ldb_dn_new failed.\n");
+        return NULL;
+    }
+
+    if (!ldb_dn_validate(dn)) {
+        DEBUG(SSSDBG_OP_FAILURE, "Original DN [%s] is not a valid DN.\n",
+                                  orig_dn);
+        talloc_free(dn);
+        return NULL;
+    }
+
+    return dn;
+}
+
+bool ipa_subdom_is_member_dom(struct ldb_dn *dn)
+{
+    const struct ldb_val *val;
+
+    if (dn == NULL) {
+        DEBUG(SSSDBG_CRIT_FAILURE, "Wrong input!\n");
+        return false;
+    }
+
+    if (ldb_dn_get_comp_num(dn) < 5) {
+        /* We are only interested in the member domain objects. In IPA the
+         * forest root object is stored as e.g.
+         * cn=AD.DOM,cn=ad,cn=trusts,dc=example,dc=com. Member domains in the
+         * forest are children of the forest root object e.g.
+         * cn=SUB.AD.DOM,cn=AD.DOM,cn=ad,cn=trusts,dc=example,dc=com. Since
+         * the forest name is not stored in the member objects we derive it
+         * from the RDN of the forest root object. */
+        DEBUG(SSSDBG_TRACE_FUNC,
+              "DN too short, not a member domain\n");
+        return false;
+    }
+
+    val = ldb_dn_get_component_val(dn, 3);
+    if (strncasecmp("trusts", (const char *) val->data, val->length) != 0) {
+        DEBUG(SSSDBG_TRACE_FUNC,
+              "4th component is not 'trust', not a member domain\n");
+        return false;
+    }
+
+    val = ldb_dn_get_component_val(dn, 2);
+    if (strncasecmp("ad", (const char *) val->data, val->length) != 0) {
+        DEBUG(SSSDBG_TRACE_FUNC,
+              "3rd component is not 'ad', not a member domain\n");
+        return false;
+    }
+
+    return true;
+}
diff --git a/src/tests/cmocka/test_ipa_subdomains_utils.c b/src/tests/cmocka/test_ipa_subdomains_utils.c
new file mode 100644
index 0000000000000000000000000000000000000000..acf8c19a2cd28990fca5e6d9b57930d86508c83e
--- /dev/null
+++ b/src/tests/cmocka/test_ipa_subdomains_utils.c
@@ -0,0 +1,227 @@
+/*
+    Authors:
+        Jakub Hrozek <jhrozek at redhat.com>
+
+    Copyright (C) 2015 Red Hat
+
+    SSSD tests: IPA subdomain util tests
+
+    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 <talloc.h>
+#include <tevent.h>
+#include <errno.h>
+#include <popt.h>
+#include <arpa/inet.h>
+#include <netinet/in.h>
+#include <sys/types.h>
+#include <stdarg.h>
+#include <stdlib.h>
+
+#include "providers/ipa/ipa_subdomains.h"
+#include "tests/cmocka/common_mock.h"
+#include "tests/cmocka/common_mock_resp.h"
+
+struct test_ipa_subdom_ctx {
+    struct ldb_context *ldb;
+};
+
+static int test_ipa_subdom_setup(void **state)
+{
+    struct test_ipa_subdom_ctx *test_ctx;
+
+    assert_true(leak_check_setup());
+
+    test_ctx = talloc_zero(global_talloc_context, struct test_ipa_subdom_ctx);
+    assert_non_null(test_ctx);
+
+    test_ctx->ldb = ldb_init(test_ctx, NULL);
+    assert_non_null(test_ctx->ldb);
+
+    check_leaks_push(test_ctx);
+    *state = test_ctx;
+    return 0;
+}
+
+static int test_ipa_subdom_teardown(void **state)
+{
+    struct test_ipa_subdom_ctx *test_ctx;
+
+    test_ctx = talloc_get_type(*state, struct test_ipa_subdom_ctx);
+    assert_non_null(test_ctx);
+
+    assert_true(check_leaks_pop(test_ctx) == true);
+    talloc_free(test_ctx);
+    assert_true(leak_check_teardown());
+    return 0;
+}
+
+static struct sysdb_attrs *dn_attrs(TALLOC_CTX *mem_ctx, const char *dn)
+{
+    struct sysdb_attrs *attrs;
+    int rv;
+
+    attrs = sysdb_new_attrs(mem_ctx);
+    assert_non_null(attrs);
+
+    rv = sysdb_attrs_add_string(attrs, SYSDB_ORIG_DN, dn);
+    assert_int_equal(rv, EOK);
+
+    return attrs;
+}
+
+static void test_ipa_subdom_ldb_dn(void **state)
+{
+    struct ldb_dn *dn;
+    struct sysdb_attrs *attrs;
+    struct test_ipa_subdom_ctx *test_ctx;
+
+    test_ctx = talloc_get_type(*state, struct test_ipa_subdom_ctx);
+    assert_non_null(test_ctx);
+
+    attrs = dn_attrs(test_ctx, "dc=foo,dc=bar");
+    assert_non_null(attrs);
+
+    dn = ipa_subdom_ldb_dn(test_ctx, test_ctx->ldb, attrs);
+    assert_non_null(dn);
+    assert_string_equal(ldb_dn_get_linearized(dn), "dc=foo,dc=bar");
+
+    talloc_free(dn);
+    talloc_free(attrs);
+}
+
+static void test_ipa_subdom_ldb_dn_fail(void **state)
+{
+    struct ldb_dn *dn;
+    struct sysdb_attrs *attrs;
+    struct test_ipa_subdom_ctx *test_ctx;
+
+    test_ctx = talloc_get_type(*state, struct test_ipa_subdom_ctx);
+    assert_non_null(test_ctx);
+
+    attrs = dn_attrs(test_ctx, "notadn");
+    assert_non_null(attrs);
+
+    dn = ipa_subdom_ldb_dn(test_ctx, NULL, NULL);
+    assert_null(dn);
+
+    dn = ipa_subdom_ldb_dn(test_ctx, test_ctx->ldb, attrs);
+    assert_null(dn);
+    talloc_free(attrs);
+
+    attrs = sysdb_new_attrs(test_ctx);
+    assert_non_null(attrs);
+    dn = ipa_subdom_ldb_dn(test_ctx, test_ctx->ldb, attrs);
+    assert_null(dn);
+    talloc_free(attrs);
+}
+
+static struct ldb_dn *get_dn(TALLOC_CTX *mem_ctx,
+                             struct ldb_context *ldb,
+                             const char *strdn)
+{
+    struct ldb_dn *dn;
+    struct sysdb_attrs *attrs;
+
+    attrs = dn_attrs(mem_ctx, strdn);
+    assert_non_null(attrs);
+
+    dn = ipa_subdom_ldb_dn(mem_ctx, ldb, attrs);
+    talloc_free(attrs);
+    assert_non_null(dn);
+
+    return dn;
+}
+
+static void test_ipa_subdom_is_member_dom(void **state)
+{
+    struct ldb_dn *dn;
+    struct test_ipa_subdom_ctx *test_ctx;
+    bool is_member;
+
+    test_ctx = talloc_get_type(*state, struct test_ipa_subdom_ctx);
+
+    dn = get_dn(test_ctx, test_ctx->ldb,
+                "cn=SUB.AD.DOM,cn=AD.DOM,cn=ad,cn=trusts,dc=example,dc=com");
+    is_member = ipa_subdom_is_member_dom(dn);
+    talloc_free(dn);
+    assert_true(is_member);
+
+    dn = get_dn(test_ctx, test_ctx->ldb,
+                "cn=AD.DOM,cn=ad,cn=trusts,dc=example,dc=com");
+    is_member = ipa_subdom_is_member_dom(dn);
+    talloc_free(dn);
+    assert_false(is_member);
+
+    dn = get_dn(test_ctx, test_ctx->ldb,
+                "cn=SUB.AD.DOM,cn=AD.DOM,cn=ad,cn=XXX,dc=example,dc=com");
+    is_member = ipa_subdom_is_member_dom(dn);
+    talloc_free(dn);
+    assert_false(is_member);
+
+    dn = get_dn(test_ctx, test_ctx->ldb,
+                "cn=SUB.AD.DOM,cn=AD.DOM,cn=YYY,cn=trusts,dc=example,dc=com");
+    is_member = ipa_subdom_is_member_dom(dn);
+    talloc_free(dn);
+    assert_false(is_member);
+}
+
+int main(int argc, const char *argv[])
+{
+    int rv;
+    poptContext pc;
+    int opt;
+    struct poptOption long_options[] = {
+        POPT_AUTOHELP
+        SSSD_DEBUG_OPTS
+        POPT_TABLEEND
+    };
+
+    const struct CMUnitTest tests[] = {
+        cmocka_unit_test_setup_teardown(test_ipa_subdom_ldb_dn,
+                                        test_ipa_subdom_setup,
+                                        test_ipa_subdom_teardown),
+        cmocka_unit_test_setup_teardown(test_ipa_subdom_ldb_dn_fail,
+                                        test_ipa_subdom_setup,
+                                        test_ipa_subdom_teardown),
+        cmocka_unit_test_setup_teardown(test_ipa_subdom_is_member_dom,
+                                        test_ipa_subdom_setup,
+                                        test_ipa_subdom_teardown),
+    };
+
+    /* Set debug level to invalid value so we can deside if -d 0 was used. */
+    debug_level = SSSDBG_INVALID;
+
+    pc = poptGetContext(argv[0], argc, argv, long_options, 0);
+    while((opt = poptGetNextOpt(pc)) != -1) {
+        switch(opt) {
+        default:
+            fprintf(stderr, "\nInvalid option %s: %s\n\n",
+                    poptBadOption(pc, 0), poptStrerror(opt));
+            poptPrintUsage(pc, stderr, 0);
+            return 1;
+        }
+    }
+    poptFreeContext(pc);
+
+    DEBUG_CLI_INIT(debug_level);
+
+    /* Even though normally the tests should clean up after themselves
+     * they might not after a failed run. Remove the old db to be sure */
+    tests_set_cwd();
+
+    rv = cmocka_run_group_tests(tests, NULL, NULL);
+    return rv;
+}
-- 
2.1.0

-------------- next part --------------
>From 404e07bcf23707599d5d921e91af551f6f6e130f Mon Sep 17 00:00:00 2001
From: Jakub Hrozek <jhrozek at redhat.com>
Date: Tue, 26 May 2015 12:41:36 +0200
Subject: [PATCH 12/29] IPA: Include ipaNTTrustDirection in the attribute set
 for trusted domains

Allows to distinguish the trust directions for trusted domains. For
domains where we don't know the direction in server mode, we assume
two-way trusts.

Member domains do not have the direction, but rather the forest root
direction is used.
---
 src/providers/ipa/ipa_subdomains.c        | 76 +++++++++++++++++++++---------
 src/providers/ipa/ipa_subdomains.h        |  9 ++++
 src/providers/ipa/ipa_subdomains_server.c | 77 +++++++++++++++++++++++++++++++
 src/util/util_errors.c                    |  1 +
 src/util/util_errors.h                    |  1 +
 5 files changed, 143 insertions(+), 21 deletions(-)

diff --git a/src/providers/ipa/ipa_subdomains.c b/src/providers/ipa/ipa_subdomains.c
index be0846d997a435e256fca678b136c3f6834797d3..e897132d5ce9e20d62ca6d0f72f8ac0a010a6dc0 100644
--- a/src/providers/ipa/ipa_subdomains.c
+++ b/src/providers/ipa/ipa_subdomains.c
@@ -363,7 +363,24 @@ done:
     return ret;
 }
 
+static errno_t ipa_get_sd_trust_direction(struct sysdb_attrs *sd,
+                                          struct ipa_id_ctx *id_ctx,
+                                          struct ldb_context *ldb_ctx,
+                                          enum trust_direction *_direction)
+{
+    if (id_ctx->server_mode != NULL) {
+        return ipa_server_get_trust_direction(sd, ldb_ctx, _direction);
+    } else {
+        /* Clients do not have access to the trust objects's trust direction
+         * and don't generally care
+         */
+        *_direction = SD_TRUST_DIRECTION_NOT_SET;
+        return EOK;
+    }
+}
+
 static errno_t ipa_subdom_store(struct sss_domain_info *parent,
+                                struct ipa_id_ctx *id_ctx,
                                 struct sdap_idmap_ctx *sdap_idmap_ctx,
                                 struct sysdb_attrs *attrs)
 {
@@ -376,6 +393,7 @@ static errno_t ipa_subdom_store(struct sss_domain_info *parent,
     int ret;
     bool mpg;
     bool enumerate;
+    enum trust_direction direction;
 
     tmp_ctx = talloc_new(parent);
     if (tmp_ctx == NULL) {
@@ -419,9 +437,20 @@ static errno_t ipa_subdom_store(struct sss_domain_info *parent,
         goto done;
     }
 
+    ret = ipa_get_sd_trust_direction(attrs, id_ctx,
+                                     sysdb_ctx_get_ldb(parent->sysdb),
+                                     &direction);
+    if (ret != EOK) {
+        DEBUG(SSSDBG_OP_FAILURE,
+              "ipa_get_sd_trust_direction failed: %d\n", ret);
+        goto done;
+    }
+
+    DEBUG(SSSDBG_FUNC_DATA,
+          "Trust direction of %s is %s\n", name, ipa_trust_dir2str(direction));
     ret = sysdb_subdomain_store(parent->sysdb, name, realm, flat,
                                 id, mpg, enumerate, forest,
-                                SD_TRUST_DIRECTION_NOT_SET);
+                                direction);
     if (ret) {
         DEBUG(SSSDBG_OP_FAILURE, "sysdb_subdomain_store failed.\n");
         goto done;
@@ -433,6 +462,23 @@ done:
     return ret;
 }
 
+static void ipa_subdom_store_step(struct sss_domain_info *parent,
+                                  struct ipa_id_ctx *id_ctx,
+                                  struct sdap_idmap_ctx *sdap_idmap_ctx,
+                                  struct sysdb_attrs *attrs)
+{
+    int ret;
+
+    ret = ipa_subdom_store(parent, id_ctx, sdap_idmap_ctx, attrs);
+    if (ret == ERR_TRUST_NOT_SUPPORTED) {
+        DEBUG(SSSDBG_MINOR_FAILURE, "Unsupported trust type, skipping\n");
+    } else if (ret) {
+        /* Nothing we can do about the error. */
+        DEBUG(SSSDBG_MINOR_FAILURE, "Failed to parse subdom data, "
+              "will try to use cached subdomain\n");
+    }
+}
+
 static errno_t ipa_subdomains_refresh(struct ipa_subdomains_ctx *ctx,
                                       int count, struct sysdb_attrs **reply,
                                       bool *changes)
@@ -477,15 +523,9 @@ static errno_t ipa_subdomains_refresh(struct ipa_subdomains_ctx *ctx,
             ipa_ad_subdom_remove(ctx->be_ctx, ctx->id_ctx, dom);
         } else {
             /* ok let's try to update it */
-            ret = ipa_subdom_store(parent, ctx->sdap_id_ctx->opts->idmap_ctx,
-                                   reply[c]);
-            if (ret) {
-                /* Nothing we can do about the errorr. Let's at least try
-                 * to reuse the existing domain
-                 */
-                DEBUG(SSSDBG_MINOR_FAILURE, "Failed to parse subdom data, "
-                      "will try to use cached subdomain\n");
-            }
+            ipa_subdom_store_step(parent, ctx->id_ctx,
+                                  ctx->sdap_id_ctx->opts->idmap_ctx,
+                                  reply[c]);
             handled[c] = true;
             h++;
         }
@@ -505,19 +545,12 @@ static errno_t ipa_subdomains_refresh(struct ipa_subdomains_ctx *ctx,
             continue;
         }
 
-        /* Nothing we can do about the errorr. Let's at least try
-         * to reuse the existing domain.
-         */
-        ret = ipa_subdom_store(parent, ctx->sdap_id_ctx->opts->idmap_ctx,
-                               reply[c]);
-        if (ret) {
-            DEBUG(SSSDBG_MINOR_FAILURE, "Failed to parse subdom data, "
-                  "will try to use cached subdomain\n");
-        }
+        ipa_subdom_store_step(parent, ctx->id_ctx,
+                              ctx->sdap_id_ctx->opts->idmap_ctx,
+                              reply[c]);
     }
 
     ret = EOK;
-
 done:
     if (ret != EOK) {
         ctx->last_refreshed = 0;
@@ -561,7 +594,8 @@ static struct ipa_subdomains_req_params subdomain_requests[] = {
     },
     { SUBDOMAINS_FILTER,
       ipa_subdomains_handler_done,
-      { IPA_CN, IPA_FLATNAME, IPA_TRUSTED_DOMAIN_SID, NULL }
+      { IPA_CN, IPA_FLATNAME, IPA_TRUSTED_DOMAIN_SID,
+        IPA_TRUST_DIRECTION, NULL }
     },
     { RANGE_FILTER,
       ipa_subdomains_handler_ranges_done,
diff --git a/src/providers/ipa/ipa_subdomains.h b/src/providers/ipa/ipa_subdomains.h
index 94027c091b547fdfdb7a5cdda49e8ae9778f24a2..76e4b3802284719cd4fb6f731f7f92fa877d8ba3 100644
--- a/src/providers/ipa/ipa_subdomains.h
+++ b/src/providers/ipa/ipa_subdomains.h
@@ -59,6 +59,15 @@ void ipa_ad_subdom_remove(struct be_ctx *be_ctx,
 int ipa_ad_subdom_init(struct be_ctx *be_ctx,
                        struct ipa_id_ctx *id_ctx);
 
+errno_t ipa_server_get_trust_direction(struct sysdb_attrs *sd,
+                                       struct ldb_context *ldb_ctx,
+                                       enum trust_direction *_direction);
+
+const char *ipa_trust_dir2str(enum trust_direction direction);
+
+/* Utilities */
+#define IPA_TRUST_DIRECTION "ipaNTTrustDirection"
+
 struct ldb_dn *ipa_subdom_ldb_dn(TALLOC_CTX *mem_ctx,
                                  struct ldb_context *ldb_ctx,
                                  struct sysdb_attrs *attrs);
diff --git a/src/providers/ipa/ipa_subdomains_server.c b/src/providers/ipa/ipa_subdomains_server.c
index 978608ea4f469623b8309e9f5e4e96661163328b..9594735dfabd5165ed1fb514d7fa028df5748670 100644
--- a/src/providers/ipa/ipa_subdomains_server.c
+++ b/src/providers/ipa/ipa_subdomains_server.c
@@ -28,6 +28,83 @@
 #include "providers/ipa/ipa_common.h"
 #include "providers/ipa/ipa_id.h"
 
+#define LSA_TRUST_DIRECTION_INBOUND  0x00000001
+#define LSA_TRUST_DIRECTION_OUTBOUND 0x00000002
+
+static enum trust_direction default_direction(TALLOC_CTX *mem_ctx,
+                                              struct ldb_context *ldb_ctx,
+                                              struct sysdb_attrs *attrs)
+{
+    struct ldb_dn *dn = NULL;
+    enum trust_direction direction;
+
+    dn = ipa_subdom_ldb_dn(mem_ctx, ldb_ctx, attrs);
+    if (dn == NULL) {
+        /* Shouldn't happen, but let's try system keytab in this case */
+        DEBUG(SSSDBG_CRIT_FAILURE,
+              "Cannot determine subdomain DN, falling back to two-way trust\n");
+        return SD_TRUST_DIRECTION_TWOWAY;
+    }
+
+    if (ipa_subdom_is_member_dom(dn) == true) {
+        /* It's expected member domains do not have the direction */
+        direction = SD_TRUST_DIRECTION_NOT_SET;
+    } else {
+        /* Old server? Default to 2way trust */
+        direction = SD_TRUST_DIRECTION_TWOWAY;
+    }
+
+    talloc_free(dn);
+    return direction;
+}
+
+errno_t ipa_server_get_trust_direction(struct sysdb_attrs *sd,
+                                       struct ldb_context *ldb_ctx,
+                                       enum trust_direction *_direction)
+{
+    uint32_t ipa_trust_direction = 0;
+    enum trust_direction direction;
+    int ret;
+
+    ret = sysdb_attrs_get_uint32_t(sd, IPA_TRUST_DIRECTION,
+                                   &ipa_trust_direction);
+    DEBUG(SSSDBG_TRACE_INTERNAL,
+          "Raw %s value: %d\n", IPA_TRUST_DIRECTION, ipa_trust_direction);
+    if (ret == ENOENT) {
+        direction = default_direction(sd, ldb_ctx, sd);
+    } else if (ret == EOK) {
+        switch (ipa_trust_direction) {
+        case LSA_TRUST_DIRECTION_OUTBOUND:
+            direction = SD_TRUST_DIRECTION_OUTBOUND;
+            break;
+        case (LSA_TRUST_DIRECTION_INBOUND | LSA_TRUST_DIRECTION_OUTBOUND):
+            direction = SD_TRUST_DIRECTION_TWOWAY;
+            break;
+        default:
+            return ERR_TRUST_NOT_SUPPORTED;
+        }
+    } else {
+        return ret;
+    }
+
+    *_direction = direction;
+    return EOK;
+}
+
+const char *ipa_trust_dir2str(enum trust_direction direction)
+{
+    switch (direction) {
+    case SD_TRUST_DIRECTION_OUTBOUND:
+        return "one-way root domain";
+    case SD_TRUST_DIRECTION_TWOWAY:
+        return "two-way root domain";
+    case SD_TRUST_DIRECTION_NOT_SET:
+        return "trust direction not set";
+    default:
+        return "unknown";
+    }
+}
+
 static errno_t
 ipa_ad_ctx_new(struct be_ctx *be_ctx,
                struct ipa_id_ctx *id_ctx,
diff --git a/src/util/util_errors.c b/src/util/util_errors.c
index 97c54a5b3352f7982a129ddea3e65f08d0351ea3..2323d2f0ede9ad9c1f3246cde7c286aeef211449 100644
--- a/src/util/util_errors.c
+++ b/src/util/util_errors.c
@@ -74,6 +74,7 @@ struct err_string error_to_str[] = {
     { "Malformed cache entry" }, /* ERR_MALFORMED_ENTRY */
     { "Unexpected cache entry type" }, /* ERR_UNEXPECTED_ENTRY_TYPE */
     { "Failed to resolve one of user groups" }, /* ERR_SIMPLE_GROUPS_MISSING */
+    { "Unsupported trust direction" }, /* ERR_TRUST_NOT_SUPPORTED */
     { "ERR_LAST" } /* ERR_LAST */
 };
 
diff --git a/src/util/util_errors.h b/src/util/util_errors.h
index 2dbf2243c753a7c418665bbff71fde469cec806f..154006fa124ce1eab6fa3034586af0c60d03be1f 100644
--- a/src/util/util_errors.h
+++ b/src/util/util_errors.h
@@ -96,6 +96,7 @@ enum sssd_errors {
     ERR_MALFORMED_ENTRY,
     ERR_UNEXPECTED_ENTRY_TYPE,
     ERR_SIMPLE_GROUPS_MISSING,
+    ERR_TRUST_NOT_SUPPORTED,
     ERR_LAST            /* ALWAYS LAST */
 };
 
-- 
2.1.0

-------------- next part --------------
>From 0ac4124cda2c64c6c498f293a2acaf13027efd4a Mon Sep 17 00:00:00 2001
From: Jakub Hrozek <jhrozek at redhat.com>
Date: Mon, 1 Jun 2015 21:18:32 +0200
Subject: [PATCH 13/29] IPA: Read forest name for trusted forest roots as well

This will reduce special-casing when establishing forest roots as all
domains will contain the forest name.

Additionally, AD subdomains already contain the forest name.
---
 src/providers/ipa/ipa_subdomains.c | 15 +++++++++++++++
 1 file changed, 15 insertions(+)

diff --git a/src/providers/ipa/ipa_subdomains.c b/src/providers/ipa/ipa_subdomains.c
index e897132d5ce9e20d62ca6d0f72f8ac0a010a6dc0..1867cd311a724e8e220f1b582d326e0330f4ea60 100644
--- a/src/providers/ipa/ipa_subdomains.c
+++ b/src/providers/ipa/ipa_subdomains.c
@@ -329,6 +329,7 @@ static errno_t ipa_subdom_get_forest(TALLOC_CTX *mem_ctx,
 {
     int ret;
     struct ldb_dn *dn = NULL;
+    const char *name;
     const struct ldb_val *val;
     char *forest = NULL;
 
@@ -340,6 +341,20 @@ static errno_t ipa_subdom_get_forest(TALLOC_CTX *mem_ctx,
     }
 
     if (ipa_subdom_is_member_dom(dn) == false) {
+        ret = sysdb_attrs_get_string(attrs, IPA_CN, &name);
+        if (ret) {
+            DEBUG(SSSDBG_OP_FAILURE, "sysdb_attrs_get_string failed.\n");
+            goto done;
+        }
+
+        forest = talloc_strdup(mem_ctx, name);
+        if (forest == NULL) {
+            DEBUG(SSSDBG_OP_FAILURE, "talloc_strndup failed.\n");
+            ret = ENOMEM;
+            goto done;
+        }
+
+        DEBUG(SSSDBG_TRACE_INTERNAL, "The forest name is %s\n", name);
         ret = EOK;
         goto done;
     }
-- 
2.1.0

-------------- next part --------------
>From e62c5ca39ec9d7d850199e811708f739781f8df0 Mon Sep 17 00:00:00 2001
From: Jakub Hrozek <jhrozek at redhat.com>
Date: Mon, 25 May 2015 19:44:05 +0200
Subject: [PATCH 14/29] IPA: Make constructing an IPA server mode context async

Refactoring in preparation for requesting the keytab in future patches.
Currently it's a fake async that just marks the request as done.
---
 src/providers/ipa/ipa_subdomains.c        |  32 ++++-
 src/providers/ipa/ipa_subdomains.h        |   7 +-
 src/providers/ipa/ipa_subdomains_server.c | 227 +++++++++++++++++++++++++++---
 3 files changed, 240 insertions(+), 26 deletions(-)

diff --git a/src/providers/ipa/ipa_subdomains.c b/src/providers/ipa/ipa_subdomains.c
index 1867cd311a724e8e220f1b582d326e0330f4ea60..6a12fa8f50ff7d2ca5570282d59eea90683ec606 100644
--- a/src/providers/ipa/ipa_subdomains.c
+++ b/src/providers/ipa/ipa_subdomains.c
@@ -769,6 +769,7 @@ ipa_subdomains_handler_get_cont(struct ipa_subdomains_req_ctx *ctx,
 }
 
 static void ipa_get_view_name_done(struct tevent_req *req);
+static void ipa_server_create_trusts_done(struct tevent_req *trust_req);
 static errno_t ipa_check_master(struct ipa_subdomains_req_ctx *ctx);
 
 static errno_t ipa_get_view_name(struct ipa_subdomains_req_ctx *ctx)
@@ -1003,6 +1004,7 @@ static void ipa_subdomains_handler_done(struct tevent_req *req)
     struct sss_domain_info *domain;
     bool refresh_has_changes = false;
     int dp_error = DP_ERR_FATAL;
+    struct tevent_req *trust_req;
 
     ctx = tevent_req_callback_data(req, struct ipa_subdomains_req_ctx);
     domain = ctx->sd_ctx->be_ctx->domain;
@@ -1048,13 +1050,16 @@ static void ipa_subdomains_handler_done(struct tevent_req *req)
         }
 
         if (ctx->sd_ctx->id_ctx->server_mode != NULL) {
-            ret = ipa_ad_subdom_refresh(ctx->sd_ctx->be_ctx,
-                                        ctx->sd_ctx->id_ctx,
-                                        domain);
-            if (ret != EOK) {
-                DEBUG(SSSDBG_OP_FAILURE, "ipa_ad_subdom_refresh failed.\n");
+            trust_req = ipa_server_create_trusts_send(ctx, ctx->sd_ctx->be_ctx->ev,
+                                                      ctx->sd_ctx->be_ctx,
+                                                      ctx->sd_ctx->id_ctx,
+                                                      domain);
+            if (trust_req == NULL) {
+                ret = ENOMEM;
                 goto done;
             }
+            tevent_req_set_callback(trust_req, ipa_server_create_trusts_done, ctx);
+            return;
         }
     }
 
@@ -1090,6 +1095,23 @@ done:
     ipa_subdomains_done(ctx->sd_ctx, ctx->be_req, dp_error, ret, NULL);
 }
 
+static void ipa_server_create_trusts_done(struct tevent_req *trust_req)
+{
+    errno_t ret;
+    int dp_error = DP_ERR_FATAL;
+    struct ipa_subdomains_req_ctx *ctx;
+
+    ctx = tevent_req_callback_data(trust_req, struct ipa_subdomains_req_ctx);
+
+    ret = ipa_server_create_trusts_recv(trust_req);
+    talloc_zfree(trust_req);
+    if (ret == EOK) {
+        dp_error = DP_ERR_OK;
+    }
+
+    ipa_subdomains_done(ctx->sd_ctx, ctx->be_req, dp_error, ret, NULL);
+}
+
 static errno_t ipa_check_master(struct ipa_subdomains_req_ctx *ctx)
 {
     int ret;
diff --git a/src/providers/ipa/ipa_subdomains.h b/src/providers/ipa/ipa_subdomains.h
index 76e4b3802284719cd4fb6f731f7f92fa877d8ba3..a2acb7887cf4156fd20944fcc82df320ae588e98 100644
--- a/src/providers/ipa/ipa_subdomains.h
+++ b/src/providers/ipa/ipa_subdomains.h
@@ -48,10 +48,15 @@ struct ipa_ad_server_ctx {
 };
 
 /* To be used by ipa_subdomains.c only */
-errno_t ipa_ad_subdom_refresh(struct be_ctx *be_ctx,
+struct tevent_req *
+ipa_server_create_trusts_send(TALLOC_CTX *mem_ctx,
+                              struct tevent_context *ev,
+                              struct be_ctx *be_ctx,
                               struct ipa_id_ctx *id_ctx,
                               struct sss_domain_info *parent);
 
+errno_t ipa_server_create_trusts_recv(struct tevent_req *req);
+
 void ipa_ad_subdom_remove(struct be_ctx *be_ctx,
                           struct ipa_id_ctx *id_ctx,
                           struct sss_domain_info *subdom);
diff --git a/src/providers/ipa/ipa_subdomains_server.c b/src/providers/ipa/ipa_subdomains_server.c
index 9594735dfabd5165ed1fb514d7fa028df5748670..e9d1184a32000a0b8f411ad5fc77ef952c601ddd 100644
--- a/src/providers/ipa/ipa_subdomains_server.c
+++ b/src/providers/ipa/ipa_subdomains_server.c
@@ -253,66 +253,178 @@ ipa_ad_ctx_new(struct be_ctx *be_ctx,
     return EOK;
 }
 
-static errno_t ipa_server_trust_add(struct be_ctx *be_ctx,
-                                    struct ipa_id_ctx *id_ctx,
-                                    struct sss_domain_info *subdom)
+struct ipa_server_trust_add_state {
+};
+
+static struct tevent_req *
+ipa_server_trust_add_send(TALLOC_CTX *mem_ctx,
+                          struct tevent_context *ev,
+                          struct be_ctx *be_ctx,
+                          struct ipa_id_ctx *id_ctx,
+                          struct sss_domain_info *subdom)
 {
+    struct tevent_req *req = NULL;
+    struct ipa_server_trust_add_state *state = NULL;
     struct ipa_ad_server_ctx *trust_ctx;
     struct ad_id_ctx *ad_id_ctx;
     errno_t ret;
 
+    req = tevent_req_create(mem_ctx, &state, struct ipa_server_trust_add_state);
+    if (req == NULL) {
+        return NULL;
+    }
+
     ret = ipa_ad_ctx_new(be_ctx, id_ctx, subdom, &ad_id_ctx);
     if (ret != EOK) {
         DEBUG(SSSDBG_OP_FAILURE,
               "Cannot create ad_id_ctx for subdomain %s\n", subdom->name);
-        return ret;
+        goto immediate;
     }
 
     trust_ctx = talloc(id_ctx->server_mode, struct ipa_ad_server_ctx);
     if (trust_ctx == NULL) {
-        return ENOMEM;
+        ret = ENOMEM;
+        goto immediate;
     }
     trust_ctx->dom = subdom;
     trust_ctx->ad_id_ctx = ad_id_ctx;
 
     DLIST_ADD(id_ctx->server_mode->trusts, trust_ctx);
+    ret = EOK;
+    goto immediate;
+
+    return req;
+
+immediate:
+    if (ret != EOK) {
+        tevent_req_error(req, ret);
+    } else {
+        tevent_req_done(req);
+    }
+    tevent_req_post(req, ev);
+    return req;
+}
+
+static errno_t ipa_server_trust_add_recv(struct tevent_req *req)
+{
+    TEVENT_REQ_RETURN_ON_ERROR(req);
     return EOK;
 }
 
-errno_t ipa_ad_subdom_refresh(struct be_ctx *be_ctx,
+struct ipa_server_create_trusts_state {
+    struct tevent_context *ev;
+    struct be_ctx *be_ctx;
+    struct ipa_id_ctx *id_ctx;
+    struct sss_domain_info *domiter;
+};
+
+static errno_t ipa_server_create_trusts_step(struct tevent_req *req);
+static void ipa_server_create_trusts_done(struct tevent_req *subreq);
+
+struct tevent_req *
+ipa_server_create_trusts_send(TALLOC_CTX *mem_ctx,
+                              struct tevent_context *ev,
+                              struct be_ctx *be_ctx,
                               struct ipa_id_ctx *id_ctx,
                               struct sss_domain_info *parent)
 {
-    struct sss_domain_info *dom;
+    struct tevent_req *req = NULL;
+    struct ipa_server_create_trusts_state *state = NULL;
+    errno_t ret;
+
+    req = tevent_req_create(mem_ctx, &state,
+                            struct ipa_server_create_trusts_state);
+    if (req == NULL) {
+        return NULL;
+    }
+
+    state->ev = ev;
+    state->be_ctx = be_ctx;
+    state->id_ctx = id_ctx;
+    state->domiter = parent;
+
+    ret = ipa_server_create_trusts_step(req);
+    if (ret != EAGAIN) {
+        goto immediate;
+    }
+
+    return req;
+
+immediate:
+    if (ret != EOK) {
+        tevent_req_error(req, ret);
+    } else {
+        tevent_req_done(req);
+    }
+    tevent_req_post(req, ev);
+    return req;
+}
+
+static errno_t ipa_server_create_trusts_step(struct tevent_req *req)
+{
+    struct tevent_req *subreq = NULL;
     struct ipa_ad_server_ctx *trust_iter;
-    errno_t ret;
+    struct ipa_server_create_trusts_state *state = NULL;
 
-    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)) {
+    state = tevent_req_data(req, struct ipa_server_create_trusts_state);
+
+    for (state->domiter = get_next_domain(state->domiter, true);
+         state->domiter && IS_SUBDOMAIN(state->domiter);
+         state->domiter = get_next_domain(state->domiter, false)) {
 
         /* Check if we already have an ID context for this subdomain */
-        DLIST_FOR_EACH(trust_iter, id_ctx->server_mode->trusts) {
-            if (trust_iter->dom == dom) {
+        DLIST_FOR_EACH(trust_iter, state->id_ctx->server_mode->trusts) {
+            if (trust_iter->dom == state->domiter) {
                 break;
             }
         }
 
         /* Newly detected trust */
         if (trust_iter == NULL) {
-            ret = ipa_server_trust_add(be_ctx, id_ctx, dom);
-            if (ret != EOK) {
-                DEBUG(SSSDBG_OP_FAILURE,
-                      "Cannot create ad_id_ctx for subdomain %s\n",
-                       dom->name);
-                continue;
+            subreq = ipa_server_trust_add_send(state, state->ev, state->be_ctx,
+                                               state->id_ctx, state->domiter);
+            if (subreq == NULL) {
+                return ENOMEM;
             }
+            tevent_req_set_callback(subreq, ipa_server_create_trusts_done, req);
+            return EAGAIN;
         }
     }
 
     return EOK;
 }
 
+static void ipa_server_create_trusts_done(struct tevent_req *subreq)
+{
+    errno_t ret;
+    struct tevent_req *req = tevent_req_callback_data(subreq,
+                                                      struct tevent_req);
+
+    ret = ipa_server_trust_add_recv(subreq);
+    talloc_zfree(subreq);
+    if (ret != EOK) {
+        tevent_req_error(req, ret);
+        return;
+    }
+
+    ret = ipa_server_create_trusts_step(req);
+    if (ret == EOK) {
+        tevent_req_done(req);
+        return;
+    } else if (ret != EAGAIN) {
+        tevent_req_error(req, ret);
+        return;
+    }
+
+    /* Will cycle back */
+}
+
+errno_t ipa_server_create_trusts_recv(struct tevent_req *req)
+{
+    TEVENT_REQ_RETURN_ON_ERROR(req);
+    return EOK;
+}
+
 void ipa_ad_subdom_remove(struct be_ctx *be_ctx,
                           struct ipa_id_ctx *id_ctx,
                           struct sss_domain_info *subdom)
@@ -343,6 +455,80 @@ void ipa_ad_subdom_remove(struct be_ctx *be_ctx,
     talloc_zfree(sdom);
 }
 
+struct ipa_ad_subdom_reinit_state {
+    struct tevent_context *ev;
+    struct be_ctx *be_ctx;
+    struct ipa_id_ctx *id_ctx;
+    struct sss_domain_info *parent;
+};
+
+static void create_trusts_at_startup_done(struct tevent_req *req)
+{
+    errno_t ret;
+
+    ret = ipa_server_create_trusts_recv(req);
+    talloc_free(req);
+    if (ret != EOK) {
+        DEBUG(SSSDBG_MINOR_FAILURE,
+              "ipa_server_create_trusts_send request failed.\n");
+    }
+}
+
+static void create_trusts_at_startup(struct tevent_context *ev,
+                                     struct tevent_immediate *imm,
+                                     void *pvt)
+{
+    struct tevent_req *req;
+    struct ipa_ad_subdom_reinit_state *state;
+
+    state = talloc_get_type(pvt, struct ipa_ad_subdom_reinit_state);
+
+    req = ipa_server_create_trusts_send(state, state->ev, state->be_ctx,
+                                        state->id_ctx, state->parent);
+    if (req == NULL) {
+        DEBUG(SSSDBG_OP_FAILURE, "ipa_server_create_trusts_send failed.\n");
+        talloc_free(state);
+        return;
+    }
+
+    tevent_req_set_callback(req, create_trusts_at_startup_done, state);
+    return;
+}
+
+static errno_t ipa_ad_subdom_reinit(TALLOC_CTX *mem_ctx,
+                                    struct tevent_context *ev,
+                                    struct be_ctx *be_ctx,
+                                    struct ipa_id_ctx *id_ctx,
+                                    struct sss_domain_info *parent)
+{
+    struct tevent_immediate *imm;
+    struct ipa_ad_subdom_reinit_state *state;
+
+    state = talloc(mem_ctx, struct ipa_ad_subdom_reinit_state);
+    if (state == NULL) {
+        return ENOMEM;
+    }
+    state->ev = ev;
+    state->be_ctx = be_ctx;
+    state->id_ctx = id_ctx;
+    state->parent = parent;
+
+    if (dp_opt_get_bool(id_ctx->ipa_options->basic,
+                        IPA_SERVER_MODE) == false) {
+        return EOK;
+    }
+
+    imm = tevent_create_immediate(mem_ctx);
+    if (imm == NULL) {
+        DEBUG(SSSDBG_OP_FAILURE, "tevent_create_immediate failed.\n");
+        talloc_free(state);
+        return ENOMEM;
+    }
+
+    tevent_schedule_immediate(imm, ev, create_trusts_at_startup, state);
+    return EOK;
+}
+
 int ipa_ad_subdom_init(struct be_ctx *be_ctx,
                        struct ipa_id_ctx *id_ctx)
 {
@@ -388,7 +574,8 @@ int ipa_ad_subdom_init(struct be_ctx *be_ctx,
     id_ctx->server_mode->trusts = NULL;
     id_ctx->server_mode->ext_groups = NULL;
 
-    ret = ipa_ad_subdom_refresh(be_ctx, id_ctx, be_ctx->domain);
+    ret = ipa_ad_subdom_reinit(be_ctx, be_ctx->ev,
+                               be_ctx, id_ctx, be_ctx->domain);
     if (ret != EOK) {
         DEBUG(SSSDBG_OP_FAILURE, "ipa_ad_subdom_refresh failed.\n");
         return ret;
-- 
2.1.0

-------------- next part --------------
>From 86fa415b514d12be46f01037341b4b48b0e013ad Mon Sep 17 00:00:00 2001
From: Jakub Hrozek <jhrozek at redhat.com>
Date: Fri, 29 May 2015 13:59:05 +0200
Subject: [PATCH 15/29] TESTS: Split off keytab creation into a common module

This change will make the keytab creating reusable by other tests.
---
 Makefile.am                         |   2 +
 src/tests/cmocka/common_mock_krb5.c | 102 ++++++++++++++++++++++++++++++++++++
 src/tests/cmocka/common_mock_krb5.h |  46 ++++++++++++++++
 src/tests/cmocka/test_copy_keytab.c |  45 ++++------------
 4 files changed, 159 insertions(+), 36 deletions(-)
 create mode 100644 src/tests/cmocka/common_mock_krb5.c
 create mode 100644 src/tests/cmocka/common_mock_krb5.h

diff --git a/Makefile.am b/Makefile.am
index 816f18e4c3ea1d7ef9e0823569ddf3b6ce8fcb58..6912fa971fbe7a4bebcce0ad5de9a9008f7344a6 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -619,6 +619,7 @@ dist_noinst_HEADERS = \
     src/tests/cmocka/common_mock_resp.h \
     src/tests/cmocka/common_mock_sdap.h \
     src/tests/cmocka/common_mock_sysdb_objects.h \
+    src/tests/cmocka/common_mock_krb5.h \
     src/tests/cmocka/test_expire_common.h \
     src/sss_client/pam_message.h \
     src/sss_client/ssh/sss_ssh_client.h \
@@ -2300,6 +2301,7 @@ test_copy_ccache_LDADD = \
     $(NULL)
 
 test_copy_keytab_SOURCES = \
+    src/tests/cmocka/common_mock_krb5.c \
     src/tests/cmocka/test_copy_keytab.c \
     src/providers/krb5/krb5_keytab.c \
     src/util/sss_krb5.c \
diff --git a/src/tests/cmocka/common_mock_krb5.c b/src/tests/cmocka/common_mock_krb5.c
new file mode 100644
index 0000000000000000000000000000000000000000..76ff697ef900e9a42e55c10e18629a8efc596be2
--- /dev/null
+++ b/src/tests/cmocka/common_mock_krb5.c
@@ -0,0 +1,102 @@
+/*
+    Authors:
+        Sumit Bose <sbose at redhat.com>
+
+    Copyright (C) 2014 Red Hat
+
+    SSSD tests: Tests keytab utilities
+
+    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 "util/sss_krb5.h"
+#include "tests/cmocka/common_mock.h"
+#include "tests/cmocka/common_mock_krb5.h"
+
+int mock_keytab(krb5_context kctx,
+                const char *kt_path,
+                krb5_keytab_entry *kt_keys,
+                size_t nkeys)
+{
+    krb5_error_code kerr;
+    krb5_keytab keytab;
+    size_t n;
+
+    kerr = krb5_kt_resolve(kctx, kt_path, &keytab);
+    assert_int_equal(kerr, 0);
+
+    for (n = 0; n < nkeys; n++) {
+        kerr = krb5_kt_add_entry(kctx, keytab, &kt_keys[n]);
+        assert_int_equal(kerr, 0);
+    }
+
+    kerr = krb5_kt_close(kctx, keytab);
+    assert_int_equal(kerr, 0);
+
+    return EOK;
+}
+
+void mock_krb5_keytab_entry(krb5_keytab_entry *kent,
+                            krb5_principal principal,
+                            krb5_timestamp timestamp,
+                            krb5_kvno vno,
+                            krb5_enctype enctype,
+                            const char *key)
+{
+    memset(kent, 0, sizeof(krb5_keytab_entry));
+
+    kent->magic = KV5M_KEYTAB_ENTRY;
+    kent->principal = principal;
+    kent->timestamp = timestamp;
+    kent->vno = vno;
+    kent->key.magic = KV5M_KEYBLOCK;
+    kent->key.enctype = enctype;
+    kent->key.length = strlen(key) - 1;
+    kent->key.contents = (krb5_octet *) discard_const(key);
+}
+
+int mock_keytab_with_contents(TALLOC_CTX *mem_ctx,
+                              const char *keytab_path,
+                              const char *keytab_princ)
+{
+    krb5_context kctx;
+    krb5_principal principal;
+    krb5_error_code kerr;
+    size_t nkeys = 2;
+    krb5_keytab_entry keys[nkeys];
+    char *keytab_file_name;
+
+    kerr = krb5_init_context(&kctx);
+    assert_int_equal(kerr, 0);
+
+    keytab_file_name = talloc_asprintf(mem_ctx, "FILE:%s", keytab_path);
+    assert_non_null(keytab_file_name);
+
+    kerr = krb5_parse_name(kctx, keytab_princ, &principal);
+    assert_int_equal(kerr, 0);
+
+    memset(&keys, nkeys, nkeys * sizeof(krb5_keytab_entry));
+
+    mock_krb5_keytab_entry(&keys[0], principal, 12345, 1, 1, "11");
+    mock_krb5_keytab_entry(&keys[1], principal, 12345, 1, 2, "12");
+
+    kerr = mock_keytab(kctx, keytab_file_name, keys, nkeys);
+    assert_int_equal(kerr, 0);
+
+    krb5_free_principal(kctx, principal);
+    krb5_free_context(kctx);
+    talloc_free(keytab_file_name);
+
+    return 0;
+}
diff --git a/src/tests/cmocka/common_mock_krb5.h b/src/tests/cmocka/common_mock_krb5.h
new file mode 100644
index 0000000000000000000000000000000000000000..15967203cd0b92e3871f48fee433ce9b97662904
--- /dev/null
+++ b/src/tests/cmocka/common_mock_krb5.h
@@ -0,0 +1,46 @@
+/*
+    Authors:
+        Sumit Bose <sbose at redhat.com>
+
+    Copyright (C) 2014 Red Hat
+
+    SSSD tests: Tests keytab utilities
+
+    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/>.
+*/
+
+#ifndef __COMMON_MOCK_KRB5_H_
+#define __COMMON_MOCK_KRB5_H_
+
+#include "util/sss_krb5.h"
+#include "tests/cmocka/common_mock.h"
+
+void mock_krb5_keytab_entry(krb5_keytab_entry *kent,
+                            krb5_principal principal,
+                            krb5_timestamp timestamp,
+                            krb5_kvno vno,
+                            krb5_enctype enctype,
+                            const char *key);
+
+int mock_keytab(krb5_context kctx,
+                const char *kt_path,
+                krb5_keytab_entry *kt_keys,
+                size_t nkeys);
+
+/* Dummy keys with user-selected principal */
+int mock_keytab_with_contents(TALLOC_CTX *mem_ctx,
+                              const char *keytab_path,
+                              const char *keytab_princ);
+
+#endif /* __COMMON_MOCK_KRB5_H_ */
diff --git a/src/tests/cmocka/test_copy_keytab.c b/src/tests/cmocka/test_copy_keytab.c
index 1999de3bf74772121b416f99d42d3734b5e1b72a..b83e03f2c8b98ab7060b04c91d1909e94f68130b 100644
--- a/src/tests/cmocka/test_copy_keytab.c
+++ b/src/tests/cmocka/test_copy_keytab.c
@@ -26,6 +26,7 @@
 #include "util/sss_krb5.h"
 #include "providers/krb5/krb5_common.h"
 #include "tests/cmocka/common_mock.h"
+#include "tests/cmocka/common_mock_krb5.h"
 
 #define KEYTAB_TEST_PRINC "test/keytab at TEST.KEYTAB"
 #define KEYTAB_PATH TEST_DIR "/keytab_test.keytab"
@@ -41,8 +42,8 @@ static int setup_keytab(void **state)
 {
     struct keytab_test_ctx *test_ctx;
     krb5_error_code kerr;
-    krb5_keytab keytab;
-    krb5_keytab_entry kent;
+    size_t nkeys = 4;
+    krb5_keytab_entry keys[nkeys];
 
     assert_true(leak_check_setup());
 
@@ -54,46 +55,18 @@ static int setup_keytab(void **state)
 
     test_ctx->keytab_file_name = "FILE:" KEYTAB_PATH;
 
-    kerr = krb5_kt_resolve(test_ctx->kctx, test_ctx->keytab_file_name, &keytab);
-    assert_int_equal(kerr, 0);
-
     kerr = krb5_parse_name(test_ctx->kctx, KEYTAB_TEST_PRINC,
                            &test_ctx->principal);
     assert_int_equal(kerr, 0);
 
-    memset(&kent, 0, sizeof(kent));
-    kent.magic = KV5M_KEYTAB_ENTRY;
-    kent.principal = test_ctx->principal;
-    kent.timestamp = 12345;
-    kent.vno = 1;
-    kent.key.magic = KV5M_KEYBLOCK;
-    kent.key.enctype = 1;
-    kent.key.length = 2;
-    kent.key.contents = (krb5_octet *) discard_const("11");
+    memset(&keys, nkeys, nkeys * sizeof(krb5_keytab_entry));
 
-    kerr = krb5_kt_add_entry(test_ctx->kctx, keytab, &kent);
-    assert_int_equal(kerr, 0);
+    mock_krb5_keytab_entry(&keys[0], test_ctx->principal, 12345, 1, 1, "11");
+    mock_krb5_keytab_entry(&keys[1], test_ctx->principal, 12345, 1, 2, "12");
+    mock_krb5_keytab_entry(&keys[2], test_ctx->principal, 12345, 2, 1, "21");
+    mock_krb5_keytab_entry(&keys[3], test_ctx->principal, 12345, 2, 2, "22");
 
-    kent.key.enctype = 2;
-    kent.key.contents = (krb5_octet *) discard_const("12");
-
-    kerr = krb5_kt_add_entry(test_ctx->kctx, keytab, &kent);
-    assert_int_equal(kerr, 0);
-
-    kent.vno = 2;
-    kent.key.enctype = 1;
-    kent.key.contents = (krb5_octet *) discard_const("21");
-
-    kerr = krb5_kt_add_entry(test_ctx->kctx, keytab, &kent);
-    assert_int_equal(kerr, 0);
-
-    kent.key.enctype = 2;
-    kent.key.contents = (krb5_octet *) discard_const("22");
-
-    kerr = krb5_kt_add_entry(test_ctx->kctx, keytab, &kent);
-    assert_int_equal(kerr, 0);
-
-    kerr = krb5_kt_close(test_ctx->kctx, keytab);
+    kerr = mock_keytab(test_ctx->kctx, test_ctx->keytab_file_name, keys, nkeys);
     assert_int_equal(kerr, 0);
 
     check_leaks_push(test_ctx);
-- 
2.1.0

-------------- next part --------------
>From c6df218fea569033dd69008f382a91cd4805e8b7 Mon Sep 17 00:00:00 2001
From: Jakub Hrozek <jhrozek at redhat.com>
Date: Fri, 29 May 2015 15:42:46 +0200
Subject: [PATCH 16/29] TESTS: Add a common mock_be_ctx function

Reduces code duplication between tests.
---
 Makefile.am                           |  4 ++++
 src/tests/cmocka/common_mock_be.c     | 39 +++++++++++++++++++++++++++++++++++
 src/tests/cmocka/common_mock_be.h     | 30 +++++++++++++++++++++++++++
 src/tests/cmocka/test_be_ptask.c      |  8 +++++--
 src/tests/cmocka/test_dyndns.c        |  6 ++----
 src/tests/cmocka/test_nested_groups.c |  5 ++---
 6 files changed, 83 insertions(+), 9 deletions(-)
 create mode 100644 src/tests/cmocka/common_mock_be.c
 create mode 100644 src/tests/cmocka/common_mock_be.h

diff --git a/Makefile.am b/Makefile.am
index 6912fa971fbe7a4bebcce0ad5de9a9008f7344a6..db5b23242d34abb3006ba5a2f2d3da16feae8664 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -620,6 +620,7 @@ dist_noinst_HEADERS = \
     src/tests/cmocka/common_mock_sdap.h \
     src/tests/cmocka/common_mock_sysdb_objects.h \
     src/tests/cmocka/common_mock_krb5.h \
+    src/tests/cmocka/common_mock_be.h \
     src/tests/cmocka/test_expire_common.h \
     src/sss_client/pam_message.h \
     src/sss_client/ssh/sss_ssh_client.h \
@@ -1935,6 +1936,7 @@ EXTRA_dyndns_tests_DEPENDENCIES = \
      $(ldblib_LTLIBRARIES)
 dyndns_tests_SOURCES = \
      $(SSSD_RESOLV_OBJ) \
+     src/tests/cmocka/common_mock_be.c \
      src/tests/cmocka/test_dyndns.c \
      src/providers/data_provider_opts.c
 dyndns_tests_CFLAGS = \
@@ -1966,6 +1968,7 @@ nestedgroups_tests_SOURCES = \
     $(TEST_MOCK_PROVIDER_OBJ) \
     src/providers/ldap/sdap_idmap.c \
     src/tests/cmocka/test_nested_groups.c \
+    src/tests/cmocka/common_mock_be.c \
     src/providers/ldap/sdap_async_nested_groups.c \
     src/providers/ldap/sdap_ad_groups.c \
     $(NULL)
@@ -2269,6 +2272,7 @@ test_sysdb_utils_LDADD = \
     $(NULL)
 
 test_be_ptask_SOURCES = \
+    src/tests/cmocka/common_mock_be.c \
     src/tests/cmocka/test_be_ptask.c \
     src/providers/dp_ptask.c \
     $(NULL)
diff --git a/src/tests/cmocka/common_mock_be.c b/src/tests/cmocka/common_mock_be.c
new file mode 100644
index 0000000000000000000000000000000000000000..29821ba3429aab2fc063cd75f5cf16652d407674
--- /dev/null
+++ b/src/tests/cmocka/common_mock_be.c
@@ -0,0 +1,39 @@
+/*
+    Authors:
+        Jakub Hrozek <jhrozek at redhat.com>
+
+    Copyright (C) 2013 Red Hat
+
+    SSSD tests: Fake back end
+
+    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 "util/util.h"
+#include "tests/cmocka/common_mock_resp.h"
+
+struct be_ctx *mock_be_ctx(TALLOC_CTX *mem_ctx, struct sss_test_ctx *tctx)
+{
+    struct be_ctx *be_ctx;
+
+    be_ctx = talloc_zero(mem_ctx, struct be_ctx);
+    assert_non_null(be_ctx);
+
+    be_ctx->cdb = tctx->confdb;
+    be_ctx->ev  = tctx->ev;
+    be_ctx->domain  = tctx->dom;
+    be_ctx->conf_path = tctx->conf_dom_path;
+
+    return be_ctx;
+}
diff --git a/src/tests/cmocka/common_mock_be.h b/src/tests/cmocka/common_mock_be.h
new file mode 100644
index 0000000000000000000000000000000000000000..80694d7c7afe61eba7abe5029612d5dbfe8a4a1c
--- /dev/null
+++ b/src/tests/cmocka/common_mock_be.h
@@ -0,0 +1,30 @@
+/*
+    Authors:
+        Sumit Bose <sbose at redhat.com>
+
+    Copyright (C) 2014 Red Hat
+
+    SSSD tests: Tests keytab utilities
+
+    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/>.
+*/
+
+#ifndef __COMMON_MOCK_BE_H_
+#define __COMMON_MOCK_BE_H_
+
+#include "tests/cmocka/common_mock.h"
+
+struct be_ctx *mock_be_ctx(TALLOC_CTX *mem_ctx, struct sss_test_ctx *tctx);
+
+#endif /* __COMMON_MOCK_BE_H_ */
diff --git a/src/tests/cmocka/test_be_ptask.c b/src/tests/cmocka/test_be_ptask.c
index f4a120c0c6201a6a306004445445024684f713d1..a0daaf967a6f1ea991ff30445488ffadff51f821 100644
--- a/src/tests/cmocka/test_be_ptask.c
+++ b/src/tests/cmocka/test_be_ptask.c
@@ -28,6 +28,7 @@
 #include "providers/dp_ptask_private.h"
 #include "providers/dp_ptask.h"
 #include "tests/cmocka/common_mock.h"
+#include "tests/cmocka/common_mock_be.h"
 #include "tests/common.h"
 
 #define DELAY  2
@@ -37,6 +38,7 @@
     cmocka_unit_test_setup_teardown(test_ ## test, test_setup, test_teardown)
 
 struct test_ctx {
+    struct sss_test_ctx *tctx;
     struct be_ctx *be_ctx;
 
     time_t when;
@@ -274,8 +276,10 @@ static int test_setup(void **state)
     test_ctx = talloc_zero(global_talloc_context, struct test_ctx);
     assert_non_null(test_ctx);
 
-    /* create be_ctx, only ev and offline field should be used */
-    test_ctx->be_ctx = talloc_zero(test_ctx, struct be_ctx);
+    test_ctx->tctx = create_ev_test_ctx(test_ctx);
+    assert_non_null(test_ctx->tctx);
+
+    test_ctx->be_ctx = mock_be_ctx(test_ctx, test_ctx->tctx);
     assert_non_null(test_ctx->be_ctx);
 
     test_ctx->be_ctx->ev = tevent_context_init(test_ctx->be_ctx);
diff --git a/src/tests/cmocka/test_dyndns.c b/src/tests/cmocka/test_dyndns.c
index 97fac9425a63e1306301c8256285b4b40f0f3e1e..689e333d4e68c4a2582894d06b8b7b20c76b9be8 100644
--- a/src/tests/cmocka/test_dyndns.c
+++ b/src/tests/cmocka/test_dyndns.c
@@ -33,6 +33,7 @@
 #include "providers/dp_dyndns.c"
 
 #include "tests/cmocka/common_mock.h"
+#include "tests/cmocka/common_mock_be.h"
 #include "src/providers/dp_dyndns.h"
 
 #define TESTS_PATH "tests_dyndns"
@@ -412,12 +413,9 @@ static int dyndns_test_setup(void **state)
                                                 TEST_ID_PROVIDER, params);
     assert_non_null(dyndns_test_ctx->tctx);
 
-    dyndns_test_ctx->be_ctx = talloc_zero(dyndns_test_ctx, struct be_ctx);
+    dyndns_test_ctx->be_ctx = mock_be_ctx(dyndns_test_ctx, dyndns_test_ctx->tctx);
     assert_non_null(dyndns_test_ctx->be_ctx);
 
-    dyndns_test_ctx->be_ctx->cdb = dyndns_test_ctx->tctx->confdb;
-    dyndns_test_ctx->be_ctx->ev  = dyndns_test_ctx->tctx->ev;
-    dyndns_test_ctx->be_ctx->conf_path = dyndns_test_ctx->tctx->conf_dom_path;
     return 0;
 }
 
diff --git a/src/tests/cmocka/test_nested_groups.c b/src/tests/cmocka/test_nested_groups.c
index 4f748a170075898fca9583ec2da7a1d15a51e2d2..75fb3a19f27596fe89fdc779c73653cbbaf5e536 100644
--- a/src/tests/cmocka/test_nested_groups.c
+++ b/src/tests/cmocka/test_nested_groups.c
@@ -25,6 +25,7 @@
 
 #include "tests/cmocka/common_mock.h"
 #include "tests/cmocka/common_mock_sdap.h"
+#include "tests/cmocka/common_mock_be.h"
 #include "tests/cmocka/common_mock_sysdb_objects.h"
 #include "providers/ldap/ldap_common.h"
 #include "providers/ldap/sdap.h"
@@ -599,12 +600,10 @@ static int nested_groups_test_setup(void **state)
                                         struct sdap_id_ctx);
     assert_non_null(test_ctx->sdap_id_ctx);
 
-    test_ctx->sdap_id_ctx->be = talloc_zero(test_ctx->sdap_id_ctx,
-                                            struct be_ctx);
+    test_ctx->sdap_id_ctx->be = mock_be_ctx(test_ctx, test_ctx->tctx);
     assert_non_null(test_ctx->sdap_id_ctx->be);
 
     test_ctx->sdap_id_ctx->opts = test_ctx->sdap_opts;
-    test_ctx->sdap_id_ctx->be->domain = test_ctx->tctx->dom;
 
     ret = sdap_idmap_init(test_ctx, test_ctx->sdap_id_ctx, &test_ctx->idmap_ctx);
     assert_int_equal(ret, EOK);
-- 
2.1.0

-------------- next part --------------
>From c4c32f8df5876d5d08ad99c2ae441d39cd85282a Mon Sep 17 00:00:00 2001
From: Jakub Hrozek <jhrozek at redhat.com>
Date: Fri, 29 May 2015 15:46:57 +0200
Subject: [PATCH 17/29] TESTS: Add a common function to set up sdap_id_ctx

Reduces code duplication between tests.
---
 src/tests/cmocka/common_mock_sdap.c   | 15 +++++++++++++++
 src/tests/cmocka/common_mock_sdap.h   |  4 ++++
 src/tests/cmocka/test_nested_groups.c | 14 +++++++-------
 3 files changed, 26 insertions(+), 7 deletions(-)

diff --git a/src/tests/cmocka/common_mock_sdap.c b/src/tests/cmocka/common_mock_sdap.c
index 4f6988c2619f5a0f4e49a629f2a3bf5b792fd2a5..1adfc7caef1cdb0f0d23621cb536e9dac29b6cd6 100644
--- a/src/tests/cmocka/common_mock_sdap.c
+++ b/src/tests/cmocka/common_mock_sdap.c
@@ -33,6 +33,21 @@ errno_t krb5_try_kdcip(struct confdb_ctx *cdb,
     return EOK;
 }
 
+struct sdap_id_ctx *mock_sdap_id_ctx(TALLOC_CTX *mem_ctx,
+                                     struct be_ctx *be_ctx,
+                                     struct sdap_options *sdap_opts)
+{
+    struct sdap_id_ctx *sdap_id_ctx;
+
+    sdap_id_ctx = talloc_zero(mem_ctx, struct sdap_id_ctx);
+    assert_non_null(sdap_id_ctx);
+
+    sdap_id_ctx->be = be_ctx;
+    sdap_id_ctx->opts = sdap_opts;
+
+    return sdap_id_ctx;
+}
+
 struct sdap_options *mock_sdap_options_ldap(TALLOC_CTX *mem_ctx,
                                             struct sss_domain_info *domain,
                                             struct confdb_ctx *confdb_ctx,
diff --git a/src/tests/cmocka/common_mock_sdap.h b/src/tests/cmocka/common_mock_sdap.h
index 937b83137fdde20eb980e062a4f37a8081909855..6fd1fb79c0fe9c36471a03634e38acf9f226c234 100644
--- a/src/tests/cmocka/common_mock_sdap.h
+++ b/src/tests/cmocka/common_mock_sdap.h
@@ -38,6 +38,10 @@ struct sdap_options *mock_sdap_options_ldap(TALLOC_CTX *mem_ctx,
                                             struct confdb_ctx *confdb_ctx,
                                             const char *conf_path);
 
+struct sdap_id_ctx *mock_sdap_id_ctx(TALLOC_CTX *mem_ctx,
+                                     struct be_ctx *be_ctx,
+                                     struct sdap_options *sdap_opts);
+
 struct sdap_handle *mock_sdap_handle(TALLOC_CTX *mem_ctx);
 
 #endif /* COMMON_MOCK_SDAP_H_ */
diff --git a/src/tests/cmocka/test_nested_groups.c b/src/tests/cmocka/test_nested_groups.c
index 75fb3a19f27596fe89fdc779c73653cbbaf5e536..390b39950823a68644739c1b38834a40c501a647 100644
--- a/src/tests/cmocka/test_nested_groups.c
+++ b/src/tests/cmocka/test_nested_groups.c
@@ -54,6 +54,7 @@
 struct nested_groups_test_ctx {
     struct sss_test_ctx *tctx;
 
+    struct be_ctx *be_ctx;
     struct sdap_options *sdap_opts;
     struct sdap_handle *sdap_handle;
     struct sdap_domain *sdap_domain;
@@ -596,15 +597,14 @@ static int nested_groups_test_setup(void **state)
     test_ctx->sdap_handle = mock_sdap_handle(test_ctx);
     assert_non_null(test_ctx->sdap_handle);
 
-    test_ctx->sdap_id_ctx = talloc_zero(test_ctx,
-                                        struct sdap_id_ctx);
+    test_ctx->be_ctx = mock_be_ctx(test_ctx, test_ctx->tctx);
+    assert_non_null(test_ctx->be_ctx);
+
+    test_ctx->sdap_id_ctx = mock_sdap_id_ctx(test_ctx,
+                                             test_ctx->be_ctx,
+                                             test_ctx->sdap_opts);
     assert_non_null(test_ctx->sdap_id_ctx);
 
-    test_ctx->sdap_id_ctx->be = mock_be_ctx(test_ctx, test_ctx->tctx);
-    assert_non_null(test_ctx->sdap_id_ctx->be);
-
-    test_ctx->sdap_id_ctx->opts = test_ctx->sdap_opts;
-
     ret = sdap_idmap_init(test_ctx, test_ctx->sdap_id_ctx, &test_ctx->idmap_ctx);
     assert_int_equal(ret, EOK);
     test_ctx->sdap_opts->idmap_ctx = test_ctx->idmap_ctx;
-- 
2.1.0

-------------- next part --------------
>From d6aee640083895a4b4aee6856c3aa3d2f6bf4a87 Mon Sep 17 00:00:00 2001
From: Jakub Hrozek <jhrozek at redhat.com>
Date: Fri, 29 May 2015 16:04:44 +0200
Subject: [PATCH 18/29] TESTS: Move krb5_try_kdcip to nested group test

Avoids duplicate errors during linking.
---
 src/tests/cmocka/common_mock_sdap.c   | 8 --------
 src/tests/cmocka/test_nested_groups.c | 8 ++++++++
 2 files changed, 8 insertions(+), 8 deletions(-)

diff --git a/src/tests/cmocka/common_mock_sdap.c b/src/tests/cmocka/common_mock_sdap.c
index 1adfc7caef1cdb0f0d23621cb536e9dac29b6cd6..cef321613d5d22db044474b9da63a40135511c88 100644
--- a/src/tests/cmocka/common_mock_sdap.c
+++ b/src/tests/cmocka/common_mock_sdap.c
@@ -25,14 +25,6 @@
 #include "providers/ldap/sdap.h"
 #include "tests/cmocka/common_mock.h"
 
-errno_t krb5_try_kdcip(struct confdb_ctx *cdb,
-                       const char *conf_path,
-                       struct dp_option *opts,
-                       int opt_id)
-{
-    return EOK;
-}
-
 struct sdap_id_ctx *mock_sdap_id_ctx(TALLOC_CTX *mem_ctx,
                                      struct be_ctx *be_ctx,
                                      struct sdap_options *sdap_opts)
diff --git a/src/tests/cmocka/test_nested_groups.c b/src/tests/cmocka/test_nested_groups.c
index 390b39950823a68644739c1b38834a40c501a647..eef9df2dc25c231cb81243f48b759e2fced1a85d 100644
--- a/src/tests/cmocka/test_nested_groups.c
+++ b/src/tests/cmocka/test_nested_groups.c
@@ -67,6 +67,14 @@ struct nested_groups_test_ctx {
     unsigned long num_groups;
 };
 
+errno_t krb5_try_kdcip(struct confdb_ctx *cdb,
+                       const char *conf_path,
+                       struct dp_option *opts,
+                       int opt_id)
+{
+    return EOK;
+}
+
 /* Both arrays must have the same length! */
 static void compare_sysdb_string_array_noorder(struct sysdb_attrs **sysdb_array,
                                                const char **string_array,
-- 
2.1.0

-------------- next part --------------
>From 511220462025f4bcbf504536166221ae6617350c Mon Sep 17 00:00:00 2001
From: Jakub Hrozek <jhrozek at redhat.com>
Date: Thu, 4 Jun 2015 11:04:41 +0200
Subject: [PATCH 19/29] TESTS: Add unit test for the subdomain_server.c module

---
 Makefile.am                                   |  31 ++
 src/tests/cmocka/test_ipa_subdomains_server.c | 617 ++++++++++++++++++++++++++
 2 files changed, 648 insertions(+)
 create mode 100644 src/tests/cmocka/test_ipa_subdomains_server.c

diff --git a/Makefile.am b/Makefile.am
index db5b23242d34abb3006ba5a2f2d3da16feae8664..4eed1069b982d35b4dc27ea5cfad7c2dad291097 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -227,6 +227,7 @@ if HAVE_CMOCKA
         test_fo_srv \
         pam-srv-tests \
         test_ipa_subdom_util \
+        test_ipa_subdom_server \
         $(NULL)
 
 if HAVE_LIBRESOLV
@@ -2441,6 +2442,36 @@ test_ipa_subdom_util_LDADD = \
     libsss_test_common.la \
     $(NULL)
 
+test_ipa_subdom_server_SOURCES = \
+    $(sssd_be_SOURCES) \
+    $(libsss_krb5_common_la_SOURCES) \
+    src/tests/cmocka/common_mock_sdap.c \
+    src/tests/cmocka/common_mock_be.c \
+    src/tests/cmocka/common_mock_krb5.c \
+    src/tests/cmocka/test_ipa_subdomains_server.c \
+    src/providers/ipa/ipa_subdomains_server.c \
+    src/providers/ipa/ipa_subdomains_utils.c \
+    $(NULL)
+test_ipa_subdom_server_CFLAGS = \
+    $(AM_CFLAGS) \
+    -DUNIT_TESTING \
+    $(NULL)
+test_ipa_subdom_server_LDFLAGS = \
+    -Wl,-wrap,krb5_kt_default \
+    $(NULL)
+test_ipa_subdom_server_LDADD = \
+    $(PAM_LIBS) \
+    $(CMOCKA_LIBS) \
+    $(SSSD_LIBS) \
+    $(CARES_LIBS) \
+    $(KRB5_LIBS) \
+    $(SSSD_INTERNAL_LTLIBS) \
+    libsss_ldap_common.la \
+    libsss_idmap.la \
+    libsss_ad_common.la \
+    libsss_test_common.la \
+    $(NULL)
+
 endif # HAVE_CMOCKA
 
 noinst_PROGRAMS = pam_test_client
diff --git a/src/tests/cmocka/test_ipa_subdomains_server.c b/src/tests/cmocka/test_ipa_subdomains_server.c
new file mode 100644
index 0000000000000000000000000000000000000000..279784cfc3639a76cd72bd3669fa040e7ba9e6a3
--- /dev/null
+++ b/src/tests/cmocka/test_ipa_subdomains_server.c
@@ -0,0 +1,617 @@
+/*
+    Authors:
+        Jakub Hrozek <jhrozek at redhat.com>
+
+    Copyright (C) 2015 Red Hat
+
+    SSSD tests: IPA subdomain util tests
+
+    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 <talloc.h>
+#include <tevent.h>
+#include <errno.h>
+#include <popt.h>
+#include <stdlib.h>
+
+#include <sys/types.h>
+#include <ifaddrs.h>
+#include <arpa/inet.h>
+
+#include "providers/ipa/ipa_subdomains.h"
+#include "providers/ipa/ipa_opts.h"
+#include "providers/data_provider.h"
+#include "tests/cmocka/common_mock.h"
+#include "tests/cmocka/common_mock_resp.h"
+#include "tests/cmocka/common_mock_krb5.h"
+#include "tests/cmocka/common_mock_sdap.h"
+#include "tests/cmocka/common_mock_be.h"
+
+#define DOM_REALM       "DOM.MAIN"
+#define HOSTNAME        "ipaserver.dom.main"
+
+#define TEST_AUTHID       "host/"HOSTNAME
+#define KEYTAB_TEST_PRINC TEST_AUTHID"@"DOM_REALM
+#define KEYTAB_PATH       TEST_DIR"/keytab_test.keytab"
+
+#define SUBDOM_NAME  "twoway.subdom.test"
+#define SUBDOM_REALM "TWOWAY.SUBDOM.TEST"
+#define SUBDOM_FLAT  "TWOWAY"
+#define SUBDOM_SID   "S-1-2-3"
+
+#define CHILD_NAME  "child."SUBDOM_NAME
+#define CHILD_REALM "CHILD."SUBDOM_REALM
+#define CHILD_FLAT  "CHILD"
+#define CHILD_SID   "S-1-2-3-4"
+
+#define TESTS_PATH "tests_ipa_subdom_server"
+#define TEST_CONF_DB "test_ipa_subdom_server.ldb"
+#define TEST_DOM_NAME "ipa_subdom_server_test"
+#define TEST_ID_PROVIDER "ipa"
+
+krb5_error_code __wrap_krb5_kt_default(krb5_context context, krb5_keytab *id)
+{
+    return krb5_kt_resolve(context, KEYTAB_PATH, id);
+}
+
+struct trust_test_ctx {
+    struct sss_test_ctx *tctx;
+    struct be_ctx *be_ctx;
+
+    struct ipa_id_ctx *ipa_ctx;
+};
+
+static struct ipa_id_ctx *mock_ipa_ctx(TALLOC_CTX *mem_ctx,
+                                       struct be_ctx *be_ctx,
+                                       struct sss_test_ctx *tctx,
+                                       const char *krb5_realm,
+                                       const char *hostname)
+{
+    struct ipa_id_ctx *ipa_ctx;
+    errno_t ret;
+
+    ipa_ctx = talloc_zero(mem_ctx, struct ipa_id_ctx);
+    assert_non_null(ipa_ctx);
+
+    ipa_ctx->ipa_options = talloc_zero(ipa_ctx, struct ipa_options);
+    assert_non_null(ipa_ctx->ipa_options);
+
+    ipa_ctx->ipa_options->id = talloc_zero(ipa_ctx->ipa_options,
+                                           struct sdap_options);
+    assert_non_null(ipa_ctx->ipa_options->id);
+
+    ret = dp_get_options(ipa_ctx->ipa_options->id,
+                         tctx->confdb,
+                         tctx->conf_dom_path,
+                         ipa_def_ldap_opts,
+                         SDAP_OPTS_BASIC,
+                         &ipa_ctx->ipa_options->id->basic);
+    assert_int_equal(ret, EOK);
+
+    ret = dp_get_options(ipa_ctx->ipa_options->basic,
+                         tctx->confdb,
+                         tctx->conf_dom_path,
+                         ipa_basic_opts,
+                         IPA_OPTS_BASIC,
+                         &ipa_ctx->ipa_options->basic);
+    assert_int_equal(ret, EOK);
+
+    ret = dp_opt_set_string(ipa_ctx->ipa_options->basic,
+                            IPA_KRB5_REALM, krb5_realm);
+    assert_int_equal(ret, EOK);
+
+    ret = dp_opt_set_string(ipa_ctx->ipa_options->basic,
+                            IPA_HOSTNAME, hostname);
+    assert_int_equal(ret, EOK);
+
+    ret = dp_opt_set_bool(ipa_ctx->ipa_options->basic,
+                          IPA_SERVER_MODE, true);
+    assert_int_equal(ret, EOK);
+
+    ipa_ctx->sdap_id_ctx = mock_sdap_id_ctx(ipa_ctx, be_ctx,
+                                            ipa_ctx->ipa_options->id);
+    assert_non_null(ipa_ctx->sdap_id_ctx);
+
+    return ipa_ctx;
+}
+
+static struct ipa_server_mode_ctx *mock_server_mode(TALLOC_CTX *mem_ctx)
+{
+    struct ipa_server_mode_ctx *server_mode;
+
+    server_mode = talloc_zero(mem_ctx, struct ipa_server_mode_ctx);
+    assert_non_null(server_mode);
+
+    server_mode->hostname = HOSTNAME;
+    server_mode->realm = DOM_REALM;
+
+    return server_mode;
+}
+
+static void add_test_subdomains(struct trust_test_ctx *test_ctx)
+{
+    errno_t
+
+    /* Add two subdomains */
+    ret = sysdb_subdomain_store(test_ctx->tctx->sysdb,
+                                SUBDOM_NAME, SUBDOM_REALM,
+                                NULL, SUBDOM_SID,
+                                true, false, SUBDOM_REALM,
+                                SD_TRUST_DIRECTION_TWOWAY);
+    assert_int_equal(ret, EOK);
+
+    ret = sysdb_subdomain_store(test_ctx->tctx->sysdb,
+                                CHILD_NAME, CHILD_REALM,
+                                CHILD_FLAT, CHILD_SID,
+                                true, false, SUBDOM_REALM,
+                                SD_TRUST_DIRECTION_TWOWAY);
+    assert_int_equal(ret, EOK);
+
+    ret = sysdb_update_subdomains(test_ctx->tctx->dom);
+    assert_int_equal(ret, EOK);
+
+}
+
+static int test_ipa_server_create_trusts_setup(void **state)
+{
+    errno_t ret;
+    struct trust_test_ctx *test_ctx;
+    struct sss_test_conf_param params[] = {
+        { NULL, NULL },             /* Sentinel */
+    };
+
+    test_ctx = talloc_zero(NULL,
+                           struct trust_test_ctx);
+    assert_non_null(test_ctx);
+
+    test_ctx->tctx = create_dom_test_ctx(test_ctx, TESTS_PATH,
+                                         TEST_CONF_DB, TEST_DOM_NAME,
+                                         TEST_ID_PROVIDER, params);
+    assert_non_null(test_ctx->tctx);
+
+    test_ctx->be_ctx = mock_be_ctx(test_ctx, test_ctx->tctx);
+    assert_non_null(test_ctx->be_ctx);
+
+    test_ctx->ipa_ctx = mock_ipa_ctx(test_ctx, test_ctx->be_ctx, test_ctx->tctx,
+                                     DOM_REALM, HOSTNAME);
+    assert_non_null(test_ctx->tctx);
+
+    test_ctx->ipa_ctx->server_mode = mock_server_mode(test_ctx->ipa_ctx);
+    assert_non_null(test_ctx->ipa_ctx->server_mode);
+
+    ret = be_init_failover(test_ctx->be_ctx);
+    assert_int_equal(ret, EOK);
+
+    mock_keytab_with_contents(test_ctx, KEYTAB_PATH, KEYTAB_TEST_PRINC);
+
+    *state = test_ctx;
+    return 0;
+}
+
+static int test_ipa_server_create_trusts_teardown(void **state)
+{
+    struct trust_test_ctx *test_ctx =
+        talloc_get_type(*state, struct trust_test_ctx);
+    errno_t ret;
+
+    ret = unlink(KEYTAB_PATH);
+    assert_int_equal(ret, 0);
+
+    talloc_free(test_ctx);
+    return 0;
+}
+
+static void test_ipa_server_create_trusts_none(struct tevent_req *req);
+static void test_ipa_server_create_trusts_twoway(struct tevent_req *req);
+static void test_ipa_server_create_trusts_finish(struct trust_test_ctx *test_ctx);
+
+static void test_ipa_server_create_trusts(void **state)
+{
+    struct trust_test_ctx *test_ctx =
+        talloc_get_type(*state, struct trust_test_ctx);
+    struct tevent_req *req;
+    errno_t ret;
+
+    req = ipa_server_create_trusts_send(test_ctx,
+                                        test_ctx->tctx->ev,
+                                        test_ctx->be_ctx,
+                                        test_ctx->ipa_ctx,
+                                        test_ctx->be_ctx->domain);
+    assert_non_null(req);
+
+    tevent_req_set_callback(req, test_ipa_server_create_trusts_none, test_ctx);
+
+    ret = test_ev_loop(test_ctx->tctx);
+    assert_int_equal(ret, ERR_OK);
+}
+
+static void test_ipa_server_create_trusts_none(struct tevent_req *req)
+{
+    struct trust_test_ctx *test_ctx = \
+        tevent_req_callback_data(req, struct trust_test_ctx);
+    errno_t ret;
+
+    ret = ipa_server_create_trusts_recv(req);
+    talloc_zfree(req);
+    assert_int_equal(ret, EOK);
+
+    /* Add two subdomains */
+    add_test_subdomains(test_ctx);
+
+    req = ipa_server_create_trusts_send(test_ctx,
+                                        test_ctx->tctx->ev,
+                                        test_ctx->be_ctx,
+                                        test_ctx->ipa_ctx,
+                                        test_ctx->be_ctx->domain);
+    assert_non_null(req);
+
+    tevent_req_set_callback(req, test_ipa_server_create_trusts_twoway, test_ctx);
+
+}
+
+static void assert_trust_object(struct ipa_ad_server_ctx *trust,
+                                const char *dom_name,
+                                const char *dom_realm,
+                                const char *sid,
+                                const char *keytab,
+                                const char *authid,
+                                const char *sdap_realm)
+{
+    const char *s;
+
+    assert_non_null(trust);
+    assert_non_null(trust->dom);
+    assert_string_equal(trust->dom->name, dom_name);
+    assert_string_equal(trust->dom->domain_id, sid);
+
+    s = dp_opt_get_string(trust->ad_id_ctx->ad_options->basic,
+                          AD_KRB5_REALM);
+    if (dom_realm != NULL) {
+        assert_non_null(s);
+        assert_string_equal(s, dom_realm);
+    } else {
+        assert_null(s);
+    }
+
+    s = dp_opt_get_string(trust->ad_id_ctx->ad_options->basic,
+                          AD_DOMAIN);
+    if (dom_name != NULL) {
+        assert_non_null(s);
+        assert_string_equal(s, dom_name);
+    } else {
+        assert_null(s);
+    }
+
+    /* the system keytab is always used with two-way trusts */
+    s = dp_opt_get_string(trust->ad_id_ctx->ad_options->id->basic,
+                          SDAP_KRB5_KEYTAB);
+    if (keytab != NULL) {
+        assert_non_null(s);
+        assert_string_equal(s, keytab);
+    } else {
+        assert_null(s);
+    }
+
+    s = dp_opt_get_string(trust->ad_id_ctx->ad_options->id->basic,
+                          SDAP_SASL_REALM);
+    if (sdap_realm != NULL) {
+        assert_non_null(s);
+        assert_string_equal(s, sdap_realm);
+    } else {
+        assert_null(s);
+    }
+
+    s = dp_opt_get_string(trust->ad_id_ctx->ad_options->id->basic,
+                          SDAP_SASL_AUTHID);
+    if (authid != NULL) {
+        assert_non_null(s);
+        assert_string_equal(s, authid);
+    } else {
+        assert_null(s);
+    }
+}
+
+static void test_ipa_server_create_trusts_twoway(struct tevent_req *req)
+{
+    struct trust_test_ctx *test_ctx = \
+        tevent_req_callback_data(req, struct trust_test_ctx);
+    errno_t ret;
+    struct sss_domain_info *child_dom;
+
+    ret = ipa_server_create_trusts_recv(req);
+    talloc_zfree(req);
+    assert_int_equal(ret, EOK);
+
+    /* Trust object should be around now */
+    assert_non_null(test_ctx->ipa_ctx->server_mode->trusts);
+
+    /* Two-way trusts should use the system realm */
+    assert_trust_object(test_ctx->ipa_ctx->server_mode->trusts,
+                        CHILD_NAME,
+                        DOM_REALM,
+                        CHILD_SID,
+                        NULL,
+                        TEST_AUTHID,
+                        DOM_REALM);
+
+    assert_non_null(test_ctx->ipa_ctx->server_mode->trusts->next);
+
+    assert_trust_object(test_ctx->ipa_ctx->server_mode->trusts->next,
+                        SUBDOM_NAME,
+                        DOM_REALM,
+                        SUBDOM_SID,
+                        NULL,
+                        TEST_AUTHID,
+                        DOM_REALM);
+
+    /* No more trust objects */
+    assert_null(test_ctx->ipa_ctx->server_mode->trusts->next->next);
+
+    ret = sysdb_subdomain_delete(test_ctx->tctx->sysdb, CHILD_NAME);
+    assert_int_equal(ret, EOK);
+
+    child_dom = find_domain_by_name(test_ctx->be_ctx->domain, CHILD_NAME, true);
+    assert_non_null(child_dom);
+
+    ipa_ad_subdom_remove(test_ctx->be_ctx, test_ctx->ipa_ctx, child_dom);
+
+    assert_trust_object(test_ctx->ipa_ctx->server_mode->trusts,
+                        SUBDOM_NAME,
+                        DOM_REALM,
+                        SUBDOM_SID,
+                        NULL,
+                        TEST_AUTHID,
+                        DOM_REALM);
+    assert_null(test_ctx->ipa_ctx->server_mode->trusts->next);
+
+    test_ipa_server_create_trusts_finish(test_ctx);
+}
+
+static void test_ipa_server_create_trusts_finish(struct trust_test_ctx *test_ctx)
+{
+    test_ctx->tctx->error = EOK;
+    test_ctx->tctx->done = true;
+}
+
+static void
+ipa_server_init_done(struct tevent_context *ev,
+                     struct tevent_timer *te,
+                     struct timeval tv, void *pvt)
+{
+    struct trust_test_ctx *test_ctx =
+        talloc_get_type(pvt, struct trust_test_ctx);
+
+    test_ctx->tctx->done = true;
+}
+
+static void test_ipa_server_trust_init(void **state)
+{
+    struct trust_test_ctx *test_ctx =
+        talloc_get_type(*state, struct trust_test_ctx);
+    errno_t ret;
+    struct tevent_timer *timeout_handler;
+    struct timeval tv;
+
+    add_test_subdomains(test_ctx);
+
+    ret = ipa_ad_subdom_init(test_ctx->be_ctx, test_ctx->ipa_ctx);
+    assert_int_equal(ret, EOK);
+
+    tv = tevent_timeval_current_ofs(1, 0);
+    timeout_handler = tevent_add_timer(test_ctx->tctx->ev, test_ctx, tv,
+                                       ipa_server_init_done, test_ctx);
+    assert_non_null(timeout_handler);
+
+    ret = test_ev_loop(test_ctx->tctx);
+    assert_int_equal(ret, ERR_OK);
+
+    assert_non_null(test_ctx->ipa_ctx->server_mode->trusts);
+
+    /* Trust object should be around now */
+    assert_non_null(test_ctx->ipa_ctx->server_mode->trusts);
+
+    /* Two-way trusts should use the system realm */
+    assert_trust_object(test_ctx->ipa_ctx->server_mode->trusts,
+                        CHILD_NAME,
+                        DOM_REALM,
+                        CHILD_SID,
+                        NULL,
+                        TEST_AUTHID,
+                        DOM_REALM);
+
+    assert_non_null(test_ctx->ipa_ctx->server_mode->trusts->next);
+
+    assert_trust_object(test_ctx->ipa_ctx->server_mode->trusts->next,
+                        SUBDOM_NAME,
+                        DOM_REALM,
+                        SUBDOM_SID,
+                        NULL,
+                        TEST_AUTHID,
+                        DOM_REALM);
+
+    /* No more trust objects */
+    assert_null(test_ctx->ipa_ctx->server_mode->trusts->next->next);
+}
+
+struct dir_test_ctx {
+    struct ldb_context *ldb;
+    struct sysdb_attrs *tdo;
+};
+
+static int test_get_trust_direction_setup(void **state)
+{
+    struct dir_test_ctx *test_ctx;
+
+    test_ctx = talloc_zero(global_talloc_context,
+                           struct dir_test_ctx);
+    assert_non_null(test_ctx);
+
+    test_ctx->ldb = ldb_init(test_ctx, NULL);
+    assert_non_null(test_ctx->ldb);
+
+    test_ctx->tdo = sysdb_new_attrs(test_ctx);
+    assert_non_null(test_ctx->tdo);
+
+    *state = test_ctx;
+    return 0;
+}
+
+static int test_get_trust_direction_teardown(void **state)
+{
+    struct dir_test_ctx *test_ctx =
+        talloc_get_type(*state, struct dir_test_ctx);
+
+    talloc_free(test_ctx);
+    return 0;
+}
+
+static void test_get_trust_direction_inbound(void **state)
+{
+    errno_t ret;
+    enum trust_direction dir;
+    struct dir_test_ctx *test_ctx =
+        talloc_get_type(*state, struct dir_test_ctx);
+
+    ret = sysdb_attrs_add_uint32(test_ctx->tdo, IPA_TRUST_DIRECTION, 1);
+    assert_int_equal(ret, EOK);
+
+    ret = ipa_server_get_trust_direction(test_ctx->tdo, test_ctx->ldb, &dir);
+    assert_int_equal(ret, ERR_TRUST_NOT_SUPPORTED);
+}
+
+static void test_get_trust_direction_outbound(void **state)
+{
+    errno_t ret;
+    enum trust_direction dir;
+    struct dir_test_ctx *test_ctx =
+        talloc_get_type(*state, struct dir_test_ctx);
+
+    ret = sysdb_attrs_add_uint32(test_ctx->tdo, IPA_TRUST_DIRECTION, 2);
+    assert_int_equal(ret, EOK);
+
+    ret = ipa_server_get_trust_direction(test_ctx->tdo, test_ctx->ldb, &dir);
+    assert_int_equal(ret, EOK);
+    assert_int_equal(dir, SD_TRUST_DIRECTION_OUTBOUND);
+}
+
+static void test_get_trust_direction_twoway(void **state)
+{
+    errno_t ret;
+    enum trust_direction dir;
+    struct dir_test_ctx *test_ctx =
+        talloc_get_type(*state, struct dir_test_ctx);
+
+    ret = sysdb_attrs_add_uint32(test_ctx->tdo, IPA_TRUST_DIRECTION, 1 | 2);
+    assert_int_equal(ret, EOK);
+
+    ret = ipa_server_get_trust_direction(test_ctx->tdo, test_ctx->ldb, &dir);
+    assert_int_equal(ret, EOK);
+    assert_int_equal(dir, SD_TRUST_DIRECTION_TWOWAY);
+}
+
+static void test_get_trust_direction_notset_root(void **state)
+{
+    errno_t ret;
+    enum trust_direction dir;
+    struct dir_test_ctx *test_ctx =
+        talloc_get_type(*state, struct dir_test_ctx);
+
+    ret = sysdb_attrs_add_string(test_ctx->tdo, SYSDB_ORIG_DN,
+                                 "cn=AD.DOM,cn=ad,cn=trusts,dc=example,dc=com");
+    assert_int_equal(ret, EOK);
+
+    ret = ipa_server_get_trust_direction(test_ctx->tdo, test_ctx->ldb, &dir);
+    assert_int_equal(ret, EOK);
+    assert_int_equal(dir, SD_TRUST_DIRECTION_TWOWAY);
+}
+
+static void test_get_trust_direction_notset_member(void **state)
+{
+    errno_t ret;
+    enum trust_direction dir;
+    struct dir_test_ctx *test_ctx =
+        talloc_get_type(*state, struct dir_test_ctx);
+
+    ret = sysdb_attrs_add_string(test_ctx->tdo, SYSDB_ORIG_DN,
+                   "cn=SUB.AD.DOM,cn=AD.DOM,cn=ad,cn=trusts,dc=example,dc=com");
+    assert_int_equal(ret, EOK);
+
+    ret = ipa_server_get_trust_direction(test_ctx->tdo, test_ctx->ldb, &dir);
+    assert_int_equal(ret, EOK);
+    assert_int_equal(dir, SD_TRUST_DIRECTION_NOT_SET);
+}
+
+int main(int argc, const char *argv[])
+{
+    int rv;
+    poptContext pc;
+    int opt;
+    struct poptOption long_options[] = {
+        POPT_AUTOHELP
+        SSSD_DEBUG_OPTS
+        POPT_TABLEEND
+    };
+
+    const struct CMUnitTest tests[] = {
+        cmocka_unit_test_setup_teardown(test_ipa_server_trust_init,
+                                        test_ipa_server_create_trusts_setup,
+                                        test_ipa_server_create_trusts_teardown),
+        cmocka_unit_test_setup_teardown(test_ipa_server_create_trusts,
+                                        test_ipa_server_create_trusts_setup,
+                                        test_ipa_server_create_trusts_teardown),
+
+        cmocka_unit_test_setup_teardown(test_get_trust_direction_inbound,
+                                        test_get_trust_direction_setup,
+                                        test_get_trust_direction_teardown),
+        cmocka_unit_test_setup_teardown(test_get_trust_direction_outbound,
+                                        test_get_trust_direction_setup,
+                                        test_get_trust_direction_teardown),
+        cmocka_unit_test_setup_teardown(test_get_trust_direction_twoway,
+                                        test_get_trust_direction_setup,
+                                        test_get_trust_direction_teardown),
+        cmocka_unit_test_setup_teardown(test_get_trust_direction_notset_root,
+                                        test_get_trust_direction_setup,
+                                        test_get_trust_direction_teardown),
+        cmocka_unit_test_setup_teardown(test_get_trust_direction_notset_member,
+                                        test_get_trust_direction_setup,
+                                        test_get_trust_direction_teardown),
+    };
+
+    /* Set debug level to invalid value so we can deside if -d 0 was used. */
+    debug_level = SSSDBG_INVALID;
+
+    pc = poptGetContext(argv[0], argc, argv, long_options, 0);
+    while((opt = poptGetNextOpt(pc)) != -1) {
+        switch(opt) {
+        default:
+            fprintf(stderr, "\nInvalid option %s: %s\n\n",
+                    poptBadOption(pc, 0), poptStrerror(opt));
+            poptPrintUsage(pc, stderr, 0);
+            return 1;
+        }
+    }
+    poptFreeContext(pc);
+
+    DEBUG_CLI_INIT(debug_level);
+
+    /* Even though normally the tests should clean up after themselves
+     * they might not after a failed run. Remove the old db to be sure */
+    tests_set_cwd();
+    test_dom_suite_cleanup(TESTS_PATH, TEST_CONF_DB, TEST_DOM_NAME);
+    test_dom_suite_setup(TESTS_PATH);
+
+    rv = cmocka_run_group_tests(tests, NULL, NULL);
+    return rv;
+}
-- 
2.1.0

-------------- next part --------------
>From 42b7e7ce5b1d5877480cf15e720a8dbc654f3f0e Mon Sep 17 00:00:00 2001
From: Jakub Hrozek <jhrozek at redhat.com>
Date: Thu, 4 Jun 2015 10:51:05 +0200
Subject: [PATCH 20/29] IPA: Fetch keytab for 1way trusts

Uses the ipa-getkeytab call to retrieve keytabs for one-way trust
relationships.

https://fedorahosted.org/sssd/ticket/2636
---
 Makefile.am                                   |   3 +
 configure.ac                                  |   1 +
 contrib/sssd.spec.in                          |   1 +
 src/conf_macros.m4                            |  14 +
 src/providers/ipa/ipa_subdomains.h            |   5 +
 src/providers/ipa/ipa_subdomains_server.c     | 410 ++++++++++++++++++++++++--
 src/tests/cmocka/test_ipa_subdomains_server.c | 162 +++++++++-
 src/util/util_errors.c                        |   2 +
 src/util/util_errors.h                        |   2 +
 9 files changed, 571 insertions(+), 29 deletions(-)

diff --git a/Makefile.am b/Makefile.am
index 4eed1069b982d35b4dc27ea5cfad7c2dad291097..bdb5c8a4f827034397545308deb546367242709a 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -2455,9 +2455,11 @@ test_ipa_subdom_server_SOURCES = \
 test_ipa_subdom_server_CFLAGS = \
     $(AM_CFLAGS) \
     -DUNIT_TESTING \
+    -DIPA_TRUST_KEYTAB_DIR=TEST_DIR \
     $(NULL)
 test_ipa_subdom_server_LDFLAGS = \
     -Wl,-wrap,krb5_kt_default \
+    -Wl,-wrap,execl \
     $(NULL)
 test_ipa_subdom_server_LDADD = \
     $(PAM_LIBS) \
@@ -3283,6 +3285,7 @@ src/sysv/systemd/journal.conf: src/sysv/systemd/journal.conf.in Makefile
 
 SSSD_USER_DIRS = \
     $(DESTDIR)$(dbpath) \
+    $(DESTDIR)$(dbpath)/keytabs \
     $(DESTDIR)$(mcpath) \
     $(DESTDIR)$(pipepath) \
     $(DESTDIR)$(pipepath)/private \
diff --git a/configure.ac b/configure.ac
index a127d0bd57ee4149f664e193a65271d50d95f83b..339dd8b68056e747244a90897c742a31366721a1 100644
--- a/configure.ac
+++ b/configure.ac
@@ -122,6 +122,7 @@ WITH_PYTHON3_BINDINGS
 WITH_CIFS_PLUGIN_PATH
 WITH_SELINUX
 WITH_NSCD
+WITH_IPA_GETKEYTAB
 WITH_SEMANAGE
 WITH_AD_GPO_DEFAULT
 WITH_GPO_CACHE_PATH
diff --git a/contrib/sssd.spec.in b/contrib/sssd.spec.in
index d4b2a93000ffa03cb135a4595a9669d008313836..bfee8f8c2a2174e269b194b58f17ddfce2d4ffa1 100644
--- a/contrib/sssd.spec.in
+++ b/contrib/sssd.spec.in
@@ -765,6 +765,7 @@ rm -rf $RPM_BUILD_ROOT
 %defattr(-,root,root,-)
 %doc COPYING
 %attr(755,root,root) %dir %{pubconfpath}/krb5.include.d
+%attr(700,sssd,sssd) %dir %{dbpath}/keytabs
 %{_libdir}/%{name}/libsss_ipa.so
 %attr(4750,root,sssd) %{_libexecdir}/%{servicename}/selinux_child
 %{_mandir}/man5/sssd-ipa.5*
diff --git a/src/conf_macros.m4 b/src/conf_macros.m4
index 0ed1694cb6b8ebf54fc4488826fbb64b050fa3bb..eeba01e1b81ceb70766544ef2d3c004e8595658d 100644
--- a/src/conf_macros.m4
+++ b/src/conf_macros.m4
@@ -424,6 +424,20 @@ AC_DEFUN([WITH_TEST_DIR],
     AC_DEFINE_UNQUOTED(TEST_DIR, "$TEST_DIR", [Directory used for 'make check' temporary files])
   ])
 
+AC_DEFUN([WITH_IPA_GETKEYTAB],
+  [ AC_ARG_WITH([ipa_getkeytab],
+                [AC_HELP_STRING([--with-ipa-getkeytab=PATH],
+                                [Path to ipa_getkeytab binary to retrieve keytabs from FreeIPA server [/usr/sbin/ipa-getkeytab]]
+                               )
+                ]
+               )
+    IPA_GETKEYTAB_PATH="/usr/sbin/ipa-getkeytab"
+    if test x"$with_ipa_getkeytab" != x; then
+        IPA_GETKEYTAB_PATH=$with_ipa_getkeytab
+    fi
+    AC_DEFINE_UNQUOTED(IPA_GETKEYTAB_PATH, "$IPA_GETKEYTAB_PATH", [The path to the ipa-getkeytab utility])
+  ])
+
 AC_DEFUN([WITH_NSCD],
   [ AC_ARG_WITH([nscd],
                 [AC_HELP_STRING([--with-nscd=PATH],
diff --git a/src/providers/ipa/ipa_subdomains.h b/src/providers/ipa/ipa_subdomains.h
index a2acb7887cf4156fd20944fcc82df320ae588e98..a4da5af06fec0a3c60e00f04213425da802ae8b8 100644
--- a/src/providers/ipa/ipa_subdomains.h
+++ b/src/providers/ipa/ipa_subdomains.h
@@ -27,6 +27,11 @@
 
 #include "providers/dp_backend.h"
 #include "providers/ipa/ipa_common.h"
+#include "config.h"
+
+#ifndef IPA_TRUST_KEYTAB_DIR
+#define IPA_TRUST_KEYTAB_DIR         DB_PATH"/keytabs"
+#endif /* IPA_TRUST_KEYTAB_DIR */
 
 /* ==Sid2Name Extended Operation============================================= */
 #define EXOP_SID2NAME_OID "2.16.840.1.113730.3.8.10.4"
diff --git a/src/providers/ipa/ipa_subdomains_server.c b/src/providers/ipa/ipa_subdomains_server.c
index e9d1184a32000a0b8f411ad5fc77ef952c601ddd..eb4867c2665084ac17472b3370ef3007a9140607 100644
--- a/src/providers/ipa/ipa_subdomains_server.c
+++ b/src/providers/ipa/ipa_subdomains_server.c
@@ -31,6 +31,25 @@
 #define LSA_TRUST_DIRECTION_INBOUND  0x00000001
 #define LSA_TRUST_DIRECTION_OUTBOUND 0x00000002
 
+static char *forest_keytab(TALLOC_CTX *mem_ctx, const char *forest)
+{
+    return talloc_asprintf(mem_ctx,
+                           "%s/%s.keytab", IPA_TRUST_KEYTAB_DIR, forest);
+}
+
+static char *subdomain_trust_princ(TALLOC_CTX *mem_ctx,
+                                   const char *forest,
+                                   struct sss_domain_info *sd)
+{
+    if (sd->parent->flat_name == NULL) {
+        DEBUG(SSSDBG_CRIT_FAILURE,
+              "Unknown flat name for parent %s\n", sd->parent->name);
+        return NULL;
+    }
+
+    return talloc_asprintf(mem_ctx, "%s$@%s", sd->parent->flat_name, forest);
+}
+
 static enum trust_direction default_direction(TALLOC_CTX *mem_ctx,
                                               struct ldb_context *ldb_ctx,
                                               struct sysdb_attrs *attrs)
@@ -105,6 +124,11 @@ const char *ipa_trust_dir2str(enum trust_direction direction)
     }
 }
 
+#ifndef IPA_GETKEYTAB_TIMEOUT
+#define IPA_GETKEYTAB_TIMEOUT 5
+#endif /* IPA_GETKEYTAB_TIMEOUT */
+
+
 static errno_t
 ipa_ad_ctx_new(struct be_ctx *be_ctx,
                struct ipa_id_ctx *id_ctx,
@@ -253,9 +277,218 @@ ipa_ad_ctx_new(struct be_ctx *be_ctx,
     return EOK;
 }
 
+struct ipa_getkeytab_state {
+    int child_status;
+    struct sss_child_ctx_old *child_ctx;
+    struct tevent_timer *timeout_handler;
+};
+
+static void ipa_getkeytab_exec(const char *server,
+                               const char *principal,
+                               const char *keytab_path);
+static void ipa_getkeytab_done(int child_status,
+                               struct tevent_signal *sige,
+                               void *pvt);
+static void ipa_getkeytab_timeout(struct tevent_context *ev,
+                                  struct tevent_timer *te,
+                                  struct timeval tv, void *pvt);
+
+static struct tevent_req *ipa_getkeytab_send(TALLOC_CTX *mem_ctx,
+                                             struct tevent_context *ev,
+                                             const char *server,
+                                             const char *principal,
+                                             const char *keytab)
+
+
+{
+    errno_t ret;
+    struct tevent_req *req = NULL;
+    struct ipa_getkeytab_state *state;
+    pid_t child_pid;
+    struct timeval tv;
+
+    req = tevent_req_create(mem_ctx, &state, struct ipa_getkeytab_state);
+    if (req == NULL) {
+        return NULL;
+    }
+    state->child_status = 0;
+
+    if (server == NULL || principal == NULL || keytab == NULL) {
+        ret = EINVAL;
+        goto done;
+    }
+
+    DEBUG(SSSDBG_TRACE_FUNC,
+          "Retrieving keytab for %s from %s into %s\n",
+          principal, server, keytab);
+
+    child_pid = fork();
+    if (child_pid == 0) { /* child */
+        ipa_getkeytab_exec(server, principal, keytab);
+    } else if (child_pid > 0) { /* parent */
+        /* Set up SIGCHLD handler */
+        ret = child_handler_setup(ev, child_pid, ipa_getkeytab_done, req,
+                                  &state->child_ctx);
+        if (ret != EOK) {
+            DEBUG(SSSDBG_OP_FAILURE, "Could not set up child handlers [%d]: %s\n",
+                ret, sss_strerror(ret));
+            ret = ERR_IPA_GETKEYTAB_FAILED;
+            goto done;
+        }
+
+        /* Set up timeout handler */
+        tv = tevent_timeval_current_ofs(IPA_GETKEYTAB_TIMEOUT, 0);
+        state->timeout_handler = tevent_add_timer(ev, req, tv,
+                                                  ipa_getkeytab_timeout, req);
+        if(state->timeout_handler == NULL) {
+            ret = ERR_IPA_GETKEYTAB_FAILED;
+            goto done;
+        }
+
+        /* Now either wait for the timeout to fire or the child
+         * to finish
+         */
+    } else { /* error */
+        ret = errno;
+        DEBUG(SSSDBG_CRIT_FAILURE,
+              "fork failed [%d][%s].\n", ret, sss_strerror(ret));
+        goto done;
+    }
+
+    ret = EOK;
+done:
+    if (ret != EOK) {
+        tevent_req_error(req, ret);
+        tevent_req_post(req, ev);
+    }
+    return req;
+}
+
+static void ipa_getkeytab_exec(const char *server,
+                               const char *principal,
+                               const char *keytab_path)
+{
+    errno_t ret;
+    int debug_fd;
+
+    if (debug_level >= SSSDBG_TRACE_LIBS) {
+        debug_fd = get_fd_from_debug_file();
+        ret = dup2(debug_fd, STDERR_FILENO);
+        if (ret == -1) {
+            ret = errno;
+            DEBUG(SSSDBG_MINOR_FAILURE,
+                "dup2 failed [%d][%s].\n", ret, sss_strerror(ret));
+            /* stderr is not fatal */
+        }
+    }
+
+    errno = 0;
+    execl(IPA_GETKEYTAB_PATH, IPA_GETKEYTAB_PATH,
+          "-r", "-s", server, "-p", principal, "-k", keytab_path, NULL);
+
+    /* The child should never end up here */
+    ret = errno;
+    DEBUG(SSSDBG_CRIT_FAILURE,
+          "execv failed [%d][%s].\n", ret, sss_strerror(ret));
+    exit(1);
+}
+
+static void ipa_getkeytab_done(int child_status,
+                               struct tevent_signal *sige,
+                               void *pvt)
+{
+    struct tevent_req *req = talloc_get_type(pvt, struct tevent_req);
+    struct ipa_getkeytab_state *state =
+            tevent_req_data(req, struct ipa_getkeytab_state);
+
+    state->child_status = child_status;
+
+    if (WIFEXITED(child_status) && WEXITSTATUS(child_status) != 0) {
+        DEBUG(SSSDBG_OP_FAILURE,
+              "ipa-getkeytab failed with status [%d]\n", child_status);
+        tevent_req_error(req, ERR_IPA_GETKEYTAB_FAILED);
+        return;
+    }
+
+    if (WIFSIGNALED(child_status)) {
+        DEBUG(SSSDBG_OP_FAILURE,
+              "ipa-getkeytab was terminated by signal [%d]\n",
+               WTERMSIG(child_status));
+        tevent_req_error(req, ERR_IPA_GETKEYTAB_FAILED);
+        return;
+    }
+
+    tevent_req_done(req);
+}
+
+static void ipa_getkeytab_timeout(struct tevent_context *ev,
+                                  struct tevent_timer *te,
+                                  struct timeval tv, void *pvt)
+{
+    struct tevent_req *req =
+            talloc_get_type(pvt, struct tevent_req);
+    struct ipa_getkeytab_state *state =
+            tevent_req_data(req, struct ipa_getkeytab_state);
+
+    DEBUG(SSSDBG_CRIT_FAILURE, "Timeout reached for retrieving keytab from IPA server\n");
+    child_handler_destroy(state->child_ctx);
+    state->child_ctx = NULL;
+    state->child_status = ETIMEDOUT;
+    tevent_req_error(req, ERR_IPA_GETKEYTAB_FAILED);
+}
+
+static errno_t ipa_getkeytab_recv(struct tevent_req *req, int *child_status)
+{
+    struct ipa_getkeytab_state *state =
+            tevent_req_data(req, struct ipa_getkeytab_state);
+
+    DEBUG(SSSDBG_TRACE_INTERNAL,
+          "ipa-getkeytab status %d\n", state->child_status);
+    if (child_status) {
+        *child_status = state->child_status;
+    }
+
+    TEVENT_REQ_RETURN_ON_ERROR(req);
+
+    return EOK;
+}
+
+static errno_t ipa_check_keytab(const char *keytab)
+{
+    errno_t ret;
+
+    ret = check_file(keytab, getuid(), getgid(), S_IFREG|0600, 0, NULL, false);
+    if (ret != EOK) {
+        if (ret != ENOENT) {
+            DEBUG(SSSDBG_OP_FAILURE, "Failed to check for %s\n", keytab);
+        } else {
+            DEBUG(SSSDBG_TRACE_FUNC, "Keytab %s is not present\n", keytab);
+        }
+        goto done;
+    }
+
+    DEBUG(SSSDBG_TRACE_ALL, "keytab %s already exists\n", keytab);
+    ret = EOK;
+done:
+    return ret;
+}
+
 struct ipa_server_trust_add_state {
+    struct tevent_context *ev;
+    struct be_ctx *be_ctx;
+    struct ipa_id_ctx *id_ctx;
+    struct sss_domain_info *subdom;
+
+    enum trust_direction direction;
+    const char *forest;
+    const char *keytab;
+    const char *principal;
 };
 
+static errno_t ipa_server_trust_add_1way(struct tevent_req *req);
+static void ipa_server_trust_1way_kt_done(struct tevent_req *subreq);
+static errno_t ipa_server_trust_add_step(struct tevent_req *req);
+
 static struct tevent_req *
 ipa_server_trust_add_send(TALLOC_CTX *mem_ctx,
                           struct tevent_context *ev,
@@ -265,44 +498,173 @@ ipa_server_trust_add_send(TALLOC_CTX *mem_ctx,
 {
     struct tevent_req *req = NULL;
     struct ipa_server_trust_add_state *state = NULL;
-    struct ipa_ad_server_ctx *trust_ctx;
-    struct ad_id_ctx *ad_id_ctx;
     errno_t ret;
 
     req = tevent_req_create(mem_ctx, &state, struct ipa_server_trust_add_state);
     if (req == NULL) {
         return NULL;
     }
+    state->ev = ev;
+    state->be_ctx = be_ctx;
+    state->id_ctx = id_ctx;
+    state->subdom = subdom;
 
-    ret = ipa_ad_ctx_new(be_ctx, id_ctx, subdom, &ad_id_ctx);
-    if (ret != EOK) {
-        DEBUG(SSSDBG_OP_FAILURE,
-              "Cannot create ad_id_ctx for subdomain %s\n", subdom->name);
+    /* Trusts are only established with forest roots */
+    if (subdom->forest_root == NULL) {
+        ret = ERR_TRUST_FOREST_UNKNOWN;
         goto immediate;
     }
 
-    trust_ctx = talloc(id_ctx->server_mode, struct ipa_ad_server_ctx);
+    state->direction = subdom->forest_root->trust_direction;
+    state->forest = subdom->forest_root->realm;
+
+    DEBUG(SSSDBG_TRACE_LIBS,
+          "Trust direction of subdom %s from forest %s is: %s\n",
+          subdom->name, state->forest,
+          ipa_trust_dir2str(state->direction));
+
+    switch (state->direction) {
+    case SD_TRUST_DIRECTION_OUTBOUND:
+        /* Need special keytab */
+        ret = ipa_server_trust_add_1way(req);
+        if (ret == EAGAIN) {
+            /* In progress.. */
+            return req;
+        } else if (ret == EOK) {
+            ret = ipa_server_trust_add_step(req);
+        }
+        break;
+    case SD_TRUST_DIRECTION_TWOWAY:
+        /* Use system keytab */
+        ret = ipa_server_trust_add_step(req);
+        break;
+    default:
+        /* Even unset is an error at this point */
+        ret = ERR_TRUST_NOT_SUPPORTED;
+        break;
+    }
+
+immediate:
+    if (ret != EOK) {
+        tevent_req_error(req, ret);
+    } else {
+        tevent_req_done(req);
+    }
+    tevent_req_post(req, ev);
+    return req;
+}
+
+static errno_t ipa_server_trust_add_1way(struct tevent_req *req)
+{
+    errno_t ret;
+    struct tevent_req *subreq = NULL;
+    struct ipa_server_trust_add_state *state =
+            tevent_req_data(req, struct ipa_server_trust_add_state);
+    const char *hostname;
+
+    state->keytab = forest_keytab(state, state->forest);
+    if (state->keytab == NULL) {
+        DEBUG(SSSDBG_CRIT_FAILURE, "Cannot set up ipa_get_keytab\n");
+        return EIO;
+    }
+
+    ret = ipa_check_keytab(state->keytab);
+    if (ret == EOK) {
+        DEBUG(SSSDBG_TRACE_FUNC,
+              "Keytab already present, can add the trust\n");
+        return EOK;
+    } else if (ret != ENOENT) {
+        DEBUG(SSSDBG_OP_FAILURE,
+              "Failed to check for keytab: %d\n", ret);
+        return ret;
+    }
+
+    DEBUG(SSSDBG_TRACE_FUNC,
+          "No keytab for %s\n", state->subdom->name);
+
+    hostname = dp_opt_get_string(state->id_ctx->ipa_options->basic,
+                                 IPA_HOSTNAME);
+
+    state->principal = subdomain_trust_princ(state, state->forest, state->subdom);
+    if (state->principal == NULL) {
+        DEBUG(SSSDBG_CRIT_FAILURE, "Cannot set up ipa_get_keytab\n");
+        return EIO;
+    }
+
+    subreq = ipa_getkeytab_send(state->be_ctx, state->be_ctx->ev,
+                                hostname,
+                                state->principal,
+                                state->keytab);
+    if (subreq == NULL) {
+        return ENOMEM;
+    }
+    tevent_req_set_callback(subreq, ipa_server_trust_1way_kt_done, req);
+    return EAGAIN;
+}
+
+static void ipa_server_trust_1way_kt_done(struct tevent_req *subreq)
+{
+    errno_t ret;
+    struct tevent_req *req = tevent_req_callback_data(subreq,
+                                                      struct tevent_req);
+    struct ipa_server_trust_add_state *state =
+            tevent_req_data(req, struct ipa_server_trust_add_state);
+
+    ret = ipa_getkeytab_recv(subreq, NULL);
+    talloc_zfree(subreq);
+    if (ret != EOK) {
+        DEBUG(SSSDBG_OP_FAILURE, "ipa_getkeytab_recv failed: %d\n", ret);
+        tevent_req_error(req, ret);
+        return;
+    }
+
+    DEBUG(SSSDBG_TRACE_FUNC,
+          "Keytab successfully retrieved to %s\n", state->keytab);
+
+    ret = ipa_check_keytab(state->keytab);
+    if (ret != EOK) {
+        DEBUG(SSSDBG_OP_FAILURE, "ipa_check_keytab failed: %d\n", ret);
+        tevent_req_error(req, ret);
+        return;
+    }
+
+    ret = ipa_server_trust_add_step(req);
+    if (ret != EOK) {
+        DEBUG(SSSDBG_OP_FAILURE,
+              "ipa_server_trust_add_step failed: %d\n", ret);
+        tevent_req_error(req, ret);
+        return;
+    }
+
+    DEBUG(SSSDBG_TRACE_FUNC,
+          "Established trust context for %s\n", state->subdom->name);
+    tevent_req_done(req);
+}
+
+static errno_t ipa_server_trust_add_step(struct tevent_req *req)
+{
+    struct ipa_ad_server_ctx *trust_ctx;
+    struct ad_id_ctx *ad_id_ctx;
+    errno_t ret;
+    struct ipa_server_trust_add_state *state =
+            tevent_req_data(req, struct ipa_server_trust_add_state);
+
+    ret = ipa_ad_ctx_new(state->be_ctx, state->id_ctx, state->subdom, &ad_id_ctx);
+    if (ret != EOK) {
+        DEBUG(SSSDBG_OP_FAILURE,
+              "Cannot create ad_id_ctx for subdomain %s\n", state->subdom->name);
+        return ret;
+    }
+
+    trust_ctx = talloc(state->id_ctx->server_mode, struct ipa_ad_server_ctx);
     if (trust_ctx == NULL) {
-        ret = ENOMEM;
-        goto immediate;
+        return ENOMEM;
     }
-    trust_ctx->dom = subdom;
+    trust_ctx->dom = state->subdom;
     trust_ctx->ad_id_ctx = ad_id_ctx;
 
-    DLIST_ADD(id_ctx->server_mode->trusts, trust_ctx);
-    ret = EOK;
-    goto immediate;
-
-    return req;
-
-immediate:
-    if (ret != EOK) {
-        tevent_req_error(req, ret);
-    } else {
-        tevent_req_done(req);
-    }
-    tevent_req_post(req, ev);
-    return req;
+    DLIST_ADD(state->id_ctx->server_mode->trusts, trust_ctx);
+    return EOK;
 }
 
 static errno_t ipa_server_trust_add_recv(struct tevent_req *req)
diff --git a/src/tests/cmocka/test_ipa_subdomains_server.c b/src/tests/cmocka/test_ipa_subdomains_server.c
index 279784cfc3639a76cd72bd3669fa040e7ba9e6a3..96a0c3621c8234e3356e8aae2b9b7ecbf01edd9e 100644
--- a/src/tests/cmocka/test_ipa_subdomains_server.c
+++ b/src/tests/cmocka/test_ipa_subdomains_server.c
@@ -41,6 +41,7 @@
 
 #define DOM_REALM       "DOM.MAIN"
 #define HOSTNAME        "ipaserver.dom.main"
+#define DOM_FLAT        "DOM"
 
 #define TEST_AUTHID       "host/"HOSTNAME
 #define KEYTAB_TEST_PRINC TEST_AUTHID"@"DOM_REALM
@@ -61,11 +62,34 @@
 #define TEST_DOM_NAME "ipa_subdom_server_test"
 #define TEST_ID_PROVIDER "ipa"
 
+#define ONEWAY_KEYTAB   TEST_DIR"/"SUBDOM_REALM".keytab"
+#define ONEWAY_AUTHID   DOM_FLAT"$@"SUBDOM_REALM
+
 krb5_error_code __wrap_krb5_kt_default(krb5_context context, krb5_keytab *id)
 {
     return krb5_kt_resolve(context, KEYTAB_PATH, id);
 }
 
+static void create_dummy_keytab(void)
+{
+    int fd;
+    errno_t ret;
+
+    assert_non_null(ONEWAY_KEYTAB);
+    fd = open(ONEWAY_KEYTAB,  O_WRONLY | O_CREAT | O_TRUNC, 0600);
+    assert_int_not_equal(fd, -1);
+    close(fd);
+
+    ret = access(ONEWAY_KEYTAB, R_OK);
+    assert_int_equal(ret, 0);
+}
+
+int __wrap_execl(const char *path, const char *arg, ...)
+{
+    create_dummy_keytab();
+    _exit(0);
+}
+
 struct trust_test_ctx {
     struct sss_test_ctx *tctx;
     struct be_ctx *be_ctx;
@@ -140,7 +164,8 @@ static struct ipa_server_mode_ctx *mock_server_mode(TALLOC_CTX *mem_ctx)
     return server_mode;
 }
 
-static void add_test_subdomains(struct trust_test_ctx *test_ctx)
+static void add_test_subdomains(struct trust_test_ctx *test_ctx,
+                                enum trust_direction direction)
 {
     errno_t
 
@@ -149,14 +174,14 @@ static void add_test_subdomains(struct trust_test_ctx *test_ctx)
                                 SUBDOM_NAME, SUBDOM_REALM,
                                 NULL, SUBDOM_SID,
                                 true, false, SUBDOM_REALM,
-                                SD_TRUST_DIRECTION_TWOWAY);
+                                direction);
     assert_int_equal(ret, EOK);
 
     ret = sysdb_subdomain_store(test_ctx->tctx->sysdb,
                                 CHILD_NAME, CHILD_REALM,
                                 CHILD_FLAT, CHILD_SID,
                                 true, false, SUBDOM_REALM,
-                                SD_TRUST_DIRECTION_TWOWAY);
+                                direction);
     assert_int_equal(ret, EOK);
 
     ret = sysdb_update_subdomains(test_ctx->tctx->dom);
@@ -180,6 +205,8 @@ static int test_ipa_server_create_trusts_setup(void **state)
                                          TEST_CONF_DB, TEST_DOM_NAME,
                                          TEST_ID_PROVIDER, params);
     assert_non_null(test_ctx->tctx);
+    test_ctx->tctx->dom->flat_name = discard_const(DOM_FLAT);
+    test_ctx->tctx->dom->realm = discard_const(DOM_REALM);
 
     test_ctx->be_ctx = mock_be_ctx(test_ctx, test_ctx->tctx);
     assert_non_null(test_ctx->be_ctx);
@@ -209,6 +236,9 @@ static int test_ipa_server_create_trusts_teardown(void **state)
     ret = unlink(KEYTAB_PATH);
     assert_int_equal(ret, 0);
 
+    unlink(ONEWAY_KEYTAB);
+    /* Ignore failures */
+
     talloc_free(test_ctx);
     return 0;
 }
@@ -248,7 +278,7 @@ static void test_ipa_server_create_trusts_none(struct tevent_req *req)
     assert_int_equal(ret, EOK);
 
     /* Add two subdomains */
-    add_test_subdomains(test_ctx);
+    add_test_subdomains(test_ctx, SD_TRUST_DIRECTION_TWOWAY);
 
     req = ipa_server_create_trusts_send(test_ctx,
                                         test_ctx->tctx->ev,
@@ -404,7 +434,7 @@ static void test_ipa_server_trust_init(void **state)
     struct tevent_timer *timeout_handler;
     struct timeval tv;
 
-    add_test_subdomains(test_ctx);
+    add_test_subdomains(test_ctx, SD_TRUST_DIRECTION_TWOWAY);
 
     ret = ipa_ad_subdom_init(test_ctx->be_ctx, test_ctx->ipa_ctx);
     assert_int_equal(ret, EOK);
@@ -553,6 +583,116 @@ static void test_get_trust_direction_notset_member(void **state)
     assert_int_equal(dir, SD_TRUST_DIRECTION_NOT_SET);
 }
 
+static void test_ipa_server_create_trusts_oneway(struct tevent_req *req);
+
+static void test_ipa_server_create_oneway(void **state)
+{
+    struct trust_test_ctx *test_ctx =
+        talloc_get_type(*state, struct trust_test_ctx);
+    struct tevent_req *req;
+    errno_t ret;
+
+    add_test_subdomains(test_ctx, SD_TRUST_DIRECTION_OUTBOUND);
+
+    ret = access(ONEWAY_KEYTAB, R_OK);
+    assert_int_not_equal(ret, 0);
+
+    assert_null(test_ctx->ipa_ctx->server_mode->trusts);
+
+    req = ipa_server_create_trusts_send(test_ctx,
+                                        test_ctx->tctx->ev,
+                                        test_ctx->be_ctx,
+                                        test_ctx->ipa_ctx,
+                                        test_ctx->be_ctx->domain);
+    assert_non_null(req);
+
+    tevent_req_set_callback(req, test_ipa_server_create_trusts_oneway, test_ctx);
+
+    ret = test_ev_loop(test_ctx->tctx);
+    assert_int_equal(ret, ERR_OK);
+}
+
+static void test_ipa_server_create_trusts_oneway(struct tevent_req *req)
+{
+    struct trust_test_ctx *test_ctx = \
+        tevent_req_callback_data(req, struct trust_test_ctx);
+    errno_t ret;
+
+    ret = ipa_server_create_trusts_recv(req);
+    talloc_zfree(req);
+    assert_int_equal(ret, EOK);
+
+    ret = access(ONEWAY_KEYTAB, R_OK);
+    assert_int_equal(ret, 0);
+
+    /* Trust object should be around now */
+    assert_non_null(test_ctx->ipa_ctx->server_mode->trusts);
+    assert_non_null(test_ctx->ipa_ctx->server_mode->trusts->next);
+
+    test_ipa_server_create_trusts_finish(test_ctx);
+}
+
+static void test_ipa_server_create_oneway_kt_exists(void **state)
+{
+    struct trust_test_ctx *test_ctx =
+        talloc_get_type(*state, struct trust_test_ctx);
+    struct tevent_req *req;
+    errno_t ret;
+
+    add_test_subdomains(test_ctx, SD_TRUST_DIRECTION_OUTBOUND);
+
+    create_dummy_keytab();
+    ret = access(ONEWAY_KEYTAB, R_OK);
+    assert_int_equal(ret, 0);
+
+    assert_null(test_ctx->ipa_ctx->server_mode->trusts);
+
+    req = ipa_server_create_trusts_send(test_ctx,
+                                        test_ctx->tctx->ev,
+                                        test_ctx->be_ctx,
+                                        test_ctx->ipa_ctx,
+                                        test_ctx->be_ctx->domain);
+    assert_non_null(req);
+
+    tevent_req_set_callback(req, test_ipa_server_create_trusts_oneway, test_ctx);
+
+    ret = test_ev_loop(test_ctx->tctx);
+    assert_int_equal(ret, ERR_OK);
+}
+
+static void test_ipa_server_trust_oneway_init(void **state)
+{
+    struct trust_test_ctx *test_ctx =
+        talloc_get_type(*state, struct trust_test_ctx);
+    errno_t ret;
+    struct tevent_timer *timeout_handler;
+    struct timeval tv;
+
+    add_test_subdomains(test_ctx, SD_TRUST_DIRECTION_OUTBOUND);
+
+    ret = ipa_ad_subdom_init(test_ctx->be_ctx, test_ctx->ipa_ctx);
+    assert_int_equal(ret, EOK);
+
+    tv = tevent_timeval_current_ofs(1, 0);
+    timeout_handler = tevent_add_timer(test_ctx->tctx->ev, test_ctx, tv,
+                                       ipa_server_init_done, test_ctx);
+    assert_non_null(timeout_handler);
+
+    ret = test_ev_loop(test_ctx->tctx);
+    assert_int_equal(ret, ERR_OK);
+
+    assert_non_null(test_ctx->ipa_ctx->server_mode->trusts);
+}
+
+static void test_ipa_trust_dir2str(void **state)
+{
+    /* Just make sure the caller can rely on getting a valid string.. */
+    assert_non_null(ipa_trust_dir2str(SD_TRUST_DIRECTION_NOT_SET));
+    assert_non_null(ipa_trust_dir2str(SD_TRUST_DIRECTION_OUTBOUND));
+    assert_non_null(ipa_trust_dir2str(SD_TRUST_DIRECTION_TWOWAY));
+    assert_non_null(ipa_trust_dir2str(999));
+}
+
 int main(int argc, const char *argv[])
 {
     int rv;
@@ -565,6 +705,18 @@ int main(int argc, const char *argv[])
     };
 
     const struct CMUnitTest tests[] = {
+        cmocka_unit_test(test_ipa_trust_dir2str),
+
+        cmocka_unit_test_setup_teardown(test_ipa_server_create_oneway,
+                                        test_ipa_server_create_trusts_setup,
+                                        test_ipa_server_create_trusts_teardown),
+        cmocka_unit_test_setup_teardown(test_ipa_server_create_oneway_kt_exists,
+                                        test_ipa_server_create_trusts_setup,
+                                        test_ipa_server_create_trusts_teardown),
+        cmocka_unit_test_setup_teardown(test_ipa_server_trust_oneway_init,
+                                        test_ipa_server_create_trusts_setup,
+                                        test_ipa_server_create_trusts_teardown),
+
         cmocka_unit_test_setup_teardown(test_ipa_server_trust_init,
                                         test_ipa_server_create_trusts_setup,
                                         test_ipa_server_create_trusts_teardown),
diff --git a/src/util/util_errors.c b/src/util/util_errors.c
index 2323d2f0ede9ad9c1f3246cde7c286aeef211449..9d94fb624e21568f33d8979b04244fc83c1b918c 100644
--- a/src/util/util_errors.c
+++ b/src/util/util_errors.c
@@ -75,6 +75,8 @@ struct err_string error_to_str[] = {
     { "Unexpected cache entry type" }, /* ERR_UNEXPECTED_ENTRY_TYPE */
     { "Failed to resolve one of user groups" }, /* ERR_SIMPLE_GROUPS_MISSING */
     { "Unsupported trust direction" }, /* ERR_TRUST_NOT_SUPPORTED */
+    { "Retrieving keytab failed" }, /* ERR_IPA_GETKEYTAB_FAILED */
+    { "Trusted forest root unknown" }, /* ERR_TRUST_FOREST_UNKNOWN */
     { "ERR_LAST" } /* ERR_LAST */
 };
 
diff --git a/src/util/util_errors.h b/src/util/util_errors.h
index 154006fa124ce1eab6fa3034586af0c60d03be1f..35a730bed98fb8e482cbd94443c93e67bdb50d71 100644
--- a/src/util/util_errors.h
+++ b/src/util/util_errors.h
@@ -97,6 +97,8 @@ enum sssd_errors {
     ERR_UNEXPECTED_ENTRY_TYPE,
     ERR_SIMPLE_GROUPS_MISSING,
     ERR_TRUST_NOT_SUPPORTED,
+    ERR_IPA_GETKEYTAB_FAILED,
+    ERR_TRUST_FOREST_UNKNOWN,
     ERR_LAST            /* ALWAYS LAST */
 };
 
-- 
2.1.0

-------------- next part --------------
>From 3ec4b7d858621b0840f1929cb9fe5c9f1c408b06 Mon Sep 17 00:00:00 2001
From: Jakub Hrozek <jhrozek at redhat.com>
Date: Tue, 26 May 2015 12:47:08 +0200
Subject: [PATCH 21/29] NOSUBMIT: Fake trust direction

---
 src/providers/ipa/ipa_subdomains_server.c | 36 +++++++++++++++++++++++++++++++
 1 file changed, 36 insertions(+)

diff --git a/src/providers/ipa/ipa_subdomains_server.c b/src/providers/ipa/ipa_subdomains_server.c
index eb4867c2665084ac17472b3370ef3007a9140607..83a3018dec29a847478c163d4cce2ce623d60c0f 100644
--- a/src/providers/ipa/ipa_subdomains_server.c
+++ b/src/providers/ipa/ipa_subdomains_server.c
@@ -50,6 +50,39 @@ static char *subdomain_trust_princ(TALLOC_CTX *mem_ctx,
     return talloc_asprintf(mem_ctx, "%s$@%s", sd->parent->flat_name, forest);
 }
 
+struct fake_trust_dir {
+    const char *name;
+    uint32_t direction;
+};
+
+struct fake_trust_dir fakedirs[] = {
+ { .name = "AD.EXAMPLE.COM", .direction = LSA_TRUST_DIRECTION_OUTBOUND },
+ { .name = NULL, .direction = 0 },
+};
+
+static errno_t NOSUBMIT_fake_trust_direction(struct sysdb_attrs *sd,
+                                             errno_t orig_ret,
+                                             uint32_t direction,
+                                             uint32_t *direction_out)
+{
+    errno_t ret;
+    const char *name;
+    size_t c;
+
+    ret = sysdb_attrs_get_string(sd, "cn", &name);
+    if (ret == EOK) {
+        for (c = 0; fakedirs[c].name != NULL; c++) {
+            if (strcasecmp(name, fakedirs[c].name) == 0) {
+                *direction_out = fakedirs[c].direction;
+                return ret;
+            }
+        }
+    }
+
+    *direction_out = direction;
+    return orig_ret;
+}
+
 static enum trust_direction default_direction(TALLOC_CTX *mem_ctx,
                                               struct ldb_context *ldb_ctx,
                                               struct sysdb_attrs *attrs)
@@ -89,6 +122,9 @@ errno_t ipa_server_get_trust_direction(struct sysdb_attrs *sd,
                                    &ipa_trust_direction);
     DEBUG(SSSDBG_TRACE_INTERNAL,
           "Raw %s value: %d\n", IPA_TRUST_DIRECTION, ipa_trust_direction);
+    ret = NOSUBMIT_fake_trust_direction(sd, ret, ipa_trust_direction, &ipa_trust_direction);
+    DEBUG(SSSDBG_TRACE_INTERNAL,
+          "Faked %s value: %d\n", IPA_TRUST_DIRECTION, ipa_trust_direction);
     if (ret == ENOENT) {
         direction = default_direction(sd, ldb_ctx, sd);
     } else if (ret == EOK) {
-- 
2.1.0

-------------- next part --------------
>From 97450da7ad3324ef3141006c5dc9a189bb7b5a5d Mon Sep 17 00:00:00 2001
From: Jakub Hrozek <jhrozek at redhat.com>
Date: Wed, 13 May 2015 12:11:32 +0200
Subject: [PATCH 22/29] NOSUBMIT: Use DM creds to fetch keytab

---
 src/providers/ipa/ipa_subdomains_server.c | 5 +++++
 1 file changed, 5 insertions(+)

diff --git a/src/providers/ipa/ipa_subdomains_server.c b/src/providers/ipa/ipa_subdomains_server.c
index 83a3018dec29a847478c163d4cce2ce623d60c0f..508e2f2a16a8d45ea0f91798f156cab75ae399dd 100644
--- a/src/providers/ipa/ipa_subdomains_server.c
+++ b/src/providers/ipa/ipa_subdomains_server.c
@@ -419,8 +419,13 @@ static void ipa_getkeytab_exec(const char *server,
     }
 
     errno = 0;
+#if 0
     execl(IPA_GETKEYTAB_PATH, IPA_GETKEYTAB_PATH,
           "-r", "-s", server, "-p", principal, "-k", keytab_path, NULL);
+#endif
+    execl(IPA_GETKEYTAB_PATH, IPA_GETKEYTAB_PATH,
+          "-r", "-s", server, "-p", principal, "-k", keytab_path,
+          "-Dcn=Directory manager", "-wblablabla", NULL);
 
     /* The child should never end up here */
     ret = errno;
-- 
2.1.0

-------------- next part --------------
>From 95b9cec78eaba2d0d02211635c12dd449e73fbc1 Mon Sep 17 00:00:00 2001
From: Jakub Hrozek <jhrozek at redhat.com>
Date: Fri, 15 May 2015 12:58:19 +0200
Subject: [PATCH 23/29] AD: Rename ad_set_ad_id_options to ad_set_sdap_options

Related:
    https://fedorahosted.org/sssd/ticket/2638
The function sets SDAP related options based on the AD ID context
options. The name should reflect what the function does.
---
 src/providers/ad/ad_common.c | 12 ++++++------
 1 file changed, 6 insertions(+), 6 deletions(-)

diff --git a/src/providers/ad/ad_common.c b/src/providers/ad/ad_common.c
index 5eeb8dd74d1df89a1a0afa50560b8341b0088778..4ae08c5a17916df59705c4c3b0999be2c2de1e03 100644
--- a/src/providers/ad/ad_common.c
+++ b/src/providers/ad/ad_common.c
@@ -30,8 +30,8 @@ struct ad_server_data {
 };
 
 errno_t ad_set_search_bases(struct sdap_options *id_opts);
-static errno_t ad_set_ad_id_options(struct ad_options *ad_opts,
-                                    struct sdap_options *id_opts);
+static errno_t ad_set_sdap_options(struct ad_options *ad_opts,
+                                   struct sdap_options *id_opts);
 
 static struct sdap_options *
 ad_create_default_sdap_options(TALLOC_CTX *mem_ctx)
@@ -148,7 +148,7 @@ ad_create_default_options(TALLOC_CTX *mem_ctx,
         return NULL;
     }
 
-    ret = ad_set_ad_id_options(ad_options, ad_options->id);
+    ret = ad_set_sdap_options(ad_options, ad_options->id);
     if (ret != EOK) {
         talloc_free(ad_options);
         return NULL;
@@ -831,8 +831,8 @@ done:
 }
 
 static errno_t
-ad_set_ad_id_options(struct ad_options *ad_opts,
-                     struct sdap_options *id_opts)
+ad_set_sdap_options(struct ad_options *ad_opts,
+                    struct sdap_options *id_opts)
 {
     errno_t ret;
     char *krb5_realm;
@@ -910,7 +910,7 @@ ad_get_id_options(struct ad_options *ad_opts,
         return ENOMEM;
     }
 
-    ret = ad_set_ad_id_options(ad_opts, id_opts);
+    ret = ad_set_sdap_options(ad_opts, id_opts);
     if (ret != EOK) {
         talloc_free(id_opts);
         return ret;
-- 
2.1.0

-------------- next part --------------
>From 0827bfdc5c7b288646ed12e1d2169365702ae556 Mon Sep 17 00:00:00 2001
From: Jakub Hrozek <jhrozek at redhat.com>
Date: Fri, 15 May 2015 13:02:14 +0200
Subject: [PATCH 24/29] AD: Rename ad_create_default_options to
 ad_create_2way_trust_options

Related:
    https://fedorahosted.org/sssd/ticket/2638

Better reflects what's going on in the function. Also adds a unit test.
---
 Makefile.am                               |  16 +++-
 src/providers/ad/ad_common.c              |   6 +-
 src/providers/ad/ad_common.h              |   6 +-
 src/providers/ad/ad_subdomains.c          |   2 +-
 src/providers/ipa/ipa_subdomains_server.c |   4 +-
 src/tests/cmocka/test_ad_common.c         | 151 +++++++++++++++++++++++++-----
 6 files changed, 149 insertions(+), 36 deletions(-)

diff --git a/Makefile.am b/Makefile.am
index bdb5c8a4f827034397545308deb546367242709a..244a877e3f8b88e83af0148a95b445cc42563067 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -2126,12 +2126,18 @@ ad_gpo_tests_LDADD = \
 
 ad_common_tests_SOURCES = \
     $(sssd_be_SOURCES) \
-    src/tests/cmocka/test_ad_common.c
+    $(libsss_krb5_common_la_SOURCES) \
+    src/tests/cmocka/common_mock_krb5.c \
+    src/tests/cmocka/test_ad_common.c \
+    $(NULL)
 ad_common_tests_CFLAGS = \
     $(AM_CFLAGS) \
-    -DUNIT_TESTING
+    -DUNIT_TESTING \
+    $(NULL)
 ad_common_tests_LDFLAGS = \
-    -Wl,-wrap,sdap_set_sasl_options
+    -Wl,-wrap,sdap_set_sasl_options \
+    -Wl,-wrap,krb5_kt_default \
+    $(NULL)
 ad_common_tests_LDADD = \
     $(PAM_LIBS) \
     $(CMOCKA_LIBS) \
@@ -2141,8 +2147,8 @@ ad_common_tests_LDADD = \
     $(SSSD_INTERNAL_LTLIBS) \
     libsss_ldap_common.la \
     libsss_idmap.la \
-    libsss_krb5_common.la \
-    libsss_test_common.la
+    libsss_test_common.la \
+    $(NULL)
 
 dp_opt_tests_SOURCES = \
     src/providers/data_provider_opts.c \
diff --git a/src/providers/ad/ad_common.c b/src/providers/ad/ad_common.c
index 4ae08c5a17916df59705c4c3b0999be2c2de1e03..e945958fc7a1b2f2671f7994b3b2d89d523ca8c5 100644
--- a/src/providers/ad/ad_common.c
+++ b/src/providers/ad/ad_common.c
@@ -108,9 +108,9 @@ fail:
 }
 
 struct ad_options *
-ad_create_default_options(TALLOC_CTX *mem_ctx,
-                          const char *realm,
-                          const char *hostname)
+ad_create_2way_trust_options(TALLOC_CTX *mem_ctx,
+                             const char *realm,
+                             const char *hostname)
 {
     struct ad_options *ad_options;
     errno_t ret;
diff --git a/src/providers/ad/ad_common.h b/src/providers/ad/ad_common.h
index dcd70bf94c682fde8b7d17a323ccc3947c659d73..5be2bcd84c8340aa0b2abe0028aac6b8f0e9fb51 100644
--- a/src/providers/ad/ad_common.h
+++ b/src/providers/ad/ad_common.h
@@ -103,9 +103,9 @@ ad_get_common_options(TALLOC_CTX *mem_ctx,
                       struct sss_domain_info *dom,
                       struct ad_options **_opts);
 
-struct ad_options *ad_create_default_options(TALLOC_CTX *mem_ctx,
-                                             const char *realm,
-                                             const char *hostname);
+struct ad_options *ad_create_2way_trust_options(TALLOC_CTX *mem_ctx,
+                                                const char *realm,
+                                                const char *hostname);
 
 errno_t
 ad_failover_init(TALLOC_CTX *mem_ctx, struct be_ctx *ctx,
diff --git a/src/providers/ad/ad_subdomains.c b/src/providers/ad/ad_subdomains.c
index 8e69cef18cd2f64ee3860bcc328b76789312ccad..40eb9aa43a49893efcaf861ef04f65bb4a02f8b5 100644
--- a/src/providers/ad/ad_subdomains.c
+++ b/src/providers/ad/ad_subdomains.c
@@ -115,7 +115,7 @@ ad_subdom_ad_ctx_new(struct be_ctx *be_ctx,
         return EINVAL;
     }
 
-    ad_options = ad_create_default_options(id_ctx, realm, hostname);
+    ad_options = ad_create_2way_trust_options(id_ctx, realm, hostname);
     if (ad_options == NULL) {
         DEBUG(SSSDBG_OP_FAILURE, "Cannot initialize AD options\n");
         talloc_free(ad_options);
diff --git a/src/providers/ipa/ipa_subdomains_server.c b/src/providers/ipa/ipa_subdomains_server.c
index 508e2f2a16a8d45ea0f91798f156cab75ae399dd..791565edf25cc73db1f840f48b7051762adf8bde 100644
--- a/src/providers/ipa/ipa_subdomains_server.c
+++ b/src/providers/ipa/ipa_subdomains_server.c
@@ -181,8 +181,8 @@ ipa_ad_ctx_new(struct be_ctx *be_ctx,
     errno_t ret;
     const char *extra_attrs;
 
-    ad_options = ad_create_default_options(id_ctx, id_ctx->server_mode->realm,
-                                           id_ctx->server_mode->hostname);
+    ad_options = ad_create_2way_trust_options(id_ctx, id_ctx->server_mode->realm,
+                                              id_ctx->server_mode->hostname);
     if (ad_options == NULL) {
         DEBUG(SSSDBG_OP_FAILURE, "Cannot initialize AD options\n");
         talloc_free(ad_options);
diff --git a/src/tests/cmocka/test_ad_common.c b/src/tests/cmocka/test_ad_common.c
index 1c44bc34b9350c4c7bca1dfb3fedd3184d7f14f2..0013d7833f7065f56a181c8affab276c30c3ddc2 100644
--- a/src/tests/cmocka/test_ad_common.c
+++ b/src/tests/cmocka/test_ad_common.c
@@ -33,12 +33,24 @@
 #include "providers/ad/ad_common.c"
 
 #include "tests/cmocka/common_mock.h"
+#include "tests/cmocka/common_mock_krb5.h"
 
 #define DOMNAME     "domname"
 #define SUBDOMNAME  "sub."DOMNAME
 #define REALMNAME   DOMNAME
 #define HOST_NAME   "ad."REALMNAME
 
+#define TEST_AUTHID       "host/"HOST_NAME
+#define KEYTAB_TEST_PRINC TEST_AUTHID"@"REALMNAME
+#define KEYTAB_PATH       TEST_DIR"/keytab_test.keytab"
+
+static bool call_real_sasl_options;
+
+krb5_error_code __wrap_krb5_kt_default(krb5_context context, krb5_keytab *id)
+{
+    return krb5_kt_resolve(context, KEYTAB_PATH, id);
+}
+
 struct ad_common_test_ctx {
     struct ad_id_ctx *ad_ctx;
     struct ad_id_ctx *subdom_ad_ctx;
@@ -47,15 +59,9 @@ struct ad_common_test_ctx {
     struct sss_domain_info *subdom;
 };
 
-static int
-ad_common_test_setup(void **state)
+static int test_ad_common_setup(void **state)
 {
     struct ad_common_test_ctx *test_ctx;
-    errno_t ret;
-    struct sdap_domain *sdom;
-    struct ad_id_ctx *ad_ctx;
-    struct ad_id_ctx *subdom_ad_ctx;
-    struct sdap_id_conn_ctx *subdom_ldap_ctx;
 
     assert_true(leak_check_setup());
     check_leaks_push(global_talloc_context);
@@ -72,11 +78,96 @@ ad_common_test_setup(void **state)
     test_ctx->subdom->name = discard_const(SUBDOMNAME);
     test_ctx->subdom->parent = test_ctx->dom;
 
-    ad_ctx = talloc_zero(test_ctx, struct ad_id_ctx);
-    assert_non_null(ad_ctx);
+    test_ctx->ad_ctx = talloc_zero(test_ctx, struct ad_id_ctx);
+    assert_non_null(test_ctx->ad_ctx);
 
-    ad_ctx->ad_options = ad_create_default_options(ad_ctx,
-                                                   REALMNAME, HOST_NAME);
+    check_leaks_push(test_ctx);
+    *state = test_ctx;
+    return 0;
+}
+
+static int test_ad_common_teardown(void **state)
+{
+    struct ad_common_test_ctx *test_ctx = talloc_get_type(*state,
+                                                  struct ad_common_test_ctx);
+    assert_non_null(test_ctx);
+
+    assert_true(check_leaks_pop(test_ctx) == true);
+    talloc_free(test_ctx);
+    assert_true(check_leaks_pop(global_talloc_context) == true);
+    assert_true(leak_check_teardown());
+
+    return 0;
+}
+
+static void test_ad_create_2way_trust_options(void **state)
+{
+    struct ad_common_test_ctx *test_ctx = talloc_get_type(*state,
+                                                  struct ad_common_test_ctx);
+    const char *s;
+
+    call_real_sasl_options = true;
+    mock_keytab_with_contents(test_ctx, KEYTAB_PATH, KEYTAB_TEST_PRINC);
+
+    test_ctx->ad_ctx->ad_options = ad_create_2way_trust_options(
+                                                            test_ctx->ad_ctx,
+                                                            REALMNAME,
+                                                            HOST_NAME);
+    assert_non_null(test_ctx->ad_ctx->ad_options);
+
+    assert_int_equal(test_ctx->ad_ctx->ad_options->id->schema_type,
+                     SDAP_SCHEMA_AD);
+
+    s = dp_opt_get_string(test_ctx->ad_ctx->ad_options->basic,
+                          AD_KRB5_REALM);
+    assert_non_null(s);
+    assert_string_equal(s, REALMNAME);
+
+    s = dp_opt_get_string(test_ctx->ad_ctx->ad_options->basic,
+                          AD_HOSTNAME);
+    assert_non_null(s);
+    assert_string_equal(s, HOST_NAME);
+
+    s = dp_opt_get_string(test_ctx->ad_ctx->ad_options->id->basic,
+                          SDAP_KRB5_KEYTAB);
+    assert_null(s); /* This is the system keytab */
+
+    s = dp_opt_get_string(test_ctx->ad_ctx->ad_options->id->basic,
+                          SDAP_SASL_REALM);
+    assert_non_null(s);
+    assert_string_equal(s, REALMNAME);
+
+    s = dp_opt_get_string(test_ctx->ad_ctx->ad_options->id->basic,
+                          SDAP_KRB5_REALM);
+    assert_non_null(s);
+    assert_string_equal(s, REALMNAME);
+
+    s = dp_opt_get_string(test_ctx->ad_ctx->ad_options->id->basic,
+                          SDAP_SASL_AUTHID);
+    assert_non_null(s);
+    assert_string_equal(s, TEST_AUTHID);
+
+    talloc_free(test_ctx->ad_ctx->ad_options);
+}
+
+static int
+test_ldap_conn_setup(void **state)
+{
+    struct ad_common_test_ctx *test_ctx;
+    errno_t ret;
+    struct sdap_domain *sdom;
+    struct ad_id_ctx *ad_ctx;
+    struct ad_id_ctx *subdom_ad_ctx;
+    struct sdap_id_conn_ctx *subdom_ldap_ctx;
+
+    ret = test_ad_common_setup((void **) &test_ctx);
+    assert_int_equal(ret, EOK);
+
+    ad_ctx = test_ctx->ad_ctx;
+
+    ad_ctx->ad_options = ad_create_2way_trust_options(ad_ctx,
+                                                      REALMNAME,
+                                                      HOST_NAME);
     assert_non_null(ad_ctx->ad_options);
 
     ad_ctx->gc_ctx = talloc_zero(ad_ctx, struct sdap_id_conn_ctx);
@@ -107,35 +198,48 @@ ad_common_test_setup(void **state)
     assert_int_equal(ret, EOK);
     sdom->pvt = subdom_ad_ctx;
 
-    test_ctx->ad_ctx = ad_ctx;
     test_ctx->subdom_ad_ctx = subdom_ad_ctx;
 
-    check_leaks_push(test_ctx);
     *state = test_ctx;
     return 0;
 }
 
 static int
-ad_common_test_teardown(void **state)
+test_ldap_conn_teardown(void **state)
 {
     struct ad_common_test_ctx *test_ctx = talloc_get_type(*state,
                                                   struct ad_common_test_ctx);
     assert_non_null(test_ctx);
 
-    assert_true(check_leaks_pop(test_ctx) == true);
-    talloc_free(test_ctx);
-    assert_true(check_leaks_pop(global_talloc_context) == true);
-    assert_true(leak_check_teardown());
+    talloc_free(test_ctx->subdom_ad_ctx);
+    talloc_free(test_ctx->ad_ctx->ad_options);
+    talloc_free(test_ctx->ad_ctx->gc_ctx);
+    talloc_free(test_ctx->ad_ctx->ldap_ctx);
+    talloc_free(test_ctx->ad_ctx->sdap_id_ctx);
+
+    test_ad_common_teardown((void **) &test_ctx);
     return 0;
 }
 
 errno_t
+__real_sdap_set_sasl_options(struct sdap_options *id_opts,
+                             char *default_primary,
+                             char *default_realm,
+                             const char *keytab_path);
+errno_t
 __wrap_sdap_set_sasl_options(struct sdap_options *id_opts,
                              char *default_primary,
                              char *default_realm,
                              const char *keytab_path)
 {
     /* Pretend SASL is fine */
+    if (call_real_sasl_options == true) {
+        return __real_sdap_set_sasl_options(id_opts,
+                                            default_primary,
+                                            default_realm,
+                                            keytab_path);
+    }
+
     return EOK;
 }
 
@@ -214,12 +318,15 @@ int main(int argc, const char *argv[])
     };
 
     const struct CMUnitTest tests[] = {
+        cmocka_unit_test_setup_teardown(test_ad_create_2way_trust_options,
+                                        test_ad_common_setup,
+                                        test_ad_common_teardown),
         cmocka_unit_test_setup_teardown(test_ldap_conn_list,
-                                        ad_common_test_setup,
-                                        ad_common_test_teardown),
+                                        test_ldap_conn_setup,
+                                        test_ldap_conn_teardown),
         cmocka_unit_test_setup_teardown(test_conn_list,
-                                        ad_common_test_setup,
-                                        ad_common_test_teardown),
+                                        test_ldap_conn_setup,
+                                        test_ldap_conn_teardown),
     };
 
     /* Set debug level to invalid value so we can deside if -d 0 was used. */
-- 
2.1.0

-------------- next part --------------
>From df7638a72b4e16400375db06bfbc57b787a2a71f Mon Sep 17 00:00:00 2001
From: Jakub Hrozek <jhrozek at redhat.com>
Date: Fri, 15 May 2015 13:17:50 +0200
Subject: [PATCH 25/29] AD: Split off ad_create_default_options

Related:
    https://fedorahosted.org/sssd/ticket/2638

Make the function reusable and add a simple unit test.
---
 src/providers/ad/ad_common.c      | 18 +++++++++++++++---
 src/providers/ad/ad_common.h      |  2 ++
 src/tests/cmocka/test_ad_common.c | 17 +++++++++++++++++
 3 files changed, 34 insertions(+), 3 deletions(-)

diff --git a/src/providers/ad/ad_common.c b/src/providers/ad/ad_common.c
index e945958fc7a1b2f2671f7994b3b2d89d523ca8c5..60b234dc22aa8f2975b45ffccc2c97f44d322b0b 100644
--- a/src/providers/ad/ad_common.c
+++ b/src/providers/ad/ad_common.c
@@ -108,9 +108,7 @@ fail:
 }
 
 struct ad_options *
-ad_create_2way_trust_options(TALLOC_CTX *mem_ctx,
-                             const char *realm,
-                             const char *hostname)
+ad_create_default_options(TALLOC_CTX *mem_ctx)
 {
     struct ad_options *ad_options;
     errno_t ret;
@@ -134,6 +132,20 @@ ad_create_2way_trust_options(TALLOC_CTX *mem_ctx,
         return NULL;
     }
 
+    return ad_options;
+}
+
+struct ad_options *
+ad_create_2way_trust_options(TALLOC_CTX *mem_ctx,
+                             const char *realm,
+                             const char *hostname)
+{
+    struct ad_options *ad_options;
+    errno_t ret;
+
+    ad_options = ad_create_default_options(mem_ctx);
+    if (ad_options == NULL) return NULL;
+
     ret = dp_opt_set_string(ad_options->basic, AD_KRB5_REALM, realm);
     if (ret != EOK) {
         DEBUG(SSSDBG_OP_FAILURE, "Cannot set AD domain\n");
diff --git a/src/providers/ad/ad_common.h b/src/providers/ad/ad_common.h
index 5be2bcd84c8340aa0b2abe0028aac6b8f0e9fb51..07b9d734f44636f3219ebfc95bd4792e96bee255 100644
--- a/src/providers/ad/ad_common.h
+++ b/src/providers/ad/ad_common.h
@@ -103,6 +103,8 @@ ad_get_common_options(TALLOC_CTX *mem_ctx,
                       struct sss_domain_info *dom,
                       struct ad_options **_opts);
 
+struct ad_options *ad_create_default_options(TALLOC_CTX *mem_ctx);
+
 struct ad_options *ad_create_2way_trust_options(TALLOC_CTX *mem_ctx,
                                                 const char *realm,
                                                 const char *hostname);
diff --git a/src/tests/cmocka/test_ad_common.c b/src/tests/cmocka/test_ad_common.c
index 0013d7833f7065f56a181c8affab276c30c3ddc2..de00e53f9c66224abee670b04656e38218d4f3e2 100644
--- a/src/tests/cmocka/test_ad_common.c
+++ b/src/tests/cmocka/test_ad_common.c
@@ -59,6 +59,22 @@ struct ad_common_test_ctx {
     struct sss_domain_info *subdom;
 };
 
+static void test_ad_create_default_options(void **state)
+{
+    struct ad_options *ad_options;
+    const char *s;
+
+    ad_options = ad_create_default_options(global_talloc_context);
+
+    assert_non_null(ad_options->basic);
+
+    /* Not too much to test here except some defaults */
+    s = dp_opt_get_string(ad_options->basic, AD_DOMAIN);
+    assert_null(s);
+
+    assert_non_null(ad_options->id);
+}
+
 static int test_ad_common_setup(void **state)
 {
     struct ad_common_test_ctx *test_ctx;
@@ -318,6 +334,7 @@ int main(int argc, const char *argv[])
     };
 
     const struct CMUnitTest tests[] = {
+        cmocka_unit_test(test_ad_create_default_options),
         cmocka_unit_test_setup_teardown(test_ad_create_2way_trust_options,
                                         test_ad_common_setup,
                                         test_ad_common_teardown),
-- 
2.1.0

-------------- next part --------------
>From a7aace841c3c554f881de945c301ca9de9abf628 Mon Sep 17 00:00:00 2001
From: Jakub Hrozek <jhrozek at redhat.com>
Date: Fri, 15 May 2015 13:35:22 +0200
Subject: [PATCH 26/29] IPA/AD: Set up AD domain in
 ad_create_2way_trust_options

Related:
    https://fedorahosted.org/sssd/ticket/2638

Removed code duplication. Amends unit test to make sure we don't
regress.
---
 src/providers/ad/ad_common.c              |  8 ++++++++
 src/providers/ad/ad_common.h              |  1 +
 src/providers/ad/ad_subdomains.c          | 14 ++++----------
 src/providers/ipa/ipa_subdomains_server.c | 15 +++++----------
 src/tests/cmocka/test_ad_common.c         |  7 +++++++
 5 files changed, 25 insertions(+), 20 deletions(-)

diff --git a/src/providers/ad/ad_common.c b/src/providers/ad/ad_common.c
index 60b234dc22aa8f2975b45ffccc2c97f44d322b0b..22af7cbd2689f8a12a712d78f617f56420daab10 100644
--- a/src/providers/ad/ad_common.c
+++ b/src/providers/ad/ad_common.c
@@ -138,6 +138,7 @@ ad_create_default_options(TALLOC_CTX *mem_ctx)
 struct ad_options *
 ad_create_2way_trust_options(TALLOC_CTX *mem_ctx,
                              const char *realm,
+                             const char *ad_domain,
                              const char *hostname)
 {
     struct ad_options *ad_options;
@@ -153,6 +154,13 @@ ad_create_2way_trust_options(TALLOC_CTX *mem_ctx,
         return NULL;
     }
 
+    ret = dp_opt_set_string(ad_options->basic, AD_DOMAIN, ad_domain);
+    if (ret != EOK) {
+        DEBUG(SSSDBG_OP_FAILURE, "Cannot set AD domain\n");
+        talloc_free(ad_options);
+        return NULL;
+    }
+
     ret = dp_opt_set_string(ad_options->basic, AD_HOSTNAME, hostname);
     if (ret != EOK) {
         DEBUG(SSSDBG_OP_FAILURE, "Cannot set AD domain\n");
diff --git a/src/providers/ad/ad_common.h b/src/providers/ad/ad_common.h
index 07b9d734f44636f3219ebfc95bd4792e96bee255..0766b4dc93dbbebd934daaa5e3d3bf4c13a0a518 100644
--- a/src/providers/ad/ad_common.h
+++ b/src/providers/ad/ad_common.h
@@ -107,6 +107,7 @@ struct ad_options *ad_create_default_options(TALLOC_CTX *mem_ctx);
 
 struct ad_options *ad_create_2way_trust_options(TALLOC_CTX *mem_ctx,
                                                 const char *realm,
+                                                const char *ad_domain,
                                                 const char *hostname);
 
 errno_t
diff --git a/src/providers/ad/ad_subdomains.c b/src/providers/ad/ad_subdomains.c
index 40eb9aa43a49893efcaf861ef04f65bb4a02f8b5..f7f448b35fa61ef99504b5e1762fa125086bb865 100644
--- a/src/providers/ad/ad_subdomains.c
+++ b/src/providers/ad/ad_subdomains.c
@@ -110,28 +110,22 @@ ad_subdom_ad_ctx_new(struct be_ctx *be_ctx,
 
     realm = dp_opt_get_cstring(id_ctx->ad_options->basic, AD_KRB5_REALM);
     hostname = dp_opt_get_cstring(id_ctx->ad_options->basic, AD_HOSTNAME);
-    if (realm == NULL || hostname == NULL) {
+    ad_domain = subdom->name;
+    if (realm == NULL || hostname == NULL || ad_domain == NULL) {
         DEBUG(SSSDBG_CONF_SETTINGS, "Missing realm or hostname.\n");
         return EINVAL;
     }
 
-    ad_options = ad_create_2way_trust_options(id_ctx, realm, hostname);
+    ad_options = ad_create_2way_trust_options(id_ctx, realm,
+                                              ad_domain, hostname);
     if (ad_options == NULL) {
         DEBUG(SSSDBG_OP_FAILURE, "Cannot initialize AD options\n");
         talloc_free(ad_options);
         return ENOMEM;
     }
 
-    ad_domain = subdom->name;
     ad_site_override = dp_opt_get_string(ad_options->basic, AD_SITE);
 
-    ret = dp_opt_set_string(ad_options->basic, AD_DOMAIN, ad_domain);
-    if (ret != EOK) {
-        DEBUG(SSSDBG_OP_FAILURE, "Cannot set AD domain\n");
-        talloc_free(ad_options);
-        return ret;
-    }
-
     gc_service_name = talloc_asprintf(ad_options, "%s%s", "gc_", subdom->name);
     if (gc_service_name == NULL) {
         talloc_free(ad_options);
diff --git a/src/providers/ipa/ipa_subdomains_server.c b/src/providers/ipa/ipa_subdomains_server.c
index 791565edf25cc73db1f840f48b7051762adf8bde..7a2ece8ab2172615e44721537ea1b3512569dc5a 100644
--- a/src/providers/ipa/ipa_subdomains_server.c
+++ b/src/providers/ipa/ipa_subdomains_server.c
@@ -175,13 +175,17 @@ ipa_ad_ctx_new(struct be_ctx *be_ctx,
     struct ad_id_ctx *ad_id_ctx;
     const char *gc_service_name;
     struct ad_srv_plugin_ctx *srv_ctx;
-    char *ad_domain;
+    const char *ad_domain;
     const char *ad_site_override;
     struct sdap_domain *sdom;
     errno_t ret;
     const char *extra_attrs;
 
+    ad_domain = subdom->name;
+    DEBUG(SSSDBG_TRACE_LIBS, "Setting up AD subdomain %s\n", subdom->name);
+
     ad_options = ad_create_2way_trust_options(id_ctx, id_ctx->server_mode->realm,
+                                              ad_domain,
                                               id_ctx->server_mode->hostname);
     if (ad_options == NULL) {
         DEBUG(SSSDBG_OP_FAILURE, "Cannot initialize AD options\n");
@@ -189,15 +193,6 @@ ipa_ad_ctx_new(struct be_ctx *be_ctx,
         return ENOMEM;
     }
 
-    ad_domain = subdom->name;
-
-    ret = dp_opt_set_string(ad_options->basic, AD_DOMAIN, ad_domain);
-    if (ret != EOK) {
-        DEBUG(SSSDBG_OP_FAILURE, "Cannot set AD domain\n");
-        talloc_free(ad_options);
-        return ret;
-    }
-
     ret = dp_opt_set_string(ad_options->basic, AD_KRB5_REALM,
                             id_ctx->server_mode->realm);
     if (ret != EOK) {
diff --git a/src/tests/cmocka/test_ad_common.c b/src/tests/cmocka/test_ad_common.c
index de00e53f9c66224abee670b04656e38218d4f3e2..c541b87db8e6de4a010383859f0335d78c675201 100644
--- a/src/tests/cmocka/test_ad_common.c
+++ b/src/tests/cmocka/test_ad_common.c
@@ -128,6 +128,7 @@ static void test_ad_create_2way_trust_options(void **state)
     test_ctx->ad_ctx->ad_options = ad_create_2way_trust_options(
                                                             test_ctx->ad_ctx,
                                                             REALMNAME,
+                                                            DOMNAME,
                                                             HOST_NAME);
     assert_non_null(test_ctx->ad_ctx->ad_options);
 
@@ -140,6 +141,11 @@ static void test_ad_create_2way_trust_options(void **state)
     assert_string_equal(s, REALMNAME);
 
     s = dp_opt_get_string(test_ctx->ad_ctx->ad_options->basic,
+                          AD_DOMAIN);
+    assert_non_null(s);
+    assert_string_equal(s, DOMNAME);
+
+    s = dp_opt_get_string(test_ctx->ad_ctx->ad_options->basic,
                           AD_HOSTNAME);
     assert_non_null(s);
     assert_string_equal(s, HOST_NAME);
@@ -183,6 +189,7 @@ test_ldap_conn_setup(void **state)
 
     ad_ctx->ad_options = ad_create_2way_trust_options(ad_ctx,
                                                       REALMNAME,
+                                                      DOMNAME,
                                                       HOST_NAME);
     assert_non_null(ad_ctx->ad_options);
 
-- 
2.1.0

-------------- next part --------------
>From 974472b54aede18cb6c0b4e0ba01790b3361dc0b Mon Sep 17 00:00:00 2001
From: Jakub Hrozek <jhrozek at redhat.com>
Date: Fri, 15 May 2015 13:36:03 +0200
Subject: [PATCH 27/29] IPA: Do not set AD_KRB5_REALM twice

Related:
    https://fedorahosted.org/sssd/ticket/2638

Both the AD common code and ipa_ad_ctx_new() used set AD_KRB5_REALM. As
verified by unit tests, we don't need to set the parameter twice.
---
 src/providers/ipa/ipa_subdomains_server.c | 8 --------
 1 file changed, 8 deletions(-)

diff --git a/src/providers/ipa/ipa_subdomains_server.c b/src/providers/ipa/ipa_subdomains_server.c
index 7a2ece8ab2172615e44721537ea1b3512569dc5a..cee03b5777e7dfb354b42e9c9d842d43c90a981c 100644
--- a/src/providers/ipa/ipa_subdomains_server.c
+++ b/src/providers/ipa/ipa_subdomains_server.c
@@ -193,14 +193,6 @@ ipa_ad_ctx_new(struct be_ctx *be_ctx,
         return ENOMEM;
     }
 
-    ret = dp_opt_set_string(ad_options->basic, AD_KRB5_REALM,
-                            id_ctx->server_mode->realm);
-    if (ret != EOK) {
-        DEBUG(SSSDBG_OP_FAILURE, "Cannot set AD realm\n");
-        talloc_free(ad_options);
-        return ret;
-    }
-
     extra_attrs = dp_opt_get_string(id_ctx->sdap_id_ctx->opts->basic,
                             SDAP_USER_EXTRA_ATTRS);
     if (extra_attrs != NULL) {
-- 
2.1.0

-------------- next part --------------
>From a81942df12b5dcf3afe60aeca6fa8d6b1dfd1adb Mon Sep 17 00:00:00 2001
From: Jakub Hrozek <jhrozek at redhat.com>
Date: Fri, 15 May 2015 14:13:40 +0200
Subject: [PATCH 28/29] AD: Add ad_create_1way_trust_options

Related:
    https://fedorahosted.org/sssd/ticket/2638

For one-way trusts we can assume that AD domain is the same as the
Kerberis realm. On the other hand, SASL realm and keytab path are
specified, unline two-way trusts that use the system keytab.

Includes a unit test.
---
 src/providers/ad/ad_common.c      | 97 +++++++++++++++++++++++++++++++++------
 src/providers/ad/ad_common.h      |  6 +++
 src/tests/cmocka/test_ad_common.c | 74 +++++++++++++++++++++++++++++
 3 files changed, 162 insertions(+), 15 deletions(-)

diff --git a/src/providers/ad/ad_common.c b/src/providers/ad/ad_common.c
index 22af7cbd2689f8a12a712d78f617f56420daab10..130cdeb613aae3843f7453a478815daaae6aab77 100644
--- a/src/providers/ad/ad_common.c
+++ b/src/providers/ad/ad_common.c
@@ -135,35 +135,102 @@ ad_create_default_options(TALLOC_CTX *mem_ctx)
     return ad_options;
 }
 
-struct ad_options *
-ad_create_2way_trust_options(TALLOC_CTX *mem_ctx,
-                             const char *realm,
-                             const char *ad_domain,
-                             const char *hostname)
+static errno_t
+set_common_ad_trust_opts(struct ad_options *ad_options,
+                         const char *realm,
+                         const char *ad_domain,
+                         const char *hostname)
 {
-    struct ad_options *ad_options;
     errno_t ret;
 
-    ad_options = ad_create_default_options(mem_ctx);
-    if (ad_options == NULL) return NULL;
-
     ret = dp_opt_set_string(ad_options->basic, AD_KRB5_REALM, realm);
     if (ret != EOK) {
-        DEBUG(SSSDBG_OP_FAILURE, "Cannot set AD domain\n");
-        talloc_free(ad_options);
-        return NULL;
+        DEBUG(SSSDBG_OP_FAILURE, "Cannot set AD krb5 realm\n");
+        return ret;
     }
 
     ret = dp_opt_set_string(ad_options->basic, AD_DOMAIN, ad_domain);
     if (ret != EOK) {
         DEBUG(SSSDBG_OP_FAILURE, "Cannot set AD domain\n");
-        talloc_free(ad_options);
-        return NULL;
+        return ret;
     }
 
     ret = dp_opt_set_string(ad_options->basic, AD_HOSTNAME, hostname);
     if (ret != EOK) {
-        DEBUG(SSSDBG_OP_FAILURE, "Cannot set AD domain\n");
+        DEBUG(SSSDBG_OP_FAILURE, "Cannot set AD hostname\n");
+        return ret;
+    }
+
+    return EOK;
+}
+
+struct ad_options *
+ad_create_2way_trust_options(TALLOC_CTX *mem_ctx,
+                             const char *realm,
+                             const char *ad_domain,
+                             const char *hostname)
+{
+    struct ad_options *ad_options;
+    errno_t ret;
+
+    ad_options = ad_create_default_options(mem_ctx);
+    if (ad_options == NULL) return NULL;
+
+    ret = set_common_ad_trust_opts(ad_options, realm, ad_domain, hostname);
+    if (ret != EOK) {
+        talloc_free(ad_options);
+        return NULL;
+    }
+
+    ret = ad_set_sdap_options(ad_options, ad_options->id);
+    if (ret != EOK) {
+        talloc_free(ad_options);
+        return NULL;
+    }
+
+    return ad_options;
+}
+
+struct ad_options *
+ad_create_1way_trust_options(TALLOC_CTX *mem_ctx,
+                             const char *ad_domain,
+                             const char *hostname,
+                             const char *keytab,
+                             const char *sasl_authid)
+{
+    struct ad_options *ad_options;
+    const char *realm;
+    errno_t ret;
+
+    ad_options = ad_create_default_options(mem_ctx);
+    if (ad_options == NULL) return NULL;
+
+    realm = get_uppercase_realm(ad_options, ad_domain);
+    if (!realm) {
+        talloc_free(ad_options);
+        return NULL;
+    }
+
+    ret = set_common_ad_trust_opts(ad_options, realm,
+                                   ad_domain, hostname);
+    if (ret != EOK) {
+        talloc_free(ad_options);
+        return NULL;
+    }
+
+    /* Set AD_KEYTAB to the special 1way keytab */
+    ret = dp_opt_set_string(ad_options->basic, AD_KEYTAB, keytab);
+    if (ret != EOK) {
+        DEBUG(SSSDBG_OP_FAILURE, "Cannot set trust keytab\n");
+        talloc_free(ad_options);
+        return NULL;
+    }
+
+    /* Set SDAP_SASL_AUTHID to the trust principal */
+    ret = dp_opt_set_string(ad_options->id->basic,
+                            SDAP_SASL_AUTHID, sasl_authid);
+    if (ret != EOK) {
+        DEBUG(SSSDBG_OP_FAILURE, "Cannot set SASL authid\n");
         talloc_free(ad_options);
         return NULL;
     }
diff --git a/src/providers/ad/ad_common.h b/src/providers/ad/ad_common.h
index 0766b4dc93dbbebd934daaa5e3d3bf4c13a0a518..817f5b42cad7cad6a88244fd43bd91a4358d56c0 100644
--- a/src/providers/ad/ad_common.h
+++ b/src/providers/ad/ad_common.h
@@ -110,6 +110,12 @@ struct ad_options *ad_create_2way_trust_options(TALLOC_CTX *mem_ctx,
                                                 const char *ad_domain,
                                                 const char *hostname);
 
+struct ad_options *ad_create_1way_trust_options(TALLOC_CTX *mem_ctx,
+                                                const char *ad_domain,
+                                                const char *hostname,
+                                                const char *keytab,
+                                                const char *sasl_authid);
+
 errno_t
 ad_failover_init(TALLOC_CTX *mem_ctx, struct be_ctx *ctx,
                  const char *primary_servers,
diff --git a/src/tests/cmocka/test_ad_common.c b/src/tests/cmocka/test_ad_common.c
index c541b87db8e6de4a010383859f0335d78c675201..985a05fae5a4d09ab102ed611c7d03ca8e4d955b 100644
--- a/src/tests/cmocka/test_ad_common.c
+++ b/src/tests/cmocka/test_ad_common.c
@@ -44,6 +44,13 @@
 #define KEYTAB_TEST_PRINC TEST_AUTHID"@"REALMNAME
 #define KEYTAB_PATH       TEST_DIR"/keytab_test.keytab"
 
+#define ONEWAY_DOMNAME     "ONEWAY"
+#define ONEWAY_HOST_NAME   "ad."ONEWAY_DOMNAME
+
+#define ONEWAY_KEYTAB_PATH       TEST_DIR"/oneway_test.keytab"
+#define ONEWAY_AUTHID            "host/"ONEWAY_HOST_NAME
+#define ONEWAY_TEST_PRINC        ONEWAY_AUTHID"@"ONEWAY_DOMNAME
+
 static bool call_real_sasl_options;
 
 krb5_error_code __wrap_krb5_kt_default(krb5_context context, krb5_keytab *id)
@@ -116,6 +123,70 @@ static int test_ad_common_teardown(void **state)
     return 0;
 }
 
+static void test_ad_create_1way_trust_options(void **state)
+{
+    struct ad_common_test_ctx *test_ctx = talloc_get_type(*state,
+                                                  struct ad_common_test_ctx);
+    const char *s;
+
+    call_real_sasl_options = true;
+    /* Make sure this is not the keytab that __wrap_krb5_kt_default uses */
+    mock_keytab_with_contents(test_ctx, ONEWAY_KEYTAB_PATH, ONEWAY_TEST_PRINC);
+
+    test_ctx->ad_ctx->ad_options = ad_create_1way_trust_options(
+                                                            test_ctx->ad_ctx,
+                                                            ONEWAY_DOMNAME,
+                                                            ONEWAY_HOST_NAME,
+                                                            ONEWAY_KEYTAB_PATH,
+                                                            ONEWAY_AUTHID);
+    assert_non_null(test_ctx->ad_ctx->ad_options);
+
+    assert_int_equal(test_ctx->ad_ctx->ad_options->id->schema_type,
+                     SDAP_SCHEMA_AD);
+
+    s = dp_opt_get_string(test_ctx->ad_ctx->ad_options->basic,
+                          AD_KRB5_REALM);
+    assert_non_null(s);
+    assert_string_equal(s, ONEWAY_DOMNAME);
+
+    s = dp_opt_get_string(test_ctx->ad_ctx->ad_options->basic,
+                          AD_DOMAIN);
+    assert_non_null(s);
+    assert_string_equal(s, ONEWAY_DOMNAME);
+
+    s = dp_opt_get_string(test_ctx->ad_ctx->ad_options->basic,
+                          AD_HOSTNAME);
+    assert_non_null(s);
+    assert_string_equal(s, ONEWAY_HOST_NAME);
+
+    s = dp_opt_get_string(test_ctx->ad_ctx->ad_options->basic,
+                          AD_KEYTAB);
+    assert_non_null(s);
+    assert_string_equal(s, ONEWAY_KEYTAB_PATH);
+
+    s = dp_opt_get_string(test_ctx->ad_ctx->ad_options->id->basic,
+                          SDAP_KRB5_KEYTAB);
+    assert_non_null(s);
+
+    s = dp_opt_get_string(test_ctx->ad_ctx->ad_options->id->basic,
+                          SDAP_SASL_REALM);
+    assert_non_null(s);
+    assert_string_equal(s, ONEWAY_DOMNAME);
+
+    s = dp_opt_get_string(test_ctx->ad_ctx->ad_options->id->basic,
+                          SDAP_KRB5_REALM);
+    assert_non_null(s);
+    assert_string_equal(s, ONEWAY_DOMNAME);
+
+    s = dp_opt_get_string(test_ctx->ad_ctx->ad_options->id->basic,
+                          SDAP_SASL_AUTHID);
+    assert_non_null(s);
+    assert_string_equal(s, ONEWAY_AUTHID);
+
+    talloc_free(test_ctx->ad_ctx->ad_options);
+
+    unlink(ONEWAY_KEYTAB_PATH);
+}
 static void test_ad_create_2way_trust_options(void **state)
 {
     struct ad_common_test_ctx *test_ctx = talloc_get_type(*state,
@@ -342,6 +413,9 @@ int main(int argc, const char *argv[])
 
     const struct CMUnitTest tests[] = {
         cmocka_unit_test(test_ad_create_default_options),
+        cmocka_unit_test_setup_teardown(test_ad_create_1way_trust_options,
+                                        test_ad_common_setup,
+                                        test_ad_common_teardown),
         cmocka_unit_test_setup_teardown(test_ad_create_2way_trust_options,
                                         test_ad_common_setup,
                                         test_ad_common_teardown),
-- 
2.1.0

-------------- next part --------------
>From ff6a9ef13f903b585534085d3632eeb3fe3df73b Mon Sep 17 00:00:00 2001
From: Jakub Hrozek <jhrozek at redhat.com>
Date: Fri, 15 May 2015 15:32:17 +0200
Subject: [PATCH 29/29] IPA: Utility function for setting up one-way trust
 context

Related:
    https://fedorahosted.org/sssd/ticket/2638
---
 src/providers/ipa/ipa_subdomains_server.c     | 67 +++++++++++++++++++++++++--
 src/tests/cmocka/test_ipa_subdomains_server.c | 29 ++++++++++--
 2 files changed, 88 insertions(+), 8 deletions(-)

diff --git a/src/providers/ipa/ipa_subdomains_server.c b/src/providers/ipa/ipa_subdomains_server.c
index cee03b5777e7dfb354b42e9c9d842d43c90a981c..97f65eae6ec9d2a2e9f12492158a0d66500123ed 100644
--- a/src/providers/ipa/ipa_subdomains_server.c
+++ b/src/providers/ipa/ipa_subdomains_server.c
@@ -164,6 +164,69 @@ const char *ipa_trust_dir2str(enum trust_direction direction)
 #define IPA_GETKEYTAB_TIMEOUT 5
 #endif /* IPA_GETKEYTAB_TIMEOUT */
 
+static struct ad_options *
+ipa_create_1way_trust_ctx(struct ipa_id_ctx *id_ctx,
+                          const char *forest,
+                          struct sss_domain_info *subdom)
+{
+    char *keytab;
+    char *principal;
+    struct ad_options *ad_options;
+    const char *ad_domain;
+
+    ad_domain = subdom->name;
+    keytab = forest_keytab(id_ctx, forest);
+    principal = subdomain_trust_princ(id_ctx, forest, subdom);
+    if (keytab == NULL || principal == NULL) {
+        return NULL;
+    }
+
+    ad_options = ad_create_1way_trust_options(id_ctx,
+                                              ad_domain,
+                                              id_ctx->server_mode->hostname,
+                                              keytab,
+                                              principal);
+    if (ad_options == NULL) {
+        talloc_free(keytab);
+        talloc_free(principal);
+        return NULL;
+    }
+
+    talloc_steal(ad_options, keytab);
+    talloc_steal(ad_options, principal);
+    return ad_options;
+}
+
+static struct ad_options *ipa_ad_options_new(struct ipa_id_ctx *id_ctx,
+                                             struct sss_domain_info *subdom)
+{
+    struct ad_options *ad_options = NULL;
+    enum trust_direction direction;
+    const char *forest;
+
+    /* Trusts are only established with forest roots */
+    direction = subdom->forest_root->trust_direction;
+    forest = subdom->forest_root->forest;
+
+    if (direction == SD_TRUST_DIRECTION_OUTBOUND) {
+        ad_options = ipa_create_1way_trust_ctx(id_ctx, forest, subdom);
+    } else if (direction == SD_TRUST_DIRECTION_TWOWAY) {
+        ad_options = ad_create_2way_trust_options(id_ctx,
+                                                  id_ctx->server_mode->realm,
+                                                  subdom->name,
+                                                  id_ctx->server_mode->hostname);
+    } else {
+        DEBUG(SSSDBG_CRIT_FAILURE, "Unsupported trust direction!\n");
+        ad_options = NULL;
+    }
+
+    if (ad_options == NULL) {
+        DEBUG(SSSDBG_OP_FAILURE, "Cannot initialize AD options\n");
+        return NULL;
+    }
+    return ad_options;
+}
+
 
 static errno_t
 ipa_ad_ctx_new(struct be_ctx *be_ctx,
@@ -184,9 +247,7 @@ ipa_ad_ctx_new(struct be_ctx *be_ctx,
     ad_domain = subdom->name;
     DEBUG(SSSDBG_TRACE_LIBS, "Setting up AD subdomain %s\n", subdom->name);
 
-    ad_options = ad_create_2way_trust_options(id_ctx, id_ctx->server_mode->realm,
-                                              ad_domain,
-                                              id_ctx->server_mode->hostname);
+    ad_options = ipa_ad_options_new(id_ctx, subdom);
     if (ad_options == NULL) {
         DEBUG(SSSDBG_OP_FAILURE, "Cannot initialize AD options\n");
         talloc_free(ad_options);
diff --git a/src/tests/cmocka/test_ipa_subdomains_server.c b/src/tests/cmocka/test_ipa_subdomains_server.c
index 96a0c3621c8234e3356e8aae2b9b7ecbf01edd9e..60891c8d84071c2f3cfcc7687368aaa5a462f853 100644
--- a/src/tests/cmocka/test_ipa_subdomains_server.c
+++ b/src/tests/cmocka/test_ipa_subdomains_server.c
@@ -63,7 +63,8 @@
 #define TEST_ID_PROVIDER "ipa"
 
 #define ONEWAY_KEYTAB   TEST_DIR"/"SUBDOM_REALM".keytab"
-#define ONEWAY_AUTHID   DOM_FLAT"$@"SUBDOM_REALM
+#define ONEWAY_PRINC    DOM_FLAT"$"
+#define ONEWAY_AUTHID   ONEWAY_PRINC"@"SUBDOM_REALM
 
 krb5_error_code __wrap_krb5_kt_default(krb5_context context, krb5_keytab *id)
 {
@@ -72,13 +73,11 @@ krb5_error_code __wrap_krb5_kt_default(krb5_context context, krb5_keytab *id)
 
 static void create_dummy_keytab(void)
 {
-    int fd;
     errno_t ret;
 
     assert_non_null(ONEWAY_KEYTAB);
-    fd = open(ONEWAY_KEYTAB,  O_WRONLY | O_CREAT | O_TRUNC, 0600);
-    assert_int_not_equal(fd, -1);
-    close(fd);
+    mock_keytab_with_contents(global_talloc_context,
+                              ONEWAY_KEYTAB, ONEWAY_AUTHID);
 
     ret = access(ONEWAY_KEYTAB, R_OK);
     assert_int_equal(ret, 0);
@@ -627,8 +626,28 @@ static void test_ipa_server_create_trusts_oneway(struct tevent_req *req)
 
     /* Trust object should be around now */
     assert_non_null(test_ctx->ipa_ctx->server_mode->trusts);
+
+    assert_trust_object(
+        test_ctx->ipa_ctx->server_mode->trusts,
+        CHILD_NAME,    /* AD domain name */
+        CHILD_REALM,   /* AD realm can be child if SDAP realm is parent's */
+        CHILD_SID,
+        ONEWAY_KEYTAB,    /* Keytab shared with parent AD dom */
+        ONEWAY_PRINC,     /* Principal shared with parent AD dom */
+        SUBDOM_REALM); /* SDAP realm must be AD root domain */
+
     assert_non_null(test_ctx->ipa_ctx->server_mode->trusts->next);
 
+    /* Here all properties point to the AD domain */
+    assert_trust_object(test_ctx->ipa_ctx->server_mode->trusts->next,
+                        SUBDOM_NAME,
+                        SUBDOM_REALM,
+                        SUBDOM_SID,
+                        ONEWAY_KEYTAB,
+                        ONEWAY_PRINC,
+                        SUBDOM_REALM);
+
+    assert_null(test_ctx->ipa_ctx->server_mode->trusts->next->next);
     test_ipa_server_create_trusts_finish(test_ctx);
 }
 
-- 
2.1.0



More information about the sssd-devel mailing list