From 355787745f21b22bb36210bb1c8e41fb34e7b665 Mon Sep 17 00:00:00 2001 From: Sebastien Buisson Date: Fri, 13 Nov 2020 19:36:14 +0900 Subject: [PATCH] LU-14121 nodemap: do not force fsuid/fsgid squashing 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 Change-Id: Iecaecac5054b105cd42206b0a9a3868cde0269b4 Reviewed-on: https://review.whamcloud.com/40645 Tested-by: jenkins Tested-by: Maloo Reviewed-by: John L. Hammond Reviewed-by: Lai Siyao Reviewed-by: Oleg Drokin --- lustre/mdt/mdt_lib.c | 8 +- lustre/tests/.gitignore | 1 + lustre/tests/Makefile.am | 2 +- lustre/tests/euid_access.c | 211 +++++++++++++++++++++++++++++++++++++++++++++ lustre/tests/sanity-sec.sh | 93 +++++++++++++++++++- 5 files changed, 307 insertions(+), 8 deletions(-) create mode 100644 lustre/tests/euid_access.c diff --git a/lustre/mdt/mdt_lib.c b/lustre/mdt/mdt_lib.c index ce7778e..be2531a 100644 --- a/lustre/mdt/mdt_lib.c +++ b/lustre/mdt/mdt_lib.c @@ -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_fsuid = nodemap->nm_squash_uid; - ucred->uc_fsgid = nodemap->nm_squash_gid; 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_fsuid = pud->pud_fsuid; + ucred->uc_fsgid = pud->pud_fsgid; /* 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); - 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; diff --git a/lustre/tests/.gitignore b/lustre/tests/.gitignore index 7861a5b..061aefd 100644 --- a/lustre/tests/.gitignore +++ b/lustre/tests/.gitignore @@ -99,3 +99,4 @@ /write_time_limit /writemany /writeme +/euid_access diff --git a/lustre/tests/Makefile.am b/lustre/tests/Makefile.am index 8fe8bf9..4890031 100644 --- a/lustre/tests/Makefile.am +++ b/lustre/tests/Makefile.am @@ -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 += 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 diff --git a/lustre/tests/euid_access.c b/lustre/tests/euid_access.c new file mode 100644 index 0000000..1e20b74 --- /dev/null +++ b/lustre/tests/euid_access.c @@ -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 +#include +#include +#include +#include +#include +#include + +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 \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); +} + diff --git a/lustre/tests/sanity-sec.sh b/lustre/tests/sanity-sec.sh index e818a68..449b54e 100755 --- a/lustre/tests/sanity-sec.sh +++ b/lustre/tests/sanity-sec.sh @@ -2692,7 +2692,7 @@ cleanup_for_enc_tests() { 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 @@ -4052,6 +4052,97 @@ test_54() { } 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() { -- 1.8.3.1