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: Mickaël Salaün <mic@digikod.net>
This commit is contained in:
Mickaël Salaün 2025-03-20 20:06:56 +01:00
parent 8d20efa9dc
commit 79625f1b3a
No known key found for this signature in database
GPG Key ID: E5E3D0E88C82F6D2
3 changed files with 39 additions and 21 deletions

View File

@ -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 = {

View File

@ -1,9 +1,10 @@
/* 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;
};
/**

View File

@ -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;