Blob Blame History Raw
From fe54de0824cac1822d6f9485165adc64bf4e0fa7 Mon Sep 17 00:00:00 2001
From: Sumit Bose <sbose@redhat.com>
Date: Tue, 24 Oct 2017 14:10:53 +0200
Subject: [PATCH 28/31] NSS: add support for SSS_NSS_EX_FLAG_INVALIDATE_CACHE

The patch adds support for the SSS_NSS_EX_FLAG_INVALIDATE_CACHE flag and
makes the existing code more flexible and handle additional flags.

If SSS_NSS_EX_FLAG_INVALIDATE_CACHE is set the requested object is only
looked up in the cache and if it was found on-disk and memory cache
entries will be invalidated.

Related to https://pagure.io/SSSD/sssd/issue/2478

Reviewed-by: Jakub Hrozek <jhrozek@redhat.com>
(cherry picked from commit 55f7d8034d783c01789d76a2b9ffc901045e8af8)
---
 src/responder/nss/nss_cmd.c            | 141 +++++++++++++++++++++++++++++++--
 src/responder/nss/nss_protocol.c       |   1 +
 src/responder/nss/nss_protocol.h       |   1 +
 src/responder/nss/nss_protocol_grent.c |   9 ++-
 src/responder/nss/nss_protocol_pwent.c |   6 +-
 src/sss_client/idmap/sss_nss_ex.c      |  20 ++++-
 src/sss_client/idmap/sss_nss_idmap.h   |   8 +-
 7 files changed, 171 insertions(+), 15 deletions(-)

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