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 | * - %LANDLOCK_SCOPE_ABSTRACT_UNIX_SOCKET: Restrict a sandboxed process from | 296 | * - %LANDLOCK_SCOPE_ABSTRACT_UNIX_SOCKET: Restrict a sandboxed process from |
| 297 | * connecting to an abstract UNIX socket created by a process outside the | 297 | * connecting to an abstract UNIX socket created by a process outside the |
| 298 | * related Landlock domain (e.g. a parent domain or a non-sandboxed process). | 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 | /* clang-format off */ | 302 | /* clang-format off */ |
| 301 | #define LANDLOCK_SCOPE_ABSTRACT_UNIX_SOCKET (1ULL << 0) | 303 | #define LANDLOCK_SCOPE_ABSTRACT_UNIX_SOCKET (1ULL << 0) |
| 304 | #define LANDLOCK_SCOPE_SIGNAL (1ULL << 1) | ||
| 302 | /* clang-format on*/ | 305 | /* clang-format on*/ |
| 303 | 306 | ||
| 304 | #endif /* _UAPI_LINUX_LANDLOCK_H */ | 307 | #endif /* _UAPI_LINUX_LANDLOCK_H */ |
| @@ -26,7 +26,7 @@ landlock_cred(const struct cred *cred) · 25 unmodified lines | |||
| 26 | return cred->security + landlock_blob_sizes.lbs_cred; | 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 | return landlock_cred(current_cred())->domain; | 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 | return -EACCES; | 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 | static struct security_hook_list landlock_hooks[] __ro_after_init = { | 1665 | static struct security_hook_list landlock_hooks[] __ro_after_init = { |
| 1643 | LSM_HOOK_INIT(inode_free_security_rcu, hook_inode_free_security_rcu), | 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 | LSM_HOOK_INIT(file_truncate, hook_file_truncate), | 1686 | LSM_HOOK_INIT(file_truncate, hook_file_truncate), |
| 1664 | LSM_HOOK_INIT(file_ioctl, hook_file_ioctl), | 1687 | LSM_HOOK_INIT(file_ioctl, hook_file_ioctl), |
| 1665 | LSM_HOOK_INIT(file_ioctl_compat, hook_file_ioctl_compat), | 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 | __init void landlock_add_fs_hooks(void) | 1693 | __init void landlock_add_fs_hooks(void) |
| @@ -52,6 +52,13 @@ struct landlock_file_security { · 51 unmodified lines | |||
| 52 | * needed to authorize later operations on the open file. | 52 | * needed to authorize later operations on the open file. |
| 53 | */ | 53 | */ |
| 54 | access_mask_t allowed_access; | 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 | #define LANDLOCK_MASK_ACCESS_NET ((LANDLOCK_LAST_ACCESS_NET << 1) - 1) | 26 | #define LANDLOCK_MASK_ACCESS_NET ((LANDLOCK_LAST_ACCESS_NET << 1) - 1) |
| 27 | #define LANDLOCK_NUM_ACCESS_NET __const_hweight64(LANDLOCK_MASK_ACCESS_NET) | 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 | #define LANDLOCK_MASK_SCOPE ((LANDLOCK_LAST_SCOPE << 1) - 1) | 30 | #define LANDLOCK_MASK_SCOPE ((LANDLOCK_LAST_SCOPE << 1) - 1) |
| 31 | #define LANDLOCK_NUM_SCOPE __const_hweight64(LANDLOCK_MASK_SCOPE) | 31 | #define LANDLOCK_NUM_SCOPE __const_hweight64(LANDLOCK_MASK_SCOPE) |
| 32 | /* clang-format on */ | 32 | /* clang-format on */ |
| @@ -18,6 +18,7 @@ · 17 unmodified lines | |||
| 18 | 18 | ||
| 19 | #include "common.h" | 19 | #include "common.h" |
| 20 | #include "cred.h" | 20 | #include "cred.h" |
| 21 | #include "fs.h" | ||
| 21 | #include "ruleset.h" | 22 | #include "ruleset.h" |
| 22 | #include "setup.h" | 23 | #include "setup.h" |
| 23 | #include "task.h" | 24 | #include "task.h" |
| @@ -242,12 +243,67 @@ static int hook_unix_may_send(struct socket *const sock, · 218 unmodified lines | |||
| 242 | return 0; | 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 | static struct security_hook_list landlock_hooks[] __ro_after_init = { | 298 | static struct security_hook_list landlock_hooks[] __ro_after_init = { |
| 246 | LSM_HOOK_INIT(ptrace_access_check, hook_ptrace_access_check), | 299 | LSM_HOOK_INIT(ptrace_access_check, hook_ptrace_access_check), |
| 247 | LSM_HOOK_INIT(ptrace_traceme, hook_ptrace_traceme), | 300 | LSM_HOOK_INIT(ptrace_traceme, hook_ptrace_traceme), |
| 248 | 301 | ||
| 249 | LSM_HOOK_INIT(unix_stream_connect, hook_unix_stream_connect), | 302 | LSM_HOOK_INIT(unix_stream_connect, hook_unix_stream_connect), |
| 250 | LSM_HOOK_INIT(unix_may_send, hook_unix_may_send), | 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 | __init void landlock_add_task_hooks(void) | 309 | __init void landlock_add_task_hooks(void) |
| @@ -12,7 +12,7 @@ · 11 unmodified lines | |||
| 12 | 12 | ||
| 13 | #include "common.h" | 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 | TEST(ruleset_with_unknown_scope) | 17 | TEST(ruleset_with_unknown_scope) |
| 18 | { | 18 | { |