mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2024-09-12 13:55:32 +00:00
![Mickaël Salaün](/assets/img/avatar_default.png)
Using ptrace(2) and related debug features on a target process can lead to a privilege escalation. Indeed, ptrace(2) can be used by an attacker to impersonate another task and to remain undetected while performing malicious activities. Thanks to ptrace_may_access(), various part of the kernel can check if a tracer is more privileged than a tracee. A landlocked process has fewer privileges than a non-landlocked process and must then be subject to additional restrictions when manipulating processes. To be allowed to use ptrace(2) and related syscalls on a target process, a landlocked process must have a subset of the target process's rules (i.e. the tracee must be in a sub-domain of the tracer). Cc: James Morris <jmorris@namei.org> Signed-off-by: Mickaël Salaün <mic@linux.microsoft.com> Reviewed-by: Jann Horn <jannh@google.com> Acked-by: Serge Hallyn <serge@hallyn.com> Reviewed-by: Kees Cook <keescook@chromium.org> Link: https://lore.kernel.org/r/20210422154123.13086-5-mic@digikod.net Signed-off-by: James Morris <jamorris@linux.microsoft.com>
120 lines
3.1 KiB
C
120 lines
3.1 KiB
C
// SPDX-License-Identifier: GPL-2.0-only
|
|
/*
|
|
* Landlock LSM - Ptrace hooks
|
|
*
|
|
* Copyright © 2017-2020 Mickaël Salaün <mic@digikod.net>
|
|
* Copyright © 2019-2020 ANSSI
|
|
*/
|
|
|
|
#include <asm/current.h>
|
|
#include <linux/cred.h>
|
|
#include <linux/errno.h>
|
|
#include <linux/kernel.h>
|
|
#include <linux/lsm_hooks.h>
|
|
#include <linux/rcupdate.h>
|
|
#include <linux/sched.h>
|
|
|
|
#include "common.h"
|
|
#include "cred.h"
|
|
#include "ptrace.h"
|
|
#include "ruleset.h"
|
|
#include "setup.h"
|
|
|
|
/**
|
|
* domain_scope_le - Checks domain ordering for scoped ptrace
|
|
*
|
|
* @parent: Parent domain.
|
|
* @child: Potential child of @parent.
|
|
*
|
|
* Checks if the @parent domain is less or equal to (i.e. an ancestor, which
|
|
* means a subset of) the @child domain.
|
|
*/
|
|
static bool domain_scope_le(const struct landlock_ruleset *const parent,
|
|
const struct landlock_ruleset *const child)
|
|
{
|
|
const struct landlock_hierarchy *walker;
|
|
|
|
if (!parent)
|
|
return true;
|
|
if (!child)
|
|
return false;
|
|
for (walker = child->hierarchy; walker; walker = walker->parent) {
|
|
if (walker == parent->hierarchy)
|
|
/* @parent is in the scoped hierarchy of @child. */
|
|
return true;
|
|
}
|
|
/* There is no relationship between @parent and @child. */
|
|
return false;
|
|
}
|
|
|
|
static bool task_is_scoped(const struct task_struct *const parent,
|
|
const struct task_struct *const child)
|
|
{
|
|
bool is_scoped;
|
|
const struct landlock_ruleset *dom_parent, *dom_child;
|
|
|
|
rcu_read_lock();
|
|
dom_parent = landlock_get_task_domain(parent);
|
|
dom_child = landlock_get_task_domain(child);
|
|
is_scoped = domain_scope_le(dom_parent, dom_child);
|
|
rcu_read_unlock();
|
|
return is_scoped;
|
|
}
|
|
|
|
static int task_ptrace(const struct task_struct *const parent,
|
|
const struct task_struct *const child)
|
|
{
|
|
/* Quick return for non-landlocked tasks. */
|
|
if (!landlocked(parent))
|
|
return 0;
|
|
if (task_is_scoped(parent, child))
|
|
return 0;
|
|
return -EPERM;
|
|
}
|
|
|
|
/**
|
|
* hook_ptrace_access_check - Determines whether the current process may access
|
|
* another
|
|
*
|
|
* @child: Process to be accessed.
|
|
* @mode: Mode of attachment.
|
|
*
|
|
* If the current task has Landlock rules, then the child must have at least
|
|
* the same rules. Else denied.
|
|
*
|
|
* Determines whether a process may access another, returning 0 if permission
|
|
* granted, -errno if denied.
|
|
*/
|
|
static int hook_ptrace_access_check(struct task_struct *const child,
|
|
const unsigned int mode)
|
|
{
|
|
return task_ptrace(current, child);
|
|
}
|
|
|
|
/**
|
|
* hook_ptrace_traceme - Determines whether another process may trace the
|
|
* current one
|
|
*
|
|
* @parent: Task proposed to be the tracer.
|
|
*
|
|
* If the parent has Landlock rules, then the current task must have the same
|
|
* or more rules. Else denied.
|
|
*
|
|
* Determines whether the nominated task is permitted to trace the current
|
|
* process, returning 0 if permission is granted, -errno if denied.
|
|
*/
|
|
static int hook_ptrace_traceme(struct task_struct *const parent)
|
|
{
|
|
return task_ptrace(parent, current);
|
|
}
|
|
|
|
static struct security_hook_list landlock_hooks[] __lsm_ro_after_init = {
|
|
LSM_HOOK_INIT(ptrace_access_check, hook_ptrace_access_check),
|
|
LSM_HOOK_INIT(ptrace_traceme, hook_ptrace_traceme),
|
|
};
|
|
|
|
__init void landlock_add_ptrace_hooks(void)
|
|
{
|
|
security_add_hooks(landlock_hooks, ARRAY_SIZE(landlock_hooks),
|
|
LANDLOCK_NAME);
|
|
}
|