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

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