From: danieldurning.work@gmail.com
To: selinux@vger.kernel.org
Cc: stephen.smalley.work@gmail.com, paul@paul-moore.com,
omosnace@redhat.com, brauner@kernel.org, mic@digikod.net
Subject: [RFC PATCH] selinux: implement namespace_alloc and namespace_install hooks
Date: Mon, 30 Mar 2026 19:31:00 +0000 [thread overview]
Message-ID: <20260330193100.3603-1-danieldurning.work@gmail.com> (raw)
From: Daniel Durning <danieldurning.work@gmail.com>
Add implementations of the namespace_alloc and namespace_install
hooks for SELinux. Corresponding permissions are defined for each
hook (create and setns, respectively), New security classes are
defined for each individual namespace type to allow granularity.
In namespace_alloc we check the caller's SID against itself. When
a namespace is allocated the SID of the caller is saved in the
namespace security blob. This SID is checked against the caller
in namespace_install. We fall back to SECINITSID_KERNEL if
ns_common is pointing to an init namespace, since these are
created before policy load and will be lacking a security blob.
Requires Christian Brauner's namespace LSM blob patch and
Mickaël Salaün's LSM namespace audit data patch, which are
the first 2 patches in the series linked below.
As an additional note, the namespace security blob does not
seem to be freed when a mount namespace is marked as anon
in the kernel. The free_mnt_ns() function only calls
ns_common_free() (which ultimately calls the namespace_free()
hook) if a mount namespace is not marked as anon.
This is causing a memory leak. This issue would need to be
addressed in the original LSM namespace blob patch.
Signed-off-by: Daniel Durning <danieldurning.work@gmail.com>
Link: https://lore.kernel.org/all/20260312100444.2609563-3-mic@digikod.net/
---
security/selinux/hooks.c | 65 +++++++++++++++++++++++++++++
security/selinux/include/classmap.h | 12 +++++-
security/selinux/include/objsec.h | 10 +++++
3 files changed, 86 insertions(+), 1 deletion(-)
diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c
index 6ef9c1aade65..e3cf07008fc8 100644
--- a/security/selinux/hooks.c
+++ b/security/selinux/hooks.c
@@ -94,6 +94,7 @@
#include <linux/io_uring/cmd.h>
#include <uapi/linux/lsm.h>
#include <linux/memfd.h>
+#include <linux/ns_common.h>
#include "initcalls.h"
#include "avc.h"
@@ -1327,6 +1328,31 @@ static inline u16 socket_type_to_security_class(int family, int type, int protoc
return SECCLASS_SOCKET;
}
+static inline u16 ns_type_to_security_class(struct ns_common *ns)
+{
+ switch (ns->ns_type) {
+ case CLONE_NEWCGROUP:
+ return SECCLASS_CGROUP_NAMESPACE;
+ case CLONE_NEWIPC:
+ return SECCLASS_IPC_NAMESPACE;
+ case CLONE_NEWNS:
+ return SECCLASS_MNT_NAMESPACE;
+ case CLONE_NEWNET:
+ return SECCLASS_NET_NAMESPACE;
+ case CLONE_NEWPID:
+ return SECCLASS_PID_NAMESPACE;
+ case CLONE_NEWTIME:
+ return SECCLASS_TIME_NAMESPACE;
+ case CLONE_NEWUSER:
+ return SECCLASS_USER_NAMESPACE;
+ case CLONE_NEWUTS:
+ return SECCLASS_UTS_NAMESPACE;
+ default:
+ WARN_ON(1);
+ return SECCLASS_NAMESPACE;
+ }
+}
+
static int selinux_genfs_get_sid(struct dentry *dentry,
u16 tclass,
u16 flags,
@@ -6899,6 +6925,42 @@ static int selinux_inode_getsecctx(struct inode *inode, struct lsm_context *cp)
cp->id = LSM_ID_SELINUX;
return 0;
}
+
+static int selinux_namespace_alloc(struct ns_common *ns)
+{
+ struct common_audit_data ad;
+ struct ns_security_struct *nssec = selinux_ns(ns);
+ u16 sclass = ns_type_to_security_class(ns);
+ u32 sid = current_sid();
+
+ ad.type = LSM_AUDIT_DATA_NS;
+ ad.u.ns.ns_type = ns->ns_type;
+ ad.u.ns.inum = ns->inum;
+
+ nssec->sid = sid;
+
+ return avc_has_perm(sid, sid, sclass, NAMESPACE__CREATE, &ad);
+}
+
+static int selinux_namespace_install(const struct nsset *nsset,
+ struct ns_common *ns)
+{
+ struct common_audit_data ad;
+ struct ns_security_struct *nssec = selinux_ns(ns);
+ u16 sclass = ns_type_to_security_class(ns);
+ u32 sid = current_sid();
+
+ ad.type = LSM_AUDIT_DATA_NS;
+ ad.u.ns.ns_type = ns->ns_type;
+ ad.u.ns.inum = ns->inum;
+
+ if (!nssec)
+ return avc_has_perm(sid, SECINITSID_KERNEL, sclass,
+ NAMESPACE__SETNS, &ad);
+
+ return avc_has_perm(sid, nssec->sid, sclass, NAMESPACE__SETNS, &ad);
+}
+
#ifdef CONFIG_KEYS
static int selinux_key_alloc(struct key *k, const struct cred *cred,
@@ -7409,6 +7471,7 @@ struct lsm_blob_sizes selinux_blob_sizes __ro_after_init = {
.lbs_bpf_map = sizeof(struct bpf_security_struct),
.lbs_bpf_prog = sizeof(struct bpf_security_struct),
.lbs_bpf_token = sizeof(struct bpf_security_struct),
+ .lbs_ns = sizeof(struct ns_security_struct),
};
/*
@@ -7610,6 +7673,7 @@ static struct security_hook_list selinux_hooks[] __ro_after_init = {
LSM_HOOK_INIT(tun_dev_attach_queue, selinux_tun_dev_attach_queue),
LSM_HOOK_INIT(tun_dev_attach, selinux_tun_dev_attach),
LSM_HOOK_INIT(tun_dev_open, selinux_tun_dev_open),
+ LSM_HOOK_INIT(namespace_install, selinux_namespace_install),
#ifdef CONFIG_SECURITY_INFINIBAND
LSM_HOOK_INIT(ib_pkey_access, selinux_ib_pkey_access),
LSM_HOOK_INIT(ib_endport_manage_subnet,
@@ -7685,6 +7749,7 @@ static struct security_hook_list selinux_hooks[] __ro_after_init = {
LSM_HOOK_INIT(inode_getsecctx, selinux_inode_getsecctx),
LSM_HOOK_INIT(sk_alloc_security, selinux_sk_alloc_security),
LSM_HOOK_INIT(tun_dev_alloc_security, selinux_tun_dev_alloc_security),
+ LSM_HOOK_INIT(namespace_alloc, selinux_namespace_alloc),
#ifdef CONFIG_SECURITY_INFINIBAND
LSM_HOOK_INIT(ib_alloc_security, selinux_ib_alloc_security),
#endif
diff --git a/security/selinux/include/classmap.h b/security/selinux/include/classmap.h
index 90cb61b16425..e904ed795208 100644
--- a/security/selinux/include/classmap.h
+++ b/security/selinux/include/classmap.h
@@ -33,6 +33,8 @@
"mac_override", "mac_admin", "syslog", "wake_alarm", "block_suspend", \
"audit_read", "perfmon", "bpf", "checkpoint_restore"
+#define COMMON_NAMESPACE_PERMS "create", "setns"
+
#ifdef __KERNEL__ /* avoid this check when building host programs */
#include <linux/capability.h>
@@ -178,9 +180,17 @@ const struct security_class_mapping secclass_map[] = {
{ "open", "cpu", "kernel", "tracepoint", "read", "write", NULL } },
{ "anon_inode", { COMMON_FILE_PERMS, NULL } },
{ "io_uring", { "override_creds", "sqpoll", "cmd", "allowed", NULL } },
- { "user_namespace", { "create", NULL } },
{ "memfd_file",
{ COMMON_FILE_PERMS, "execute_no_trans", "entrypoint", NULL } },
+ { "cgroup_namespace", { COMMON_NAMESPACE_PERMS, NULL } },
+ { "ipc_namespace", { COMMON_NAMESPACE_PERMS, NULL } },
+ { "mnt_namespace", { COMMON_NAMESPACE_PERMS, NULL } },
+ { "net_namespace", { COMMON_NAMESPACE_PERMS, NULL } },
+ { "pid_namespace", { COMMON_NAMESPACE_PERMS, NULL } },
+ { "time_namespace", { COMMON_NAMESPACE_PERMS, NULL } },
+ { "user_namespace", { COMMON_NAMESPACE_PERMS, NULL } },
+ { "uts_namespace", { COMMON_NAMESPACE_PERMS, NULL } },
+ { "namespace", { COMMON_NAMESPACE_PERMS, NULL } },
/* last one */ { NULL, {} }
};
diff --git a/security/selinux/include/objsec.h b/security/selinux/include/objsec.h
index 5bddd28ea5cb..4692a67b7c2f 100644
--- a/security/selinux/include/objsec.h
+++ b/security/selinux/include/objsec.h
@@ -27,6 +27,7 @@
#include <linux/msg.h>
#include <net/net_namespace.h>
#include <linux/bpf.h>
+#include <linux/ns_common.h>
#include "flask.h"
#include "avc.h"
@@ -178,6 +179,10 @@ struct perf_event_security_struct {
u32 sid; /* SID of perf_event obj creator */
};
+struct ns_security_struct {
+ u32 sid; /* SID of ns obj creator */
+};
+
extern struct lsm_blob_sizes selinux_blob_sizes;
static inline struct cred_security_struct *selinux_cred(const struct cred *cred)
{
@@ -259,6 +264,11 @@ selinux_perf_event(void *perf_event)
return perf_event + selinux_blob_sizes.lbs_perf_event;
}
+static inline struct ns_security_struct *selinux_ns(struct ns_common *ns)
+{
+ return ns->ns_security + selinux_blob_sizes.lbs_ns;
+}
+
#ifdef CONFIG_BPF_SYSCALL
static inline struct bpf_security_struct *
selinux_bpf_map_security(struct bpf_map *map)
--
2.51.0
next reply other threads:[~2026-03-30 19:31 UTC|newest]
Thread overview: 5+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-03-30 19:31 danieldurning.work [this message]
2026-04-14 16:54 ` [RFC PATCH] selinux: implement namespace_alloc and namespace_install hooks Stephen Smalley
2026-04-15 12:04 ` Stephen Smalley
2026-04-16 8:57 ` Christian Brauner
2026-04-16 14:15 ` Stephen Smalley
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=20260330193100.3603-1-danieldurning.work@gmail.com \
--to=danieldurning.work@gmail.com \
--cc=brauner@kernel.org \
--cc=mic@digikod.net \
--cc=omosnace@redhat.com \
--cc=paul@paul-moore.com \
--cc=selinux@vger.kernel.org \
--cc=stephen.smalley.work@gmail.com \
/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.