Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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>

authored by

Mickaël Salaün and committed by
James Morris
afe81f75 385975dc

+137 -1
+1 -1
security/landlock/Makefile
··· 1 1 obj-$(CONFIG_SECURITY_LANDLOCK) := landlock.o 2 2 3 3 landlock-y := setup.o object.o ruleset.o \ 4 - cred.o 4 + cred.o ptrace.o
+120
security/landlock/ptrace.c
··· 1 + // SPDX-License-Identifier: GPL-2.0-only 2 + /* 3 + * Landlock LSM - Ptrace hooks 4 + * 5 + * Copyright © 2017-2020 Mickaël Salaün <mic@digikod.net> 6 + * Copyright © 2019-2020 ANSSI 7 + */ 8 + 9 + #include <asm/current.h> 10 + #include <linux/cred.h> 11 + #include <linux/errno.h> 12 + #include <linux/kernel.h> 13 + #include <linux/lsm_hooks.h> 14 + #include <linux/rcupdate.h> 15 + #include <linux/sched.h> 16 + 17 + #include "common.h" 18 + #include "cred.h" 19 + #include "ptrace.h" 20 + #include "ruleset.h" 21 + #include "setup.h" 22 + 23 + /** 24 + * domain_scope_le - Checks domain ordering for scoped ptrace 25 + * 26 + * @parent: Parent domain. 27 + * @child: Potential child of @parent. 28 + * 29 + * Checks if the @parent domain is less or equal to (i.e. an ancestor, which 30 + * means a subset of) the @child domain. 31 + */ 32 + static bool domain_scope_le(const struct landlock_ruleset *const parent, 33 + const struct landlock_ruleset *const child) 34 + { 35 + const struct landlock_hierarchy *walker; 36 + 37 + if (!parent) 38 + return true; 39 + if (!child) 40 + return false; 41 + for (walker = child->hierarchy; walker; walker = walker->parent) { 42 + if (walker == parent->hierarchy) 43 + /* @parent is in the scoped hierarchy of @child. */ 44 + return true; 45 + } 46 + /* There is no relationship between @parent and @child. */ 47 + return false; 48 + } 49 + 50 + static bool task_is_scoped(const struct task_struct *const parent, 51 + const struct task_struct *const child) 52 + { 53 + bool is_scoped; 54 + const struct landlock_ruleset *dom_parent, *dom_child; 55 + 56 + rcu_read_lock(); 57 + dom_parent = landlock_get_task_domain(parent); 58 + dom_child = landlock_get_task_domain(child); 59 + is_scoped = domain_scope_le(dom_parent, dom_child); 60 + rcu_read_unlock(); 61 + return is_scoped; 62 + } 63 + 64 + static int task_ptrace(const struct task_struct *const parent, 65 + const struct task_struct *const child) 66 + { 67 + /* Quick return for non-landlocked tasks. */ 68 + if (!landlocked(parent)) 69 + return 0; 70 + if (task_is_scoped(parent, child)) 71 + return 0; 72 + return -EPERM; 73 + } 74 + 75 + /** 76 + * hook_ptrace_access_check - Determines whether the current process may access 77 + * another 78 + * 79 + * @child: Process to be accessed. 80 + * @mode: Mode of attachment. 81 + * 82 + * If the current task has Landlock rules, then the child must have at least 83 + * the same rules. Else denied. 84 + * 85 + * Determines whether a process may access another, returning 0 if permission 86 + * granted, -errno if denied. 87 + */ 88 + static int hook_ptrace_access_check(struct task_struct *const child, 89 + const unsigned int mode) 90 + { 91 + return task_ptrace(current, child); 92 + } 93 + 94 + /** 95 + * hook_ptrace_traceme - Determines whether another process may trace the 96 + * current one 97 + * 98 + * @parent: Task proposed to be the tracer. 99 + * 100 + * If the parent has Landlock rules, then the current task must have the same 101 + * or more rules. Else denied. 102 + * 103 + * Determines whether the nominated task is permitted to trace the current 104 + * process, returning 0 if permission is granted, -errno if denied. 105 + */ 106 + static int hook_ptrace_traceme(struct task_struct *const parent) 107 + { 108 + return task_ptrace(parent, current); 109 + } 110 + 111 + static struct security_hook_list landlock_hooks[] __lsm_ro_after_init = { 112 + LSM_HOOK_INIT(ptrace_access_check, hook_ptrace_access_check), 113 + LSM_HOOK_INIT(ptrace_traceme, hook_ptrace_traceme), 114 + }; 115 + 116 + __init void landlock_add_ptrace_hooks(void) 117 + { 118 + security_add_hooks(landlock_hooks, ARRAY_SIZE(landlock_hooks), 119 + LANDLOCK_NAME); 120 + }
+14
security/landlock/ptrace.h
··· 1 + /* SPDX-License-Identifier: GPL-2.0-only */ 2 + /* 3 + * Landlock LSM - Ptrace hooks 4 + * 5 + * Copyright © 2017-2019 Mickaël Salaün <mic@digikod.net> 6 + * Copyright © 2019 ANSSI 7 + */ 8 + 9 + #ifndef _SECURITY_LANDLOCK_PTRACE_H 10 + #define _SECURITY_LANDLOCK_PTRACE_H 11 + 12 + __init void landlock_add_ptrace_hooks(void); 13 + 14 + #endif /* _SECURITY_LANDLOCK_PTRACE_H */
+2
security/landlock/setup.c
··· 11 11 12 12 #include "common.h" 13 13 #include "cred.h" 14 + #include "ptrace.h" 14 15 #include "setup.h" 15 16 16 17 struct lsm_blob_sizes landlock_blob_sizes __lsm_ro_after_init = { ··· 21 20 static int __init landlock_init(void) 22 21 { 23 22 landlock_add_cred_hooks(); 23 + landlock_add_ptrace_hooks(); 24 24 pr_info("Up and running.\n"); 25 25 return 0; 26 26 }