Blame SOURCES/0028-NSS-add-support-for-SSS_NSS_EX_FLAG_INVALIDATE_CACHE.patch

ced1f5
From fe54de0824cac1822d6f9485165adc64bf4e0fa7 Mon Sep 17 00:00:00 2001
ced1f5
From: Sumit Bose <sbose@redhat.com>
ced1f5
Date: Tue, 24 Oct 2017 14:10:53 +0200
ced1f5
Subject: [PATCH 28/31] NSS: add support for SSS_NSS_EX_FLAG_INVALIDATE_CACHE
ced1f5
ced1f5
The patch adds support for the SSS_NSS_EX_FLAG_INVALIDATE_CACHE flag and
ced1f5
makes the existing code more flexible and handle additional flags.
ced1f5
ced1f5
If SSS_NSS_EX_FLAG_INVALIDATE_CACHE is set the requested object is only
ced1f5
looked up in the cache and if it was found on-disk and memory cache
ced1f5
entries will be invalidated.
ced1f5
ced1f5
Related to https://pagure.io/SSSD/sssd/issue/2478
ced1f5
ced1f5
Reviewed-by: Jakub Hrozek <jhrozek@redhat.com>
ced1f5
(cherry picked from commit 55f7d8034d783c01789d76a2b9ffc901045e8af8)
ced1f5
---
ced1f5
 src/responder/nss/nss_cmd.c            | 141 +++++++++++++++++++++++++++++++--
ced1f5
 src/responder/nss/nss_protocol.c       |   1 +
ced1f5
 src/responder/nss/nss_protocol.h       |   1 +
ced1f5
 src/responder/nss/nss_protocol_grent.c |   9 ++-
ced1f5
 src/responder/nss/nss_protocol_pwent.c |   6 +-
ced1f5
 src/sss_client/idmap/sss_nss_ex.c      |  20 ++++-
ced1f5
 src/sss_client/idmap/sss_nss_idmap.h   |   8 +-
ced1f5
 7 files changed, 171 insertions(+), 15 deletions(-)
ced1f5
ced1f5
diff --git a/src/responder/nss/nss_cmd.c b/src/responder/nss/nss_cmd.c
ced1f5
index c5ddd2f2cc2122cd169ea991b94a14eb5bad095f..545257a0be7e91e9de767a57848bb77c5791db4e 100644
ced1f5
--- a/src/responder/nss/nss_cmd.c
ced1f5
+++ b/src/responder/nss/nss_cmd.c
ced1f5
@@ -50,6 +50,26 @@ nss_cmd_ctx_create(TALLOC_CTX *mem_ctx,
ced1f5
     return cmd_ctx;
ced1f5
 }
ced1f5
 
ced1f5
+static errno_t eval_flags(struct nss_cmd_ctx *cmd_ctx,
ced1f5
+                          struct cache_req_data *data)
ced1f5
+{
ced1f5
+    if ((cmd_ctx->flags & SSS_NSS_EX_FLAG_NO_CACHE) != 0
ced1f5
+            && (cmd_ctx->flags & SSS_NSS_EX_FLAG_INVALIDATE_CACHE) != 0) {
ced1f5
+        DEBUG(SSSDBG_CRIT_FAILURE, "Flags SSS_NSS_EX_FLAG_NO_CACHE and "
ced1f5
+                                   "SSS_NSS_EX_FLAG_INVALIDATE_CACHE are "
ced1f5
+                                   "mutually exclusive.\n");
ced1f5
+        return EINVAL;
ced1f5
+    }
ced1f5
+
ced1f5
+    if ((cmd_ctx->flags & SSS_NSS_EX_FLAG_NO_CACHE) != 0) {
ced1f5
+        cache_req_data_set_bypass_cache(data, true);
ced1f5
+    } else if ((cmd_ctx->flags & SSS_NSS_EX_FLAG_INVALIDATE_CACHE) != 0) {
ced1f5
+        cache_req_data_set_bypass_dp(data, true);
ced1f5
+    }
ced1f5
+
ced1f5
+    return EOK;
ced1f5
+}
ced1f5
+
ced1f5
 static void nss_getby_done(struct tevent_req *subreq);
