From patchwork Tue Apr 12 11:33:42 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: "Yang Xu (Fujitsu)" X-Patchwork-Id: 12810597 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 52FD5C4167B for ; Tue, 12 Apr 2022 11:48:55 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1350926AbiDLLvC (ORCPT ); Tue, 12 Apr 2022 07:51:02 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:39078 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1355040AbiDLLtJ (ORCPT ); Tue, 12 Apr 2022 07:49:09 -0400 Received: from mail1.bemta32.messagelabs.com (mail1.bemta32.messagelabs.com [195.245.230.65]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id E89D06165; Tue, 12 Apr 2022 03:33:11 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=fujitsu.com; s=170520fj; t=1649759590; i=@fujitsu.com; bh=CIzjA74MEWi+h5owqklrQgjAz3Lbuv5+Ro+Bd5ui2SA=; h=From:To:CC:Subject:Date:Message-ID:MIME-Version:Content-Type; b=cWvc+RNJfuMp6pdMclfWbYhJQ038JvIhpg2CmbN1MPXalcwCxvtaZxW+0oZS65gVv 1EfU5Fv066cYhj05CeVs7vO1Hgyy4Py0yih1QKU2uqRFbIoUOX1sdb0rVwlSPzAtCO 5qNZwjRqgh6yOxb1+bMK5/S6ZcpFRc2mzTsB61NUHkSZYQW+FSpmoHdcFaIxLByClO Tl2tk1BaQV/iwQ+CZGxKaTPbXaG0Sz5wydWZ+a4evgcPJisNjL0/ISEUycducZD/rv caiDSm3T3mSgDhsgW0qcUsYlxE6KKnWFRc92yWmhZ6BEZ/lkDyqXdTF2Ql2+1AwOl5 ag9NEyIM8RHIQ== X-Brightmail-Tracker: H4sIAAAAAAAAA+NgFjrLIsWRWlGSWpSXmKPExsViZ8MxSTc1NDT JoHWGscXrw58YLbYcu8docfkJn8Xplr3sFnv2nmRxYPU4tUjCY9OqTjaPz5vkApijWDPzkvIr ElgzWlpOsRd8Yqt4veAJewPjB9YuRk4OIYEtjBL/1vJ1MXIB2QuYJObe3MEE4exhlLiz8ywzS BWbgKbEs84FYLaIgIvEt/1/2UBsZoEUiYbzTYwgtrBAhMT6HROZQGwWAVWJvsabYBt4BTwkdj X+AquREFCQmPLwPTNEXFDi5MwnLBBzJCQOvnjBDFGjKHGp4xtUfYXErFltTBC2msTVc5uYJzD yz0LSPgtJ+wJGplWMVklFmekZJbmJmTm6hgYGuoaGprpA0shYL7FKN1EvtVS3PLW4RNdQL7G8 WC+1uFivuDI3OSdFLy+1ZBMjMJRTihmqdzD+7/2pd4hRkoNJSZTXgCc0SYgvKT+lMiOxOCO+q DQntfgQowwHh5IEr1wwUE6wKDU9tSItMwcYVzBpCQ4eJRHe2ECgNG9xQWJucWY6ROoUo6KUOC 9nCFBCACSRUZoH1waL5UuMslLCvIwMDAxCPAWpRbmZJajyrxjFORiVhHn5QabwZOaVwE1/BbS YCWhx6LZAkMUliQgpqQamSQ9ebbJXSOKU6tnJozz7XUdI5vuEpbpdEx4s4Zt095Miu20a49Xp VdpezI96N/3MPGj5ziNJtK1BfH42w4vkx2rWTTeqcoV8zjHYHo24nfKwWuGcfl0Pb89nt4rZV WbGLIlHPlraLluTl3z80rfXm2+2Cb968fOYVeRXl7Dw///Evtx+Xle199qzyz99Dc6/71Sc/z cjeMaG8ktdMRtqOrx1cj6vZb9r86HCeaHN15iD5uJdZyzZlBrYEnWczW9N6Fq1vlFG5Xvrs8Z jsWufbGZvFBTcsnLXr6QP3pcsW5aI+J2we6S6nD+u3CD/0vTjkppr/bvDJ3U+T5t+vPJq4t9d ji63novsNL+QF7RdiaU4I9FQi7moOBEAFB/db2ADAAA= X-Env-Sender: xuyang2018.jy@fujitsu.com X-Msg-Ref: server-14.tower-587.messagelabs.com!1649759589!2976!1 X-Originating-IP: [62.60.8.146] X-SYMC-ESS-Client-Auth: outbound-route-from=pass X-StarScan-Received: X-StarScan-Version: 9.85.5; banners=-,-,- X-VirusChecked: Checked Received: (qmail 20749 invoked from network); 12 Apr 2022 10:33:09 -0000 Received: from unknown (HELO n03ukasimr02.n03.fujitsu.local) (62.60.8.146) by server-14.tower-587.messagelabs.com with ECDHE-RSA-AES256-GCM-SHA384 encrypted SMTP; 12 Apr 2022 10:33:09 -0000 Received: from n03ukasimr02.n03.fujitsu.local (localhost [127.0.0.1]) by n03ukasimr02.n03.fujitsu.local (Postfix) with ESMTP id 5CA13100445; Tue, 12 Apr 2022 11:33:09 +0100 (BST) Received: from R01UKEXCASM126.r01.fujitsu.local (unknown [10.183.43.178]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-SHA384 (256/256 bits)) (No client certificate requested) by n03ukasimr02.n03.fujitsu.local (Postfix) with ESMTPS id 4E120100331; Tue, 12 Apr 2022 11:33:09 +0100 (BST) Received: from localhost.localdomain (10.167.220.84) by R01UKEXCASM126.r01.fujitsu.local (10.183.43.178) with Microsoft SMTP Server (TLS) id 15.0.1497.32; Tue, 12 Apr 2022 11:33:03 +0100 From: Yang Xu To: , , CC: , , Yang Xu Subject: [PATCH v3 1/5] idmapped-mounts: Reset errno to zero after detect fs_allow_idmap Date: Tue, 12 Apr 2022 19:33:42 +0800 Message-ID: <1649763226-2329-1-git-send-email-xuyang2018.jy@fujitsu.com> X-Mailer: git-send-email 1.8.3.1 MIME-Version: 1.0 X-Originating-IP: [10.167.220.84] X-ClientProxiedBy: G08CNEXCHPEKD07.g08.fujitsu.local (10.167.33.80) To R01UKEXCASM126.r01.fujitsu.local (10.183.43.178) X-Virus-Scanned: ClamAV using ClamSMTP Precedence: bulk List-ID: X-Mailing-List: linux-fsdevel@vger.kernel.org If we run case on old kernel that doesn't support mount_setattr and then fail on our own function before call is_setgid/is_setuid function to reset errno, run_test will print "Function not implement" error. Signed-off-by: Yang Xu Acked-by: Christian Brauner (Microsoft) --- src/idmapped-mounts/idmapped-mounts.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/idmapped-mounts/idmapped-mounts.c b/src/idmapped-mounts/idmapped-mounts.c index 4cf6c3bb..8e6405c5 100644 --- a/src/idmapped-mounts/idmapped-mounts.c +++ b/src/idmapped-mounts/idmapped-mounts.c @@ -14070,6 +14070,8 @@ int main(int argc, char *argv[]) die("failed to open %s", t_mountpoint_scratch); t_fs_allow_idmap = fs_allow_idmap(); + /* don't copy ENOSYS errno to child process on older kernel */ + errno = 0; if (supported) { /* * Caller just wants to know whether the filesystem we're on From patchwork Tue Apr 12 11:33:43 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: "Yang Xu (Fujitsu)" X-Patchwork-Id: 12810594 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id D6BCFC4332F for ; Tue, 12 Apr 2022 11:48:41 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1348205AbiDLLuu (ORCPT ); Tue, 12 Apr 2022 07:50:50 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:39026 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1355049AbiDLLtJ (ORCPT ); Tue, 12 Apr 2022 07:49:09 -0400 Received: from mail1.bemta36.messagelabs.com (mail1.bemta36.messagelabs.com [85.158.142.113]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 714C16329; Tue, 12 Apr 2022 03:33:19 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=fujitsu.com; s=170520fj; t=1649759597; i=@fujitsu.com; bh=/6FqkHTAW3qkYDVGKL25u4oczxQc+1U7/FW1LcygMS4=; h=From:To:CC:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=kjTotdBltPESsEFbbqXPsAZSWlopwg88NLREXEwhN7vhReBgUffbzoBo+tpIdXlrc J8U7xKxPOkUZb5KDCSE0lajVb9itsRecUGdK3dZVjrchAVYiOcLM+ByGrUQH7lC38K yUEfzmBDjerdRcEdZR1zfjydZakwsbamtEojebrhRmNTLk8qcR91Hj9hargwbgSDZv UifCYGLB7DTc0MEgicsJTwqhACcQCUaRZDHET7Kb7ZS/BOPdIl2c/TbsMlBOjTCgUy JPyFz6qRTuyIIgXQXaw7O5YI3O0hUTS1X7ANhEfXAiW8n62Weg04MgnrIVUbPa36k7 JvBs+OjcWs1zw== X-Brightmail-Tracker: H4sIAAAAAAAAA+NgFrrDIsWRWlGSWpSXmKPExsViZ8MRopsbGpp kcHyqiMXrw58YLbYcu8docfkJn8Xplr3sFnv2nmRxYPU4tUjCY9OqTjaPz5vkApijWDPzkvIr Elgzzp5fw1JwP6bizaHTLA2ML3y6GLk4hAReM0p8vf+JBcLZwyjxd/VJpi5GTg42AU2JZ50Lm EFsEQEXiW/7/7KB2MwCORINu3+A1QgL+Eq8mtzGCGKzCKhK7Lk9B6ieg4NXwEPiTyMHSFhCQE FiysP3YGM4BTwlJh3ZxwJiCwGVzGhdDTaSV0BQ4uTMJywQ4yUkDr54wQzRqyhxqeMbI4RdITF rVhvTBEb+WUhaZiFpWcDItIrRLqkoMz2jJDcxM0fX0MBA19DQVNfMQtfQxFgvsUo3US+1VDc5 Na+kKBEorZdYXqyXWlysV1yZm5yTopeXWrKJERjQKcUu/TsY7/f91DvEKMnBpCTKa8ATmiTEl 5SfUpmRWJwRX1Sak1p8iFGGg0NJglcuGCgnWJSanlqRlpkDjC6YtAQHj5IIb2wgUJq3uCAxtz gzHSJ1ilGX4++nv3uZhVjy8vNSpcR5OUOAigRAijJK8+BGwCL9EqOslDAvIwMDgxBPQWpRbmY JqvwrRnEORiVhXn6QKTyZeSVwm14BHcEEdETotkCQI0oSEVJSDUzph566dRncLxJZ8umDi2iz qdmU2jthtpJnUlSmiSrl/5P7935hyXOtObJWkxVtFh9Nv7/256wlwZFFNc/8/z0tXSO9OOhod urP+6dTNEU+Nf+95N06WZjjyNIVGY7PDVYdvbtHJl+0/47KYfcHW4v710/ZcnGCzpN3x1N/+v MqVta6prYzLpZR5TTPkZw91b7AX+GpwJELRnt5jQ4zfGF+37Lu4s5XM+xPTeqqiow3ObIwKZx 5K6PnofU/nUp0qjbfbkwVFHC9tWnuTtH4q92vsgze72KSip2q1jtHVcHkZPvUu5zzgh5u799+ hVstawXDRSs3w/8zZj4T+b+t+9jHa4z2P78z2U7VyGBx8yhRYinOSDTUYi4qTgQA4y8UhG8DA AA= X-Env-Sender: xuyang2018.jy@fujitsu.com X-Msg-Ref: server-16.tower-545.messagelabs.com!1649759596!62404!1 X-Originating-IP: [62.60.8.84] X-SYMC-ESS-Client-Auth: outbound-route-from=pass X-StarScan-Received: X-StarScan-Version: 9.85.5; banners=-,-,- X-VirusChecked: Checked Received: (qmail 25341 invoked from network); 12 Apr 2022 10:33:17 -0000 Received: from unknown (HELO mailhost3.uk.fujitsu.com) (62.60.8.84) by server-16.tower-545.messagelabs.com with ECDHE-RSA-AES256-GCM-SHA384 encrypted SMTP; 12 Apr 2022 10:33:17 -0000 Received: from R01UKEXCASM126.r01.fujitsu.local ([10.183.43.178]) by mailhost3.uk.fujitsu.com (8.14.5/8.14.5) with ESMTP id 23CAXGW3000905 (version=TLSv1/SSLv3 cipher=AES256-SHA bits=256 verify=FAIL); Tue, 12 Apr 2022 11:33:16 +0100 Received: from localhost.localdomain (10.167.220.84) by R01UKEXCASM126.r01.fujitsu.local (10.183.43.178) with Microsoft SMTP Server (TLS) id 15.0.1497.32; Tue, 12 Apr 2022 11:33:13 +0100 From: Yang Xu To: , , CC: , , Yang Xu Subject: [PATCH v3 2/5] idmapped-mounts: Add mknodat operation in setgid test Date: Tue, 12 Apr 2022 19:33:43 +0800 Message-ID: <1649763226-2329-2-git-send-email-xuyang2018.jy@fujitsu.com> X-Mailer: git-send-email 1.8.3.1 In-Reply-To: <1649763226-2329-1-git-send-email-xuyang2018.jy@fujitsu.com> References: <1649763226-2329-1-git-send-email-xuyang2018.jy@fujitsu.com> MIME-Version: 1.0 X-Originating-IP: [10.167.220.84] X-ClientProxiedBy: G08CNEXCHPEKD07.g08.fujitsu.local (10.167.33.80) To R01UKEXCASM126.r01.fujitsu.local (10.183.43.178) Precedence: bulk List-ID: X-Mailing-List: linux-fsdevel@vger.kernel.org Since mknodat can create file, we should also check whether strip S_ISGID. Also add new helper caps_down_fsetid to drop CAP_FSETID because strip S_ISGID depend on this cap and keep other cap(ie CAP_MKNOD) because create character device needs it when using mknod. Only test mknodat with character device in setgid_create function and the another two functions test mknodat with whiteout device. Since kernel commit a3c751a50 ("vfs: allow unprivileged whiteout creation") in v5.8-rc1, we can create whiteout device in userns test. Since kernel 5.12, mount_setattr and MOUNT_ATTR_IDMAP was supported, we don't need to detect kernel whether allow unprivileged whiteout creation. Using fs_allow_idmap as a proxy is safe. Tested-by: Christian Brauner (Microsoft) Reviewed-by: Christian Brauner (Microsoft) Signed-off-by: Yang Xu Reviewed-by: Christian Brauner (Microsoft) --- src/idmapped-mounts/idmapped-mounts.c | 219 +++++++++++++++++++++++++- 1 file changed, 213 insertions(+), 6 deletions(-) diff --git a/src/idmapped-mounts/idmapped-mounts.c b/src/idmapped-mounts/idmapped-mounts.c index 8e6405c5..617f56e0 100644 --- a/src/idmapped-mounts/idmapped-mounts.c +++ b/src/idmapped-mounts/idmapped-mounts.c @@ -241,6 +241,34 @@ static inline bool caps_supported(void) return ret; } +static int caps_down_fsetid(void) +{ + bool fret = false; +#ifdef HAVE_SYS_CAPABILITY_H + cap_t caps = NULL; + cap_value_t cap = CAP_FSETID; + int ret = -1; + + caps = cap_get_proc(); + if (!caps) + goto out; + + ret = cap_set_flag(caps, CAP_EFFECTIVE, 1, &cap, 0); + if (ret) + goto out; + + ret = cap_set_proc(caps); + if (ret) + goto out; + + fret = true; + +out: + cap_free(caps); +#endif + return fret; +} + /* caps_down - lower all effective caps */ static int caps_down(void) { @@ -7805,8 +7833,8 @@ out_unmap: #endif /* HAVE_LIBURING_H */ /* The following tests are concerned with setgid inheritance. These can be - * filesystem type specific. For xfs, if a new file or directory is created - * within a setgid directory and irix_sgid_inhiert is set then inherit the + * filesystem type specific. For xfs, if a new file or directory or node is + * created within a setgid directory and irix_sgid_inhiert is set then inherit the * setgid bit if the caller is in the group of the directory. */ static int setgid_create(void) @@ -7863,18 +7891,44 @@ static int setgid_create(void) if (!is_setgid(t_dir1_fd, DIR1, 0)) die("failure: is_setgid"); + /* create a special file via mknodat() vfs_create */ + if (mknodat(t_dir1_fd, FILE2, S_IFREG | S_ISGID | S_IXGRP, 0)) + die("failure: mknodat"); + + if (!is_setgid(t_dir1_fd, FILE2, 0)) + die("failure: is_setgid"); + + /* create a character device via mknodat() vfs_mknod */ + if (mknodat(t_dir1_fd, CHRDEV1, S_IFCHR | S_ISGID | S_IXGRP, makedev(5, 1))) + die("failure: mknodat"); + + if (!is_setgid(t_dir1_fd, CHRDEV1, 0)) + die("failure: is_setgid"); + if (!expected_uid_gid(t_dir1_fd, FILE1, 0, 0, 0)) die("failure: check ownership"); if (!expected_uid_gid(t_dir1_fd, DIR1, 0, 0, 0)) die("failure: check ownership"); + if (!expected_uid_gid(t_dir1_fd, FILE2, 0, 0, 0)) + die("failure: check ownership"); + + if (!expected_uid_gid(t_dir1_fd, CHRDEV1, 0, 0, 0)) + die("failure: check ownership"); + if (unlinkat(t_dir1_fd, FILE1, 0)) die("failure: delete"); if (unlinkat(t_dir1_fd, DIR1, AT_REMOVEDIR)) die("failure: delete"); + if (unlinkat(t_dir1_fd, FILE2, 0)) + die("failure: delete"); + + if (unlinkat(t_dir1_fd, CHRDEV1, 0)) + die("failure: delete"); + exit(EXIT_SUCCESS); } if (wait_for_pid(pid)) @@ -7889,8 +7943,8 @@ static int setgid_create(void) if (!switch_ids(0, 10000)) die("failure: switch_ids"); - if (!caps_down()) - die("failure: caps_down"); + if (!caps_down_fsetid()) + die("failure: caps_down_fsetid"); /* create regular file via open() */ file1_fd = openat(t_dir1_fd, FILE1, O_CREAT | O_EXCL | O_CLOEXEC, S_IXGRP | S_ISGID); @@ -7917,6 +7971,19 @@ static int setgid_create(void) die("failure: is_setgid"); } + /* create a special file via mknodat() vfs_create */ + if (mknodat(t_dir1_fd, FILE2, S_IFREG | S_ISGID | S_IXGRP, 0)) + die("failure: mknodat"); + + if (is_setgid(t_dir1_fd, FILE2, 0)) + die("failure: is_setgid"); + + /* create a character device via mknodat() vfs_mknod */ + if (mknodat(t_dir1_fd, CHRDEV1, S_IFCHR | S_ISGID | S_IXGRP, makedev(5, 1))) + die("failure: mknodat"); + + if (is_setgid(t_dir1_fd, CHRDEV1, 0)) + die("failure: is_setgid"); /* * In setgid directories newly created files always inherit the * gid from the parent directory. Verify that the file is owned @@ -7933,6 +8000,24 @@ static int setgid_create(void) if (!expected_uid_gid(t_dir1_fd, DIR1, 0, 0, 0)) die("failure: check ownership"); + if (!expected_uid_gid(t_dir1_fd, FILE2, 0, 0, 0)) + die("failure: check ownership"); + + if (!expected_uid_gid(t_dir1_fd, CHRDEV1, 0, 0, 0)) + die("failure: check ownership"); + + if (unlinkat(t_dir1_fd, FILE1, 0)) + die("failure: delete"); + + if (unlinkat(t_dir1_fd, DIR1, AT_REMOVEDIR)) + die("failure: delete"); + + if (unlinkat(t_dir1_fd, FILE2, 0)) + die("failure: delete"); + + if (unlinkat(t_dir1_fd, CHRDEV1, 0)) + die("failure: delete"); + exit(EXIT_SUCCESS); } if (wait_for_pid(pid)) @@ -8035,6 +8120,20 @@ static int setgid_create_idmapped(void) die("failure: is_setgid"); } + /* create a special file via mknodat() vfs_create */ + if (mknodat(open_tree_fd, FILE2, S_IFREG | S_ISGID | S_IXGRP, 0)) + die("failure: mknodat"); + + if (is_setgid(open_tree_fd, FILE2, 0)) + die("failure: is_setgid"); + + /* create a whiteout device via mknodat() vfs_mknod */ + if (mknodat(open_tree_fd, CHRDEV1, S_IFCHR | S_ISGID | S_IXGRP, 0)) + die("failure: mknodat"); + + if (is_setgid(open_tree_fd, CHRDEV1, 0)) + die("failure: is_setgid"); + /* * In setgid directories newly created files always inherit the * gid from the parent directory. Verify that the file is owned @@ -8051,6 +8150,24 @@ static int setgid_create_idmapped(void) if (!expected_uid_gid(open_tree_fd, DIR1, 0, 10000, 10000)) die("failure: check ownership"); + if (!expected_uid_gid(open_tree_fd, FILE2, 0, 10000, 10000)) + die("failure: check ownership"); + + if (!expected_uid_gid(open_tree_fd, CHRDEV1, 0, 10000, 10000)) + die("failure: check ownership"); + + if (unlinkat(open_tree_fd, FILE1, 0)) + die("failure: delete"); + + if (unlinkat(open_tree_fd, DIR1, AT_REMOVEDIR)) + die("failure: delete"); + + if (unlinkat(open_tree_fd, FILE2, 0)) + die("failure: delete"); + + if (unlinkat(open_tree_fd, CHRDEV1, 0)) + die("failure: delete"); + exit(EXIT_SUCCESS); } if (wait_for_pid(pid)) @@ -8149,18 +8266,44 @@ static int setgid_create_idmapped_in_userns(void) if (!is_setgid(open_tree_fd, DIR1, 0)) die("failure: is_setgid"); + /* create a special file via mknodat() vfs_create */ + if (mknodat(open_tree_fd, FILE2, S_IFREG | S_ISGID | S_IXGRP, 0)) + die("failure: mknodat"); + + if (!is_setgid(open_tree_fd, FILE2, 0)) + die("failure: is_setgid"); + + /* create a whiteout device via mknodat() vfs_mknod */ + if (mknodat(open_tree_fd, CHRDEV1, S_IFCHR | S_ISGID | S_IXGRP, 0)) + die("failure: mknodat"); + + if (!is_setgid(open_tree_fd, CHRDEV1, 0)) + die("failure: is_setgid"); + if (!expected_uid_gid(open_tree_fd, FILE1, 0, 0, 0)) die("failure: check ownership"); if (!expected_uid_gid(open_tree_fd, DIR1, 0, 0, 0)) die("failure: check ownership"); + if (!expected_uid_gid(open_tree_fd, FILE2, 0, 0, 0)) + die("failure: check ownership"); + + if (!expected_uid_gid(open_tree_fd, CHRDEV1, 0, 0, 0)) + die("failure: check ownership"); + if (unlinkat(open_tree_fd, FILE1, 0)) die("failure: delete"); if (unlinkat(open_tree_fd, DIR1, AT_REMOVEDIR)) die("failure: delete"); + if (unlinkat(open_tree_fd, FILE2, 0)) + die("failure: delete"); + + if (unlinkat(open_tree_fd, CHRDEV1, 0)) + die("failure: delete"); + exit(EXIT_SUCCESS); } if (wait_for_pid(pid)) @@ -8190,9 +8333,12 @@ static int setgid_create_idmapped_in_userns(void) exit(EXIT_SUCCESS); } - if (!switch_userns(attr.userns_fd, 0, 0, true)) + if (!switch_userns(attr.userns_fd, 0, 0, false)) die("failure: switch_userns"); + if (!caps_down_fsetid()) + die("failure: caps_down_fsetid"); + /* create regular file via open() */ file1_fd = openat(open_tree_fd, FILE1, O_CREAT | O_EXCL | O_CLOEXEC, S_IXGRP | S_ISGID); if (file1_fd < 0) @@ -8218,6 +8364,20 @@ static int setgid_create_idmapped_in_userns(void) die("failure: is_setgid"); } + /* create a special file via mknodat() vfs_create */ + if (mknodat(open_tree_fd, FILE2, S_IFREG | S_ISGID | S_IXGRP, 0)) + die("failure: mknodat"); + + if (is_setgid(open_tree_fd, FILE2, 0)) + die("failure: is_setgid"); + + /* create a whiteout device via mknodat() vfs_mknod */ + if (mknodat(open_tree_fd, CHRDEV1, S_IFCHR | S_ISGID | S_IXGRP, 0)) + die("failure: mknodat"); + + if (is_setgid(open_tree_fd, CHRDEV1, 0)) + die("failure: is_setgid"); + /* * In setgid directories newly created files always inherit the * gid from the parent directory. Verify that the file is owned @@ -8234,12 +8394,24 @@ static int setgid_create_idmapped_in_userns(void) if (!expected_uid_gid(open_tree_fd, DIR1, 0, 0, 1000)) die("failure: check ownership"); + if (!expected_uid_gid(open_tree_fd, FILE2, 0, 0, 1000)) + die("failure: check ownership"); + + if (!expected_uid_gid(open_tree_fd, CHRDEV1, 0, 0, 1000)) + die("failure: check ownership"); + if (unlinkat(open_tree_fd, FILE1, 0)) die("failure: delete"); if (unlinkat(open_tree_fd, DIR1, AT_REMOVEDIR)) die("failure: delete"); + if (unlinkat(open_tree_fd, FILE2, 0)) + die("failure: delete"); + + if (unlinkat(open_tree_fd, CHRDEV1, 0)) + die("failure: delete"); + exit(EXIT_SUCCESS); } if (wait_for_pid(pid)) @@ -8266,9 +8438,12 @@ static int setgid_create_idmapped_in_userns(void) exit(EXIT_SUCCESS); } - if (!switch_userns(attr.userns_fd, 0, 1000, true)) + if (!switch_userns(attr.userns_fd, 0, 1000, false)) die("failure: switch_userns"); + if (!caps_down_fsetid()) + die("failure: caps_down_fsetid"); + /* create regular file via open() */ file1_fd = openat(open_tree_fd, FILE1, O_CREAT | O_EXCL | O_CLOEXEC, S_IXGRP | S_ISGID); if (file1_fd < 0) @@ -8295,12 +8470,44 @@ static int setgid_create_idmapped_in_userns(void) die("failure: is_setgid"); } + /* create a special file via mknodat() vfs_create */ + if (mknodat(open_tree_fd, FILE2, S_IFREG | S_ISGID | S_IXGRP, 0)) + die("failure: mknodat"); + + if (is_setgid(open_tree_fd, FILE2, 0)) + die("failure: is_setgid"); + + /* create a whiteout device via mknodat() vfs_mknod */ + if (mknodat(open_tree_fd, CHRDEV1, S_IFCHR | S_ISGID | S_IXGRP, 0)) + die("failure: mknodat"); + + if (is_setgid(open_tree_fd, CHRDEV1, 0)) + die("failure: is_setgid"); + if (!expected_uid_gid(open_tree_fd, FILE1, 0, 0, 0)) die("failure: check ownership"); if (!expected_uid_gid(open_tree_fd, DIR1, 0, 0, 0)) die("failure: check ownership"); + if (!expected_uid_gid(open_tree_fd, FILE2, 0, 0, 0)) + die("failure: check ownership"); + + if (!expected_uid_gid(open_tree_fd, CHRDEV1, 0, 0, 0)) + die("failure: check ownership"); + + if (unlinkat(open_tree_fd, FILE1, 0)) + die("failure: delete"); + + if (unlinkat(open_tree_fd, DIR1, AT_REMOVEDIR)) + die("failure: delete"); + + if (unlinkat(open_tree_fd, FILE2, 0)) + die("failure: delete"); + + if (unlinkat(open_tree_fd, CHRDEV1, 0)) + die("failure: delete"); + exit(EXIT_SUCCESS); } if (wait_for_pid(pid)) From patchwork Tue Apr 12 11:33:44 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: "Yang Xu (Fujitsu)" X-Patchwork-Id: 12810595 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id EA773C433EF for ; Tue, 12 Apr 2022 11:48:41 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1348608AbiDLLuy (ORCPT ); Tue, 12 Apr 2022 07:50:54 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:37188 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1355043AbiDLLtJ (ORCPT ); Tue, 12 Apr 2022 07:49:09 -0400 Received: from mail3.bemta32.messagelabs.com (mail3.bemta32.messagelabs.com [195.245.230.18]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 270B965D3; Tue, 12 Apr 2022 03:33:49 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=fujitsu.com; s=170520fj; t=1649759627; i=@fujitsu.com; bh=rsWNElBYKxD76xRqoti8fLFU5SgVLPmvtambBsME55w=; h=From:To:CC:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=BhY7NDGBveU1jolOKuM2jx1jPay2HjjKLUAK5D9g7XHiLlr+IHWX/m8d7dTNVcMvD tCgvgPHkXMjDNg8I883nQLusvblMh7zOkJ0hWs32ZTvP6K/uA1i0eGDkCCWPwHk3Y1 D/EnIeAVpCBbkNuOjAVKEG7rhA7919nbYX0P/5QeS9fEfMCNXYNc8eluy9lwqFenX1 VDEFrzhHcUf2vopIw89UHqjUzGBx6zA7Fh0DL0kKJLo+UNkZooGAUiVvkBJCU49Tr4 uUDclhPut9tJk2gUP0DdM8GL0EihoJ/LwF0rDD+1TUKUfPqqoggOIFbpCZd6zY24Fr N4eJnlAJYSFvA== X-Brightmail-Tracker: H4sIAAAAAAAAA+NgFnrCIsWRWlGSWpSXmKPExsViZ8ORqNsdGpp kcLmDzeL14U+MFluO3WO0uPyEz+J0y152iz17T7I4sHqcWiThsWlVJ5vH501yAcxRrJl5SfkV CawZb68KFXx1qFi18ARTA+MK0y5GLg4hgS2MEksP/2ODcBYwSSy4uoERwtnDKNHTfpe1i5GTg 01AU+JZ5wJmEFtEwEXi2/6/bCA2s0CKRMP5JkYQW1ggUmLN8+dg9SwCqhIrZ/4Fq+cV8JDY2t fABGJLCChITHn4HizOKeApMenIPhYQWwioZkbrajaIekGJkzOfsEDMl5A4+OIFM0SvosSljm+ MEHaFxKxZbVAz1SSuntvEPIFRcBaS9llI2hcwMq1itEwqykzPKMlNzMzRNTQw0DU0NNU11DW1 1Eus0k3USy3VLU8tLtE11EssL9ZLLS7WK67MTc5J0ctLLdnECAz8lGJG6x2MHX0/9Q4xSnIwK YnyGvCEJgnxJeWnVGYkFmfEF5XmpBYfYpTh4FCS4JULBsoJFqWmp1akZeYAoxAmLcHBoyTCGx sIlOYtLkjMLc5Mh0idYlSUEudtDAFKCIAkMkrz4NpgkX+JUVZKmJeRgYFBiKcgtSg3swRV/hW jOAejkjAvP8gUnsy8Erjpr4AWMwEtDt0WCLK4JBEhJdXAxN0/6Y/e6wkctif5FzeZ3K9XPyxR ctb+jP+yiueM8y2PHWPl1DypvVRupdHchGOBV85scVr3SkI17ubtPfsr+LkzM++0sR2fcrVz+ dpjPGdvntjO0mx16tU96ycvtjvZyLNoKDCqNp9qLPjpvFlumurBos4fm/9uzPYNK5FUe7h/AY +Fov33if2mKk9Dk0uYZTUzL6rwJiX6yS59FfqjNMetcPuz0jeHL7DlLXw0IW7hkerFv34fai6 zvhk9c4KN5CluOZf+Ws7XW1s/yQVVXrCSWsrzwyRuz3JJL6ujzUEKG+3Dg3SjU2ft5/vzeOlt 9YfKV/qldvF/YrDPeiv8w2nT8i7T92/EH5nrKTdtV2Ipzkg01GIuKk4EAJlAiqV3AwAA X-Env-Sender: xuyang2018.jy@fujitsu.com X-Msg-Ref: server-14.tower-585.messagelabs.com!1649759626!109095!1 X-Originating-IP: [62.60.8.97] X-SYMC-ESS-Client-Auth: outbound-route-from=pass X-StarScan-Received: X-StarScan-Version: 9.85.5; banners=-,-,- X-VirusChecked: Checked Received: (qmail 25695 invoked from network); 12 Apr 2022 10:33:47 -0000 Received: from unknown (HELO n03ukasimr01.n03.fujitsu.local) (62.60.8.97) by server-14.tower-585.messagelabs.com with ECDHE-RSA-AES256-GCM-SHA384 encrypted SMTP; 12 Apr 2022 10:33:47 -0000 Received: from n03ukasimr01.n03.fujitsu.local (localhost [127.0.0.1]) by n03ukasimr01.n03.fujitsu.local (Postfix) with ESMTP id 504E3100197; Tue, 12 Apr 2022 11:33:46 +0100 (BST) Received: from R01UKEXCASM126.r01.fujitsu.local (unknown [10.183.43.178]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-SHA384 (256/256 bits)) (No client certificate requested) by n03ukasimr01.n03.fujitsu.local (Postfix) with ESMTPS id 0FA2B100184; Tue, 12 Apr 2022 11:33:46 +0100 (BST) Received: from localhost.localdomain (10.167.220.84) by R01UKEXCASM126.r01.fujitsu.local (10.183.43.178) with Microsoft SMTP Server (TLS) id 15.0.1497.32; Tue, 12 Apr 2022 11:33:24 +0100 From: Yang Xu To: , , CC: , , Yang Xu Subject: [PATCH v3 3/5] idmapped-mounts: Add open with O_TMPFILE operation in setgid test Date: Tue, 12 Apr 2022 19:33:44 +0800 Message-ID: <1649763226-2329-3-git-send-email-xuyang2018.jy@fujitsu.com> X-Mailer: git-send-email 1.8.3.1 In-Reply-To: <1649763226-2329-1-git-send-email-xuyang2018.jy@fujitsu.com> References: <1649763226-2329-1-git-send-email-xuyang2018.jy@fujitsu.com> MIME-Version: 1.0 X-Originating-IP: [10.167.220.84] X-ClientProxiedBy: G08CNEXCHPEKD07.g08.fujitsu.local (10.167.33.80) To R01UKEXCASM126.r01.fujitsu.local (10.183.43.178) X-Virus-Scanned: ClamAV using ClamSMTP Precedence: bulk List-ID: X-Mailing-List: linux-fsdevel@vger.kernel.org Since we can create temp file by using O_TMPFILE flag and filesystem driver also has this api, we should also check this operation whether strip S_ISGID. Reviewed-by: Christian Brauner (Microsoft) Signed-off-by: Yang Xu --- src/idmapped-mounts/idmapped-mounts.c | 148 ++++++++++++++++++++++++++ 1 file changed, 148 insertions(+) diff --git a/src/idmapped-mounts/idmapped-mounts.c b/src/idmapped-mounts/idmapped-mounts.c index 617f56e0..02f91558 100644 --- a/src/idmapped-mounts/idmapped-mounts.c +++ b/src/idmapped-mounts/idmapped-mounts.c @@ -51,6 +51,7 @@ #define FILE1_RENAME "file1_rename" #define FILE2 "file2" #define FILE2_RENAME "file2_rename" +#define FILE3 "file3" #define DIR1 "dir1" #define DIR2 "dir2" #define DIR3 "dir3" @@ -337,6 +338,24 @@ out: return fret; } +static bool openat_tmpfile_supported(int dirfd) +{ + int fd = -1; + + fd = openat(dirfd, ".", O_TMPFILE | O_RDWR, S_IXGRP | S_ISGID); + if (fd == -1) { + if (errno == ENOTSUP) + return false; + else + return log_errno(false, "failure: create"); + } + + if (close(fd)) + log_stderr("failure: close"); + + return true; +} + /* __expected_uid_gid - check whether file is owned by the provided uid and gid */ static bool __expected_uid_gid(int dfd, const char *path, int flags, uid_t expected_uid, gid_t expected_gid, bool log) @@ -7841,7 +7860,10 @@ static int setgid_create(void) { int fret = -1; int file1_fd = -EBADF; + int tmpfile_fd = -EBADF; pid_t pid; + bool supported = false; + char path[PATH_MAX]; if (!caps_supported()) return 0; @@ -7866,6 +7888,8 @@ static int setgid_create(void) goto out; } + supported = openat_tmpfile_supported(t_dir1_fd); + pid = fork(); if (pid < 0) { log_stderr("failure: fork"); @@ -7929,6 +7953,25 @@ static int setgid_create(void) if (unlinkat(t_dir1_fd, CHRDEV1, 0)) die("failure: delete"); + /* create tmpfile via filesystem tmpfile api */ + if (supported) { + tmpfile_fd = openat(t_dir1_fd, ".", O_TMPFILE | O_RDWR, S_IXGRP | S_ISGID); + if (tmpfile_fd < 0) + die("failure: create"); + /* link the temporary file into the filesystem, making it permanent */ + snprintf(path, PATH_MAX, "/proc/self/fd/%d", tmpfile_fd); + if (linkat(AT_FDCWD, path, t_dir1_fd, FILE3, AT_SYMLINK_FOLLOW)) + die("failure: linkat"); + if (close(tmpfile_fd)) + die("failure: close"); + if (!is_setgid(t_dir1_fd, FILE3, 0)) + die("failure: is_setgid"); + if (!expected_uid_gid(t_dir1_fd, FILE3, 0, 0, 0)) + die("failure: check ownership"); + if (unlinkat(t_dir1_fd, FILE3, 0)) + die("failure: delete"); + } + exit(EXIT_SUCCESS); } if (wait_for_pid(pid)) @@ -8018,6 +8061,25 @@ static int setgid_create(void) if (unlinkat(t_dir1_fd, CHRDEV1, 0)) die("failure: delete"); + /* create tmpfile via filesystem tmpfile api */ + if (supported) { + tmpfile_fd = openat(t_dir1_fd, ".", O_TMPFILE | O_RDWR, S_IXGRP | S_ISGID); + if (tmpfile_fd < 0) + die("failure: create"); + /* link the temporary file into the filesystem, making it permanent */ + snprintf(path, PATH_MAX, "/proc/self/fd/%d", tmpfile_fd); + if (linkat(AT_FDCWD, path, t_dir1_fd, FILE3, AT_SYMLINK_FOLLOW)) + die("failure: linkat"); + if (close(tmpfile_fd)) + die("failure: close"); + if (is_setgid(t_dir1_fd, FILE3, 0)) + die("failure: is_setgid"); + if (!expected_uid_gid(t_dir1_fd, FILE3, 0, 0, 0)) + die("failure: check ownership"); + if (unlinkat(t_dir1_fd, FILE3, 0)) + die("failure: delete"); + } + exit(EXIT_SUCCESS); } if (wait_for_pid(pid)) @@ -8039,6 +8101,9 @@ static int setgid_create_idmapped(void) .attr_set = MOUNT_ATTR_IDMAP, }; pid_t pid; + int tmpfile_fd = -EBADF; + bool supported = false; + char path[PATH_MAX]; if (!caps_supported()) return 0; @@ -8086,6 +8151,8 @@ static int setgid_create_idmapped(void) goto out; } + supported = openat_tmpfile_supported(open_tree_fd); + pid = fork(); if (pid < 0) { log_stderr("failure: fork"); @@ -8168,6 +8235,25 @@ static int setgid_create_idmapped(void) if (unlinkat(open_tree_fd, CHRDEV1, 0)) die("failure: delete"); + /* create tmpfile via filesystem tmpfile api */ + if (supported) { + tmpfile_fd = openat(open_tree_fd, ".", O_TMPFILE | O_RDWR, S_IXGRP | S_ISGID); + if (tmpfile_fd < 0) + die("failure: create"); + /* link the temporary file into the filesystem, making it permanent */ + snprintf(path, PATH_MAX, "/proc/self/fd/%d", tmpfile_fd); + if (linkat(AT_FDCWD, path, open_tree_fd, FILE3, AT_SYMLINK_FOLLOW)) + die("failure: linkat"); + if (close(tmpfile_fd)) + die("failure: close"); + if (is_setgid(open_tree_fd, FILE3, 0)) + die("failure: is_setgid"); + if (!expected_uid_gid(open_tree_fd, FILE3, 0, 10000, 10000)) + die("failure: check ownership"); + if (unlinkat(open_tree_fd, FILE3, 0)) + die("failure: delete"); + } + exit(EXIT_SUCCESS); } if (wait_for_pid(pid)) @@ -8191,6 +8277,9 @@ static int setgid_create_idmapped_in_userns(void) .attr_set = MOUNT_ATTR_IDMAP, }; pid_t pid; + int tmpfile_fd = -EBADF; + bool supported = false; + char path[PATH_MAX]; if (!caps_supported()) return 0; @@ -8238,6 +8327,8 @@ static int setgid_create_idmapped_in_userns(void) goto out; } + supported = openat_tmpfile_supported(open_tree_fd); + pid = fork(); if (pid < 0) { log_stderr("failure: fork"); @@ -8304,6 +8395,25 @@ static int setgid_create_idmapped_in_userns(void) if (unlinkat(open_tree_fd, CHRDEV1, 0)) die("failure: delete"); + /* create tmpfile via filesystem tmpfile api */ + if (supported) { + tmpfile_fd = openat(open_tree_fd, ".", O_TMPFILE | O_RDWR, S_IXGRP | S_ISGID); + if (tmpfile_fd < 0) + die("failure: create"); + /* link the temporary file into the filesystem, making it permanent */ + snprintf(path, PATH_MAX, "/proc/self/fd/%d", tmpfile_fd); + if (linkat(AT_FDCWD, path, open_tree_fd, FILE3, AT_SYMLINK_FOLLOW)) + die("failure: linkat"); + if (close(tmpfile_fd)) + die("failure: close"); + if (!is_setgid(open_tree_fd, FILE3, 0)) + die("failure: is_setgid"); + if (!expected_uid_gid(open_tree_fd, FILE3, 0, 0, 0)) + die("failure: check ownership"); + if (unlinkat(open_tree_fd, FILE3, 0)) + die("failure: delete"); + } + exit(EXIT_SUCCESS); } if (wait_for_pid(pid)) @@ -8412,6 +8522,25 @@ static int setgid_create_idmapped_in_userns(void) if (unlinkat(open_tree_fd, CHRDEV1, 0)) die("failure: delete"); + /* create tmpfile via filesystem tmpfile api */ + if (supported) { + tmpfile_fd = openat(open_tree_fd, ".", O_TMPFILE | O_RDWR, S_IXGRP | S_ISGID); + if (tmpfile_fd < 0) + die("failure: create"); + /* link the temporary file into the filesystem, making it permanent */ + snprintf(path, PATH_MAX, "/proc/self/fd/%d", tmpfile_fd); + if (linkat(AT_FDCWD, path, open_tree_fd, FILE3, AT_SYMLINK_FOLLOW)) + die("failure: linkat"); + if (close(tmpfile_fd)) + die("failure: close"); + if (is_setgid(open_tree_fd, FILE3, 0)) + die("failure: is_setgid"); + if (!expected_uid_gid(open_tree_fd, FILE3, 0, 0, 1000)) + die("failure: check ownership"); + if (unlinkat(open_tree_fd, FILE3, 0)) + die("failure: delete"); + } + exit(EXIT_SUCCESS); } if (wait_for_pid(pid)) @@ -8508,6 +8637,25 @@ static int setgid_create_idmapped_in_userns(void) if (unlinkat(open_tree_fd, CHRDEV1, 0)) die("failure: delete"); + /* create tmpfile via filesystem tmpfile api */ + if (supported) { + tmpfile_fd = openat(open_tree_fd, ".", O_TMPFILE | O_RDWR, S_IXGRP | S_ISGID); + if (tmpfile_fd < 0) + die("failure: create"); + /* link the temporary file into the filesystem, making it permanent */ + snprintf(path, PATH_MAX, "/proc/self/fd/%d", tmpfile_fd); + if (linkat(AT_FDCWD, path, open_tree_fd, FILE3, AT_SYMLINK_FOLLOW)) + die("failure: linkat"); + if (close(tmpfile_fd)) + die("failure: close"); + if (is_setgid(open_tree_fd, FILE3, 0)) + die("failure: is_setgid"); + if (!expected_uid_gid(open_tree_fd, FILE3, 0, 0, 0)) + die("failure: check ownership"); + if (unlinkat(open_tree_fd, FILE3, 0)) + die("failure: delete"); + } + exit(EXIT_SUCCESS); } if (wait_for_pid(pid)) From patchwork Tue Apr 12 11:33:45 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: "Yang Xu (Fujitsu)" X-Patchwork-Id: 12810593 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 6B5AEC433F5 for ; Tue, 12 Apr 2022 11:48:34 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1346822AbiDLLuo (ORCPT ); Tue, 12 Apr 2022 07:50:44 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:47068 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1355050AbiDLLtJ (ORCPT ); Tue, 12 Apr 2022 07:49:09 -0400 Received: from mail1.bemta34.messagelabs.com (mail1.bemta34.messagelabs.com [195.245.231.4]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 705DA9FEE; Tue, 12 Apr 2022 03:33:50 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=fujitsu.com; s=170520fj; t=1649759628; i=@fujitsu.com; bh=mAumIPc8r56GCBPV0WVMEx+GRmeDp0lAwL4fXJ/7Bn0=; h=From:To:CC:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=PloExhdnHyNytenD12Ov9pTvwAg7IfWoZ0EqdcSsgfC0VaQS7Swew6HmSFh6DuS89 DjoR9GKccSAmRw8AFSgMMDM320MSAWklBM2MCJ8bYI5QCYYEvRt3ITssiqzPXl+mXj YPngodUeTY8aNJNTqLap5I6pdryY0Lz5af0RQwKYMxEzBrJx2q1oLLZMMokXyvFbY2 kVSJ+uiIZ4GdkYEXjqpqhzdBb78VivsBYkEVinvuye6CofJfNQGysTWPc/G7yd1LGg KX4hCzMfCaPokheb5l7YQceY9m0IsiYCvQ6OIo04sUmxgrVrFLwrXtr24ayLEHrbea qFCt3PFHOaO9Q== X-Brightmail-Tracker: H4sIAAAAAAAAA+NgFrrNIsWRWlGSWpSXmKPExsViZ8MxRbcnNDT JYONEcYvXhz8xWmw5do/R4vITPovTLXvZLfbsPcniwOpxapGEx6ZVnWwenzfJBTBHsWbmJeVX JLBmzFj0kLHgQTdjxe91T9gbGG8WdDFycQgJvGaUuNm/gRnC2cMocXnqGdYuRk4ONgFNiWedC 5hBbBEBF4lv+/+ygdjMAjkSDbt/MIHYwgKeEl1PToLVsAioStw5e5YRxOYV8JDo3ziJHcSWEF CQmPLwPVgNJ1D9pCP7WEBsIaCaGa2r2SDqBSVOznzCAjFfQuLgixfMEL2KEpc6vjFC2BUSs2a 1MU1g5J+FpGUWkpYFjEyrGK2TijLTM0pyEzNzdA0NDHQNDU11jS10DU0M9BKrdBP1Ukt1y1OL S3SN9BLLi/VSi4v1iitzk3NS9PJSSzYxAsM5pVitZwfjiVU/9Q4xSnIwKYnyGvCEJgnxJeWnV GYkFmfEF5XmpBYfYpTh4FCS4JULBsoJFqWmp1akZeYAYwsmLcHBoyTCGxsIlOYtLkjMLc5Mh0 idYtTl+Pvp715mIZa8/LxUKXFezhCgIgGQoozSPLgRsDi/xCgrJczLyMDAIMRTkFqUm1mCKv+ KUZyDUUmYlx9kCk9mXgncpldARzABHRG6LRDkiJJEhJRUA1PeCf8mmZ3Hog9+y7opM58pdq2C 7jTH54w1DvOmP74/3c1uYYdv+a2E+Ksu7/d++qea7TiniaHV0+f+knX2eke8Jtz8psxRxi8/c Z5vf7bwrgWTTX+5euh5fi7721fix/X4Xl0Z53Pjb8Yllqa677WW7NwqI7b1g94DrwTja24fb/ 9ue3ai4j7vZNnv7+sa5y1ozsycs/pO8JVL26JYbi9XSe0XlLRoO/z4miFj5H+/hb1rJ/PN/lb 8Kmtmise0k82zl69o6v2l3HbS0Xt6XLr2tfdNnnd0V4WpLStuTvHhWOC/vvRAjXd9ykkxg4hp Xk80wl56le54xxPfyvdcdFlAws/tz+PP6c6ozU0szVNiKc5INNRiLipOBADbHMS6bgMAAA== X-Env-Sender: xuyang2018.jy@fujitsu.com X-Msg-Ref: server-16.tower-565.messagelabs.com!1649759628!58522!1 X-Originating-IP: [62.60.8.148] X-SYMC-ESS-Client-Auth: outbound-route-from=pass X-StarScan-Received: X-StarScan-Version: 9.85.8; banners=-,-,- X-VirusChecked: Checked Received: (qmail 31504 invoked from network); 12 Apr 2022 10:33:48 -0000 Received: from unknown (HELO mailhost1.uk.fujitsu.com) (62.60.8.148) by server-16.tower-565.messagelabs.com with ECDHE-RSA-AES256-GCM-SHA384 encrypted SMTP; 12 Apr 2022 10:33:48 -0000 Received: from R01UKEXCASM126.r01.fujitsu.local ([10.183.43.178]) by mailhost1.uk.fujitsu.com (8.14.5/8.14.5) with ESMTP id 23CAXmvN004638 (version=TLSv1/SSLv3 cipher=AES256-SHA bits=256 verify=FAIL); Tue, 12 Apr 2022 11:33:48 +0100 Received: from localhost.localdomain (10.167.220.84) by R01UKEXCASM126.r01.fujitsu.local (10.183.43.178) with Microsoft SMTP Server (TLS) id 15.0.1497.32; Tue, 12 Apr 2022 11:33:45 +0100 From: Yang Xu To: , , CC: , , Yang Xu Subject: [PATCH v3 4/5] idmapped-mounts: Add new setgid_create_umask test Date: Tue, 12 Apr 2022 19:33:45 +0800 Message-ID: <1649763226-2329-4-git-send-email-xuyang2018.jy@fujitsu.com> X-Mailer: git-send-email 1.8.3.1 In-Reply-To: <1649763226-2329-1-git-send-email-xuyang2018.jy@fujitsu.com> References: <1649763226-2329-1-git-send-email-xuyang2018.jy@fujitsu.com> MIME-Version: 1.0 X-Originating-IP: [10.167.220.84] X-ClientProxiedBy: G08CNEXCHPEKD07.g08.fujitsu.local (10.167.33.80) To R01UKEXCASM126.r01.fujitsu.local (10.183.43.178) Precedence: bulk List-ID: X-Mailing-List: linux-fsdevel@vger.kernel.org The current_umask() is stripped from the mode directly in the vfs if the filesystem either doesn't support acls or the filesystem has been mounted without posic acl support. If the filesystem does support acls then current_umask() stripping is deferred to posix_acl_create(). So when the filesystem calls posix_acl_create() and there are no acls set or not supported then current_umask() will be stripped. Here we only use umask(S_IXGRP) to check whether inode strip S_ISGID works correctly. Signed-off-by: Yang Xu --- src/idmapped-mounts/idmapped-mounts.c | 505 +++++++++++++++++++++++++- tests/generic/680 | 26 ++ tests/generic/680.out | 2 + 3 files changed, 532 insertions(+), 1 deletion(-) create mode 100755 tests/generic/680 create mode 100644 tests/generic/680.out diff --git a/src/idmapped-mounts/idmapped-mounts.c b/src/idmapped-mounts/idmapped-mounts.c index 02f91558..e6c14586 100644 --- a/src/idmapped-mounts/idmapped-mounts.c +++ b/src/idmapped-mounts/idmapped-mounts.c @@ -14146,6 +14146,494 @@ out: return fret; } +/* The following tests are concerned with setgid inheritance. These can be + * filesystem type specific. For xfs, if a new file or directory or node is + * created within a setgid directory and irix_sgid_inhiert is set then inheritthe + * setgid bit if the caller is in the group of the directory. + * + * The current_umask() is stripped from the mode directly in the vfs if the + * filesystem either doesn't support acls or the filesystem has been + * mounted without posic acl support. + * + * If the filesystem does support acls then current_umask() stripping is + * deferred to posix_acl_create(). So when the filesystem calls + * posix_acl_create() and there are no acls set or not supported then + * current_umask() will be stripped. + * + * Use umask(S_IXGRP) to check whether inode strip S_ISGID works correctly. + */ +static int setgid_create_umask(void) +{ + int fret = -1; + int file1_fd = -EBADF; + int tmpfile_fd = -EBADF; + pid_t pid; + bool supported = false; + char path[PATH_MAX]; + mode_t mode; + + if (!caps_supported()) + return 0; + + if (fchmod(t_dir1_fd, S_IRUSR | + S_IWUSR | + S_IRGRP | + S_IWGRP | + S_IROTH | + S_IWOTH | + S_IXUSR | + S_IXGRP | + S_IXOTH | + S_ISGID), 0) { + log_stderr("failure: fchmod"); + goto out; + } + + /* Verify that the setgid bit got raised. */ + if (!is_setgid(t_dir1_fd, "", AT_EMPTY_PATH)) { + log_stderr("failure: is_setgid"); + goto out; + } + + supported = openat_tmpfile_supported(t_dir1_fd); + + /* Only umask with S_IXGRP because inode strip S_ISGID will check mode + * whether has group execute or search permission. + */ + umask(S_IXGRP); + mode = umask(S_IXGRP); + if (!(mode & S_IXGRP)) + die("failure: umask"); + + pid = fork(); + if (pid < 0) { + log_stderr("failure: fork"); + goto out; + } + if (pid == 0) { + if (!switch_ids(0, 10000)) + die("failure: switch_ids"); + + if (!caps_down_fsetid()) + die("failure: caps_down_fsetid"); + + /* create regular file via open() */ + file1_fd = openat(t_dir1_fd, FILE1, O_CREAT | O_EXCL | O_CLOEXEC, S_IXGRP | S_ISGID); + if (file1_fd < 0) + die("failure: create"); + + /* Neither in_group_p() nor capable_wrt_inode_uidgid() so setgid + * bit needs to be stripped. + */ + if (is_setgid(t_dir1_fd, FILE1, 0)) + die("failure: is_setgid"); + + /* create directory */ + if (mkdirat(t_dir1_fd, DIR1, 0000)) + die("failure: create"); + + if (xfs_irix_sgid_inherit_enabled()) { + /* We're not in_group_p(). */ + if (is_setgid(t_dir1_fd, DIR1, 0)) + die("failure: is_setgid"); + } else { + /* Directories always inherit the setgid bit. */ + if (!is_setgid(t_dir1_fd, DIR1, 0)) + die("failure: is_setgid"); + } + + /* create a special file via mknodat() vfs_create */ + if (mknodat(t_dir1_fd, FILE2, S_IFREG | S_ISGID | S_IXGRP, 0)) + die("failure: mknodat"); + + if (is_setgid(t_dir1_fd, FILE2, 0)) + die("failure: is_setgid"); + + /* create a character device via mknodat() vfs_mknod */ + if (mknodat(t_dir1_fd, CHRDEV1, S_IFCHR | S_ISGID | S_IXGRP, makedev(5, 1))) + die("failure: mknodat"); + + if (is_setgid(t_dir1_fd, CHRDEV1, 0)) + die("failure: is_setgid"); + + if (unlinkat(t_dir1_fd, FILE1, 0)) + die("failure: delete"); + + if (unlinkat(t_dir1_fd, DIR1, AT_REMOVEDIR)) + die("failure: delete"); + + if (unlinkat(t_dir1_fd, FILE2, 0)) + die("failure: delete"); + + if (unlinkat(t_dir1_fd, CHRDEV1, 0)) + die("failure: delete"); + + /* create tmpfile via filesystem tmpfile api */ + if (supported) { + tmpfile_fd = openat(t_dir1_fd, ".", O_TMPFILE | O_RDWR, S_IXGRP | S_ISGID); + if (tmpfile_fd < 0) + die("failure: create"); + /* link the temporary file into the filesystem, making it permanent */ + snprintf(path, PATH_MAX, "/proc/self/fd/%d", tmpfile_fd); + if (linkat(AT_FDCWD, path, t_dir1_fd, FILE3, AT_SYMLINK_FOLLOW)) + die("failure: linkat"); + if (close(tmpfile_fd)) + die("failure: close"); + if (is_setgid(t_dir1_fd, FILE3, 0)) + die("failure: is_setgid"); + if (unlinkat(t_dir1_fd, FILE3, 0)) + die("failure: delete"); + } + + exit(EXIT_SUCCESS); + } + if (wait_for_pid(pid)) + goto out; + + fret = 0; + log_debug("Ran test"); +out: + safe_close(file1_fd); + return fret; +} + +static int setgid_create_umask_idmapped(void) +{ + int fret = -1; + int file1_fd = -EBADF, open_tree_fd = -EBADF; + struct mount_attr attr = { + .attr_set = MOUNT_ATTR_IDMAP, + }; + pid_t pid; + int tmpfile_fd = -EBADF; + bool supported = false; + char path[PATH_MAX]; + mode_t mode; + + if (!caps_supported()) + return 0; + + if (fchmod(t_dir1_fd, S_IRUSR | + S_IWUSR | + S_IRGRP | + S_IWGRP | + S_IROTH | + S_IWOTH | + S_IXUSR | + S_IXGRP | + S_IXOTH | + S_ISGID), 0) { + log_stderr("failure: fchmod"); + goto out; + } + + /* Verify that the sid bits got raised. */ + if (!is_setgid(t_dir1_fd, "", AT_EMPTY_PATH)) { + log_stderr("failure: is_setgid"); + goto out; + } + + /* Changing mount properties on a detached mount. */ + attr.userns_fd = get_userns_fd(0, 10000, 10000); + if (attr.userns_fd < 0) { + log_stderr("failure: get_userns_fd"); + goto out; + } + + open_tree_fd = sys_open_tree(t_dir1_fd, "", + AT_EMPTY_PATH | + AT_NO_AUTOMOUNT | + AT_SYMLINK_NOFOLLOW | + OPEN_TREE_CLOEXEC | + OPEN_TREE_CLONE); + if (open_tree_fd < 0) { + log_stderr("failure: sys_open_tree"); + goto out; + } + + if (sys_mount_setattr(open_tree_fd, "", AT_EMPTY_PATH, &attr, sizeof(attr))) { + log_stderr("failure: sys_mount_setattr"); + goto out; + } + + supported = openat_tmpfile_supported(open_tree_fd); + + /* Only umask with S_IXGRP because inode strip S_ISGID will check mode + * whether has group execute or search permission. + */ + umask(S_IXGRP); + mode = umask(S_IXGRP); + if (!(mode & S_IXGRP)) + die("failure: umask"); + + pid = fork(); + if (pid < 0) { + log_stderr("failure: fork"); + goto out; + } + if (pid == 0) { + if (!switch_ids(10000, 11000)) + die("failure: switch fsids"); + + /* create regular file via open() */ + file1_fd = openat(open_tree_fd, FILE1, O_CREAT | O_EXCL | O_CLOEXEC, S_IXGRP | S_ISGID); + if (file1_fd < 0) + die("failure: create"); + + /* Neither in_group_p() nor capable_wrt_inode_uidgid() so setgid + * bit needs to be stripped. + */ + if (is_setgid(open_tree_fd, FILE1, 0)) + die("failure: is_setgid"); + + /* create directory */ + if (mkdirat(open_tree_fd, DIR1, 0000)) + die("failure: create"); + + if (xfs_irix_sgid_inherit_enabled()) { + /* We're not in_group_p(). */ + if (is_setgid(open_tree_fd, DIR1, 0)) + die("failure: is_setgid"); + } else { + /* Directories always inherit the setgid bit. */ + if (!is_setgid(open_tree_fd, DIR1, 0)) + die("failure: is_setgid"); + } + + /* create a special file via mknodat() vfs_create */ + if (mknodat(open_tree_fd, FILE2, S_IFREG | S_ISGID | S_IXGRP, 0)) + die("failure: mknodat"); + + if (is_setgid(open_tree_fd, FILE2, 0)) + die("failure: is_setgid"); + + /* create a whiteout device via mknodat() vfs_mknod */ + if (mknodat(open_tree_fd, CHRDEV1, S_IFCHR | S_ISGID | S_IXGRP, 0)) + die("failure: mknodat"); + + if (is_setgid(open_tree_fd, CHRDEV1, 0)) + die("failure: is_setgid"); + + if (unlinkat(open_tree_fd, FILE1, 0)) + die("failure: delete"); + + if (unlinkat(open_tree_fd, DIR1, AT_REMOVEDIR)) + die("failure: delete"); + + if (unlinkat(open_tree_fd, FILE2, 0)) + die("failure: delete"); + + if (unlinkat(open_tree_fd, CHRDEV1, 0)) + die("failure: delete"); + + /* create tmpfile via filesystem tmpfile api */ + if (supported) { + tmpfile_fd = openat(open_tree_fd, ".", O_TMPFILE | O_RDWR, S_IXGRP | S_ISGID); + if (tmpfile_fd < 0) + die("failure: create"); + /* link the temporary file into the filesystem, making it permanent */ + snprintf(path, PATH_MAX, "/proc/self/fd/%d", tmpfile_fd); + if (linkat(AT_FDCWD, path, open_tree_fd, FILE3, AT_SYMLINK_FOLLOW)) + die("failure: linkat"); + if (close(tmpfile_fd)) + die("failure: close"); + if (is_setgid(open_tree_fd, FILE3, 0)) + die("failure: is_setgid"); + if (unlinkat(open_tree_fd, FILE3, 0)) + die("failure: delete"); + } + + exit(EXIT_SUCCESS); + } + if (wait_for_pid(pid)) + goto out; + + fret = 0; + log_debug("Ran test"); +out: + safe_close(attr.userns_fd); + safe_close(file1_fd); + safe_close(open_tree_fd); + + return fret; +} + +static int setgid_create_umask_idmapped_in_userns(void) +{ + int fret = -1; + int file1_fd = -EBADF, open_tree_fd = -EBADF; + struct mount_attr attr = { + .attr_set = MOUNT_ATTR_IDMAP, + }; + pid_t pid; + int tmpfile_fd = -EBADF; + bool supported = false; + char path[PATH_MAX]; + mode_t mode; + + if (!caps_supported()) + return 0; + + if (fchmod(t_dir1_fd, S_IRUSR | + S_IWUSR | + S_IRGRP | + S_IWGRP | + S_IROTH | + S_IWOTH | + S_IXUSR | + S_IXGRP | + S_IXOTH | + S_ISGID), 0) { + log_stderr("failure: fchmod"); + goto out; + } + + /* Verify that the sid bits got raised. */ + if (!is_setgid(t_dir1_fd, "", AT_EMPTY_PATH)) { + log_stderr("failure: is_setgid"); + goto out; + } + + /* Changing mount properties on a detached mount. */ + attr.userns_fd = get_userns_fd(0, 10000, 10000); + if (attr.userns_fd < 0) { + log_stderr("failure: get_userns_fd"); + goto out; + } + + open_tree_fd = sys_open_tree(t_dir1_fd, "", + AT_EMPTY_PATH | + AT_NO_AUTOMOUNT | + AT_SYMLINK_NOFOLLOW | + OPEN_TREE_CLOEXEC | + OPEN_TREE_CLONE); + if (open_tree_fd < 0) { + log_stderr("failure: sys_open_tree"); + goto out; + } + + if (sys_mount_setattr(open_tree_fd, "", AT_EMPTY_PATH, &attr, sizeof(attr))) { + log_stderr("failure: sys_mount_setattr"); + goto out; + } + + supported = openat_tmpfile_supported(open_tree_fd); + + /* Only umask with S_IXGRP because inode strip S_ISGID will check mode + * whether has group execute or search permission. + */ + umask(S_IXGRP); + mode = umask(S_IXGRP); + if (!(mode & S_IXGRP)) + die("failure: umask"); + + /* Below we verify that setgid inheritance for a newly created file or + * directory works correctly. As part of this we need to verify that + * newly created files or directories inherit their gid from their + * parent directory. So we change the parent directorie's gid to 1000 + * and create a file with fs{g,u}id 0 and verify that the newly created + * file and directory inherit gid 1000, not 0. + */ + if (fchownat(t_dir1_fd, "", -1, 1000, AT_SYMLINK_NOFOLLOW | AT_EMPTY_PATH)) { + log_stderr("failure: fchownat"); + goto out; + } + + pid = fork(); + if (pid < 0) { + log_stderr("failure: fork"); + goto out; + } + if (pid == 0) { + if (!switch_userns(attr.userns_fd, 0, 0, false)) + die("failure: switch_userns"); + + if (!caps_down_fsetid()) + die("failure: caps_down_fsetid"); + + /* create regular file via open() */ + file1_fd = openat(open_tree_fd, FILE1, O_CREAT | O_EXCL | O_CLOEXEC, S_IXGRP | S_ISGID); + if (file1_fd < 0) + die("failure: create"); + + /* Neither in_group_p() nor capable_wrt_inode_uidgid() so setgid + * bit needs to be stripped. + */ + if (is_setgid(open_tree_fd, FILE1, 0)) + die("failure: is_setgid"); + + /* create directory */ + if (mkdirat(open_tree_fd, DIR1, 0000)) + die("failure: create"); + + if (xfs_irix_sgid_inherit_enabled()) { + /* We're not in_group_p(). */ + if (is_setgid(open_tree_fd, DIR1, 0)) + die("failure: is_setgid"); + } else { + /* Directories always inherit the setgid bit. */ + if (!is_setgid(open_tree_fd, DIR1, 0)) + die("failure: is_setgid"); + } + + /* create a special file via mknodat() vfs_create */ + if (mknodat(open_tree_fd, FILE2, S_IFREG | S_ISGID | S_IXGRP, 0)) + die("failure: mknodat"); + + if (is_setgid(open_tree_fd, FILE2, 0)) + die("failure: is_setgid"); + + /* create a whiteout device via mknodat() vfs_mknod */ + if (mknodat(open_tree_fd, CHRDEV1, S_IFCHR | S_ISGID | S_IXGRP, 0)) + die("failure: mknodat"); + + if (is_setgid(open_tree_fd, CHRDEV1, 0)) + die("failure: is_setgid"); + + if (unlinkat(open_tree_fd, FILE1, 0)) + die("failure: delete"); + + if (unlinkat(open_tree_fd, DIR1, AT_REMOVEDIR)) + die("failure: delete"); + + if (unlinkat(open_tree_fd, FILE2, 0)) + die("failure: delete"); + + if (unlinkat(open_tree_fd, CHRDEV1, 0)) + die("failure: delete"); + + /* create tmpfile via filesystem tmpfile api */ + if (supported) { + tmpfile_fd = openat(open_tree_fd, ".", O_TMPFILE | O_RDWR, S_IXGRP | S_ISGID); + if (tmpfile_fd < 0) + die("failure: create"); + /* link the temporary file into the filesystem, making it permanent */ + snprintf(path, PATH_MAX, "/proc/self/fd/%d", tmpfile_fd); + if (linkat(AT_FDCWD, path, open_tree_fd, FILE3, AT_SYMLINK_FOLLOW)) + die("failure: linkat"); + if (close(tmpfile_fd)) + die("failure: close"); + if (is_setgid(open_tree_fd, FILE3, 0)) + die("failure: is_setgid"); + if (unlinkat(open_tree_fd, FILE3, 0)) + die("failure: delete"); + } + + exit(EXIT_SUCCESS); + } + if (wait_for_pid(pid)) + goto out; + + fret = 0; + log_debug("Ran test"); +out: + safe_close(attr.userns_fd); + safe_close(file1_fd); + safe_close(open_tree_fd); + + return fret; +} + static void usage(void) { fprintf(stderr, "Description:\n"); @@ -14164,6 +14652,7 @@ static void usage(void) fprintf(stderr, "--test-nested-userns Run nested userns idmapped mount testsuite\n"); fprintf(stderr, "--test-btrfs Run btrfs specific idmapped mount testsuite\n"); fprintf(stderr, "--test-setattr-fix-968219708108 Run setattr regression tests\n"); + fprintf(stderr, "--test-setgid-umask Run setgid create umask test\n"); _exit(EXIT_SUCCESS); } @@ -14181,6 +14670,7 @@ static const struct option longopts[] = { {"test-nested-userns", no_argument, 0, 'n'}, {"test-btrfs", no_argument, 0, 'b'}, {"test-setattr-fix-968219708108", no_argument, 0, 'i'}, + {"test-setgid-umask", no_argument, 0, 'u'}, {NULL, 0, 0, 0}, }; @@ -14278,6 +14768,12 @@ struct t_idmapped_mounts t_setattr_fix_968219708108[] = { { setattr_fix_968219708108, true, "test that setattr works correctly", }, }; +struct t_idmapped_mounts t_setgid_umask[] = { + { setgid_create_umask, false, "create operations by using umask in directories with setgid bit set", }, + { setgid_create_umask_idmapped, true, "create operations by using umask in directories with setgid bit set on idmapped mount", }, + { setgid_create_umask_idmapped_in_userns, true, "create operations by using umask in directories with setgid bit set on idmapped mounts inside userns", }, +}; + static bool run_test(struct t_idmapped_mounts suite[], size_t suite_size) { int i; @@ -14355,7 +14851,7 @@ int main(int argc, char *argv[]) int index = 0; bool supported = false, test_btrfs = false, test_core = false, test_fscaps_regression = false, test_nested_userns = false, - test_setattr_fix_968219708108 = false; + test_setattr_fix_968219708108 = false, test_setgid_umask = false; while ((ret = getopt_long_only(argc, argv, "", longopts, &index)) != -1) { switch (ret) { @@ -14392,6 +14888,9 @@ int main(int argc, char *argv[]) case 'i': test_setattr_fix_968219708108 = true; break; + case 'u': + test_setgid_umask = true; + break; case 'h': /* fallthrough */ default: @@ -14463,6 +14962,10 @@ int main(int argc, char *argv[]) ARRAY_SIZE(t_setattr_fix_968219708108))) goto out; + if (test_setgid_umask && + !run_test(t_setgid_umask, ARRAY_SIZE(t_setgid_umask))) + goto out; + fret = EXIT_SUCCESS; out: diff --git a/tests/generic/680 b/tests/generic/680 new file mode 100755 index 00000000..aa9c7375 --- /dev/null +++ b/tests/generic/680 @@ -0,0 +1,26 @@ +#! /bin/bash +# SPDX-License-Identifier: GPL-2.0 +# Copyright (c) 2022 Fujitsu Limited. All Rights Reserved. +# +# FS QA Test 680 +# +# Test that idmapped mounts setgid's behave correctly when using umask. +# +. ./common/preamble +_begin_fstest auto quick cap idmapped mount perms rw unlink + +# Import common functions. +. ./common/filter + +# real QA test starts here + +_supported_fs generic +_require_test + +echo "Silence is golden" + +$here/src/idmapped-mounts/idmapped-mounts --test-setgid-umask --device "$TEST_DEV" \ + --mount "$TEST_DIR" --fstype "$FSTYP" + +status=$? +exit diff --git a/tests/generic/680.out b/tests/generic/680.out new file mode 100644 index 00000000..f4950cda --- /dev/null +++ b/tests/generic/680.out @@ -0,0 +1,2 @@ +QA output created by 680 +Silence is golden From patchwork Tue Apr 12 11:33:46 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: "Yang Xu (Fujitsu)" X-Patchwork-Id: 12810596 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 7F1F0C433FE for ; Tue, 12 Apr 2022 11:48:53 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1349712AbiDLLu6 (ORCPT ); Tue, 12 Apr 2022 07:50:58 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:39080 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1355363AbiDLLtT (ORCPT ); Tue, 12 Apr 2022 07:49:19 -0400 Received: from mail1.bemta34.messagelabs.com (mail1.bemta34.messagelabs.com [195.245.231.4]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 1746C19284; Tue, 12 Apr 2022 03:34:46 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=fujitsu.com; s=170520fj; t=1649759685; i=@fujitsu.com; bh=WE0H8u6NMFZxVA6KQdMCLJqSSWftjC4J+9Oi8D1rfa8=; h=From:To:CC:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=qAUZxsIFrtumSqXZnvRkOf4/NOgMB83ef1tFriBzxcJ/SzwnJ1uK8VTYf+xALqOEw cUy9SlK0ZN74+3goiXLj92+JlIzeq/L9OWMNNQMMCqWWOvQb7x2qxU0Fo/tcvTYrvF nbFe5ZetPEh+jogeDtaPShsJB/mDrMlq+GlTORLqlC9rVxlEJUDAThfDZ3HUaQJzpg XeCZPw36QaDig+JskXpyqhUC/mK/sY3la9gLR6HQHDGnJh7kGN1j5LdQY1GZIhbVZ0 vUSJdGX7J2IdPTIr+CHN8lswyDNbHq5TdX9ipmVe5EzpBZZgI26Ue7AHtjqED3rtvJ Mg3RWxvWuqY9g== X-Brightmail-Tracker: H4sIAAAAAAAAA+NgFrrFIsWRWlGSWpSXmKPExsViZ8MxRfdIaGi SwdXvGhavD39itNhy7B6jxeUnfBanW/ayW+zZe5LFgdXj1CIJj02rOtk8Pm+SC2COYs3MS8qv SGDNeHnoAUtB313Giqb/q9kbGKdsZuxi5OIQEnjNKLF+5wXmLkZOIGcPo8TD/RUgNpuApsSzz gVgcREBF4lv+/+ygdjMAjkSDbt/MIHYwgLuEutezACrYRFQlZh19QlYnFfAQ2LjmaUsILaEgI LElIfvwWo4BTwlJh3ZxwKxy0NiRutqNoh6QYmTM5+wQMyXkDj44gUzRK+ixKWOb4wQdoXErFl tTBMY+WchaZmFpGUBI9MqRqukosz0jJLcxMwcXUMDA11DQ1NdYyNdQxO9xCrdRL3UUt3y1OIS XSO9xPJivdTiYr3iytzknBS9vNSSTYzAYE4pVuDbwdi86qfeIUZJDiYlUV4DntAkIb6k/JTKj MTijPii0pzU4kOMMhwcShK8csFAOcGi1PTUirTMHGBkwaQlOHiURHhjA4HSvMUFibnFmekQqV OMuhx/P/3dyyzEkpeflyolztsYAlQkAFKUUZoHNwIW5ZcYZaWEeRkZGBiEeApSi3IzS1DlXzG KczAqCfPyg0zhycwrgdv0CugIJqAjQrcFghxRkoiQkmpgklF6tSXUcvabZ+Eri05OVZwxM3Li VwF2M+WlNu/tg/v0TxSuZFTKyam9mHNUf8cqg3pTkSbn/x9d+Bam9hT1aEg/fiBacLrsxIN+0 xc7QoUYnR1EOCqzHrlf/32q8dkCobzb29bsjH4bXbcy8f//+r7pl7/raoac23CkqtnWovFrXt fm6RPr73N2Hw26Lc3IyZeqdDLlylfT09pTyjMYvc9P2TU3cbr9v9SaH5Ltlkdsk9YsZgjZL/p G+xnHlu7qiYo6mgdnSD+InF/uwsrxnE1EuTxuwu4GNd3WC/qHS57K/+xxrLgg6aE9IUE1ntOX /1Dw9T0fNW99X7Z6vuonD8nWuisRnFtzWXrrdn1QYinOSDTUYi4qTgQAtH13MW0DAAA= X-Env-Sender: xuyang2018.jy@fujitsu.com X-Msg-Ref: server-10.tower-548.messagelabs.com!1649759684!54629!1 X-Originating-IP: [62.60.8.148] X-SYMC-ESS-Client-Auth: outbound-route-from=pass X-StarScan-Received: X-StarScan-Version: 9.85.8; banners=-,-,- X-VirusChecked: Checked Received: (qmail 14189 invoked from network); 12 Apr 2022 10:34:44 -0000 Received: from unknown (HELO mailhost1.uk.fujitsu.com) (62.60.8.148) by server-10.tower-548.messagelabs.com with ECDHE-RSA-AES256-GCM-SHA384 encrypted SMTP; 12 Apr 2022 10:34:44 -0000 Received: from R01UKEXCASM126.r01.fujitsu.local ([10.183.43.178]) by mailhost1.uk.fujitsu.com (8.14.5/8.14.5) with ESMTP id 23CAYiKg004938 (version=TLSv1/SSLv3 cipher=AES256-SHA bits=256 verify=FAIL); Tue, 12 Apr 2022 11:34:44 +0100 Received: from localhost.localdomain (10.167.220.84) by R01UKEXCASM126.r01.fujitsu.local (10.183.43.178) with Microsoft SMTP Server (TLS) id 15.0.1497.32; Tue, 12 Apr 2022 11:34:41 +0100 From: Yang Xu To: , , CC: , , Yang Xu Subject: [PATCH v3 5/5] idmapped-mounts: Add new setgid_create_acl test Date: Tue, 12 Apr 2022 19:33:46 +0800 Message-ID: <1649763226-2329-5-git-send-email-xuyang2018.jy@fujitsu.com> X-Mailer: git-send-email 1.8.3.1 In-Reply-To: <1649763226-2329-1-git-send-email-xuyang2018.jy@fujitsu.com> References: <1649763226-2329-1-git-send-email-xuyang2018.jy@fujitsu.com> MIME-Version: 1.0 X-Originating-IP: [10.167.220.84] X-ClientProxiedBy: G08CNEXCHPEKD07.g08.fujitsu.local (10.167.33.80) To R01UKEXCASM126.r01.fujitsu.local (10.183.43.178) Precedence: bulk List-ID: X-Mailing-List: linux-fsdevel@vger.kernel.org The current_umask() is stripped from the mode directly in the vfs if the filesystem either doesn't support acls or the filesystem has been mounted without posic acl support. If the filesystem does support acls then current_umask() stripping is deferred to posix_acl_create(). So when the filesystem calls posix_acl_create() and there are no acls set or not supported then current_umask() will be stripped. If the parent directory has a default acl then permissions are based off of that and current_umask() is ignored. Specifically, if the ACL has an ACL_MASK entry, the group permissions correspond to the permissions of the ACL_MASK entry. Otherwise, if the ACL has no ACL_MASK entry, the group permissions correspond to the permissions of the ACL_GROUP_OBJ entry. Here we only use setfacl to set default acl or add ACL_MASK to check whether inode strip S_ISGID works correctly. Signed-off-by: Yang Xu --- src/idmapped-mounts/idmapped-mounts.c | 793 +++++++++++++++++++++++++- tests/generic/681 | 28 + tests/generic/681.out | 2 + 3 files changed, 822 insertions(+), 1 deletion(-) create mode 100755 tests/generic/681 create mode 100644 tests/generic/681.out diff --git a/src/idmapped-mounts/idmapped-mounts.c b/src/idmapped-mounts/idmapped-mounts.c index e6c14586..0dbbc64c 100644 --- a/src/idmapped-mounts/idmapped-mounts.c +++ b/src/idmapped-mounts/idmapped-mounts.c @@ -14634,6 +14634,781 @@ out: return fret; } +/* The following tests are concerned with setgid inheritance. These can be + * filesystem type specific. For xfs, if a new file or directory or node is + * created within a setgid directory and irix_sgid_inhiert is set then inheritthe + * setgid bit if the caller is in the group of the directory. + * + * The current_umask() is stripped from the mode directly in the vfs if the + * filesystem either doesn't support acls or the filesystem has been + * mounted without posic acl support. + * + * If the filesystem does support acls then current_umask() stripping is + * deferred to posix_acl_create(). So when the filesystem calls + * posix_acl_create() and there are no acls set or not supported then + * current_umask() will be stripped. + * + * If the parent directory has a default acl then permissions are based off + * of that and current_umask() is ignored. Specifically, if the ACL has an + * ACL_MASK entry, the group permissions correspond to the permissions of + * the ACL_MASK entry. Otherwise, if the ACL has no ACL_MASK entry, the + * group permissions correspond to the permissions of the ACL_GROUP_OBJ + * entry. + * + * Use setfacl to check whether inode strip S_ISGID works correctly. + */ +static int setgid_create_acl(void) +{ + int fret = -1; + int file1_fd = -EBADF; + int tmpfile_fd = -EBADF; + pid_t pid; + bool supported = false; + char path[PATH_MAX]; + + if (!caps_supported()) + return 0; + + if (fchmod(t_dir1_fd, S_IRUSR | + S_IWUSR | + S_IRGRP | + S_IWGRP | + S_IROTH | + S_IWOTH | + S_IXUSR | + S_IXGRP | + S_IXOTH | + S_ISGID), 0) { + log_stderr("failure: fchmod"); + goto out; + } + + /* Verify that the setgid bit got raised. */ + if (!is_setgid(t_dir1_fd, "", AT_EMPTY_PATH)) { + log_stderr("failure: is_setgid"); + goto out; + } + + supported = openat_tmpfile_supported(t_dir1_fd); + + pid = fork(); + if (pid < 0) { + log_stderr("failure: fork"); + goto out; + } + if (pid == 0) { + /* If the parent directory has a default acl then permissions are based off + * of that and current_umask() is ignored. Specifically, if the ACL has an + * ACL_MASK entry, the group permissions correspond to the permissions of + * the ACL_MASK entry. + */ + umask(S_IXGRP); + snprintf(t_buf, sizeof(t_buf), "setfacl -d -m u::rwx,g::rwx,o::rwx,m:rw %s/%s", t_mountpoint, T_DIR1); + if (system(t_buf)) + die("failure: system"); + + if (!switch_ids(0, 10000)) + die("failure: switch_ids"); + + if (!caps_down_fsetid()) + die("failure: caps_down_fsetid"); + + /* create regular file via open() */ + file1_fd = openat(t_dir1_fd, FILE1, O_CREAT | O_EXCL | O_CLOEXEC, S_IXGRP | S_ISGID); + if (file1_fd < 0) + die("failure: create"); + + /* Neither in_group_p() nor capable_wrt_inode_uidgid() so setgid + * bit needs to be stripped. + */ + if (is_setgid(t_dir1_fd, FILE1, 0)) + die("failure: is_setgid"); + + /* create directory */ + if (mkdirat(t_dir1_fd, DIR1, 0000)) + die("failure: create"); + + if (xfs_irix_sgid_inherit_enabled()) { + /* We're not in_group_p(). */ + if (is_setgid(t_dir1_fd, DIR1, 0)) + die("failure: is_setgid"); + } else { + /* Directories always inherit the setgid bit. */ + if (!is_setgid(t_dir1_fd, DIR1, 0)) + die("failure: is_setgid"); + } + + /* create a special file via mknodat() vfs_create */ + if (mknodat(t_dir1_fd, FILE2, S_IFREG | S_ISGID | S_IXGRP, 0)) + die("failure: mknodat"); + + if (is_setgid(t_dir1_fd, FILE2, 0)) + die("failure: is_setgid"); + + /* create a character device via mknodat() vfs_mknod */ + if (mknodat(t_dir1_fd, CHRDEV1, S_IFCHR | S_ISGID | S_IXGRP, makedev(5, 1))) + die("failure: mknodat"); + + if (is_setgid(t_dir1_fd, CHRDEV1, 0)) + die("failure: is_setgid"); + + if (unlinkat(t_dir1_fd, FILE1, 0)) + die("failure: delete"); + + if (unlinkat(t_dir1_fd, DIR1, AT_REMOVEDIR)) + die("failure: delete"); + + if (unlinkat(t_dir1_fd, FILE2, 0)) + die("failure: delete"); + + if (unlinkat(t_dir1_fd, CHRDEV1, 0)) + die("failure: delete"); + + /* create tmpfile via filesystem tmpfile api */ + if (supported) { + tmpfile_fd = openat(t_dir1_fd, ".", O_TMPFILE | O_RDWR, S_IXGRP | S_ISGID); + if (tmpfile_fd < 0) + die("failure: create"); + /* link the temporary file into the filesystem, making it permanent */ + snprintf(path, PATH_MAX, "/proc/self/fd/%d", tmpfile_fd); + if (linkat(AT_FDCWD, path, t_dir1_fd, FILE3, AT_SYMLINK_FOLLOW)) + die("failure: linkat"); + if (close(tmpfile_fd)) + die("failure: close"); + if (is_setgid(t_dir1_fd, FILE3, 0)) + die("failure: is_setgid"); + if (unlinkat(t_dir1_fd, FILE3, 0)) + die("failure: delete"); + } + exit(EXIT_SUCCESS); + } + if (wait_for_pid(pid)) + goto out; + + pid = fork(); + if (pid < 0) { + log_stderr("failure: fork"); + goto out; + } + if (pid == 0) { + /* If the parent directory has a default acl then permissions are based off + * of that and current_umask() is ignored. Specifically, if the ACL has an + * ACL_MASK entry, the group permissions correspond to the permissions of + * the ACL_MASK entry. Otherwise, if the ACL has no ACL_MASK entry, the + * group permissions correspond to the permissions of the ACL_GROUP_OBJ + * entry. + */ + umask(S_IXGRP); + snprintf(t_buf, sizeof(t_buf), "setfacl -d -m u::rwx,g::rw,o::rwx, %s/%s", t_mountpoint, T_DIR1); + if (system(t_buf)) + die("failure: system"); + + if (!switch_ids(0, 10000)) + die("failure: switch_ids"); + + if (!caps_down_fsetid()) + die("failure: caps_down_fsetid"); + + /* create regular file via open() */ + file1_fd = openat(t_dir1_fd, FILE1, O_CREAT | O_EXCL | O_CLOEXEC, S_IXGRP | S_ISGID); + if (file1_fd < 0) + die("failure: create"); + + /* Neither in_group_p() nor capable_wrt_inode_uidgid() so setgid + * bit needs to be stripped. + */ + if (is_setgid(t_dir1_fd, FILE1, 0)) + die("failure: is_setgid"); + + /* create directory */ + if (mkdirat(t_dir1_fd, DIR1, 0000)) + die("failure: create"); + + if (xfs_irix_sgid_inherit_enabled()) { + /* We're not in_group_p(). */ + if (is_setgid(t_dir1_fd, DIR1, 0)) + die("failure: is_setgid"); + } else { + /* Directories always inherit the setgid bit. */ + if (!is_setgid(t_dir1_fd, DIR1, 0)) + die("failure: is_setgid"); + } + + /* create a special file via mknodat() vfs_create */ + if (mknodat(t_dir1_fd, FILE2, S_IFREG | S_ISGID | S_IXGRP, 0)) + die("failure: mknodat"); + + if (is_setgid(t_dir1_fd, FILE2, 0)) + die("failure: is_setgid"); + + /* create a character device via mknodat() vfs_mknod */ + if (mknodat(t_dir1_fd, CHRDEV1, S_IFCHR | S_ISGID | S_IXGRP, makedev(5, 1))) + die("failure: mknodat"); + + if (is_setgid(t_dir1_fd, CHRDEV1, 0)) + die("failure: is_setgid"); + + if (unlinkat(t_dir1_fd, FILE1, 0)) + die("failure: delete"); + + if (unlinkat(t_dir1_fd, DIR1, AT_REMOVEDIR)) + die("failure: delete"); + + if (unlinkat(t_dir1_fd, FILE2, 0)) + die("failure: delete"); + + if (unlinkat(t_dir1_fd, CHRDEV1, 0)) + die("failure: delete"); + + /* create tmpfile via filesystem tmpfile api */ + if (supported) { + tmpfile_fd = openat(t_dir1_fd, ".", O_TMPFILE | O_RDWR, S_IXGRP | S_ISGID); + if (tmpfile_fd < 0) + die("failure: create"); + /* link the temporary file into the filesystem, making it permanent */ + snprintf(path, PATH_MAX, "/proc/self/fd/%d", tmpfile_fd); + if (linkat(AT_FDCWD, path, t_dir1_fd, FILE3, AT_SYMLINK_FOLLOW)) + die("failure: linkat"); + if (close(tmpfile_fd)) + die("failure: close"); + if (is_setgid(t_dir1_fd, FILE3, 0)) + die("failure: is_setgid"); + if (unlinkat(t_dir1_fd, FILE3, 0)) + die("failure: delete"); + } + exit(EXIT_SUCCESS); + } + if (wait_for_pid(pid)) + goto out; + + fret = 0; + log_debug("Ran test"); +out: + safe_close(file1_fd); + return fret; +} + +static int setgid_create_acl_idmapped(void) +{ + int fret = -1; + int file1_fd = -EBADF, open_tree_fd = -EBADF; + struct mount_attr attr = { + .attr_set = MOUNT_ATTR_IDMAP, + }; + pid_t pid; + int tmpfile_fd = -EBADF; + bool supported = false; + char path[PATH_MAX]; + + if (!caps_supported()) + return 0; + + if (fchmod(t_dir1_fd, S_IRUSR | + S_IWUSR | + S_IRGRP | + S_IWGRP | + S_IROTH | + S_IWOTH | + S_IXUSR | + S_IXGRP | + S_IXOTH | + S_ISGID), 0) { + log_stderr("failure: fchmod"); + goto out; + } + + /* Verify that the setgid bit got raised. */ + if (!is_setgid(t_dir1_fd, "", AT_EMPTY_PATH)) { + log_stderr("failure: is_setgid"); + goto out; + } + + attr.userns_fd = get_userns_fd(0, 10000, 10000); + if (attr.userns_fd < 0) { + log_stderr("failure: get_userns_fd"); + goto out; + } + + open_tree_fd = sys_open_tree(t_dir1_fd, "", + AT_EMPTY_PATH | + AT_NO_AUTOMOUNT | + AT_SYMLINK_NOFOLLOW | + OPEN_TREE_CLOEXEC | + OPEN_TREE_CLONE); + if (open_tree_fd < 0) { + log_stderr("failure: sys_open_tree"); + goto out; + } + + if (sys_mount_setattr(open_tree_fd, "", AT_EMPTY_PATH, &attr, sizeof(attr))) { + log_stderr("failure: sys_mount_setattr"); + goto out; + } + + supported = openat_tmpfile_supported(open_tree_fd); + + pid = fork(); + if (pid < 0) { + log_stderr("failure: fork"); + goto out; + } + if (pid == 0) { + /* If the parent directory has a default acl then permissions are based off + * of that and current_umask() is ignored. Specifically, if the ACL has an + * ACL_MASK entry, the group permissions correspond to the permissions of + * the ACL_MASK entry. + */ + umask(S_IXGRP); + snprintf(t_buf, sizeof(t_buf), "setfacl -d -m u::rwx,g::rwx,o::rwx,m:rw %s/%s", t_mountpoint, T_DIR1); + if (system(t_buf)) + die("failure: system"); + + if (!switch_ids(10000, 11000)) + die("failure: switch_ids"); + + file1_fd = openat(open_tree_fd, FILE1, O_CREAT | O_EXCL | O_CLOEXEC, S_IXGRP | S_ISGID); + if (file1_fd < 0) + die("failure: create"); + + /* Neither in_group_p() nor capable_wrt_inode_uidgid() so setgid + * bit needs to be stripped. + */ + if (is_setgid(open_tree_fd, FILE1, 0)) + die("failure: is_setgid"); + + /* create directory */ + if (mkdirat(open_tree_fd, DIR1, 0000)) + die("failure: create"); + + if (xfs_irix_sgid_inherit_enabled()) { + /* We're not in_group_p(). */ + if (is_setgid(open_tree_fd, DIR1, 0)) + die("failure: is_setgid"); + } else { + /* Directories always inherit the setgid bit. */ + if (!is_setgid(open_tree_fd, DIR1, 0)) + die("failure: is_setgid"); + } + + /* create a special file via mknodat() vfs_create */ + if (mknodat(open_tree_fd, FILE2, S_IFREG | S_ISGID | S_IXGRP, 0)) + die("failure: mknodat"); + + if (is_setgid(open_tree_fd, FILE2, 0)) + die("failure: is_setgid"); + + /* create a whiteout device via mknodat() vfs_mknod */ + if (mknodat(open_tree_fd, CHRDEV1, S_IFCHR | S_ISGID | S_IXGRP, 0)) + die("failure: mknodat"); + + if (is_setgid(open_tree_fd, CHRDEV1, 0)) + die("failure: is_setgid"); + + if (unlinkat(open_tree_fd, FILE1, 0)) + die("failure: delete"); + + if (unlinkat(open_tree_fd, DIR1, AT_REMOVEDIR)) + die("failure: delete"); + + if (unlinkat(open_tree_fd, FILE2, 0)) + die("failure: delete"); + + if (unlinkat(open_tree_fd, CHRDEV1, 0)) + die("failure: delete"); + + /* create tmpfile via filesystem tmpfile api */ + if (supported) { + tmpfile_fd = openat(open_tree_fd, ".", O_TMPFILE | O_RDWR, S_IXGRP | S_ISGID); + if (tmpfile_fd < 0) + die("failure: create"); + /* link the temporary file into the filesystem, making it permanent */ + snprintf(path, PATH_MAX, "/proc/self/fd/%d", tmpfile_fd); + if (linkat(AT_FDCWD, path, open_tree_fd, FILE3, AT_SYMLINK_FOLLOW)) + die("failure: linkat"); + if (close(tmpfile_fd)) + die("failure: close"); + if (is_setgid(open_tree_fd, FILE3, 0)) + die("failure: is_setgid"); + if (unlinkat(open_tree_fd, FILE3, 0)) + die("failure: delete"); + } + exit(EXIT_SUCCESS); + } + if (wait_for_pid(pid)) + goto out; + + pid = fork(); + if (pid < 0) { + log_stderr("failure: fork"); + goto out; + } + if (pid == 0) { + /* If the parent directory has a default acl then permissions are based off + * of that and current_umask() is ignored. Specifically, if the ACL has an + * ACL_MASK entry, the group permissions correspond to the permissions of + * the ACL_MASK entry. Otherwise, if the ACL has no ACL_MASK entry, the + * group permissions correspond to the permissions of the ACL_GROUP_OBJ + * entry. + */ + umask(S_IXGRP); + snprintf(t_buf, sizeof(t_buf), "setfacl -d -m u::rwx,g::rw,o::rwx, %s/%s", t_mountpoint, T_DIR1); + if (system(t_buf)) + die("failure: system"); + + if (!switch_ids(10000, 11000)) + die("failure: switch_ids"); + + /* create regular file via open() */ + file1_fd = openat(open_tree_fd, FILE1, O_CREAT | O_EXCL | O_CLOEXEC, S_IXGRP | S_ISGID); + if (file1_fd < 0) + die("failure: create"); + + /* Neither in_group_p() nor capable_wrt_inode_uidgid() so setgid + * bit needs to be stripped. + */ + if (is_setgid(open_tree_fd, FILE1, 0)) + die("failure: is_setgid"); + + /* create directory */ + if (mkdirat(open_tree_fd, DIR1, 0000)) + die("failure: create"); + + if (xfs_irix_sgid_inherit_enabled()) { + /* We're not in_group_p(). */ + if (is_setgid(open_tree_fd, DIR1, 0)) + die("failure: is_setgid"); + } else { + /* Directories always inherit the setgid bit. */ + if (!is_setgid(open_tree_fd, DIR1, 0)) + die("failure: is_setgid"); + } + + /* create a special file via mknodat() vfs_create */ + if (mknodat(open_tree_fd, FILE2, S_IFREG | S_ISGID | S_IXGRP, 0)) + die("failure: mknodat"); + + if (is_setgid(open_tree_fd, FILE2, 0)) + die("failure: is_setgid"); + + /* create a character device via mknodat() vfs_mknod */ + if (mknodat(open_tree_fd, CHRDEV1, S_IFCHR | S_ISGID | S_IXGRP, 0)) + die("failure: mknodat"); + + if (is_setgid(open_tree_fd, CHRDEV1, 0)) + die("failure: is_setgid"); + + if (unlinkat(open_tree_fd, FILE1, 0)) + die("failure: delete"); + + if (unlinkat(open_tree_fd, DIR1, AT_REMOVEDIR)) + die("failure: delete"); + + if (unlinkat(open_tree_fd, FILE2, 0)) + die("failure: delete"); + + if (unlinkat(open_tree_fd, CHRDEV1, 0)) + die("failure: delete"); + + /* create tmpfile via filesystem tmpfile api */ + if (supported) { + tmpfile_fd = openat(open_tree_fd, ".", O_TMPFILE | O_RDWR, S_IXGRP | S_ISGID); + if (tmpfile_fd < 0) + die("failure: create"); + /* link the temporary file into the filesystem, making it permanent */ + snprintf(path, PATH_MAX, "/proc/self/fd/%d", tmpfile_fd); + if (linkat(AT_FDCWD, path, open_tree_fd, FILE3, AT_SYMLINK_FOLLOW)) + die("failure: linkat"); + if (close(tmpfile_fd)) + die("failure: close"); + if (is_setgid(open_tree_fd, FILE3, 0)) + die("failure: is_setgid"); + if (unlinkat(open_tree_fd, FILE3, 0)) + die("failure: delete"); + } + exit(EXIT_SUCCESS); + } + if (wait_for_pid(pid)) + goto out; + + fret = 0; + log_debug("Ran test"); +out: + safe_close(attr.userns_fd); + safe_close(file1_fd); + safe_close(open_tree_fd); + + return fret; +} + +static int setgid_create_acl_idmapped_in_userns(void) +{ + int fret = -1; + int file1_fd = -EBADF, open_tree_fd = -EBADF; + struct mount_attr attr = { + .attr_set = MOUNT_ATTR_IDMAP, + }; + pid_t pid; + int tmpfile_fd = -EBADF; + bool supported = false; + char path[PATH_MAX]; + + if (fchmod(t_dir1_fd, S_IRUSR | + S_IWUSR | + S_IRGRP | + S_IWGRP | + S_IROTH | + S_IWOTH | + S_IXUSR | + S_IXGRP | + S_IXOTH | + S_ISGID), 0) { + log_stderr("failure: fchmod"); + goto out; + } + + /* Verify that the sid bits got raised. */ + if (!is_setgid(t_dir1_fd, "", AT_EMPTY_PATH)) { + log_stderr("failure: is_setgid"); + goto out; + } + + /* Changing mount properties on a detached mount. */ + attr.userns_fd = get_userns_fd(0, 10000, 10000); + if (attr.userns_fd < 0) { + log_stderr("failure: get_userns_fd"); + goto out; + } + + open_tree_fd = sys_open_tree(t_dir1_fd, "", + AT_EMPTY_PATH | + AT_NO_AUTOMOUNT | + AT_SYMLINK_NOFOLLOW | + OPEN_TREE_CLOEXEC | + OPEN_TREE_CLONE); + if (open_tree_fd < 0) { + log_stderr("failure: sys_open_tree"); + goto out; + } + + if (sys_mount_setattr(open_tree_fd, "", AT_EMPTY_PATH, &attr, sizeof(attr))) { + log_stderr("failure: sys_mount_setattr"); + goto out; + } + + supported = openat_tmpfile_supported(open_tree_fd); + + /* Below we verify that setgid inheritance for a newly created file or + * directory works correctly. As part of this we need to verify that + * newly created files or directories inherit their gid from their + * parent directory. So we change the parent directorie's gid to 1000 + * and create a file with fs{g,u}id 0 and verify that the newly created + * file and directory inherit gid 1000, not 0. + */ + if (fchownat(t_dir1_fd, "", -1, 1000, AT_SYMLINK_NOFOLLOW | AT_EMPTY_PATH)) { + log_stderr("failure: fchownat"); + goto out; + } + + pid = fork(); + if (pid < 0) { + log_stderr("failure: fork"); + goto out; + } + if (pid == 0) { + /* If the parent directory has a default acl then permissions are based off + * of that and current_umask() is ignored. Specifically, if the ACL has an + * ACL_MASK entry, the group permissions correspond to the permissions of + * the ACL_MASK entry. + */ + umask(S_IXGRP); + snprintf(t_buf, sizeof(t_buf), "setfacl -d -m u::rwx,g::rwx,o::rwx,m:rw %s/%s", t_mountpoint, T_DIR1); + if (system(t_buf)) + die("failure: system"); + + if (!switch_userns(attr.userns_fd, 0, 0, false)) + die("failure: switch_userns"); + + if (!caps_down_fsetid()) + die("failure: caps_down_fsetid"); + + /* create regular file via open() */ + file1_fd = openat(open_tree_fd, FILE1, O_CREAT | O_EXCL | O_CLOEXEC, S_IXGRP | S_ISGID); + if (file1_fd < 0) + die("failure: create"); + + /* Neither in_group_p() nor capable_wrt_inode_uidgid() so setgid + * bit needs to be stripped. + */ + if (is_setgid(open_tree_fd, FILE1, 0)) + die("failure: is_setgid"); + + /* create directory */ + if (mkdirat(open_tree_fd, DIR1, 0000)) + die("failure: create"); + + if (xfs_irix_sgid_inherit_enabled()) { + /* We're not in_group_p(). */ + if (is_setgid(open_tree_fd, DIR1, 0)) + die("failure: is_setgid"); + } else { + /* Directories always inherit the setgid bit. */ + if (!is_setgid(open_tree_fd, DIR1, 0)) + die("failure: is_setgid"); + } + + /* create a special file via mknodat() vfs_create */ + if (mknodat(open_tree_fd, FILE2, S_IFREG | S_ISGID | S_IXGRP, 0)) + die("failure: mknodat"); + + if (is_setgid(open_tree_fd, FILE2, 0)) + die("failure: is_setgid"); + + /* create a whiteout device via mknodat() vfs_mknod */ + if (mknodat(open_tree_fd, CHRDEV1, S_IFCHR | S_ISGID | S_IXGRP, 0)) + die("failure: mknodat"); + + if (is_setgid(open_tree_fd, CHRDEV1, 0)) + die("failure: is_setgid"); + + if (unlinkat(open_tree_fd, FILE1, 0)) + die("failure: delete"); + + if (unlinkat(open_tree_fd, DIR1, AT_REMOVEDIR)) + die("failure: delete"); + + if (unlinkat(open_tree_fd, FILE2, 0)) + die("failure: delete"); + + if (unlinkat(open_tree_fd, CHRDEV1, 0)) + die("failure: delete"); + + /* create tmpfile via filesystem tmpfile api */ + if (supported) { + tmpfile_fd = openat(open_tree_fd, ".", O_TMPFILE | O_RDWR, S_IXGRP | S_ISGID); + if (tmpfile_fd < 0) + die("failure: create"); + /* link the temporary file into the filesystem, making it permanent */ + snprintf(path, PATH_MAX, "/proc/self/fd/%d", tmpfile_fd); + if (linkat(AT_FDCWD, path, open_tree_fd, FILE3, AT_SYMLINK_FOLLOW)) + die("failure: linkat"); + if (close(tmpfile_fd)) + die("failure: close"); + if (is_setgid(open_tree_fd, FILE3, 0)) + die("failure: is_setgid"); + if (unlinkat(open_tree_fd, FILE3, 0)) + die("failure: delete"); + } + exit(EXIT_SUCCESS); + } + if (wait_for_pid(pid)) + goto out; + + pid = fork(); + if (pid < 0) { + log_stderr("failure: fork"); + goto out; + } + if (pid == 0) { + /* If the parent directory has a default acl then permissions are based off + * of that and current_umask() is ignored. Specifically, if the ACL has an + * ACL_MASK entry, the group permissions correspond to the permissions of + * the ACL_MASK entry. Otherwise, if the ACL has no ACL_MASK entry, the + * group permissions correspond to the permissions of the ACL_GROUP_OBJ + * entry. + */ + umask(S_IXGRP); + snprintf(t_buf, sizeof(t_buf), "setfacl -d -m u::rwx,g::rw,o::rwx %s/%s", t_mountpoint, T_DIR1); + if (system(t_buf)) + die("failure: system"); + + if (!switch_userns(attr.userns_fd, 0, 0, false)) + die("failure: switch_userns"); + + if (!caps_down_fsetid()) + die("failure: caps_down_fsetid"); + + /* create regular file via open() */ + file1_fd = openat(open_tree_fd, FILE1, O_CREAT | O_EXCL | O_CLOEXEC, S_IXGRP | S_ISGID); + if (file1_fd < 0) + die("failure: create"); + + /* Neither in_group_p() nor capable_wrt_inode_uidgid() so setgid + * bit needs to be stripped. + */ + if (is_setgid(open_tree_fd, FILE1, 0)) + die("failure: is_setgid"); + + /* create directory */ + if (mkdirat(open_tree_fd, DIR1, 0000)) + die("failure: create"); + + if (xfs_irix_sgid_inherit_enabled()) { + /* We're not in_group_p(). */ + if (is_setgid(open_tree_fd, DIR1, 0)) + die("failure: is_setgid"); + } else { + /* Directories always inherit the setgid bit. */ + if (!is_setgid(open_tree_fd, DIR1, 0)) + die("failure: is_setgid"); + } + + /* create a special file via mknodat() vfs_create */ + if (mknodat(open_tree_fd, FILE2, S_IFREG | S_ISGID | S_IXGRP, 0)) + die("failure: mknodat"); + + if (is_setgid(open_tree_fd, FILE2, 0)) + die("failure: is_setgid"); + + /* create a whiteout device via mknodat() vfs_mknod */ + if (mknodat(open_tree_fd, CHRDEV1, S_IFCHR | S_ISGID | S_IXGRP, 0)) + die("failure: mknodat"); + + if (is_setgid(open_tree_fd, CHRDEV1, 0)) + die("failure: is_setgid"); + + if (unlinkat(open_tree_fd, FILE1, 0)) + die("failure: delete"); + + if (unlinkat(open_tree_fd, DIR1, AT_REMOVEDIR)) + die("failure: delete"); + + if (unlinkat(open_tree_fd, FILE2, 0)) + die("failure: delete"); + + if (unlinkat(open_tree_fd, CHRDEV1, 0)) + die("failure: delete"); + + /* create tmpfile via filesystem tmpfile api */ + if (supported) { + tmpfile_fd = openat(open_tree_fd, ".", O_TMPFILE | O_RDWR, S_IXGRP | S_ISGID); + if (tmpfile_fd < 0) + die("failure: create"); + /* link the temporary file into the filesystem, making it permanent */ + snprintf(path, PATH_MAX, "/proc/self/fd/%d", tmpfile_fd); + if (linkat(AT_FDCWD, path, open_tree_fd, FILE3, AT_SYMLINK_FOLLOW)) + die("failure: linkat"); + if (close(tmpfile_fd)) + die("failure: close"); + if (is_setgid(open_tree_fd, FILE3, 0)) + die("failure: is_setgid"); + if (unlinkat(open_tree_fd, FILE3, 0)) + die("failure: delete"); + } + exit(EXIT_SUCCESS); + } + if (wait_for_pid(pid)) + goto out; + + fret = 0; + log_debug("Ran test"); +out: + safe_close(attr.userns_fd); + safe_close(file1_fd); + safe_close(open_tree_fd); + + return fret; +} + static void usage(void) { fprintf(stderr, "Description:\n"); @@ -14653,6 +15428,7 @@ static void usage(void) fprintf(stderr, "--test-btrfs Run btrfs specific idmapped mount testsuite\n"); fprintf(stderr, "--test-setattr-fix-968219708108 Run setattr regression tests\n"); fprintf(stderr, "--test-setgid-umask Run setgid create umask test\n"); + fprintf(stderr, "--test-setgid-acl Run setgid create acl tests\n"); _exit(EXIT_SUCCESS); } @@ -14671,6 +15447,7 @@ static const struct option longopts[] = { {"test-btrfs", no_argument, 0, 'b'}, {"test-setattr-fix-968219708108", no_argument, 0, 'i'}, {"test-setgid-umask", no_argument, 0, 'u'}, + {"test-setgid-acl", no_argument, 0, 'l'}, {NULL, 0, 0, 0}, }; @@ -14774,6 +15551,12 @@ struct t_idmapped_mounts t_setgid_umask[] = { { setgid_create_umask_idmapped_in_userns, true, "create operations by using umask in directories with setgid bit set on idmapped mounts inside userns", }, }; +struct t_idmapped_mounts t_setgid_acl[] = { + { setgid_create_acl, false, "create operations by using acl in directories with setgid bit set", }, + { setgid_create_acl_idmapped, false, "create operations by using acl in directories with setgid bit set on idmapped mount", }, + { setgid_create_acl_idmapped_in_userns, false, "create operations by using acl in directories with setgid bit set on idmapped mount inside userns", }, +}; + static bool run_test(struct t_idmapped_mounts suite[], size_t suite_size) { int i; @@ -14851,7 +15634,8 @@ int main(int argc, char *argv[]) int index = 0; bool supported = false, test_btrfs = false, test_core = false, test_fscaps_regression = false, test_nested_userns = false, - test_setattr_fix_968219708108 = false, test_setgid_umask = false; + test_setattr_fix_968219708108 = false, test_setgid_umask = false, + test_setgid_acl = false; while ((ret = getopt_long_only(argc, argv, "", longopts, &index)) != -1) { switch (ret) { @@ -14891,6 +15675,9 @@ int main(int argc, char *argv[]) case 'u': test_setgid_umask = true; break; + case 'l': + test_setgid_acl = true; + break; case 'h': /* fallthrough */ default: @@ -14966,6 +15753,10 @@ int main(int argc, char *argv[]) !run_test(t_setgid_umask, ARRAY_SIZE(t_setgid_umask))) goto out; + if (test_setgid_acl && + !run_test(t_setgid_acl, ARRAY_SIZE(t_setgid_acl))) + goto out; + fret = EXIT_SUCCESS; out: diff --git a/tests/generic/681 b/tests/generic/681 new file mode 100755 index 00000000..a78ca2d1 --- /dev/null +++ b/tests/generic/681 @@ -0,0 +1,28 @@ +#! /bin/bash +# SPDX-License-Identifier: GPL-2.0 +# Copyright (c) 2022 Fujitsu Limited. All Rights Reserved. +# +# FS QA Test 681 +# +# Test that idmapped mounts setgid's behave correctly when using acl. +# +. ./common/preamble +_begin_fstest auto quick acl cap idmapped mount perms rw unlink + +# Import common functions. +. ./common/filter +. ./common/attr + +# real QA test starts here + +_supported_fs generic +_require_test +_require_acls + +echo "Silence is golden" + +$here/src/idmapped-mounts/idmapped-mounts --test-setgid-acl --device "$TEST_DEV" \ + --mount "$TEST_DIR" --fstype "$FSTYP" + +status=$? +exit diff --git a/tests/generic/681.out b/tests/generic/681.out new file mode 100644 index 00000000..15274cbc --- /dev/null +++ b/tests/generic/681.out @@ -0,0 +1,2 @@ +QA output created by 681 +Silence is golden