Received: by 2002:a05:6358:5282:b0:b5:90e7:25cb with SMTP id g2csp2249841rwa; Mon, 22 Aug 2022 04:57:59 -0700 (PDT) X-Google-Smtp-Source: AA6agR7CUQluJISqgPxPWV/Da6zD9e/DlT8ZM9uQ9Yk7qpqUKAJ9oAciWWerol/Te0dwooqrX+L+ X-Received: by 2002:a17:906:8446:b0:730:ab78:2353 with SMTP id e6-20020a170906844600b00730ab782353mr12771179ejy.625.1661169478971; Mon, 22 Aug 2022 04:57:58 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1661169478; cv=none; d=google.com; s=arc-20160816; b=WJ/c2DpZGY5WDfl0xhV8fMhkl/hIXr2CJhzboq4Q3G/Vn99WoX8XywVZi3S0zr7Oe2 bzJBMjWl/Xh8biwi+3RQw2cMP03wKYOvfqyVtiNevr1wSgnAbYG8rvhxINQQeOPnZlEr LxLe+FBpoZSy7B1wj3gHoir7yCG4WZrnd9iz5y8i30lA1pOvEaDoFBHMpRZoPefD71g6 vlvp+b/v847lIAMi0wnIN5UlfpJUNUKtJtK776OoVxYaz6Oqn2w67gE4ZPXTtBsqx15i elOB7wQ2p/2Ffclp31VLhYbSATHCYJzo+RPrwd7JLFORwqwI5e7V36RIoqwIFJx6Kfwp 7uLA== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:mime-version:references:in-reply-to:message-id :date:subject:cc:to:from; bh=dttybs4ExoBMLxCxBfcaHa3QdVNLFLae8EbbYQ37trA=; b=D8V80J6ABi/IT9sUOgYlSqVO+MF7keFL22zfFI0JVot0dNQE3lAuSQdbOxMpGVtkJP ascqo9N7jvoOzVItozw4Z8Gi3qjgWwPma4eHo1TfC8+uGX4Qk2YcC1mC5Ekm3q1VCIky OcFXMXkv4JgmNiRa3kFEExu9sb3TW0Zb2XgSzkEs0cx7Cf2YPnCQMWDQuViZyoUAgYgx LxOYZkyIDPgCQuMuZJL9ItZL2K53KkOJyUPM/hRxdPJtTqoFT8CepjFomeGPYVnuUaQn Ci71xzhmyWRoqAs7c0Qrm79FoniNz+UOiLHxv/Rk3L/yksBauxJgE6+pXXdh4g/lRWNH XhhQ== ARC-Authentication-Results: i=1; mx.google.com; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=fail (p=QUARANTINE sp=QUARANTINE dis=NONE) header.from=huawei.com Return-Path: Received: from out1.vger.email (out1.vger.email. [2620:137:e000::1:20]) by mx.google.com with ESMTP id dn8-20020a17090794c800b00730753784b1si10588668ejc.45.2022.08.22.04.57.33; Mon, 22 Aug 2022 04:57:58 -0700 (PDT) Received-SPF: pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) client-ip=2620:137:e000::1:20; Authentication-Results: mx.google.com; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=fail (p=QUARANTINE sp=QUARANTINE dis=NONE) header.from=huawei.com Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S229882AbiHVLvv (ORCPT + 99 others); Mon, 22 Aug 2022 07:51:51 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:52532 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S234866AbiHVLuR (ORCPT ); Mon, 22 Aug 2022 07:50:17 -0400 Received: from szxga03-in.huawei.com (szxga03-in.huawei.com [45.249.212.189]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id C34CE18E2D; Mon, 22 Aug 2022 04:50:15 -0700 (PDT) Received: from dggpeml500023.china.huawei.com (unknown [172.30.72.55]) by szxga03-in.huawei.com (SkyGuard) with ESMTP id 4MB9c35BfHzGpmK; Mon, 22 Aug 2022 19:48:35 +0800 (CST) Received: from ubuntu1804.huawei.com (10.67.174.58) by dggpeml500023.china.huawei.com (7.185.36.114) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256) id 15.1.2375.24; Mon, 22 Aug 2022 19:50:13 +0800 From: Xiu Jianfeng To: , , , , , CC: , , , Subject: [PATCH -next 3/5] landlock/selftests: add selftests for chmod and chown Date: Mon, 22 Aug 2022 19:46:59 +0800 Message-ID: <20220822114701.26975-4-xiujianfeng@huawei.com> X-Mailer: git-send-email 2.17.1 In-Reply-To: <20220822114701.26975-1-xiujianfeng@huawei.com> References: <20220822114701.26975-1-xiujianfeng@huawei.com> MIME-Version: 1.0 Content-Type: text/plain X-Originating-IP: [10.67.174.58] X-ClientProxiedBy: dggems706-chm.china.huawei.com (10.3.19.183) To dggpeml500023.china.huawei.com (7.185.36.114) X-CFilter-Loop: Reflected X-Spam-Status: No, score=-4.2 required=5.0 tests=BAYES_00,RCVD_IN_DNSWL_MED, SPF_HELO_NONE,SPF_PASS,T_SCC_BODY_TEXT_LINE autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on lindbergh.monkeyblade.net Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Add the following simple testcases: 1. chmod/fchmod: remove S_IWUSR and restore S_IWUSR with or without restriction. 2. chown/fchown: set original uid and gid with or without restriction, because chown needs CAP_CHOWN and testcase framework don't have this capability, setting original uid and gid is ok to cover landlock function. Signed-off-by: Xiu Jianfeng --- tools/testing/selftests/landlock/fs_test.c | 228 +++++++++++++++++++++ 1 file changed, 228 insertions(+) diff --git a/tools/testing/selftests/landlock/fs_test.c b/tools/testing/selftests/landlock/fs_test.c index 5b55b93b5570..f47b4ccd2b26 100644 --- a/tools/testing/selftests/landlock/fs_test.c +++ b/tools/testing/selftests/landlock/fs_test.c @@ -59,6 +59,9 @@ static const char file2_s2d3[] = TMP_DIR "/s2d1/s2d2/s2d3/f2"; static const char dir_s3d1[] = TMP_DIR "/s3d1"; static const char file1_s3d1[] = TMP_DIR "/s3d1/f1"; +static const char file2_s3d1[] = TMP_DIR "/s3d1/f2"; +static const char file3_s3d1[] = TMP_DIR "/s3d1/f3"; + /* dir_s3d2 is a mount point. */ static const char dir_s3d2[] = TMP_DIR "/s3d1/s3d2"; static const char dir_s3d3[] = TMP_DIR "/s3d1/s3d2/s3d3"; @@ -211,6 +214,8 @@ static void create_layout1(struct __test_metadata *const _metadata) create_file(_metadata, file2_s2d3); create_file(_metadata, file1_s3d1); + create_file(_metadata, file2_s3d1); + create_file(_metadata, file3_s3d1); create_directory(_metadata, dir_s3d2); set_cap(_metadata, CAP_SYS_ADMIN); ASSERT_EQ(0, mount("tmp", dir_s3d2, "tmpfs", 0, "size=4m,mode=700")); @@ -234,6 +239,8 @@ static void remove_layout1(struct __test_metadata *const _metadata) EXPECT_EQ(0, remove_path(file1_s2d1)); EXPECT_EQ(0, remove_path(file1_s3d1)); + EXPECT_EQ(0, remove_path(file2_s3d1)); + EXPECT_EQ(0, remove_path(file3_s3d1)); EXPECT_EQ(0, remove_path(dir_s3d3)); set_cap(_metadata, CAP_SYS_ADMIN); umount(dir_s3d2); @@ -3272,6 +3279,227 @@ TEST_F_FORK(layout1, truncate) EXPECT_EQ(0, test_creat(file_in_dir_w)); } +static int test_chmod(const char *path) +{ + int ret; + struct stat st; + mode_t mode; + + ret = stat(path, &st); + if (ret < 0) + return errno; + /* save original mode in order to restore */ + mode = st.st_mode & 0777; + /* remove S_IWUSR */ + ret = chmod(path, mode & ~0200); + if (ret < 0) + return errno; + ret = stat(path, &st); + if (ret < 0) + return errno; + /* check if still has S_IWUSR */ + if (st.st_mode & 0200) + return -EFAULT; + /* restore the original mode */ + ret = chmod(path, mode); + if (ret < 0) + return errno; + return 0; +} + +static int test_fchmod(const char *path) +{ + int ret, fd; + struct stat st; + mode_t mode; + + ret = stat(path, &st); + if (ret < 0) + return errno; + /* save original mode in order to restore */ + mode = st.st_mode & 0777; + + fd = openat(AT_FDCWD, path, O_RDWR | O_CLOEXEC); + if (fd < 0) + return errno; + /* remove S_IWUSR */ + ret = fchmod(fd, mode & ~0200); + if (ret < 0) + goto err; + ret = stat(path, &st); + if (ret < 0) + goto err; + /* check if still has S_IWUSR */ + if (st.st_mode & 0200) { + ret = -1; + errno = -EFAULT; + goto err; + } + /* restore the original mode */ + ret = fchmod(fd, mode); +err: + if (close(fd) < 0) + return errno; + return ret ? errno : 0; +} + +static int test_chown(const char *path) +{ + int ret; + struct stat st; + + ret = stat(path, &st); + if (ret < 0) + return errno; + /* + * chown needs CAP_CHOWN to modify uid and/or gid, however + * there is no such capability when the testcases framework + * setup, so just chown to original uid/gid, which can also + * cover the function in landlock. + */ + ret = chown(path, st.st_uid, st.st_gid); + if (ret < 0) + return errno; + return 0; +} + +static int test_fchown(const char *path) +{ + int ret, fd; + struct stat st; + + ret = stat(path, &st); + if (ret < 0) + return errno; + fd = openat(AT_FDCWD, path, O_RDWR | O_CLOEXEC); + if (fd < 0) + return errno; + /* + * fchown needs CAP_CHOWN to modify uid and/or gid, however + * there is no such capability when the testcases framework + * setup, so just fchown to original uid/gid, which can also + * cover the function in landlock. + */ + ret = fchown(fd, st.st_uid, st.st_gid); + if (close(fd) < 0) + return errno; + return ret ? errno : 0; +} + +TEST_F_FORK(layout1, unhandled_chmod) +{ + const struct rule rules[] = { + { + .path = file2_s3d1, + .access = LANDLOCK_ACCESS_FS_READ_FILE | + LANDLOCK_ACCESS_FS_WRITE_FILE, + }, + { + .path = file3_s3d1, + .access = LANDLOCK_ACCESS_FS_READ_FILE | + LANDLOCK_ACCESS_FS_WRITE_FILE, + }, + {}, + }; + const int ruleset_fd = + create_ruleset(_metadata, ACCESS_RW, rules); + + ASSERT_LE(0, ruleset_fd); + enforce_ruleset(_metadata, ruleset_fd); + ASSERT_EQ(0, close(ruleset_fd)); + + ASSERT_EQ(0, test_chmod(file2_s3d1)); + ASSERT_EQ(0, test_fchmod(file2_s3d1)); + ASSERT_EQ(0, test_chmod(file3_s3d1)); + ASSERT_EQ(0, test_chmod(dir_s3d1)); +} + +TEST_F_FORK(layout1, chmod) +{ + const struct rule rules[] = { + { + .path = file2_s3d1, + .access = LANDLOCK_ACCESS_FS_READ_FILE | + LANDLOCK_ACCESS_FS_WRITE_FILE | + LANDLOCK_ACCESS_FS_CHMOD, + }, + { + .path = file3_s3d1, + .access = LANDLOCK_ACCESS_FS_READ_FILE | + LANDLOCK_ACCESS_FS_WRITE_FILE, + }, + {}, + }; + const int ruleset_fd = + create_ruleset(_metadata, ACCESS_RW | LANDLOCK_ACCESS_FS_CHMOD, rules); + + ASSERT_LE(0, ruleset_fd); + enforce_ruleset(_metadata, ruleset_fd); + ASSERT_EQ(0, close(ruleset_fd)); + + ASSERT_EQ(0, test_chmod(file2_s3d1)); + ASSERT_EQ(0, test_fchmod(file2_s3d1)); + ASSERT_EQ(EACCES, test_chmod(file3_s3d1)); + ASSERT_EQ(EACCES, test_chmod(dir_s3d1)); +} + +TEST_F_FORK(layout1, no_chown) +{ + const struct rule rules[] = { + { + .path = file2_s3d1, + .access = LANDLOCK_ACCESS_FS_READ_FILE | + LANDLOCK_ACCESS_FS_WRITE_FILE, + }, + { + .path = file3_s3d1, + .access = LANDLOCK_ACCESS_FS_READ_FILE | + LANDLOCK_ACCESS_FS_WRITE_FILE, + }, + {}, + }; + const int ruleset_fd = + create_ruleset(_metadata, ACCESS_RW, rules); + + ASSERT_LE(0, ruleset_fd); + enforce_ruleset(_metadata, ruleset_fd); + ASSERT_EQ(0, close(ruleset_fd)); + + ASSERT_EQ(0, test_chown(file2_s3d1)); + ASSERT_EQ(0, test_fchown(file2_s3d1)); + ASSERT_EQ(0, test_chown(file3_s3d1)); + ASSERT_EQ(0, test_chown(dir_s3d1)); +} + +TEST_F_FORK(layout1, chown) +{ + const struct rule rules[] = { + { + .path = file2_s3d1, + .access = LANDLOCK_ACCESS_FS_READ_FILE | + LANDLOCK_ACCESS_FS_WRITE_FILE | + LANDLOCK_ACCESS_FS_CHOWN, + }, + { + .path = file3_s3d1, + .access = LANDLOCK_ACCESS_FS_READ_FILE | + LANDLOCK_ACCESS_FS_WRITE_FILE, + }, + {}, + }; + const int ruleset_fd = + create_ruleset(_metadata, ACCESS_RW | LANDLOCK_ACCESS_FS_CHOWN, rules); + + ASSERT_LE(0, ruleset_fd); + enforce_ruleset(_metadata, ruleset_fd); + ASSERT_EQ(0, close(ruleset_fd)); + + ASSERT_EQ(0, test_chown(file2_s3d1)); + ASSERT_EQ(0, test_fchown(file2_s3d1)); + ASSERT_EQ(EACCES, test_chown(file3_s3d1)); + ASSERT_EQ(EACCES, test_chown(dir_s3d1)); +} + /* clang-format off */ FIXTURE(layout1_bind) {}; /* clang-format on */ -- 2.17.1