From 0d73f92c1f1846da1ac7492edb2933df7c3259ed Mon Sep 17 00:00:00 2001
From: Yassir Elley <yelley@redhat.com>
Date: Sun, 7 Sep 2014 23:23:26 -0400
Subject: [PATCH] AD-GPO: delete stale GPOs

---
 src/db/sysdb.h            |  5 +++
 src/db/sysdb_gpo.c        | 93 +++++++++++++++++++++++++++++++++++++++++++++++
 src/providers/ad/ad_gpo.c | 36 ++++++++++++++++++
 3 files changed, 134 insertions(+)

diff --git a/src/db/sysdb.h b/src/db/sysdb.h
index 3cef1e66bdc5c3870ed1a349c797e66ac4ad3475..abd8ce3d8ec78b8abafd8324fe906822510bb16e 100644
--- a/src/db/sysdb.h
+++ b/src/db/sysdb.h
@@ -901,4 +901,9 @@ errno_t sysdb_gpo_get_gpos(TALLOC_CTX *mem_ctx,
                            struct sss_domain_info *domain,
                            struct ldb_result **_result);
 
+errno_t sysdb_gpo_delete_stale_gpos(TALLOC_CTX *mem_ctx,
+                                    struct sss_domain_info *domain,
+                                    const char **gpo_guid_list,
+                                    int num_gpos);
+
 #endif /* __SYS_DB_H__ */
diff --git a/src/db/sysdb_gpo.c b/src/db/sysdb_gpo.c
index 7dd31d81a69c065fb03840ba9d2b7810abad4cad..e0b9bb9f0a26ff8defcaf7813588281d11976bfc 100644
--- a/src/db/sysdb_gpo.c
+++ b/src/db/sysdb_gpo.c
@@ -352,3 +352,96 @@ done:
     talloc_free(tmp_ctx);
     return ret;
 }
+
+static inline bool
+sysdb_gpo_guid_in_list(const char **gpo_guid_list, int num_gpos, const char *gpo_guid)
+{
+    size_t i;
+
+    for (i = 0; i < num_gpos; i++) {
+        if (strcasecmp(gpo_guid_list[i], gpo_guid) == 0) {
+            break;
+        }
+    }
+
+    return (i < num_gpos) ? true : false;
+}
+
+errno_t
+sysdb_gpo_delete_stale_gpos(TALLOC_CTX *mem_ctx,
+                            struct sss_domain_info *domain,
+                            const char **gpo_guid_list,
+                            int num_gpos)
+{
+    struct ldb_result *res;
+    errno_t ret, sret;
+    int i;
+    bool in_transaction = false;
+    const char *cached_gpo_guid;
+    bool stale_gpo_found = false;
+
+    ret = sysdb_transaction_start(domain->sysdb);
+    if (ret != EOK) {
+        DEBUG(SSSDBG_CRIT_FAILURE, "Failed to start transaction\n");
+        goto done;
+    }
+
+    in_transaction = true;
+
+    ret = sysdb_gpo_get_gpos(mem_ctx, domain, &res);
+    if (ret != EOK && ret != ENOENT) {
+        goto done;
+    } else if (ret != ENOENT) {
+        for (i = 0; i < res->count; i++) {
+            cached_gpo_guid = ldb_msg_find_attr_as_string(res->msgs[i],
+                                                          SYSDB_GPO_GUID_ATTR,
+                                                          NULL);
+            if (cached_gpo_guid == NULL) {
+                DEBUG(SSSDBG_CRIT_FAILURE,
+                      "No gpo_guid attribute found in gpo cache entry\n");
+                ret = EFAULT;
+                goto done;
+            }
+
+            if (sysdb_gpo_guid_in_list(gpo_guid_list, num_gpos, cached_gpo_guid)) {
+                /* the cached_gpo_guid is still applicable, skip it */
+                continue;
+            } else {
+                stale_gpo_found = true;
+                /* the cached_gpo_guid is no longer applicable, delete it */
+                DEBUG(SSSDBG_TRACE_FUNC, "Deleting stale GPO [gpo_guid:%s]\n",
+                      cached_gpo_guid);
+
+                ret = sysdb_delete_entry(domain->sysdb, res->msgs[i]->dn, true);
+                if (ret != EOK) {
+                    DEBUG(SSSDBG_MINOR_FAILURE,
+                          "Could not delete GPO cache entry [gpo_guid:%s]\n",
+                          cached_gpo_guid);
+                    goto done;
+                }
+            }
+        }
+    }
+
+    if (!stale_gpo_found) {
+        DEBUG(SSSDBG_TRACE_FUNC, "No stale GPOs found\n");
+    }
+
+    ret = sysdb_transaction_commit(domain->sysdb);
+    if (ret != EOK) {
+        DEBUG(SSSDBG_CRIT_FAILURE,
+              "Could not commit transaction: [%s]\n", strerror(ret));
+        goto done;
+    }
+    in_transaction = false;
+
+done:
+    if (in_transaction) {
+        sret = sysdb_transaction_cancel(domain->sysdb);
+        if (sret != EOK) {
+            DEBUG(SSSDBG_CRIT_FAILURE, "Could not cancel transaction\n");
+        }
+    }
+    return ret;
+
+}
diff --git a/src/providers/ad/ad_gpo.c b/src/providers/ad/ad_gpo.c
index 19d419471afb66ca35a14e90555cfe26fdc922c8..aa1b3c547a0725a4890a10ee86b93699b79c361a 100644
--- a/src/providers/ad/ad_gpo.c
+++ b/src/providers/ad/ad_gpo.c
@@ -1730,6 +1730,7 @@ ad_gpo_process_gpo_done(struct tevent_req *subreq)
     struct gp_gpo **candidate_gpos = NULL;
     int num_candidate_gpos = 0;
     int i = 0;
