From 66742c306e81c013113edb3ea8f290ac32d33b07 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Pavel=20B=C5=99ezina?= <pbrezina@redhat.com>
Date: Tue, 10 Nov 2015 12:32:16 +0100
Subject: [PATCH 13/32] SUDO: obtain host information when going online

Resolves:
https://fedorahosted.org/sssd/ticket/2672
---
 src/providers/ldap/sdap_async_sudo.c | 75 ++++++++++++++++++++++++++++++----
 src/providers/ldap/sdap_sudo.c       | 78 ++++++++++++++----------------------
 src/providers/ldap/sdap_sudo.h       |  2 +
 3 files changed, 100 insertions(+), 55 deletions(-)

diff --git a/src/providers/ldap/sdap_async_sudo.c b/src/providers/ldap/sdap_async_sudo.c
index 84eff3b2a99e9f7c38e49c870cd301f4f88b4d02..627fc45ce69c91eb816b8a50244576efe6b4e0e3 100644
--- a/src/providers/ldap/sdap_async_sudo.c
+++ b/src/providers/ldap/sdap_async_sudo.c
@@ -486,6 +486,8 @@ struct sdap_sudo_refresh_state {
 
 static errno_t sdap_sudo_refresh_retry(struct tevent_req *req);
 static void sdap_sudo_refresh_connect_done(struct tevent_req *subreq);
+static void sdap_sudo_refresh_hostinfo_done(struct tevent_req *subreq);
+static errno_t sdap_sudo_refresh_sudoers(struct tevent_req *req);
 static void sdap_sudo_refresh_done(struct tevent_req *subreq);
 
 struct tevent_req *sdap_sudo_refresh_send(TALLOC_CTX *mem_ctx,
@@ -577,7 +579,6 @@ static void sdap_sudo_refresh_connect_done(struct tevent_req *subreq)
 {
     struct tevent_req *req;
     struct sdap_sudo_refresh_state *state;
-    char *filter;
     int dp_error;
     int ret;
 
@@ -597,15 +598,74 @@ static void sdap_sudo_refresh_connect_done(struct tevent_req *subreq)
 
     DEBUG(SSSDBG_TRACE_FUNC, "SUDO LDAP connection successful\n");
 
+    /* Renew host information if needed. */
+    if (state->sudo_ctx->run_hostinfo) {
+        subreq = sdap_sudo_get_hostinfo_send(state, state->opts,
+                                             state->sudo_ctx->id_ctx->be);
+        if (subreq == NULL) {
+            state->dp_error = DP_ERR_FATAL;
+            tevent_req_error(req, ENOMEM);
+            return;
+        }
+
+        tevent_req_set_callback(subreq, sdap_sudo_refresh_hostinfo_done, req);
+        state->sudo_ctx->run_hostinfo = false;
+        return;
+    }
+
+    ret = sdap_sudo_refresh_sudoers(req);
+    if (ret != EAGAIN) {
+        state->dp_error = DP_ERR_FATAL;
+        tevent_req_error(req, ret);
+    }
+}
+
+static void sdap_sudo_refresh_hostinfo_done(struct tevent_req *subreq)
+{
+    struct sdap_sudo_ctx *sudo_ctx;
+    struct sdap_sudo_refresh_state *state;
+    struct tevent_req *req;
+    errno_t ret;
+
+    req = tevent_req_callback_data(subreq, struct tevent_req);
+    state = tevent_req_data(req, struct sdap_sudo_refresh_state);
+
+    sudo_ctx = state->sudo_ctx;
+
+    ret = sdap_sudo_get_hostinfo_recv(sudo_ctx, subreq, &sudo_ctx->hostnames,
+                                      &sudo_ctx->ip_addr);
+    talloc_zfree(subreq);
+    if (ret != EOK) {
+        DEBUG(SSSDBG_OP_FAILURE, "Unable to retrieve host information, "
+                                 "host filter will be disabled [%d]: %s\n",
+                                 ret, sss_strerror(ret));
+        sudo_ctx->use_host_filter = false;
+    } else {
+        sudo_ctx->use_host_filter = true;
+    }
+
+    ret = sdap_sudo_refresh_sudoers(req);
+    if (ret != EAGAIN) {
+        state->dp_error = DP_ERR_FATAL;
+        tevent_req_error(req, ret);
+    }
+}
+
+static errno_t sdap_sudo_refresh_sudoers(struct tevent_req *req)
+{
+    struct sdap_sudo_refresh_state *state;
+    struct tevent_req *subreq;
+    char *filter;
+
+    state = tevent_req_data(req, struct sdap_sudo_refresh_state);
+
     /* We are connected. Host information may have changed during transition
      * from offline to online state. At this point we can combine search
      * and host filter. */
     filter = sdap_sudo_get_filter(state, state->opts->sudorule_map,
                                   state->sudo_ctx, state->search_filter);
     if (filter == NULL) {
-        state->dp_error = DP_ERR_FATAL;
-        tevent_req_error(req, ENOMEM);
-        return;
+        return ENOMEM;
     }
 
     subreq = sdap_sudo_load_sudoers_send(state, state->ev,
@@ -613,14 +673,13 @@ static void sdap_sudo_refresh_connect_done(struct tevent_req *subreq)
                                          sdap_id_op_handle(state->sdap_op),
                                          filter);
     if (subreq == NULL) {
-        state->dp_error = DP_ERR_FATAL;
-        tevent_req_error(req, ENOMEM);
-        return;
+        talloc_free(filter);
+        return ENOMEM;
     }
 
     tevent_req_set_callback(subreq, sdap_sudo_refresh_done, req);
 
-    return;
+    return EAGAIN;
 }
 
 static void sdap_sudo_refresh_done(struct tevent_req *subreq)
diff --git a/src/providers/ldap/sdap_sudo.c b/src/providers/ldap/sdap_sudo.c
index ed4ecc6748ff499a41bc92cc094cf2cd2dc9a474..a212bdd5e87042cca1484717c6fe6353fa040bfa 100644
--- a/src/providers/ldap/sdap_sudo.c
+++ b/src/providers/ldap/sdap_sudo.c
@@ -41,7 +41,20 @@ struct bet_ops sdap_sudo_ops = {
     .finalize = sdap_sudo_shutdown
 };
 
-static void sdap_sudo_get_hostinfo_done(struct tevent_req *req);
+static void sdap_sudo_online_cb(void *pvt)
+{
+    struct sdap_sudo_ctx *sudo_ctx;
+
+    sudo_ctx = talloc_get_type(pvt, struct sdap_sudo_ctx);
+    if (sudo_ctx == NULL) {
+        DEBUG(SSSDBG_FATAL_FAILURE, "BUG: sudo_ctx is NULL\n");
+        return;
+    }
+
+    DEBUG(SSSDBG_TRACE_FUNC, "We are back online. SUDO host information will "
+                             "be renewed on next refresh.\n");
+    sudo_ctx->run_hostinfo = true;
+}
 
 int sdap_sudo_init(struct be_ctx *be_ctx,
                    struct sdap_id_ctx *id_ctx,
@@ -49,7 +62,6 @@ int sdap_sudo_init(struct be_ctx *be_ctx,
                    void **pvt_data)
 {
     struct sdap_sudo_ctx *sudo_ctx = NULL;
-    struct tevent_req *req = NULL;
     int ret;
 
     DEBUG(SSSDBG_TRACE_INTERNAL, "Initializing sudo LDAP back end\n");
@@ -79,23 +91,25 @@ int sdap_sudo_init(struct be_ctx *be_ctx,
         goto done;
     }
 
-    req = sdap_sudo_get_hostinfo_send(sudo_ctx, id_ctx->opts, be_ctx);
-    if (req == NULL) {
-        DEBUG(SSSDBG_CRIT_FAILURE, "Unable to retrieve host information - "
-              "(host filter will be disabled)\n");
-
-        sudo_ctx->use_host_filter = false;
-
-        ret = sdap_sudo_ptask_setup(sudo_ctx->id_ctx->be, sudo_ctx);
+    if (sudo_ctx->use_host_filter) {
+        ret = be_add_online_cb(sudo_ctx, sudo_ctx->id_ctx->be,
+                               sdap_sudo_online_cb, sudo_ctx, NULL);
         if (ret != EOK) {
-             DEBUG(SSSDBG_OP_FAILURE,
-                   "Unable to setup periodical refresh"
-                    "of sudo rules [%d]: %s\n", ret, strerror(ret));
-             /* periodical updates will not work, but specific-rule update
-              * is no affected by this, therefore we don't have to fail here */
+            DEBUG(SSSDBG_OP_FAILURE, "Unable to install online callback "
+                                     "[%d]: %s\n", ret, sss_strerror(ret));
+            goto done;
         }
-    } else {
-        tevent_req_set_callback(req, sdap_sudo_get_hostinfo_done, sudo_ctx);
+
+        /* Obtain hostinfo with the first refresh. */
+        sudo_ctx->run_hostinfo = true;
+    }
+
+    ret = sdap_sudo_ptask_setup(sudo_ctx->id_ctx->be, sudo_ctx);
+    if (ret != EOK) {
+         DEBUG(SSSDBG_OP_FAILURE, "Unable to setup periodical refresh "
+               "of sudo rules [%d]: %s\n", ret, strerror(ret));
+         /* periodical updates will not work, but specific-rule update
+          * is no affected by this, therefore we don't have to fail here */
     }
 
     ret = EOK;
@@ -108,36 +122,6 @@ done:
     return ret;
 }
 
-static void sdap_sudo_get_hostinfo_done(struct tevent_req *req)
-{
-    struct sdap_sudo_ctx *sudo_ctx = NULL;
-    char **hostnames = NULL;
-    char **ip_addr = NULL;
-    int ret;
-
-    sudo_ctx = tevent_req_callback_data(req, struct sdap_sudo_ctx);
-
-    ret = sdap_sudo_get_hostinfo_recv(sudo_ctx, req, &hostnames, &ip_addr);
-    talloc_zfree(req);
-    if (ret != EOK) {
-        DEBUG(SSSDBG_CRIT_FAILURE, "Unable to retrieve host information - "
-              "(host filter will be disabled) [%d]: %s\n", ret, strerror(ret));
-        sudo_ctx->use_host_filter = false;
-    }
-
-    talloc_zfree(sudo_ctx->hostnames);
-    talloc_zfree(sudo_ctx->ip_addr);
-
-    sudo_ctx->hostnames = talloc_move(sudo_ctx, &hostnames);
-    sudo_ctx->ip_addr = talloc_move(sudo_ctx, &ip_addr);
-
-    ret = sdap_sudo_ptask_setup(sudo_ctx->id_ctx->be, sudo_ctx);
-    if (ret != EOK) {
-        DEBUG(SSSDBG_OP_FAILURE, "Unable to setup periodical refresh"
-                                  "of sudo rules [%d]: %s\n", ret, strerror(ret));
-    }
-}
-
 static void sdap_sudo_reply(struct tevent_req *req)
 {
     struct be_req *be_req = NULL;
diff --git a/src/providers/ldap/sdap_sudo.h b/src/providers/ldap/sdap_sudo.h
index c3222b211c9de705fd69fb23446210df707ca594..6b4d249a810e48b0eb1c049798ea086c5cad60f0 100644
--- a/src/providers/ldap/sdap_sudo.h
+++ b/src/providers/ldap/sdap_sudo.h
@@ -35,6 +35,8 @@ struct sdap_sudo_ctx {
 
     bool full_refresh_done;
     bool full_refresh_in_progress;
+
+    bool run_hostinfo;
 };
 
 /* Common functions from ldap_sudo.c */
-- 
2.1.0

