Currently, a sandbox process is not restricted to sending a signal (e.g. SIGKILL) to a process outside the sandbox environment. The ability to send a signal for a sandboxed process should be scoped the same way abstract UNIX sockets are scoped. Therefore, we extend the "scoped" field in a ruleset with LANDLOCK_SCOPE_SIGNAL to specify that a ruleset will deny sending any signal from within a sandbox process to its parent (i.e. any parent sandbox or non-sandboxed processes). This patch adds file_set_fowner and file_free_security hooks to set and release a pointer to the file owner's domain. This pointer, fown_domain in landlock_file_security will be used in file_send_sigiotask to check if the process can send a signal. The ruleset_with_unknown_scope test is updated to support LANDLOCK_SCOPE_SIGNAL. This depends on two new changes: - commit 1934b212615d ("file: reclaim 24 bytes from f_owner"): replace container_of(fown, struct file, f_owner) with fown->file . - commit 26f204380a3c ("fs: Fix file_set_fowner LSM hook inconsistencies"): lock before calling the hook.
Unified · Split
| @@ -296,9 +296,12 @@ struct landlock_net_port_attr { · 295 unmodified lines | ||
| 296 | 296 | * - %LANDLOCK_SCOPE_ABSTRACT_UNIX_SOCKET: Restrict a sandboxed process from |
| 297 | 297 | * connecting to an abstract UNIX socket created by a process outside the |
| 298 | 298 | * related Landlock domain (e.g. a parent domain or a non-sandboxed process). |
| 299 | + * - %LANDLOCK_SCOPE_SIGNAL: Restrict a sandboxed process from sending a signal | |
| 300 | + * to another process outside the domain. | |
| 299 | 301 | */ |
| 300 | 302 | /* clang-format off */ |
| 301 | 303 | #define LANDLOCK_SCOPE_ABSTRACT_UNIX_SOCKET (1ULL << 0) |
| 304 | +#define LANDLOCK_SCOPE_SIGNAL (1ULL << 1) | |
| 302 | 305 | /* clang-format on*/ |
| 303 | 306 | |
| 304 | 307 | #endif /* _UAPI_LINUX_LANDLOCK_H */ |
| @@ -26,7 +26,7 @@ landlock_cred(const struct cred *cred) · 25 unmodified lines | ||
| 26 | 26 | return cred->security + landlock_blob_sizes.lbs_cred; |
| 27 | 27 | } |
| 28 | 28 | |
| 29 | −static inline const struct landlock_ruleset *landlock_get_current_domain(void) | |
| 29 | +static inline struct landlock_ruleset *landlock_get_current_domain(void) | |
| 30 | 30 | { |
| 31 | 31 | return landlock_cred(current_cred())->domain; |
| 32 | 32 | } |
| @@ -1639,6 +1639,29 @@ static int hook_file_ioctl_compat(struct file *file, unsigned int cmd, · 1638 unmodified lines | ||
| 1639 | 1639 | return -EACCES; |
| 1640 | 1640 | } |
| 1641 | 1641 | |
| 1642 | +static void hook_file_set_fowner(struct file *file) | |
| 1643 | +{ | |
| 1644 | + struct landlock_ruleset *new_dom, *prev_dom; | |
| 1645 | + | |
| 1646 | + /* | |
| 1647 | + * Lock already held by __f_setown(), see commit 26f204380a3c ("fs: Fix | |
| 1648 | + * file_set_fowner LSM hook inconsistencies"). | |
| 1649 | + */ | |
| 1650 | + lockdep_assert_held(&file_f_owner(file)->lock); | |
| 1651 | + new_dom = landlock_get_current_domain(); | |
| 1652 | + landlock_get_ruleset(new_dom); | |
| 1653 | + prev_dom = landlock_file(file)->fown_domain; | |
| 1654 | + landlock_file(file)->fown_domain = new_dom; | |
| 1655 | + | |
| 1656 | + /* Called in an RCU read-side critical section. */ | |
| 1657 | + landlock_put_ruleset_deferred(prev_dom); | |
| 1658 | +} | |
| 1659 | + | |
| 1660 | +static void hook_file_free_security(struct file *file) | |
| 1661 | +{ | |
| 1662 | + landlock_put_ruleset_deferred(landlock_file(file)->fown_domain); | |
| 1663 | +} | |
| 1664 | + | |
| 1642 | 1665 | static struct security_hook_list landlock_hooks[] __ro_after_init = { |
| 1643 | 1666 | LSM_HOOK_INIT(inode_free_security_rcu, hook_inode_free_security_rcu), |
| 1644 | 1667 | |
| @@ -1663,6 +1686,8 @@ static struct security_hook_list landlock_hooks[] __ro_after_init = { · 18 unmodified lines | ||
| 1663 | 1686 | LSM_HOOK_INIT(file_truncate, hook_file_truncate), |
| 1664 | 1687 | LSM_HOOK_INIT(file_ioctl, hook_file_ioctl), |
| 1665 | 1688 | LSM_HOOK_INIT(file_ioctl_compat, hook_file_ioctl_compat), |
| 1689 | + LSM_HOOK_INIT(file_set_fowner, hook_file_set_fowner), | |
| 1690 | + LSM_HOOK_INIT(file_free_security, hook_file_free_security), | |
| 1666 | 1691 | }; |
| 1667 | 1692 | |
| 1668 | 1693 | __init void landlock_add_fs_hooks(void) |
| @@ -52,6 +52,13 @@ struct landlock_file_security { · 51 unmodified lines | ||
| 52 | 52 | * needed to authorize later operations on the open file. |
| 53 | 53 | */ |
| 54 | 54 | access_mask_t allowed_access; |
| 55 | + /** | |
| 56 | + * @fown_domain: Domain of the task that set the PID that may receive a | |
| 57 | + * signal e.g., SIGURG when writing MSG_OOB to the related socket. | |
| 58 | + * This pointer is protected by the related file->f_owner->lock, as for | |
| 59 | + * fown_struct's members: pid, uid, and euid. | |
| 60 | + */ | |
| 61 | + struct landlock_ruleset *fown_domain; | |
| 55 | 62 | }; |
| 56 | 63 | |
| 57 | 64 | /** |
| @@ -26,7 +26,7 @@ · 25 unmodified lines | ||
| 26 | 26 | #define LANDLOCK_MASK_ACCESS_NET ((LANDLOCK_LAST_ACCESS_NET << 1) - 1) |
| 27 | 27 | #define LANDLOCK_NUM_ACCESS_NET __const_hweight64(LANDLOCK_MASK_ACCESS_NET) |
| 28 | 28 | |
| 29 | −#define LANDLOCK_LAST_SCOPE LANDLOCK_SCOPE_ABSTRACT_UNIX_SOCKET | |
| 29 | +#define LANDLOCK_LAST_SCOPE LANDLOCK_SCOPE_SIGNAL | |
| 30 | 30 | #define LANDLOCK_MASK_SCOPE ((LANDLOCK_LAST_SCOPE << 1) - 1) |
| 31 | 31 | #define LANDLOCK_NUM_SCOPE __const_hweight64(LANDLOCK_MASK_SCOPE) |
| 32 | 32 | /* clang-format on */ |
| @@ -18,6 +18,7 @@ · 17 unmodified lines | ||
| 18 | 18 | |
| 19 | 19 | #include "common.h" |
| 20 | 20 | #include "cred.h" |
| 21 | +#include "fs.h" | |
| 21 | 22 | #include "ruleset.h" |
| 22 | 23 | #include "setup.h" |
| 23 | 24 | #include "task.h" |
| @@ -242,12 +243,67 @@ static int hook_unix_may_send(struct socket *const sock, · 218 unmodified lines | ||
| 242 | 243 | return 0; |
| 243 | 244 | } |
| 244 | 245 | |
| 246 | +static int hook_task_kill(struct task_struct *const p, | |
| 247 | + struct kernel_siginfo *const info, const int sig, | |
| 248 | + const struct cred *const cred) | |
| 249 | +{ | |
| 250 | + bool is_scoped; | |
| 251 | + const struct landlock_ruleset *dom; | |
| 252 | + | |
| 253 | + if (cred) { | |
| 254 | + /* Dealing with USB IO. */ | |
| 255 | + dom = landlock_cred(cred)->domain; | |
| 256 | + } else { | |
| 257 | + dom = landlock_get_current_domain(); | |
| 258 | + } | |
| 259 | + | |
| 260 | + /* Quick return for non-landlocked tasks. */ | |
| 261 | + if (!dom) | |
| 262 | + return 0; | |
| 263 | + | |
| 264 | + rcu_read_lock(); | |
| 265 | + is_scoped = domain_is_scoped(dom, landlock_get_task_domain(p), | |
| 266 | + LANDLOCK_SCOPE_SIGNAL); | |
| 267 | + rcu_read_unlock(); | |
| 268 | + if (is_scoped) | |
| 269 | + return -EPERM; | |
| 270 | + | |
| 271 | + return 0; | |
| 272 | +} | |
| 273 | + | |
| 274 | +static int hook_file_send_sigiotask(struct task_struct *tsk, | |
| 275 | + struct fown_struct *fown, int signum) | |
| 276 | +{ | |
| 277 | + const struct landlock_ruleset *dom; | |
| 278 | + bool is_scoped = false; | |
| 279 | + | |
| 280 | + /* Lock already held by send_sigio() and send_sigurg(). */ | |
| 281 | + lockdep_assert_held(&fown->lock); | |
| 282 | + dom = landlock_file(fown->file)->fown_domain; | |
| 283 | + | |
| 284 | + /* Quick return for unowned socket. */ | |
| 285 | + if (!dom) | |
| 286 | + return 0; | |
| 287 | + | |
| 288 | + rcu_read_lock(); | |
| 289 | + is_scoped = domain_is_scoped(dom, landlock_get_task_domain(tsk), | |
| 290 | + LANDLOCK_SCOPE_SIGNAL); | |
| 291 | + rcu_read_unlock(); | |
| 292 | + if (is_scoped) | |
| 293 | + return -EPERM; | |
| 294 | + | |
| 295 | + return 0; | |
| 296 | +} | |
| 297 | + | |
| 245 | 298 | static struct security_hook_list landlock_hooks[] __ro_after_init = { |
| 246 | 299 | LSM_HOOK_INIT(ptrace_access_check, hook_ptrace_access_check), |
| 247 | 300 | LSM_HOOK_INIT(ptrace_traceme, hook_ptrace_traceme), |
| 248 | 301 | |
| 249 | 302 | LSM_HOOK_INIT(unix_stream_connect, hook_unix_stream_connect), |
| 250 | 303 | LSM_HOOK_INIT(unix_may_send, hook_unix_may_send), |
| 304 | + | |
| 305 | + LSM_HOOK_INIT(task_kill, hook_task_kill), | |
| 306 | + LSM_HOOK_INIT(file_send_sigiotask, hook_file_send_sigiotask), | |
| 251 | 307 | }; |
| 252 | 308 | |
| 253 | 309 | __init void landlock_add_task_hooks(void) |
| @@ -12,7 +12,7 @@ · 11 unmodified lines | ||
| 12 | 12 | |
| 13 | 13 | #include "common.h" |
| 14 | 14 | |
| 15 | −#define ACCESS_LAST LANDLOCK_SCOPE_ABSTRACT_UNIX_SOCKET | |
| 15 | +#define ACCESS_LAST LANDLOCK_SCOPE_SIGNAL | |
| 16 | 16 | |
| 17 | 17 | TEST(ruleset_with_unknown_scope) |
| 18 | 18 | { |