+    const char **cse_filtered_gpo_guids;
 
     req = tevent_req_callback_data(subreq, struct tevent_req);
     state = tevent_req_data(req, struct ad_gpo_access_state);
@@ -1795,14 +1796,49 @@ ad_gpo_process_gpo_done(struct tevent_req *subreq)
         goto done;
     }
 
+    /* we create and populate an array of applicable gpo-guids */
+    cse_filtered_gpo_guids =
+        talloc_array(state, const char *, state->num_cse_filtered_gpos);
+    if (cse_filtered_gpo_guids == NULL) {
+        ret = ENOMEM;
+        goto done;
+    }
+
     for (i = 0; i < state->num_cse_filtered_gpos; i++) {
         DEBUG(SSSDBG_TRACE_FUNC, "cse_filtered_gpos[%d]->gpo_guid is %s\n", i,
                                   state->cse_filtered_gpos[i]->gpo_guid);
+        cse_filtered_gpo_guids[i] = talloc_steal(cse_filtered_gpo_guids,
+                                                 state->cse_filtered_gpos[i]->gpo_guid);
+        if (cse_filtered_gpo_guids[i] == NULL) {
+            ret = ENOMEM;
+            goto done;
+        }
     }
 
     DEBUG(SSSDBG_TRACE_FUNC, "num_cse_filtered_gpos: %d\n",
           state->num_cse_filtered_gpos);
 
+    /*
+     * we now have the array of applicable gpos in hand; however, since we
+     * may have cached a larger set of gpo-guids previously, we delete
+     * all stale gpo cache entries (i.e. entries that have a gpo-guid that
+     * doesn't match any of the gpo-guids in the cse_filtered_gpo_guids list)
+     */
+    ret = sysdb_gpo_delete_stale_gpos(state, state->domain,
+                                      cse_filtered_gpo_guids,
+                                      state->num_cse_filtered_gpos);
+    if (ret != EOK) {
+        switch (ret) {
+        case ENOENT:
+            DEBUG(SSSDBG_OP_FAILURE, "No GPOs available in cache\n");
+        default:
+            DEBUG(SSSDBG_FATAL_FAILURE,
+                  "Could not delete stale GPOs from cache: [%s]\n",
+                  strerror(ret));
+            goto done;
+        }
+    }
+
     ret = ad_gpo_cse_step(req);
 
  done:
-- 
1.8.5.3

