URL: https://github.com/SSSD/sssd/pull/702
Author: jhrozek
 Title: #702: NSS: Avoid changing the memory cache ownership away from the SSSD 
user
Action: opened

PR body:
"""
Resolves: https://pagure.io/SSSD/sssd/issue/3890

In case SSSD is compiled --with-sssd-user but run as root (which is the 
default on RHEL and derivatives), then the memory cache will be owned by 
the user that sssd_nss runs as, so root.

This conflicts with the packaging which specifies sssd.sssd as the owner.
And in turn, this means that users can't reliably assess the package
integrity using rpm -V.

This patch makes sure that the memory cache files are chowned to sssd.sssd 
even if the nss responder runs as root.

Also, this patch changes the sssd_nss responder so that is becomes a member 
of the supplementary sssd group. Even though in traditional UNIX sense, a
process running as root could write to a file owned by sssd:sssd, with 
SELinux enforcing mode this becomes problematic as SELinux emits an error 
such as:

type=AVC msg=audit(1543524888.125:1495): avc:  denied  { fsetid } for 
pid=7706 comm="sssd_nss" capability=4  scontext=system_u:system_r:sssd_t:s0 
tcontext=system_u:system_r:sssd_t:s0 tclass=capability

To make it possible for the sssd_nss process to write to the files, the 
files are also made group-writable. The 'others' permission is still set to
read only.
"""

To pull the PR as Git branch:
git remote add ghsssd https://github.com/SSSD/sssd
git fetch ghsssd pull/702/head:pr702
git checkout pr702
From ed33e33df552ed53130135a925678c8e25f2e0d2 Mon Sep 17 00:00:00 2001
From: Jakub Hrozek <jhro...@redhat.com>
Date: Thu, 29 Nov 2018 09:18:32 +0100
Subject: [PATCH] NSS: Avoid changing the memory cache ownership away from the
 SSSD user

Resolves:
https://pagure.io/SSSD/sssd/issue/3890

In case SSSD is compiled --with-sssd-user but run as root (which is the
default on RHEL and derivatives), then the memory cache will be owned by
the user that sssd_nss runs as, so root.

This conflicts with the packaging which specifies sssd.sssd as the owner. And
in turn, this means that users can't reliably assess the package integrity
using rpm -V.

This patch makes sure that the memory cache files are chowned to sssd.sssd
even if the nss responder runs as root.

Also, this patch changes the sssd_nss responder so that is becomes a member
of the supplementary sssd group. Even though in traditional UNIX sense,
a process running as root could write to a file owned by sssd:sssd, with
SELinux enforcing mode this becomes problematic as SELinux emits an error
such as:

type=AVC msg=audit(1543524888.125:1495): avc:  denied  { fsetid } for
pid=7706 comm="sssd_nss" capability=4  scontext=system_u:system_r:sssd_t:s0
tcontext=system_u:system_r:sssd_t:s0 tclass=capability

To make it possible for the sssd_nss process to write to the files, the
files are also made group-writable. The 'others' permission is still set
to read only.
---
 contrib/sssd.spec.in                  |   8 +-
 src/responder/nss/nsssrv.c            | 111 +++++++++++++++++++++++++-
 src/responder/nss/nsssrv_mmap_cache.c |  43 +++++++++-
 src/responder/nss/nsssrv_mmap_cache.h |   1 +
 4 files changed, 155 insertions(+), 8 deletions(-)

diff --git a/contrib/sssd.spec.in b/contrib/sssd.spec.in
index 26fae6d68..22a1063b2 100644
--- a/contrib/sssd.spec.in
+++ b/contrib/sssd.spec.in
@@ -1039,11 +1039,11 @@ done
 %dir %{sssdstatedir}
 %dir %{_localstatedir}/cache/krb5rcache
 %attr(700,sssd,sssd) %dir %{dbpath}
-%attr(755,sssd,sssd) %dir %{mcpath}
+%attr(775,sssd,sssd) %dir %{mcpath}
 %attr(751,sssd,sssd) %dir %{deskprofilepath}
