1 // SPDX-License-Identifier: GPL-2.0-only 2 /* 3 * Landlock - Ptrace and scope hooks 4 * 5 * Copyright © 2017-2020 Mickaël Salaün <mic@digikod.net> 6 * Copyright © 2019-2020 ANSSI 7 * Copyright © 2024-2025 Microsoft Corporation 8 */ 9 10 #include <asm/current.h> 11 #include <linux/cleanup.h> 12 #include <linux/cred.h> 13 #include <linux/errno.h> 14 #include <linux/kernel.h> 15 #include <linux/lsm_audit.h> 16 #include <linux/lsm_hooks.h> 17 #include <linux/rcupdate.h> 18 #include <linux/sched.h> 19 #include <linux/sched/signal.h> 20 #include <net/af_unix.h> 21 #include <net/sock.h> 22 23 #include "audit.h" 24 #include "common.h" 25 #include "cred.h" 26 #include "domain.h" 27 #include "fs.h" 28 #include "ruleset.h" 29 #include "setup.h" 30 #include "task.h" 31 32 /** 33 * domain_scope_le - Checks domain ordering for scoped ptrace 34 * 35 * @parent: Parent domain. 36 * @child: Potential child of @parent. 37 * 38 * Checks if the @parent domain is less or equal to (i.e. an ancestor, which 39 * means a subset of) the @child domain. 40 * 41 * Return: True if @parent is an ancestor of or equal to @child, false 42 * otherwise. 43 */ 44 static bool domain_scope_le(const struct landlock_ruleset *const parent, 45 const struct landlock_ruleset *const child) 46 { 47 const struct landlock_hierarchy *walker; 48 49 /* Quick return for non-landlocked tasks. */ 50 if (!parent) 51 return true; 52 53 if (!child) 54 return false; 55 56 for (walker = child->hierarchy; walker; walker = walker->parent) { 57 if (walker == parent->hierarchy) 58 /* @parent is in the scoped hierarchy of @child. */ 59 return true; 60 } 61 62 /* There is no relationship between @parent and @child. */ 63 return false; 64 } 65 66 static int domain_ptrace(const struct landlock_ruleset *const parent, 67 const struct landlock_ruleset *const child) 68 { 69 if (domain_scope_le(parent, child)) 70 return 0; 71 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 * Return: 0 if permission is granted, -errno if denied. 86 */ 87 static int hook_ptrace_access_check(struct task_struct *const child, 88 const unsigned int mode) 89 { 90 const struct landlock_cred_security *parent_subject; 91 int err; 92 93 /* Quick return for non-landlocked tasks. */ 94 parent_subject = landlock_cred(current_cred()); 95 if (!parent_subject) 96 return 0; 97 98 scoped_guard(rcu) 99 { 100 const struct landlock_ruleset *const child_dom = 101 landlock_get_task_domain(child); 102 err = domain_ptrace(parent_subject->domain, child_dom); 103 } 104 105 if (!err) 106 return 0; 107 108 /* 109 * For the ptrace_access_check case, we log the current/parent domain 110 * and the child task. 111 */ 112 if (!(mode & PTRACE_MODE_NOAUDIT)) 113 landlock_log_denial(parent_subject, &(struct landlock_request) { 114 .type = LANDLOCK_REQUEST_PTRACE, 115 .audit = { 116 .type = LSM_AUDIT_DATA_TASK, 117 .u.tsk = child, 118 }, 119 .layer_plus_one = parent_subject->domain->num_layers, 120 }); 121 122 return err; 123 } 124 125 /** 126 * hook_ptrace_traceme - Determines whether another process may trace the 127 * current one 128 * 129 * @parent: Task proposed to be the tracer. 130 * 131 * If the parent has Landlock rules, then the current task must have the same 132 * or more rules. Else denied. 133 * 134 * Return: 0 if permission is granted, -errno if denied. 135 */ 136 static int hook_ptrace_traceme(struct task_struct *const parent) 137 { 138 const struct landlock_cred_security *parent_subject; 139 const struct landlock_ruleset *child_dom; 140 int err; 141 142 child_dom = landlock_get_current_domain(); 143 144 guard(rcu)(); 145 parent_subject = landlock_cred(__task_cred(parent)); 146 err = domain_ptrace(parent_subject->domain, child_dom); 147 148 if (!err) 149 return 0; 150 151 /* 152 * For the ptrace_traceme case, we log the domain which is the cause of 153 * the denial, which means the parent domain instead of the current 154 * domain. This may look unusual because the ptrace_traceme action is a 155 * request to be traced, but the semantic is consistent with 156 * hook_ptrace_access_check(). 157 */ 158 landlock_log_denial(parent_subject, &(struct landlock_request) { 159 .type = LANDLOCK_REQUEST_PTRACE, 160 .audit = { 161 .type = LSM_AUDIT_DATA_TASK, 162 .u.tsk = current, 163 }, 164 .layer_plus_one = parent_subject->domain->num_layers, 165 }); 166 return err; 167 } 168 169 /** 170 * domain_is_scoped - Check if an interaction from a client/sender to a 171 * server/receiver should be restricted based on scope controls. 172 * 173 * @client: IPC sender domain. 174 * @server: IPC receiver domain. 175 * @scope: The scope restriction criteria. 176 * 177 * Returns: True if @server is in a different domain from @client, and @client 178 * is scoped to access @server (i.e. access should be denied). 179 */ 180 static bool domain_is_scoped(const struct landlock_ruleset *const client, 181 const struct landlock_ruleset *const server, 182 access_mask_t scope) 183 { 184 int client_layer, server_layer; 185 const struct landlock_hierarchy *client_walker, *server_walker; 186 187 /* Quick return if client has no domain */ 188 if (WARN_ON_ONCE(!client)) 189 return false; 190 191 client_layer = client->num_layers - 1; 192 client_walker = client->hierarchy; 193 /* 194 * client_layer must be a signed integer with greater capacity 195 * than client->num_layers to ensure the following loop stops. 196 */ 197 BUILD_BUG_ON(sizeof(client_layer) > sizeof(client->num_layers)); 198 199 server_layer = server ? (server->num_layers - 1) : -1; 200 server_walker = server ? server->hierarchy : NULL; 201 202 /* 203 * Walks client's parent domains down to the same hierarchy level 204 * as the server's domain, and checks that none of these client's 205 * parent domains are scoped. 206 */ 207 for (; client_layer > server_layer; client_layer--) { 208 if (landlock_get_scope_mask(client, client_layer) & scope) 209 return true; 210 211 client_walker = client_walker->parent; 212 } 213 /* 214 * Walks server's parent domains down to the same hierarchy level as 215 * the client's domain. 216 */ 217 for (; server_layer > client_layer; server_layer--) 218 server_walker = server_walker->parent; 219 220 for (; client_layer >= 0; client_layer--) { 221 if (landlock_get_scope_mask(client, client_layer) & scope) { 222 /* 223 * Client and server are at the same level in the 224 * hierarchy. If the client is scoped, the request is 225 * only allowed if this domain is also a server's 226 * ancestor. 227 */ 228 return server_walker != client_walker; 229 } 230 client_walker = client_walker->parent; 231 server_walker = server_walker->parent; 232 } 233 return false; 234 } 235 236 static bool sock_is_scoped(struct sock *const other, 237 const struct landlock_ruleset *const domain) 238 { 239 const struct landlock_ruleset *dom_other; 240 241 /* The credentials will not change. */ 242 lockdep_assert_held(&unix_sk(other)->lock); 243 dom_other = landlock_cred(other->sk_socket->file->f_cred)->domain; 244 return domain_is_scoped(domain, dom_other, 245 LANDLOCK_SCOPE_ABSTRACT_UNIX_SOCKET); 246 } 247 248 static bool is_abstract_socket(struct sock *const sock) 249 { 250 struct unix_address *addr = unix_sk(sock)->addr; 251 252 if (!addr) 253 return false; 254 255 if (addr->len >= offsetof(struct sockaddr_un, sun_path) + 1 && 256 addr->name->sun_path[0] == '\0') 257 return true; 258 259 return false; 260 } 261 262 static const struct access_masks unix_scope = { 263 .scope = LANDLOCK_SCOPE_ABSTRACT_UNIX_SOCKET, 264 }; 265 266 static int hook_unix_stream_connect(struct sock *const sock, 267 struct sock *const other, 268 struct sock *const newsk) 269 { 270 size_t handle_layer; 271 const struct landlock_cred_security *const subject = 272 landlock_get_applicable_subject(current_cred(), unix_scope, 273 &handle_layer); 274 275 /* Quick return for non-landlocked tasks. */ 276 if (!subject) 277 return 0; 278 279 if (!is_abstract_socket(other)) 280 return 0; 281 282 if (!sock_is_scoped(other, subject->domain)) 283 return 0; 284 285 landlock_log_denial(subject, &(struct landlock_request) { 286 .type = LANDLOCK_REQUEST_SCOPE_ABSTRACT_UNIX_SOCKET, 287 .audit = { 288 .type = LSM_AUDIT_DATA_NET, 289 .u.net = &(struct lsm_network_audit) { 290 .sk = other, 291 }, 292 }, 293 .layer_plus_one = handle_layer + 1, 294 }); 295 return -EPERM; 296 } 297 298 static int hook_unix_may_send(struct socket *const sock, 299 struct socket *const other) 300 { 301 size_t handle_layer; 302 const struct landlock_cred_security *const subject = 303 landlock_get_applicable_subject(current_cred(), unix_scope, 304 &handle_layer); 305 306 if (!subject) 307 return 0; 308 309 /* 310 * Checks if this datagram socket was already allowed to be connected 311 * to other. 312 */ 313 if (unix_peer(sock->sk) == other->sk) 314 return 0; 315 316 if (!is_abstract_socket(other->sk)) 317 return 0; 318 319 if (!sock_is_scoped(other->sk, subject->domain)) 320 return 0; 321 322 landlock_log_denial(subject, &(struct landlock_request) { 323 .type = LANDLOCK_REQUEST_SCOPE_ABSTRACT_UNIX_SOCKET, 324 .audit = { 325 .type = LSM_AUDIT_DATA_NET, 326 .u.net = &(struct lsm_network_audit) { 327 .sk = other->sk, 328 }, 329 }, 330 .layer_plus_one = handle_layer + 1, 331 }); 332 return -EPERM; 333 } 334 335 static const struct access_masks signal_scope = { 336 .scope = LANDLOCK_SCOPE_SIGNAL, 337 }; 338 339 static int hook_task_kill(struct task_struct *const p, 340 struct kernel_siginfo *const info, const int sig, 341 const struct cred *cred) 342 { 343 bool is_scoped; 344 size_t handle_layer; 345 const struct landlock_cred_security *subject; 346 347 if (!cred) { 348 /* 349 * Always allow sending signals between threads of the same process. 350 * This is required for process credential changes by the Native POSIX 351 * Threads Library and implemented by the set*id(2) wrappers and 352 * libcap(3) with tgkill(2). See nptl(7) and libpsx(3). 353 * 354 * This exception is similar to the __ptrace_may_access() one. 355 */ 356 if (same_thread_group(p, current)) 357 return 0; 358 359 /* Not dealing with USB IO. */ 360 cred = current_cred(); 361 } 362 363 subject = landlock_get_applicable_subject(cred, signal_scope, 364 &handle_layer); 365 366 /* Quick return for non-landlocked tasks. */ 367 if (!subject) 368 return 0; 369 370 scoped_guard(rcu) 371 { 372 is_scoped = domain_is_scoped(subject->domain, 373 landlock_get_task_domain(p), 374 signal_scope.scope); 375 } 376 377 if (!is_scoped) 378 return 0; 379 380 landlock_log_denial(subject, &(struct landlock_request) { 381 .type = LANDLOCK_REQUEST_SCOPE_SIGNAL, 382 .audit = { 383 .type = LSM_AUDIT_DATA_TASK, 384 .u.tsk = p, 385 }, 386 .layer_plus_one = handle_layer + 1, 387 }); 388 return -EPERM; 389 } 390 391 static int hook_file_send_sigiotask(struct task_struct *tsk, 392 struct fown_struct *fown, int signum) 393 { 394 const struct landlock_cred_security *subject; 395 bool is_scoped = false; 396 397 /* Lock already held by send_sigio() and send_sigurg(). */ 398 lockdep_assert_held(&fown->lock); 399 subject = &landlock_file(fown->file)->fown_subject; 400 401 /* 402 * Quick return for unowned socket. 403 * 404 * subject->domain has already been filtered when saved by 405 * hook_file_set_fowner(), so there is no need to call 406 * landlock_get_applicable_subject() here. 407 */ 408 if (!subject->domain) 409 return 0; 410 411 scoped_guard(rcu) 412 { 413 is_scoped = domain_is_scoped(subject->domain, 414 landlock_get_task_domain(tsk), 415 signal_scope.scope); 416 } 417 418 if (!is_scoped) 419 return 0; 420 421 landlock_log_denial(subject, &(struct landlock_request) { 422 .type = LANDLOCK_REQUEST_SCOPE_SIGNAL, 423 .audit = { 424 .type = LSM_AUDIT_DATA_TASK, 425 .u.tsk = tsk, 426 }, 427 #ifdef CONFIG_AUDIT 428 .layer_plus_one = landlock_file(fown->file)->fown_layer + 1, 429 #endif /* CONFIG_AUDIT */ 430 }); 431 return -EPERM; 432 } 433 434 static struct security_hook_list landlock_hooks[] __ro_after_init = { 435 LSM_HOOK_INIT(ptrace_access_check, hook_ptrace_access_check), 436 LSM_HOOK_INIT(ptrace_traceme, hook_ptrace_traceme), 437 438 LSM_HOOK_INIT(unix_stream_connect, hook_unix_stream_connect), 439 LSM_HOOK_INIT(unix_may_send, hook_unix_may_send), 440 441 LSM_HOOK_INIT(task_kill, hook_task_kill), 442 LSM_HOOK_INIT(file_send_sigiotask, hook_file_send_sigiotask), 443 }; 444 445 __init void landlock_add_task_hooks(void) 446 { 447 security_add_hooks(landlock_hooks, ARRAY_SIZE(landlock_hooks), 448 &landlock_lsmid); 449 } 450