587 lines · 17.7 KB · C
RawDownload
// SPDX-License-Identifier: GPL-2.0-only/* * Landlock - Log helpers * * Copyright © 2023-2025 Microsoft Corporation * Copyright © 2026 Cloudflare, Inc. */ #include <kunit/test.h>#include <linux/bitops.h>#include <uapi/linux/landlock.h> #include "access.h"#include "audit.h"#include "common.h"#include "cred.h"#include "domain.h"#include "limits.h"#include "log.h"#include "ruleset.h"#include "trace.h" static struct landlock_hierarchy *get_hierarchy(const struct landlock_domain *const domain, const size_t layer){	struct landlock_hierarchy *hierarchy = domain->hierarchy;	ssize_t i; 	if (WARN_ON_ONCE(layer >= domain->num_layers))		return hierarchy; 	for (i = domain->num_layers - 1; i > layer; i--) {		if (WARN_ON_ONCE(!hierarchy->parent))			break; 		hierarchy = hierarchy->parent;	} 	return hierarchy;} #ifdef CONFIG_SECURITY_LANDLOCK_KUNIT_TEST static void test_get_hierarchy(struct kunit *const test){	struct landlock_hierarchy dom0_hierarchy = {		.id = 10,	};	struct landlock_hierarchy dom1_hierarchy = {		.parent = &dom0_hierarchy,		.id = 20,	};	struct landlock_hierarchy dom2_hierarchy = {		.parent = &dom1_hierarchy,		.id = 30,	};	struct landlock_domain dom2 = {		.hierarchy = &dom2_hierarchy,		.num_layers = 3,	}; 	KUNIT_EXPECT_EQ(test, 10, get_hierarchy(&dom2, 0)->id);	KUNIT_EXPECT_EQ(test, 20, get_hierarchy(&dom2, 1)->id);	KUNIT_EXPECT_EQ(test, 30, get_hierarchy(&dom2, 2)->id);	/* KUNIT_EXPECT_EQ(test, 30, get_hierarchy(&dom2, -1)->id); */} #endif /* CONFIG_SECURITY_LANDLOCK_KUNIT_TEST */ /* Get the youngest layer that denied the access_request. */static size_t get_denied_layer(const struct landlock_domain *const domain,			       access_mask_t *const access_request,			       const struct layer_masks *masks){	for (ssize_t i = ARRAY_SIZE(masks->layers) - 1; i >= 0; i--) {		if (masks->layers[i].access & *access_request) {			*access_request &= masks->layers[i].access;			return i;		}	} 	/* Not found - fall back to default values */	*access_request = 0;	return domain->num_layers - 1;} #ifdef CONFIG_SECURITY_LANDLOCK_KUNIT_TEST static void test_get_denied_layer(struct kunit *const test){	const struct landlock_domain dom = {		.num_layers = 5,	};	const struct layer_masks masks = {		.layers[0].access = LANDLOCK_ACCESS_FS_EXECUTE |				    LANDLOCK_ACCESS_FS_READ_DIR,		.layers[1].access = LANDLOCK_ACCESS_FS_READ_FILE |				    LANDLOCK_ACCESS_FS_READ_DIR,		.layers[2].access = LANDLOCK_ACCESS_FS_REMOVE_DIR,	};	access_mask_t access; 	access = LANDLOCK_ACCESS_FS_EXECUTE;	KUNIT_EXPECT_EQ(test, 0, get_denied_layer(&dom, &access, &masks));	KUNIT_EXPECT_EQ(test, access, LANDLOCK_ACCESS_FS_EXECUTE); 	access = LANDLOCK_ACCESS_FS_READ_FILE;	KUNIT_EXPECT_EQ(test, 1, get_denied_layer(&dom, &access, &masks));	KUNIT_EXPECT_EQ(test, access, LANDLOCK_ACCESS_FS_READ_FILE); 	access = LANDLOCK_ACCESS_FS_READ_DIR;	KUNIT_EXPECT_EQ(test, 1, get_denied_layer(&dom, &access, &masks));	KUNIT_EXPECT_EQ(test, access, LANDLOCK_ACCESS_FS_READ_DIR); 	access = LANDLOCK_ACCESS_FS_READ_FILE | LANDLOCK_ACCESS_FS_READ_DIR;	KUNIT_EXPECT_EQ(test, 1, get_denied_layer(&dom, &access, &masks));	KUNIT_EXPECT_EQ(test, access,			LANDLOCK_ACCESS_FS_READ_FILE |				LANDLOCK_ACCESS_FS_READ_DIR); 	access = LANDLOCK_ACCESS_FS_EXECUTE | LANDLOCK_ACCESS_FS_READ_DIR;	KUNIT_EXPECT_EQ(test, 1, get_denied_layer(&dom, &access, &masks));	KUNIT_EXPECT_EQ(test, access, LANDLOCK_ACCESS_FS_READ_DIR); 	access = LANDLOCK_ACCESS_FS_WRITE_FILE;	KUNIT_EXPECT_EQ(test, 4, get_denied_layer(&dom, &access, &masks));	KUNIT_EXPECT_EQ(test, access, 0);} #endif /* CONFIG_SECURITY_LANDLOCK_KUNIT_TEST */ static size_tget_layer_from_deny_masks(access_mask_t *const access_request,			  const access_mask_t all_existing_optional_access,			  const deny_masks_t deny_masks,			  optional_access_t quiet_optional_accesses,			  bool *quiet){	const unsigned long access_opt = all_existing_optional_access;	const unsigned long access_req = *access_request;	access_mask_t missing = 0;	size_t youngest_layer = 0;	size_t access_index = 0;	unsigned long access_bit;	bool should_quiet = false; 	/* This will require change with new object types. */	WARN_ON_ONCE(access_opt != _LANDLOCK_ACCESS_FS_OPTIONAL); 	for_each_set_bit(access_bit, &access_opt,			 BITS_PER_TYPE(access_mask_t)) {		if (access_req & BIT(access_bit)) {			const size_t layer =				(deny_masks >>				 (access_index *				  HWEIGHT(LANDLOCK_MAX_NUM_LAYERS - 1))) &				(LANDLOCK_MAX_NUM_LAYERS - 1);			const bool layer_has_quiet =				!!(quiet_optional_accesses & BIT(access_index)); 			if (layer > youngest_layer) {				youngest_layer = layer;				missing = BIT(access_bit);				should_quiet = layer_has_quiet;			} else if (layer == youngest_layer) {				missing |= BIT(access_bit);				/*				 * Whether the layer has rules with quiet flag				 * covering the file accessed does not depend on				 * the access, and so the following				 * WARN_ON_ONCE() should not fail.				 */				WARN_ON_ONCE(should_quiet && !layer_has_quiet);				should_quiet = layer_has_quiet;			}		}		access_index++;	} 	*access_request = missing;	*quiet = should_quiet;	return youngest_layer;} #ifdef CONFIG_SECURITY_LANDLOCK_KUNIT_TEST static void test_get_layer_from_deny_masks(struct kunit *const test){	deny_masks_t deny_mask;	access_mask_t access;	optional_access_t quiet_optional_accesses;	bool quiet; 	/* truncate:0 ioctl_dev:2 */	deny_mask = 0x20;	quiet_optional_accesses = 0; 	access = LANDLOCK_ACCESS_FS_TRUNCATE;	KUNIT_EXPECT_EQ(test, 0,			get_layer_from_deny_masks(				&access, _LANDLOCK_ACCESS_FS_OPTIONAL,				deny_mask, quiet_optional_accesses, &quiet));	KUNIT_EXPECT_EQ(test, access, LANDLOCK_ACCESS_FS_TRUNCATE);	KUNIT_EXPECT_EQ(test, quiet, false); 	access = LANDLOCK_ACCESS_FS_IOCTL_DEV;	KUNIT_EXPECT_EQ(test, 2,			get_layer_from_deny_masks(				&access, _LANDLOCK_ACCESS_FS_OPTIONAL,				deny_mask, quiet_optional_accesses, &quiet));	KUNIT_EXPECT_EQ(test, access, LANDLOCK_ACCESS_FS_IOCTL_DEV);	KUNIT_EXPECT_EQ(test, quiet, false); 	access = LANDLOCK_ACCESS_FS_TRUNCATE | LANDLOCK_ACCESS_FS_IOCTL_DEV;	KUNIT_EXPECT_EQ(test, 2,			get_layer_from_deny_masks(				&access, _LANDLOCK_ACCESS_FS_OPTIONAL,				deny_mask, quiet_optional_accesses, &quiet));	KUNIT_EXPECT_EQ(test, access, LANDLOCK_ACCESS_FS_IOCTL_DEV);	KUNIT_EXPECT_EQ(test, quiet, false); 	/* layer denying truncate: quiet, ioctl: not quiet */	quiet_optional_accesses = 0b01; 	access = LANDLOCK_ACCESS_FS_TRUNCATE;	KUNIT_EXPECT_EQ(test, 0,			get_layer_from_deny_masks(				&access, _LANDLOCK_ACCESS_FS_OPTIONAL,				deny_mask, quiet_optional_accesses, &quiet));	KUNIT_EXPECT_EQ(test, access, LANDLOCK_ACCESS_FS_TRUNCATE);	KUNIT_EXPECT_EQ(test, quiet, true); 	access = LANDLOCK_ACCESS_FS_IOCTL_DEV;	KUNIT_EXPECT_EQ(test, 2,			get_layer_from_deny_masks(				&access, _LANDLOCK_ACCESS_FS_OPTIONAL,				deny_mask, quiet_optional_accesses, &quiet));	KUNIT_EXPECT_EQ(test, access, LANDLOCK_ACCESS_FS_IOCTL_DEV);	KUNIT_EXPECT_EQ(test, quiet, false); 	access = LANDLOCK_ACCESS_FS_TRUNCATE | LANDLOCK_ACCESS_FS_IOCTL_DEV;	KUNIT_EXPECT_EQ(test, 2,			get_layer_from_deny_masks(				&access, _LANDLOCK_ACCESS_FS_OPTIONAL,				deny_mask, quiet_optional_accesses, &quiet));	KUNIT_EXPECT_EQ(test, access, LANDLOCK_ACCESS_FS_IOCTL_DEV);	KUNIT_EXPECT_EQ(test, quiet, false); 	/* Reverse order - truncate:2 ioctl_dev:0 */	deny_mask = 0x02;	quiet_optional_accesses = 0; 	access = LANDLOCK_ACCESS_FS_TRUNCATE;	KUNIT_EXPECT_EQ(test, 2,			get_layer_from_deny_masks(				&access, _LANDLOCK_ACCESS_FS_OPTIONAL,				deny_mask, quiet_optional_accesses, &quiet));	KUNIT_EXPECT_EQ(test, access, LANDLOCK_ACCESS_FS_TRUNCATE);	KUNIT_EXPECT_EQ(test, quiet, false); 	access = LANDLOCK_ACCESS_FS_IOCTL_DEV;	KUNIT_EXPECT_EQ(test, 0,			get_layer_from_deny_masks(				&access, _LANDLOCK_ACCESS_FS_OPTIONAL,				deny_mask, quiet_optional_accesses, &quiet));	KUNIT_EXPECT_EQ(test, access, LANDLOCK_ACCESS_FS_IOCTL_DEV);	KUNIT_EXPECT_EQ(test, quiet, false); 	access = LANDLOCK_ACCESS_FS_TRUNCATE | LANDLOCK_ACCESS_FS_IOCTL_DEV;	KUNIT_EXPECT_EQ(test, 2,			get_layer_from_deny_masks(				&access, _LANDLOCK_ACCESS_FS_OPTIONAL,				deny_mask, quiet_optional_accesses, &quiet));	KUNIT_EXPECT_EQ(test, access, LANDLOCK_ACCESS_FS_TRUNCATE);	KUNIT_EXPECT_EQ(test, quiet, false); 	/* layer denying truncate: quiet, ioctl: not quiet */	quiet_optional_accesses = 0b01; 	access = LANDLOCK_ACCESS_FS_TRUNCATE;	KUNIT_EXPECT_EQ(test, 2,			get_layer_from_deny_masks(				&access, _LANDLOCK_ACCESS_FS_OPTIONAL,				deny_mask, quiet_optional_accesses, &quiet));	KUNIT_EXPECT_EQ(test, access, LANDLOCK_ACCESS_FS_TRUNCATE);	KUNIT_EXPECT_EQ(test, quiet, true); 	access = LANDLOCK_ACCESS_FS_IOCTL_DEV;	KUNIT_EXPECT_EQ(test, 0,			get_layer_from_deny_masks(				&access, _LANDLOCK_ACCESS_FS_OPTIONAL,				deny_mask, quiet_optional_accesses, &quiet));	KUNIT_EXPECT_EQ(test, access, LANDLOCK_ACCESS_FS_IOCTL_DEV);	KUNIT_EXPECT_EQ(test, quiet, false); 	access = LANDLOCK_ACCESS_FS_TRUNCATE | LANDLOCK_ACCESS_FS_IOCTL_DEV;	KUNIT_EXPECT_EQ(test, 2,			get_layer_from_deny_masks(				&access, _LANDLOCK_ACCESS_FS_OPTIONAL,				deny_mask, quiet_optional_accesses, &quiet));	KUNIT_EXPECT_EQ(test, access, LANDLOCK_ACCESS_FS_TRUNCATE);	KUNIT_EXPECT_EQ(test, quiet, true); 	/* layer denying truncate: not quiet, ioctl: quiet */	quiet_optional_accesses = 0b10; 	access = LANDLOCK_ACCESS_FS_TRUNCATE;	KUNIT_EXPECT_EQ(test, 2,			get_layer_from_deny_masks(				&access, _LANDLOCK_ACCESS_FS_OPTIONAL,				deny_mask, quiet_optional_accesses, &quiet));	KUNIT_EXPECT_EQ(test, access, LANDLOCK_ACCESS_FS_TRUNCATE);	KUNIT_EXPECT_EQ(test, quiet, false); 	access = LANDLOCK_ACCESS_FS_IOCTL_DEV;	KUNIT_EXPECT_EQ(test, 0,			get_layer_from_deny_masks(				&access, _LANDLOCK_ACCESS_FS_OPTIONAL,				deny_mask, quiet_optional_accesses, &quiet));	KUNIT_EXPECT_EQ(test, access, LANDLOCK_ACCESS_FS_IOCTL_DEV);	KUNIT_EXPECT_EQ(test, quiet, true); 	access = LANDLOCK_ACCESS_FS_TRUNCATE | LANDLOCK_ACCESS_FS_IOCTL_DEV;	KUNIT_EXPECT_EQ(test, 2,			get_layer_from_deny_masks(				&access, _LANDLOCK_ACCESS_FS_OPTIONAL,				deny_mask, quiet_optional_accesses, &quiet));	KUNIT_EXPECT_EQ(test, access, LANDLOCK_ACCESS_FS_TRUNCATE);	KUNIT_EXPECT_EQ(test, quiet, false); 	/* truncate:15 ioctl_dev:15 */	deny_mask = 0xff;	quiet_optional_accesses = 0; 	access = LANDLOCK_ACCESS_FS_TRUNCATE;	KUNIT_EXPECT_EQ(test, 15,			get_layer_from_deny_masks(				&access, _LANDLOCK_ACCESS_FS_OPTIONAL,				deny_mask, quiet_optional_accesses, &quiet));	KUNIT_EXPECT_EQ(test, access, LANDLOCK_ACCESS_FS_TRUNCATE);	KUNIT_EXPECT_EQ(test, quiet, false); 	access = LANDLOCK_ACCESS_FS_TRUNCATE | LANDLOCK_ACCESS_FS_IOCTL_DEV;	KUNIT_EXPECT_EQ(test, 15,			get_layer_from_deny_masks(				&access, _LANDLOCK_ACCESS_FS_OPTIONAL,				deny_mask, quiet_optional_accesses, &quiet));	KUNIT_EXPECT_EQ(test, access,			LANDLOCK_ACCESS_FS_TRUNCATE |				LANDLOCK_ACCESS_FS_IOCTL_DEV);	KUNIT_EXPECT_EQ(test, quiet, false); 	/* Both quiet (same layer so quietness must be the same) */	quiet_optional_accesses = 0b11; 	access = LANDLOCK_ACCESS_FS_TRUNCATE;	KUNIT_EXPECT_EQ(test, 15,			get_layer_from_deny_masks(				&access, _LANDLOCK_ACCESS_FS_OPTIONAL,				deny_mask, quiet_optional_accesses, &quiet));	KUNIT_EXPECT_EQ(test, access, LANDLOCK_ACCESS_FS_TRUNCATE);	KUNIT_EXPECT_EQ(test, quiet, true); 	access = LANDLOCK_ACCESS_FS_TRUNCATE | LANDLOCK_ACCESS_FS_IOCTL_DEV;	KUNIT_EXPECT_EQ(test, 15,			get_layer_from_deny_masks(				&access, _LANDLOCK_ACCESS_FS_OPTIONAL,				deny_mask, quiet_optional_accesses, &quiet));	KUNIT_EXPECT_EQ(test, access,			LANDLOCK_ACCESS_FS_TRUNCATE |				LANDLOCK_ACCESS_FS_IOCTL_DEV);	KUNIT_EXPECT_EQ(test, quiet, true);} #endif /* CONFIG_SECURITY_LANDLOCK_KUNIT_TEST */ static bool is_valid_request(const struct landlock_request *const request){	if (WARN_ON_ONCE(request->layer_plus_one > LANDLOCK_MAX_NUM_LAYERS))		return false; 	if (WARN_ON_ONCE(!(!!request->layer_plus_one ^ !!request->access)))		return false; 	if (request->access) {		if (WARN_ON_ONCE(!(!!request->layer_masks ^				   !!request->all_existing_optional_access)))			return false;	} else {		if (WARN_ON_ONCE(request->layer_masks ||				 request->all_existing_optional_access))			return false;	} 	if (request->deny_masks) {		if (WARN_ON_ONCE(!request->all_existing_optional_access))			return false;		static_assert(sizeof(request->all_existing_optional_access) ==			      sizeof(u32));		if (WARN_ON_ONCE(			    request->quiet_optional_accesses >=			    BIT(hweight32(				    request->all_existing_optional_access))))			return false;	} 	return true;} static access_mask_tpick_access_mask_for_request_type(const enum landlock_request_type type,				  const struct access_masks access_masks){	switch (type) {	case LANDLOCK_REQUEST_FS_ACCESS:		return access_masks.fs;	case LANDLOCK_REQUEST_NET_ACCESS:		return access_masks.net;	default:		WARN_ONCE(1, "Invalid request type %d passed to %s", type,			  __func__);		return 0;	}} /* * Whether a quiet rule silences the denial: the rule must cover the whole * denied access in the layer that denied it (a quiet rule in a non-denying * layer does not suppress the denial). */static boolis_denial_quieted(const struct landlock_request *const request,		  const struct landlock_hierarchy *const youngest_denied,		  const access_mask_t missing, const bool object_quiet_flag){	if (object_quiet_flag) {		const access_mask_t quiet_mask =			pick_access_mask_for_request_type(				request->type, youngest_denied->quiet_masks); 		return (quiet_mask & missing) == missing;	} 	/*	 * Either the object is not quiet, or this is a scope request.  We check	 * request->type to distinguish between the two cases.	 */	switch (request->type) {	case LANDLOCK_REQUEST_SCOPE_SIGNAL:		return !!(youngest_denied->quiet_masks.scope &			  LANDLOCK_SCOPE_SIGNAL);	case LANDLOCK_REQUEST_SCOPE_ABSTRACT_UNIX_SOCKET:		return !!(youngest_denied->quiet_masks.scope &			  LANDLOCK_SCOPE_ABSTRACT_UNIX_SOCKET);	/*	 * Leave LANDLOCK_REQUEST_PTRACE and LANDLOCK_REQUEST_FS_CHANGE_TOPOLOGY	 * unhandled for now - they are never quiet.	 */	default:		return false;	}} /* * Computes whether a denial from youngest_denied is selected for logging by the * domain's policy: its logging must not be disabled (by both per-execution * flags being off, or by an ancestor's * LANDLOCK_RESTRICT_SELF_LOG_SUBDOMAINS_OFF), the per-execution flag matching * same_exec must be set, and no quiet rule may cover the denied access. * landlock_log_denial() computes this once and passes it to * landlock_audit_denial(), which additionally requires audit_enabled. */static boolis_denial_logged(const struct landlock_request *const request,		 const struct landlock_hierarchy *const youngest_denied,		 const access_mask_t missing, const bool same_exec,		 const bool object_quiet_flag){	if (READ_ONCE(youngest_denied->log_status) == LANDLOCK_LOG_DISABLED)		return false; 	if (!(same_exec ? youngest_denied->log_same_exec :			  youngest_denied->log_new_exec))		return false; 	return !is_denial_quieted(request, youngest_denied, missing,				  object_quiet_flag);} /** * landlock_log_denial - Log a denied access * * @subject: The Landlock subject's credential denying an action. * @request: Detail of the user space request. */void landlock_log_denial(const struct landlock_cred_security *const subject,			 const struct landlock_request *const request){	struct landlock_hierarchy *youngest_denied;	size_t youngest_layer;

Lines 1–500 of 587.