ced1f5
 static void nss_getlistby_done(struct tevent_req *subreq);
ced1f5
 
ced1f5
@@ -65,7 +85,6 @@ static errno_t nss_getby_name(struct cli_ctx *cli_ctx,
ced1f5
     struct tevent_req *subreq;
ced1f5
     const char *rawname;
ced1f5
     errno_t ret;
ced1f5
-    uint32_t flags = 0;
ced1f5
 
ced1f5
     cmd_ctx = nss_cmd_ctx_create(cli_ctx, cli_ctx, type, fill_fn);
ced1f5
     if (cmd_ctx == NULL) {
ced1f5
@@ -73,8 +92,9 @@ static errno_t nss_getby_name(struct cli_ctx *cli_ctx,
ced1f5
         goto done;
ced1f5
     }
ced1f5
 
ced1f5
+    cmd_ctx->flags = 0;
ced1f5
     if (ex_version) {
ced1f5
-        ret = nss_protocol_parse_name_ex(cli_ctx, &rawname, &flags);
ced1f5
+        ret = nss_protocol_parse_name_ex(cli_ctx, &rawname, &cmd_ctx->flags);
ced1f5
     } else {
ced1f5
         ret = nss_protocol_parse_name(cli_ctx, &rawname);
ced1f5
     }
ced1f5
@@ -92,8 +112,10 @@ static errno_t nss_getby_name(struct cli_ctx *cli_ctx,
ced1f5
         goto done;
ced1f5
     }
ced1f5
 
ced1f5
-    if ((flags & SSS_NSS_EX_FLAG_NO_CACHE) != 0) {
ced1f5
-        cache_req_data_set_bypass_cache(data, true);
ced1f5
+    ret = eval_flags(cmd_ctx, data);
ced1f5
+    if (ret != EOK) {
ced1f5
+        DEBUG(SSSDBG_OP_FAILURE, "eval_flags failed.\n");
ced1f5
+        goto done;
ced1f5
     }
ced1f5
 
ced1f5
     subreq = nss_get_object_send(cmd_ctx, cli_ctx->ev, cli_ctx,
ced1f5
@@ -129,7 +151,6 @@ static errno_t nss_getby_id(struct cli_ctx *cli_ctx,
ced1f5
     struct tevent_req *subreq;
ced1f5
     uint32_t id;
ced1f5
     errno_t ret;
ced1f5
-    uint32_t flags = 0;
ced1f5
 
ced1f5
     cmd_ctx = nss_cmd_ctx_create(cli_ctx, cli_ctx, type, fill_fn);
ced1f5
     if (cmd_ctx == NULL) {
ced1f5
@@ -138,7 +159,7 @@ static errno_t nss_getby_id(struct cli_ctx *cli_ctx,
ced1f5
     }
ced1f5
 
ced1f5
     if (ex_version) {
ced1f5
-        ret = nss_protocol_parse_id_ex(cli_ctx, &id, &flags);
ced1f5
+        ret = nss_protocol_parse_id_ex(cli_ctx, &id, &cmd_ctx->flags);
ced1f5
     } else {
ced1f5
         ret = nss_protocol_parse_id(cli_ctx, &id;;
ced1f5
     }
ced1f5
@@ -156,8 +177,10 @@ static errno_t nss_getby_id(struct cli_ctx *cli_ctx,
ced1f5
         goto done;
ced1f5
     }
ced1f5
 
ced1f5
-    if ((flags & SSS_NSS_EX_FLAG_NO_CACHE) != 0) {
ced1f5
-        cache_req_data_set_bypass_cache(data, true);
ced1f5
+    ret = eval_flags(cmd_ctx, data);
ced1f5
+    if (ret != EOK) {
ced1f5
+        DEBUG(SSSDBG_OP_FAILURE, "eval_flags failed.\n");
ced1f5
+        goto done;
ced1f5
     }
ced1f5
 
ced1f5
     subreq = nss_get_object_send(cmd_ctx, cli_ctx->ev, cli_ctx,
ced1f5
@@ -425,6 +448,98 @@ done:
ced1f5
     return EOK;
ced1f5
 }
ced1f5
 
ced1f5
+static errno_t invalidate_cache(struct nss_cmd_ctx *cmd_ctx,
ced1f5
+                                struct cache_req_result *result)
ced1f5
+{
ced1f5
+    int ret;
ced1f5
+    enum sss_mc_type memcache_type;
ced1f5
+    const char *name;
ced1f5
+    char *output_name = NULL;
ced1f5
+    bool is_user;
ced1f5
+    struct sysdb_attrs *attrs = NULL;
ced1f5
+
ced1f5
+    switch (cmd_ctx->type) {
ced1f5
+    case CACHE_REQ_INITGROUPS:
ced1f5
+    case CACHE_REQ_INITGROUPS_BY_UPN:
ced1f5
+        memcache_type = SSS_MC_INITGROUPS;
ced1f5
+        is_user = true;
ced1f5
+        break;
ced1f5
+    case CACHE_REQ_USER_BY_NAME:
ced1f5
+    case CACHE_REQ_USER_BY_ID:
ced1f5
+        memcache_type = SSS_MC_PASSWD;
ced1f5
+        is_user = true;
ced1f5
+        break;
ced1f5
+    case CACHE_REQ_GROUP_BY_NAME:
ced1f5
+    case CACHE_REQ_GROUP_BY_ID:
ced1f5
+        memcache_type = SSS_MC_GROUP;
ced1f5
+        is_user = false;
ced1f5
+        break;
ced1f5
+    default:
ced1f5
+        /* nothing to do */
ced1f5
+        return EOK;
ced1f5
+    }
ced1f5
+
ced1f5
+    /* Find output name to invalidate memory cache entry*/
ced1f5
+    name = sss_get_name_from_msg(result->domain, result->msgs[0]);
ced1f5
+    if (name == NULL) {
ced1f5
+        DEBUG(SSSDBG_CRIT_FAILURE, "Found object has no name.\n");
ced1f5
+        return EINVAL;
ced1f5
+    }
ced1f5
+    ret = sss_output_fqname(cmd_ctx, result->domain, name,
ced1f5
+                            cmd_ctx->nss_ctx->rctx->override_space,
ced1f5
+                            &output_name);
ced1f5
+    if (ret != EOK) {
ced1f5
+        DEBUG(SSSDBG_OP_FAILURE, "sss_output_fqname failed.\n");
ced1f5
+        return ret;
ced1f5
+    }
ced1f5
+
ced1f5
+    memcache_delete_entry(cmd_ctx->nss_ctx, cmd_ctx->nss_ctx->rctx, NULL,
ced1f5
+                          output_name, 0, memcache_type);
ced1f5
+    if (memcache_type == SSS_MC_INITGROUPS) {
ced1f5
+        /* Invalidate the passwd data as well */
ced1f5
+        memcache_delete_entry(cmd_ctx->nss_ctx, cmd_ctx->nss_ctx->rctx,
ced1f5
+                              result->domain, output_name, 0, SSS_MC_PASSWD);
ced1f5
+    }
ced1f5
+    talloc_free(output_name);
ced1f5
+
ced1f5
+    /* Use sysdb name to invalidate disk cache entry */
ced1f5
+    name = ldb_msg_find_attr_as_string(result->msgs[0], SYSDB_NAME, NULL);
ced1f5
+    if (name == NULL) {
ced1f5
+        DEBUG(SSSDBG_CRIT_FAILURE, "Found object has no name.\n");
ced1f5
+        return EINVAL;
ced1f5
+    }
ced1f5
+
ced1f5
+    if (memcache_type == SSS_MC_INITGROUPS) {
ced1f5
+        attrs = sysdb_new_attrs(cmd_ctx);
ced1f5
+        if (attrs == NULL) {
ced1f5
+            DEBUG(SSSDBG_OP_FAILURE, "sysdb_new_attrs failed.\n");
ced1f5
+            return ENOMEM;
ced1f5
+        }
ced1f5
+
ced1f5
+        ret = sysdb_attrs_add_time_t(attrs, SYSDB_INITGR_EXPIRE, 1);
ced1f5
+        if (ret != EOK) {
ced1f5
+            talloc_free(attrs);
ced1f5
+            DEBUG(SSSDBG_OP_FAILURE, "sysdb_attrs_add_time_t failed.\n");
ced1f5
+            return ret;
ced1f5
+        }
ced1f5
+
ced1f5
+        ret = sysdb_set_user_attr(result->domain, name, attrs, SYSDB_MOD_REP);
ced1f5
+        talloc_free(attrs);
ced1f5
+        if (ret != EOK) {
ced1f5
+            DEBUG(SSSDBG_OP_FAILURE, "sysdb_set_user_attr failed.\n");
ced1f5
+            return ret;
ced1f5
+        }
ced1f5
+    }
ced1f5
+
ced1f5
+    ret = sysdb_invalidate_cache_entry(result->domain, name, is_user);
ced1f5
+    if (ret != EOK) {
ced1f5
+        DEBUG(SSSDBG_OP_FAILURE, "sysdb_invalidate_cache_entry failed.\n");
ced1f5
+        return ret;
ced1f5
+    }
ced1f5
+
ced1f5
+    return EOK;
ced1f5
+}
ced1f5
+
ced1f5
 static void nss_getby_done(struct tevent_req *subreq)
ced1f5
 {
ced1f5
     struct cache_req_result *result;
ced1f5
@@ -440,6 +555,16 @@ static void nss_getby_done(struct tevent_req *subreq)
ced1f5
         goto done;
ced1f5
     }
ced1f5
 
ced1f5
+    if ((cmd_ctx->flags & SSS_NSS_EX_FLAG_INVALIDATE_CACHE) != 0) {
ced1f5
+        ret = invalidate_cache(cmd_ctx, result);
ced1f5
+        if (ret != EOK) {
ced1f5
+            DEBUG(SSSDBG_OP_FAILURE, "Failed to invalidate cache for [%s].\n",
ced1f5
+                                     cmd_ctx->rawname);
ced1f5
+            nss_protocol_done(cmd_ctx->cli_ctx, ret);
ced1f5
+            goto done;
ced1f5
+        }
ced1f5
+    }
ced1f5
+
ced1f5
     nss_protocol_reply(cmd_ctx->cli_ctx, cmd_ctx->nss_ctx, cmd_ctx,
ced1f5
                        result, cmd_ctx->fill_fn);
ced1f5
 
ced1f5
diff --git a/src/responder/nss/nss_protocol.c b/src/responder/nss/nss_protocol.c
ced1f5
index 17bfc4f4e71960a72e9e04622eac95b94a865ec7..2655386498754c46fbb363bdd1f976f9ded6a434 100644
ced1f5
--- a/src/responder/nss/nss_protocol.c
ced1f5
+++ b/src/responder/nss/nss_protocol.c
ced1f5
@@ -233,6 +233,7 @@ nss_protocol_parse_id_ex(struct cli_ctx *cli_ctx, uint32_t *_id,
ced1f5
     SAFEALIGN_COPY_UINT32(&flags, body + sizeof(uint32_t), NULL);
ced1f5
 
ced1f5
     *_id = id;
ced1f5
+    *_flags = flags;
ced1f5
 
ced1f5
     return EOK;
ced1f5
 }
ced1f5
diff --git a/src/responder/nss/nss_protocol.h b/src/responder/nss/nss_protocol.h
ced1f5
index ca5b040237dc18acdca9a7a3a7a7dbb64265aa95..76724d2b2db7b11c9147fa927e39abab731328b2 100644
ced1f5
--- a/src/responder/nss/nss_protocol.h
ced1f5
+++ b/src/responder/nss/nss_protocol.h
ced1f5
@@ -50,6 +50,7 @@ struct nss_cmd_ctx {
ced1f5
     struct nss_ctx *nss_ctx;
ced1f5
     struct nss_state_ctx *state_ctx;
ced1f5
     nss_protocol_fill_packet_fn fill_fn;
ced1f5
+    uint32_t flags;
ced1f5
 
ced1f5
     /* For initgroups- */
ced1f5
     const char *rawname;
ced1f5
diff --git a/src/responder/nss/nss_protocol_grent.c b/src/responder/nss/nss_protocol_grent.c
ced1f5
index ee228c722a153a1ba7aa8a1b30a1e551108424bb..6f6ae57dd97b000ad3cf174b0f649d46981563e2 100644
ced1f5
--- a/src/responder/nss/nss_protocol_grent.c
ced1f5
+++ b/src/responder/nss/nss_protocol_grent.c
ced1f5
@@ -274,8 +274,10 @@ nss_protocol_fill_grent(struct nss_ctx *nss_ctx,
ced1f5
 
ced1f5
         num_results++;
ced1f5
 
ced1f5
-        /* Do not store entry in memory cache during enumeration. */
ced1f5
-        if (!cmd_ctx->enumeration) {
ced1f5
+        /* Do not store entry in memory cache during enumeration or when
ced1f5
+         * requested. */
ced1f5
+        if (!cmd_ctx->enumeration
ced1f5
+                && (cmd_ctx->flags & SSS_NSS_EX_FLAG_INVALIDATE_CACHE) == 0) {
ced1f5
             members = (char *)&body[rp_members];
ced1f5
             members_size = body_len - rp_members;
ced1f5
             ret = sss_mmap_cache_gr_store(&nss_ctx->grp_mc_ctx, name, &pwfield,
ced1f5
@@ -390,7 +392,8 @@ nss_protocol_fill_initgr(struct nss_ctx *nss_ctx,
ced1f5
         num_results++;
ced1f5
     }
ced1f5
 
ced1f5
-    if (nss_ctx->initgr_mc_ctx) {
ced1f5
+    if (nss_ctx->initgr_mc_ctx
ced1f5
+                && (cmd_ctx->flags & SSS_NSS_EX_FLAG_INVALIDATE_CACHE) == 0) {
ced1f5
         to_sized_string(&rawname, cmd_ctx->rawname);
ced1f5
         to_sized_string(&unique_name, result->lookup_name);
ced1f5
 
ced1f5
diff --git a/src/responder/nss/nss_protocol_pwent.c b/src/responder/nss/nss_protocol_pwent.c
ced1f5
index db5c071e2ff172a2267c08c9817fecfbcc7cabc3..f449ec69b6a86a6db2aaed368e217c1a791faaa2 100644
ced1f5
--- a/src/responder/nss/nss_protocol_pwent.c
ced1f5
+++ b/src/responder/nss/nss_protocol_pwent.c
ced1f5
@@ -295,8 +295,10 @@ nss_protocol_fill_pwent(struct nss_ctx *nss_ctx,
ced1f5
 
ced1f5
         num_results++;
ced1f5
 
ced1f5
-        /* Do not store entry in memory cache during enumeration. */
ced1f5
-        if (!cmd_ctx->enumeration) {
ced1f5
+        /* Do not store entry in memory cache during enumeration or when
ced1f5
+         * requested. */
ced1f5
+        if (!cmd_ctx->enumeration
ced1f5
+                && (cmd_ctx->flags & SSS_NSS_EX_FLAG_INVALIDATE_CACHE) == 0) {
ced1f5
             ret = sss_mmap_cache_pw_store(&nss_ctx->pwd_mc_ctx, name, &pwfield,
ced1f5
                                           uid, gid, &gecos, &homedir, &shell);
ced1f5
             if (ret != EOK) {
ced1f5
diff --git a/src/sss_client/idmap/sss_nss_ex.c b/src/sss_client/idmap/sss_nss_ex.c
ced1f5
index edb3ea652ef7032b76c8f815b9f83fe185a669ea..148eb7b35ec236b6272dd203a0035399cfdef73d 100644
ced1f5
--- a/src/sss_client/idmap/sss_nss_ex.c
ced1f5
+++ b/src/sss_client/idmap/sss_nss_ex.c
ced1f5
@@ -103,6 +103,18 @@ errno_t sss_nss_mc_get(struct nss_input *inp)
ced1f5
     }
ced1f5
 }
ced1f5
 
ced1f5
+static int check_flags(uint32_t flags)
ced1f5
+{
ced1f5
+    /* SSS_NSS_EX_FLAG_NO_CACHE and SSS_NSS_EX_FLAG_INVALIDATE_CACHE are
ced1f5
+     * mutually exclusive */
ced1f5
+    if ((flags & SSS_NSS_EX_FLAG_NO_CACHE) != 0
ced1f5
+            && (flags & SSS_NSS_EX_FLAG_INVALIDATE_CACHE) != 0) {
ced1f5
+        return EINVAL;
ced1f5
+    }
ced1f5
+
ced1f5
+    return 0;
ced1f5
+}
ced1f5
+
ced1f5
 int sss_get_ex(struct nss_input *inp, uint32_t flags, unsigned int timeout)
ced1f5
 {
ced1f5
     uint8_t *repbuf = NULL;
ced1f5
@@ -117,7 +129,13 @@ int sss_get_ex(struct nss_input *inp, uint32_t flags, unsigned int timeout)
ced1f5
     size_t idx;
ced1f5
     bool skip_mc = false;
ced1f5
 
ced1f5
-    if ((flags & SSS_NSS_EX_FLAG_NO_CACHE) != 0) {
ced1f5
+    ret = check_flags(flags);
ced1f5
+    if (ret != 0) {
ced1f5
+        return ret;
ced1f5
+    }
ced1f5
+
ced1f5
+    if ((flags & SSS_NSS_EX_FLAG_NO_CACHE) != 0
ced1f5
+            || (flags & SSS_NSS_EX_FLAG_INVALIDATE_CACHE) != 0) {
ced1f5
         skip_mc = true;
ced1f5
     }
ced1f5
 
ced1f5
diff --git a/src/sss_client/idmap/sss_nss_idmap.h b/src/sss_client/idmap/sss_nss_idmap.h
ced1f5
index 1649830afbb80c617fd339f054aef8bc8e585fb9..3755643312f05a31d1cf1aa76dfc22848ef1e3ec 100644
ced1f5
--- a/src/sss_client/idmap/sss_nss_idmap.h
ced1f5
+++ b/src/sss_client/idmap/sss_nss_idmap.h
ced1f5
@@ -170,9 +170,15 @@ void sss_nss_free_kv(struct sss_nss_kv *kv_list);
ced1f5
 #define SSS_NSS_EX_FLAG_NO_FLAGS 0
ced1f5
 
ced1f5
 /** Always request data from the server side, client must be privileged to do
ced1f5
- *  so, see nss_trusted_users option in man sssd.conf for details */
ced1f5
+ *  so, see nss_trusted_users option in man sssd.conf for details.
ced1f5
+ *  This flag cannot be used together with SSS_NSS_EX_FLAG_INVALIDATE_CACHE */
ced1f5
 #define SSS_NSS_EX_FLAG_NO_CACHE (1 << 0)
ced1f5
 
ced1f5
+/** Invalidate the data in the caches, client must be privileged to do
ced1f5
+ *  so, see nss_trusted_users option in man sssd.conf for details.
ced1f5
+ *  This flag cannot be used together with SSS_NSS_EX_FLAG_NO_CACHE */
ced1f5
+#define SSS_NSS_EX_FLAG_INVALIDATE_CACHE (1 << 1)
ced1f5
+
ced1f5
 #ifdef IPA_389DS_PLUGIN_HELPER_CALLS
ced1f5
 
ced1f5
 /**
ced1f5
-- 
ced1f5
2.13.6
ced1f5