Whamcloud - gitweb
LU-14121 nodemap: do not force fsuid/fsgid squashing 45/40645/6
authorSebastien Buisson <sbuisson@ddn.com>
Fri, 13 Nov 2020 10:36:14 +0000 (19:36 +0900)
committerOleg Drokin <green@whamcloud.com>
Sun, 13 Dec 2020 08:23:22 +0000 (08:23 +0000)
In the current implementation, if the real uid is squashed, then the
fsuid is similarly squashed, no matter what is the value of the
effective uid.
This squashing is a little bit too strict, and we should instead trust
mapped fsuid and fsgid values.

Also add euid_access test program and sanity-sec test_55 to verify
the issue is fixed.

Signed-off-by: Sebastien Buisson <sbuisson@ddn.com>
Change-Id: Iecaecac5054b105cd42206b0a9a3868cde0269b4
Reviewed-on: https://review.whamcloud.com/40645
Tested-by: jenkins <devops@whamcloud.com>
Tested-by: Maloo <maloo@whamcloud.com>
Reviewed-by: John L. Hammond <jhammond@whamcloud.com>
Reviewed-by: Lai Siyao <lai.siyao@whamcloud.com>
Reviewed-by: Oleg Drokin <green@whamcloud.com>
lustre/mdt/mdt_lib.c
lustre/tests/.gitignore
lustre/tests/Makefile.am
lustre/tests/euid_access.c [new file with mode: 0644]
lustre/tests/sanity-sec.sh

