--- v12
+++ v20
@@ -1,171 +1,62 @@
-Add a first Landlock hook that can be used to enforce a security policy
-or to audit some process activities. For a sandboxing use-case, it is
-needed to inform the kernel if a task can legitimately debug another.
-ptrace(2) can also be used by an attacker to impersonate another task
-and remain undetected while performing malicious activities.
-
-Using ptrace(2) and related features on a target process can lead to a
-privilege escalation. A sandboxed task must then be able to tell the
-kernel if another task is more privileged, via ptrace_may_access().
+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' rules (i.e. the tracee must be in a sub-domain of the tracer).
Signed-off-by: Mickaël Salaün <mic@digikod.net>
-Cc: Alexei Starovoitov <ast@kernel.org>
-Cc: Andy Lutomirski <luto@amacapital.net>
-Cc: Daniel Borkmann <daniel@iogearbox.net>
Cc: James Morris <jmorris@namei.org>
+Cc: Jann Horn <jannh@google.com>
Cc: Kees Cook <keescook@chromium.org>
Cc: Serge E. Hallyn <serge@hallyn.com>
-Cc: Will Drewry <wad@chromium.org>
---
-Changes since v10:
-* revamp and replace the static policy with a Landlock hook which may be
- used by the corresponding BPF_LANDLOCK_PTRACE program (attach) type
- and a dedicated process_cmp_landlock_ptrace() BPF helper
-* check prog return value against LANDLOCK_RET_DENY (ret is a bitmask)
-
-Changes since v6:
-* factor out ptrace check
-* constify pointers
-* cleanup headers
-* use the new security_add_hooks()
+Changes since v14:
+* Constify variables.
+
+Changes since v13:
+* Make the ptrace restriction mandatory, like in the v10.
+* Remove the eBPF dependency.
+
+Previous changes:
+https://lore.kernel.org/lkml/20191104172146.30797-5-mic@digikod.net/
---
- security/landlock/Makefile | 4 +-
- security/landlock/bpf_run.c | 62 +++++++++++++++++
- security/landlock/bpf_run.h | 25 +++++++
- security/landlock/hooks_ptrace.c | 114 +++++++++++++++++++++++++++++++
- security/landlock/hooks_ptrace.h | 19 ++++++
- security/landlock/init.c | 2 +
- 6 files changed, 224 insertions(+), 2 deletions(-)
- create mode 100644 security/landlock/bpf_run.c
- create mode 100644 security/landlock/bpf_run.h
- create mode 100644 security/landlock/hooks_ptrace.c
- create mode 100644 security/landlock/hooks_ptrace.h
+ security/landlock/Makefile | 2 +-
+ security/landlock/ptrace.c | 120 +++++++++++++++++++++++++++++++++++++
+ security/landlock/ptrace.h | 14 +++++
+ security/landlock/setup.c | 2 +
+ 4 files changed, 137 insertions(+), 1 deletion(-)
+ create mode 100644 security/landlock/ptrace.c
+ create mode 100644 security/landlock/ptrace.h
diff --git a/security/landlock/Makefile b/security/landlock/Makefile
-index 0b291f2c027c..93e4c2f31c8a 100644
+index 041ea242e627..f1d1eb72fa76 100644
--- a/security/landlock/Makefile
+++ b/security/landlock/Makefile
-@@ -1,6 +1,6 @@
+@@ -1,4 +1,4 @@
obj-$(CONFIG_SECURITY_LANDLOCK) := landlock.o
- landlock-y := init.o \
-- bpf_verify.o bpf_ptrace.o \
-+ bpf_verify.o bpf_run.o bpf_ptrace.o \
- domain_manage.o domain_syscall.o \
-- hooks_cred.o
-+ hooks_cred.o hooks_ptrace.o
-diff --git a/security/landlock/bpf_run.c b/security/landlock/bpf_run.c
+ landlock-y := setup.o object.o ruleset.o \
+- cred.o
++ cred.o ptrace.o
+diff --git a/security/landlock/ptrace.c b/security/landlock/ptrace.c
new file mode 100644
-index 000000000000..8874958bdc30
+index 000000000000..61df38b13f5c
--- /dev/null
-+++ b/security/landlock/bpf_run.c
-@@ -0,0 +1,62 @@
++++ b/security/landlock/ptrace.c
+@@ -0,0 +1,120 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
-+ * Landlock LSM - eBPF program evaluation
-+ *
-+ * Copyright © 2016-2019 Mickaël Salaün <mic@digikod.net>
-+ * Copyright © 2018-2019 ANSSI
-+ */
-+
-+#include <asm/current.h>
-+#include <linux/bpf.h>
-+#include <linux/errno.h>
-+#include <linux/filter.h>
-+#include <linux/rculist.h>
-+#include <uapi/linux/landlock.h>
-+
-+#include "bpf_run.h"
-+#include "common.h"
-+#include "hooks_ptrace.h"
-+
-+static const void *get_prog_ctx(struct landlock_hook_ctx *hook_ctx)
-+{
-+ switch (hook_ctx->type) {
-+ case LANDLOCK_HOOK_PTRACE:
-+ return landlock_get_ctx_ptrace(hook_ctx->ctx_ptrace);
-+ }
-+ WARN_ON(1);
-+ return NULL;
-+}
-+
-+/**
-+ * landlock_access_denied - run Landlock programs tied to a hook
-+ *
-+ * @domain: Landlock domain pointer
-+ * @hook_ctx: non-NULL valid eBPF context pointer
-+ *
-+ * Return true if at least one program return deny, false otherwise.
-+ */
-+bool landlock_access_denied(struct landlock_domain *domain,
-+ struct landlock_hook_ctx *hook_ctx)
-+{
-+ struct landlock_prog_list *prog_list;
-+ const size_t hook = get_hook_index(hook_ctx->type);
-+
-+ if (!domain)
-+ return false;
-+
-+ for (prog_list = domain->programs[hook]; prog_list;
-+ prog_list = prog_list->prev) {
-+ u32 ret;
-+ const void *prog_ctx;
-+
-+ prog_ctx = get_prog_ctx(hook_ctx);
-+ if (!prog_ctx || WARN_ON(IS_ERR(prog_ctx)))
-+ return true;
-+ rcu_read_lock();
-+ ret = BPF_PROG_RUN(prog_list->prog, prog_ctx);
-+ rcu_read_unlock();
-+ if (ret & LANDLOCK_RET_DENY)
-+ return true;
-+ }
-+ return false;
-+}
-diff --git a/security/landlock/bpf_run.h b/security/landlock/bpf_run.h
-new file mode 100644
-index 000000000000..3461cbb8ec12
---- /dev/null
-+++ b/security/landlock/bpf_run.h
-@@ -0,0 +1,25 @@
-+/* SPDX-License-Identifier: GPL-2.0-only */
-+/*
-+ * Landlock LSM - eBPF program evaluation headers
-+ *
-+ * Copyright © 2016-2019 Mickaël Salaün <mic@digikod.net>
-+ * Copyright © 2018-2019 ANSSI
-+ */
-+
-+#ifndef _SECURITY_LANDLOCK_BPF_RUN_H
-+#define _SECURITY_LANDLOCK_BPF_RUN_H
-+
-+#include "common.h"
-+#include "hooks_ptrace.h"
-+
-+struct landlock_hook_ctx {
-+ enum landlock_hook_type type;
-+ union {
-+ struct landlock_hook_ctx_ptrace *ctx_ptrace;
-+ };
-+};
-+
-+bool landlock_access_denied(struct landlock_domain *domain,
-+ struct landlock_hook_ctx *hook_ctx);
-+
-+#endif /* _SECURITY_LANDLOCK_BPF_RUN_H */
-diff --git a/security/landlock/hooks_ptrace.c b/security/landlock/hooks_ptrace.c
-new file mode 100644
-index 000000000000..8e518a472d04
---- /dev/null
-+++ b/security/landlock/hooks_ptrace.c
-@@ -0,0 +1,114 @@
-+// SPDX-License-Identifier: GPL-2.0-only
-+/*
-+ * Landlock LSM - ptrace hooks
-+ *
-+ * Copyright © 2017-2019 Mickaël Salaün <mic@digikod.net>
-+ * Copyright © 2019 ANSSI
++ * Landlock LSM - Ptrace hooks
++ *
++ * Copyright © 2017-2020 Mickaël Salaün <mic@digikod.net>
++ * Copyright © 2020 ANSSI
+ */
+
+#include <asm/current.h>
@@ -173,98 +64,104 @@
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/lsm_hooks.h>
++#include <linux/rcupdate.h>
+#include <linux/sched.h>
-+#include <uapi/linux/landlock.h>
-+
-+#include "bpf_run.h"
++
+#include "common.h"
-+#include "hooks_ptrace.h"
-+
-+struct landlock_hook_ctx_ptrace {
-+ struct landlock_context_ptrace prog_ctx;
-+};
-+
-+const struct landlock_context_ptrace *landlock_get_ctx_ptrace(
-+ const struct landlock_hook_ctx_ptrace *hook_ctx)
-+{
-+ if (WARN_ON(!hook_ctx))
-+ return NULL;
-+
-+ return &hook_ctx->prog_ctx;
-+}
-+
-+static int check_ptrace(struct landlock_domain *domain,
-+ struct task_struct *tracer, struct task_struct *tracee)
-+{
-+ struct landlock_hook_ctx_ptrace ctx_ptrace = {
-+ .prog_ctx = {
-+ .tracer = (uintptr_t)tracer,
-+ .tracee = (uintptr_t)tracee,
-+ },
-+ };
-+ struct landlock_hook_ctx hook_ctx = {
-+ .type = LANDLOCK_HOOK_PTRACE,
-+ .ctx_ptrace = &ctx_ptrace,
-+ };
-+
-+ return landlock_access_denied(domain, &hook_ctx) ? -EPERM : 0;
-+}
++#include "cred.h"
++#include "ptrace.h"
++#include "ruleset.h"
++#include "setup.h"
+
+/**
-+ * hook_ptrace_access_check - determine whether the current process may access
++ * 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: the process to be accessed
-+ * @mode: the mode of attachment
-+ *
-+ * If the current task (i.e. tracer) has one or multiple BPF_LANDLOCK_PTRACE
-+ * programs, then run them with the `struct landlock_context_ptrace` context.
-+ * If one of these programs return LANDLOCK_RET_DENY, then deny access with
-+ * -EPERM, else allow it by returning 0.
-+ */
-+static int hook_ptrace_access_check(struct task_struct *child,
-+ unsigned int mode)
-+{
-+ struct landlock_domain *dom_current;
-+ const size_t hook = get_hook_index(LANDLOCK_HOOK_PTRACE);
-+
-+ dom_current = landlock_cred(current_cred())->domain;
-+ if (!(dom_current && dom_current->programs[hook]))
-+ return 0;
-+ return check_ptrace(dom_current, current, child);
++ * @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 - determine whether another process may trace the
++ * hook_ptrace_traceme - Determines whether another process may trace the
+ * current one
+ *
-+ * @parent: the task proposed to be the tracer
-+ *
-+ * If the parent task (i.e. tracer) has one or multiple BPF_LANDLOCK_PTRACE
-+ * programs, then run them with the `struct landlock_context_ptrace` context.
-+ * If one of these programs return LANDLOCK_RET_DENY, then deny access with
-+ * -EPERM, else allow it by returning 0.
-+ */
-+static int hook_ptrace_traceme(struct task_struct *parent)
-+{
-+ struct landlock_domain *dom_parent;
-+ const size_t hook = get_hook_index(LANDLOCK_HOOK_PTRACE);
-+ int ret;
-+
-+ rcu_read_lock();
-+ dom_parent = landlock_cred(__task_cred(parent))->domain;
-+ if (!(dom_parent && dom_parent->programs[hook])) {
-+ ret = 0;
-+ goto put_rcu;
-+ }
-+ ret = check_ptrace(dom_parent, parent, current);
-+
-+put_rcu:
-+ rcu_read_unlock();
-+ return ret;
-+}
-+
-+static struct security_hook_list landlock_hooks[] = {
++ * @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),
+};
@@ -274,48 +171,46 @@
+ security_add_hooks(landlock_hooks, ARRAY_SIZE(landlock_hooks),
+ LANDLOCK_NAME);
+}
-diff --git a/security/landlock/hooks_ptrace.h b/security/landlock/hooks_ptrace.h
+diff --git a/security/landlock/ptrace.h b/security/landlock/ptrace.h
new file mode 100644
-index 000000000000..53fe651bdb3e
+index 000000000000..6740c6a723de
--- /dev/null
-+++ b/security/landlock/hooks_ptrace.h
-@@ -0,0 +1,19 @@
++++ b/security/landlock/ptrace.h
+@@ -0,0 +1,14 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
-+ * Landlock LSM - ptrace hooks headers
++ * Landlock LSM - Ptrace hooks
+ *
+ * Copyright © 2017-2019 Mickaël Salaün <mic@digikod.net>
+ * Copyright © 2019 ANSSI
+ */
+
-+#ifndef _SECURITY_LANDLOCK_HOOKS_PTRACE_H
-+#define _SECURITY_LANDLOCK_HOOKS_PTRACE_H
-+
-+struct landlock_hook_ctx_ptrace;
-+
-+const struct landlock_context_ptrace *landlock_get_ctx_ptrace(
-+ const struct landlock_hook_ctx_ptrace *hook_ctx);
++#ifndef _SECURITY_LANDLOCK_PTRACE_H
++#define _SECURITY_LANDLOCK_PTRACE_H
+
+__init void landlock_add_hooks_ptrace(void);
+
-+#endif /* _SECURITY_LANDLOCK_HOOKS_PTRACE_H */
-diff --git a/security/landlock/init.c b/security/landlock/init.c
-index 8836ec4defd3..541aad17418e 100644
---- a/security/landlock/init.c
-+++ b/security/landlock/init.c
-@@ -10,11 +10,13 @@
++#endif /* _SECURITY_LANDLOCK_PTRACE_H */
+diff --git a/security/landlock/setup.c b/security/landlock/setup.c
+index 39ee1766f175..5e7540fdeefa 100644
+--- a/security/landlock/setup.c
++++ b/security/landlock/setup.c
+@@ -11,6 +11,7 @@
#include "common.h"
- #include "hooks_cred.h"
-+#include "hooks_ptrace.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)
{
- pr_info(LANDLOCK_NAME ": Registering hooks\n");
landlock_add_hooks_cred();
+ landlock_add_hooks_ptrace();
+ pr_info("Up and running.\n");
return 0;
}
-
--
-2.23.0
+2.28.0.rc2
+