]> www.infradead.org Git - users/jedix/linux-maple.git/commitdiff
landlock: Add signal scoping
authorTahera Fahimi <fahimitahera@gmail.com>
Fri, 6 Sep 2024 21:30:03 +0000 (15:30 -0600)
committerMickaël Salaün <mic@digikod.net>
Mon, 16 Sep 2024 21:50:52 +0000 (23:50 +0200)
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.

Signed-off-by: Tahera Fahimi <fahimitahera@gmail.com>
Closes: https://github.com/landlock-lsm/linux/issues/8
Link: https://lore.kernel.org/r/df2b4f880a2ed3042992689a793ea0951f6798a5.1725657727.git.fahimitahera@gmail.com
[mic: Update landlock_get_current_domain()'s return type, improve and
fix locking in hook_file_set_fowner(), simplify and fix sleepable call
and locking issue in hook_file_send_sigiotask() and rebase on the latest
VFS tree, simplify hook_task_kill() and quickly return when not
sandboxed, improve comments, rename LANDLOCK_SCOPED_SIGNAL]
Co-developed-by: Mickaël Salaün <mic@digikod.net>
Signed-off-by: Mickaël Salaün <mic@digikod.net>
include/uapi/linux/landlock.h
security/landlock/cred.h
security/landlock/fs.c
security/landlock/fs.h
security/landlock/limits.h
security/landlock/task.c
tools/testing/selftests/landlock/scoped_test.c

index 70edd17bafdc371a90f22835688d56598ba6be6a..33745642f7875a17261fc584322a46f8c894b86f 100644 (file)
@@ -296,9 +296,12 @@ struct landlock_net_port_attr {
  * - %LANDLOCK_SCOPE_ABSTRACT_UNIX_SOCKET: Restrict a sandboxed process from
  *   connecting to an abstract UNIX socket created by a process outside the
  *   related Landlock domain (e.g. a parent domain or a non-sandboxed process).
+ * - %LANDLOCK_SCOPE_SIGNAL: Restrict a sandboxed process from sending a signal
+ *   to another process outside the domain.
  */
 /* clang-format off */
 #define LANDLOCK_SCOPE_ABSTRACT_UNIX_SOCKET            (1ULL << 0)
+#define LANDLOCK_SCOPE_SIGNAL                          (1ULL << 1)
 /* clang-format on*/
 
 #endif /* _UAPI_LINUX_LANDLOCK_H */
index af89ab00e6d10f83815a88f725281dd9e11a4795..bf755459838a557348541183864a1885cd52f89d 100644 (file)
@@ -26,7 +26,7 @@ landlock_cred(const struct cred *cred)
        return cred->security + landlock_blob_sizes.lbs_cred;
 }
 
-static inline const struct landlock_ruleset *landlock_get_current_domain(void)
+static inline struct landlock_ruleset *landlock_get_current_domain(void)
 {
        return landlock_cred(current_cred())->domain;
 }
index 0804f76a67be2e65d27d23ae835c18f967e371ea..7d79fc8abe2172ff9cfe0714bdc0aff8cd9f56d3 100644 (file)
@@ -1639,6 +1639,29 @@ static int hook_file_ioctl_compat(struct file *file, unsigned int cmd,
        return -EACCES;
 }
 