index ce7778e..be2531a 100644 (file)
@@ -317,14 +317,12 @@ static int new_init_ucred(struct mdt_thread_info *info, ucred_init_type_t type,
        ucred->uc_gid = pud->pud_gid;
 
        if (nodemap && ucred->uc_o_uid == nodemap->nm_squash_uid) {
        ucred->uc_gid = pud->pud_gid;
 
        if (nodemap && ucred->uc_o_uid == nodemap->nm_squash_uid) {
-               ucred->uc_fsuid = nodemap->nm_squash_uid;
-               ucred->uc_fsgid = nodemap->nm_squash_gid;
                ucred->uc_cap = 0;
        } else {
                ucred->uc_cap = 0;
        } else {
-               ucred->uc_fsuid = pud->pud_fsuid;
-               ucred->uc_fsgid = pud->pud_fsgid;
                ucred->uc_cap = pud->pud_cap;
        }
                ucred->uc_cap = pud->pud_cap;
        }
+       ucred->uc_fsuid = pud->pud_fsuid;
+       ucred->uc_fsgid = pud->pud_fsgid;
 
        /* process root_squash here. */
        mdt_root_squash(info, peernid);
 
        /* process root_squash here. */
        mdt_root_squash(info, peernid);
@@ -473,8 +471,6 @@ static int old_init_ucred_common(struct mdt_thread_info *info,
                if (nodemap->nmf_deny_unknown)
                        RETURN(-EACCES);
 
                if (nodemap->nmf_deny_unknown)
                        RETURN(-EACCES);
 
-               uc->uc_fsuid = nodemap->nm_squash_uid;
-               uc->uc_fsgid = nodemap->nm_squash_gid;
                uc->uc_cap = 0;
                uc->uc_suppgids[0] = -1;
                uc->uc_suppgids[1] = -1;
                uc->uc_cap = 0;
                uc->uc_suppgids[0] = -1;
                uc->uc_suppgids[1] = -1;
index 7861a5b..061aefd 100644 (file)
@@ -99,3 +99,4 @@
 /write_time_limit
 /writemany
 /writeme
 /write_time_limit
 /writemany
 /writeme
+/euid_access
index 8fe8bf9..4890031 100644 (file)
@@ -67,7 +67,7 @@ THETESTS += opendirunlink opendevunlink unlinkmany checkstat
 THETESTS += statone runas openfile smalliomany
 THETESTS += small_write multiop ll_sparseness_verify
 THETESTS += ll_sparseness_write mrename ll_dirstripe_verify mkdirmany
 THETESTS += statone runas openfile smalliomany
 THETESTS += small_write multiop ll_sparseness_verify
 THETESTS += ll_sparseness_write mrename ll_dirstripe_verify mkdirmany
-THETESTS += openfilleddirunlink rename_many memhog
+THETESTS += openfilleddirunlink rename_many memhog euid_access
 THETESTS += mmap_sanity writemany reads flocks_test flock_deadlock
 THETESTS += write_time_limit rwv lgetxattr_size_check checkfiemap
 THETESTS += listxattr_size_check check_fhandle_syscalls badarea_io
 THETESTS += mmap_sanity writemany reads flocks_test flock_deadlock
 THETESTS += write_time_limit rwv lgetxattr_size_check checkfiemap
 THETESTS += listxattr_size_check check_fhandle_syscalls badarea_io
diff --git a/lustre/tests/euid_access.c b/lustre/tests/euid_access.c
new file mode 100644 (file)
index 0000000..1e20b74
--- /dev/null
@@ -0,0 +1,211 @@
+/*
+ * GPL HEADER START
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 only,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License version 2 for more details (a copy is included
+ * in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; If not, see
+ * http://www.gnu.org/licenses/gpl-2.0.html
+ *
+ * GPL HEADER END
+ */
+/*
+ * Copyright (c) 2020, Whamcloud.
+ */
+/*
+ * This file is part of Lustre, http://www.lustre.org/
+ */
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <pwd.h>
+#include <grp.h>
+#include <fcntl.h>
+
+void print_groups(int num_groups, gid_t *groups)
+{
+       int i;
+
+       fprintf(stdout, "\tGroups ");
+       for (i = 0; i < num_groups; i++) {
+               struct group *gr = getgrgid(groups[i]);
+
+               if (gr == NULL)
+                       perror("getgrgid");
+               fprintf(stdout, "%6d - %s, ", gr->gr_gid, gr->gr_name);
+       }
+       fprintf(stdout, "\n");
+}
+
+char usage[] =
+"Usage: %s <user to switch euid to> <path to file to access>\n";
+
+int main(int argc, char **argv)
+{
+       char *user_to_euid;
+       char *filename;
+       struct passwd *user_pwd;
+       char *user_name;
+       int num_groups = 0;
+       gid_t *groups;
+       struct passwd *switch_pwd;
+       uid_t switch_uid, switch_gid;
+       int switch_num_groups = 0;
+       gid_t *switch_groups;
+       int final_num_groups = 0;
+       gid_t *final_groups;
+       int fd = 0;
+       int save_errno;
+       int rc;
+
+       if (argc != 3) {
+               fprintf(stderr, usage, argv[0]);
+               exit(1);
+       }
+
+       user_to_euid = argv[1];
+       filename = argv[2];
+
+       user_pwd = getpwuid(getuid());
+       if (!user_pwd) {
+               if (errno) {
+                       save_errno = errno;
+                       perror("getpwuid");
+               } else {
+                       save_errno = 1;
+                       fprintf(stderr, "Cannot find user in passwd db\n");
+               }
+               exit(save_errno);
+       }
+       user_name = user_pwd->pw_name;
+
+       fprintf(stdout, "Initially %s ruid:rgid %d:%d, euid:egid %d:%d\n",
+               user_name, getuid(), getgid(), geteuid(), getegid());
+
+       num_groups = getgroups(0, NULL);
+       if (num_groups) {
+               groups = malloc(sizeof(gid_t) * num_groups);
+               if (!groups) {
+                       save_errno = errno;
+                       perror("groups");
+                       exit(save_errno);
+               }
+               rc = getgrouplist(user_name, getgid(), groups, &num_groups);
+               if (rc == -1) {
+                       save_errno = errno;
+                       free(groups);
+                       perror("groups list");
+                       exit(save_errno);
+               }
+               print_groups(num_groups, groups);
+               free(groups);
+       }
+
+       /* lookup information about switch_user provided */
+       switch_pwd = getpwnam(user_to_euid);
+       if (!switch_pwd) {
+               if (errno) {
+                       save_errno = errno;
+                       perror("getpwnam");
+               } else {
+                       save_errno = 1;
+                       fprintf(stderr, "Cannot find user %s in passwd db\n",
+                               user_to_euid);
+               }
+               exit(save_errno);
+       }
+       switch_uid = switch_pwd->pw_uid;
+       switch_gid = switch_pwd->pw_gid;
+
+       fprintf(stdout, "To switch to effective %s uid:gid %d:%d\n",
+               user_to_euid, switch_uid, switch_gid);
+
+       rc = setegid(switch_gid);
+       if (rc == -1) {
+               save_errno = errno;
+               perror("setegid");
+               exit(save_errno);
+       }
+
+       getgrouplist(user_to_euid, switch_gid, NULL, &switch_num_groups);
+       if (switch_num_groups) {
+               switch_groups = malloc(sizeof(gid_t) * switch_num_groups);
+               if (!switch_groups) {
+                       save_errno = errno;
+                       perror("switch_groups");
+                       exit(save_errno);
+               }
+               rc = getgrouplist(user_to_euid, switch_gid,
+                                 switch_groups, &switch_num_groups);
+               if (rc == -1) {
+                       save_errno = errno;
+                       free(switch_groups);
+                       perror("switch_groups list");
+                       exit(save_errno);
+               }
+               print_groups(switch_num_groups, switch_groups);
+               rc = setgroups(switch_num_groups, switch_groups);
+               save_errno = errno;
+               free(switch_groups);
+               if (rc == -1) {
+                       perror("setgroups");
+                       exit(save_errno);
+               }
+       }
+
+       rc = seteuid(switch_uid);
+       if (rc == -1) {
+               save_errno = errno;
+               perror("seteuid");
+               exit(save_errno);
+       }
+
+       fprintf(stdout, "Now %s ruid:rgid %d:%d, euid:egid %d:%d\n",
+               user_name, getuid(), getgid(), geteuid(), getegid());
+
+       final_num_groups = getgroups(0, NULL);
+       final_groups = malloc(sizeof(gid_t) * final_num_groups);
+       if (!final_groups) {
+               save_errno = errno;
+               perror("final_groups");
+               exit(save_errno);
+       }
+       rc = getgroups(final_num_groups, final_groups);
+       if (rc == -1) {
+               save_errno = errno;
+               free(final_groups);
+               perror("final_groups list");
+               exit(save_errno);
+       }
+       print_groups(final_num_groups, final_groups);
+       free(final_groups);
+
+       fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC | O_APPEND, 0666);
+       if (fd == -1) {
+               save_errno = errno;
+               perror("open");
+               exit(save_errno);
+       }
+       rc = write(fd, "test", 5);
+       if (rc == -1) {
+               save_errno = errno;
+               perror("write");
+               exit(save_errno);
+       }
+       close(fd);
+       fprintf(stdout, "File %s successfully written\n", filename);
+
+       exit(0);
+}
+
index e818a68..449b54e 100755 (executable)
@@ -2692,7 +2692,7 @@ cleanup_for_enc_tests() {
 
 cleanup_nodemap_after_enc_tests() {
        do_facet mgs $LCTL nodemap_modify --name default \
 
 cleanup_nodemap_after_enc_tests() {
        do_facet mgs $LCTL nodemap_modify --name default \
-               --property forbid_encryption --value 1
+               --property forbid_encryption --value 0
        wait_nm_sync default forbid_encryption
        do_facet mgs $LCTL nodemap_activate 0
        wait_nm_sync active
        wait_nm_sync default forbid_encryption
        do_facet mgs $LCTL nodemap_activate 0
        wait_nm_sync active
@@ -4052,6 +4052,97 @@ test_54() {
 }
 run_test 54 "Encryption policies with fscrypt"
 
 }
 run_test 54 "Encryption policies with fscrypt"
 
+cleanup_55() {
+       # unmount client
+       if is_mounted $MOUNT; then
+               umount_client $MOUNT || error "umount $MOUNT failed"
+       fi
+
+       do_facet mgs $LCTL nodemap_del c0
+       do_facet mgs $LCTL nodemap_modify --name default \
+                --property admin --value 0
+       do_facet mgs $LCTL nodemap_modify --name default \
+                --property trusted --value 0
+       wait_nm_sync default admin_nodemap
+       wait_nm_sync default trusted_nodemap
+
+       do_facet mgs $LCTL nodemap_activate 0
+       wait_nm_sync active 0
+
+       if $SHARED_KEY; then
+               export SK_UNIQUE_NM=false
+       fi
+
+       # remount client
+       mount_client $MOUNT ${MOUNT_OPTS} || error "remount failed"
+       if [ "$MOUNT_2" ]; then
+               mount_client $MOUNT2 ${MOUNT_OPTS} || error "remount failed"
+       fi
+}
+
+test_55() {
+       local client_ip
+       local client_nid
+
+       mkdir -p $DIR/$tdir/$USER0/testdir_groups
+       chown root:$ID0 $DIR/$tdir/$USER0
+       chmod 770 $DIR/$tdir/$USER0
+       chmod g+s $DIR/$tdir/$USER0
+       chown $ID0:$ID0 $DIR/$tdir/$USER0/testdir_groups
+       chmod 770 $DIR/$tdir/$USER0/testdir_groups
+       chmod g+s $DIR/$tdir/$USER0/testdir_groups
+
+       # unmount client completely
+       umount_client $MOUNT || error "umount $MOUNT failed"
+       if is_mounted $MOUNT2; then
+               umount_client $MOUNT2 || error "umount $MOUNT2 failed"
+       fi
+
+       do_nodes $(comma_list $(all_mdts_nodes)) \
+               $LCTL set_param mdt.*.identity_upcall=NONE
+
+       stack_trap cleanup_55 EXIT
+
+       do_facet mgs $LCTL nodemap_activate 1
+       wait_nm_sync active
+
+       do_facet mgs $LCTL nodemap_del c0 || true
+       wait_nm_sync c0 id ''
+
+       do_facet mgs $LCTL nodemap_modify --name default \
+               --property admin --value 1
+       do_facet mgs $LCTL nodemap_modify --name default \
+               --property trusted --value 1
+       wait_nm_sync default admin_nodemap
+       wait_nm_sync default trusted_nodemap
+
+       client_ip=$(host_nids_address $HOSTNAME $NETTYPE)
+       client_nid=$(h2nettype $client_ip)
+       do_facet mgs $LCTL nodemap_add c0
+       do_facet mgs $LCTL nodemap_add_range \
+                --name c0 --range $client_nid
+       do_facet mgs $LCTL nodemap_modify --name c0 \
+                --property admin --value 0
+       do_facet mgs $LCTL nodemap_modify --name c0 \
+                --property trusted --value 1
+       wait_nm_sync c0 admin_nodemap
+       wait_nm_sync c0 trusted_nodemap
+
+       if $SHARED_KEY; then
+               export SK_UNIQUE_NM=true
+               # set some generic fileset to trigger SSK code
+               export FILESET=/
+       fi
+
+       # remount client to take nodemap into account
+       zconf_mount_clients $HOSTNAME $MOUNT $MOUNT_OPTS ||
+               error "remount failed"
+       unset FILESET
+
+       euid_access $USER0 $DIR/$tdir/$USER0/testdir_groups/file
+}
+run_test 55 "access with seteuid"
+
 log "cleanup: ======================================================"
 
 sec_unsetup() {
 log "cleanup: ======================================================"
 
 sec_unsetup() {