Blame SOURCES/0056-NSS-Avoid-changing-the-memory-cache-ownership-away-f.patch

b6526f
From 118c44f90c9c901ffbf1b676be57b7a83a190399 Mon Sep 17 00:00:00 2001
b6526f
From: Jakub Hrozek <jhrozek@redhat.com>
b6526f
Date: Fri, 30 Nov 2018 13:06:13 +0100
b6526f
Subject: [PATCH] NSS: Avoid changing the memory cache ownership away from the
b6526f
 sssd user
b6526f
MIME-Version: 1.0
b6526f
Content-Type: text/plain; charset=UTF-8
b6526f
Content-Transfer-Encoding: 8bit
b6526f
b6526f
Resolves:
b6526f
https://pagure.io/SSSD/sssd/issue/3890
b6526f
b6526f
In case SSSD is compiled --with-sssd-user but run as root (which is the
b6526f
default on RHEL and derivatives), then the memory cache will be owned by
b6526f
the user that sssd_nss runs as, so root.
b6526f
b6526f
This conflicts with the packaging which specifies sssd.sssd as the owner. And
b6526f
in turn, this means that users can't reliably assess the package integrity
b6526f
using rpm -V.
b6526f
b6526f
This patch makes sure that the memory cache files are chowned to sssd.sssd
b6526f
even if the nss responder runs as root.
b6526f
b6526f
Also, this patch changes the sssd_nss responder so that is becomes a member
b6526f
of the supplementary sssd group. Even though in traditional UNIX sense,
b6526f
a process running as root could write to a file owned by sssd:sssd, with
b6526f
SELinux enforcing mode this becomes problematic as SELinux emits an error
b6526f
such as:
b6526f
b6526f
type=AVC msg=audit(1543524888.125:1495): avc:  denied  { fsetid } for
b6526f
pid=7706 comm="sssd_nss" capability=4  scontext=system_u:system_r:sssd_t:s0
b6526f
tcontext=system_u:system_r:sssd_t:s0 tclass=capability
b6526f
b6526f
To make it possible for the sssd_nss process to write to the files, the
b6526f
files are also made group-writable. The 'others' permission is still set
b6526f
to read only.
b6526f
b6526f
Reviewed-by: Michal Židek <mzidek@redhat.com>
b6526f
(cherry picked from commit 61e4ba58934b20a950255e05797aca25aadc1242)
b6526f
---
b6526f
 src/responder/nss/nss_private.h       |   2 +
b6526f
 src/responder/nss/nsssrv.c            | 106 ++++++++++++++++++++++++--
b6526f
 src/responder/nss/nsssrv_mmap_cache.c |  51 ++++++++++++-
b6526f
 src/responder/nss/nsssrv_mmap_cache.h |   5 +-
b6526f
 5 files changed, 154 insertions(+), 10 deletions(-)
b6526f
b6526f
diff --git a/src/responder/nss/nss_private.h b/src/responder/nss/nss_private.h
b6526f
index cd0d35517..bae5fe074 100644
b6526f
--- a/src/responder/nss/nss_private.h
b6526f
+++ b/src/responder/nss/nss_private.h
b6526f
@@ -87,6 +87,8 @@ struct nss_ctx {
b6526f
     struct sss_mc_ctx *pwd_mc_ctx;
b6526f
     struct sss_mc_ctx *grp_mc_ctx;
b6526f
     struct sss_mc_ctx *initgr_mc_ctx;
b6526f
+    uid_t mc_uid;
b6526f
+    gid_t mc_gid;
b6526f
 };
b6526f
 
b6526f
 struct sss_cmd_table *get_nss_cmds(void);
b6526f
diff --git a/src/responder/nss/nsssrv.c b/src/responder/nss/nsssrv.c
b6526f
index d6c5a08a9..87a3f1d50 100644
b6526f
--- a/src/responder/nss/nsssrv.c
b6526f
+++ b/src/responder/nss/nsssrv.c
b6526f
@@ -101,7 +101,8 @@ static int nss_clear_memcache(struct sbus_request *dbus_req, void *data)
b6526f
 
b6526f
     /* TODO: read cache sizes from configuration */
b6526f
     DEBUG(SSSDBG_TRACE_FUNC, "Clearing memory caches.\n");
