diff options
| -rw-r--r-- | include/uapi/linux/landlock.h | 3 | ||||
| -rw-r--r-- | security/landlock/cred.h | 2 | ||||
| -rw-r--r-- | security/landlock/fs.c | 25 | ||||
| -rw-r--r-- | security/landlock/fs.h | 7 | ||||
| -rw-r--r-- | security/landlock/limits.h | 2 | ||||
| -rw-r--r-- | security/landlock/task.c | 56 | ||||
| -rw-r--r-- | tools/testing/selftests/landlock/scoped_test.c | 2 | 
7 files changed, 94 insertions, 3 deletions
| diff --git a/include/uapi/linux/landlock.h b/include/uapi/linux/landlock.h index 70edd17bafdc..33745642f787 100644 --- a/include/uapi/linux/landlock.h +++ b/include/uapi/linux/landlock.h @@ -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 */ diff --git a/security/landlock/cred.h b/security/landlock/cred.h index af89ab00e6d1..bf755459838a 100644 --- a/security/landlock/cred.h +++ b/security/landlock/cred.h @@ -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;  } diff --git a/security/landlock/fs.c b/security/landlock/fs.c index 0804f76a67be..7d79fc8abe21 100644 --- a/security/landlock/fs.c +++ b/security/landlock/fs.c @@ -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) diff --git a/security/landlock/fs.h b/security/landlock/fs.h index 488e4813680a..1487e1f023a1 100644 --- a/security/landlock/fs.h +++ b/security/landlock/fs.h @@ -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;  };  /** diff --git a/security/landlock/limits.h b/security/landlock/limits.h index d74818003ed4..15f7606066c8 100644 --- a/security/landlock/limits.h +++ b/security/landlock/limits.h @@ -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 */ diff --git a/security/landlock/task.c b/security/landlock/task.c index 4f8013ca412e..4acbd7c40eee 100644 --- a/security/landlock/task.c +++ b/security/landlock/task.c @@ -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) diff --git a/tools/testing/selftests/landlock/scoped_test.c b/tools/testing/selftests/landlock/scoped_test.c index 2d15f09d63e2..b90f76ed0d9c 100644 --- a/tools/testing/selftests/landlock/scoped_test.c +++ b/tools/testing/selftests/landlock/scoped_test.c @@ -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)  { | 
