selftests/landlock: Add capability restriction tests

Add tests to exercise LANDLOCK_PERMISSION_CAPABILITY_USE enforcement.  A
sandboxed process is denied a handled capability when no rule grants it,
and an explicit rule restores it.  Unknown capability values above
CAP_LAST_CAP are accepted at rule-add time yet have no runtime effect,
because deny-by-default still applies once the domain is enforced.
Stacking tests cover the per-layer allow/deny combinations, including a
layer that does not handle the permission, and invalid rule attributes
return the expected errors.

Three tests exercise non-standard capability contexts: enforcing a
domain via CAP_SYS_ADMIN authorization without no_new_privs, restricting
capabilities gained through the kernel's user namespace ownership bypass
(cap_capable_helper), and checking CAP_SYS_ADMIN against a descendant
user namespace, with an unsandboxed positive control, to confirm the
decision is independent of the target.

Audit tests verify denied and allowed capabilities, per-member quiet
interactions, and CAP_OPT_NOAUDIT suppression.  Trace tests cover
accepted, rejected, and no-op rule adds, and normal, quiet, and
CAP_OPT_NOAUDIT denials.

Test coverage for security/landlock is 91.8% of 2864 lines according to
LLVM 22.

Cc: Christian Brauner <brauner@kernel.org>
Cc: Günther Noack <gnoack@google.com>
Cc: Paul Moore <paul@paul-moore.com>
Cc: Serge E. Hallyn <serge@hallyn.com>
Link: https://patch.msgid.link/20261014082718.1435842-7-mic@digikod.net
Signed-off-by: Mickaël Salaün <mic@digikod.net>
This commit is contained in:
Mickaël Salaün 2026-10-14 10:27:12 +02:00
parent de3a636ca5
commit 5dcf1c8577
No known key found for this signature in database
GPG Key ID: E5E3D0E88C82F6D2
3 changed files with 1413 additions and 0 deletions

View File

@ -142,6 +142,25 @@ TEST(errata)
ASSERT_EQ(EINVAL, errno);
}
#define PERMISSION_LAST LANDLOCK_PERMISSION_CAPABILITY_USE
TEST(ruleset_with_unknown_permission)
{
__u64 permission_mask;
for (permission_mask = 1ULL << 63; permission_mask != PERMISSION_LAST;
permission_mask >>= 1) {
struct landlock_ruleset_attr ruleset_attr = {
.handled_permissions = permission_mask,
};
/* Unknown handled_permissions values must be rejected. */
ASSERT_EQ(-1, landlock_create_ruleset(&ruleset_attr,
sizeof(ruleset_attr), 0));
ASSERT_EQ(EINVAL, errno);
}
}
/* Tests ordering of syscall argument checks. */
TEST(create_ruleset_checks_ordering)
{

File diff suppressed because it is too large Load Diff

View File

@ -33,6 +33,8 @@
TRACEFS_LANDLOCK_DIR "/landlock_add_rule_net_port/enable"
#define TRACEFS_ADD_RULE_NAMESPACE_ENABLE \
TRACEFS_LANDLOCK_DIR "/landlock_add_rule_namespace/enable"
#define TRACEFS_ADD_RULE_CAPABILITY_ENABLE \
TRACEFS_LANDLOCK_DIR "/landlock_add_rule_capability/enable"
#define TRACEFS_CHECK_RULE_FS_ENABLE \
TRACEFS_LANDLOCK_DIR "/landlock_check_rule_inode/enable"
#define TRACEFS_CHECK_RULE_NET_ENABLE \
@ -43,6 +45,8 @@
TRACEFS_LANDLOCK_DIR "/landlock_deny_access_net/enable"
#define TRACEFS_DENY_PERMISSION_NAMESPACE_ENABLE \
TRACEFS_LANDLOCK_DIR "/landlock_deny_permission_namespace/enable"
#define TRACEFS_DENY_PERMISSION_CAPABILITY_ENABLE \
TRACEFS_LANDLOCK_DIR "/landlock_deny_permission_capability/enable"
#define TRACEFS_DENY_PTRACE_ENABLE \
TRACEFS_LANDLOCK_DIR "/landlock_deny_ptrace/enable"
#define TRACEFS_DENY_SCOPE_SIGNAL_ENABLE \
@ -110,6 +114,17 @@
#define REGEX_ADD_RULE_NAMESPACE(task) \
REGEX_ADD_RULE_NAMESPACE_VERSION(task, "[0-9]\\+")
#define REGEX_ADD_RULE_CAPABILITY_VERSION(task, version) \
TRACE_PREFIX(task) \
"landlock_add_rule_capability: " \
"ruleset=[0-9a-f]\\+\\." version " " \
"permissions=[a-z._|]* " \
"allowed_capabilities=0x[0-9a-f]\\+ " \
"quiet_capabilities=0x[0-9a-f]\\+$"
#define REGEX_ADD_RULE_CAPABILITY(task) \
REGEX_ADD_RULE_CAPABILITY_VERSION(task, "[0-9]\\+")
#define REGEX_CREATE_RULESET(task) \
TRACE_PREFIX(task) \
"landlock_create_ruleset: " \
@ -173,6 +188,15 @@
"namespace_type=0x[0-9a-f]\\+ " \
"namespace_id=[0-9]\\+$"
#define REGEX_DENY_PERMISSION_CAPABILITY(task) \
TRACE_PREFIX(task) \
"landlock_deny_permission_capability: " \
"domain=[0-9a-f]\\+ " \
"same_exec=[01] " \
"logged=[01] " \
"blockers=[a-z_|]* " \
"capability=[0-9]\\+$"
#define REGEX_DENY_PTRACE(task) \
TRACE_PREFIX(task) \
"landlock_deny_ptrace: " \