* src_parent would result in having the same or fewer access rights if it were
* moved under new_parent.
*/
-static bool may_refer(const struct layer_access_masks *const src_parent,
- const struct layer_access_masks *const src_child,
- const struct layer_access_masks *const new_parent,
+static bool may_refer(const struct layer_masks *const src_parent,
+ const struct layer_masks *const src_child,
+ const struct layer_masks *const new_parent,
const bool child_is_dir)
{
- for (size_t i = 0; i < ARRAY_SIZE(new_parent->access); i++) {
- access_mask_t child_access = src_parent->access[i] &
- src_child->access[i];
- access_mask_t parent_access = new_parent->access[i];
+ for (size_t i = 0; i < ARRAY_SIZE(new_parent->layers); i++) {
+ access_mask_t child_access = src_parent->layers[i].access &
+ src_child->layers[i].access;
+ access_mask_t parent_access = new_parent->layers[i].access;
if (!child_is_dir) {
child_access &= ACCESS_FILE;
* that child2 may be used from parent2 to parent1 without increasing its access
* rights), false otherwise.
*/
-static bool no_more_access(const struct layer_access_masks *const parent1,
- const struct layer_access_masks *const child1,
+static bool no_more_access(const struct layer_masks *const parent1,
+ const struct layer_masks *const child1,
const bool child1_is_dir,
- const struct layer_access_masks *const parent2,
- const struct layer_access_masks *const child2,
+ const struct layer_masks *const parent2,
+ const struct layer_masks *const child2,
const bool child2_is_dir)
{
if (!may_refer(parent1, child1, parent2, child1_is_dir))
static void test_no_more_access(struct kunit *const test)
{
- const struct layer_access_masks rx0 = {
- .access[0] = LANDLOCK_ACCESS_FS_EXECUTE |
- LANDLOCK_ACCESS_FS_READ_FILE,
+ const struct layer_masks rx0 = {
+ .layers[0].access = LANDLOCK_ACCESS_FS_EXECUTE |
+ LANDLOCK_ACCESS_FS_READ_FILE,
};
- const struct layer_access_masks mx0 = {
- .access[0] = LANDLOCK_ACCESS_FS_EXECUTE |
- LANDLOCK_ACCESS_FS_MAKE_REG,
+ const struct layer_masks mx0 = {
+ .layers[0].access = LANDLOCK_ACCESS_FS_EXECUTE |
+ LANDLOCK_ACCESS_FS_MAKE_REG,
};
- const struct layer_access_masks x0 = {
- .access[0] = LANDLOCK_ACCESS_FS_EXECUTE,
+ const struct layer_masks x0 = {
+ .layers[0].access = LANDLOCK_ACCESS_FS_EXECUTE,
};
- const struct layer_access_masks x1 = {
- .access[1] = LANDLOCK_ACCESS_FS_EXECUTE,
+ const struct layer_masks x1 = {
+ .layers[1].access = LANDLOCK_ACCESS_FS_EXECUTE,
};
- const struct layer_access_masks x01 = {
- .access[0] = LANDLOCK_ACCESS_FS_EXECUTE,
- .access[1] = LANDLOCK_ACCESS_FS_EXECUTE,
+ const struct layer_masks x01 = {
+ .layers[0].access = LANDLOCK_ACCESS_FS_EXECUTE,
+ .layers[1].access = LANDLOCK_ACCESS_FS_EXECUTE,
};
- const struct layer_access_masks allows_all = {};
+ const struct layer_masks allows_all = {};
/* Checks without restriction. */
NMA_TRUE(&x0, &allows_all, false, &allows_all, NULL, false);
#undef NMA_TRUE
#undef NMA_FALSE
-static bool is_layer_masks_allowed(const struct layer_access_masks *masks)
+static bool is_layer_masks_allowed(const struct layer_masks *masks)
{
- return mem_is_zero(&masks->access, sizeof(masks->access));
+ for (size_t i = 0; i < ARRAY_SIZE(masks->layers); i++) {
+ if (masks->layers[i].access)
+ return false;
+ }
+ return true;
}
/*
* Returns true if the request is allowed, false otherwise.
*/
static bool scope_to_request(const access_mask_t access_request,
- struct layer_access_masks *masks)
+ struct layer_masks *masks)
{
bool saw_unfulfilled_access = false;
if (WARN_ON_ONCE(!masks))
return true;
- for (size_t i = 0; i < ARRAY_SIZE(masks->access); i++) {
- masks->access[i] &= access_request;
- if (masks->access[i])
+ for (size_t i = 0; i < ARRAY_SIZE(masks->layers); i++) {
+ masks->layers[i].access &= access_request;
+ if (masks->layers[i].access)
saw_unfulfilled_access = true;
}
return !saw_unfulfilled_access;
static void test_scope_to_request_with_exec_none(struct kunit *const test)
{
/* Allows everything. */
- struct layer_access_masks masks = {};
+ struct layer_masks masks = {};
/* Checks and scopes with execute. */
KUNIT_EXPECT_TRUE(test,
scope_to_request(LANDLOCK_ACCESS_FS_EXECUTE, &masks));
- KUNIT_EXPECT_EQ(test, 0, masks.access[0]);
+ KUNIT_EXPECT_EQ(test, 0, (access_mask_t)masks.layers[0].access);
}
static void test_scope_to_request_with_exec_some(struct kunit *const test)
{
/* Denies execute and write. */
- struct layer_access_masks masks = {
- .access[0] = LANDLOCK_ACCESS_FS_EXECUTE,
- .access[1] = LANDLOCK_ACCESS_FS_WRITE_FILE,
+ struct layer_masks masks = {
+ .layers[0].access = LANDLOCK_ACCESS_FS_EXECUTE,
+ .layers[1].access = LANDLOCK_ACCESS_FS_WRITE_FILE,
};
/* Checks and scopes with execute. */
KUNIT_EXPECT_FALSE(test, scope_to_request(LANDLOCK_ACCESS_FS_EXECUTE,
&masks));
- KUNIT_EXPECT_EQ(test, LANDLOCK_ACCESS_FS_EXECUTE, masks.access[0]);
- KUNIT_EXPECT_EQ(test, 0, masks.access[1]);
+ /*
+ * These casts to access_mask_t are needed because typeof(), used in
+ * KUNIT_EXPECT_EQ(), does not work on bitfields.
+ */
+ KUNIT_EXPECT_EQ(test, LANDLOCK_ACCESS_FS_EXECUTE,
+ (access_mask_t)masks.layers[0].access);
+ KUNIT_EXPECT_EQ(test, 0, (access_mask_t)masks.layers[1].access);
}
static void test_scope_to_request_without_access(struct kunit *const test)
{
/* Denies execute and write. */
- struct layer_access_masks masks = {
- .access[0] = LANDLOCK_ACCESS_FS_EXECUTE,
- .access[1] = LANDLOCK_ACCESS_FS_WRITE_FILE,
+ struct layer_masks masks = {
+ .layers[0].access = LANDLOCK_ACCESS_FS_EXECUTE,
+ .layers[1].access = LANDLOCK_ACCESS_FS_WRITE_FILE,
};
/* Checks and scopes without access request. */
KUNIT_EXPECT_TRUE(test, scope_to_request(0, &masks));
- KUNIT_EXPECT_EQ(test, 0, masks.access[0]);
- KUNIT_EXPECT_EQ(test, 0, masks.access[1]);
+ KUNIT_EXPECT_EQ(test, 0, (access_mask_t)masks.layers[0].access);
+ KUNIT_EXPECT_EQ(test, 0, (access_mask_t)masks.layers[1].access);
}
#endif /* CONFIG_SECURITY_LANDLOCK_KUNIT_TEST */
* Returns true if there is at least one access right different than
* LANDLOCK_ACCESS_FS_REFER.
*/
-static bool is_eacces(const struct layer_access_masks *masks,
+static bool is_eacces(const struct layer_masks *masks,
const access_mask_t access_request)
{
if (!masks)
return false;
- for (size_t i = 0; i < ARRAY_SIZE(masks->access); i++) {
+ for (size_t i = 0; i < ARRAY_SIZE(masks->layers); i++) {
/* LANDLOCK_ACCESS_FS_REFER alone must return -EXDEV. */
- if (masks->access[i] & access_request &
+ if (masks->layers[i].access & access_request &
~LANDLOCK_ACCESS_FS_REFER)
return true;
}
static void test_is_eacces_with_none(struct kunit *const test)
{
- const struct layer_access_masks masks = {};
+ const struct layer_masks masks = {};
IE_FALSE(&masks, 0);
IE_FALSE(&masks, LANDLOCK_ACCESS_FS_REFER);
static void test_is_eacces_with_refer(struct kunit *const test)
{
- const struct layer_access_masks masks = {
- .access[0] = LANDLOCK_ACCESS_FS_REFER,
+ const struct layer_masks masks = {
+ .layers[0].access = LANDLOCK_ACCESS_FS_REFER,
};
IE_FALSE(&masks, 0);
static void test_is_eacces_with_write(struct kunit *const test)
{
- const struct layer_access_masks masks = {
- .access[0] = LANDLOCK_ACCESS_FS_WRITE_FILE,
+ const struct layer_masks masks = {
+ .layers[0].access = LANDLOCK_ACCESS_FS_WRITE_FILE,
};
IE_FALSE(&masks, 0);
is_access_to_paths_allowed(const struct landlock_ruleset *const domain,
const struct path *const path,
const access_mask_t access_request_parent1,
- struct layer_access_masks *layer_masks_parent1,
+ struct layer_masks *layer_masks_parent1,
struct landlock_request *const log_request_parent1,
struct dentry *const dentry_child1,
const access_mask_t access_request_parent2,
- struct layer_access_masks *layer_masks_parent2,
+ struct layer_masks *layer_masks_parent2,
struct landlock_request *const log_request_parent2,
struct dentry *const dentry_child2)
{
child1_is_directory = true, child2_is_directory = true;
struct path walker_path;
access_mask_t access_masked_parent1, access_masked_parent2;
- struct layer_access_masks _layer_masks_child1, _layer_masks_child2;
- struct layer_access_masks *layer_masks_child1 = NULL,
- *layer_masks_child2 = NULL;
+ struct layer_masks _layer_masks_child1, _layer_masks_child2;
+ struct layer_masks *layer_masks_child1 = NULL,
+ *layer_masks_child2 = NULL;
if (!access_request_parent1 && !access_request_parent2)
return true;
}
if (unlikely(dentry_child1)) {
+ /*
+ * Get the layer masks for the child dentries for use by domain
+ * check later.
+ */
if (landlock_init_layer_masks(domain, LANDLOCK_MASK_ACCESS_FS,
&_layer_masks_child1,
LANDLOCK_KEY_INODE))
};
const struct landlock_cred_security *const subject =
landlock_get_applicable_subject(current_cred(), masks, NULL);
- struct layer_access_masks layer_masks;
+ struct layer_masks layer_masks;
struct landlock_request request = {};
if (!subject)
static bool collect_domain_accesses(const struct landlock_ruleset *const domain,
const struct dentry *const mnt_root,
struct dentry *dir,
- struct layer_access_masks *layer_masks_dom)
+ struct layer_masks *layer_masks_dom)
{
bool ret = false;
access_mask_t access_request_parent1, access_request_parent2;
struct path mnt_dir;
struct dentry *old_parent;
- struct layer_access_masks layer_masks_parent1 = {},
- layer_masks_parent2 = {};
+ struct layer_masks layer_masks_parent1 = {}, layer_masks_parent2 = {};
struct landlock_request request1 = {}, request2 = {};
if (!subject)
allow_parent2 = collect_domain_accesses(subject->domain, mnt_dir.dentry,
new_dir->dentry,
&layer_masks_parent2);
-
if (allow_parent1 && allow_parent2)
return 0;
*/
static void unmask_scoped_access(const struct landlock_ruleset *const client,
const struct landlock_ruleset *const server,
- struct layer_access_masks *const masks,
+ struct layer_masks *const masks,
const access_mask_t access)
{
int client_layer, server_layer;
server_walker = server_walker->parent;
for (; client_layer >= 0; client_layer--) {
- if (masks->access[client_layer] & access &&
+ if (masks->layers[client_layer].access & access &&
client_walker == server_walker)
- masks->access[client_layer] &= ~access;
+ masks->layers[client_layer].access &= ~access;
client_walker = client_walker->parent;
server_walker = server_walker->parent;
{
const struct landlock_ruleset *dom_other;
const struct landlock_cred_security *subject;
- struct layer_access_masks layer_masks;
+ struct layer_masks layer_masks;
struct landlock_request request = {};
static const struct access_masks fs_resolve_unix = {
.fs = LANDLOCK_ACCESS_FS_RESOLVE_UNIX,
static int hook_file_open(struct file *const file)
{
- struct layer_access_masks layer_masks = {};
+ struct layer_masks layer_masks = {};
access_mask_t open_access_request, full_access_request, allowed_access,
optional_access;
const struct landlock_cred_security *const subject =
* are still unfulfilled in any of the layers.
*/
allowed_access = full_access_request;
- for (size_t i = 0; i < ARRAY_SIZE(layer_masks.access); i++)
- allowed_access &= ~layer_masks.access[i];
+ for (size_t i = 0; i < ARRAY_SIZE(layer_masks.layers); i++)
+ allowed_access &= ~layer_masks.layers[i].access;
}
/*