* [PATCH v5] selinux: implement namespace_init and namespace_install hooks
@ 2026-09-21 13:31 Stephen Smalley
2026-09-21 13:48 ` sashiko-bot
0 siblings, 1 reply; 2+ messages in thread
From: Stephen Smalley @ 2026-09-21 13:31 UTC (permalink / raw)
To: selinux; +Cc: paul, omosnacek, Daniel Durning, Stephen Smalley
From: Daniel Durning <danieldurning.work@gmail.com>
Add implementations of the namespace_init and namespace_install
hooks for SELinux. Corresponding permissions are defined for each
hook (create and setns, respectively). Additionally, mount
namespaces have a create_anon permission to control anonymous
mount namespace creation separately. New security classes are
defined for each individual namespace type to allow granularity.
In namespace_init we check the caller's SID against itself. When
a namespace is created the SID of the caller is saved in the
namespace security blob. This SID is checked against the caller
in namespace_install.
Depends on
https://lore.kernel.org/linux-security-module/20260918143142.19435-2-stephen.smalley.work@gmail.com/
to ensure that the security blob is initialized for the initial namespaces.
Signed-off-by: Daniel Durning <danieldurning.work@gmail.com>
Signed-off-by: Stephen Smalley <stephen.smalley.work@gmail.com>
---
v5 drops the no-longer-necessary statement about falling back to
SECINITSID_KERNEL if ns_common is pointing to an initial namespace.
v4 dropped the corresponding code from the hook function that was
performing that fallback logic. Both depend on the patch linked
above to ensure that the security blob is initialized for the
initial namespaces. I re-ran the corresponding testsuite tests
added by
https://lore.kernel.org/selinux/20260501125634.7700-1-danieldurning.work@gmail.com/
and they all passed.
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 368f9dacdcef..ad4436e85eab 100644
--- a/security/selinux/hooks.c
+++ b/security/selinux/hooks.c
@@ -94,6 +94,8 @@
#include <linux/io_uring/cmd.h>
#include <uapi/linux/lsm.h>
#include <linux/memfd.h>
+#include <linux/ns_common.h>
+#include <linux/nsfs.h>
#include <uapi/linux/inet_diag.h>
#include "initcalls.h"
@@ -1329,6 +1331,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,
@@ -7040,6 +7067,41 @@ static int selinux_inode_getsecctx(struct inode *inode, struct lsm_context *cp)
cp->id = LSM_ID_SELINUX;
return 0;
}
+
+static int selinux_namespace_init(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.ns_id = ns->ns_id;
+
+ nssec->sid = sid;
+ if (ns->inum == MNT_NS_ANON_INO)
+ return avc_has_perm(sid, sid, sclass,
+ MNT_NAMESPACE__CREATE_ANON, &ad);
+
+ 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.ns_id = ns->ns_id;
+
+ 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,
@@ -7551,6 +7613,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),
};
/*
@@ -7754,6 +7817,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,
@@ -7829,6 +7893,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_init, selinux_namespace_init),
#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..26ee7a0123a6 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, "create_anon", 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 3c0a16ec978b..c6eae8f9c16b 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"
@@ -180,6 +181,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)
{
@@ -268,6 +273,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.55.0
^ permalink raw reply related [flat|nested] 2+ messages in thread* Re: [PATCH v5] selinux: implement namespace_init and namespace_install hooks
2026-09-21 13:31 [PATCH v5] selinux: implement namespace_init and namespace_install hooks Stephen Smalley
@ 2026-09-21 13:48 ` sashiko-bot
0 siblings, 0 replies; 2+ messages in thread
From: sashiko-bot @ 2026-09-21 13:48 UTC (permalink / raw)
Cc: selinux
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [Medium] selinux: audit log records ns_id=0 for all namespace creations
--
commit ee58aea3229beb02443b58cad2bfbb58dd22830b
Author: Daniel Durning <danieldurning.work@gmail.com>
selinux: implement namespace_init and namespace_install hooks
Add implementations of the namespace_init and namespace_install
hooks for SELinux. Corresponding permissions are defined for each
hook (create and setns, respectively).
> diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c
> index f2394cf6646e5..cd7ec2c90d1d0 100644
> --- a/security/selinux/hooks.c
> +++ b/security/selinux/hooks.c
[ ... ]
> @@ -7184,6 +7211,41 @@ static int selinux_inode_getsecctx(struct inode *inode, struct lsm_context *cp)
> cp->id = LSM_ID_SELINUX;
> return 0;
> }
> +
> +static int selinux_namespace_init(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.ns_id = ns->ns_id;
[Severity: Medium]
Will this regression result in the audit log systematically recording an
ns_id of 0 for every namespace creation?
Looking at __ns_common_init() in kernel/nscommon.c, it initializes ns->ns_id
to 0 just before the security_namespace_init() hook is invoked:
kernel/nscommon.c:__ns_common_init() {
ns->ns_id = 0;
ns->ns_type = ns_type;
...
ret = security_namespace_init(ns);
}
The actual namespace ID appears to be generated only after the namespace
infrastructure finishes invoking the security hooks. For example, in
alloc_mnt_ns() in fs/namespace.c:
fs/namespace.c:alloc_mnt_ns() {
if (anon)
ret = ns_common_init_inum(new_ns, MNT_NS_ANON_INO);
else
ret = ns_common_init(new_ns);
if (ret) {
kfree(new_ns);
dec_mnt_namespaces(ucounts);
return ERR_PTR(ret);
}
ns_tree_gen_id(new_ns);
}
Could this timing behavior prevent properly correlating namespace creation
events with subsequent actions in the security audit logs, since the actual
ID isn't assigned until after the audit record is generated?
> +
> + nssec->sid = sid;
> + if (ns->inum == MNT_NS_ANON_INO)
> + return avc_has_perm(sid, sid, sclass,
> + MNT_NAMESPACE__CREATE_ANON, &ad);
> +
> + return avc_has_perm(sid, sid, sclass, NAMESPACE__CREATE, &ad);
> +}
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260921133145.17768-2-stephen.smalley.work@gmail.com?part=1
^ permalink raw reply [flat|nested] 2+ messages in thread
end of thread, other threads:[~2026-09-21 13:48 UTC | newest]
Thread overview: 2+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-09-21 13:31 [PATCH v5] selinux: implement namespace_init and namespace_install hooks Stephen Smalley
2026-09-21 13:48 ` sashiko-bot
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox