Thread (20 messages) flat view 20 messages, 4 authors, 7d ago

Re: [PATCH v3 07/12] landlock: Enforce namespace use restrictions

From: Tingmao Wang <hidden>
Date: 2026-08-09 16:01:32
Also in: linux-fsdevel, lkml

On 7/26/26 17:13, Mickaël Salaün wrote:
Add Landlock enforcement for namespace use via the LSM namespace_init
and namespace_install hooks.  This lets a sandboxed process restrict
which namespace types it can acquire, using LANDLOCK_PERM_NAMESPACE_USE
and per-type rules.

Introduce the handled_perm field in struct landlock_ruleset_attr for
per-category permissions: each permission gates all uses of a
kernel-defined category (CLONE_NEW* for namespace types, CAP_* for
capabilities) and provides complete deny-by-default coverage of category
members.  Rule values reference constants from other kernel subsystems
(CLONE_NEW* for namespaces); unknown values are silently accepted
because the allow-list denies them by default.  See the "Ruleset
restriction models" section in the kernel documentation for the full
design rationale.

The new permissions extend the UAPI, so this bumps the Landlock ABI
version to 11.

Both hooks share check_ns_type(): if the namespace's CLONE_NEW* type is
not in the layer's allowed set, the operation is denied.  No domain
ancestry bypass, no namespace creator tracking, just a flat per-layer
allowed-types bitmask.

- hook_namespace_init() fires during unshare(CLONE_NEW*) and
  clone(CLONE_NEW*) via __ns_common_init().

- hook_namespace_install() fires during setns() via validate_ns().

Both record namespace_type and ns_id in the audit data; ns_id is zero at
namespace creation.

struct perm_masks is __packed __aligned(sizeof(u64)) because on m68k GCC
packs bitfields at byte granularity, so without it a u64 bitfield struct
can be smaller than sizeof(u64).
(The reason for forcing u64 is not obvious just from this patch alone, but
it makes sense after adding 40 capability bits in the patch following)
The rule's perm selector must be LANDLOCK_PERM_NAMESPACE_USE and
reserves room for future per-rule-type sub-permissions.
allowed_namespace_types lists the allowed CLONE_NEW* flags; unknown bits
are accepted for forward compatibility and have no effect since the
allow-list denies by default.

The rule also carries a quiet_namespace_types bitmask that silences the
audit records of specific denied members without granting them.  A
sandbox that knowingly runs a caller probing a namespace type or
capability it will never be granted (e.g. an old runtime kept for
compatibility) would otherwise flood the audit log and drown the
surprising denials that matter.  Quiet is per-member rather than a
coarse per-category ruleset bit so a sandbox can silence one member
(CLONE_NEWNET) while still auditing another (CLONE_NEWUTS).  Making
quiet the complement of the allowed set would be broad, could not audit
a member that is neither allowed nor explicitly quieted, and would
auto-hide members added by future kernels.  For the same reason a
sandbox should quiet only specific members known to be requested but
expected to be denied, never a blanket set, which follows the running
kernel's known members and hides surprising or future denials.  A bit
set in both allowed_namespace_types and quiet_namespace_types has no
effect, since an allowed member is never denied and its layer can never
become the youngest denying layer for that member.
I agree this uAPI makes more sense, having a quiet_perm is a bit wasteful
given that the quiet configuration for perms can be determined solely via
the struct landlock_namespace_attr.
quoted hunk ↗ jump to hunk
The merged filesystem and network quiet feature marks unbounded rb-tree
objects (paths, ports) quiet through the LANDLOCK_ADD_RULE_QUIET flag
and the ruleset quiet_access_* masks.  Capabilities and namespace types
are instead a small, bounded, kernel-defined member set, so their quiet
list is a per-rule bitmask sibling to the allowed bitmask, and the
shared flag is rejected for these rule types.  A single add_rule call
can allow some members and quiet others independently, and a quiet-only
rule (empty allowed set) is valid.  Only the youngest denying layer's
quiet mask decides, so a parent cannot silence a denial made by a deeper
layer.

[...]
@@ -268,6 +281,40 @@ struct landlock_net_port_attr {
 	__u64 port;
 };
 
+/**
+ * struct landlock_namespace_attr - Namespace type definition
+ *
+ * Argument of sys_landlock_add_rule() with %LANDLOCK_RULE_NAMESPACE.
+ */
+struct landlock_namespace_attr {
+	/**
+	 * @perm: Must be set to %LANDLOCK_PERM_NAMESPACE_USE.
+	 */
+	__u64 perm;
+	/**
+	 * @allowed_namespace_types: Bitmask of namespace types (``CLONE_NEW*``
+	 * flags) to allow under this rule.  Unknown bits are silently ignored
+	 * for forward compatibility.
+	 */
+	__u64 allowed_namespace_types;
+	/**
+	 * @quiet_namespace_types: Bitmask of namespace types (``CLONE_NEW*``
+	 * flags) whose denial by this layer should not be logged, even if
+	 * logging would normally take place per landlock_restrict_self() flags.
+	 * Only denials attributed to this layer are suppressed (see `permission
+	 * flags`_).  Bits also set in @allowed_namespace_types have no effect,
+	 * since an allowed type is never denied.  Unknown bits are silently
+	 * ignored.
+	 *
+	 * At least one of @allowed_namespace_types or @quiet_namespace_types
+	 * must be non-zero, otherwise the call returns ``-ENOMSG``.  The
Worth noting here that if a layer does not want to allow nor quiet any
bits it should just not do the landlock_add_rule.
quoted hunk ↗ jump to hunk
+	 * non-zero check runs on the raw input before unknown-bit masking, so a
+	 * rule that sets only bits unknown to the running kernel succeeds but
+	 * has no runtime effect.
+	 */
+	__u64 quiet_namespace_types;
+};
+
 /**
  * DOC: fs_access
  *
[...]
diff --git a/security/landlock/domain.h b/security/landlock/domain.h
index 56cceed5f50c..c2b72795b0d6 100644
--- a/security/landlock/domain.h
+++ b/security/landlock/domain.h
@@ -116,6 +116,12 @@ struct landlock_hierarchy {
 	 * logged) if the related object is marked as quiet.
 	 */
 	struct access_masks quiet_access;
I'm not sure if this is worth adjusting or mentioning in a comment but
basically quiet_access.perm is unused.
+	/**
+	 * @quiet_perm: Per-member quiet bitmasks for permission types
+	 * (capabilities and namespace types).  A member denied by this layer is
+	 * not logged when its bit is set here.
+	 */
+	struct perm_masks quiet_perm;
 #endif /* CONFIG_AUDIT */
 };
 
[...]
Reviewed-by: Tingmao Wang <redacted>
Keyboard shortcuts
hback out one level
jnext message in thread
kprevious message in thread
ldrill in
Escclose help / fold thread tree
?toggle this help