[PATCH v3 2/8] fs/acl: Add ACL mask interaction tests
Sachin Sant <[email protected]>
| Newsgroups | gmane.linux.ltp |
|---|---|
| Message-ID | <[email protected]> |
Add acl_mask01 test with 3 test cases to validate ACL_MASK interaction with named user and group entries: 1. ACL_USER with mask - Named user permissions restricted by mask 2. ACL_GROUP with mask - Named group permissions restricted by mask 3. ACL_GROUP_OBJ with mask - Group owner permissions restricted by mask Each test verifies that: - With mask set to rwx, access is granted - With mask cleared (---), access is denied with EACCES This validates that ACL_MASK correctly restricts permissions for ACL_USER, ACL_GROUP, and ACL_GROUP_OBJ entries. Suggested-by: Cyril Hrubis <[email protected]> Signed-off-by: Sachin Sant <[email protected]> --- V3 changes: - Switch to kernel only test validation to remove dependency on libacl and useradd/del commands. - v2 link https://lore.kernel.org/ltp/20260604065417.25924-1-sachinp-tEXmvtCZX7AybS5Ee8rs3A@public.gmane.org/T/#t V2 changes: - Updated copyright header as per LTP format. - v1 link https://lore.kernel.org/ltp/20260602121958.27494-1-sachinp-tEXmvtCZX7AybS5Ee8rs3A@public.gmane.org/T/#t V1 changes: - Use HAVE_LIBACL guards in .c code - Report TCONF when libacl is not available - rfc link https://lore.kernel.org/ltp/477836fd-80c8-4168-bfe6-00b374bb2534-tEXmvtCZX7AybS5Ee8rs3A@public.gmane.org/T/#t --- runtest/fs | 1 + testcases/kernel/fs/acl/.gitignore | 1 + testcases/kernel/fs/acl/acl_mask01.c | 372 +++++++++++++++++++++++++++ 3 files changed, 374 insertions(+) create mode 100644 testcases/kernel/fs/acl/acl_mask01.c diff --git a/runtest/fs b/runtest/fs index 2a878744b..69ecb8647 100644 --- a/runtest/fs +++ b/runtest/fs @@ -90,3 +90,4 @@ squashfs01 squashfs01 # Run the acl tests acl_user_obj01 acl_user_obj01 +acl_mask01 acl_mask01 diff --git a/testcases/kernel/fs/acl/.gitignore b/testcases/kernel/fs/acl/.gitignore index d9c46db11..bfcdee93d 100644 --- a/testcases/kernel/fs/acl/.gitignore +++ b/testcases/kernel/fs/acl/.gitignore @@ -1 +1,2 @@ /acl_user_obj01 +/acl_mask01 diff --git a/testcases/kernel/fs/acl/acl_mask01.c b/testcases/kernel/fs/acl/acl_mask01.c new file mode 100644 index 000000000..ca690ba6a --- /dev/null +++ b/testcases/kernel/fs/acl/acl_mask01.c @@ -0,0 +1,372 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (c) 2026 IBM + * + * Original shell test by Kai Zhao ([email protected]) + * Converted to C by Sachin Sant <[email protected]> + */ + +/*\ + * Test ACL mask interaction with named users and groups using direct xattr + * manipulation. + * + * Verify that ACL_MASK correctly restricts permissions for: + * - ACL_USER (named user) entries + * - ACL_GROUP (named group) entries + * - ACL_GROUP_OBJ (group owner) entries + * + * The mask acts as an upper bound on permissions for these entry types. + * Even if an entry grants full permissions, the mask can restrict them. + * ACL_USER_OBJ and ACL_OTHER are not affected by the mask. + * + * This test uses arbitrary UIDs without creating actual users, testing + * only the kernel ACL implementation. + * + * [Algorithm] + * + * For each entry type (ACL_USER, ACL_GROUP, ACL_GROUP_OBJ): + * 1. Set up ACL with full permissions for the entry + * 2. Set mask to allow full permissions (rwx) + * 3. Verify access is granted + * 4. Clear mask permissions (---) + * 5. Verify access is denied despite entry having full permissions + */ + +#include "acl_lib.h" + +#define TEST_UID 1000 +#define TEST_GID 1000 +#define USER2_UID 2000 +#define USER2_GID 2000 +#define USER3_UID 3000 +#define USER3_GID 3000 + +/* + * Test ACL_USER permissions with mask. + * Named user permissions should be restricted by ACL_MASK. + */ +static void test_acl_user_with_mask(void) +{ + struct acl *acl = NULL; + int err; + + tst_res(TINFO, "Testing ACL_USER with mask"); + reset_test_path(); + + SAFE_CHOWN(TESTDIR, TEST_UID, TEST_GID); + + acl = acl_init(); + if (!acl) + tst_brk(TBROK | TERRNO, "acl_init failed"); + + if (acl_add_entry(acl, ACL_USER_OBJ, + ACL_READ | ACL_WRITE | ACL_EXECUTE, 0) < 0) + goto cleanup_acl; + + if (acl_add_entry(acl, ACL_USER, + ACL_READ | ACL_WRITE | ACL_EXECUTE, USER3_UID) < 0) + goto cleanup_acl; + + if (acl_add_entry(acl, ACL_GROUP_OBJ, 0, 0) < 0) + goto cleanup_acl; + + if (acl_add_entry(acl, ACL_MASK, + ACL_READ | ACL_WRITE | ACL_EXECUTE, 0) < 0) + goto cleanup_acl; + + if (acl_add_entry(acl, ACL_OTHER, 0, 0) < 0) + goto cleanup_acl; + + if (acl_set_file(TESTDIR, ACL_TYPE_ACCESS, acl) < 0) { + if (errno == EOPNOTSUPP) { + acl_free(acl); + tst_brk(TCONF | TERRNO, "ACL not supported"); + } + goto cleanup_acl; + } + + acl_free(acl); + acl = NULL; + + err = try_create_as(USER3_UID, USER3_GID, 0644); + if (err) { + errno = err; + tst_res(TFAIL | TERRNO, + "ACL_USER with mask rwx should allow access"); + return; + } + + cleanup_testfile(); + + /* Clear mask permissions */ + acl = acl_get_file(TESTDIR, ACL_TYPE_ACCESS); + if (!acl) + tst_brk(TBROK | TERRNO, "acl_get_file failed"); + + if (acl_set_mask_perms(acl, 0) < 0) { + acl_free(acl); + tst_brk(TBROK | TERRNO, "acl_set_mask_perms failed"); + } + + if (acl_set_file(TESTDIR, ACL_TYPE_ACCESS, acl) < 0) { + acl_free(acl); + tst_brk(TBROK | TERRNO, "acl_set_file failed"); + } + + acl_free(acl); + + err = try_create_as(USER3_UID, USER3_GID, 0644); + if (!err) { + cleanup_testfile(); + tst_res(TFAIL, "ACL_USER with mask --- should deny access"); + return; + } + + if (err != EACCES) { + errno = err; + tst_res(TFAIL | TERRNO, "Expected EACCES from named user"); + return; + } + + tst_res(TPASS, "ACL_USER with mask works correctly"); + return; + +cleanup_acl: + acl_free(acl); + tst_brk(TBROK | TERRNO, "ACL setup failed"); +} + +/* + * Test ACL_GROUP permissions with mask. + * Named group permissions should be restricted by ACL_MASK. + */ +static void test_acl_group_with_mask(void) +{ + struct acl *acl = NULL; + int err; + + tst_res(TINFO, "Testing ACL_GROUP with mask"); + reset_test_path(); + + SAFE_CHOWN(TESTDIR, TEST_UID, TEST_GID); + + acl = acl_init(); + if (!acl) + tst_brk(TBROK | TERRNO, "acl_init failed"); + + if (acl_add_entry(acl, ACL_USER_OBJ, + ACL_READ | ACL_WRITE | ACL_EXECUTE, 0) < 0) + goto cleanup_acl; + + if (acl_add_entry(acl, ACL_GROUP_OBJ, 0, 0) < 0) + goto cleanup_acl; + + if (acl_add_entry(acl, ACL_GROUP, + ACL_READ | ACL_WRITE | ACL_EXECUTE, USER2_GID) < 0) + goto cleanup_acl; + + if (acl_add_entry(acl, ACL_MASK, + ACL_READ | ACL_WRITE | ACL_EXECUTE, 0) < 0) + goto cleanup_acl; + + if (acl_add_entry(acl, ACL_OTHER, 0, 0) < 0) + goto cleanup_acl; + + if (acl_set_file(TESTDIR, ACL_TYPE_ACCESS, acl) < 0) { + if (errno == EOPNOTSUPP) { + acl_free(acl); + tst_brk(TCONF | TERRNO, "ACL not supported"); + } + goto cleanup_acl; + } + + acl_free(acl); + acl = NULL; + + err = try_create_as(USER2_UID, USER2_GID, 0644); + if (err) { + errno = err; + tst_res(TFAIL | TERRNO, + "ACL_GROUP with mask rwx should allow access"); + return; + } + + cleanup_testfile(); + + /* Clear mask permissions */ + acl = acl_get_file(TESTDIR, ACL_TYPE_ACCESS); + if (!acl) + tst_brk(TBROK | TERRNO, "acl_get_file failed"); + + if (acl_set_mask_perms(acl, 0) < 0) { + acl_free(acl); + tst_brk(TBROK | TERRNO, "acl_set_mask_perms failed"); + } + + if (acl_set_file(TESTDIR, ACL_TYPE_ACCESS, acl) < 0) { + acl_free(acl); + tst_brk(TBROK | TERRNO, "acl_set_file failed"); + } + + acl_free(acl); + + err = try_create_as(USER2_UID, USER2_GID, 0644); + if (!err) { + cleanup_testfile(); + tst_res(TFAIL, "ACL_GROUP with mask --- should deny access"); + return; + } + + if (err != EACCES) { + errno = err; + tst_res(TFAIL | TERRNO, "Expected EACCES from named group"); + return; + } + + tst_res(TPASS, "ACL_GROUP with mask works correctly"); + return; + +cleanup_acl: + acl_free(acl); + tst_brk(TBROK | TERRNO, "ACL setup failed"); +} + +/* + * Test ACL_GROUP_OBJ permissions with mask. + * Group owner permissions should be restricted by ACL_MASK. + */ +static void test_acl_group_obj_with_mask(void) +{ + struct acl *acl = NULL; + int err; + + tst_res(TINFO, "Testing ACL_GROUP_OBJ with mask"); + reset_test_path(); + + SAFE_CHOWN(TESTDIR, TEST_UID, USER2_GID); + + acl = acl_init(); + if (!acl) + tst_brk(TBROK | TERRNO, "acl_init failed"); + + if (acl_add_entry(acl, ACL_USER_OBJ, + ACL_READ | ACL_WRITE | ACL_EXECUTE, 0) < 0) + goto cleanup_acl; + + if (acl_add_entry(acl, ACL_GROUP_OBJ, + ACL_READ | ACL_WRITE | ACL_EXECUTE, 0) < 0) + goto cleanup_acl; + + if (acl_add_entry(acl, ACL_MASK, + ACL_READ | ACL_WRITE | ACL_EXECUTE, 0) < 0) + goto cleanup_acl; + + if (acl_add_entry(acl, ACL_OTHER, 0, 0) < 0) + goto cleanup_acl; + + if (acl_set_file(TESTDIR, ACL_TYPE_ACCESS, acl) < 0) { + if (errno == EOPNOTSUPP) { + acl_free(acl); + tst_brk(TCONF | TERRNO, "ACL not supported"); + } + goto cleanup_acl; + } + + acl_free(acl); + acl = NULL; + + err = try_create_as(USER2_UID, USER2_GID, 0644); + if (err) { + errno = err; + tst_res(TFAIL | TERRNO, + "ACL_GROUP_OBJ with mask rwx should allow access"); + return; + } + + cleanup_testfile(); + + /* Clear mask permissions */ + acl = acl_get_file(TESTDIR, ACL_TYPE_ACCESS); + if (!acl) + tst_brk(TBROK | TERRNO, "acl_get_file failed"); + + if (acl_set_mask_perms(acl, 0) < 0) { + acl_free(acl); + tst_brk(TBROK | TERRNO, "acl_set_mask_perms failed"); + } + + if (acl_set_file(TESTDIR, ACL_TYPE_ACCESS, acl) < 0) { + acl_free(acl); + tst_brk(TBROK | TERRNO, "acl_set_file failed"); + } + + acl_free(acl); + + err = try_create_as(USER2_UID, USER2_GID, 0644); + if (!err) { + cleanup_testfile(); + SAFE_CHOWN(TESTDIR, TEST_UID, TEST_GID); + tst_res(TFAIL, + "ACL_GROUP_OBJ with mask --- should deny access"); + return; + } + + if (err != EACCES) { + SAFE_CHOWN(TESTDIR, TEST_UID, TEST_GID); + errno = err; + tst_res(TFAIL | TERRNO, "Expected EACCES from group owner"); + return; + } + + SAFE_CHOWN(TESTDIR, TEST_UID, TEST_GID); + tst_res(TPASS, "ACL_GROUP_OBJ with mask works correctly"); + return; + +cleanup_acl: + acl_free(acl); + tst_brk(TBROK | TERRNO, "ACL setup failed"); +} + +static void setup(void) +{ + reset_test_path(); +} + +static void cleanup(void) +{ + cleanup_test_paths(); +} + +static void run(unsigned int n) +{ + switch (n) { + case 0: + test_acl_user_with_mask(); + break; + case 1: + test_acl_group_with_mask(); + break; + case 2: + test_acl_group_obj_with_mask(); + break; + } +} + +static struct tst_test test = { + .test = run, + .tcnt = 3, + .setup = setup, + .cleanup = cleanup, + .needs_root = 1, + .mount_device = 1, + .mntpoint = MNTPOINT, + .forks_child = 1, + .filesystems = (struct tst_fs[]) { + {.type = "ext2", .mnt_data = "acl"}, + {.type = "ext3", .mnt_data = "acl"}, + {.type = "ext4", .mnt_data = "acl"}, + {.type = "xfs"}, + {.type = "btrfs"}, + {} + } +}; -- 2.39.1 -- Mailing list info: https://lists.linux.it/listinfo/ltp