landlock: Add ptrace restrictions
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>
This commit is contained in:
parent
385975dca5
commit
afe81f7541
|
@ -1,4 +1,4 @@
|
|||
obj-$(CONFIG_SECURITY_LANDLOCK) := landlock.o
|
||||
|
||||
landlock-y := setup.o object.o ruleset.o \
|
||||
cred.o
|
||||
cred.o ptrace.o
|
||||
|
|
|
@ -0,0 +1,120 @@
|
|||
// 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);
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
/*
|
||||
* Landlock LSM - Ptrace hooks
|
||||
*
|
||||
* Copyright © 2017-2019 Mickaël Salaün <mic@digikod.net>
|
||||
* Copyright © 2019 ANSSI
|
||||
*/
|
||||
|
||||
#ifndef _SECURITY_LANDLOCK_PTRACE_H
|
||||
#define _SECURITY_LANDLOCK_PTRACE_H
|
||||
|
||||
__init void landlock_add_ptrace_hooks(void);
|
||||
|
||||
#endif /* _SECURITY_LANDLOCK_PTRACE_H */
|
|
@ -11,6 +11,7 @@
|
|||
|
||||
#include "common.h"
|
||||
#include "cred.h"
|
||||
#include "ptrace.h"
|
||||
#include "setup.h"
|
||||
|
||||
struct lsm_blob_sizes landlock_blob_sizes __lsm_ro_after_init = {
|
||||
|
@ -20,6 +21,7 @@ struct lsm_blob_sizes landlock_blob_sizes __lsm_ro_after_init = {
|
|||
static int __init landlock_init(void)
|
||||
{
|
||||
landlock_add_cred_hooks();
|
||||
landlock_add_ptrace_hooks();
|
||||
pr_info("Up and running.\n");
|
||||
return 0;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue