[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <20220621082313.3330667-9-konstantin.meskhidze@huawei.com>
Date: Tue, 21 Jun 2022 16:23:04 +0800
From: Konstantin Meskhidze <konstantin.meskhidze@...wei.com>
To: <mic@...ikod.net>
CC: <willemdebruijn.kernel@...il.com>,
<linux-security-module@...r.kernel.org>, <netdev@...r.kernel.org>,
<netfilter-devel@...r.kernel.org>, <yusongping@...wei.com>,
<anton.sirazetdinov@...wei.com>
Subject: [PATCH v6 08/17] landlock: adds support network rules
This commit adds network rules support
in internal landlock functions
(presented in ruleset.c) and
landlock_create_ruleset syscall.
Signed-off-by: Konstantin Meskhidze <konstantin.meskhidze@...wei.com>
---
Changes since v5:
* Gets rid of partial revert from landlock_add_rule
syscall.
* Formats code with clang-format-14.
Changes since v4:
* Refactors landlock_create_ruleset() - splits ruleset and
masks checks.
* Refactors landlock_create_ruleset() and landlock mask
setters/getters to support two rule types.
* Refactors landlock_add_rule syscall add_rule_path_beneath
function by factoring out get_ruleset_from_fd() and
landlock_put_ruleset().
Changes since v3:
* Splits commit.
* Adds network rule support for internal landlock functions.
* Adds set_mask and get_mask for network.
* Adds rb_root root_net_port.
---
security/landlock/limits.h | 8 +++-
security/landlock/ruleset.c | 78 +++++++++++++++++++++++++++++++-----
security/landlock/ruleset.h | 31 ++++++++++++--
security/landlock/syscalls.c | 8 +++-
4 files changed, 111 insertions(+), 14 deletions(-)
diff --git a/security/landlock/limits.h b/security/landlock/limits.h
index b54184ab9439..23694bf05cb7 100644
--- a/security/landlock/limits.h
+++ b/security/landlock/limits.h
@@ -22,6 +22,12 @@
#define LANDLOCK_MASK_ACCESS_FS ((LANDLOCK_LAST_ACCESS_FS << 1) - 1)
#define LANDLOCK_NUM_ACCESS_FS __const_hweight64(LANDLOCK_MASK_ACCESS_FS)
-/* clang-format on */
+#define LANDLOCK_LAST_ACCESS_NET LANDLOCK_ACCESS_NET_CONNECT_TCP
+#define LANDLOCK_MASK_ACCESS_NET ((LANDLOCK_LAST_ACCESS_NET << 1) - 1)
+#define LANDLOCK_NUM_ACCESS_NET __const_hweight64(LANDLOCK_MASK_ACCESS_NET)
+#define LANDLOCK_MASK_SHIFT_NET 16
+
+#define LANDLOCK_RULE_TYPE_NUM LANDLOCK_RULE_NET_SERVICE
+/* clang-format on */
#endif /* _SECURITY_LANDLOCK_LIMITS_H */
diff --git a/security/landlock/ruleset.c b/security/landlock/ruleset.c
index cbca85f5cc6d..6ca6373b3950 100644
--- a/security/landlock/ruleset.c
+++ b/security/landlock/ruleset.c
@@ -36,6 +36,7 @@ static struct landlock_ruleset *create_ruleset(const u32 num_layers)
refcount_set(&new_ruleset->usage, 1);
mutex_init(&new_ruleset->lock);
new_ruleset->root_inode = RB_ROOT;
+ new_ruleset->root_net_port = RB_ROOT;
new_ruleset->num_layers = num_layers;
/*
* hierarchy = NULL
@@ -46,16 +47,21 @@ static struct landlock_ruleset *create_ruleset(const u32 num_layers)
}
struct landlock_ruleset *
-landlock_create_ruleset(const access_mask_t access_mask)
+landlock_create_ruleset(const access_mask_t access_mask_fs,
+ const access_mask_t access_mask_net)
{
struct landlock_ruleset *new_ruleset;
/* Informs about useless ruleset. */
- if (!access_mask)
+ if (!access_mask_fs && !access_mask_net)
return ERR_PTR(-ENOMSG);
new_ruleset = create_ruleset(1);
- if (!IS_ERR(new_ruleset))
- landlock_set_fs_access_mask(new_ruleset, access_mask, 0);
+ if (IS_ERR(new_ruleset))
+ return new_ruleset;
+ if (access_mask_fs)
+ landlock_set_fs_access_mask(new_ruleset, access_mask_fs, 0);
+ if (access_mask_net)
+ landlock_set_net_access_mask(new_ruleset, access_mask_net, 0);
return new_ruleset;
}
@@ -92,9 +98,11 @@ create_rule(struct landlock_object *const object_ptr,
return ERR_PTR(-ENOMEM);
RB_CLEAR_NODE(&new_rule->node);
- if (object_ptr) {
+ if (object_ptr && !object_data) {
landlock_get_object(object_ptr);
new_rule->object.ptr = object_ptr;
+ } else if (object_data && !object_ptr) {
+ new_rule->object.data = object_data;
} else if (object_ptr && object_data) {
WARN_ON_ONCE(1);
return ERR_PTR(-EINVAL);
@@ -130,10 +138,12 @@ static void build_check_ruleset(void)
.num_layers = ~0,
};
typeof(ruleset.access_masks[0]) fs_access_mask = ~0;
+ typeof(ruleset.access_masks[0]) net_access_mask = ~0;
BUILD_BUG_ON(ruleset.num_rules < LANDLOCK_MAX_NUM_RULES);
BUILD_BUG_ON(ruleset.num_layers < LANDLOCK_MAX_NUM_LAYERS);
BUILD_BUG_ON(fs_access_mask < LANDLOCK_MASK_ACCESS_FS);
+ BUILD_BUG_ON(net_access_mask < LANDLOCK_MASK_ACCESS_NET);
}
/**
@@ -179,6 +189,11 @@ static int insert_rule(struct landlock_ruleset *const ruleset,
object_data = (uintptr_t)object_ptr;
root = &ruleset->root_inode;
break;
+ case LANDLOCK_RULE_NET_SERVICE:
+ if (WARN_ON_ONCE(object_ptr))
+ return -EINVAL;
+ root = &ruleset->root_net_port;
+ break;
default:
WARN_ON_ONCE(1);
return -EINVAL;
@@ -232,6 +247,15 @@ static int insert_rule(struct landlock_ruleset *const ruleset,
&ruleset->root_inode);
free_rule(this, rule_type);
break;
+ case LANDLOCK_RULE_NET_SERVICE:
+ new_rule = create_rule(NULL, object_data, &this->layers,
+ this->num_layers, &(*layers)[0]);
+ if (IS_ERR(new_rule))
+ return PTR_ERR(new_rule);
+ rb_replace_node(&this->node, &new_rule->node,
+ &ruleset->root_net_port);
+ free_rule(this, rule_type);
+ break;
}
return 0;
}
@@ -249,6 +273,15 @@ static int insert_rule(struct landlock_ruleset *const ruleset,
rb_insert_color(&new_rule->node, &ruleset->root_inode);
ruleset->num_rules++;
break;
+ case LANDLOCK_RULE_NET_SERVICE:
+ new_rule = create_rule(NULL, object_data, layers, num_layers,
+ NULL);
+ if (IS_ERR(new_rule))
+ return PTR_ERR(new_rule);
+ rb_link_node(&new_rule->node, parent_node, walker_node);
+ rb_insert_color(&new_rule->node, &ruleset->root_net_port);
+ ruleset->num_rules++;
+ break;
}
return 0;
}
@@ -309,6 +342,9 @@ static int tree_merge(struct landlock_ruleset *const src,
case LANDLOCK_RULE_PATH_BENEATH:
src_root = &src->root_inode;
break;
+ case LANDLOCK_RULE_NET_SERVICE:
+ src_root = &src->root_net_port;
+ break;
default:
return -EINVAL;
}
@@ -335,6 +371,11 @@ static int tree_merge(struct landlock_ruleset *const src,
rule_type, &layers,
ARRAY_SIZE(layers));
break;
+ case LANDLOCK_RULE_NET_SERVICE:
+ err = insert_rule(dst, NULL, walker_rule->object.data,
+ rule_type, &layers,
+ ARRAY_SIZE(layers));
+ break;
}
if (err)
return err;
@@ -370,6 +411,10 @@ static int merge_ruleset(struct landlock_ruleset *const dst,
err = tree_merge(src, dst, LANDLOCK_RULE_PATH_BENEATH);
if (err)
goto out_unlock;
+ /* Merges the @src network tree. */
+ err = tree_merge(src, dst, LANDLOCK_RULE_NET_SERVICE);
+ if (err)
+ goto out_unlock;
out_unlock:
mutex_unlock(&src->lock);
@@ -389,10 +434,13 @@ static int tree_copy(struct landlock_ruleset *const parent,
case LANDLOCK_RULE_PATH_BENEATH:
parent_root = &parent->root_inode;
break;
+ case LANDLOCK_RULE_NET_SERVICE:
+ parent_root = &parent->root_net_port;
+ break;
default:
return -EINVAL;
}
- /* Copies the @parent inode tree. */
+ /* Copies the @parent inode or network tree. */
rbtree_postorder_for_each_entry_safe(walker_rule, next_rule,
parent_root, node) {
switch (rule_type) {
@@ -401,6 +449,11 @@ static int tree_copy(struct landlock_ruleset *const parent,
rule_type, &walker_rule->layers,
walker_rule->num_layers);
break;
+ case LANDLOCK_RULE_NET_SERVICE:
+ err = insert_rule(child, NULL, walker_rule->object.data,
+ rule_type, &walker_rule->layers,
+ walker_rule->num_layers);
+ break;
}
if (err)
return err;
@@ -423,6 +476,10 @@ static int inherit_ruleset(struct landlock_ruleset *const parent,
/* Copies the @parent inode tree. */
err = tree_copy(parent, child, LANDLOCK_RULE_PATH_BENEATH);
+ if (err)
+ goto out_unlock;
+ /* Copies the @parent network tree. */
+ err = tree_copy(parent, child, LANDLOCK_RULE_NET_SERVICE);
if (err)
goto out_unlock;
@@ -458,6 +515,9 @@ static void free_ruleset(struct landlock_ruleset *const ruleset)
rbtree_postorder_for_each_entry_safe(freeme, next, &ruleset->root_inode,
node)
free_rule(freeme, LANDLOCK_RULE_PATH_BENEATH);
+ rbtree_postorder_for_each_entry_safe(freeme, next,
+ &ruleset->root_net_port, node)
+ free_rule(freeme, LANDLOCK_RULE_NET_SERVICE);
put_hierarchy(ruleset->hierarchy);
kfree(ruleset);
}
@@ -552,13 +612,13 @@ landlock_find_rule(const struct landlock_ruleset *const ruleset,
{
const struct rb_node *node;
- if (!object_data)
- return NULL;
-
switch (rule_type) {
case LANDLOCK_RULE_PATH_BENEATH:
node = ruleset->root_inode.rb_node;
break;
+ case LANDLOCK_RULE_NET_SERVICE:
+ node = ruleset->root_net_port.rb_node;
+ break;
default:
WARN_ON_ONCE(1);
return NULL;
diff --git a/security/landlock/ruleset.h b/security/landlock/ruleset.h
index c1cf7cce2cb5..0cedfe65e326 100644
--- a/security/landlock/ruleset.h
+++ b/security/landlock/ruleset.h
@@ -101,6 +101,12 @@ struct landlock_ruleset {
* tree is immutable until @usage reaches zero.
*/
struct rb_root root_inode;
+ /**
+ * @root_net_port: Root of a red-black tree containing object nodes
+ * for network port. Once a ruleset is tied to a process (i.e. as a domain),
+ * this tree is immutable until @usage reaches zero.
+ */
+ struct rb_root root_net_port;
/**
* @hierarchy: Enables hierarchy identification even when a parent
* domain vanishes. This is needed for the ptrace protection.
@@ -156,7 +162,8 @@ struct landlock_ruleset {
};
struct landlock_ruleset *
-landlock_create_ruleset(const access_mask_t access_mask);
+landlock_create_ruleset(const access_mask_t access_mask_fs,
+ const access_mask_t access_mask_net);
void landlock_put_ruleset(struct landlock_ruleset *const ruleset);
void landlock_put_ruleset_deferred(struct landlock_ruleset *const ruleset);
@@ -183,9 +190,9 @@ static inline void landlock_get_ruleset(struct landlock_ruleset *const ruleset)
/* A helper function to set a filesystem mask. */
static inline void
landlock_set_fs_access_mask(struct landlock_ruleset *ruleset,
- const access_mask_t access_maskset, u16 mask_level)
+ const access_mask_t access_mask_fs, u16 mask_level)
{
- ruleset->access_masks[mask_level] = access_maskset;
+ ruleset->access_masks[mask_level] = access_mask_fs;
}
/* A helper function to get a filesystem mask. */
@@ -196,6 +203,24 @@ landlock_get_fs_access_mask(const struct landlock_ruleset *ruleset,
return (ruleset->access_masks[mask_level] & LANDLOCK_MASK_ACCESS_FS);
}
+/* A helper function to set a network mask. */
+static inline void
+landlock_set_net_access_mask(struct landlock_ruleset *ruleset,
+ const access_mask_t access_mask_net,
+ u16 mask_level)
+{
+ ruleset->access_masks[mask_level] |=
+ (access_mask_net << LANDLOCK_MASK_SHIFT_NET);
+}
+
+/* A helper function to get a network mask. */
+static inline u32
+landlock_get_net_access_mask(const struct landlock_ruleset *ruleset,
+ u16 mask_level)
+{
+ return (ruleset->access_masks[mask_level] >> LANDLOCK_MASK_SHIFT_NET);
+}
+
access_mask_t get_handled_accesses(const struct landlock_ruleset *const domain,
u16 rule_type, u16 num_access);
diff --git a/security/landlock/syscalls.c b/security/landlock/syscalls.c
index 246bc48deba3..72fa01ba9de7 100644
--- a/security/landlock/syscalls.c
+++ b/security/landlock/syscalls.c
@@ -189,8 +189,14 @@ SYSCALL_DEFINE3(landlock_create_ruleset,
LANDLOCK_MASK_ACCESS_FS)
return -EINVAL;
+ /* Checks network content (and 32-bits cast). */
+ if ((ruleset_attr.handled_access_net | LANDLOCK_MASK_ACCESS_NET) !=
+ LANDLOCK_MASK_ACCESS_NET)
+ return -EINVAL;
+
/* Checks arguments and transforms to kernel struct. */
- ruleset = landlock_create_ruleset(ruleset_attr.handled_access_fs);
+ ruleset = landlock_create_ruleset(ruleset_attr.handled_access_fs,
+ ruleset_attr.handled_access_net);
if (IS_ERR(ruleset))
return PTR_ERR(ruleset);
--
2.25.1
Powered by blists - more mailing lists