dpward / rpms / sssd

Forked from rpms/sssd 3 years ago
Clone

Blame SOURCES/0026-files-allow-root-membership.patch

1bb595
From 8969c43dc2d8d0800c2f0b509d078378db855622 Mon Sep 17 00:00:00 2001
1bb595
From: =?UTF-8?q?Pavel=20B=C5=99ezina?= <pbrezina@redhat.com>
1bb595
Date: Tue, 23 Jun 2020 12:05:08 +0200
1bb595
Subject: [PATCH] files: allow root membership
1bb595
1bb595
There are two use cases that do not work with files provider:
1bb595
1bb595
1. User has primary GID 0:
1bb595
1bb595
This is fine by itself since SSSD does not store this user in cache and it is
1bb595
handled only by `nss_files` so the user (`tuser`) is returned correctly. The
1bb595
problem is when you try to resolve group that the user is member of. In this
1bb595
case that the membership is missing the group (but only if the user was
1bb595
previously resolved and thus stored in negative cache).
1bb595
1bb595
```
1bb595
tuser:x:1001:0::/home/tuser:/bin/bash
1bb595
tuser:x:1001:tuser
1bb595
1bb595
// tuser@files is ghost member of the group so it is returned because it is not in negative cache
1bb595
$ getent group tuser
1bb595
tuser:x:1001:tuser
1bb595
1bb595
// expire memcache
1bb595
// tuser@files is ghost member but not returned because it is in negative cache
1bb595
$ id tuser // returned from nss_files
1bb595
uid=1001(tuser) gid=0(root) groups=0(root),1001(tuser)
1bb595
[pbrezina /dev/shm/sssd]$ getent group tuser
1bb595
tuser:x:1001:
1bb595
```
1bb595
1bb595
**2. root is member of other group**
1bb595
1bb595
The root member is missing from the membership since it was filtered out by
1bb595
negative cache.
1bb595
1bb595
```
1bb595
tuser:x:1001:root
1bb595
1bb595
$ id root
1bb595
uid=0(root) gid=0(root) groups=0(root),1001(tuser)
1bb595
[pbrezina /dev/shm/sssd]$ getent group tuser
1bb595
tuser:x:1001:
1bb595
```
1bb595
1bb595
In files provider, only the users that we do not want to managed are stored
1bb595
as ghost member, therefore we can let nss_files handle group that has ghost
1bb595
members.
1bb595
1bb595
Tests are changed as well to work with this behavior. Users are added when
1bb595
required and ghost are expected to return ENOENT.
1bb595
1bb595
Resolves:
1bb595
https://github.com/SSSD/sssd/issues/5170
1bb595
1bb595
Reviewed-by: Sumit Bose <sbose@redhat.com>
1bb595
---
1bb595
 src/responder/nss/nss_protocol_grent.c | 18 +++++++
1bb595
 src/tests/intg/files_ops.py            | 13 +++++
1bb595
 src/tests/intg/test_files_provider.py  | 73 ++++++++++++++++----------
1bb595
 3 files changed, 77 insertions(+), 27 deletions(-)
