SELinux Security Module development
 help / color / mirror / Atom feed
* [PATCH v4] selinux: implement namespace_init and namespace_install hooks
@ 2026-09-21 13:28 Stephen Smalley
  2026-09-21 13:45 ` sashiko-bot
  0 siblings, 1 reply; 2+ messages in thread
From: Stephen Smalley @ 2026-09-21 13:28 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. 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.

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>
---
 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 v4] selinux: implement namespace_init and namespace_install hooks
  2026-09-21 13:28 [PATCH v4] selinux: implement namespace_init and namespace_install hooks Stephen Smalley
@ 2026-09-21 13:45 ` sashiko-bot
  0 siblings, 0 replies; 2+ messages in thread
From: sashiko-bot @ 2026-09-21 13:45 UTC (permalink / raw)
  Cc: selinux

Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
- [High] selinux: missing SECINITSID_KERNEL fallback for initial namespaces in setns
- [Medium] selinux: audit logs report zero ns_id during namespace creation
--

commit 7cfaed9361f973ecdd7fd2e074c01074e97b7cd5
Author: Daniel Durning <danieldurning.work@gmail.com>

selinux: implement namespace_init and namespace_install hooks
    
This commit adds implementations for the namespace_init and
namespace_install hooks to control namespace creation and setns
operations via SELinux. It defines new security classes for different
namespace types and saves the creator's SID in the namespace security
blob for validation.

> 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 audit log record an ns_id of 0 for all namespace creation events?

During namespace creation, ns_common_init() initializes ns->ns_id to 0 and
immediately calls security_namespace_init(). The actual namespace ID isn't
assigned until ns_tree_gen_id() is called later in the lifecycle.

Because selinux_namespace_init() reads ns->ns_id before it is assigned, does
this leave the audit logs ineffective for correlating namespace lifecycle
events?

> +
> +	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);

[Severity: High]
Is the fallback to SECINITSID_KERNEL missing here?

The commit message states: "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."

However, this line unconditionally dereferences nssec->sid. If setns() targets
an initial namespace (where ns->ns_security might be uninitialized or NULL),
could this result in a NULL pointer dereference or an evaluation against
SECINITSID_UNLABELED instead of SECINITSID_KERNEL?

> +}
> +

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260921132825.17526-1-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:45 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:28 [PATCH v4] selinux: implement namespace_init and namespace_install hooks Stephen Smalley
2026-09-21 13:45 ` sashiko-bot

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox