[PATCH v6 4/5] SELinux: add support for lsm_config_system_policy
Stephen Smalley
stephen.smalley.work at gmail.com
Fri Oct 10 13:58:56 UTC 2025
On Fri, Oct 10, 2025 at 9:27 AM Maxime Bélair
<maxime.belair at canonical.com> wrote:
>
> Enable users to manage SELinux policies through the new hook
> lsm_config_system_policy. This feature is restricted to CAP_MAC_ADMIN.
(added selinux mailing list and Fedora/Red Hat SELinux kernel maintainer to cc)
A couple of observations:
1. We do not currently require CAP_MAC_ADMIN for loading SELinux
policy, since it was only added later for Smack and SELinux implements
its own permission checks. When loading policy via selinuxfs, one
requires uid-0 or CAP_DAC_OVERRIDE to write to /sys/fs/selinux/load
plus the corresponding SELinux permissions, but this is just an
artifact of the filesystem-based interface. I'm not opposed to using
CAP_MAC_ADMIN for loading policy via the new system call but wanted to
note it as a difference.
2. The SELinux namespaces support [1], [2] is based on instantiating a
separate selinuxfs instance for each namespace; you load a policy for
a namespace by mounting a new selinuxfs instance after unsharing your
SELinux namespace and then write to its /sys/fs/selinux/load
interface, only affecting policy for the new namespace. Your interface
doesn't appear to support such an approach and IIUC will currently
always load the init SELinux namespace's policy rather than the
current process' SELinux namespace.
[1] https://github.com/stephensmalley/selinuxns
[2] https://lore.kernel.org/selinux/20250814132637.1659-1-stephen.smalley.work@gmail.com/
>
> Signed-off-by: Maxime Bélair <maxime.belair at canonical.com>
> ---
> security/selinux/hooks.c | 27 +++++++++++++++++++++++++++
> security/selinux/include/security.h | 7 +++++++
> security/selinux/selinuxfs.c | 16 ++++++++++++----
> 3 files changed, 46 insertions(+), 4 deletions(-)
>
> diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c
> index e7a7dcab81db..3d14d4e47937 100644
> --- a/security/selinux/hooks.c
> +++ b/security/selinux/hooks.c
> @@ -7196,6 +7196,31 @@ static int selinux_uring_allowed(void)
> }
> #endif /* CONFIG_IO_URING */
>
> +/**
> + * selinux_lsm_config_system_policy - Manage a LSM policy
> + * @op: operation to perform. Currently, only LSM_POLICY_LOAD is supported
> + * @buf: User-supplied buffer
> + * @size: size of @buf
> + * @flags: reserved for future use; must be zero
> + *
> + * Returns: number of written rules on success, negative value on error
> + */
> +static int selinux_lsm_config_system_policy(u32 op, void __user *buf,
> + size_t size, u32 flags)
> +{
> + loff_t pos = 0;
> +
> + if (op != LSM_POLICY_LOAD || flags)
> + return -EOPNOTSUPP;
> +
> + if (!selinux_null.dentry || !selinux_null.dentry->d_sb ||
> + !selinux_null.dentry->d_sb->s_fs_info)
> + return -ENODEV;
> +
> + return __sel_write_load(selinux_null.dentry->d_sb->s_fs_info, buf, size,
> + &pos);
> +}
> +
> static const struct lsm_id selinux_lsmid = {
> .name = "selinux",
> .id = LSM_ID_SELINUX,
> @@ -7499,6 +7524,8 @@ static struct security_hook_list selinux_hooks[] __ro_after_init = {
> #ifdef CONFIG_PERF_EVENTS
> LSM_HOOK_INIT(perf_event_alloc, selinux_perf_event_alloc),
> #endif
> + LSM_HOOK_INIT(lsm_config_system_policy, selinux_lsm_config_system_policy),
> +
> };
>
> static __init int selinux_init(void)
> diff --git a/security/selinux/include/security.h b/security/selinux/include/security.h
> index e7827ed7be5f..7b779ea43cc3 100644
> --- a/security/selinux/include/security.h
> +++ b/security/selinux/include/security.h
> @@ -389,7 +389,14 @@ struct selinux_kernel_status {
> extern void selinux_status_update_setenforce(bool enforcing);
> extern void selinux_status_update_policyload(u32 seqno);
> extern void selinux_complete_init(void);
> +
> +struct selinux_fs_info;
> +
> extern struct path selinux_null;
> +extern ssize_t __sel_write_load(struct selinux_fs_info *fsi,
> + const char __user *buf, size_t count,
> + loff_t *ppos);
> +
> extern void selnl_notify_setenforce(int val);
> extern void selnl_notify_policyload(u32 seqno);
> extern int selinux_nlmsg_lookup(u16 sclass, u16 nlmsg_type, u32 *perm);
> diff --git a/security/selinux/selinuxfs.c b/security/selinux/selinuxfs.c
> index 47480eb2189b..1f7e611d8300 100644
> --- a/security/selinux/selinuxfs.c
> +++ b/security/selinux/selinuxfs.c
> @@ -567,11 +567,11 @@ static int sel_make_policy_nodes(struct selinux_fs_info *fsi,
> return ret;
> }
>
> -static ssize_t sel_write_load(struct file *file, const char __user *buf,
> - size_t count, loff_t *ppos)
> +ssize_t __sel_write_load(struct selinux_fs_info *fsi,
> + const char __user *buf, size_t count,
> + loff_t *ppos)
>
> {
> - struct selinux_fs_info *fsi;
> struct selinux_load_state load_state;
> ssize_t length;
> void *data = NULL;
> @@ -605,7 +605,6 @@ static ssize_t sel_write_load(struct file *file, const char __user *buf,
> pr_warn_ratelimited("SELinux: failed to load policy\n");
> goto out;
> }
> - fsi = file_inode(file)->i_sb->s_fs_info;
> length = sel_make_policy_nodes(fsi, load_state.policy);
> if (length) {
> pr_warn_ratelimited("SELinux: failed to initialize selinuxfs\n");
> @@ -626,6 +625,15 @@ static ssize_t sel_write_load(struct file *file, const char __user *buf,
> return length;
> }
>
> +static ssize_t sel_write_load(struct file *file, const char __user *buf,
> + size_t count, loff_t *ppos)
> +{
> + struct selinux_fs_info *fsi = file_inode(file)->i_sb->s_fs_info;
> +
> + return __sel_write_load(fsi, buf, count, ppos);
> +}
> +
> +
> static const struct file_operations sel_load_ops = {
> .write = sel_write_load,
> .llseek = generic_file_llseek,
> --
> 2.48.1
>
More information about the Linux-security-module-archive
mailing list