1bb595
1bb595
diff --git a/src/responder/nss/nss_protocol_grent.c b/src/responder/nss/nss_protocol_grent.c
1bb595
index 9c443d0e7..6d8e71083 100644
1bb595
--- a/src/responder/nss/nss_protocol_grent.c
1bb595
+++ b/src/responder/nss/nss_protocol_grent.c
1bb595
@@ -141,6 +141,24 @@ nss_protocol_fill_members(struct sss_packet *packet,
1bb595
     members[0] = nss_get_group_members(domain, msg);
1bb595
     members[1] = nss_get_group_ghosts(domain, msg, group_name);
1bb595
 
1bb595
+    if (is_files_provider(domain) && members[1] != NULL) {
1bb595
+        /* If there is a ghost member in files provider it means that we
1bb595
+         * did not store the user on purpose (e.g. it has uid or gid 0).
1bb595
+         * Therefore nss_files does handle the user and therefore we
1bb595
+         * must let nss_files to also handle this group in order to
1bb595
+         * provide correct membership. */
1bb595
+        DEBUG(SSSDBG_TRACE_FUNC,
1bb595
+              "Unknown members found. nss_files will handle it.\n");
1bb595
+
1bb595
+        ret = sss_ncache_set_group(rctx->ncache, false, domain, group_name);
1bb595
+        if (ret != EOK) {
1bb595
+            DEBUG(SSSDBG_OP_FAILURE, "sss_ncache_set_group failed.\n");
1bb595
+        }
1bb595
+
1bb595
+        ret = ENOENT;
1bb595
+        goto done;
1bb595
+    }
1bb595
+
1bb595
     sss_packet_get_body(packet, &body, &body_len);
1bb595
 
1bb595
     num_members = 0;
1bb595
diff --git a/src/tests/intg/files_ops.py b/src/tests/intg/files_ops.py
1bb595
index c1c4465e7..57959f501 100644
1bb595
--- a/src/tests/intg/files_ops.py
1bb595
+++ b/src/tests/intg/files_ops.py
1bb595
@@ -103,6 +103,13 @@ class FilesOps(object):
1bb595
 
1bb595
         contents = self._read_contents()
1bb595
 
1bb595
+    def _has_line(self, key):
1bb595
+        try:
1bb595
+            self._get_named_line(key, self._read_contents())
1bb595
+            return True
1bb595
+        except KeyError:
1bb595
+            return False
1bb595
+
1bb595
 
1bb595
 class PasswdOps(FilesOps):
1bb595
     """
1bb595
@@ -132,6 +139,9 @@ class PasswdOps(FilesOps):
1bb595
     def userdel(self, name):
1bb595
         self._del_line(name)
1bb595
 
1bb595
+    def userexist(self, name):
1bb595
+        return self._has_line(name)
1bb595
+
1bb595
 
1bb595
 class GroupOps(FilesOps):
1bb595
     """
1bb595
@@ -158,3 +168,6 @@ class GroupOps(FilesOps):
1bb595
 
1bb595
     def groupdel(self, name):
1bb595
         self._del_line(name)
1bb595
+
1bb595
+    def groupexist(self, name):
1bb595
+        return self._has_line(name)
1bb595
diff --git a/src/tests/intg/test_files_provider.py b/src/tests/intg/test_files_provider.py
1bb595
index 023333020..90be198c3 100644
1bb595
--- a/src/tests/intg/test_files_provider.py
1bb595
+++ b/src/tests/intg/test_files_provider.py
1bb595
@@ -60,11 +60,13 @@ OV_USER1 = dict(name='ov_user1', passwd='x', uid=10010, gid=20010,
1bb595
                 dir='/home/ov/user1',
1bb595
                 shell='/bin/ov_user1_shell')
1bb595
 
1bb595
-ALT_USER1 = dict(name='altuser1', passwd='x', uid=60001, gid=70001,
1bb595
+ALT_USER1 = dict(name='alt_user1', passwd='x', uid=60001, gid=70001,
1bb595
                  gecos='User for tests from alt files',
1bb595
                  dir='/home/altuser1',
1bb595
                  shell='/bin/bash')
1bb595
 
1bb595
+ALL_USERS = [CANARY, USER1, USER2, OV_USER1, ALT_USER1]
1bb595
+
1bb595
 CANARY_GR = dict(name='canary',
1bb595
                  gid=300001,
1bb595
                  mem=[])
1bb595
@@ -365,21 +367,34 @@ def setup_pw_with_canary(passwd_ops_setup):
1bb595
     return setup_pw_with_list(passwd_ops_setup, [CANARY])
1bb595
 
1bb595
 
1bb595
-def setup_gr_with_list(grp_ops, group_list):
1bb595
+def add_group_members(pwd_ops, group):
1bb595
+    members = {x['name']: x for x in ALL_USERS}
1bb595
+    for member in group['mem']:
1bb595
+        if pwd_ops.userexist(member):
1bb595
+            continue
1bb595
+
1bb595
+        pwd_ops.useradd(**members[member])
1bb595
+
1bb595
+
1bb595
+def setup_gr_with_list(pwd_ops, grp_ops, group_list):
1bb595
     for group in group_list:
1bb595
+        add_group_members(pwd_ops, group)
1bb595
         grp_ops.groupadd(**group)
1bb595
+
1bb595
     ent.assert_group_by_name(CANARY_GR['name'], CANARY_GR)
1bb595
     return grp_ops
1bb595
 
1bb595
 
1bb595
 @pytest.fixture
1bb595
-def add_group_with_canary(group_ops_setup):
1bb595
-    return setup_gr_with_list(group_ops_setup, [GROUP1, CANARY_GR])
1bb595
+def add_group_with_canary(passwd_ops_setup, group_ops_setup):
1bb595
+    return setup_gr_with_list(
1bb595
+        passwd_ops_setup, group_ops_setup, [GROUP1, CANARY_GR]
1bb595
+    )
1bb595
 
1bb595
 
1bb595
 @pytest.fixture
1bb595
-def setup_gr_with_canary(group_ops_setup):
1bb595
-    return setup_gr_with_list(group_ops_setup, [CANARY_GR])
1bb595
+def setup_gr_with_canary(passwd_ops_setup, group_ops_setup):
1bb595
+    return setup_gr_with_list(passwd_ops_setup, group_ops_setup, [CANARY_GR])
1bb595
 
1bb595
 
1bb595
 def poll_canary(fn, name, threshold=20):
1bb595
@@ -766,7 +781,9 @@ def test_gid_zero_does_not_resolve(files_domain_only):
1bb595
     assert res == NssReturnCode.NOTFOUND
1bb595
 
1bb595
 
1bb595
-def test_add_remove_add_file_group(setup_gr_with_canary, files_domain_only):
1bb595
+def test_add_remove_add_file_group(
1bb595
+        setup_pw_with_canary, setup_gr_with_canary, files_domain_only
1bb595
+):
1bb595
     """
1bb595
     Test that removing a group is detected and the group
1bb595
     is removed from the sssd database. Similarly, an add
1bb595
@@ -776,6 +793,7 @@ def test_add_remove_add_file_group(setup_gr_with_canary, files_domain_only):
1bb595
     res, group = call_sssd_getgrnam(GROUP1["name"])
1bb595
     assert res == NssReturnCode.NOTFOUND
1bb595
 
1bb595
+    add_group_members(setup_pw_with_canary, GROUP1)
1bb595
     setup_gr_with_canary.groupadd(**GROUP1)
1bb595
     check_group(GROUP1)
1bb595
 
1bb595
@@ -817,8 +835,10 @@ def test_mod_group_gid(add_group_with_canary, files_domain_only):
1bb595
 
1bb595
 
1bb595
 @pytest.fixture
1bb595
-def add_group_nomem_with_canary(group_ops_setup):
1bb595
-    return setup_gr_with_list(group_ops_setup, [GROUP_NOMEM, CANARY_GR])
1bb595
+def add_group_nomem_with_canary(passwd_ops_setup, group_ops_setup):
1bb595
+    return setup_gr_with_list(
1bb595
+        passwd_ops_setup, group_ops_setup, [GROUP_NOMEM, CANARY_GR]
1bb595
+    )
1bb595
 
1bb595
 
1bb595
 def test_getgrnam_no_members(add_group_nomem_with_canary, files_domain_only):
1bb595
@@ -911,16 +931,19 @@ def test_getgrnam_ghost(setup_pw_with_canary,
1bb595
                         setup_gr_with_canary,
1bb595
                         files_domain_only):
1bb595
     """
1bb595
-    Test that a group with members while the members are not present
1bb595
-    are added as ghosts. This is also what nss_files does, getgrnam would
1bb595
-    return group members that do not exist as well.
1bb595
+    Test that group if not found (and will be handled by nss_files) if there
1bb595
+    are any ghost members.
1bb595
     """
1bb595
     user_and_group_setup(setup_pw_with_canary,
1bb595
                          setup_gr_with_canary,
1bb595
                          [],
1bb595
                          [GROUP12],
1bb595
                          False)
1bb595
-    check_group(GROUP12)
1bb595
+
1bb595
+    time.sleep(1)
1bb595
+    res, group = call_sssd_getgrnam(GROUP12["name"])
1bb595
+    assert res == NssReturnCode.NOTFOUND
1bb595
+
1bb595
     for member in GROUP12['mem']:
1bb595
         res, _ = call_sssd_getpwnam(member)
1bb595
         assert res == NssReturnCode.NOTFOUND
1bb595
@@ -932,7 +955,10 @@ def ghost_and_member_test(pw_ops, grp_ops, reverse):
1bb595
                          [USER1],
1bb595
                          [GROUP12],
1bb595
                          reverse)
1bb595
-    check_group(GROUP12)
1bb595
+
1bb595
+    time.sleep(1)
1bb595
+    res, group = call_sssd_getgrnam(GROUP12["name"])
1bb595
+    assert res == NssReturnCode.NOTFOUND
1bb595
 
1bb595
     # We checked that the group added has the same members as group12,
1bb595
     # so both user1 and user2. Now check that user1 is a member of
1bb595
@@ -1027,28 +1053,21 @@ def test_getgrnam_add_remove_ghosts(setup_pw_with_canary,
1bb595
     modgroup = dict(GROUP_NOMEM)
1bb595
     modgroup['mem'] = ['user1', 'user2']
1bb595
     add_group_nomem_with_canary.groupmod(old_name=modgroup['name'], **modgroup)
1bb595
-    check_group(modgroup)
1bb595
+    time.sleep(1)
1bb595
+    res, group = call_sssd_getgrnam(modgroup['name'])
1bb595
+    assert res == sssd_id.NssReturnCode.NOTFOUND
1bb595
 
1bb595
     modgroup['mem'] = ['user2']
1bb595
     add_group_nomem_with_canary.groupmod(old_name=modgroup['name'], **modgroup)
1bb595
-    check_group(modgroup)
1bb595
+    time.sleep(1)
1bb595
+    res, group = call_sssd_getgrnam(modgroup['name'])
1bb595
+    assert res == sssd_id.NssReturnCode.NOTFOUND
1bb595
 
1bb595
     res, _ = call_sssd_getpwnam('user1')
1bb595
     assert res == NssReturnCode.NOTFOUND
1bb595
     res, _ = call_sssd_getpwnam('user2')
1bb595
     assert res == NssReturnCode.NOTFOUND
1bb595
 
1bb595
-    # Add this user and verify it's been added as a member
1bb595
-    pwd_ops.useradd(**USER2)
1bb595
-    # The negative cache might still have user2 from the previous request,
1bb595
-    # flushing the caches might help to prevent a failed lookup after adding
1bb595
-    # the user.
1bb595
-    subprocess.call(["sss_cache", "-E"])
1bb595
-    res, groups = sssd_id_sync('user2')
1bb595
-    assert res == sssd_id.NssReturnCode.SUCCESS
1bb595
-    assert len(groups) == 2
1bb595
-    assert 'group_nomem' in groups
1bb595
-
1bb595
 
1bb595
 def realloc_users(pwd_ops, num):
1bb595
     # Intentionally not including the last one because
1bb595
-- 
1bb595
2.21.3
1bb595