diff options
| author | Mark Brown <broonie@kernel.org> | 2026-10-08 11:39:37 +0200 |
|---|---|---|
| committer | Mark Brown <broonie@kernel.org> | 2026-10-08 11:39:37 +0200 |
| commit | faf79d91d67e539ed4ce446f2a31127ec27dbe63 (patch) | |
| tree | 940048866c3aef03cdc2673d88125608905a2751 /include | |
| parent | 05e11e9b39f6bf8b49e7e738f7af4ead3a8f9776 (diff) | |
| parent | 64b9e6eff524804de6ec7a7be83131ec3d8ae17f (diff) | |
| download | linux-next-faf79d91d67e539ed4ce446f2a31127ec27dbe63.tar.gz linux-next-faf79d91d67e539ed4ce446f2a31127ec27dbe63.zip | |
Merge branch 'next' of https://git.kernel.org/pub/scm/linux/kernel/git/mic/linux.git
Diffstat (limited to 'include')
| -rw-r--r-- | include/linux/landlock.h | 29 | ||||
| -rw-r--r-- | include/trace/events/landlock.h | 244 | ||||
| -rw-r--r-- | include/uapi/linux/landlock.h | 139 |
3 files changed, 381 insertions, 31 deletions
diff --git a/include/linux/landlock.h b/include/linux/landlock.h index 004cbd0b9298..0ee4880988ba 100644 --- a/include/linux/landlock.h +++ b/include/linux/landlock.h @@ -13,14 +13,16 @@ #include <uapi/linux/landlock.h> /* - * Access-right and scope names, shared between the audit records (get_blocker() - * in security/landlock/audit.c) and the trace events + * Access-right, scope, and permission names, shared between the audit records + * (get_blocker() in security/landlock/audit.c) and the trace events * (include/trace/events/landlock.h). A consumer defines * _LANDLOCK_NAME_ENTRY(mask, name) before expanding a list and undefines it * afterwards: audit maps each entry to a "[bit] = name" slot for O(1) lookup, * the trace events map it to a __print_flags() { mask, name } pair. The bit * value lives only in the LANDLOCK_* UAPI constant each entry references. - * Names are unprefixed; audit prepends the "fs."/"net."/"scope." category. + * Access-right and scope names are unprefixed; audit prepends the + * "fs."/"net."/"scope." category. Permission entries carry an action and a + * domain for the qualified and bare views below. */ #define _LANDLOCK_ACCESS_FS_NAMES \ _LANDLOCK_NAME_ENTRY(LANDLOCK_ACCESS_FS_EXECUTE, "execute"), \ @@ -53,4 +55,25 @@ "abstract_unix_socket"), \ _LANDLOCK_NAME_ENTRY(LANDLOCK_SCOPE_SIGNAL, "signal") +#define _LANDLOCK_PERMISSION_NAMESPACE_NAME "namespace" +#define _LANDLOCK_PERMISSION_CAPABILITY_NAME "capability" + +#define _LANDLOCK_PERMISSION_LIST(entry) \ + entry(LANDLOCK_PERMISSION_NAMESPACE_USE, "use", \ + _LANDLOCK_PERMISSION_NAMESPACE_NAME), \ + entry(LANDLOCK_PERMISSION_CAPABILITY_USE, "use", \ + _LANDLOCK_PERMISSION_CAPABILITY_NAME) + +#define _LANDLOCK_PERMISSION_QUALIFIED_ENTRY(mask, action, domain) \ + _LANDLOCK_NAME_ENTRY(mask, domain "." action) + +#define _LANDLOCK_PERMISSION_BARE_ENTRY(mask, action, ...) \ + _LANDLOCK_NAME_ENTRY(mask, action) + +#define _LANDLOCK_PERMISSION_NAMES \ + _LANDLOCK_PERMISSION_LIST(_LANDLOCK_PERMISSION_QUALIFIED_ENTRY) + +#define _LANDLOCK_PERMISSION_BLOCKER_NAMES \ + _LANDLOCK_PERMISSION_LIST(_LANDLOCK_PERMISSION_BARE_ENTRY) + #endif /* _LINUX_LANDLOCK_H */ diff --git a/include/trace/events/landlock.h b/include/trace/events/landlock.h index 3a43638c9bc2..b11f98d0a736 100644 --- a/include/trace/events/landlock.h +++ b/include/trace/events/landlock.h @@ -36,6 +36,8 @@ static_assert(sizeof(access_mask_t) <= sizeof(u64)); TRACE_DEFINE_ENUM(LANDLOCK_REQUEST_FS_CHANGE_TOPOLOGY); TRACE_DEFINE_ENUM(LANDLOCK_REQUEST_FS_ACCESS); TRACE_DEFINE_ENUM(LANDLOCK_REQUEST_NET_ACCESS); +TRACE_DEFINE_ENUM(LANDLOCK_REQUEST_NAMESPACE); +TRACE_DEFINE_ENUM(LANDLOCK_REQUEST_CAPABILITY); #ifdef CREATE_TRACE_POINTS @@ -276,13 +278,14 @@ static inline const char *__trace_landlock_print_layers( * Every denial event shares three fields. domain is the ID of the * innermost domain that blocked the access. same_exec tells whether the * current task is the same executable that entered that domain. logged is - * the domain's audit-logging decision for this denial (its log_status is - * enabled and the per-execution flag selected by same_exec is set); a - * stateless ftrace filter can select the denials the domain submits to - * audit with logged==1, without reconstructing it from the per-execution - * log flags. Denial events order their fields as domain, same_exec, - * logged, then blockers (deny_access events only), then the type-specific - * object fields, then any variable-length field. + * the complete audit-submission selection for this denial: the domain's + * log_status and per-execution flag, per-object or per-member quiet rules, + * and request-specific no-audit options. This excludes global audit state + * and audit-side filters. A stateless ftrace filter can select the denials + * the domain submits to audit with logged==1, without reconstructing those + * inputs. Denial events order their fields as domain, same_exec, + * logged, then the blockers verdict input, then the + * type-specific object fields, then any variable-length field. * * Relational referents * ~~~~~~~~~~~~~~~~~~~~~ @@ -302,9 +305,9 @@ static inline const char *__trace_landlock_print_layers( * Blocker fields * ~~~~~~~~~~~~~~ * - * The filesystem and network blocker arguments identify the request type - * and carry its final missing access subset when applicable. The type - * determines how to interpret the access value. + * Blocker arguments identify the request type and carry its final missing + * access subset when applicable. The type determines how to interpret the + * access value. */ /* @@ -343,26 +346,31 @@ TRACE_EVENT(landlock_create_ruleset, TP_ARGS(ruleset), TP_STRUCT__entry( - __field( u64, ruleset_id ) - __field( u64, ruleset_version ) - __field( access_mask_t, handled_fs ) - __field( access_mask_t, handled_net ) - __field( access_mask_t, scoped ) + __field( u64, ruleset_id ) + __field( u64, ruleset_version ) + __field( access_mask_t, handled_fs ) + __field( access_mask_t, handled_net ) + __field( access_mask_t, scoped ) + __field( access_mask_t, handled_permissions ) ), TP_fast_assign( __entry->ruleset_id = ruleset->id; __entry->ruleset_version = ruleset->version; - __entry->handled_fs = ruleset->handled_masks.fs; - __entry->handled_net = ruleset->handled_masks.net; - __entry->scoped = ruleset->handled_masks.scope; + __entry->handled_fs = ruleset->layer.handled.fs; + __entry->handled_net = ruleset->layer.handled.net; + __entry->scoped = ruleset->layer.handled.scope; + __entry->handled_permissions = + ruleset->layer.handled.permissions; ), - TP_printk("ruleset=%llx.%llu handled_fs=%s handled_net=%s scoped=%s", + TP_printk("ruleset=%llx.%llu handled_fs=%s handled_net=%s scoped=%s handled_permissions=%s", __entry->ruleset_id, __entry->ruleset_version, __print_flags(__entry->handled_fs, "|", _LANDLOCK_ACCESS_FS_NAMES), __print_flags(__entry->handled_net, "|", _LANDLOCK_ACCESS_NET_NAMES), - __print_flags(__entry->scoped, "|", _LANDLOCK_SCOPE_NAMES)) + __print_flags(__entry->scoped, "|", _LANDLOCK_SCOPE_NAMES), + __print_flags(__entry->handled_permissions, "|", + _LANDLOCK_PERMISSION_NAMES)) ); /** @@ -497,6 +505,103 @@ TRACE_EVENT(landlock_add_rule_net_port, ); /** + * landlock_add_rule_namespace - Namespace rule added to a ruleset + * + * @ruleset: Source ruleset (never NULL). + * @flags: Complete validated landlock_add_rule_flags value supplied by this + * successful call, not the rule's accumulated quiet state. + * @permissions: Validated permission mask from the rule attribute. + * @allowed_namespace_types: Effective known namespace types allowed by this + * call, using CLONE_NEW* values. + * @quiet_namespace_types: Effective known namespace types quieted by this + * call, using CLONE_NEW* values. + * + * Emitted by sys_landlock_add_rule() under the modified ruleset's lock, so + * the reported ruleset is a stable snapshot that no concurrent writer can + * change. + */ +TRACE_EVENT(landlock_add_rule_namespace, + + TP_PROTO(const struct landlock_ruleset *ruleset, u32 flags, + u64 permissions, u64 allowed_namespace_types, + u64 quiet_namespace_types), + + TP_ARGS(ruleset, flags, permissions, allowed_namespace_types, + quiet_namespace_types), + + TP_STRUCT__entry( + __field( u64, ruleset_id ) + __field( u64, ruleset_version ) + __field( access_mask_t, permissions ) + __field( u64, allowed_namespace_types ) + __field( u64, quiet_namespace_types ) + ), + + TP_fast_assign( + lockdep_assert_held(&ruleset->lock); + __entry->ruleset_id = ruleset->id; + __entry->ruleset_version = ruleset->version; + __entry->permissions = permissions; + __entry->allowed_namespace_types = allowed_namespace_types; + __entry->quiet_namespace_types = quiet_namespace_types; + ), + + TP_printk("ruleset=%llx.%llu permissions=%s allowed_namespace_types=0x%llx quiet_namespace_types=0x%llx", + __entry->ruleset_id, __entry->ruleset_version, + __print_flags(__entry->permissions, "|", + _LANDLOCK_PERMISSION_NAMES), + __entry->allowed_namespace_types, + __entry->quiet_namespace_types) +); + +/** + * landlock_add_rule_capability - Capability rule added to a ruleset + * + * @ruleset: Source ruleset (never NULL). + * @flags: Complete validated landlock_add_rule_flags value supplied by this + * successful call, not the rule's accumulated quiet state. + * @permissions: Validated permission mask from the rule attribute. + * @allowed_capabilities: Effective known capabilities allowed by this call. + * @quiet_capabilities: Effective known capabilities quieted by this call. + * + * Emitted by sys_landlock_add_rule() under the modified ruleset's lock, so + * the reported ruleset is a stable snapshot that no concurrent writer can + * change. + */ +TRACE_EVENT(landlock_add_rule_capability, + + TP_PROTO(const struct landlock_ruleset *ruleset, u32 flags, + u64 permissions, u64 allowed_capabilities, + u64 quiet_capabilities), + + TP_ARGS(ruleset, flags, permissions, allowed_capabilities, + quiet_capabilities), + + TP_STRUCT__entry( + __field( u64, ruleset_id ) + __field( u64, ruleset_version ) + __field( access_mask_t, permissions ) + __field( u64, allowed_capabilities ) + __field( u64, quiet_capabilities ) + ), + + TP_fast_assign( + lockdep_assert_held(&ruleset->lock); + __entry->ruleset_id = ruleset->id; + __entry->ruleset_version = ruleset->version; + __entry->permissions = permissions; + __entry->allowed_capabilities = allowed_capabilities; + __entry->quiet_capabilities = quiet_capabilities; + ), + + TP_printk("ruleset=%llx.%llu permissions=%s allowed_capabilities=0x%llx quiet_capabilities=0x%llx", + __entry->ruleset_id, __entry->ruleset_version, + __print_flags(__entry->permissions, "|", + _LANDLOCK_PERMISSION_NAMES), + __entry->allowed_capabilities, __entry->quiet_capabilities) +); + +/** * landlock_create_domain - New domain created * * @domain: Newly created domain (never NULL, immutable after creation). @@ -874,6 +979,105 @@ TRACE_EVENT(landlock_deny_access_net, ); /** + * landlock_deny_permission_namespace - Namespace use denied + * + * @hierarchy: Denying domain's hierarchy node (never NULL); its id is the + * domain field. + * @same_exec: Whether the current task entered the denying domain itself. + * @logged: Whether this denial was selected for audit logging. + * @blockers: Request type and final missing permission subset (never NULL). + * @namespace_type: CLONE_NEW* namespace type that was denied. + * @namespace_id: Namespace ID, or 0 when creation was denied. + * + * Emitted when a Landlock domain denies namespace use. + */ +TRACE_EVENT(landlock_deny_permission_namespace, + + TP_PROTO(const struct landlock_hierarchy *hierarchy, bool same_exec, + bool logged, const struct landlock_blockers *blockers, + u32 namespace_type, u64 namespace_id), + + TP_ARGS(hierarchy, same_exec, logged, blockers, namespace_type, + namespace_id), + + TP_STRUCT__entry( + __field( u64, domain_id ) + __field( bool, same_exec ) + __field( bool, logged ) + __field( enum landlock_request_type, blockers_type ) + __field( access_mask_t, blockers_access ) + __field( u32, namespace_type ) + __field( u64, namespace_id ) + ), + + TP_fast_assign( + __entry->domain_id = hierarchy->id; + __entry->same_exec = same_exec; + __entry->logged = logged; + __entry->blockers_type = blockers->type; + __entry->blockers_access = blockers->access; + __entry->namespace_type = namespace_type; + __entry->namespace_id = namespace_id; + ), + + TP_printk("domain=%llx same_exec=%d logged=%d blockers=%s namespace_type=0x%x namespace_id=%llu", + __entry->domain_id, __entry->same_exec, __entry->logged, + __entry->blockers_type == LANDLOCK_REQUEST_NAMESPACE ? + __print_flags(__entry->blockers_access, "|", + _LANDLOCK_PERMISSION_BLOCKER_NAMES) : + "unknown", + __entry->namespace_type, __entry->namespace_id) +); + +/** + * landlock_deny_permission_capability - Capability use denied + * + * @hierarchy: Denying domain's hierarchy node (never NULL); its id is the + * domain field. + * @same_exec: Whether the current task entered the denying domain itself. + * @logged: Whether this denial was selected for audit logging. + * @blockers: Request type and final missing permission subset (never NULL). + * @capability: CAP_* number that was denied. + * + * Emitted when a Landlock domain denies capability use, except for checks + * made with CAP_OPT_NOAUDIT, which are denied without emitting this event. + */ +TRACE_EVENT(landlock_deny_permission_capability, + + TP_PROTO(const struct landlock_hierarchy *hierarchy, bool same_exec, + bool logged, const struct landlock_blockers *blockers, + int capability), + + TP_ARGS(hierarchy, same_exec, logged, blockers, capability), + + TP_STRUCT__entry( + __field( u64, domain_id ) + __field( bool, same_exec ) + __field( bool, logged ) + __field( enum landlock_request_type, blockers_type ) + __field( access_mask_t, blockers_access ) + __field( int, capability ) + ), + + TP_fast_assign( + __entry->domain_id = hierarchy->id; + __entry->same_exec = same_exec; + __entry->logged = logged; + __entry->blockers_type = blockers->type; + __entry->blockers_access = blockers->access; + __entry->capability = capability; + ), + + TP_printk("domain=%llx same_exec=%d logged=%d blockers=%s capability=%d", + __entry->domain_id, __entry->same_exec, __entry->logged, + __entry->blockers_type == LANDLOCK_REQUEST_CAPABILITY ? + __print_flags(__entry->blockers_access, "|", + _LANDLOCK_PERMISSION_BLOCKER_NAMES) : + "unknown", + __entry->capability) +); + +/** * landlock_deny_ptrace - Ptrace access denied by a Landlock domain * * @hierarchy: Denying domain's hierarchy node (never NULL); its id is the diff --git a/include/uapi/linux/landlock.h b/include/uapi/linux/landlock.h index cceda3b3b961..24510e5d8479 100644 --- a/include/uapi/linux/landlock.h +++ b/include/uapi/linux/landlock.h @@ -33,14 +33,14 @@ * (and that they have tested with a kernel that supported them all). * * @quiet_access_fs and @quiet_access_net are bitmasks of actions for which a - * denial by this layer will not trigger a log if the corresponding object (or - * its children, for filesystem rules) is marked with the "quiet" bit via - * %LANDLOCK_ADD_RULE_QUIET, even if logging would normally take place per + * denial by this layer will not trigger an audit record if the corresponding + * object (or its children, for filesystem rules) is marked with the "quiet" bit + * via %LANDLOCK_ADD_RULE_QUIET, even if logging would normally take place per * landlock_restrict_self() flags. @quiet_scoped is similar, except that it * does not require marking any objects as quiet - if the ruleset is created * with any bits set in @quiet_scoped, then denial of such scoped resources will - * not trigger any log. These 3 fields are available since Landlock ABI version - * 10. + * not trigger an audit record. These three fields are available since Landlock + * ABI version 10. * * @quiet_access_fs, @quiet_access_net and @quiet_scoped must be a subset of * @handled_access_fs, @handled_access_net and @scoped respectively. @@ -66,18 +66,24 @@ struct landlock_ruleset_attr { __u64 scoped; /** * @quiet_access_fs: Bitmask of filesystem actions which should not be - * logged if per-object quiet flag is set. + * submitted to audit if the per-object quiet flag is set. */ __u64 quiet_access_fs; /** * @quiet_access_net: Bitmask of network actions which should not be - * logged if per-object quiet flag is set. + * submitted to audit if the per-object quiet flag is set. */ __u64 quiet_access_net; /** - * @quiet_scoped: Bitmask of scoped actions which should not be logged. + * @quiet_scoped: Bitmask of scoped actions which should not be + * submitted to audit. */ __u64 quiet_scoped; + /** + * @handled_permissions: Bitmask of handled permissions (cf. `Permission + * flags`_). + */ + __u64 handled_permissions; }; /** @@ -228,6 +234,15 @@ enum landlock_rule_type { * landlock_net_port_attr . */ LANDLOCK_RULE_NET_PORT, + /** + * @LANDLOCK_RULE_NAMESPACE: Type of a &struct landlock_namespace_attr . + */ + LANDLOCK_RULE_NAMESPACE, + /** + * @LANDLOCK_RULE_CAPABILITY: Type of a &struct + * landlock_capability_attr . + */ + LANDLOCK_RULE_CAPABILITY, }; /** @@ -282,6 +297,77 @@ struct landlock_net_port_attr { }; /** + * struct landlock_namespace_attr - Namespace type definition + * + * Argument of sys_landlock_add_rule() with %LANDLOCK_RULE_NAMESPACE. + */ +struct landlock_namespace_attr { + /** + * @permissions: Must be set to %LANDLOCK_PERMISSION_NAMESPACE_USE. + */ + __u64 permissions; + /** + * @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 submitted to audit, + * even when landlock_restrict_self() enables audit logging. Only audit + * records attributed to this layer are suppressed; denial tracepoints + * still fire (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 + * 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. Programs should omit this rule when they + * neither allow nor quiet a namespace type. + */ + __u64 quiet_namespace_types; +}; + +/** + * struct landlock_capability_attr - Capability definition + * + * Argument of sys_landlock_add_rule() with %LANDLOCK_RULE_CAPABILITY. + */ +struct landlock_capability_attr { + /** + * @permissions: Must be set to %LANDLOCK_PERMISSION_CAPABILITY_USE. + */ + __u64 permissions; + /** + * @allowed_capabilities: Bitmask of capabilities (``1ULL << CAP_*``) to + * allow under this rule. Bits above ``CAP_LAST_CAP`` are silently + * ignored for forward compatibility. + */ + __u64 allowed_capabilities; + /** + * @quiet_capabilities: Bitmask of capabilities (``1ULL << CAP_*``) + * whose denial by this layer should not be submitted to audit, even if + * audit logging would normally take place per landlock_restrict_self() + * flags. Only audit records attributed to this layer are suppressed; + * denial tracepoints still fire (see `Permission flags`_). Bits also + * set in @allowed_capabilities have no effect, since an allowed + * capability is never denied. Bits above ``CAP_LAST_CAP`` are silently + * ignored. + * + * At least one of @allowed_capabilities or @quiet_capabilities must be + * non-zero, otherwise the call returns ``-ENOMSG``. The non-zero check + * runs on the raw input before unknown-bit masking, so a rule that sets + * only bits unknown to the running kernel (above ``CAP_LAST_CAP``) + * succeeds but has no runtime effect. Programs should omit this rule + * when they neither allow nor quiet a capability. + */ + __u64 quiet_capabilities; +}; + +/** * DOC: fs_access * * A set of actions on kernel objects may be defined by an attribute (e.g. @@ -507,4 +593,41 @@ struct landlock_net_port_attr { #define LANDLOCK_SCOPE_SIGNAL (1ULL << 1) /* clang-format on*/ +/** + * DOC: permission + * + * Permission flags + * ~~~~~~~~~~~~~~~~ + * + * These flags restrict the use of members of a category, each member being + * identified by a constant from another kernel subsystem (e.g. CLONE_NEW* + * namespace types, CAP_* capabilities). A flag covers every kernel path that + * uses a member of its category, and members that no rule explicitly allows are + * denied. Values unknown to the running kernel are silently accepted for + * forward compatibility and stay denied by default. See + * Documentation/security/landlock.rst for design details. + * + * When a ruleset handles multiple permissions whose operations overlap (e.g. a + * non-user namespace needs both its namespace type and CAP_SYS_ADMIN), the + * operation is allowed only if each handled permission independently allows it. + * See Documentation/userspace-api/landlock.rst. + * + * - %LANDLOCK_PERMISSION_NAMESPACE_USE: Restrict the use of specific namespace + * types: creation (:manpage:`unshare(2)`, :manpage:`clone(2)`, + * :manpage:`clone3(2)`), joining (:manpage:`setns(2)`), and acquiring an fd + * reference (:manpage:`open_tree(2)`, :manpage:`fsmount(2)`). A process in a + * Landlock domain that handles this permission is denied from using namespace + * types that are not explicitly allowed by a %LANDLOCK_RULE_NAMESPACE rule. + * Support added in Landlock ABI version 12. + * - %LANDLOCK_PERMISSION_CAPABILITY_USE: Restrict the use of specific Linux + * capabilities. A process in a Landlock domain that handles this permission + * is denied from exercising capabilities that are not explicitly allowed by a + * %LANDLOCK_RULE_CAPABILITY rule. This hook is purely restrictive: it can + * deny capabilities that the kernel would otherwise grant, but it can never + * grant capabilities that the kernel already denied. Support added in + * Landlock ABI version 12. + */ +#define LANDLOCK_PERMISSION_NAMESPACE_USE (1ULL << 0) +#define LANDLOCK_PERMISSION_CAPABILITY_USE (1ULL << 1) + #endif /* _UAPI_LINUX_LANDLOCK_H */ |
