[PATCH -next 3/5] landlock/selftests: add selftests for chmod and chown

Xiu Jianfeng xiujianfeng at huawei.com
Mon Aug 22 11:46:59 UTC 2022


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 <xiujianfeng at huawei.com>
---
 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



More information about the Linux-security-module-archive mailing list