-%ghost %attr(0644,sssd,sssd) %verify(not md5 size mtime) %{mcpath}/passwd
-%ghost %attr(0644,sssd,sssd) %verify(not md5 size mtime) %{mcpath}/group
-%ghost %attr(0644,sssd,sssd) %verify(not md5 size mtime) %{mcpath}/initgroups
+%ghost %attr(0664,sssd,sssd) %verify(not md5 size mtime) %{mcpath}/passwd
+%ghost %attr(0664,sssd,sssd) %verify(not md5 size mtime) %{mcpath}/group
+%ghost %attr(0664,sssd,sssd) %verify(not md5 size mtime) %{mcpath}/initgroups
 %attr(755,sssd,sssd) %dir %{pipepath}
 %attr(750,sssd,root) %dir %{pipepath}/private
 %attr(755,sssd,sssd) %dir %{pubconfpath}
diff --git a/src/responder/nss/nsssrv.c b/src/responder/nss/nsssrv.c
index fb7326a02..808b96108 100644
--- a/src/responder/nss/nsssrv.c
+++ b/src/responder/nss/nsssrv.c
@@ -209,6 +209,8 @@ static int setup_memcaches(struct nss_ctx *nctx)
 {
     int ret;
     int memcache_timeout;
+    uid_t sssd_uid;
+    gid_t sssd_gid;
 
     /* Remove the CLEAR_MC_FLAG file if exists. */
     ret = unlink(SSS_NSS_MCACHE_DIR"/"CLEAR_MC_FLAG);
@@ -236,22 +238,40 @@ static int setup_memcaches(struct nss_ctx *nctx)
         return EOK;
     }
 
+    /*
+     * We explicitly read the IDs of the SSSD user even though the server
+     * receives --uid and --gid by parameters to account for the case where
+     * the SSSD is compiled --with-sssd-user=sssd but the default of the
+     * user option is root (this is what RHEL does)
+     */
+    ret = sss_user_by_name_or_uid(SSSD_USER, &sssd_uid, &sssd_gid);
+    if (ret != EOK) {
+        DEBUG(SSSDBG_MINOR_FAILURE, "Cannot get info on "SSSD_USER);
+        return ret;
+    }
+
     /* TODO: read cache sizes from configuration */
