From: Simon THOBY <git@nightmared.fr>
To: linux-security-module@vger.kernel.org
Cc: Simon THOBY <git@nightmared.fr>,
linux-integrity@vger.kernel.org, linux-doc@vger.kernel.org
Subject: [RFC PATCH 3/9] Loadpol LSM: filter kernel module request according to the policy
Date: Wed, 21 May 2025 16:01:07 +0200 [thread overview]
Message-ID: <20250521140121.591482-4-git@nightmared.fr> (raw)
In-Reply-To: <20250521140121.591482-1-git@nightmared.fr>
When a kernel module is loaded, the LSM accepts or rejects the demand
according to its policy.
Signed-off-by: Simon THOBY <git@nightmared.fr>
---
security/loadpol/Makefile | 2 +-
security/loadpol/loadpol.c | 22 ++++++++++++
security/loadpol/loadpol.h | 27 ++++++++++++++
security/loadpol/loadpol_policy.c | 59 +++++++++++++++++++++++++++++++
4 files changed, 109 insertions(+), 1 deletion(-)
create mode 100644 security/loadpol/loadpol_policy.c
diff --git a/security/loadpol/Makefile b/security/loadpol/Makefile
index a794c8cfbfee..062215e1f831 100644
--- a/security/loadpol/Makefile
+++ b/security/loadpol/Makefile
@@ -1 +1 @@
-obj-$(CONFIG_SECURITY_LOADPOL) := loadpol.o
+obj-$(CONFIG_SECURITY_LOADPOL) := loadpol.o loadpol_policy.o
diff --git a/security/loadpol/loadpol.c b/security/loadpol/loadpol.c
index 3fc29263e2f8..4d1a495a1462 100644
--- a/security/loadpol/loadpol.c
+++ b/security/loadpol/loadpol.c
@@ -6,6 +6,15 @@
#include "loadpol.h"
+// default policy: allow all modules
+static struct loadpol_policy_entry default_policy_entries[] __ro_after_init = {
+ {
+ .origin = (ORIGIN_KERNEL | ORIGIN_USERSPACE),
+ .action = ACTION_ALLOW,
+ .module_name = NULL,
+ },
+};
+
static int __init loadpol_init(void);
static const struct lsm_id loadpol_lsmid = {
@@ -14,6 +23,7 @@ static const struct lsm_id loadpol_lsmid = {
};
static struct security_hook_list loadpol_hooks[] __ro_after_init = {
+ LSM_HOOK_INIT(kernel_module_load, loadpol_kernel_module_load),
};
DEFINE_LSM(LOADPOL_NAME) = {
@@ -23,6 +33,18 @@ DEFINE_LSM(LOADPOL_NAME) = {
static int __init loadpol_init(void)
{
+ for (int i = 0; i < ARRAY_SIZE(default_policy_entries); i++) {
+ struct loadpol_policy_entry *entry = kmemdup(
+ &default_policy_entries[i],
+ sizeof(struct loadpol_policy_entry),
+ GFP_KERNEL
+ );
+ if (!entry)
+ return -ENOMEM;
+
+ list_add_tail(&entry->list, loadpol_policy);
+ }
+
security_add_hooks(loadpol_hooks, ARRAY_SIZE(loadpol_hooks), &loadpol_lsmid);
pr_info("Loadpol started.\n");
return 0;
diff --git a/security/loadpol/loadpol.h b/security/loadpol/loadpol.h
index 5e11474191f0..a81d52f6d4da 100644
--- a/security/loadpol/loadpol.h
+++ b/security/loadpol/loadpol.h
@@ -3,6 +3,33 @@
#ifndef _SECURITY_LOADPOL_LOADPOL_H
#define _SECURITY_LOADPOL_LOADPOL_H
+#include "linux/list.h"
+
#define LOADPOL_NAME "loadpol"
+enum policy_entry_origin {
+ ORIGIN_KERNEL = 1 << 0,
+ ORIGIN_USERSPACE = 1 << 1,
+};
+
+enum __packed policy_entry_action {
+ ACTION_UNDEFINED,
+ ACTION_ALLOW,
+ ACTION_DENY
+};
+
+struct loadpol_policy_entry {
+ struct list_head list;
+ // bitfield of policy_entry_origin
+ u8 origin;
+ enum policy_entry_action action;
+ // when NULL, the policy apply to every module
+ char *module_name;
+};
+
+extern struct list_head __rcu *loadpol_policy;
+
+// evaluate if a kernel module called 'kmod' is allowed to be loaded in the kernel
+int loadpol_kernel_module_load(const char *kmod);
+
#endif /* _SECURITY_LOADPOL_LOADPOL_H */
diff --git a/security/loadpol/loadpol_policy.c b/security/loadpol/loadpol_policy.c
new file mode 100644
index 000000000000..6ba5ab600e3e
--- /dev/null
+++ b/security/loadpol/loadpol_policy.c
@@ -0,0 +1,59 @@
+// SPDX-License-Identifier: GPL-2.0-only
+
+#include "linux/rculist.h"
+#include <linux/sched.h>
+#include <linux/sysctl.h>
+#include <linux/parser.h>
+
+#include "loadpol.h"
+
+/* use A/B policy entries: switch from one to the next every time the policy get overwritten */
+static LIST_HEAD(loadpol_policy_a);
+static LIST_HEAD(loadpol_policy_b);
+struct list_head __rcu *loadpol_policy = (struct list_head __rcu *)(&loadpol_policy_a);
+
+int loadpol_kernel_module_load(const char *kmod)
+{
+ struct task_struct *parent_task;
+ struct loadpol_policy_entry *entry;
+ struct list_head *policy_list_tmp;
+ enum policy_entry_origin orig = ORIGIN_USERSPACE;
+ bool allowed = false;
+
+ rcu_read_lock();
+ parent_task = rcu_dereference(current->parent);
+ /* the parent of the current task is a workqueue -> the request comes from the kernel */
+ if (parent_task && (parent_task->flags & PF_WQ_WORKER))
+ orig = ORIGIN_KERNEL;
+ rcu_read_unlock();
+
+ pr_debug("Loadpol: trying to load '%s' (asked by %s)",
+ kmod,
+ orig == ORIGIN_KERNEL ? "kernel" : "userspace");
+
+ rcu_read_lock();
+ policy_list_tmp = rcu_dereference(loadpol_policy);
+ list_for_each_entry_rcu(entry, policy_list_tmp, list) {
+ /* the requestor does not match */
+ if ((orig & entry->origin) == 0)
+ continue;
+
+ allowed = entry->action == ACTION_ALLOW;
+
+ if (!entry->module_name)
+ goto unlock_and_exit;
+
+ if (entry->module_name && match_wildcard(entry->module_name, kmod))
+ goto unlock_and_exit;
+ }
+
+ /* No match -> reject the demand */
+ allowed = false;
+
+unlock_and_exit:
+ rcu_read_unlock();
+
+ pr_debug("Loadpol: load of module '%s' %s", kmod, allowed ? "allowed" : "blocked");
+
+ return allowed ? 0 : -EPERM;
+}
--
2.49.0
next prev parent reply other threads:[~2025-05-21 14:06 UTC|newest]
Thread overview: 22+ messages / expand[flat|nested] mbox.gz Atom feed top
2025-05-21 14:01 [RFC PATCH 0/9] Introducing the Loadpol LSM Simon THOBY
2025-05-21 14:01 ` [RFC PATCH 1/9] LSM: Introduce a new hook: security_kernel_module_load Simon THOBY
2025-05-21 22:03 ` Serge E. Hallyn
2025-05-22 8:57 ` Simon Thoby
2025-05-21 14:01 ` [RFC PATCH 2/9] Introduce a new LSM: loadpol Simon THOBY
2025-05-21 14:01 ` Simon THOBY [this message]
2025-05-21 15:47 ` [RFC PATCH 3/9] Loadpol LSM: filter kernel module request according to the policy Casey Schaufler
2025-05-21 16:21 ` Randy Dunlap
2025-05-21 16:26 ` Simon Thoby
2025-05-21 14:01 ` [RFC PATCH 4/9] Loadpol LSM: add a file in securityfs to read/modify " Simon THOBY
2025-05-21 14:01 ` [RFC PATCH 5/9] Loadpol LSM: add a sysctl to lock " Simon THOBY
2025-05-21 14:01 ` [RFC PATCH 6/9] Loadpol LSM: emit an audit log Simon THOBY
2025-05-21 14:01 ` [RFC PATCH 7/9] module: expose the list of blacklisted modules Simon THOBY
2025-05-21 14:01 ` [RFC PATCH 8/9] Loadpol LSM: include the blacklisted kernel modules in the policy Simon THOBY
2025-05-21 14:01 ` [RFC PATCH 9/9] Loadpol LSM: add a minimal documentation Simon THOBY
2025-05-21 16:26 ` Randy Dunlap
2025-05-21 16:29 ` Simon Thoby
2025-05-21 21:31 ` Paul Moore
2025-05-22 9:23 ` Simon Thoby
2025-05-29 23:49 ` Paul Moore
2025-05-30 7:03 ` Simon Thoby
2025-05-30 14:59 ` Paul Moore
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20250521140121.591482-4-git@nightmared.fr \
--to=git@nightmared.fr \
--cc=linux-doc@vger.kernel.org \
--cc=linux-integrity@vger.kernel.org \
--cc=linux-security-module@vger.kernel.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.