[SSSD] [PATCH] Detect re-created trusts on an IPA server

Jakub Hrozek jhrozek at redhat.com
Wed Sep 23 08:09:40 UTC 2015


Hi,

the attached patches fix https://fedorahosted.org/sssd/ticket/2639

The first patches always retry fetching the keytab on lookup failure.

For the last two patches, I used Sumit's idea to check the last connection
time of the AD LDAP connection and compare it with the TDO modifyTimestamp
to only fetch the keytab and retry if there is a newer trust.

Testing is a bit tricky, but what I did is pretty much:
    $ getent passwd administrator at win.trust.test
    administrator at win.trust.test:*:962400500:962400500:Administrator:/home/win.trust.test/administrator:
    # Make sure sssd doesn't reuse already established and working
    # connection. Somewhat simulates GSSAPI connection expiring
    $ sudo pkill -USR1 sssd
    $ sudo pkill -USR2 sssd
    $ ipa trust-add --type=ad win.trust.test --admin Administrator --password
    # Make sure a wrong keytab is in place for the next connection
    # attempt
    $ sudo cp old.keytab /var/lib/sss/keytabs/win.trust.test.keytab
    $ sudo sss_cache -E
    $ getent passwd administrator at win.trust.test
    administrator at win.trust.test:*:962400500:962400500:Administrator:/home/win.trust.test/administrator:
-------------- next part --------------
>From 0d585474a63197957f5bf41fedf8ecac7b2ad810 Mon Sep 17 00:00:00 2001
From: Jakub Hrozek <jhrozek at redhat.com>
Date: Thu, 17 Sep 2015 17:09:24 +0200
Subject: [PATCH 1/6] IPA: Change ipa_server_trust_add_send request to be
 reusable from ID code

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

Expose a request ipa_server_trusted_dom_setup_send that sets up a
trusted domain. The setup might include actions like retrieving a keytab
for one-way trusts.

Creating the AD ID context for the trused domain is now done in the
caller of this new request.
---
 src/providers/ipa/ipa_subdomains.h        |  11 +++
 src/providers/ipa/ipa_subdomains_server.c | 122 ++++++++++++++++--------------
 2 files changed, 75 insertions(+), 58 deletions(-)

diff --git a/src/providers/ipa/ipa_subdomains.h b/src/providers/ipa/ipa_subdomains.h
index 2302c5f03e80de2ea1efad424769e777cd6dd8d5..0c13f8ed2eeda87237dfb097f532c7137095ddf1 100644
--- a/src/providers/ipa/ipa_subdomains.h
+++ b/src/providers/ipa/ipa_subdomains.h
@@ -52,6 +52,17 @@ struct ipa_ad_server_ctx {
     struct ipa_ad_server_ctx *next, *prev;
 };
 
+/* Can be used to set up trusted subdomain, for example fetch
+ * keytab in server mode
+ */
+struct tevent_req *
+ipa_server_trusted_dom_setup_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);
+errno_t ipa_server_trusted_dom_setup_recv(struct tevent_req *req);
+
 /* To be used by ipa_subdomains.c only */
 struct tevent_req *
 ipa_server_create_trusts_send(TALLOC_CTX *mem_ctx,
diff --git a/src/providers/ipa/ipa_subdomains_server.c b/src/providers/ipa/ipa_subdomains_server.c
index dfecab1bc362b5772379bae6d51f9cef8443f225..c561118946e2ba76b2b4076e5057b1b5c0075a41 100644
--- a/src/providers/ipa/ipa_subdomains_server.c
+++ b/src/providers/ipa/ipa_subdomains_server.c
@@ -563,7 +563,7 @@ done:
     return ret;
 }
 
-struct ipa_server_trust_add_state {
+struct ipa_server_trusted_dom_setup_state {
     struct tevent_context *ev;
     struct be_ctx *be_ctx;
     struct ipa_id_ctx *id_ctx;
@@ -578,22 +578,22 @@ struct ipa_server_trust_add_state {
     const char *ccache;
 };
 
-static errno_t ipa_server_trust_add_1way(struct tevent_req *req);
+static errno_t ipa_server_trusted_dom_setup_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,
-                          struct be_ctx *be_ctx,
-                          struct ipa_id_ctx *id_ctx,
-                          struct sss_domain_info *subdom)
+struct tevent_req *
+ipa_server_trusted_dom_setup_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_server_trusted_dom_setup_state *state = NULL;
     errno_t ret;
 
-    req = tevent_req_create(mem_ctx, &state, struct ipa_server_trust_add_state);
+    req = tevent_req_create(mem_ctx, &state,
+                            struct ipa_server_trusted_dom_setup_state);
     if (req == NULL) {
         return NULL;
     }
@@ -626,16 +626,19 @@ ipa_server_trust_add_send(TALLOC_CTX *mem_ctx,
           ipa_trust_dir2str(state->direction));
 
     if (state->direction & LSA_TRUST_DIRECTION_OUTBOUND) {
-        /* Use system keytab */
-        ret = ipa_server_trust_add_step(req);
+        /* Use system keytab, nothing to do here */
+        ret = EOK;
+        goto immediate;
     } else if (state->direction & LSA_TRUST_DIRECTION_INBOUND) {
         /* Need special keytab */
-        ret = ipa_server_trust_add_1way(req);
+        ret = ipa_server_trusted_dom_setup_1way(req);
         if (ret == EAGAIN) {
             /* In progress.. */
             return req;
         } else if (ret == EOK) {
-            ret = ipa_server_trust_add_step(req);
+            /* Keytab available, shortcut */
+            ret = EOK;
+            goto immediate;
         }
     } else {
         /* Even unset is an error at this point */
@@ -658,12 +661,12 @@ immediate:
     return req;
 }
 
-static errno_t ipa_server_trust_add_1way(struct tevent_req *req)
+static errno_t ipa_server_trusted_dom_setup_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);
+    struct ipa_server_trusted_dom_setup_state *state =
+            tevent_req_data(req, struct ipa_server_trusted_dom_setup_state);
     const char *hostname;
 
     state->keytab = forest_keytab(state, state->forest);
@@ -715,8 +718,8 @@ 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);
+    struct ipa_server_trusted_dom_setup_state *state =
+            tevent_req_data(req, struct ipa_server_trusted_dom_setup_state);
 
     ret = ipa_getkeytab_recv(subreq, NULL);
     talloc_zfree(subreq);
@@ -764,46 +767,12 @@ static void ipa_server_trust_1way_kt_done(struct tevent_req *subreq)
     DEBUG(SSSDBG_TRACE_FUNC,
           "Keytab %s contains the expected principals\n", state->new_keytab);
 
