Something went wrong. Try again.
(I'm so sorry!) Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242// SPDX-License-Identifier: GPL-2.0-only/* * Landlock - Namespace hooks * * Copyright © 2026 Cloudflare, Inc. */
#include <linux/bitops.h>#include <linux/bug.h>#include <linux/lsm_audit.h>#include <linux/lsm_hooks.h>#include <linux/ns/ns_common_types.h>#include <linux/ns_common.h>#include <linux/nsproxy.h>#include <uapi/linux/landlock.h>
#include "cred.h"#include "domain.h"#include "limits.h"#include "log.h"#include "ns.h"#include "ruleset.h"#include "setup.h"
#define _LANDLOCK_NS_TYPE(type, flag) flag,
static const u32 landlock_namespace_types[] = { /* clang-format off */ FOR_EACH_NS_TYPE(_LANDLOCK_NS_TYPE) /* clang-format on */};
#undef _LANDLOCK_NS_TYPE
static_assert(ARRAY_SIZE(landlock_namespace_types) == LANDLOCK_NUM_NAMESPACE_TYPE);
/* Ensures the audit ns_id field can hold ns_common.ns_id without truncation. */static_assert(sizeof(((struct common_audit_data *)NULL)->u.ns.ns_id) >= sizeof(((struct ns_common *)NULL)->ns_id));
/** * landlock_ns_type_to_bit - Convert a namespace type to a compact bitmask * * @ns_type: Namespace type (``CLONE_NEW*``). * * Return: The compact bit for @ns_type, or 0 if @ns_type is invalid (with a * warning). */u64 landlock_ns_type_to_bit(const u32 ns_type){ size_t i;
for (i = 0; i < ARRAY_SIZE(landlock_namespace_types); i++) { if (landlock_namespace_types[i] == ns_type) return BIT_ULL(i); }
WARN_ONCE(1, "Unknown namespace type 0x%x\n", ns_type); return 0;}
/** * landlock_ns_types_to_bits - Convert namespace types to a compact bitmask * * @ns_types: Bitmask of namespace types (``CLONE_NEW*``). * * Return: The compact bits for all known @ns_types. Warns if unknown bits are * present (callers must pre-mask user input). */u64 landlock_ns_types_to_bits(const u64 ns_types){ u64 bits = 0; size_t i;
/* Callers pre-mask (CLONE_NS_ALL); the WARN guards future callers. */ WARN_ON_ONCE(ns_types & ~(u64)CLONE_NS_ALL); for (i = 0; i < ARRAY_SIZE(landlock_namespace_types); i++) { if (ns_types & landlock_namespace_types[i]) bits |= BIT_ULL(i); } return bits;}
static const struct access_masks ns_permission = { .permissions = LANDLOCK_PERMISSION_NAMESPACE_USE,};
/** * check_ns_type - Check namespace use permission * * @ns: The namespace being allocated or installed. * * Shared check for namespace_init (creation via clone(2), unshare(2), * open_tree(2), or fsmount(2)) and namespace_install (use via setns(2)): denies * when the namespace type is not in the domain's allowed set. At allocation * time @ns->ns_id is still zero and is logged as such. * * Return: 0 if allowed, -EPERM if denied. */static int check_ns_type(struct ns_common *const ns){ const struct landlock_cred_security *subject; size_t denied_layer;
subject = landlock_get_applicable_subject(current_cred(), ns_permission, NULL); if (!subject) return 0;
denied_layer = landlock_permission_is_denied( subject->domain, LANDLOCK_PERMISSION_NAMESPACE_USE, landlock_ns_type_to_bit(ns->ns_type)); if (!denied_layer) return 0;
landlock_log_denial(subject, &(struct landlock_request){ .type = LANDLOCK_REQUEST_NAMESPACE, .audit.type = LSM_AUDIT_DATA_NS, .permission = LANDLOCK_PERMISSION_NAMESPACE_USE, .audit.u.ns.ns_type = ns->ns_type, .audit.u.ns.ns_id = ns->ns_id, .layer_plus_one = denied_layer, }); return -EPERM;}
static int hook_namespace_init(struct ns_common *const ns){ return check_ns_type(ns);}
static int hook_namespace_install(const struct nsset *const nsset, struct ns_common *const ns){ return check_ns_type(ns);}
static struct security_hook_list landlock_hooks[] __ro_after_init = { LSM_HOOK_INIT(namespace_init, hook_namespace_init), LSM_HOOK_INIT(namespace_install, hook_namespace_install),};
__init void landlock_add_ns_hooks(void){ security_add_hooks(landlock_hooks, ARRAY_SIZE(landlock_hooks), &landlock_lsmid);}
#ifdef CONFIG_SECURITY_LANDLOCK_KUNIT_TEST
#include <kunit/test.h>
static void test_ns_type_to_bit(struct kunit *const test){ u64 seen = 0; size_t i;
for (i = 0; i < ARRAY_SIZE(landlock_namespace_types); i++) { const u64 bit = landlock_ns_type_to_bit(landlock_namespace_types[i]);
KUNIT_EXPECT_NE(test, 0ULL, bit); KUNIT_EXPECT_EQ(test, 0ULL, seen & bit); seen |= bit; }
KUNIT_EXPECT_EQ(test, GENMASK_ULL(LANDLOCK_NUM_NAMESPACE_TYPE - 1, 0), seen);}
static void test_ns_type_to_bit_unknown(struct kunit *const test){ if (!IS_ENABLED(CONFIG_BUG)) kunit_skip(test, "requires CONFIG_BUG");
/* clang-format off */ kunit_warning_suppress(test) { /* clang-format on */ KUNIT_EXPECT_EQ(test, 0ULL, landlock_ns_type_to_bit(CLONE_THREAD)); KUNIT_EXPECT_SUPPRESSED_WARNING_COUNT(test, 1); }}
static void test_ns_types_to_bits_all(struct kunit *const test){ KUNIT_EXPECT_EQ(test, GENMASK_ULL(LANDLOCK_NUM_NAMESPACE_TYPE - 1, 0), landlock_ns_types_to_bits(CLONE_NS_ALL));}
static void test_ns_types_to_bits_single(struct kunit *const test){ size_t i;
for (i = 0; i < ARRAY_SIZE(landlock_namespace_types); i++) KUNIT_EXPECT_EQ( test, landlock_ns_type_to_bit(landlock_namespace_types[i]), landlock_ns_types_to_bits(landlock_namespace_types[i]));}
static void test_ns_types_to_bits_unknown(struct kunit *const test){ if (!IS_ENABLED(CONFIG_BUG)) kunit_skip(test, "requires CONFIG_BUG");
/* clang-format off */ kunit_warning_suppress(test) { /* clang-format on */ KUNIT_EXPECT_EQ(test, 0ULL, landlock_ns_types_to_bits(CLONE_THREAD)); KUNIT_EXPECT_SUPPRESSED_WARNING_COUNT(test, 1); }}
static void test_ns_types_to_bits_zero(struct kunit *const test){ KUNIT_EXPECT_EQ(test, 0ULL, landlock_ns_types_to_bits(0));}
static struct kunit_case test_cases[] = { KUNIT_CASE(test_ns_type_to_bit), KUNIT_CASE(test_ns_type_to_bit_unknown), KUNIT_CASE(test_ns_types_to_bits_all), KUNIT_CASE(test_ns_types_to_bits_single), KUNIT_CASE(test_ns_types_to_bits_unknown), KUNIT_CASE(test_ns_types_to_bits_zero), {}};
static struct kunit_suite test_suite = { .name = "landlock_ns", .test_cases = test_cases,};
kunit_test_suite(test_suite);
#endif /* CONFIG_SECURITY_LANDLOCK_KUNIT_TEST */