[SSSD] [PATCH] Temporarily mark subdomain as inactive instead marking the whole back end offline

Jakub Hrozek jhrozek at redhat.com
Fri Sep 4 12:35:49 UTC 2015


Hi,

the attached patches implement https://fedorahosted.org/sssd/ticket/2637

There are two main use-cases:
    1) If AD DCs are not reachable on the IPA server itself, we should
    avoid going offline completely, at least the IPA domain should be
    still reachable.
    2) If SSSD is connected to a non-root AD DC, we still try to contact
    the forest root because only the forest root normally knows all the
    subdomains. But in many setups, the forest root is not reachable
    due to network restrictions.

The full design is described here:
https://fedorahosted.org/sssd/wiki/DesignDocs/OneWayTrusts#Subdomainofflinestatuschanges

There is one area that I'm not sure about myself -- in the
ad_subdomains.c changes, I always set ignore_mark_offline to true for
the root DC. I think it's safe because in this case the root domain is a
subdomain and we don't want a subdomain to allow marking the be as
offline, but I would like to ask that this change is double-checked.

I mostly checked by pausing AD DC VMs in my test setups and making sure
that the backend stays offline after lookup error, a subsequent lookup
is answered as if we were online on the backend side and that the
inactive status is reset. Also, main domain failures still must mark
sssd as offline.
-------------- next part --------------
>From cde1bf93e93003e10220b4c5e10e066dae06767d Mon Sep 17 00:00:00 2001
From: Jakub Hrozek <jhrozek at redhat.com>
Date: Tue, 18 Aug 2015 15:15:44 +0000
Subject: [PATCH 1/8] UTIL: Convert domain->disabled into tri-state with domain
 states

Required for:
https://fedorahosted.org/sssd/ticket/2637

This is a first step towards making it possible for domain to be around,
but not contacted by Data Provider.

Also explicitly create domains as enabled, previously we only relied on
talloc_zero marking dom->disabled as false.
---
 src/confdb/confdb.c                      |  2 ++
 src/confdb/confdb.h                      |  8 +++++++-
 src/db/sysdb_subdomains.c                |  7 +++++--
 src/providers/ad/ad_subdomains.c         |  2 +-
 src/providers/ipa/ipa_subdomains.c       |  2 +-
 src/responder/common/responder_common.c  |  5 +++--
 src/tests/cmocka/test_sysdb_subdomains.c |  6 +++++-
 src/tests/cmocka/test_utils.c            |  6 +++---
 src/util/domain_info_utils.c             | 17 ++++++++++++++---
 src/util/util.h                          |  3 +++
 src/util/util_errors.c                   |  1 +
 src/util/util_errors.h                   |  1 +
 12 files changed, 46 insertions(+), 14 deletions(-)