b6526f
-    ret = sss_mmap_cache_reinit(nctx, SSS_MC_CACHE_ELEMENTS,
b6526f
+    ret = sss_mmap_cache_reinit(nctx, nctx->mc_uid, nctx->mc_gid,
b6526f
+                                SSS_MC_CACHE_ELEMENTS,
b6526f
                                 (time_t) memcache_timeout,
b6526f
                                 &nctx->pwd_mc_ctx);
b6526f
     if (ret != EOK) {
b6526f
@@ -110,7 +111,8 @@ static int nss_clear_memcache(struct sbus_request *dbus_req, void *data)
b6526f
         return ret;
b6526f
     }
b6526f
 
b6526f
-    ret = sss_mmap_cache_reinit(nctx, SSS_MC_CACHE_ELEMENTS,
b6526f
+    ret = sss_mmap_cache_reinit(nctx, nctx->mc_uid, nctx->mc_gid,
b6526f
+                                SSS_MC_CACHE_ELEMENTS,
b6526f
                                 (time_t) memcache_timeout,
b6526f
                                 &nctx->grp_mc_ctx);
b6526f
     if (ret != EOK) {
b6526f
@@ -119,7 +121,8 @@ static int nss_clear_memcache(struct sbus_request *dbus_req, void *data)
b6526f
         return ret;
b6526f
     }
b6526f
 
b6526f
-    ret = sss_mmap_cache_reinit(nctx, SSS_MC_CACHE_ELEMENTS,
b6526f
+    ret = sss_mmap_cache_reinit(nctx, nctx->mc_uid, nctx->mc_gid,
b6526f
+                                SSS_MC_CACHE_ELEMENTS,
b6526f
                                 (time_t)memcache_timeout,
b6526f
                                 &nctx->initgr_mc_ctx);
b6526f
     if (ret != EOK) {
b6526f
@@ -284,21 +287,27 @@ static int setup_memcaches(struct nss_ctx *nctx)
b6526f
     }
b6526f
 
b6526f
     /* TODO: read cache sizes from configuration */
b6526f
-    ret = sss_mmap_cache_init(nctx, "passwd", SSS_MC_PASSWD,
b6526f
+    ret = sss_mmap_cache_init(nctx, "passwd",
b6526f
+                              nctx->mc_uid, nctx->mc_gid,
b6526f
+                              SSS_MC_PASSWD,
b6526f
                               SSS_MC_CACHE_ELEMENTS, (time_t)memcache_timeout,
b6526f
                               &nctx->pwd_mc_ctx);
b6526f
     if (ret) {
b6526f
         DEBUG(SSSDBG_CRIT_FAILURE, "passwd mmap cache is DISABLED\n");
b6526f
     }
b6526f
 
b6526f
-    ret = sss_mmap_cache_init(nctx, "group", SSS_MC_GROUP,
b6526f
+    ret = sss_mmap_cache_init(nctx, "group",
b6526f
+                              nctx->mc_uid, nctx->mc_gid,
b6526f
+                              SSS_MC_GROUP,
b6526f
                               SSS_MC_CACHE_ELEMENTS, (time_t)memcache_timeout,
b6526f
                               &nctx->grp_mc_ctx);
b6526f
     if (ret) {
b6526f
         DEBUG(SSSDBG_CRIT_FAILURE, "group mmap cache is DISABLED\n");
b6526f
     }
b6526f
 
b6526f
-    ret = sss_mmap_cache_init(nctx, "initgroups", SSS_MC_INITGROUPS,
b6526f
+    ret = sss_mmap_cache_init(nctx, "initgroups",
b6526f
+                              nctx->mc_uid, nctx->mc_gid,
b6526f
+                              SSS_MC_INITGROUPS,
b6526f
                               SSS_MC_CACHE_ELEMENTS, (time_t)memcache_timeout,
b6526f
                               &nctx->initgr_mc_ctx);
b6526f
     if (ret) {
b6526f
@@ -308,6 +317,79 @@ static int setup_memcaches(struct nss_ctx *nctx)
b6526f
     return EOK;
b6526f
 }
b6526f
 
b6526f
+static int sssd_supplementary_group(struct nss_ctx *nss_ctx)
b6526f
+{
b6526f
+    errno_t ret;
b6526f
+    int size;
b6526f
+    gid_t *supp_gids = NULL;
b6526f
+
b6526f
+    /*
b6526f
+     * We explicitly read the IDs of the SSSD user even though the server
b6526f
+     * receives --uid and --gid by parameters to account for the case where
b6526f
+     * the SSSD is compiled --with-sssd-user=sssd but the default of the
b6526f
+     * user option is root (this is what RHEL does)
b6526f
+     */
b6526f
+    ret = sss_user_by_name_or_uid(SSSD_USER,
b6526f
+                                  &nss_ctx->mc_uid,
b6526f
+                                  &nss_ctx->mc_gid);
b6526f
+    if (ret != EOK) {
b6526f
+        DEBUG(SSSDBG_MINOR_FAILURE, "Cannot get info on "SSSD_USER);
b6526f
+        return ret;
b6526f
+    }
b6526f
+
b6526f
+    if (getgid() == nss_ctx->mc_gid) {
b6526f
+        DEBUG(SSSDBG_TRACE_FUNC, "Already running as the sssd group\n");
b6526f
+        return EOK;
b6526f
+    }
b6526f
+
b6526f
+    size = getgroups(0, NULL);
b6526f
+    if (size == -1) {
b6526f
+        ret = errno;
b6526f
+        DEBUG(SSSDBG_CRIT_FAILURE, "Getgroups failed! (%d, %s)\n",
b6526f
+                                    ret, sss_strerror(ret));
b6526f
+        return ret;
b6526f
+    }
b6526f
+
b6526f
+    if (size > 0) {
b6526f
+        supp_gids = talloc_zero_array(NULL, gid_t, size);
b6526f
+        if (supp_gids == NULL) {
b6526f
+            DEBUG(SSSDBG_CRIT_FAILURE, "Allocation failed!\n");
b6526f
+            ret = ENOMEM;
b6526f
+            goto done;
b6526f
+        }
b6526f
+    }
b6526f
+
b6526f
+    size = getgroups(size, supp_gids);
b6526f
+    if (size == -1) {
b6526f
+        ret = errno;
b6526f
+        DEBUG(SSSDBG_CRIT_FAILURE, "Getgroups failed! (%d, %s)\n",
b6526f
+                                    ret, sss_strerror(ret));
b6526f
+        goto done;
b6526f
+    }
b6526f
+
b6526f
+    for (int i = 0; i < size; i++) {
b6526f
+        if (supp_gids[i] == nss_ctx->mc_gid) {
b6526f
+            DEBUG(SSSDBG_TRACE_FUNC,
b6526f
+                  "Already assigned to the SSSD supplementary group\n");
b6526f
+            ret = EOK;
b6526f
+            goto done;
b6526f
+        }
b6526f
+    }
b6526f
+
b6526f
+    ret = setgroups(1, &nss_ctx->mc_gid);
b6526f
+    if (ret != EOK) {
b6526f
+        ret = errno;
b6526f
+        DEBUG(SSSDBG_OP_FAILURE,
b6526f
+              "Cannot setgroups [%d]: %s\n", ret, sss_strerror(ret));
b6526f
+        goto done;
b6526f
+    }
b6526f
+
b6526f
+    ret = EOK;
b6526f
+done:
b6526f
+    talloc_free(supp_gids);
b6526f
+    return ret;
b6526f
+}
b6526f
+
b6526f
 int nss_process_init(TALLOC_CTX *mem_ctx,
b6526f
                      struct tevent_context *ev,
b6526f
                      struct confdb_ctx *cdb)
b6526f
@@ -405,6 +487,18 @@ int nss_process_init(TALLOC_CTX *mem_ctx,
b6526f
         ret = EFAULT;
b6526f
         goto fail;
b6526f
     }
b6526f
+    /*
b6526f
+     * Adding the NSS process to the SSSD supplementary group avoids
b6526f
+     * dac_override AVC messages from SELinux in case sssd_nss runs
b6526f
+     * as root and tries to write to memcache owned by sssd:sssd
b6526f
+     */
b6526f
+    ret = sssd_supplementary_group(nctx);
b6526f
+    if (ret != EOK) {
b6526f
+        DEBUG(SSSDBG_MINOR_FAILURE,
b6526f
+              "Cannot add process to the sssd supplementary group [%d]: %s\n",
b6526f
+              ret, sss_strerror(ret));
b6526f
+        goto fail;
b6526f
+    }
b6526f
 
b6526f
     ret = setup_memcaches(nctx);
b6526f
     if (ret != EOK) {
b6526f
diff --git a/src/responder/nss/nsssrv_mmap_cache.c b/src/responder/nss/nsssrv_mmap_cache.c
b6526f
index de9e67513..d5181d771 100644
b6526f
--- a/src/responder/nss/nsssrv_mmap_cache.c
b6526f
+++ b/src/responder/nss/nsssrv_mmap_cache.c
b6526f
@@ -57,6 +57,9 @@ struct sss_mc_ctx {
b6526f
     char *file;             /* mmap cache file name */
b6526f
     int fd;                 /* file descriptor */
b6526f
 
b6526f
+    uid_t uid;              /* User ID of owner */
b6526f
+    gid_t gid;              /* Group ID of owner */
b6526f
+
b6526f
     uint32_t seed;          /* pseudo-random seed to avoid collision attacks */
b6526f
     time_t valid_time_slot; /* maximum time the entry is valid in seconds */
b6526f
 
b6526f
@@ -623,7 +626,9 @@ static errno_t sss_mc_get_record(struct sss_mc_ctx **_mcc,
b6526f
         if (ret == EFAULT) {
b6526f
             DEBUG(SSSDBG_CRIT_FAILURE,
b6526f
                   "Fatal internal mmap cache error, invalidating cache!\n");
b6526f
-            (void)sss_mmap_cache_reinit(talloc_parent(mcc), -1, -1, _mcc);
b6526f
+            (void)sss_mmap_cache_reinit(talloc_parent(mcc),
b6526f
+                                        -1, -1, -1, -1,
b6526f
+                                        _mcc);
b6526f
         }
b6526f
         return ret;
b6526f
     }
b6526f
@@ -1144,6 +1149,26 @@ static errno_t sss_mc_create_file(struct sss_mc_ctx *mc_ctx)
b6526f
         return ret;
b6526f
     }
b6526f
 
b6526f
+    /* Make sure that the memory cache files are chowned to sssd.sssd even
b6526f
+     * if the nss responder runs as root. This is because the specfile
b6526f
+     * has the ownership recorded as sssd.sssd
b6526f
+     */
b6526f
+    ret = fchown(mc_ctx->fd, mc_ctx->uid, mc_ctx->gid);
b6526f
+    if (ret != 0) {
b6526f
+        ret = errno;
b6526f
+        DEBUG(SSSDBG_CRIT_FAILURE, "Failed to chown mmap file %s: %d(%s)\n",
b6526f
+                                   mc_ctx->file, ret, strerror(ret));
b6526f
+        return ret;
b6526f
+    }
b6526f
+
b6526f
+    ret = fchmod(mc_ctx->fd, S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH);
b6526f
+    if (ret == -1) {
b6526f
+        ret = errno;
b6526f
+        DEBUG(SSSDBG_CRIT_FAILURE, "Failed to chmod mmap file %s: %d(%s)\n",
b6526f
+                                   mc_ctx->file, ret, strerror(ret));
b6526f
+        return ret;
b6526f
+    }
b6526f
+
b6526f
     ret = sss_br_lock_file(mc_ctx->fd, 0, 1, retries, t);
b6526f
     if (ret != EOK) {
b6526f
         DEBUG(SSSDBG_FATAL_FAILURE,
b6526f
@@ -1224,6 +1249,7 @@ static int mc_ctx_destructor(struct sss_mc_ctx *mc_ctx)
b6526f
 }
b6526f
 
b6526f
 errno_t sss_mmap_cache_init(TALLOC_CTX *mem_ctx, const char *name,
b6526f
+                            uid_t uid, gid_t gid,
b6526f
                             enum sss_mc_type type, size_t n_elem,
b6526f
                             time_t timeout, struct sss_mc_ctx **mcc)
b6526f
 {
b6526f
@@ -1259,6 +1285,9 @@ errno_t sss_mmap_cache_init(TALLOC_CTX *mem_ctx, const char *name,
b6526f
         goto done;
b6526f
     }
b6526f
 
b6526f
+    mc_ctx->uid = uid;
b6526f
+    mc_ctx->gid = gid;
b6526f
+
b6526f
     mc_ctx->type = type;
b6526f
 
b6526f
     mc_ctx->valid_time_slot = timeout;
b6526f
@@ -1352,7 +1381,9 @@ done:
b6526f
     return ret;
b6526f
 }
b6526f
 
b6526f
-errno_t sss_mmap_cache_reinit(TALLOC_CTX *mem_ctx, size_t n_elem,
b6526f
+errno_t sss_mmap_cache_reinit(TALLOC_CTX *mem_ctx,
b6526f
+                              uid_t uid, gid_t gid,
b6526f
+                              size_t n_elem,
b6526f
                               time_t timeout, struct sss_mc_ctx **mc_ctx)
b6526f
 {
b6526f
     errno_t ret;
b6526f
@@ -1389,12 +1420,26 @@ errno_t sss_mmap_cache_reinit(TALLOC_CTX *mem_ctx, size_t n_elem,
b6526f
         timeout = (*mc_ctx)->valid_time_slot;
b6526f
     }
b6526f
 
b6526f
+    if (uid == (uid_t)-1) {
b6526f
+        uid = (*mc_ctx)->uid;
b6526f
+    }
b6526f
+
b6526f
+    if (gid == (gid_t)-1) {
b6526f
+        gid = (*mc_ctx)->gid;
b6526f
+    }
b6526f
+
b6526f
     talloc_free(*mc_ctx);
b6526f
 
b6526f
     /* make sure we do not leave a potentially freed pointer around */
b6526f
     *mc_ctx = NULL;
b6526f
 
b6526f
-    ret = sss_mmap_cache_init(mem_ctx, name, type, n_elem, timeout, mc_ctx);
b6526f
+    ret = sss_mmap_cache_init(mem_ctx,
b6526f
+                              name,
b6526f
+                              uid, gid,
b6526f
+                              type,
b6526f
+                              n_elem,
b6526f
+                              timeout,
b6526f
+                              mc_ctx);
b6526f
     if (ret != EOK) {
b6526f
         DEBUG(SSSDBG_CRIT_FAILURE, "Failed to re-initialize mmap cache.\n");
b6526f
         goto done;
b6526f
diff --git a/src/responder/nss/nsssrv_mmap_cache.h b/src/responder/nss/nsssrv_mmap_cache.h
b6526f
index b84fbc8ed..e06257949 100644
b6526f
--- a/src/responder/nss/nsssrv_mmap_cache.h
b6526f
+++ b/src/responder/nss/nsssrv_mmap_cache.h
b6526f
@@ -34,6 +34,7 @@ enum sss_mc_type {
b6526f
 };
b6526f
 
b6526f
 errno_t sss_mmap_cache_init(TALLOC_CTX *mem_ctx, const char *name,
b6526f
+                            uid_t uid, gid_t gid,
b6526f
                             enum sss_mc_type type, size_t n_elem,
b6526f
                             time_t valid_time, struct sss_mc_ctx **mcc);
b6526f
 
b6526f
@@ -70,7 +71,9 @@ errno_t sss_mmap_cache_gr_invalidate_gid(struct sss_mc_ctx *mcc, gid_t gid);
b6526f
 errno_t sss_mmap_cache_initgr_invalidate(struct sss_mc_ctx *mcc,
b6526f
                                          struct sized_string *name);
b6526f
 
b6526f
-errno_t sss_mmap_cache_reinit(TALLOC_CTX *mem_ctx, size_t n_elem,
b6526f
+errno_t sss_mmap_cache_reinit(TALLOC_CTX *mem_ctx,
b6526f
+                              uid_t uid, gid_t gid,
b6526f
+                              size_t n_elem,
b6526f
                               time_t timeout, struct sss_mc_ctx **mc_ctx);
b6526f
 
b6526f
 void sss_mmap_cache_reset(struct sss_mc_ctx *mc_ctx);
b6526f
-- 
b6526f
2.19.1
b6526f