-    ret = sss_mmap_cache_init(nctx, "passwd", SSS_MC_PASSWD,
+    ret = sss_mmap_cache_init(nctx, "passwd",
+                              sssd_uid, sssd_gid,
+                              SSS_MC_PASSWD,
                               SSS_MC_CACHE_ELEMENTS, (time_t)memcache_timeout,
                               &nctx->pwd_mc_ctx);
     if (ret) {
         DEBUG(SSSDBG_CRIT_FAILURE, "passwd mmap cache is DISABLED\n");
     }
 
-    ret = sss_mmap_cache_init(nctx, "group", SSS_MC_GROUP,
+    ret = sss_mmap_cache_init(nctx, "group",
+                              sssd_uid, sssd_gid,
+                              SSS_MC_GROUP,
                               SSS_MC_CACHE_ELEMENTS, (time_t)memcache_timeout,
                               &nctx->grp_mc_ctx);
     if (ret) {
         DEBUG(SSSDBG_CRIT_FAILURE, "group mmap cache is DISABLED\n");
     }
 
-    ret = sss_mmap_cache_init(nctx, "initgroups", SSS_MC_INITGROUPS,
+    ret = sss_mmap_cache_init(nctx, "initgroups",
+                              sssd_uid, sssd_gid,
+                              SSS_MC_INITGROUPS,
                               SSS_MC_CACHE_ELEMENTS, (time_t)memcache_timeout,
                               &nctx->initgr_mc_ctx);
     if (ret) {
@@ -421,6 +441,78 @@ int nss_process_init(TALLOC_CTX *mem_ctx,
     return ret;
 }
 
+static int sssd_supplementary_group(void)
+{
+    gid_t sssd_gid;
+    errno_t ret;
+    int size;
+    gid_t *supp_gids = NULL;
+
+    /*
+     * We explicitly read the IDs of the SSSD user even though the server
+     * receives --uid and --gid by parameters to account for the case where
+     * the SSSD is compiled --with-sssd-user=sssd but the default of the
+     * user option is root (this is what RHEL does)
+     */
+    ret = sss_user_by_name_or_uid(SSSD_USER, NULL, &sssd_gid);
+    if (ret != EOK) {
+        DEBUG(SSSDBG_MINOR_FAILURE, "Cannot get info on "SSSD_USER);
+        return ret;
+    }
+
+    if (getgid() == sssd_gid) {
+        DEBUG(SSSDBG_TRACE_FUNC, "Already running as the sssd group\n");
+        return EOK;
+    }
+
+    size = getgroups(0, NULL);
+    if (size == -1) {
+        ret = errno;
+        DEBUG(SSSDBG_CRIT_FAILURE, "Getgroups failed! (%d, %s)\n",
+                                    ret, sss_strerror(ret));
+        return ret;
+    }
+
+    if (size > 0) {
+        supp_gids = talloc_zero_array(NULL, gid_t, size);
+        if (supp_gids == NULL) {
+            DEBUG(SSSDBG_CRIT_FAILURE, "Allocation failed!\n");
+            ret = ENOMEM;
+            goto done;
+        }
+    }
+
+    size = getgroups(size, supp_gids);
+    if (size == -1) {
+        ret = errno;
+        DEBUG(SSSDBG_CRIT_FAILURE, "Getgroups failed! (%d, %s)\n",
+                                    ret, sss_strerror(ret));
+        goto done;
+    }
+
+    for (int i = 0; i < size; i++) {
+        if (supp_gids[i] == sssd_gid) {
+            DEBUG(SSSDBG_TRACE_FUNC,
+                  "Already assigned to the SSSD supplementary group\n");
+            ret = EOK;
+            goto done;
+        }
+    }
+
+    ret = setgroups(1, &sssd_gid);
+    if (ret != EOK) {
+        ret = errno;
+        DEBUG(SSSDBG_OP_FAILURE,
+              "Cannot setgroups [%d]: %s\n", ret, sss_strerror(ret));
+        goto done;
+    }
+
+    ret = EOK;
+done:
+    talloc_free(supp_gids);
+    return ret;
+}
+
 int main(int argc, const char *argv[])
 {
     int opt;
@@ -469,6 +561,19 @@ int main(int argc, const char *argv[])
                        &main_ctx);
     if (ret != EOK) return 2;
 
+    /*
+     * Adding the NSS process to the SSSD supplementary group avoids
+     * dac_override AVC messages from SELinux in case sssd_nss runs
+     * as root and tries to write to memcache owned by sssd:sssd
+     */
+    ret = sssd_supplementary_group();
+    if (ret != EOK) {
+        DEBUG(SSSDBG_MINOR_FAILURE,
+              "Cannot add process to the sssd supplementary group [%d]: %s\n",
+              ret, sss_strerror(ret));
+        return 4;
+    }
+
     ret = die_if_parent_died();
     if (ret != EOK) {
         /* This is not fatal, don't return */
diff --git a/src/responder/nss/nsssrv_mmap_cache.c b/src/responder/nss/nsssrv_mmap_cache.c
index de9e67513..ac99caa19 100644
--- a/src/responder/nss/nsssrv_mmap_cache.c
+++ b/src/responder/nss/nsssrv_mmap_cache.c
@@ -57,6 +57,9 @@ struct sss_mc_ctx {
     char *file;             /* mmap cache file name */
     int fd;                 /* file descriptor */
 
+    uid_t uid;              /* User ID of owner */
+    gid_t gid;              /* Group ID of owner */
+
     uint32_t seed;          /* pseudo-random seed to avoid collision attacks */
     time_t valid_time_slot; /* maximum time the entry is valid in seconds */
 
@@ -1144,6 +1147,20 @@ static errno_t sss_mc_create_file(struct sss_mc_ctx *mc_ctx)
         return ret;
     }
 
+    ret = fchown(mc_ctx->fd, mc_ctx->uid, mc_ctx->gid);
+    if (ret != 0) {
+        ret = errno;
+        return ret;
+    }
+
+    ret = fchmod(mc_ctx->fd, S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH);
+    if (ret == -1) {
+        ret = errno;
+        DEBUG(SSSDBG_CRIT_FAILURE, "Failed to chmod mmap file %s: %d(%s)\n",
+                                   mc_ctx->file, ret, strerror(ret));
+        return ret;
+    }
+
     ret = sss_br_lock_file(mc_ctx->fd, 0, 1, retries, t);
     if (ret != EOK) {
         DEBUG(SSSDBG_FATAL_FAILURE,
@@ -1224,6 +1241,7 @@ static int mc_ctx_destructor(struct sss_mc_ctx *mc_ctx)
 }
 
 errno_t sss_mmap_cache_init(TALLOC_CTX *mem_ctx, const char *name,
+                            uid_t uid, gid_t gid,
                             enum sss_mc_type type, size_t n_elem,
                             time_t timeout, struct sss_mc_ctx **mcc)
 {
@@ -1259,6 +1277,9 @@ errno_t sss_mmap_cache_init(TALLOC_CTX *mem_ctx, const char *name,
         goto done;
     }
 
+    mc_ctx->uid = uid;
+    mc_ctx->gid = gid;
+
     mc_ctx->type = type;
 
     mc_ctx->valid_time_slot = timeout;
@@ -1359,6 +1380,8 @@ errno_t sss_mmap_cache_reinit(TALLOC_CTX *mem_ctx, size_t n_elem,
     TALLOC_CTX* tmp_ctx = NULL;
     char *name;
     enum sss_mc_type type;
+    uid_t sssd_uid;
+    gid_t sssd_gid;
 
     if (mc_ctx == NULL || (*mc_ctx) == NULL) {
         DEBUG(SSSDBG_CRIT_FAILURE,
@@ -1394,7 +1417,25 @@ errno_t sss_mmap_cache_reinit(TALLOC_CTX *mem_ctx, size_t n_elem,
     /* make sure we do not leave a potentially freed pointer around */
     *mc_ctx = NULL;
 
-    ret = sss_mmap_cache_init(mem_ctx, name, type, n_elem, timeout, mc_ctx);
+    /*
+     * We explicitly read the IDs of the SSSD user even though the server
+     * receives --uid and --gid by parameters to account for the case where
+     * the SSSD is compiled --with-sssd-user=sssd but the default of the
+     * user option is root (this is what RHEL does)
+     */
+    ret = sss_user_by_name_or_uid(SSSD_USER, &sssd_uid, &sssd_gid);
+    if (ret != EOK) {
+        DEBUG(SSSDBG_MINOR_FAILURE, "Cannot get info on "SSSD_USER);
+        goto done;
+    }
+
+    ret = sss_mmap_cache_init(mem_ctx,
+                              name,
+                              sssd_uid, sssd_gid,
+                              type,
+                              n_elem,
+                              timeout,
+                              mc_ctx);
     if (ret != EOK) {
         DEBUG(SSSDBG_CRIT_FAILURE, "Failed to re-initialize mmap cache.\n");
         goto done;
diff --git a/src/responder/nss/nsssrv_mmap_cache.h b/src/responder/nss/nsssrv_mmap_cache.h
index b84fbc8ed..b1820f718 100644
--- a/src/responder/nss/nsssrv_mmap_cache.h
+++ b/src/responder/nss/nsssrv_mmap_cache.h
@@ -34,6 +34,7 @@ enum sss_mc_type {
 };
 
 errno_t sss_mmap_cache_init(TALLOC_CTX *mem_ctx, const char *name,
+                            uid_t uid, gid_t gid,
                             enum sss_mc_type type, size_t n_elem,
                             time_t valid_time, struct sss_mc_ctx **mcc);
 
_______________________________________________
sssd-devel mailing list -- sssd-devel@lists.fedorahosted.org
To unsubscribe send an email to sssd-devel-le...@lists.fedorahosted.org
Fedora Code of Conduct: https://getfedora.org/code-of-conduct.html
List Guidelines: https://fedoraproject.org/wiki/Mailing_list_guidelines
List Archives: 
https://lists.fedorahosted.org/archives/list/sssd-devel@lists.fedorahosted.org

Reply via email to