diff --git a/src/confdb/confdb.c b/src/confdb/confdb.c
index 3a8a1c01b92e62302ac4f787ccd085be9d8f05c3..a207ce3ba55eec50ba6392d14ccf835c6ba7b578 100644
--- a/src/confdb/confdb.c
+++ b/src/confdb/confdb.c
@@ -1342,6 +1342,8 @@ static int confdb_get_domain_internal(struct confdb_ctx *cdb,
     domain->has_views = false;
     domain->view_name = NULL;
 
+    domain->state = DOM_ENABLED;
+
     *_domain = domain;
     ret = EOK;
 done:
diff --git a/src/confdb/confdb.h b/src/confdb/confdb.h
index 427c309a290dc9b02270311dacfcf308bd78430c..7456e0c6c226b3bc6ea3a64808fba0f732329265 100644
--- a/src/confdb/confdb.h
+++ b/src/confdb/confdb.h
@@ -216,6 +216,12 @@
 struct confdb_ctx;
 struct config_file_ctx;
 
+enum sss_domain_state {
+    DOM_ENABLED,
+    DOM_DISABLED,
+    DOM_INACTIVE,
+};
+
 /**
  * Data structure storing all of the basic features
  * of a domain.
@@ -278,7 +284,7 @@ struct sss_domain_info {
     struct sss_domain_info *prev;
     struct sss_domain_info *next;
 
-    bool disabled;
+    enum sss_domain_state state;
     char **sd_inherit;
 
     /* Do not use the forest pointer directly in new code, but rather the
diff --git a/src/db/sysdb_subdomains.c b/src/db/sysdb_subdomains.c
index 142520c1836d74ef7bc5c5269487b8971f261b88..ffcb4235cd9e3510eeedc28030332b88a4ead3be 100644
--- a/src/db/sysdb_subdomains.c
+++ b/src/db/sysdb_subdomains.c
@@ -111,6 +111,8 @@ struct sss_domain_info *new_subdomain(TALLOC_CTX *mem_ctx,
     dom->enumerate = enumerate;
     dom->fqnames = true;
     dom->mpg = mpg;
+    dom->state = DOM_ENABLED;
+
     /* If the parent domain filters out group members, the subdomain should
      * as well if configured */
     inherit_option = string_in_list(CONFDB_DOMAIN_IGNORE_GROUP_MEMBERS,
@@ -268,7 +270,7 @@ errno_t sysdb_update_subdomains(struct sss_domain_info *domain)
     /* disable all domains,
      * let the search result refresh any that are still valid */
     for (dom = domain->subdomains; dom; dom = get_next_domain(dom, false)) {
-        dom->disabled = true;
+        sss_domain_set_state(dom, DOM_DISABLED);
     }
 
     if (res->count == 0) {
@@ -312,7 +314,8 @@ errno_t sysdb_update_subdomains(struct sss_domain_info *domain)
         /* 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) {
-                dom->disabled = false;
+                sss_domain_set_state(dom, DOM_ENABLED);
+
                 /* in theory these may change, but it should never happen */
                 if (strcasecmp(dom->realm, realm) != 0) {
                     DEBUG(SSSDBG_TRACE_INTERNAL,
diff --git a/src/providers/ad/ad_subdomains.c b/src/providers/ad/ad_subdomains.c
index 9b42f03a0067ab5844432a0f19dd2930dcc200c9..d1d468043410c80e6bf7f0f48a13bd9e962552af 100644
--- a/src/providers/ad/ad_subdomains.c
+++ b/src/providers/ad/ad_subdomains.c
@@ -376,7 +376,7 @@ static errno_t ad_subdomains_refresh(struct ad_subdomains_ctx *ctx,
 
         if (c >= count) {
             /* ok this subdomain does not exist anymore, let's clean up */
-            dom->disabled = true;
+            sss_domain_set_state(dom, DOM_DISABLED);
             ret = sysdb_subdomain_delete(dom->sysdb, dom->name);
             if (ret != EOK) {
                 goto done;
diff --git a/src/providers/ipa/ipa_subdomains.c b/src/providers/ipa/ipa_subdomains.c
index b2e2fec353f7b168d28a880cb0f1b6181abb1ccb..089736b47d8f384a8024682dd203d324292df9ce 100644
--- a/src/providers/ipa/ipa_subdomains.c
+++ b/src/providers/ipa/ipa_subdomains.c
@@ -528,7 +528,7 @@ static errno_t ipa_subdomains_refresh(struct ipa_subdomains_ctx *ctx,
 
         if (c >= count) {
             /* ok this subdomain does not exist anymore, let's clean up */
-            dom->disabled = true;
+            sss_domain_set_state(dom, DOM_DISABLED);
             ret = sysdb_subdomain_delete(dom->sysdb, dom->name);
             if (ret != EOK) {
                 goto done;
diff --git a/src/responder/common/responder_common.c b/src/responder/common/responder_common.c
index 36e7f15948632e9c637886dee259b494e46ceecb..2097004cb0fc24d8b356f9d924243f948227ef58 100644
--- a/src/responder/common/responder_common.c
+++ b/src/responder/common/responder_common.c
@@ -923,7 +923,7 @@ responder_get_domain(struct resp_ctx *rctx, const char *name)
     struct sss_domain_info *ret_dom = NULL;
 
     for (dom = rctx->domains; dom; dom = get_next_domain(dom, true)) {
-        if (dom->disabled) {
+        if (sss_domain_get_state(dom) == DOM_DISABLED) {
             continue;
         }
 
@@ -958,7 +958,8 @@ errno_t responder_get_domain_by_id(struct resp_ctx *rctx, const char *id,
     id_len = strlen(id);
 
     for (dom = rctx->domains; dom; dom = get_next_domain(dom, true)) {
-        if (dom->disabled || dom->domain_id == NULL) {
+        if (sss_domain_get_state(dom) == DOM_DISABLED ||
+                dom->domain_id == NULL) {
             continue;
         }
 
diff --git a/src/tests/cmocka/test_sysdb_subdomains.c b/src/tests/cmocka/test_sysdb_subdomains.c
index 82e77815ec848afcdedc90e35e440f7532b5c0b2..8d1a26a5918eaa9dec975c360f69840400e4bd2c 100644
--- a/src/tests/cmocka/test_sysdb_subdomains.c
+++ b/src/tests/cmocka/test_sysdb_subdomains.c
@@ -151,7 +151,11 @@ static void test_sysdb_subdomain_create(void **state)
     ret = sysdb_update_subdomains(test_ctx->tctx->dom);
     assert_int_equal(ret, EOK);
 
-    assert_true(test_ctx->tctx->dom->subdomains->disabled);
+    assert_int_equal(sss_domain_get_state(test_ctx->tctx->dom->subdomains),
+                     DOM_DISABLED);
+    assert_int_equal(
+            sss_domain_get_state(test_ctx->tctx->dom->subdomains->next),
+            DOM_DISABLED);
 }
 
 static void test_sysdb_master_domain_ops(void **state)
diff --git a/src/tests/cmocka/test_utils.c b/src/tests/cmocka/test_utils.c
index c7ebe0997ec00197e8852bedbcf26ef1f6394fc3..0f72434ca77fbfe1bd88a75fd932719dbfc59444 100644
--- a/src/tests/cmocka/test_utils.c
+++ b/src/tests/cmocka/test_utils.c
@@ -259,7 +259,7 @@ void test_find_domain_by_name_disabled(void **state)
         dom = dom->next;
     }
     assert_non_null(dom);
-    dom->disabled = true;
+    sss_domain_set_state(dom, DOM_DISABLED);
 
     for (c = 0; c < test_ctx->dom_count; c++) {
         name = talloc_asprintf(global_talloc_context, DOMNAME_TMPL, c);
@@ -426,7 +426,7 @@ void test_find_domain_by_sid_disabled(void **state)
         dom = dom->next;
     }
     assert_non_null(dom);
-    dom->disabled = true;
+    sss_domain_set_state(dom, DOM_DISABLED);
 
     for (c = 0; c < test_ctx->dom_count; c++) {
         name = talloc_asprintf(global_talloc_context, DOMNAME_TMPL, c);
@@ -578,7 +578,7 @@ static void test_get_next_domain_disabled(void **state)
     struct sss_domain_info *dom = NULL;
 
     for (dom = test_ctx->dom_list; dom; dom = get_next_domain(dom, true)) {
-        dom->disabled = true;
+        sss_domain_set_state(dom, DOM_DISABLED);
     }
 
     dom = get_next_domain(test_ctx->dom_list, true);
diff --git a/src/util/domain_info_utils.c b/src/util/domain_info_utils.c
index 4eabcff7a0e0af342ec3833d24da26ede0cb5148..dc988bb275445ca5ddcb1e7f699217699c5b5b6e 100644
--- a/src/util/domain_info_utils.c
+++ b/src/util/domain_info_utils.c
@@ -50,7 +50,7 @@ struct sss_domain_info *get_next_domain(struct sss_domain_info *domain,
         } else {
             dom = NULL;
         }
-        if (dom && !dom->disabled) break;
+        if (dom && sss_domain_get_state(dom) != DOM_DISABLED) break;
     }
 
     return dom;
@@ -91,7 +91,7 @@ struct sss_domain_info *find_domain_by_name(struct sss_domain_info *domain,
         return NULL;
     }
 
-    while (dom && dom->disabled) {
+    while (dom && sss_domain_get_state(dom) == DOM_DISABLED) {
         dom = get_next_domain(dom, true);
     }
     while (dom) {
@@ -119,7 +119,7 @@ struct sss_domain_info *find_domain_by_sid(struct sss_domain_info *domain,
 
     sid_len = strlen(sid);
 
-    while (dom && dom->disabled) {
+    while (dom && sss_domain_get_state(dom) == DOM_DISABLED) {
         dom = get_next_domain(dom, true);
     }
 
@@ -730,3 +730,14 @@ done:
 
     return ret;
 }
+
+enum sss_domain_state sss_domain_get_state(struct sss_domain_info *dom)
+{
+    return dom->state;
+}
+
+void sss_domain_set_state(struct sss_domain_info *dom,
+                          enum sss_domain_state state)
+{
+    dom->state = state;
+}
diff --git a/src/util/util.h b/src/util/util.h
index 3e29e748768fc2cac547d16d61b449f1b555a56f..f9fe1ca7189c6b2cdcb29f143005b20a2d969fee 100644
--- a/src/util/util.h
+++ b/src/util/util.h
@@ -566,6 +566,9 @@ struct sss_domain_info *find_domain_by_name(struct sss_domain_info *domain,
                                             bool match_any);
 struct sss_domain_info *find_domain_by_sid(struct sss_domain_info *domain,
                                            const char *sid);
+enum sss_domain_state sss_domain_get_state(struct sss_domain_info *dom);
+void sss_domain_set_state(struct sss_domain_info *dom,
+                          enum sss_domain_state state);
 
 struct sss_domain_info*
 sss_get_domain_by_sid_ldap_fallback(struct sss_domain_info *domain,
diff --git a/src/util/util_errors.c b/src/util/util_errors.c
index fd6b9fbfe32eb8bb456e42cacacfebe439091754..ed19346d9b588a711367af4c891b1298cd4f067e 100644
--- a/src/util/util_errors.c
+++ b/src/util/util_errors.c
@@ -81,6 +81,7 @@ struct err_string error_to_str[] = {
     { "p11_child failed" }, /* ERR_P11_CHILD */
     { "Address family not supported" }, /* ERR_ADDR_FAMILY_NOT_SUPPORTED */
     { "Message sender is the bus" }, /* ERR_SBUS_SENDER_BUS */
+    { "Subdomain is inactive" }, /* ERR_SUBDOM_INACTIVE */
     { "ERR_LAST" } /* ERR_LAST */
 };
 
diff --git a/src/util/util_errors.h b/src/util/util_errors.h
index bda0c9b7d330d8e7a15bc460fc8ed97c80510ea8..c1d081912a382d645c27809a3ac336ff90047cdf 100644
--- a/src/util/util_errors.h
+++ b/src/util/util_errors.h
@@ -103,6 +103,7 @@ enum sssd_errors {
     ERR_P11_CHILD,
     ERR_ADDR_FAMILY_NOT_SUPPORTED,
     ERR_SBUS_SENDER_BUS,
+    ERR_SUBDOM_INACTIVE,
     ERR_LAST            /* ALWAYS LAST */
 };
 
-- 
2.4.3

-------------- next part --------------
>From 591d07855b70aacbb4488ba9a54438ee9ded48b5 Mon Sep 17 00:00:00 2001
From: Jakub Hrozek <jhrozek at redhat.com>
Date: Fri, 4 Sep 2015 09:27:17 +0200
Subject: [PATCH 2/8] DP: Provide a way to mark subdomain as disabled and
 auto-enable it later with offline_timeout

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

Adds a new Data Provider function be_mark_dom_offline() that is a
replacement for be_mark_offline(). When called, the function would
either set the whole back end offline, just like be_mark_offline or just
set the subdomain status to inactive.

When a subdomain is inactive, there is a singleton timed task that would
re-set the subdomin after offline_timeout seconds.
---
 src/providers/data_provider_be.c | 99 ++++++++++++++++++++++++++++++++++++----
 src/providers/dp_backend.h       |  1 +
 2 files changed, 91 insertions(+), 9 deletions(-)

diff --git a/src/providers/data_provider_be.c b/src/providers/data_provider_be.c
index d147630248f0a24f5a632760b55b9284a6928e40..42778580b691ece990442912a75e5578e7d62268 100644
--- a/src/providers/data_provider_be.c
+++ b/src/providers/data_provider_be.c
@@ -478,6 +478,24 @@ try_to_go_online(TALLOC_CTX *mem_ctx,
     return EOK;
 }
 
+static int get_offline_timeout(struct be_ctx *ctx)
+{
+    errno_t ret;
+    int offline_timeout;
+
+    ret = confdb_get_int(ctx->cdb, ctx->conf_path,
+                         CONFDB_DOMAIN_OFFLINE_TIMEOUT, 60,
+                         &offline_timeout);
+    if (ret != EOK) {
+        DEBUG(SSSDBG_CRIT_FAILURE,
+              "Failed to get offline_timeout from confdb. "
+              "Will use 60 seconds.\n");
+        offline_timeout = 60;
+    }
+
+    return offline_timeout;
+}
+
 void be_mark_offline(struct be_ctx *ctx)
 {
     int offline_timeout;
@@ -493,15 +511,9 @@ void be_mark_offline(struct be_ctx *ctx)
         /* This is the first time we go offline - create a periodic task
          * to check if we can switch to online. */
         DEBUG(SSSDBG_TRACE_INTERNAL, "Initialize check_if_online_ptask.\n");
-        ret = confdb_get_int(ctx->cdb, ctx->conf_path,
-                             CONFDB_DOMAIN_OFFLINE_TIMEOUT, 60,
-                             &offline_timeout);
-        if (ret != EOK) {
-            DEBUG(SSSDBG_CRIT_FAILURE,
-                  "Failed to get offline_timeout from confdb. "
-                  "Will use 60 seconds.\n");
-            offline_timeout = 60;
-        }
+
+        offline_timeout = get_offline_timeout(ctx);
+
         ret = be_ptask_create_sync(ctx, ctx,
                                    offline_timeout, offline_timeout,
                                    offline_timeout, 30, offline_timeout,
@@ -524,10 +536,79 @@ void be_mark_offline(struct be_ctx *ctx)
     be_run_offline_cb(ctx);
 }
 
+static void be_subdom_reset_status(struct tevent_context *ev,
+                                  struct tevent_timer *te,
+                                  struct timeval current_time,
+                                  void *pvt)
+{
+    struct sss_domain_info *subdom = talloc_get_type(pvt,
+                                                     struct sss_domain_info);
+
+    DEBUG(SSSDBG_TRACE_LIBS, "Resetting subdomain %s\n", subdom->name);
+    subdom->state = DOM_ENABLED;
+}
+
+static void be_mark_subdom_offline(struct sss_domain_info *subdom,
+                                   struct be_ctx *be_ctx)
+{
+    struct timeval tv;
+    struct tevent_timer *timeout = NULL;
+    int reset_status_timeout;
+
+    reset_status_timeout = get_offline_timeout(be_ctx);
+    tv = tevent_timeval_current_ofs(reset_status_timeout, 0);
+
+    switch (subdom->state) {
+    case DOM_DISABLED:
+        DEBUG(SSSDBG_MINOR_FAILURE, "Won't touch disabled subdomain\n");
+        return;
+    case DOM_INACTIVE:
+        DEBUG(SSSDBG_TRACE_ALL, "Subdomain already disabled\n");
+        return;
+    case DOM_ENABLED:
+        break;
+    }
+
+    timeout = tevent_add_timer(be_ctx->ev, be_ctx, tv,
+                               be_subdom_reset_status, subdom);
+    if (timeout == NULL) {
+        DEBUG(SSSDBG_OP_FAILURE, "Cannot create timer\n");
+        return;
+    }
+
+    subdom->state = DOM_INACTIVE;
+}
+
+void be_mark_dom_offline(struct sss_domain_info *dom, struct be_ctx *ctx)
+{
+    if (IS_SUBDOMAIN(dom) == false) {
+        DEBUG(SSSDBG_TRACE_LIBS, "Marking back end offline\n");
+        be_mark_offline(ctx);
+    } else {
+        DEBUG(SSSDBG_TRACE_LIBS, "Marking subdomain %s offline\n", dom->name);
+        be_mark_subdom_offline(dom, ctx);
+    }
+}
+
+static void reactivate_subdoms(struct sss_domain_info *head)
+{
+    struct sss_domain_info *dom;
+
+    DEBUG(SSSDBG_TRACE_LIBS, "Resetting all subdomains");
+
+    for (dom = head; dom; dom = get_next_domain(dom, true)) {
+        if (sss_domain_get_state(dom) == DOM_INACTIVE) {
+            sss_domain_set_state(dom, DOM_ENABLED);
+        }
+    }
+}
 static void be_reset_offline(struct be_ctx *ctx)
 {
     ctx->offstat.went_offline = 0;
     ctx->offstat.offline = false;
+
+    reactivate_subdoms(ctx->domain);
+
     be_ptask_disable(ctx->check_if_online_ptask);
     be_run_online_cb(ctx);
 }
diff --git a/src/providers/dp_backend.h b/src/providers/dp_backend.h
index bca0c2f97691a96086555482ce181dbdf684466b..4bffcee9e8739302343b7813d3540eb43e966581 100644
--- a/src/providers/dp_backend.h
+++ b/src/providers/dp_backend.h
@@ -189,6 +189,7 @@ struct be_host_req {
 
 bool be_is_offline(struct be_ctx *ctx);
 void be_mark_offline(struct be_ctx *ctx);
+void be_mark_dom_offline(struct sss_domain_info *dom, struct be_ctx *ctx);
 
 int be_add_reconnect_cb(TALLOC_CTX *mem_ctx,
                         struct be_ctx *ctx,
-- 
2.4.3

-------------- next part --------------
>From 794e5d9a89988c6dc329c2d6ba8bf758723bb60c Mon Sep 17 00:00:00 2001
From: Jakub Hrozek <jhrozek at redhat.com>
Date: Wed, 2 Sep 2015 13:40:48 +0200
Subject: [PATCH 3/8] SDAP: Do not set is_offline if ignore_mark_offline is set

Required for:
https://fedorahosted.org/sssd/ticket/2637

The caller of the sdap_id_op requests can set the ignore_mark_offline
flag to avoid the sdap_id_op from marking the whole back end as offline.

However, there was a small bug - the is_offline internal sdap_id_op flag
was still being set. As a consequence, the error code from the
connection was ignored and EAGAIN was always returned.
---
 src/providers/ldap/sdap_id_op.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/providers/ldap/sdap_id_op.c b/src/providers/ldap/sdap_id_op.c
index 508bbd2ad3ebb3f9159a8b7f48258ff31a7cd6e2..0474a9cb7828ef43ef76cf6bebac077315296875 100644
--- a/src/providers/ldap/sdap_id_op.c
+++ b/src/providers/ldap/sdap_id_op.c
@@ -567,9 +567,9 @@ static void sdap_id_op_connect_done(struct tevent_req *subreq)
             DEBUG(SSSDBG_CRIT_FAILURE,
                   "Failed to connect, going offline (%d [%s])\n",
                    ret, strerror(ret));
+            is_offline = true;
             be_mark_offline(conn_cache->id_conn->id_ctx->be);
         }
-        is_offline = true;
     }
 
     if (ret == EOK) {
-- 
2.4.3

-------------- next part --------------
>From d517bfafb30fb231a5c60a069d920b437508d723 Mon Sep 17 00:00:00 2001
From: Jakub Hrozek <jhrozek at redhat.com>
Date: Wed, 2 Sep 2015 13:42:06 +0200
Subject: [PATCH 4/8] AD: Only ignore errors from SDAP lookups if there's
 another connection to fallback to

Required for:
https://fedorahosted.org/sssd/ticket/2637

The AD lookup code honors the ignore_mark_offline flag in the sense that
if it's set, the sdap return code is not reported to the upper layer,
but EOK is returned as request status and the sdap return code is
returned separately.

This patch modifies the behaviour further to only apply if there is
another connection to fall back to.
---
 src/providers/ad/ad_id.c | 1 +
 1 file changed, 1 insertion(+)

diff --git a/src/providers/ad/ad_id.c b/src/providers/ad/ad_id.c
index 7a0c6eccd2d2f0d4f8a545a9d4873a9447179a00..4f327f823173eb113153a556322dae4cc4b42f3e 100644
--- a/src/providers/ad/ad_id.c
+++ b/src/providers/ad/ad_id.c
@@ -146,6 +146,7 @@ ad_handle_acct_info_done(struct tevent_req *subreq)
 
     ret = sdap_handle_acct_req_recv(subreq, &dp_error, &err, &sdap_err);
     if (dp_error == DP_ERR_OFFLINE
+        && state->conn[state->cindex+1] != NULL
         && state->conn[state->cindex]->ignore_mark_offline) {
          /* This is a special case: GC does not work.
           *  We need to Fall back to ldap
-- 
2.4.3

-------------- next part --------------
>From 47218745680afdab941aa9da15e763933d90343b Mon Sep 17 00:00:00 2001
From: Jakub Hrozek <jhrozek at redhat.com>
Date: Wed, 2 Sep 2015 15:53:34 +0200
Subject: [PATCH 5/8] KRB5: Offline operation with disabled domain

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

If a subdomain is in the disabled state, switch krb5_child operation
into offline mode.

Similarly, instead of marking the whole back end as offline, mark just
the domain as offline -- depending on the domain type, this would mark
the whole back end or just inactivate subdomain.
---
 src/providers/krb5/krb5_auth.c | 14 +++++++++++++-
 1 file changed, 13 insertions(+), 1 deletion(-)

diff --git a/src/providers/krb5/krb5_auth.c b/src/providers/krb5/krb5_auth.c
index d35df13994a3e16feff90592bec16d7a8f30b70a..dd99b725f760f99157cb9a0c5e26d0373939f69f 100644
--- a/src/providers/krb5/krb5_auth.c
+++ b/src/providers/krb5/krb5_auth.c
@@ -720,7 +720,9 @@ static void krb5_auth_resolve_done(struct tevent_req *subreq)
              * was found good, setting offline,
              * but we still have to call the child to setup
              * the ccache file if we are performing auth */
-            be_mark_offline(state->be_ctx);
+            DEBUG(SSSDBG_TRACE_FUNC,
+                  "Marking domain %s as offline\n", state->domain->name);
+            be_mark_dom_offline(state->domain, state->be_ctx);
             kr->is_offline = true;
 
             if (kr->pd->cmd == SSS_PAM_CHAUTHTOK ||
@@ -754,9 +756,19 @@ static void krb5_auth_resolve_done(struct tevent_req *subreq)
         kr->is_offline = be_is_offline(state->be_ctx);
     }
 
+    if (!kr->is_offline
+            && sss_domain_get_state(state->domain) == DOM_INACTIVE) {
+        DEBUG(SSSDBG_TRACE_INTERNAL,
+              "Subdomain %s is inactive, will proceed offline\n",
+              state->domain->name);
+        kr->is_offline = true;
+    }
+
     if (kr->is_offline
             && sss_krb5_realm_has_proxy(dp_opt_get_cstring(kr->krb5_ctx->opts,
                                         KRB5_REALM))) {
+        DEBUG(SSSDBG_TRACE_FUNC,
+              "Resetting offline status, KDC proxy is in use\n");
         kr->is_offline = false;
     }
 
-- 
2.4.3

-------------- next part --------------
>From d93d79b851acfbff4425bbe0cd52eebbb7abe31b Mon Sep 17 00:00:00 2001
From: Jakub Hrozek <jhrozek at redhat.com>
Date: Wed, 2 Sep 2015 15:52:51 +0200
Subject: [PATCH 6/8] AD: Do not mark the whole back end as offline if
 subdomain lookup fails

Required for:
https://fedorahosted.org/sssd/ticket/2637

Rather mark the domain as inactive. It will be marked as active later,
in the meantime the main domain can continue to work online and
subdomain requests will be answered from cache.

The lookup request itself just returns a special error code and lets the
caller handle the error code as appropriate (normally by disabling the
subdomain temporarily).
---
 src/providers/ad/ad_id.c | 77 +++++++++++++++++++++++++++++++++++++++---------
 1 file changed, 63 insertions(+), 14 deletions(-)

diff --git a/src/providers/ad/ad_id.c b/src/providers/ad/ad_id.c
index 4f327f823173eb113153a556322dae4cc4b42f3e..ebaf3ff9aa454b6a80362655b712237bd6fc24b7 100644
--- a/src/providers/ad/ad_id.c
+++ b/src/providers/ad/ad_id.c
@@ -91,17 +91,27 @@ ad_handle_acct_info_send(TALLOC_CTX *mem_ctx,
     state->ad_options = ad_options;
     state->cindex = 0;
 
+    if (sss_domain_get_state(sdom->dom) == DOM_INACTIVE) {
+        ret = ERR_SUBDOM_INACTIVE;
+        goto immediate;
+    }
+
     ret = ad_handle_acct_info_step(req);
-    if (ret == EOK) {
-        tevent_req_done(req);
-        tevent_req_post(req, be_ctx->ev);
-    } else if (ret != EAGAIN) {
-        tevent_req_error(req, ret);
-        tevent_req_post(req, be_ctx->ev);
+    if (ret != EAGAIN) {
+        goto immediate;
     }
 
     /* Lookup in progress */
     return req;
+
+immediate:
+    if (ret != EOK) {
+        tevent_req_error(req, ret);
+    } else {
+        tevent_req_done(req);
+    }
+    tevent_req_post(req, be_ctx->ev);
+    return req;
 }
 
 static errno_t
@@ -160,8 +170,7 @@ ad_handle_acct_info_done(struct tevent_req *subreq)
         state->dp_error = dp_error;
         state->err = err;
 
-        tevent_req_error(req, ret);
-        return;
+        goto fail;
     }
 
     if (sdap_err == EOK) {
@@ -170,8 +179,8 @@ ad_handle_acct_info_done(struct tevent_req *subreq)
     } else if (sdap_err == ERR_NO_POSIX) {
         disable_gc(state->ad_options);
     } else if (sdap_err != ENOENT) {
-        tevent_req_error(req, EIO);
-        return;
+        ret = EIO;
+        goto fail;
     }
 
     /* Ret is only ENOENT or ERR_NO_POSIX now. Try the next connection */
@@ -188,12 +197,27 @@ ad_handle_acct_info_done(struct tevent_req *subreq)
             /* No more connections */
             tevent_req_done(req);
         } else {
-            tevent_req_error(req, ret);
+            goto fail;
         }
         return;
     }
 
     /* Another lookup in progress */
+    return;
+
+fail:
+    if (IS_SUBDOMAIN(state->sdom->dom)) {
+        /* Deactivate subdomain on lookup errors instead of going
+         * offline completely.
+         * This is a stopgap, until our failover is per-domain,
+         * not per-backend. Unfortunately, we can't do it on some
+         * errors only, because sdap_id_op code encapsulated the
+         * failover as well..
+         */
+        ret = ERR_SUBDOM_INACTIVE;
+    }
+    tevent_req_error(req, ret);
+    return;
 }
 
 errno_t
@@ -258,6 +282,12 @@ get_conn_list(struct be_req *breq, struct ad_id_ctx *ad_ctx,
         break;
     }
 
+    if (IS_SUBDOMAIN(dom)) {
+        for (cindex = 0; clist[cindex] != NULL; cindex++) {
+            clist[cindex]->ignore_mark_offline = true;
+        }
+    }
+
     return clist;
 }
 
@@ -328,6 +358,11 @@ done:
 
 static void ad_account_info_complete(struct tevent_req *req);
 
+struct ad_account_info_state {
+    struct be_req *be_req;
+    struct sss_domain_info *dom;
+};
+
 void
 ad_account_info_handler(struct be_req *be_req)
 {
@@ -341,6 +376,7 @@ ad_account_info_handler(struct be_req *be_req)
     struct sdap_id_conn_ctx **clist;
     bool shortcut;
     errno_t ret;
+    struct ad_account_info_state *state;
 
     ad_ctx = talloc_get_type(be_ctx->bet_info[BET_ID].pvt_bet_data,
                              struct ad_id_ctx);
@@ -391,13 +427,21 @@ ad_account_info_handler(struct be_req *be_req)
         goto fail;
     }
 
+    state = talloc(be_req, struct ad_account_info_state);
+    if (state == NULL) {
+        ret = ENOMEM;
+        goto fail;
+    }
+    state->dom = sdom->dom;
+    state->be_req = be_req;
+
     req = ad_handle_acct_info_send(be_req, be_req, ar, sdap_id_ctx,
                                    ad_ctx->ad_options, sdom, clist);
     if (req == NULL) {
         ret = ENOMEM;
         goto fail;
     }
-    tevent_req_set_callback(req, ad_account_info_complete, be_req);
+    tevent_req_set_callback(req, ad_account_info_complete, state);
     return;
 
 fail:
@@ -412,12 +456,17 @@ ad_account_info_complete(struct tevent_req *req)
     int dp_error;
     const char *error_text = "Internal error";
     const char *req_error_text;
+    struct ad_account_info_state *state;
 
-    be_req = tevent_req_callback_data(req, struct be_req);
+    state = tevent_req_callback_data(req, struct ad_account_info_state);
+    be_req = state->be_req;
 
     ret = ad_handle_acct_info_recv(req, &dp_error, &req_error_text);
     talloc_zfree(req);
-    if (dp_error == DP_ERR_OK) {
+    if (ret == ERR_SUBDOM_INACTIVE) {
+        be_mark_dom_offline(state->dom, be_req_get_be_ctx(be_req));
+        return be_req_terminate(be_req, DP_ERR_OFFLINE, EAGAIN, "Offline");
+    } else if (dp_error == DP_ERR_OK) {
         if (ret == EOK) {
             error_text = NULL;
         } else {
-- 
2.4.3

-------------- next part --------------
>From 67d37b52468bf58f23f037a0460a4ddf4116b7e8 Mon Sep 17 00:00:00 2001
From: Jakub Hrozek <jhrozek at redhat.com>
Date: Wed, 2 Sep 2015 12:10:03 +0000
Subject: [PATCH 7/8] AD: Set ignore_mark_offline=false when resolving AD root
 domain

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

Avoid going offline in cases where SSSD is connected to a child domain
but the root domain is not accessible.
---
 src/providers/ad/ad_subdomains.c | 56 +++++++++++++++++++++++-----------------
 1 file changed, 33 insertions(+), 23 deletions(-)

diff --git a/src/providers/ad/ad_subdomains.c b/src/providers/ad/ad_subdomains.c
index d1d468043410c80e6bf7f0f48a13bd9e962552af..8ed3dab0995f78a16f4a7df2e729ea88a39a782c 100644
--- a/src/providers/ad/ad_subdomains.c
+++ b/src/providers/ad/ad_subdomains.c
@@ -80,7 +80,8 @@ struct ad_subdomains_req_ctx {
     struct ad_id_ctx *root_id_ctx;
     struct sdap_id_op *root_op;
     size_t root_base_iter;
-    struct sysdb_attrs *root_domain;
+    struct sysdb_attrs *root_domain_attrs;
+    struct sss_domain_info *root_domain;
 
     size_t reply_count;
     struct sysdb_attrs **reply;
@@ -689,6 +690,7 @@ static errno_t ad_subdomains_get_root(struct ad_subdomains_req_ctx *ctx)
     return EAGAIN;
 }
 
+static struct sss_domain_info *ads_get_root_domain(struct ad_subdomains_req_ctx *ctx);
 static struct ad_id_ctx *ads_get_root_id_ctx(struct ad_subdomains_req_ctx *ctx);
 static void ad_subdomains_root_conn_done(struct tevent_req *req);
 
@@ -769,7 +771,14 @@ static void ad_subdomains_get_root_domain_done(struct tevent_req *req)
         }
     }
 
-    ctx->root_domain = reply[0];
+    ctx->root_domain_attrs = reply[0];
+    ctx->root_domain = ads_get_root_domain(ctx);
+    if (ctx->root_domain == NULL) {
+        DEBUG(SSSDBG_OP_FAILURE, "Could not find the root domain\n");
+        ret = EFAULT;
+        goto fail;
+    }
+
     ctx->root_id_ctx = ads_get_root_id_ctx(ctx);
     if (ctx->root_id_ctx == NULL) {
         DEBUG(SSSDBG_OP_FAILURE, "Cannot create id ctx for the root domain\n");
@@ -803,15 +812,13 @@ fail:
     be_req_terminate(ctx->be_req, dp_error, ret, NULL);
 }
 
-static struct ad_id_ctx *ads_get_root_id_ctx(struct ad_subdomains_req_ctx *ctx)
+static struct sss_domain_info *ads_get_root_domain(struct ad_subdomains_req_ctx *ctx)
 {
     errno_t ret;
     const char *name;
     struct sss_domain_info *root;
-    struct sdap_domain *sdom;
-    struct ad_id_ctx *root_id_ctx;
 
-    ret = sysdb_attrs_get_string(ctx->root_domain, AD_AT_TRUST_PARTNER, &name);
+    ret = sysdb_attrs_get_string(ctx->root_domain_attrs, AD_AT_TRUST_PARTNER, &name);
     if (ret != EOK) {
         DEBUG(SSSDBG_OP_FAILURE, "sysdb_attrs_get_string failed.\n");
         return NULL;
@@ -820,32 +827,40 @@ static struct ad_id_ctx *ads_get_root_id_ctx(struct ad_subdomains_req_ctx *ctx)
     /* With a subsequent run, the root should already be known */
     root = find_domain_by_name(ctx->sd_ctx->be_ctx->domain,
                                name, false);
-    if (root == NULL) {
-        DEBUG(SSSDBG_OP_FAILURE, "Could not find the root domain\n");
-        return NULL;
-    }
 
-    sdom = sdap_domain_get(ctx->sd_ctx->ad_id_ctx->sdap_id_ctx->opts, root);
+    return root;
+}
+
+static struct ad_id_ctx *ads_get_root_id_ctx(struct ad_subdomains_req_ctx *ctx)
+{
+    errno_t ret;
+    struct sdap_domain *sdom;
+    struct ad_id_ctx *root_id_ctx;
+
+    sdom = sdap_domain_get(ctx->sd_ctx->ad_id_ctx->sdap_id_ctx->opts,
+                           ctx->root_domain);
     if (sdom == NULL) {
         DEBUG(SSSDBG_OP_FAILURE,
-              "Cannot get the sdom for %s!\n", root->name);
+              "Cannot get the sdom for %s!\n", ctx->root_domain->name);
         return NULL;
     }
 
     if (sdom->pvt == NULL) {
         ret = ad_subdom_ad_ctx_new(ctx->sd_ctx->be_ctx,
                                    ctx->sd_ctx->ad_id_ctx,
-                                   root,
+                                   ctx->root_domain,
                                    &root_id_ctx);
         if (ret != EOK) {
             DEBUG(SSSDBG_OP_FAILURE, "ad_subdom_ad_ctx_new failed.\n");
             return NULL;
         }
+
         sdom->pvt = root_id_ctx;
     } else {
         root_id_ctx = sdom->pvt;
     }
 
+    root_id_ctx->ldap_ctx->ignore_mark_offline = true;
     return root_id_ctx;
 }
 
@@ -860,16 +875,11 @@ static void ad_subdomains_root_conn_done(struct tevent_req *req)
     ret = sdap_id_op_connect_recv(req, &dp_error);
     talloc_zfree(req);
     if (ret) {
-        if (dp_error == DP_ERR_OFFLINE) {
-            DEBUG(SSSDBG_MINOR_FAILURE,
-                  "No AD server is available, cannot get the "
-                  "subdomain list while offline\n");
-        } else {
-            DEBUG(SSSDBG_OP_FAILURE,
-                  "Failed to connect to AD server: [%d](%s)\n",
-                  ret, strerror(ret));
-        }
+        be_mark_dom_offline(ctx->root_domain, be_req_get_be_ctx(ctx->be_req));
 
+        DEBUG(SSSDBG_OP_FAILURE,
+              "Failed to connect to AD server: [%d](%s)\n",
+              ret, strerror(ret));
         goto fail;
     }
 
@@ -1040,7 +1050,7 @@ static void ad_subdomains_get_slave_domain_done(struct tevent_req *req)
      */
     ret = ad_subdomains_process(ctx, ctx->sd_ctx->be_ctx->domain,
                                 ctx->reply_count, ctx->reply,
-                                ctx->root_domain, &nsubdoms, &subdoms);
+                                ctx->root_domain_attrs, &nsubdoms, &subdoms);
     if (ret != EOK) {
         DEBUG(SSSDBG_OP_FAILURE, ("Cannot process subdomain list\n"));
         tevent_req_error(req, ret);
-- 
2.4.3

-------------- next part --------------
>From b09543eb9b6db92a8953aa9dc219e2e17bfab7fe Mon Sep 17 00:00:00 2001
From: Jakub Hrozek <jhrozek at redhat.com>
Date: Wed, 2 Sep 2015 13:41:26 +0200
Subject: [PATCH 8/8] IPA: Do not allow the AD lookup code to set backend as
 offline in server mode

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

In server mode, we should not allow the AD lookups to set the backend
offline. Rather just let them report an error and deal with the error
separately.
---
 src/providers/ipa/ipa_subdomains_id.c | 9 ++++++++-
 1 file changed, 8 insertions(+), 1 deletion(-)

diff --git a/src/providers/ipa/ipa_subdomains_id.c b/src/providers/ipa/ipa_subdomains_id.c
index ad1743ae5fe7ff80518e7bc58e4df04143732719..7dd003cf235ac572324468948d7f2b61d25a2491 100644
--- a/src/providers/ipa/ipa_subdomains_id.c
+++ b/src/providers/ipa/ipa_subdomains_id.c
@@ -634,6 +634,7 @@ ipa_get_ad_acct_send(TALLOC_CTX *mem_ctx,
             ret = ENOMEM;
             goto fail;
         }
+        clist[1]->ignore_mark_offline = true;
         break;
     default:
         clist = talloc_zero_array(req, struct sdap_id_conn_ctx *, 2);
@@ -642,6 +643,7 @@ ipa_get_ad_acct_send(TALLOC_CTX *mem_ctx,
             goto fail;
         }
         clist[0] = ad_id_ctx->ldap_ctx;
+        clist[0]->ignore_mark_offline = true;
         clist[1] = NULL;
     }
 
@@ -1037,7 +1039,12 @@ ipa_get_ad_acct_ad_part_done(struct tevent_req *subreq)
 
     ret = ad_handle_acct_info_recv(subreq, &state->dp_error, NULL);
     talloc_zfree(subreq);
-    if (ret != EOK) {
+    if (ret == ERR_SUBDOM_INACTIVE) {
+        DEBUG(SSSDBG_TRACE_LIBS, "AD subdomain is offline\n");
+        be_mark_dom_offline(state->obj_dom, be_req_get_be_ctx(state->be_req));
+        tevent_req_error(req, ret);
+        return;
+    } else if (ret != EOK) {
         DEBUG(SSSDBG_OP_FAILURE, "AD lookup failed: %d\n", ret);
         tevent_req_error(req, ret);
         return;
-- 
2.4.3



More information about the sssd-devel mailing list