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>