-    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) {
-        return ENOMEM;
-    }
-    trust_ctx->dom = state->subdom;
-    trust_ctx->ad_id_ctx = ad_id_ctx;
-
-    DLIST_ADD(state->id_ctx->server_mode->trusts, trust_ctx);
-    return EOK;
-}
-
-static errno_t ipa_server_trust_add_recv(struct tevent_req *req)
+errno_t ipa_server_trusted_dom_setup_recv(struct tevent_req *req)
 {
     TEVENT_REQ_RETURN_ON_ERROR(req);
     return EOK;
@@ -817,6 +786,7 @@ struct ipa_server_create_trusts_state {
 };
 
 static errno_t ipa_server_create_trusts_step(struct tevent_req *req);
+static errno_t ipa_server_create_trusts_ctx(struct tevent_req *req);
 static void ipa_server_create_trusts_done(struct tevent_req *subreq);
 
 struct tevent_req *
@@ -879,8 +849,11 @@ static errno_t ipa_server_create_trusts_step(struct tevent_req *req)
 
         /* Newly detected trust */
         if (trust_iter == NULL) {
-            subreq = ipa_server_trust_add_send(state, state->ev, state->be_ctx,
-                                               state->id_ctx, state->domiter);
+            subreq = ipa_server_trusted_dom_setup_send(state,
+                                                       state->ev,
+                                                       state->be_ctx,
+                                                       state->id_ctx,
+                                                       state->domiter);
             if (subreq == NULL) {
                 return ENOMEM;
             }
@@ -898,13 +871,19 @@ static void ipa_server_create_trusts_done(struct tevent_req *subreq)
     struct tevent_req *req = tevent_req_callback_data(subreq,
                                                       struct tevent_req);
 
-    ret = ipa_server_trust_add_recv(subreq);
+    ret = ipa_server_trusted_dom_setup_recv(subreq);
     talloc_zfree(subreq);
     if (ret != EOK) {
         tevent_req_error(req, ret);
         return;
     }
 
+    ret = ipa_server_create_trusts_ctx(req);
+    if (ret != EOK) {
+        tevent_req_error(req, ret);
+        return;
+    }
+
     ret = ipa_server_create_trusts_step(req);
     if (ret == EOK) {
         tevent_req_done(req);
@@ -917,6 +896,33 @@ static void ipa_server_create_trusts_done(struct tevent_req *subreq)
     /* Will cycle back */
 }
 
+static errno_t ipa_server_create_trusts_ctx(struct tevent_req *req)
+{
+    struct ipa_ad_server_ctx *trust_ctx;
+    struct ad_id_ctx *ad_id_ctx;
+    errno_t ret;
+    struct ipa_server_create_trusts_state *state = NULL;
+
+    state = tevent_req_data(req, struct ipa_server_create_trusts_state);
+
+    ret = ipa_ad_ctx_new(state->be_ctx, state->id_ctx, state->domiter, &ad_id_ctx);
+    if (ret != EOK) {
+        DEBUG(SSSDBG_OP_FAILURE,
+              "Cannot create ad_id_ctx for subdomain %s\n", state->domiter->name);
+        return ret;
+    }
+
+    trust_ctx = talloc(state->id_ctx->server_mode, struct ipa_ad_server_ctx);
+    if (trust_ctx == NULL) {
+        return ENOMEM;
+    }
+    trust_ctx->dom = state->domiter;
+    trust_ctx->ad_id_ctx = ad_id_ctx;
+
+    DLIST_ADD(state->id_ctx->server_mode->trusts, trust_ctx);
+    return EOK;
+}
+
 errno_t ipa_server_create_trusts_recv(struct tevent_req *req)
 {
     TEVENT_REQ_RETURN_ON_ERROR(req);
-- 
2.4.3

-------------- next part --------------
>From 63c31bbd54f92a90f82f1a9db09b3b4ac2e7cb4a Mon Sep 17 00:00:00 2001
From: Jakub Hrozek <jhrozek at redhat.com>
Date: Mon, 21 Sep 2015 12:31:18 +0200
Subject: [PATCH 2/6] FO: Add an API to reset all servers in a single service

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

Previously, we had a function that allowed the caller to reset the
status of all services in the global fail over context. This patch adds
a new function that allows the caller to reset a single service instead.

The main user would be IPA subdomain provider that might need to reset
the status of an AD trusted domain on demand.
---
 src/providers/data_provider_fo.c | 19 +++++++++++++++++++
 src/providers/dp_backend.h       |  1 +
 src/providers/fail_over.c        | 30 ++++++++++++++++++++----------
 src/providers/fail_over.h        |  2 ++
 4 files changed, 42 insertions(+), 10 deletions(-)

diff --git a/src/providers/data_provider_fo.c b/src/providers/data_provider_fo.c
index 41d70de065260f31dcea4c2b664a1f436823ccc1..cd57340a0ba0ac7e474dc502bf1f1b4de0e1f778 100644
--- a/src/providers/data_provider_fo.c
+++ b/src/providers/data_provider_fo.c
@@ -743,6 +743,25 @@ void reset_fo(struct be_ctx *be_ctx)
     fo_reset_services(be_ctx->be_fo->fo_ctx);
 }
 
+void be_fo_reset_svc(struct be_ctx *be_ctx,
+                     const char *svc_name)
+{
+    struct fo_service *service;
+    int ret;
+
+    DEBUG(SSSDBG_TRACE_LIBS,
+          "Resetting all servers in service %s\n", svc_name);
+
+    ret = fo_get_service(be_ctx->be_fo->fo_ctx, svc_name, &service);
+    if (ret != EOK) {
+        DEBUG(SSSDBG_MINOR_FAILURE,
+              "Cannot retrieve service [%s]\n", svc_name);
+        return;
+    }
+
+    fo_reset_servers(service);
+}
+
 void _be_fo_set_port_status(struct be_ctx *ctx,
                             const char *service_name,
                             struct fo_server *server,
diff --git a/src/providers/dp_backend.h b/src/providers/dp_backend.h
index 4bffcee9e8739302343b7813d3540eb43e966581..0ced851be8468ce21a9d283e26461fc47194557e 100644
--- a/src/providers/dp_backend.h
+++ b/src/providers/dp_backend.h
@@ -283,6 +283,7 @@ int be_fo_run_callbacks_at_next_request(struct be_ctx *ctx,
                                         const char *service_name);
 
 void reset_fo(struct be_ctx *be_ctx);
+void be_fo_reset_svc(struct be_ctx *be_ctx, const char *svc_name);
 
 errno_t be_res_init(struct be_ctx *ctx);
 
diff --git a/src/providers/fail_over.c b/src/providers/fail_over.c
index 6d835ae0efdfdf96532e8458e12238ba17910a4f..562f0b3fd7870387a80c889b245cda0d39dea509 100644
--- a/src/providers/fail_over.c
+++ b/src/providers/fail_over.c
@@ -1547,21 +1547,31 @@ time_t fo_get_service_retry_timeout(struct fo_service *svc)
     return svc->ctx->opts->retry_timeout;
 }
 
+void fo_reset_servers(struct fo_service *service)
+{
+    struct fo_server *server;
+
+    DLIST_FOR_EACH(server, service->server_list) {
+        if (server->srv_data != NULL) {
+            set_srv_data_status(server->srv_data, SRV_NEUTRAL);
+        } else {
+            fo_set_server_status(server, SERVER_NAME_NOT_RESOLVED);
+        }
+
+        fo_set_port_status(server, PORT_NEUTRAL);
+    }
+}
+
+
 void fo_reset_services(struct fo_ctx *fo_ctx)
 {
     struct fo_service *service;
-    struct fo_server *server;
+
+    DEBUG(SSSDBG_TRACE_LIBS,
+          "Resetting all servers in all services\n");
 
     DLIST_FOR_EACH(service, fo_ctx->service_list) {
-        DLIST_FOR_EACH(server, service->server_list) {
-            if (server->srv_data != NULL) {
-                set_srv_data_status(server->srv_data, SRV_NEUTRAL);
-            } else {
-                fo_set_server_status(server, SERVER_NAME_NOT_RESOLVED);
-            }
-
-            fo_set_port_status(server, PORT_NEUTRAL);
-        }
+        fo_reset_servers(service);
     }
 }
 
diff --git a/src/providers/fail_over.h b/src/providers/fail_over.h
index d44ad2ff145dc6b3617e6f2ea665c7d82d923ddb..e49c6414a14eb6ca2cad333f8efbb58576811345 100644
--- a/src/providers/fail_over.h
+++ b/src/providers/fail_over.h
@@ -198,6 +198,8 @@ time_t fo_get_service_retry_timeout(struct fo_service *svc);
 
 void fo_reset_services(struct fo_ctx *fo_ctx);
 
+void fo_reset_servers(struct fo_service *svc);
+
 bool fo_svc_has_server(struct fo_service *service, struct fo_server *server);
 
 /*
-- 
2.4.3

-------------- next part --------------
>From 63de05590cd0a9c1319880f44c6c1557e272d249 Mon Sep 17 00:00:00 2001
From: Jakub Hrozek <jhrozek at redhat.com>
Date: Mon, 21 Sep 2015 12:31:38 +0200
Subject: [PATCH 3/6] FO: Also reset the server common data in addition to SRV

In a server that is expanded from a SRV query was reset, only it's
'meta-server' status was set to neutral, but the server->common
structure still retained its not_working status.

This patch also resets the status of the common structure so that both
the SRV query and resolving the server are retried next time.
---
 src/providers/fail_over.c      |  4 +++-
 src/tests/cmocka/test_fo_srv.c | 44 +++++++++++++++++++++++++++++++++++++-----
 2 files changed, 42 insertions(+), 6 deletions(-)

diff --git a/src/providers/fail_over.c b/src/providers/fail_over.c
index 562f0b3fd7870387a80c889b245cda0d39dea509..b076687ac6e571f7e27402fd11ac60183ea46951 100644
--- a/src/providers/fail_over.c
+++ b/src/providers/fail_over.c
@@ -1554,7 +1554,9 @@ void fo_reset_servers(struct fo_service *service)
     DLIST_FOR_EACH(server, service->server_list) {
         if (server->srv_data != NULL) {
             set_srv_data_status(server->srv_data, SRV_NEUTRAL);
-        } else {
+        }
+
+        if (server->common) {
             fo_set_server_status(server, SERVER_NAME_NOT_RESOLVED);
         }
 
diff --git a/src/tests/cmocka/test_fo_srv.c b/src/tests/cmocka/test_fo_srv.c
index e5c5e4fe3b818ca2df0ab7bcd1a83719fefba191..953f8b48b951e241b43d0236ff5971cf0e0703d8 100644
--- a/src/tests/cmocka/test_fo_srv.c
+++ b/src/tests/cmocka/test_fo_srv.c
@@ -292,13 +292,10 @@ static void test_fo_srv_done1(struct tevent_req *req);
 static void test_fo_srv_done2(struct tevent_req *req);
 static void test_fo_srv_done3(struct tevent_req *req);
 static void test_fo_srv_done4(struct tevent_req *req);
+static void test_fo_srv_done5(struct tevent_req *req);
 
-void test_fo_srv(void **state)
+static void test_fo_srv_mock_dns(struct test_fo_srv_ctx *test_ctx)
 {
-    errno_t ret;
-    struct tevent_req *req;
-    struct test_fo_srv_ctx *test_ctx =
-        talloc_get_type(*state, struct test_fo_srv_ctx);
     struct ares_srv_reply *s1;
     struct ares_srv_reply *s2;
     char *dns_domain;
@@ -325,6 +322,16 @@ void test_fo_srv(void **state)
     assert_non_null(dns_domain);
 
     mock_srv_results(s1, TEST_SRV_TTL, dns_domain);
+}
+
+static void test_fo_srv(void **state)
+{
+    errno_t ret;
+    struct tevent_req *req;
+    struct test_fo_srv_ctx *test_ctx =
+        talloc_get_type(*state, struct test_fo_srv_ctx);
+
+    test_fo_srv_mock_dns(test_ctx);
 
     ret = fo_add_srv_server(test_ctx->fo_svc, "_ldap", "sssd.com",
                             "sssd.local", "tcp", test_ctx);
@@ -425,6 +432,33 @@ static void test_fo_srv_done4(struct tevent_req *req)
     /* No servers are left..*/
     assert_int_equal(ret, ENOENT);
 
+    /* reset the server status and try again.. */
+    fo_reset_servers(test_ctx->fo_svc);
+    test_fo_srv_mock_dns(test_ctx);
+
+    req = fo_resolve_service_send(test_ctx, test_ctx->ctx->ev,
+                                  test_ctx->resolv, test_ctx->fo_ctx,
+                                  test_ctx->fo_svc);
+    assert_non_null(req);
+    tevent_req_set_callback(req, test_fo_srv_done5, test_ctx);
+}
+
+static void test_fo_srv_done5(struct tevent_req *req)
+{
+    struct test_fo_srv_ctx *test_ctx = \
+        tevent_req_callback_data(req, struct test_fo_srv_ctx);
+    struct fo_server *srv;
+    errno_t ret;
+
+    ret = fo_resolve_service_recv(req, &srv);
+    talloc_zfree(req);
+
+    assert_int_equal(ret, ERR_OK);
+
+    /* ldap1.sssd.com has lower priority, it must always be first */
+    check_server(srv, 389, "ldap1.sssd.com");
+
+    /* OK, we made a full circle with the test, done */
     test_ctx->ctx->error = ERR_OK;
     test_ctx->ctx->done = true;
 }
-- 
2.4.3

-------------- next part --------------
>From d382b037398d8040045ec16fdd123fc5125dcb21 Mon Sep 17 00:00:00 2001
From: Jakub Hrozek <jhrozek at redhat.com>
Date: Thu, 17 Sep 2015 17:11:34 +0200
Subject: [PATCH 4/6] IPA: Retry fetching keytab if IPA user lookup fails

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

Instead of calling ipa_get_ad_acct_send directly, call a new request
ipa_srv_ad_acct_send. The new request wraps ipa_get_ad_acct_send and
either tries to request a new keytab every time the lookup fails but the
domain is online.

be_mark_dom_offline() is called when the retry fails with the new code.

The retry tries to re-setup the trusted domain. With two-way setups, the
request is a no-op. With one-way trust setups, the request re-fetches
new keytab unconditionally.
---
 src/providers/ipa/ipa_id.h            |   9 --
 src/providers/ipa/ipa_subdomains_id.c | 190 +++++++++++++++++++++++++++++++++-
 2 files changed, 185 insertions(+), 14 deletions(-)

diff --git a/src/providers/ipa/ipa_id.h b/src/providers/ipa/ipa_id.h
index c03ca037a2850478a8f4933bac4fcf8bd70ada04..91a94115386955f63eca706bf6f4ac41884445eb 100644
--- a/src/providers/ipa/ipa_id.h
+++ b/src/providers/ipa/ipa_id.h
@@ -70,15 +70,6 @@ struct tevent_req *ipa_get_subdom_acct_send(TALLOC_CTX *memctx,
                                             struct be_acct_req *ar);
 int ipa_get_subdom_acct_recv(struct tevent_req *req, int *dp_error_out);
 
-struct tevent_req *ipa_get_ad_acct_send(TALLOC_CTX *mem_ctx,
-                                        struct tevent_context *ev,
-                                        struct ipa_id_ctx *ipa_ctx,
-                                        struct be_req *be_req,
-                                        struct sysdb_attrs *override_attrs,
-                                        struct be_acct_req *ar);
-
-errno_t ipa_get_ad_acct_recv(struct tevent_req *req, int *dp_error_out);
-
 errno_t get_be_acct_req_for_sid(TALLOC_CTX *mem_ctx, const char *sid,
                                 const char *domain_name,
                                 struct be_acct_req **_ar);
diff --git a/src/providers/ipa/ipa_subdomains_id.c b/src/providers/ipa/ipa_subdomains_id.c
index 7c609ab6e69d6f23c4de7c1c9569d73074d4e2dd..8f13608bcfd2f17c27fcba7f087e1a27086a2a1c 100644
--- a/src/providers/ipa/ipa_subdomains_id.c
+++ b/src/providers/ipa/ipa_subdomains_id.c
@@ -34,6 +34,16 @@
 #include "providers/ad/ad_id.h"
 #include "providers/ipa/ipa_subdomains.h"
 
+static struct tevent_req *
+ipa_srv_ad_acct_send(TALLOC_CTX *mem_ctx,
+                     struct tevent_context *ev,
+                     struct ipa_id_ctx *ipa_ctx,
+                     struct be_req *be_req,
+                     struct sysdb_attrs *override_attrs,
+                     struct be_acct_req *ar);
+static errno_t
+ipa_srv_ad_acct_recv(struct tevent_req *req, int *dp_error_out);
+
 struct ipa_subdomain_account_state {
     struct tevent_context *ev;
     struct ipa_id_ctx *ipa_ctx;
@@ -45,6 +55,7 @@ struct ipa_subdomain_account_state {
     struct be_acct_req *ar;
 
     bool ipa_server_mode;
+    bool server_retry;
     int entry_type;
     const char *filter;
     int filter_type;
@@ -263,7 +274,7 @@ static errno_t ipa_subdomain_account_get_original_step(struct tevent_req *req,
     struct tevent_req *subreq;
 
     if (state->ipa_server_mode) {
-        subreq = ipa_get_ad_acct_send(state, state->ev, state->ipa_ctx,
+        subreq = ipa_srv_ad_acct_send(state, state->ev, state->ipa_ctx,
                                       state->be_req, state->override_attrs, ar);
     } else {
         subreq = ipa_get_subdom_acct_send(state, state->ev, state->ipa_ctx,
@@ -291,7 +302,7 @@ static void ipa_subdomain_account_done(struct tevent_req *subreq)
     int ret;
 
     if (state->ipa_server_mode) {
-        ret = ipa_get_ad_acct_recv(subreq, &dp_error);
+        ret = ipa_srv_ad_acct_recv(subreq, &dp_error);
     } else {
         ret = ipa_get_subdom_acct_recv(subreq, &dp_error);
     }
@@ -576,7 +587,7 @@ static void ipa_get_ad_acct_done(struct tevent_req *subreq);
 static struct ad_id_ctx *ipa_get_ad_id_ctx(struct ipa_id_ctx *ipa_ctx,
                                            struct sss_domain_info *dom);
 
-struct tevent_req *
+static struct tevent_req *
 ipa_get_ad_acct_send(TALLOC_CTX *mem_ctx,
                      struct tevent_context *ev,
                      struct ipa_id_ctx *ipa_ctx,
@@ -1040,7 +1051,6 @@ 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 == ERR_SUBDOM_INACTIVE) {
-        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) {
@@ -1325,7 +1335,7 @@ ipa_get_ad_acct_done(struct tevent_req *subreq)
     tevent_req_done(req);
 }
 
-errno_t
+static errno_t
 ipa_get_ad_acct_recv(struct tevent_req *req, int *dp_error_out)
 {
     struct ipa_get_ad_acct_state *state = tevent_req_data(req,
@@ -1339,3 +1349,173 @@ ipa_get_ad_acct_recv(struct tevent_req *req, int *dp_error_out)
 
     return EOK;
 }
+
+struct ipa_srv_ad_acct_state {
+    struct tevent_context *ev;
+    struct ipa_id_ctx *ipa_ctx;
+    struct be_req *be_req;
+    struct sysdb_attrs *override_attrs;
+    struct be_acct_req *ar;
+
+    struct sss_domain_info *obj_dom;
+    struct be_ctx *be_ctx;
+    bool retry;
+
+    int dp_error;
+};
+
+static int ipa_srv_ad_acct_lookup_step(struct tevent_req *req);
+static void ipa_srv_ad_acct_lookup_done(struct tevent_req *subreq);
+static void ipa_srv_ad_acct_retried(struct tevent_req *subreq);
+
+static struct tevent_req *
+ipa_srv_ad_acct_send(TALLOC_CTX *mem_ctx,
+                     struct tevent_context *ev,
+                     struct ipa_id_ctx *ipa_ctx,
+                     struct be_req *be_req,
+                     struct sysdb_attrs *override_attrs,
+                     struct be_acct_req *ar)
+{
+    errno_t ret;
+    struct tevent_req *req;
+    struct ipa_srv_ad_acct_state *state;
+
+    req = tevent_req_create(mem_ctx, &state, struct ipa_srv_ad_acct_state);
+    if (req == NULL) {
+        return NULL;
+    }
+
+    state->ev = ev;
+    state->ipa_ctx = ipa_ctx;
+    state->be_req = be_req;
+    state->override_attrs = override_attrs;
+    state->ar = ar;
+    state->retry = true;
+    state->dp_error = DP_ERR_FATAL;
+    state->be_ctx = be_req_get_be_ctx(state->be_req);
+
+    state->obj_dom = find_domain_by_name(
+                                  state->ipa_ctx->sdap_id_ctx->be->domain,
+                                  state->ar->domain, true);
+    if (state->obj_dom == NULL) {
+        DEBUG(SSSDBG_OP_FAILURE, "Domain not found\n");
+        ret = ERR_DOMAIN_NOT_FOUND;
+        goto fail;
+    }
+
+    ret = ipa_srv_ad_acct_lookup_step(req);
+    if (ret != EOK) {
+        goto fail;
+    }
+
+    return req;
+
+fail:
+    tevent_req_error(req, ret);
+    tevent_req_post(req, ev);
+    return req;
+}
+
+static int ipa_srv_ad_acct_lookup_step(struct tevent_req *req)
+{
+    struct tevent_req *subreq;
+    struct ipa_srv_ad_acct_state *state = tevent_req_data(req,
+                                            struct ipa_srv_ad_acct_state);
+
+    DEBUG(SSSDBG_TRACE_FUNC, "Looking up AD account\n");
+    subreq = ipa_get_ad_acct_send(state, state->ev, state->ipa_ctx,
+                                  state->be_req, state->override_attrs,
+                                  state->ar);
+    if (subreq == NULL) {
+        return ENOMEM;
+    }
+    tevent_req_set_callback(subreq, ipa_srv_ad_acct_lookup_done, req);
+
+    return EOK;
+}
+
+static void ipa_srv_ad_acct_lookup_done(struct tevent_req *subreq)
+{
+    errno_t ret;
+    int dp_error = DP_ERR_FATAL;
+    struct tevent_req *req = tevent_req_callback_data(subreq,
+                                                struct tevent_req);
+    struct ipa_srv_ad_acct_state *state = tevent_req_data(req,
+                                            struct ipa_srv_ad_acct_state);
+
+    ret = ipa_get_ad_acct_recv(subreq, &dp_error);
+    talloc_free(subreq);
+    if (ret == ERR_SUBDOM_INACTIVE && state->retry == true) {
+
+        state->retry = false;
+
+        DEBUG(SSSDBG_MINOR_FAILURE,
+              "Sudomain lookup failed, will try to reset sudomain..\n");
+        subreq = ipa_server_trusted_dom_setup_send(state, state->ev,
+                                                   state->be_ctx,
+                                                   state->ipa_ctx,
+                                                   state->obj_dom);
+        if (subreq == NULL) {
+            goto fail;
+        }
+        tevent_req_set_callback(subreq, ipa_srv_ad_acct_retried, req);
+        return;
+    } else if (ret != EOK) {
+        be_mark_dom_offline(state->obj_dom, state->be_ctx);
+
+        DEBUG(SSSDBG_OP_FAILURE, "ipa_get_*_acct request failed: [%d]: %s.\n",
+              ret, sss_strerror(ret));
+        goto fail;
+    }
+
+    state->dp_error = DP_ERR_OK;
+    tevent_req_done(req);
+    return;
+
+fail:
+    state->dp_error = dp_error;
+    tevent_req_error(req, ret);
+}
+
+static void ipa_srv_ad_acct_retried(struct tevent_req *subreq)
+{
+    errno_t ret;
+    struct tevent_req *req = tevent_req_callback_data(subreq,
+                                                struct tevent_req);
+    struct ipa_srv_ad_acct_state *state = tevent_req_data(req,
+                                            struct ipa_srv_ad_acct_state);
+
+    ret = ipa_server_trusted_dom_setup_recv(subreq);
+    talloc_free(subreq);
+    if (ret != EOK) {
+        DEBUG(SSSDBG_OP_FAILURE,
+              "Failed to re-set subdomain [%d]: %s\n", ret, sss_strerror(ret));
+        state->dp_error = DP_ERR_FATAL;
+        tevent_req_error(req, ret);
+    }
+
+    DEBUG(SSSDBG_TRACE_FUNC, "Sudomain re-set, will retry lookup\n");
+    be_fo_reset_svc(state->be_ctx, state->obj_dom->name);
+
+    ret = ipa_srv_ad_acct_lookup_step(req);
+    if (ret != EOK) {
+        DEBUG(SSSDBG_OP_FAILURE,
+              "Failed to look up AD acct [%d]: %s\n", ret, sss_strerror(ret));
+        state->dp_error = DP_ERR_FATAL;
+        tevent_req_error(req, ret);
+    }
+}
+
+static errno_t
+ipa_srv_ad_acct_recv(struct tevent_req *req, int *dp_error_out)
+{
+    struct ipa_srv_ad_acct_state *state = tevent_req_data(req,
+                                                struct ipa_srv_ad_acct_state);
+
+    if (dp_error_out) {
+        *dp_error_out = state->dp_error;
+    }
+
+    TEVENT_REQ_RETURN_ON_ERROR(req);
+    return EOK;
+}
-- 
2.4.3

-------------- next part --------------
>From c40b2e130b559caf90cf737977edba1f5703efc9 Mon Sep 17 00:00:00 2001
From: Jakub Hrozek <jhrozek at redhat.com>
Date: Mon, 21 Sep 2015 13:18:47 +0200
Subject: [PATCH 5/6] LDAP: Save connection time from LDAP provider

Whenever a connection request ends successfully, store the success time
to the sdap_id_conn_ctx.
---
 src/providers/ldap/ldap_common.h | 2 ++
 src/providers/ldap/sdap_id_op.c  | 1 +
 2 files changed, 3 insertions(+)

diff --git a/src/providers/ldap/ldap_common.h b/src/providers/ldap/ldap_common.h
index f552520a0503908f82b845f8e813cf67306ec954..2e060f0ba6448aa6aceda02b3ac5a710eb64a6e0 100644
--- a/src/providers/ldap/ldap_common.h
+++ b/src/providers/ldap/ldap_common.h
@@ -57,6 +57,8 @@ struct sdap_id_conn_ctx {
     struct sdap_id_conn_ctx *prev, *next;
     /* do not go offline, try another connection */
     bool ignore_mark_offline;
+    /* time of last successfull connection */
+    time_t conn_time;
 };
 
 struct sdap_id_ctx {
diff --git a/src/providers/ldap/sdap_id_op.c b/src/providers/ldap/sdap_id_op.c
index 0474a9cb7828ef43ef76cf6bebac077315296875..6d8cdc38432e3436b8f94695876d3e605f826be2 100644
--- a/src/providers/ldap/sdap_id_op.c
+++ b/src/providers/ldap/sdap_id_op.c
@@ -773,6 +773,7 @@ static void sdap_id_op_connect_req_complete(struct sdap_id_op *op, int dp_error,
     state->result = ret;
 
     if (ret == EOK) {
+        op->conn_data->conn_cache->id_conn->conn_time = time(NULL);
         tevent_req_done(req);
     } else {
         sdap_id_op_hook_conn_data(op, NULL);
-- 
2.4.3

-------------- next part --------------
>From 3366a3cdbf93eea757123e11a32307a005c05443 Mon Sep 17 00:00:00 2001
From: Jakub Hrozek <jhrozek at redhat.com>
Date: Mon, 21 Sep 2015 15:53:50 +0200
Subject: [PATCH 6/6] IPA: Only re-fetch the keytab if modifyTimestamp is newer
 than last LDAP connection

Resolves:
    https://fedorahosted.org/sssd/ticket/2639

When a subdomain account lookup errors out, try to re-setup the trust
object. Only do this, if the connection was established after the last
re-set of the trust object.

Internally, the setup function looks at the modifyTimestamp operational
attribute of the TDO. If the modifyTimestamp is newer than the last
keytab check, then the trust was re-created and we need to fetch the
keytab again.

Marking the back end as online re-sets the TDO check timestamp so that
after cycling the sssd, the keytab would always be checked.
---
 src/providers/ipa/ipa_id.c                |   4 +
 src/providers/ipa/ipa_subdomains.h        |   7 +-
 src/providers/ipa/ipa_subdomains_id.c     |  67 +++++--
 src/providers/ipa/ipa_subdomains_server.c | 290 +++++++++++++++++++++++++++++-
 4 files changed, 346 insertions(+), 22 deletions(-)

diff --git a/src/providers/ipa/ipa_id.c b/src/providers/ipa/ipa_id.c
index e81ccb34dd6eb44618538593f5473fbe5e89d896..1e91fc5990e1269172682382874d8e75ecf13f80 100644
--- a/src/providers/ipa/ipa_id.c
+++ b/src/providers/ipa/ipa_id.c
@@ -1293,5 +1293,9 @@ void ipa_check_online(struct be_req *be_req)
     ipa_ctx = talloc_get_type(be_ctx->bet_info[BET_ID].pvt_bet_data,
                               struct ipa_id_ctx);
 
+    if (ipa_ctx->server_mode == NULL) {
+        ipa_subdom_reset_trust(ipa_ctx->server_mode);
+    }
+
     return sdap_do_online_check(be_req, ipa_ctx->sdap_id_ctx);
 }
diff --git a/src/providers/ipa/ipa_subdomains.h b/src/providers/ipa/ipa_subdomains.h
index 0c13f8ed2eeda87237dfb097f532c7137095ddf1..e33bad92fa1b66d359ce7842ebbb59bbbf701305 100644
--- a/src/providers/ipa/ipa_subdomains.h
+++ b/src/providers/ipa/ipa_subdomains.h
@@ -48,6 +48,7 @@ int ipa_subdom_init(struct be_ctx *be_ctx,
 struct ipa_ad_server_ctx {
     struct sss_domain_info *dom;
     struct ad_id_ctx *ad_id_ctx;
+    time_t last_kt_check;
 
     struct ipa_ad_server_ctx *next, *prev;
 };
@@ -60,9 +61,13 @@ ipa_server_trusted_dom_setup_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 sss_domain_info *subdom,
+                                  time_t newer_than);
 errno_t ipa_server_trusted_dom_setup_recv(struct tevent_req *req);
 
+/* Reset keytab check time after going online */
+void ipa_subdom_reset_trust(struct ipa_server_mode_ctx *server_mode);
+
 /* To be used by ipa_subdomains.c only */
 struct tevent_req *
 ipa_server_create_trusts_send(TALLOC_CTX *mem_ctx,
diff --git a/src/providers/ipa/ipa_subdomains_id.c b/src/providers/ipa/ipa_subdomains_id.c
index 8f13608bcfd2f17c27fcba7f087e1a27086a2a1c..2c5e6d1959ed0c880e79acff2b8dbc54caf6ac62 100644
--- a/src/providers/ipa/ipa_subdomains_id.c
+++ b/src/providers/ipa/ipa_subdomains_id.c
@@ -681,8 +681,8 @@ fail:
     return req;
 }
 
-static struct ad_id_ctx *
-ipa_get_ad_id_ctx(struct ipa_id_ctx *ipa_ctx,
+static struct ipa_ad_server_ctx *
+ipa_get_trust_ctx(struct ipa_id_ctx *ipa_ctx,
                   struct sss_domain_info *dom)
 {
     struct ipa_ad_server_ctx *iter;
@@ -691,7 +691,17 @@ ipa_get_ad_id_ctx(struct ipa_id_ctx *ipa_ctx,
         if (iter->dom == dom) break;
     }
 
-    return (iter) ? iter->ad_id_ctx : NULL;
+    return iter;
+}
+
+static struct ad_id_ctx *
+ipa_get_ad_id_ctx(struct ipa_id_ctx *ipa_ctx,
+                  struct sss_domain_info *dom)
+{
+    struct ipa_ad_server_ctx *trust;
+
+    trust = ipa_get_trust_ctx(ipa_ctx, dom);
+    return (trust) ? trust->ad_id_ctx : NULL;
 }
 
 static errno_t
@@ -1365,6 +1375,7 @@ struct ipa_srv_ad_acct_state {
 };
 
 static int ipa_srv_ad_acct_lookup_step(struct tevent_req *req);
+static errno_t ipa_srv_ad_acct_retry(struct tevent_req *req);
 static void ipa_srv_ad_acct_lookup_done(struct tevent_req *subreq);
 static void ipa_srv_ad_acct_retried(struct tevent_req *subreq);
 
@@ -1446,19 +1457,14 @@ static void ipa_srv_ad_acct_lookup_done(struct tevent_req *subreq)
     ret = ipa_get_ad_acct_recv(subreq, &dp_error);
     talloc_free(subreq);
     if (ret == ERR_SUBDOM_INACTIVE && state->retry == true) {
-
-        state->retry = false;
-
         DEBUG(SSSDBG_MINOR_FAILURE,
               "Sudomain lookup failed, will try to reset sudomain..\n");
-        subreq = ipa_server_trusted_dom_setup_send(state, state->ev,
-                                                   state->be_ctx,
-                                                   state->ipa_ctx,
-                                                   state->obj_dom);
-        if (subreq == NULL) {
+        ret = ipa_srv_ad_acct_retry(req);
+        if (ret != EOK) {
+            DEBUG(SSSDBG_OP_FAILURE,
+                  "Retry failed[ [%d]: %s\n", ret, sss_strerror(ret));
             goto fail;
         }
-        tevent_req_set_callback(subreq, ipa_srv_ad_acct_retried, req);
         return;
     } else if (ret != EOK) {
         be_mark_dom_offline(state->obj_dom, state->be_ctx);
@@ -1477,6 +1483,43 @@ fail:
     tevent_req_error(req, ret);
 }
 
+static errno_t ipa_srv_ad_acct_retry(struct tevent_req *req)
+{
+    struct tevent_req *subreq;
+    struct ipa_ad_server_ctx *trust;
+    struct ipa_srv_ad_acct_state *state = tevent_req_data(req,
+                                            struct ipa_srv_ad_acct_state);
+
+    state->retry = false;
+
+    trust = ipa_get_trust_ctx(state->ipa_ctx, state->obj_dom);
+    if (trust == NULL) {
+        DEBUG(SSSDBG_OP_FAILURE,
+              "Cannot find truct ctx for %s\n", state->obj_dom->name);
+        return EINVAL;
+    }
+
+    if (trust->last_kt_check > trust->ad_id_ctx->ldap_ctx->conn_time) {
+        DEBUG(SSSDBG_MINOR_FAILURE,
+              "Last kt check time %ld is past last connection time %ld\n",
+              trust->last_kt_check, trust->ad_id_ctx->ldap_ctx->conn_time);
+        return ERR_SUBDOM_INACTIVE;
+    }
+
+    subreq = ipa_server_trusted_dom_setup_send(
+                                        state, state->ev,
+                                        state->be_ctx,
+                                        state->ipa_ctx,
+                                        state->obj_dom,
+                                        trust->ad_id_ctx->ldap_ctx->conn_time);
+    if (subreq == NULL) {
+        return ENOMEM;
+    }
+    tevent_req_set_callback(subreq, ipa_srv_ad_acct_retried, req);
+
+    return EOK;
+}
+
 static void ipa_srv_ad_acct_retried(struct tevent_req *subreq)
 {
     errno_t ret;
diff --git a/src/providers/ipa/ipa_subdomains_server.c b/src/providers/ipa/ipa_subdomains_server.c
index c561118946e2ba76b2b4076e5057b1b5c0075a41..23d3325b7572c0ebd31630bdbfc40bc8fed8e826 100644
--- a/src/providers/ipa/ipa_subdomains_server.c
+++ b/src/providers/ipa/ipa_subdomains_server.c
@@ -34,6 +34,9 @@
 #define LSA_TRUST_DIRECTION_INBOUND  0x00000001
 #define LSA_TRUST_DIRECTION_OUTBOUND 0x00000002
 
+#define SUBDOMAINS_FILTER "objectclass=ipaNTTrustedDomain"
+#define MODIFY_TIMESTAMP  "modifyTimestamp"
+
 static char *forest_keytab(TALLOC_CTX *mem_ctx, const char *forest)
 {
     return talloc_asprintf(mem_ctx,
@@ -330,6 +333,214 @@ ipa_ad_ctx_new(struct be_ctx *be_ctx,
     return EOK;
 }
 
+struct tdo_get_mod_stamp_state {
+    struct tevent_context *ev;
+    struct ipa_id_ctx *id_ctx;
+    const char *tdo_name;
+
+    struct sdap_id_op *sdap_op;
+    struct sdap_search_base **bases;
+    int search_base_iter;
+    const char *filter;
+
+    time_t tdo_mod;
+};
+
+static void tdo_get_mod_stamp_conn_done(struct tevent_req *subreq);
+static errno_t tdo_get_mod_stamp_next_base(struct tevent_req *req);
+static void tdo_get_mod_stamp_done(struct tevent_req *subreq);
+
+static struct tevent_req *
+tdo_get_mod_stamp_send(TALLOC_CTX *mem_ctx,
+                       struct tevent_context *ev,
+                       struct ipa_id_ctx *id_ctx,
+                       const char *tdo_name)
+{
+    errno_t ret;
+    struct tevent_req *req = NULL;
+    struct tevent_req *subreq = NULL;
+    struct tdo_get_mod_stamp_state *state;
+
+    req = tevent_req_create(mem_ctx, &state, struct tdo_get_mod_stamp_state);
+    if (req == NULL) {
+        return NULL;
+    }
+
+    state->search_base_iter = 0;
+    state->ev = ev;
+    state->id_ctx = id_ctx;
+    state->tdo_name = tdo_name;
+    state->bases = id_ctx->ipa_options->subdomains_search_bases;
+    state->filter = talloc_asprintf(state, "(&(cn=%s)(%s))",
+                                    tdo_name, SUBDOMAINS_FILTER);
+    if (state->filter == NULL) {
+        ret = ENOMEM;
+        goto fail;
+    }
+
+    state->sdap_op = sdap_id_op_create(state,
+                                       id_ctx->sdap_id_ctx->conn->conn_cache);
+    if (state->sdap_op == NULL) {
+        DEBUG(SSSDBG_OP_FAILURE, "sdap_id_op_create failed.\n");
+        goto fail;
+    }
+
+    subreq = sdap_id_op_connect_send(state->sdap_op, state, &ret);
+    if (subreq == NULL) {
+        DEBUG(SSSDBG_OP_FAILURE, "sdap_id_op_connect_send failed: %d(%s).\n",
+                                  ret, sss_strerror(ret));
+        goto fail;
+    }
+
+    tevent_req_set_callback(subreq, tdo_get_mod_stamp_conn_done, req);
+    return req;
+
+fail:
+    tevent_req_error(req, ret);
+    tevent_req_post(req, ev);
+    return req;
+}
+
+static void tdo_get_mod_stamp_conn_done(struct tevent_req *subreq)
+{
+    errno_t ret;
+    struct tevent_req *req = tevent_req_callback_data(subreq,
+                                                      struct tevent_req);
+    int dp_error = DP_ERR_FATAL;
+
+    ret = sdap_id_op_connect_recv(subreq, &dp_error);
+    talloc_zfree(subreq);
+    if (ret) {
+        if (dp_error == DP_ERR_OFFLINE) {
+            DEBUG(SSSDBG_MINOR_FAILURE,
+                  "No IPA server is available, cannot get the "
+                  "TDO data while offline");
+        } else {
+            DEBUG(SSSDBG_OP_FAILURE,
+                  "Failed to connect to IPA server: [%d](%s)\n",
+                  ret, sss_strerror(ret));
+        }
+
+        tevent_req_error(req, ret);
+        return;
+    }
+
+    ret = tdo_get_mod_stamp_next_base(req);
+    if (ret == EOK) {
+        DEBUG(SSSDBG_TRACE_FUNC, "All bases iterated over, done\n");
+        tevent_req_done(req);
+    } else if (ret != EAGAIN) {
+        tevent_req_error(req, ret);
+    }
+
+    /* Will resume in callback */
+}
+
+static errno_t tdo_get_mod_stamp_next_base(struct tevent_req *req)
+{
+    struct tdo_get_mod_stamp_state *state =
+            tevent_req_data(req, struct tdo_get_mod_stamp_state);
+    struct sdap_search_base *base;
+    int timeout;
+    struct tevent_req *subreq;
+    const char *attrs[] = { MODIFY_TIMESTAMP, NULL};
+
+    base = state->bases[state->search_base_iter];
+    if (base == NULL) {
+        return EOK;
+    }
+
+    timeout = dp_opt_get_int(state->id_ctx->sdap_id_ctx->opts->basic,
+                             SDAP_SEARCH_TIMEOUT);
+
+    subreq = sdap_get_generic_send(state, state->ev,
+                                   state->id_ctx->sdap_id_ctx->opts,
+                                   sdap_id_op_handle(state->sdap_op),
+                                   base->basedn, base->scope,
+                                   state->filter,
+                                   attrs, NULL, 0,
+                                   timeout,
+                                   false);
+    if (subreq == NULL) {
+        DEBUG(SSSDBG_OP_FAILURE, "sdap_get_generic_send failed.\n");
+        return ENOMEM;
+    }
+
+    DEBUG(SSSDBG_TRACE_FUNC, "Looking up TDO..\n");
+    tevent_req_set_callback(subreq, tdo_get_mod_stamp_done, req);
+    return EAGAIN;
+}
+
+static void tdo_get_mod_stamp_done(struct tevent_req *subreq)
+{
+    errno_t ret;
+    struct tevent_req *req = tevent_req_callback_data(subreq,
+                                                      struct tevent_req);
+    struct tdo_get_mod_stamp_state *state =
+            tevent_req_data(req, struct tdo_get_mod_stamp_state);
+    size_t reply_count;
+    struct sysdb_attrs **reply;
+    const char *value;
+
+    ret = sdap_get_generic_recv(subreq, state, &reply_count, &reply);
+    talloc_free(subreq);
+    if (ret != EOK) {
+        tevent_req_error(req, ret);
+        return;
+    }
+
+    if (reply_count == 0) {
+        DEBUG(SSSDBG_TRACE_LIBS, "No TDO found, moving to next search base\n");
+        state->search_base_iter++;
+
+        ret = tdo_get_mod_stamp_next_base(req);
+        if (ret == EOK) {
+            /* TDO not found? */
+            tevent_req_error(req, ENOENT);
+        } else if (ret != EAGAIN) {
+            tevent_req_error(req, ret);
+        }
+        return;
+    } else if (reply_count > 1) {
+        DEBUG(SSSDBG_OP_FAILURE, "More than one TDO found!\n");
+        tevent_req_error(req, EIO);
+        return;
+    }
+
+    /* One TDO, extract timestamp */
+    ret = sysdb_attrs_get_string(reply[0], MODIFY_TIMESTAMP, &value);
+    if (ret != EOK) {
+        DEBUG(SSSDBG_OP_FAILURE, "sysdb_attrs_get_string failed.\n");
+        tevent_req_error(req, ret);
+        return;
+    }
+
+    ret = sss_utc_to_time_t(value, "%Y%m%d%H%M%SZ", &state->tdo_mod);
+    if (ret != EOK) {
+        DEBUG(SSSDBG_OP_FAILURE, "sysdb_attrs_get_string failed.\n");
+        tevent_req_error(req, ret);
+        return;
+    }
+
+    DEBUG(SSSDBG_TRACE_LIBS,
+          "TDO %s has timestamp %ld\n", state->tdo_name, state->tdo_mod);
+    tevent_req_done(req);
+}
+
+static int tdo_get_mod_stamp_recv(struct tevent_req *req, time_t *_tdo_mod)
+{
+    struct tdo_get_mod_stamp_state *state =
+            tevent_req_data(req, struct tdo_get_mod_stamp_state);
+
+    TEVENT_REQ_RETURN_ON_ERROR(req);
+
+    if (_tdo_mod) {
+        *_tdo_mod = state->tdo_mod;
+    }
+
+    return EOK;
+}
+
 struct ipa_getkeytab_state {
     int child_status;
     struct sss_child_ctx_old *child_ctx;
@@ -353,8 +564,6 @@ static struct tevent_req *ipa_getkeytab_send(TALLOC_CTX *mem_ctx,
                                              const char *server,
                                              const char *principal,
                                              const char *keytab)
-
-
 {
     errno_t ret;
     struct tevent_req *req = NULL;
@@ -569,6 +778,7 @@ struct ipa_server_trusted_dom_setup_state {
     struct ipa_id_ctx *id_ctx;
     struct sss_domain_info *subdom;
 
+    time_t newer_than;
     uint32_t direction;
     const char *forest;
     const char *keytab;
@@ -579,6 +789,8 @@ struct ipa_server_trusted_dom_setup_state {
 };
 
 static errno_t ipa_server_trusted_dom_setup_1way(struct tevent_req *req);
+static errno_t ipa_server_trust_1way_getkt(struct tevent_req *subreq);
+static void ipa_server_trust_1way_tstamp_done(struct tevent_req *subreq);
 static void ipa_server_trust_1way_kt_done(struct tevent_req *subreq);
 
 struct tevent_req *
@@ -586,7 +798,8 @@ ipa_server_trusted_dom_setup_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 sss_domain_info *subdom,
+                                  time_t newer_than)
 {
     struct tevent_req *req = NULL;
     struct ipa_server_trusted_dom_setup_state *state = NULL;
@@ -601,6 +814,7 @@ ipa_server_trusted_dom_setup_send(TALLOC_CTX *mem_ctx,
     state->be_ctx = be_ctx;
     state->id_ctx = id_ctx;
     state->subdom = subdom;
+    state->newer_than = newer_than;
 
     /* Trusts are only established with forest roots */
     if (subdom->forest_root == NULL) {
@@ -664,10 +878,9 @@ immediate:
 static errno_t ipa_server_trusted_dom_setup_1way(struct tevent_req *req)
 {
     errno_t ret;
-    struct tevent_req *subreq = NULL;
+    struct tevent_req *subreq;
     struct ipa_server_trusted_dom_setup_state *state =
             tevent_req_data(req, struct ipa_server_trusted_dom_setup_state);
-    const char *hostname;
 
     state->keytab = forest_keytab(state, state->forest);
     if (state->keytab == NULL) {
@@ -690,9 +903,6 @@ static errno_t ipa_server_trusted_dom_setup_1way(struct tevent_req *req)
     DEBUG(SSSDBG_TRACE_FUNC,
           "Will re-fetch 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_realm,
                                              state->subdom);
@@ -701,6 +911,58 @@ static errno_t ipa_server_trusted_dom_setup_1way(struct tevent_req *req)
         return EIO;
     }
 
+    if (state->newer_than > 0) {
+        DEBUG(SSSDBG_TRACE_FUNC,
+              "Check if there is a TDO newer than %ld\n", state->newer_than);
+        subreq = tdo_get_mod_stamp_send(state,
+                                        state->ev,
+                                        state->id_ctx,
+                                        state->subdom->name);
+        if (subreq == NULL) {
+            return ENOMEM;
+        }
+        tevent_req_set_callback(subreq,
+                                ipa_server_trust_1way_tstamp_done, req);
+        return EAGAIN;
+    }
+
+    return ipa_server_trust_1way_getkt(req);
+}
+
+static void ipa_server_trust_1way_tstamp_done(struct tevent_req *subreq)
+{
+    errno_t ret;
+    time_t tdo_mod;
+    struct tevent_req *req = tevent_req_callback_data(subreq,
+                                                      struct tevent_req);
+    struct ipa_server_trusted_dom_setup_state *state =
+            tevent_req_data(req, struct ipa_server_trusted_dom_setup_state);
+
+    ret = tdo_get_mod_stamp_recv(subreq, &tdo_mod);
+    if (tdo_mod < state->newer_than) {
+        DEBUG(SSSDBG_TRACE_FUNC, "Did not find a newer TDO\n");
+        tevent_req_done(req);
+        return;
+    }
+
+    DEBUG(SSSDBG_TRACE_FUNC, "TDO was recreated, fetching keytab\n");
+    ret = ipa_server_trust_1way_getkt(req);
+    if (ret != EOK && ret != EAGAIN) {
+        tevent_req_done(req);
+        return;
+    }
+}
+
+static errno_t ipa_server_trust_1way_getkt(struct tevent_req *req)
+{
+    struct tevent_req *subreq;
+    struct ipa_server_trusted_dom_setup_state *state =
+            tevent_req_data(req, struct ipa_server_trusted_dom_setup_state);
+    const char *hostname;
+
+    hostname = dp_opt_get_string(state->id_ctx->ipa_options->basic,
+                                 IPA_HOSTNAME);
+
     subreq = ipa_getkeytab_send(state->be_ctx, state->be_ctx->ev,
                                 state->ccache,
                                 hostname,
@@ -853,7 +1115,8 @@ static errno_t ipa_server_create_trusts_step(struct tevent_req *req)
                                                        state->ev,
                                                        state->be_ctx,
                                                        state->id_ctx,
-                                                       state->domiter);
+                                                       state->domiter,
+                                                       0);
             if (subreq == NULL) {
                 return ENOMEM;
             }
@@ -1112,3 +1375,12 @@ int ipa_ad_subdom_init(struct be_ctx *be_ctx,
 
     return EOK;
 }
+
+void ipa_subdom_reset_trust(struct ipa_server_mode_ctx *server_mode)
+{
+    struct ipa_ad_server_ctx *trust_iter;
+
+    DLIST_FOR_EACH(trust_iter, server_mode->trusts) {
+        trust_iter->last_kt_check = 0;
+    }
+}
-- 
2.4.3



More information about the sssd-devel mailing list