* [PATCH v1 1/2] Landlock: Add signal control @ 2024-07-05 21:21 Tahera Fahimi 2024-07-05 21:21 ` [PATCH v1 2/2] Landlock: Signal scoping tests Tahera Fahimi 2024-07-22 12:45 ` [PATCH v1 1/2] Landlock: Add signal control Jann Horn 0 siblings, 2 replies; 4+ messages in thread From: Tahera Fahimi @ 2024-07-05 21:21 UTC (permalink / raw) To: mic, gnoack, paul, jmorris, serge, linux-security-module, linux-kernel, bjorn3_gh, jannh, outreachy, netdev Cc: Tahera Fahimi Currently, a sandbox process is not restricted to send a signal (e.g. SIGKILL) to a process outside of the sandbox environment. Ability to sending a signal for a sandboxed process should be scoped the same way abstract unix sockets are scoped. The same way as abstract unix socket, we extend "scoped" field in a ruleset with "LANDLOCK_SCOPED_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 procsses). Signed-off-by: Tahera Fahimi <fahimitahera@gmail.com> --- include/uapi/linux/landlock.h | 3 +++ security/landlock/limits.h | 2 +- security/landlock/task.c | 49 +++++++++++++++++++++++++++++++++++ 3 files changed, 53 insertions(+), 1 deletion(-) diff --git a/include/uapi/linux/landlock.h b/include/uapi/linux/landlock.h index 010aaca5b05a..878479a1b9dd 100644 --- a/include/uapi/linux/landlock.h +++ b/include/uapi/linux/landlock.h @@ -291,8 +291,11 @@ struct landlock_net_port_attr { * from connecting to an abstract unix socket created by a process * outside the related Landlock domain (e.g. a parent domain or a process * which is not sandboxed). + * - %LANDLOCK_SCOPED_SIGNAL: Restrict a sandboxed process from sending a signal + * to another process outside sandbox domain. */ /* clang-format off */ #define LANDLOCK_SCOPED_ABSTRACT_UNIX_SOCKET (1ULL << 0) +#define LANDLOCK_SCOPED_SIGNAL (1ULL << 1) /* clang-format on*/ #endif /* _UAPI_LINUX_LANDLOCK_H */ diff --git a/security/landlock/limits.h b/security/landlock/limits.h index eb01d0fb2165..fa28f9236407 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_SCOPED_ABSTRACT_UNIX_SOCKET +#define LANDLOCK_LAST_SCOPE LANDLOCK_SCOPED_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 acc6e0fbc111..caee485b97b2 100644 --- a/security/landlock/task.c +++ b/security/landlock/task.c @@ -168,11 +168,60 @@ static int hook_unix_may_send(struct socket *const sock, return -EPERM; } +static bool signal_is_scoped(const struct landlock_ruleset *const sender_dom, + struct task_struct *const target) +{ + const struct landlock_ruleset *target_dom = + landlock_get_task_domain(target); + + /* quick return if there is no domain or .scoped is not set */ + if (!sender_dom || !get_scoped_accesses(sender_dom)) + return true; + + if (!target_dom || !get_scoped_accesses(target_dom)) + return false; + + /* other is scoped, they connect if they are in the same domain */ + return domain_scope_le(sender_dom, target_dom); +} + +static int hook_task_kill(struct task_struct *const p, + struct kernel_siginfo *const info, const int sig, + const struct cred *const cred) +{ + const struct landlock_ruleset *const dom = + landlock_get_current_domain(); + bool ret = false; + + if (!cred) + ret = signal_is_scoped(dom, p); + else + ret = signal_is_scoped(landlock_cred(cred)->domain, p); + if (ret) + return 0; + return EPERM; +} + +static int hook_file_send_sigiotask(struct task_struct *tsk, + struct fown_struct *fown, int signum) +{ + const struct task_struct *result = + get_pid_task(fown->pid, fown->pid_type); + + const struct landlock_ruleset *const dom = + landlock_get_task_domain(result); + if (signal_is_scoped(dom, tsk)) + return 0; + return EPERM; +} + 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) -- 2.34.1 ^ permalink raw reply related [flat|nested] 4+ messages in thread
* [PATCH v1 2/2] Landlock: Signal scoping tests 2024-07-05 21:21 [PATCH v1 1/2] Landlock: Add signal control Tahera Fahimi @ 2024-07-05 21:21 ` Tahera Fahimi 2024-07-09 15:18 ` Günther Noack 2024-07-22 12:45 ` [PATCH v1 1/2] Landlock: Add signal control Jann Horn 1 sibling, 1 reply; 4+ messages in thread From: Tahera Fahimi @ 2024-07-05 21:21 UTC (permalink / raw) To: mic, gnoack, paul, jmorris, serge, linux-security-module, linux-kernel, bjorn3_gh, jannh, outreachy, netdev Cc: Tahera Fahimi Signed-off-by: Tahera Fahimi <fahimitahera@gmail.com> --- .../testing/selftests/landlock/ptrace_test.c | 216 ++++++++++++++++++ 1 file changed, 216 insertions(+) diff --git a/tools/testing/selftests/landlock/ptrace_test.c b/tools/testing/selftests/landlock/ptrace_test.c index a19db4d0b3bd..e092b67f8b67 100644 --- a/tools/testing/selftests/landlock/ptrace_test.c +++ b/tools/testing/selftests/landlock/ptrace_test.c @@ -17,6 +17,8 @@ #include <sys/wait.h> #include <unistd.h> +#include <signal.h> + #include "common.h" /* Copied from security/yama/yama_lsm.c */ @@ -25,6 +27,8 @@ #define YAMA_SCOPE_CAPABILITY 2 #define YAMA_SCOPE_NO_ATTACH 3 +static sig_atomic_t signaled; + static void create_domain(struct __test_metadata *const _metadata) { int ruleset_fd; @@ -436,4 +440,216 @@ TEST_F(hierarchy, trace) _metadata->exit_code = KSFT_FAIL; } +static void create_sig_domain(struct __test_metadata *const _metadata) +{ + int ruleset_fd; + const struct landlock_ruleset_attr ruleset_attr = { + .scoped = LANDLOCK_SCOPED_SIGNAL, + }; + + ruleset_fd = + landlock_create_ruleset(&ruleset_attr, sizeof(ruleset_attr), 0); + EXPECT_LE(0, ruleset_fd) + { + TH_LOG("Failed to create a ruleset: %s", strerror(errno)); + } + enforce_ruleset(_metadata, ruleset_fd); + EXPECT_EQ(0, close(ruleset_fd)); +} + +static void scope_signal_handler(int sig, siginfo_t *info, void *ucontext) +{ + if (sig == SIGHUP || sig == SIGURG || sig == SIGTSTP || sig == SIGTRAP) + signaled = 1; + + // signal process group + //kill(-(t->pid), SIGKILL); +} + +/* clang-format off */ +FIXTURE(signal_scoping) {}; +/* clang-format on */ + +FIXTURE_VARIANT(signal_scoping) +{ + const int sig; + const bool domain_both; + const bool domain_parent; + const bool domain_child; +}; + +/* Default Action: Terminate*/ +/* clang-format off */ +FIXTURE_VARIANT_ADD(signal_scoping, deny_with_forked_domain) { + /* clang-format on */ + .sig = SIGHUP, + .domain_both = true, + .domain_parent = true, + .domain_child = true, +}; + +/* clang-format off */ +FIXTURE_VARIANT_ADD(signal_scoping, allow_with_forked_domain) { + /* clang-format on */ + .sig = SIGHUP, + .domain_both = false, + .domain_parent = true, + .domain_child = false, +}; + +/* Default Action: Ignore*/ +/* clang-format off */ +FIXTURE_VARIANT_ADD(signal_scoping, deny_with_forked_domain_SIGURG) { + /* clang-format on */ + .sig = SIGURG, + .domain_both = true, + .domain_parent = true, + .domain_child = true, +}; + +/* clang-format off */ +FIXTURE_VARIANT_ADD(signal_scoping, allow_with_forked_domain_SIGURG) { + /* clang-format on */ + .sig = SIGURG, + .domain_both = false, + .domain_parent = true, + .domain_child = false, +}; + +/* Default Action: Stop*/ +/* clang-format off */ +FIXTURE_VARIANT_ADD(signal_scoping, deny_with_forked_domain_SIGTSTP) { + /* clang-format on */ + .sig = SIGTSTP, + .domain_both = true, + .domain_parent = true, + .domain_child = true, +}; + +/* clang-format off */ +FIXTURE_VARIANT_ADD(signal_scoping, allow_with_forked_domain_SIGTSTP) { + /* clang-format on */ + .sig = SIGTSTP, + .domain_both = false, + .domain_parent = true, + .domain_child = false, +}; + +/* Default Action: Coredump*/ +/* clang-format off */ +FIXTURE_VARIANT_ADD(signal_scoping, deny_with_forked_domain_SIGTRAP) { + /* clang-format on */ + .sig = SIGTRAP, + .domain_both = true, + .domain_parent = true, + .domain_child = true, +}; + +/* clang-format off */ +FIXTURE_VARIANT_ADD(signal_scoping, allow_with_forked_domain_SIGTRAP) { + /* clang-format on */ + .sig = SIGTRAP, + .domain_both = false, + .domain_parent = true, + .domain_child = false, +}; + +FIXTURE_SETUP(signal_scoping) +{ +} + +FIXTURE_TEARDOWN(signal_scoping) +{ +} + +TEST_F(signal_scoping, test_signal) +{ + pid_t child; + pid_t parent = getpid(); + int status; + bool can_signal; + int pipe_child[2], pipe_parent[2]; + //char buf_parent; + + struct sigaction action = { + .sa_sigaction = scope_signal_handler, + .sa_flags = SA_SIGINFO, + + }; + + can_signal = !variant->domain_child; + + //sigemptyset(&act.sa_mask); + + ASSERT_LE(0, sigaction(variant->sig, &action, NULL)) + { + TH_LOG("ERROR in sigaction %s", strerror(errno)); + } + + if (variant->domain_both) { + create_sig_domain(_metadata); + if (!__test_passed(_metadata)) + /* Aborts before forking. */ + return; + } + ASSERT_EQ(0, pipe2(pipe_child, O_CLOEXEC)); + ASSERT_EQ(0, pipe2(pipe_parent, O_CLOEXEC)); + + child = fork(); + ASSERT_LE(0, child); + if (child == 0) { + char buf_child; + int err; + + ASSERT_EQ(0, close(pipe_parent[1])); + ASSERT_EQ(0, close(pipe_child[0])); + + if (variant->domain_child) + create_sig_domain(_metadata); + + /* Waits for the parent to be in a domain, if any. */ + ASSERT_EQ(1, read(pipe_parent[0], &buf_child, 1)); + + //err = raise(SIGHUP); + err = kill(parent, variant->sig); + if (can_signal) { + ASSERT_EQ(0, err); + } else { + ASSERT_EQ(EPERM, errno) + { + TH_LOG("Invalid error cached: %s", + strerror(errno)); + } + } + _exit(_metadata->exit_code); + return; + } + + ASSERT_EQ(0, close(pipe_child[1])); + ASSERT_EQ(0, close(pipe_parent[0])); + if (variant->domain_parent) + create_sig_domain(_metadata); + + /* Signals that the parent is in a domain, if any. */ + ASSERT_EQ(1, write(pipe_parent[1], ".", 1)); + + if (can_signal) { + ASSERT_EQ(-1, pause()); + ASSERT_EQ(EINTR, errno); + ASSERT_EQ(1, signaled); + } + + ASSERT_EQ(child, waitpid(child, &status, 0)); + + if (WIFEXITED(status)) { + TH_LOG("Exited with code %d:", WEXITSTATUS(status)); + if (!can_signal) + ASSERT_NE(1, signaled); + } + + if (WIFSIGNALED(status) || !WIFEXITED(status) || + WEXITSTATUS(status) != EXIT_SUCCESS) + _metadata->exit_code = KSFT_FAIL; +} + TEST_HARNESS_MAIN -- 2.34.1 ^ permalink raw reply related [flat|nested] 4+ messages in thread
* Re: [PATCH v1 2/2] Landlock: Signal scoping tests 2024-07-05 21:21 ` [PATCH v1 2/2] Landlock: Signal scoping tests Tahera Fahimi @ 2024-07-09 15:18 ` Günther Noack 0 siblings, 0 replies; 4+ messages in thread From: Günther Noack @ 2024-07-09 15:18 UTC (permalink / raw) To: Tahera Fahimi Cc: mic, paul, jmorris, serge, linux-security-module, linux-kernel, bjorn3_gh, jannh, outreachy, netdev On Fri, Jul 05, 2024 at 03:21:43PM -0600, Tahera Fahimi wrote: > Signed-off-by: Tahera Fahimi <fahimitahera@gmail.com> > --- > .../testing/selftests/landlock/ptrace_test.c | 216 ++++++++++++++++++ > 1 file changed, 216 insertions(+) > > diff --git a/tools/testing/selftests/landlock/ptrace_test.c b/tools/testing/selftests/landlock/ptrace_test.c > index a19db4d0b3bd..e092b67f8b67 100644 > --- a/tools/testing/selftests/landlock/ptrace_test.c > +++ b/tools/testing/selftests/landlock/ptrace_test.c > [...] > +static void scope_signal_handler(int sig, siginfo_t *info, void *ucontext) > +{ > + if (sig == SIGHUP || sig == SIGURG || sig == SIGTSTP || sig == SIGTRAP) > + signaled = 1; > + > + // signal process group > + //kill(-(t->pid), SIGKILL); There is commented-out code like this in various places in this patch. I am pretty sure that scripts/checkpatch.pl should flag that. See https://docs.kernel.org/dev-tools/checkpatch.html and https://docs.kernel.org/process/submitting-patches.html#style-check-your-changes I personally just keep a checklist of things to remember before sending a patch. (rebase as needed, clang-format -i (for Landlock files), run tests, check commit metadata, git format-patch with -v and --cover-letter, scripts/checkpatch.pl, edit cover letter, git send-email) —Günther ^ permalink raw reply [flat|nested] 4+ messages in thread
* Re: [PATCH v1 1/2] Landlock: Add signal control 2024-07-05 21:21 [PATCH v1 1/2] Landlock: Add signal control Tahera Fahimi 2024-07-05 21:21 ` [PATCH v1 2/2] Landlock: Signal scoping tests Tahera Fahimi @ 2024-07-22 12:45 ` Jann Horn 1 sibling, 0 replies; 4+ messages in thread From: Jann Horn @ 2024-07-22 12:45 UTC (permalink / raw) To: Tahera Fahimi Cc: mic, gnoack, paul, jmorris, serge, linux-security-module, linux-kernel, bjorn3_gh, outreachy, netdev On Fri, Jul 5, 2024 at 11:22 PM Tahera Fahimi <fahimitahera@gmail.com> wrote: > Currently, a sandbox process is not restricted to send a signal > (e.g. SIGKILL) to a process outside of the sandbox environment. > Ability to sending a signal for a sandboxed process should be > scoped the same way abstract unix sockets are scoped. > > The same way as abstract unix socket, we extend "scoped" field > in a ruleset with "LANDLOCK_SCOPED_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 procsses). > > Signed-off-by: Tahera Fahimi <fahimitahera@gmail.com> [...] > +static int hook_file_send_sigiotask(struct task_struct *tsk, > + struct fown_struct *fown, int signum) > +{ > + const struct task_struct *result = > + get_pid_task(fown->pid, fown->pid_type); get_pid_task() returns a refcounted reference; you'll have to call put_task_struct(result) to drop this reference at the end of the function. > + const struct landlock_ruleset *const dom = > + landlock_get_task_domain(result); > + if (signal_is_scoped(dom, tsk)) > + return 0; > + return EPERM; > +} ^ permalink raw reply [flat|nested] 4+ messages in thread
end of thread, other threads:[~2024-07-22 12:46 UTC | newest] Thread overview: 4+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2024-07-05 21:21 [PATCH v1 1/2] Landlock: Add signal control Tahera Fahimi 2024-07-05 21:21 ` [PATCH v1 2/2] Landlock: Signal scoping tests Tahera Fahimi 2024-07-09 15:18 ` Günther Noack 2024-07-22 12:45 ` [PATCH v1 1/2] Landlock: Add signal control Jann Horn
This is an external index of several public inboxes, see mirroring instructions on how to clone and mirror all data and code used by this external index.