--- /dev/null
+// 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);
+}