+static void hook_file_set_fowner(struct file *file)
+{
+       struct landlock_ruleset *new_dom, *prev_dom;
+
+       /*
+        * Lock already held by __f_setown(), see commit 26f204380a3c ("fs: Fix
+        * file_set_fowner LSM hook inconsistencies").
+        */
+       lockdep_assert_held(&file_f_owner(file)->lock);
+       new_dom = landlock_get_current_domain();
+       landlock_get_ruleset(new_dom);
+       prev_dom = landlock_file(file)->fown_domain;
+       landlock_file(file)->fown_domain = new_dom;
+
+       /* Called in an RCU read-side critical section. */
+       landlock_put_ruleset_deferred(prev_dom);
+}
+
+static void hook_file_free_security(struct file *file)
+{
+       landlock_put_ruleset_deferred(landlock_file(file)->fown_domain);
+}
+
 static struct security_hook_list landlock_hooks[] __ro_after_init = {
        LSM_HOOK_INIT(inode_free_security_rcu, hook_inode_free_security_rcu),
 
@@ -1663,6 +1686,8 @@ static struct security_hook_list landlock_hooks[] __ro_after_init = {
        LSM_HOOK_INIT(file_truncate, hook_file_truncate),
        LSM_HOOK_INIT(file_ioctl, hook_file_ioctl),
        LSM_HOOK_INIT(file_ioctl_compat, hook_file_ioctl_compat),
+       LSM_HOOK_INIT(file_set_fowner, hook_file_set_fowner),
+       LSM_HOOK_INIT(file_free_security, hook_file_free_security),
 };
 
 __init void landlock_add_fs_hooks(void)
index 488e4813680ab7f104a3f63792f79de5ae58fb03..1487e1f023a19234bca5b4d7a6260e53f4db5d94 100644 (file)
@@ -52,6 +52,13 @@ struct landlock_file_security {
         * needed to authorize later operations on the open file.
         */
        access_mask_t allowed_access;
+       /**
+        * @fown_domain: Domain of the task that set the PID that may receive a
+        * signal e.g., SIGURG when writing MSG_OOB to the related socket.
+        * This pointer is protected by the related file->f_owner->lock, as for
+        * fown_struct's members: pid, uid, and euid.
+        */
+       struct landlock_ruleset *fown_domain;
 };
 
 /**
index d74818003ed4d7b2fd267b858d050b4f55bddc98..15f7606066c807dee0e931d98bb86e6b7182797e 100644 (file)
@@ -26,7 +26,7 @@
 #define LANDLOCK_MASK_ACCESS_NET       ((LANDLOCK_LAST_ACCESS_NET << 1) - 1)
 #define LANDLOCK_NUM_ACCESS_NET                __const_hweight64(LANDLOCK_MASK_ACCESS_NET)
 
-#define LANDLOCK_LAST_SCOPE            LANDLOCK_SCOPE_ABSTRACT_UNIX_SOCKET
+#define LANDLOCK_LAST_SCOPE            LANDLOCK_SCOPE_SIGNAL
 #define LANDLOCK_MASK_SCOPE            ((LANDLOCK_LAST_SCOPE << 1) - 1)
 #define LANDLOCK_NUM_SCOPE             __const_hweight64(LANDLOCK_MASK_SCOPE)
 /* clang-format on */
index 4f8013ca412ec6bcbf5d798ab8342c61c567dac4..4acbd7c40eee5cf02ee7589d0629e4fd2d5eb2b5 100644 (file)
@@ -18,6 +18,7 @@
 
 #include "common.h"
 #include "cred.h"
+#include "fs.h"
 #include "ruleset.h"
 #include "setup.h"
 #include "task.h"
@@ -242,12 +243,67 @@ static int hook_unix_may_send(struct socket *const sock,
        return 0;
 }
 
+static int hook_task_kill(struct task_struct *const p,
+                         struct kernel_siginfo *const info, const int sig,
+                         const struct cred *const cred)
+{
+       bool is_scoped;
+       const struct landlock_ruleset *dom;
+
+       if (cred) {
+               /* Dealing with USB IO. */
+               dom = landlock_cred(cred)->domain;
+       } else {
+               dom = landlock_get_current_domain();
+       }
+
+       /* Quick return for non-landlocked tasks. */
+       if (!dom)
+               return 0;
+
+       rcu_read_lock();
+       is_scoped = domain_is_scoped(dom, landlock_get_task_domain(p),
+                                    LANDLOCK_SCOPE_SIGNAL);
+       rcu_read_unlock();
+       if (is_scoped)
+               return -EPERM;
+
+       return 0;
+}
+
+static int hook_file_send_sigiotask(struct task_struct *tsk,
+                                   struct fown_struct *fown, int signum)
+{
+       const struct landlock_ruleset *dom;
+       bool is_scoped = false;
+
+       /* Lock already held by send_sigio() and send_sigurg(). */
+       lockdep_assert_held(&fown->lock);
+       dom = landlock_file(fown->file)->fown_domain;
+
+       /* Quick return for unowned socket. */
+       if (!dom)
+               return 0;
+
+       rcu_read_lock();
+       is_scoped = domain_is_scoped(dom, landlock_get_task_domain(tsk),
+                                    LANDLOCK_SCOPE_SIGNAL);
+       rcu_read_unlock();
+       if (is_scoped)
+               return -EPERM;
+
+       return 0;
+}
+
 static struct security_hook_list landlock_hooks[] __ro_after_init = {
        LSM_HOOK_INIT(ptrace_access_check, hook_ptrace_access_check),
        LSM_HOOK_INIT(ptrace_traceme, hook_ptrace_traceme),
 
        LSM_HOOK_INIT(unix_stream_connect, hook_unix_stream_connect),
        LSM_HOOK_INIT(unix_may_send, hook_unix_may_send),
+
+       LSM_HOOK_INIT(task_kill, hook_task_kill),
+       LSM_HOOK_INIT(file_send_sigiotask, hook_file_send_sigiotask),
 };
 
 __init void landlock_add_task_hooks(void)
index 2d15f09d63e2316da6fe69b15eeec6b0a1a8b4b1..b90f76ed0d9c52640cad14c3ce7b14e03a9b51f3 100644 (file)
@@ -12,7 +12,7 @@
 
 #include "common.h"
 
-#define ACCESS_LAST LANDLOCK_SCOPE_ABSTRACT_UNIX_SOCKET
+#define ACCESS_LAST LANDLOCK_SCOPE_SIGNAL
 
 TEST(ruleset_with_unknown_scope)
 {