Unverified Commit 79625f1b authored by Mickaël Salaün's avatar Mickaël Salaün
Browse files

landlock: Prepare to use credential instead of domain for fowner

This cosmetic change is needed for audit support, specifically to be
able to filter according to cross-execution boundaries.

struct landlock_file_security's size stay the same for now but it will
increase with struct landlock_cred_security's size.

Only save Landlock domain in hook_file_set_fowner() if the current
domain has LANDLOCK_SCOPE_SIGNAL, which was previously done for each
hook_file_send_sigiotask() calls.  This should improve a bit
performance.

Replace hardcoded LANDLOCK_SCOPE_SIGNAL with the signal_scope.scope
variable.

Use scoped guards for RCU read-side critical sections.

Cc: Günther Noack <gnoack@google.com>
Link: https://lore.kernel.org/r/20250320190717.2287696-8-mic@digikod.net


Signed-off-by: default avatarMickaël Salaün <mic@digikod.net>
parent 8d20efa9
Loading
Loading
Loading
Loading
+14 −6
Original line number Diff line number Diff line
@@ -1670,15 +1670,23 @@ static bool control_current_fowner(struct fown_struct *const fown)
static void hook_file_set_fowner(struct file *file)
{
	struct landlock_ruleset *prev_dom;
	struct landlock_ruleset *new_dom = NULL;
	struct landlock_cred_security fown_subject = {};

	if (control_current_fowner(file_f_owner(file))) {
		new_dom = landlock_get_current_domain();
		landlock_get_ruleset(new_dom);
		static const struct access_masks signal_scope = {
			.scope = LANDLOCK_SCOPE_SIGNAL,
		};
		const struct landlock_cred_security *new_subject =
			landlock_get_applicable_subject(current_cred(),
							signal_scope, NULL);
		if (new_subject) {
			landlock_get_ruleset(new_subject->domain);
			fown_subject = *new_subject;
		}
	}

	prev_dom = landlock_file(file)->fown_domain;
	landlock_file(file)->fown_domain = new_dom;
	prev_dom = landlock_file(file)->fown_subject.domain;
	landlock_file(file)->fown_subject = fown_subject;

	/* May be called in an RCU read-side critical section. */
	landlock_put_ruleset_deferred(prev_dom);
@@ -1686,7 +1694,7 @@ static void hook_file_set_fowner(struct file *file)

static void hook_file_free_security(struct file *file)
{
	landlock_put_ruleset_deferred(landlock_file(file)->fown_domain);
	landlock_put_ruleset_deferred(landlock_file(file)->fown_subject.domain);
}

static struct security_hook_list landlock_hooks[] __ro_after_init = {
+9 −6
Original line number Diff line number Diff line
/* SPDX-License-Identifier: GPL-2.0-only */
/*
 * Landlock LSM - Filesystem management and hooks
 * Landlock - Filesystem management and hooks
 *
 * Copyright © 2017-2020 Mickaël Salaün <mic@digikod.net>
 * Copyright © 2018-2020 ANSSI
 * Copyright © 2024-2025 Microsoft Corporation
 */

#ifndef _SECURITY_LANDLOCK_FS_H
@@ -14,6 +15,7 @@
#include <linux/rcupdate.h>

#include "access.h"
#include "cred.h"
#include "ruleset.h"
#include "setup.h"

@@ -54,12 +56,13 @@ struct landlock_file_security {
	 */
	access_mask_t allowed_access;
	/**
	 * @fown_domain: Domain of the task that set the PID that may receive a
	 * signal e.g., SIGURG when writing MSG_OOB to the related socket.
	 * This pointer is protected by the related file->f_owner->lock, as for
	 * fown_struct's members: pid, uid, and euid.
	 * @fown_subject: Landlock credential of the task that set the PID that
	 * may receive a signal e.g., SIGURG when writing MSG_OOB to the
	 * related socket.  This pointer is protected by the related
	 * file->f_owner->lock, as for fown_struct's members: pid, uid, and
	 * euid.
	 */
	struct landlock_ruleset *fown_domain;
	struct landlock_cred_security fown_subject;
};

/**
+16 −9
Original line number Diff line number Diff line
@@ -302,22 +302,29 @@ static int hook_task_kill(struct task_struct *const p,
static int hook_file_send_sigiotask(struct task_struct *tsk,
				    struct fown_struct *fown, int signum)
{
	const struct landlock_ruleset *dom;
	const struct landlock_cred_security *subject;
	bool is_scoped = false;

	/* Lock already held by send_sigio() and send_sigurg(). */
	lockdep_assert_held(&fown->lock);
	dom = landlock_get_applicable_domain(
		landlock_file(fown->file)->fown_domain, signal_scope);
	subject = &landlock_file(fown->file)->fown_subject;

	/* Quick return for unowned socket. */
	if (!dom)
	/*
	 * Quick return for unowned socket.
	 *
	 * subject->domain has already been filtered when saved by
	 * hook_file_set_fowner(), so there is no need to call
	 * landlock_get_applicable_subject() here.
	 */
	if (!subject->domain)
		return 0;

	rcu_read_lock();
	is_scoped = domain_is_scoped(dom, landlock_get_task_domain(tsk),
				     LANDLOCK_SCOPE_SIGNAL);
	rcu_read_unlock();
	scoped_guard(rcu)
	{
		is_scoped = domain_is_scoped(subject->domain,
					     landlock_get_task_domain(tsk),
					     signal_scope.scope);
	}
	if (is_scoped)
		return -EPERM;